文章目录
1. 剑指 Offer 30. 包含 min 函数的栈
/*
1. 栈每次压入一个元素,最小值都可能改变。(同时对弹出元素来说也是这样的)
2. 所以对栈执行操作时,同时维护一个存储最小值的栈。
3. 每次对栈进行操作时,得到当前栈中所有元素的最小值存入栈中。
*/
/**
* initialize your data structure here.
*/
var MinStack = function() {
this.x_stack = [];
this.min_stack = [Infinity];
}
/**
* @param {number} x
* @return {void}
*/
MinStack.prototype.push = function(x) {
this.x_stack.push(x);
this.min_stack.push(Math.min(this.min_stack[this.min_stack.length-1],x));
}
/**
* @return {void}
*/
MinStack.prototype.pop = function() {
this.x_stack.pop();
this.min_stack.pop();
}
/**
* @return {number}
*/
MinStack.prototype.top = function() {
return this.x_stack[this.x_stack.length-1];
}
/**
* @return {number}
*/
MinStack.prototype.min = function() {
return this.min_stack[this.min_stack.length-1];
}
2. 剑指 Offer 35. 复杂链表的复制
map
/*
1. 对于普通链表的复制,顺序遍历创建新的节点就可以
2. 复杂链表的复制问题在于有随机指针。
3. 随机指针会指向任意节点,但这个节点有可能还没复制到,还不存在。
4. 两次遍历来复制。
*/
/**
* @param {Node} head
* @return {Node}
*/
var copyRandomList = function (head) {
if (!head) return head;
let node = head;
// 把所有节点都存到map中
let map = new Map();
while (node) {
map.set(node, new Node(node.val));
node = node.next;
}
// 再将当前指针指向头节点
node = head;
// 从头节点再开始遍历,更改map中对应节点的next指针和random指针
while (node) {
map.get(node).next = map.get(node.next) || null;
map.get(node).random = map.get(node.random);
node = node.next;
}
// 返回map中的头节点,也就是复制后的头节点
return map.get(head);
}
3. 剑指 Offer 58 - II. 左旋转字符串
substring 字符串截取
/**
* @param {string} s
* @param {number} n
* @return {string}
*/
// substring
var reverseLeftWords = function(s, n) {
return s.substring(n,s.length) + s.substring(0,n);
}
// 数组
var reverseLeftWords = function(s, n) {
const res = [];
for(let i=n; i<s.length; i++) res.push(s[i]);
for(let i=0; i<n; i++) res.push(s[i]);
return res.join("");
}
4. 剑指 Offer 59 - I. 滑动窗口的最大值
原题链接
双向队列(队尾插入,队尾弹出,队首弹出)
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
var maxSlidingWindow = function(nums, k) {
const n = nums.length;
const q = []; // 队列存储最大值的下标
const ans = [];
if(n===0) return ans;
// 第一个滑动窗口
for(let i=0; i<k; i++){
while(q.length && nums[i] >= nums[q[q.length-1]]){
q.pop();
}
q.push(i)
}
ans.push(nums[q[0]]);
for(let i=k; i<n; i++){
while(q.length && nums[i] >= nums[q[q.length-1]]){
q.pop();
}
q.push(i);
// q中所存的下标是否还在滑动窗口中
while(q[0] <= i-k){
q.shift();
}
ans.push(nums[q[0]]);
}
return ans;
}
5. 剑指 Offer 59 - II. 队列的最大值
原题链接
双向队列
/*
1. 需要维护一个存储最大值的队列。
2. 与 剑指Offer30 不同。队列为先入先出的数据结构。
如果队首元素是最大值,出队后,无法确定哪一个是次最大值。
3. 存储最大值的队列 应始终是一个单调递减的队列。
4. 每次入队时将maxQueue中小于当前值的元素全部弹出。
5. 出队时如果最大值与出队元素相等,maxQueue也弹出队首元素。
*/
var MaxQueue = function() {
this.queue = [];
this.maxQueue = [];
}
/**
* @return {number}
*/
MaxQueue.prototype.max_value = function() {
return this.maxQueue.length ? this.maxQueue[0] : -1;
}
/**
* @param {number} value
* @return {void}
*/
MaxQueue.prototype.push_back = function(value) {
this.queue.push(value); // 正常入队
// 执行入队 push_back() 时: 若入队一个比队列某些元素更大的数字 x
// 则为了保持此列表递减,需要将双向队列 尾部所有小于 x 的元素 弹出。
while(this.maxQueue.length && this.maxQueue[this.maxQueue.length - 1] < value) {
this.maxQueue.pop();
}
this.maxQueue.push(value);
}
/**
* @return {number}
*/
MaxQueue.prototype.pop_front = function() {
if(!this.queue.length) return -1;
const val = this.queue.shift(); // 正常弹出
// 执行出队 pop_front() 时: 若出队的元素是最大元素
// 则 双向队列 需要同时 将首元素出队 ,以保持队列和双向队列的元素一致性。
if(this.maxQueue[0] === val) {
this.maxQueue.shift();
}
return val;
}