服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C++数据结构之单链表

C++数据结构之单链表

2022-08-16 10:07sasorit C/C++

这篇文章主要介绍了C++数据结构之单链表,链表是由一个个结点链结成的。结点包括数据域和指针域两部分,数据域用来存储数据元素的信息,指针域用来存储下一个结点的地址,更详细内容请需要的小伙伴参考下面文章内容

简介:

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

C++数据结构之单链表

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

单链表结构的定义

?
1
2
3
4
5
6
7
typedef int SLTDataType;
 
typedef struct SListNode
{
    SLTDataType data;        //数据
    struct SListNode* next;        //指向下一个结点的指针
}SLTNode;

单链表打印

?
1
2
3
4
5
6
7
8
9
10
void SListPrint(SLTNode* phead)
{
    SLTNode* cur = phead;
    while (cur)
    {
        printf("%d -> ", cur->data);
        cur = cur->next;
    }
    printf("NULL\n");
}

C++数据结构之单链表

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

动态申请一个结点

?
1
2
3
4
5
6
7
8
9
10
11
12
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

单链表尾插

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

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

C++数据结构之单链表

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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;
    }
}

C++数据结构之单链表

单链表尾删

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

C++数据结构之单链表

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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指向新结点,新结点就变成了新的头结点。

C++数据结构之单链表

?
1
2
3
4
5
6
7
void SListPushFront(SLTNode** pphead, SLTDataType x)
{
    assert(pphead);
    SLTNode* newnode = BuySListNode(x);
    newnode->next = *pphead;
    *pphead = newnode;
}

单链表头删

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

C++数据结构之单链表

?
1
2
3
4
5
6
7
8
void SListPopFront(SLTNode** pphead)
{
    assert(pphead);
    assert(*pphead);
    SLTNode* next = (*pphead)->next;    //指针next记录头结点的下一个结点
    free(*pphead);
    *pphead = next;
}

求单链表长度

?
1
2
3
4
5
6
7
8
9
10
11
int SListSize(SLTNode* phead)
{
    int size = 0;
    SLTNode* cur = phead;
    while (cur)
    {
        ++size;
        cur = cur->next;
    }
    return size;
}

单链表查找

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

?
1
2
3
4
5
6
7
8
9
10
11
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前一个结点的指针域存放新结点的地址,把它们链结起来。

C++数据结构之单链表

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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结点的指针域存放新结点的地址。

C++数据结构之单链表

?
1
2
3
4
5
6
7
void SListInsertAfter(SLTNode* pos, SLTDataType x)
{
    assert(pos);
    SLTNode* newnode = BuySListNode(x);
    newnode->next = pos->next;
    pos->next = newnode;
}

单链表删除pos位置

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

C++数据结构之单链表

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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的下一个结点。

C++数据结构之单链表

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

判断单链表是否为空

?
1
2
3
4
bool SListEmpty(SLTNode* phead)
{
    return phead == NULL;
}

头文件

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#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);

源文件

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#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++ 单链表内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/weixin_45806959/article/details/119955158

延伸 · 阅读

精彩推荐
  • C/C++区分c++中的声明与定义

    区分c++中的声明与定义

    这篇文章主要介绍了如何区分c++中的声明与定义,帮助大家更好的理解和学习c++,感兴趣的朋友可以了解下...

    Dabelv11892021-09-26
  • C/C++桶排序算法的理解及C语言版代码示例

    桶排序算法的理解及C语言版代码示例

    桶排序算法顾名思义,就是把要排序的元素分桶排序后合并结果,这里我们就来看一下桶排序算法的理解及C语言版代码示例: ...

    Mitchell8912021-04-08
  • C/C++C++函数指针和回调函数使用解析

    C++函数指针和回调函数使用解析

    这篇文章主要为大家详细介绍了C++函数指针和回调函数的使用,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    KiddouXiaoyu9972021-07-02
  • C/C++从汇编看c++中引用与指针的使用分析

    从汇编看c++中引用与指针的使用分析

    在c++中,引用和指针具有相同的作用,都可以用来在函数里面给变函数外面对象或者变量的值,下面就来看他们的原理...

    C++教程网3642020-11-24
  • C/C++C语言单链表贪吃蛇小游戏

    C语言单链表贪吃蛇小游戏

    这篇文章主要为大家详细介绍了C语言单链表贪吃蛇小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    白家名6662021-08-13
  • C/C++详解C++-(=)赋值操作符、智能指针编写

    详解C++-(=)赋值操作符、智能指针编写

    C++的智能指针是克服C++大坑的非常有用的的手段,之所以说它智能,是因为它为程序员克服了重要的编程问题——悬挂指针,下面通过本文给大家分享C++-(...

    LifeYx11722021-06-23
  • C/C++C语言位图算法详解

    C语言位图算法详解

    这篇文章主要介绍了C语言实现的位图算法,主要包括了位图算法的定义与应用,对于C程序算法设计的学习有一定的借鉴价值,需要的朋友可以参考下...

    C语言程序设计5002021-01-31
  • C/C++VC++实现模拟汉诺塔效果

    VC++实现模拟汉诺塔效果

    本文给大家分享的是一则使用vc++实现模拟汉诺塔效果的代码,代码实现起来很简单,主要是汉诺塔算法的思路要正确,正在练习汉诺塔的小伙伴也可以来看...

    C++教程网5022021-02-22