排序(前篇)
1.排序的概念及其运用
2.插入排序的概念及实现
3.希尔排序的概念及实现
4.选择排序概念及实现
总代码(对比各个排序在大量的数据情况排序所化的时间):
1.排序的概念及其运用
1.1排序的概念
排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次
序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
内部排序:数据元素全部放在内存中的排序。
外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不断地在内外存之间移动数据的排序。
1.2排序的运用
在网上购物时,会出现综合,销量及评论数等,会以此为参考来排序,选出综合最高,销量最高的产品,以及院校排名这些,都使用了排序。
(以下图片仅参考)
1.3 常见的排序算法
2.插入排序的概念及实现
2.1插入排序的概念
就像扑克牌一样,每拿到一张牌都要放进已经排好序的牌里面,用这张牌跟里面的比较,再把这张牌放到合适的位置,插入排序的原理就是把数插入已经排好序的数里面,比较排好数的里面的每一个数,找到位置就插入。
代码:
#include<stdio.h>
void InsertSort(int* a,int n)
{for (int i = 0; i < n - 1; i++){int end = i;int tmp = a[end+1];while (end >= 0){if (tmp < a[end]){a[end+1] = a[end];end--;}else{break;}}a[end + 1] = tmp;}
}
int main()
{int a[10] = { 3,4,5,1,2,6,7,8,2 };int size = sizeof(a) / sizeof(a[0]);InsertSort(a,size);for (int i = 0; i < 9; i++){printf("%d ", a[i]);}return 0;
}
代码分析:
while循环出去有俩种情况,1是触发break,二是while循环的条件不满足,在循环外把tmp赋给数组下标为end+1的位置, 是因为如果是while循环结束的话刚好也可以赋值,但是在里面的话,因为while结束的就无法把数据加回去(移动带来覆盖,会有一位重复,需要赋值来填回去)。
(动态图关于插入排序24年-05月27日--排序/动图/插入排序.gif · 比特杭哥/113期 - Gitee.com)
2.2冒牌排序的时间复杂度
冒泡排序的最坏情况:
假设if条件每次都会执行,那么时间复杂度就为O(N^2).
最好的情况:
上面冒泡代码不能做到O(N),需要对其优化才能使冒泡排序达到最好情况,
设置一个flag去判断是否触发了if条件,如果触发了就置为0,后面再去检验值是否为一开始的初始值,是就说明没有触发if条件,没触发就说明顺序是排好的,只执行了里面的if判断,所以时间复杂度是O(N)。
void BubbleSort(int* a, int n)
{for (int i = 0; i < n; i++){int flag = 1;for (int j = 0; j < n-i-1; j++){if (a[j] > a[j + 1]){int tmp = a[j];a[j] = a[j + 1];a[j + 1] = tmp;flag = 0;}}if (flag == 1){break;}}
}
2.3插入排序的时间复杂度
按照最坏打算:
既逆序情况
最好的情况:
当数据是以升序排序时,里面的循环不执行(会从break跳出),则只有for循环执行,所以时间复杂度就为O(N)。
插入排序与冒泡排序的时间复杂度虽然是一样的,但插入排序是比冒泡排序好,因为插入排序最坏的情况很难达到,只有是逆序(或者接近这个情况)的情况下,插入排序才会慢下来,而冒泡排序一般都是O(N^2),因为冒泡每次遍历选出其中最大的放最后,if的条件任意触发。
void BubbleSort(int* a, int n)//冒泡排序
{for (int i = 0; i < n; i++){for (int j = 0; j < n-i-1; j++){if (a[j] > a[j + 1]){int tmp = a[j];a[j] = a[j + 1];a[j + 1] = tmp;}}}
}
3.希尔排序的概念及实现
3.1希尔排序概念
希尔排序与插入排序密不可分,基于插入排序来实现希尔排序的。
希尔排序:首先会对数据进行预排序(让数组接近有序),再进行插入排序(因为插入排序怕的是数组是逆序情况)。
预排序:把数组在逻辑上分成gap组,gap是间隔的距离,然后对每个gap组进行插入排序,这样可以使大的数据跑到比较后面的位置,小的数据会跑到比较前面的位置,gap设置的越大,大的数据越快到后面,小的数据越快到前面,越不接近有序,gap设置的越小,数据跳的慢,但是会越接近有序的情况,gap为1的时候就是插入排序了,希尔排序就是慢慢把gap的值调小(设置gap是为了最后一次gap为一做铺垫,为了插入排序能更快的完成),最后gap为一时执行插入排序。
3.2希尔排序的代码实现
代码实现1:
void swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}
void ShellSort1(int* a, int n)
{int gap = n;while (gap > 1){gap = gap / 3 + 1;for (int i = 0; i < n - gap; i++){int end = i;int tmp = a[end + gap];while (end >= 0){if (a[end] > tmp){swap(&a[end], &a[end + gap]);end--;}elsebreak;}a[end + gap] = tmp;}}
}
代码实现二:
void ShellSort2(int* a, int n)
{int gap = n;while (gap > 1){gap = gap / 3 + 1;for (int j = 0; j < gap; j++){for (int i = j; i < n - gap; i += gap){int end = i;int tmp = a[end + gap];while (end >= 0){if (a[end] > tmp){swap(&a[end], &a[end + gap]);end -= gap;}elsebreak;}a[end + gap] = tmp;}}}
}
代码分析:上面俩个代码的样子不一样,但是效率是一样的俩种都可以使用,
n是数组的个数,一般gap的值都为个数的三分之一+1,加一是为了最后gap的值能达到一进行插入排序,gap/2的效率不如gap/3(大量数据实验得出的),代码一是多个gap组同时进行预排序,每次i加一,但是比的是gap距离的数据,gap第一组的第一个跟其gap距离的比完后,第二个gap组的第一个跟其gap距离的比,再到第三个gap组的第一个与其gap距离的数据比较,再到第二个等等,代码二则是第一个gap组会一直比完才到第二个gap组比。还需要注意的是循环终止条件是n-gap,是因为循环里面有end+gap,n+gap-gap刚好是为n,也就是数组最后一个数据下标的下一个,有效数据的下一个是不能访问的,会造成越界访问,数组最多能访问到n-1的位置,如果是i<n,则a[end+gap]就会越界了。
3.3希尔排序的时间复杂度(简单分析)
希尔排序的时间复杂度为O(N^1.3)。
4.选择排序概念及实现
选择排序就是遍历数组找到最小的数据并把它放在最前面,可以对它进行优化,就是在遍历的同时把最大和最小的数据找出来并放在俩边
代码实现:
void SelectSort(int* a, int n)
{int begin, end;begin = 0;end = n - 1;while (begin < end){int mini, max;mini = max = begin;for (int i = begin+1; i <= end; i++){if (a[mini] > a[i]){mini = i;}if (a[max] < a[i]){max = i;}}swap(&a[mini], &a[begin]);swap(&a[max], &a[end]);end--;begin++;}
}
代码分析:
因为是找最大和最小并放在俩边,所以begin和end会慢慢往中间靠近,在定义最小值和最大值去和数组的每一个数比较,比定义的最大值大就交换一下下标,比最小值小就交换 下标,遍历完后在交换值,并且把begin++和end--,因为begin和end都放好了对应的值,要放其它位置的值。
总代码(对比各个排序在大量的数据情况排序所化的时间):
test.c文件:
#include"Sort.h"void TestInsertSort()
{int a[] = { 2,4,1,7,8,3,9,2 };InsertSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));
}void TestShellSort()
{int a[] = { 9,1,2,5,7,4,6,3,5,9,1,2,5,7,4,6,3,5,9,1,2,5,7,4,6,3,5,9,1,2,5,7,4,6,3,5,9,1,2,5,7,4,6,3,5 };//InsertSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));ShellSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));
}void TestSelectSort()
{//int a[] = { 9,1,2,5,7,4,6,3,5,9,1,2,5,7,4,6,3,5,9,1,2,5,7,4,6,3,5,9,1,2,5,7,4,6,3,5,9,1,2,5,7,4,6,3,5 };//InsertSort(a, sizeof(a) / sizeof(int));//int a[] = { 2,4,1,7,8,3,9,2 };int a[] = { 9,1,2,5,7,4,6,3 };PrintArray(a, sizeof(a) / sizeof(int));SelectSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));
}void TestOP()
{srand(time(0));const int N = 1000000;int* a1 = (int*)malloc(sizeof(int) * N);int* a2 = (int*)malloc(sizeof(int) * N);int* a3 = (int*)malloc(sizeof(int) * N);int* a4 = (int*)malloc(sizeof(int) * N);int* a5 = (int*)malloc(sizeof(int) * N);int* a6 = (int*)malloc(sizeof(int) * N);int* a7 = (int*)malloc(sizeof(int) * N);for (int i = 0; i < N; ++i){a1[i] = rand()+i;a2[i] = a1[i];a3[i] = a1[i];a4[i] = a1[i];a5[i] = a1[i];a6[i] = a1[i];a7[i] = a1[i];}int begin1 = clock();//InsertSort(a1, N);int end1 = clock();int begin2 = clock();ShellSort(a2, N);int end2 = clock();int begin3 = clock();//SelectSort(a3, N);int end3 = clock();int begin4 = clock();HeapSort(a4, N);int end4 = clock();int begin5 = clock();//QuickSort(a5, 0, N - 1);int end5 = clock();int begin6 = clock();//MergeSort(a6, N);int end6 = clock();int begin7 = clock();//BubbleSort(a7, N);int end7 = clock();printf("InsertSort:%d\n", end1 - begin1);printf("ShellSort:%d\n", end2 - begin2);printf("SelectSort:%d\n", end3 - begin3);printf("HeapSort:%d\n", end4 - begin4);printf("QuickSort:%d\n", end5 - begin5);printf("MergeSort:%d\n", end6 - begin6);printf("BubbleSort:%d\n", end7 - begin7);free(a1);free(a2);free(a3);free(a4);free(a5);free(a6);free(a7);
}int main()
{//TestInsertSort();//TestShellSort();TestSelectSort();//TestOP();return 0;
}
Sort.h文件:
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>void PrintArray(int* a, int n);// 有实践意义
void InsertSort(int* a, int n);void ShellSort(int* a, int n);void ShellSort(int* a, int n);void HeapSort(int* a, int n);// 适合教学,实践中没啥价值
void BubbleSort(int* a, int n);void SelectSort(int* a, int n)
Sort.c文件:
#include"Sort.h"void PrintArray(int* a, int n)
{for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");
}void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}// 时间复杂度:O(N^2) 什么情况最坏:逆序
// 最好:顺序有序,O(N)
// 插入排序
void InsertSort(int* a, int n)
{// [0, n-1]for (int i = 0; i < n - 1; i++){// [0, n-2]是最后一组// [0,end]有序 end+1位置的值插入[0,end],保持有序int end = i;int tmp = a[end + 1];while (end >= 0){if (tmp < a[end]){a[end + 1] = a[end];--end;}else{break;}}a[end + 1] = tmp;}
}// O(N^1.3)
//void ShellSort(int* a, int n)
//{
// /*int gap = 3;
// for (int j = 0; j < gap; j++)
// {
// for (size_t i = j; i < n - gap; i += gap)
// {
// int end = i;
// int tmp = a[end + gap];
// while (end >= 0)
// {
// if (tmp < a[end])
// {
// a[end + gap] = a[end];
// end -= gap;
// }
// else
// {
// break;
// }
// }
// a[end + gap] = tmp;
// }
// }*/
//
// int gap = n;
// while (gap > 1)
// {
// // +1保证最后一个gap一定是1
// // gap > 1时是预排序
// // gap == 1时是插入排序
// gap = gap / 3 + 1;
//
// for (size_t i = 0; i < n - gap; ++i)
// {
// int end = i;
// int tmp = a[end + gap];
// while (end >= 0)
// {
// if (tmp < a[end])
// {
// a[end + gap] = a[end];
// end -= gap;
// }
// else
// {
// break;
// }
// }
// a[end + gap] = tmp;
// }
// //printf("gap:%2d->", gap);
// //PrintArray(a, n);
// }
//}// O(N ^ 1.3)
void ShellSort(int* a, int n)
{int gap = n;while (gap > 1){// +1保证最后一个gap一定是1// gap > 1时是预排序// gap == 1时是插入排序gap = gap / 3 + 1;for (size_t i = 0; i < n - gap; ++i){int end = i;int tmp = a[end + gap];while (end >= 0){if (tmp < a[end]){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = tmp;}}
}void AdjustDown(int* a, int n, int parent)
{// 先假设左孩子小int child = parent * 2 + 1;while (child < n) // child >= n说明孩子不存在,调整到叶子了{// 找出小的那个孩子if (child + 1 < n && a[child + 1] > a[child]){++child;}if (a[child] > a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}void HeapSort(int* a, int n)
{// 向下调整建堆 O(N)for (int i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDown(a, n, i);}// O(N*logN)int end = n - 1;while (end > 0){Swap(&a[0], &a[end]);AdjustDown(a, end, 0);--end;}
}// O(N^2) 最坏
// O(N) 最好
void BubbleSort(int* a, int n)
{for (int j = 0; j < n; j++){// 单趟int flag = 0;for (int i = 1; i < n - j; i++){if (a[i - 1] > a[i]){Swap(&a[i - 1], &a[i]);flag = 1;}}if (flag == 0){break;}}
}void SelectSort(int* a, int n)
{int begin = 0, end = n - 1;while (begin < end){int mini = begin, maxi = begin;for (int i = begin + 1; i <= end; ++i){if (a[i] > a[maxi]){maxi = i;}if (a[i] < a[mini]){mini = i;}}Swap(&a[begin], &a[mini]);Swap(&a[end], &a[maxi]);++begin;--end;}
}
相关文章:

排序(前篇)
1.排序的概念及其运用 2.插入排序的概念及实现 3.希尔排序的概念及实现 4.选择排序概念及实现 总代码(对比各个排序在大量的数据情况排序所化的时间): 1.排序的概念及其运用 1.1排序的概念 排序:所谓排序,就是使…...

Linux学习笔记(二)
一、Linux文件目录 1.命令:tree -L 1 2.挂载命令(例如U盘,需要挂载之后才能访问): mount /dev/cdrom /mnt ls /mnt 3.查看登录信息: last / lastlog 4.修改/查看网络信息 vi /etc/sysconfig/netw…...

Git——pull request详细教程
当我们需要协助其他仓库完成更改时,往往会用到git中的Pull Request操作,从而方便团队的协作管理和代码持续集成。 下面是详细的教程步骤。 一. Fork目标项目 比如说我现在要fork以下Qwen-VL的项目,如图所示: 随后点击Create即可…...

Prompt工程与实践
Prompt工程与实践 一、Prompt与大模型 1.1 大模型的定义 大模型本质上就是一个概率生成模型,该模型的模型参数足够大,并且在训练过程中阅读了非常多的各个领域的语料。这个时候,如果通过一个正确的、有效的指令去引导这个模型,…...

电脑录屏怎么录?7个电脑录屏软件免费版强势来袭,赶快收藏!
电脑录屏怎么录?相信很多小伙伴们都不知道怎么在Windows电脑上录屏吧?在当今社会,随着互联网的快速发展,越来越多的小伙伴们开始通过制作视频内容来分享知识、展示技能或者记录生活。电脑录屏成为了一种简单高效的方式,…...

ts: 映射类型
映射类型会携带接口的可选和只读属性 interface User { name: string; age?: number; readonly email: string; } interface User { name: string; age?: number; readonly email: string; } type change<T> {[P in keyof T]: T[P] } type obj change<…...

调出idea解决冲突界面
背景 我对idea使用不熟练,还是习惯用git bash来合并代码,合并爆冲突后,我进入idea准备解决冲突,却发现找不到解决冲突的界面。 解决 右击idea中冲突的文件,将鼠标移动到菜单栏的git上,此时应该出现包含有…...

android studio项目 gradle-xx-bin.zip下载失败或很慢的解决方法
一、环境: gradle官网:https://services.gradle.org/distributions/ IDE: android studio 二、下载并拷贝以下文件 下面已 gradle-8.0 为例,gradle缓存目录为(file->settings->Build,Execution…->Build Tools->Gradle查看Gradle…...

Python系列:教你使用PyMySQL操作MySQL数据库
Python系列 PyMySQL操作MySQL数据库 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite:http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csdn.net/qq_2855…...

mysql数据库管理面试题-1
1. 描述MySQL架构及其主要组件(例如存储引擎、缓存、优化器等)。 MySQL的架构可以分为三层: 客户端层:这是用户和MySQL数据库交互的接口,包括命令行工具、图形化管理工具等。 服务层:这是MySQL的核心部分…...

Linux安装zsh并配置oh-my-zsh
配置oh-my-zsh 查看当前shell安装zsh切换到zsh配置ohmysh 查看当前shell cat /etc/shells# /etc/shells: valid login shells /bin/sh /bin/bash /usr/bin/bash /bin/rbash /usr/bin/rbash /bin/dash /usr/bin/dash安装zsh sudo apt install zsh# /etc/shells: valid login s…...

echarts(6大基础图表)的使用
目录 一、vue2挂载 二、柱状图 2.1、基础柱状图介绍 2.2、标记:最大值\最小值(markPoint)、平均值(markLine) 2.3、显示:数值显示(label)、柱子宽度(barWidth)、横向柱状图 三、折线图 3.1、标记:最大值\最小值(markPoint)、平均值(ma…...

React hooks - forwardRef+useImperativeHandle
forwardRefuseImperativeHandle React.forwardRef用法useImperativeHandle用法第三个参数的用法 React.forwardRef与useImperativeHandle配合使用注意事项 React.forwardRef用法 1.创建一个 能够接受到ref属性的React 组件。 ref 用来获取实例,但函数组件不存在实例…...

MyBatis出现:SQLSyntaxErrorException: Unknown column ‘XXX‘ in ‘field list‘
<update id"updateStudent">update tb_students set stu_name${stuName},stu_gender${stuGender},stu_age${stuAge},stu_tel${stuTel}where stu_num ${stuNum}</update> 本质上来说,是Mybatis使用上的错误,不熟悉,理…...

代码随想录算法训练营Day54 | 392.判断子序列、115.不同的子序列 | Python | 个人记录向
本文目录 392.判断子序列做题看文章 115.不同的子序列做题看文章 以往忽略的知识点小结个人体会 392.判断子序列 代码随想录:392.判断子序列 Leetcode:392.判断子序列 做题 借鉴Day53中1143.最长公共子序列的思路,最后改一下判断逻辑即可。…...

利用oracle默认事务隔离级别(提交读)提升多表联查速度
利用oracle默认事务隔离级别(提交读)提升查询速度) 背景介绍: 数据量大查询缓慢,添加太多条件,使用IN走了全表查询导致查询速度缓慢。 解决方案: 版本一: 新建临时表,在查询是将数据插入到临时表中&#…...

B/S架构+java语言+Mysqladr数 据 库ADR药物不良反应监测系统源码 ADR药物不良反应监测系统有哪些作用?
B/S架构+java语言+Mysqladr数 据 库ADR药物不良反应监测系统源码 ADR药物不良反应监测系统有哪些作用? 药物不良反应(ADR)是指在合格药物以正常用量和用法用于预防、诊断、治疗疾病或调节生理功能时所发生的意外的、与防治目的无关的、不利或…...

Matlab中% note that Wilkinson notation (‘L1~L4~1‘) is used to specify the model
fitrm 函数的输入参数不正确,似乎出错的地方是在定义 fitrm 对象时使用了不正确的参数。 fitrm 函数的语法是这样的: rm fitrm(tbl, model, WithinDesign, withinDesign) 其中: - tbl 是一个表格,包含了待分析的数据。 - mod…...

测试测试测试
一分钟速览新闻点! 京东前副总裁蔡磊回应被指装病:没有时间、精力和能力应对 百度沈抖:主力模型免费的原因很朴素,希望大家别再天天拉表格比价格 蚂蚁集团CTO何征宇:蚂蚁一直在努力优化和提高AI的可靠性、经济性和易…...

动态规划专题
leecode 221 class Solution { public:int maximalSquare(vector<vector<char>>& matrix) {int n matrix.size();if (n 0) return 0; // 如果矩阵为空,则直接返回0 int m matrix[0].size();vector<vector<int>> ans(n, vector<i…...

.net8.0与halcon编程环境构建
1.安装vs2022 2.安装h-12.0.exe ,不要勾选复选框 3.vs2022新建wpf应用程序 4.依赖项添加项目应用,选择halcondotnet.dll 5.安装System.Drawing 安装 HalconDotNet 安装 Rti.HDevEngineDotNet 在工具箱 空白处右键 应用halcon.dll WPF控件也应用halcon.dll 6.xaml申明hal…...

文心智能体平台:快来创建你的Java学习小助理,全方位辅助学习
文章目录 一、文心智能体平台1.1平台介绍1.2智能体介绍 二、智能体创建三、体验与总结 一、文心智能体平台 文心智能体平台是百度推出的基于文心大模型的智能体(Agent)平台,支持广大开发者根据自身行业领域、应用场景,选取不同类…...

AppInventor2 表格布局的外面的黑框怎么去掉?
问:表格布局的外面的黑框怎么去掉啊? 答:这个黑框是界面设计的布局位置示意,实际 App 测试时并没有框。 来源:AppInventor2 表格布局的外面的黑框怎么去掉? - App应用开发 - 清泛IT社区,为创新…...

爬楼梯(进阶版)
思路: 没什么难的,就是一个排序的01背包问题,秒了 #include<bits/stdc.h> using namespace std;int n,m; int main(){cin>>n>>m;vector<int>dp(2000,0);dp[0]1;for(int i0;i<n;i){for(int j1;j<m;j){if(i>…...

echarts-事件
echarts部分事件 添加点击事件 添加点击事件: let options {tooltip: {},xAxis: {type: "category",data: ["d1", "d2", "d3", "d4"],},yAxis: {},series: [{type: "line",data: d1,},{type: &qu…...

备受推崇的公司文件加密文件推荐榜单
迄今为止,加密依然是最有效的用于保护数据、通讯安全的手段之一 在数字化时代,文件加密软件成为了保护个人和企业数据安全的重要工具。随着技术的不断进步,市场上涌现出了众多优秀的文件加密软件。 以下十款文件加密软件因其出色的性能、易…...

QT——QSlider实现,QT滑动控件的使用
目录 简介滑动块调节两种方法滑动条触发信号量理想滑动块运用(参考) 简介 QT中滑动条的控件叫QSlider,继承自QAbstractSlider类。 主要用途是通过滑块的滑动的方式在一定范围内调节某个值。根据调节的后得到的结果去执行一些处理,…...

【网络协议Http】Http中get,post,put,delete区别
Http协议 超文本传输协议(Hypertext Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上。 【参考】 GET && POST 对比 关于tcp数据包:对于GET方式的请求,浏览器会把http hea…...

软硬中断区别,磁盘块、扇区、页区别与之间的关系
软硬中断: 软中断是执行中断指令产生的,而硬中断是由外设引发的。 硬中断的中断号是由中断控制器提供的,软中断的中断号由指令直接指出,无需使用中断控制器。 硬中断是可屏蔽的,软中断不可屏蔽。 硬中断处理程序要…...

在线思维导图编辑!3个AI思维导图生成软件推荐!
思维导图,一种以创新为驱动的视觉化思考工具,已经渗透到我们日常生活和工作的各个角落。当我们需要整理思绪、规划项目或者梳理信息时,思维导图总能提供极大的帮助。 近些年随着云服务等基础设施的完善,我们可以看到越来越多提供…...