力扣刷题之部分数组经典算法题目
作者vantoker
LeetCode1: 两数之和
题目说明:
说明:给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出和为目标值 的那两个整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
注意:
1,返回结果为数组下标
2,只会存在一个有效答案
3,你可以按任意顺序返回答案
代码:
//经典解法
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
if (map.containsKey(target - nums[i])) {
return new int[]{map.get(target - nums[i]), i};
} else
map.put(nums[i], i);
}
return new int[2];
}
执行结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:38.7 MB, 在所有 Java 提交中击败了47.08% 的用户
LeetCode59: 螺旋矩阵II
题目说明:
说明:输入一个整数n,返回一个n*n的二维数组存放1到n2的所有元素,顺时针螺旋排列。
代码:
注意点:
1,本题的难点在于边界处理,行尾列尾到头之后,需要将行列的索引进行修正。
2,设置合理的右上边界,和左下边界,并且随着num位置的移动,适时的修改边界值的大小
3,外部循环条件,不可设置为num<=numLimit,会形成死循环,因为num达到numLimit之后不会再进入任何一个自循环进行自增。
public int[][] generateMatrix(int n) {
int[][] res = new int[n][n];
int num = 0, row = 0, col = -1;
int numLimit = n * n, leftUpLimit = 0, rightDownLimit = n - 1;
while (num != numLimit) {
while (++col <= rightDownLimit) {
res[row][col] = ++num;
}
col--; //索引修正
while (++row <= rightDownLimit) {
res[row][col] = ++num;
}
row--;
rightDownLimit--; //边界值修正
while (--col >= leftUpLimit) {
res[row][col] = ++num;
}
col++;
leftUpLimit++;
while (--row >= leftUpLimit) {
res[row][col] = ++num;
}
row++;
}
return res;
}
运行结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:36.7 MB, 在所有 Java 提交中击败了20.52% 的用户
LeetCode66: 加一
题目说明:
说明:给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
代码:
注意点:
1,注意几种特殊的情况,会产生进位或者进位让数组长度加一。
2,如果当前元素没有产生进位,那么本次结束就可以return。
3,判断的核心不是carry是否为1,而是curNum是否大于等于10.
public int[] plusOne(int[] digits) {
int carry = 1, curNum = 0;
for (int i = digits.length - 1; i >= 0; i--) {
curNum = digits[i] + carry;
if (curNum >= 10) { //如果产生进位
curNum = curNum % 10;
digits[i] = curNum;
} else { //如果当前位置没有进位,则return
digits[i] = curNum;
return digits;
}
}
int[] res = new int[digits.length + 1]; //如果运行到当前位置,说明原数组所有位置都产生了进位
res[0] = 1;
return res;
}
执行结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:37.1 MB, 在所有 Java 提交中击败了19.22% 的用户
剑指Offer03: 数组中重复的数字
题目说明:
说明:在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,
也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
限制:2 <= n <= 100000
代码一:
//解法一:利用Set集合的特性
public int findRepeatedNumber(int[] nums){
Set<Integer> set = new HashSet<>();
for (int num : nums) {
if (set.contains(num)){
return num;
}else
set.add(num);
}
return -1;
}
执行结果:
执行用时:8 ms, 在所有 Java 提交中击败了27.03% 的用户
内存消耗:47.2 MB, 在所有 Java 提交中击败了37.71% 的用户
代码二:
//解法二:排序+遍历
public int findRepeatedNumber2(int[] nums){
Arrays.sort(nums);
for (int i = 1; i < nums.length; i++) {
if (nums[i]==nums[i-1])
return nums[i];
}
return -1;
}
执行结果:
执行用时:3 ms, 在所有 Java 提交中击败了57.86% 的用户
内存消耗:46.1 MB, 在所有 Java 提交中击败了74.85% 的用户
代码三:
//解法三:原地置换
public int findRepeatedNumber3(int[] nums){
int temp;
for (int i = 0; i < nums.length; i++) {
while (nums[i] != i) { //如果索引i位置的元素不为i,则进入一下处理
if (nums[i] == nums[nums[i]]) //如果索引为i的位置处数字也为nums[i],说明存在重复
return nums[i];
/*temp = nums[i];
nums[i] = nums[nums[i]];
nums[nums[i]] = temp;*/ //此处不可以这样调用,因为nums[i]在第二步发生了改变,不可作为原本的索引了。
temp = nums[i];
nums[i]=nums[temp];
nums[temp]=temp;
}
}
return -1;
}
执行结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:46.2 MB, 在所有 Java 提交中击败了63.94% 的用户
LeetCode75: 颜色分类
题目说明:
说明:给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,
并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
代码一:
//解法一:计数+填充数字
public void sortColors(int[] nums) {
int red = 0, white = 0, blue = 0;
for (int num : nums) {
if (num == 0)
red++;
else if (num == 1)
white++;
else
blue++;
}
int index = -1;
while (red-- > 0) {
nums[++index] = 0;
}
while (white-- > 0) {
nums[++index] = 1;
}
while (blue-- > 0) {
nums[++index] = 2;
}
}
执行结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:37.2 MB, 在所有 Java 提交中击败了11.64% 的用户
代码二:
注意点:
1,明确first,second指向的准备位置,否则在判断时把小于等于号写为小于号就会出错。
2,first左边位置(不包含本身位置)的元素都是0,second右边位置(不包含本身位置)的元素都是2.
//解法二:双指针快排(一趟遍历)
public void sortColors2(int[] nums) {
int i = 0;
int first = 0, second = nums.length - 1;//两个边界指针
int temp;
while (i <= second) { //注意此处是i小于等于边界值second,不是小于second,也不是小于nums.length
if (nums[i] == 1) {
i++;
} else if (nums[i] == 0) {
temp = nums[i];
nums[i] = nums[first];
nums[first] = temp;
first++;
i++;
} else {
temp = nums[i];
nums[i] = nums[second];
nums[second] = temp;
second--;
}
}
}
执行结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:37 MB, 在所有 Java 提交中击败了55.35% 的用户
LeetCode54: 螺旋矩阵
题目说明:
说明:给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
提示:
m == matrix.length
n == matrix[i].length
1 <= m, n <= 10
-100 <= matrix[i][j] <= 100
代码一:
注意点:
1,本题和螺旋矩阵II的区别是,矩阵的行列值不相同,那么设置边界值就需要不同。
2,本题在每个while循环之后都进行了判断,这是防止添加结束之后,不进入while循环,但是会改变边界值导致可能进入后面的while循环。
3,上述说的后面到终点时,会可能继续会进入while循环的可能原因是只设置了三个边界值,如果设置四个边界值,可能就不需要每个while循环之后添加判断。
public List<Integer> spiralOrder(int[][] matrix) {
int m = matrix.length, n = matrix[0].length, leftLimit = 0;
int i = 0, j = -1;
int count = 0, countLimit = m * n;
List<Integer> res = new ArrayList<>();
while (true) {
while (++j < n) {
res.add(matrix[i][j]);
count++;
}
if (count >= countLimit)
break;
j--;
n--;
while (++i < m) {
res.add(matrix[i][j]);
count++;
}
if (count >= countLimit)
break;
i--;
m--;
while (--j >= leftLimit) {
res.add(matrix[i][j]);
count++;
}
if (count >= countLimit)
break;
j++;
leftLimit++;
while (--i >= leftLimit) {
res.add(matrix[i][j]);
count++;
}
if (count >= countLimit)
break;
i++;
}
return res;
}
执行结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:36.6 MB, 在所有 Java 提交中击败了47.37% 的用户
代码二:
//设置四个边界值的尝试
public List<Integer> spiralOrder2(int[][] matrix) {
int m = matrix.length, n = matrix[0].length, leftUPLimit = 0, leftDownLimit = 0;
int i = 0, j = -1;
int count = 0, countLimit = m * n;
List<Integer> res = new ArrayList<>();
while (count != countLimit) {
while (++j < n) {
res.add(matrix[i][j]);
count++;
}
j--;
n--;
while (++i < m) {
res.add(matrix[i][j]);
count++;
}
i--;
m--;
while (--j >= leftDownLimit) {
res.add(matrix[i][j]);
count++;
}
j++;
leftDownLimit++;
leftUPLimit++;
while (--i >= leftUPLimit) {
res.add(matrix[i][j]);
count++;
}
i++;
}
return res;
}
执行结果:
结论:
1,尝试不成功,并不是边界值少的问题,而是队列的行和列数不相等的关系。
2,由于m,n不等,导致了左边的边界在终点处无法限制终点值往左边移动。
LeetCode560: 和为k的子数组
题目说明:
说明:给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。
注意 :
数组的长度为 [1, 20,000]。
数组中元素的范围是 [-1000, 1000] ,且整数 k 的范围是 [-1e7, 1e7]。
代码一:
//暴力解法
public int subarraySum(int[] nums, int k) {
int count = 0, sum = 0;
int n = nums.length;
for (int i = 0; i < n; i++) {
for (int j = i; j < n; j++) {
sum += nums[j];
if (sum == k)
count++;
}
sum = 0;
}
return count;
}
执行结果:
执行用时:1102 ms, 在所有 Java 提交中击败了37.22% 的用户
内存消耗:41.5 MB, 在所有 Java 提交中击败了27.24% 的用户
代码二:
注意点:
1,利用前缀和来保留前面的累加和,后面可以直接调用,但是执行结果似乎还没有暴力解法的效率高。
2,调用前缀和时注意preNum[j]表示的是j之前的和,不包含j本身的位置。
//解法二:前缀和
public int subarraySum3(int[] nums, int k) {
int[] preSum = new int[nums.length + 1];
int count = 0;
for (int i = 0; i < nums.length; i++) {
preSum[i + 1] = preSum[i] + nums[i];
}
for (int i = 0; i < nums.length; i++) {
for (int j = i; j < nums.length; j++) {
if (preSum[j + 1] - preSum[i] == k) //此处注意是j+1
count++;
}
}
return count;
}
执行结果:
执行用时:1790 ms, 在所有 Java 提交中击败了5.03% 的用户
内存消耗:41 MB, 在所有 Java 提交中击败了48.07% 的用户
代码三:
注意点:
1,map里面存放的都是连续的前缀和
2,preSum是从头到当前位置的连续的和,在map中查找pre-k就是查找满足条件的连续前缀和
3,仔细揣摩一下,其实就是类似两数之和的解法思想,每到当前位置就对当前长度进行判断。
//解法三:前缀和优化(利用HashMap)
public int subarraySum4(int[] nums, int k) {
Map<Integer, Integer> map = new HashMap<>();
map.put(0, 1);
int preSum = 0, count = 0;
for (int i = 0; i < nums.length; i++) {
preSum += nums[i];
if (map.containsKey(preSum - k)) {
count += map.get(preSum - k);
}
map.put(preSum, map.getOrDefault(preSum, 0) + 1);
}
return count;
}
执行结果:
执行用时:26 ms, 在所有 Java 提交中击败了80.64% 的用户
内存消耗:41.1 MB, 在所有 Java 提交中击败了40.62% 的用户
代码四:
注意点:
1,没有想到用前缀数组来存储前缀的和,会比上一种解法效率高
//前缀和数组+map优化
public int subarraySum5(int[] nums, int k) {
Map<Integer, Integer> map = new HashMap<>();
int[] preSum = new int[nums.length + 1];
int count = 0;
preSum[0] = nums[0];
for (int i = 1; i < nums.length; i++) {
preSum[i] = preSum[i - 1] + nums[i];
}
map.put(0, 1);
for (int i = 0; i < nums.length; i++) {
Integer needPreSum = map.get(preSum[i]-k); //此处也可能提高了效率,因为只调用了get没有调用containsKey方法
if (needPreSum!=null)
count += needPreSum;
map.put(preSum[i], map.getOrDefault(preSum[i], 0) + 1);
}
return count;
}
执行结果:
执行用时:20 ms, 在所有 Java 提交中击败了97.24% 的用户
内存消耗:41.8 MB, 在所有 Java 提交中击败了13.71% 的用户
浙公网安备 33010602011771号