Implementation of single linked list of primary data structure (4)



The concept and structure of linked list

The concept of linked list : Linked list is a non-sequential and non-sequential storage structure in physical storage structure. The logical order of data elements is realized through the link order of pointers in the linked list.


1. What is the logical structure of the linked list?

The so-called logical structure is actually to enable us to better understand what this thing is? Then let's use pictures to understand and understand! ! In the singly linked list, there are two quantities stored, one is the address quantity and the other is the data quantity. As shown in the figure, this is a leading singly linked list.

insert image description here

Second, the initialization of the linked list

2.1 Schematic of Linked List Initialization

As mentioned in the figure above, there is a data volume and an address volume in the singly linked list, so when we define the structure, we should set a data data volume and a pointer to store the address volume defined by ourselves.

2.2 Linked list initialization code implementation

code show as below:

typedef int SLTDataType;

typedef struct SListnode
{
    
    
	SLTDataType data;
	struct SListnode* next;
}SLTNode;

And also need to define several header files as shown below

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

Here is a website CPlusplus that queries header files


3. Definition of various interface functions of linked list

void SLTPrint(SLTNode* phead);
void SLTPushBack(SLTNode** pphead, SLTDataType x);
void SLTPushFront(SLTNode** pphead, SLTDataType x);

void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);

SLTNode* SLTFind(SLTNode* phead, SLTDataType x);

//在pos之前插入
void SLInsert(SLTNode** pphead,SLTNode* pos, SLTDataType x);
void SLInsertAfter(SLTNode* pos, SLTDataType x);
//删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos);
void SLEraseAfter(SLTNode* pos);

void SLTDestroy(SLTNode* phead);

Fourth, the code implementation of various interface functions of the linked list

4.1 Print code implementation of linked list

Because we are writing a single linked list with the head, we can traverse from the beginning. We define a structure pointer to store the head node, because the head node stores the address of the next location at this time, so we only need to take a pointer to receive it.

Then we should think about it at this time. Although we are printing data through traversal, do we have to think about the end condition? If we use cur->next == NULL as the end condition, are we thinking wrong? Let's draw a picture to explain! From the picture, if we use cur->next as the end condition, then we will not be able to print the last number, so we think wrong

insert image description here

Let's take a look at the picture below, and we found that we can traverse all of them by traversing cur itself. Well, here we can explain it!

insert image description here

4.1.1 Implementation of printing code

void SLTPrint(SLTNode* phead)
{
    
    
	SLTNode* cur = phead;
	while (cur)
	{
    
    
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

4.2 Tail insertion code implementation of linked list

Tail insertion, as the name implies, is to insert at the end, so how should we implement the tail insertion of the singly linked list? We still draw a picture to understand, as shown in the figure below.

We want to insert at the end, should we traverse to the end node first? Yes, that's right! But here we have to pay attention to the conditions for the end of our traversal, why do you say that? Assuming that if we traverse as we printed above, we will traverse into wild pointers, so the condition of our traverse is cur->next, so it will be solved!

insert image description here

Here we have to consider two situations.
1. How should we deal with this linked list when it is empty? We just assign the newly created node to our head directly.
2. That is, normal insertion! Let the pointer of the last node point to the new node, and then change the position of the last node to complete!

4.2.1 Tail plug code implementation

void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
    
    
	assert(pphead);
	SLTNode* newnode = BuyLTNode(x);
	if ((*pphead) == NULL)
	{
    
    
		*pphead = newnode;
	}
	else
	{
    
    
		SLTNode* tail = *pphead;
		while (tail->next)
		{
    
    
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

4.3 Header code implementation of linked list

Regarding header insertion, we should deal with it as follows:
1. Because we are a singly linked list with a head node, and because our head node stores the address of the first node, we only need to assign the address stored in the head node to The next position of the newly opened node. At this time, the new node points to the position of the original first node.
2. Because the head node stores the address of the first node, we need to assign the address of the newly opened node to Head knotted, this completes the head plug!

1 Graphic explanation: Before inserting:

insert image description here

2. Inserting:

insert image description here

3. End insertion:
insert image description here

4.3.1 Realization of plug-in code

void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
    
    
	assert(pphead);
	SLTNode* newnode = BuyLTNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

4.4 Implementation of BuyLTNode function

This function means to dynamically open up a node on the heap!

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

4.5 Tail deletion code implementation of linked list

In a singly linked list, the efficiency of tail deletion is actually very poor, because the singly linked list needs to be traversed all the time to search, so the efficiency is very expensive.

In the process of tail deletion, we also need to consider two situations!
The first case: how should we delete when there is only one node in the linked list
The second case: how should we delete when there are multiple nodes in the linked list?

When there is only one node, we should free the node directly, because our nodes are dynamically opened, so we use the free function to destroy the node

When there are multiple nodes, we should use two pointers, one at the front and one at the back. When the previous node is empty, it means that we have found the tail, and we can free the tail node!

4.5.1 Realization of tail deletion code

void SLTPopBack(SLTNode** pphead)
{
    
    
	//当只有一个节点的时候
	//多个节点
	assert(pphead);
	assert(*pphead);
	if ((*pphead)->next == NULL)
	{
    
    
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
    
    
		SLTNode* tail = *pphead;
		SLTNode* prev = NULL;
		while (tail->next)
		{
    
    
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		prev->next = NULL;
	}
}

4.6 Implementation of head deletion code of linked list

As shown in the figure below, let pphead take a step forward first, find a previous node in advance, then free it, then assign pphead to prev, and then let *pphead go forward!

insert image description here
insert image description here

4.6.1 Realization of plug-in code

void SLTPopFront(SLTNode** pphead)
{
    
    
	//空链表
	assert(*pphead);
	assert(pphead);
	SLTNode* prev = *pphead;
	*pphead = prev->next;
	free(prev);
}

Five, the realization of the complete code

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"listnode.h"

void test1()
{
    
    
	SLTNode* plist = NULL;
	SLTPushFront(&plist, 0);
	SLTPushFront(&plist, 1);
	SLTPushFront(&plist, 2);
	SLTPushFront(&plist, 3);
	SLTPrint(plist);
	SLTPushBack(&plist, -1);
	SLTPrint(plist);
}
void test2()
{
    
    
	SLTNode* plist = NULL;
	SLTPushBack(&plist, -1);
	SLTPushBack(&plist, 0);
	SLTPushBack(&plist, 1);
	SLTPrint(plist);
	SLTPopBack(&plist);
	SLTPopBack(&plist);
	SLTPopBack(&plist);
	SLTPrint(plist);
}
void test3()
{
    
    
	SLTNode* plist = NULL;
	SLTPushBack(&plist, -1);
	SLTPushBack(&plist, 0);
	SLTPushBack(&plist, 1);
	SLTPrint(plist);
	SLTPopFront(&plist);
	SLTPopFront(&plist);
	SLTPopFront(&plist);
	SLTPrint(plist);
}
void test4()
{
    
    
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	SLTPrint(plist);
	SLTNode* pos = SLTFind(plist, 3);
	pos->data = 30;
	SLInsert(&plist, pos, 20);
	SLTPrint(plist);
	SLInsertAfter(pos, 40);
	SLTPrint(plist);
	SLEraseAfter(pos);
	SLTPrint(plist);
	SLTDestroy(plist);
}
int main()
{
    
    
	test4();
	return 0;
}

listnode.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "listnode.h"

void SLTPrint(SLTNode* phead)
{
    
    
	SLTNode* cur = phead;
	while (cur)
	{
    
    
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

SLTNode* BuyLTNode(SLTDataType x)
{
    
    
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
    
    
		perror("malloc fail");
		return NULL;
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
    
    
	assert(pphead);
	SLTNode* newnode = BuyLTNode(x);
	if ((*pphead) == NULL)
	{
    
    
		*pphead = newnode;
	}
	else
	{
    
    
		SLTNode* tail = *pphead;
		while (tail->next)
		{
    
    
			tail = tail->next;
		}
		tail->next = newnode;
	}
}
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
    
    
	assert(pphead);
	SLTNode* newnode = BuyLTNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}
//尾删的第一种方式
void SLTPopBack(SLTNode** pphead)
{
    
    
	//当只有一个节点的时候
	//多个节点
	assert(pphead);
	assert(*pphead);
	if ((*pphead)->next == NULL)
	{
    
    
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
    
    
		SLTNode* tail = *pphead;
		SLTNode* prev = NULL;
		while (tail->next)
		{
    
    
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		prev->next = NULL;
	}
}
//尾删的第二种方式
/*
void SLTPopBack(SLTNode** pphead)
{
	SLTNode* tail = *pphead;
	while (tail->next->next)
	{
		tail = tail->next;
	}
	free(tail->next);
	tail->next = NULL;
}
*/
void SLTPopFront(SLTNode** pphead)
{
    
    
	//空链表
	assert(*pphead);
	assert(pphead);
	SLTNode* prev = *pphead;
	*pphead = prev->next;
	free(prev);
	//一个节点
	//if ((*pphead)->next == NULL)
	//{
    
    
	//	free(*pphead);
	//	*pphead = NULL;
	//}
	多个节点
	//else
	//{
    
    
	//	SLTNode* prev = *pphead;
	//	*pphead = prev->next;
	//	free(prev);
	//}
}

SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
    
    
	SLTNode* cur = phead;
	while (cur)
	{
    
    
		if (cur->data == x)
			return cur;
		cur = cur->next;
	}
	return NULL;
}


//在pos之前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
    
    
	assert(pphead);
	assert(pos);
	if (*pphead == pos)
	{
    
    
		SLTPushFront(pphead, x);
	}
	else
	{
    
    
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
    
    
			prev = prev->next;
		}
		SLTNode* newnode = BuyLTNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
	
}
void SLInsertAfter(SLTNode* pos, SLTDataType x)
{
    
    
	assert(pos);
	SLTNode* newnode = BuyLTNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}
//删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos)
{
    
    
	assert(pphead);
	assert(pos);
	if (pos == *pphead)
	{
    
    
		SLTPopFront(pphead);
	}
	else
	{
    
    
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
    
    
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
	}
}
void SLEraseAfter(SLTNode* pos)
{
    
    
	assert(pos);
	assert(pos->next);
	SLTNode* next = pos->next;
	pos->next = next->next;
	free(next);
}

void SLTDestroy(SLTNode* phead)
{
    
    
	SLTNode* cur = phead;
	while (cur)
	{
    
    
		phead = phead->next;
		free(cur);
		cur = phead;
	}
}

listnode.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLTDataType;

typedef struct SListnode
{
    
    
	SLTDataType data;
	struct SListnode* next;
}SLTNode;
 
void SLTPrint(SLTNode* phead);
void SLTPushBack(SLTNode** pphead, SLTDataType x);
void SLTPushFront(SLTNode** pphead, SLTDataType x);

void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);

SLTNode* SLTFind(SLTNode* phead, SLTDataType x);

//在pos之前插入
void SLInsert(SLTNode** pphead,SLTNode* pos, SLTDataType x);
void SLInsertAfter(SLTNode* pos, SLTDataType x);
//删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos);
void SLEraseAfter(SLTNode* pos);

void SLTDestroy(SLTNode* phead);

Summarize

Well, I will write out most of the solutions for today's single-linked list in detail! See you next time! Hey see you every day! ! !

Guess you like

Origin blog.csdn.net/m0_64361522/article/details/130772531