爲了表示每一個數據元素ai與其直接後繼數據元素ai+1之間的邏輯關係,對數據元素ai來講,除了存儲其自己的信息以外,還需存儲一個指示其直接後繼的信息。咱們把存儲數據元素信息的域稱爲數據域,把存儲直接後繼位置的域稱爲指針域。這兩部分信息組成數據元素的存儲映像,稱爲結點。
n個結點鏈組成一個鏈表,即爲線性表的鏈式存儲結構,由於此鏈表的每一個結點中只包含一個指針域,因此叫作單鏈表。
對於線性表來講,有頭有尾,鏈表中的第一個結點的存儲位置叫作頭指針,鏈表的存取必須是從頭指針開始進行。
有時候我了增長對鏈表操做的方便性,咱們會在鏈表的第一個結點(首元結點)前增長一個結點,稱爲頭結點。頭結點的數據域能夠不存任何數據,指針域存儲第一個結點的指針。
鏈式存儲結構中咱們默認都帶上頭結點。
圖片示意以下:函數
頭指針和頭結點的區別:
頭指針:性能
頭結點:spa
單鏈表結點中包含了數據域和指針域,以下圖所示:
結點定義以下:3d
#define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 /* ElemType類型根據實際狀況而定,這裏假設爲int */ typedef int ElemType; /* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */ typedef int Status; //定義結點 typedef struct Node{ ElemType data; // 存儲在數據域中的數據 struct Node *next; // 直接後繼結點的地址指針 }Node; typedef struct Node * LinkList;
思路:指針
Status InitList(LinkList *L){ //產生頭結點,並使用L指向此頭結點 *L = (LinkList)malloc(sizeof(Node)); //存儲空間分配失敗 if(*L == NULL) return ERROR; //將頭結點的指針域置空 (*L)->next = NULL; return OK; }
假設要在單鏈表的兩個數據元素A和B之間插⼊一個數據元素e,已知p爲其單鏈表存儲結構中指向結點A指針。以下圖所示
思路:code
代碼實現以下:blog
/* 初始條件:順序線性表L已存在,1≤i≤ListLength(L); 操做結果:在L中第i個位置以前插入新的數據元素e,L的長度加1; */ Status ListInsert(LinkList *L,int i,ElemType e){ int j; LinkList p,s; p = *L; j = 1; //尋找第i-1個結點 while (p && j<i) { p = p->next; ++j; } //第i-1個元素不存在 if(!p || j>i) return ERROR; //生成新結點s s = (LinkList)malloc(sizeof(Node)); //將e賦值給s的數值域 s->data = e; //將p的後繼結點賦值給s的後繼 s->next = p->next; //將s賦值給p的後繼 p->next = s; return OK; }
要刪除單鏈表中指定位置的元素,同插入元素同樣,首先應該找到該位置的前驅結點,以下圖所示在單鏈表中刪除元素B時,應該首先找到其前驅結點A,爲了在單鏈表中實現元素A,B,C之間的邏輯關係的變化,僅需修改結點A中的指針域便可.
思路:圖片
代碼實現以下:內存
/* 初始條件:順序線性表L已存在,1≤i≤ListLength(L) 操做結果:刪除L的第i個數據元素,並用e返回其值,L的長度減1 */ Status ListDelete(LinkList *L,int i,ElemType *e){ int j; LinkList p,q; p = *L; j = 1; //查找第i-1個結點,p指向該結點 while (p->next && j<i) { p = p->next; ++j; } //當i>n 或者 i<1 時,刪除位置不合理 if (!(p->next) || j>i) return ERROR; //q指向要刪除的結點 q = p->next; //將q的後繼賦值給p的後繼 p->next = q->next; //將q結點中的數據給e *e = q->data; //讓系統回收此結點,釋放內存; free(q); return OK; }
在單鏈表中,咱們不能像順序存儲結構那樣直接經過下標直接獲取數據,咱們沒辦法一開始就知道,必須得從頭開始找,進行遍歷。
思路:get
代碼實現以下:
/* 初始條件: 順序線性表L已存在,1≤i≤ListLength(L); 操做結果:用e返回L中第i個數據元素的值 */ Status GetElem(LinkList L,int i,ElemType *e){ int j = 1; //聲明結點p; LinkList p; //將結點p 指向鏈表L的首元結點; p = L->next; //p不爲空,且計算j不等於i,則循環繼續 while (p && j<i) { //p指向下一個結點 p = p->next; ++j; } //若是p爲空或者j>i,則返回error if(!p || j > i) return ERROR; //e = p所指的結點的data *e = p->data; return OK; }
思路:
循環:
代碼實現以下:
/* 隨機產生n個元素值,創建帶表頭結點的單鏈線性表L(前插法)*/ void CreateListHead(LinkList *L, int n){ LinkList p; //創建1個帶頭結點的單鏈表 *L = (LinkList)malloc(sizeof(Node)); (*L)->next = NULL; //循環前插入隨機數據 for(int i = 0; i < n;i++) { //生成新結點 p = (LinkList)malloc(sizeof(Node)); //i賦值給新結點的data p->data = i; p->next = (*L)->next; //將結點P插入到頭結點以後; (*L)->next = p; } }
思路:
循環:
代碼實現以下:
/* 隨機產生n個元素值,創建帶表頭結點的單鏈線性表L(後插法)*/ void CreateListTail(LinkList *L, int n){ LinkList p,r; //創建1個帶頭結點的單鏈表 *L = (LinkList)malloc(sizeof(Node)); //r指向尾部的結點 r = *L; for (int i=0; i<n; i++) { //生成新結點 p = (Node *)malloc(sizeof(Node)); p->data = i; //將表尾終端結點的指針指向新結點 r->next = p; //將當前的新結點定義爲表尾終端結點 r = p; } //將尾指針的next = null r->next = NULL; }
思路:
循環:
代碼實現以下:
/* 初始條件:順序線性表L已存在。操做結果:將L重置爲空表 */ Status ClearList(LinkList *L) { LinkList p,q; p=(*L)->next; /* p指向第一個結點 */ while(p) /* 沒到表尾 */ { q=p->next; free(p); p=q; } (*L)->next=NULL; /* 頭結點指針域爲空 */ return OK; }
存儲分配方式:
時間性能:
查找
插入與刪除
空間性能:
經過對比,咱們可得知:
若線性表須要頻繁查找,不多進行插入和刪除操做時,宜採用順序存儲結構。若須要頻繁插入和刪除時,宜採用單鏈表結構。
當線性表中的元素個數變化較大或者根本不知道有多大的時候,最好採用單鏈表結構,這樣能夠不須要考慮存儲空間大小的問題。而若是事先知道線性表的大體長度,就能夠用這個順序存儲結構,用起來比較高效。
將單鏈表中的終端結點的指針從空指針改成指向頭結點,就使整個單鏈表造成一個環,這種頭尾相接的單鏈表稱爲單循環鏈表,簡稱循環鏈表。
爲了使空鏈表與非空鏈表處理一致,咱們一般設一個頭結點。
循環列表中帶有頭結點的空鏈表以下圖:
對於非空的循環鏈表以下圖:
思路:
代碼實現:
/* 建立一個帶有頭結點的循環鏈表 */ Status CreateList(LinkList *L) { int item; LinkList temp = NULL; LinkList r = NULL; // 尾結點指針 printf("請輸入新的結點, 當輸入0時結束!\n"); while (1) { scanf("%d",&item); if (item == 0) { break; } // 當鏈表爲空時,建立鏈表,帶上頭結點。 if (*L == NULL) { *L = (LinkList)malloc(sizeof(Node)); if (!*L) return ERROR; (*L)->next = *L; // 使其next指針指向本身 r = *L; } temp = (LinkList)malloc(sizeof(Node)); if(!temp) return ERROR; temp->data = item; temp->next = *L; r->next = temp; r = temp; } return OK; }
思路:
代碼實現以下:
/* 循環鏈表插入數據 */ Status ListInsert2(LinkList *L, int place, int num) { if (place < 1) { return ERROR; } LinkList target; LinkList temp; int k; temp = (LinkList)malloc(sizeof(Node)); if (!temp) return ERROR; temp->data = num; // 查找插入位置的前一個結點 for (k = 1, target = *L; k < place && target->next != *L; k++, target = target->next); temp->next = target->next; target->next = temp; return OK; }
思路:
代碼實現以下:
/* 循環鏈表刪除數據,鏈表表L已存在,1≤place≤ListLength(L) */ Status LinkListDelete(LinkList *L,int place) { if (place < 1) { return ERROR; } LinkList temp, target; int k; for (k = 1, target = *L; k < place && target->next != *L; k++, target = target->next); if (place > k) { return ERROR; } temp = target->next; target->next = temp->next; free(temp); return OK; }
思路:
代碼實現以下:
/* 循環鏈表查詢值的位置 */ int findValue(LinkList L, int value) { int i = 1; LinkList p; p = L->next; while (p->data != value && p->next != L) { i++; p = p->next; } // 若是已經遍歷到最後一個元素,且尚未找到,那麼直接返回-1. if (p->next == L && p->data != value) { return -1; } return i; }