leetcode经典题目(6)--排序

1. 数组中的第K个最大元素(NO.215)

题目描述:在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
解题思路:对数组进行排序,返回第K个最大的元素。
(1)可以使用排序函数sort,时间复杂度 O(NlogN),空间复杂度 O(1)

class Solution {
    
    
public:
    int findKthLargest(vector<int>& nums, int k) {
    
    
        int n = nums.size();
        sort(nums.begin(),nums.end());
        return nums[n-k];
    }
};

(2)使用堆排序,时间复杂度 O(NlogK),空间复杂度 O(K)。topk (前k大)用小根堆,维护堆大小不超过 k 即可。每次压入堆前和堆顶元素比较,如果比堆顶元素还小,直接扔掉,否则压入堆。检查堆大小是否超过 k,如果超过,弹出堆顶。求前 k 大,用小根堆,求前 k 小,用大根堆。

class Solution {
    
    
public:
    int findKthLargest(vector<int>& nums, int k) {
    
    
        int n = nums.size();
        for (int i = n / 2 - 1; i >= 0; i--)//生成初始堆
            adjust(nums,i,n);
        for (int j = n-1; j >= n-k; j--){
    
    //循环k次,可以找到最大的k个数并排序
            int tmp = nums[0];
            nums[0] = nums[j];
            nums[j] = tmp;
            adjust(nums,0,j);
        }
        return nums[n-k];
    }
private:
    void adjust(vector<int>& nums, int i, int n){
    
    //调整以节点i为根的二叉树
        int j = 2*i + 1;
        while (j < n){
    
    
            if (j+1 < n && nums[j] < nums[j+1])
                j++;//j指向两个子节点中较大的
            if (nums[i] < nums[j]){
    
    //需要交换
                int tmp = nums[i];
                nums[i] = nums[j];
                nums[j] = tmp;
                i = j;
                j = 2 * i + 1;
            }
            else//不用交换
                break;
        }
    }
};

使用STL中的priority_queue来创建堆。greater< int >创建的是小根堆,less< int> 创建的是大根堆。

class Solution {
    
    
public:
    int findKthLargest(vector<int>& nums, int k) {
    
    
        priority_queue<int,vector<int>,greater<int>> heap;
        for (int num : nums){
    
    
            if (heap.size() < k)
                heap.push(num);
            else if (num > heap.top()){
    
    
                heap.pop();
                heap.push(num);
            }
        }
        return heap.top();
    }
};

(3)使用快速排序。从大到小排序,如果k-1位置上的元素归位,则找到了第k大的元素。如果归位的元素在k-1的右侧,则接着对该归位元素的左侧区间进行操作;如果归位元素在k-1的左侧,则对右侧区间继续操作。

class Solution {
    
    
public:
    int findKthLargest(vector<int>& nums, int k) {
    
    
        int l = 0, r = nums.size()-1;
        while (l < r){
    
    
            int j = MyPartition(nums,l,r);
            if (j == k - 1)
                break;
            else if (j < k - 1)
                l = j + 1;
            else
                r = j - 1;
        }
        return nums[k-1];
    }
private:
    int MyPartition(vector<int>& nums, int l, int r){
    
    
    random_shuffle(nums.begin()+l, nums.begin()+r+1);
    int i = l, j = r;
    int tmp = nums[i];
    while (i < j){
    
    //将大于nums[i]的放在左侧区间,小于的放在右侧区间
        while(i < j && nums[j] <= tmp)
            j--;
        nums[i] = nums[j];
        while (i < j && nums[i] >= tmp)
            i++;
        nums[j] = nums[i];
    }
    nums[j] = tmp;
    return j;
}
};
2. 出现频率最多的 k 个元素(NO.347)

题目描述:给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
解题思路:遍历一次数组,使用unordered_map记录各个数字及其出现的次数。建立一个大小为k的小根堆,保存出现频率最多的k个元素,这里往堆中添加元素,如果堆的规模大于k,则将堆顶元素弹出,每次弹出的都是当前堆中的最小值。最后将这些元素加入到结果数组中。

class Solution {
    
    
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
    
    
        vector<int> res;
        unordered_map<int,int> mp;
        priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> pq;
        for (auto i : nums)
            mp[i]++;//将键值i对应的值加1
        for (auto p : mp){
    
    
            pq.push(pair<int,int>(p.second,p.first));
            if (pq.size() > k) pq.pop();
        }
        while (k--){
    
    
            res.push_back(pq.top().second);//pq元素的second对应于p元素的first,表示键
            pq.pop();
        }
        return res;
    }
};
3. 根据字符出现频率排序(NO.451)

题目描述:给定一个字符串,请将字符串里的字符按照出现的频率降序排列。
解题思路:使用unordered_map将字母与出现的次数对应着存储,然后建立大根堆进行排序。

class Solution {
    
    
public:
    string frequencySort(string s) {
    
    
        string res;
        unordered_map<char,int> mp;
        priority_queue<pair<int,char>,vector<pair<int,char>>,less<pair<int,char>>> pq;
        for (auto c : s)
            mp[c]++;

        for (auto p : mp){
    
    
            pq.push(pair<int,char>(p.second,p.first));
        }
        while (!pq.empty()){
    
    
            for (int i = 0; i < pq.top().first; i++)
                res.push_back(pq.top().second);
            pq.pop();
        }
        return res;
    }
};
4. 按颜色进行排序(NO.75)

题目描述:给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
输入: [2,0,2,1,1,0];输出: [0,0,1,1,2,2]
解题思路
在这里插入图片描述
初始化0的最右边界:p0 = 0。在整个算法执行过程中 nums[idx < p0] = 0. 即p0左端的都为0
初始化2的最左边界 :p2 = n - 1。在整个算法执行过程中 nums[idx > p2] = 2. 即p2右端的都为2.
初始化当前考虑的元素序号 :curr = 0.
While curr <= p2 :
若 nums[curr] = 0 :交换第 curr个 和 第p0个 元素,并将指针都向右移。
若 nums[curr] = 2 :交换第 curr个和第 p2个元素,并将 p2指针左移 。
若 nums[curr] = 1 :将指针curr右移。

class Solution {
    
    
public:
    void swap(vector<int>& nums, int i, int j){
    
    
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }
    void sortColors(vector<int>& nums) {
    
    
        int zero = 0, one = 0, two = nums.size() - 1;
        while (one <= two){
    
    
            if (nums[one] == 2){
    
    
                swap(nums,one,two--);//two指针左移
            }
            else if (nums[one] == 0)
                swap(nums,one++,zero++);//one、zero指针都右移
            else
                one++;//one指针右移
        }
    }
};

猜你喜欢

转载自blog.csdn.net/qq_42820853/article/details/107166855