排序算法可视化

前言

这两天在B站上刷到一个视频,用python把各种排序动画可视化显示了出来觉得还蛮好玩的,当即就决定用Flutter写一个玩玩,顺便复习一下排序算法,话不多说,进入正文~

效果图:

该效果图为鸡尾酒排序(双向冒泡排序)的演示

在线体验地址:https://dartpad.dev/?id=cb52d49828a2e2f879353458bbb7b80f

单击run之后等待一会~

代码下载:https://www.aliyundrive.com/s/1TcPCBhSWyW

布局绘制

想看算法实现的朋友请直接跳至排序算法实现一节

顶部

选择使用什么排序算法,通过一个PopupMenuButton弹出菜单来实现。

appBar: AppBar(title: Text("当前选择的是:${getTitle()}",style: const TextStyle(fontSize: 14),),actions: <Widget>[PopupMenuButton(initialValue: currentSort,itemBuilder: (ctx) {return const [PopupMenuItem(value: 'bubble',child: Text("Bubble Sort"),),...];})],
),
排序数组渲染

在这里使用StreamBuilder,用于实时接收到排序数组的变化。当排序算法进行中,数组发生变化时,StreamBuilder 会自动重新构建,并更新 UI。这样可以实现动态的排序过程,让我们可以看到每一步的变化。

body: StreamBuilder<Object>(initialData: numbers,stream: streamController.stream,builder: (context, snapshot) {List<int> numbers = snapshot.data as List<int>;int counter = 0;return Row(children: numbers.map((int num) {通过更新counter,标记要渲染数组中的索引counter++;return CustomPaint(painter: BarPainter(width: MediaQuery.of(context).size.width / sampleSize,value: num,index: counter,),);}).toList(),);},
),
绘制线段

通过CustomPainter对排序数组中的值进行渲染。

class BarPainter extends CustomPainter {//宽度final double width;//高度(数组中对应的值)final int value;//位置索引final int index;BarPainter({required this.width, required this.value, required this.index});@overridevoid paint(Canvas canvas, Size size) {Paint paint = Paint();if (value < 500 * .10) {paint.color = Colors.blue.shade100;} else if (value < 500 * .20) {paint.color = Colors.blue.shade200;} else if (value < 500 * .30) {paint.color = Colors.blue.shade300;} else if (value < 500 * .40) {paint.color = Colors.blue.shade400;} else if (value < 500 * .50) {paint.color = Colors.blue.shade500;} else if (value < 500 * .60) {paint.color = Colors.blue.shade600;} else if (value < 500 * .70) {paint.color = Colors.blue.shade700;} else if (value < 500 * .80) {paint.color = Colors.blue.shade800;} else if (value < 500 * .90) {paint.color = Colors.blue.shade900;} else {paint.color = const Color(0xFF011E51);}paint.strokeWidth = width;paint.strokeCap = StrokeCap.round;//绘制线段canvas.drawLine(Offset(index * width, 0),Offset(index * width,//将一个数字向上取整并转换为双精度浮点数value.ceilToDouble(),),paint);}@overridebool shouldRepaint(covariant CustomPainter oldDelegate) {return true;}
}
底部控制

借助ScaffoldbottomNavigationBar快速实现需求(偷懒下🤣)

bottomNavigationBar: BottomAppBar(child: Row(mainAxisAlignment: MainAxisAlignment.spaceAround,children: <Widget>[ElevatedButton(onPressed: isSorting ? null : () {}, child: const Text("重置")),ElevatedButton(onPressed: isSorting ? null : sort, child: const Text("开始排序")),ElevatedButton(onPressed: isSorting ? null : changeSpeed,child: Text("${speed + 1}x",style: const TextStyle(fontSize: 20),),),],),
),
初始化数组
reset() {//用于判断是否排序isSorted = false;numbers = [];//往numbers中随机添加sampleSize个值for (int i = 0; i < sampleSize; ++i) {numbers.add(Random().nextInt(500));}streamController.add(numbers);
}

这样就实现了对界面的渲染布局~

其他关联操作

对动画时间的控制

定义一个速度等级,在定义默认的动画时间,根据动画更新的速度计算动画时间。

//排序动画更新的速度
int speed = 0;static int duration = 1500;///动画时间
changeSpeed() {//加到4级后重置为1级(默认从1级开始)if (speed >= 3) {speed = 0;duration = 1500;} else {speed++;//每次缩短一半时间duration = duration ~/ 2;}setState(() {});
}
计算算法运行时长

借助Stopwatch计时器实现。

Stopwatch stopwatch = Stopwatch()..start();
。。。开始排序//排序结束,定时器停止
stopwatch.stop();//打印排序时间
print("Sorting completed in ${stopwatch.elapsed.inMilliseconds} ms");

排序算法实现

冒泡排序

冒泡排序的主要思路是通过多次遍历数组,比较相邻元素的大小并交换位置,使得逐渐大的元素移动到数组的右侧。每一轮循环都会确定一个数字的最终位置,因为较大的元素会逐渐“冒泡”到数组的末尾。

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

该排序算法其主要思想是在排序过程中,首先从左往右逐个比较并交换相邻元素,直到最后一个元素。然后再从右往左逐个比较并交换相邻元素,直到第一个元素。这样可以确保每一轮排序后都能找到当前未排序部分的最大值和最小值。

///鸡尾酒排序(双向冒泡排序)
cocktailSort() async {bool swapped = true; // 表示是否进行了交换int start = 0; // 当前未排序部分的起始位置int end = numbers.length; // 当前未排序部分的结束位置// 开始排序循环,只有当没有进行交换时才会退出循环while (swapped == true) {swapped = false;// 从左往右遍历需要排序的部分for (int i = start; i < end - 1; ++i) {// 对每两个相邻元素进行比较if (numbers[i] > numbers[i + 1]) {// 如果前面的元素大于后面的元素,则交换它们的位置int temp = numbers[i];numbers[i] = numbers[i + 1];numbers[i + 1] = temp;swapped = true; // 进行了交换}await Future.delayed(getDuration());streamController.add(numbers);}// 如果没有进行交换,则说明已经排好序,退出循环if (swapped == false) break;// 重设为false,准备进行下一轮排序swapped = false; // 将end设置为上一轮排序的最后一个元素的位置end = end - 1; // 从右往左遍历需要排序的部分for (int i = end - 1; i >= start; i--) {// 对每两个相邻元素进行比较if (numbers[i] > numbers[i + 1]) {// 如果前面的元素大于后面的元素,则交换它们的位置int temp = numbers[i];numbers[i] = numbers[i + 1];numbers[i + 1] = temp;swapped = true; // 进行了交换}await Future.delayed(getDuration());streamController.add(numbers);}// 将start向右移一位,准备下一轮排序start = start + 1; }
}
梳排序(一种改进的冒泡排序算法)

梳排序是一种改进的冒泡排序算法,通过逐渐减小间隔来将最大的元素逐步归位。算法中的关键部分在于确定合理的间隔值和交换操作。

///梳排序(Comb Sort)
combSort() async {int gap = numbers.length;bool swapped = true;// 当间隔不为1或存在交换时执行循环while (gap != 1 || swapped == true) {// 通过缩小间隔来逐步将元素归位gap = getNextGap(gap);swapped = false;for (int i = 0; i < numbers.length - gap; i++) {// 如果当前元素大于间隔位置上的元素,则交换它们的位置if (numbers[i] > numbers[i + gap]) {int temp = numbers[i];numbers[i] = numbers[i + gap];numbers[i + gap] = temp;swapped = true;}await Future.delayed(getDuration());streamController.add(numbers);}}
}int getNextGap(int gap) {// 根据当前间隔值计算下一个间隔值gap = (gap * 10) ~/ 13;if (gap < 1) return 1;return gap;
}
鸽巢排序

鸽巢排序是一种比较简单的排序算法,它的基本思想是将待排序的数组中的数字分配到一个或多个“鸽巢”中,然后再从鸽巢中按照一定的顺序取出数字,将它们重新放回到数组中,最终得到一个有序的数组。

///鸽巢排序
pigeonHole() async {int min = numbers[0];int max = numbers[0];int range, i, j, index;// 找到数组中的最大值和最小值for (int a = 0; a < numbers.length; a++) {if (numbers[a] > max) max = numbers[a];if (numbers[a] < min) min = numbers[a];}// 计算鸽巢的个数range = max - min + 1;List<int> p = List.generate(range, (i) => 0);// 将数字分配到各个鸽巢中for (i = 0; i < numbers.length; i++) {p[numbers[i] - min]++;}index = 0;// 将鸽巢中的数字取出,重新放回到数组中for (j = 0; j < range; j++) {while (p[j]-- > 0) {numbers[index++] = j + min;await Future.delayed(getDuration());streamController.add(numbers);}}
}
希尔排序

希尔排序是一种改进的插入排序算法,它通过将待排序的数组按照一定的间隔(称为gap)拆分成若干个子序列,对每个子序列进行插入排序,然后逐渐缩小gap值,最终完成整个数组的排序。

///希尔排序shellSort() async {//定义变量 gap 并初始化为数组长度的一半。每次循环完成后将 gap 减半直到等于 0。for (int gap = numbers.length ~/ 2; gap > 0; gap ~/= 2) {//遍历每个子序列并进行插入排序。初始时从第一个子序列的第二个元素开始,即 i = gap,以 gap 为步长逐个遍历每个子序列。for (int i = gap; i < numbers.length; i += 1) {//将当前遍历到的元素赋值给它int temp = numbers[i];//内部使用一个 for 循环来实现插入排序。//循环开始时定义变量 j 并将其初始化为当前遍历到的元素的下标。通过不断比较前后相隔 gap 的元素大小并交换位置,将当前元素插入到正确的位置。int j;for (j = i; j >= gap && numbers[j - gap] > temp; j -= gap) {numbers[j] = numbers[j - gap];}numbers[j] = temp;await Future.delayed(getDuration());streamController.add(numbers);}}}
选择排序

选择排序是一种简单直观的排序算法,它每次在未排序部分中选择最小(或最大)的元素,然后将其与未排序部分的第一个元素进行交换,从而逐步形成有序序列。

///选择排序
selectionSort() async {for (int i = 0; i < numbers.length; i++) {for (int j = i + 1; j < numbers.length; j++) {// 遍历未排序部分,内层循环控制变量 jif (numbers[i] > numbers[j]) {// 判断当前元素是否比后续元素小int temp = numbers[j];// 交换当前元素和后续较小的元素numbers[j] = numbers[i];numbers[i] = temp;}await Future.delayed(getDuration(), () {});streamController.add(numbers);}}
}
循环排序

循环排序是一种稳定的排序算法,它通过不断将数组中的每个元素放置到其正确的位置上来完成排序。在整个排序过程中,会根据每个元素的值找到它应该位于的位置,并进行适当的交换。

///循环排序
cycleSort() async {int writes = 0;for (int cycleStart = 0; cycleStart <= numbers.length - 2; cycleStart++) {int item = numbers[cycleStart];int pos = cycleStart;// 在未排序部分中寻找比当前元素小的元素个数for (int i = cycleStart + 1; i < numbers.length; i++) {if (numbers[i] < item) pos++;}// 如果当前元素已经在正确位置上,则跳过此次迭代if (pos == cycleStart) {continue;}// 将当前元素放置到正确的位置上,并记录写操作次数while (item == numbers[pos]) {pos += 1;}if (pos != cycleStart) {int temp = item;item = numbers[pos];numbers[pos] = temp;writes++;}// 循环将位于当前位置的元素放置到正确的位置上while (pos != cycleStart) {pos = cycleStart;// 继续在未排序部分中寻找比当前元素小的元素个数for (int i = cycleStart + 1; i < numbers.length; i++) {if (numbers[i] < item) pos += 1;}// 将当前元素放置到正确的位置上,并记录写操作次数while (item == numbers[pos]) {pos += 1;}if (item != numbers[pos]) {int temp = item;item = numbers[pos];numbers[pos] = temp;writes++;}await Future.delayed(getDuration());streamController.add(numbers);}}
}
堆排序

堆排序是一种高效的排序算法,它利用了堆的数据结构来完成排序过程。堆是一种特殊的二叉树,其中每个父节点的值都大于或等于其子节点的值(称为最大堆)。

///堆排序
heapSort() async {// 从最后一个非叶子节点开始,构建最大堆for (int i = numbers.length ~/ 2; i >= 0; i--) {await heapify(numbers, numbers.length, i);streamController.add(numbers);}// 依次取出最大堆的根节点(最大值),并进行堆化for (int i = numbers.length - 1; i >= 0; i--) {int temp = numbers[0];numbers[0] = numbers[i];numbers[i] = temp;await heapify(numbers, i, 0);streamController.add(numbers);}
}heapify(List<int> arr, int n, int i) async {int largest = i;int l = 2 * i + 1; // 左子节点索引int r = 2 * i + 2; // 右子节点索引// 如果左子节点存在并且大于父节点,则更新最大值索引if (l < n && arr[l] > arr[largest]) largest = l;// 如果右子节点存在并且大于父节点或左子节点,则更新最大值索引if (r < n && arr[r] > arr[largest]) largest = r;// 如果最大值索引不等于当前节点索引,则交换节点值,并递归进行堆化if (largest != i) {int temp = numbers[i];numbers[i] = numbers[largest];numbers[largest] = temp;heapify(arr, n, largest);}await Future.delayed(getDuration());
}
插入排序

插入排序是一种简单直观的排序算法,它的基本思想是将数组分为已排序和未排序两个部分。在每一轮迭代中,从未排序部分选择一个元素,并将它插入到已排序部分的合适位置,以保证已排序部分仍然有序。

///插入排序
insertionSort() async {for (int i = 1; i < numbers.length; i++) {int temp = numbers[i]; // 将当前元素存储到临时变量 temp 中int j = i - 1; // j 表示已排序部分的最后一个元素的索引// 在已排序部分从后往前查找,找到合适位置插入当前元素while (j >= 0 && temp < numbers[j]) {numbers[j + 1] = numbers[j]; // 当前元素比已排序部分的元素小,将元素后移一位--j; // 向前遍历await Future.delayed(getDuration()); streamController.add(numbers); // 更新排序结果}numbers[j + 1] = temp; // 插入当前元素到已排序部分的正确位置await Future.delayed(getDuration(), () {});streamController.add(numbers); }
}
地精排序 (侏儒排序)

地精排序基本思想是通过不断比较相邻元素的大小,将小的元素“拍”到正确的位置,直到所有的元素都排好序。

///地精排序 (侏儒排序)
gnomeSort() async {int index = 0;while (index < numbers.length) {// 当 index 小于数组长度时执行循环if (index == 0) index++; if (numbers[index] >= numbers[index - 1]) {// 如果当前元素大于等于前面的元素,则将 index 加1index++;} else {// 否则,交换这两个元素,并将 index 减1(使得元素可以沉到正确位置)int temp = numbers[index];numbers[index] = numbers[index - 1];numbers[index - 1] = temp;index--;}await Future.delayed(getDuration()); streamController.add(numbers); }return;
}
奇偶排序(Odd-Even Sort)

奇偶排序算法(Odd-Even Sort)是一种简单的并行排序算法,它通过比较和交换数组中的相邻元素来实现排序。该算法适用于通过并行计算来加速排序过程的环境。

///奇偶排序(Odd-Even Sort)
oddEvenSort() async {bool isSorted = false;while (!isSorted) {// 当 isSorted 为 false 时执行循环isSorted = true; // 先假设数组已经排好序for (int i = 1; i <= numbers.length - 2; i = i + 2) {// 对奇数索引位置进行比较if (numbers[i] > numbers[i + 1]) {// 如果当前元素大于后面的元素,则交换它们的值int temp = numbers[i];numbers[i] = numbers[i + 1];numbers[i + 1] = temp;isSorted = false; // 若发生了交换,则说明数组仍未完全排序,将 isSorted 设为 falseawait Future.delayed(getDuration()); streamController.add(numbers); }}for (int i = 0; i <= numbers.length - 2; i = i + 2) {// 对偶数索引位置进行比较if (numbers[i] > numbers[i + 1]) {// 如果当前元素大于后面的元素,则交换它们的值int temp = numbers[i];numbers[i] = numbers[i + 1];numbers[i + 1] = temp;isSorted = false;await Future.delayed(getDuration());streamController.add(numbers);}}}return;
}
快速排序

快速排序是一种分治策略的排序算法,它通过将一个数组分成两个子数组,然后递归地对子数组进行排序,最终得到完全有序的数组。

///快速排序
quickSort(int leftIndex, int rightIndex) async {// 定义一个名为 _partition 的异步函数,用于划分数组,并返回划分后的基准元素的索引位置Future<int> _partition(int left, int right) async {// 选择中间位置的元素作为基准元素int p = (left + (right - left) / 2).toInt();// 交换基准元素和最右边的元素var temp = numbers[p];numbers[p] = numbers[right];numbers[right] = temp;await Future.delayed(getDuration());streamController.add(numbers);// 初始化游标 cursorint cursor = left;// 遍历数组并根据基准元素将元素交换到左侧或右侧for (int i = left; i < right; i++) {if (cf(numbers[i], numbers[right]) <= 0) {// 如果当前元素小于等于基准元素,则交换它和游标位置的元素var temp = numbers[i];numbers[i] = numbers[cursor];numbers[cursor] = temp;cursor++;await Future.delayed(getDuration());streamController.add(numbers);}}// 将基准元素放置在游标位置temp = numbers[right];numbers[right] = numbers[cursor];numbers[cursor] = temp;await Future.delayed(getDuration());streamController.add(numbers);return cursor;  // 返回基准元素的索引位置}// 如果左索引小于右索引,则递归地对数组进行快速排序if (leftIndex < rightIndex) {int p = await _partition(leftIndex, rightIndex);await quickSort(leftIndex, p - 1);  // 对基准元素左侧的子数组进行快速排序await quickSort(p + 1, rightIndex);  // 对基准元素右侧的子数组进行快速排序}
}// 比较函数,用于判断两个元素的大小关系
cf(int a, int b) {if (a < b) {return -1;  // 若 a 小于 b,则返回 -1} else if (a > b) {return 1;   // 若 a 大于 b,则返回 1} else {return 0;   // 若 a 等于 b,则返回 0}
}
归并排序

归并排序也是一种分治策略的排序算法,它将一个数组不断分成两个子数组,直到每个子数组只包含一个元素,然后再将两个有序的子数组合并成一个有序的数组。

///归并排序
mergeSort(int leftIndex, int rightIndex) async {// 定义一个名为 merge 的异步函数,用于合并两个有序子数组Future<void> merge(int leftIndex, int middleIndex, int rightIndex) async {// 计算左侧子数组和右侧子数组的大小int leftSize = middleIndex - leftIndex + 1;int rightSize = rightIndex - middleIndex;// 创建左侧子数组和右侧子数组List leftList = List.generate(leftSize, (index) => 0);List rightList = List.generate(rightSize, (index) => 0);// 将原始数组中的元素分别复制到左侧子数组和右侧子数组中for (int i = 0; i < leftSize; i++) {leftList[i] = numbers[leftIndex + i];}for (int j = 0; j < rightSize; j++) {rightList[j] = numbers[middleIndex + j + 1];}// 初始化游标和索引int i = 0, j = 0;int k = leftIndex;// 比较左侧子数组和右侧子数组的元素,并按顺序将较小的元素放入原始数组中while (i < leftSize && j < rightSize) {if (leftList[i] <= rightList[j]) {numbers[k] = leftList[i];i++;} else {numbers[k] = rightList[j];j++;}await Future.delayed(getDuration());streamController.add(numbers);k++;}// 将左侧子数组或右侧子数组中剩余的元素放入原始数组中while (i < leftSize) {numbers[k] = leftList[i];i++;k++;await Future.delayed(getDuration());streamController.add(numbers);}while (j < rightSize) {numbers[k] = rightList[j];j++;k++;await Future.delayed(getDuration());streamController.add(numbers);}}// 如果左索引小于右索引,则递归地对数组进行归并排序if (leftIndex < rightIndex) {// 计算中间索引位置int middleIndex = (rightIndex + leftIndex) ~/ 2;// 分别对左侧子数组和右侧子数组进行归并排序await mergeSort(leftIndex, middleIndex);await mergeSort(middleIndex + 1, rightIndex);await Future.delayed(getDuration());streamController.add(numbers);// 合并两个有序子数组await merge(leftIndex, middleIndex, rightIndex);}
}

都看到这里啦,给个赞吧~

关于我

Hello,我是Taxze,如果您觉得文章对您有价值,希望您能给我的文章点个❤️,有问题需要联系我的话:我在这里 。如果您觉得文章还差了那么点东西,也请通过关注督促我写出更好的文章~万一哪天我进步了呢?😝

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

K8s 概念及组件

K8s 的全称为Kubernetes&#xff0c;是一种开源的容器编排平台&#xff0c;用于自动化部署以及扩展和管理容器化的应用程序&#xff0c;它提供了一种容器编排和管理的方式&#xff0c;可以帮助开发人员更轻松的管理容器化的应用程序&#xff0c;并且提供了一种跨多个主机的自动…

【ELK 使用指南 3】Zookeeper、Kafka集群与Filebeat+Kafka+ELK架构(附部署实例)

EFLKK 一、Zookeeper1.1 简介1.2 zookeeper的作用1.3 Zookeeper的特点1.5 Zookeeper的数据结构1.6 Zookeeper的应用场景1.7 Zookeeper的选举机制&#xff08;重要&#xff09;1.7.1 第一次启动时1.7.2 非第一次启动时 二、Zookeeper集群部署2.1 安装前准备2.2 安装 ZookeeperSt…

用echarts在vue2中实现3d饼图

先看效果&#xff0c;再看文章&#xff1a; 一、安装插件 3d的图不仅用到echarts&#xff0c;还用到了echarts-gl&#xff0c;因此都需要安装一下哦~ npm install echarts npm install echarts-gl2.0.9 //可以指定版本&#xff0c;也可不指定二、在main.js中引入 import * …

Spring创建复杂对象

目录 一、什么是复杂对象 二、创建复杂对象的3种方式 2.1 实现FactoryBean接口 2.1.1 普通的创建方式 2.1.1 依赖注入的方式 2.1.3 FactoryBean的工作原理 2.2 实例工厂 2.3 静态工厂 一、什么是复杂对象 书接上回&#xff0c;我们已经分析了Spring是怎么去创建对象的了。那什…

springweb+vue前后端分离开发,集成部署

背景&#xff1a; 在自己做测试的时候&#xff0c;由于需要项目和项目的前端页面使用同样接口访问&#xff0c;所以需要将前端代码部署到后端项目下。前端采用vue&#xff0c;后端采用springboot。 首先时建立一个vue项目&#xff0c;这个可以参照网上的案例&#xff0c;创建方…

AI绘画使用Stable Diffusion(SDXL)绘制中国古代神兽

一、引言 说到神奇异兽&#xff0c;脑海中首先就会跳出我国古代神话传说中的各种神兽。比如青龙、白虎、朱雀、玄武&#xff0c;再比如麒麟、凤凰、毕方、饕餮等等&#xff0c;这些都是大家耳熟能详的的神兽。 这些神兽不仅体现了人们丰富的创造力和想象力&#xff0c;更是我…

一次说全COLA应用架构

一&#xff0c;为什么需要好的应用架构 上图比较清晰地说明了好的应用架构的作用——去繁为简&#xff0c;化无序为有序。 二&#xff0c;关于COLA的几种定义 1&#xff0c;原版 GitHub - alibaba/COLA: &#x1f964; COLA: Clean Object-oriented & Layered Architec…

【大数据】Kafka 数据存储

Kafka 数据存储 1.文件目录2.日志分段3.日志索引3.1 偏移量索引3.2 时间戳索引 4.日志清理4.1 日志删除4.1.1 基于时间4.1.2 基于日志大小4.1.3 基于日志起始偏移量 4.2 日志压缩 1.文件目录 Kafka 中的消息是存储在磁盘上的&#xff0c;一个分区副本对应一个 日志&#xff08…

异常数据检测 | Python基于Hampel的离群点检测

文章目录 文章概述模型描述源码分享文章概述 在时间序列数据分析领域,识别和处理异常点是至关重要的任务。异常点或离群点是明显偏离预期模式的数据点,可能表明存在错误、欺诈或有价值的见解。 应对这一挑战的一种有效技术是汉普尔过滤器(Hampel Filter)。 模型描述 汉…

OpenCV实现物体尺寸的测量

一 &#xff0c;项目分析 物体尺寸测量的思路是找一个确定尺寸的物体作为参照物&#xff0c;根据已知的计算未知物体尺寸。 如下图所示&#xff0c;绿色的板子尺寸为220*300&#xff08;单位&#xff1a;毫米&#xff09;&#xff0c;通过程序计算白色纸片的长度。 主要是通过…

2023区块链国赛有黑幕

2023全国职业院校技能大赛区块链技术应用赛项 有黑幕&#xff01;&#xff01;河北软件职业技术学院举行的全国职业院校技能大赛区块链技术应用赛项违反比赛公平原则&#xff1a; 1、在评分阶段居然允许企业人员进入裁判所在区域&#xff0c;偏向性的引导裁判评分&#xff0c…

小程序实现后台数据交互及WXS的使用

一&#xff0c;数据交互准备工作 1.1 后端准备 后端部分代码&#xff0c;可自行创建后端代码 package com.zking.minoa.wxcontroller;import com.zking.minoa.mapper.InfoMapper; import com.zking.minoa.model.Info; import com.zking.minoa.util.ResponseUtil; import org…

从入门到进阶 之 ElasticSearch 配置优化篇

&#x1f339; 以上分享从入门到进阶 之 ElasticSearch 配置优化篇&#xff0c;如有问题请指教写。&#x1f339;&#x1f339; 如你对技术也感兴趣&#xff0c;欢迎交流。&#x1f339;&#x1f339;&#x1f339; 如有需要&#xff0c;请&#x1f44d;点赞&#x1f496;收藏…

ant design vue Message 用法以及内容为 html片段情况

ant design vue 的 Message 用法 全局展示操作反馈信息 何时使用 # 可提供成功、警告和错误等反馈信息。顶部居中显示并自动消失&#xff0c;是一种不打断用户操作的轻量级提示方式。 全局配置&#xff1a; // main.ts// 进行全局配置 message.config({top: 0.7rem,//高度…

nvm管理不同版本nodejs

文章目录 nvm下载卸载本地node安装nvm安装nodejsnvm查看已安装版本nvm切换nodejs版本nvm删除nodejs版本 nvm下载 nvm github下载链接 nvm 1.1.7-setup.zip&#xff1a;安装版&#xff0c;推荐使用 卸载本地node 打开cmd where node 找到上面找到的路径&#xff0c;将node.…

【广州华锐互动】利用AR进行野外地质调查学习,培养学生实践能力

在科技发展的驱动下&#xff0c;AR&#xff08;增强现实&#xff09;技术已经在许多领域中找到了应用&#xff0c;包括医疗、教育、建筑和娱乐等。然而&#xff0c;有一个领域尚未充分利用AR技术的潜力&#xff0c;那就是野外地质调查。通过将AR技术引入到这个传统上需要大量人…

基于nodejs+vue中学信息技术线上学习系统

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

如何使用visual studio 2010构建SQLite3.lib文件

sqlite3官网只提供了dll&#xff0c;并没有lib文件。需要自己生成sqlite3.lib。因项目升级到x64&#xff0c;以前并没有生成64位的链接库&#xff0c;需要自己创建。本人电脑操作系统windows 10, 开发环境为visual studio 2010。下面是详细生成过程。 1. 从源下载源&#xff08…

低代码助力软件开发

低代码开发工具正在日益变得强大&#xff0c;它正不断弥合着前后端开发之间的差距。对于后端来说&#xff0c;基于低代码平台开发应用时&#xff0c;完全不用担心前端的打包、部署等问题&#xff0c;也不用学习各种框架&#xff08;Vue、React、Angular等等&#xff09;&#x…

深入探究音视频开源库 WebRTC 中 NetEQ 音频抗网络延时与抗丢包的实现机制

目录 1、引言 2、什么是NetEQ&#xff1f; 3、NetEQ技术详解 3.1、NetEQ概述 3.2、抖动消除技术 3.3、丢包补偿技术 3.4、NetEQ概要设计 3.5、NetEQ的命令机制 3.6、NetEQ的播放机制 3.7、MCU的控制机制 3.8、DSP的算法处理 3.9、DSP算法的模拟测试 4、NetEQ源文件…