堆排序、快速排序和归并排序

堆排序、快速排序和归并排序是所有排序中最重要的三个排序,也是难度最大的三个排序;所以本文单独拿这三个排序来讲解

目录

一、堆排序 

1.建堆

2.堆排序

二、快速排序

1.思想解析

2.Hoare版找基准

3.挖坑法找基准

4.快速排序的优化

5.快速排序非递归

三、归并排序

1.归并思路

2.代码展示及步骤

3.代码分析

 4.归并排序非递归

 5.使用归并排序解决海量数据


一、堆排序 

所谓堆排序,就是在堆的基础上进行排序,那么如何建堆,就是堆排序的重点。

堆排序核心思想:

1.建堆:排升序建大根堆,排降序建小根堆

2.在建好堆后,每次堆顶元素和最后一个位置的元素交换

3.每交换一次,就进行向下调整操作,使其满足堆的性质

4.交换后,最后一个位置向前走一步

1.建堆

堆有两种,大根堆和小根堆。

(1)排升序,建立大根堆

(2)排降序,建立小根堆

再利用大根堆或者小根堆进行排序

(1)建堆

我们建堆使用向下调整的方式,从最后一棵子树开始。每一棵子树都需要进行向下调整。

向下调整创建大根堆

核心:先将一维数组以层序遍历的方式创建成一棵完全二叉树,然后从最后一棵子树(最后一个父亲结点)开始,向下调整(从父亲到最后一个孩子)。每一轮结束,父亲结点减一。

回忆父亲结点和孩子结点的关系:假设知道父亲结点的下标为i,则左孩子的下标为:(2*i)-1;如果知道孩子结点的下标(左右都可)为i,则父亲结点为:(i-1)/2。

通过图示理解大根堆的向下调整创建:

有该数组:{27,15,19,18,28,34,65,49,25,37}

得到一棵逻辑上的完全二叉树:

然后从最后一棵子树开始向下调整:

调整的步骤:

(1)左右孩子比较,找出孩子大的下标(2)大孩子与父亲结点比较,大于父亲结点则交换(3)父亲结点下标走到孩子位置,孩子再等于新的下标

下面是调整的过程:

第一轮:调整最后一棵子树,p的开始位置为4

c=2*9+1=18,上面标错了 

第二轮:p--,走到3的位置

c=2*7+1=15,上面标错了  

第三轮:p--,走到2的位置

c=2*6+1=13,上面标错了  

第四轮:p--,走到1的位置

 

第五轮:p--,走到0的位置

上面就是堆调整的全过程,我们总结一下:

p代表需要调整的子树,每调整完一轮,p--,直到p调整完;而在每一轮的调整中,交换完一小轮,p就要向下走,c也要继续往下走,直到不满足条件。

下面是建立大根堆的代码:

public void create(int[] arr) {//从最后一棵子树向下调整for (int parent = (arr.length -1-1)/2; parent >= 0 ; parent--) {siftDown(arr,parent,arr.length);}}/***  向下调整*/
public void siftDown(int[] arr,int parent,int len) {int child = parent*2 + 1;while(child < len) {//1.找左右孩子最大的if(child+1 < len && arr[child+1] > arr[child]) {child = child + 1;}if(arr[child] > arr[parent]) {swap(arr,child,parent);parent = child;child = parent*2 + 1;}else {break;}}
}
public void swap(int[] arr,int i,int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;}

下面是建立小根堆的代码:

 public void createminHeap() {for (int parent = (usedSize-1)/2; parent >= 0 ; parent--) {siftDown2(parent,usedSize);}}private void siftDown2(int parent,int end) {int child = parent*2+1;//找到左孩子下标//循环结束,说明一棵子树调整完成while (child < end) {//1.找左右孩子最小的一个.进入if说明第二个孩子比较大if(child+1 < usedSize && elem[child] > elem[child+1]) {child++;}//2.比较父亲结点和大孩子结点if(elem[parent] > elem[child]) {swap(parent,child);parent = child;child = parent*2+1;//找到左孩子下标}else {break;//满足则结束循环}}}
public void swap(int[] arr,int i,int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;}

2.堆排序

在学会如何建堆之后,才能进行堆排序操作,堆排序操作是比较简单的

思想:每次堆顶元素和最后一个元素交换,交换完进行一次向下调整,每次过后最后一个元素向前走

 大根堆可以保证堆顶元素是最大的,每次和最后一个位置交换;不断交换,就会形成升序。

 堆排序部分代码:

public void headSort(int[] arr) {//1.排升序,建大根堆create(arr);int end = arr.length - 1;while(end > 0) {//2.每次交换堆顶和最后一个元素swap(arr,0,end);//3.交换完调整siftDown(arr,0,end);end--;}}

堆排序完整代码:

public class heapSort {public void headSort(int[] arr) {//1.排升序,建大根堆create(arr);int end = arr.length - 1;while(end > 0) {//2.每次交换堆顶和最后一个元素swap(arr,0,end);//3.交换完调整siftDown(arr,0,end);end--;}}/*** 交换*/public void swap(int[] arr,int i,int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;}public void create(int[] arr) {//从最后一棵子树向下调整for (int parent = (arr.length -1-1)/2; parent >= 0 ; parent--) {siftDown(arr,parent,arr.length);}}/***  向下调整*/public void siftDown(int[] arr,int parent,int len) {int child = parent*2 + 1;while(child < len) {//1.找左右孩子最大的if(child+1 < len && arr[child+1] > arr[child]) {child = child + 1;}if(arr[child] > arr[parent]) {swap(arr,child,parent);parent = child;child = parent*2 + 1;}else {break;}}}}

总结:

(1)时间复杂度:O(n*logn)

(2)空间复杂度:O(1)

(3)稳定性:不稳定

二、快速排序

快速排序是一种基于二叉树形式的交换数据排序,快听名字就知道他很快

下面介绍的快速排序,我们会介绍:快排的思想、Hoare版分割法、挖坑法分割法、如何优化快速排序快速排序的非递归写法

1.思想解析

(1)官方概念

在待排序的 N个记录中任意取一个记录,把该 记录放在最终位置后, 数据序列被此记录分成两部分。 (如何分成两个部分:所有关键字比该记录关键 字小的放在前一部分, 所有比它大的放置在后一部分, 并把该记录排在这两部分 的中间,这个过程称作一次快速排序)之后重复上述过程, 直到每一部分内只有 一个记录为止。

(2)简述概念

(1)每次找一个基准,再定义两个指针(分别指向数据序列的两头),遍历该数据序列。(2)遍历时,满足一定的条件,就交换指针所指向的值或者其他操作,直到两个指针相遇。

(3)指针相遇的位置就将该序列分成左右两部分

(4)左右两个部分再重复(1)-(3)的操作,直到不能再分解,排序完成

我们这里暂时称(1)(2)两个步骤为:寻找基准法 

上面的都是干巴巴的概念,很难理解,下面结合图解。

(3)图解如何完成排序

下面第一步到找到下一个基准的过程称为:找基准法(官方概念为Hoare版);找基准法还有另一种称为:挖坑法

使用Hoare找基本法的每一轮:

(4)部分代码

根据上述的图解,我们得出,每次结束,就会将数组分成两个部分,然后每个部分再重复步骤,可以想到使用递归的方式完成。

partition2方法就是找基准法,具体实现下面介绍

private static void quick(int[] array,int start,int end) {if(start >= end) {return;}int pivot = partition2(array,start,end);//记录每一轮基准的位置//递归左边quick(array,start,pivot-1);//递归右边quick(array,pivot+1,end);}

找基准法一共有三种:Hoare版、挖坑法、前后指针法,其中最常用的是:挖坑法;不常见的是:前后指针法

2.Hoare版找基准

根据Hoare版的思想完成的代码,具体思想不再重复

private static int partition1(int[] array,int left,int right) {//1.确定基准int tmp = array[left];int l = left;//2.遍历数组,直到相遇while (left < right) {while (left < right && array[right] >= tmp) {right--;}while (left < right && array[left] <= tmp) {left++;}//交换两个值swap(array,left,right);}//3.交换相遇位置和基本位置的值swap(array,l,right);//4.返回相遇位置,作为下一次的分割点return right;
}
private static void swap(int[] array,int i,int j) {int tmp = array[i];array[i] = array[j];array[j] = tmp;}

第一种快速排序完整代码:

public static void quick(int[] array,int start,int end) {if(start >= end) {return;}int pivot = partition(array,start,end);//记录每一轮基准的位置//递归左边quick(array,start,pivot-1);//递归右边quick(array,pivot+1,end);
}
private static int partition(int[] array,int left,int right) {//1.确定基准int tmp = array[left];int l = left;//2.遍历数组,直到相遇while (left < right) {while (left < right && array[right] >= tmp) {right--;}while (left < right && array[left] <= tmp) {left++;}//交换两个值swap(array,left,right);}//3.交换相遇位置和基本位置的值swap(array,l,right);//4.返回相遇位置,作为下一次的分割点return right;
}
private static void swap(int[] array,int i,int j) {int tmp = array[i];array[i] = array[j];array[j] = tmp;
}

3.挖坑法找基准

挖坑法是最常用的一种,具体实现和Hoare版很相似。

(1)挖坑法思想

每一轮的结果:

(2)挖坑法代码

private static int partition(int[] array,int left,int right) {//1.将基准存入临时变量中,形成一个坑int tmp = array[left];//2.遍历数组,直到相遇while (left < right) {while (left < right && array[right] >= tmp) {right--;}//3.放入left位置array[left] = array[right];while (left < right && array[left] <= tmp) {left++;}//4.放入right位置array[right] = array[left];}//5.补坑array[right] = tmp;return right;
}
private static void swap(int[] array,int i,int j) {int tmp = array[i];array[i] = array[j];array[j] = tmp;}

第二种快速排序完整代码:

public static void quick(int[] array,int start,int end) {if(start >= end) {return;}int pivot = partition(array,start,end);//记录每一轮基准的位置//递归左边quick(array,start,pivot-1);//递归右边quick(array,pivot+1,end);
}
private static int partition(int[] array,int left,int right) {//1.将基准存入临时变量中,形成一个坑int tmp = array[left];//2.遍历数组,直到相遇while (left < right) {while (left < right && array[right] >= tmp) {right--;}//3.放入left位置array[left] = array[right];while (left < right && array[left] <= tmp) {left++;}//4.放入right位置array[right] = array[left];}//5.补坑array[right] = tmp;return right;
}
private static void swap(int[] array,int i,int j) {int tmp = array[i];array[i] = array[j];array[j] = tmp;}

4.快速排序的优化

对于不同的数据,死板的写法不一定有其他排序快;但是根据不同的场景进行不同的优化,可以大大提高快速排序的效率。

下面介绍两种常见的优化方式 

(1)三数取中法

思想:在一堆序列中,找到最左边的数、中间的数和最右边的数中 中间小的一个数与最左边的数进行交换,再以最左边的数为基准进行划分。可以防止升序或者逆序而造成的单分支情况。

应对情况:常见与已有序的序列(顺序或者逆序) 

核心:找到三个数中排在中间的数

举例:下面为顺序的序列,当默认选择第一个数为基准时,右边都是比基准大的而不会交换;在递归过程中,就会形成单分支的二叉树,复杂度进而变大

 三数取中,取的数为:

 如何找中间值:

代码如下:

private static void quick(int[] array,int start,int end) {if(start >= end) {return;}//三数取中法(优化)int index = findMid(array,start,end);swap(array,index,start);int pivot = partition(array,start,end);//递归左边quick(array,start,pivot-1);//递归右边quick(array,pivot+1,end);}
/*找中间大的元素下标*/private static int findMid(int[] array,int left,int right) {int mid = left+( (right - left) >> 1 );if(array[left] > array[right]) {if(array[left] < array[mid]) {return left;}else if(array[right] > array[mid]) {return right;}else  {return mid;}}else {if(array[mid] > array[right]) {return right;}else if(array[left] > array[mid]) {return left;}else {return mid;}}}

(2)递归到小区间时,使用插入排序

思想:结合插入排序

应对情况:一棵二叉树的结点,一般2/3都集中在最后面的几层;如果一直递归下去,计算量是非常大的。

核心:会使用插入排序

private static void quick(int[] array,int start,int end) {if(start >= end) {return;}//递归到一定的区间,使用插入排序(优化)if((end-start+1) < 5) {insertSort(array,start,end);return;}int pivot = partition2(array,start,end);//递归左边quick(array,start,pivot-1);//递归右边quick(array,pivot+1,end);}/*** 区间插入排序*/private static void insertSort(int[] array,int start,int end) {for (int i = start+1; i <= end; i++) {int tmp = array[i];int j = i-1;for (; j >= start ; j--) {if(array[j] > tmp) {array[j+1] = array[j];}else {break;}}array[j+1] = tmp;}}

总结:针对递归法

(1)时间复杂度:O(N*logN)

(2)空间复杂度:O(logN)

(3)稳定性:不稳定

(4)使用场景:数据较乱,不适合趋于有序或者已有序的

5.快速排序非递归

思想:

(1)先找一次基准(借助挖坑法)

(2)(判断:如果基准的左边:pivot-1>left不成立,则不放入栈;右边:pivot+1>right不成立也不放入)放入基准左边头跟尾的两个元素,再放入基准右边的头跟尾两个元素。

(3)栈不为空,取fenbuie除两个元素,分别赋值给右跟左

(4)以左跟右的区间继续找基准

(5)重复(2)-(4)

图解:

判断是否入栈:

代码: 

public static void quickSortNor(int[] array) {Stack<Integer> stack = new Stack<>();int left = 0;int right = array.length - 1;//1.找第一次基准int pivot = partition(array,left,right);//2.将基准左右两边存入栈中if(pivot-1>left) {stack.push(left);stack.push(pivot-1);}if(pivot+1<right) {stack.push(pivot+1);stack.push(right);}//3.栈不为空出栈while (!stack.empty()) {right = stack.pop();left = stack.pop();pivot = partition2(array,left,right);//2.将基准左右两边存入栈中if(pivot-1>left) {stack.push(left);stack.push(pivot-1);}if(pivot+1<right) {stack.push(pivot+1);stack.push(right);}}}
private static int partition(int[] array,int left,int right) {//1.将基准存入临时变量中,形成一个坑int tmp = array[left];//2.遍历数组,直到相遇while (left < right) {while (left < right && array[right] >= tmp) {right--;}//3.放入left位置array[left] = array[right];while (left < right && array[left] <= tmp) {left++;}//4.放入right位置array[right] = array[left];}//5.补坑array[right] = tmp;return right;
}
private static void swap(int[] array,int i,int j) {int tmp = array[i];array[i] = array[j];array[j] = tmp;}

时间复杂度:nlogn

空间复杂度:longn

稳定性:不稳定

三、归并排序

下面的归并排序,我们注重介绍:归并排序的思路(包括文字思路,图片思路)、归并排序的代码代码执行过程的讲解

归并排序和快速排序类型,使用了分治的思想,其中,也需要用到递归的。而归并排序的核心就是一分为二,再合并。

1.归并思路

(1)大致思想

(1)将数据不断平分为两段,直到不能再分解,形成一个单独的有序个体。

(2)然后在往回走的过程中,不断合并做到有序。

(2)具体图解:

分解思路:

并起思路:在合并的过程中就完成了排序

(3)如何分解:加入一点数学知识

下面是所有片段的left,mid和right位置

根据这里得出一个规律:当left>=right时,分解停止。

2.代码展示及步骤

代码中的注释很全面的介绍了每个步骤

(1)代码展示

public class mergeSort {public static void mergeS(int[] array){mergeF(array,0,array.length-1);}//一.分解的方法(归)private static void mergeF(int[] array,int left,int right){//1.停止分解的条件if(left>=right) {return;}//2.记录拆分的位置int mid = left + ((right - left)>>1);//3.拆成的左半部分(使用递归)mergeF(array,left,mid);//4.拆成的右半部分(使用递归)mergeF(array,mid+1,right);//5.开始合并merge(array,left,mid,right);}//二.合并的方法(并)private static void merge(int[] array,int left,int mid,int right) {int[] arrTmp = new int[right-left+1];int s1 = left;//遍历第一个子序列int e1 = mid; //记录第一个子序列末端位置int s2 = mid+1;//遍历第二个子序列int e2 = right;//记录第二个子序列末端位置int k = 0;//遍历临时数组//1.将其中一个序列的数据全部拷入临时数组中while (s1<=e1 && s2<=e2) {if(array[s1] > array[s2]) {arrTmp[k++] = array[s2++];}else {arrTmp[k++] = array[s1++];}}//2.将未拷完的子序列继续拷入while (s1<=e1) {arrTmp[k++] = array[s1++];}while (s2<=e2) {arrTmp[k++] = array[s2++];}//3.将排序好的临时数组中的数据拷回原数组for (int i = 0; i < arrTmp.length; i++) {array[i+left] = arrTmp[i];}}
}

(2)数据拷贝回原数组部分

//3.将排序好的临时数组中的数据拷回原数组for (int i = 0; i < arrTmp.length; i++) {array[i+left] = arrTmp[i];}

3.代码分析

上面对代码的简述也只是一种简单的概述,下面详细介绍一下代码的执行过程,包括是如何递归,及如何并。

部分过程:

类似二叉树的递归,当递归到一定条件时,才会开始合并;并不是和前面的图片一样,要全部分解完才开始逐一递归。

部分合并过程:

总结:

(1)时间复杂度:O(N*logN)

(2)空间复杂度:O(logn)

(3)稳定性:稳定

(4)使用场景:在磁盘中的外排序问题

 4.归并排序非递归

引入:非递归的归并排序,重点也在后面的合并方法上;递归法是先递归到最小单元才开始合并,而非递归是直接开始合并。

步骤分析:

(1)定义一个变量gap,用来标识最小的有序集;每次合并两个有序集,当gap>=数组长度一半时,代表排序完成。

(2)定义一个变量i,用来遍历数组中所有的有序集,每次跳过两个有序集

(3)我们只需要根据合并的方法拿到对应的下标即可

思路解析:非递归,我们使用直接从最小单元开始合并,也就是以一个数据作为最小单位。


需要拿到的下标:这是根据上面递归法写的合并方法,这里通用。

根据上面得到以下代码:

public static void mergeSortNor(int[] array) {int gap = 1;//一组的数据个数//循环一次,完成一轮合并while (gap < array.length) {//每循环一次,代表合并两个组for (int i = 0; i < array.length; i = i+2*gap) {int left = i;int mid = left+gap-1;//两个if,防止数组越界if(mid >= array.length) {mid = array.length-1;}int right = mid+gap;if(right >= array.length) {right = array.length-1;}merge(array,left,mid,right);}gap = gap*2;}}

完整非递归代码:

public static void mergeSortNor(int[] array) {int gap = 1;//一组的数据个数//循环一次,完成一轮合并while (gap < array.length) {//每循环一次,代表合并两个组for (int i = 0; i < array.length; i = i+2*gap) {int left = i;int mid = left+gap-1;//两个if,防止数组越界if(mid >= array.length) {mid = array.length-1;}int right = mid+gap;if(right >= array.length) {right = array.length-1;}merge(array,left,mid,right);}gap = gap*2;}}
private static void merge(int[] array,int left,int mid,int right) {int[] arrTmp = new int[right-left+1];int s1 = left;int e1 = mid;int s2 = mid+1;int e2 = right;int k = 0;while (s1<=e1 && s2<=e2) {if(array[s1] > array[s2]) {arrTmp[k++] = array[s2++];}else {arrTmp[k++] = array[s1++];}}while (s1<=e1) {arrTmp[k++] = array[s1++];}while (s2<=e2) {arrTmp[k++] = array[s2++];}for (int i = 0; i < arrTmp.length; i++) {array[i+left] = arrTmp[i];}}

 5.使用归并排序解决海量数据

当所排序的对象巨大时,需要使用外部排序,而归并排序就是外部排序的一种典型代表。

外部排序:排序过程需要在磁盘等外部存储进行的排序(不直接借助内存)

使用场景:内存只有1G,而排序的数据却有100G

所以我们需要使用归并排序去完成:

(1)先把文件切分成 200 份,每个 512 M
(2)分别对 512 M 排序,因为内存已经可以放的下,所以任意排序方式都可以
(3)进行 2路归并,同时对 200 份有序文件做归并过程,最终结果就有序了(这个时候不借助内存)

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

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

相关文章

C语言--左旋字符/右旋字符实现及其判断

1.题目解释 左旋就是把对应的左边的放到右边 例如ABCDEF左旋2个字符就是BCDEFAB&#xff0c;左旋3个字符就是DEFABC&#xff1b; 2.代码实现 void leftmove(char* str, int k) {int j 0;assert(str);for (j 0; j < k; j){char temp *str;int len strlen(str);int i …

单个文件实现cpu的信息检测:ruapu.h的学习笔记

https://github.com/nihui/ruapu是nihui大佬开发的用单文件检测CPU特性的项目 ruapu.h的使用 "ruapu.h"主要提供了两个函数 ruapu_init 和 ruapu_supports&#xff0c;分别用于初始化和检测指令集支持。 // 使用示例见&#xff1a;https://github1s.com/nihui/rua…

MyBatis核心配置文件

1、properties属性&#xff1a; 将变量提取出来变成全局变量 enable-default-value&#xff1a;启动默认值 数据库环境四要素 2、settings属性 &#xff1a; 开启二级缓存&#xff0c;开启延迟加载懒加载 消极懒加载积极懒加载 <setting name"cacheEnable" valu…

Spring数据脱敏实现

在当今的数字化时代&#xff0c;数据安全和个人隐私保护变得日益重要。为了遵守各种数据保护法规&#xff0c;如欧盟的GDPR&#xff08;通用数据保护条例&#xff09;&#xff0c;企业在处理敏感信息时需要格外小心。数据脱敏是一种常见的技术手段&#xff0c;用于隐藏敏感数据…

Servlet使用Cookie和Session

一、会话技术 当用户访问web应用时&#xff0c;在许多情况下&#xff0c;web服务器必须能够跟踪用户的状态。比如许多用户在购物网站上购物&#xff0c;Web服务器为每个用户配置了虚拟的购物车。当某个用户请求将一件商品放入购物车时&#xff0c;web服务器必须根据发出请求的…

windows实现ip1:port1转发至ip2:port2教程

第一步&#xff1a;创建虚拟网卡(如果ip1为本机127.0.0.1跳过此步骤)&#xff0c;虚拟网卡的IPV4属性设置ip1 1> 创建虚拟网卡参见 Windows系统如何添加虚拟网卡&#xff08;环回网络适配器&#xff09;_windows添加虚拟网卡-CSDN博客​​​​​​ 2> 设置虚拟网卡使用…

MongoDB索引解析:工作原理、类型选择及优化策略

MongoDB&#xff0c;作为一款广受欢迎的NoSQL数据库&#xff0c;以其灵活的数据模型和出色的性能赢得了开发者的青睐。然而&#xff0c;随着数据量的不断增长和查询需求的日益复杂&#xff0c;如何确保高效的查询性能成为了关键。这时&#xff0c;索引的重要性便凸显出来。本文…

【Go的函数】

函数 函数的引入函数细节祥讲包的引入包的细节详讲init函数匿名函数闭包defer关键字系统函数字符串相关函数日期和时间相关函数内置函数 函数的引入 【1】为什么要使用函数&#xff1a; 提高代码的复用&#xff0c;减少代码的冗余&#xff0c;代码的维护性也提高了 【2】函数…

简单mock server模拟用户请求给小程序提供数据

整理小程序代码时发现一此小程序离开了mock-server基本上没有办法显示了,因此用node,express来满足给小程序提供演示数据的功能 const express require(express); const { createCanvas, Image } require(canvas); const fs require(fs); const path require(path);…

Python爬虫实战:图片爬取与保存

引言&#xff1a; 在本文中&#xff0c;我们将学习如何使用Python创建一个简单的图片爬虫。 我们将利用requests库来发送HTTP请求&#xff0c;BeautifulSoup库来解析HTML页面&#xff0c;以及os和shutil库来下载和保存图片。通过这个教程&#xff0c;你将学会如何爬取网…

你要不要搞副业

最近看到了几个网友关于年轻人要不要搞副业的一点讨论&#xff0c;学习到了很多。整理分享如下&#xff1a; plantegg 你要不要搞副业&#xff1f; 最近网上看到很多讨论搞副业和远程工作的&#xff0c;我也说点自己的经验看法 当然这完全是出于个人认知肯定不是完全对的、也…

react + Typescript 中 react有多少内置的类型 分别是什么

react Typescript 中 react有多少内置的类型 分别是什么 React 和 TypeScript 结合使用时&#xff0c;React 提供了一系列的内置类型&#xff08;也称为类型定义或类型别名&#xff09;来帮助你在 TypeScript 中编写类型安全的代码。这些类型定义涵盖了 React 的各个方面&…

day4:对话框与事件

使用qt搭建一个简易的闹钟系统 #include "second.h" #include "ui_second.h"second::second(QWidget *parent) :QWidget(parent),ui(new Ui::second) {ui->setupUi(this);this->setWindowFlag(Qt::FramelessWindowHint);this->setAttribute(Qt::…

面试纪实(一)

类加载机制&#xff0c;解决了什么问题 类加载机制&#xff0c;是在程序运行时&#xff0c;加载字节码文件到内存中使用的过程&#xff0c;由jvm的类加载器完成&#xff0c;包括加载&#xff0c;链接&#xff08;验证&#xff0c;准备&#xff0c;解析&#xff09;&#xff0c…

《高效使用Redis》- 由面试题“Redis是否为单线程”引发的思考

由面试题“Redis是否为单线程”引发的思考 很多人都遇到过这么一道面试题&#xff1a;Redis是单线程还是多线程&#xff1f;这个问题既简单又复杂。说他简单是因为大多数人都知道Redis是单线程&#xff0c;说复杂是因为这个答案其实并不准确。 难道Redis不是单线程&#xff1f…

手把手教你Jenkins整合Jmeter实现自动化接口测试!

01、在机器上安装jmeter 下载&#xff1a;http://jmeter.apache.org/download_jmeter.cgi 这里我用了一台Windows安装jmeter用来写接口测试的脚本&#xff0c;启动前修改jmeter.properties 中 jmeter.save.saveservice.output_format值为xml。 编写接口测试脚本&#xff1a; 脚…

CSS @符规则(@font-face、@keyframes、@media、@scope等)

随着前端开发的不断发展&#xff0c;CSS 的功能日益强大&#xff0c;其中 规则扮演着举足轻重的角色。它们不仅扩展了 CSS 的功能边界&#xff0c;还为开发者提供了更加灵活和高效的样式定义方式&#xff0c;让我们来一同探索这些强大而实用的 规则吧&#xff01; font-face …

在Win11上部署Stable Diffusion WebUI Forge

Stable Diffusion WebUI Forge 是 Stable Diffusion WebUI&#xff08;基于 Gradio&#xff09;之上的平台&#xff0c;可简化开发、优化资源管理并加快推理速度。“Forge”这个名字的灵感来自“Minecraft Forge”。这个项目旨在成为SD WebUI的Forge。 与原始 WebUI&#xff0…

如何成为一个“不掉头发”的程序员?

保持健康的生活方式和良好的工作习惯是成为一个不掉头发的程序员的关键。以下是一些建议&#xff0c;可以帮助您保持健康、减少压力&#xff0c;从而减少掉发的可能性&#xff1a; 合理安排工作和休息时间&#xff1a;保持工作和生活的平衡&#xff0c;避免过度加班和长时间的工…

掌握Pillow:Python图像处理的艺术

掌握Pillow&#xff1a;Python图像处理的艺术 引言Python与图像处理的概述Pillow库基础导入Pillow库基本概念图像的打开、保存和显示 图像操作基础图像的剪裁图像的旋转和缩放色彩转换和滤镜应用文字和图形的绘制 高级图像处理图像的合成与蒙版操作像素级操作与图像增强复杂图形…