排序算法可以分为两大类:
(1)非线性时间排序算法:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序。这类算法包括:冒泡排序、插入排序、选择排序、希尔排序、归并排序、快速排序、堆排序。
(2)线性时间非比较类排序算法:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此称为线性时间非比较类排序。 这类算法包括:桶排序、计数排序、基数排序。不过,虽然这几种算法的时间复杂度比较低,但是他们对要排序的数据要求也比较苛刻。
不同算法的复杂度分析如下:
/**
* 归并排序:
* 采用分治思想,如果要排序一个数组,我们先把数组从中间分成前后两个部分,
* 然后对前后两个部分分别进行排序,再将排好序的两部分合并在一起,这样整个数组就都有序了。
* @author mling
*/
public class mergeSortTest {
public static void main(String[] args) {
int[] arr = new int[]{1,3,2,5,3};
mergeSort(arr,0,arr.length-1);
print(arr);
}
/**归并算法:
* @param arr 数组
* @param l 待排序的起始下标(左边)
* @param r 待排序的终止下标(右边)
* */
public static void mergeSort(int[] arr,int l,int r){
if(l >= r){//递归终止条件
return;
}
int mid = (l+r)/2;//取数组的中间点
mergeSort(arr,l,mid);//对数组前半部分进行排序
mergeSort(arr,mid+1,r);//对数组后半部分进行排序
merge(arr,l,r,mid);//合并数组前半部分和后半部分
}
/**
* 合并数组前半部分和后半部分: 从小到大
* //l为起点,r为终点,mid为第1区间与第2区间的分界线
*/
private static void merge(int[] arr, int l, int r, int mid) {
//初始化变量:i表示前半部分的下标,j表示后半部分的下标,k表示临时数组的下标
int i=l,j=mid+1,k=0;
int[] tmp = new int[r-l+1];
while(i<=mid && j<=r){//1,2区间均未结束
if(arr[i] <= arr[j]){//1区间的数<2区间
tmp[k++] = arr[i++];
}else{
tmp[k++] = arr[j++];
}
}
//将剩余数据拷贝到临时数组中
for(;i<=mid;i++) tmp[k++]=arr[i];
for(;j<=r;j++) tmp[k++]=arr[j];
//将tmp临时数组拷贝回原数组
for(int x=0;x
/**
* 快速排序
* @author rmling
*/
public class QuickSortTest {
public static void main(String[] args) {
int[] arr = new int[]{1,3,2,5,3};
quickSort(arr,0,arr.length-1);
print(arr);
}
/**快速排序算法:从小到大
* @param arr:数组 l:待排序的起始下标(左边) r:待排序的终止下标(右边)
* */
public static void quickSort(int[] arr,int l,int r){
if(l >= r){//递归终止条件
return;
}
int part = partition(arr,l,r);//获取分区点
quickSort(arr,l,part-1);//对左分区进行排序
quickSort(arr,part+1,r);//对右分区进行排序
}
/**返回分区点下标*/
private static int partition(int[] arr, int l, int r) {
int i=l;int prv=arr[r];
for(int j=l;j<=r;j++){
if(arr[j] < prv){
swap(arr,i,j);
i++;
}
}
swap(arr,i,r);
return i;
}
private static void swap(int[] a,int x,int y){
int temp=a[x];
a[x]=a[y];
a[y]=temp;
}
private static void print(int[] arr) {
for(int i=0; i
归并与快排的区别:
(1)归并排序和快速排序都是用分治的思想,代码都是通过递归来实现的,但是归并排序的核心是merge()合并函数,而快速排序的核心是partition()分区函数。归并排序的处理过程是从下到上,先处理子问题,然后再合并。而快速排序的处理过程正好相反,它的处理过程是从上到下的,先分区,然后再处理子问题。
(2)归并排序算法是一种任何情况下时间复杂度都比较稳定的排序算法,时间复杂度都是O(nlogn);快速排序算法最坏情况下时间复杂度为O(n^2) ,但是平均时间复杂度都是O(nlogn),不仅如此,快速排序时间复杂度退化到O(n^2)的概率非常小,我们可以通过合理选择pivot来避免这种情况。
(3)归并排序不是原地排序算法,空间复杂度比较高,为O(n);快速排序是原地排序算法,空间复杂度为O(1)。
(4)归并排序是稳定的算法,快速排序不稳定。
/**
* 冒泡排序
* @author mling
*/
public class BubbleSortTest {
public static void main(String[] args) {
int[] arr = new int[]{1,3,2,5,3};
bubbleSort(arr);
print(arr);
}
/**从小到大排序 */
public static void bubbleSort(int[] arr){
int len = arr.length;
for(int i=0; i arr[j+1]){
swap(arr,j,j+1);
flag=true;//true表示有数据交换
}
}
if(!flag) break;//没有数据交换,提前退出
}
}
private static void swap(int[] a,int x,int y){
int temp=a[x];a[x]=a[y];a[y]=temp;
}
private static void print(int[] arr) {
for(int i=0; i
/**
* 插入排序
* @author mling
*/
public class InsertSortTest {
public static void main(String[] args) {
int[] arr = new int[]{1,3,2,5,3};
insertSort(arr);
print(arr);
}
/**从小到大排序 */
public static void insertSort(int[] arr){
int len = arr.length;
for(int i=1; i=0;j--){
if(arr[j] > current){
arr[j+1]=arr[j];
}else{
break;
}
}
arr[j+1] = current;
}
}
private static void print(int[] arr) {
for(int i=0; i
/**
* 插入排序
* @author mling
*/
public class SelectSortTest {
public static void main(String[] args) {
int[] arr = new int[]{1,3,2,5,3};
selectionSort(arr);
print(arr);
}
/**选择排序:从小到大*/
public static void selectionSort(int[] arr){
int len = arr.length;
int minIndex;
for(int i=0;i
/**
* 希尔排序
* @author mling
*/
public class ShellSortTest {
public static void main(String[] args) {
int[] arr = new int[]{1,3,2,5,3};
shellSort(arr);
print(arr);
}
/**希尔排序:从小到大*/
public static void shellSort(int[] arr){
int len = arr.length;
int gap=1;//希尔排序的关键在于设置增量,这里我们动态设置一个增量
while(gap0;gap=(int) Math.floor(gap/3)){
for(int i=gap;i=0 && arr[j]>temp;j-=gap){
arr[j+gap]=arr[j];
}
arr[j+gap]=temp;
}
}
}
private static void print(int[] arr) {
for(int i=0; i
参考链接: 排序算法