代码随想录算法训练营第十一天|栈与队列

39 阅读2分钟

LeetCode 150.逆波兰表达式求值

📖 考察点

栈的应用

📖 题意理解

💡 解题思路

数字入栈,字符出栈两个,结果入栈

🔑 关键点总结

💻 代码实现

JavaScript

var evalRPN = function (tokens) {
	let stack = [];
	for (let char of tokens) {
		switch (char) {
			case "+": {
				let right = +stack.pop();
				let left = +stack.pop();
				stack.push(left + right);
				break;
			}
			case "-": {
				let right = +stack.pop();
				let left = +stack.pop();
				stack.push(left - right);
				break;
			}
			case "/": {
				let right = +stack.pop();
				let left = +stack.pop();
				stack.push(~~(left / right));
				break;
			}
			case "*": {
				let right = +stack.pop();
				let left = +stack.pop();
				stack.push(left * right);
				break;
			}
			default: {
				stack.push(char);
				break;
			}
		}
	}
	return +stack.pop();
};

Rust


⏱️ 复杂度分析

📚 总结与反思


LeetCode 239. 滑动窗口最大值

📖 考察点

自定义单调队列

📖 题意理解

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

返回 滑动窗口中的最大值

💡 解题思路

思路一:

思路二:

🔑 关键点总结

💻 代码实现

JavaScript

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number[]}
 */
var maxSlidingWindow = function (nums, k) {
	const deQueue = [];
	const pop = (num) => {
		if (deQueue.length && deQueue[0] === num) {
			deQueue.shift();
		}
	};
	const push = (num) => {
		while (num >= deQueue[deQueue.length - 1]) {
			deQueue.pop();
		}
		deQueue.push(num);
	};
	const getMaxValue = (num) => {
		return deQueue[0];
	};
	let res = [];
	for (let i = 0; i < nums.length; i++) {
		push(nums[i]);
		if (i >= k - 1) {
			pop(nums[i - k]);
			res.push(getMaxValue());
		}
	}
	return res;
};

Rust


⏱️ 复杂度分析

📚 总结与反思


LeetCode 347.前 K 个高频元素

📖 考察点

哈希表 堆的应用 堆的实现

📖 题意理解

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

💡 解题思路

维护一个k个元素的小顶堆,超过k个元素弹出栈顶元素。

思路一:

思路二:

🔑 关键点总结

💻 代码实现

JavaScript

class MyHeap {
	constructor(compareFn) {
		this.heap = [];
		this.compareFn = compareFn;
	}
	get size() {
		return this.heap.length;
	}
	compare(left, right) {
		return this.compareFn(this.heap[left], this.heap[right]);
	}
	swap(i, j) {
		let temp = this.heap[i];
		this.heap[i] = this.heap[j];
		this.heap[j] = temp;
	}
	getParentIndex = (index) => {
		return ~~((index - 1) / 2);
	};
	getLeftChildIndex = (index) => {
		return index * 2 + 1;
	};

	insert(value) {
		this.heap.push(value);
		this.heapifyUp(this.heap.length - 1);
	}
	extractTop() {
		if (this.heap.length === 0) {
			return null;
		}
		const max = this.heap[0];
		const last = this.heap.pop();
		if (this.size > 0) {
			this.heap[0] = last;
			this.heapifyDown();
		}
		return max;
	}
	heapifyUp(index) {
		let parentIndex = this.getParentIndex(index);
		while (index > 0 && this.compare(parentIndex, index) > 0) {
			this.swap(index, parentIndex);
			index = parentIndex;
			parentIndex = this.getParentIndex(index);
		}
	}
	heapifyDown() {
		let current = 0;
		do {
			let next = current;
			let leftChildIndex = this.getLeftChildIndex(current);
			let rightChildIndex = leftChildIndex + 1;
			if (leftChildIndex < this.size && this.compare(next, leftChildIndex) > 0) {
				next = leftChildIndex;
			}
			if (rightChildIndex < this.size && this.compare(next, rightChildIndex) > 0) {
				next = rightChildIndex;
			}
			if (next !== current) {
				this.swap(next, current);
				current = next;
			} else {
				break;
			}
		} while (true);
	}
}
/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number[]}
 */
var topKFrequent = function (nums, k) {
	const map = new Map();
	const heap = new MyHeap((a, b) => a[1] - b[1]);
	for (let num of nums) {
		let count = map.get(num);
		if (count) {
			map.set(num, count + 1);
		} else {
			map.set(num, 1);
		}
	}
	for (let record of map.entries()) {
		heap.insert(record);
		if (heap.size > k) {
			heap.extractTop();
		}
	}
	return heap.heap.map((record) => record[0]);
};

Rust


⏱️ 复杂度分析

📚 总结与反思

执行交换的条件应保持一致,注意处理index的范围。超过范围的不可以执行。向下堆化时,左右都应该执行一次compare