1、 排序算法的分类
2、冒泡排序
冒泡排序是一种简单的排序算法,它重复地走访过要排序的数列,一次比较两个元素,如果他们的前一个元素比后一个元素大就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
例如:将原始数据 3,9,-1,10,20 进行冒泡排序
代码实现
public class BubbleSort {
public static void main(String[] args) {
int[] arr = {3, 9, -1, 10, 20};
bubbleSort(arr);
}
private static void bubbleSort(int[] arr) {
int temp;
for (int i = 0; i < arr.length - 1; i++) {
for (int i1 = 0; i1 < arr.length - 1 - i; i1++) {
if (arr[i1] > arr[i1 + 1]) {
temp = arr[i1];
arr[i1] = arr[i1 + 1];
arr[i1 + 1] = temp;
}
}
System.out.println("第" + (i + 1) + "次排序是 " + Arrays.toString(arr));
}
}
}
输出结果
第1次排序是 [3, -1, 9, 10, 20]
第2次排序是 [-1, 3, 9, 10, 20]
第3次排序是 [-1, 3, 9, 10, 20]
第4次排序是 [-1, 3, 9, 10, 20]
冒泡排序的优化
代码实现
public class BubbleSort {
public static void main(String[] args) {
int[] arr = {3, 9, -1, 10, 20};
bubbleSort(arr);
}
private static void bubbleSort(int[] arr) {
int temp;
boolean flag = false;
for (int i = 0; i < arr.length - 1; i++) {
for (int i1 = 0; i1 < arr.length - 1 - i; i1++) {
if (arr[i1] > arr[i1 + 1]) {
flag = true;
temp = arr[i1];
arr[i1] = arr[i1 + 1];
arr[i1 + 1] = temp;
}
}
if (!flag) {
break;
} else {
flag = false;
}
System.out.println("第" + (i + 1) + "次排序是 " + Arrays.toString(arr));
}
}
}
结果
第1次排序是 [3, -1, 9, 10, 20]
第2次排序是 [-1, 3, 9, 10, 20]
3、选择排序
给定一个数组将第一个值看成最小值,然后和后续的比较找出最小值和下标,交换本次遍历的起始值和最小值。
说明:每次遍历的时候,将前面找出的最小值,看成一个有序的列表,后面的看成无序的列表,然后每次遍历无序列表找出最小值。
选择排序
public class BubbleSort {
public static void main(String[] args) {
int[] arr = {101, 24, 119, 1};
selectSort(arr);
}
private static void selectSort(int[] arr) {
// 101, 24, 119, 1
// 假定第一个值为最小值 定义最小值索引
int minIndex;
int min;
for (int i = 0; i < arr.length - 1; i++) {
minIndex = i;
min = arr[i];
for (int i1 = i + 1; i1 < arr.length; i1++) {
if (min > arr[i1]) {
minIndex = i1;
min = arr[i1];
}
}
// 每次循环结束 进行数据交换
if (minIndex != i) {
arr[minIndex] = arr[i];
arr[i] = min;
}
System.out.println("第" + (i + 1) + "次排序是 " + Arrays.toString(arr));
}
}
}
第1次排序是 [1, 24, 119, 101]
第2次排序是 [1, 24, 119, 101]
第3次排序是 [1, 24, 101, 119]
4、插入排序
public class BubbleSort {
public static void main(String[] args) {
int[] arr = {101, 24, 119, 1};
insertSort(arr);
}
private static void insertSort(int[] arr) {
// 101, 34, 119, 1
for (int i = 1; i < arr.length; i++) {
// 定义待插入的数
int insertVal = arr[i];
// 定义带插入的索引
int insertIndex = i - 1;
// 判断
while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
arr[insertIndex + 1] = arr[insertIndex];
insertIndex--;
}
// 插入最小值的位置
if (insertIndex + 1 != i) {
arr[insertIndex + 1] = insertVal;
}
System.out.println("第" + (i) + "次排序是 " + Arrays.toString(arr));
}
}
}
第1次排序是 [24, 101, 119, 1]
第2次排序是 [24, 101, 119, 1]
第3次排序是 [1, 24, 101, 119]
5、希尔排序
基本上和插入排序一样的道理,不一样的地方在于,每次循环的步长,通过减半的方式来实现。
说明:基本原理和插入排序类似,不一样的地方在于。通过间隔多个数据来进行插入排序。
public class BubbleSort {
public static void main(String[] args) {
int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
shellSort(arr);
}
private static void shellSort(int[] arr) {
// 步长
for (int stepLength = arr.length / 2; stepLength > 0; stepLength /= 2) {
for (int i1 = stepLength; i1 < arr.length; i1++) {
// 要插入的索引
int insertIndex = i1;
// 要插入的数据
int insertVal = arr[insertIndex];
// 分组前面的元素 大于后面的元素
if (arr[insertIndex] < arr[insertIndex - stepLength]) {
while (insertIndex - stepLength >= 0 && insertVal < arr[insertIndex - stepLength]) {
arr[insertIndex] = arr[insertIndex - stepLength];
insertIndex -= stepLength;
}
// 将小的值赋值给前面的索引
arr[insertIndex] = insertVal;
}
}
System.out.println("第" + (stepLength) + "次排序是 " + Arrays.toString(arr));
}
}
}
6、快速排序
通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
package com.example.datastructureandalgorithm.datastructure.sort;
import java.util.Arrays;
/* *
* @program: DataStructureAndAlgorithm
* @description
* @author: swq
* @create: 2021-04-27 23:20
**/
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));
}
private 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;
}
// l>=r 说明pivot的左右两边的值 已经按照左边全部是小于等于pivot的值 右边大于等于pivot的值
if (l >= r) {
break;
}
// 交换
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
// 如果交换之后 发现 arr[l] == pivot 则 r向前移动
if (arr[l] == pivot) {
r -= 1;
}
// 如果交换之后 发现 arr[r] == pivot 则 l向后移动
if (arr[r] == pivot) {
l += 1;
}
}
// 如果交换之后 发现 l==r 则 必须 l++,r--;
if (l == r) {
l += 1;
r -= 1;
}
// 向左递归
if (left < r) {
quickSort(arr, left, r );
}
// 向右递归
if (right > l) {
quickSort(arr, l, right);
}
}
}
7、归并排序
归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
package com.example.datastructureandalgorithm.datastructure.sort;
import java.util.Arrays;
/* *
* @program: DataStructureAndAlgorithm
* @description 归并排序
* @author: swq
* @create: 2021-04-29 22:29
**/
public class MergeSort {
public static void main(String[] args) {
int[] arr = {-9, 78, 0, 23, -567, 70};
int[] temp = new int[arr.length];
int left = 0;
int right = arr.length - 1;
mergeSort(arr, left, right, temp);
System.out.println(Arrays.toString(arr));
}
/**
* 分开 + 合并
*
* @param arr
* @param left
* @param right
* @param temp
*/
private 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);
// 合并
meger(arr, left, mid, right, temp);
}
}
/**
* 合并
*
* @param arr 数组
* @param left 左边索引
* @param mid 中值
* @param right 右边索引
* @param temp 临时数组
*/
private static void meger(int[] arr, int left, int mid, int right, int[] temp) {
int i = left; // 初始化i, 左边有序序列的初始索引
int j = mid + 1; //初始化j, 右边有序序列的初始索引
int t = 0; // 指向temp数组的当前索引
//(一)
//先把左右两边(有序)的数据按照规则填充到temp数组
//直到左右两边的有序序列,有一边处理完毕为止
while (i <= mid && j <= right) {
//如果左边的有序序列的当前元素,小于等于右边有序序列的当前元素
//即将左边的当前元素,填充到 temp数组
//然后 t++, i++
if (arr[i] <= arr[j]) {
temp[t] = arr[i];
i+=1;
t+=1;
} else { //反之,将右边有序序列的当前元素,填充到temp数组
temp[t] = arr[j];
j+=1;
t+=1;
}
}
//(二)
//把有剩余数据的一边的数据依次全部填充到temp
while (i <= mid) { //左边的有序序列还有剩余的元素,就全部填充到temp
temp[t] = arr[i];
i+=1;
t+=1;
}
while (j <= right) { //右边的有序序列还有剩余的元素,就全部填充到temp
temp[t] = arr[j];
j+=1;
t+=1;
}
//(三)
//将temp数组的元素拷贝到arr
//注意,并不是每次都拷贝所有
t = 0;
int tempLeft = left;
//第一次合并 tempLeft = 0 , right = 1 // tempLeft = 2 right = 3 // tL=0 ri=3
//最后一次 tempLeft = 0 right = 7
while (tempLeft <= right) {
arr[tempLeft] = temp[t];
t += 1;
tempLeft += 1;
}
}
}
8、基数排序
将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列
package com.example.datastructureandalgorithm.datastructure.sort;
import java.sql.SQLOutput;
import java.util.Arrays;
/* *
* @program: DataStructureAndAlgorithm
* @description
* @author: swq
* @create: 2021-05-02 09:47
**/
public class RadixSort {
public static void main(String[] args) {
int[] arr = {53, 3, 542, 748, 14, 214};
radixSort(arr);
}
/* *
* 基数排序
* @param arr
*/
private static void radixSort(int[] arr) {
// 首先获取最大值
int max = arr[0];
for (int value : arr) {
if (value > max) {
max = value;
}
}
// 获取最大值的长度
int length = (max + "").length();
// 创建二维数组
int[][] buckets = new int[10][arr.length];
//创建一维数组 存放的是每个桶中的个数
int[] bucketCounts = new int[10];
// 循环桶
for (int bucket = 0; bucket < length; bucket++) {
for (int arrIndex = 0; arrIndex < arr.length; arrIndex++) {
// 获取个 十 百 的数值
// 要存放桶下标的索引
int pow = (int) Math.pow(10, bucket);
int bucketIndex = (arr[arrIndex] / pow) % 10;
// 存放数据
buckets[bucketIndex][bucketCounts[bucketIndex]] = arr[arrIndex];
bucketCounts[bucketIndex]++;
}
// 进行数据取出的操作
// 循环遍历10个桶 如果有数据 则按照顺序取出
int bucketLength = buckets.length;
//存储数据的数组下表起始索引
// 循环桶
int index = 0;
for (int i = 0; i < bucketLength; i++) {
// 判断是否有数据
if (buckets[i] != null) {
// 有数据 则取出
// 获取该桶的元素个数
int bucketCount = bucketCounts[i];
for (int i1 = 0; i1 < bucketCount; i1++) {
arr[index] = buckets[i][i1];
index++;
}
}
// 重置装有每个桶个数的一维数组
bucketCounts[i] = 0;
}
System.out.println("第" + (bucket + 1) + "轮数据===========> " + Arrays.toString(arr));
}
}
}
9、堆排序
堆是具有一个的完全二叉树。
- 每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆, 注意 : 没有要求结点的左孩子的值和右孩子的值的大小关系。
- 每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
堆排序的基本思想是:
- 将待排序序列构造成一个大顶堆
- 此时,整个序列的最大值就是堆顶的根节点。
- 将其与末尾元素进行交换,此时末尾就为最大值。
- 然后将剩余 n-1 个元素重新构造成一个堆,这样会得到 n 个元素的次小值。如此反复执行,便能得到一个有序序列了。
例如:给你一个数组 {4,6,8,5,9} , 要求使用堆排序法,将数组升序排序
package com.example.datastructureandalgorithm.algorithm;
import java.util.Arrays;
/* *
* @program: DataStructureAndAlgorithm
* @description
* @author: swq
* @create: 2021-05-03 15:55
**/
public class HeapSort {
public static void main(String[] args) {
//要求将数组进行升序排序
int arr[] = {4, 6, 8, 5, 9};
heapSort(arr);
}
//编写一个堆排序的方法
public static void heapSort(int arr[]) {
// 临时变量
int temp = 0;
//将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆
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));
}
/**
* 调节堆大小
*
* @param arr 数组
* @param i 调整的起始索引
* @param length 长度
*/
public static void adjustHeap(int arr[], int i, int length) {
//先取出当前元素的值,保存在临时变量
int temp = arr[i];
// 循环遍历堆
for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {
//说明左子结点的值小于右子结点的值
if (k + 1 < length && arr[k] < arr[k + 1]) {
k++; // k改为指向右子结点
}
//如果子结点大于父结点
if (arr[k] > temp) {
//把较大的值赋给当前结点
arr[i] = arr[k];
// i 指向 k,继续循环比较
i = k;
}else {
break;
}
//当for 循环结束后,我们已经将以i 为父结点的树的最大值,放在了 最顶(局部)
arr[i] = temp;//将temp值放到调整后的位置
}
}
}