整理了常见的几种基础的排序算法,纯代码演示,拿来即用,以备临时面试、考试等快速复习。具体算法原理可以百度。
public class Sort {
public static void main(String[] args) {
int[] verifyNumbers = null;
try {
verifyNumbers = getRandomIntArray(20, 300);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("算法正确性校验");
System.out.println("原本:" + Arrays.toString(verifyNumbers));
System.out.println("冒泡:" + Arrays.toString(bubbleSort(Arrays.copyOf(verifyNumbers, verifyNumbers.length))));
System.out.println("插入:" + Arrays.toString(insertSort(Arrays.copyOf(verifyNumbers, verifyNumbers.length))));
System.out.println("选择:" + Arrays.toString(selectSort(Arrays.copyOf(verifyNumbers, verifyNumbers.length))));
int[] temp = Arrays.copyOf(verifyNumbers,verifyNumbers.length);
mergeSort(temp, 0, verifyNumbers.length - 1);
System.out.println("归并:" +Arrays.toString(temp));
int[] temp2 = Arrays.copyOf(verifyNumbers,verifyNumbers.length);
quickSort(temp2, 0, temp2.length - 1);
System.out.println("快速:" +Arrays.toString(temp2));
}
public static int[] getRandomIntArray(int count, int maxValue) throws Exception {
if (count < 1) throw new Exception("array length must over 0");
int[] numbers = new int[count];
for (int i = 0; i < count; ++i) {
numbers[i] = (int) (Math.random() * maxValue);
}
return numbers;
}
/**
* 冒泡排序:每次遍历数组时可将一个数移动到正确的位置
* 遍历时当前位置的值与下一个值进行比较判断是否需要交换位置
* 简单的优化策略:
* 1 如果一次遍历时没有发生移动则可结算循环(排序已完成)
* 2 不需要每次都遍历全部数组因为每次都会将一个数放到正确的位置,二次遍历时就不需要在遍历这个数了。
*/
public static int[] bubbleSort(int[] numbers) {
boolean finished;
//外层循环实际作用是控制循环次数(每次循环可将一个数移动到正确位置),length个数最多需要length-1次循环
for (int i=0; i < numbers.length - 1; ++i) {
finished = true;
//内部循环的作用是进行值比较和位置交换,每一次内部循环需要比较length-1-i次
for (int j = 0; j < numbers.length - 1 - i; ++j) {
if (numbers[j] > numbers[j + 1]) {
int temp = numbers[j];
numbers[j] = numbers[j + 1];
numbers[j + 1] = temp;
finished = false;
}
}
if (finished) break;
}
return numbers;
}
/**
* 选择排序:分已排序区间和未排序区间。选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。
* @param numbers
* @return
*/
public static int[] selectSort(int[] numbers) {
//控制循环轮次,length个数最多只需length-1次循环
for (int i = 0; i < numbers.length - 1; ++i) {
//找出未排序区间中最小的数
int index = i;
for (int j = i + 1; j < numbers.length; ++j) {
if (numbers[index] > numbers[j]) {
index = j;
}
}
//将最小值添加到已排序区间的末尾
int temp = numbers[index];
numbers[index] = numbers[i];
numbers[i] = temp;
}
return numbers;
}
/**
* 插入排序:将数组中的数据分为两个区间,已排序区间和未排序区间。初始已排序区间只有一个元素,就是数组的第一个元素,
* 插入算法的核心思想是取未排序区间中的元素,在已排序区间中找到合适的插入位置将其插入,并保证已排序区间数据一直有序。
* 重复这个过程,直到未排序区间中元素为空,算法结束。
* @return
*/
public static int[] insertSort(int[] numbers) {
//外部循环用于控制插入的次数(实际取决于为排序区间中的数值个数,
// 因为需要将每一个未排序区间中的数拿去与已排序区间中的数进行对比)
for (int i = 1; i < numbers.length; i++) {
int j = i - 1, value = numbers[i];
//内部循环用于将未排序区间的第一个数与已排序区间进行比较
//采用从尾到头的比较,有序区间的末尾就是有序区间中最大的值
for (;j >= 0; --j) {
//移动数据
if (numbers[j] > value) {
numbers[j + 1] = numbers[j];
} else {
break;
}
}
//插入数据
numbers[j + 1] = value;
}
return numbers;
}
/**
* 归并排序:把数组从中间分成前后两部分,然后对前后两部分分别排序,再将排好序的两部分合并在一起,这样整个数组就都有序了。
* 归并排序使用的就是分治思想,分治是一种解决问题的处理思想,递归是一种编程技巧.
* @param numbers
* @param startIndex
* @param endIndex
*/
public static void mergeSort(int[] numbers, int startIndex, int endIndex) {
//递归出口
if (startIndex >= endIndex) return;
//计算数组的中间元素下标
int middleIndex = (endIndex + startIndex) / 2;
//对左边进行排序
mergeSort(numbers, startIndex, middleIndex);
//对右边进行排序
mergeSort(numbers, middleIndex + 1, endIndex);
//合并左右有序数组
merge(numbers, startIndex, middleIndex, endIndex);
}
/**
* 合并两个有序数组
* @param numbers
* @param startIndex
* @param middleIndex
* @param endIndex
*/
private static void merge(int[] numbers, int startIndex, int middleIndex, int endIndex) {
//定义一个缓存数组
int[] buffer = new int[endIndex - startIndex + 1];
//从左边开始比较,左边的小于等于右边的就排在前面,这样原有数组中元素大小的相对位置不会改变,该算法就为稳定的排序算法
//左边有序数组number[startIndex]->number[middleIndex]
//右边有序数组number[middleIndex + 1]->number[endIndex]
//while中的条件是为了保证左边或右边的有序数组至少有一个遍历完
int l = startIndex, r = middleIndex + 1, i = 0;
while (l <= middleIndex && r <= endIndex) {
if (numbers[l] <= numbers[r]) {
buffer[i++] = numbers[l++];
} else {
buffer[i++] = numbers[r++];
}
}
//如果左边的有序数组未遍历完则直接提取出左边的元素
while (l <= middleIndex) {
buffer[i++] = numbers[l++];
}
//如果右边的有序数组未遍历完则直接提取出左边的元素
while (r <= endIndex) {
buffer[i++] = numbers[r++];
}
//赋值给原始数组
System.arraycopy(buffer, 0, numbers, startIndex, buffer.length);
}
/**
* 快速排序
* 关于快速排序的原理讲解可参考(挖坑理解):https://blog.youkuaiyun.com/morewindows/article/details/6684558
* (1) 从数列中挑出一个基准值。
* (2) 将所有比基准值小的摆放在基准前面,所有比基准值大的摆在基准的后面(相同的数可以到任一边);在这个分区退出之后,该基准就处于数列的中间位置。
* (3) 递归地把"基准值前面的子数列"和"基准值后面的子数列"进行排序。
* @param numbers
* @param startIndex
* @param endIndex
*/
public static void quickSort(int[] numbers, int startIndex, int endIndex) {
if (startIndex >= endIndex) return;
int pivot = numbers[startIndex];
int low = startIndex,high = endIndex;
while (low < high) {
while (high > low && numbers[high] >= pivot) {
high--;
}
numbers[low] = numbers[high];
while (low < high && numbers[low] <= pivot) {
low++;
}
numbers[high] = numbers[low];
}
numbers[low] = pivot;
quickSort(numbers,startIndex, low - 1);
quickSort(numbers, low + 1, endIndex);
}
}