线性表之链表
作者:互联网
目录
1.链表表示和实现(单链表+双向链表)
2.单链表的实现
// 1、无头+单向+非循环链表增删查改实现
#pragma once
#include <stdio.h>
#include <stdlib.h>
typedef int SLTDateType;
typedef struct SListNode
{
SLTDateType data; // 存放节点的数据
struct SListNode* next; // 下一个节点的位置
};
typedef struct SListNode SLTNode;
// 动态申请一个节点
SLTNode* BuySListNode(SLTDataType x);
/*(不会改变链表的头指针,传一级指针)*/
// 单链表打印
void SListPrint(SLTNode* phead);
/*(可能会改变链表的头指针,传二级指针)*/
// 单链表尾插
void SListPushBack(SLTNode** pphead, SLTDataType x);
// 单链表的头插
void SListPushFront(SLTNode** pphead, SLTDataType x);
// 单链表的尾删
void SListPopBack(SLTNode** pphead);
// 单链表头删
void SListPopFront(SLTNode** pphead);
// 单链表查找
SLTNode* SListFind(SLTNode* phead, SLTDataType x);
// 单链表在pos位置之前插入x
void SListInsert(SLTNode** phead, SLTNode* pos, SLTDataType x);
// 单链表删除pos位置的值
void SListErase(SLTNode** phead, SLTNode* pos);
// 有些地方也有这样的
在pos的前面插入x
//void SListInsert(SLTNode** phead, int i, SLTDataType x);
删除pos位置的值
//void SListErase(SLTNode** phead, int i);
#include "SList.h"
// 动态申请一个节点
SLTNode* BuySListNode(SLTDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
//当引发了异常: 写入访问权限冲突,data无法读取内存
//错误是没有引用包含 malloc 的头文件
newnode->data = x;
newnode->next = NULL;
return newnode;
}
/*(不会改变链表的头指针,传一级指针)*/
// 单链表打印
void SListPrint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur != NULL) // 可以理解为看自己为不为空
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
/*(可能会改变链表的头指针,传二级指针)*/
// 单链表尾插
// pphead 存放的是 plist的地址,要改变plist,需要解引用(*pphead)
void SListPushBack(SLTNode** pphead, SLTDataType x)
{
SLTNode* newnode = BuySListNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
// 找尾节点的指针
SLTNode* tail = *pphead;
while (tail->next != NULL) // 可以理解为看下一个为不为空
{
tail = tail->next;
}
// 尾节点,链接新节点
tail->next = newnode;
}
}
// 单链表的头插
void SListPushFront(SLTNode** pphead, SLTDataType x)
{
SLTNode* newnode = BuySListNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
// 单链表的尾删
void SListPopBack(SLTNode** pphead)
{
// 1. 空
// 2. 一个节点
// 3. 一个以上节点
if (*pphead == NULL)
{
return;
}
else if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* pre = NULL;
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
pre = tail;
tail = tail->next;
}
pre->next = NULL;
}
}
// 单链表头删
void SListPopFront(SLTNode** pphead)
{
// 先保存,后free
SLTNode* next = (*pphead)->next;
free(*pphead);
*pphead = next;
}
// 单链表查找
SLTNode* SListFind(SLTNode* phead, SLTDataType x)
{
SLTNode* newnode = BuySListNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
// 单链表在pos位置之前插入x
void SListInsert(SLTNode** phead, SLTNode* pos, SLTDataType x)
{
// 当只有一个节点
if (pos == *pphead)
{
SListPushFront(pphead, x);
}
else
{
SLTNode* newnode = BuySListNode(x);
SLTNode* pre = *pphead;
while (pre->next != pos)
{
pre = pre->next;
}
pre->next = newnode;
newnode->next = pos;
}
}
// 单链表删除pos位置的值
void SListErase(SLTNode** phead, SLTNode* pos)
{
if (pos == *pphead)
{
SListPopFront(pphead, pos);
}
else
{
SLTNode* pre = *pphead;
while (pre->next != pos)
{
pre = pre->next;
}
pre->next = pos->next;
free(pos);
}
}
#include "SList.h"
void TestSList1()
{
SLTNode* plist = NULL;
SListPushBack(&plist, 1); // 这里要传地址,因为要改变plist
SListPushBack(&plist, 2); // plist又是一个指针,所以后面函数要用二级指针
SListPushBack(&plist, 3);
SListPushBack(&plist, 4);
SListPushFront(&plist, 0);
SListPrint(plist);
SListPopFront(&plist);
SListPopFront(&plist);
SListPopFront(&plist);
SListPrint(plist);
SListPopFront(&plist);
SListPopFront(&plist);
SListPrint(plist);
}
void TestSList2()
{
SLTNode* plist = NULL;
SListPushBack(&plist, 1);
SListPushBack(&plist, 2);
SListPushBack(&plist, 3);
SListPushBack(&plist, 4);
SListPrint(plist);
SListPopBack(&plist);
SListPopBack(&plist);
SListPopBack(&plist);
SListPopBack(&plist);
SListPopBack(&plist);
SListPrint(plist);
}
void TestSList3()
{
SLTNode* plist = NULL;
SListPushBack(&plist, 1);
SListPushBack(&plist, 2);
SListPushBack(&plist, 3);
SListPushBack(&plist, 4);
SListPrint(plist);
// 想在3的前面插入一个30 ,需要先找到要删除节点的位置pos
SLTNode* pos = SListFind(plist, 1);
if (pos)
{
SListInsert(&plist, pos, 10);
}
SListPrint(plist);
pos = SListFind(plist, 3);
if (pos)
{
SListInsert(&plist, pos, 30);
}
SListPrint(plist);
}
void TestSList4()
{
SLTNode* plist = NULL;
SListPushBack(&plist, 1);
SListPushBack(&plist, 2);
SListPushBack(&plist, 3);
SListPushBack(&plist, 4);
SListPrint(plist);
SLTNode* pos = SListFind(plist, 1);
if (pos)
{
SListErase(&plist, pos);
}
SListPrint(plist);
pos = SListFind(plist, 4);
if (pos)
{
SListErase(&plist, pos);
}
SListPrint(plist);
pos = SListFind(plist, 3);
if (pos)
{
SListErase(&plist, pos);
}
SListPrint(plist);
pos = SListFind(plist, 2);
if (pos)
{
SListErase(&plist, pos);
}
SListPrint(plist);
}
int main()
{
TestSList4();
return 0;
}
3. 双链表的实现
-
接口实现(List.h)
// 2、带头+双向+循环链表增删查改实现
// 带头双向循环 -- 最优链表结构,任意位置插入删除都是O(1)
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* next;
struct ListNode* prev;
LTDataType data;
}ListNode;
// 创建节点
ListNode* BuyListNode(LTDataType x)
// 双向链表初始化
ListNode* ListInit();
// 双向链表销毁
void ListDestory(ListNode* phead);
// 双向链表打印
void ListPrint(ListNode* phead);
// 双向链表尾插
void ListPushBack(ListNode* phead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* phead);
// 双向链表头插
void ListPushFront(ListNode* phead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* phead);
// 双向链表查找
ListNode* ListFind(ListNode* phead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);
//bool ListEmpty(ListNode* phead);
//int ListSize(ListNode* phead);
-
接口函数实现(List.c)
// 创建节点
ListNode* BuyListNode(LTDataType x)
{
ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
newnode->data = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
// 双向链表初始化
ListNode* ListInit()
{
ListNode* phead = BuyListNode(0);
phead->next = phead; // 哨兵节点指向自己
phead->prev = phead; // 哨兵节点指向自己
return phead;
}
// 双向链表销毁
void ListDestory(ListNode* phead)
{
assert(phead); // 断言传入的phead是否为空
ListNode* cur = phead->next;
while (cur != phead) // 双向循环链表有一个哨兵节点
{
// 要先记录下一个节点,然后释放当前节点,否者会找不到下一个节点
ListNode* next = cur->next;
free(cur);
cur = next;
}
// 释放哨兵节点
free(phead);
phead = NULL;
}
// 双向链表打印
void ListPrint(ListNode* phead)
{
ListNode* cur = phead->next;
while (cur != phead)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
// 双向链表尾插
void ListPushBack(ListNode* phead, LTDataType x)
{
/// 断言的好处,当代码有几十万行代码时,这里phead传入错误时
/// 代码运行后里面会显示某个.c某行出现问题了
assert(phead);
//ListNode* tail = phead->prev; //找尾节点
//ListNode* newnode = BuyListNode(x); //创建新的节点
//tail->next = newnode; //尾节点指向新的节点
//newnode->prev = tail; //新的节点要指向前面的节点
//newnode->next = phead; //新创建的尾节点要指向第一个节点
//phead->prev = newnode; //第一个节点要重新指向新创建的最后的节点
ListInsert(phead, x); /// 尾插就是在phead前面
}
// 双向链表尾删
void ListPopBack(ListNode* phead)
{
assert(phead);
//assert(phead->next != phead); // 当没有节点时,不能删
//ListNode* tail = phead->prev;
//ListNode* prev = tail->prev;
//prev->next = phead;
//phead->prev = prev;
//free(tail);
//tail = NULL;
ListErace(phead->prev);
}
// 双向链表头插(不推荐写法,顺序不能变不易掌控)
//void ListPushFront(ListNode* phead, LTDataType x)
//{
// assert(phead);
//
// //ListNode* first = phead->next;
// ///在不创建first,后面的顺序就不能变
// ///这种方法不推荐
// ListNode* newnode = BuyListNode(x);
//
// newnode->next = phead->next;
// phead->next->prev = newnode;
//
// phead->next = newnode;
// newnode->prev = phead;
//}
// 双向链表头插(推荐写法)
void ListPushFront(ListNode* phead, LTDataType x)
{
assert(phead);
//ListNode* first = phead->next;
//ListNode* newnode = BuyListNode(x);
//phead->next = newnode;
//newnode->next = first;
//first->prev = newnode;
//newnode->prev = phead;
ListInsert(phead->next, x);
}
// 双向链表头删
void ListPopFront(ListNode* phead)
{
assert(phead);
//assert(phead->next!=phead); // 当没有节点时,不能删
//ListNode* first = phead->next;
//ListNode* second = first->next;
//phead->next = second;
//second->prev = phead;
//free(first);
//first = NULL;
ListErace(phead->next);
}
// 双向链表查找
ListNode* ListFind(ListNode* phead, LTDataType x)
{
assert(phead);
ListNode* cur = phead->next;
while (cur != phead)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x)
{
assert(pos);
ListNode* prev = pos->prev;
ListNode* newnode = BuyListNode(x);
// prev newnode pos
prev->next = newnode;
newnode->prev = prev;
newnode->next = pos;
pos->prev = newnode;
}
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{
assert(pos);
ListNode* prev = pos->prev;
ListNode* next = pos->next;
prev->next = next;
next->prev = prev;
free(pos);
}
//bool ListEmpty(ListNode* phead);
//int ListSize(ListNode* phead);
-
主文件(text.c)
#include "List.h"
void TestList1()
{
ListNode* plist = ListInit();
ListPushBack(plist, 1);
ListPushBack(plist, 2);
ListPushBack(plist, 3);
ListPushBack(plist, 4);
ListPrint(plist);
ListPushFront(plist, 0);
ListPushFront(plist, -1);
ListPrint(plist);
ListPopFront(plist);
ListPopFront(plist);
ListPopFront(plist);
ListPrint(plist);
ListPopBack(plist);
ListPrint(plist);
ListDestory(plist);
}
void TestList2()
{
ListNode* plist = ListInit();
ListPushBack(plist, 1);
ListPushBack(plist, 2);
ListPushBack(plist, 3);
ListPushBack(plist, 4);
ListPrint(plist);
ListNode* pos = ListFind(plist, 3);
if (pos)
{
// 查找,附带着修改的作用
pos->data *= 10;
printf("找到了,并且节点的值乘以10\n");
}
else
{
printf("没有找到\n");
}
ListPrint(plist);
ListInsert(pos, 300);
ListPrint(plist);
ListErase(pos);
ListPrint(plist);
}
int main()
{
TestList1();
return 0;
}
4.顺序表和链表的区别和联系
标签:ListNode,线性表,pos,next,链表,phead,SLTNode,plist 来源: https://blog.csdn.net/weixin_54183294/article/details/123031153