CPT204 Advanced Obejct-Oriented Programming 高级面向对象编程 Pt.8 排序算法

文章目录

  • 1. 排序算法
    • 1.1 冒泡排序(Bubble sort)
    • 1.2 归并排序(Merge Sort)
    • 1.3 快速排序(Quick Sort)
    • 1.4 堆排序(Heap Sort)
  • 2. 在面向对象编程中终身学习
    • 2.1 记录和反思学习过程
    • 2.2 EDI 原则
    • 2.3 人工智能辅助的管理工具进行软件开发
  • 3. 练习
    • 3.1 基础练习
    • 3.2 进阶练习
      • 3.2.1 编写两个泛型方法来实现归并排序(Merge Sort)
      • 3.2.2 编写两个泛型方法来实现插入排序(Insertion Sort)

1. 排序算法

我们之前的学习已经对所有排序方法都有了了解,这里稍微复习一下。

1.1 冒泡排序(Bubble sort)

冒泡排序是一种简单的排序算法,它重复地遍历待排序的列表,比较每一对相邻的元素,如果它们的顺序错误就交换它们。每次遍历都会让最大的元素“冒泡”到列表的末尾。这个过程会重复进行,直到整个列表有序。
代码如下。

public class BubbleSort {public static void bubbleSort(int[] list) {for (int k = 1; k < list.length; k++) { // 外层循环控制趟数for (int i = 0; i < list.length - k; i++) { // 内层循环比较相邻元素if (list[i] > list[i + 1]) { // 如果当前元素大于后面的元素// 交换它们int temp = list[i];list[i] = list[i + 1];list[i + 1] = temp;}}}}public static void main(String[] args) {int[] list = {64, 34, 25, 12, 22, 11, 90};bubbleSort(list); // 调用冒泡排序System.out.println("Sorted list:");for (int num : list) {System.out.print(num + " ");}}
}

如果列表几乎已经排序好了,或者已经完全排序好了,传统的冒泡排序算法仍然会进行不必要的遍历和比较,这会浪费计算资源。
下面的代码会引入一个布尔变量 needNextPass 来检测数组是否可能已经排序好了,从而避免不必要的遍历。

public class BubbleSort {public static void bubbleSort(int[] list) {boolean needNextPass = true;for (int k = 1; k < list.length && needNextPass; k++) {needNextPass = false; // 假设这一轮不需要再排序for (int i = 0; i < list.length - k; i++) {if (list[i] > list[i + 1]) {int temp = list[i];list[i] = list[i + 1];list[i + 1] = temp;needNextPass = true; // 发生了交换,说明还需要继续排序}}}}public static void main(String[] args) {int[] list = {1, 2, 3, 4};bubbleSort(list);System.out.println("Sorted list:");for (int num : list) {System.out.print(num + " ");}}
}

冒泡排序的时间复杂度再最佳情况下是 O ( n ) O(n) O(n),在最坏情况下(数组完全逆序)是 O ( n 2 ) O(n^2) O(n2)

1.2 归并排序(Merge Sort)

归并排序使用分治法(Divide and Conquer)策略,因此详细步骤如下:

  1. 分解(Divide):
    将数组分成两半。如果数组的长度是奇数,那么可以将中间的元素单独处理,或者将其归入左边或右边的子数组。
    这个过程是递归进行的,即对每个子数组继续进行分解,直到每个子数组只包含一个元素。由于只有一个元素的数组自然是有序的,所以这是递归的终止条件。
  2. 解决(Conquer):
    对每个子数组进行排序。由于子数组是由原始数组分解而来,且每个子数组只包含一个元素,所以这一步实际上是在递归地对子数组进行排序。
  3. 合并(Combine):
    将排序好的子数组合并成一个有序数组。这一步是归并排序的核心,需要将两个已排序的子数组合并成一个有序数组。
    合并过程是通过比较两个子数组的首元素,将较小的元素放入新数组中,然后从相应的子数组中移除该元素,重复这个过程直到两个子数组中的元素都被合并到新数组中。
    在这里插入图片描述
    代码如下。
public class MergeSortTest {public static void mergeSort(int[] list) {if (list.length > 1) {// 分解第一个子数组int[] firstHalf = new int[list.length / 2];System.arraycopy(list, 0, firstHalf, 0, list.length / 2);mergeSort(firstHalf); // 递归排序第一个子数组// 分解第二个子数组int secondHalfLength = list.length - list.length / 2;int[] secondHalf = new int[secondHalfLength];System.arraycopy(list, list.length / 2, secondHalf, 0, secondHalfLength);mergeSort(secondHalf); // 递归排序第二个子数组// 合并两个子数组到原数组merge(firstHalf, secondHalf, list);}}public static void merge(int[] list1, int[] list2, int[] list) {int current1 = 0; // 当前索引在 list1 中int current2 = 0; // 当前索引在 list2 中int current3 = 0; // 当前索引在 list 中// 合并两个子数组while (current1 < list1.length && current2 < list2.length) {if (list1[current1] < list2[current2]) {list[current3++] = list1[current1++];} else {list[current3++] = list2[current2++];}}// 复制剩余的元素(如果有的话)while (current1 < list1.length) {list[current3++] = list1[current1++];}while (current2 < list2.length) {list[current3++] = list2[current2++];}}public static void main(String[] args) {int size = 100000;int[] a = new int[size];randomInitiate(a);long startTime = System.currentTimeMillis();mergeSort(a);long endTime = System.currentTimeMillis();System.out.println((endTime - startTime) + "ms");}private static void randomInitiate(int[] a) {for (int i = 0; i < a.length; i++) {a[i] = (int) (Math.random() * a.length);}}
}

归并排序的递归关系式为:
T ( n ) = T ( n / 2 ) + T ( n / 2 ) + 2 n − 1 T(n)=T(n/2)+T(n/2)+2n−1 T(n)=T(n/2)+T(n/2)+2n1
其中:
第一个 T ( n / 2 ) T(n/2) T(n/2)表示对数组的前半部分进行排序所需的时间。
第二个 T ( n / 2 ) T(n/2) T(n/2) 表示对数组的后半部分进行排序所需的时间。
2 n − 1 2n−1 2n1表示合并两个已排序子数组所需的时间,包括 n − 1 n−1 n1次比较(用于比较两个子数组的元素)和 n n n次移动(将每个元素放入临时数组)。
因此归并排序的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

1.3 快速排序(Quick Sort)

快速排序也是使用分治法(Divide and Conquer)策略来实现排序。

  1. 算法从数组中选择一个元素,称为基准(pivot)。
  2. 将数组分成两部分:
    第一部分(list1)包含所有小于或等于基准的元素。
    第二部分(list2)包含所有大于基准的元素。
    这个过程称为分区(partitioning)。
  3. 递归排序:
    对第一部分(list1)和第二部分(list2)分别递归地应用快速排序算法,直到每个子数组只包含一个元素或为空。
    在这里插入图片描述
    代码如下。
public class QuickSortTest {public static void quickSort(int[] list) {quickSort(list, 0, list.length - 1);}public static void quickSort(int[] list, int first, int last) {if (last > first) {int pivotIndex = partition(list, first, last);quickSort(list, first, pivotIndex - 1);quickSort(list, pivotIndex + 1, last);}}public static int partition(int[] list, int first, int last) {int pivot = list[first]; // Choose the first element as pivotint low = first + 1; // Index for forward searchint high = last; // Index for backward searchwhile (high > low) {// Search forward from leftwhile (low <= high && list[low] <= pivot)low++;// Search backward from rightwhile (low <= high && list[high] > pivot)high--;// Swap two elements in the listif (high > low) {int temp = list[high];list[high] = list[low];list[low] = temp;}}// Account for duplicated elements:while (high > first && list[high] >= pivot)high--;// Swap pivot with list[high]if (pivot > list[high]) {list[first] = list[high];list[high] = pivot;return high;} else {return first;}}public static void main(String[] args) {int size = 100000;int[] a = new int[size];randomInitiate(a);long startTime = System.currentTimeMillis();quickSort(a);long endTime = System.currentTimeMillis();System.out.println((endTime - startTime) + "ms");}private static void randomInitiate(int[] a) {for (int i = 0; i < a.length; i++)a[i] = (int) (Math.random() * a.length);}
}

快速排序的递归关系式为:
T ( n ) = T ( n / 2 ) + T ( n / 2 ) + n T(n)=T(n/2)+T(n/2)+n T(n)=T(n/2)+T(n/2)+n
其中:
第一个 T ( n / 2 ) T(n/2) T(n/2)表示对数组的前半部分进行排序所需的时间。
第二个 T ( n / 2 ) T(n/2) T(n/2) 表示对数组的后半部分进行排序所需的时间。
n n n表示分区所需的时间。
因此快速排序的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

然而在最坏情况下,每次分区都将数组分成一个非常大的子数组和一个空的子数组。如,假设数组的第一个元素是分区的基准:
[ 1 , 2 , 3 , 4 , 5 , … , n ] , s i z e = n [1,2,3,4,5,…,n], size=n [1,2,3,4,5,,n],size=n
第一次分区,基准为 1 1 1
左侧:空,右侧: [ 2 , 3 , 4 , 5 , … , n ] [2, 3, 4, 5, …, n] [2,3,4,5,,n],右侧子数组大小为 n − 1 n−1 n1
第二次分区,基准为 2 2 2
左侧:空,右侧: [ 3 , 4 , 5 , 6 , … , n ] [3, 4, 5, 6, …, n] [3,4,5,6,,n],右侧子数组大小为 n − 2 n−2 n2
以此类推,直到子数组大小为 1 1 1
由于每次分区都将数组分成一个非常大的子数组和一个空的子数组,因此需要递归地对 n − 1 n−1 n1个子数组进行分区操作。
每个分区操作的时间复杂度为 O ( n ) O(n) O(n),因此总的时间复杂度为 O ( n 2 ) O(n^2) O(n2)

1.4 堆排序(Heap Sort)

这个算法在本学期的算法课上详细介绍过。可以看这里的文章。
相关文章
在堆排序算法中,通常使用完全二叉树(Complete Binary Tree)来实现堆结构。完全二叉树是一种特殊的二叉树,其中除了最后一层外,每一层都是满的,并且最后一层的节点尽可能地集中在左侧。
堆排序算法通过维护一个堆结构来实现排序,堆结构有两种类型:
最大堆(Max Heap):每个节点的值都大于或等于其子节点的值。
最小堆(Min Heap):每个节点的值都小于或等于其子节点的值。
堆可以存储在 ArrayList 或数组中,前提是堆的大小是已知的。
对于数组中位置为 i i i的节点,其左子节点的位置是 2 i + 1 2i + 1 2i+1,右子节点的位置是 2 i + 2 2i + 2 2i+2,父节点的位置是 ( i − 1 ) / 2 (i - 1) / 2 (i1)/2(注意使用整数除法)。
以位置为 4 4 4的节点为例,其两个子节点的位置分别是 2 ∗ 4 + 1 = 9 2 * 4 + 1 = 9 24+1=9 2 ∗ 4 + 2 = 10 2 * 4 + 2 = 10 24+2=10,其父节点的位置是 ( 4 − 1 ) / 2 = 1 (4 - 1)/ 2 = 1 41/2=1(注意这里使用的是整数除法,结果为 1 1 1,而不是 1.5 1.5 1.5)。
在这里插入图片描述

在堆排序中,通常使用最大堆来实现。算法的基本步骤如下:

  1. 将待排序的数组转换为最大堆。
  2. 将堆顶元素(最大值)与数组的最后一个元素交换,然后将数组缩小一个元素。
  3. 重新调整堆,使其满足最大堆的性质。
  4. 重复步骤 2 和 3,直到数组完全有序。

因此实现这个算法需要依赖于堆(Heap)这种数据结构。
其UML图如下。
在这里插入图片描述
代码如下。

import java.util.ArrayList;public class Heap<E extends Comparable> {private ArrayList<E> list = new ArrayList<E>();// Create a default heappublic Heap() {}// Create a heap from an array of objectspublic Heap(E[] objects) {for (int i = 0; i < objects.length; i++)add(objects[i]);}// Add a new object into the heappublic void add(E newObject) {list.add(newObject); // Append to the end of the heapint currentIndex = list.size() - 1; // The index of the last nodewhile (currentIndex > 0) {int parentIndex = (currentIndex - 1) / 2;// Swap if the current object is greater than its parentif (list.get(currentIndex).compareTo(list.get(parentIndex)) > 0) {E temp = list.get(currentIndex);list.set(currentIndex, list.get(parentIndex));list.set(parentIndex, temp);} elsebreak; // the tree is a heap nowcurrentIndex = parentIndex;}}// Remove the root from the heappublic E remove() {if (list.size() == 0) return null;E removedObject = list.get(0);list.set(0, list.get(list.size() - 1));list.remove(list.size() - 1);int currentIndex = 0;while (currentIndex < list.size()) {int leftChildIndex = 2 * currentIndex + 1;int rightChildIndex = 2 * currentIndex + 2;// Find the maximum between two childrenif (leftChildIndex >= list.size())break; // The tree is a heapint maxIndex = leftChildIndex;if (rightChildIndex < list.size())if (list.get(maxIndex).compareTo(list.get(rightChildIndex)) < 0)maxIndex = rightChildIndex;// Swap if the current node is less than the maximumif (list.get(currentIndex).compareTo(list.get(maxIndex)) < 0) {E temp = list.get(maxIndex);list.set(maxIndex, list.get(currentIndex));list.set(currentIndex, temp);currentIndex = maxIndex;} elsebreak; // The tree is a heap}return removedObject;}// Get the number of nodes in the treepublic int getSize() {return list.size();}
}

所以堆排序的完整代码如下。

import java.util.ArrayList;public class HeapSort {public static <E extends Comparable> void heapSort(E[] list) {// Create a Heap of EHeap<E> heap = new Heap<E>();// Add elements to the heapfor (int i = 0; i < list.length; i++)heap.add(list[i]);// Remove the highest elements from the heap// and store them in the list from end to startfor (int i = list.length - 1; i >= 0; i--)list[i] = heap.remove();}/** A test method */public static void main(String[] args) {Integer[] list = {2, 3, 2, 5, 6, 1, -2, 3, 14, 12};heapSort(list);for (int i = 0; i < list.length; i++)System.out.print(list[i] + " ");}
}class Heap<E extends Comparable> {private ArrayList<E> list = new ArrayList<E>();// Create a default heappublic Heap() {}// Create a heap from an array of objectspublic Heap(E[] objects) {for (int i = 0; i < objects.length; i++)add(objects[i]);}// Add a new object into the heappublic void add(E newObject) {list.add(newObject); // Append to the end of the heapint currentIndex = list.size() - 1; // The index of the last nodewhile (currentIndex > 0) {int parentIndex = (currentIndex - 1) / 2;// Swap if the current object is greater than its parentif (list.get(currentIndex).compareTo(list.get(parentIndex)) > 0) {E temp = list.get(currentIndex);list.set(currentIndex, list.get(parentIndex));list.set(parentIndex, temp);} elsebreak; // the tree is a heap nowcurrentIndex = parentIndex;}}// Remove the root from the heappublic E remove() {if (list.size() == 0) return null;E removedObject = list.get(0);list.set(0, list.get(list.size() - 1));list.remove(list.size() - 1);int currentIndex = 0;while (currentIndex < list.size()) {int leftChildIndex = 2 * currentIndex + 1;int rightChildIndex = 2 * currentIndex + 2;// Find the maximum between two childrenif (leftChildIndex >= list.size())break; // The tree is a heapint maxIndex = leftChildIndex;if (rightChildIndex < list.size())if (list.get(maxIndex).compareTo(list.get(rightChildIndex)) < 0)maxIndex = rightChildIndex;// Swap if the current node is less than the maximumif (list.get(currentIndex).compareTo(list.get(maxIndex)) < 0) {E temp = list.get(maxIndex);list.set(maxIndex, list.get(currentIndex));list.set(currentIndex, temp);currentIndex = maxIndex;} elsebreak; // The tree is a heap}return removedObject;}// Get the number of nodes in the treepublic int getSize() {return list.size();}
}

堆排序算法的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)
需要一个临时数组来合并两个子数组。这个临时数组的大小与原数组相同,因此归并排序的空间复杂度是 O(n),其中 n 是数组的长度。
而堆排序不需要额外的数组空间,因此在空间效率上优于归并排序。

2. 在面向对象编程中终身学习

终身学习:指的是持续的、自愿的、自我激励的知识追求,目的是为了个人或职业发展。
在 OOP 领域的应用:终身学习鼓励开发者跟上不断发展的概念、技术和工具,以提高他们在设计、实现和维护软件系统方面的熟练程度。

终身学习的重要性如下:

  1. 不断演变的技术环境:
    终身学习确保开发者能够跟上最新的最佳实践、框架和设计模式。
  2. 提高专业能力:
    终身学习增强了你解决复杂问题、优化代码和设计健壮系统的能力。
  3. 适应新工具:
    学习 OOP 中的新工具使你保持敏捷,并为你打开新的职业机会。
  4. 个人成长:
    不断提高你的 OOP 技能不仅增强了你的技术专长,还发展了其他宝贵的技能,如批判性思维、解决问题和有效沟通。

2.1 记录和反思学习过程

我们可以记录和反思学习过程。

  1. 维护反思日志/记录:
    记录每个学习阶段的关键收获。
    记录在解决编程挑战后获得的见解和思考。
    记录需要进一步澄清的问题或领域。
    记录你已经工作过的代码片段和示例。
    日志有助于巩固你的学习,并为未来的问题提供参考。
  2. 定期自我评估:
    定期评估你与设定目标的进展。你是否掌握了基础知识,或者需要重新审视某些概念。
    诚实地面对挑战。你是否在复杂的设计模式或高级 OOP 原则的实现上遇到困难?接下来专注于这些领域。
    认可你的成就。完成课程或参与 OOP 项目是一个值得庆祝的重要里程碑。
  3. 加入社区和论坛:
    加入像 StackOverflow、Reddit 、GitHub 或 CSDN 这样的专注于 OOP 的社区。
    参与讨论,提问,并与他人分享你的知识。

2.2 EDI 原则

  1. 平等(Equality):
    在 OOP 中,平等意味着所有开发者,无论性别、种族、性取向或残疾状况,都有平等的职业机会和参与软件开发过程的能力。
  2. 多样性(Diversity):
    在 OOP 中,多样性通过汇集来自不同背景、经验和观点的人来丰富问题解决。这种多样性导致更创造性和创新性的解决方案来应对复杂的软件设计挑战。
  3. 包容性(Inclusion):
    在 OOP 中,包容性超越了代表性和平等;它强调创造一个每个人都感到尊重和有价值的环境。

将 EDI 融入 OOP 实践便是:

  1. 采用包容性编码实践:
    在代码、注释和文档中使用包容性语言。避免可能无意中疏远或排除个体的术语或实践。
  2. 实施可访问的设计模式:
    确保你构建的软件设计对广泛受众可访问。纳入可访问性功能,如可定制的 UI 选项、语音识别和高对比度主题。
  3. 鼓励跨多样化团队的协作:
    积极寻求来自不同背景和经验的团队成员。这可能意味着促进科技领域的女性、支持 LGBTQ+ 个体,或与包括来自不同种族和文化的人的团队合作。
  4. 利用 EDI 聚焦资源:
    利用专注于软件开发社区多样性和包容性的资源、课程和材料。这些资源帮助开发者更意识到编码实践中的潜在偏见,并提供减轻它们的策略。

2.3 人工智能辅助的管理工具进行软件开发

我们可以使用 JIRA 等 AI 辅助管理工具来规划和跟踪软件开发过程中的各种任务和问题。通过这些工具,团队可以更有效地管理项目进度,分配任务,跟踪问题,并确保项目按计划进行。这些工具不仅提高了团队的协作效率,还帮助团队成员更好地理解和执行项目需求。

3. 练习

3.1 基础练习

1.给定列表:{2, 9, 5, 4, 8, 1}
冒泡排序算法第一步后,结果是什么?

答案是{2, 5, 4, 8, 1, 9}。因为 2 < 9 2 < 9 2<9,所以不需要交换。

2.对于堆来问号处可以是多少?
在这里插入图片描述
答案是28/29。

3.2 进阶练习

3.2.1 编写两个泛型方法来实现归并排序(Merge Sort)

第一个方法使用 Comparable 接口进行排序,第二个方法使用 Comparator 接口进行排序。
示例代码如下。

import java.util.Comparator;public class MergeSortExample {// 使用 Comparable 接口进行排序public static <E extends Comparable<E>> void mergeSort(E[] list) {if (list.length > 1) {int middle = list.length / 2;@SuppressWarnings("unchecked")E[] firstHalf = (E[]) new Comparable[middle];@SuppressWarnings("unchecked")E[] secondHalf = (E[]) new Comparable[list.length - middle];System.arraycopy(list, 0, firstHalf, 0, middle);System.arraycopy(list, middle, secondHalf, 0, list.length - middle);mergeSort(firstHalf);mergeSort(secondHalf);merge(list, firstHalf, secondHalf);}}// 使用 Comparator 接口进行排序public static <E> void mergeSort(E[] list, Comparator<? super E> comparator) {if (list.length > 1) {int middle = list.length / 2;@SuppressWarnings("unchecked")E[] firstHalf = (E[]) new Comparable[middle];@SuppressWarnings("unchecked")E[] secondHalf = (E[]) new Comparable[list.length - middle];System.arraycopy(list, 0, firstHalf, 0, middle);System.arraycopy(list, middle, secondHalf, 0, list.length - middle);mergeSort(firstHalf, comparator);mergeSort(secondHalf, comparator);merge(list, firstHalf, secondHalf, comparator);}}// 合并两个已排序的子数组private static <E extends Comparable<E>> void merge(E[] list, E[] firstHalf, E[] secondHalf) {int i = 0, j = 0, k = 0;while (i < firstHalf.length && j < secondHalf.length) {if (firstHalf[i].compareTo(secondHalf[j]) <= 0) {list[k++] = firstHalf[i++];} else {list[k++] = secondHalf[j++];}}while (i < firstHalf.length) {list[k++] = firstHalf[i++];}while (j < secondHalf.length) {list[k++] = secondHalf[j++];}}// 合并两个已排序的子数组,使用 Comparatorprivate static <E> void merge(E[] list, E[] firstHalf, E[] secondHalf, Comparator<? super E> comparator) {int i = 0, j = 0, k = 0;while (i < firstHalf.length && j < secondHalf.length) {if (comparator.compare(firstHalf[i], secondHalf[j]) <= 0) {list[k++] = firstHalf[i++];} else {list[k++] = secondHalf[j++];}}while (i < firstHalf.length) {list[k++] = firstHalf[i++];}while (j < secondHalf.length) {list[k++] = secondHalf[j++];}}public static void main(String[] args) {Integer[] list = {2, 3, 2, 5, 6, 1, -2, 3, 14, 12};System.out.println("Original list: " + java.util.Arrays.toString(list));mergeSort(list);System.out.println("Sorted list using Comparable: " + java.util.Arrays.toString(list));String[] stringList = {"banana", "apple", "cherry", "date"};System.out.println("Original string list: " + java.util.Arrays.toString(stringList));mergeSort(stringList, (a, b) -> b.compareTo(a));System.out.println("Sorted string list using Comparator: " + java.util.Arrays.toString(stringList));}
}

3.2.2 编写两个泛型方法来实现插入排序(Insertion Sort)

第一个方法使用 Comparable 接口进行排序,第二个方法使用 Comparator 接口进行排序。
示例代码如下。

import java.util.Comparator;public class InsertionSortExample {// 使用 Comparable 接口进行排序public static <E extends Comparable<E>> void insertionSort(E[] list) {for (int i = 1; i < list.length; i++) {E current = list[i];int j = i - 1;while (j >= 0 && list[j].compareTo(current) > 0) {list[j + 1] = list[j];j--;}list[j + 1] = current;}}// 使用 Comparator 接口进行排序public static <E> void insertionSort(E[] list, Comparator<? super E> comparator) {for (int i = 1; i < list.length; i++) {E current = list[i];int j = i - 1;while (j >= 0 && comparator.compare(list[j], current) > 0) {list[j + 1] = list[j];j--;}list[j + 1] = current;}}public static void main(String[] args) {Integer[] list = {2, 3, 2, 5, 6, 1, -2, 3, 14, 12};System.out.println("Original list: " + java.util.Arrays.toString(list));insertionSort(list);System.out.println("Sorted list using Comparable: " + java.util.Arrays.toString(list));String[] stringList = {"banana", "apple", "cherry", "date"};System.out.println("Original string list: " + java.util.Arrays.toString(stringList));insertionSort(stringList, (a, b) -> b.compareTo(a));System.out.println("Sorted string list using Comparator: " + java.util.Arrays.toString(stringList));}
}

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

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

相关文章

【element plus】解决报错error:ResizeObserver loop limit exceeded的问题

当我们在使用element plus框架时&#xff0c;有时会遇到屏幕突然变暗&#xff0c;然后来一句莫名其妙的报错ResizeObserver loop limit exceeded&#xff0c;其实这是因为改变屏幕大小时el-table导致的报错 网上给出了几种解决方案&#xff0c;我试了其中两种可以实现 方案一&…

LeetCode算法题(Go语言实现)_60

题目 给你一个整数数组 cost &#xff0c;其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用&#xff0c;即可选择向上爬一个或者两个台阶。 你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。 请你计算并返回达到楼梯顶部的最低花费。 一、代码实现…

马架构的Netty、MQTT、CoAP面试之旅

标题&#xff1a;马架构的Netty、MQTT、CoAP面试之旅 在互联网大厂的Java求职者面试中&#xff0c;一位名叫马架构的资深Java架构师正接受着严格的考验。他拥有十年的Java研发经验和架构设计经验&#xff0c;尤其对疑难问题和线索问题等有着丰富的经历。 第一轮提问&#xff…

焦化烧结行业无功补偿解决方案—精准分组补偿 稳定电能质量沃伦森

在焦化、烧结等冶金行业&#xff0c;负荷运行呈现长时阶梯状变化&#xff0c;功率波动相对平缓&#xff0c;但对无功补偿的分组精度要求较高。传统固定电容器组补偿方式无法动态跟随负荷变化&#xff0c;导致功率因数不稳定&#xff0c;甚至可能因谐波放大影响电网安全。 行业…

使用String path = FileUtilTest.class.getResource(“/1.txt“).getPath(); 报找不到路径

在windows环境运行&#xff0c;下面的springboot中path怎么找不到文件呢&#xff1f; path输出后的结果是&#xff1a;路径是多少&#xff1a;/D:/bjpowernode/msb/%e4%b9%90%e4%b9%8b%e8%80%85/apache%20commons/SpringBootBase6/target/test-classes/1.txt 怎么解决一下呢&am…

【C++】二叉树进阶面试题

根据二叉树创建字符串 重点是要注意括号省略问题&#xff0c;分为以下情况&#xff1a; 1.左字树为空&#xff0c;右子树不为空&#xff0c;左边括号保留 2.左右子树都为空&#xff0c;括号都不保留 3。左子树不为空&#xff0c;右子树为空&#xff0c;右边括号不保留 如果根节…

RSUniVLM论文精读

一些收获&#xff1a; 1. 发现这篇文章的table1中&#xff0c;有CDChat ChangeChat Change-Agent等模型&#xff0c;也许用得上。等会看看有没有源代码。 摘要&#xff1a;RSVLMs在遥感图像理解任务中取得了很大的进展。尽管在多模态推理和多轮对话中表现良好&#xff0c;现有模…

低空AI系统的合规化与标准化演进路径

随着AI无人机集群逐步参与城市空域治理、物流服务与公共安全作业&#xff0c;其系统行为不再是“技术封闭域”&#xff0c;而需接受法规监管、责任评估与接口协同的多方审查。如何将AI集群系统推向标准化、可接入、可审计的合规体系&#xff0c;成为未来空中交通演进的关键。本…

【金仓数据库征文】从云计算到区块链:金仓数据库的颠覆性创新之路

目录 一、引言 二、金仓数据库概述 2.1 金仓数据库的背景 2.2 核心技术特点 2.3 行业应用案例 三、金仓数据库的产品优化提案 3.1 性能优化 3.1.1 查询优化 3.1.2 索引优化 3.1.3 缓存优化 3.2 可扩展性优化 3.2.1 水平扩展与分区设计 3.2.2 负载均衡与读写分离 …

致远oa部署

文章目录 环境搭建项目构建 仅供学习使用 环境搭建 准备项目&#xff1a; https://pan.quark.cn/s/04a166575e94 https://pan.xunlei.com/s/VOOc1c9dBdLIuU8KKiqDa68NA1?pwdmybd# 官方文档: https://open.seeyoncloud.com/v5devCTP/ 安装时 mysql 数据库可能出现字符集设置…

移远通信智能模组助力东成“无边界智能割草机器人“闪耀欧美市场

2025年4月21日&#xff0c;移远通信宣布&#xff0c;旗下SC206E-EM智能模组已成功应用于江苏东成电动工具有限公司旗下的DCK TERRAINA无边界智能割草机器人。 这款智能模组高度集成计算、通信、定位等多元能力&#xff0c;以小型化、低功耗、实时性强和低成本等综合优势&#…

100.HTB-Meow

学习成果 在第一层&#xff0c;您将获得网络安全渗透测试领域的基本技能。您将首先学习如何匿名连接到各种服务&#xff0c;例如 FTP、SMB、Telnet、Rsync 和 RDP。接下来&#xff0c;您将发现 Nmap 的强大功能&#xff0c;Nmap 是一个有价值的工具&#xff0c;用于识别目标系统…

大厂面试-redis

前言 本章内容来自B站黑马程序员java大厂面试题和小林coding 博主学习笔记&#xff0c;如果有不对的地方&#xff0c;海涵。 如果这篇文章对你有帮助&#xff0c;可以点点关注&#xff0c;点点赞&#xff0c;谢谢你&#xff01; 1.redis的使用场景 1.1 缓存 缓存穿透 在布…

【含文档+PPT+源码】基于SpringBoot+vue的疫苗接种系统的设计与实现

项目介绍 本课程演示的是一款 基于SpringBootvue的疫苗接种系统的设计与实现&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运行本套系…

【Pandas】pandas DataFrame dot

Pandas2.2 DataFrame Binary operator functions 方法描述DataFrame.add(other)用于执行 DataFrame 与另一个对象&#xff08;如 DataFrame、Series 或标量&#xff09;的逐元素加法操作DataFrame.add(other[, axis, level, fill_value])用于执行 DataFrame 与另一个对象&…

Windows上Tomcat 11手动启动startup.bat关闭shutdown.bat

发现tomcat11无法手动双击startup.bat和shutdown.bat进行开启和关闭。双击startup.bat命令窗口一闪而过就是启动失败了&#xff0c;正常启动成功是cmd命令窗口有全副的执行输出且不关闭窗口。 解决方法如下&#xff1a;主要更改一个tomcat安装目录下的/conf/server.xml配置 1.…

7.9 Python+Click实战:5步打造高效的GitHub监控CLI工具

Python+Click实战:5步打造高效的GitHub监控CLI工具 GitHub Sentinel Agent 命令行界面开发实战 关键词:CLI 开发实践、Click 框架、API 集成、命令行参数解析、错误处理机制 1. 命令行界面技术选型与架构设计 GitHub Sentinel 采用 Click + Requests 技术栈构建 CLI 工具,…

安全框架概述

Java中的安全框架通常是指解决Web应用安全问题的框架&#xff0c;如果开发Web应用时没有使用安全框架&#xff0c;开发者需要自行编写代码增加Web应用安全性。自行实现Web应用的安全性并不容易&#xff0c;需要考虑不同的认证和授权机制、网络关键数据传输加密等多方面的问题&a…

配置 C/C++ 语言智能感知(IntelliSense)的 c_cpp_properties.json 文件内容

配置 C/C 语言智能感知&#xff08;IntelliSense&#xff09;的 c_cpp_properties.json 文件内容 {"configurations": [{"name": "Linux","includePath": ["${workspaceFolder}/**","/opt/ros/humble/include/**&quo…

【安全扫描器原理】网络扫描算法

【安全扫描器原理】网络扫描算法 1.非顺序扫描2.高速扫描 & 分布式扫描3.服务扫描 & 指纹扫描 1.非顺序扫描 参考已有的扫描器&#xff0c;会发现几乎所有的扫描器都无一例外地使用增序扫描&#xff0c;即对所扫描的端口自小到大依次扫描&#xff0c;殊不知&#xff0…