【李木华】线性表

来源:互联网 发布:潍坊全影网络是传销吗 编辑:程序博客网 时间:2024/04/30 16:30

【转】:http://blog.csdn.net/alfa_/article/details/53736853

线性表

  1. 定义:
    • 线性表是由称为元素(element)的数据项组成的一种有限且有序的序列。
    • 线性表中不包括任何元素时,称之为空表。当前存储的元素数目称之为线性表的长度(length)。线性表的开始节点称为表头(head),结尾节点称为表尾(tail)。
    • 线性表的第一个元素使0来表示的。
    • 线性表有两种实现方式:顺序表和链表。
ADT:template <typename E> class List {public:            List(){}            virtual ~List() {}            virtual void append( E& item) = 0;            virtual E remove() = 0;            virtual void insert() = 0;            virtual void moveToStart() = 0;            virtual void moveToEnd() = 0;            virtual void moveToPos(int pos) = 0;            virtual void prev() = 0;            virtual void next() = 0;            virtual int length() = 0;            virtual int currPos() = 0;            virtual const E& getValue() const = 0;};
  1. 顺序表:
AList:template <typename E>class AList :public List < E > {private:    int maxSize;     //顺序表的最大长度    int listSize;      //当前顺序表中的元素个数    int curr;        //当前的元素    E* listArray;     //数组public:    AList(int size = defaultSize){        maxSize = size;        listSize = curr = 0;        listArray = new E[maxSize];    }    ~AList(){ delete[] listArray; }    void clear()    //清空    {        delete[] listArray;        listSize = curr = 0;        listArray = new E[maxSize];    }    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 moveToStart() { curr = 0; }    //移动到开头    void moveToEnd() { curr = listSize; }//移动到结尾    void prev() { if(curr != 0) curr--; }   //上一个    void next() { if(curr<listSize) curr++; }//下一个    int length() const { return listSize; }   //当前顺序表长度    int currPos() const { return curr; }     //当前位置    void append(E& item)             //在末尾添加元素    {        assert(listSize < maxSize, "List capacity exceeded");        listArray[listSize++] = item;    }    void moveToPos(int pos)        //移动到某一个位置    {        assert((pos >= 0) && (pos < listSize), "No element");        curr = pos;    }    E remove()                     //删除某一元素{        assert((curr >= 0) && (curr < listSize), "No element");        E it = listArray[curr];        for (int i = curr; i < listSize-1; i++)            listArray[i] = listArray[i + 1];        listSize--;        return it;    }    const E& getValue() const        //当前位置的值    {        assert((curr>0) && (curr < listSize), "No current element");        return listArray[curr];    }};

  平均来说,插入和删除要移动一半的元素,即需要Θ(n)时间。
3. 链表:
* 利用指针实现线性表,链表是动态的,它能够按照需要为表中新的元素分配存储空间。
* 链表是由一系列称为表的节点的对象组成的。
* 节点的定义:

Link:    template <typename E> class Link {public:    E element;   //节点的值    Link *next;   //指向下一个节点    //构造函数    Link(const E& elemval, Link* nextval = NULL)    {        element = elemval;        next = nextval;    }    Link(Link* nextval = NULL) { next = nextval; }};
  • 链表类的定义:
template<typename E> class LList : public Link < 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 = defaultSize){ init(); }    ~LList(){ removeall(); }void print()   //打印链表元素    {        Link* temp = head->next;        for (int i = 0; i < cnt; i++)        {            cout << temp->element << " ";            temp = temp->next;        }        cout << endl;    }    void clear(){ removeall(); init(); }    void insert(const E& it)    {        curr->next = new Link<E>(it, curr->next);        if (tail == curr) tail = curr->next;        cnt++;    }    void append(const E& it)    //添加链表元素    {        tail = tail->next = new Link<E>(it, NULL);        cnt++;    }    const E& getValue() const   //获取值    {        assert(curr->next != NULL, "No value");        return curr->next->element;    }    E remove()             //移除当前节点    {        assert(curr->next != NULL, "No Element!");        E it = curr->next->element;        Link<E>* ltemp = curr->next;        curr->nect = curr->next->next;        delete ltemp;        cnt--;        return it;    }    void moveToEnd()    {        curr = head;    }    void moveToStart()    {        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 cnt;    }    int currPos() const  //当前结点    {        Link<E>* temp = head;        int i = 0;        while (temp != curr)        {            temp = temp->next;            i++;        }        return i;    }    void moveToPos(int pos)  //移到当前结点    {        assert((pos >= 0) && (pos <= cnt), "position out of range");        curr = head;        for (int i = 0; i < pos; i++)            curr = curr->next;    }};
  • 可利用空间表
    Link类能管理自己的可利用空间表(freelist),以取代反复调用的new和delete。可利用空间表存放当前那些不用的线性表,从一个链表中删除的结点就可以放到可利用空间表的首端。当需要把一个新元素增加到链表中时,先检查可利用空间表,看看是否有可用的线性表节点。如果有空结点,则从可利用空间表中取走一个结点。只有当可利用空间表为空时,才会调用标准操作符new。

  • 可利用空间表的实现方法:在Link类实现中增加两个新函数,以替代标准的存储分配和回收管理例程new和delete。

链表总结:

  数组在内存中是一种连续的数据结构,而链表在内存中是一种不连续的,通过它的定义可以知道,它由一个一个的节点关联组成。以一般的单链表举例,当拿到一个节点的时候,我们可以访问它的下一个节点。要插入一个新节点的时候直接在尾部插入即可。当要修改一个节点的时候,只需要找到它然后直接对它操作。如果需要删除一个节点,就需要找到它的前驱节点,让它的前驱节点指向它的后继节点。这个节点就算被删除了。(严格地说,我们还要考虑待删除的节点所占用的内存,不过这里我们忽略了内存管理的部分)。如下图所示
这里写图片描述
  问题特地把红色箭头及其所能控制的范围标了红色,是想表明,只要得到一个节点,就可以通过该节点中指向下一节点的指针来访问下一节点,只要得到下一节点,就会得到下下一节点,如此类推。这种内容与指针的结合方式,最好使用结构体:

struct Node{    Type element;    Node *next;};

  而定义链表的时候,只需要把链表的头和尾控制好就行

struct List{    Node *head;    Node *tail;    int size;    ............};

  遍历链表的时候,从head开始,一直next找到自己想要的内容即可。



Copyright© by 李木华
Judge by 寒江雪
Date:2016.12.20

#

0 0
原创粉丝点击