leetcode栈与队列必刷题——用栈实现队列、用队列实现栈、有效的括号、删除字符串中的所有相邻重复项、逆波兰表达式求值、滑动窗口最大值、前 K 个高频元素

用栈实现队列

题目链接

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(pushpoppeekempty):

实现 MyQueue 类:

  • void push(int x) 将元素 x 推到队列的末尾
  • int pop() 从队列的开头移除并返回元素
  • int peek() 返回队列开头的元素
  • boolean empty() 如果队列为空,返回 true ;否则,返回 false

说明:

  • 只能 使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
  • 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。

示例 1:

输入:
["MyQueue", "push", "push", "peek", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 1, 1, false]

解释:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false

提示:

  • 1 <= x <= 9
  • 最多调用 100pushpoppeekempty
  • 假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)

Python:

#本人解法
class MyQueue:
    def __init__(self):
        self.size = 0
        self.queue = []

    def push(self, x: int) -> None:
        self.size += 1
        self.queue.append(x)

    def pop(self) -> int:
        self.size -= 1
        out = self.queue[0]
        self.queue = self.queue[1:]
        return out

    def peek(self) -> int:
        return self.queue[0]

    def empty(self) -> bool:
        if self.size != 0:
            return False
        return True


# Your MyQueue object will be instantiated and called as such:
# obj = MyQueue()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.peek()
# param_4 = obj.empty()
#将一个栈当作输入栈,用于压入 push 传入的数据;另一个栈当作输出栈,用于 pop和 peek操作。每次 pop或peek时,若输出栈为空则将输入栈的全部数据依次弹出并压入输出栈,这样输出栈从栈顶往栈底的顺序就是队列从队首往队尾的顺序。
class MyQueue:
    def __init__(self):
        self.in_queue = []
        self.out_queue = []

    def push(self, x: int) -> None:
        self.in_queue.append(x)

    def in2out(self) -> None:
        while len(self.in_queue):
            self.out_queue.append(self.in_queue[-1])
            self.in_queue = self.in_queue[:-1]

    def pop(self) -> int:
        if not len(self.out_queue):
            self.in2out()
        x = self.out_queue[-1]
        self.out_queue = self.out_queue[:-1]
        return x

    def peek(self) -> int:
        if not len(self.out_queue):
            self.in2out()
        return self.out_queue[-1]

    def empty(self) -> bool:
        if self.in_queue or self.out_queue:
            return False
        return True


# Your MyQueue object will be instantiated and called as such:
# obj = MyQueue()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.peek()
# param_4 = obj.empty()

Go:

//本人解法
type MyQueue struct {
	size  int
	queue []int
}

func Constructor() MyQueue {
	return MyQueue{0, []int{}}
}

func (q *MyQueue) Push(x int) {
	q.size++
	q.queue = append(q.queue, x)
}

func (q *MyQueue) Pop() (out int) {
	q.size--
	out = q.queue[0]
	q.queue = q.queue[1:]
	return
}

func (q *MyQueue) Peek() int {
	return q.queue[0]
}

func (q *MyQueue) Empty() bool {
	if q.size != 0 {
		return false
	}
	return true
}
//双栈法
//将一个栈当作输入栈,用于压入 push 传入的数据;另一个栈当作输出栈,用于 pop和 peek操作。每次 pop或peek时,若输出栈为空则将输入栈的全部数据依次弹出并压入输出栈,这样输出栈从栈顶往栈底的顺序就是队列从队首往队尾的顺序。

type MyQueue struct {
    inStack, outStack []int
}

func Constructor() MyQueue {
    return MyQueue{}
}

func (q *MyQueue) Push(x int) {
    q.inStack = append(q.inStack, x)
}

func (q *MyQueue) in2out() {
    for len(q.inStack) > 0 {
        q.outStack = append(q.outStack, q.inStack[len(q.inStack)-1])
        q.inStack = q.inStack[:len(q.inStack)-1]
    }
}

func (q *MyQueue) Pop() int {
    if len(q.outStack) == 0 {
        q.in2out()
    }
    x := q.outStack[len(q.outStack)-1]
    q.outStack = q.outStack[:len(q.outStack)-1]
    return x
}

func (q *MyQueue) Peek() int {
    if len(q.outStack) == 0 {
        q.in2out()
    }
    return q.outStack[len(q.outStack)-1]
}

func (q *MyQueue) Empty() bool {
    return len(q.inStack) == 0 && len(q.outStack) == 0
}

用队列实现栈

题目链接

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(pushtoppopempty)。

实现 MyStack 类:

  • void push(int x) 将元素 x 压入栈顶。
  • int pop() 移除并返回栈顶元素。
  • int top() 返回栈顶元素。
  • boolean empty() 如果栈是空的,返回 true ;否则,返回 false

注意:

  • 你只能使用队列的基本操作 —— 也就是 push to backpeek/pop from frontsizeis empty 这些操作。
  • 你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。

示例:

输入:
["MyStack", "push", "push", "top", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 2, 2, false]

解释:
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 False

提示:

  • 1 <= x <= 9
  • 最多调用100pushpoptopempty
  • 每次调用 poptop 都保证栈不为空

Python:

class MyStack:
    def __init__(self):
        self.size = 0
        self.stack = []

    def push(self, x: int) -> None:
        self.size += 1
        self.stack.append(x)

    def pop(self) -> int:
        self.size -= 1
        l = len(self.stack)
        out = self.stack[l - 1]
        self.stack = self.stack[: l - 1]
        return out

    def top(self) -> int:
        return self.stack[len(self.stack) - 1]

    def empty(self) -> bool:
        if self.size:
            return False
        return True


# Your MyStack object will be instantiated and called as such:
# obj = MyStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.empty()
class MyStack:

    def __init__(self):
        self.queue1 = []
        self.queue2 = []

    def push(self, x: int) -> None:
        self.queue2.append(x)
        while self.queue1:
            self.queue2.append(self.queue1[0])
            self.queue1 = self.queue1[1:]
        self.queue1, self.queue2 = self.queue2, self.queue1

    def pop(self) -> int:
        v = self.queue1[0]
        self.queue1 = self.queue1[1:]
        return v

    def top(self) -> int:
        return self.queue1[0]

    def empty(self) -> bool:
        return len(self.queue1) == 0


# Your MyStack object will be instantiated and called as such:
# obj = MyStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.empty()
class MyStack:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.queue1 = collections.deque()
        self.queue2 = collections.deque()


    def push(self, x: int) -> None:
        """
        Push element x onto stack.
        """
        self.queue2.append(x)
        while self.queue1:
            self.queue2.append(self.queue1.popleft())
        self.queue1, self.queue2 = self.queue2, self.queue1


    def pop(self) -> int:
        """
        Removes the element on top of the stack and returns that element.
        """
        return self.queue1.popleft()


    def top(self) -> int:
        """
        Get the top element.
        """
        return self.queue1[0]


    def empty(self) -> bool:
        """
        Returns whether the stack is empty.
        """
        return not self.queue1
class MyStack:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.queue = collections.deque()


    def push(self, x: int) -> None:
        """
        Push element x onto stack.
        """
        n = len(self.queue)
        self.queue.append(x)
        for _ in range(n):
            self.queue.append(self.queue.popleft())


    def pop(self) -> int:
        """
        Removes the element on top of the stack and returns that element.
        """
        return self.queue.popleft()


    def top(self) -> int:
        """
        Get the top element.
        """
        return self.queue[0]


    def empty(self) -> bool:
        """
        Returns whether the stack is empty.
        """
        return not self.queue

Go:

type MyStack struct {
	size  int
	stack []int
}

func Constructor() MyStack {
	return MyStack{0, []int{}}
}

func (s *MyStack) Push(x int) {
	s.size++
	s.stack = append(s.stack, x)
}

func (s *MyStack) Pop() int {
	s.size--
	l := len(s.stack)
	out := s.stack[l-1]
	s.stack = s.stack[:l-1]
	return out
}

func (s *MyStack) Top() int {
	return s.stack[len(s.stack)-1]
}

func (s *MyStack) Empty() bool {
	if s.size != 0 {
		return false
	}
	return true
}
type MyStack struct {
    queue1, queue2 []int
}

/** Initialize your data structure here. */
func Constructor() (s MyStack) {
    return
}

/** Push element x onto stack. */
func (s *MyStack) Push(x int) {
    s.queue2 = append(s.queue2, x)
    for len(s.queue1) > 0 {
        s.queue2 = append(s.queue2, s.queue1[0])
        s.queue1 = s.queue1[1:]
    }
    s.queue1, s.queue2 = s.queue2, s.queue1
}

/** Removes the element on top of the stack and returns that element. */
func (s *MyStack) Pop() int {
    v := s.queue1[0]
    s.queue1 = s.queue1[1:]
    return v
}

/** Get the top element. */
func (s *MyStack) Top() int {
    return s.queue1[0]
}

/** Returns whether the stack is empty. */
func (s *MyStack) Empty() bool {
    return len(s.queue1) == 0
}
type MyStack struct {
    queue []int
}

/** Initialize your data structure here. */
func Constructor() (s MyStack) {
    return
}

/** Push element x onto stack. */
func (s *MyStack) Push(x int) {
    n := len(s.queue)
    s.queue = append(s.queue, x)
    for ; n > 0; n-- {
        s.queue = append(s.queue, s.queue[0])
        s.queue = s.queue[1:]
    }
}

/** Removes the element on top of the stack and returns that element. */
func (s *MyStack) Pop() int {
    v := s.queue[0]
    s.queue = s.queue[1:]
    return v
}

/** Get the top element. */
func (s *MyStack) Top() int {
    return s.queue[0]
}

/** Returns whether the stack is empty. */
func (s *MyStack) Empty() bool {
    return len(s.queue) == 0
}

有效的括号

题目链接

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

示例 1:

输入:s = "()"
输出:true

示例 2:

输入:s = "()[]{}"
输出:true

示例 3:

输入:s = "(]"
输出:false

提示:

  • 1 <= s.length <= 10^4
  • s 仅由括号 '()[]{}' 组成

Python:

#本人解法
class Solution:
    def isValid(self, s: str) -> bool:
        if len(s)%2:
            return False
        stack = []
        for v in s:
            if v == ")":
                if stack and stack[-1] == "(":
                    stack.pop()
                else:
                    return False
            elif v == "]":
                if stack and stack[-1] == "[":
                    stack.pop()
                else:
                    return False
            elif v == "}":
                if stack and stack[-1] == "{":
                    stack.pop()
                else:
                    return False
            else:
                stack.append(v)
        return not stack


class Solution:
    def isValid(self, s: str) -> bool:
        if len(s) % 2 == 1:
            return False  
        pairs = {
            ")": "(",
            "]": "[",
            "}": "{",
        }
        stack = list()
        for ch in s:
            if ch in pairs:
                if not stack or stack[-1] != pairs[ch]:
                    return False
                stack.pop()
            else:
                stack.append(ch)     
        return not stack

Go:

//本人解法,注意到有效字符串的长度一定为偶数,因此如果字符串的长度为奇数,我们可以直接返回False,省去后续的遍历判断过程。
func isValid(s string) bool {
	if len(s)%2 != 0 {
		return false
	}
	stack := make([]rune, 0)
	for _, v := range s {
		switch v {
		case ')':
			l := len(stack)
			if l != 0 && stack[l-1] == '(' {
				stack = stack[:l-1]
			} else {
				return false
			}
		case ']':
			l := len(stack)
			if l != 0 && stack[l-1] == '[' {
				stack = stack[:l-1]
			} else {
				return false
			}
		case '}':
			l := len(stack)
			if l != 0 && stack[l-1] == '{' {
				stack = stack[:l-1]
			} else {
				return false
			}
		default:
			stack = append(stack, v)
		}
	}
	return len(stack) == 0
}
//简洁版
func isValid(s string) bool {
    n := len(s)
    if n % 2 == 1 {
        return false
    }
    pairs := map[byte]byte{
        ')': '(',
        ']': '[',
        '}': '{',
    }
	var stack []byte
    for i := 0; i < n; i++ {
        if pairs[s[i]] > 0 {
            if len(stack) == 0 || stack[len(stack)-1] != pairs[s[i]] {
                return false
            }
            stack = stack[:len(stack)-1]
        } else {
            stack = append(stack, s[i])
        }
    }
    return len(stack) == 0
}

删除字符串中的所有相邻重复项

题目链接

给出由小写字母组成的字符串 S重复项删除操作会选择两个相邻且相同的字母,并删除它们。

在 S 上反复执行重复项删除操作,直到无法继续删除。

在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

示例:

输入:"abbaca"
输出:"ca"
解释:
例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。

提示:

  1. 1 <= S.length <= 20000
  2. S 仅由小写英文字母组成。

Python:

#栈
class Solution:
    def removeDuplicates(self, s: str) -> str:
        stk = list()
        for ch in s:
            if stk and stk[-1] == ch:
                stk.pop()
            else:
                stk.append(ch)
        return "".join(stk)
#双指针
class Solution:
    def removeDuplicates(self, s: str) -> str:
        b = list(s)
        slow = fast = 0
        l = len(b)
        while fast < l:
            b[slow] = b[fast]
            if slow and b[slow] == b[slow - 1]:
                slow -= 1
            else:
                slow += 1
            fast += 1
        return "".join(b[:slow])

Go:

//双指针
func removeDuplicates(s string) string {
	b := []byte(s)
	slow, fast := 0, 0
	l := len(s)
	for fast < l {
		b[slow] = b[fast]
		if slow > 0 && b[slow] == b[slow-1] {
			slow--
		} else {
			slow++
		}
		fast++
	}
	return string(b[0:slow])
}
//本人解法
func removeDuplicates(s string) string {
	var stack []rune
outer:
	for _, v := range s {
		l := len(stack)
		for l != 0 && stack[l-1] == v {
			stack = stack[:l-1]
			continue outer
		}
		stack = append(stack, v)
	}
	return string(stack)
}
func removeDuplicates(s string) string {
    stack := []byte{}
    for i := range s {
        if len(stack) > 0 && stack[len(stack)-1] == s[i] {
            stack = stack[:len(stack)-1]
        } else {
            stack = append(stack, s[i])
        }
    }
    return string(stack)
}
//自己写的一种很傻的解法
func removeDuplicates(s string) string {
	if len(s) >= 2 {
		b := []byte(s)
		for {
			f := false
			for i := 0; i < len(b)-1; i++ {
				if b[i] == b[i+1] {
					b = append(b[:i], b[i+2:]...)
					f = true
				}
			}
			if f == false {
				break
			}
		}
		return string(b)
	}
	return s
}

逆波兰表达式求值

题目链接

给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。

请你计算该表达式。返回一个表示表达式值的整数。

注意:

  • 有效的算符为 '+''-''*''/'
  • 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
  • 两个整数之间的除法总是 向零截断
  • 表达式中不含除零运算。
  • 输入是一个根据逆波兰表示法表示的算术表达式。
  • 答案及所有中间计算结果可以用 32 位 整数表示。

示例 1:

输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9

示例 2:

输入:tokens = ["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6

示例 3:

输入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
输出:22
解释:该算式转化为常见的中缀算术表达式为:
  ((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

提示:

  • 1 <= tokens.length <= 104
  • tokens[i] 是一个算符("+""-""*""/"),或是在范围 [-200, 200] 内的一个整数

逆波兰表达式:

逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。

  • 平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 )
  • 该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * )

逆波兰表达式主要有以下两个优点:

  • 去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
  • 适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中

Python:

class Solution:
    def evalRPN(self, tokens: List[str]) -> int:
        stack = []
        for token in tokens:
            if token in {"+", "-", "*", "/"}:
                operand2 = stack.pop()
                operand1 = stack.pop()
                if token == "+":
                    result = operand1 + operand2
                elif token == "-":
                    result = operand1 - operand2
                elif token == "*":
                    result = operand1 * operand2
                else:
                    result = int(operand1 / operand2)
                stack.append(result)
            else:
                stack.append(int(token))
        return stack[0]
class Solution:
    def evalRPN(self, tokens: List[str]) -> int:
        stack = []
        operators = {
            "+": lambda x, y: x + y,
            "-": lambda x, y: x - y,
            "*": lambda x, y: x * y,
            "/": lambda x, y: int(x / y),
        }
        for token in tokens:
            if token in operators:
                operand2 = stack.pop()
                operand1 = stack.pop()
                operation = operators[token]
                result = operation(operand1, operand2)
                stack.append(result)
            else:
                stack.append(int(token))
        return stack[0]
class Solution:
    def evalRPN(self, tokens: List[str]) -> int:
        stack = [0] * ((len(tokens) + 1) // 2)
        index = -1
        operators = {
            "+": lambda x, y: x + y,
            "-": lambda x, y: x - y,
            "*": lambda x, y: x * y,
            "/": lambda x, y: int(x / y),
        }
        for token in tokens:
            try:
                val = int(token)
                index += 1
                stack[index] = val
            except ValueError:
                index -= 1
                stack[index] = operators[token](stack[index], stack[index + 1])
        return stack[0]

Go:

func evalRPN(tokens []string) int {
	var stack []int
	for _, v := range tokens {
		switch v {
		case "+":
			stack = append(stack[:len(stack)-2], stack[len(stack)-2]+stack[len(stack)-1])
		case "-":
			stack = append(stack[:len(stack)-2], stack[len(stack)-2]-stack[len(stack)-1])
		case "*":
			stack = append(stack[:len(stack)-2], stack[len(stack)-2]*stack[len(stack)-1])
		case "/":
			stack = append(stack[:len(stack)-2], stack[len(stack)-2]/stack[len(stack)-1])
		default:
			num, _ := strconv.Atoi(v)
			stack = append(stack, num)
		}
	}
	return stack[0]
}
func evalRPN(tokens []string) int {
	var stack []int
	operators := map[string]func(int, int) int{
		"+": func(x, y int) int { return x + y },
		"-": func(x, y int) int { return x - y },
		"*": func(x, y int) int { return x * y },
		"/": func(x, y int) int { return x / y },
	}

	for _, token := range tokens {
		if operator, ok := operators[token]; ok {
			operand2 := stack[len(stack)-1]
			operand1 := stack[len(stack)-2]
			stack = stack[:len(stack)-2]
			result := operator(operand1, operand2)
			stack = append(stack, result)
		} else {
			num, _ := strconv.Atoi(token)
			stack = append(stack, num)
		}
	}
	return stack[0]
}
func evalRPN(tokens []string) int {
	stack := make([]int, (len(tokens)+1)/2)
	index := -1

	operators := map[string]func(int, int) int{
		"+": func(x, y int) int { return x + y },
		"-": func(x, y int) int { return x - y },
		"*": func(x, y int) int { return x * y },
		"/": func(x, y int) int { return x / y },
	}

	for _, token := range tokens {
		if val, err := strconv.Atoi(token); err == nil {
			index++
			stack[index] = val
		} else {
			index--
			stack[index] = operators[token](stack[index], stack[index+1])
		}
	}

	return stack[0]
}

滑动窗口最大值

给你一个整数数组 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]

提示:

  • 1 <= nums.length <= 10^5
  • -10^4 <= nums[i] <= 10^4
  • 1 <= k <= nums.length

这道题难就难在使用暴力法会超出时间限制。

Python:

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        n = len(nums)
        # 注意 Python 默认的优先队列是小根堆
        q = [(-nums[i], i) for i in range(k)]
        heapq.heapify(q)

        ans = [-q[0][0]]
        for i in range(k, n):
            heapq.heappush(q, (-nums[i], i))
            while q[0][1] <= i - k:
                heapq.heappop(q)
            ans.append(-q[0][0])
        
        return ans
#单调队列,使用python的deque
class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        n = len(nums)  # 获取输入数组长度
        q = collections.deque()  # 双端队列存放数组索引

        # 处理前k个元素,维护一个递减队列
        for i in range(k):
            while q and nums[i] >= nums[q[-1]]:
                q.pop()  # 如果新来的数比队尾元素要大,则队尾元素出队
            q.append(i)  # 默认新来的数入队

        ans = [nums[q[0]]]  # 把队首元素加入结果中,因为它是当前最大的元素

        for i in range(k, n):  # 对数组剩余元素执行上述操作
            while q and nums[i] >= nums[q[-1]]:
                q.pop()
            q.append(i)
            while q[0] <= i - k:  # 如果队首元素不在滑动窗口范围内,要移除
                q.popleft()
            ans.append(nums[q[0]])  # 把队首元素加入结果

        return ans  # 返回结果
#单调队列
class Solution:
    # 定义最大滑动窗口函数
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        # 初始化一个空队列
        q = []

        # 定义一个用于入队操作的函数,保证队列头部始终是当前窗口的最大值
        def push(i: int) -> None:
            # 使用nonlocal关键字指定q是外部函数的局部变量
            nonlocal q
            # 在当前数入队前,将队列中所有小于当前数的数弹出,确保队列头部元素始终为最大值
            while q and nums[i] >= nums[q[-1]]:
                q = q[:-1]
            # 将当前索引压入队列
            q.append(i)

        # 首次形成窗口
        for i in range(k):
            push(i)
        # 取首个窗口的最大值
        ans = [nums[q[0]]]
        
        # 滑动窗口并更新结果
        for i in range(k, len(nums)):
            push(i)
            # 如果队列头部的值已经离开了窗口,那么将其从队列中移除
            while q[0] <= i - k:
                q = q[1:]
            # 将当前窗口最大值放入结果数组
            ans.append(nums[q[0]])
        
        # 返回结果数组
        return ans
class MyQueue:
    def __init__(self):
        self.queue = deque()  # 使用deque实现单调队列

    def pop(self, value):
        """
        每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。
        同时pop之前判断队列当前是否为空。
        """
        if self.queue and value == self.queue[0]:
            self.queue.popleft()

    def push(self, value):
        """
        如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值为止。
        这样就保持了队列里的数值是单调从大到小的了。
        """
        while self.queue and value > self.queue[-1]:
            self.queue.pop()
        self.queue.append(value)

    def front(self):
        """查询当前队列里的最大值,直接返回队列前端也就是front就可以了。"""
        return self.queue[0]

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        que = MyQueue()
        result = []

        for i in range(k):  # 先将前k的元素放进队列
            que.push(nums[i])
        result.append(que.front())  # result 记录前k的元素的最大值

        for i in range(k, len(nums)):
            que.pop(nums[i - k])  # 滑动窗口移除最前面元素
            que.push(nums[i])  # 滑动窗口前加入最后面的元素
            result.append(que.front())  # 记录对应的最大值

        return result

Go:

//优先队列
// 定义全局可变的整数切片 a
var a []int

// 创建一个结构体 hp,该结构体包含 sort 接口的 IntSlice
type hp struct{ sort.IntSlice }

// 重写 Less 方法,使其实现堆的从大到小排列
// 如果 h.IntSlice[i] 的值小于 h.IntSlice[j] 的值,那么返回true
func (h hp) Less(i, j int) bool  { return a[h.IntSlice[i]] > a[h.IntSlice[j]] }

// Push 方法用于添加元素到堆中
func (h *hp) Push(v interface{}) { h.IntSlice = append(h.IntSlice, v.(int)) }

// Pop 方法用于从堆中移除元素
func (h *hp) Pop() interface{} { 
    a := h.IntSlice
    v := a[len(a)-1] 
    h.IntSlice = a[:len(a)-1]
    return v 
}

// maxSlidingWindow 是滑动窗口最大值的具体实现
func maxSlidingWindow(nums []int, k int) []int {
    // 赋值 a
    a = nums
    
    // 创建 hp 结构体实例
    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
}
//单调队列
func maxSlidingWindow(nums []int, k int) []int {
	var q []int // 使用一个队列q来存储元素的索引

	// 定义一个局部函数push,它的功能是向队列中添加元素
	push := func(i int) {
		// 如果队列不为空,且当前元素值大于等于队列尾部元素值
		for len(q) > 0 && nums[i] >= nums[q[len(q)-1]] {
			// 队列尾部元素出队
			q = q[:len(q)-1]
		}
		// 将当前元素的索引入队
		q = append(q, i)
	}

	// 先将前k个元素做处理
	for i := 0; i < k; i++ {
		push(i)
	}
	n := len(nums)

	// 初始化并设置返回结果数组的第一个元素
	ans := make([]int, 1, n-k+1)
	ans[0] = nums[q[0]]

	// 从k开始遍历,对于每个元素,都将其添加到队列中
	// 并保证队列的头部元素总是当前窗口的最大值
	for i := k; i < n; i++ {
		push(i)
		// 如果队列中的头部元素不在当前窗口中,就将其出队
		for q[0] <= i-k {
			q = q[1:]
		}
		// 把当前窗口的最大值(队列q的头部元素对应的值)添加到结果数组中
		ans = append(ans, nums[q[0]])
	}
	return ans
}
// MyQueue 结构体定义了一个单调队列解决方案
type MyQueue struct {
    queue []int  // 队列中的元素
}

// MyQueue的构造函数
func NewMyQueue() *MyQueue {
    return &MyQueue{
        queue: make([]int, 0),
    }
}

// Front 返回队列的第一个元素
func (m *MyQueue) Front() int {
    return m.queue[0]
}

// Back 返回队列的最后一个元素
func (m *MyQueue) Back() int {
    return m.queue[len(m.queue)-1]
}

// Empty 检查队列是否为空
func (m *MyQueue) Empty() bool {
    return len(m.queue) == 0
}

// Push 在队列后面添加一个元素
// 如果新元素比队列末尾的元素大,则移除队列末尾的元素
func (m *MyQueue) Push(val int) {
    for !m.Empty() && val > m.Back() {
        m.queue = m.queue[:len(m.queue)-1]
    }
    m.queue = append(m.queue, val)
}

// Pop 从队列前面移除一个元素
func (m *MyQueue) Pop(val int) {
    if !m.Empty() && val == m.Front() {
        m.queue = m.queue[1:]
    }
}

// maxSlidingWindow 返回每个子数组(大小为k)的最大值
func maxSlidingWindow(nums []int, k int) []int {
    queue := NewMyQueue()  // 初始化队列
    length := len(nums)
    res := make([]int, 0)  // 存储结果

    // 将前k个元素放入队列并记录最大值
    for i := 0; i < k; i++ {
        queue.Push(nums[i])
    }
    res = append(res, queue.Front())

    // 使窗口滑过数组的其余部分
    for i := k; i < length; i++ {
        queue.Pop(nums[i-k])  // 移除窗口外的元素
        queue.Push(nums[i])   // 添加进窗口的新元素
        res = append(res, queue.Front())  // 记录最大值
    }
    return res
}

前 K 个高频元素

题目链接

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

示例 1:

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

示例 2:

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

提示:

  • 1 <= nums.length <= 105
  • k 的取值范围是 [1, 数组中不相同的元素的个数]
  • 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的

Python:

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        m = defaultdict(int)
        for i in nums:
            m[i] += 1
        return sorted(m, key=lambda x: m[x], reverse=True)[:k]
class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        m = defaultdict(int)
        for i in nums:
            m[i] += 1
        top = sorted(m.values(), reverse=True)[:k]
        return [k for k in m if m[k] in top]
class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        # 统计每个数字出现的频次
        occurrences = {}
        for num in nums:
            occurrences[num] = occurrences.get(num, 0) + 1

        # 建立最小堆
        min_heap = []
        for key, value in occurrences.items():
            heapq.heappush(min_heap, (value, key))
            if len(min_heap) > k:
                heapq.heappop(min_heap)

        # 将堆中的元素取出,放入返回结果
        result = [heapq.heappop(min_heap)[1] for _ in range(k)]

        # 返回最频繁的k个元素
        return result

Go:

//本人解法,简单暴力
func topKFrequent(nums []int, k int) []int {
	m := make(map[int]int)
	for _, v := range nums {
		m[v]++
	}
	keys := make([]int, 0, len(m))
	for key := range m {
		keys = append(keys, key)
	}
	sort.Slice(keys, func(i, j int) bool {
		return m[keys[i]] > m[keys[j]]
	})
	return keys[:k]
}
//优先级队列,小顶堆
func topKFrequent(nums []int, k int) []int {
    // 统计每个数字出现的频次
    occurrences := map[int]int{}
    for _, num := range nums {
        occurrences[num]++
    }

    // 建立堆
    h := &IHeap{}
    heap.Init(h)

    // 遍历频次表,将数字和对应的频次压入堆。当堆的元素数量大于k时,弹出堆顶
    for key, value := range occurrences {
        heap.Push(h, [2]int{key, value})
        if h.Len() > k {
            heap.Pop(h)
        }
    }

    // 将堆中的元素取出,放入返回结果
    ret := make([]int, k)
    for i := 0; i < k; i++ {
        ret[k - i - 1] = heap.Pop(h).([2]int)[0]
    }

    // 返回最频繁的k个元素
    return ret
}

// 定义堆
type IHeap [][2]int

// Len 方法返回堆的长度
func (h IHeap) Len() int           { return len(h) }

// Less 方法实现堆中元素的比较规则
func (h IHeap) Less(i, j int) bool { return h[i][1] < h[j][1] }

// Swap 方法实现堆中元素的交换
func (h IHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }

// Push 方法实现元素的入堆操作
func (h *IHeap) Push(x interface{}) {
    *h = append(*h, x.([2]int))
}

// Pop 方法实现元素的出堆操作
func (h *IHeap) Pop() interface{} {
    old := *h
    n := len(old)
    x := old[n-1]
    *h = old[0 : n-1]
    return x
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王翊珩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值