算法_八大排序算法总结

    最近笔试面试中经常考到排序算法,及其对应的时间复杂度和空间复杂度分析,现做如下总结。

一,冒泡排序

       思想:对于0~n-1,依次比较相邻两个数,前者比后者大就交换,一轮后A[n-1]是最大数,在对0~n-2执行以上步骤,则A[n-2]是第二大的数,循环执行上面的步骤即可,形象的可以理解为大的数一个个冒到后面去,所以叫冒泡排序。

     示意图:

首先6和3比较,6比3大,交换

6和5比较,交换

6和7比较,不用交换

依次执行以上步骤,第一轮后,序列变为

接着,在0到n-2上执行以上步骤

一轮过后,则变为

依次执行以上步骤,最后序列为

   时间复杂度:  O(n^2)

   空间复杂度:O(1)

   代码:

 1 class BubbleSort {
 2 public:
 3     int* bubbleSort(int* A, int n) 
 4     {
 5         int temp;
 6         // write code here
 7         for(int i = 0; i < n; i++)
 8         {
 9             for(int j = 0; j < n - i - 1; j++)
10             {
11                 if(A[j] > A[j+1])
12                 {
13                     temp = A[j];
14                     A[j] = A[j + 1];
15                     A[j + 1] = temp;
16                 }
17             }
18             
19         }
20         return A;
21     }
22 };

 

 二,选择排序

     思想:在序列中依次选择最小值放到最前端,重复以上步骤,只到排序完成

    示意图:

最小数为0,放到最前端

1到n-1最小数为1,放到最前端

依次执行以上步骤,最后为

  时间复杂度:O(n^2)

   空间复杂度:O(1)

   代码:

 1 class SelectionSort {
 2 public:
 3     int* selectionSort(int* A, int n) 
 4     {
 5         // write code here
 6         //从前往后依次放入为排序的数组的最小值
 7         int min_b;
 8         int temp;
 9         for(int i = 0; i < n - 1; i++)
10         {
11             min_b = i;
12             for(int j = i; j < n; j++)  //寻找最小值
13             {
14                 if(A[min_b] > A[j])
15                     min_b = j;
16                     
17             }
18             temp = A[i];
19             A[i] = A[min_b];
20             A[min_b] = temp;
21         }
22         return A;
23     }
24 };

 

 

三,插入排序

       思想:对于数组A[n],保证前面的A[0]~A[m]是排序好的,再把A[m+1]插入到前面排好序的序列中,m递增,知道m=n-2

       示意图:

原始序列为:

6和5比较,6比5大,要交换

接下来把3插入到前面排好序的序列中,首先3和6比,6大,后移一位

接着3和5比较,5大,后移一位

只到前面没有数了,或者前面的数比要插入的数小,就在对应的位置插入该数

再对1执行以上步骤

重复以上步骤,只到整个序列排序完成

   时间复杂度:O(n^2)

   空间复杂度:O(1)

       代码

 1 class InsertionSort {
 2 public:
 3     int* insertionSort(int* A, int n) 
 4     {
 5         // write code here
 6         int temp;
 7         for(int i = 1; i < n; i ++)
 8         {
 9             temp = A[i];
10             for(int j = i - 1; j >= 0; j--)
11             {
12                 if(temp < A[j])
13                 {
14                     A[j + 1] = A[j];
15                     if(j == 0)
16                     {
17                         A[j] = temp;
18                     }
19                 }
20                 else
21                 {
22                     A[j + 1] = temp;
23                     break;
24                 }
25             }
26         }
27         return A;
28     }
29 };

 

 

四,归并排序

     思想:对数组中每个数看成是长度为1的有序区间,接着合并相邻两个长度为1的有序区间,变为长度为2的有序区间,接着合并相邻长度为2的有序区间变成长度为4的有序区间,依次进行,只到排序完成

    示意图:

    首先为长度为1的有序区间

  

   合并为长度为2的有序区间

   合并为长度为4的有序区间

 

    合并为长度为8的有序区间,排序完成

     时间复杂度:O(nlogn)

     空间复杂度:O(N)

 

     代码

 1 class MergeSort {
 2 public:
 3     int* mergeSort(int* A, int n) 
 4     {
 5         mergeSort(A,0,n-1);
 6         return A;
 7         
 8     }
 9       void mergeSort(int* A, int left, int right)
10     {
11         if(left == right)
12             return;
13         int mid=(left+right)/2;
14         mergeSort(A,left,mid);
15         mergeSort(A,mid+1,right);
16         merge_p(A,left,mid,right);
17         return;
18     }
19     
20     void merge_p(int* A, int left, int mid, int right)
21     {
22         int* temp = new int[right - left + 1];
23         int l = left;
24         int r = mid + 1;
25         int k = 0;
26         while(l <= mid && r <= right)
27         {
28             if(A[l] < A[r])
29                 temp[k++] = A[l++];
30             else
31                 temp[k++] = A[r++]; 
32         }
33         while(l <= mid)
34             temp[k++] = A[l++];
35         while(r <= right)
36            temp[k++] = A[r++]; 
37         for(int i = 0; i < k; i++)
38         {
39             A[left + i] = temp[i];
40         }
41     }
42     
43 };

 

   

五,快速排序

      思想:随机选择数组中的数,小于等于这个数的放在左边,大于这个数的放在右边,递归调用以上步骤,完成排序

      示意图:

     首先随机选择,划分区间

      

      递归调用,即可完成排序。

      

    问题的关键在于如何划分区间,即小于等于的数如何放在左边,大于的数如何放在右边,即Partition过程

    首先假设选择3为枢纽源

   

   把枢纽源和最后一个数字交换

    

   维持一个小于等于区间

    

   接下来从左到右遍历所有数,大于枢纽源的数则不动,小于等于枢纽源的数时把该数和小于等于区间的下一个数交换,并令小于等于区间右移一位,如此进行,只到最后一个数,即枢纽源,再把枢纽源和小于等于区间的下一个数交换即可。

   接上图,4,5,6都大于3不用动,0小于3,要与小于等于区间的下一个数即4交换位置,小于等于区间右移一位,即:

如此进行下去

 

最后一步,把枢纽源3和小于等于区间的下一个数4交换位置

划分完毕,划分时间复杂度为O(n)。

  时间复杂度: O(nlogn)

  空间复杂度:O(logn) ~O(n)

代码

 1 class QuickSort {
 2 public:
 3      
 4     void swap(int &a, int &b)
 5     {
 6         int c;
 7         c = a;
 8         a = b;
 9         b = c;
10     }
11     
12     void quick_s(int* A, int left, int right)   //枢纽元直接取中间值
13     {
14         if(left >= right )
15             return;
16         int mid = (left + right)/2;
17         int key = left;
18         swap(A[mid],A[right]);
19         for(int i = left; i < right; i++)
20         {
21             if(A[i] < A[right])
22             {
23                 swap(A[key],A[i]);
24                 key++;
25             }
26         }
27         swap(A[key],A[right]);
28         quick_s(A,left,key - 1);
29         quick_s(A,key + 1,right);
30     }
31    
32     
33         
34     int* quickSort(int* A, int n) 
35     {
36         // write code here
37         quick_s( A, 0, n-1);
38         return A;
39     }
40 };

 

 

六,堆排序

   思想: 把数组构建为一个大小为n的大根堆,堆顶为所有元素的最大值,把堆顶元素和最后一个值交换,作为有序部分放在最后,接着调整剩下的n-1个元素的大根堆,第二大的值就是堆顶元素,在和该堆的最后一个元素交换,并脱离堆作为有序部分,重复以上步骤。

  示意图:

  原始数组为

构建最大堆

堆顶元素和最后一个数交换,作为有序部分放到最后

调整最大堆

堆顶元素和最后一个数交换,作为有序部分放到最后

调整最大堆

重复以上步骤,直到排序完成

   时间复杂度:O(nlogn)

   空间复杂度:O(1)

   代码

 1 class HeapSort 
 2 {
 3 public:
 4     void Prcdown(int* A, int i, int n)
 5     {
 6         int tem;
 7         int Child;
 8         for(tem = A[i]; 2 * i + 1 < n; i = Child)
 9         {
10              Child = 2 * i + 1;
11               if(Child != n-1 && A[Child + 1] > A[Child])  //寻找较大的儿子
12                   Child++;
13               if(tem < A[Child])
14                   A[i] = A[Child];
15               else
16                  break;
17         }
18         A[i] = tem;
19         
20     }
21     void Swap(int &a, int &b)
22     {
23       int c;
24       c = a;
25       a = b;
26       b = c;
27     }
28     int* heapSort(int* A, int n) 
29     {
30         // write code here
31         int i;
32        for(i = n / 2; i >= 0; i--)    //在数组上构建最大堆
33           Prcdown(A,i,n);
34         for(i = n - 1; i > 0; i--)
35        {
36           Swap(A[0],A[i]);   //开始排序,把最大值换到后面去
37           Prcdown(A,0,i);   //整理堆,剩下元素的最大值会回到开头
38        }
39         return A;
40     }
41 };

 

 

七,希尔排序

     思想:希尔排序是插入排序的改良版,插入排序的步长为1,希尔排序的步长依次递减。

    示意图:

   假设原始数组为以下,步长为3

    

   前三个数不考虑

  

  从1开始比较,步长为3跳3步和6比较,6比1大,交换位置

1再往前跳3步就越界,因此开始比较8,跳3步和5比较,8比5大,不交换位置

停止8的交换,开始下一个数7 的交换,7比3大,不用交换

停止7的交换,开始2的交换,2和6比,2比6小,交换位置

2再跳3步和1比较,2比1大,不用交换,停止2的比较,开始4的交换,4和8比,4比8小,交换位置

4再跳3步和5比较,4比5小,交换位置

4再跳3步,越界,停止比较,步长为3调整结束,接下来步长为2和1调整,得到结果

    时间复杂度:O(nlogn)

   空间复杂度:O(1)

  代码

 1 class ShellSort 
 2 {
 3 public:
 4     int* shellSort(int* A, int n) 
 5     {
 6         // write code here
 7         int i, j;
 8         int Increment = n/2;
 9         int tmp;
10         while(Increment >= 1)
11         {
12             for(i = Increment; i < n; ++i)
13             {
14                 tmp = A[i];
15                 for(j = i - Increment; j >= 0 && tmp < A[j]; j = j - Increment)
16                 {
17                     A[j + Increment] = A[j];
18                 }
19                 A[j + Increment] = tmp;
20             }
21             Increment = Increment / 2;
22         }
23         return A;
24     }
25 };

 八,通排序

     桶排序不是基于比较的排序,因此时间复杂度可以达到O(n),空间复杂度为O(M),M为桶的个数,桶排序的思想在于是先把元素放入对应的桶中,再到出,结果即为排完序的结果。

     基于桶排序思想的排序算法有基数排序和计数排序,原理差不多。

    具体请看我的另外一篇博客https://www.cnblogs.com/1242118789lr/p/6858486.html

 

   总结:

   稳定的算法:冒泡排序,插入排序,归并排序,桶排序

   不稳定的算法:选择排序,快速排序,希尔排序,堆排序

   元素的移动次数与关键字的初始排列次序无关的是:基数排序

   元素的比较次数与初始序列无关的是:选择排序

 

   夜深了,,,

 

   天亮了,昨晚平安夜。

 

 

 

  

 

posted on 2018-09-07 10:45  wu_xin  阅读(208)  评论(0编辑  收藏  举报

导航