排序算法
8种排序
内部排序:
- 插入排序:直接插入排序、希尔排序
- 选择排序:简单选择排序、堆排序
- 交换排序:冒泡排序、快速排序
- 归并排序
- 基数排序
外部排序:
冒泡排序
/**
* 冒泡排序
* @author qb
* @version 1.0
* @since 2022/2/23 13:55
*/
public class BubbleSort {
public static void main(String[] args) {
int[] array = {3,9,-1,10,-2};
//临时变量
int temp = 0;
//冒泡排序时间复杂度为 O(n^2)
for (int i = 0; i < array.length - 1; i++) {
//第一趟排序,就是将最大的数排到后面
for (int j = 0; j < array.length - 1 - i; j++) {
//如果前面的数大于后面的数,则交换
if (array[j] > array[j+1]) {
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
System.out.println("第"+(i+1)+"趟排序后的数组");
System.out.println(Arrays.toString(array));
}
/*
//第一趟排序,就是将最大的数排到后面
for (int j = 0; j < array.length - 1; j++) {
//如果前面的数大于后面的数,则交换
if (array[j] > array[j+1]) {
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
System.out.println("第一趟排序后的数组");
System.out.println(Arrays.toString(array));
//第二趟排序
for (int j = 0; j < array.length - 2; j++) {
//如果前面的数大于后面的数,则交换
if (array[j] > array[j+1]) {
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
System.out.println("第二趟排序后的数组");
System.out.println(Arrays.toString(array));
//第三趟排序
for (int j = 0; j < array.length - 3; j++) {
//如果前面的数大于后面的数,则交换
if (array[j] > array[j+1]) {
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
System.out.println("第三趟排序后的数组");
System.out.println(Arrays.toString(array));
//第四趟排序
for (int j = 0; j < array.length - 4; j++) {
//如果前面的数大于后面的数,则交换
if (array[j] > array[j+1]) {
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
System.out.println("第四趟排序后的数组");
System.out.println(Arrays.toString(array));*/
}
}
冒泡排序优化
public static void main(String[] args) {
int array[] = {3,9,-1,10,20};
//临时变量
int temp = 0;
//标识变量,是否进行过交换
boolean flag = false;
//冒泡排序时间复杂度为 O(n^2)
for (int i = 0; i < array.length - 1; i++) {
//第一趟排序,就是将最大的数排到后面
for (int j = 0; j < array.length - 1 - i; j++) {
//如果前面的数大于后面的数,则交换
if (array[j] > array[j+1]) {
flag = true;
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
System.out.println("第"+(i+1)+"趟排序后的数组");
System.out.println(Arrays.toString(array));
if(!flag){
//在一趟排序中一次交换都没有发生
break;
}
else{
flag = false;
}
}
}
选择排序
/**
* 选择排序
* @author qb
* @version 1.0
* @since 2022/2/23 14:52
*/
public class SelectSort {
public static void main(String[] args) {
int[] arr = {101,34,119,1};
selectSort(arr);
}
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; j < arr.length ; j++) {
if(arr[j] < min){
minIndex = j;
min = arr[j];
}
}
if(minIndex != i){
arr[minIndex] = arr[i];
arr[i] = min;
}
System.out.println("第"+(i+1)+"轮后~~~");
System.out.println(Arrays.toString(arr));
}
}
}
插入排序
执行速度没有选择排序快,比冒泡速度快
/**
* 插入排序
*
* @author qb
* @version 1.0
* @since 2022/2/23 16:36
*/
public class InsertSort {
public static void main(String[] args) {
int[] arr = {101,34,119,1,-1,89};
insertSort(arr);
}
public static void insertSort(int[] arr){
for (int i = 1; i < arr.length; i++) {
//定义待插入的数
int insertVal = arr[i];
//待插入数的前一个数的索引 1-1
int insertIndex = i-1;
//给insertVal 找到插入的位置
//1. insertIndex >=0 保证给insertVal找插入位置时不越界
//2. insertVal < arr[insertIndex] 待插入的数还没找到插入位置
//3. 需要将arr[insertIndex] 后移
while(insertIndex >=0 && insertVal < arr[insertIndex]){
arr[insertIndex + 1] = arr[insertIndex];
insertIndex--;
}
//退出循环说明插入的位置找到,insertIndex+1的位置
if(insertIndex+1 != i){
arr[insertIndex + 1] = insertVal;
}
System.out.println("第"+i+"轮后");
System.out.println(Arrays.toString(arr));
}
/*//第一轮
//定义待插入的数
int insertVal = arr[1];
//待插入数的前一个数的索引 1-1
int insertIndex = 1-1;
//给insertVal 找到插入的位置
//1. insertIndex >=0 保证给insertVal找插入位置时不越界
//2. insertVal < arr[insertIndex] 待插入的数还没找到插入位置
//3. 需要将arr[insertIndex] 后移
while(insertIndex >=0 && insertVal < arr[insertIndex]){
arr[insertIndex + 1] = arr[insertIndex];
insertIndex--;
}
//退出循环说明插入的位置找到,insertIndex+1的位置
arr[insertIndex + 1] = insertVal;
System.out.println("第一轮后");
System.out.println(Arrays.toString(arr));
//第二轮
//定义待插入的数
insertVal = arr[2];
//待插入数的前一个数的索引 1-1
insertIndex = 2-1;
//给insertVal 找到插入的位置
//1. insertIndex >=0 保证给insertVal找插入位置时不越界
//2. insertVal < arr[insertIndex] 待插入的数还没找到插入位置
//3. 需要将arr[insertIndex] 后移
while(insertIndex >=0 && insertVal < arr[insertIndex]){
arr[insertIndex + 1] = arr[insertIndex];
insertIndex--;
}
//退出循环说明插入的位置找到,insertIndex+1的位置
arr[insertIndex + 1] = insertVal;
System.out.println("第二轮后");
System.out.println(Arrays.toString(arr));
//第三轮
//定义待插入的数
insertVal = arr[3];
//待插入数的前一个数的索引 1-1
insertIndex = 3-1;
//给insertVal 找到插入的位置
//1. insertIndex >=0 保证给insertVal找插入位置时不越界
//2. insertVal < arr[insertIndex] 待插入的数还没找到插入位置
//3. 需要将arr[insertIndex] 后移
while(insertIndex >=0 && insertVal < arr[insertIndex]){
arr[insertIndex + 1] = arr[insertIndex];
insertIndex--;
}
//退出循环说明插入的位置找到,insertIndex+1的位置
arr[insertIndex + 1] = insertVal;
System.out.println("第三轮后");
System.out.println(Arrays.toString(arr));*/
}
}
希尔排序
交换法 时间复杂度是O(n^3)
/**
* 希尔排序
*
* @author qb
* @version 1.0
* @since 2022/2/24 8:44
*/
public class ShellSort {
public static void main(String[] args) {
int[] arr = {8,9,1,7,2,3,5,4,6,0};
shellSort(arr);
}
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++) {
//遍历各组中所有的元素(共gap组,每组有2个元素),步长gap
for (int j = i-gap;j>=0;j -= gap){
System.out.println(gap + "---" + i +"----"+ j);
//如果当前元素大于加上步长后的哪个元素说明交换
if(arr[j] > arr[j+gap]){
temp = arr[j];
arr[j] = arr[j+gap];
arr[j+gap] = temp;
}
}
}
System.out.println("希尔排序第"+(++count)+"轮: "+Arrays.toString(arr));
}
//希尔排序的第一轮
//因为第一轮排序,是将10个数据分成了5组
/* for (int i = 5; i < arr.length; i++) {
//遍历各组中所有的元素(共5组,每组有2个元素),步长5
for (int j = i-5;j>=0;j -= 5){
//如果当前元素大于加上步长后的哪个元素说明交换
if(arr[j] > arr[j+5]){
temp = arr[j];
arr[j] = arr[j+5];
arr[j+5] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
//第二轮
for (int i = 2; i < arr.length; i++) {
//遍历各组中所有的元素(共5组,每组有2个元素),步长5
for (int j = i-2;j>=0;j -= 2){
//如果当前元素大于加上步长后的哪个元素说明交换
if(arr[j] > arr[j+2]){
temp = arr[j];
arr[j] = arr[j+2];
arr[j+2] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
//第二轮
for (int i = 1; i < arr.length; i++) {
//遍历各组中所有的元素(共5组,每组有2个元素),步长5
for (int j = i-1;j>=0;j -= 1){
//如果当前元素大于加上步长后的哪个元素说明交换
if(arr[j] > arr[j+1]){
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
System.out.println(Arrays.toString(arr));*/
}
}
位移式 速度快 8万 1s
/**
* 交换法
* @param arr 数组
*/
public static void shellSort2(int[] arr) {
int count = 0;
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];
//向前查找 与 当前当前的i进行比较
if(arr[j] < arr[j-gap]){
//前面的大,就向后移,移到i的位置
while (j - gap >= 0 && temp < arr[j-gap]){
//移动
arr[j] = arr[j-gap];
j -= gap;
}
//退出循环后,就找到了插入的位置
arr[j] = temp;
}
}
System.out.println("希尔排序第"+(++count)+"轮: "+Arrays.toString(arr));
}
}
快速排序
/**
* 快速排序
*
* @author qb
* @version 1.0
* @since 2022/2/24 10:11
*/
public class QuickSort {
public static void main(String[] args) {
int[] arr = {-9,78,0,23,-567,70};
quickSort(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
public static void quickSort(int[] arr,int left,int right){
int l = left;
int r = right;
//中轴
int pivot = arr[(l + r) /2];
//临时变量
int temp = 0;
//只要左边的索引还小于右边的索引就一直循环
while (l < r){
//左边的索引大于中间值就退出
while (arr[l] < pivot){
l += 1;
}
while (arr[r] > pivot){
r -=1;
}
//如果 l >= r 说明pivot 的左右两边,已经安全左边全部是小于等于pivot值
//右边就是大于等于pivot
if(l >= r){
break;
}
//交换
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
//如果交换完后,发现arr[l] = pivot,r往前再走一步(l--)
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);
}
}
}
归并排序
时间复杂度 线性对数阶 n-1
/**
* 归并排序
*
* @author qb
* @version 1.0
* @since 2022/2/24 11:54
*/
public class MergeSort {
public static void main(String[] args) {
int[] arr = {8,4,5,7,1,3,6,2};
int[] temp = new int[arr.length];
mergeSort(arr,0,arr.length-1,temp);
System.out.println("归并排序后的结果:"+ Arrays.toString(arr));
}
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);
}
}
/**
* 合并方法
* @param arr 排序的原数组
* @param left 左边有序序列的初始索引
* @param mid 中间索引
* @param right 右边有序序列的初始索引
* @param temp 临时数组
*/
public static void merge(int[] arr, int left,int mid,int right,int[] temp){
//初始化i 左边有序队列的初始索引
int i = left;
//初始化j 右边有序队列的初始索引
int j = mid+1;
//指向temp数组的当前索引
int t = 0;
//1.先把左右两边(有序)的数据,按照规则填充到temp数组,直到左右两边的有序序列又一遍处理完毕
while(i <= mid && j<= right){
// 如果左边的有序序列的当前元素,小于等于右边有序序列的当前元素,
// 即将左边的当前元素,拷贝到temp数组
//t后移,i后移
if(arr[i] <= arr[j]){
temp[t] = arr[i];
t +=1;
i +=1;
}
else{
//反之,将右边的当前元素移到temp数组
temp[t] = arr[j];
t +=1;
j +=1;
}
}
//2.把剩余数据的一边的数据一次全部填充到temp
while(i <= mid){
//说明左边的有序序列还有剩余的,就全部填充到temp
temp[t] = arr[i];
t+=1;
i+=1;
}
while(j <= right){
//说明右边的有序序列还有剩余的,就全部填充到temp
temp[t] = arr[j];
t+=1;
j+=1;
}
//3.将temp数组的元素拷贝到arr
//注意,并不是每次都拷贝所有
t = 0;
int tempLeft = left;
while(tempLeft <= right){
arr[tempLeft] = temp[t];
t +=1;
tempLeft +=1;
}
}
}
基数排序
/**
* 基数排序
*
* @author qb
* @version 1.0
* @since 2022/2/24 14:28
*/
public class RadixSort {
public static void main(String[] args) {
int[] arr = {53,3,542,748,14,214};
radixSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void radixSort(int[] arr) {
//最终的基数排序
//1.先得到数组中最大的数的位数,假设第一个数就是最大的
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){
//循环该捅,即第k个一维数组
for (int l = 0; l < bucketElementCounts[k]; l++) {
//取出元素放入到arr
arr[index] = bucket[k][l];
index ++;
}
}
//第一轮处理后将 bucketElementCounts[k] 清0;
bucketElementCounts[k] = 0;
}
System.out.println("第"+(i+1)+"轮处理结果: "+Arrays.toString(arr));
}
/*//第一轮
//定义一个二位数组 表示10个捅,每一个捅就是一个一维数组
//1.二维数组包含10个一维数组
//2.为了防止在放入的时候,数据溢出,则每一个数组(捅),大小定为arr.length
//3.很明确 基数排序是空间换时间的经典算法
int[][] bucket = new int[10][arr.length];
//为了记录每个捅中实际存放了多少数据,定义一个一维数组来记录各个捅每次放入的数据个数
int[] bucketElementCounts = new int[10];
//第一轮根据个位进行排序
for (int j = 0; j < arr.length; j++) {
//取出每个元素的个位
int digitOfElement = arr[j] % 10;
//放入对应的捅中
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
bucketElementCounts[digitOfElement] ++;
}
//按照这个捅的顺序(一维数组的下标一次取出数据,放入原来的数组)
int index = 0;
//遍历每一个捅,并将捅中的数据放入到元素中
for (int k = 0; k < bucketElementCounts.length; k++) {
//如果捅中有数据,我们才放入到原数组
if(bucketElementCounts[k] != 0){
//循环该捅,即第k个一维数组
for (int l = 0; l < bucketElementCounts[k]; l++) {
//取出元素放入到arr
arr[index] = bucket[k][l];
index ++;
}
}
//第一轮处理后将 bucketElementCounts[k] 清0;
bucketElementCounts[k] = 0;
}
//第二轮
for (int j = 0; j < arr.length; j++) {
//取出每个元素的十位
int digitOfElement = arr[j] / 10 % 10;
//放入对应的捅中
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
bucketElementCounts[digitOfElement] ++;
}
//按照这个捅的顺序(一维数组的下标一次取出数据,放入原来的数组)
index = 0;
//遍历每一个捅,并将捅中的数据放入到元素中
for (int k = 0; k < bucketElementCounts.length; k++) {
//如果捅中有数据,我们才放入到原数组
if(bucketElementCounts[k] != 0){
//循环该捅,即第k个一维数组
for (int l = 0; l < bucketElementCounts[k]; l++) {
//取出元素放入到arr
arr[index] = bucket[k][l];
index ++;
}
}
//第一轮处理后将 bucketElementCounts[k] 清0;
bucketElementCounts[k] = 0;
}
//第三轮
for (int j = 0; j < arr.length; j++) {
//取出每个元素的百位
int digitOfElement = arr[j] / 100 % 10;
//放入对应的捅中
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
bucketElementCounts[digitOfElement] ++;
}
//按照这个捅的顺序(一维数组的下标一次取出数据,放入原来的数组)
index = 0;
//遍历每一个捅,并将捅中的数据放入到元素中
for (int k = 0; k < bucketElementCounts.length; k++) {
//如果捅中有数据,我们才放入到原数组
if(bucketElementCounts[k] != 0){
//循环该捅,即第k个一维数组
for (int l = 0; l < bucketElementCounts[k]; l++) {
//取出元素放入到arr
arr[index] = bucket[k][l];
index ++;
}
}
}*/
}
}
堆排序
时间复杂度 n的对数阶
/**
* 堆排序
*
* @author qb
* @version 1.0
* @since 2022/3/2 16:41
*/
public class HeapSort {
public static void main(String[] args) {
//数组升序排列
int[] arr = {4,6,8,5,9,0,10,3};
heapSort(arr);
}
public static void heapSort(int[] arr){
System.out.println("堆排序");
int temp = 0;
/* //分步完成
adjustHeap(arr,1,arr.length);
//4,9,8,5,6
System.out.println("第一次调整:"+ Arrays.toString(arr));
adjustHeap(arr,0,arr.length);
//9,6,8,5,4
System.out.println("第二次调整:"+Arrays.toString(arr));*/
//1.将无序序列构建成一个堆,根据升序将去需求选择大顶堆或小丁堆
for (int i =arr.length / 2 -1; i >=0; i--) {
adjustHeap(arr,i,arr.length);
}
//2.将堆定元素与末尾元素交换,将最大的元素 沉到数组末端
//3.重新调整机构,使其满足堆定义,然后继续交换顶元素与当前末尾元素,反复执行调整 + 交换步骤,
// 直到整个序列有序
for (int j = arr.length-1; j > 0; j--) {
//交换
temp = arr[j];
arr[j] = arr[0];
arr[0] = temp;
adjustHeap(arr,0,j);
}
System.out.println(Arrays.toString(arr));
}
/**
* 将一个数组(二叉树),调整成一个大顶堆
* 完成将以 i对应的非叶子结点的数调整成大顶堆
* @param arr 待调整的数组
* @param i 表示非叶子结点在数组中的索引
* @param length 表示对多少个元素进行调整
*/
public static void adjustHeap(int[] arr,int i,int length){
//先取出当前元素的值
int temp = arr[i];
//开始调整
//k指向 i是左子节点
for (int k = i * 2+1;k<length;k = k*2+1){
//说明左子节点的值小于右子节点的值
if((k+1) < length && arr[k] < arr[k+1]){
k++;
}
//子节点大于父节点
if(arr[k] > temp){
//把较大的值赋给当前节点
arr[i] = arr[k];
// !!! i 指向k,继续循环比较
i=k;
}
else{
break;
}
}
//当for循环结束后,我们已经将以i为父节点的最大值放在了 最顶上
//将temp值放在调整后的位置
arr[i] = temp;
}
}