鏈表描述

在鏈表描述中,數據對象實例的每個元素都放在單元或節點中進行描述。不過,節點不必是一個數組元素,因此沒有什麼公式可用來定位某個元素。取而代之的是,每個節點中都包含了與該節點相關的其他節點的位置信息。這種關於其他節點的位置信息被稱之爲鏈或指針。

這裏寫圖片描述
圖中的每個鏈表節點都正好有一個鏈接域,所以該圖的鏈表結構被稱之爲單向鏈表(singly linked list)。

第一個節點e1 的指針指向第二個節點e2e2 的指針指向e3 ,…,最後一個節點鏈接域爲NULL(或0 ),這種結構被稱作鏈(chain)。

線性表的鏈表描述不需要指定表的最大長度。

用鏈表實現線性表

這裏寫圖片描述刪除節點

這裏寫圖片描述插入節點

#pragma once
template <class T>
class ChainNode
{
    friend Chain<T>;
private:
    T data;
    ChainNode<T> *link;
};

template<class T>
class Chain
{
public:
    Chain() { first = 0; last = 0}
    ~Chain();
    bool IsEmpty() const { return first == 0; }
    int Length() const;
    bool Find(int k, T& x) const;
    int Search(const T& x) const;
    Chain<T>& Delete(int k, T& x);
    Chain<T>& Insert(int k, const T& x);
    void Output(ostream& out) const;
    void Erase();
    void Zero() { first = 0; }  //類內定義,可能是內聯函數,這種編程風格不好?
    Chain<T>& Append(const T& x)
private:
    ChainNode<T> *first; //指向第一個節點的指針
    ChainNode<T> *last;  //指向最後一個節點的指針
};

template<class T>
Chain<T>::~Chain()
{
    // 鏈表的析構函數,用於刪除鏈表中的所有節點
    last = 0;
    ChainNode<T> *next; // 下一個節點
    while (first) 
    {
        next = first->link;
        delete first;
        first = next;
    }
}

template<class T>
int Chain<T>::Length() const
{
    // 返回鏈表中的元素總數
    ChainNode<T> *current = first;
    int len = 0;
    while (current)
    {
        len++;
        current = current->link;
    }
    return len;
}

template<class T>
bool Chain<T>::Find(int k, T& x) const
{
    //尋找鏈表中的第k個元素,並將其傳送至x
    //如果不存在第k個元素,則返回false,否則返回true
        if (k < 1) return false;
    ChainNode<T> *current = first;
    int index = 1; // current的索引
    while (index < k && current) 
    {
        current = current->link;
        index++;
    }
    if (current) { x = current->data; return true; }
    return false; // 不存在第k個元素
}

template<class T>
int Chain<T>::Search(const T& x) const
{
    //本函數假定對於類型T定義了 != 操作
    //尋找x,如果發現x,則返回x的地址
    //如果x不在鏈表中,則返回0
    ChainNode<T> *current = first;
    int index = 1; // current的索引
    while (current && current->data != x)
    {
        current = current->link;
        index++;
    }
    if (current)
        return index;
    return 0;
}

template<class T>
void Chain<T>::Output(ostream& out) const
{
    //本函數要求對於類型T必須定義<<操作
    //將鏈表元素送至輸出流
    ChainNode<T> *current;
    for (current = first; current; current = current->link)
        out << current->data << " ";
}
// 重載<<
template <class T>
ostream& operator<<(ostream& out, const Chain<T>& x)
{
    //用法就是,cout << L (L是一個Chain鏈表),
    //從這裏觸發對Output()的調用,所以Output()是一個實用函數。
    x.Output(out); 
    return out;
}

template<class T>
Chain<T>& Chain<T>::Delete(int k, T& x)
{
    //把第k個元素取至x,然後從鏈表中刪除第k個元素
    //如果不存在第k個元素,則引發異常OutOfBounds
        if (k < 1 || !first)
            throw OutOfBounds(); // 不存在第k個元素
    // p最終將指向第k個節點
    ChainNode<T> *p = first;
    // 將p移動至第k個元素,並從鏈表中刪除該元素
    if (k == 1) // p已經指向第k個元素
        first = first->link; // 刪除之
    else
    {
        //用q指向第k - 1個元素
        ChainNode<T> *q = first;
        for (int index = 1; index < k - 1 && q; index++)
            q = q->link;
        if (!q || !q->link)
            throw OutOfBounds(); //不存在第k個元素
        p = q->link; //存在第k個元素
        if (p == last) last = q; //對last指針的一種可能處理,
                                 //如果剛好刪除最後一個元素
        q->link = p->link;  //從鏈表中刪除該元素,如果p指向最後一個節點,
                            //則此處保證q->link=NULL
    }
    //保存第k個元素並釋放節點p
    x = p->data;
    delete p;
    return *this;
}
template<class T>
Chain<T>& Chain<T>::Insert(int k, const T& x)
{
    //在第k個元素之後插入x
    //如果不存在第k個元素,則引發異常OutOfBounds
    //如果沒有足夠的空間,則傳遞NoMem異常
    if (k < 0) 
        throw OutOfBounds();
    //p最終將指向第k個節點
    ChainNode<T> *p = first;
    //將p移動至第k個元素
    for (int index = 1; index < k && p; index++)
        p = p->link;
    if (k > 0 && !p) 
        throw OutOfBounds(); //不存在第k個元素
    //插入
    ChainNode<T> *y = new ChainNode<T>;
    y->data = x;
    if (k) 
    {
        //在p之後插入
        y->link = p->link; //如果實在最後插入元素,
                           //那麼此處可以保證y->link=NULL
        p->link = y;
    }
    else 
    {
        //作爲第一個元素插入
        y->link = first;
        first = y;
    }
    if (!y->link) last = y; //對last指針的一種可能處理,
                            //如果剛好在最後的位置插入元素
    return *this;
}

template<class T>
void Chain<T>::Erase()
{
    //刪除鏈表中的所有節點
    last = 0;
    ChainNode<T> *next;
    while (first)
    {
        next = first->link;
        delete first;
        first = next;
    }
}

template<class T>
Chain<T>& Chain<T>::Append(const T& x)
{
    // 在鏈表尾部添加x
    ChainNode<T> *y;
    y = new ChainNode<T>;
    y->data = x; y->link = 0;
    if (first)
    {
        //鏈表非空
        last->link = y;
        last = y;
    }
    else //鏈表爲空
        first = last = y;
    return *this;
}

//假定Output()不是Chain類的成員函數,並且在該類中沒有重載操作符<<。
//鏈表遍歷器類
template<class T>
class ChainIterator 
{
public:
    T* Initialize(const chain<T>& c)
    {
        location = c.first;
        if (location) return &location->data;
        return 0;
    }
    T* Next()
    {
        if(!location) return 0;
        location = location->link;
        if (location) return &location->data;
        return 0;
    }
private:
    ChainNode<T> *location;
};

//採用以上鍊表遍歷器輸出鏈表
//int *x;
//ChainIterator<int> c;
//x = c.Initialize(X);
//while (x) {
//  cout << *x << ' ';
//  x = c.Next();
//}
//cout << endl;

這裏寫圖片描述
循環鏈表

在帶有頭節點的循環鏈表中進行查找

template<class T>
int CircularList<T>::Search(const T& x) const
{
    //在帶有頭節點的循環鏈表中尋找x
    ChainNode<T> *current = first->link;
    int index = 1; // current的索引
    first->data = x; // 把x放入頭節點
    // 查找x
    while (current->data != x)
    {
    current = current->link;
    index++;
    }
    // 是鏈表表頭嗎?
    return ((current == first) ? 0 : index);
}

這裏寫圖片描述
雙向鏈表

雙向鏈表的類定義

template <class T>
class DoubleNode 
{
    friend Double<T>;
private:
    T data;
    DoubleNode<T> *left, *right;
};

template<class T>
class Double {
public:
    Double() { LeftEnd = RightEnd = 0; };
    ~Double();
    int Length() const;
    bool Find(int k, T& x) const;
    int Search(const T& x) const;
    Double<T>& Delete(int k, T& x);
    Double<T>& Insert(int k, const T& x);
    void Output(ostream& out) const;
private:
    DoubleNode<T> *LeftEnd, *RightEnd;
};

以上內容整理自網絡電子資料,僅供學習交流用,勿作商業用途。轉載請註明來源。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章