由於順序表再插入或者刪除時需要移動大量數據,並且如果表比較大, 會比較難分配連續的存儲空間導致存儲數據失敗。因此可以採用鏈表結構,鏈表結構是一種動態存儲分配的結構形式,可以根據需要動態的申請所需的存儲單元。
鏈表又分爲單鏈表,雙向鏈表,以及單循環鏈表,多重鏈的循環鏈表。本文先介紹單鏈表。
典型的單鏈表結構如圖所示:
鏈表每個結點都應包括如下內容:
- 數據部分,保存的是該結點的實際數據。
- 地址部分,保存的是下一個結點的地址。
鏈表的結構是有許多個這樣的節點構成。在進行單鏈表操作時,首先需要訂一個“頭引用”變量,一般以head表示,該引用變量指向鏈表的結構的第一個結點,第一個結點的地址部分指向第二個結點。。知道最後一個結點,最後一個結點不指向其他結點,稱爲表尾。一般表尾的地址部分放一個空地址null,單鏈表到此結束。整個存儲過程十分;類似於一條長鏈,火車,而結點就是火車廂。所以稱之爲單鏈表,或者鏈式結構。
由於採用了引用來指向下一個數據的地址,因此在鏈表結構中,邏輯上相鄰的結點在內存中並不一定相鄰,邏輯相鄰關係通過地址部分的引用變量來實現。
鏈表結構帶來的最大的好處就是結點之間不需要連續存放,因此保存大量數據時不需要分配連續的存儲空間。可以使用new函數動態分配結點的存儲空間,刪除結點時,將該結點賦值爲null,釋放其佔有的內存空間。
而鏈表的缺點就是浪費存儲空間。
鏈表的訪問只能從表頭逐個查找,即通過表頭head引用,移動指針,從第一個結點一直找到最後一個結點或者需要的結點爲止。不像順序表或者數組通過下標那樣隨機訪問。
以下設計鏈表的數據存儲操作:
1、結點數據:
/**
* 鏈表節點,相當於火車的車廂
* @author Administrator
*
*/
public class MyNode {
//數據域
public long data;
//指針域
public MyNode next;
/**
* 前指針域
*/
public MyNode previous;
/**
* 創建節點,數據域賦值
* @param value
*/
public MyNode(long value){
this.data=value;
}
public void display(){
System.err.print(data+" ");
}
}
鏈表類:
/**
* 鏈表,相當於火車
* @author Administrator
*
*/
public class MyLinkList {
/**
* 頭結點
*/
private MyNode first;
/**
* 構造方法
*/
public MyLinkList(){
first=null;
}
}
2、追加結點
表尾結點的地址部分原來保存的是null,此時需要將其設置爲新增結點的地址,然後將新增結點的地址部分設置爲null,使其成爲表尾。
/**
* 在尾結點後追加結點
* @param value
*/
public void insertEnd(long value){
MyNode node=new MyNode(value);
if (first==null) {
first=node; //鏈表爲null時,即新增節點就是頭結點
}else {
MyNode ntemp=first;
while(ntemp.next!=null){//循環找到尾結點,等於null時跳出循環,即爲尾結點
ntemp=ntemp.next;
}
ntemp.next=node;
}
}
3、插入頭結點
新增結點即爲頭結點。
/**
* 在頭結點後插入節點
* @param value
*/
public void insetFirst(long value){
MyNode node=new MyNode(value);
if (first==null) {
first=node;
}else {
node.next=first;
first=node;
}
}
4、查找結點
根據關鍵字查詢結點。
/**
* 查找方法
* @param value
* @return
*/
public MyNode find(long value){
MyNode currNode=first;
while(currNode!=null){
if (currNode.data==value) {
return currNode;
}
currNode=currNode.next;
}
return null;
}
5、插入結點
在鏈表的中間某一個指定的位置插入一個結點
/**
* 根據關鍵字key,在其中間插入一個結點value
* @param value
* @param key
*/
public void insertFindByKey(long value,long key){
MyNode node=new MyNode(value);
MyNode keyNode=find(key);
if (keyNode==null) {
System.out.println("未找到正確的插入位置!");
}else {
node.next=keyNode.next;
keyNode.next=node;
}
}
6、刪除結點
(1)分爲刪除頭結點後的結點,即第一個結點:
/**
* 刪除頭結點後的節點
* @return
*/
public MyNode deleteFirst(){
MyNode temp=first;//頭結點
first=temp.next;
return temp;
}
(2)刪除第一個結點後的結點
/**
* 刪除
* @param value
* @return
*/
public MyNode delete(long value){
MyNode currNode=first;
MyNode previous=first;
while(currNode.data!=value){
if (currNode.next==null) {
return null;
}
previous=currNode;
currNode=currNode.next;
}
if (currNode==first) {
first=first.next;
}else {
previous.next=currNode.next;
}
return currNode;
}
7、計算鏈表長度:
/**
* 計算鏈表長度,即結點個數
* @return
*/
public int getLinkLength(){
int length=0;
MyNode currNode=first;
while(currNode!=null){
length++;
currNode=currNode.next;
}
return length;
}
8、顯示所有結點
/**
* 顯示結點
*/
public void display(){
MyNode currNode=first;
while(currNode!=null){
currNode.display(); //結點類中已定以這個方法
currNode=currNode.next;
}
}