单链表的学习

链表是一种很重要的数据结构,它由两部分组成,第一个部分是我们要储存的数据,第二个部分是指向下一个储存单元的指针。链表在使用中有顺序表无法比拟的灵活性,免去了储存空间不够,又有可能浪费的尴尬。

单链表有一个头指针pHead,当我们没有数据要储存的时候它指向NULL,当我们有数据的时候它指向第一块储存单元。储存单元里面有两个部分,前面的部分是我们要储存的数据data,后面的部分是指向下一个储存单元的指针pNext,当后面没有储存单元的时候就指向NULL。那么我们储存的数据在内存中并不是连续储存的,而是在内存中跳跃式储存的。要使用的时候再直接申请一块空间。

下面是链表的定义

typedef struct ListNode
{
	DataType data;
	struct ListNode *pNext;
}SListNode, *PSListNode;

可以看到单链表的两个成员。为了使用方便我们直接typedef重命名。

单链表有几种基本操作,比如插入数据,删除数据,那我下面实现了一下。

首先,我写了一个申请新单元的函数

PSListNode ByeNode(DataType data)
{
	PSListNode pNewNode = (PSListNode)malloc(sizeof(SListNode));
	if (NULL != pNewNode)
	{
		pNewNode->data = data;
		pNewNode->pNext = NULL;
	}
	return pNewNode;
}

它可以为我们直接在内存中申请一块新的空间并且返回它的地址。

第一个实现就是我们从尾部插入数据

void PushBack(PSListNode* pHead, DataType data)
{
	PSListNode pNode = NULL;
	PSListNode pNewNode = NULL;

	assert(pHead);

	if (NULL == *pHead)
	{
		*pHead = ByeNode(data);
	}
	else
	{
		pNode = *pHead;
		while (NULL != pNode->pNext)
		{
			pNode = pNode->pNext;
		}
		pNewNode = ByeNode(data);
		pNode->pNext = pNewNode;
	}
}

这里我们传的参数是二级指针,因为我们是要改变它指针的指向。假如我们直接传递一级指针,那么我们并不能改变它的指向,相当于我们函数中操作了半天,其实都是在操作一个临时的指针变量,只不过他跟我们的头指针的指向是一样的,最后什么也没有返回,头指针什么变化都没有。

接下来就是我们从尾部删除数据的实现

void PopBack(PSListNode* pHead)
{
	/*PSListNode pPerNode = NULL;
	PSListNode pCurNode = NULL;
	assert(pHead);

	if (NULL == *pHead)
	{
		return;
	}
	else
	{
		pCurNode = *pHead;
		pPerNode = pCurNode;

		while (NULL != pCurNode->pNext)
		{
			pPerNode = pCurNode;
			pCurNode = pCurNode->pNext;
		}
		if (pCurNode==pPerNode)
		{
			*pHead = NULL;
			free(pCurNode);
			pCurNode = NULL;
			pPerNode = NULL;
		}
		else
		{
			pPerNode->pNext = NULL;
			free(pCurNode);
			pCurNode = NULL;
		}
	}*/

	PSListNode pPerNOde = *pHead;
	PSListNode pCurNode = *pHead;
	assert(pHead);

	if (NULL == *pHead)
	{
		return;
	}
	else
	{
		if (NULL == pCurNode->pNext)
		{
			return;
		}
		else
		{
			while (NULL!=pCurNode->pNext)
			{
				pPerNOde = pCurNode;
				pCurNode = pCurNode->pNext;
			}

			pPerNOde->pNext = NULL;
			free(pCurNode);
			pCurNode = NULL;
		}
	}

}

注释中的代码是我刚开始的时候写的,我发现他的逻辑不是很清晰,在第二个部分中我把链表中只有一个节点的情况单列了出来,逻辑比之前清晰了很多。

那我们也可以在链表的头部插入数据

void PushFront(PSListNode* pHead, DataType data)
{
	PSListNode NewNode = NULL;
	assert(pHead);

	if (NULL == *pHead)
	{
		*pHead = ByeNode(data);
	}
	else
	{
		NewNode = ByeNode(data);
		if (NULL == NewNode)
		{
			return;
		}
		else
		{
			NewNode->pNext = (*pHead);
			*pHead = NewNode;
		}
	}
}

思路有了之前的两个函数做铺垫想起来并不难。申请一块新的空间之后,让它的pNext指向我们之前的第一块空间。然后改变我们的头指针的指向,让它指向我们的新空间。这里注意我们申请空间是有可能失败的,所以要判断一下。

当然还有从头部删除

void PopFront(PSListNode* pHead)
{
	assert(pHead);

	if (NULL == *pHead)
	{
		return;
	}
	else
	{
		PSListNode pCurNode = *pHead;

		pCurNode = pCurNode->pNext;
		free(*pHead);
		*pHead = pCurNode;
		pCurNode = NULL;
	}
}

千万不要忘记free空间之后要给指针赋空,否则会形成野指针。

还有就是寻找我们链表中的元素

PSListNode Find(PSListNode pHead, DataType data)
{
	if (NULL == pHead)
	{
		return NULL;
	}
	else
	{
		PSListNode pNode = pHead;
		/*while (data != pNode->data)
		{
			if (NULL == pNode->pNext)
			{
				return NULL;
			}
			pNode = pNode->pNext;
		}
		return pNode;*/

		while (NULL != pNode)
		{
			if (data == pNode->data)
				return pNode;

			pNode = pNode->pNext;
		}
		return NULL;
	}
}

注释掉的代码是我第一次写的,后来我发现它的逻辑有点问题,我可以更简单的实现它的功能。

最后返回我要找的数据的位置,假如没有找到那么就返回空。

打印我链表中的元素

void PrintList(PSListNode pHead)
{
	PSListNode pNode = pHead;

	while (NULL!=pNode)
	{
		printf("%d ", pNode->data);
		pNode = pNode->pNext;
	}

	printf("\n");
}

删除我的任意位置的节点

void  Erase(PSListNode* pHead, PSListNode pos)
{
	PSListNode pCurNode = pos;
	PSListNode pPerNode = NULL;
	assert(pHead);

	if (NULL == *pHead)
	{
		return;
	}
	else
	{
		pPerNode = *pHead;
		while (pPerNode->pNext != pCurNode)
		{
			pPerNode = pPerNode->pNext;
		}
		pPerNode->pNext = pCurNode->pNext;
		free(pCurNode);
		pCurNode = NULL;
	}
}

在我的链表的任意位置插入一个节点

void  Insert(PSListNode* pHead, PSListNode pos, DataType data)
{
	PSListNode ptmpNode = *pHead;
	PSListNode pNode = *pHead;
	assert(pHead);

	if (NULL == *pHead)
	{
		*pHead = ByeNode(data);
	}
	else
	{
		while (pos != pNode)
		{
			if (NULL == pNode)
			{
				return;
			}
			pNode = pNode->pNext;
		}
		ptmpNode = pNode->pNext;
		pNode->pNext = ByeNode(data);
		pNode = pNode->pNext;
		pNode->pNext = ptmpNode;
	}
}
时间: 2024-08-03 11:39:12

单链表的学习的相关文章

单链表 知识点学习--Android地基系列(一)

嗯,看别人整理过,自己再做的笔记,题目都是常见的必考题目,嘻嘻嘻,单链 表的基础知识我就不写了,太多人写了. 看前注意!!!!!!!涉及C知识较多,C知识我以后再考虑是否写一篇附加文 可以辅助以后的Android必备地基系列博文 struct ListNode { int data; ListNode * Next; }; 1.求链表中结点个数 思路:若链表为空,返回0:循环判断链表下一个指针是否为空,no,长度++(初始化用unsigned int  length = 0),直到下一个为nul

数据结构(一) 单链表的实现-JAVA

数据结构还是很重要的,就算不是那种很牛逼的,但起码得知道基础的东西,这一系列就算是复习一下以前学过的数据结构和填补自己在这一块的知识的空缺.加油.珍惜校园中自由学习的时光.按照链表.栈.队列.排序.数组.树这种顺序来学习数据结构这门课程把. -WH 一.单链表的概念 链表是最基本的数据结构,其存储的你原理图如下图所示 上面展示的是一个单链表的存储原理图,简单易懂,head为头节点,他不存放任何的数据,只是充当一个指向链表中真正存放数据的第一个节点的作用,而每个节点中都有一个next引用,指向下一

C语言实现单链表,单链表面试题面试

单链表是学习不可缺少的一个重要模块,在面试中也会出很多的单链表变种问题,今天就把他们汇总宋总结一下 首先来是实现一个简单的单链表: (在这里,一些简单的实现单链表的操作函数就不备注了) typedef  int DataType;//typedef了一个类型,以后如果想要改变单链表节点内储存数据的类型就可以直接在这里改变 typedef struct SListNode { DataType data;           //数据 struct SListNode* next;  //指向下一

单链表的实现-JAVA

该内容为转载,原地址: 数据结构(一) 单链表的实现-JAVA 数据结构还是很重要的,就算不是那种很牛逼的,但起码得知道基础的东西,这一系列就算是复习一下以前学过的数据结构和填补自己在这一块的知识的空缺.加油.珍惜校园中自由学习的时光.按照链表.栈.队列.排序.数组.树这种顺序来学习数据结构这门课程把. -WH 一.单链表的概念 链表是最基本的数据结构,其存储的你原理图如下图所示 上面展示的是一个单链表的存储原理图,简单易懂,head为头节点,他不存放任何的数据,只是充当一个指向链表中真正存放数

java单链表

单链表 一.单链表的概念 链表是最基本的数据结构,其存储的你原理图如下图所示 上面展示的是一个单链表的存储原理图,简单易懂,head为头节点,他不存放任何的数据,只是充当一个指向链表中真正存放数据的第一个节点的作用,而每个节点中都有一个next引用,指向下一个节点,就这样一节一节往下面记录,直到最后一个节点,其中的next指向null. 链表有很多种,比如单链表,双链表等等.我们就对单链表进行学习,其他的懂了原理其实是一样的. 二.用java实现单链表 语言只是一种工具,数据结构真正体会的是那种

单链表(包含反转、导出、循环链表思路)

生活永远是自己的,美哉美哉.实习告一段落,大学也算彻底结束,就像毛不易唱的二零三,给我想要的自由.最近学习汇编及数据结构(C语言),链表也总算告一段落,本篇是单链表的学习代码笔记,本来也想想每一步都做图,分享知识,让更多的朋友去学习,但是本人局限于能力,图片无法表达自己想要的描述,所以干脆不做图了.随后日子会有双链表的操作,后面仍然会分享栈.队列的自学笔记,也可能写汇编8086的心得,希望大家一起共勉.代码可能有些繁琐(很多地方都可以优化),只是新手 给 新手的一些参考反转链表用的迭代思路参考(

链表的学习-2

一.单链表的学习 单链表:也叫单向链表,只能从一个方向遍历和进行操作. 单链表分为2种,带头结点的和不带头结点的,这里主要说明一下带头结点的单链表. 二.带头结点的单链表 三.单链表的结点结构体 typedef struct LNode { int data;//存放数据 存放的数据类型也可以是字符等等,为了方便先存放int型. struct LNode* pNext;//指针指向下个结点 }LNode; 结点的结构体名字可以随便定义看个人喜好 四.链表的初始化 单链表的写法各有个的写法,各有个

【算法导论学习-23】两个单链表(single linked)求交点

问题:A.B两个单链表如果有交点,返回第一个交点在A中的位置(链表头结点位置为0). 分析:A.B如果有交点,交点的后继一定也是交点,所以一定是Y型相交,所以算法的思想如下 1)  求得A.B的长度,比如ALength,Blength 2)  判断ALength,Blength谁大,比如Alength>Blength 3)  Alength移动到Alength-Blength的位置,开始判断每个节点是否相等,相等则退出. 以本博客中"[算法导论学习-20]单链表(single linked

日常学习随笔-数组、单链表、双链表三种形式实现栈结构的基本操作

一.栈结构 栈(stack)是限制插入和删除只能在一个位置上的表,该位置是 表的末端,叫做栈的顶(Top).对栈的基本操作有push(进栈),pop(出栈),peak(栈顶元素),size(栈容量)等. 栈的核心思想:"先进后出". 二.案例一:数组实现"栈" 1 package com.xfwl.algorithmAnalysis.stack; 2 3 import java.util.Arrays; 4 5 /** 6 * 自定义栈结构(基于数组的形式) 7 *