当前位置: 首页 > article >正文

【数据结构初阶】单链表SLlist

描述

不同于顺序表,顺序表的数据是存储在一个连续的空间里的

链表它是链接起来的结构体地址。

所以我们不用像顺序表一样先创建一块空间出来,而是创建一个能存数据节点和节点与下一个节点之间的连接

所以:“一个能存数据节点”我们用int Data表示;

与下一个节点之间的连接”:我们用指针连接起来。

组织

打印

为了方便测试,我们先写个打印单链表的函数;

1.这个打印函数需要断言吗?
不需要,即使结构体为空,也能打印,只不过是没有数据而已,这时打印出来的就是空的。如果能打印出来,但是却断言报错,不太合适。

2.怎么打印?
用一个指针cur指向结构体,用while循环打印出来,当cur指向的结构体为空时,停止打印。

3.while的判断条件可以是while(cur->next)吗?
不可以,因为这样最后一个的数据就打印不出来了。

4.在while循环中,让cur指向下一个结构体,可以用cur++吗?
不可以,不同于顺序表,顺序表的数据是存储在一个连续的空间里的,链表它是链接起来的结构体地址,是节点与节点之间的连接,cur++无法指向下一个节点。
 

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

1.尾插

每在插入一个数据之前,首先得为这个结构体开辟一个结点

用malloc开辟,由于插入数据时我们都要进行开辟一个结点的操作,所以我们可以打包成一个函数


进行尾插首先就是要找到尾节点

找尾分两种情况:

1. 当*pphead本身为空时,就直接将newnode插入就可以了;

2. *pphead本身不为空时,只要找到tail->next为空的,那个就是结构体的尾了


当pphead不为空时,找尾while循环的判断条件可以写成这样tail!=NULL与插入结点时tail=newnode吗?
不可以,因为这样就无法保持链接状态

尾插的本质是:原为节点中存储新的尾节点的地址

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

}

void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	//创建节点
	SLTNode* newnode = SLTNewnode(x);
	if (newnode == NULL)
	{
		return;
	}

	//情况一:pphead为空
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	//情况二:pphead不为空
	else
	{
		//找到尾节点
		SLTNode* tail = *pphead;
		while (tail->next)
		{
			tail = tail->next;
		}

		tail->next = newnode;
	}
}

2.尾删

1.尾删需要断言吗?
需要,因为如果链表为空,是删不了的;


2.尾删的思路
尾删分三种讨论:

1.如果链表为空,删不了,我们这里断言判断一下
2.链表中只有一个数据
3.链表中的数据为一个以上

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

}

3.头插

1.头插需要断言吗?
但是当链表为空的时候,可以插入数据,*pphead是不需要断言的。


2.头插的思路
首先先要创建一个结点,将结点的next与链表的第一个指针链接起来。最后要注意把链表的头给改成newnode。

void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = SLTNewnode(x);
	if (newnode == NULL)
	{
		return;
	}

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


4.头删

1.头删需要断言吗?
空链表不能删除,所以*pphead也需要断言。

头删的思路:

void SLTPopFront(SLTNode** pphead)
{
	assert(*pphead);
	

	SLTNode* head = *pphead;
	*pphead = head->next;
	free(head);
	head = NULL;

}


5.查找

1.查找需要断言吗?
不需要,链表为空就返回NULL;


2.查找的思路
当链表的cur不为空,就继续逐一比对,找到了就返回cur,没找到就指向下一个,直到cur指向空;

如果还没找到,就返回NULL;

这里的phead用一级指针就可以了,因为不用修改里面的任何值;

SLTNode* SListFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->Data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	//如果没找到就返回NULL
	return NULL;
}



6.指定位置后插入

1.需要断言吗?
指定的位置pos不能为空,所以需要断言;


2.思路
创建一个新结点newnode,然后先让newnode->next = pos->next;让newnode的后面链接起来,在让pos和newnode链接起来pos->next = newnode;;

反过来写的话,由于pos->next已经被改了,所以不能是pos与newnode链接,插入就会失败;


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


	SLTNode* newnode = SLTNewnode(x);
	if (newnode == NULL)
	{
		return;
	}

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

}



7.指定位置后删除

1.需要断言吗?
指定的位置pos不能为空,所以需要断言;

void SListEraseAfter( SLTNode* pos)
{
	assert(pos);

	if (pos->next == NULL)
	{
		printf("已是最后一个,不能删\n");
		return;
	}

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

8.链表的销毁

思路
结点逐一free,最后记得把*pphead改为最后的cur。

void SLTDel(SLTNode** pphead)
{
	assert(*pphead);

	SLTNode* cur = *pphead;
	SLTNode* prev = cur;
	while (cur)
	{
		prev = cur->next;
		free(cur);
		cur = prev;
	}
	*pphead = cur;
}

关于其他的一些细节问题

为什么不像顺序表一样写初始化函数?

可写可不写,这里结构体里面的数据比较少,我们在测试代码的时候直接用指针指向了一块空间。

为什么要传二级指针?

想要改变变量的数值而不会因为栈帧的销毁而销毁,就得取到该变量的地址;

什么时候要传二级指针,什么时候要传一级指针?

想要改变变量里的数值就要传址调用,二级指针用来接收一级指针;

如果只是简单的访问就用传值调用

整个程序

.h文件

#define _CRT_SECURE_NO_WARNINGS 1

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


typedef int SLTDataType;

typedef struct SLTlist
{
	SLTDataType Data;	
	struct SLTlist * next;

}SLTNode;

void SLTPrint(SLTNode* phead);
void SLTPushBack(SLTNode** pphead, SLTDataType x);
void SLTPopBack(SLTNode** pphead);
void SLTPushFront(SLTNode** pphead, SLTDataType x);
void SLTPopFront(SLTNode** pphead);
SLTNode* SListFind(SLTNode* phead, SLTDataType x);
void SLTInsertAfter( SLTNode* pos, SLTDataType x);
void SListEraseAfter( SLTNode* pos);
void SLTDel(SLTNode** pphead);

.c文件

#include"SLlist.h"

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

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

}

void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	//创建节点
	SLTNode* newnode = SLTNewnode(x);
	if (newnode == NULL)
	{
		return;
	}

	//情况一:pphead为空
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	//情况二:pphead不为空
	else
	{
		//找到尾节点
		SLTNode* tail = *pphead;
		while (tail->next)
		{
			tail = tail->next;
		}

		tail->next = newnode;
	}
}

void SLTPopBack(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 SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = SLTNewnode(x);
	if (newnode == NULL)
	{
		return;
	}

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

void SLTPopFront(SLTNode** pphead)
{
	assert(*pphead);
	

	SLTNode* head = *pphead;
	*pphead = head->next;
	free(head);
	head = NULL;

}

SLTNode* SListFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->Data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	//如果没找到就返回NULL
	return NULL;
}

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


	SLTNode* newnode = SLTNewnode(x);
	if (newnode == NULL)
	{
		return;
	}

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

}

void SListEraseAfter( SLTNode* pos)
{
	assert(pos);

	if (pos->next == NULL)
	{
		printf("已是最后一个,不能删\n");
		return;
	}

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

void SLTDel(SLTNode** pphead)
{
	assert(*pphead);

	SLTNode* cur = *pphead;
	SLTNode* prev = cur;
	while (cur)
	{
		prev = cur->next;
		free(cur);
		cur = prev;
	}
	*pphead = cur;
}


http://www.kler.cn/news/135094.html

相关文章:

  • STM32——STM32Cubemx的学习使用总结
  • C++类与对象(1)—初步认识
  • [Kettle] 生成随机数
  • 【LeetCode刷题】--9.回文数
  • Figma 插件学习(一)
  • STM32框架之按键扫描新思路
  • Burpsuite抓HTTPS证书导入问题
  • 《rPPG》——(1)PyTorch——Windows环境配置
  • 如何使用MybatisPlus进行数据分页显示
  • SpringMVC log4j1升级log4j2
  • 数据采集与大数据架构分享
  • uniapp App 端 版本更新检测
  • 刷题方法论
  • 中国智能音箱市场销量下降,百度稳居第一 /中国即评出10个大模型创新案例 |魔法半周报
  • Dockerfile基础
  • 前台查看日志功能
  • RK3568 + YT 9215交换机芯片,MAC TO MAC 调试记录
  • 有趣的按钮分享
  • 【备忘录】Docker容器、镜像删除与资源清理命令
  • PC3329L DC-DC降压 10V-100V输入3A大流输出带EN功能实现零功耗只需极少元器件
  • 如何解决 Critical dependency: the request of a dependency is an expression ?
  • 全国行业就业数据,了解第一二三产业的就业人数~
  • 打不开github网页解决方法
  • MFC 对话框
  • Scala--- Actor通信模型
  • sqli-labs关卡18(基于http头部报错盲注)通关思路
  • 同一台Linux同时安装MYSQL5.7和MYSQL8(第二篇)
  • 2024年全网最全的Jmeter教程:jmeter_BeanShell脚本通过BeanShell进行加解密方法
  • C语言——冒泡排序
  • Flume学习笔记(3)—— Flume 自定义组件