二分查找极其变形算法
二分查找是典型的分治法的应用,要求待查序列排好序,这里都按照从小到大排列处理,查找时间代价o(log(n)).
思路和中间的数字比较,如果相等则找到,如果<则在左边找,如果>则在右边找。
分治及许多计算机算法的核心就是将问题设法转化为相同的形式问题而规模减小,即子问题。能够找到规模减小的子问题,意味着问题的解决。
注意分治不一定非要递归,如果每次只走一个分支,那么循环的写法也非常简单,是更好的写法。
1 def bsearch(li, val):
2
3 start = 0
4
5 end = len(li) - 1
6
7 while(start <= end):
8
9 middle = int((start + end)/2)
10
11 if val == li[middle]:
12
13 return middle
14
15 if val < li[middle]:
16
17 end = middle - 1
18
19 else:
20
21 start = middle + 1
22
23
24 return -1
2
3 start = 0
4
5 end = len(li) - 1
6
7 while(start <= end):
8
9 middle = int((start + end)/2)
10
11 if val == li[middle]:
12
13 return middle
14
15 if val < li[middle]:
16
17 end = middle - 1
18
19 else:
20
21 start = middle + 1
22
23
24 return -1
二分变形1
在数组中查找val,有可能数组中有多个相同的val,返回第一个的位置。
方法1,二分查找val,找到后向左线性扫描直到遇到更小的。
方法2,找到val以后,对左边的数组继续二分查找直到找到第一个位置
其它方法? TODO参考一下stl的lower bound
下面给出方法2的解法。
1 """find the first val like li avove search 3 will return 2"""
2
3 def bsearch2(li, val):
4
5 start = 0
6
7 end = len(li) - 1
8
9 find = -1
10
11 while(start <= end):
12
13 middle = int((start + end)/2)
14
15 if val == li[middle]:
16
17 end = middle - 1
18
19 find = middle
20
21 if val < li[middle]:
22
23 end = middle - 1
24
25 else:
26
27 start = middle + 1
28
29 return find
2
3 def bsearch2(li, val):
4
5 start = 0
6
7 end = len(li) - 1
8
9 find = -1
10
11 while(start <= end):
12
13 middle = int((start + end)/2)
14
15 if val == li[middle]:
16
17 end = middle - 1
18
19 find = middle
20
21 if val < li[middle]:
22
23 end = middle - 1
24
25 else:
26
27 start = middle + 1
28
29 return find
二分变形2
将排好序的数组移位,而且不知道具体的移位位置,
如1 , 2, 3, 4, 5, 6, 7
移位后可能为
6 , 7, 1, 2, 3, 4, 5 //list1
3, 4, 5, 6, 7, 1, 2 //list2
这样查找过程中,要注意如何能够找到相同形式规模缩小的子问题呢?
仍然二分查找,但是每次下一步查找的区间的确定,就不仅仅由待查找的val和当前数组中间值的关系确定了,要考虑数组的头尾元素值。
例如 list1中查找 7, 那么先和2 比较发现较大, 但是下一步不能去右边区间找,因为7 > list1的首元素了,所以应该在左边区间查找。
注意区分list1,list2的不同 list1的中间值小于list1的首元素,意味着中间值在较小的序列中。
而list2的中间值大于首元素,意味着中间值在较大的序列中。
1 def bsearch3(li, val):
2
3 start = 0
4
5 end = len(li) - 1
6
7 while(start <= end):
8
9 mid = int((start + end)/2)
10
11 #print '*', mid, val, li[mid]
12
13 if val == li[mid]:
14
15 return mid
16
17 if val == li[start]:
18
19 return start
20
21 if val == li[end]:
22
23 return end
24
25 if val < li[mid]:
26
27 if li[mid] < li[start]: #like 6 7 1 .2. 3 4 5
28
29 end = mid - 1 #here to find 1
30
31 else: #li[mid] >= li[start]like 3 4 5 .6. 7 1 2
32
33 if (val < li[start]): #here to find 2
34
35 start = mid + 1
36
37 else:
38
39 end = mid - 1 #here to find 4
40
41 else: #val > li[mid]
42
43 if li[mid] < li[start]: #like 6 7 1 .2. 3 4 5
44
45 if val < li[end]: #here to find 3 or 4
46
47 start = mid + 1
48
49 else:
50
51 end = mid - 1 #here to find 7
52
53 else: #like 3 4 5 .6. 7 1 2
54
55 start = mid + 1 #here to find 7
56
57 return -1
2
3 start = 0
4
5 end = len(li) - 1
6
7 while(start <= end):
8
9 mid = int((start + end)/2)
10
11 #print '*', mid, val, li[mid]
12
13 if val == li[mid]:
14
15 return mid
16
17 if val == li[start]:
18
19 return start
20
21 if val == li[end]:
22
23 return end
24
25 if val < li[mid]:
26
27 if li[mid] < li[start]: #like 6 7 1 .2. 3 4 5
28
29 end = mid - 1 #here to find 1
30
31 else: #li[mid] >= li[start]like 3 4 5 .6. 7 1 2
32
33 if (val < li[start]): #here to find 2
34
35 start = mid + 1
36
37 else:
38
39 end = mid - 1 #here to find 4
40
41 else: #val > li[mid]
42
43 if li[mid] < li[start]: #like 6 7 1 .2. 3 4 5
44
45 if val < li[end]: #here to find 3 or 4
46
47 start = mid + 1
48
49 else:
50
51 end = mid - 1 #here to find 7
52
53 else: #like 3 4 5 .6. 7 1 2
54
55 start = mid + 1 #here to find 7
56
57 return -1