單鏈表反轉的Java實現

今天羣裏大佬給我拋出了一個問題,單鏈表反轉。
非常仁慈的給了我10分鐘的時間,結果我連鏈表的數據結構都沒寫完,果然我還是太菜了。

雖說我也是看過數據結構的,但是一,時間有點久了;二,當時看的時候就沒怎麼寫代碼。

所以欠的債要還,硬着頭皮也要寫。

最開始我就冒出了一個想法,及其幼稚,思路就是從鏈表中取值,然後把所有值在外部排序完,再放回鏈表中,但是我一看這根本就是離題了啊!可是暫時沒有什麼別的想法,想着寫寫試試吧,結果發現這麼寫好像也挺麻煩的,就放棄了。

然後也想過遞歸方式實現,但是我還是太年輕了,對於遞歸理解的不是很到位,當時認爲使用遞歸的話,不好找前一個節點,這樣就沒辦法設置當前節點的下一個節點。

總之,我還是百度了,看了下思路。

兩種方法:遞歸遍歷

下面的方法參考了文章:點這裏

遞歸

首先是遞歸

public static SingleLinkedListNode reversal(SingleLinkedListNode node){
        if (node == null || node.next == null){
            return node;
        }else {
            SingleLinkedListNode headNode = reversal(node.next);
            node.next.next = node;
            node.next = null;
            return headNode;
        }
    }

思路就是從最後一個節點開始,向前反轉。
這裏有一個比較關鍵的點,也是我之前以爲遞歸行不通的癥結就是:遞歸會入棧,而棧中保存了節點所有信息,並且不是設置當前節點的下一節點爲前一節點,而是設置當前節點的下一節點的next爲當前節點。

遍歷

接着是遍歷法

public static SingleLinkedListNode reversal2(SingleLinkedListNode node){
        SingleLinkedListNode previousNode = null;
        SingleLinkedListNode currentNode = node;
        SingleLinkedListNode headNode = null;

        while (currentNode != null){
            SingleLinkedListNode nextNode = currentNode.next;
            if (nextNode == null){
                headNode = currentNode;
            }
            currentNode.next = previousNode;
            previousNode = currentNode;
            currentNode = nextNode;
        }
        return headNode;
    }

這裏的思路是從前往後反轉,每經過一個節點,就將當前節點的next進行反轉,指向之前的節點。

遍歷另一版

我這裏自己思考實現了另一版,但是沒有上面那個優雅,且考慮的不夠全面

public static SingleLinkedListNode reversal3(SingleLinkedListNode node){
        SingleLinkedListNode previousNode = null;
        SingleLinkedListNode currentNode = node;
        SingleLinkedListNode nextNode = null;

        while (currentNode.next != null){
            nextNode = currentNode.next;
            currentNode.next = previousNode;
            previousNode = currentNode;
            currentNode = nextNode;
        }
        currentNode.next = previousNode;
        return currentNode;
    }

雖然運行起來的時候沒什麼問題,但是我在考慮的時候確實忽略了入參鏈表爲空的情況的。

完整代碼

完整代碼如下

/**
 * @Auther: yubt
 * @Description: 單鏈表反轉
 * @Date: Created in 11:04 2018/9/27
 * @Modified By:
 */
public class Reversal_linkedList {

    private static class SingleLinkedListNode {
        private int data;
        private SingleLinkedListNode next;
        
        public int getData() {
            return data;
        }

        public void setData(int data) {
            this.data = data;
        }

        public SingleLinkedListNode getNext() {
            return next;
        }

        public void setNext(SingleLinkedListNode next) {
            this.next = next;
        }

        @Override
        public String toString() {
            return "SingleLinkedListNode{" +
                    "data=" + data +
                    ", next=" + next +
                    '}';
        }
    }

    // 遞歸法
    // 運行正常,debug會棧溢出,因爲toString()方法
    public static SingleLinkedListNode reversal(SingleLinkedListNode node){
        if (node == null || node.next == null){
            return node;
        }else {
            SingleLinkedListNode headNode = reversal(node.next);
            node.next.next = node;
            node.next = null;
            return headNode;
        }
    }

    // 遍歷法
    public static SingleLinkedListNode reversal2(SingleLinkedListNode node){
        SingleLinkedListNode previousNode = null;
        SingleLinkedListNode currentNode = node;
        SingleLinkedListNode headNode = null;

        while (currentNode != null){
            SingleLinkedListNode nextNode = currentNode.next;
            if (nextNode == null){
                headNode = currentNode;
            }
            currentNode.next = previousNode;
            previousNode = currentNode;
            currentNode = nextNode;
        }
        return headNode;
    }

    public static SingleLinkedListNode reversal3(SingleLinkedListNode node){
        SingleLinkedListNode previousNode = null;
        SingleLinkedListNode currentNode = node;
        SingleLinkedListNode nextNode = null;

        while (currentNode.next != null){
            nextNode = currentNode.next;
            currentNode.next = previousNode;
            previousNode = currentNode;
            currentNode = nextNode;
        }
        currentNode.next = previousNode;
        return currentNode;
    }

    public static void main(String[] args) {
        SingleLinkedListNode node1 = new SingleLinkedListNode();
        node1.setData(1);
        SingleLinkedListNode node2 = new SingleLinkedListNode();
        node2.setData(2);
        SingleLinkedListNode node3 = new SingleLinkedListNode();
        node3.setData(3);
        SingleLinkedListNode node4 = new SingleLinkedListNode();
        node4.setData(4);
        SingleLinkedListNode node5 = new SingleLinkedListNode();
        node5.setData(5);

        node1.setNext(node2);
        node2.setNext(node3);
        node3.setNext(node4);
        node4.setNext(node5);

//        System.out.println(node1);

//        SingleLinkedListNode reversalNode = reversal(node1);
//        System.out.println(reversalNode);

//        SingleLinkedListNode reversalNode2 = reversal2(node1);
//        System.out.println(reversalNode2);

        SingleLinkedListNode reversalNode3 = reversal3(node1);
        System.out.println(reversalNode3);
    }
}

所以這些基礎的東西還是要時不時就回顧一下,不會就趕緊補,有遺忘就複習,總之要夯實基礎,雖然工作中不太可能用的上,但是隻會業務邏輯,天天增刪改查也太low了吧。。。

附加題

結果大佬又提了一個問題,存心搞我。

說存在 0 <= m < n <= ll爲鏈表長度,然後要反轉mn之間的部分。

吭呲癟肚一下午,寫了一坨勉強能用的吧

// m,n 從0起
    public static SingleLinkedListNode specialReversal(SingleLinkedListNode node, int m, int n){
        SingleLinkedListNode headNode = node;
        SingleLinkedListNode beforeM = node;
        SingleLinkedListNode afterM = null;
        SingleLinkedListNode mBetweenN = null;
        SingleLinkedListNode afterN = null;
        if (m == 0){
            beforeM = null;
            afterM = node;
        }else {
            // 截斷m處的節點
            for (int i = 0; node != null; i++) {
                // 操作next節點偏移量+1
                if (i + 1 == m) {
                    afterM = node.next;
                    node.next = null;
                }
                node = node.next;
            }
        }
        // 截斷n處節點,並獲得m和n之間的鏈表
        mBetweenN = afterM;
        for (int j = 0; j < n - m; j++){
            afterM = afterM.next;
        }
        afterN = afterM.next;
        afterM.next = null;
        // 對m和n間的鏈表進行反轉
        SingleLinkedListNode reversalMN = reversal2(mBetweenN);
        // 將n之後的鏈表連接回來
        SingleLinkedListNode tmp = reversalMN;
        while (reversalMN.next != null){
            reversalMN = reversalMN.next;
        }
        reversalMN.next = afterN;
        if (m != 0) {
            // 將m之前的鏈表連接回來
            while (beforeM.next != null) {
                beforeM = beforeM.next;
            }
            beforeM.next = tmp;
        }else {
            // 由於m爲0,無鏈表,所以直接替換頭部節點
            headNode = tmp;
        }
        // 返回頭部節點
        return headNode;
    }

我的思路是把mn之間的鏈表截斷出來,然後反轉之後再接回去。

大佬的思路呢,是直接對mn之間的節點進行交換,比我這個簡單

把他的代碼貼出來把

val dummy = new ListNode(0)
    dummy.next = head
    var pre = dummy
    for (_ <- 0 until m - 1)
      pre = pre.next
    val start = pre.next
    var tail = start.next
    for (_ <- 0 until n - m) {
      start.next = tail.next
      tail.next = pre.next
      pre.next = tail
      tail = start.next
    }
    dummy.next

先這樣吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章