排序算法可视化

前言

这两天在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.mzph.cn/news/108265.shtml

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

相关文章

OpenRemote: Java 开源 IoT 物联网开发平台,匹配智慧城市、智能家居、能源管理

OpenRemote 是一个直观、用户友好的基于Java语言的开源 IoT 物联网设备管理平台&#xff0c;它包括从连接设备到构建应用程序和特定领域的智能应用程序的所有功能和特性。通过OpenRemote物联网平台&#xff0c;用户可以收集和处理来自不同设备的传感器数据&#xff0c;适用于智…

Java Kids-百倍提速【Mac IOS】

引言&#xff1a;当今社会&#xff0c;创新和提升效率已经成为了大家普遍的追求。无论是个人生活还是企业经营&#xff0c;我们都希望能够以更高的效率完成任务&#xff0c;节省时间和资源。因此&#xff0c;提速成为了一种时代的要求&#xff0c;而"Java Kids 百倍提速&q…

C#进阶——反射(Reflection)

定义&#xff1a;反射指的是在运行时动态地获取、检查和操作程序中的类型信息&#xff0c;而在我们的Unity中反射允许开发者在运行时通过代码来访问和修改对象的属性、方法和字段&#xff0c;而不需要提前知道这些成员的具体信息。 举一个例子&#xff0c;我们使用反射在运行的…

c++ --- 归并排序

2、归并排序 步骤&#xff1a; 选取中间点 mid (LR)/2递归排序 左边left 和 右边 right归并 ---- 将数组合二为一 模板代码 int temp[10]; void merge_sort(int q[], int l, int r) {//如果左右边界相等 直接退出if (l > r) return;//获取数组中心int mid (l r) / 2;/…

Vue双向绑定原理

响应式原理 响应式是Vue的核心特性之一&#xff0c;数据驱动视图&#xff0c;我们修改数据视图随之响应更新&#xff0c;其原理是&#xff1a; ⚡️ 通过数据劫持结合发布和-订阅者模式的方式&#xff0c;通过拦截对数据的操作&#xff0c;在数据变动时发 布消息给订阅者&…

Element el-table 表格内容 样式错乱的问题

表格切换样式错乱展示 因为切换行的高度变化可能未异步渲染 解决方法&#xff1a; 在需要使用v-if渲染的el-table-column元素上加上一个不重复的key值即可解决问题 :key“Math.random()” <el-table-columnprop""label"问题"width"630.5px":…

day35

今日内容概要 Socket抽象层(socket编程) 基于TCP协议的借助socket可以编程客户端和服务端的程序 链接循环 通信循环 基于UDP协议的套接字(socket)编程 粘包现象 如何解决粘包现象(重要的是解决的思路) struct模块的使用(打包、解包) 今日内容详细 Socket抽象层&#x…

Selenium自动化测试总结

一、Selenium自动化测试&#xff08;基于python&#xff09; 1、Selenium简介&#xff1a; 1.1 Selenium是一款主要用于Web应用程序自动化测试的工具集合。Selenium测试直接运行在浏览器中&#xff0c;本质是通过驱动浏览器&#xff0c;模拟浏览器的操作&#xff0c;比如跳转、…

Qt 样式表大全整理

【QT】史上最全最详细的QSS样式表用法及用例说明_qt样式表使用大全_半醒半醉日复日&#xff0c;花落花开年复年的博客-CSDN博客 QT样式表的使用_qt 设置按下 release hover 按钮样式表_create_right的博客-CSDN博客 QPushButton {border-image: url(:/Start_Stop.png); } QPu…

你的助听器装置效果好吗?

作者&#xff1a;兰明 助听效果的好坏是一个多维的概念&#xff0c;简单的讲就是能使听障人士成功地应付生活的程度。影响助听装置效果的因素主要有三个方面&#xff1a;听障人士自身的因素、助听装置本身的因素以及专业服务的因素。其中病史超过半年的听障人士自身的因素&…

【linux】重定向+缓冲区

重定向缓冲区 1.重定向1.1重定向本质1.2重定向接口1.3重定向分类1.3.1>输出重定向1.3.2>>追加重定向1.3.3<输入重定向 2.理解 >&#xff0c; >>&#xff0c; <3.如何理解linux下一切皆文件4.缓冲区4.1理解缓冲区问题4.1.1为什么要有缓冲区4.1.2缓冲区刷…

数据结构:平衡二叉树

平衡二叉树 平衡二叉树&#xff0c;又称为AVL树。实际上就是遵循以下两个特点的二叉树&#xff1a; 每棵子树中的左子树和右子树的深度差不能超过 1&#xff1b;二叉树中每棵子树都要求是平衡二叉树&#xff1b; 其实就是在二叉树的基础上&#xff0c;若树中每棵子树都满足其…

Win10玩游戏老是弹回桌面的解决方法

在Win10电脑中&#xff0c;用户不仅可以办公&#xff0c;也可以畅玩各种各样的游戏。但是&#xff0c;有时候用户在玩游戏的时候&#xff0c;遇到了游戏老是自己弹回桌面的问题&#xff0c;这样是非常影响游戏体验的&#xff0c;却不清楚具体的解决方法。下面小编给大家带来了简…

系统架构与Tomcat的安装和配置

2023.10.16 今天是学习javaweb的第一天&#xff0c;主要学习了系统架构的相关知识和原理&#xff0c;下载了web服务器软件&#xff1a;Tomcat&#xff0c;并对其进行了配置。 系统架构 包括&#xff1a;C/S架构 和 B/S架构。 C/S架构&#xff1a; Client / Server&#xff0…

OJ项目——统一数据格式返回,我是如何处理的?

目录 前言 OJ项目中是如何处理的 1、准备一个类&#xff0c;作为统一的数据返回格式 2、准备一个类&#xff0c;实现ResponseBodyAdvice接口 3、我们如何写返回值更好 4、进一步优化返回值 小结 前言 关于SpringBoot的同一功能处理&#xff0c;本博主在这篇博客已经有介…

MatrixOne Logtail 设计解析

Logtail 是 CN&#xff08;Computation Node&#xff09;与 TN&#xff08;Transaction Node&#xff09;之间的一种日志同步协议&#xff0c;是 CN 和 TN 协同工作的基础。本文将介绍 logtail 协议的基本定位&#xff0c;协议内容和产生过程&#xff0c;也会提及一些遇到的挑战…

如何选择最适合你的LLM优化方法:全面微调、PEFT、提示工程和RAG对比分析

一、前言 自从ChatGPT问世以来&#xff0c;全球各地的企业都迫切希望利用大型语言模型&#xff08;LLMs&#xff09;来提升他们的产品和运营。虽然LLMs具有巨大的潜力&#xff0c;但存在一个问题&#xff1a;即使是最强大的预训练LLM也可能无法直接满足你的特定需求。其原因如…

微信小程序获取手机号(2023年10月 python版)[无需订阅]

技术栈&#xff1a; 1. 微信开发者工具中的调试基础库版本&#xff1a;3.1.2。 2. 后台&#xff1a;django。 步骤&#xff1a; 1. 首先在后台django项目的定时任务中增加一个下载access_token函数&#xff0c;并把得到的access_token保存在数据库中&#xff08;其实随便哪里…

INTELlij IDEA编辑VUE项目

菜单中选择setting–>Plugins 或者快捷键 ctrlalts 搜索vue&#xff0c;但有些情况会搜索不出来&#xff0c;先说搜索到的情况 如下图所示&#xff1a; 如果没有vue.js则说明过去已经安装了。 搜索到了后点击Install安装即可&#xff0c; 但即使搜索成功了&#xff0c;也不…

功夫猫小游戏

欢迎来到程序小院 功夫猫 玩法&#xff1a; 对准对方猫点击鼠标左键进行扑街&#xff0c;碰到敌方猫扑街X1&#xff0c;不要让对方猫碰到自己&#xff0c;统计扑街次数&#xff0c;快去玩功夫猫吧^^。开始游戏https://www.ormcc.com/play/gameStart/189 html <canvas id&q…