码迷,mamicode.com
首页 > 其他好文 > 详细

双向链表

时间:2015-01-14 21:14:52      阅读:205      评论:0      收藏:0      [点我收藏+]

标签:

一. 链表结点类

链表结点类最好是定义为链表类的私有内部类. 不过由于该代码用到了模板, 在VS2013下会遇到各种各样的编译问题, 因此改为定义在外部.

template<class T>
struct Node
{
    T      data;
    Node*  prev;
    Node*  next;

    Node(const T& src = T(), Node* p = NULL, Node* n = NULL) :
        data(src), prev(p), next(n){}
    friend class LinkedList < T > ;
};

二. 迭代器类

由于双向链表的遍历, 插入, 删除操作涉及大量的重复的指针操作. 为了提高代码复用性, 我们引入迭代器类.

引入迭代器之后, 就有必要引入两个私有哨兵(sentinel/sentry node)结点: head和tail. 建议写成公共内部类. 但本文中由于VS编译环境对模板的语法要求太苛刻, 只能从权改为外部类

head的作用:

1.便于判断链表的空和满.

2.简化链表操作. 主要是便于取到链表的第一个元素, 无论第一个元素经历多少次更迭. 其次还有便于反向遍历时控制迭代器的终止条件

tail的作用:

1. 便于在尾部插入(这也是双向链表和单向链表的区别之一:单向链表只能在当前结点的后面插入, 双向链表最好在当前结点之前插入)

2. 使用迭代器时, 便于控制正向遍历的终止条件.

template<class T>
class iterator<T>
{
public:
    Node<T>*           current;
    LinkedList<T>*  host;
public:
    iterator<T>(Node* c, LinkedList<T>* h) :
        current(c), host(h){}

    //迭代器的解引用
    T& operator* ()
    {
        return current->data;
    }
    const T& operator* () const
    {
        return current->data;
    }

    //迭代器的移动
    //前置++
    iterator<T>& operator++()
    {
        current = current->next;
        return *this;
    }
    //后置++, 出于代码需要, 这里面的后置++并不能真正的移动迭代器, 只是向后peek一下
    iterator<T>  operator++(int)
    {
        auto i = iterator<T>(this->current->next, this->host);
        return i;
    }
    iterator<T>& operator--()
    {
        current = current->prev;
        return *this;
    }
    // 并不移动, 只是向前peek
    iterator<T>  operator--(int)
    {
        auto i = iterator<T>(this->current->prev, this->host);
        return i;
    }

    //迭代器的比较
    bool operator==(const iterator<T>& src) const
    {
        return (current == src.current && host == src.host);
    }
    bool operator!=(const iterator<T>& src) const
    {
        return (current != src.current || host != src.host);
    }
    friend class LinkedList < T > ;
};

三. 链表类ADT

template <class T>
class LinkedList
{
private:
    int    sz;
    Node<T>*  head;
    Node<T>*  tail;
    void   init();   //initialize head and tail;
public:

public:
    LinkedList();
    LinkedList(const LinkedList& src);
    virtual ~LinkedList();
    LinkedList& operator= (const LinkedList& src);

    iterator<T> begin();
    iterator<T> end();
    const iterator<T> begin() const;
    const iterator<T> end()   const;

    int size() const
    {
        return sz;
    }

    bool isEmpty() const
    {
        return sz==0;
    }

    void clear();

    void pushBack(const T& src);
    void pushFront(const T& src);
    void popBack();
    void popFront();

    iterator<T> insert (iterator<T> it, const T& src);
    iterator<T> remove (iterator<T> it);
    iterator<T> remove (iterator<T> from, iterator<T> to);
};

四. begin()和end()操作

template <class T>
iterator<T> LinkedList<T>::begin()
{
    return iterator<T>(head->next, this);
}

template <class T>
const iterator<T> LinkedList<T>::begin() const 
{
    return iterator<T>(head->next,this);
}

template <class T>
iterator<T> LinkedList<T>::end()
{
    return iterator<T>(tail, this);
}

template <class T>
const iterator<T> LinkedList<T>::end() const
{
    return iterator<T>(tail, this);
}

五. 插入

1. 插入指定的迭代器位置

template <class T>
iterator<T> LinkedList<T>::insert(iterator<T> it, const T& src)
{
    Node<T>* p = it.current;
    ++sz;
    /*
     * return处是对如下代码的简化
     * Node* myPrev = p->prev; //待插入结点的前驱
     * Node* myNext = p;       //待插入结点的后继
     * Node* myNode = new Node(it, myPrev, myNext); //申请新节点, 并连接前驱和后继
     * p->prev->next = myNode; //让前驱,后继和自己连接
     * p->prev = myNode;       
     */
    return iterator<T>(p->prev = p->prev->next = new Node(src, p->prev, p), this);
    // 最终结果是插入到it的前面.
}

2. 插入在头部或尾部

template <class T>
void LinkedList<T>::pushBack(const T& src)
{
    insert(end(), src);
}

template <class T>
void LinkedList<T>::pushFront(const T& src)
{
    insert(begin(), src);
}

 

六. 删除结点

1. 删除指定迭代器位置的结点

template <class T>
iterator<T> LinkedList<T>::remove(iterator<T> it)
{
    Node<T>* p = it.current;
    --sz;
    iterator<T> result(p->next, this);
    p->prev->next = p->next;
    p->next->prev = p->prev;
    delete p;
    return result;
}

template <class T>
iterator<T> LinkedList<T>::remove(iterator<T> from, iterator<T> to)
{
    if(sz==0) return begin();
    for(auto it = from; it!= to; )
        it=remove(it);
    return to;
}

2. 其他删除操作

template <class T>
void LinkedList<T>::popBack()
{
    remove(--end());
}

template <class T>
void LinkedList<T>::popFront()
{
    remove(begin());
}
template <class T>
void LinkedList<T>::clear()
{
    remove(begin(), --end());
}

七. 内存管理

template <class T>
LinkedList<T>::LinkedList()
{
    init();
}

template <class T>
LinkedList<T>::LinkedList(const LinkedList& src)
{
    init();
    *this = src; //call the overloaded operator=
}

template <class T>
LinkedList<T>::~LinkedList()
{
    clear();
    delete head;
    delete tail;
}

template <class T>
LinkedList<T>& LinkedList<T>::operator=(const LinkedList<T>& src)
{

    if (this==&src)
    {
        return *this;
    }
    clear();
    for (iterator<T> it = src.begin(); it!=src.end(); ++it)
    {
        this->pushBack(*it);
    }
    return *this;
}

template <class T>
void LinkedList<T>::init()
{
    sz = 0;
    head = new Node();
    tail = new Node();
    head->next = tail;
    tail->prev = head;
}

双向链表

标签:

原文地址:http://www.cnblogs.com/roy-mustango/p/4224846.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!