数据结构:单链表(Singly Linked List篇)手把手带你入门数据结构~


前言

在这里插入图片描述

学习了顺序表过后,我们再来看一种新的线性表----单链表(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;
}

真相永远只有一个!
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值