兩個有序鏈表的合並


分享一個關於java算法的問題:怎么合並兩個有序鏈表

這里提供兩種解決方法:1.遞歸實現 ; 2.非遞歸實現

任何一種方式,都要先創建節點類,沒有什么重點,直接寫代碼:

package com.dataClass;

/**
 * @author 新生代菜鳥
 */
public class Node {
    // 數據存儲變量
    public int data;

    // 節點信息存放(指針信息)
    public Node next;

    // 構造函數,用來給data傳值 ---這里先不考慮批量插入的問題
    public Node(int data) {
        this.data = data;
    }
}
View Code

遞歸實現:

傳入的兩個鏈表是list1和list2,考慮它們是否為空會有三種情況:

  1. 兩個都是null , 怎么合並都會是一個空鏈表,直接返回null即可
  2. 兩個中有一個是null , 找到非空的那一個直接返回即可
  3. 兩個都不是null,此時就是重點了

  我們分析第三種情況:

  假如:list1為:  1  -->  3   -->  5 -- null ;  list2為:2 -->  4  -->  6  --> 8 -- > null;

我們指定head用來存放data小的那個對象,剛開始時list與list實際是指向了兩個鏈表的表頭,即list1.data = 1 , list2.data = 2 ,此時我們發現1 <= 2 ,我們將head指向list1 ,那么新問題就是:head的next指向哪?遞歸算法的魅力就在這,我們不用考慮別的,反正一定是在兩個鏈表剩下的元素中,那么我們將list1.next和list2作為遞歸方法的參數,繼續調用遞歸方法即可 。

  我們發現當list1指向null時,list並沒有指向null,就是說本例中list2中還有元素沒有被合並,那么在實際中可能是list1也可能是list2會有元素沒有合並,但其中一個已經已經是null了,此時只需判斷哪個非null,將其合並在后面即可。下面看代碼:

public Node mergeByRecursion(Node list1, Node list2) {
                //將1,2合並
        if (list1== null || list2 == null) {
            return list1 == null ? list2 : list1;
        }

        Node head = null;
        if (list1.data <=list2.data) {
            head = list1;
            head.next = mergeByRecursion(list1.next, list2);
        } else {
            head = list2;
            head.next = mergeByRecursion(list1, list2.next);
        }

        return head;
    }
View Code

 

非遞歸實現:

遞歸算法實現簡單,代碼也容易理解,但是它的弊端也很明顯時間空間開銷都很大,效率低,現在考慮一種比較容易理解的非遞歸算法:

傳入的兩個鏈表是node1和node2 , 開頭和遞歸算法一樣,也是那三種情況,前兩種情況處理也一樣,關鍵在第三種情況,此時我們這么考慮,node1是一個在node1鏈表上的指針,node2是node2鏈表上的一個指針,我們有一個新的指針head,在兩個指針所指都不是null時,我們讓head指向data更小的那個,同樣:

  假如list1為:  1  -->  3   -->  5 -- null ;  list2為:2 -->  4  -->  6  --> 8 -- > null;

剛開始1 < 2 , 讓head = list1 , 然后list1 = list.next,    使用另一個指針Node point = head 來記注這個合並后鏈表的表頭,

現在再看那么list1.data = 3 ,  list2.data = 2 , 2 < 3 ,那么head.next = list2 , list2 = list2.next  , 在讓head指針也向着后移動,即head = head.next ,

...

其實就是使用四個指針,一個記錄node1上的當前位置,一個記錄node2上的當前位置,一個記錄合並鏈表的表頭,另外一個不斷的指向當前位置下data更小的那一個。看一下代碼:

/**
     * 非遞歸算法---簡化
     * 
     * @param node1測試鏈表1
     * @param node2測試鏈表2
     * @return 返回合並后的鏈表
     */

    public Node mergeNoRecursion2(Node node1, Node node2) {
        // 合並1,2
        if (node1 == null || node2 == null) {
            return node1 == null ? node2 : node1;
        }

        // head記錄合並后鏈表的表頭(理解有邊的代碼)
        Node head = node1.data <= node2.data ? node1 : node2;

        // list1,list2用來記錄兩鏈表當前位置
        Node list1 = head == node1 ? node1 : node2;
        Node list2 = head == node1 ? node2 : node1;

        // point用來指向合並鏈表的下一個節點
        Node point = head;
        list1 = list1.next;// 開始時已經指向了list1的表頭,后面的比較list1要從第二個開始

        // 使用循環遍歷兩個鏈表,根據list1.data和list2.data的比較結果,決定point下一節點
        while (list1 != null && list2 != null) {
            if (list1.data <= list2.data) {
                // list1.data <= list2.data 首先更新point的下一節點list1,然后更新list1的位置
                point.next = list1;
                list1 = list1.next;
            } else {
                // list1.data > list2.data 首先更新point的下一節點list2,然后更新list2的位置
                point.next = list2;
                list2 = list2.next;
            }
            // 更新point的位置為它的下一節點
            point = point.next;
        }

        /*
         * 至少有一個為空了,list1是否已經便利完 如果list1遍歷完,則將point.next直接指向list2
         * 否則及list2遍歷完,那么point.next直接指向list1
         **/
        point.next = list1 == null ? list2 : list1;

        return head;
    }

}
View Code

 

兩種實現已經寫完了,下面就是比較期待又緊張的測試階段了,天靈靈,地靈靈,保佑測試一定過...

public class Test {
        //輸出函數
    public static void syso(Node head) {
        while (head != null) {
            System.out.print(head.data + "-->");
            head = head.next;
        }
        System.out.println("null");
    }

    public static void main(String[] args) {

        MyNodeList list = new MyNodeList();
                //測試遞歸算法
        // 測試鏈表1.1
        Node node1 = new Node(1);
        Node node3 = new Node(3);
        node1.next = node3;
        Node node5 = new Node(5);
        node3.next = node5;

        // 測試鏈表1.2
        Node node2 = new Node(2);
        Node node4 = new Node(4);
        node2.next = node4;
        Node node6 = new Node(6);
        node4.next = node6;

        // 遞歸方法測試
        Node test1 = list.mergeByRecursion(node1, node2);
        System.out.print("遞歸合並結果:");
        syso(test1);


                //測試非遞歸算法
        // 測試鏈表2.1
        Node node21 = new Node(1);
        Node node23 = new Node(3);
        node21.next = node23;
        Node node25 = new Node(5);
        node23.next = node25;

        // 測試鏈表2.2
        Node node22 = new Node(2);
        Node node24 = new Node(4);
        node22.next = node24;
        Node node26 = new Node(6);
        node24.next = node26;
        Node node28 = new Node(8);
        node26.next = node28;

        Node test2 = list.mergeNoRecursion2(node21, node22);
        System.out.print("非遞歸合並結果:");
        syso(test2);
    }
}    
View Code

 測試結果為:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM