1. 程式人生 > 其它 >【菜雞新手 - 牛客網刷題NC105】有重複數字的升序陣列,二分查詢第一個大於等於查詢值的位置 - 二分查詢 || 統稱為找不到的情況 || python

【菜雞新手 - 牛客網刷題NC105】有重複數字的升序陣列,二分查詢第一個大於等於查詢值的位置 - 二分查詢 || 統稱為找不到的情況 || python

技術標籤:資料結構與刷題資料結構二分法

文章目錄


以下為原創程式碼,可以參考,但禁止轉載!
@ author = yhr


一. 普通二分查詢

(二分查詢一定建立於有序陣列上,以升序為例)

圖 來自 牛客網 -王清楚稍有改動
圖 來自 牛客網 王清楚

標記 low =0 (left) ; high = len(陣列)-1 = right

普通二分法虛擬碼

# @ author = yhr
while low <= high:
	mid = (low+high)//2
	if v <a[mid]:
# 去左半部分找 high = mid -1 elif a[mid] < v: # 去右半部分找 low = mid + 1 elif a[mid] == v: # 找到了一個 return mid return -1 # 真沒找到

普通二分法的一些個人觀點:

【我自己琢磨的,說不出來這個味,不對就打我吧。不確保絕對正確,但我感覺是正確的,大家可以一起琢磨琢磨,如果有數學證明就更好了!】

A. 如果查詢成功,一定在 mid位置, 此時low可能<high。也可能low=high=mid。
B. 如果查詢失敗,最後此數一定在(low-1 , low)之間。

B.1 查詢失敗情況一:最終當low=high=mid時,如果v<a[mid],那麼high-=1,此時v一定在 a[high]=a[low-1] 與 a[low]之間。
B.2 查詢失敗情況二:最終當 low=high=mid時,如果a[mid]<v,那麼low+=1,此時v一定在 a[high]=a[low-1] 與 a[low]之間。

這個性質就可以用到該題上:


二. 本題 – 我的解題思路

題目描述:

請實現有重複數字升序陣列的二分查詢。 輸出在陣列中 第一個 大於等於 查詢值的位置,如果陣列中不存在這樣的數,則輸出陣列長度加一。
(輸出位置從1開始計算)

兩個主關鍵點: 1. 第一個 2. 大於等於

一個次關鍵點: 3. 輸出位置從1開始計算

這裡就忽略意外情況,不做細說:比如陣列為空!

剛才我說,普通二分查詢裡,如果找不到數值v,最後他一定是在a[low-1] 與 a[low]之間!

主關鍵點: 1. 第一個 2. 大於等於他的數的位置,那麼我們可以把大於等於統統歸咎於一個情況:

在陣列中,找到1. 第一個, 2.稍微大於v的位置

什麼意思呢?

  • 我們把數組裡所有等於v的數 都看成:v + 正無窮小,即:數組裡沒有等於v的數,但是 有永遠比他只大一點的數

  • 那麼這個題目就變成 普通二分查詢裡永遠找不到 的情況。

  • 最後 a[low] 一定是比他大的(包括只大正無窮小的情況)第一個數的位置。

那麼只需要把普通二分查詢中,a[mid]==v的情況 --> 變成同a[mid]>v情況一起處理即可。

於是程式碼可以如此處理:

#
# 二分查詢
# @param n int整型 陣列長度
# @param v int整型 查詢值
# @param a int整型一維陣列 有序陣列
# @return int整型
#
class Solution:
    def upper_bound_(self , n , v , a ):
        if len(a) ==0:
        	# 處理陣列為空的情況
            return 1
        low = 0
        high = n-1
        while low <=high:
            mid = (low+high)//2
            if v <= a[mid]:
                high = mid-1
            elif v > a[mid]:
                low=mid +1

        return low +1

但是,還有些情況,我們可以直接寫出來,避免程式碼多跑些無用功,比如:

a. 當 v 小於 整個 升序陣列
b. 當 v 大於 整個 升序陣列

#
# 二分查詢
# @param n int整型 陣列長度
# @param v int整型 查詢值
# @param a int整型一維陣列 有序陣列
# @return int整型
#
class Solution:
    def upper_bound_(self , n , v , a ):
        if len(a) ==0:
        	# 處理陣列為空的情況
            return 1
        low = 0
        high = n-1

加入兩種特殊情況的處理工作,避免程式碼多跑無用功。

		# 加入兩個特殊情況
		if v < a[low]:
			# 輸出位置從1開始計算 
            return 1
        if v > a[high]:
            # 輸出位置從1開始計算 
            return high+1+1
		while low <=high:
            mid = (low+high)//2
            if v <= a[mid]:
                high = mid-1
            elif v > a[mid]:
                low=mid +1
		# 輸出位置從1開始計算 
        return low +1