排序算法(C语言版)

前言

排序作为生产环境中常见的需求之一,对整个产品有举足轻重的影响,可以说使用一个合适的排序算法是业务逻辑中比较重要的一部分。今天我们就来介绍常见的排序算法以及实现

排序

所谓排序无非就是按照特定的规则对一组数据就行顺序化。

常见的排序有升序和降序。

对于数字类型的数据常见的规则是按照其大小排序。如果学过Java的朋友应该知道,Java引入了对象的概念,对象的排序规则一般需要程序员自己定义,实现Comparable或者Comparator接口,在在接口内部实现排序的逻辑

除排序本身的定义外,我们还需要了解一点关于排序的性质

  • 稳定性:当“大小”一致的两个数据应该如何规定两者的顺序呢?比如一个班级中有两位考100分的同学,我们应该如何规定两者的顺序呢?显然,一个常见的想法是谁先交卷谁是第一名。这种保证“交卷顺序”的排序算法,我们可以描述为稳定的排序算法。稳定性即保证排序后“大小”一致的数据顺序与排序前一致
  • 内/外排序:这是一组相对的概念。内部排序要求排序的数据全部在内存中完成排序。外部排序要求排序的数据在硬盘内存中移动来完成排序,常用于数据量巨大或者内存不够的时候使用。

常见的排序算法

常见的排序算法有比较类的排序:冒泡排序,插入排序,希尔排序,选择排序,堆排序,快速排序,归并排序

非比较类的排序:基数排序,计数排序,桶排序

下面我们就一个一个来了解上述算法的思想和代码实现,以下笔者均以排升序举例。

笔者在排序算法的实现中可能会用到交换算法,在这里先实现,后面不在介绍

//交换元素
void swap(int* p1, int* p2) {int tmp = *p1;*p1 = *p2;*p2 = tmp;
}

这里是一个简答的交换,我们只是把它封装一层,实现代码的复用

冒泡排序

冒牌排序,思路是相邻两个数据就行比较,遍历一遍为一趟排序。一趟排序后可以保证本趟最大值位于合适的位置。由此,每趟排序后下一趟的比较次数可以优化。也可以定义一个标记,如果本趟没有交换,整个序列均有序

实现

//冒泡排序
void BubbleSort(int* a, int n);void BubbleSort(int* a, int n)
{for (int y = 0; y < n - 1; y++){int flag = 1;int len = n - y;for (int i = 0; i < len - 1; i++){if (a[i] > a[i + 1]){swap(a + i + 1, a + i);flag = 0;}}if (flag) return;}
}

1. 冒泡排序是一种非常容易理解的排序
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1)
4. 稳定性:稳定

插入排序

插入排序,思路是假设前n个数据已经有序,第n+1个数据插入到有序的序列中。这个算法的适应性很强

实现

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

1. 元素集合越接近有序,直接插入排序算法的时间效率越高
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1)
4. 稳定性:稳定
 

希尔排序

希尔排序是对插入排序的优化。插入排序的缺陷在于,在将降序排序成升序时,后面的较小值越来越难以移动到前面的位置,因为移动的次数由1,2,3……往上自增。希尔排序的优化在于增加跨度的方让序列更快的接近有序。 引入一个gap变量,即数据分为gap组,以组为单位进行插入排序。不断改变gap的值,有两个常用的gap算式 gap=gap/2 和 gap=gap/3+1。这两个算式均可以保证最后一次gap为1,即保证最后一次希尔排序转换为插入排序(此时序列接近有序,前面介绍插入排序时介绍插入排序的适应性很强,表现在排序一个接近有序的序列时效率很高)。当然也可以自定义gap算式,但是需要保证gap最后一次的取值是1

实现

//希尔排序
void ShellSort(int* a, int n);void ShellSort(int* a, int n)
{int gap = n;while (gap > 1){gap = gap / 3 + 1;int i = 0;while (i < gap){int y = i;while (1){if (y >= n || y + gap >= n)break;if (a[y + gap] < a[y]) {int x = y;int tmp = a[y + gap];while (x >= 0 && tmp < a[x]){a[x + gap] = a[x];x -= gap;}a[x + gap] = tmp;}y += gap;}i++;}}
}

1. 可以理解为gap!=1时是预排序,gap=1时是插入排序。希尔排序是对插入排序的优化
2. 时间复杂度:O(N^1.3)
3. 空间复杂度:O(1)
4. 稳定性:不稳定

选择排序

选择排序是一个比较简单的排序算法,主要的逻辑就是每一趟找出最小的值,和本趟开头位置的元素呼唤。这里可以有一点优化,就是在一趟遍历的时候,同时找最大值和最小值。

实现

这里我们提供两个版本的实现,优化和非优化的版本(说是优化,其实性能并没有提升多少)

//选择排序
void SelectSort(int* a, int n);//优化版,同时选择大小
void SelectSort1(int* a, int n);//只选择小void SelectSort1(int* a, int n)//取小交换
{for (int y = 0; y < n - 1; y++) {int i = y;int tmp = y + 1;for (; tmp < n; tmp++){if (a[tmp] < a[i])i = tmp;}swap(a + i, a + y);}
}
void SelectSort(int* a, int n)//优化,取大小交换
{int begin = 0;int end = n - 1;while (begin < end){int min = begin;int max = begin;for (int i = begin + 1; i <= end; i++){if (a[i] < a[min])min = i;else if (a[i] > a[max])max = i;}swap(a + begin, a + min);if (begin == max)max = min;swap(a + end, a + max);begin++;end--;}
}

1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1)
4. 稳定性:不稳定
 

堆排序

堆排序是依赖于堆这个数据结构实现的一种排序算法。什么是堆这里不再展开。我们想要用堆排序算法实现升序,需要用所有的数据建立一个大根堆,然后不断的pop堆顶的元素与堆末尾(逻辑上是堆,实际上是数组)元素交换,调整堆……知道堆中只剩一个元素,此时排序完成。

实现

//堆排序
void HeapSort(int* a, int n);void xiangXiaTiaoZheng(int* arr, int pr, int k) {//向下调整int ch = pr * 2 + 1;while (ch < k) {if (ch + 1 < k && arr[ch + 1] > arr[ch]) ch++;if (arr[ch] > arr[pr]) {swap(arr + ch, arr + pr);pr = ch;ch = pr * 2 + 1;}else return;}
}
void jianDui(int* arr, int k)//建堆
{for (int i = (k - 2) / 2; i >= 0; i--) {xiangXiaTiaoZheng(arr, i, k);}
}
void HeapSort(int* a, int n)
{jianDui(a, n);int end = n - 1;while (end > 0) {swap(a, a + end);xiangXiaTiaoZheng(a, 0, end);end--;}
}

1. 堆排序使用堆来选数,效率就高了很多(topK问题)。向下调整建堆的算法要比向上调整建堆的算法效率高
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(1)
4. 稳定性:不稳定
 

快速排序

快速排序的思想是以每趟开始的元素为基准,将大于基准的元素放在基准数的右边,将小于基准的元素放在基准的左边,此时可以保证基准位于合适的位置。然后再分别处理基准的左半边和右半边,知道所有的元素都位于合适的位置上。

我们可以将实现分类为递归实现和非递归实现

递归实现

递归实现的大体逻辑是相同的,只不过在每一趟的实现有可以分为不同的版本。这里我们介绍三种不同的实现方式——hoare版本、挖坑版本、双指针版本

hoare版本

这里单趟的逻辑是以左侧开始位置的元素为基准元素,右边开始向左遍历在第一个小于基准元素的位置停下,左边开始向右遍历在第一个大于基准元素的位置停下,交换左右的元素,以上逻辑循环直到左右相遇,退出循环再交换相遇位置和起始位置的元素。

void QuickSort(int* a, int begin, int end);//hoare版本void QuickSort(int* a, int begin, int end)//hoare版本
{if (begin >= end)return;/*if ((end - begin + 1) < 10){InsertSort(a + begin, end - begin + 1);return;}int z = QuZhong(a, begin, end);swap(a + begin, a + z);*/int tmp = a[begin];int left = begin;int right = end;while (left < right){while (left < right && a[right] >= tmp){right--;}while (left < right && a[left] <= tmp){left++;}swap(a + left, a + right);}swap(a + begin, a + left);QuickSort(a, begin, left - 1);QuickSort(a, left + 1, end);
}

此版本是最初实现快速排序是使用的版本。

为了方便理解,后面再开发出双指针法和挖坑法

挖坑版本

挖坑法单趟的思路是以左侧开始位置的元素为基准元素,同时将左侧开始位置的元素记录到tmp中,设置此位置为坑。右边开始向左遍历在第一个小于基准元素的位置停下,将此位置的元素放到坑中,再将此位置设置为坑。将左边开始向右遍历在第一个大于基准元素的位置停下,将此位置的元素放到坑中,再将此位置设置为坑。以上逻辑循环直到左右相遇,退出循环,此时相遇的位置是坑位,将tmp添入坑位中

void QuickSort1(int* a, int begin, int end);//挖坑法版本void QuickSort1(int* a, int begin, int end)
{if (begin >= end)return;/*if ((end - begin + 1) < 10){InsertSort(a + begin, end - begin + 1);return;}int z = QuZhong(a, begin, end);swap(a + begin, a + z);*/int tmp = a[begin];int tmpi = begin;int left = begin;int right = end;while (left < right){while (left < right && a[right] >= tmp){right--;}if (left < right){swap(a + tmpi, a + right);tmpi = right;left++;}while (left < right && a[left] <= tmp){left++;}if (left < right){swap(a + tmpi, a + left);tmpi = left;right--;}}QuickSort1(a, begin, left - 1);QuickSort1(a, left + 1, end);
}

双指针版本

以本趟开始位置的元素为基准元素。定义两个指针变量,第一个指针prev指向本趟开始的元素,第二个指针cut指向本趟的第二个元素。如果cut指向的元素小于等于基准,则prev指针+1,prev与cut元素交换,cut指针+1:否则cut指针+1。直到cut指向空指针,退出循环,将基准元素与prev指向的元素互换。

void QuickSort2(int* a, int begin, int end);//双指针版本void QuickSort2(int* a, int begin, int end)
{if (begin >= end)return;/*if ((end - begin + 1) < 10){InsertSort(a + begin, end - begin + 1);return;}int z = QuZhong(a, begin, end);swap(a + begin, a + z);*/int prev = begin;int cut = begin + 1;int tmp = a[begin];while (cut <= end){while (cut <= end && a[cut] <= tmp){prev++;cut++;}while (cut <= end && a[cut] > tmp){cut++;}if (cut <= end){prev++;swap(a + prev, a + cut);}}swap(a + begin, a + prev);QuickSort2(a, begin, prev - 1);QuickSort2(a, prev + 1, end);
}

非递归实现

递归版本在内存的开销上有可能会造成栈溢出,此时改成非递归是常见的需求。该非递归的核心思路就是用栈模拟递归的过程,利用栈存储关键的信息。

非递归需要依赖于栈这个数据结构,这里也不在展开

栈:

#pragma once
#include <stdio.h>
#include <stdlib.h>// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{STDataType* _a;int _top;		// 栈顶int _capacity;  // 容量 
}Stack;// 初始化栈 
void StackInit(Stack* ps);
// 入栈 
void StackPush(Stack* ps, STDataType data);
// 出栈 
void StackPop(Stack* ps);
// 获取栈顶元素 
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数 
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps);
// 销毁栈 
void StackDestroy(Stack* ps);#define _CRT_SECURE_NO_WARNINGS 1
#include "Stack.h"void StackInit(Stack* ps)
{ps->_a = NULL;ps->_top = -1;ps->_capacity = 0;
}void StackPush(Stack* ps, STDataType data)
{if (ps->_capacity == 0) {ps->_a = (STDataType*)malloc(sizeof(STDataType) * 4);//默认初始化长度为4ps->_capacity = 4;ps->_top = 0;}else if (ps->_top == ps->_capacity){ps->_capacity *= 2;ps->_a = (STDataType*)realloc(ps->_a, sizeof(STDataType) * ps->_capacity);//默认扩容至原先的两倍}(ps->_a)[ps->_top++] = data;
}void StackPop(Stack* ps)
{if (ps->_top <= 0) return;ps->_top--;
}STDataType StackTop(Stack* ps) {if (ps->_top <= 0) return 0;return (ps->_a)[--ps->_top];
}int StackSize(Stack* ps)
{return ps->_top;
}int StackEmpty(Stack* ps)
{if (ps->_top <= 0) return 1;return 0;
}void StackDestroy(Stack* ps)
{free(ps->_a);ps->_a = NULL;ps = NULL;
}
void QuickSortNonR(int* a, int begin, int end);//非递归版本void QuickSortNonR(int* a, int begin, int end) 	//非递归版本
{Stack sk;StackInit(&sk);StackPush(&sk, end);StackPush(&sk, begin);while (!StackEmpty(&sk)){int left = StackTop(&sk);int right = StackTop(&sk);int rembegin = left;int remend = right;int tmp = a[left];while (left < right){while (left < right && a[right] >= tmp){right--;}while (left < right && a[left] <= tmp){left++;}swap(a + left, a + right);}swap(a + rembegin, a + left);if (left + 1 < remend) {StackPush(&sk, remend);StackPush(&sk, left + 1);}if (rembegin < left - 1) {StackPush(&sk, left - 1);StackPush(&sk, rembegin);}}StackDestroy(&sk);
}

优化

在此基础上可以对快速排序展开两个简单的优化

  • 小区间优化(当排序元素小于10可以转换为插入排序,减少递归的深度,减少开销)

将代码中注释的部分放开即可以实现小区间优化

  • 三数取中(避免出现向一边递归,算法退化为N^2的情况)
int QuZhong(int* arr, int first, int last)//优化,三数取中
{int z = (first + last) / 2;int a = arr[first];int b = arr[z];int c = arr[last];if (b > c){if (c > a)return last;else{if (a < b)return first;elsereturn z;}}else{if (b > a)return z;else{if (a < c)return first;elsereturn last;}}
}

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

归并排序

归并排序是一种典型的分治思想,将大问题拆分成不能再分割的小问题。思路在将待排序不断对半分割分割,分割到不能再分割时将相邻两个组合并为一个有序的大组,再将两个相邻的两个大组合并为一个有序的大大组……

这里依然是提供两个版本的实现,递归版和非递归版

这里非递归的实现,是直接将单个元素视为递归下来不能再分割的最小单位,实现往上回归的部分

//归并排序
void MergeSort(int* a, int begin, int end);//递归版本
void MergeSortNonR(int* a, int begin, int end);//非递归版本void MergeSort(int* a, int begin, int end)//归并排序
{int n = end - begin + 1;if (n < 2) return;int min = begin + n / 2;MergeSort(a, begin, min - 1);MergeSort(a, min, end);int* arr = (int*)malloc(sizeof(int) * n);int left = begin;int right = min;int i = 0;while (left < min && right <= end){if (a[left] < a[right]){arr[i++] = a[left++];}else{arr[i++] = a[right++];}}if (left == min) {while (right <= end) {arr[i++] = a[right++];}}else{while (left < min) {arr[i++] = a[left++];}}i = 0;for (int y = begin; y <= end; y++){a[y] = arr[i++];}free(arr);
}void MergeSortNonR(int* a, int begin, int end)//非递归版本
{int gap = 1;int n = end - begin + 1;int* arr = (int*)malloc(sizeof(int) * n);while (gap < n){for (int y = 0; y < n; y += gap * 2){int left = y;int right = y + gap;if (right >= n) {for (int z = left; z < n; z++){arr[z] = a[z];}break;}int i = y;int end1 = y + gap;int end2 = right + gap;if (end2 >= n)end2 = n;while (left < end1 && right < end2){if (a[left] < a[right]){arr[i++] = a[left++];}else{arr[i++] = a[right++];}}if (left == end1) {while (right < end2) {arr[i++] = a[right++];}}else{while (left < end1) {arr[i++] = a[left++];}}}for (int z = 0; z < n; z++){a[z] = arr[z];}gap *= 2;}free(arr);
}

1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(N)
4. 稳定性:稳定
 

计数排序

计数排序是一种非比较的排序,适用于对整数进行排序。思想是得到序列中的最大值和最小值,计算得到中间最多有多少个元素,遍历一遍序列,将元素映射到计数表中,再从计数表中依次读取出元素。

//计数排序
void CountSort(int* a, int n);void CountSort(int* a, int n)
{int min = a[0];int max = a[0];for (int i = 1; i < n; i++){if (a[i] < min)min = a[i];else if (a[i] > max)max = a[i];}int countN = max - min + 1;int* arr = (int*)calloc(countN, sizeof(int));for (int i = 0; i < n; i++){arr[a[i] - min]++;}int y = 0;for (int i = 0; i < countN&&y<n; i++){int sz = arr[i];while (sz--){a[y++] = i + min;}}
}

1. 计数排序在数据范围集中时,效率很高,但是适用范围及场景有限。
2. 时间复杂度:O(MAX(N,范围))
3. 空间复杂度:O(范围)
4. 稳定性:稳定

桶排序和基数排序

桶排序和基数排序不常用,这里只简单介绍思想不介绍具体实现

桶排序工作的原理是将数组分到有限数量的桶里。每个桶再分别排序,最后依次把各个桶中的记录列出来记得到有序序列

基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较

排序算法的比较

 

 

结语

排序这部分重点掌握不同算法的思想,把握一趟是如何实现的,其他每一趟都是一样的思想

以上便是今天的全部内容。如果有帮助到你,请给我一个免费的赞。

因为这对我很重要。

编程世界的小比特,希望与大家一起无限进步。

感谢阅读!

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

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

相关文章

Spring项目报错解读与全部报错详解

你好,我是Qiuner. 为帮助别人少走弯路和记录自己编程学习过程而写博客 这是我的 github https://github.com/Qiuner ⭐️ ​ gitee https://gitee.com/Qiuner &#x1f339; 如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的 &#x1f604; (^ ~ ^) 想看更多 那就点个关注吧 我…

图像大模型中的注意力和因果掩码

AIM — 图像领域中 LLM 的对应物。尽管 iGPT 已经存在 2 年多了&#xff0c;但自回归尚未得到充分探索。在本文中&#xff0c;作者表明&#xff0c;当使用 AIM 对网络进行预训练时&#xff0c;一组图像数据集上的下游任务的平均准确率会随着数据和参数的增加而线性增加。 要运…

uniApp获取实时定位

通过你获取的key放到项目manifest.json里面&#xff0c;对应填写你所需要的key值&#xff0c;还有高德用户名 用户名&#xff1a; key值的位置&#xff1a; 代码&#xff1a; html: <view class"intList pdNone"><view class"label">详细地…

爬虫:爬取知乎热榜一级评论及回答2024不包含翻页

一、先上结果&#xff08;注:本文仅为兴趣爱好探究&#xff0c;请勿进行商业利用或非法研究&#xff0c;负责后果自负&#xff0c;与作者无关&#xff09; 1、爬标题及其具体内容 2、抓标题下的对应回答 3、爬取对应一级评论 二、上流程 1、获取cookies&#xff08;相信哥哥姐姐…

静心冥想训练入门|流静

在喧嚣的都市中&#xff0c;我们时常被琐事所困&#xff0c;心灵难以得到片刻的宁静。然而&#xff0c;静心冥想训练如同一扇通往内心宁静的门户&#xff0c;引领我们踏上一段静谧的旅程。 静心冥想&#xff0c;并非遥不可及的高深技艺&#xff0c;而是每个人都能掌握的心灵修炼…

优思学院|「按计划推动型」与「需求拉动型」的生产模式

针对生产架构做对比分类的用语&#xff0c;主要有按计划推进型与需求拉动型。 「按计划推动型」与「需求拉动型」两者乃是生产架构上常使用、成对比的两个用语。不过&#xff0c;有时不只用来指单纯的生产现场架构&#xff0c;也有人把它应用在更广泛的生产架构设计上。 按计划…

【yolov8系列】ubuntu上yolov8的开启训练的简单记录

前言 yolov8的广泛使用&#xff0c;拉取yolov8源码工程&#xff0c;然后配置环境后直接运行&#xff0c;初步验证自己数据的检测效果&#xff0c;在数据集准备OK的情况下 需要信手拈来&#xff0c;以保证开发过程的高效进行。 本篇博客更注意为了方便自己使用时参考。顺便也记录…

每日一题——力扣144. 二叉树的前序遍历(举一反三+思想解读+逐步优化)五千字好文

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 我的写法&#xff1a; 代码结构 时间复杂度 空间复杂度 总结 我要更强 代码说明 …

C语言力扣刷题7——删除排序链表中的重复元素 II——[快慢双指针法]

力扣刷题7——删除排序链表中的重复元素 II——[快慢双指针法] 一、博客声明二、题目描述三、解题思路1、思路说明 四、解题代码&#xff08;附注释&#xff09; 一、博客声明 找工作逃不过刷题&#xff0c;为了更好的督促自己学习以及理解力扣大佬们的解题思路&#xff0c;开辟…

好书安利 | LangChain入门指南:构建高可复用、可扩展的LLM应用程序(送PDF)轻松入门LangChain

《LangChain入门指南》 LangChain作为大模型集成框架鼎鼎大名&#xff0c;这本《LangChain入门指南》是一本很及时的书&#xff0c;值得推荐&#xff5e; 01 为什么需要LangChain 首先想象一个开发者在构建一个LLM应用时的常见场景。 当你开始构建一个新项目时&#xff0c;…

不使用canvs也能创建出点状背景

div{ height: 100%; touch-action: none; background: radial-gradient(circle, #e6e6e6 1px, transparent 1px); /* 创建一个点状背景 */ background-size: 15px 15px; /* 控制点的大小和间距 */ padding: 20px; /* 添加内边距使内容不靠边 */ position: relative; /* 让内部内…

树形DP——AcWing 323. 战略游戏

树形DP 定义 树形动态规划&#xff08;Tree Dynamic Programming&#xff0c;简称树形DP&#xff09;是一种在树形结构上应用动态规划算法的技术。它利用树的递归结构&#xff0c;通过定义状态和状态转移方程&#xff0c;来求解与树相关的最优化问题&#xff0c;如树上的最长…

不使用cmake,如何在vs2019对cpp项目进行文件夹分类?

不使用cmake&#xff0c;如何在vs2019对cpp项目进行文件夹分类&#xff1f; 1.不使用cmake的根目录指的是哪里&#xff1f;2.什么时候进行项目管理&#xff1f;3.应该分成什么样的文件夹&#xff1f;4.如何分类&#xff1f; 1.不使用cmake的根目录指的是哪里&#xff1f; 查看项…

最新AI智能聊天对话问答系统源码(图文搭建部署教程)+AI绘画,文生图,TTS语音识别输入,文档分析

一、人工智能语言模型和AI绘画在多个领域广泛应用 人工智能语言模型和AI绘画在多个领域都有广泛的应用。以下是一些它们的主要用处&#xff1a; 人工智能语言模型 内容生成 写作辅助&#xff1a;帮助撰写文章、博客、报告、剧本等。 代码生成&#xff1a;自动生成或补全代码&…

sudo: /etc/init.d/ssh: command not found

在 WSL 中尝试启动 SSH 服务时遇到 sudo: /etc/init.d/ssh: command not found 错误 安装 OpenSSH 服务器 更新软件包列表 sudo apt update安装 OpenSSH 服务器 sudo apt install openssh-server启动 SSH 服务 在 WSL 2 上,服务管理与传统 Linux 系统有所不同。你可以手动启动…

C++之STL(十)

1、适配器 2、函数适配器 #include <iostream> using namespace std;#include <algorithm> #include <vector> #include <functional>bool isOdd(int n) {return n % 2 1; } int main() {int a[] {1, 2, 3, 4, 5};vector <int> v(a, a 5);cou…

ONLYOFFICE 8.1版本桌面编辑器测评:重塑办公效率的巅峰之作

在数字化办公日益普及的今天&#xff0c;一款高效、便捷且功能强大的桌面编辑器成为了职场人士不可或缺的工具。ONLYOFFICE 8.1版本桌面编辑器凭借其卓越的性能和丰富的功能&#xff0c;成功吸引了众多用户的目光。今天&#xff0c;我们将对ONLYOFFICE 8.1版本桌面编辑器进行全…

使用el-amap-info-window遇到的问题

使用的这个库https://github.com/yangyanggu/vue-amap 想要滚动amapInfoWindow里的内容&#xff0c;但不触发地图缩放 默认滚动amapInfoWindow里的内容&#xff0c;会触发地图缩放。看了C站一个大佬的文章解决了。 amapInfoWindow会自动滚动到顶部 我的amapInfoWindow里面用了…

【智能制造-4】机器人控制器

机器人控制器中分哪几个模块&#xff1f; 机器人控制器通常由以下几个主要模块组成: 运动控制模块: 负责机器人各轴电机的位置、速度、加速度等控制 实现机器人末端执行器的精确定位和运动控制传感器接口模块: 负责机器人各种传感器信号的采集和处理 为运动控制、环境感知等提…

Python-正则表达式

目录 一、打开正则表达式 二、正则表达式的使用 1、限定符 &#xff08;1&#xff09;x*&#xff1a;*表示它前面的字符y 可以有0个或多个&#xff1b; &#xff08;2&#xff09;x&#xff1a;表示它前面的字符可以出现一次以上&#xff1b;&#xff08;只可以匹配多次&…