Circular linked list and bidirectional linked list

Keywords: C Algorithm data structure linked list

1, Foreword

The sequential storage structure of linear table (such as array) has continuous storage space, so we don't have to worry about the logical relationship between elements. The biggest advantage of linear table is that it can quickly access the elements at 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, the time complexity is O(n), the length of linear table is difficult to adapt to large changes, and the expansion of linear table needs to reopen a continuous space that meets the size requirements, which is easy to cause space fragmentation.
We learn the single linked list with linked storage structure. The single linked list can well solve these problems of the array. However, to access the elements at a certain position of the single linked list, we need to traverse the whole single linked list, and we have to start traversing in one direction every time. In order to solve these problems, we refer to circular linked list and bidirectional linked list.

2, Circular linked list

Circular linked list: point the pointer field of the last node in the single linked list to the head node (the linked list of the leading node) or the initial node (the linked list of the non leading node), so that the whole single linked list forms an end-to-end ring.

Empty table of the leading node of the circular linked list:

Non empty table:

When judging whether the single linked list is the last node, you only need 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 it is the last node, otherwise it is not.

When we often add a query at the end of the linked list, we can use the circular linked list with tail pointer to replace the head pointer with the tail pointer and point to the last node of the linked list.

Circular linked list to solve Joseph Ring problem

Joseph Ring problem: there are n people in a circle, each with a number of 1-n. start from the k-th person, and the person with a number of m will go out of the circle. The rest of the people continue to repeat this process until everyone goes out of the circle and output the sequence of going out of the circle.

Joseph Ring has two major problems:

  1. How to repeat a circle
  2. Ruge judges the end of the cycle without causing a dead cycle

The first problem is perfectly solved by using the circular linked list. The person who leaves the circle only needs to delete this node.
There are three ways to solve the second problem

  • The cyclic linked list of the leading node can be realized by judging whether there is only one head node, but each cycle should judge whether the node is the head node, and then jump over the head node. This obviously does not reflect the advantage that the chain storage structure is discontinuous and does not need to be deleted
  • If the header node is not used, only the loop judgment condition is p - > next= Null so that the last remaining node will cause an endless loop. Let's change our thinking. The last remaining node must be the last one out of the circle. Since it's OK for us to go out of the circle after the end of the cycle, as long as the next of the last node is itself, it means that there is only one node left. At this time, end the cycle, and finally let the last node go out of the circle alone.
  • We can use another variable to record the number of people remaining in the circle, so that we can jump out of the loop when the number of people remaining is 0.
#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 temporarily store the first node for subsequent establishment of circular linked list 
	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)  //First node 
	{
		q = p;
		pr = p;        //pr suspend the entire linked list 
	}else {          //Not the first node 
	   pr->next = p;
	   pr = p; 
	        }
   }
		p->next = q;  //The last node of the linked list points to the first node to form a circular linked list 
	p = q;
	for(i = 0;i < k-1;i++){         
		p = p->next;   //p points to the node that starts counting 
	}
      while(p != p->next){    //The condition of loop exit is that only one node is left, and another node is output. Otherwise, it is an dead loop 
           count++;    //count 
      if(count != m)
	{
		q = p;  //q points to the previous node to delete 
		p = p->next;
	}else {
		count = 0;
		 pr = p;   //pr temporarily stores the node to be deleted so that the deletion does not affect p to continue to cycle the next node 
		printf("%5d",pr->data);
		q->next = pr->next;
		free(pr);
		p = p->next;	
	}
	
 } 
 printf("%5d",p->data);
	
}
int main()
{
	int n,m,k;
	printf("Please enter the total number of people:");
	scanf("%d",&n);
		printf("Please enter the person to be listed:");
	scanf("%d",&m);
	printf("Please enter the number of people to start counting off:"); 
	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) {
   //Dynamically create array memory 
   int *flags = (int*)malloc(sizeof(int)*n); 
    int i;
   //The array is fully initialized to 0 
for(  i = 0;i < n;i++ ){
	flags[i] = 0;
}
  int nowIndex = k - 1;                    // Current subscript
   int count = 0;                           // Counter
  int nowLength = n;                         // Remaining number
  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;
              }
     }
}

3, Bidirectional linked list

Two way linked list: each node in the linked list has two pointer fields, one pointing to the direct forward trend and the other to the successor, forming a two-way linked list.

//Linked list node definition:
typedef struct node{
	int data;  //Data domain
	struct node* next;  //immediate predecessor 
	struct node* prior;  //Direct successor
}Node;

Empty table of the leading node of the bidirectional linked list:

Non empty table:

Delete node operation

	//p is the node to be deleted    
	q=p->prior;
	q->next=p->next;
	p->next->prior=q;
	free(p);




Insert node operation:

p->prior = q;  //The precursor of p points to q
q->next->prior = p;   // The precursor of the direct successor node of q points to p
p->next = q->next;   //The successor of p points to the direct successor node of q
q->next = p; // The successor of q points to p

Create, traverse and delete a node of a two-way 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)
{
	//Create header node 
	head=(Node*)malloc(sizeof(Node));
	head->next=NULL;
	head->prior=NULL;
	head->data=0;
	int a;
	Node*p=NULL,*q=head;
    //Initialize bidirectional linked list 
	for(int i=0;i<n;i++)
	{
		//Initialize new node p 
			p=(Node*)malloc(sizeof(Node));
	    p->next=NULL;
     	p->prior=NULL;
		scanf("%d",&a);
		p->data=a;
		 //Move the q pointer to the last node 
		q->next=p;
		p->prior=q;
		q=q->next;

	 }         
      return head; 
}
  //Traversal two-way linked list, before and after two traversal 
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 is the node to be deleted    
	q=p->prior;
	q->next=p->next;
	p->next->prior=q;
	free(p);
return head;
}

//Add a node at the end of the linked list  
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;
    //Traverse to get the last node 
		while(pr->next){
			pr=pr->next;
		}
		//Add a node 
		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");   //Delete a number k 
	scanf("%d",&k);
	head=del(head,k);
	display(head);
	printf("please input a add number:\n");  //Add a number m 
	scanf("%d",&m);
	head=add(head,m); 
	display(head);   
	return 0;
}

4, Summary

1. Whether it is a circular linked list or a two-way linked list, it is essentially a linear linked storage linked list. The difference is that changing the pointer direction and number of the linked list makes the linked list more convenient to use in a certain scene.

2. In solving the linked list problem, in order to make the insertion and deletion of any position operate consistently, a header node is usually added. The header node is not the first node, and the header node is also called dummy node, and its data field is meaningless. Of course, the head node is not necessary, but a node for convenient operation and reference.
3. The addition and deletion of linked list combined with graphics is more convenient to write code and understand the process.

Refer to Dahua data structure (Cheng Jie)

Posted by tonchily on Sun, 28 Nov 2021 10:17:26 -0800