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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

LinkList

發(fā)布時間:2025/4/5 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 LinkList 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

    • 1 線性表的鏈?zhǔn)酱鎯Y(jié)構(gòu)
      • 1.1 鏈?zhǔn)酱鎯Φ亩x
      • 1.2 鏈?zhǔn)酱鎯壿嫿Y(jié)構(gòu)
      • 1.3 專業(yè)術(shù)語的統(tǒng)一
      • 1.4 鏈表中的基本概念
      • 1.5 單鏈表中的結(jié)點定義
      • 1.6 單鏈表的內(nèi)部結(jié)構(gòu)
      • 1.7 在目標(biāo)位置處插入數(shù)據(jù)元素
      • 1.8 在目標(biāo)位置處刪除數(shù)據(jù)元素
    • 2 LinkList設(shè)計要點
    • 3 繼承關(guān)系圖和接口實現(xiàn)
    • 4 代碼實現(xiàn)
    • 5 頭結(jié)點存在的隱患
    • 6 順序表和單鏈表分析
      • 6.1 時間復(fù)雜度對比分析
      • 6.2 效率深度分析
    • 7 單鏈表的遍歷與優(yōu)化
      • 7.1 當(dāng)前遍歷的問題
      • 7.2 優(yōu)化的思路
      • 7.3 代碼實現(xiàn)

1 線性表的鏈?zhǔn)酱鎯Y(jié)構(gòu)

順序存儲結(jié)構(gòu)的最大問題就是插入和刪除時需要移動大量的元素,這個時候就需要鏈?zhǔn)酱鎯Y(jié)構(gòu)的線性表出場了。

1.1 鏈?zhǔn)酱鎯Φ亩x

為了表示每個數(shù)據(jù)元素與其直接后繼元素之間的邏輯關(guān)系,數(shù)據(jù)元素除了存儲本身的信息外,還需要存儲直接后繼的信息。

1.2 鏈?zhǔn)酱鎯壿嫿Y(jié)構(gòu)

基于鏈?zhǔn)酱鎯Y(jié)構(gòu)的線性表中,每個結(jié)點都包含數(shù)據(jù)域和指針域。

  • 數(shù)據(jù)域:存儲數(shù)據(jù)元素本身。
  • 指針域:存儲相鄰結(jié)點的地址。

1.3 專業(yè)術(shù)語的統(tǒng)一

順序表

  • 基于順序存儲結(jié)構(gòu)的線性表

鏈表

  • 基于鏈?zhǔn)酱鎯Y(jié)構(gòu)的線性表
    • 單鏈表:每個結(jié)點只包含直接后繼的地址信息
    • 循環(huán)鏈表:單鏈表中的最后一個結(jié)點的直接后繼為第一個結(jié)點
    • 雙向鏈表:單鏈表中的結(jié)點包含直接前驅(qū)和直接后繼的地址信息

1.4 鏈表中的基本概念

頭結(jié)點: 鏈表中的輔助結(jié)點,包含指向第一個數(shù)據(jù)元素的指針。
數(shù)據(jù)結(jié)點: 鏈表中代表數(shù)據(jù)元素的結(jié)點,表現(xiàn)形式為:(數(shù)據(jù)元素,地址)。
尾結(jié)點: 鏈表中的最后一個數(shù)據(jù)結(jié)點,包含的地址信息為空。

1.5 單鏈表中的結(jié)點定義

1.6 單鏈表的內(nèi)部結(jié)構(gòu)

頭結(jié)點在單鏈表中的意義: 輔助數(shù)據(jù)元素的定位,方便插入和刪除操作;因此,頭結(jié)點不存儲實際的數(shù)據(jù)元素。

1.7 在目標(biāo)位置處插入數(shù)據(jù)元素

  • 從頭結(jié)點開始,通過current指針定位到目標(biāo)位置。
  • 從堆空間申請新的Node結(jié)點。
  • 執(zhí)行操作:
    node->value = e;
    node->next = current->next;
    current->next = node;
  • 1.8 在目標(biāo)位置處刪除數(shù)據(jù)元素

  • 從頭結(jié)點開始,通過current指針定位到目標(biāo)位置。
  • 使用toDel指針指向需要刪除的結(jié)點。
  • 執(zhí)行操作:
    toDel = current->next;
    current->next = toDel->next;
    delete toDel;
  • 注意: 插入和刪除操作一定要保證鏈表的完整性。


    2 LinkList設(shè)計要點

    • 類模板,通過頭結(jié)點訪問后繼結(jié)點。
    • 定義內(nèi)部結(jié)點類型Node,用于描述數(shù)據(jù)域和指針域。
    • 實現(xiàn)線性表的關(guān)鍵操作(增,刪,查,等)。

    3 繼承關(guān)系圖和接口實現(xiàn)

    繼承關(guān)系圖

    接口實現(xiàn)


    4 代碼實現(xiàn)

    LinkList.h

    #ifndef LINKLIST_H #define LINKLIST_H#include "List.h" #include "Exception.h"namespace LemonLib { template < typename T > class LinkList : public List<T> { protected:struct Node : public Object{T value;Node* next;};mutable Node m_header;int m_length;Node* position(int index) const{Node* ret = &m_header;for (int i=0; i<index; i++){ret = ret->next;}return ret;} public:LinkList(){m_header.next = NULL;m_length = 0;}bool insert(int index, const T& e){bool ret = (0 <= index) && (index <= m_length);if (ret){Node* node = new Node();if (node != NULL){Node* current = position(index);node->value = e;node->next = current->next;current->next = node;m_length++;}else{THROW_EXCEPTION(NoEnoughMemoryException, "No enough memory to inset element ...");}}return ret;}bool insert(const T& e)// 插入到尾部{return insert(m_length, e);}bool remove(int index){bool ret = (0 <= index) && (index < m_length);if (ret){Node* current = position(index);Node* toDel = current->next;current->next = toDel->next;m_length--;delete toDel;}return ret;}bool set(int index, const T& e){bool ret = (0 <= index) && (index < m_length);if (ret){position(index)->next->value = e;}return ret;}bool get(int index, T& e) const{bool ret = (0 <= index) && (index < m_length);if (ret){e = position(index)->next->value;}return ret;}T get(int index) const{T ret;if (get(index, ret)){return ret;}else{THROW_EXCEPTION(InvalidParameterException, "Invalid parameter in get element...");}}int find(const T& e) const{int ret = -1;int index = 0;Node* current = m_header.next;while (current){if (current->value == e){ret = index;break;}else{index++;current = current->next;}}return ret;}int length() const{return m_length;}void clear(){while (m_header.next){Node* toDel = m_header.next;m_header.next = toDel->next;m_length--;delete toDel;}}~LinkList(){clear();} }; }#endif // LINKLIST_H

    main.cpp

    #include <iostream> #include "Object.h" #include "Exception.h" #include "List.h" #include "Seqlist.h" #include "Staticlist.h" #include "Dynamiclist.h" #include "Staticarray.h" #include "DynamicArray.h" #include "Linklist.h"using namespace std; using namespace LemonLib;int main() {LinkList<int> list;for (int i=0; i<5; i++){list.insert(i);}for (int i=0; i<list.length(); i++){int val = 0;if (list.get(i, val)){cout << val << endl;}}cout << endl;list.remove(1);for (int i=0; i<list.length(); i++){cout << list.get(i) << endl;}cout << endl;return 0; }

    5 頭結(jié)點存在的隱患

    如果寫出如上的代碼將會直接拋出異常,這顯然是不合適的,因為這個時候僅僅創(chuàng)建了單鏈表類而沒有創(chuàng)建有問題的Test類,因此我們需要對頭結(jié)點進行優(yōu)化。

    優(yōu)化后的代碼如下:
    LinkList.h

    template < typename T > class LinkList : public List<T> { protected:struct Node : public Object{T value;Node* next;};/* 注意:當(dāng)前結(jié)構(gòu)體必須繼承自O(shè)bject類,否則會導(dǎo)致和Node的內(nèi)存布局不同,直接拋出異常 */mutable struct : public Object{char reserved[sizeof(T)];Node* next;}m_header;int m_length;Node* position(int index) const{Node* ret = reinterpret_cast<Node*>(&m_header);for (int i=0; i<index; i++){ret = ret->next;}return ret;} public:// 和之前代碼完全相同 };

    6 順序表和單鏈表分析

    6.1 時間復(fù)雜度對比分析

    順序表的整體時間復(fù)雜度比單鏈表要低,那么單鏈表還有使用價值嗎?

    6.2 效率深度分析

    注意:在實際工程開發(fā)中,時間復(fù)雜度只是效率的一個參考指標(biāo)。

    對比點順序表單鏈表
    內(nèi)置基礎(chǔ)類型順序表和鏈表效率不相上下順序表和鏈表效率不相上下
    自定義類型順序表在效率上低于單鏈表順序表在效率上低于單鏈表
    插入和刪除涉及大量數(shù)據(jù)對象的復(fù)制操作只涉及指針操作,效率與數(shù)據(jù)對象無關(guān)
    數(shù)據(jù)訪問隨機訪問,可直接定位數(shù)據(jù)對象順序訪問,必須從頭訪問數(shù)據(jù),無法直接定位

    工程開發(fā)中的實際選擇:

    • 順序表:數(shù)據(jù)元素類型相對簡單,不涉及深拷貝;數(shù)據(jù)元素相對穩(wěn)定,訪問操作遠(yuǎn)多于插入和刪除操作。
    • 單鏈表:數(shù)據(jù)元素類型相對復(fù)雜,復(fù)制操作相對耗時;數(shù)據(jù)元素不穩(wěn)定,需要經(jīng)常插入和刪除,訪問操作較少。

    7 單鏈表的遍歷與優(yōu)化

    7.1 當(dāng)前遍歷的問題

    問題: 如何遍歷單鏈表中每一個元素?

    對于當(dāng)前實現(xiàn)的鏈表來說,可以用如下的方式進行遍歷:

    可以看到通過上面的遍歷方法我們不能以線性的時間復(fù)雜度完成單鏈表的遍歷,目前遍歷鏈表的時間復(fù)雜度是O(n2),這顯然是有改進的空間的。我們需要為單鏈表提供新的方法,以便實現(xiàn)在線性的時間內(nèi)完成遍歷。

    7.2 優(yōu)化的思路

    設(shè)計思路

    • 在單鏈表內(nèi)部定義一個游標(biāo)(Node* m_current)
    • 遍歷開始前將游標(biāo)指向位置為0的數(shù)據(jù)元素
    • 獲取游標(biāo)指向的數(shù)據(jù)元素
    • 通過結(jié)點中的next指針移動游標(biāo)

    我們可以提供如下的一組函數(shù),從而以線性的時間復(fù)雜度遍歷鏈表。

    遍歷函數(shù)原型設(shè)計

    注意:遍歷相關(guān)的成員函數(shù)是相互依賴、相互配合的關(guān)系,一定要按照規(guī)則使用,否則將有可能出現(xiàn)錯誤!

    7.3 代碼實現(xiàn)

    為了實現(xiàn)單鏈表類的擴展,我們對當(dāng)前單鏈表內(nèi)部再進行一次封裝。

    為了提高擴展性,我們對結(jié)點的申請和釋放進行封裝,這樣我們就可以自己指定結(jié)點分配的空間。這里我們?yōu)殪o態(tài)單鏈表(StaticLinkList)的實現(xiàn)做準(zhǔn)備,StaticLinkList與LinkList的不同僅在于鏈表結(jié)點在內(nèi)存分配上的不同。因此,只需要將僅有不同封裝在父類和子類的虛函數(shù)中。

    LinkList.h

    #ifndef LINKLIST_H #define LINKLIST_H#include "List.h" #include "Exception.h"namespace LemonLib { template < typename T > class LinkList : public List<T> { protected:struct Node : public Object{T value;Node* next;};/* 注意:當(dāng)前結(jié)構(gòu)體必須繼承自O(shè)bject類,否則會導(dǎo)致和Node的內(nèi)存布局不同,直接拋出異常 */mutable struct : public Object{char reserved[sizeof(T)];Node* next;}m_header;int m_length;Node* m_current;int m_step;Node* position(int index) const{Node* ret = reinterpret_cast<Node*>(&m_header);for (int i=0; i<index; i++){ret = ret->next;}return ret;}virtual Node* create(){return new Node();}virtual void destroy(Node* pn){delete pn;} public:LinkList(){m_header.next = NULL;m_length = 0;m_current = NULL;m_step = 0;}bool insert(int index, const T& e){bool ret = (0 <= index) && (index <= m_length);if (ret){Node* node = create();if (node != NULL){Node* current = position(index);node->value = e;node->next = current->next;current->next = node;m_length++;}else{THROW_EXCEPTION(NoEnoughMemoryException, "No enough memory to inset element ...");}}return ret;}bool insert(const T& e)// 插入到尾部{return insert(m_length, e);}bool remove(int index){bool ret = (0 <= index) && (index < m_length);if (ret){Node* current = position(index);Node* toDel = current->next;current->next = toDel->next;if (m_current == toDel){m_current = toDel->next;}m_length--;destroy(toDel);}return ret;}bool set(int index, const T& e){bool ret = (0 <= index) && (index < m_length);if (ret){position(index)->next->value = e;}return ret;}bool get(int index, T& e) const{bool ret = (0 <= index) && (index < m_length);if (ret){e = position(index)->next->value;}return ret;}virtual T get(int index) const{T ret;if (get(index, ret)){return ret;}else{THROW_EXCEPTION(InvalidParameterException, "Invalid parameter in get element...");}}int find(const T& e) const{int ret = -1;int index = 0;Node* current = m_header.next;while (current){if (current->value == e){ret = index;break;}else{index++;current = current->next;}}return ret;}virtual bool move(int index, int step = 1){bool ret = ((0 <= index) && (index < m_length) && (step > 0));if (ret){m_current = position(index)->next;m_step = step;}return ret;}virtual bool end(){return (m_current == NULL);}virtual T current(){if (!end()){return m_current->value;}else{THROW_EXCEPTION(InvalidOperationException, "Invalid operation to get current value ...");}}virtual bool next(){int i = 0;while ((i < m_step) && (!end())){m_current = m_current->next;i++;}return (i == m_step);}int length() const{return m_length;}void clear(){while (m_header.next){Node* toDel = m_header.next;m_header.next = toDel->next;m_length--;destroy(toDel);}}~LinkList(){clear();} }; }#endif // LINKLIST_H

    main.cpp

    #include <iostream> #include "Object.h" #include "Exception.h" #include "List.h" #include "Seqlist.h" #include "Staticlist.h" #include "Dynamiclist.h" #include "Staticarray.h" #include "DynamicArray.h" #include "Linklist.h"using namespace std; using namespace LemonLib;int main() {LinkList<int> list;for (int i=0; i<5; i++){list.insert(0, i);}for (list.move(0); !list.end(); list.next()){cout << list.current() << endl;}return 0; }

    總結(jié)

    以上是生活随笔為你收集整理的LinkList的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。