單鏈表的實現-JAVA
該內容為轉載,原地址: 數據結構(一) 單鏈表的實現-JAVA
數據結構還是很重要的,就算不是那種很牛逼的,但起碼得知道基礎的東西,這一系列就算是復習一下以前學過的數據結構和填補自己在這一塊的知識的空缺。加油。珍惜校園中自由學習的時光。按照鏈表、棧、隊列、排序、數組、樹這種順序來學習數據結構這門課程把。
-WH
一、單鏈表的概念
鏈表是最基本的數據結構,其存儲的你原理圖如下圖所示
上面展示的是一個單鏈表的存儲原理圖,簡單易懂,head為頭節點,他不存放任何的數據,只是充當一個指向鏈表中真正存放數據的第一個節點的作用,而每個節點中都有一個next引用,指向下一個節點,就這樣一節一節往下面記錄,直到最後一個節點,其中的next指向null。
鏈表有很多種,比如單鏈表,雙鏈表等等。我們就對單鏈表進行學習,其他的懂了原理其實是一樣的。
二、用java實現單鏈表
語言只是一種工具,數據結構真正體會的是那種思想,這句話確實是這樣,不管用什麽寫,其思想是不改變的。以前使用的是C++,現在用的是java,一步步來實現。
2.1、編寫一個Node類來充當結點的模型。我們知道,其中有兩個屬性,1存放數據的data,2存放下一結點的引用,
package com.wuhao.demo01; public class Node { //為了方便,這兩個變量都使用public,而不用private就不需要編寫get、set方法了。View Code//存放數據的變量,簡單點,直接為int型 public int data; //存放結點的變量,默認為null public Node next; //構造方法,在構造時就能夠給data賦值 public Node(int data){ this.data = data; } }
2.2、單鏈表的簡單操作(增加,刪除,獲取總長度,鏈表元素排序,鏈表遍歷)
2.2.1、增加結點操作,addNode(Node)
想法: 一開始也會想如果什麽結點也沒有。是不是需要判斷插入的是第一個結點的問題,但寫完後發現沒有必要,是不是第一個結點操作都是一樣的,所以通過移動的指針遍歷整個鏈表,找到最後一個結點,往後添加即可。沒難度。
/** * 增加操作 * 直接在鏈表的最後插入新增的結點即可 * 將原本最後一個結點的next指向新結點 */ public void addNode(Node node){ //鏈表中有結點,遍歷到最後一個結點 Node temp = head; //一個移動的指針(把頭結點看做一個指向結點的指針) while(temp.next != null){ //遍歷單鏈表,直到遍歷到最後一個則跳出循環。 temp = temp.next; //往後移一個結點,指向下一個結點。 } temp.next = node; //temp為最後一個結點或者是頭結點,將其next指向新結點 }View Code
2.2.2、插入結點到鏈表的指定位置。 insertNodeByIndex(int index,Node node)
註意:要知道插入操作需要的前提是什麽,你才好寫代碼,寫完之後,考慮如果在特殊位置上插入,是否也一樣。還有需要進行插入位置是否可行的判斷。
/** * insertNodeByIndex:在鏈表的指定位置插入結點。 * 插入操作需要知道1個結點即可,當前位置的前一個結點 * index:插入鏈表的位置,從1開始 * node:插入的結點 */ public void insertNodeByIndex(int index,Node node){ //首先需要判斷指定位置是否合法, if(index<1||index>length()+1){ System.out.println("插入位置不合法。"); return; } int length = 1; //記錄我們遍歷到第幾個結點了,也就是記錄位置。 Node temp = head; //可移動的指針 while(head.next != null){//遍歷單鏈表 if(index == length++){ //判斷是否到達指定位置。 //註意,我們的temp代表的是當前位置的前一個結點。 //前一個結點 當前位置 後一個結點 //temp temp.next temp.next.next //插入操作。 node.next = temp.next; temp.next = node; return; } temp = temp.next; } }View Code
2.2.3、刪除指定位置上的結點 delNodeByIndex(int index)
/** * 通過index刪除指定位置的結點,跟指定位置增加結點是一樣的,先找到準確位置。然後進行刪除操作。 * 刪除操作需要知道1個結點即可:和當前位置的前一個結點。 * @param index:鏈表中的位置,從1開始 * */ public void delNodeByIndex(int index){ //判斷index是否合理 if(index<1 || index>length()){ System.out.println("給定的位置不合理"); return; } //步驟跟insertNodeByIndex是一樣的,只是操作不一樣。 int length=1; Node temp = head; while(temp.next != null){ if(index == length++){ //刪除操作。 temp.next = temp.next.next; return; } temp = temp.next; } }View Code
2.2.4、單鏈表進行選擇排序 selectSortNode()
前提要知道什麽是選擇排序,如果不會,請查看我講解排序的文章
分析
/** * 對鏈表中的結點進行排序,按照從小到大的順序,使用選擇排序。 * 使用雙層遍歷。第一層遍歷,正常遍歷鏈表,第二層遍歷,遍歷第一層遍歷時所用的結點後面所有結點並與之比較 * 選擇排序比較簡單,明白其原理,就能夠寫的出來。 */ public void selectSortNode(){ //判斷鏈表長度大於2,不然只有一個元素,就不用排序了。 if(length()<2){ System.out.println("無需排序"); return; } //選擇排序 Node temp = head; //第一層遍歷使用的移動指針,最處指向頭結點,第一個結點用temp.next表示 while(temp.next != null){ //第一層遍歷鏈表,從第一個結點開始遍歷 Node secondTemp = temp.next; //第二層遍歷使用的移動指針,secondTemp指向第一個結點,我們需要用到是第二個結點開始,所以用secondNode.next while(secondTemp.next != null){//第二層遍歷,從第二個結點開始遍歷 if( temp.next.data > secondTemp.next.data){ //第二層中的所有結點依次與第一次遍歷中選定的結點進行比較, int t = secondTemp.next.data; secondTemp.next.data = temp.next.data; temp.next.data = t; } secondTemp = secondTemp.next; } temp = temp.next; } }View Code
2.2.5、單鏈表進行插入排序 insertSortNode()
前提:要知道什麽是插入排序。這個使用插入排序寫了我好久,懵逼一樣的狀態,並且我認為我自己寫效率不是很高。不管怎樣,是騾子是馬拿出來溜溜
/** * 對鏈表進行插入排序,按從大到小的順序,只要這裏會寫,那麽手寫用數組插入排序 * 也是一樣的。先要明白原理。什麽是插入排序,這樣才好寫代碼。 * 插入排序:分兩組,一組當成有序序列,一組當成無序,將無序組中的元素與有序組中的元素進行比較(如何比較,那麽就要知道插入排序的原理是什麽這裏不過多闡述) * 這裏我想到的方法是,構建一個空的鏈表當成有序序列,而原先的舊鏈表為無序序列,按照原理,一步步進行編碼即可。 * */ public void insertSortNode(){ //判斷鏈表長度大於2,不然只有一個元素,就不用排序了。 if(length()<2){ System.out.println("無需排序"); return; } //創建新鏈表 Node newHead = new Node(0); //新鏈表的頭結點 Node newTemp = newHead; //新鏈表的移動指針 Node temp = head; //舊鏈表的移動指針 if(newTemp.next == null){ //將第一個結點直接放入新鏈表中。 Node node = new Node(temp.next.data); newTemp.next = node; temp = temp.next; //舊鏈表中指針移到下一位(第二個結點處)。 } while(temp.next != null){ // 遍歷現有鏈表 while(newTemp.next != null){ //先跟新鏈表中的第一個結點進行比較,如果符合條件則添加到新鏈表,註意是在第一個位置上增加結點 //如果不符合,則跟新鏈表中第二個結點進行比較,如果都不符合,跳出while,判斷是否是到了新鏈表的最後一個結點,如果是則直接在新鏈表後面添加即可 if(newTemp.next.data < temp.next.data){ Node node = new Node(temp.next.data); node.next = newTemp.next; newTemp.next = node; break; } newTemp = newTemp.next; } if(newTemp.next == null){//到達最末尾還沒符合,那麽說明該值是新鏈表中最小的數,直接添加即可到鏈表中即可 //直接在新鏈表後面添加 Node node = new Node(temp.next.data); newTemp.next = node; } //舊鏈表指針指向下一位結點,繼續重復和新鏈表中的結點進行比較。 temp = temp.next; //新鏈表中的移動指針需要復位,指向頭結點 newTemp = newHead; } //開始使用新鏈表,舊鏈表等待垃圾回收機制將其收回。 head = newHead; }View Code
2.2.6、當然還可以使用冒泡排序、歸並排序,等等等,都可以自己去嘗試,我就不寫了。如果不懂這些排序,那麽就看我寫排序的文章把。
2.2.7、計算單鏈表的長度
/** * 計算單鏈表的長度,也就是有多少個結點 * @return 結點個數 */ public int length() { int length=0; Node temp = head; while(temp.next != null){ length++; temp = temp.next; } return length; }View Code
2.2.8、遍歷單鏈表,打印data
/** * 遍歷單鏈表,打印所有data */ public void print(){ Node temp = head.next; while(temp != null){ System.out.print(temp.data+","); temp = temp.next; } System.out.println(); }View Code
三、總結
基本的單鏈表操作就是上面這些了,自己完全手動寫一寫,會對單鏈表這種數據結構的理解有很大的幫助。當然其中比較難的是在於對鏈表的排序上,說難也不難,只要懂的幾種排序的原理,就跟寫偽代碼一樣,去實現即可。這裏還有一些操作,大家可以動手做一做,我會在下一篇文章中講解這些答案。
3.1、如何從鏈表中刪除重復數據
3.2、如何找出單鏈表中的倒數第k個元素
3.3、如何實現鏈表的反轉
3.4、如何從尾到頭輸出單鏈表
3.5、如何尋找單鏈表的中間結點
3.6、如何檢測一個鏈表是否有環
3.7、如何在不知道頭結點的情況下刪除指定結點
3.8、如何判斷兩個鏈表是否相交
單鏈表的實現-JAVA