leetcode算法练习——24.两两交换链表中的节点/19.删除链表的倒数第N个节点/面试题 02.07.链表相交/142.环形链表II

leetcode算法练习

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

题目链接

思路&注意点:
1. 创建虚拟头结点来使得操作头结点和非头结点一致
2. 当前指针cur指向哪里 那么就可以操作他所指的后两个元素 例如:1-2-3-4-5-null 那么cur指向2的时候 可以操作3 4这两个结点 即cur指针一定要指向要反转两个节点的前一个结点 弄清这一点才能知道什么时候遍历结束
3. 对于while (prev.next != null && prev.next.next != null)中 条件的书写顺序 必须要这样写 不能倒过来写成 prev.next.next != null && prev.next != null 因为如果倒过来 prev.next如果为空 再取.next会引起空指针异常 并且要用&& 而不是|| 因为奇数节点就不需要交换了 所以只有满足后面有偶数个节点的时候才会进入循环

步骤:
链表交换结点
方法:

/**
 * 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 swapPairs(ListNode head) {
        if (head == null) {
            return null;
        }
        // 创建虚拟头结点
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        ListNode cur = dummyHead;
        // 为偶数结点 cur.next == null遍历结束
        // 为奇数结点 cur.next.next == null遍历结束 奇数节点最后一个就不需要交换了 所以只有满足后面有偶数个节点的时候才会进入循环
        while (cur.next != null && cur.next.next != null) {
            ListNode temp = cur.next;
            ListNode temp1 = cur.next.next.next;
            cur.next = cur.next.next;
            cur.next.next = temp;
            temp.next = temp1; 
            cur = cur.next.next;
        }
        return dummyHead.next;
    }
}

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

题目链接

思路&注意点:
1. 关键在于找到倒数第n个结点
2. 删除一个结点 那操作指针一定要移动到该结点的前一个结点
3. 定义两个指针:一个快指针 一个慢指针 快指针先移动n步 而后快慢指针一起移动 直到快指针指向了空结点null 那么此时慢指针就指向了要删的那一个结点 可以进行删除操作
但是!!! 由2所说——慢指针应该指向要删除结点的前一个结点 故快指针应该移动n + 1步!!!这样当快指针指向空结点null的时候 慢指针指向了要被删除元素的前一个元素

/**
 * 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; }
 * }
 */
/**
 * 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) {
        ListNode dummyHead = new ListNode();
        dummyHead.next = head;
        ListNode fast = dummyHead;
        ListNode slow = dummyHead;
        n++;
        while (n-- != 0 && fast != null) {
            fast = fast.next;
        }
        // 如果上面没有n++ 那么当n大于链表长度的话 那么fast已经指向空了 那么fast = fast.next; 就是对快指针进行一个空指针的操作了
        // fast = fast.next;
        while (fast != null) {
            fast = fast.next;
            slow = slow.next;
        }
        slow.next = slow.next.next;
        return dummyHead.next;
    }
}

面试题 02.07.链表相交

题目链接

思路&注意点:
1. 求出两个链表的长度 并求出两个链表长度的差值 然后让curA移动到 和curB末尾对齐的位置
2. 再比较curA和curB是否相同 如果不相同 同时向后移动curA和curB 如果遇到curA == curB 则找到交点 否则循环退出返回空指针
3. 比较的是不是数值相等 而是指针相等 因此 应该是 if (curA == curB)
不要把值和节点的概念混淆起来 节点是一个实例 占用一块空间 值只是它的成员变量 值怎样和节点本身没有任何关系 一个实例只由它的地址唯一确定

链表相交

/**
 * 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) {
        ListNode curA = headA;
        ListNode curB = headB;
        int lenA = 0, lenB = 0;
        // 求链表A的长度
        while (curA != null) {
            lenA++;
            curA = curA.next;
        }
        // 求链表B的长度
        while (curB != null) {
            lenB++;
            curB = curB.next;
        }
        curA = headA;
        curB = headB;
        // 让curA为最长链表的头 lenA为其长度
        if (lenB > lenA) {
            //1. swap (lenA, lenB);
            int tmpLen = lenA;
            lenA = lenB;
            lenB = tmpLen;
            //2. swap (curA, curB);
            ListNode tmpNode = curA;
            curA = curB;
            curB = tmpNode;
        }
        // 求长度差
        int gap = lenA - lenB;
        // 让curA和curB在同一起点上(末尾位置对齐)
        while (gap-- > 0) {
            curA = curA.next;
        }
        // 遍历curA 和 curB 遇到相同则直接返回
        while (curA != null) {
            if (curA == curB) {
                return curA;
            }
            curA = curA.next;
            curB = curB.next;
        }
        return null;
    }

}

142.环形链表II

题目链接

思路&注意点:
1. 判断是否有环
2. 找到环的入口
3. 判断链表是否有环 利用快慢指针来判断 如果两个指针相遇就意味着有环
快指针每次走2格 慢指针每次走1格
如何先到利用快慢指针?
因为如果是一条直线(没有环) 那么快慢指针永远不会相遇
如果有环 快慢指针一定会环内相遇 快指针进入环后慢指针进入环 快指针其实就是追慢指针的一个过程 快指针对于慢指针移动来说 每次移动一个结点
4. 快指针和慢指针相遇 一定是快指针去追慢指针 故而 快指针肯定至少在环里转了一圈了(因为如果快指针没有转一圈 怎么去追慢指针呢?)

环形链表

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        // 因为是一次走两步 因此还要判断fast.next != null
        while (fast != null && fast.next != null) {
            // 慢指针走一步
            slow = slow.next;
            // 快指针走两步
            fast = fast.next.next;
            // 快慢指针相遇 代表有环
            if (slow == fast) {
                // 相遇的点
                ListNode index1 = fast;
                // 从头开始
                ListNode index2 = head;
                // 两个指针 从头结点和相遇结点 各走一步 直到相遇 相遇点即为环入口
                while (index1 != index2) {
                    index1 = index1.next;
                    index2 = index2.next;
                }
                return index1;
            }
        }
        return null;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值