在上篇部落格《線性表的概念和順序儲存》中講解了線性表的順序儲存,它的儲存結構實際上是在陣列中儲存,相關操作的實現在部落格中有講解,今天的部落格講講線性表的鏈式儲存
在講解鏈式儲存之前我們先講講順序儲存存在的缺點:
需要提前分配好陣列儲存空間,而且絕大多數情況下這段記憶體空間都不會被充分利用,存在資源浪費
如果在表中進行插入和刪除的操作時需要移動大量的元素
線性表的鏈式儲存結構的特點是用一組任意的儲存單元儲存線性表的資料元素,這組儲存單元可以是連續的,也可以是不連續的,所以這些資料元素可以存在記憶體未被占用的任意位置
物理結構如下圖所示:
資料域,儲存直接後繼位置的部分稱為指標域,資料域和指標域組成結點
n個結點組成乙個鍊錶,也就是線性表(a1, a2,…,an),每個結點中只有乙個指標域,所以這樣的鍊錶又稱為單鏈表
這裡要講解兩個概念:
頭指標:頭指標是指向鍊錶的第乙個結點的指標,如果有頭結點,則頭指標指向頭結點
頭結點,如果沒有頭結點,則頭指標直接指向線性表中的第乙個元素,那麼插入或刪除第1個結點和其他位置的結點就要分情況討論,新增頭結點可以將這兩種情況進行統一
在知道了鍊錶中每個結點的組成後,我們就可以使用c語言的結構體來定義每個節點
# define maxsize 20 //給線性表的儲存空間的初始分配量
# define ok 1
# define error 0
# define true 1
# define false 0
typedef int elemtype; //elemtype就是線性表中的元素的型別,這裡設定為int
typedef int status;
typedef struct node
node;
typedef struct node *linklist;
在建立完結點後,我們通過**來對鍊錶中的元素進行讀取,讀取步驟為:
定義乙個指標p
指向鍊錶中的第乙個結點,定義乙個整型變數j=1
來進行計數
遍歷鍊錶,讓p
不斷指向下一結點,j
累計+1
當j>=查詢元素序號i
或者p
為null時跳出迴圈
跳出後進行判斷,如果p
為空則查詢的結點不存在,
否則查詢位置的節點存在,列印該結點的資料域
**實現:
status getelem(linklist l, int i, elemtype *e)
while(j < i && p)
if(!p || j > i)
*e = p->data;
return ok
}
其實我是將鍊錶中的某個元素讀取出來後賦給指標變數e
指向的型別為elemtype
的變數,這樣不會存在區域性變數的問題,當然也可以通過設定返回值來將查詢到的結點的位址返回給主函式,可以根據實際需要靈活處理
單鏈表的插入操作:
如果我們要將乙個資料插入到鍊錶中的第4個位置,那麼我們就需要遍歷鍊錶來找到3個位置,將這個位置的元素的後繼修改為要插入的新元素,將新元素的後繼修改為之前的第4個位置的元素,這樣就將元素順利插入
步驟為:
宣告指標p
指向煉表頭結點,定義乙個整型變數j=1
,進行計數
遍歷鍊錶,讓p
不斷指向下一結點,j
累計+1
當j>=查詢元素序號i
或者p
為空時跳出迴圈
跳出後進行判斷,如果p
為空則插入位置的前驅節點不存在,插入失敗
否則插入位置的前驅節點存在,修改該結點的後繼
修改插入結點的後繼,至此鍊錶插操作完成
**實現為:
status listinsert(linklist l, int i, elemtype e)
if(!p || j > i)
s = (linklist) malloc(sizeof(node));
s->data = e;
s->next = p->next;
p->next = s;
return ok;
}
在寫上面這段**時有幾點注意事項:
在查詢**中有句
if(i < 0)
後來發現這段**沒必要存在,因為如果i<0
則不會進入while
迴圈,所以j>i
,在後續if
中符合j>i
條件,直接return error;
,所以上面這句**可以直接去掉
s
要在s = (linklist) malloc(sizeof(node));
就初始化好
在插入**中p
就是頭結點所在的位址,j=1
,後面p
向後移動,j
自增1,所以跳出迴圈後p
的指向就是插入位置的前驅結點;而在查詢**中p
就是頭結點後面的結點也就是鍊錶中的第1個結點的位址,j=1
,後面p
向後移動,j
自增1,所以跳出迴圈後p
的指向就是我們要查詢的結點,直接列印結點的資料域即可
可以使用二級指標將鍊錶傳入函式,進行操作
單鏈表刪除操作:
status listdelete(linklist l, int i, elemtype *e)
if(!p || j > i)
q = p->next;
p->next = q->next;
*e = q->next;
free(q);
return ok;
}
和在單鏈表中插入元素類似,在插入元素前我們需要查詢插入位置的前驅節點;而在刪除時我們需要檢視刪除的位置的元素是否存在,與此同時跳出while迴圈後的p要指向刪除位置的前驅節點,所以我們將迴圈的條件設定為j < i && p->next
我們將刪除的元素的前驅節點的後繼結點修改為刪除的元素的後繼結點,之後將刪除位置的那個結點的空間釋放,刪除完成
在講解了上面的基本操作後,我們來講解一下鍊錶的整表建立:
我們建立乙個鍊錶,在鍊錶的每個結點的資料域存放乙個隨機數:
void createlist(linklist *l, int n)
r->next = null;
}
這裡在建立方法中傳入的是二級指標,也就是定義了乙個指標變數l
,這個變數中存放的位址指向的變數型別為linklist
也就是struct node *
,在struct node *
型別的變數中存放的值是個位址值,指向node
結構體,所以*l
中存放的值實際上是煉表頭結點所在的位址,這裡實際上採用的是尾插法,最新的資料永遠在表尾,當然通過修改也可以修改為頭插法
除了單鏈表的整表建立,我們還需要掌握單鏈表的整表刪除:
步驟為:
宣告結點p
和q
將第乙個結點賦值給p
迴圈:1.將下一節點賦值給q
2.釋放p
3.將q
賦給p
**實現
status clearlist(linklist *l)
(*l)->next = null; //將頭結點的指標域賦為null
return ok;
}
為了便於讀者理解我在後續會新增圖示 線性表的鏈式儲存
此方法雖然簡單,但是真寫起來太複雜了。線性表的鏈式儲存 include include struct lnode 線性表的初始化 void init l lnode l 線性表的後插建立 void create l1 lnode l n next null 線性表的後插建立 void create ...
線性表的鏈式儲存
引言 一 單鏈表 相較於順序儲存用連續的儲存單元儲存,單鏈表採用鏈式儲存結構,用一組位址任意的儲存單元儲存資料元素。特點 1 儲存單元可以是不連續的,即邏輯結構與物理結構可以不相同 2 元素用結點儲存,每個結點由元素值和下乙個元素的位址構成 3 單鏈表是由每個結點的指標域按照邏輯次序相互連線而成的。...
線性表的鏈式儲存
include include include typedef int elemtype typedef struct nodenode,nodeptr 鍊錶節點 typedef struct listlist,listptr 鍊錶,頭結點為0位置 listptr initlist 初始化鍊錶 vo...