尚硅谷视频
插入排序
1、插入排序
1.1、基本思想
考虑升序排序情况: (1)将序列划分为有序区和无序区。有序区为序列第一个元素,无序区为剩下的元素。 (2)每一次操作都将无序区的第一个元素r[i]与有序区里从最后一个元素开始向前比较,直到找到第一个比r[i]小的元素r[j],并将r[i]插入到r[j]后面;如果找不到元素比r[i]小,则r[i]要插入的位置为序列首部 (3)将r[i]插到r[j]后面,调整有序区中其它元素位置 插入排序的算法策略是减一法,即每插入一个元素后,原问题规模变成少了一个元素的子问题 例题: 对数据43,32,18,26,21,4,12,7,10,12进行插入排序,写出每趟的结果,并写出比较次数 注意:若当前数据插入到有序区最开头,与哨兵值也要比较1次
初始: (43) 32,18,26,21,4,12,7,10,12 第一趟: (32,43) 18,26,21,4,12,7,10,12 插32比较2次 第二趟: (18,32,43) 26,21,4,12,7,10,12 插18比较3次 第三趟: (18,26,32,43) 21,4,12,7,10,12 插26比较3次 第四趟: (18,21,26,32,43) 4,12,7,10,12 插21比较4次 第五趟: (4,18,21,26,32,43) 12,7,10,12 插4比较6次 第六趟: (4,12,18,21,26,32,43) 7,10,12 插12比较6次 第七趟: (4,7,12,18,21,26,32,43) 10,12 插7比较7次 第八趟: (4,7,10,12,18,21,26,32,43) 12 插10比较7次 第九趟: (4,7,10,12,12,18,21,26,32,43) 插12比较6次 共44次
1.2、代码
package sort;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class InsertSort {
public static void main(String[] args) {
int[] arr = new int[80000];
for (int i = 0; i < 80000; i++) {
arr[i] = (int) (Math.random() * 8000000);
}
System.out.println("插入排序前");
Date data1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date1Str = simpleDateFormat.format(data1);
System.out.println("排序前的时间是=" + date1Str);
insertSort(arr);
Date data2 = new Date();
String date2Str = simpleDateFormat.format(data2);
System.out.println("排序前的时间是=" + date2Str);
}
public static void insertSort(int[] arr) {
int insertVal = 0;
int insertIndex = 0;
for(int i = 1; i < arr.length; i++) {
insertVal = arr[i];
insertIndex = i - 1;
while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
arr[insertIndex + 1] = arr[insertIndex];
insertIndex--;
}
if(insertIndex + 1 != i) {
arr[insertIndex + 1] = insertVal;
}
}
}
}
1.3、总结
1、定义要插入的数和要插入的数的前一个元素的下标位置; 2、while循环保证有序区元素没有越界和要插入的数据比有序区的元素小,小的话说明没有找到,那么就将前一个元素的往后移动,前一个元素的下标减1 3、如果不满足while循环条件的话,那么说明有序区的元素中找到有比要插入的数据小的元素,将要插入的数据插入到这个有序区元素的后面
2、希尔排序
当需要插入的数是较小的数时,后移的次数明显增多,对效率有影响。希尔排序是希尔于1959年提出的一种排序算法,希尔排序是一种插入排序,他是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。
2.1、基本思想
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止
2.2、应用实例
有一群小牛, 考试成绩分别是 {8,9,1,7,2,3,5,4,6,0} 请从小到大排序. 请分别使用
- 希尔排序时, 对有序序列在插入时采用交换法, 并测试排序速度.
- 希尔排序时, 对有序序列在插入时采用移动法, 并测试排序速度
代码:
package sort;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class ShellSort {
public static void main(String[] args) {
int[] arr = new int[8000000];
for (int i = 0; i < 8000000; i++) {
arr[i] = (int) (Math.random() * 8000000);
}
System.out.println("排序前");
Date data1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date1Str = simpleDateFormat.format(data1);
System.out.println("排序前的时间是=" + date1Str);
shellSort2(arr);
Date data2 = new Date();
String date2Str = simpleDateFormat.format(data2);
System.out.println("排序前的时间是=" + date2Str);
}
public static void shellSort(int[] arr) {
int temp = 0;
int count = 0;
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
for (int j = i - gap; j >= 0; j -= gap) {
if (arr[j] > arr[j + gap]) {
temp = arr[j];
arr[j] = arr[j + gap];
arr[j + gap] = temp;
}
}
}
}
}
public static void shellSort2(int[] arr) {
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
int j = i;
int temp = arr[j];
if (arr[j] < arr[j - gap]) {
while (j - gap >= 0 && temp < arr[j - gap]) {
arr[j] = arr[j-gap];
j -= gap;
}
arr[j] = temp;
}
}
}
}
}
交换法
1、for循环分组,所有数据除以2得到步长 2、for循环数据中分为几组开始遍历数组 3、for循环变量各组元素,各组元素之间的下标相差步长的距离 4、if判断如果遍历到的各组元素中的前面元素大于后面的元素,那么两者交换位置。
public static void shellSort(int[] arr) {
int temp = 0;
int count = 0;
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
for (int j = i - gap; j >= 0; j -= gap) {
if (arr[j] > arr[j + gap]) {
temp = arr[j];
arr[j] = arr[j + gap];
arr[j + gap] = temp;
}
}
}
}
移动法
1、for循环逐步缩小增量gap 2、从第gap个元素开始,for遍历后面是数组 3、定义要增量前元素为j位置,要插入的数为j+gap位置 4、while确保不越界和判断要插入的数小于增量前的元素,说明没有找到 5、没有找到就移动增量前的元素到要插入的数的位置,移动增量前的元素移动增量个位置 6、将要插入的数放到增量前元素的位置
public static void shellSort2(int[] arr) {
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
int j = i;
int temp = arr[j];
if (arr[j] < arr[j - gap]) {
while (j - gap >= 0 && temp < arr[j - gap]) {
arr[j] = arr[j-gap];
j -= gap;
}
arr[j] = temp;
}
}
}
}
选择排序
3、选择排序
选择式排序也属于内部排序法,是从欲排序的数据中,按指定的规则选出某一元素,再依规定交换位置后达到排序的目的。
3.1、基本思想
- 将序列划分成有序区(左边)和无序区(右边)
- 初始时,有序区为空,无序区为序列全部
- 每一次选择出无序区中最小元素,与无序区中第1个元素交换,成为有序区的最后一个元素
真题:对数据48,70,8,30,23,11,15进行选择排序,写出每趟的结果 解:每次都从无序区中选择最小元素与无序区第一个元素交换成为有序区末尾 初始: ()48,70,8,30,23,11,15 第1趟:(8)70,48,30,23,11,15 第2趟:(8,11)48,30,23, 70,15 第3趟:(8,11,15)30,23, 70,48 第4趟:(8,11,15,23)30, 70,48 第5趟:(8,11,15,23,30)70,48 第6趟:(8,11,15,23,30,48)70
3.2、应用实例
有一群牛 , 颜值分别是 101, 34, 119, 1 请使用选择排序从低到高进行排序 [101, 34, 119, 1]
package com.atguigu.sort;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class SelectSort {
public static void main(String[] args) {
int[] arr = new int[80000];
for (int i = 0; i < 80000; i++) {
arr[i] = (int) (Math.random() * 8000000);
}
System.out.println("排序前");
Date data1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date1Str = simpleDateFormat.format(data1);
System.out.println("排序前的时间是=" + date1Str);
selectSort(arr);
Date data2 = new Date();
String date2Str = simpleDateFormat.format(data2);
System.out.println("排序前的时间是=" + date2Str);
}
public static void selectSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
int minIndex = i;
int min = arr[i];
for (int j = i + 1; j < arr.length; j++) {
if (min > arr[j]) {
min = arr[j];
minIndex = j;
}
}
if (minIndex != i) {
arr[minIndex] = arr[i];
arr[i] = min;
}
}
}
}
4、堆排序
问题:采用堆结构来描述数据,并在此基础上实现排序 定义:堆——具有以下性质的完全二叉树: 每个结点的值都小于或等于左右孩子结点的值(小根堆);或者每个结点的值都大于或等于其左右孩子结点的值(大根堆)。
4.1、基本思想
(1)根据堆的特性,将待排序序列建立堆。升序排序采用大根堆;降序排序采用小根堆 (2)堆顶元素为最大值(大根堆)或最小值(小根堆),将该元素与最后一个元素交换并移入有序区 (3)调整无序区为一个堆结构 (4)重复(2)和(3)直到无序区剩一个元素 由于每次交换堆顶元素并调整堆后,无序区都是比原问题少一个元素的堆,因此堆排序也是采用减一法的设计策略
示例2(以序列{1,2,9,11,4,6,8,10,16,5}为例) (1)建立堆 ① 按广度优先方式建立完全二叉树,每个结点的编号按广度优先分配,如根结点为1号,最后的结点(5)为10号
② 采用筛选法调整为大根堆,从编号为[n/2]?5的结点开始往前调整 (2)交换 建立好大根堆后,堆顶元素即为最大值,将堆顶元素与最后的元素进行交换
(3)调整堆 采用筛选法对根结点进行调整 (4)重复交换与调整
4.2、应用实例
要求:给你一个数组 {4,6,8,5,9} , 要求使用堆排序法,将数组升序排序。
package com.atguigu.tree;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class HeapSort {
public static void main(String[] args) {
int[] arr = new int[8000000];
for (int i = 0; i < 8000000; i++) {
arr[i] = (int) (Math.random() * 8000000);
}
System.out.println("排序前");
Date data1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date1Str = simpleDateFormat.format(data1);
System.out.println("排序前的时间是=" + date1Str);
heapSort(arr);
Date data2 = new Date();
String date2Str = simpleDateFormat.format(data2);
System.out.println("排序前的时间是=" + date2Str);
}
public static void heapSort(int arr[]) {
int temp = 0;
System.out.println("堆排序!!");
for(int i = arr.length / 2 -1; i >=0; i--) {
adjustHeap(arr, i, arr.length);
}
for(int j = arr.length-1;j >0; j--) {
temp = arr[j];
arr[j] = arr[0];
arr[0] = temp;
adjustHeap(arr, 0, j);
}
}
public static void adjustHeap(int arr[], int i, int lenght) {
int temp = arr[i];
for(int k = i * 2 + 1; k < lenght; k = k * 2 + 1) {
if(k+1 < lenght && arr[k] < arr[k+1]) {
k++;
}
if(arr[k] > temp) {
arr[i] = arr[k];
i = k;
} else {
break;
}
}
arr[i] = temp;
}
}
交换排序
5、起泡排序
5.1、基本思想
- 将序列划分成有序区(右边)和无序区(左边)
- 初始时,有序区为空,无序区为序列全部
- 每一次“起泡”相邻元素依次两两比较,若发现逆序则交换,使值较大
的元素逐渐从前移向后部,就象水底下的气泡一样逐渐 向上冒。
例:采用起泡排序法对数据48,70,8,30,23,11,15进行升序排序,写出每趟的结果 解: 初始: 48,70,8,30,23,11,15() 第1趟起泡: 48,70,8,30,23,11,15 48,8,70,30,23,11,15 48,8,30,70,23,11,15 48,8,30,23,70,11,15 48,8,30,23,11,70,15 48,8,30,23,11,15,(70) 第2趟起泡: 8,48,30,23,11,15,(70) 8,30,48,23,11,15,(70) 8,30,23,48,11,15,(70) 8,30,23,11,48,15,(70) 8,30,23,11,15,(48, 70) 第3趟起泡: 8,30,23,11,15,(48, 70) 8,23,30,11,15,(48, 70) 8,23,11,30,15,(48, 70) 8,23,11,15,(30,48, 70) 第4趟起泡: 8,23,11,15,(30,48, 70) 8,11,23,15,(30,48, 70) 8,11,15,(23,30,48, 70) 第5趟起泡: 8,11,15,(23,30,48, 70) 8,11,(15,23,30,48, 70) 第6趟起泡: 8,(11,15,23,30,48, 70)
5.2、应用实例
- 因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来没有进行过交换,就说明序列有序,因此要在排序过程中设置一个标志flag判断元素是否进行过交换。从而减少不必要的比较。(这里说的优化,可以在冒泡排序写好后,在进行)
- 我们举一个具体的案例来说明冒泡法。我们将五个无序的数:3, 9, -1, 10, -2 使用冒泡排序法将其排成一个从小到大的有序数列。
package sort;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class BubbleSort {
public static void main(String[] args) {
int[] arr = new int[80000];
for(int i =0; i < 80000;i++) {
arr[i] = (int)(Math.random() * 8000000);
}
Date data1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date1Str = simpleDateFormat.format(data1);
System.out.println("排序前的时间是=" + date1Str);
bubbleSort(arr);
Date data2 = new Date();
String date2Str = simpleDateFormat.format(data2);
System.out.println("排序后的时间是=" + date2Str);
}
public static void bubbleSort(int[] arr) {
int temp = 0;
boolean flag = false;
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
flag = true;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
if (!flag) {
break;
} else {
flag = false;
}
}
}
}
6、快速排序
6.1、基本思想
快速排序(Quicksort)是对冒泡排序的一种改进。
(1)划分:选定一个数做为轴值,把序列分成比轴值小(左边)和比轴值大(右边)两个子序列
- 选定第一个元素作为轴值,下标用i标记,后面的元素用j标记;从后面和前面比较,后面的值比轴值大就后面的下标向前移动一位,如果后面的比前面的小,那么两个元素交换位置,前面的下标向后移动一位;交换后之前是轴值有j标记,所以下次比较如果后面的大于前面的就是i向后移动不是j向前移动。
- 当i和j重合时结束
(2)求解子问题:分别对两个子序列进行轴值划分 (3)合并:不需要合并!!
例:采用排序方法对以下序列进行排序,写出每一趟的排序结果: 48,70,8,30,23,11,15,28 解: 初始: 48,70,8,30,23,11,15,28 第一趟: [28,15,8,30,23,11],(48),[70] 第二趟: [11,15,8,23],(28),[30],(48),[70] 第三趟: [8],(11),[15,23],(28),[30],(48),[70] 第四趟: (8,11),[15,23],(28),[30],(48),[70] 第五趟: (8,11,15),[23],(28),[30],(48),[70] 第六趟: (8,11,15,23,28),[30],(48),[70] 第七趟: (8,11,15,23,28,30,48),[70] 第八趟: 8,11,15,23,28,30,48,70
加粗 表示轴值; 圆括号表示已经排好的数 中括号表示待排序的区
6.2、应用实例
要求: 对 [-9,78,0,23,-567,70] 进行从小到大的排序,要求使用快速排序法。
package sort;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class QuickSort {
public static void main(String[] args) {
int[] arr = new int[8000000];
for (int i = 0; i < 8000000; i++) {
arr[i] = (int) (Math.random() * 8000000);
}
System.out.println("排序前");
Date data1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date1Str = simpleDateFormat.format(data1);
System.out.println("排序前的时间是=" + date1Str);
quickSort(arr, 0, arr.length-1);
Date data2 = new Date();
String date2Str = simpleDateFormat.format(data2);
System.out.println("排序前的时间是=" + date2Str);
}
public static void quickSort(int[] arr,int left, int right) {
int l = left;
int r = right;
int pivot = arr[(left + right) / 2];
int temp = 0;
while( l < r) {
while( arr[l] < pivot) {
l += 1;
}
while(arr[r] > pivot) {
r -= 1;
}
if( l >= r) {
break;
}
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
if(arr[l] == pivot) {
r -= 1;
}
if(arr[r] == pivot) {
l += 1;
}
}
if (l == r) {
l += 1;
r -= 1;
}
if(left < r) {
quickSort(arr, left, r);
}
if(right > l) {
quickSort(arr, l, right);
}
}
}
6.3、算法分析
关于快速排序的你可能不知道的事 (1)快速排序的适用场合 根据时间复杂度分析,快速排序对已有序(不管是正序还是逆序)的序列,效率最差。
(2)快速排序属于“有序度增长法”,虽然每次划分得到的序列并非有序,但每次划分实质上是找出轴值在排序后的位置,即每次划分是对轴值进行排序
(3)假如某次划分后,轴值所在位置为i,实质上表明该轴值在序列中是第i小的值(注意,这里说的序列是参与划分的序列),因此可借助快速排序的划分操作实现“求序列第i小数”
归并排序
7、归并排序
7.1、基本思想
将序列划分成两个长度相等的子序列,分别进行排序,最后将两个有序序列合并 步骤:
- 将序列分成两段相等长度的子序列
- 从两段序列的第一个元素开始比较两段序列的元素,取当前较小的值为新序列的元素
- 将采纳的子序列下标向后移动,采纳的元素添加到新的队列中。
- 当其中一个序列全部移动到新序列后,剩余序列直接拷贝
真题:对数据48,70,8,30,23,11,15,28进行归并排序,写出每趟的结果 ① 划分: 48,70,8,30 23,11,15,28 48,70 8,30 23,11 15,28 48 70 8 30 23 11 15 28
② 求解子问题,并合并子问题: 第一趟: 48 70 8 30 23 11 15 28 第二趟: 48,70 8,30 11,23 15,28 第三趟: 8,30,48,70 11,15,23,28 第四趟: 8,11,15,23,28,30,48,70
7.2、应用实例
给你一个数组, val arr = Array(9,8,7,6,5,4,3,2,1), 请使用归并排序完成
package sort;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class MergetSort {
public static void main(String[] args) {
int[] arr = new int[8000000];
for (int i = 0; i < 8000000; i++) {
arr[i] = (int) (Math.random() * 8000000);
}
System.out.println("排序前");
Date data1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date1Str = simpleDateFormat.format(data1);
System.out.println("排序前的时间是=" + date1Str);
int temp[] = new int[arr.length];
mergeSort(arr, 0, arr.length - 1, temp);
Date data2 = new Date();
String date2Str = simpleDateFormat.format(data2);
System.out.println("排序前的时间是=" + date2Str);
}
public static void mergeSort(int[] arr, int left, int right, int[] temp) {
if(left < right) {
int mid = (left + right) / 2;
mergeSort(arr, left, mid, temp);
mergeSort(arr, mid + 1, right, temp);
merge(arr, left, mid, right, temp);
}
}
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
int i = left;
int j = mid + 1;
int t = 0;
while (i <= mid && j <= right) {
if(arr[i] <= arr[j]) {
temp[t] = arr[i];
t += 1;
i += 1;
} else {
temp[t] = arr[j];
t += 1;
j += 1;
}
}
while( i <= mid) {
temp[t] = arr[i];
t += 1;
i += 1;
}
while( j <= right) {
temp[t] = arr[j];
t += 1;
j += 1;
}
t = 0;
int tempLeft = left;
while(tempLeft <= right) {
arr[tempLeft] = temp[t];
t += 1;
tempLeft += 1;
}
}
}
基数排序
8、基数排序
8.1、基本思想
- 将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
- 这样说明,比较难理解,下面我们看一个图文解释,理解基数排序的步骤
基数排序图文说明:将数组 {53, 3, 542, 748, 14, 214} 使用基数排序, 进行升序排序。
8.2、应用实例
将数组 {53, 3, 542, 748, 14, 214 } 使用基数排序, 进行升序排序
package sort;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class RadixSort {
public static void main(String[] args) {
int arr[] = { 53, 3, 542, 748, 14, 214};
System.out.println("排序前");
Date data1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date1Str = simpleDateFormat.format(data1);
System.out.println("排序前的时间是=" + date1Str);
radixSort(arr);
Date data2 = new Date();
String date2Str = simpleDateFormat.format(data2);
System.out.println("排序前的时间是=" + date2Str);
System.out.println("基数排序后 " + Arrays.toString(arr));
}
public static void radixSort(int[] arr) {
int max = arr[0];
for(int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
int maxLength = (max + "").length();
int[][] bucket = new int[10][arr.length];
int[] bucketElementCounts = new int[10];
for(int i = 0 , n = 1; i < maxLength; i++, n *= 10) {
for(int j = 0; j < arr.length; j++) {
int digitOfElement = arr[j] / n % 10;
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
bucketElementCounts[digitOfElement]++;
}
int index = 0;
for(int k = 0; k < bucketElementCounts.length; k++) {
if(bucketElementCounts[k] != 0) {
for(int l = 0; l < bucketElementCounts[k]; l++) {
arr[index++] = bucket[k][l];
}
}
bucketElementCounts[k] = 0;
}
}
}
}
总结
|