首先放今天的力扣打卡题。
在这里插入图片描述
在第一次做的过程中,我忽略了“升序排列”这个条件,没有使用二分。
由于是最近才刚开始学golang,所以很有兴趣的在这道题里使用了go的大杀器——goroutine。思路就是使用两个goroutine,一个从头到尾遍历数组,找出开始位置,一个倒序遍历数组,找到结束位置。
代码如下:

func searchRange(nums []int, target int) []int {//使用WaitGroup控制goroutinevar wg sync.WaitGroup//wg加入两个任务wg.Add(2)length := len(nums)//将start end直接赋值-1 当找不到时直接返回start := -1end := -1//第一个goroutinego func() {//函数运行结束后wg任务-1defer wg.Done()//正序遍历数组,找到start就跳出for i := 0; i < length; i++ {if nums[i] == target {start = ibreak}}}()go func() {defer wg.Done()//倒序遍历数组,找到end就跳出for i := length-1; i >= 0; i-- {if nums[i] == target {end = ibreak}}}()//wg一直等待两个任务结束wg.Wait()return []int{start,end}
}

由于go出色的性能,结果还是挺令人满意。
在这里插入图片描述
在完成这道题后,我去看了看题解,这才发现数组是有序的,可以使用二分法完成。
而官方的Go题解非常简练优雅。
这里放上代码并加上注释。

func searchRange(nums []int, target int) []int {/*SearchInts函数会返回数组中target第一次出现的位置。如果没有找到,则会返回数组长度n。*///找起始位置leftmost := sort.SearchInts(nums, target)if leftmost == len(nums) || nums[leftmost] != target {return []int{-1, -1}}//结束位置(这里是找target+1,比target大一的数字的起始位置就在target结束位置的右边)rightmost := sort.SearchInts(nums, target + 1) - 1return []int{leftmost, rightmost}
}

其中使用到了SearchInts这个函数。
跟踪进去,发现它调用了sort包下的Search函数。
这里对这个使用二分进行查找的函数进行分析。

func Search(n int, f func(int) bool) int {i, j := 0, nfor i < j {/*这里使用了移位操作,其结果与(i+j)/2一样。uint是无符号的int,范围是2^32即0到4294967295。使用uint可以避免因为i+j太大而造成的溢出*/h := int(uint(i+j) >> 1)// 如果f(h)返回false,说明从i到h中没有目标值。这时更新i为h+1 从原先的i到现在的i之间的数就不会再次扫描了 //相反的,如果f(h)返回true,说明从i到h中有目标值。这时更新j为 hif !f(h) {i = h + 1 } else {j = h // preserves f(j) == true}}//当 i==j 时,说明找到了(或者找完了但是没有找到,这时返回的是数组长度)return i
}

在阅读这段简单的源码时,其中的移位操作很有趣。在此之前,我并不知道还可以使用移位来进行/2的操作。其中原理也很简单:
在二进制中,每一位等于前面所有位的值之和再加一。
例如,7位二进制的最大值为1111111,即127。
给它加一即为10000000,128。
在这里插入图片描述
因此,对数字向右移位一位,就是其/2的结果。

而在查资料的时候,发现移位实现的乘除法比直接乘除的效率高很多。
总结:
1.遇到查找有序数组中的元素的时候应该反应出来使用二分。
2.遇到需要大量进行乘除法的操作时,考虑使用移位。