【数据结构】常见八大排序算法总结

目录

前言

1.直接插入排序

2.希尔排序

3.选择排序

4.堆排序

5.冒泡排序

6.快速排序

6.1Hoare版本

6.2挖坑法

6.3前后指针法

6.4快速排序的递归实现 

6.5快速排序的非递归实现

7.归并排序

8.计数排序(非比较排序)

9.补充:基数排序

10.总结:排序算法的复杂度及稳定性分析


前言

排序 :排序就是使一串记录按照其中某个或某些关键字的大小,递增或者递减的排列起来的操作

内部排序:数据元素全部存放在内存中的排序

外部排序:数据元素太多而不能同时放在内存中,根据排序过程的要求不断在内外存之间移动数据的排序

常见的排序算法:

以上排序算法都是比较排序,还有计数排序这类非比较排序算法,一下我们对各个排序算法进行代码实现

1.直接插入排序

直接插入排序是一种简单的插入排序算法,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列

过程:当插入第i(i>=1)个元素时,前面的array[0],...,array[i-1]已经有序,此时用array[i]的排序码与array[i-1],array[i-2],...的排序码依次比较,找到插入位置后将原来位置上的元素顺序后移,将array[i]插入

📖Note:

对于数组中的第一个数据,不需要进行比较,因此第一步操作的是a[1]处的数据,区间[0,0]上的数据即a[0]这一个数据是有序的,因此外层循环只需要对n-1次,对a[1]到a[n-1]共n-1个数据进行处理

对于要处理的a[i],依次与a[i-1],a[i-2]比较,如果大于a[i]则交换(升序排列的情况)

优化:备份i处的数据,依次与a[i-1],a[i-2]比较,大于a[i]则向后移动一个位置,否则在小于a[i]的数据元素的后一个位置插入a[i]

时间复杂度与空间复杂度:

插入排序是在存放原数据的数组上进行操作,所以直接插入排序算法的空间复杂度是O(1)

1️⃣当原数据的序列是逆序时,为最坏情况,此时直接插入排序算法的时间复杂度是O(N^2)

如上图,序列逆序时,数据的挪动次数为1+2+3+...+n-1 = n(n-1)/2

所以时间复杂度为O(N^2)

2️⃣当原数据的序列有序时,为最好情况,此时直接插入排序算法的时间复杂度是O(N)

如上图:序列顺序排列时,总共进行了n-1次比较,没有数据的挪动

因此时间复杂度为O(N)

总结:元素集合越接近有序,直接插入排序算法的时间效率越高

//直接插入排序
void InsertSort(int* a, int n)
{for (int i = 0; i < n - 1; ++i){// [0,end]有序,end+1处的数据找到正确位置后,[0, end+1]有序int end = i;int tmp = a[end + 1];while (end >= 0){//升序排列if (a[end] > tmp){a[end + 1] = a[end];--end;}else{break;}}//此时,a[end]<=tmpa[end + 1] = tmp;}
}

2.希尔排序

希尔排序又称为缩小增量排序,希尔排序的基本思想:先选定一个整数gap,把待排序文件中的所有记录分成n/gap个组,每一组内的记录进行排序,然后更新(缩小)gap,重复上述分组和排序的操作,当gap=1时,所有记录在同一组内排序,即使所有记录有序

以下以初始增量为3为例

第一次分组如下图

每一组内的记录进行排序,使用直接插入排序算法

缩小gap=1,即对序列:5 1 2 5 6 3 8 7 4 9进行排序

每一组记录使用直接插入排序算法,当gap不为1时,每组记录的数据元素并不是连续排列的,而是间隔gap,因此需要对直接插入排序算法进行改造

for (int i = 0; i < n - gap; ++i)
{int end = i;int tmp = a[end + gap];while (end >= 0){if (a[end] > tmp){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = tmp;
}

优化:for循环调整部分为i++时,每次直接插入排序排一组记录,当改为i+=gap时,可以实现多组记录同时排序

📖Note:

希尔排序是对直接插入排序的优化,当gap>1时都是预排序,目的是使数据集合接近有序,当数据集合接近有序时,直接插入排序的时间效率较高

gap的选择:

gap的取法有多种,最初Shell提出取gap = n/2,gap = gap/2,直到gap = 1;后来Knuth提出取gap = gap/3 + 1。我们采用Knuth提出的方式取值。

gap越大,大数可以越快的跳到后面;gap越小,跳的越慢,但数据集合越接近有序

时间复杂度和空间复杂度:

希尔排序算法不额外开辟空间,其空间复杂度为O(1)

对希尔排序的时间复杂度分析很困难,在特定情况下可以准确估算关键码的比较次数和对象移动次数,但想要弄清楚关键码的比较次数和对象移动次数与增量选择之间的依赖关系,目前还没有较完整的数学分析,Knuth在《计算机程序设计技巧》中的结论是:在n很大时,关键码的平均比较次数和对象平均移动次数大约在n^1.25到1.6*n^1.25范围内,这是在利用直接插入排序作为子序列排序方法的情况下得到的。

//希尔排序
void ShellSort(int* a, int n)
{int gap = n;while (gap > 1){gap = gap / 3 + 1;//更新增量//for (int i = 0; i < n - gap; ++i)for (int i = 0; i < n - gap; i += gap){int end = i;int tmp = a[end + gap];while (end >= 0){if (a[end] > tmp){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = tmp;}}
}

3.选择排序

选择排序的基本思想:每一次从待排的数据元素中选出最小或最大的一个元素,存放在序列的起始位置,直到所有待排序的数据元素排完

直接选择排序的步骤

1️⃣在元素集合array[i]到array[n-1]中选择关键码最大(小)的数据元素

2️⃣若它不是这组元素中的最后一个或第一个元素,则将它与这组元素中的最后一个或第一个元素交换

3️⃣在剩余的array[i]到array[n-2](array[i+1]到array[n-1])集合中,重复上述步骤,直到集合剩余一个元素

简单来说直接选择排序将数据集合分成两部分,有序部分和无序部分

以排升序为例,每次选取待排数据集合即无序部分中的最大数放到无序部分的第一个位置即有序部分之后的第一个位置,就完成了一个数据元素的排序

优化:选择元素时选取最大元素和最小元素的操作同时进行,一趟比较选出最大的元素放在a[end]位置,选出最小的元素放在a[begin]位置,begin++,end--,重复上述操作

优化之后数组的两端有序部分,中间为无序部分

时间复杂度和空间复杂度:

直接选择排序算法不额外开辟空间,其空间复杂度为O(1)

1️⃣当原数据的序列是逆序时,为最坏情况,此时选择排序算法的时间复杂度是O(N^2)

遍历待排数据元素集合,共n个数据,选出最大值和最小值,需要比较n-1次

遍历待排数据元素集合,共n-2个数据,选出最大值和最小值,需要比较n-3次

综上所述,当原序列的数据元素逆序时,总共比较的次数为

(n-1)+(n-3)+...+1 = (n/2) *n  / 2

所以直接选择排序的时间复杂度为O(N^2)

2️⃣当原数据的序列有序时,为最好情况,此时直接插入排序算法的时间复杂度是O(N^2)

对于直接选择排序算法来说,逆序和顺序的时间复杂度相同,因为进行数据选择的操作是相同的,都是进行遍历选数,因此效率不高】

void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}
//选择排序
void SelectSort(int* a, int n)
{int begin = 0, end = n - 1;while (begin < end){// 选出最小的放begin位置// 选出最大的放end位置int mini = begin, maxi = begin;for (int i = begin + 1; i <= end; ++i){if (a[i] > a[maxi]){maxi = i;}if (a[i] < a[mini]){mini = i;}}Swap(&a[begin], &a[mini]);// 修正maxi:特殊情况begin和maxi重叠时,执行一次交换,maxi记录的是最小值if (maxi == begin)maxi = mini;Swap(&a[end], &a[maxi]);++begin;--end;}
}

4.堆排序

冒泡排序详解可以参考:CSDN

5.冒泡排序

冒泡排序详解可以参考:CSDN

6.快速排序

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,快速排序的基本思想为:任取待排元素序列中的某个元素作为基准值,按照该排序码将待排序集合分割成两个子序列,左子序列中的所有元素均小于基准值,右子序列中的所有元素均大于基准值,然后左右序列重复该过程,直到所有元素都排列在相应的位置上为止

快速排序递归实现与二叉树的前序遍历规则类似,需要注意的是如何按照基准值来对区间中数据进行划分,常见的方式有以下几种:

6.1Hoare版本

单趟排序:选定一个基准值key,一般情况下为第一个或最后一个元素,设置两个变量left和right分别指向数据元素集合的头和尾,left向后找大数,right向前找小数,找到则交换,直到二者相遇,将相遇位置的数与key交换。

单趟排序结束后,数据元素的排列为:小数  key  大数,其中小(大)数是指小(大)于key的数。key的位置已经确定,不需要再更改,分割出了两个子区间,若这两个子区间有序,则整体有序,因此递归对两个子区间进行排序即可使整体有序。

以下为单趟排序的参考代码:

int keyi = left;//选取第一个数作为关键值while (left < right){//right找小数while (left < right && a[right] >= a[keyi]){--right;}//left找大数while(left < right && a[left] <= a[keyi]){++left;}//判断left和right的关系,防止错过if (left < right){Swap(&a[left], &a[right]);}}//left和right相遇,交换相遇位置的值和关键值Swap(&a[left], &a[keyi]);

📖Note:

1️⃣与关键值比较大小时,需要注意与关键值相等的特殊情况

对left,找大数,遇到小于key的值时继续向后走;遇到等于key的值时,也应该向后继续寻找,如果不过滤等于key的值,则会产生死循环

当数据元素集合中为单值时,如果不过滤等于的情况,right找小数的循环将会是一个死循环;或者当key == a[right]时,也会陷入死循环

2️⃣当选取第一个数据元素作为key,left与right相遇时,交换相遇位置的数据与key,如何保证相遇的位置比key小?(key是第一个数据元素,属于小数区间)

left和right相遇有两种情况:

  1. right停下来,left撞到right相遇,相遇位置比key小:right找小数,所以right停下来的位置为小于key的数,即相遇位置为小于key的数,属于小数区间。因此若选取第一个数据元素做key,则right先走
  2. left停下来,right撞到left相遇,相遇位置比key大:left找大数,所以left停下来的位置为大于key的数,即相遇位置为大于key的数,属于大数区间。因此若选取最后一个数据元素做key,则left先走
// Hoare
int PartSort1(int* a, int left, int right)
{//取第一个数据元素为关键值int keyi = left;while (left < right){// R找小while (left < right && a[right] >= a[keyi]){--right;}// L找大while (left < right && a[left] <= a[keyi]){++left;}if (left < right)Swap(&a[left], &a[right]);}//相遇位置为meetiint meeti = left;Swap(&a[meeti], &a[keyi]);return meeti;
}

快速排序的优化:主要是对关键值key选取的优化

key的选择有三种方法:

  1. 随机选key
  2. 针对有序序列,先key为最中间位置的数据
  3. 三数取中:即第一个元素,中间位置元素,最后一个位置元素三数取中位数

三数取中的函数接口如下:

int GetMidIndex(int* a, int left, int right)
{int mid = left + (right - left) / 2;if (a[left] < a[mid]){if (a[mid] < a[right]){return mid;}else if (a[left] > a[right]){return left;}else{return right;}}// a[left] >= a[mid]else{if (a[mid] > a[right]){return mid;}else if (a[left] < a[right]){return left;}else{return right;}}
}

对key的选择优化之后,可以减少递归的层数,有效避免栈溢出

关键值key的选择优化之后,Hoare版本的单趟排序可以优化:通过三数取中的方法确定关键值key后,将key与第一个数据交换,此时关键值key仍为第一个数据,因此其他地方不需要修改

// Hoare
int PartSort1(int* a, int left, int right)
{// 三数取中int mid = GetMidIndex(a, left, right);Swap(&a[left], &a[mid]);int keyi = left;while (left < right){// R找小while (left < right && a[right] >= a[keyi]){--right;}// L找大while (left < right && a[left] <= a[keyi]){++left;}if (left < right)Swap(&a[left], &a[right]);}//相遇位置为meetiint meeti = left;Swap(&a[meeti], &a[keyi]);return meeti;
}

6.2挖坑法

挖坑法是对Hoare版本的改进,确定关键值key之后,将其备份,并将其作为一个坑位(数据集合中的第一个元素),right先走找小数,找到则将该小数填到坑位中,并更新该小数的位置为新的坑位,left再走找大数,找到后将该大数填到坑位中,并更新该大数的位置为新的坑位,重复上述操作,直到left和right相遇,相遇位置为一个坑位,填入关键值key

具体过程如下图所示;

// 挖坑法
int PartSort2(int* a, int left, int right)
{// 三数取中int mid = GetMidIndex(a, left, right);Swap(&a[left], &a[mid]);int key = a[left];int hole = left;while (left < right){// 右边找小,填到左边坑while (left < right && a[right] >= key){--right;}a[hole] = a[right];hole = right;// 左边找大,填到右边坑while (left < right && a[left] <= key){++left;}a[hole] = a[left];hole = left;}a[hole] = key;return hole;
}

6.3前后指针法

前后指针法也是对Hoare版本的改进,确定关键值key(数据元素集合的第一个元素)并备份,设置两个指针,prev和cur,prev初始化为第一个数据元素,cur初始化为prev的下一个数据元素,当cur没有越界时,cur向后找小数,找到则交换prev指向的数据和cur指向的元素,prev++,cur++,重复上述操作,直到cur越界时,交换prev指向的数据元素和关键值key

📖Note:prev++的操作在交换操作之前,cur++的操作在交换操作之后

在cur找到小数时,prev指向的也为小数,其下一个位置为大数,所以prev需要先加1再交换

具体过程如下图所示:

特殊情况:++prev == cur时,会产生自己和自己交换的情况,因此需要过滤

// 前后指针法
int PartSort3(int* a, int left, int right)
{// 三数取中int mid = GetMidIndex(a, left, right);Swap(&a[left], &a[mid]);int keyi = left;int prev = left;int cur = left + 1;while (cur <= right){// cur找小if (a[cur] < a[keyi] && ++prev != cur)Swap(&a[cur], &a[prev]);++cur;}Swap(&a[keyi], &a[prev]);return prev;
}

6.4快速排序的递归实现 

快速排序的递归实现:

单趟排序结束后,数据元素的排列为:小数  key  大数

小数区间为[begin ,keyi -1],大数区间为[keyi+1,end]

按照单趟排序的方法递归对这两个区间排序即可

递归结束的条件:区间中只有一个值时递归调用结束,begin==end;或者begin>end,区间无效时递归调用结束

//快速排序
void QuickSort(int* a, int begin, int end)
{if (begin >= end){return;}//每趟排序区间的划分:Hoare,挖坑法,前后指针法//调用相应接口即可int keyi = PartSort1(a, begin, end);QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);
}

6.5快速排序的非递归实现

函数的调用需要在开辟栈帧,如果递归调用的层数太深,可能会造成栈溢出

我们可以借助数据结构中的栈来优化快速排序,数据结构中的栈是在堆区开辟空间,堆区的空间要远远大于栈区空间

实际上,快速排序的非递归是借助数据结构中的栈模拟递归的过程

非递归实现思路:每次将区间的左右区间压栈,当栈不为空时,取栈顶两个元素分别作为一趟排序的区间的左右端点进行排序

📖Note:

如果先定义right,则需要区间左端点begin要先入栈,区间右端点end后入栈,这样才能保证right被区间右端点赋值,left被区间左端点赋值

//快速排序的非递归实现
void QuickSortNonR(int* a, int begin, int end)
{ST st;StackInit(&st);StackPush(&st, begin);StackPush(&st, end);while (!StackEmpty(&st)){//区间右端点int right = StackTop(&st);StackPop(&st);//区间左端点int left = StackTop(&st);StackPop(&st);//区间划分,单趟排序int keyi = PartSort3(a, left, right);//右区间if (keyi + 1 < right){StackPush(&st, keyi + 1);StackPush(&st, right);}//左区间if (left < keyi - 1){StackPush(&st, left);StackPush(&st, keyi - 1);}}StackDestroy(&st);
}

7.归并排序

归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的典型应用。将已经有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序,若将两个有序表合并成一个有序表,称为二路归并。

二路归并排序的步骤如下图:

由如下动态图,可以更好的理解二路归并排序的过程

归并排序的递归实现类似于二叉树的后续遍历,采用二叉树的后续遍历框架,递归结束的条件是begin>=end;区间的划分为 [begin, mid] 和 [mid+1, end],mid为中间位置

归并排序的时间复杂度和空间复杂度:

归并排序需要额外开辟一个长度为N的数组,因此其空间复杂度为O(N)

归并排序的时间复杂度为O(N*logN)

void _MergeSort(int* a, int begin, int end, int* tmp)
{if (begin >= end)return;int mid = (end + begin) / 2;_MergeSort(a, begin, mid, tmp);_MergeSort(a, mid + 1, end, tmp);// 两两归并成有序序列:取小的尾插// [begin, mid] [mid+1, end]int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int i = begin;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] <= a[begin2]){tmp[i++] = a[begin1++];}else{tmp[i++] = a[begin2++];}}//左区间中还有数据while (begin1 <= end1){tmp[i++] = a[begin1++];}//右区间中还有数据while (begin2 <= end2){tmp[i++] = a[begin2++];}// 拷贝回原数组:归并哪部分就拷贝哪部分回去memcpy(a + begin, tmp + begin, (end - begin + 1) * sizeof(int));
}void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}_MergeSort(a, 0, n - 1, tmp);free(tmp);tmp = NULL;
}

8.计数排序(非比较排序)

计数排序又称为鸽巢原理,是对哈希定址法的直接应用

计数排序的操作步骤:

  1. 统计相同元素出现的次数
  2. 根据统计的结果将序列回收到原来的序列中

📖Note:

计数排序中的相对映射

1.统计个数,得到个数记录数组C

2.将数组C转换成C[i]中存放的是值小于等于i的数据的个数

3.为A数组从前向后的每个元素找到对应的B中的位置,每次从A中复制一个元素到B中,C中相应的计数减一

4.当A中的所有数据都复制到B之后,B中存放的就是有序的数据

总结:计数排序在数据范围集中时,效率很高,但其适用范围有限

void CountSort(int* a, int n)
{int max = a[0], min = a[0];for (int i = 1; i < n; i++){if (a[i] > max){max = a[i];}if (a[i] < min){min = a[i];}}int range = max - min + 1;//统计计数int* countA = (int*)malloc(sizeof(int) * range);if (countA == NULL){perror("malloc fail");return;}memset(countA, 0, sizeof(int) * range);for (int i = 0; i < n; i++){countA[a[i] - min]++;//映射的相对位置}//排序int j = 0;for (int i = 0; i < range; i++){while (countA[i]--){a[j] = i + min;j++;}}
}

9.补充:基数排序

基数排序也属于非比较排序

基数排序的操作步骤:

  1. 分发数据
  2. 回收数据

多关键字排序有两种方式:MSD(最高位优先)和LSD(最低位优先)

基数排序的步骤如下图:

10.总结:排序算法的复杂度及稳定性分析

稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,a[i] = a[j],且a[i] 在 a[j]之前,而在排序后的序列中,a[i] 仍在 a[j]之前,则称这种排序算法是稳定的,否则称之为不稳定的

1️⃣直接插入排序:稳定

关键码相同则不调整,继续向后排序

2️⃣希尔排序:不稳定

预排序时,相同的数据可能分到不同的组,不能保证稳定性

3️⃣选择排序:不稳定

4️⃣堆排序不稳定

当一个堆为单值时,向下调整会影响该值的稳定性,因此堆排序不稳定

5️⃣冒泡排序:稳定

关键码相同则不调整,继续向后排序

6️⃣快速排序:不稳定

7️⃣归并排序:稳定

关键码相同则不调整,继续向后排序

时间复杂度与空间复杂度总结:

算法平均情况最好情况最坏情况辅助空间稳定性
冒泡排序O(N^2)O(N)O(N^2)O(1)稳定
简单选择排序O(N^2)O(N^2)O(N^2)O(1)不稳定
直接插入排序O(N^2)O(N)O(N^2)O(1)稳定
希尔排序O(N*logN)~O(N^2)O(N^1.3)O(N^2)O(1)不稳定
堆排序O(N*logN)O(N*logN)O(N*logN)O(1)不稳定
归并排序O(N*logN)O(N*logN)O(N*logN)O(N)稳定
快速排序O(N*logN)O(N*logN)O(N^2)O(logN)~O(N)不稳定

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

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

相关文章

【Java】十年老司机转开发语言,新小白从学习路线图开始

欢迎来到《小5讲堂》 大家好&#xff0c;我是全栈小5。 这是《Java》序列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对知识点的理解和掌握…

【HTML5】 canvas 绘制图形

文章目录 一、基本用法二、用法详见2.0、方法属性2.1、绘制线条2.2、绘制矩形2.3、绘制圆形2.4、绘制文本2.5、填充图像 一、基本用法 canvas 标签&#xff1a;可用于在网页上绘制图形&#xff08;使用 JavaScript 在网页上绘制图像&#xff09;画布是一个矩形区域&#xff0c…

决战排序之巅(二)

决战排序之巅&#xff08;二&#xff09; 排序测试函数 void verify(int* arr, int n) 归并排序递归方案代码可行性测试 非递归方案代码可行性测试 特点分析 计数排序代码实现代码可行性测试 特点分析 归并排序 VS 计数排序&#xff08;Release版本&#xff09;说明1w rand( ) …

1 python计算机基础

计算机基础和环境搭建 1 计算机基础和环境搭建1.计算机基础1.1 基本概念1.2 编程语言1.3 编译器/解释器 2.学习编程的本质3.Python的介绍3.1 语言的分类3.2 Python3.3 Python的解释器种类&#xff08;了解&#xff09;3.4 CPython解释器的版本 4.环境搭建4.1 安装Python解释器4…

前端架构师需要具备哪些能力?

文章目录 公司一工作职责岗位要求 公司二岗位职责任职要求 公司三岗位职责任职要求 公司四工作职责任职要求 公司五职位职责任职要求 前端架构师需要具备的能力 我们先看看前端架构师的招聘要求。 公司一 工作职责 1、参与项目需求分析评审&#xff0c;负责核心功能详细设计…

计算机网络-VLAN间通信

之前复习了VLAN的概念以及几个接口类型。VLAN在二层可以实现广播域的划分&#xff0c;VLAN间可以实现二层通信&#xff0c;但是不能实现三层通信&#xff0c;需要借助其它方式。 一、概述 实际网络部署中一般会将不同IP地址段划分到不同的VLAN。同VLAN且同网段的PC之间可直接进…

1月17日代码随想录合并二叉树

617.合并二叉树 给你两棵二叉树&#xff1a; root1 和 root2 。 想象一下&#xff0c;当你将其中一棵覆盖到另一棵之上时&#xff0c;两棵树上的一些节点将会重叠&#xff08;而另一些不会&#xff09;。你需要将这两棵树合并成一棵新二叉树。合并的规则是&#xff1a;如果两…

ElasticSearch概述+SpringBoot 集成ES

ES概述 开源的、高扩展的、分布式全文检索引擎【站内搜索】 解决问题 1.搜索词是一个整体时&#xff0c;不能拆分&#xff08;mysql整体连续&#xff09; 2.效率会低&#xff0c;不会用到索引&#xff08;mysql索引失效&#xff09; 解决方式 进行数据的存储&#xff08;只存储…

支持华为GaussDB数据库的免费开源ERP:人力资源管理解决方案概述

开源智造所推出的Odoo SuperPeople数字化解决方案将HR和薪资数据与财务、项目规划、预算和采购流程连接起来&#xff0c;消除了多套系统给企业带来的信息孤岛问题。 ——复星集团 人力资源中心 高经理 一种更具吸引力、更有洞察力的人员管理方式 什么是开源智造Odoo的人力资源…

信驰达科技参与《汽车玻璃集成UWB数字钥匙发展研究白皮书》编制工作

为进一步探索汽车数字钥匙技术路线及开发思路&#xff0c;中国智能网联汽车产业创新联盟&#xff08;CAICV&#xff09;、福耀玻璃工业集团股份有限公司联合发起了《汽车玻璃集成UWB数字钥匙发展研究白皮书》研究工作。 2023年12月20日&#xff0c;由中国智能网联汽车产业创新…

Linux--部署 Tomcat 及其负载均衡

1.案例前置知识点 1&#xff09;Tomcat简介 名称由来&#xff1a;Tomcat最初是由 Sun的软件构架师詹姆斯邓肯戴维森开发的。后来他帮助将其变 为开源项目&#xff0c;并由Sun贡献给Apache软件基金会。由于大部分开源项目OReilly都会出一本相关的 书&#xff0c;并且将其封面设…

2024年第二届“华数杯”国际大学生数学建模竞赛 (A题 MCM)| 废水扩散分析 |数学建模完整代码+建模过程全解全析

当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2022年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题。 让我们来看看华数杯的A题&#xff01; 完整内容可以在文章末…

OpenCV-Python(34):FAST算法

目标 理解 FAST 算法的基础使用OpenCV 中的FAST 算法相关函数进行角点检测 介绍 FAST算法&#xff08;Features from Accelerated Segment Test&#xff09;是一种用于在图像中快速检测角点的算法。它是一种基于像素的检测方法&#xff0c;具有高效、准确的特点&#xff0c;常…

基于信号完整性的PCB设计原则

最小化单根信号线质量的一些PCB设计建议 1. 使用受控阻抗线&#xff1b; 2. 理想情况下&#xff0c;所有信号都应该使用完整的电源或地平面作为其返回路径&#xff0c;关键信号则使用地平面作为返回路径&#xff1b; 3. 信号的返回参考面发生变化时&#xff0c;在尽可能接近…

压力测试+接口测试(工具jmeter)

jmeter是apache公司基于java开发的一款开源压力测试工具&#xff0c;体积小&#xff0c;功能全&#xff0c;使用方便&#xff0c;是一个比较轻量级的测试工具&#xff0c;使用起来非常简单。因 为jmeter是java开发的&#xff0c;所以运行的时候必须先要安装jdk才可以。jmeter是…

几内亚ECTN是什么?怎么办理?建议收藏!

几内亚ECTN是什么&#xff1f;怎么办理&#xff1f;建议收藏&#xff01; 一、去往几内亚的货物&#xff0c;从六月一日开始强制实施ECTN制度&#xff0c;取消原来并行的ENS制度。如若货物到港前没申请ECTN&#xff0c;几内亚海关将会强行扣货。 ECTN是英文&#xff1a;ELECTR…

Angular系列教程之自定义指令

文章目录 前言指令的基本概念在模板中使用指令总结 前言 在Angular中&#xff0c;指令是一种非常强大的工具&#xff0c;用于扩展HTML元素的功能和行为。它们允许我们创建可重用的组件&#xff0c;并在应用程序中的多个地方使用它们。本文将介绍Angular指令的基础知识&#xf…

散列函数,哈希表hash table

附上一句话&#xff1a;我知道大家可能曾经了解过这个散列表了&#xff0c;我发现&#xff0c;如果多看几个相关的视频&#xff0c;从不同的表述方式和不同的理解角度来理解这个问题&#xff0c;我会明白的更透彻&#xff0c;也有更多新的收获&#xff0c;尤其是对这个算法的应…

ElasticSearch降本增效常见的方法 | 京东云技术团队

Elasticsearch在db_ranking 的排名不断上升&#xff0c;其在存储领域已经蔚然成风且占有非常重要的地位。 随着Elasticsearch越来越受欢迎&#xff0c;企业花费在ES建设上的成本自然也不少。那如何减少ES的成本呢&#xff1f;今天我们就特地来聊聊ES降本增效的常见方法&#x…

Android 仿快手视频列表,RecyclerView与Banner联动效果

这是看到群里讨论过快手APP的一个观看他人视频列表的一个联动效果&#xff0c;但是并不是完全按照这个软件的效果来做的&#xff0c;只是参考&#xff0c;并不是完全仿照这个软件来做的&#xff0c;没时间去优化排版问题了&#xff0c;请见谅&#xff0c;如图&#xff1a; 实现…