目录
1. 为什么要有链表?
上篇文章我们介绍了顺序表,顺序表有很多缺陷比如
1. 空间不够需要增容,增容需要付出代价
2. 为了避免频繁扩容,我们满了基本上都是扩2倍,可能会导致一定的空间浪费
3. 要求数据从开始位置连续存储,我们在头部或中间插入删除数据,需要挪动数据,效率不高
那有人就针对顺序表的诸多缺陷,设计出了链表
链表的优缺点如下:
优点:
按需申请空间,不用了就释放空间,头部中间插入删除数据不需要挪动数据,不存在空间浪费。缺点:
每存一个数据都要存一个指针去链接后面的内存节点,不支持随机访问(用下标直接访问第i个)
2. 链表的种类
单向或双向,带头或不带头,循环或不循环
共八种
最常用的为
1. 无头单向非循环链表
结构简单,更多作为其他数据结构的子结构
2. 带头双向循环链表
结构复杂,一般用于单独存储数据,实际使用中结构复杂实现简单
3. 具体功能实现
(1)节点结构体定义
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
typedef int SLTDataType;
typedef struct SListNode
{
int data;
struct SListNode* next;
}SListNode;
data 用来存储数据
next用来存储下一个节点的地址
(2)申请节点
插入数据时节省代码量
SListNode* BuySListNode(SLTDataType x)
{
SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
if (newnode == NULL)
{
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
(3)尾插
在链表的末尾链接上一个节点
void SListPushBack(SListNode** phead, SLTDataType x)
{
SListNode* newnode = BuySListNode(x);
if (*phead == NULL)
{
*phead = newnode;
}
else
{
SListNode* tail = *phead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
(4)尾删
删除链表最末尾的那个节点
void SListPopBack(SListNode** phead)
{
if (*phead == NULL)
return;
//assert(*phead!=NULL);
if ((*phead)->next == NULL)
{
free(*phead);
*phead = NULL;
}
else
{
SListNode* tail = *phead;
SListNode* t = tail;
while (tail->next != NULL)
{
t = tail;
tail = tail->next;
}
free(tail);
tail = NULL;
t->next = NULL;
//SListNode* tail = *phead;
//while (tail->next->next != NULL)
//{
// tail = tail->next;
//}
//free(tail->next);
//tail->next = NULL;
}
}
(5)头插
在链表头部插入数据
void SListPushFront(SListNode** phead, SLTDataType x)
{
SListNode* newnode = BuySListNode(x);
newnode->next = *phead;
*phead = newnode;
}
(6)头删
删除链表头部的数据
void SListPopFront(SListNode** phead)
{
if (*phead == NULL)
return;
SListNode* front=(*phead)->next;
free(*phead);
*phead = front;
}
(7)查找
查找链表中的某个数据,返回地址,找不到返回空指针
SListNode* SListFind(SListNode* phead, SLTDataType x)
{
SListNode* cur = phead;
while (cur)
{
if (cur->data == x)
{
return cur;
}
else
{
cur = cur->next;
}
}
return NULL;
}
(8)指定位置插入
根据给的地址插入数据(插入到指定位置前面)
void SListInsert(SListNode** phead, SListNode* pos, SLTDataType x)//pos配合查找函数
{
SListNode* newnode = BuySListNode(x);
if (*phead == pos)
{
void SListPopFront(phead);
}
//找到pos的前一位置
else
{
SListNode* posPrey = *phead;
while (posPrey->next != pos)
{
posPrey = posPrey->next;
}
posPrey->next = newnode;
newnode->next = pos;
}
}
(9)插入指定位置后面
void SListInsertTail(SListNode* pos, SLTDataType x)
{
assert(pos);
SListNode* newnode = BuySListNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
(10)消除指定位置元素
void SListErase(SListNode** phead, SListNode* pos)
{
assert(head && (*head));
assert(pos);
if (*phead == pos)
{
*phead = (*phead)->next;
free(pos);
pos = NULL;
}
else
{
SListNode* prev = *phead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
pos = NULL;
}
}
(11)消除指定位置后的数据
void SListEraseAfter(SListNode* pos)
{
assert(pos);
if (pos == NULL)
{
exit(-1);
}
SListNode* next = pos->next;
pos->next = next->next;
free(next);
next = NULL;
}
(12)打印链表
void SListPrint(SListNode* phead)
{
SListNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("\n");
}
(13)销毁链表
使用结束后要销毁
void SListDestroy(SListNode** phead)
{
assert(phead && *phead);
SListNode* plist = *phead;
while (plist->next != NULL)
{
plist = plist->next;
free(*phead);
*phead = plist;
}
free(*phead);
*phead = NULL;
}
4. 完整代码
1. slist.c
#define _CRT_SECURE_NO_WARNINGS h
#include"sllist.h"
SListNode* BuySListNode(SLTDataType x)
{
SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
if (newnode == NULL)
{
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
void SListPrint(SListNode* phead)
{
SListNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("\n");
}
void SListPushBack(SListNode** phead, SLTDataType x)
{
SListNode* newnode = BuySListNode(x);
if (*phead == NULL)
{
*phead = newnode;
}
else
{
SListNode* tail = *phead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
void SListPushFront(SListNode** phead, SLTDataType x)
{
SListNode* newnode = BuySListNode(x);
newnode->next = *phead;
*phead = newnode;
}
void SListPopBack(SListNode** phead)
{
if (*phead == NULL)
return;
//assert(*phead!=NULL);
if ((*phead)->next == NULL)
{
free(*phead);
*phead = NULL;
}
else
{
SListNode* tail = *phead;
SListNode* t = tail;
while (tail->next != NULL)
{
t = tail;
tail = tail->next;
}
free(tail);
tail = NULL;
t->next = NULL;
//SListNode* tail = *phead;
//while (tail->next->next != NULL)
//{
// tail = tail->next;
//}
//free(tail->next);
//tail->next = NULL;
}
}
void SListPopFront(SListNode** phead)
{
if (*phead == NULL)
return;
SListNode* front=(*phead)->next;
free(*phead);
*phead = front;
}
SListNode* SListFind(SListNode* phead, SLTDataType x)
{
SListNode* cur = phead;
while (cur)
{
if (cur->data == x)
{
return cur;
}
else
{
cur = cur->next;
}
}
return NULL;
}
void SListInsert(SListNode** phead, SListNode* pos, SLTDataType x)//pos配合查找函数
{
SListNode* newnode = BuySListNode(x);
if (*phead == pos)
{
void SListPopFront(phead);
}
//找到pos的前一位置
else
{
SListNode* posPrey = *phead;
while (posPrey->next != pos)
{
posPrey = posPrey->next;
}
posPrey->next = newnode;
newnode->next = pos;
}
}
void SListInsertTail(SListNode* pos, SLTDataType x)
{
assert(pos);
SListNode* newnode = BuySListNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
void SListErase(SListNode** phead, SListNode* pos)
{
assert(head && (*head));
assert(pos);
if (*phead == pos)
{
*phead = (*phead)->next;
free(pos);
pos = NULL;
}
else
{
SListNode* prev = *phead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
pos = NULL;
}
}
void SListEraseAfter(SListNode* pos)
{
assert(pos);
if (pos == NULL)
{
exit(-1);
}
SListNode* next = pos->next;
pos->next = next->next;
free(next);
next = NULL;
}
void SListDestroy(SListNode** phead)
{
assert(phead && *phead);
SListNode* plist = *phead;
while (plist->next != NULL)
{
plist = plist->next;
free(*phead);
*phead = plist;
}
free(*phead);
*phead = NULL;
}
2. slist.h
#define _CRT_SECURE_NO_WARNINGS h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
typedef int SLTDataType;
typedef struct SListNode
{
int data;
struct SListNode* next;
}SListNode;
void SListPrint(SListNode* phead);
//打印
void SListPushBack(SListNode** phead, SLTDataType x);
//尾插
void SListPushFront(SListNode** phead,SLTDataType x);
//头插
void SListPopBack(SListNode** phead);
//尾删
void SListPopFront(SListNode** phead);
//头删
SListNode* SListFind(SListNode* phead, SLTDataType x);
//找位置
//在pos位置之前去插入一个节点
void SListInsert(SListNode** phead, SListNode* pos, SLTDataType x);
//在pos前面插入
//void SListInsert(SListNode* phead, int pos, SLTDataType x);
void SListErase(SListNode** phead, SListNode* pos);
//找坐标删除
void SListEraseAfter(SListNode* pos);
//删除坐标后面的值
void SListDestroy(SListNode** phead)
3. 测试文件
#define _CRT_SECURE_NO_WARNINGS h
#include"sllist.h"
#include<stdio.h>
#include<stdlib.h>
void TestSList1()
{
SListNode* plist = NULL;//初始值
SListPushBack(&plist, 1);
SListPushBack(&plist, 2);
SListPushBack(&plist, 3);
SListPushFront(&plist, 4);
SListPushFront(&plist, 5);
SListPrint(plist);
SListPopFront(&plist);
SListPopFront(&plist);
SListPrint(plist);
}
void TestSList2()
{
SListNode* plist = NULL;//初始值
SListPushBack(&plist, 1);
SListPushBack(&plist, 2);
SListPushBack(&plist, 3);
SListPushBack(&plist, 3);
SListPushBack(&plist, 2);
SListNode* pos = SListFind(plist, 2);
SListInsert(&plist ,pos,20);
SListPrint(plist);
//SListDestroy(&plist);
//SListPrint(plist);
int i = 1;
while (pos)
{
printf("第%d个节点%p->%d\n", i++, pos, pos->data);
pos = SListFind(pos->next, 2);
}
pos = SListFind(plist, 3);
while(pos)
{
pos->data = 30;
pos = SListFind(pos->next, 3);
}
SListPrint(plist);
}
typedef struct xx
{
int* n;
}xx;
int main()
{
TestSList2();
//xx p;
//int a = 0;
//p.n = &a;
//a = 2;
//printf("%d", *(p.n));
return 0;
}
这篇文章就到这里了,希望可以帮到您
(๑′ᴗ‵๑)I Lᵒᵛᵉᵧₒᵤ❤