C++数据结构之单链表

目录
  • 单链表结构的定义
  • 单链表打印
  • 动态申请一个结点
  • 单链表尾插
  • 单链表尾删
  • 单链表头插
  • 单链表头删
  • 求单链表长度
  • 单链表查找
  • 单链表在pos位置插入
  • 单链表在pos后面位置插入
  • 单链表删除pos位置
  • 单链表删除pos的下一个结点
  • 判断单链表是否为空
  • 头文件
  • 源文件

简介:

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

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

单链表结构的定义

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++ 单链表内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++数据结构之list详解

    目录 前言 一.list的节点 二.list的迭代器 2.1 const 迭代器 2.2 修改方法 二.美中不足 三.迭代器的分类 3.x std::find的一个报错 总结 前言 list相较于vector来说会显得复杂,它的好处是在任意位置插入,删除都是一个O(1)的时间复杂度. 一.list的节点 template <class T> struct __list_node { typedef void* void_pointer; void_pointer next; void_poin

  • C++数据结构的队列详解

    目录 前言 1. 队列的概念及结构 2. 队列的实现 2.1 queue.h 2.2 queue.c 2.3 test.c 总结 前言 hello,大家好,这期文章我们来分享数据结构关于队列的知识.希望对大家有所帮助,闲言少叙,现在开始. 1. 队列的概念及结构 队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头 2. 队列的实现 2.1

  • C++数据结构关于栈迷宫求解示例

    目录 一.实验目的 二.预备知识 三.实验内容 定义一些代码: 定义类 类的成员函数的一些说明: 找迷宫的方法(dfs算法) 主函数(创建对象) 运行的一些截图: 1.当入口和终点一样时: 2.终点是可以到达的路径 3.出口不通的情况 一.实验目的 理解栈的抽象数据类型定义及操作特点.掌握顺序栈的存储结构的描述.掌握顺序栈的基本操作的实现方法.理解栈的广泛应用. 二.预备知识 阅读课程教材P44~45页内容,掌握栈的逻辑定义及"后进先出"的特点,理解抽象数据类型栈的定义.阅读课程教材P

  • C++实现约瑟夫环的循环单链表

    约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知 n 个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围.. 从编号为 k 的人开始报数,数到 m 的那个人出圈:他的下一个人又从 1 开始报数,数到 m 的那个人又出圈:依此规律重复下去,直到剩余最后一个胜利者.. 例如:有10个人围成一圈进行此游戏,每个人编号为 1-10 .. 若规定数到 3 的人出圈.. 则游戏过程如下.. (1)开始报数,第一个数到 3 的人为 3 号,3 号出圈.. 1, 2, [ 3 ], 4, 5, 6, 7

  • C++数据结构链表基本操作示例过程

    目录 首先创建好一个节点 其次创建一个统计节点属性 增加节点 用表头插入的方法插入节点 删除节点 首先创建好一个节点 typedef struct node { int date; struct node* next; }*PNODE; PNODE creatnode(int date ) { PNODE newnode = (PNODE)malloc(sizeof(struct node)); assert(newnode); newnode->next = NULL; newnode->d

  • C++实现数据结构的顺序表详解

    目录 前言: 代码 1.SeqList.h 2.SeqList.cpp 3.test.cpp 总结 前言: hello,大家好,这篇文章博主来分享一下C++实现数据结构中的顺序表的代码.希望对大家有所帮助. 在博主之前的文章中,已经详细地写过顺序表,读者可以点击查看C语言如何建立链表并实现增删查改,在之前的文章中,是用C语言来实现的,这篇文章中,我们用C++来实现. 代码 1.SeqList.h #ifndef SEQLIST_H #define SEQLIST_H #include<iostr

  • C#数据结构之单链表(LinkList)实例详解

    本文实例讲述了C#数据结构之单链表(LinkList)实现方法.分享给大家供大家参考,具体如下: 这里我们来看下"单链表(LinkList)".在上一篇<C#数据结构之顺序表(SeqList)实例详解>的最后,我们指出了:顺序表要求开辟一组连续的内存空间,而且插入/删除元素时,为了保证元素的顺序性,必须对后面的元素进行移动.如果你的应用中需要频繁对元素进行插入/删除,那么开销会很大. 而链表结构正好相反,先来看下结构: 每个元素至少具有二个属性:data和next.data

  • Python数据结构之单链表详解

    本文实例为大家分享了Python数据结构之单链表的具体代码,供大家参考,具体内容如下 # 节点类 class Node(): __slots__=['_item','_next'] # 限定Node实例的属性 def __init__(self,item): self._item = item self._next = None # Node的指针部分默认指向None def getItem(self): return self._item def getNext(self): return s

  • python算法与数据结构之单链表的实现代码

    =一.链表 链表是一种物理存储单元上非连续.非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的.链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成.每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域. 相比于线性表顺序结构,操作复杂.由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是

  • Java数据结构之单链表详解

    一.图示 二.链表的概念及结构 链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 . 实际中链表的结构非常多样,以下情况组合起来就有8种链表结构: 单向.双向 带头.不带头 循环.非循环 今天,我们实现的是一个 单向 无头 非循环的链表. 下面是此链表的结构组成. 三.单链表的实现 (1)定义一个节点类型 我们创建一个 ListNode 的类作为节点类型,那么我们如何定义成员属性呢? 通过上面的结构分析,我们需要定义两个成员变量 val --作为该节点的

  • C++数据结构之单链表

    目录 单链表结构的定义 单链表打印 动态申请一个结点 单链表尾插 单链表尾删 单链表头插 单链表头删 求单链表长度 单链表查找 单链表在pos位置插入 单链表在pos后面位置插入 单链表删除pos位置 单链表删除pos的下一个结点 判断单链表是否为空 头文件 源文件 简介: 线性表的顺序存储结构有一个缺点就是插入和删除时需要移动大量元素,这会耗费许多时间.能不能想办法解决呢?干脆所有的元素都不要考虑相邻位置了,哪有空位就到哪里,让每一个元素都知道它下一个元素的位置在哪里.线性表链式存储结构: 用

  • C++数据结构之单链表的实现

    目录 一.单链表的定义 二.单链表的基本操作的实现 1.初始化 2.取值 3.查找 4.插入 5.删除 三.完整代码 四.测试一下代码 一.单链表的定义 线性表的链式存储又称为单链表,它是指通过一组任意的存储单元来存储线性表中的数据元素.为了建立数据元素之间的线性关系,对每个链表结点,除存放元素自身的信息外,还需要存放一个指向其后继的指针. 单链表中结点类型的描述如下: typedef struct LNode{ // 定义单链表节点类型 ElemType data; // 数据域 struct

  • C语言数据结构之单链表的实现

    目录 一.为什么使用链表 二.链表的概念 三.链表的实现 3.1 创建链表前须知 3.2 定义结构体 3.3 申请一个节点 3.4 链表的头插 3.5 链表的尾插 3.6 链表的尾删 3.7 链表的头删 3.8 寻找某节点 3.9 在指定节点前插入节点 3.10 删除指定节点前的节点 3.11 链表的销毁 一.为什么使用链表 在学习链表以前,我们存储数据用的方式就是数组.使用数组的好处就是便于查找数据,但缺点也很明显. 使用前需声明数组的长度,一旦声明长度就不能更改 插入和删除操作需要移动大量的

  • C语言数据结构之单链表与双链表的增删改查操作实现

    目录 前言 单链表的增删改查 定义结构体以及初始化 增加结点 删除结点 查找修改结点 移除结点 最终效果 双链表的基本操作 初始化建表 遍历双链表 指定位置插入结点 指定位置删除结点 查找结点位置 最终效果 结语 前言 上篇博客分享了创建链表传入二级指针的细节,那么今天就分享几个c语言课程实践设计吧.这些程序设计搞懂了的话相当于链表的基础知识牢牢掌握了,那么再应对复杂的链表类的题也就能慢慢钻研了.学习是一个积累的过程,想要游刃有余就得勤学苦练! 单链表的增删改查 (1)项目需求 构造带有头结点的

  • C语言数据结构之单链表存储详解

    目录 1.定义一个链表结点 2.初始化单链表 3.输出链表数据 4.完整代码 如果说,顺序表的所占用的内存空间是连续的,那么链表则是随机分配的不连续的,那么为了使随机分散的内存空间串联在一起形成一种前后相连的关系,指针则起到了关键性作用. 单链表的基本结构: 头指针:永远指向链表第一个节点的位置. 头结点:不存任何数据的空节点,通常作为链表的第一个节点.对于链表来说,头节点不是必须的,它的作用只是为了方便解决某些实际问题. 首元结点:首个带有元素的结点. 其他结点:链表中其他的节点. 1.定义一

  • C语言数据结构之单链表操作详解

    目录 1.插入操作 2.删除操作 3.查找操作 4.修改操作 5.完整代码 1.插入操作 (1)创建一个新的要插入的结点 (2)将新结点的 next 指针指向插入位置后的结点 (3)将插入位置前的节点指针 next 指向新的结点 注意:步骤(2)(3)的顺序不能颠倒,否则会导致插入位置后的部分链表丢失. 插入位置一共分三种,分别是头部插入.中间插入和尾部插入. 如图: 代码: link* insertElem(link* p,int elem,int pos){ link* temp = p;/

随机推荐