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 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//错误思路:只考虑左上角
                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(target0][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=0&&column0].length){
            int compareValue = matrix[row][column];
            if(target==compareValue){
                return true;
            }else if(target//排除列
                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 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 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 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 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 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; ifor (int j=0; j1+ 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(iif(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
//         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]if(dp[j].symbol!=1){
                        if(dp[i].valuevalue+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].valuevalue+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]
    //             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 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 preorderTraversal(TreeNode root) {
        List 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 preorderTraversal(TreeNode root) {
        List list = new ArrayList<>();
        //Stack is a subclass of Vector that implements a standard last-in, first-out stack.
        Stack 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||qhighreturn 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
//             int area=Integer.MIN_VALUE;
//             for(int j=i;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
//             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 s = new Stack();
        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 s = new Stack();
        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;
    }
}

你可能感兴趣的:(LeetCode)