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


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

C
CyC2018 已提交
15
用于求解  **Kth Element**  问题,使用快速排序的 partition() 进行实现。
C
CyC2018 已提交
16

C
CyC2018 已提交
17
需要先打乱数组,否则最坏情况下时间复杂度为 O(N<sup>2</sup>)。
C
CyC2018 已提交
18

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

C
CyC2018 已提交
21
用于求解  **TopK Elements**  问题,通过维护一个大小为 K 的堆,堆中的元素就是 TopK Elements。
C
CyC2018 已提交
22

C
CyC2018 已提交
23
堆排序也可以用于求解 Kth Element 问题,堆顶元素就是 Kth Element。
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
## Kth Element
C
CyC2018 已提交
30

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

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

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

```java
C
CyC2018 已提交
38 39 40
public int findKthLargest(int[] nums, int k) {
    Arrays.sort(nums);
    return nums[nums.length - k];
C
CyC2018 已提交
41 42 43
}
```

C
CyC2018 已提交
44
**堆排序** :时间复杂度 O(NlogK),空间复杂度 O(K)。
C
CyC2018 已提交
45 46

```java
C
CyC2018 已提交
47 48 49 50 51 52 53 54
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 已提交
55 56 57
}
```

C
CyC2018 已提交
58
**快速选择** :时间复杂度 O(N),空间复杂度 O(1)
C
CyC2018 已提交
59 60

```java
C
CyC2018 已提交
61 62 63 64 65 66 67 68 69 70 71 72 73 74
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 已提交
75 76
}

C
CyC2018 已提交
77 78 79 80 81 82 83 84 85 86 87 88
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 已提交
89 90
}

C
CyC2018 已提交
91 92 93 94
private void swap(int[] a, int i, int j) {
    int t = a[i];
    a[i] = a[j];
    a[j] = t;
C
CyC2018 已提交
95 96 97
}
```

C
CyC2018 已提交
98
# 桶排序
C
CyC2018 已提交
99

C
CyC2018 已提交
100
## 出现频率最多的 k 个数
C
CyC2018 已提交
101

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

```html
C
CyC2018 已提交
105
Given [1,1,1,2,2,3] and k = 2, return [1,2].
C
CyC2018 已提交
106 107
```

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

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

```java
C
CyC2018 已提交
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
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 已提交
138 139 140
}
```

C
CyC2018 已提交
141
## 按照字符出现次数对字符串排序
C
CyC2018 已提交
142

C
CyC2018 已提交
143
[451. Sort Characters By Frequency (Medium)](https://leetcode.com/problems/sort-characters-by-frequency/description/)
C
CyC2018 已提交
144 145 146 147 148 149 150 151 152

```html
Input:
"tree"

Output:
"eert"

Explanation:
C
CyC2018 已提交
153 154
'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 已提交
155 156 157
```

```java
C
CyC2018 已提交
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
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 已提交
183 184 185
}
```

C
CyC2018 已提交
186
# 荷兰国旗问题
C
CyC2018 已提交
187 188 189 190 191 192 193

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

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

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

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

C
CyC2018 已提交
196

C
CyC2018 已提交
197
## 按颜色进行排序
C
CyC2018 已提交
198

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

```html
C
CyC2018 已提交
202 203
Input: [2,0,2,1,1,0]
Output: [0,0,1,1,2,2]
C
CyC2018 已提交
204 205
```

C
CyC2018 已提交
206
题目描述:只有 0/1/2 三种颜色。
C
CyC2018 已提交
207 208

```java
C
CyC2018 已提交
209 210 211 212 213 214 215 216 217 218 219
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 已提交
220 221
}

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




C
CyC2018 已提交
232
</br><div align="center">🎨 </br></br> 更多精彩内容将发布在公众号 **CyC2018**,公众号提供了该项目的离线阅读版本,后台回复"下载" 即可领取。也提供了一份技术面试复习思维导图,不仅系统整理了面试知识点,而且标注了各个知识点的重要程度,从而帮你理清多而杂的面试知识点,后台回复"资料" 即可领取。我基本是按照这个思维导图来进行复习的,对我拿到了 BAT 头条等 Offer 起到很大的帮助。你们完全可以和我一样根据思维导图上列的知识点来进行复习,就不用看很多不重要的内容,也可以知道哪些内容很重要从而多安排一些复习时间。</div></br>
C
CyC2018 已提交
233
<div align="center"><img width="180px" src="https://cyc-1256109796.cos.ap-guangzhou.myqcloud.com/%E5%85%AC%E4%BC%97%E5%8F%B7.jpg"></img></div>