羊羊刷题笔记Day04/60 | 第二章 链表P2 | 24. 交换链表节点、19. 删除链表倒数第N个节点、(面试题)160. 链表相交、142环形链表II

146 阅读4分钟

学习资料:

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

19. 删除链表的倒数第N个节点

面试题 02.07. 链表相交

142. 环形链表Ⅱ

两两交换链表中节点

重点是画图把哪个指针指哪里弄清楚 思路:三指针实现两个元素,分别指向对应元素以及前一个实现互换。

// 定义头节点
ListNode h = new ListNode(-1,head);

// 指针
ListNode pre = h;
ListNode cur1;
ListNode cur2;

while (pre.next != null && pre.next.next != null){
    // 定义位置
    cur1 = pre.next;
    cur2 = pre.next.next;

    // 交换位置
    cur1.next = cur2.next;
    cur2.next = cur1;
    pre.next = cur2;


    pre = cur1;
}

return h.next;

19 删除链表的倒数第N个节点

自己做:

思路:

  1. 遍历链表,得出size,从而获得索引
  2. 继续遍历,删除指定元素

这种方法无疑时间复杂度达到了O(n2)

看文档

巧妙地运用倒数第n个的特点解决问题。 思路: 双指针:设置两个指针间距为n,当遍历完链表后前指针即为倒数第n个元素

// 方法1:
/*
* 遍历链表,得出size,从而获得索引
* 继续遍历,删除索引元素
* */

// 方法2:
/*
* 设置两个指针间距为n,当遍历完链表后前指针即为倒数第n个元素
* */

// 定义头节点
ListNode h = new ListNode(-1,head);

ListNode low = head;
ListNode fast = low;
ListNode pre = h;
for (; n - 1 > 0;n--){
    fast = fast.next;
}

while (fast.next != null){
    pre = pre.next;
    fast = fast.next;
    low = low.next;
}

// 删除节点
System.out.println(pre.val);
System.out.println(low.val);
System.out.println(fast.val);
pre.next = low.next;

return h.next;

160 相交链表

自己写:

  • 疑惑:为什么例子中

listA = [4,1,8,4,5] listB = [5,6,1,8,4,5] 相交在 8 而不在 1

✅解决:用System.out.print打印两个数组每个元素地址值:(由于篇幅简化了地址值)

@5fcfe4b2 @6bf2d08e @5eb5c224 @53e25b76 @73a8dfcc @ea30797 @7e774085 @3f8f9dd6 @aec6354 @1c655221 @53e25b76 @73a8dfcc @ea30797

可以看出从 8 开始地址值相同,而 1 地址值是不同的,因此在 8 中相交 据此可以想成:只认地址值不认数值

因此思路很清晰:两个数组遍历,找到地址值相同的节点连接

  • 疑惑2:如何找到相同节点的地址

✅解决:由于最后相交后两个列表长度相同,因此使两个列表右对齐后,按较短的第一个节点开始遍历

代码:

//定义头节点
ListNode h1 = new ListNode(-1);
h1.next = headA;
ListNode h2 = new ListNode(-1);
h2.next = headB;

ListNode cur1 = h1;
ListNode cur2 = h2;

// 右对齐数组
// 获取两个数组长度 - 确定较长的数组
int count1 = 0, count2 = 0;
while (cur1.next != null){
    count1++;
    cur1 = cur1.next;
}

while (cur2.next != null){
    count2++;
    cur2 = cur2.next;
}

// cur指针归位
cur1 = h1;
cur2 = h2;

// 使较长数组为1
if (count2 > count1){
    // 数组交换
    ListNode temp = cur1;
    cur1 = cur2;
    cur2 = temp;

    // 计算的长度也要交换!
    int temp1 = count1;
    count1 = count2;
    count2 = temp1;
}

// 使listA指针对齐
int gap = count1 - count2;
for (int i = 0; i < gap; i++) {
    cur1 = cur1.next;
}

// 开始遍历,寻找地址相同
while (cur1 != null){
    // 在此点相交
    if (cur1 == cur2){
        return cur2;
    }
    cur1 = cur1.next;
    cur2 = cur2.next;
}

return null;

142 环形链表Ⅱ

自己写:

  • 如何判断有环

逐一遍历判断?时间复杂度O(n2)

  • 判断尾节点指向哪一个节点

看视频

手写笔记: 手写笔记

✅使用快慢双指针判断是否有环:根据定义

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。

环如同田径场,如果两人跑步速度不同,最后肯定会相遇

假设slow 每次只走一个单位 fast每次走2 或者更多 个单位 设环里有a个元素

难点:

  1. 快慢指针会在第一圈内相遇,即k = 0?

✅原因: 当fast 在环中: fast > a 时,实际在环里位移是 fast % a < a,因此fast ∈ [1,a - 1] 那么也就说,至多走a - 1次就会相遇,而此时slow还没有走完一圈

有种特殊情况(当然不需要理会,只是会出现情况),slow会走完一圈,当fast刚好等于a且x也等于a的情况下,但这不影响后面的计算。

  1. 为什么fast再快速度相遇时也会满足x = z

✅根据公式推导可得出,n = 1时 x = z ;而无论n等于几,也只是转圈,最终回到原位。因此还是x = z,就可以利用这一性质求得相交点

而在上面说的特殊情况,可以看作z = 一圈的距离(而非等于零,所以说不影响后面计算)

  1. 需要一定数学基础,寻找规律

代码:

ListNode cur1 = head;
ListNode cur2 = head;

// 判断是否有环
while (cur2 != null && cur2.next != null){
    cur1 = cur1.next;
    cur2 = cur2.next.next;
    if (cur1 == cur2){
        // 有环 - 且头节点与此位置到相交点距离相同
        ListNode index1 = head;
        ListNode index2 = cur1;
        while (index1 != index2){
            index1 = index1.next;
            index2 = index2.next;
        }
        return index1;
    }
}
return null;