C++数据结构与算法——单向循环链表

单向循环链表:相对于单向链表的最后一个节点的next指针指向nullptr,单向循环链表的最后一个节点的next指针指向链表头。此处主要实现了单向循环链表的插入、删除、构造、析构、输出操作符等简单功能。

注意:单向循环链表最主要关心的是插入、删除时头节点的更新问题。如,在链表头删除时需要将链表头指向下一个节点或者置为nullptr。

单向循环链表实现:

#pragma once

#include <iostream>

using namespace std;
template<typename T>
struct ChainNode
{
    ChainNode<T> *next;
    T data;
    ChainNode(const T &t) { data = t; next = nullptr; }
    ChainNode(const ChainNode<T> & d):next(d.next),data(d.data){}
};

template<typename T>
class CircularList
{
public:
    CircularList():m_head(nullptr),m_isize(0){}
    ~CircularList();
    bool Insert(const T &t, const int n = 0);
    bool DeleteAt(T &t, const int n);
    bool IsEmpty() { return (m_isize == 0); }
    void AddBack(const T &t);
    ChainNode<T> *GetTail();
    friend ostream &operator<< (ostream &os, const CircularList<T> & rhl)
    {
        ChainNode<T> *tmp = rhl.m_head;
        for (int i = 0; i < rhl.m_isize;++i)
        {
            os << tmp->data << "  ";
            tmp = tmp->next;
        }
        return os << endl;
    }
protected:
    ChainNode<T> *GetAt(const int n);
private:
    ChainNode<T> *m_head;
    int m_isize;
};

// 删除第n个节点
// 删除主要考虑的是,要将删除节点的前一节点和后一节点连接起来,要考虑删除头节点时更新头节点的情况
template<typename T>
bool CircularList<T>::DeleteAt(T &t, const int n)
{
    if (n < 0 || n > m_isize - 1)
    {
        return false;
    }

    ChainNode<T> *tmp = GetAt(n);
    if (n == 0)
    {
        ChainNode<T> *tail = GetTail();
        if (m_isize == 1)
        {
            m_head = nullptr;
        }
        else
        {
            m_head = tmp->next;
            tail->next = m_head;
        }           
    }
    else
    {
        ChainNode<T> *prev = GetAt(n - 1);
        prev->next = tmp->next;
    }
    t = tmp->data;
    delete tmp;
    m_isize--;
    return true;
}

// 将t插入到第n个节点后,如果为空链表则调用addback
// 插入主要考虑的是将第n个节点指向新节点,将新节点指向n+1个节点(当n为m_isize-1时需要指向m_head)
template<typename T>
bool CircularList<T>::Insert(const T &t, const int n)
{
    if (IsEmpty())
    {
        if (n == 0)
        {
            AddBack(t);
            return true;
        }
        return false;
    }

    // 插入的地方非法
    if (n < 0 || n >= m_isize)
    {
        return false;
    }

    // 如果链表不为空,则有可以是在头、中、尾三个地方插入,以下代码满足三种情况
    ChainNode<T> *tmp = GetAt(n);
    ChainNode<T> *next = tmp->next;
    ChainNode<T> *newnode = new ChainNode<T>(t);

    tmp->next = newnode;
    newnode->next = next;
    ++m_isize;
    return true;
}

template<typename T>
ChainNode<T> * CircularList<T>::GetTail()
{
    return GetAt(m_isize - 1);
}

template<typename T>
CircularList<T>::~CircularList()
{
    // 由于是单向循环链表,遍历时如果以指针是否为空或者指向head节点为结束条件都不合适,因此使用m_isize不为0为条件进行遍历
    ChainNode<T> *tmp = m_head;
    while ( m_isize != 0)
    {
        tmp = m_head->next;
        delete m_head;
        m_head = tmp;
        --m_isize;
    }
}

template<typename T>
inline void CircularList<T>::AddBack(const T & t)
{
    ChainNode<T> * newnode = new ChainNode<T>(t);

    // 当前链表为空
    if (m_head == nullptr)
    {
        m_head = newnode;
        m_head->next = m_head;
        ++m_isize;
        return;
    }

    // 链表不为空,则将最后一个节点指向新节点,新节点指向head节点
    ChainNode<T> *tmp = GetTail();
    tmp->next = newnode;
    newnode->next = m_head;
    ++m_isize;
    return;
}

template<typename T>
ChainNode<T> * CircularList<T>::GetAt(const int n)
{
    // 只能获取下标[0,m_isize - 1]的元素
    if (n < 0 || n >= m_isize)
    {
        return nullptr;
    }

    ChainNode<T> * tmp = m_head;
    for (int i = 0;i < n;++i)
    {
        tmp = tmp->next;
    }
    return tmp;
}

单向循环链表测试:


#include "stdafx.h"
#include "CircularList.h"
#include <iostream>

using namespace std;

int main()
{
    CircularList<int> *pnode = new CircularList<int>;
    pnode->AddBack(10);
    pnode->AddBack(15);
    pnode->AddBack(24);
    pnode->AddBack(36);

    cout << *pnode;

    pnode->Insert(1314, 1);
    cout << *pnode; // 10 15 1314 24 36

    int iret = -1;
    pnode->AddBack(386);// 10 15 1314 24 36 386
    pnode->DeleteAt(iret, 4);// delete 36
    cout << *pnode; // 10 15 1314 24 386
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值