当前位置: 首页 > news >正文

Java算法——排序

目录

  • 引言
  • 1. 插入排序
    • 1.1 基本思想
    • 1.2 直接插入排序
    • 1.3 希尔排序
  • 2. 选择排序
    • 2.1 基本思想
    • 2.2 直接选择排序
    • 2.3 直接选择排序变种
    • 2.4 堆排序
  • 3. 交换排序
    • 3.1 基本思想
    • 3.2 冒泡排序
    • 3.3 快速排序
      • 3.3.1 快速排序的基本结构
      • 3.3.2 Hoare法
      • 3.3.3 挖坑法
      • 3.3.4 双指针法
    • 3.4 快速排序非递归法
    • 3.5 快速排序分析
  • 4. 归并排序
    • 4.1 基本思想
    • 4.1 归并排序递归
    • 4.2 归并排序非递。
  • 5. 不基于比较的排序
    • 5.1 计数排序
  • 6. 总结

引言

在一些场景中,我们需要对数据进行排序,就如之前提到的冒泡排序,这篇文章将讲述一些主流的排序算法。

1. 插入排序

1.1 基本思想

插入排序的基本思想是将数组分为已排序和未排序两部分,逐步将未排序部分的元素插入到已排序部分的适当位置,直到整个数组有序。

1.2 直接插入排序

// 插入排序
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 {break;}}array[j + 1] = tmp;}
}
  1. 从数组的第二个元素开始(索引为1),认为第一个元素是已排序的。 取出当前元素 tmp,并将其与已排序部分的元素从后向前比较。
  2. 如果已排序部分的元素大于 tmp,则将该元素向后移动一位。
  3. 重复上述步骤,直到找到一个不大于 tmp 的元素位置。
  4. 将 tmp 插入到该位置。
  5. 重复步骤2-5,直到所有元素都插入到已排序部分。

时间复杂度:O(n2)
空间复杂度:O(1)
稳定性:稳定

1.3 希尔排序

希尔排序的基本思想是通过将数组分成若干子序列分别进行插入排序,从而加快排序速度。它是对直接插入排序的一种改进。

// 希尔排序
public static void shellSort(int[] array) {int gap = array.length / 2;while (gap > 0) {shell(array, gap);gap /= 2;}
}public 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 {break;}}array[j + gap] = tmp;}
}
  1. shellSort 方法初始化间隔 gap 为数组长度的一半。
  2. 在 gap 大于0的情况下,调用 shell 方法对数组进行分组排序,然后将 gap 减半。
  3. shell 方法对每个间隔为 gap 的子序列进行插入排序。
  4. 在 shell 方法中,从索引 gap 开始,取出当前元素 tmp,并将其与间隔为 gap 的已排序部分的元素从后向前比较。
  5. 如果已排序部分的元素大于 tmp,则将该元素向后移动 gap 位。
  6. 重复上述步骤,直到找到一个不大于 tmp 的元素位置。
  7. 将 tmp 插入到该位置。
  8. 重复步骤4-7,直到所有子序列都排序完成。

希尔排序由于gap值不确定,时间复杂度并没有确切的值,但是时间复杂度是比直接插入排序要低的。
空间复杂度:O(1)
稳定性:不稳定

2. 选择排序

2.1 基本思想

每⼀次从待排序的数据元素中选出最⼩(或最⼤)的⼀个元素,存放在序列的起始位置,直到全部待
排序的数据元素排完。

2.2 直接选择排序

// 选择排序
public static void selectSort(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;}}if (minIndex != i) {swap(array, i, minIndex);}}
}// 辅助方法:交换数组中的两个元素
private static void swap(int[] array, int i, int j) {int temp = array[i];array[i] = array[j];array[j] = temp;
}
  1. 从数组的第一个元素开始,认为第一个元素是已排序的。
  2. 在未排序部分中找到最小的元素,并记录其索引 minIndex。
  3. 将最小元素与当前元素交换位置(如果 minIndex 不等于当前索引 i)。
  4. 重复步骤2-3,直到所有元素都排序完成。

时间复杂度:O(n2)
空间复杂度:O(1)
稳定性:不稳定

2.3 直接选择排序变种

直接选择排序的变种在每一趟排序中同时找到未排序部分的最小值和最大值,并分别将它们放到未排序部分的两端。

public static void selectSort2(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[minIndex]) {minIndex = i;}if (array[i] > array[maxIndex]) {maxIndex = i;}}if (minIndex != left) {swap(array, minIndex, left);}if (maxIndex == left) {maxIndex = minIndex;}if (maxIndex != right) {swap(array, maxIndex, right);}left++;right--;}
}// 辅助方法:交换数组中的两个元素
private static void swap(int[] array, int i, int j) {int temp = array[i];array[i] = array[j];array[j] = temp;
}
  1. 初始化两个指针 left 和 right,分别指向数组的两端。
  2. 在未排序部分中找到最小值和最大值的索引 minIndex 和 maxIndex。
  3. 将最小值与 left 位置的元素交换。
  4. 如果最大值的索引是 left,则更新 maxIndex 为 minIndex,因为最小值已经被交换到 left 位置。
  5. 将最大值与 right 位置的元素交换。
  6. 移动 left 和 right 指针,缩小未排序部分的范围。
  7. 重复步骤2-6,直到 left 和 right 相遇。

2.4 堆排序

堆排序是一种基于堆数据结构的排序算法,利用堆的性质来排序数组。

// 堆排序
public static void heapSort(int[] array) {createHeap(array);int end = array.length - 1;while (end > 0) {swap(array, 0, end);siftDown(array, 0, end);end--;}
}// 创建最大堆
public static void createHeap(int[] array) {for (int p = (array.length - 1 - 1) / 2; p >= 0; p--) {siftDown(array, p, array.length);}
}// 向下调整堆
private static void siftDown(int[] array, int p, int length) {int c = 2 * p + 1;while (c < length) {if (c + 1 < length && array[c] < array[c + 1]) {c++;}if (array[c] > array[p]) {swap(array, c, p);p = c;c = 2 * p + 1;} else {break;}}
}// 辅助方法:交换数组中的两个元素
private static void swap(int[] array, int i, int j) {int temp = array[i];array[i] = array[j];array[j] = temp;
}
  1. 创建最大堆:调用 createHeap 方法,将数组转换为最大堆。

     从最后一个非叶子节点开始,向上逐个节点进行向下调整(siftDown),确保每个子树都是最大堆。
    
  2. 排序过程:

     将堆顶元素(最大值)与当前未排序部分的最后一个元素交换。调整堆顶元素,使剩余部分重新成为最大堆(siftDown)。缩小未排序部分的范围,重复上述步骤,直到整个数组有序。
    

时间复杂度:O(nlogn)
空间复杂度:O(1)
稳定性:不稳定

3. 交换排序

3.1 基本思想

交换排序的基本思想是通过比较和交换数组中的元素,使数组逐步有序。

3.2 冒泡排序

冒泡排序通过重复地遍历数组,每次比较相邻的两个元素,如果它们的顺序错误就交换它们的位置。这样,每一趟遍历都会将当前未排序部分的最大元素“冒泡”到数组的末尾。重复这个过程,直到整个数组有序。

    //冒泡排序public static void bubbleSort(int[] array) {for (int i = 0; i < array.length; i++) {boolean flg = true;for (int j = 0; j < array.length - i - 1; j++) {if (array[j] > array[j + 1]) {swap(array, j, j + 1);flg = false;}}if (flg) {break;}}}
  1. 外层循环:遍历数组的每一个元素,控制排序的趟数。
  2. 内层循环:从数组的第一个元素开始,比较相邻的两个元素,如果前一个元素大于后一个元素,就交换它们的位置。
  3. 标志变量 flg:用于检测当前趟排序是否发生了交换。如果在某一趟排序中没有发生交换,说明数组已经有序,可以提前结束排序。
  4. 交换函数 swap:用于交换数组中的两个元素。

时间复杂度:O(n2)
空间复杂度:O(1)
稳定性:稳定

3.3 快速排序

快速排序的基本思想是通过选择一个枢轴元素,将数组分成两部分,使得左侧部分的所有元素都小于枢轴,右侧部分的所有元素都大于枢轴。然后递归地对左右两部分进行快速排序,直到整个数组有序。快速排序有多种排序方法,先介绍基本结构。

3.3.1 快速排序的基本结构

public static void quickSort(int[] array) {quick(array, 0, array.length - 1);}public static void quick(int[] array, int left, int right) {if (left >= right) {return;}int index = partition(array, left, right);quick(array, left, index - 1);quick(array, index + 1, right);}
  1. 快速排序入口方法 (quickSort):

     这是快速排序的入口方法,接收一个数组 array 作为参数。调用 quick 方法,对整个数组进行排序,初始的左右边界分别是 0 和 array.length - 1。
    
  2. 递归排序方法 (quick):

    这是快速排序的递归方法,用于对数组的某个子区间进行排序。
    参数 left 和 right 分别表示当前子区间的左边界和右边界。
    基本条件:如果 left 大于或等于 right,说明当前子区间已经有序或只有一个元素,直接返回。
    调用 partition 方法,将数组分成两部分,并返回枢轴元素的位置 index。
    递归地对左侧部分(left 到 index - 1)进行排序。
    递归地对右侧部分(index + 1 到 right)进行排序。
    

下面介绍partition的各种实现方法:

3.3.2 Hoare法

Hoare法的partition(分区)算法是快速排序中的一种分区方法。它通过选择一个枢轴元素,将数组分成两部分,使得左侧部分的所有元素都小于等于枢轴,右侧部分的所有元素都大于等于枢轴。

    private static int partition1(int[] array, int left, int right) {int i = left;int j = right;int pivot = array[left];while (i < j) {while (i < j && array[j] >= pivot) {j--;}while (i < j && array[i] <= pivot) {i++;}swap(array, i, j);}swap(array, i, left);return i;}
  1. 选择枢轴:

     选择最左边的元素作为枢轴(pivot)。
    
  2. 初始化指针:

     初始化两个指针 i 和 j,分别指向数组的左边界和右边界。
    
  3. 分区过程:

     使用两个 while 循环,分别从右向左和从左向右扫描数组:从右向左找到第一个小于枢轴的元素,更新指针 j。从左向右找到第一个大于枢轴的元素,更新指针 i。如果 i 小于 j,交换 i 和 j 指向的元素。重复上述过程,直到 i 和 j 相遇或交错。
    
  4. 交换枢轴:

     将枢轴元素放到正确的位置,即 i 位置。返回 i 作为分区点。
    

3.3.3 挖坑法

    public static int partition(int[] array, int left, int right) {int base = array[left];int i = left;int j = right;while (i < j) {while (i < j && array[j] >= base) {j--;}array[i] = array[j];while (i < j && array[i] <= base) {i++;}array[j] = array[i];}array[i] = base;return i;}
  1. 选择枢轴:

     选择最左边的元素作为枢轴(base)。
    
  2. 初始化指针:

     初始化两个指针 i 和 j,分别指向数组的左边界和右边界。
    
  3. 分区过程:

     使用两个 while 循环,分别从右向左和从左向右扫描数组:从右向左找到第一个小于枢轴的元素,更新指针 j,并将该元素放到 i 位置。从左向右找到第一个大于枢轴的元素,更新指针 i,并将该元素放到 j 位置。重复上述过程,直到 i 和 j 相遇或交错。
    
  4. 交换枢轴:

     将枢轴元素放到正确的位置,即 i 位置。返回 i 作为分区点。
    

3.3.4 双指针法

  private static int partition(int[] array, int left, int right) {int prev = left;int cur = left + 1;while (cur <= right) {if (array[cur] < array[left] && array[++prev] != array[cur]) {swap(array, cur, prev);}cur++;}swap(array, prev, left);return prev;}
  1. 选择枢轴:

     选择最左边的元素作为枢轴(array[left])。
    
  2. 初始化指针:

     初始化两个指针 prev 和 cur,分别指向数组的左边界和枢轴的下一个位置。
    
  3. 分区过程:

     使用 while 循环,从 cur 指针开始遍历数组,直到 cur 超过右边界 right。如果 cur 指针指向的元素小于枢轴元素,并且 prev 指针和 cur 指针指向的元素不同,则交换 cur 和 prev 指针指向的元素。每次交换后,prev 指针向右移动一位。cur 指针每次循环后向右移动一位。
    
  4. 交换枢轴:

     将枢轴元素放到正确的位置,即 prev 位置。返回 prev 作为分区点。
    

3.4 快速排序非递归法

非递归快速排序通过使用栈来模拟递归调用,从而避免了递归带来的栈溢出问题。

    public static void quickSortNonR(int[] a, int left, int right) {Stack<Integer> st = new Stack<>();st.push(left);st.push(right);while (!st.empty()) {right = st.pop();left = st.pop();if (right - left <= 1) {continue;}int div = partition(a, left, right);st.push(div + 1);st.push(right);st.push(left);st.push(div);}}
  1. 初始化栈:

     创建一个栈 st,用于存储数组的左右边界。将初始的左右边界 left 和 right 压入栈中。
    
  2. 循环处理:

     当栈不为空时,循环执行以下步骤:从栈中弹出 right 和 left,表示当前需要处理的子数组的左右边界。如果子数组的长度小于等于1(即 right - left <= 1),则跳过当前循环,继续处理下一个子数组。调用 partition 方法对当前子数组进行分区,返回枢轴位置 div。将右侧子数组的左右边界(div + 1 和 right)压入栈中。将左侧子数组的左右边界(left 和 div)压入栈中。
    
  3. 分区方法:

     partition 方法用于将数组分成两部分,使得左侧部分的所有元素都小于等于枢轴,右侧部分的所有元素都大于等于枢轴。
    

3.5 快速排序分析

时间复杂度:O(nlogn)
空间复杂度:O(logn)
稳定性:不稳定

4. 归并排序

归并排序是一种基于分治法的排序算法。它将数组分成两个子数组,分别对这两个子数组进行排序,然后将排序后的子数组合并成一个有序的数组。

4.1 基本思想

  1. 分解:将数组分成两个子数组,分别对这两个子数组进行排序。
  2. 递归排序:递归地对每个子数组进行归并排序。
  3. 合并:将两个有序的子数组合并成一个有序的数组。

4.1 归并排序递归

// 归并排序
public static void mergeSort(int[] array) {mergeSortChild(array, 0, array.length - 1);
}// 递归排序子数组
public static void mergeSortChild(int[] array, int left, int right) {if (left >= right) {return;}int mid = (left + right) / 2;mergeSortChild(array, left, mid);mergeSortChild(array, mid + 1, right);merge(array, left, mid, right);
}// 合并两个有序子数组
public static void merge(int[] array, int left, int mid, int right) {int s1 = left;int s2 = mid + 1;int[] tmp = new int[right - left + 1];int i = 0;while (s1 <= mid && s2 <= right) {if (array[s1] <= array[s2]) {tmp[i++] = array[s1++];} else {tmp[i++] = array[s2++];}}while (s1 <= mid) {tmp[i++] = array[s1++];}while (s2 <= right) {tmp[i++] = array[s2++];}for (int j = 0; j < tmp.length; j++) {array[left + j] = tmp[j];}
}
  1. 归并排序入口方法 (mergeSort):

     这是归并排序的入口方法,接收一个数组 array 作为参数。调用 mergeSortChild 方法,对整个数组进行排序,初始的左右边界分别是 0 和 array.length - 1。
    
  2. 递归排序子数组 (mergeSortChild):

     这是归并排序的递归方法,用于对数组的某个子区间进行排序。参数 left 和 right 分别表示当前子区间的左边界和右边界。基本条件:如果 left 大于或等于 right,说明当前子区间已经有序或只有一个元素,直接返回。计算中间位置 mid,将数组分成两部分。递归地对左侧部分(left 到 mid)进行排序。递归地对右侧部分(mid + 1 到 right)进行排序。调用 merge 方法,将两个有序的子数组合并成一个有序的数组。
    
  3. 合并两个有序子数组 (merge):

     参数 left、mid 和 right 分别表示当前子区间的左边界、中间位置和右边界。初始化两个指针 s1 和 s2,分别指向两个子数组的起始位置。创建一个临时数组 tmp,用于存储合并后的有序数组。使用 while 循环,将两个子数组中的元素按顺序合并到临时数组 tmp 中。将剩余的元素(如果有)复制到临时数组 tmp 中。将临时数组 tmp 中的元素复制回原数组 array 中。
    

时间复杂度:O(nlogn)
空间复杂度:O(n)
稳定性:稳定

4.2 归并排序非递。

非递归归并排序(也称为迭代归并排序)通过逐步增加子数组的大小来实现排序。

    public static void mergeSortNoR(int[] array) {int gap = 1;while(gap < array.length) {for (int i = 0; i < array.length; i += 2 * gap) {int left = i;int mid = left + gap - 1;if(mid >= array.length - 1) {mid = array.length - 1;}int right = mid + gap;if(right >= array.length) {right = array.length - 1;}merge(array, left, mid, right);}gap *= 2;}}
  1. 初始化间隔 (gap):

     初始化间隔 gap 为1,表示初始的子数组大小为1。
    
  2. 外层循环:

     当 gap 小于数组长度时,继续循环。每次循环将 gap 翻倍,表示子数组的大小逐步增加。
    
  3. 内层循环:

     遍历数组,将数组分成若干个大小为 2 * gap 的子数组。计算每个子数组的左边界 left、中间位置 mid 和右边界 right。调用 merge 方法,将两个有序的子数组合并成一个有序的数组。
    

5. 不基于比较的排序

不基于比较的排序算法主要包括计数排序、基数排序和桶排序。这些算法不通过比较元素来排序,而是利用元素的值来确定其位置,从而实现线性时间复杂度的排序。这里就介绍计数排序。

5.1 计数排序

计数排序适用于元素值范围较小的情况。它通过统计每个元素出现的次数,然后根据这些计数来确定每个元素的位置。

public static void countingSort(int[] array) {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[] countArray = new int[len];// 统计每个元素出现的次数for (int i = 0; i < array.length; i++) {int index = array[i] - min;countArray[index]++;}// 根据计数数组重新填充原数组int k = 0;for (int i = 0; i < countArray.length; i++) {while (countArray[i] > 0) {array[k++] = i + min;countArray[i]--;}}
}
  1. 找到最大值和最小值:

     遍历数组,找到数组中的最大值 max 和最小值 min。
    
  2. 创建计数数组:

     根据最大值和最小值计算计数数组的长度 len,即 max - min + 1。创建一个长度为 len 的计数数组 countArray,用于统计每个元素出现的次数。
    
  3. 统计每个元素出现的次数:

     遍历原数组,对于每个元素 array[i],计算其在计数数组中的索引 index,即 array[i] - min。将计数数组中对应位置的值加1,表示该元素出现了一次。
    
  4. 根据计数数组重新填充原数组:

     遍历计数数组,对于每个非零的计数值,将对应的元素填充回原数组。使用一个指针 k 来记录当前填充的位置。对于计数值 countArray[i],将元素 i + min 填充回原数组 countArray[i] 次。
    

6. 总结

以上就是一些常用的排序算法的介绍和代码实现,具体如何排序,可以看这里的链接: 十大经典排序算法动画与解析。

相关文章:

Java算法——排序

目录 引言1. 插入排序1.1 基本思想1.2 直接插入排序1.3 希尔排序 2. 选择排序2.1 基本思想2.2 直接选择排序2.3 直接选择排序变种2.4 堆排序 3. 交换排序3.1 基本思想3.2 冒泡排序3.3 快速排序3.3.1 快速排序的基本结构3.3.2 Hoare法3.3.3 挖坑法3.3.4 双指针法 3.4 快速排序非…...

【Python・机器学习】多元回归模型(原理及代码)

前言 自学笔记&#xff0c;分享给语言学/语言教育学方向的&#xff0c;但对语言数据处理感兴趣但是尚未入门&#xff0c;却需要在论文中用到的小伙伴&#xff0c;欢迎大佬们补充或绕道。ps&#xff1a;本文最少限度涉及公式讲解&#xff08;文科生小白友好体质&#xff09;&am…...

mysql数据被误删的恢复方案

文章目录 一、使用备份恢复二、使用二进制日志&#xff08;Binary Log&#xff09;三、使用InnoDB表空间恢复四、使用第三方工具预防措施 数据误删是一个严重的数据库管理问题&#xff0c;但通过合理的备份策略和使用适当的恢复工具&#xff0c;可以有效地减少数据丢失的风险…...

使用EasyExcel(FastExcel) 的模板填充报Create workbook failure

场景 使用 EasyExcel (FastExcel) 做数据导出时&#xff0c;用了通过模板导出数据的形式。 在读取模板文件的时候出现错误导致创建Workbook 失败&#xff0c; 错误日志&#xff1a; Create workbook failure... No valid entries or contents found, this is not a valid OOX…...

[C]基础8.详解操作符

博客主页&#xff1a;算法歌者本篇专栏&#xff1a;[C]您的支持&#xff0c;是我的创作动力。 文章目录 0、总结1、操作符的分类2、二进制和进制转换2.1、2进制转10进制2.2、10进制转2进制2.3、2进制转8进制和16进制 3、原码、反码、补码4、移位操作符4.1 左移操作符4.2 右移操…...

MySQL篇之对MySQL进行参数优化,提高MySQL性能

1. MySQL参数优化说明 MySQL 参数调优是提高数据库性能的重要手段之一。通过调整 MySQL 的配置参数&#xff0c;可以优化查询速度、提升并发处理能力、减少资源消耗等。 MySQL 的性能优化涉及到多个方面&#xff0c;包括内存管理、磁盘 I/O、查询优化、连接管理、复制配置等。…...

Vue 3 的 keep-alive 及生命周期钩子

在 Vue 3 中&#xff0c;keep-alive 是一个内置组件&#xff0c;用于提高性能和减少不必要的组件销毁与重建。它与组件的生命周期紧密相关&#xff0c;特别是在动态组件和路由切换场景下&#xff0c;能够缓存组件的状态并避免重新渲染。 而 onActivated 和 onDeactivated 是 …...

ComfyUI实现老照片修复——AI修复老照片(ComfyUI-ReActor / ReSwapper)解决天坑问题及加速pip下载

AI修复老照片&#xff0c;试试吧&#xff0c;不一定好~~哈哈 2023年4月曾用过ComfyUI&#xff0c;当时就感慨这个工具和虚幻的蓝图很像&#xff0c;以后肯定是专业人玩的。 2024年我写代码去了&#xff0c;AI做图没太关注&#xff0c;没想到&#xff0c;现在ComfyUI真的变成了工…...

OpenEuler学习笔记(十一):OpenEuler上搭建LAMP环境

LAMP环境指的是Linux、Apache、MySQL&#xff08;或MariaDB&#xff09;和PHP的组合&#xff0c;下面为你介绍在OpenEuler上搭建LAMP环境的详细步骤&#xff1a; 1. 系统更新 首先要更新系统中的软件包&#xff0c;保证系统处于最新状态。 sudo dnf update -y2. 安装Apache…...

Mongodb 慢查询日志分析 - 1

Mongodb 慢查询日志分析 使用 mloginfo 处理过的日志会在控制台输出, 显示还是比较友好的. 但是如果内容较大, 就不方便查看了, 如果可以导入到 excel 就比较方便筛选/排序. 但是 mloginfo 并没有提供生成到 excel 的功能. 可以通过一个 python 脚本辅助生成: import pandas…...

MySQL面试题2025 每日20道【其四】

1、你们生产环境的 MySQL 中使用了什么事务隔离级别&#xff1f;为什么&#xff1f; 中等 在生产环境中&#xff0c;MySQL数据库的事务隔离级别通常由开发团队或数据库管理员根据应用的需求来设定。MySQL支持四种标准的事务隔离级别&#xff1a; 读未提交&#xff08;Read Unc…...

微服务学习-Nacos 注册中心实战

1. 注册中心的设计思路 1.1. 微服务为什么会用到注册中心&#xff1f; 服务与服务之间调用需要有服务发现功能&#xff1b;例如订单服务调用库存服务&#xff0c;库存服务如果有多个&#xff0c;订单服务到底调用那个库存服务呢&#xff08;负载均衡器&#xff09;&#xff0…...

k8s服务StatefulSet部署模板

java 服务StatefulSet部署模板 vim templates-test.yamlapiVersion: apps/v1 kind: StatefulSet metadata:labels:app: ${app_labels}name: ${app_name}namespace: ${app_namespace} spec:replicas: ${app_replicas_count}selector:matchLabels:app: ${app_labels}template:la…...

07 区块链安全技术

概述 区块链的安全特性 区块链解决了在不可靠网络上可靠地传输信息的难题&#xff0c;由于不依赖与中心节点的认证和管理&#xff0c;因此防止了中心节点被攻击造成的数据泄露和认证失败的风险。 区块链安全防护的三大特点 共识机制代替中心认证机制数据篡改“一发动全身”…...

Adobe的AI生成3D数字人框架:从自拍到生动的3D化身

一、引言 随着人工智能技术的发展,我们见证了越来越多创新工具的出现,这些工具使得图像处理和视频编辑变得更加智能与高效。Adobe作为全球领先的创意软件公司,最近推出了一项令人瞩目的新技术——一个能够将普通的二维自拍照转换成栩栩如生的三维(3D)数字人的框架。这项技…...

dfs专题四:综合练习

key&#xff1a;画出决策树&#xff08;就是找个简单例子模拟一下的树状决策图&#xff09; dfs传参 or 全局变量&#xff1a; int, double等常量/比较小的变量&#xff0c;可以dfs参数传递vector等线性O&#xff08;N&#xff09;变量&#xff0c;要用全局变量 回溯&#x…...

【线性代数】列主元法求矩阵的逆

列主元方法是一种用于求解矩阵逆的数值方法&#xff0c;特别适用于在计算机上实现。其基本思想是通过高斯消元法将矩阵转换为上三角矩阵&#xff0c;然后通过回代求解矩阵的逆。以下是列主元方法求解矩阵 A A A 的逆的步骤&#xff1a; [精确算法] 列主元高斯消元法 步骤 1&am…...

大写——蓝桥杯

1.题目描述 给定一个只包含大写字母和小写字母的字符串&#xff0c;请将其中所有的小写字母转换成大写字母后将字符串输出。 输入描述 输入一行包含一个字符串。 输出描述 输出转换成大写后的字符串。 输入输出样例 示例 输入 LanQiao输出 LANQIAO评测用例规模与约定 对…...

HTML `<head>` 元素详解

在 HTML 文档中&#xff0c;<head> 元素是一个非常重要的部分&#xff0c;它包含了文档的元数据&#xff08;metadata&#xff09;和其他与文档相关的信息。虽然 <head> 中的内容不会直接显示在网页上&#xff0c;但它对网页的行为、样式和搜索引擎优化&#xff08…...

一文速通stack和queue的理解与使用

CSTL之stack和queue 1.stack1.1.stack的基本概念1.2.stack的接口 2.queue2.1.queue的基本概念2.2.queue的接口 3.priority_queue3.1.priority_queue的基本概念3.2.priority_queue的接口3.3.仿函数 4.容器适配器5.deque5.1.deque的简单了解5.2.deque的优缺点 &#x1f31f;&…...

Antd React Form使用Radio嵌套多个Select和Input的处理

使用Antd React Form使用Radio会遇到嵌套多个Select和Input的处理&#xff0c;需要多层嵌套和处理默认事件和冒泡&#xff0c;具体实现过程直接上代码。 实现效果布局如下图 代码 <Formname"basic"form{form}labelWrap{...formItemLayoutSpan(5, 19)}onFinish{on…...

Vue - toRefs() 和 toRef() 的使用

一、toRefs() 在 Vue 3 中,toRefs()可以将响应式对象的属性转换为可响应的 refs。主要用于在解构响应式对象时&#xff0c;保持属性的响应性。 1. 导入 toRefs 函数 import { toRefs } from vue;2. 将响应式对象的属性转换为 ref const state reactive({count: 0,message:…...

Python3 OS模块中的文件/目录方法说明九

一. 简介 前面文章简单学习了 Python3 中 OS模块中的文件/目录的部分函数。 本文继续来学习 OS 模块中文件、目录的操作方法&#xff1a;os.pipe() 方法、os.popen() 方法。 二. Python3 OS模块中的文件/目录方法 1. os.pipe() 方法 os.pipe() 方法用于创建一个管道, 返回…...

OpenCV文字绘制支持中文显示

OpenCV版本&#xff1a;4.4 IDE&#xff1a;VS2019 功能描述 OpenCV绘制文本的函数putText()不支持中文的显示&#xff0c;网上很多方法推荐的都是使用FreeType来支持&#xff0c;FreeType是什么呢&#xff1f;FreeType的官网上有介绍 FreeType官网 https://www.freetype.or…...

opengrok_windows_多工程环境搭建

目录 多工程的目录 工程代码下载和log配置 工程的索引 工程部署 工程测试 参考列表 多工程的目录 工程代码下载和log配置 工程代码下载 在每个工程的src目录下&#xff0c;下载工程代码&#xff0c;以下载pulseaudio的代码为例。 git clone gitgithub.com…...

基于ollama,langchain,springboot从零搭建知识库三【解析文档并存储到向量数据库】

安装环境 安装pgvector&#xff0c;先设置docker镜像源&#xff1a; vim /etc/docker/daemon.json {"registry-mirrors": ["https://05f073ad3c0010ea0f4bc00b7105ec20.mirror.swr.myhuaweicloud.com","https://mirror.ccs.tencentyun.com",&…...

Elasticsearch 和arkime 安装

安装一定要注意版本号&#xff0c;不然使用不了 这里Ubuntu使用ubuntu-20.04.6-desktop-amd64.iso elasticsearch这里使用Elasticsearch 7.17.5 | Elastic arkime这里使用wget https://s3.amazonaws.com/files.molo.ch/builds/ubuntu-20.04/arkime_3.4.2-1_amd64.deb 大家想…...

git回退

git回退 1、未使用 git add 缓存代码时 git checkout –- filepathname 放弃单个文件的修改 git checkout . 放弃所有的文件修改 此命令用来放弃掉所有还没有加入到缓存区&#xff08;就是 git add 命令&#xff09;的修改&#xff1a;内容修改与整个文件删除。但是此命令不…...

pytest+playwright落地实战大纲

前言 很久没有更新博客&#xff0c;是因为在梳理制作Playwright测试框架实战相关的课程内容。现在课程已经完结&#xff0c;开个帖子介绍下这门课程&#xff08;硬广, o(〃&#xff3e;▽&#xff3e;〃)o&#xff09; 课程放在CSDN学习频道&#xff0c; 欢迎关注~ PyTestPl…...

01-硬件入门学习/嵌入式教程-CH340C使用教程

前言 CH340C广泛应用于DIY项目和嵌入式开发中&#xff0c;用于USB数据转换和串口通信。本文将详细介绍CH340C的基本功能、引脚接线及使用方法。 CH340C简介 CH340C是一款USB转TTL电平转换器&#xff0c;可以将电脑的USB数据转换成串口数据&#xff0c;方便与单片机&#xff…...