单向循环链表是单链表的另一种形式,其结构特点是链表中最后一个结点的指针不再是结束标记,而是指向整个链表的第一个结点,从而使单链表形成一个环。
和单链表相比,循环单链表的长处是从链尾到链头比较方便。当要处理的数据元素序列具有环型结构特点时,适合于采用循环单链表。
单循环链表的初始化操作:示意图
非空表示意图:
假如终端结点用尾指针 rear 指示, 则查找终端结点是 O(1), 而开始结点,其实就是 rear->next->next, 时间复杂度也是 O(1)。
下面用C++ 代码实现单循环链表:
#include
#include
using namespace std;
using ElemType = int;
using Status = void;
class CirLinkList
{
public:
struct Node
{
ElemType m_data;
Node *m_next;
};
enum State
{
TT_ERROR = 0,
TT_OK = 1
};
public:
CirLinkList();
~CirLinkList();
ElemType insertAt(ElemType i, ElemType elem);
ElemType removeAt(ElemType i, ElemType &elemOut);
ElemType getAt(ElemType i, ElemType &elemOut);
ElemType locationElemAt(ElemType &i, ElemType elemOut);//查找与e相等的元素,返回第一个与e相等元素在线性表中的下标,否则,返回0
Status getLength()const;
Status isEmpty()const;
ElemType destroy();
ElemType clear();
Status show();
Status backRemoveElemAt(); //尾删
Status backInsertElemAt(ElemType elem);//尾插
Status headInsetElemAt(ElemType elem); //头插
Status headRemoveElemAt(); //头删
Status reserve(); //逆置链表
ElemType priorElemAt(ElemType cur_e, ElemType &pri_e);//若cur_e是链表的数据元素,且不是第一个,则用pre_e返回它的前驱
ElemType nextElemAt(ElemType cur_e, ElemType &Nex_e); //若cur_e是链表的数据元素,且不是最后一个,则用next_e返回它的后继,
Status createTail(ElemType *datas, ElemType length);//创建长度为length的链表,数据通过数组指定,这里采用尾插法
Status createHead(ElemType *datas, ElemType length); //头插法
Node *prevElemIndexAt(Node *nodeElem); //返回nodeElem指向的前面一个节点
ElemType modify(ElemType findElem, ElemType replaceElem); //把原有的elem1修改成elem2
Node *find(ElemType preElemAddress); //返回要查找元素的前面一个的地址
private:
Node *m_head;
Node *m_tail;
};
CirLinkList::CirLinkList()
{
m_head = new Node;
assert(m_head != nullptr);
m_head->m_next = m_head;
m_tail = m_head;
cout << "\n********************单向的循环列表初始化成功!************************" << endl;
}
CirLinkList::~CirLinkList()
{
this->destroy();
}
Status CirLinkList::createHead(ElemType *datas, ElemType length) //头插法
{
m_head->m_data = length;
Node *s = new Node;
if (m_head->m_next == m_head)
{
//this->insertAt(1,datas[0]); 利用insertAt 函数创建头插法的第一个结点
s->m_data = datas[0];
m_head->m_next = s;
s->m_next = m_head;
m_tail = s;
}
//Node *s = m_head->m_next;
for (int i = 1; i < length; ++i)
{
Node *p = new Node;
p->m_data = datas[i];
p->m_next = s;
m_head->m_next = p;
s = p;
}
}
CirLinkList::Node *CirLinkList::find(ElemType preElemAddress) //返回要查找元素的前面一个的地址
{
Node *p = m_head;
while ((m_tail != p) && (m_head) != (p->m_next) && (preElemAddress) != (p->m_next->m_data))
{
p = p->m_next; //循环结束,p指向x的前面一个元素
}
if (m_head == p->m_next) //如果p指向最后一个元素,说明没有找到
{
cout << "没找到该元素!" << endl;
return nullptr;
}
return p;
}
ElemType CirLinkList::modify(ElemType findElem, ElemType replaceElem) //把原有的elem1修改成elem2
{
if (!(m_head->m_data))
{
return TT_ERROR;
}
Node *p = find(findElem);
if (nullptr != p)
{
p->m_next->m_data = replaceElem;
}
return TT_OK;
}
CirLinkList::Node *CirLinkList::prevElemIndexAt(Node *nodeElem) //返回nodeElem指向的前面一个节点
{
if (nodeElem != m_head)
{
Node *q = m_head->m_next;
while (q != m_head && q->m_next != nodeElem) //依次往后移,知道尾指针的前面一个节点
{
q = q->m_next;
}
if (q->m_next == nodeElem)
{
return q;
}
}
return nullptr;
}
Status CirLinkList::reserve() //逆置链表
{
/* 自己编写的代码,没通过
m_tail->m_next = nullptr;
Node *p = m_head->m_next;
m_tail = p;
m_head->m_next = nullptr;
Node *s = new Node;
Node *m = s;
while (p)
{
p->m_next = s;
s = p;
p = p->m_next;
}
m_head->m_next = p;
delete m;*/
if (!(m_head->m_data) || (m_head->m_data == 1))
{
cout << "链表中没有元素,或者只有一个元素,无法反转列表!" << endl;
system("pause");
exit(0);
}
Node *s = new Node; //建立一个节点
s->m_next = m_tail;
Node *p = m_tail;
while (m_tail != m_head->m_next) //把原链表的尾节点到第一个节点依次连接到新节点上
{
m_tail = prevElemIndexAt(m_tail);
m_tail->m_next = m_head;
p->m_next = m_tail;
p = p->m_next;
}
p->m_next = s; //p指向第一个节点,即新链表的最后一个节点,尾指针的next指向头结点s,链表结束
delete m_head; //释放原来的头结点
m_head = s; //把s变成新的头指针
}
ElemType CirLinkList::priorElemAt(ElemType cur_e, ElemType &pri_e)//若cur_e是链表的数据元素,且不是第一个,则用pre_e返回它的前驱
{
if (!(m_head->m_data) || (m_head->m_data == 1))
{
return TT_ERROR;
}
Node *p = m_head->m_next->m_next;
Node *q = m_head->m_next;
while ((p->m_data != cur_e) &&(p != m_head))
{
q = p;
p = p->m_next;
}
if (!(p != m_head))
{
return TT_ERROR;
}
pri_e= (q->m_data);
return TT_OK;
}
ElemType CirLinkList::nextElemAt(ElemType cur_e, ElemType &Nex_e) //若cur_e是链表的数据元素,且不是最后一个,则用next_e返回它的后继,
{
if (!(m_head->m_data))
{
return TT_ERROR;
}
Node *q = m_head->m_next;
while ((q != m_tail) && (q->m_data != cur_e))
{
q = q->m_next;
}
if (!(q != m_tail))
{
return TT_ERROR;
}
Nex_e = (q->m_next->m_data);
return TT_OK;
}
Status CirLinkList::backRemoveElemAt() //尾删
{
if (!m_head->m_data)
{
cout << "错误,列表中没有元素,无法删除!" << endl;
}
else
{
Node *p = m_head;
Node *q = m_head->m_next;
while (q != m_tail)
{
p = q;
q = q->m_next;
}
m_tail = p;
delete q;
p->m_next = m_head;
--m_head->m_data;
}
}
Status CirLinkList::backInsertElemAt(ElemType elem)//尾插
{
Node *p = m_head->m_next;
while (p != m_tail)
{
p = p->m_next;
}
Node *q = new Node;
q->m_data = elem;
p->m_next = q;
q->m_next = m_head;
m_tail = q;
++m_head->m_data;
}
Status CirLinkList::headInsetElemAt(ElemType elem) //头插
{
Node *s = new Node;
s->m_data = elem;
s->m_next = m_head->m_next;
m_head->m_next = s;
++m_head->m_data;
}
Status CirLinkList::headRemoveElemAt() //头删
{
if (!m_head->m_data)
{
cout << "错误,列表中没有元素,无法删除!" << endl;
}
else
{
Node *q = m_head->m_next;
m_head->m_next = q->m_next;
delete q;
--m_head->m_data;
}
}
Status CirLinkList::createTail(ElemType *datas, ElemType length)//创建长度为length的链表,数据通过数组
{
m_head->m_data = length;
for (int i = 0; i < length; ++i)
{
//this->insertAt(i+1,data[0]); //利用该函数可以节省代码尾插法创建链表
Node *p = new Node;
assert(p != nullptr); //断言,表达式为真,接着往下执行
p->m_data = datas[i];
m_tail->m_next = p; //m_tail指向最后一个节点,把新建立的节点链接到链表的最后
p->m_next = m_head; //单循环链表,新节点m_next指向头结点
m_tail = p; //改变尾指针的指向
}
}
ElemType CirLinkList::insertAt(ElemType i, ElemType elem)
{
if ((i < 1) || (i > ((m_head->m_data) + 1))) //i位置不合理时,返回false
{
return TT_ERROR;
}
Node *p = m_head->m_next, *q = m_head;
int j = 1;
while (j < i) //当j小于i时 就继续
{
q = p;
p = p->m_next;
++j;
}
Node *s = new Node;
s->m_data = elem;
q->m_next = s; //把新节点插入到链表中(在p,q之间插入)
s->m_next = p;
if (p == m_head) //在表尾插入改变尾指针
{
m_tail = s;
m_tail->m_next = m_head;
}
++m_head->m_data;
return TT_OK;
}
ElemType CirLinkList::removeAt(ElemType i, ElemType &elemOut)
{
if ((i < 1) || (i > m_head->m_data)) //i位置不合理时,返回false
{
return TT_ERROR;
}
Node *p = m_head;
int j = 1;
while (j < i) //当j小于i时 就继续
{
p = p->m_next;
++j;
}
Node *q = p->m_next; //q指向待删除结点
p->m_next = q->m_next;
elemOut = q->m_data;
if (m_tail == q) //删除的是表尾元素,表尾指针发生改变
{
m_tail = p;
m_tail->m_next = m_head;
}
delete q;
--m_head->m_data;
return TT_OK;
}
Status CirLinkList::getLength()const
{
cout << "当前的单循环链表中的元素个数有:" << (m_head->m_data) << "个!" << endl;
}
Status CirLinkList::isEmpty()const
{
if (!(m_head->m_data))
{
cout << "目前该线性表为空!" << endl;
}
else
cout << "目前该线性表非空!" << endl;
}
Status CirLinkList::show()
{
if (!(m_head->m_data))
{
cout << "该列表中没有元素,无法显示!" << endl;
}
else
{
Node *p = m_head->m_next;
cout << "输出单向循环链表中的所有元素:";
while (p != m_head) //p未到表头
{
cout << (p->m_data) << " ";
p = p->m_next;
}
cout << endl;
}
}
ElemType CirLinkList::getAt(ElemType i, ElemType &elemOut)
{
if ((i < 1) || (i > (m_head->m_data)))
{
return TT_ERROR;
}
Node *q = m_head->m_next; //让q指向链表的第一个节点
int j = 1;
while ((j < i))
{
q = q->m_next;
++j;
}
elemOut = q->m_data; //取第i个元素的数据
return TT_OK;
}
ElemType CirLinkList::locationElemAt(ElemType &i, ElemType elemOut)
{
Node *p = m_head->m_next; //从第一个结点开始遍历
int searchLocation = 1;
while (p !=m_head && (p->m_data != elemOut))
{
++searchLocation;
p = p->m_next;
}
if (!(p != m_head))
{
return TT_ERROR;
}
i = searchLocation;
return TT_OK;
}
ElemType CirLinkList::destroy()
{
this->clear();
delete m_head;
m_head = m_tail = nullptr;
return TT_OK;
}
ElemType CirLinkList::clear()
{
Node *p = m_head->m_next;
Node *q = p;
while (m_head != p)
{
p = p->m_next; //p依次后移,跟屁虫q依次释放节点
delete q;
q = p;
}
m_tail = m_head; //修改尾指针和链表长度
m_head->m_next = m_head;
m_head->m_data = 0;
return TT_OK;
}
//测试单循环链表2
void testMyCirLinkList()
{
CirLinkList myCirLinkList;
int myLength(0); //单链表的整表创建,尾插法
cout << "想创建多少数据的链表?";
cin >> myLength;
int *myDatas = new int[myLength];
cout << "请依次输入这" << myLength << "个数据,中间以回车符隔开:" << endl;
for (int i = 0; i < myLength; ++i)
{
cin >> myDatas[i]; //输入要存储的数据的值
}
myCirLinkList.createHead(myDatas, myLength); //调用createTail函数 建立单链表
myCirLinkList.show();
while (true)
{
{
cout << "\n*************************************************************" << endl
<< "******************* 单向循环链表的基本功能展示 *******************" << endl
<< "*****************************************************************" << endl
<< "******************** 选择1——数据插入. **********************" << endl
<< "******************** 选择2——数据删除. **********************" << endl
<< "******************** 选择3——获取元素. **********************" << endl
<< "******************** 选择4——查找元素. **********************" << endl
<< "\n*************************************************************" << endl
<< "******************** 选择5——是否为空. **********************" << endl
<< "******************** 选择6——获取链表长度. **********************" << endl
<< "******************** 选择7——清空元素. **********************" << endl
<< "******************** 选择8——输出所有元素. ************************" << endl
<< "\n*************************************************************" << endl
<< "******************** 选择9——销毁链表. ************************" << endl
<< "********************* 选择10——获得元素的前驱. *****************" << endl
<< "********************* 选择11——获得元素的后继. *****************" << endl
<< "********************* 选择12——尾插. ****************************" << endl
<< "********************* 选择13——尾删. *****************************" << endl
<< "\n*************************************************************" << endl
<< "********************* 选择14——头插. *****************************" << endl
<< "********************* 选择15——头删. ****************************" << endl
<< "********************* 选择16——逆置列表元素. ****************************" << endl
<< "******************** 选择17——清屏! ************************" << endl
<< "******************** 选择0——退出程序! ************************" << endl
<< "***********************************************************************" << endl
<< "***********************************************************************" << endl;
}
cout << "\n******************** 请输入你想要使用的循环链表功能的序号 ***************" << endl;
cout << "请输入你的选择:";
int userChoice(0);
cin >> userChoice;
if (userChoice == 0)
{
cout << "程序已退出,感谢您的使用!" << "\n" << endl;
break;
}
switch (userChoice)
{
case 1:
{ //插入元素
int pushLocate(0);
int pushElement;
cout << "请输入想添加元素的位置:";
cin >> pushLocate;
cout << "请输入想添加的元素:";
cin >> pushElement;
if (myCirLinkList.insertAt(pushLocate, pushElement))
{
cout << "数据" << pushElement << "插入成功!" << endl;
myCirLinkList.show();
}
else
cout << "插入的位置不合理,数据" << pushElement << "插入失败!" << endl;
break;
}
case 2:
{ //删除元素
int cancelElem(0); //要删除的元素
int cancelLocate(0); //要删除的位置
cout << "请输入想要删除的位置:";
cin >> cancelLocate;
if (myCirLinkList.removeAt(cancelLocate, cancelElem))
{
cout << "数据" << cancelElem << "删除成功!" << endl;
myCirLinkList.show();
}
else
cout << "删除的位置不合理或者链表中没有元素,数据删除失败!" << endl;
break;
}
case 3: //获取元素
{
int getLocate(0);
int getElem(0);
cout << "请输入想要获取的位置:";
cin >> getLocate;
if (myCirLinkList.getAt(getLocate, getElem))
{
cout << "获取的元素为:" << getElem << endl;
myCirLinkList.show();
}
else
{
cout << "获取的位置不合理或者链表中没有元素,获取数据失败!" << endl;
myCirLinkList.getLength();
}
break;
}
case 4:
{ //查找元素
int getLocate(0);
int findElem(0);
cout << "请输入你想要查找的元素的值:";
cin >> findElem;
if (myCirLinkList.locationElemAt(getLocate, findElem))
{
cout << "找到了元素" << findElem << ",其在表中的索引值为:" << getLocate << "\n" << endl;
myCirLinkList.show();
}
else
{
cout << "链表中不存在所需找的值!" << "\n" << endl;
myCirLinkList.show();
}
break;
}
case 5:
myCirLinkList.isEmpty();
break;
case 6:
{ //获取长度
myCirLinkList.getLength();
break;
}
case 7:
if (myCirLinkList.clear())
{
cout << "循环链表已被清空!" << endl;
myCirLinkList.show();
}
else
{
cout << "循环链表清空失败!" << endl;
myCirLinkList.show();
}
break;
case 8:
myCirLinkList.show();
break;
case 9:
{
cout << "你确定要销毁该单链表吗?(若销毁请输入输入(Y/y))";
char yesOrNo;
cin >> yesOrNo;
if ((yesOrNo == 'Y') || (yesOrNo == 'y'))
{
if (myCirLinkList.destroy())
{
cout << "单链表已被销毁." << "\n" << endl;
}
else
cout << "单链表销毁失败." << "\n" << endl;
}
break;
}
case 10:
{
int frontElem(0);
int getElem(0); //返回的是要获得哪一个元素的前驱的元素
cout << "输入你想要获得哪一个元素的前驱?(注意:不能获取第一个元素的):";
cin >> frontElem;
if (myCirLinkList.priorElemAt(frontElem, getElem))
{
cout << "数据元素" << frontElem << "的,前驱元素是:" << getElem << endl;
myCirLinkList.show();
}
else
cout << "获取前驱元素失败,不能获取第一个元素的前驱或者链表中没有你输入的元素!" << endl;
break;
}
case 11:
{
int rearElem(0);
int getElem(0);
cout << "输入你想要获得哪一个元素的后继?(注意:不能获取最后一个元素的):";
cin >> rearElem;
if (myCirLinkList.nextElemAt(rearElem, getElem))
{
cout << "数据元素" << rearElem << "的,后继元素是:" << getElem << endl;
myCirLinkList.show();
}
else
cout << "获取后继元素失败!不能获取最后一个元素的后继或者链表中没有你输入的元素!" << endl;
break;
}
case 12:
{
int pushElement = 0;
cout << "请输入想在尾部添加的元素:";
cin >> pushElement;
myCirLinkList.backInsertElemAt(pushElement);
myCirLinkList.show();
break;
}
case 13:
{
myCirLinkList.backRemoveElemAt();
myCirLinkList.show();
break;
}
case 14:
{
int pushElement = 0;
cout << "请输入想在头部添加的元素:";
cin >> pushElement;
myCirLinkList.headInsetElemAt(pushElement);
myCirLinkList.show();
break;
}
case 15:
{
myCirLinkList.headRemoveElemAt();
myCirLinkList.show();
break;
}
case 16:
{
myCirLinkList.reserve();
myCirLinkList.show();
break;
}
case 17:
system("cls");
cout << "屏幕已经清屏,可以重新输入!" << "\n" << endl;
break;
default:
cout << "输入的序号不正确,请重新输入!" << "\n" << endl;
}
}
delete[]myDatas;
myDatas = nullptr;
}
int main()
{
testMyCirLinkList();
system("pause");
return 0;
}
下面用C# 代码实现单循环链表:
namespace DateStructure
{
interface ISingleLoopList
{
void insertAt(int pos, int elem);
int removeAt(int pos);
int getAt(int pos);
int locationElemAt(int value);
int getLength();
bool isEmpty();
void clear();
void show();
void createTail(params int[] arrElem); //尾插法
}
class Node
{
public int m_Data { set; get; } = 0;
public Node m_Next { set; get; } = null;
public Node m_Prev { set; get; } = null;
}
class CirLinkList : ISingleLoopList
{
private Node m_Head;
private Node m_Tail; // 该指针始终指向链表中的最后一个
public CirLinkList()
{
m_Head = new Node();
Debug.Assert(m_Head != null, "头结点创建失败!");
m_Head.m_Next = m_Head;
m_Tail = m_Head;
}
public void createTail(params int[] arrElem) //尾插法
{
m_Head.m_Data = arrElem.Length;
for (int i=0; i!=arrElem.Length; ++i)
{
Node newNode = new Node();
Debug.Assert(newNode != null, "整表创建单循环链表失败!");
newNode.m_Data = arrElem[i];
m_Tail.m_Next = newNode;
newNode.m_Next = m_Head;
m_Tail = newNode;
}
}
public void insertAt(int pos, int elem)
{
if ((pos < 1) || (pos > ((m_Head.m_Data) + 1))) //i位置不合理时,返回false
{
WriteLine("插入元素失败,插入的位置不正确!");
return;
}
Node temp = m_Head;
int locitionPos = 1;
while (locitionPos < pos)
{
temp = temp.m_Next;
++locitionPos;
}
Node newNode = new Node();
Debug.Assert(newNode != null, "插入节点分配内存失败!");
newNode.m_Data = elem;
if(temp.m_Next == m_Head) // 如果插入的位置是尾部
{
temp.m_Next = newNode;
newNode.m_Next = m_Head;
m_Tail = newNode;
}
else
{
newNode.m_Next = temp.m_Next;
temp.m_Next = newNode;
}
++m_Head.m_Data;
}
public void clear()
{
m_Head.m_Data = 0;
m_Head.m_Next = m_Head;
m_Tail = m_Head;
}
public int getLength()
{
return m_Head.m_Data;
}
public bool isEmpty()
{
return m_Head.m_Data == 0;
}
public int getAt(int pos)
{
if (pos < 1 || m_Head.m_Data == 0 || pos > m_Head.m_Data)
{
WriteLine("获取元素的位置不正确,或者该该链表为空!");
return -1;
}
Node temp = m_Head;
int locitionPos = 1;
while (locitionPos < pos)
{
temp = temp.m_Next;
++locitionPos;
}
return temp.m_Next.m_Data;
}
public int removeAt(int pos)
{
if (pos < 1 || m_Head.m_Data == 0 || pos > m_Head.m_Data)
{
WriteLine("删除的位置不正确,或者该链表是个空表,没有元素可以删除!");
return -1;
}
Node temp = m_Head;
int locitionPos = 1;
while (locitionPos < pos)
{
temp = temp.m_Next;
++locitionPos;
}
var removeElem = temp.m_Next.m_Data;
if(temp.m_Next.m_Next == m_Head) // 如果删除的是表尾元素
{
temp.m_Next = m_Head;
m_Tail = m_Head;
}
else
{
temp.m_Next = temp.m_Next.m_Next;
}
--m_Head.m_Data;
return removeElem;
}
public int locationElemAt(int value)
{
if (m_Head.m_Data == 0)
{
WriteLine("该单向循环链表为空,无法进行查找!");
return -1;
}
Node temp = m_Head;
for (int i = 0; i != m_Head.m_Data; ++i)
{
temp = temp.m_Next;
if (temp.m_Data == value)
{
return i + 1;
}
}
return -1;
}
public void show()
{
if (m_Head.m_Data == 0)
{
WriteLine("该单向循环列表中没有数据,无法显示!");
return;
}
else
{
Node temp = m_Head;
Write("输出此时单向循环列表中所有的元素:");
for (int i = 0; i != m_Head.m_Data; ++i)
{
temp = temp.m_Next;
Write($"{temp.m_Data},");
}
WriteLine();
}
}
}
class Program
{
static void Main(string[] args)
{
CirLinkList myList = new CirLinkList();
const int arraySize = 5;
int[] myIntArray = new int[arraySize] { 0, 22, 33, 44, 55 };
myList.createTail(myIntArray);
myList.show();
WriteLine("请输入你想要查找的元素的值:");
int locationPosElem = Convert.ToInt32(Console.ReadLine());
int tempElem = myList.locationElemAt(locationPosElem);
if (0 <= tempElem)
{
WriteLine($"该元素的位置序号是:{tempElem}");
myList.show();
}
else
{
WriteLine("该元素不再链表中!");
}
WriteLine("请输入你想获取链表中哪一个位置的元素:");
int getArrElem = Convert.ToInt32(Console.ReadLine());
WriteLine($"该位置的元素是:{myList.getAt(getArrElem)}");
myList.show();
WriteLine("请输入你想删除哪一个位置的元素:");
int removePos = Convert.ToInt32(Console.ReadLine());
int temp = myList.removeAt(removePos);
if (temp >= 0)
{
WriteLine($"被删除的元素为:{temp}");
}
WriteLine($"\n获取当前链表的总个数:{myList.getLength()}");
myList.show();
WriteLine("请输入你想插入的元素值:");
int pushElem = Convert.ToInt32(Console.ReadLine());
WriteLine("请输入你想插入的位置:");
int pushLocition = Convert.ToInt32(Console.ReadLine());
myList.insertAt(pushLocition, pushElem);
WriteLine($"\n获取当前链表的总个数:{myList.getLength()}");
myList.show();
if (myList.isEmpty())
{
WriteLine("当前的链表为空!");
}
else
WriteLine("当前的链表非空!");
WriteLine($"\n获取当前链表的总个数:{myList.getLength()}");
myList.clear();
WriteLine($"获取当前链表的总个数:{myList.getLength()}");
}
}
}