为了简单起见,只讨论基于比较的从小到大的整数排序
简单排序
冒泡排序
算法思想:
类似于水中冒泡,假设从小到大排序,即为较大的数慢慢往后排,较小的数慢慢往前排。依次比较两个相邻的元素,如果顺序错误就把他们交换过来,每一趟遍历,将一个最大的数移到序列末尾。重复进行直到没有相邻元素需要交换为止。
算法描述
算法描述:
- 比较相邻的元素,如果前一个比后一个大,交换之。
- 第一趟排序第1个和第2个元素,比较与交换,随后第2个和第3个比较交换,这样直到倒数第2个和最后1个,将最大的数移动到最后一位。
- 第二趟将第二大的数移动至倒数第二位
。。。
需要比较n-1趟。
算法实现
public static void bubbleSort(int[] a) {
int tmp;
for(int p=a.length-1; p>=0; p--){
for(int i=0; i<p; i++ ) {
/* 一趟冒泡*/
if( a[i] > a[i+1] ) {
tmp = a[i];
a[i] = a[i+1];
a[i+1] = tmp;
}
}
}
}
最好情况:顺序T= O( N)
最坏情况:逆序T= O( N2)
插入排序
算法思想:
它的基本思想是将一个记录插入到已经排好序的有序表中,从而生成一个新的、记录数增1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动。
算法描述:
- 从数组的第二个数据开始往前比较,即一开始用第二个数和他前面的一个比较,如果第二个数小于第一个数,则让他们交换位置。
- 然后再用第三个数和第二个比较,如果小于则交换,继续往前比较,当遇到大于时停止比较。
- 重复步骤二,一直到数据全都排完。
算法实现:
public static void insertionSort(int[] a) {
for(int p=1; p<a.length; p++) {
int tmp = a[p];
/* 摸下一张牌*/
int i;
for(i=p; i>0 && a[i-1]>tmp; i--)
/* 移出空位*/
a[i] = a[i-1];
/* 新牌落位*/
a[i] = tmp;
}
}
最好情况:顺序T= O( N)
最坏情况:逆序T= O( N2)
如果序列基本有序,则插入排序简单且高效
希尔排序
定理:任何仅以交换相邻两元素来排序的算法,其平均时间复杂度为( N2)。
而交换2个相邻元素正好消去1个逆序对!
这意味着:要提高算法效率,我们必须每次消去不止1个逆序对,每次交换相隔较远的2个元素!
算法思想:
定义一个增量序列DM> DM-1> …> D1= 1,对每个Dk进行“Dk-间隔”排序( k = M, M-1, …1 )
注意:“Dk-间隔”有序的序列,在执行“Dk-1-间隔”排序后,仍然是“Dk间隔”有序的。
算法实现:
public static void shellSort(int[] a) {
/* 希尔增量序列*/
for(int d=a.length/2; d>0; d/=2) {
/* 插入排序*/
for(int p=d; p<a.length; p++) {
int tmp= a[p];
int i;
for(i=p; i>=d && a[i-d]>tmp; i-=d)
a[i] = a[i-d];
a[i] = tmp;
}
}
}
最坏情况:T= Θ( N2)
增量元素不互质,则小增量可能根本不起作用。
更多增量序列
- Hibbard 增量序列:Dk= 2k–1 相邻元素互质
最坏情况:T = Θ( N3/2)
猜想:Tavg= O ( N5/4) - Sedgewick增量序列 {1, 5, 19, 41, 109, …}
猜想:Tavg= O ( N7/6),Tworst= O ( N4/3)
选择排序
算法思想:
第一次从待排序的数据元素中选出最小的元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。选择排序是不稳定的排序方法。
算法实现:
public void selectionSort(int[] a) {
for(int i = 0; i < a.length; i ++ ) {
/* 从A[i]到A[N–1]中找最小元,并将其位置赋给MinPosition*/
int minPosition= scanForMin(a, i, a.length);
/* 将未排序部分的最小元换到有序部分的最后位置*/
int tmp = a[i];
a[i] = a[minPosition];
a[minPosition] = tmp;
}
}
private int scanForMin(int[] a, int i, int l) {
int minPosition = i;
for (int j = i + 1; j < l; j++){
if(a[j] < a[minPosition]) minPosition = j;
}
return minPosition;
}
无论如何:T= Θ(N2)
如何快速找到最小元素?
堆排序
算法思想:
创建最小堆存储所有元素,每次弹出的元素就是最小元素。
算法实现:
算法1
public int[] heapSort(int[] a){
PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
for (int i:a) {
pq.add(i);
}
int[] tmp = new int[a.length];
for(int i=0; i<a.length; i++ )
tmp[i] = pq.poll(); /*O(logN)*/
return tmp;
}
T( N ) = O ( Nlog N)
算法2
快速排序
算法思想:
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
注意:基准有不同的取法,对算法速度有影响。常见的选法有:选取左边或右边、选取左右中间三个数的中间值、随机选取。
算法实现:
public void quick_Sort(int[] a) {
quicksort(a,0,a.length-1);
}
public void quicksort(int[] a, int left, int right) {
if(left < right){
//以数组中右边的元素为基准
int pivot = a[right];
int i = left;
int j = right-1;
int tmp;
while (i < j){
while(i<=right-1 && a[i] <= pivot)i++;
while(j>=left && a[j] >= pivot)j--;
if(i < j){
//交换元素
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
//交换元素
tmp = a[i];
a[i] = a[right];
a[right] = tmp;
quicksort(a,left,i-1);
quicksort(a,i+1,right);
}
}
每次正好中分:T(N)=O(NlogN)
最坏情况,数组本身就是有序的,每次选取左边或右边的元素为基准:T(N) = O(N2)
归并排序
算法思想:
该算法是采用分治法的一个非常典型的应用,先使每个子序列有序,再将已有序的子序列合并,最终得到完全有序的序列。
算法实现:
递归实现
public void mergeSort(int[] a) {
int[] tmp = new int[a.length+1];
mSort(a, tmp, 0,a.length-1);
}
private void mSort(int[] a, int[] tmp, int l, int rightEnd) {
int center;
if( l < rightEnd) {
center = (l + rightEnd) / 2;
mSort(a, tmp, l, center);
mSort(a, tmp, center+1, rightEnd);
merge(a, tmp, l, center+1, rightEnd);
}
}
private void merge(int[] a, int[] tmp, int l, int r, int rightEnd) {
int leftEnd = r - 1; /*左边终点位置。假设左右两列挨着*/
int tmpl = l;/*存放结果的数组的初始位置*/
int num = rightEnd - l + 1;
while(l <= leftEnd && r <= rightEnd) {
if(a[l] <= a[r])
tmp[tmpl++] = a[l++];
else
tmp[tmpl++] = a[r++];
}
while(l <= leftEnd) /* 直接复制左边剩下的*/
tmp[tmpl++] = a[l++];
while(r <= rightEnd) /*直接复制右边剩下的*/
tmp[tmpl++] = a[r++];
for(int i = 0; i < num; i++, rightEnd--)
a[rightEnd] = tmp[rightEnd];
}
T(N) = O(NlogN)
非递归实现
public void mergePass(int[] a, int[] tmp, int n, int length)
{
//两两归并相邻有序子列
int i, j;
for (i=0; i <= n-2*length; i += 2*length)
merge( a, tmp, i, i+length, i+2*length-1);
//归并最后2个子列
if (i+length < n )
merge( a, tmp, i, i+length, n-1);
//最后只剩1个子列,什么也不做
}
public void mergeSort1(int[] a)
{
int[] tmp = new int[a.length];
int length = 1; /* 初始化子序列长度*/
while(length < a.length) {
mergePass(a, tmp, a.length, length);
length *= 2;
}
}
基数排序
分为两类:
- 低位优先法,简称LSD法:先从最低位开始排序,再对次低位排序,直到对最高位排序后得到一个有序序列。
- 最高位优先法,简称MSD法:先从最高位开始排序,再逐个对各分组按次高位进行子排序,循环直到最低位。
算法实现:
LSD
public static void radixSort(int[] a){
int n = a.length;
//创建10个桶
List<LinkedList<Integer>> t = new ArrayList<LinkedList<Integer>>();
for(int i = 0 ;i < 10;i++){
LinkedList<Integer> list = new LinkedList<>();
t.add(list);
}
//得到最大数的基数
int max = a[0];
for(int i = 1 ;i < n;i++){
if(max < a[i])
max = a[i];
}
double d = Math.pow(10, String.valueOf(max).length());
int k = 1;
while(k < d){
for(int i : a){
int m = (i / k) % 10;
t.get(m).add(i);
}
int c = 0;
for(int i = 0; i < 10; i++){
while(!t.get(i).isEmpty()){
a[c++] = t.get(i).pollFirst();
}
}
k = k*10;
}
}
参考:https://blog.csdn.net/u011948899/article/details/78027838
MSD
//得到元素特定位置上的数据
public static int GetNumInPos(int num, int pos) {
int temp = 1;
for (int i = 0; i < pos - 1; i++) {
temp *= 10;
}
return (num / temp) % 10;
}
//MSD,调用时指定最高位数
public static void sort(LinkedList<Integer> A, int d) {
int len = A.size();
//分为0~9的序列空间
List<LinkedList<Integer>> radixArray = new ArrayList<LinkedList<Integer>>();
for (int i = 0; i < 10; i++){
radixArray.add(new LinkedList<Integer>());
}
//位数大于0,且数组长度大于1
if (d >= 1 && len > 1) {
while(!A.isEmpty()){
int num = GetNumInPos(A.peekFirst(), d);
radixArray.get(num).add(A.pollFirst());
}
for (int i = 0, j = 0; i < 10; i++) {
if(radixArray.get(i).size() == 1){
A.add(radixArray.get(i).pollFirst());
} else if(radixArray.get(i).size() > 1){
sort(radixArray.get(i), d-1);//递归,对每个子桶从次高位开始分配
while (!radixArray.get(i).isEmpty())
A.add(radixArray.get(i).pollFirst());
}
}
}
}
public static LinkedList<Integer> MSDSort(int[] n){
LinkedList<Integer> list = new LinkedList<>();
int max = n[0];
for(int i = 0 ;i < n.length;i++){
list.add(n[i]);
if(max < n[i])
max = n[i];
}
int maxL = String.valueOf(max).length(); //获取数组中最长元素长度
sort(list, maxL);
return list;
}