相对于单链表,循环列表所不同的就是增加了一个指向尾节点的尾指针,且尾指针指向头节点,这样就使得这个链表可以一直不断地循环了。实现方法上,与单链表雷同,所需要做的改变:数据成员处增加一个指向尾节点的尾指针,在成员函数处注意移动尾指针,在循环处需要把原先p==NULL用来做判断条件的循环条件改变,用数据成员m_Length,来做判断(这一点有了数据成员m_Length后使得即使尾指针的next部分也不为NULL的情况下,用另一条件来做判断也很方便)。
以下就是实现代码:
节点部分的结构依然没变
#pragma once
template<typename T>
class Node
{
public:
T data;
Node *next;
};
类模板的实现部分
#pragma once
#include<iostream>
#include"Node.h";
using namespace std;
template<typename T>
class LinkList
{
public:
LinkList(T a[], int n); //有参构造函数 使用了头插法
~LinkList(); //析构函数
int Length(); //返回单链表的长度
T Get(int i); //按位查找,查找第i个节点的元素
int Locate(T x); //按值查找,查找链表中第一个值为x的元素,并返回序号
bool Insert(int i, T x); //插入元素,在第i个位置插入值x
bool Delete(int i); //删除节点,删除第i个节点
bool InsertHead(T x); //头插法插入节点
bool InsertTail(T x); //尾插法插入节点
void ListTraverse(); //遍历节点
void printrear() { cout << endl << "尾指针为:" << rear->data << endl; }
private:
Node<T> *first; //头指针
Node<T> *rear; //尾指针
int m_Length; //实际使用过程当中,添加多一个数据成员Length会更好
};
template<typename T>
LinkList<T>::LinkList(T a[], int n) //有参构造函数, 使用头插法(注意点:头插法是将元素放在头结点的后面)
{
first = new Node<T>; //空链表的初始化,同无参构造函数
first->next =first ;
for (int i = 0; i<n; i++)
{
Node<T> *s = new Node<T>;
s->data = a[i];
if (first->next==first)
{
first->next = s;
rear = s;
rear->next = first;
}
else
{
s->next = first->next;
first->next = s;
}
}
m_Length = n;
}
//template<typename T>
//LinkList::LinkList(T a[], int n) //同样是有参构造函数,但是使用的是尾插法
//{
//
// first = new Node<T>;
// first = rear;
// Node<T> *r = first; //将头指针赋值给变量r
// for (int i = 0; i < n; i++)
// {
// Node<T> *s = new Node<T>;
// s->data = a[i]; //创建新节点,赋值
// s->next = rear->next; //尾指针赋值
// rear->next = s; //上链,将尾指针
// rear = s;
// }
//}
template<typename T>
bool LinkList<T>::InsertHead(T x) //头插发插入数据
{
Node<T> *s = new Node<T>; //建立新节点
if (s == NULL) //判断新节点是否申请成功
{
return false;
}
s->data = x; //赋值
if (first->next==first)
{
first->next = s;
rear = s;
rear->next = first;
}
else
{
Node<T> *Temp = first->next; //建立指向头指针的临时变量
s->next = Temp; //上链
first->next = s;
}
m_Length++; //链表的长度加一
return true; //插入成功,返回true
}
template<typename T>
bool LinkList<T>::InsertTail(T x) //使用尾插法插入数据,使数据插入到最后一个
{
Node<T> *s = new Node<T>; //建立新节点
if (s == NULL) //判断新节点是否申请成功,若申请失败,则退出函数,不插入数据
{
return false;
}
s->data = x;
s->next = rear->next;
rear->next = s;
rear = s;
m_Length++;
return true; //返回true,插入成功
}
template<typename T>
LinkList<T>::~LinkList() //析构函数
{
int i = 0;
while (i<m_Length)
{
Node<T> *q = first; //遍历删除头指针指向的节点,将头指针暂存
first = first->next; //将头指针后移
delete q; //从链表中脱离出来的指针删除,释放内存
i++;
}
m_Length = 0;
}
template<typename T>
int LinkList<T>::Length() /*返回链表的长度的算法,实现思想:设定循环函数,将节点的指针从头指针开始依次后移,
后移一次便将计数器自加1,直至到尾节点的指针为空,此时结束循环,返回计数器*/
{
/*int num=0;
Node<T> *p = first->next;
while (p!= NULL)
{
p = p->next;
num++;
}*/
return m_Length; //添加数据成员length后,使得返回链表的长度函数更简单,代码更少
/*return num;*/
}
template<typename T>
T LinkList<T>::Get(int i) //按位查找,返回第i个节点的元素
{
Node<T> *p = first->next;
int count = 1;
if (i>m_Length)
{
cout << "无此节点(节点位置超出当前链表长度)" << endl;
}
else
{
while (count < i)
{
p = p->next;
count++;
}
return p->data;
}
}
template<typename T>
int LinkList<T>::Locate(T x) //按值查找,返回d第一个匹配值的序号
{
Node<T> *p = first;
int count = 0,i=0;
while (i<=m_Length)
{
if (p->data == x)
{
return count;
}
p = p->next;
count++;
i++;
}
return -1; //返回-1表示没找到
}
template<typename T>
bool LinkList<T>::Insert(int i, T x) //往链表中插入元素,i为要插入的位置,x为要插入的值
{
Node<T> *p = first;
int count = 0;
int num = i - 1,j=0;
if (j>=m_Length) //要插入的位置大于当前链表的长度,将会把数据插入到链表尾中 return false代表插入到尾部
{
Node<T> *s = new Node<T>;
s->data = x;
s->next = rear->next;
rear->next = s;
rear = s;
m_Length++;
return false;
}
else
{
while (count <num)
{
p = p->next;
count++;
}
Node<T> *s = new Node<T>;
s->data = x;
s->next = p->next;
p->next = s;
m_Length++;
return true; //return true代表插入到了目标位置
}
}
template<typename T>
void LinkList<T>::ListTraverse() //遍历函数
{
Node<T> *p = first->next;
int i = 0;
while (i<m_Length)
{
cout << p->data << ",";
p = p->next; //遍历的指针的后移,注意不能写成p++,因为这是节点
i++;
}
}
template<typename T>
bool LinkList<T>::Delete(int i)
{
Node<T> *p = first;
int count = 0;
if (i>m_Length)
{
return false; //删除目标的节点大于当前链表的长度(节点不存在),直接返回
}
else
{
while (count < i - 1)
{
p = p->next;
count++;
}
Node<T> *q;
q = p->next;
p->next = q->next;
delete q;
m_Length--;
return true;
}
}
实例调用:
#include<iostream>
#include"CircuList.h"
using namespace std;
int main()
{
int a[5] = { 1,2,3,4,5 };
LinkList<int> MyList(a, 5);
cout <<"链表长度为:"<< MyList.Length() << endl; //链表长度函数调用成功
cout << "第5个节点的元素为:" << MyList.Get(5) << endl; //输入有效长度的数据可成功调用,但是,若输入不合法的数据将出错,throw的语句未完成
if (MyList.Locate(5) != 0)
{
cout << "元素5所在的位置为:" << MyList.Locate(5) << endl;
} //输入合法的数据,测试成功
else if (MyList.Locate(5) == 0) {
cout << "输入数据不合法,输入的节点位置超过链表超度" << endl;
}
if (MyList.Insert(3, 2))
{
cout << "插入元素成功!" << endl;
}//插入元素测试成功
else
{
cout << "插入元素失败!" << endl;
}
MyList.ListTraverse();
if (MyList.Delete(3))
{
cout << "删除节点成功" << endl;
}
else
{
cout << "删除节点失败" << endl;
}
MyList.ListTraverse(); //单链表的遍历成功
cout << endl;
MyList.InsertHead(7); //调用头插法
cout << "调用头插法成功!";
MyList.ListTraverse();
MyList.InsertTail(4); //调用尾插法
cout << endl;
cout << "调用尾插法成功!";
MyList.ListTraverse();
return 0;
}
可见,该结果与上一单链表的结果相同(输入样本数据相同)
单链表特点:
- 查找开始节点和尾节点都变得特别方便(时间复杂度下降了,为o(1))
- 可以从任一节点出发遍历整个链表(注意:可从任一节点出发不代表可以实现随机储存)
- 容易造成死循环(缺点),需要格外注意循环的条件
以上!如果日后遇到其他需要注意的(新奇的,好玩的),会更新