经典的排序算法,复习用。
排序方法 | 平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | O(n*n) | O(n) | O(n*n) | O(1) | 稳定 |
选择排序 | O(n*n) | O(n*n) | O(n*n) | O(1) | 不稳定 |
插入排序 | O(n*n) | O(n) | O(n*n) | O(1) | 稳定 |
希尔排序 | O(n1.5) | s | s | s | 不稳定 |
堆排序 | O(nlgn) | O(nlgn) | O(nlgn) | O(1) | 不稳定 |
归并排序 | O(nlgn) | O(nlgn) | O(nlgn) | O(n) | 稳定 |
快速排序 | O(nlgn) | O(nlgn) | O(n*n) | O(n*n) | 不稳定 |
计数排序 | O(n+k) | O(n+k) | O(n+k) | O(k) | 稳定 |
桶排序 | O(n+k) | O(n+k) | O(n*n) | O(n+k) | 稳定 |
基数排序 | O(n*k) | O(n*k) | O(n*k) | O(n+k) | 稳定 |
冒泡排序
思想:通过与相邻元素的比较,将较小的元素交换的前面
//冒泡排序O(n^2),稳定算法
//2020年3月23日17:33:40
void BubbleSort(vector<int> nums){
int i=0,j=0;
bool flag=true;//标志位,若未发生交换,则表明列表已经有序
for(;i<nums.size()&&flag;i++){
flag=false;
for(j=nums.size()-1;j>i;j--){
if(nums[j]<nums[j-1])
swap(nums[j],nums[j-1]);
flag=true;
}
}
}
选择排序
思想:和冒泡排序思想类似,是一次排序后将最小的元素放到最前面
具体做法,就是每次找到最小元素下标,与最前面的元素交换
//选择排序O(n^2) 不稳定算法
//找到最小值的下标
//2020年3月23日21:05:14
void SelectSort(vector<int> &nums){
int min_a=0;
for(int i=0;i<nums.size();i++){
min_a=i;
for(int j=i+1;j<nums.size();j++){
if(nums[j]<nums[min_a])
min_a=j;
}
if(i!=min_a)
swap(nums[i],nums[min_a]);
}
}
插入排序
思想:维持一个排序数组,每次判断下个元素的插入位置,将插入点后的元素依次后移。
//插入排序O(n^2)系数比前面两种方法小,稳定算法
//2020年3月23日21:15:02
void InsertSort(vector<int> &nums){
//数组长度小于等于1,没有进行循环操作
int val=0;
for(int i=1;i<nums.size();i++){
//如果nums[i]大于nums[i-1]直接纳入排序数组,不用其他操作
if(nums[i]<nums[i-1]){
val=nums[i];//保存该值
for(int j=i-1;j>=0&&nums[j]>val;j--)
nums[j+1]=nums[j];
nums[j+1]=val;
}
}
}
希尔排序
思想:将整个记录序列按照增量分割成若干个子序列,某个增量序列进行插入排序。
void Shellsort(vector<int> &nums){
int len=nums.size();
int increment=len;
do{
increment=increment/3+1;//增量序列
for(int i=increment;i<len;i++){
if(nums[i]<nums[i-increment]){
int tmp=nums[i];
int j=i-increment;
for(;j>0&&tmp<nums[j];j-=increment)
nums[j+increment]=nums[j];
nums[j+increment]=tmp;
}
}
}
while(increment>1);
}
堆排
// 堆排序O(nlgn)
//2020年3月23日21:45:48
void HeapSort(vector<int> &nums){
//假设数据由1...nums.size()排列
for(int i=nums.size()/2;i>0;i++){//建立堆
HeapAdjust(nums,i);
}
for(i=nums.size();i>1;i--){//到第二个
swap(nums[i],nums[1]);
HeapAdjust(nums,1);
}
}
// parent(i) i/2
//left(i) 2*i
// right(i) 2*i+1
void HeapAdjust(vector<int>&nums,int i){
int left=2*i;
int right=2*i+1;
int largest=0;
if(left<=nums.size()&&nums[left]>nums[i])
largest=left;
else largest=i;
if(right<=nums.size()&&nums[right]>nums[largest])
largest=right;
if(largest!=i){
swap(nums[i],nums[largest]);
HeapAdjust(nums,larget);
}
}
//build_MAX_heap
void BuildMaxHeap(vector<int>& int nums){
for(int i=nums.size()/2;i>0;i--)
HeapAdjust(nums,i);
}
归并排序
思想:采用递归分治的思想,先递归划分子问题,然后合并结果。
时间复杂度O(nlgn),空间复杂度O(n)
//归并排序O(nlgn)
//2020年3月26日16:49:35
//占用内存,效率高且稳定
void Msort(vector<int> &SR,int left,int right){
if(right-left<=0)//确保区间存在[left,right]
return;
int mid=left+(right-left)/2;
Msort(SR,left,mid);
Msort(SR,mid+1,right);//
Merge(SR,left,mid,mid+1,right);//合并上面两个区间
}
//实现[a,b] [c,d]两个区间的原位合并,外部确定是连续空间
void Merge(vector<int> &SR,int a,int b,int c,int d){
int i=a;
int j=c;
int tmp=0;
vector<int> St;
while(i<=b&&j<=d){
tmp=SR[i]<SR[j]?SR[i++]:SR[j++];
St.push_back(tmp);
}
while(j<=d){
St.push_back(SR[j++])
}
while(i<=b){
St.push_back(SR[i++])
}
for(int m=a;m<St.size();m++){
SR[m+a]=St[m];
}
}
快速排序
思想:每次找到一个基数,将区间划分为左右两区间。
//快排 O(nlgn) 2020年3月26日18:30:44 不稳定
//
void QuickSort(vector<int> & nums){
Qsort(nums,0,nums.size()-1);
}
void Qsort(vector<int> &nums,int left,int right){
if (right-left<=0)return;
int key=Partition(nums,left,high);//找到一个关键值的下标,左边都比他小右边都比他大
Qsort(nums,left,key-1);
Qsort(nums,key+1,right);
}
int Partition(vector<int>& nums, int left,int right){
if(right<left) return -1;//出错
if(right==left) return left;//不用比较
int val=nums[left];//选取的比较随意
//可以用三数取中方法确定,left right mid下标对应的排序后中间的值
while(left<right){
while(left<right&&nums[right]>=val){
right--;
}
nums[left]=nums[right];
//swap(nums[left],nums[right]);
while(left<right&&nums[left]<=val){
left++;
}
nums[right]=nums[left];
//swap(nums[left],nums[right]);
}
nums[left]=val;
return left;
}
void sortsort(int x){
//稳定排序 O(n*n) 冒泡 插入排序
//稳定排序 O(nlgn) 归并排序
//不稳定排序 快速排序 希尔排序 堆排序 选择排序(选择最小的换)
}
计数排序
//外部排序
//计数排序 O(n)
void Countsort(vector<int> &nums ){
if(nums.empty()) return ;
int min_val=nums[0],max_val=nums[0];
for(int i=1;i<nums.size();i++){
if(min_val>nums[i])
min_val=nums[i];
if(max_val<nums[i])
max_val=nums[i];
//min_val=min_val>nums[i]?nums[i]:min_val;
//max_val=max_val<nums[i]?nums[i]:max_val;
}
vector<int> cnt(max_val-min_val+1,0);
for(int i=0;i<nums.size();i++){
cnt[nums[i]-min_val]++;
}
int k=0;
for(int i=0;i<cnt.size();i++){
while(cnt[i]--)
nums[k++]=min_val+i;
}
}
桶排序
//桶排序 外部排序,桶的数量可以外部定义或者内部定义
void BucketSort(vector<int> & nums){
if(nums.empty()) return ;
int min_val=nums[0],max_val=nums[0];
for(int i=1;i<nums.size();i++){
if(min_val>nums[i])
min_val=nums[i];
if(max_val<nums[i])
max_val=nums[i];
//min_val=min_val>nums[i]?nums[i]:min_val;
//max_val=max_val<nums[i]?nums[i]:max_val;
}
int bucketcnt=nums.size()/2;//桶的数量
vector<vector<int>> buckets(bucketcnt,vector<int>());
int bucketsize=ceil(static_cast<double>((max_val-min_val+1)/bucketcnt));
for(int i=0;i<nums.size();i++){
int bucketindex=(nums[i]-min_val)/bucketsize;
buckets[bucketindex].push_back(nums[i]);
}
int k=0;
for(auto bucket: buckets){
if(!bucket.empty()) {
SelectSort(bucket);
for(int value:bucket){
nums[k++]=value;
}
}
}
}
参考书籍:《大话数据结构》、《算法导论》
参考博客:
十大经典排序算法及C++实现
经典排序算法C++实现(10种)