github.com/qiuhoude/go-web@v0.0.0-20220223060959-ab545e78f20d/algorithm/datastructures/binary_search/binary_search.go (about)

     1  package binary_search
     2  
     3  import "math"
     4  
     5  /*
     6  二分查找
     7  有点类似于数学中的夹逼定理, 两边不断逼近某个值
     8  
     9  十个二分九个错
    10  
    11  时间复杂度
    12  2^k = n 第 k次可以找到该元素
    13  k = log2n 所以 O(logn)
    14  
    15  */
    16  func BSearch(arr []int, val int) int {
    17  	n := len(arr)
    18  	if n < 1 {
    19  		return -1
    20  	}
    21  	low := 0
    22  	high := n - 1
    23  	for low <= high { // 注意是 <= 不是 <
    24  		mid := low + ((high - low) >> 1)
    25  		if arr[mid] == val {
    26  			return mid
    27  		} else if arr[mid] < val {
    28  			low = mid + 1 // 注意需要+1
    29  		} else {
    30  			high = mid - 1
    31  		}
    32  	}
    33  	return -1
    34  }
    35  
    36  // 二分查找递归写法
    37  func BSearchRecusion(arr []int, val int) int {
    38  	n := len(arr)
    39  	if n < 1 {
    40  		return -1
    41  	}
    42  	return bsearchR(arr, val, 0, n-1)
    43  }
    44  
    45  func bsearchR(arr []int, val, low, high int) int {
    46  	if low > high {
    47  		return -1
    48  	}
    49  	mid := low + ((high - low) >> 1)
    50  	if arr[mid] == val {
    51  		return mid
    52  	} else if arr[mid] < val {
    53  		return bsearchR(arr, val, mid+1, high)
    54  	} else {
    55  		return bsearchR(arr, val, low, mid-1)
    56  	}
    57  }
    58  
    59  // 二分法求平方根
    60  func BSqrt(x float64, precise float64) float64 {
    61  	if x <= 0 {
    62  		if x == 0 {
    63  			return 0
    64  		}
    65  		return -1
    66  	}
    67  	// 大于1 在 0~x 之间查找
    68  	low := 0.0
    69  	high := x
    70  	if x < 1 {
    71  		// 小于1 在 x~1 之间查找该值
    72  		low = x
    73  		high = 1
    74  	}
    75  	mid := x / 2.0
    76  	for math.Abs(mid*mid-x) > precise {
    77  		if mid*mid < x {
    78  			low = mid
    79  		} else {
    80  			high = mid
    81  		}
    82  		mid = (low + high) / 2.0
    83  	}
    84  	return mid
    85  }
    86  
    87  /*
    88  二分查找的 变体(里面有重复值时)
    89  变体1. 查找第一个值等于给定值的元素
    90  */
    91  func BSearchFirst(arr []int, val int) int {
    92  	n := len(arr)
    93  	if n < 1 {
    94  		return -1
    95  	}
    96  	low := 0
    97  	high := n - 1
    98  	for low <= high {
    99  		mid := low + ((high - low) >> 1)
   100  		if arr[mid] > val {
   101  			high = mid - 1
   102  		} else if arr[mid] < val {
   103  			low = mid + 1
   104  		} else {
   105  			//如果 mid 等于 0,那这个元素已经是数组的第一个元素,那它肯定是我们要找的
   106  			//如果 mid 不等于 0,但 a[mid] 的前一个元素 a[mid-1] 不等于 value,那也说明
   107  			//a[mid] 就是我们要找的第一个值等于给定值的元素
   108  			if mid == 0 || arr[mid-1] != val {
   109  				return mid
   110  			} else {
   111  				// 需要找的范围肯定 [low,mid]范围,但需要找第一个所以往前逼近
   112  				// 所以近一步向前缩小范围 [low, mid -1]
   113  				high = mid - 1
   114  			}
   115  		}
   116  	}
   117  	return -1
   118  }
   119  
   120  // 烧脑的写法
   121  func BSearchFirst2(arr []int, val int) int {
   122  	n := len(arr)
   123  	if n < 1 {
   124  		return -1
   125  	}
   126  	low := 0
   127  	high := n - 1
   128  	for low <= high {
   129  		mid := low + ((high - low) >> 1)
   130  		if arr[mid] >= val {
   131  			high = mid - 1
   132  		} else {
   133  			low = mid + 1
   134  		}
   135  	}
   136  	if arr[low] == val {
   137  		return low
   138  	}
   139  	return -1
   140  }
   141  
   142  /*
   143  变体2:查找最后一个值等于给定值的元素
   144  */
   145  func BSearchLast(arr []int, val int) int {
   146  	n := len(arr)
   147  	if n < 1 {
   148  		return -1
   149  	}
   150  	low := 0
   151  	high := n - 1
   152  	for low <= high {
   153  		mid := low + ((high - low) >> 1)
   154  		if arr[mid] > val {
   155  			high = mid - 1
   156  		} else if arr[mid] < val {
   157  			low = mid + 1
   158  		} else {
   159  			// 数组最后一个, 后面一个又不等于需要的值
   160  			if mid == n-1 || arr[mid+1] != val {
   161  				return mid
   162  			} else { //向后逼近
   163  				low = mid + 1
   164  			}
   165  		}
   166  	}
   167  	return -1
   168  }
   169  
   170  /*
   171  变体3:查找第一个大于等于给定值的元素
   172  */
   173  func BSearchFirstGeVal(arr []int, val int) int {
   174  	n := len(arr)
   175  	if n < 1 {
   176  		return -1
   177  	}
   178  	low := 0
   179  	high := n - 1
   180  	for low <= high {
   181  		mid := low + ((high - low) >> 1)
   182  		if arr[mid] >= val { // 大于等于某个值
   183  			// 数组的第一个 ,前面一个又小于该值
   184  			if mid == 0 || arr[mid-1] < val {
   185  				return mid
   186  			} else {
   187  				// 向前逼近
   188  				high = mid - 1
   189  			}
   190  		} else if arr[mid] < val {
   191  			low = mid + 1
   192  		}
   193  	}
   194  	return -1
   195  }
   196  
   197  // 查找第一个大于 给定值的下标
   198  func BSearchFirstGVal(arr []int, val int) int {
   199  	n := len(arr)
   200  	if n < 1 {
   201  		return -1
   202  	}
   203  	low := 0
   204  	high := n - 1
   205  	for low <= high {
   206  		mid := low + ((high - low) >> 1)
   207  		if arr[mid] > val {
   208  			high = mid - 1
   209  		} else {
   210  			low = mid + 1
   211  		}
   212  	}
   213  	return low
   214  }
   215  
   216  /*
   217  变体4:查找最后一个小于等于给定值的元素
   218  */
   219  func BSearchLastLeVal(arr []int, val int) int {
   220  	n := len(arr)
   221  	if n < 1 {
   222  		return -1
   223  	}
   224  	low := 0
   225  	high := n - 1
   226  	for low <= high {
   227  		mid := low + ((high - low) >> 1)
   228  		if arr[mid] > val {
   229  			high = mid - 1
   230  		} else if arr[mid] <= val {
   231  			// 数组最后一个, 后面一个又大于需要的值
   232  			if mid == n-1 || arr[mid+1] > val {
   233  				return mid
   234  			} else { //向后逼近
   235  				low = mid + 1
   236  			}
   237  		}
   238  	}
   239  	return -1
   240  }