06-排序

排序

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.tpcf.cn/news/908376.html

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

相关文章

从失效文档到知识资产:Gitee Wiki 引领研发知识管理变革

在关键领域软件研发的复杂生态中&#xff0c;知识管理正成为制约行业发展的关键瓶颈。随着软件系统规模不断扩大、技术栈日益复杂&#xff0c;传统文档管理模式已难以满足现代软件工厂对知识沉淀、共享和传承的需求。Gitee Wiki作为新一代知识管理平台&#xff0c;通过技术创新…

MySQL 性能调优入门 - 慢查询分析与索引优化基础

MySQL 性能调优入门 - 慢查询分析与索引优化基础 性能问题诊断的通用思路 当数据库出现性能问题时,切忌盲目猜测或随意调整参数。一个科学的诊断流程通常包括: 基于数据,而非猜测 (Data-Driven, Not Guesswork):利用我们在上一篇讨论的性能监控指标和建立的基线。查看哪些…

8天Python从入门到精通【itheima】-73~74(数据容器“集合”+案例练习)

目录 73节-集合的基础定义和操作 1.学习目标 2.为什么要用集合 3.集合的定义 4.关于集合的常用操作 【1】添加新元素&#xff1a;add方法 【2】移除元素&#xff1a;remove方法 【3】随机取出元素&#xff1a;pop方法 【4】清空集合&#xff1a;clear方法 【5】取出两…

国芯思辰| AD7894的优质替代方案:SC1424模数转换器在分布式控制系统中的应用优势

分布式控制系统将控制任务分散至多个节点&#xff0c;各节点协同工作以实现复杂的控制目标。在这一架构下&#xff0c;系统ADC提出了严苛要求。高精度是精准采集各类模拟信号&#xff08;如传感器输出的电压、电流信号&#xff09;的基础&#xff0c;关乎控制决策的准确性&…

Unity基础-数学向量

Unity基础-数学向量 二、向量相关用法 概述 向量在Unity游戏开发中扮演着重要角色&#xff0c;用于表示位置、方向、速度等。Unity提供了Vector2、Vector3等结构体来处理向量运算。 1. 向量基础操作 1.1 向量创建和访问 // 创建向量 Vector3 position new Vector3(1, 2,…

Neo4j 数据建模:原理、技术与实践指南

Neo4j 作为领先的图数据库,其核心优势在于利用图结构直观地表达和高效地查询复杂关系。其数据建模理念与传统关系型数据库截然不同,专注于实体(节点)及其连接(关系)。以下基于官方文档,系统阐述其建模原理、关键技术、实用技巧及最佳实践: 一、 核心原理:以关系为中心…

volka 25个短语动词

以下是分句分段后的内容&#xff1a; 3,000. Thats 95% of spoken English. And I am teaching you all of these words. First, Ill teach you todays words. And then youll hear them in real conversations. With my brother. Stick around until the end, because witho…

服务器中日志分析的作用都有哪些

服务器日志是用来检测和排查可疑行为的主要工具&#xff0c;运维团队可以通过分析和解读日志文件&#xff0c;发现服务器中潜在的网络安全威胁或异常活动&#xff0c;下面&#xff0c;就让小编和大家一起来了解一下服务器中日志分析的作用都有什么吧&#xff01; 对于服务器中的…

嵌入式硬件篇---龙芯2k1000串口

针对串口错误 “device reports readiness to read but returned no data (Device disconnected or multiple access on port?)” 的排查和解决方法 硬件方面 检查连接 确认串口设备(如串口线、连接的模块等)与龙芯设备之间的物理连接是否牢固,没有松动、脱落情况。尝试重新…

基于langchain的简单RAG的实现

闲来无事&#xff0c;想研究一下RAG的实现流程&#xff0c;看网上用langchain的比较多&#xff0c;我自己在下面也跑了跑&#xff0c;代码很简单&#xff0c;以次博客记录一下&#xff0c;方便回顾 langchain LangChain 是一个基于大型语言模型&#xff08;LLM&#xff09;开发…

视频监控平台建设方案

第三方视频监控平台是整合视频监控、门禁、报警等多业务的安防软件系统,具备兼容性、开放性、多业务整合和多级联网能力。其核心价值在于兼容友商编解码设备(如 IPC、DVR)、整合第三方子系统(如报警联动)、支持多级多域架构(适应平安城市等大规模场景)及提供集中存储方案…

天机学堂(学习计划和进度)

经过前面的努力&#xff0c;我们已经完成了《我的课程表》相关的功能的基础部分&#xff0c;不过还有功能实现的并不完善。还记得昨天给大家的练习题吗&#xff1f;《查询我正在学习的课程》&#xff0c;在原型图中有这样的一个需求&#xff1a; 我们需要在查询结果中返回已学习…

软件项目管理(3) 软件项目任务分解

一、相关概念 1.任务分解的方法和步骤 &#xff08;1&#xff09;方法 模板参照方法&#xff1a;参照有标准或半标准的任分解结构图类比方法&#xff1a;任务分解结构图经常被重复使用&#xff0c;具有相似性自顶向下方法&#xff1a;一般->特殊&#xff0c;演绎推理从大…

Vite 双引擎架构 —— Esbuild 概念篇

Vite 底层采用 双引擎架构&#xff0c;核心构建引擎是 Esbuild 和 Rollup&#xff0c;二者在开发和生产环境中分工协作&#xff0c;共同实现高性能构建。不可否认&#xff0c;作为 Vite 的双引擎之一&#xff0c;Esbuild 在很多关键的构建阶段(如依赖预编译、TS 语法转译、代码…

leetcode hot100 链表(二)

书接上回&#xff1a; leetcode hot100 链表&#xff08;一&#xff09;-CSDN博客 8.删除链表的倒数第N个结点 class Solution { public:ListNode* removeNthFromEnd(ListNode* head, int n) {ListNode* currhead;int len0;while(curr){currcurr->next;len;}int poslen-n…

Compose Multiplatform 实现自定义的系统托盘,解决托盘乱码问题

Compose Multiplatform是 JetBrains 开发的声明式 UI 框架&#xff0c;可让您为 Android、iOS、桌面和 Web 开发共享 UI。将 Compose Multiplatform 集成到您的 Kotlin Multiplatform 项目中&#xff0c;即可更快地交付您的应用和功能&#xff0c;而无需维护多个 UI 实现。 在…

C++11 Move Constructors and Move Assignment Operators 从入门到精通

文章目录 一、引言二、基本概念2.1 右值引用&#xff08;Rvalue References&#xff09;2.2 移动语义&#xff08;Move Semantics&#xff09; 三、移动构造函数&#xff08;Move Constructors&#xff09;3.1 定义和语法3.2 示例代码3.3 使用场景 四、移动赋值运算符&#xff…

Linux配置yum 时间同步服务 关闭防火墙 关闭ESlinux

1、配置yum 1.1、Could not resolve host: mirrorlist.centos.org; 未知的错误 https://blog.csdn.net/fansfi/article/details/146369946?fromshareblogdetail&sharetypeblogdetail&sharerId146369946&sharereferPC&sharesourceRockandrollman&sharefr…

使用 uv 工具快速部署并管理 vLLM 推理环境

uv&#xff1a;现代 Python 项目管理的高效助手 uv&#xff1a;Rust 驱动的 Python 包管理新时代 在部署大语言模型&#xff08;LLM&#xff09;推理服务时&#xff0c;vLLM 是一个备受关注的方案&#xff0c;具备高吞吐、低延迟和对 OpenAI API 的良好兼容性。为了提高部署效…

基于sqlite的任务锁(支持多进程/多线程)

前言 介绍 任务锁,在多进程服务间控制耗时任务的锁,确保相同id的耗时任务同时只有一个在执行 依赖 SqliteOp,参考这篇文章 https://blog.csdn.net/weixin_43721000/article/details/137019125 实现方式 utils/taskLock.py import timefrom utils.SqliteOp import Sqli…