Data structure circular linked list and its basic operations

1. Introduction

In the singly linked list, we end after traversing the nodes. In order to make the table processing more convenient and flexible, we hopeStarting from any node, you can find other nodes in the linked list, so we introduce a circular linked list.

2. Definition of circular linked list

Changing the pointer end of the terminal node in the singly linked list from a null pointer to a head node makes the whole singly linked list form a ring.Singly linked list head to tailcalled a circular linked list.

A simple understanding is to form a closed loop and find the nodes you need in the loop.

3. Comparison of single linked list and circular linked list

3.1 Graphic comparison

Singly linked list:
Please add a picture description
Circular linked list:
Please add a picture description

3.2 Code comparison

Single list:

typedef struct Node{
    
     //定义单链表结点类型
	int data; //数据域,可以是别的各种数据类型
	struct Node *next; //指针域
}LNode, *LinkList;

Circular linked list:

typedef int ELEMTYPE;
typedef struct Clist
{
    
    
	ELEMTYPE data; //数据域   存放数据
	struct Clist* next;
//指针域存放下一个节点的地址(尾结点的next保存头结点的地址)
}Clist, * PClist;

4. Operation of circular linked list

The circular linked list is a structure extended from the single linked list, so many operations are the same as the single linked list, such as finding the length, finding an element, and obtaining an element. Here we perform theinitialize, create, insert, delete, destroya series of operations.

4.1 Initialization of circular linked list

The initialization comparison of single linked list and circular linked list is as follows:

Single list circular linked list
Data fields are not processed Data fields are not processed
The next field is assigned a value of NULL The next field is assigned the address of the head node itself
code show as below:
void InitClist(struct Clist* plist)
{
    
    
	//assert

	//plist->data; // 数据域不处理
	plist->next = plist;
}

4.2 Establishment of circular linked list

The establishment of circular linked list is based on single linked list, so there are two ways of head insertion and tail insertion.

4.2.1 Head insertion method to establish a circular linked list

The head interpolation method to build a circular linked list is mainly these two lines of code:

pnewnode->next=plist->next;
plist->next=pnewnode;

Here we need to think about whether it is applicable when the linked list is empty: Here we clearly tell you that no matter whether it is an empty linked list, these two lines of code can be used. Let me show you with a schematic diagram below; if it is not an empty link: it is an empty link: the code is
as
insert image description here
follows
Please add a picture description
:

bool Insert_head(PClist plist, ELEMTYPE val)
{
    
    
	//assert
	//1.购买新节点
	struct Clist* pnewnode = (struct Clist*)malloc(1 * sizeof(struct Clist));
	assert(pnewnode != NULL);
	pnewnode->data = val;//购买的新节点  处理完毕

	//2.找到插入位置  (头插  不用找)
	//3.插入
	pnewnode->next = plist->next;
	plist->next = pnewnode;

	return true;
}

4.2.2 Tail insertion method to establish a circular linked list

The main code of the tail insertion method to establish a circular linked list is as follows:

for(p=plist;p->next!=plist;p=p->next);

Please add a picture description
code show as below:

bool Insert_tail(PClist plist, ELEMTYPE val)
{
    
    
	//assert
	//1.购买新节点
	struct Clist* pnewnode = (struct Clist*)malloc(1 * sizeof(struct Clist));
	assert(pnewnode != NULL);
	pnewnode->data = val;//购买的新节点  处理完毕


	//2.找到插入位置(通过带前驱的for循环)
	struct Clist* p = plist;
	for (p; p->next != plist; p = p->next);
	//此时 for循环执行结束   p指向尾结点

	//3.插入
	pnewnode->next = p->next;
	p->next = pnewnode;

	return true;
}

4.3 Insert operation of circular linked list

Different from the insertion of a singly linked list, when inserting a circular singly linked list, it can only be inserted behind the head node. From the initial structure, if you want to complete the insert operation, you must first find the previous node where you want to insert, and then modify the corresponding pointer. Done.
Please add a picture description

bool Insert_pos(PClist plist, int pos, ELEMTYPE val)
{
    
    
	assert(plist != NULL);
	assert(pos >= 0 && pos <= Get_length(plist));


	//1.购买新节点
	struct Clist* pnewnode = (struct Clist*)malloc(1 * sizeof(struct Clist));
	assert(pnewnode != NULL);
	pnewnode->data = val;//购买的新节点  处理完毕


	//2.找到插入位置(通过带前驱的for循环)
	struct Clist* p = plist;
	for (int i = 0; i < pos; i++)
	{
    
    
		p = p->next;
	}
	//此时 for循环执行结束   p指向待插入的合适位置

	//3.插入
	pnewnode->next = p->next;
	p->next = pnewnode;

	return true;
}

4.4 Delete operation of circular linked list

The deletion operation of the circular linked list is equivalent to the deletion operation of the singly linked list, which is mainly divided into the following three steps:

  1. The pointer p points to the node to be deleted;
  2. The pointer q points to the previous node of the node to be deleted;
  3. Pointing across.
void Delete(list **pNode,int place)  //删除操作
{
    
    
	list *temp,*target;
	int i;
	temp=*pNode;
	if(temp==NULL)				//首先判断链表是否为空
	{
    
    
		printf("这是一个空指针 无法删除\n");
		return;
	}
	if(place==1)		//如果删除的是头节点	
	{
    
    				//应当特殊处理,找到尾节点,使尾节点的next指向头节点的下一个节点
                    // rear->next=(*head)->next;然后让新节点作为头节点,释放原来的头节点
		for(target=*pNode;target->next!=*pNode;target=target->next);
		temp=*pNode;
		
		*pNode=(*pNode)->next;
		target->next=*pNode;
		free(temp);
	}
	else //删除其他节点
	{
    
    		//首先找出尾节点
		for(i=1,target=*pNode;target->next!=*pNode&&i!=place-1;target=target->next,i++); 
		if(target->next==*pNode)		//判断要删除的位置是否大于链表长度,若大于链表长度,        
                                        //特殊处理直接删除尾节点
		{
    
    
                //找出尾节的前一个节点
			for(target=*pNode;target->next->next!=*pNode;target=target->next);
			temp=target->next;	 //	尾节点的前一个节点直接指向头节点  释放原来的尾节点									
			target->next=*pNode;
			printf("数字太大删除尾巴\n");
			free(temp);
		}
		else
		{
    
    
			temp=target->next;//  删除普通节点  找到要删除节点的前一个节点target,
                                //使target指向要删除节点的下一个节点  转存删除节点地址
			target->next=temp->next;	//  然后释放这个节点
			free(temp);
		}
	}
}

4.5 Destruction of circular linked list

There are two ways to destroy a circular linked list:

4.5.1 Unlimited header deletion

void Destroy1(PClist plist)
{
    
    
	//assert
	while (plist->next != plist)
	{
    
    
		struct Clist* p = plist->next;
		plist->next = p->next;
		free(p);
	}

	plist->next = plist;
}

4.5.2 Two pointer variables

void Destroy2(PClist plist)
{
    
    
	//assert
	struct Clist* p = plist->next;
	struct Clist* q = NULL;

	plist->next = plist;

	while (p != plist)
	{
    
    
		q = p->next;
		free(p);
		p = q;
	}
}

4.6 Other operations

The circular linked list also has a series of operations such as finding value, judging empty, finding the length of the linked list, clearing, etc. These operations are basically the same as those of the single linked list, and will not be described in detail here, and will be written in the complete code later.

5. Summary

The circular linked list is a little more complicated than the singly linked list. The main idea is similar to that of the singly linked list, except that the first bit of the linked list is connected, but it is precisely because of the connection between the end and the end that it is convenient for us to start from any node. Find other nodes in the linked list.

6. All codes

#include <stdio.h>
#include <malloc.h>
#include <assert.h>
//循环链表里边很少出现NULL, nullptr

//初始化
void InitClist(struct Clist* plist)
{
    
    
	//assert

	//plist->data; // 数据域不处理
	plist->next = plist;
}

//头插
bool Insert_head(PClist plist, ELEMTYPE val)
{
    
    
	//assert
	//1.购买新节点
	struct Clist* pnewnode = (struct Clist*)malloc(1 * sizeof(struct Clist));
	assert(pnewnode != NULL);
	pnewnode->data = val;//购买的新节点  处理完毕

	//2.找到插入位置  (头插  不用找)

	//3.插入
	pnewnode->next = plist->next;
	plist->next = pnewnode;

	return true;
}

//尾插
bool Insert_tail(PClist plist, ELEMTYPE val)
{
    
    
	//assert
	//1.购买新节点
	struct Clist* pnewnode = (struct Clist*)malloc(1 * sizeof(struct Clist));
	assert(pnewnode != NULL);
	pnewnode->data = val;//购买的新节点  处理完毕


	//2.找到插入位置(通过带前驱的for循环)
	struct Clist* p = plist;
	for (p; p->next != plist; p = p->next);
	//此时 for循环执行结束   p指向尾结点

	//3.插入
	pnewnode->next = p->next;
	p->next = pnewnode;

	return true;
}

//按位置插
bool Insert_pos(PClist plist, int pos, ELEMTYPE val)
{
    
    
	assert(plist != NULL);
	assert(pos >= 0 && pos <= Get_length(plist));


	//1.购买新节点
	struct Clist* pnewnode = (struct Clist*)malloc(1 * sizeof(struct Clist));
	assert(pnewnode != NULL);
	pnewnode->data = val;//购买的新节点  处理完毕


	//2.找到插入位置(通过带前驱的for循环)
	struct Clist* p = plist;
	for (int i = 0; i < pos; i++)
	{
    
    
		p = p->next;
	}
	//此时 for循环执行结束   p指向待插入的合适位置

	//3.插入
	pnewnode->next = p->next;
	p->next = pnewnode;

	return true;
}

//头删
bool Del_head(PClist plist)
{
    
    
	//assert
	if (IsEmpty(plist))//不空  则至少存在一个有效值
	{
    
    
		return false;
	}

	//1.指针p指向待删除节点
	struct Clist* p = plist->next;

	//2.指针q指向待删除节点的前一个节点
	//q 就是 头结点  这里就不再额外处理

	//3.跨越指向
	plist->next = p->next;
	free(p);

	return true;

}

//尾删
bool Del_tail(PClist plist)
{
    
    
	//assert
	if (IsEmpty(plist))//不空  则至少存在一个有效值
	{
    
    
		return false;
	}

	//1.指针p指向待删除节点(尾删的话,这里指向尾结点)
	struct Clist* p = plist;
	for (p; p->next != plist; p = p->next);
	//此时 for指向结束  代表着p指向尾结点

	//2.指针q指向倒数第二个节点
	struct Clist* q = plist;
	for (q; q->next != p; q = q->next);
	//此时 for指向结束  代表着q指向p的上一个节点

	//3.跨越指向
	q->next = p->next;
	free(p);

	return true;
}

//按位置删
bool Del_pos(PClist plist, int pos)
{
    
    
	assert(plist != NULL);
	assert(pos >= 0 && pos < Get_length(plist));

	if (IsEmpty(plist))
	{
    
    
		return false;
	}

	//1.指针p指向待删除节点
	//2.指针q指向待删除节点的上一个节点
	//这里采用第二种方案找pq,先找q再找p
	struct Clist* q = plist;
	for (int i = 0; i < pos; i++)
	{
    
    
		q = q->next;
	}
	struct Clist* p = q->next;

	//3.跨越指向
	q->next = p->next;
	free(p);

	return true;


}

//按值删除
bool Del_val(PClist plist, ELEMTYPE val)
{
    
    
	//assert
	struct Clist* p = Search(plist, val);
	if (p == NULL)
	{
    
    
		return false;
	}

	struct Clist* q = plist;
	for (q; q->next != p; q = q->next);

	q->next = p->next;
	free(p);

	return true;


}

//查找(如果查找到,需要返回找到的这个节点的地址)
struct Clist* Search(struct Clist* plist, ELEMTYPE val)
{
    
    
	//assert
	for (struct Clist* p = plist->next; p != plist; p = p->next)
	{
    
    
		if (p->data == val)
		{
    
    
			return p;
		}
	}

	return NULL;
}

//判空
bool IsEmpty(PClist plist)
{
    
    
	//assert
	return plist->next == plist;
}
//判满(循环链表不需要这个操作)

//获取长度
/*指针p从头结点的下一个节点开始向后跑,如果p再次遇到了头结点,
证明p走了一圈回来了,这是有效节点肯定已经遍历结束*/
int Get_length(PClist plist)
{
    
    
	//不带前驱的for循环 跑一遍就好
	int count = 0;

	for (struct Clist* p = plist->next; p != plist; p = p->next)
	{
    
    
		count++;
	}

	return count;
}

//清空
void Clear(PClist plist)
{
    
    
	//assert
	Destroy1(plist);
}

//销毁1(无限头删)
void Destroy1(PClist plist)
{
    
    
	//assert
	while (plist->next != plist)
	{
    
    
		struct Clist* p = plist->next;
		plist->next = p->next;
		free(p);
	}

	plist->next = plist;
}

//销毁2(要用到两个指针变量)
void Destroy2(PClist plist)
{
    
    
	//assert
	struct Clist* p = plist->next;
	struct Clist* q = NULL;

	plist->next = plist;

	while (p != plist)
	{
    
    
		q = p->next;
		free(p);
		p = q;
	}
}

//打印
void Show(struct Clist* plist)
{
    
    
	//assert
	for (struct Clist* p = plist->next; p != plist; p = p->next)
	{
    
    
		printf("%d ", p->data);
	}
	printf("\n");
}

(A Xiaobai, please correct me if I am wrong)

Guess you like

Origin blog.csdn.net/weixin_56935264/article/details/123592599