单向循环链表:相对于单向链表的最后一个节点的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;
}