LeetCode 題解記錄 - 二分查找

刷題 - 二分查找

[TOC]java


兩數相除

29. 兩數相除 中等數組

題目描述:bash

給定兩個整數,被除數 dividend 和除數 divisor。將兩數相除,要求不使用乘法、除法和 mod 運算符。ide

返回被除數 dividend 除以除數 divisor 獲得的商。函數

輸入: dividend = 10, divisor = 3
輸出: 3

輸入: dividend = 7, divisor = -3
輸出: -2
複製代碼

說明:單元測試

  • 被除數和除數均爲 32 位有符號整數。
  • 除數不爲 0。
  • 假設咱們的環境只能存儲 32 位有符號整數,其數值範圍是 [−231, 231 − 1]。本題中,若是除法結果溢出,則返回 231 − 1。

解題思路:測試

使用位運算代替乘除法+二分查找。spa

除法:被除數 / 除數 = 商...餘數code

同理,除法能夠理解爲多少個除數相加,而後加上餘數,等於被除數,例如:10 / 3 = 3 * 2 + 3 * 1 + 1,商 = 2 + 1 = 3,最後一個沒有相乘的數字就是餘數,餘數 = 1cdn

位移:向左移動一位,至關於乘以 2;向右移動一位,至關於除以 2。

以 10 / 3 舉例:

第一個符合條件:
i = 1;
t >> 1 = 10 / 2 ^ 1 = 5 > 3;
result = result + 1 << 1 = 0 + 1 * 2 ^ 1 = 2;
t = t - 3 << 1 = 10 - 3 * 2 = 4;

第二個符合條件:
i = 0;
t >> 0 = 4 / 2 ^ 0 = 4 > 3;
result = result + 1 << 0 = 2 + 1 * 2 ^ 0 = 3;
t = 4 - 3 << 0 = 4 - 3 * 2 ^ 0 = 1;
複製代碼

經過上面的例子,最終 t 就是餘數,result 就是咱們要求的商

代碼:

public int divide(int dividend, int divisor) {
    if (divisor == 0) {
        return 0;
    }
    if (dividend == Integer.MIN_VALUE && divisor == -1) {
        return Integer.MAX_VALUE;
    }
    // 用來標誌兩數符號是否不一致, 計算過程當中使用絕對值
    boolean negative = (dividend ^ divisor) < 0;
    long t = Math.abs((long) dividend);
    long d = Math.abs((long) divisor);
    int result = 0;
    for (int i = 31; i >= 0; i--) {
        // 找出足夠大的數 2 ^ i * divisor
        if ((t >> i) >= d) {
            // 結果加上 2 ^ i
            result += 1 << i;
            // 被除數減去 2 ^ i * divisor
            t -= d << i;
        }
    }
    return negative ? - result : result;
}
複製代碼

Pow(x, n)

50. Pow(x, n) 中等

題目描述:

實現 pow(x, n) ,即計算 x 的 n 次冪函數。

輸入: 2.00000, 10
輸出: 1024.00000

輸入: 2.10000, 3
輸出: 9.26100

輸入: 2.00000, -2
輸出: 0.25000
解釋: 2-2 = 1/22 = 1/4 = 0.25

說明:

一、 -100.0 < x < 100.0
二、 n 是 32 位有符號整數,其數值範圍是 [−231, 231 − 1] 。
複製代碼

解題思路:

一、一開始想到的確定是循環 n 次進行相乘,暴力解開。

二、使用折半計算,每次將 n 減半。

每次計算時,將底數相乘,達到翻倍的效果。

當 n 變成奇數時,少乘一次 x 的值。

最後判斷次方數的符號,大於零返回 res,否者取倒數。

代碼:

public double myPow(double x, int n) {
    double res = 1.0;
    for (int i = n; i !=0; i/=2) {
        if (i % 2 != 0){
            res *= x;
        }
         x *= x;
    }
    return n < 0 == true ? 1/ res : res;
}
複製代碼

尋找旋轉排序數組中的最小值

153. 尋找旋轉排序數組中的最小值 中等

題目描述:

假設按照升序排序的數組在預先未知的某個點上進行了旋轉。

( 例如,數組 [0,1,2,4,5,6,7] 可能變爲 [4,5,6,7,0,1,2] )。

請找出其中最小的元素。

你能夠假設數組中不存在重複元素。

輸入: [3,4,5,1,2]
輸出: 1

輸入: [4,5,6,7,0,1,2]
輸出: 0
複製代碼

解答思路:

使用折半查找,每次循環,比較中位數,與左右下標的數進行對比。

不斷縮減左右下標,找到斷層的數字。

代碼:

public int findMin(int[] nums) {
    if (nums == null || nums.length == 0) {
        return 0;
    }
    int l = 0, r = nums.length - 1;
    // 若是長度爲 1,或者數組已是有序的,返回第一個數
    if (l == r || nums[l] < nums[r]) {
        return nums[0];
    }
    // 每次使用數組的最左和最右下標對應的數字進行判斷
    while (l < r) {
        // 折半取中下標
        int mid = (l + r) / 2;
        if (mid == l) {
            // 只剩兩個數,取最小值
            return Math.min(nums[l], nums[r]);
        }
        if (nums[mid] > nums[l]) {
            // 中位下標對應的數比左下標大
            if (nums[mid] < nums[r]) {
                // 同時比右下標小,說明斷層發生在[左下標和中下標)之間
                r = mid - 1;
            } else {
                // 否者說明斷層發生在(中下標和右下標]之間
                l = mid + 1;
            }
        } else {
            // 直接判斷 斷層發生在[左下標和中座標]之間
            r = mid;
        }
    }
    return nums[l];
}
複製代碼

尋找峯值

162. 尋找峯值 中等

描述:

峯值元素是指其值大於左右相鄰值的元素。

給定一個輸入數組 nums,其中 nums[i] ≠ nums[i+1],找到峯值元素並返回其索引。

數組可能包含多個峯值,在這種狀況下,返回任何一個峯值所在位置便可。

你能夠假設 nums[-1] = nums[n] = -∞。

輸入: nums = [1,2,3,1]
輸出: 2
解釋: 3 是峯值元素,你的函數應該返回其索引 2。

輸入: nums = [1,2,1,3,5,6,4]
輸出: 1 或 5 
解釋: 你的函數能夠返回索引 1,其峯值元素爲 2;
     或者返回索引 5, 其峯值元素爲 6。
複製代碼

解題思路:

因爲題目描述中,點明

1、不會出現nums[i] != nums[i + 1]

2、峯值元素是指其值大於左右相鄰值的元素(即只需找到 nums[i] > nums[i+1],就是其中一個峯值)

3、有多個峯值狀況下,返回任意一個便可

因此只須要經過二分查找,定位到某一個解

代碼:

public int findPeakElement(int[] nums) {
    int left = 0;
    int right = nums.length - 1;
    
    while (left < right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] > nums[mid + 1]) {
            right = mid;
        } else {
            left = mid + 1;
        }
    }
    return left;
}
複製代碼

第一個錯誤的版本

題目描述:

你是產品經理,目前正在帶領一個團隊開發新的產品。不幸的是,你的產品的最新版本沒有經過質量檢測。因爲每一個版本都是基於以前的版本開發的,因此錯誤的版本以後的全部版本都是錯的。

假設你有 n 個版本 [1, 2, ..., n],你想找出致使以後全部版本出錯的第一個錯誤的版本。

你能夠經過調用 bool isBadVersion(version) 接口來判斷版本號 version 是否在單元測試中出錯。實現一個函數來查找第一個錯誤的版本。你應該儘可能減小對調用 API 的次數。

給定 n = 5,而且 version = 4 是第一個錯誤的版本。

調用 isBadVersion(3) -> false
調用 isBadVersion(5) -> true
調用 isBadVersion(4) -> true

因此,4 是第一個錯誤的版本。 
複製代碼

解題思路:

是個中規中矩的二分查找,找中下標時,使用了mid = left + (right - left) / 2,避免(right + left) / 2 狀況x

代碼:

/* The isBadVersion API is defined in the parent class VersionControl. boolean isBadVersion(int version); */

public class Solution extends VersionControl {
    public int firstBadVersion(int n) {
        int left = 1;
        int right = n;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (isBadVersion(mid)) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }
}
複製代碼
相關文章
相關標籤/搜索