前言
查找和排序算法是算法的入门知识,其经典思想可以用于很多算法中因为其实现代码较短,应用较常见,所以在面试中经常会问到排序算法及其相关的问题。但万变不离其中,只要熟悉了思想,灵活运用也不是难事。
一般在面试中最常考的是快速排序和归并排序,并且经常有面试官要求现场写出这两种排序的代码,对这两种排序的代码一定要信手拈来。还有插入排序、冒泡排序、堆排序、基数排序、桶排序等。面试官对于这些排序可能会要求比较各自的优劣、各种算法的思想及其使用场景,还要会分析算法的时间和空间复杂度。通常查找和排序算法的考察是面试的开始,如果这些问题回答不好,估计面试官没有继续面试下去的兴趣。所以想开个好头就要把常见的排序算法及其特点要熟练掌握,有必要时要熟练写出代码。
插入排序不是通过交换位置而是通过比较找到合适的位置插入元素来达到排序的目的。原理就是找到一个合适的位置插入。举个例子,对5,3,8,6,4这个无序序列进行简单插入排序,首先假设第一个数的位置是正确的,想一下在拿到第一张牌的时候,没必要整理。然后3要插到5前面,把5后移一位,变成3,5,8,6,4。然后8不用动,6插在8前面,8后移一位,4插在5前面,从5开始都向后移一位。注意在插入一个数的时候要保证这个数前面的数已经有序。简单插入排序的时间复杂度也是O(n
^2)。
实现代码:
- #include <iostream>
- using namespace std;
-
- void swap(int arr[],int i,int j)
- {
- int temp=arr[i];
- arr[i]=arr[j];
- arr[j]=temp;
- }
-
- void InsertSort(int arr[],int n)
- {
- for(int i=1; i<n; i++)
- {
- for(int j=i; j>0; j--)
- {
- if(arr[j]<arr[j-1])
- swap(arr,j,j-1);
- else
- break;
- }
- }
- }
-
- int main()
- {
- int arr[5]= {5,3,8,6,4};
- InsertSort(arr,5);
- for(int i=0; i<5; i++)
- {
- cout<<arr[i]<<" ";
- }
- cout<<endl;
- return 0;
- }
冒泡排序
冒泡排序是最简单的排序之一了,大体思想就是通过与相邻元素的比较和交换来把小的数交换到最前面。这个过程类似于水泡向上升一样,因此而得名。举个例子,对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)。
实现代码:
- #include <iostream>
- using namespace std;
-
- void swap(int arr[], int i, int j)
- {
- int temp = arr[i];
- arr[i] = arr[j];
- arr[j] = temp;
- }
-
- void BubbleSort(int arr[], int n)
- {
- for (int i = 0; i < n - 1; i++)
- {
- for (int j = n - 1; j > i; j--){
- if (arr[j] < arr[j - 1])
- swap(arr, j, j - 1);
- }
- }
- }
-
- int main(){
- int arr[5] = { 5, 3, 6, 4, 8 };
- BubbleSort(arr, 5);
- for (int i = 0; i < 5; i++){
- cout << arr[i] << " ";
- }
- return 0;
- }
选择排序
选择排序的思想其实和冒泡排序有点类似,都是在一次排序后把最小的元素放到最前面。但是过程不同,冒泡排序是通过相邻的比较和交换,而选择排序是通过对整体的选择。举个例子,对5,3,8,6,4这个无序序列进行简单排序,首先要选择5以外的最小数来和5交换,也就是选择3和5交换,一次排序后就变成了3,5,8,6,4.对剩下的序列依次进行选择和交换,最终就会得到一个有序序列。其实选择排序可以看成是冒泡排序的优化,因为其目的相同,只是选择排序只有在确定了最小数的前提下才进行交换,大大减少了交换的次数。选择排序的时间复杂度为O(n
^2)。选择排序不依赖原始数组的输入顺序。
实现代码:
- #include <iostream>
- using namespace std;
-
- void swap(int arr[],int i,int j)
- {
- int temp=arr[i];
- arr[i]=arr[j];
- arr[j]=temp;
- }
-
- void SelectSort(int arr[],int n)
- {
- for(int i=0;i<n-1;i++)
- {
- int smallest=i;
- for(int j=i+1;j<n;j++)
- {
- if(arr[smallest]>arr[j])
- {
- smallest=j;
- }
- }
- swap(arr,i,smallest);
- }
- }
-
- int main()
- {
- int arr[5]={5,3,8,6,4};
- SelectSort(arr,5);
- for(int i=0;i<5;i++)
- {
- cout<<arr[i]<<" ";
- }
- return 0;
- }
快速排序
在实际应用中快速排序是表现最好的排序算法。快速排序来自冒泡排序,冒泡排序是通过相邻元素的比较和交换把最小的冒泡到最顶端,而快速排序是比较和交换小数和大数,这样一来不仅把小数冒泡到上面同时也把大数沉到下面。
举个例子,对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)。
实现代码:
- #include <iostream>
- using namespace std;
-
- void QuickSort(int a[],int low,int high)
- {
- if(low>=high)
- {
- return;
- }
- int first=low;
- int last=high;
- int key=a[first];
- while(first<last)
- {
- while(first<last&&a[last]>=key)
- {
- --last;
- }
- a[first]=a[last];
- while(first<last&&a[first]<=key)
- {
- ++first;
- }
- a[last]=a[first];
- }
- a[first]=key;
- QuickSort(a,low,first-1);
- QuickSort(a,first+1,high);
- }
-
- int main()
- {
- int a[]={5,3,6,4,8};
- QuickSort(a,0,sizeof(a) / sizeof(a[0]) - 1);
- for(int i=0;i<sizeof(a) / sizeof(a[0]);i++)
- {
- cout<<a[i]<<" ";
- }
- return 0;
- }
-
-
-
总结快速排序的思想:冒泡+二分+递归分治,慢慢体会。。。
希尔排序
希尔排序是插入排序的一种高效率的实现,也叫缩小增量排序。简单的插入排序中,如果待排序列是正序时,时间复杂度是O(n),如果序列是基本有序的,使用直接插入排序效率就非常高。希尔排序就利用了这个特点。基本思想:先将整个待排记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录基本有序时再对全体记录进行一次直接插入排序。
举个例子:
实现代码:
- #include <iostream>
- using namespace std;
-
- void swap(int a[],int i,int j)
- {
- int temp=a[i];
- a[i]=a[j];
- a[j]=temp;
- }
-
- void shellSort(int a[],int n)
- {
- int i,j,gap;
-
- for(gap=n/2;gap>0;gap/=2)
- for(i=gap;i<n;i++)
- for(j=i-gap;j>=0&&a[j]>a[j+gap];j-=gap)
- swap(a,j,j+gap);
- }
-
- int main()
- {
- int a[]={5,3,6,4,8};
- shellSort(a,sizeof(a)/sizeof(a[0])-1);
- for(int i=0;i<sizeof(a)/sizeof(a[0]);i++)
- {
- cout<<a[i]<<" ";
- }
- return 0;
- }
归并排序
归并排序是另一种不同的排序方法,因为归并排序使用了递归分治的思想,所以理解起来比较容易。其基本思想是,先递归划分子问题,然后合并结果。把待排序列看成由两个有序的子序列,然后合并两个子序列,然后把子序列看成两个有序序列。倒着来看,其实就是先两两合并,然后四四合并,最终形成有序序列。空间复杂度为O(n),时间复杂度为O(nlogn)。
举个例子:
实现代码:
- #include<iostream>
- using namespace std;
-
-
- void Merge(int r[],int r1[],int s,int m,int t)
- {
- int i=s;
- int j=m+1;
- int k=s;
- while(i<=m&&j<=t)
- {
- if(r[i]<=r[j])
- r1[k++]=r[i++];
- else
- r1[k++]=r[j++];
- }
- if(i<=m)
- while(i<=m)
- r1[k++]=r[i++];
- else
- while(j<=t)
- r1[k++]=r[j++];
- for(int n=s; n<=t; n++)
- r[n]=r1[n];
- }
-
-
- void MergeSort(int r[],int r1[],int s,int t)
- {
- if(s<t)
- {
- int m=(s+t)/2;
- MergeSort(r,r1,s,m);
- MergeSort(r,r1,m+1,t);
- Merge(r,r1,s,m,t);
- }
- }
-
- int main()
- {
- int r[8]= {10,3,5,1,9,34,54,565};
- int r1[8];
- MergeSort(r,r1,0,7);
- for(int q=0; q<8; q++)
- cout<<r[q]<<" ";
- return 0;
- }