演算法 二分法

2021-07-22 22:32:06 字數 2623 閱讀 9418

二分法可以歸為兩大類:

* 二分查詢演算法

* 二分排序演算法

* 二分合併演算法

演算法中經常用到二分查詢演算法,比如最常規的應用就是在乙個有序陣列中找特定的數,但是如何寫出乙個完整準確的二分法呢,邊界條件如何判斷,到底是等於還是不等?可能會困惱大家,比如說查詢第乙個等於5的數,那又在如何查詢呢?查詢最後乙個等於5的數,又該如何改變?

這裡有乙個公式,可以解決所有這類的問題,它分為四步走:

1. 判定條件為start+1小於end,start=0, end=size-1

2. mid=start+((end-start)>>1)

3. 如果data[mid]滿足條件直接返回,如果滿足條件的資料在mid的右邊則將start=mid,如果滿足條件的資料在mid左邊則將end=mid

4. 根據條件再次判定start,end兩個元素是否滿足條件

其實整個過程就不斷縮小範圍的過程,最後一步就是將整個陣列的範圍縮小到start end兩個元素而已。

這裡第3步有乙個小的竅門就是不管什麼情況下都應該單獨判data[mid]大於等於和小於的情況,而不應該合併,因為會比較容易出錯。

下面以求在有序陣列中求最後乙個等於特定元素的位置為例

class solution 

int start = 0;

intend = a.size() - 1;

while(start+1

< end)

else

if( a[mid] < target) else

}if( a[end] == target) else

if( a[start] == target) else

}};

非常簡單吧。

典型題目,求sqrt

class

solution

long start = 0;

long end = x;

while(start+1

< end)

else

if(midsquare < x) else

}if(end*end < x) else

}};

還有一種典型的題目就是在乙個sorted array中尋找乙個數字,但是這個sorted array可能旋轉過的

二分排序演算法思想是分治,將大的問題簡化成為小的問題,所有小的問題全部解決了,整個問題就解決了。

二分排序的期望複雜度是o(nlgn),最差情況是o(n^2)

二分排序中最重要的是partition函式,直接寫不容易寫對,下面直接給出模板

int pivot = nums[begin];

intleft = begin;

intright = end;

while (left

< right)

nums[left] = nums[ right] ;

while (left

< right && nums[left] <= pivot)

nums[right] = nums[ left] ;

}nums[left] = pivot;

return left;最後left和right會相遇,相遇位置存放的就是pivot,接下來就是繼續處理左右兩個子集

典型應用有,求第k大數

從大到小的採用partition方法,如果返回的pivot位置正好是k-1,則直接返回,如果比k-1小,則第k-1個數肯定在pivot的右邊,也就是找k-1-pivot大的數,如果比k-1大,則在pivot的左邊找第k-1大的數。

這種演算法叫做quickselect,期望的複雜度是o(n),最差是o(n^2)

另外一種場景就是在乙個資料流中求中位數或者第k大數

資料流的意思就是不能一下子load出所有的資料,只能乙個乙個處理,這種場景就需要建立兩個pq,乙個是最小堆,乙個是最大堆,前面k個數為最小堆,後面len-k個數為最大堆,當有新的數進來的時候先判斷這個數是應該放在左邊還是右邊,然後直接插入,再重新調整,保證左邊的資料個數為k個就可以,所以每次壓入乙個新數,左邊的堆頂元素則為所求。這也是採用partition的思想來解決問題。

這裡有一種變形就是如果是window的第k大數,則需要用hashheap,因為window移動的時候需要增加乙個元素同時再刪除乙個元素,hash就可以最快的找到元素從heap中刪除。

ps:在求前k個數或者第k大數時優先考慮quickselect,然後就是pq是乙個比較好的策略,演算法的複雜度為nlgk

二分排序是將大問題分解成為小問題,當所有小問題全部解決了,那最後的結果也就出來了,二分合併則是先將問題分割成為小問題,解決每個小問題,然後將小問題的結果合併得到最後的結果。

最典型的問題就是合併n個鍊錶,n個有序鍊錶,如何合併成為乙個有序鍊錶?這道題目有兩種解法:

1. 將n個有序序列的第乙個元素壓入到pq中,然後從pq每次取出最大值,同時將最大值的下乙個節點再次壓入pq中,依次將所有節點合併。這個演算法的複雜度為mlgn,總共有m個節點

2. 每次兩兩合併,最終得到乙個結果,這是典型的合併排序,這個演算法複雜度為mlgn

二分法在實際的專案中運用還是非常多的,算是基本演算法,其中心思想是分治,它除了在陣列中使用較多,另外乙個應用的場景就是二叉樹的問題。

二分法 演算法

查詢演算法中的 二分法 是這樣定義的 給定n個從小到大排好序的整數序列list,以及某待查詢整數x,我們的目標是找到x在list中的下標。即若有list i x,則返回i 否則返回 1表示沒有找到。二分法是先找到序列的中點list m 與x進行比較,若相等則返回中點下標 否則,若list m x,則...

二分法 演算法

二分法查詢,這個演算法要求資料要是有序的。比如有這樣的問題 找出乙個陣列中,兩個數的和小於等於15,然後輸出他們,否則就單獨輸出較大的數。binarysearch.cpp include using namespace std void binarysearch int array,int leng...

演算法 二分法

從乙個有序陣列查詢指定元素 ll 1,2,3 100 普通查詢 如果查詢的數字是66,從最小開始查詢,需要66次才能找到。def search1 ll,num for i in ll if i num return i return none二分查詢說明 如果查詢的數字是99,可以先從中間開始匹配 ...