排序学习整理(2)
上集回顾
排序学习整理(1)-CSDN博客
2.3 交换排序
交换排序的基本思想是:根据序列中两个记录键值的比较结果,交换这两个记录在序列中的位置。
特点:
- 通过比较和交换操作,将键值较大的记录逐步移动到序列的尾部,而键值较小的记录逐步移动到序列的前部。
- 重复此过程,直到整个序列变得有序。
2.3.1 冒泡排序
冒泡排序(Bubble Sort)是一种简单的交换排序算法,其核心思想是通过重复的比较和交换操作,将序列中的最大值或最小值逐步“冒泡”到序列的尾部或头部。
基本思想
从序列的起始位置开始,依次比较相邻的两个元素:
如果它们的顺序不正确(如升序时前一个大于后一个),则交换它们的位置。
一轮比较后,当前未排序部分的最大值会被“冒泡”到末尾。
对剩下的未排序部分重复上述过程,直到整个序列有序。
算法步骤
- 初始化一个外层循环,用来控制需要比较的轮数。
- 在每轮循环中,从序列的开头开始,逐一比较相邻的两个元素,完成交换。
- 每轮排序结束后,未排序部分的长度减1。
- 重复上述操作,直到不需要再比较。
代码实现
void BubbleSort(int* arr, int n) {for (int i = 0; i < n - 1; i++) { // 控制排序轮数int swapped = 0; // 标记本轮是否发生交换for (int j = 0; j < n - 1 - i; j++) { // 控制每轮比较次数if (arr[j] > arr[j + 1]) { // 如果顺序不正确int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;swapped = 1; // 标记发生了交换}}if (!swapped) { // 如果本轮没有交换,说明已经有序break;}}
}
特性总结
算法复杂度
- 时间复杂度:
- 最好情况(序列已排序):O(n)
- 最坏情况(序列逆序):O(n^2)
- 平均情况:O(n^2)
- 空间复杂度:只需要常数级额外空间,空间复杂度为 O(1)。
- 稳定性:冒泡排序是稳定的排序算法,因为在比较相等元素时,它不会改变它们的相对位置。
优缺点和适用场景就不介绍了,冒泡的效率太过于低下,基本只用于教学了
2.3.2 快速排序
基本思想是:从待排序序列中随机选取一个元素作为基准值,依据该基准值将序列划分为两个子序列,其中左子序列的所有元素都小于基准值,右子序列的所有元素都大于基准值。然后对左右子序列分别重复这一过程,直到所有元素都排列到正确的位置上为止。
快速排序实现的主框架
//快速排序
void QuickSort(int* a, int left, int right){if (left >= right) {return;}//_QuickSort⽤于按照基准值将区间[left,right)中的元素进⾏划分int meet = _QuickSort(a, left, right);QuickSort(a, left, meet - 1);QuickSort(a, meet + 1, right);
}
主要有以下几种实现方法:
2.3.2.1 hoare版本
Hoare版本的快速排序是由算法提出者Tony Hoare在1962年设计的一种经典快速排序实现。该版本利用分区的思想,将数组分成两部分,使得一部分元素小于基准值,另一部分元素大于基准值。随后对两部分递归地应用快速排序,直到数组有序。
基本思想
核心操作是“分区”,使用左右指针分别从两端向中间扫描,找到需要交换的元素位置,最终将数组划分为左右两部分。
-
Hoare版本的快速排序通过分区的思想,将数组分成两部分:
- 左子数组中的元素均小于等于基准值。
- 右子数组中的元素均大于等于基准值。 采用递归或迭代方式,对子数组继续进行快速排序,直到子数组长度为1或0,排序完成。
算法步骤
- 选择基准值: 通常选择当前子数组的第一个元素作为基准值。
- 分区:
- 设置左右两个指针,分别指向当前子数组的两端。
- 右指针向左移动,找到第一个小于等于基准值的元素。
- 左指针向右移动,找到第一个大于等于基准值的元素。
- 如果左右指针未交错,交换两指针指向的元素,继续移动指针。
- 当左右指针交错时,分区完成,并返回分界点。
- 递归排序:
- 将数组按照分界点划分为左右两部分。
- 分别对左右部分递归执行上述过程,直到子数组长度为1或0。
问题1:为什么跳出循环后right位置的值⼀定不大于key?
当 left > right 时,右指针(right)已经移动到了左指针(left)的左侧。由于在分区过程中,左指针从左到右扫描的所有数据均不大于基准值(key),因此,跳出循环时,右指针(right)所指向的数据一定不大于基准值(key)。
问题2:为什么left 和 right指定的数据和key值相等时也要交换?
相等的值参与交换确实有一些额外消耗,但在实际中,数组可能存在大量重复元素,如果相等的值不参与交换,可能导致分区无法有效进行,从而影响排序的正确性和效率。
代码实现
// 交换函数
void Swap(int* p1, int* p2) {int tmp = *p1;*p1 = *p2;*p2 = tmp;
}
// 快速排序
void QuickSort(int* a, int left, int right) {if (left >= right) { // 递归边界条件:区间只有一个元素或无元素return;}int begin = left, end = right; // 记录当前区间int keyi = left; // 基准值初始为第一个元素的下标while (left < right) {// 从右向左寻找比基准值小的元素while (a[right] >= a[keyi] && left < right) {right--;}// 从左向右寻找比基准值大的元素while (a[left] <= a[keyi] && left < right) {left++;}// 交换左右指针指向的值Swap(&a[left], &a[right]);}// 基准值归位Swap(&a[left], &a[keyi]);keyi = left; // 更新基准值位置// 对左右两部分递归排序QuickSort(a, begin, keyi - 1); // 左部分QuickSort(a, keyi + 1, end); // 右部分
}
特性总结
算法复杂度
时间复杂度:
最好情况:O(n logn),每次分区都能平均划分为两半。
最坏情况:O(n^2),当基准值总是选择到最小或最大值时,分区极不均衡。
平均情况:O(n logn),这是快速排序的典型表现。
空间复杂度:
O(logn):递归调用栈的深度。
优缺点
优点:
- 时间复杂度低:平均性能优于大多数排序算法,适合大规模数据。
- 原地排序:不需要额外的内存(仅递归栈空间)。
- 实现简单:通过分区逻辑实现高效的递归。
缺点:
- 不稳定:相同元素的相对顺序可能改变。
- 最坏情况效率低:对于完全有序或接近有序的数据,性能可能退化为O(n^2)。
- 递归调用栈问题:当数据规模过大时,递归调用深度可能导致栈溢出。
适用场景
- 大规模数据排序:快速排序在大数据场景下表现良好。
- 对稳定性无要求的场景:例如数字或字符排序。
- 通用排序需求:适合随机分布的无序数据集。
很明显的缺点不少,接下来介绍针对不同情况优化的另外三种快排
2.3.2.2 挖坑法
挖坑法(也称为坑填法或坑填交换法)是一种在排序算法中常用的技巧,特别是在快速排序和堆排序等算法的实现中。常用于处理两个元素交换问题,并且通常配合双指针或者分区操作来完成排序。
挖坑法在大量重复元素的情况下能减少交换次数,因此在这些场景下的优化效果明显。
基本思想
在元素交换过程中使用一个临时“坑”来存储一个元素的值,避免出现直接交换时元素覆盖的情况。这使得元素能够顺利地放到正确的位置,并且交换过程更加高效。
在快速排序的分区操作中,使用两个指针分别从两端扫描,遇到需要交换的元素时,利用“坑”存储其中一个元素,等另一个元素找到合适位置后再进行交换。最终基准值会被放置到“坑”中,实现正确的分区。
算法步骤
- 选择基准值:选择一个元素作为基准值(通常是第一个元素,或随机选择一个)。
- 左右指针扫描:
右指针从右侧开始,向左扫描,找到一个小于等于基准值的元素。
左指针从左侧开始,向右扫描,找到一个大于等于基准值的元素。
- 交换元素:
如果左指针和右指针没有交错,就交换这两个元素。
交换时,使用一个临时“坑”来存放其中一个元素的值,避免覆盖问题。
- 继续扫描:重复步骤2和步骤3,直到左右指针交错。
- 放置基准值:当左右指针交错时,将基准值放入当前“坑”位置,即完成分区。
- 递归排序:对左右两部分继续递归执行快速排序。
代码实现
void QuickSort(int* a, int left, int right) {if (left >= right) {return; // 当区间为空或只有一个元素时,递归结束}int begin = left;int end = right;int keyi = a[left]; // 基准值while (left < right) {// 从右向左寻找比基准值小的数while (a[right] >= keyi && left < right) {right--;}if (left < right) {a[left] = a[right]; // 填坑}// 从左向右寻找比基准值大的数while (a[left] <= keyi && left < right) {left++;}if (left < right) {a[right] = a[left]; // 填坑}}// 最后将基准值填入当前坑位a[left] = keyi;// 对左右区间递归排序QuickSort(a, begin, left - 1); // 左区间QuickSort(a, left + 1, end); // 右区间
}
复杂度优缺点什么的和hoare版差不多,就介绍下与hoare法相对的优化情况吧
减少了交换次数:Hoare法在某些情况下需要交换多个位置,尤其是当扫描到不满足条件的元素时。挖坑法则通过填坑和直接交换,使得不需要频繁交换,进一步提升了效率。
保持数据的稳定性:挖坑法对于数据的处理方式相对较为稳定,避免了一些冗余的交换,且能够使得算法执行过程中更有序。
提高了数据的分区效果:挖坑法有助于实现更平衡的分区,尤其是在遇到重复元素或者大范围的相同值时,能够更均匀地进行分割。由于它通过“填坑”的方式保持了分区的连续性,因此在数据比较“均匀”的情况下能够带来较好的分割效果。
2.3.2.3 前后指针法
前后指针法(Two-pointer technique)是一种常用的技术,通常用于排序算法中的分区操作,特别是在快速排序的分区阶段。它通过设置两个指针从数组的两端向中间扫描,逐步找到需要交换的位置,从而实现分区操作。
前后指针法对于提高交换效率和减少不必要的交换有很大帮助,尤其是在数据量大且分布不均匀时
基本思想
- 使用两个指针,一个从数组的左端开始(前指针),另一个从数组的右端开始(后指针)。
- 前指针向右扫描,直到找到一个大于等于基准值的元素;后指针向左扫描,直到找到一个小于等于基准值的元素。
- 当前指针小于后指针时,交换这两个元素。交换后,继续移动两个指针,直到指针交错或相遇。
- 当指针交错时,结束扫描,将基准值放到指针交错的位置,完成分区。
算法步骤
- 选择基准值:选择一个基准值,通常选择数组的第一个元素或者最后一个元素。
- 初始化指针:设置两个指针,前指针(
left
)指向数组的起始位置,后指针(right
)指向数组的结束位置。 - 扫描交换:
- 前指针:从左向右扫描,找到第一个大于等于基准值的元素。
- 后指针:从右向左扫描,找到第一个小于等于基准值的元素。
- 如果前指针小于后指针,则交换这两个元素,继续扫描。
- 基准值放置:当指针交错或相遇时,将基准值放入指针交错的位置,实现分区。
- 递归排序:对左侧和右侧子数组进行递归排序。
代码实现
Swap函数和上面的一样,就不再水一次了
void QuickSort(int* a, int left, int right) {// 如果区间无效或只剩一个元素,直接返回if (left >= right) {return;}// 设置初始基准值的位置(选取左边的元素作为基准值)int prev = left; // prev 用于标记小于基准值的区域的末尾位置int cur = prev + 1; // cur 用于遍历数组int pivotIndex = left; // 基准值的位置(初始化为左边界)// 遍历整个区间,进行分区while (cur <= right) {// 如果当前元素小于基准值并且 prev 位置还没有与 cur 交换if (a[cur] < a[pivotIndex] && ++prev != cur) {// 如果符合条件,则交换 a[prev] 和 a[cur]Swap(&a[prev], &a[cur]);}++cur; // 继续向右移动 cur 指针}// 最后将基准值与 prev 位置的元素交换,将基准值放到正确的位置Swap(&a[prev], &a[pivotIndex]);pivotIndex = prev; // 更新基准值位置// 递归处理左右两个子区间QuickSort(a, left, pivotIndex - 1); // 处理左区间QuickSort(a, pivotIndex + 1, right); // 处理右区间
}
算法复杂度和优缺点一样继续与hoare同,优化在于
减少了交换次数:在Hoare法中,当左右指针扫描时,可能会在每次比较时都进行交换。而前后指针法则是只在需要时才交换,并且扫描过程是从两端开始的,能够更高效地找到合适的交换元素。
提高了分区效率:前后指针法可以确保左右指针每次都能找到需要交换的元素,因此比传统的Hoare法可能更快地将数据分割为两部分。这种方法避免了多个不必要的交换,提高了分区的速度。
优化情况:当数据的顺序比较混乱,或者大部分元素都较接近基准值时,前后指针法能够更有效地进行数据分割,减少了不必要的比较和交换操作。
2.3.2.4 非递归版本
饺子醋来咯,尝试尽量讲清楚
首先,这个版本需要栈来实现,学了栈再来,先上官方一点的介绍
传统的快速排序算法通常使用递归来实现分区和排序操作,但在某些情况下(例如,当递归深度过大时,可能导致栈溢出),可以使用非递归版的快速排序来避免递归带来的开销。非递归版快速排序使用显式的栈来模拟递归的调用,借助栈保存子数组的边界,逐步进行分区和排序。
非递归版则主要解决了递归带来的栈溢出问题,适合处理大规模数据或栈空间受限的情况,但对于排序速度并没有显著提高。
基本思想
非递归版快速排序的基本思想是:
- 使用栈代替递归的调用栈,通过显式地维护子数组的起始和结束位置。
- 每次分区操作后,将新的左右子数组的边界压入栈中,继续分区,直到栈为空。
- 由于栈模拟了递归过程,因此在内存方面比传统递归方法更加高效。
算法步骤
- 初始化栈:初始化一个栈,用来存储子数组的左右边界。
- 压入初始边界:将整个数组的左右边界压入栈中(即初始的低位和高位)。
- 分区操作:
- 从栈中弹出一个子数组的边界(即子数组的左端点和右端点)。
- 对该子数组进行分区操作,将其分为两个部分,基准值将放在正确位置。
- 压入子数组的边界:根据分区结果,将两个子数组的边界压入栈中,继续分区。
- 循环直到栈为空:重复上述步骤,直到栈为空,即所有子数组都被处理完。
其实就记住这个操作就好了,取栈顶区间,单趟排序,右左子区间入栈,不断重复及完成了整个函数。
代码实现
void QuickSortNonR(int* a, int left, int right) {ST st;STInit(&st); // 初始化栈STPush(&st, right); // 将右边界入栈STPush(&st, left); // 将左边界入栈// 循环栈中的区间进行排序while (!STEmpty(&st)) {int begin = STTop(&st); // 获取当前区间的左边界STPop(&st); // 弹出左边界int end = STTop(&st); // 获取当前区间的右边界STPop(&st); // 弹出右边界int keyi = begin; // 选择基准元素的初始位置int cur = begin + 1; // 当前元素指针int prev = begin; // 上一个小于基准元素的位置// 对区间内的元素进行排序while (cur <= end) {if (a[cur] < a[keyi] && ++prev != cur) // 如果当前元素小于基准元素,则交换Swap(&a[prev], &a[cur]);++cur;}// 将基准元素放到正确的位置Swap(&a[keyi], &a[prev]);keyi = prev; // 更新基准元素位置// 如果基准元素右边还有元素,继续排序右边区间if (keyi + 1 < end) {STPush(&st, end);STPush(&st, keyi + 1);}// 如果基准元素左边还有元素,继续排序左边区间if (keyi - 1 > begin) {STPush(&st, keyi - 1);STPush(&st, begin);}}STDestroy(&st); // 销毁栈
}
栈实现的代码拿的是自己上次的栈的实现(基础)-CSDN博客
特性总结
算法复杂度
时间复杂度不变,空间复杂度正常也是 O(logn),但在最坏情况(有序或接近有序)会到O(n)
优缺点
优点:
- 避免栈溢出:非递归版的快速排序通过显式的栈来模拟递归,避免了因递归深度过大而导致栈溢出的风险。
- 节省栈空间:在递归方法中,栈的空间随着递归深度的增加而增加,而非递归版的快速排序通过显式栈管理,节省了递归栈空间。
缺点:
- 代码复杂度增加:非递归版的快速排序实现需要额外的栈结构来模拟递归,增加了代码的复杂性。
- 空间开销:尽管非递归版避免了递归栈的开销,但仍然需要额外的栈空间来存储子数组的边界,尤其对于大数组时,空间开销可能较大。
适用场景
- 递归深度较大时:当数据集非常大或递归深度较深时,非递归版快速排序通过避免栈溢出,使得排序过程更稳定。
- 内存限制:如果系统的栈空间有限,递归深度可能会导致栈溢出,此时非递归版更为适用。
- 需要改进性能的应用场景:对于一些对性能要求较高的应用,避免递归栈开销可能会带来一定的性能提升。
2.3.2.5 优化方法
2.3.2.5.1 三数取中
这个方法针对前面4种都不利的有序数组,从这个角度出发,我们有了三数取中的优化方式:
即选出mid=(left+right)/2
a[left]、a[right]、a[mid]这三个数中,值为中位数的那个数,然后将它于a[keyi]交换。
下面代码基于前后指针法
// 三数取中法
int GetMidi(int* a, int begin, int end) {// 计算中间元素的索引int mid = begin + ((end - begin) >> 1);// 判断mid元素是否在begin和end之间(即a[mid] 是位于 [a[begin], a[end]] 之间)if ((a[mid] >= a[begin] && a[mid] <= a[end]) || (a[mid] >= a[end] && a[mid] <= a[begin])) {return mid; // 如果mid符合条件,返回mid的索引}// 如果a[begin]比a[mid]大,且a[begin]比a[end]小,或者a[begin]比a[mid]小,且a[begin]比a[end]大if ((a[begin] <= a[mid] && a[begin] >= a[end]) ||(a[begin] >= a[mid] && a[begin] <= a[end])) {return begin; // 返回begin的索引}// 默认返回end的索引return end;
}// 快速排序主函数
int QuickSort(int* a, int begin, int end) {// 三数取中法优化:选择begin, mid, end三个元素的中位数作为基准元素int ki = GetMidi(a, begin, end); // 获取中位数元素的索引Swap(&a[begin], &a[ki]); // 将中位数元素与begin位置的元素交换int keyi = begin; // keyi是基准元素的初始位置int prev = begin; // prev是扫描过程中已排序元素的最右边的位置int next = begin + 1; // next是当前扫描到的元素位置// 前后指针法进行分区操作while (next <= end) {// 如果当前元素小于基准元素,交换到prev位置,并更新prev的位置if (a[next] < a[keyi] && ++prev != next) {Swap(&a[prev], &a[next]);}next++; // 移动next指针,扫描下一个元素}// 将基准元素放到正确的位置(即prev位置),并返回基准元素的位置Swap(&a[keyi], &a[prev]);return prev; // 返回基准元素的最终位置
}
注释这么详细就不用额外介绍了吧(
来下一个优化
2.3.2.5.2 小区间优化
当区间很小时,直接采用插入排序,就不用继续递归了。
void QuickSort(int* a, int left, int right)
{// 如果区间内的元素只有一个或没有,直接返回,因为已经是最小子问题(即已经排好序)if (left >= right)return;// 对于小区间(小于10个元素),使用插入排序,可以减少递归深度,提高效率if (right - left + 1 < 10){InsertSort(a + left, right - left + 1); // 对[a+left, right]区间的元素进行插入排序}else{int begin = left, end = right;int midi = GetMidi(a, left, right); // 获取三数取中的基准索引Swap(&a[left], &a[midi]); // 将基准元素交换到left位置// 定义keyi为基准元素的索引,进行分区操作int keyi = left;// 进行前后指针法分区while (left < right){// 从右边开始,找一个小于基准的元素while (left < right && a[right] >= a[keyi]){--right; // right指针向左移动}// 从左边开始,找一个大于基准的元素while (left < right && a[left] <= a[keyi]){++left; // left指针向右移动}// 找到符合条件的元素后交换Swap(&a[left], &a[right]);}// 将基准元素放到正确的位置(即left位置),完成分区Swap(&a[left], &a[keyi]);keyi = left;// 递归调用对左右子数组进行排序QuickSort1(a, begin, keyi - 1); // 对左子数组进行排序QuickSort1(a, keyi + 1, end); // 对右子数组进行排序}
}
下一篇就讲归并排序和非比较排序吧,估计不长了
相关文章:
排序学习整理(2)
上集回顾 排序学习整理(1)-CSDN博客 2.3 交换排序 交换排序的基本思想是:根据序列中两个记录键值的比较结果,交换这两个记录在序列中的位置。 特点: 通过比较和交换操作,将键值较大的记录逐步移动到序列…...
【前端】将vue的方法挂载到window上供全局使用,也方便跟原生js做交互
【前端】将vue的方法挂载到window上供全局使用,也方便跟原生js做交互 <template><div><el-button click"start">调用方法</el-button></div> </template> <script> // import { JScallbackProc } from ./JScal…...
单片机的中断系统
作者简介 彭煜轩,男,银川科技学院计算机与人工智能学院,2022级计算机与科学技术8班本科生,单片机原理及应用课程第3组。 指导老师:王兴泽 电子邮件:1696409709qq.com 前言 本篇文章是参考《单片机原理…...
Java基础面向对象(接口高级)
高版本的接口 JDK8.0 普通的公开非抽象方法(默认方法) [public] default 返回值类型 方法名(形参列表){//操作语句 } default: 在此位置身份为非抽象标识 接口中的非抽象方法实现类不需要进行重写且通常不会进行重写 当父类与接口的方法体出现冲突时, 优先执行父类内容 (类优…...
OpenCV圆形标定板检测算法findCirclesGrid原理详解
OpenCV的findCirclesGrid函数检测圆形标定板的流程如下: findCirclesGrid函数源码: //_image,输入图像 //patternSize,pattern的宽高 //_centers,blobs中心点的位置 //flags,pattern是否对称 //blobDetector,这里使用的是SimpleBlobDetector bool cv::findCirclesGrid(…...
Linux 网卡收包流程如下
Linux 网卡收包流程如下 网卡收到数据包将数据包从网卡硬件缓存移动到服务器内存中(DMA方式,不经过CPU)通过硬中断通知CPU处理CPU通过软中断通知内核处理经过TCP/IP协议栈处理应用程序通过read()从socket buffer读取数据 网卡丢包 我们先看下ifconfig的输出&#…...
普中51单片机——LED流水灯模块
1、GPIO概念 GPIO(general purpose intput output)是通用输入输出端口的简称,可以通过软件来控制其输入和输出。51 单片机芯片的 GPIO 引脚与外部设备连接起来,从而实现与外部通讯、 控制以及数据采集的功能。 1.1、GPIO分类 &a…...
Linux 各个目录作用
刚毕业的时候学习Linux基础知识,发现了一份特别好的文档快乐的 Linux 命令行,翻译者是happypeter,作者当年也在慕课录制了react等前端相关的视频,通俗易懂,十分推荐 关于Linux的目录,多数博客已有详细介绍…...
【包教包会】CocosCreator3.x——重写Sprite,圆角、3D翻转、纹理循环、可合批调色板、不影响子节点的位移旋转缩放透明度
一、效果演示 重写Sprite组件,做了以下优化: 1、新增自变换,在不影响子节点的前提下位移、旋转、缩放、改变透明度 新增可合批调色板,支持色相、明暗调节 新增圆角矩形、3D透视旋转、纹理循环 所有功能均支持合批、原生平台&…...
腾讯阅文集团Java后端开发面试题及参考答案
Java 的基本数据类型有哪些?Byte 的数值范围是多少? Java 的基本数据类型共有 8 种,可分为 4 类: 整数类型:包括 byte、short、int 和 long。byte 占 1 个字节,其数值范围是 - 128 到 127,用于表示较小范围的整数,节省内存空间,在处理一些底层的字节流数据或对内存要求…...
Kafka如何保证消息可靠?
大家好,我是锋哥。今天分享关于【Kafka如何保证消息可靠?】面试题。希望对大家有帮助; Kafka如何保证消息可靠? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Kafka通过多种机制来确保消息的可靠性,主要包…...
【layui】tabs 控件内通过 iframe 加载url 方式渲染tab页面
<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>tabs 内部使用 iframe 嵌套 url 页面</title><link rel"stylesheet" href"../../../libs/layui/layui-2.4.5/dist/css/layui.css"><scr…...
EtherCAT转DeviceNe台达MH2与欧姆龙CJ1W-DRM21通讯案例
一.案例背景 台达MH2设备通常采用EtherCAT通信协议,这种协议在高速实时通信方面表现出色,适合设备之间的快速数据交换和精准控制。而欧姆龙CJ1W-DRM21 模块基于DeviceNet通信协议,DeviceNet在工业现场总线领域应用广泛,侧重于设备…...
清华、智谱团队:「6000亿合成交错语音文本」预训练,问答性能提升近3倍
与基于文本的大语言模型(LLM)相比,语音语言模型(SpeechLM)接受语音输入并生成语音输出,能够实现更自然的人机交互。然而,传统的 SpeechLM 因缺乏无监督语音数据和并行语音-文本数据,…...
Python办公——openpyxl处理Excel每个sheet每行 修改为软雅黑9号剧中+边框线
目录 专栏导读背景1、库的介绍①:openpyxl 2、库的安装3、核心代码4、完整代码5、最快的方法(50万行44秒)——表头其余单元格都修改样式总结 专栏导读 🌸 欢迎来到Python办公自动化专栏—Python处理办公问题,解放您的双手 🏳️…...
遇到问题:hive中的数据库和sparksql 操作的数据库不是同一个。
遇到的问题: 1、hive中的数据库和sparksql 操作的数据库不同步。 观察上面的数据库看是否同步 !!! 2、查询服务器中MySQL中hive的数据库,发现创建的位置没有在hdfs上,而是在本地。 这个错误产生的原因是&…...
《网络攻防实践》实践五报告
1.实践内容 防火墙 (1)基本概念 所谓“防火墙”是指一种将内部网和公众访问网(如Internet)分开的方法,它实际上是一种建立在现代通信网络技术和信息安全技术基础上的应用性安全技术,隔离技术。越来…...
深入傅里叶级数与傅里叶变换:从基础到应用
傅里叶分析是数学、物理和工程领域的一项基础工具,其核心思想是将复杂的信号或函数分解为一系列简单的正弦和余弦函数的叠加。本文将带你从傅里叶级数入门,逐步深入傅里叶变换的概念及其应用场景。 一、傅里叶级数:周期信号的分解 1. 什么是傅…...
C++入门一
一、命名空间 #include <stdio.h> int rand 0; int main() {printf("hello world\n");printf("%d\n", rand); } 这样是可以运行的,可是当我们加入一个头文件的时候 #include <stdio.h> #include <stdlib.h> int rand 0;…...
Spring Boot 项目集成camunda流程引擎
Spring Boot 项目集成camunda流程引擎 camunda地址 camunda中文地址 使用camunda开源工作流引擎有:通过docker运行、使用springboot集成、部署camunda发行包、基于源代码编译运行等多种方式。 文本重点介绍如何在Spring Boot应用程序中如何集成Camunda Platform开…...
Ubuntu20.04编译安装Carla全过程
前言 Carla的安装是我现阶段解决的第一个问题,现记录一下我安装Carla的过程以及我在安装过程中遇到的一些问题。 一、安装前准备 1、硬件环境 carla是一款基于UE4开发的模拟仿真软件,本身对硬件的要求比较高。 我是windows与ubuntu双系统࿰…...
typecho 自动订阅 RSS
昨天学习了一下 RSS 订阅知识之后,经过一番百度搜索,终于在自己的博客上实现了 RSS 订阅功能,但苦于技术有限,不能对 Feed 文件进行定时缓存,每次打开链接都会比较延迟。今天继续对这个功能进行了学习,突然…...
MFC图形函数学习13——在图形界面输出文字
本篇是图形函数学习的最后一篇,相关内容暂告一段落。 在图形界面输出文字,涉及文字字体、大小、颜色、背景、显示等问题,完成这些需要系列函数的支持。下面做简要介绍。 一、输出文本函数 原型:virtual BOOL te…...
11.25.2024刷华为OD
文章目录 HJ76 尼科彻斯定理(观察题,不难)HJ77 火车进站(DFS)HJ91 走格子方法,(动态规划,递归,有代表性)HJ93 数组分组(递归)语法知识…...
Python练习55
Python日常练习 题目: 补充函数getLastDay(y,m),其功能是计算y年m月共有多少天。 --------------------------------------------------------- 注意: 部分源程序给出如下。请勿改动主函数main和其它函数中的 任何内容,…...
DDR5和DDR4之区别(The Difference between DDR5 and DDR4)
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 DDR是什么意思? DDR5和D…...
分层架构 IM 系统之 Router 架构分析
通过前面文章的分析,我们已经明确,Router 的核心职责是作为中央存储记录在线客户端的连接状态,Router 在本质上是一个内存数据库。 内存是一种易失性的存储,既如此,Router 的可用性如何保障呢? 副本是分布…...
用函数实现模块化程序设计(七)--数组作为函数参数(排序算法)
调用有参函数时,需要实参,实参可以是常量,变量,表达式,数组元素的作用与变量相当,凡是变量出现的地方都可用数组代替,数组元素可以用作函数实参,数组名可以作实参和形参,…...
M31系列LoRa分布式IO模块功能简介
M31系列LoRa 分布式 IO 模块简介 M31系列LoRa分布式IO主机模块是一款强大的无线远程控制与采集设备,该设备采用 LoRa 无线技术(内置了无线模块),可通过串口或远程 LoRa 组网设备发送 Modbus RTU 指令进行控制,可搭配E…...
Dockerfile 安装echarts插件给java提供服务
java调用echarts插件,生成图片保存到磁盘然后插入到pptx中报表。 Dockerfile文件内容: #基础镜像,如果本地仓库没有,会从远程仓库拉取 openjdk:8 FROM docker.io/centos:centos7 #暴露端口 EXPOSE 9311 # 避免centos 日志输出 …...
学习threejs,使用VideoTexture实现视频Video更新纹理
👨⚕️ 主页: gis分享者 👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨⚕️ 收录于专栏:threejs gis工程师 文章目录 一、🍀前言1.1 ☘️VideoTexture 视频纹理 二、…...
【二分查找】Leetcode例题
【1】69. x 的平方根 - 力扣(LeetCode) 🍡解题思路:首先想到的是暴力查找,从1开始依次比较x与num*num的大小,然后找出满足num*num<x且(num1)*(num1)>x的num值;再来看看能不能优化一下&…...
稳定运行的以MySQL数据库为数据源和目标的ETL性能变差时提高性能方法和步骤
在ETL(Extract, Transform, Load)过程中,数据源和目标都为MySQL数据库时,性能变差可能由多种原因引起。提高以MySQL为数据源和目标的ETL性能需要综合考虑数据库性能、ETL任务的处理方式、硬件资源和工具的选择。通过批量处理、并行…...
Springboot(四十九)SpringBoot3整合jetcache缓存
上文中我们学习了springboot中缓存的基本使用。缓存分为本地caffeine缓存和远程redis缓存。现在有一个小小的问题,我想使用本地caffeine缓存和远程redis缓存组成二级缓存。还想保证他们的一致性,这个事情该怎么办呢? Jetcache框架为我们解决了这个问题。 JetCache是一个…...
JVM 参数前缀 -XX: 含义 详解
在 Java 虚拟机(JVM)中,参数前缀 -XX: 表示的是 JVM 的非标准(实验性)选项。这些参数用于调整和优化 JVM 的性能、垃圾回收行为、内存分配策略等。 1. 参数分类 -XX: 参数大致分为三类,根据其格式区分&…...
【Mac】安装Gradle
1、说明 Gradle 运行依赖 JVM,需要先安装JDK,Gradle 与 JDK的版本对应参见:Java Compatibility IDEA的版本也是有要求Gradle版本的,二者版本对应关系参见:Third-Party Software and Licenses 本次 Gradle 安装版本为…...
证明切平面过定点的曲面是锥面
目录 证明:切平面过定点的曲面是锥面. 证明:切平面过定点的曲面是锥面. 证明: 方法一: 设曲面 S : r r ( u , v ) S:\mathbf{r}\mathbf{r}(u,v) S:rr(u,v)的切平面过定点 P 0 P_0 P0,其位置向量为 p 0 . \mathbf{p}_0. p0…...
【WPS】【EXCEL】将单元格中字符按照分隔符拆分按行填充到其他单元格
问题:实现如下图的效果 解答: 一、函数 IFERROR(TRIM(MID(SUBSTITUTE($A$2,",",REPT(" ",LEN($A$2))),(ROW(A1)-1)*LEN($A$2)1,LEN($A$2))),"") 二、在单元格C2中填写如下函数 三、全选要填充的单元格并且按CTRLD 函数…...
工作:三菱PLC防止程序存储器爆满方法
工作:三菱PLC防止程序存储器爆满方法 一、防止程序存储器爆满方法1、编程时,添加行注释时,记得要选“外围”,这样不会占用PLC程序存储器内存;2、选择“外围”的注释,前面会有个*星号,方便检查 二…...
【计算机组成原理】1位预测机制与2位预测机制详解
1位预测机制与2位预测机制详解 在计算机体系结构中,分支预测是为了提高流水线效率而对分支指令执行结果进行预测的技术。1位预测机制和2位预测机制是两种常见的分支预测策略,它们通过预测分支是否发生来决定是否跳转。 一、1位预测机制 1位预测机制是…...
基于SpringBoot+Vue的靓车汽车销售网站-无偿分享 (附源码+LW+调试)
目录 1. 项目技术 2. 功能菜单 3. 部分功能截图 4. 研究背景 5. 研究目的 6. 可行性分析 6.1 技术可行性 6.2 经济可行性 6.3 操作可行性 7. 系统设计 7.1 概述 7.2 系统流程和逻辑 7.3 系统结构 8. 数据库设计 8.1 数据库ER图 (1)材料分…...
ESP32-S3模组上跑通ES8388(13)
接前一篇文章:ESP32-S3模组上跑通ES8388(12) 二、利用ESP-ADF操作ES8388 2. 详细解析 上一回解析了es8388_init函数中的第6段代码,本回继续往下解析。为了便于理解和回顾,再次贴出es8388_init函数源码,在…...
洛谷 P1651 塔(DP)
题目传送门https://www.luogu.com.cn/problem/P1651 解题思路 设 表示前 个积木,两塔高度差为 (第一个比第二个高多少),的最大高度。 易得: 首先,不选当前的积木: 其次,选当前…...
去哪儿Java开发面试题及参考答案
怎么设置缓存能更快让用户收到数据? 要设置缓存以更快让用户收到数据,可从以下几方面着手。首先,选择合适的缓存位置很关键。将缓存放置在离用户近的地方,如 CDN 缓存,能极大缩短数据传输距离与时间。对于动态内容,可在应用服务器本地设置内存缓存,像使用 Ehcache 等库,…...
DDOS分布式拒绝服务攻击
DDOS分布式拒绝服务攻击 简单来说 传统的DOS就是一台或者多台服务对一个受害目标(服务器,路由,ip,国家)进行攻击,当范围过大时就是DDOS。目的就是通过大规模的网络流量使得正常流量不能访问受害目标&…...
AI后端工程师面试题的内容
AI后端工程师面试题主要包括以下几个方面的内容: 一、技术基础和项目经验: 1. 微服务架构的理解和应用:请描述你对微服务架构的理解,并举例说明一个你参与过的微服务项目,阐述你在该项目中扮演的角色和所承…...
使用go语言写一个脚本 实现WebSockt连接 用户发送a 得到返回b
在Go语言中实现一个简单的WebSocket服务器,该服务器能够接收客户端发送的“a”并返回“b”,可以按照以下步骤进行。首先,确保你的环境中已经安装了Go语言环境。接下来,你需要安装一个WebSocket库来处理WebSocket连接。这里我们使用…...
浏览器中输入一个URL后,按下回车后发生了什么
URL ,统一资源定位符, 简单点就是网址 ip 或域名 端口号 资源位置 参数 锚点 大致流程 URL 解析DNS 查询TCP 连接处理请求接受响应渲染页面 1 .输入一个网址之后,首先浏览器通过查询 DNS ,查找这个 URL 的 IP …...
ISO26262-(Timing Monitoring)在多核MCU的TPU上功能安全ASILB与ASILD有什么区别
在多核微控制器(MCU)的时间保护方面,针对功能安全ASIL B与ASILD等级的设计和实施存在显著差异,这些差异主要体现在系统对时间关键性操作的保障程度、故障检测能力、以及系统响应的严格性上。 ASIL B 级别: 时间关键性:在ASIL B等级,系统设计注重于识别并处理大部分可能…...
【大数据学习 | 面经】Spark 3.x 中的AQE(自适应查询执行)
Spark 3.x 中的自适应查询执行(Adaptive Query Execution,简称 AQE)通过多种方式提升性能,主要包括以下几个方面: 动态合并 Shuffle 分区(Coalescing Post Shuffle Partitions): 当 …...