目录
一、什么是链表
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链 接次序实现的 。
二、顺序表的缺陷
1.头部/中间的插入删除,时间复杂度为O(N)
2.增容需要realloc申请新空间,拷贝数据,释放旧空间,也会有大量消耗
3.增容一般呈2倍增长,也会有一定的空间浪费
三、链表的实现
1.链表创建
跟顺序表不同,链表每个节点分为两个部分,一部分是数据,另一部分是指向下一节点的指针,物理空间不连续,data存放的数据,next存放的是下一个节点的地址。
typedef int SLTDataType;
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
}SLTNode;
2.接口设计
因为创建链表时使用的是一级指针,所以当我们要修改一级指针时需要运用二级指针来接收。
个别接口不用二级指针接收,例如 打印 ,当我们打印时只需要读取数据,所以用一级指针就能完成。
void test1()
{
SLTNode* plist = NULL;
SLPushFront(&plist, 1);
SLPushFront(&plist, 2);
SLPushFront(&plist, 3);
SLPushFront(&plist, 4);
SLPrint(plist);
}
void SLPrint(SLTNode* phead);//打印
SLTNode* SLFind(SLTNode* phead, SLTDataType x);//查找
SLTNode* BuyLTNode(SLTDataType x);//申请空间
void SLPushFront(SLTNode** pphead, SLTDataType x);//头插
void SLPushBack(SLTNode** pphead, SLTDataType x);//尾插
void SLPopFront(SLTNode** pphead);//头删
void SLPopBack(SLTNode** pphead);//尾删
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);//中间前面插入
void SLErase(SLTNode** pphead, SLTNode* pos);//中间删除
void SLInsertAfter(SLTNode* pos, SLTDataType x);//中间后面插入
void SLEraseAfter(SLTNode* pos);//中间后面删除
void SLDestroy(SLTNode** pphead);//销毁
1.打印链表
利用循环遍历链表,通过next指向下一个节点,到NULL为止
void SLPrint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
2.创建节点
SLTNode* BuyLTNode(SLTDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
printf("BuyLTNode is error");
return;
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
3.头插
创建节点后,指向原来的头节点,再让链表头指向新节点即可,不用考虑链表为空
void SLPushFront(SLTNode** pphead, SLTDataType x)
{
assert(pphead);//链表为空,pphead也不能为空,因为他是头指针plist地址
//assert(*pphead);//不能断言,链表为空,也需要插入
SLTNode* newnode = BuyLTNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
4.尾插
尾插分为两种情况:
1.链表为空:直接创建节点让链表头指向新节点即可。
2.链表不为空:先使用循环找到链表尾,然后使链表尾指向新节点,新节点指向空。
void SLPushBack(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
SLTNode* newnode = BuyLTNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
5.头删
先断言是否传入空指针,然后让链表头指向下一节点,释放刚才的链表头。
void SLPopFront(SLTNode** pphead)
{
assert(pphead);
assert(*pphead);
SLTNode* head = *pphead;
*pphead = head->next;
free(head);
}
6.尾删
尾删也有两种情况:
先断言是否传入空指针
1.链表只有一个节点:直接释放链表头并置空
2.链表有一个以上节点:先使用循环找到链表尾的前一个节点,然后释放链表尾并置空。
(释放前,必须将倒数第二个节点的指针指向空,否则将在指向最后一个被释放的节点时,变成野指针。)
void SLPopBack(SLTNode** pphead)
{
assert(pphead);
assert(*pphead);
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
//第一种方法
//SLTNode* prev = NULL;
//SLTNode* tail = *pphead;
//assert(tail);
//while (tail->next != NULL)
//{
// prev = tail;
// tail = tail->next;
//}
//free(tail);
//prev->next = NULL;
//第二种方法
SLTNode* tail = *pphead;
while (tail->next->next != NULL)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
7.查找
利用遍历,判断每一个数据,返回数据的地址
SLTNode* SLFind(SLTNode* phead, SLTDataType x)
{
assert(phead);
SLTNode* cur = phead;
while (cur)
{
if (cur->data == x)
{
return cur;
}
else
{
cur = cur->next;
}
}
return NULL;
}
8.中间插入
分为两种情况,一种是目标插入,一种是目标后方插入。
1.目标插入
1.在链表头插入,相当于头插。
2.在非链表头插入,先遍历链表找到目标位置的前一个节点,然后申请新节点使其指向目标位置地址,最后使目标位置的前一个节点指向新节点。
//在pos前面插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
assert(pphead);
assert(pos);
if (*pphead == NULL)
{
SLPushFront(pphead, x);
}
else
{
SLTNode* cur = *pphead;
while (cur->next != pos)
{
cur = cur->next;
}
SLTNode* newnode = BuyLTNode(x);
newnode->next = pos;
cur->next = newnode;
}
}
2.目标后方插入
直接申请新节点,插入目标节点后即可。
//在pos后面插入
void SLInsertAfter(SLTNode* pos, SLTDataType x)
{
assert(pos);
SLTNode* newnode = BuyLTNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
9.中间删除
分为两种情况,一种是目标删除,一种是目标后方删除。
1.目标删除
1.删除链表头时,删除节点相当于头删
2.当链表含有多个节点时,需要将目标上一个节点指向目标下一个节点,再释放目标节点。
void SLErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead);
assert(pos);
if (*pphead == pos)
{
SLPopFront(pphead);
}
else
{
SLTNode* cur = *pphead;
while (cur->next != pos)
{
cur = cur->next;
}
cur->next = pos->next;
free(pos);
}
}
2.目标后方删除
用变量tail记录下目标地址的next,再让目标地址的next指向tail->next,再释放tail即可。
void SLEraseAfter(SLTNode* pos)
{
assert(pos);
assert(pos->next);
SLTNode* tail = pos->next;
pos->next = tail->next;
free(tail);
}
10.销毁
void SLDestroy(SLTNode** pphead)
{
assert(pphead);
SLTNode* cur = *pphead;
SLTNode* tail = NULL;
while (cur)
{
tail = cur;
cur = cur->next;
free(tail);
}
}
四、全部代码
SList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
typedef int SLTDataType;
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
}SLTNode;
void SLPrint(SLTNode* phead);//打印
SLTNode* SLFind(SLTNode* phead, SLTDataType x);//查找
SLTNode* BuyLTNode(SLTDataType x);//申请空间
void SLPushFront(SLTNode** pphead, SLTDataType x);//头插
void SLPushBack(SLTNode** pphead, SLTDataType x);//尾插
void SLPopFront(SLTNode** pphead);//头删
void SLPopBack(SLTNode** pphead);//尾删
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);//中间插入
void SLErase(SLTNode** pphead, SLTNode* pos);//中间删除
void SLInsertAfter(SLTNode* pos, SLTDataType x);//中间后面插入
void SLEraseAfter(SLTNode* pos);//中间后面删除
void SLDestroy(SLTNode** pphead);//销毁
SList.c
#define _CRT_SECURE_NO_WARNINGS
#include"SList.h"
void SLPrint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
SLTNode* BuyLTNode(SLTDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
printf("BuyLTNode is error");
return;
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
void SLPushFront(SLTNode** pphead, SLTDataType x)
{
assert(pphead);//链表为空,pphead也不能为空,因为他是头指针plist地址
//assert(*pphead);//不能断言,链表为空,也需要插入
SLTNode* newnode = BuyLTNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
void SLPushBack(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
SLTNode* newnode = BuyLTNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
void SLPopFront(SLTNode** pphead)
{
assert(pphead);
assert(*pphead);
SLTNode* head = *pphead;
*pphead = head->next;
free(head);
}
void SLPopBack(SLTNode** pphead)
{
assert(pphead);
assert(*pphead);
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
//第一种方法
//SLTNode* prev = NULL;
//SLTNode* tail = *pphead;
//assert(tail);
//while (tail->next != NULL)
//{
// prev = tail;
// tail = tail->next;
//}
//free(tail);
//prev->next = NULL;
//第二种方法
SLTNode* tail = *pphead;
while (tail->next->next != NULL)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
SLTNode* SLFind(SLTNode* phead, SLTDataType x)
{
assert(phead);
SLTNode* cur = phead;
while (cur)
{
if (cur->data == x)
{
return cur;
}
else
{
cur = cur->next;
}
}
return NULL;
}
//在pos插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
assert(pphead);
assert(pos);
if (*pphead == NULL)
{
SLPushFront(pphead, x);
}
else
{
SLTNode* cur = *pphead;
while (cur->next != pos)
{
cur = cur->next;
}
SLTNode* newnode = BuyLTNode(x);
newnode->next = pos;
cur->next = newnode;
}
}
//在pos后面插入
void SLInsertAfter(SLTNode* pos, SLTDataType x)
{
assert(pos);
SLTNode* newnode = BuyLTNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
void SLErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead);
assert(pos);
if (*pphead == pos)
{
SLPopFront(pphead);
}
else
{
SLTNode* cur = *pphead;
while (cur->next != pos)
{
cur = cur->next;
}
cur->next = pos->next;
free(pos);
}
}
void SLEraseAfter(SLTNode* pos)
{
assert(pos);
assert(pos->next);
SLTNode* tail = pos->next;
pos->next = tail->next;
free(tail);
}
void SLDestroy(SLTNode** pphead)
{
assert(pphead);
SLTNode* cur = *pphead;
SLTNode* tail = NULL;
while (cur)
{
tail = cur;
cur = cur->next;
free(tail);
}
}
Test.c
#define _CRT_SECURE_NO_WARNINGS
#include"SList.h"
void test1()
{
SLTNode* plist = NULL;
SLPushFront(&plist, 1);
SLPushFront(&plist, 2);
SLPushFront(&plist, 3);
SLPushFront(&plist, 4);
SLPrint(plist);
SLDestroy(&plist);
}
void test2()
{
SLTNode* plist = NULL;
SLPushBack(&plist, 1);
SLPushFront(&plist, 2);
SLPushFront(&plist, 3);
SLPushBack(&plist, 4);
SLPrint(plist);
SLPopFront(&plist);
SLPrint(plist);
SLPopBack(&plist);
SLPrint(plist);
SLDestroy(&plist);
}
void test3()
{
SLTNode* plist = NULL;
SLPushFront(&plist, 1);
SLPushFront(&plist, 2);
SLPushFront(&plist, 3);
SLPushFront(&plist, 4);
SLPrint(plist);
SLPopFront(&plist);
SLPopFront(&plist);
SLPopFront(&plist);
SLPopFront(&plist);
SLPrint(plist);
SLDestroy(&plist);
}
void test4()
{
SLTNode* plist = NULL;
SLPushFront(&plist, 1);
SLPushFront(&plist, 2);
SLPushFront(&plist, 3);
SLPushFront(&plist, 4);
SLPrint(plist);
SLTNode* prev = SLFind(plist, 3);
SLInsert(&plist, prev, 10);
SLInsertAfter(prev, 18);
SLPrint(plist);
SLErase(&plist, prev);
SLPrint(plist);
prev = SLFind(plist, 2);
SLEraseAfter(prev);
SLPrint(plist);
SLDestroy(&plist);
}
int main()
{
test1();
return 0;
}