鏈表面試題彙總

從尾到頭打印鏈表

blic ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer>list=new ArrayList<>();
        if(listNode==null)return list;
        while(listNode!=null){
            list.add(0,listNode.val);
            listNode=listNode.next;
        }
        return list;
    }

鏈表中倒數第k個結點

先走k步,如果中途爲空,說明鏈表長度小於k,則返回null

 public ListNode FindKthToTail(ListNode head,int k) {
        if(head==null)
            return null;
        ListNode a=head;
        ListNode b=head;
        for(int i=0;i<k;i++){
            if(a!=null){
                a=a.next;
            }else{
                return null;
            }
        }
        while(a!=null){
            b=b.next;
            a=a.next;
        }
        return b;
    }

反轉鏈表

 public ListNode ReverseList(ListNode head) {
        if(head==null)return null;
        ListNode pre=head;
        ListNode result=null;
        while(pre!=null){
            ListNode next=pre.next;
            pre.next=result;
            result=pre;
            pre=next;
        }
        return result;
    }

合併兩個排序的鏈表

 public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1==null)return list2;
        if(list2==null)return list1;
        ListNode node=new ListNode(0);
        ListNode result=node;
        ListNode pre1=list1;
        ListNode pre2=list2;
        while(pre1!=null&&pre2!=null){
            if(pre1.val<=pre2.val){
                result.next=pre1;
                result=pre1;
                pre1=pre1.next;
            }else{
                result.next=pre2;
                result=pre2;
                pre2=pre2.next;
            }
                
        }
        if(pre1!=null)result.next=pre1;
        if(pre2!=null)result.next=pre2;
        return node.next;
    }

刪除鏈表的重複結點

用三個指針遍歷

public ListNode deleteDuplication(ListNode pHead)
    {
        if(pHead==null)return null;
        ListNode node=new ListNode(0);
        node.next=pHead;
        ListNode pre1=node;
        ListNode pre2=pHead;
        ListNode pre3=pHead.next;
        while(pre3!=null){
            if(pre2.val!=pre3.val){
                pre1=pre1.next;
                pre2=pre2.next;
                pre3=pre3.next;
            }else{
                while(pre3!=null&&pre2.val==pre3.val){
                    pre3=pre3.next;
                }
                pre1.next=pre3;
                pre2=pre3;
                if(pre3!=null){
                    pre3=pre3.next;
                }
            }
        }
        return node.next;
    }

鏈表中環的入口結點

  • 判斷鏈表是否帶環,用快慢指針,如果相遇說明有環。
  • 一個指針從頭開始,一個從相遇結點開始,直到它們兩相遇,就是環的入口結點
 public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        if(pHead==null)return null;
        ListNode p1=pHead;
        ListNode p2=pHead;
        do{
            p1=p1.next;
            if(p1==null)
                return null;
            p1=p1.next;
            p2=p2.next;
        }while(p1!=p2);
        ListNode p3=pHead;
        while(p1!=p3){
            p1=p1.next;
            p3=p3.next;
        }
        return p3;
    }

兩個鏈表的第一個公共結點

思路一:求出兩個鏈表的長度,找出長的鏈表,讓長的鏈表走長度差,再讓兩個一起走,直到相遇。

public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
 if(pHead1==null)return null;
        if(pHead2==null)return null;
        ListNode p1=pHead1;
        ListNode p2=pHead2;
        int len1=getLength(pHead1);
        int len2=getLength(pHead2);
       int k=0;
        if(len1<len2){
            p1=pHead2;
            p2=pHead1;
            k=len2-len1;
        }
        k=len1-len2;
        for(int i=0;i<k;i++){
            p1=p1.next;
        }
        while(p1!=p2){
            p1=p1.next;
            p2=p2.next;
        }
        return p2;
    }
    public static int getLength(ListNode phead){
        if(phead==null)return 0;
        int count=0;
        ListNode p=phead;
        while(p!=null){
            count++;
            p=p.next;
        }
        return count;
    }

思路二:相當於遍歷一遍兩個鏈表的長度和即可
例如:
l1:0-1-2-3-4-5-null
l2:a-b-4-5-null
p1: 0-1-2-3-4-5-null(此時遇到ifelse)-a-b-4-5-null
p2: a-b-4-5-null(此時遇到ifelse)0-1-2-3-4-5-null
因此,兩個指針所要遍歷的鏈表就長度一樣了!

public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
 if(pHead1==null)return null;
        if(pHead2==null)return null;
        ListNode p1=pHead1;
        ListNode p2=pHead2;
        while(p1!=p2){
       p1=p1==null?pHead2:p1.next;
        p2=p2==null?pHead1:p2.next;
        }
        return p1;
    }

二叉搜索樹與雙向鏈表

用的是中序遍歷非遞歸版本

public TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree==null)return null;
        Stack<TreeNode>stack=new Stack<>();
        TreeNode pre=pRootOfTree;
        TreeNode node=null;
        boolean bool=true;
        while(!stack.isEmpty()||pre!=null){
            while(pre!=null){
                stack.push(pre);
                pre=pre.left;
            }
            TreeNode top=stack.pop();
            if(bool){
                pRootOfTree=top;//讓第一個結點作爲頭
                node=top;
                bool=false;
            }else{
                node.right=top;
                top.left=node;
                node=top;
            }
            pre=top.right;
        }
        return pRootOfTree;
    }

複雜鏈表的複製

在這裏插入圖片描述
在這裏插入圖片描述

public RandomListNode Clone(RandomListNode pHead)
    {
        if(pHead==null)return null;
        RandomListNode node=pHead;
        //合併
        while(node!=null){
            RandomListNode a=new RandomListNode(node.label);
            a.random=null;
            a.next=node.next;
            node.next=a;
            node=a.next;
        }
        //算random
        RandomListNode pre1=pHead;
        while(pre1!=null){
            RandomListNode pre2=pre1.next;
            if(pre1.random!=null){
                pre2.random=pre1.random.next;
            }
            pre1=pre2.next;
        }
        //拆分
        pre1=pHead;
        RandomListNode result=pHead.next;
        while(pre1!=null){
            RandomListNode p=pre1.next;
            pre1.next=p.next;
            if(p.next!=null){
                p.next=p.next.next;
            }
            pre1=pre1.next;
        }
        return result;
    }

重排鏈表

方法一:找到中間結點,將尾部鏈表逆置,然後遍歷兩個鏈表,交叉插入結點。
方法二:將結點放入到隊列中,方便取兩邊的結點。注意最後要設置空結點。

public void reorderList(ListNode head) {
        LinkedList<ListNode>queue=new LinkedList<>();
        if(head==null)return;
        ListNode pre=head;
        while(pre!=null){
            queue.addLast(pre);
            pre=pre.next;
        }
        while(!queue.isEmpty()){
            if(pre==null){
                pre=queue.pollFirst();
            }else{
                pre.next=queue.pollFirst();
                pre=pre.next;
            }
            pre.next=queue.pollLast();
            pre=pre.next;
        }
        if(pre!=null)
        pre.next=null;
    }

分割鏈表

將小於x的放在一個鏈表中,大於等於x的放到另外鏈表中,鏈表尾插比較麻煩,所以添加一個0元素的結點就不用每次去判斷頭結點爲不爲空。左後合併鏈表的時候,分爲三中情況。

 public ListNode partition(ListNode head, int x) {
        if(head==null)return null;
        ListNode head1=new ListNode(0);
        ListNode p1=head1;
        ListNode head2=new ListNode(0);
        ListNode p2=head2;
        ListNode pre=head;
        while(pre!=null){
            if(pre.val<x){
                p1.next=pre;
                p1=p1.next;
                pre=pre.next;
            }else{
                p2.next=pre;
                p2=p2.next;
                pre=pre.next;
            }
        }
        if(p1==null)return head2.next;
        else if(p2==null)return head1.next;
        else{
            p1.next=head2.next;
            p2.next=null;
        }
        return head1.next;
    }

鏈表求和

思想:遍歷兩個鏈表,carry用來存進位,每次算出一個數就添加一個新的結點,最後好需要判斷下carry是不是爲0,不爲0還要再添加一個結點。

 public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        if(l1==null)return l2;
        if(l2==null)return l1;
        ListNode result=new ListNode(0);
        ListNode res=result;
        int carry=0;
        while(l1!=null||l2!=null){
            int num1=l1==null?0:l1.val;
            int num2=l2==null?0:l2.val;
            int sum=num1+num2+carry;
            carry=sum/10;
            res.next=new ListNode(sum%10);
            res=res.next;
            l1=l1==null?null:l1.next;
            l2=l2==null?null:l2.next;
        }
        if(carry>0){
            res.next=new ListNode(carry);
        }
        return result.next;
    }

旋轉鏈表

思路:首先算出鏈表的長度,計算出要循環的次數,也就是length-(k%length),然後將鏈表變成循環鏈表,一個從尾指針開始,一個從頭指針開始,遍歷次數完成,將尾指針的next設爲null,返回頭指針現在所在的位置,就是頭結點。

 public ListNode rotateRight(ListNode head, int k) {
        if(head==null||k==0)return head;
        int length=1;
        ListNode p1=head;
        while(p1.next!=null){
            p1=p1.next;
            length++;
        }
        int top=length-(k%length);
        ListNode p2=head;
        //構建環形鏈表  p1是尾指針,p2是頭指針
        p1.next=head;
        for(int i=0;i<top;i++){
            p1=p1.next;
            p2=p2.next;
        }
        p1.next=null;
        return p2;
    }

奇偶鏈表

public ListNode oddEvenList(ListNode head) {
        if(head==null)return head;
        ListNode o=head;//head爲奇鏈表的頭結點,o爲奇鏈表的尾結點
        ListNode p=head.next;//偶鏈表的頭結點
        ListNode e=head.next;//偶鏈表的尾結點
        while(o.next!=null&&e.next!=null){
            o.next=e.next;
            o=o.next;
            e.next=o.next;
            e=e.next;
        }
        o.next=p;
        return head;
    }

移除鏈表元素

有可能鏈表只有一個結點,並且這個結點值就是val,有可能val再結尾會出現,所以方便減少代碼判斷,先設置一個0的結點放在head的前面。這樣就不用考慮特殊情況了。

public ListNode removeElements(ListNode head, int val) {
        if(head==null)return head;
        ListNode node=new ListNode(0);
        node.next=head;
        ListNode pre=node;
        while(pre.next!=null){
            if(pre.next.val!=val){
            pre=pre.next;}
            else
            pre.next=pre.next.next;
            
        }
        // if(pre.val==val)
        // pre.next=null;
        return node.next;
    }

刪除鏈表中的結點

比如3–4--5,需要刪除4,讓4這個結點值爲5,再繞過5這個結點即可。

 public void deleteNode(ListNode node) {
        node.val=node.next.val;
        node.next=node.next.next;
    }

兩兩交換鏈表中的結點

三個指針進行交換

 public ListNode swapPairs(ListNode head) {
       if(head==null||head.next==null)return head;
       ListNode node=new ListNode(0);
       ListNode p1=node;
       node.next=head;
       while(head!=null&&head.next!=null){
           ListNode p2=head;
           ListNode p3=head.next;
           p1.next=p3;
           p2.next=p3.next;
           p3.next=p2;

           p1=p2;
           head=p2.next;
       }
       return node.next;
    }

鏈表的中間結點

public ListNode middleNode(ListNode head) {
       ListNode a=head;
       ListNode b=head;
       while(a!=null&&a.next!=null){
           a=a.next.next;
           b=b.next;
       }
       return b;
    }

k個一組反轉鏈表

public ListNode reverseKGroup(ListNode head, int k) {
        ListNode node=new ListNode(0),prev=node,next,curry=head;
        int length=0;
        while(curry!=null){
            length++;
            curry=curry.next;
        }
        curry=head;
        node.next=head;
        for(int i=0;i<length/k;i++){
            for(int j=0;j<k-1;j++){
                next=curry.next;
                curry.next=next.next;
                next.next=prev.next;
                prev.next=next;
            }
            prev=curry;
            curry=curry.next;
        }
        return node.next;
    }

合併k個排序鏈表

public ListNode mergeKLists(ListNode[] lists) {
       if(lists.length==0)return null;
        if(lists.length==1)return lists[0];
        if(lists.length==2) return mergeTwo(lists[0],lists[1]);

        int mid=lists.length/2;
        ListNode[]l1=new ListNode[mid];
        ListNode[]l2=new ListNode[lists.length-mid];
        for(int i=0;i<mid;i++){
            l1[i]=lists[i];
        }
        for(int j=0,i=mid;i<lists.length;j++,i++){
            l2[j]=lists[i];
        }
        return mergeTwo(mergeKLists(l1),mergeKLists(l2));
    }

    private ListNode mergeTwo(ListNode p, ListNode q) {
        if(p==null)return q;
        if(q==null)return p;
        ListNode result=new ListNode(-1);
        ListNode res=result;
        while(p!=null&&q!=null){
            if(p.val<=q.val){
                result.next=new ListNode(p.val);
                result=result.next;
                p=p.next;
            }else{
                result.next=new ListNode(q.val);
                result=result.next;
                q=q.next;
            }
        }
        if(p!=null)result.next=p;
        if(q!=null)result.next=q;
        return res.next;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章