数据结构-链表

一.数据结构

1.1 什么叫数据结构

**数据结构:**一组用来保存一种或多种特定关系的数据集合(组织和存储数据)。

程序设计 = 数据结构 + 算法

1.2 数据与数据之间的关系

1.2.1 逻辑结构:

数据元素与元素之间的关系

1、集合:平等关系

2、线性结构:元素与元素之间一对一的关系(顺序表,链表,队列、栈)

3、树形结构:元素与元素之间一对多的关系(二叉树)

4、图形结构:元素与元素之间多对多的关系(图)

1.2.2 物理结构:

数据元素在计算机内存中的存储方式

1、顺序结构:选用一段连续的内存空间

比如:数组

  1. 数据访问方便(O(1))

  2. 元素插入和删除需要移动大量数据,效率低

  3. 预分配内存空间

2、链式结构:选用非连续的内存空间

比如:链表

  1. 数据访问需要遍历(O(n))

  2. 插入和删除元素方便

  3. 不需要预分配,可以动态存储

  4. 可以有效利用内存碎片(内存碎片:一些游离的小的内存空间)

3、散列结构(哈希结构):将要存储的数据的关键字存储位置之间构建映射关系(哈希函数)存储和查找都根据映射关系查找,为了提高数据的查找效率。

4、索引存储:通关索引表寻找数据的存储位置,为了提高数据的查找效率。

二. 单向链表

2.1 单向链表结构

2.2 单向链表实现

cpp 复制代码
//链表节点结构体
typedef struct linknode{
    DATATYPE_T data;//数据域
    struct linknode *pnext;//指针域
}Node_t;

//链表对象结构体
typedef struct link{
    Node_t *phead;//存储头节点
    int clen;//链表节点个数
}Link_t;

这里Node_t 与 Link_t 中 _t 的含义:_ttype 的缩写,意思是:这是一个通过 typedef 定义的

【类型别名】,不是原生类型。

2.3 创建链表对象

cpp 复制代码
//创建链表对象
Link_t *creat_link()
{
    Link_t *plink = malloc(sizeof(Link_t));
    if(plink == NULL)
    {
        printf("malloc error!\n");
        return NULL;
    }
    plink->phead = NULL;
    plink->clen = 0;
    return plink;
}

2.4 创建链表新节点

cpp 复制代码
//创建链表新节点
Node_t *creat_node(DATATYPE_T x)
{
    Node_t *pnode = malloc(sizeof(Node_t));
    if(pnode == NULL)
    {
        printf("malloc error!\n");
        return NULL;
    }
    pnode->data = x;
    pnode->pnext = NULL;
    return pnode;
}

2.5 链表判空

cpp 复制代码
bool is_empty_link(Link_t *plink)
{
    return plink->clen == 0;
}

2.6 链表头插

cpp 复制代码
//链表头插
int insert_link_head(Link_t *plink, DATATYPE_T data)
{

    Node_t *pnewnode = creat_node(data);
    if(pnewnode == NULL)
        return -1;

    pnewnode->pnext = plink->phead;
    plink->phead = pnewnode;
    plink->clen++;
    return 0;
}

2.7 链表头删

保存原头节点

cpp 复制代码
//链表头删
int delete_link_head(Link_t *plink)
{
    if(is_empty_link(plink))
        return 0;
    else
    {
        Node_t *pfree = plink->phead;//保存原头节点
        plink->phead = pfree->pnext;
        plink->clen--;
        free(pfree);
        pfree = NULL;
        return 0;
    }
}

2.8 链表尾插

注意:这里找尾节点是对ptail->pnext 进行循环判断

cpp 复制代码
//链表尾插
int insert_link_tail(Link_t *plink, DATATYPE_T data)
{
    Node_t *pnewnode = creat_node(data);//创建新节点
    if(pnewnode == NULL)
        return -1;
    Node_t *ptail = plink->phead;
    if(is_empty_link(plink))//表示链表为空
    {
        plink->phead = pnewnode;
        plink->clen++;
        return 0;
    }
    while(ptail->pnext)//开始找尾节点
    {
        ptail = ptail->pnext;
    }
    ptail->pnext = pnewnode;//连接新节点
    plink->clen++;
    return 0;
}

2.9 链表尾删

注意:链表尾删要分三种情况:空链表,只有一个节点,一个以上节点。

cpp 复制代码
//链表尾删
int delete_link_tail(Link_t *plink)
{
    if(is_empty_link(plink))//链表为空
        return 0;
    else if(plink->phead->pnext == NULL)//链表只有一个节点
    {
        delete_link_head(plink);
        return 0;
    }
    //一个以上节点
    else
    {
        Node_t *ptail = plink->phead, *prev = NULL;
        while(ptail->pnext)
        {
            prev = ptail;
            ptail = ptail->pnext;
        }
        prev->pnext = NULL;
        free(ptail);
        ptail = NULL;
        plink->clen--;
        return 0;
    }
}

2.10 链表打印

cpp 复制代码
//打印链表
void print_link(Link_t *plink)
{
    Node_t *cur = plink->phead;
    while(cur)
    {
        printf("%d->",cur->data);
        cur = cur->pnext;
    }
    printf("NULL\n");
}

2.11 链表销毁

cpp 复制代码
//销毁链表
void destroy_link(Link_t *plink)
{
    while(!is_empty_link(plink))
    {
        delete_link_head(plink);
    }
    free(plink);
}

三. valgrind工具

3.1 简介

Valgrind 是 Linux GNU提供的一个内存错误检查软件 ,核心用途是内存调试内存泄漏检测线程错误检测性能分析。在程序运行过程中检查内存错误。

3.2 安装方法

使用命令:

bash 复制代码
sudo apt-get install valgrind

3.3 使用方法

++valgrind ./可执行程序++

执行结果:

cpp 复制代码
==3101== 
==3101== HEAP SUMMARY:  //堆内存使用总结
==3101==     in use at exit: 96 bytes in 6 blocks //程序退出时还有96字节,6块内存没释放
==3101==   total heap usage: 7 allocs, 1 frees, 1,120 bytes allocated 
                            //总共7次malloc,但是只释放了1次,1120字节被申请 
==3101== 
==3101== LEAK SUMMARY:  //泄漏总结
==3101==    definitely lost: 16 bytes in 1 blocks //明确泄漏
==3101==    indirectly lost: 80 bytes in 5 blocks //间接泄漏
==3101==      possibly lost: 0 bytes in 0 blocks //可疑泄漏
==3101==    still reachable: 0 bytes in 0 blocks //可回收但没回收
==3101==         suppressed: 0 bytes in 0 blocks //系统忽略的内存
==3101== Rerun with --leak-check=full to see details of leaked memory
==3101== 
==3101== For counts of detected and suppressed errors, rerun with: -v
==3101== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
                        //崩溃、越界、野指针的情况
相关推荐
CSharp精选营4 天前
关系型 vs 非关系型:从原理到选型,一文搞定数据库核心分类
数据结构·nosql·关系型数据库·非关系型数据库·技术选型
刘马想放假7 天前
Modbus 全栈技术解析:TCP、RTU、ASCII、RTU over TCP
数据结构·网络协议
北域码匠8 天前
冒泡排序太慢?鸡尾酒排序双向优化,原生 C# 零第三方库完整代码
数据结构·排序算法·泛型·c# 算法·鸡尾酒排序·原生 c# 开发·冒泡排序优化·嵌入式算法
Darling噜啦啦15 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
小小工匠16 天前
Redis - 事务机制:能实现 ACID 属性吗
数据结构·redis·性能优化·并发·持久化
玖玥拾16 天前
C/C++ 数据结构(七)栈、容器适配器
c语言·数据结构·c++··容器适配器
Qres82116 天前
算法复键——树状数组
数据结构·算法
牛油果子哥q16 天前
并查集(DSU)超精讲,路径压缩、按秩合并、万能模板、连通性判定、最小生成树与刷题实战全解
数据结构·c++·最小生成树·并查集
凌波粒16 天前
LeetCode--491.递增子序列(回溯算法)
数据结构·算法·leetcode
疯狂成瘾者16 天前
Java 集合 LinkedList 详解:链表结构、常用方法和队列使用
java·开发语言·链表