队列
239. 滑动窗口最大值:给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值 。
思路:这道题你可能想使用暴力求解或者动态规划,但这是一道典型的双端队列的应用场景。回顾一下双端队列的定义:双端队列(deque,全名double-ended queue)是一种具有队列和栈性质的抽象数据类型。双端队列中的元素可以从两端弹出,插入和删除操作限定在队列的两边进行。
我们可以声明一个双端队列,我们从尾部添加元素,但是在添加元素的时候要保证队列尾部的元素比它大,如果比它小则删除元素,这个过程可能要删除多个尾部元素,因为队列中的数据可能有多个比插入元素小的元素,等滑动窗口内的元素都插入完成,双端队列的第一个元素就是最大值,当然我们还要判断队头的元素是不是还在滑动窗口的范围内,如果不在则从队头移除出去,继续判断下一个队头元素。
根据这个思路来实现代码:
function maxSlidingWindow(nums: number[], k: number): number[] {
// 声明双端队列 为了方便判断队头是否超出边界 队列中我们存储元素的索引
const deque: number[] = [];
let res: number[] = [],
len = nums.length
for (let i = 0; i < len; i++) {
// 从队尾取出所有小于当前元素的索引
while (
deque.length &&
nums[deque[deque.length - 1]] <= nums[i]
) {
deque.pop();
}
// 将当前索引加入到deque
deque.push(i);
// 队头索引在边界之外的全部shift i-k+1即为窗口的左边界
while (deque.length && deque[0] <= i - k) {
deque.shift();
}
// 只有窗口大小达到k才存储结果
if (i + 1 >= k) res.push(nums[deque[0]]);
}
return res;
}
链表
19. 删除链表的倒数第 N 个结点:给你一个链表,删除链表的倒数第 n
**个结点,并且返回链表的头结点。
思路:这道题乍一看好像很简单,只需要拿到倒数第n-1个节点即可,这通常需要先遍历一次链表拿到链表的长度,然后再次从头开始遍历到n-1位置删除?那么有没有办法在遍历一次的情况下就拿到倒数第n-1个节点?
其实使用快慢指针即可:我们先创建一个虚拟节点dummy,dummy.next = head(这是做链表题目通用的套路,可以避免很多边界的判断),声明快慢指针指向dummy。快的指针先向后移动n步,快指针到位后,快慢指针一起向前移动,直到快指针的next === null,此时慢指针的位置就是倒数第n-1个节点。光看文字可能不好理解,你可以看图验证一下这种方法。这里根据这个思路实现代码:
function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null {
// 虚拟节点
const dummy = new ListNode(0, head)
// 快慢指针
let slow = dummy, fast = dummy;
// 快指针先向前移动n步
for (let i = 0; i < n; i++) {
fast = fast.next!;
}
// 快慢指针一起移动
while (fast.next !== null) {
slow = slow.next!;
fast = fast.next!;
}
// 删除倒数第n个节点
slow.next = slow.next!.next;
return dummy.next;
}
24. 两两交换链表中的节点:给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
思路:同样是先声明一个虚拟节点,声明一个变量cur用来记录交换元素的前面一个节点,然后使用双指针指向cur.next和cur.next.next,交换双指针位置,将cur移动到新的需要交换位置的两个节点之前,重复交换过程即可:
function swapPairs(head: ListNode | null): ListNode | null {
const dummy = new ListNode(0, head);
let cur = dummy;
while(cur.next !== null && cur.next.next !== null) {
// 双指针
const n1 = cur.next;
const n2 = cur.next.next;
// 交换位置
cur.next = n2;
n1.next = n2.next;
n2.next = n1;
// cur指向n1
cur = n1;
}
return dummy.next;
}