day1 | 数组 part-1 | 704 二分查找、27 移除元素

参加了一个代码随想录的算法训练营,跟着大家系统学习一遍,此博客开始,每日题目总结记录一下,加油....

今日任务 


二分查找

 想法:

        看到这个题目(一个有序数组,返回给出的 target值的下标),第一反应就是暴力啦;但是根据题目的标题以及有序数组的关键信息,要使用有 middle 下标的二分去解决.

问题:

        在找 middle 的过程中,存在一些问题,left 和 right 的值如何判断,循环该怎么做. (下面会附上我做错的几种情况).

解题思路:

        在看了视频讲解之后,顿时明白了二分查找的核心思路(一般分两种写法:区间左闭右闭、左闭右开). 在写代码之前要先自己想好要以哪个规则来写, 不需要问为什么,就是先带入一个规则,然后接下来就可以逐步确定 left、right、middle 的值和之间的关系.(需要参考下面的视频 理解)

视频讲解:手把手带你撕出正确的二分法 | LeetCode:704. 二分查找_哔哩哔哩_bilibili

 文章: 代码随想录

// 二分法 左闭右闭
// (1)区间两侧都能作为下标取数,所以left = 0 ,r = len(nums)-1;
// (2)选取循环的条件应该是根据在左闭右闭的情况下l 和 r 相等是否合法,
//    例如数组[1],左闭右闭,左右都可以取得到下标 0,所以可以相等.左闭右开则不合法.
// (3)对于 num[mid]的值大于 target 时,则接下来的计算应该是 left~mid 之间的元素,
//    则 right要向左移,然后 right为什么是等于 mid-1,因为右闭,及 right对应的元素
//    会参与到计算中,而 mid 对应的元素已经比较过大小了,无需再次比较,则 right = mid -1. 
//    num[mid]的值小于 target 时,同理可得.

func search(nums []int, target int) int {
	if len(nums) < 1 {
		return -1
	}
	left, right := 0, len(nums)-1
	for left <= right {
		mid := (left + right) / 2
		if nums[mid] == target {
			return mid
		}
		if nums[mid] < target {
			left = mid + 1
		}
		if nums[mid] > target {
			right = mid - 1
		}
	}
	return -1
}
// 二分法 左闭右开
// (1)left能作为下标取数l = 0 ,right 不能直接用来取数,因此r = len(nums)即可;
// (2)选取循环的条件应该是根据 在左闭右开的情况下l 和 r 相等是否合法,
//    例如[1],左闭右开,左右下标不可能相等,左闭右开则不合法.
// (3)对于 num[mid]的值大于 target 时,则接下来的计算应该是 left~mid 之间的元素
//    ,则 right要向左移,然后 right为什么是等于 mid,因为右开,right下标对应的元素
//    不会参与到其中的计算,无需考虑 right=mid 时,还会将已经比较过的 num[mid]参与
//    计算的情况.则 right = mid.  num[mid]的值小于 target 时,因为左闭,左边的元素
//    还是会参与到计算中,所以 left=mid+1.

func search(nums []int, target int) int {
	if len(nums) < 1 {
		return -1
	}
	left, right := 0, len(nums)
	for left < right {
		mid := (left + right) / 2
		if nums[mid] == target {
			return mid
		}
		if nums[mid] < target {
			left = mid + 1
		}
		if nums[mid] > target {
			right = mid
		}
	}
	return -1
}

错误代码

// 最开始在不了解具体二分法的写法规则时瞎写的,只知道要找 mid下标
// 唯一能通过的测试用例应该是target 值在数组最左侧.
// 这样写肯定不行的呀,mid每次都是重新计算的,最终mid的值不准。
func search(nums []int, target int) int {
	if len(nums) == 0 {
		return -1
	}
	mid := (len(nums) - 1) / 2
	if nums[mid] == target {
		return mid
	} else if nums[mid] > target {
		return search(nums[:mid], target)
	} else if nums[mid] < target {
		return search(nums[mid+1:], target)
	}
	return -1
}
// 这种写法会导致一直循环,
// 此时也是不知道左闭右闭那些规则的,理不清 left、right、mid 之间的关系
// search([]int{-1, 0, 3, 5, 9, 12}, 12)
// left right mid
0 5 2
2 5 3
3 5 4
4 5 4
4 5 4

func search(nums []int, target int) int {
	if len(nums) < 1 {
		return -1
	}
	left, right := 0, len(nums)-1
	for left < right {
		mid := (left + right) / 2
		if nums[mid] == target {
			return mid
		}
		if nums[mid] < target {
			left = mid
		}
		if nums[mid] > target {
			right = mid - 1
		}
	}
	return -1
}

移除元素(双指针)

        在原数组上移除指定的 target 值,最后返回移除后的数组大小,然后题目的输出会根据你返回的数值去原数组上取新的数组

想法:

        这题我连暴力都没写出来,代码能力属实有点弱了....

问题:

        看着好像只需 for 循环把相应的 target 找出来即可,但是因为要保持你的原数组能根据你返回的值,直接取前多少个元素作为新数组输出, 所以找到 target 后,要把后面的元素往前移.

解题思路:

        看了老师的视频,讲解双指针(快慢指针)的用法,顿时明白了,快指针先行一步外出寻找元素(目标元素丢弃,非目标元素传回家),慢指针在家等候,将快指针收集到的元素存放好....

        其实看了视频讲解,很容易理解,然而我想到了好像也可以使用首尾两个指针(因为刚做了二分..)去解决啊,好像比“快慢指针”更方便,不用每个重新赋值.( 具体思路见代码)

视频讲解:数组中移除元素并不容易! | LeetCode:27. 移除元素_哔哩哔哩_bilibili

文章: 代码随想录 

// 首尾两个指针
// 1、left 等于 0、right = len(nums)-1;
// 2、如果 nums[left]的元素不是我们要去除的元素,就让 left 右移
//    如果 nums[left]是我们要去除的元素,那么就让 right 对应的元素赋值过来,
      不去考虑本身 right对应的元素是否为要移除的,只管往左边赋值,这时我们不去判断
      nums[left]是否仍为要去除的要素,因为下次循环会判断 left ;然后我们将 right左移

func removeElement(nums []int, val int) int {
	left, right := 0, len(nums)-1

	for left <= right {    // “=”判断条件,其实可以理解为我们的 l、r 的初始值满足左闭右闭
		if nums[left] != val {
			left++
			continue
		}
		nums[left] = nums[right]
		right--
	}
	return left
}

错误代码:

// 我下面这段代码的问题,其实就是考虑太多,想着要是 right 指向的元素也为 target 怎么办,是否要先左移怎么的,然后就把自己搞糊涂了.下面的代码会无法通过测试用例[3,3]的, 可以通过[0,1,2,2,3,0,4,2]    

// 想使用两个指针,左右指针进行操作,但是会存在一些问题的,思路还有点没想明白
func removeElement(nums []int, val int) int {
	left, right := 0, len(nums)-1

	for left <= right {
		if nums[right] == val {
			right--
		}
		if right < left {
			return left
		}
		if nums[left] == val {
			nums[left] = nums[right]
			left++
			right--
			continue
		}
		left++
	}
	return left
}

总结

        二分查找、双指针感觉都还是挺好理解的,但是像二分查找要先根据区间规则,这种思想,自己恐怕琢磨不出来.......   

留坑: 二分查找的还有两个题目没有做,后面做了 补上链接 :  35.搜索插入位置 和 34. 在排序数组中查找元素的第一个和最后一个位置.  
  • 12
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值