06-排序

article/2025/6/7 14:42:12

排序

1. 排序的概念及其应用

1.1 排序的概念

排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。

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

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

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

1.2 常见的排序算法

在这里插入图片描述

2. 常见排序算法的实现

在下面的算法中会频繁使用到比较和交换,这里把这两个功能实现以下,下面直接调用。

// 设置一个比较函数,调整升序降序时只需要修改这个函数即可
bool Com(int a, int b)
{return a < b;// <为升序,>为降序
}// 交换函数
void Swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}

2.1 插入排序

2.1.1 基本思想

思想:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。

类似于打扑克牌时,码牌的过程。

在这里插入图片描述

2.1.2 直接插入排序

当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移。

// 插入排序
void InsertSort(int* a, int n)
{for (int i = 1; i < n; i++){int tmp = a[i];int pos = i - 1;while (pos >= 0)if (!Com(a[pos], tmp))a[pos + 1] = a[pos--];elsebreak;a[pos + 1] = tmp;}
}

直接插入排序的特性总结:

  1. 元素集合越接近有序,直接插入排序算法的时间效率越高

  2. 时间复杂度: O ( N 2 ) O(N^2) O(N2)

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

  4. 稳定性:稳定

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

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数gap,把待排序文件中所有记录分成若干个组,所有距离相同的记录分在同一组内,并对每一组内的记录进行排序。然后减小gap,我们这里使gap=gap/3+1(缩小gap时,必须保证最后的gap为1),重复上述分组和排序的工作。当gap=1时,所有记录在统一组内排好序

// 希尔排序
void ShellSort(int* a, int n)
{int gap = n;while (gap > 1){gap = gap / 3 + 1;// 对每个分组进行直接插入排序for (int i = gap; i < n; i++){int tmp = a[i];int pos = i - gap;while (pos >= 0)if (!Com(a[pos], tmp)){a[pos + gap] = a[pos];pos -= gap;}elsebreak;a[pos + gap] = tmp;}}
}

希尔排序的特性总结:

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

  2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。

  3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些书中给出的希尔排序的时间复杂度都不固定。

    数据结构(C语言版)》— 严蔚敏

    在这里插入图片描述

    《数据结构-用面相对象方法与C++描述》— 殷人昆

    在这里插入图片描述

    我们这里的gap是按照Knuth提出的方式进行取值的,而且Knuth进行了大量的试验统计,我们暂时就按照: O ( N 1.25 ) − O ( 1.6 ∗ N 1.25 ) O(N^{1.25})-O(1.6*N^{1.25}) O(N1.25)O(1.6N1.25)来算。

  4. 稳定性:不稳定

2.2选择排序

2.2.1 基本思想

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

2.2.2 直接选择排序
  • 在元素集合array[i]–array[n-1]中选择关键码最大(小)的数据元素
  • 若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换
  • 在剩余的array[i]–array[n-2](array[i+1]–array[n-1])集合中,重复上述步骤,直到集合剩余1个元素
// 选择排序
void SelectSort(int* a, int n)
{for (int i = n - 1; i > 0; i--){int pos = i;for (int j = 0; j < i; j++)if (Com(a[pos], a[j]))pos = j;Swap(&a[i], &a[pos]);}
}

优化

// 选择排序优化,两端同时进行排序
void SelectSort(int* a, int n)
{int begin = 0;int end = n - 1;while (begin < end){int mini = begin;int maxi = end;for (int i = begin; i <= end; i++){if (Com(a[i], a[mini]))mini = i;if (Com(a[maxi], a[i]))maxi = i;}Swap(&a[begin], &a[mini]);if (maxi == begin)maxi = mini;Swap(&a[end], &a[maxi]);begin++;end--;}
}

直接选择排序的特性总结:

  1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用

  2. 时间复杂度: O ( N 2 ) O(N^2) O(N2)

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

  4. 稳定性:不稳定

2.2.3 堆排序

利用堆这个数据结构进行排序,是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。

(关于数据结构—堆,详见05-二叉树-CSDN博客)

// 堆---向下调整
void AdjustDwon(int* a, int n, int root)
{int child = root * 2 + 1;while (child < n){if (child + 1 < n && Com(a[child], a[child + 1]))child++;if (!Com(a[root], a[child])) break;Swap(&a[root], &a[child]);root = child;child = root * 2 + 1;}
}// 堆排序
void HeapSort(int* a, int n)
{for (int i = n / 2; i >= 0; i--)AdjustDwon(a, n, i);for (int i = n - 1; i > 0; i--){Swap(&a[0], &a[i]);AdjustDwon(a, i, 0);}
}

直接选择排序的特性总结:

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

  2. 时间复杂度: O ( N ∗ l o g N ) O(N*logN) O(NlogN)

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

  4. 稳定性:不稳定

2.3 交换排序

2.3.1 基本思想

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

2.3.2 冒泡排序

很基础的算法,这里不过多介绍。(详细介绍:012-C语言指针(2)-CSDN博客)

// 冒泡排序
void BubbleSort(int* a, int n)
{for (int i = n - 1; i > 0; i--){bool flag = true;for (int j = 0; j < i; j++)if (Com(a[j + 1], a[j])){flag = false;Swap(&a[j + 1], &a[j]);}if (flag) break;}
}

冒泡排序的特性总结:

  1. 冒泡排序是一种非常容易理解的排序

  2. 时间复杂度: O ( N 2 ) O(N^2) O(N2)

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

  4. 稳定性:稳定

2.3.3 快速排序

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

// 假设按照升序对array数组中[left, right)区间中的元素进行排序
void QuickSort(int array[], int left, int right)
{if (right - left <= 1)return;// 按照基准值对array数组的 [left, right)区间中的元素进行划分int div = partion(array, left, right);// 划分成功后以div为边界形成了左右两部分 [left, div) 和 [div+1, right)// 递归排[left, div)QuickSort(array, left, div);// 递归排[div+1, right)QuickSort(array, div + 1, right);
}

上述为快速排序递归实现的主框架,发现与二叉树前序遍历规则非常像,在写递归框架时可想想二叉树前序遍历规则即可快速写出来,后序只需分析如何按照基准值来对区间中数据进行划分的方式即可。

关于选择基准值:

为了提高效率,选择基准值时要尽量避免选择到最值,所以这里实现一个选择函数。

// 筛选key
int GetMidi(int* a, int begin, int end)
{int midi = (end + begin) / 2;if (a[begin] > a[end]){if (a[midi] > a[end]){if (a[begin] > a[midi])return midi;elsereturn begin;}elsereturn end;}else{if (a[begin] > a[midi])return begin;else{if (a[midi] > a[end])return end;elsereturn midi;}}
}

将区间按照基准值划分为左右两半部分的常见方式有三种(这里以升序序列为例):

  1. hoare版本

    对于一个待排序序列,选择其中任意一个值作为基准值key,把这个基准值拿出来,与序列中的第一个元素交换位置,剩余的序列中,使用两个指针,分别指向序列的头+1(因为现在第一个位置存放了基准值key)和尾(开区间),右指针一直–,直到碰到<key的值,左指针一直++,直到碰到>key的值然后停下,然后将左指针和右指针指向的值交换,再重复上述操作,直到两个指针相遇,此时两个指针都指向<=key的值(这里不做证明,注意上面指针移动的顺序),此时将基准值和这个值进行交换,序列就分成了两段。

    int PartSort1(int* a, int begin, int end)// 基础法
    {int midi = GetMidi(a, begin, end);// 优化,排除最值做key,提升排序效率Swap(&a[begin], &a[midi]);int keyi = begin;int left = begin;int right = end;while (left < right){while (left < right && !Com(a[right], a[keyi]))right--;while (left < right && !Com(a[keyi], a[left]))left++;Swap(&a[left], &a[right]);}Swap(&a[keyi], &a[left]);return left;
    }
    
  2. 挖坑法

    对于一个待排序序列,选择一个基准值,将这个基准值和序列第一个值进行交换,然后将这个基准值key记录下来,将左指针和右指针分别指向第一个元素和最后一个元素,此时第一个元素的位置是一个“坑”,因为这里的值已经被我们提取出来了,此时判断右指针指向的元素,只要该元素>=key,右指针–,直到碰见小于key的值,将这个值放到左指针指向的“坑”,此时右指针指向一个“坑”,判断左指针指向的元素,只要该元素<=key左指针++,直到碰见大于key的值,将该值放到右指针指向的“坑”中,反复上面过程,直到左右指针相遇,此时它们共同指向一个坑,将我们记录的基准值放到这个坑中即可。

    int PartSort2(int* a, int begin, int end)// 挖坑法
    {int midi = GetMidi(a, begin, end);// 优化,排除最值做key,提升排序效率Swap(&a[begin], &a[midi]);int key = a[begin];int left = begin;int right = end;while (left < right){while (right > left && !Com(a[right], key))right--;a[left] = a[right];while (right > left && !Com(key, a[left]))left++;a[right] = a[left];}a[left] = key;return left;
    }
    
  3. 前后指针版本

    同样,对于一个待排序序列,选一个基准值key,放在第一个位置,此时我们需要两个指针prev和cur,分别指向第一个元素和第二个元素,cur一直++,直到碰到;小于key的元素,将这个元素和prev+1位置的元素交换位置,prev++,然后重复上述操作,直到cur碰到end,交换prev和begin位置(key)的元素,单趟排序结束,在这个过程中,(begin,prev]代表小于等于key的序列,(prev,cur)代表大于等于key的序列,[cur,end)代表待排序序列。

    int PartSort3(int* a, int begin, int end)// 双指针法
    {int midi = GetMidi(a, begin, end);// 优化,排除最值做key,提升排序效率Swap(&a[begin], &a[midi]);int keyi = begin;int cur = begin + 1;int prev = begin;while (cur <= end){if (Com(a[cur], a[keyi]) && ++prev != cur)Swap(&a[prev], &a[cur]);cur++;}Swap(&a[keyi], &a[prev]);return prev;
    }
    

快速排序主体

// 快速排序 - 递归实现
void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;// 当待排序序列较小时,可以直接使用插入排序,可以有效减少递归的深度。当然不加也是可以的。if (end - begin + 1 <= 10)// 小区间优化,减少递归深度,防止栈溢出InsertSort(a + begin, end - begin + 1);else{//三种方法实现单趟排序,此处任选一种单趟排序即可//int keyi = PartSort1(a, begin, end);//常规//int keyi = PartSort2(a, begin, end);//挖坑int keyi = PartSort3(a, begin, end);//双指针QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);}
}

上面实现的是递归的快速排序,我们还可以使用迭代的方法来实现,但这需要借助栈的数据结构(关于栈,参考:04-栈和队列-CSDN博客,在下面的实现中,使用这篇文章中实现的栈的接口)。

// 快速排序 - 非递归实现(用栈的数据结构实现)
void QuickSortNonR(int* a, int begin, int end)
{Stack ST;StackInit(&ST);StackPush(&ST, end);StackPush(&ST, begin);while (!StackEmpty(&ST)){int left = StackTop(&ST);StackPop(&ST);int right = StackTop(&ST);StackPop(&ST);//进行单趟排序,任选一种即可//int keyi = PartSort1(a, left, right);//常规//int keyi = PartSort2(a, left, right);//挖坑int keyi = PartSort3(a, left, right);//双指针if (right > keyi + 1){StackPush(&ST, right);StackPush(&ST, keyi + 1);}if (left < keyi - 1){StackPush(&ST, keyi - 1);StackPush(&ST, left);}}StackDestroy(&ST);
}

快速排序的特性总结:

  1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
  2. 时间复杂度: O ( N ∗ l o g N ) O(N*logN) O(NlogN)
  3. 空间复杂度: O ( l o g N ) O(logN) O(logN)
  4. 稳定性:不稳定

2.4 归并排序

2.4.1 基本思想

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

在这里插入图片描述

2.4.2 具体实现

递归实现

void _MergeSort(int* a, int begin, int end, int* tmp)
{if (begin >= end)return;int midi = (end + begin) / 2;_MergeSort(a, begin, midi, tmp);_MergeSort(a, midi + 1, end, tmp);int begin1 = begin;int begin2 = midi + 1;int i = begin;while (i <= end){if (Com(a[begin1], a[begin2])){tmp[i] = a[begin1];begin1++;}else{tmp[i] = a[begin2];begin2++;}i++;if (begin1 > midi || begin2 > end)break;}if (begin1 > midi)while (i <= end)tmp[i++] = a[begin2++];elsewhile (i <= end)tmp[i++] = a[begin1++];memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}// 归并排序 - 递归实现
void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc error");exit(-1);}_MergeSort(a, 0, n - 1, tmp);free(tmp);
}

迭代实现

// 归并排序 - 非递归实现
void MergeSortNonR(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc error");exit(-1);}int gap = 1;while (gap < n){for (int i = 0; i < n; i += 2 * gap){int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;if (end1 >= n || begin2 >= n)break;if (end2 >= n)end2 = n - 1;int j = begin1;while (j <= end2){if (Com(a[begin2], a[begin1]))tmp[j++] = a[begin1++];elsetmp[j++] = a[begin2++];if (begin1 > end1 || begin2 > end2)break;}while (begin1 <= end1)tmp[j++] = a[begin1++];while (begin2 <= end2)tmp[j++] = a[begin2++];memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));}gap *= 2;}
}

归并排序的特性总结:

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

  2. 时间复杂度: O ( N ∗ l o g N ) O(N*logN) O(NlogN)

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

  4. 稳定性:稳定

2.5 非比较排序(计数排序)

2.5.1 基本思想

计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。 操作步骤:

  1. 统计相同元素出现次数

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

在这里插入图片描述

2.5.2 具体实现
// 计数排序 - 效率奇高,适用于数据范围较小时
void CountSort(int* a, int n)
{int max = a[0];int min = a[0];for (int i = 0; i < n; i++){if (a[i] > max)max = a[i];if (a[i] < min)min = a[i];}int* count = (int*)calloc(max - min + 1, sizeof(int));if (count == NULL){perror("calloc error");exit(-1);}for (int i = 0; i < n; i++)count[a[i] - min]++;int i = 0;int j = 0;while (i <= max - min){if (count[i]){a[j++] = i + min;count[i]--;}elsei++;}free(count);
}

计数排序的特性总结:

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

  2. 时间复杂度: O ( M A X ( N , 范围 ) ) O(MAX(N,范围)) O(MAX(N,范围))

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

  4. 稳定性:稳定

3. 排序算法复杂度及稳定性分析

在这里插入图片描述

在这里插入图片描述


http://www.hkcw.cn/article/VWCygHewys.shtml

相关文章

MS1023/MS1224——10MHz 到 80MHz、10:1 LVDS 并串转换器(串化器)/串并转换器(解串器)

产品简述 MS1023 串化器和 MS1224 解串器是一对 10bit 并串 / 串并转 换芯片&#xff0c;用于在 LVDS 差分底板上传输和接收 10MHz 至 80MHz 的并行字速率的串行数据。起始 / 停止位加载后&#xff0c;转换为负载编 码输出&#xff0c;串行数据速率介于 120Mbps…

Cyber Weekly #58

赛博新闻 1、DeepSeek新版R1更新&#xff0c;幻觉率大幅降低 5月28日&#xff0c;DeepSeek-R1模型已升级至DeepSeek-R1-0528版本&#xff0c;核心在于显著提升模型的思维深度与推理能力。该版本基于DeepSeek V3 Base模型&#xff0c;通过强化后训练显著优化了在数学、编程及通…

换一条宽带ip地址会变吗?同一个宽带如何不同ip地址

宽带IP地址是否变化取决于更换的方式&#xff0c;以及你使用的是公网IP还是内网IP。以下是具体分析&#xff0c;并附上同一个宽带下切换IP的实用方法&#xff1a; &#x1f310; 一、更换宽带是否会改变IP地址&#xff1f; 1. 更换宽带线路&#xff08;如从电信换到移动&#x…

环境对象以及回调函数

1.环境对象 2.回调函数

SQL Indexes(索引)

目录 Indexes Using Clustered Indexes Using Nonclustered Indexes Declaring Indexes Using Indexes Finding Rows Without Indexes Finding Rows in a Heap with a Nonclustered Index Finding Rows in a Clustered Index Finding Rows in a Clustered Index with …

graphviz, dot, Error: lost rA sA edge; 独立的模块

1) 有向图dot文件 digraph R { node [shaperecord]; { ranksame rA sA tA } { ranksame uB vB wB } rA -> sA; sA -> vB; t -> rA; uB -> vB; wB -> u; wB -> tA; } 2&#xff09;出现报警信息 Warning: flat edge between adjacent …

SpringBoot接入Kimi实践记录轻松上手

kimi简单使用 什么是Kimi API 官网&#xff1a;https://platform.moonshot.cn/ Kimi API 并不是一个我所熟知的广泛通用的术语。我的推测是&#xff0c;你可能想问的是关于 API 的一些基础知识。API&#xff08;Application Programming Interface&#xff0c;应用程序编程接…

Windows版PostgreSQL 安装 vector 扩展

问题 spring-ai在集成PGVector向量存储的时候会报错如下&#xff0c;那么就需要安装pgsql的vector扩展。 SQL [CREATE EXTENSION IF NOT EXISTS vector]; 错误: 无法打开扩展控制文件 "C:/Program Files/PostgreSQL/9.6/share/extension/vector.control": No such …

【操作系统原理08】文件管理

文章目录 零.大纲一.文件管理0.大纲1.文件管理1.1 **文件属性**1.2 文件内部数据组织1.3 文件之间的组织1.4操作系统提供功能1.5 文件在外存存放 二.文件的逻辑结构0.大纲1.无结构文件2.有结构文件 三.文件目录0.大纲1.文件控制块2.目录结构3.索引节点(FCB改进) 四.文件共享0.大…

力扣面试150题--二叉搜索树中第k小的元素

Day 58 题目描述 思路 直接采取中序遍历&#xff0c;不过我们将k参与到中序遍历中&#xff0c;遍历到第k个元素就结束 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* …

Linux网络基础概念(1)

文章目录 前言一、计算机网络背景网络发展认识协议 二、网络协议协议分层OSI七层模型TCP/IP五层&#xff08;或四层&#xff09;模型 三、网络传输基本流程同局域网的两台主机通信跨网络的两台主机通信 四、网络中的地址管理认识IP地址认识MAC地址 总结 前言 到网络喽&#xff…

【Typst】6.布局函数

概述 上节我们介绍了文档结构元素的函数&#xff0c;本节介绍一些控制布局使用的函数&#xff0c;掌握他们之后你可以更进一步的控制页面元素的布局。 系列目录 1.Typst概述2.Typst标记语法和基础样式3.Typst脚本语法4.导入、包含和读取5.文档结构元素与函数6.布局函数 对齐…

初识高通camx

一、chi和camx之间如何通信&#xff1a; Chi对Camx的操作&#xff0c;需要通过 ExtensionModule 进行操作&#xff0c;因此&#xff0c;CamX对外提供的接口扩展需要通过ExtensionModule进行&#xff0c;里面一个重要的变量就是g_chiContextOps。 Camx对Chi的操作&#xff0c;是…

NebulaAI V2.6.0发布:工作流功能正式上线!

2025年5月30日&#xff0c;NebulaAI V2.6.0版本正式上线&#xff01; 在V2.6.0版本中&#xff0c;NBAI团队对工作流功能进行了重磅升级&#xff0c;用户可以通过创建工作流来与大模型交互&#xff1a; 支持选择常用大模型进行工作流的交互&#xff1b; 支持文件上传&#…

Flowith,有一种Agent叫无限

大家好&#xff0c;我是羊仔&#xff0c;专注AI工具、智能体、编程。 今天羊仔要和大家聊聊一个最近发现的超级实用的Agent平台&#xff0c;名字叫Flowith。 这篇文章会带你从零了解到实战体验&#xff0c;搞清楚Flowith是如何让工作效率飙升好几倍&#xff0c;甚至重新定义未…

【Linux系统编程】库制作与原理

目录 理解软硬链接 动态库与静态库 手动制作静态库并使用 制作静态库 使用静态库方法一 使用静态库方法二 使用静态库方法三 手动制作动态库并使用 制作动态库 使用动态库方法一 使用动态库方法二 使用动态库方法三 动静态库同时使用的细节说明 动态库的理解、动…

SAP学习笔记 - 开发20 - 前端Fiori开发 Nest View(嵌套视图) ,Fragment(片段)

上一章讲了Page和Panel&#xff0c;Shell Control(信箱效果)&#xff0c;Margin / Padding&#xff0c;自定义CSS。 SAP学习笔记 - 开发19 - 前端Fiori开发 Page和Panel&#xff0c;Shell Control(信箱效果)&#xff0c;Margin / Padding&#xff0c;自定义CSS-CSDN博客 本章…

选择正确的电平转换解决方案

1. 简介 在目前大多数电子系统中&#xff0c;对电压电平转换的需求非常普遍。 例如&#xff0c; ASIC可能在电源电压 VCCA 下工作&#xff0c;而 I/O器件可能在电源电压VCCB下工作。 为了使这些器件间能够互相通信&#xff0c;需要如下图所示的电平转换解决方案。   电子器件…

OpenLayers:通过自动布局调整解决Overlay重叠问题

一、解决Overlay重叠问题的尝试 我在最近的开发工作中遇到了一个问题。我开发的项目需要给地图上的站点添加Tooltip提示框&#xff08;即Overlay&#xff09;&#xff0c;但是由于地图上的部分站点比较密集&#xff0c;导致Tooltip的重叠比较严重&#xff0c;部分Tooltip的内容…

7.5- Loading a pretrained LLM

Chapter 7-Fine-tuning to follow instructions 7.5- Loading a pretrained LLM 开始微调前&#xff0c;我们先加载GPT2模型&#xff0c;加载 3.55 亿参数的中型版本&#xff0c;因为 1.24 亿模型太小&#xff0c;无法通过指令微调获得定性合理的结果 ​ 加载 gpt2-medium (…