链表:🎈24.两两交换链表中的节点&02.07. 链表相交&142. 环形链表 II&2. 两数相加&83. 删除排序链表中的重复元素

146 阅读5分钟

24.两两交换链表中的节点:4指针

24.两两交换链表中的节点 交换相邻两个节点,不是改值是改链表指针物理位置 image.png 思路都差不多,只是做法些许不同 原则:知道要 操作两个节点前一个指针

  • 循环开始指针移动的规则:看一次循环结束,第二次循环开始指针在哪
    • 每次进入循环时要完成指针的更新
    • 技巧:以一个指针为定位更新,其他指针相对它变

image.png

4指针

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var swapPairs = function(head) {
    let dhead = new ListNode(0, head) //先定义虚头
    let pre = dhead // 每次循环时的虚拟头结点
    let pre1 = null
    let cur = null //cur指向要交换的后一个
    let tmp = null //存cur的下一个
    while(pre.next !== null && pre.next.next !== null) {
        // 指针到位:
        pre1 = pre //pre1指向虚拟头结点
        pre = pre1.next //pre指向虚头下一个:要交换的前一个
        cur = pre.next //cur指向要交换的后一个
        tmp = cur.next //存cur的下一个,每次循环都要更新
        pre1.next = cur //反转节点
        cur.next = pre
        pre.next = tmp 
        // 一次交换完后,pre变成下一次的虚拟头结点
    }
    return dhead.next
};

面试题 02.07. 链表相交

面试题 02.07. 链表相交 先求出两个链表的长度,计算出长度差,长的那个链表指针先前移长度差步-->让两个链表对齐 之后就可以同时后移,如果指针的值相等说明,找到交点,如果到最后指针都指向null了还没找到说明没有交点-->返回null image.png

双指针

  • 解构赋值一定要加;
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} headA
 * @param {ListNode} headB
 * @return {ListNode}
 */
var getIntersectionNode = function(headA, headB) {
    let curA = headA
    let curB = headB
    let lenA = 0
    let lenB = 0
    while (curA) { // 求A长度
        lenA++
        curA = curA.next
    }
    while (curB) { // 求B长度
        lenB++
        curB = curB.next
    }
    curA = headA // 回到开头
    curB = headB
    if (lenB > lenA) { // 让curA指向长
    //    let tmp = curB //交换
    //     curB = curA
    //     curA = tmp
    //     tmp = lenA
    //     lenA = lenB
    //     lenB = tmp
     // 交换变量注意加 “分号” ,两个数组交换变量在同一个作用域下时
        // 如果不加分号,下面两条代码等同于一条代码: [curA, curB] = [lenB, lenA]
        [lenA, lenB] = [lenB, lenA];
        [curA, curB] = [curB, curA];
    }
    let n = lenA - lenB
    while (n--) {
        curA = curA.next // curA先前移多出来的对齐
    }
    while (curA !== curB) { //一起移直到指向同一个
        curA = curA.next
        curB = curB.next
    }
    return curA
};

142. 环形链表 II

142. 环形链表 II 判断有没有环,无环返回空,有环返回环的入口(开始进入环的第一个节点) image.png

  • 判断有无环:快(fast 每次移两个),慢(slow 每次移一个)双指针,如果相遇就说明有环
    • 如果没有环,快慢指针会一直向前走,永远不可能相遇
    • 当有环时,快指针先进入环内转圈,慢指针进入后,快指针相对慢指针位移为1,每次前进1追慢指针
    • 所以两个指针相遇说明有环
  • 返回环的入口

image.png 重点:x = z + (n-1)圈,所以一个指针从相遇点,一个指针从起点开始,以相同速度移动,相遇的点为入口处 总结:

  1. 快(fast 每次移两个)慢指针(slow 每次移一个):相遇,说明有环
  2. 环入口为:一个指针从相遇点,一个指针从起点开始,以相同速度移动,相遇的点

快慢双指针:相遇有环、起点和相遇同走找入口

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var detectCycle = function(head) {
    let fast = head //快慢指针初始都为头结点开始,快一次两个,慢一次一个
    let slow = head
    while (fast && fast.next) { ////因为fast先走,只判断它就行 避免操作空指针
        fast = fast.next.next //fast一次两个
        slow = slow.next //slow一次一个
        if (fast === slow) { // 快慢指针相遇,说明有环
            index1 = fast //相遇点开始
            index2 = head //头结点
            while (index1 !== index2) { //两指针相遇点为入口
                index1 = index1.next
                index2 = index2.next
            } //出循环时相遇
            return index1
        }
    }
    return null //都遍历完了快慢指针还没相遇说明无环
};

2. 两数相加

2. 两数相加 注意:两个链表的结果给第三个链表用||,只要还有一个链表有元素(不为空) 空指针取值,如果为空取值为0,移动时也一定要判断空指针 最后一次如果还有进位,要记得加到链表最后

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
var addTwoNumbers = function(l1, l2) {
    let l3 = new ListNode(0, null) // 定义新链表存结果,虚头
    let p1 = l1; // 定义指针
    let p2 = l2;
    let p3 = l3;
    let v1 = 0, v2 = 0, v3 = 0, val = 0
    let carry = 0 // 进位
    while (p1 || p2) { // 只要还有
        v1 = p1 ? p1.val : 0 // 不为空取值,为空取0
        v2 = p2 ? p2.val : 0
        val = v1 + v2 + carry // 相加总值
        v3 = val % 10 // 个位数
        carry = Math.floor(val / 10) // 十位数,JS计算结果有小数
        p3.next = new ListNode(v3, null) // 加入新链表
        p3 = p3.next // 指针继续移动
        if (p1) p1 = p1.next // 一定要判断空指针
        if (p2) p2 = p2.next
    }
    if (carry) { // 如果最后一次计算结果还是有进位,加到最后
        p3.next = new ListNode(carry, null)
    }
    return l3.next
};

83. 删除排序链表中的重复元素

83. 删除排序链表中的重复元素

  • 删除结点肯定要使用虚拟头结点
  • 注意判断空指针,保证不操作空指针
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var deleteDuplicates = function(head) {
    let dhead = new ListNode(0, head) // 删除肯定要用虚头
    let pre = dhead // 便于删除
    let cur = pre.next // 遍历元素
    while (cur && cur.next) {
        if (cur.val === cur.next.val) { // 相等则删
            pre.next = cur.next
            cur = cur.next
        } else {
            pre = cur
            cur = cur.next
        }
    }
    return dhead.next
};