排序
一、插入排序
- 直接插入排序
算法步骤:
时间复杂度O(n2),空间复杂度O(1)。
特点:
1.稳定排序
2.算法简便,容易实现
3.也适用于链式存储结构,只是在单链表上无需移动记录,只需修改相应的指针
4.更适合于初始记录基本有序的情况,当初始记录无序,n较大时,此算法时间复杂度较高,不宜采用
直接插入排序代码如下:
#include<bits/stdc++.h>
using namespace std;
#define MAXNUMS 999999
int main()
{
int n = 8;
int a[n] = {49,38,65,97,76,13,27,49};
for(int i=1;i<n;i++)
{
if(a[i]<a[i-1])
{
int t = a[i];
a[i] = a[i-1];
int j;
for(j = i - 1 ; t<a[j] && j>=0 ; j--)
{
a[j+1] = a[j];
}
a[j+1] = t;
}
}
for(int i=0;i<n;i++)
{
printf("%d ",a[i]);
}
}
- 折半插入排序
思想:直接插入排序+折半查找
时间复杂度:O(n2),空间复杂度O(1)。
特点:
1.稳定排序
2.因为要进行折半查找,只能用于顺序结构,不能用于链式结构
3.适合初始记录无序、n较大时的情况。
#include<bits/stdc++.h>
using namespace std;
#define MAXNUMS 999999
int main()
{
int n = 8;
int a[n] = {49,38,65,97,76,13,27,49};
for(int i=1;i<n;i++)
{
int t = a[i];
int low = 0,high = i - 1;
while(low<=high)
{
int mid = (low+high)/2;
if(a[mid]>t)
{
high = mid - 1;
}
else
{
low = mid + 1;
}
}
for(int j = i - 1;j>=high+1;j--)
{
a[j+1] = a[j];
}
a[high+1] = t;
}
for(int i=0;i<n;i++)
{
printf("%d ",a[i]);
}
}
3. 希尔排序(缩小增量排序)
思想:实际上采用分组插入的方法,将整个序列分割成几组,从而减少参与直接插入排序的数据量,对每组分别进行直接插入序列,然后增加每组的数据量,重新分组。这样当经过几次分组排序后,整个序列中的记录“基本有序”时,在对全体记录进行一次直接插入排序。
时间复杂度O(n1.3) 或 O(n2),空间复杂度O(1)。
特点:
1.记录跳跃式地移动导致排序方法是不稳定的
2.只能用于顺序结构,不能用于链式结构
3.增量序列可以有各种取法,但应该使增量序列中的值没有除1之外的公因子,并且最后一个增量值必须等于1
4.记录总的比较次数和移动次数都比直接插入序列要少,n越大时,效果越明显。所以适合初始记录无序、n较大时的情况
#include<bits/stdc++.h>
using namespace std;
#define MAXNUMS 999999
int main()
{
int n = 10;
int a[n] = {49,38,65,97,76,13,27,49,55,04};
int len_d = 3;
int dt[len_d] = {5,3,1};
printf("初始序列:");
for(int i=0;i<n;i++)
{
printf("%d ",a[i]);
}
printf("\n");
for(int k=0;k<len_d;k++)
{
for(int i = dt[k];i<n;i++)
{
if(a[i]<a[i-dt[k]])
{
int t = a[i];
int j;
for(j = i - dt[k];j>=0 &&t<a[j];j = j -dt[k])
{
a[j+dt[k]] = a[j];
}
a[j+dt[k]] = t;
}
}
printf("第%d趟排序结果:",k+1);
for(int z=0;z<n;z++)
{
printf("%d ",a[z]);
}
printf("\n");
}
}
程序运行结果如下:
二、交换排序
- 冒泡排序
时间复杂度:O(n2),空间复杂度O(1)。
特点:
1.稳定排序
2.可用于链式存储结构
3.移动记录次数较多,算法平均时间性能比直接插入排序差
#include<bits/stdc++.h>
using namespace std;
#define MAXNUMS 999999
int main()
{
int n = 10;
int a[n] = {49,38,65,97,76,13,27,49,55,04};
printf("初始序列:");
for(int i=0;i<n;i++)
{
printf("%d ",a[i]);
}
printf("\n");
///冒泡排序
for(int i=0;i<n-1;i++)
{
for(int j=i+1;j<n;j++)
{
if(a[i]>a[j])
{
int t = a[i];
a[i] = a[j];
a[j] = t;
}
}
printf("第%d趟排序结果:",i+1);
for(int j=0;j<n;j++)
{
printf("%d ",a[j]);
}
printf("\n");
}
}
2. 快速排序
思路:
Partition函数:
两个指针low指向最左边,high指向最右边,设置变量t存储最左边的值。
进行循环while(low<high):
while(low<high && a[high]>=t):
指针high向左移一位
指针low指向的值更改为指针high指向的值(此时a[high]<t)
while(low<high && a[low]<=t):
指针low向右移一位
指针high指向的值更改为指针low指向的值(此时a[low]>t)
循环结束,将指针low指向的值更改为t,函数运行结束,返回low。
时间复杂度:O(nlog2n),空间复杂度O(log2n)~o(n)。
特点:
1.记录非顺次的移动导致排序方法是不稳定的
2.排序过程中需要定位表的下界和上界,所以适合用于顺序结构,很难用于链式结构
3.当n较大时,在平均情况下快速排序是所有内部排序方法中速度最快的一种,所以其适合初始记录无序、n较大的情况
#include<bits/stdc++.h>
using namespace std;
#define MAXNUMS 999999
int Partition(int *a,int low,int high)
{
int t = a[low];
while(low<high)
{
while(low<high && a[high]>=t)
{
high--;
}
a[low] = a[high];
while(low<high && a[low]<=t)
{
low++;
}
a[high] = a[low];
}
a[low] = t;
return low;
}
void Mysort(int *a,int low,int high)
{
if(low<high)
{
int tmp = Partition(a,low,high);
Mysort(a,low,tmp-1);
Mysort(a,tmp+1,high);
}
}
int main()
{
int n = 10;
int a[n] = {49,38,65,97,76,13,27,49,55,04};
int *p = a;
printf("初始序列:");
for(int i=0;i<n;i++)
{
printf("%d ",a[i]);
}
printf("\n");
Mysort(a,0,n);
for(int i=0;i<n;i++)
{
printf("%d ",a[i]);
}
printf("\n");
}
三、选择排序
- 简单选择排序(直接选择排序)
时间复杂度O(n2),空间复杂度O(1)。
特点:
1.稳定的排序方法
2.可用于链式存储结构
3.移动记录次数较少,当每一记录占用的空间较多时,此方法比直接插入排序快
#include<bits/stdc++.h>
using namespace std;
#define MAXNUMS 999999
int main()
{
int n = 10;
int a[n] = {49,38,65,97,76,13,27,49,55,04};
int *p = a;
printf("初始序列:");
for(int i=0;i<n;i++)
{
printf("%d ",a[i]);
}
printf("\n");
for(int i=0;i<n;i++)
{
int k = i;
for(int j = i + 1;j<n;j++)
{
if(a[j]<a[k])
{
k = j;
}
}
if(k!=i)
{
int t = a[i];
a[i] = a[k];
a[k] = t;
}
}
for(int i=0;i<n;i++)
{
printf("%d ",a[i]);
}
}
- 树形选择排序(锦标赛排序)
①左右兄弟结点进行比较,选出最小的作为自己的双亲结点,最后输出根结点。
②输出根结点后,将叶子结点中最小关键字(即根结点对应的叶子结点)修改为最大值。
③重复执行①和②,当根结点为最大值时结束。 - 堆排序
大根堆a[n]>=a[2n]且a[n]>=a[2n+1];
小根堆a[n]<=a[2n]且a[n]<=a[2n+1]。
时间复杂度O(nlog2n),空间复杂度O(1)。
特点:
1.是不稳定排序
2.只能用于顺序结构,不能用于链式结构
3.初始建堆所需的比较次数较多,因此记录数较少时不宜采用。
#include<bits/stdc++.h>
using namespace std;
#define MAXNUMS 999999
void HeapMinAdjust(int *a,int s,int m)
{
int t = a[s];
for(int i=2*s;i<=m;i=i*2)
{
if(i<m && a[i]>a[i+1]) i++;
if(t <= a[i]) break;
a[s] = a[i];
s = i;
}
a[s] = t;
}
void CreateMinHeap(int *a,int n)
{
for(int i=n/2;i>0;i--)
{
HeapMinAdjust(a,i,n-1);
}
}
void HeapMinSort(int *a,int n)
{
CreateMinHeap(a,n);
printf("最小堆:");
for(int i=1;i<n;i++)
{
printf("%d ",a[i]);
}
printf("\n");
for(int i=n;i>1;i--)
{
int t = a[1];
a[1] = a[i];
a[i] = t;
HeapMinAdjust(a,1,i-1);
}
}
void HeapMaxAdjust(int *a,int s,int m)
{
int t = a[s];
for(int i=2*s;i<=m;i=i*2)
{
if(i<m && a[i]<a[i+1]) i++;
if(t >= a[i]) break;
a[s] = a[i];
s = i;
}
a[s] = t;
}
void CreateMaxHeap(int *a,int n)
{
for(int i=n/2;i>0;i--)
{
HeapMaxAdjust(a,i,n-1);
}
}
void HeapMaxSort(int *a,int n)
{
CreateMaxHeap(a,n);
printf("最大堆:");
for(int i=1;i<n;i++)
{
printf("%d ",a[i]);
}
printf("\n");
for(int i=n;i>1;i--)
{
int t = a[1];
a[1] = a[i];
a[i] = t;
HeapMaxAdjust(a,1,i-1);
}
}
int main()
{
int n = 11;
int a[n] = {0,49,38,65,97,76,13,27,49,55,04};
printf("初始序列:");
for(int i=1;i<n;i++)
{
printf("%d ",a[i]);
}
printf("\n");
HeapMaxSort(a,n-1);
printf("堆排序结果:");
for(int i=1;i<n;i++)
{
printf("%d ",a[i]);
}
printf("\n");
HeapMinSort(a,n-1);
printf("堆排序结果:");
for(int i=1;i<n;i++)
{
printf("%d ",a[i]);
}
printf("\n");
}