LeetCode题目练习记录 _栈、队列01 _20211012

LeetCode题目练习记录 _栈、队列01 _20211012

84. 柱状图中最大的矩形

难度困难1581

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。

示例 1:

img

输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10

示例 2:

img

输入: heights = [2,4]
输出: 4

提示:

  • 1 <= heights.length <=105
  • 0 <= heights[i] <= 104

方法一:单调栈

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;
    }
}
func largestRectangleArea(heights []int) int {
    n := len(heights)
    left, right := make([]int, n), make([]int, n)
    mono_stack := []int{}
    for i := 0; i < n; i++ {
        for len(mono_stack) > 0 && heights[mono_stack[len(mono_stack)-1]] >= heights[i] {
            mono_stack = mono_stack[:len(mono_stack)-1]
        }
        if len(mono_stack) == 0 {
            left[i] = -1
        }else{
            left[i] = mono_stack[len(mono_stack)-1]
        }
        mono_stack = append(mono_stack, i)
    }
    mono_stack = []int{}
    for i := n -1; i >= 0; i-- {
        for len(mono_stack) > 0 && heights[mono_stack[len(mono_stack) -1]] >= heights[i] {
            mono_stack = mono_stack[:len(mono_stack) -1]
        }
        if len(mono_stack) == 0 {
            right[i] = n
        }else {
            right[i] = mono_stack[len(mono_stack) -1]
        }
        mono_stack = append(mono_stack, i)
    }
    ans := 0
    for i := 0; i < n; i++ {
        ans = max(ans, (right[i] - left[i] -1) * heights[i])
    }
    return ans
}

func max(x,y int) int {
    if x > y {
        return x
    }
    return y
}

239. 滑动窗口最大值

难度困难1215

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回滑动窗口中的最大值。

示例 1:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

示例 2:

输入:nums = [1], k = 1
输出:[1]

示例 3:

输入:nums = [1,-1], k = 1
输出:[1,-1]

示例 4:

输入:nums = [9,11], k = 2
输出:[11]

示例 5:

输入:nums = [4,-2], k = 2
输出:[4]

提示:

  • 1 <= nums.length <= 105
  • -104 <= nums[i] <= 104
  • 1 <= k <= nums.length

方法一:优先队列

class Solution {
    // Java 方法一:优先队列
    public int[] maxSlidingWindow(int[] nums, int k) {
        int n = nums.length;
        PriorityQueue<int[]> pq = new PriorityQueue<int[]>(new Comparator<int[]>(){
            public int compare(int[] pair1,int[] pair2){
                return pair1[0] != pair2[0] ? pair2[0] - pair1[0] : pair2[1] - pair1[1];
            }
        });
        for (int i = 0; i < k; i++) {
            pq.offer(new int[]{nums[i],i});
        }
        int[] ans = new int[n - k + 1];
        ans[0] = pq.peek()[0];
        for (int i = k; i < n; i++) {
            pq.offer(new int[]{nums[i], i});
            while (pq.peek()[1] <= i -k) {
                pq.poll();
            }
            ans[i - k +1] = pq.peek()[0];
        }
        return ans;
    }
}
// Go 方法一:优先队列
var a []int
type hp struct{ sort.IntSlice }
func (h hp)Less(i, j int)bool { 
    return a[h.IntSlice[i]] > a[h.IntSlice[j]] 
    }
func (h *hp) Push(v interface{}) { 
    h.IntSlice = append(h.IntSlice, v.(int)) 
    }
func (h *hp) Pop() interface{} { 
    a := h.IntSlice
    v := a[len(a)-1] 
    h.IntSlice = a[:len(a)-1]
    return v
    }
func maxSlidingWindow(nums []int, k int) []int {
    a = nums
    q := &hp{make([]int,k)}
    for i := 0; i < k; i++ {
        q.IntSlice[i] = i
    }
    heap.Init(q)

    n := len(nums)
    ans := make([]int, 1, n-k+1)
    ans[0] = nums[q.IntSlice[0]]
    for i := k; i < n; i++ {
        heap.Push(q, i)
        for q.IntSlice[0] <= i-k {
            heap.Pop(q)
        }
        ans = append(ans,nums[q.IntSlice[0]])
    }
    return ans
}

方法二:单调队列

class Solution {
    // Java 方法二:单调队列
    public int[] maxSlidingWindow(int[] nums, int k) {
        int n = nums.length;
        Deque<Integer> deque = new LinkedList<Integer>();
        for (int i = 0; i < k; i++) {
            while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
                deque.pollLast();
            }
            deque.offerLast(i);
        }

        int[] ans = new int[n - k + 1];
        ans[0] = nums[deque.peekFirst()];
        for (int i=k; i < n; i++) {
            while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
                deque.pollLast();
            }
            deque.offerLast(i);
            while (deque.peekFirst() <= i -k) {
                deque.pollFirst();
            }
            ans[i - k + 1] = nums[deque.peekFirst()];
        }
        return ans;
    }
}
func maxSlidingWindow(nums []int, k int) []int {
    // Go 方法二:单调队列
    q := []int{}
    push := func(i int) {
        for len(q) > 0 && nums[i] >= nums[q[len(q) -1]] {
            q = q[:len(q)-1]
        }
        q = append(q,i)
    }

    for i := 0; i < k; i++ {
        push(i)
    }

    n := len(nums)
    ans := make([]int,1,n-k+1)
    ans[0] = nums[q[0]]
    for i := k; i < n; i++ {
        push(i)
        for q[0] <= i-k {
            q = q[1:]
            }
            ans = append(ans, nums[q[0]])
    }
    return ans
}

方法三:分块 + 预处理

class Solution {
    // Java 方法三:分块 + 预处理 —— 最速解
    public int[] maxSlidingWindow(int[] nums, int k) {
        int n = nums.length;
        int[] prefixMax = new int[n];
        int[] suffixMax = new int[n];
        for (int i = 0; i < n; i++) {
            if (i % k == 0) {
                prefixMax[i] = nums[i];
            }else {
                prefixMax[i] = Math.max(prefixMax[i-1],nums[i]);
            }
        }
        for (int i = n-1; i >= 0; i--) {
            if (i == n-1 || (i+1)%k == 0) {
                suffixMax[i] = nums[i];
            }else{
                suffixMax[i] = Math.max(suffixMax[i + 1],nums[i]);
            }
        }
        int[] ans = new int [n - k + 1];
        for (int i = 0; i <= n -k; ++i) {
            ans[i] = Math.max(suffixMax[i], prefixMax[i + k -1]);
        }
        return ans;
    }
}
func maxSlidingWindow(nums []int, k int) []int {
    // Go 方法三:分块 + 预处理
    n := len(nums)
    prefixMax := make([]int, n)
    suffixMax := make([]int, n)
    for i, v := range nums {
        if i % k == 0 {
            prefixMax[i] = v
        }else {
            prefixMax[i] = max(prefixMax[i-1], v)
        }
    }
    for i := n - 1; i >=0; i-- {
        if i == n-1 || (i+1)%k == 0 {
            suffixMax[i] = nums[i]
        } else {
            suffixMax[i] = max(suffixMax[i+1], nums[i])
        }
    }

    ans := make([]int, n-k+1)
    for i := range ans {
        ans[i] = max(suffixMax[i],prefixMax[i+k-1])
    }
    return ans
}

func max(a,b int) int {
    if a > b {
        return a
    }
    return b
}

641. 设计循环双端队列

难度中等98

设计实现双端队列。
你的实现需要支持以下操作:

  • MyCircularDeque(k):构造函数,双端队列的大小为k。
  • insertFront():将一个元素添加到双端队列头部。 如果操作成功返回 true。
  • insertLast():将一个元素添加到双端队列尾部。如果操作成功返回 true。
  • deleteFront():从双端队列头部删除一个元素。 如果操作成功返回 true。
  • deleteLast():从双端队列尾部删除一个元素。如果操作成功返回 true。
  • getFront():从双端队列头部获得一个元素。如果双端队列为空,返回 -1。
  • getRear():获得双端队列的最后一个元素。 如果双端队列为空,返回 -1。
  • isEmpty():检查双端队列是否为空。
  • isFull():检查双端队列是否满了。

示例:

MyCircularDeque circularDeque = new MycircularDeque(3); // 设置容量大小为3
circularDeque.insertLast(1);			        // 返回 true
circularDeque.insertLast(2);			        // 返回 true
circularDeque.insertFront(3);			        // 返回 true
circularDeque.insertFront(4);			        // 已经满了,返回 false
circularDeque.getRear();  				// 返回 2
circularDeque.isFull();				        // 返回 true
circularDeque.deleteLast();			        // 返回 true
circularDeque.insertFront(4);			        // 返回 true
circularDeque.getFront();				// 返回 4
 

提示:

  • 所有值的范围为 [1, 1000]
  • 操作次数的范围为 [1, 1000]
  • 请不要使用内置的双端队列库。
// Java 双向链表实现,一个节点包含前后两个指针
class MyCircularDeque {
    int capacity;
    int curContain = 0;

    class ListNode {
        int val;
        ListNode next;
        ListNode prev;

        public ListNode(int val) {
            this.val = val;
        }
    }

    ListNode start;
    ListNode end;

    public MyCircularDeque(int k) {
        this.capacity = k;
        start = new ListNode(0);
        end = start;
    }
    
    public boolean insertFront(int value) {
        if (isFull()) {
            return false;
        }
        ListNode newNode = new ListNode(value);
        newNode.next = start.next;
        if (newNode.next != null) {
            newNode.next.prev = newNode;
        }else{
            end = newNode;
        }
        newNode.prev = start;
        start.next = newNode;
        curContain += 1;
        return true;
    }
    
    public boolean insertLast(int value) {
        if (isFull()) {
            return false;
        }
        ListNode newNode = new ListNode(value);
        end.next = newNode;
        newNode.prev = end;
        end = end.next;
        curContain += 1;
        return true;
    }
    
    public boolean deleteFront() {
        if (isEmpty()) {
            return false;
        }
        ListNode nextNode = start.next.next;
        start.next.next = null;
        start.next.prev = null;
        start.next = nextNode;
        if (start.next != null) {
            start.next.prev = start;
        }else{
            end = start;
        }
        curContain -= 1;
        return true;
    }
    
    public boolean deleteLast() {
        if (isEmpty()) {
            return false;
        }
        ListNode preNode = end.prev;
        end.prev = null;
        preNode.next = null;
        end = preNode;
        curContain -= 1;
        return true;
    }
    
    public int getFront() {
        if (isEmpty()) {
            return -1;
        }
        return start.next.val;
    }
    
    public int getRear() {
        if (isEmpty()) {
            return -1;
        }
        return end.val;
    }
    
    public boolean isEmpty() {
        return start == end;
    }
    
    public boolean isFull() {
        return capacity == curContain;
    }
}

/**
 * Your MyCircularDeque object will be instantiated and called as such:
 * MyCircularDeque obj = new MyCircularDeque(k);
 * boolean param_1 = obj.insertFront(value);
 * boolean param_2 = obj.insertLast(value);
 * boolean param_3 = obj.deleteFront();
 * boolean param_4 = obj.deleteLast();
 * int param_5 = obj.getFront();
 * int param_6 = obj.getRear();
 * boolean param_7 = obj.isEmpty();
 * boolean param_8 = obj.isFull();
 */
// Java 数组实现
class MyCircularDeque {
    int size = 0;
    int tail = -1;
    int[] queue;

    public MyCircularDeque(int k) {
        size = k;
        queue = new int[k];
    }
    
    public boolean insertFront(int value) {
        if (isFull()) {
            return false;
        }
        for (int i = tail; i >= 0; i--) {
            queue[i + 1] = queue[i];
        }
        queue[0] = value;
        tail++;
        return true;
    }
    
    public boolean insertLast(int value) {
        if (isFull()) {
            return false;
        }
        tail++;
        queue[tail] = value;
        return true;
    }
    
    public boolean deleteFront() {
        if (isEmpty()) {
            return false;
        }
        for (int i=1; i <= tail; i++) {
            queue[i-1] = queue[i];
        }
        queue[tail] = 0;
        tail--;
        return true;
    }
    
    public boolean deleteLast() {
        if (isEmpty()) {
            return false;
        }
        queue[tail] = 0;
        tail--;
        return true;
    }
    
    public int getFront() {
        if (isEmpty()) {
            return -1;
        }
        return queue[0];
    }
    
    public int getRear() {
        if (isEmpty()) {
            return -1;
        }
        return queue[tail];
    }
    
    public boolean isEmpty() {
        return tail == -1;
    }
    
    public boolean isFull() {
        return tail == (size - 1);
    }
}

/**
 * Your MyCircularDeque object will be instantiated and called as such:
 * MyCircularDeque obj = new MyCircularDeque(k);
 * boolean param_1 = obj.insertFront(value);
 * boolean param_2 = obj.insertLast(value);
 * boolean param_3 = obj.deleteFront();
 * boolean param_4 = obj.deleteLast();
 * int param_5 = obj.getFront();
 * int param_6 = obj.getRear();
 * boolean param_7 = obj.isEmpty();
 * boolean param_8 = obj.isFull();
 */
// Go 数组实现
type MyCircularDeque struct {
    queue []int
    head int
    tail int
}

/** Initialize your data structure here. Set the size of the deque to be k. */
func Constructor(k int) MyCircularDeque {
    return MyCircularDeque{
        queue: make([]int, k + 1),
        head: 0,
        tail: 0,
    }
}

/** Adds an item at the front of Deque. Return true if the operation is successful. */
func (this *MyCircularDeque) InsertFront(value int) bool {
    if this.IsFull(){
        return false
    }
    this.head = (this.head - 1 + len(this.queue)) % len(this.queue)
    this.queue[this.head] = value
    return true
}

/** Adds an item at the rear of Deque. Return true if the operation is successful. */
func (this *MyCircularDeque) InsertLast(value int) bool {
    if this.IsFull() {
        return false
    }
    this.queue[this.tail] = value
    this.tail = (this.tail + 1) % len(this.queue)
    return true
}

/** Deletes an item from the front of Deque. Return true if the operation is successful. */
func (this *MyCircularDeque) DeleteFront() bool {
    if this.IsEmpty() {
        return false
    }
    this.head = (this.head + 1) % len(this.queue)
    return true
}

/** Deletes an item from the rear of Deque. Return true if the operation is successful. */
func (this *MyCircularDeque) DeleteLast() bool {
    if this.IsEmpty() {
        return false
    }
    this.tail = (this.tail - 1 + len(this.queue)) % len(this.queue)
    return true
}

/** Get the front item from the deque. */
func (this *MyCircularDeque) GetFront() int {
    if this.IsEmpty() {
        return -1
    }
    return this.queue[this.head]
}

/** Get the last item from the deque. */
func (this *MyCircularDeque) GetRear() int {
    if this.IsEmpty() {
        return -1
    }
    n := (this.tail - 1 + len(this.queue)) % len(this.queue)
    return this.queue[n]
}

/** Checks whether the circular deque is empty or not. */
func (this *MyCircularDeque) IsEmpty() bool {
    return this.head == this.tail
}

/** Checks whether the circular deque is full or not. */
func (this *MyCircularDeque) IsFull() bool {
    return (this.tail + 1) % len(this.queue) == this.head
}


/**
 * Your MyCircularDeque object will be instantiated and called as such:
 * obj := Constructor(k);
 * param_1 := obj.InsertFront(value);
 * param_2 := obj.InsertLast(value);
 * param_3 := obj.DeleteFront();
 * param_4 := obj.DeleteLast();
 * param_5 := obj.GetFront();
 * param_6 := obj.GetRear();
 * param_7 := obj.IsEmpty();
 * param_8 := obj.IsFull();
 */

42. 接雨水

难度困难2756

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

示例 1:

img

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 

示例 2:

输入:height = [4,2,0,3,2,5]
输出:9

提示:

  • n == height.length
  • 1 <= n <= 2 * 104
  • 0 <= height[i] <= 105
class Solution {
    // Java 方法 1:暴力
    public int trap(int[] height) {
        int ans = 0;
        int size = height.length;
        for (int i = 1; i < size - 1; i++) {
            int max_left = 0; 
            int max_right = 0;
            for (int j = i; j >=0 ; j--) {
                max_left = Math.max(max_left,height[j]);
            }
            for (int j = i; j < size; j++) {
                max_right = Math.max(max_right, height[j]);
            }
            ans += Math.min(max_left,max_right) - height[i];
        }
        return ans;
    }
}
// G 方法 1:暴力
func trap(height []int) int {
    ans := 0
    size := len(height)
    for i := 1; i < size; i++ {
        max_left := 0
        max_right := 0
        for j := i; j >= 0; j-- {
            max_left = max(max_left,height[j])
        }
        for j := i ; j < size ; j++ {
            max_right = max(max_right,height[j])
        }
        ans += min(max_left,max_right) - height[i]
    }
    return ans
}
func max(x, y int) int {
    if x > y {
        return x
    }
    return y
}
func min(x, y int) int {
    if x < y {
        return x
    }
    return y
}

方法二:单调栈

class Solution {
    // Java 方法二:单调栈
    public int trap(int[] height) {
        int ans = 0;
        Deque<Integer> stack = new LinkedList<Integer>();
        int n = height.length;
        for (int i = 0; i < n; i++) {
            while (!stack.isEmpty() && height[i] > height[stack.peek()]) {
                int top = stack.pop();
                if (stack.isEmpty()) {
                    break;
                }
                int left = stack.peek();
                int currWidth = i - left - 1;
                int currHeight = Math.min(height[left], height[i]) - height[top];
                ans += currWidth * currHeight;
            }
            stack.push(i);
        }
        return ans;
    }
}
// Go 方法二:单调栈 
// 指明返回的是ans 就可以直接return 不用带参数
func trap(height []int) (ans int) {
    stack := []int{}
    for i, h := range height {
        for len(stack) > 0 && h > height[stack[len(stack) - 1]] {
            top := stack[len(stack) - 1]
            stack = stack[:len(stack) - 1]
            if len(stack) == 0 {
                break
            }
            left := stack[len(stack) - 1]
            curWidth := i - left -1
            curHeight := min(height[left],h) - height[top]
            ans += curWidth * curHeight
        }
        stack = append(stack,i)
    }
    return
}

func min(x, y int) int {
    if x < y {
        return x
    }
    return y
}
class Solution {
    // Java 方法三:双指针
    public int trap(int[] height) {
        int ans = 0;
        int left = 0;
        int right = height.length - 1;
        int leftMax = 0;
        int rightMax = 0;
        while (left < right) {
            leftMax = Math.max(leftMax, height[left]);
            rightMax = Math.max(rightMax, height[right]);
            if (height[left] < height[right]) {
                ans += leftMax - height[left];
                left ++;
            } else {
                ans += rightMax - height[right];
                right --;
            }
        }
        return ans;
    }
}
// Go 方法三:双指针
// 声明返回的是ans 就可以直接return 不用带参数
func trap(height []int) (ans int) {
    left := 0
    right := len(height) -1
    leftMax := 0
    rightMax := 0
    for left < right {
        leftMax = max(leftMax,height[left])
        rightMax = max(rightMax,height[right])
        if (height[left] < height[right]) {
            ans += leftMax - height[left]
            left++
        } else {
            ans += rightMax - height[right]
            right--
        }
    }
    return
}

func max(x, y int) int {
    if x > y {
        return x
    }
    return y
}
原文地址:https://www.cnblogs.com/OwlInTheOaktree/p/15398773.html