数据结构(初阶3.单链表)
一、单链表
1.1概念与结构
我们可以用火车类比一下:
淡季时⻋次的⻋厢会相应减少,旺季时⻋次的⻋厢会额外增加⼏节。只需要将⽕⻋⾥的某节⻋厢去掉/ 加上,不会影响其他⻋厢,每节⻋厢都是独⽴存在的。
在链表⾥,每节“⻋厢”是什么样的呢?
与顺序表不同的是,链表⾥的每节"⻋厢"都是独⽴申请下来的空间,我们称之为“结点/结点”
图中指针变量 plist保存的是第⼀个结点的地址,我们称plist此时“指向”第⼀个结点,如果我们希望
plist“指向”第⼆个结点时,只需要修改plist保存的内容为0x0012FFA0。
(注意:在链表中,没有增容的概念,需要插入新的数据,直接申请一个结点大小的空就可以了)
1.3链表的性质
1、链式机构在逻辑上是连续的,在物理结构上不⼀定连续
2、结点⼀般是从堆上申请的
3、从堆上申请来的空间,是按照⼀定策略分配出来的,每次申请的空间可能连续,可能不连续
结合前⾯学到的结构体知识,我们可以给出每个结点对应的结构体代码:
#include<stdio.h>
#include<stdlib.h>
//定义链表的结构
typedef int SLTDataType; // 方便后面一键替换
typedef struct SListNode {
}SLNode;
void SLTPrint(SLNode* phead)
{
while (pcur)
{
printf("%d->", pcur->data); // 第一次打印 node1—> data =1;
}
printf("NULL\n");
}
#include"SList.h"
void creatslist() {
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;
node1->next = node2;
node2->next = node3;
node3->next = node4;
node4->next = NULL;
SLNode* plist = node1;
SLTPrint(plist);
}
int main()
{
creatslist();
}
二、实现单链表
SList.h#include<stdio.h>
#include<stdlib.h>
//定义链表的结构
typedef int SLTDataType; // 方便后面一键替换
typedef struct SListNode {
}SLNode;
//打印链表
void SLTPrint(SLNode* phead)
void SLTPushBack(SLNode** pphead, SLTDataType x);
void SLTPushFront(SLNode** pphead, SLTDataType x);
void SLTPopBack(SLNode** pphead);
void SLTPopFront(SLNode** pphead);
//查找
SLTNode* SLTFind(SLNode* phead, SLTDataType x);
void SLTInsert(SLNode** pphead, SLNode* pos, SLTDataType x);
void SLTErase(SLNode** pphead, SLNode* pos);
void SLTInsertAfter(SLNode* pos, SLTDataType x);
void SLTEraseAfter(SLNode* pos);
//销毁链表
void SListDestroy(SLNode** pphead);
SList.c
申请新节点
//申请新节点
SLNode* SLTBuyNode(SLTDataType x)
{
SLNode* node = (SLNode*)malloc(sizeof(SLNode));
if (node == NULL)
{
perror("malloc fail!");
}
node->data = x;
node->next = NULL;
return node;
}
(补充:形参的改变要影响实参,必须传实参的地址)
举个简单的例子:
int a = 1;
int* pa = &a;
void SLTPushBack(SLNode** pphead, SLTDataType x)
{
assert(pphead);
//申请新节点
SLNode* newnode = SLTBuyNode(x);
if (*pphead == NULL) //指向第一个节点的指针为空
{
*pphead = newnode;
}
else
{
//找尾结点
SLNode* pcur = *pphead;
while (pcur->next)
{
pcur = pcur->next;
}
pcur->next = newnode;
}
}
第一个结点:*plist ——————> **pphead 指向第一个结点的指针: plist ——————> *pphead指向指向第一个结点的指针的地址:&pilst——————> pphead
void SLTPushFront(SLNode** pphead, SLTDataType x)
{
//申请新节点
SLNode* newnode = SLTBuyNode(x);
*pphead = newnode; //新插入的结点变成头节点
}
void SLTPopBack(SLNode** pphead)
{
assert(pphead && *pphead);
{
free(*pphead);
*pphead = NULL;
}
else
{
while (ptail->next != NULL)
{
prev = ptail; //将 ptail 指向下一个的结点赋值给 prev
}
prev->next = NULL;
free(ptail);
ptail = NULL;
}
}
头删
void SLTPopFornt(SLNode** pphead)
{
assert(pphead && *pphead);
free(*pphead); //先释放
*pphead = next; //后赋值
}
void SLTInsert(SLNode** pphead, SLNode* pos, SLTDataType x)
{
assert(pphead);
assert(pos);
{
SLTPushFront(pphead, x);
}
else
{
while (prve->next != pos)
{
prve = prve->next;
}
newnode->next = pos; //当 prve -> next = pos 也就是到达 pos 前的结点,插入 newnode ,让 newnode->mext = pos
prve->next = newnode; //零 prve->next = newnode
}
}
在指定位置之后插入数据
void SLTInsertAfter(SLNode*pos, SLTDataType x)
{
assert(pos);
newnode->next = pos->next; //先让 newnode 指向的下一个结点为原来 pos 指向的结点
pos->next = newnode; //再让 newnode 赋值称为 原来 pos 指向的下一个结点
}
void SLTErase(SLNode** pphead, SLNode* pos)
{
assert(pphead && *pphead);
assert(pos);
{
SLTPopFornt(pphead);
}
else
{
while (prve->next != pos)
{
prve = prve->next;
}
prve->next = pos->next; //当 prve->next 的结点就是 pos 时,prve->next = pos->next(意思就是跳过pos 结点)
free(pos); //释放pos ,NULL
pos = NULL;
}
}
删除指定位置之后的结点
void SLTEraseAfter( SLNode* pos)
{
pos->next = pos->next->next; //跳过 del 结点
free(del); //释放 del ,NULL
del = NULL;
}
void SLTDestory(SLNode** pphead)
{
assert(pphead && *pphead);
SLNode* pcur = *pphead;
while (pcur)
{
SLNode* next = pcur->next;
free(pcur);
pcur = next;
}
*pphead = NULL;
}
#include"SList.h"
void SListtest()
{
SLNode* plist = NULL;
SLTPushBack(&plist, 4);
SLTPushBack(&plist, 3);
SLTPushBack(&plist, 2);
SLTPushBack(&plist, 1);
printf("尾插后的数据:");
SLTPrint(plist);
printf("\n");
SLTPushFront(&plist, 5);
printf("头插数据5 :");
SLTPrint(plist);
printf("\n");
SLTPopBack(&plist);
printf("尾删后的数据:");
SLTPrint(plist);
printf("\n");
SLTPopFornt(&plist);
printf("头删后的数据:");
SLTPrint(plist);
printf("\n");
SLTFind(plist, 3);
SLNode* find1 = SLTFind(plist, 3);
SLTInsert(&plist, find1, 6);
SLTInsertAfter(find1, 7);
SLTPrint(plist);
printf("\n");
SLTEraseAfter(find1);
SLTErase(&plist, find1);
SLTPrint(plist);
printf("\n");
SLTDestory(&plist);
printf("销毁链表plist:");
SLTPrint(plist);
}
int main()
{
SListtest();
return 0;
}
未完待续~~
Ongwu博客 版权声明:以上内容未经允许不得转载!授权事宜或对内容有异议或投诉,请联系站长,将尽快回复您,谢谢合作!