排序算法
初级排序算法:
选择排序 从数组中选择最小元素,将它与数组的第一个元素交换位置。再从数组剩下的元素中选择出最小的元素,将它与数组的第二个元素交换位置。不断进行这样的操作,直到将整个数组排序。
特点:时间与输入数组的性质无关(已经很慢了),就算排好了也全走一遍
数据移动少,每次移动俩。
public class Selection<T extends Comparable<T>> extends Sort<T> {
@Override
public void sort(T[] nums) {
int N = nums.length;
for (int i = 0; i < N - 1; i++) {
int min = i;
for (int j = i + 1; j < N; j++) {
if (less(nums[j], nums[min])) {
min = j;
}
}
swap(nums, i, min);
}
}
}
冒泡排序 从左到右不断交换相邻逆序的元素,在一轮的循环之后,可以让未排序的最大元素上浮到右侧。在一轮循环中,如果没有发生交换,那么说明数组已经是有序的,此时可以直接退出。
public class Bubble<T extends Comparable<T>> extends Sort<T> {
@Override
public void sort(T[] nums) {
int N = nums.length;
boolean isSorted = false;
for (int i = N - 1; i > 0 && !isSorted; i--) {
isSorted = true;
for (int j = 0; j < i; j++) {
if (less(nums[j + 1], nums[j])) {
isSorted = false;
swap(nums, j, j + 1);
}
}
}
}
}
插入排序 从a[0]开始,与它前面的元素做比较,满足小于等于则做交换(将当前元素插入到左侧已经排序的数组),使得插入之后左侧数组依然有序。
特点:对部分有序的数组性能优越
适合小规模数组
public class Insertion<T extends Comparable<T>> extends Sort<T> {
@Override
public void sort(T[] nums) {
int N = nums.length;
for (int i = 1; i < N; i++) {
for (int j = i; j > 0 && less(nums[j], nums[j - 1]); j--) {
swap(nums, j, j - 1);
}
}
}
}
希尔排序 将h间隔内的数进行插入排序(跨着排),逐步减小h。其中h初始化为1,4,13,40(3*h+1)且满足N/3的最大值。每次h/3
特点:数组越大,优势越明显。
代码量小
无需额外空间
实际上是优化了的插入排序,减小低效交换次数加快速度
public class Shell<T extends Comparable<T>> extends Sort<T> {
@Override
public void sort(T[] nums) {
int N = nums.length;
int h = 1;
while (h < N / 3) {
h = 3 * h + 1; // 1, 4, 13, 40, ...
}
while (h >= 1) {
for (int i = h; i < N; i++) {
for (int j = i; j >= h && less(nums[j], nums[j - h]); j -= h) {
swap(nums, j, j - h);
}
}
h = h / 3;
}
}
}
归并排序 分为自顶向下与自底向上使用归并方法
public abstract class MergeSort<T extends Comparable<T>> extends Sort<T> {
protected T[] aux;
protected void merge(T[] nums, int l, int m, int h) {
int i = l, j = m + 1;
for (int k = l; k <= h; k++) {
aux[k] = nums[k]; // 将数据复制到辅助数组
}
for (int k = l; k <= h; k++) {
if (i > m) {
nums[k] = aux[j++];
} else if (j > h) {
nums[k] = aux[i++];
} else if (aux[i].compareTo(aux[j]) <= 0) {
nums[k] = aux[i++]; // 先进行这一步,保证稳定性
} else {
nums[k] = aux[j++];
}
}
}
}
自顶向下:递归分成小段排序,归并
public class Up2DownMergeSort<T extends Comparable<T>> extends MergeSort<T> { @Override public void sort(T[] nums) { aux = (T[]) new Comparable[nums.length]; sort(nums, 0, nums.length - 1); } private void sort(T[] nums, int l, int h) { if (h <= l) { return; } int mid = l + (h - l) / 2; sort(nums, l, mid); sort(nums, mid + 1, h); merge(nums, l, mid, h); } }
自底向上:先遍历,两两归并(最小的归并就是排序),子数组大小每次加倍。(不需要递归,代码量较少)
public class Down2UpMergeSort<T extends Comparable<T>> extends MergeSort<T> { @Override public void sort(T[] nums) { int N = nums.length; aux = (T[]) new Comparable[N]; for (int sz = 1; sz < N; sz += sz) { for (int lo = 0; lo < N - sz; lo += sz + sz) { merge(nums, lo, lo + sz - 1, Math.min(lo + sz + sz - 1, N - 1)); } } } }
特点:需要的额外空间与待排序数组大小N成正比(空间复杂度不佳)
最差情形与一般情形时间复杂度相同:都为NlgN,在基于比较排序的算法中渐进最优。
堆排序 应用优先队列对元素排序,建堆并且不断调整堆,不断取下最大值调整(递归)
特点:同时最好地利用时间空间的排序算法
代码简洁
无法使用缓存
快速排序 将待排的数组分为两个子数组,其中左子数组全小于中间元素,右子数组全大于中间元素。两个子数组分别排序
特点:内循环简洁,不在内循环中移动数据,速度快
比较次数较少,代码量少
问题较多,较为脆弱
偏爱随机性,排前打乱数组
改进版快排----三向切分(适用于待排序的数组中存在大量重复数据,开销由线性对数降低到线性),三个指针分三部分。

浙公网安备 33010602011771号