一,链表的概念及结构
1,线性表的概念
线性表是具有相同类型的n个数据元素的有限序列。
线性表有两个特点:物理结构和逻辑结构。
物理结构:指的是数据在内存中的存储结构。有线性和非线性两种情况。
逻辑结构:指的是数据之间的逻辑关系,是抽象的。比如一些数据是没有任何关系,是孤立的,就是非线性的,如果这些数据可以想象成用一条“线’连接起来,那就是线性性的。而对于线性表来说,它在逻辑结构上一定是线性的。如下图:
2,链表的概念
链表是线性表的一种,他在逻辑结构上是线性的,在物理结构上是非线性的。它的数据元素的逻辑顺序是通过指针连接实现的
3,链表的结构
我们可以将链表看成是一个火车,火车是由一节一节的车厢组成的,链表是由一个一个节点组成的。
由上图可看出,节点是由两部分组成的,一部分存储数据,一部分存储指向下一个节点的指针,通过指针将一个个的节点连接起来。
也叫做数据域和指针域。这里就需要用到自定义类型-----结构体类型。
struct SListNode
{
int data;struct SListNode* next; //指向下一个节点的指针
};
二,链表的实现
1,首先我们需要定义三个文件
头文件SListNode.h,用来声明各种函数和包含所需的头文件。
源文件SListNode.c,用来实现所声明的函数。
源文件test.c,作为测试文件,测试所写的方法。
2,写代码
2.1,结构设计,设计节点
//定义节点的结构
// 数据+指向下一个节点的指针
typedef int SLTDataType;
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
}SLNode;
对结构体类型进行重命名,方便使用。
2.2,链表的功能(声明函数)
//创建节点
SLNode* SLBuyNode(SLTDataType x);
//打印链表
void SLPrint(SLNode* phead);
//尾插
void SLPushBack(SLNode** pphead, SLTDataType x);
//头插
void SLPushFront(SLNode** pphead, SLTDataType x);
//尾删
void SLPopBack(SLNode** pphead);
//头删
void SLPopFront(SLNode** ppheead);
//查找
SLNode* SLFind(SLNode** pphead, SLTDataType x);
//在指定位置之前插入数据
void SLInsert(SLNode** pphead, SLNode* pos, SLTDataType x);
//在指定位置之后插入数据
void SLInserAfter(SLNode* pos, SLTDataType x);
//删除pos节点
void SLErase(SLNode** pphead, SLNode* pos);
//删除pos之后的节点
void SLEraseAfter(SLNode* pos);
2.3,链表功能的实现
1,创建节点
//创建新节点
SLNode* SLBuyNode(SLTDataType x)
{
//先动态开辟申请一块内存
SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
if (newnode == NULL) //判断申请是否成功
{
perror("malloc fail");
exit(1); //失败直接结束程序
}
newnode->data = x; //将x存入新节点的data
newnode->next = NULL; //next置位空
return newnode;
}
2,打印链表
我们需要再定义一个指针来遍历整个链表,指针每向后走,打印该节点处的数据。
//打印链表
void SLPrint(SLNode* phead)
{
SLNode* pcur = phead; //pcur指向第一个节点
while (pcur) //开始遍历整个链表
{
printf("%d->", pcur->data);
pcur = pcur->next; //打印完一个后,pcur向后走,走到下一个节点
//打印完最后一个数据后,pcur->next=NULL,pcur=NULL,循环停止
}
printf("NULL\n");
}
3,尾插
注意:在创建新节点时,已经让next=NULL,所以在尾插后不需要再置位空。
而且这里在传参的时候,需要传地址,形参用二级指针接收,因为这里所传的链表可能是个空链表,这时候需要改变链表的指向,而用一级指针接收的话,只会改变形参,实参是不会改变的。
//尾插
void SLPushBack(SLNode** pphead, SLTDataType x)
{
//创建新节点,调用SLBuyNode函数,将x存入
SLNode* newnode = SLBuyNode(x);
//链表为空和不为空
//为空
if (*pphead == NULL)
{
*pphead = newnode; //链表为空,直接让新节点成为头节点
}
//不为空
else
{
//ptail找尾节点
SLNode* ptail = *pphead;
//找尾
while (ptail->next != NULL)
{
ptail = ptail->next;
}
//ptail就是尾节点
ptail->next = newnode;
}
}
4,头插
//头插
void SLPushFront(SLNode** pphead, SLTDataType x)
{
assert(pphead); //pphead不能为空
//创建新节点
SLNode* newnode = SLBuyNode(x);
//新节点的next指向头节点*pphead
newnode->next = *pphead;
*pphead = newnode; //*pphead指向newnode,新的头节点
}
5,尾删
//尾删
void SLPopBack(SLNode** pphead)
{
SLNode* ptail = *pphead;
SLNode* pcur = NULL;
//找尾节点
while (ptail->next)
{
pcur = ptail;
ptail = ptail->next;
}//pcur记录尾节点的上一个节点
pcur->next = NULL;
free(ptail);
ptail = NULL; //释放尾节点ptaail
//ptail为尾节点
}
6,头删
//头删
void SLPopFront(SLNode** pphead)
{
assert(pphead && *pphead);//链表不能为空,pphead也不能为空
SLNode* next = (*pphead)->next;//定义next指针指向第二个节点
// ->优先级比*高,需要加()
free(*pphead); //释放第一个节点
*pphead = next; //next成为新的头节点
}
7,查找指定元素
//查找
SLNode* SLFind(SLNode** pphead, SLTDataType x)
{
SLNode* pre = *pphead;//用pre来遍历链表
while (pre)
{
if (pre->data == x)
{
return pre; //找到了,返回该节点的地址
}
pre = pre->next;
}
//循环结束还没找到,返回NIULL
return NULL;
}
8,在指定位置之前插入数据
//在指定位置之前插入数据
void SLInsert(SLNode** pphead, SLNode* pos, SLTDataType x)
{
assert(pphead && *pphead);
assert(pos); //给定位置不能为空
SLNode* newnode = SLBuyNode(x); //要插入的节点
//pos是不是头节点
//
//是头节点,直接调用头插函数
if (pos == *pphead)
{
SLPushFront(pphead, x);
}
//不是头节点
else
{
SLNode* prv = *pphead;
//找到pos之前的节点
while (prv->next != pos)
{
prv = prv->next;
}
//prv就是pos之前的节点
//prv newnode pos 连接
prv->next = newnode;
newnode->next = pos;
}
}
9,在指定位置之后插入数据
//在指定位置之后插入数据
void SLInserAfter(SLNode* pos, SLTDataType x)
{
SLNode* newnode = SLBuyNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
10,删除pos节点
//删除pos节点
void SLErase(SLNode** pphead, SLNode* pos)
{
assert(pphead && *pphead);
assert(pos); //删除的节点不能为空
SLNode* prv = *pphead;
//pos是头节点
if (pos == *pphead)
{
SLPopFront(pphead);//调用头删函数
}
//找出pos之前的节点
while (prv->next != pos)
{
prv = prv->next;
}
prv->next = pos->next;
free(pos); //释放pos
pos = NULL;
}
三,代码总体
头文件
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//定义节点的结构
// 数据+指向下一个节点的指针
typedef int SLTDataType;
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
}SLNode;
//创建节点
SLNode* SLBuyNode(SLTDataType x);
//打印链表
void SLPrint(SLNode* phead);
//尾插
void SLPushBack(SLNode** pphead, SLTDataType x);
//头插
void SLPushFront(SLNode** pphead, SLTDataType x);
//尾删
void SLPopBack(SLNode** pphead);
//头删
void SLPopFront(SLNode** ppheead);
//查找
SLNode* SLFind(SLNode** pphead, SLTDataType x);
//在指定位置之前插入数据
void SLInsert(SLNode** pphead, SLNode* pos, SLTDataType x);
//在指定位置之后插入数据
void SLInserAfter(SLNode* pos, SLTDataType x);
//删除pos节点
void SLErase(SLNode** pphead, SLNode* pos);
源文件
//实现方法
#include "SlistDode.h"
//创建新节点
SLNode* SLBuyNode(SLTDataType x)
{
//先动态开辟申请一块内存
SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
if (newnode == NULL) //判断申请是否成功
{
perror("malloc fail");
exit(1);
}
newnode->data = x; //将x存入新节点的data
newnode->next = NULL; //next置位空
return newnode;
}
//打印链表
void SLPrint(SLNode* phead)
{
SLNode* pcur = phead; //pcur指向第一个节点
while (pcur) //开始遍历整个链表
{
printf("%d->", pcur->data);
pcur = pcur->next; //打印完一个后,pcur向后走,走到下一个节点
}
printf("NULL\n");
}
//尾插
void SLPushBack(SLNode** pphead, SLTDataType x)
{
//创建新节点,调用SLBuyNode函数,将x存入
SLNode* newnode = SLBuyNode(x);
//链表为空和不为空
//为空
if (*pphead == NULL)
{
*pphead = newnode; //链表为空,直接让新节点成为头节点
}
//不为空
else
{
//ptail找尾节点
SLNode* ptail = *pphead;
//找尾
while (ptail->next != NULL)
{
ptail = ptail->next;
}
//ptail就是尾节点
ptail->next = newnode;
}
}
//头插
void SLPushFront(SLNode** pphead, SLTDataType x)
{
assert(pphead); //pphead不能为空
//创建新节点
SLNode* newnode = SLBuyNode(x);
//新节点的next指向头节点*pphead
newnode->next = *pphead;
*pphead = newnode; //*pphead指向newnode,新的头节点
}
//尾删
void SLPopBack(SLNode** pphead)
{
SLNode* ptail = *pphead;
SLNode* pcur = NULL;
//找尾节点
while (ptail->next)
{
pcur = ptail;
ptail = ptail->next;
}//pcur记录尾节点的上一个节点
pcur->next = NULL;
free(ptail);
ptail = NULL; //释放尾节点ptaail
//ptail为尾节点
}
//头删
void SLPopFront(SLNode** pphead)
{
assert(pphead && *pphead);//链表不能为空,pphead也不能为空
SLNode* next = (*pphead)->next;//定义next指针指向第二个节点
// ->优先级比*高,需要加()
free(*pphead); //释放第一个节点
*pphead = next; //next成为新的头节点
}
//查找
SLNode* SLFind(SLNode** pphead, SLTDataType x)
{
SLNode* pre = *pphead;//用pre来遍历链表
while (pre)
{
if (pre->data == x)
{
return pre; //找到了,返回该节点的地址
}
pre = pre->next;
}
//循环结束还没找到,返回NIULL
return NULL;
}
//在指定位置之前插入数据
void SLInsert(SLNode** pphead, SLNode* pos, SLTDataType x)
{
assert(pphead && *pphead);
assert(pos); //给定位置不能为空
SLNode* newnode = SLBuyNode(x); //要插入的节点
//pos是不是头节点
//
//是头节点,直接调用头插函数
if (pos == *pphead)
{
SLPushFront(pphead, x);
}
//不是头节点
else
{
SLNode* prv = *pphead;
//找到pos之前的节点
while (prv->next != pos)
{
prv = prv->next;
}
//prv newnode pos 连接
prv->next = newnode;
newnode->next = pos;
}
}
//在指定位置之后插入数据
void SLInserAfter(SLNode* pos, SLTDataType x)
{
SLNode* newnode = SLBuyNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
//删除pos节点
void SLErase(SLNode** pphead, SLNode* pos)
{
assert(pphead && *pphead);
assert(pos); //删除的节点不能为空
SLNode* prv = *pphead;
//pos是头节点
if (pos == *pphead)
{
SLPopFront(pphead);//调用头删函数
}
//找出pos之前的节点
while (prv->next != pos)
{
prv = prv->next;
}
prv->next = pos->next;
free(pos); //释放pos
pos = NULL;
}
测试文件test.c
/测试
#include "SlistDode.h"
void test1()
{
SLNode* node1 =(SLNode*) malloc(sizeof(SLNode));
node1->data = 1;
SLNode* node2 =(SLNode*) malloc(sizeof(SLNode));
node2->data = 2;
SLNode* node3 = (SLNode*)malloc(sizeof(SLNode));
node3->data = 3;
SLNode* node4 = (SLNode*)malloc(sizeof(SLNode));
node4->data = 4;
//连接4个节点
node1->next = node2;
node2->next = node3;
node3->next = node4;
node4->next = NULL;
SLNode* PList = node1;
SLPrint(PList);
}
void test2()
{
SLNode* PList = NULL;
//尾插
SLPushBack(&PList, 1);
SLPushBack(&PList, 2);
SLPushBack(&PList, 3);
SLPushBack(&PList, 4);
//头插
/*SLPushFront(&PList, 1);
SLPushFront(&PList, 2);
SLPushFront(&PList, 3);
SLPushFront(&PList, 4);
SLPrint(PList);*/
//尾删
/*SLPopBack(&PList);
SLPrint(PList);
SLPopBack(&PList);
SLPrint(PList);
SLPopBack(&PList);
SLPrint(PList);*/
//头删
/*SLPopFront(&PList);
SLPrint(PList);
SLPopFront(&PList);
SLPrint(PList);
SLPopFront(&PList);
SLPrint(PList);
SLPopFront(&PList);
SLPrint(PList);*/
//查找
SLNode* find = SLFind(&PList, 2);
if (find == NULL)
printf("找不到");
else
printf("找到了");
//在指定位置之前插入数据
SLInsert(&PList, find, 4);
SLPrint(PList);
}
int main()
{
//test1();
test2();
return 0;
}
感谢观看,有问题还请指教!