总结一下所学的排序算法。
一、冒泡排序
从小到大排序,思想:遍历数组,下一个元素比当前元素大就交换,这样经过一轮后,把最大元素排到了最后,经过n-1轮,就把n-1大的元素排到了数组最后,最小值默认就排到了第一个位置。
时间复杂度:最坏O(n^2),最好是O(n)(可以用一个标记,第一趟如果没交换,说明数组是有序的),比较次数与原始数组无关。
/**
* @Title: BubbleSort.java
* @Package com.lian
* @Description: TODO()
* @author: Lian
* @date: 2018年7月15日 上午10:02:04
* @version V1.0
*/
package com.lian;
public class BubbleSort {
public static void main(String[] args) {
// TODO Auto-generated method stub
int a[]= {3,1,5,7,10,2,4,6,8,9};
int n=a.length;
for(int i=0;i<n-1;i++) {
//每次把最大的元素排到了数组末尾
boolean flag=false;//标记
for(int j=0;j<n-i-1;j++) {
if(a[j]>a[j+1]) {
int temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
flag=true;
}
}
if(flag==false)break;
}
for(int i=0;i<n;i++) {
System.out.print(a[i]+" ");
}
System.out.println();
}
}
二、选择排序
从小到大排序,思路:A[1...i]有序后,从余下数组中选择最小的元素排到A[i+1],进行n-1次后选出了n-1个最小值,最后一个默认就是最大值。
时间复杂度:最坏/最好都是Θ(n^2),比较次数与原始数组无关。
/**
* @Title: SelectionSort.java
* @Package com.lian
* @Description: TODO()
* @author: Lian
* @date: 2018年7月15日 上午9:54:00
* @version V1.0
*/
package com.lian;
public class SelectionSort {
public static void main(String[] args) {
// TODO Auto-generated method stub
int a[]= {3,1,5,7,10,2,4,6,8,9};
int n=a.length;
for(int i=0;i<n-1;i++) {
int min=a[i];
int id=i;
for(int j=i+1;j<n;j++) {
if(min>a[j]) {
min=a[j];
id=j;
}
}
int temp=a[id];a[id]=a[i];a[i]=temp;
}
for(int i=0;i<n;i++) {
System.out.print(a[i]+" ");
}
System.out.println();
}
}
三、插入排序
从小到大排序思路:将a[j]插入到a[0...j-1]中,即a[0...j-1]有序后,将a[j]插入到a[0...j-1]中。
时间复杂度:最坏O(n^2),最好O(n)(有序),比较次数与原始数组无关
/**
* @Title: InsertionSort.java
* @Package com.lian
* @Description: TODO()
* @author: lian
* @date: 2018年6月26日 下午9:27:32
* @version V1.0
*/
package com.lian;
public class InsertionSort {
public static void main(String[] args) {
int i,j;
int a[]= {3,1,5,7,10,2,4,6,8,9};
for( j=1;j<a.length;j++) {
//insert a[j] into the sorted sequence a[0...j-1]
int temp=a[j];
for(i=j-1;i>=0;i--) {
if(a[i]>temp) {
a[i+1]=a[i];
}
else break;
}
a[i+1]=temp;
}
for(i=0;i<a.length;i++)
{
System.out.print(a[i]+" ");
}
}
}
四、归并排序
从小到大排序思路:分治的思想,将数组递归的平分为两部分,回溯过程中归并,归并方法是将左右两部分排好序的数组,归并到原来a数组,此区间便是有序的了。
时间复杂度:Θ(nlgn)
package com.lian;
public class MergeSort {
public void merge(int[] a,int p,int q,int r) {
int i,j;
int n1=q-p+1;
int n2=r-q;
int[] L=new int[n1+1];
int[] R=new int[n2+1];
for(i=0;i<n1;i++) {
L[i]=a[p+i];
}
for(j=0;j<n2;j++) {
R[j]=a[q+1+j];
}
L[n1]=R[n2]=10000000;//多出一位,下面程序的比较中会体现。
i=0;j=0;
for(int k=p;k<=r;k++) {
if(L[i]>R[j]) {
a[k]=R[j];
j++;
}
else {
a[k]=L[i];
i++;
}
}
}
public void mergeSort(int[] a,int p,int r) {
if(p<r)
{
int q=(p+r)>>1;
mergeSort(a,p,q);
mergeSort(a,q+1,r);
merge(a,p,q,r);
}
}
public static void main(String[] args) {
int i;
int a[]= {3,1,5,7,10,2,4,6,8,9};
new MergeSort().mergeSort(a, 0, a.length-1);
for(i=0;i<a.length;i++) {
System.out.print(a[i]+" ");
}
}
}
五、快排
被认为是最好的排序方法,从小到大思想:分治思想,选择一个轴(一个标准点),将小于此值的元素划分到左支,大于此元素的划分到右支。一直这样划分下去,到叶节点便是有序的了。
时间复杂度: 当数据组有序或者无序时,时间复杂度T(n)=T(n-1)+Θ(n)=Θ(n^2);
如果最后一个元素都能使划分最好,那么T(n)=2*T(n/2)+Θ(n)=Θ(nlgn);
任意划分,如:T(n)=T(9n/10)+T(n/10)+Θ(n)=Θ(nlgn);
因此,期望时间复杂度Θ(nlgn)
/**
* @Title: QuickSort.java
* @Package com.lian
* @Description: TODO()
* @author: Lian
* @date: 2018年7月11日 下午9:38:25
* @version V1.0
*/
package com.lian;
/**
* 当数据组有序或者无序时,时间复杂度T(n)=T(n-1)+Θ(n)=Θ(n^2);
* 如果最后一个元素都能使划分最好,那么T(n)=2*T(n/2)+Θ(n)=Θ(nlgn);
* 任意划分,如:T(n)=T(9n/10)+T(n/10)+Θ(n)=Θ(nlgn);
* 因此,期望时间复杂度Θ(nlgn)
*/
public class QuickSort {
public static int partition(int[] a,int q,int r) {
//选最后一个元素作为枢纽,可以随机选一个元素。
int x=a[r];
int i=q-1;
for(int j=q;j<r;j++) {
if(a[j]<x) {
i++;
int temp=a[i];a[i]=a[j];a[j]=temp;
}
}
i++;
int temp=a[i];a[i]=a[r];a[r]=temp;
return i;
}
public static void quickSort(int[] a,int q,int r) {
if(q<r) {
int index=partition(a,q,r);
quickSort(a, q, index-1);
quickSort(a, index+1, r);
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int [] a= {5,4,6,7,8,3,1,2,9,10,1232,34,56,777,44,22};
quickSort(a,0,a.length-1);
for(int i=0;i<a.length;i++) {
System.out.print(a[i]+" ");
}
System.out.println();
}
}
//非递归实现,主要就是利用栈来模拟递归,每次存入一对左右边界
void quickSort(int[] array,int left,int right) {
stack<Integer> sta;
sta.push(left);
sta.push(right);
while(!sta.empty()) {
int r = sta.pop();
//sta.pop();
int l = sta.pop();
//sta.pop();
int index = partition(array, l, r);
if(index - 1 > l) {
sta.push(l);
sta.push(index - 1);
}
if(index + 1 < r) {
sta.push(index + 1);
sta.push(r);
}
}
}
六、堆排序
从大到小排序,即最大堆思想:a[parent(i)]>=a[i],父节点元素永远大于或等于子元素。
时间复杂度:O(nlgn),假设有n个元素,那么树高为O(lgn)。
维护最大堆MAX-HEAPIFY过程:O(lgn)
建树BUILD-MAX-HEAP过程:线性时间复杂度,从无序的输入数组中构造一个最大堆。证明见算法导论
排序HEAPSORT过程:O(nlgn),功能是堆一个数组进行原址排序(即不需要额外空间)。
同理可以构造个优先队列。
/**
* @Title: HeapSort.java
* @Package com.lian
* @Description: TODO()
* @author: Lian
* @date: 2018年7月11日 上午10:58:11
* @version V1.0
*/
package com.lian;
/**
* 堆排序和插入排序是in place(即不需要额外的内存空间);
*/
public class HeapSort {
public static void maxHeapify(int[] a,int i,int length)
{
int left=i<<1;
int right=i<<1|1;
//在i,left,right找出最大的值
int largest=i;
if(left<=length&&a[left]>a[i]) {
largest=left;
}
if(right<=length&&a[right]>a[largest]) {
largest=right;
}
if(largest!=i) {
int temp=a[i];
a[i]=a[largest];
a[largest]=temp;
maxHeapify(a,largest,length);
}
}
public static void buildMaxHeap(int[] a,int length) {
for(int i=length/2;i>=1;i--) {
maxHeapify(a,i,length);
}
}
public static void heapSort(int[] a,int length) {
buildMaxHeap(a,length);
for(int i=1;i<a.length;i++)
System.out.print(a[i]+" ");
System.out.println();
for(int i=length;i>=2;i--) {
int temp=a[1];a[1]=a[i];a[i]=temp;
length--;
maxHeapify(a,1,length);
}
}
/**
* @Description: TODO(优先队列)
*/
//返回堆顶元素
public static int heapMaxMum(int[] a) {
return a[1];
}
//返回堆顶元素并删除
public static int heapExtractMax(int[] a,int length) {
if(length<1) {
System.out.println("队列为空");
return -1;
}
int max=a[1];
a[1]=a[length];
length--;
maxHeapify(a,1,length);
return max;
}
//数组的第i个元素置为key
public static void insertKey(int[] a,int i,int key)
{
if(key<a[i]) {
System.out.println("key不能小于其值");
return;
}
a[i]=key;
while(i>1&&a[i/2]<a[i]) {
int temp=a[i/2];a[i/2]=a[i];a[i]=temp;
i/=2;
}
}
//插入一个元素
public static void insert(int[] a,int value,int length) {
length++;
a[length]=-1000000;
insertKey(a,length,value);
}
public static void priorityQueue(int[] que,int length)
{
buildMaxHeap(que, length);
System.out.println("建树后:");
for(int i=1;i<=length;i++) {
System.out.print(que[i]+" ");
}
System.out.println();
int top=heapMaxMum(que);
System.out.println("top:"+top);
int pop=heapExtractMax(que, length--);
System.out.println("pop:"+pop+" length:"+length);
System.out.println("弹出元素后:");
for(int i=1;i<=length;i++) {
System.out.print(que[i]+" ");
}
System.out.println();
insert(que,111,length);
length++;
System.out.println("插入元素后:");
for(int i=1;i<=length;i++) {
System.out.print(que[i]+" ");
}
System.out.println();
}
public static void main(String[] args) {
int a[]= {-100000,3,1,5,7,10,2,4,6,8,9};
/*堆排序*/
// heapSort(a,a.length-1);
// for(int i=1;i<a.length;i++)
// System.out.print(a[i]+" ");
/*优先队列*/
int[] que=new int[100];
for(int i=0;i<a.length;i++) {
que[i]=a[i];
}
priorityQueue(que,a.length-1);
}
}
public static void maxHeapify(int[] a,int i,int length)
{
int left=i<<1;
int right=i<<1|1;
//在i,left,right找出最大的值
int largest=i;
if(left<=length&&a[left]>a[i]) {
largest=left;
}
if(right<=length&&a[right]>a[largest]) {
largest=right;
}
if(largest!=i) {
int temp=a[i];
a[i]=a[largest];
a[largest]=temp;
maxHeapify(a,largest,length);
}
}
public static void buildMaxHeap(int[] a,int length) {
for(int i=length/2;i>=1;i--) {
maxHeapify(a,i,length);
}
}
public static void heapSort(int[] a,int length) {
buildMaxHeap(a,length);
for(int i=1;i<a.length;i++)
System.out.print(a[i]+" ");
System.out.println();
for(int i=length;i>=2;i--) {
int temp=a[1];a[1]=a[i];a[i]=temp;
length--;
maxHeapify(a,1,length);
}
}
/**
* @Description: TODO(优先队列)
*/
//返回堆顶元素
public static int heapMaxMum(int[] a) {
return a[1];
}
//返回堆顶元素并删除
public static int heapExtractMax(int[] a,int length) {
if(length<1) {
System.out.println("队列为空");
return -1;
}
int max=a[1];
a[1]=a[length];
length--;
maxHeapify(a,1,length);
return max;
}
//数组的第i个元素置为key
public static void insertKey(int[] a,int i,int key)
{
if(key<a[i]) {
System.out.println("key不能小于其值");
return;
}
a[i]=key;
while(i>1&&a[i/2]<a[i]) {
int temp=a[i/2];a[i/2]=a[i];a[i]=temp;
i/=2;
}
}
//插入一个元素
public static void insert(int[] a,int value,int length) {
length++;
a[length]=-1000000;
insertKey(a,length,value);
}
public static void priorityQueue(int[] que,int length)
{
buildMaxHeap(que, length);
System.out.println("建树后:");
for(int i=1;i<=length;i++) {
System.out.print(que[i]+" ");
}
System.out.println();
int top=heapMaxMum(que);
System.out.println("top:"+top);
int pop=heapExtractMax(que, length--);
System.out.println("pop:"+pop+" length:"+length);
System.out.println("弹出元素后:");
for(int i=1;i<=length;i++) {
System.out.print(que[i]+" ");
}
System.out.println();
insert(que,111,length);
length++;
System.out.println("插入元素后:");
for(int i=1;i<=length;i++) {
System.out.print(que[i]+" ");
}
System.out.println();
}
public static void main(String[] args) {
int a[]= {-100000,3,1,5,7,10,2,4,6,8,9};
/*堆排序*/
// heapSort(a,a.length-1);
// for(int i=1;i<a.length;i++)
// System.out.print(a[i]+" ");
/*优先队列*/
int[] que=new int[100];
for(int i=0;i<a.length;i++) {
que[i]=a[i];
}
priorityQueue(que,a.length-1);
}
}
七、计数排序、基数排序、桶排序
都是线性时间的排序。
计数排序:在元素值不是很大时可以用,对每一个元素x,确定小于x的元素个数。利用这一信息可以直接把x放到它在输出数组中的位置上。
基数排序:本质就是将每个数值分解成x位,然后,从低位->高位排序。
桶排序:假设输入数据均匀、独立分布在[0,1)区间上,平均情况下它的时间代价为O(n),将[0,1)区间划分为n个大小相同的子区间(“桶”)。然后,将n个输入元素分别方法各个桶中。因为是均匀分布,所以不会出现一个桶有很多元素的情况。然后,一次对每个桶中的元素进行排序,然后遍历每个桶,按照次序把每个桶中的元素列出来。