数据结构基础(12) --双向循环链表的设计与实现

简介: 双向链表的操作特点:    (1) “查询” 和单链表相同;    (2)“插入” 和“删除”时需要同时修改两个方向上的指针。

双向链表的操作特点:

    (1) “查询” 和单链表相同;

    (2)“插入” 和“删除”时需要同时修改两个方向上的指针。

   但是对于双向循环链表则在表尾插入非常的迅速, 只需O(1)的时间,因为有指向前面的指针, 因此双向循环链表会很容易的找到位于表尾的元素,因此双向循环链表比较适用于频繁在表尾插入的情况.



空链表:


双向循环链表节点构造:

class DoubleListNode
{
private:
    Type data;
    DoubleListNode *prev;   //前向指针域
    DoubleListNode *next;   //后项指针域
};

因为需要将其用于DoubleList,因此需要将其改造如下:

template <typename Type>
class DoubleListNode
{
    //友元声明
    friend class DoubleList<Type>;
    friend class ListIterator<Type>;

    template <typename T>
    friend ostream &operator<<(ostream &os, const DoubleList<T> &list);
private:
    DoubleListNode(const Type &dataValue)
        :data(dataValue),prev(NULL),next(NULL) {}

    Type data;
    DoubleListNode *prev;   //前向指针域
    DoubleListNode *next;   //后项指针域
};

双向循环链表构造:

template <typename Type>
class DoubleList
{
    friend class ListIterator<Type>;
    template <typename T>
    friend ostream &operator<<(ostream &os, const DoubleList<T> &list);
public:
    DoubleList();
    ~DoubleList();

    void push_back(const Type &data);
    void push_front(const Type &data);
    void insert(int position, const Type &data);

    void pop_front();
    void pop_back();
    void remove(const Type &removeData);

    bool search(const Type &searchData) const;
    bool isEmpty() const
    {
        return (first->next == first);
    }

private:
    //将节点x插入到节点previous后面
    void insertPrivate(DoubleListNode<Type> *previous,
                       DoubleListNode<Type> *x);
    void removePrivate(DoubleListNode<Type> *x);

private:
    DoubleListNode<Type> *first;
};

链表的构造与析构:

//构造链表
template <typename Type>
DoubleList<Type>::DoubleList()
{
    first = new DoubleListNode<Type>(0);
    first->next = first;
    first->prev = first;
}
//析构链表
template <typename Type>
DoubleList<Type>::~DoubleList()
{
    DoubleListNode<Type> *deleteNode = NULL;
    //保存链表尾元素
    DoubleListNode<Type> *tmp = first;

    //first首先指向第一个真实的元素
    first = first->next;
    //一路到达链表结尾
    while (first != tmp)
    {
        deleteNode = first;
        first = first -> next;
        delete deleteNode;
    }
    // 释放掉链表的空节点(表头)
    delete tmp;
}

链表元素插入与删除的两大主力:

//同为private成员
//插入节点
template <typename Type>
void DoubleList<Type>::insertPrivate(DoubleListNode<Type> *previous,
                                     DoubleListNode<Type> *x)
{
    x->prev = previous;
    x->next = previous->next;
    previous->next->prev = x;
    previous->next = x;
}

//删除节点
template <typename Type>
void DoubleList<Type>::removePrivate(DoubleListNode<Type> *x)

{
    if (x == first)
        throw std::range_error("permission denied to delete first pointer");

    x->prev->next = x->next;
    x->next->prev = x->prev;
    delete x;
}

提供给客户的插入:

//插入到表尾
template <typename Type>
void DoubleList<Type>::push_back(const Type &data)
{
    DoubleListNode<Type> *newNode = new DoubleListNode<Type>(data);
    //找到first的前一个节点
    DoubleListNode<Type> *previous = first->prev;

    //插入
    insertPrivate(previous, newNode);
}
//插入到表头
template <typename Type>
void DoubleList<Type>::push_front(const Type &data)
{
    DoubleListNode<Type> *newNode = new DoubleListNode<Type>(data);
    //插入到first之后
    insertPrivate(first, newNode);
}

//插入到任意位置(依position指定)
template <typename Type>
void DoubleList<Type>::insert(int position, const Type &data)
{
    if (position == 1)
        return push_front(data);

    int count = 1;
    //previous 代表着要插入位置之前的一个位置
    DoubleListNode<Type> *previous = first->next;

    //如果position过大, 则previous查找到first就会停止
    //此时应该将该元素插入到链表结尾
    while (count < position-1 && previous != first)
    {
        ++ count;
        previous = previous->next;
    }

    //如果查找到了链表结尾或此时链表为空, 因此插入到表尾
    if (previous == first)
        return push_back(data);

    //如果找到了合适的插入位置
    DoubleListNode<Type> *newNode = new DoubleListNode<Type>(data);
    insertPrivate(previous, newNode);
}

提供给客户的删除:

//删除表尾元素
template <typename Type>
void DoubleList<Type>::pop_back()
{
    removePrivate(first->prev);
}
//删除表头元素
template <typename Type>
void DoubleList<Type>::pop_front()
{
    removePrivate(first->next);
}

//删除元素值为removeData的所有元素
template <typename Type>
void DoubleList<Type>::remove(const Type &removeData)
{
    if (isEmpty())
        throw std::range_error("link list is empty");

    for( DoubleListNode<Type> *searchNode = first->next;
            searchNode != first;
            searchNode = searchNode->next)
    {
        if (searchNode->data == removeData)
            removePrivate(searchNode);
    }
}

查看是否存在于链表中:

template <typename Type>
bool DoubleList<Type>::search(const Type &searchData) const
{
    DoubleListNode<Type> *searchNode = first->next;
    while (searchNode != first)
    {
        if (searchNode->data == searchData)
            return true;

        searchNode = searchNode->next;
    }
    return false;
}

输出链表所有元素(测试用):

template <typename Type>
ostream &operator<<(ostream &os, const DoubleList<Type> &list)
{
    for (DoubleListNode<Type> *currentNode = (list.first)->next;
            currentNode != list.first;
            currentNode = currentNode->next)
        os << currentNode->data << ' ';

    return os;
}

双向循环链表迭代器的设计与实现:

//除了添加了operator--, 几乎没做任何改变
template <typename Type>
class ListIterator
{
public:
    ListIterator(const DoubleList<Type> &_list):
        list(_list),
        currentNode((_list.first)->next) {}

    //重载 *operator
    const Type &operator*() const throw (std::out_of_range);
    Type &operator*() throw (std::out_of_range);

    //重载 ->operator
    const DoubleListNode<Type> *operator->() const throw (std::out_of_range);
    DoubleListNode<Type> *operator->() throw (std::out_of_range);

    //重载 ++operator
    ListIterator &operator++() throw (std::out_of_range);
    //注意:此处返回的是值,而不是reference
    ListIterator operator++(int) throw (std::out_of_range);

    //重载 --operator,
    // 其实这个版本的--operator是不完美的,
    // 因为他没有做任何的判错控制
    ListIterator &operator--();
    //注意:此处返回的是值,而不是reference
    ListIterator operator--(int);

    bool isEmpty() const
    {
        return (currentNode == list.first);
    }

private:
    const DoubleList<Type> &list;
    DoubleListNode<Type> *currentNode;
};
template <typename Type>
const Type &ListIterator<Type>::operator*() const
throw (std::out_of_range)
{
    if (isEmpty())
        throw std::out_of_range("iterator is out of range");
    // 返回当前指针指向的内容
    return currentNode->data;
}
template <typename Type>
Type &ListIterator<Type>::operator*()
throw (std::out_of_range)
{
    return
        const_cast<Type &>(
            static_cast<const ListIterator<Type> &>(*this).operator*()
        );
}
template <typename Type>
const DoubleListNode<Type> *ListIterator<Type>::operator->() const
throw (std::out_of_range)
{
    if (isEmpty())
        throw std::out_of_range("iterator is out of range");

    //直接返回指针
    return currentNode;
}

template <typename Type>
DoubleListNode<Type> *ListIterator<Type>::operator->()
throw (std::out_of_range)
{
    return
        const_cast<DoubleListNode<Type> *> (
            static_cast<const ListIterator<Type> >(*this).operator->()
        );
}
template <typename Type>
ListIterator<Type> &ListIterator<Type>::operator++()
throw (std::out_of_range)
{
    if (isEmpty())
        throw std::out_of_range("iterator is out of range");
    //指针前移
    currentNode = currentNode->next;
    return *this;
}
template <typename Type>
ListIterator<Type> ListIterator<Type>::operator++(int)
throw (std::out_of_range)
{
    ListIterator tmp(*this);
    ++ (*this); //调用前向++版本

    return tmp;
}
template <typename Type>
ListIterator<Type> &ListIterator<Type>::operator--()
{
    //指针前移
    currentNode = currentNode->prev;
    return *this;
}
template <typename Type>
ListIterator<Type> ListIterator<Type>::operator--(int)
{
    ListIterator<Type> tmp(*this);
    -- (*this);

    return tmp;
}

测试代码:

int main()
{
    cout << "-------- 1 --------" << endl;
    DoubleList<int> myList;

    for (int i = 0; i < 3; ++i)
        myList.push_back(i+1);
    for (int i = 0; i < 5; ++i)
        myList.push_front(10+i);
    for (int i = 0; i < 3; ++i)
        myList.push_back(i+1);

    ListIterator<int> iter(myList), iter2(myList);
    while (!iter.isEmpty())
    {
        cout << *iter << ' ';
        ++ iter;

        ++ iter2;
    }
    cout << endl;

    -- iter2;
    while (!iter2.isEmpty())
    {
        cout << *iter2 << ' ';
        iter2 --;
    }
    cout << endl;

    cout << "-------- 2 --------" << endl;

    cout << myList << endl;
    cout << "Test insert..." << endl;
    myList.insert(1, 14);
    myList.insert(2, 13);
    myList.insert(2, 13);
    myList.insert(88, 88);
    cout << myList << endl;

    myList.pop_back();
    myList.pop_front();
    cout << myList << endl;

    for (int i = 0; i < 5; ++i)
    {
        if (myList.search(i))
            cout << i << ": Have found!" << endl;
        else
            cout << i << ": Not in the list!" << endl;
    }

    cout << "Test remove..." << endl;
    cout << myList << endl;
    int value;
    while (cin >> value)
    {
        try
        {
            myList.remove(value);
        }
        catch (const std::exception &e)
        {
            cerr << e.what() << endl;
        }

        cout << myList << endl;
        if (myList.isEmpty())
        {
            cout << "empty" << endl;
        }
        else
        {
            cout << "not empty" << endl;
        }
    }

    return 0;
}

目录
相关文章
|
4天前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
25 4
|
5天前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
5天前
|
存储 Web App开发 算法
2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构之单双链表按位、值查找;[前后]插入;删除指定节点;求表长、静态链表等代码及具体思路详解步骤;举例说明、注意点及常见报错问题所对应的解决方法
|
25天前
|
存储 Java
数据结构第三篇【链表的相关知识点一及在线OJ习题】
数据结构第三篇【链表的相关知识点一及在线OJ习题】
23 7
|
26天前
|
存储 安全 Java
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
21 3
|
24天前
|
算法 Java
数据结构与算法学习五:双链表的增、删、改、查
双链表的增、删、改、查操作及其Java实现,并通过实例演示了双向链表的优势和应用。
15 0
数据结构与算法学习五:双链表的增、删、改、查
|
4天前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
15 0
【数据结构】——双向链表详细理解和实现
【数据结构】——双向链表详细理解和实现
|
29天前
|
存储 Java
【数据结构】链表
【数据结构】链表
16 1
|
29天前
|
存储 缓存
数据结构3——双向链表
数据结构3——双向链表
104 1