代码随想录(1)-数组
数组是一个非常基础的数据类型。
数组是存放在连续内存空间上的相同类型数据的集合。
下标从零开始,且元素从只能被覆盖不能被删除。
题单
- 704.二分查找
- 27.移除元素
- 977.有序数组的平方
- 209.长度最小的子数组(middle)
- 59.螺旋矩阵II
704. 二分查找
题目:给定一个排好序的一维数组和一个查找目标,需要给出目标在数组中的下标,若无则返回-1。
递归-我的解
我通过递归完成了这一题。但观察官方是通过循环完成的。循环的开销小一点,应该更比递归更块一点。
class Solution {
public int search(int[] nums, int target) {
return searchWithIndex(nums, target, 0, nums.length - 1);
}
public int searchWithIndex(int[] nums, int target, int left, int right){
if(left > right){
return -1;
}
int middle = (int) (left + right) / 2;
if( nums[middle] == target ){
return middle;
}else if( nums[middle] > target ){
return searchWithIndex(nums, target, left, middle - 1);
}else{
return searchWithIndex(nums, target, left + 1, right);
}
}
}
循环
所以我通过循环实现一次。
class Solution {
public int search(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while(left <= right ){
int middle = (left + right) / 2;
if( nums[middle] > target ){
right = middle - 1;
}else if( nums[middle] < target ){
left = middle + 1;
}else{
return middle;
}
}
return -1;
}
}
结果对比
结果显示循环确实更快,但是内存并没有更低。我觉得递归方法栈的开销应该比循环体的开销大啊。【挖坑待填】27. 移除元素
题目:
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
我的思路:
计数器n为0,遍历数组元素位置i:
若nums[i]等于val,n++;
否则,该元素向前移动n个位置。
返回原长度减去n。
代码:
class Solution {
public int removeElement(int[] nums, int val) {
int n = 0, len = nums.length ;
for (int i = 0; i < nums.length; i++) {
if( nums[i] == val ){
++n;
}else{
nums[i-n] = nums[i];
}
}
return len - n;
}
}
2023/01/28 11:47:43
解答成功:
执行耗时:0 ms,击败了100.00% 的Java用户
内存消耗:40 MB,击败了63.71% 的Java用户
977.有序数组的平方
双指针法:核心思路是从两边往中心推进,直至没有元素。
class Solution {
public int[] sortedSquares(int[] nums) {
int len = nums.length;
int[] ans = new int[len];
int i = 0, j = len - 1;
while( i <= j ){
if (nums[i] * nums[i] <= nums[j] * nums[j]) {
ans[--len] = nums[j] * nums[j];
j--;
}else{
ans[--len] = nums[i] * nums[i];
i++;
}
}
return ans;
}
}
209.长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例:
输入:s = 7, nums = [2,3,1,2,4,3] 输出:2 解释:子数组 [4,3] 是该条件下的长度最小的子数组。
额外数组法(我的最早思路)
额外用两个数组分别储存最小的连续个数,这些连续数的和。
然后遍历数组处理,在前一个元素的基础上,更新最小连续个数以及他们的和。
public int minSubArrayLen(int target, int[] nums) {
int[] ans = new int[nums.length]; // 当前元素代表和大于等于target的最少的前n个元素(若前面元素相加的最大值,则取最大值)
int[] sum = new int[nums.length]; // 当前元素代表前n个元素的和
int min = 1 << 30;
if(nums == null || nums.length == 0){
return 0;
}
ans[0] = 1;
sum[0] = nums[0];
for (int i = 1; i < nums.length; i++) {
// 首要目标是完成累加
// 先把当前元素加上前一个元素的结果
ans[i] = ans[ i - 1 ] + 1;
sum[i] = sum[ i - 1 ] + nums[i];
// 若sum大于target,然后对最前的元素进行删减判断,直到前面不能筛
for (int j = ans[i] - 1; j > 0 && ( sum[i] - nums[i - j] >= target ); j--) {
sum[i] -= nums[i - j];
ans[i]--;
}
if(sum[i] >= target && ans[i] < min){
min = ans[i];
}
}
for (int i = 0; i < nums.length; i++) {
if(sum[i] >= target && ans[i] < min){
min = ans[i];
}
}
if( min == 1 << 30 ){
return 0;
}else {
return min;
}
}
我的代码逻辑有点冗余,阅读题解后,思想可以提炼为滑动窗口的思想。
滑动窗口
使用前后两个下标即可。
遍历右下标,每次加上右下标所在的元素,然后判断左下标是否可以删除,最后判断是否刷新了最短记录。
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int i = 0, j = 0, sum = 0, min = 1 << 30;
for (; i < nums.length && j <= i; i++) {
sum += nums[i];
while ((sum - nums[j]) >= target) {
sum -= nums[j];
j++;
}
if (sum >= target && min > (i - j + 1)) {
min = i - j + 1;
}
}
if( min == 1 << 30 ){
return 0;
}else {
return min;
}
}
}
59.螺旋矩阵II
给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
示例:
输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]
思路
注意区间,画四条边注意左闭右开。
public static int[][] generateMatrix(int n) {
int[][] ans = new int[n][n];
int loop = (int)( n / 2.0 + 0.5);
int counter = 1;
for (int i = 0; i < loop; i++) {
int x = i, y = i;
for (; y < n - i - 1 ; y++) {
ans[x][y] = counter++;
}
for (; x < n - i - 1 ; x++) {
ans[x][y] = counter++;
}
for (; y > i ; y--) {
ans[x][y] = counter++;
}
for (; x > i ; x--) {
ans[x][y] = counter++;
}
}
if( n % 2 == 1){
ans[loop - 1][loop - 1] = counter;
}
return ans;
}
总结
通过双指针法可以更优雅的完成一些探索区间的任务,减少逻辑判断。
通过螺旋矩阵这一题注意数值区间开闭的重要性。