重点掌握快排、堆排序、归并、桶排序等
下面排序算法直接复制Leetcode101里的
void quick_sort(vector<int> &nums, int l, int r) {
if (l + 1 >= r) {
return;
}
int first = l, last = r - 1, key = nums[first];
while (first < last){
while(first < last && nums[last] >= key) {
--last;
}
nums[first] = nums[last];
while (first < last && nums[first] <= key) {
++first;
}
nums[last] = nums[first];
}
nums[first] = key;
quick_sort(nums, l, first);
quick_sort(nums, first + 1, r);
}
// 分而治之
void merge_sort(vector<int> &nums, int l, int r, vector<int> &temp) {
if (l + 1 >= r) {
return;
}
// divide
int m = l + (r - l) / 2;
merge_sort(nums, l, m, temp);
merge_sort(nums, m, r, temp);
// conquer
int p = l, q = m, i = l;
while (p < m || q < r) {
if (q >= r || (p < m && nums[p] <= nums[q])) {
temp[i++] = nums[p++];
} else {
temp[i++] = nums[q++];
}
}
for (i = l; i < r; ++i) {
nums[i] = temp[i];
}
}
215. 数组中的第K个最大元素 – 中等
返回数组中第k个大元素
输入: [3,2,1,5,6,4], k = 2
输出: 5
/**
快排:每次会找到某个元素应该在的位置,如果此位置恰好是 nums.length - k 即是答案‘
使用随机快排:时间复杂度接近O(n)
**/
class Solution {
public int findKthLargest(int[] nums, int k) {
int location = nums.length - k; // 第k大元素应该数组中的位置
int l = 0, r = nums.length - 1;
while(l < r) { // l <= r 快排会越界
int mid = quickFind(nums, l, r);
if (mid > location) r = mid - 1;
else if (mid < location) l = mid + 1;
else return nums[mid];
}
return nums[location];
}
// 快速找到l应该在数组中的位置
private int quickFind(int[] nums, int l, int r) {
int loc = nums[l];
int low = l + 1, high = r;
while (true) { // 寻找loc位置
while(nums[low] <= loc && low < r) ++low;
while(nums[high] >= loc && high > l) --high;
if(low >= high) break;
swap(nums, low, high);
}
swap(nums, l, high);
return high;
}
public void swap(int[] nums, int l, int r) {
int temp = nums[l];
nums[l] = nums[r];
nums[r] = temp;
}
}
/**
堆排序: 时间复杂度O(nlogn)
初始构造长度 nums.length 的大根堆
每次移除一个元素,堆长度相应-1,一共移除 k-1 次, 此时堆顶元素就是第k大值
// 注:如果k超过 1/2 数组,可以找第 nums.length - k 小元素
**/
class Solution {
public int findKthLargest(int[] nums, int k) {
int heapSize = nums.length;
buildTopHeap(nums, heapSize);
for (int i = nums.length - 1; i > nums.length - k; --i) {
swap(nums, 0, i);
findMax(nums, 0, --heapSize);
}
return nums[0];
}
// 从 heapSize/2 上浮
private void buildTopHeap(int[] nums, int heapSize) {
for (int i = heapSize/2; i >= 0; --i) {
findMax(nums, i, heapSize);
}
}
private void findMax(int[] nums, int top, int heapSize) {
int lChild = 2 * top + 1, rChild = 2 * top + 2, maxIndex = top;
if (lChild < heapSize && nums[lChild] > nums[top]) maxIndex = lChild;
if (rChild < heapSize && nums[rChild] > nums[maxIndex]) maxIndex = rChild;
if (maxIndex != top) {
swap(nums, top, maxIndex);
findMax(nums, maxIndex, heapSize);
}
}
private void swap(int[] nums, int top, int maxIndex) {
int temp = nums[top];
nums[top] = nums[maxIndex];
nums[maxIndex] = temp;
}
}
347. 前 K 个高频元素 – 中等
返回数组中频率出现做多次数的前k个元素。 (多个答案返回任意一个)
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
/**
掌握如何实现桶排序
思路:优先选择频率较大的桶中的元素。
**/
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> map = new HashMap<>();
int maxSize = 0;
for (int i = 0; i < nums.length; i++) {
int cur = nums[i];
map.put(cur, map.getOrDefault(cur, 0) + 1);
maxSize = Math.max(maxSize, map.get(cur));
}
List<Integer>[] buckerLists = new ArrayList[maxSize + 1];
for (int key : map.keySet()) {
int score = map.get(key);
if (buckerLists[score] == null) buckerLists[score] = new ArrayList<>();
buckerLists[score].add(key);
}
int[] res = new int[k];
int idx = k-1;
for (int i = buckerLists.length - 1; i >= 0; --i) {
if (buckerLists[i] == null) continue;
for (int j = 0; j < buckerLists[i].size(); j++) {
res[idx--] = buckerLists[i].get(j);
if (idx < 0) break;
}
if (idx < 0) break;
}
return res;
}
}
451. 根据字符出现频率排序 – 中等
对字符串s按照字符频率降序排序,返回任何一个即可。
输入: s = "tree"
输出: "eert"
解释: 'e'出现两次,'r'和't'都只出现一次。
因此'e'必须出现在'r'和't'之前。此外,"eetr"也是一个有效的答案。
/**
桶排序:
排序:
也可以用Collection集合对key进行排序:
List<Character> list = new ArrayList<Character>(map.keySet());
Collections.sort(list, (a, b) -> map.get(b) - map.get(a));
这样效率低
**/
class Solution {
public String frequencySort(String s) {
Map<Character, Integer> map = new HashMap<>();
int max = 0;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
map.put(c, map.getOrDefault(c, 0) + 1);
max = Math.max(max, map.get(c));
}
List<Character>[] bucket = new ArrayList[max + 1];
for(char key : map.keySet()) {
int size = map.get(key);
if (bucket[size] == null) bucket[size] = new ArrayList<>();
bucket[size].add(key);
}
StringBuilder sb = new StringBuilder();
for (int i = bucket.length - 1; i > 0; --i) {
if (bucket[i] == null) continue;
for (char c : bucket[i]) {
int size = i;
while (size-- != 0) sb.append(c);
}
}
return new String(sb);
}
}
75. 颜色分类 – 中等
原地对三个颜色排序,使得相同颜色相邻,每个数字代表一种颜色。
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
/**
单指针:需要两次遍历,第一次把0放到最前面,第2次把1放到最前面。
双指针:一次遍历,只需要把0放前面,2放后面即可。
**/
class Solution {
public void sortColors(int[] nums) {
int p0 = 0, p2 = nums.length - 1;
for (int i = 0; i < nums.length; i++) {
if(i > p2) break;
if (nums[i] == 0 && i > p0) swap(nums, i--, p0++);
else if (nums[i] == 2 && i < p2) swap(nums, i--, p2--);
}
}
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}