排序算法是计算机科学中的一类常见算法,用于将一组数据按照特定的顺序进行排列;排序算法的应用非常广泛,涉及到数据处理、搜索算法、数据库操作等众多领域。常见的排序算法有以下几种:
奈何本人技术不太行,对于树和堆的理解不够透彻,所以涉及到这两种数据结构的排序算法可能要以后再补充
文章使用 Java语言 进行演示,使用 int数据 类型作为示范,操作的数据结构均以 数组 为准,排序的结果为 从小到大 排列,同时提供随机初始化数据的方法
相关的全局变量和初始化数据方法如下
public static int number;//用例数据量
public static int[] nums;//用例数组
/**
* 用例随机初始化
* @param number 用例数据量
* @param start 用例起始范围
* @param end 用例结束范围
*/
public static void initNums(int number, int start, int end) {
nums = new int[number];
for (int i = 0; i < nums.length; i++) {
nums[i] = (int) (Math.random() * (end - start + 1) + start);
}
}
冒泡排序是一种简单直观的排序算法,它通过依次比较相邻的元素,并在必要时交换位置,使得每一轮循环后最大(或最小)的元素浮到序列的末尾。冒泡排序的过程可以描述如下:
/**
* 冒泡排序:从左到右逐个比较相邻的元素,并进行交换,每一轮将最大的元素移到最右侧
*/
public static void bubbleSort() {
int[] bubbleSortNums = nums.clone();
for (int i = 1; i < bubbleSortNums.length; i++) {
for (int j = 0; j < bubbleSortNums.length - i; j++) {
if (bubbleSortNums[j] > bubbleSortNums[j + 1]) {//已经确保两个数不相同,所以异或不会丢失数据
bubbleSortNums[j] ^= bubbleSortNums[j + 1];
bubbleSortNums[j + 1] ^= bubbleSortNums[j];
bubbleSortNums[j] ^= bubbleSortNums[j + 1];
}
}
}
System.out.print("冒泡排序: ");
System.out.println(Arrays.toString(bubbleSortNums));
}
冒泡排序的时间复杂度为O(N2),其中N为待排序元素的数量
插入排序是一种简单直观的排序算法,它的工作原理类似于整理扑克牌的过程。插入排序的基本思想是将待排序的元素逐个插入到已经排序的序列中的正确位置,从而逐步构建有序序列。下面是插入排序的步骤:
/**
* 插入排序:逐个将元素插入到已排序的序列中的正确位置,构建最终有序序列
*/
public static void insertSort() {
int[] insertSortNums = nums.clone();
int temp = 0;//记录待插入的值
int index = 0;//记录插入的位置
boolean isFlag;
for (int i = 1; i < insertSortNums.length; i++) {
isFlag = false;
for (int j = 0; j < i; j++) {
if (insertSortNums[i] < insertSortNums[j]) {
index = j;
temp = insertSortNums[i];
isFlag = true;
break;
}
}
if (isFlag) {//当需要向前插入的时候才执行,否则不用移动
System.arraycopy(insertSortNums, index, insertSortNums, index + 1, i - index);
// for (int j = i; j > index; j--) {
// insertSortNums[j] = insertSortNums[j - 1];
// }
insertSortNums[index] = temp;
}
}
System.out.print("插入排序: ");
System.out.println(Arrays.toString(insertSortNums));
}
插入排序的时间复杂度为O(N2),其中N为待排序元素的数量
选择排序是一种简单直观的排序算法,其基本思想是每一次从待排序的元素中选择最小(或最大)的元素,放置在已排序序列的末尾,从而逐步构建有序序列。选择排序的步骤如下:
/**
* 选择排序:每一轮从未排序的部分选择最小(或最大)的元素,放到已排序部分的末尾
*/
public static void selectSort() {
int[] selectionSortNums = nums.clone();
for (int i = 0; i < selectionSortNums.length - 1; i++) {//计数
int maxNum = 0;//记录极大值的索引
for (int j = 0; j < selectionSortNums.length - i; j++) {
if (selectionSortNums[maxNum] < selectionSortNums[j]) {
maxNum = j;
}
}
//下面的if是用异或交换元素的重点,防止置0
if (maxNum != selectionSortNums.length - i - 1) {//如果已经在对应位置就不需要移动,否则会导致异或同一个变量置0
selectionSortNums[maxNum] ^= selectionSortNums[selectionSortNums.length - i - 1];
selectionSortNums[selectionSortNums.length - i - 1] ^= selectionSortNums[maxNum];
selectionSortNums[maxNum] ^= selectionSortNums[selectionSortNums.length - i - 1];
}
}
System.out.print("选择排序: ");
System.out.println(Arrays.toString(selectionSortNums));
}
选择排序的时间复杂度为O(N2),其中N为待排序元素的数量
快速排序是一种高效的排序算法,它采用分治法的思想。快速排序的基本思想是选择一个基准元素,将待排序序列划分为左右两个子序列,其中左边子序列的元素都小于等于基准元素,右边子序列的元素都大于等于基准元素,然后递归地对左右子序列进行排序,最终得到有序序列。快速排序的步骤如下:
快速排序(双指针法)动画演示
/**
* 快速排序:选择一个基准元素,将数组分为小于基准值和大于基准值的两部分,递归地对这两部分进行排序(默认首元素为基准)
*/
public static void quickSort() {
int[] quickSortNums = nums.clone();
quick(quickSortNums, 0, quickSortNums.length - 1);
System.out.print("快速排序: ");
System.out.println(Arrays.toString(quickSortNums));
}
/**
* 快速排序:选择一个基准元素,将数组分为小于基准值和大于基准值的两部分,递归地对这两部分进行排序(默认首元素为基准)
* @param quickSortNums 待排序数组
* @param start 左起点
* @param end 右起点
*/
public static void quick(int[] quickSortNums, int start, int end) {
if (start < end) {//直到序列内只有一个元素时,无需排序,结束递归
int left = start;
int right = end;
int pivotIndex = start;//基准元素的索引值(选择第一个元素作为基准元素)
//寻找基轴位置
while (left < right) {
//右侧扫描
while (left < right && quickSortNums[right] >= quickSortNums[pivotIndex]) {//找到第一个小于基准的元素
right--;
}//和基轴交换位置
if (left < right) {//保证小于基准的元素在右侧
quickSortNums[right] ^= quickSortNums[pivotIndex];
quickSortNums[pivotIndex] ^= quickSortNums[right];
quickSortNums[right] ^= quickSortNums[pivotIndex];
pivotIndex = right;
}
//左侧扫描
while (left < right && quickSortNums[left] <= quickSortNums[pivotIndex]) {//找到第一个大于基准的元素
left++;
}//和基轴交换位置
if (left < right) {//保证大于基准的元素在左侧
quickSortNums[left] ^= quickSortNums[pivotIndex];
quickSortNums[pivotIndex] ^= quickSortNums[left];
quickSortNums[left] ^= quickSortNums[pivotIndex];
pivotIndex = left;
}
}
//左右子序列递归排序
quick(quickSortNums, start, pivotIndex - 1);
quick(quickSortNums, pivotIndex + 1, end);
}
}
快速排序的时间复杂度为O(NlogN),其中N为待排序元素的数量
归并排序是一种高效的排序算法,它采用分治法的思想。归并排序的基本思想是将待排序序列分成两个较小的子序列,递归地对子序列进行排序,然后将两个有序子序列合并成一个有序序列。以下是归并排序的算法实现步骤:
/**
* 归并排序:将数组递归地分成两半,对每个子数组进行排序,然后将两个有序子数组合并成一个有序数组
*/
public static void mergeSort() {
int[] mergeSortNums = nums.clone();
merge(mergeSortNums, 0, mergeSortNums.length - 1);
System.out.print("归并排序: ");
System.out.println(Arrays.toString(mergeSortNums));
}
/**
* 归并排序:将数组递归地分成两半,对每个子数组进行排序,然后将两个有序子数组合并成一个有序数组
* @param mergeSortNums 子序列
* @param start 子序列起点
* @param end 子序列终点
*/
public static void merge(int[] mergeSortNums, int start, int end) {
if (start < end) {
int middle = (end - start) / 2 + start;
merge(mergeSortNums, start, middle);
merge(mergeSortNums, middle + 1, end);
//合并两个子序列,暂存到临时数组
int num = start;
int i = start;
int j = middle + 1;
int[] temp = new int[mergeSortNums.length];
while (i <= middle && j <= end) {
if (mergeSortNums[i] <= mergeSortNums[j]) {
temp[num++] = mergeSortNums[i++];
} else {
temp[num++] = mergeSortNums[j++];
}
}
while (i <= middle) {
temp[num++] = mergeSortNums[i++];
}
while (j <= end) {
temp[num++] = mergeSortNums[j++];
}
//拷贝回初始数组
System.arraycopy(temp, start, mergeSortNums, start, end - start + 1);
}
}
归并排序的时间复杂度为O(NlogN),其中N为待排序元素的数量
计数排序是一种非比较的排序算法,它通过统计待排序序列中每个元素出现的次数,进而确定每个元素在有序序列中的位置。计数排序适用于待排序序列中的元素都是非负整数,并且元素的范围相对较小的情况。计数排序的步骤如下:
/**
* 计数排序:对每个输入元素进行计数,然后根据计数值的顺序重构有序数组
* @param start 数据左起始范围
* @param end 数据右起始范围
*/
public static void countSort(int start, int end) {
int[] countSortNums = nums.clone();
int[][] numCount = new int[Math.abs(start) + Math.abs(end) + 1][2];//计数
int temp = start;
for (int i = 0; i < numCount.length; i++) {
numCount[i][0] = temp++;
}
for (int i = 0; i < countSortNums.length; i++) {
numCount[countSortNums[i] + Math.abs(start)][1]++;
}
int index = 0;
int indexNum = 0;
for (int i = 0; i < numCount.length; i++) {
if (numCount[i][1] != 0 && indexNum != 0) {
numCount[i][1] += numCount[index][1];
index = i;
} else if (numCount[i][1] != 0 && indexNum == 0) {
indexNum++;
index = i;
}
}//计算排序位置
int left = 0;
int right = -1;
for (int j = 0; j < numCount.length; j++) {
if (numCount[j][1] != 0) {
right = numCount[j][1];
for (int i = left; i < right; i++) {
countSortNums[i] = numCount[j][0];
}
left = right;
}
}
System.out.print("计数排序: ");
System.out.println(Arrays.toString(countSortNums));
}
package Yskysoar;
import java.util.Arrays;
import java.util.Scanner;
/**
* @author Yskysoar
* @createTime 2023-07-12 19:29
* @description 排序算法
* 1.所有测试对象为数组、链表或者树,默认为数组
* 2.提供用例随机初始化方法
* 3.排序结果从小到大排列
*/
public class SortAlgorithm {
public static int number;//用例数据量
public static int[] nums;//用例数组
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入数据量,数据起点,数据终点:");
number = scanner.nextInt();
int start = scanner.nextInt();
int end = scanner.nextInt();
// nums = new int[] {3, 8, 10, 5, 10, 10, 3, 4, 2, 9};
initNums(number, start, end);
System.out.print("初始数据: ");
System.out.println(Arrays.toString(nums));
//冒泡排序
bubbleSort();
//选择排序
selectSort();
//插入排序
insertSort();
//快速排序
quickSort();
//归并排序
mergeSort();
//计数排序
countSort(start, end);
}
/**
* 用例随机初始化
* @param number 用例数据量
* @param start 用例起始范围
* @param end 用例结束范围
*/
public static void initNums(int number, int start, int end) {
nums = new int[number];
for (int i = 0; i < nums.length; i++) {
nums[i] = (int) (Math.random() * (end - start + 1) + start);
}
}
/**
* 冒泡排序:从左到右逐个比较相邻的元素,并进行交换,每一轮将最大的元素移到最右侧
*/
public static void bubbleSort() {
int[] bubbleSortNums = nums.clone();
for (int i = 1; i < bubbleSortNums.length; i++) {
for (int j = 0; j < bubbleSortNums.length - i; j++) {
if (bubbleSortNums[j] > bubbleSortNums[j + 1]) {//已经确保两个数不相同,所以异或不会丢失数据
bubbleSortNums[j] ^= bubbleSortNums[j + 1];
bubbleSortNums[j + 1] ^= bubbleSortNums[j];
bubbleSortNums[j] ^= bubbleSortNums[j + 1];
}
}
}
System.out.print("冒泡排序: ");
System.out.println(Arrays.toString(bubbleSortNums));
}
/**
* 选择排序:每一轮从未排序的部分选择最小(或最大)的元素,放到已排序部分的末尾
*/
public static void selectSort() {
int[] selectionSortNums = nums.clone();
for (int i = 0; i < selectionSortNums.length - 1; i++) {//计数
int maxNum = 0;//记录极大值的索引
for (int j = 0; j < selectionSortNums.length - i; j++) {
if (selectionSortNums[maxNum] < selectionSortNums[j]) {
maxNum = j;
}
}
//下面的if是用异或交换元素的重点,防止置0
if (maxNum != selectionSortNums.length - i - 1) {//如果已经在对应位置就不需要移动,否则会导致异或同一个变量置0
selectionSortNums[maxNum] ^= selectionSortNums[selectionSortNums.length - i - 1];
selectionSortNums[selectionSortNums.length - i - 1] ^= selectionSortNums[maxNum];
selectionSortNums[maxNum] ^= selectionSortNums[selectionSortNums.length - i - 1];
}
}
System.out.print("选择排序: ");
System.out.println(Arrays.toString(selectionSortNums));
}
/**
* 插入排序:逐个将元素插入到已排序的序列中的正确位置,构建最终有序序列
*/
public static void insertSort() {
int[] insertSortNums = nums.clone();
int temp = 0;//记录待插入的值
int index = 0;//记录插入的位置
boolean isFlag;
for (int i = 1; i < insertSortNums.length; i++) {
isFlag = false;
for (int j = 0; j < i; j++) {
if (insertSortNums[i] < insertSortNums[j]) {
index = j;
temp = insertSortNums[i];
isFlag = true;
break;
}
}
if (isFlag) {//当需要向前插入的时候才执行,否则不用移动
/*
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
src:源数组
srcPos:源数组要复制的起始位置
dest:目的数组
destPos:目的数组放置的起始位置
length:复制的长度
*/
System.arraycopy(insertSortNums, index, insertSortNums, index + 1, i - index);
// for (int j = i; j > index; j--) {
// insertSortNums[j] = insertSortNums[j - 1];
// }
insertSortNums[index] = temp;
}
}
System.out.print("插入排序: ");
System.out.println(Arrays.toString(insertSortNums));
}
/**
* 快速排序:选择一个基准元素,将数组分为小于基准值和大于基准值的两部分,递归地对这两部分进行排序(默认首元素为基准)
*/
public static void quickSort() {
int[] quickSortNums = nums.clone();
quick(quickSortNums, 0, quickSortNums.length - 1);
System.out.print("快速排序: ");
System.out.println(Arrays.toString(quickSortNums));
}
/**
* 快速排序:选择一个基准元素,将数组分为小于基准值和大于基准值的两部分,递归地对这两部分进行排序(默认首元素为基准)
* @param quickSortNums 待排序数组
* @param start 左起点
* @param end 右起点
*/
public static void quick(int[] quickSortNums, int start, int end) {
if (start < end) {//直到序列内只有一个元素时,无需排序,结束递归
int left = start;
int right = end;
int pivotIndex = start;//基准元素的索引值(选择第一个元素作为基准元素)
//寻找基轴位置
while (left < right) {
//右侧扫描
while (left < right && quickSortNums[right] >= quickSortNums[pivotIndex]) {//找到第一个小于基准的元素
right--;
}//和基轴交换位置
if (left < right) {//保证小于基准的元素在右侧
quickSortNums[right] ^= quickSortNums[pivotIndex];
quickSortNums[pivotIndex] ^= quickSortNums[right];
quickSortNums[right] ^= quickSortNums[pivotIndex];
pivotIndex = right;
}
//左侧扫描
while (left < right && quickSortNums[left] <= quickSortNums[pivotIndex]) {//找到第一个大于基准的元素
left++;
}//和基轴交换位置
if (left < right) {//保证大于基准的元素在左侧
quickSortNums[left] ^= quickSortNums[pivotIndex];
quickSortNums[pivotIndex] ^= quickSortNums[left];
quickSortNums[left] ^= quickSortNums[pivotIndex];
pivotIndex = left;
}
}
//左右子序列递归排序
quick(quickSortNums, start, pivotIndex - 1);
quick(quickSortNums, pivotIndex + 1, end);
}
}
/**
* 归并排序:将数组递归地分成两半,对每个子数组进行排序,然后将两个有序子数组合并成一个有序数组
*/
public static void mergeSort() {
int[] mergeSortNums = nums.clone();
merge(mergeSortNums, 0, mergeSortNums.length - 1);
System.out.print("归并排序: ");
System.out.println(Arrays.toString(mergeSortNums));
}
/**
* 归并排序:将数组递归地分成两半,对每个子数组进行排序,然后将两个有序子数组合并成一个有序数组
* @param mergeSortNums 子序列
* @param start 子序列起点
* @param end 子序列终点
*/
public static void merge(int[] mergeSortNums, int start, int end) {
if (start < end) {
int middle = (end - start) / 2 + start;
merge(mergeSortNums, start, middle);
merge(mergeSortNums, middle + 1, end);
//合并两个子序列,暂存到临时数组
int num = start;
int i = start;
int j = middle + 1;
int[] temp = new int[mergeSortNums.length];
while (i <= middle && j <= end) {
if (mergeSortNums[i] <= mergeSortNums[j]) {
temp[num++] = mergeSortNums[i++];
} else {
temp[num++] = mergeSortNums[j++];
}
}
while (i <= middle) {
temp[num++] = mergeSortNums[i++];
}
while (j <= end) {
temp[num++] = mergeSortNums[j++];
}
//拷贝回初始数组
System.arraycopy(temp, start, mergeSortNums, start, end - start + 1);
}
}
/**
* 计数排序:对每个输入元素进行计数,然后根据计数值的顺序重构有序数组
* @param start 数据左起始范围
* @param end 数据右起始范围
*/
public static void countSort(int start, int end) {
int[] countSortNums = nums.clone();
int[][] numCount = new int[Math.abs(start) + Math.abs(end) + 1][2];//计数
int temp = start;
for (int i = 0; i < numCount.length; i++) {
numCount[i][0] = temp++;
}
for (int i = 0; i < countSortNums.length; i++) {
numCount[countSortNums[i] + Math.abs(start)][1]++;
}
int index = 0;
int indexNum = 0;
for (int i = 0; i < numCount.length; i++) {
if (numCount[i][1] != 0 && indexNum != 0) {
numCount[i][1] += numCount[index][1];
index = i;
} else if (numCount[i][1] != 0 && indexNum == 0) {
indexNum++;
index = i;
}
}//计算排序位置
int left = 0;
int right = -1;
for (int j = 0; j < numCount.length; j++) {
if (numCount[j][1] != 0) {
right = numCount[j][1];
for (int i = left; i < right; i++) {
countSortNums[i] = numCount[j][0];
}
left = right;
}
}
System.out.print("计数排序: ");
System.out.println(Arrays.toString(countSortNums));
}
}
本人技术水平一般,各位大佬如果发现我的不足之处或者有好的建议可以指出,谢谢大家