今天我们继续来学习栈和队列
目录
150. 逆波兰表达式求值
239. 滑动窗口最大值-->
队列的应用
347.前 K 个高频元素
题目1.150. 逆波兰表达式求值
题目链接/讲解链接:代码随想录
python(双指针)
这是笔者第一个版本,超出了时间限制ww
class Solution(object):
def evalRPN(self, tokens):
res = 0
while tokens != None or len(tokens) != 1:
#快慢指针,每次循环都会重置快慢指针
fast = 1
slow = 0
#快指针向前移动一个单位,如果快指针移动后指向的是数字,慢指针也移动
#如果快指针移动后指向的是运算符,慢指针不动,进行运算后移除数字和符号,然后重置指针
if type(tokens[fast + 1]) == int:
fast += 1
slow += 1
if type(tokens[fast + 1]) != int:
if tokens[fast + 1] == '+':
#加法
temp = tokens[fast] + tokens[slow]
#还要对数字和符号作替换操作,替换成运算后的结果
del tokens[slow + 1 : fast + 1] #end的位置是开区间
tokens[slow] = temp #慢指针的位置替换成temp即可
if tokens[fast + 1] == '-':
temp = tokens[fast] - tokens[slow]
del tokens[slow + 1 : fast + 1]
tokens[slow] = temp
if tokens[fast + 1] == '*':
temp = tokens[fast] * tokens[slow]
del tokens[slow + 1 : fast + 1]
tokens[slow] = temp
if tokens[fast + 1] == '/':
temp = tokens[fast] / tokens[slow]
del tokens[slow + 1 : fast + 1]
tokens[slow] = temp
#当tokens中只有一个元素时,循环结束,直接返回列表中的值即可
return tokens[0]
python(栈)
优化一下代码,使用stack来完成:
class Solution(object):
def evalRPN(self, tokens):
#初始化一个栈
stack = []
operators = {'+', '-', '*', '/'}
for token in tokens:
if token not in operators:
# 如果是数字,则转换为整数(假设所有数字都是整数)
stack.append(int(token))
else:
# 如果是运算符,则从栈中弹出两个数字进行计算
num2 = stack.pop()
num1 = stack.pop()
if token == '+':
stack.append(num1 + num2)
elif token == '-':
stack.append(num1 - num2)
elif token == '*':
stack.append(num1 * num2)
elif token == '/':
# 注意处理除数为0的情况
if num2 == 0:
raise ValueError("除数或被除数不能为0")
stack.append(num1 / num2) # 如果需要整数除法
# 栈中最后剩下的元素就是表达式的结果
return stack[0]
C(栈)
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h> // 用于isdigit函数
#define MAX_SIZE 1000 // 假设输入的tokens数量不会超过这个值
// 函数声明
int evalRPN(char** tokens, int tokensSize);
int main() {
// 示例:tokens数组和大小,注意C语言中字符串是以null结尾的
char* tokens[] = {"2", "1", "+", "3", "*"};
int tokensSize = sizeof(tokens) / sizeof(tokens[0]);
// 调用evalRPN函数并打印结果
int result = evalRPN(tokens, tokensSize);
printf("Result: %d\n", result);
return 0;
}
// 逆波兰表达式求值函数
int evalRPN(char** tokens, int tokensSize) {
int stack[MAX_SIZE]; // 使用静态数组作为栈
int top = -1; // 栈顶指针,初始化为-1表示栈为空
for (int i = 0; i < tokensSize; i++) {
if (isdigit(tokens[i][0])) { // 如果当前token是数字
// 将数字转换为整数并入栈
stack[++top] = atoi(tokens[i]);
} else { // 如果当前token是运算符
int num2 = stack[top--]; // 弹出栈顶元素作为第二个操作数
int num1 = stack[top--]; // 弹出新的栈顶元素作为第一个操作数
switch (tokens[i][0]) { // 根据运算符执行相应的运算
case '+':
stack[++top] = num1 + num2;
break;
case '-':
stack[++top] = num1 - num2;
break;
case '*':
stack[++top] = num1 * num2;
break;
case '/':
if (num2 == 0) {
// 处理除数为0的情况
fprintf(stderr, "Error: Division by zero\n");
exit(EXIT_FAILURE);
}
stack[++top] = num1 / num2;
break;
default:
// 未知运算符
fprintf(stderr, "Error: Unknown operator '%c'\n", tokens[i][0]);
exit(EXIT_FAILURE);
}
}
}
// 栈中最后剩下的元素就是表达式的结果
return stack[top];
}
题目2.239. 滑动窗口最大值
题目链接/讲解链接:代码随想录
python
笔者刚开始没有注意到K的条件限制,无语了~
完成本题有几个注意点(基于个人的错误,第一个是错误版本,第二个是更改版本):
class Solution(object):
def maxSlidingWindow(self, nums, k):
i = 0
#初始化一个队列
Myqueue = []
res = []
while i <= (len(nums) - 3):
for item in nums[i:i + 3]:
#如果队列是空的
if Myqueue == []:
Myqueue.append(item)
if Myqueue[0] < item:
Myqueue.pop()
Myqueue.append(item)
if Myqueue[0] == item or Myqueue[0] > item:
Myqueue.append(item)
res.append(Myqueue.pop())
Myqueue.clear()
return res
char * removeDuplicates(char * s){
//求出字符串长度
int strLength = strlen(s);
//开辟栈空间。栈空间长度应为字符串长度+1(为了存放字符串结束标志'\0')
char* stack = (char*)malloc(sizeof(char) * strLength + 1);
int stackTop = 0;
int index = 0;
//遍历整个字符串
while(index < strLength) {
//取出当前index对应字母,之后index+1
char letter = s[index++];
//若栈中有元素,且栈顶字母等于当前字母(两字母相邻)。将栈顶元素弹出
if(stackTop > 0 && letter == stack[stackTop - 1])
stackTop--;
//否则将字母入栈
else
stack[stackTop++] = letter;
}
//存放字符串结束标志'\0'
stack[stackTop] = '\0';
//返回栈本身作为字符串
return stack;
}
-
滑动窗口的大小是
k
而不是固定的 。 -
队列应该在添加新元素时保持队列的递减顺序(即队列头部始终是窗口中的最大值),并且在添加新元素前应该移除所有小于新元素的元素。
-
每次窗口滑动后,保存队列的头部元素(即当前窗口的最大值)到结果列表中,而不是在每次迭代中都清空队列和添加队列尾部元素到结果列表。
-
循环条件
i <= (len(nums) - 3)
应该是i <= (len(nums) - k)
,以确保处理所有可能的窗口。
题目3.347.前 K 个高频元素
题目链接/讲解链接:代码随想录
优先级队列正式登场!大顶堆、小顶堆该怎么用?| LeetCode:347.前 K 个高频元素_哔哩哔哩_bilibili
这道题目主要涉及到如下三块内容:
- 要统计元素出现频率
- 对频率排序
- 找出前K个高频元素
python
class Solution(object):
def topKFrequent(self, nums, k):
# 使用字典统计数字出现次数
time_dict = defaultdict(int)
for num in nums:
time_dict[num] += 1
# 更改字典,key为出现次数,value为相应的数字的集合
index_dict = defaultdict(list)
for key in time_dict:
index_dict[time_dict[key]].append(key)
# 排序
key = list(index_dict.keys())
key.sort()
result = []
cnt = 0
# 获取前k项
while key and cnt != k:
result += index_dict[key[-1]]
cnt += len(index_dict[key[-1]])
key.pop()
return result[0: k]
C
public int[] TopKFrequent(int[] nums, int k) {
//哈希表-标权重
Dictionary<int,int> dic = new();
for(int i = 0; i < nums.Length; i++){
if(dic.ContainsKey(nums[i])){
dic[nums[i]]++;
}else{
dic.Add(nums[i], 1);
}
}
//优先队列-从小到大排列
PriorityQueue<int,int> pq = new();
foreach(var num in dic){
pq.Enqueue(num.Key, num.Value);
if(pq.Count > k){
pq.Dequeue();
}
}
// //Stack-倒置
// Stack<int> res = new();
// while(pq.Count > 0){
// res.Push(pq.Dequeue());
// }
// return res.ToArray();
//数组倒装
int[] res = new int[k];
for(int i = k - 1; i >= 0; i--){
res[i] = pq.Dequeue();
}
return res;
}
总结:1、在循环中直接修改列表 tokens
的长度和内容,这通常不是处理此类逆波兰表达式求值的问题的最佳方式,因为它可能导致索引错误或不必要的复杂性。
2、待续