重要排序算法(更新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);
        }
    }
相关推荐
自学小白菜20 分钟前
每周刷题 - 第三周 - 双指针专题 - 02
python·算法·leetcode
杜子不疼.34 分钟前
【LeetCode76_滑动窗口】最小覆盖子串问题
算法·哈希算法
ComputerInBook43 分钟前
代数基本概念理解——特征向量和特征值
人工智能·算法·机器学习·线性变换·特征值·特征向量
不能只会打代码1 小时前
力扣--3433. 统计用户被提及情况
java·算法·leetcode·力扣
biter down1 小时前
C++ 解决海量数据 TopK 问题:小根堆高效解法
c++·算法
用户6600676685391 小时前
斐波那契数列:从递归到缓存优化的极致拆解
前端·javascript·算法
初夏睡觉2 小时前
P1055 [NOIP 2008 普及组] ISBN 号码
算法·p1055
程芯带你刷C语言简单算法题2 小时前
Day28~实现strlen、strcpy、strncpy、strcat、strncat
c语言·c++·算法·c
踏浪无痕2 小时前
周末拆解:QLExpress 如何做到不编译就能执行?
后端·算法·架构
一个不知名程序员www2 小时前
算法学习入门--- 树(C++)
c++·算法