目录
数组理论基础
704.二分查找
注意事项
第一种解法:左闭右闭
第二种解法:左闭右开
27. 移除元素
暴力解法
双指针解法
Debug
文章链接
题目链接
文章讲解
1,使用二分查找,数组必须是有序的。此题已经排序,否则需要自己进行排序。
2,题目已经假设,数组中的数都是不重复的,因为如果重复,结果就不唯一了。
3,对区间的定义不同,有两种写法,一种是左闭右闭,一种是左闭右开(其他两种不太常用,硬写也行,没有必要而已)。个人比较习惯左闭右闭的写法。
4,区间的定义可以认为是一种模式,一旦选择了某种模式,后面循环条件、变量初始化、变量更新等,就不要违反它。
这种情况下搜索区间为 [left, right],
当 target < nums[m] 时(即 target 在左区间时),对应的搜索区间为 [left, mid - 1],因为 mid 已经搜索过了,所以我们只需在 mid 左侧的闭区间内进行搜索;
当 target > nums[m] 时(即 target 在右区间时),对应的搜索区间为 [mid + 1, right],解释同上;
当循环结束时,left == right + 1,未找到目标,返回 -1。
// (版本一) 左闭右闭区间 [left, right]
int search(int* nums, int numsSize, int target){
int left = 0;
int right = numsSize-1;
int middle = 0;
//若left小于等于right,说明区间中元素不为0
while(left<=right) {
//更新查找下标middle的值
middle = (left+right)/2;
//此时target可能会在[left,middle-1]区间中
if(nums[middle] > target) {
right = middle-1;
}
//此时target可能会在[middle+1,right]区间中
else if(nums[middle] < target) {
left = middle+1;
}
//当前下标元素等于target值时,返回middle
else if(nums[middle] == target){
return middle;
}
}
//若未找到target元素,返回-1
return -1;
}
时间复杂度:O(log n)
空间复杂度:O(1)
这种情况下搜索区间为 [left, right),
当 target < nums[m] 时(即 target 在左区间时),对应的搜索区间为 [left, mid),因为右边界为开,不会被搜索,故我们只需让 right = mid 即可;
当 target > nums[m] 时(即 target 在右区间时),对应的搜索区间为 [mid + 1, right),因为左边界为闭,而 mid 已被搜索过,故我们需要让 left = mid + 1;
当循环结束时,left == right,我们需要判断当前下标对应的数组元素是否为目标元素,若是则返回下标,否则返回 -1。
// (版本二) 左闭右开区间 [left, right)
int search(int* nums, int numsSize, int target){
int length = numsSize;
int left = 0;
int right = length; //定义target在左闭右开的区间里,即:[left, right)
int middle = 0;
while(left < right){ // left == right时,区间[left, right)属于空集,所以用 < 避免该情况
int middle = left + (right - left) / 2;
if(nums[middle] < target){
//target位于(middle , right) 中为保证集合区间的左闭右开性,可等价为[middle + 1,right)
left = middle + 1;
}else if(nums[middle] > target){
//target位于[left, middle)中
right = middle ;
}else{ // nums[middle] == target ,找到目标值target
return middle;
}
}
//未找到目标值,返回-1
return -1;
}
时间复杂度:O(log n)
空间复杂度:O(1)
题目链接
文章讲解
两层for循环,一个for循环遍历数组元素 ,第二个for循环更新数组。
class Solution {
public:
int removeElement(vector& nums, int val) {
int size = nums.size();
for (int i = 0; i < size; i++) {
if (nums[i] == val) { // 发现需要移除的元素,就将数组集体向前移动一位
for (int j = i + 1; j < size; j++) {
nums[j - 1] = nums[j];
}
i--; // 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
size--; // 此时数组的大小-1
}
}
return size;
}
};
时间复杂度:O(n^2)
空间复杂度:O(1)
双指针法(快慢指针法): 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
定义快慢指针
int removeElement(int* nums, int numsSize, int val){
int slow = 0;
for(int fast = 0; fast < numsSize; fast++) {
//若快指针位置的元素不等于要删除的元素
if(nums[fast] != val) {
//将其挪到慢指针指向的位置,慢指针+1
nums[slow++] = nums[fast];
}
}
//最后慢指针的大小就是新的数组的大小
return slow;
}
时间复杂度:O(n)
空间复杂度:O(1)
应该用if却用成while
if和while是C语言中的两种控制语句,它们的主要区别如下:
功能不同:if语句用于执行条件判断,根据条件的真假来决定是否执行特定的代码块;而while语句用于循环执行特定的代码块,直到条件不再满足时停止循环。
执行次数不同:if语句只执行一次,根据条件的结果决定是否执行代码块;而while语句可以执行多次,只要条件满足就会一直执行代码块,直到条件不再满足时停止。
条件判断位置不同:if语句的条件判断通常在代码块之前进行,用来决定是否执行代码块;而while语句的条件判断通常在代码块之后进行,用来决定是否继续执行代码块。
代码块执行顺序不同:if语句中的代码块只有在条件为真时才会执行,否则跳过;而while语句中的代码块会在每次循环之前进行判断,只有在条件为真时才会执行。
总的来说,if语句用于条件判断,根据条件的真假来决定是否执行代码块;而while语句用于循环执行代码块,只要条件满足就会一直执行,直到条件不再满足为止。
循环变量和数组大小数值改变