Data structure | implementation of linked list

Posted by goldages05 on Tue, 30 Nov 2021 16:27:50 +0100

In the linear list, in addition to the important structure of sequential list, there is also the chain structure, which is also what we often call the chain list.

Generally, each node of the linked list is represented by defining a structure (class). Generally speaking, the nodes of the linked list will have data fields and address fields. The data field is used to store data, and the address field is used to link nodes.

Common linked list structures are:

  1. Leading node and non leading node
  2. Single pointer and double pointer
  3. Cyclic and non cyclic

The linked list structure is implemented in C language.

The code is also uploaded to Gitee

Single linked list

This is the implementation of the single linked list, which is a headless node non cyclic linked list.

Sub file implementation:. h file is the declaration of related functions, and. c file is the definition of functions.

. h file

//Implementation of one-way acyclic linked list without sentinel node

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>


//Define the data type of the linked list
typedef int SLListDataType;


//Defines the nodes of the structure
typedef struct SLListNode {

	SLListDataType data;
	struct SLListNode* next;

}SLLN;


//Dynamically apply for a node
SLLN* BuySLListNode(SLListDataType x);

//Destruction of single linked list
void SLListDestory(SLLN* plist);

//Single linked list printing
void SLListPrint(SLLN* plist);


//Single chain table tail insertion
void SLListPushBack(SLLN** plist, SLListDataType x);


//Single chain tail deletion
void SLListPopBack(SLLN** plist);


//Single chain meter insertion
void SLListPushFront(SLLN** plist, SLListDataType x);


//Single chain header deletion
void SLListPopFront(SLLN** plist);


//Single linked list to find a data
SLLN* SLListFind(SLLN* plist, SLListDataType x);


//The single linked list inserts x before the pos position, and the incoming pos should be the structure address
void SLListInsert(SLLN** plist, SLLN* pos, SLListDataType x);

//The single linked list inserts an x after the pos position
void SLListInsertAfter(SLLN* plist, SLLN* pos, SLListDataType x);


//The single linked list deletes the value at pos
//To delete the pos location, you need to get the location of the previous node of the location first
//In this way, we can connect the previous node with the next node
//Therefore, the node behind the pos is usually deleted
void SLListErase(SLLN** plist, SLLN* pos);


void SLListEraseAfter(SLLN* plist, SLLN* pos);

The data type of the typedef node. The implementation in the function does not need to be changed after subsequent modification

Defines the structure type, including data field and pointer field

Nodes need to apply dynamically, so malloc function is used

To implement a printing function is to check whether the function is implemented correctly

The tail interpolation function is the last node added to the linked list

Tail deletion is to delete the last node of the linked list

Header insertion is to add a new node at the head of the linked list, and the header pointer points to the new node

Header deletion is to delete the node at the head of the linked list, and the header pointer points to the next node

Searching data is based on the value of the data field. Of course, it can also be designed to search according to specific subscripts

A single linked list inserts a node at a certain location, which does not need to move data like an array

. c Documents

//Implementation of one-way acyclic linked list without sentinel bit head node

#include"SingleLinkedList.h"

//Dynamically apply for a node and assign a value
SLLN* BuySLListNode(SLListDataType x) {

	//Open up a node space
	SLLN* tmp = (SLLN*)malloc(sizeof(SLLN));

	//Judge whether the returned pointer is null
	if (tmp == NULL) {

		exit(-1);
	}
	else {
		//Apply for one node at a time, put the data to be stored in the node, and the next pointer is null
		tmp->next = NULL;
		tmp->data = x;
	}
	return tmp;


}


//Destruction of single linked list
void SLListDestory(SLLN** plist) {

	assert(*plist && plist);

	SLLN* p = *plist;
	while (*plist != NULL) {
		p = *plist;
		*plist = p->next;
		free(p);
	}

}


//Single linked list printing
void SLListPrint(SLLN* plist) {

	//Determine whether the passed in pointer is null
	//assert(plist);

	while (plist != NULL) {

		//The print format should be determined according to the type of SLListDataType
		printf("%d->", plist->data);

		plist = plist->next;
	}
	printf("NULL\n");
}


//Single chain table tail insertion
//What is passed in here is the secondary pointer of the structure. The purpose is to modify the external structure pointer to point to the dynamically opened space
void SLListPushBack(SLLN** plist, SLListDataType x) {

	//Determine whether the passed in secondary pointer is null
	assert(plist);

	SLLN* newnode = BuySLListNode(x);


	SLLN* p = *plist;

	if (*plist == NULL) {
		*plist = newnode;
	}
	else {

		while (p->next != NULL) {
			p = p->next;
		}

		p->next = newnode;
	}
		
}


//Single chain tail deletion
//If the single linked list has only one node, there will be no nodes after tail deletion, so we need to set plist empty
void SLListPopBack(SLLN** plist) {

	//Judge whether the passed structure pointer is empty. If it is empty, it means that there is no node and it does not need to be deleted
	assert(*plist);

	//Define two pointers to record the last node and the penultimate node
	SLLN* tail = *plist;
	SLLN* prev = NULL;
	while (tail->next != NULL) {

		prev = tail;
		tail = tail->next;
	}

	//If there is only one node in the single linked list, we can directly free it
	if (prev == NULL) {
		free(tail);
		*plist = NULL;
	}
	else {
		free(tail);
		prev->next = NULL;
	}



}


//Single chain meter insertion
//Similarly, you need to pass in the secondary pointer
void SLListPushFront(SLLN** plist, SLListDataType x) {

	SLLN* newnode = BuySLListNode(x);
	SLLN* p = *plist;

	if (*plist == NULL) {
		*plist = newnode;
	}
	else
	{
		newnode->next = p;
		*plist = newnode;
	}


}


//Single chain header deletion
//You need to change the direction of the header pointer, so you also need to pass in the secondary pointer
void SLListPopFront(SLLN** plist) {

	//Judge whether there is at least one node
	assert(*plist);

	SLLN* p = *plist;
	*plist = (*plist)->next;

	free(p);



}


//Single linked list to find a data
//If found, return the address pointing to the node
SLLN* SLListFind(SLLN* plist, SLListDataType x) {

	//Determine whether the passed in pointer is null
	assert(plist);

	SLLN* p = plist;

	//Exit the loop when p is a null pointer
	while (p) {

		if (p->data == x) {

			return p;
		}
		p = p->next;

	}
	return NULL;

}


//The single linked list inserts an x after the pos position
void SLListInsertAfter(SLLN* plist, SLLN* pos, SLListDataType x) {

	//Determine whether the two passed in pointers are null
	assert(plist && pos);

	SLLN* p = plist;



	while (p != pos) {

		//If p is NULL, it means that the entire linked list has been traversed, but no suitable pos has been found
		if (p == NULL) {
			printf("Entered pos non-existent\n");
			return;
		}

		p = p->next;
	}

	SLLN* newnode = BuySLListNode(x);

	newnode->next = p->next;

	p->next = newnode;


}


//Single linked list inserts x before pos position
//Header insertion may occur, so you also need to pass in a secondary structure pointer here
//At this time, you need to define two structure pointers, one pointing to pos and the other pointing to a node in front of pos
void SLListInsert(SLLN** plist, SLLN* pos, SLListDataType x) {

	assert(plist && pos);

	SLLN* prev = NULL;
	SLLN* tail = *plist;

	SLLN* newnode = BuySLListNode(x);

	if (tail == pos) {

		//This is equivalent to header insertion. You can directly call the interface of header insertion
		newnode->next = *plist;
		*plist = newnode;

	}
	else {

		//Here, tail= Null prevents pos from being found after traversing the linked list
		//The design here is to insert the data at the end if pos is not found after traversing the linked list
		while (tail != pos && tail != NULL) {
			prev = tail;
			tail = tail->next;
		}
		prev->next = newnode;
		newnode->next = tail;
	}


}


//The single linked list deletes the value at pos
//Header deletion may occur, so it is also necessary to pass in the secondary pointer to change the header node pointed to by the header pointer
void SLListErase(SLLN** plist, SLLN* pos) {

	assert(plist && pos);

	SLLN* prev = NULL;
	SLLN* tail = *plist;

	if (tail == pos) {
		*plist = tail->next;
		free(tail);
	}
	else {
		while (tail != pos) {

			//Prevent pos from being invalid, so that no POS can be found after traversing the complete linked list
			assert(tail);

			prev = tail;
			tail = tail->next;
		}
		prev->next = tail->next;
		free(tail);
	}


}

void SLListEraseAfter(SLLN* plist, SLLN* pos) {

	SLLN* p = plist;
	SLLN* tmp = NULL;
	while (p != pos) {

		//It is not allowed to pass in invalid pos and delete data after the last node
		assert(p);

		p = p->next;

	}

	//It is not allowed to delete the next node of the last node
	assert(p->next);

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

}

Points to note:

  1. When deleting the tail, you need to traverse the linked list to find the last node and the previous position of the last node, make the node of the previous position point to NULL, and then release the last node
  2. When inserting and deleting headers, the address of the first node in the linked list will be changed. We need to change the external header pointer accordingly, either by passing in the secondary pointer or by returning a new header node
  3. When inserting a node in the middle of the linked list, pay attention to the order of disconnecting the old node and the order of linking the new node. The old node must not be disconnected first, otherwise the second half of the disconnected part will not be found
  4. The addition, deletion, modification and query of a single linked list is not very convenient, so it is generally not used as a data storage structure, but the general topic is the single linked list, so it also needs to be understood

Double linked list

Double linked list means that each node has two pointers, one is the successor pointer to point to the next node, and the other is the precursor pointer to point to the previous node.

Here is also a circular linked list of the head node with sentinel bit. The head node does not need to store data. Its only function is that the next pointer always points to the real first node of the linked list.

. h file

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int DataType;

typedef struct ListNode {

	struct ListNode* prev;
	struct ListNode* next;
	DataType data;

}ListNode;


//Create a header node and return
ListNode* ListCreat();

//Creation of linked list nodes
ListNode* ListNodeBuy(DataType x);

//Destruction of linked list
void ListDestroy(ListNode* pHead);

//Printing of linked list
void ListPrint(ListNode* pHead);

//Header insertion of linked list
void ListPushFront(ListNode* pHead, DataType x);

//Header deletion of linked list
void ListPopFront(ListNode* pHead);

//Tail insertion of linked list
void ListPushBack(ListNode* pHead,DataType x);

//Tail deletion of linked list
void ListPopBack(ListNode* pHead);

//Search of linked list
ListNode* ListFind(ListNode* pHead, DataType);

//Insert a node in front of the pos position in the linked list
void ListInsert(ListNode* pos, DataType x);

//Delete the node above the pos position
void ListErase(ListNode* pos);

The related functions of the double linked list are defined in the. h file. The approximate function is similar to that of the single linked list

. c Documents

//Implementation of two-way circular linked list
#include"List.h"

//Create a header node and return
ListNode* ListCreat() {

	ListNode* pHead = (ListNode*)malloc(sizeof(ListNode));
	if (pHead == NULL) {
		exit(-1);
	}

	pHead->next = pHead;
	pHead->prev = pHead;
	return pHead;
}

//Creation of linked list nodes
ListNode* ListNodeBuy(DataType x) {

	ListNode* plist = malloc(sizeof(ListNode));
	if (plist == NULL) {
		exit(-1);
	}
	else {
		plist->data = x;
		plist->next = NULL;
		plist->prev = NULL;
		return plist;
	}

}

//Destruction of linked list
void ListDestroy(ListNode* pHead) {

	assert(pHead);
	assert(pHead->next);
	
	ListNode* cur = pHead->next;
	while (cur != pHead) {
		ListNode* next = cur->next;
		free(cur);
		cur = next;
	}

	free(pHead);


}

//Printing of linked list
void ListPrint(ListNode* pHead) {

	assert(pHead);

	ListNode* cur = pHead->next;
	while (cur != pHead) {
		printf("%d ", cur->data);
		cur = cur->next;

	}
	printf("NULL");
	printf("\n");
}

//Header insertion of linked list
void ListPushFront(ListNode* pHead, DataType x) {

	ListNode* newnode = ListNodeBuy(x);

	pHead->next->prev = newnode;
	newnode->next = pHead->next;
	newnode->prev = pHead;
	pHead->next = newnode;


}

//Header deletion of linked list
void ListPopFront(ListNode* pHead) {

	assert(pHead->next != pHead);

	ListNode* tmp = pHead->next;
	ListNode* Nexttmp = tmp->next;
	pHead->next = tmp->next;
	Nexttmp->prev = pHead;
	free(tmp);
}

//Tail insertion of linked list
void ListPushBack(ListNode* pHead,DataType x) {

	ListNode* newnode = ListNodeBuy(x);

	ListNode* tail = pHead->prev;
	newnode->next = pHead;
	newnode->prev = tail;
	tail->next = newnode;
	pHead->prev = newnode;

}

//Tail deletion of linked list
void ListPopBack(ListNode* pHead) {
	assert(pHead->prev != pHead);

	ListNode* tmp = pHead->prev;
	ListNode* Prevtmp = tmp->prev;	
}

//Search of linked list
ListNode* ListFind(ListNode* pHead, DataType x) {

	ListNode* cur = pHead->next;
	while (cur != pHead) {
		if (cur->data == x) {
			return cur;
		}
		else {
			cur = cur->next;
		}
	}
	return NULL;
}

//Insert a node in front of the pos position in the linked list
void ListInsert(ListNode* pos, DataType x) {

	assert(pos);

	ListNode* newnode = ListNodeBuy(x);
	ListNode* posPrev = pos->prev;


	posPrev->next = newnode;
	newnode->prev = posPrev;

	newnode->next = pos;
	pos->prev = newnode;

}



//Delete the node above the pos position
void ListErase(ListNode* pos) {

	assert(pos);
	//It is not allowed to delete the head node of sentinel position
	assert(pos != pos->next);

	ListNode* Next = pos->next;
	ListNode* Prev = pos->prev;

	Prev->next = Next;
	Next->prev = Prev;
	free(pos);
}

It should be noted that:

  1. Because the double linked list can find the previous node and the latter node, it is not necessary to record the previous node at any time when deleting a node
  2. Because it is a circular linked list, when deleting the last node, you don't need to traverse the whole linked list to find the tail node. You only need to use the precursor pointer of the head node
  3. A two-way circular linked list is generally used to store data

Comparison of array structure and chain structure

Data structure and chain structure are linear in logical structure

However, due to their respective characteristics, the scenarios used are also different

Disadvantages of arrays:

  1. If there is not enough space, it needs to be expanded. The expansion consumes a lot
  2. Deleting data or inserting data in the middle or header requires moving data, which is expensive
  3. Generally, in order to avoid frequent capacity expansion, each capacity expansion is doubled, but this may also cause a waste of space

Advantages of arrays:

  1. It supports random access, and can support some algorithms that need orderly or random access, such as binary search, quick sorting, etc
  2. Compared with the single linked list, the tail delete data and insert data do not need to be traversed, which is more efficient

Disadvantages of linked list:

  1. In addition to storing data, each node also stores pointers, which consumes more space than arrays
  2. Random access is not supported. Searching for some data requires traversal

Advantages of linked list:

  1. More reasonable use of space, and apply for space when storage is needed, which will not cause a waste of space
  2. There is no need to move the data to delete the data in the header and middle

Topics: C data structure