常用的一些排序算法

前言

查找和排序算法是算法的入门知识,其经典思想可以用于很多算法中因为其实现代码较短,应用较常见,所以在面试中经常会问到排序算法及其相关的问题。但万变不离其中,只要熟悉了思想,灵活运用也不是难事。

一般在面试中最常考的是快速排序和归并排序,并且经常有面试官要求现场写出这两种排序的代码,对这两种排序的代码一定要信手拈来。还有插入排序、冒泡排序、堆排序、基数排序、桶排序等。面试官对于这些排序可能会要求比较各自的优劣、各种算法的思想及其使用场景,还要会分析算法的时间和空间复杂度。通常查找和排序算法的考察是面试的开始,如果这些问题回答不好,估计面试官没有继续面试下去的兴趣。所以想开个好头就要把常见的排序算法及其特点要熟练掌握,有必要时要熟练写出代码。

插入排序

插入排序不是通过交换位置而是通过比较找到合适的位置插入元素来达到排序的目的。原理就是找到一个合适的位置插入。举个例子,对5,3,8,6,4这个无序序列进行简单插入排序,首先假设第一个数的位置是正确的,想一下在拿到第一张牌的时候,没必要整理。然后3要插到5前面,把5后移一位,变成3,5,8,6,4。然后8不用动,6插在8前面,8后移一位,4插在5前面,从5开始都向后移一位。注意在插入一个数的时候要保证这个数前面的数已经有序。简单插入排序的时间复杂度也是O(n ^2)。
实现代码:
[cpp]  view plain  copy
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. void swap(int arr[],int i,int j)    //交换函数,交换数组中两个元素的位置  
  5. {  
  6.     int temp=arr[i];  
  7.     arr[i]=arr[j];  
  8.     arr[j]=temp;  
  9. }  
  10.   
  11. void InsertSort(int arr[],int n)  
  12. {  
  13.     for(int i=1; i<n; i++)  
  14.     {  
  15.         for(int j=i; j>0; j--)      //从i=1开始  
  16.         {  
  17.             if(arr[j]<arr[j-1])     //如果arr[j]<arr[j-1],则交换两个值  
  18.                 swap(arr,j,j-1);  
  19.             else  
  20.                 break;  
  21.         }  
  22.     }  
  23. }  
  24.   
  25. int main()  
  26. {  
  27.     int arr[5]= {5,3,8,6,4};  
  28.     InsertSort(arr,5);  
  29.     for(int i=0; i<5; i++)  
  30.     {  
  31.         cout<<arr[i]<<" ";  
  32.     }  
  33.     cout<<endl;  
  34.     return 0;  
  35. }  

冒泡排序

冒泡排序是最简单的排序之一了,大体思想就是通过与相邻元素的比较和交换来把小的数交换到最前面。这个过程类似于水泡向上升一样,因此而得名。举个例子,对5,3,8,6,4这个无序序列进行冒泡排序。首先从后向前冒泡,4和6比较,把4交换到前面,序列变成5,3,8,4,6.同理4和8交换,变成5,3,4,8,6。3和4无需交换。5和3交换,变成3,5,4,8,6。这样一次冒泡就完了,把最小的数3排到最前面了。对剩下的序列依次冒泡就会得到一个有序序列。冒泡排序的时间复杂度为O(n ^2)。
实现代码:
[cpp]  view plain  copy
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. void swap(int arr[], int i, int j)//交换函数,交换数组中两个元素的位置  
  5. {  
  6.     int temp = arr[i];  
  7.     arr[i] = arr[j];  
  8.     arr[j] = temp;  
  9. }  
  10.   
  11. void BubbleSort(int arr[], int n)  
  12. {  
  13.     for (int i = 0; i < n - 1; i++)  
  14.     {  
  15.         for (int j = n - 1; j > i; j--){   //从后往前进行,相邻两个数进行比较,如果arr[j] < arr[j - 1]就交换  
  16.             if (arr[j] < arr[j - 1])  
  17.                 swap(arr, j, j - 1);  
  18.         }  
  19.     }  
  20. }  
  21.   
  22. int main(){  
  23.     int arr[5] = { 5, 3, 6, 4, 8 };  
  24.     BubbleSort(arr, 5);  
  25.     for (int i = 0; i < 5; i++){  
  26.         cout << arr[i] << "  ";  
  27.     }  
  28.     return 0;  
  29. }  

选择排序

选择排序的思想其实和冒泡排序有点类似,都是在一次排序后把最小的元素放到最前面。但是过程不同,冒泡排序是通过相邻的比较和交换,而选择排序是通过对整体的选择。举个例子,对5,3,8,6,4这个无序序列进行简单排序,首先要选择5以外的最小数来和5交换,也就是选择3和5交换,一次排序后就变成了3,5,8,6,4.对剩下的序列依次进行选择和交换,最终就会得到一个有序序列。其实选择排序可以看成是冒泡排序的优化,因为其目的相同,只是选择排序只有在确定了最小数的前提下才进行交换,大大减少了交换的次数。选择排序的时间复杂度为O(n ^2)。选择排序不依赖原始数组的输入顺序。
实现代码:
[csharp]  view plain  copy
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. void swap(int arr[],int i,int j)  
  5. {  
  6.     int temp=arr[i];  
  7.     arr[i]=arr[j];  
  8.     arr[j]=temp;  
  9. }  
  10.   
  11. void SelectSort(int arr[],int n)  
  12. {  
  13.     for(int i=0;i<n-1;i++)           //只需要比较n-1次  
  14.     {  
  15.         int smallest=i;  
  16.         for(int j=i+1;j<n;j++)       //从i+1开始比较,因为smallest最小数默认为i,i就没必要比较了  
  17.         {  
  18.             if(arr[smallest]>arr[j])  
  19.             {  
  20.                 smallest=j;  
  21.             }  
  22.         }  
  23.         swap(arr,i,smallest);  
  24.     }  
  25. }  
  26.   
  27. int main()  
  28. {  
  29.     int arr[5]={5,3,8,6,4};  
  30.     SelectSort(arr,5);  
  31.     for(int i=0;i<5;i++)  
  32.     {  
  33.         cout<<arr[i]<<" ";  
  34.     }  
  35.     return 0;  
  36. }  
 

快速排序

在实际应用中快速排序是表现最好的排序算法。快速排序来自冒泡排序,冒泡排序是通过相邻元素的比较和交换把最小的冒泡到最顶端,而快速排序是比较和交换小数和大数,这样一来不仅把小数冒泡到上面同时也把大数沉到下面。
举个例子,对5,3,8,6,4这个无序序列进行快速排序,思路是右指针找比基准数小的,左指针找比基准数大的,交换之。
5,3,8,6,4用5作为比较的基准,最终会把比5小的移动到5的左边,比5大的移动到5的右边。
5,3,8,6,4首先设置i,j两个指针分别指向两端,j指针先扫描,4比5小,停止。然后i扫描,8比5大,停止。交换i,j位置。
5,3,4,6,8一次划分后达到了左边比5小,右边比5大的目的。之后对左右子序列递归排序,最终得到有序序列。
快速排序是不稳定的,其平均时间复杂度是O(nlgn)。
实现代码:
[cpp]  view plain  copy
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. void QuickSort(int a[],int low,int high)  
  5. {  
  6.     if(low>=high)  
  7.     {  
  8.         return;  
  9.     }  
  10.     int first=low;  
  11.     int last=high;  
  12.     int key=a[first]; //第一个数组元素作为基准  
  13.     while(first<last)  
  14.     {  
  15.         while(first<last&&a[last]>=key)  
  16.         {  
  17.             --last;  
  18.         }  
  19.         a[first]=a[last]; //将比基准小的移动到低端  
  20.         while(first<last&&a[first]<=key)  
  21.         {  
  22.             ++first;  
  23.         }  
  24.         a[last]=a[first]; //将比基准大的移动到高端  
  25.     }  
  26.     a[first]=key;       //将基准移到最后的正确位置  
  27.     QuickSort(a,low,first-1);  
  28.     QuickSort(a,first+1,high);  
  29. }  
  30.   
  31. int main()  
  32. {  
  33.     int a[]={5,3,6,4,8};  
  34.     QuickSort(a,0,sizeof(a) / sizeof(a[0]) - 1);//这里原文第三个参数要减1否则内存越界  
  35.     for(int i=0;i<sizeof(a) / sizeof(a[0]);i++)  
  36.     {  
  37.         cout<<a[i]<<" ";  
  38.     }  
  39.     return 0;  
  40. }  
  41. /*sizeof(a)是一个数组总共占用的字节数 
  42. sizeof(a[0])是一个元素占的字节数, 
  43. sizeof(a)/sizeof(a[0])就是这个数组的长度*/  
总结快速排序的思想:冒泡+二分+递归分治,慢慢体会。。。

希尔排序

希尔排序是插入排序的一种高效率的实现,也叫缩小增量排序。简单的插入排序中,如果待排序列是正序时,时间复杂度是O(n),如果序列是基本有序的,使用直接插入排序效率就非常高。希尔排序就利用了这个特点。基本思想:先将整个待排记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录基本有序时再对全体记录进行一次直接插入排序。
举个例子:

实现代码:
[cpp]  view plain  copy
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. void swap(int a[],int i,int j)  
  5. {  
  6.     int temp=a[i];  
  7.     a[i]=a[j];  
  8.     a[j]=temp;  
  9. }  
  10.   
  11. void shellSort(int a[],int n)  
  12. {  
  13.     int i,j,gap;  
  14.   
  15.     for(gap=n/2;gap>0;gap/=2)  
  16.     for(i=gap;i<n;i++)  
  17.     for(j=i-gap;j>=0&&a[j]>a[j+gap];j-=gap)  
  18.     swap(a,j,j+gap);  
  19. }  
  20.   
  21. int main()  
  22. {  
  23.     int a[]={5,3,6,4,8};  
  24.     shellSort(a,sizeof(a)/sizeof(a[0])-1);  
  25.     for(int i=0;i<sizeof(a)/sizeof(a[0]);i++)  
  26.     {  
  27.         cout<<a[i]<<" ";  
  28.     }  
  29.     return 0;  
  30. }  

归并排序

归并排序是另一种不同的排序方法,因为归并排序使用了递归分治的思想,所以理解起来比较容易。其基本思想是,先递归划分子问题,然后合并结果。把待排序列看成由两个有序的子序列,然后合并两个子序列,然后把子序列看成两个有序序列。倒着来看,其实就是先两两合并,然后四四合并,最终形成有序序列。空间复杂度为O(n),时间复杂度为O(nlogn)。
举个例子:

实现代码:
[cpp]  view plain  copy
  1. #include<iostream>  
  2. using namespace std;  
  3.   
  4. //归并过程--将两个有序数组合并成一个有序数组  
  5. void Merge(int r[],int r1[],int s,int m,int t)  
  6. {  
  7.     int i=s;  
  8.     int j=m+1;  
  9.     int k=s;  
  10.     while(i<=m&&j<=t)  
  11.     {  
  12.         if(r[i]<=r[j])  
  13.             r1[k++]=r[i++];  
  14.         else  
  15.             r1[k++]=r[j++];  
  16.     }  
  17.     if(i<=m)  
  18.         while(i<=m)  
  19.             r1[k++]=r[i++];  
  20.     else  
  21.         while(j<=t)  
  22.             r1[k++]=r[j++];  
  23.     for(int n=s; n<=t; n++)  
  24.         r[n]=r1[n];  
  25. }  
  26.   
  27. //两路归并排序--r[]为待排数组,r1[]为临时数组,s和t分别是数组两端*  
  28. void MergeSort(int r[],int r1[],int s,int t)  
  29. {  
  30.     if(s<t)  
  31.     {  
  32.         int m=(s+t)/2;  
  33.         MergeSort(r,r1,s,m);  
  34.         MergeSort(r,r1,m+1,t);  
  35.         Merge(r,r1,s,m,t);  
  36.     }  
  37. }  
  38.   
  39. int main()  
  40. {  
  41.     int r[8]= {10,3,5,1,9,34,54,565};  
  42.     int r1[8];  
  43.     MergeSort(r,r1,0,7);  
  44.     for(int q=0; q<8; q++)  
  45.         cout<<r[q]<<" ";  
  46.     return 0;  
  47. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值