• Golang sort包Search函数源码分析


    此文转载自:https://blog.csdn.net/luyuan492/article/details/110450704

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

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

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

    func Search(n int, f func(int) bool) int {
    	i, j := 0, n
    	for 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为 h
    		if !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.遇到需要大量进行乘除法的操作时,考虑使用移位。

       

    更多内容详见微信公众号:Python测试和开发

    Python测试和开发

  • 相关阅读:
    经验谈 论前端架构的重要性
    论 Angular的混乱
    DTW 算法(转)
    软件提高发射功率原理
    (转)LSI SAS 1068E Raid CentOS 5.5 安装实例浪潮NF5220系列 分类: linux
    聚类算法总结
    信号相似性的描述
    python科学计算整理
    一个无线通信类投稿的期刊list
    2012年Elsevier旗下Computer Science期刊最新SCI影响因子排名
  • 原文地址:https://www.cnblogs.com/phyger/p/14073236.html
Copyright © 2020-2023  润新知