排序算法:
-
-
快速排序 O(nlogn)
-
堆排序 O(nlogn)
-
归并排序 O(nlogn)
-
-
-
冒泡排序 O(n^2)
-
选择排序 O(n^2)
-
插入排序 O(n^2)
-
希尔排序
-
1.快速排序:
快速排序原理就是先找一个基准值,将小于基准值的值放在左端,将大于基准值的值放在右端,然后按基准值划分成两部分,分别进行递归快速排序。
java代码:
public class QuickSort {
public static void quick_sort(int[] a ,int left ,int right){
if (left < right) {
int i = left;
int j = right;
//以最左端的值作为基准值
int x = a[left];
while (i<j){
//从右端开始寻找小于基准的值
while (i<j && a[j]>x){
j--;
}
//找到后再进行赋值
if (i<j)
a[i++]=a[j];
//再从左端开始寻找大于基准值的数
while(i<j && a[i]<x){
i++;
}
//找到后进行赋值
if (i<j)
a[j--]=a[i];
}
//当i>=j时,将基准值赋值给左端计数变量位置的元素a[i]
a[i]=x;
//
quick_sort(a,left,i-1);//左端进行递归快速排序
quick_sort(a,i+1,right);//右端进行递归快速排序
}
}
public static void main(String[] args){
int[] a = {3,4,6,1,2,5};
quick_sort(a,0,a.length-1);
for (int i :
a) {
System.out.print(i+" ");
}
}
}
快速排序平均比较次数为:O(nlogn);所以时间复杂度为O(nlogn);没有用到辅助数组,所以空间复杂度为O(1)
2.堆排序:
原理:将要排序的数据构建成堆结构,然后从最后一个非叶子节点处转换成大顶堆或者小顶堆,最后将堆顶值和最后一个节点值进行交换。循环上述操作,将除了最后一个节点的剩余节点进行再排序。以此类推。
代码:
public class HeapSort {
/**
* 排序主函数
* @param a
*/
public static void sort(int []a){
//首先从最后一个非叶子节点处构建堆结构
for(int i = a.length/2-1;i>=0;i--){
changeHeap(a,i,a.length);
}
//交换数据,前提是堆已经转换为大顶堆
for (int t = a.length-1;t>0;t--){
//进行交换
swap(a,0,t);
//对交换后的剩余数据从0开始进行再调整,使其成为大顶堆
changeHeap(a,0,t);
}
}
/**
* 构建堆结构和转换大顶堆或小顶堆
* @param a
* @param i
* @param length
*/
public static void changeHeap(int []a ,int i,int length){
//获取当前元素值
int temp = a[i];
//与左右两子节点进行比较
for (int j=2*i+1;j<length;j=2*j+1){
//如果右节点大于左节点,则j会指向值大的节点
if (j+1<length && a[j]<a[j+1]){
j++;
}
//判断子节点中最大值和当前节点值的大小,若大于则赋值给当前节点
if (a[j] > temp){
//此时a[j]并未改变
a[i]=a[j];
i=j;
}else
break;
}
//在此处会对原来的a[j]进行赋值
a[i]=temp;
}
/**
* 进行元素交换函数
* @param a
* @param i
* @param j
*/
public static void swap(int []a ,int i,int j){
int temp = a[i];
a[i]=a[j];
a[j]=temp;
}
public static void main(String[] args){
int []a={2,6,3,0,1};
sort(a);
for (int i :
a) {
System.out.print(i+" ");
}
}
}
堆排序是一种选择排序,堆结构是完全二叉树结构,所以每次循环将进行log2(n-i)次比较所以堆排序的时间复杂度为O(nlogn)
堆排序适合进行百万级别的数据排序,而快速排序和归并排序需要进行递归操作,所以在数据量很大时会出现堆栈溢出风险。
3.归并排序:
原理:通过递归方法将所有数据分成单独的元素,然后再进行两两合并排序,最后得到有序数列。
代码:
public class MergeSort {
/**
* 排序主方法
* @param a
* @param left
* @param right
* @param temp
*/
public static void sort(int []a ,int left,int right,int []temp){
if (left<right){
int mid = (left+right)/2;//求中间值
sort(a,left,mid,temp);//左半部分递归排序
sort(a,mid+1,right,temp);//右半部分递归排序
merge(a,left,mid,right,temp);//将两部分归并到一起
}
}
/**
* 归并方法
* @param a
* @param left
* @param mid
* @param right
* @param temp
*/
public static void merge(int []a,int left,int mid,int right,int []temp) {
int i = left;
int j = mid + 1;
int t = 0;
//判断是否越界
while (i <= mid && j <= right){
if (a[i] <= a[j]) {
temp[t++] = a[i++];
} else {
temp[t++] = a[j++];
}
}
//执行到此处时,肯定有一部分已经全部排完
while (i <= mid) {//将左边剩余元素填充进temp中
temp[t++] = a[i++];
}
while (j <= right) {//将右序列剩余元素填充进temp中
temp[t++] = a[j++];
}
t = 0;
//将temp中的元素全部拷贝到原数组中
while (left <= right) {
a[left++] = temp[t++];
}
}
//主函数
public static void main(String[] args){
int a[] = {4,1,5,2,3};
//准备一个与原程序一样长度的副本数组
int []temp = new int[a.length];
sort(a,0,a.length-1,temp);
for (int i :
a) {
System.out.print(i+" ");
}
}
}
归并排序是稳定排序,java自带的排序算法就是归并排序的优化版本,其最好最坏平均的时间复杂度均为O(nlogn)。因为用到了一个辅助数组所以空间复杂度为O(n)。
在递归过程中先对短的那段进行递归,可以使递归深度最小。
4.简单选择排序:
原理:在所有数列中选出最小值,并且与第一个元素交换,然后从剩余的元素中再选择一个最小值并与第二个元素交换,依次循环直至结尾。
代码:
public class ChooseSort {
/**
* 选择排序主方法
* @param a
*/
public static void sort(int []a){
int min=0;
for (int i =0 ;i < a.length;i++){
min=i;
for (int k = i+1;k<a.length;k++){
if (a[k] < a[min]){
min=k;
}
}
//如果最小值发生了改变则进行交换
if (min != i){
int temp =a[i];
a[i]=a[min];
a[min]=temp;
}
}
}
public static void main(String[] args){
int a[]={2,4,1,5,3};
sort(a);
for (int i :
a) {
System.out.print(i+" ");
}
}
}
选择排序在排序过程中每次循环均需要比较n-i次,所以其时间复杂度为:O(n^2),空间复杂度为O(1)
5.冒泡排序:
原理:从数列头进行两两比较,然后经过多轮比较,将最大值交换到最后端,然后再进行多伦比较将次大数交换到最大值后,以此循环。
代码:
public class BubbleSort {
public static void sort(int []a){
int temp =0;
//需要冒的泡泡数
for (int i = 0;i < a.length-1;i++){
//每个泡泡需要比较的次数
for (int k = 0;k < a.length-1-i;k++){
if (a[k]>a[k+1]){
temp =a[k];
a[k]=a[k+1];
a[k+1]=temp;
}
}
}
}
public static void main(String[] args){
int []a={2,1,5,4,3,6};
sort(a);
for (int i :
a) {
System.out.print(i+" ");
}
}
}
冒泡排序在排序过程中每次循环都需要进行n-i次比较,所以时间复杂度和选择排序一样都是O(n^2)。
6.插入排序(步长为1):
原理:因为步长为1,所以从第二个元素开始与前一个元素进行比较两两比较,若小于前面的元素则与其交换,然后在与前一个元素比较交换,直至没有前一个元素,然后再从第三个元素开始进行循环比较。
希尔排序也是插入排序的一种,只不过其步长不为1,它的步长在排完一次后减小1,最后减小为1,然后进行步长为1的插入排序。
代码:
public class InsertSort {
/**
*
* @param a
*/
public static void sort(int []a){
for (int i =1 ; i < a.length ; i++){
//这里可以使用fori()循环替代
int j =i;
while (j > 0 && a[j]<a[j-1]){
int temp=a[j];
a[j]=a[j-1];
a[j-1]=temp;
j--;
}
}
}
public static void main(String[] args){
int a[]={5,4,3,2,1};
sort(a);
for (int i :
a) {
System.out.print(i+ " ");
}
}
}