根据排序时数据所占用存储器不同,排序可分为
- 内部排序:整个排序过程在内存进行
- 外部排序:需要借助外存
按逐步扩大记录有序序列长度的方法,可将内部排序分为五大类
- 插入类:将无序子序列中的一个或几个记录插入到有序序列中
- 选择类:从记录的无序子序列中选择关键字最小或最大的记录,并将其加入到有序子序列中
- 交换类:通过交换无序子序列中的记录从而得到其中关键字最小或最大的记录,并将其加入到有序子序列中
- 归并类:通过归并两个或两个以上的记录有序子序列
- 分配类:唯一不需要进行关键字之间比较的一类排序算法,它主要利用分配和收集两种基本操作实现整个排序过程
代码:gitee 仓库地址: gitee 仓库入口
1. 插入类排序
package com.algorithm.order.insert;
import java.util.Arrays;
/**
* 插入类排序。
*
* 思想: 在一个已经排好序的有序序列区内,对待排序列的无序序列区中记录逐个进行处理,每一步将一个待排序的记录与同组那些已经排好序的
* 记录进行比较,然后有序插入到该有序序列区中,直到所有待排记录全部插入为止。
*
* 直接插入排序
* 折半插入排序
* 希尔排序(缩小增量排序)
*
*
*/
public class InsertOrder {
/** 直接插入排序。 */
public static void directInsert(int[] nums) {
System.out.println("direct insert: " + Arrays.toString(nums));
if (nums == null || nums.length <= 1) {
return;
}
int tmp = 0;
for (int i = 1; i < nums.length; i++) {
if (nums[i] < nums[i-1]) {
tmp = nums[i];
int j = i - 1;
for (; j >= 0 && tmp < nums[j]; j--) {
nums[j+1] = nums[j];
}
nums[j+1] = tmp;
}
}
}
/** 折半插入排序。 */
public static void reduceByHalfInsert(int[] nums) {
System.out.println("reduce by half insert: " + Arrays.toString(nums));
if (nums == null || nums.length <= 1) {
return;
}
int tmp = 0;
for (int i = 1; i < nums.length; i++) {
if (nums[i] < nums[i-1]) {
tmp = nums[i];
int low = 0, high = i - 1;
while (low <= high) {
int mid = low + (high - low)/2;
if (nums[mid] > tmp) {
high = mid - 1;
} else {
low = mid + 1;
}
}
for (int j = i - 1; j >= low; j--) {
nums[j+1] = nums[j];
}
nums[low] = tmp;
}
}
}
/** 希尔排序。 */
public static void shellInsert(int[] nums) {
System.out.println("shell insert: " + Arrays.toString(nums));
for (int dt = nums.length/2; dt > 0; dt /= 2) {
for (int i = dt; i < nums.length; i++) {
int j = i;
while (j - dt >= 0 && nums[j] < nums[j - dt]) {
int tmp = nums[j];
nums[j] = nums[j - dt];
nums[j - dt] = tmp;
j -= dt;
}
}
}
}
/** 测试。 */
public static void main(String[] args) {
int[] nums = {
3, 9, 1, 0, 4, 6, 7, 5, 8, 2};
int[] result = {
};
// 直接插入排序
result = nums.clone();
directInsert(result);
System.out.println(Arrays.toString(result));
// 折半插入排序
result = Arrays.copyOf(nums, nums.length);
reduceByHalfInsert(result);
System.out.println(Arrays.toString(result));
// 希尔排序
result = Arrays.copyOf(nums, nums.length);
shellInsert(result);
System.out.println(Arrays.toString(result));
}
}
2. 选择类排序
package com.algorithm.order.select;
import java.util.Arrays;
/**
* 选择类排序
*
* 思想:在第 i 趟的记录序列中选取关键字第 i 小的记录作为有序序列的第 i 个记录。
* 关键:从剩余的待排序列中找出最小或最大的记录。
*
* 简单选择排序
* 树形选择排序
* 堆排序
*/
public class SelectOrder {
/** 简单选择排序。 */
public static void simpleSelect(int nums[]) {
System.out.println("simple select sort: " + Arrays.toString(nums));
if (nums == null || nums.length <= 1) {
return;
}
for (int i = 0; i < nums.length - 1; i++) {
int k = i;
for (int j = i + 1; j < nums.length; j++) {
if (nums[j] < nums[k]) {
k = j;
}
}
if (k != i) {
int tmp = nums[i];
nums[i] = nums[k];
nums[k] = tmp;
}
}
}
/**
* 树形选择排序。
* 思路:
* 1. 将数组的元素两两相比较,得到 n/2 个较小的元素 ;
* 2. 重复步骤1,直至取到最小的元素
* 3. 将剩余 n-1 个元素继续 1,2 步骤比较
*/
public static void treeSelect(int[] nums) {
System.out.println("tree select: " + Arrays.toString(nums));
if (nums == null || nums.length <= 1) {
return;
}
int len = nums.length, treeSize = 2 * len - 1; // 完全二叉树节点数
int[] tree = new int[treeSize];
int j = treeSize;
// 将数组放到 tree 中
for (int i = len - 1; i >= 0; i --) {
tree[j-1] = nums[i];
j--;
}
// 根据叶子节点两两比较,小的填充非叶子节点
for (int i = treeSize - 1; i > 0; i -= 2) {
tree[(i-1)/2] = tree[i] > tree[i-1] ? tree[i-1] : tree[i];
}
// 重复寻找最小值,将最小值按顺序放回原来的数组
int ii = 0;
while (ii < len) {
int min = tree[0];
int minIndex = 0;
// 确定最小值在 tree 中的位置
for (int i = treeSize - 1; i > 0; i--) {
if (min == tree[i]) {
minIndex = i;
// 将最小值设置为最大(设置为 Integer.MAX_VALUE)
tree[i] = Integer.MAX_VALUE;
break;
}
}
// 将最小值按顺序重新放回原来的数组
nums[ii++] = min;
// 找到其兄弟节点
while (minIndex > 0) {
if (minIndex % 2 == 0) {
tree[(minIndex-1)/2] = tree[minIndex] > tree[minIndex-1] ? tree[minIndex-1] : tree[minIndex];
minIndex = (minIndex-1)/2;
} else {
tree[minIndex/2] = tree[minIndex] > tree[minIndex+1] ? tree[minIndex+1] : tree[minIndex];
minIndex = minIndex/2;
}
}
}
}
/** 堆排序。 */
public static void heapSort(int[] nums) {
System.out.println("heap sort: " + Arrays.toString(nums));
if (nums == null || nums.length <= 1) {
return;
}
for (int i = nums.length/2 - 1; i >= 0; i--) {
heapAdjust(nums, i, nums.length);
}
for (int i = nums.length - 1; i > 0; i--) {
int tmp = nums[0];
nums[0] = nums[i];
nums[i] = tmp;
heapAdjust(nums, 0, i);
}
}
public static void heapAdjust(int[] nums, int index, int len) {
int tmp = nums[index];
for (int j = 2*index + 1; j < len; j = 2*j + 1) {
if (j + 1 < len && nums[j] < nums[j+1]) {
j++;
}
if (tmp >= nums[j]) {
break;
}
nums[index] = nums[j];
index = j;
}
nums[index] = tmp;
}
/** 测试。 */
public static void main(String[] args) {
int[] nums = {
3, 9, 1, 0, 4, 6, 7, 5, 2, 8};
int[] result = {
};
// 简单选择排序
result = nums.clone();
simpleSelect(result);
System.out.println(Arrays.toString(result));
// 树形选择排序
result = nums.clone();
treeSelect(result);
System.out.println(Arrays.toString(result));
// 堆排序
result = nums.clone();
heapSort(result);
System.out.println(Arrays.toString(result));
}
}
3. 交换类排序
package com.algorithm.order.swap;
import java.util.Arrays;
/**
* 交换类排序
*
* 思想:对代排序记录对关键字两两比较,只要发现两个记录为逆序就进行交换,直到没有逆序对记录为止。
*
* 冒泡排序
* 快速排序
*/
public class SwapOrder {
/** 冒泡排序。 */
public static void bubbleSort(int nums[]) {
System.out.println("bubble sort: " + Arrays.toString(nums));
if (nums == null || nums.length <= 1) {
return;
}
boolean flag = true;
for (int i = 0; i < nums.length - 1 && flag; i++) {
flag = false;
for (int j = 0; j < nums.length - i - 1; j++) {
if (nums[j+1] < nums[j]) {
int tmp = nums[j];
nums[j] = nums[j+1];
nums[j+1] = tmp;
flag = true;
}
}
}
}
/** 快速排序。 */
public static void quickSort(int[] nums) {
System.out.println("quick sort: " + Arrays.toString(nums));
if (nums == null || nums.length <= 1) {
return;
}
int low = 0, high = nums.length - 1;
if (low < high) {
int pos = quickSortOne(nums, low, high);
quickSortOne(nums, 0, pos - 1);
quickSortOne(nums, pos + 1, high);
}
}
private static int quickSortOne(int[] nums, int low, int high) {
int key = nums[low];
while (low < high) {
while (low < high && nums[high] >= key) {
high --;
}
nums[low] = nums[high];
while (low < high && nums[low] < key) {
low ++;
}
nums[high] = nums[low];
}
nums[low] = key;
return low;
}
/** 测试。 */
public static void main(String[] args) {
int[] nums = {
3, 9, 1, 0, 4, 6, 7, 5, 8, 2};
int[] result = {
};
// 冒泡排序
result = nums.clone();
bubbleSort(result);
System.out.println(Arrays.toString(result));
// 快速排序
result = nums.clone();
quickSort(result);
System.out.println(Arrays.toString(result));
}
}
4. 归并类排序
package com.algorithm.order.merge;
import java.util.Arrays;
/**
* 归并类排序。
*
* 思想:首先将原始无序序列划分为两个子序列,然后分别对每个子序列递归地进行排序,然后再将有序子序列合并。
* 二路归并排序
* 自然归并排序
*/
public class MergeSort {
/** 二路归并排序。 */
public static void twoWayMerge(int[] nums) {
System.out.println("two way merge sort: " + Arrays.toString(nums));
if (nums == null || nums.length <= 1) {
return;
}
twoWayMergeDivide(nums, 0, nums.length - 1);
}
private static void twoWayMergeDivide(int[] nums, int low, int high) {
if (low < high) {
int middle = (low + high) / 2;
twoWayMergeDivide(nums, low, middle);
twoWayMergeDivide(nums, middle + 1, high);
twoWayMergeConquer(nums, low, middle, high);
}
}
private static void twoWayMergeConquer(int[] nums, int low, int middle, int high) {
int i = low; // 左半部有序序列的起始位置
int j = middle + 1; // 右半部有序序列的起始位置
int index = low;
int[] tmp = Arrays.copyOf(nums, nums.length);
while (i <= middle && j <= high) {
nums[index++] = tmp[i] <= tmp[j] ? tmp[i++] : tmp[j++];
}
while (i <= middle) {
nums[index++] = tmp[i++];
}
while (j <= high) {
nums[index++] = tmp[j++];
}
}
/** 自然归并排序。 */
public static void nativeMerge(int[] nums) {
System.out.println("two way merge sort: " + Arrays.toString(nums));
if (nums == null || nums.length <= 1) {
return;
}
int i = 0, sum = 0, low = 0, mid = 0, high = 0;
int len = nums.length;
while (true) {
i = 0;
sum = 1;
while (i < len - 1) {
low = i;
while (i < len - 1 && nums[i] < nums[i+1]) {
i++;
}
mid = i++;
while (i < len - 1 && nums[i] < nums[i+1]) {
i++;
}
high = i++;
if (i <= len) {
merge(nums, low, mid, high);
sum ++;
}
}
if (sum == 1) {
break;
}
}
}
private static void merge(int[] nums, int low, int mid, int high) {
int i = low, j = mid+1, p = low, q = 0;
int[] tmp = Arrays.copyOf(nums, nums.length);
while (i <= mid && j <= high) {
nums[p++] = tmp[i] <= tmp[j] ? tmp[i++] : tmp[j++];
}
int tmpHighIndex = 0;
if (i > mid) {
q = j;
tmpHighIndex = high;
} else {
q = i;
tmpHighIndex = mid;
}
for (; q <= tmpHighIndex; q++) {
nums[p++] = tmp[q];
}
}
/** 测试。 */
public static void main(String[] args) {
int[] nums = {
3, 9, 1, 0, 4, 6, 7, 5, 2, 8};
int[] result = {
};
// 二路归并排序
result = nums.clone();
twoWayMerge(result);
System.out.println(Arrays.toString(result));
// 自然归并排序
result = nums.clone();
nativeMerge(result);
System.out.println(Arrays.toString(result));
}
}
5. 分配类排序
package com.algorithm.order.distribution;
import java.util.*;
/**
* 分配类排序。
*
* 思想:利用分配和收集来完成排序,不需要进行关键字比较。
*
* 桶排序
* 基数排序
*/
public class DistributeSort {
/** 桶排序。 */
public static void bucketSort(int[] nums) {
System.out.println("two way merge sort: " + Arrays.toString(nums));
if (nums == null || nums.length <= 1) {
return;
}
ArrayList<LinkedList<Integer>> buckets = new ArrayList<>();
int max = nums[0];
for (int i = 0; i < nums.length; i++) {
max = Math.max(nums[i], max);
}
for (int i = 0; i <= max; i++) {
buckets.add(new LinkedList<>());
}
for (int i = 0; i < nums.length; i++) {
int item = nums[i];
insertBucket(buckets.get(item), item);
}
for (int i = 0, j = 0; i <= max; i++) {
List<Integer> list = buckets.get(i);
if (list != null) {
ListIterator<Integer> iterator = list.listIterator();
while (iterator.hasNext()) {
nums[j++] = iterator.next();
}
}
}
}
private static void insertBucket(List<Integer> bucket, int num) {
ListIterator<Integer> iterator = bucket.listIterator();
boolean flag = false;
while (iterator.hasNext()) {
if (num <= iterator.next()) {
iterator.previous();
iterator.add(num);
flag = true;
break;
}
}
if (!flag) {
bucket.add(num);
}
}
/** 链式基数排序。 */
public static void radixSort(int[] nums) {
System.out.println("radix sort: " + Arrays.toString(nums));
if (nums == null || nums.length <= 1) {
return;
}
ArrayList<LinkedList<Integer>> buckets = new ArrayList<>();
for (int i = 0; i < 10; i++) {
buckets.add(new LinkedList<>());
}
int maxLen = 0;
for (int i = 0; i < nums.length; i++) {
maxLen = Math.max(String.valueOf(nums[i]).length(), maxLen);
}
for (int i = 0; i < maxLen; i++ ) {
// 分配
for (int j = 0; j < nums.length; j++) {
int num = (nums[j] % (int) Math.pow(10, i+1)) / (int) Math.pow(10, i);
insertBucket(buckets.get(num), nums[j]);
}
// 收集
for (int k = 0, j = 0; k < 10; k++) {
List<Integer> list = buckets.get(k);
if (list != null) {
ListIterator<Integer> iterator = list.listIterator();
while (iterator.hasNext()) {
nums[j++] = iterator.next();
iterator.remove();
}
}
}
}
}
/** 测试。 */
public static void main(String[] args) {
int[] nums = {
3, 9, 1, 0, 4, 6, 7, 5, 2, 8};
int[] result = {
};
// 桶排序排序
result = nums.clone();
bucketSort(result);
System.out.println(Arrays.toString(result));
// 链式基数排序
result = nums.clone();
radixSort(result);
System.out.println(Arrays.toString(result));
nums = new int[] {
345, 92, 1, 0, 2224, 46, 37, 45, 12, 8444};
// 桶排序排序
result = nums.clone();
bucketSort(result);
System.out.println(Arrays.toString(result));
// 链式基数排序
result = nums.clone();
radixSort(result);
System.out.println(Arrays.toString(result));
}
}