二分查找
将一组有序数组一分为二,将要查找元素与分割点比较,有三种情况:
1.查找元素比分割点大 。->在分割点右侧继续查找
2.查找元素比分割点小。->在分割点左侧继续查找
3.查找原始等于分割点。->直接返回。
使用二分查找方法,每经过一次查找,查找范围缩小一半,如果数组元素个数为n,假设n是3的幂。第一次查找后,剩下N/2个元素继续查,第二次(n/2)/2个元素需要继续查。则第i次比较后,剩下n/2i个元素需要继续搜索。当i=log2n(以2为底n的对数),只剩下一个元素了,因而只需要再进行一次比较。因此二分查找方法在一个已排序数组中查找一个关键字,在最坏情况下只需log2n(以2为底n的对数)+1次比较操作,对于一个有1024个(210)元素的列表,二分搜索在最坏情况下只需11次比较,而顺序搜索方法在最坏情况下需1024次比较。
二分搜索方法在每次比较之后,会将需要搜索的数组范围缩小一半,可以用变量low和high分别表示当前搜索的数组区域的首下标和尾下标,则变量mid的值就为(low+high)/2
时间复杂: O(lgn).
要求
- 必须是有序的数组,并能支持随机访问
变形
- 查找第一个值等于给定的
- 在相等的时候做处理,向前查
- 查找最后一个值等于给定的值
- 在相等的时候做处理,向后查
- 查找第一个大于等于给定的值
- 判断边界减1
- 查找最后一个小于等于给定的值
- 判断边界加1
实际应用
- 用户ip区间段查询
- 用于相似度查询
-
二分查找的优点
折半查找的时间复杂度为O(logn),远远好于顺序查找的O(n)。
二分查找的缺点
虽然二分查找的效率高,但是要将表按关键字排序。而排序本身是一种很费时的运算。既使采用高效率的排序方法也要花费O(nlgn)的时间。
适用情况
二分查找只适用顺序存储结构。为保持表的有序性,在顺序结构里插入和删除都必须移动大量的结点。因此,二分查找特别适用于那种一经建立就很少改动、而又经常需要查找的线性表。
对那些查找少而又经常需要改动的线性表,可采用链表作存储结构,进行顺序查找。链表上无法实现二分查找。
-
goland代码实现
-
package main import ( "bufio" "fmt" "os" "strconv" "strings" ) func cmd_search(arr []int, finddata int) int { low := 0 high := len(arr) - 1 for low <= high { mid := (low + high) / 2 fmt.Println(mid) if arr[mid] > finddata { high = mid - 1 } else if arr[mid] < finddata { low = mid + 1 } else { return mid } } return -1 } func main() { arr := []int{3, 4, 7, 9, 13, 45, 67, 89, 100, 180} // 排序前的数值 fmt.Println("数组中的数据: ") fmt.Println(arr) fmt.Print("输入查找的值: ") reader := bufio.NewReader(os.Stdin) str, _ := reader.ReadString('\n') str = strings.Replace(str, " ", "", -1) str = strings.Replace(str, "\n", "", -1) m, _ := strconv.Atoi(str) cmd_search(arr,m) }