我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C++数据结构之单链表

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C++数据结构之单链表

简介:

线性表的顺序存储结构有一个缺点就是插入和删除时需要移动大量元素,这会耗费许多时间。能不能想办法解决呢?
干脆所有的元素都不要考虑相邻位置了,哪有空位就到哪里,让每一个元素都知道它下一个元素的位置在哪里。
线性表链式存储结构: 用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的)。
链表是由一个个结点链结成的。
结点包括数据域和指针域两部分,数据域用来存储数据元素的信息,指针域用来存储下一个结点的地址。

接下来实现一个无头单向非循环链表。

单链表结构的定义

typedef int SLTDataType;

typedef struct SListNode
{
    SLTDataType data;        //数据
    struct SListNode* next;        //指向下一个结点的指针
}SLTNode;

单链表打印

void SListPrint(SLTNode* phead)
{
    SLTNode* cur = phead;
    while (cur)
    {
        printf("%d -> ", cur->data);
        cur = cur->next;
    }
    printf("NULL\n");
}

将指向链表的指针plist做参数传给函数,遍历一遍链表并输出每个结点数据域的内容。

动态申请一个结点

SLTNode* BuySListNode(SLTDataType x)
{
    SLTNode* node = (SLTNode*)malloc(sizeof(SLTNode));
    if (node == NULL)
    {
        printf("malloc fail");
        exit(-1);
    }
    node->data = x;
    node->next = NULL;
    return node;
}

malloc动态开辟一个结点,如果node为NULL说明开辟失败,退出程序。否则将结点node的数据域赋值为x,指针域赋值为NULL

单链表尾插

如果单链表为空,开辟一个新结点用指针指向它即可;如果链表不为空,需要开辟一个新结点,然后找到链表的最后一个结点,让最后一个结点的指针域存放新结点的地址。

有一个链表,为链表尾插一个新结点:

void SListPushBack(SLTNode** pphead, SLTDataType x)
{
    assert(pphead);        //plist地址一定不为NULL,进行断言
    if (*pphead == NULL)    //链表为空
    {
        SLTNode* newnode = BuySListNode(x);
        *pphead = newnode;
    }
    else           //链表不为空
    {        
        SLTNode* tail = *pphead;
        while (tail->next)        //找到最后一个结点
            tail = tail->next;
        SLTNode* newnode = BuySListNode(x);
        tail->next = newnode;
    }
}

单链表尾删

如果链表只有一个结点,把这个结点free掉即可。如果链表有多个结点,找到链表的尾结点和尾结点的前一个结点,让尾结点的前一个结点的指针域指向NULL,free掉尾结点。

void SListPopBack(SLTNode** pphead)
{
    assert(pphead);        //断言pphead
    assert(*pphead);    //当链表为空时说明没有结点,没法进行删除操作,所以*pphead不能为NULL
    if ((*pphead)->next == NULL)    //只有一个结点
    {
        free(*pphead);
        *pphead = NULL;
    }
    else                //多个结点
    {
        SLTNode* tail = *pphead;    //tail表示为节点
        SLTNode* prev = NULL;        //prev表示尾结点的前一个结点
        while (tail->next)        //找到尾结点和尾结点的前一个结点
        {
            prev = tail;
            tail = tail->next;
        }
        prev->next = NULL;
        free(tail);
        tail = NULL;
    }
}

单链表头插

申请一个新结点,让新结点的指针域存放头结点的地址,原来指向头结点的指针plist指向新结点,新结点就变成了新的头结点。

void SListPushFront(SLTNode** pphead, SLTDataType x)
{
    assert(pphead);
    SLTNode* newnode = BuySListNode(x);
    newnode->next = *pphead;
    *pphead = newnode;
}

单链表头删

用一个指针指向头结点的下一个结点,把头结点的空间释放掉,指向头结点的指针指向头结点的下一个结点。头结点的下一个结点就变成了新的头结点。同时要考虑链表为空时不能删除,进行相应的断言。

void SListPopFront(SLTNode** pphead)
{
    assert(pphead);
    assert(*pphead);
    SLTNode* next = (*pphead)->next;    //指针next记录头结点的下一个结点
    free(*pphead);
    *pphead = next;
}

求单链表长度

int SListSize(SLTNode* phead)
{
    int size = 0;
    SLTNode* cur = phead;
    while (cur)
    {
        ++size;
        cur = cur->next;
    }
    return size;
}

单链表查找

遍历一遍单链表,如果某一结点数据域内容与要查找内容相同,返回该结点。遍历完没有找到,返回NULL

SLTNode* SListFind(SLTNode* phead, SLTDataType x)
{
    SLTNode* cur = phead;
    while (cur)
    {
        if (x == cur->data)
            return cur;
        cur = cur->next;
    }
    return NULL;
}

单链表在pos位置插入

在pos位置插入,如果pos这个位置是头结点,和头插的逻辑是一样的,可以调用之前写过的头插函数。
如果这个位置是除头结点外的任意一个结点,我们需要申请一个新结点,并且记录pos结点的前一个结点,让新结点的指针域存放pos的地址,让pos前一个结点的指针域存放新结点的地址,把它们链结起来。

void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
    assert(pphead);        //指向头结点指针的地址不能为NULL,进行断言
    assert(pos);        //插入位置pos不能为NULL进行断言
    if (pos == *pphead)        //要插入的位置pos和头结点是一个位置
    {
        SListPushFront(pphead, x);
    }
    else                //pos不是头结点
    {
        SLTNode* prev = *pphead;    //prev用来找到pos位置的前一个结点
        while (prev->next != pos)
            prev = prev->next;
        SLTNode* newnode = BuySListNode(x);        //申请一个新结点
        newnode->next = pos;        //把新结点链结
        prev->next = newnode;
    }
}

单链表在pos后面位置插入

申请一个新结点,让新结点的指针域存放pos结点下一个结点的地址,pos结点的指针域存放新结点的地址。

void SListInsertAfter(SLTNode* pos, SLTDataType x)
{
    assert(pos);
    SLTNode* newnode = BuySListNode(x);
    newnode->next = pos->next;
    pos->next = newnode;
}

单链表删除pos位置

如果pos位置是头结点,删除逻辑和头删相同,调用头删函数即可。
如果是除头结点外的其它结点,找到pos的前一个结点,让这个结点的指针域指向pos的下一个结点。把pos结点空间释放。

void SListErase(SLTNode** pphead, SLTNode* pos)
{
    assert(pphead && *pphead);    //pphead不能为空,链表不能为空进行断言
    assert(pos);            //pos不能为空
    if (pos == *pphead)        //要删除的位置pos是头结点
    {
        SListPopFront(pphead);
    }
    else                    //不是头结点
    {
        SLTNode* prev = *pphead;    //prev指针用来找到pos结点的前一个结点
        while (prev->next != pos)
            prev = prev->next;
        prev->next = pos->next;
        free(pos);
        pos = NULL;
    }
}

单链表删除pos的下一个结点

记录下pos的下一个结点,pos结点指针域指向pos下一个结点指针域指向的结点,释放掉pos的下一个结点。

void SListEraseAfter(SLTNode* pos)
{
    //pos不能为空,不能为尾结点,因为尾结点的下一个是NULL,什么也删除不了
    assert(pos && pos->next);    
    SLTNode* next = pos->next;        //next指针指向pos下一个结点
    pos->next = next->next;
    free(next);
    next = NULL;
}

判断单链表是否为空

bool SListEmpty(SLTNode* phead)
{
    return phead == NULL;
}

头文件

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

typedef int SLTDataType;

typedef struct SListNode
{
    SLTDataType data;        //数据
    struct SListNode* next;        //指向下一个结点的指针
}SLTNode;

//打印
void SListPrint(SLTNode* phead);
//新节点
SLTNode* BuySListNode(SLTDataType x);
//尾插
void SListPushBack(SLTNode** pphead, SLTDataType x);
//尾删
void SListPopBack(SLTNode** pphead);
//头插
void SListPushFront(SLTNode** pphead, SLTDataType x);
//头删
void SListPopFront(SLTNode** pphead);
//长度
int SListSize(SLTNode* phead);
//查找
SLTNode* SListFind(SLTNode* phead, SLTDataType x);
//在pos位置插入
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//pos位置后面插入
void SListInsertAfter(SLTNode* pos, SLTDataType x);
//删除pos位置
void SListErase(SLTNode** pphead, SLTNode* pos);
//删除pos后面位置
void SListEraseAfter(SLTNode* pos);
//判空
bool SListEmpty(SLTNode* phead);

源文件

#define _CRT_SECURE_NO_WARNINGS 1
#include "SList.h"

void SListPrint(SLTNode* phead)
{
    SLTNode* cur = phead;
    while (cur)
    {
        printf("%d -> ", cur->data);
        cur = cur->next;
    }
    printf("NULL\n");
}

SLTNode* BuySListNode(SLTDataType x)
{
    SLTNode* node = (SLTNode*)malloc(sizeof(SLTNode));
    if (node == NULL)
    {
        printf("malloc fail");
        exit(-1);
    }
    node->data = x;
    node->next = NULL;
    return node;
}

void SListPushBack(SLTNode** pphead, SLTDataType x)
{
    assert(pphead);        //plist地址一定不为NULL,进行断言
    if (*pphead == NULL)    //链表为空
    {
        SLTNode* newnode = BuySListNode(x);
        *pphead = newnode;
    }
    else           //链表不为空
    {        
        SLTNode* tail = *pphead;
        while (tail->next)
            tail = tail->next;
        SLTNode* newnode = BuySListNode(x);
        tail->next = newnode;
    }
}

void SListPopBack(SLTNode** pphead)
{
    assert(pphead);        //断言pphead
    assert(*pphead);    //当链表为空时说明没有结点,没法进行删除操作,所以*pphead不能为NULL
    if ((*pphead)->next == NULL)    //只有一个结点
    {
        free(*pphead);
        *pphead = NULL;
    }
    else                //多个结点
    {
        SLTNode* tail = *pphead;    //tail表示为节点
        SLTNode* prev = NULL;        //prev表示尾结点的前一个结点
        while (tail->next)        //找到尾结点和尾结点的前一个结点
        {
            prev = tail;
            tail = tail->next;
        }
        prev->next = NULL;
        free(tail);
        tail = NULL;
    }
}

void SListPushFront(SLTNode** pphead, SLTDataType x)
{
    assert(pphead);
    SLTNode* newnode = BuySListNode(x);
    newnode->next = *pphead;
    *pphead = newnode;
}

void SListPopFront(SLTNode** pphead)
{
    assert(pphead);
    assert(*pphead);
    SLTNode* next = (*pphead)->next;
    free(*pphead);
    *pphead = next;
}

int SListSize(SLTNode* phead)
{
    int size = 0;
    SLTNode* cur = phead;
    while (cur)
    {
        ++size;
        cur = cur->next;
    }
    return size;
}

SLTNode* SListFind(SLTNode* phead, SLTDataType x)
{
    SLTNode* cur = phead;
    while (cur)
    {
        if (x == cur->data)
            return cur;
        cur = cur->next;
    }
    return NULL;
}

void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
    assert(pphead);        //指向头结点指针的地址不能为NULL,进行断言
    assert(pos);        //插入位置pos不能为NULL进行断言
    if (pos == *pphead)        //要插入的位置pos和头结点是一个位置
    {
        SListPushFront(pphead, x);
    }
    else                //pos不是头结点
    {
        SLTNode* prev = *pphead;    //prev用来找到pos位置的前一个结点
        while (prev->next != pos)
            prev = prev->next;
        SLTNode* newnode = BuySListNode(x);        //申请一个新结点
        newnode->next = pos;        //把新结点链结
        prev->next = newnode;
    }
}

void SListInsertAfter(SLTNode* pos, SLTDataType x)
{
    assert(pos);
    SLTNode* newnode = BuySListNode(x);
    newnode->next = pos->next;
    pos->next = newnode;
}

void SListErase(SLTNode** pphead, SLTNode* pos)
{
    assert(pphead && *pphead);    //pphead不能为空,链表不能为空进行断言
    assert(pos);            //pos不能为空
    if (pos == *pphead)        //要删除的位置pos是头结点
    {
        SListPopFront(pphead);
    }
    else                    //不是头结点
    {
        SLTNode* prev = *pphead;    //prev指针用来找到pos结点的前一个结点
        while (prev->next != pos)
            prev = prev->next;
        prev->next = pos->next;
        free(pos);
        pos = NULL;
    }
}

void SListEraseAfter(SLTNode* pos)
{
    //pos不能为空,不能为尾结点,因为尾结点的下一个是NULL,什么也删除不了
    assert(pos && pos->next);    
    SLTNode* next = pos->next;        //next指针指向pos下一个结点
    pos->next = next->next;
    free(next);
    next = NULL;
}

bool SListEmpty(SLTNode* phead)
{
    return phead == NULL;
}

到此这篇关于C++数据结构之单链表的文章就介绍到这了,更多相关C++ 单链表内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C++数据结构之单链表

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C++数据结构之单链表如何实现

这篇文章主要介绍了C++数据结构之单链表如何实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C++数据结构之单链表如何实现文章都会有所收获,下面我们一起来看看吧。一、单链表的定义线性表的链式存储又称为单链表,
2023-06-30

C语言数据结构之单链表怎么实现

本文小编为大家详细介绍“C语言数据结构之单链表怎么实现”,内容详细,步骤清晰,细节处理妥当,希望这篇“C语言数据结构之单链表怎么实现”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。一.为什么使用链表在学习链表以前,
2023-07-02

Java数据结构之单链表是什么

这篇文章给大家分享的是有关Java数据结构之单链表是什么的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。一、图示二、链表的概念及结构 链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接
2023-06-15

python数据结构之链表

''''链表的实现,单向链表''''''建立节点'''class jd:    def __init__(self,data):        self.data = data        self.next = None'''实现链表的
2023-01-31

python数据结构之链表(linked

目录基础 知识 1.1 链表的基本结构 1.2 节点类和链表节点的定义 1.3 顺序打印和逆序打印链表的基本操作 2.1 计算链表长度 2.2 从前,后插入数据 2.3 查找与删除参考1.基础 知识1.1 链表的基本结构链表是通过一个个节点
2023-01-31

C++ 数据结构中单链表的示例分析

小编给大家分享一下C++ 数据结构中单链表的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!一、链表是什么链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接依次实现的。由图,链
2023-06-29

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录