下面我们将讲述七大基于比较的排序算法的基本原理及实现。并从稳定性、时间复杂度、空间复杂度3种性能对每种排序进行分析。
重点:快速排序和堆排序;难点:快速排序和归并排序

目录

一、排序概念

二、常见排序算法的实现

2.1 插入排序

2.1.1 直接插入排序

2.1.2 希尔排序(缩小增量排序)

2.2 选择排序

2.2.1 直接选择排序

2.2.2 直接选择排序优化

2.2.3 堆排序

2.3 交换排序

2.3.1 冒泡排序

2.3.2 快速排序

1、Hoare 法

2、🔴挖坑法

3、前后指针

2.3.3 快速排序优化

1、三数取中法

2、减少递归深度

2.3.4 快速排序非递归

2.4 归并排序

2.4.1 

2.4.2 归并排序非递归实现

三、特性总结

四、其他非基于比较排序

4.1 计数排序


一、排序概念

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

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

外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求在内外村之间移动数据的排序,例如磁盘上的排序。

二、常见排序算法的实现

2.1 插入排序

2.1.1 直接插入排序

        基本思想:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。例如玩扑克牌前对手里的牌进行整理。

比如现在你手上有一副卡牌,不看花色:10、2、9、4、7,从左往右的第2张发现比第一张小,因此将其抽出插到第一张的前面。

public class Sort {public static void insertSort(int[] array){for (int i = 1; i < array.length; i++) {int tmp = array[i];int j = i-1;for (; j >= 0; j--) {if (array[j] > tmp) {  // 若此处加了 = ,则得到的数据不稳定,但实际上直接插入排序法是稳定的array[j + 1] = array[j];}else{array[j + 1] = array[j];break;}}array[j+1] = tmp;}}
}

直接插入排序的特性:

1. 元素集合越接近有序,直接插入排序算法的时间效率越高,时间复杂度为O(N);

2. 时间复杂度:最坏情况下是 O(N²);

3. 空间复杂度:O(1),没有开辟空间;

4. 稳定性:稳定。

2.1.2 希尔排序(缩小增量排序)

        基本思想:先选定一个整数作为增量,把待排序文件中的所有数据按照这个整数为距离分成多个组,即所有距离为增量的数据分在同一组内,将每一组的数据进行排序。然后,取另一个小于原来增量的整数(可以是原来的 1/2 或者 1/3+1),重复上述的分组和排序规则,当增量 = 1时,所有数据在同一组内排好序。是直接插入排序的优化,所以直接插入排序的代码可以直接拿来修改。如下图

    public static void shellSort(int[] array){int gap = array.length;while (gap > 1){gap /= 2;  // 最后都能等于1shell(array, gap);}}private static void shell(int[] array, int gap) {for (int i = gap; i < array.length; i++) {int tmp = array[i];int j = i-gap;for (; j >= 0 ; j -= gap) {if (array[j] > tmp){array[j+gap] = array[j];}else{array[j+gap] = tmp;break;}}array[j+gap] = tmp;}}

希尔排序的特性:

1. 希尔排序是对直接插入排序的优化。

2. 当 gap > 1 时都是预排序,目的是让数组更接近于有序。当 gap == 1 时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。

3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些书中给出的希尔排 、序的时间复杂度都不固定,归纳总结其时间复杂度的范围在O(n^1.3 ~ n^1.5)之间;

4、空间复杂度:O(1);

5、稳定性:不稳定。


        性能测试:main() 方法中只有 testEfficiency() 测试数组是有序的或者无序时,各种排序方法的效率;而 testSimple() 则是查看是否排序成功。

package sort;import java.util.Arrays;
import java.util.Random;public class Test {public static void orderArray(int[] array){    // 初始化有序的数组for (int i = 0; i < array.length; i++) {// 顺序array[i] = i;// 倒序//array[i] = array.length-1-i;}}public static void notOrderArray(int[] array){  // 初始化无序的数组Random random = new Random();for (int i = 0; i < array.length; i++) {array[i] = random.nextInt(10_0000);  // 生成0到10万的随机数}}public static void testInsertEfficiency(int[] array){array = Arrays.copyOf(array, array.length);     // 不改变原来数组的顺序,而是将排序后的新的数组复制一份long startTime = System.currentTimeMillis();Sort.insertSort(array);long finishTime = System.currentTimeMillis();System.out.println("直接插入排序耗时:"+(finishTime-startTime));}public static void testShellEfficiency(int[] array){array = Arrays.copyOf(array, array.length);     // 不改变原来数组的顺序,而是将排序后的新的数组复制一份long startTime = System.currentTimeMillis();Sort.shellSort(array);long finishTime = System.currentTimeMillis();System.out.println("希尔排序耗时:"+(finishTime-startTime));}public static void main(String[] args) {//testSimple();      // 测试例子给的数组testEfficiency();  // 测试耗时效率}public static void testSimple(){int[] array = {9,3,4,7,1,5,8,6,5,2};System.out.println("排序前:"+Arrays.toString(array));Sort.shellSort(array);System.out.println("排序后:"+Arrays.toString(array));}public static void testEfficiency(){int[] array = new int[10_0000];  // 数组元素个数为10万orderArray(array);//notOrderArray(array);testInsertEfficiency(array);testShellEfficiency(array);}
}

        对于有序的数组,直接插入排序耗时要比希尔排序短一点,但差不了多少,而如果是对无序的数组进行排序,希尔排序耗时要远比直接插入排序耗时短,几乎达到1:100。

2.2 选择排序

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

2.2.1 直接选择排序

假设0下标的元素为最小值,在后面的元素中找到实际最小值,然后交换……

    public static void DirectSelectSort(int[] array){for (int i = 0; i < array.length; i++) {int minIndex = i;for (int j = i+1; j < array.length; j++) {if (array[j] < array[minIndex]){minIndex = j;}}swap(array, i, minIndex);}}private static void swap(int[] array, int i, int j) {int tmp = array[i];array[i] = array[j];array[j] = tmp;}

直接选择排序的特性:

1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用,因此重要的是算法思想;
2. 时间复杂度:O(n²),和数组是否有序无关
3. 空间复杂度:O(1)
4. 稳定性:不稳定


2.2.2 直接选择排序优化

从左端点开始向右找到最小值将下标存到 minIndex 中,找到最大值并将下标存到 maxIndex 中。

上图的步骤看似没毛病,编写代码如下:

    public static void selectSort(int[] array){int left = 0;int right = array.length-1;while (left < right) {int minIndex = left;int maxIndex = left;for (int i = left+1; i <= right; i++) {if (array[i] > array[maxIndex]){maxIndex = i;}if (array[i] < array[minIndex]){minIndex = i;}}swap(array,left,minIndex);swap(array,right,maxIndex);left++;right--;}}

但是如果我们换一组数据进行测试,得到的结果如下:

所以在更换完最小值之后需要增加一项条件。

    public static void selectSort(int[] array){int left = 0;int right = array.length-1;while (left < right) {int minIndex = left;int maxIndex = left;for (int i = left+1; i <= right; i++) {if (array[i] > array[maxIndex]){maxIndex = i;}if (array[i] < array[minIndex]){minIndex = i;}}swap(array,left,minIndex);// 如果最大值正好是 left 下标,那么交换 left 之后,最大值给到 minIndex 下标if (maxIndex == left){maxIndex = minIndex;}swap(array,right,maxIndex);left++;right--;}}

对于无序的数组,选择排序的耗时如下:

2.2.3 堆排序

利用堆这种数据结构来实现排序,需要注意的是升序建大根堆,降序则是建小根堆。

我的Java集合中的优先级队列(堆)中有提及。

    public static void heapSort(int[] array){createHeap(array);  // 1、建大根堆int end = array.length-1;while (end > 0){swap(array,0,end);  // 2、交换根节点和最后一个节点的值// 3、将除了最后一个元素(已是最大值)的其他节点重新整理成大根堆siftDown(array,0,end);end--;  // 4、倒着重复上面的操作}}public static void createHeap(int[] array){int parent = (array.length-1-1)/2;for (; parent >= 0; parent--) {siftDown(array,parent,array.length);}}private static void siftDown(int[] array, int parent, int size) {int child = 2*parent+1;while (child < size){if(child+1 < size && array[child+1] > array[child]){child++;}if (array[child] > array[parent]){swap(array,child,parent);parent = child;child = 2*parent+1;}else{break;}}}

堆排序的特性:

1. 堆排序使用堆来选数,效率就高了很多;

2. 时间复杂度:O(N*log₂N) ,(目前效率最快的排序方法)

3. 空间复杂度:O(1)

4. 稳定性:不稳定

2.3 交换排序

        基本思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

2.3.1 冒泡排序

关键词:趟数、循环嵌套、j+1不要越界

    public static void bubbleSort(int[] array){for (int i = 0; i < array.length-1; i++) {boolean tag = false;for (int j = 0; j < array.length-1-i; j++) {if (array[j] > array[j+1]){swap(array,j, j+1);tag = true;}}if (!tag){break;}}}

1、时间复杂度 O(N²)

        【一般讨论没有优化情况下的时间复杂度,即没有boolean元素和 -i 的操作】

        【优化之后,时间复杂度可以达到 O(N)】

2、空间复杂度 O(1)

3、稳定性:稳定

2.3.2 快速排序

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

    public static void quickSort(int[] array){quick(array,0,array.length-1);}public static void quick(int[] array, int start, int end){if (start >= end){  // 递归结束条件return;}//按照基准值对数组进行分割//int pivot = partitionHoare(array,start,end);          // Hoare//int pivot = partitionDig(array,start,end);            // 挖坑//int pivot =partitionDoublePointer(array,start,end);   // 双指针解法1int pivot = partitionDoublePointer2(array,start,end);   // 双指针解法2// 递归左边的quick(array, start, pivot-1);// 递归右边的quick(array,pivot+1,end);}

        上述为快速排序递归实现的主框架,与二叉树前序遍历规则非常像,下面的3种分割方法都是在分割之后应用上面的递归方法进行排序的(如下图右侧内容),因此下面只讨论不同分割方法的主要思想。

1、Hoare 法

    public static void quickSort(int[] array){quick(array,0,array.length-1);}public static void quick(int[] array, int start, int end){if (start >= end){  // 递归结束条件return;}int pivot = partitionHoare(array,start,end);quick(array, start, pivot-1);quick(array,pivot+1,end);}private static int partitionHoare(int[] array, int left, int right){int tmp = array[left];int tmpIndex = left;while(left < right){while (left < right && array[right] >= tmp){right--;}while (left < right && array[left] <= tmp){left++;}swap(array, left, right);}swap(array,left,tmpIndex);return left;}

问题:画图理解 / 调试解决

        1、为什么从后面开始找,而不是从前面开始找?

        2、为什么在 while 条件中有 等于号?


快排序的特性:

1、时间复杂度:

        最坏情况下:当给定数据是1 2 3 4 5 6…… / ……9 8 7 6 5 4 3 2 1这种有序的情况下,是O(N²);

        最好情况下:是 O(N*log₂N),对于满二叉树来说,对上面的代码进行优化后可实现。

2、空间复杂度:递归一定会开辟内存

        最坏情况下: O(N),单分支二叉树;

        最好情况下: O(log₂N),对于满二叉树,递归完左边再递归右边时,左边开辟的内存会被回收。

3、稳定性:不稳定

2、🔴挖坑法

如果在题目中遇到快速排序的结果,但没有明确指出是哪种快排时,优先考虑挖坑法。

    public static void quickSort(int[] array){quick(array,0,array.length-1);}public static void quick(int[] array, int start, int end){if (start >= end){  // 递归结束条件return;}int pivot = partitionDig(array,start,end);quick(array, start, pivot-1);quick(array,pivot+1,end);}private static int partitionDig(int[] array, int left, int right){int key = array[left];while(left < right){while (left < right && array[right] >= key){right--;}array[left] = array[right];while (left < right && array[left] <= key){left++;}array[right] = array[left];}array[left] = key;return left;}
3、前后指针

两种分割的方法:


        【对于有序的数组】当我们在测试快排序的效率时,程序崩溃了,这是因为递归需要开辟内存空间,对于十万如此大量的且有序的数据就得开辟十万个空间。而将数据量减小,比如1万,并不会崩,但是效率还是比较低。

2.3.3 快速排序优化

1、三数取中法

在前面测试有序数组时程序崩溃了,这是因为递归有n个节点的单分支的二叉树需要开辟n个内存,那么如果我们找到有序数组的中间值并将其作为分割的基准值,就能将有序数组分为两部分。

    public static void quick(int[] array, int start, int end){if (start >= end){  // 递归结束条件return;}// 三数取中法优化,有序的数组不再是单分支的二叉树了//System.out.println("start: " + start + " end: " + end);  // 打印也会耗时int midIndex = getMiddleIndex(array, start, end);swap(array,start,midIndex);//int pivot = partitionHoare(array,start,end);          // Hoareint pivot = partitionDig(array,start,end);            // 挖坑//int pivot =partitionDoublePointer(array,start,end);   // 双指针解法1//int pivot = partitionDoublePointer2(array,start,end);   // 双指针解法2quick(array, start, pivot-1);quick(array,pivot+1,end);}private static int getMiddleIndex(int[] array, int start, int end) {int mid = (start+end)/2;if (array[start] < array[end]) {if (array[mid] < array[start]) {return start;} else if (array[mid] > array[end]) {return end;} else {return mid;}} else {if (array[mid] > array[start]) {return start;} else if (array[mid] < array[end]) {return end;} else {return mid;}}}
2、减少递归深度

        因为在多次的预排序后数组已经趋于有序了,所以当递归到小的子区间时,可以考虑使用插入排序。

比如当数据量剩下的范围是0到7之间时,直接使用插入排序,注意修改原来编写的插入排序的开始和结束范围,以及使用插入排序之后及时结束进程:

    public static void quick(int[] array, int start, int end){if (start >= end){  // 递归结束条件return;}// 减少递归深度优化if (end - start <= 7){insertSortRage(array, start, end);return;    // 记得此处要结束循环了}// 三数取中法优化,有序的数组不再是单分支的二叉树了//System.out.println("start: " + start + " end: " + end);  // 打印也会耗时int midIndex = getMiddleIndex(array, start, end);swap(array,start,midIndex);//int pivot = partitionHoare(array,start,end);          // Hoareint pivot = partitionDig(array,start,end);            // 挖坑//int pivot =partitionDoublePointer(array,start,end);   // 双指针解法1//int pivot = partitionDoublePointer2(array,start,end);   // 双指针解法2quick(array, start, pivot-1);quick(array,pivot+1,end);}private static void insertSortRage(int[] array, int start, int end) {for (int i = start+1; i <= end; i++) {int tmp = array[i];int j = i-1;for (; j >= start; j--){if (array[j] > tmp){array[j+1] = array[j];}else{array[j+1] = tmp;break;}}array[j+1] = tmp;}}

2.3.4 快速排序非递归

        基本思想:使用一个栈来存放分割后要使用二叉树数据结构进行排序的开始下标和结束下标。再拿出下标进行分割,就是不断地分割、存放下标、拿出下标、分割……的过程。

    public static void quickSort(int[] array){// 非递归的方法quickNor(array,0,array.length-1);}private static void quickNor(int[] array, int start, int end) {Deque<Integer> stack = new ArrayDeque<>();int pivot = partitionDig(array,start,end);// 第一次分割后存放下标if (pivot > start+1){stack.push(start);stack.push(pivot-1);}if (pivot < end-1){stack.push(pivot+1);stack.push(end);}// 拿出下标再进行分割while (!stack.isEmpty()){end = stack.pop();start = stack.pop();pivot = partitionDig(array,start,end);if (pivot > start+1){stack.push(start);stack.push(pivot-1);}if (pivot < end-1){stack.push(pivot+1);stack.push(end);}}}

快速排序的特性:

1、 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序;

2、时间复杂度:O(N*logN);

3、空间复杂度:O(logN),即树的高度;

4. 稳定性:不稳定。

2.4 归并排序

2.4.1 

        基本思想:归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤:

    public static void mergeSort(int[] array){mergeSortTmp(array, 0, array.length-1);}public static void mergeSortTmp(int[] array, int left, int right){if (left >= right){return;}int mid = (left + right)/2;mergeSortTmp(array, left, mid);mergeSortTmp(array, mid+1, right);// 到这里分解结束// 下面进行合并merge(array,left,mid,right);}private static void merge(int[] array, int left, int mid, int right) {int[] tmp = new int[right-left+1];int k = 0;int s1 = left;//int e1 = mid;int s2 = mid+1;//int e2 = right;while (s1 <= mid && s2 <= right){if (array[s1] < array[s2]){tmp[k++] = array[s1++];}else{tmp[k++] = array[s2++];}}while (s1 <= mid){   // 要用 while 的原因是有可能有多组tmp[k++] = array[s1++];}while (s2 <= right){tmp[k++] = array[s2++];}for (int i = 0; i < k; i++) {array[i+left] = tmp[i];  // +left处理巧妙解决右边下标对应问题}}

归并排序特性:

1、归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外部排序问题;

2、时间复杂度:O(N*log₂N);

3、空间复杂度:O(N)

4、稳定性:稳定

海量数据的排序问题:

外部排序:排序过程需要在磁盘等外部存储进行的排序

前提:内存只有 1G,需要排序的数据有 100G

        因为内存中因为无法把所有数据全部放下,所以需要外部排序,而归并排序是最常用的外部排序

1. 先把文件切分成 200 份,每个 512 M

2. 分别对 512 M 排序,因为内存已经可以放的下,所以任意排序方式都可以

3. 进行 2路归并,同时对 200 份有序文件做归并过程,最终结果就有序了

2.4.2 归并排序非递归实现

        基本思想:将数组多次分解之后合并为一整个数组进行排序。

    private static void merge(int[] array, int left, int mid, int right) {  // 合并int[] tmp = new int[right-left+1];int k = 0;int s1 = left;//int e1 = mid;int s2 = mid+1;//int e2 = right;while (s1 <= mid && s2 <= right){if (array[s1] < array[s2]){tmp[k++] = array[s1++];}else{tmp[k++] = array[s2++];}}while (s1 <= mid){   // 要用 while 的原因是有可能有多组tmp[k++] = array[s1++];}while (s2 <= right){tmp[k++] = array[s2++];}for (int i = 0; i < k; i++) {array[i+left] = tmp[i];}}// 归并排序非递归public static void mergeSortNor(int[] array){int gap = 1;while (gap < array.length) {for (int i = 0; i < array.length; i = i + gap * 2) {int left = i;int mid = left + gap - 1;int right = mid + gap;merge(array, left, mid, right);  // 合并}gap *= 2;}}

上面的 mergeSortNor() 代码看似没有问题,但是如果是如下情况,该如何完善?

    public static void mergeSortNor(int[] array){int gap = 1;while (gap < array.length) {for (int i = 0; i < array.length; i = i + gap * 2) {int left = i;int mid = left + gap - 1;if (mid >= array.length){  // 处理越界情况mid = array.length -1;}int right = mid + gap;if (right >= array.length){right = array.length -1;}merge(array, left, mid, right);  // 合并}gap *= 2;}}

三、特性总结

堆排序、快速排序、归并排序时间复杂度都是 O(N*log₂N);但相比空间复杂度,堆排序占优势

插入排序、冒泡排序和归并排序都是稳定的;

排序方法最好平均最坏空间复杂度稳定性
冒泡排序O(n²)(优化之后能达到O(n))O(n²)O(n²)O(1)稳定
插入排序O(n)O(n²)O(n²)O(1)稳定

选择排序

O(n²)O(n²)O(n²)O(1)不稳定
希尔排序O(n)O(n^1.3)O(n^1.5)O(1)不稳定
堆排序O(n * log₂n)O(n * log₂n)O(n * log₂n)O(1)不稳定
快速排序O(n * log₂n)O(n * log₂n)O(n²)O(log₂n)~O(n)不稳定
归并排序O(n * log₂n)O(n * log₂n)O(n * log₂n)O(n)稳定

一些操作关键词:

四、其他非基于比较排序

4.1 计数排序

应用场景:

        集中在某个范围内的一组数据。

操作步骤:

        1、新建一个计数数组来利用下标统计相同元素出现的次数;

        2、根据统计的结果将序列回收到原来的序列中。

    public static void countSort(int[] array){// 1、找出数组中的最大值和最小值,得到申请空间的大小int max = array[0];int min = array[0];for (int i = 1; i < array.length; i++) {if (array[i] > max){max = array[i];}if (array[i] < min){min = array[i];}}int len = max - min + 1;int[] count = new int[len];// 2、遍历数组计算出现的次数for (int i = 0; i < array.length; i++) {int index = array[i];count[index-min]++;}// 3、将计数数组中的数据按顺序拿出放到 arrayint index = 0;for (int i = 0; i < count.length; i++) {while (count[i] != 0) {array[index] = i + min;index++;count[i]--;}}}

计数排序的特性:

1、计数排序在数据范围集中时,效率很高,但是适用范围及场景有限。

2、时间复杂度:O(MAX(N,范围))

3、空间复杂度:O(范围)

4、稳定性:稳定

其他:桶排序、基数排序。

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

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

相关文章

RabbitMQ:SpringAMQP 多消费者绑定同一队列

目录一、案例需求二、代码实现三、总结生产者源码 消费者源码 一、案例需求 模拟WorkQueue&#xff0c;实现一个队列绑定多个消费者。 在RabbitMQ的控制台创建一个队列&#xff0c;命名为work.queue。在生产者服务中定义测试方法&#xff0c;在1s内产生50条消息&#xff0c;…

Java技术总监的成长之路(技术干货分享)

以下是针对 ​Java技术总监​ 在 Linux 环境下搭建企业级开发环境的完整指南&#xff0c;涵盖 JDK 配置、工程工具链、协作平台及性能优化方案&#xff1a; 本文章仅提供学习&#xff0c;切勿将其用于不法手段&#xff01; 一、核心环境搭建 1. ​JDK 安装与调优​ ​版本选择…

C++代码解释:实现一个 mystring 类,用于表示字符串,实现构造函数,默认构造长度为 10 的空间,提供打印字符串,获取空间大小,修改内容的成员函数

题目代码#include <cstring> // 包含字符串处理函数库&#xff0c;如strlen、strncpy等 #include <iostream> // 包含输入输出流库&#xff0c;用于cout等操作 using namespace std; // 使用标准命名空间&#xff0c;避免重复书写std::class mystring { // 定…

如何解决IDEA/Datagrip无法连接数据库的问题:解决方法为添加参数-Djava.net.preferIPv4Stack=true

如何解决IDEA/Datagrip无法连接数据库的问题&#xff1a;解决方法为添加参数-Djava.net.preferIPv4Stacktrue 引言 在开发过程中&#xff0c;我们常常使用集成开发环境&#xff08;IDE&#xff09;如 IntelliJ IDEA 或 JetBrains DataGrip 来与数据库进行交互。然而&#xff…

走进数字时代,融入数字生活,构建数字生态

一、准备在IT行业深耕十七年&#xff0c;始终专注于企业生产经营中的实际应用问题&#xff0c;历经开发、测试、运维、实施、架构设计等多个技术岗位&#xff0c;并参与肉制品的生产与销售业务&#xff0c;推进了企业主业的市场管理落地&#xff0c;积累了业务与信息技术融合的…

【Vue开发】在Vite+Vue3项目中实现离线Iconify图标方案

在ViteVue3项目中实现离线Iconify图标方案 项目背景 当前项目需要部署到无网络连接的离线环境&#xff0c;因此需要将Iconify图标集打包到项目构建结果中&#xff0c;实现完全离线使用。 技术环境 框架: Vue 3构建工具: Vite核心依赖:"iconify/json": "^2.2…

Kotlin 协程之Channel

前言 在之前的文章中&#xff0c;我们已经知道了协程的启动、挂起、取消、异常以及常用的协程作用域等基础应用。 这些基础应用适合的场景是一次性任务&#xff0c;执行完就结束了的场景。 launch / async 适合的场景 网络请求数据库查询文件读写并行计算任务等等 Channel …

linux系统装google chrome,amd64

google chrome官网最下边其他平台&#xff0c;linux 查看自己的系统架构&#xff08;用下边这行代码查看&#xff09;&#xff0c;看看是amd还是 &#xff0c;我的显示amd64&#xff0c;amd对应.deb,rpm对应x86 &#xff0c;选择下载 dpkg --print-architecture 然后 sudo…

【C++基础】C++ 中const与volatile关键字深度解析:从面试考点到底层实现

在 C 开发岗位的面试中&#xff0c;const与volatile关键字是高频考点之一。这两个关键字看似简单&#xff0c;但实际上蕴含着丰富的语义和底层机制。本文从基础语法到高级应用&#xff0c;结合大厂真题&#xff0c;深入解析这两个关键字的奥秘。一、const关键字&#xff1a;常量…

达梦分布式集群DPC_故障分析_yxy

达梦分布式集群DPC_节点故障分析1 DPC核心概念回顾2 场景1-主库故障3 场景2-少数备库故障4 场景3-多数节点故障4.1 多数节点故障&#xff08;包括主库&#xff09;4.2 多数备库节点故障&#xff08;不包括主库&#xff09;1 DPC核心概念回顾 达梦分布式集群DPC&#xff0c;基于…

【高并发内存池】一、简介 定长内存池实现

文章目录Ⅰ. 项目介绍1、这个项目要做什么2、项目的要求Ⅱ. 什么是内存池1、池化技术2、内存池3、mallocⅢ. 设计一个定长内存池1、定长内存池的概念2、实现如何实现定长❓❓❓如何绕开 malloc 向堆直接申请空间❓❓❓3、性能测试Ⅰ. 项目介绍 1、这个项目要做什么 tcmalloc源…

产品设计.原型设计

产品思维&#xff1a; 1. 产品定位&#xff1a;产品的具体的、用户画像&#xff1b; --什么样的人在什么环境下做什么事情的场景 2. 范围层: 发现、识别和决策需求。--识别真假需求&#xff0c;做ROI判断 3. 可复用的、MVP产品方案--要能复用的解决方案&#xff0c;最小可用产品…

vue3+element-plus 输入框el-input设置背景颜色和字体颜色,样式效果等同于不可编辑的效果

应用效果&#xff1a;代码&#xff1a;<template> ......<el-form-item label"文件编号" label-position"right"><el-input v-model"qualityFileForm.fileNo" clearable :disabled"!props.isNew" /></el-form-it…

[ CSS 前端 ] 网页内容的修饰

目录 一. CSS 1. 概述 2. 基本语法 (1)行内样式表 (2)内嵌样式表 (3)外部样式表 3. 选择器 (1)标签选择器: (2)类选择器: (3)通配选择器: (4)后代选择器: 4. 基础样式 (1). 文本样式 (2). 背景样式 (3). 列表样式 5. 伪类 (1)定义: (2)伪类的语法&#xff1a; …

全面深入了解榛树游戏引擎

本文还有配套的精品资源&#xff0c;点击获取 简介&#xff1a;榛树游戏引擎&#xff08;Hazel&#xff09;是一款专为游戏开发设计的先进软件工具&#xff0c;它集成了多种功能&#xff0c;支持现代图形API&#xff0c;具有高性能的物理模拟系统和易学易用的脚本语言&#…

“大模型”技术专栏 | 浅谈基于 Kubernetes 的 LLM 分布式推理框架架构:概览

编者按&#xff1a;人工智能正以前所未有的渗透力重塑生产与生活图景。作为国内领先的数据智能科技企业&#xff0c;和鲸科技自 2015 年成立以来&#xff0c;深耕人工智能与数据科学&#xff0c;历经十年发展&#xff0c;已在气象、教育、医疗、航空航天、金融、通信、能源、零…

【JS】认识并实现一个chrome扩展程序

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍chrome扩展程序。 学其所用&#xff0c;用其所学。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c;下次更新不迷路&#…

jeecgboot项目遇见的一些问题:

1.当你想修改项目的标题&#xff0c;前端将jeecgboot改成你想要的标题的时候&#xff0c;去前端的.env文件中进行修改。图1 修改标题根据路径找到文件&#xff0c;将网站标题改成自己需要的就可以正常显示了。图2 显示前图3 显示后2.在动态数组中&#xff0c;如果你知道数组需要…

项目里程碑设定有哪些方法

要科学设定项目里程碑&#xff0c;可采用以下几种方法&#xff1a;基于项目阶段划分法、关键交付物导向法、依赖关系链分析法、时间驱动法、风险节点识别法、目标成果导向法、资源约束分析法、客户验收节点设定法。其中&#xff0c;关键交付物导向法尤为实用。该方法以项目中必…

英伟达显卡驱动怎么更新 详细步骤教程

英伟达显卡驱动程序对于电脑的图形性能至关重要&#xff0c;它能确保显卡在游戏、设计、视频渲染等方面发挥最大性能。如果驱动过旧&#xff0c;可能会导致游戏运行不畅、软件不兼容&#xff0c;甚至系统出现错误。因此&#xff0c;定期更新英伟达显卡驱动非常必要。下面将为大…