力扣关于链表有关的练习题

目录

力扣27—— 移除元素

力扣26——删除有序数组中的重复项

力扣283——移动零的位置

力扣203号题——移除链表元素

力扣83——删除链表中的重复元素

力扣82——删除排序链表中的重复元素 II

力扣206——反转链表

力扣92——反转链表 II

力扣876——链表的中间结点

力扣剑指offer22——链表中倒数第k个节点

力扣234——回文链表

力扣21——合并两个有序链表

力扣面试题02.04—— 分割链表

力扣160——相交链表​​​​​​​

力扣141——环形链表

力扣142——环形链表 II


力扣27—— 移除元素

力扣关于链表有关的练习题_第1张图片

力扣关于链表有关的练习题_第2张图片

class Solution {

    public int removeElement(int[] nums, int val){
        int p1 = 0;//只指向对的元素
        int p2 = 0;//遇到待删除元素就跳过

        for (int i = 0; i < nums.length; i++) {

            if(nums[i] != val){

             nums[p1] = nums[p2];
             p1 ++;
            }
            p2 ++;

        }
        return p1;
    }
    
}


解析:当遇到不是待删除元素时,p1和p2同时向后走,但遇到待删除数时,p1按兵不动,让p2一直走,直到走到不为待删除数时。

p1收集的是正确的元素,p2遇到待删除的数就跳过。

走完正个数组后,p1刚好指向正确元素的下一个位置,数值刚好等于长度值,所以直接返回 p1就可以了

力扣26——删除有序数组中的重复项

力扣关于链表有关的练习题_第3张图片

力扣关于链表有关的练习题_第4张图片

class Solution {
    
    public int removeDuplicates(int[] nums) {
        
        //判断边界条件
        if(nums == null || nums.length == 0)return 0;
        
        int slow = 0;//只用来接收正确的
        int fast = 0;//遇到重复的就跳过

        while(fast < nums.length){
            if(nums[slow] != nums[fast]){
                slow ++;
                nums[slow] = nums[fast];
            }
            fast ++;
        }
        return slow + 1;
    }    
}

 解析:先进行判空处理,可能数组为空。

用双指针来解决:注意题目的数组是有序的,说明重复的数必然相邻。

 slow  只用来接收正确的
 fast 遇到重复的就跳过

如果相等,f 后移 1 位
如果不相等,将 s 位置的元素复制到 s+1 位置上,s 后移一位,f 后移 1 位
重复上述过程,直到 q 等于数组长度。

返回 s + 1,即为新数组长度。

力扣283——移动零的位置

力扣关于链表有关的练习题_第5张图片

力扣关于链表有关的练习题_第6张图片

public class Num_283_moveZeroes {
    public void moveZeroes(int[] nums){

        int p1 = 0;
        int p2 = 0;
        int conut = 0;//统计0的个数
        while(p2 < nums.length){
            if(nums[p2] != 0){
                nums[p1] = nums[p2];
                p1++;
            }else{
                conut++;
            }
            p2++;
        }

        while(conut-- > 0){
            nums[p1++] = 0;
        }

    }
}

解析:当遇到不是0时,p1和p2同时向后走,但遇到0时,p1按兵不动,让p2一直走,直到走到不为0时。

p1收集的是正确的元素,p2遇到0就跳过。

当遇到0时 conut++统计0的个数

走完正个数组后,p1刚好指向正确元素的下一个位置,再次while循环添加conut个 0

力扣203号题——移除链表元素

力扣关于链表有关的练习题_第7张图片

力扣关于链表有关的练习题_第8张图片有三种常规的思路:

/**
 * 思路一:最普通的方法,找前驱,和 特殊处理头节点
 */
    public ListNode removeElements(ListNode head, int val) {
        //保证头节点不是待删除的数
        //1.必须先保证heal不是空指针,才能继续访问里面的val值
        while(head != null && head.val == val){
            //1.0
//            ListNode node = head;
//            head = node.next;
//            node.next = null;
            //其实题目里给的链表,不需要考虑回收、释放的问题,可以直接支这样写
            //2.0
            head = head.next;
        }
        //走到这就保证了头节点不再是待删除的数
        //先判空
        if(head == null){
            return null;
        }else{
            //到这里说明头节点不是待删除的节点
            //开始找前驱
            ListNode prve = head;
            while(prve.next != null ){
                if( prve.next.val == val ) {
                    prve.next = prve.next.next;
                }else{
                    //只有不等于val值,才能移动前驱
                    prve = prve.next;
                }

            }
        }
        return head;
    }

/**
 * 思路二:递归写法。题目值给了我们头节点,那我们就只判断头节点是否是需要删除的节点,
 * 剩下的节点交给子方法去处理并把结果返回。
  *
    public ListNode removeElements(ListNode head, int val) {


        //1.什么情况下不需要拆分了?:终止条件
        if( head == null ){
            return null;
        }else{
            //要用head.next来接收子函数的返回值
            head.next =  removeElements(head.next, val);
            if(head.val == val){//跳过
                head = head.next;
            }
        }
        return head;

    }

/**
 * 思路三: 创建一个虚拟头节点来删除。
 * 之前删除老是需要考虑特殊的头节点问题,之前就给头节点创出来一个虚拟节点
 */
    public ListNode removeElements(ListNode head, int val) {
        //判断是否为空
        if(head == null){
            return head;
        }
        //创建出一个虚拟头节点
        ListNode dummyHead = new ListNode();
        dummyHead.next = head;//连接

        ListNode prve = dummyHead;//前驱
        while(prve.next != null){
            if(prve.next.val == val){
                prve.next = prve.next.next;
            }else{
                prve = prve.next;
            }
        }

        return dummyHead.next;
    }

力扣83——删除链表中的重复元素

力扣关于链表有关的练习题_第9张图片

力扣关于链表有关的练习题_第10张图片

/**
 * 思路一:用双指针 prve 和 node。这是个排序的链表。如果当prve和node相等时,
 * prve所指向的节点肯定是第一次出现,而node指向的指针就是所谓的重复数了。
 * 让node一直向后走,当node不等于prve时,才后prve向后走一步。
 * 注释:node肯定是每次都要向后走的,node == null结束
 */

class Solution {
    public ListNode deleteDuplicates(ListNode head) {

        //特殊情况:当head为空,或者只有一个元素的时候,肯定没有重复元素存在
        if(head == null || head.next == null){
            return head;
        }
        
        ListNode prve = head;
        ListNode node = head.next;
        
        while(node != null){
            
            if(prve.val == node.val){
                //前后相等的时候,出现重复元素
                node = node.next;
                prve.next = node;//更新prve所指向的地址
            }else{
                //两个数不相等,依次向后移动
                prve = node;
                node = node.next;
            }
        }
        return head;

    }
}
/**
 * 思路二:递归方法。他们只给我们头节点,所以我们给只处理头节点是否是重复数
 * 剩下的交给子方法去解决。 看返回节点的头节点。如果这是重复数,那么就已经被子方法
 * 删的只剩下一个了,再与原来的头节点相比较是否需要删除,如果返回的不是重复数就正常连接就可以了
 */
 public ListNode deleteDuplicates(ListNode head) {

        //终止条件:当head为空,或者只有一个元素的时候,肯定没有重复元素存在
        if(head == null || head.next == null){
            return head;
        }else{
            //到这里说明至少存在两个节点,可能会出现重复元素
            head.next = deleteDuplicates(head.next);
            if(head.val == head.next.val){
                return head.next;//正在删除还得看这步
            }
            return head;
        }

    }

力扣82——删除排序链表中的重复元素 II

力扣关于链表有关的练习题_第11张图片

力扣关于链表有关的练习题_第12张图片

/**
 * 思路一:虚拟头节点 + 三指针。之前我们删除一个链表都是找前驱,但现在头结点也有可能会是重复元素改怎么办?
 * 可以创建一个虚拟头节点,使得每个节点都具备头节点。那边怎么知道一个数是否是重复元素?
 * 可以用两种指针来比较node1和node2,当他们相等时,肯定就是了。但要求的是一个重复元素都
 * 不留,两个指针都用来判断的,改怎么删除节点呢?可以再创建 一个指针prve(前驱)来串联所有
 * 正确的节点
 */
//上下两个思路是一样的,看看留个印象
/**
 * 思路:要求是重复节点一个都不留,我们之前单链表的删除都是通过
 * 前驱节点删除 =》 现在第一个节点就有可能会是重复节点了,
 * 要保证 prve 坚决不能能带删除的节点,我们可以引用
 * 虚拟头节点的概念,如果不增加一个空节点来做,那么题目会变的非常的复制难办。
 * 但现在我们还需要 两个指针来判断是否为 重复数,那就再引入 node1,node2
 */
//注释:一定要先判断node2 != null 用while循环到不是重复数为止
    public ListNode deleteDuplicates(ListNode head){

        // //当链表为空或者 只有一个是时候,肯定没有重复元素
        if(head == null || head.next == null){
            return head;
        }
        //此时说明至少有两个节点,可能存在重复数
        ListNode dummyHead = new ListNode();//虚拟头节点
        dummyHead.next = head;//连接一下
        ListNode prve = dummyHead;//前驱
        ListNode node1 = prve.next;//遇到重复数保持不动
        ListNode node2 = node1.next;//每次向后移动
        while(node2 != null){
            if(node1.val == node2.val){
                //说明此时两个节点都是要被删除的
                while(node2 != null && node1.val == node2.val){
                    node2 = node2.next;
                }//走到不是刚遇到的重复数的时候停下来
                prve.next = node2;//更新前驱节点保存的地址
                //判断一下node2是否走到头了
                if(node2 == null)
                    return dummyHead.next;
            }else{
                //如果他们都不是重复的数
                //移动prve的位置
                prve = prve.next;
            }
            node1 = node2;
            node2 = node2.next;
        }
        return dummyHead.next;
    }

力扣关于链表有关的练习题_第13张图片

/**
 * 递归写法:功能是删除链表里面所有重复的元素,返回新链表的头
 */

/**
 * 思路:我们要做的是 必须保证头节点不是待删除元素,然后把头节点不是待删除链表
 * 交给子方法去处理,让它完成里面的重复删除。
 */
    public ListNode deleteDuplicates(ListNode head) {

        //什么情况下不用删除了?
        if(head == null || head.next == null){
            //为空,或者只有一个节点,那肯定没有重复数啊
            return head;
        }
        //判断头节点是否是待删除节点
        if(head.val != head.next.val){
            head.next = deleteDuplicates(head.next);
            return head;//因为第一个不是待删除节点,所以可以直接连接
        }else{
            //重点!如果是带删除节点的话 ,先处理掉
            //注释:我们只需要做到删除头节点带删数,至于后面的数里面有没有头节点交给方法去执行
            ListNode node = head.next;
            while(node != null && head.val == node.val){
                //重复的话就一直让node往后走
                node = node.next;
            }//循环完后node跟第一个头节点val相比,肯定不相同。
            //node是新的头节点,至于这个头节点是否需要删除,就交给子方法
            return deleteDuplicates(node);

        }
    }

力扣206——反转链表

力扣关于链表有关的练习题_第14张图片

 力扣关于链表有关的练习题_第15张图片

/**
 * 思路一:头插法。因为题目没有要求原地翻转,最简单的方法就可以创建一个新链表
 * 用一个虚拟头节点,再用一个指针去遍历原链表,遇到一个节点就new一个一模一样值
 * 的节点用来头插入虚拟头节点。这样虚拟头节点得到的链表就是反过来了的
 */

public class Num206_reverseList {
    public ListNode reverseList(ListNode head) {

        //当链表为空或者 只有一个是时候,就不需要翻转链表了
        if(head == null || head.next == null){
            return head;
        }

        ListNode dummyHead = new ListNode();
        while(head != null){
            ListNode node = new ListNode(head.val);
            node.next = dummyHead.next;
            dummyHead.next = node;
            head = head.next;
        }
        return dummyHead.next;
    }
}

/**
 * 思路二:这个就是迭代法。 原地移动链表。 所谓的链表反转就是相当于 原来的 1 -》 2 变成了 2 -》 1
 * 把每个节点的 原来的 所指向的方法反一下就可以了。
 * 用 两个指针 prve 和 cur 来实现相反,再加上一个指针 next记录下
 * 一个节点的地址,保持三指针的移动。
 * 注释:当 cur走到null的时候, 你会发现 此时的prve正好在 链表末尾上,
 * 到时候直接返回 prve就可以了
 */


/**
 * 思路二:迭代法。可以遇到一个节点就让那个节点进行头插步骤。但需要一个不动的前驱。
 * 所以创建一个虚拟头节点。再用prve和node两个节点,每遇到一个节点,prve就让他保存
 * 的地址指向下一个的下一个,遇到null为截止。node就是prve.next地址节点进行头插。
 * 然后每次都更新dummyHead链接的地址
 */

/*
public class Num206_reverseList {
    public ListNode reverseList(ListNode head) {

        //当链表为空或者 只有一个是时候,就不需要翻转链表了
        if(head == null || head.next == null){
            return head;
        }
        //此时链表至少有两个节点
        ListNode dummyHead = new ListNode();//虚拟前驱
        ListNode prve = head;//因为要让第一个节点指向null
        dummyHead.next = prve;//连接一下
        while(prve.next != null){
            ListNode node  = prve.next;
            prve.next = node.next;//跳过头插节点
            node.next = dummyHead.next;//进行头插
            dummyHead.next = node;//更新新的头节点地址
        }
        return dummyHead.next;
    }
}

/**    //这个就是迭代法
 * 思路2:原地移动链表。 所谓的链表反转就是相当于 原来的 1 -》 2 变成了 2 -》 1
 * 把每个节点的 原来的 所指向的方法反一下就可以了。
 * 用 两个指针 prve 和 cur 来实现相反,再加上一个指针 next记录下
 * 一个节点的地址,保持三指针的移动。
 * 注释:当 cur走到null的时候, 你会发现 此时的prve正好在 链表末尾上,
 * 到时候直接返回 prve就可以了
 */
class Solution {
    public ListNode reverseList(ListNode head) {

        ListNode prve = null;//因为要让第一个节点指向null
        ListNode cur = head;

        while(cur != null){
            ListNode next = cur.next;//记录下一个节点的地址,不然等会反转就找不到了
            cur.next = prve;
            prve = cur;//移动一位
            cur = next;//移位移位
        }
        return prve;

    }
}

/**
 * 思路三:运用递归法来解决。 可以这么拆分:我们每次都知道头节点,所以我们也只
 * 处理头节点的情况, 把 head.next交给子方法去解决后用 prve 接受一下返回地址,
 * 因为它就是接下来新链表的头节点了
 * 然后我们手动把头节点移动到链表末尾
 * 注释:记得先保存链表的第二个节点地址,因为等会交给子方法翻转后它就是末尾了。
 */
public class Num206_reverseList {

    public ListNode reverseList(ListNode head) {

        //什么情况下可以不用拆分,或者可以直接知道答案?
        if(head == null || head.next == null){
            return head;
        }
        //到这说明至少有两个节点了
        ListNode node = head.next;//保存地址,方便等会头节点插它后面
        ListNode prve = reverseList(head.next);//这就是新链表的头节点,保存下来
        node.next = head;
        head.next = null;//不置空 会 回环

        return prve;
    }
}

力扣92——反转链表 II

力扣关于链表有关的练习题_第16张图片

 力扣关于链表有关的练习题_第17张图片

/**
 *思路一:迭代法。找到left节点的前驱,所以需要一个虚拟头节点,循环left - 1次就可以找到
 * 第一个翻转节点到最后肯定是在末尾的,所以用指针node执行它并保持不动,每次更新它执行的地址,
 * node后面的节点都需要进行头插,用cur执行。prve就是前驱的地址。每次都要更新prve.next
 * 和 node.next的地址
 */
public class Num_92_reverseBetween {

    public ListNode reverseBetween(ListNode head, int left, int right) {

        //当链表为空或者 只有一个是时候,就不需要翻转链表了
        if(head == null || head.next == null){
            return head;
        }

        ListNode dummyHead = new ListNode();
        ListNode prve = dummyHead;
        dummyHead.next = head;

        //寻找left - 1次找到前驱位置
        for (int i = 0; i < left - 1; i++) {
            prve = prve.next;
        }//出来后此时的prve就是待翻转节点的前驱了

        ListNode node = prve.next;
        //需要翻转的次数是 right - left
        for (int i = 0; i < right - left; i++) {
            ListNode cur = node.next;//待翻转的节点
            node.next = cur.next;//跳过待翻转的节点连接下一个节点地址
            cur.next = prve.next;//头插
            prve.next = cur;//更新头插节点地址
        }
        return dummyHead.next;
    }

}

/**
 * 思路三: 定义一个静态变量,让它执行翻转后末尾节点指向的地址,保留。
 * 末尾的节点分为两种情况,第一种是 后面不存在不需要翻转的节点,那么此刻的 next就应该是null
 * 第二种是 后面存在 不需要翻转的节点,如:1~5 我只翻转 2到4,那么 5 就是不需要翻转的节点,
 * 那么此刻的 next 保存的就是 5 的地址,这种情况发生在 left == right 时刻发生。
 */
public class Num_92_reverseBetween {

    //定义一个静态变量,让它执行翻转后末尾节点指向的地址,保留。
    public static ListNode next = null;
    public ListNode reverseBetween(ListNode head, int left, int right) {

        //终止条件
        if( head.next == null || left == right ){
            next = head.next;
            return head;
        }

        //如果此时头节点就需要翻转
        if(left == 1){
            ListNode prve = head;//需要我们手动尾插的头节点
            ListNode cur = prve.next;//等会翻转链表后的尾节点
            ListNode node = reverseBetween(head.next, left, right - -1);
            cur.next = prve;
            prve.next = next;

            return node;
        }

        head.next = reverseBetween(head.next, left - 1, right - 1);
        return head;
    }

}

力扣876——链表的中间结点

力扣关于链表有关的练习题_第18张图片

力扣关于链表有关的练习题_第19张图片

/**
 * 思路一:简单的办法就是统计长度。如果我们知道链表长度为 n,那么只需要 走 n / 2 步就
 * 会刚好走到中间节点位置
 */
public class Num876_middleNode {
    public ListNode middleNode(ListNode head) {
        int conut = 0;
        //统计链表长度
        for (ListNode x = head; x != null ; x = x.next) {
            conut ++;
        }
        //找到中间节点
        for (int i = 0; i < conut / 2; i++) {
            head = head.next;
        }
        return head;
    }
}
/**
 * 思路二:快慢指针。我们引入两个引用 slow ,fast
 *  slow走一步,fast每次走两步,当 fast走到头的时候,
 *  slow刚好停留在 中间节点上
 */
public class Num876_middleNode {
    public ListNode middleNode(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;


        //只有还有下一个节点存在,指针就可以走两步不报错,
        //一步走到最后节点上,二步走到 null 不影响。
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }
}

力扣剑指offer22——链表中倒数第k个节点

力扣关于链表有关的练习题_第20张图片

 

/**
 * 思路一:总长度 - K。所谓的倒数第k个节点,就是正的数 n - k个节点。所以先统计出总长度
 * 再从头走 n - k次,就是倒数第k个的节点.就是从 head 走 n - k 步的节点
 * 这里还需要考虑k的合法性
 */
public class 剑指offer_Num22_getKthFromEnd {
    public ListNode getKthFromEnd(ListNode head, int k) {

           //边界
        if(head == null || k < 0){
            return null;
        }

        int n = 0;
        //统计链表长度
        for (ListNode x = head; x != null ; x = x.next) {
                n++;
        }
        //如果 n - k小于 0就直接不进入循环
        for (int i = 0; i < n - k; i++) {
            head = head.next;
        }
        return head;
    }
}

/**
 * 思路二:相对距离法。 引入两个引用,slow和 fast。
 * 先让 fast 走 k 步,然后两个引用再同时开始依次向后移动
 * 因为他们相距的距离是不变的,当 fast走到 null头的时候,
 * 此时的 slow正好落在 倒数第 k 个节点的位置上。
 * 不懂的可以逆着想,如果我们让一个指针落在 k 上,一个指针落在 null末尾
 * 它们同时向 左走,当那个落在 k上的指针到头节点的时候停止。一个道理。
 */
//注释:任需要考虑边界问题,k的合法性。
public class 剑指offer_Num22_getKthFromEnd {
    public ListNode getKthFromEnd(ListNode head, int k) {

        //边界
        if(head == null || k < 0){
            return null;
        }

        ListNode slow = head;
        ListNode fast = head;

        for (int i = 0; i < k; i++) {

            if(fast == null){
                //k的合理性 如果还没移动完就走到头了,k肯定是超标了
                return head;
            }
            fast = fast.next;
        }

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

        return  slow;
    }
}

力扣234——回文链表

力扣关于链表有关的练习题_第21张图片

力扣关于链表有关的练习题_第22张图片

/**
 * 思路一: 开辟一个新链表,链表的内容就是元链表的反转。
 *  然后两个链表依次比较到 null 如果全部都相等,就说明是回味链表。
 */
public class Num234_isPalindrome {

    public boolean isPalindrome(ListNode head) {

        ListNode p1 = reverseList(head);
        ListNode p2 = head;

        while(p1 != null || p2 != null){

            if(p1.val != p2.val){
                return false;
            }
            p1 = p1.next;
            p2 = p2.next;
        }

        return true;
    }

    //头插法
    public ListNode reverseList(ListNode head) {
        ListNode dummyHead = new ListNode();

        while(head != null){
            ListNode node = new ListNode(head.val);//每次的新节点
            node.next = dummyHead.next;
            dummyHead.next = node;

            head = head.next;
        }

        return dummyHead.next;
    }

}
/**
 * 思路二:反转链表的中间部分。可以先用之前的快慢指针找到链表
 * 的中间节点位置,然后在这个节点往后的链表进行反转,然后依次比较
 */
public class Num234_isPalindrome {

    public boolean isPalindrome(ListNode head) {
        //先找到中间节点的地址
        ListNode prve = middleNode(head);
        //反转链表中间位置及以后的链表
        ListNode node = reverseList(prve);

        //这里的条件也可以设置成 node != null
        //比如是 1~5,虽然说它中间节点反转的节点多一个,
        //但 头链表的2后面还是连接在那个中间节点 3 上面的
        while(head != prve){

            if(head.val != node.val){
                return false;
            }

            head = head.next;
            node = node.next;
        }
        return true;
    }
    //翻转链表
    public ListNode reverseList(ListNode head) {

        //当链表为空或者 只有一个是时候,就不需要翻转链表了
        if(head == null || head.next == null){
            return head;
        }
        //此时链表至少有两个节点
        ListNode dummyHead = new ListNode();//虚拟前驱
        ListNode prve = head;//因为要让第一个节点指向null
        dummyHead.next = prve;//连接一下
        while(prve.next != null){
            ListNode node  = prve.next;
            prve.next = node.next;//跳过头插节点
            node.next = dummyHead.next;//进行头插
            dummyHead.next = node;//更新新的头节点地址
        }
        return dummyHead.next;
    }

    //返回链表的中间节点地址,用的是快慢指针的方法
    public ListNode middleNode(ListNode head) {

        ListNode slow = head;
        ListNode fast = head;

        //只有还有下一个节点存在,指针就可以走两步不报错,
        //一步走到最后节点上,二步走到 null 不影响。
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }
}

力扣21——合并两个有序链表

力扣关于链表有关的练习题_第23张图片

力扣关于链表有关的练习题_第24张图片 

/**  迭代法
 *思路一: 要求新链表是原链表拼接而成,说明这里不允许创建新节点。
 * 搞一个虚拟节点dummyHead,然后用prve引用连接正确的一次然后,
 * 向后移。 如果有一个走到空要记得连接另一个
 */
//注释要判空,不然会空引用

 

public class Num_21_mergeTwoLists {

    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {

        //判空
        if(list1 == null){
            return list2;
        }
        //判空
        if(list2 == null){
            return list1;
        }

        ListNode dummyHead = new ListNode();//虚拟头节点
        ListNode prve = dummyHead;

        while (list1 != null || list2 != null){

            if(list1.val <= list2.val){
                prve.next = list1;
                list1 = list1.next;//后移
            }else{
                prve.next = list2;
                list2 = list2.next;//后移
            }
            prve = prve.next;//后移
        }

        //判空
        if(list1 == null){
            prve.next = list2;
        }
        //判空
        if(list2 == null){
            prve.next = list1;
        }

        return dummyHead.next;
    }

}
/**
 * 思路二:递归法。 每次只判断头节点的大小,剩下的交给子方法去处理,并返回地址
 */
public class Num_21_mergeTwoLists {

    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {

        //终止条件
        if(list1 == null){
            return list2;
        }
        if(list2 == null){
            return list1;
        }

        if(list1.val <= list2.val){

            list1.next = mergeTwoLists(list1.next, list2);
            return list1;
        }

        list2.next = mergeTwoLists(list1, list2.next);
        return list2;

    }
}

力扣面试题02.04—— 分割链表

力扣关于链表有关的练习题_第25张图片

力扣关于链表有关的练习题_第26张图片 

/**
 * 思路一:创建两个新链表。这两个链表就是由原链表组成的。
 * 一个链表存放小于 x 的节点。
 * 一个链表存放大于等于 x 的节点
 * 然后小链表连接大链表就成功了。
 */

 

public class 面试题_Num02_04_partition {

    public ListNode partition(ListNode head, int x) {
        //边界--判空
        if(head == null || head.next == null){
            return head;
        }

        //存放小于x的链表
        ListNode dummyHead1 = new ListNode();
        ListNode prve1 = dummyHead1;//尾节点,用于连接

        //存放大于等于x的链表
        ListNode dummyHead2 = new ListNode();
        ListNode prve2 = dummyHead2;//尾节点,用于连接

        //方法1 new新的链表
//        while(head != null){
//            if(head.val < x){
//                prve1.next = new ListNode(head.val);
//                prve1 = prve1.next;//后移
//            }else{
//                prve2.next = new ListNode(head.val);
//                prve2 = prve2.next;//后移
//            }
//            head = head.next;
//        }

        //方法2 原地修改
        while(head != null){
            if(head.val < x){
                prve1.next = head;
                prve1 = prve1.next;
                head = head.next;

                prve1.next = null;//切断后面的连接

            }else{
                prve2.next = head;
                prve2 = prve2.next;
                head = head.next;

                prve2.next = null;//切断后面的连接

            }
        }
        //小链表连接大链表
        prve1.next = dummyHead2.next;

        return dummyHead1.next;
    }
}

力扣160——相交链表​​​​​​​

力扣关于链表有关的练习题_第27张图片

力扣关于链表有关的练习题_第28张图片 

力扣关于链表有关的练习题_第29张图片 

力扣关于链表有关的练习题_第30张图片 

力扣关于链表有关的练习题_第31张图片 

/**

 * 思路一:路径相等。

 * 我们假设A链表不相等的为 a ,相等路径为 c

 * 我们假设B链表不相等的为 b ,相等路径为 c

 * 整个A链表路径 = a + c, 整个B链表路径 = b + c

 * 如果此时引入两个指针,指针A走完A链表路径再走B链表不相等路程

 * 指针B走完B链表路径再走A链表不相等路程

 * 两个指针的总路径为: a + c + b = b + c + a; 如果它们相交,此刻肯定在

 * 第一个相交的节点上,因为路径相等。

 * 如果是不想交的话此刻两个链表值都为 null

 */

 力扣关于链表有关的练习题_第32张图片

 

    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {

        ListNode l1 = headA;
        ListNode l2 = headB;

        //地址相等的话就结束循环
        while(l1 != l2){
            l1 = (l1 == null) ? headB : l1.next;
            l2 = (l2 == null) ? headA : l2.next;
        }

        //走到null什么不相交
        if(l1 == null ){
            return null;
        }

        return l1;
    }

力扣141——环形链表

力扣关于链表有关的练习题_第33张图片

力扣关于链表有关的练习题_第34张图片 

力扣关于链表有关的练习题_第35张图片 

 /**

 * 思路:用快慢指针。创建两个引用slow和fast。

 * slow一次走一步,fast一次走两步。当slow进入环形节点时,

 * 当slow走当一半路径时,不管当时的fast在哪肯定会超过slow。

 * 因为fast比slow快一倍,slow走一半,fast肯定走完了一圈,

 * 如果是环形必然会路过。

 */

public class Solution {
    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;
    }
}

力扣142——环形链表 II

力扣关于链表有关的练习题_第36张图片

力扣关于链表有关的练习题_第37张图片 

/**
 * 思路:假设环形节点前的路径为 a;
 * 环形入口的节点 到 快慢指针在环内相交时的节点距离为 b
 * 环形内剩下的距离为 c。
 * 那么快指针的路径为 a + N(b + c) + b;(N表示走了N圈)
 * 那么慢指针的路径为 a + b;(因为慢指针在进入环的时候走一半就肯定会被快指针追上)
 * 快指针的路径是慢的两倍得出等式:  a + N(b + c) + b = 2(a + b);
 * 化简一下: N(b + c) = a + b,再化简一下,从N里面提出一个 b + c;
 *
 * 等于 (N - 1)(b + c) + c = a;
 * 重点来了!:(N - 1)(b + c)其中(b + c)是一圈的路径
 * (N - 1)(b + c)这个只是表示循环了的圈数,走了也等于白走,会回归原地,路程相当于是 0
 * 当一个指针从头节点开始走,慢指针从b点上开始走,当两个指针相遇时,必然是入口节点。
 * 因为 (N - 1)(b + c) + c = a; 路径是相等的。不懂的话你可以先
 * 假设 如果 N == 1的话 a = c
 */​​​​​​​

public class Solution {
    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 prve = head;
                while(prve != slow){
                    prve = prve.next;
                    slow = slow.next;
                }//循环完后就相遇了
                return prve;
            }
        }

        return null;
    }
}

你可能感兴趣的:(力扣题目,关于链表的题目,java,力扣)