LeetCode:DC,Greedy,Stack

5.Divide and Conquer

1.Majority Element

Given an array of size n, find the majority element. The majority element is the element that appears more than ⌊ n/2 ⌋ times.

You may assume that the array is non-empty and the majority element always exist in the array.
解答:

public class Solution {
    //success 1
    //sort方法,很trick,對於任何數組來說,如果存在majority element,sort後一定在length/2處。
    // public int majorityElement(int[] nums) {
    //     Arrays.sort(nums);
    //     int length = nums.length;
    //     return nums[length/2];
    // }
    //success 2
    //利用hashMap
    // public int majorityElement(int[] nums) {
    //     int length = nums.length;
    //     HashMap<Integer,Integer> map = new HashMap<>();
    //     for (int num:
    //          nums) {
    //         if(!map.containsKey(num)){
    //             map.put(num,1);
    //         }else{
    //             int value = map.get(num)+1;
    //             map.put(num,value);
    //         }
    //         if(map.get(num)>length/2){
    //                 return num;
    //             }
    //     }
    //     return -1;
    // }
    //success 3
    //該問題專用的算法:Boyer–Moore majority vote algorithm
    public int majorityElement(int[] nums) {
        int value = Integer.MAX_VALUE;
        int count = 0;
        for (int num:
             nums) {
            if(count==0){
                value = num;
                count++;
            }else if(value!=num){
                count--;
            }else{
                count++;
            }
        }
        return value;
    }
}

關於算法三,可以參考Boyer–Moore majority vote algorithm。另附:一個演示該算法的網站

2.Search a 2D Matrix II

Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:

1.Integers in each row are sorted in ascending from left to right.
2.Integers in each column are sorted in ascending from top to bottom.

從上面兩條規則可以得出,一個元素的所有左上角元素一定比之小,一個元素的所有右下角元素一定比之大,其他部位(右上角和左下角)的所有元素與之關係不確定。
For example,

Consider the following matrix:

[
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
]

Given target = 5, return true.
Given target = 20, return false.

我剛開始誤以爲對角線元素可以做分割,但之後確認是錯誤的,貼出錯誤代碼:

//請注意,該代碼不完整,只是爲了演示我最初的思路
public boolean findMatrix(int[][] matrix,int[] start,int[] end,int target) {
            int[] middle = new int[2];
            middle[0] = (start[0]+end[0])/2;
            middle[1] = (start[1]+end[1])/2;
            int middleValue = matrix[middle[0]][middle[1]];
            if(target==middleValue){
                return true;
            }else if(target<middleValue){
            //錯誤思路:只考慮左上角
                return findMatrix(matrix,start,middle,target);
            }else if(target>middleValue){
            //錯誤思路:只考慮右下角
                return findMatrix(matrix,middle,end,target);
            }
            }

思路2:根據規則,最左上爲最小,最右下爲最大。跟這樣的元素做比較然後通過遞歸調用是沒有用的,因爲對於不同的路分割條件是一樣的。

如果取右上角元素就可以遞歸,因爲不同的路分割條件是不一樣的。比如,如果target比該元素小,那麼排除該元素所在的列(因爲都比target大),同理,如果比該元素小,排除該元素所在的行。這樣,每次調用方法都排除一行或一列,總的時間複雜度爲O(M+N)

代碼如下:

public class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int M = matrix.length;
        int N = matrix[0].length;
        if(target<matrix[0][0]||target>matrix[M-1][N-1]){
            return false;
        }
        boolean result = findTarget(matrix,0,N-1,target);
        return result;
    }
    public boolean findTarget(int[][] matrix,int row,int column,int target){
        if(row>=0&&row<matrix.length&&column>=0&&column<matrix[0].length){
            int compareValue = matrix[row][column];
            if(target==compareValue){
                return true;
            }else if(target<compareValue){
                //排除列
                return findTarget(matrix,row,column-1,target);
            }else if(target>compareValue){
                //排除行
                return findTarget(matrix,row+1,column,target);
            }
        }else{
            return false;
        }
        return false;
    }
    }

上述代碼解決很優美,如果想關於此題的更多解法,可參考Search in a Sorted Matrix

3.Kth Largest Element in an Array

Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.

For example,
Given [3,2,1,5,6,4] and k = 2, return 5.

代碼如下:

public class Solution {
    //success 1
    //O(N lg N) running time + O(1) memory
    //直接排序,然後找出來
    // public int findKthLargest(int[] nums, int k) {
    //     Arrays.sort(nums);
    //     int reveredK = nums.length+1-k;
    //     return nums[reveredK-1];
    // }

    //success 2
    //O(N lg K) running time + O(K) memory
    //維護一個k size的優先隊列
    public int findKthLargest(int[] nums, int k) {
    //對於Integer來說,優先隊列默認順序是數值小的在隊列頭
    final PriorityQueue<Integer> pq = new PriorityQueue<>();
    for(int val : nums) {
        //添加進隊列,添加後會自動排序
        pq.offer(val);
        if(pq.size() > k) {
            //彈出隊列頭元素
            pq.poll();
        }
    }
    //取得隊列頭元素值,但不彈出
    return pq.peek();
}
}

4.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.

第一種方法很簡單,直接寫:

public class Solution {
    //success 1
    //直接寫,很簡單
    public int maxSubArray(int[] nums) {
        int count=0,largest=Integer.MIN_VALUE;
        for (int i = 0; i < nums.length; i++) {
            count+=nums[i];
            if(count>largest){
                largest=count;
            }
            if(count<0){
                count=0;
            }
        }
        return largest;
    }
    }

我自己寫的算法居然還跟著名的算法Kadane’s Algorithm一樣!哈哈哈!它應該是針對此類問題的比較好的解法。

第二種方法,我想用分治法實現,但是出問題了,代碼如下:

public class Solution {
    public int maxSubArray(int[] nums) {
        if(nums.length==1){
            return nums[0];
        }
        if(nums.length==2){
            return Math.max(nums[0]+nums[1],Math.max(nums[0],nums[1]));
        }
        int[] result = findMaxArray(nums,0,nums.length-1);
        return result[2];
    }
    public int[] findMaxArray(int[] nums,int left,int right){
        //return maxsum's low,high and the sum
        System.out.println(left+" "+right);
        if(right==left){
            int[] result = {left,right,nums[left]};
            return result;
        }
        //注意:此種寫法容易引起溢出
        //int middle = (left+right)/2;
        //正確寫法
        int middle = left+(right-left)/2;
        int[] leftResult = findMaxArray(nums,left,middle);
        int[] rightResult = findMaxArray(nums,middle+1,right);
        int leftLargest = leftResult[2];
        int rightLargest = rightResult[2];
        int leftHigh = leftResult[1];
        int rightLow = rightResult[0];
        int intervalSum=0;
        for (int i = leftHigh+1; i < rightLow; i++) {
            intervalSum+=nums[i];
        }
        int middleLargest = leftLargest+rightLargest+intervalSum;
        System.out.println("left: "+leftLargest+" right: "+rightLargest+" middle: "+middleLargest);
        if(leftLargest >= rightLargest && leftLargest >= middleLargest){// left part is max
            System.out.println("left");
            return leftResult;
        }
        if(rightLargest >= leftLargest && rightLargest >= middleLargest){// right part is max
            System.out.println("right");
            return rightResult;
        }
        System.out.println("middle");
        int[] result = {leftResult[0],rightResult[1],middleLargest};
        return result;
    }
    }

通過打log,輸出如下:

0 8
0 4
0 2
0 1
0 0
1 1
left: -2 right: 1 middle: -1
right
2 2
left: 1 right: -3 middle: -2
left
3 4
3 3
4 4
left: 4 right: -1 middle: 3
left
left: 1 right: 4 middle: 2
right
5 8
5 6
5 5
6 6
left: 2 right: 1 middle: 3
middle
7 8
7 7
8 8
left: -5 right: 4 middle: -1
right
left: 3 right: 4 middle: 2
right
left: 4 right: 4 middle: 5
middle

通過log,分析得出錯誤原因:在定義middle時並不是包含中間元素的最大和子數組。log中發現對於leftLargest=4,rightLargest=4時,程序發現[4,-1,2,1,-5,4]和爲5,比leftLargest和rightLargest都大,所以輸出最大和子數組爲5。但是,我們可以通過[4,-1,2,1]發現最大和爲6。

修改定義middle的策略,得出正確答案:

public class Solution {
    public int maxSubArray(int[] nums) {
        if(nums.length==1){
            return nums[0];
        }
        if(nums.length==2){
            return Math.max(nums[0]+nums[1],Math.max(nums[0],nums[1]));
        }
        int[] result = findMaxArray(nums,0,nums.length-1);
        return result[2];
    }
public int[] findMaxArray(int[] nums,int left,int right){
        //return maxsum's low,high and the sum
        System.out.println(left+" "+right);
        if(right==left){
            int[] result = {left,right,nums[left]};
            return result;
        }
        //注意:此種寫法容易引起溢出
        //int middle = (left+right)/2;
        //正確寫法
        int middle = left+(right-left)/2;
        int[] leftResult = findMaxArray(nums,left,middle);
        int[] rightResult = findMaxArray(nums,middle+1,right);
        int leftLargest = leftResult[2];
        int rightLargest = rightResult[2];
        int leftSum=Integer.MIN_VALUE,rightSum=Integer.MIN_VALUE,leftSumTmp=0,rightSumTmp=0,middleLeft=0,middleRight=0;
        for (int i = middle; i >=left; i--) {
            leftSumTmp+=nums[i];
            if(leftSumTmp>leftSum){
                leftSum = leftSumTmp;
                middleLeft=i;
            }
        }
        for (int i = middle+1; i <=right; i++) {
            rightSumTmp+=nums[i];
            if(rightSumTmp>rightSum){
                rightSum = rightSumTmp;
                middleRight=i;
            }
        }
        int middleLargest = leftSum+rightSum;
        System.out.println("left: "+leftLargest+" right: "+rightLargest+" middle: "+middleLargest);
        if(leftLargest >= rightLargest && leftLargest >= middleLargest){// left part is max
            System.out.println("left");
            return leftResult;
        }
        if(rightLargest >= leftLargest && rightLargest >= middleLargest){// right part is max
            System.out.println("right");
            return rightResult;
        }
        System.out.println("middle");
        int[] result = {middleLeft,middleRight,middleLargest};
        return result;
    }
    }

通過log發現,程序正常運轉:

0 8
0 4
0 2
0 1
0 0
1 1
left: -2 right: 1 middle: -1
right
2 2
left: 1 right: -3 middle: -2
left
3 4
3 3
4 4
left: 4 right: -1 middle: 3
left
left: 1 right: 4 middle: 2
right
5 8
5 6
5 5
6 6
left: 2 right: 1 middle: 3
middle
7 8
7 7
8 8
left: -5 right: 4 middle: -1
right
left: 3 right: 4 middle: 2
right
left: 4 right: 4 middle: 6
middle
//最大子數組start index
3
//最大子數組end index
6
//最大子數組sum
6

5.Count of Smaller Numbers After Self

You are given an integer array nums and you have to return a new counts array. The counts array has the property where counts[i] is the number of smaller elements to the right of nums[i].

Example:

Given nums = [5, 2, 6, 1]
Return the array [2, 1, 1, 0].

第一種思路比較簡單,就是無腦對比,貼上源碼:

public class Solution {
    //fail 1,Time Limit Exceeded
    //思想很樸素,就是無腦對比
    public List<Integer> countSmaller(int[] nums) {
        if(nums.length==0){
            return new ArrayList<>();
        }
        Integer[] smallerArray = new Integer[nums.length];
        smallerArray[smallerArray.length-1] = 0;
        for (int i = nums.length-2; i >=0; i--) {
            int count=0;
            for (int j = i; j < nums.length; j++) {
                if(nums[i]>nums[j]){
                    count++;
                }
                if(j==nums.length-1){
                    smallerArray[i]=count;
                }
            }
        }
        List<Integer> list = new ArrayList<>();
        list = Arrays.asList(smallerArray);
        return list;
    }
}

此方法的時間效率不高,在submit時會出現TimeOut。得想出更高效率的算法啊。有大神在Discuss中提到一個思路:

The smaller numbers on the right of a number are exactly those that jump from its right to its left during a stable sort. So I do mergesort with added tracking of those right-to-left jumps.

因此我可以用merge sort的思想實現試試。

首先溫習一下merge sort:
Merge Sort

貼出源碼:

public class Solution {

    //success 2
    //運用merge sort追加記錄的方式實現
    public List<Integer> countSmaller(int[] nums) {
        Integer[] smaller = new Integer[nums.length];
        Arrays.fill(smaller,0);
        MapNums[] mapNums= new MapNums[nums.length];
        for (int i = 0; i < nums.length; i++) {
            mapNums[i] = new MapNums(i,nums[i]);
        }
        sort(mapNums, 0, mapNums.length-1,smaller);
        List<Integer> list = new ArrayList<>();
        list = Arrays.asList(smaller);
        return list;
    }

    void merge(MapNums arr[], int l, int m, int r,Integer[] smaller)
    {
        // Find sizes of two subarrays to be merged
        int n1 = m - l + 1;
        int n2 = r - m;

        /* Create temp arrays */
        MapNums L[] = new MapNums [n1];
        MapNums R[] = new MapNums [n2];

        /*Copy data to temp arrays*/
        for (int i=0; i<n1; ++i)
            L[i] = arr[l + i];
        for (int j=0; j<n2; ++j)
            R[j] = arr[m + 1+ j];


        /* Merge the temp arrays */

        // Initial indexes of first and second subarrays
        int i = 0, j = 0;

        // Initial index of merged subarry array
        int k = l;
        while (i < n1 && j < n2)
        {
            if (L[i].number <= R[j].number)
            {
                arr[k] = L[i];
                //本算法精髓
                smaller[L[i].index]+=j;
                i++;
            }
            else
            {
                arr[k] = R[j];
                j++;
            }
            k++;
        }

        /* Copy remaining elements of L[] if any */
        while (i < n1)
        {
            arr[k] = L[i];
            //本算法精髓
            smaller[L[i].index]+=j;
            i++;
            k++;
        }

        /* Copy remaining elements of L[] if any */
        while (j < n2)
        {
            arr[k] = R[j];
            j++;
            k++;
        }
    }

    // Main function that sorts arr[l..r] using
    // merge()
    void sort(MapNums arr[], int l, int r,Integer[] smaller)
    {
        if (l < r)
        {
            // Find the middle point
            //注意:此種寫法容易引起溢出
            //int m = (l+r)/2;
            //正確寫法
            int m = l+(r-l)/2;
            // Sort first and second halves
            sort(arr, l, m,smaller);
            sort(arr , m+1, r,smaller);

            // Merge the sorted halves
            merge(arr, l, m, r,smaller);
        }
    }

//內部類
//類MapNums主要是記錄每個元素的index和number,
//因爲運用merge sort排序後,如果不記錄index(在原始數組中的位置)的話,就沒有辦法向smaller數組中寫入正確信息。
class MapNums{
    int number;
    int index;
    public MapNums(int index,int number){
        this.index = index;
        this.number = number;
    }
}

}

6.Greedy

1.Assign Cookies

分配糖果的問題,很簡單,直接寫:

public class Solution {
    public int findContentChildren(int[] g, int[] s) {
        //sort
        Arrays.sort(g);
        Arrays.sort(s);
        int m = g.length;
        int n = s.length;
        int i=0,j=0,sum=0;
        while(i<m&&j<n){
            if(g[i]<=s[j]){
                sum++;
                i++;
                j++;
            }else{
                j++;
            } 
        }
        return sum;
    }
}

2.Jump Game

Given an array of non-negative integers, you are initially positioned at the first index of the array.

Each element in the array represents your maximum jump length at that position.

Determine if you are able to reach the last index.

For example:
A = [2,3,1,1,4], return true.

A = [3,2,1,0,4], return false.

public class Solution {
    //success 1
    //從後向前
    public boolean canJump(int[] nums) {
        int n = nums.length;
        int last=n-1;
        for(int i=n-2;i>=0;i--){
            if(i+nums[i]>=last)last=i;
        }
        return last<=0;
    }

    //success 2
    //從前向後
//     public boolean canJump(int[] nums) {
//     int reachable = 0;
//     for (int i=0; i<nums.length; ++i) {
//         if (i > reachable) return false;
//         reachable = Math.max(reachable, i + nums[i]);
//     }
//     return true;
// }
}

3.Wiggle Subsequence

A sequence of numbers is called a wiggle sequence if the differences between successive numbers strictly alternate between positive and negative. The first difference (if one exists) may be either positive or negative. A sequence with fewer than two elements is trivially a wiggle sequence.

For example, [1,7,4,9,2,5] is a wiggle sequence because the differences (6,-3,5,-7,3) are alternately positive and negative. In contrast, [1,4,7,2,5] and [1,7,4,5,5] are not wiggle sequences, the first because its first two differences are positive and the second because its last difference is zero.

Given a sequence of integers, return the length of the longest subsequence that is a wiggle sequence. A subsequence is obtained by deleting some number of elements (eventually, also zero) from the original sequence, leaving the remaining elements in their original order.

Examples:
Input: [1,7,4,9,2,5]
Output: 6
The entire sequence is a wiggle sequence.

Input: [1,17,5,10,13,15,10,5,16,8]
Output: 7
There are several subsequences that achieve this length. One is [1,17,10,13,10,16,8].

Input: [1,2,3,4,5,6,7,8,9]
Output: 2

public class Solution {
    //success 1
    //跟LIS的思路相同
    public int wiggleMaxLength(int[] nums) {
        int length = nums.length;
        if(length==0){
            return 0;
        }
        Pair[] dp = new Pair[length];
        //特別注意:這種寫法fill的都是同一個對象,所以會相互影響!
        //Arrays.fill(dp,new Pair(1,0));
        //改寫爲下式
        for (int i = 0; i < dp.length; i++) {
            Pair pair = new Pair(1,0);
            dp[i] = pair;
        }
        //from j to i
        for (int i = 1; i < nums.length; i++) {
            for (int j = 0; j < i; j++) {
                if(nums[j]<nums[i]){
                    if(dp[j].symbol!=1){
                        if(dp[i].value<dp[j].value+1){
                            dp[i].value = dp[j].value+1;
                            dp[i].symbol=1;
                        }
                    }
                }else if(nums[j]>nums[i]){
                    if(dp[j].symbol!=2){
                        if(dp[i].value<dp[j].value+1){
                            dp[i].value = dp[j].value+1;
                            dp[i].symbol=2;
                        }
                    }
                }
            }
        }

        int largest = Integer.MIN_VALUE;
        for (int i = 0; i < dp.length; i++) {
            if(dp[i].value>largest){
                largest = dp[i].value;
            }
        }
        return largest;
    }
    class Pair{
         public int value;
        //1 for add,2 for sub,0 for init
         public int symbol;
        public Pair(int value,int symbol){
            this.value = value;
            this.symbol = symbol;
        }
    }
}

思路二:運用greedy的思想,參考Very Simple Java Solution with detail explanation實現之:

public class Solution {
    public int wiggleMaxLength(int[] nums) {
        if (nums.length == 0 || nums.length == 1) {
            return nums.length;
        }
        int k = 0;
        while (k < nums.length - 1 && nums[k] == nums[k + 1]) {  //Skips all the same numbers from series beginning eg 5, 5, 5, 1
            k++;
        }
        if (k == nums.length - 1) {
            return 1;
        }
        int result = 2;     // This will track the result of result array
        boolean smallReq = nums[k] < nums[k + 1];       //To check series starting pattern
        for (int i = k + 1; i < nums.length - 1; i++) {
            if (smallReq && nums[i + 1] < nums[i]) {
                nums[result] = nums[i + 1];
                result++;
                smallReq = !smallReq;    //Toggle the requirement from small to big number
            } else {
                if (!smallReq && nums[i + 1] > nums[i]) {
                    nums[result] = nums[i + 1];
                    result++;
                    smallReq = !smallReq;    //Toggle the requirement from big to small number
                }
            }
        }
        return result;
    }
}

根據該greedy的實現思想,如果需要,還能夠返回具有該最大長度的數組是哪個。

4.Candy

There are N children standing in a line. Each child is assigned a rating value.

You are giving candies to these children subjected to the following requirements:

Each child must have at least one candy.
Children with a higher rating get more candies than their neighbors.
What is the minimum candies you must give?代碼:

public class Solution {
    //fail 1
    //timeout
    // public int candy(int[] ratings) {
    //     int length = ratings.length;
    //     int[] candys = new int[length];
    //     Arrays.fill(candys,1);
    //     for (int j = 0; j < length-1; j++) {
    //         if(ratings[j]>ratings[j+1]){
    //             while(j>=0&&candys[j]<=candys[j+1]&&ratings[j]>ratings[j+1]){
    //                 candys[j]=candys[j+1]+1;
    //                 j--;
    //             }
    //         }else if(ratings[j]<ratings[j+1]){
    //             candys[j+1]=candys[j]+1;
    //         }
    //     }
    //     int num = 0;
    //     for (int i = 0; i < candys.length; i++) {
    //         num+=candys[i];
    //     }
    //     return num;
    // }

    //success 2
    //在1的基礎上進行優化    
    public int candy(int[] ratings) {

    int len = ratings.length;
    int[] candy = new int[len];

    candy[0] =1;
    for (int i = 1; i < len; ++i) {
        if (ratings[i] > ratings[i-1]) {
            candy[i] = candy[i-1] + 1;
        } else {
            candy[i] = 1;
        }
    }

    int total = candy[len-1];
    for (int i = len - 2; i >= 0; --i) {
        if (ratings[i] > ratings[i+1] && candy[i] <= candy[i+1]) {
            candy[i] = candy[i+1] + 1;
        }
        total += candy[i];    
    }
    return total;
}
}

5.Create Maximum Number

Given an array of non-negative integers, you are initially positioned at the first index of the array.

Each element in the array represents your maximum jump length at that position.

Your goal is to reach the last index in the minimum number of jumps.

For example:
Given array A = [2,3,1,1,4]

The minimum number of jumps to reach the last index is 2. (Jump 1 step from index 0 to 1, then 3 steps to the last index.)

Note:
You can assume that you can always reach the last index.

public class Solution {
    //fail 1
    //timeOut
    //dp的思想
    // public int jump(int[] nums) {
    //     if(nums.length<=1){
    //         return 0;
    //     }
    //     if(nums.length==2){
    //         return 1;
    //     }
    //     //dp代表最小几步到下一層
    //     int length = nums.length;
    //     int[] dp = new int[length];
    //     Arrays.fill(dp,Integer.MAX_VALUE);
    //     dp[length-1]=0;
    //     for (int i = length-2; i >=0 ; i--) {
    //         for (int j = i+1; j < length; j++) {
    //             //can reach
    //             if(nums[i]+i>=j){
    //                 if(dp[j]!=Integer.MAX_VALUE){
    //                 dp[i] = Math.min(dp[i],dp[j]+1);
    //                 }
    //             }
    //         }
    //     }
    //     return dp[0];
    // }

    //success 2
    //BFS solution
    public int jump(int A[]) {
     int n=A.length;
     if(n<2)return 0;
     int level=0,currentMax=0,i=0,nextMax=0;

     while(currentMax-i+1>0){       //nodes count of current level>0
         level++;
         for(;i<=currentMax;i++){   //traverse current level , and update the max reach of next level
            nextMax=Math.max(nextMax,A[i]+i);
            if(nextMax>=n-1)return level;   // if last element is in level+1,  then the min jump=level 
         }
         currentMax=nextMax;
     }
     return 0;
 }
}

7.Stack

1.Min Stack

Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.

push(x) – Push element x onto stack.
pop() – Removes the element on top of the stack.
top() – Get the top element.
getMin() – Retrieve the minimum element in the stack.

記錄最小值,這樣可以實現快速返回,注意Stack裏爲long,因爲有int的相減,可能發生越界。

public class MinStack {
    Stack<Long> stack;
    long min;//存儲最小的值,這樣可以快速返回

    /**
     * initialize your data structure here.
     */
    public MinStack() {
        stack = new Stack<>();
    }

    public void push(int x) {
        if (stack.isEmpty()) {
            min = x;
            stack.push(0L);
        } else {
            //push動作比較的最小值是push動作之前所有值中的最小值
            stack.push(x - min);
            if (x < min) {
                min = x;
            }
        }
    }

    public void pop() {
        if (stack.isEmpty()) {
            return;
        } else {
            long tmp = stack.pop();
            if (tmp < 0) {
                min = min - tmp;
            }
        }
    }

    public int top() {
        long result = stack.peek();
        if (result > 0) {
            return (int)(result + min);
        } else {
            return (int)min;
        }
    }

    public int getMin() {
        return (int)min;
    }
}

2.Binary Tree Preorder Traversal

實現二叉樹的前序遍歷。

思路一:利用遞歸(簡單標準做法):

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {

    //success 1
    //Recursive method
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if(root!=null){
            recursive(root,list);
        }
        return list;
    }
    public void recursive(TreeNode root,List list){
        if(root!=null){
            list.add(root.val);
        }
        if(root.left!=null){
            recursive(root.left,list);
        }
        if(root.right!=null){
            recursive(root.right,list);
        }
    }
}

思路二:用遞歸的實現很簡單,但需要調用更多的程序棧,我們可以用迭代的思想再實現之:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    //success 2
    //iterative method
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        //Stack is a subclass of Vector that implements a standard last-in, first-out stack.
        Stack<TreeNode> stack = new Stack<>();
        //用stack來記錄right
        while(root!=null){
            list.add(root.val);
            //處理right
            if(root.right!=null){
                stack.push(root.right);
            }
            //處理left
            root = root.left;
            if(root==null&&!stack.isEmpty()){
                root = stack.pop();
            }
        }
        return list;
    }
}

下面我們來了解一種數據結構,叫segment tree

如果我們需要在一個array中查找index在[start,end]區間中的最小值(最大值,和等),那麼我們最樸素的做法是直接比較,然後返回,這樣對於m次不同的查找,時間複雜度爲O(mn),m爲次數,n爲array的length。當m很大,即查詢次數很多時,效率就很低,這時就可以引入segment tree了,它能夠使查詢的時間複雜度爲O(logN)。

一種加快的方式是,事先計算好這些結果,然後存入矩陣中,矩陣中元素(i,j)的值代表index在區間[i,j]上的最小值。但是,要維護這個矩陣,我們需要O(N×N)時間創建它,需要O(N×N)空間存儲,我們還可以用更好的數據結構來得到這種便利。

segment tree:我們花O(N)來創建它,需要O(N)空間存儲,查詢複雜度爲O(logN)。Segment Tree Range Minimum Query youtube 視頻

這裏寫圖片描述

如視頻截圖,我們的模型雖然是tree,但存儲該樹的元素是用數組存儲的,這跟heap sort中實現的策略是一樣的。

視頻中對於遞歸方法調用的步驟分析用到了lineNumber,這種分析思路值得借鑑。

具體的關於創建和查詢的代碼:

public class Solution {
    private int[] input;
    private int[] segTree;
    void init(int[] input1){
        input = input1;
        int n  = input.length;
        int length;
        if(isPowOfN(n,2)){
            length=2*n-1;
        }else{
            length = 2*findNextPowOfN(n,2)-1;
        }
        segTree = new int[length];
        //fill max value
        Arrays.fill(segTree,Integer.MAX_VALUE);
        //pos 表示tree的根節點
        constructTree(input,segTree,0,n-1,0);
    }


    void constructTree(int[] input,int[] segTree,int low,int high,int pos){
        if(low==high){
            segTree[pos] = input[low];
            return;
        }
        int mid = low+(high-low)/2;
        //left
        constructTree(input,segTree,low,mid,2*pos+1);
        //right
        constructTree(input,segTree,mid+1,high,2*pos+2);
        segTree[pos] = Math.min(segTree[2*pos+1],segTree[2*pos+2]);
    }

    boolean isPowOfN(int num,int n){
        if(n==0){
            return false;
        }
        while(num%n==0){
            num = num/n;
        }
        return num==1;
    }

    int findNextPowOfN(int num,int n){
        if(n==0){
            return 0;
        }
        while(!isPowOfN(num,n)){
            num++;
        }
        return num;
    }

    int rangeMinQuery(int[] segTree,int qlow,int qhigh,int low,int high,int pos){
        //case 2:totally overlap
        if(qlow<=low&&qhigh>=high){
            return segTree[pos];
        }
        //case 3:no overlap
        else if(qlow>high||qhigh<low){
            return Integer.MAX_VALUE;
        }
        //case 1:partially overlap
        else {
            int mid = low + (high - low) / 2;
            //區間減半,pos變成child
            int left = rangeMinQuery(segTree, qlow, qhigh, low, mid, 2 * pos + 1);
            int right = rangeMinQuery(segTree, qlow, qhigh, mid + 1, high, 2 * pos + 2);
            return Math.min(left, right);
        }
    }

    int query(int qlow,int qhigh){
        //注意low與high是相對於input的
        return rangeMinQuery(segTree,qlow,qhigh,0,input.length-1,0);
    }

    public static void main(String[] args){
        int input[] = {0,3,4,2,1,6,-1};
        Solution s = new Solution();
        s.init(input);
        System.out.println(s.query(0,3));
        System.out.println(s.query(1,5));
        System.out.println(s.query(1,6));
        System.out.println(s.query(6,6));
    }

}

3.Largest Rectangle in Histogram

思路一是利用上述的RMQ的segmentTree實現,思路二是Largest Rectangular Area in a Histogram GeeksforGeeks的實現。

關於思路二更多的參考資料:
1.What is the algorithmic approach to find the maximum rectangular area in a histogram?

2.Tushar Roy大神的講解

// public class Solution {

//     //fail 1
//     //timeOut
//     public int largestRectangleArea(int[] heights) {
//         if(heights.length==0){
//             return 0;
//         }
//         init(heights);
//         int max=Integer.MIN_VALUE;
//         for(int i=0;i<heights.length;i++){
//             int area=Integer.MIN_VALUE;
//             for(int j=i;j<heights.length;j++){
//                 area = Math.max(area,query(i,j)*(j-i+1));
//             }
//             max = Math.max(max,area);
//         }
//         return max;
//     }

//     private int[] input;
//     private int[] segTree;
//     void init(int[] input1){
//         input = input1;
//         int n  = input.length;
//         int length;
//         if(isPowOfN(n,2)){
//             length=2*n-1;
//         }else{
//             length = 2*findNextPowOfN(n,2)-1;
//         }
//         segTree = new int[length];
//         //fill max value
//         Arrays.fill(segTree,Integer.MAX_VALUE);
//         //pos 表示tree的根節點
//         constructTree(input,segTree,0,n-1,0);
//     }


//     void constructTree(int[] input,int[] segTree,int low,int high,int pos){
//         if(low==high){
//             segTree[pos] = input[low];
//             return;
//         }
//         int mid = low+(high-low)/2;
//         //left
//         constructTree(input,segTree,low,mid,2*pos+1);
//         //right
//         constructTree(input,segTree,mid+1,high,2*pos+2);
//         segTree[pos] = Math.min(segTree[2*pos+1],segTree[2*pos+2]);
//     }

//     boolean isPowOfN(int num,int n){
//         if(n==0){
//             return false;
//         }
//         while(num%n==0){
//             num = num/n;
//         }
//         return num==1;
//     }

//     int findNextPowOfN(int num,int n){
//         if(n==0){
//             return 0;
//         }
//         while(!isPowOfN(num,n)){
//             num++;
//         }
//         return num;
//     }

//     int rangeMinQuery(int[] segTree,int qlow,int qhigh,int low,int high,int pos){
//         //case 2:totally overlap
//         if(qlow<=low&&qhigh>=high){
//             return segTree[pos];
//         }
//         //case 3:no overlap
//         else if(qlow>high||qhigh<low){
//             return Integer.MAX_VALUE;
//         }
//         //case 1:partially overlap
//         else {
//             int mid = low + (high - low) / 2;
//             //區間減半,pos變成child
//             int left = rangeMinQuery(segTree, qlow, qhigh, low, mid, 2 * pos + 1);
//             int right = rangeMinQuery(segTree, qlow, qhigh, mid + 1, high, 2 * pos + 2);
//             return Math.min(left, right);
//         }
//     }

//     int query(int qlow,int qhigh){
//         //注意low與high是相對於input的
//         return rangeMinQuery(segTree,qlow,qhigh,0,input.length-1,0);
//     }
// }

//success 2
public class Solution {
    public int largestRectangleArea(int[] height) {
        int len = height.length;
        Stack<Integer> s = new Stack<Integer>();
        int maxArea = 0;
        for(int i = 0; i <= len; i++){
            //這裏有個小技巧,就是最後i==len時,
            //h=0是肯定小於height[s.peek()]的,
            //所以最後stack中的元素得以彈出,不然的話
            //就得像success 3那樣多寫幾步實現之。
            int h = (i == len ? 0 : height[i]);
            if(s.isEmpty() || h >= height[s.peek()]){
                s.push(i);
            }else{
                int tp = s.pop();
                maxArea = Math.max(maxArea, height[tp] * (s.isEmpty() ? i : i - 1 - s.peek()));
                i--;
            }
        }
        return maxArea;
    }
}

Tushar Roy的實現(跟success 2思路一樣):

//success 3
//Tushar Roy的實現
public class Solution {
    public int largestRectangleArea(int[] height) {
        int len = height.length;
        Stack<Integer> s = new Stack<Integer>();
        int maxArea = 0,area=0,i;
        for(i = 0; i < len; i++){
            if(s.isEmpty() || height[i] >= height[s.peek()]){
                s.push(i);
            }else{
                int tp = s.pop();
                maxArea = Math.max(maxArea, height[tp] * (s.isEmpty() ? i : i - 1 - s.peek()));
                i--;
            }
        }
        //多寫幾步,將stack清空
        while(!s.isEmpty()){
            int tp = s.pop();
            maxArea = Math.max(maxArea, height[tp] * (s.isEmpty() ? i : i - 1 - s.peek()));
        }
        return maxArea;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章