LeetCode 排序
基础部分
912. 排序数组
中等
给你一个整数数组 nums
,请你将该数组升序排列。
示例 1:
输入:nums = [5,2,3,1]
输出:[1,2,3,5]
示例 2:
输入:nums = [5,1,1,2,0,0]
输出:[0,0,1,1,2,5]
提示:
1 <= nums.length <= 50000
-50000 <= nums[i] <= 50000
class Solution {
public int[] sortArray(int[] nums) {
quickSort(nums,0,nums.length-1);
return nums;
}
private void quickSort(int[] nums, int l, int r) {
if (l >= r) return;
int mid = pattern(nums,l,r);
if (mid != l){
int tmp = nums[mid];
nums[mid] = nums[l];
nums[l] = tmp;
}
quickSort(nums,l,mid-1);
quickSort(nums,mid+1,r);
}
private int pattern(int[] nums, int l, int r) {
int first = nums[l];
int i = l;
int j = r + 1;
while (i < j){
do { //先判断后加,会多加一个,所以 do..while
i++;
}while (nums[i] < first && i < r);
do {
j--;
}while (nums[j] > first && j > l);
if (i >= j) break; //弄多了就换回来了,所以检查一下
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
return j;
}
}
215. 数组中的第K个最大元素
中等
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
说明:
你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。
//减治的快排
class Solution {
public int findKthLargest(int[] nums, int k){
quickSort(nums,k-1,0,nums.length-1);
return nums[k-1];
}
private void quickSort(int[] nums, int index, int l, int r) {
if (l >= r) return;
int mid = pattern(nums,l,r);
if (mid != l){
int tmp = nums[mid];
nums[mid] = nums[l];
nums[l] = tmp;
}
if (mid > index) quickSort(nums,index,l,mid-1);
else quickSort(nums,index,mid+1,r);
}
private int pattern(int[] nums, int l, int r) {
int first = nums[l];
int i = l;
int j = r + 1;
while (i < j){
do {
i++;
}while (nums[i] > first && i < r);
do {
j--;
}while (nums[j] < first && j > l);
if (i >= j) break;
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
return j;
}
}
//堆排序:建立k大小的小根堆,堆顶即为所求
class Solution {
public int findKthLargest(int[] nums, int k){ //O(n*logk)
//PriorityQueue 优先级队列,底层是二叉堆
//(n1,n2)->n1-n2 小根堆
PriorityQueue<Integer> heap = new PriorityQueue<>((n1,n2)->n1-n2);
for (int num : nums) {
heap.add(num); //O(logk)
if (heap.size() > k) heap.poll(); //O(logk)
}
return heap.poll();
}
}
347. 前 K 个高频元素
中等
给定一个非空的整数数组,返回其中出现频率前 *k* 高的元素。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
提示:
- 你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
- 你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。
- 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。
- 你可以按任意顺序返回答案。
// 设置若干个桶,每个桶存储出现频率相同的数。桶的下标表示数出现的频率,即第 i 个桶中存储的数出现的频率为 i。
// 把数都放到桶之后,从后向前遍历桶,最先得到的 k 个数就是出现频率最多的的 k 个数。
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer,Integer> map = new HashMap<>();
for (int num : nums)
map.put(num,map.getOrDefault(num,0)+1); //数字频数哈希表
List<Integer>[] buckets = new LinkedList[nums.length+1];
for (Integer num : map.keySet()){
int freq = map.get(num);
if (buckets[freq] == null) //没初始化=>没有内存=>没法add
buckets[freq] = new LinkedList<>();
buckets[freq].add(num); //放到对应频数的桶里
}
int[] res = new int[k];
int index = 0;
for (int i = buckets.length-1; i > 0 && index < k; i--) {
if (buckets[i] == null) continue;
for (Integer num : buckets[i])
res[index++] = num;
}
return res;
}
}
451. 根据字符出现频率排序
中等
给定一个字符串,请将字符串里的字符按照出现的频率降序排列。
示例 1:
输入:
"tree"
输出:
"eert"
解释:
'e'出现两次,'r'和't'都只出现一次。
因此'e'必须出现在'r'和't'之前。此外,"eetr"也是一个有效的答案。
示例 2:
输入:
"cccaaa"
输出:
"cccaaa"
解释:
'c'和'a'都出现三次。此外,"aaaccc"也是有效的答案。
注意"cacaca"是不正确的,因为相同的字母必须放在一起。
示例 3:
输入:
"Aabb"
输出:
"bbAa"
解释:
此外,"bbaA"也是一个有效的答案,但"Aabb"是不正确的。
注意'A'和'a'被认为是两种不同的字符。
class Solution {
public String frequencySort(String s) {
char[] chars = s.toCharArray();
Map<Character,Integer> map = new HashMap<>();
for (char aChar : chars)
map.put(aChar,map.getOrDefault(aChar,0)+1);
List<Character>[] buckets = new LinkedList[chars.length+1];
for (Character c : map.keySet()) {
int freq = map.get(c);
if (buckets[freq] == null) buckets[freq] = new LinkedList<>();
buckets[freq].add(c);
}
StringBuilder res = new StringBuilder();
for (int i = buckets.length - 1; i > 0; i--) {
if (buckets[i] == null) continue;
for (Character c : buckets[i]) {
for (int j = 0; j < i; j++) res.append(c);
if (res.length() == chars.length) break;
}
}
return res.toString();
}
}
75. 颜色分类
中等
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
注意:
不能使用代码库中的排序函数来解决这道题。
示例:
输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]
进阶:
- 一个直观的解决方案是使用计数排序的两趟扫描算法。
首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。 - 你能想出一个仅使用常数空间的一趟扫描算法吗?
// 0跟head换,head++;2跟tail换,tail--
// 换就完事了,判断一样费时间
// 注意:跟tail换时指针不往前走,因为不知道换个啥回来
class Solution {
public void sortColors(int[] nums) {
int head = 0, curr = 0;
int tail = nums.length - 1;
int tmp;
while (curr <= tail) {
if (nums[curr] == 0) {
tmp = nums[head];
nums[head++] = nums[curr];
nums[curr++] = tmp;
}
else if (nums[curr] == 2) {
tmp = nums[curr];
nums[curr] = nums[tail];
nums[tail--] = tmp;
}
else curr++;
}
}
}
频率排序
853. 车队
中等
N
辆车沿着一条车道驶向位于 target
英里之外的共同目的地。
每辆车 i
以恒定的速度 speed[i]
(英里/小时),从初始位置 position[i]
(英里) 沿车道驶向目的地。
一辆车永远不会超过前面的另一辆车,但它可以追上去,并与前车以相同的速度紧接着行驶。
此时,我们会忽略这两辆车之间的距离,也就是说,它们被假定处于相同的位置。
车队 是一些由行驶在相同位置、具有相同速度的车组成的非空集合。注意,一辆车也可以是一个车队。
即便一辆车在目的地才赶上了一个车队,它们仍然会被视作是同一个车队。
会有多少车队到达目的地?
示例:
输入:target = 12, position = [10,8,0,5,3], speed = [2,4,1,1,3]
输出:3
解释:
从 10 和 8 开始的车会组成一个车队,它们在 12 处相遇。
从 0 处开始的车无法追上其它车,所以它自己就是一个车队。
从 5 和 3 开始的车会组成一个车队,它们在 6 处相遇。
请注意,在到达目的地之前没有其它车会遇到这些车队,所以答案是 3。
提示:
0 <= N <= 10 ^ 4
0 < target <= 10 ^ 6
0 < speed[i] <= 10 ^ 6
0 <= position[i] < target
- 所有车的初始位置各不相同。
class Car{ //用类来整合比别的数据结构舒服
int position;
double time;
public Car(int position, double time) {
this.position = position;
this.time = time;
}
}
class Solution {
public int carFleet(int target, int[] position, int[] speed) {
int len = position.length;
Car[] cars = new Car[len];
for (int i = 0; i < len; i++)
cars[i] = new Car(position[i],(double)(target-position[i])/speed[i]);
Arrays.sort(cars,(x,y)->(y.position-x.position)); //按位置的降序
int res = 0;
for (int i = 0; i < len; i++) {
res++;
double head = cars[i].time;
while (i < len && cars[i].time <= head) i++; //在我后边的,用时小于等于我的,才能追上我,和我同步到达
i--; //for自带自增,会多加一次,所以减回来
}
return res;
}
}
179. 最大数
中等
给定一组非负整数,重新排列它们的顺序使之组成一个最大的整数。
示例 1:
输入: [10,2]
输出: 210
示例 2:
输入: [3,30,34,5,9]
输出: 9534330
说明: 输出结果可能非常大,所以你需要返回一个字符串而不是整数。
class Solution {
public String largestNumber(int[] nums) {
//这样写会报错,基本数据类型都不能这么些,所以后边先把int[]转成了String[]
//Arrays.sort(nums,(x,y) -> (String.valueOf(y)+String.valueOf(x)).compareTo(String.valueOf(x)+String.valueOf(y));
String[] strs = new String[nums.length];
for(int i = 0; i < nums.length; i++)
strs[i] = String.valueOf(nums[i]);
Arrays.sort(strs, (x, y) -> (y + x).compareTo(x + y));
StringBuilder res = new StringBuilder();
for(String s : strs)
res.append(s);
return res.toString();
}
}
148. 排序链表
中等
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0
输出: -1->0->3->4->5
class Solution {
public ListNode sortList(ListNode head) {
if (head == null || head.next == null)
return head; //递归到头只有head了,这时候开始merge
ListNode slow = head, fast = head.next;
while (fast != null && fast.next != null){ //二分链表
fast = fast.next.next;
slow = slow.next;
}
ListNode head2 = slow.next;
slow.next = null;
return merge(sortList(head),sortList(head2)); //递归合并
}
private ListNode merge(ListNode a,ListNode b) { //合并有序链表
ListNode node = new ListNode(0);
ListNode p = node;
while (a != null && b != null){
if (a.val < b.val){
p.next = a;
a = a.next;
}else {
p.next = b;
b = b.next;
}
p = p.next;
}
p.next = a != null ? a : b;
return node.next;
}
}
56. 合并区间
中等
给出一个区间的集合,请合并所有重叠的区间。
示例 1:
输入: [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入: [[1,4],[4,5]]
输出: [[1,5]]
解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。
class Solution {
public int[][] merge(int[][] intervals) {
if (intervals.length < 2) return intervals;
Arrays.sort(intervals,(a,b)->(a[0]-b[0]));
List<int[]> list = new LinkedList<>();
list.add(intervals[0]);
for (int i = 1; i < intervals.length; i++) {
int tail = list.get(list.size()-1)[1];
if (intervals[i][0] > tail){
list.add(intervals[i]);
}else if (intervals[i][1] > tail){
list.get(list.size()-1)[1] = intervals[i][1];
}
}
int len = list.size();
int[][] res = new int[len][2];
for (int i = 0; i < len; i++) {
res[i] = list.get(i);
}
return res;
}
}