前言
学习了顺序表过后,我们再来看一种新的线性表----单链表(Singly Linked List)。
单链表在逻辑上是线性结构,但是在物理结构上不是连续的, 链表在物理上存储时,通常以链式结构的形式存储。
一、单链表的概念
1. 单链表是什么?
概念:链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构,数据元素的逻辑顺序是通过链表中的
指针链接次序实现的。
单链表就像是火车一样,每一节车厢都用链子相互连接起来。
淡季时⻋次的⻋厢会相应减少,旺季时⻋次的⻋厢会额外增加⼏节。只需要将⽕⻋⾥的某节⻋厢去掉/
加上,不会影响其他⻋厢,每节⻋厢都是独⽴存在的。
2. 节点
节点就是每一节车厢中的元素,就像车厢中包含了人,服务员,行李…
节点中的元素一个是包含了节点所储存的数据,另一个是指向下一个节点地址的指针~
链表的结构:
在链表中,每个节点都有两个主要组成部分:
数据部分:存储当前节点的数据信息。
指针部分:存储下一个节点的地址(指针),用于指向下一个节点的位置。
plist 的作用:
plist 是一个指向链表第一个节点的指针,最初它指向链表的头节点(即第一个节点)。如果我们想要让 plist 指向第二个节点,只需要更新它的值为第二个节点的地址(例如 0x0012FFA0)。
链表操作的动态特性:
与顺序表不同,链表的每个节点都是在需要的时候动态分配的。每当插入数据时,使用 malloc 等内存分配函数申请一块内存空间,并创建新的节点。通过前一个节点的指针指向新创建的节点,使链表得以保持连接。
二、链表的分类
链表的结构⾮常多样,以下情况组合起来就有8种(2 x 2 x 2)链表结构:
链表说明:
1. 单向或双向链表
2. 带头节点或不带头结点链表
头节点又叫哨兵位,它不存储有效的数据,也不可以被改变。
3. 循环或不循环链表
我们主要学习的两种链表分别是单向不带头不循环链表,简称单链表。
双向带头循环链表,简称双链表。
三、单链表的实现
1.单链表的结构
typedef int SLTDataType;
typedef struct SLTNode
{
SLTDataType date; //储存的数据
struct SLTNode* next; //下一个结点的地址
}SLTNode;
2. 单链表的打印
//链表的打印
void SLTPrint(SLTNode* phead);
打印链表就是要遍历链表,打印出每个节点储存的内容。
//打印链表
void SLTPrint(SLTNode* phead)
{
SLTNode* pcur;
pcur = phead;
while (pcur)
{
printf("%d -> ", pcur->date);
pcur = pcur->next;
}
printf("NULL\n");
}
3. 单链表尾插
//链表尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x);
在链表尾插之前,我们先思考一个我问题,链表不像顺序表,一次开辟了连续的空间,链表只是一个一个的节点连接起来。那么,我们就需要对新插入的元素申请新的节点。
//申请新节点
SLTNode* SLTBuyNode(SLTDataType x)
{
SLTNode* node = (SLTNode* )malloc(sizeof(SLTNode));
if (node == NULL)
{
perror("malloc file!");
exit(1);
}
node->date = x;
node->next = NULL;
return node;
}
申请好空间后就要开始插入了,那么就又有一个问题。这里的传参为什么是二级指针呢?
一级指针可不可以呢?
我们可以看到,如果使用一级指针,假设我们链表当前元素为0,那么在插入数据的时候我们就需要在phead处插入,就相当于改变了我们传过来的形参,如phead = newnode,看似是地址的赋值,但是指针这里是作为形参的,形参的改变不会影响到实参,因此不能传一级指针。
借助函数栈帧来看
函数栈帧的学习
像下面这张图,形参的创建实在调用函数call指令时拷贝了一份实参的数据,他们值相同,空间不同,形参的改变不会影响实参。
因此我们要传二级指针~
//链表尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
SLTNode* newnode = SLTBuyNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
SLTNode* pcur = *pphead;
while (pcur->next)
{
pcur = pcur->next;
}
pcur->next = newnode;
}
}
4. 单链表头插
//链表头插
void SLTPushFront(SLTNode** pphead, SLTDataType x);
//链表头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
SLTNode* newnode = SLTBuyNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
5. 单链表尾删
//链表尾删
void SLTPopBack(SLTNode** pphead);
链表只有一个节点时,就是要删除头节点
有多个节点时,为了删除数据,将最后一个数据删除,就要将前一个节点的next置空。
因此我们需要prev来记录前一个节点的位置。
//链表尾删
void SLTPopBack(SLTNode** pphead)
{
assert(pphead && *pphead);
//处理只有一个节点的情况
if ((*pphead)->next==NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* ptail = *pphead;
SLTNode* prev = NULL;
while (ptail->next)
{
prev = ptail;
ptail = ptail->next;
}
prev->next = NULL;
free(ptail);
ptail = NULL;
}
}
6. 单链表头删
//链表头删
void SLTPopFront(SLTNode** pphead);
头删需要提前保存下一个节点,不然删除之后无法访问下一个节点将他置为头节点。
//链表头删
void SLTPopFront(SLTNode** pphead)
{
assert(pphead&& *pphead);
SLTNode* next = (*pphead)->next;
free(*pphead);
*pphead = next;
}
7. 单链表查找
//链表查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
和打印一样,查找不就是遍历链表吗?遇到 pcur->data = x 就返回当前节点。
//链表查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
assert(phead);
SLTNode* pcur = phead;
while (pcur)
{
if (pcur->date == x)
{
return pcur;
}
else
{
pcur = pcur->next;
}
}
//没有找到
return NULL;
}
8. 单链表指定位置之前插入数据
//指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
如果这个位置就是头节点的话就是头插。
头插就涉及到了头节点的改变因此需要传二级指针
如果当前位置不是头节点
其中我们一共牵扯到了原链表中的两个数据,因此我们需要知道pos位置的节点,和pos位置的前驱节点prev。
//指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
assert(pphead);
assert(pos);
if (pos == *pphead)
{
SLTPushFront(pphead, x);
}
else
{
SLTNode* newnode = SLTBuyNode(x);
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
newnode->next = pos;
prev->next = newnode;
}
}
9. 单链表指定位置之后插入数据
//指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
向后插入相对于向前插入要简单很多,因为向后插入不涉及pos之前的节点,只需要pos节点就可以从pos开始取出向后的所有节点。
向后插入涉及不到头结点,因此传一级指针就可以了。
//指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
assert(pos);
SLTNode* newnode = SLTBuyNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
10. 单链表删除指定位置的节点
//删除指定位置的节点
void SLTErase(SLTNode** pphead, SLTNode* pos);
同样的,删除指定位置之后的节点需要pos节点之前的前驱指针,因此需要头节点来一步一步遍历。
删除指定位置的数据有可能删除头节点,因此需要传二级指针。
//删除指定位置的节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead && *pphead && pos);
if (pos == *pphead)
{
SLTPopFront(pphead);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
pos = NULL;
}
}
11. 单链表删除指定位置之后的节点
//删除指定位置之后的节点
void SLTEraseAfter(SLTNode* pos);
删除pos位置之后的元素,不用涉及到pos的前驱,用pos就可以找到他的后继。
删除pos之后的元素用不到头节点,因此传一级指针就可以。
//删除指定位置之后的节点
void SLTEraseAfter(SLTNode* pos)
{
assert(pos&&pos->next);
SLTNode* del = pos->next;
pos->next = pos->next->next;
free(del);
del = NULL;
}
12. 单链表的销毁
//销毁链表
void SLTDestroy(SLTNode** pphead)
销毁链表需要我们遍历链表,手动销毁链表中所有动态开辟的节点。
为了保证销毁后还能找到原链表,需要用next记录链表的pcur的后继节点。
//销毁链表
void SLTDestroy(SLTNode** pphead)
{
assert(pphead && *pphead);
SLTNode* pcur = *pphead;
while (pcur)
{
SLTNode* next = pcur->next;
free(pcur);
pcur = next;
}
*pphead = NULL;
}
三、小试牛刀,单链表练习题
1. 反转链表
https://leetcode.cn/problems/reverse-linked-list/description/
/*
解题思路: 此题一般常用的方法有两种,三指针翻转法和头插法
1. 三指针翻转法
记录连续的三个节点,原地修改节点指向
2. 头插法
每一个节点都进行头插
*/
// 三个指针翻转的思想完成逆置
struct ListNode* reverseList(struct ListNode* head) {
if(head == NULL || head->next == NULL)
return head;
struct ListNode* n1, *n2, *n3;
n1 = head;
n2 = n1->next;
n3 = n2->next;
n1->next = NULL;
//中间节点不为空,继续修改指向
while(n2)
{
//中间节点指向反转
n2->next = n1;
//更新三个连续的节点
n1 = n2;
n2 = n3;
if(n3)
n3 = n3->next;
}
//返回新的头
return n1;
}
// 取节点头插的思想完成逆置
struct ListNode* reverseList(struct ListNode* head) {
struct ListNode* newhead = NULL;
struct ListNode* cur = head;
while(cur)
{
struct ListNode* next = cur->next;
//头插新节点,更新头
cur->next = newhead;
newhead = cur;
cur = next;
}
return newhead;
}
2. 链表的中间结点
https://leetcode.cn/problems/middle-of-the-linked-list/description/
/*
解题思路:
通过快慢指针找到中间节点,快指针每次走两步,慢指针每次走一步,当快指针走到结尾的时候,慢指针正好走到中间位置
*/
typedef struct ListNode Node;
struct ListNode* middleNode(struct ListNode* head){
Node* slow = head;
Node* fast = head;
while(fast!=NULL && fast->next != NULL)
{
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
3. 合并两个有序链表
https://leetcode.cn/problems/merge-two-sorted-lists/description/
/*
解题思路:
此题可以先创建一个空链表,然后依次从两个有序链表中选取最小的进行尾插操作进行合并。
*/
typedef struct ListNode Node;
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
if(l1 == NULL)
return l2;
else if(l2 == NULL)
return l1;
Node* head = NULL, *tail = NULL;
//创建空链表
head = tail = (Node*)malloc(sizeof(Node));
tail->next = NULL;
while(l1 && l2)
{
// 取小的进行尾插
if(l1->val < l2->val)
{
tail->next = l1;
tail = tail->next;
l1 = l1->next;
}
else
{
tail->next = l2;
tail = tail->next;
l2 = l2->next;
}
}
//剩余元素直接拼接
if(l1)
tail->next = l1;
else
tail->next = l2;
Node* list = head->next;
free(head);
return list;
}
4. 链表分割
https://www.nowcoder.com/practice/0e27e0b064de4eacac178676ef9c9d70
/*
解题思路
创建两个链表,分别存放小于x的节点和大于等于x的节点,分别进行尾插
*/
class Partition {
public:
ListNode* partition(ListNode* pHead, int x) {
if(pHead == NULL)
return NULL;
struct ListNode* lessHead, *lessTail,*greaterHead, *greaterTail;
//创建链表表头
lessHead = lessTail = (struct ListNode*)malloc(sizeof(struct ListNode));
greaterHead = greaterTail = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* cur = pHead;
while(cur)
{
//小于x的尾插到lessTail
if(cur->val < x)
{
lessTail->next = cur;
lessTail = lessTail->next;
}
//大于等于x的尾插到greaterTail
else
{
greaterTail->next = cur;
greaterTail = greaterTail->next;
}
cur = cur->next;
}
//链接两个链表
lessTail->next = greaterHead->next;
greaterTail->next = NULL;
//获取表头
pHead = lessHead->next;
free(lessHead);
free(greaterHead);
return pHead;
}
};
5. 链表的回⽂结构
https://www.nowcoder.com/practice/d281619e4b3e4a60a2cc66ea32855bfa
/*
解题思路:
此题可以先找到中间节点,然后把后半部分逆置,最近前后两部分一一比对,如果节点的值全部相同,则即为回文。
*/
class PalindromeList {
public:
bool chkPalindrome(ListNode* A) {
if (A == NULL || A->next == NULL)
return true;
ListNode* slow, *fast, *prev, *cur, *nxt;
slow = fast = A;
//找到中间节点
while (fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
prev = NULL;
//后半部分逆置
cur = slow;
while (cur)
{
nxt = cur->next;
cur->next = prev;
prev = cur;
cur = nxt;
}
//逐点比对
while (A && prev)
{
if (A->val != prev->val)
return false;
A = A->next;
prev = prev->next;
}
return true;
}
};
/*
此题也可以先把链表中的元素值全部保存到数组中,然后再判断数组是否为回文。不建议使用这种解法,因为如果没有告诉链表最大长度,则不能同此解法
*/
6. 相交链表
https://leetcode.cn/problems/intersection-of-two-linked-lists/description/
/*
解题思路:
此题可以先计算出两个链表的长度,让长的链表先走相差的长度,然后两个链表同时走,直到遇到相同的节点,即为第一个公共节点
*/
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
int lenA = 0, lenB = 0;
struct ListNode* curA = headA, *curB = headB;
//计算链表长度
while(curA) {
++lenA;
curA = curA->next;
}
while(curB) {
++lenB;
curB = curB->next;
}
int gap = abs(lenA-lenB);
struct ListNode* longList = headA, *shortList = headB;
if(lenA < lenB) {
longList = headB;
shortList = headA;
}
//让长链表先走几步
while(gap--){
longList = longList->next;
}
//两个链表同时走,直到遇到相同的节点
while(longList && shortList)
{
if(longList == shortList) {
return longList;
}
else {
longList = longList->next;
shortList = shortList->next;
}
}
return NULL;
}
7. 环形链表I
https://leetcode.cn/problems/linked-list-cycle/description/
/*
解题思路:
定义快慢指针fast,slow, 如果链表确实有环,fast指针一定会在环内追上slow指针。
*/
typedef struct ListNode Node;
bool hasCycle(struct ListNode *head) {
Node* slow = head;
Node* fast = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
return true;
}
return false;
}
8. 环形链表II
https://leetcode.cn/problems/linked-list-cycle-ii/description/
/*
解题思路:
如果链表存在环,则fast和slow会在环内相遇,定义相遇点到入口点的距离为X,定义环的长度为C,定义头到入口的距离为L,fast在slow进入环之后一圈内追上slow,则会得知:
slow所走的步数为:L + X
fast所走的步数为:L + X + N * C
并且fast所走的步数为slow的两倍,故:
2*(L + X) = L + X + N * C
即: L = N * C - X
所以从相遇点开始slow继续走,让一个指针从头开始走,相遇点即为入口节点
*/
typedef struct ListNode Node;
struct ListNode *detectCycle(struct ListNode *head) {
Node* slow = head;
Node* fast = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
//走到相遇点
if(slow == fast)
{
// 求环的入口点
Node* meet = slow;
Node* start = head;
while(meet != start)
{
meet = meet->next;
start = start->next;
}
return meet;
}
}
return NULL;
}
9. 随机链表的复制
https://leetcode.cn/problems/copy-list-with-random-pointer/description/
/*
解题思路:
此题可以分三步进行:
1.拷贝链表的每一个节点,拷贝的节点先链接到被拷贝节点的后面
2.复制随机指针的链接:拷贝节点的随机指针指向被拷贝节点随机指针的下一个位置
3.拆解链表,把拷贝的链表从原链表中拆解出来
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
// 1.拷贝链表,并插入到原节点的后面
Node* cur = head;
while(cur)
{
Node* next = cur->next;
Node* copy = (Node*)malloc(sizeof(Node));
copy->val = cur->val;
// 插入
cur->next = copy;
copy->next = next;
// 迭代往下走
cur = next;
}
// 2.置拷贝节点的random
cur = head;
while(cur)
{
Node* copy = cur->next;
if(cur->random != NULL)
copy->random = cur->random->next;
else
copy->random = NULL;
cur = copy->next;
}
// 3.解拷贝节点,链接拷贝节点
Node* copyHead = NULL, *copyTail = NULL;
cur = head;
while(cur)
{
Node* copy = cur->next;
Node* next = copy->next;
// copy解下来尾插
if(copyTail == NULL)
{
copyHead = copyTail = copy;
}
else
{
copyTail->next = copy;
copyTail = copy;
}
cur->next = next;
cur = next;
}
return copyHead;
}
};
四、顺序表与链表的区别
不同点 | 顺序表 | 链表(单链表) |
---|---|---|
存储空间上 | 物理上⼀定连续 | 逻辑上连续,但物理上不⼀定连续 |
随机访问 | ⽀持O(1) | 不⽀持:O(N) |
任意位置插⼊或者删除元素 | 可能需要搬移元素,效率低O(N) | 只需修改指针指向 |
插⼊ | 动态顺序表,空间不够时需要扩容和空间浪费 | 没有容量的概念,按需申请释放,不存在空间浪费 |
应⽤场景 | 元素⾼效存储+频繁访问 | 任意位置⾼效插⼊和删除 |
总结
到这里相信大家对单链表有一个深入的了解了,最后的总结单链表实现的源码奉上需要的小伙伴们自取哦~
//SList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLTDataType;
typedef struct SLTNode
{
SLTDataType date;
struct SLTNode* next;
}SLTNode;
//链表的打印
void SLTPrint(SLTNode* phead);
//申请新节点
SLTNode* SLTBuyNode(SLTDataType);
//链表尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x);
//链表头插
void SLTPushFront(SLTNode** pphead, SLTDataType x);
//链表尾删
void SLTPopBack(SLTNode** pphead);
//链表头删
void SLTPopFront(SLTNode** pphead);
//链表查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
//指定位置之前插入数据
void SLTInsert(SLTNode** pphead,SLTNode* pos, SLTDataType x);
//指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
//删除指定位置的节点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//删除指定位置之后的节点
void SLTEraseAfter(SLTNode* pos);
//销毁链表
void SLTDestroy(SLTNode** pphead);
//SList.c
#include"SList.h"
//打印链表
void SLTPrint(SLTNode* phead)
{
SLTNode* pcur;
pcur = phead;
while (pcur)
{
printf("%d -> ", pcur->date);
pcur = pcur->next;
}
printf("NULL\n");
}
//申请新节点
SLTNode* SLTBuyNode(SLTDataType x)
{
SLTNode* node = (SLTNode* )malloc(sizeof(SLTNode));
if (node == NULL)
{
perror("malloc file!");
exit(1);
}
node->date = x;
node->next = NULL;
return node;
}
//链表尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
SLTNode* newnode = SLTBuyNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
SLTNode* pcur = *pphead;
while (pcur->next)
{
pcur = pcur->next;
}
pcur->next = newnode;
}
}
//链表头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
SLTNode* newnode = SLTBuyNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
//链表尾删
void SLTPopBack(SLTNode** pphead)
{
assert(pphead && *pphead);
//处理只有一个节点的情况
if ((*pphead)->next==NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* ptail = *pphead;
SLTNode* prev = NULL;
while (ptail->next)
{
prev = ptail;
ptail = ptail->next;
}
prev->next = NULL;
free(ptail);
ptail = NULL;
}
}
//链表头删
void SLTPopFront(SLTNode** pphead)
{
assert(pphead&& *pphead);
SLTNode* next = (*pphead)->next;
free(*pphead);
*pphead = next;
}
//链表查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
assert(phead);
SLTNode* pcur = phead;
while (pcur)
{
if (pcur->date == x)
{
return pcur;
}
else
{
pcur = pcur->next;
}
}
//没有找到
return NULL;
}
//指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
assert(pphead);
assert(pos);
if (pos == *pphead)
{
SLTPushFront(pphead, x);
}
else
{
SLTNode* newnode = SLTBuyNode(x);
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
newnode->next = pos;
prev->next = newnode;
}
}
//指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
assert(pos);
SLTNode* newnode = SLTBuyNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
//删除指定位置的节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead && *pphead && pos);
if (pos == *pphead)
{
SLTPopFront(pphead);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
pos = NULL;
}
}
//删除指定位置之后的节点
void SLTEraseAfter(SLTNode* pos)
{
assert(pos&&pos->next);
SLTNode* del = pos->next;
pos->next = pos->next->next;
free(del);
del = NULL;
}
//销毁链表
void SLTDestroy(SLTNode** pphead)
{
assert(pphead && *pphead);
SLTNode* pcur = *pphead;
while (pcur)
{
SLTNode* next = pcur->next;
free(pcur);
pcur = next;
}
*pphead = NULL;
}
//测试用例
#include"SList.h"
void test01()
{
SLTNode* n1 = NULL;
SLTPushBack(&n1, 1);
SLTPrint(n1);
SLTPushBack(&n1, 2);
SLTPrint(n1);
SLTPushBack(&n1, 3);
SLTPrint(n1);
SLTPushFront(&n1, 100);
SLTPushFront(&n1, 101);
SLTPushFront(&n1, 102);
SLTPrint(n1);
SLTPopBack(&n1);
SLTPrint(n1);
SLTPopBack(&n1);
SLTPrint(n1);
SLTPopFront(&n1);
SLTPopFront(&n1);
SLTPrint(n1);
SLTNode* findnode =SLTFind(n1, 100);
if (findnode == NULL)
{
printf("没找到\n");
}
else
{
printf("找到了%d\n", findnode->date);
}
SLTNode* findnode2 = SLTFind(n1, 10);
if (findnode2 == NULL)
{
printf("没找到\n");
}
else
{
printf("找到了%d\n", findnode2->date);
}
SLTInsert(&n1,findnode , 50);
SLTInsert(&n1, findnode, 20);
SLTPrint(n1);
SLTInsertAfter(findnode, 1314);
SLTInsertAfter(findnode, 1314);
SLTPrint(n1);
SLTErase(&n1, findnode);
SLTPrint(n1);
findnode = SLTFind(n1, 1314);
SLTEraseAfter(findnode);
SLTPrint(n1);
SLTDestroy(&n1);
}
int main()
{
test01();
return 0;
}
真相永远只有一个!