数据结构排序算法之直接插入、折半插入、希尔排序、冒泡排序、快速排序、简单选择排序、堆排序、归并排序(二路归并排序)C语言实现

插入:直接插入、折半插入、希尔排序

交换:冒泡排序、快速排序⭐

选择:简单选择排序、堆排序⭐

归并:归并排序(二路归并排序)⭐

快速排序、堆排序和归并排序为时间复杂度较小的排序方法,为O(nlogn);

英语不太好,有些算法就直接用拼音了...

这些算法的输入输出语句用的是cout.cin。(为C++内容,只需将头文件改为#include<iostream>  using namespace std;即可兼容C\C++语句。或可直接将cin.cout语句改为scanf\printf语句。)

以下所有算法的原数据都应为数组储存。即对是数组进行排序操作。A[N+1]={0,........,N};

int _show(int a[],int n)  //展示排序后的结果
{
cout<<"排序后结果如下\n"<<endl;
for(int i=1;i<=n;i++)
cout<<a[i]<<endl;
return 1;
}

1.直接插入排序:

简单算法思路:将a[1]作为第一个有序序列,a[2]为第一个无序序列数不断与有序序列进行比较找到插入位置后插入,使得有序序列增加,无序序列减小,一共进行N-1趟。(假设一共有N个数据)

a[0]作为哨兵让循环条件减少,使得程序耗时减少。且a[0]可暂存要插入的数据,防止数据丢失。
int direct_insert(int a[],int n)
{
for(int i=2;i<=n;i++)
{
a[0]=a[i];
for(int j=i-1;a[0]<a[j];j--)
a[j+1]=a[j]; //查找要插入位置,数据同时后移。
a[j+1]=a[0];
}
 _show(a,n);
 return 1;
}

折半插入排序:

简单算法思路:与直接插入排序的区别为,将顺序查找的方式改为了折半查找,使得查找次数减少。

哨兵的作用仅为暂存数据。

int insert(int a[],int x,int j)  //x为要插入的位置,j为要插入数据的原位置。
{
for(int i=j-1;i>=x;i--)
{
a[i+1]=a[i];
}
a[x]=a[0];
return 1;
}

int bino_insert(int a[],int n)
{
int low,high,mid;
for(int i=2;i<=n;i++)
{
a[0]=a[i];
low=1;
high=i-1;
mid=(low+high)/2;
for(;low<=high;)
{
if(a[0]<=a[mid])
high=mid-1;
else
low=mid+1;
mid=(high+low)/2;
}  //此循环过后low为a[0]应插入位置
insert(a,low,i);
}
_show(a,n);
return 1;
}

希尔排序(缩小增量排序法):

简单算法思路:

将数组的数据按增量进行分组(一般初始增量为N/2,以后每趟增量为前一趟的1/2),并把组内的数据依次进行直接插入排序。(注意:每组的数据是有间隔的,此间隔为增量)。然后逐渐减小增量,并进行各个组内的直接排序。直至当增量变为1,进行最后一次直接排序,即对整个数组的数据进行直接排序。

哨兵的作用仅为暂存数据。

例如N=6;初始增量为3.(每趟增量为多少即有多少个小组)则第一趟的3个分组分别为:A[1].A[4] || A[2].A[5] ||A[3].A[6]

二趟的增量为3/2=1.则第二趟的1个分组分别为:A[1].1[2].A[3].A[4].A[5].A[6]

 

希尔排序优于直接插入排序原因为:当数组为顺序时时间复杂度最小,又因为当数组的N较小时,直接插入排序的最好时间复杂度O[N]和O[N²]差别不大。而希尔排序一开始的增量较大,每组的记录较少,故各组内直接插入较快,当增量逐渐减小,各组内的数据增多,但此时因为组内元素已经过多次排序所有更加接近有序状态,所以此时排序过程也较快。

 


int xier(int a[],int n)
{
int mark=n;
n=n/2;
//n=1;
for(;n>=1;n=n/2)   //最后加上这个for实现所有希尔排序
{
for(int i=1;i<=n;i++)   //加上这个for实现每次希尔排序的所有小组的排序
{
for(int j=i+n;j<=mark;j=j+n) //这两个小for用来实现每次希尔排序里的每个小组的排序。
{
a[0]=a[j];
for(int f=j-n;a[0]<a[f]&&f>0;f=f-n)
a[f+n]=a[f];
a[f+n]=a[0];
}
}        
}
_show(a,mark);
return 1;
}

冒泡排序:

简单算法思路:(描述为大的上升,小的下沉)从A[1]开始每两个相邻的元素进行比较,若前者大于后者则交换。每一趟这样的反复交换使得有序序列增加,直到无序序列的最后一个数据。初始时我们假定无序序列最后一个数据为数组的最后一个数据。若某趟冒泡进行的交换次数不为无序序列的个数-1时,此时说明这次交换以后的数据都已经有序了,即可标志此时最后一次交换的下标以减少冒泡的趟数。

int maopao(int a[],int n)
{
int mark=n;//无序序列中最后一个记录的位置
int lastexchangeindex; //某一趟冒泡排序最后一次交换时的下标,作为新的无序序列的最后记录的位置
int temp;
while(mark>1)
{
lastexchangeindex=1;
for(int j=1;j<mark;j++)
{
if(a[j]>a[j+1])
{temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
lastexchangeindex=j;
}//end of if
}//end of for
mark=lastexchangeindex;
}//end of while
_show(a,n);
return 1;
}

快速排序:

简单算法思路,将此数组序列的第一个元素看作枢轴,小于此元素的移至此枢轴的左边,大于移至右边。再对枢轴的左边、右边也进行这样的操作,即选一个枢轴(左、右边的第一元素),小于的移左边,大的移右边。知道数组变得有序。(递归)

void _fast(int a[],int low,int high)
{
int c=low;
int d=high;
a[0]=a[low];
while(low<high)
{
for(;low<high&&a[0]<=a[high];)
high--;                        //从高处开始扫描。
a[low]=a[high];                //这里不用担心赋值会将a[low]原来的值消灭,因为a[0]已经保存。同理之后的赋值操作都不会消灭值,因为前一步赋值已经保存了。
for(;low<high&&a[0]>a[low];)
low++;
a[high]=a[low];
}
a[low]=a[0];
if(c<(low-1))
{
_fast(a,c,low-1);
}
if((low+1)<d)
{
_fast(a,low+1,d);
}
}
int __fast(int a[],int n)
{
_fast(a,1,n);
_show(a,n);
return 1;
}
简单选择排序:

简单算法思路:将A[i]到A[N]里的最小数据找出来与A[1]进行交换,重复N-1次(i从1到N-1),直至数组有序。

int _min(int a[],int n,int length)
{
int mi=a[n];
int pos=n;
for(int i=n+1;i<=length;i++)
{
if(mi>a[i])
{
mi=a[i];
pos=i;
}
}
return pos;
}
int easy_choose(int a[],int n)
{
int pos;
int temp;
for(int i=1;i<n;i++)
{
pos=_min(a,i,n);
temp=a[pos];
a[pos]=a[i];
a[i]=temp;
}
_show(a,n);
return 1;
}

归并排序:

简单算法思路:

先写出一个数组中两段分别有序且连续的数组变为一组有序的数据的函数。可将一个无序的数组看成由N个在数组中有序且连续的数据组成,利用递归将数组不断的分割成小数组直到变成一个数据时再依次合并起来。(递归)

int hebing(int p[],int k[],int a,int b,int c) //将p数组里的有序列a-b及有序列b+1到c合并为有序数组k
{
int sec=b+1;
int i=a;
while(a<=b&&sec<=c)
{
if(p[a]<p[sec])
{
k[i]=p[a];
a++;
i++;
}
else
{
k[i]=p[sec];
sec++;
i++;
}
}
if(a<=b)
{
for(int g=a;g<=b;g++)
{
k[i]=p[g];
i++;
}
}
if(sec<=c)
{
for(int g=sec;g<=c;g++)
{
k[i]=p[g];
i++;
}
}
return 1;
}
void _erluhebing(int a[],int b[],int w,int j)
{
int mid;
int c[100];
if(w==j)
b[w]=a[w];
else
{
mid=(w+j)/2;
_erluhebing(a,c,w,mid);
_erluhebing(a,c,mid+1,j);
hebing(c,b,w,mid,j);
}

堆排序:

简单算法思路:完全二叉树的思路,但是在数组上进行操作。先建堆,后取出堆的第一个数据(表现在数组上为:第一个元素与最后一个元素交换),此时堆因为堆首元素的改变而破坏(但是左右子树依然为堆),此时应筛选堆,再推出首元素,再筛选,再推出,直至推出的元素个数为N-1.因为建堆的操作为从N/2位置开始筛选,知道第一个元素。因此最重要的函数为筛选函数。筛选函数为左子树为堆,右子树为堆,但是加上了根结点以后,整棵树就不为堆。因此需要将左、右子树的最大值和根结点交换,此时与根节点交换的那颗树可能会变为不是堆,(另一棵绝不会变)所以应对这棵子树进行类似的操作,直至叶子结点或堆依然不变。这些关于树的分析,都需要转变到数组上的操作,例如A[N]的左孩子为A[2*N],右孩子为A[2*N+1];

 

int back(int a[],int n,int b)
{
if(2*b>n&&(2*b+1)>n)
return -1;
else
{
if(2*b<=n&&(2*b+1)>n)
return 2*b;
if(2*b<=n&&(2*b+1)<=n)
return (a[2*b]>a[2*b+1]?2*b:2*b+1);
}
}
int shaixuan(int a[],int c,int d) //因为a[c]的加入,使得以c为根结点的二叉树不成堆。
{
int back1=back(a,d,c);
int temp;
if(back1==-1)
return OK;
if(a[c]>=a[back1])
return OK;
else
{
temp=a[back1];
a[back1]=a[c];
a[c]=temp;
shaixuan(a,back1,d);
}
}
int dui(int a[],int n)
{
int temp;
for(int i=n/2;i>=1;i--)
{
shaixuan(a,i,n);
}
//_show(a,n);
temp=a[1];
a[1]=a[n];
a[n]=temp;
for(int j=n-1;j>1;j--)
{
shaixuan(a,1,j);
temp=a[1];
a[1]=a[j];
a[j]=temp;
}
_show(a,n);
return 1;
}

 

 

  • 3
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值