幽映每白日,清辉照衣裳。这篇文章主要讲述常见的基于比较的排序算法相关的知识,希望能为你提供帮助。
写在前面我们生活中是离不开排序的,比如一场考试下来,我们需要根据成绩进行排名,有例如去银行取钱,我们需要取号,然后等待.这些都是排序在我们生活中的应用.数据结构的初阶部分快分享完了,今天把排序的分享完后,后面还有一个哈希表。希望大家一直坚持。今天我们说的是基于比较的常见的排序算法。在我们开始正式内容之前我想说一下,排序最重要的是思想,不是代码。我们可以把代码背下来,可是要是不理解原理,照样还是不懂,下次遇到排序的时候,你的脑子可能会欺骗你.
排序关于什么是排序我们就不用说了吧,简单的理解就是对数字排成升序或者降序。关于排序的实际应用,我们现实生活中有很多例子。例如当我们用手机购物时,可能会选择销量优先或者是价格优先,这些都是排序的实际应用。排序的算法有很多,这里我就谈谈几种常用的。
排序优劣主要从三个方面来分析
- 时间复杂度
- 空间复杂度
- 稳定性
这里也有个可以简便的判断排序是不是稳定的方法,要是我们跳着数字交换,就一定是不稳定的
文章图片
排序的分类排序的算法五花八门,我们只需要见识一下最常见的排序算法就可以了.我个人认为排序分为比较排序和不比较排序,有人可能会疑惑还有不比较就可以排序的吗?开什么玩笑!实际上是存在的,下面的基数排序就是其中的一中。至于我们今天所谈的都是基于比较的排序算法。
- 基于比较的排序
- 不基于比较的排序
文章图片
常见的基于比较的排序我们要谈的算法有7种,下面我先给出名字和种类,后面会通过代码一一实现。我每一个都会附加一个动图,不用担心自己会模糊。
文章图片
直接插入排序前面的几种排序都很简单,所谓的插入排序就是我们选定一个数字,判断这个数字前面的数字是不是比这个数字大,要是大,大的数字往后移动一个单位。把待排序的记录按其关键码值的大小逐个插入到一个已经排好了的有序序列中,当我们完成所有的记录插入完为止,得到一个新的有序序列 。这就是插入排序。
我们要做的就是从有序区间找到一个比待排序数字(没有更好)大的数字,然后对他们依次进行排序,直到无序区间变得没有
文章图片
代码我们分为单趟排序和完整代码来写,直到我们可以写出正确的
int j = i - 1;
int ret = arr[i];
while (j >
= 0)if (arr[j] >
ret)arr[j + 1] = arr[j];
elsebreak;
j--;
//跳出的两种情况都适合
arr[j + 1] = ret;
//代码一
void interSort(int* arr,int len)assert(arr);
for (int i = 1;
i <
len;
i++)int j = i - 1;
int ret = arr[i];
while (j >
= 0)if (arr[j] >
ret)arr[j + 1] = arr[j];
elsebreak;
j--;
arr[j + 1] = ret;
//代码 二
void interSort(int* arr, int len)assert(arr);
for (int i = 0;
i <
len - 1;
i++)int end = i;
;
int ret = arr[end + 1];
while (end >
= 0)if (arr[end] >
ret)arr[end + 1] = arr[end];
elsebreak;
end--;
arr[end + 1] = ret;
性能分析我们来分析一下直接插入排序的性能.
- 最好的情况:数组一开始就是有序(和我们要的有序一样)的,我们就是遍历了一遍数组 时间复杂度O(N)
- 最坏的情况:数组是逆序的,我们每个到一个ret,都要进行遍历有序区间O = 0 + 1+ 2+...+ len-1 ; 时间复杂度O(N==^2^==)
我们要是判断arr[end] > ret加上等号 就是不稳定的,不加就是稳定的,所以直接插入排序是一个稳定的排序(稳定的排序可以实现为不稳定,反之则不能).
总结
代码优化一般了解基础的就可以了,不过有时我们就是害怕面试官让你当场优化这个算法,我这里也谈谈吧.我这里给一个折半插入的优化方案
折半插入
折半插入就是在有序区间内寻找比ret大的数字,和折半查找差不多.只要找到比ret大的有序区间,就要可以进行插入排序了
文章图片
int halfInsert(int* arr, int keyi)// 返回 的是最后一个不比ret 大的下标
assert(arr);
int left = 0;
int right = keyi-1;
int ret = arr[keyi];
while (left <
= right)int mid = (left + right) >
>
1;
if (arr[mid] <
= ret)left = mid + 1;
if (arr[left] >
ret)return mid;
elseright = mid - 1;
return right;
void halfInsertSort(int* arr, int len)assert(arr);
for (int i = 1;
i <
len;
i++)int ret = arr[i];
int left = halfInsert(arr, i) +1;
int right = i - 1;
while (left <
= right)arr[right + 1] = arr[right];
right--;
arr[left] = ret;
#define CAP 100000
int main()srand((unsigned)time(NULL));
int arr1[CAP] =0 ;
int arr2[CAP] =0 ;
for (int i = 0;
i <
CAP;
i++)int n = rand() % CAP + 1;
arr1[i] = n;
arr2[i] = n;
int begin1 = clock();
interSort(arr1, CAP);
int end1 = clock();
printf("直接插入排序:%d \\n", end1 - begin1);
int begin2 = clock();
halfInsertSort(arr2, CAP);
int end2 = clock();
printf("优化 :%d \\n", end2 - begin2);
system("pause");
return 0;
文章图片
希尔排序希尔排序法又称缩小增量法。前面我们说了直接插入排序,好的时候性能可以达到O(N),不好的时候时间复杂度就是O(N==^2^==),这也太不稳定了吧,即使是上面的折半插入也是治标不治本,我们希望有一个更好的排序算法.一个叫做希尔的大佬提出了一种解决方法.又可以进一步优化这个算法.
希尔排序既然插入排序在接近有序的时候时间复杂的接近O(N),我们是不是可以把这个数组进行分组,每组先进行插入排序,最后在进行整体的插入排序.这不是一个很好的方法吗.是的,我们这个想法就跟希尔大佬是一样的.
如何分组
我知道,我知道,不要拦我,让我说,不就是分组吗,很简单,给我一堆数字,说吧,分成几组,我都给你分出来.
文章图片
然而希尔大佬的思想不是我们这些普通人可以想到的,这才是真正的厉害,通过这种分法,大的数字可以尽快的跑到后面,小的数字可以跑到前面,
文章图片
代码
既然我们已经知道如何分组了,下面就可以写出单趟排序的代码了。当gap等于 1时,和我们插入排序是一样的
for (int i = gap;
i <
len;
i += gap)int ret = arr[i];
int j = i - gap;
while (j >
= 0)if (arr[j] >
ret)arr[j + gap] = arr[j];
elsebreak;
j -= gap;
arr[j + gap] = ret;
// 代码 一
void shell(int* arr, int len, int gap)assert(arr);
for (int k = 0;
k <
gap;
k++)for (int i = gap + k;
i <
len;
i += gap)int ret = arr[i];
int j = i - gap;
while (j >
= 0)if (arr[j] >
ret)arr[j + gap] = arr[j];
elsebreak;
j -= gap;
arr[j + gap] = ret;
void shellSort(int* arr, int len)assert(arr);
int gap = len;
while (gap >
1)gap = gap / 3 + 1;
shell(arr, len, gap);
//代码二
//这个也算是一个优化把,不过只是形式变化了,性能没有提高
void shell(int* arr, int len, int gap)assert(arr);
for (int i = gap ;
i <
len;
i ++)int ret = arr[i];
int j = i - gap;
while (j >
= 0)if (arr[j] >
ret)arr[j + gap] = arr[j];
elsebreak;
j -= gap;
arr[j + gap] = ret;
void shellSort(int* arr, int len)assert(arr);
int gap = len;
while (gap >
1)gap = gap / 3 + 1;
shell(arr, len, gap);
性能分析空间复杂度是O(1)
希尔排序的时间复杂度很难计算,我们也不知道gap等于多少的时候代码的性能最高,gap为多少在数学上也是一个未被解决的问题.当gap很大时,数值较大的数据往后移动的很快,当gap小的时候,数据移动数量有些多,这很矛盾,所以我们可以大概的计算到当gap = len/3的时候,速度可能快点,希尔排序的时间复杂度是O(N==^1.3^==) ~ O(N==^1.5^==).
希尔排序是一个不稳定的排序,数据发生了跨格移动
选择排序选择排序是我们所有排序当中最挫的,它的思想也很简单,从数组中拿出一个数,和它之后的数据进行比较,要是比他小,就交换,直到比较时遍历整个数组.
文章图片
- 在元素集合array[i]--array[n-1]中选择关键码最大(小)的数据元素
- 若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换
- 在剩余的array[i]--array[n-2](array[i+1]--array[n-1])集合中,重复上述步骤,直到集合剩余1个元素
for (int j = i + 1;
j <
len;
j++)if (arr[j] <
arr[i])int ret = arr[j];
arr[j] = arr[i];
arr[i] = ret;
void selectSort(int* arr, int len)for (int i = 0;
i <
len-1;
i++)for (int j = i + 1;
j <
len;
j++)if (arr[j] <
arr[i])int ret = arr[j];
arr[j] = arr[i];
arr[i] = ret;
性能分析
- 时间复杂度最好最坏都是O(N^2^)
- 空间复杂度O(1)
- 稳定性不稳定
void selectSort(int* arr, int len)int left = 0;
int right = len - 1;
while (left <
right)int mini = left;
int maxi = left;
for (int i = left;
i <
= right;
i++)if (arr[i] <
arr[mini])mini = i;
if (arr[i] >
arr[maxi])maxi = i;
swap(&
arr[left], &
arr[mini]);
//防止被掉包
if (left == maxi)maxi = mini;
swap(&
arr[right], &
arr[maxi]);
left++;
right--;
#define CAP 10000
int main()srand((unsigned)time(NULL));
int* arr1 = (int*)malloc(sizeof(4) * CAP);
int* arr2 = (int*)malloc(sizeof(4) * CAP);
assert(arr1 &
&
arr2);
for (int i = 0;
i <
CAP;
i++)int n = rand()% CAP + 1;
arr1[i] = n;
arr2[i] = n;
int begin1 = clock();
selectSort1(arr1, CAP);
int end1 = clock();
int begin2 = clock();
selectSort(arr2, CAP);
int end2 = clock();
printf("没有优化 %d\\n", end1 - begin1);
printf("优化%d\\n", end2 - begin2);
free(arr1);
free(arr2);
system("pause");
return 0;
文章图片
堆排序我们说一说堆排序,给定一个数组,我们如何把它排成一个升序。之前我们可以借助冒泡排序,今天谈一个新的排序方式,堆排序。在说这个之前,我们先看看堆的一些性质
- 小堆的堆定 是所有元素中最小的
- 大堆的堆定 是所有元素中最大的
使用向上调整对数组进行建堆
文章图片
我来解释解释下面的代码
i 可以从 0 开始,但是对于一个元素建堆,他本来就可以是一个堆,所以不用在建了,
为何是 i+1,这个看个人对向上调整函数参数的定义,我的是传入的元素的个数,所以要i+1,比如当i = 1时,对前两个元素进行建堆
void HeapSort(int* arr, int len)assert(arr);
for (int i = 1;
i <
len;
i++)adjustUp(arr, i+1);
完整的代码
void HeapSort(int* arr, int len)assert(arr);
//建堆
for (int i = 1;
i <
len;
i++)adjustUp(arr, i+1);
//堆排序
for (int i = len;
i >
0;
)//交换
int ret = arr[0];
arr[0] = arr[i - 1];
arr[i - 1] = ret;
i--;
adjustDown(arr, i, 0);
使用向下调整对数组进行建堆
文章图片
前面使用的向上调整建堆,这里使用向下调整,
void HeapSort(int* arr,int len)assert(arr);
for (int parent = (len - 1 - 1) / 2;
parent >
= 0;
parent--)//叶子不需要 调
adjustDown(arr, len, parent);
void HeapSort(int* arr,int len)assert(arr);
for (int parent = (len - 1 - 1) / 2;
parent >
= 0;
parent--)//叶子不需要 调
adjustDown(arr, len, parent);
//堆排序
for (int i = len;
i >
0;
)//交换
int ret = arr[0];
arr[0] = arr[i - 1];
arr[i - 1] = ret;
i--;
adjustDown(arr, i, 0);
性能分析
- 空间复杂度O(1)
- 时间复杂度O(NlogN)
- 稳定性不稳定
void bubbleSort(int* arr, int len)assert(arr);
for (int i = 0;
i <
len - 1;
i++)int flag = 0;
for (int j = 0;
j <
len - 1 - i;
j++)if (arr[j] >
arr[j + 1])int ret = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = ret;
flag = 1;
if (0 == flag)break;
性能分析
- 空间复杂度O(1)
- 时间复杂度O(N==^2^==)
- 稳定性稳定,这一点是要注意的.
文章图片
但是这种优化的效果也是不太好的,不过也算是一种优化,书上还有一个更好的优化,鸡尾酒排序,它是基于冒泡的一种升级版,这里篇幅有限,我们就不具体说了。
void bubbleSortIndex(int* arr, int len)assert(arr);
int sortBoundary = len - 1;
for (int i = 0;
i <
len - 1;
i++)int flag = 0;
int lastExchangeIndex = 0;
for (int j = 0;
j <
sortBoundary;
j++)if (arr[j] >
arr[j + 1])int ret = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = ret;
flag = 1;
lastExchangeIndex = j;
sortBoundary = lastExchangeIndex;
if (0 == flag)break;
文章图片
快速排序总算谈到快排了,我们在C语言中就是用过qsort,它就是快排.既然敢称快速排序,那么一定有自己额优点.
单趟排序在谈快排之前,我们必须要理解如何进行一趟排序,这是太重要了。
hoare版本
快排的提出人hoare给了这么一个单趟排序思想
我们在数组的左边或者是右边找出一个key值,遍历整个数组,是的key的左边都不必key大,右边都不必key小.这里需要解决一个问题
它们碰在一起有两种情况
- left碰到rightright本来就是停到比key小的的地方
- right碰到leftleft虽然是停到bikey大的地方,但是前一轮已经被替换了.所以还是比key小
文章图片
//选左做 keyi
int PartSort1(int* arr, int left, int right)assert(arr);
int keyi = left;
while (left <
right)while (arr[right] >
arr[keyi])right--;
while (left <
right &
&
arr[left] <
= arr[keyi])left++;
swap(&
arr[left], &
arr[right]);
swap(&
arr[keyi], &
arr[left]);
return left;
//选右做 keyiint PartSort1(int* arr, int left, int right)assert(arr);
int keyi = right;
while (left <
right)while (left <
right &
&
arr[left] <
= arr[keyi])left++;
while (left <
right &
&
arr[right] >
= arr[keyi])right--;
swap(&
arr[left], &
arr[right]);
swap(&
arr[left], &
arr[keyi]);
return left;
挖坑法
前面的方法有点不太好理解,这里有人优化了一下,虽然没有提高效率.但是我们更容易理解.
文章图片
//选左做 坑
int PartSort2(int* arr, int left, int right)assert(arr);
int ret = arr[left];
int piti = left;
while (left <
right)while (arr[right] >
ret)right--;
arr[piti] = arr[right];
piti = right;
while (left <
right &
&
arr[left] <
= ret)left++;
arr[piti] = arr[left];
piti = left;
arr[left] = ret;
return left;
//选右做 坑int PartSort2(int* arr, int left, int right)assert(arr);
int piti = right;
int ret = arr[right];
while (left <
right)while (left <
right &
&
arr[left] <
= ret)left++;
arr[piti] = arr[left];
piti = left;
while (left <
right &
&
arr[right] >
= ret)right--;
arr[piti] = arr[right];
piti = right;
arr[piti] = ret;
return piti;
前后指针法
最近又有一种方法,我们来看看吧.使用前后指针的方法可以更加简洁一些.
文章图片
//选左做 keyi
int PartSort3(int* arr, int left, int right)assert(arr);
int prev = left;
int keyi = left;
for (int cur = left + 1;
cur <
= right;
cur++)if (arr[cur] <
arr[keyi] &
&
arr[cur] != arr[++prev])swap(&
arr[prev], &
arr[cur]);
swap(&
arr[keyi], &
arr[prev]);
return prev;
////选右做 keyiint PartSort3(int* arr, int left, int right)assert(arr);
int keyi = right;
int prev = left -1;
for (int cur = left;
cur <
right;
cur++)if (arr[cur] >
arr[keyi] &
&
arr[++prev] != arr[cur])swap(&
arr[prev], &
arr[cur]);
prev++;
swap(&
arr[keyi], &
arr[prev]);
return prev;
快速排序上面的三种方法都是单趟排序,得到可以key的左侧和右侧都是很特殊,接着我们通过递归一步步来排成有序,这里我要提一下,使用这种不优化的快排很垃圾,1ow个数,就会导致栈溢出。
文章图片
void QuickSort(int* arr, int len)assert(arr);
Quick(arr, 0, len-1);
void Quick(int* arr, int left, int right)assert(arr);
//递归结束的条件
if (left >
= right)return;
int pos = PartSort3(arr, left, right);
Quick(arr, left, pos - 1);
Quick(arr, pos + 1, right);
性能分析最好NlogN
最坏 N==^2^==
不稳定
优化快速排序我们会疑惑,上面最好的时间复杂度最好才是NlogN,还好意思称快速排序,好意思吗?但是我们库里面给的是快排,不是希尔排序,这又是什么原因呢?事实上,我们可以优化快速排序,让他的性能大大提高,标准库里卖弄也是优化过的.
选key值优化
我们前面选择key的都是左边的.这就给有序的数据快排有一定的困难.我们主要就选l来优化
- 随机选key适合运气呱呱的来
- 三数取中
int getMid(int* arr, int left, int right)assert(arr);
int mid = (left + right) >
>
1;
int i = left;
if (arr[left] <
arr[mid])if (arr[mid] <
arr[right])return mid;
elseif (arr[left] <
arr[right])return left;
elsereturn right;
//3 1 2
elseif (arr[mid] >
arr[right])return mid;
elseif (arr[left] <
arr[right])return left;
elsereturn right;
小区间优化
在较小的的区间内,我们可以直接选择插入排序,避免往下再次递归了.
if (right - left + 1 <
13)InterSort(arr + left, right - left + 1);
return;
非递归实现快速排序我们前面使用递归的方法实现了快速排序,这里通过非递归来实现。这样面试的时候用哪个都可以,这里我就不写关于堆的源码了,之前和大家分享过。
void QuickSort(int* arr, int len)QuickSortNonR(arr, 0, len - 1);
void QuickSortNonR(int* arr, int left, int right)assert(arr);
Stack stack;
InitStack(&
stack);
int pivot = PartSort2(arr, left, right);
if (pivot >
left + 1)push(&
stack, left);
push(&
stack, pivot - 1);
if(pivot <
right-1)push(&
stack, pivot + 1);
push(&
stack, right);
while (!IsEmpty(&
stack))right = pop(&
stack);
left = pop(&
stack);
pivot = PartSort2(arr, left, right);
if (pivot >
left + 1)push(&
stack, left);
push(&
stack, pivot - 1);
if (pivot <
right - 1)push(&
stack, pivot + 1);
push(&
stack, right);
归并排序归并排序也是一个很好的排序算法.归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并.
文章图片
排序两个有序数组在说这个之前,我们需要对两个有序数组进行排序,这位后面打下基础.
文章图片
int* sortTwoArray(int* arr1, int len1, int* arr2, int len2, int* returnSize)assert(arr1 &
&
arr2);
*returnSize = len1 + len2;
int* array = (int*)malloc(sizeof(int) * (len1 + len2));
assert(array);
int s1 = 0;
int e1 = len1-1;
int s2 = 0;
int e2 = len2-1;
int i = 0;
while (s1 <
= e1 &
&
s2 <
= e2)if (arr1[s1] <
= arr2[s2])array[i++] = arr1[s1++];
elsearray[i++] = arr2[s2++];
while (s1 <
= e1)array[i++] = arr1[s1++];
while (s2 <
= e2)array[i++] = arr2[s2++];
return array;
int main()int arr1[] =1,3;
int sz1 = sizeof(arr1) / sizeof(arr1[0]);
int arr2[] =2,4,6,8,10;
int sz2 = sizeof(arr2) / sizeof(arr2[0]);
int count = 0;
int* array = sortTwoArray(arr1, sz1, arr2, sz2,&
count);
for (int i = 0;
i <
count;
i++)printf("%d ", array[i]);
return 0;
文章图片
递归实现归并排序我们先用递归的方法实现一下,递归的思想就是分治,它额和我们的二叉树的后续遍历很相似,我们再递归的末尾进行两个有序数组的排序,借助一个额外的数组记录排序的结果.最后每次把排序的结果再次给到原数组.
文章图片
void merge(int* arr, int left, int right,int* ret)if (left >
= right)return;
int mid = (left + right) >
>
1;
merge(arr, left, mid,ret);
merge(arr, mid+1, right,ret);
int i = left;
int s1 = left;
int e1 = mid;
int s2 = mid + 1;
int e2 = right;
while (s1 <
= e1 &
&
s2 <
= e2)if (arr[s1] <
= arr[s2])ret[i++] = arr[s1++];
elseret[i++] = arr[s2++];
while (s1 <
= e1)ret[i++] = arr[s1++];
while (s2 <
= e2)ret[i++] = arr[s2++];
memcpy(arr+left, ret+left, sizeof(int) * (right - left + 1));
void mergeSort(int* arr, int len)assert(arr);
int* ret = (int*)malloc(sizeof(int) * len);
assert(ret);
merge(arr, 0, len - 1,ret);
free(ret);
性能分析
- 时间复杂的 NlogN
- 空间复杂度 O(N)
- 稳定性稳定
文章图片
void mergeSortNoR(int* arr, int len)assert(arr);
int* ret = (int*)malloc(sizeof(int)*len);
assert(ret);
int gap = 1;
while (gap <
len)for (int i = 0;
i <
len;
i+=2*gap)int left = i;
int mid = left + gap - 1;
//防止数组越界
if (mid >
= len)mid = len - 1;
int right = mid + gap;
//防止数组越界
if (right >
= len)right = len - 1;
int j = left;
int s1 = left;
int e1 = mid;
int s2 = mid+1;
int e2 = right;
while (s1 <
= e1 &
&
s2 <
= e2)if (arr[s1] <
= arr[s2])ret[j++] = arr[s1++];
elseret[j++] = arr[s2++];
while (s1 <
= e1)ret[j++] = arr[s1++];
while (s2 <
= e2)ret[j++] = arr[s2++];
//最后再把元素拷贝会原数组
memcpy(arr, ret , sizeof(int) * len);
gap *= 2;
free(ret);
int main()int arr[] =3,3,4,5,6,7,3,3,3 ;
int sz = sizeof(arr) / sizeof(arr[0]);
mergeSortNoR(arr, sz);
for (int i = 0;
i <
sz;
i++)printf("%d ", arr[i]);
return 0;
文章图片
海量数据的排序问题在工作中,我们经常会遇到这种问题,我们要排序100G的数据,但是我们的内存只有1G,这怎么办.因为内存中因为无法把所有数据全部放下,所以需要外部排序,而归并排序是最常用的外部排序 .
- 先把文件切分成 200 份,每个 512 M
- 分别对 512 M 排序,因为内存已经可以放的下,所以任意排序方式都可以
- 进行 200 路归并,同时对 200 份有序文件做归并过程,最终结果就有序了
排序 | 时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|
插入排序 | 最好 O(N)最坏 O(N^2^) | O(1) | 稳定 |
希尔排序 | O(N^1.3^~O(N^1.5^)) | O(1) | 不稳定 |
选择排序 | O(N^2^) | O(1) | 不稳定 |
堆排序 | O(NlogN) | O(N) | 不稳定 |
冒泡排序 | O(N^2^) | O(1) | 稳定 |
快速排序 | 最好O(NlogN)最坏 O(N^2^) | 最好 O(logN)最坏 O(N) | 不稳定 |
归并排序 | O(n * log(n)) | O(N) | 稳定 |
文章图片
推荐阅读
- 简单了解 TiDB 架构
- Redis主从故障模拟及恢复
- yum管理工具
- 自制yum仓库
- ceph基于VMware Workstation虚拟机Ceph集群安装配置笔记#私藏项目实操分享#
- 如何使用maven把项目及其依赖打包为可运行jar包
- ROS 2.0-SPRINGER-机器人学工具科研和教学重要参考书-机器人操作系统(ROS)-THE COMPLETE REFERENCE
- Prometheus监控K8S各项指标
- linux之service命令