数据结构中各类排序算法

数据结构中各类排序算法

一、插入排序

1.直接插入排序

 直接插入排序(straight insertion sort)的做法是:
 每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序。
 第一趟比较前两个数,然后把第二个数按大小插入到有序表中; 第二趟把第三个数据与前两个数从前向后扫描,把第三个数按大小插入到有序表中;依次进行下去,进行了(n-1)趟扫描以后就完成了整个排序过程。
 直接插入排序属于稳定的排序,最坏时间复杂性为O(n^2),空间复杂度为O(1)。
 直接插入排序是由两层嵌套循环组成的。外层循环标识并决定待比较的数值。内层循环为待比较数值确定其最终位置。直接插入排序是将待比较的数值与它的前一个数值进行比较,所以外层循环是从第二个数值开始的。当前一数值比待比较数值大的情况下继续循环比较,直到找到比待比较数值小的并将待比较数值置入其后一位置,结束该次循环。
 C++代码:
#include<iostream>
usingnamespacestd;
int main()
{
    int a[]={98,76,109,34,67,190,80,12,14,89,1};
    int k=sizeof(a)/sizeof(a[0]);
    int j;
    for(int i=1;i<k;i++)//循环从第2个元素开始
    {
        if(a[i]<a[i-1])
        {
            int temp=a[i];
            for(j=i-1;j>=0 && a[j]>temp;j--)
            {//下方争论皆因未加大括号引起误解,故增加以避免误导
                a[j+1]=a[j];
            }
            a[j+1]=temp;//此处就是a[j+1]=temp;
        }
    }
    for(intf=0;f<k;f++)
    {
        cout<<a[f]<<"";
    }
    return0;
}
      直接插入排序的优缺点:
优点:算法简单、易行。当待排序记录数量较少时,该算法非常有效。
缺点:期望复杂度为O(n^2)。

     2.Shell排序(希尔排序)

又名缩小增量排序。
基本思想:先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为dl的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<;…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。该方法实质上是一种分组插入方法。
希尔排序示意图:

Shell排序的执行时间依赖于增量序列。
好的增量序列的共同特征:
① 最后一个增量必须为1;
② 应该尽量避免序列中的值(尤其是相邻的值)互为倍数的情况。
希尔排序的优缺点:
优点:对直接插入法的一种改进,属于分组插入法,如果增量的选取比较合理,时间复杂度为O(n(logn)^2)。
缺点:是一种不稳定的算法,性能取决于增量的选取。
C语言代码:
/*
*D.Shell最初的算法。
*/
int shellsortSh(int p[],int n)
{
int op=0;
int h,i,j,temp;
for(h=n/2;h>0;h=h/2){
for(i=h;i<n;i++){
temp=p[i];
for(j=i-h;j>=0&&p[j]>temp;j-=h){
p[j+h]=p[j];
op++;
}
p[j+h]=temp;
op++;
}
}
return op;
}

二、交换排序

1.冒泡排序

它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端,故名。
冒泡排序示意图:

冒泡排序:时间复杂度为O(n^2),稳定排序算法。
C语言代码:
#include<stdio.h>
#defineSIZE8
 
voidbubble_sort(inta[],intn);
 
voidbubble_sort(inta[],intn)//n为数组a的元素个数
{
    inti,j,temp;
    for(j=0;j<n-1;j++)
        for(i=0;i<n-1-j;i++)
        {
            if(a[i]>a[i+1])//数组元素大小按升序排列
            {
                temp=a[i];
                a[i]=a[i+1];
                a[i+1]=temp;
            }
        }
}
intmain()
{
    intnumber[SIZE]={95,45,15,78,84,51,24,12};
    inti;
    bubble_sort(number,SIZE);
    for(i=0;i<SIZE;i++)
    {
        printf("%d",number[i]);
    }
    printf("\n");
}

对冒泡排序的改进:交替冒泡排序
基本思想:冒泡排序每次冒泡把最大的数上浮到最顶端,而交替冒泡排序为将最大数上浮与最小数下沉交替进行的,事实证明,这样更具有效率。

     2.快速排序

快速排序(Quicksort)是对冒泡排序的一种改进。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
假设用户输入了如下数组:
下标
0
1
2
3
4
5
数据
6
2
7
3
8
9
创建变量i=0(指向第一个数据), j=5(指向最后一个数据), k=6(赋值为第一个数据的值)。
我们取走了下标0的数据,于是,我们需要找到一个数字来替换他。由于我们要把所有比6小的数移动到左面,所以我们可以开始寻找比6小的数并从右往左找。别急,我们要按顺序找哦。不断递减j的值,我们发现下标3的数据比6小,于是把3移到下标0(实际是i指向的位置。代码中要用i,因为后面还会循环这个步骤,不用i的话第二次循环:
下标
0
1
2
3
4
5
数据
3
2
7
3
8
9
i=0 j=3 k=6
由于变量k已经储存了下标0的数据,所以我们可以放心的把下标0覆盖了。如此一来,下标3虽然有数据,但是相当于没有了,因为数据已经复制到别的地方了。于是我们再找一个数据来替换他。这次要变成找比k大的了,而且要从前往后找了。递加变量i,发现下标2是第一个比k大的,于是用下标2的数据7替换j指向的下标3的数据,数据状态变成下表:
下标
0
1
2
3
4
5
数据
3
2
7
7
8
9
i=2 j=3 k=6
重复上面的步骤,递减变量j。这时,我们发现i和j“碰头”了:他们都指向了下标2。于是,循环结束,把k填回下标2里,即得到结果。
填回k之后状态为:
下标
0
1
2
3
4
5
数据
3
2
6
7
8
9
如果i和j没有碰头的话,就递加i找大的,还没有,就再递减j找小的,如此反复,不断循环。注意判断和寻找是同时进行的。
       注意:快速排序不会直接得到最终结果,只会把比k大和比k小的数分到k的两边。(你可以想象一下i和j是两个机器人,数据就是大小不一的石头,先取走i前面的石头留出回旋的空间,然后他们轮流分别挑选比k大和比k小的石头扔给对面,最后在他们中间把取走的那块石头放回去,于是比这块石头大的全扔给了j那一边,小的全扔给了i那一边。只是这次运气好,扔完一次刚好排整齐。)为了得到最后结果,需要再次对下标2两边的数组分别执行此步骤,然后再分解数组,直到数组不能再分解为止(只有一个数据),才能得到正确结果。
C语言代码:
void QuickSort(int a[],int numsize)/*a是整形数组,numsize是元素个数*/
{
 int i=0,j=numsize-1;
 int val=a[0];/*指定参考值val大小*/
 if(numsize>1)/*确保数组长度至少为2,否则无需排序*/
 {
 while(i<j)/*循环结束条件*/
 {
 /*从后向前搜索比val小的元素,找到后填到a[i]中并跳出循环*/
 for(;j>i;j--)
 if(a[j]<val)
 {
 a[i++]=a[j];
 break;
 }
 /*从前往后搜索比val大的元素,找到后填到a[j]中并跳出循环*/
 for(;i<j;i++)
 if(a[i]>val)
 {
 a[j--]=a[i];
 break;
 }
 }
 a[i]=val;/*将保存在val中的数放到a[i]中*/
 QuickSort(a,i);/*递归,对前i个数排序*/
 QuickSort(a+i+1,numsize-i-1);/*对i+2到numsize这numsize-1-i个数排序*/
 }
}

算法时间复杂度:O(nlogn)

三、选择排序

1.直接选择排序
直接选择排序(Straight Select Sorting) 也是一种简单的排序方法,它的基本思想是:第一次从R[0]~R[n-1]中选取最小值,与R[0]交换,第二次从R[1]~R[n-1]中选取最小值,与R[1]交换,....,第i次从R[i-1]~R[n-1]中选取最小值,与R[i-1]交换,.....,第n-1次从R[n-2]~R[n-1]中选取最小值,与R[n-2]交换,总共通过n-1次,得到一个按排序码从小到大排列的有序序列·
例如:
给定n=8,数组R中的8个元素的排序码为(8,3,2,1,7,4,6,5),则直接选择排序的过程如下所示:
初始状态 [ 8 3 2 1 7 4 6 5 ] 8 -- 1
第一次 [ 1 3 2 8 7 4 6 5 ] 3 -- 2
第二次 [ 1 2 3 8 7 4 6 5 ] 3 -- 3
第三次 [ 1 2 3 8 7 4 6 5 ] 8 -- 4
第四次 [ 1 2 3 4 7 8 6 5 ] 7 -- 5
第五次 [ 1 2 3 4 5 8 6 7 ] 8 -- 6
第六次 [ 1 2 3 4 5 6 8 7 ] 8 -- 7
第七次 [ 1 2 3 4 5 6 7 8 ] 
排序完成。
算法的时间复杂度:O(n^2)

2.堆排序

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。
算法时间复杂度:O(nlogn),不稳定算法。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章