一.直接插入排序
基本思想:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。
代码实现:
public void insertSort(int[] array) {
for (int i = 1; i < array.length; i++) {
int tmp = array[i];
int j = 0;
for (j = i - 1; j >= 0 ; j--) {
if (array[j] > tmp) {
array[j + 1] = array[j];
}
else {
break;
}
}
array[j + 1] = tmp;
}
}
直接插入排序的特性总结:
- 元素集合越接近有序,直接插入排序算法的时间效率越高
- 时间复杂度:平均速度和最坏情况速度都为O(n^2)
- 空间复杂度:O(1)
- 稳定性:稳定
二.希尔排序
基本思想:先选定一个整数gap,把待排序文件中所有记录分成若干个组,所有距离为gap的记录分在同一组内,并对每一组内的记录进行排序。然后重复上述分组和排序的工作。当到达gap==1时,所有记录在统一组内排好序。
代码实现:
public void shell(int[] array,int gap) {
for (int i = gap; i < array.length; i++) {
int tmp = array[i];
int j = 0;
for (j = i - gap; j >= 0 ; j -= gap) {
if (array[j] > tmp) {
array[j + gap] = array[j];
}
else {
break;
}
}
array[j + gap] = tmp;
}
}
public void shellSort(int[] array) {
int[] drr = {5,3,1};
for (int i = 0; i < drr.length; i++) {
shell(array,drr[i]);
}
}
希尔排序的特性总结:
- 希尔排序是对直接插入排序的优化。
- 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。
- 时间复杂度:平均速度为O(n^(3/2)),最坏情况速度为O(n*n)
- 空间复杂度:O(1)
- 稳定性:不稳定
三.直接选择排序
基本思想:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。
代码实现:
public void selectSort(int[] array) {
for (int i = 0; i < array.length; i++) {
for (int j = i + 1; j < array.length; j++) {
if (array[i] > array[j]) {
int tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
}
}
}
直接选择排序的特性总结:
- 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
- 时间复杂度:平均速度和最坏情况速度都为O(n^2)
- 空间复杂度:O(1)
- 稳定性:不稳定
四.堆排序
基本思想:利用堆结构和二叉树的一些性质来完成数据的排序。
实现代码:
//构造堆结构
public void adjust (int[] array,int start,int end) {
int tmp = array[start];
for (int i = 2*start+1; i <= end; i = i*2+1) {
if ((i < end) && array[i] < array[i+1]) {
i++;
}
if (array[i] > tmp) {
array[start] = array[i];
start = i;
}
else if (array[i] < tmp) {
break;
}
}
array[start] = tmp;
}
public void heapSort(int[] array) {
for (int i = (array.length-1-1)/2; i >= 0; i--) {
adjust(array,i,array.length-1);
}
for (int i = 1; i < array.length; i++) {
int tmp = array[array.length-i];
array[array.length-i] = array[0];
array[0] = tmp;
adjust(array,0,array.length-1-i);
}
}
堆排序的特性总结:
- 堆排序使用堆来选数,效率就高了很多。
- 排升序要建大堆,排降序建小堆。
- 时间复杂度:平均速度和最坏情况速度都为O(n*logn)
- 空间复杂度:O(1)
- 稳定性:不稳定
五.冒泡排序
基本思想:根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,
将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。
代码实现:
public void BubbleSort(int[] array) {
for (int i = 1; i < array.length-1; i++) {
for (int j = 0; j < array.length-1-i; j++) {
if (array[j] > array[j+1]) {
int tmp = array[j];
array[j] = array[j+1];
array[j+1] = tmp;
}
}
}
}
冒泡排序的特性总结:
- 冒泡排序是一种非常容易理解的排序
- 时间复杂度:平均速度和最坏情况速度都为O(n^2)
- 空间复杂度:O(1)
- 稳定性:稳定
六.快速排序
基本思想:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
代码实现:
//确定一个数在这组数中的位置
public int partion(int[] array,int low,int high) {
int tmp = array[low];
while (low < high) {
while ((low < high) && array[high] >= tmp) {
high--;
}
if (low == high) {
break;
}
else {
array[low] = array[high];
}
while ((low < high) && array[low] <= tmp) {
low++;
}
if (low == high) {
break;
}
else {
array[high] = array[low];
}
}
array[low] = tmp;
return low;
}
public void swap(int[] array,int start,int end){
int tmp = array[start];
array[start] = array[end];
array[end] = tmp;
}
//三数取中法(快排的一种优化)
public void medianOfThree(int[] array,int low,int high) {
int mid = (low+high)>>1;
if (array[mid] > array[low]) {
swap(array,mid,low);
}
if (array[mid] > array[high]) {
swap(array,mid,high);
}
if (array[low] > array[high]) {
swap(array,low,high);
}
}
//快排的递归形式
public void quick(int[] array,int start,int end) {
//递归到小的子区间时,考虑使用插入排序(快排的一种优化)
if(end-start+1 <= 5){
insertSort(array,start,end);
return;
}
medianOfThree(array,start,end);
int par = partion(array,start,end);
if(par > start+1){
quick(array,start,par-1);
}
if(par < end-1){
quick(array,par+1,end);
}
}
//快排的非递归形式
public void quickSort(int[] array) {
int[] stack = new int[2*array.length];
int top = 0;
int low = 0;
int high = array.length-1;
int par = partion(array,low,high);
if (par > low + 1) {
stack[top++] = low;
stack[top++] = par-1;
}
if (par < high - 1) {
stack[top++] = par+1;
stack[top++] = high;
}
while (top > 0) {
high = stack[top-1];
top--;
low = stack[top-1];
top--;
par = partion(array,low,high);
if (par > low + 1) {
stack[top++] = low;
stack[top++] = par-1;
}
if (par < high - 1) {
stack[top++] = par+1;
stack[top++] = high;
}
}
}
快速排序的特性总结:
- 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
- 时间复杂度:平均速度为O(n*logn),最坏情况速度为O(n^2)
- 空间复杂度:O(logn)
- 稳定性:不稳定
七.归并排序
基本思想:首先将含有n个结点的待排序数据序列看作由n个长度为1的有序字表组成,
将其依次两两合并,得到长度为2的若干有序子表;然后,再对这些子表进行两两合并,
得到长度为4的若干有序子表…,重复上述过程,一直到最后的子表长度为n,从而完成排序过程。
代码实现:
//归并排序的递归形式
public void merge(int[] array,int start,int mid,
int end) {
int[] tmpArray = new int[array.length];
int tmpIndex = start;
int start2 = mid+1;
int i = start;
while(start <= mid && start2 <= end) {
if(array[start] <= array[start2]) {
tmpArray[tmpIndex++] = array[start++];
}else {
tmpArray[tmpIndex++] = array[start2++];
}
}
while (start <= mid) {
tmpArray[tmpIndex++] = array[start++];
}
while (start2 <= end) {
tmpArray[tmpIndex++] = array[start2++];
}
while (i <= end) {
array[i] = tmpArray[i];
i++;
}
}
public void mergeSort(int[] array,int start,int end) {
if (start == end) {
return;
}
int mid = (start+end)/2;
mergeSort(array,start,mid);
mergeSort(array,mid+1,end);
merge(array,start,mid,end);
}
//归并排序的非递归形式
//gap 表示每组数据的个数,通过gap来确定s和e
public void merge(int[] array,int gap) {
int[] tmpArray = new int[array.length];
int i = 0;
int start1 = 0;
int end1 = start1+gap-1;
int start2 = end1+1;
int end2 = start2+gap-1 > array.length-1 ? array.length-1 : start2+gap-1;
while (start2 < array.length) {
while (start1 <= end1 && start2 <= end2) {
if (array[start1] <= array[start2]) {
tmpArray[i++] = array[start1++];
}
else {
tmpArray[i++] = array[start2++];
}
}
while (start1 <= end1) {
tmpArray[i++] = array[start1++];
}
while (start2 <= end2) {
tmpArray[i++] = array[start2++];
}
start1 = end2+1;
end1 = start1+gap-1;
start2 = end1+1;
end2 = start2+gap-1 > array.length-1 ? array.length-1 : start2+gap-1;
}
while (start1 <= array.length-1) {
tmpArray[i++] = array[start1++];
}
for (int j = 0; j < tmpArray.length; j++) {
array[j] = tmpArray[j];
}
}
public void mergeSort(int[] array) {
for (int i = 1; i < array.length; i = i*2) {
merge(array,i);
}
}
归并排序的特性总结:
- 归并的缺点在于需要O(n)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
- 时间复杂度:平均速度和最坏情况速度都为O(n*logn)
- 空间复杂度:O(n)
- 稳定性:稳定
八.计数排序
基本思想:计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。
代码实现:
public int[] countSort(int[] array) {
//1.得到数组的最大值和最小值,并算出差值d
int max = array[0];
int min = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
if (array[i] < min) {
min = array[i];
}
}
int d = max - min;
//2.创建统计数组并统计对应元素个数
int[] countArray = new int[d+1];
for (int i = 0; i < array.length; i++) {
countArray[array[i]-min]++;
}
//3.统计数组做变形,后面元素等于前面的元素之和
int sum = 0;
for (int i = 0; i < countArray.length; i++) {
sum += countArray[i];
countArray[i] = sum;
}
//4.倒序遍历原始数组,从统计数组找到正确位置,输出结果数组
int[] sortedArray = new int[array.length];
for (int i = array.length-1; i >= 0; i--) {
sortedArray[countArray[array[i]-min]-1] = array[i];
countArray[array[i]-min]--;
}
return sortedArray;
}
计数排序的特性总结:
- 计数排序在数据范围集中时,效率很高,但是适用范围及场景有限。
- 当数列最大最小值差距过大,或者数列元素不是整数时,计数排序不适用。
- 时间复杂度:平均速度和最坏情况速度都为O(n+m)
- 空间复杂度:O(m)
- 稳定性:稳定
九.基数排序
基本思想:将待排数据中的每组关键字依次进行桶分配以达到排序的作用。
代码实现:
public void radixSort(int[] array) {
//找到数组中的最大值
int max = array[0];
for (int i = 0; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
}
//确定最大值的位数
int keysNum = 0;
while (max > 0) {
max /= 10;
keysNum++;
}
//设置10个桶
List<ArrayList<Integer>> buckets = new ArrayList<ArrayList<Integer>>();
for (int i = 0; i < 10; i++) {
buckets.add(new ArrayList<Integer>());
}
for (int i = 0; i < keysNum; i++) {
//将元素放到对应的桶中
for (int j = 0; j < array.length; j++) {
int key = array[j]%(int)Math.pow(10,i+1)/(int)Math.pow(10,i);
buckets.get(key).add(array[j]);
}
int count = 0;
//将桶中的元素依次复制回数组
for (int j = 0; j < 10; j++) {
ArrayList<Integer> bucket = buckets.get(j);
while (bucket.size() > 0) {
array[count++] = bucket.remove(0);
}
}
}
}
基数排序的特性总结:
- 在某些时候,基数排序的效率高于其它的稳定性排序。
- 时间复杂度:平均速度为O(nlogm),最坏情况速度为O(nlogn)
- 空间复杂度:O(n)
- 稳定性:稳定
以上的叙述都很粗略,想要真正理解每一种排序的奇妙所在还得需要自己通过画图来细细体会。