1. 程式人生 > >連結串列反轉(Java三種實現方式)

連結串列反轉(Java三種實現方式)

Mackyhuang

連結串列這個資料結果經常遇見,這裡提供一個連結串列反轉的java程式碼實現,有三種演算法,一種是遞迴的,倆種是非遞迴的。

首先為了方便測試,在博文最後貼上遞迴實現連結串列建立的程式碼,以供讀者快速上手測試,提供的程式碼可以複製以後直接測試

先看看Node節點把

public class Node {
    //連結串列用於儲存值
    private final int value;
    //指向下一個節點  理解為Node next更加恰當
    private Node node;

    public Node(int value) {
        this
.value = value; this.node = null; } public int getValue() { return value; } public Node getNode() { return node; } public void setNode(Node node) { this.node = node; } }

連結串列反轉遞迴方法

    public Node reverserLinkedList(Node node){
        if
(node.getNode() == null || node == null){ return node; } Node newdata = reverserLinkedList(node.getNode()); node.getNode().setNode(node); node.setNode(null); return newdata; } //這個遞迴,返回值只是為了控制返回的是最後一個節點 //然後通過遞迴通過棧的特性,這裡就是讓它可以從最後一個節點開始把自己的子節點的子節點改成自己
//自己的子節點改為null
  • 遞迴的實現總是這麼的簡單,程式碼簡練就是遞迴的好處,而且邏輯易於處理,只要能夠出找出一層的邏輯,然後找出特殊值和出口,一個遞迴就已經完成啦

  • 這裡出口顯然就是那個if,為的是找到最後一個節點,然後就可以開始往前遞迴反轉,同時這個if可以排除引數只有一個節點,引數為null的情況。

  • 遞迴的棧累計到最高層的時候(遞迴本質是棧,每一次遞迴放入一個棧,如果這層執行結束,就會彈出,執行下一層),最後一個if結束以後, 開始反轉, 反轉的邏輯其實很簡單, 吧當前節點的下一個節點指向自己,然後自己指向null

說完了遞迴的演算法,也瞭解遞迴其實就是棧,現在就用相同的邏輯,只不過把遞迴變成迴圈,用java本身實現的Stack資料結構編寫一個更加高效的程式碼

public Node reverserLinkedList2(Node node){
        Stack<Node> nodeStack = new Stack<>();
        Node head = null;
        //存入棧中,模擬遞迴開始的棧狀態
        while (node != null){
            nodeStack.push(node);
            node = node.getNode();
        }
        //特殊處理第一個棧頂元素(也就是反轉前的最後一個元素,因為它位於最後,不需要反轉,如果它參與下面的while, 因為它的下一個節點為空,如果getNode(), 那麼為空指標異常)
        if ((!nodeStack.isEmpty())){
            head = nodeStack.pop();
        }
        //排除以後就可以快樂的迴圈
        while (!nodeStack.isEmpty()){
            Node tempNode = nodeStack.pop();
            tempNode.getNode().setNode(tempNode);
            tempNode.setNode(null);
        }
        return head;
    }
  • 邏輯一目瞭然,備註上面的解釋已經很清楚啦

還有一個迴圈寫法更加簡單,使用倆個指標,不需要棧結構

public Node reverserLinkedList3(Node node){
        //指向空,可以想象成位於第一個節點之前
        Node newNode = null;
        //指向第一個節點
        Node curNode = node;

        //迴圈中,使用第三變數事先儲存curNode的後面一個節點

        while (curNode != null){
            Node tempNode = curNode.getNode();
            //把curNode反向往前指
            curNode.setNode(newNode);
            //newNode向後移動
            newNode = curNode;
            //curNode 向後移動
            curNode = tempNode;
        }

        return newNode;
    }
  • 這個的思路就是 倆個指標,把一個連結串列分成倆個部分, newNode是已經反轉部分, curNode是為反轉部分,然後通過倆個指標的配合,不斷的右移直到前部反轉

現在貼其他程式碼部分啦,先貼連結串列構建的

public class LinkedListCreator {
    //構建函式
    public Node createLinkedList(List<Integer> list){
        if (list.isEmpty()){
            return null;
        }
        Node headNode = new Node(list.get(0));
        Node tempNode = createLinkedList(list.subList(1, list.size()));
        headNode.setNode(tempNode);
        return headNode;
    }

    //測試方便的列印函式
    public void printList(Node node){
        while (node != null){
            System.out.print(node.getValue());
            System.out.print(" ");
            node = node.getNode();
        }
        System.out.println();
    }
}

main函式

public static void main(String[] args) {
        LinkedListCreator linkedListCreator = new LinkedListCreator();
        Node node = linkedListCreator.createLinkedList(Arrays.asList(1, 2, 3, 4, 5));
        Node node2 = linkedListCreator.createLinkedList(Arrays.asList(1, 2, 3, 4, 5));
        Node node3 = linkedListCreator.createLinkedList(Arrays.asList(1, 2, 3, 4, 5));
        LinkedListReverser linkedListReverser = new LinkedListReverser();

        Node res = linkedListReverser.reverserLinkedList(node);
        Node res2 = linkedListReverser.reverserLinkedList2(node2);
        Node res3 = linkedListReverser.reverserLinkedList3(node3);

        linkedListCreator.printList(res);
        linkedListCreator.printList(res2);
        linkedListCreator.printList(res3);
    }