专题-经典排序算法
经典排序算法
1. 冒泡排序
依次比较相邻两元素大小。如果按升序排列,较大元素放后面;如果按降序排列较小元素放后面。
void BubbleSort(int *arr, int n){ if(arr==NULL) return; for(int i=0; i<n-1; i++){ for(j=0; j<n-1-i; j++){ if(arr[j]>arr[j+1]){ //升序排列 int temp=arr[j]; arr[j]=arr[j+1]; arr[j+1]=temp; } } } return; }
时间复杂度分析:
最佳情况:O(n)原始数组按序排列
最差情况:O(n^2)原始数组逆序排列
2. 选择排序
和冒泡排序思路几乎一样,唯一区别不是交换相邻两元素而是找出当前未排序序列的最小(大)元素,与当前未排序序列第一个元素交换。
void SelectionSort(int *arr, int n){ if(arr==NULL) return; for(int i=n-1; i>=0; i--){ int maxIndex=i; for(j=i; j>=0; j--){ if(arr[j]>arr[maxIndex]) //升序排列 maxIndex=j; } int temp=arr[i]; arr[i]=arr[maxIndex]; arr[maxIndex]=temp; } return; }
时间复杂度分析:
O(n^2)
3. 插入排序
第一个元素默认是排序的,以后每选择一个元素插入排序中。
void InsertSort(int *arr, int n){ if(arr==NULL) return; for(int i=0; i<n; i++){ int curVal=arr[i]; int preIndex=i-1; while(preIndex>=0&&curVal<arr[preIndex]){ arr[preIndex+1]=arr[preIndex]; preIndex--; } arr[preIndex+1]=curVal; } return; }
时间复杂度分析:
最佳情况:O(n)原始数组有序排列
最坏情况:O(n^2)原始数组逆序排列
4. 希尔排序(递减增量排序算法)
希尔排序是对插入排序的改进。插入排序对几乎已经拍好序的数据操作时,效率高,即可达到线性排序的效率;希尔排序是将待排序序列分割成若干子序列分别进行直接插入排序,待整个序列基本有序时,再对整体进行直接插入排序。
void ShellSort(int *arr, int n){ if(arr==NULL) return; int gap=n; //间隔 while(true){ //每一步都是一个插入排序 gap=gap/2; for(int i=0; i<gap; i++){ for(int j=i+gap; j<n; j++){ int temp=arr[j]; int m; for(m=i-gap; m>=0 && arr[m]>temp; m=m-gap){ arr[m+gap]=arr[m]; } arr[m+gap]=temp; } } if(gap==1) break; } return; }
时间复杂度分析:
O(nlogn)
5. 快速排序
随机选择数组中的一个数,比选择的数字小的数字移到数组左边,比选择数字大的移到数组右边。
void swap(int *a, int* b){ //交换两个数字 int temp; temp=*a; *a=*b; *b=temp; return; } void QuickSort(int data[], int start, int end){ if(start>=end) return; int temp; //待排序的第最后一个元素作为基准元素 int small=start-1; for(int index=start; index<end; index++){ if(data[index]<data[end]){ small++; if(small!=index) swap(data[small], data[index]); } } small++; swap(data[small], data[end]); QuickSort(data[], start, small-1); QuickSort(data[],small+1, end); return; }
时间复杂度分析:
最佳情况:O(nlogn)每次正好中分
最差情况:分为1个元素和其它元素两部分
6. 归并排序
包括分解和合并两部分
合并相邻有序子序列
(此图转载自博客:https://www.cnblogs.com/linjiaxin/p/7615196.html)
void MergeSortCore(int *data, int* copy, int start, int end){ if(start==end){ copy[start]=data[start]; return; } int length=(start+end)/2; MergeSortCore(copy, data, start, start+length); //分解 MergeSortCore(copy, data, start+length+1, end); int i=start+length; int j=end; int indexCopy=end; while(i>=start&& j>=start+length+1){ //合并 if(data[i]>data[j]) copy[indexCopy--]=data[i--]; else copy[indexCopy--]=data[j--]; } for(; i>=start; --i) copy[indexCopy--]=data[i]; for(; j>=start; --j) copy[indexCopy--]=data[j]; return; } void MergeSort(int *data, int length){ if(data==NULL|| length<0) return; int* copy=new int[length]; for(int i=0; i<length; i++) copy[i]=data[i]; MergeSortCore(data, copy, 0, length-1); delete[] copy; return; }
时间复杂度分析:
O(nlogn)
7. 堆排序:
堆排序,就是以堆的方式去排序,什么是堆呢?满足对任意一个父节点不大(小)于其子节点的完全二叉树。大根堆用于升序排列,小根堆用于将序排列。
步骤:
将原始数组构建成大(小)根堆;
将堆顶元素和最后一个元素进行交换,得到新的无序数组区域和新的有序数组区域;
重新调整无序数组区域为大(小)根堆,将堆顶元素和最后一个元素进行交换,直到排列完
(对原理讲解的很不错的博客:https://blog.csdn.net/u013384984/article/details/79496052)
void swap(int &a, int &b){ int temp=a; a=b; b=temp; } void AdjustHeap(int arr[], int nodeIndex, int n){ int leftChild=2*nodeIndex+1; int rightChild=2*nodeIndex+2; int maxIndex=nodeIndex; if(leftChild<n && arr[maxIndex]<arr[leftChild]) maxIndex=leftChild; if(rightChild<n && arr[maxIndex]<arr[rightChild]) maxIndex=rightChild; if(maxIndex!=nodeIndex){ swap(arr[maxIndex], arr[nodeIndex]); adjustHeap(arr, maxIndex, n); } } //大根堆,升序排列 void HeapSort(int arr[], int n){ if(n==1) //直到无序区的元素个数为1 return; //最后一个非叶子节点编号为n/2-1,从0开始计数 for(int i=n/2-1; i>=0; i--) AdjustHeap(arr, i, n); //调整为大根堆 swap(arr[0], nums[n-1]); //将堆顶与最后一个元素交换 HeapSort(arr, n-1); //排序数组减一 }
时间复杂度分析:
O(nlogn)
8. 桶排序(基数排序)
找出最大的数
将所有待比较的数统一为同样的位数长度,较短前面补0
从最低位开始,依次进行一次排序
int maxBit(int data[], int n){ int maxData=data[0]; for(int i=1; i<n; i++){ if(maxData<data[i]) maxData=data[i]; } int maxn=0; while(maxData!=0){ maxData/=10; maxn++; } return maxn; } void BucketSort(int data[], int n){ int maxn=maxBit(data, n); //最大位数 int temp=new int[n]; //临时存储 int *count=new int[10]; //10个桶0-9 int i,j,k; int radix=1; for(i=1; i<=maxn; i++){ //进行maxn次排序 for(j=0; j<10; j++)//每次排序前初始化为0 count[j]=0; for(j=0; j<n; j++){ //桶统计 k=(data[j]/radix)%10; count[k]++; } for(j=1; j<10; j++) count[i]+=count[i-1]; //将temp中的位置依次分配给各个桶 for(j=n-1; j>=0; j--){ //先占据后面的位置所以从后往前放。 k=(data[j]/radix)%10; temp[count[k]-1]=data[j]; //放入到分配的桶 count[k]--; //依次前移放入 } for(j=0; j<n; j++) //一次排序结果 data[j]=temp[j]; radix=radix*10; //调整桶基数 } delete []temp; delete []count; }
时间复杂度分析
O(p(n+b)),其中n是待排元素个数,b是桶个数,p是排序的次数