Circular linked list and doubly linked list

I. Introduction

The sequential storage structure of the linear table (for example: array) , the storage space is continuous , so we do not have to worry about the logical relationship between the elements. The biggest advantage of the linear table is that it can quickly access elements in any position in the table.
The disadvantage of linear table sequential storage is that a large number of elements need to be moved during insertion and deletion , and the time complexity is O(n). Demand and continuous space, which is prone to space debris . We learn the singly linked list of the chain storage structure. The singly linked list can solve these problems of the array very well. However, to access an element in a certain position of the singly linked list, it is necessary to traverse the entire singly linked list, and each time it is necessary to start from the beginning in one direction. traverse. To solve these problems we refer to circular linked list and doubly linked list.

2. Circular linked list

Circular linked list: Point the pointer field of the last node in the singly linked list to the head node (a linked list with a head node) or the first node (a linked list without a head node), so that the entire singly linked list forms an end-to-end ring.

An empty list with a head node in a circular linked list:
insert image description here

Non-empty list:
insert image description here
When judging whether the singly linked list is the last node, it is only necessary to judge whether p->next is NULL, but the circular linked list needs to judge whether p-> is equal to the head node. If it is equal, it means that it is the last node. Otherwise not.

When we often perform additional check operations at the end of the linked list, we can use a circular linked list with a tail pointer, and replace the head pointer with a tail pointer to point to the last node of the linked list.
insert image description here

Circular linked list to solve the Joseph ring problem

Joseph ring problem: that is, there are n people in a circle, and each person is numbered 1-n, starting from the kth person to report the number, the person with the number m is out of the circle, and the remaining people continue to repeat this process until everyone is out. circle, output the order of the circle.

There are two major problems with Joseph rings:

  1. How to repeat a loop
  2. If the song judges the end of the loop without causing an infinite loop

The first problem is solved perfectly with a circular circular linked list. Those who come out of the circle only need to delete this node.
There are three ways to solve the second problem

  • The circular linked list with the head node can be realized by judging whether there is only one head node left, but each cycle needs to judge whether the node is the head node and then skip the head node. This obviously does not reflect the advantages of the chain storage structure being discontinuous and deleting when it is not needed.
  • If the head node is not used, only the loop judgment condition is p->next != null so that the last remaining node will cause an infinite loop. Let's change the way of thinking. The last remaining node must be the last out of the circle. Since it is also possible for us to go out of the circle after the loop is over, as long as the next of the last node is itself, it means that there is only one node left. At this time, the loop is ended, and finally the last node is left out of the circle alone.
  • We can use another variable to record the remaining number of people in the circle, so that when the remaining number of people is 0, we can jump out of the loop.
#include <stdio.h>
#include <stdlib.h>
typedef struct node{
    
    
	int data;
	struct node*next;
}Node,*link;
yuesefuhuan(int n,int m,int k)
{
    
    
	int i,count=0;
	Node *p = NULL,*pr = NULL,*q = NULL;  //q暂存第一个节点以便后续建立循环链表 
	for(i = 1;i <= n;i++){
    
    
			p=(Node*)malloc(sizeof(Node));
	if(p == NULL)
	{
    
    
		printf("memory out use");
		exit(0);
	}
		p->data = i;
	if(q == NULL)  //第一个节点的情况 
	{
    
    
		q = p;
		pr = p;        //pr挂起整个链表 
	}else {
    
              //非第一个节点 
	   pr->next = p;
	   pr = p; 
	        }
   }
		p->next = q;  //链表最后一个节点指向第一个节点构成循环链表 
	p = q;
	for(i = 0;i < k-1;i++){
    
             
		p = p->next;   //p指向开始报数的节点 
	}
      while(p != p->next){
    
        //循环退出的条件仅剩一个节点最后另外输出否则就是死循环 
           count++;    //计数 
      if(count != m)
	{
    
    
		q = p;  //q指向要删除节点的前一个节点 
		p = p->next;
	}else {
    
    
		count = 0;
		 pr = p;   //pr 暂存要删除的节点,以便删除后不影响p继续循环下一个节点 
		printf("%5d",pr->data);
		q->next = pr->next;
		free(pr);
		p = p->next;	
	}
	
 } 
 printf("%5d",p->data);
	
}
int main()
{
    
    
	int n,m,k;
	printf("请输入总人数:");
	scanf("%d",&n);
		printf("请输入到第几个人出列:");
	scanf("%d",&m);
	printf("请输入从第几个人开始报数:"); 
	scanf("%d",&k);
	 yuesefuhuan(n,m,k);
	return 0;
}

Circular array solution:

#include<stdio.h>
#include <stdlib.h>
int fun(int n, int m, int k);
int main() {
    
    
    int n = 0, m = 0, k = 0;
     scanf("%d%d%d", &n, &m, &k);
     fun(n, m, k);
     return 0;
}
int fun(int n, int m, int k) {
    
    
   //动态创建数组 开辟内存 
   int *flags = (int*)malloc(sizeof(int)*n); 
    int i;
   //数组全初始化为0 
for(  i = 0;i < n;i++ ){
    
    
	flags[i] = 0;
}
  int nowIndex = k - 1;                    // 当前下标
   int count = 0;                           // 计数器
  int nowLength = n;                         // 剩余人数
  while (nowLength > 0) {
    
    
        if (flags[nowIndex] == 0) {
    
    
             count ++;
             if (count == m) {
    
    
                   count = 0;
                   flags[nowIndex] = 1;
                   nowLength --;
                   printf("%d ", nowIndex + 1);
                  }
             }
           nowIndex ++;
              if (nowIndex == n) {
    
    
              nowIndex = 0;
              }
     }
}

Three, doubly linked list

Doubly linked list: Each node in the linked list has two pointer fields, one points to the immediate predecessor and the other points to the successor, forming a doubly linked list.

//链表结点定义:
typedef struct node{
    
    
	int data;  //数据域
	struct node* next;  //直接前趋
	struct node* prior;  //直接后继
}Node;

Doubly linked list empty list with head node:
insert image description here

Non-empty table:
insert image description here
delete node operation

	//p为待删除结点    
	q=p->prior;
	q->next=p->next;
	p->next->prior=q;
	free(p);

insert image description here
insert image description here
insert image description here
insert image description here

Insert node operation:

p->prior = q;  //p的前驱指向q
q->next->prior = p;   // q的直接后继结点的前驱指向p
p->next = q->next;   //p的后继指向q的直接后继结点
q->next = p; // q的后继指向p

insert image description here

insert image description here

insert image description here

Create, traverse, delete a node in a doubly linked list

#include <stdio.h>
#include <stdlib.h>
typedef struct node{
    
    
	int data;
	struct node*next;
	struct node*prior;
}Node;
Node*create(Node*head,int n)
{
    
    
	//创建头结点 
	head=(Node*)malloc(sizeof(Node));
	head->next=NULL;
	head->prior=NULL;
	head->data=0;
	int a;
	Node*p=NULL,*q=head;
    //初始化双向链表 
	for(int i=0;i<n;i++)
	{
    
    
		//初始化新的结点 p 
			p=(Node*)malloc(sizeof(Node));
	    p->next=NULL;
     	p->prior=NULL;
		scanf("%d",&a);
		p->data=a;
		 //移动q指针,指向最后一个结点 
		q->next=p;
		p->prior=q;
		q=q->next;

	 }         
      return head; 
}
  //遍历双向链表,前后两种遍历 
void display(Node*head)
{
    
    
	Node*pr = head->next,*p = NULL;
	while(pr)
	{
    
    
		printf("%5d",pr->data);
		p=pr;
		pr=pr->next;
	}
	printf("\n");
	while(p->prior)
	{
    
    
		printf("%5d",p->data);
		p=p->prior;
	} 
	printf("\n");  
}
 Node*del(Node*head,int num)
{
    
    
	Node*p=head->next,*q=NULL;
	if(head==NULL)
	{
    
    
		printf("empty list\n");
		return NULL;
	}
	while(p->data != num)
	{
    
    
		p = p->next;
	}
	if( p == NULL)
	{
    
    
		printf("not find \n");
	}
	//p为待删除结点    
	q=p->prior;
	q->next=p->next;
	p->next->prior=q;
	free(p);
return head;
}

//在链表尾部加入一个结点  
Node*add(Node*head,int num)
{
    
    
	Node*p=NULL,*pr=head;
	p=(Node*)malloc(sizeof(Node));
	if(p==NULL){
    
    
		printf("memory out use \n");
		return NULL;
	}
	p->data=num;
    //遍历得到最后一个结点 
		while(pr->next){
    
    
			pr=pr->next;
		}
		//增加一个结点 
		pr->next=p;
		p->prior=pr;
		p->next=NULL;
	return head;
}
int main()
{
    
    
	int n,m,k;
	scanf("%d",&n);
	Node*head=NULL;
	head=create(head,n);
	display(head);
	printf("please input a delete number:\n");   //删除一个数k 
	scanf("%d",&k);
	head=del(head,k);
	display(head);
	printf("please input a add number:\n");  //增加一个数m 
	scanf("%d",&m);
	head=add(head,m); 
	display(head);   
	return 0;
}

4. Summary

1. Whether it is a circular linked list or a doubly linked list, it is essentially a linked list stored in a linear chain. The difference is that changing the pointer point and number of the linked list makes the linked list more convenient to use in a certain scenario.

2. In order to solve the linked list problem, in order to make insertion and deletion consistent at any position, a head node is usually added. The head node is not the first node. The head node is also called a dummy node. Its data domain Pointless. Of course, the head node is not necessary, it is just a node that is convenient for operating the reference.
3. The addition and deletion of linked lists combined with graphics is more convenient to write code and understand the process.

Refer to "Dahua Data Structure" (Cheng Jie)

Guess you like

Origin blog.csdn.net/qq_52595134/article/details/121591241