聊聊 HTTP 常見的請求方式
阿新 • • 發佈:2020-12-23
C語言的連結串列基本操作。
為何需要連結串列?
學習 C 語言的開始,我們最早接觸的資料型別是陣列,它有兩個特點:
- 順序儲存所有的元素,連續佔用記憶體空間;
- 建立陣列時需要事先知道儲存元素的個數,確定陣列的長度;
- 查詢元素複雜度是 O(1)O(1),刪除和插入的複雜度是 O(n)O(n)。
陣列元素非常適合元素個數基本保持不變的場景,特別便於查閱操作,但是如果資料儲存的元素個數經常發生變化,操作起來複雜度就很高,因此急需一種資料結構便於插入和刪除,連結串列就很合適。
概念
一個連結串列的示意圖如下所示
連結串列在記憶體中不是連續的順序儲存的,連結串列包括表頭,表尾和中間結點。
- 中間結點包括資料域和指標域
- 資料域用於儲存當前結點的資料,通常是資料型別
- 指標域指示下一個結點在記憶體中的地址,是一個指標
- 表頭是特殊的中間結點,它的資料域可以包括表的長度或者什麼都沒有,但是指標域必須指向單鏈表的第一個中間結點的位置
- 表尾也是特殊的中間結點,資料域可以儲存資料,因為已經沒有後繼結點,指標域為空
優勢
單鏈表和陣列相比的優勢體現在如下兩個方面:
- 空間儲存更靈活,單鏈表不需要連續佔用記憶體空間,因為每個結點都知道它的後繼結點在記憶體中的位置,所以不需要連續儲存;
- 空間申請更靈活,可以使用動態記憶體分配,在程式執行時候按需申請記憶體大小,避免陣列申請過大造成的浪費。
- 空間釋放和增加更靈活,刪除一個元素僅僅需要將該元素的前置元素指標指向它的後繼結點,釋放該結點即可,增加元素只需要更改指標指向即可,複雜度是 O(1)O(1)
但是,單鏈表的劣勢也很明顯,單鏈表中的元素僅僅知道後繼結點的位置,如果需要查詢某個元素,只能從頭結點開始一個一個地去遍歷,而不像陣列直接給出下標一步就能定位到,因此單鏈表的查詢複雜度是 O(n)O(n)。
程式碼示例
假定單鏈表的資料元素的資料型別是ElemType
,那單鏈表可以定義如下
typedef struct Node {
ElemType data;
struct Node *next;
}Node;
typedef struct Node * LinkList;
特別注意,有個特點,單鏈表有一個指向自身的指標,表示下一個結點next
的位置。單鏈表的常用操作包括:建立,查詢,插入,刪除,清除。
建立列表
建立有兩種方法,頭插法和尾插法,第一種是保持新插入的元素始終在表的第一個元素,第二個保持新插入的元素始終在表的最後一個元素。
/*頭插法建立連結串列*/
void CreateListHead(LinkList *L, int n){
LinkList p;
int i;
srand(time(0));
*L = (LinkList)malloc(sizeof(Node));
(*L)->next = NULL;
for(i = 0; i < n; i++){
p = (LinkList)malloc(sizeof(Node));
p->data = rand() % 100 + 1;
p->next = (*L)->next;
(*L)->next = p;
}
}
尾插法程式碼如下
/*尾插法建立連結串列*/
void CreateListTail(LinkList *L, int n){
LinkList p,r;
int i;
srand(time(0));
*L = (LinkList)malloc(sizeof(Node));
(*L)->next = NULL;
r = *L;
for(i = 0; i < n; i++){
p = (LinkList)malloc(sizeof(Node));
p->data = rand() % 100 + 1;
r->next = p;
r = p;
}
r->next = NULL;
}
查詢元素
工作的基本原理就是工作指標後移,這個技巧也是單鏈表操作的基礎和關鍵。
/* 單鏈表已經存在,用e返回L中的第i個元素的值*/
Status GetElem(LinkList L, int i, ElemType *e)
{
int j = 1;
LinkList p = L->next;
while(p && j < i) //p不為空並且j還沒有和i相等時,繼續往後查詢
{
p = p->next; //一直指向下一個結點,順藤摸瓜
++j;
}
if(!p || j > i)
return ERROR;
*e = p->data;
return OK;
}
插入元素
/* 單鏈表已經存在,在第i個元素位置插入新的元素e,L的表長加1*/
Status ListInsert(LinkList *L, int i, ElemType e)
{
int j = 1;
LinkList p = *L->next, s;
while(p && j < i) //p不為空並且j還沒有和i相等時,繼續往後查詢
{
p = p->next; //一直指向下一個結點,順藤摸瓜
++j;
}
if(!p || j > i)
return ERROR;
s = (LinkList)malloc(sizeof(Node));
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}
刪除元素
/* 單鏈表已經存在,刪除連結串列L的第i個元素,並用e返回其值,表長減1*/
Status ListInsert(LinkList *L, int i, ElemType *e)
{
int j = 1;
LinkList p = *L->next, q;
while(p && j < i) //p不為空並且j還沒有和i相等時,繼續往後查詢
{
p = p->next; //一直指向下一個結點,順藤摸瓜
++j;
}
if(!p || j > i)
return ERROR;
q = p->next;
p->next = q->next;
*e = q->data;
free(q);
return OK;
}
刪除整表
/*順序連結串列L已經存在,將L重置為空表*/
Status ClearList(LinkList *L)
{
LinkList p, q;
p = (*L)->next;
while(p)
{
q = p->next;
free(p);
p = q;
}
(*L)->next = NULL;
return OK;
}