目录
一. 单链表的特点
单链表的结点是随机的不是逻辑上相邻即物理上相邻的。用指针指向下一个节点的地址。
1.1 解引用拓展 🤖
解引用有两种,* ->
*p的意思是:是取p所指向内存的值,取多少大小的值,取决于结构体前的数据类型,如int 取四个字节,char取一个字节。
p->的意思是: 结构体用->,取决于->后面是什么值,如果是->val则取data域的值,->next则 取下个结点的地址。相当于(*p).next
二. 单链表的操作
单链表分为带头结点和不带头结点 ☆*: .。. o(≧▽≦)o .。.:*☆
2.1不带头节点的操作
2.1.1 打印
打印不用二级指针的原因是:打印不用改变外部结构体指针,只用遍历打印就好了,当要改变结构体指针就要用二级指针。
void SLTPrint(SLNode* phead)
{
SLNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->val);
cur = cur->next;
}
printf("NULL\n");
}
2.1.1.1 创建结点
SLNode* CreateNode(SLNDataType x)
{
SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->val = x;
newnode->next = NULL;
return newnode;
}
2.1.2 尾插(需要二级指针)
void SLTPushBack(SLNode** pphead, SLNDataType x)
{
SLNode* newnode = CreateNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
// 找尾
SLNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
注意形参的值不改变实参:(精髓部分)
接下来是很多人都迷迷糊糊搞不懂的地方。
当前函数定义的可以直接修改,不是当前当前函数定义的,无论是外部函数,还是malloc出来的空间,访问时都要用指针去通过地址修改。全局变量可以直接修改。
形参不不能改变实参的,要传实参的地址才能改变形参。
想用形参pphead改变外部指针phead(实参) ,先将实参的地址&plist,传给实参pphead,这时pphead代表的是plist地址(&plist),*pphead解引用所以*pphead代表是plist,这里是要改变SNode*,所以要node**。这里是要修改结构体的指针plist,所以是需要结构体指针的地址&plist传给*pphead。
如果要改变外部定义的结构指针SLNode*,要用二级指针SLNode**。
如果要改变外部定义的结构体Node,就要一级指针Node*。
2.1.3 头插
void SLTPushFront(SLNode** pphead, SLNDataType x)
{
SLNode* newnode = CreateNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
2.1.4 尾删
删后n-1个不需要二级指针,直接free();
但是只有一个空间的时候,删掉,plist就成了没有指向空间的野指针,所以需要将plist置空,需要改变结构体指针。
void SLTPopBack(SLNode** pphead)
{
// 温柔的检查
//if (*pphead == NULL)
// return;
// 空
assert(*pphead);
// 1、一个节点
// 2、一个以上节点
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
// 找尾
/*SLNode* prev = NULL;
SLNode* tail = *pphead;
while (tail->next != NULL)
{
prev = tail;
tail = tail->next;
}
free(tail);
tail = NULL;
prev->next = NULL;*/
SLNode* tail = *pphead;
while (tail->next->next != NULL)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
2.1.5 头删
void SLPopFront(SLNode** pphead) {
assert(pphead);
if ((*pphead)== NULL) {
free(*pphead);
*pphead= NULL;
}
else {
/**pphead = (*pphead)->next;
free(*pphead);*/
SLNode* tmp = *pphead;
*pphead = (*pphead)->next;
free(tmp);
}
}
2.1.6 查找
查找的作用主要是,和Insert()配合着使用。
void SLTFind(SLNode* phead, SLDataType x)
{
SLNode* cur = phead;
while (cur)
{
if (cur->val == x)
{
return cur;
}
else
{
cur = cur->next;
}
}
return NULL;
}
2.1.7 在pos前插入
//在pos之前插入
void SLTInsert(SLNode** pphead, SLNode* pos, SLDataType x)
{
assert(pphead); //plist地址不可能为NULL
assert(!*pphead && !pos || *pphead && pos); // *pphead为空时pos也为NULL,
SLNode* newnode = CreateNode(x);
if (*pphead==pos)
{
SLPushFront(pphead,x); //这里不是在测试类,没有将plist地址给pphead,*pphead表示解引用使用plist
}
else {
SLNode* pre = *pphead;
while (pre->next != pos) {
pre = pre->next;
}
newnode->next = pos;
pre->next = newnode;
}
}
一级指针和二级指针的区别
2.1.8 删除pos位置
//删除pos位置的元素
void SLTErase(SLNode** pphead, SLNode* pos)
{
assert(pphead);
assert(*pphead);
assert(pos);
SLNode* pre = *pphead;
if (*pphead==pos) {
SLPopFront(pphead);
}
else
{
while (pre->next != pos) {
pre = pre->next;
}
pre->next = pos->next;
free(pos);
pos->next = NULL;
}
}
2.1.9 在pos之后插入
//在pos的之后插入
void SLTInsertAfter(SLNode** pphead, SLNode* pos, SLDataType x)
{
assert(pphead);
assert(!*pphead && !pos || *pphead && pos);
SLNode* newnode = CreateNode(x);
if (*pphead == pos)
{
SLPushFront(pphead, x);
}
else
{
newnode->next = pos->next;
pos->next = newnode;
}
}
2.1.20 删除pos之后的元素
//删除指定位置的下一个位置
void SLTEraseAfter(SLNode** pphead, SLNode* pos)
{
assert(pphead);
assert(*pphead);
assert(pos->next != NULL);
SLNode* tmp = pos->next;
pos->next = pos->next->next;
free(tmp);
tmp = NULL;
}
注意错误写法
pos->next = pos->next->next;
free(pos->next);
错误原因:这里pos->next已经赋值成pos->next->next。所以真正删除的其实是 pos->next->next,其次这样会导致单链表断裂,后面的结点会没有指针指向成野结点。
2.1.21 销毁
//销毁
void STDestory(SLNode** pphead)
{
assert(pphead);
SLNode* cur = *pphead;
while (cur != NULL) {
SLNode* tmp = cur->next;
free(cur);
cur = tmp;
}
*pphead = NULL;
}
不带头结点的完整代码
//头文件
#pragma once
#include<stdio.h>
#include<stdlib.h>
typedef int SLDataType;
typedef struct SListNode {
SLDataType val;
struct SListNode* next;
}SLNode;
void SLPrint(SLNode* phead);
void SLPushBack(SLNode** pphead, SLDataType x);
void SLPopBack(SLNode** pphead);
void SLPushFront(SLNode** pphead, SLDataType x);
void SLPopFront(SLNode** pphead);
SLNode* SLTFind(SLNode* phead, SLDataType x);
void SLTInsert(SLNode** pphead, SLNode* pos, SLDataType x);
void SLTInsertAfter(SLNode** pphead, SLNode* pos, SLDataType x);
void SLTErase(SLNode** pphead, SLNode* pos);
void SLTEraseAfter(SLNode** pphead, SLNode* pos);
void STDestory(SLNode** pphead);
//SList.c文件
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include"SList.h"
#include<assert.h>
//打印
void SLPrint(SLNode* phead) {
assert(phead);
while (phead!= NULL) {
printf("%d->", phead->val);
phead = phead->next;
}
printf("NULL\n");
}
//创建新节点
SLNode* CreateNode(SLDataType x) {
SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
while (newnode == NULL) {
perror("malloc fail");
exit(-1);
}
newnode->val = x;
newnode->next = NULL;
return newnode;
}
//尾插
void SLPushBack(SLNode** pphead, SLDataType x) {
SLNode* newnode = CreateNode(x); //调用函数,实参没有数据类型
if (*pphead == NULL) {
*pphead = newnode;
}
else {
SLNode* tail = *pphead;
while (tail->next != NULL) {
tail = tail->next;
}
newnode->next = NULL;
tail->next = newnode;
}
}
//尾删
void SLPopBack(SLNode** pphead) {
assert(*pphead);
if ((*pphead)->next == NULL) {
free(*pphead);
*pphead = NULL;
}
else {
SLNode* pre = NULL;
SLNode* tail = *pphead;
while (tail->next != NULL) {
pre = tail;
tail = tail->next;
}
free(tail);
tail = NULL;
pre->next = NULL;
}
}
//头插
void SLPushFront(SLNode** pphead, SLDataType x) {
SLNode* newnode = CreateNode(x);
/*if (*pphead == NULL) {
*pphead = newnode;
}*/ //可以不要
newnode->next = *pphead;
*pphead= newnode;
}
//头删
void SLPopFront(SLNode** pphead) {
assert(*pphead);
if ((*pphead)== NULL) {
free(*pphead);
*pphead= NULL;
}
else {
/**pphead = (*pphead)->next;
free(*pphead);*/
SLNode* tmp = *pphead;
*pphead = (*pphead)->next;
free(tmp);
}
}
// 查找
SLNode* SLTFind(SLNode* phead, SLDataType x)
{
SLNode* cur = phead; //查找不需要二级指针,所以phead为头指针
while (cur)
{
if (cur->val == x)
{
return cur;
}
else
{
cur = cur->next;
}
}
return NULL;
}
//在pos之前插入
void SLTInsert(SLNode** pphead, SLNode* pos, SLDataType x)
{
assert(pphead); //plist地址不可能为NULL
assert(!*pphead && !pos || *pphead && pos); // *pphead为空时pos也为NULL,
SLNode* newnode = CreateNode(x);
if (*pphead==pos)
{
SLPushFront(pphead,x); //这里不是在测试类,没有将plist地址给pphead,*pphead表示解引用使用plist
}
else {
SLNode* pre = *pphead;
while (pre->next != pos) {
pre = pre->next;
}
newnode->next = pos;
pre->next = newnode;
}
}
//在pos的之后插入
void SLTInsertAfter(SLNode** pphead, SLNode* pos, SLDataType x)
{
assert(pphead);
assert(!*pphead && !pos || *pphead && pos);
SLNode* newnode = CreateNode(x);
if (*pphead == pos)
{
SLPushFront(pphead, x);
}
else
{
newnode->next = pos->next;
pos->next = newnode;
}
}
//删除pos位置的元素
void SLTErase(SLNode** pphead, SLNode* pos)
{
assert(pphead);
assert(*pphead);
assert(pos);
SLNode* pre = *pphead;
if (*pphead==pos) {
SLPopFront(pphead);
}
else
{
while (pre->next != pos) {
pre = pre->next;
}
pre->next = pos->next;
free(pos);
pos->next = NULL;
}
}
//删除指定位置的下一个位置
void SLTEraseAfter(SLNode** pphead, SLNode* pos)
{
assert(pphead);
assert(*pphead);
assert(pos->next != NULL);
SLNode* tmp = pos->next;
pos->next = pos->next->next;
free(tmp);
tmp = NULL;
}
//销毁
void STDestory(SLNode** pphead)
{
assert(pphead);
SLNode* cur = *pphead;
while (cur != NULL) {
SLNode* tmp = cur->next;
free(cur);
cur = tmp;
}
*pphead = NULL;
}
//Test.c 文件
#define _CRT_SECURE_NO_WARNINGS
#include"SList.h"
void Test1() {
SLNode* splist=NULL;
SLPushBack(&splist, 1);
SLPushBack(&splist, 2);
SLPushBack(&splist, 3);
SLPushBack(&splist, 4);
SLPrint(splist);
SLPopBack(&splist);
SLPopBack(&splist);
SLPrint(splist);
SLPushFront(&splist, 1);
SLPushFront(&splist, 2);
SLPushFront(&splist, 3);
SLPushFront(&splist, 4);
SLPrint(splist);
SLPopFront(&splist);
SLPopFront(&splist);
SLPrint(splist);
}
void Test2() {
SLNode* splist = NULL;
SLPushBack(&splist, 1);
SLPushBack(&splist, 2);
SLPushBack(&splist, 3);
SLPushBack(&splist, 4);
SLPrint(splist);
SLNode* pos = SLTFind(splist, 3);
SLTInsert(&splist, pos, 20);
SLPrint(splist);
pos = SLTFind(splist, 4);
SLTInsertAfter(&splist, pos, 30);
SLPrint(splist);
pos = SLTFind(splist, 2);
SLTErase(&splist, pos);
SLPrint(splist);
pos = SLTFind(splist, 3);
SLTEraseAfter(&splist, pos);
SLPrint(splist);
STDestory(&splist);
SLPrint(splist);
}
int main() {
Test2();
return 0;
}