嵌入式——02 数据结构

数据结构:

单链表的增删改查操作、实现思路

bash 复制代码
// 向单向链表的指定位置插入数据
// p保存链表的头指针 post 插入的位置 data插入的数据
int InsertIntoPostLinkList(link_node_t *p, int post, datatype data)
{
    // 0.容错判断
    if (post < 0 || post > LengthLinkList(p))
    {
        perror("InsertIntoPostLinkList err");
        return -1;
    }
    // 1.将头指针指向被插入位置的前一个节点
    for (int i = 0; i < post; i++)
        p = p->next;
    // 2.创建一个新节点,存放数据
    link_list_t pnew = (link_list_t)malloc(sizeof(link_node_t));
    if (pnew == NULL)
    {
        perror("pnew malloc err");
        return -1;
    }
    pnew->data = data;
    pnew->next = NULL;
    // 3.将新节点插入链表中(先连后面,再连前面)
    pnew->next = p->next;
    p->next = pnew;
    return 0;
}
//6.判断单向链表是否为空 1代表空 0代表非空
int IsEpLinkList(link_node_t *p)
{
    return p->next == NULL;
}
// 5.删除单向链表中指定位置的数据 post 代表的是删除的位置
int DeletePostLinkList(link_node_t *p, int post)
{
    // 0.容错判断
    if (post < 0 || post >= LengthLinkList(p))
    {
        perror("DeletePostLinkList err");
        return -1;
    }
    // 1.将头指针指向被删除位置的前一个节点
    for (int i = 0; i < post; i++)
        p = p->next;
    // 2.进行删除操作
    // 1)定义一个指针pdel指向被删除位置
    link_list_t pdel = p->next;
    // 2)跨过被删除节点
    p->next = pdel->next;
    // 3)释放被删除节点
    free(pdel);
    pdel = NULL;
    return 0;
}
//9.删除单向链表中出现的指定数据,data代表将单向链表中出现的所有data数据删除
int DeleteDataLinkList(link_node_t *p, datatype data)
{
    //1.定义一个指针q指向头结点的前一个节点,此时可以看做q指向一个无头单向链表
    link_list_t q = p->next;
    //2.用q遍历链表,将每一个节点的数据域与data作比较,如果相同就删除该节点
    while(q != NULL)
    {
        if(q->data == data)//如果相等,删除
        {
            p->next = q->next;
            free(q);
            q = p->next;
        }
        else//如果不相等,指针向后移动
        {
            p=p->next;
            q=p->next;
        }
    }
    return 0;
}
//7.修改指定位置的数据 post 被修改的位置 data修改成的数据
int ChangePostLinkList(link_node_t *p, int post, datatype data)
{
    //0.容错判断
    if(post < 0 || post >=LengthLinkList(p))
    {
        perror("ChangePostLinkList");
        return -1;
    }
    //1.将头指针指向被修改的节点
    for(int i=0;i<=post;i++)
        p=p->next;
    //2.修改数据
    p->data = data;
    return 0;

}
//8.查找指定数据出现的位置 data被查找的数据 //search 查找
int SearchDataLinkList(link_node_t *p, datatype data)
{
    //1.定义变量记录查找位置
    int post=0;
    //2.遍历链表,查找数据
    while(p->next != NULL)
    {
        p=p->next;
        if(p->data == data)
            return post;
        post++;
    }
    return -1;
}

单链表的倒置思路

(1) 将头节点与当前链表断开,断开前保存下头节点的下一个节点,保证后面链表能找得到,定义一个q保存头节点的下一个节点,断开后前面相当于一个空的链表,后面是一个无头的单向链表

(2) 遍历无头链表的所有节点,将每一个节点当做新节点,插入空链表头节点的下一个节点(每次插入的头节点的下一个节点位置)

bash 复制代码
void ReverseLinkList(link_node_t *p)
{
    link_node_t *q = p->next;
    link_node_t *temp = NULL;
    /*将头节点和下一个节点断开 */
    p->next = NULL;
    /*遍历无头单向节点 */
    while (q != NULL)
    {
        temp = q->next;
        /*进行插入操作 */
        q->next = p->next;
        p->next = q;
        q = temp;
    }
}

单链表和双向链表的区别

单链表(Singly Linked List)和双向链表(Doubly Linked List)是两种常见的链式存储结构,它们在数据结构中有一些不同之处。

单链

  1. 单链表中的每个节点包含两个部分:数据域和指针域(通常称为next指针)。
  2. 每个节点只有一个指针,指向下一个节点,最后一个节点的next指针指向NULL,表示链表结束。
  3. 单链表只能从头节点开始依次遍历访问节点,无法从后往前访问。
  4. 在单链表中,如果要删除某个节点,需要知道其前驱节点,因为只有前驱节点的next指针可以修改。

双向链表:

  1. 双向链表中的每个节点包含三个部分:数据域、前驱指针(prev指针)和后继指针(next指针)。
  2. 每个节点有两个指针,分别指向前一个节点和后一个节点。第一个节点的prev指针和最后一个节点的next指针指向NULL,表示链表的开始和结束。
  3. 双向链表可以从头节点开始正向遍历访问节点,也可以从尾节点开始反向遍历访问节点,这使得双向链表的某些操作更加高效。
  4. 在双向链表中,如果要删除某个节点,可以直接通过前驱指针和后继指针完成删除操作,不需要知道其前驱节点。

区别总结:

  1. 单链表每个节点只有一个指针域,只能从头节点开始单向遍历。

双向链表每个节点有两个指针域,可以从头节点开始正向遍历,也可以从尾节点开始反向遍历,灵活性更高。

  1. 双向链表相对于单链表,每个节点多了一个prev指针,这增加了一定的内存开销。
  2. 在插入和删除操作上,双向链表的操作可能比单链表更复杂一些,因为需要同时修改前驱和后继节点的指针。但是在一些情况下,双向链表的操作效率更高,特别是对于涉及到反向遍历或删除操作时。

栈和队列的区别

  1. 特点

栈:栈是一种后进先出(Last In First Out,LIFO)的数据结构,类似于把元素放在一个垂直的栈中,最后放入的元素最先被取出。

队列:队列是一种先进先出(First In First Out,FIFO)的数据结构,类似于排队,最先放入的元素最先被取出。

  1. 插入和删除操作:

栈:栈的插入操作称为入栈(Push),删除操作称为出栈(Pop)。元素只能从栈顶进。

队列:队列的插入操作称为入队(Enqueue),删除操作称为出队(Dequeue)。元素只能从队列的前端出队,从队列的后端入队。

  1. 访问限制:

栈:栈只允许访问栈顶的元素,无法直接访问其他位置的元素。

队列:队列只允许访问队列的前端和后端元素,无法直接访问其他位置的元素。

  1. 应用场景:

栈:常用于函数调用和递归调用的内存管理、表达式求值、括号匹配等场景。

1)方法调用和递归:在方法调用过程中,每次调用一个方法时将其压入栈,方法执行完毕后再将其弹出,保证方法调用的顺序。

2)表达式求值:在计算表达式的过程中,使用栈来存储运算符和操作数,根据运算符的优先级进行计算。

3)括号匹配:使用栈来判断括号的匹配情况,当遇到左括号时将其压入栈,遇到右括号时弹出栈顶元素并进行匹配。

队列:常用于任务调度、缓冲区管理、打印任务队列等场景。

1)消息队列:在分布式系统中,使用队列来进行消息的传递和处理,确保消息的顺序和可靠性。

2)多线程任务调度:使用队列来存储待执行的任务,并由多个线程从队列中取出任务进行执行。

3)打印任务队列:在打印机服务中,使用队列来存储待打印的任务,保证打印任务的顺序和公平性。

两个栈怎样实现一个队列,思路说一下

  1. 使用栈 A 作为入队栈,栈 B 作为出队栈。
  2. 入队操作时,直接将元素压入栈 A。
  3. 出队操作时,首先检查栈 B 是否为空,如果不为空,则直接弹出栈顶元素;如果栈 B 为空,则将栈 A 中的所有元素逐个弹出并压入栈 B,然后再弹出栈 B 的栈顶元素作为出队元素。

冒泡排序原理?时间复杂度多少?

原理:冒泡排序是一种简单的排序算法,每次比较相邻的两个元素,如果它们的顺序错误,则交换位置,通过多次遍历,将最大的元素逐渐冒泡到正确的位置上。

时间复杂度:冒泡排序的平均时间复杂度为O(n²),其中n是待排序元素的数量,最好情况下时间复杂度为O(n)

什么是二叉树、满二叉树?

二叉树:是一种特殊的树结构,每个节点最多有两个子树,分别称为左子树和右子树

满二叉树:深度为k(k>=1)时,节点数为2k - 1(2的k次幂-1)个的二叉树。

有空再了解一下平衡二叉树和红黑树的相关知识。

二叉树的前序中序后序遍历

(1)前序遍历:先访问根节点,然后按照左子树、右子树的顺序进行遍历

(2)中序遍历:按照左子树、根节点、右子树的顺序进行遍历

(3)后序遍历:按照左子树、右子树、根节点的顺序进行遍历

查找算法学过哪些 ?二分查找的时间复杂度多少?

顺序查找:从头到尾逐个比较查找,时间复杂度为O(n)

二分查找:对有序数组进行查找,每次将查找区间二分,并与目标值进行比较,时间复杂度为O(nlog n )

分块查找:块间有序、块内无序 ,时间复杂度为O(log(m)+n/m)

哈希表的原理?

哈希表:是一种通过哈希函数将键映射到存储位置的数据结构。查找元素时,先使用哈希函数计算出键的哈希值,然后根据哈希值找到对应的存储位置,在哈希冲突的情况下,通常采用开放地址法、活链地址法来解决.

排序算法学过哪些?快速排序的实现原理?

  1. 冒泡排序:比较相邻元素,将较大的元素向后移动,时间复杂度为O(n²)
  2. 选择排序:从数列中找最小值,找到后和第一个位置的数据进行交互,再从剩下数中找最小值,依次类推。
  3. 快速排序:采用"分治"的思想,对于一组数据,选择一个基准元素(base),通常选择第一个或最后一个元素,通过第一轮扫描,比base小的元素都在base左边,比base大的元素都在base右边,再有同样方法递归排序这两部分,直到序列中所有数据均有序为止。
相关推荐
Yu_Lijing1 小时前
基于C++的《Head First设计模式》笔记——生成器模式
c++·笔记·设计模式
计算机安禾2 小时前
【C语言程序设计】第36篇:二进制文件的读写
c语言·开发语言·c++·算法·github·visual studio code·visual studio
2301_807367192 小时前
C++代码风格检查工具
开发语言·c++·算法
Morwit2 小时前
*【力扣hot100】 215. 数组中的第K个最大元素
数据结构·c++·算法·leetcode·职场和发展
weiyvyy2 小时前
接口开发的完整流程:从需求到验证
驱动开发·嵌入式硬件·硬件架构·硬件工程
我是唐青枫2 小时前
深入理解 C#.NET TaskScheduler:为什么大量使用 Work-Stealing
c#·.net
博语小屋2 小时前
多路转接select、poll
开发语言·网络·c++·php
m0_730115112 小时前
C++中的命令模式实战
开发语言·c++·算法
Albert Edison2 小时前
【C++11】可变参数模板
java·开发语言·c++