您现在的位置是:首页 >学无止境 >数据结构入门(二)——单链表(增,删,查,改)网站首页学无止境

数据结构入门(二)——单链表(增,删,查,改)

吃火锅的腿腿 2023-06-17 20:00:02
简介数据结构入门(二)——单链表(增,删,查,改)

1.单链表的概念

概念:链表是一种物理存储结构非连续、非顺序的存储结构,但链表在逻辑上连续的,顺序的,而数据元素的逻辑顺序是通过链表中的指针连接次序实现的。

1.2链表的结构

我们给int重新定义一下新类型叫做SLDataType,这里我们要说明一下,如果我们想要改变数据类型在这里直接就可以改变,不需要在程序中挨个改变

 这里面SLTDataType data是储存数据用的,而*next本质是一个指针,实际储存的是下一个数据的地址,也是单链表中起到 “ 桥梁 ”的主要部分

我们实际就是这样储存的,是一个内存块内储存着下一个内存的地址


 2.单链表的实现

我们先把函数的声明给大家

#pragma once
#include<string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>

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

void SLTPrint(SLTNode* phead);//打印单链表i
void SLPushFront(SLTNode** pphead, SLTDataType x);//头插
void SLPushBack(SLTNode** pphead, SLTDataType x);//尾插

void SLPopFront(SLTNode** pphead);//头删
void SLPopBack(SLTNode** pphead);//尾删

void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);//在pos位置之前插入
void SLInsertAfter(SLTNode* pos, SLTDataType x);//在pos后面插入

void SLErase(SLTNode** pphead, SLTNode* pos);//单链表结点删除
void SLEraseAfter(SLTNode* pos);//结点后面删除

2.1内存的开辟

这里我想就不用多说了直接向内存中申请空间开辟内存块

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;
}

2.2尾插

我们在进行尾插的时候回有两种情况,第一个情况是你传过来的是空指针,那么我们就需要开辟一块空间来让pphead中plist来存放这个空间的地址

第二个情况:如果这里面传的不是空指针,那么我们就要创建一个tail(让tail代替pphead来一个一个的走),起到链接的作用,再让新开辟的内存地址,存放在tail的后面,让tail指向下一个结点,直达为NULL时结束

void SLPushBack(SLTNode** pphead, SLTDataType x)
{
    //申请内存
	SLTNode* newnode = BuyLTNode(x);
    
    //第一个情况
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
    
    //第二个情况
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

2.3头插

我们要实现头插思路要和尾插相反,就是让pphead的地址赋给我们新创建内存块旁边的空间里,这样我们就实现了新内存找到老内存的一个过程,之后我们在把新内存的地址赋给我们pphead让指针指向新内存块的地址,这样就形成一个头插的循环

void SLPushFront(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = BuyLTNode(x);

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

2.4尾删

这里和前面一样,核心思想是什么?核心思想就是使指向最后一个内存块的指针为NULL,将最后一个内存块释放掉,就可以了

void SLPopBack(SLTNode** pphead)
{
	assert(*pphead);
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else 
	{
		SLTNode* tail = *pphead;
		while (tail->next->next)
		{
			tail = tail->next;
		}

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

2.5头删

void SLPopFront(SLTNode** pphead)
{
	assert(pphead);
	SLTNode* del = *pphead;
	*pphead = (*pphead)->next;
	free(del);
}

2.6中间插入(pos之前)

我们这里写一个while循环首先要找到你要插入的位置,这时我们创建一个空间让你要插入位置前面的指针指向你这个新的空间,这个新空间的尾部指向你之前原本这个位置的地址,这样中间插入就完成了


void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	assert(pos);
	if (*pphead == pos)
	{
		SLPushFront(pphead, x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		SLTNode* newnode = BuyLTNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}

}

2.7中间插入(pos之后)

我们在找到pos位置时,让他旁边的内存地址存入到新内存旁边的地址,再让新内存的地址赋给我们pos旁边的位置,这样中间插入就完成了

void SLInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);

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

2.8删除结点

void SLTErase(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);
	}
}

2.9删除后面的结点

void SLEraseAfter(SLTNode* pos)
{
	assert(pos);
	assert(pos->next);

	SLTNode* next = pos->next;
	pos->next = next->next;
	free(next);
}

2.10打印单链表

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

3.代码总结

SList.c

#include "SList.h"

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

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 SLPushFront(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = BuyLTNode(x);

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

void SLPushBack(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = BuyLTNode(x);

	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

void SLPopFront(SLTNode** pphead)
{
	assert(pphead);
	SLTNode* del = *pphead;
	*pphead = (*pphead)->next;
	free(del);
}

void SLPopBack(SLTNode** pphead)
{
	assert(*pphead);
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else 
	{
		SLTNode* tail = *pphead;
		while (tail->next->next)
		{
			tail = tail->next;
		}

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

void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	assert(pos);
	if (*pphead == pos)
	{
		SLPushFront(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;
}

void SLTErase(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);
}

SList.h

#pragma once
#include<string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>

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

void SLTPrint(SLTNode* phead);//打印单链表i
void SLPushFront(SLTNode** pphead, SLTDataType x);//头插
void SLPushBack(SLTNode** pphead, SLTDataType x);//尾插

void SLPopFront(SLTNode** pphead);//头删
void SLPopBack(SLTNode** pphead);//尾删

void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);//在pos位置之前插入
void SLInsertAfter(SLTNode* pos, SLTDataType x);//在pos后面插入

void SLErase(SLTNode** pphead, SLTNode* pos);//单链表结点删除
void SLEraseAfter(SLTNode* pos);//结点后面删除

这里面我就不写test.c的内容了


以上就是本次单链表的文章分享,喜欢的话请三联支持一下,感谢您的支持


风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。