數據結構:線性表
線性表設計與實現
線性表基本概念
線性表定義
線性表(List)是零個或多個數據元素的集合
線性表中的數據元素之間是有順序的
線性表中的數據元素個數是有限的
線性表中的數據元素的類型必須相同
數學定義
線性表是具有相同類型的 n( ≥ 0)個數據元素的有限序列(a1, a2, …, an)ai是表項,n 是表長度。
性質
a0為線性表的第一個元素,只有一個後繼
an為線性表的最後一個元素,只有一個前驅
除a0和an外的其它元素ai,既有前驅,又有後繼
線性表能夠逐項訪問和順序存取
練習
下面的關系中可以用線性表描述的是
A.班級中同學的友誼關系
B.公司中的上下級關系
C.冬天圖書館排隊占座關系
D.花名冊上名字之間的關系
線性表的操作
創建線性表
銷毀線性表
清空線性表
將元素插入線性表
將元素從線性表中刪除
獲取線性表中某個位置的元素
獲取線性表的長度
線性表在程序中表現為一種特殊的數據類型
線性表的操作在程序中的表現為一組函數
C語言描述=====》線性表的設計與實現 |
#ifndef _WBM_LIST_H_ #define _WBM_LIST_H_
typedef void List; typedef void ListNode;
//創建並且返回一個空的線性表 List* LinkList_Create();
//銷毀一個線性表list void List_Destroy(List* list);
//將一個線性表list中的所有元素清空, 線性表回到創建時的初始狀態 void List_Clear(List* list);
//返回一個線性表list中的所有元素個數 int List_Length(List* list);
//向一個線性表list的pos位置處插入新元素node int List_Insert(List* list, ListNode* node, int pos);
//獲取一個線性表list的pos位置處的元素 ListNode* List_Get(List* list, int pos);
//刪除一個線性表list的pos位置處的元素 返回值為被刪除的元素,NULL表示刪除失敗 ListNode* List_Delete(List* list, int pos);
#endif |
線性表的順序存儲結構
1、基本概念
2、設計與實現
插入元素算法 判斷線性表是否合法 判斷插入位置是否合法 把最後一個元素到插入位置的元素後移一個位置 將新元素插入 線性表長度加1 |
獲取元素操作 判斷線性表是否合法 判斷位置是否合法 直接通過數組下標的方式獲取元素 |
刪除元素算法 判斷線性表是否合法 判斷刪除位置是否合法 將元素取出 將刪除位置後的元素分別向前移動一個位置 線性表長度減1 |
3、優點和缺點
優點:
無需為線性表中的邏輯關系增加額外的空間
可以快速的獲取表中合法位置的元素
缺點:
插入和刪除操作需要移動大量元素
當線性表長度變化較大時難以確定存儲空間的容量
線性表的鏈式存儲
1、基本概念
鏈式存儲定義
為了表示每個數據元素與其直接後繼元素之間的邏輯關系,每個元素除了存儲本身的信息外,還需要存儲指示其直接後繼的信息。
表頭結點
鏈表中的第一個結點,包含指向第一個數據元素的指針以及鏈表自身的一些信息
數據結點
鏈表中代表數據元素的結點,包含指向下一個數據元素的指針和數據元素的信息
尾結點
鏈表中的最後一個數據結點,其下一元素指針為空,表示無後繼。
2、設計與實現
在C語言中可以用結構體來定義鏈表中的指針域 鏈表中的表頭結點也可以用結構體實現 |
帶頭結點、位置從0的單鏈表 返回鏈表中第3個位置處,元素的值 LinkListNode* LinkList_Get(LinkList* list, int pos) { int i = 0; TLinkList *tList = (TLinkList *)list; LinkListNode *current = NULL; LinkListNode *ret = NULL;
if (list==NULL ||pos<0 || pos>=tList->length) { return NULL; } current = (LinkListNode *)tList; for (i=0; i<pos; i++) { current = current->next; } ret = current->next; return ret ; }
返回第三個位置的 移動pos次以後,當前指針指向哪裏? 答案:指向位置2,所以需要返回 ret = current->next;
備註: 循環遍歷時, 遍歷第1次,指向位置0 遍歷第2次,指向位置1 遍歷第3次,指向位置2 遍歷第n次,指向位置n-1; 所以如果想返回位置n的元素的值,需要怎麽做 ret = current->next; 此問題是:指向頭結點的指針移動n次 和 第n個元素之間的關系? |
刪除元素
|
3、優點和缺點
優點:
無需一次性定制鏈表的容量
插入和刪除操作無需移動數據元素
缺點:
數據元素必須保存後繼元素的位置信息
獲取指定數據的元素操作需要順序訪問之前的元素
循環鏈表
1、基本概念
循環鏈表的定義:將單鏈表中最後一個數據元素的next指針指向第一個元素
循環鏈表擁有單鏈表的所有操作 |
創建鏈表 銷毀鏈表 獲取鏈表長度 清空鏈表 獲取第pos個元素操作 插入元素到位置pos 刪除位置pos處的元素 |
新增功能:遊標的定義 |
在循環鏈表中可以定義一個"當前"指針,這個指針通常稱為遊標,可以通過這個遊標來遍歷鏈表中的所有元素。 |
循環鏈表新操作 |
獲取當前遊標指向的數據元素 將遊標重置指向鏈表中的第一個數據元素 將遊標移動指向到鏈表中的下一個數據元素 直接指定刪除鏈表中的某個數據元素 |
CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node); CircleListNode* CircleList_Reset(CircleList* list); CircleListNode* CircleList_Current(CircleList* list); CircleListNode* CircleList_Next(CircleList* list); |
2、設計與實現
插入元素的分析
|
在第一個位置插入 |
刪除節點 |
3、優點和缺點
優點:功能強了。 循環鏈表只是在單鏈表的基礎上做了一個加強 |
循環鏈表可以完全取代單鏈表的使用 循環鏈表的Next和Current操作可以高效的遍歷鏈表中的所有元素 |
缺點: 代碼復雜度提高了 |
約瑟夫問題-循環鏈表典型應用 n 個人圍成一個圓圈,首先第 1 個人從 1 開始一個人一個人順時針報數,報到第 m 個人,令其出列。然後再從下一 個人開始從 1 順時針報數,報到第 m 個人,再令其出列,…,如此下去,求出列順序。 |
雙向鏈表
1、基本概念
單鏈表的結點都只有一個指向下一個結點的指針 單鏈表的數據元素無法直接訪問其前驅元素 逆序訪問單鏈表中的元素是極其耗時的操作! |
len = LinkList_Length(list); for (i=len-1; len>=0; i++) //O(n) { LinkListNode *p = LinkList_Get(list, i); //O(n) //訪問數據元素p中的元素 // } |
雙向鏈表的定義 在單鏈表的結點中增加一個指向其前驅的pre指針 |
雙向鏈表擁有單鏈表的所有操作 創建鏈表 銷毀鏈表 獲取鏈表長度 清空鏈表 獲取第pos個元素操作 插入元素到位置pos 刪除位置pos處的元素 |
2、設計與實現
插入操作 |
刪除操作 |
雙向鏈表的新操作 |
獲取當前遊標指向的數據元素 將遊標重置指向鏈表中的第一個數據元素 將遊標移動指向到鏈表中的下一個數據元素 將遊標移動指向到鏈表中的上一個數據元素 直接指定刪除鏈表中的某個數據元素 |
DLinkListNode* DLinkList_DeleteNode(DLinkList* list, DLinkListNode* node); DLinkListNode* DLinkList_Reset(DLinkList* list); DLinkListNode* DLinkList_Current(DLinkList* list); DLinkListNode* DLinkList_Next(DLinkList* list); DLinkListNode* DLinkList_Pre(DLinkList* list); |
//大家一定要註意:教科書不會告訴你 項目上如何用;哪些點是項目的重點; 做一個企業級的財富庫,完成你人生開發經驗的積累,是我們的學習重點,要註意! |
3、優點和缺點
優點:雙向鏈表在單鏈表的基礎上增加了指向前驅的指針 功能上雙向鏈表可以完全取代單鏈表的使用 循環鏈表的Next,Pre和Current操作可以高效的遍歷鏈表中的所有元素 |
缺點:代碼復雜 |
數據結構:線性表