数据结构之——单向循环链表完备版(C++实现)

单向循环列表

⚪概念

顾名思义,单向循环链表就是在单向链表的基础上实现了循环,即将最后一个节点再指向第一个节点。
单向循环列表

⚪难点

由于单向循环链表是对于单向链表的一个改进,因此只需要在单向链表代码的基础上进行部分改动。而单向循环链表和单向链表的区别就在于多了一根尾节点指向头节点的线,所以只有添加和删除操作的头尾部分的代码进行改进。

①添加

1、头

当添加头节点时,除了将添加节点的next指针指向原先节点之外要考虑到头尾之间的线,因此还要获得尾节点,再将尾节点的next指针指向新添加的头节点


	if (index == 0)
	{
		Node<E>* New_first = new Node<E>(element, first);		//单独拿出头节点,以防拿最后一个节点的时候拿到的是尾节点的前一个结点

		//拿到最后一个位置的节点
		Node<E>* last = find_node(size - 1);
		last->next = New_first;
		first = New_first;		//最后再拿这个新节点覆盖头节点
	}

第一个节点
而有一种特殊情况就是当添加的头节点是第一个节点那么在获得尾节点时获得的就是自己本身。
那么就要把Node<E>* last = find_node(size - 1);这句代码改成Node<E>* last = (size == 0) ? New_first : find_node(size - 1);
因此最后的代码是:

if (index == 0)
	{
		Node<E>* New_first = new Node<E>(element, first);		//单独拿出头节点,以防拿最后一个节点的时候拿到的是尾节点的前一个结点

		//拿到最后一个位置的节点
		Node<E>* last = (size == 0) ? New_first : find_node(size - 1);
		last->next = New_first;
		first = New_first;		//最后再拿这个新节点覆盖头节点
	}
2、尾

在处理头部的操作和尾部的操作无过多区别,没有头指针的改变,更为简单,便不过多赘述

Node<E>* prev = find_node(index - 1);		//获得前一个节点
		prev->next = new Node<E>(element, prev->next);		//由于从右向左运算 首先让新元素的下一个指向原有位置使其成为下一个元素 再让前节点指向新元素
3、总代码
template<class E>
void Cirle_LinkList<E>::add(int index, E element)		//在指定位置添加元素
{
	if (index == 0)
	{
		Node<E>* New_first = new Node<E>(element, first);		//单独拿出头节点,以防拿最后一个节点的时候拿到的是尾节点的前一个结点

		//拿到最后一个位置的节点
		Node<E>* last = (size == 0) ? New_first : find_node(size - 1);
		last->next = New_first;
		first = New_first;		//最后再拿这个新节点覆盖头节点
	}
	else
	{
		Node<E>* prev = find_node(index - 1);		//获得前一个节点
		prev->next = new Node<E>(element, prev->next);		//由于从右向左运算 首先让新元素的下一个指向原有位置使其成为下一个元素 再让前节点指向新元素
	}
	size++;
}

②删除

1、头

删除头节点在单向链表中将first指针指向头节点的下一个节点之外,还要将尾节点的next指针指向新的头节点,最后再delete原来的头节点即可。
删除头节点

p = first;
			Node<E>* last = find_node(size - 1);
			first = first->next;
			last->next = first;

而此处的删除也有一个特殊情况,因为上方代码存在size-1,当size=1,即本来只有一个元素,此处代码就无法通用了,因此还要增加一个size=1时的讨论。
只有一个元素
那么此处只要将first=NULL再delete这个元素即可

if (size == 1)
		{
			first = NULL;
		}
		else
		{
			p = first;
			Node<E>* last = find_node(size - 1);
			first = first->next;
			last->next = first;
		}
2、尾

在处理头部的操作和尾部的操作无过多区别,没有头指针的改变,更为简单,便不过多赘述

Node<E>* prev = find_node(index - 1);		//获得前一个节点
		p = prev->next;		//获得index节点
		prev->next = prev->next->next;		//前一个节点指向index后面这个节点
3、总代码
template<class E>
E Cirle_LinkList<E>::remove(int index)		//移除某个元素,返回被移除的元素  要讨论是否为头节点
{

	Node<E>* p = NULL;		//创建一个节点,用来返回被移除的元素

	if (index == 0)		//若为头节点直接指向下个节点
	{
		if (size == 1)
		{
			first = NULL;
		}
		else
		{
			p = first;
			Node<E>* last = find_node(size - 1);
			first = first->next;
			last->next = first;
		}
	}
	else		//若不是头节点,让上一个节点指向这个节点的下一个节点
	{
		Node<E>* prev = find_node(index - 1);		//获得前一个节点
		p = prev->next;		//获得index节点
		prev->next = prev->next->next;		//前一个节点指向index后面这个节点
	}
	size--;
	return	p->element;
}

⚪总代码

Cir_Single_List.h

#pragma once
#pragma once
#include <iostream>
using namespace std;

template<class E>
class Node
{
public:
	E element;		//元素值

	Node<E>* next;		//指向下一个节点

	Node() {};		//不含参数的构造函数

	Node(E element, Node<E>* next)		//含参数的构造函数
	{
		this->element = element;
		this->next = next;
	}

	~Node() {};		//析构函数
};


template<class E>
class Cirle_LinkList
{

private:
	int size;		//数据大小
	Node<E>* first;		//头节点

	static const int ELEMENT_NOT_FOUND = -1;		//代表未找到此值

public:

	void clear();						//清除所有节点
	void add(E element);					//在尾节点添加元素
	void add(int index, E element);		//在指定位置添加元素	
	int cacul_size();					//计算size的大小
	int indexOf(E element);				//查找元素element是否存在(只需要第一个出现的元素)
	bool isEmpty();						//看链表是否为空
	bool contains(E element);	     	//是否包含某个元素
	E get(int index);					//获得某个index元素的值
	E set(int index, E element);			//在index位置设置元素,返回原来的element
	E remove(int index);					//移除某个元素,返回被移除的元素  要讨论是否为头节点
	void print();						//输出
	Node<E>* find_node(int index);		//找出某个节点

	~CIrle_LinkList() {};
};

Cir_Single_List.cpp

#include <iostream>
#include "Circle_Link_list.h"


template<class E>
void Cirle_LinkList<E>::clear()		//清除所有节点
{
	size = 0;
	Node<E>* p;
	while (this->first)
	{
		p = first->next;
		free(first);
		first = p;
	}
	cout << "已被清空" << endl;
}

template<class E>
void  Cirle_LinkList<E>::add(E element)		//在尾节点添加元素
{
	add(size, element);
}

template<class E>
void Cirle_LinkList<E>::add(int index, E element)		//在指定位置添加元素
{
	if (index == 0)
	{
		Node<E>* New_first = new Node<E>(element, first);		//单独拿出头节点,以防拿最后一个节点的时候拿到的是尾节点的前一个结点

		//拿到最后一个位置的节点
		Node<E>* last = (size == 0) ? New_first : find_node(size - 1);
		last->next = New_first;
		first = New_first;		//最后再拿这个新节点覆盖头节点
	}
	else
	{
		Node<E>* prev = find_node(index - 1);		//获得前一个节点
		prev->next = new Node<E>(element, prev->next);		//由于从右向左运算 首先让新元素的下一个指向原有位置使其成为下一个元素 再让前节点指向新元素
	}
	size++;
}

template<class E>
int Cirle_LinkList<E>::cacul_size() //计算size的大小
{
	return size;
}

template<class E>
int Cirle_LinkList<E>::indexOf(E element)		//查找元素element是否存在(只需要第一个出现的元素)
{
	if (element == NULL)		//考虑element为空的情况
	{
		Node<E>* node = first;
		for (int i = 0; i < size; i++)
		{
			if (node->element == NULL)
			{
				return i;
			}
			node = node->next;
		}
	}
	else
	{
		Node<E>* node = first;
		for (int i = 0; i < size; i++)
		{
			if (element == node->element)
			{
				return i;
			}
			node = node->next;
		}
	}
}

template<class E>
bool Cirle_LinkList<E>::isEmpty()		//看链表是否为空
{
	return size == 0;
}

template<class E>
bool Cirle_LinkList<E>::contains(E element)
{
	return indexOf(element) != -ELEMENT_NOT_FOUND;
}

template<class E>
E Cirle_LinkList<E>::get(int index)		//获得某个index元素的值
{
	return find_node(index).element;		//可以直接利用find_node()函数
}

template<class E>
E Cirle_LinkList<E>::set(int index, E element)		//在index位置设置元素,返回原来的element
{
	Node<E>* node = find_node(index);
	E old = node->element;		//用来保存原来的element
	node->element = element;
	return old;
}

template<class E>
E Cirle_LinkList<E>::remove(int index)		//移除某个元素,返回被移除的元素  要讨论是否为头节点
{

	Node<E>* p = NULL;		//创建一个节点,用来返回被移除的元素

	if (index == 0)		//若为头节点直接指向下个节点
	{
		if (size == 1)
		{
			first = NULL;
		}
		else
		{
			p = first;
			Node<E>* last = find_node(size - 1);
			first = first->next;
			last->next = first;
		}
	}
	else		//若不是头节点,让上一个节点指向这个节点的下一个节点
	{
		Node<E>* prev = find_node(index - 1);		//获得前一个节点
		p = prev->next;		//获得index节点
		prev->next = prev->next->next;		//前一个节点指向index后面这个节点
	}
	size--;
	return	p->element;
}

template<class E>
void Cirle_LinkList<E>::print()
{
	Node <E>* p = first;
	int n = size;
	while (n--)
	{
		cout << p->element << "->";
		p = p->next;
	}
	cout << endl;
}

template<class E>
Node<E>* Cirle_LinkList<E>::find_node(int index)		//找出某个节点
{
	if (index < 0 || index >= size)		//对于index都要进行规范性检测
	{
		cout << "index不规范" << endl;
		throw "index不符合规范";
	}
	Node<E>* node = first;
	for (int i = 0; i < index; i++)
	{
		node = node->next;
	}
	return node;
}

main.cpp

#include "Circle_Link_list.h"
#include <iostream>

int main()
{
    Cirle_LinkList<int> clist;
    clist.add(11);
    clist.add(22);
    clist.add(33);
    clist.add(44);
    clist.print();
    clist.add(0,55);
    clist.add(2,66);
    clist.print();
    clist.add(clist.cacul_size(),77);
    clist.remove(0);
    clist.remove(2);
    clist.print();
    return 0;
}

附:
1.本博客是由学习小码哥视频所总结文章
2.代码都通过合格检测,请放心食用~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GXM.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值