Loading

Leetcode刷题记录之排序算法

快速排序

快速排序的模板:

https://leetcode.cn/leetbook/read/illustration-of-algorithm/p57uhr/

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

题目描述

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

输入输出

输入: [3,2,1,5,6,4], k = 2
输出: 5

题解

image-20220811231934191

时间复杂度证明

这题使用快排时间复杂度为O(n),可以这样理解:
1、快排可以看成是二叉树,1分2,2分4......
2、每一层都是O(n)的,一共有logn层,故快排时间复杂度是O(nlogn)
3、这一题因为有个判断,可以每次快排时只对一边快排,故每一层的时间复杂度都是上一层的一半。
第一层n,第二层n/2,第三层n/4,共logn层。套用等比数列求和公式,可以得出O(n).

代码

class Solution {
public:
  int findKthLargest(vector<int>& nums, int k) {
    int left = 0, right = static_cast<int>(nums.size()) - 1;
    int target = (static_cast<int>(nums.size()) - (k - 1)) - 1;
    std::shuffle(nums.begin(), nums.end(), std::mt19937(std::random_device()()));
    while (left < right) {
      int mid = partition(nums, left, right);
      if (mid == target) {
        return nums[target];
      } else if (mid < target) { // 去数组右半部分继续寻找
        left = mid + 1;
      } else { // 去数组左半部分继续寻找
        right = mid - 1;
      }
    }
    return nums[left];
  }
  int partition(vector<int> &nums, int left, int right) {
    // 选择left作为划分的基数
    int base = left;
    int i = left, j = right;
    while (i < j) {
      while (i < j && nums[j] >= nums[base]) --j;
      while (i < j && nums[i] <= nums[base]) ++i;
      std::swap(nums[i], nums[j]);
    }
    std::swap(nums[i], nums[base]);
    return i;
  }
};

刷题记录

一刷 2022年8月11日

剑指 Offer 40. 最小的k个数

题目描述

输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

输入输出

输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]

题解

和 215 题一样,使用基于快速排序的快速选择,注意在选择之前先用 shuffle 打乱数组。

代码

class Solution {
public:
  vector<int> getLeastNumbers(vector<int>& arr, int k) {
    int i = 0, j = static_cast<int>(arr.size()) - 1;
    std::shuffle(arr.begin(), arr.end(), std::mt19937(std::random_device()()));
    while (i <= j) {
      int mid = partition(arr, i, j);
      if (mid == k - 1) return vector<int>{arr.begin(), arr.begin() + k};
      else if (mid < k - 1) i = mid + 1;
      else j = mid - 1;
    }
    return {};
  }
  int partition(vector<int> &arr, int left, int right) {
    int i = left, j = right;
    while (i < j) {
      while (i < j && arr[j] >= arr[left]) --j;
      while (i < j && arr[i] <= arr[left]) ++i;
      std::swap(arr[i], arr[j]);
    }
    std::swap(arr[i], arr[left]);
    return i;
  }
};

刷题记录

一刷 2022年8月12日

桶排序

什么是桶排序

https://blog.csdn.net/sinat_31275315/article/details/107868240

347. 前 K 个高频元素

题目描述

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

输入输出

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

题解

利用桶排序的思想:

  1. 遍历字符串,统计每个整数出现的频率,同时记录最高频率 max_frequency

  2. 创建桶,以整数出现的频率作为数组的下标,每个桶中存放相同出现频率的整数集合;

  3. 按照出现频率从大到小的顺序遍历桶,对于每个出现频率,获得对应的整数。

image-20220813110632040

代码

class Solution {
public:
  vector<int> topKFrequent(vector<int>& nums, int k) {
    // [key, value] = [num, num出现的频率]
    unordered_map<int, int> frequency;
    // 最高的出现频率,作为桶的长度
    int max_frequency = 0;
    for (const auto &item: nums) {
      max_frequency = std::max(max_frequency, ++frequency[item]);
    }

    // 使用vector作为桶
    // buckets[频率] = [num1, num2, ....] 把频率相同的元素放入同一个桶内
    vector<vector<int>> buckets(max_frequency + 1);
    for (const auto &item: frequency) {
      buckets[item.second].push_back(item.first);
    }

    vector<int> result(k);
    int j = 0;
    // 从后往前遍历桶,将桶内元素加到result数组中
    for (int i = static_cast<int>(buckets.size()) - 1; i >= 0; --i) {
      for (const auto &num : buckets[i]) {
        if (j >= k) return result;
        result[j++] = num;
      }
    }
    return result;
  }
};

刷题记录

一刷 2022年8月13日

451. 根据字符出现频率排序

题目描述

给定一个字符串 s ,根据字符出现的 频率 对其进行 降序排序 。一个字符出现的 频率 是它出现在字符串中的次数。

返回 已排序的字符串 。如果有多个答案,返回其中任何一个。

输入输出

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

题解

和 347 题一样,利用桶排序的思想,只不过这次是将整数换成了字符。

代码

class Solution {
public:
  string frequencySort(string s) {
    unordered_map<char, int> frequency_map;
    int max_frequency{};
    for (const auto &item: s) {
      max_frequency = std::max(max_frequency, ++frequency_map[item]);
    }
    // buckets[字符出现的频率] = [a, b, ...]
    vector<vector<char>> buckets(max_frequency + 1);
    for (auto &[c, frequency] : frequency_map) {
      buckets[frequency].push_back(c);
    }
    int idx = 0;

    // 从后往前遍历桶中的元素,构造返回值
    for (int i = static_cast<int>(buckets.size()) - 1; i > 0; --i) {
      for (int j = 0; j < buckets[i].size(); ++j) {
        for (int k = 0; k < i; ++k) {
          if (idx >= s.size()) return s;
          s[idx++] = buckets[i][j];
        }
      }
    }
    return s;
  }
};

刷题记录

一刷 2022年8月13日
posted @ 2022-08-11 23:22  cclemontree  阅读(78)  评论(0)    收藏  举报