走進資料結構和演算法(c++版)(3)——線性表的鏈式儲存結構
線性表的鏈式儲存結構
我們知道線性表的順序儲存結構在插入和刪除操作時需要移動大量的資料,他們的時間複雜度為。當我們需要經常插入和刪除資料時,順序儲存結構就不適用了,這時我們就需要用到線性表的鏈式儲存結構。
線性表的鏈式儲存結構的特點是用一組任意的儲存單元儲存線性表的資料元素,這組儲存單元可以是連續的,也可以是不連續的。這就意味著,這些資料元素可以存在記憶體未被佔用的任意位置。我們先來看下鏈式儲存結構。
上圖是鏈式儲存結構中單鏈表的示意圖。從圖中,我們可以看出一個鏈式儲存結構由若干個結點(Node),串聯而成。每個結點有兩個部分,資料域和指標域。資料域中儲存資料元素,指標域中儲存後繼指標地址。為了能夠找到連結串列位置,就需要一個指向連結串列第一個元素的指標,我們把它稱為頭指標。此外,我們把連結串列中第一個結點叫做頭結點。頭結點的資料域一般不儲存任何資訊,也可以用於儲存線性表的長度等附加資訊。
線性錶鏈式儲存結構程式碼描述
下面我們以單鏈表為例,可以用C++的類來描述。
class Node
{
public:
Node();
~Node();
friend void traverse_list(Node *pHead);//輸出整個連結串列
friend Node * create_list();//建立非迴圈單鏈表
friend bool is_empty_list(Node *pHead);//判斷是否為空
friend int length_list(Node *pHead);//連結串列長度
friend bool insert_list(Node *pHead, int pos, int val);//在pos位置插入值val
friend bool delet_list(Node *pHead, int pos , int*pval);//將pos位置的值刪除
friend void sort_list(Node *pHead);//排序
private:
int data;//資料域
Node *pNext;//指標域
};
我們再看下每個操作的具體實現程式碼:
Node * create_list()//建立非迴圈單鏈表
{
int len;
int val;//存放臨時的值
cout << "請輸入要生成的連結串列節點的個數:len=" ;
cin >> len;
cout << endl;
Node* pHead = new Node;//頭節點
pHead->pNext = NULL;
Node* pTail = pHead;
for (int i = 0; i < len; i++)
{
cout << "請輸入第" << i + 1 << "個節點的值:";
cin >> val;
cout << endl;
Node* pNew = new Node;
pNew->data = val;
pTail->pNext = pNew;
pNew->pNext = NULL;
pTail = pNew;
}
return pHead;
}
void traverse_list(Node *pHead)//輸出整個連結串列
{
Node *p = pHead->pNext;
while (p != NULL)
{
cout << p->data << endl;
p = p->pNext;
}
}
bool is_empty_list(Node *pHead)//判斷是否為空
{
return pHead->pNext == NULL ? true : false;
}
int length_list(Node *pHead)//連結串列長度
{
Node* p = pHead;
int len = 0;
while (p->pNext != NULL)
{
len++;
p = p->pNext;
}
return len;
}
void sort_list(Node *pHead)//排序
{
int i, j, t;
int len = length_list(pHead);
Node *p, *q;
for (i = 0, p = pHead->pNext; i < len - 1; i++, p = p->pNext)
{
for (j = i+1, q = p->pNext; j < len; j++, q = q->pNext)
{
if (p->data > q->data)
{
t = p->data;
p->data = q->data;
q->data = t;
}
}
}
}
bool insert_list(Node *pHead, int pos, int val)//在pos位置插入值val
{
int i = 0;
Node* p=pHead;
while (p != NULL&&i < pos - 1)
{
p = p->pNext;
i++;
}
if (i>pos - 1 || p == NULL)
return false;
Node*pNew = new Node;
pNew->data = val;
pNew->pNext = p->pNext;
p->pNext = pNew;
return true;
}
bool delet_list(Node *pHead, int pos, int* pval)//將pos位置的值刪除
{
int i = 0;
Node* p = pHead;
while (p->pNext != NULL&&i < pos - 1)
{
p = p->pNext;
i++;
}
if (i>pos - 1 || p->pNext == NULL)
return false;
Node *q = p->pNext;
*pval = q->data;
p->pNext = p->pNext->pNext;
delete q;
return true;
}
我們先來看下單鏈表的建立,其實就相當於一個數組的初始化。其演算法思路如下:
- 宣告一結點 p 和長度變數 len
- 初始化一空連結串列 L
- 讓 L 的頭結點的指標指向 NULL ,即建立一個帶頭結點的單鏈表i
- 迴圈:
生成一新結點賦值給 p;
隨機生成一數字賦值給 p 的資料域 p->data;
將 p 插入到連結串列中。
要輸出整個連結串列則先讀取頭結點下個結點資料域中的資料元素,再讀取指標域中的地址,將指標指向下一個結點。
#include<iostream>
#include "Node.h"
using namespace std;
Node * create_list();
void traverse_list(Node *pHead);
bool is_empty_list(Node *pHead);
int length_list(Node *pHead);
bool insert_list(Node *pHead,int pos,int val);
bool delet_list(Node *pHead,int pos,int* pval);
void sort_list(Node *pHead);
int main()
{
Node* pHead = NULL;
int val;
pHead = create_list();
system("pause");
return 0;
}
在VS下執行結果如下:
單鏈表第 i 個數據插入結點的演算法思路如下:
- 宣告一結點 p 指向連結串列第一個結點,初始化 j 從 1 開始
- 當時,就遍歷連結串列,讓 p 的指標向後移動,不斷指向下一結點, j 累加 1
- 若到鏈衰末尾 p 為空,則說明第 i 個元素不存在
- 否則查詢成功,在系統中生成一個空結點 s;
- 將資料元素 e 賦值給s->data ;
- 單鏈表的插入標準語句 s->next=p->next.; p->next=s ;
- 返回成功。
#include<iostream>
#include "Node.h"
using namespace std;
Node * create_list();
void traverse_list(Node *pHead);
bool is_empty_list(Node *pHead);
int length_list(Node *pHead);
bool insert_list(Node *pHead,int pos,int val);
bool delet_list(Node *pHead,int pos,int* pval);
void sort_list(Node *pHead);
int main()
{
Node* pHead = NULL;
int val;
pHead = create_list();
insert_list(pHead, 2, 9);
traverse_list(pHead);
system("pause");
return 0;
}
在VS下執行結果如下:
單鏈表第 i 個數據刪除結點的演算法思路:
- 宣告一結點 p 指向連結串列第一個結點, 初始化j 從 1 開始j;
- 當時,就遍歷連結串列,讓 p 的指標向後移動,不斷指向下一結點, j 累加 1;
- 若到連結串列末尾p 為空,則說明第 i 個元素不存在;
- 否則查詢成功,將欲刪除的結點 p->next 賦值給q ;
- 主在連結串列的刪除標準語句 p->next=q->next;
- 將 q 結點中的資料賦值給e ,作為返回;
- 釋放q 結點;
- 返回成功。
#include<iostream>
#include "Node.h"
using namespace std;
Node * create_list();
void traverse_list(Node *pHead);
bool is_empty_list(Node *pHead);
int length_list(Node *pHead);
bool insert_list(Node *pHead,int pos,int val);
bool delet_list(Node *pHead,int pos,int* pval);
void sort_list(Node *pHead);
int main()
{
Node* pHead = NULL;
int val;
pHead = create_list();
traverse_list(pHead);
delet_list(pHead, 2, &val);
cout << "被刪除的資料元素是:" << val << endl;
traverse_list(pHead);
system("pause");
return 0;
}
在VS下執行結果如下:
單鏈表結構與順序儲存結構優缺點
儲存分配方式
- 順序儲存結構:連續依次儲存。
- 單鏈表:一組任意的儲存單元可以離散儲存。
時間效能
查詢 | 插入和刪除 | |
---|---|---|
順序儲存結構 | O(1) | O(n) |
單鏈表 | O(n) | O(1) |
空間效能
- 順序儲存結構:需要預分配儲存空間,分大了浪費,分小了易發生上溢。
- 單鏈表:不需要預分配儲存空間,只要有就可以分配,元素個數也不受限制。
其他的鏈式儲存結構
下面我們簡單介紹下其他幾種鏈式儲存結構。
靜態連結串列
靜態連結串列是用陣列描述的連結串列,又叫做遊標實現法。它其實是為了給沒有指標的高階語言設計的一種實現單鏈表能力的方法。
迴圈連結串列
將單鏈表中終端結點的指標端由空指標改為指向頭結點,就使整個單鏈表形成一個環,這種頭尾相接的單鏈表稱為單迴圈連結串列,簡稱迴圈連結串列 ( circular linked list) 。
雙向連結串列
雙向連結串列(double linked List) 是在單鏈表的每個結點中,再設定一個指向其前驅結點的指標域。所以在雙向連結串列中的結點都有兩個指標域, 一個指向直接後繼,另一個指向直接前驅。
相關推薦
線性表——鏈式儲存結構合併操作
採取的結構和上一篇博文一致,均為單鏈表儲存結構。#include<iostream> #include<stdio.h> #include<stdlib.h> #define ElemType int #define Status
走進資料結構和演算法(c++版)(3)——線性表的鏈式儲存結構
線性表的鏈式儲存結構 我們知道線性表的順序儲存結構在插入和刪除操作時需要移動大量的資料,他們的時間複雜度為O(n)O(n)。當我們需要經常插入和刪除資料時,順序儲存結構就不適用了,這時我們就需要用到線性表的鏈式儲存結構。 線性表的鏈式儲存結構的特點是
資料結構和演算法精講版(陣列、棧、佇列、連結串列、遞迴、排序、二叉樹、紅黑樹、堆、雜湊表)Java版
查詢和排序是最基礎也是最重要的兩類演算法,熟練地掌握這兩類演算法,並能對這些演算法的效能進行分析很重要,這兩類演算法中主要包括二分查詢、快速排序、歸併排序等等。我們先來了解查詢演算法! 順序查詢: 順序查詢又稱線性查詢。它的過程為:從查詢表的最後一個元素開始逐個與給定關鍵字比較,若某個記錄的關鍵字和給定值比較
資料結構與演算法分析c語言描述(Mark Allen)--佇列ADT連結串列實現
佇列ADT連結串列實現 使用連結串列儲存 操作集合 入隊 出隊 初始化 返回隊前元素 列印 #include <stdio.h> #includ
資料結構與演算法分析c語言描述(Mark Allen)--迴圈佇列ADT陣列實現
迴圈佇列ADT陣列實現 使用陣列儲存 操作集合 入隊 出隊 清空 初始化 返回隊前元素 列印 重點注意! 對於一個迴圈佇列 front == rear時候佇列
資料結構與演算法分析c語言描述(Mark Allen)--線性錶鏈表方法實現
線性表--連結串列實現 標頭檔案 #define ElementType int #define INF INT_MAX #ifndef _List_H struct Node; typedef struct Node *PtrToNode; typedef PtrToN
資料結構與演算法分析c語言描述(Mark Allen)--多項式ADT陣列實現
多項式ADT陣列實現 使用陣列進行儲存 操作集合 乘法 加法 標頭檔案 //cpp head file PloynomialADTarray.h #define MaxDegree 1000 typedef struct Pol { int C
資料結構與演算法分析c語言描述(Mark Allen)--多項式ADT連結串列實現
多項式ADT連結串列實現 使用連結串列結構儲存 操作集合 多項式加法 多項式乘法 多項式的顯示 標頭檔案 //標頭檔案 typedef struct Node *PtrToNode; struct Node { int Cofficient
資料結構與演算法分析c語言描述(Mark Allen)--棧ADT陣列實現
棧ADT陣列實現 使用陣列儲存 操作集合 入棧push 出棧pop 清空 初始化 返回棧頂元素 得到一個隨機棧 列印整個棧 #include <stdio.h> #include <stdlib.h> #include <
Java資料結構和演算法中文第二版.pdf免費下載
. ● 2-3樹 ● 揹包問題 ● 從n個事物中取k個的組合方案 ● 雜湊函式的數字摺疊法 ● 基數排序 章末問題 每章結束後都會有幾個簡短的問題,它們涵蓋了該章的所有重點。在附錄C“問題答案”中可以找到相應的答案。這些問題是用來給讀者自測的,以確保他們已經基本理解該章的內容。 實驗 本書中收入了一些希
JAVA資料結構和演算法:第三章(棧和佇列)
棧 棧是限制僅在一個位置上進行插入和刪除的線性表。允許插入和刪除的一端為末端,稱為棧頂。另一端稱為棧底。不含任何資料元素的棧稱為空棧。棧又成為後進先出(LIFO)表,後進入的元素最先出來。 首先,棧是一個線性表,元素之間具有線性關係,即前驅後繼關係,其次,
資料結構與演算法分析c語言描述(Mark Allen)--棧ADT連結串列實現
棧ADT連結串列實現 使用連結串列儲存 操作集合 入棧push 出棧pop 清空 初始化 返回棧頂元素 列印整個棧 #include <stdio.h> #include <time.h> #include <stdli
重讀《學習JavaScript資料結構與演算法-第三版》- 第3章 陣列(二)
定場詩 守法朝朝憂悶,強梁夜夜歡歌; 損人利己騎馬騾,正值公平捱餓; 修橋補路瞎眼,殺人放火兒多; 我到西天問我佛,佛說:我也沒轍! 前言 讀《學習JavaScript資料結構與演算法》- 第3章 陣列,本小節將繼續為各位小夥伴分享陣列的相關知識:ES6陣列的新功能。 一、ES6陣列新功能 ES5和ES6陣列
使用Async和Await進行異步編程(C#版 適用於VS2015)
send click cli inpu 成員 出錯 obj aging ros 你可以使用異步編程來避免你的應用程序的性能瓶頸並且加強總體的響應。然而,用傳統的技術來寫異步應用是復雜的,同時編寫,調試和維護都很困難。VS2012介紹了簡單的方法,那就是異步編程,它在.Net
資料結構學習筆記——堆疊之鏈式儲存結構(c語言實現)
棧的鏈式儲存結構使用單鏈表實現,同線性表一樣,鏈式儲存結構的堆疊在儲存空間的利用上顯得更加靈活,只要硬體允許一般不會出現溢位的情況。但鏈式棧相對於順序棧稍顯麻煩,若所需堆疊空間確定,順序棧顯得方便一些。關於鏈式和順序式的選擇視具體情況而定。 1.棧的鏈式儲存結構
《資料結構與演算法分析-c語言描述》第三章表ADT程式碼補全
這本書講的很不錯(英文版)也讓我第一次懷疑了自己是否是中國人,翻譯啊真的有時候看不懂有時還要看英文版的才能看懂這個意思而且書上的程式碼給的不全故放部落格上給自己複習和造福大眾(???) #include<stdio.h> #include<stdlib.h
資料結構棧和佇列(五)棧的順序儲存結構和鏈式儲存結構的實現
一、 實驗目的1. 熟悉棧的特點(先進後出)及棧的抽象類定義;2. 掌握棧的順序儲存結構和鏈式儲存結構的實現;3. 熟悉佇列的特點(先進先出)及佇列的抽象類定義;4. 掌握棧的順序儲存結構和鏈式儲存結構的實現;二、實驗要求1. 複習課本中有關棧和佇列的知識;2. 用C++語言
【資料結構和演算法】8 線性表:線性表的順序儲存結構
線性表的順序儲存結構 線性表有兩種物理儲存結構: 順序儲存結構 和 鏈式儲存結構。 物理上的儲存方式事實上就是在記憶體中找個初始地址,然後通過佔位的形式,把一定的記憶體空間給佔了,然後把相同資料型別的資料元素依次放在這塊空地中。 順序儲存結構:指的是用一段地址連續的儲
《資料結構和演算法》之棧的鏈式儲存結構
在上篇博文中我已經將棧的順序儲存結構簡單地介紹了一下,也舉了一個進位制轉換的例子,供大家學習參考。這裡將繼續進行棧的有關介紹,本篇博文重點對棧的鏈式儲存結構進行分析。 一,棧的鏈式儲存結構 棧的鏈式儲存結構簡稱棧鏈。棧因為只是棧頂來做插入和刪
【資料結構】二叉樹的鏈式儲存結構(通過前序序列和中序序列構造二叉樹)
說明:需要分別輸入要二叉樹的前序序列和中序序列才能構建二叉樹。如果構建失敗,程式會報錯。 比如我們給定一個二叉樹,容易知道 前序序列為:GDAFEMHZ 中序序列為:ADEFGHMZ 程式執行結果: 原始碼 #include<stdio.h> #