Leetcode 题解 - 排序.md 7.8 KB
Newer Older
C
CyC2018 已提交
1
<!-- GFM-TOC -->
C
CyC2018 已提交
2
* [快速选择](#快速选择)
C
CyC2018 已提交
3 4
* [](#堆)
    * [1. Kth Element](#1-kth-element)
C
CyC2018 已提交
5
* [桶排序](#桶排序)
C
CyC2018 已提交
6 7
    * [1. 出现频率最多的 k 个元素](#1-出现频率最多的-k-个元素)
    * [2. 按照字符出现次数对字符串排序](#2-按照字符出现次数对字符串排序)
C
CyC2018 已提交
8
* [荷兰国旗问题](#荷兰国旗问题)
C
CyC2018 已提交
9
    * [1. 按颜色进行排序](#1-按颜色进行排序)
C
CyC2018 已提交
10
<!-- GFM-TOC -->
C
CyC2018 已提交
11 12


C
CyC2018 已提交
13
# 快速选择
C
CyC2018 已提交
14

C
CyC2018 已提交
15
用于求解  **Kth Element**  问题,也就是第 K 个元素的问题。
C
CyC2018 已提交
16

C
CyC2018 已提交
17
可以使用快速排序的 partition() 进行实现。需要先打乱数组,否则最坏情况下时间复杂度为 O(N<sup>2</sup>)。
C
CyC2018 已提交
18

C
CyC2018 已提交
19
# 堆
C
CyC2018 已提交
20

C
CyC2018 已提交
21
用于求解  **TopK Elements**  问题,也就是 K 个最小元素的问题。可以维护一个大小为 K 的最小堆,最小堆中的元素就是最小元素。最小堆需要使用大顶堆来实现,大顶堆表示堆顶元素是堆中最大元素。这是因为我们要得到 k 个最小的元素,因此当遍历到一个新的元素时,需要知道这个新元素是否比堆中最大的元素更小,更小的话就把堆中最大元素去除,并将新元素添加到堆中。所以我们需要很容易得到最大元素并移除最大元素,大顶堆就能很好满足这个要求。
C
CyC2018 已提交
22

C
CyC2018 已提交
23
堆也可以用于求解 Kth Element 问题,得到了大小为 k 的最小堆之后,因为使用了大顶堆来实现,因此堆顶元素就是第 k 大的元素。
C
CyC2018 已提交
24

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

C
CyC2018 已提交
27
可以看到,快速选择和堆排序都可以求解 Kth Element 和 TopK Elements 问题。
C
CyC2018 已提交
28

C
CyC2018 已提交
29
## 1. Kth Element
C
CyC2018 已提交
30

C
CyC2018 已提交
31 32
[215. Kth Largest Element in an Array (Medium)](https://leetcode.com/problems/kth-largest-element-in-an-array/description/)

C
CyC2018 已提交
33 34 35 36 37 38
```text
Input: [3,2,1,5,6,4] and k = 2
Output: 5
```

题目描述:找到倒数第 k 个的元素。
C
CyC2018 已提交
39 40

**排序** :时间复杂度 O(NlogN),空间复杂度 O(1)
C
CyC2018 已提交
41 42

```java
C
CyC2018 已提交
43 44 45
public int findKthLargest(int[] nums, int k) {
    Arrays.sort(nums);
    return nums[nums.length - k];
C
CyC2018 已提交
46 47 48
}
```

C
CyC2018 已提交
49
**堆** :时间复杂度 O(NlogK),空间复杂度 O(K)。
C
CyC2018 已提交
50 51

```java
C
CyC2018 已提交
52 53 54 55 56 57 58 59
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();
C
CyC2018 已提交
60 61 62
}
```

C
CyC2018 已提交
63
**快速选择** :时间复杂度 O(N),空间复杂度 O(1)
C
CyC2018 已提交
64 65

```java
C
CyC2018 已提交
66 67 68 69 70 71 72 73 74 75 76 77 78 79
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];
C
CyC2018 已提交
80 81
}

C
CyC2018 已提交
82 83 84 85 86 87 88 89 90 91 92 93
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;
C
CyC2018 已提交
94 95
}

C
CyC2018 已提交
96 97 98 99
private void swap(int[] a, int i, int j) {
    int t = a[i];
    a[i] = a[j];
    a[j] = t;
C
CyC2018 已提交
100 101 102
}
```

C
CyC2018 已提交
103
# 桶排序
C
CyC2018 已提交
104

C
CyC2018 已提交
105
## 1. 出现频率最多的 k 个元素
C
CyC2018 已提交
106

C
CyC2018 已提交
107
[347. Top K Frequent Elements (Medium)](https://leetcode.com/problems/top-k-frequent-elements/description/)
C
CyC2018 已提交
108 109

```html
C
CyC2018 已提交
110
Given [1,1,1,2,2,3] and k = 2, return [1,2].
C
CyC2018 已提交
111 112
```

C
CyC2018 已提交
113
设置若干个桶,每个桶存储出现频率相同的数。桶的下标表示数出现的频率,即第 i 个桶中存储的数出现的频率为 i。
C
CyC2018 已提交
114

C
CyC2018 已提交
115
把数都放到桶之后,从后向前遍历桶,最先得到的 k 个数就是出现频率最多的的 k 个数。
C
CyC2018 已提交
116 117

```java
C
CyC2018 已提交
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
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;
C
CyC2018 已提交
143 144 145
}
```

C
CyC2018 已提交
146
## 2. 按照字符出现次数对字符串排序
C
CyC2018 已提交
147

C
CyC2018 已提交
148
[451. Sort Characters By Frequency (Medium)](https://leetcode.com/problems/sort-characters-by-frequency/description/)
C
CyC2018 已提交
149 150 151 152 153 154 155 156 157

```html
Input:
"tree"

Output:
"eert"

Explanation:
C
CyC2018 已提交
158 159
'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.
C
CyC2018 已提交
160 161 162
```

```java
C
CyC2018 已提交
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
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();
C
CyC2018 已提交
188 189 190
}
```

C
CyC2018 已提交
191
# 荷兰国旗问题
C
CyC2018 已提交
192 193 194

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

C
CyC2018 已提交
195
有三种颜色的球,算法的目标是将这三种球按颜色顺序正确地排列。它其实是三向切分快速排序的一种变种,在三向切分快速排序中,每次切分都将数组分成三个区间:小于切分元素、等于切分元素、大于切分元素,而该算法是将数组分成三个区间:等于红色、等于白色、等于蓝色。
C
CyC2018 已提交
196

C
CyC2018 已提交
197
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/7a3215ec-6fb7-4935-8b0d-cb408208f7cb.png"/> </div><br>
C
CyC2018 已提交
198

C
CyC2018 已提交
199

C
CyC2018 已提交
200
## 1. 按颜色进行排序
C
CyC2018 已提交
201

C
CyC2018 已提交
202
[75. Sort Colors (Medium)](https://leetcode.com/problems/sort-colors/description/)
C
CyC2018 已提交
203 204

```html
C
CyC2018 已提交
205 206
Input: [2,0,2,1,1,0]
Output: [0,0,1,1,2,2]
C
CyC2018 已提交
207 208
```

C
CyC2018 已提交
209
题目描述:只有 0/1/2 三种颜色。
C
CyC2018 已提交
210 211

```java
C
CyC2018 已提交
212 213 214 215 216 217 218 219 220 221 222
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;
        }
    }
C
CyC2018 已提交
223 224
}

C
CyC2018 已提交
225 226 227 228
private void swap(int[] nums, int i, int j) {
    int t = nums[i];
    nums[i] = nums[j];
    nums[j] = t;
C
CyC2018 已提交
229 230
}
```
C
CyC2018 已提交
231 232 233 234




C
CyC2018 已提交
235 236 237 238 239 240
# 微信公众号


微信公众号 CyC2018 提供了该项目的离线阅读版本,后台回复 "下载" 即可领取。也提供了一份技术面试复习大纲,不仅系统整理了面试知识点,而且标注了各个知识点的重要程度,从而帮你理清多而杂的面试知识点,后台回复 "大纲" 即可领取。我基本是按照这个大纲来进行复习的,对我拿到了 BAT 头条等 Offer 起到很大的帮助。你们完全可以和我一样根据大纲上列的知识点来进行复习,就不用看很多不重要的内容,也可以知道哪些内容很重要从而多安排一些复习时间。


C
CyC2018 已提交
241
<img width="580px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/other/公众号海报2.png"></img>