常见的排序算法总览
算法名 | 时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|
冒泡排序 | O(N2) | O(1) | 不稳定 |
选择排序 | O(N2) | O(1) | 不稳定 |
插入排序 | O(N2) | O(1) | 稳定 |
归并排序 | O(Nlogn) | O(N) 可以优化到 O(1) | 稳定 |
快速排序 | O(Nlogn) | O(Logn−n) | 不稳定 |
堆排序 | O(Nlogn) | O(1) | 不稳定 |
希尔排序 | O(Nlogn) | O(1) | 不稳定 |
计数排序 | O(N) | O(M) | 稳定 |
基数排序 | O(N) | O(M) | 稳定 |
其中基数排序和基数排序都是基于桶原理的排序(其他排序方法基于比较的排序), 其空间复杂度 O(M) 其中, M为桶的个数。 稳定性指的是在排序过程中相同元素的顺序是否会被改变,比如, 在一个数组中出现两个连续的元素{…, 2(a), 2(b), …},若经过排序后这连个元素的顺序被改变为{…, 2(b), 2(a), …},则该排序算法是不稳定的。
1 冒泡排序:
在[0, n-1]的区间内,第一个数和第二个数比较, 如果前面的数比后面大就交换顺序, 然后第二再和第三比较, 依次比较下去, 那么最大的数就会被放在最后面。然后再对[0, n-2]区间进行相同的操作。冒泡排序的迭代是从后向前的, 数组的后i个元素会被优先排序。
class BubbleSort {
public:
int* bubbleSort(int* A, int n) {
for(int i=0;i<n;i++)
{//通过这里的j=n-1-i来实现从[0,n-1]到[0, n-i]的迭
for(int j=0;j<n-1-i;j++)
{
if(A[j]>A[j+1])
{
swap(A[j],A[j+1]);
}
}
}
return A;
}
};
2 选择排序:
在范围[0, n-1]上找到一个最小值, 并把它放在位置0上,然后在[1, n-1]的范围选出最小值放在位置1上,依次进行直到只剩下一个数,排序就完成了。注意选择排序的迭代是向后缩小排序范围的, 也就是前i个元素会优先排序, 着刚好与冒泡排序相反。
class SelectionSort {
public:
int* selectionSort(int* A, int n) {
int min_index;
for(int i=0;i<n;i++)
{
min_index = i;
for(int j=i+1;j<n;j++)
{
min_index = A[min_index]>A[j] ? j: min_index;//不断比较直到找到最小值
}
swap(A, i, min_index);
}
return A;
}
void swap(int* A, int m, int n)
{
int temp;
temp = A[m];
A[m]=A[n];
A[n]=temp;
}
};
3 插入排序:
首先是位置0上的数和位置1上的数进行比较,如果位置1上的数更小就和位置0上的数进行交换, 接下来就看位置2上的数,和位置1上的数进行比较,如果位置2上的数小就和1上的数进行交换。交换之后位置1上的数再和位置0上的数进行比较,如果还是跟小,就和位置0上的数进行交换。
class InsertionSort {
public:
int* insertionSort(int* A, int n) {
int finger;
for(int i=0;i<n;i++){
finger=i;
while(finger>0){
if(A[finger-1]>A[finger]){
swap(A, finger-1,finger);
}
finger--;
}
}
return A;
}
void swap(int* A, int index1, int index2){
int temp;
temp=A[index1];
A[index1]=A[index2];
A[index2]=temp;
}
};
4 归并排序:
让数组中的每一个数单独成为长度为1的有序区间,然后把相邻的长度为1 的有序区间进行合并得到长度为2的有序区间。 然后再把相邻有序区间进行合并,得到最大长度为4的有序区间, 直到让数组里所有的数合并为一个有序区间,排序结束。每个合并的过程可以通过两个indexes 来实现。
//归并排序的递归实现
class MergeSort {
public:
int* mergeSort(int* A, int n) {
division(A, 0, n-1);
return A;
}
void division(int* A, int left, int right){
if(left==right){
return;
}
int mid = (left+right)/2;
division(A, left, mid);
division(A, mid+1, right);
merge(A, left, mid, right);
}
void merge(int* A, int left, int mid, int right){
vector<int> temp(right-left+1);
int l=left;
int r=mid+1;
int index=0;//how to make it better? more precise?
while(l<=mid && r<=right){
temp[index++]=(A[l]<A[r])? A[l++]:A[r++];
}
while(l<=mid){
temp[index++]=A[l++];
}
while(r<=right){
temp[index++]=A[r++];
}
for(int i=0;i<temp.size();i++){
A[left+i]=temp[i];
}
}
};
5 快速排序:
#include<stdlib.h>
class QuickSort {
public:
int* quickSort(int* A, int n) {
division(A, 0,n-1);
return A;
}
void division(int* A, int left, int right){
if(left<right){
int rd = left+(int)(rand()%(right-left+1));
swap(A, rd, right);
int mid = partition(A, left, right);
division(A, left, mid);
division(A, mid+1, right);
}
}
void swap(int* A, int index1, int index2){
int temp;
temp = A[index1];
A[index1]=A[index2];
A[index2]=temp;
}
int partition(int* A, int left, int right){
int initial = left-1;
int index = left;
while(index <= right){
if(A[index]<=A[right]){
swap(A, ++initial,index);
}
index++;
}
return initial;
}
};
6 堆排序:
先把数组建立为一个大小为n的大根堆, 堆顶是整个数组的最大值, 将堆顶元素和堆得最后一个数进行交换,然后把最大值脱离出整个堆结构,放在数组最后的位置。然后对前面n-1大小的堆,从堆顶位置进行大根堆的调整,和上一次步骤相同,分离出最大值。直到只剩下一个数,排序过程结束。
class HeapSort {
public:
void swap(int* A, int i, int j){
int temp = A[i];
A[i]=A[j];
A[j]=temp;
}
void maxHeap(int* A, int n, int init, int end){
int parent = init, child = 2*parent+1;
int val=A[parent];
while(child<=end){
if(child<end && A[child]<A[child+1])
child++;
if(val<A[child]){
A[parent]=A[child];
parent=child;
child=2*parent +1;
}
else break;
}
A[parent] = val;
}
int* heapSort(int* A, int n) {
for(int i=n/2-1;i>=0;i--){
maxHeap(A,n,i,n-1);
}
for(int i=n-1;i>0;i--){
swap(A,0,i);
maxHeap(A, n,0,i-1);
}
return A;
}
};
7 希尔排序:
改良的排序算法,改变了插入排序的步长。
class ShellSort {
public:
void swap(int* A, int i, int j){
int temp=A[i];
A[i]=A[j];
A[j]=temp;
}
int* shellSort(int* A, int n) {
for(int step = n/2;step>0;step = step/2){
int index = 0;
//i=step for the first case
for(int i = step ;i<n;i++){
index = i;
while(index>0){
if((A[index]<A[index-step]) && (index-step>=0)){
swap(A,index,index-step);
index = index - step;
}
else{
break;
}
}
}
}
return A;
}
};
8 计数排序:
先建立排序区间内的桶,然后让排序数组里的数对应进桶,然后从最小桶开始依次倒出,倒出顺序就是排序顺序。
#include<vector>
class CountingSort {
public:
int* countingSort(int* A, int n) {
int max_ele=0, min_ele=A[0];
for(int i=0;i<n;i++){
if(A[i]>max_ele) max_ele = A[i];
if(A[i]<min_ele) min_ele = A[i];
}
int len = max_ele - min_ele +1;
vector<vector<int>> ans(len);
for(int i=0; i<n;i++){
ans[A[i]-min_ele].push_back(A[i]);
}
int count = 0;
for(int i = 0; i < len; i++){
if(!ans[i].empty()){
for(vector<int>::iterator itr=ans[i].begin();itr !=ans[i].end();++itr){
A[count++] = *itr;
}
}
}
return A;
}
};
9 基数排序:
根据个位数入桶,然后依次倒出;再根据十位数入桶,依次倒出,直到排序数组的最高位入桶并倒出后排序结束。
class RadixSort {
public:
int* radixSort(int* A, int n) {
for(int i=0;i<=4;i++){
distribute(A, n, i);
}
return A;
}
void distribute(int* A, int n, int count){
queue<int> ans[10];
for(int i=0;i<n;i++){
int num=A[i]/pow(10,count);
ans[num%10].push(A[i]);
}
int index =0;
for(int i=0;i<10;i++){
while(!ans[i].empty()){
A[index]=ans[i].front();
ans[i].pop();
index++;
}
}
}
};