顺序表与链表-----进阶

常见OJ题:

1.现在有一个链表的头指针ListHead,用户输入一个固定的值,编写一段代码将所有小于X的节点排在其余结点之前,况且是不可以改变原来的数据顺序,返回重新排列之后链表的头指针;最后的链表,左边都小于X右边都大于X(原来的X不一定在链表当中)

思路:

1)我们现在来写一个循环,定义一个current来遍历原来的单链表,循环条件是current!=null

2)在循环里面,我们要做两件事(我们要创建两个单链表A和B,A是存放比X小的节点,B是存放比X大的节点)

3)在每一个链表里面,都要定义头节点和尾节点,HeadA和TailA,HeadB和TailB;我们让头节点保持不动,每次进行插入元素时,只进行移动链表的尾巴节点;

把比X小的数据都放到HeadA里面,比X大的数据都放到HeadB里面

4)出循环之后,会出现三种情况

在原链表中,既有比X小的数据,也有比X大的数据,所以HeadA与HeadB里面都有数据,这是我们可以让TailA.next=HeadB;

在原链表中,只有比X大的数据,这时HeadA指向的链表就是空的

在原链表中,只有比X小的数据,这时HeadB指向的链表就是空的

5)出了链表之后,还有一种极端情况,HeadB里面只有一个元素(HeadB=current),此时TailA.next=HeadB;这是一个新的链表就合成成功了,但是此时最后一个结点的Next值不是空,所以我们还要手动置为空;

  public ListNode partition(ListNode head, int x) {
         ListNode HeadA=null;
      ListNode HeadB=null;
      ListNode TailA=null;
      ListNode TailB=null;
      ListNode current=head;
      while(current!=null)
      {
          if(current.val<x){
              if(HeadA==null)
              {
                  HeadA=current;
                  TailA=current;     
              }else{
                  TailA.next=current;
                  TailA=TailA.next;     
              }
          }else 
          {
              if(HeadB==null)
              {
                  HeadB=current;
                  TailB=current;     
              }else{
                  TailB.next=current;
                TailB=TailB.next;
              }
          }
          current=current.next;
      }
      if(HeadB==null)
      {  
          return HeadA;
      }else if(HeadA==null){
          TailB.next=null;
          return HeadB;
      }else{
          TailA.next=HeadB;
          TailB.next=null;
          return HeadA;
      } 

2.删除已经排序过的链表中所有重复的节点 

1)这里面的重复的节点一定是连接在一起的,是紧挨在一起的

2)重复的元素是不止一个的

思路:创建我们还是创造一个虚拟节点,把所有不重复的节点都放到虚拟节点的后面;

主要判断条件是定义一个节点,current一开始要指向头节点,外层循环里面遍历原来的链表,只要发现current.val!=current.next.val那么就把current放到新创建的链表当中

 public ListNode deleteDuplicates(ListNode head) {
        if(head==null)
        {
            return null;
        }
           ListNode HeadA=new ListNode(-1);
           ListNode TailA=HeadA;
           ListNode current=head;
           while(current!=null)
           {
               if(current.next!=null&&current.val==current.next.val)//这里面的条件不可以写成if(current.val!=current.next.val)如果此时链表只有一个节点,那么此时就会发生空指针异常
               {
//如果这里面的数据是34,34,34,34,34,34,此时如果条件是current.val==current.nextval就会发生空指针异常,因为current会一直向后走,直到最后一个节点发现current.next此时就会发生空指针异常
                    while(current.next!=null&&current.val==current.next.val)
                    {
                        current=current.next;
                    }
//此时出来的节点是重复结点的最后一个节点
                    current=current.next;
               }else {
                  TailA.next=current;
                  TailA=TailA.next;
                  current=current.next;
               }
           }
           TailA.next=null;//因为这里面是节点之间进行相互赋值,所以如果说12,12,34,45,45,整个循环走完之后发现新创建的链表的next值不为空,此时链表就会发生错误
一定要注意新创建的链表的最后一个节点一定要是空
           return HeadA.next;
    }

3.判断链表的回文结构,空间复杂度(O(1));

输入:1,2,2,1 输出:true

输入:1,2 输出:true 

解题思路:我们还是希望他是可以从后向前进行遍历的,所以我们要反转中间节点以后的单链表;

1)运用快慢指针,先找到链表的中间节点

2)进行翻转,真正进行翻转操作的是两个节点,第三个节点用来进行计算前两个节点中的一个较靠后的结点的next,况且要放到循环的第一行

3)我们进行回溯判断的时候,一定要从slow往前走(只有四个节点,拿fast看一下,判断中间节点之后,并进行翻转,此时的fast的值已经指向空了,所以不能再用fast进行回溯;

4)进行翻转之后,我们要让slow从后往前走,head从前往后走,知道两个引用相遇(奇数情况)

5)如果发现slow.next=head或者slow=head在中间位置说明已经相遇了,我们在循环中都要比较head.data是否等于slow.data,如果不相等,直接返回false;

 public boolean isPalindrome(ListNode head) {
        ListNode fast=head;
        ListNode slow=head;
        ListNode current=null;
        ListNode currentNext=null;
        if(head==null)
        {
            return true;
        }
        //1找到中间节点
        while(fast!=null&&fast.next!=null)
        {
            fast=fast.next.next;
            slow=slow.next;
        }
        //2进行反转单链表
        current=slow.next;
        while(current!=null)
        {
            currentNext=current.next;
            current.next=slow;
            slow=current;
            current=currentNext;
        }
        //3进行判断是否会文,先按照奇数来进行处理(先把偶数抛到一边),写出完整的代码,全部写完之后,再从循环里面写出完整的代码
        while(head!=slow)
        {
            if(head.val!=slow.val)
            {
                return false;
            }
            if(head.next==slow)
            {
                return true;
            }
          
            slow=slow.next;
            head=head.next;
        }
        return true;

    }

4. 链表相交,返回中间节点

1)首先我们先要搞明白一个问题,如果两个链表相交,那么形状是Y的形状还是X的形状

2)如果两个链表相交,那么是val域相同还是next域相同呢?

是Y的形状,并且相交链表的next域是相同的;

 

做题思路:

1)如果两个单链表长度是不相同的,那么他们分别从头节点走,那么他们是永远不会相遇的

2)我们先要知道两个链表的长度,先让长的那个链表走差值步(两个链表长度的差值)

3)我们在让他们同时一步一步地走

 public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA==null||headB==null)
        {
            return null;
        }
        int count1=0;
        int count2=0;
        int len=0;
        ListNode current1=headA;
        ListNode current2=headB;
        while(current1!=null)
        {
            current1=current1.next;
            count1++;
        }
        while(current2!=null)
        {
            current2=current2.next;
            count2++;
        }
        current1=headA;
        current2=headB;
        if(count1>count2)
        {
            len=count1-count2;
            while(len!=0)
            {
              current1=current1.next;
              len--;
            }
        }else{
            len=count2-count1;
            while(len!=0)
            {
                current2=current2.next;
                len--;
            }
        }
        while(current1!=current2)//为什么不进行判断current1.next!=current2.next作为判断条件,最终我们进行返回current1.next
        {
            current1=current1.next;
            current2=current2.next;
        }
        return current1;

    }

5.判断链表是否有环 

我们定义一个快慢指针,定义一个快指针fast,一次走两步,我们再定义一个慢指针,一次走一步;

为啥不能一个走三步,一个走一步呢?

我们必须等到每一次走完之后才可以进行对比,有可能比一次走两步慢,也有可能永远相遇不到; 

我们举例的时候,举只有两个节点的环,再进行演示走两步和走三步进行演示,画图

 public boolean hasCycle(ListNode head) {
        ListNode fast=head;
        ListNode slow=head;
        while(fast!=null&&fast.next!=null)
        {
            fast=fast.next.next;
            slow=slow.next;
            if(slow==fast)
            {
                return true;
            }
        }
        return false;
    }

6.返回链表入环时候的第一个节点(OJ),链表无环那么返回空,空间复杂度是O(1)

 

题解:在上述图中,我们可以找到定义快指针和慢指针的相遇节点,我们肯定实现要进行找到相遇的节点;

设:起始点到入口点的距离是X,我们设相遇点和入口点之间的距离是Y,环的长度是C

那么这时fast走的路程是slow走的路程的2倍

下面我们要算在相遇的时候slow走的路程和fast走的路程(沿顺时针方向走)

因为fast的速度是slow的2倍,他们有在相同的时间进行了相遇,所以fast的路程是slow的2倍;

slow走的路程:X+C-Y

fast走的路程:X+C+C-Y

假设只走了一圈,fast只走了一圈,在第二圈就相遇了;进行连立,发现X=Y;

那么我们就得出结论:起始点到入口点之间的距离,和入口点到相遇点之间的距离是相同的;

我们定义两个节点一个从链表的头位置走,一个从中间位置向回走,相遇节点就是环的入口结点;

但是实际上可能fast会走很多圈,slow的路程是不变的

2(X+C-Y)=X+NC+C-Y

X=(N-1)C+Y

N是任意的,X肯定是确定的,因为起始点到入口点之间的距离是确定的,现在N和C是不确定的,转的圈越多,说明C就越小,也就说明一圈的路程是特别小的;

slow不可能在环里面转很多圈

  public ListNode detectCycle(ListNode head) {
        ListNode fast=head;
        ListNode slow=head;
        while(fast!=null&&fast.next!=null)
        {
            fast=fast.next.next;
            slow=slow.next;
//如果不写这个if语句,代码是就会死再循环里面,fast==slow,及时相遇了,不写条件,fast和slow还会一直向下走
//所以我们约定,只要进行了相遇,那么就退出
            if(fast==slow)
            {
                break;
            }
        }
//此时代码可以走到这里有两种情况,一个是fast=null或者fast.next=null,这种属于fast指针已经走到了链表的末尾,此时slow是中间节点
        if(fast==null||fast.next==null)
        {
            return null;
        }
        while(slow!=head)
        {
            head=head.next;
            slow=slow.next;
        }
        return slow;
        
    }

猜你喜欢

转载自blog.csdn.net/weixin_61518137/article/details/124685101