leetcode力扣_排序问题(已完结)

目录

215.数组中的第K个最大元素

① 冒泡排序:(超出时间限制)

② C++标准库中的排序算法( std::sort):

③ 快速排序:(超出时间限制)

④ 用官方题解给出的改进的排序算法:

⑤ 堆排序

 ⑥ 桶排序:

347.前K个高频元素(完全没思路的嘞)

451.根据字符出现频率排序(哈希表还是 一点都不会用 需再看)

75.颜色分类

写在最后

215.数组中的第K个最大元素

        鉴于已经将之前学的排序算法忘得差不多了,只会一个冒泡排序法了,就写了一个冒牌排序法,将给的数组按照降序排列,然后取nums[k-1]就是题目要求的,但是提交之后对于有的示例显示”超出时间限制”

① 冒泡排序:(超出时间限制)

         因为冒泡排序的时间复杂度是O(n2),不满足题目要求,所以需要用其他的排序算法或者用其他思路来解这个题目。

② C++标准库中的排序算法( std::sort):

        可以直接使用C++标准库中的排序算法(如 std::sort)来对数组进行排序操作。但是这样好像就没有什么练习的意义了哈哈,还是先放一个

有时间来学一下这个三路快速排序算法 。

③ 快速排序:(超出时间限制)

//快速排序算法
class Solution {
public:
    void quickSort(vector<int>& nums, int left ,int right){
        //左端点大于等于右端点 递归结束
        if(left >= right){
            return ;
        }
        //  确定边界和关键值
        int key = nums[left] ;
        int i = left, j = right;

        while(i<j){
            while(nums[j] >= key && i<j){
                j--;
            }
            if(i<j){
                nums[i] = nums[j] ;
                i++;
            }
            while(nums[i] <= key &&  i<j){
                i++;
            }
            if(i<j){
                nums[j] = nums[i] ;
                j--;
            }
        }
        nums[i] = key ;
        quickSort(nums, left, i - 1);
        quickSort(nums, i + 1, right);
    }
    int findKthLargest(vector<int>& nums, int k) {
        int right = nums.size() ;
        quickSort(nums, 0, right-1);
        return nums[right - k];
    }  
};

④ 用官方题解给出的改进的排序算法:

        在分解的过程当中,对子数组进行划分,如果划分得到的 i 正好就是我们需要的下标,就直接返回 nums[i] ;否则,如果目标下标 nums.size() - k 比下标 i 小,就递归左子区间,否则递归右子区间。这样就可以把原来递归两个区间变成只递归一个区间,提高了时间效率。【没超出时间限制但是击败 5.09% 😭😭😭】

//基于快速排序的改进方法(升序)
class Solution {
public:
    int quickSort(vector<int>& nums , int left , int right, int k ){
        //已经按升序拍好序了
        if(left == right){
            return nums[k];
        }

        int key = nums[left] ;
        int i = left , j = right ;
        while(i != j){
            while(nums[j] >= key && i<j){
                j-- ;
            }
            if(i<j){
                nums[i] = nums[j] ;
                i++ ;
            }
            while(nums[i] <= key && i<j){
                i++ ;
            }
            if(i<j){
                nums[j] = nums[i] ;
                j-- ;
            }
        }
        nums[i] = key ;
        if(k<i){
             return quickSort(nums , left , i-1 ,k);
        }else if(k == i){
            return nums[i];
        }else{
            return quickSort(nums , i+1 , right ,k );
        }
    }
    int findKthLargest(vector<int>& nums, int k) {
        int right = nums.size()-1;
        return quickSort(nums , 0 , right , right+1-k );
    }
};

⑤ 堆排序

堆排序主要分为两步,构建初始堆以及排序(交换节点后对剩余数据再构建堆

// 堆排序
class Solution {
public:
    //构建堆
    void heapify(vector<int>& nums , int len , int i ){
        int maxindex = i ;
        int left = 2 * i + 1 ;
        int right = 2 * i + 2 ;
        if(left<len && nums[left] > nums[maxindex]){
            maxindex = left ;
        }
        if(right<len && nums[right] > nums[maxindex]){
            maxindex = right ;
        }
        if(maxindex != i){
            swap(nums[i] , nums[maxindex]);
            heapify(nums , len ,maxindex);
        }
    }

    //堆排序
    void heapsort(vector<int>& nums){
        int len = nums.size() ;
        for(int i = len / 2 - 1 ; i >= 0 ; i--){
            heapify(nums , len , i) ;
        }
        for(len = len-1 ;len>=0 ;len--){
            swap(nums[len],nums[0]);
            heapify(nums ,len ,0);
        }
    }
    
    int findKthLargest(vector<int>& nums, int k) {
    heapsort(nums);
    return nums[nums.size()-k];
    }
};

 ⑥ 桶排序:

        这里桶的个数是自己设置的,每个桶的大小是动态的,但是每个桶能容纳的数据范围是通过 buckSize 计算得到的。

// 桶排序
class Solution {
public:
    void buckSort(vector<int>& nums , int buckCount){
        if(nums.size() == 0 || buckCount<=0){
            return ;
        }
        int min = *min_element(nums.begin() ,nums.end()) ;
        int max = *max_element(nums.begin() ,nums.end()) ;
        int buckSize = ( max - min )/buckCount + 1 ;
        vector<vector<int>> buckets(buckCount) ;
        for(int num : nums){
            int buckIndex = (num - min) / buckSize ;
            buckets[buckIndex].push_back(num) ;
        }
        nums.clear() ;
        for(vector<int>& bucket : buckets){
            sort(bucket.begin(),bucket.end()) ;
            nums.insert(nums.end() , bucket.begin(),bucket.end()) ;
        }
    } 
    int findKthLargest(vector<int>& nums, int k) {
    int buckCount = 3 ;
    buckSort(nums , buckCount) ;
    return nums[nums.size() - k];
    }
};

347.前K个高频元素(完全没思路的嘞)

        给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。【进阶:你所设计算法的时间复杂度 必须 优于 O(nlogn)其中 n 是数组大小】

示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

示例 2:
输入: nums = [1], k = 1
输出: [1]

力扣官方题解:

class Solution {
public:
    static bool cmp(pair<int, int>& m, pair<int, int>& n) {
        return m.second > n.second;
    }

    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int, int> occurrences;
        for (auto& num : nums) {
            occurrences[num]++;
        }

        // pair 的第一个元素代表数组的值,第二个元素代表了该值出现的次数
        priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(&cmp)> q(cmp);
        for (auto& [num, count] : occurrences) {
            if (q.size() == k) {
                if (q.top().second < count) {
                    q.pop();
                    q.emplace(num, count);
                }
            } else {
                q.emplace(num, count);
            }
        }
        vector<int> ret;
        while (!q.empty()) {
            ret.emplace_back(q.top().first);
            q.pop();
        }
        return ret;
    }
};

 代码语句解析:

unordered_map 简介:

   unordered_map 是 C++ 标准模板库(STL)中的一种关联容器,基于哈希表实现,提供了平均时间复杂度为 O(1) 的插入、删除和查找操作。与 map 不同,unordered_map 不保证元素的顺序。unordered_map<int, int> occurrences; 用于声明一个名为 occurrences 哈希表,其中键和值的类型都是 int。

   occurrences[num]++  会根据当前整数 num 更新哈希表。如果 num 尚未出现在哈希表中,则插入一个新的键值对 {num, 1}。如果 num 已经在哈希表中,则对应的值(出现次数)递增。

优先队列简介:

  priority_queue 是 C++ 标准库中的一种容器适配器,通常用于实现最大堆或最小堆。默认情况下,priority_queue 实现的是最大堆,即队列头部的元素总是优先级最高的元素(值最大的元素)。

 关于这段代码

for (auto& [num, count] : occurrences) {
            if (q.size() == k) {
                if (q.top().second < count) {
                    q.pop();
                    q.emplace(num, count);
                }
            } else {
                q.emplace(num, count);
            }
        }

为什么是 q.top().second 而不是 q.second

  • q 是一个优先队列(priority_queue),不是一个 pair
  • q.top() 返回优先队列中的堆顶元素,这是一个 pair<int, int>
  • q.top().second 访问的是这个 pair 的第二个元素,也就是频率。

         最后一部分代码的作用是从优先队列 q 中提取元素,将每个元素的第一个值(即数字)存储到 ret 向量中,并且在提取之后从优先队列中删除该元素。最终,这段代码会构建一个包含优先队列中所有数字的向量 ret。       

451.根据字符出现频率排序(哈希表还是 一点都不会用 需再看)

        给定一个字符串 s ,根据字符出现的 频率 对其进行 降序排序 。一个字符出现的 频率 是它出现在字符串中的次数。返回 已排序的字符串 。如果有多个答案,返回其中任何一个。s 由大小写英文字母和数字组成。

示例 1:
输入: s = "tree"
输出: "eert"
解释: 'e'出现两次,'r'和't'都只出现一次。
因此'e'必须出现在'r'和't'之前。此外,"eetr"也是一个有效的答案。

示例 2:
输入: s = "cccaaa"
输出: "cccaaa"
解释: 'c'和'a'都出现三次。此外,"aaaccc"也是有效的答案。
注意"cacaca"是不正确的,因为相同的字母必须放在一起。

解题思路:自己没什么思路,搬运一下官方给的思路

 代码如下:

class Solution {
public:
    string frequencySort(string s) {
        //创建哈希表 记录字符及其出现的次数
        unordered_map<char,int> mp ;
        int len = s.length() ;
        int maxfre = 0;
        for(char c : s){
            mp[c]++ ;
            maxfre = max(maxfre , mp[c]) ;
        }
        //假如得到的maxfre为3,那就需要四个桶,分别储存出现频率为0,1,2,3的字符
        //虽然桶0不会存储任何数据
        vector<string> buckets(maxfre+1) ;
        //for(unordered_map<char,int> [c,num] mp) 
        //C++不支持在范围for循环中直接进行解构绑定,故上式表达错误
        for(auto [c,num] : mp){
            //字符c出现的次数为num,则将其储存在编号为num的桶中
            buckets[num].push_back(c);
        }
        string ret ;
        for(int i = maxfre ; i>0 ;i--){
            string &bucket = buckets[i] ;
            for(char c : bucket){
                //k<i 是确保字符在结果数组中出现的次数与其在输入字符串中的频率相同
                //即 第i个桶里的字符 每一个字符都是出现i次的
                for(int k = 0 ;k<i ;k++){
                    ret.push_back(c);
                }
            }
        }
        return ret ;
    }
};

75.颜色分类

        给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。必须在不使用库内置的 sort 函数的情况下解决这个问题

        进阶:你能想出一个仅使用常数空间的一趟扫描算法吗

示例 1:

输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]

示例 2:

输入:nums = [2,0,1]
输出:[0,1,2]

 解题思路:大概是因为上一题的影响,我第一个想到的是用桶排序,将0.1.2三个数分别存进对应编号的桶里,然后在遍历桶中数据输出。但是这样好像就不是原地排序了🤔哪个排序方法是原地排序啊🤔插入排序么。没关系问题不大,先试试

class Solution {
public:
    void sortColors(vector<int>& nums) {
        int len = nums.size() ;
        for(int i = 1 ;i<len ;i++){
            int key = nums[i] ;
            int j = i - 1 ;
            while(j>=0 && key < nums[j]){
                nums[j+1] = nums[j] ;
                j-- ;
            }
            nums[j+1] = key ;
        }
    }
};

为什么这个题也是中等啊,感觉比上一道题简单多了。看了一下题解,插入排序对 nums 不止遍历了一次,还有更简单的方法。

写在最后

排序问题就先告一小段落啦,问题还是挺大的,需要多回顾一下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值