代码随想录算法训练营Day4|24.两两交换单链表节点、19.删除单链表的倒数第N个节点、160.单链表相交,求起始相交节点

24.两两交换单链表节点(双指针)

package com.liqi.day.day4;

import com.liqi.day.day3.ListNode;

public class LeetCode_24 { //给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点 你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)
    public static void main(String[] args) {

    }

    class Solution {
        public ListNode swapPairs(ListNode head) {
            ListNode dummy = new ListNode(0, head);
            ListNode temp;
            ListNode firstNode;
            ListNode secondNode;
            ListNode cur = dummy;// cur一定得是第一个节点的前一个节点,不然无法修改
            while (cur.next != null && cur.next.next != null) {
                // 循环终止条件为cur后面不在有两两成对的节点
                temp = cur.next.next.next;
                firstNode = cur.next;
                secondNode = cur.next.next;
                cur.next = secondNode;
                secondNode.next = firstNode;
                firstNode.next = temp;
                cur = firstNode;  //todo  第一次写的时候忘记每次循环的变量了,即cur要移动
            }

            return dummy.next; //这里不能return head  因为使用了虚拟头节点,虚拟头节点的next才是链表真正的head
            //return head; 如果当我们的操作改变了原来的head的话得到的结果就是错误的
        }

    }

}

总结:使用虚拟头节点记得返回的是虚拟头节点的next而不是原来的head,原来的head可能会被修改,二刷要做一下递归的解法

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

package com.liqi.day.day4;

import com.liqi.day.day3.ListNode;

public class LeetCode_19 {  //19.删除链表的倒数第N个节点   todo  1 <= n <= size
    public static void main(String[] args) {

    }

    class Solution {
        public ListNode removeNthFromEnd(ListNode head, int n) {
            //给你一个单链表,要求删除链表中的倒数第N个节点并返回
            //思路:双指针,快指针遍历到尾节点,慢指针比快指针慢走N个节点(因为删除倒数第N个节点要拿到它的前一个节点)
            //这里涉及到删除节点,为了统一我们仍然是使用虚拟头节点
            ListNode dummy = new ListNode(0);
            dummy.next = head;

            ListNode cur = dummy;
            ListNode slow = dummy;
            //因为题目已经给出条件  1<=n<=size 所以直接for循环先让快指针比慢指针多走n个节点
            for (int i = 0; i < n; i++) { //这里是<n 不是<=n  因为<=n 意思就是快指针比慢指针多走n+1步了
                cur = cur.next;
            }
            while (cur.next != null) {
                cur = cur.next;
                slow = slow.next;
            }

            //while循环退出时slow指向的下个节点就是我们要删除的节点
            slow.next = slow.next.next;
            return dummy.next;//返回链表的头节点
        }
    }

}

总结:判断清楚快指针多走的步数是n-1 还是n还是n+1即可

160.单链表相交,求起始相交节点

package com.liqi.day.day4;

import com.liqi.day.day3.ListNode;

public class LeetCode_160 { //160.链表相交, 给你两个单链表的头节点 headA 和 headB ,
    // 请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
//    题目数据 保证 整个链式结构中不存在环。
//注意,函数返回结果后,链表必须 保持其原始结构
    public static void main(String[] args) {
    }

    public static class Solution {
        public static ListNode getIntersectionNode(ListNode headA, ListNode headB) {
            //目标:时间复杂度o(n) 空间复杂度:o(1)  思路 使用双指针(一个指向链表A 一个指向链表B) 单循环遍历
            //因为不涉及链表节点的位置或者结构变动,所以不需要使用虚拟头节点
            ListNode curA = headA;
            ListNode curB = headB;

            //第一遍自己做的时候老是想着如果先分别遍历链表A和B求出长度然后再遍历寻找想交起始节点时间复杂度就不是o(n)了
            //但其实没有嵌套在一起的多个循环最终的时间复杂度仍然是o(N)
            //先分别求出链表A和B的长度
            int sizeA = 1;
            int sizeB = 1;
            while (curA.next != null) {
                curA=curA.next;
                sizeA++;
            }

            while (curB.next != null) {
                curB=curB.next;
                sizeB++;
            }
            curA=headA;
            curB=headB;

            //让curA指向两个链表中长度大的那一个,
            // todo  回归老韩过关斩将思想,所有符合条件的情况等于排除不符合条件的特殊情况(符合条件的情况不好找的时候就用过关斩将法)
            if (sizeB > sizeA) {
                int tempSize=sizeB;
                sizeB=sizeA;
                sizeA=tempSize;
                ListNode temp=curB;
                curB=curA;
                curA=temp;
                }

            int gap=sizeA-sizeB;

            for(int i=0;i<gap;i++){
                curA=curA.next;    //让两个链表的起点到终点的长度相等
            }

            while (curA!=null) {//同时移动两个指针并比较内存地址是否相等
                if (curA == curB) {//注意这里的交点指的是节点的内存地址相同而不是单纯值相同
                    return curA;
                }
                curA = curA.next;
                curB = curB.next;
            }

            return null;
        }
    }

总结:有了思路后写代码要细心,中间一个小步骤错了可能就会导致死循环,要得到两个链表中长度大的那一个可以直接默认某个指针就是指向最大的链表,然后使用代码进行相应的逻辑处理(参考代码片段中的if sizeB>sizeA部分)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值