代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素

目录

数组理论基础  

704.二分查找

注意事项

第一种解法:左闭右闭

第二种解法:左闭右开

 27. 移除元素

暴力解法

双指针解法

 Debug


数组理论基础  

文章链接

704.二分查找

题目链接

文章讲解

  • 注意事项

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)


 27. 移除元素

题目链接

文章讲解

  • 暴力解法

两层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)

  •  Debug

应该用if却用成while

if和while是C语言中的两种控制语句,它们的主要区别如下:

  1. 功能不同:if语句用于执行条件判断,根据条件的真假来决定是否执行特定的代码块;而while语句用于循环执行特定的代码块,直到条件不再满足时停止循环。

  2. 执行次数不同:if语句只执行一次,根据条件的结果决定是否执行代码块;而while语句可以执行多次,只要条件满足就会一直执行代码块,直到条件不再满足时停止。

  3. 条件判断位置不同:if语句的条件判断通常在代码块之前进行,用来决定是否执行代码块;而while语句的条件判断通常在代码块之后进行,用来决定是否继续执行代码块。

  4. 代码块执行顺序不同:if语句中的代码块只有在条件为真时才会执行,否则跳过;而while语句中的代码块会在每次循环之前进行判断,只有在条件为真时才会执行。

总的来说,if语句用于条件判断,根据条件的真假来决定是否执行代码块;而while语句用于循环执行代码块,只要条件满足就会一直执行,直到条件不再满足为止。

循环变量和数组大小数值改变

你可能感兴趣的:(代码随想录一刷,算法,数据结构)