给定一个直方图(也称柱状图),假设有人从上面源源不断地倒水,最后直方图能存多少水量?直方图的宽度为 1。
    面试题  17.21 直方图的水量 - 图1
    上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的直方图,在这种情况下,可以接 6 个单位的水(蓝色部分表示水)。 感谢 Marcos 贡献此图。

    示例:**

    1. 输入: [0,1,0,2,1,0,1,3,2,1,2,1]
    2. 输出: 6

    题解1:暴力法
    这次代码算是简洁了,但是复杂度上天。具体来讲就是自底向上每行横着刷一遍,看到可以积水的地方就把空间加上。第一种是一行一行刷,第二种优化了一下,按高度的跨度几行几行刷。

    1. class Solution {
    2. int[] height;
    3. public int trap(int[] ht) {
    4. height = ht.clone();
    5. int n = height.length, counting, capacity=0, tmp;
    6. if(n<3)
    7. return 0;
    8. Arrays.sort(ht);
    9. for(int i=1;i<=ht[n-2];++i){
    10. counting=0;
    11. tmp=0;
    12. for(int j=0;j<n;++j){
    13. if(height[j]>=i){
    14. counting=1;
    15. if(tmp>0){
    16. capacity+=tmp;
    17. tmp=0;
    18. }
    19. }
    20. else if(counting==1){
    21. tmp++;
    22. }
    23. }
    24. }
    25. return capacity;
    26. }
    27. }
    1. class Solution {
    2. int[] height;
    3. public int trap(int[] ht) {
    4. height = ht.clone();
    5. int n = height.length, counting, capacity=0, tmp;
    6. Arrays.sort(ht);
    7. LinkedHashSet<Integer> set=new LinkedHashSet<Integer>();
    8. for (int item:
    9. ht) {
    10. set.add(item);
    11. }
    12. int former = 0;
    13. for (int box:
    14. set) {
    15. if(box==ht[0]){
    16. former=box;
    17. }
    18. else if(box<=ht[n-2]){
    19. counting=0;
    20. tmp=0;
    21. int increase = box-former;
    22. for (int i : height) {
    23. if (i >= box) {
    24. counting = 1;
    25. if (tmp > 0) {
    26. capacity += tmp;
    27. tmp = 0;
    28. }
    29. } else if (counting == 1) {
    30. tmp += increase;
    31. }
    32. }
    33. former = box;
    34. }
    35. }
    36. return capacity;
    37. }
    38. }

    题解2:动态规划
    我觉得不能算是严格意义上的动态规划,只是一种空间换取时间的简单策略,且没有任何代价函数及子问题可言。思路很简单,分别自左自右扫描一遍,看看每一列元素左方与右方的最大值,从而确定该列究竟能装多高的水量。

    1. class Solution {
    2. int[] height;
    3. public int trap(int[] height) {
    4. int n = height.length;
    5. int[] leftHeight = new int[n];
    6. int[] rightHeight = new int[n];
    7. int capacity=0;
    8. int maxHeight = 0;
    9. for(int i=0;i<n;++i){
    10. if(height[i]>maxHeight)
    11. maxHeight = height[i];
    12. leftHeight[i] = maxHeight;
    13. }
    14. maxHeight = 0;
    15. for(int i=n-1;i>=0;--i){
    16. if(height[i]>maxHeight)
    17. maxHeight = height[i];
    18. rightHeight[i] = maxHeight;
    19. }
    20. for(int i=0;i<n;++i){
    21. capacity+=(Math.min(leftHeight[i], rightHeight[i])-height[i]);
    22. }
    23. return capacity;
    24. }
    25. }

    题解3:单调栈

    1. class Solution {
    2. public int trap(int[] height) {
    3. int ans = 0;
    4. Deque<Integer> stack = new LinkedList<Integer>();
    5. int n = height.length;
    6. for (int i = 0; i < n; ++i) {
    7. while (!stack.isEmpty() && height[i] > height[stack.peek()]) {
    8. int top = stack.pop();
    9. if (stack.isEmpty()) {
    10. break;
    11. }
    12. int left = stack.peek();
    13. int currWidth = i - left - 1;
    14. int currHeight = Math.min(height[left], height[i]) - height[top];
    15. ans += currWidth * currHeight;
    16. }
    17. stack.push(i);
    18. }
    19. return ans;
    20. }
    21. }

    题解4:双指针

    1. class Solution {
    2. public int trap(int[] height) {
    3. int ans = 0;
    4. int left = 0, right = height.length - 1;
    5. int leftMax = 0, rightMax = 0;
    6. while (left < right) {
    7. leftMax = Math.max(leftMax, height[left]);
    8. rightMax = Math.max(rightMax, height[right]);
    9. if (height[left] < height[right]) {
    10. ans += leftMax - height[left];
    11. ++left;
    12. } else {
    13. ans += rightMax - height[right];
    14. --right;
    15. }
    16. }
    17. return ans;
    18. }
    19. }