马上要找开始找工作了
抽点空对各种排序算法进行一下总结,随手从维基百科上搜索了一下。
排序算法大概分这么多种
稳定的
冒泡排序(bubble sort) — O(n2)
鸡尾酒排序 (Cocktail sort, 双向的冒泡排序) — O(n2)
插入排序 (insertion sort)— O(n2)
桶排序 (bucket sort)— O(n); 需要 O(k) 额外空间
计数排序 (counting sort) — O(n+k); 需要 O(n+k) 额外空间
合并排序 (merge sort)— O(n log n); 需要 O(n) 额外空间
原地合并排序 — O(n2)
二叉排序树排序 (Binary tree sort) — O(n log n)期望时间; O(n2)最坏时间; 需要 O(n) 额外空间
鸽巢排序 (Pigeonhole sort) — O(n+k); 需要 O(k) 额外空间
基数排序 (radix sort)— O(n·k); 需要 O(n) 额外空间
Gnome 排序 — O(n2)
图书馆排序 — O(n log n) with high probability, 需要 (1+ε)n 额外空间
[编辑] 不稳定
选择排序 (selection sort)— O(n2)
希尔排序 (shell sort)— O(n log n) 如果使用最佳的现在版本
组合排序 — O(n log n)
堆排序 (heapsort)— O(n log n)
平滑排序 — O(n log n)
快速排序 (quicksort)— O(n log n) 期望时间, O(n2) 最坏情况; 对于大的、乱数列表一般相信是最快的已知排序
Introsort — O(n log n)
Patience sorting — O(n log n + k) 最坏情况时间,需要 额外的 O(n + k) 空间,也需要找到最长的递增子串行(longest increasing subsequence)
[编辑] 不实用的排序算法
Bogo排序 — O(n × n!) 期望时间,无穷的最坏情况。
Stupid sort — O(n3); 递归版本需要 O(n2) 额外存储器
珠排序(Bead sort) — O(n) or O(√n), 但需要特别的硬件
Pancake sorting — O(n), 但需要特别的硬件
这里面有很多我听说过的,很多没听说过的,有些用过的,有些没用过,大部分用java实现了一遍,从中看一下他们的核心思想。
一:冒泡排序
大学课程里面第一个排序算法,它的基本思想是每一个不断的遍历数组,每次遍历总是把最大的那个数找出来放到数组的尾部。经过n轮遍历之后,就排序完成。
/**
* 实现冒泡泡排序
* @return void
* @param int[]
* 它的原理是每一次都把最大的数放到最后面
* */
@SuppressWarnings("unused")
private void bubbleSort(int[] array){
//用于临时存数
int temp = 0;
for(int i = 1;i<array.length;i++){
for(int j=0;j<array.length-i;j++){
if(array[j]>array[j+1]){
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
}
}
二:插入排序
核心思想是:将一个数插入到一个有序表中,形成一个新的有序表
/**
* 实现插入排序
* 实现原理是将后面一个数都插入到前面已经排序好的序列中
* */
@SuppressWarnings("unused")
private void insertSort(int[] array){
//从头开始遍历
for(int i = 0;i<array.length;i++){
int temp = array[i];
int j = i;
//确定每一个数的位置
while(j>=0&&array[j]>temp){
//如果有大的那么将前面的数向后面移动
array[j+1] = array[j];
j--;
}
array[j] = temp;
}
}
三:选择排序
核心思想是:遍历数组找出最大的那个数,然后插入到合适的正确的位置,这个跟冒泡很像
@SuppressWarnings("unused")
public void chooseSort(int[] array){
for(int i=0;i<array.length;i++){
//先给他一个小点的值
int max = min;
//存储那个最大数的位置
int place = -1;
for(int j=0;j<array.length-i;j++){
//找到每一轮的最大的那个数和那个数的位置
if(array[j]>max){
max = array[j];
place = j;
}
}
//然后把它们插入到合适的位置
array[place] = array[array.length-i-1];
array[array.length-i] = max;
}
}
四:堆排序
这是所有排序中我觉得最复杂的一个,核心思想:其实也是找出最大值,然后也是插入到正确位置,只是找出最大值得办法是使用大顶堆,它的根就是最大值,然后与堆的最后一个节点交换,再进行向下堆调,成为一个新堆,这样重复,直到堆为空。
那么步骤是:1:建堆2:交换3:向下堆调
/**
* 实现堆排序
* 原理:先建立一个大顶堆,将最后一个元素与第一个元素调换然后向下堆调,依次下去就能得到一个有序数组了
* 堆:大顶堆
* */
//建堆
//向下堆调
@SuppressWarnings("unused")
private void shiftDown(int[] array,int st,int end){
int parent = st;
//这里之所以加1是因为数组的下标从0开始
int lchild = 2*st+1;
int rchild = 2*st+2;
int max = 0,temp = 0;
while(lchild<end){
max = lchild;
if(rchild<end){
if(array[lchild]<array[rchild]){
max=rchild;
}
}
if(array[parent]<array[max]){
//交换数据
temp = array[parent];
array[parent] = array[max];
array[max] = temp;
//进行下一次堆调当然也可以使用递归
parent = max;
lchild = 2*parent+1;
rchild = 2*parent+2;
}
else{break;}
}
}
//创建堆
private void createHeap(int[] array){
for(int i=array.length/2-1;i>=0;i--){
shiftDown(array,i,array.length);
}
print(array);
}
//这里是堆排序的实现
public void heapSort(int[] array){
createHeap(array);
int temp = 0;
for(int i=1;i<array.length;i++){
temp = array[0];
array[0] = array[array.length-i];
array[array.length-i] = temp;
shiftDown(array,0, array.length-i-1);
}
}
五:计数排序
核心思想:对于特定元素x来说,如果确定了序列中比其小的元素的个数。那么就能确定x的位置了,a数组中的数都为非负整数。如果有负数,则因该加上一个数,使其为正。
在这个程序中我们创建了两个大数组,一个用于计数,一个用于临时存储。
/**
* 计数排序
* 对于特定的元素来说,如果确定了序列中比其小的元素的个数,那么就能确定这个元素的位置了
* */
public void countingSort(int[] array){
//int数组赋值为0
int[] count = new int[10000];
int[] copy = new int[10000];
//给count数组赋值
for(int i=0;i<array.length;i++){
count[array[i]]=1;
}
//得到比count[i]小的数只是用了一种特殊的方式
//在纸上画一下就知道了
for(int i=1;i<10000;i++){
count[i] = count[i]+count[i-1];
}
//在合适的地方放上合适的数
for(int i=array.length-1;i>=0;i--){
copy[count[array[i]]-1] = array[i];
}
//再把排序好的数据放好
for(int i=0;i<array.length;i++){
array[i] = copy[i];
}
}
六:归并排序
归并排序使用了分治的思想,将一个大的排序分为若干个小的排序
归并排序分为自顶向下,自底向上的排序,当然我们熟知的是自顶向下
后者与前者的区别主要在于后者是合并的过程,而前者是划分的过程,前者与快速排序的思想很相像。下面来看一下它的实现过程。
/**
* 合并数组的两段
* @param array int[]
* @param start
* @param end
* @param mid
* 将两个排序好的数组合并成一个
* */
public void merge(int[] array,int start,int mid,int end){
//一个辅助数组
int[] comment = new int[10000];
int s = start,m = mid,index=start;
while(s<mid&&m<end){
if(array[s]<=array[m]){
comment[index] = array[s];
s++;
}
else {
comment[index] = array[m];
m++;
}
index++;
}
//如果前面这个数组被索引完毕
if(s == mid){
while(m<end){
comment[index++] = array[m++];
}
}else{
//System.out.println("s=="+s);
while(s<end){
comment[index++] = array[s++];
}
}
for(int i = start;i<end;i++){
array[i] = comment[i];
}
}
//实现mergeSort归并排序
public void mergeSort(int[] array){
int t = 1;
int start = 0;
int i = 0;
while(t<=array.length){
start = t;
t = 2*start;
i = 0;
while((i+t)<=array.length){
merge(array, i, i+start, i+t);
i = i+t;
}
print(array);
if(i+start<array.length){
merge(array, i, i+start, array.length);
}
}
}
//这里实现的是自顶向上的排序
public void mergeSort1(int[] array, int p1, int p3) {
if((p3-p1) <= 1) return; //判断是否不需要继续递归
int p2 = (p3 + p1)/2; //计算中间位置p2
mergeSort1(array,p1,p2); //将p1 ~ p2之间递归排序
mergeSort1(array,p2,p3); //将p2 ~ p3之间递归排序
merge(array,p1,p2,p3); //合并p1~p2,p2~p3
}
七:快速排序
快速排序是一个也是典型的分治
/**
* 这里是最经典也用的最多的快速排序
* 与merge的不同在于它是一个划分的过程
* */
private int split(int[] array,int left,int right){
int key = array[left];
int temp = 0;
int low = left+1;
int high = right;
while(low<high){
if(array[low]<=key) low++;
else if(array[high]>=key) high--;
else {
temp = array[low];
array[low] = array[high];
array[high] = temp;
}
}
array[left] = array[--low];
array[low] =key;
return low;
}
//实现快速排序
public void quickSort(int[] array,int low,int right){
int devide = 0;
if(low<right){
devide = split(array, low, right);
System.out.println(devide);
print(array);
quickSort(array, low, devide-1);
quickSort(array, devide+1, right);
}
}
八:希尔排序
先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
/**
* 希尔排序
* h = h*3+1.初始 h=1
* */
public void shellSort(int[] array){
int h = 1;
while(h<array.length)
h = h*3+1;
h = (h-1)/3;
while(h>=1)
{
for(int i=h;i<array.length;i++)
{
int j = i-h;
int key = array[i];
while(j>=0&&array[j]>key)
{
array[j+h] = array[j];
j-=h;
}
j+=h;
array[j]=key;
}
h=(h-1)/3;
}
}
九:基数排序
这个描述起来比较复杂,算是归纳法的一种吧。实现方式就是一下的,没有这么写注释,就是按照十进制位来排序,比如先第一位排序,然后按照第二位,直到最高位,需要比较大的空间开销。
public void radixSort(int[] number, int d) {
int k=0;
int n=1;
int m=1;
int[][] temp = new int[number.length][number.length];
int[] order = new int[number.length];
while(m <= d) {
for(int i = 0; i < number.length; i++) {
int lsd = ((number[i] / n) % 10);
temp[lsd][order[lsd]] = number[i];
order[lsd]++;
}
for(int i = 0; i < d; i++) {
if(order[i] != 0)
for(int j = 0; j < order[i]; j++) {
number[k] = temp[i][j];
k++;
}
order[i] = 0;
}
n *= 10;
k = 0;
m++;
}
}
马上要找工作了,以上的排序算法在大学多多少少都接触过,但很长时间没有用了,有的就忘记了,现在把它们整理一遍。只是对一些常见的排序算法的一个实现,没有仔细的研究各个算法的时间复杂度空间复杂度。