//Java class Solution { public boolean isSymmetric(TreeNode root) { return check(root, root); }
public boolean check(TreeNode u, TreeNode v) { Queue<TreeNode> q = new LinkedList<TreeNode>(); q.offer(u); q.offer(v); while (!q.isEmpty()) { u = q.poll(); v = q.poll(); if (u == null && v == null) { continue; } if ((u == null || v == null) || (u.val != v.val)) { return false; }
当我们枚举到第 i 根柱子时,我们的数据结构中存放了 j0,j1,⋯ ,js,如果第 iii 根柱子左侧且最近的小于其高度的柱子为 ji,那么必然有height[j0]<height[j1]<⋯<height[ji]<height[i]≤height[ji+1]<⋯<height[js] 这样我们就可以使用二分查找的方法找到 i 对应的 ji,但真的需要吗?当我们枚举到 i+1 时,原来的 i 也变成了 j值,因此 i 会被放入数据结构。由于所有在数据结构中的 j 值均小于 i,那么所有高度大于等于 height[i] 的 j 都不会作为答案,需要从数据结构中移除。而我们发现,这些被移除的 j 值恰好就是ji+1,⋯ ,js。
这样我们在枚举到第 i 根柱子的时候,就可以先把所有高度大于等于 height[i] 的 j 值全部移除,剩下的 j值中高度最高的即为答案。在这之后,我们将 i 放入数据结构中,开始接下来的枚举。此时,我们需要使用的数据结构也就呼之欲出了,它就是栈。
栈中存放了 j 值。从栈底到栈顶,j 的值严格单调递增,同时对应的高度值也严格单调递增;
当我们枚举到第 i 根柱子时,我们从栈顶不断地移除 height[j]≥height[i] 的 j 值。在移除完毕后,栈顶的 j 值就一定满足 height[j]<height[i],此时 j 就是 i 左侧且最近的小于其高度的柱子。
这里会有一种特殊情况。如果我们移除了栈中所有的 j 值,那就说明 i 左侧所有柱子的高度都大于 height[i],那么我们可以认为 i 左侧且最近的小于其高度的柱子在位置 j=−1,它是一根「虚拟」的、高度无限低的柱子。这样的定义不会对我们的答案产生任何的影响,我们也称这根「虚拟」的柱子为「哨兵」。
//Java class Solution { public int largestRectangleArea(int[] heights) { int n = heights.length; int[] left = new int[n]; int[] right = new int[n]; Deque<Integer> mono_stack = new ArrayDeque<Integer>(); for (int i = 0; i < n; ++i) { while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) { mono_stack.pop(); } left[i] = (mono_stack.isEmpty() ? -1 : mono_stack.peek()); mono_stack.push(i); }
mono_stack.clear(); for (int i = n - 1; i >= 0; --i) { while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) { mono_stack.pop(); } right[i] = (mono_stack.isEmpty() ? n : mono_stack.peek()); mono_stack.push(i); } int ans = 0; for (int i = 0; i < n; ++i) { ans = Math.max(ans, (right[i] - left[i] - 1) * heights[i]); } return ans; } }
答案是可以的。在方法一中,我们在对位置 i 进行入栈操作时,确定了它的左边界。从直觉上来说,与之对应的我们在对位置 i 进行出栈操作时可以确定它的右边界!仔细想一想,这确实是对的。当位置 i 被弹出栈时,说明此时遍历到的位置 i0 的高度小于等于 height[i],并且在 i0 与 i 之间没有其他高度小于等于 height[i] 的柱子。这是因为,如果在 i 和 i0之间还有其它位置的高度小于等于 height[i] 的,那么在遍历到那个位置的时候,i 应该已经被弹出栈了。所以位置 i0 就是位置 i 的右边界。
//Java class Solution { public int largestRectangleArea(int[] heights) { int n = heights.length; int[] left = new int[n]; int[] right = new int[n]; Arrays.fill(right, n); Deque<Integer> mono_stack = new ArrayDeque<Integer>(); for (int i = 0; i < n; ++i) { while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) { right[mono_stack.peek()] = i; mono_stack.pop(); } left[i] = (mono_stack.isEmpty() ? -1 : mono_stack.peek()); mono_stack.push(i); } int ans = 0; for (int i = 0; i < n; ++i) { ans = Math.max(ans, (right[i] - left[i] - 1) * heights[i]); } return ans; } }
//Java class Solution { public int[] dailyTemperatures(int[] temperatures) { int length = temperatures.length; int[] ans = new int[length]; Deque<Integer> stack = new LinkedList<Integer>(); for (int i = 0; i < length; i++) { int temperature = temperatures[i]; while (!stack.isEmpty() && temperature > temperatures[stack.peek()]) { int prevIndex = stack.pop(); ans[prevIndex] = i - prevIndex; } stack.push(i); } return ans; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
//C++ class Solution { public: vector<int> dailyTemperatures(vector<int>& temperatures) { int n = temperatures.size(); vector<int> ans(n); stack<int> s; for (int i = 0; i < n; ++i) { while (!s.empty() && temperatures[i] > temperatures[s.top()]) { int previousIndex = s.top(); ans[previousIndex] = i - previousIndex; s.pop(); } s.push(i); } return ans; } };
复杂度分析
时间复杂度:O(n),其中 n 是温度列表的长度。正向遍历温度列表一遍,对于温度列表中的每个下标,最多有一次进栈和出栈的操作。