LeetCode_02_BinarySearch筆記總結

摘要

今天主要涉及到的二分查找的一些變型形式,比如在樹上,其中涉及到好些知識點,完全二叉樹,二分搜索樹,還有移位運算的原理等.

正文

1. LC167. Two Sum II - Input array is sorted


題目
Given an array of integers that is already sorted in ascending order, find two numbers such that they add up to a specific target number.
The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based.
You may assume that each input would have exactly one solution and you may not use the same element twice.
Input: numbers={2, 7, 11, 15}, target=9
Output: index1=1, index2=2
使用公司:
Amazon
難度:
容易


題目與思路分析:
這道題是說給定一個正序排列了的數組,找到兩個值要和等於sum,返回這兩個數是第幾個數(下標+1). 這道題要類比search 2D matrix這個題. 大家會發現特別的相似. 2Dmatrix這個題我們找的點就是行最大點列最小點,這樣我們通過移動遍歷m+n就可以達到目的.所以向這類型排序數組中找值的題目,都按照這種思路. 回到這個題目, 我們不妨採用兩點的形式,left在最左面,right在最右面.這樣left只能右走,right只能左走. 也就是說當sum>target的時候,說明值大了,這樣不能讓left右移動吧,因爲left右移動只會將數組變得更大, 所以只能right左移.

直接上代碼:

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int[] res = new int[2];//結果集
        if(numbers.length < 2 || numbers == null){
            return res;
        }
        int left = 0;
        int right = numbers.length - 1;
        while(left <= right){
            int sum = numbers[left] + numbers[right];
            if(sum == target){
                res[0] = left + 1;//下標+1
                res[1] = right + 1;
                break;
            }
            else if(sum > target){
                right--;
            }else{
                left++;
            }
        }
        return res;
    }
}

2. LC53. Maximum Subarray


題目
Find the contiguous subarray within an array (containing at least one number) which has the largest sum.
For example, given the array [-2,1,-3,4,-1,2,1,-5,4],
the contiguous subarray [4,-1,2,1] has the largest sum = 6.
More practice:
If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.
使用公司:
Microsoft, Bloomberg, Linkedln
難度:
容易


題目與思路分析:
這道題加上附加的問題的話,應該可以達到中等難度. 首先分析題意, 在一個數組中找到連續的子數組和最大. 這個題目應該是很簡單的,維護兩個變量,一個不斷地往前面加,然後每次和當前數組值進行比較,如果等於或者是小於的話,立馬換成當前的值,因爲你前面加了那麼多都沒我現在的多,說明前面的對我當前的值來說就是拉後腿的,所以幹掉.循環走完就可以.這樣的時間複雜度是O(n). 附加題說要分治法處理一下.這樣的話就得知道個思路,怎麼分?我們要將這個數組分成三部分去分別chuli : left, mid, right. 左右知道,中間是什麼鬼?中間就是要向兩邊擴散的形式找到包括中間值在內的最大sum. 最後再進行left, right, mid取最大就行了.從代碼上進行解析吧.

直接上代碼非分治:

class Solution {
    public int maxSubArray(int[] nums) {
        if(nums.length == 0 || nums == null){
            return -1;
        }
        int res = nums[0];
        int sum = nums[0];
        for(int i = 1; i < nums.length; i++){
            sum = Math.max(sum + nums[i], nums[i]);
            res = Math.max(sum, res);
        }
        return res;
    }
}

直接上代碼分治:

class Solution {
    public int maxSubArray(int[] nums) {
        if(nums.length == 0 || nums == null){
            return -1;
        }
        return helper(nums, 0, nums.length - 1);
    }    
    private int helper(int[] nums, int left, int right){
        if(left >= right){
            return nums[left];
            //當分到很小的時候就重了,取左右都行
        }
        int mid = left + (right - left) / 2;
        //分
        int lmax = helper(nums, left, mid - 1);//左最大
        int rmax = helper(nums, mid + 1, right);//右最大
        int mmax = nums[mid];//中間最大

        int tmp = mmax;
        //中間向左右兩邊靠取包括中間值在內的最大sum
        for(int i = mid - 1; i >= left; i--){
            tmp += nums[i];
            mmax = Math.max(mmax, tmp);
        }
        tmp = mmax;
        for(int i = mid + 1; i <= right; i++){
            tmp += nums[i];
            mmax = Math.max(mmax, tmp);
        }
        //三者最後進行比較
        return Math.max(mmax, Math.max(lmax, rmax));
    }
}

3. LC209. Minimum Size Subarray Sum

題目
Given an array of n positive integers and a positive integer s, find the minimal length of a contiguous subarray of which the sum ≥ s. If there isn’t one, return 0 instead.
For example, given the array [2,3,1,2,4,3] and s = 7,
the subarray [4,3] has the minimal length under the problem constraint.
使用公司:
Facebook
難度:
中等


題目與思路分析:
這道題還是挺有意思的, 和上題稍有類似, 這道題是說讓你找出這個數組中最小個數的連續子序列和>=target的subArray長度. 這裏有個很巧妙的思路: 首先題目說是要求連續子序列, 那麼就是整塊遷移,所以right最小下標也是從0開始不斷加起來的位置, 如圖:
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
在上面這裏發現大於等於了,但是不一定是最短的,所以猜測很有可能遠大於7以至於能去除一些元素, left就派上用場了. 首先記錄下長度,然後能移動的話一直右移動到sum < target. 這時候記錄下的就是right從開始移動最短的長度>= target, 然後循環.
這裏寫圖片描述
最後注意一下邊角問題.
直接上代碼:

class Solution {
    public int minSubArrayLen(int s, int[] nums) {  
        if(nums.length == 0 || nums == null){
            return 0;
        }
        int left = 0;
        int right = 0;
        int len = nums.length;
        int subLen = Integer.MAX_VALUE;
        //只要right沒有到邊界就能每輪靠left右移找到最小長度
        int sum = 0;
        while(right < len){
            //只有<s的時候才能繼續讓right移動
            while(right < len && sum < s){
                sum += nums[right];
                right++;
            }
            while(sum >= s){
                subLen = Math.min(subLen, right - left);
                //這裏不減1是因爲上面r++了
                sum -= nums[left];
                left++;
            }
        }
        return subLen == Integer.MAX_VALUE ? 0 : subLen;
    }
}

4. LC222. Count Complete Tree Nodes

題目
Given a complete binary tree, count the number of nodes.
使用公司:

難度:
中等


題目與思路分析:
題目就是讓求一個平衡二叉樹上的節點個數. 首先,什麼是完全二叉樹? 簡單點就是每個節點的左右節點都是有的(這裏不包括葉子節點),並且葉子節點部分,儘量是左靠的. 這樣很自然想到一個問題就是一直遍歷左節點就能知道整個樹的高度. 但是我們不用這個思路,我們的思路是: 以某個結點爲root節點的一直左的高度等於一直又的高度,說明這個樹肯定是完全bt, 並且是一種滿bt. 這種情況下有一個公式2^h - 1就是整個樹的高度. 如果不是這樣的話,那麼我們就分而治之,分到滿足這個條件的地方.敢這樣分就是因爲肯定有左右孩子.我們比較容易的找到h,但是2^h怎麼搞,這裏就要用到移位運算了

<<運算規則:按二進制形式把所有的數字向左移動對應的位數,高位移出(捨棄),低位的空位補零。
  語法格式:
  需要移位的數字 << 移位的次數
  例如: 3 << 2,則是將數字3左移2位
  計算過程:
  3 << 2
  首先把3轉換爲二進制數字0000 0000 0000 0000 0000 0000 0000 0011,然後把該數字高位(左側)的兩個零移出,其他的數字都朝左平移2位,最後在低位(右側)的兩個空位補零。則得到的最終結果是0000 0000 0000 0000 0000 0000 0000 1100,則轉換爲十進制是12.數學意義:
  在數字沒有溢出的前提下,對於正數和負數,左移一位都相當於乘以2的1次方,左移n位就相當於乘以2的n次方。
  >>運算規則:按二進制形式把所有的數字向右移動對應巍峨位數,低位移出(捨棄),高位的空位補符號位,即正數補零,負數補1.
  語法格式:
  需要移位的數字 >> 移位的次數
  例如11 >> 2,則是將數字11右移2位
  計算過程:11的二進制形式爲:0000 0000 0000 0000 0000 0000 0000 1011,然後把低位的最後兩個數字移出,因爲該數字是正數,所以在高位補零。則得到的最終結果是0000 0000 0000 0000 0000 0000 0000 0010.轉換爲十進制是3.數學意義:右移一位相當於除2,右移n位相當於除以2的n次方。
  
直接上代碼:

class Solution {
    public int countNodes(TreeNode root) {
        if(root == null){
            return 0;
        }
        //如果以某個結點爲root的最左和最右子樹的高度一樣,節點數2^h - 1
        //這個公式只是針對完全二叉樹
        int leftH = getLeftHight(root);
        //這裏不能直接傳root.left因爲如果Left爲空呢
        int rightH = getRightHight(root);
        if(leftH == rightH){
            return (2<<(leftH - 1)) - 1;
            //這裏注意leftH + 1表示前面的數要乘以2的多少次方
        }else{
            //如果不是那就迭代分別計算左右子樹
            return countNodes(root.left) + 
            countNodes(root.right) + 1;
        }
    }
    private int getLeftHight(TreeNode root){
        int count = 1;
        while(root.left != null){
            count++;
            root = root.left;
        }
        return count;
    }
    private int getRightHight(TreeNode root){
        int count = 1;
        while(root.right != null){
            count++;
            root = root.right;
        }
        return count;
    }
}

5. LC270. Closest Binary Search Tree Value

題目
Given a non-empty binary search tree and a target value, find the value in the BST that is closest to the target.
Note:
Given target value is a floating point.
You are guaranteed to have only one unique value in the BST that is closest to the target.
使用公司:
Google, Microsoft, Snapchat
難度:
容易


題目與思路分析:
題中說給定一個二分查找樹,即BST, 找到最接近target的節點,這個taget是個float類型. 這道題就比較簡單了,首先要知道什麼是二分查找樹,就是在二叉樹中,如果左子樹存在,則一定小於root, 如果右子樹存在,則一定大於root.並且不存在重複值,其實就是中序遍歷的數組. 這裏我們的思路是: 將每個節點都看作是root然後進行比較, 但是爲了節省時間複雜度,利用到二分查找,所有每次都判斷root和target的值, 小於就把root給了left,然後記錄下差的絕對值, 不然就給了right.
直接上代碼:

class Solution {
    public int closestValue(TreeNode root, double target) {
        if(root == null){
            return -1;
        }
        //就判斷root就行,把每個點都當做是root
        int res = root.val;
        double minValue = Double.MAX_VALUE;
        while(root != null){
            if(Math.abs(root.val - target) < minValue){
                minValue = Math.abs(root.val - target);
                res = root.val;
            }
            if(target > root.val){
                root = root.right;
            }
            else if(target < root.val){ 
                root = root.left;
            }else{
                return root.val;
            }
        }
        return res;
    }
}

總結

今天的題目有得很有趣,有一些小的技巧在裏面,一次不會沒事,多去回過頭來看看怎麼操作這些數組或樹. BinarySearch的這類型題目也是很容易出現在公司的面試中.


聯繫方式: [email protected]

發佈了19 篇原創文章 · 獲贊 2 · 訪問量 4155
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章