数据结构(排序)

1概述

一、定义

排序是将一组数据元素按照某个关键字的值递增或递减的次序重新排列的过程。这个关键字是数据元素中的某个数据项,通过比较关键字的大小来确定数据元素的先后顺序。

二、目的

  1. 便于查找
    • 例如在一个有序数组中查找某个元素,使用二分查找等算法效率更高。如果数据是无序的,可能只能进行顺序查找,时间复杂度较高。
  2. 数据呈现的有序性
    • 在很多应用场景中,如成绩排名、文件按时间顺序排列等,需要数据以有序的形式展示,以便于人们理解和分析。

三、分类

  1. 内部排序和外部排序
    • 内部排序
      • 指待排序的数据元素全部存放在计算机内存中的排序算法。常见的内部排序算法有冒泡排序、插入排序、选择排序、快速排序、归并排序、堆排序等。这些算法在处理相对较小规模的数据时效率较高,因为内存的读写速度相对较快。
    • 外部排序
      • 当待排序的数据量很大,内存无法一次性容纳全部数据时采用的排序方法。外部排序通常需要在内存和外部存储(如磁盘)之间进行数据交换,它会先将数据分成若干个小的数据块,分别进行内部排序,然后再将这些有序的数据块进行归并等操作,最终得到整体有序的序列。
  2. 基于比较的排序和非基于比较的排序
    • 基于比较的排序
      • 这种排序算法通过比较数据元素的关键字大小来确定它们的相对顺序。例如冒泡排序,它通过不断比较相邻元素的大小并交换来使较大(或较小)的元素逐步 “浮” 到数组的一端。常见的基于比较的排序算法的时间复杂度下限是,其中是待排序数据的个数。
    • 非基于比较的排序
      • 不依赖于元素之间的比较操作来确定顺序。例如计数排序,它是通过统计每个元素出现的次数来确定元素的顺序;桶排序则是将数据分到不同的桶中,然后在每个桶内进行排序;基数排序是根据数据的各位数字来进行排序。非基于比较的排序算法在特定情况下可以实现线性时间复杂度,但它们往往对数据有一定的要求,如数据的取值范围等。

2插入类排序方法

插入类排序方法主要包括直接插入排序、折半插入排序和希尔排序。

2.1直接插入排序

1 基本思想:直接插入排序是一种简单的排序算法,其基本思想是将待排序的数组分为已排序和未排序两部分,通过不断将未排序部分的元素插入到已排序部分的合适位置,逐步建立起一个有序序列。以下是对直接插入排序的详细介绍,包括其工作原理、算法实现以及时间复杂度分析。

2工作原理
直接插入排序的基本步骤如下:
1. 将数组的第一个元素看作已排序的初始状态,其他元素视为未排序。
2. 从未排序的元素中选择一个元素,将其与已排序部分的元素进行比较,并找到合适的位置进行插入。
3. 重复步骤 2,直到未排序部分的元素全部移动到已排序部分,从而完成排序。

3 示例
假设我们有一个数组 [5, 2, 9, 1, 5, 6],我们要对它进行直接插入排序。过程如下:

1. 初始数组:[5, 2, 9, 1, 5, 6]
2. 已排序部分:[5],未排序部分:[2, 9, 1, 5, 6]
   - 将 2 插入已排序部分,结果:[2, 5]
3. 已排序部分:[2, 5],未排序部分:[9, 1, 5, 6]
   - 9 已在正确位置,无需移动。
4. 已排序部分:[2, 5, 9],未排序部分:[1, 5, 6]
   - 将 1 插入,结果:[1, 2, 5, 9]
5. 已排序部分:[1, 2, 5, 9],未排序部分:[5, 6]
   - 将 5 插入,结果:[1, 2, 5, 5, 9]
6. 已排序部分:[1, 2, 5, 5, 9],未排序部分:[6]
   - 将 6 插入,结果:[1, 2, 5, 5, 6, 9]

最后得到排序后的数组:[1, 2, 5, 5, 6, 9]。

4C 语言实现
下面是直接插入排序的 C 语言实现代码:

#include <stdio.h>// 函数声明
void insertionSort(int arr[], int n);// 主函数
int main() {int arr[] = {5, 2, 9, 1, 5, 6};int n = sizeof(arr) / sizeof(arr[0]);printf("排序前的数组:\n");for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");insertionSort(arr, n);printf("排序后的数组:\n");for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");return 0;
}// 插入排序的函数定义
void insertionSort(int arr[], int n) {for (int i = 1; i < n; i++) { // 从第二个元素开始int key = arr[i]; // 当前待插入的元素int j = i - 1;// 找到适当的位置插入当前元素while (j >= 0 && arr[j] > key) {arr[j + 1] = arr[j]; // 向右移动元素j--;}arr[j + 1] = key; // 插入当前元素}
}

5时间复杂度
1. 最坏情况:O(n²)(数组是倒序的情况下需要进行最多的比较和移动)。
2. 最好情况:O(n)(数组已经是有序的情况下,只需进行 n-1 次比较)。
3. 平均情况:O(n²)。

6空间复杂度
直接插入排序是原地排序算法,因此其空间复杂度为 O(1)。

7稳定性
直接插入排序是稳定的,因为相等的元素在排序完成后依然保持它们在原始数组中的相对顺序。

8 总结
直接插入排序是一种简单、直观的排序方法,适用于数据量较小的场景。虽然在大规模数据上表现不佳,但由于其简单性和稳定性,常常会用于某些特定应用或作为其他复杂排序算法的基础。在许多实战场景中,直接插入排序用于处理几乎已排序的数组时表现尤其良好。

2.2希尔排序

1基本思想:希尔排序(Shell Sort)是一种改进的插入排序算法,由Donald Shell于1959年提出。它通过比较相隔一定间隔的元素来工作,而不是直接进行插入排序。随着算法的进行,间隔逐渐减小,直到变为1,此时算法退化为标准的插入排序。希尔排序的主要优点是其相对较高的效率和简单性。

2工作原理
希尔排序的核心思想是将数组分成若干子序列,分别对这些子序列进行插入排序。随着算法的进行,子序列的长度逐渐减小,最终整个数组变为一个子序列,完成最终的排序。主要步骤如下:

1. 选择一个增量序列(间隔序列),通常是递减的整数序列。
2. 根据当前的增量,将数组划分为若干个子序列。
3. 对每个子序列进行插入排序。
4. 重复步骤2和3,直到增量减小到1。
5. 最后,对整个数组进行一次标准的插入排序。

3示例
假设我们有一个数组 [5, 2, 9, 1, 5, 6],我们要对它进行希尔排序,使用增量序列 [3, 1](即先按间隔3进行排序,然后按间隔1进行排序)。过程如下:

1. 初始数组:[5, 2, 9, 1, 5, 6]
2. 第一步(间隔3):
   - 子序列1:[5, 1] → [1, 5]
   - 子序列2:[2, 5] → [2, 5]
   - 子序列3:[9, 6] → [6, 9]
   第一次排序后数组:[1, 2, 6, 5, 5, 9]
3. 第二步(间隔1):
   - 子序列:[1, 2, 6, 5, 5, 9]
   - 直接进行插入排序(由于间隔为1,此时退化为标准插入排序)
   最终排序后的数组:[1, 2, 5, 5, 6, 9]

4 C 语言实现
下面是希尔排序的 C 语言实现代码:

#include <stdio.h>// 函数声明
void shellSort(int arr[], int n);// 主函数
int main() {int arr[] = {5, 2, 9, 1, 5, 6};int n = sizeof(arr) / sizeof(arr[0]);printf("排序前的数组:\n");for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");shellSort(arr, n);printf("排序后的数组:\n");for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");return 0;
}// 希尔排序的函数定义
void shellSort(int arr[], int n) {// 初始增量为 n / 2,并对增量进行逐步缩小for (int gap = n / 2; gap > 0; gap /= 2) {// 对每个子序列进行插入排序for (int i = gap; i < n; i++) {int temp = arr[i];int j;// 插入排序for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) {arr[j] = arr[j - gap];}arr[j] = temp;}}
}

5时间复杂度
希尔排序的时间复杂度取决于所选的增量序列。不同的增量序列会导致不同的时间复杂度:
1. 最坏情况:对于某些增量序列,最坏情况下的时间复杂度为 O(n²)。
2. 最好情况:对于某些增量序列,时间复杂度可以达到 O(n log² n)。
3. 平均情况:取决于增量序列的选择,通常在 O(n*1.25) 到 O(n*1.5) 之间。

6空间复杂度
希尔排序是原地排序算法,因此其空间复杂度为 O(1)。

7稳定性
希尔排序是不稳定的,因为在排序过程中,相同的元素可能会在不同的子序列中进行交换,从而改变它们在原始数组中的相对顺序。

8 总结
希尔排序是一种高效的排序算法,特别适用于中等规模的数据。它通过减少内循环的比较和移动次数来提高效率,同时保持了插入排序的简单性。尽管其时间复杂度不是最优的,但在实践中它通常比其他 O(n²) 级别的排序算法有更好的性能。


2.3二分插入排序

1基本思想:二分插入排序(Binary Insertion Sort)是插入排序的一种优化版本。它通过使用二分查找来确定插入的位置,从而减少比较的次数,提高插入排序的效率。标准插入排序在查找插入位置时是线性查找,而二分插入排序则是使用二分查找,这在大规模数据上可以显著减少比较次数。

2工作原理
二分插入排序的基本步骤如下:
1. 将数组分为已排序和未排序两部分。
2. 从未排序部分的第一个元素开始,使用二分查找在已排序部分中找到合适的插入位置。
3. 将元素插入到找到的位置,并移动已排序部分中的其他元素。
4. 重复步骤2和3,直到未排序部分的元素全部插入到已排序部分。

3示例
假设我们有一个数组 [5, 2, 9, 1, 5, 6],我们要对它进行二分插入排序。过程如下:

1. 初始数组:[5, 2, 9, 1, 5, 6]
2. 当前元素:5(第一个元素,直接视为已排序),已排序部分:[5],未排序部分:[2, 9, 1, 5, 6]
3. 插入元素:2
   - 二分查找插入位置:low = 0, high = 0, mid = 0
   - 插入位置:0
   - 结果:[2, 5]
4. 插入元素:9
   - 二分查找插入位置:low = 1, high = 1, mid = 1
   - 插入位置:2
   - 结果:[2, 5, 9]
5. 插入元素:1
   - 二分查找插入位置:low = 0, high = 2, mid = 1, low = 0, high = 1, mid = 0
   - 插入位置:0
   - 结果:[1, 2, 5, 9]
6. 插入元素:5
   - 二分查找插入位置:low = 1, high = 3, mid = 2
   - 插入位置:3
   - 结果:[1, 2, 5, 5, 9]
7. 插入元素:6
   - 二分查找插入位置:low = 2, high = 4, mid = 3
   - 插入位置:3
   - 结果:[1, 2, 5, 5, 6, 9]

最终得到排序后的数组:[1, 2, 5, 5, 6, 9]。

4C 语言实现
下面是二分插入排序的 C 语言实现代码:

#include <stdio.h>// 函数声明
void binaryInsertionSort(int arr[], int n);// 主函数
int main() {int arr[] = {5, 2, 9, 1, 5, 6};int n = sizeof(arr) / sizeof(arr[0]);printf("排序前的数组:\n");for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");binaryInsertionSort(arr, n);printf("排序后的数组:\n");for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");return 0;
}// 二分插入排序的函数定义
void binaryInsertionSort(int arr[], int n) {for (int i = 1; i < n; i++) {int key = arr[i];int left = 0;int right = i - 1;// 二分查找插入位置while (left <= right) {int mid = left + (right - left) / 2;if (arr[mid] > key) {right = mid - 1;} else {left = mid + 1;}}// 找到插入位置后,移动元素for (int j = i - 1; j >= left; j--) {arr[j + 1] = arr[j];}arr[left] = key;}
}

       

5时间复杂度
1. 最坏情况:O(n²)(在最坏情况下,二分查找可以减少比较次数,但移动元素的次数仍然与标准插入排序相同)。
2. 最好情况:O(n log n)(数组已经是有序的情况下,比较次数减少为 O(log n) 次,但移动元素的次数仍为 O(n) 次)。
3. 平均情况:O(n²)(相比于标准插入排序,比较次数减少,但移动元素的次数仍为 O(n²) 次)。

6 空间复杂度
二分插入排序是原地排序算法,因此其空间复杂度为 O(1)。

7稳定性
二分插入排序是稳定的,因为相等的元素在排序完成后依然保持它们在原始数组中的相对顺序。

8总结
二分插入排序是一种对标准插入排序的优化,通过减少比较次数提高了算法的效率。尽管在平均和最坏情况下的时间复杂度仍然是 O(n²),但在某些情况下,二分插入排序比标准插入排序有更好的表现,特别是在数据量较大时。

3交换类排序方法

交换类排序方法是一类通过不断交换元素位置来实现排序的算法,主要包括冒泡排序和快速排序。

3.1冒泡排序

1基本思想:冒泡排序(Bubble Sort)是一种简单的排序算法,它通过重复地遍历要排序的列表,比较相邻的元素并交换它们的位置,直到整个列表排序完成。由于在排序过程中,较小的元素会逐渐“浮”到列表的顶部,较大的元素会“沉”到底部,因此得名“冒泡排序”。

2工作原理
冒泡排序的基本步骤如下:
1. 从列表的第一个元素开始,依次比较相邻的两个元素。
2. 如果前一个元素大于后一个元素,则交换它们的位置。
3. 继续遍历列表,直到没有需要交换的元素为止。
4. 重复上述步骤,直到整个列表排序完成。

3示例
假设我们有一个数组 [5, 2, 9, 1, 5, 6],我们要对它进行冒泡排序。过程如下:

1. 初始数组:[5, 2, 9, 1, 5, 6]
2. 第一轮遍历:
   - 比较 5 和 2,交换位置:[2, 5, 9, 1, 5, 6]
   - 比较 5 和 9,不交换位置:[2, 5, 9, 1, 5, 6]
   - 比较 9 和 1,交换位置:[2, 5, 1, 9, 5, 6]
   - 比较 9 和 5,交换位置:[2, 5, 1, 5, 9, 6]
   - 比较 9 和 6,交换位置:[2, 5, 1, 5, 6, 9]
3. 第二轮遍历:
   - 比较 2 和 5,不交换位置:[2, 5, 1, 5, 6, 9]
   - 比较 5 和 1,交换位置:[2, 1, 5, 5, 6, 9]
   - 比较 5 和 5,不交换位置:[2, 1, 5, 5, 6, 9]
   - 比较 5 和 6,不交换位置:[2, 1, 5, 5, 6, 9]
4. 第三轮遍历:
   - 比较 2 和 1,交换位置:[1, 2, 5, 5, 6, 9]
   - 比较 2 和 5,不交换位置:[1, 2, 5, 5, 6, 9]
   - 比较 5 和 5,不交换位置:[1, 2, 5, 5, 6, 9]
5. 第四轮遍历:
   - 比较 1 和 2,不交换位置:[1, 2, 5, 5, 6, 9]
   - 比较 2 和 5,不交换位置:[1, 2, 5, 5, 6, 9]
6. 第五轮遍历:
   - 比较 1 和 2,不交换位置:[1, 2, 5, 5, 6, 9]

最终得到排序后的数组:[1, 2, 5, 5, 6, 9]。

4 C 语言实现
下面是冒泡排序的 C 语言实现代码:

#include <stdio.h>// 函数声明
void bubbleSort(int arr[], int n);// 主函数
int main() {int arr[] = {5, 2, 9, 1, 5, 6};int n = sizeof(arr) / sizeof(arr[0]);printf("排序前的数组:\n");for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");bubbleSort(arr, n);printf("排序后的数组:\n");for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");return 0;
}// 冒泡排序的函数定义
void bubbleSort(int arr[], int n) {for (int i = 0; i < n - 1; i++) {// 标记是否发生了交换int swapped = 0;// 每一轮遍历for (int j = 0; j < n - 1 - i; j++) {if (arr[j] > arr[j + 1]) {// 交换元素int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;swapped = 1;}}// 如果没有发生交换,说明数组已经有序,提前退出if (swapped == 0) {break;}}
}

5时间复杂度
1. 最坏情况:O(n²)(当数组完全逆序时,需要进行 n(n-1)/2 次比较和交换)。
2. 最好情况 :O(n)(当数组已经有序时,只需要进行一次遍历,比较 n-1 次,但不进行交换)。
3. 平均情况:O(n²)(平均情况下,需要进行 n(n-1)/4 次比较和交换)。

6 空间复杂度
冒泡排序是原地排序算法,因此其空间复杂度为 O(1)。

7稳定性
冒泡排序是稳定的,因为相等的元素在排序完成后依然保持它们在原始数组中的相对顺序。

8总结
冒泡排序是一种简单但效率较低的排序算法,特别适合用于教学和理解排序算法的基本概念。尽管它在实际应用中很少使用,但它的实现简单,易于理解和调试。对于小规模数据或部分有序的数据,冒泡排序可能是一个合适的选择。

3.2快速排序

1基本思想:快速排序(Quick Sort)是一种高效的排序算法,采用分治法(Divide and Conquer)策略,将一个数组分为两个子数组,再分别对这两个子数组进行排序。快速排序的平均时间复杂度为 (O(n log n)),通常在实际应用中表现优越,因此被广泛使用。

2工作原理
快速排序的基本步骤如下:

1. 选择基准:从数组中选择一个元素作为“基准”(pivot),通常可以选择第一个元素、最后一个元素或者随机选择一个元素。
2. 分区:
   - 将数组重新排列,使得所有比基准小的元素位于基准的左边,所有比基准大的元素位于基准的右边。
   - 此时,基准元素就处于它最终的位置。
3. 递归排序:对基准左边和右边的子数组进行递归调用快速排序。

3示例
假设我们有一个数组 [9, 7, 5, 11, 12, 2, 14]。我们将使用快速排序对其进行排序,步骤如下:

1. 原始数组:[9, 7, 5, 11, 12, 2, 14]
2. 选择基准:选择最后一个元素12作为基准。
3. 分区过程:
   - 将小于12的元素移到左侧,>12的移到右侧。
   - 排序后:[9, 7, 5, 11, 2, 12, 14](12位于正确的位置)。
4. 递归对左边子数组 [9, 7, 5, 11, 2] 和右边子数组 `[14]` 进行快速排序。
5. 继续分区和递归,最终得到排序完成的数组 [2, 5, 7, 9, 11, 12, 14]。

4C 语言实现
快速排序的 C 语言实现代码如下:

#include <stdio.h>// 函数声明
void quickSort(int arr[], int low, int high);
int partition(int arr[], int low, int high);// 主函数
int main() {int arr[] = {9, 7, 5, 11, 12, 2, 14};int n = sizeof(arr) / sizeof(arr[0]);printf("排序前的数组:\n");for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");quickSort(arr, 0, n - 1);printf("排序后的数组:\n");for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");return 0;
}// 快速排序的函数定义
void quickSort(int arr[], int low, int high) {if (low < high) {int pi = partition(arr, low, high); // 获取分区索引// 递归排序quickSort(arr, low, pi - 1); // 排序基准左侧quickSort(arr, pi + 1, high); // 排序基准右侧}
}// 分区函数
int partition(int arr[], int low, int high) {int pivot = arr[high];  // 选择最后一个元素为基准int i = (low - 1);      // 小于基准的元素索引for (int j = low; j < high; j++) {if (arr[j] < pivot) {  // 如果当前元素小于基准i++;                // 增加小于基准的元素索引// 交换元素int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}// 交换基准元素到正确位置int temp = arr[i + 1];arr[i + 1] = arr[high];arr[high] = temp;return i + 1;  // 返回基准的索引
}

5时间复杂度
1. 最坏情况:(O(n*2))(当输入数组已经是有序的,且每次选择的基准是最大或最小元素时)。
2. 最好情况:(O(n log n))(每次划分都将数组平均分成两部分)。
3. 平均情况:(O(n log n))。

6 空间复杂度
快速排序是一个原地排序算法,空间复杂度为 (O(log n))(递归调用栈的深度)。最大情况下可能为 (O(n))。

7稳定性
快速排序是非稳定的。相同的元素在排序后可能会改变其相对位置。

8总结
快速排序是一种高效且广泛使用的排序算法,特别适合大规模数据的排序。它的优点是平均情况下速度较快,然而在最坏情况下可能会出现性能问题。因此,在实际应用中,常常结合其他排序方法(如随机化基准选择)来提高性能。

4选择类排序方法

选择类排序方法是一类通过不断选择最值元素来实现排序的算法,主要包括简单选择排序和堆排序。

4.1简单选择排序

1基本思想:简单选择排序(Simple Selection Sort)是选择排序算法的一种实现方式。其基本思想是通过逐步选择未排序部分的最小(或最大)元素并将其放到已排序部分的末尾。这个过程重复进行,直到整个数组都有序。

2工作原理

简单选择排序的基本步骤如下:

1. 找到最小元素:在未排序部分中找到最小(或最大)元素。
2. 交换位置:将找到的最小(或最大)元素与未排序部分的第一个元素交换位置。
3. 缩小范围:将已排序部分的边界向右扩展一位,未排序部分缩小一位。
4. 重复过程:重复步骤1-3,直到所有元素都被排序。

3 示例

假设我们有一个数组 [64, 25, 12, 22, 11],我们将使用简单选择排序对其进行排序,步骤如下:

1. 找到最小元素 11,和第一个元素 64 交换:[11,25, 12, 22, 64]
2. 找到最小元素 12,和第二个元素 25 交换:[11, 12,25, 22, 64]
3. 找到最小元素 22,和第三个元素 25 交换:[11, 12, 22, 25, 64]
4. 已排序的部分为 [11, 12, 22],剩下的 [25, 64] 依次到最后,不需要交换。

最终得到排序后的数组:[11, 12, 22, 25, 64]。

4C 语言实现

下面是简单选择排序的 C 语言实现代码:

#include <stdio.h>// 函数声明
void selectionSort(int arr[], int n);// 主函数
int main() {int arr[] = {64, 25, 12, 22, 11};int n = sizeof(arr) / sizeof(arr[0]);printf("排序前的数组:\n");for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");selectionSort(arr, n);printf("排序后的数组:\n");for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");return 0;
}// 简单选择排序的函数定义
void selectionSort(int arr[], int n) {for (int i = 0; i < n - 1; i++) {// 假设当前 i 位置是最小值int minIndex = i;// 在剩余部分寻找最小值for (int j = i + 1; j < n; j++) {if (arr[j] < arr[minIndex]) {minIndex = j;  // 更新最小值索引}}// 交换元素if (minIndex != i) {int temp = arr[i];arr[i] = arr[minIndex];arr[minIndex] = temp;}}
}

5 时间复杂度

1. 最坏情况:(O(n*2))(无论输入顺序如何,选择排序都需要进行 (n-1) 次查找)。
2. 最好情况:(O(n*2))(同上述情况)。
3. 平均情况:(O(n*2))。

6空间复杂度

简单选择排序是原地排序算法,空间复杂度为 \(O(1)\)。

7稳定性

简单选择排序是不稳定的,因为在交换时,相同的元素可能会改变相对顺序。

8总结

简单选择排序是一种简单易懂的排序算法,适合小规模的数据排序。在实际使用中,由于其时间复杂度较高,通常不用于大规模数据的排序。然而,选择排序的原理和实现过程对于学习其他更复杂的排序算法有很大的帮助。

4.2堆排序

堆的基本概念

  1. :堆是一种完全二叉树,分为最大堆和最小堆。

    • 最大堆:每个节点的值都大于或等于其子节点的值。
    • 最小堆:每个节点的值都小于或等于其子节点的值。
  2. 完全二叉树:除了最后一层外,其他层的节点都是满的,且最后一层的节点都尽量靠左。

1基本思想:利用堆这种数据结构的性质,将待排序序列构建成一个堆,然后不断取出堆顶元素,并调整堆使其继续满足堆的性质,从而实现排序。

2算法步骤

  1. 构建初始堆

    • 首先将待排序的数组看作是一个完全二叉树。
    • 从最后一个非叶子节点开始,逐步向上调整,使每个节点都满足堆的性质(对于大根堆,父节点的值大于等于子节点的值;对于小根堆,父节点的值小于等于子节点的值)。
  2. 排序过程

    • 取出堆顶元素,将其与当前无序序列的最后一个元素交换位置。
    • 对交换后的堆进行调整,使其重新成为一个堆。
    • 重复上述步骤,直到所有元素都被取出并排序完毕。

例如,对于数组 [4, 6, 8, 5, 9]

  • 构建大根堆:
    • 首先将数组看作完全二叉树:
         4/   \6     8/ \   /5   9
  • 从最后一个非叶子节点 6 开始调整,6 比其子节点 5 和 9 都大,无需调整。
  • 接着调整节点 4,4 小于其子节点 6 和 8,不满足大根堆性质。交换 4 和 8,得到:

         8/   \6     4/ \   /5   9
  • 继续调整,8 大于其子节点 6 和 5,无需调整。此时大根堆构建完成。

  • 排序过程:

    • 取出堆顶元素 8,与最后一个元素 9 交换,得到 [9, 6, 4, 5, 8]。
    • 对剩余元素调整为大根堆,从节点 6 开始调整,交换 6 和 9,得到 [9, 6, 4, 5, 8](此时无需继续调整)。
    • 取出堆顶元素 9,与最后一个元素 5 交换,得到 [5, 6, 4, 9, 8]。
    • 调整堆,交换 6 和 5,得到 [6, 5, 4, 9, 8]。
    • 取出堆顶元素 6,与最后一个元素 4 交换,得到 [4, 5, 6, 9, 8]。
    • 调整堆,交换 5 和 4,得到 [5, 4, 6, 9, 8]。
    • 取出堆顶元素 5,与最后一个元素 6 交换,得到 [6, 4, 5, 9, 8](此时无需继续调整)。
    • 取出堆顶元素 6,与最后一个元素 8 交换,得到 [8, 4, 5, 9, 6]。
    • 调整堆,交换 4 和 8,得到 [8, 4, 5, 9, 6](此时无需继续调整)。
    • 取出堆顶元素 8,与最后一个元素 9 交换,得到 [9, 4, 5, 8, 6]。
    • 调整堆,交换 4 和 9,得到 [9, 4, 5, 8, 6](此时无需继续调整)。
    • 取出堆顶元素 9,与最后一个元素 6 交换,得到 [6, 4, 5, 8, 9]。
    • 调整堆,交换 4 和 6,得到 [6, 4, 5, 8, 9](此时无需继续调整)。
    • 取出堆顶元素 6,与最后一个元素 8 交换,得到 [8, 4, 5, 6, 9]。
    • 调整堆,交换 4 和 8,得到 [8, 4, 5, 6, 9](此时无需继续调整)。
    • 取出堆顶元素 8,与最后一个元素 9 交换,得到 [9, 4, 5, 6, 8]。
    • 调整堆,交换 4 和 9,得到 [9, 4, 5, 6, 8](此时无需继续调整)。
    • 取出堆顶元素 9,排序完成,得到 [4, 5, 6, 8, 9]。
  • C 语言实现

    下面是堆排序的 C 语言实现代码

#include <stdio.h>// 函数声明
void heapSort(int arr[], int n);
void heapify(int arr[], int n, int i);
void swap(int *a, int *b);// 主函数
int main() {int arr[] = {4, 10, 3, 5, 1};int n = sizeof(arr) / sizeof(arr[0]);printf("排序前的数组:\n");for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");heapSort(arr, n);printf("排序后的数组:\n");for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");return 0;
}// 堆排序的函数定义
void heapSort(int arr[], int n) {// 构建最大堆for (int i = n / 2 - 1; i >= 0; i--) {heapify(arr, n, i);}// 逐个抽取元素for (int i = n - 1; i > 0; i--) {// 将当前根节点(最大值)与数组末尾元素交换swap(&arr[0], &arr[i]);// 重新调整堆heapify(arr, i, 0);}
}// 调整堆的函数定义
void heapify(int arr[], int n, int i) {int largest = i;  // 初始化最大值为根节点int left = 2 * i + 1;  // 左子节点int right = 2 * i + 2;  // 右子节点// 如果左子节点比根节点大if (left < n && arr[left] > arr[largest]) {largest = left;}// 如果右子节点比当前最大值大if (right < n && arr[right] > arr[largest]) {largest = right;}// 如果最大值不是根节点if (largest != i) {swap(&arr[i], &arr[largest]);// 递归调整受影响的子树heapify(arr, n, largest);}
}// 交换两个元素的函数定义
void swap(int *a, int *b) {int temp = *a;*a = *b;*b = temp;
}

时间复杂度

  1. 构建堆:时间复杂度为 O(n)。
  2. 排序:每次交换和调整堆的时间复杂度为 O(log⁡n),总共需要 n 次,因此总时间复杂度为 O(nlogn)。

空间复杂度

堆排序是原地排序算法,空间复杂度为 O(1)。

稳定性

堆排序是不稳定的,因为在交换时,相同的元素可能会改变相对顺序。

总结

堆排序是一种高效的排序算法,适合用于大规模数据的排序。它的时间复杂度为 O(nlog⁡n),并且是原地排序算法,空间复杂度为 O(1)(。由于其非稳定的特性,堆排序在某些场景下可能不如其他排序算法适用,但对于内存有限且需要高效排序的场景,堆排序是一个很好的选择。

5归并排序

归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法。

1、基本原理

归并排序采用分治(Divide and Conquer)策略,将待排序序列分成若干个子序列,分别对每个子序列进行排序,然后将已排序的子序列合并成一个有序序列。

2、算法步骤

  1. 分割:

    • 把长度为 n 的输入序列分成两个长度为 n/2 的子序列。
    • 如果子序列的长度不为 1,则继续分割,直到每个子序列的长度为 1。
  2. 归并:

    • 比较两个已排序的子序列的最小元素,将较小的元素取出并放入新的有序序列中。
    • 重复这个过程,直到两个子序列中的所有元素都被合并到新的有序序列中。

例如,对于序列 [5, 3, 8, 2, 7, 1, 6, 4]:

  • 分割过程:
    • 第一次分割得到 [5, 3, 8, 2] 和 [7, 1, 6, 4]。
    • 继续分割得到 [5, 3]、[8, 2]、[7, 1]、[6, 4]。
    • 再分割得到 [5]、[3]、[8]、[2]、[7]、[1]、[6]、[4]。
  • 归并过程:
    • 合并 [5] 和 [3] 得到 [3, 5]。
    • 合并 [8] 和 [2] 得到 [2, 8]。
    • 合并 [7] 和 [1] 得到 [1, 7]。
    • 合并 [6] 和 [4] 得到 [4, 6]。
    • 继续合并 [3, 5] 和 [2, 8] 得到 [2, 3, 5, 8]。
    • 合并 [1, 7] 和 [4, 6] 得到 [1, 4, 6, 7]。
    • 最后合并 [2, 3, 5, 8] 和 [1, 4, 6, 7] 得到 [1, 2, 3, 4, 5, 6, 7, 8]。

3C 语言中的归并排序实现

以下是一个用 C 语言实现的归并排序的示例代码:

#include <stdio.h>// 合并两个已排序的子数组
void merge(int arr[], int left, int mid, int right) {int i, j, k;int n1 = mid - left + 1;  // 左半部分的大小int n2 = right - mid;     // 右半部分的大小// 创建临时数组int L[n1], R[n2];// 将数据复制到临时数组 L[] 和 R[]for (i = 0; i < n1; i++) {L[i] = arr[left + i];}for (j = 0; j < n2; j++) {R[j] = arr[mid + 1 + j];}// 合并临时数组 L[] 和 R[]i = 0; // 初始化左半部分的索引j = 0; // 初始化右半部分的索引k = left; // 初始化合并后的数组的索引while (i < n1 && j < n2) {if (L[i] <= R[j]) {arr[k] = L[i];i++;} else {arr[k] = R[j];j++;}k++;}// 复制 L[] 的剩余部分(如果有的话)while (i < n1) {arr[k] = L[i];i++;k++;}// 复制 R[] 的剩余部分(如果有的话)while (j < n2) {arr[k] = R[j];j++;k++;}
}// 归并排序的递归实现
void mergeSort(int arr[], int left, int right) {if (left < right) {int mid = left + (right - left) / 2; // 防止溢出// 递归排序左半部分和右半部分mergeSort(arr, left, mid);mergeSort(arr, mid + 1, right);// 合并已排序的部分merge(arr, left, mid, right);}
}// 打印数组的函数
void printArray(int arr[], int size) {for (int i = 0; i < size; i++) {printf("%d ", arr[i]);}printf("\n");
}// 主函数
int main() {int arr[] = {38, 27, 43, 3, 9, 82, 10};int arr_size = sizeof(arr) / sizeof(arr[0]);printf("原始数组:\n");printArray(arr, arr_size);mergeSort(arr, 0, arr_size - 1);printf("排好序的数组:\n");printArray(arr, arr_size);return 0;
}

4、时间复杂度

归并排序的时间复杂度始终为 O(nlogn),其中 n 是待排序元素的个数。这是因为无论原始序列的状态如何,每次分割都将问题规模减半,而每次归并操作需要O(n)  的时间。

5、空间复杂

归并排序的空间复杂度为O(n) ,因为在归并过程中需要额外的空间来存储临时的有序序列

6、稳定性

归并排序是稳定的排序算法。在归并过程中,当比较两个相等的元素时,会优先将来自前面子序列的元素放入新的有序序列中,从而保证了相等元素的相对顺序不变。

6线性时间排序算法

线性时间排序算法是指能够在与输入规模成线性关系的时间内完成排序的算法。常见的线性时间排序算法有计数排序、基数排序和桶排序。

6.1计数排序

  1. 基本原理

    • 计数排序是一种非比较型整数排序算法。它通过统计每个元素在序列中出现的次数,然后根据元素的出现次数确定其在排序后的序列中的位置。
  2. 算法步骤

    • 找出待排序数组中的最大和最小元素,确定计数范围。
    • 创建一个计数数组,其长度为计数范围,初始值为 0。
    • 遍历待排序数组,统计每个元素出现的次数,将次数存入计数数组中对应位置。
    • 对计数数组进行累加操作,使得每个位置上的数值表示小于等于该位置对应元素的个数。
    • 创建一个结果数组,用于存储排序后的结果。
    • 从后往前遍历待排序数组,根据计数数组确定每个元素在结果数组中的位置,并将元素放入结果数组中,同时对计数数组对应位置的值减 1。
  3. 实例

    • 待排序数组为 [2, 5, 3, 0, 2, 3, 0, 3]。
    • 首先确定计数范围为 0 到 5。
    • 创建计数数组 [0, 0, 0, 0, 0, 0]。
    • 统计每个元素出现的次数后,计数数组变为 [2, 0, 0, 3, 1, 1]。
    • 进行累加操作后,计数数组变为 [2, 2, 2, 5, 6, 7]。
    • 创建结果数组,从后往前遍历待排序数组,第一个元素 3 在计数数组中对应位置的值为 5,将 3 放入结果数组的第 4 个位置,同时计数数组对应位置的值减 1,变为 [2, 2, 2, 4, 6, 7]。依次类推,最终结果数组为 [0, 0, 2, 2, 3, 3, 3, 5]。
  4. C 语言代码实现

#include <stdio.h>
#include <stdlib.h>void countingSort(int arr[], int size) {int max = arr[0];int min = arr[0];// 找到数组中的最大值和最小值for (int i = 1; i < size; i++) {if (arr[i] > max) {max = arr[i];}if (arr[i] < min) {min = arr[i];}}// 计数数组的大小为 max - min + 1int range = max - min + 1;int count[range];int output[size];// 初始化计数数组为 0for (int i = 0; i < range; i++) {count[i] = 0;}// 统计每个元素的出现次数for (int i = 0; i < size; i++) {count[arr[i] - min]++;}// 累加计数数组for (int i = 1; i < range; i++) {count[i] += count[i - 1];}// 根据累加后的计数数组放置元素for (int i = size - 1; i >= 0; i--) {output[count[arr[i] - min] - 1] = arr[i];count[arr[i] - min]--;}// 将排序后的元素复制回原数组for (int i = 0; i < size; i++) {arr[i] = output[i];}
}// 打印数组的函数
void printArray(int arr[], int size) {for (int i = 0; i < size; i++) {printf("%d ", arr[i]);}printf("\n");
}// 主函数
int main() {int arr[] = {4, 2, 2, 8, 3, 3, 1};int size = sizeof(arr) / sizeof(arr[0]);printf("原始数组:\n");printArray(arr, size);countingSort(arr, size);printf("排好序的数组:\n");printArray(arr, size);return 0;
}
6.2基数排序
  1. 基本原理

    • 基数排序是一种非比较型整数排序算法。它按照位数将整数进行分组,然后对每一位分别进行排序,从最低位到最高位依次进行,最终得到有序序列。
  2. 算法步骤

    • 确定待排序数组中的最大元素,确定排序的位数。
    • 从最低位开始,依次对每一位进行排序。
    • 对于每一位的排序,可以使用稳定的计数排序或其他线性时间排序算法。
    • 重复步骤 2 和 3,直到对最高位进行排序完成。
  3. 实例

    • 待排序数组为 [123, 45, 678, 90, 234]。
    • 首先确定最大元素为 678,有三位数,所以进行三次排序。
    • 第一次对个位进行排序,得到 [90, 45, 123, 234, 678]。
    • 第二次对十位进行排序,得到 [45, 90, 123, 234, 678]。
    • 第三次对百位进行排序,得到 [45, 90, 123, 234, 678],排序完成。
  4. C 语言代码实现

    #include <stdio.h>
    #include <stdlib.h>// 获取数组中的最大值
    int getMax(int arr[], int size) {int max = arr[0];for (int i = 1; i < size; i++) {if (arr[i] > max) {max = arr[i];}}return max;
    }// 基数排序中的计数排序部分
    void countingSortForRadix(int arr[], int size, int exp) {int output[size];int count[10] = {0};// 统计每个数字的出现次数for (int i = 0; i < size; i++) {count[(arr[i] / exp) % 10]++;}// 累加计数数组for (int i = 1; i < 10; i++) {count[i] += count[i - 1];}// 根据累加后的计数数组放置元素for (int i = size - 1; i >= 0; i--) {output[count[(arr[i] / exp) % 10] - 1] = arr[i];count[(arr[i] / exp) % 10]--;}// 将排序后的元素复制回原数组for (int i = 0; i < size; i++) {arr[i] = output[i];}
    }// 基数排序
    void radixSort(int arr[], int size) {int max = getMax(arr, size);// 对每一位进行计数排序for (int exp = 1; max / exp > 0; exp *= 10) {countingSortForRadix(arr, size, exp);}
    }// 打印数组的函数
    void printArray(int arr[], int size) {for (int i = 0; i < size; i++) {printf("%d ", arr[i]);}printf("\n");
    }// 主函数
    int main() {int arr[] = {170, 45, 75, 90, 802, 24, 2, 66};int size = sizeof(arr) / sizeof(arr[0]);printf("原始数组:\n");printArray(arr, size);radixSort(arr, size);printf("排好序的数组:\n");printArray(arr, size);return 0;
    }

6.3桶排序
  1. 基本原理

    • 桶排序是一种分布式排序算法。它将待排序元素分配到不同的桶中,然后对每个桶中的元素进行单独排序,最后将所有桶中的元素依次取出,得到有序序列。
  2. 算法步骤

    • 确定桶的数量和每个桶的范围。
    • 将待排序元素分配到各个桶中。
    • 对每个桶中的元素进行排序,可以使用其他排序算法。
    • 依次取出各个桶中的元素,得到有序序列。
  3. 实例

    • 待排序数组为 [0.89, 0.56, 0.65, 0.12, 0.68, 0.34]。
    • 假设将数据分为 5 个桶,每个桶的范围为 [0, 0.2), [0.2, 0.4), [0.4, 0.6), [0.6, 0.8), [0.8, 1.0)。
    • 将元素分配到各个桶中后,第一个桶中有 0.12,第二个桶中有 0.34,第三个桶中有 0.56 和 0.65,第四个桶中有 0.68,第五个桶中有 0.89。
    • 对每个桶中的元素进行插入排序(这里可以使用任何排序算法)。
    • 依次取出各个桶中的元素,得到有序序列 [0.12, 0.34, 0.56, 0.65, 0.68, 0.89]。
  4. C 语言代码实现

    #include <stdio.h>
    #include <stdlib.h>// 定义链表节点结构
    typedef struct ListNode {double data;struct ListNode* next;
    } ListNode;// 插入排序函数,用于对链表进行排序
    void insertionSortList(ListNode* head) {if (head == NULL || head->next == NULL) {return;}ListNode* sortedList = NULL;ListNode* current = head;while (current!= NULL) {ListNode* next = current->next;if (sortedList == NULL || current->data < sortedList->data) {current->next = sortedList;sortedList = current;} else {ListNode* temp = sortedList;while (temp->next!= NULL && temp->next->data < current->data) {temp = temp->next;}current->next = temp->next;temp->next = current;}current = next;}head = sortedList;
    }// 桶排序函数
    void bucketSort(double arr[], int n) {// 创建桶数组,并初始化每个桶为 NULLListNode** buckets = (ListNode**)malloc(n * sizeof(ListNode*));for (int i = 0; i < n; i++) {buckets[i] = NULL;}// 将元素分配到各个桶中for (int i = 0; i < n; i++) {int bucketIndex = arr[i] * n;ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));newNode->data = arr[i];newNode->next = buckets[bucketIndex];buckets[bucketIndex] = newNode;}// 对每个桶中的元素进行排序for (int i = 0; i < n; i++) {insertionSortList(buckets[i]);}// 将各个桶中的元素依次取出,得到有序序列int index = 0;for (int i = 0; i < n; i++) {ListNode* current = buckets[i];while (current!= NULL) {arr[index++] = current->data;ListNode* temp = current;current = current->next;free(temp);}}free(buckets);
    }

    7通用类型数据的排序

通用类型数据的排序是指将一组包含不同数据类型(如整数、浮点数、字符等)或者自定义数据类型的数据按照特定的规则进行重新排列的过程。

C 语言中的通用类型数据排序

在 C 语言中,没有像 Python 中的 sorted() 函数那样的内置排序函数。通常,你需要自己实现排序算法。C 标准库中提供了 qsort() 函数,可以对任意类型的数组进行排序。

qsort() 函数

qsort() 函数可以对任何类型的数组进行排序。它的原型如下:

void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));

  • base:指向要排序的数组的指针。
  • nmemb:数组中元素的数量。
  • size:每个元素的大小(以字节为单位)。
  • compar:比较函数的指针,用于指定排序的顺序。

示例:对整数数组进行排序

#include <stdio.h>
#include <stdlib.h>// 比较函数:用于比较两个整数
int compare_int(const void *a, const void *b) {return (*(int *)a - *(int *)b);
}int main() {int numbers[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};int n = sizeof(numbers) / sizeof(numbers[0]);// 使用 qsort 对数组进行排序qsort(numbers, n, sizeof(int), compare_int);// 输出排序后的数组for (int i = 0; i < n; i++) {printf("%d ", numbers[i]);}printf("\n");return 0;
}

示例:对字符串数组进行排序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 比较函数:用于比较两个字符串
int compare_string(const void *a, const void *b) {return strcmp(*(const char **)a, *(const char **)b);
}int main() {const char *words[] = {"apple", "banana", "cherry", "date"};int n = sizeof(words) / sizeof(words[0]);// 使用 qsort 对字符串数组进行排序qsort(words, n, sizeof(const char *), compare_string);// 输出排序后的字符串数组for (int i = 0; i < n; i++) {printf("%s ", words[i]);}printf("\n");return 0;
}

示例:学生数据排序

假设我们要根据学生的成绩对学生数据进行排序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 定义学生结构体
typedef struct {char name[50];  // 学生姓名int age;        // 学生年龄float score;    // 学生成绩
} Student;// 比较函数:按成绩排序
int compare_scores(const void *a, const void *b) {Student *studentA = (Student *)a;Student *studentB = (Student *)b;// 按分数升序排列,如果成绩相同则按姓名排序if (studentA->score != studentB->score) {return (studentA->score > studentB->score) ? 1 : -1;} else {return strcmp(studentA->name, studentB->name);}
}int main() {// 创建学生数据Student students[] = {{"Alice", 20, 88.5},{"Bob", 22, 95.0},{"Charlie", 21, 85.0},{"David", 23, 90.0},{"Eva", 19, 88.5}};int n = sizeof(students) / sizeof(students[0]);// 排序学生数据qsort(students, n, sizeof(Student), compare_scores);// 输出排序后的学生数据printf("Sorted student data (by score):\n");for (int i = 0; i < n; i++) {printf("Name: %s, Age: %d, Score: %.2f\n", students[i].name, students[i].age, students[i].score);}return 0;
}

总结:

排序算法时间复杂度(平均)时间复杂度(最坏)时间复杂度(最好)空间复杂度稳定性所需额外空间说明
冒泡排序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(nlogn)O(n²)O(nlogn)O(logn) - O(n)不稳定递归调用需要栈空间,取决于递归深度。
归并排序O(nlogn)O(nlogn)O(nlogn)O(n)稳定需要额外的与原数组大小相同的空间用于临时存储。
堆排序O(nlogn)O(nlogn)O(nlogn)O(1)不稳定仅需几个临时变量用于交换元素和调整堆。
希尔排序O (nlog²n) - O (n²)(较复杂)O(n²)O(nlog²n)O(1)不稳定仅需几个临时变量用于交换元素。
计数排序O (n + k)(k 为数据范围)O(n + k)O(n + k)O(k)稳定需要额外的空间存储计数数组和临时数组,大小取决于数据范围 k。
桶排序O (n + k)(k 为桶的数量)O(n²)O(n + k)O(n + k)稳定(取决于桶内排序算法)需要额外的空间创建桶和临时存储,大小取决于桶的数量和数据分布。
基数排序O (d (n + k))(d 为位数,k 为进制)O(d(n + k))O(d(n + k))O(n + k)稳定需要额外的空间存储计数数组和临时数组,大小取决于数

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

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

相关文章

DeepACO:用于组合优化的神经增强蚂蚁系统解决TSP问题的代码阅读

总体概括 DeepACO与普通ACO不同的是将问题输入实例输入到一个训练的网络中&#xff0c;将网络训练成为一个类似于专家知识的模块&#xff0c;可以生成相应的启发式矩阵网络&#xff0c;从而省去相应的专家知识。 其中在训练网络的代码中&#xff1a; 是进行监督式训练通过trai…

TCL Android面试题大全及参考答案

能谈谈Jetpack组件吗? Jetpack 是一套用于 Android 开发的工具和组件库,它可以帮助开发者更高效地构建高质量的 Android 应用。 一、主要组件分类 架构组件: ViewModel:负责存储和管理与界面相关的数据,当屏幕旋转或配置发生变化时,ViewModel 可以帮助保存数据,避免数据…

shutil模块简介

shutil 是 Python 标准库中的一个模块&#xff0c;主要用于文件和目录的高阶操作。 以下是 shutil 模块的一些常见功能&#xff1a; 复制文件和目录&#xff1a; shutil.copy(src, dst): 复制文件内容和权限。dst 可以是文件路径或目录路径。如果是目录路径&#xff0c;文件将…

大模型基础:基本概念、Prompt、RAG、Agent及多模态

随着大模型的迅猛发展&#xff0c;LLM 作为人工智能的核心力量&#xff0c;正以前所未有的方式重塑着我们的生活、学习和工作。无论是智能语音助手、自动驾驶汽车&#xff0c;还是智能决策系统&#xff0c;大模型都是幕后英雄&#xff0c;让这些看似不可思议的事情变为可能。本…

前端框架选择指南

前端框架选择指南 引言 在搭建现代网站时,你可能会面临一个常见但又重要的抉择——该选择哪个前端框架?这是一个看似简单的问题,但却隐藏着无数的选择和复杂性。前端框架就像建筑的蓝图,为建筑师(开发者)提供了构建结构的方式。而不同的框架则对应了不同的建筑风格和材…

软键盘一直存在实现

在此记录系统需要提供方法给APP可以控制当接入物理键盘时软键盘保持显示实现方法&#xff0c;网上找了很多方法都不管用&#xff0c;最终还是要自己去研究源码和系统设置内功能&#xff0c;最终找到了一个超级简单的方法&#xff1b;我们只需要在\packages\apps\Settings\src\c…

java中的I/O(8个案例+代码+效果图)

目录 1.File类 1&#xff09;常用构造方法 1&#xff09;File(String pathname) 2&#xff09;File(String parent, String child) 3&#xff09;File(File parent, String child) 2&#xff09;常用方法 1&#xff09;boolean canRead() 2&#xff09;boolean canWrite() 3&am…

计算机网络——ftp

在网络通信中&#xff0c;控制连接和数据连接是两种不同类型的连接&#xff0c;它们各自具有特定的功能和用途。 一、控制连接 定义与功能&#xff1a; 控制连接主要用于在通信双方之间传输控制信息&#xff0c;以建立、维护和终止数据连接。它负责协调和管理数据传输的过程&am…

Leetcode - 周赛418

目录 一&#xff0c;3309. 连接二进制表示可形成的最大数值 二&#xff0c;3310. 移除可疑的方法 三&#xff0c;3311. 构造符合图结构的二维矩阵 四&#xff0c;3312. 查询排序后的最大公约数 一&#xff0c;3309. 连接二进制表示可形成的最大数值 本题数据范围较小&#…

操作系统中的进程管理详细介绍——进程的调度与通信

进程管理是操作系统中至关重要的功能之一&#xff0c;它负责协调和管理计算机系统中运行的所有进程。以下是对进程管理各个方面的详细介绍&#xff1a; 1. 进程调度 进程调度是操作系统决定哪个进程在何时运行的过程&#xff0c;目的是最大化CPU的利用率和系统的整体性能。常…

chatGPT模型接口分享

前言: 仅供学习和交流&#xff0c;请合理使用。 API&#xff1a;https://api.gptnet.org key&#xff1a;sk-x9Rmq3HeHh5z9EIi8wFaXCl02OfxRSk5UAFodYm1o4zo5X3i 支持模型&#xff1a;gpt-3.5-turbo、gpt-3.5-turbo-16k、gpt-4o-mini、llama-3.1-405b 暂时支持以上四个模型…

Java基础-基础知识体系小结 Q/A

文章目录 知识体系Q&AJava 中应该使用什么数据类型来代表价格?怎么将 byte 转换为 String?Java 中怎样将 bytes 转换为 long 类型?存在两个类&#xff0c;B 继承 A&#xff0c;C 继承 B&#xff0c;我们能将 B 转换为 C 么? 如 C (C) B&#xff1b;Java 中 操作符是线…

Java初阶~~四种内部类总结

文章目录 1.内部类的分类2.局部内部类2.1.基本语法2.2就近原则的理解 3.匿名内部类3.1基于接口的匿名内部类3.2基于普通类的匿名内部类3.3基于抽象类的匿名内部类3.4匿名内部类的细节3.5匿名内部类实践3.5.1作为实参进行传递3.5.2实践案例 4.成员内部类4.1基本介绍4.2外部类&am…

5本一投就中的极速期刊,性价比高,1周-1个月录用,见刊极快!

在当今快节奏的学术界&#xff0c;研究者们不仅追求高质量的研究成果&#xff0c;还希望能够迅速地将这些成果分享给全球的同行。为此&#xff0c;科检易学术精心挑选了10本以高效审稿流程著称的期刊&#xff0c;这些期刊不仅性价比高&#xff0c;而且从投稿到录用的时间极短&a…

ARM base instruction -- asr

算术右移&#xff0c;结果带符号。 Arithmetic Shift Right (immediate) shifts a register value right by an immediate number of bits, shifting in copies of the sign bit in the upper bits and zeros in the lower bits, and writes the result to the destination reg…

数据结构_绪论(选择题)

2-1 在数据结构中&#xff0c;从逻辑上可以把数据结构分成&#xff08; &#xff09;。 A.动态结构和静态结构 B.紧凑结构和非紧凑结构 C.线性结构和非线性结构 D.内部结构和外部结构 2-2 与数据元素本身的形式、内容、相对位置、个数无关的是数据的&#xff08; &am…

SD NAND Flash 小容量存储解决方案及其STM32测试例程讲解

文章目录 前言一、Flash闪存是什么&#xff1f;二、SD NAND Flash三、STM32测试例程四、总结 前言 随着移动存储技术的快速发展和便携式数字设备的广泛应用&#xff0c;Flash闪存作为非易失性存储解决方案&#xff0c;在各种电子设备中扮演着越来越重要的角色。本文提供关于Fl…

【含开题报告+文档+PPT+源码】基于SpringBoot乡村助农益农平台的设计与实现

开题报告 近年来&#xff0c;随着社会经济的快速发展和人民生活水平的提高&#xff0c;人们对优质农产品的需求越来越高。然而&#xff0c;传统的农产品销售管理模式存在一些问题。首先&#xff0c;农产品供应链信息不透明&#xff0c;导致生产者难以了解市场需求和价格变动趋…

iMazing只能苹果电脑吗 Win和Mac上的iMazing功能有区别吗

在当今数字时代&#xff0c;管理和备份手机数据变得越来越重要。无论是转移照片、备份短信&#xff0c;还是管理应用程序&#xff0c;一个强大的工具可以大大简化这些操作。iMazing作为一款备受好评的iOS设备管理软件&#xff0c;已经成为许多用户的选择。但是&#xff0c;许多…

《C++代码热更新:为高效开发注入新活力》

一、引言 在软件开发的过程中&#xff0c;我们常常面临着这样的挑战&#xff1a;当程序已经部署到生产环境后&#xff0c;发现了一些需要紧急修复的 bug 或者需要添加新的功能。传统的方法是停止程序运行&#xff0c;进行代码修改&#xff0c;然后重新编译、部署&#xff0c;这…