链表相关考题

1.打印有序链表的公共值

  问题描述:

    现有两个升序链表,且链表中均无重复元素。请设计一个高效的算法,打印两个链表的公共值部分。

    给定两个链表的头指针headAheadB,请返回一个vector,元素为两个链表的公共部分。请保证返回数组的升序。两个链表的元素个数均小于等于500。保证一定有公共                   值测试样例:

    {1,2,3,4,5,6,7},{2,4,6,8,10}
    返回:[2.4.6]
  
 方法:两个头指针分别指向表头,哪个结点元素小,哪个指针前移,如果相同,则都前移,任意一个链表走完,函数停止。
public int[] findCommonParts(ListNode headA, ListNode headB) {
        // write code here
        List<Integer> arr=new ArrayList<Integer>();
        if(headA==null||headB==null) return null;
        while(headA!=null&&headB!=null){
            if(headA.val==headB.val) 
            {arr.add(headA.val);
             headA=headA.next;
             headB=headB.next;
            }else if(headA.val<headB.val) headA=headA.next;
            else
                headB=headB.next;
        }
        int[] array=new int[arr.size()];
        for(int i=0;i<arr.size();i++) array[i]=arr.get(i);
        return array;
    }


2.用栈完成链表的K逆序  

问题:

  有一个单链表,请设计一个算法,使得每K个节点之间逆序,如果最后不够K个节点一组,则不调整最后几个节点。例如链表1->2->3->4->5->6->7->8->null,K=3这个例子。调            整后为,3->2->1->6->5->4->7->8->null。因为K==3,所以每三个节点之间逆序,但其中的7,8不调整,因为只有两个节点不够一组。

  给定一个单链表的头指针head,同时给定K值,返回逆序后的链表的头指针。

方法:利用栈的特性,对k个元素进行反转

public ListNode inverse(ListNode head, int k) {
        // write code here
        ListNode[] stk=new ListNode[k];
        int top=-1;
        ListNode current=head;
        ListNode lastGroupHead=new ListNode(-1);
        ListNode newHead=lastGroupHead;
        while(current!=null){
            if(top==k-1){
                while(top!=-1){
                    lastGroupHead=lastGroupHead.next=stk[top--];
                }
            }else{
                stk[++top]=current;
                current=current.next;
            }
        }
        if(top==k-1){
             while(top!=-1)
                 lastGroupHead=lastGroupHead.next=stk[top--];
        }else{
            for(int i=0;i<=top;i++) 
                 lastGroupHead=lastGroupHead.next=stk[i];
        }lastGroupHead.next=null;
        return newHead.next;
    }

 

 

3.判断一个链表是否为回文结构:

  方法1:T=O(n),S=O(n), 利用栈把链表每个元素入栈,然后依次出栈和链表元素对比。

  方法2:T=O(n/2),S=O(n),设置两个指针,一个快指针(一次前进两步),一个慢指针,目的是找到链表的中间的位置。

      分析:当链表元素个数为奇数时,快指针.next=null时(一定会走到.next=null),则此时移动后的慢指针就是中间位置。

         当偶数时,快指针=null时(必然会走到null),移动前的慢指针就是中间两个元素的左边位置。

        当得到中间位置的指针时,只需将一半的链表入栈,进行对比。

  方法3:T=O(n),S=O(1),将后半部分指针翻转,如 1->2->3->2->1 变换成1->2->3<-2<-1,用右半部分和左半部分判断。

    

public static boolean isPalindrome(ListNode pHead) {
        ListNode slow=pHead;
        ListNode fast=pHead;
        while(fast!=null&&fast.next!=null) {
            fast=fast.next.next;
            slow=slow.next;
        }
        
        ListNode right;
     //从slow开始翻转(如果是偶数,如1,2,3,3,2,1,奇数时如1,2,3,2,1 右半部分都是1,2,3
     //无论是奇是偶,只需把右半部分比较完即可 right
=reverse(slow); ListNode current=pHead;
while(right!=null) { if(current.val!=right.val) return false; current=current.next; right=right.next; } return true; }

//翻转右半部分
public static ListNode reverse(ListNode head) { ListNode n1=head; ListNode current=head.next; n1.next=null; ListNode n2=current; while(current!=null) { n2=current; ListNode tmp=current.next; n2.next=n1; n1=current; current=tmp; } return n2; }

4.复杂链表的复制

  问题描述:输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回一个链表的拷贝。

  方法图解:

    

 代码:

 public RandomListNode Clone(RandomListNode pHead)
    {
        RandomListNode cur=pHead;
        while(cur!=null){
            RandomListNode tmp=cur.next;
            RandomListNode cp=new RandomListNode(cur.label);
            cp.next=cur.next;
            cur.next=cp;
            cur=tmp;
        }
        cur=pHead; 
        while(cur!=null){
            if(cur.random==null) cur.next.random=null;
            else cur.next.random=cur.random.next;     
            cur=cur.next.next;         
        }
        RandomListNode newHead=new RandomListNode(-1);
        RandomListNode newcur=newHead;
        cur=pHead;
        
        while(cur!=null){
            newcur.next=cur.next;
            newcur=newcur.next;
            cur.next=cur.next.next;
            cur=cur.next;
        }
        return newHead.next;
    }    

5.链表判断否是有环

  问题:如何判断一个单链表是否有环?有环的话返回进入环的第一个节点的值,无环的话返回-1。

      如果链表的长度为N,请做到时间复杂度O(N),额外空间复杂度O(1)。

  分析:如果不做空间上的限制,可以使用哈希表来辅助,当一个节点重复出现时,即是环的入口。

     当限制空间时,采用两个指针,快和慢,当快指针和慢指针相交时,即表示有环,此时让快指针回头开始位置,两个指针每次走一步,

     当相交时,即进入环(此方法的数学证明略)

  代码:

 public int chkLoop(ListNode head, int adjust) {
        // write code here
        ListNode slow=head;
        ListNode fast=head;
        while(fast!=null&&fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
            if(slow==fast){
                fast=head;
                while(slow!=fast){
                    fast=fast.next;
                    slow=slow.next;
                }
                return fast.val;
            }
        }
        return -1;
    }

 

6.无环链表判断相交

问题:

  现在有两个无环单链表,若两个链表的长度分别为m和n,请设计一个时间复杂度为O(n + m),额外空间复杂度为O(1)的算法,判断这两个链表是否相交

分析:

  由于链表相交,则必然最后会有公共部分,故可以直接判断最后一个节点是否相同。

 若要找到第一个交点,则需让较长的链表先走n2-n1步,然后两链表同步走,直到相交

代码:

public boolean chkIntersect(ListNode headA, ListNode headB) {
        // write code here
        ListNode lastA=headA;
        while(lastA.next!=null) lastA=lastA.next;
        ListNode lastB=headB;
        while(lastB.next!=null) lastB=lastB.next;
        
        return lastA==lastB;
    }

7.有环链表判断相交

问题:

  如何判断两个有环单链表是否相交?相交的话返回第一个相交的节点,不想交的话返回空。如果两个链表长度分别为N和M,请做到时间复杂度O(N+M),额外空间复杂度O(1)。

分析:

  首先取得带环链表的环入口,如果两入口相等,则有1,2两种情况,否则有3,4两种情况。

代码:

public boolean chkInter(ListNode head1, ListNode head2, int adjust0, int adjust1) {
        // write code here
        ListNode entry1=chkLoop(head1,0);
        ListNode entry2=chkLoop(head2,0);
        if(entry1==null||entry2==null) return false;
        if(entry1==entry2){
           return true; 
        }else{
            ListNode tmp=entry2.next;
            while(tmp!=entry2){
                if(tmp==entry1) return true;
                tmp=tmp.next;
            }
            return false;
        }
    }

 

8.链表判断相交

   综合已上几种情况,分为链表都有环和都无环进行考虑,其他情况则不可能相交

posted @ 2018-05-13 16:32  即便那总是过去  阅读(171)  评论(0编辑  收藏  举报