Leetcode 题解 - 排序

    技术2022-07-11  128

    快速选择

    用于求解 Kth Element 问题,使用快速排序的 partition() 进行实现。

    需要先打乱数组,否则最坏情况下时间复杂度为 O(N2)。

    堆排序

    用于求解 TopK Elements 问题,通过维护一个大小为 K 的堆,堆中的元素就是 TopK Elements。

    堆排序也可以用于求解 Kth Element 问题,堆顶元素就是 Kth Element。

    快速选择也可以求解 TopK Elements 问题,因为找到 Kth Element 之后,再遍历一次数组,所有小于等于 Kth Element 的元素都是 TopK Elements。

    可以看到,快速选择和堆排序都可以求解 Kth Element 和 TopK Elements 问题。

    Kth Element

    215. Kth Largest Element in an Array (Medium)

    题目描述:找到第 k 大的元素。

    排序 :时间复杂度 O(NlogN),空间复杂度 O(1)

    public int findKthLargest(int[] nums, int k) { Arrays.sort(nums); return nums[nums.length - k]; }

    堆排序 :时间复杂度 O(NlogK),空间复杂度 O(K)。

    public int findKthLargest(int[] nums, int k) { PriorityQueue<Integer> pq = new PriorityQueue<>(); // 小顶堆 for (int val : nums) { pq.add(val); if (pq.size() > k) // 维护堆的大小为 K pq.poll(); } return pq.peek(); }

    快速选择 :时间复杂度 O(N),空间复杂度 O(1)

    public int findKthLargest(int[] nums, int k) { k = nums.length - k; int l = 0, h = nums.length - 1; while (l < h) { int j = partition(nums, l, h); if (j == k) { break; } else if (j < k) { l = j + 1; } else { h = j - 1; } } return nums[k]; } private int partition(int[] a, int l, int h) { int i = l, j = h + 1; while (true) { while (a[++i] < a[l] && i < h) ; while (a[--j] > a[l] && j > l) ; if (i >= j) { break; } swap(a, i, j); } swap(a, l, j); return j; } private void swap(int[] a, int i, int j) { int t = a[i]; a[i] = a[j]; a[j] = t; }

    桶排序

    出现频率最多的 k 个数

    347. Top K Frequent Elements (Medium)

    Given [1,1,1,2,2,3] and k = 2, return [1,2].

    设置若干个桶,每个桶存储出现频率相同的数,并且桶的下标代表桶中数出现的频率,即第 i 个桶中存储的数出现的频率为 i。

    把数都放到桶之后,从后向前遍历桶,最先得到的 k 个数就是出现频率最多的的 k 个数。

    public List<Integer> topKFrequent(int[] nums, int k) { Map<Integer, Integer> frequencyForNum = new HashMap<>(); for (int num : nums) { frequencyForNum.put(num, frequencyForNum.getOrDefault(num, 0) + 1); } List<Integer>[] buckets = new ArrayList[nums.length + 1]; for (int key : frequencyForNum.keySet()) { int frequency = frequencyForNum.get(key); if (buckets[frequency] == null) { buckets[frequency] = new ArrayList<>(); } buckets[frequency].add(key); } List<Integer> topK = new ArrayList<>(); for (int i = buckets.length - 1; i >= 0 && topK.size() < k; i--) { if (buckets[i] == null) { continue; } if (buckets[i].size() <= (k - topK.size())) { topK.addAll(buckets[i]); } else { topK.addAll(buckets[i].subList(0, k - topK.size())); } } return topK; }

    按照字符出现次数对字符串排序

    451. Sort Characters By Frequency (Medium)

    Input: "tree" Output: "eert" Explanation: 'e' appears twice while 'r' and 't' both appear once. So 'e' must appear before both 'r' and 't'. Therefore "eetr" is also a valid answer. public String frequencySort(String s) { Map<Character, Integer> frequencyForNum = new HashMap<>(); for (char c : s.toCharArray()) frequencyForNum.put(c, frequencyForNum.getOrDefault(c, 0) + 1); List<Character>[] frequencyBucket = new ArrayList[s.length() + 1]; for (char c : frequencyForNum.keySet()) { int f = frequencyForNum.get(c); if (frequencyBucket[f] == null) { frequencyBucket[f] = new ArrayList<>(); } frequencyBucket[f].add(c); } StringBuilder str = new StringBuilder(); for (int i = frequencyBucket.length - 1; i >= 0; i--) { if (frequencyBucket[i] == null) { continue; } for (char c : frequencyBucket[i]) { for (int j = 0; j < i; j++) { str.append(c); } } } return str.toString(); }

    荷兰国旗问题

    荷兰国旗包含三种颜色:红、白、蓝。

    有三种颜色的球,算法的目标是将这三种球按颜色顺序正确地排列。

    它其实是三向切分快速排序的一种变种,在三向切分快速排序中,每次切分都将数组分成三个区间:小于切分元素、等于切分元素、大于切分元素,而该算法是将数组分成三个区间:等于红色、等于白色、等于蓝色。

    按颜色进行排序

    75. Sort Colors (Medium)

    Input: [2,0,2,1,1,0] Output: [0,0,1,1,2,2]

    题目描述:只有 0/1/2 三种颜色。

    public void sortColors(int[] nums) { int zero = -1, one = 0, two = nums.length; while (one < two) { if (nums[one] == 0) { swap(nums, ++zero, one++); } else if (nums[one] == 2) { swap(nums, --two, one); } else { ++one; } } } private void swap(int[] nums, int i, int j) { int t = nums[i]; nums[i] = nums[j]; nums[j] = t; }
    Processed: 0.012, SQL: 10