每个排序算法的理解我都写在注释里面了,相信只要耐心点,都能看懂的.
1.冒泡排序
package com.wqc.sort;
import java.util.Arrays;
/**
* @author 高瞻远瞩
* @version 1.0
* @motto 算法并不可怕, 可怕的是你不敢面对它, 加油!别浮躁~冲击大厂!!!
* 演示冒泡排序 时间复杂度是o(n^2)
*/
public class BubbleSort {
static int count = 0;
public static void main(String[] args) {
int[] arr = new int[80000];//模拟80000条数据
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) (Math.random() * 80000);
}
long start = System.currentTimeMillis();
System.out.println("开始排序");
bubbleSort(arr);
long end = System.currentTimeMillis();
System.out.println("80000次共耗时=" + (end - start));//8772
// System.out.println("=======排序过后=========");
// System.out.println(Arrays.toString(arr));
// System.out.println("count=" + count);//4
}
public static void bubbleSort(int[] arr) {
int temp = 0;
for (int i = 0; i < arr.length - 1; i++) {
boolean flag = true;//重置
for (int j = 0; j < arr.length - 1 - i; j++) {
count++;
if (arr[j] > arr[j + 1]) {
flag = false;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
if (flag) {//如果在某趟排序中 一次也没发生过交换 说明有序 就break不再进行排序
break;
}
}
}
}
2.简单插入排序
package com.wqc.sort;
import java.util.Arrays;
/**
* @author 高瞻远瞩
* @version 1.0
* @motto 算法并不可怕, 可怕的是你不敢面对它, 加油!别浮躁~冲击大厂!!!
* 演示简单插入排序 核心比较的思想是:让第一个和第0个比较 如果比它大的话就不用再插 此时前两个是有序的
* 然后让第三个和第二个进行比较 如果比它大的话 也不用插入了 此时前三个是有序的
* 然后让第4个和第三个进行比较 如果它比第三个小的话 让第三个向后移动覆盖他的值
* 然后继续和第二个进行比较 如果它还比第二个小的话 让第二个向后移动 覆盖第三个的值
* 然后继续和第一个比较 如果比第一个小的话 就结束以上循环操作 这就实现了比他大都往后移动的效果
* 最后让它插入到要插入下标的下一个位置 在这个过程中 要插入位置的下标在不断-1 但始终要>=0
* 直到下标=-1 或者在循环结束前找到合适插入的位置 然后插入下标+1就是它要插入的位置
*/
public class InsertSort {
public static void main(String[] args) {
int[] arr = {104,34,58,1};
// int[] arr = new int[80000];//模拟80000条数据
// for (int i = 0; i < arr.length; i++) {
// arr[i] = (int) (Math.random() * 8000000);
// }
// long start = System.currentTimeMillis();
// System.out.println("开始排序");
insertSort(arr);
// long end = System.currentTimeMillis();
// System.out.println("80000次共耗时=" + (end - start));//435
System.out.println(Arrays.toString(arr));
}
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;//和前一个比较
//insertIndex >= 0 标志插入的位置不能越界 极端情况下也就是=-1
//insertVal < arr[insertIndex] 只要符合这个条件 说明插入的位置还没找到
while(insertIndex >= 0 && insertVal < arr[insertIndex]){
arr[insertIndex + 1] = arr[insertIndex];
insertIndex--;
}
if(insertIndex + 1 != i) {
arr[insertIndex + 1] = insertVal;
}
}
/**
//第一轮
int insertVal = arr[1];
int insertIndex = 1-1;
while(insertIndex >= 0 && insertVal < arr[insertIndex]){
arr[insertIndex + 1] = arr[insertIndex];
insertIndex--;
}
//当体跳出while循环时 insertIndex下表所在数组的元素及之后的有序元素均向后移动了
//因为初始insertIndex = 0 因此把arr[0]赋给arr[1]之后就跳出了while循换 实现了后移的效果
//然后此时insertIndex=-1 即插入的位置为insertIndex+1
arr[insertIndex + 1] = insertVal;
System.out.println("第一轮=" + Arrays.toString(arr));
//第二轮
insertVal = arr[2];
insertIndex = 2-1;
while(insertIndex >= 0 && insertVal < arr[insertIndex]){
arr[insertIndex + 1] = arr[insertIndex];
insertIndex--;
}
arr[insertIndex + 1] = insertVal;
System.out.println("第二轮=" + Arrays.toString(arr));
**/
}
}
3.归并排序
package com.wqc.sort;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author 高瞻远瞩
* @version 1.0
* @motto 算法并不可怕, 可怕的是你不敢面对它, 加油!别浮躁~冲击大厂!!!
* 归并排序 ---- 分治思想 排序n个 合并n-1次 时间复杂度为 n*log n 可以这样理解这个时间复杂度,时间复杂度是n*递归的深度,而递归的深度可以理解成是一个完全二叉树,树高就是log n,所以时间复杂度是n*log n
*/
public class MergeSort {
public static void main(String[] args) {
// int[] arr = new int[8000000];//模拟80000条数据
// for (int i = 0; i < arr.length; i++) {
// arr[i] = (int) (Math.random() * 8000000);
// }
// long start = System.currentTimeMillis();
// System.out.println("开始排序");
int[] arr = {8, 4, 5, 7, 1, 3, 6, 2};
int[] temp = new int[arr.length];
part(arr, 0, arr.length - 1, temp);
// long end = System.currentTimeMillis();
// System.out.println("8000000次共耗时=" + (end - start));//800万条数据932
System.out.println("分治算法后arr=" + Arrays.toString(arr));
}
public static void part(int[] arr, int left, int right, int[] temp) {
int medium = (left + right) / 2;
if (left < right) {
//向左进行分解
part(arr, left, medium, temp);
//向右进行分解
part(arr, medium + 1, right, temp);
// System.out.println("******");
//然后进行合并
merge(arr, left, medium, right, temp);
}
}
/**
* @param arr 要进行排序的数组
* @param left 最左的索引 作为左边有序序列的第一个索引
* @param medium 中间的索引 加1是右边有序序列的第一个索引
* @param right 最右的索引
* @param temp 临时数组
* 目的是把两个有序的数组合并成一个有序的数组
*/
public static void merge(int[] arr, int left, int medium, int right, int[] temp) {
//1,两两进行比较 小的数值加入到temp数组中
int l = left; //左边有序序列的第一个值
int r = medium + 1; //右边有序系列的第一个值
int m = 0;
while (l <= medium && r <= right) { //两个有序序列的索引都需要在各自索引的范围内
if (arr[l] < arr[r]) {
temp[m] = arr[l];
m++;
l++;
} else {
temp[m] = arr[r];
m++;
r++;
}
}
//当结束while循环时 把非空的那个序列加入到临时数组的后面
while (r <= right) {
temp[m] = arr[r];//说明第一个序列遍历完毕 第二个序列是非空的
m++;
r++;
}
while (l <= medium) {//这种情况是第二个序列遍历完毕 第一个是非空的
temp[m] = arr[l];
m++;
l++;
}
//最后一步 将temp数组拷贝到arr数组 拷贝的是arr索引left到right之间的元素
// for (int i = left,k = 0; i <= right; i++,k++) {
// arr[i] = temp[k];
System.out.println("left=" + left + "right=" + right);
// }
int tempLeft = left;
int t = 0;
// System.out.println("left=" + left + "right=" + right);
while (tempLeft <= right) {
arr[tempLeft] = temp[t];
tempLeft++;
t++;
}
}
}
4.快速排序
共有两种实现方式,区别在于哨兵的选取位置的不同.
package com.wqc.sort;
import java.util.Arrays;
/**
* @author 高瞻远瞩
* @version 1.0
* @motto 算法并不可怕, 可怕的是你不敢面对它, 加油!别浮躁~冲击大厂!!!
* 演示快速排序 思想是设置一个中轴值 两个指针分别在最左边和最右边 然后分别向中轴值的方向移动
* 如果左指针所指向的值大于中轴值就不再进行移动 如果右指针所指向的值小于中轴值也不再进行移动 然后进行交换
* 如果两个指针在移动过程中 都没有发现以上的情况 最后移动到中轴值后就都结束移动 说明中轴值的左边是都比它小的
* 右边的值是都比它大的
*/
public class QuickSort {
public static void main(String[] args) {
int[] arr = {0,1,2,3,4,0,3,3,8,1,4,6,2,8,8,15,10,0,9,9,1,2,17,8,17,25,18,18,16,13,18,29,2,3,32,2,26,23,18,8,34,8,11,36,36,39,46,30,21,25,21,14,41,10,31,55,45,16,33,47,4,52,59,60,1,43,42,10,12,56,12,27,22,52,38,12,41,42,71,5,42,76,8,3,31,65,11,29,28,68,33,50,73,87,22,68,31,1,38,89,
60};
// int[] arr = new int[8000000];//模拟80000条数据
// for (int i = 0; i < arr.length; i++) {
// arr[i] = (int) (Math.random() * 8000000);
// }
// long start = System.currentTimeMillis();
// System.out.println("开始排序");
quickSort(arr, 0, arr.length - 1);
// long end = System.currentTimeMillis();
// System.out.println("8000000次共耗时=" + (end - start));//800万条 903
System.out.println("排序后arr=" + Arrays.toString(arr));
}
/**
* @param arr
* @param left 需要排序的最左边的下标
* @param right 需要排序的最右边的下标
* 这个利用中值作为哨兵 l和r没有先后之分
*/
public static void quickSort(int[] arr, int left, int right) {
int l = left;
int r = right;
int temp;
if(l >= r){//递归结束的条件
return;
}
int pivot = arr[(left + right) / 2];//中轴值
while (l <= r) { //l在不断的++ r在不断的-- 当l大于r就跳出循环
while (arr[r] > pivot) {//小于等于中间值就停止循环 否则就一直左移
r --;
}
while (arr[l] < pivot) {//大于等于中间值就停止循环 否则就一直右移
l ++;
}
//在结束以上while循环后 如果满足l>=r的条件 说明pivot的左边的值都比pivot小
//右边的值都比pivot大
if (l >= r) {
break;
}
//不满足结束while循环的结束条件的话 说明需要进行交换
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
//交换过后 如果l所在的下标的值等于pivot 就让r-- 前移
if (arr[l] == pivot) {
r --;
}
//交换过后 如果r所在的下标的值等于pivot 就让l++ 后移
if (arr[r] == pivot) {
l ++;
}
}
//经过以上while循环 所有小于基准数的都被放在了pivot索引左边 大于的放在了右边 而且r要么在l的左边 要么和l相等
//还有一点需要注意的是 l一定>=r 所以当r-1 l+1 把位置错开 方便再进行递归
if (r == l) {
r --;
l ++;
}
//以上的if条件不管发不发生 到这里r一定在l的左边
// 然后进行递归 如果r>left的话 就进行左递归 也可以相等
// if (r > left) {
quickSort(arr, left, r);
// }
//如果l<right的话 进行右递归 也可以相等
// if (l < right) {
quickSort(arr, l, right);
// }
}
/**
*
* @param arr
* @param left
* @param right
* 这个如果让数组最左元素作为哨兵的话 必须先要移动r 后移动l
*/
public static void quickSort2(int[] arr, int left, int right) {
int i = left;
int j = right;
// if(left >= right){
// return;//递归结束条件
// }
while(i < j){
while(i < j && arr[j] >= arr[left]){//选取left作为基准数 还必须先移动j 再移动i
j--;
}
while(i < j && arr[i] <= arr[left]){
i++;
}
//i和j进行交换
swap(arr,i,j);
}
//当结束while循环时 再将left 和 i 进行交换 此步的目的就是将哨兵放入合适的位置 交换过后 哨兵左边的都是比它小的 右边的都是比它大的
swap(arr,left,i);
//进行递归
if(i - 1 > left) {
quickSort2(arr, left, i - 1);//递归的话 传入的应该是i因为此时i是基准值 可以理解为中值
//它的左边全是比它小的 右边都是比它大的
//所以不应该是left
}
if(right > i + 1) {
quickSort2(arr, i + 1, right);
}
}
public static void swap(int[] arr,int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
5.基数排序
package com.wqc.sort;
import java.util.Arrays;
import java.util.OptionalInt;
/**
* @author 高瞻远瞩
* @version 1.0
* @motto 算法并不可怕, 可怕的是你不敢面对它, 加油!别浮躁~冲击大厂!!!
* 演示基数排序 桶排序的升级版 思想:先按照个位排 是哪个数就放在哪个桶
* 然后是百位 千位 以此类推
* 典型的空间换时间
*/
public class RadixSort {
public static void main(String[] args) {
int[] arr = {53, 3, 542, 748, 14, 214};
// int[] arr = new int[8000000];//模拟8000000条数据
// for (int i = 0; i < arr.length; i++) {
// arr[i] = (int) (Math.random() * 8000000);
// }
// long start = System.currentTimeMillis();
// System.out.println("开始排序");
radixSort(arr);
// long end = System.currentTimeMillis();
// System.out.println("8000000次共耗时=" + (end - start));//800万条数据379
System.out.println("桶排序后arr=" + Arrays.toString(arr));
}
public static void radixSort(int[] arr) {
//归纳总结 按位排序 先计算出需要排序的数组的最高位数是多少
int[][] bucket = new int[10][arr.length];
int[] bucketCounts = new int[10];//记录每个桶的个数
// int max = arr[0];
int max = Arrays.stream(arr).max().getAsInt();
// for (int i = 1; i < arr.length; i++) {//比较出最大值
// if (max < arr[i]) {
// max = arr[i];
// }
// }
int maxLength = (max + "").length();
for (int j = 0,n=1; j < maxLength; j++, n *= 10) {//最高位长度是多少就进行几轮排序
for (int i = 0; i < arr.length; i++) {
int bit = arr[i] / n % 10;
bucket[bit][bucketCounts[bit]] = arr[i];
bucketCounts[bit]++;
}
int index = 0;
for (int i = 0; i < bucket.length; i++) {//遍历每个桶 从第一个桶开始
//然后遍历桶里面的有效数据 需要进行桶里面的个数是否等于0判断 进行过滤
if(bucketCounts[i] != 0) {
for (int k = 0; k < bucketCounts[i]; k++) {//得到每个桶有多少个有效数据
//如果i等于0 的话 bucketCounts[0]=5 说明第一桶放了5个数据 在二维数组中对应的下标就是
//bucket[i][0] ~ bucket[i][4]
arr[index++] = bucket[i][k];
}
}
//取完每个桶的数据之后 把个数清0 方便下次从桶中取数据时都是新的 虽然二维数组里面的数据没有清0
// 但是在取数据的时候进行个数是否等于0的过滤
bucketCounts[i] = 0;
}
}
/*
int[][] bucket = new int[10][arr.length];
//这个数组可以理解成10个一维数组 分别是0~9 每一个一维数组的大小是放的个数
//就比如bucket[9].length = 5; 说明最后一个桶放了5个数 为了区别每个桶放的个数 需要再定义一个一维数组
int[] bucketCounts = new int[10];
//这就是为什么每个数的最大值是arr.length的原因 因为可能取完位数之后都放在那个桶
//典型的空间换时间
//进行第一轮排序 每个数分别进行取个位 然后放入对应的桶中
for (int i = 0; i < arr.length; i++) {
int bit = arr[i] / 1 % 10;
bucket[bit][bucketCounts[bit]] = arr[i];//二维数组是真正存放值的
bucketCounts[bit]++;//假如bit=1 初始化的时候bucketCounts[1] = 0
// 放完之后bucket[1][0] = arr[i] 当放第二个时 如果bit还等于1时 放完之后 bucket[1][1] = arr[i]
//所以每次放完之后bucketCounts[bit]要++ 所以bucketCounts对应的就是每个桶放的个数
//加入bucketCounts[0] = 10的话 说明第一个桶已经放了10个元素了
}
int index = 0;
//然后取每个桶里的数 加入到arr数组中
for (int i = 0; i < bucket.length; i++) {//遍历每个桶
//然后遍历桶里面的有效数据
for (int j = 0; j < bucketCounts[i]; j++) {//得到每个桶有多少个有效数据
//如果i等于0 的话 bucketCounts[0]=5 说明第一桶放了5个数据 在二维数组中对应的下标就是0~4
if(bucketCounts[i] != 0){//这一点的判断需要加在外面
arr[index++] = bucket[i][j];
}
}
//取完每个桶的数据之后 把个数清0 方便下次从桶中取数据时都是新的 虽然二维数组里面的数据没有清0
// 但是在取数据的时候进行个数是否等于0的过滤
bucketCounts[i] = 0;
}
System.out.println("第一轮桶排序后arr=" + Arrays.toString(arr));
//第二轮排序 按十位排序
for (int i = 0; i < arr.length; i++) {
int bit = arr[i]/10 % 10;
bucket[bit][bucketCounts[bit]] = arr[i];
bucketCounts[bit]++;
}
index = 0;
//然后取每个桶里的数 加入到arr数组中
for (int i = 0; i < bucket.length; i++) {//遍历每个桶
//然后遍历桶里面的有效数据
for (int j = 0; j < bucketCounts[i]; j++) {
if(bucketCounts[i] != 0){//这一点的判断需要加在外面
arr[index++] = bucket[i][j];
}
}
bucketCounts[i] = 0;
}
System.out.println("第二轮桶排序后arr=" + Arrays.toString(arr));
//第三轮排序 按百位排序
for (int i = 0; i < arr.length; i++) {
int bit = arr[i]/100 % 10;
bucket[bit][bucketCounts[bit]] = arr[i];
bucketCounts[bit]++;
}
index = 0;
//然后取每个桶里的数 加入到arr数组中
for (int i = 0; i < bucket.length; i++) {//遍历每个桶
//然后遍历桶里面的有效数据
for (int j = 0; j < bucketCounts[i]; j++) {
if(bucketCounts[i] != 0){//这一点的判断需要加在外面
arr[index++] = bucket[i][j];
}
}
bucketCounts[i] = 0;
}
System.out.println("第三轮桶排序后arr=" + Arrays.toString(arr));
*/
}
}
6.简单选择排序
package com.wqc.sort;
import java.util.Arrays;
/**
* @author 高瞻远瞩
* @version 1.0
* @motto 算法并不可怕, 可怕的是你不敢面对它, 加油!别浮躁~冲击大厂!!!
* 演示简单选择排序 思想:每轮把最小的放在前面
* 算法实现步骤:第一步从arr[0] ~ arr[n] 中选一个最小的放在前面
* 可以先把arr[0] 设置成最小的 然后让它和之后的进行比较 最后找出最小的进行和他交换
* 第一步结束后 最小的就被放在了arr[0]的位置
* 第二步是从arr[1] ~ arr[n] 中选一个最小的值让它和arr[1] 交换
* 依次类推
* 时间复杂度是o(n^2)
*/
public class SelectSort {
public static void main(String[] args) {
// int[] arr = {5, 4, 3, 2, 1};
int[] arr = new int[80000];//模拟80000条数据
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) (Math.random() * 80000);
}
long start = System.currentTimeMillis();
System.out.println("开始排序");
simpleSelectSort(arr);
long end = System.currentTimeMillis();
System.out.println("80000次共耗时=" + (end - start));//1500
// System.out.println("排序过后arr=" + Arrays.toString(arr));
}
public static void simpleSelectSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
int min = arr[i];//最小值
int minIndex = i;//最小值下标
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < min) {
min = arr[j];//重新修改最小值
minIndex = j;//修改最小值下标
}
}
if (minIndex != i) {//优化 如果他的最小下标没有发生变化 就说明不用交换
arr[minIndex] = arr[i];//把需要交换的这个arr[i]的值赋给最小值下标的元素
arr[i] = min;//将最小值赋给arr[0]
//以上两步实现了将找到的最小值下标的元素和arr[0]交换的目的
// 之后就是和arr[1]交换
//直到和arr[n-1]交换完后 即把n-1个最小值依次放在前面 实现了排序的效果
}
}
}
}
7.希尔排序
package com.wqc.sort;
import java.util.Arrays;
/**
* @author 高瞻远瞩
* @version 1.0
* @motto 算法并不可怕, 可怕的是你不敢面对它, 加油!别浮躁~冲击大厂!!!
* 演示希尔排序 两种方法 一种是基于移位的希尔排序 跟简单插入排序的区别就是要进行分组 代码基本一致
* 另一种是基于换位的希尔排序 这个算法没怎么看懂是怎么for循环的
*/
public class ShellSort {
public static void main(String[] args) {
//移位法的希尔排序
// int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
// int[] arr = new int[80000];//模拟80000条数据
// for (int i = 0; i < arr.length; i++) {
// arr[i] = (int) (Math.random() * 8000000);
// }
// long start = System.currentTimeMillis();
// System.out.println("开始排序");
// shellSort(arr);
// long end = System.currentTimeMillis();
// System.out.println("80000次共耗时=" + (end - start));//移位的希尔排序13
// System.out.println(Arrays.toString(arr));
//交换法的希尔排序
int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
// int[] arr = new int[80000];//模拟80000条数据
// for (int i = 0; i < arr.length; i++) {
// arr[i] = (int) (Math.random() * 8000000);
// }
// long start = System.currentTimeMillis();
// System.out.println("开始排序");
shellSort2(arr);
// long end = System.currentTimeMillis();
// System.out.println("80000次共耗时=" + (end - start));//交换的希尔排序4359
System.out.println(Arrays.toString(arr));
}
//移位法
public static void shellSort(int[] arr) {
int insertValue = 0;
int insertIndex = 0;
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
//第一次相隔5给分1组 第二次相隔2个分一组 第三次相隔一个分一组 所以一共分3次
for (int i = gap; i < arr.length; i++) {
insertValue = arr[i];
insertIndex = i - gap;//和前一个元素比较 经过分组后前一个元素为i-gap
while (insertIndex >= 0 && insertValue < arr[insertIndex]) {
arr[insertIndex + gap] = arr[insertIndex];//后移 2>3 3>4 4>5 n>n+1
insertIndex -= gap;//减步长继续比较
}
if (insertIndex + gap != i) {
arr[insertIndex + gap] = insertValue;//最后插入的位置在insertIndex+步长gap
}
}
}
}
//交换法
public static void shellSort2(int[] arr) {
//以下综合成一段代码为
for (int gap = arr.length/2; gap > 0 ; gap/=2) {
int temp = 0;
for (int i = gap; i < arr.length; i++) {
for (int j = i - gap; j >= 0; j -= gap) {
if(arr[j] > arr[j + gap]) {//arr[0] 大于 arr[0+gap] 再交换
temp = arr[j + gap];
arr[j + gap] = arr[j];
arr[j] = temp;
}
}
}
}
/*
//演示第一次分组 间隔为arr.length/2=5 间隔为5的是一组 一共分成5组
//i=5 j=0 i=6 j=1
int temp = 0;
for (int i = 5; i < arr.length; i++) {
for (int j = i - 5; j >= 0; j -= 5) {
if(arr[j] > arr[j + 5]) {//arr[0] 大于 arr[5] 再交换
temp = arr[j + 5];
arr[j + 5] = arr[j];
arr[j] = temp;
}
}
}
System.out.println("第一组交换后=" + Arrays.toString(arr));
//间隔是2的分成一组 一共分成2组 第一组 arr[1] 3 5 7 9 第二组 arr[0] 2 4 6 8
//按理说应该第一组和第二组分别进行排序
//3 1 0 9 7 0 1 3 7 9 arr[0] 跟 arr[2]比较 arr[1] 跟arr[3] 比较 arr[2]跟arr[4] arr[0]跟arr[2]比较
//arr[3] <--> arr[5] arr[1] <--> arr[3] arr[4]<-->arr[6] arr[2]<-->arr[4] arr[0]<-->arr[2]
for (int i = 2; i < arr.length; i++) {
for (int j = i - 2 ; j >= 0; j -= 2) {
if(arr[j] > arr[j+2]) {
temp = arr[j+2];
arr[j+2] = arr[j];
arr[j] = temp;
}
}
}
System.out.println("第二组交换后=" + Arrays.toString(arr));
//第三组交换后
for (int i = 1; i < arr.length; i++) {
for (int j = i - 1 ; j >= 0; j -= 1) {
if(arr[j] > arr[j+1]) {
temp = arr[j+1];
arr[j+1] = arr[j];
arr[j] = temp;
}
}
}
System.out.println("第二组交换后=" + Arrays.toString(arr));
*/
}
}
8.堆排序
package com.wqc.tree;
import java.util.Arrays;
import java.util.PriorityQueue;
/**
* @author 高瞻远瞩
* @version 1.0
* @motto 算法并不可怕, 可怕的是你不敢面对它, 加油!别浮躁~冲击大厂!!!
* 演示堆排序(选择排序) 基于完全二叉树 升序--》构建大顶堆 降序---》构建小顶堆 构建的过程--》练习详细
* 时间复杂度是n*log^n
*/
public class HeapSort {
public static void main(String[] args) {
int[] arr = {4,6,8,5,9};
// int[] arr = new int[8000000];//模拟8000000条数据
// for (int i = 0; i < arr.length; i++) {
// arr[i] = (int) (Math.random() * 8000000);
// }
// long start = System.currentTimeMillis();
// System.out.println("开始排序");
heapSort(arr);
// long end = System.currentTimeMillis();
// System.out.println("8000000次共耗时=" + (end - start));//800万条数据1313
}
public static void heapSort(int[] arr) {
System.out.println("堆排序");
// //1,将待排序序列调整成一个大顶堆 此时堆顶元素就是数组中元素的最大值
// adjustHeap(arr,1,arr.length);//第一次调整 {4,9,8,5,6}
// adjustHeap(arr,0,arr.length);//第二次调整 [9,6,8,5,4]
//总结以上两句话为一个for循环
for (int i = arr.length / 2 - 1; i >= 0; i--) {
adjustHeap(arr, i, arr.length);
}
System.out.println(Arrays.toString(arr));
//2,将其与末尾元素进行交换 此时末尾元素为最大值 然后将剩余n-1个元素重新构成一个堆 这样会得到n个元素的次小值 循环执行
int temp = 0;
for (int i = arr.length - 1; i > 0; i--) { //循环的次数为数组长度-1
// 当数组的n-1个元素都排好序的话 数组就变为有序的 选择排序的思想 每次选最大的或者最小的放在数组的最后
temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
//经过以上交换 第一个最大的元素就被放到了数组最后面
adjustHeap(arr,0,i);//下一次的调整堆待排序元素的个数不断减1
}
System.out.println("堆排序后数组");
System.out.println(Arrays.toString(arr));
}
/**
* 功能是把以i为非叶子结点作为子树构成一个大顶堆
*
* @param arr 数组
* @param i 非叶子结点的索引(从第一个开始) 假如此时数组{4,6,8,5,9} 第一个非叶子结点的索引是length/2 - 1 = 1
* 交换过后--》{4,9,8,5,6} 然后交换第二个非叶子结点 此时索引为 1-1=0 调整后--》[9,6,8,5,4]
* @param len 待排序的元素个数
*/
public static void adjustHeap(int[] arr, int i, int len) {
int temp = arr[i];//先保存此时的非叶子结点的值
for (int j = 2 * i + 1; j < len; j = 2 * j + 1) {
if (j + 1 < len && arr[j] < arr[j + 1]) {//如果此时的非叶子的左子树值小于右子树的话 j指向右子树
//这里的j+1 < len len就相当于数组的长度 j+1是右子树的索引 如果没有这个判断条件
//假如果j+1的索引等于数组长度的话 然后j再加1 arr[j] 的话数组下标会越界
j++;
}
//以上的if条件选出了此时结点的左右子树的最大值
if (arr[j] > temp) {//如果当前左右子树的最大值大于此结点 就让此最大值覆盖非叶子结点的值
arr[i] = arr[j];//覆盖
i = j; //让i指向j 循环进行以上步骤 同时也方便交换值 必须写的步骤!!!!
} else {
break;
}
//!!!!重要操作 恢复此最大值的索引为temp 相当于交换了最大值和非叶子结点的值 交换有点类似简单选择排序
arr[i] = temp;//arr[j] = temp 也可以
}
//for循环后 就把以i为父节点的树的最大值,放在了最顶部
}
}