代码随想录算法训练营|DAY4|24.两两交换链表|19.删除倒数第n个结点|相交链表|环形链表

24.两两交换链表

刚开始做的时候没有定义虚拟头结点,写得比较乱。

  • 定义一个dummy,让它指向head。
  • 用临时变量存两个值:1.dummy下一个结点的地址值。2.两两交换部分的下一个结点的地址值。
  • 思考:为什么需要这两个临时变量?
  • 交换结点的意义在于:交换node1和node2的next域里的地址值。如图中node1的next里是100,node2里的是150。
  • 第一步。dummy里的next改成100(这个100是node2的地址,表示dummy下一个结点变成node2)
  • 第二步。我们现在已经把node2结点换到dummy的下一个了!接下来需要让node1结点变成node2的下一个结点,怎么做呢,就是把node2的next域里的地址修改成node1结点的地址(这里就需要用到我们刚刚保存的临时变量了,图中tmp1=50,50就是node1结点的地址值),那么我们修改node2.next=50,到此,node1就变成node2的下一个结点了。
  • 第三步。到这里还没结束,我们完成了结点的交换,还需要把链表给连起来对吧,所以node1.next我们也需要改变,改成原来node2的next。但是刚刚node2.next已经被我们改成50了!所以这个时候临时变量的用处又来了,还好我们保存了一份,所以node1.next=tmp2。链表又被重新的连接起来了。
  • 第四步。让cur指针移动到node1的位置。继续上述操作!

在这里插入图片描述

19.删除倒数第n个结点

这里要考虑几个边界情况!一开始我没有考虑到。

  • 倒数第n个节点是头结点
  • 倒数第n个节点是尾结点
    判断逻辑:
 if (n == ListLen) {
            head=head.next;
            dummy.next=head;
        }else if(n==1){
            cur.next=null;
        }else {
            //删除操作
            .......
        }
/**
 * 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) {
        if(head==null){
            return null;
        }
        if(n==1){

        }
        ListNode p=head;
        int ListLen=0;
        ListNode dummy=new ListNode(0);
        dummy.next=head;
        ListNode cur=dummy;
        //记录链表长度
        while(p!=null){
            p=p.next;
            ListLen++;
        }
        for(int i=0;i<ListLen-n;i++){
            cur=cur.next;
        }
        //判断倒数第n个结点是否是头尾结点
        if (n == ListLen) {
            head=head.next;
            dummy.next=head;
        }else if(n==1){
            cur.next=null;
        }else {
            cur.next = cur.next.next;
        }
        return dummy.next;
    }
}

160.相交链表

我们知道链表的每一个结点在内存中都是有一个独立地址的。
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/e7ef04a07e054ff090adff773144bd83.png像上图,节点1和节点3的next域里面存储的地址相等,假设是100,也就是说它们会一同指向这个空间里地址为100的节点,也就是2(假设它的地址值是100)。这个结点在这个空间里肯定是唯一的,2会指向下一个节点。。。后面都是公共的了
我们只需要用两个指针分别遍历两个链表,只要当这两个指针指向了同一个地址值(换句话说就是两个指针的值相等)我们就可以说找到了相交的第一个节点。
上面这个说法只适用于长度一样的链表,因为长度一样,步子都是一样的,走到相交点的步子肯定一样。
但如果是长度不一样的链表,我们就要计算他们的差值,加入说A表比B表长2,那么我们为了让他们保持同一步子走到相交节点,我们要让A先走两步。(因为假如他们相交了,那相交之后的长度是一样的,也就是说A和B不相交的部分,A比B长2,A走完这两步,就可以和B同一起点开始走向他们相交的点)

/**
 * 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) {
          if(headA==null||headB==null){
            return null;
        }

        //计算两个链表的长度
        ListNode curA1=headA;
        ListNode curB1=headB;

        ListNode curA2=headA;
        ListNode curB2=headB;

        //记录两个链表的长度
        int listlenA=0;
        int listlenB=0;
        while(curA1!=null){
            curA1=curA1.next;
            listlenA++;
        }
          while(curB1!=null){
            curB1=curB1.next;
            listlenB++;
        }
        int diff=0;//记录两个链表的长度差
        int flag=0;//0表示A长,1表示B长
        if(listlenA>listlenB){
            diff=listlenA-listlenB;
        }
        if(listlenB>listlenA){
            diff=listlenB-listlenA;
            flag=1;//B更长
        }
        //如果两个链表没有长度差,可以从头开始遍历
        if(diff==0){
            while(curB2!=null&&curA2!=null){
                if(curA2!=curB2){
                    //指针往后移动
                    curA2=curA2.next;
                    curB2=curB2.next;
                 }else{
                    return curA2;
                 }
            }
        //两个链表存在长度差,需要让长的那个链表先走几步,走到和短的链表同一位置
        }else{
            //如果链表A更长,就链表A先走
            if(flag==0){
                while(diff!=0){
                    curA2=curA2.next;
                    diff--;
                }
            //如果链表B更长,就链表B先走
            }else{
                 while(diff!=0){
                    curB2=curB2.next;
                    diff--;
                }
            }
            //两个链表从同一起点开始遍历
            while(curB2!=null&&curA2!=null){
            if(curA2!=curB2){
                curA2=curA2.next;
                curB2=curB2.next;
            }else{
                return curA2;
            }
        }
        }
        return null;
    }
}

142.环形链表

在这里插入图片描述
自己写没什么思路。看了解答。
在这里插入图片描述

假设头节点到环入口的距离是m,入口到相遇点的距离是y,相遇点走回路口的距离是x,环的长度是n
快指针的速度是慢指针的两倍
一定是快指针先进环里绕圈等慢指针。慢指针第一次进环的时候它们就会相遇
设快指针绕了k圈.
快指针走过的路程=m+kn+y
慢指针走过的路程=m+y
m+kn+y=2(m+y)
化简得
m=kn-y=kn-(n-x)=(k-1)n+x
(k-1)n表示运动了n圈,(k-1)n+x表示绕了(k-1)圈后又走了x
所以定义两个指针,一个在head,一个放在相遇点,同步出发
等到他们相遇的时候,就是环的入口。

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 index1 = fast;
                ListNode index2 = head;
                // 两个指针,从头结点和相遇结点,各走一步,直到相遇,相遇点即为环入口
                while (index1 != index2) {
                    index1 = index1.next;
                    index2 = index2.next;
                }
                return index1;
            }
        }
        return null;
    }
}
  • 8
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值