zoukankan      html  css  js  c++  java
  • 链表+快慢指针:环形链表 (Leetcode141/Leetcode142/剑指24/Leetcode61/剑指35)

      

     

     

     

     

     

     

     

     

    暴力解法:二次到达

    链表实现代码:

    class ListNode {
    int val;
    ListNode next;
    ListNode(int x) {
    val = x;
    next = null;
    }
    }
    /**
    * 解法一:二次到达解法
    * 1.定义数组记录已访问节点
    * new ListNode[10000];
    * 2.遍历链表的每个节点,并与容器中已存放的节点依次比较:
    * 相同则方法结束,返回true
    * 不同则存入最新位置,继续遍历下个节点
    * 3.若next指针为null,则方法结束,返回false
    *
    * @param head
    * @return
    */
    public boolean hasCycle(ListNode head) {
    // 1.定义数组记录已访问节点
    ListNode[] array = new ListNode[10000];
    // 2.遍历链表的每个节点,
    while (head != null) {
    // 并与容器钟已存放的节点依次比较
    for (int i = 0; i < array.length; i++) {
    if (array[i] == head) {
    return true;
    }
    if (array[i] == null) {
    array[i] = head; // 将当前节点存放到最新位置
    break; // 结束容器的遍历
    }
    }
    head = head.next;
    }
    // 3.若next指针为null,则方法结束,返回false
    return false;
    }

     

     

     最优解:追击问题(快慢指针)

    /**
    * 解法二:快慢指针解法
    * 1.定义快慢两个指针:
    * slow=head; fast=head.next;
    * 2.遍历链表:
    * 快指针步长为2:fast=fast.next.next;
    * 慢指针步长为1:slow=slow.next;
    * 3.当且仅当快慢指针重合,有环,返回true
    * 4.快指针为null,或其next指向null,没有环,返回false,操作结束
    * @param head
    * @return
    */
    public boolean hasCycle(ListNode head) {
    if (head == null) { // 链表中有节点[0, 10^4]个
    return false;
    }
    // 1.定义快慢两个指针:
    ListNode slow = head;
    ListNode fast = head.next;
    // 2.遍历链表:快指针步长为2,慢指针步长为1
    while (fast != null && fast.next != null) {
    // 3.当且仅当快慢指针重合:有环,操作结束
    if (slow == fast) {
    return true;
    }
    fast = fast.next.next; // 快指针步长为2
    slow = slow.next; // 慢指针步长为1
    } // 4.快指针为null,或其next指向null,没有环,返回false,操作结束
    return false;
    }

    测试用例

    辅助数据结构:链表。代码如下:

    class ListNode {
    int val;
    ListNode next;
    ListNode(int x) {
    val = x;
    next = null;
    }
    }
    输入:head = [1], pos = -1
    输出:false
    解释:pos 为环开始节点的索引,若pos = -1,则没有环。pos 不作为参数进行传递,仅仅是为了标识链表的实际情况
    输入:head = [1,2], pos = 0
    输出:true
    输入:head = [3,2,0,-4], pos = 1
    输出:true

     

     

    1/4 环形链表 II - Leetcode 142

    给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

    为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。

    说明:不允许修改给定的链表。

    进阶:

    你是否可以使用 O(1) 空间解决此题?

    示例 1:

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

    示例 2:

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

    示例 3:

    输入:head = [1], pos = -1
    输出:返回 null
    解释:链表中没有环。

    提示:

    链表中节点的数目范围在范围 [0, 10^4] 内
    -10^5<= Node.val <= 10^5
    pos 的值为 -1 或者链表中的一个有效索引

    /**
     * Definition for singly-linked list.
     * class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode(int x) {
     *         val = x;
     *         next = null;
     *     }
     * }
     */
    /*
    使用快慢指针找到是否有环(快指针一次走两步,慢指针一次走一步)
    找到环以后快指针,慢指针再回到头结点,快慢指针都按照步长1前进,等两个指针再次相遇就是环的入口位置。
    大家可以回想一个场景,在学校田径运动中,跑的快的同学会套圈跑的慢的同学,这个其实就是和快慢指针的思路很像的。
    
    如果链表从的头结点就是环的入口,那就是一个标准环形跑道,慢的人跑完一整圈,正好被那个两倍速度的套圈,也就是两个人在起点相遇,那么如果两个人不是在起点相遇,那么相差的距离其实就是头结点到环入口的距离。
    */
    public class Solution {
        public ListNode detectCycle(ListNode head) {
            ListNode fast = head;
            ListNode slow = head;
            while(fast != null && fast.next != null){
                fast = fast.next.next;//快指针走两步
                slow = slow.next;//慢指针走一步
                if(fast == slow)  break; //相遇代表有环
            }
            if(fast == null || fast.next == null)return null;
            slow = head;//慢指针回到链表头部
            while(fast != slow){
                slow = slow.next;
                fast = fast.next;//快指针也调整为一次走一步
            }
            return slow;
        }
    }
    2/4 反转链表 - 剑指24

    定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

    示例:

    输入: 1->2->3->4->5->NULL
    输出: 5->4->3->2->1->NULL

    限制:

    0 <= 节点个数 <= 5000

    class Solution {
        public ListNode reverseList(ListNode head) {
            ListNode cur = head, pre = null;
            while(cur != null) {
                ListNode tmp = cur.next; // 暂存后继节点 cur.next
                cur.next = pre;          // 修改 next 引用指向
                pre = cur;               // pre 暂存 cur
                cur = tmp;               // cur 访问下一节点
            }
            return pre;
        }
    }
    3/4 旋转链表 - Leetcode 61

    给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。

    示例 1:
    输入: 1->2->3->4->5->NULL, k = 2
    输出: 4->5->1->2->3->NULL
    解释:
    向右旋转 1 步: 5->1->2->3->4->NULL
    向右旋转 2 步: 4->5->1->2->3->NULL

    示例 2:
    输入: 0->1->2->NULL, k = 4
    输出: 2->0->1->NULL
    解释:
    向右旋转 1 步: 2->0->1->NULL
    向右旋转 2 步: 1->2->0->NULL
    向右旋转 3 步: 0->1->2->NULL
    向右旋转 4 步: 2->0->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; }
     * }
     */
    class Solution {
        public ListNode rotateRight(ListNode head, int k) {
            if(head==null||k==0){
                return head;
            }
            ListNode cursor=head;
            ListNode tail=null;//尾指针
            int length=1;
            while(cursor.next!=null)//循环 得到总长度
            {
                cursor=cursor.next;
                length++;
            }
            int loop=length-(k%length);//得到循环的次数
            tail=cursor;//指向尾结点
            cursor.next=head;//改成循环链表
            cursor=head;//指向头结点
            for(int i=0;i<loop;i++){//开始循环
                cursor=cursor.next;
                tail=tail.next;
            }
            tail.next=null;//改成单链表
            return cursor;//返回当前头
        }
    }
    4/4 复杂链表的复制 - 剑指35

    请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。

    示例 1:
    e1.png
    输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
    输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
    示例 2:
    e2.png
    输入:head = [[1,1],[2,1]]
    输出:[[1,1],[2,1]]
    示例 3:
    e3.png
    输入:head = [[3,null],[3,0],[3,null]]
    输出:[[3,null],[3,0],[3,null]]
    示例 4:

    输入:head = []
    输出:[]
    解释:给定的链表为空(空指针),因此返回 null。

    提示:

    -10000 <= Node.val <= 10000
    Node.random 为空(null)或指向链表中的节点。
    节点数目不超过 1000 。

    /*
    // Definition for a Node.
    class Node {
        int val;
        Node next;
        Node random;
    
        public Node(int val) {
            this.val = val;
            this.next = null;
            this.random = null;
        }
    }
    */
    /**
     * 考虑构建 原节点 1 -> 新节点 1 -> 原节点 2 -> 新节点 2 -> …… 的拼接链表,
     * 如此便可在访问原节点的 random 指向节点的同时找到新对应新节点的 random 指向节点。
     */
    
    class Solution {
        /**
         * 移花接木
         * 考虑构建 原节点 1 -> 新节点 1 -> 原节点 2 -> 新节点 2 -> …… 的拼接链表,
         * 如此便可在访问原节点的 random 指向节点的同时找到新对应新节点的 random 指向节点。
         * 1. 先插入新节点
         * 2. 更新新节点的random指针
         * 3. 拆分出新节点
         */
        public Node copyRandomList(Node head) {
            if (head == null) {
                return null;
            }
            // 完成链表节点的复制插入
            Node cur = head;
            while (cur != null) {
                //在原节点后插入新复制的节点
                Node copyNode = new Node(cur.val);
                copyNode.next = cur.next;
                cur.next = copyNode;
                cur = copyNode.next;//原链表的下一个节点
            }
            // 完成链表复制节点的随机指针复制
            cur = head;
            while (cur != null) {
                if (cur.random != null) { // 注意判断原来的节点有没有random指针
                    cur.next.random = cur.random.next;//复制原节点的random指针(random指向节点的下一个节点就是对应的新节点
                }
                cur = cur.next.next;
            }
            // 将链表一分为二
            Node copyHead = head.next; //新链表的头节点
            cur = head;
            Node curCopy = head.next;
            while (cur != null) {
                cur.next = cur.next.next;//还原原始链表
                cur = cur.next;
                if (curCopy.next != null) {//拆分新链表
                    curCopy.next = curCopy.next.next;
                    curCopy = curCopy.next;
                }
            }
            return copyHead;
        }
    }
  • 相关阅读:
    02.简单的DOM操作
    03.获取和设置元素特性的值
    异常为"当IDENTITY_INSERT设置为OFF时" 的解决
    GridView的RowCommand事件中取得行索引 技巧
    01.判断页面加载完成的方法
    今天新开了博客园
    C#巧用anchor和dock设计复杂界面(控件随着窗体大小的变化而变化)
    C#如何使用webbrowser控件制作一个简易的IE浏览器(菜鸟级)
    断箭——不相信自己的意志,永远也做不成将军
    成长与成功
  • 原文地址:https://www.cnblogs.com/JasperZhao/p/15120951.html
Copyright © 2011-2022 走看看