重要排序算法(更新ing)

堆排序

核心思路:

构建堆的时候是从下到上的,从一个个分支节点开始调整的。调整一个分支节点的时候,需要与其最大max孩子结点做比较,如果当前分支节点比max孩子小的话,就交换,由于交换可能会破坏max孩子子堆的最大堆性质,所以需要继续朝着最大max孩子方向做调整,直到遇到叶子节点。

出堆的时候,堆顶top之间出去,然后用最后一个元素代替堆顶top,然后从上至下的调整堆。

非递归实现

692. 前K个高频单词 - 力扣(LeetCode)

cpp 复制代码
class Solution {
public:

    bool stringOrder(string a, string b){
        //a和b肯定是不一样的
        //a的字典序是否比b小呢?
        // 如果所有字符都相同,则较短的字符串被认为更小。
        int minLen = min(a.size(), b.size());
        for (int i = 0; i < minLen; ++i) {
            if (a[i] != b[i]) {
                return a[i] < b[i];
            }
        }
        return a.size()<b.size();
    }

    void buildHeap(int node, vector<pair<string, int>>& nums){
        int n = nums.size();
        //把从node开始往下的所有分支结点都做调整
        for(int i = node; i<=n/2-1;){
            int max = (i+1)*2 - 1; //默认孩子节点里面最大的是左孩子
            if(max+1<n){
                //如果右孩子存在的话
                //如果右孩子的次数比左孩子多的话那么就让max为右孩子
                //或者如果右孩子与左孩子次数一样但是字典序比左孩子小的话
                if(nums[max].second<nums[max+1].second ||
                (nums[max].second==nums[max+1].second &&
                stringOrder(nums[max+1].first, nums[max].first)))
                max = max + 1;
            }
            bool change = false;
            //如果结点的次数比孩子max的次数要小的话
            //如果次数一样但是max的字典序比node的小
            if(nums[i].second<nums[max].second ||
            (nums[i].second==nums[max].second &&
            stringOrder(nums[max].first, nums[i].first))){
                change = true;
            }
            if(change){
                pair<string, int> tmp = nums[i];
                nums[i] = nums[max];
                nums[max] = tmp;
                i = max;
                //就向max的方向继续调整
                //如果max不是分支节点的话 那么它就会退出for循环
            }else break;
            //当不需要change的时候 就说明这个堆没问题了就退出for
        }
    }

    string getTop(vector<pair<string, int>>& nums){
        pair<string, int> top = nums[0];
        int n = nums.size();
        nums[0] = nums[n-1]; //用最后一个元素代替第一个元素
        nums.pop_back(); //删掉最后一个元素
        if(nums.size()>0) buildHeap(0, nums); //调整堆
        return top.first;
    }

    vector<string> topKFrequent(vector<string>& words, int k) {
        //构建<字符串,次数>数组nums --- begin --- 
        map<string, int> cnt;
        for(string tmp: words){
            if(cnt.find(tmp)!=cnt.end()){
                cnt[tmp]++;
            }else cnt[tmp]=1;
        }
        vector<pair<string, int>> nums;
        for(auto [word, time]: cnt){
            nums.emplace_back(pair<string, int>(word, time));
        }
        //--- end ---
        //从分支节点从下往上调整建立大根堆 --- begin ---
        int n = nums.size();
        for(int i = n/2-1; i>=0; i--){
            buildHeap(i, nums);
        }
        //--- end ---
        vector<string> res;
        while(k--){
            res.emplace_back(getTop(nums));
        }
        return res;
    }
};

递归实现

347. 前 K 个高频元素 - 力扣(LeetCode)

cpp 复制代码
class Solution {
public:

    void heap(int node, vector<pair<int, int>>& nums){
            //王道书里面采用的for循环实现最大堆的调整
            //我这里采用递归我感觉更好理解而且代码更少
            if(node>nums.size()/2-1) return; //只允许分支节点操作
            //因为索引是从0开始的 那么左孩子的索引应该是 (node+1)*2-1
            int max = (node+1)*2-1;
            //if(max>=nums.size()) printf("%d ", max);
            //max代表node的左右孩子里面最大的那个结点的索引
            //默认是left
            if(max+1<nums.size()&&nums[max].second<nums[max+1].second)
                max = max + 1;
                //如果有右孩子并且右孩子的值比左孩子大
                //那么max = right
            if(nums[node].second<nums[max].second){
                //如果是孩子结点比node大 那么就交换值
                int tmp = nums[max].second;
                nums[max].second = nums[node].second;
                nums[node].second = tmp;
                int index = nums[max].first;
                nums[max].first = nums[node].first;
                nums[node].first = index;
                heap(max, nums);
                //向调整的方向继续调整
            }
    }

    int getHeapTop(vector<pair<int, int>>& nums){
        int n = nums.size();
        int res = nums[0].first;
        nums[0].first = nums[n-1].first;
        nums[0].second = nums[n-1].second; //用最后一个元素来替代第一个元素的位置
        nums.pop_back(); //舍弃最后一个元素
        if(nums.size()>0) heap(0, nums);// 如果还有元素的话就调整堆
        return res;
    }

    vector<int> topKFrequent(vector<int>& nums, int k) {
        //首先要统计每个元素的出现次数
        map<int, int> cnt;
        for(int i: nums){
            if(cnt.find(i)!=cnt.end()){
                cnt[i]++;
            }else cnt[i] = 1;
        }
        vector<pair<int, int>> values;
        for (const auto& [key, value] : cnt) {
            values.emplace_back(pair<int, int>(key, value));
        }
        //基于堆排序实现
        int n = values.size();
        //对所有的分支节点进行操作
        for(int i = n/2-1; i>=0; i--){
            heap(i, values);
        }

        vector<int> res;
        while(k--){
            res.emplace_back(getHeapTop(values));
        }
        
        return res;
    }
};

二路归并排序

912. 排序数组 - 力扣(LeetCode)

cpp 复制代码
class Solution {
public:
    void merge(vector<int>& nums, int low, int mid, int high) {
        // 合并两个有序的数组
        vector<int> res(nums); // 复制数组
        int l, m;
        l = low;
        m = mid + 1;
        int index = l;
        while (l <= mid && m <= high) {
            if (res[l] < res[m]) {
                nums[index++] = res[l++];
            } else
                nums[index++] = res[m++];
        }
        while (l <= mid)
            nums[index++] = res[l++];
        while (m <= high)
            nums[index++] = res[m++];
    }
    // 下面是优化版本
    // void merge(vector<int>& nums, int low, int mid, int high) {
    //     vector<int> tmp(high - low + 1); // 1. 只申请这段区间
    //     int l = low, r = mid + 1, idx = 0;

    //     while (l <= mid && r <= high)
    //         tmp[idx++] = (nums[l] < nums[r]) ? nums[l++] : nums[r++];
    //     while (l <= mid)
    //         tmp[idx++] = nums[l++];
    //     while (r <= high)
    //         tmp[idx++] = nums[r++];

    //     for (int i = 0; i < idx; ++i) // 2. 拷回原数组对应位置
    //         nums[low + i] = tmp[i];
    // }

    void twoRoad(vector<int>& nums, int low, int high) {
        if (low < high) {
            int mid = (low + high) / 2;
            twoRoad(nums, low, mid);
            twoRoad(nums, mid + 1, high);
            merge(nums, low, mid, high);
        }
    }

    vector<int> sortArray(vector<int>& nums) {
        twoRoad(nums, 0, nums.size() - 1);
        return nums;
    }
};

快速排序

cpp 复制代码
    int sort(vector<int>& nums, int left, int right){
        int base = nums[left];
        while(left<right){
            while(left<right&&nums[right]>=base) --right;
            nums[left] = nums[right];
            while(left<right&&nums[left]<=base) ++left;
            nums[right] = nums[left];
        }
        nums[left] = base;
        return left;
    }

    void fastSort(vector<int>& nums, int left, int right){
        if(left<right){
            int p = sort(nums, left, right);
            fastSort(nums, left, p-1);
            fastSort(nums, p+1, right);
        }
    }
相关推荐
冉佳驹1 小时前
数据结构 ——— 八大排序算法的思想及其实现
c语言·数据结构·排序算法·归并排序·希尔排序·快速排序·计数排序
Hello_Embed2 小时前
FreeRTOS 入门(四):堆的核心原理
数据结构·笔记·学习·链表·freertos·
虹科测试测量3 小时前
德思特干货 | 单通道、多通道衰减器与衰减矩阵:如何选择合适的衰减方案
服务器·测试工具·算法·矩阵
mit6.8243 小时前
py期中实验选题:实现天气预测
python·算法
烧冻鸡翅QAQ3 小时前
考研408笔记——数据结构
数据结构·笔记·考研
xuehaikj3 小时前
YOLOv8多场景人物识别定位与改进ASF-DySample算法详解
算法·yolo·目标跟踪
异步的告白3 小时前
C语言-数据结构-2-单链表程序-增删改查
c语言·开发语言·数据结构
Wenhao.3 小时前
LeetCode 救生艇
算法·leetcode·golang
超级无敌大学霸3 小时前
二分查找和辗转相除法
c语言·算法