数组是存放在连续内存空间上的相同类型数据的集合。
数组下标都是从0开始的。
数组内存空间的地址是连续的
正是因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,要移动其他元素的地址。
二分查找
力扣题目链接
题目描述
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例 1:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
示例 2:
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
提示:
你可以假设 nums 中的所有元素是不重复的。
n 将在 [1, 10000]之间。
nums 的每个元素都将在 [-9999, 9999]之间。
思路
这道题目的前提是数组为有序数组,同时题目还强调数组中无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的,这些都是使用二分法的前提条件。
首先,定义 target 是在一个在左闭右闭的区间里,也就是[left, right]
因为定义target在[left, right]区间,所以有如下两点:
- while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
- if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
示例:
nums = [-1,0,3,5,9,12], target = 9
第一次:left->0,right->5; mid=2 ; nums[mid]=3<9 所以target一定在mid右边;将mid+1的值赋值给left;
第二次:left->3,right->5; mid=4; nums[mid]=9 找到target,返回下标
代码
public class Q704erfen {
public static void main(String[] args) {
int n = 6;
int[] nums = new int[n];
int target = 9;
nums = new int[]{-1, 0, 3, 5, 9, 12};
target = 9;
System.out.println(search(nums,target));
}
public static int search(int[] nums, int target) {
// 避免当 target 小于nums[0] nums[nums.length - 1]时多次循环运算
if (target < nums[0] || target > nums[nums.length - 1]) {
return -1;
}
int left = 0;
int right = nums.length-1; // 定义target在左闭右闭的区间里,[left, right]
while (left<=right){ // 当left==right,区间[left, right]依然有效,所以用 <=
int mid = (left + (right-left)/2); // 防止溢出 等同于(left + right)/2
if(nums[mid]>target){
right = mid-1; //中间值比目标值大,说明目标值在左边,右指针左移
} else if (nums[mid]<target) {
left = mid+1; //中间值比目标值小,说明目标值在右边,左指针右移
}else {
return mid; //中间值刚好等于目标值,说明找到了下标
}
}
return -1; // 数组中没有目标值,返回-1
}
}
移除元素---双指针
力扣链接
题目描述
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝
int len = removeElement(nums, val);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}
示例 1:
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
示例 2:
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,3,0,4]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。
提示:
0 <= nums.length <= 100
0 <= nums[i] <= 50
0 <= val <= 100
思路
双指针法
双指针法(快慢指针法): 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
定义快慢指针:
快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
慢指针:指向更新 新数组下标的位置
示例:
nums = [0,1,2,2,3,0,4,2], val = 2
- fast->0,slow->0; nums[fast]=0 slow++
- fast->1,slow->1; nums[fast]=1 slow++
- fast->2,slow->2; nums[fast]=2
- fast->3,slow->2; nums[fast]=2
- fast->4,slow->2; nums[fast]=3 nums[slow]=nums[fast] slow++
- fast->5,slow->3; nums[fast]=0 nums[slow]=nums[fast] slow++
- fast->6,slow->4; nums[fast]=4 nums[slow]=nums[fast] slow++
- fast->7,slow->5; nums[fast]=2
- fast遍历结束 return slow;
代码
public class Q27Removelement {
public static void main(String[] args) {
int n = 8;
int[] nums = new int[n];
int val = 9;
nums = new int[]{0, 1, 2, 2, 3, 0, 4, 2};
val = 2;
System.out.println(removeElement(nums,val));
}
public static int removeElement(int[] nums, int val) {
int slow = 0;
for(int fast=0;fast<nums.length;fast++){
//不符合目标值的时候双指针一起移动,
//符合目标值之后,快指针移动(跳过该元素),
//慢指针接着等待下一次不符合的元素
if(nums[fast]!=val){
nums[slow++]=nums[fast];
}
}
return slow;
}
}
长度最小的子数组---滑动窗口
力扣链接
题目描述
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例:
输入:s = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
提示:
1 <= target <= 10^9
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^5
思路
滑动窗口:
所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
s=7, 数组是 2,3,1,2,4,3
代码
public class Q209minLengthArray {
public static void main(String[] args) {
int[] nums = new int[]{2,3,1,2,4,3};
int target = 7;
System.out.println(minSubArrayLen(target,nums));
}
public static int minSubArrayLen(int s, int[] nums) {
int left = 0;
int sum = 0;
int result = Integer.MAX_VALUE; //记录最小的符合条件的子数组长度
//left窗口代表起始位置,right代表窗口的终点位置
for(int right=0;right<nums.length;right++){
sum+=nums[right]; //计算滑动窗口内的总和
while (sum>=s){ //判断滑动窗口内的总和是否大于等于目标值
//true,记录窗口大小,并减小窗口,计算当前窗口总和
result = Math.min(result,(right-left+1)); //比较最小的子数组长度
sum -= nums[left++]; //计算当前总和并窗口减小,即窗口起始位置+1
}
}
return result==Integer.MAX_VALUE ? 0 : result;
}
}