当前位置:首页 » 《随便一记》 » 正文

数据结构学习笔记--单链表_Zero__two_的博客

6 人参与  2022年03月13日 15:07  分类 : 《随便一记》  评论

点击全文阅读


1.单链表概念

单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(s数据元素的映象) +指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。


2.链表分类

链表的结构多种多样有三个大分类:

1.单向、双向

2.带头、不带头

3.循环、不循环

而三组之间又可以互相组合,使得链表的结构多达8种,本篇的单链表主要是指单向不带头不循环链表,这也是本篇的重点。

2.1单向链表

单向链表:指链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始,只能通过前一个结点找到后一结点,而不能从后一结点找到前一结点。

2.2双向链表

双向链表:指链表的每个数据中结点都有两个指针,分别指向直接后继和直接前驱,它能从双向链表中的任意一个结点开始,并且可以方便地访问它的前驱结点和后继结点。

2.3不带头链表

不带头链表:指链表以一个结点结构的指针开头,我们存储的数据直接从该指针指向的位置存储,该链表实现相较于带头结点稍微复杂,是各oj题与面试题的常客。

2.4带头链表

带头链表:指链表的以一个不存任何数据的结点开始,我们称之为头结点,而我们存储的数据则从头节点的下一结点开始存储,这样就能有效地避免因链表为空带来的各种问题。

2.5不循环链表

不循环链表:指链表的最后一个结点指向空,使得当链表走到空时我们就可以知道链表已经走到了尽头。

2.6循环链表

循环链表:指链表的最后一个结点指向链表的头结点,从而在逻辑形态中就像一个完整的闭环。


3.单链表实现

3.1单链表结构

typedef int SListDataType 
typedef struct SListNode
{
	SListDataType data;
	struct SListNode* next;
}SListNode;

SListNode:链表头结点代称。

SListDataType:指链表中每个结点存储的数据类型,只需要改动typedef int SListDataType 中的int就能该变节点内存放的数据类型。

data:存放类型结构体变量名

next:指向下一结点的指针。

3.2链表功能实现

文件:SList.c

3.2.1创建新结点

SListNode* BuySListNode(SListDataType x)	//创建新结点
{
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));    //开辟新结点的空间

	if (newnode == NULL)        //判断是否开辟成功					
	{
		printf("开辟内存失败\n");       
		exit(-1);
	}

	newnode->data = x;       //将新节点中需要存放的值放入结点的data中
	newnode->next = NULL;    //将新节点指向空
	return newnode;          //返回新结点指针
}

3.2.2链表打印

void SListPrint(SListNode* plist)			//链表打印
{											
	SListNode* cur = plist;                 //可以不用创建cur,单这样会使可读性更强

	while (cur != NULL)                     //链表不为空才打印,依次遍历打印
	{                                       //直到cur为NULL
		printf("%d ", cur->data);            
		cur = cur->next;
	}
	printf("\n");
}

3.2.3链表尾插

void SListPushBack(SListNode** pplist, SListDataType x)		//尾插
{
	SListNode* newnode = BuySListNode(x);

	if (*pplist == NULL)						//当链表为空
	{
		*pplist = BuySListNode(x);				//直接创建新节点,plist指向新结点
	}
	else
	{
		SListNode* backnode = *pplist;	
		while (backnode->next != NULL)
		{
			backnode = backnode->next;			//链表不为空找尾结点
		}
		backnode->next = newnode;				//在尾结点插入新结点
	}
}

3.2.4链表头插

void SListPushFront(SListNode** pplist, SListDataType x)	//头插
{
	SListNode* newnode = BuySListNode(x);	//创建新结点
	newnode->next = *pplist;				//新节点指向头指针
	*pplist = newnode;						//头指针指向新节点
}

3.2.5链表按位插入

void SListInsert(SListNode** pplist, int pos, SListDataType x)	//按位插入
{
	SListNode* cur = *pplist;
	SListNode* pre = NULL;
	SListNode* newnode;
	int i = 0;

	//特殊情况1:在首位置插入(链表为空)
	//特殊情况2:在首位置插入(链表不为空)

	if (pos == 0)
	{
		newnode = BuySListNode(x);
		*pplist = newnode;
		newnode->next = cur;
		//printf("在首位置插入\n");					//调试用
		return;
	}
	
	while (cur != NULL)
	{
		//特殊情况3:在除首位置及末位置插入
		if (pos == i)
		{
			newnode = BuySListNode(x);
			pre->next = newnode;
			newnode->next = cur;
			//printf("在除首位置及末位置插入\n");	//调试用
			return;
		}
		pre = cur;
		cur = cur->next;
		i++;
	}
	//特殊情况4:在末位置插入
	if (pos == i)
	{
		newnode = BuySListNode(x);
		pre->next = newnode;
		newnode->next = cur;
		//printf("在末位置插入\n");					//调试用
	}
	//特殊情况5:超出可插入范围
	else
	{
		printf("超出可插入范围\n");					//错误提示
		return;
	}
}

3.2.6链表尾删

void SListPopBack(SListNode** pplist)		//尾删
{
	SListNode* pre = NULL;
	SListNode* backnode = *pplist;

	//特殊情况1:当链表为空
	if (backnode == NULL)
	{	
		//printf("链表为空\n");				//调试用
		return;
	}

	//特殊情况2:当链表只有一个元素
	else if (backnode->next == NULL)
	{
		//printf("链表只有一个结点\n");		//调试用
		free(*pplist);
		*pplist = NULL;
	}

	//正常情况
	else
	{
		while (backnode->next != NULL)		//找尾
		{
			pre = backnode;
			backnode = backnode->next;
		}

		free(backnode);						//释放结点
		backnode = NULL;

		pre->next = NULL;					//上一结点的next指向NULL
	}
}

3.2.7链表头删

void SListPopFront(SListNode** pplist)		//头删
{
	SListNode* frontnode = *pplist;
	*pplist = frontnode->next;
	free(frontnode);
	frontnode = NULL;
}

3.2.8链表按位删除

void SListErase(SListNode** pplist, int pos)					//位删
{
	SListNode* cur = *pplist;
	SListNode* pre = NULL;
	int i = 0;

	//特殊情况1:当链表为空
	if (cur == NULL)
	{
		printf("链表为空\n");
		return;
	}

	//特殊情况2:在首位置删除
	else if (pos == 0)
	{
		*pplist = cur->next;
		free(cur);
		cur = NULL;
		printf("首位置删除\n");
		return;
	}

	//特殊情况3;中间与末位置删除
	while (cur != NULL)
	{	
		if (pos == i)
		{
			pre->next = cur->next;
			free(cur);
			cur = NULL;
			printf("当链表不为空删除\n");
			return;
		}
		pre = cur;
		cur = cur->next;
		i++;
	}

	//特殊情况4:超出可删除范围
	printf("超出可删除位置\n");
	return;
}

3.2.9链表按值查找

int SListFind(SListNode* plist, SListDataType x)	//按值查找
{
	int pos = 0;
	while (plist != NULL)
	{
		if (plist->data == x)							
			return pos;								//找到查找值返回位置
		else
		{
			pos++;
			plist = plist->next;					//没找到值往链表下一位走
		}
	}
	return - 1;										//没找到该值返回-1
}

3.3链表头文件

文件:"SList.h"

#pragma once

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

typedef int SListDataType;


typedef struct SListNode
{
	SListDataType data;
	struct SListNode* next;
}SListNode;


void SListPrint(SListNode* plist);								//打印		
void SListPushBack(SListNode** pplist, SListDataType x);		//尾插
void SListPushFront(SListNode** pplist, SListDataType x);		//头插
void SListInsert(SListNode** pplist, int pos, SListDataType x);	//按位插入
void SListPopBack(SListNode** pplist);							//尾删
void SListPopFront(SListNode** pplist);							//头删
void SListErase(SListNode** plist, int pos);					//位删
int SListFind(SListNode* plist, SListDataType x);				//按值查找

3.4链表功能测试

文件:"test.c"

#include "SList.h"

void SListTest1()
{
	SListNode* plist = NULL;

	
	SListPopBack(&plist);			//尾删第一种情况测试

	SListPushBack(&plist, 0);
	SListPrint(plist);
	SListPopBack(&plist);			//尾删第二种情况测试

	SListPushFront(&plist, 2);
	SListPushFront(&plist, 1);
	SListPrint(plist);
	SListPopBack(&plist);			//尾删第三种情况测试
	SListPrint(plist);

	SListPushFront(&plist, 0);
	SListPushFront(&plist, -1);
	SListPushBack(&plist, 2);
	SListPushBack(&plist, 3);
	SListPrint(plist);
	SListPopFront(&plist);
	SListPrint(plist);				//头删测试

	return;
}

void SListTest2()					//按值查找测试
{
	SListNode* plist = NULL;
	int pos;
	SListPushBack(&plist, 0);
	SListPushBack(&plist, 1);
	SListPushBack(&plist, 2);
	SListPushBack(&plist, 3);
	SListPrint(plist);
	pos = SListFind(plist, 1);
	printf("%d ", pos);
	pos = SListFind(plist, 9);
	printf("%d ", pos);
}

void SListTest3()
{
	SListNode* plist = NULL;
	SListInsert(&plist, 0, 5);			//在首位置插入且链表为空测试
	SListPushFront(&plist, 4);
	SListPrint(plist);

	SListInsert(&plist, 0, 3);			//在首位置插入且链表不为空测试
	SListPrint(plist);

	SListPushFront(&plist, 2);			
	SListPushFront(&plist, 1);
	SListPushFront(&plist, 0);
	SListInsert(&plist, 3, 9);			//在除首位置和末位置插入测试
	SListPrint(plist);

	SListInsert(&plist, 7, 9);			//在末位置插入测试
	SListPrint(plist);

	SListInsert(&plist, -1, 9);			//在超出链表的位置插入
	SListInsert(&plist, 10, 9);
	SListPrint(plist);
}

void SListTest4()					//按位删除测试
{
	SListNode* plist = NULL;
	SListErase(&plist, 3);			//链表为空测试
	
	SListInsert(&plist, 0, 4);
	SListPrint(plist);
	SListErase(&plist, 0);			//首位置删除测试
	SListPrint(plist);

	SListInsert(&plist, 0, 0);
	SListInsert(&plist, 0, 1);
	SListInsert(&plist, 0, 2);
	SListInsert(&plist, 0, 3);
	SListPrint(plist);
	SListErase(&plist, 2);			//中间位置删除测试
	SListPrint(plist);

	SListErase(&plist, 2);			//末位置删除测试
	SListPrint(plist);

	SListErase(&plist, 3);			//超出可删除范围测试
	SListPrint(plist);


}


int main()
{
	SListTest1();
	SListTest2();
	SListTest3();
	SListTest4();
	return 0;
}

✨写在最后

⏱笔记时间:2021_10_3

🌐代码:Gitee:朱雯睿 (zhu-wenrui) - Gitee.com

               Github:Zero0Tw0 · GitHub


点击全文阅读


本文链接:http://zhangshiyu.com/post/36145.html

链表  结点  插入  
<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1