数据结构(二): 链表篇
文章目錄
- C鏈表
- 初識鏈表
- 單鏈表
- 單鏈表實現
- 尾插法
- 循環鏈表
- 尋找鏈表入環點
- 雙向鏈表
- 旋轉鏈表
- STL 中的 List
- 3、List基本函數使用
C鏈表
鏈表在C語言的數據結構中的地位可不低。后面很多的數據結構,特別是樹,都是基于鏈表發展的。
所以學好鏈表,后面的結構才有看的必要。
初識鏈表
鏈表是一種物理存儲單元上非連續、非順序的存儲結構,數據元素的邏輯順序是通過鏈表中的指針鏈接次序實現的。鏈表由一系列結點(鏈表中每一個元素稱為結點)組成,結點可以在運行時動態生成。每個結點包括兩個部分:一個是存儲數據元素的數據域,另一個是存儲下一個結點地址的指針域。 相比于線性表順序結構,操作復雜。由于不必須按順序存儲,鏈表在插入的時候可以達到O(1)的復雜度,比另一種線性表順序表快得多,但是查找一個節點或者訪問特定編號的節點則需要O(n)的時間,而線性表和順序表相應的時間復雜度分別是O(logn)和O(1)。
但是鏈表失去了數組隨機讀取的優點,同時鏈表由于增加了結點的指針域,空間開銷比較大。
鏈表有很多種不同的類型:單向鏈表,雙向鏈表以及循環鏈表。
單鏈表
單鏈表實現
話不多說啊,這里我只想直接放代碼:
#include<iostream>using namespace std;class ListNode { private:int value; //值域struct ListNode* next; //指針域public:ListNode(int value) {this->value = value;this->next = NULL;}void add_node(ListNode* next_node) {this->next = next_node;}void set_value(int value) {this->value = value;}int get_value() {return this->value;}ListNode* get_next() {return this->next;}//創建結點ListNode* creat(int data ){ ListNode*p=NULL;p = new ListNode();if(NULL == p){cout<<"申請內存失敗"<<endl;exit(-1);}p->value=data;p->next=NULL; //處理干凈身后事return p;}//新增節點 這里默認頭已經有了void add(ListNode* the_head,int data ) {ListNode* pNode = the_head;//后面再接上一個while (pNode->next != NULL){ //遍歷鏈表,找到最后一個節點pNode = pNode->next;}pNode->next = new ListNode(data); }//刪除節點void del(ListNode* the_head, int index){ //刪除第index個節點ListNode* pFree = NULL; //用來刪除if(index < 1){return;}if(1 == index){pFree = the_head;head = the_head->next;free(pFree->pData); //先釋放數據free(pFree); //釋放指針return;}ListNode* pNode = the_head;while (pNode->next != NULL && index>2){pNode = pNode->next;} pFree = pNode->next; //再指向數據域就爆了pNode->next=pNode->next->next; //這里要無縫銜接free(pFree->pData); //先釋放數據free(pFree); //釋放指針}//計算節點數int Count(ListNode* the_head){int count = 0;ListNode*pNode = the_head;while (pNode != NULL){pNode = pNode->next;count++; } return count;}//查找固定節點數據ListNode* find(ListNode* the_head,int index){ListNode* pNode=NULL;pNode=the_head;while(pNode != NULL && index){pNode = pNode->next;index--; }if(NULL == pNode){cout<<"節點不存在"<<endl; //可以寫日志里}return pNode;} }; //判斷鏈表成環 bool is_ring(ListNode* a) {ListNode* fast = a,*slow = a;while (fast->get_next()) {fast = fast->get_next();if (fast->get_next() && slow->get_next() && fast != slow) {fast = fast->get_next();slow = slow->get_next();}if (fast == slow) {return true;}}return false; }//尋找入環點(默認成環) int find_ring(ListNode* a) {ListNode* fast = a->get_next()->get_next(), * slow = a->get_next();while (fast != slow) {fast = fast->get_next()->get_next(); slow = slow->get_next();}ListNode* slow2 = a;while (fast != slow2) {fast = fast->get_next()->get_next();slow2 = slow2->get_next();}return slow2->get_value(); }//原地翻轉鏈表 ListNode* reverse_list(ListNode* a) {ListNode* temp = new ListNode(0);ListNode* h = NULL;while(a->get_next()) {temp = a;a = a->get_next();temp->add_node(h); h = temp;}return temp; }尾插法
若將鏈表的左端固定,鏈表不斷向右延伸,這種建立鏈表的方法稱為尾插法。尾插法建立鏈表時,頭指針固定不動,故必須設立一個搜索指針,向鏈表右邊延伸,則整個算法中應設立三個鏈表指針,即頭指針head、搜索指針p2、申請單元指針pl。尾插法最先得到的是頭結點。
上面那個就是。
循環鏈表
尋找鏈表入環點
這個就比較需要腦子了,前邊那個有手就行的。
我這個人,懶了點,來張現成的圖吧。
看啊,在相遇之前呢,慢指針走的距離很好求的:L1 = D+S1;
快指針走的距離:設它在相遇前繞了n圈(n>1),那么:L2 = D+S1+n(S1+S2);
不過,還有一個等量關系,不要忽略掉,快指針的速度是慢指針的兩倍,所以:L2 = 2L1;
那么就是:n(S1+S2)-S1 = D;
再轉換一下就是:(n-1)(S1+S2)+S2 = D;
那也就是說,在相遇時候,把一個慢指針放在鏈表頭,開始遍歷,把一個慢指針放在相遇點開始轉圈,當它倆相遇的時候,就是入環點了。
其實吧,用腦子想一開始很難想出來,用手想就快多了。
環的大小就不用我多說了吧,相遇之后,定住快指針,慢指針再繞一圈,再相遇的時候就是一圈了。
雙向鏈表
參考單鏈表。
旋轉鏈表
這個也比較有意思啊,題目時這樣的:給定一個當鏈表,讓你順時針/逆時針旋轉N個位置,要求原地旋轉。
我講一下思路吧:
1、將鏈表自成環。
2、從剛剛的頭往后遍歷N個位置,N為要旋轉的數。
3、環斷開。
解決。
秀吧,我就是覺得解法好玩,就收藏了。
STL 中的 List
每一個自己寫過鏈表的人都知道,鏈表的節點和鏈表本身是分開設計的。
那我們來看一下List的節點設計:
顯而易見,這是一個通用雙向鏈表的節點(如果對通用鏈表不了解,建議一定要自己動手設計一個)。
3、List基本函數使用
其實增刪還是推薦使用迭代器來,因為直接對數據進行操作會存在一定的危險。
在本文第三部分詳細講述了List迭代器操作增刪。
除了這個函數:test.clear();這個函數安全得很,反正都清理掉了。
要遍歷還是首推迭代器。所有和遍歷有關的還是用迭代器。
(1)壓縮list
最后還是要提一下頭文件:
分不清楚就都寫上
總結
以上是生活随笔為你收集整理的数据结构(二): 链表篇的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 超级无敌狂爆龙战士
- 下一篇: 仿雷电——飞机大战类游戏Ⅰ