当前位置: 首页 > news >正文

FlutterUnit 周边 | 收录排序算法可视化


theme: cyanosis

1. FlutterUnit 更新:排序算法可视化

排序算法可视化是用视图层表现出算法执行过程中排序的过程,感谢 编程的平行世界 在 《十几种排序算法的可视化效果,快来看看!👀》》 一文中提供的算法支持。我进行了一些代码和交互上的优化,将其集成到了 FlutterUnit 中,大家可以在 release v2.9.3 下载全平台应用查看体验 ~

image.png


掘金目前已经支持插入视频了,但目前支持西瓜视频。下面通过两个视频来看一下效果:

移动端: 交互视频

jvideo

桌面端: 交互视频

jvideo


2. 交互界面介绍

在移动端,排序算法可视化被放在 知识/可视排序 页签下,左上角的绿色按钮点击后启动排序,从而驱动数字列表数据变化,更新主界面产生排序的动态效果。下拉可以展开排序算法列表,选择对应的算法进行排序:

| 标题 | | | --- | --- | | 0240f2d1ca9b3778246fa722d1382bc.jpg | 433d6cab476b3dbc51ac22ed58b6854.jpg |

在桌面端,排序算法可视化先放在 可视排序 侧栏导航下,以后可能会拓展其他的有趣案例。

image.png

另外排序有设置界面,可以设置配置参数。

个数表示数据的数量,每个数据对应主界面中的一个线条。
间隔时长是排序过程中每步间的停顿时间,单位是微秒。
随机种子是随机数的种子,不为 -1 的话,相同的种子,每次重置生成的随机数列表一致。便于比较不排序算法下,同一组数据表现。

image.png


3. 项目的结构

这里核心代码新建了一个 algorithm 的包来单独维护,其中 algorithm/sort 文件夹中盛放排序的具体算法。把它们分文件放置,更便于阅读。

image.png


algorithm/data_scope 中,用于维护界面中的数据;在 algorithm/views 中处理视图组件的构建。

image.png


最后在 pubspec.yaml 中通过 path 引入本地的包,就可以在主项目中使用 algorithm 包中的组件进行展示。比如下面,在侧栏导航中添加一个 可视排序 的菜单栏,对应 DeskSortPage 组件。

yaml algorithm: path: packages/algorithm

image.png


4. 代码实现细节:算法方面

可视化排序的思路是:在每次排序列表数据发生变化时,通过回调来通知处理。这里定义 SortCallback 类型方便维护,其返回 Future<void> 对象,可以回调排序过程中此时的列表数据。

dart typedef SortCallback = Future<void> Function(List<int> dist);

拿冒泡排序来说,定义 bubbleSort 函数,传入待排序的数字列表,每次循环完成,出发 callback 通知调用者。比如想要放慢排序的过程,每一步可以等待一定的时间,也就是设置中的间隔微秒数。

dart ///冒泡排序 Future<void> bubbleSort(List<int> src, SortCallback callback) async{ //控制需要进行排序的次数。每一轮循环都会确定一个数字的最终位置。 for (int i = 0; i < src.length; ++i) { //遍历当前未排序的元素,通过相邻的元素比较并交换位置来完成排序。 for (int j = 0; j < src.length - i - 1; ++j) { //如果 _numbers[j] 大于 _numbers[j + 1],则交换它们的位置,确保较大的元素移到右边。 if (src[j] > src[j + 1]) { int temp = src[j]; src[j] = src[j + 1]; src[j + 1] = temp; } //实现一个延迟,以便在ui上展示排序的动画效果 await callback(src); } } }

另外排序的函数结构都是一致的,输入待排序列表与回调,可以通过 typedef 定义一个排序函数类型 SortFunction

dart typedef SortFunction = Future<void> Function(List<int> src, SortCallback callback);

这样就可以维护排序的名称和排序函数间的映射关系:

dart Map<String, SortFunction> sortFunctionMap = { 'insertion': insertionSort, 'bubble': bubbleSort, 'cocktail': cocktailSort, 'comb': combSort, 'pigeonHole': pigeonHoleSort, 'shell': shellSort, 'selection': selectionSort, 'gnome': gnomeSort, 'cycle': cycleSort, 'heap': heapSort, 'quick': quickSort, 'merge': mergeSort, };


5. 代码实现细节:数据方面

数据方面的代码在 data_scope 包中,这里排序界面中的数据有三大类:

其一是待排序数字列表。
其二是配置的参数。
其三是排序状态。

配置参数包括四个,通过 SortConfig 类维护:

```dart class SortConfig { final int count; // 列表数字数量 final int seed; // 随机数种子 final Duration duration; // 间隔时长 final String name; // 算法名称

SortConfig({ this.count = 100, this.duration = const Duration(microseconds: 1500), this.seed = -1, this.name = 'insertion', });

SortConfig copyWith({ int? count, int? seed, Duration? duration, String? name, }) => SortConfig( count:count??this.count, seed:seed??this.seed, duration:duration??this.duration, name:name??this.name, ); } ```


排序状态通过 SortStatus 枚举定义:

dart enum SortStatus{ none, // 未操作 sorting, // 排序中 sorted, // 排序完成 }


排序界面整体的数据状态通过 SortState 维护,它继承自 ChangeNotifier,可以在数据变化时调用 notifyListeners 通知监听者,从而实现界面的更新。 SortState 调用 sort 方法触发排序,会根据排序算法名,从 sortFunctionMap 中拿到排序算法调用。每次回调时触发 notifyListeners 方法通知更新。

```dart class SortState with ChangeNotifier{

SortState(){ reset(); }

SortStatus status = SortStatus.none; List data = []; SortConfig _config = SortConfig(); SortConfig get config => _config; Random random = Random();

set config(SortConfig config){ _config = config; reset(); notifyListeners(); }

void reset(){ data.clear(); status = SortStatus.none; notifyListeners(); int count = config.count; if(config.seed!=-1){ random = Random(config.seed); } for (int i = 0; i < count; i++) { data.add(random.nextInt(1000)); } }

void sort() async{ status = SortStatus.sorting; notifyListeners(); SortFunction? sortFunction = sortFunctionMap[config.name]; if(sortFunction!=null){ await sortFunction(data,(arr) async { await Future.delayed(config.duration); notifyListeners(); }); } status = SortStatus.sorted; notifyListeners(); } } ```


6. 代码实现细节:界面方面

这里目前没有使用三方状态管理包,而是通过 Flutter 内部的 InheritedNotifier 完成子树共享可监听数据的任务。

```dart class SortStateScope extends InheritedNotifier { const SortStateScope({ required super.notifier, required super.child, super.key, });

static SortState of(BuildContext context) => context.dependOnInheritedWidgetOfExactType ()!.notifier!; } ```

如果某个组件是数据的依赖者,在可监听对象发生变化时,会通知其更新。拿 SortButton 来说,他需要依赖排序状态 SortStatus 数据来展示不同的图标,或响应不同的事件。使用 SortStateScope.of(context) 相当于依赖了数据,那么数据(SortState)在 notifyListeners 时,就会通知 SortButton 进行重新构建,这就是 InheritedNotifier 组件的功能。

| none | sorting |sorted | | --- | --- |--- | | image.png | image.png |image.png

```dart class SortButton extends StatelessWidget { const SortButton({super.key});

@override Widget build(BuildContext context) { SortState state = SortStateScope.of(context); VoidCallback? action; IconData icon; Color color; switch (state.status) { case SortStatus.none: icon = Icons.notstartedoutlined; color = Colors.green; action = state.sort; break; case SortStatus.sorting: icon = Icons.stopcircleoutlined; color = Colors.grey; action = null; break; case SortStatus.sorted: icon = Icons.refresh; color = Colors.black; action = state.reset; break; }

return GestureDetector(onTap: action,child: Icon(icon,color: color,),
);

} } ```


最后,主体界面通过 CustomPainter 对数字列表进行绘制,遍历数据根据数值大小绘制不同高度的线条。

```dart class DataPainter extends CustomPainter { final List data;

DataPainter({required this.data});

@override void paint(Canvas canvas, Size size) { canvas.clipRect(Offset.zero & size); double itemWidth = size.width / data.length;

Paint paint = Paint();
paint.strokeWidth = itemWidth;
paint.strokeCap = StrokeCap.round;for (int i = 0; i < data.length; i++) {int value = data[i];if (value < 1000 * .10) {paint.color = Colors.blue.shade100;} else if (value < 1000 * .20) {paint.color = Colors.blue.shade200;} else if (value < 1000 * .30) {paint.color = Colors.blue.shade300;} else if (value < 1000 * .40) {paint.color = Colors.blue.shade400;} else if (value < 1000 * .50) {paint.color = Colors.blue.shade500;} else if (value < 1000 * .60) {paint.color = Colors.blue.shade600;} else if (value < 1000 * .70) {paint.color = Colors.blue.shade700;} else if (value < 1000 * .80) {paint.color = Colors.blue.shade800;} else if (value < 1000 * .90) {paint.color = Colors.blue.shade900;} else {paint.color = const Color(0xFF011E51);}canvas.drawLine(Offset(i * itemWidth + itemWidth / 2, 0),Offset(i * itemWidth + itemWidth / 2,size.height * (value / 1000),),paint);
}

}

@override bool shouldRepaint(covariant DataPainter oldDelegate) { return true; } } ```

整个核心的逻辑就是这些,有兴趣的可以自己查阅 FlutterUnit 中的相关代码,那么本文就到这里,谢谢观看,我们下次再见~

相关文章:

FlutterUnit 周边 | 收录排序算法可视化

theme: cyanosis 1. FlutterUnit 更新&#xff1a;排序算法可视化 排序算法可视化是用视图层表现出算法执行过程中排序的过程&#xff0c;感谢 编程的平行世界 在 《十几种排序算法的可视化效果&#xff0c;快来看看&#xff01;&#x1f440;》》 一文中提供的算法支持。我进行…...

代码随想录Day30 贪心05 LeetCode T435无重叠区间 T763划分字母区间 T56 合并区间

LeetCode T435 无重叠区间 题目链接:435. 无重叠区间 - 力扣&#xff08;LeetCode&#xff09; 题目思路: 这题思路和昨天的打气球类似,我们需要按照左区间或者右区间进行排序,然后哦判断第i个区间的左端点和第i-1个区间的右端点的大小关系,,如果大于等于,那么就无需操作,一旦…...

发展高质量存储力,中国高科技力量聚浪成潮

中国信息通信研究院指出&#xff0c;在全球数字化转型与产业变革的浪潮下&#xff0c;算力正在成为改变全球竞争格局的关键力量。而根据最新的《算力基础设施高质量发展行动计划》&#xff0c;算力是集信息计算力、数据存储力和网络运载力于一体的新型生产力。当前&#xff0c;…...

修改svc的LoadBalancer的IP引发的惨案

文章目录 背景修改externalIPs的操作api-server报错日志挽救教训 背景 k8s集群没有接外部负载均衡&#xff0c;部署istio的时候ingressgateway一直pending。 于是手动修改了这个lb svc的externalIP&#xff0c;于是k8s就崩了&#xff0c;如何崩的&#xff0c;且听我还道来。 …...

2520. 统计能整除数字的位数

2520. 统计能整除数字的位数 class Solution {public int countDigits(int num) {int res 0;int o num;while (num > 0) {if (o % (num % 10) 0) {res 1;}num num / 10;}return res;} }...

BeanUtils.copyProperties的用法

常见场景 我们如果有两个具有很多相同属性名的JavaBean对象a和b&#xff0c;想把a中的属性赋值到b&#xff0c;例如 接口中将接收到的前端请求参数XxxReqVo,我们想把这个入参转化为XxxQuery对象作为数据库的查询条件对象 传统做法是手动set&#xff0c;即 XxxQuery xxxQuer…...

【RabbitMQ 实战】12 镜像队列

一、镜像队列的概念 RabbitMQ的镜像队列是将消息副本存储在一组节点上&#xff0c;以提高可用性和可靠性。镜像队列将队列中的消息复制到一个或多个其他节点上&#xff0c;并使这些节点上的队列保持同步。当一个节点失败时&#xff0c;其他节点上的队列不受影响&#xff0c;因…...

PyCharm社区版安装

PyCharm社区版安装 到中国官网下载 https://www.jetbrains.com/zh-cn/pycharm/download/?sectionwindows 首次创建项目&#xff0c;会自动下载安装Python 3.9 社区版的区别 社区版的区别...

【LeetCode每日一题合集】2023.10.16-2023.10.22(只出现一次的数字Ⅲ)

文章目录 260. 只出现一次的数字 III⭐&#xff08;异或&#xff09;&#x1f402;2652. 倍数求和解法1——枚举模拟解法2—— O ( 1 ) O(1) O(1)容斥原理相似题目——1201. 丑数 III&#xff08;二分查找容斥原理&#xff09; 2530. 执行 K 次操作后的最大分数解法1——贪心优…...

尚硅谷大数据项目《在线教育之实时数仓》笔记003

视频地址&#xff1a;尚硅谷大数据项目《在线教育之实时数仓》_哔哩哔哩_bilibili 目录 第7章 数仓开发之ODS层 P015 第8章 数仓开发之DIM层 P016 P017 P018 P019 01、node001节点Linux命令 02、KafkaUtil.java 03、DimSinkApp.java P020 P021 P022 P023 第7章 数…...

【Linux】部署单体项目以及前后端分离项目(项目部署)

一、简介 以下就是Linux部署单机项目和前后端分离项目的优缺点&#xff0c;希望对你有所帮助。 1、Linux部署单机项目&#xff1a; 优点&#xff1a; 1.简化了系统管理&#xff1a;由于所有服务都在同一台机器上运行&#xff0c;因此可以简化系统管理和维护。 2.提高了性能&a…...

设计模式之门面模式

前言 什么是门面模式 门面模式是一种结构型设计模式&#xff0c;它提供了一个统一的接口&#xff0c;用来访问子系统中的一群接口。它定义了一个高层接口&#xff0c;让子系统更容易使用。这种模式常用于将一个复杂的子系统封装成一个简单的接口&#xff0c;使得客户端可以方…...

Postman的使用

Postman的使用 Postman断言Postman常用断言1、断言响应状态码2、断言包含某个字符串3、断言JSON数据4、Postman断言工作原理 Postman关联Postman自动关联创建环境 3、Postman参数化CSV文件JSON文件1、用例集的导入导出2、环境导出 Postman断言 让Postman工具代替人自动判断预期…...

QGIS008:QGIS拓扑检查、修改及验证

摘要&#xff1a;本文介绍使用QGIS拓扑检查器和几何图形检查器检查图层的拓扑错误&#xff0c;修改拓扑错误&#xff0c;并对修改后的图层进行错误验证。 实验数据&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1Vy2s-KYS-XJevqHNdavv9A?pwdf06o 提取码&#xff1a…...

安装DBD-Oracle报错处理

cd DBD-Oracle-1.83 perl Makefile.PL make && make install make编译报错如下&#xff1a; /bin/ld: 找不到 -lnsl collect2: 错误&#xff1a;ld 返回 1 make: *** [Makefile:524&#xff1a;blib/arch/auto/DBD/Oracle/Oracle.so] 错误 1 [rootlocalhost DBD-Ora…...

【机器学习】KNN算法-鸢尾花种类预测

KNN算法-鸢尾花种类预测 文章目录 KNN算法-鸢尾花种类预测1. 数据集介绍2. KNN优缺点&#xff1a; K最近邻&#xff08;K-Nearest Neighbors&#xff0c;KNN&#xff09;算法是一种用于模式识别和分类的简单但强大的机器学习算法。它的工作原理非常直观&#xff1a;给定一个新数…...

LuatOS-SOC接口文档(air780E)--lora - lora驱动模块

常量 常量 类型 解释 lora.SLEEP number SLEEP模式 lora.STANDBY number STANDBY模式 lora.init(ic, loraconfig,spiconfig) lora初始化 参数 传入值类型 解释 string lora 型号&#xff0c;当前支持&#xff1a; llcc68 sx1268 table lora配置参数,与具体设备…...

Compose 自定义 - 绘制 Draw

一、概念 所有的绘制操作都是通过调整像素大小来执行的。若要确保项目在不同的设备密度和屏幕尺寸上都能采用一致的尺寸&#xff0c;请务必使用 .toPx() 对 dp 进行转换或者采用小数尺寸。 二、Modifier 修饰符绘制 官方页面 在修饰的可组合项之上或之下绘制。 .drawWithCon…...

c#学习相关系列之构造函数

目录 一、构造函数的作用 二、构造函数的特征 三、三种构造函数介绍 1、实例构造函数 2、静态构造函数 3、私有构造函数 一、构造函数的作用 构造函数用来创建对象&#xff0c;并且可以在构造函数中对此对象进行初始化。构造函数具有与类相同的名称&#xff0c;它通常用来…...

CS224W1.3——图表示的选择

文章目录 1. 图网络构成2. 选择一个合适的表示3. 图结构实例3.1 二部图3.2 图的表示 4. 节点和边的属性 这小节主要讲图表示的选择。 1. 图网络构成 对于每个实体&#xff0c;我们创建节点 N N N&#xff0c;对于每个关系&#xff0c;我们创建边 E E E&#xff0c;对于整体而言…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作&#xff0c;无需更改相机配置。但是&#xff0c;一…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...

ip子接口配置及删除

配置永久生效的子接口&#xff0c;2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

JAVA后端开发——多租户

数据隔离是多租户系统中的核心概念&#xff0c;确保一个租户&#xff08;在这个系统中可能是一个公司或一个独立的客户&#xff09;的数据对其他租户是不可见的。在 RuoYi 框架&#xff08;您当前项目所使用的基础框架&#xff09;中&#xff0c;这通常是通过在数据表中增加一个…...

网页端 js 读取发票里的二维码信息(图片和PDF格式)

起因 为了实现在报销流程中&#xff0c;发票不能重用的限制&#xff0c;发票上传后&#xff0c;希望能读出发票号&#xff0c;并记录发票号已用&#xff0c;下次不再可用于报销。 基于上面的需求&#xff0c;研究了OCR 的方式和读PDF的方式&#xff0c;实际是可行的&#xff…...

背包问题双雄:01 背包与完全背包详解(Java 实现)

一、背包问题概述 背包问题是动态规划领域的经典问题&#xff0c;其核心在于如何在有限容量的背包中选择物品&#xff0c;使得总价值最大化。根据物品选择规则的不同&#xff0c;主要分为两类&#xff1a; 01 背包&#xff1a;每件物品最多选 1 次&#xff08;选或不选&#…...