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

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

704.二分查找

代码随想录文档

二分查找的使用前提是有序数组并无重复数字
在写二分查找时,关键是搞明白具体区间的左右开闭

自己首先写一版出来

首先自己想到的就是递归,写的代码如下,一次ac:

class Solution {
 public:
	int search(vector<int> &nums, int target) {
		int num = nums.size();
		return search2(nums, 0, num - 1, target);
	}
 private:
	int search2(vector<int> &nums, int low, int high, int target) {
		if (low > high) return -1;
		int mid = low + (high - low) / 2;
		if (nums[mid] > target) return search2(nums, low, mid-1, target);
		if (nums[mid] == target) return mid;
		if (nums[mid] < target) return search2(nums, mid+1, high, target);
		return false;
	}
};

自己写的时候想的几个点:

  • 首先原始函数入参只有数组和目标,直接递归的话,需要分割新的数组占用空间。所以直接新建函数,使用下标检索递归
  • 注意nums.size()的值比最大数组下标大一,所以搜索数组时high =nums.size()-1
  • 在递归中:
    • 首先写出退出的情况,如果low比high还大,那么说明数组中没有找到目标,直接返回-1。
    • 在求每次的二分中点的时候,想到卡哥说的防止超过int范围,所有使用low+(high-low)/2的方法,
  • 接下来就是二分法比较中点数值时的三种情况(数组升序):关键是在递归寻找两边的数组时,注意开闭区间。在左边的时候需要把右边的中值去掉选择[low,mid-1],在右边是选择去掉左边的中值[mid+1,high]。也是一种左闭右闭
  • 最后按照leetcode的要求,每个循环最后都需要有返回值,所以最后随便加了一行return false

代码随想录解法

好像都没有用递归,我自己写的递归占用空间比较多

使用左闭右闭区间搜索

定义target在左闭右闭这个区间,[left, right]

几个关键点:

  • 首先循环条件定为while (left <= right) ,因为左闭右闭,所以左=右是有意义的
  • if (nums[middle] > target) right = middle - 1 和我写的一个意思,因为左闭右闭,需要去掉比较过的mid值

具体代码如下:

// 版本一
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]
        while (left <= right) { // 当left==right,区间[left, right]依然有效,所以用 <=
            int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
            if (nums[middle] > target) {
                right = middle - 1; // target 在左区间,所以[left, middle - 1]
            } else if (nums[middle] < target) {
                left = middle + 1; // target 在右区间,所以[middle + 1, right]
            } else { // nums[middle] == target
                return middle; // 数组中找到目标值,直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};

  • 时间复杂度O(log n): 二分法循环的区间量级,n,n/2,n/4,…,n/2k(接下来操作元素的剩余个数),其中k就是循环的次数。 n/2^k=1.可得k=log2n(2为底)。所以复杂度为O(log n)
  • 空间复杂度:一直使用一个数组的常数空间所以O(n)
使用左闭右开搜索

定义target在左闭右闭这个区间,[left, right)

几个关键点:

  • 首先循环条件定为while (left < right) ,因为左闭右开,所以比较右边界是没有意义的。
  • if (nums[middle] > target) right = middle 因为当前nums[mid]不等于目标,去左边区间寻找,因为时左闭右开,可以将比较过的mid写进区间中,下一次也不会在对他进行比较

具体代码如下:

// 版本二
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size(); // 定义target在左闭右开的区间里,即:[left, right)
        while (left < right) { // 因为left == right的时候,在[left, right)是无效的空间,所以使用 <
            int middle = left + ((right - left) >> 1);
            if (nums[middle] > target) {
                right = middle; // target 在左区间,在[left, middle)中
            } else if (nums[middle] < target) {
                left = middle + 1; // target 在右区间,在[middle + 1, right)中
            } else { // nums[middle] == target
                return middle; // 数组中找到目标值,直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};
  • 时间复杂度O(log n)
  • 空间复杂度O(n)

27.移除元素

想的是直接将数组尾部元素补到需要删除的元素位置,但写了半天没写出来

代码随想录文档
BILIBILI

代码随想录答案-快慢指针

class Solution {
 public:
	int removeElement(vector<int> &nums, int val) {
		int slow = 0;
		for (int fast = 0; fast < nums.size(); fast++) {
			if (nums[fast] != val)
				nums[slow++] = nums[fast];
		}
		return slow;
	}
};

其中快慢指针的定义:

  • 快指针:寻找新数组的元素, 把原数组全部遍历一遍
  • 慢指针:指向更新的新数组的下标位置

代码随想录答案-移动最少元素

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int leftIndex = 0;
        int rightIndex = nums.size() - 1;
        while (leftIndex <= rightIndex) {
            // 找左边等于val的元素
            while (leftIndex <= rightIndex && nums[leftIndex] != val){
                ++leftIndex;
            }
            // 找右边不等于val的元素
            while (leftIndex <= rightIndex && nums[rightIndex] == val) {
                -- rightIndex;
            }
            // 将右边不等于val的元素覆盖左边等于val的元素
            if (leftIndex < rightIndex) {
                nums[leftIndex++] = nums[rightIndex--];
            }
        }
        return leftIndex;   // leftIndex一定指向了最终数组末尾的下一个元素
    }
};

使用左右两个指针,向中间逼近,我一开始也是这么想的,但是没写出来

使用while,结束条件为左指针超过右指针,说明所有元素都被遍历一遍

但是在每次判断时都要确保左指针没有超过右指针

  1. 首先移动左指针,寻找值为vla的元素,如果不等于val就一直移动
  2. 同理移动右指针,直到遇到值不等于val的元素,方便覆盖左指针找到的val
  3. 如果能走到if判断,并且右指针位置大于左指针,就使用右指针值覆盖左指针
  4. 最后返回的是左指针位置,代表左边数组都是不含有val的,此时就是数组元素个数

第一天结束

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二十二天的算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值