剑指 Offer II 链表

ListNode“变量”是引用 是指向该对象的遥控器 不真的是该对象

021. 删除链表的倒数第 n 个结点

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        int m=0;
        ListNode dummy=new ListNode(-1);
        dummy.next=head;
        //while(head!=null)
     for(ListNode p=dummy;p!=null;p=p.next)m++;//包含dummy是 实际链表长度+1
     ListNode p=dummy;
      for(int i=0;i<m-n-1;i++)p=p.next;  
        p.next=p.next.next;
        return dummy.next;

    }
}

022链表中环的入口节点 第一次见这种做法:快慢指针扫描

public class Solution {
    
    public ListNode detectCycle(ListNode head) {
        if(head==null||head.next==null)return null;
        ListNode first=head;
        ListNode second=head;//一次走两步
        while(first!=null&&second!=null)
        {
            first=first.next;
            second=second.next;
            if(second!=null)second=second.next;
            else return null;

            if(first==second)
            {
                first=head;
                while(first!=second)
                {
                    first=first.next;
                    second=second.next;
                }
                return first;
            }
        }
        return null;
    }
}

如上图所示,aa 是起点,bb 是环的入口,cc 是两个指针的第一次相遇点,abab 之间的距离是 xx,bcbc 之间的距离是 yy。
另外感谢@watay147提供的另一种思路,可以用公式来说明:a,b,c,x,ya,b,c,x,y 的含义同上,我们用 zz 表示从 cc 点顺时针走到 bb 的距离。则第一次相遇时 secondsecond 所走的距离是 x+(y+z)∗n+yx+(y+z)∗n+y, nn 表示圈数,同时 secondsecond 走过的距离是 firstfirst 的两倍,也就是 2(x+y)2(x+y),所以我们有 x+(y+z)∗n+y=2(x+y)x+(y+z)∗n+y=2(x+y),所以 x=(n−1)×(y+z)+zx=(n−1)×(y+z)+z。那么我们让 secondsecond 从 cc 点开始走,走 xx 步,会恰好走到 bb 点;让 firstfirst 从 aa 点开始走,走 xx 步,也会走到 bb 点。

时间复杂度分析:firstfirst 总共走了 2x+y2x+y 步,secondsecond 总共走了 2x+2y+x2x+2y+x 步,所以两个指针总共走了 5x+3y5x+3y 步。由于当第一次 firstfirst 走到 bb 点时,secondsecond 最多追一圈即可追上 firstfirst,所以 yy 小于环的长度,所以 x+yx+y 小于等于链表总长度。所以总时间复杂度是 O(n)O(n)。

作者:yxc
链接:https://www.acwing.com/solution/content/241/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

023. 两个链表的第一个重合节点

思路难想 一条走到尾的话从另一条开始走

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode p=headA,q=headB;
        while(p!=q)
        {
            p= p==null ? headB:p.next;//p走到头了吗? 有的话从b开头走
            q= q==null ? headA:q.next;
        }
        return p; 
    }
}

024. 反转链表 这道我做了好多遍还是忘~~~

a b两指针从head开始每次移动一位
    public ListNode reverseList(ListNode head) {
        if(head==null)return head;
        ListNode a=head,b=head.next;
        while(b!=null)//当b指向空 a是尾节点
        {
            ListNode c=b.next;//temp
            b.next=a;//我只反a b之间的 方向 最后再让第一个a指向空 
            a=b;b=c;//把两个指针往后移动一格
        }
        head.next=null;
        return a;
    }
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head==null||head.next==null)return head;
        ListNode tail=reverseList(head.next);//会返回一个 以原来尾为头 原来head.next为尾的反转过的链表
        head.next.next=head;
        head.next=null;
        return tail;   
    }
}

025. 链表中的两数相加

 public ListNode rev(ListNode head)
    {
         ListNode a=head,b=head.next;
        while(b!=null)
        {
            ListNode c=b.next;
            b.next=a;
            a=b;b=c;
        }
        head.next=null;
        return a;
    }
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        l1=rev(l1);l2=rev(l2);
        ListNode ans=new ListNode(-1);
        int t=0;
        while(l1!=null||l2!=null||t!=0)
        {
            if(l1!=null)
            {
                t+=l1.val;
                l1=l1.next;
            }
            if(l2!=null){
                t+=l2.val;
                l2=l2.next;
            }
            ListNode c=new ListNode(t%10);
            t/=10;
            c.next=ans.next;
            ans.next=c;  
        }
        return ans.next;
    }

026. 重排链表

class Solution {
    public ListNode rev(ListNode x)
    {
        ListNode a=x,b=x.next;
        while(b!=null)
        {
            ListNode c=b.next;
            b.next=a;
            a=b;b=c;
        }
        x.next=null;
        return a;
    } 
    public void reorderList(ListNode head) {
        if(head==null||head.next==null)return ;
        /*
        1.找中点
        2.把(n+1)/2后的点反转
        3.合并
        */
        int n=0;
        for(ListNode p=head;p!=null;p=p.next)n++;
        ListNode p=head;
        for(int i=0;i<(n-1)/2;i++)p=p.next;//n/2次 5-》跳2次 4-》跳1次
        ListNode q=p.next;//p q中间两个点
        q=rev(q);
        p.next=q;

        while(head!=null&&q!=null)//前后两个指针走
        {
            ListNode H=head.next,Q=q.next;
            第一段的尾连上第二段头的next
            p.next=q.next;
           
            插入
            q.next=head.next; 
            head.next=q;

            head=H;     q=Q;
        }   
    }
}

027. 回文链表

class Solution {
    ListNode rev(ListNode x)
    {
        if(x.next==null||x==null)return x;
        ListNode tail=rev(x.next);
       x.next.next=x;
        x.next=null;
        return tail;
    }
    public boolean isPalindrome(ListNode head) {
        List<Integer>a=new ArrayList();
        ListNode p=head;  
         while(p!=null)
        {  
             //System.out.println(p.val);
            a.add(p.val);
            p=p.next; 
        }
       //   System.out.println(a.get(0));
    head=rev(head);
      int t=0;
      boolean flag=true;
      while(head!=null)
      {
          if(!a.get(t).equals(head.val)) flag=false;  
           t++;
          head=head.next;
      }
      return flag;
    }
}

028. 展平多级双向链表

class Solution {
    public Node flatten(Node head) {
        if(head==null||(head.next==null&&head.child==null))return head;
       Node p;
       if(head.child==null){
           p=flatten(head.next);//处理好扁平的链表
            head.next=p;
            p.prev=head;
       }  
       else
       {       
           p=flatten(head.child);
          Node q=p;
          while(q.next!=null)q=q.next;//找最后一个点
          q.next=head.next;
       if(head.next!=null)   head.next.prev=q;//太细了 wa在这里

           head.next=p;
           p.prev=head; 

           head.child=null;
       }
        return head;
    }
}
posted @ 2022-02-28 15:06  liv_vil  阅读(28)  评论(0)    收藏  举报