C语言实现单链表

1、用于存放声明的头文件

#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include <stdio.h>
#include <assert.h>
#include <malloc.h>typedef int DataType;typedef struct SListNode
{DataType data;                    //链表中存放的数据struct SListNode *pNextNode;      //指向下一节点的指针
}SListNode, *PSListNode;// 初始化单链表(对于无头结点单链表,该函数没有意义)
void InitList(PSListNode* pHead);
// 销毁单链表
void DestroyList(PSListNode* pHead);
// 尾插
void PushBack(PSListNode* pHead, DataType data);
// 尾出
void PopBack(PSListNode* pHead);
// 头插
void PushFront(PSListNode* pHead, DataType data);
// 头出
void PopFront(PSListNode* pHead);
// 在链表中查找元素data
PSListNode Find(PSListNode pHead, DataType data);
// 删除pos位置的结点(注意不能用那种替换形式)
void  Erase(PSListNode* pHead, PSListNode pos);
// 在链表的pos位置插入元素data
void  Insert(PSListNode* pHead, PSListNode pos, DataType data);
//打印链表存放的数据
void PrintList(PSListNode pHead);

2、用于实现声明的.c文件

#include "SListNode.h"//这儿的参数是二级指针,因为要对结构体指针的内容进行修改,所以必须传该指针的地址(即一个二级指针)
//传一级指针的话相当于是值传递,不会对实参有改变
void InitList(PSListNode* pHead)
{assert(pHead);*pHead = NULL;
}PSListNode ByeNode(DataType data)
{PSListNode pNewNode = (PSListNode)malloc(sizeof(struct SListNode));if (NULL != pNewNode){pNewNode->data = data;//注意使开辟的新节点的指向为空pNewNode->pNextNode = NULL;}return pNewNode;
}void PushBack(PSListNode* pHead, DataType data)
{PSListNode pNode = NULL;PSListNode pNewNode = NULL;assert(pHead);if (NULL == (*pHead)){//此时可以不对ByeNode函数是否成功开辟空间做检测,因为即使它没有开辟成功空间,//那么使头结点头结点等于NULL逻辑一样正确*pHead = ByeNode(data);}else{pNode = *pHead;//找到尾节点while (NULL != pNode->pNextNode){//保存尾节点pNode = pNode->pNextNode;}pNewNode = ByeNode(data);//当开辟空间失败,说明向链表里插入了一个空指针pNode->pNextNode = pNewNode;}
}void PopBack(PSListNode* pHead)
{PSListNode pPerNode = NULL;PSListNode pCurNode = NULL;assert(pHead);if (NULL == (*pHead)){printf("链表中没有数据节点!\n");}else{pCurNode = *pHead;//注意循环结束的条件while (NULL != pCurNode->pNextNode){pPerNode = pCurNode;pCurNode = pCurNode->pNextNode;}//把要删除的结点从链表中断开(让该节点的上一个结点指向该结点的下一个结点)pPerNode->pNextNode = NULL;//删除该结点free(pCurNode);pCurNode = NULL;}
}void PushFront(PSListNode* pHead, DataType data)
{assert(pHead);if (NULL == (*pHead)){//此时可以不对ByeNode函数是否成功开辟空间做检测,因为即使它没有开辟成功空间,//那么使头结点头结点等于NULL逻辑一样正确*pHead = ByeNode(data);}else{PSListNode pNewNode = ByeNode(data);if (NULL == pNewNode){printf("开辟节点空间失败!\n");}else{//使指向头结点的指针指向新开辟的节点,新开辟的节点指向链表中原来的头结点pNewNode->pNextNode = *pHead;*pHead = pNewNode;}}
}void PopFront(PSListNode* pHead)
{assert(pHead);if (NULL == (*pHead)){printf("链表中没有数据节点!\n");}else{PSListNode pNode = *pHead;//直接使头结点指向链表中第三个节点,即可做到头删的目的*pHead = (*pHead)->pNextNode;free(pNode);pNode = NULL;}
}PSListNode Find(PSListNode pHead, DataType data)
{PSListNode pNode = pHead;while (NULL != pNode){if (pNode->data == data){return pNode;}else{pNode = pNode->pNextNode;}}//没有找到对应的结点return NULL;
}void  Erase(PSListNode* pHead, PSListNode pos)
{//更简单的方法(pos指向的结点在链表中)://直接交换pos指向的节点和pos指向的节点的下一个结点的数据,然后使pos指向的节点的下一个结点为它的下下一个结点PSListNode pCurNode = *pHead;PSListNode pPerNode = *pHead;assert(pHead);if (NULL == pos){return;}//要是传的参数没有在链表中,那么删除就显得没有意义了,因此对pos的检测(pos是否是链表中的某个节点)可有可无while ((pCurNode != pos) && (pCurNode != NULL)){pPerNode = pCurNode;pCurNode = pCurNode->pNextNode;}//使当前结点的上一个结点指向当前结点的下一个结点,然后释放当前结点的空间,即可做到删除当前结点pPerNode->pNextNode = pPerNode->pNextNode->pNextNode;free(pCurNode);pCurNode = NULL;
}void  Insert(PSListNode* pHead, PSListNode pos, DataType data)
{//更简单的方法(pos指向的结点在链表中)://直接交换pos指向的节点和新插入的节点的的数据,然后使新插入的结点的下一个结点为pos指向的节点的下一个结点,//pos指向的节点的下一个结点为新插入的结点PSListNode pTempNode = *pHead;PSListNode pNode = *pHead;assert(pHead);//pTempNode!=NULL是为了避免pos是一个创建的孤结点,它没有插入到链表中while ((pTempNode != pos) && (pTempNode != NULL)){pNode = pTempNode;pTempNode = pTempNode->pNextNode;}if ((pTempNode == NULL) && (pos != NULL)){printf("链表中不存在该结点!\n");return;}else{PSListNode pNewNode = ByeNode(data);if (NULL == pNewNode){printf("开辟空间失败!\n");}else{pNewNode->pNextNode = pNode->pNextNode;pNode->pNextNode = pNewNode;}}
}void PrintList(PSListNode pHead)
{PSListNode pNode = pHead;//当pHead指向空时,说明此时链表中没有数据,那么不会进入打印数据的循环,而是等到函数结束返回while (NULL != pNode){printf("%d  ", pNode->data);pNode = pNode->pNextNode;}printf("\n");
}void DestroyList(PSListNode* pHead)
{PSListNode pCurNode = NULL;PSListNode pPerNode = *pHead;assert(pHead);while (NULL != pCurNode){//每次销毁链表最前面那个结点的空间pPerNode = pCurNode->pNextNode;free(pCurNode);//不会出现野指针pCurNode = pPerNode;}
}

3、用于测试函数正确性的Test.c文件

#include "SListNode.h"//Test PushBack() / PopBack()
void TestFun1()
{PSListNode pHead = NULL;InitList(&pHead);PushBack(&pHead, 0);PushBack(&pHead, 1);PushBack(&pHead, 2);PushBack(&pHead, 3);PrintList(pHead);PopBack(&pHead);PopBack(&pHead);PrintList(pHead);
}//Test PushFront() / PopFront()
void TestFun2()
{PSListNode pHead = NULL;InitList(&pHead);PushFront(&pHead, 0);PushFront(&pHead, 1);PushFront(&pHead, 2);PushFront(&pHead, 3);PrintList(pHead);PopFront(&pHead);PopFront(&pHead);PrintList(pHead);
}//Test Find() / Erase() / Insert()
void TestFun3()
{PSListNode pHead = NULL;PSListNode pRetNode = NULL;InitList(&pHead);PushBack(&pHead, 0);PushBack(&pHead, 1);PushBack(&pHead, 2);PushBack(&pHead, 3);PrintList(pHead);pRetNode = Find(pHead, 2);printf("%d\n", pRetNode->data);Erase(&pHead, Find(pHead, 1));PrintList(pHead);Insert(&pHead, Find(pHead, 2), 1);PrintList(pHead);
}//Test DestroyList()
void TestFun4()
{PSListNode pHead = NULL;InitList(&pHead);PushBack(&pHead, 0);PushBack(&pHead, 1);PushBack(&pHead, 2);PushBack(&pHead, 3);DestroyList(&pHead);PrintList(pHead);
}int main()
{//TestFun1();//TestFun2();//TestFun3();TestFun4();system("pause");return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/526189.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vim复制、删除和粘贴一行

声明&#xff1a;下面的操作的都是在基本模式&#xff08;Esc&#xff09;&#xff0c;否则无法操作。 1、复制命令&#xff1a; y1 复制光标处的一个字符到缓冲区   yw 复制当前单词到缓冲区   yy 复制当前行到缓冲区   nyy / yny 复制当前行及其下面n-1行的内容到缓…

printf函数的格式修饰符

本文介绍printf函数的一些特殊的格式控制修饰符 1、域宽修饰符 –> 数字 对所有格式控制符都有效&#xff0c;即便是%%也不例外 宽度修饰符出现在%和格式控制符之间的数字&#xff0c;使数据在固定区域打印。如果待打印的数值不能填满位置&#xff0c;它的左侧会被天上空…

C语言实现单链表面试题汇总

这篇博客只有针对单链表的不同面试题的不同函数&#xff0c;没有对单链表的具体实现方法的介绍。 单链表的具体实现方法&#xff08;创建&#xff0c;初始化&#xff0c;前插&#xff0c;后插&#xff0c;删除&#xff0c;插入&#xff0c;销毁等&#xff09;&#xff0c;可以…

线性表之顺序表与单链表的区别及优缺点

这里比较的是基于C语言实现的顺序表与单链表&#xff0c;与其他语言的实现可能会有差异&#xff0c;但我相信语言是相通的&#xff0c;它们的实现机制应该也差不多。 1、What 什么是顺序表和单链表 ①顺序表&#xff1a; 顺序表是在计算机内存中以数组的形式保存的线性表&a…

前置++与后置++之一道简单的题目引发的思考

引言 昨晚一时兴起&#xff0c;我脑子就问自己下面的代码会输出什么&#xff0c;也不知道我脑子为什么有这个代码模型&#xff0c;只是模糊的有些印象&#xff1a; #include <stdio.h> #include <stdlib.h>int main(int argc,char** argv) {int i3,j;j(i)(i)(i);…

有趣的for循环

#include <stdio.h> int main() {int i 0;for(i 0, printf(" First %d", i);i<10, printf(" Second %d", i);i, printf(" Third %d", i)){printf(" Fourth %d", i);}system("pause");return 0; } 这段代码会…

重载函数编译后的新名字

我们都知道很多语言都支持函数重载&#xff0c;那么编译器是怎么处理编译后它们的命名冲突的呢&#xff1f; 1、先给出几个正确的重载函数&#xff1a; #include <iostream> using namespace std;int Add(int a, int b) {return a b; }double Add(double a, double b)…

初识C++之函数重载

最近开始学习C&#xff0c;了解到它在C语言没有的一个特性 – 函数重载&#xff0c;这一特性使得c的函数数量得以减少&#xff0c;减小了对名字空间的污染&#xff0c;另外对程序的可读性也有很大帮助。 那么c的函数重载这一特性是怎么实现的&#xff0c;为什么不会发生命名冲…

const在C与C++中的区别

1、const用于C –> 运行时常量 a. 修饰变量 修饰变量为常变量&#xff08;只读变量&#xff09; const int i 10; 此时i还是一个变量&#xff0c;不信你可以把它用来当作定义一个数组时的数组长度&#xff0c;一定会报错。不过它具有一种常属性&#xff0c;它的值一直都…

乱入的'\0'

看这个题之前&#xff0c;先来回忆一下strlen函数的工作机制&#xff1a; strlen所作的仅仅是一个计数器的工作&#xff0c;它从内存的某个位置&#xff08;可以是字符串开头&#xff0c;中间某个位置&#xff0c;甚至是某个不确定的内存区域&#xff09;开始扫描&#xff0c;…

强迫症的自我恢复

怎样克服强迫症&#xff1f;这是很多的人比较关注的一个问题。强迫症给人们的工作和生活带来很严重的影响&#xff0c;如果不及时进行治疗&#xff0c;对人的健康损害是很大的。那么怎样克服强迫症呢&#xff1f;下面就介绍一些怎样克服强迫症的调试方法&#xff0c;希望能帮助…

如何克服拖延症

拖延几乎成为现代人的通病&#xff0c;“先放一下&#xff0c;待会再行动”成为拖延者最大的思想毒瘤&#xff0c;如何积极地克服拖延症&#xff0c;已经成为了一个大课题。 1、正视自己的拖延症。 要改掉拖延症坏毛病&#xff0c;首先就是要正确认识到拖延症的危害&#xff…

初识C++之指针与引用

1、What a. 指针&#xff1a; 指针可以看做是一个特殊的变量&#xff0c;它是用来存放变量的地址值的。 b. 引用 引用的话&#xff0c;可以看做是给变量起的一个别名&#xff0c;而不是定义一个新变量&#xff0c;它与那个变量的本质是相同的&#xff0c;内容与地址都是一样…

初识C++之剖析const与#define

1、 编译器处理方式不同   #define是一个宏定义命令&#xff0c;它是在预处理阶段就进行了替换&#xff1b;   const修饰的是一个编译时常量&#xff0c;它是在编译阶段处理的。 2、 类型和安全检查不同   #define定义的标识符仅仅是产生文本替换&#xff0c;不管内容…

初识C++之运算符重载

C里面有一个叫作运算符重载的特性&#xff0c;它其实是基于函数实现的&#xff0c;下面就来介绍一下运算符重载。 1、What  C中预定义的运算符的操作对象只能是基本数据类型。但实际上&#xff0c;对于许多用户自定义类型&#xff08;例如类&#xff09;&#xff0c;也需要类…

对堆栈的认识

什么是堆和栈&#xff0c;它们在哪儿&#xff1f; 问题描述 编程语言书籍中经常解释值类型被创建在栈上&#xff0c;引用类型被创建在堆上&#xff0c;但是并没有本质上解释这堆和栈是什么。我仅有高级语言编程经验&#xff0c;没有看过对此更清晰的解释。我的意思是我理解什…

Waiting For Debugger

最近楼主在使用手机上的邮政银行时&#xff0c;总是打不开&#xff0c;要强制关闭&#xff0c;给我一个“Waiting For Debugger”的提示&#xff0c;相信朋友们应该遇到过类似的问题&#xff0c;当然这里不一定就是邮政银行出问题&#xff0c;可能是你手机里的任意一款软件&…

防止头文件重复包含之pragma once与#ifndef

在我们自己编写 C/C的头文件时&#xff0c;可能会忽略一点&#xff1a;用一些处理机制来避免头文件的重复包含&#xff0c;因为头文件的内容在预编译时是把头文件的内容完全拷贝到引入的地方替换头文件的包含命令&#xff0c;而包含的头文件可能有包含很多内容&#xff0c;所以…

初识C++之继承

1、何为继承 C中所谓继承&#xff0c;就是在一个已存在类的基础上创建一个新的类&#xff0c;新类获得已存在类的部分特性&#xff08;为什么是部分特性&#xff0c;后面会讲到&#xff09;。已存在类被称为基类&#xff08;Base Class&#xff09;或父类&#xff08;Father Cl…

初识C++之函数重载、重写、重定义的区别

在C的学习中&#xff0c;慢慢接触了一些很容易混淆的名词&#xff0c;今天就来剖析几个容易混淆的名词。 1、函数重载   重载函数是函数的一种特殊情况&#xff0c;为方便使用&#xff0c;C允许在同一范围中声明几个功能类似的同名函数&#xff0c;但是这些同名函数的形式参…