常见排序算法复杂度
排序方法 | 平均情况 | 最好情况 | 最坏情况 | 辅助空间 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | O(n2) | O(n) | O(n2) | O(1) | 稳定 |
简单选择排序 | O(n2) | O(n2) | O(n2) | O(1) | 不稳定 |
直接插入排序 | O(n2) | O(n) | O(n2) | O(1) | 稳定 |
希尔排序 | O(nlogn)~O(n2) | O(n1.3) | O(n2) | O(1) | 不稳定 |
堆排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(1) | 不稳定 |
归并排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(n) | 稳定 |
快速排序 | O(nlogn) | O(nlogn) | O(n2) | O(logn)~O(n) | 不稳定 |
算法的稳定性
有一点我们很容易忽略的是排序算法的稳定性。
排序算法稳定性的简单形式化定义为:如果Ai = Aj,排序前Ai在Aj之前,排序后Ai还在Aj之前,则称这种排序算法是稳定的。通俗地讲就是保证排序前后两个相等的数的相对顺序不变。
对于不稳定的排序算法,只要举出一个实例,即可说明它的不稳定性;而对于稳定的排序算法,必须对算法进行分析从而得到稳定的特性。需要注意的是,排序算法是否为稳定的是由具体算法决定的,不稳定的算法在某种条件下可以变为稳定的算法,而稳定的算法在某种条件下也可以变为不稳定的算法。
例如,对于冒泡排序,原本是稳定的排序算法,如果将记录交换的条件改成A[i] >= A[i + 1],则两个相等的记录就会交换位置,从而变成不稳定的排序算法。
其次,说一下排序算法稳定性的好处。排序算法如果是稳定的,那么从一个键上排序,然后再从另一个键上排序,前一个键排序的结果可以为后一个键排序所用。基数排序就是这样,先按低位排序,逐次按高位排序,低位排序后元素的顺序在高位也相同时是不会改变的
希尔排序
void shellSort(vector<int> &nums){
int gap=0;
int i=0,j=0;
for(gap=nums.size()/2;gap>=1;gap/=2){
for(i=gap;i<nums.size();i++){
for(j=i-gap;j>=0&&nums[j+gap]<nums[j];j=j-gap)
swap(nums[j+gap],nums[j]);
}
}
}
归并排序
//迭代版
int min(int x, int y) {
return x < y ? x : y;
}
void merge_sort1(vector<int> &nums){
vector<int> a=nums;
vector<int> b(nums.size(),0);
int seg,start;
for(seg=1;seg<nums.size();seg+=seg){
for(start=0;start<nums.size();start+=seg+seg){
int low=start,mid=min(start+seg,nums.size());
int high=min(start+seg+seg,nums.size());
int k=low;
int start1=low,end1=mid;
int start2=mid,end2=high;
while(start1<end1&&start2<end2)
b[k++]=a[start1]<a[start2]?a[start1++]:a[start2++];
while(start1<end1)
b[k++]=a[start1++];
while(start2<end2)
b[k++]=a[start2++];
}
vector<int> temp=a;
a=b;
b=temp;
}
nums=a;
}
//递归版
void merge_sort2(vector <int> &nums,vector <int>& temp,int start,int end){
if(start>=end) return;
int len=end-start,mid=(len>>1)+start;
int start1=start,end1=mid;
int start2=mid+1,end2=end;
merge_sort2(nums,temp,start1,end1);
merge_sort2(nums,temp,start2,end2);
int k=start;
while(start1<=end1&&start2<=end2)
temp[k++] = nums[start1] < nums[start2] ? nums[start1++] : nums[start2++];
while(start1<=end1)
temp[k++]=nums[start1++];
while(start2<=end2)
temp[k++]=nums[start2++];
nums=temp;
}
快速排序
int partition(vector<int> &nums,int left,int right){
int i=left,j=right;
int temp=nums[left];
while(i<j){
while(nums[i]<=temp&&i<=j)
i++;
while(nums[j]>=temp&&i<=j)
j--;
if(i<j)
swap(nums[i],nums[j]);
}
nums[left]=nums[i-1];
nums[i-1]=temp;
return i-1;
}
void quick_sort(vector<int> &nums,int left,int right){
if(left<right){
int i=partition(nums,left,right);
quick_sort(nums,left,i-1);
quick_sort(nums, i+1,right);
}
}
堆排序
void adjust(vector<int>& nums,int len,int index){
int left=2*index+1; //左子节点
int right=2*index+2; //右子节点
int maxIndex=index;
if(left<len && nums[left]>nums[maxIndex]) maxIndex=left;
if(right<len && nums[right]>nums[maxIndex]) maxIndex=right;
if(maxIndex!=index){
swap(nums[maxIndex],nums[index]);
adjust(nums,len,maxIndex);
}
}
void heapSort(vector<int> &nums){
//构建大根堆
for(int i=nums.size()/2-1;i>=0;i--){
adjust(nums,nums.size(),i);
}
//调制小根堆
for(int i=nums.size()-1;i>=1;i--){
swap(nums[0],nums[i]);//把当其最大的放置到数组末尾
adjust(nums,i,0);
}
}
完整代码
#include<iostream>
#include<vector>
using namespace std;
void show(vector<int> nums){
if(nums.size()==0) return;
for(int i=0;i<nums.size();i++){
cout<<nums[i]<<" ";
}
cout<<endl;
}
void shellSort(vector<int> &nums){
int gap=0;
int i=0,j=0;
for(gap=nums.size()/2;gap>=1;gap/=2){
for(i=gap;i<nums.size();i++){
for(j=i-gap;j>=0&&nums[j+gap]<nums[j];j=j-gap)
swap(nums[j+gap],nums[j]);
}
}
}
int min(int x, int y) {
return x < y ? x : y;
}
void merge_sort1(vector<int> &nums){
vector<int> a=nums;
vector<int> b(nums.size(),0);
int seg,start;
for(seg=1;seg<nums.size();seg+=seg){
for(start=0;start<nums.size();start+=seg+seg){
int low=start,mid=min(start+seg,nums.size());
int high=min(start+seg+seg,nums.size());
int k=low;
int start1=low,end1=mid;
int start2=mid,end2=high;
while(start1<end1&&start2<end2)
b[k++]=a[start1]<a[start2]?a[start1++]:a[start2++];
while(start1<end1)
b[k++]=a[start1++];
while(start2<end2)
b[k++]=a[start2++];
}
vector<int> temp=a;
a=b;
b=temp;
}
nums=a;
}
void merge_sort2(vector <int> &nums,vector <int>& temp,int start,int end){
if(start>=end) return;
int len=end-start,mid=(len>>1)+start;
int start1=start,end1=mid;
int start2=mid+1,end2=end;
merge_sort2(nums,temp,start1,end1);
merge_sort2(nums,temp,start2,end2);
int k=start;
while(start1<=end1&&start2<=end2)
temp[k++] = nums[start1] < nums[start2] ? nums[start1++] : nums[start2++];
while(start1<=end1)
temp[k++]=nums[start1++];
while(start2<=end2)
temp[k++]=nums[start2++];
nums=temp;
}
int partition(vector<int> &nums,int left,int right){
int i=left,j=right;
int temp=nums[left];
while(i<j){
while(nums[i]<=temp&&i<=j)
i++;
while(nums[j]>=temp&&i<=j)
j--;
if(i<j)
swap(nums[i],nums[j]);
}
nums[left]=nums[i-1];
nums[i-1]=temp;
return i-1;
}
void quick_sort(vector<int> &nums,int left,int right){
if(left<right){
int i=partition(nums,left,right);
quick_sort(nums,left,i-1);
quick_sort(nums, i+1,right);
}
}
//堆排序
//构建大根堆
void adjust(vector<int>& nums,int len,int index){
int left=2*index+1; //左子节点
int right=2*index+2; //右子节点
int maxIndex=index;
if(left<len && nums[left]>nums[maxIndex]) maxIndex=left;
if(right<len && nums[right]>nums[maxIndex]) maxIndex=right;
if(maxIndex!=index){
swap(nums[maxIndex],nums[index]);
adjust(nums,len,maxIndex);
}
}
void heapSort(vector<int> &nums){
//构建大根堆
for(int i=nums.size()/2-1;i>=0;i--){
adjust(nums,nums.size(),i);
}
//调制小根堆
for(int i=nums.size()-1;i>=1;i--){
swap(nums[0],nums[i]);//把当其最大的放置到数组末尾
adjust(nums,i,0);
}
}
int main(void){
vector<int> nums={5,23,4,3,78,3,1,8,1,63,4,7};
vector<int> temp=nums;
show(nums);
heapSort(nums);
show(nums);
return 0;
}