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

704.二分查找

二分查找的思路前几天还复习过一遍,没想到还是一写就废。看了卡哥的视频讲解思路清晰了不少。

思路重点:区间的定义——是左闭右闭还是左闭右开?

· 右开还是右闭决定了right指向的值能否被访问


区间的定义回答了以下问题:

1、right的初始值是nums.size()还是nums.size()-1?

2、循环条件是left < right还是left <= right?

3、right的更新是right = middle还是right = middle - 1?

最开始的写法,最近一直在看C++ Primer,所以风格受影响比较多(比如条件的!=和decltype),没想到实际写起来一写就废 /(T_T)/

// 最开始的写法,更接近左闭右开(但实际是左也开了)
int search(vector<int>& nums, int target) {
    // 为了追求规范使用了decltype,但实际right可能会向下越界(size_t是无符号)
    decltype(nums.size()) ind_f = 0; 
    // 因为右区间无法访问到,所以初始值应改成num.size();
    decltype(nums.size()) ind_b = nums.size() - 1;      

    while (ind_f != ind_b) {
        decltype(nums.size()) ind_m = (ind_f + ind_b) / 2;
        if (nums[ind_m] == target)
            return ind_m;
        else if (nums[ind_m] > target) {
            ind_b = ind_m;
        }
        else {
            ind_f = ind_m;      // 左应该是闭的,所以改成=middle+1
        }
    }
    return -1;
}

修正后的左闭右开写法

int search(vector<int>& nums, int target) {
    int left = 0;
    int right = nums.size();        // 初始值等于size

    while (left < right) {
        int middle = (left + right) / 2;
        if (nums[middle] == target)
            return middle;
        else if (nums[middle] > target) {
            right = middle;         // 右开,所以直接等于middle
        }
        else {
            left = middle + 1;
        }
    }
    return -1;
}

左闭右闭写法

int search(vector<int>& nums, int target) {
    int left = 0;
    int right = nums.size() - 1;

    // 右闭时条件为<=
    while (left <= right) {
        //int middle = (left + right) / 2;          // 这样的写法可能int越界
        int middle = left + (right - left) / 2;
        if (nums[middle] == target)
            return middle;
        else if (nums[middle] > target) {
            right = middle - 1;      // 右闭时,middle不应该在搜索区间内
        }
        else {
            left = middle + 1;
        }
    }
    return -1;
}

27.移除元素

 知识点:数组中的元素不能删除,只能覆盖

这题倒是一写就会了

快慢指针:

· 快指针——负责遍历数组

· 慢指针——负责更新数组

双指针法:

// 双指针解法,时间复杂度O(n),空间复杂度O(1)
int removeElement(vector<int>& nums, int val) {
	int fast = 0;			// 快指针负责向前遍历
	int slow = 0;			// 慢指针负责维护更新

	while (fast < nums.size()) {
        // 不需要“删除”的值使用slow进行更新
		if (nums[fast] != val) {
			nums[slow] = nums[fast];
			++fast;
			++slow;
		}
        // 需要“删除”的值直接忽略(没看到就是不存在:D)
		else {
			++fast;
		}
	}
	return slow;
}

 暴力解——双层遍历,一层遍历一层更新

// 暴力解法,时间复杂度O(n^2),空间复杂度O(1)
int removeElement(vector<int>& nums, int val) {
	int size = nums.size();
	int i = 0;
	while (i < size) {
		if (nums[i] == val) {
			for (int j = i; j < nums.size() - 1; ++j) 
				nums[j] = nums[j + 1];
			--size;
		}
		else 
			++i;
	}
	return size;
}

 拓展:35.搜索插入位置

也是利用了二分搜索思维的一道题,开始想的是如何返回正确的middle值,看了题解后发现是与left和right的性质有关

left:左闭的情况下left所指位置的左侧一定小于target

rigjt:右闭的情况下right所指位置的右侧一定大于target 


解题思路的转换:寻找插入位置->寻找第一个大于target值的位置->寻找一个位置,它的左侧值全都小于target

int searchInsert(vector<int>& nums, int target) {
	int left = 0;
	int right = nums.size() - 1;		// 左闭右闭
	int middle = 0;

	while (left <= right) {
		middle = left + (right - left) / 2;
		if (nums[middle] == target)
			return middle;
		else if (nums[middle] < target)
			left = middle + 1;
		else
			right = middle - 1;
	}
	return left;		// 此时left为第一个大于target的下标(left左侧的值始终小于target)
}

还看了下34. 在排序数组中查找元素的第一个和最后一个位置,没看懂,之后再抽时间看看吧。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值