牛客C++笔记——排序

各种排序对比分析

在这里插入图片描述

冒泡排序

冒泡排序:基本思想是,依次把大的往后放,这样最后面的就是最大的了,依次缩小范围,大的逐渐见往后排
class Solution {
public:
    vector<int> MySort(vector<int>& arr) {
        // write code here
        for(int i = arr.size() - 1; i >= 1; i --){
            bool is_sort = true;
            for(int j = 1; j <= i; j ++){
                if(arr[j - 1] > arr[j]){
                     swap(arr[j - 1] , arr[j]);
                    is_sort = false;
                }
            }
            if(is_sort) break;
        }
        return arr;
    }
};
 
//冒泡排序——递归
class Solution {
private:
    void back_sort(vector<int>& num, int end){
        if(end == 0) return;
        bool is_sort = true;
        for(int j = 1; j <= end; j ++){
            if(num[j - 1] > num[j]){
                 swap(num[j - 1] , num[j]);
                is_sort = false;
            }
        }
        if(is_sort) return;
        back_sort(num, --end);
        return;
    }
     
public:
    vector<int> MySort(vector<int>& arr) {
        // write code here
        back_sort(arr, arr.size() - 1);
        return arr;
    }
};

选择排序

选择排序:走一趟找到最小的,放第一个,然后再在剩下的里面找最小的,放第二个,直到全部找完
改进的思路的话,走一趟不仅选最小的,也选出最大的。

//选择排序:找到最小的,放第一个,然后再在剩下的里面找最小的,放第二个,直到全部找完
class Solution {
public:
    vector<int> MySort(vector<int>& arr) {
        // write code here
        for(int i = 0; i < arr.size(); i ++){
            int min_indx = i;
            for(int j = i; j < arr.size(); j ++ ){
                if(arr[j] < arr[min_indx]) {
                    min_indx = j;
                }
            }  
            swap(arr[i], arr[min_indx]);
        }
        
        return arr;
    }
};

递归实现

//选择排序,这里还增加了优化,一趟寻找下来,既找最小的也找最大的,上面迭代法也可以这样修改
class Solution {
    void back_sort(vector<int>& num, int start, int end){
        if(start >= num.size()) return;
        int min_indx = start;
        int max_indx = end;
        for(int j = start; j <= end; j ++ ){
            if(num[j] < num[min_indx]) min_indx = j;
            if(num[j] > num[max_indx]) max_indx = j;
        }
        //这里有这些判断的目的是防止被交换过去的,又被换走
        if(min_indx != start) swap(num[min_indx], num[start]);
        if(max_indx == start) max_indx = min_indx;
        if(max_indx != end) swap(num[end], num[max_indx]);
        back_sort(num, ++start, --end);
    } 
public:
    vector<int> MySort(vector<int>& arr) {
        // write code here
        back_sort(arr, 0, arr.size() - 1);
        return arr;
    }
};

插入排序

插入排序:就类似于打牌给牌排序一样,一张一张拿出来插入到已经排好的序列中
class Solution {
public:
    vector<int> MySort(vector<int>& arr) {
        // write code here
        for(int i = 1; i < arr.size(); i ++){
            for(int j = i; j >= 1; j --){
                if(arr[j] > arr[j - 1]) break
                else if(arr[j - 1] > arr[j]) swap(arr[j - 1] , arr[j]);
            }
        }
        return arr;
    }
};

递归方法


插入排序-递归实现
class Solution {
    void back_sort(vector<int>& arr, int end){
        if(end == arr.size()) return;
        for(int j = end; j >= 1; j --){
            if(arr[j] > arr[j - 1]) break;
            else if(arr[j - 1] > arr[j]) swap(arr[j - 1] , arr[j]);
        }
        back_sort(arr, ++end);
    }
public:
    vector<int> MySort(vector<int>& arr) {
        // write code here
        back_sort(arr, 1);
        return arr;
    }
};

希尔排序

由于插入排序中,如果一个很小的数在很后面,那它插到前面的话需要移动很远,这样效率不高,因此希尔排序的思想是把数据分成一些组,组内使用插入排序,组内移动距离就变短了,再逐渐改变组的规模。

//希尔排序——相当于是插入排序的改进版
void group_sort(vector<int>& arr, int start, int step){
    for(int i = start + step; i < arr.size(); i += step){
        for(int j = i; j >= start + step; j -= step){
            if(arr[j] > arr[j - 1]) break;
            else if(arr[j - 1] > arr[j]) swap(arr[j - 1] , arr[j]);
        }
    }
}
vector<int> MySort(vector<int>& arr) {
    //step逐渐减半,就是分组逐渐变多,直到最后一个元素一组
    //这样的目的是相当于减少在插入排序中移动位置的距离,不然如果有的小数很靠后,要移动很远
    int step = arr.size()/2;
    while(step){
        //这层负责把每组都用插入排序弄一遍
        for(int i = 0; i < step; i ++){
            //这里是进行插入排序,到最后step=1的时候,就相当于是全部来了一遍插入排序
            group_sort(arr, i, step);
        }
        //每次分组数量增多,这里就选用增加一倍
        step = step/2;
    }
    return arr;
}

快速排序

在这里插入图片描述

实现思路一:挖坑填数+分治

在这里插入图片描述

  1. 为了方便,可以第一次选第一个作为基数就行,后面采用挖坑填数法
  2. 先来处理基数右边区间,把基数挖出去留下坑,左指针留在坑这里,右指针从右区间最右边开始走,找到比基数大的,填到坑里
  3. 左指针右走一下,挖坑,右指针从右区间最右边开始走,找到比基数大的,填到坑里
  4. 直到左右指针重合,那就把基数放到坑这里,基数位置就确定好了
  5. 下面处理基数左边区间,选择左边区间,选择左区间最最左边的作为基数,挖坑
  6. 然后对基数右边按照2、3、4步骤再来
  7. 重复以上操作,直到基数左边、右边的区间大小都小于2了,那就停止
class Solution {  
private:
    void quick_sort(vector<int>& num, int start, int end){
        if(end - start < 1) return;//区间里面的数字少于两个了,那就不用排序了
        
        int center_base = num[start];//选择中心基准数,这里为了方便就选第一个了,选的好的话可以提高排序效率
        bool move = true;//true——左移, false——右移
        int left = start, right = end;
        
        while(left < right){
            if(move){//左移
                //找比center_base小的数字,放到左边去
                if(num[right] < center_base){
                    num[left] = num[right];
                    move = false;//找到之后,换成右移
                    left ++;//left需要更新,右移一下
                }else right --;
            }else{//右移
                //找比center_base大的数字,放到右边去
                if(num[left] > center_base){
                    num[right] = num[left];
                    move = true;//找到之后,换成右移
                    right --;//right需要更新,左移一下
                }else left ++;
            }
        }
        
        //left和right重合,那就把center_base放在这里
        num[left] = center_base;
        
        quick_sort(num, start, left - 1);//排序左边区间
        quick_sort(num, left + 1, end);//排序右边区间
    }
    
public:
    vector<int> MySort(vector<int>& arr) {
        quick_sort(arr, 0, arr.size() - 1);
        return arr;
    }
};

可以优化的思路:

在这里插入图片描述

归并排序

在这里插入图片描述
在这里插入图片描述

//归并排序
class Solution { 
public:
    vector<int> MySort(vector<int>& arr) {
        vector<int> tmp(arr);

        for(int step = 1; step < tmp.size(); step *= 2){
            int p = 0;//用来填数字的下标,每轮都要从0开始
            //这样一层循环是处理一对,一对里面有两组数
            for(int start = 0; start <arr.size(); start += (step * 2)){
                //第一组就是start开头
                int start_1 = start;
                //组1的结束就是组2的开始,它们最大也不能超过size
                int end_1 ,start_2;  
                end_1 = start_2 = (start_1 + step) < tmp.size() ? (start_1 + step) : tmp.size();
                //第二组的结束最大也不能超过size
                int end_2 = (start_2 + step) < tmp.size() ? (start_2 + step) : tmp.size();  
        
                while(start_1 < end_1 && start_2 < end_2){
                    //小的排前面,大的排后面
                    if(tmp[start_1] < tmp[start_2])  arr[p ++] = tmp[start_1 ++];
                    else  arr[p ++] = tmp[start_2 ++];
                }
                //第一组里还有数据的话,都放到后面
                while(start_1 < end_1)
                    arr[p ++] = tmp[start_1 ++];
                
                //第二组里还有数据的话,都放到后面
                while(start_2 < end_2)
                    arr[p ++] = tmp[start_2 ++];
            }
            
            tmp = arr;//把这一轮排好的复制到tmp里面
        }
        return arr;
    }
};

堆排序

在这里插入图片描述

实现步骤

在这里插入图片描述

  1. 把序列按层构建成完全二叉树
    其实数组,按照层序的方式,它就可以表示树的结构了
    在这里插入图片描述

在这里插入图片描述
2. 调整为大顶堆:从倒数第二层的最后一个节点(最后一个父节点)开始调整,小的往下交换
在这里插入图片描述
经过调整,变成了大顶堆
在这里插入图片描述
3. 把根节点和最后一个叶子节点交换,这样最后那个叶子节点就位置确定了
在这里插入图片描述
4. 交换之后,树就不是大顶堆了,根节点比较小,这个时候,把根节点下沉(下沉原则是把他和比较大的子节点交换)
在这里插入图片描述
经过下沉调整,树又变成大顶堆了
在这里插入图片描述
5. 把倒数第二个叶子节点和根节点交换(这样这个节点的位置也确认好了),重复3、4,直到整棵树的节点都完成3、4这样的步骤
6. 在这里插入图片描述

//堆排序
class Solution { 
    //这个函数的功能就是,选中一个子树的根节点,把这个子树调整成大顶堆(小的往下,大的在上)
    void headpify(vector<int>& arr, int node_indx, int end){
        int dad = node_indx;
        int son = dad * 2 + 1;//从左孩子开始看
        //还有孩子就需要考虑下沉
        while(son <= end){
            //如果右孩子大于左孩子,那最大的孩子就是右孩子
            if(son + 1 <= end && arr[son] < arr[son + 1]) son ++;
            //如果dad比最大的孩子还大,那就不用下沉了
            if(arr[dad] > arr[son]) return;
            else{//不然,就是dad比最大的孩子小,那就需要把他和最大的孩子交换
                swap(arr[dad] , arr[son]);
                dad = son;
                son = dad * 2 + 1;
            }
        }
    }
public:
    vector<int> MySort(vector<int>& arr) {
        //有了arr之后,其实就有了树了,只是说还不是大顶堆
        
        //调整成大顶堆:就是从最后一个父节点(就是最后一个节点的父亲:(arr.size()-1)-1)/2)开始,一直使用hedapify
        for(int i = ((arr.size() - 1) - 1)/2; i >= 0; i --)
            headpify(arr, i, arr.size() - 1);
        //从最后往前,依次换到根节点上,再做headpify
        for(int i = arr.size() - 1; i >= 1; i --){
            //换到根节点上,这样i的位置就是确定好了
            swap(arr[0], arr[i]);
            //把根节点拿去headpify,headpify的范围不要包含到已经确定好位置的地方
            headpify(arr, 0, i - 1);
        }
        return arr;
    }
};

计数排序

  1. 找出序列中最大的值
  2. 创建一个长度为最大值+1的临时容器
  3. 以元素值为下标,统计每个元素出现的次数
    在这里插入图片描述
    在这里插入图片描述
  4. 按照次数挨个把元素排列出来
    在这里插入图片描述

他的应用比较局限
在这里插入图片描述
改进思路,不要弄0-max这样的的数组了,这样很浪费,我们用min-max这样的数组来存
在这里插入图片描述

桶排序

设计几个桶,把元素都放到对应的桶里,再对每个桶进行排序,采用别的(快速、希尔、堆排等),排完之后,再把同之间的元素合并
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

基数排序

在这里插入图片描述

  1. 确定出序列里面最大的是905,最大为百位
  2. 按个位分配到桶里
    在这里插入图片描述
  3. 把桶里的数据依次放出来,从下标0到9,按照先进先出原则,这样数据就变成按个位有序的了
    在这里插入图片描述
  4. 按十位分配到桶里
    在这里插入图片描述
  5. 把桶里的数据依次放出来,从下标0到9,按照先进先出原则,这样数据就变成按十位有序的了在这里插入图片描述
  6. 按百位分配到桶里
    在这里插入图片描述
  7. 把桶里的数据依次放出来,从下标0到9,按照先进先出原则,这样数据就变成按百位有序的了,同样,到这里就完成了排序, 个十百位都弄完了
    在这里插入图片描述
    代码实现
class Solution {  
public:
    vector<int> MySort(vector<int>& arr) {
        //创建桶,0-9,每个桶装对应位数的元素
        vector<vector<int>> buckets(10);
        
        //浏览一遍,找到最大的,确定出他有几位
        int max_num = arr[0];
        for(int a : arr){
            if(a > max_num) max_num = a;
        }
        
        int pos = 1;//先从个位开始
        int a_pos_num;
        //max_num的位数决定着循环的次数
        while(max_num){
            for(int i = 0; i < buckets.size(); i ++) {
                buckets[i].clear();
            }
            //挨个求它们的位上的数字,放到桶里
            for(int a : arr){
                //如果a小于要求的位数,那这个 位 肯定就是0
                if(a < pos) buckets[0].push_back(a);
                else {
                    //求a对应pos位上的数字
                    a_pos_num = (a / pos) % 10;
                    buckets[a_pos_num].push_back(a);
                }
            }
            int indx = 0;
            //按照0-9的顺序,取出来,先进先出
            for(int i = 0; i < buckets.size(); i ++){
                for(int j = 0; j < buckets[i].size(); j ++){
                    arr[indx] = buckets[i][j];
                    indx ++;
                }
            }
            pos *= 10;//位数往前一位
            max_num /= 10;
        }
        return arr;
    }
};

基数排序本质理解为按关键字排序,对于整数,我们选择用他们的每个位上的数字来作为关键字,这样的方法也可以用于堆其他类型 进行排序
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值