本文是为了加深自己对线性表的印象。附有复杂度的分析,数组实现和指针实现的优缺点等。

一、线性表

template <typename E> class List{
    private:
    void operator=(const List&){} //protect assignment why?
    List(const List&) {}//protect copy constructor
    
    public:
    List(){}
    virtual ~List(){}
    
    virtual void clear()=0;//clear contents from the lists,to make it empty
    
    virtual void insert(const E& item)=0;
    
    virtual void append(const E& item)=0;//add a element in the end of the list
    
    virtual E remove()=0;//remove the current element
    
    virtual void moveToStart()=0;
    
    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;
    
}

 

二、数组实现线性表

#include "List.h"
template <typename E> class AList:public List<E>{
    private:
    int maxSize;
    int listSize;
    int curr;
    E* listArray;
    
    public:
    AList(){}
    AList(int size){
        maxSize=size;
        listSize=0;
        curr=0;
        listArray=new E[maxSize];
    }
    ~AList(){ delete [] listArray;}
    
    void clear(){
        delete [] listArray;
        curr=listSize=0;
        listArray=new E[maxSize];
    }
    
    bool check(){
        if(listSize>maxSize||listSize<0) {
            cout<<"out of range"<<endl;
            return true;
        }
    }
    
    //time complexity:O(n)
    void insert(const E& item){
        listSize++;
        if(check()) return;
        for(int cnt=listSize-1;cnt>curr;cnt--){
            listArray[cnt]=listArray[cnt-1];
        }
        listArray[curr]=item;
        return;
    }
    
    //time complexity:O(1)
    void append(const E&item){
        listSize++;
        if(check()) return;
        listArray[listSize-1]=item;
    }
    
    //time complexity:O(n)
    E remove(){
        int temp=listArray[cnt];
        listSize--;
        if(check()) return NULL;
        for(int cnt=curr;cnt<listSize;cnt++){
            listArray[cnt]=listArray[cnt+1];
        }
        return temp;
    }
    
    //time complexity:O(1)
    void moveToStart(){
        curr=0;
    }
    
    void moveToEnd(){
        curr=listSize;
    }
    
    void next(){
        if(curr>=listSize-1) cout<<"out of range"<<endl;
        curr++;
    }
    
    void prev(){
        if(curr<=0) cout<<"out of range"<<endl;
        curr--;
    }
    
    int length(){ return listSize;}
    int currPos(){return curr;}
    
    void moveToPos(int pos){
        if(pos<=0||pos>=listSize) cout<<"out of range"<<endl;
        curr=pos;
    }
    
    const E& getValue() const{
        if(listSize<=0) cout<<"No current element"<<endl;
        return listArray[curr];
    }

    void print() const{
        for (int cnt = 0; cnt < listSize; cnt++) {
            cout << listArray[cnt] << " ";
        }
        cout << endl;
    }
}

三、指针实现线性表

template <typename E> class Link{
    public:
    E element;
    Link *next;
    
    Link(){
        next=nullptr;
    }
    Link(const E&item){
        element=item;
        next=nullptr;
    }
    Link(const E&item,Link* nextval){
        element=item;
        next=nextval;
    }
    Link(Link* nextval){
        next=nextval;
    }
}

template <typename E> class LList:public List<E>{
    private:
    Link<E>* head;
    Link<E>* tail;
    Link<E>* curr;
    int size;
    
    void init(){
        curr=tail=head=new Link<E>;
        cnt=0;
    }
    
    void removeall(){
        while(head!=nullptr){
            curr=head;
            head=head->next;
            delete curr;
        }
    }
    
    public:
    LList(){init();}
    LList(int length){
        init();
        cnt=length;
        int temp=cnt-1;
        while(temp--){
            append();
        }
    }
    
    void clear(){
        removeall();
        init();
    }
    
    void insert(const E&item){
        Link<E>* temp=new Link<E>(item,curr->next);
        curr->next=temp;
        if(tail==curr) tail=temp;
        size++;
    }
    
    void append(){
        tail->next=new Link<E>(0,nullptr);
        tail=tail->next;
        cnt++;
    }
    void append(const E&item){
        tail=tail->next=new Link<E>(item,nullptr);
        cnt++;
    }
    
    E remove(){//remove curr->next
        assert(curr!=tail,"No,element"); 
        E temp=curr->next->element;
        if(curr->next==tail){
            curr->next=nullptr;
            delete tail;
            tail=curr;
            cnt--;
        }
        else{
            Link<E>* ltemp=curr->next;
            curr->next=curr->next->next;
            delete ltemp;
            cnt--;
        }
        return temp;
    }
    
    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) return;
        curr=curr->next;
    }
    
    int length() const{
        return size;
    }
    
    int currPos() const{
        Link<E>* temp=head;
        int cnt=0;
        while(temp!=curr){
            temp=temp->next;
            cnt++;
        return cnt;
    } 
    
    void moveToPos(int pos){
        Link<E>* temp=head;
        while(pos--){
            temp=temp->next;
        }
        curr=temp;
    }
    
    const E& getValue() const{
        return curr->element;
    }
}

四、两种实现方式各自的优缺点

1.从存储空间上考虑 

数组需要 size*n的大小。而链表需要 size*(n+4*cnt),指针本身需要空间存放。

2.从访问速度上考虑

数组访问任一成员的时间复杂度为O(1),而链表为O(n)

3.从删除或添加成员带来的代价上考虑

数组添加或删除任一成员的时间复杂度为O(n),而链表为O(1)

4.从可扩展性考虑

数组一旦建立,便不能更改大小,而链表可以随意扩大

五、从链表衍生出来的其它数据结构

freelist

在链表创建和删除结点时,Link类的程序员能够提供简单而有效的内存管理例程,以代替系统级的存储分配和回收操作符。Link类能管理自己的可利用空间表(freelist),以取代反复调用的newdelete。当需要把一个新元素增加到链表时,先检查freelist是否为空,如果freelist不为空,则可从freelist中取走结点。在每次删除结点时,将其插入freelist中。

template <typename E>class Link{
    private:
    static Link<E>* freelist;
    
    public:
    E element;
    Link* next;
    
    Link(Link* nextval=nullptr){
        next=nextptr;
    }
    Link(const E&item,Link* nextval=nullptr){
        element=item;
        next=nextptr;
    }
    
    void* operator new(size_t){
        if(freelist=nullptr) return ::new Link;
        Link<E>* temp=freelist;
        freelist=freelist->next;
        return temp;
    }
    
    void operator delete(void* ptr){
        ((Link<E>*)ptr)->next=freelist;
        freelist=(Link<E>*)ptr;
    }
}

template <typename E> Link<E>* Link<E>::freelist=nullptr;

在重载new 函数中,原来的new函数被现在的 ::new 函数代替了。这表明标准的C++new操作符被调用,而不是重载的new操作,避免死循环。

利用空间表,还能产生额外的功效:

调用100次系统new操作符获得一百个结点远比调用一次new操作(一次获得100个结点)要慢。因此如果程序员需要成千上万个链表结点,可以先创建很多个结点在freelist中,实现程序的加速。

 

posted on 2017-12-18 17:24  logosG  阅读(201)  评论(0编辑  收藏  举报