5.<tag-链表和快慢针>lt.19-删除链表的倒数第 N 个结点 + lt.160-相交链表 + 剑指 Offer 22. 链表中倒数第k个节点 1

<tag-数组和二分查找_15>-lt.xx-xxxxxx + lt.xx-xxxxxx

lt.19-删除链表的倒数第 N 个结点

[案例需求]
在这里插入图片描述

[思路分析一, 学弱解法]

  1. 学弱解法: 先遍历一遍链表得到链表的结点数量count , 再去遍历链表到倒数第n个(整数第count -n 个的下一个)结点的前驱结点(count - n)的位置, 删除待删除的结点即可.
  2. 注意: 删除链表时最重要的是设置头结点 dummyNode, 这样使得首元结点(head)的删除跟其他结点的删除操作保持一致(都有前驱结点, 能够使用 temp.next = temp.next.next)
  3. 第一次遍历可以得知链表的长度L,第二次遍历从头找到L-n位置的链表元素,删除即可。两次遍历,时间复杂度为O(n), 空间复杂度O(1)。
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        int size = listSize(head);
        int count = size - n;

        ListNode dummyNode = new ListNode(-1);
        dummyNode.next = head;
        ListNode temp = dummyNode;

        while(true){
            if(temp.next == null)return null;
            if(count == 0)break;
            temp = temp.next;
            --count;
        }

        temp.next = temp.next.next;
        //System.out.println(size);
        return dummyNode.next;
    }


    public int listSize(ListNode head){
        int count = 0;

        ListNode temp = head;

        while(true){
            if(temp == null)return count;

            ++count;
            temp = temp.next;
        }
    }
}

[思路分析, 解法二, 快慢针]

  1. 我们设置两个指针,
  2. 两个指针初始状态都指向dummyNode, 指针fast先走n步, slow不动(让fast 与 slow的步长等于 n)
  3. 然后指针fast和指针slow同步向前继续遍历链表, 直到fast 的和后续结点为空, 此时slow到达被删除结点的前驱结点
  4. 对slow后的结点实行删除结点即可.
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummyNode = new ListNode(-1);
        dummyNode.next = head;
        ListNode fast = dummyNode;
        ListNode slow = dummyNode;
        int count = 0;

        while(count < n){
            fast = fast.next;
            ++count;
        }

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

        slow.next = slow.next.next;

        return dummyNode.next;
    }
}

160. 相交链表

[案例需求]
在这里插入图片描述

[思路分析一, 学弱解法]

  • 把其中一条链表放入集合中, 需要对这一条链表进行遍历
  • 然后遍历第二条链表, 遍历的同时跟集合中结点进行比较, 在遍历到第二条链表末尾之前, 如果集合包含了第二条链表中的某个节点, 则返回这个节点, 否则就返回空.
///1. 普通解法, hashset存储. 比较
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
            Set<ListNode> listA = new HashSet<>();

            ListNode tempA = headA;

            while(true){
                if(tempA == null)break;

                listA.add(tempA);
                tempA = tempA.next;
            }
            
            //遍历链表B
           // ListNode dummyNode = new ListNode(-1);
          //  dummyNode.next = headB;
            ListNode tempB = headB;

            while(true){
                
                if(tempB == null)return null; 
                if(listA.contains(tempB))return tempB;
                
                tempB = tempB.next;
               
            }
    }
}
  • 时空复杂度
    在这里插入图片描述

[思路分析二, 两条链表交替遍历]

pA走过的路径为A链+B链

pB走过的路径为B链+A链

pA和pB走过的长度都相同,都是A链和B链的长度之和,相当于将两条链从尾端对齐,如果相交,则会提前在相交点相遇,如果没有相交点,则会在最后相遇。

若相交,链表A: a+c, 链表B : b+c.
a+c+b+c = b+c+a+c 。则会在公共处c起点相遇。
若不相交,a +b = b+a 。因此相遇处是NULL
pA:1->2->3->4->5->6->null->9->5->6->null
pB:9->5->6->null->1->2->3->4->5->6->null

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
           ListNode pA = headA;
           ListNode pB = headB;

           while(pA != pB){
               pA = pA != null ? pA.next : headB;
               pB = pB != null ? pB.next : headA;
           }

           return pA;
    }
}

在这里插入图片描述

剑指 Offer 22. 链表中倒数第k个节点

[案例需求]

在这里插入图片描述

[思路分析]

起始先让快指针 fast 先走 k 步,此时 fast 和 slow 之间距离为 k,之后让 fast 和 slow 指针一起走(始终维持相对距离为 k),当 fast 到达链表尾部,slow 即停在倒数第 k 个节点处。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        //2. 快慢针
        ListNode fast = head;
        ListNode slow = head;

        while(k > 0){
           
           fast = fast.next; 
           --k;
        }

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

        return slow;
    }
}

[思路分析二, 栈]

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        Deque<ListNode> stack = new LinkedList<>();

        ListNode temp = head;

        while(true){
            if(temp == null)break;

            stack.push(temp);
            temp = temp.next;
        }

        for(int i = 0; i < k; i++){
           
            if(i == k - 1)return stack.pop();
             stack.pop();
        }

        return null;
    }
}
posted @   青松城  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示