前端算法之链表和栈

111 阅读3分钟

1.链表

image.png

1.反转链表1

2.反转链表2

(跳转:leetcode.cn/problems/re…image.png
析:
我们在一开始对链表进行遍历的时候就可以根据left和right找到“四个指针”,并用四个变量记录下来:区间的头指针,区间的尾指针,左断点、右断点。
关键点1: 记录左右两个断点。我们反转了区间链表之后怎么和未反转的部分“相连”。所以我们需要两个变量来记录与区间相连的两个左右断点。原来与反转区间相连的“左断点”和“右断点”。只要将左断点的next指向反转后的“头指针”,将反转后的“尾指针”的next指向右断点,就可以完成相连。
关键点2: 找到区间,并隔断连接处,使其成为完整链表,然后进行完整链表的反转操作。我们执行链表的操作比较擅长的是反转完整链表,所以我们需要将区间与断点割裂,并在头尾两端连接null,那么我们就可以调用反转整个链表的函数。
关键点3: 连接 反转的核心思路是找到要反转的区间(这个区间的头指针和尾指针),然后对这个区间进行反转,反转之后原来的头指针变成了“尾指针”,原来的尾指针变成了“头指针”。并且我们也记录了左右断点,所以连结就是将左断点的next指向尾指针,头指针的next指向右断点

var reverseBetween = function(head, left, right) {
    const vnode = new ListNode(-1);
    vnode.next = head;
    // 先创建一个虚拟节点,指向这个链表,防止left是链表开头这种边界情况,那么pre和left就指向同一个
    // step1: 寻找左断点
    let leftPoint = vnode; // 左断点赋初始值
    for (let index = 1; index < left; index++) {
        leftPoint = leftPoint.next
    }
    // step2: 找到中间链表的头节点
    let part_head = leftPoint.next;
    // step3: 寻找链表的尾节点
    let part_end = part_head;
    for (let index = left; index < right; index++) {
        part_end = part_end.next;
    }
    // step4: 找到右断点
    let rightPoint = part_end.next;
    
    // step5: 断开链表
    leftPoint.next = null;
    part_end.next = null;
    
    // step6:反转区间链表
    reverse(part_head)
    function reverse(head) {
       let pre = null;
       let cur = head;
       while(cur) {
           let next = cur.next;
           cur.next = pre;
           pre = cur;
           cur = next;
       }
       return pre;
    }
    leftPoint.next = part_end;
    part_head.next = rightPoint;
    return vnode.next

};

3.删除链表中的节点

image.png image.png

4.删除链表的倒数第 n 个结点

(跳转:leetcode.cn/problems/SL…

image.png

5.两两交换链表中的节点

leetcode.cn/problems/sw…

image.png

6.合并两个有序链表(扩展到合并k个有序链表)

image.png

var deleteNode = function(head, val) {
    let link = head1 = new ListNode()
    link.next = head;
    while(link.next) {
        if (link.next.val === val) {
            link.next = link.next.next;
        } else {
            link = link.next;
        }
    }
    return head1.next;
};

[链表中倒数第k个节点]

leetcode.cn/problems/li…

image.png

2.栈

image.png

1.验证栈序列

image.png

思路:这题本质上是通过一个入栈的最终结果表,能否判断出这个栈在这一过程中(经历入栈和出栈),最终形成的出栈表是否为给定这个表。

例如,我对一个栈执行一系列的入栈出栈操作,最终将这个栈全部出栈,得到的结果是不是给定的出栈结果。 这一系列出栈入栈操作为:入栈1,2,3,4,出栈4,入栈5,出栈5,出栈3,2,1 我们通过栈的特点可以发现,pop这个出栈结果中的第一位一定是最先从pushed中pop的。为什么?因为加入我一开始全部执行入栈操作,还没有执行pop操作,那么第一个执行pop操作的元素,一定是放在poped数组的第一位(这里poped数组是指将出栈的元素push入栈),然后poped第二个元素就是pushed中第二个出栈的。那么我们需要做的就是通过poped的结果来逆向模拟这一系列入栈出栈的过程,最后如果这个pushed栈为空,那么就是一一对应,否则,两个不对应。

var validateStackSequences = function(pushed, popped) {
    let index2 = 0;
    let stack = [];
    for (let index = 0; index < pushed.length; index++) {
        stack.push(pushed[index]);
        while (stack.length > 0 && stack[stack.length - 1] === popped[index2]) {
            stack.pop();
            index2++;
        }
    }
    return stack.length === 0

};