0
点赞
收藏
分享

微信扫一扫

图解无头单向非循环链表【数据结构】

洒在心头的阳光 2022-03-14 阅读 126

0.前言🐑

🌵🌵
大家好啊,2天不见,甚是想念,呜呜网课要结束了,今天就要开始线下上课了,ε=(´ο`*)))唉,美好生活不复返了。话不多说,今天开始回顾链表中的无头单向非循环链表。
在这里插入图片描述

🌵🌵
在这里插入图片描述

本节重点:

  1. 链表&顺序表对比
  2. 单链表各个接口的实现

1.链表 🐱

关于顺序表的不足:

链表优点

链表缺点

2.单链表🐶

image-20220308205422839

struct SListNode
{
	int data;
	struct SListNode* next;
};
void Test()
{
	struct SListNode* node1 = (struct SListNode*)malloc(sizeof(struct SListNode));
	struct SListNode* node2 = (struct SListNode*)malloc(sizeof(struct SListNode));
	struct SListNode* node3 = (struct SListNode*)malloc(sizeof(struct SListNode));
	struct SListNode* node4 = (struct SListNode*)malloc(sizeof(struct SListNode));

	printf("%p\n", node1);
	printf("%p\n", node2);
	printf("%p\n", node3);
	printf("%p\n", node4);
}
//node1 等其实只是结点地址

image-20220206204249510

逻辑结构&物理结构

image-20220312152236359

链表组合

虽然有这么的组合,但实际中最常用的只有2种:
image-20220312153039673

3.单链表实现🐦

定义

typedef int SListDataType;
typedef struct SListNode // Single Link List
{
    SListDataType data;
    struct SListNode *next; //存储下一个节点的地址
} SListNode;

创建节点

SListNode *CreateNewNode(SListDataType x)
{
    SListNode *newNode = (SListNode *)malloc(sizeof(SListNode));
    if (newNode == NULL)
    {
        printf("malloc newNode fail\n");
        exit(-1);
    }
    else
    {
        newNode->data = x;
        newNode->next = NULL;
    }
    return newNode;
}

打印

void SListPrint(SListNode* pList)
{
    //不需要assert(plist)
	SListNode* cur = pList;
	while (cur != NULL)
    {
		printf("%d->", cur->data); 
		cur = cur->next;//cur->next里面存的就是下一个结点的地址
	}
	printf("NULL\n");
}

查找

//单链表查找
SListNode *SListFind(SListNode *plist, SListDataType x)
{
    SListNode *cur = plist;
    // while(cur != NULL)
    while (cur)
    {
        if (cur->data == x)
        {
            return cur;
            //查找兼具修改的作用
        }
        cur = cur->next;
    }
    return NULL;
}

效果展示:

尾删

void SListPopBack(SListNode** ppList)
{
    //1.没有节点,无法删除,直接return
    if(*ppList == NULL)
    {
        return;
    }
    //2.单个节点
    else if((*ppList)->next == NULL)
    {
        free(*ppList);
        *ppList = NULL;
    }
    //3.多个节点
    else
    {
        SListNode* prev = NULL;
        SListNode* tail = *ppList;
        while (tail->next != NULL)
        {
            prev = tail;
            tail = tail->next;
        }
        free(tail);
        tail = NULL;
        prev->next = NULL;//尾删时要将最后一个结点的上一个结点的next置为NULL才行
    }
}

效果展示:

传引用:

void SListPopBack(SListNode*& pList)
{
    //1.没有节点,无法删除,直接return
    if(pList == NULL)
    {
        return;
    }
    //2.单个节点
    else if((pList)->next == NULL)
    {
        free(pList);
        pList = NULL;
    }
    //3.多个节点
    else
    {
        SListNode* prev = NULL;
        SListNode* tail = pList;
        while (tail->next != NULL)
        {
            prev = tail;
            tail = tail->next;
        }
        free(tail);
        tail = NULL;
        prev->next = NULL;//尾删时要将最后一个结点的上一个结点的next置为NULL才行
    }
}

效果展示:

头删

void SListPopFront(SListNode **ppList)
{
    // 1.没有节点
    if (*ppList == NULL)
    {
        return;
    }
    // 2.单个节点
    // 3.多个节点
    //先写多个节点的情况,再去比较单个节点能否适用,发现恰好可以匹配。
    //保存plist->next,如果直接free plist就找不到后面的空间了
    SListNode *next = (*ppList)->next;
    free(*ppList);//这里只是释放了*ppList指向的那块空间内容,但*ppList还是指向那块空间的。
    *ppList = next;
}

效果展示:

传引用:

void SListPopFront(SListNode *&pList)
{
    // 1.没有节点
    if (pList == NULL)
    {
        return;
    }
    // 2.单个节点
    // 3.多个节点
    //先写多个节点的情况,再去比较单个节点能否适用,发现恰好可以匹配。
    //保存plist->next,如果直接free plist就找不到后面的空间了
    SListNode *next = pList->next;
    free(pList); //这里只是释放了*ppList指向的那块空间内容,但*ppList还是指向那块空间的。
    pList = next;
}

效果展示:

头插

image-20220312171831564

void SListPushFront(SListNode *&pList, SListDataType x)
{
    //即使传进来的是NULL也能解决
    SListNode *newNode = CreateNewNode(x);
    newNode->next = pList; //pList指向的就是第一个节点,其实存的也就是第一个节点的地址
    pList = newNode;
}

image-20220312172747038

传引用的写法:

void SListPushFront(SListNode *&plist, SListDataType x)
{
    //即使传进来的是NULL也能解决
    SListNode *newNode = CreateNewNode(x);
    newNode->next = plist; //*pplist 其实就是 plist
    plist = newNode;
}

image-20220312175413772

尾插

void SListPushBack(SListNode **ppList, SListDataType x)
{
    //同样不需要断言空,因为本来就有可能传空链表
    SListNode* newNode = CreateNewNode(x);
    //1.空链表
    if(*ppList == NULL)
    {
        *ppList = newNode;//也就是把newNode的地址覆盖掉sList原来的NULL地址
        //要修改sList必须传址调用
    }
    //2.正常链表,去找尾
    else
    {
        SListNode* tail = *ppList;//不能直接修改plist,plist一改就找不到链表了
        while (tail->next != NULL)
        {
            tail = tail->next;
        }
        //出来时tail->next 指向的是NULL
        tail->next = newNode;
    }
}

如果不想用二级指针,也可以传引用。

void SListPushBack(SListNode *&pList, SListDataType x)
{
    //同样不需要断言空,因为本来就有可能传空链表
    SListNode* newNode = CreateNewNode(x);
    //1.空链表
    if(pList == NULL)
    {
        pList = newNode;//也就是把newNode的地址覆盖掉pList原来的NULL地址
        //传进来空链表,要修改plist必须传址调用
    }
    //2.正常链表,去找尾
    else
    {
        SListNode* tail = pList;//不能直接修改plist,plist一改就找不到链表了
        while (tail->next != NULL)
        {
            tail = tail->next;
        }
        //出来时tail->next 指向的是NULL
        tail->next = newNode;
    }
}

效果展示:

pos后插入

//在pos后面插入
void SListInserAfter(SListNode *pos, SListDataType x)
{
    assert(pos);
    SListNode *newNode = CreateNewNode(x);
    //注意顺序不要反了
    newNode->next = pos->next;
    pos->next = newNode;
}
//或者临时保存 pos->next
//在pos后面插入
void SListInserAfter(SListNode *pos, SListDataType x)
{
    assert(pos);
    SListNode *newNode = CreateNewNode(x);
    SListNode* next = pos->next;//这样就无需关心顺序问题了
    pos->next = newNode;
    newNode->next = next;
}

效果展示:

pos前插入

void SListInserBefore(SListNode** ppList, SListNode* pos, SListDataType x)
{
    assert(pos);
    SListNode* newNode = CreateNewNode(x);
    if(*ppList == pos)//相当于头插
    {
        newNode->next = pos;
        *ppList = newNode;
    }
    else
    {
        SListNode* prev = NULL;
        SListNode* cur = *ppList;
        while (cur != pos)
        {
            prev = cur;
            cur = cur->next;
        }
        prev->next = newNode;
        newNode->next = cur;
    }
}

效果展示:

传引用的写法:

void SListInserBefore(SListNode *&pList, SListNode *pos, SListDataType x)
{
    assert(pos);
    SListNode *newNode = CreateNewNode(x);
    if (pList == pos) //相当于头插
    {
        newNode->next = pos;
        pList = newNode;
    }
    else
    {
        SListNode *prev = NULL;
        SListNode *cur = pList;
        while (cur != pos)
        {
            prev = cur;
            cur = cur->next;
        }
        prev->next = newNode;
        newNode->next = cur;
    }
}

效果展示:

提问:

image-20220207001858221

pos后擦除

void SListEraseAfter(SListNode *pos)
{
    assert(pos);
    //只有一个节点的情况
    if (pos->next == NULL)
    {
        return;
    }
    else
    {
        SListNode *next = pos->next;
        pos->next = next->next;
        free(next);
        next = NULL;
    }
}

pos擦除

void SListEraseCur(SListNode** ppList, SListNode* pos)
{
    //pos指向第一个节点,相当于头删
    if(pos == *ppList)
    {
        SListNode* next = (*ppList)->next;
        free(*ppList);
        *ppList = next;
    }
    else
    {
        SListNode* prev = NULL;
        SListNode* cur = *ppList;
        while (cur != pos)
        {
            prev = cur;
            cur = cur->next;
        }
        //出来时cur指向的pos,prev指向pos前一个
        prev->next = cur->next;
        free(cur);
        cur = NULL;
    }
}

效果展示:

void SListEraseCur(SListNode *&pList, SListNode *pos)
{
    // pos指向第一个节点,相当于头删
    if (pos == pList)
    {
        SListNode *next = pList->next;
        free(pList);
        pList = next;
    }
    else
    {
        SListNode *prev = NULL;
        SListNode *cur = pList;
        while (cur != pos)
        {
            prev = cur;
            cur = cur->next;
        }
        //出来时cur指向的pos,prev指向pos前一个
        prev->next = cur->next;
        free(cur);
        cur = NULL;
    }
}

效果展示:

4.源代码:🐘

SLinkList.h

#pragma once
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>

typedef int SListDataType;
typedef struct SListNode // Single Link List
{
    SListDataType data;
    struct SListNode *next; //存储下一个节点的地址
} SListNode;

void SListPrint(SListNode *pList);
SListNode *CreateNewNode(SListDataType x);
// void SListPushBack(SListNode **ppList, SListDataType x);
void SListPushBack(SListNode *&pList, SListDataType x);
void SListPushFront(SListNode *&pList, SListDataType x);
// void SListPushFront(SListNode** pplist, SListDataType x);

// void SListPopBack(SListNode** ppList);
void SListPopBack(SListNode *&pList);

// void SListPopFront(SListNode **ppList);
void SListPopFront(SListNode *&pList);

SListNode *SListFind(SListNode *plist, SListDataType x);

void SListInserAfter(SListNode *pos, SListDataType x);

// void SListInserBefore(SListNode** ppList, SListNode* pos, SListDataType x);
void SListInserBefore(SListNode *&pList, SListNode *pos, SListDataType x);

void SListEraseAfter(SListNode *pos);

void SListEraseCur(SListNode *&pList, SListNode *pos);
// void SListEraseCur(SListNode** ppList, SListNode* pos);

SLinkList.cpp

#include "SLinkList.h"
void SListPrint(SListNode *pList)
{
    //不需要assert(plist)
    SListNode *cur = pList;
    while (cur != NULL)
    {
        printf("%d->", cur->data);
        cur = cur->next; // cur->next里面存的就是下一个结点的地址
    }
    printf("NULL\n");
}
SListNode *CreateNewNode(SListDataType x)
{
    SListNode *newNode = (SListNode *)malloc(sizeof(SListNode));
    if (newNode == NULL)
    {
        printf("malloc newNode fail\n");
        exit(-1);
    }
    else
    {
        newNode->data = x;
        newNode->next = NULL;
    }
    return newNode;
}
// void SListPushBack(SListNode **ppList, SListDataType x)
// {
//     //同样不需要断言空,因为本来就有可能传空链表
//     SListNode* newNode = CreateNewNode(x);
//     //1.空链表
//     if(*ppList == NULL)
//     {
//         *ppList = newNode;//也就是把newNode的地址覆盖掉pList原来的NULL地址
//         //传进来空链表,要修改plist必须传址调用
//     }
//     //2.正常链表,去找尾
//     else
//     {
//         SListNode* tail = *ppList;//不能直接修改plist,plist一改就找不到链表了
//         while (tail->next != NULL)
//         {
//             tail = tail->next;
//         }
//         //出来时tail->next 指向的是NULL
//         tail->next = newNode;
//     }
// }

void SListPushBack(SListNode *&pList, SListDataType x)
{
    //同样不需要断言空,因为本来就有可能传空链表
    SListNode *newNode = CreateNewNode(x);
    // 1.空链表
    if (pList == NULL)
    {
        pList = newNode; //也就是把newNode的地址覆盖掉pList原来的NULL地址
        //传进来空链表,要修改plist必须传址调用
    }
    // 2.正常链表,去找尾
    else
    {
        SListNode *tail = pList; //不能直接修改plist,plist一改就找不到链表了
        while (tail->next != NULL)
        {
            tail = tail->next;
        }
        //出来时tail->next 指向的是NULL
        tail->next = newNode;
    }
}

void SListPushFront(SListNode *&pList, SListDataType x)
{
    //即使传进来的是NULL也能解决
    SListNode *newNode = CreateNewNode(x);
    newNode->next = pList; // pList指向的就是第一个节点,其实存的也就是第一个节点的地址
    pList = newNode;
}

// void SListPopBack(SListNode** ppList)
// {
//     //1.没有节点,无法删除,直接return
//     if(*ppList == NULL)
//     {
//         return;
//     }
//     //2.单个节点
//     else if((*ppList)->next == NULL)
//     {
//         free(*ppList);
//         *ppList = NULL;
//     }
//     //3.多个节点
//     else
//     {
//         SListNode* prev = NULL;
//         SListNode* tail = *ppList;
//         while (tail->next != NULL)
//         {
//             prev = tail;
//             tail = tail->next;
//         }
//         free(tail);
//         tail = NULL;
//         prev->next = NULL;//尾删时要将最后一个结点的上一个结点的next置为NULL才行
//     }
// }

void SListPopBack(SListNode *&pList)
{
    // 1.没有节点,无法删除,直接return
    if (pList == NULL)
    {
        return;
    }
    // 2.单个节点
    else if ((pList)->next == NULL)
    {
        free(pList);
        pList = NULL;
    }
    // 3.多个节点
    else
    {
        SListNode *prev = NULL;
        SListNode *tail = pList;
        while (tail->next != NULL)
        {
            prev = tail;
            tail = tail->next;
        }
        free(tail);
        tail = NULL;
        prev->next = NULL; //尾删时要将最后一个结点的上一个结点的next置为NULL才行
    }
}
// void SListPopFront(SListNode **ppList)
// {
//     // 1.没有节点
//     if (*ppList == NULL)
//     {
//         return;
//     }
//     // 2.单个节点
//     // 3.多个节点
//     //先写多个节点的情况,再去比较单个节点能否适用,发现恰好可以匹配。
//     //保存plist->next,如果直接free plist就找不到后面的空间了
//     SListNode *next = (*ppList)->next;
//     free(*ppList);//这里只是释放了*ppList指向的那块空间内容,但*ppList还是指向那块空间的。
//     *ppList = next;
// }

void SListPopFront(SListNode *&pList)
{
    // 1.没有节点
    if (pList == NULL)
    {
        return;
    }
    // 2.单个节点
    // 3.多个节点
    //先写多个节点的情况,再去比较单个节点能否适用,发现恰好可以匹配。
    //保存plist->next,如果直接free plist就找不到后面的空间了
    SListNode *next = pList->next;
    free(pList); //这里只是释放了*ppList指向的那块空间内容,但*ppList还是指向那块空间的。
    pList = next;
}

//单链表查找
SListNode *SListFind(SListNode *plist, SListDataType x)
{
    SListNode *cur = plist;
    // while(cur != NULL)
    while (cur)
    {
        if (cur->data == x)
        {
            return cur;
            //查找兼具修改的作用
        }
        cur = cur->next;
    }
    return NULL;
}

// //在pos后面插入
// void SListInserAfter(SListNode *pos, SListDataType x)
// {
//     assert(pos);
//     SListNode *newNode = CreateNewNode(x);
//     //注意顺序不要反了
//     newNode->next = pos->next;
//     pos->next = newNode;
// }

//或者临时保存 pos->next
//在pos后面插入
void SListInserAfter(SListNode *pos, SListDataType x)
{
    assert(pos);
    SListNode *newNode = CreateNewNode(x);
    SListNode *next = pos->next; //这样就无需关心顺序问题了
    pos->next = newNode;
    newNode->next = next;
}

// void SListInserBefore(SListNode** ppList, SListNode* pos, SListDataType x)
// {
//     assert(pos);
//     SListNode* newNode = CreateNewNode(x);
//     if(*ppList == pos)//相当于头插
//     {
//         newNode->next = pos;
//         *ppList = newNode;
//     }
//     else
//     {
//         SListNode* prev = NULL;
//         SListNode* cur = *ppList;
//         while (cur != pos)
//         {
//             prev = cur;
//             cur = cur->next;
//         }
//         prev->next = newNode;
//         newNode->next = cur;
//     }
// }

void SListInserBefore(SListNode *&pList, SListNode *pos, SListDataType x)
{
    assert(pos);
    SListNode *newNode = CreateNewNode(x);
    if (pList == pos) //相当于头插
    {
        newNode->next = pos;
        pList = newNode;
    }
    else
    {
        SListNode *prev = NULL;
        SListNode *cur = pList;
        while (cur != pos)
        {
            prev = cur;
            cur = cur->next;
        }
        prev->next = newNode;
        newNode->next = cur;
    }
}

void SListEraseAfter(SListNode *pos)
{
    assert(pos);
    //只有一个节点的情况
    if (pos->next == NULL)
    {
        return;
    }
    else
    {
        SListNode *next = pos->next;
        pos->next = next->next;
        free(next);
        next = NULL;
    }
}

// void SListEraseCur(SListNode** ppList, SListNode* pos)
// {
//     //pos指向第一个节点,相当于头删
//     if(pos == *ppList)
//     {
//         SListNode* next = (*ppList)->next;
//         free(*ppList);
//         *ppList = next;
//     }
//     else
//     {
//         SListNode* prev = NULL;
//         SListNode* cur = *ppList;
//         while (cur != pos)
//         {
//             prev = cur;
//             cur = cur->next;
//         }
//         //出来时cur指向的pos,prev指向pos前一个
//         prev->next = cur->next;
//         free(cur);
//         cur = NULL;
//     }
// }

void SListEraseCur(SListNode *&pList, SListNode *pos)
{
    // pos指向第一个节点,相当于头删
    if (pos == pList)
    {
        SListNode *next = pList->next;
        free(pList);
        pList = next;
    }
    else
    {
        SListNode *prev = NULL;
        SListNode *cur = pList;
        while (cur != pos)
        {
            prev = cur;
            cur = cur->next;
        }
        //出来时cur指向的pos,prev指向pos前一个
        prev->next = cur->next;
        free(cur);
        cur = NULL;
    }
}

Test.cpp

#include "SLinkList.h"
void Test1()
{
    SListNode *sList = NULL;
    SListPrint(sList); // NULL
}
void Test2()
{
    SListNode *sList = NULL; //空链表
    // SListPushBack(&sList, 1);
    // SListPushBack(&sList, 2);
    // SListPushBack(&sList, 3);
    SListPushBack(sList, 1);
    SListPushBack(sList, 2);
    SListPushBack(sList, 3);
    SListPrint(sList);

    // SListPushFront(&sList, 2);
    // SListPushFront(&sList, 3);
    SListPushFront(sList, 1);
    SListPushFront(sList, 2);
    SListPushFront(sList, 3);
    SListPrint(sList);
}
void Test3()
{
    SListNode *sList = NULL; //空链表
    SListPushBack(sList, 1);
    SListPushBack(sList, 2);
    SListPushBack(sList, 3);
    // SListPrint(sList);
    // SListPopBack(&sList);
    // SListPrint(sList);
    SListPrint(sList);
    SListPopBack(sList);
    SListPrint(sList);
    // SListPopFront(&sList);
    SListPopFront(sList);
    SListPrint(sList);
}
void Test4()
{
    SListNode *sList = NULL; //空链表
    SListPushBack(sList, 1);
    SListPushBack(sList, 2);
    SListPushBack(sList, 3);
    SListPrint(sList);
    SListPopFront(sList);
    SListPrint(sList);
    SListPopFront(sList);
    SListPrint(sList);
    SListPopFront(sList);
    SListPrint(sList);
    SListPopFront(sList);
    SListPrint(sList);
}
void Test5()
{
    SListNode *sList = NULL; //空链表
    SListPushBack(sList, 1);
    SListPushBack(sList, 2);
    SListPushBack(sList, 3);
    SListPrint(sList);
    SListNode *pos = SListFind(sList, 3);
    if (pos)
    {
        pos->data = 30;
        printf("找到了并修改为30\n");
    }
    else
    {
        printf("找不到\n");
    }
    SListPrint(sList);
}
void Test6()
{
    SListNode *sList = NULL; //空链表
    SListPushBack(sList, 1);
    SListPushBack(sList, 2);
    SListPushBack(sList, 3);
    SListPrint(sList);
    SListNode *pos = SListFind(sList, 2);
    SListInserAfter(pos, 10);
    SListPrint(sList);

    // SListInserBefore(&sList, pos, 20);
    // SListPrint(sList);
    // SListNode *pos2 = SListFind(sList, 1);
    // SListInserBefore(&sList, pos2, 100);
    // SListPrint(sList);

    SListInserBefore(sList, pos, 20);
    SListPrint(sList);
    SListNode *pos2 = SListFind(sList, 1);
    SListInserBefore(sList, pos2, 1000);
    SListPrint(sList);

    SListNode *pos3 = SListFind(sList, 1000);
    SListEraseAfter(pos3);
    SListPrint(sList);
}

void Test7()
{
    SListNode *sList = NULL; //空链表
    SListPushBack(sList, 1);
    SListPushBack(sList, 2);
    SListPushBack(sList, 3);
    SListPrint(sList);
    // SListNode *pos1 = SListFind(sList, 2);
    // SListEraseCur(&sList, pos1);
    // SListPrint(sList);
    // SListNode *pos2 = SListFind(sList, 1);
    // SListEraseCur(&sList, pos2);
    // SListPrint(sList);
    SListNode *pos1 = SListFind(sList, 2);
    SListEraseCur(sList, pos1);
    SListPrint(sList);
    SListNode *pos2 = SListFind(sList, 3);
    SListEraseCur(sList, pos2);
    SListPrint(sList);
}
int main(int argc, char const *argv[])
{
    Test7();
    system("pause");
    return 0;
}

5.尾声🐜

🌵🌵
今天的单链表就回顾到这里啦。
写文不易,如果有帮助烦请点个赞~ 👍👍👍

🌹🌹Thanks♪(・ω・)ノ🌹🌹

👀👀由于笔者水平有限,在今后的博文中难免会出现错误之处,本人非常希望您如果发现错误,恳请留言批评斧正,希望和大家一起学习,一起进步ヽ( ̄ω ̄( ̄ω ̄〃)ゝ,期待您的留言评论。
附GitHub仓库链接
在这里插入图片描述

举报

相关推荐

0 条评论