【数据结构】排序合集(万字详解)

文章目录

  • 前言
    • 插入排序
    • 希尔排序
    • 选择排序
    • 堆排序
    • 快速排序
      • hoare原生版本
      • 挖坑法
      • 前后指针法
      • 三数取中优化
      • 随机数取key优化
      • 三路划分版
      • 非递归
    • 归并排序
      • 递归
      • 非递归
        • 调整边界
        • 单次归并单次拷贝
  • 总结

前言

排序,以字面意思来说就是通过特定的算法将一组或多组无序或者接近有序的数据,以升序或者降序的方式重新进行排序组合;

[7,4,2,9,8,6,5,1,3];

以升序的方式进行排序最终为:

[1,2,3,4,5,6,7,8,9];

排序算法就是如何使得数据按照要求排列的方法;
排序的算法多种多样,基本的排序算法共有八种,分别为:

冒泡排序选择排序插入排序希尔排序归并排序快速排序堆排序计数排序

插入排序

插入排序作为排序中较为简单和经典的排序,插入排序的思维可以看作为一个打牌中摸牌顺牌的阶段一样;
假设手里的牌都是顺序的,摸下一张牌与之前的牌一张一张进行比较,设这张牌为n,将n与之前顺序的牌进行比较,当遇到比它小的牌的时候即将该牌放到此处;

在这里插入图片描述

设[0,end]区间为有序,cur为end后的下一个数据,将该数据进行保存,若是遇到比cur大的数据则挪动数据,将cur放至比cur小的数据之前,若是cur比end位置大则不需要进行上步操作,若是cur比[0,end]区间内的任意数据都小,那就挪动数据,将cur放置0处,同时++end;

在这里插入图片描述

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 (a[end] > tmp) {a[end + 1] = a[end];end--;}else break;}a[end+1] = tmp;}
}

时间复杂度:
插入排序的时间复杂度有两种情况:

  • 当数据为有序的情况下,只需要遍历n-1次,为最优的情况,时间复杂度为O(N);
  • 当数据为逆序的情况时,为最坏的情况,最需要遍历1+2+3+4+…+N-1次,时间复杂度为O(N2);

空间复杂度:

  • 插入排序的空间复杂度为常数阶O(1);

稳定性

  • 稳定

希尔排序

希尔排序是在以插入排序为前提下进行的一种优化的排序,由于插入排序不太擅长排序逆序的情况,所以一位叫做Donald L. Shell的大佬对该排序进行了改进;
希尔排序又被称为" 缩小增量排序 ",即在插入排序之前进行预排序;

设gap,将间隔为gap的数据分为一组,共分为gap组;
gap没有具体的规范,但是一般而言gap可以跟数据量有关系;
将每组数据利用插入排序的方法进行一次预排序,当预排序结束时,数据并没有完全有序,但是较为接近有序;

在这里插入图片描述
当然,根据数据量的变化,预排序可以进行不止一次;
再预排序过后即可进行插入排序;

void ShellSort(int* a, int n)
{	/** gap根据数据个数进行变化* 故初始值赋值数据个数*/int gap = n;while(gap>1){/** 控制gap使其不停进行变化* 当gap为1时则为最后一次排序,也是直接插入排序*/gap = gap / 3 + 1;for (int i = 0; i < n - gap; i++) {//n-gap防止越界/** 直接插入排序的思路*/int end = i;int tmp = a[end + gap];while (end >= 0) {if (a[end] > tmp) {a[end + gap] = a[end];end-=gap;}else break;}a[end + gap] = tmp;}}
}

时间复杂度

  • 希尔排序的时间复杂度为O(N3/2),相对于直接插入排序的最坏情况O(N2)来说要快一些;

空间复杂度

  • 希尔排序的空间复杂度也为O(1);

稳定性

  • 不稳定

选择排序

选择排序的思路为双指针:
设begin为0,cur为begin+1,cur遍历一遍数据,找到最小或者最大的那个数据并将该位置保存,最后与begin所在的数据进行交换;
交换过后begin++;

在这里插入图片描述
为了使排序不那么繁琐可以使用begin与end两个指针记录头尾,cur指针从前往后分别找到最小和最大的数据并保存位置,最小的数据与begin交换,最大的数据与end进行交换,交换过后++begin与–end从而控制区间;

void SelectSort(int*a ,int n)
{int begin = 0, end = n - 1;//[begin,end]左闭右闭区间while (begin < end) {int min = begin, max = begin;for (int i = begin + 1; i <= end; i++) {if (a[i] < a[min]) {min = i;}if (a[i] > a[max]) {max = i;}}swap(&a[begin], &a[min]);if (begin == max) {max = min;}swap(&a[max], &a[end]);++begin, --end;}
}

时间复杂度

  • 当数据为完全顺序的情况下,不需要进行交换,则时间复杂度为O(N);
  • 若是数据为逆序的情况下则为最坏的情况,交换次数即为O(N-1)次;

空间复杂度

  • 选择排序的空间复杂度为常数阶O(1);

稳定性

  • 不稳定

堆排序

堆排序是基于堆而做的一种排序,同时堆排序也算是选择排序中的一种排序;
既然是基于堆的排序,首先数据必须为一个堆(升序建大堆,降序建小堆),而所给的每组数据中是堆的可能性并不大;
所以再进行排序之前应该先进行建堆,建堆的同时分为向上调整建堆与向下调整建堆;

堆排的思路为将头尾数据进行交换再进行向下调整,故向下调整是必须具有的一个,同时基于向下调整建堆的时间复杂度(O(N))优于向上调整建堆(O(N*logN)),故在这里只需要写一个向下调整算法就能实现堆排序;

在这里插入图片描述
在这里插入图片描述

void AdjustDown(int*a,int parent,int n)//parent==0 n == 6
{int child = parent * 2 + 1;//1while(child<n) {//child<6if (child + 1<n&& a[child + 1] > a[child]) {child++;}if (a[child] > a[parent]) {swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else break;}
}
void HeatSort(int* a, int n)//n==7
{/*建堆*/for (int i = (n-2)/2; i >= 0; i--) {AdjustDown(a, i, n);}int end = n - 1;while (end>0) {swap(&a[0], &a[end]); //交换头尾AdjustDown(a, 0,end);//传值传的是个数end--;}
}

时间复杂度

  • 堆排是在堆中进行选书,相对传统的选择排序来说效率快了很多,时间复杂度为O(N*logN);

空间复杂度

  • 堆排的空间复杂度为常数阶O(1);

稳定性

  • 不稳定

快速排序

快速排序是一个相对来说应用最广泛的一种排序算法,它一开始为一名英国计算机科学家霍尔于1960年所发明;
流行的原因是这个排序算法实现简单,且适用于不同的数据,同时相对于排序算法中的其他算法要快得多;
快速排序的基本思想为一种左右分治的思想,就如二叉树相同,将问题分为两个子问题进行解决;

基本思想为:

任取待排元素序列中的其中一个元素作为key值基准值;
按照待排序的区间分为两个序列,key值左边的数据均小于key,key值右边的数据均大于key;
再进行不断分治,最终将对应顺序的数据排列再相应位置即结束;

hoare原生版本

这个版本的也是最初的版本
其单次排序的思想即为:

选取数据中最左边的数据作为基准值key,保存该数据的下标keyi方便数据与key进行比较;
设左右两个指针left与right,left为keyi+1,right为最后一个数据的下标;
left从前往后遍历,right从后往前进行遍历,left找比key大的数据,right找比eky小的数据;
当left找到相应的数据同时right也找到相应的数据时,将两个数据进行交换;
交换过后再继续遍历;
当left与right相遇时,则停止遍历,同时将相遇位置的值与key进行交换;

同时,在遍历时若是需要左右指针都在中间位置停止,那必须保证key在左边时右指针先往前遍历,key若是在最右边选数则需要左边指针先进行遍历

在这里插入图片描述

[单次排序示意图]
//单次排序
int PartSort(int* a, int left, int right)
{int keyi = left;while (left < right) {while (left < right&&a[right] >= a[keyi])right--;while (left < right&&a[left ] <= a[keyi])left++;swap(&a[left], &a[right]);}swap(&a[keyi], &a[left]);return left;
}

当第一次排序过后,将要以前序遍历的方式进行分治;
区间将会以[begin,keyi-1]keyi[keyi+1,end]进行划分;
根据该思维进行递归;

void QuitSort(int* a, int begin, int end)
{if (begin>=end) {return;} int kiey = PartSort(a, begin, end);QuitSort(a, begin, kiey-1);QuitSort(a, kiey+1, end);
}
递归的结束条件即为该组数据中只存在一个数或是该段区间不存在;

挖坑法

挖坑法的思路与hoare原生版本的思路大体相同:

思路即为在开始前记住最左边的基准值key的值并将其保存,左右指针同时往中间进行遍历
左边找比基准值key大的值,右边找小的值,与hoare相同,key在左边时右指针先进行遍历;

当最左边key值被保存之后,key所在的位置即形成一个坑;
当右指针找到相应的值时则将右指针的值给给这个坑,此时left所在的位置,也就是key的位置的坑被填;
同时right指针所在的位置又新生成一个坑,left指针再从后往前遍历找到对应位置进行填补,如此反复;
当两个指针相遇或者相交时,所在的位置必定是一个坑,此时再把key值填入次坑;

在这里插入图片描述

[单次排序示意图]
//单次排序
int PartSort(int* a, int left, int right)
{int key = a[left];int hole = left;while (left < right) {while (left < right&&a[right] >= key)right--;a[hole] = a[right];hole = right;while (left < right&&a[left ] <= key)left++;a[hole] = a[left];hole = left;}a[hole] = key;return hole;
}

其递归的思路都一致;


前后指针法

前后指针,顾名思义即为前后两个指针进行遍历;
其思路为:

设指针keyi,指针prev与指针cur,用cur指针进行遍历,当cur所在位置小于基准值key值时,
prev指针先++,而后将cur指针所在的数据与prev所在的数据进行交换;
当cur大于right时,则停止遍历,将此时prev所在位置的值与key值进行交换;
此时key值左边的数小于key,右边则大于key;

在这里插入图片描述

[单次排序示意图]
//单趟排序
int PartSort(int* a, int left, int right)
{int keyi = left;int prev = left;int cur = left + 1;while (cur<=right) {if (a[cur] < a[keyi]) {prev++;swap(&a[prev], &a[cur]);}cur++;}swap(&a[prev], &a[keyi]);return prev;
}

该方法的递归思路与上面两种方式相当;


三数取中优化

快速排序,是基于在综合性能以及综合使用场景情况下速度较快,效率较高才之所以叫做快速排序;
当然在一定情况下,快速排序依旧有短板;

在一般情况下,综合来说,快速排序的时间复杂度为O(N*logN),而不排除一些极端的情况;
即数据本身就为有序的情况;

在这个情况下,由于上述选基准值key值的方式都是为数据中最左边或者最右边的数据,而若是数据本身有序的情况;
快速排序的时间复杂度将会为O(N2);

在这里插入图片描述

由于key值每次都为最小值,导致快速排序遍历速度更慢,最终时间复杂度将会为O(N2);

在这里插入图片描述

针对该种情况,有人设计了一种情况,即为将key值取头尾中间三个数据中,既不是最大也不是最小的值;

int GetMidIndex(int* a, int left, int right) {int mid = (left + right) / 2;if (a[mid] > a[left]) {if (a[right] > a[mid]) {return mid;}else if (a[left] > a[right]) {return left;}else return right;}else // a[left]>a[mid]{if (a[right] > a[left]) {return left;}else if (a[mid] > a[right]) {return mid;}else return right;}
}

当每次单趟排序时,设置一个指针mid指向中间位置,将该中间值与最左以及最右进行三数取中并返回下标;
再将它与key值进行交换;

//使用hoare原生版本的单趟排序作为演示
int PartSort(int* a, int left, int right)
{int mid = GetMidIndex(a,left,right);swap(&a[left], &a[mid]);int keyi = left;while (left < right) {while (left < right&&a[right] >= a[keyi])right--;while (left < right&&a[left ] <= a[keyi])left++;swap(&a[left], &a[right]);}swap(&a[keyi], &a[left]);return left;
}

该方法的优化解决了快速排序中遇到的有序情况;


随机数取key优化

由于三数取中是能解决一部分办法,但是并没有完全解决,有些题目的测试用例对于专门三数取中的优化还是并不能完全解决;
因此在此基础上有出现了一个为随机数取key的优化;
即为将数据中[left,right]这段区间中随机选一个数做key;

//使用hoare原生版本的单趟排序作为演示
int PartSort(int* a, int left, int right)
{int randi = left+rand()%(right-left);//在使用rand函数前需先调用srand函数swap(&a[left], &a[randi]);int keyi = left;while (left < right) {while (left < right&&a[right] >= a[keyi])right--;while (left < right&&a[left ] <= a[keyi])left++;swap(&a[left], &a[right]);}swap(&a[keyi], &a[left]);return left;
}

三路划分版

三路划分版本的快速排序针对一些重复率较高的情况:

[2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,]

若是遇到该种情况,三数取中与随机数取key的方法将会再次失效,时间复杂度也会再度来到 O(N2)的极端情况;
则在该种情况下,又有人想出了一种对应的方法,这个方法即为三路划分法;
该方法的思想为:

将原本的[left,keyi-1]keyi[keyi+1,right]区间;
划分为[<key][==key][>key]三个区间;

在这里插入图片描述
思路为:

使用三数取中的方式选取一个适和的数据作为key值并将key值进行保存(保存的为数据不为下标),
设指针cur指向left所指数据的后一个位置,即cur = left+1;
当cur指针不大于最右边界的情况进行循环遍历;
当cur所指向的数据小于key值时,则交换left指向的数据与cur指向的数据,并将两个指针都向后走一步;
当cur所指向的数据等于key值时,则不进行交换,cur继续遍历;
当cur指向的数据大于key值时,将cur所指向的数据与right所指向的数据进行交换,同时right–;
此时cur指针保存不懂避免交换过后cur所指向的数据小于key值;

void QuitSort(int* a, int begin, int end) 
{if (begin >= end) {return;}int left = begin;int right = end;int mid = GetMidIndex(a, left, right);swap(&a[mid], &a[left]);int key = a[left];int cur = left+1;while (cur <= right) {if (a[cur] < key) {swap(&a[left], &a[cur]);cur++, left++;}else if (a[cur] > key) {swap(&a[right], &a[cur]);right--;}else {cur++;}}QuitSort(a,begin, left - 1);QuitSort(a, right+1, end);
}

非递归

快速排序除了递归以外同时也存在非递归的写法,
非递归的快速排序依靠为栈或者队列;
基本思想为:
将每次需要处理的区间进行入栈(注意栈的顺序为LIFO),
出栈后将出栈的区间进行处理(处理采用单趟排序处理);
当栈不为空的时候说明栈内仍存在区间需要进行出栈并处理,反复直到区间不存在或是区间内只有一个数时则不进行处理;
在这里插入图片描述
以改图为例,按照顺序先进[6,0];
再出栈[0,6]并将这段区间进行处理;
处理过后返回keyi,即区间又可以划分为[left,keyi-1]keyi[keyi+1,right];
如此循环;

void QuiteSortNonR(int* a, int begin, int end) {ST st;InitStack(&st);/**第一次入栈整段区间*并在循环中进行处理(循环将会返回keyi值)*/StackPush(&st, end);StackPush(&st, begin);while (!StackEmpty(&st)) {int left = StackTop(&st);StackPop(&st);int right = StackTop(&st);StackPop(&st);int key = PartSort(a, left, right);if (key + 1 < right) {StackPush(&st,right);StackPush(&st, key+1);}if (key - 1 > left) {StackPush(&st, key - 1);StackPush(&st, left);}}Destruc(&st);//销毁栈
}

稳定性

  • 综合上述来说,快速排序并不是一个十分稳定的排序,它在综合情况以及使用场景之中,总体效率都比较不错
    但是若是遇到比较极端的情况下却不能很好解决,如有序的情况,有序的情况下一些排序将会停止排序,而快速排序若是在这种
    情况下用三数取中的方式进行处理,仍会有一定的开销;
    故快速排序不稳定;

归并排序

归并排序,是一种基于归并操作的排序算法,该算法与快速排序算法一样采用了分治法的排序;
基本思想为:
将两个已经有序的子序列进行合并,得到一个完全有序的序列;
要使两个子序列有序,一样要将子序列中的子序列变得有序;将问题化为子问题;

在这里插入图片描述

分治思路示意图

递归

递归的思路即为上述思路,
相应的思路可以参考二叉树中的后序遍历;
具体步骤为:

开出一块与数据内容相应大小的空间temp,这块空间用来进行每两组之间的归并;
以上图为参考,因为每个子区间中的数据不一定都是有序的;
所以要使用后序遍历的思路,最终将数据分为单个数据(单个数据可以看作有序);
使用归并的思路将数据归并至temp中;
最终再每次递归结束时将数据拷贝回原数组;

//递归
void _MergeSort(int* a, int begin, int end, int* tmp) 
{if (begin == end) {return;}int mid = (begin + end) / 2;/**这里分出来的区间为[begin,mid][mid+1,end]*走后续,因为要左右区间都为有序才能进行归并*/_MergeSort(a, begin, mid, tmp);_MergeSort(a, mid + 1, end, tmp);//与快排不同,快排的区间可以看作为三块//而归并排序只需要分为左右两块进行归并即可int left1 = begin, right1 = mid;int left2 = mid + 1, right2 = end;int i = begin;//使用归并的思路将数据依次放至tmp数组中while (left1 <= right1 && left2 <= right2) {if (a[left1] < a[left2]) {tmp[i++] = a[left1++];}else//a[left1]>=a[left2]tmp[i++] = a[left2++];}//但凡两个其中一个结束//将剩余数据依次拷贝至tmp数组while (left1 <= right1) {tmp[i++] = a[left1++];}while (left2 <= right2) {tmp[i++] = a[left2++];}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("MergeSort::malloc fail");exit(-1);}_MergeSort(a, 0, n - 1, tmp);free(tmp);
}

非递归

相比于递归不同,非递归比递归所需要在意的细节更多;
但是思路上大体相同;
与快排不同,归并排序的非递归并不需要用到栈或者队列;
归并排序的非递归大致的思路为:

设一个gap为间距值,gap一开始为1,且gap在每次归并过一次时*=2;
当gap为1时实现一一归并,
当gap为2时实现两两归并,以此类推;

void MergeSortNonR(int* a, int n/*数据个数*/)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL) {perror("MergeSort::malloc fail");exit(-1);}int gap = 1;//1  2  4  8while (gap < n) {for (int j = 0;j<n; j += 2 * gap) {int left1 = j, right1 = j + gap - 1;int left2 = j + gap, right2 = j + 2 * gap - 1;int i = j;while (left1 <= right1 && left2 <= right2) {if (a[left1] < a[left2]) {tmp[i++] = a[left1++];}else//a[left1]>=a[left2]tmp[i++] = a[left2++];}//单反两个其中一个结束while (left1 <= right1) {tmp[i++] = a[left1++];}while (left2 <= right2) {tmp[i++] = a[left2++];}}memcpy(a , tmp , sizeof(int) * n);gap *= 2;}free(tmp);}

该段代码可以完美实现上图中所对应的数据所给的排序;
但是这个方法的缺陷特别明显;

缺陷:

  1. 当数据量不为2n时,将会有越界风险,即gap是一个不是很好控制的因素所以相应的需要进行边界的修正;
  2. 当数据在拷贝时,由于在控制边界之后,有些数据不能直接转入temp空间,导致空间中存在随机值,若是盲目拷贝整块空间将会出现随机数覆盖原有效数组的风险;

根据相应的缺陷需要制定出有效的措施;


调整边界

既然gap不能很好的控制调整,那就在遍历的过程中调整边界以达到不会越界;
从上述情况种可以了解到在非递归种共有四个指针,分别为left1,right1,left2与right2;
而非递归中越界的情况分为3种;

right1,left2与right2都越界

在这里插入图片描述

left2与right2越界

在这里插入图片描述

right2越界

在这里插入图片描述
相应的解法即为修改边界,当right1或者left2越界时,
即表示该段区间后的数据不需要进行归并,若是归并则会越界;
当right2越界时,则将right2的边界改为n-1;

void MergeSortNonR(int* a, int n/*数据个数*/)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL) {perror("MergeSort::malloc fail");exit(-1);}int gap = 1;//1  2  4  8while (gap < n) {for (int j = 0;j<n; j += 2 * gap) {int left1 = j, right1 = j + gap - 1;int left2 = j + gap, right2 = j + 2 * gap - 1;int i = j;//right1 left2 right2 全部越界//left2 right2 越界//right2//修正前if (right1 >= n || left2 >= n){break;}if (right2 >= n) {right2 = n - 1;}//修正后while (left1 <= right1 && left2 <= right2) {if (a[left1] < a[left2]) {tmp[i++] = a[left1++];}else//a[left1]>=a[left2]tmp[i++] = a[left2++];}//两个其中一个结束while (left1 <= right1) {tmp[i++] = a[left1++];}while (left2 <= right2) {tmp[i++] = a[left2++];}}memcpy(a, tmp, sizeof(int)n);gap *= 2;}free(tmp);

单次归并单次拷贝

第二个缺点的解决方法即为,每次归并结束后,都将这段归并的数据拷贝回原数组,归并一段拷贝一段;
即不会出现随机数覆盖原有效数据的问题;

最终代码:

void MergeSortNonR(int* a, int n/*数据个数*/)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL) {perror("MergeSort::malloc fail");exit(-1);}int gap = 1;//1  2  4  8while (gap < n) {for (int j = 0;j<n; j += 2 * gap) {int left1 = j, right1 = j + gap - 1;int left2 = j + gap, right2 = j + 2 * gap - 1;int i = j;//right1 left2 right2 全部越界//left2 right2 越界//right2if (right1 >= n || left2 >= n){break;}if (right2 >= n) {right2 = n - 1;}while (left1 <= right1 && left2 <= right2) {if (a[left1] < a[left2]) {tmp[i++] = a[left1++];}else//a[left1]>=a[left2]tmp[i++] = a[left2++];}while (left1 <= right1) {tmp[i++] = a[left1++];}while (left2 <= right2) {tmp[i++] = a[left2++];}memcpy(a + j, tmp + j, sizeof(int) * (right2 - j + 1));//     0        0                     1      2//     1        1                      3     }gap *= 2;}free(tmp);}

时间复杂度

  • 归并排序的时间复杂度为O(N*logN),但是以综合性能的话,略逊快排一点,但无伤大雅;

空间复杂度

  • 归并排序的缺点之一即为空间复杂度,在归并的过程中的开销并不多,但由于归并排序需要
    开一段大小为N的空间,故空间复杂度为O(N);

稳定性

  • 稳定

总结

排序算是一种较为基本的且需要掌握的算法,优秀的排序算法能在程序中使得效率倍式的提升;
不同的排序算法可以根据不同的使用场景分别进行使用;
排序中较为难的点为一些排序算法中的边界问题,以及递归中分治思路的理解;

在类似快速排序样式的排序中依旧可以再进行小区间优化,
即在递归到一定数据量时使用稳定的且效率还行的非复杂排序对数据进行排序;
小区间优化的目的是为了减少排序过程中递归调用的次数从而达成效率的优化;

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

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

相关文章

02Redis的命令行客户端和桌面客户端的下载和安装

Redis桌面客户端 安装完成Redis服务,我们就可以在Redis的客户端操作Redis的数据库实现数据的CRUD了,客户端分为三类命令行客户端, 图形化桌面客户端,编程客户端 命令行客户端 Redis安装完成后就自带了命令行客户端: redis-cli [options] [commonds] -h选项&#xff1a;指定…

矢量图形编辑软件illustrator 2023 mac软件特点

illustrator 2023 mac是一款矢量图形编辑软件&#xff0c;用于创建和编辑排版、图标、标志、插图和其他类型的矢量图形。 illustrator mac软件特点 矢量图形&#xff1a;illustrator创建的图形是矢量图形&#xff0c;可以无限放大而不失真&#xff0c;这与像素图形编辑软件&am…

计算机网络 实验二 交换机的基本配置

实验二 交换机的基本配置 实验目的 • 掌握交换机的配置方式及切换命令&#xff1b; • 掌握交换机端口的基本配置&#xff1b; • 掌握交换机mac地址的查看与管理方法。 实验设备 以太网交换机一台服务器一台PC机五台配置电缆、网线若干 网络拓扑及IP地址分配 给计算…

Docker部署ActiveMQ消息中间件

1、准备工作 docker pull webcenter/activemq:5.14.3 Pwd"/data/software/activemq" mkdir ${Pwd}/data -p2、运行容器 docker run -d --name activemq \-p 61616:61616 \-p 8161:8161 \-v ${Pwd}/data:/opt/activemq/data \-v /etc/localtime:/etc/localtime \--r…

服务器补丁管理软件

随着漏洞的不断上升&#xff0c;服务器修补是增强企业网络安全的典型特征。作为业务关键型机器&#xff0c;计划服务器维护的停机时间无疑是一件麻烦事。但是&#xff0c;借助高效的服务器补丁管理软件&#xff08;如 Patch Manager Plus&#xff09;&#xff0c;管理员可以利用…

UE5读取json文件

一、下载插件 在工程中启用 二、定义读取外部json文件的函数&#xff0c;参考我之前的文章 ue5读取外部文件_艺菲的博客-CSDN博客 三、读取文件并解析为json对象 这里Load Text就是自己定义的函数&#xff0c;ResourceBundle为一个字符串常量&#xff0c;通常是读取的文件夹…

UML活动图

在UML中&#xff0c;活动图本质上就是流程图&#xff0c;它描述系统的活动、判定点和分支等&#xff0c;因此它对开发人员来说是一种重要工具。 活动图 活动是某件事情正在进行的状态&#xff0c;既可以是现实生活中正在进行的某一项工作&#xff0c;也可以是软件系统中某个类…

Ubuntu上通过源码方式安装Redis

上一篇文章Ubuntu上安装、使用Redis的详细教程已经介绍了再Ubuntu操作系统上安装Redis的详细过程&#xff0c;但是因为安装的Redis只有最主要的配置文件和redis-server&#xff0c;为了更深入地学习Redis和进行更复杂的操作&#xff0c;需要安装一个完整的Redis服务。 这篇文章…

SEO方案尝试--Nuxtjs项目基础配置

Nuxtjs 最新版 Nuxt3 项目配置 安装nuxtjs 最新版 Nuxt3 参考官网安装安装插件安装ElementPlus页面怎么跳转&#xff0c;路由怎么实现404页面该怎么配置配置 网页的title 安装nuxtjs 最新版 Nuxt3 参考官网安装 安装插件 安装ElementPlus 安装 Element Plus 和图标库 # 首先&…

EasyWindow - Android 悬浮窗框架

官网 https://github.com/getActivity/EasyWindow 项目介绍 本框架意在解决一些极端需求&#xff0c;如果是普通的 Toast 封装推荐使用 Toaster 集成步骤 如果你的项目 Gradle 配置是在 7.0 以下&#xff0c;需要在 build.gradle 文件中加入 allprojects {repositories {/…

【C++面向对象侯捷】8.栈,堆和内存管理

文章目录 栈&#xff0c;堆stack object的生命周期static local object的生命周期global object的生命周期heap objects 的生命期new&#xff1a;先分配memory&#xff0c;再调用构造函数delete: 先调用析构函数&#xff0c;再释放 memory动态分配所得的内存块&#xff0c;in V…

腾讯mini项目-【指标监控服务重构】2023-08-25

今日已办 traefik proxy jaeger Prometheus prometheus | Prometheus 配置完依然无法实现 web-url的前缀访问【待解决】 Set span storage type : elasticsearch services:elasticsearch:image: elasticsearch:7.17.12container_name: elasticsearchnetworks:- backend # …

超好用的接口自动化框架,lemon-easytest内测版发布,赶紧用起来~

easytest easytest 是一个接口自动化框架。 功能特点&#xff1a; 支持 http 接口测试 支持 json&#xff0c;html,xml 格式的响应断言 支持数据库断言 支持用例标记筛选 支持用例失败重运行 支持多线程 安装 pip install lemon_easytest 快速使用 不需要写任何代码…

postgresql|数据库|centos7下基于postgresql-12的主从复制的pgpool-4.4的部署和使用

前言&#xff1a; postgresql数据库只用自身的一些配置是无法做到最优的优化的&#xff0c;需要通过一些外置插件&#xff08;中间件&#xff09;来提高服务器的整体性能&#xff0c;通俗的说就是数据库仅仅依靠自身是无法达到性能最优的&#xff0c;很多时候需要更改数据库的…

Java内存模型介绍

文章目录 Java内存模型前言Java内存模型基本介绍指令重排相关概念主存和本地内存相关介绍JMM中的8种同步规则和8种同步操作happens-before 原则内存屏障总结 Java内存模型 前言 本文主要介绍一下JMM中的一些常见概念&#xff0c;通过本文让你能够快速的对JMM有一个大致的了解 …

【Java】基于物联网技术的智慧工地源码(项目端、监管端、APP端、智慧大屏)

智慧工地是将云计算、大数据、物联网、移动技术和智能设备等信息化技术手段&#xff0c;聚集在建筑工地施工管理现场&#xff0c;围绕人员、机械、物料、环境等关键要素&#xff0c;建立智能信息采集、高效协同管理、数据科学分析、过程智慧预测&#xff0c;最终实现建筑工地的…

2023/9/20 -- C++/QT

时钟: widget.h: #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QPainter> #include <QPaintEvent> #include <QDateTime> #include <QLabel> #include <QTimer> #include <QDebug>QT_BEGIN_NAMESPACE namespac…

CSS复习之选择器

目录 一、常用选择器 1.1元素选择器 1.2id选择器 1.3class选择器 二、复合选择器 2.1交集选择器 2.2并集选择器 三、关系选择器 3.1子元素选择器 3.2后代选择器 3.3兄弟选择器 四、属性选择器 五、伪类选择器 六、伪元素的选择器 七、超链接的伪类 一、常用选择器…

【ArcGIS】土地利用变化分析详解(矢量篇)

土地利用变化分析详解-矢量篇 土地利用类型分类1 统计不同土地利用类型的面积/占比1.1 操作步骤Step1&#xff1a;Step2&#xff1a;计算面积Step3&#xff1a;计算占比 2 统计不同区域各类土地利用类型的面积2.1 操作步骤 3 土地利用变化转移矩阵3.1 研究思路3.2 操作步骤 4 分…

【卖出备兑看涨期权策略(Covered_call)】

卖出备兑看涨期权策略&#xff08;Covered_call&#xff09; 卖出备兑看涨期权策略是一种最基本的收入策略&#xff0c;该策略主要操作就是在持有标的资产的同时卖出对应的看涨期权合约&#xff0c;以此来作为从持有的标的资产中获取租金的一种方法。如果标的资产的价格上涨到…