相邻两个数比较,如果逆序,则交换,可以想象有两个指针在辅助排序过程:
每一趟排序,数组最大的数就会确定,就像水泡冒出
因为每次循环后都会排序好的一个位置,最后一个数字不用排序,就是最小的那个。
public class MaoPao {
public static void main(String[] args) {
int[] num = new int[] { 3, 9, 5, 7, 2, 1, 8, 0, 10, -5, -10 };
int temp;
boolean flag = false;//用于优化,如果进行交换,则复制为true
//由于每趟排序都会有一个位置确定,所以最多num.length趟排序即可确定所有位置
for (int i = 0; i < num.length - 1; i++) {
//内层循环
//第一次循环:因为要对所有数字两两相邻比较大小,一共有num.length个数字,所以第一次需要比较 num.length - 1次
//第二次循环:第一次循环已经确定了最大数,剩下num.length - 1个数字,所以
//只要将除了最大数之外的其他数字两两相邻比较即可,即比较剩下数字 - 1次 = num.length - 2
//第三次循环:同上 ,比较次数为num.length - 3
// ...
//由于每次比较次数递减,而外层的i刚好递增,可以利用i变量多的值来控制比较次数:num.length - 1 - i
for (int j = 0; j < num.length - 1 - i; j++) {
if (num[j] > num[j + 1]) {
flag = true;
temp = num[j + 1];
num[j + 1] = num[j];
num[j] = temp;
}
}
if(!flag) {//在本趟中一次都没有交换过,则表示已完成排序,可以退出
break;
}else {
flag = false;
}
}
for (int i = 0; i < num.length; i++)
System.out.printf("%d ", num[i]);
}
}
public class SelectSort {
public static void main(String[] args) {
int[] num = new int[] { 3, 9, 5, 7, 2, 1, 8, 0, 10, -5, -10 };
int max = 0;//最小值的下标
int temp = 0;
//如果每次都选一个最小的放在最前面,则需要num.length - 1 次
for (int i = 0; i < num.length - 1; i++) {
//假定当前下标的数字为最小值
max = i;
//从后一个开始比较
for (int j = i + 1; j < num.length; j++) {
if (num[j] > num[max])
max = j;
}
// 如果小标发生改变,则交换
if (max != i) {
temp = num[max];
num[max] = num[i];
num[i] = temp;
}
}
for (int i = 0; i < num.length; i++)
System.out.printf("%d ", num[i]);
}
}
public class InsertSort {
public static void main(String[] args) {
int[] num = new int[] { 4, 1, 2, 3, -6, 8, 0, 10, -5, -10 };
System.out.println("初始");
System.out.println(Arrays.toString(num));
int inserValue;
int insertIndex;
for(int i = 1;i<num.length;i++) {
//记录要插入的数据
inserValue = num[i];
//从插入数据的前一个开始找插入的位置
insertIndex = i - 1;
//如果找到的位置不小于0且该位置上的数字大于要插入的数字,则该位置数据后移,继续查找
while (insertIndex >= 0 && num[insertIndex] > inserValue) {
//将数据右移(后移)
num[insertIndex+1] = num[insertIndex];
// System.out.println(Arrays.toString(num));
insertIndex--;
}
//如果跳出,要么是越界要么是找到了小于等于的数字
//如果越界,表示要插入的数字很小,直接插在头部
//如果找到了小于等于的数字,则插在该数字的后面
insertIndex++;
num[insertIndex] = inserValue;
System.out.println("第"+i+"轮");
System.out.println(Arrays.toString(num));
}
}
}
首先分组,例如有十个数字,就有5组,每个组内只用两个数字,虽然在数组里的顺序不相连,但是可以用间隔来模拟,对两个数字做排序是很简单的。
可以得知,当间隔的大小为1时,希尔排序就和插入排序(冒泡排序)一致了。
希尔排序内部可以使用冒泡和插入排序:
public class ShellSort {
public static void main(String[] args) {
int[] num = new int[] { 8, 9, 1, 7, 2, 3, 5, 4, 6, 0 };
shellSort(num);
}
public static void shellSort(int[] arr) {
int temp = 0;
//gap从数组长度的一半,之后再一步步减半,只要为0(int)
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
//当遍历到某一组时,利用间隔在该组使用冒泡排序(倒序)
for (int j = i - gap; j >= 0; j -= gap) {
if (arr[j] > arr[j + gap]) {
temp = arr[j + gap];
arr[j + gap] = arr[j];
arr[j] = temp;
}
}
}
}
System.out.println(Arrays.toString(arr));
}
}
public class ShellSort {
public static void main(String[] args) {
int[] num = new int[] { 8, 9, 1, 7, 2, 3, 5, 4, 6, 0 };
shellSort(num);
}
public static void shellSort2(int[] arr) {
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
//从第gap个元素开始,逐个对其所在的组进行直接插入排序
for (int i = gap; i < arr.length; i++) {
// 变量j用来辅助移动数据
int j = i;
int temp = arr[j];
//arr[j-gap]表示组内前一个元素
if(arr[j] < arr[j-gap]) {
while(j-gap >=0 && temp < arr[j-gap]) {
//移动,覆盖
arr[j] = arr[j-gap];
j-=gap;
}
//当退出while后,就给temp找到出入的位置
arr[j] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
}
}
图解:
首先记录第一个数字为中间值
定义一个start指向开头,end指向末尾,从末尾开始移动
public class QuickSort {
public static void main(String[] args) {
int[] arr = new int[] { 3, 4, 6, 7, 2, 7, 2, 8, 0 };
quickSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
public static void quickSort(int[] arr, int start, int end) {
//递归退出条件
if(!(start<end)) {
return;
}
// 把数组中第0个数字作为标准数
// 记录该数据
int started = arr[start];
// 记录需要排序的下标
int low = start;
int high = end;
while (low < high) {
// 从后面开始找一个小的
// 保证high的大小比low大,不等于,这样可以保证出了while后
// high值不越界
// 如果后面的比标准数大于或等于,则继续向前找
while (high > low && arr[high] >= started) {
// 向前找
high--;
}
// 找到小的
// 开始复制到前面low的位置
arr[low] = arr[high];
// 保持low小于high
// 要找一个大的等于的放到后面
// 所以遇到小于的就要过滤
while (low < high && arr[low] < started) {
low++;
}
// 如果出来了,就说明找到了
arr[high] = arr[low];
}
// 由于一开始低位向高位移动,高位向低位移动,如果能够跳出循环,则表示两个下标重合
// 将标准数插入即可
arr[low] = started;
quickSort(arr, start, low);
quickSort(arr, low + 1, end);
}
}
public class MergeSort {
public static void main(String[] args) {
int[] arr = new int[] { 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) {
// 每次合并两个有序序列,左右各一个
// 左边有序序列的初始索引
int i = left;
// 右边有序序列初始索引
int j = mid + 1;
// temp数组的初始化索引
int t = 0;
// 将左右两边有序数组的诗句按照规则填充到temp数组里
// 直到左右边的有序序列,有一边处理完成为止
while (i <= mid && j <= right) {
if (arr[i] < arr[j]) {
temp[t] = arr[i];
i++;
} else {
temp[t] = arr[j];
j++;
}
t++;
}
// 把有剩余数据的一边的数据依次全部填充到temp
// if (i > mid && j <= right) {
while (j <= right) {
temp[t] = arr[j];
t++;
j++;
}
// }
// if (i <= mid && j > right) {
while (i <= mid) {
temp[t] = arr[i];
t++;
i++;
}
// }
// 将temp数组的元素拷贝到arr中
// 注意,并不是每次都拷贝所有
t = 0;
int templeft = left;
// 第一次合并templeft = 0,right = 1
// 最后一次templeft = 0,right = 7
while (templeft <= right) {
arr[templeft] = temp[t];
t++;
templeft++;
}
}
}
public class RadixSort {
public static void main(String[] args) {
int arr[] = { 53, 3, 542, 748, 14, 214 };
radixSort(arr);
}
public static void radixSort(int[] arr) {
// 得到数组中最大数字的位数
int max = arr[0];
for (int i = 0; i < arr.length; i++) {
if (max < arr[i])
max = arr[i];
}
// 获得最大数的位数
int length = (max + "").length();
// 定义一个二维数组,表示是个桶,每个桶就是一个一位数组
// 说明
// 1.二维数组包含10个一位数组
// 2.为了防止在放入数的时候,数据溢出,则被一个一维数组,大小为arr.length
int[][] bucket = new int[10][arr.length];
// 为了记录每个桶中的,实际放了多少数据,我们定义一个一维数组来记录各个桶的每次放入的数据个数
int[] bucketElementCounts = new int[10];
//用于获取每个位的数字
int n = 1;
while (0 < length) {
for (int i = 0; i < arr.length; i++) {
int digitOfElement = arr[i] / n % 10;
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[i];
bucketElementCounts[digitOfElement]++;
}
int index = 0;
for (int i = 0; i < bucket.length; i++) {
for (int j = 0; j < bucketElementCounts[i]; j++) {
arr[index] = bucket[i][j];
index++;
}
//在每次使用完桶后,将桶的内容清0
bucketElementCounts[i] = 0;
}
n *= 10;
length --;
}
System.out.println(Arrays.toString(arr));
}
}