排序方法总结

目录

基本概要

冒泡排序

选择排序

插入排序

希尔排序

归并排序

快速排序

堆排序


基本概要

排序算法大体可分为两种:

  • 一种是比较排序,时间复杂度O(nlogn) ~ O(n^2),主要有:冒泡排序,选择排序,插入排序,归并排序,堆排序,快速排序等。
  • 另一种是非比较排序,时间复杂度可以达到O(n),主要有:计数排序,基数排序,桶排序等。

下面给出常见比较算法的排序性能

希尔排序的时间复杂度???

稳定性 : 排序前:3(1),5,3(2),4,7,1;

              排序后:1,3(1),3(2),4,5,7----稳定;

              排序后:1,3(2),3(1),4,5,7----不稳定。


冒泡排序

public class Bubble {//冒泡排序
	public void sort(int[] a) {
		for(int i = 0;i<a.length;i++) {
			for(int j = 0;j<a.length-i-1;j++) {
				if(a[j]>a[j+1]) {
					exch(a, j, j+1);//在循环里面交换
				}
			}
		}
	}
	private void exch(int[] a,int i,int j) {//a[i]和a[j]交换位置
		int t = a[i];
		a[i] = a[j];
		a[j] = t;
	}
 
}

最好的情况O(n):如下代码

public void bubbleSort(int arr[]) {
    boolean didSwap;
    for(int i = 0, len = arr.length; i < len - 1; i++) {
        didSwap = false;
        for(int j = 0; j < len - i - 1; j++) {
            if(arr[j + 1] < arr[j]) {
                swap(arr, j, j + 1);
                didSwap = true;
            }
        }
        if(didSwap == false)
            return;
    }    
}

选择排序

public class Selection {//选择排序
	public void sort(int[] a) {
		for(int i =0;i<a.length;i++) {
			//首先找到最小的值
			int min = i;//指向最小值的指针
			for(int j = i;j<a.length;j++) {
				if(a[j]<a[min]) {
					min = j;
				}
			}
			exch(a,i,min);//在循环外面交换
		}
	}
	private void exch(int[] a,int i,int j) {//a[i]和a[j]交换位置
		int t = a[i];
		a[i] = a[j];
		a[j] = t;
	}  
}

插入排序

public class Insertion {//插入排序
	public void sort(int[] a) {
		for(int i = 1;i<a.length;i++) {
			int key = a[i];
			int j = i;
			while(j>0 && a[j-1]>key) {
				a[j]=a[j-1];
				j--;
			}
			a[j] = key;
		}
	}
}

最好的情况O(n):

希尔排序

public void sort(int[] a) {
		int h = a.length/2;
		while(h>=1) {
			sort(a,h);
			h = h/2;
		}
	}
	
	private void sort(int[] a,int h) {//进行插入排序,增量为h
		for(int i = h;i<a.length;i++) {
			int key = a[i];
			int j = i;
			while(j>=h&&a[j-h]>key) {
				a[j] = a[j-h];
				j = j-h;
			}
			a[j] = key;
		}
	}
}

归并排序

public class Merge {//归并排序
    public void mergeSort(int[] data) {  
        sort(data, 0, data.length - 1);  
    }  
    public void sort(int[] data, int left, int right) {  
        if (left >= right)  
            return;  
        // 找出中间索引  
        int center = (left + right) / 2;  
        // 对左边数组进行递归  
        sort(data, left, center);  
        // 对右边数组进行递归  
        sort(data, center + 1, right);  
        // 合并  
        merge(data, left, center, right);   
    }  
  
    /** 
     * 将两个数组进行归并,归并前面2个数组已有序,归并后依然有序 
     *  
     * @param data 
     *            数组对象 
     * @param left 
     *            左数组的第一个元素的索引 
     * @param center 
     *            左数组的最后一个元素的索引,center+1是右数组第一个元素的索引 
     * @param right 
     *            右数组最后一个元素的索引 
     */  
    public void merge(int[] data, int left, int center, int right) {  
        // 临时数组  
        int[] tmpArr = new int[data.length];  
        // 右数组第一个元素索引  
        int mid = center + 1;  
        // third 记录临时数组的索引  
        int third = left;  
        // 缓存左数组第一个元素的索引  
        int tmp = left;  
        while (left <= center && mid <= right) {  
            // 从两个数组中取出最小的放入临时数组  
            if (data[left] <= data[mid]) {  
                tmpArr[third++] = data[left++];  
            } else {  
                tmpArr[third++] = data[mid++];  
            }  
        }  
        // 剩余部分依次放入临时数组(实际上两个while只会执行其中一个)  
        while (mid <= right) {  
            tmpArr[third++] = data[mid++];  
        }  
        while (left <= center) {  
            tmpArr[third++] = data[left++];  
        }  
        // 将临时数组中的内容拷贝回原数组中  
        // (原left-right范围的内容被复制回原数组)  
        while (tmp <= right) {  
            data[tmp] = tmpArr[tmp++];  
        }  
    }  
}

快速排序

public class Quick {//快速排序
	public void sort(int[] a,int lo,int hi) {
		if(lo<hi) {
			int mid = getMiddle(a,lo,hi);
			sort(a,0,mid-1);
			sort(a,mid+1,hi);
		}
	}
	private int getMiddle(int[] a,int low,int high) {
		int key = a[low];
		while(low<high) {
			while(low<high && a[high]>=key) {
				high--;
			}
			a[low] = a[high];
			while(low<high && a[low]<=key) {
				low++;
			}
			a[high]=a[low];
		}
		a[low] = key;
		return low;
	}
}

1.时间复杂度
快速排序涉及到递归调用,所以该算法的时间复杂度还需要从递归算法的复杂度开始说起;
递归算法的时间复杂度公式:T[n] = aT[n/b] + f(n)  ;对于递归算法的时间复杂度这里就不展开来说了;

2.最优情况下时间复杂度
快速排序最优的情况就是每一次取到的元素都刚好平分整个数组(很显然我上面的不是);
此时的时间复杂度公式则为:T[n] = 2T[n/2] + f(n);T[n/2]为平分后的子数组的时间复杂度,f[n] 为平分这个数组时所花的时间;
        下面来推算下,在最优的情况下快速排序时间复杂度的计算(用迭代法):
                                         T[n] =  2T[n/2] + n                                                                     ----------------第一次递归

                 令:n = n/2        =  2 { 2 T[n/4] + (n/2) }  + n                                               ----------------第二次递归

                                            =  2^2 T[ n/ (2^2) ] + 2n

                令:n = n/(2^2)   =  2^2  {  2 T[n/ (2^3) ]  + n/(2^2)}  +  2n                         ----------------第三次递归  

                                            =  2^3 T[  n/ (2^3) ]  + 3n

                ......................................................................................                        

                令:n = n/(  2^(m-1) )    =  2^m T[1]  + mn                                                  ----------------第m次递归(m次后结束)

当最后平分的不能再平分时,也就是说把公式一直往下跌倒,到最后得到T[1]时,说明这个公式已经迭代完了(T[1]是常量了)。

               得到:T[n/ (2^m) ]  =  T[1]    ===>>   n = 2^m   ====>> m = logn;

               T[n] = 2^m T[1] + mn ;其中m = logn;

               T[n] = 2^(logn) T[1] + nlogn  =  n T[1] + nlogn  =  n + nlogn  ;其中n为元素个数

               又因为当n >=  2时:nlogn  >=  n  (也就是logn > 1),所以取后面的 nlogn;

               综上所述:快速排序最优的情况下时间复杂度为:O( nlogn )


3.最差情况下时间复杂度

     最差的情况就是每一次取到的元素就是数组中最小/最大的,这种情况其实就是冒泡排序了(每一次都排好一个元素的顺序)

     这种情况时间复杂度就好计算了,就是冒泡排序的时间复杂度:T[n] = n * (n-1) = n^2 + n;

     综上所述:快速排序最差的情况下时间复杂度为:O( n^2 )


4.平均时间复杂度
       快速排序的平均时间复杂度也是:O(nlogn)

5.空间复杂度
        其实这个空间复杂度不太好计算,因为有的人使用的是非就地排序,那样就不好计算了(因为有的人用到了辅助数组,所以这就要计算到你的元素个数了);我就分析下就地快速排序的空间复杂度吧;
        首先就地快速排序使用的空间是O(1)的,也就是个常数级;而真正消耗空间的就是递归调用了,因为每次递归就要保持一些数据;
     最优的情况下空间复杂度为:O(logn)  ;每一次都平分数组的情况
     最差的情况下空间复杂度为:O( n )      ;退化为冒泡排序的情况
---------------------

快排时间空间复杂度参考:
作者:庾志辉
来源:CSDN
原文:https://blog.csdn.net/yuzhihui_no1/article/details/44198701
版权声明:本文为博主原创文章,转载请附上博文链接!

 

堆排序

堆排序

  堆排序是利用这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。首先简单了解下堆结构。

  堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:

同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子 

该数组从逻辑上讲就是一个堆结构,我们用简单的公式来描述一下堆的定义就是:

大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]  

小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]  

ok,了解了这些定义。接下来,我们来看看堆排序的基本思想及基本步骤:

堆排序基本思想及步骤

堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了

 

import java.util.Arrays;
public class HeapSort {
    public static void main(String []args){
        int []arr = {9,8,7,6,5,4,3,2,1};
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void sort(int []arr){
        //1.构建大顶堆
        for(int i=arr.length/2-1;i>=0;i--){
            //从第一个非叶子结点从下至上,从右至左调整结构
            adjustHeap(arr,i,arr.length);
        }
        //2.调整堆结构+交换堆顶元素与末尾元素
        for(int j=arr.length-1;j>0;j--){
            swap(arr,0,j);//将堆顶元素与末尾元素进行交换
            adjustHeap(arr,0,j);//重新对堆进行调整
        }

    }

    //调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上)
    public static void adjustHeap(int []arr,int i,int length){
        int temp = arr[i];//先取出当前元素i
        for(int k=i*2+1;k<length;k=k*2+1){//从i结点的左子结点开始,也就是2i+1处开始
            if(k+1<length && arr[k]<arr[k+1]){//如果左子结点小于右子结点,k指向右子结点
                k++;
            }
            if(arr[k] >temp){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
                arr[i] = arr[k];
                i = k;
            }else{
                break;
            }//上面的if就是为了找到父,左右节点中最大的放到父节点处
        }
        arr[i] = temp;//将temp值放到最终的位置,最终的位置就是刚刚最大节点的位置
    }

    // 交换元素
    public static void swap(int []arr,int a ,int b){
        int temp=arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值