[Data Structure] Linked List Details

 The content to be shared in this film is a linked list. For the convenience of reading, the following is the catalog of this film

Table of contents

1. Questions and thoughts on the sequence table

1. Traversing the linked list

2. Head insertion

2.1 Open up space function packing

3. Tail Insertion

correct

4. Tail removal

5. Head deletion  

6. Data search

7. Insert at any position


1. Questions and thoughts on the sequence table

The previous article explained the operation of adding, deleting, checking and modifying in the sequence table, but if only using the sequence table to solve the problem in a very large project has a little more limitations, we might as well think about whether the sequence table has the following problems

1. Insertion and deletion in the middle/head, the time complexity is O(N)
2. To increase capacity, you need to apply for new space, copy data, and release old space. There will be a lot of consumption.
3. The increase in capacity is generally a 2-fold increase, and there is bound to be a certain amount of space waste. For example, the current capacity is 100, and when it is full
, it will be increased to 200. We continue to insert 5 more data, and no data will be inserted later, so 95 data spaces will be wasted.

How to solve these problems at will?

1. No expansion is required;

2. Apply for free space on demand;

3. Solve the problem of moving data when inserting and deleting;

The root of the sequence table is a continuous physical space like an array, so we can apply for a small space one by one to store his information, unlike the sequence table that applies for a large space when applying for space.

So you can use a linked list to solve such a problem.

   

 In the previous article, we mentioned that the space of the sequence table is continuous, and the data can be found by accessing the subscript, while the space of the singly linked list is not continuous one by one, so we cannot access the data through the subscript

So we can still access their information through pointers.

Store the pointer of the previous space in the next space of the linked list, and connect them all in series like a string; in this way, you can access a space and know the address of the next space at the same time.

 We might as well define a structure to complete the operation of the linked list

typedef int SLDatatype;
typedef struct SListNode
{
	SLDatatype data;
	struct SListNode* next;
}SLTNode;

First simply rename the data type we want to operate, so that the data type can be modified later;

Define a structure, only one data is stored for easy operation during explanation, and the address of the previous space needs to be stored at the same time, so a structure pointer is needed;

Next, operate on the linked list;

1. Traversing the linked list

 It is a very simple operation to traverse and display the content of the singly linked list. The code is as follows:

void SLPrint(SLTNode* phead)
{
	SLTNode* cur = phead;//将phead的内容拷贝一份给cur
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

Here we first define a pointer of structure type, and copy the content of phead to cur, which is equivalent to having two pointers with the same content;

 (The address in the box is just random assignment, no other meaning)

The next step is to use the pointer of the structure type to manipulate the content, and cur->points to the content of the structure to access its content, and then use the print function to output it;

That is to say, cur->next is the position of the next node. Every time the next node is visited, its position will be saved in cur, and the cycle goes on and on.

2. Head insertion

The main thing to insert the head is that you only need to make a fuss about the starting position of the linked list. The code is as follows ( note that the code is wrong )

//头部插入
void SLPushFront(SLTNode* phead, SLDatatype x)
{
    //开辟空间
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}

    //赋值
	newnode->data = x;
	newnode->next = NULL;

    //链接
	newnode->next = phead;
	phead = newnode;
}

 There are two parameters, one is the address, and the other is the content like the added data;

Since it is to be added, malloc must be used to expand the capacity, and at the same time, the space for malloc expansion must be judged. I have shared it many times and will not repeat it here;

Then access the structure content through the structure pointer to change the value;

The last newnode is a pointer variable, through which next is assigned a value. Since the head is inserted, the first data of the original linked list becomes the second, so the value of next is the address of the original linked list data. It is not difficult to see here that phead is the address of the first data in the original linked list, so it is assigned to next;

At the same time, newnode becomes the first data of the linked list after inserting data, so the value of newnode must be assigned to phead, so that the head can be inserted next time;

Let's use the code to run in the main function

We found that the values ​​were not being output as we expected 

The reason is that there are still some problems in logic

SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));

 We found that newnode is a local variable, which will be destroyed automatically after going out of scope. The same phead is a formal parameter, which will be destroyed after use, so we cannot find the first node of the linked list, so we have to Keep a pointer in the main function

SLTNode* list = NULL;

to ensure that the linked list can find the first node

Since the local variable in the main function is a pointer

We might as well start from the previous two-number exchange function to observe the problem of the above code

 When we write the exchange of two numbers in the form of code, we will write the formal parameter as the address of the actual parameter, and apply it in the function to achieve the function of exchanging two numbers. From this we can conclude that if we want to operate a certain content, we can use The function is passed the address of this content.

 When we want to change the content pointed by the pointer, we find that calling the function cannot change the content,

So here is still the conclusion mentioned above, if you want to operate a certain content, use the function to pass the address of this content;

Here we want to operate two pointers, we need to pass the address of the pointer, pay attention to the address of the pointer, so we must use the secondary pointer to solve the problem.

The following is the correct code

void SLPushFront(SLTNode** pphead, SLDatatype x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;


	newnode->next = *pphead;
	*pphead = newnode;
}

In this way, we bring it into the main function and use the function we wrote

 In this way, we can achieve the desired effect

2.1 Open up space function packing

We found that in the code written before, we need to use malloc to open up space, judge the success of the opening, and finally empty the node, so we might as well write these operations as a function for our convenience.

SLTNode* BuyLTNode(SLDatatype x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
}

You can see that in this function, there are operations to open up space, check the space, and empty the space. In the next insert operation, we use the above function; 

3. Tail Insertion

Tail insertion is a bit more troublesome than head insertion, because you need to find the tail of the previous node, which may be difficult to understand. The code is as follows (the following code is wrong)

void SLPuchBack(SLTNode* phead, SLDatatype x)
{
	SLTNode* tail = phead;
	while (tail->next != NULL)
	{
		tail = tail->next;

	}
	SLTNode* newnode = BuyLTNode(x);
	tail->next = newnode;
}

First, we need to define a pointer to a structure variable, and assign the value of phead to tail. We only need to operate tail to operate phead;

Next, we need to find the tail of the entire linked list, so we can see the judgment conditions of the above code. When the next variable in tail is not empty, tail points to the next node, that is to say, when next is empty, the loop ends;

Next, re-create a structure pointer newnode, which is the space where we want to store data for tail insertion, and we use the BuyLTNode function to initialize it;

Because newnode is a new structure pointer, and next is also the structure pointer we created initially, so finally assign the newnode structure pointer to next in tail, so that the new space is connected to the space at the previous tail stand up;

It should be noted that when we want to form a link with the previous node and the next node in the linked list, we must operate the next in the structure pointer variable, because next is also a structure pointer variable, which stores the next The address of the node, so that it can be connected to the next node.

correct

One more thing we need to consider is what to do when the linked list has no data at the beginning? Obviously the above code does not take this situation into account; when the linked list is empty, the above code cannot insert data;

 We will find that the program will crash

So let's go ahead and solve this situation

We found that plist is a pointer variable, as mentioned before, if we want to change the data, we manipulate the address of the data;

Obviously, what we pass is the address of the pointer, so we need to manipulate the address of the pointer, so when writing a function, we need to use a secondary pointer to manipulate the address of the pointer;

code show as below

//尾部插入
void SLPuchBack(SLTNode** pphead, SLDatatype x)
{
	SLTNode* newnode = BuyLTNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;

		}
		tail->next = newnode;
	}
}

 The situation we consider this time is that if there is no data in the linked list, we need to apply for a space to store our data, as shown in the if statement of the above code;

Next, if it is not an empty linked list, insert and link according to the original code, it should not be difficult to understand;

It should also be noted here that we only use the secondary pointer to add the first space for the first time, that is, when the linked list is empty, and the subsequent additions only need to operate the next pointer in the pointer. When the linked list is When it is empty, only the second-level pointer is used once, and the first-level pointer is used when adding data backward;

4. Tail removal

The deletion operation should pay attention to the problem of two pointers. If the operation is not done properly, wild pointers may appear and the program will report an error;

Let's first observe the following wrong writing

void SLPopBack(SLTNode** pphead, SLDatatype x)
{
	SLTNode* tail = *pphead;
	while (tail->next !=NULL)
	{
		tail = tail->next;
	}
	free(tail);
	tail = NULL;
}

This string of code codes seems to have no problem, judging, there is basically no problem with free release

But if you want to study it in depth, you will find that it is completely different from the normal code;

As mentioned before, deleting is just to empty this node, but we should pay attention that when we empty the node, the pointer pointing to this node will become a wild pointer, so we must find a way to avoid wild pointers.

We might as well create another pointer variable prev

SLTNode* prev = NULL;

We can see tail = tail->next above; this string of code means to let tail store the address of the next pointer, so we might as well let prev store tail, and let prev store the value of tail every time tail takes a step

 

 Tail and prev are in front of each other, and each step of tail will let prev store the value of tail once;

When tail is the data we want to delete, we can make next in prev empty, so as to achieve the effect of deletion, and also avoid the appearance of the above-mentioned wild pointers.

But also pay attention to what if there is only one data in the linked list? What should I do if the linked list is empty? With only one node, we cannot find the next of the previous node and make it blank; so we still use the judgment condition and the secondary pointer to solve the problem.

That code is as follows

//尾部删除
void SLPopBack(SLTNode** pphead)
{
	//暴力检查指针是否为空
	assert(*pphead); 

	//判断是否只有一个数据
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	//判断多个数据 
	else
	{
		SLTNode* prev = NULL;
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		prev->next = NULL;
	}

} 

We can take it to the main function and apply

 

 You can see that the 4 at the end has been deleted

Let's try again when there is no data in the linked list

 It can be seen that when the pointer to the first data in the linked list is empty, the assert assertion works, and an error occurs in the program.

5. Head deletion  

The same deletion also needs to add the judgment of the empty linked list and whether there is only one data in the linked list;

The specific code is as follows

//头部删除
void SLPopFront(SLTNode** pphead)
{
	//暴力检查指针是否为空
	assert(*pphead);

	//判断是否只有一个数据
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	//判断多个数据 
	else
	{
		SLTNode* del = *pphead;
		*pphead = del->next;
		free(del);
	}
}

 When judging multiple data, we only need to create a new structure variable to save *pphead, that is, save the plist, and then assign the address of the next node to *pphead, making the next node the first node. Finally, release the del before the operation to complete the head deletion operation; 

6. Data search

It is not too difficult to find the linked list data, we only need to traverse the data in the linked list

//数据查找
SLTNode* SLTFind(SLTNode* phead, SLDatatype x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL; 
}

Redefine a structure pointer cur, assign the value of phead to cur, and we can operate on cur;

Use the while loop to traverse the structure. If the data in the structure is equal to the number x we ​​want to find, just return x;

Return empty if not found;

When we want to use it in the main function, we should pay attention that its return type is a pointer type, so we need to define a pointer type in the main function to accept it;

 

Since this number is found and its pointer is returned, I can modify its value through this function (as shown above),

The effect of operation is as follows

 It can be seen that we changed 3 to 30 after finding 3;

7. Insert at any position

This needs to be consistent with the insertion in the sequence table, and it needs to be inserted at the position before the input number;

Similarly, we also need to use assert to assert whether the position of pos and the position of phead are empty;

The specific code is as follows

//任意位置插入
void SLInsert(SLTNode** pphead, SLTNode* pos,SLDatatype x)
{
	assert(pphead);
	assert(pos);
	if (*pphead == pos)
	{
		SLPuchBack(pphead, x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}


		SLTNode* newnode= BuyLTNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}

When the position we want to insert the number is equal to the starting position of the linked list, we insert the end to realize the insertion of data;

In other cases, we need to redefine a structure pointer to copy the address of plist to the newly defined pointer;

Next, use the normal operation when inserting, and judge whether prev can be connected to the next node through conditions;

The last is to exchange the content of the pointer to realize the operation of adding data in the linked list

 
The above is about adding, deleting, checking and modifying the single-linked list. If it is helpful to you, please support Sanlian. Thank you for reading.

Guess you like

Origin blog.csdn.net/wangduduniubi/article/details/130264527