C language: single linked list

Keywords: C linked list Singly Linked List

catalogue

Write before:

Preface - array defects

What is a linked list

node

Chain header

Create a new node

Play - inserting nodes

Traversal of linked list

Lookup (search) of linked list

Delete Linked List

Clearing of linked list

summary

Write it at the back

reference material

Write before:

This article is suitable for beginners. In view of my limited ability and the principle that I hope readers can obtain the highest income in the shortest time, I will not elaborate too many professional terms and underlying knowledge, be concise and concise, and simplify the text to avoid the obscurity and lengthy of the article.

The knowledge needed in this article: pointer, structure, typedef, dynamic memory allocation (malloc and free)

Preface - array defects

When you first contact arrays, you will feel that arrays are very convenient and can store a large amount of data. However, with the deepening of learning, the functions of arrays can no longer meet our needs, and the disadvantages of arrays are gradually highlighted:

1. The number of elements must be determined before using the array, which is not flexible enough;  

2. For the operations of inserting and deleting elements, it often makes a large part of the elements in the array move forward or backward, which is not efficient enough;

3. For a given array, it can only store variables of a single type;

In order to solve these problems, we can use variable arrays, but this method is not common in practical application, so we won't repeat it. We prefer to use Linked List to solve these problems.

What is a linked list

Linked list is a collection of independent data structures (i.e. nodes) containing data. The nodes in the linked list are connected by pointers, and the program accesses the nodes and the data in the nodes through pointers.

I believe you are no stranger to online games. We take online games as a metaphor, so that readers can get a better understanding. There are often some rewarding "reward tasks" in the game, often in the "peeling onion" mode. First go to a place to do something, and then tell you the location and content of the next task. In this cycle, you can get the "reward" until you finish the last task. The same is true of our linked list. You can slowly realize it in the following introduction.

node

The node of the linked list is of struct type, for example:

    struct Node{
        //Data domain
		int num; 
        //Pointer field 
		struct Node *next;
	}; 

This is a simple node. The memory area where data is stored in the node is called the data field, and the memory area where pointers are stored is called the pointer field. The existence of pointer field makes it possible for us to connect the nodes of the linked list.

Chain header

For a linked list, we need to know its starting address, just as we need to know its array name when using an array (the array name is the starting address of the array), and its function is also equivalent to the array name of the array. Therefore, we define a root pointer, which points to the first node in the linked list. Generally speaking, we do not initialize or use the data in the header. We can define the root pointer as follows:

struct Node * headList=NULL; 

We first point it to NULL to avoid it becoming a wild pointer.

Create a new node

	 //Create node
	 int cnum;
	 cin>>cnum;
	 struct Node* newNode =(struct Node*)malloc(sizeof (struct Node));
	 newNode->num=cnum;
	 newNode->next=NULL;

The method of creating nodes is almost the same as that of creating headers. When the above code segments are put into the loop, new nodes can be created continuously to save the input data. Obviously, creating nodes in this way can not meet our needs because it does not connect each created node. Because there are many ways to insert connections, the author will explain creation and insertion separately.

Play - inserting nodes

It is meaningless to create new nodes without inserting them. Nodes without connections are like isolated islands scattered all over the world. At this time, we need to play the powerful role of pointers. If we can get the address of the next node at the same time when we access a node, can we realize continuous access to nodes (traversal)? Therefore, after creating the node, we should select the appropriate way to insert and connect the node in the appropriate position. The common insertion methods include head insertion and tail insertion. Let's take tail insertion as an example (tail interpolation means that the new node is connected behind the last node in the chain list, and head interpolation means that the new node is connected between the chain header and the original first node)

	 //Insert node 
	 int cnum;
	 cin>>cnum;
	 struct Node* newNode =(struct Node*)malloc(sizeof (struct Node));
	 newNode->date=cnum;
	 newNode->next=NULL;
	 //End node found 
	 struct Node * tail=headList; 
	 if(tail){
		 while(tail->next){
		 	tail=tail->next;//When the next in the node pointed to by the tail is not NULL, the end node is not found and the tail points to the next node 
		 }
		 //Connect the new node at the end 
		 tail->next=newNode;
	}else
		headList=newNode;//When the tail pointer is null, that is, when the linked list is null, we make the header point to the new node, that is, the first node of the linked list 

Let's explain the above code. First, we create a new node, and then define a pointer variable tail to traverse the whole linked list from the header. We want to find the tail of the linked list. According to the characteristics that the next pointer of the tail points to NULL, we design the above loop. After finding the tail node, we make the next pointer of the tail node point to the new node to establish the The relationship between the original linked list and the new node realizes the insertion of the new node. It should be noted that it is necessary to judge whether the tail pointer itself is a NULL pointer. If it is a NULL pointer, it means that the linked list is NULL. At this time, just make the header point to the new node.

Traversal of linked list

The traversal of the linked list is realized through the interconnection of pointers. Each time, the pointer can be moved to the next node. When traversing to the tail node, the pointer is NULL and exits the circular traversal.

		while(headList){
			cout<<headList->num<<endl;
			headList=headList->next;
	}

Lookup (search) of linked list

There are many ways to search an array, such as linear search and more efficient binary search. For a single linked list, we introduce a common simple search method similar to array linear search:

	 struct Node * findNode=headList;
	 int aim_num;
	 bool flag=true;
	 cin>>aim_num;
	 while(findNode){
	 	if(findNode->num==aim_num){
	 		cout<<"eureka\n";
	 		flag=false;
	 		break;
	 	}
	 	if(findNode->next==NULL)//Traversing to the last node is still not found 
	 		break;
	 	else
	 		findNode=findNode->next;
	 }
	 if(flag)
	 	cout<<"can't find";

Delete Linked List

As mentioned above, deleting an element in the middle of an array will cause all the elements behind the element to move forward, resulting in very low efficiency. In the linked list, how do we delete a node? First, based on the search of the linked list, we can locate the node we want to delete through the pointer.

  As shown in the figure, how can we delete this node from the linked list? We might as well call the node we want to delete as the target node. To delete the target node, we just need to make the next pointer in the previous node point to the next node of the target node instead of the target node.

  In addition, do you remember how we created new nodes? Yes, it is established through dynamic memory application. When we no longer use this node (delete node), we also need to release this part of memory through the free() function. The deletion code is as follows:

	struct Node * findNode=head;
	struct Node * p=NULL;
	int aim_num;
	cin>>aim_num;
	while(findNode){
	 	if(findNode->num==aim_num){
	 		p->next=findNode->next;
	 		free(findNode);
	 		cout<<"Successfully deleted\n";
	 		break;
	 	}
	 	if(findNode->next==NULL){//Traversing to the last node is still not found 
	 		cout<<"Not found, deletion failed\n";
	 		break; 
	 		} 
	 	else{
	 			p=findNode;
	 			findNode=findNode->next;
	 		}
	 }

We create a new pointer P, which always points to the previous node of the target node. In this way, when we find the target node, we can operate on the previous node through pointer p to make the next pointer point to the address pointed to by next in the target node. However, there are loopholes in doing so. When the target node to be deleted is the first node in the linked list, it is not difficult to find that the P pointer is NULL, and the NULL "-" > "(arrow) operation is invalid. To solve this problem, we only need to make the following modifications:

	struct Node * findNode=headList;
	struct Node * p=NULL;
	int aim_num;
	cin>>aim_num;
	while(findNode){
	 	if(findNode->num==aim_num){
	 		//Determine whether p is a null pointer 
	 		if(p)
	 			p->next=findNode->next;
	 		else
	 			headList=findNode->next;//To delete the first node, we need to make the header point to the original second node 
	 		free(findNode);
	 		cout<<"Successfully deleted\n";
	 		break;
	 	}
	 	if(findNode->next==NULL){//Traversing to the last node is still not found 
	 		cout<<"Not found, deletion failed\n";
	 		break; 
	 		} 
	 	else{
	 			p=findNode;
	 			findNode=findNode->next;
	 		}
	 }

Clearing of linked list

When we no longer use this linked list, we need to release the memory where it is located. The method is also very simple. We can traverse it once.

	 struct Node* p=headList;
	 struct Node* q=p;
	 while(p){
	 	q=p->next;
	 	free(p);
	 	p=q;
	 }

We define two pointers. When we want to free the node referred to by the P pointer, we make the Q pointer point to the next node of the node in advance. After releasing the node referred to by P, we make p=q, that is, make the P pointer point to the next node of the node just released. This cycle goes back and forth until the memory of the last node is released, p=NULL, and the cycle exits, The linked list is cleared.

summary

The linked list is like a treasure game. There will be a clue to point you to a place, and then get another clue to point you to a place... Some of the above codes seem cumbersome. In fact, the author deliberately did it for the convenience of beginners (for example, using the while loop will be simplified, but it is inconvenient to understand), The following author will encapsulate the above functions through functions:

# include <iostream>
# include <cstdio>
# include <cstdlib>
using namespace std;
	typedef	struct myNode{
		int num;//Data domain 
		struct myNode *next;//Pointer field 
	} Node; 
void List_creat(Node ** pheadList);
void NewNode(Node * headList,int cdate) ;
void  Tail_inter(Node ** headList,int cdate);
void  traversal_list(Node * pheadList);
void  Search_node(Node * headList,int aim_num);
void  Remove_node(Node * headList,int aim_num);
void  Clear_list(Node * headList);
int main()
{
	Node * mylist;
	List_creat(&mylist);
	Tail_inter(&mylist,1);
	Tail_inter(&mylist,3);
	Tail_inter(&mylist,5);
	Tail_inter(&mylist,7);
	Tail_inter(&mylist,9);
	traversal_list(mylist);
	cout<<"Search whether there is 7 this data in the linked list"<<endl;
	Search_node(mylist,7);
	cout<<"Search whether there is 10 this data in the linked list"<<endl;
	Search_node(mylist,10);
	//Delete 7 this data from the linked list 
	Remove_node(mylist,7);
	//Search 7 this data again
	Search_node(mylist,7);
	//Let's go through the linked list again
	cout<<endl<<"traverse again \n";
	traversal_list(mylist);
	//Reclaim memory
	Clear_list(mylist) ;
	return 0;
}

//Create header 
void List_creat(Node ** pheadList)
{
	*pheadList=NULL;
}

//Create node
void NewNode(Node * headList,int cdate) 
{
	Node* newNode =(Node*)malloc(sizeof (Node));
	newNode->num=cdate;
	newNode->next=NULL;
}

//Inserting nodes by tail interpolation
void  Tail_inter(Node ** pheadList,int cdate)
{
	Node* newNode =(Node*)malloc(sizeof (Node));
	newNode->num=cdate;
	newNode->next=NULL;
	Node * tail=*pheadList; 
	 if(tail){
		 while(tail->next){
		 	tail=tail->next;
		 }
		 tail->next=newNode;
	}else
		*pheadList=newNode;
}

//Traversal linked list 
void  traversal_list(Node * headList)
{
		while(headList){
			cout<<headList->num<<endl;
			headList=headList->next;
	}
}

//Search node
void  Search_node(Node * headList,int aim_num)
{
	Node * findNode=headList;
	 bool flag=true;
	 while(findNode){
	 	if(findNode->num==aim_num){
	 		cout<<"eureka\n";
	 		flag=false;
	 		break;
	 	}
	 	if(findNode->next==NULL)//Traversing to the last node is still not found 
	 		break;
	 	else
	 		findNode=findNode->next;
	 }
	 if(flag)
	 	cout<<"can't find\n";
}

//Delete node
void  Remove_node(Node * headList,int aim_num)
{	Node * findNode=headList;
	Node * p=NULL;
	while(findNode){
	 	if(findNode->num==aim_num){
	 		//Determine whether p is a null pointer 
	 		if(p)
	 			p->next=findNode->next;
	 		else
	 			headList=findNode->next;//To delete the first node, we need to make the header point to the original second node 
	 		free(findNode);
	 		cout<<"Successfully deleted\n";
	 		break;
	 	}
	 	if(findNode->next==NULL){//Traversing to the last node is still not found 
	 		cout<<"Not found, deletion failed\n";
	 		break; 
	 		} 
	 	else{
	 			p=findNode;
	 			findNode=findNode->next;
	 		}
	 }
	
}

//Clear linked list
void  Clear_list(Node * headList)
{
	Node* p=headList;
	Node* q=p;
	while(p){
	 	q=p->next;
	 	free(p);
	 	p=q;
	 }
}

The new node function has not been used. For various reasons, the author still retains it.

Write it at the back

The code in this article is not perfect and slightly cumbersome. It is mainly for beginners to elaborate in detail. If there are technical errors, you are welcome to comment and point out in a private letter. Thank you!

reference material

C and the pointer by Kenneth A.Reek

Introduction to algorithm competition (Second Edition) by Liu Rujia

Posted by AnthonyArde on Wed, 24 Nov 2021 05:29:13 -0800