1. 程式人生 > >資料結構之Java單鏈表反轉

資料結構之Java單鏈表反轉

        本文為資料結構基礎,研究得不是很深。用Java實現單鏈表的反轉,雖然本文研究得不是很深,但是因為是資料結構,所以必須是在對Java記憶體比較清楚的情況下才能真正的搞懂吃透,如果對Java記憶體不夠清楚,那最多隻能學形而不能學其內在。

         首先我們要搞清楚連結串列是啥玩意兒?先看看定義:

         講連結串列之前我們先說說Java記憶體的分配情況:我們new物件的時候,會在java堆中為物件分配記憶體,當我們呼叫方法的時候,會將方法載入到方法區,在方法區儲存了載入類的資訊,常量,靜態變數等等。搞明白這個我們再來講連結串列。

       連結串列是一種物理儲存單元

上非連續、非順序的儲存結構資料元素的邏輯順序是通過連結串列中的指標連結次序實現的。連結串列由一系列結點(連結串列中每一個元素稱為結點)組成,結點可以在執行時動態生成。每個結點包括兩個部分:一個是儲存資料元素的資料域,另一個是儲存下一個結點地址的指標域。 這個定義估計太過書面化,對初學者來說,不好理解,其實簡單點說就是醬紫的。我們建立了一個類Node,這個類當中有兩個變數,一個data用於儲存資料,還有一個Node型別的變數next用於儲存另一個物件在java堆中的地址然後new了很多個Node類的物件,我們通過setNext方法將第二個物件node2的地址給node1儲存起來,同樣的將第三個物件node3的地址交給node2儲存起來。通過這種方式,我們就將很多個物件連成串了,形成了一種鏈狀。這就是連結串列了

       這兒著重宣告:在Java中,沒有地址一說,只有hashCode。其實hashCode就是通過演算法,將每一個物件的地址算成一個code轉成一個特有的字串。當我們沒有複寫Object類的toString方法的時候,該類的物件呼叫toString方法,打印出來,或者不呼叫toString方法,直接列印該類的物件,其實就是將hashCode打印出來了。這個hashCode就相當於是記憶體了。

     好了搞懂了這些我們就可以來看看例項了:

節點Node類,其實節點就是我們的物件,每一個節點就是一個物件

/**
 * 其實一個節點就對應我們java中的一個物件,我們在分析的時候需要注意除了
next要儲存一個地址外,自己也是物件自己也有地址 * Created by PICO-USER dragon on 2017/3/16. */ public class Node { //資料域儲存資料 private int data; //指標域用於儲存下一個節點的地址 private Node next; public Node(int data) { this.data = data; } public int getData() { return data; } public void setData(int data) { this.data = data; } public Node getNext() { return next; } public void setNext(Node next) { this.next = next; } }

反轉連結串列的方法,當傳入的節點為null的時候,直接染回null。如果只有一個節點,頭尾都是它,直接返回該節點

public static Node reverseList(Node head) {

    if (head == null) {

        return null;
    }

    if (head.getNext() == null) {
        return head;
    }

    //previous上一個節點
Node preNode = null;
    //current節點當前節點,並讓它指向傳進來的物件所在地址(是儲存該物件的地址,不是它的next值)
Node curNode = head;
    //next節點下一個節點
Node nextNode = null;

    while (curNode != null) {

        //next節點指向後一個節點所在地址,並改變新地址的值(包括datanextnextNode = curNode.getNext();
        if (nextNode != null) {
            System.out.print("nextNode data :" + nextNode.getData() + " next :" + nextNode.getNext() + " " + nextNode + "\n");
        }

        //current節點儲存的地址(也就是next)的值改為preNode節點所指向的地址(這樣就把指向箭頭反轉了)這兒有個誤區
//注意:是將preNode指向的地址給curNodenext,不是把preNodenext給它。
curNode.setNext(preNode);
        if (curNode != null) {
            System.out.print("curNode data :" + curNode.getData() + " next :" + curNode.getNext() + " " + curNode + "\n");
        }

        //previous節點指向的地址向後移動一個單位,並改變新地址的值(包括datanextpreNode = curNode;
        if (preNode != null) {
            System.out.print("preNode data :" + preNode.getData() + " next :" + preNode.getNext() + " " + preNode + "\n");
        }

        //current節點的索引向後移動一個單位,並改變新地址的值包括(datanextcurNode = nextNode;
        if (curNode != null) {
            System.out.print("curNode data :" + curNode.getData() + " next :" + curNode.getNext() + " " + curNode + "\n");
        }

        System.out.print("-----------------------\n");
    }

    return preNode;
}
public class MainRun {

    public static void main(String[] arg0) {

        //建立連結串列的節點,建立了三個物件,那就是三個節點
Node node0 = new Node(1);
        Node node1 = new Node(2);
        Node node2 = new Node(3);
        //將這些節點,串連起來形成連結串列
node0.setNext(node1);
        node1.setNext(node2);

        //連結串列的頭結點代表了該連結串列,因為頭結點能找到第二個,第二個能找到第三個,依次找下去,全都找到了
Node head1 = node0;

        //先列印反轉之前的連結串列的值,將hashCode一起打印出來,方便去每一行程式碼都對誰做了什麼操作
while (head1 != null) {

            System.out.print("data :" + head1.getData() + " next :" + head1.getNext() + "  " + head1.toString() + "\n");
            head1 = head1.getNext();
        }

        System.out.print("---++++++-----\n");

        //注意了,我們是從頭開始反轉,所以這兒不能用head1,因為head1在上面的while迴圈中已經成為最後一個節點了
Node oldHead = node0;
        Node newHead = reverseList(oldHead);

        //列印反轉後的節點
while (newHead != null) {

            System.out.print("data :" + newHead.getData() + " next :" + newHead.getNext() + " " + newHead + "\n");
            newHead = newHead.getNext();
        }

    }

看看執行結果:


下面給出分析結果,我自己用筆畫的,網友可以根據這個分析步驟,跟著while迴圈的程式碼一句一句往下分析,每一行程式碼執行之後改動的值是什麼?多看看,多分析分析就通了。


第二種方法:遞迴呼叫實現單鏈表反轉

/**
 * 因為遞迴的思想是直接更改當前節點的next的值為前一個節點所在的地址,所以需要用到兩個引數,當前節點和前一個節點,
* 這兒給外面用就只給一個方法,我們再自己封一個兩個引數的方法。
*
 * @param head
* @return
*/
public static Node reverseList2(Node head) { return reverseListRecursively(null, head);}/** * 遞迴呼叫實現的思想很簡單,就是直接改變curNodenext的值。原本是指向後面一個節點的,現在需要改為前一個節點。* 所以參與演算法的人只有當前節點和當前的前一個節點,而下一個節點的作用只是用於讓需要更換next的物件往後面移動* * @param preNode* @param curNode* @return*/public static Node reverseListRecursively(Node preNode, Node curNode) { if (curNode == null) { return null; } if (curNode.getNext() == null) { curNode.setNext(preNode); return curNode; } //curNode中儲存的地址改成前一個節點所在的地址curNode.setNext(preNode); //如果當前節點有下一個節點就將該節點拿出來Node nextNode = curNode.getNext(); //遞迴呼叫本方法,相當於讓preNodecurNode指向的地址都向後移動一個單位,直到所有的節點都將自己儲存的地址改為前一個為止Node newNode = reverseListRecursively(curNode, nextNode); return newNode;}
public static void main(String[] arg0) {

    //建立連結串列的節點,建立了三個物件,那就是三個節點
Node node0 = new Node(1);
    Node node1 = new Node(2);
    Node node2 = new Node(3);
    //將這些節點,串連起來形成連結串列
node0.setNext(node1);
    node1.setNext(node2);

    //連結串列的頭結點代表了該連結串列,因為頭結點能找到第二個,第二個能找到第三個,依次找下去,全都找到了
Node head1 = node0;

    //先列印反轉之前的連結串列的值,將hashCode一起打印出來,方便去每一行程式碼都對誰做了什麼操作
while (head1 != null) {

        System.out.print("data :" + head1.getData() + " next :" + head1.getNext() + "  " + head1.toString() + "\n");
        head1 = head1.getNext();
    }

    System.out.print("---++++++-----\n");

    //注意了,我們是從頭開始反轉,所以這兒不能用head1,因為head1在上面的while迴圈中已經成為最後一個節點了
Node oldHead = node0;
    Node newHead = reverseList2(oldHead);

    //列印反轉後的節點
while (newHead != null) {

        System.out.print("data :" + newHead.getData() + " next :" + newHead.getNext() + " " + newHead + "\n");
        newHead = newHead.getNext();
    }

}

相關推薦

資料結構Java單鏈反轉

        本文為資料結構基礎,研究得不是很深。用Java實現單鏈表的反轉,雖然本文研究得不是很深,但是因為是資料結構,所以必須是在對Java記憶體比較清楚的情況下才能真正的搞懂吃透,如果對Java記憶體不夠清楚,那最多隻能學形而不能學其內在。          首先我

java資料結構迴圈單鏈

直接上程式碼舉例說明: public class CircularLinkedList { //java中迴圈單鏈表 private class Node {//建立一個內部節點類 private Node next = null; private Objec

資料結構靜態單鏈

靜態連結串列L儲存 遊標 cur 5 2 3 4 0 6 7 .... 1 資料 data   A

資料結構——關於單鏈的頭插法和尾插法的程式碼和思路

頭插法 思路:每次插入都在第一個節點之前,頭結點之後,那每次插入時的賦值就只要將頭結點的地址賦給插入的節點,然後將插入的節點的地址依次給後面節點就可以了。 1、定義一個要插入的節點q 2、q->data內給值e 3、q->next = p->nex

資料結構迴圈單鏈(C++實現)

基本的概念與連結串列相同,不同的是 最後一個鏈結點的指標指向頭部 形成了迴圈連結串列#include <iostream> using namespace std; class Node { public: Node *next; int data;

資料結構鏈式的實現--單鏈(C語言)

學習參考: 嚴蔚敏: 《資料結構-C語言版》 基本操作: 單鏈表的建立 新增結點(頭插法) 新增結點(尾插法) 單鏈表的輸出 單鏈表的修改 單鏈表的插入 單鏈表的刪除 單鏈表按

資料結構學習(二)——單鏈的操作頭插法和尾插法建立連結串列

連結串列也是線性表的一種,與順序表不同的是,它在記憶體中不是連續存放的。在C語言中,連結串列是通過指標相關實現的。而單鏈表是連結串列的其中一種,關於單鏈表就是其節點中有資料域和只有一個指向下個節點的指標域。建立單鏈表的方法有兩種,分別是頭插法和尾插法。 所謂頭插法,就是按節

資料結構雜湊java實現

雜湊表是一種資料結構,提供快速的插入和查詢功能。雜湊表基於陣列儲存資料,因此能在O(1)時間內定位資料。關鍵字值通過雜湊函式對映為陣列下標。缺點就是陣列建立後容量固定,如果資料較多需要不斷擴充套件其長度。如何將關鍵字轉換為陣列下標?這個操作是通過雜湊函式完成的。比如,下面就

C++數據結構傳統單鏈

fad div class for reverse while 結點 next using   這幾天有空重寫一下數據結構,從單鏈表開始吧,這個是C++版本的,後面會根據情況是否補充上C版本的。 這個文章寫了之後,也查看了網絡上其他的數據結構寫法,發現大家的寫法多多少少都有

資料結構與演算法——單鏈(一)

單鏈表的頭插法,插入時就是逆序。 InsertList()還不完善。 #include<stdio.h> #include<stdlib.h> #define ERROR 0 #define OK 1 typedef int Status ; typedef int

資料結構 筆記:單鏈的具體實現

LinkList設計要點 -類模板,通過頭結點訪問後繼結點 -定義內部結點型別Node,用於描述資料域和指標域 -實現線性表的關鍵操作(增,刪,查,等) template<typename T> class LinkList : public List<T>

資料結構 筆記:單鏈的遍歷與優化

如何遍歷單鏈表中的每一個數據元素? 為單鏈表提供新的方法,線上性時間內完成遍歷 設計思路(遊標) -在單鏈表的內部定義一個遊標(Node* m_current) -遍歷開始前將遊標指向位置為0的資料元素 -獲取遊標指向的資料元素 -通過結點中的next指標移動遊標 提供一

java 單鏈反轉 遞迴法 遍歷法

以下是一個例子 package com.haha.demo.util; public class node { static class Node { Integer data; Node next; } static Node readyNode() { Node

資料結構廣義線性

陣列(定義、順序儲存結構) 矩陣的壓縮儲存 廣義表 陣列的順序儲存(將二維的陣列壓縮要一維儲存) 行優先,低下標(aij的位序為k<以1作為開始>,每行n個元素): k = (i - 1) * n + (j - 1) + 1 = (i

演算法題系列三 - 單鏈反轉

問題: 實現單鏈表反轉 答案: 連結串列準備 class Node { private int Data;// 資料域 private Node Next;// 指標域 public Node(int Data) { // super(); this.Data = Da

資料結構實驗三單鏈學生資訊

#include<iostream> #include<stdlib.h> using namespace std; class Student {private: struct Node {char name[20]; char age[4]; int nu

java 單鏈反轉

連結:https://www.jianshu.com/p/d201e6a00e3f .準備連結串列 準備一個由DataNode組成的單向連結串列,DataNode如下: public class DataNode { private int data; private

資料結構靜態順序和動態順序

@Sock對靜態順序表和動態順序表的總結 簡單概括他們的異同點 相同點:記憶體空間連續, 資料順序儲存 不同點:它們所佔記憶體空間的位置不同, 靜態定義一個順序表, 順序表所佔的記憶體空間開闢在記憶體的靜態區, 即所謂的函式棧上, 隨著函式呼叫的結束, 這塊記憶體區域會被系統自動

資料結構動態順序

動態順序表 動態順序表是跟靜態順序表大體相似,有些地方是不同的,動態順序表是在動態變化中,當我們的所需的記憶體不夠時,它會自動開闢一個我們需要的空間,來供我們使用。 動態順序表與靜態順序表的不同在於初始化/銷燬/所有插入,其他和靜態順序表完全一樣。 定義一個結構體 先將我們需

資料結構靜態順序

title: 資料結構之靜態順序表 date: 2018-11-09 14:21:51 tags: C-資料結構 靜態順序表屬於資料結構開始的一種基本結構 首先我們要知道資料結構的概念 資料結構 資料的組織關係 演算法 為了達到特定的目的的一系列過程。在這個過程中又