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. 总结

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

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

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

相关文章

数据库SQLite和SCADA DIAView应用教程

课程简介 此系列课程大纲主要包含七个课时。主要使用到的开发工具有&#xff1a;SQLite studio 和 SCADA DIAView。详细的可成内容大概如下&#xff1a; 1、SQLite 可视化管理工具SQLite Studio &#xff1a;打开数据库和查询数据&#xff1b;查看视频 2、创建6个变量&#x…

YOLOv8改进,YOLOv8检测头融合DSConv(动态蛇形卷积),并添加小目标检测层(四头检测),适合目标检测、分割等

精确分割拓扑管状结构例如血管和道路,对各个领域至关重要,可确保下游任务的准确性和效率。然而,许多因素使任务变得复杂,包括细小脆弱的局部结构和复杂多变的全局形态。在这项工作中,注意到管状结构的特殊特征,并利用这一知识来引导 DSCNet 在三个阶段同时增强感知:特征…

Addressable学习

AssetsBundle是Unity的资源管理机制,将资源打包到AssetsBundle资源包并提供接口能从ab包里面加载资源出来。有了这个机制以后&#xff0c;我们要做资源管理&#xff0c;还需要做: a: 根据项目需求,编写编辑器扩展,提供指定资源打入对应bundle包工具策略; b: 根据项目的需求,资源…

概率密度函数(PDF)分布函数(CDF)——直方图累积直方图——直方图规定化的数学基础

对于连续型随机变量&#xff0c;分布函数&#xff08;Cumulative Distribution Function, CDF&#xff09;是概率密度函数&#xff08;Probability Density Function, PDF&#xff09;的变上限积分&#xff0c;概率密度函数是分布函数的导函数。 如果我们有一个连续型随机变量…

深入解析:Docker 容器如何实现文件系统与资源的多维隔离?

目录 一、RootFs1. Docker 镜像与文件系统层2. RootFs 与容器隔离的意义 二、Linux Namespace1. 进程命名空间1.1 lsns 命令说明1.2 查看“祖先进程”命名空间1.3 查看当前用户进程命名空间 2. 容器进程命名空间2.1 查看容器进程命名空间列表2.2 容器进程命名空间的具体体现 三…

深度学习|表示学习|卷积神经网络|由参数共享引出的特征图|08

如是我闻&#xff1a; Feature Map&#xff08;特征图&#xff09;的概念与 Parameter Sharing&#xff08;参数共享&#xff09;密切相关。换句话说&#xff0c;参数共享是生成 Feature Map 的基础。Feature Map 是卷积操作的核心产物&#xff0c;而卷积操作的高效性正是由参数…

【Linux网络编程】传输层协议

目录 一&#xff0c;传输层的介绍 二&#xff0c;UDP协议 2-1&#xff0c;UDP的特点 2-2&#xff0c;UDP协议端格式 三&#xff0c;TCP协议 3-1&#xff0c;TCP报文格式 3-2&#xff0c;TCP三次握手 3-3&#xff0c;TCP四次挥手 3-4&#xff0c;滑动窗口 3-5&#xf…

ARP 表、MAC 表、路由表、跨网段 ARP

文章目录 一、ARP 表1、PC2、路由器 - AR22203、交换机 - S57004、什么样的设备会有 ARP 表&#xff1f; 二、MAC 表什么样的设备会有 MAC 表&#xff1f; 三、路由表什么样的设备会有路由表&#xff1f; 四、抓取跨网段 ARP 包 所谓 “透明” 就是指不用做任何配置 一、ARP 表…

算法10(力扣20)-有效的括号

1、问题 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 每个右括号都有一个对应的相同类…

每日一题——二叉树的前序遍历和中序遍历和后序遍历

文章目录 二叉树的前序、中序与后序遍历题目描述数据范围&#xff1a;示例&#xff1a;示例 1&#xff1a; 解题思路1. 递归法2. 迭代法 代码实现&#xff08;递归法&#xff09;代码解析示例输出复杂度分析 中序遍历后序遍历 二叉树的前序、中序与后序遍历 题目描述 给定一棵…

【优选算法】8----四数之和

有看过我上篇算法博客并且去做过的铁子们&#xff0c;对这道题的话应该就不会那么陌生了&#xff0c;因为这两道题 的解题思路有着异曲同工之妙~ -----------------------------------------begin------------------------------------- 题目解析&#xff1a; 跟三数之和就多了…

【开源免费】基于SpringBoot+Vue.JS校园失物招领系统(JAVA毕业设计)

本文项目编号 T 150 &#xff0c;文末自助获取源码 \color{red}{T150&#xff0c;文末自助获取源码} T150&#xff0c;文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…

2025美赛倒计时,数学建模五类模型40+常用算法及算法手册汇总

数学建模美赛倒计时&#xff0c;对于第一次参加竞赛且没有相关基础知识的同学来讲&#xff0c;掌握数学建模常用经典的模型算法知识&#xff0c;并熟练使用相关软件进行建模是关键。本文将介绍一些常用的模型算法&#xff0c;以及软件操作教程。 数学建模常用模型包括&#xf…

【深度学习】常见模型-循环神经网络(Recurrent Neural Network, RNN)

循环神经网络&#xff08;RNN&#xff09; 概念简介 循环神经网络&#xff08;Recurrent Neural Network, RNN&#xff09;是一种用于处理序列数据&#xff08;如时间序列、文本序列等&#xff09;的神经网络模型。与传统神经网络不同&#xff0c;RNN 的结构具有记忆能力&…

Matlab总提示内存不够用,明明小于电脑内存

目录 前言情况1&#xff08;改matlab最大内存限制&#xff09;情况2&#xff08;重启电脑&#xff09;情况3 前言 在使用matlab中&#xff0c;有时候需要占用的内存并没有超过电脑内存依旧会报错&#xff0c;提示内存不够用&#xff0c;可以尝试下面几种方法&#xff0c;总有一…

数据结构(Java)——二叉树

1.概念 二叉树是一种树形数据结构&#xff0c;其中每个节点最多有两个子节点&#xff0c;通常被称为左子节点和右子节点。二叉树可以是空的&#xff08;即没有节点&#xff09;&#xff0c;或者由一个根节点以及零个或多个左子树和右子树组成&#xff0c;其中左子树和右子树也分…

Windows电脑上记录工作待办清单的工具用哪个?

在Windows电脑上记录日常工作待办清单的工具用哪一个比较好呢&#xff1f;想要工作效率高&#xff0c;借助Windows电脑上的待办清单工具&#xff0c;可以帮助大家快速完成各项任务&#xff0c;今天给大家盘点几款工作待办清单工具。 Windows自带的便笺Stickynotes Windows自…

H266/VVC 量化编码中 JCCR 技术

色度联合编码 JCCR VVC 支持色度分量联合编码(Joint Coding of Chroma Residual&#xff0c;JCCR)&#xff0c; 两个色度残差联合编码。通过 TU 的标志位 tu_joint_cbcr_residual_flag 指定该工 具&#xff0c;并通过 CBF 来隐式传输所选的模式。仅当 TU 的至少一个色度分量的 …

云原生时代,如何构建高效分布式监控系统

文章目录 一.监控现状二.Thanos原理分析SidecarQuerierStoreCompactor 三.Sidecar or ReceiverThanos Receiver工作原理 四.分布式运维架构 一.监控现状 Prometheus是CNCF基金会管理的一个开源监控项目&#xff0c;由于其良好的架构设计和完善的生态&#xff0c;迅速成为了监控…

DAY3,模拟终端

题目 实现一个终端的功能&#xff0c;注意需要带有cd功能 思路 如何 C语言执行shell命令&#xff1f; 使用进程替换exec函数&#xff1b;&#xff1b;使用system函数。 如何 切换工作目录&#xff1f; 使用chdir函数切换工作目录。 其…