【算法速查】万字图解带你快速入门八大排序(下)

news2025/1/21 0:49:48

在这里插入图片描述

君兮_的个人主页

即使走的再远,也勿忘启程时的初心

C/C++ 游戏开发

Hello,米娜桑们,这里是君兮_,首先在这里祝大家中秋国庆双节同乐!!抓住假期的小尾巴,今天来把算法速查的八大排序的后续写完,当然由于篇幅的原因不是每一种算法都详解,这篇文章更多是作为让初学者有一个初步的了解以及学过的人某个排序算法忘了的话的快速回忆,后续我也会把每种算法的重点以及难点挑出来单独为大家讲解的

  • 好了废话不多说,开始我们今天的学习吧!!

    八大排序

    • 前言
    • 五.冒泡排序
    • 六.快速排序
      • 1.hoare版本
      • 2.挖坑版本
      • 3.前后指针版本
    • 七.归并排序
      • 非递归实现
    • 八.计数排序
    • 几种排序对比
      • 不同排序的适用场景
      • 稳定性以及时/空间复杂度对比
    • 总结

前言

  • 在开始前,我们还是通过一张图片带大家认识一下有哪八大排序
    在这里插入图片描述
  • 之前我们已经讲了什么是排序以及前面的四种排序,具体内容在以下链接
    【算法速查】一篇文章带你快速入门八大排序(上)
  • 今天我们来讲讲后面四种排序

五.冒泡排序

  • 在我们日常的应用中,实际上由于冒泡排序的时间复杂度实在是太高,我们几乎不会用到,但由于冒泡排序比较简单,它通常出现在课堂上帮助大家入门排序算法
  • 由于比较简单,这里就不详细讲了,感兴趣可以看看我之前写过的这篇博客
    【C语言初阶】带你玩转C语言中的数组,并逐步实现冒泡排序,三子棋,扫雷
  • 这里是冒泡排序的动图
    在这里插入图片描述
void Qsort(int* a, int n)
{
    assert(a);//断言防止越界
    for (int i = 0; i < n-1; i++)
    {
        for (int j = 0; j < n - 1 - i; j++)
        {
            int tmp = a[j + 1];
            if (a[j] > a[j + 1])
            {
                a[j + 1] = a[j];
                a[j] = tmp;
            }
        }
    }
}

int main()
{
    int a[5] = { 5,6,2,9,0 };
    Qsort(a, 5);
    for (int i = 0; i < 5; i++)
    {
        printf("%d ", a[i]);
    }
    return 0;
}

六.快速排序

  • 相信很多人都听过快速排序的顶顶大名,什么排序算法这么狂,敢叫快速排序,根本不把其他算法放在眼里
    在这里插入图片描述
  • 快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中
    的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右
    子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
  • 下面我们来讲讲三种实现快速排序的方法

1.hoare版本

  • 这是由Hoare提出的最初的快速排序,具体的排序是这样的
    在这里插入图片描述

基本思想:
1.确定一个key值,先让右走,遇到比key值大的就继续走,遇到比key值小的就停下,此时让左走,遇到比key小的就继续走,遇到比key大的就停下,此时交换两者的值,让比key大的值到右边,比key小的值到左边
2.重复上述的循环,直到左右两者相遇,此时交换key和此时左右相遇位置的值,由于我们是让右先走的,如果此时相遇的位置的值比key大,它不可能停下,也就是说,两者相遇位置的值一定是比key小的,交换后,我们就实现了一趟快排循环,此时key右边的值一定大于等于key,key左边的值一定小于等于key值
3.以key为界限,把key左边和key右边的数据继续进行单趟快排的操作,直至该数组中所有数据都有序,完成快速排序

Swap(int* p1, int* p2)
{
    int tmp = *p1;
    *p1 = *p2;
    *p2 = tmp;
}
int getMid(int* a, int left, int right)
{
    assert(a);
    int mid = (left + right) / 2;
    if (a[left] > a[mid])
    {
        if (a[mid] > a[right])
            return mid;
        else if(a[right]>a[left])
        {
            return left;
        }
        else
        {
            return right;
        }
    }
    else
    {
        if (a[mid] < a[right])
            return mid;
        else if (a[left] > a[right])
        {
            return left;
        }
        else
        {
            return right;
        }

    }
}
int PartSort1(int* a, int left, int right)
{
    int mid = getMid(a, left, right);//三数取中
    //把取好的key放到left中
    Swap(&a[left], &a[mid]);
    int key = left;
    while (left<right)
    {
    //右边先走
        while (left < right && a[right] >= a[key])
        {
            right--;
        }
      //左边走
        while (left<right&&a[left]<=a[key])
        {
            left++;
        }
        //交换
        Swap(&a[left], &a[right]);
    }
    //交换停下的位置的值把key换到此时左右相遇的位置
    Swap(&a[key], &a[left]);
    //此时key的左右都有序,由于right,left都指处于key的位置返回任意即可
    return right;
}
void QuickSort(int* a, int left,int right)
{
	//只剩下一个值了,说明已经有序,不需要再排,递归结束
    if (left >= right)
        return;
   
    int key = PartSort1(a,left,right);
    //key已经满足左右都有序了不需要再排
    //排key的左边
    QuickSort(a, left, key-1);
    //排key的右边
    QuickSort(a, key+1, right);

}

2.挖坑版本

  • 与上面的hoare版本的快速排序非常类似,但是用“挖坑”的形式来替换上面的left和right
  • 动图演示如下:
    在这里插入图片描述

基本思想:
1.和hoare一样,先找到一个key值保存到所需排序数据的第一个位置中,通过临时变量保存下来,使此时的第一个位置变成一个“坑位”,让右边先移动,当遇到比key值大的就继续朝左走,遇到比key值小的位置时,就把这个位置的值赋给“坑位”,此时,当前位置变成了新的“坑位”,此时让左边移动,遇到比key值小的继续向右走,遇到比key值大的就把这个值赋给“坑位”,此时这个位置变成新的“坑位”
2.重复上述的过程,直到左右相遇,此时它们一定是在某个“坑位”相遇的,再把key值赋给这个“坑位”,就完成了一次快排
3.对此时位置的左右再分别进行上述操作,直至整个所需排序的数据都有序。

int PartSort2(int* a, int left, int right)
{
    int mid = getMid(a, left, right);
    Swap(&a[left], &a[mid]);
    int hole = left;//坑位
    int key = a[left];//保存left的值
    while (left<right)
    {
        while (left < right && a[right] >= key)
        {
            right--;
        }
        //把此时小于key的值赋给坑位,此时的位置变成新的坑
        a[hole] = a[right];
        hole = right;
        while (left < right && a[left] <= key)
        {
            left++;
        }
        //把此时大于key的值赋给坑位,此时的位置变成新的坑
        a[hole] = a[left];
        hole = left;
    }
    //key的值赋给坑
    a[hole] = key;
    return hole;
}
void QuickSort(int* a, int left,int right)
{
    if (left >= right)
        return;
   
    int key = PartSort2(a,left,right);
    QuickSort(a, left, key-1);
    QuickSort(a, key+1, right);

}

3.前后指针版本

  • 本质上与前面两个版本都差不多,使用两个快慢不同的指针实习
  • 动图演示如下:
    在这里插入图片描述

基本思想:
1.找到一个key值保存在left中,定义一前一后两个指针prev和cur,当cur中保存的值比key小的时候,两个指针一起朝右走,当cur中保存的值比key大时,只有cur向右走走,直至再次遇到比key值小的,此时交换cur和prev中保存的值。
2.重复上述的过程直至cur走到尾,此时交换prev中的值与key的值,完成一次快速排序
3.此时prev左右均有序,对左右再进行上述的循环,直至待排数组全部有序

int PartSort3(int* a, int left, int right)
{
    int mid = getMid(a, left, right);
    Swap(&a[left], &a[mid]);
    int key = left;
    //定义两个指向前后位置的整形
    int prev = left;
    int cur = prev + 1;
    while (cur <= right)
    {
       //当cur中的值比key小且此时cur和prev中有大于key值的数时
        while (a[cur]<=a[key] && ++prev != cur)
        {
        //交换下一个位置的prev的值和此时cur保存的值
            Swap(&a[prev], &a[cur]);
        }
        //无论cur中的值比key大还是小,每次循环都要向前走
        ++cur;

    }
    //cur走到尾,交换此时prev的值和key的值
    Swap(&a[prev], &a[key]);
    //把左右有序的位置返回
    return prev;
}
void QuickSort(int* a, int left,int right)
{
    if (left >= right)
        return;
   
    int key = PartSort3(a,left,right);
    QuickSort(a, left, key-1);
    QuickSort(a, key+1, right);

}
  • 这里有些小细节需要说明:
  • 1.++prev!=cur ,当相等时,说明prev和cur没拉开差距,此时它们保存的值都是小于key的,不需要进行交换
  • [x] 2.前置的++,无论两者是否拉开差距,只要满足cur的值小于key,cur和prev都得++,因此先++再判断是否拉开差距
  • 3.prev没走到尾,但是我们知道,只要prev和cur拉开差距,说明只有cur++了,此时里面保存的值一定都是大于key的,因此只需要cur走到尾就能满足prev左边的值小于key右边的值大于key了。

  • 注意:这三种快速排序的实现中间都加了三数取中的优化,主要是保证取到的值不在最左或者最右增加排序的效率,由于篇幅原因,这里不展开讲了,之后会具体出有关博客讲讲快速排序中的细节优化和非递归实现的。
  • 快速排序的特性总结:
  • 1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
  • 2. 时间复杂度:O(N*logN)
  • 3. 空间复杂度:O(logN)
  • 4. 稳定性:不稳定

七.归并排序

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and
Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有
序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

  • 简单的来说,就是先保证子序列有序,把子序列合并排序,最终把整个序列变为有序(你可以对应二叉树的后序遍历来理解)
  • 动图演示如下:

在这里插入图片描述

基本思想:先拆分后合并
1.拆分
把所需排序的序列拆除左序列和右序列,循环这个过程直至左序列和右序列都只有一个数结束
2.合并
把拆分好的左右子序列合并并排序,这个过程在我们开辟的和原所需排序的数组大小相同的数组中进行,每次排序完后都把这部分排序好的值重新赋给原数组,重复这个过程,直至合并所有左右子序列,此时新的数组即为有序。

void _MergeSort(int* a, int* tmp, int left, int right)
{
    if (left >= right)
        return;
    int mid = (left + right) / 2;
    //分左子列
    _MergeSort(a, tmp, left, mid);
    //分右子列
    _MergeSort(a, tmp, mid + 1, right);
    int begin1 = left, end1 = mid;
    int begin2 = mid + 1, end2 = right;
    int index = left;
    //排序,把左右子列的数据从小到大填入我们开辟的数组中
    while (begin1 <= end1 && begin2 <= end2)
    {
        if (a[begin1] < a[begin2])
        {
            tmp[index++] = a[begin1++];
        }
        else
        {
            tmp[index++] = a[begin2++];
        }
    }
    //左子列还有数据
    while (begin1 <= end1)
    {
        tmp[index++] = a[begin1++];
    }
    //右子列还有数据
    while (begin2 <= end2)
    {
        tmp[index++] = a[begin2++];
    }
    //每排完一次,都把对应排好的合并序列拷回原数组中
    memcpy(a + left, tmp + left, sizeof(int) * (right - left + 1));
}
void MergeSort(int* a, int n)
{
 //开辟与原数组大小相同的数组
    int* tmp = (int*)malloc(sizeof(int) * n);
    if (tmp == NULL)
    {
        return;
    }
    _MergeSort(a, tmp, 0, n - 1);
    free(tmp);
}

非递归实现

  • 上面讲的是递归实现归并排序,但是我们知道,递归是在栈空间开辟内存,而栈空间往往都是很小的,当数据很大时容易“爆栈”,由于归并排序是八大排序中唯一一种外排序算法,而磁盘中的数据往往又很多很容易就“爆栈”了,因此我们还需要学会使用循环非递归实现归并算法
    在这里插入图片描述

基本思想:
1.通过一个gap来控制归并子序列的大小,gap从1开始,同递归一样,每次把子序列排序好后合并拷贝回原数组
2.gap的值从1开始,每经过上述过程,gap*=2,循环直至gap>=n,此时所有子序列均排序合并,完成归并排序

MergeSortNonR(int* a, int n)
{
    int* tmp = (int*)malloc(sizeof(int) * n);
    int gap = 1;
    while(gap < n)
    {
    //每次需要排序并归并的子序列大小为2*gap,从0开始,到n-1结束此时gap大小的合并
        for (int i = 0; i < n; i += 2*gap)
        {
            int begin1 = i, end1 = i + gap - 1;
            int begin2 = i + gap, end2 = i + gap * 2 - 1;
            //gap太大超出数组大小越界,直接break返回
            if (begin2 >= n)
                break;
             //n比2*gap小,直接让end2等于最后一个元素,防止越界
            if (end2 >= n)
                end2 = n - 1;
            int index = i;
            while (begin1 <= end1 && begin2 <= end2)
            {
                if (a[begin1] < a[begin2])
                {
                    tmp[index++] = a[begin1++];
                }
                else
                {
                    tmp[index++] = a[begin2++];
                }
            }
            while (begin1 <= end1)
            {
                tmp[index++] = a[begin1++];
            }
            while (begin2 <= end2)
            {
                tmp[index++] = a[begin2++];
            }
            //拷贝回
            memcpy(a + i, tmp + i, sizeof(int) * (end2-i+1));
        }
        //完成一次子序列合并,把合并后的序列当子序列,增大gap继续
        gap *= 2;
    }
    free(tmp);
}
  • 归并排序的特性总结
  • 1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
  • 2. 时间复杂度:O(N*logN)
  • 3. 空间复杂度:O(N)
  • 4. 稳定性:稳定

八.计数排序

  • 基本思想:计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。 操作步骤:
  • 1. 统计相同元素出现次数
  • 2. 根据统计的结果将序列回收到原来的序列中
  • 其实就是笨办法,我们通过找出最大最小值来确定排序序列的范围,统计每一个数出现的次数然后把出现的数从小到大按次数从小到大插入回原序列中
    在这里插入图片描述
void CountSort(int* a, int n)
{
    int min = a[0];
    int max = a[0];
    for (int i = 0; i < n; i++)
    {
        if (a[i] > max)
            max = a[i];
        if (a[i] < min)
            min = a[i];
    }
    //统计数的范围
    int Range = max - min + 1;
    int* Count = (int*)malloc(sizeof(int) * Range);
    if (Count == NULL)
    {
        perror("malloc failed");
        exit(-1);
    }
    //初始化一下,防止出现随机数
    memset(Count, 0, sizeof(int) * Range);
    for (int i = 0; i < n; i++)
    {
        //对应位置的数的次数
        Count[a[i] - min]++;
    }
    int j = 0;
    for (int i = 0; i < Range; i++)
    {
        //把Count中存在的数出现了几次重新填回原序列
        while (Count[i]--)
        {
            //i+min是对应位置的值
            a[j++] = i + min;
        }
    }
    free(Count);
}
  • 计数排序的特性总结:
  • 1. 计数排序在数据范围集中时,效率很高,但是适用范围及场景有限。
  • 2. 时间复杂度:O(MAX(N,范围))
  • 3. 空间复杂度:O(范围)
  • 4. 稳定

几种排序对比

不同排序的适用场景

  • 冒泡排序:适用于小规模数据的排序,时间复杂度为O(n^2),不适合处理大规模数据。

  • 插入排序:适用于对已经接近有序的数据进行排序,时间复杂度为O(n^2),效率较高。

  • 选择排序:适用于简单选择最小(或最大)的元素,时间复杂度为O(n^2),适合处理小规模数据。

  • 快速排序:适用于任意规模的数据排序,具有较好的平均时间复杂度O(nlogn),但最坏情况下可能达到O(n^2)。

  • 归并排序:适用于任意规模的数据排序,具有稳定的时间复杂度O(nlogn),但需要额外的空间来存储临时数组。

  • 堆排序:适用于对大规模数据进行排序,时间复杂度为O(nlogn),不需要额外的空间。

  • 希尔排序:适用于当数据规模较大,插入排序性能较差,且要求对内存消耗较小时,时间复杂度约为O(n^1.3)。

  • 计数排序:适用于数据存在大量重复值且数据范围相对集中,时间复杂度为O(MAX(N,Range))

稳定性以及时/空间复杂度对比

在这里插入图片描述


总结

  • 好啦,我们总算把八大排序算法都讲完了,算法这一块光靠看代码不是那么容易理解的,因此我花了大量的时间画图分析,希望能对你有所帮助
  • 当然,这篇文章创作的初衷是希望帮助初学者对排序算法有一个大致的了解,对已经学过的人起到在需要使用的时候快速回忆的效果,因此可能还有一部分细节不全,之后我会挑出重点单独出博客讲解
  • 有任何的问题和对文章内容的疑惑欢迎在评论区中提出,当然也可以私信我,我会在第一时间回复的!!

新人博主创作不易,如果感觉文章内容对你有所帮助的话不妨三连一下再走呗。你们的支持就是我更新的动力!!!

**(可莉请求你们三连支持一下博主!!!点击下方评论点赞收藏帮帮可莉吧)**

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1064106.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Go 复合数据类型之结构体与自定义类型

Go 复合数据类型之结构体与自定义类型 文章目录 Go 复合数据类型之结构体与自定义类型一、类型别名和自定义类型1.1 类型定义&#xff08;Type Definition&#xff09;简单示例 1.2 类型别名简单示例 1.3 类型定义和类型别名的区别 二、结构体2.1 结构体介绍2.2 结构体的定义2.…

MySQL视图、用户管理

目录 视图概念和操作 视图基本操作 视图规则和限制 用户管理 用户 视图概念和操作 什么是视图&#xff1f; 视图是一个虚拟表&#xff0c;由一个或多个基本表的查询结果组成&#xff08;视图是存储在数据库中的查询的SQL 语句&#xff0c;不在数据库中以存储的数据值集形式…

VC6 MFC Dialog as apllication 编程

MFC框架虽然古老&#xff0c;但编程还是方便多了&#xff0c;在操控界面控件的基础上&#xff0c;平时在Console模式习练的类可以融入到MFC中&#xff0c;开发应用程序还是比较方便的。下图是习练程序的界面。 说明&#xff1a; 一个EDIT框&#xff0c;一个Label框&#xff1b…

Linux进程相关管理(ps、top、kill)

目录 一、概念 二、查看进程 1、ps命令查看进程 1&#xff09;ps显示某个时间点的程序运行情况 2&#xff09;查看指定的进程信息 2、top命令查看进程 1&#xff09;信息统计区&#xff1a; 2&#xff09;进程信息区 3&#xff09;交互式命令 三、信号控制进程 四、…

假期AI新闻热点:亚运会Al技术亮点;微软GPT-4V论文精读;Perplexity推出pplx-api;DALL-E 3多渠道测评 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f525; 科技感拉满&#xff0c;第19届杭州亚运会中的Al技术亮点 八年筹备&#xff0c;杭州第19届亚运会开幕式于9月23日晚隆重举行&#xff0…

口袋参谋:如何有效地监测你的竞争对手!

​在淘宝天猫上开店&#xff0c;竞争是非常大的&#xff0c;那么就会出现许多同样的产品&#xff0c;如果想要在竞争中胜出&#xff0c;就需要多去研究同行的数据&#xff0c;知己知彼&#xff0c;百战百胜。 掌握竞争对手数据目的主要是有2个&#xff1a; 1、掌握对手是怎么起…

【二叉树】的实现

&#x1f4d9;作者简介&#xff1a; 清水加冰&#xff0c;目前大二在读&#xff0c;正在学习C/C、Python、操作系统、数据库等。 &#x1f4d8;相关专栏&#xff1a;C语言初阶、C语言进阶、C语言刷题训练营、数据结构刷题训练营、有感兴趣的可以看一看。 欢迎点赞 &#x1f44d…

竞赛选题 深度学习 python opencv 动物识别与检测

文章目录 0 前言1 深度学习实现动物识别与检测2 卷积神经网络2.1卷积层2.2 池化层2.3 激活函数2.4 全连接层2.5 使用tensorflow中keras模块实现卷积神经网络 3 YOLOV53.1 网络架构图3.2 输入端3.3 基准网络3.4 Neck网络3.5 Head输出层 4 数据集准备4.1 数据标注简介4.2 数据保存…

中断:ZYNQ

整个中断系统架构中&#xff0c;只包含以下三种中断&#xff1a;  软件生成中断(Software Generated Interrupts&#xff0c;SGI)  私有外设中断(Private Peripheral Interrupts&#xff0c;PPI)  共享外设中断(Shared Peripheral Interrupts&#xff0c;SPI) 每种中断…

华为云云耀云服务器L实例评测|云耀云服务器L实例部署DjangoBlog个人博客系统

华为云云耀云服务器L实例评测&#xff5c;云耀云服务器L实例部署DjangoBlog个人博客系统 一、云耀云服务器L实例介绍1.1 云耀云服务器L实例简介1.2 云耀云服务器L实例特点 二、DjangoBlog介绍2.1 DjangoBlog介绍2.2 DjangoBlog特点 三、本次实践介绍3.1 本次实践简介3.2 本次环…

java socket实现代理Android App

实现逻辑就是转发请求和响应。 核心代码 // 启动代理服务器private void startProxyServer() {new Thread(new ProxyServer()).start();}// 代理服务器static class ProxyServer implements Runnable {Overridepublic void run() {try {// 监听指定的端口int port 8098; //一…

AutoGen - 多个Agent开发LLM应用的框架

文章目录 关于安装使用关于 Enable Next-Gen Large Language Model Applications 用多个Agent开发LLM应用的框架,这些agent可相互交流以解决任务。 官网:https://microsoft.github.io/autogen/github : http://github.com/microsoft/autogendiscord : https://discord.com/i…

LVGL_基础控件滚轮roller

LVGL_基础控件滚轮roller 1、创建滚轮roller控件 /* 创建一个 lv_roller 部件(对象) */ lv_obj_t * roller lv_roller_create(lv_scr_act()); // 创建一个 lv_roller 部件(对象),他的父对象是活动屏幕对象// 将部件(对象)添加到组&#xff0c;如果设置了默认组&#xff0c…

buuctf-crypto 1

rsarsa 题目描述 Math is cool! Use the RSA algorithm to decode the secret message, c, p, q, and e are parameters for the RSA algorithm.p 96484230290105156765905517400104265349457376392357398006439893520398525072984913995610350091634270503701075707336333…

Java循环队列

目录 一、循环队列 二、设计循环队列 一、循环队列 队列&#xff1a;只能在一端进行插入数据操作&#xff0c;另一端进行删除数据操作的特殊线性表&#xff0c;是一种先进先出的存储结构 插入操作的一端为队尾&#xff0c;删除操作的一端为队头 在线性队列中&#xff0c;一…

leetcode 62. 不同路径、63.不同路径||

62. 不同路径 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。 问总共有多少条不同的路径&…

课题学习(三)----倾角和方位角的动态测量方法(基于陀螺仪的测量系统)

一、内容介绍 该测量系统基于三轴加速度和三轴陀螺仪&#xff0c;安装在钻柱内部&#xff0c;随钻柱一起旋转&#xff0c;形成捷联惯性导航系统&#xff0c;安装如下图所示&#xff1a;   假设三轴加速度和陀螺仪的输出为: f b [ f x f y f z ] T f^b\begin{bmatrix}f_{x} …

C++ 学习系列 -- std::list

一 std::list 介绍 list 是 c 中的序列式容器&#xff0c;其实现是双向链表&#xff0c;每个元素都有两个指针&#xff0c;分别指向前一个节点与后一个节点 链表与数组都是计算机常用的内存数据结构&#xff0c;与数组连续内存空间不一样的地方在于&#xff0c;链表的空间是不…

Springboot实现登录功能(token、redis、登录拦截器、全局异常处理)

登录流程&#xff1a; 1、前端调用登录接口&#xff0c;往接口里传入账号&#xff0c;密码 2、根据账号判断是否有这个用户&#xff0c;如果有则继续判断密码是否正确 3、验证成功后&#xff0c;则是根据账号&#xff0c;登录时间生成token&#xff08;用JWT&#xff09; 4、将…

64位Office API声明语句第111讲

跟我学VBA&#xff0c;我这里专注VBA, 授人以渔。我98年开始&#xff0c;从源码接触VBA已经20余年了&#xff0c;随着年龄的增长&#xff0c;越来越觉得有必要把这项技能传递给需要这项技术的职场人员。希望职场和数据打交道的朋友&#xff0c;都来学习VBA,利用VBA,起码可以提高…