java链表2

一、环形链表Ⅱ(LeetCode 142)

 

1. 问题描述

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。如果链表无环,则返回  null 。通过跟踪  next  指针能再次到达某个节点时,链表存在环,评测系统用整数  pos  表示链尾连接到链表中的位置(索引从 0 开始 ), pos  不作为参数传递,仅用于标识实际情况,且不允许修改链表。

2. 示例

- 输入: head = [3,2,0,-4] ,  pos = 1 ;输出:返回索引为 1 的链表节点,链表中有环,尾部连接到索引为 1 的节点。

- 输入: head = [1,2] ,  pos = 0 ;输出:返回索引为 0 的链表节点,链表中有环,尾部连接到索引为 0 的节点 。

- 输入: head = [1] ,  pos = -1 ;输出:返回  null ,链表中无环。

3. 解题思路 - 快慢指针法

定义两个指针,慢指针  slow  每次走一步,快指针  fast  每次走两步。

- 判断链表是否有环:若链表无环, fast  会先到达链表末尾;若有环, fast  会在环内追上  slow ,二者相遇。

- 找到环的入口:相遇后,设  cur1  从相遇位置出发, cur2  从链表头部出发,二者每次都走一步,再次相遇的位置就是环的入口。这是因为根据数学推导(设链表进入环前长度为  L ,环长为  R ,相遇点距离环入口距离为  X  ,推导得出  L = X  )。

4. 代码实现

 

public ListNode detectCycle(ListNode head) {

    ListNode slow = head;

    ListNode fast = head;

    // 寻找快慢指针相遇点

    while (fast != null && fast.next != null) {

        slow = slow.next;

        fast = fast.next.next;

        if (slow == fast) {

            // 相遇后,重新设置指针找环入口

            ListNode cur1 = fast;

            ListNode cur2 = head;

            while (cur1 != cur2) {

                cur1 = cur1.next;

                cur2 = cur2.next;

            }

            return cur1;

        }

    }

    return null; // 无环情况

}

 

 

二、判断链表是否有环

 

1. 简单做法(空间复杂度 O(N))

遍历链表,用一个  List  存储每个遍历过的节点地址(引用) ,每次遍历新节点时,检查该节点是否已在  List  中存在,若存在则说明链表有环。

 

public boolean hasCycle(ListNode head) {

    if (head == null) {

        return false;

    }

    List<ListNode> list = new ArrayList<>();

    for (ListNode cur = head; cur != null; cur = cur.next) {

        if (list.contains(cur)) {

            return true;

        } else {

            list.add(cur);

        }

    }

    return false;

}

 

 

1. 快慢指针法(空间复杂度 O(1))

创建两个指针都指向链表头部, fast  每次走两步, slow  每次走一步。若链表无环,两指针不会重合;若有环, fast  会追上  slow ,二者会重合。

 

public boolean hasCycle(ListNode head) {

    ListNode slow = head;

    ListNode fast = head;

    while (fast != null && fast.next != null) {

        slow = slow.next;

        fast = fast.next.next;

        if (slow == fast) {

            return true;

        }

    }

    return false;

}

 

 

三、链表常见笔试题思路

 

1. 删除链表中等于给定值  val  的所有节点

先处理后续节点,最后处理头结点。遍历链表,跳过值为  val  的节点,调整指针连接。

2. 反转一个单链表

使用三个引用  prev (前驱节点 )、 cur (当前节点 )、 next (后继节点 ) ,遍历链表,执行  cur.next = prev  操作,当  next  为  null  时, cur  为反转后的头结点。

3. 求带头发结点  head  的非空单链表的中间结点

求链表长度后除以 2 得到中间节点的“步数” ,从头结点出发走相应步数。若有两个中间结点,返回第二个。

4. 输入一个链表,输出该链表中倒数第  k  个结点

先求链表长度,通过  size - k  得到“步数” ,从头结点出发走相应步数。

5. 合并两个有序链表

创建两个引用分别指向两个链表头结点,比较值大小,将小的节点插入新链表末尾。创建新头结点  newHead  和新尾结点  newTail (可借助傀儡节点 ),遍历过程中,一个链表遍历完后,将另一个链表剩余部分拼接到结果末尾。

6. 以给定值  x  为基准将链表分割成两部分,小于  x  的结点排在大于或等于  x  的结点之前

遍历链表,比较每个值和  x  的关系,准备两个链表分别保存小于  x  和大于等于  x  的元素,最后连接两个链表。

 

四、判断链表是否为回文结构

 

1. 空间复杂度 O(N) 的做法

复制一份链表,将复制链表反转,然后比较原链表和反转后链表是否相同。

2. 空间复杂度 O(1) 的做法

找到链表中间节点,将后半部分链表反转,比较前半部分和反转后的后半部分是否相同,忽略长度比较,只要其中一个子链表结束,比较就结束。

 

五、判断两个链表是否相交

 

1. 解题思路

先分别求出两个链表的长度,创建两个引用指向头结点,让较长链表的引用先走,走的步数为两链表长度差值,然后两个引用同时走,每次走一步,若相遇则链表相交。

2. 代码实现

 

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {

    int lenA = 0, lenB = 0;

    ListNode curA = headA, curB = headB;

    // 计算链表 A 的长度

    while (curA != null) {

        lenA++;

        curA = curA.next;

    }

    // 计算链表 B 的长度

    while (curB != null) {

        lenB++;

        curB = curB.next;

    }

    curA = headA;

    curB = headB;

    // 让长链表指针先走

    if (lenA > lenB) {

        for (int i = 0; i < lenA - lenB; i++) {

            curA = curA.next;

        }

    } else {

        for (int i = 0; i < lenB - lenA; i++) {

            curB = curB.next;

        }

    }

    // 同时遍历找相交点

    while (curA != null && curB != null) {

        if (curA == curB) {

            return curA;

        }

        curA = curA.next;

        curB = curB.next;

    }

    return null;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值