循环链表

单向循环链表的定义

循环链表与单向链表的结构比较类似,不同的地方在于其最后一个节点的指针域并不指向NULL,而是指向了首节点first,这样就构成了一个循环,其结构如下:
在这里插入图片描述
从上面也可以看出,其和单向链表的区别就在于最后一个节点的指针域的值不同,并且其还有一个节点作为表头节点,这个节点是的数据是空的。所以,对于单项循环链表的描述和单向链表是一致的,不过在插入等操作上会有不同,下面是其描述代码:

template<class T>//声明链表类
class Link;

template<class T>//创建结点类
class Node{
	friend class Link<T>;
	private:
		Node<T>* next;//指针域
		T data;//数据域
		Node(T newdata){data=newdata;}//构造方法1
		Node(){}//这个构造方法用来创建第一个空节点,即表头节点,这是
}

template<class T>//实现链表类
class Link{
	public:
		Link();//构造方法
		Insert(const int k,const T& x);//向k处插入数据x,k最小值为1
		void Delete(T& x);//删除节点x
		void Browser();//浏览链表中的所有数据
		int Length();//获取链表长度
		void Reserve();//反转链表
	private:
		Node<T>* first=NULL;
}
构造方法的实现

与单向链表的构造方法不同,因为单向链表可以不需要表头,所以其构造方法可以什么都不做;但是循环单向链表必须要有表头,所以在其构造方法中,必须创建一个表头节点,这也是两者在构造方法上的不同。
具体的代码实现如下:

template<class T>
Link<T>::Link(){
    Node<T>* head = new Node<T>(); //创建表头
    first=head;  //让first指向表头
    head->next=first; //让表头指向first
}

如果用上述构造方法创建一个链表,那么其初始的结构如下所示:
在这里插入图片描述
上述结构也表明链表是一个空链表,但是要注意,data域中没有数据。

插入方法的实现

与单向链表的插入不同,循环单链表插入时只能往表头节点的后面插入,由初始结构可知,想完成插入操作,必须先找到要插入位置的前一个节点,然后修改相应指针即可完成操作。图解如下:
在这里插入图片描述
在这里插入图片描述
下面是代码实现示例:

template<class T>
void Link<T>::Insert(const int k,const T& x){
    if(k<1 || k>Length()+2){  //插入位置非法
        cerr<<"the value of k is too big"<<endl;
        throw this;
    }
    //满足条件,执行插入操作
    Node<T>* pt = first;
    for(int i=1;i<k;i++){ //将pt移动至插入位置的前一个元素
        pt = pt->next;
    }
    Node<T>* temp = new Node<T>(x); //封装要插入的数据
    Node<T>* ptn = pt->next; //保存上一个结点的下一个结点信息
    pt->next=temp;  //让上一个的下一个结点改为要插入的节点
    temp->next=ptn; //让插入的节点的下一个结点为其上一个结点原来的恶下一个结点
}
获取循环链表长度的方法
template<class T>
int Link<T>::Length(){
    int length=0;
    Node<T>* p = first;
    while(p->next!=first){ //如果其下一个结点不是first,长度+1
        length++;
        p=p->next;
    }
    return length;
}

浏览链表数据方法的实现
template<class T>
void Link<T>::Browser(){
    Node<T>* p = first->next; //指向第一个元素
    while(p!=first){  //如果第一个元素有数据,输出其,然后移动p指针
        cout<<p->data;
        p = p->next;
        if(p!=first){
            cout<<" -> ";//如果p不是最后一个元素,添加“->”符号
        }
    }
    cout<<endl;
}
删除操作的实现

以下面一个循环链表为例,删除“2”节点的操作:
在这里插入图片描述
在这里插入图片描述

template<class T>
void Link<T>::Delete(const T& x){
	Node<T>* pr=first;
	Node<T>* middle=NULL;
	Node<T>* right=NULL;
	while(pr->next!=first){
		middle=pr->next;//让当前结点为pr的下一个
		if(middle->data==x){ //找到,删除
			right = middle->next;
			pr->next=right;
			delete middle;
			middle = NULL;
			break;
		}
		pr=pr->next;
	}
}
对循环链表制作简易迭代器,代码如下:
//获取下一个值
template<class T>
class Iterator{
    public:
        T Next();
        Iterator(Link<T> x);
    private:
        Node<T>* p=NULL;
        Node<T>* temp=NULL;
};

//构造方法
template<class T>
Iterator<T>::Iterator(Link<T> x){
    p = x.getFirst();
    temp=x.getFirst();
}

//Next方法
template<class T>
T Iterator<T>::Next(){
    if(p==temp){
        p=p->next;
    }
    T data = p->data;
    p = p->next;
    return data;
}

注: 需要在最前面声明Iterator类,然后再将其声明为Node类的友元类。

翻转循环链表的实现

反转循环链表和反转单向链表很相似,可以先将循环链表从表头节点的下一个结点处断开,这样循环链表就成为了单向链表,然后运用反转单链表的方式即可,最后设置其表头节点的下一个结点为最后一个节点即q

template<class T>
void Link<T>::Reserve(){
	Node<T>* p = first->next;//指向表头节点的下一个节点
	Node<T>* q = first; //即将q节点设为first,这样q会指向最后一个节点,即表头节点
	while(p!=first){
		Node<T>* temp = q;
		q = p;
		p = p->next;
		q->next = temp;
	}
	first->next = q;//让表头结点的下一个结点为最后一个节点
}
以上所有方法的测试程序
int main(){
    //创建一个构造函数
    Link<int> Link1 = Link<int>();
    //创建两个变量,用于数据的插入,其中k表示插入的位置,而x表示插入的值
    int k=1;
    int x=5;
    Link1.Insert(k,x); //插入数据
    Link1.Browser(); //浏览数据
    int length = Link1.Length(); //获取当前长度
    k = 2;x=12;
    Link1.Insert(k,x);
    k = 3;x=15;
    Link1.Insert(k,x);
    k = 2;x=17;
    Link1.Insert(k,x);
    Link1.Browser();
    cout<<"the Length is "<<Link1.Length()<<endl;
    //下一轮测试开始
    Link1.Delete(15);
    Link1.Browser();
    cout<<"Length is "<<length<<endl;
    //测试Next方法
    Iterator<int>* It1 = new Iterator<int>(Link1);
    int data = It1->Next();
    cout<<"the data is "<<data<<endl;
    data = It1->Next();
    cout<<"the data is "<<data<<endl;
    data = It1->Next();
    cout<<"the data is "<<data<<endl;
    data = It1->Next();
    cout<<"the data is "<<data<<endl;
    data = It1->Next();
    cout<<"the data is "<<data<<endl;
    data = It1->Next();
    cout<<"the data is "<<data<<endl;
    data = It1->Next();
    cout<<"the data is "<<data<<endl;
    //测试Reverse方法
    Link1.Reserve();
    Link1.Browser();
    data = It1->Next();
    cout<<"the data is "<<data<<endl;
    data = It1->Next();
    cout<<"the data is "<<data<<endl;
    data = It1->Next();
    cout<<"the data is "<<data<<endl;
    data = It1->Next();
    cout<<"the data is "<<data<<endl;
    data = It1->Next();
    cout<<"the data is "<<data<<endl;
    data = It1->Next();
    cout<<"the data is "<<data<<endl;
    cout<<"Execute Successful!";
    return 0;
}
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值