目錄
1、屬性
2瘪松、鏈表和數(shù)組的區(qū)別
2.1、數(shù)組概述
2.2、數(shù)組和鏈表優(yōu)缺點(diǎn)
2.3诬辈、鏈表和數(shù)組的比較
3样屠、單向鏈表
3.1悦穿、單向鏈表的基本操作
3.1.1、鏈表的遍歷
3.1.2、鏈表的插入
3.1.2.1、在單向鏈表的開頭插入結(jié)點(diǎn)
3.1.2.2、在單向鏈表的結(jié)尾插入結(jié)點(diǎn)
3.1.2.3边坤、在單向鏈表的中間插入結(jié)點(diǎn)
3.1.2.4踢故、函數(shù)實(shí)現(xiàn)
3.1.3耸峭、鏈表的刪除
3.1.3.1洽瞬、刪除鏈表的表頭
3.1.3.2菩颖、刪除鏈表的表尾
3.1.3.3、刪除鏈表中間的結(jié)點(diǎn)
3.1.3.4、函數(shù)實(shí)現(xiàn)
3.1.4、鏈表的釋放
4落塑、雙向鏈表
4.1伴逸、雙向鏈表的插入
4.1.1错蝴、在雙向鏈表的開頭插入結(jié)點(diǎn)
4.1.2洲愤、在雙向鏈表的末尾插入結(jié)點(diǎn)
4.1.3、在雙向鏈表的中間插入一個(gè)結(jié)點(diǎn)
4.1.4顷锰、函數(shù)實(shí)現(xiàn)
4.2柬赐、雙向鏈表的刪除
4.2.1官紫、刪除雙向鏈表的第一個(gè)結(jié)點(diǎn)
4.2.2肛宋、刪除雙向鏈表的最后一個(gè)結(jié)點(diǎn)
4.2.3州藕、刪除雙向鏈表中間的一個(gè)結(jié)點(diǎn)
4.2.4、代碼實(shí)現(xiàn)
5酝陈、循環(huán)鏈表
5.1床玻、遍歷循環(huán)鏈表
5.2、在循環(huán)鏈表的表尾插入結(jié)點(diǎn)
5.3沉帮、在循環(huán)鏈表的表頭插入結(jié)點(diǎn)
5.4锈死、刪除循環(huán)鏈表中的最后一個(gè)結(jié)點(diǎn)
5.5、刪除循環(huán)鏈表中的第一個(gè)結(jié)點(diǎn)
6穆壕、松散鏈表
6.1待牵、在松散鏈表查找一個(gè)元素
6.2、在松散鏈表中插入一個(gè)元素
6.3喇勋、在松散鏈表中執(zhí)行移動(dòng)操作
正文
1缨该、屬性
①:相鄰元素之間通過指針連接。
②:最后一個(gè)元素的后繼指針為NULL茄蚯。
③:鏈表的空間能夠按需分配压彭。
④:沒有內(nèi)存空間的浪費(fèi)。
2渗常、鏈表和數(shù)組的區(qū)別
鏈表和數(shù)組都可以用于存儲(chǔ)數(shù)據(jù)集合壮不,由于兩者的用途相同,所以需要對(duì)它們的用法進(jìn)行區(qū)分皱碘。
2.1询一、數(shù)組概述
整個(gè)數(shù)組所有元素存儲(chǔ)在操作系統(tǒng)分配的一個(gè)內(nèi)存塊中,通過使用特定元素的索引作為數(shù)組下標(biāo)癌椿,可以在常數(shù)時(shí)間內(nèi)訪問數(shù)組元素健蕊。
2.2、數(shù)組和鏈表優(yōu)缺點(diǎn)
- 數(shù)組的優(yōu)點(diǎn):
1踢俄、簡單且易用 缩功。
2、訪問元素為常數(shù)時(shí)間都办。 - 數(shù)組的缺點(diǎn):
1嫡锌、大小固定,在使用數(shù)組前需要指定數(shù)組大小琳钉。
2势木、需要分配一個(gè)連續(xù)空間塊,當(dāng)數(shù)組規(guī)模過大時(shí)歌懒,就無法分配存儲(chǔ)整個(gè)數(shù)組的內(nèi)存空間啦桌。
3、如果要在數(shù)組中插入元素及皂,可能需要移動(dòng)存儲(chǔ)在數(shù)組中的其他元素甫男,這樣才能騰出指定的位置來放插入的元素且改。 - 鏈表的優(yōu)點(diǎn):
1、鏈表可以在常數(shù)時(shí)間內(nèi)擴(kuò)展查剖。
2钾虐、容易添加新元素。 - 鏈表的缺點(diǎn):
1笋庄、訪問單個(gè)元素的時(shí)間開銷問題過大。
2倔监、有時(shí)很難對(duì)鏈表操作直砂,如果要?jiǎng)h除最后一項(xiàng),倒數(shù)第二項(xiàng)必須更改后繼指針為NULL浩习。這需要從頭遍歷鏈表静暂,找到倒數(shù)第二個(gè)結(jié)點(diǎn)的鏈接,并設(shè)置其后置指針為NULL谱秽。
3洽蛀、鏈表中的額外指針引用需要浪費(fèi)內(nèi)存。
2.3疟赊、鏈表和數(shù)組的比較
參數(shù) | 鏈表 | 數(shù)組 |
---|---|---|
索引 | O(n) | O(1) |
在最前端插入/刪除 | O(1) | O(n)郊供,如果數(shù)組空間沒有填滿(需要移動(dòng)元素) |
在最末端插入 | O(n) | O(1),如果數(shù)組空間沒有填滿 |
在最末端刪除 | O(n) | O(1) |
在中間插入 | O(n) | O(n)近哟,如果數(shù)組空間沒有填滿(需要移動(dòng)元素) |
在中間刪除 | O(n) | O(n) 驮审,如果數(shù)組空間沒有填滿(需要移動(dòng)元素) |
空間浪費(fèi) | O(n) | 0 |
3、單向鏈表
鏈表通常是指單向鏈表吉执,它包含多個(gè)結(jié)點(diǎn)疯淫,每個(gè)結(jié)點(diǎn)都有一個(gè)指向后繼元素的next(下一個(gè))指針。表中最后一個(gè)結(jié)點(diǎn)的next值為NULL戳玫,表示鏈表的結(jié)束熙掺。
下面是單向鏈表的類型聲明:
public class ListNode {
private int data;
private ListNode next;
public ListNode(int data){
this.data=data;
}
public void setData(int data){
this.data=data;
}
public int getData(){
return data;
}
public void setNext(ListNode next){
this.next=next;
}
public ListNode getNext() {
return next;
}
}
3.1、單向鏈表的基本操作
- 遍歷鏈表咕宿。
- 在鏈表中插入一個(gè)元素币绩。
- 從鏈表中刪除一個(gè)元素。
- 刪除鏈表荠列。
3.1.1类浪、鏈表的遍歷
假設(shè)表頭結(jié)點(diǎn)的指針指向鏈表中的第一個(gè)結(jié)點(diǎn)。遍歷鏈表需要完成以下步驟:
- 沿指針遍歷肌似。
- 遍歷時(shí)顯示結(jié)點(diǎn)的內(nèi)容费就。
- 當(dāng)next指針為NULL時(shí)結(jié)束遍歷。
下面是遍歷函數(shù)的聲明:
public int ListLength(ListNode headNode){
int length=0;
ListNode currentNode=headNode;
while (currentNode!=null){
length++;
System.out.print(currentNode.getData());
currentNode=currentNode.getNext();
}
return length;
}
3.1.2川队、鏈表的插入
3.1.2.1力细、在單向鏈表的開頭插入結(jié)點(diǎn)
若要在當(dāng)前表頭結(jié)點(diǎn)前插入一個(gè)新結(jié)點(diǎn)睬澡,只需要修改一個(gè)next指針(新結(jié)點(diǎn)的next指針),如下步驟:
-
更新新結(jié)點(diǎn)的next指針眠蚂,使其指向當(dāng)前的表頭結(jié)點(diǎn)煞聪。
-
更新表頭指針的值,使其指向新結(jié)點(diǎn)逝慧。
3.1.2.2昔脯、在單向鏈表的結(jié)尾插入結(jié)點(diǎn)
若要在表尾后插入一個(gè)新結(jié)點(diǎn),則需要修改兩個(gè)next指針(最后一個(gè)結(jié)點(diǎn)的next指針和新結(jié)點(diǎn)的next指針)笛臣,如下步驟:
-
新結(jié)點(diǎn)的next指針指向NULL云稚。
-
最后一個(gè)結(jié)點(diǎn)的next指針指向新結(jié)點(diǎn)。
3.1.2.3沈堡、在單向鏈表的中間插入結(jié)點(diǎn)
假設(shè)在這種情況下插入新結(jié)點(diǎn)静陈,則需要修改兩個(gè)next指針,如下步驟:
-
如果要在位置3增加一個(gè)元素诞丽,則首先要將指針定位在鏈表的位置2鲸拥。即需要從表頭開始經(jīng)過兩個(gè)結(jié)點(diǎn),然后插入新結(jié)點(diǎn)僧免。為了簡單起見刑赶,假設(shè)第二個(gè)結(jié)點(diǎn)為位置結(jié)點(diǎn),新結(jié)點(diǎn)的next指針指向位置結(jié)點(diǎn)的下一個(gè)結(jié)點(diǎn)猬膨。
-
位置結(jié)點(diǎn)的next指針指向新結(jié)點(diǎn)角撞。
3.1.2.4、函數(shù)實(shí)現(xiàn)
ListNode InsertInLinkedList(ListNode headNode,ListNode nodeToInsert,int position){
if(headNode==null){
return headNode;
}
//求鏈表長度
int size=ListLength(headNode);
if(position>size+1||position<1){
System.out.print("position 的范圍在1-(size+1)");
return headNode;
}
if(position==1){
//在鏈表開頭插入
nodeToInsert.setNext(headNode);
return nodeToInsert;
}
else {
//在鏈表中間或者末尾插入
//位置結(jié)點(diǎn)
ListNode previousNode=headNode;
int count=1;
while (count<position-1){
previousNode=headNode.getNext();
count++;
}
//位置結(jié)點(diǎn)的下一結(jié)點(diǎn)
ListNode currentNode=previousNode.getNext();
//更新新結(jié)點(diǎn)
nodeToInsert.setNext(currentNode);
//更新位置結(jié)點(diǎn)
previousNode.setNext(nodeToInsert);
}
return headNode;
}
3.1.3勃痴、鏈表的刪除
3.1.3.1谒所、刪除鏈表的表頭
-
創(chuàng)建一個(gè)臨時(shí)結(jié)點(diǎn),它指向表頭指針?biāo)赶虻慕Y(jié)點(diǎn)沛申。
-
修改表頭指針的值劣领,使其指向下一個(gè)結(jié)點(diǎn),并移除臨時(shí)結(jié)點(diǎn)铁材。
3.1.3.2尖淘、刪除鏈表的表尾
該操作比刪除鏈表的第一個(gè)結(jié)點(diǎn)要稍微復(fù)雜一些,因?yàn)樗惴ㄐ枰业奖砦步Y(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)著觉。分三步實(shí)現(xiàn):
-
遍歷鏈表村生,在遍歷時(shí)還要保存前驅(qū)結(jié)點(diǎn)的地址,當(dāng)遍歷到鏈表的表尾時(shí)饼丘,將有兩個(gè)指針趁桃,分別是表尾結(jié)點(diǎn)的指針和指向表尾結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)的指針。
-
將表尾的前驅(qū)結(jié)點(diǎn)的next指針更新為NULL。
-
移除表尾結(jié)點(diǎn)
3.1.3.3卫病、刪除鏈表中間的結(jié)點(diǎn)
這種情況下油啤,被刪除的結(jié)點(diǎn)總是位于兩個(gè)結(jié)點(diǎn)之間,因此不需要更新表頭和表尾的指針蟀苛。分兩步實(shí)現(xiàn):
-
遍歷鏈表益咬,在遍歷時(shí)保存前驅(qū)結(jié)點(diǎn)的地址,一旦找到被刪除的結(jié)點(diǎn)帜平,將前驅(qū)結(jié)點(diǎn)的next指針的值更新為被刪除的結(jié)點(diǎn)的next指針的值幽告。
-
移除需刪除的當(dāng)前結(jié)點(diǎn)
3.1.3.4、函數(shù)實(shí)現(xiàn)
ListNode DeleteNodeFromLinkedList(ListNode headNode,int position){
//求鏈表長度
int size=ListLength(headNode);
if(position>size||position<1){
System.out.print("position 的范圍在1-size");
return headNode;
}
if(position==1){
//刪除表頭
ListNode currentNode=headNode.getNext();
headNode=null;
return currentNode;
}else {
//刪除中間或表尾
//前驅(qū)結(jié)點(diǎn)
ListNode previousNode=headNode;
int count=1;
while(count<position-1){
previousNode=previousNode.getNext();
count++;
}
//要?jiǎng)h除的結(jié)點(diǎn)
ListNode currentNode=previousNode.getNext();
//更新前驅(qū)結(jié)點(diǎn)
previousNode.setNext(currentNode.getNext());
//刪除當(dāng)前結(jié)點(diǎn)
currentNode=null;
}
return headNode;
}
3.1.4裆甩、鏈表的釋放
這個(gè)操作通過將當(dāng)前結(jié)點(diǎn)存儲(chǔ)在臨時(shí)變量中评腺,然后釋放當(dāng)前結(jié)點(diǎn)。當(dāng)釋放完當(dāng)前結(jié)點(diǎn)后淑掌,移動(dòng)到下一個(gè)結(jié)點(diǎn)并將其存儲(chǔ)在臨時(shí)變量中,然后不斷重復(fù)該過程直至釋放所有結(jié)點(diǎn)蝶念。代碼如下:
void DeleteLinkedList(ListNode headNode){
//臨時(shí)變量 和 迭代器
ListNode tempNode,iterator=headNode;
while (iterator!=null){
tempNode=iterator.getNext();
iterator=null;
iterator=tempNode;
}
}
4抛腕、雙向鏈表
- 雙向鏈表的優(yōu)點(diǎn):對(duì)于鏈表中一個(gè)給定的結(jié)點(diǎn),可以從兩個(gè)方向進(jìn)行操作媒殉。
- 雙向鏈表的缺點(diǎn):每個(gè)結(jié)點(diǎn)需要一個(gè)額外的指針担敌,因此需要更多的開銷,插入和刪除時(shí)更費(fèi)時(shí)廷蓉。
下面給出雙向鏈表的類型聲明:
public class DLLNode {
private int data;
private DLLNode previous;
private DLLNode next;
public DLLNode(int data){
this.data=data;
}
public void setData(int data) {
this.data = data;
}
public int getData() {
return data;
}
public void setPrevious(DLLNode previous) {
this.previous = previous;
}
public DLLNode getPrevious() {
return previous;
}
public void setNext(DLLNode next) {
this.next = next;
}
public DLLNode getNext() {
return next;
}
}
4.1全封、雙向鏈表的插入
分為以下三種情況:
- 在鏈表的開頭前插入一個(gè)新結(jié)點(diǎn)。
- 在鏈表的末尾后插入一個(gè)新結(jié)點(diǎn)桃犬。
- 在鏈表的中間插入一個(gè)新結(jié)點(diǎn)刹悴。
4.1.1、在雙向鏈表的開頭插入結(jié)點(diǎn)
-
將新結(jié)點(diǎn)的后繼指針更新為指向當(dāng)前的表頭結(jié)點(diǎn)攒暇,將前驅(qū)指針賦值為NULL土匀。
-
將表頭結(jié)點(diǎn)的前驅(qū)指針更新為指向新結(jié)點(diǎn),然后將新結(jié)點(diǎn)作為表頭形用。
4.1.2就轧、在雙向鏈表的末尾插入結(jié)點(diǎn)
這種情況下需要遍歷到鏈表的最后,然后插入新結(jié)點(diǎn)田度。
-
將新結(jié)點(diǎn)的后繼指針賦值為NULL妒御,前驅(qū)指針指向表尾結(jié)點(diǎn)。
-
將表尾結(jié)點(diǎn)的后繼指針更新為指向新結(jié)點(diǎn)镇饺。
4.1.3乎莉、在雙向鏈表的中間插入一個(gè)結(jié)點(diǎn)
-
將新結(jié)點(diǎn)的后繼指針賦值為位置結(jié)點(diǎn)的后繼結(jié)點(diǎn),前驅(qū)指針指向位置結(jié)點(diǎn)。
-
將位置結(jié)點(diǎn)的后繼結(jié)點(diǎn)的前驅(qū)指針賦值為新結(jié)點(diǎn)梦鉴,將位置結(jié)點(diǎn)的后繼指針賦值為新結(jié)點(diǎn)李茫。
4.1.4肥橙、函數(shù)實(shí)現(xiàn)
public DLLNode DLLInsert(DLLNode headNode,DLLNode nodeToInsert,int position){
if(headNode==null){
return nodeToInsert;
}
//求鏈表長度
int size=ListLength(headNode);
if(position<1||position>size+1){
System.out.print("position 的范圍在1-(size+1)");
return headNode;
}
if(position==1){
//在鏈表開頭插入
nodeToInsert.setNext(headNode);
headNode.setPrevious(nodeToInsert);
return nodeToInsert;
}else {
//在鏈表中間或者表尾插入
//位置結(jié)點(diǎn)
DLLNode previousNode=headNode;
int count=1;
while (count<position-1){
previousNode=previousNode.getNext();
count++;
}
//位置結(jié)點(diǎn)的后繼結(jié)點(diǎn)
DLLNode currentNode=previousNode.getNext();
//更新新結(jié)點(diǎn)
nodeToInsert.setNext(currentNode);
nodeToInsert.setPrevious(previousNode);
//更新位置結(jié)點(diǎn)
previousNode.setNext(nodeToInsert);
//更新位置結(jié)點(diǎn)的后繼結(jié)點(diǎn)
if(currentNode!=null){
currentNode.setPrevious(nodeToInsert);
}
}
return headNode;
}
4.2魄宏、雙向鏈表的刪除
分為以下三種情況:
- 刪除鏈表的表頭。
- 刪除鏈表的表尾存筏。
- 刪除鏈表中間的一個(gè)結(jié)點(diǎn)宠互。
4.2.1、刪除雙向鏈表的第一個(gè)結(jié)點(diǎn)
-
創(chuàng)建一個(gè)臨時(shí)結(jié)點(diǎn)椭坚,它與表頭指向同一個(gè)結(jié)點(diǎn)予跌。
-
修改表頭指針,使其指向下一個(gè)結(jié)點(diǎn)善茎,將表頭指針的前驅(qū)指針賦值為NULL券册,然后移除臨時(shí)結(jié)點(diǎn)。
4.2.2烁焙、刪除雙向鏈表的最后一個(gè)結(jié)點(diǎn)
需要找到表尾結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn),分以下三步進(jìn)行:
-
遍歷鏈表耕赘,同時(shí)保存前驅(qū)結(jié)點(diǎn)的地址骄蝇。當(dāng)遍歷到表尾時(shí),有兩個(gè)指針分別是指向表尾結(jié)點(diǎn)的指針和指向表尾結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)的指針操骡。
-
更新表尾結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)的后繼指針的值為NULL。
-
移除表尾結(jié)點(diǎn)册招。
4.2.3岔激、刪除雙向鏈表中間的一個(gè)結(jié)點(diǎn)
-
與一種刪除情況類似,在遍歷鏈表時(shí)保存前驅(qū)結(jié)點(diǎn)跨细。一旦找到要?jiǎng)h除的結(jié)點(diǎn)鹦倚,更改前驅(qū)結(jié)點(diǎn)的后繼指針使其指向被刪除結(jié)點(diǎn)的后繼結(jié)點(diǎn),更改被刪除結(jié)點(diǎn)的后繼結(jié)點(diǎn)的前驅(qū)指針指向被刪除結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)冀惭。
-
移除被刪除的當(dāng)前結(jié)點(diǎn)震叙。
4.2.4、代碼實(shí)現(xiàn)
public DLLNode DLLDelete(DLLNode headNode,int position){
int size=ListLength(headNode);
if(position<1||position>size){
System.out.print("position 的范圍在1-size");
return headNode;
}
if(position==1){
//刪除鏈表的第一個(gè)結(jié)點(diǎn)
DLLNode currentNode=headNode.getNext();
currentNode.setPrevious(null);
headNode=null;
return currentNode;
}else {
//刪除鏈表的中間結(jié)點(diǎn)或者最后一個(gè)結(jié)點(diǎn)
//前驅(qū)結(jié)點(diǎn)
DLLNode previousNode=headNode;
int count=1;
while(count<position-1){
previousNode=previousNode.getNext();
count++;
}
//刪除結(jié)點(diǎn)
DLLNode currentNode=previousNode.getNext();
//后繼結(jié)點(diǎn)
DLLNode laterNode=currentNode.getNext();
//更新前驅(qū)結(jié)點(diǎn)
previousNode.setNext(laterNode);
//更新后繼結(jié)點(diǎn)
if(laterNode!=null){
laterNode.setPrevious(previousNode);
}
currentNode=null;
}
return headNode;
}
5散休、循環(huán)鏈表
在單向鏈表和雙向鏈表中媒楼,都采用NULL值作為鏈表的結(jié)束。然而循環(huán)鏈表沒有結(jié)束標(biāo)志戚丸。下面給出循環(huán)鏈表的類型聲明:
public class CLLNode {
private int data;
private CLLNode next;
public CLLNode(int data){
this.data=data;
}
public void setData(int data) {
this.data = data;
}
public int getData() {
return data;
}
public void setNext(CLLNode next) {
this.next = next;
}
public CLLNode getNext() {
return next;
}
}
5.1划址、遍歷循環(huán)鏈表
循環(huán)鏈表可以通過標(biāo)記為表頭的結(jié)點(diǎn)進(jìn)行訪問扔嵌,從標(biāo)記為表頭的結(jié)點(diǎn)開始遍歷,利用虛擬結(jié)點(diǎn)——當(dāng)前結(jié)點(diǎn)夺颤,當(dāng)當(dāng)前結(jié)點(diǎn)再次達(dá)到開始結(jié)點(diǎn)時(shí)結(jié)束遍歷痢缎。代碼實(shí)現(xiàn)如下:
public int CircularListLength(CLLNode headNode){
int length=0;
CLLNode currentNode=headNode;
while (currentNode!=null){
length++;
currentNode=currentNode.getNext();
if(currentNode==headNode){
break;
}
}
return length;
}
5.2、在循環(huán)鏈表的表尾插入結(jié)點(diǎn)
在由表頭開始的循環(huán)鏈表的表尾后插入一個(gè)新結(jié)點(diǎn)世澜,也就是在表尾結(jié)點(diǎn)和第一個(gè)結(jié)點(diǎn)之間插入該新結(jié)點(diǎn)独旷。
-
創(chuàng)建一個(gè)新結(jié)點(diǎn),并且初始化其next指針指向該結(jié)點(diǎn)自身寥裂。
-
更新新結(jié)點(diǎn)的next指針為表頭結(jié)點(diǎn)嵌洼,然后循環(huán)遍歷鏈表直至表尾。即插入位置應(yīng)為循環(huán)鏈表中下一個(gè)結(jié)點(diǎn)是表頭結(jié)點(diǎn)的結(jié)點(diǎn)位置封恰。
-
更新表頭結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)的next指針指向新結(jié)點(diǎn)麻养。
- 代碼實(shí)現(xiàn):
public void InsertAtEndInCLL(CLLNode headNode,CLLNode nodeToInsert){
CLLNode currentNode=headNode;
while (currentNode.getNext()!=headNode){
currentNode=currentNode.getNext();
}
//初始化
nodeToInsert.setNext(nodeToInsert);
if(headNode==null){
headNode=nodeToInsert;
}
else {
//更新新結(jié)點(diǎn)
nodeToInsert.setNext(headNode);
//更新表頭的前驅(qū)結(jié)點(diǎn)
currentNode.setNext(nodeToInsert);
}
}
5.3、在循環(huán)鏈表的表頭插入結(jié)點(diǎn)
與上述表尾后插入結(jié)點(diǎn)的唯一區(qū)別是诺舔,在插入新結(jié)點(diǎn)后鳖昌,還需要更新指針。
-
創(chuàng)建一個(gè)新結(jié)點(diǎn)低飒,并且初始化其next指針指向該結(jié)點(diǎn)自身遗遵。
-
更新新結(jié)點(diǎn)的next指針為表頭結(jié)點(diǎn),然后循環(huán)遍歷鏈表直至表尾逸嘀。即插入位置應(yīng)為循環(huán)鏈表中下一個(gè)結(jié)點(diǎn)是表頭結(jié)點(diǎn)的結(jié)點(diǎn)位置。
-
更新表頭結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)的next指針指向新結(jié)點(diǎn)允粤。
-
設(shè)置新結(jié)點(diǎn)為表頭結(jié)點(diǎn)崭倘。
代碼實(shí)現(xiàn):
public void InsertAtBeginInCLL(CLLNode headNode,CLLNode nodeToInsert){
CLLNode currentNode=headNode;
while (currentNode.getNext()!=headNode){
currentNode=currentNode.getNext();
}
//初始化
nodeToInsert.setNext(nodeToInsert);
if(headNode==null){
headNode=nodeToInsert;
}
else {
//更新新結(jié)點(diǎn)
nodeToInsert.setNext(headNode);
//更新表頭的前驅(qū)結(jié)點(diǎn)
currentNode.setNext(nodeToInsert);
//設(shè)置新結(jié)點(diǎn)為表頭
headNode=nodeToInsert;
}
}
5.4、刪除循環(huán)鏈表中的最后一個(gè)結(jié)點(diǎn)
為了刪除最后一個(gè)結(jié)點(diǎn)类垫,需要遍歷循環(huán)鏈表找到倒數(shù)第二個(gè)結(jié)點(diǎn)司光。該結(jié)點(diǎn)成為了新的表尾結(jié)點(diǎn),其next指針指向第一個(gè)結(jié)點(diǎn)悉患。
-
遍歷循環(huán)鏈表残家,找到表尾結(jié)點(diǎn)及其前驅(qū)結(jié)點(diǎn)。
-
更新表尾結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)的next指針售躁,使其指向表頭結(jié)點(diǎn)坞淮。
-
移除表尾結(jié)點(diǎn)。
代碼實(shí)現(xiàn):
public void DeleteLastNodeFromCLL(CLLNode headNode){
//表尾的前驅(qū)結(jié)點(diǎn)
CLLNode temp=headNode;
//表尾
CLLNode currentNode=headNode;
if(headNode==null){
System.out.print("List is empty");
return;
}
while (currentNode.getNext()!=headNode){
temp=currentNode;
currentNode=currentNode.getNext();
}
//更新表尾的前驅(qū)結(jié)點(diǎn)
temp.setNext(headNode);
//移除表尾
currentNode=null;
return;
}
5.5陪捷、刪除循環(huán)鏈表中的第一個(gè)結(jié)點(diǎn)
只需要將表尾結(jié)點(diǎn)的next指針指向第一個(gè)結(jié)點(diǎn)的后繼結(jié)點(diǎn)回窘。
-
遍歷循環(huán)鏈表找到表尾結(jié)點(diǎn),就是要?jiǎng)h除結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)市袖。
-
創(chuàng)建一個(gè)指向表頭結(jié)點(diǎn)的臨時(shí)結(jié)點(diǎn)啡直,更新表尾結(jié)點(diǎn)的next指針,使其指向第一個(gè)結(jié)點(diǎn)的后繼結(jié)點(diǎn)。
-
修改表頭指針的值酒觅,使其指向其后繼結(jié)點(diǎn)撮执,移除臨時(shí)結(jié)點(diǎn)。
代碼實(shí)現(xiàn):
public void DeleteFrontNodeFromCLL(CLLNode headNode){
//臨時(shí)結(jié)點(diǎn)
CLLNode temp=headNode;
//表尾結(jié)點(diǎn)
CLLNode currentNode=headNode;
if(headNode==null){
System.out.print("List Empty");
return;
}
while (currentNode.getNext()!=headNode){
currentNode=currentNode.getNext();
}
//更新表尾結(jié)點(diǎn)
currentNode.setNext(headNode.getNext());
//更新表頭
headNode=headNode.getNext();
//移除臨時(shí)結(jié)點(diǎn)
temp=null;
return;
}
6舷丹、松散鏈表
與數(shù)組相比抒钱,鏈表的最大優(yōu)勢(shì)在于,在任何位置插入元素的時(shí)間開銷僅為O(1)掂榔。然而在鏈表中查找某元素的時(shí)間開銷則是O(n)继效。下面介紹單向鏈表的簡單變種,稱為松散鏈表装获。
松散鏈表中的每個(gè)結(jié)點(diǎn)存儲(chǔ)多個(gè)元素(簡稱為塊)瑞信。而每一塊中的所有結(jié)點(diǎn)由循環(huán)鏈表鏈接在一起。
- 假設(shè)在任何時(shí)候松散鏈表的元素個(gè)數(shù)不超過n穴豫,為了進(jìn)一步簡化問題凡简,假設(shè)除了最后一塊外,其它塊恰好有? √n?(表示不小于根號(hào)n的最小整數(shù))精肃,所以在任何時(shí)候秤涩,松散鏈表中的個(gè)數(shù)不會(huì)超過?√n?(表示不大于根號(hào)n的最大整數(shù))。舉個(gè)例子司抱,n為10的時(shí)候筐眷,? √n?=4,?√n?=2习柠。
6.1匀谣、在松散鏈表查找一個(gè)元素
在松散鏈表中查找第k個(gè)元素,時(shí)間復(fù)雜度為O(√n)资溃。具體描述如下:
- 遍歷鏈表找到包含第k個(gè)結(jié)點(diǎn)的塊武翎,已知每塊? √n?個(gè)結(jié)點(diǎn),所以在第? k/? √n??塊溶锭。舉個(gè)例子宝恶,n為10的時(shí)候,? √n?=4趴捅,假設(shè)此時(shí)k=9垫毙。那么? 9/4?=3,即不小于9/4的最小整數(shù)拱绑。因?yàn)閗<=n露久,所以最多遍歷√n個(gè)塊,那么時(shí)間復(fù)雜度也就是O(√n)欺栗。
- 接著在這個(gè)塊的循環(huán)鏈表找到第(k mod ? √n?)個(gè)結(jié)點(diǎn)毫痕。這個(gè)過程的時(shí)間復(fù)雜度同樣也是O(√n)征峦,原因是每塊中的結(jié)點(diǎn)個(gè)數(shù)最多為? √n?。
6.2消请、在松散鏈表中插入一個(gè)元素
當(dāng)插入結(jié)點(diǎn)的時(shí)候钉答,可能需要調(diào)整松散鏈表中的結(jié)點(diǎn)以保證前面提到的鏈表屬性豫缨,也就是除了最后一塊外,其它塊恰好有? √n?個(gè)結(jié)點(diǎn)。舉個(gè)例子蝴乔,如下圖:
6.3径荔、在松散鏈表中執(zhí)行移動(dòng)操作
上述插入過程中會(huì)對(duì)一些元素進(jìn)行移動(dòng)少辣,使其滿足松散鏈表的相關(guān)性質(zhì)耐齐。其實(shí)每次移動(dòng)操作包括從塊中的循環(huán)鏈表的表尾移除一個(gè)結(jié)點(diǎn),并在下一塊中的循環(huán)鏈表的表頭插入一個(gè)結(jié)點(diǎn)需频,時(shí)間復(fù)雜度為O(1)丁眼。那么可以推出在松散鏈表中插入一個(gè)元素的時(shí)間復(fù)雜度為O(√n),因?yàn)樽疃鄨?zhí)行√n次移動(dòng)操作昭殉。