当前位置: 首页 > 编程日记 > 正文

图解八大排序算法——我见过的最详细的讲解(转)

一、分类

1.内部排序和外部排序 

  • 内部排序:待排序记录存放在计算机随机存储器中(说简单点,就是内存)进行的排序过程。
  • 外部排序:待排序记录的数量很大,以致于内存不能一次容纳全部记录,所以在排序过程中需要对外存进行访问的排序过程。

2.比较类排序和非比较排序

  • 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序。
  • 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。 

 二、复杂度分析,算法稳定性和适用场景

  • 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
  • 不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。
  • 时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
  • 空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。

三、八大排序算法详解

1.选择排序

1.1 动图演示

1.2 思路分析

1.  第一个跟后面的所有数相比,如果小于(或小于)第一个数的时候,暂存较小数的下标,第一趟结束后,将第一个数,与暂存的那个最小数进行交换,第一个数就是最小(或最大的数)

2.  下标移到第二位,第二个数跟后面的所有数相比,一趟下来,确定第二小(或第二大)的数

重复以上步骤

直到指针移到倒数第二位,确定倒数第二小(或倒数第二大)的数,那么最后一位也就确定了,排序完成。

1.3  负杂度分析

1.  不管原始数组是否有序,时间复杂度都是O(n2

因为没一个数都要与其他数比较一次,(n-1)2次,分解:n2-2n+1,  去掉低次幂和常数,剩下n2,所以最后的时间复杂度是n2

2.  空间复杂度是O(1),因为只定义了两个辅助变量,与n的大小无关,所以空间复杂度为O(1)

 1.4  Java 代码如下:

import java.util.Arrays;
public class Main {public static void main(String[] args) {int[] n = new int[]{1,6,3,8,33,27,66,9,7,88};int temp,index = -1;for (int i = 0; i < n.length-1; i++) {index=i;//如果大于,暂存较小的数的下标for (int j = i+1; j <n.length; j++) {if(n[index]>n[j]){index = j;}}将一趟下来求出的最小数,与这个数交换if(index>0){temp = n[i];n[i] = n[index];n[index] = temp;}System.out.println(Arrays.toString(n));}System.out.println(Arrays.toString(n));}
}

2. 冒泡排序

2.1  动图演示

2.2  思路分析

1.  相邻两个数两两相比,n[i]跟n[j+1]比,如果n[i]>n[j+1],则将连个数进行交换,

2.  j++, 重复以上步骤,第一趟结束后,最大数就会被确定在最后一位,这就是冒泡排序又称大(小)数沉底,

3.  i++,重复以上步骤,直到i=n-1结束,排序完成。

2.3  负杂度分析

1.  不管原始数组是否有序,时间复杂度都是O(n2

因为没一个数都要与其他数比较一次,(n-1)2次,分解:n2+2n-1,  去掉低次幂和常数,剩下n2,所以最后的时间复杂度是n2

2.  空间复杂度是O(1),因为只定义了一个辅助变量,与n的大小无关,所以空间复杂度为O(1)

2.4  选择排序和冒泡排序的比较

1. 时间负责度都是O(n2

2. 空间复杂度都是O(1)

3. 选择排序是从第一位开始确定最大或最小的数,保证前面的数都是有序的,且都比后面的数小或大,

冒泡排序是从最后一位开始确定最大或最小的数,保证后面的数都是有序的且都大于或小于前面的数。

2.5  Java 代码如下

import java.util.Arrays;
public class 冒泡 {public static void main(String[] args) {int[] n = new int[]{1,6,3,8,33,27,66,9,7,88};int temp;for (int i = 0; i < n.length-1; i++) {for (int j = 0; j <n.length-1; j++) {if(n[j]>n[j+1]){temp = n[j];n[j] = n[j+1];n[j+1] = temp;}}}System.out.println(Arrays.toString(n));}
}

3. 直接插入排序

3.1  动图演示

3.2  思路分析

例如从小到大排序:

1.  从第二位开始遍历,

2.  当前数(第一趟是第二位数)与前面的数依次比较,如果前面的数大于当前数,则将这个数放在当前数的位置上,当前数的下标-1

3.  重复以上步骤,直到当前数不大于前面的某一个数为止,这时,将当前数,放到这个位置,

1-3步就是保证当前数的前面的数都是有序的,内层循环的目的就是将当前数插入到前面的有序序列里

4.  重复以上3步,直到遍历到最后一位数,并将最后一位数插入到合适的位置,插入排序结束。

根据思路分析,每一趟的执行流程如下图所示:

3.3  复杂度分析

1.  时间复杂度:插入算法,就是保证前面的序列是有序的,只需要把当前数插入前面的某一个位置即可。

所以如果数组本来就是有序的,则数组的最好情况下时间复杂度为O(n)

如果数组恰好是倒=倒序,比如原始数组是5 4 3 2 1,想要排成从小到大,则每一趟前面的数都要往后移,一共要执行n-1 + n-2 + … + 2 + 1 = n * (n-1) / 2 = 0.5 * n2 - 0.5 * n次,去掉低次幂及系数,所以最坏情况下时间复杂度为O(n2

平均时间复杂度(n+n2 )/2,所以平均时间复杂度为O(n2

2.  空间复杂度:插入排序算法,只需要两个变量暂存当前数,以及下标,与n的大小无关,所以空间复杂度为:O(1)

3.4  Java 代码如下

import java.util.Arrays;
public class insertSort {public static void main(String[] args) {int[] n = new int[]{20,12,15,1,5,49,58,24,578,211,20,214,78,35,125,789,11};int temp = 0,j;for (int i = 1; i < n.length; i++) {temp = n[i];for (j = i; j >0; j--) {//如果当前数前面的数大于当前数,则把前面的数向后移一个位置if(n[j-1]>temp){n[j] = n[j-1];//第一个数已经移到第二个数,将当前数放到第一个位置,这一趟结束if(j==1){n[j-1] = temp;break;}}else{//如果不大于,将当前数放到j的位置,这一趟结束
                n[j] = temp;break;}}System.out.println(Arrays.toString(n));}System.out.println(Arrays.toString(n));}
}

4. 快速排序

4.1  动图演示

4.2  思路分析

快速排序的思想就是,选一个数作为基数(这里我选的是第一个数),大于这个基数的放到右边,小于这个基数的放到左边,等于这个基数的数可以放到左边或右边,看自己习惯,这里我是放到了左边,

一趟结束后,将基数放到中间分隔的位置,第二趟将数组从基数的位置分成两半,分割后的两个的数组继续重复以上步骤,选基数,将小数放在基数左边,将大数放到基数的右边,在分割数组,,,直到数组不能再分为止,排序结束。

例如从小到大排序:

1.  第一趟,第一个数为基数temp,设置两个指针left = 0,right = n.length,

①从right开始与基数temp比较,如果n[right]>基数temp,则right指针向前移一位,继续与基数temp比较,直到不满足n[right]>基数temp

②将n[right]赋给n[left]

③从left开始与基数temp比较,如果n[left]<=基数temp,则left指针向后移一位,继续与基数temp比较,直到不满足n[left]<=基数temp

④将n[left]赋给n[rigth]

⑤重复①-④步,直到left==right结束,将基数temp赋给n[left]

2.  第二趟,将数组从中间分隔,每个数组再进行第1步的操作,然后再将分隔后的数组进行分隔再快排,

3.  递归重复分隔快排,直到数组不能再分,也就是只剩下一个元素的时候,结束递归,排序完成

根据思路分析,第一趟的执行流程如下图所示:

4.3  负杂度分析

1.  时间复杂度:

最坏情况就是每一次取到的元素就是数组中最小/最大的,这种情况其实就是冒泡排序了(每一次都排好一个元素的顺序)

这种情况时间复杂度就好计算了,就是冒泡排序的时间复杂度:T[n] = n * (n-1) = n^2 + n;

最好情况下是O(nlog2n),推导过程如下:

递归算法的时间复杂度公式:T[n] = aT[n/b] + f(n)  )

所以平均时间复杂度为O(nlog2n)

2.  空间复杂度:

快速排序使用的空间是O(1)的,也就是个常数级;而真正消耗空间的就是递归调用了,因为每次递归就要保持一些数据:
最优的情况下空间复杂度为:O(log2n);每一次都平分数组的情况
最差的情况下空间复杂度为:O( n );退化为冒泡排序的情况
所以平均空间复杂度为O(log2n)

 4.4  Java 代码如下

import java.util.Arrays;
public class quick{public static void main(String[] args) {int[] arr = new int[]{10,6,3,8,33,27,66,9,7,88};f(arr,0,arr.length-1);System.out.println(Arrays.toString(arr));}public static void f(int[] arr,int start,int end){//直到start=end时结束递归if(start<end){int left = start;int right = end;int temp = arr[start];while(left<right){//右面的数字大于标准数时,右边的数的位置不变,指针向左移一个位置while(left<right && arr[right]>temp){right--;}//右边的数字小于或等于基本数,将右边的数放到左边arr[left] = arr[right];left++;左边的数字小于或等于标准数时,左边的数的位置不变,指针向右移一个位置while(left<right && arr[left]<=temp){left++;}//左边的数字大于基本数,将左边的数放到右边arr[right] = arr[left];}//一趟循环结束,此时left=right,将基数放到这个重合的位置,arr[left] = temp;System.out.println(Arrays.toString(arr));//将数组从left位置分为两半,继续递归下去进行排序
            f(arr,start,left);f(arr,left+1,end);}}
}

5. 归并排序

5.1  动图演示

5.2  思路分析

归并排序就是递归得将原始数组递归对半分隔,直到不能再分(只剩下一个元素)后,开始从最小的数组向上归并排序

1.  向上归并排序的时候,需要一个暂存数组用来排序,

2.  将待合并的两个数组,从第一位开始比较,小的放到暂存数组,指针向后移,

3.  直到一个数组空,这时,不用判断哪个数组空了,直接将两个数组剩下的元素追加到暂存数组里,

4.  再将暂存数组排序后的元素放到原数组里,两个数组合成一个,这一趟结束。

根据思路分析,每一趟的执行流程如下图所示:

5.3  负杂度分析

1.  时间复杂度:递归算法的时间复杂度公式:T[n] = aT[n/b] + f(n) 

无论原始数组是否是有序的,都要递归分隔并向上归并排序,所以时间复杂度始终是O(nlog2n)

2.  空间复杂度:

  每次两个数组进行归并排序的时候,都会利用一个长度为n的数组作为辅助数组用于保存合并序列,所以空间复杂度为O(n)

 5.4  Java 代码如下

import java.util.Arrays;
public class Main {public static void main(String[] args) {int[] arr = new int[]{3,6,4,7,5,2};merge(arr,0,arr.length-1);System.out.println(Arrays.toString(arr));}//归并public static void merge(int[] arr,int low,int high){int center = (high+low)/2;if(low<high){//递归,直到low==high,也就是数组已不能再分了,
            merge(arr,low,center);merge(arr,center+1,high);//当数组不能再分,开始归并排序
            mergeSort(arr,low,center,high);System.out.println(Arrays.toString(arr));}}//排序public static void mergeSort(int[] arr,int low,int center,int high){//用于暂存排序后的数组的临时数组int[] tempArr = new int[arr.length];int i = low,j = center+1;//临时数组的下标int index = 0;//循环遍历两个数组的数字,将小的插入到临时数组里while(i<=center && j<= high){//左边数组的数小,插入到新数组if(arr[i]<arr[j]){tempArr[index] = arr[i];i++;}else{//右边数组的数小,插入到新数组tempArr[index] = arr[j];j++;}index++;}//处理左半边数组多余的数据,将左半边多余的数据直接追加的临时数组的后面while(i<=center){tempArr[index] = arr[i];i++;index++;}//处理右半边数组多余的数据,将右半边多余的数据直接追加的临时数组的后面while(j<= high){tempArr[index] = arr[j];j++;index++;}//将临时数组中的数据重新放进原数组for (int k = 0; k < index; k++) {arr[k+low] = tempArr[k];}}
}

6. 基数排序

6.1  动图演示

6.2  思路分析

基数排序第i趟将待排数组里的每个数的i位数放到tempj(j=1-10)队列中,然后再从这十个队列中取出数据,重新放到原数组里,直到i大于待排数的最大位数。

1.数组里的数最大位数是n位,就需要排n趟,例如数组里最大的数是3位数,则需要排3趟。

2.若数组里共有m个数,则需要十个长度为m的数组tempj(j=0-9)用来暂存i位上数为j的数,例如,第1趟,各位数为0的会被分配到temp0数组里,各位数为1的会被分配到temp1数组里......

3.分配结束后,再依次从tempj数组中取出数据,遵循先进先进原则,例如对数组{1,11,2,44,4},进行第1趟分配后,temp1={1,11},temp2={2},temp4={44,4},依次取出元素后{1,11,2,44,4},第一趟结束

4.循环到n趟后结束,排序完成

根据思路分析,每一趟的执行流程如下图所示:

通过基数排序对数组{53, 3, 542, 748, 14, 214, 154, 63, 616}:

6.3  负杂度分析

1.  时间复杂度:

每一次关键字的桶分配都需要O(n)的时间复杂度,而且分配之后得到新的关键字序列又需要O(n)的时间复杂度。

假如待排数据可以分为d个关键字,则基数排序的时间复杂度将是O(d*2n) ,当然d要远远小于n,因此基本上还是线性级别的。

系数2可以省略,且无论数组是否有序,都需要从个位排到最大位数,所以时间复杂度始终为O(d*n) 。其中,n是数组长度,d是最大位数。

2.  空间复杂度: 

  基数排序的空间复杂度为O(n+k),其中k为桶的数量,需要分配n个数。

6.4  Java 代码如下

import java.util.Arrays;
public class Main {public static void main(String[] args) {int[] arr = new int[]{10,6,3,8,33,27,66,9,7,88};radixSort(arr);}private static void radixSort(int[] arr) {//求出待排数的最大数int maxLength=0;for (int i = 0; i < arr.length; i++) {if(maxLength<arr[i])maxLength = arr[i];}//根据最大数求最大长度maxLength = (maxLength+"").length();//用于暂存数据的数组int[][] temp = new int[10][arr.length];//用于记录temp数组中每个桶内存的数据的数量int[] counts = new int[10];//用于记录每个数的i位数int num = 0;//用于取的元素需要放的位置int index = 0;//根据最大长度决定排序的次数for (int i = 0,n=1; i < maxLength; i++,n*=10) {for (int j = 0; j < arr.length; j++) {num = arr[j]/n%10;temp[num][counts[num]] = arr[j];counts[num]++;}//从temp中取元素重新放到arr数组中for (int j = 0; j < counts.length; j++) {for (int j2 = 0; j2 < counts[j]; j2++) {arr[index] = temp[j][j2];index++;}counts[j]=0;}index=0;}System.out.println(Arrays.toString(arr));}
}

7. 希尔(shell)排序

7.1  动图演示

7.2  思路分析

希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

  简单插入排序很循规蹈矩,不管数组分布是怎么样的,依然一步一步的对元素进行比较,移动,插入,比如[5,4,3,2,1,0]这种倒序序列,数组末端的0要回到首位置很是费劲,比较和移动元素均需n-1次。

而希尔排序在数组中采用跳跃式分组的策略,通过某个增量将数组元素划分为若干组,然后分组进行插入排序,随后逐步缩小增量,继续按组进行插入排序操作,直至增量为1。希尔排序通过这种策略使得整个数组在初始阶段达到从宏观上看基本有序,小的基本在前,大的基本在后。然后缩小增量,到增量为1时,其实多数情况下只需微调即可,不会涉及过多的数据移动。

来看下希尔排序的基本步骤,在此选择增量gap=length/2,缩小增量继续以gap = gap/2的方式,这种增量选择可以用一个序列来表示,{n/2,(n/2)/2...1},称为增量序列。希尔排序的增量序列的选择与证明是个数学难题,选择的这个增量序列是比较常用的,也是希尔建议的增量,称为希尔增量,但其实这个增量序列不是最优的。此处做示例使用希尔增量。

7.3  复杂度分析

1.  时间复杂度:最坏情况下,每两个数都要比较并交换一次,则最坏情况下的时间复杂度为O(n2, 最好情况下,数组是有序的,不需要交换,只需要比较,则最好情况下的时间复杂度为O(n)。

经大量人研究,希尔排序的平均时间复杂度为O(n1.3(这个我也不知道咋来的,书上和博客上都这样说,也没找到个具体的依据,,,)。

2.  空间复杂度:希尔排序,只需要一个变量用于两数交换,与n的大小无关,所以空间复杂度为:O(1)。

7.4  Java 代码如下

import java.util.Arrays;public class shell {public static void main(String[] args) {int[] arr = new int[]{10,6,3,8,33,27,66,9,7,88};shellSort(arr);System.out.println(Arrays.toString(arr));}private static void shellSort(int[] arr) {int temp;//控制增量序列,增量序列为1的时候为最后一趟for (int i = arr.length/2; i >0; i/=2) {//根据增量序列,找到每组比较序列的最后一个数的位置for (int j = i; j < arr.length; j++) {//根据该比较序列的最后一个数的位置,依次向前执行插入排序for (int k = j-i; k >=0; k-=i) {if(arr[k]>arr[k+i]){temp = arr[k];arr[k]  = arr[k+i];arr[k+i] = temp;}}}}}
}

8. 堆排序

8.1  动图演示

8.2  思路分析

先来了解下堆的相关概念:堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:

同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子

该数组从逻辑上讲就是一个堆结构,我们用简单的公式来描述一下堆的定义就是:

大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]  

小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]  

了解了这些定义。接下来看看堆排序的基本思想及基本步骤:

堆排序基本思想及步骤

堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了

步骤一 构造初始堆。将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆)。

a.假设给定无序序列结构如下

2.此时我们从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点 arr.length/2-1=5/2-1=1,也就是下面的6结点),从左至右,从下至上进行调整。

4.找到第二个非叶节点4,由于[4,9,8]中9元素最大,4和9交换。

这时,交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6。

此时,我们就将一个无需序列构造成了一个大顶堆。

步骤二 将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。

a.将堆顶元素9和末尾元素4进行交换

b.重新调整结构,使其继续满足堆定义

c.再将堆顶元素8与末尾元素5进行交换,得到第二大元素8.

后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序

再简单总结下堆排序的基本思路:

a.将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;

  b.将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;

  c.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。

8.3  复杂度分析

1.  时间复杂度:堆排序是一种选择排序,整体主要由构建初始堆+交换堆顶元素和末尾元素并重建堆两部分组成。其中构建初始堆经推导复杂度为O(n),在交换并重建堆的过程中,需交换n-1次,而重建堆的过程中,根据完全二叉树的性质,[log2(n-1),log2(n-2)...1]逐步递减,近似为nlogn。所以堆排序时间复杂度最好和最坏情况下都是O(nlogn)级。

2.  空间复杂度:堆排序不要任何辅助数组,只需要一个辅助变量,所占空间是常数与n无关,所以空间复杂度为O(1)

8.4  Java 代码如下

import java.util.Arrays;
public class Main {public static void main(String[] args) {int[] arr = new int[]{4,6,8,5,9};//从最后一个非叶节点开始构建大顶堆for (int i = arr.length/2-1; i >=0; i--) {maximumHeap(i,arr);}//从最小的叶子节点开始与根节点进行交换并重新构建大顶堆for (int i = arr.length-1; i >=0; i--) {swap(arr,0,i);maximumHeap(0,arr);}System.out.println(Arrays.toString(arr));}//构建大顶堆public static void maximumHeap(int i,int[] arr){int temp = arr[i];for (int j = i*2+1; j < arr.length; j=j*2+1) {//如果右孩子大于做孩子,则指向右孩子if(j+1<arr.length && arr[j+1]>arr[j]){j++;}//如果最大的孩子大于当前节点,则将大孩子赋给当前节点,修改当前节点为其大孩子节点,再向下走。if(arr[j]>temp){arr[i] = arr[j];i = j;}else{break;}}//将temp放到最终位置arr[i] = temp;}//交换public static void swap(int[] arr,int i,int j){int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}
}

原文链接:https://www.cnblogs.com/l199616j/p/10742603.html

转载于:https://www.cnblogs.com/weiyalin/articles/11287694.html

相关文章:

UE4创建第一人称射击游戏学习教程 Unreal Engine 4: Create Your Own First-Person Shooter

UE4创建第一人称射击游戏学习教程本课程包含38节视频课&#xff0c;将逐步指导您完成以下主题: 云桥网络 平台获取课程&#xff01; 如何创建6种可定制的武器(包括手枪、突击步枪、猎枪、狙击枪、榴弹发射器和火箭发射器) 如何制作基于命中扫描和投射的武器 如何制作第一人…

PS多形式的部分之间复制“笨办法”

PS剪切页面&#xff0c;有时候你可能会遇到这样的情况&#xff1a;设计改进&#xff0c;但是&#xff0c;我们要具有相同的切片。 在此假设&#xff0c;可以直接用于切割片。我们可以节省大量的时间&#xff0c;又分为片。 但是&#xff0c;人们一般不会在你的上跨片设计PSD在变…

Java学习总结:30

线程的生命周期 suspend()方法&#xff1a;暂时挂起线程&#xff1b; resume()方法&#xff1a;恢复挂起的线程&#xff1b; stop()方法&#xff1a;停止线程。 对于以上三个方法不推荐使用&#xff0c;它们已经被慢慢废除掉了&#xff0c;主要原因是这三个方法在使用时容易产…

SVN优化(一) SVN忽略maven项目的target

SVN优化(一) SVN忽略maven项目的target 一 eclipse刚开始导入的项目: 二 解决办法 方式一&#xff1a; 在项目代码路径,如: F:\xyx\sl 鼠标右键,“TortoiseSVN”-- >“Settings” -->"Subversion"-->"Global ignore pattern" 添加:target *.…

“WPF老矣,尚能饭否”—且说说WPF今生未来(上):担心

2019独角兽企业重金招聘Python工程师标准>>> 近日微软公布了最新的WPF路线图&#xff0c;一片热议&#xff1b;对于老牌控件提供商葡萄城来说&#xff0c;这是WPF系列控件一个重要的机遇&#xff0c;因此&#xff0c;Spread Studio for WPF产品做了一次重要更新&…

C语言新手写扫雷攻略3

界面绘制好后&#xff0c;雷数也布置了&#xff0c;接下来就是游戏的运行过程了&#xff0c;今天先不说具体过程&#xff0c;再来看看需要用到的辅助函数 先是简单的画红旗&#xff0c;鼠标右键的功能是画红旗&#xff0c;至此我们都是在使用函数自己绘图&#xff0c;效率是低&…

制作欧比旺·克诺比逼真的CG角色学习教程

艺术站-制作欧比旺克诺比逼真的Cg角色 大小解压后&#xff1a;4.98G 含课程素材文件 1920X1080 .mp4 语言&#xff1a;英语中英文字幕&#xff08;根据原英文字幕机译更准确&#xff09; 课程获取&#xff1a;制作欧比旺克诺比逼真的CG角色学习教程 信息: 欧比旺是我一直以来…

Java学习总结:31(StringBuffer类)

StringBuffer类 在Java中String类不适合使用于频繁修改字符串的操作上(因为其字符串常量一旦声明则不可改变&#xff0c;只能改变字符串对象&#xff0c;改变的是其内存地址的指向)&#xff0c;所以我们可以使用StringBuffer类方便用户进行内容的修改。 例&#xff1a;观察Str…

c语言常用数据类型转换整理

你要发送原始数据流 还是 格式化输出&#xff1f; 如果是格式化 按原子说的 &#xff0c;用sprintf / printf; 如果发送原始内存数据流&#xff0c; 可按下面发送&#xff0c; 发送 #define BYTE0(pointer) (*((char*)(&pointer)0)); #define BYTE1(pointer) (*((char…

2022-2028年中国老年旅游市场深度调研及开发战略研究报告

【报告类型】产业研究 【报告价格】4500起 【出版时间】即时更新&#xff08;交付时间约3个工作日&#xff09; 【发布机构】智研瞻产业研究院 【报告格式】PDF版 本报告介绍了中国老年旅游行业市场行业相关概述、中国老年旅游行业市场行业运行环境、分析了中国老年旅游行…

Andriod使用webview控件往APP里内嵌网页

1.布局文件片段&#xff1a;res-layout <WebView android:id"id/Toweb" android:layout_width"fill_parent" android:layout_height"fill_parent" /> 2.Java片段&#xff1a;src Overrideprotected void onCreate(Bundle savedInstance…

垃圾回收器ZGC应用分析总结

ZGC 是一款低延迟、高吞吐的垃圾回收器,由 Oracle 公司开发。它适用于大型、多核、内存容量较大的应用程序。ZGC 的设计目标是在最大限度地减少停顿时间的同时,为大型内存提供可伸缩性,并为生产部署提供高吞吐量和稳定性。它的目标是以不到 10 毫秒的暂停时间来控制 100MB 到 4TB 的内存。此外,ZGC 还致力于避免全局 JVM 暂停,从而提高系统的可用性。简单来说,它的设计目标是在不超过 10 毫秒的暂停时间内,尽可能地回收大量的堆内存。低延迟:ZGC 的主要目标是最小化 GC 暂停时间。

14个Java并发容器,你用过几个?

不考虑多线程并发的情况下,容器类一般使用ArrayList、HashMap等线程不安全的类,效率更高。在并发场景下,常会用到ConcurrentHashMap、ArrayBlockingQueue等线程安全的容器类,虽然牺牲了一些效率,但却得到了安全。上面提到的线程安全容器都在java.util.concurrent包下,这个包下并发容器不少,今天全部翻出来鼓捣一下。仅做简单介绍,后续再分别深入探索。ConcurrentHashMap:并发版HashMap。

MySQL主从复制(基于binlog日志方式)

主从复制,是用来建立一个和主数据库完全一样的数据库环境,称为从数据库;主数据库一般是准实时的业务数据库。主从复制的作用1.做数据的热备,作为后备数据库,主数据库服务器故障后,可切换到从数据库继续工作,避免数据丢失。2.架构的扩展。业务量越来越大,I/O访问频率过高,单机无法满足,此时做多库的存储,降低磁盘I/O访问的频率,提高单个机器的I/O性能。3.读写分离,使数据库能支撑更大的并发。a.从服务器可以执行查询工作(就是我们常说的读功能),降低主服务器压力;(主库写,从库读,降压)

RabbitMQ安装

windows上安装过程&#xff1a;https://blog.csdn.net/linsongbin1/article/details/80170567运行中给新建用户赋权限&#xff0c;如果界面创完用户忘给权限了可以用这个方法&#xff1a;https://blog.csdn.net/hello_world_php/article/details/83859767 备注&#xff1a;我的…

Complete C# Unity Game Developer 2D

你会学到什么 从头开始学习C#&#xff0c;一门强大的现代语言。没有编程经验是必要的。 在使用Unity游戏引擎方面变得优秀。 为游戏设计和游戏开发打下坚实的基础&#xff0c;这将有助于您构建自己的游戏。 了解面向对象编程在实践中如何工作。 创建可玩的游戏项目——对你的投…

玩转android自定义控件二——自定义索引栏listview

带索引栏的listview&#xff0c;在android开发非常普遍&#xff0c;方便用户进行字母索引&#xff0c;就像微信通讯录这样&#xff1a; 今天&#xff0c;我们就从零到一实现这个具有索引栏的listview. 怎么实现这个控件了&#xff0c;我们应当梳理出一个思路。 ①首先应当将字母…

172. 阶乘后的零

给定一个整数 n&#xff0c;返回 n! 结果尾数中零的数量。 示例 1: 输入: 3 输出: 0 解释: 3! 6, 尾数中没有零。示例 2: 输入: 5 输出: 1 解释: 5! 120, 尾数中有 1 个零.说明: 你算法的时间复杂度应为 O(log n) 。 话不多说&#xff0c;先上代码&#xff1a; class So…

Mysql函数group_concat、find_in_set 多值分隔字符字段进行数据库字段值翻译

Mysql函数group_concat、find_in_set进行数据库字段值翻译 场景 配方表&#xff1a;记录包含的原料 sources表示原料&#xff0c;字段值之间用逗号分隔 原料表&#xff1a;对应原料id和原料名称 现需要查询出的原料结果显示为原料名称&#xff0c;并以逗号分隔。可通过函数g…

网站速度优化-前端篇

2019独角兽企业重金招聘Python工程师标准>>> 页面中最好不要有js片段&#xff0c;合并多个js为一个js&#xff0c;并将js压缩 &#xff08;百度搜索&#xff0c;有在线js压缩、解压缩工具&#xff09;&#xff0c;并放在文档底部页面中最好不要有css片段&#xff0c…

HashMap总结

为什么用HashMap HashMap是一个Hash桶(数组链表)&#xff0c;桶存储的内容是键值对(Key-value)映射HashMap采用了数组和链表的数据结构&#xff0c;能在查询和修改方便继承了数组的线性查找和链表的寻址修改HashMap是非synchronized&#xff0c;所以HashMap很快(哈哈哈)与HashT…

Blender与Substance painter制作三维手枪

你会学到: Blender和Substance painter的基础知识 建模 纹理制作 烘焙 Uv展开 Boolens和斜角修改器 如何制作游戏准备枪 课程获取&#xff1a;Blender与Substance painter制作三维手枪 – 云桥网络-CG技术学习平台 要求 Blender Substance painter 你好&#xff0c;我是3d艺术…

Java学习总结:32(Runtime类)

Runtime类 该类用于表示虚拟机(JVM)运行时的状态&#xff0c;每次启动JVM都对应一个Runtime实例&#xff0c;且只有一个实例&#xff0c;利用Runtime类可以启动新的进程或进行相关运行时环境的操作。此外&#xff0c;该类采用单例模式设计&#xff0c;对象不可以直接实例化。所…

转:查看系统是64位还是32位

1、getconf LONG_BIT or getconf WORD_BIT 例如&#xff1a; 2、file /bin/ls 例如&#xff1a; 查看linux的版本: 转载于:https://www.cnblogs.com/lei-lei/p/5029120.html

Redis问题——Error: 磁盘在使用中,或被另一个进程锁定。

Redis出于对数据保护&#xff0c;默认只能本地客户端连接。远程连接就会出现以上错误。如何解决这一问题&#xff0c;看下&#xff1a; server -A&#xff0c;PC-A&#xff0c; 修改server-A的redis.conf:注释掉本地绑定&#xff1b; bind 127.0.0.1 表示指定绑定本机IP&…

[转]C#索引器

索引器是一种特殊的类成员&#xff0c;它能够让对象以类似数组的方式来存取&#xff0c;使程序看起来更为直观&#xff0c;更容易编写。 1、索引器的定义 C#中的类成员可以是任意类型&#xff0c;包括数组和集合。当一个类包含了数组和集合成员时&#xff0c;索引器将大大简化对…

从Java8到Java21各版本新特性详解

​ 下面这张图是 Oracle 官方给出的 Oracle JDK 支持的时间线,可以看出,JDK 17的支持时间最长,可以延续到2029年9月。考虑到技术更新的速度,这次免费商用8年的时间可谓是经过精心考虑,旨在让用户放心地升级到JDK 17(不过JDK 8的支持时间更长,到2030年12月)。​ 从JDK诞生到现在,仅有几个版本得到了长期支持,主要包括JDK 7、JDK 8、JDK 11以及即将发布的JDK 17,它将是继Java 8之后最重要的LTS版本,是Java社区八年努力的成果。

make[1]: g++: Command not found

今天装了nmap软件&#xff0c;开始报这种错&#xff1a;g -c -Iliblua -Ilibdnet-stripped/include -Ilibpcre -Ilibpcap -Inbase -Insock/include -fno-strict-aliasing -DHAVE_CONFIG_H -DNMAP_NAME\"Nmap\" -DNMAP_URL\"http://nmap.org\" -DNMAP_PL…

学习如何用平板电脑设计和绘制自己的动漫角色

创造你自己的动漫人物插图 学习如何用平板电脑设计和绘制自己的动漫角色 大家好&#xff0c;我是Pesa&#xff0c;一个想把快乐和希望融入到讲述故事的插画中的插画师。一点一点地&#xff0c;我画出我喜欢和希望的事物的场景&#xff0c;它把我带到了101班。 艺术世界 艺术是…

Java学习总结:33(System类)

System类 System类的方法 No.方法类型描述1public static void arraycopy(Object src&#xff0c;int srcPos&#xff0c;Object dest&#xff0c;int destPos&#xff0c;int length)普通数组粘贴操作2public static long currentTimeMillis()普通取得当前的日期时间&#x…