日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数据结构(二): 链表篇

發布時間:2024/1/8 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构(二): 链表篇 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 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的節點設計:

template <typename T> struct __list_node {typedef void* void_pointer;void_pointer prev;void_pointer neet;T date; }

顯而易見,這是一個通用雙向鏈表的節點(如果對通用鏈表不了解,建議一定要自己動手設計一個)。

3、List基本函數使用

  • #include<list>typedef struct rect {··· }Rect;list<Rect>test; //聲明一個鏈表,用于存放結構體數據//如果想以其他方法初始化list列表,可以移步到下一行那個Vector的介紹
  • Rect a; ··· test.push_back(a); test.push_front(a); //頭尾插入(雙向鏈表)//定點插入 test.insert(test.begin()+10,a); //在第十個節點之前插入a
  • //刪除test頭部的元素 test.erase(test.begin());//刪除test從頭到尾的元素 test.erase(test.begin(), test.end());test.pop_back(); test.pop_front();

    其實增刪還是推薦使用迭代器來,因為直接對數據進行操作會存在一定的危險。
    在本文第三部分詳細講述了List迭代器操作增刪。

    除了這個函數:test.clear();這個函數安全得很,反正都清理掉了。


  • 查、改
  • //迭代器 list<int>::iterator p; for (p = test.begin(); p != test.end(); p++)cout << *p << " ";

    要遍歷還是首推迭代器。所有和遍歷有關的還是用迭代器。


  • #include<algorithm> sort(test.begin(),test.end()); //從頭到尾,默認從小到大排序//一般排序都是要自定義排序的: bool Comp(const int &a,const int &b) {return a>b; } sort(test.begin(),test.end(),Comp); //這樣就降序排序。
  • 大小
  • test.size(); //容器已存入數據量 test.capacity(); //容器還能存多少數據量//其實不用擔心容器不夠大,容量要滿的時候它會自己擴容
  • 其他
    (1)壓縮list
  • //去除重復的元素至只保留一個副本 test.unique(); //已經過大小排序的list才能使用 (2)合并list test.splice(test.end(),test2);//將test2剪切到test后面

    最后還是要提一下頭文件:
    分不清楚就都寫上

    #include<algorithm> #include<list>

    總結

    以上是生活随笔為你收集整理的数据结构(二): 链表篇的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。