今日内容
数组理论基础,704. 二分查找,27. 移除元素
代码随想录链接:programmercarl.com/%E6%95%B0%E…
数组理论基础
数组是存放在连续内存空间上的相同类型数据的集合
- 数组的下标都是从0开始
- 数组内存空间的地址是连续的
因此增删数组中间的元素后,往往需要改变其他元素的地址
数组的元素是不能删除的,只能覆盖
二维数组在C++上的存储是连续的,Java上是随机开辟内存空间存放一维数组
704,二分查找
给定一个
n个元素有序的(升序)整型数组nums和一个目标值target,写一个函数搜索nums中的target,如果目标值存在返回下标,否则返回-1。
提示:
- 你可以假设
nums中的所有元素是不重复的。n将在[1, 10000]之间。nums的每个元素都将在[-9999, 9999]之间。
按照区间的定义,二分查找可以分为左右都闭合、左闭合右不闭合两种思路,但都是把用过的点都去掉
1.左右都闭合
区间的定义这就决定了二分法的代码应该如何写,因为定义target在[left, right]区间,所以有如下两点:
- while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
- if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
代码如下
class Solution {
public int search(int[] nums, int target) {
if(target < nums[0] || target > nums[nums.length - 1]){// 避免当 target 小于nums[0] nums[nums.length - 1]时多次循环运算
return -1;
}
int left = 0;
int right = nums.length - 1;
while(right >= left){
int mid = left + ((right - left) / 2);//防溢出
if (nums[mid] < target)
left = mid + 1;
else if (nums[mid] > target)
right = mid - 1;
else
return mid;
}
return -1;
}
}
两处细节:开头避免多次循环运算和mid的计算方式要防止溢出(代码注解)
2.左闭合右不闭合
这种情况下,只需要对上一种情况进行三处改动(不要死记硬背),代码如下:
class Solution {
public int search(int[] nums, int target) {
if(target < nums[0] || target > nums[nums.length - 1]){// 避免当 target 小于nums[0] nums[nums.length - 1]时多次循环运算
return -1;
}
int left = 0;
int right = nums.length;//第一处改动
while(right > left){//第二处改动
int mid = left + ((right - left) / 2);//防溢出
if (nums[mid] < target)
left = mid + 1;
else if (nums[mid] > target)
right = mid;//第三处改动
else
return mid;
}
return -1;
}
}
- 第一处改动:当区间右边是开区间时,right本身指向的元素不会被计算,为了保证所有元素都被计算,right要加一位,指向数组最后一位的后面一位。
- 第二处改动:因为right是开的(虚的),所以left等于right也就没有元素了。
- 第三处改动:因为实际上计算到的元素是right前一位元素,把mid赋值给right并不会重复计算mid元素。
小结
二分查找的两种方法都会遵循一些规律:
- 被计算过的mid将不会再被计算,因此在左右闭合的情况下,left和right都会在mid的基础上+1或-1,而左闭合右不闭合的情况下,left仍旧+1,而right直接取mid(right所指的元素不会在下一轮被计算)
- 抽象的来说,右开区间的情况下,实际计算的右边界是'right'值的前一个
27,移除元素
给你一个数组
nums**和一个值val,你需要 原地 移除所有数值等于val**的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用O(1)额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素
题目看了半天,解题思路是把数组里不等于val的数换到前面去?然后发现我for循环忘了怎么写了(乐.jpg)
代码如下:
class Solution {
public int removeElement(int[] nums, int val) {
int length=0 ;
for( int i = 0; i < nums.length;i++){
if(nums[i] != val){
nums[length] = nums[i];
length++;
}
}
return length;
}
}
补充:这是↑ 快慢指针方法 ↑,length是慢指针,i是快指针
还有一种解法, 代码如下
class Solution {
public int removeElement(int[] nums, int val) {
int left=0 ;
int right = nums.length-1;
while(right >= 0 && nums[right] == val)right--;//找到第一个非val元素
while(left <= right){
if(nums[left] == val){
nums[left] = nums[right];
right--;
}
left++;
while(right >= 0 && nums[right] == val) right--;//找到下一个非val元素
}
return left;
}
}
这是↑ 相向双指针法 ↑这个思路就是让指针从后往前找非val元素,然后换到前面的val元素位置上,计数的工作由left来完成。
还有另一写法,让left直接等于right,直到换过来的right不是val,再让left++,有巧妙:
class Solution {
public int removeElement(int[] nums, int val) {
int left = 0;
int right = nums.length - 1;
while(left <= right){
if(nums[left] == val){
nums[left] = nums[right];
right--;
}else {
// 这里兼容了right指针指向的值与val相等的情况
left++;
}
}
return left;
}
}
以上便是第一天的内容,第一次刷题,虽然题目好像很基础,奈何脑子不好使,写得磕磕绊绊。如有错误,请您指出。