代码随想录-数组理论基础
数组理论基础
二分查找
二分查找前提条件:有序数组且无重复元素,想好是用左闭右闭还是左闭右开!
- 如果是前者,while(left <= right),left==right是有意义的,后续if(nums[middle] > target),right要赋值为middle-1,因为当前的middle已经判断不等于,所以接下来查找的区间为middle-1
- 如果是后者,while(left < right), left==right无意义,后续的比较right就可以更新为middle,即:下一个查询区间不会去比较nums[middle]
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例 1:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
// 采用left <= right
public int search(int[] nums, int target){
// 比较首尾提前判断
if (target < nums[0] || target > nums[nums.length - 1]) {
return -1;
}
int left = 0, right = nums.length-1;
// left < right
while (left <= right) {
// 位运算,相当于/2
int mid = left + ((right - left) >> 1);
if (nums[mid] == target) {
return mid;
} else if (nums[mid] < target) {
// left = mid;
left = mid + 1;
} else if (nums[mid] > target) {
// right = mid;
right = mid - 1;
}
}
return -1;
}
移除元素(暴力&双指针)
要知道数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖(从后往前移)。
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并**原地**修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1: 给定 nums = [3,2,2,3], val = 3, 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。 你不需要考虑数组中超出新长度后面的元素。
// 暴力
public static int solution(int[] nums, int val) {
// 定义数组长度,用于后续删减
int size = nums.length;
for (int i = 0; i < size; i++) {
// 如果是目标数则将后续元素向前移,同时减掉i和size的数值
if (nums[i] == val) {
for (int j = i + 1; j < size; j++) {
nums[j - 1] = nums[j];
}
i--;
size--;
}
}
return size;
}
// 快慢指针
public static int removeElement(int[] nums, int val) {
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.length; fastIndex++) {
// 如果是目标数的话,只增加fast,反之两者都加,最后取slow
if (nums[fastIndex] != val) {
nums[slowIndex] = nums[fastIndex];
slowIndex++;
}
}
return slowIndex;
}
有序数组的平方(暴力&双指针)
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例 1: 输入:nums = [-4,-1,0,3,10] 输出:[0,1,9,16,100] 解释:平方后,数组变为 [16,1,0,9,100],排序后,数组变为 [0,1,9,16,100]
// 暴力,不需要管数字是否按顺序
public void sortedArr() {
int[] arr = {-4, -1, 0, 3, 10};
for (int i = 0; i < arr.length; i++) {
arr[i] = arr[i] * arr[i];
}
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
}
// 双指针,前提是数组是排好序的
public void squreArrSorted(int[] arr){
// 设置双指针
int right = arr.length - 1;
int left = 0;
// 新数组用来存放数据
int[] result = new int[arr.length];
int index = arr.length - 1;
// 因为排完序后,数的平方(最大值)一定是在两边的,所以index从后往前
while (left <= right) {
if (arr[left] * arr[left] > arr[right] * arr[right]) {
result[index--] = arr[left] * arr[left];
++left;
} else {
result[index--] = arr[right] * arr[right];
--right;
}
System.out.println(Arrays.toString(result));
}
}
长度最小的子数组(滑动窗口)
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例:
输入:s = 7, nums = [2,3,1,2,4,3] 输出:2 解释:子数组 [4,3] 是该条件下的长度最小的子数组。
滑动窗口:不断调节子序列的起始位置和终止位置,从而得出我们想要的结果
- 窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组
- 窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)
- 窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引
public static void main(String[] args) {
int[] nums = {2, 3, 1, 2, 4, 3};
int target = 7;
int left = 0, sum = 0;
// 假定一个最小值,如果最小值没有出现,在后续三元运算中处理
int result = Integer.MAX_VALUE;
// 开始滑动
for (int right = 0; right < nums.length; right++) {
sum += nums[right];
// 如果总数等于目标值,进行判断,大于的情况就移动左指针同时减少总数
while (sum >= target) {
// 获取子数组最小值
result = Math.min(result, right - left + 1);
sum -= nums[left++];
}
}
// 用三元运算符处理最小值,如果没有的话就返回0
result = result == Integer.MAX_VALUE ? 0 : result;
System.out.println(result);
}
螺旋矩阵
给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
示例:
输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]
坚持循环不变量原则,模拟顺时针画矩阵的过程:
- 填充上行从左到右
- 填充右列从上到下
- 填充下行从右到左
- 填充左列从下到上
由外向内一圈一圈这么画下去
public int[][] generateMatrix(int n) {
// n:表示n*n的数组
int[][] res = new int[n][n];
// 每次循环开始点,圈数,起始数(填充数字)
int start = 0, loop = 0, count = 1;
int i, j = 0;
// 如果n/2(表示一共需要转几次圈)为奇数,中心点需要手动赋值
while (loop++ < n / 2) {
// 上侧从左往右,都是左闭右开(如果n=3,则填充0和1,2留到下一个循环使用)
for (j = start; j < n - loop; j++) {
res[start][j] = count++;
}
// 右侧从上往下
for (i = start; i < n - loop; i++) {
res[i][j] = count++;
}
// 下测从右往左
for (; j >= loop; j--) {
res[i][j] = count++;
}
// 左侧从下往上
for (; i >= loop; i--) {
res[i][j] = count++;
}
// 每转完一圈需要在起始点加一
start++;
}
// 如果转的圈数为奇数,中心点需要手动赋值
if (n % 2 == 1) {
res[start][start] = count;
}
return res;
}