1、線性表的鏈式存儲結構
每個元素多用一個位置來存放指向下一個元素位置的指針,依次類推,可以找到所有的元素。鏈式存儲中,除了要存儲數據本身外,還要存儲它的后繼元素的存儲地址(指針)。
數據域:存儲數據信息的域;
指針域:存儲直接后繼位置的域。
這兩部分信息組成數據元素稱之為存儲映像,節點Node。鏈表中每個結點中只包含一個指針域,為單鏈表。鏈表中的第一個結點的存儲位置叫做頭指針,最后一個結點指針為空。
2、頭節點和頭指針(頭節點不是必須的)
頭指針:
1)頭指針是指鏈表指向第一個結點的指針,若鏈表有頭節點,則是指向頭結點的指針。 2)頭指針具有標識作用,常用頭指針冠以鏈表的名字(指針變量的名字)。 3)無論鏈表是否為空,頭指針均不為空。 4)頭指針是鏈表的必要元素。
頭節點:
1)放在第一個元素的結點之前,其數據域一般無意義(可以用來存放鏈表長度) 2)為了操作的統一方便設立(在第一元素結點前插入和刪除第一結點與其他結點操作統一) 3)頭節點不一定是鏈表的必要元素。
3、帶頭結點的單鏈表實現
C語言中可以用結構指針來描述單鏈表:
typedef int ElemType; typedef struct Node { ElemType data; //數據域 struct Node* Next;//指針域 }Node, *LinkList; //結點由存放數據的數據域和存放后繼結點地址的指針域組成
單鏈表的插入:
//單鏈表的插入 STATUS ListInsert(LinkList* L, int i, ElemType e)//L是指向頭節點的二級指針 { if ((L == NULL) || (i > (*L)->data + 1)) { return 0; } int j=1; LinkList p = *L; while (p && (j < i))//找到要插入的位置 { p = p->Next; j++; } LinkList n = (LinkList)malloc(sizeof(Node)); n->data = e; n->Next = p->Next; p->Next = n; (*L)->data++; //頭節點的數據域,表示當前鏈表的長度 return 1; }
單鏈表的刪除
//單鏈表的刪除 STATUS ListDelete(LinkList* L, int i, ElemType* e) { if (L == NULL || i > (*L)->data + 1) return 0; int j = 1; LinkList p = (*L); while (p&&j<i) { p = p->Next; j++; } LinkList q = p->Next; //要刪除的結點 *e = q->data; p->Next = q->Next; free(q); (*L)->data--; return 1; }
//四個基本操作,初始,清空,判斷是否為空,獲取長度 //初始化帶有頭結點的鏈表 Status InitList(LinkList* L) { *L = (LinkList)malloc(sizeof(Node)); //使頭指針指向頭結點 if (*L == NULL) //內存分配失敗 return ERROR; (*L)->next = NULL; //指針域為空 (*L)->data = 0; //頭結點數據域用來存放鏈表長度 return OK; } //清空鏈表(不會清除頭結點) Status ClearList(LinkList* L) { LinkList q, p; q = (*L)->next; //是q指向第一個結點 while (q) { p = q; q = q->next; free(p); } (*L)->next = NULL; return OK; } //判斷鏈表是否為空 Status ListEmpty(LinkList L) { if (L->next) return FALSE; return TRUE; } //獲取列表長度 int ListLength(LinkList L) { /* int length=0; LinkList q=L; while (q=q->next) length++; return length; */ return L->data; }

#include <stdio.h> #include <stdlib.h> typedef int ElemType; typedef int STATUS; typedef struct Node { ElemType data; //數據域 struct Node* Next;//指針域 }Node, *LinkList; //結點由存放數據的數據域和存放后繼結點地址的指針域組成 //單鏈表的插入 STATUS ListInsert(LinkList* L, int i, ElemType e)//L是指向頭節點的二級指針 { if ((L == NULL) || (i > (*L)->data + 1)) { return 0; } int j=1; LinkList p = *L; while (p && (j < i))//找到要插入的位置 { p = p->Next; j++; } LinkList n = (LinkList)malloc(sizeof(Node)); n->data = e; n->Next = p->Next; p->Next = n; (*L)->data++; //頭節點的數據域,表示當前鏈表的長度 return 1; } //單鏈表的刪除 STATUS ListDelete(LinkList* L, int i, ElemType* e) { if (L == NULL || i > (*L)->data + 1) return 0; int j = 1; LinkList p = (*L); while (p&&j<i) { p = p->Next; j++; } LinkList q = p->Next; //要刪除的結點 *e = q->data; p->Next = q->Next; free(q); (*L)->data--; return 1; } int main() { LinkList L=(LinkList)malloc(sizeof(Node)); L->Next = NULL; L->data = 0; for (int i = 1; i <= 10; i++) { ListInsert(&L, i, i * i); } LinkList p = L->Next; while (p) { printf_s("%d ", p->data); p = p->Next; } printf_s("\n"); int j,e; printf_s("請輸入要刪除第幾個結點\n"); scanf_s("%d",&j); ListDelete(&L, j, &e); printf_s("刪除的結點為:%d\n", e); printf_s("刪除后的鏈表為:\n"); p = L->Next; while (p) { printf_s("%d ", p->data); p = p->Next; } return 1; }
對於插入或刪除數據越頻繁的操作,單鏈表的效率優勢越明顯。
4、創建鏈表
頭插法:從一個空表開始,生成新節點,讀取的數據放到新節點的數據域中,然后將新節點插入到當前鏈表的表頭上。也就是說,把新加的元素放在表頭后的第一個位置。
void CreatListHead(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() % 10 + 1; p->Next = (*L)->Next; (*L)->Next = p;
(*L)->data++; } }
尾插法:把新節點插入鏈表的最后,需要增加一個指向鏈表最后一個尾節點的指針r。
void CreatListEnd(LinkList* L, int n) { LinkList p, r; //創建頭結點 *L = (LinkList)malloc(sizeof(Node)); (*L)->data = 0; (*L)->Next = NULL; p = *L; srand(time(0)); for (int i = 0; i < n; i++) { r = (LinkList)malloc(sizeof(Node)); r->data = rand() % 10 + 1; r->Next = p->Next; p->Next = r; p = r; //p指向最后一個結點 (*L)->data++; } }
5、單鏈表結構與順序存儲結構的優缺點:
1)若線性表需要頻繁查找,很少進行插入和刪除操作,采用順序存儲結構合適。 例如用戶注冊信息,絕大多數都是讀取數據。
2)若需要頻繁的插入和刪除,采用單鏈表合適。
優缺點 | 順序存儲結構 | 單鏈表 |
存儲分配方式: | 一段連續的存儲單元依次存儲線性表的數據元素 | 鏈式存儲,用一組任意的存儲單元存放線性表的元素 |
時間性能: | 查找O(1),插入和刪除O(n) | 查找O(n),插入和刪除O(1) |
空間性能: | 需要預先分配存儲空間,較大浪費,較小溢出 | 利用零碎空間 |