秩序之舞——排序算法中的数字星河

news2026/5/6 3:20:32
一引言在计算机科学的世界里排序是最基础、也最重要的核心算法之一。无论是日常开发中的列表数据整理、数据库查询的结果规整还是电商平台商品价格、销量的智能排行亦或是机器学习、大数据处理中的数据预处理环节排序算法都无处不在默默支撑着程序高效运转。排序的本质就是将一组无序的数据元素按照既定规则升序、降序、自定义权重重新排列为有序序列。而市面上并没有万能的排序算法有的实现简单却效率偏低有的时间复杂度优异但逻辑晦涩有的适合小规模数据有的在海量数据场景下才能发挥优势。冒泡、选择、插入、希尔、归并、快速排序…… 这些经典排序算法不仅是编程入门的必学知识点更是面试高频考点也是我们理解时间复杂度、空间复杂度、稳定性三大算法核心概念的最佳载体。本文就带大家从零拆解常见经典排序算法从底层原理、代码实现、复杂度分析到适用场景逐一梳理帮你彻底吃透排序逻辑既能手写源码也能在实际开发中精准选型。二冒泡排序以及鸡尾酒排序这是许多入门者都信手拈来的冒泡排序默认排升序my_swapa函数在此处实现下面的排序算法就不再对此作过多赘述了)void my_swap(int* a, int* b) { int temp *a; *a *b; *b temp; } void bubbleSort(int* a, int size) { for (int i 0; i size - 1; i) { int flag 0; for (int j 0; j size - 1 - i; j) { if (a[j] a[j 1]) { my_swap(a[j], a[j1]); flag 1; } } if (flag 0)break; } }注释其中flag相关语句是对算法的优化每次交换就改变flag的值当一趟排序下来flag的值没有改变就说明此趟排序中数组已经有序了就停止。其算法作用示意图如下但是这样的冒泡排序还是有一定的缺陷排序顺序只能单向走小元素在最右侧时移动极慢乌龟问题因此我们引入鸡尾酒排序。鸡尾酒排序 双向冒泡排序普通冒泡从左往右一趟只把最大值冒泡到最右边鸡尾酒排序先左→右再右→左来回双向遍历像调酒来回摇晃杯子一样所以叫鸡尾酒排序相关算法原理1. 第一轮从左向右冒泡把最大值推到数组末尾2. 第二轮从右向左冒泡把最小值推到数组开头3. 不断左右来回遍历逐步收紧左右边界4. 重复直到整个数组有序相关代码实现//鸡尾酒排序双向冒泡排序 void cocktailSort(int* a, int size) { int left 0; int right size-1; int flag 0; for (int i left; i right; i) { if (a[i] a[i 1]) { my_swap(a[i], a[i 1]); flag 1; } } if (flag 0)return; flag 0; for (int i right; i left; i--) { if (a[i] a[i - 1]) { my_swap(a[i], a[i - 1]); flag 1; } } if (flag 0)return; }作用示意图三、直接插入排序和希尔排序首先看一个生活场景你一定玩过纸牌我们都是这么玩的左手拿已经理好的有序牌从小到大排好每次右手摸一张新牌从左手手里的牌从后往前依次比对找到合适位置把新牌插进去左手始终保持全程有序直到摸完所有牌。这就是直接插入排序的原版逻辑一模一样。我们通过代码来类比一下void insertSort(int *a,int size) { for(int i0;isize-1;i) { int endi; // end左手有序区最后一张牌 int tempa[end1]; // temp右手新摸来的一张牌 // 从后往前比对手牌 while(end0) { if(a[end]temp) { a[end1]a[end]; // 手里大牌往后挪一位 end--; } else break; // 找到比它小的停下不用再往前找 } a[end1]temp; // 把新牌插进空位 } }算法的逻辑是这样的假定我们规定【0end】这一个区间是有序的我们再引入一个元素a[end1],再通过算法使【0end1】有序主要步骤为从后往前找比a[end1]元素大的值让他们整体向后挪动一位直到找到第一个比a【end1】还要小的元素将a[end1]元素放置在这个元素后面就可以了。值得注意的是两个循环条件1 isize-1:endi,但是引入元素a【end1】end1size2 end0:当找不到比a【end1】还要小的元素时a【end1】应该放置在a【0】的位置上故此时end10,end-1,整个while循环的执行条件应该是end0,使得end-1时刚好退出循环。插入排序的示例图如下我们通过计算可以知道整个算法的交换语句运行次数最坏满足等差数列时间复杂度为O(n^2)怎样使得算法效率提高,希尔在这个算法的基础之上做出了调整得到了大名鼎鼎的希尔排序。我们首先来看一看代码void shellSort(int* a, int size) { int gap size; while(gap1) { gap gap / 3 1; for (int j 0; j size - gap; j) { int end j;//记录有序数组尾端下标 int temp a[end gap]; while (end 0) { if (temp a[end]) { a[end gap] a[end]; end-gap; } else break; } a[end gap] temp; } } }先回顾直接插入排序核心思想1. 原理把数组分成已排序区间和未排序区间第 1 个元素默认是有序区间从第 2 个元素开始逐个把未排序区间的元素插入到前面有序区间的合适位置像打牌时一张张摸牌插到手里已排好的牌里。2. 直接插入排序的致命缺点数据逆序程度大时每次插入都要大量元素后移效率极低只能一个一个元素慢慢往前挪步子太小慢得离谱时间复杂度最坏O(n2)数据量大时完全不能用。希尔给出的优化方法是根据元素对应下标对于gap的余数分组每组分别套用直接插入排序的思路将直接插入排序操作中的1全部变为gap)作为一次预排序每组数据都有序了。我们可以发现当gap足够大时大的元素经过一次排序调整距离结尾更近但是排序用时较长gap较小时大的元素在排序调整后中距离结尾更远但是排序时间更短所以我们让gap递减进行多次预排序就可以使数组有序。排序示例图如下四、计数排序举个最接地气的例子班级按考试分数排队假设一场考试满分只有5 分分数范围很小只能是 0、1、2、3、4、5班里同学分数如下分数列表[3, 1, 4, 1, 5, 3, 2, 3]现在要从小到大给所有人按分数排序你会怎么最简单排普通人的思路先数一下得 0 分几个人、1 分几个人、2 分几个人……5 分几个人然后按分数从小到大依次把人列出来就行。我们数一遍0 分0 人1 分2 人2 分1 人3 分3 人4 分1 人5 分1 人然后顺着写1,1,2,3,3,3,4,5直接排好序了这就是计数排序的核心思想。通过例子我们可以得知计数排序具有一定的局限性只适用于元素较为紧凑的一系列数据排序当元素大小分布较为分散时使用计数排序就明显有所不足人为实现会导致效率低下计算机实现会导致占用大量内存导致内存利用效率不高具体代码如下void countSort(int* a, int size) { int min a[0], max a[0]; for (int i 0; i size; i)//遍历找最大最小元素以方便开辟辅助数组 { if (a[i] max)max a[i]; if (a[i] min)min a[i]; } int* index (int*)calloc(sizeof(int) ,(max - min 1)); for (int cur 0; cur size; cur)index[a[cur] - min];//计数 int j 0; for (int i 0; i max - min 1; i) { while (index[i]--) { a[j] imin; j; } }; }这里运用到简单哈希的思想1.首先遍历整个数组找到最大元素和最小元素即数据范围。2.根据数据范围动态开辟计数数组数组大小为max-min1)3.遍历整个原数组进行计数4.遍历计数数组来写入数据到原数组相关示例图解如下五、选择排序void selecSort(int * a,int size) { // i 从 size-1 开始到 1 结束 for(int i size - 1; i 1; i--) { int index 0; // 找 0~i 最大值 for(int j 0; j i; j) { if(a[j] a[index]) index j; } // 交换 a[i] 和 最大值 my_swap(a[i], a[index]); } }这是一个朴素的选择排序每i趟排序选出一个最大值放置在数组倒数第i个位置。但是一趟排序只能确定一个元素的位置故算法最坏时间复杂度为O(n^2)。我们可以稍作改进每次选出一个最大的元素一个最小的元素这样一趟排序下来就有两个元素的位置确定了。//选择排序 void selectSort(int* a, int len) { int begin 0; int endlen-1; while(beginend) { int max begin, min begin; for (int i begin 1; i end; i) { if (a[i] a[max])max i;//找到一趟中最大元素的下标 if (a[i] a[min])min i;//找到一趟中最小元素的下标 } my_swap(a[begin], a[min]); if (max begin)max min;//特例一趟中maxbegin my_swap(a[end], a[max]); begin; end--; } }值得注意的是这个程序有一个小细节if (max begin)max min;这样做的原因是当beging刚好是max记录的下标时第一次交换就会导致max下标失效所以在第二次交换前要检查max下标的有效性。代码运行关键步骤图解六、堆排序堆排序是基于数据结构堆来实现的相关细节请看往期博客c语言数据结构——堆详解下面是代码实现//升序建大堆 void adjustDown(int* arr, int size, int parent) { int child 2 * parent 1; while(childsize) { if (child 1size arr[child 1] arr[child]) child; if (arr[child] arr[parent]) { my_swap(arr[child], arr[parent]); parent child; child parent * 2 1; } else break; } } void heapSort(int* arr, int size) { for (int i (size - 1 - 1)/2; i 0; i--) { //向下调整建堆 adjustDown(arr,size, i); } int end size - 1; while (end 0) { my_swap(arr[0], arr[end]); adjustDown(arr, end, 0); end--; } }再次强调算法思想1.向下调整建堆升序建大堆降序建小堆2.交换首位元素对堆顶元素调用向下调整算法类似于堆顶元素的删除算法七归并排序给出一个生活中的小例子场景两摞已经排好序的作业本合并成一摞整体有序班里有两组同学的数学作业本都已经各自按分数从低到高排好了第一组[60, 72, 85]第二组[65, 78, 90, 95]现在老师要把两摞有序的本子合成一摞从头到尾有序你会怎么干不用打乱重排最聪明的做法两边各拿最上面一本比大小把分数低的先放到新队伍里哪一边拿完了剩下的直接全部接在后面。过程60 vs 65 → 放 6072 vs 65 → 放 6572 vs 78 → 放 7285 vs 78 → 放 7885 vs 90 → 放 85最后把 90、95 直接补上最终[60,65,72,78,85,90,95]相关算法涉及两个有序数组的合并两个有序数组的合并算法题归并排序的核心思想是1将数组一分为二2将两个数组分别排有序3.将两个有序数组合并那么怎么将两个数组排有序呢这不又回到问题本身了吗是不是有递归的味道相关代码实现//归并排序 void _mergeSort(int* a, int *arr,int left,int right) { if (left right)return; int mid (left right) / 2; //分治数组划分[left,mid][mid1,right]注意[left,mid-1][mid,right]分法有bug _mergeSort(a, arr, left, mid); _mergeSort(a, arr, mid 1, right); int begin1 left, end1 mid; int begin2 mid 1, end2 right; int i left; //两个有序数组的合并 while (begin1 end1 begin2 end2) { if (a[begin1] a[begin2])arr[i] a[begin1]; else arr[i] a[begin2]; } while (begin1 end1) arr[i] a[begin1]; while (begin2 end2)arr[i] a[begin2]; memcpy(aleft, arrleft, sizeof(int) * (right - left 1)); } void mergeSort(int* a, int size) { int* arr (int*)malloc(sizeof(int) * size); if (arr NULL) { perror(malloc fail); return; } _mergeSort(a, arr, 0, size - 1); free(arr); arr NULL; }归并排序相关图解第一步[8,4,5,7,1,3,6,2]/ \[8,4,5,7] [1,3,6,2]/ \ / \[8,4] [5,7] [1,3] [6,2]/ \ / \ / \ / \[8] [4] [5] [7] [1] [3] [6] [2]第二步[8] [4] → [4,8] [5] [7] → [5,7][1] [3] → [1,3] [6] [2] → [2,6][4,8] [5,7] → [4,5,7,8][1,3] [2,6] → [1,2,3,6]最后合并两大段[4,5,7,8] [1,2,3,6]↓最终有序[1,2,3,4,5,6,7,8]归并排序还有非递归版本//非递归版 void mergeSort2(int* a, int size) { //开辟辅助数组 int* nums (int*)malloc(sizeof(int) * size); for(int gap1;gapsize;gap*2) { //[i,gapi-1] [gapi,2*gapi-1] for (int i 0; i size; i 2 * gap) { int begin1 i, end1 gap i - 1; int begin2 gap i, end2 2 * gap i - 1; //处理越界情况 if (begin2 size)break;//第二组数据越界第一组数据部分越界不需要再归并了 if (end2 size)end2size-1;//第二组数据部分越界更新end2 int j begin1; //两个有序数组的合并 while (begin1 end1 begin2 end2) { if (a[begin1] a[begin2])nums[j] a[begin1]; else nums[j] a[begin2]; } while (begin1 end1) nums[j] a[begin1]; while (begin2 end2)nums[j] a[begin2]; memcpy(a i, nums i, sizeof(int) * (end2 - i 1)); } } free(nums); nums NULL; }这里有两个小细节已知将相邻的一段数据划为两段数据[i,gapi-1] [gapi,2*gapi-1]if (begin2 size)break;//第二组数据越界第一组数据部分越界不需要再归并了if (end2 size)end2size-1;//第二组数据部分越界更新end2再归并两段数据八快速排序首先来一个小例子引入场景全班同学按身高排队随便先挑一个人当基准中间人比如身高 170cm。所有人分成三拨比170 矮的站左边刚好 170站中间比170 高的站右边然后左边矮的一堆再随便挑一个中间人再分高矮右边高的一堆同样再分一直递归分下去全班自然就从矮到高排好了。这就是快排核心选基准、分区、左右递归我们首先写一个朴素的快速排序haore)版本//快速排序 void quickSort0(int* a, int left, int right) { if (left right)return; int keyi left; int end right; int begin left1; while (begin end) { while (begin end a[end] a[keyi])end--;//右边找大 while (begin end a[begin] a[keyi])begin;//左边找小 my_swap(a[begin], a[end]);//交换一大一小 } my_swap(a[begin], a[keyi]);//当二者相遇再交换keyi和begin指向的元素 quickSort(a, left, begin - 1);//begin左边排有序 quickSort(a, begin 1,right);//begin右边排有序 }算法的核心是将每个数组的第一个元素作为基准值用左右指针先让右指针找比基准值小的数之后让左指针找比基准值要大的数二者交换累次循环直到左右指针相遇每次都是右指针先移动再交换相遇位置所对应的元素和基准值此时由于基准值所在位置左边都是比它小的值右边都是比它大的值此时基准值在数组中的位置就唯一确定了。将基准值从原数组中忽略左右部分分别进行如上操作递归就可以使原数组有序了。代码演示图解数组[5, 3, 8, 4, 2, 7, 1, 6]初始调用quickSort0(a, left0, right7)plaintextleft0 right7 keyi 0 基准值 a[0] 5 begin left 1 1 end right 7初始状态下标 0 1 2 3 4 5 6 7数值5, 3 , 8 , 4 , 2 , 7 , 1 , 6 keyi begin end进入第一层 while (begin end) 循环第一步end 左移找 5 的数循环while (begin end a[end] a[keyi]) end--从最右端往左扫跳过所有≥5 的数扫到 a [6]1 小于 5停下plaintext下标 0 1 2 3 4 5 6 7 数值**5** , 3 , 8 , 4 , 2 , 7 , 1 , 6 keyi begin end第二步begin 右移找 5 的数循环while (begin end a[begin] a[keyi]) begin从 begin 往右扫跳过所有≤5 的数扫到 a [2]8 大于 5停下plaintext下标 0 1 2 3 4 5 6 7 数值**5** , 3 , 8 , 4 , 2 , 7 , 1 , 6 keyi begin end第三步不相遇交换 begin 和 end 元素my_swap(a[begin], a[end]);交换后数组plaintext下标 0 1 2 3 4 5 6 7 数值**5** , 3 , 1 , 4 , 2 , 7 , 8 , 6 keyi begin end继续循环begin end 再次执行end 继续左找小于 5 → 走到下标 4数值 2begin 继续右找大于 5 → 此时begin 和 end 碰到一起停止循环相遇最终状态plaintext下标 0 1 2 3 4 5 6 7 数值**5** , 3 , 1 , 4 , 2 , 7 , 8 , 6 keyi begin end跳出大循环交换基准位 和 相遇位置my_swap(a[begin], a[keyi]);交换基准 5 和 相遇位置 2单趟走完数组变成plaintext下标 0 1 2 3 4 5 6 7 数值 2 , 3 , 1 , 4 ,**5**, 7 , 8 , 6✅ 效果基准值 5 归位左边全部 ≤5右边全部 ≥5我们发现当数组元素是有序的排升序原数组降序时算法就会退化时间复杂度降为On^2)当我们选取的基准值是每趟排序中的中位数时函数调用栈帧创建的结构就形似二叉树时间复杂度为NlogN)所以我们设计出了一种三数取中算法来让基准值尽量为中位数同时当待排序元素较少时我们使用插入排序也能使排序性能提高int getMed(int* a, int left, int right) { int med (left right)/ 2; if (a[left] a[right]) { if (a[right] a[med])return med; else if (a[med] a[left])return left; else return right; } else { if (a[left] a[med])return left; else if (a[med] a[right])return right; else return med; } } void quickSort(int* a, int left, int right) { if (left right)return; if (right - left 1 10)//小区间优化 { insertSort(a left, right - left 1); } else { int keyi left; int end right; int begin left;//不可跳过设置begin为left1:9 8 7 6 int med getMed(a, left, right); my_swap(a[keyi], a[med]);//三数取中算法避免退化成o(N^2) while (begin end) { while (begin end a[end] a[keyi])end--;//右边找小 while (begin end a[begin] a[keyi])begin;//左边找大 my_swap(a[begin], a[end]);//交换一大一小 } my_swap(a[begin], a[keyi]);//当二者相遇再交换keyi和begin指向的元素 quickSort(a, left, begin - 1);//begin左边排有序 quickSort(a, begin 1, right);//begin右边排有序 } }下面是挖洞法对快速排序的实现基本逻辑和上面相同只是更直观//挖洞法 void quickSort2(int* a, int left,int right) { if (left right)return; int tempa[left],holeleft; int end right, begin left; while (beginend) { while (end begina[end] temp)end--; a[hole] a[end]; hole end; while (end begin a[begin] temp)begin; a[hole] a[begin]; hole begin; } a[hole] temp; quickSort2(a, left, hole - 1); quickSort2(a, hole 1, right); }前后指针法实现快速排序//前后指针法 void quickSort3(int* a, int left, int right) { if (left right)return;//递归返回条件 int cur left 1;//遍历找小 int prev left;//指向左边最后一个小于a[left]的元素 while (cur right) { if (a[cur] a[left] prev ! cur)my_swap(a[cur], a[prev]);//cur找小就交换 cur; }//最后一次交换后prev停留在比a[left]小的元素的位置 my_swap(a[prev], a[left]); quickSort3(a, left, prev - 1); quickSort3(a, prev 1, right); }三区间划分法实现快排算法实现用到了三指针和这道OJ题的算法思想相似数组的三段划分三段划分思想的核心是定义三个指针prev,cur,endcur负责遍历整个数组prev指向第一个区间的末尾end指向最后一个区间的开始就这样在遍历的过程中数组被分割成4个部分【leftprev】【prev1,cur-1】【cur,end-1】【end,right】开始时prevleft-1;endright1;当curend时遍历结束上述区间的第三个就不存在了整个数组被分成三个区间当然划分的方法也有讲究假设最终的123区间分别满足特性1特性2特性3当cur指向元素满足特性2时只需要将cur;让这个元素囊括到2区间当cur指向元素满足特性3是只需要将end--,再交换cur,end所指向的元素即可由于cur右边区域的元素还没有遍历的无法确定其特性还需再次判断cur不能当cur指向元素满足特性1时将prev,交换cur与prev所指向元素cur,相当于将1区间扩容将2区间首元素移动到后面。单趟排序将数组按照上面思想按照基准值进行划分再对 基准值的区间进行递归排序操作就可以让数组有序了。非递归法实现快排//非递归版本 void quickSort5(int* a, int left, int right) { stackint st; st.push(right); st.push(left); while (!st.empty()) { left st.top(); st.pop(); right st.top(); st.pop(); if (left right) continue; int keyi left; int begin left, end right; while (begin end) { // 找小从右向左 while (begin end a[end] a[keyi]) end--; // 找大从左向右 while (begin end a[begin] a[keyi]) begin; my_swap(a[begin], a[end]); } // 将基准值放到正确位置 my_swap(a[begin], a[keyi]); // 压入右子区间 [begin1, right] if (begin 1 right) { st.push(right); st.push(begin 1); } // 压入左子区间 [left, begin-1] if (left begin - 1) { st.push(begin - 1); st.push(left); } } }核心思路是用栈这个数据结构来模拟函数递归调用的过程主要算法和hoare版本相同模仿递归的过程是按照栈的后进先出原则来实现首先将待排序数组的左右下标传入出栈获取进行一趟排序之后首先压入右子区间的左右下标然后是左子区间如果区间违法就不入栈再循环进行出入栈排序的操作直到栈中元素为空。这个算法是来拟合二叉树的前度遍历。九、常用八大排序的性能总结排序算法平均时间复杂度最好时间复杂度最坏时间复杂度空间复杂度稳定性排序方式冒泡排序O(n2)O(n)O(n2)O(1)稳定原地直接插入排序O(n2)O(n)O(n2)O(1)稳定原地简单选择排序O(n2)O(n2)O(n2)O(1)不稳定原地希尔排序O(n1.3)O(n)O(n2)O(1)不稳定原地堆排序O(nlogn)O(nlogn)O(nlogn)O(1)不稳定原地快速排序O(nlogn)O(nlogn)O(n2)O(logn)递归栈不稳定原地归并排序O(nlogn)O(nlogn)O(nlogn)O(n)稳定非原地计数排序O(nk)O(nk)O(nk)O(nk)稳定非原地稳定性判断核心理解一、什么是排序稳定性定义假设数组中有两个相等的元素排序前A在B前面排序后如果A 仍然在 B 前面→稳定排序如果A、B 相对位置颠倒了→不稳定排序值相等的元素排序后相对位置不变 排序具有稳定性二、举个秒懂例子有数组括号是原始身份2(①) , 5 , 2(②) , 3两个22①在前2②在后稳定排序结果2(①) , 2(②) , 3 , 5相等元素保持原来先后顺序✅ 稳定不稳定排序可能出现2(②) , 2(①) , 3 , 5相等元素位置颠倒❌ 不稳定三、为什么会不稳定核心原因算法存在远距离交换只要有跨位置直接交换相等元素就容易打乱相对次序相邻交换、后移覆盖 一般是稳定的四、八大排序稳定 / 不稳定 归类 原因✅ 稳定排序冒泡排序只相邻交换相等不交换 → 稳定直接插入排序元素后移不跳跃交换 → 稳定归并排序左右合并时相等优先放左边原有元素 → 稳定计数排序计数排序一般只用于整形数据的排序研究稳定性意义不大❌ 不稳定排序简单选择排序直接把最值和最前面远距离交换容易打乱相等元素如果中间有和被交换数据大小相同的元素希尔排序分组跳跃式插入不是相邻移动 → 不稳定快速排序基准交换是远距离交换→ 不稳定堆排序堆调整是上下层远距离交换→ 不稳定

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

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

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…