標籤:資料結構 入門 快速 演算法 筆記
小豬的資料結構學習筆記(三)
線性表之單鏈表
本章引言:
上一節中我們見識了第一個資料結構——線性表中的順序表;
當你把操作的代碼自己寫幾遍就會有點感覺了,如果現在讓你寫順序表的
插入演算法,你能夠想出大概的代碼嗎?如果可以,那麼你就可以進入新的章節了;
否則,還是回頭看看吧!在本節,我們將迎來線性表的鏈式表示——單鏈表
單鏈表和順序表有什麼優勢和劣勢呢?單鏈表的頭插法和尾插法有什麼不同呢?
請大家跟隨筆者的腳步來解析線性表中的單鏈表把!
本節學習路線圖
路線圖解析:
①先要理解順序表和單鏈表各自的優點和缺點,考試常考的地方!
②熟記單鏈表的形式,理解頭指標,頭結點,尾結點,資料域與指標域!
③熟記單鏈表的結點結構代碼
④區分頭指標域頭結點!
⑤兩種方式建立單鏈表:頭插法和尾插法
⑥單鏈表的12個基本操作
⑦有趣的演算法::如何獲得單鏈表中位置排中間的結點?
本文:
單鏈表的引入:
單鏈表的結構圖:
單鏈表的結點儲存結構
頭指標與頭結點的比較
頭插法與尾插法建立表的比較:
①頭插法的流程:
ps:最前面的代表頭指標
代碼:
<span style="font-family:Microsoft YaHei;">void HeadCreateList(LinkList &L,int n){LinkList p;int i;//初始化隨機數種子,為頭指標分配記憶體空間,並且指標網域設定為空白srand(time(0));L = (LinkList)malloc(sizeof(LNode));L->next = NULL;//利用迴圈,隨機產生n個數字並且儲存到單鏈表中:for(i = 0;i < n;i++){//產生新的結點p = (LNode)malloc(sizeof(LNode));//產生兩位元隨機數就是100,三位就是1000,以此類推p -> data = rand()%100 + 1;p -> next = L - > next;L -> next = p;}}</span>
代碼步驟解析:
①初始化頭結點,並且讓頭指標指向頭結點,同時讓頭結點的指標網域設定為NULL;
②構造單鏈表的結點,這裡用的是隨機產生,當然你也可以自己scanf!
③構造完後,讓當前結點的指標域指向L指向的下一個結點
④將頭指標指向當前結點
②尾插法的流程
PS:因為每次都是將新節點插入到鏈表尾部,所以需要加入一個指標R用於始終指向標的尾結點,以便
新節點插入到鏈表的尾部
代碼
<span style="font-family:Microsoft YaHei;">void TailCraeteList(LinkList &L,int n){LinkList p,r;int i;srand(time(0));L = (LinkList)malloc(sizeof(LNode));L -> next = NULL;r = L;for(i = 0;i < n;i++){L = (LNode)malloc(sizeof(LNode));p -> data = rand()%100 + 1;//這裡要注意,p是中介結點,當前尾結點的next指向pr -> next = p;//r一直指向尾結點,此時p才是尾結點,所以吧p的值賦值給rr = p;}r -> next = NULL;}</span>
代碼步驟解析:
①構造一個頭結點,讓頭指標指向頭結點,同時把頭指標的值賦值給指標R(R一直指向尾結點)
②隨機產生n個隨機數並且構造結點,同時將r的指標域指向p
③因為現在出現新的尾結點,中介變數p,所以r要再指向p,從而保證p一直指向為節點
④將為節點的指標網域設定為NULL
有趣的演算法:尋找中間結點
如何獲得單鏈表中位置排中間的結點?
分析:
訪問單鏈表中的元素只能從頭到尾一個個地挪動,貌似只有一個方法了,就是在記錄單鏈表的長度了,
然後再除以2得出第幾項為中間結點!假如我們不知道長度呢?有沒有其他方法呢?答案肯定是有得,
這裡提供一個簡單的方法:用兩個不同的指標,按照不同的移動順序,暫且稱為快慢指標,每次迴圈
①快指標每次挪動2個節點:p = p->next->next ②慢指標每次挪動1個節點:q = q -> next;
當快指標到達尾部時,此時慢指標剛好指向中間結點!完成!
代碼:
<span style="font-family:Microsoft YaHei;">Status GetMidLNode(LinkList L,ElemType &e){LinkList p,q;p = q = L;while(p -> next -> next != NULL){if(p -> next -> next != NULL){p = p -> next -> next;q = q -> next;}else {p = p -> next;}}e = q -> data;return OK;}</span>
代碼就不解釋了,慢慢理解下把!
十二個基本操作:
=====================================================================
====分割線,上面的是本節的重點和痛點,基本操作都很簡單,實在有興趣的再往下看吧=====
=====================================================================
①單鏈表的儲存結構:
<span style="font-family:Microsoft YaHei;">typedef struct LNode{ElemType data; //資料域struct Lnode *next; //指標域 }LNode;typedef struct LNode *LinkList;//定義兩個是方便使用 </span>
②構造空表
<span style="font-family:Microsoft YaHei;">void InitList(LinkList &L){L = (LinkList)malloc(sizeof(LNode));if(!L)exit(ERROR);L -> next = NULL;} </span>
③將表置為空白表:
<span style="font-family:Microsoft YaHei;">void ClearList(LinkList &L){LinkList p = L -> next;L -> next = NULL;//接著就是銷毀除頭結點外的結點了 while(p){q = L -> next;//釋放首元節點 free(L);//移動到當前的首元節點 L = q;}}</span>
④判斷表是否為空白表
有兩種情況:
有頭結點: L -> next = NULL,此時表為空白表
無頭結點: L = NULL ,此時L為頭指標
<span style="font-family:Microsoft YaHei;">Status ListEmpty(LinkList L){//只需要判斷頭結點的指標域是否為空白即可if(L -> next)return False;else return TRUE; }</span>
⑤銷毀單鏈表
<span style="font-family:Microsoft YaHei;">void Destory(LinkList &L){LinkList q;while(L){//除了頭結點外的結點都刪除//指向首元節點,不是頭結點 q = L -> next;free(L);//刪除後,重新指向首元 L = q;}}</span>
⑥獲得表的長度:
<span style="font-family:Microsoft YaHei;">int ListLength(LinkList L){int i = 0;LinkList p = L -> next; //指向首元節點,非頭結點while(p){i++;p = p -> next;} return i;} </span>
⑦獲得表中第i個元素的值
<span style="font-family:Microsoft YaHei;">Status GetElem(LinkList L,int i,ElemType &e){int j = 1;//指向首元,然後依次後移//如果到了結尾或者j的值大於i還沒找到說明i不合法 LinkList p = L -> next;while(p&&j < i){j++;p = p->next;}if(!p || j > i)return ERROR;e = p ->data;return OK;}</span>
⑧尋找表中是否有滿足條件的元素
<span style="font-family:Microsoft YaHei;">int LocateElem(LinkList L,ElemType e,Status(*compare)(ElemType,ElemType)){int i = 0;LinkList p = L -> next;while(p){i++;if(compare(p->data,e))return i;p = p -> next;}return 0;} </span>
⑨返回某個結點的直接前驅
<span style="font-family:Microsoft YaHei;">Status BeforeElem(LinkList L,ElemType choose,ElemType &before){LinkList q,p = L -> next;while(p -> next){q = p -> next;if(q ->data == choose){before = p -> data;return OK;}//如果p的後繼不為choose,向後移 }return ERROR;}</span>
⑩返回某個結點的直接後繼
<span style="font-family:Microsoft YaHei;">Status NextElem(LinkList L,ElemType choose,ElemType &behind){LinkList p = L -> next;while(p -> next){if(p ->data == choose){behind = p -> next -> data;return OK;}p = p -> next;}return ERROR;} </span>
?往表中第i個位置插入元素
<span style="font-family:Microsoft YaHei;">Status ListInsert(ListList L,int i,ElemType){int j = 0;LinkList p,q = L;while(q && j < i - 1){j++;q = q -> next;}if(!p && j > i - 1)return ERROR;p = (LinkList)malloc(sizeof(LNode));//要先讓插入的結點的指標域指向插入位置的後繼結點//再讓插入節點的前驅的指標域指向插入結點//!!!順序不能亂哦1 p -> data = e;p -> next = q -> next;q -> next = p;return OK;}</span>
?刪除表中第i個位置的元素
<span style="font-family:Microsoft YaHei;">Status ListDelete(LinkList L,int i,ElemType &e){int j = 0;LinkList p,q = L;while(q -> next && j < i - 1){j++;q = q -> next;}//判斷輸入的位置是否合理if(!q -> next|| j > i - 1)return ERROR;//指向準備刪除的結點 p = q -> next;//刪除結點的前驅的指標域指向刪除結點的後繼 q -> next = p ->next;e = p -> data;//釋放要刪除的結點 free(p);return OK;} </span>
?遍曆單鏈表中的所有元素
<span style="font-family:Microsoft YaHei;">void ListTraverser(LinkList L,void(*visit)(ElemType)){LinkList p = L -> next;while(p){visit(p -> data);p = p -> next;}printf("\n");}</span>
相關代碼下載:
點擊下載