(Pure c) data structure ------> linked list (detailed explanation)

Table of contents

1. Definition of linked list

        1. The structure of the linked list.

        2. Why should there be linked lists and the advantages of linked lists?

2. Common interfaces of headless one-way linked list

        1. Head plug \ tail plug

        2. Head delete \ tail delete

        3. Destroy linked list / print linked list

        4. Insert a value after pos position

        5. Eliminate the value after pos position

        6. Find the value in the linked list and return its address

       7. Create a dynamically opened node

3. Comparison of advantages and disadvantages of sequential list and linked list.


        Article start->:

    1. Definition of linked list

First of all, we have already learned the data structure of sequence table before         learning singly linked list . We know that when using sequence table, we need to expand when we don’t have enough space , and when we perform head insertion and head deletion . We need to move elements. It is very time-consuming to perform these operations , and a certain amount of space may be wasted during expansion . Of course, this is also the shortcoming of the sequence table. In order to solve these troubles, we created another data. Structure --> (linked list) .

The definition         of the linked list : the elements are continuous in the logical structure , but the linked list is a discontinuous and non-sequential storage structure in the actual physical structure , and the logical order of the data elements is actually connected by pointers .

        The following is the normal logical structure

 

        Therefore, the structure of linked list can easily solve the problem of sequence table. It does not need to expand when managing data, but when we need space, it will open up a space and then we only need to change the pointing of the pointer. The data can be linked, which also saves the time of moving elements.

        Summarize the advantages of the single-linked list: when using the memory space, the single-linked list does not need to be expanded like a sequential table, but when we need space, it will automatically open up a space in the memory, and there is no need to move elements when inserting elements , we only need to change the pointing of the pointer to realize the logical order management of the linked list.

        In fact, the biggest advantage of the linked list is that it can be inserted and deleted.

        The following figure is the operation when we insert elements:->

        

 In this way, we don't need to move the element, just change the point of the pointer.

        

The next step is our focus on entering the detailed explanation of commonly used interfaces -->

2. Common interfaces of headless singly linked list

      First of all, what we need to understand is headless : the so-called headless means that we did not apply for a node first , but our head pointer directly points to the first node . If the linked list is empty , then our head pointer points to is a null pointer.

        First, let's take a look at the structure of the linked list

           Logically -->

        In fact -->

        

         In fact, there is no such thing as a pointer in our memory, it is just introduced for the convenience of understanding the data structure of the linked list.

The code for the linked list definition is as follows:

 typedef int SlistDataType;
    typedef struct Slist
{
    struct Slist* next;
    SlistDataType data;
}ST;

1. Destroy the linked list/print the linked list: Here we need to pay attention to the difference between actual participation and formal parameters , otherwise our operation may have problems. If we print, we can just traverse it directly.

        This interface is necessary, because we must have the structure of the linked list before creating the linked list, and destroying the linked list is to prevent memory leaks in our program.

        Destroy the linked list: Because the space we apply for is the space opened up on the heap area, and the space opened up on the heap area needs to be released by ourselves, and the space opened up by the linked list is not a continuous block of space, so we need to come Traversing the linked list ensures that we will fully release the space we have opened up to prevent memory leaks.

        

void DestorySlist(STNode* plist)
{
	assert(plist);
	//这里我们需要一个一个的删除链表的结点
	while (plist != NULL)
	{
		STNode* newplist = plist->next;//存放下一个结点
		free(plist);
		plist = newplist;
	}

}

        Print linked list: we only need to traverse until the node is empty

        Here is the code that prints:

        

void PrintSlist(STNode* plist)
{
	assert(plist);
	while (plist != NULL)
	{
		printf("%d->", plist->data);
		plist = plist->next;
	}
	printf("NULL\n");
}

     

  2. Dynamically create a node: When we insert related operations, we need to apply for a space to store the value to be inserted, so this step cannot be omitted:

        Let's go directly to the code:

        

STNode* BuySlistNode(SlistDataType x)
{
	STNode* newnode = (STNode*)malloc(sizeof(STNode));
	//判断开辟的空间成功没
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;//这样设计可以使得我们最后一个结点不需要在进行单独的设空
	return newnode;

}

 3. Tail plug/head plug: Here we need to use the secondary pointer, because we know that changing the structure requires a structure pointer, and changing the structure pointer, we need the pointer of the structure pointer, that is, the secondary pointer to use. When we are doing the first insertion of tail insertion, we need to change the structure pointer, so we have to use a secondary pointer. , and the head plug needs to change the structure pointer every time

        The following code: tail plug

        

//注意二级指针
void PushBackSlist(STNode** pplist, SlistDataType x)
{
	STNode* newnode = BuySlistNode(x);
	if (*pplist == NULL)
	{
		//插入第一个
		*pplist = newnode;
	}
	else
	{
		STNode* tail = *pplist;
		//我们得先找尾指针
		while(tail->next!=NULL)
		{ 
			
			tail = tail->next;
		}
		tail->next = newnode;

	}
}

        Pictures of the tail plug:

     

 

  Head insertion code: Here we need to pay attention to that when changing the pointer, we need to first point the next pointer of the new node to the head, and then change the head. If it is reversed, we will make newnode->next point to itself.

        

void PushFrontSlist(STNode** pplist, SlistDataType x)
{
	STNode* newnode = BuySlistNode(x);
	newnode->data = x;
	newnode->next = *pplist;
	*pplist = newnode;

}

        Header illustration:

4. Head delete/tail delete

        Head deletion: We first judge whether the linked list is empty before deleting. If it is empty, we will report an error. If it is not empty, we will continue to operate. When there is only one node in the linked list, then we need to modify the structure body pointer.

        Here is the code:

void PopFrontSlist(STNode** pplist)
{
	//为空
	assert(*pplist);
	//一个结点
	if ((*pplist)->next == NULL)
	{
		STNode* del = *pplist;
		free(del);
		*pplist = NULL;
	}
	//多个结点
	else
	{
		STNode* del = *pplist;
		STNode* newnode = (*pplist)->next;
		free(del);
		*pplist = newnode;
	}
}

        Pictures during the test:

        

         Tail deletion: Here we must first determine whether the linked list is empty, and then if there is only one element, we need to change the structure pointer, and for others, we only need to point the previous pointer to NULL.

        Tail deletion code:

        

void PopBackSlist(STNode** pplist)
{
	//判断是否为空
	assert(*pplist);
	//一个结点
	if ((*pplist)->next == NULL)
	{
		STNode* del = *pplist;
		free(del);
		*pplist = NULL;
	}
	else
	{
		STNode* cur = *pplist;
		//找前一个
		while (cur->next->next != NULL)
		{
			cur = cur->next;
		}
		free(cur->next);
		cur->next = NULL;
	}
}

        Tail deletion test:

        

         5. Search interface of the linked list: if found, return the address of this value, if not found, print not found, idea: we only need to traverse the array.

STNode* FindSlist(STNode* plist, SlistDataType x)
{
	assert(plist);
	STNode* cur = plist;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	printf("未找到\n");
	return NULL;
}

        test:

        

         6. Insert after the pos position, and remove the interface after the pos position

        Insert: first we have to judge whether pos is meaningful, if so, it means meaningful, we save the node after pos position, then pos->next=newnode, newnode->next=posafter;

        code:

void InsertafterSlist( STNode* pos, SlistDataType x)
{

		assert(pos);

		STNode* posafter = pos->next;
		STNode* newnode = BuySlistNode(x);
		pos->next = newnode;
		newnode->next = posafter;
}

        test:

                

      Delete the value after pos: We first judge whether pos is meaningful, and if it is meaningful, directly delete the value after pos, pos->next=NUll;

        code:

        

void EraseafterSlist(STNode* pos)
{
	assert(pos);
	STNode* posafter = pos->next;
	pos->next =posafter->next ;
	free(posafter);

}

         Test chart:

        

        Note: The latter three interfaces are usually used together.

        Here we have almost finished explaining the commonly used interfaces, and then proceed to the last part of the explanation --->

    Three: Comparison of the advantages and disadvantages of sequential lists and linked lists

        Advantages of the sequence table : the sequence table can randomly access the address of the opened space, and it is a continuous space in the memory, supports random access, and has a relatively high cache utilization.

Disadvantage: It needs to be expanded                        when inserting again , and the underlying principle of expansion is actually very troublesome. Here you can see the realloc expansion of the design of the dynamic memory development that I wrote earlier. I won’t introduce it in detail here, and after the expansion It will also waste space. Secondly, we need to move elements when performing head deletion/head insertion . This will cause a lot of space to be continuously used, wasting a lot of space , and the expansion may expand a lot, and arbitrary insertion and deletion of elements The efficiency is low, and the time complexity is O(n).

        Sequence tables are suitable for frequent access scenarios.

       The advantage of the linked list : when inserting and deleting at any position , there is no need to move elements, the time complexity is O(1) , and there is no need to expand the operation, just add a new node, and then change the pointing of the pointer . .        

        Disadvantage: It cannot access memory randomly . And the cache utilization is low .

        Linked list is suitable for insertion and deletion at any position.

        All in all: each data structure has its own advantages and disadvantages, and these data structures are suitable for different application scenarios.

                 ~~This chapter is over, thank you for your patience in watching, if you find it useful, you can give it a thumbs up!

                

 

Guess you like

Origin blog.csdn.net/2201_75964502/article/details/132510629