当前位置: 首页 > article >正文

【数据结构与算法】LeetCode:堆和快排

文章目录

  • LeetCode:堆和快排
    • 排序数组
    • 数组中的第K个最大元素 (Hot 100)
    • 前 K 个高频元素(Hot 100)
    • 数据流的中位数(Hot 100)

LeetCode:堆和快排

排序数组

排序数组

双向切分实现快排:

class Solution {
private:
    void quick_sort(vector<int>& nums, int left, int right){
        if (left >= right) return;
        
        // 随机选择基准值
        int k = rand() % (right - left + 1) + left; 
        swap(nums[right], nums[k]);

        int base = nums[right];
        int slow = left; // slow之前都是小于等于base的

        for(int fast = left; fast < right; fast++){ // 从left开始
            if(nums[fast] <= base){ 
                swap(nums[slow], nums[fast]); 
                slow++;
            }
        }
        swap(nums[slow], nums[right]); 

        quick_sort(nums, left, slow - 1);  // 比base小的部分 
        quick_sort(nums, slow + 1, right); // 比base大的部分
    }

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

三向切分实现快排:
三向切分快速排序在处理包含大量重复元素的数组时比双向切分快速排序更快。

class Solution {
private:
    void quick_sort(vector<int>& nums, int begin, int end){
        if (begin >= end) return;
        // 随机选择基准值
        int k = rand() % (end - begin + 1) + begin; 
        swap(nums[end], nums[k]);
        int base = nums[end];

        // 三向切分:使用 left 和 right 指针来划分小于、等于和大于基准值的区域。
        int left = begin, i = begin, right = end;

        while (i <= right) {
            if (nums[i] < base) {  // 小于base的换到左边
                swap(nums[left], nums[i]);
                left++;
                i++;
            } else if (nums[i] > base) { // 大于base的换到右边
                swap(nums[i], nums[right]);
                right--;
            } else { // 等于base的元素直接跳过,所以交换操作的次数也减少了
                i++;
            }
        }
 		// left 和right之间的值都等于base
        quick_sort(nums, begin, left - 1);
        quick_sort(nums, right + 1, end);

    }

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

数组中的第K个最大元素 (Hot 100)

数组中的第K个最大元素

堆:
当我们想要找到数组中第k个最大的元素时,我们应该维护一个大小为k的最小堆,因为最小堆的堆顶元素总是最小的:

  
class Solution {  
public:  
    int findKthLargest(std::vector<int>& nums, int k) {  
        std::priority_queue<int, std::vector<int>, std::greater<int>> min_heap; // 最小堆  
  
        // 遍历数组,维护一个大小为K的最小堆  
        for (int num : nums) {  
            if (min_heap.size() < k) {  
                min_heap.push(num);   
            } else if (num > min_heap.top()) {  
                min_heap.pop();      // 弹出最小值
                min_heap.push(num);  // 加入新值  
            }  
        }  
  
        // 堆顶为第K大的元素
        return min_heap.top();  
    }  
};

快排:

class Solution {
public:
    int quickselect(vector<int> &nums, int begin, int end, int k) {
        // 随机选择基准值
        int picked = rand() % (end - begin + 1) + begin;
        swap(nums[picked], nums[end]);

        int base = nums[end];
        int left = begin,right = end,i = begin;  
        while (i <= right) {
            if (nums[i] > base) {
                swap(nums[left], nums[i]);
                left++;
                i++;
            } else if (nums[i] < base) {
                swap(nums[i], nums[right]);
                right--;
            } else {
                i++;
            }
        }

        //nums[begin..left-1] > base,nums[left..right] == base,nums[right+1..end] < base
        if (k >= left && k <= right) return nums[k];                      // k 落在等于 base 的区间
        else if (k < left) return quickselect(nums, begin, left - 1, k);  // k 在左边
        else return quickselect(nums, right + 1, end, k);                  // k 在右边
    } 

    int findKthLargest(vector<int> &nums, int k) {
        int n = nums.size();
       
        return quickselect(nums, 0, n - 1, k - 1);
    }
};

前 K 个高频元素(Hot 100)

前 K 个高频元素

堆:

class Solution {
public:
    class mycomparison{
    public:
        bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs){
            return lhs.second > rhs.second; // 按照频率从大到小排序
        }
    };
    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int, int> map;
        // 统计元素频率<元素,出现次数>
        for(int i = 0; i < nums.size(); i++)
            map[nums[i]]++;
        priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparison> pri_que;

        for(auto num_freq : map){
            pri_que.push(num_freq); 
            if(pri_que.size() > k) pri_que.pop();  // 只保留K个最高频元素
        }

        vector<int> result(k);
        for(int i = 0; i < k; i++){
            result[i] = pri_que.top().first;
            pri_que.pop();
        }
        return result;

    }

};

快排:

class Solution {
public:
    void qsort(vector<pair<int, int>>& v, int l, int r, vector<int>& result, int k) {
		// 随机选择基准值
        int picked = rand() % (r - l + 1) + l;
        swap(v[picked], v[r]);

        int base = v[r].second;
        int i = l; 

        for (int j = l; j < r; j++) {
            if (v[j].second >= base) {  // 找到频率大于等于基准值的元素
                swap(v[i], v[j]);      // 将大于等于基准值的元素放到左边
                i++;
            }
        }
        swap(v[i], v[r]);

        if (k < i - l + 1) {            // 左侧的子数组个数大于k,包含前 k个高频元素
            qsort(v, l, i - 1, result, k); 
        } else if (k > i - l + 1) {     // 左侧的子数组个数小于k
            // k个高频元素包括左侧子数组的全部元素以及右侧子数组中的部分元素
            for (int m = l; m <= i; m++) result.push_back(v[m].first); // 左侧子数组的全部元素
            qsort(v, i + 1, r, result, k - (i - l + 1));               // 右侧子数组中的部分元素
        }else {                         // 左侧的子数组个数等于k
            for (int m = l; m <= i; m++) result.push_back(v[m].first);
        }
    }

    vector<int> topKFrequent(vector<int>& nums, int k) {
        // 统计元素频率<元素,出现次数>
        unordered_map<int, int> map;
        for (auto& num : nums) map[num ]++;

        // 将 unordered_map 转换为 vector 以便可以随机访问
        vector<pair<int, int>> num_freq(map.begin(), map.end());

        vector<int> result;

        // 使用快速选择算法查找前 k 大的频率
        qsort(num_freq, 0, num_freq.size() - 1, result, k);

        return result;
    }
};

数据流的中位数(Hot 100)

数据流的中位数

class MedianFinder {
public:
    priority_queue<int, vector<int>, greater<int>> A; // 小顶堆,保存较大的一半
    priority_queue<int, vector<int>, less<int>> B;    // 大顶堆,保存较小的一半
    MedianFinder() { }
    void addNum(int num) {  
        if (A.size() != B.size()) { // 当前为奇数个值
            A.push(num);            // A添加一个数值
            B.push(A.top()); 		// A的最小值给B
            A.pop();         		// A弹出最小值
        } else {              		// 当前为偶数个值
            B.push(num);      		// B添加一个数值
            A.push(B.top());  		// B的最大值给A
            B.pop();          		// B弹出最大值
        }
    }
    double findMedian() {
        return A.size() != B.size() ? A.top() : (A.top() + B.top()) / 2.0;
    }
};




http://www.kler.cn/a/331601.html

相关文章:

  • 【算法题解】B. President‘s Office - Python实现
  • 【开源免费】基于SpringBoot+Vue.JS租房管理系统(JAVA毕业设计)
  • Pytorch | 利用SMI-FGRM针对CIFAR10上的ResNet分类器进行对抗攻击
  • git 总结+场景应用
  • 电脑缺失sxs.dll文件要怎么解决?
  • hive中的四种排序类型
  • 深入浅出MongoDB(二)
  • 网络编程-TCP
  • 关于Elastic Search与MySQL之间的数据同步
  • 二、MySQL的数据目录
  • 16.数据结构与算法-串,数组与广义表(串,BF算法,KMP算法)
  • linux第二课:常用命令
  • 828华为云征文|使用Flexus X实例创建FDS+Nginx服务实现图片上传功能
  • 微服务(二)
  • Electron 主进程与渲染进程、预加载preload.js
  • 使用rust实现rtsp码流截图
  • Stable Diffusion绘画 | 来训练属于自己的模型:秋叶训练器使用
  • 爬虫——爬取小音乐网站
  • 土地规划中的公共设施布局:科学规划,赋能土地高效利用的艺术
  • SCoRe: 通过强化学习教导大语言模型进行自我纠错
  • 鸿蒙 HarmonyNext 与 Flutter 的异同之处
  • Python Selenium常用语法汇总(包含XPath语法)
  • Linux命令大全及小例子
  • 【服务器】服务器虚拟化概述
  • 基于PyQt5和SQLite的数据库操作程序
  • NLP任务之预测最后一个词