上一篇咱們總結完了順序表,這一篇咱們要總結的是線性表的鏈表,我想從如下幾點進行總結。node
1,爲何要使用鏈表?
2,鏈表的存儲結構?
3,鏈表的經常使用操做代碼實現?函數
經過上一篇的學習,咱們知道順序表存在一些問題,主要有如下兩個方面。性能
1,順序表的長度是固定的,若是超出分配的長度就會形成溢出,若是存放的數據太少則會形成空間浪費。
2,在插入元素和刪除元素時(尤爲不在尾部時),會移動大量的元素,形成性能和效率低下。學習
基於以上問題,使用鏈表能夠很好地避免順序表中出現的問題。這也是咱們要使用鏈表的緣由。spa
從上圖能夠看出,單鏈表中的每一個結點都包含一個「數據域」和一個「指針域」。「數據域」中包含當前結點的數據,「指針域」包含下一節點的存儲地址,頭指針head是指向開始結點的,結束結點沒有後繼結點,因此結束結點的指針域爲空,即null。3d
鏈表經常使用的操做有:指針
1,插入結點到表頭code
思路:將head頭指針的next指針給新增結點的next,而後將整個新增結點給head頭指針的next。所以時間複雜度爲O(1)。對象
示意圖:blog
思路:插入方法與插入到表頭同樣,只不過多一個步驟就是經過head頭指針循環找到終端結點。所以時間複雜度爲O(n)。
示意圖:
思路:插入方法與插入到表頭同樣,多循環查找當前結點的動做。所以時間複雜度爲O(n)。
示意圖:
思路:同插入結點同樣,時間複雜度爲O(n)。
示意圖:
思路:與插入結點和刪除結點方法相似,時間複雜度爲O(n)。
6,獲取鏈表長度
思路:不像順序表是連續存儲的,獲取表的長度很是容易。在鏈表中,數據不是連續存儲的,所以須要循環遍歷才能求得鏈表的長度,因此時間複雜度爲O(n)。
下面是具體的實現代碼。
C#版:
namespace DS.Model { /// <summary> /// 學生實體 /// </summary> public class Student { public int ID { get; set; } public string Name { get; set; } public int Age { get; set; } } } namespace DS.BLL { /// <summary> /// 封裝鏈表的經常使用操做 /// </summary> public class ChainListBLL { /// <summary> /// 插入結點在表頭 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="head"></param> /// <param name="data"></param> /// <returns></returns> public static Node<T> InsertFirst<T>(Node<T> head, T data) { //建立一個新結點 Node<T> node = new Node<T>(); node.data = data; node.next = head; head = node; return head; } /// <summary> /// 插入結點在表尾 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="head"></param> /// <param name="data"></param> /// <returns></returns> public static Node<T> InsertEnd<T>(Node<T> head, T data) { //建立一個新結點 Node<T> node = new Node<T>(); node.data = data; node.next = null; //空鏈表直接返回新增的結點 if (head == null) { head = node; return head; } GetLastNode(head).next = node; return head; } /// <summary> /// 插入結點(在包含關鍵字key的結點以後插入新的結點) /// </summary> /// <typeparam name="T"></typeparam> /// <param name="head"></param> /// <param name="data"></param> /// <returns></returns> public static Node<T> Insert<T,W>(Node<T> head,string key,Func<T,W> where, T data) where W:IComparable { //檢查鏈表是否爲空 if (head == null) return null; //查找包含關鍵字key的結點 if (where(head.data).CompareTo(key) == 0) { Node<T> node = new Node<T>(); node.data = data; node.next = head.next; //注意這裏順序不要弄反了 head.next = node; } Insert(head.next,key,where,data); //用遞歸繼續查找下一個結點 return head; } /// <summary> /// 刪除結點(刪除包含關鍵字key的結點) /// </summary> /// <typeparam name="T"></typeparam> /// <typeparam name="W"></typeparam> /// <param name="head"></param> /// <param name="key"></param> /// <param name="where"></param> /// <returns></returns> public static Node<T> Delete<T, W>(Node<T> head, string key, Func<T, W> where) where W : IComparable { if (head == null) return null; //若是隻有一個結點 if (where(head.data).CompareTo(key) == 0) { if (head.next != null) head = head.next; //向後移動指針 else return head = null; } else { //判斷此結點是不是要刪除結點的前一結點 while (head.next != null && where(head.next.data).CompareTo(key) == 0) { head.next = head.next.next; } } Delete(head.next,key,where); //使用遞歸繼續查找 return head; } /// <summary> /// 查找結點(查找包含關鍵字key的結點) /// </summary> /// <typeparam name="T"></typeparam> /// <typeparam name="W"></typeparam> /// <param name="head"></param> /// <param name="key"></param> /// <param name="where"></param> /// <returns></returns> public static Node<T> GetNodeByKey<T, W>(Node<T> head, string key, Func<T, W> where) where W : IComparable { if (head == null) return null; if (where(head.data).CompareTo(key) == 0) return head; return GetNodeByKey(head.next,key,where); } /// <summary> /// 獲取鏈表長度 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="head"></param> /// <returns></returns> public static int GetLength<T>(Node<T> head) { int count = 0; while (head != null) { head = head.next; //移動指針 count++; } return count; } private static Node<T> GetLastNode<T>(Node<T> head) { if (head.next == null) return head; //最後結點 return GetLastNode(head.next); //使用遞歸繼續查找 } } /// <summary> /// 封裝鏈表 /// </summary> /// <typeparam name="T"></typeparam> public class Node<T> { public T data; //數據 public Node<T> next; //指針 } } namespace ChainList.CSharp { class Program { static void Main(string[] args) { //實例化一個鏈表對象 Node<Student> node = null; //插入結點在表頭 Console.WriteLine("\n****************插入三條數據在表頭**********************\n"); node = ChainListBLL.InsertFirst(node, new Student { ID = 1, Name = "a", Age = 10 }); node = ChainListBLL.InsertFirst(node, new Student { ID = 2, Name = "b", Age = 11 }); node = ChainListBLL.InsertFirst(node, new Student { ID = 3, Name = "c", Age = 12 }); Display(node); //插入結點在表尾 Console.WriteLine("\n****************插入一條數據在表尾**********************\n"); node = ChainListBLL.InsertEnd(node, new Student { ID = 4, Name = "d", Age = 13 }); Display(node); //插入結點(在包含關鍵字key的結點以前插入新的結點) Console.WriteLine("\n*************插入結點(在包含關鍵字key的結點以後插入新的結點)***********\n"); Console.WriteLine("將ID=5的結點插入到包含'b'關鍵字的結點以後"); node = ChainListBLL.Insert(node, "b", p => p.Name, new Student { ID = 5, Name = "e", Age = 14 }); Display(node); //刪除結點(刪除包含關鍵字key的結點) Console.WriteLine("\n*************刪除結點(刪除包含關鍵字key的結點)***********\n"); Console.WriteLine("刪除Name='c'的結點"); node = ChainListBLL.Delete(node,"c",p=>p.Name); Display(node); //查找結點(查找包含關鍵字key的結點) Console.WriteLine("\n*************查找結點(查找包含關鍵字key的結點)***********\n"); Console.WriteLine("查找Name='d'的結點"); var singNode = ChainListBLL.GetNodeByKey(node, "d", p => p.Name); DisplaySingle(singNode); //獲取鏈表長度 Console.WriteLine("\n*************獲取鏈表長度***********\n"); Console.WriteLine("目前鏈表中有{0}個結點",ChainListBLL.GetLength(node)); Console.ReadKey(); } /// <summary> /// 展現鏈表數據 /// </summary> /// <param name="head"></param> private static void Display(Node<Student> head) { Console.WriteLine("\n****************開始展現鏈表數據**********************\n"); while(head!=null) { Console.WriteLine("ID={0},Name={1},Age={2}",head.data.ID,head.data.Name,head.data.Age); head=head.next; //向後移動指針 } Console.WriteLine("\n****************鏈表數據展現完成**********************\n"); } private static void DisplaySingle(Node<Student> head) { if (head != null) Console.WriteLine("ID={0},Name={1},Age={2}", head.data.ID, head.data.Name, head.data.Age); else Console.WriteLine("未查找到數據!"); } } }
程序運行結果:
C語言版:
#include "stdio.h" #include "string.h" #include "ctype.h" #include "stdlib.h" #include "io.h" #include "math.h" #include "time.h" #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 typedef int Status;/* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */ typedef int ElemType;/* ElemType類型根據實際狀況而定,這裏假設爲int */ typedef struct Node { ElemType data; //數據域 struct Node *next; //指針域 }Node; typedef struct Node *ChainList; /* 定義鏈表 */ Node *p;//定義一個指向結點的指針變量 ChainList head; //定義指向鏈表的頭指針 /*初始化*/ Status Init(ChainList *head) { //建立頭結點,並使頭指針指向頭結點 *head=(ChainList)malloc(sizeof(Node)); //分配存儲地址失敗 if (*head==NULL) return ERROR; //指針域爲空 (*head)->next=NULL; return OK; } /*插入結點(在以head爲頭指針的帶頭結點的鏈表中第i個結點的位置上插入新的結點s)*/ /************************************************************************/ /* 思路:因爲第i個結點的存儲地址是存儲在第i-1個結點的指針域next中,所以,先使 p指向第i-1個結點,而後生成一個數據域值爲e的新結點s,再進行插入操做*/ /************************************************************************/ Status Insert(ChainList head,int i,ElemType e) { int j=0; //聲明一結點p指向鏈表頭結點,s表示要插入的新結點 ChainList p,s; p=head; //使p指向新i-1個結點 while(p!=NULL&&j<i-1) { p=p->next; //移動指針 j++; } //判斷插入位置是否合法 if (p==NULL||j>i-1) return ERROR; s=(ChainList)malloc(sizeof(Node));//建立新結點 s->data=e; s->next=p->next; p->next=s; return OK; } /*刪除結點(在以head爲頭指針帶頭結點的鏈表中刪除第i個結點)*/ /************************************************************************/ /* 思路:因爲第i個結點的存儲地址是存儲在第i-1個結點的指針域next中,所以要先使p 指向第i-1個結點,而後使得p->next指向第i+1個結點,再將第i個結點釋放掉*/ /************************************************************************/ Status Delete(ChainList head,int i) { int j=0; ElemType e; //聲明一結點p指向鏈表頭結點,s表示要插入的新結點 ChainList p,s; p=head; //使p指向第i-1個結點 while(p!=NULL&&j<i-1) { p=p->next;//移動指針 j++; } //刪除位置錯誤 if (p==NULL) return ERROR; else { s=p->next; p->next=s->next; e=s->data; free(s); //free函數釋放資源 return OK; } } /*獲取鏈表的長度*/ /************************************************************************/ /* 思路:遍歷鏈表,用計數器計算循環次數*/ /************************************************************************/ int GetLength(ChainList head) { int i=0; //指向第一個結點 ChainList p=head->next; while(p!=NULL) { p=p->next; i++; } return i; } /*獲取第i個元素的值*/ /************************************************************************/ /* 思路:因爲第i個結點的存儲地址是存儲在第i-1個結點的指針域next中,所以要先使p 指向第i-1個結點,而後取第i-1個結點的值*/ /************************************************************************/ Status GetDataByIndex(ChainList head,int i,ElemType *e) { int j=0; ChainList p,s; //讓p指向第一個結點 p=head->next; while(p!=NULL&&j<i-1) { p=p->next; j++; } if (p==NULL||j>i-1) return ERROR; *e=p->data; return OK; } /*展現數據*/ Status Display(ChainList head) { ChainList p=head->next; printf("\n****開始展現數據****\n"); while(p!=NULL) { printf("%d ",p->data); p=p->next; } printf("\n****展現完畢****\n"); return OK; } void main() { ChainList head; ElemType e; Status i; int j,k; //初始化鏈表 printf("\n*****************初始化************************\n"); i=Init(&head); printf("初始化後,鏈表的長度爲:%d\n",GetLength(head)); //插入結點(數據) printf("\n*****************插入10條數據************************\n"); for (j=0;j<10;j++) { i=Insert(head,1,j); } printf("插入成功"); Display(head); //刪除結點(數據) printf("\n*****************刪除第2條數據************************\n"); i=Delete(head,2); printf("刪除成功"); Display(head); //查找結點 printf("\n*****************獲取第5個結點數據*********************\n"); i=GetDataByIndex(head,5,&e); printf("%d\n",e); //獲取鏈表長度 printf("\n*****************獲取鏈表當前長度**********************\n"); k=GetLength(head); printf("當前長度爲%d\n",k); getchar(); }
程序運行結果: