【数据结构与算法】:快速排序和冒泡排序

一,快速排序

快速排序是一种比较复杂的排序算法,它总共有4种实现方式,分别是挖坑法左右"指针"法前后"指针"法,以及非递归的快速排序,并且这些算法中也会涉及多种优化措施,比如三数取中小区间优化,下面都会一一介绍。

由于它效率极高的缘故,快速排序也是日常开发中使用最多的,最重要的排序算法。

1. 挖坑法

1.1 基本思想:

任取待排序元素序列中的某元素(一般选最左边或最右边的元素)作为基准值(也叫做 key 关键字),按照该排序码将待排序集合分割成两子序列左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

1.2 一趟排序图解如下:

给定一无序数组,选第一个元素为关键字 key = 6
在这里插入图片描述

我们选定关键字 key = 6后,就说明6的位置就可以被覆盖了,所以我们就说左边形成了一个****坑用pivot 表示

![!](https://img-blog.csdnimg.cn/direct/3d53fed3d5e64ef4a46eb2d2f4faf554.png

左边有坑,右边的 end 要从最后一个元素开始找比 key 小的数找到后放到左边的坑里,所以5放进了坑中

在这里插入图片描述
5被拿走之后,右边它原来所在的位置就形成了一个新坑,此时,左边的 begin 要开始找比 key 大的数找到后放到右边的坑里,所以7放进了坑中
在这里插入图片描述
7被拿走后,左边又形成了一个新坑,此时,end 又要开始找比 key 小的数放到左边的坑里,所以4放进了坑中

在这里插入图片描述
此时,右边又形成了新坑,begin 要开始找比 key 大的数,找到后放到右边的坑里,所以9放进了坑中

在这里插入图片描述
左边又形成了坑,右边 end 开始找,找到了3,放入坑中

在这里插入图片描述
最后一次 begin++ 后,begin 和 end 重叠了,并且它们一定相遇在坑中,此时,把 key 放入坑中即可。

在这里插入图片描述
上述操作只是第一趟排序,只排好了一个数,此时第一个基准 key = 6已经在它合适的位置上了(排好序后的位置),后面对左右子序列排序时6不动。并且已经把数组分成了两个子序列,以 key 为基准,左边的元素都比它小,右边的元素都比它大。

1.3 单趟排序的代码实现如下:

注意:第二个和第三个 while 中的 begin < end 不能缺少,要防止在找大和找小的时候 begin 和 end 错开或是在极端情况下(比如已经升序时)end一直减导致越界。

int PartSort1(int* arr, int sz)
{int begin = 0;int end = sz -1;int key = arr[begin];int pivot = begin;//这是排一趟,只排好了一个数while (begin < end){//左边有坑,右边end找比key小的while (begin < end && arr[end] > key){end--;}//小的放到了左边的坑里,右边end自己形成了新的坑arr[pivot] = arr[end];pivot = end;//右边有坑,左边end找比key大的while (begin < end && arr[begin] < key){begin++;}//大的放到右边的坑里,左边begin自己形成新的坑arr[pivot] = arr[begin];pivot = begin;}//最后begin和end相遇了,把key放入该位置pivot = begin;arr[begin] = key;}

1.4 整体排序

要利用分治递归思想。第一趟排序把整个数组分割成了左子序列和右子序列,如果左右子序列都有序了,那么整个数组就有序了,所以再递归使用前面的挖坑算法,再找出关键字,再把左右子序列分割成子序列…… 直到关键字的左右两边只有一个数据不可再递归,或者是关键字的左序列,右序列都是有序,那么整体就有序了。

如图所示:
在这里插入图片描述

1.5 整体排序过程代码实现如下:

注意:因为是左右子序列,所以要控制一个区间。

void QuickySort(int* arr, int left,int right)
{//当左右子区间不存在,或只有一个元素时,//就不需要递归了,排序完成if (left >= right){return;}int begin = left;int end = right;int key = arr[begin];int pivot = begin;//这是排一趟,只排好了一个数while (begin < end){//左边有坑,右边end找比key小的while (begin < end && arr[end] > key){end--;}//小的放到了左边的坑里,右边end自己形成了新的坑arr[pivot] = arr[end];pivot = end;//右边有坑,左边end找比key大的while (begin < end && arr[begin] < key){begin++;}//大的放到右边的坑里,左边begin自己形成新的坑arr[pivot] = arr[begin];pivot = begin;}//最后begin和end相遇了,把key放入该位置pivot = begin;arr[begin] = key;//[left] pivot [right]// [left pivot-1]  pivot [pivot+1 right]//左子区间和右子区间有序,整体就有序了QuickySort(arr, left, pivot-1);QuickySort(arr, pivot+1, right);}

2. 快速排序的优化

2.1 三数取中

上文快排的算法思想有一个致命的缺陷:那就是当数据为有序时,其时间复杂度为O(N*N)。

原因:这是因为在取关键字 key 的值时,一直都是选最左边(或最右边)的数据。当数组本为升序时,每次关键字的右子序列的值都比它大,再次递归调用时,右子序列的子序列也是如此(降序同理)。

所以这个缺陷的原因就是 key 的取值。
那该如何取 key的值呢?一个比较好的方法是三数取中

三数取中:并不是指取所有数据中间的那数,而是指在三个数中取那个不大不小的中间数,这个数可能在最左边,也可能在最右边。

通过这种类似随机选数的方法,就能保证一定不是数据中最大或最小的值做 key。

2.1.1 三数取中的代码的实现:

//三数取中
int GetMidIndex(int* arr, int left, int right)
{//右移有除2的效果int mid = (left + right) >> 1;if (arr[mid] > arr[left]){if (arr[mid] < arr[right]){return mid;}else if(arr[left]>arr[right]){return left;}else{return right;}}else   //arr[mid] < arr[left]{if (arr[mid] > arr[right]){return mid;}else if (arr[left] < arr[right]){return left;}else{return right;}}
}

但是挖坑算法中我们习惯拿 begin 作为 key ,为了保持挖坑算法不被改变,我们把 begin 指向的值和通过三数取中选出的数的指向的值进行交换,确保 key 仍是begin指向的值。

代码实现为:


void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}void QuickySort(int* arr, int left,int right)
{   //当左右子区间不存在,或只有一个元素时,//就不需要递归了,排序完成if (left >= right){return;}int begin = left;int end = right;int index = GetMidIndex(arr, left, right);Swap(&arr[index], &arr[left]);//交换一下,保证key还是最左边的数int key = arr[begin];int pivot = begin;//这是排一趟,只排好了一个数while (begin < end){//左边有坑,右边end找比key小的while (begin < end && arr[end] > key){end--;}//小的放到了左边的坑里,右边end自己形成了新的坑arr[pivot] = arr[end];pivot = end;//右边有坑,左边end找比key大的while (begin < end && arr[begin] < key){begin++;}//大的放到右边的坑里,左边begin自己形成新的坑arr[pivot] = arr[begin];pivot = begin;}//最后begin和end相遇了,把key放入该位置pivot = begin;arr[begin] = key;// [left] pivot [right]// [left pivot-1]  pivot [pivot+1 right]// 左子区间和右子区间有序,整体就有序了QuickySort(arr, left, pivot-1);QuickySort(arr, pivot+1, right);}

2.2 小区间优化

我们知道在函数调用的过程中会在内存中建立栈帧,栈帧的建立也是需要时间和空间的。假设用上述代码排100W个数据,则大致有20层的递归调用,但是在最后几层中就大概调用了80多万次函数,它占用了栈帧的绝大多数空间和时间。

那么有人就会想,能不能把最后几层的函数递归调用消除呢?

官方给出的一种方法是小区间优化法,用于减少递归调用次数。

就是在排序的过程中当左右子序列中的数据个数大于某个数量时,不进行递归了,而是选用其他排序算法进行排序。这里一般用插入排序。

2.2.1 小区间优化的代码实现:

(注意:插入排序的算法这里没有给出,想要了解的请前往我的主页。)

//小区间优化法:减少递归调用次数//  keyindex - 1 - left 指子序列中的元素个数
//  > 10是我们控制的一个界限  
if (keyindex - 1 - left > 10)
{QuickySort(arr, left, keyindex - 1);
}
else
{  // arr + left 是指这时的子序列不一定从第一个元素开始//keyindex - 1 - left + 1 是指元素的个数InsertSort(arr + left, keyindex - 1 - left + 1);
}if (right - (keyindex + 1) > 10)
{QuickySort(arr, keyindex + 1, right);
}
else
{InsertSort(arr + keyindex + 1, right - (keyindex + 1) + 1);
}

但是由于小区间优化所带来的效率提升并不显著,而且它是与我们所控制的那个界限有关,所以平时并没有过于注重这个优化

3.挖坑法的完整排序代码


void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}//三数取中
int GetMidIndex(int* arr, int left, int right)
{//右移有除2的效果int mid = (left + right) >> 1;if (arr[mid] > arr[left]){if (arr[mid] < arr[right]){return mid;}else if(arr[left]>arr[right]){return left;}else{return right;}}else   //arr[mid] < arr[left]{if (arr[mid] > arr[right]){return mid;}else if (arr[left] < arr[right]){return left;}else{return right;}}
}//挖坑法
int PartSort1(int* arr, int left, int right)
{int index = GetMidIndex(arr, left, right);Swap(&arr[index], &arr[left]);//交换一下,保证key还是最左边的数int begin = left;int end = right;int key = arr[begin];int pivot = begin;//这是排一趟,只排好了一个数while (begin < end){//左边有坑,右边end找比key小的while (begin < end && arr[end] > key){end--;}//小的放到了左边的坑里,右边end自己形成了新的坑arr[pivot] = arr[end];pivot = end;//右边有坑,左边end找比key大的while (begin < end && arr[begin] < key){begin++;}//大的放到右边的坑里,左边begin自己形成新的坑arr[pivot] = arr[begin];pivot = begin;}//最后begin和end相遇了,把key放入该位置pivot = begin;arr[begin] = key;return key;
}void QuickySort(int* arr, int left,int right)
{   //当左右子区间不存在,或只有一个元素时,//就不需要递归了,排序完成if (left >= right){return;}int keyindex = PartSort1(arr, left, right);// [left] keyindex [right]// [left keyindex -1]  keyindex [keyindex +1 right]// 左子区间和右子区间有序,整体就有序了QuickySort(arr, left, keyindex - 1);QuickySort(arr, keyindex + 1, right);//或是/*if (keyindex - 1 - left > 10)
{QuickySort(arr, left, keyindex - 1);
}
else
{  // arr + left 是指这时的子序列不一定从第一个元素开始//keyindex - 1 - left + 1 是指元素的个数InsertSort(arr + left, keyindex - 1 - left + 1);
}if (right - (keyindex + 1) > 10)
{QuickySort(arr, keyindex + 1, right);
}
else
{InsertSort(arr + keyindex + 1, right - (keyindex + 1) + 1);}*/

排序结果为:
在这里插入图片描述

3.1 时间复杂度与稳定性

挖坑法的时间复杂度是O(N*logN),是不稳定的排序。

3. 左右"指针"法

3.1 算法思想:

与挖坑法类似,一般也要用三数取中法选一个关键字做 key,最终也是把整个数组分割成左右两个子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值。

只是实现的方式不同,左右"指针"法是分别从数组的最左边和最右边开始找数左边的 begin 找比 key大的数右边的 end 找比 key 小的数找到后把这两个位置上的数交换,直到分割成左右两个子序列,然后左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

3.2 单趟排序的图解如下:

给定一无序数组,选第一个元素为关键字 keyi = 6,这里的keyi是数组的下标
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/d2905a16ec794096b226e39d8bcd8af6.png

begin++ 找比 keyi 大的数,end – 找比 keyi 小的数,找到后停下来交换
在这里插入图片描述
重复上述操作
在这里插入图片描述
最后当 begin 和 end 相遇时,把相遇位置上的数与关键字 keyi所在位置的数 交换
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/450119654a7d4a46a921d89f45ad3bb7.png

最终排完第一趟后,以 keyi所指向的数6为基准,左边的元素都比它小,右边的元素都比它大。

3.3 单趟排序的代码实现:

注意:
1.代码中的三数取中函数与交换函数在上文,此处就直接调用

2.第二个和第三个while中的 begin < end 和 <= 中的等于号二者缺一不可。
在这里插入图片描述


//左右指针法
int PartSort2(int* arr, int left, int right)
{int index = GetMidIndex(arr, left, right);Swap(&arr[index], &arr[left]);//交换一下,保证key还是最左边的数int begin = left;int end = right;int keyi = begin;//第一个元素的下标while (begin < end){//找比key小的while (begin < end && arr[keyi] <= arr[end]){end--;}//找比key大的while (begin < end && arr[keyi] >= arr[begin]){begin++;}Swap(&arr[begin], &arr[end]);}//当begin与end相遇时Swap(&arr[begin], &arr[keyi]);return begin;
}

4. 前后"指针"法

4.1 算法思想

与挖坑法类似,一般也要用三数取中法选一个关键字做 key,最终也是把整个数组分割成左右两个子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值。

只是实现方式不同,前后"指针"法是要定义两个前后变量( cur 和 prev,其中 cur 在前,prev 在后)分别指向数组的前两个元素,前面的 cur 先往前走,prev 后走,cur 找到比key 小的值,每次找到就停下来,prev++,再交换 prev 和 cur 所在位置的值。

直到分割成左右两个子序列,然后左右子序列重复该过程,直到所有元素都排列在相应位置上为止

4.2 单趟排序的部分图解如下:

给定一无序数组,选第一个元素为关键字 keyi = 6,这里的keyi是数组的下标

在这里插入图片描述
前几个数 cur 和 prev 重叠,省略图解

当cur在3的位置上时,prev指向7,此时,交换两数
在这里插入图片描述
再cur++指向了4,停下,prev++指向了9,此时再交换
在这里插入图片描述
………………(重复上述操作)

当cur超出数组界限时,把此时 prev 所指向的值和 keyi 所指向的关键字交换,最终的结果是:

在这里插入图片描述
最终排完第一趟后,以 keyi所指向的数6为基准,左边的元素都比它小,右边的元素都比它大。

4.3 单趟排序的代码实现如下:


//前后指针法
int  PartSort3(int* arr, int left, int right)
{int index = GetMidIndex(arr, left, right);Swap(&arr[index], &arr[left]);//交换一下,保证key还是最左边的数int keyi = left;int prev = left;int cur = left + 1;while (cur <= right){if (arr[cur] < arr[keyi]){prev++;Swap(&arr[cur], &arr[prev]);}cur++;}Swap(&arr[keyi], &arr[prev]);return prev;
}

4.4 代码的小优化

通过上面的图解可知,当 cur 和 prev 重叠时,我们也进行了交换,但是这种自己和自己的交换其实是多于的。

优化代码如下:

在if判断条件中多了++prev != cur

int  PartSort3(int* arr, int left, int right)
{int index = GetMidIndex(arr, left, right);Swap(&arr[index], &arr[left]);//交换一下,保证key还是最左边的数int keyi = left;int prev = left;int cur = left + 1;while (cur <= right){//++prev != cur是指当cur和prev重合时不用多于的交换if (arr[cur] < arr[keyi]&& ++prev != cur){Swap(&arr[cur], &arr[prev]);}cur++;}Swap(&arr[keyi], &arr[prev]);return prev;
}

二,快速排序总结:

  • 快速排序的三种思想虽然实现方式不同,但是最终结果都是以key为基准值把整个数组分割成左右两个子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值。
  • 在我们日常写快速排序算法时,那两种优化方式三数取中,最小区间优化并不是一定要有,可以根据情况自主添加。

1.比如没有优化的挖坑法的代码实现:


void QuickySort(int* arr, int left,int right)
{//当左右子区间不存在,或只有一个元素时,//就不需要递归了,排序完成if (left >= right){return;}int begin = left;int end = right;int key = arr[begin];int pivot = begin;//这是排一趟,只排好了一个数while (begin < end){//左边有坑,右边end找比key小的while (begin < end && arr[end] > key){end--;}//小的放到了左边的坑里,右边end自己形成了新的坑arr[pivot] = arr[end];pivot = end;//右边有坑,左边end找比key大的while (begin < end && arr[begin] < key){begin++;}//大的放到右边的坑里,左边begin自己形成新的坑arr[pivot] = arr[begin];pivot = begin;}//最后begin和end相遇了,把key放入该位置pivot = begin;arr[begin] = key;//[left] pivot [right]// [left pivot-1]  pivot [pivot+1 right]//左子区间和右子区间有序,整体就有序了QuickySort(arr, left, pivot-1);QuickySort(arr, pivot+1, right);}void PrintArray(int* arr, int sz)
{for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");}int main()
{int arr[] = { 6,7,9,2,4,3,5,1,0,8,-1};int sz = sizeof(arr) / sizeof(int);//快速排序QuickySort(arr, 0, sz - 1);PrintArray(arr, sz);
}

2.比如没有优化的前后"指针"法的代码实现:


void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}void QuickySort(int* arr, int left,int right)
{//当左右子区间不存在,或只有一个元素时,//就不需要递归了,排序完成if (left >= right){return;}int keyi = left;int prev = left;int cur = left + 1;while (cur <= right){//++prev != cur是指当cur和prev重合时不用多于的交换if (arr[cur] < arr[keyi]&& ++prev != cur){Swap(&arr[cur], &arr[prev]);}cur++;}Swap(&arr[keyi], &arr[prev]);//[left] pivot [right]// [left pivot-1]  pivot [pivot+1 right]//左子区间和右子区间有序,整体就有序了QuickySort(arr, left, keyi-1);QuickySort(arr, keyi+1, right);}void PrintArray(int* arr, int sz)
{for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");}int main()
{int arr[] = { 6,7,9,2,4,3,5,1,0,8,-1};int sz = sizeof(arr) / sizeof(int);//快速排序QuickySort(arr, 0, sz - 1);PrintArray(arr, sz);
}

三,冒泡排序

1.基本思想:

从序列的一端开始往另一端冒泡,依次比较相邻的两个数的大小。

设数组长度为N。

1.每轮比较相邻的前后两个数据,如果前面数据大于(或者小于)后面的数据,就将这两个个数据交换。

2.这样每轮对数组的第0个数据到N-1个数据进行一次遍历后,最大或者最小的一个数据就到数组第N-1个位置。

3.第一轮比较到下标为N-1的数据(最后一个),以后每次比较都-1。

2.图解冒泡排序:
以 [ 8,2,5,9,7 ] 这组数字来做示例:
从左往右依次冒泡,将小的往右移动(排降序)
第一轮冒泡:

在这里插入图片描述
首先比较第一个数和第二个数的大小,我们发现 2 比 8 要小,那么保持原位,不做改动。位置还是 8,2,5,9,7 。指针往右移动一格,接着比较:

在这里插入图片描述

比较第二个数和第三个数的大小,发现 2 比 5 要小,所以位置交换,交换后数组更新为:[ 8,5,2,9,7 ]。
指针再往右移动一格,继续比较:

在这里插入图片描述

比较第三个数和第四个数的大小,发现 2 比 9 要小,所以位置交换,交换后数组更新为:[ 8,5,9,2,7 ]。同样,指针再往右移动,继续比较:

在这里插入图片描述

比较第 4 个数和第 5 个数的大小,发现 2 比 7 要小,所以位置交换,交换后数组更新为:[ 8,5,9,7,2 ]。

下一步,指针再往右移动,发现已经到底了,则本轮冒泡结束,处于最右边的 2 就是已经排好序的数字。

通过这一轮不断的对比交换,数组中最小的数字移动到了最右边。

重复上述步骤,得到的最终结果是:

在这里插入图片描述
3.代码实现冒泡排序如下:


void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}void BubbleSort(int* arr, int sz)
{for (int j = 0; j < sz; j++){//一趟排序for (int i = 1; i < sz-j; i++){if (arr[i - 1] < arr[i]){//前一个比后一个小,就交换Swap(&arr[i - 1], &arr[i]);}}}
}

4.冒泡排序的小优化:

假设我们要排降序,如果数组此时就是降序,那么在第一轮比较过后数据并没有发生交换,那就不要再进行多于的后续比较了,直接跳出循环即可。

void BubbleSort(int* arr, int sz)
{for (int j = 0; j < sz; j++){int exchange = 0;//默认是有序的//一趟排序for (int i = 1; i < sz-j; i++){if (arr[i - 1] > arr[i]){//前一个比后一个大,就交换Swap(&arr[i - 1], &arr[i]);//如果不是有序的就发生了交换,exchange=1exchange = 1; }}//如果一趟比较过后发现是有序的,就直接跳出循环if (exchange == 0){break;}}
}

5.时间复杂度和稳定性的分析

最好:就是顺序时,时间复杂度为O(N)
乱序时:时间复杂度为O(N*N)

所以冒泡排序的时间复杂度是O(N*N)。
冒泡排序算法是稳定的。

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

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

相关文章

1. 先简单看看项目吧

前言 就目前的前端生态而言&#xff0c;跨端开发基本算是每一个前端开发者必备的技能点之一了&#xff0c;随便看看各大招聘网站上关于前端岗位的招聘要求&#xff0c;几乎都要小程序开发了.... 另外由于Uni-app等这些跨端框架的产生&#xff0c;中小型企业对于Android/Ios等…

APP的UI设计规范

APP的设计规范是一系列原则和标准&#xff0c;旨在确保应用程序提供一致、易用且美观的用户体验。以下是一些关键的APP设计规范。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1.一致性&#xff1a; 保持界面元素和交互行为的一致性…

网络协议——HTTP协议

目录 ​编辑 一&#xff0c;HTTP协议基本认识 二&#xff0c;认识URL 三&#xff0c;http协议的格式 1&#xff0c;发送格式 2&#xff0c;回应格式 四&#xff0c;服务端代码 五&#xff0c;http报文细节 1&#xff0c;Post与Get方法 2&#xff0c;Content_lenth 3&…

vscode教程

个人笔记&#xff08;整理不易&#xff0c;有帮助点个赞&#xff09; 笔记目录&#xff1a;学习笔记目录_pytest和unittest、airtest_weixin_42717928的博客-CSDN博客 个人随笔&#xff1a;工作总结随笔_8、以前工作中都接触过哪些类型的测试文档-CSDN博客 目录 一&#xff1a…

zheng项目:从零到一打造全方位J2EE企业级开发解决方案

zheng项目&#xff1a;从零到一打造全方位J2EE企业级开发解决方案 摘要&#xff1a; 在当今快速发展的企业级应用开发领域&#xff0c;一套高效、稳定且可扩展的解决方案对于企业的成功至关重要。zheng项目旨在提供一套全面的J2EE企业级开发解决方案&#xff0c;从前端模板到自…

Leetcode 581. 最短无序连续子数组

心路历程&#xff1a; 本以为这道题要用动态规划求解&#xff0c;因为题目中这几个关键字与动态规划太匹配了&#xff0c;结果想了半天也没发现dp(i)和dp(i-1)的递推关系。 这道题本意考察双指针的做法&#xff0c;也可以用排序后做比较的方式来做。 注意的点&#xff1a; 1…

性能优化 - 你能说一说,为什么做了骨架屏,FCP的指标还是没有提升吗

难度级别:中高级及以上 提问概率:80% FCP的全程是First Contentful Paint,是衡量网页性能的一个重要指标,很多人把FCP理解为元素内容首次渲染到浏览器上的时间。但由于现在比较流行的Vue或是React项目中,HTML文档最初只有一个id为app的DIV…

React18从入门到实战

文章目录 一、React环境的搭建二、项目文件的介绍&#xff08;1&#xff09;package.json&#xff0c;他是项目存放依赖包的地方&#xff0c;里面包括了一些项目核心包及下载的其他插件包&#xff08;2&#xff09;src文件夹是项目源码目录&#xff0c;平时开发页面就在其中&am…

Linux从入门到精通 --- 4(上).快捷键、软件安装、systemctl、软链接、日期和时区、IP地址

文章目录 第四章(上)&#xff1a;4.1 快捷键4.1.1 ctrl c 强制停止4.1.2 ctrl d 退出4.1.3 history4.1.4 历史命令搜索4.1.5 光速移动快捷键4.1.6 清屏 4.2 软件安装4.2.1 yum4.2.2 apt 4.3 systemctl4.4 软链接4.4.1 ln 4.5 日期和时区4.5.1 date命令4.5.2 date进行日期加减…

如何魔改 diffusers 中的 pipelines

如何魔改 diffusers 中的 pipelines 整个 Stable Diffusion 及其 pipeline 长得就很适合 hack 的样子。不管是通过简单地调整采样过程中的一些参数&#xff0c;还是直接魔改 pipeline 内部甚至 UNet 内部的 Attention&#xff0c;都可以实现很多有趣的功能或采样生图结果。 本…

从0到1搭建文档库——sphinx + git + read the docs

sphinx git read the docs 目录 一、sphinx 1 sphinx的安装 2 本地构建文件框架 1&#xff09;创建基本框架&#xff08;生成index.rst &#xff1b;conf.py&#xff09; conf.py默认内容 index.rst默认内容 2&#xff09;生成页面&#xff08;Windows系统下&#xf…

项目:自主实现Boost搜索引擎

文章目录 写在前面开源仓库和项目上线其他文档说明 项目背景项目的宏观原理技术栈与环境搜索引擎原理正排索引倒排索引 去标签和数据清洗模块html文件名路径保存函数html数据解析函数文件写入函数 建立索引模块检索和读取信息建立索引建立正排索引建立倒排索引jieba工具的使用倒…

基于FPGA的按键消抖

按键工作原理 当KEY1按下时&#xff0c;整条电路就会导通&#xff0c;这个时候KEY1就是低电平&#xff1b; 当KEY1松开时&#xff0c;整条电路就会断开&#xff0c;这个时候KEY1就是高定平&#xff1b; 我们可以通过判断KEY1的高低电平来判断按键是否被按下。 为什么按键消…

关于ansible的模块 ③

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 接《关于Ansible的模块①》和《关于Ansible的模块②》&#xff0c;继续学习ansible的user模块。 user模块可以增、删、改linux远…

idea 开发serlvet汽车租赁管理系统idea开发sqlserver数据库web结构计算机java编程layUI框架开发

一、源码特点 idea开发 java servlet 汽车租赁管理系统是一套完善的web设计系统sqlserver数据库 系统采用serlvetdaobean mvc 模式开发&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 java se…

移动平台相关(安卓)

目录 安卓开发 Unity打包安卓 ​编辑​编辑 BuildSettings PlayerSettings OtherSettings 身份证明 配置 脚本编译 优化 PublishingSettings 调试 ReMote Android Logcat AndroidStudio的调试 Java语法 ​编辑​编辑​编辑 变量 运算符 ​编辑​编辑​编辑​…

猫咪也爱美食!这款猫粮让你的猫主子欲罢不能!

&#x1f44b; 亲爱的宠友们&#xff0c;最近我也在为家里的猫主子换猫粮的事情犯愁呢&#xff01;我知道&#xff0c;给猫咪选择一款合适的猫粮真的是个挺重要的事情。我家猫咪现在吃的是福派斯牛肉高脂猫粮&#xff0c;感觉还不错。 &#x1f431; 首先说说我家猫咪的情况吧。…

Java方法引用

方法引用概述 把已经有的东西拿过来用,当做函数式接口中的抽象方法的方法体 import java.util.*;public class test {public static void main(String[] args) {//需求:创建一个数组,进行倒序排列Integer[] arr {3,5,4,1,6,2};//匿名内部类Arrays.sort(arr, new Comparator&l…

某虚假交友APP(信息窃取)逆向分析

应用初探 在群里水群的时候 群u发了一个交友APP 于是拿来分析一下 可以看到应用打开后又一个登录的界面 需要用户输入手机号与验证码进行登录 #在线云沙箱分析 将APK放入某安信云沙箱中分析 提示应用请求了过多的敏感权限 逆向分析 直接拖入Jadx分析 好在程序没有加固 也没…

Vue 有哪些主要的指令修饰符

目录 1. 什么是指令修饰符 2. 指令修饰符有哪些 2.1. 按键修饰符 2.2. v-model修饰符 2.3. 事件修饰符 1. 什么是指令修饰符 通过 "." 指明一些指令 后缀&#xff0c;不同 后缀 封装了不同的处理操作 目的&#xff1a;简化代码 2. 指令修饰符有哪些 2.1. 按键…