目录
- 1. 轮转数组:给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数
- 2. 下一个更大元素:nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素
- 3. 图像渲染
- 4. 最长递增子序列
- 5. 最小K个数
- 6. 救生艇
- 7. 组合总和
- 8. 螺旋矩阵
- 9. 四数之和
- 10. 加油站在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。给定两个整数数组 gas 和 cost ,如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的
- 11. 有效的数独
- 12. 只出现一次的数字 III
- 13. 二维数组中的查找
- 14. 螺旋矩阵 II:给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix
- 15. 第 N 位数字
- 16. 前 K 个高频元素:给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案
- 17. 三数之和:给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。注意:答案中不可以包含重复的三元组
- 18. 四数之和:给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复)
1. 轮转数组:给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数
示例:
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]
解法1:
public void rotate(int[] nums, int k) {
int len = nums.length;
int[] newArr = new int[len];
for (int i = 0; i < len; i++) {
newArr[(i + k) % len] = nums[i];
}
System.arraycopy(newArr, 0, nums, 0, len);
}
解法2:
public void rotate1(int[] nums, int k) {
int len = nums.length;
k = k % len;
reverse(nums, 0, len - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, len - 1);
}
public void reverse(int[] nums, int start, int end) {
while (start < end) {
int temp = nums[start];
nums[start] = nums[end];
nums[end] = temp;
start++;
end--;
}
}
解法3:
public void rotate2(int[] nums, int k) {
int len = nums.length;
k = k % len;
int count = gcd(k, len);
for (int i = 0; i < count; i++) {
int pre = nums[i];
int current = i;
do {
int next = (current + k) % len;
int temp = nums[next];
nums[next] = pre;
current = next;
pre = temp;
} while (i != current);
}
}
private int gcd(int x, int y) {
return y > 0 ? gcd(y, x % y) : x;
}
补充说明:
2. 下一个更大元素:nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素
示例:
输入:nums1 = [4,1,2], nums2 = [1,3,4,2].
输出:[-1,3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:
- 4 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
- 1 ,用加粗斜体标识,nums2 = [1,3,4,2]。下一个更大元素是 3 。
- 2 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
Deque<Integer> stack = new ArrayDeque<Integer>();
for (int i = nums2.length - 1; i >= 0; --i) {
int num = nums2[i];
while (!stack.isEmpty() && num >= stack.peek()) {
stack.pop();
}
map.put(num, stack.isEmpty() ? -1 : stack.peek());
stack.push(num);
}
int[] res = new int[nums1.length];
for (int i = 0; i < nums1.length; ++i) {
res[i] = map.get(nums1[i]);
}
return res;
}
补充说明:
3. 图像渲染
示例:
输入: image = [[1,1,1],[1,1,0],[1,0,1]],sr = 1, sc = 1, newColor = 2
输出: [[2,2,2],[2,2,0],[2,0,1]]
解析: 在图像的正中间,(坐标(sr,sc)=(1,1)),在路径上所有符合条件的像素点的颜色都被更改成2。
注意,右下角的像素没有更改为2,因为它不是在上下左右四个方向上与初始点相连的像素点。
private int[][] array = new int[][]{{0, -1}, {0, 1}, {1, 0}, {-1, 0}};
private int rowLen = 0;
private int colLen = 0;
public int[][] floodFill(int[][] image, int sr, int sc, int color) {
int originColor = image[sr][sc];
if (originColor == color) {
return image;
}
rowLen = image.length;
colLen = image[0].length;
dealArray(image, sr, sc, originColor, color);
return image;
}
private void dealArray(int[][] image, int sr, int sc, int originColor, int color) {
if (sr < 0 || sc < 0 || sr >= rowLen || sc >= colLen
|| image[sr][sc] != originColor) {
return;
}
image[sr][sc] = color;
for (int i = 0; i < array.length; i++) {
dealArray(image, sr + array[i][0],
sc + array[i][1], originColor, color);
}
}
4. 最长递增子序列
示例:
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
public int lengthOfLIS1(int[] nums) {
int len = nums.length;
int[] dp = new int[len];
int result = 1;
dp[0] = 1;
for (int i = 1; i < len; i++) {
dp[i] = 1;
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
result = Math.max(result, dp[i]);
}
return result;
}
补充说明:
5. 最小K个数
示例:
输入: arr = [1,3,5,7,2,4,6,8], k = 4
输出: [1,2,3,4]
解法1:堆排序
public int[] smallestK(int[] arr, int k) {
int[] result = new int[k];
if (k == 0) {
return result;
}
PriorityQueue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
for (int i = 0; i < k; i++) {
queue.offer(arr[i]);
}
for (int i = k; i < arr.length; i++) {
if (arr[i] < queue.peek()) {
queue.poll();
queue.offer(arr[i]);
}
}
Iterator<Integer> integerIterator = queue.iterator();
int index = 0;
while (integerIterator.hasNext()) {
result[index++] = integerIterator.next();
}
return result;
}
解法2:快排思想
public static int[] smallestK1(int[] arr, int k) {
int[] result = new int[k];
if (k == 0) {
return result;
}
quickDealArr(arr, 0, arr.length - 1, k);
for (int i = 0; i < k; i++) {
result[i] = arr[i];
}
return result;
}
private static void quickDealArr(int[] arr, int left, int right, int k) {
if (left >= right) {
return;
}
int index = randomPartition(arr, left, right);
int num = index - left + 1;
if (num == k) {
return;
} else if (num > k) {
quickDealArr(arr, left, index - 1, k);
} else {
quickDealArr(arr, index + 1, right, k - num);
}
}
private static int randomPartition(int[] arr, int left, int right) {
int random = new Random().nextInt(right - left + 1) + left;
spwap(arr, random, right);
return spliteArray(arr, left, right);
}
private static int spliteArray(int[] arr, int left, int right) {
int index = left;
for (int i = left; i <= right - 1; i++) {
if (arr[i] <= arr[right]) {
spwap(arr, index, i);
index++;
}
}
spwap(arr, index, right);
return index;
}
private static void spwap(int[] arr, int random, int right) {
int temp = arr[random];
arr[random] = arr[right];
arr[right] = temp;
}
6. 救生艇
示例:
输入:people = [3,2,2,1], limit = 3
输出:3
解释:3 艘船分别载 (1, 2), (2) 和 (3)
public int numRescueBoats(int[] people, int limit) {
int result = 0;
Arrays.sort(people);
int left = 0;
int right = people.length - 1;
while(left <= right){
if(people[left] + people[right] <= limit){
left ++;
}
result ++;
right --;
}
return result;
}
7. 组合总和
示例:
输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> ans = new ArrayList<List<Integer>>();
List<Integer> combine = new ArrayList<Integer>();
dfs(candidates, target, ans, combine, 0);
return ans;
}
public void dfs(int[] candidates, int target, List<List<Integer>> ans, List<Integer> combine, int idx) {
if (idx == candidates.length) {
return;
}
if (target == 0) {
ans.add(new ArrayList<Integer>(combine));
return;
}
if(target < 0){
return;
}
// 直接跳过
dfs(candidates, target, ans, combine, idx + 1);
// 选择当前数
if (target - candidates[idx] >= 0) {
combine.add(candidates[idx]);
dfs(candidates, target - candidates[idx], ans, combine, idx);
combine.remove(combine.size() - 1);
}
}
补充说明:
8. 螺旋矩阵
示例:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
public static List<Integer> spiralOrder(int[][] matrix) {
List<Integer> res = new ArrayList<>();
int m = matrix.length;
int n = matrix[0].length;
boolean[][] flag = new boolean[m][n];
int[] dirRow = new int[]{0, 1, 0, -1};
int[] dirCol = new int[]{1, 0, -1, 0};
int startDir = 0;
int startRow = 0;
int starCol = 0;
while (res.size() < m * n) {
res.add(matrix[startRow][starCol]);
flag[startRow][starCol] = true;
int nextRow = startRow + dirRow[startDir];
int nextCol = starCol + dirCol[startDir];
if (nextRow < 0 || nextRow >= m ||
nextCol < 0 || nextCol >= n
|| flag[nextRow][nextCol]) {
startDir = (startDir + 1) % 4;
}
startRow = startRow + dirRow[startDir];
starCol = starCol + dirCol[startDir];
}
return res;
}
9. 四数之和
示例:
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> quadruplets = new ArrayList<List<Integer>>();
if (nums == null || nums.length < 4) {
return quadruplets;
}
Arrays.sort(nums);
int length = nums.length;
for (int i = 0; i < length - 3; i++) {
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
if ((long) nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) {
break;
}
if ((long) nums[i] + nums[length - 3] + nums[length - 2] + nums[length - 1] < target) {
continue;
}
for (int j = i + 1; j < length - 2; j++) {
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
if ((long) nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) {
break;
}
if ((long) nums[i] + nums[j] + nums[length - 2] + nums[length - 1] < target) {
continue;
}
int left = j + 1, right = length - 1;
while (left < right) {
int sum = nums[i] + nums[j] + nums[left] + nums[right];
if (sum == target) {
quadruplets.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
while (left < right && nums[left] == nums[left + 1]) {
left++;
}
left++;
while (left < right && nums[right] == nums[right - 1]) {
right--;
}
right--;
} else if (sum < target) {
left++;
} else {
right--;
}
}
}
}
return quadruplets;
}
补充说明:
10. 加油站在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。给定两个整数数组 gas 和 cost ,如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的
示例:
输入: gas = [1,2,3,4,5], cost = [3,4,5,1,2]
输出: 3
解释:
从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
因此,3 可为起始索引。
public int canCompleteCircuit(int[] gas, int[] cost) {
int n = gas.length;
int i = 0;
while (i < n) {
int sumOfGas = 0, sumOfCost = 0;
int cnt = 0;
while (cnt < n) {
int j = (i + cnt) % n;
sumOfGas += gas[j];
sumOfCost += cost[j];
if (sumOfCost > sumOfGas) {
break;
}
cnt++;
}
if (cnt == n) {
return i;
} else {
i = i + cnt + 1;
}
}
return -1;
}
补充说明:
若从位置i能到达i + cnt,但不能到达i + cnt+1。
则i~i+cnt的任意位置,都无法到达i + cnt+1。则可以直接
跳过i~i+cnt,令i + cnt+1为新的起点。
11. 有效的数独
示例:
输入:board =
[[“5”,“3”,“.”,“.”,“7”,“.”,“.”,“.”,“.”]
,[“6”,“.”,“.”,“1”,“9”,“5”,“.”,“.”,“.”]
,[“.”,“9”,“8”,“.”,“.”,“.”,“.”,“6”,“.”]
,[“8”,“.”,“.”,“.”,“6”,“.”,“.”,“.”,“3”]
,[“4”,“.”,“.”,“8”,“.”,“3”,“.”,“.”,“1”]
,[“7”,“.”,“.”,“.”,“2”,“.”,“.”,“.”,“6”]
,[“.”,“6”,“.”,“.”,“.”,“.”,“2”,“8”,“.”]
,[“.”,“.”,“.”,“4”,“1”,“9”,“.”,“.”,“5”]
,[“.”,“.”,“.”,“.”,“8”,“.”,“.”,“7”,“9”]]
输出:true
public boolean isValidSudoku(char[][] board) {
int[][] rows = new int[9][9];
int[][] columns = new int[9][9];
int[][][] subboxes = new int[3][3][9];
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
char c = board[i][j];
if (c != '.') {
int index = c - '0' - 1;
rows[i][index]++;
columns[j][index]++;
subboxes[i / 3][j / 3][index]++;
if (rows[i][index] > 1 || columns[j][index] > 1 || subboxes[i / 3][j / 3][index] > 1) {
return false;
}
}
}
}
return true;
}
12. 只出现一次的数字 III
示例:
输入:nums = [1,2,1,3,2,5]
输出:[3,5]
解释:[5, 3] 也是有效的答案。
解法1:
、public int[] singleNumber(int[] nums) {
int [] result = new int[2];
int index = 0;
Set<Integer> set = new HashSet<>();
for (int i = 0; i < nums.length; i++) {
if(set.contains(nums[i])){
set.remove(nums[i]);
}else{
set.add(nums[i]);
}
}
Iterator iterator = set.iterator();
while(iterator.hasNext()){
result[index ++] = (int) iterator.next();
}
return result;
}
解法2:
public int[] singleNumber2(int[] nums) {
int [] result = new int[2];
int index = 0;
Arrays.sort(nums);
int len = nums.length;
for (int i = 0; i < len; i++) {
if(i == 0){
if(nums[i] != nums[i + 1]){
result[index ++] = nums[i];
}
}else if(i == len - 1){
if(nums[i] != nums[i - 1]){
result[index ++] = nums[i];
}
}else{
if(nums[i] != nums[i - 1] && nums[i] != nums[i + 1]){
result[index ++] = nums[i];
}
}
}
return result;
}
解法3:
public int[] singleNumber(int[] nums) {
int xorsum = 0;
for (int num : nums) {
xorsum ^= num;
}
// 防止溢出
int lsb = (xorsum == Integer.MIN_VALUE ? xorsum : xorsum & (-xorsum));
int type1 = 0, type2 = 0;
for (int num : nums) {
if ((num & lsb) != 0) {
type1 ^= num;
} else {
type2 ^= num;
}
}
return new int[]{type1, type2};
}
补充说明:
一个数和它的相反数做与运算,能把最低位得1提取出来。
对Integer.MIN_VALUE取反,会出现溢出得问题,值仍为Integer.MIN_VALUE。
且Integer.MIN_VALUE仅有最高位存在唯一得1,所以没有必要做与运算。
13. 二维数组中的查找
示例:
现有矩阵 matrix 如下:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false。
public boolean findNumberIn2DArray(int[][] matrix, int target) {
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return false;
}
int rows = matrix.length, columns = matrix[0].length;
int row = 0, column = columns - 1;
while (row < rows && column >= 0) {
int num = matrix[row][column];
if (num == target) {
return true;
} else if (num > target) {
column--;
} else {
row++;
}
}
return false;
}
14. 螺旋矩阵 II:给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix
示例:
输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]
public int[][] generateMatrix(int n) {
int[][] res = new int[n][n];
boolean[][] flag = new boolean[n][n];
int startNum = 1;
int[][] dir = new int[][]{{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
int dirNum = 0;
int row = 0;
int col = 0;
while (startNum <= n * n) {
res[row][col] = startNum ++;
flag[row][col] = true;
int nextRow = row + dir[dirNum][0];
int nextCol = col + dir[dirNum][1];
if (nextRow < 0 || nextRow >= n || nextCol < 0 || nextCol >= n
|| flag[nextRow][nextCol]) {
dirNum = (dirNum + 1) % 4;
}
row = row + dir[dirNum][0];
col = col + dir[dirNum][1];
}
return res;
}
补充说明:
可以不声明二维数组进行标识,利用数组本身元素值大于0即可代表已赋值过这一性质即可。
boolean[][] flag = new boolean[n][n]
15. 第 N 位数字
示例:
输入:n = 11
输出:0
解释:第 11 位数字在序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, … 里是 0 ,它是 10 的一部分。
解法1:
class Solution {
public int findNthDigit(int n) {
int low = 1, high = 9;
while (low < high) {
int mid = (high - low) / 2 + low;
if (totalDigits(mid) < n) {
low = mid + 1;
} else {
high = mid;
}
}
int d = low;
int prevDigits = totalDigits(d - 1);
int index = n - prevDigits - 1;
int start = (int) Math.pow(10, d - 1);
int num = start + index / d;
int digitIndex = index % d;
int digit = (num / (int) (Math.pow(10, d - digitIndex - 1))) % 10;
return digit;
}
public int totalDigits(int length) {
int digits = 0;
int curLength = 1, curCount = 9;
while (curLength <= length) {
digits += curLength * curCount;
curLength++;
curCount *= 10;
}
return digits;
}
}
补充说明:
解法2:
class Solution1 {
public int findNthDigit(int n) {
int d = 1, count = 9;
while (n > (long) d * count) {
n -= d * count;
d++;
count *= 10;
}
int index = n - 1;
int start = (int) Math.pow(10, d - 1);
int num = start + index / d;
int digitIndex = index % d;
int digit = (num / (int)(Math.pow(10, d - digitIndex - 1))) % 10;
return digit;
}
}
16. 前 K 个高频元素:给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案
示例:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
解法1:快排思想
public int[] topKFrequent(int[] nums, int k) {
int[] result = new int[k];
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
map.put(nums[i], map.getOrDefault(nums[i], 0) + 1);
}
ArrayList<int[]> list = new ArrayList<>();
for (Integer key : map.keySet()) {
int[] arr = new int[]{key, map.get(key)};
list.add(arr);
}
dealNums(list, 0, list.size() - 1, k);
for (int i = 0; i < k; i++) {
result[i] = list.get(i)[0];
}
return result;
}
private void dealNums(ArrayList<int[]> list, int left, int right, int k) {
int index = getIndexByList(list, left, right);
int num = index - left + 1;
if (num == k) {
return;
} else if (num > k) {
dealNums(list, left, index - 1, k);
} else {
dealNums(list, index + 1, right, k - num);
}
}
private int getIndexByList(ArrayList<int[]> list, int left, int right) {
int randomIndex = getRandom(left, right);
Collections.swap(list, randomIndex, right);
int num = list.get(right)[1];
int index = left;
for (int i = left; i < right; i++) {
if (list.get(i)[1] >= num) {
Collections.swap(list, index, i);
index++;
}
}
Collections.swap(list, index, right);
return index;
}
private int getRandom(int left, int right) {
return new Random().nextInt(right - left + 1) + left;
}
解法2:堆排思想(类库实现)
public static int[] topKFrequent(int[] nums, int k) {
int len = nums.length;
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < len; i++) {
map.put(nums[i], map.getOrDefault(nums[i], 0) + 1);
}
PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[1] - o2[1];
}
});
for (int key : map.keySet()) {
queue.offer(new int[]{key, map.get(key)});
if (queue.size() > k) {
queue.poll();
}
}
int[] re = new int[k];
int index = 0;
while (!queue.isEmpty()) {
re[index++] = queue.poll()[0];
}
return re;
}
解法3:堆排思想(手写堆排)
public int[] topKFrequent2(int[] nums, int k) {
int len = nums.length;
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < len; i++) {
map.put(nums[i], map.getOrDefault(nums[i], 0) + 1);
}
ArrayList<int[]> list = new ArrayList<>();
for (int key : map.keySet()) {
int[] arr = new int[]{key, map.get(key)};
list.add(arr);
}
int listIndex = list.size() - 1;
buildMaxList(list, listIndex);
Collections.swap(list, 0, listIndex);
for (int i = listIndex - 1; i >= listIndex - k + 1; i--) {
buildList(list, 0, i);
Collections.swap(list, 0, i);
}
int[] result = new int[k];
int index = 0;
for (int i = listIndex; i >= listIndex - k + 1; i--) {
result[index++] = list.get(i)[0];
}
return result;
}
private void buildMaxList(ArrayList<int[]> list, int maxIndex) {
for (int i = maxIndex / 2; i >= 0; i--) {
buildList(list, i, maxIndex);
}
}
private void buildList(ArrayList<int[]> list, int root, int maxIndex) {
int left = 2 * root + 1;
int right = 2 * root + 2;
int max = root;
if (left <= maxIndex && list.get(left)[1] > list.get(root)[1]) {
max = left;
}
if (right <= maxIndex && list.get(right)[1] > list.get(max)[1]) {
max = right;
}
if (max != root) {
Collections.swap(list, max, root);
buildList(list, max, maxIndex);
}
}
17. 三数之和:给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。注意:答案中不可以包含重复的三元组
示例:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
Arrays.sort(nums);
int len = nums.length;
for (int i = 0; i < len - 2; i++) {
if(i > 0 && nums[i] == nums[i - 1]){
continue;
}
if(nums[i] + nums[i + 1] + nums[i + 2] > 0){
break;
}
if(nums[i] + nums[len - 1] + nums[len - 2] < 0){
continue;
}
int left = i + 1;
int right = len - 1;
while(left < right){
int sum = nums[i] + nums[left] + nums[right];
if(sum == 0){
List<Integer> cur = new ArrayList<>();
cur.add(nums[i]);
cur.add(nums[left]);
cur.add(nums[right]);
list.add(cur);
while(left < right && nums[left] == nums[left + 1]){
left++;
}
left++;
while(left < right && nums[right] == nums[right - 1]){
right--;
}
right--;
}else if(sum > 0){
right--;
}else{
left++;
}
}
}
return list;
}
18. 四数之和:给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复)
示例:
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
public static List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> list = new ArrayList<>();
int len = nums.length;
if (len <= 3) {
return list;
}
Arrays.sort(nums);
for (int i = 0; i < len - 3; i++) {
while (i > 0 && i < len - 3 && nums[i - 1] == nums[i]) {
i++;
}
if (i < len - 3 && (long) nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) {
break;
}
if (i < len - 3 && (long) nums[i] + nums[len - 3] + nums[len - 2] + nums[len - 1] < target) {
continue;
}
for (int j = i + 1; j < len - 2; j++) {
while (j > i + 1 && j < len - 2 && nums[j - 1] == nums[j]) {
j++;
}
if (j < len - 2 && (long) nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) {
break;
}
if (j < len - 2 && (long) nums[i] + nums[j] + nums[len - 2] + nums[len - 1] < target) {
continue;
}
int left = j + 1;
int right = len - 1;
int remain = target - nums[i] - nums[j];
while (left < right) {
int tempValue = nums[left] + nums[right];
if (tempValue == remain) {
List<Integer> temp = new ArrayList<>();
temp.add(nums[i]);
temp.add(nums[j]);
temp.add(nums[left]);
temp.add(nums[right]);
list.add(temp);
}
if (tempValue >= remain) {
int rightLess = right - 1;
while (rightLess > left && nums[rightLess] == nums[right]) {
rightLess--;
}
right = rightLess;
}
if (tempValue <= remain) {
int leftMore = left + 1;
while (right > leftMore && nums[leftMore] == nums[left]) {
leftMore++;
}
left = leftMore;
}
}
}
}
return list;
}
补充说明:
此题有两个需要注意的地方:
1.以0, 0, 0, 1000000000, 1000000000, 1000000000, 1000000000为例,在此处将出现数值溢出的问题。所以要先转成long。
2.对于重复元素的处理