嵌入式学习——数据结构(单向无头链表)——day46

  1. 数据结构

1.1 定义

数据结构是指计算机中数据的组织、管理和存储方式。它不仅包括数据元素的存储方式,还包括数据元素之间的关系,以及对数据进行操作的方法和算法。数据结构的选择和设计直接影响算法的效率和程序的性能,是计算机科学与编程中非常重要的基础

1.2 数据结构的分类

(1)逻辑角度

  1. 线性结构(一对一)数组、链表、队列、栈

  2. 树型结构(一对多)二叉树

  3. 图形结构(多对多)网状结构

(2)存储角度

  1. 顺序存储:采用一段连续的内存空间保存元素(数组)

优点:空间连续,访问方便

缺点:插入删除需要移动大量元素、需要预分配内存空间,容易造成存储空间碎片

  1. 链式存储:采用一组非连续的内存空间保存元素(链表)

优点:插入和删除数据方便、不需要预分配内存

缺点:访问元素效率低

  1. 散列存储(哈希存储)将数据元素的存储位置与关键码之间建立确定对应关系从而实现查找的存储方式

  2. 索引存储:通过关键字构建索引表、通过索引表来找到数据的存储位置

补充:

  1. 重点学习内容

顺序表、链式表、顺序栈、链式栈、顺序队列、链式队列、二叉树、哈希表

  1. 程序 = 数据结构 + 算法

  2. 分析算法效率的两个指标

2.1 时间复杂度

是指算法在执行过程中所需时间的量度。它衡量的是算法的执行时间随输入规模的变化情况,通常用大O记号表示,如O(n)、O(log n)等

2.2 空间复杂度

是指算法在执行过程中所需存储空间的量度。它衡量的是算法的内存使用情况,通常也用大O记号表示。

3.无头单向链表代码

3.1 makefile

cpp 复制代码
OBJ:=seqlist
OBJS+=SeqList.c
CC:=gcc 

$(OBJ):$(OBJS)
	$(CC) $^ -o $@
.PHONY:
clean:
	rm $(OBJ)
test:
	valgrind --tool=memcheck --leak-check=full ./$(OBJ)

3.2 头文件

cpp 复制代码
#ifndef _LINKLIST_H_
#define _LINKLIST_H_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef int DataType;

typedef struct node
{
    DataType Data;
    struct node *pNext;
}LinkNode;

typedef struct list
{
    LinkNode *pHead;
    int cLen;
}LinkList;

extern LinkList *create_LinkList(void);
extern int is_empty_link(LinkList *pTmpList);
extern int push_head_link(LinkList *pTmpList, DataType Data);//头插、空链表也能使用
extern int push_tail_link(LinkList *pTmpList, DataType Data);//尾插、空链表也能使用
extern void link_for_each(LinkList *pTmpList);
extern int pop_head_link(LinkList *pTmpList);
extern int pop_tail_link(LinkList *pTmpList);
extern LinkNode *find_data(LinkList *pTmpList, DataType Data);
extern int modify_data(LinkList *pTmpList, DataType old_data, DataType new_data);
extern void destory_link(LinkList *pTmpList);
extern LinkNode *find_mid_node(LinkList *pTmpList);//找中间节点
extern LinkNode *find_last_k_node(LinkList *pList, int k);//寻找倒数第k个节点
extern int pop_data_link(LinkList *pTmpList, DataType Data);//删除数据为key的节点
extern void invert_link(LinkList *pTmpList);
extern void insert_sort_link(LinkList *pTmpList);

#endif

3.3 主函数

cpp 复制代码
#include "LinkList.h"

LinkList *create_LinkList(void)//创建空链表
{
    LinkList *pList = NULL;

    pList = malloc(sizeof(LinkList));
    if (NULL == pList)
    {
        perror("fail to malloc");
        return NULL;
    }

    pList->pHead = NULL;
    pList->cLen = 0;

    return pList;
}

int is_empty_link(LinkList *pTmpList)//判断链表是否为空链表
{
    if (NULL == pTmpList->pHead)
    {
        return 1;
    }

    return 0;
}

int push_head_link(LinkList *pTmpList, DataType Data)//头插、空链表也能使用
{
    LinkNode *pTmpNode = NULL;

    pTmpNode = malloc(sizeof(LinkNode));
    if (NULL == pTmpNode)
    {
        perror("fail to malloc");
        return -1;
    }

    pTmpNode->pNext = NULL;//插入节点初始化
    pTmpNode->Data = Data;
    
    pTmpNode->pNext = pTmpList->pHead;
    pTmpList->pHead = pTmpNode;
    pTmpList->cLen++;

    return 0;
}

int push_tail_link(LinkList *pTmpList, DataType Data)//尾插、空链表也能使用
{
    LinkNode *pTmpNode = NULL;
    LinkNode *pLastNode = NULL;

    pTmpNode = malloc(sizeof(LinkNode));
    if (NULL == pTmpNode)
    {
        perror("fail to malloc");
        return -1;
    }

    pTmpNode->pNext = NULL;
    pTmpNode->Data = Data;
    
    if (is_empty_link(pTmpList))
    {
        pTmpList->pHead = pTmpNode;
    }
    else 
    {
        pLastNode = pTmpList->pHead;
        while (pLastNode->pNext != NULL)
        {
            pLastNode = pLastNode->pNext;
        }
        pLastNode->pNext = pTmpNode;
    }
    pTmpList->cLen++;

    return 0;
}

void link_for_each(LinkList *pTmpList)//遍历输出链表
{
    LinkNode *pTmpNode = NULL;

    pTmpNode = pTmpList->pHead;
    while (pTmpNode != NULL)
    {
        printf("%d ", pTmpNode->Data);
        pTmpNode = pTmpNode->pNext;
    }
    putchar('\n');
}

int pop_head_link(LinkList *pTmpList)//头删
{
    if (is_empty_link(pTmpList))
    {
        return -1;
    }

    LinkNode *pTmpNode = NULL;

    pTmpNode = pTmpList->pHead;
    pTmpList->pHead = pTmpNode->pNext;
    free(pTmpNode);
    pTmpList->cLen--;
    
    return 0;
}

int pop_tail_link(LinkList *pTmpList)//尾删
{
    if (is_empty_link(pTmpList))
    {
        return -1;
    }
    if (1 == pTmpList->cLen)
    {
        pop_head_link(pTmpList);
    }
    else
    {
        LinkNode *pTmpNode = NULL;

        pTmpNode = pTmpList->pHead;
        while (pTmpNode->pNext->pNext != NULL)
        {
            pTmpNode = pTmpNode->pNext;
        }
        free(pTmpNode->pNext);
        pTmpNode->pNext = NULL;
        pTmpList->cLen--;
    }
    
    return 0;
}

LinkNode *find_data(LinkList *pTmpList, DataType Data)//在链表中寻找数据
{
    LinkNode *pTmpNode = NULL;

    pTmpNode = pTmpList->pHead;

    while (pTmpNode)
    {
        if (Data == pTmpNode->Data)
        {
            return pTmpNode;
        }
        pTmpNode = pTmpNode->pNext;
    }

    return NULL;
}

int modify_data(LinkList *pTmpList, DataType old_data, DataType new_data)//修改数据
{
    LinkNode *pTmpNode = NULL;

    pTmpNode = find_data(pTmpList, old_data);
    if (pTmpNode != NULL)
    {
        pTmpNode->Data = new_data;
        return 0;
    }
    else 
    {
        return -1;
    }
}

void destory_link(LinkList *pTmpList)//销毁链表
{
    while (!is_empty_link(pTmpList))
    {
        pop_head_link(pTmpList);
    }
    free(pTmpList);
}

LinkNode *find_mid_node(LinkList *pTmpList)//找中间节点
{
    LinkNode *pSlowNode = NULL;
    LinkNode *pFastNode = NULL;

    pFastNode = pTmpList->pHead;
    pSlowNode = pFastNode;

    while (pFastNode != NULL)
    {
        pFastNode = pFastNode->pNext;
        if (NULL == pFastNode)
        {
            break;
        }
        pFastNode = pFastNode->pNext;//pFastNode 速度是 pSlowNode的两倍
        pSlowNode = pSlowNode->pNext;
    }

    return pSlowNode;
}

LinkNode *find_last_k_node(LinkList *pTmpList, int k)//寻找倒数第k个节点
{
    LinkNode *pFastNode = NULL;
    LinkNode *pSlowNode = NULL;
    int i = 0;

    pFastNode = pTmpList->pHead;
    pSlowNode = pFastNode;

    for (i = 0; i < k; i++)//快慢指针相差k个位置
    {
        if (NULL == pFastNode)
        {
            return NULL;
        }
        pFastNode = pFastNode->pNext;
    }

    while (pFastNode != NULL)
    {
        pFastNode = pFastNode->pNext;
        pSlowNode = pSlowNode->pNext;
    }

    return pSlowNode;
}

int pop_data_link(LinkList *pTmpList, DataType Data)//删除数据为key的节点
{
    if (is_empty_link(pTmpList))
    {
        return 1;
    }

    LinkNode *pTmpNode = NULL;
    LinkNode *pPreNode = NULL;

    pTmpNode = pTmpList->pHead;
    while (pTmpNode != NULL)
    {
        if (Data == pTmpNode->Data)//找到数据
        {
            if (pTmpNode == pTmpList->pHead)//判断是否只有头结点
            {
                pTmpList->pHead = pTmpNode->pNext;
                free(pTmpNode);
                pTmpNode = pTmpList->pHead;
            }
            else
            {
                pPreNode->pNext = pTmpNode->pNext;
                free(pTmpNode);
                pTmpNode = pPreNode->pNext;
            }
            pTmpList->cLen--;
        }
        else
        {
            pPreNode = pTmpNode;
            pTmpNode = pTmpNode->pNext;
        }
    }
}

void invert_link(LinkList *pTmpList)//链表的倒置
{
    LinkNode *pTmpNode = NULL;
    LinkNode *pInsertNode = NULL;

    pTmpNode = pTmpList->pHead;
    pTmpList->pHead = NULL;
    
    while (pTmpNode != NULL)
    {
        pInsertNode = pTmpNode;
        pTmpNode = pTmpNode->pNext;

        pInsertNode->pNext = pTmpList->pHead;
        pTmpList->pHead = pInsertNode;
    }
}

void  insert_sort_link(LinkList *pTmpList)
{
    if (is_empty_link(pTmpList) || NULL == pTmpList->pHead->pNext)//空链表或者只有一个节点,不用排序
    {
        return ;
    }

    LinkNode *pTmpNode = NULL;//记录剩余节点的起始位置
    LinkNode *pInsertNode = NULL;//插入节点的位置
    LinkNode *p = NULL;//插入排序中遍历已经插入的节点的指针

    pTmpNode = pTmpList->pHead->pNext;//第一个节点不排序,从第二个节点开始排序
    pTmpList->pHead->pNext = NULL;//从原链表的第一个节点之后断开

    while (pTmpNode != NULL)//判断剩余未排序节点是否存在,最后一个节点参与操作
    {
        pInsertNode = pTmpNode;
        pTmpNode = pTmpNode->pNext;

        if (pInsertNode->Data <= pTmpList->pHead->Data)//判断要插入节点数据的大小是否小于第一个节点的数据
        {
            pInsertNode->pNext = pTmpList->pHead;//头插
            pTmpList->pHead = pInsertNode;
        }
        else //往后插
        {
            p = pTmpList->pHead;
            while (p->pNext != NULL && p->pNext->Data < pInsertNode->Data)
            {
                p = p->pNext;
            }

            pInsertNode->pNext = p->pNext;
            p->pNext = pInsertNode;
        }
    }
}

int main(void)
{
    LinkList *pList = NULL;
    LinkNode *pTmpNode = NULL;
    
    pList = create_LinkList();//创建
    if (NULL == pList)
    {
        return -1;
    }
#if 0
    push_head_link(pList, 3);//头插
    push_head_link(pList, 2);
    push_head_link(pList, 1);
    link_for_each(pList);
#endif
    push_tail_link(pList, 1);//尾插
    push_tail_link(pList, 2);
    push_tail_link(pList, 3);
    push_tail_link(pList, 4);
    push_tail_link(pList, 5);
    push_tail_link(pList, 6);
    push_tail_link(pList, 7);
    link_for_each(pList);
#if 0
    pop_head_link(pList);//头删
    link_for_each(pList);

    pop_tail_link(pList);//尾删
    link_for_each(pList);
#endif
    pTmpNode = find_data(pList, 4);//找数据
    if (NULL != pTmpNode)
    {
        printf("find node data = %d\n", pTmpNode->Data);
    }
    else
    {
        printf("not find this node\n");
    }

    modify_data(pList, 2, 20);//修改数据
    modify_data(pList, 3, 30);
    modify_data(pList, 4, 40);
    modify_data(pList, 5, 50);
    link_for_each(pList);

    pTmpNode = find_mid_node(pList);//找链表中间节点
    if (pTmpNode != NULL)
    {
        printf("Mid node data = %d\n", pTmpNode->Data);
    }
    link_for_each(pList);

    pTmpNode = find_last_k_node(pList, 5);//寻找链表倒数第k个节点
    if (pTmpNode != NULL)
    {
        printf("last node data = %d\n", pTmpNode->Data);
    }

    link_for_each(pList);
    pop_data_link(pList, 30);//删除链表数据
    link_for_each(pList);

    invert_link(pList);//链表倒置
    link_for_each(pList);

    insert_sort_link(pList);
    link_for_each(pList);
    

    destory_link(pList);//销毁链表
    
    return 0;
}
  1. 快慢双指针解决链表问题

4.1 寻找中间节点------(后面的指针前进速度是前面指针的一半)

cpp 复制代码
LinkNode *find_mid_node(LinkList *pTmpList)//找中间节点
{
    LinkNode *pSlowNode = NULL;
    LinkNode *pFastNode = NULL;

    pFastNode = pTmpList->pHead;
    pSlowNode = pFastNode;
    
    while (pFastNode != NULL)//要操作pFastNod
    {
        pFastNode = pFastNode->pNext;
        if (NULL == pFastNode)
        {
            break;
        }
        pFastNode = pFastNode->pNext;//pFastNode 速度是 pSlowNode的两倍
        pSlowNode = pSlowNode->pNext;
    }

    return pSlowNode;
}

4.2 寻找倒数第k个节点------(两指针相差k)

cpp 复制代码
LinkNode *find_last_k_node(LinkList *pTmpList, int k)//寻找倒数第k个节点
{
    LinkNode *pFastNode = NULL;
    LinkNode *pSlowNode = NULL;
    int i = 0;

    pFastNode = pTmpList->pHead;
    pSlowNode = pFastNode;

    for (i = 0; i < k; i++)//快慢指针相差k个位置
    {
        if (NULL == pFastNode)//节点数不足k程序直接结束
        {
            return NULL;
        }
        pFastNode = pFastNode->pNext;
    }

    while (pFastNode != NULL)
    {
        pFastNode = pFastNode->pNext;
        pSlowNode = pSlowNode->pNext;
    }

    return pSlowNode;
}

4.3 删除数据为key的节点

cpp 复制代码
int pop_data_link(LinkList *pTmpList, DataType Data)//删除数据为key的节点
{
    if (is_empty_link(pTmpList))
    {
        return -1;
    }

    LinkNode *pTmpNode = NULL;
    LinkNode *pPreNode = NULL;

    pTmpNode = pTmpList->pHead;
    while (pTmpNode != NULL)
    {
        if (Data == pTmpNode->Data)//找到数据
        {
            if (pTmpNode == pTmpList->pHead)//判断是否只有头结点
            {
                pTmpList->pHead = pTmpNode->pNext;
                free(pTmpNode);
                pTmpNode = pTmpList->pHead;
            }
            else
            {
                pPreNode->pNext = pTmpNode->pNext;
                free(pTmpNode);
                pTmpNode = pPreNode->pNext;
            }
            pTmpList->cLen--;
        }
        else
        {
            pPreNode = pTmpNode;
            pTmpNode = pTmpNode->pNext;
        }
    }
}
  1. 单向链表算法

5.1 链表的倒置

cpp 复制代码
void invert_link(LinkList *pTmpList)//链表的倒置
{
    LinkNode *pTmpNode = NULL;
    LinkNode *pInsertNode = NULL;

    pTmpNode = pTmpList->pHead;
    pTmpList->pHead = NULL;//断开链表
    
    while (pTmpNode != NULL)
    {
        pInsertNode = pTmpNode;
        pTmpNode = pTmpNode->pNext;

        pInsertNode->pNext = pTmpList->pHead;
        pTmpList->pHead = pInsertNode;
    }
}

5.2 链表的插入排序(从小到大)

cpp 复制代码
void  insert_sort_link(LinkList *pTmpList)//插入排序法
{
    if (is_empty_link(pTmpList) || NULL == pTmpList->pHead->pNext)//空链表或者只有一个节点,不用排序
    {
        return ;
    }

    LinkNode *pTmpNode = NULL;//记录剩余节点的起始位置
    LinkNode *pInsertNode = NULL;//插入节点的位置
    LinkNode *p = NULL;//插入排序中遍历已经插入的节点的指针

    pTmpNode = pTmpList->pHead->pNext;//第一个节点不排序,从第二个节点开始排序
    pTmpList->pHead->pNext = NULL;//从原链表的第一个节点之后断开

    while (pTmpNode != NULL)//判断剩余未排序节点是否存在,最后一个节点参与操作
    {
        pInsertNode = pTmpNode;
        pTmpNode = pTmpNode->pNext;

        if (pInsertNode->Data <= pTmpList->pHead->Data)//判断要插入节点数据的大小是否小于第一个节点的数据
        {
            pInsertNode->pNext = pTmpList->pHead;//头插
            pTmpList->pHead = pInsertNode;
        }
        else //往后插
        {
            p = pTmpList->pHead;
            while (p->pNext != NULL && p->pNext->Data < pInsertNode->Data)
            {
                p = p->pNext;
            }

            pInsertNode->pNext = p->pNext;
            p->pNext = pInsertNode;
        }
    }
}
相关推荐
爱吃喵的鲤鱼5 分钟前
linux进程的状态之环境变量
linux·运维·服务器·开发语言·c++
懒惰才能让科技进步27 分钟前
从零学习大模型(十二)-----基于梯度的重要性剪枝(Gradient-based Pruning)
人工智能·深度学习·学习·算法·chatgpt·transformer·剪枝
DARLING Zero two♡31 分钟前
关于我、重生到500年前凭借C语言改变世界科技vlog.16——万字详解指针概念及技巧
c语言·开发语言·科技
Gu Gu Study33 分钟前
【用Java学习数据结构系列】泛型上界与通配符上界
java·开发语言
love_and_hope42 分钟前
Pytorch学习--神经网络--搭建小实战(手撕CIFAR 10 model structure)和 Sequential 的使用
人工智能·pytorch·python·深度学习·学习
Chef_Chen1 小时前
从0开始学习机器学习--Day14--如何优化神经网络的代价函数
神经网络·学习·机器学习
芊寻(嵌入式)1 小时前
C转C++学习笔记--基础知识摘录总结
开发语言·c++·笔记·学习
一颗松鼠1 小时前
JavaScript 闭包是什么?简单到看完就理解!
开发语言·前端·javascript·ecmascript
泉崎1 小时前
11.7比赛总结
数据结构·算法
有梦想的咸鱼_1 小时前
go实现并发安全hashtable 拉链法
开发语言·golang·哈希算法