寒假算法打卡第四天

本文详细讲解了链表中的关键操作,包括使用虚拟头结点实现两两交换节点,递归方法删除倒数第N个节点,判断链表相交,以及寻找环形链表的入口。通过快慢指针策略,作者演示了解决这些问题的有效算法
摘要由CSDN通过智能技术生成

学习目标:

第二章链表

  • 24.两两交换链表中的节点
  • 19.删除链表的倒数第N个节点
  • 面试题0207链表相交
  • 142.环形链表Il

学习内容:

24.两两交换链表中的节点
(1)虚拟头结点
思路:设置虚拟头结点保证所有节点都是进行一样的操作,利用两个中间指针完成交换操作

public ListNode swapPairs_01(ListNode head) {
            ListNode cur = new ListNode();
            ListNode dummyhead = new ListNode();
            ListNode temp = new ListNode();
            ListNode temp1 = new ListNode();
            dummyhead.next = head;
            cur = dummyhead;
            while(cur.next != null && cur.next.next != null){
                  temp = cur.next;
                  temp1 = cur.next.next.next;

                  cur.next =cur.next.next;
                  cur.next.next = temp;
                  temp.next = temp1;
                  cur = cur.next.next;
            }
            return dummyhead.next;
      }

(2)递归算法
思路:在原链表上直接进行交换操作,主要逻辑是:将头结点的位置移动到next的下一节点从而完成交换操作,例如[1,2,3]会变为[1,2,1,3]头结点位置移动到2后面了,也就是说2后面是新的头结点head,通过递归逆序完成交换操作也就是先完成后面节点的操作再完成前面节点的交换操作

   public ListNode swapPairs_02(ListNode head){
            //递归解法
            // base case 退出提交
            if(head == null || head.next == null) return head;
            // 获取当前节点的下一个节点
            ListNode next = head.next;
            // 进行递归
            ListNode newNode = swapPairs_02(next.next);//从后往前交换,例如[1,2,3,4]先交换3,4再退出递归交换1,2
            // 这里进行交换
            next.next = head;//也就是将头结点的位置移动到next的下一节点从而完成交换操作,例如[1,2,3]会变为[1,2,1,3]头结点位置移动到2后面了,也就是说2后面是新的头结点head
            head.next = newNode;//在新的头结点位置的下一节点就是另外一个递归中的节点

            return next;
      }

19.删除链表的倒数第N个节点
(1)思路:设置快慢指针,让快指针先走n+1步,然后快慢指针一起走,就可以保证当快指针走到null时,慢指针停在倒数第n个数据的前一个数据,因为进行删除操作需要将前一个节点的next指向后一个节点。

public ListNode removeNthFromEnd(ListNode head, int n) {
            //设置快慢指针,让快指针先走n+1步,然后快慢指针一起走,就可以保证当快指针走到null时,慢指针停在倒数第n个数据的前一个数据,因为进行删除操作需要将前一个节点的next指向后一个节点
            ListNode fast = new ListNode();
            ListNode slow = new ListNode();
            ListNode dummyhead = new ListNode();
            dummyhead.next = head;
            fast = dummyhead;
            slow = dummyhead;

            n++;//先走n+1步
            while(n-- > 0 && fast != null){
                  fast = fast.next;//快指针先走
            }
            while(fast != null){//快慢指针一起走
                  fast = fast.next;
                  slow = slow.next;
            }
            slow.next = slow.next.next;//进行删除操作

            return dummyhead.next;
      }


面试题0207链表相交
思路:要找到交点,就要先判断哪个链表长一点,然后将两个链表的末尾对齐,因此长的链表需要优先移动直至长度相同。为确保统一操作,固定为A大于等于B的链表长度,并且增加判断条件若B>A那就将长度和链表均进行交换。末尾对其后AB指针同时移动判断节点是否相等,若相等则返回该节点,否则循环结束后返回null

   public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
            //要找到交点,就要先判断哪个链表长一点,然后将两个链表的末尾对齐,因此长的链表需要优先移动直至长度相同
            //为确保统一操作,固定为A大于等于B的链表长度,并且增加判断条件若B>A那就将长度和链表均进行交换
            //末尾对其后AB指针同时移动判断节点是否相等,若相等则返回该节点,否则循环结束后返回null
            ListNode curA = new ListNode();
            ListNode curB = new ListNode();
            ListNode curT = new ListNode();
            //初始化时均指向头结点
            curA = headA;
            curB = headB;

            int lenA = 0;
            int lenB = 0;
            int temp = 0;
            int n = 0;
            //统计链表长度
            while(curA != null){
                  curA = curA.next;
                  lenA++;
            }
            while(curB != null){
                  curB = curB.next;
                  lenB++;
            }
            //重新初始化为头结点位置
            curA = headA;
            curB = headB;

            //进行交换链表
            if(lenB > lenA){
                  //长度交换
                  temp = lenA;
                  lenA = lenB;
                  lenB = temp;

                  //节点交换
                  curT = curA;
                  curA = curB;
                  curB = curT;
            }
            //计算长度差,以便于知道后面需要提前移动多少个位置
            n = lenA - lenB;

            //提前移动直至末尾对齐
            while(n-- > 0 && curA != null){
                  curA = curA.next;
            }
            //判断是否存在相同的节点
            while(curA != null && curB != null){
                  if(curA == curB){
                        return curA;
                  }
                  curA = curA.next;
                  curB = curB.next;
            }

            return null;
      }

142.环形链表Il
找到环路思路:设置快慢指针,快指针走两格一次,慢指针一格一次,若快慢指针相遇则说明一定存在环;
找到入口思路:设环外路程为x,进入环到相遇的路程为y,剩下环的路程为z.因为快指针先进入环,那么等到慢指针进入环时快指针已经走了n圈(n>1)在慢指针走第一圈时一定会与快指针相遇,因为若慢指针走完第一圈,快指针的路程是慢指针的两倍,因此第一圈时快指针一定会与慢指针相遇才能超过慢指针。
因此当快慢指针相遇时,快指针总路程为慢指针的两倍,也就是 2*(x+y) = x + n*(y+z) + y,化简可得x + y = n*(y+z),
所以x = n*(y+z) - y = (n-1) * (y+z) + z,由此可见将n=1代入可得x=z;
从相对位置来看,每走完一圈都是回到入口,根据上述化简后的式子可知,从头结点走到入口的位置跟从相遇位置走到入口位置的路程是一样的
所以index2记录头结点位置,index1记录相遇位置,他们同时像后面的节点移动,直到他们相遇就说明到达环的入口位置

   public ListNode detectCycle(ListNode head) {
            //设置快慢指针,快指针走两格一次,慢指针一格一次,若快慢指针相遇则说明一定存在环
            //找到入口思路:设环外路程为x,进入环到相遇的路程为y,剩下环的路程为z.因为快指针先进入环,那么等到慢指针进入环时快指针已经走了n圈(n>1)
            //在慢指针走第一圈时一定会与快指针相遇,因为若慢指针走完第一圈,快指针的路程是慢指针的两倍,因此第一圈时快指针一定会与慢指针相遇才能超过慢指针
            //因此当快慢指针相遇时,快指针总路程为慢指针的两倍,也就是2*(x+y) = x + n*(y+z) + y
            //化简可得x + y = n*(y+z),所以x = n*(y+z) - y = (n-1) * (y+z) + z,由此可见将n=1代入可得x=z;
            //从相对位置来看,每走完一圈都是回到入口,根据上述化简后的式子可知,从头结点走到入口的位置跟从相遇位置走到入口位置的路程是一样的
            //所以index2记录头结点位置,index1记录相遇位置,他们同时像后面的节点移动,直到他们相遇就说明到达环的入口位置
            ListNode fast = new ListNode();
            ListNode slow = new ListNode();
            ListNode index1 = new ListNode();
            ListNode index2 = new ListNode();

            fast = head;
            slow = head;
            index2 = head;

            while(fast != null && fast.next != null){
                  fast = fast.next.next;//快指针两格
                  slow = slow.next;//慢指针一格
                  if(fast == slow){
                        index1 = fast;//记录相遇位置
                        while(index1 != index2){//同时前进直到相遇返回值便是入口位置
                              index1 = index1.next;
                              index2 = index2.next;
                        }
                        return index1;
                  }
            }
            return null;
      }

学习时间:

下午四小时。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值