1. 程式人生 > >九章演算法筆記 2.Binary Search

九章演算法筆記 2.Binary Search

大綱 cs3k.com

第一境界 二分法模板

• 時間複雜度小練習
• 遞迴與非遞迴的權衡
• 二分的三大痛點
• 通用的二分法模板

第二境界

• 二分位置 之 圈圈叉叉 Binary Search on Index – OOXX
• 找到滿足某個條件的第一個位置或者最後一個位置

第三境界

•二分位置 之 保留一半 Binary Search on Index – Half half
• 保留有解的一半,或者去掉無解的一半

第一境界 二分法模板

cs3k.com

時間複雜度

T(N) = T(N/2) + O(1) = O(LOGN) 通過O(1)的時間,把規模為N的問題變為N/2, 其中T(N)是個FUNCTION

T(n) = T(n/2) + O(1)

=T(n/4) + O(1) + O(1)

=T(n/8) + O(1) +O(1) +O(1)

=T(1) + logn * O(1)

T(N) = T(N/2) + O(N) = O(N) 通過O(N)的時間,把規模為N的問題變為N/2?

T(n) = T(n/2) + O(n)

=T(n/4) + O(n) + O(n/2)

=T(n/8) + O(n) +O(n/2) +O(n/4)

=T(1) + )(n * (1 + 1/2 + 1/4 + … + 1/n)

≈O(2n)

≈O(n)

Time Complexity in Coding Interview

• O(1) 極少
• O(logn) 幾乎都是二分法
• O(√n) 幾乎是分解質因數
• O(n) 高頻
• O(nlogn) 一般都可能要排序
• O(n2) 陣列,列舉,動態規劃
• O(n3) 陣列,列舉,動態規劃
• O(2^n) 與組合有關的搜尋 combination
• O(n!) 與排列有關的搜尋 permutation

比O(n)更優的時間複雜度,幾乎只能是O(logn)的二分法
經驗之談:根據時間複雜度倒推演算法是面試中的常用策略

面試中是否使用 Recursion 的幾個判斷條件

cs3k.com

  1. 面試官是否要求了不使用 Recursion (如果你不確定,就向面試官詢問)
  2. 不用 Recursion 是否會造成實現變得很複雜
  3. Recursion 的深度是否會很深
  4. 題目的考點是 Recursion vs Non-Recursion 還是就是考你是否會Recursion?

記住:不要自己下判斷,要跟面試官討論!

二分法模板四要素

cs3k.com

1.start + 1 < end
2.start + (end – start) / 2
3.A[mid] ==,
4.A[start] A[end] ? target

1.start + 1 < end

為什麼不是WHILE(START < END)?

因為如果要求小於的話,那麼退出的條件就是隻有等於

假設問題為在[2,2]中找2最後一次出現的位置,就會

1 2 3 4 5 6 7 8 9 while (start < end) {   mid = (0 + 1) / 2 = 0;   if (mid == target) {   start = mid;   }

永遠都不能等於,所以死迴圈了。

應該用相鄰或者相等退出,即 start + 1 < end

2.start + (end – start) / 2

二分不是確定答案在哪, 而是減少搜尋範圍

3.A[mid] ==,

分三種情況:

num[mid] == target

num[mid]   target

4.A[start] A[end] ? target

根據題意 找last position 先比較 num[end] == target

PS:

陣列和指標要看NULL和length = 0 的情況

第二境界 二分位置 之 OOXX

cs3k.com

一般會給你一個數組, 讓你找陣列中第一個/最後一個滿足某個條件的位置
即在一個如下數列:   OOOOOOO…OOXX….XXXXXX中找第一個X, 做後一個O類似

First Bad Version

First version that is bad version

Search In a Big Sorted Array

Given a big sorted array with positive integers sorted by ascending order. The array is so big so that you can not get the length of the whole array directly, and you can only access the kth number by ArrayReader.get(k) (or ArrayReader->get(k) for C++). Find the first index of a target number. Your algorithm should be in O(log k), where k is the first index of the target number.

Return -1, if the number doesn’t exist in the array.

Notice

If you accessed an inaccessible index (outside of the array), ArrayReader.get will return 2,147,483,647.

倍分法:

先以跨度加倍的方式(即index位置為1, 2, 4, 8…..2^x, 2^(x+1)), 找到第一個比target大的數

再二分查詢target

Find Minimum in Rotated Sorted Array

Suppose a sorted array is rotated at some pivot unknown to you beforehand.

(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).

Find the minimum element.

Notice

You may assume no duplicate exists in the array.

畫圖表示的話呢

FullSizeRender-11-03-17-19-01-4

就是在兩段上升區間, 找到第一個x.

First position <= Last Number

target可以定為:  第一個比first number小的數, 或者第一個比last number小的數.

因為一段上升空間和一個空區間是兩段上升區間的一個corner case, 如圖:

FullSizeRender-11-03-17-19-01-3

所以只能選第一個比end小的數

主要看特殊情況,決定選first number還是last number,上升找最小值比尾巴

Smallest Rectangle Enclosing Black Pixels

cs3k.com

An image is represented by a binary matrix with 0 as a white pixel and 1 as a black pixel. The black pixels are connected, i.e., there is only one black region. Pixels are connected horizontally and vertically. Given the location (x, y) of one of the black pixels, return the area of the smallest (axis-aligned) rectangle that encloses all black pixels.

01矩陣中,所有的1連成一片,找到最小的能覆蓋所有1的矩形, 輸入給出了其中一個1的位置

之前要做的兩道題:

1.Search a 2D Matrix

http://www.lintcode.com/en/problem/search-a-2d-matrix/

二分一次行,二分一次列, 兩次二分

2.Search for a Range

http://www.lintcode.com/en/problem/search-for-a-range/

圖解

FullSizeRender-11-03-17-19-01

即找出黑色虛線虛線框

黑色虛線框是什麼呢,  是上下左右四個位置:

左:第一個出現綠色的列

右:最後一個出現綠色的列

上:第一個綠的行

下:最後一個綠的行

所以找左右需要的時間複雜度為:  colSize *log(rowSize)

上下的時間複雜度為:rowSize *log(colSize)

最終的時間複雜度為: nlog(m) + mlog(n)

Maximum Number in Mountain Sequence

cs3k.com

Given a mountain sequence of n integers which increase firstly and then decrease, find the mountain top.

在先增後減的序列中找最大值

FullSizeRender-11-03-17-19-16

找第一個下降的點

Find Peak Element

There is an integer array which has the following features:

We define a position P is a peek if:

A[P] > A[P-1] && A[P] > A[P+1]

找到任意一個peak, 前面數個上升, 後面無數個下降

A[0] A[A.length – 1]是說明至少有一個峰

for迴圈解法

for一下,找比左邊和右邊都大的數, O(n)

O(logn)的解法

對於一個不是中間的數,有如下四種情況:

FullSizeRender-11-03-17-19-19

第一種直接返回, 其他情況,保留先增後減的一半.

Search in Rotated Sorted Array

暴力for迴圈O(n)

比O(n)快的就只有O(logn)的二分了

圖解:

FullSizeRender-11-03-17-19-01-1

M1可能落在左半邊, 也可能落在右半邊. M1 >= S1, 左; M1 < S1, 右

即把陣列分為了兩部分,粉色部分[S,M]和橙色部分[M]

我們想去掉一半, 讓它仍保持RSA(rotated sorted array)

如果M落在左, 切掉[S,M]; 如果M落在右, 切掉[M,E]

此種類型題都沒有重的數字, 有重的只能for

三步翻轉法:

cs3k.com

RECOVER ROTATED SORTED ARRAY

Given a rotated sorted array, recover it to sorted array in-place.

Example [4, 5, 1, 2, 3] -> [1, 2, 3, 4, 5]

思路:45 翻轉變54, 123翻轉變321,現有54321,再全翻轉一遍

public class Solution {
    /**
     * @param nums: The rotated sorted array
     * @return: The recovered sorted array
     */
    private void reverse(ArrayList<Integer> nums, int start, int end) { for (int i = start, j = end; i < j; i++, j--) { int temp = nums.get(i); nums.set(i, nums.get(j)); nums.set(j, temp); } } public void recoverRotatedSortedArray(ArrayList<Integer> nums) { for (int index = 0; index < nums.size() - 1; index++) { if (nums.get(index) > nums.get(index + 1)) { reverse(nums, 0, index); reverse(nums, index + 1, nums.size() - 1); reverse(nums, 0, nums.size() - 1); return; } } } }

 

這道題,因為最壞的情況下, n-1個數的位置都要移動, 所以時間複雜度不會小於O(n).

難點在於空間複雜度上, 只用O(1).

在陣列操作中, 對於兩個元素之間的操作, O(1)的有一個swap.

運用swap和雙指標, 對於一個數組, 就有一個O(n)的操作叫做reverse.

所以就用reverse啦

類似的還有rotate string這道題

Search a 2D Matrix II

 

Write an efficient algorithm that searches for a value in an m x n matrix, return the occurrence of it.

This matrix has the following properties:

  • Integers in each row are sorted from left to right.
  • Integers in each column are sorted from up to bottom.
  • No duplicate integers in each row or column.

img_2899

橙色線上,從左往右,後一個一定大於前一個;

藍色線上,後一個可能和前一個相等;

所以左下到右上這條線上呢,相當於中間的位置mid,跟它比較找結果。

public class Solution {
    /**
     * @param matrix: A list of lists of integers
     * @param: A number you want to search in the matrix
     * @return: An integer indicate the occurrence of target in the given matrix
     */
    public int searchMatrix(int[][] matrix, int target) { // check corner case if (matrix == null || matrix.length == 0) { return 0; } if (matrix[0] == null || matrix[0].length == 0) { return 0; } // from bottom left to top right int n = matrix.length; int m = matrix[0].length; int x = n - 1; int y = 0; int count = 0; while (x >= 0 && y < m) { if (matrix[x][y] < target) { y++; } else if (matrix[x][y] > target) { x--; } else { count++; x--; y++; } } return count; } }