1. 程式人生 > >雙向連結串列(c++實現)

雙向連結串列(c++實現)

  單向連結串列的缺點:逆序訪問單向連結串列中的資料元素,效率低下。
  若從頭節點開始依次訪問單向連結串列的元素,可使用m_current遊標,但是逆序訪問,只能通過下面程式碼實現訪問:

int main(void)
{
    LinkList<int> ll;

    for (int i = 0; i < 6; ++i) //O(n)
    {
        ll.insert(0, i);
    }

    for (int i = ll.length() - 1; i >= 0; --i)  //O(n^2)
    {
        cout << ll.get
(i) << nedl; } return 0; }

  這種方式,遍歷操作的時間複雜度是插入操作的平方,要做程式碼優化,那就和單向連結串列的正序訪問一樣,逆序訪問就需要一個可以逆序移動的迭代器,即構成一個雙向連結串列:在單向連結串列的每一個節點增加一個指標域,用於指向當前節點的前節點的位置。
  在軟體設計上,因為雙向連結串列的節點型別已經和單向連結串列不同,所以雙向連結串列不能繼承自單向連結串列,只可以繼承自抽象類List。(List原型見單向連結串列(c++實現)
這裡寫圖片描述

#ifndef __DUALLINKLIST_H__
#define __DUALLINKLIST_H__
#include "LinkList.h" #include <stdexcept> template<typename T> class DualLinkList : public List<T> { protected: struct Node_t //節點的型別 { T value; Node_t *next; Node_t *pre; }; mutable struct { //頭節點變數,記憶體佈局和Node_t一致 char reserved[sizeof
(T)]; Node_t *next; Node_t *pre; }m_header; int m_length; int m_step; //next()或pre()每次移動的步長 Node_t* m_current; //遊標,方便遍歷 Node_t* _position(int i) const //定位目標位置 { Node_t *ret = reinterpret_cast<Node_t*>(&m_header); for (int p = 0; p < i; ++p) { ret = ret->next; } return ret; } //動態分配節點,定義為虛擬函式,方便派生類繼承自本類時修改分配方式,如靜態分配記憶體實現記憶體池 virtual Node_t* CreateNode() { return new Node_t(); } virtual void DestroyNode(Node_t *p) { delete p; } public: DualLinkList() { m_header.next = NULL; m_header.pre = NULL; m_length = 0; m_step = 1; m_current = NULL; } bool insert(int i, const T& e) { bool ret = ((i >= 0) && (i <= m_length)); if (ret) { Node_t *node = CreateNode(); if (node) { //確定目標地址 Node_t* current = _position(i); Node_t* next = current->next; node->value = e; node->next = next; current->next = node; //插入的位置不是首節點 if (current != reinterpret_cast<Node_t*>(&m_header)) node->pre = current; else node->pre = NULL; //插入的位置不是末節點 if (next != NULL) next->pre = node; ++m_length; } } else { throw(std::out_of_range("LinkList::insert(): i of randge")); } return ret; } bool insert(const T& e) { return insert(m_length, e); } bool remove(int i) { bool ret = ((0 <= i) && (i < m_length)); if (ret) { //確定目標位置 Node_t* current = _position(i); Node_t* toDel = current->next; Node_t* next = toDel->next; //若刪除的位置正是遊標m_current所指向,需要將m_current移動到有效位置 if (m_current == toDel) { m_current = next; } current->next = next; if (next != NULL) //刪除的不是末節點 { next->pre = toDel->pre; } --m_length; DestroyNode(toDel); } return ret; } bool set(int i, const T& e) { bool ret = ((0 <= i) && (i < m_length)); if (ret) { _position(i)->next->value = e; } return ret; } bool get(int i, T& e) const { bool ret = ((0 <= i) && (i < m_length)); if (ret) { e = _position(i)->next->value; } return ret; } //這是LinkList<T>沒有的函式,定義為虛擬函式方便派生類基層發生多型 virtual T get(int i) const { T ret; if (i, ret) return ret; else throw(std::out_of_range("LinkList::insert(): i of randge")); } int find(const T& e) const { int ret = -1; int i = 0; Node_t* node = m_header.next; while (node) { if (node->value == e) { ret = i; break; } else { node = node->next; ++i; } } return ret; } int length() const { return m_length; } void clear() { while (m_length > 0) remove(0); } virtual bool move(int i, int step = 1) //設定遊標的位置 { bool ret = ((0 <= i) && (i < m_length) && (step > 0)); if (ret) { m_current = _position(i)->next; m_step = step; } return ret; } virtual bool end() { return m_current == NULL; } virtual T current() { if (!end()) { return m_current->value; } else { throw(std::runtime_error("DualLinkList is NULL...")); } } //移動遊標至下一個步長的位置 virtual bool next() { int i = 0; while ((i < m_step) && !end()) { m_current = m_current->next; ++i; } return (i == m_step); } //移動遊標至上一個步長的位置 virtual bool pre() { int i = 0; while ((i < m_step) && !end()) { m_current = m_current->pre; ++i; } return (i == m_step); } ~DualLinkList() { clear(); } }; #endif /* __DUALLINKLIST_H__ */ //main.cpp int main(void) { DualLinkList<int> dl; //插入資料 for (int i = 0; i < 6; ++i) { dl.insert(0, i + 2); dl.insert(0, 16); } //順序列印資料 for (dl.move(0); !dl.end(); dl.next()) { cout << dl.current() << " "; } cout << endl; //刪除目標資料 dl.move(dl.length() - 1); while (!dl.end()) { if (dl.current() == 16) { dl.remove(dl.find(dl.current())); } else dl.pre(); } //順序列印資料 for (dl.move(0); !dl.end(); dl.next()) { cout << dl.current() << " "; } cout << endl; //逆序列印資料 for (dl.move(dl.length() - 1); !dl.end(); dl.pre()) { cout << dl.current() << " "; } cout << endl; getchar(); return 0; }

  編譯執行:
這裡寫圖片描述