1. 程式人生 > >線性表的兩種實現 -- 順序表和連結串列(C++)

線性表的兩種實現 -- 順序表和連結串列(C++)

線性表抽象類宣告

#ifndef ADT_H
#define ADT_H
//線性表ADT
template <typename E>
class List{
private:
    void operator =(const List){}   //賦值運算子
    List(const List&){}          //複製建構函式
public:
    List(){}            //預設建構函式
    virtual ~List(){}  //解構函式

    //清空所有元素
    virtual void clear() = 0;

    //在當前位置插入元素item
virtual void insert(const E& item) = 0; //list尾部新增一個元素 virtual void append(const E& item) = 0; //刪除並返回當前位置的元素 virtual E remove() = 0; //設定當前位置為list的開始位置 virtual void moveToStart() = 0; //設定當前位置為list的末尾位置 virtual void moveToEnd() = 0; //當前位置左移一個位置,不改變元素 virtual
void prev() = 0; //當前位置右移一個位置,不改變元素 virtual void next() = 0; //返回元素數量--即獲取長度 virtual int length() const = 0; //返回當前元素位置 virtual int currPos() const = 0; //設定當前位置 virtual void moveToPos(int pos) = 0; //獲取當前元素 virtual const E& getValue() const = 0; }; #endif

順序表的實現

#include "ADT.h"

template <typename E>
class AList : public List<E>{
private:
    int maxSize;        //list 最大容量
    int listSize;       //list 當前元素個數
    int curr;           //當前元素位置
    E* listArray;       //list指標

public:
    AList(int size = defaultSize){          //建構函式 
        maxSize = size;
        listSize = curr = 0;
        listArray = new E[maxSize];
    }

    ~AList(){ delete[] listArray; }            //解構函式

    void clear(){                           //重新初始化list
        delete [] AList;
        listSize = 0;
        listArray = new E[maxSize];
    }

    //在當前位置插入 it 元素
    void insert(const E& it){
        Assert(listSize < maxSize, "List capacity exceeded");

        for (int i = listSize; i > curr; i--)
            listArray[i] = listArray[i - 1];
        listArray[curr] = it;
        listSize++;
    }

    //末尾新增元素
    void append(const E& it){
        Assert(listSize < maxSize, "List capacity exceeded");
        listArray[listSize++] = it;
    }

    //刪除並返回當前位置的元素
    E remove(){
        Assert(listSize < maxSize, "List capacity exceeded");
        E it = listArray[curr];
        for (int i = curr; i < listSize - 1; i++)
            listArray[i] = listArray[i + 1];
        listSize--;
        return it;
    }

    void moveToStart(){ curr = 0; }             //位置重置為start位置
    void moveToEnd(){ curr = listSize; }        //位置重置為end位置
    void prev(){ if (curr != 0) curr--; };      //上一個元素位置
    void next(){ if (curr < listSize) curr++ }; //下一個元素位置

    //返回list元素個數
    int length() const { return listSize };

    //返回當前元素位置
    int currPos() const { return curr };

    //設定當前list位置為 pos
    void moveToPos(int pos){
        Assert((pos >= 0) && (pos <= listSize), "pos out of range");
        curr = pos;
    }

    //獲取當前位置元素
    const E& getValue() const {
        Assert((curr >= 0) && (curr <= listSize), "No current element");
        return listArray[curr];
    }
};

單鏈表的實現

#include "ADT.h"

//單鏈表結點定義
template <typename E>
class Link{
public:
    E element;                          //節點的值
    Link* next;                         //next
    Link(const E& elemval, Link* nextval = NULL){
        element = elemval; 
        next = nextval;
    }
    Link(Link* nextval = NULL){
        next = nextval;
    }
};

template <typename E>
class LList: public List<E>
{
private:
    Link<E>* head;
    Link<E>* tail;
    Link<E>* curr;
    int cnt;

    //初始化
    void init(){
        curr = tail = head = new Link<E>;
        cnt = 0;
    }

    //清空所有元素
    void removeall(){
        while (head != NULL){
            curr = head;
            head = head->next;
            delete curr;
        }
    }

public:
    LList(int size = defalueSize){ init(); }            //建構函式
    ~LList(){ removeall(); }                           //解構函式

    void print() const;                                 //print List
    void clear(){ removeall(); init(); }                //clear List

    void insert(const E& it){
        curr->next = new Link<E>(it, curr->next);
        if (tail == currr)
            tail = curr->next;
        cnt++;
    }

    void append(const E& it){
        tail = tail->next = new Link<E>(it, NULL);
        cnt++;
    }

    E remove(){
        Assert(curr->next != NULL, "No element");
        E it = curr->next->element;             //儲存資料
        Link<E>* ltemp = curr->next;            //儲存連結串列節點
        if (tail == curr->next) tail = curr;    //重置尾節點
        curr->next = curr->next->next;          //刪除節點
        delete ltemp;                           //釋放空間
        cnt--;                                  //cnt - 1
        return it;
    }

    //移動至連結串列開始位置
    void moveToStart(){ curr = head; }

    //移動至連結串列尾端
    void moveToEnd(){ curr = tail; }            

    //上一個節點
    void prev(){
        if (curr == head) return;
        Link<E>* temp = head;
        while (temp->next != curr){
            temp = temp->next;
        }
        curr = temp;
    }

    //下一個節點
    void next(){
        if (curr != tail)
            curr = curr->next;
    }

    //返回長度
    int length() const { return cnt; }

    //連結串列當前位置
    int currPos() const{
        Link<E>* temp = head;
        int i;
        for (i = 0; curr != temp; i++){
            temp = temp->next;
        }
        return i;
    }

    //移動至連結串列的pos位置
    void moveToPos(int pos){
        Assert((pos >= 0) && (pos <= cnt), "Pos out of range");
        curr = head;
        for (int i = 0; i < pos; i++)
            curr = curr->next;
    }
    //獲取當前元素
    const E& getValue() const {
        Assert(curr->next != NULL, "No Value");
        return curr->next->element;
    }
};

線性表兩種實現方法的比較

空間方面:
順序表的缺點:大小事先固定。雖然便於分配空間,但是元素只有少數的幾個時造成空間的浪費。
連結串列的優點:只有實際在連結串列中的物件需要空間,只要存在可用的記憶體空間分配,連結串列中元素的個數沒有限制。

順序表的優點是對於表中的每一個元素沒有浪費空間,而連結串列需要在每個節點附加上一個指標。在可以大概估計出線性表長度的情況下,順序表比連結串列有更高的空間效率。

隨機訪問元素:
取出線性表中第i個元素這樣的按位置隨機訪問,使用順序表更快些;通過next和prev可以很容易調整當前位置向前或向後,這兩種操作需要的時間為O(1)。
相比之下,單鏈表不能直接訪問前面的元素,按位置只能從表頭(或者當前位置)開始,直到找到那個特定的位置。假定表中每個位置是由prev和moveToPos平均訪問到的,那麼訪問線性表第i個元素的操作所需的平均情況下時間和最差情況下時間都是O(n)。

插入和刪除元素:
給出指向連結串列中合適位置的指標後,insert和remove函式所需要的時間僅為O(1)。
而順序表必須在陣列內將其餘元素向前或向後移動。這種方法所需的平均情況下時間和最差時間均為O(n)。
對於許多應用,插入和刪除是最主要的操作,因此他們的時間效率是舉足輕重的。僅就這個原因而言,連結串列往往比順序表更好。