代码月记之JAVA排序

本文深入讲解了各种排序算法,包括直接插入排序、希尔排序、直接选择排序等的时间复杂度、空间复杂度、稳定性及适用场景,对比分析了快速排序、堆排序、归并排序等高级排序方法的优劣,提供了详细的代码实现与测试类,帮助读者理解和掌握不同排序算法的特点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

排序类型、时间复杂度、空间复杂度、稳定性、复杂性

我们首先对时间复杂度空间复杂度做一下整理:

  1. 时间复杂度 :一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。
  2. 空间复杂度 :是对一个算法在运行过程中临时占用存储空间的度量,一个算法在计算机存储器上所占用的存储空间包括存储算法本身所占用的空间,算数和输入输出所占用的存储空间以及临时占用存储空间三个部分,算法的输入输出数据所占用的存储空间是由待解决的问题来决定的,通过参数表由调用函数而来,它随本算法的不同而改变,存储算法本身所占用的存储空间有算法的书写长短成正比。算法在运行过程中占用的临时空间由不同的算法决定。

复杂度总结

排序方法时间复杂度(平均)时间复杂度(最坏)时间复杂度(最好)空间复杂度稳定性复杂性
直接插入排序O(n²)O(n²)O(n)O(1)稳定简单
希尔排序O(n㏒2n)O(n2)O(n)O(1)不稳定较复杂
直接选择排序O(n2)O(n2)O(n2)O(1)不稳定简单
堆排序O(n㏒2n)O(n㏒2n)O(n㏒2n)O(1)不稳定较复杂
冒泡排序O(n2)O(n2)O(n)O(1)稳定简单
快速排序O(n㏒2n)O(n2)O(n㏒2n)O(n㏒2n)不稳定较复杂
归并排序O(n㏒2n)O(n㏒2n)O(n㏒2n)O(n)稳定较复杂
基数排序O(d(n+r))O(d(n+r))O(d(n+r))O(n+r)稳定较复杂

代码

测试类

testSort.java 测试排序算法类

package com.ck.test;

import com.ck.exception.CKException;
import com.ck.serialize.ReSerialize;
import com.ck.serializeimpl.ReSerializeimpl;

public class testSort {
	public static void main(String args[]){
        int[] a = {65,55,96,25,33,1,23,88,123,122};
        int[] result;
        ReSerialize reSerializes = new ReSerializeimpl();
        try {
			//result=reSerializes.insertSort(a);//插入排序
        	//result=reSerializes.twoinsertSort(a);//二分排序
        	//result = reSerializes.shellSort(a);//简单选择排序
        	//result = reSerializes.selectSort(a);//简单选择排序
        	//result = reSerializes.twoselectSort(a);//二元选择排序
        	//result = reSerializes.heapSort(a);//堆排序
        	//result = reSerializes.bubbleSort(a);//冒泡排序
        	//result = reSerializes.bubbleSortX(a);//改进冒泡排序
        	//result = reSerializes.quickSort(a);//快速排序
        	//result = reSerializes.quickSortX(a);//改进快速排序
        	//result = reSerializes.mergeSort(a);//归并排序
        	result = reSerializes.radixSort(a);//基数排序
			for(int i = 0 ; i< result.length ;i++){
			System.out.println(result[i]);
			}
		} catch (CKException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

序列化接口

ReSerialize.java 接口类

package com.ck.serialize;


import com.ck.exception.CKException;

public interface ReSerialize {
     public int[] insertSort(int[] a) throws CKException;
     
     public int[] twoinsertSort(int[] a) throws CKException;
     
     public int[] shellSort(int[] a) throws CKException;
     
     public int[] selectSort(int[] a) throws CKException;
     
     public int[] twoselectSort(int[] a) throws CKException;
     
     public int[] heapSort(int[] a) throws CKException;
     
     public int[] bubbleSort(int[] a) throws CKException;
     
     public int[] bubbleSortX(int[] a) throws CKException;
     
     public int[] quickSort(int[] a) throws CKException;
     
     public int[] quickSortX(int[] a) throws CKException;
     
     public int[] mergeSort(int[] a) throws CKException;
     
     public int[] radixSort(int[] a) throws CKException;
}

ReSerializeimpl.java 接口实现类

package com.ck.serializeimpl;

import com.ck.dao.ReSerializeDao;
import com.ck.dao.impl.ReSreializeDaoimpl;
import com.ck.exception.CKException;
import com.ck.serialize.ReSerialize;

public class ReSerializeimpl implements ReSerialize{

	@Override
	public int[] insertSort(int[] a) throws CKException {		
		ReSerializeDao Reserializedao = new ReSreializeDaoimpl(); 
		int[] NewA = Reserializedao.insertSort(a);
		
		return NewA;
	}

	@Override
	public int[] twoinsertSort(int[] a) throws CKException {
		ReSerializeDao Reserializedao = new ReSreializeDaoimpl();
		int[] NewB = Reserializedao.twoinsertSort(a);
		return NewB;
	}
	
	@Override
	public int[] shellSort(int[] a) throws CKException{
		ReSerializeDao Reserializedao = new ReSreializeDaoimpl();
		int[] NewC = Reserializedao.shellSort(a);
		return NewC;
	}

	@Override
	public int[] selectSort(int[] a) throws CKException {
		ReSerializeDao Reserializedao = new ReSreializeDaoimpl();
		int[] NewD = Reserializedao.selectSort(a);
		return NewD;
	}

	@Override
	public int[] twoselectSort(int[] a) throws CKException {
		ReSerializeDao Reserializedao = new ReSreializeDaoimpl();
		int[] NewE = Reserializedao.twoselectSort(a);
		return NewE;
	}

	@Override
	public int[] heapSort(int[] a) throws CKException {
		// TODO Auto-generated method stub
		ReSerializeDao Reserializedao = new ReSreializeDaoimpl();
		int[] NewF = Reserializedao.heapSort(a);
		return NewF;
	}

	@Override
	public int[] bubbleSort(int[] a) throws CKException {
		// TODO Auto-generated method stub
		ReSerializeDao Reserializedao = new ReSreializeDaoimpl();
		int[] NewG = Reserializedao.bubbleSort(a);
		return NewG;
	}
	
	@Override
	public int[] bubbleSortX(int[] a) throws CKException {
		// TODO Auto-generated method stub
		ReSerializeDao Reserializedao = new ReSreializeDaoimpl();
		int[] NewH = Reserializedao.bubbleSortX(a);
		return NewH;
	}
	
	@Override
	public int[] quickSort(int[] a) throws CKException {
		// TODO Auto-generated method stub
		ReSerializeDao Reserializedao = new ReSreializeDaoimpl();
		int[] NewI = Reserializedao.quickSort(a);
		return NewI;
	}
	
	@Override
	public int[] quickSortX(int[] a) throws CKException {
		// TODO Auto-generated method stub
		ReSerializeDao Reserializedao = new ReSreializeDaoimpl();
		int[] NewJ = Reserializedao.quickSortX(a);
		return NewJ;
	}
	
	@Override
	public int[] mergeSort(int[] a) throws CKException {
		// TODO Auto-generated method stub
		ReSerializeDao Reserializedao = new ReSreializeDaoimpl();
		int[] NewK = Reserializedao.mergeSort(a);
		return NewK;
	}
	
	@Override
	public int[] radixSort(int[] a) throws CKException {
		// TODO Auto-generated method stub
		ReSerializeDao Reserializedao = new ReSreializeDaoimpl();
		int[] NewL = Reserializedao.radixSort(a);
		return NewL;
	}
}

Dao层接口

ReSerializeDao .java 接口类

package com.ck.dao;

import java.util.List;

import com.ck.exception.CKException;

public interface ReSerializeDao {
	//直接插入排序
    public int[] insertSort(int[] a) throws CKException;
    //二分插入排序
    public int[] twoinsertSort(int[] a) throws CKException;
    //希尔排序
    public int[] shellSort(int[] a) throws CKException;
    //简单选择排序
    public int[] selectSort(int[] a) throws CKException;
    //二元选择排序
    public int[] twoselectSort(int[] a) throws CKException;
    //堆排序
    public int[] heapSort(int[] a) throws CKException;
    //冒泡排序
    public int[] bubbleSort(int[] a) throws CKException;
    //改进冒泡排序
    public int[] bubbleSortX(int[] a) throws CKException;
    //快速排序
    public int[] quickSort(int[] a) throws CKException;
    //改进快速排序
    public int[] quickSortX(int[] a) throws CKException;
    //归并排序
    public int[] mergeSort(int[] a) throws CKException;
    //基数排序
    public int[] radixSort(int[] a) throws CKException;
}

ReSerializeimpl.java 接口实现类

package com.ck.dao.impl;

import java.util.List;
import java.util.ArrayList;

import com.ck.dao.ReSerializeDao;
import com.ck.exception.CKException;

public class ReSreializeDaoimpl implements ReSerializeDao{

	@Override
	public int[] insertSort(int[] a) throws CKException {
		// TODO Auto-generated method stub
		//直接插入排序
		for (int i=1 ;i<a.length; i++){
			int temp = a[i];
			int j;   //2 1
			for(j = i-1 ; j >= 0 && a[j] > temp; j--){
				a[j+1] = a[j];
			}
			a[j+1] = temp;			
		}		
		return a;
	}
	
	
	public int[] twoinsertSort(int[] a) throws CKException{
		//二分插入排序                    
	   
	        for (int i = 0; i < a.length; i++) {
	            int temp = a[i];         
	            int left = 0;      
	            int right = i - 1;  
	            int mid;            
	            while (left <= right) {     
	                mid = (left + right) / 2;   
	                //System.out.println("+++++++++++"+mid);
	                if (temp < a[mid]) {
	                    right = mid - 1;  
	                } else {
	                    left = mid + 1; 
	                }
	            }
	            for (int j = i - 1; j >= left; j--) {  
	                a[j + 1] = a[j]; 
	            }
	            if (left != i) {       
	                a[left] = temp;
	            }
	        }
		return a;
	}


	@Override
	public int[] shellSort(int[] a) throws CKException {
		// TODO Auto-generated method stub   654321   321654  123654
		int dk = a.length/2;
		while (dk>=1){   
			for (int i = dk ; i < a.length ; i++){
				int temp = a[i];     
				int j;
				for (j = i - dk ; j >= 0 && a[j] > temp ; j = j-dk){
					a[j+dk] = a[j];   
				}
				a[j+dk] = temp;   
			}
		    dk = dk/2;
		}
		return a;
	}


	@Override
	public int[] selectSort(int[] a) throws CKException {
		int min;
		// TODO Auto-generated method stub
		for(int i = 0; i<a.length ;i++){
			min = i;
			for(int j=i+1;j<a.length;j++){
				if(a[j]<a[min]){
					min = j;
				}
			}
			if(i!= min){
		    a[i] = a[i]+a[min];
		    a[min] = a[i]-a[min];
		    a[i] = a[i]-a[min];
			}
		}
		return a;
	}


	@Override
	public int[] twoselectSort(int[] a) throws CKException {
		// TODO Auto-generated method stub
		int min , max ;
		for(int i = 0 ; i<a.length/2;i++){
			min = i;
			max = i;
			for(int j=i+1;j<a.length-i;j++){
				if(a[j]>a[max]){
					max = j;
					continue;
				}
				if(a[j]<a[min]){
					min = j;
				}
			}
			//最小值交换
			if(i != min){
			a[i] = a[i]+a[min];
		    a[min] = a[i]-a[min];
		    a[i] = a[i]-a[min];
			}
		    //
		    if(i == max){
		    	max = min;//如果当前i值为最大值,当i的位置与最小值交换后,当max值交换时,当前值已到min位置,所以i需要更新为min
		    }
		    //最大值交换
		    if(a.length-1-i != max){
		    a[a.length-1-i] = a[a.length-1-i]+a[max];
		    a[max] = a[a.length-1-i]-a[max];
		    a[a.length-1-i] = a[a.length-1-i]-a[max];
		    }
		}
		return a;
	}


	@Override
	public int[] heapSort(int[] a) throws CKException {
		// TODO Auto-generated method stub
		for(int i=0;i<a.length;i++){
			createMaxHeap(a,a.length-1-i);
			swap(a,0,a.length-1-i);
		}
		return a;
	}
	
	public void createMaxHeap(int data[],int lastIndex){//用子节点大值与父节点比较,直到根,根节点与最后一个子节点对换,最后一个子节点为最大值,一直循环。
		 // 从lastIndex处节点(最后一个节点)的父节点开始
		for(int i=(lastIndex-1)/2;i>=0;i--){
			// 保存当前正在判断的节点
			int k=i;
			// 如果当前k节点的子节点存在
			while(2*k+1<=lastIndex){
				// biggerIndex总是记录较大节点的值,先赋值为当前节点的左子节点的索引
				int biggerIndex = 2*k+1;
				// 若当前节点的右子节点存在
				if(biggerIndex+1<=lastIndex){
					if(data[biggerIndex]<data[biggerIndex+1]){
						// 若右子节点值比左子节点值大,则biggerIndex记录的是右子节点的索引
						biggerIndex++;
					}
				}
				// 如果k节点的值小于其较大的子节点的值
				if(data[k] < data[biggerIndex]){
					swap(data,k,biggerIndex);
					// 将biggerIndex赋予k,开始while循环的下一次循环,重新保证k节点的值大于其左右子节点的值
					k=biggerIndex;
				}else{
					break;
				}
			}
		}
	}
	
	
	public int[] bubbleSort(int[] a) throws CKException {
		// TODO Auto-generated method stub
		for(int i=0;i<a.length;i++){
			for(int j = 0; j<a.length-1;j++){
				if(a[j]>a[j+1]){
				    swap(a,j,j+1);
				}
			}
		}
		return a;
	}
	
	public int[] bubbleSortX(int[] a) throws CKException {
		// TODO Auto-generated method stub
	    int low = 0;
	    int high = a.length - 1;
	    int i;
	    while(low < high){
	    	for(i=low ; i < high ;i++){//正向冒泡,遍历最大值,每次high-1,减少循环次数
	    		if(a[i] > a[i+1]){
	    			swap(a,i,i+1);
	    		}
	    	}
	    	--high;
	    	for(i=high ; i>low ;i--){//逆向冒泡,遍历最小值,每次low+1,减少循环次数
	    		if(a[i] < a[i-1]){
	    			swap(a ,i ,i-1);
	    		}
	    	}
	    	++low;
	    }
		return a;
	}
	
	public int[] quickSort(int[] a) throws CKException{
		// TODO Auto-generated method stub
		quickSort(a,0,a.length-1);
		return a;
	}
	
	private void quickSort(int[] a, int low ,int high){//每次根据low,high得出middle,确定基准,进入quickSort方法筛选一大一小两值,最后将空出的位置填入基准
		if(low<high){//如果不加这个判断递归会无法退出导致堆栈溢出异常
			int middle = getMiddle(a , low , high);
			quickSort(a , 0 , middle - 1);//递归对低子表递归排序
			quickSort(a , middle + 1 ,high);//递归对高子表递归排序
		}
		
	}
	
	public int getMiddle(int[] a,int low ,int high){
		int key = a[low];//基准元素,排序中会空出来一个位置
		while(low<high){
			while(low < high && a[high] >= key){//从high开始找比基准小的,与low换位置
				high--;
			}
			a[low] = a[high];
			while(low < high && a[low] <= key){//从low开始找比基准大,放到之前high空出来的位置上
				low++;
			}
			a[high] = a[low];
		}
		a[low] = key;//此时low=high 是基准元素的位置,也是空出来的那个位置
		return low;
	}
	
	public int[] quickSortX(int[] a) throws CKException{
		//TODO Auto-generated method stub
		quickSortX(a , 0 , a.length -1 ,8);//先调用改进算法quickSortX使之基本有序,k=8
		//再用插入排序对基本有序序列排序
		for(int i = 1; i< a.length ;i++){
			int temp = a[i];
			int j;
			for (j = i - 1 ; j >= 0 && a[j] > temp ;j--){
				a[j+1] = a[j];
			}
			a[j+1] = temp;
		}
		return a;
	}
	
	private void quickSortX(int[] a , int low ,int high ,int k){
		if(high - low > k){//长度大于k时递归, k为指定的数
			int pivot = partition(a , low , high);
			quickSortX(a , low , pivot - 1 , k);
			quickSortX(a , pivot + 1 ,high , k);
		}
	}
	
	private int partition(int[] a , int low ,int high){
		int privotKey = a[low];//基准元素
		while(low < high){//从表的两端交替地向中间扫描
			while(low < high && a[high] >= privotKey){//从high 所指位置向前搜索,至多到low+1 位置。将比基准元素小的交换到低端
				high--;
			}
			swap(a , low , high);
			while(low < high && a[low] <= privotKey){//将比基准元素大的交换到high
				low++;
			}
			swap(a , low , high);
		}
		return low;
	}
	
	
   
    public int[] mergeSort(int[] a) {//创建一个[ , ]  放入a有序的a[0] a[1]  , 再创建一个[ , , ] ,放入有序的 a[0]  a[1]  a[2]   .........
        mergeSort(a, 0 , a.length - 1);
        return a;
    }

    public void mergeSort(int[] a, int low, int high) {
        int mid = (low + high) / 2;
        if (low < high) {
            // 左边
            mergeSort(a, low, mid);
            // 右边
            mergeSort(a, mid + 1, high);
            // 左右归并
            merge(a, low, mid, high);
        }
    }

    public void merge(int[] a, int low, int mid, int high) {
        int[] temp = new int[high - low + 1];
        int i = low;// 左指针
        int j = mid + 1;// 右指针
        int k = 0;
        // 把较小的数先移到新数组中
        while (i <= mid && j <= high) {
            if (a[i] < a[j]) {
                temp[k++] = a[i++];
            } else {
                temp[k++] = a[j++];
            }
        }
        // 把左边剩余的数移入数组
        while (i <= mid) {
            temp[k++] = a[i++];
        }
        // 把右边边剩余的数移入数组
        while (j <= high) {
            temp[k++] = a[j++];
        }
        // 把新数组中的数覆盖a数组
        for (int k2 = 0; k2 < temp.length; k2++) {
            a[low + k2] = temp[k2];
        }
    }
	
    /**
     * 桶排序/基数排序
     * @param
     */
    public int[] radixSort(int[] a) {
        // 找到最大数,确定要排序几趟
        int max = 0;
        for (int i = 0; i < a.length; i++) {
            if (max < a[i]) {
                max = a[i];
            }
        }
        // 判断位数
        int times = 0;
        while (max > 0)  {
            max = max / 10;
            times++;
        }
        // 建立十个队列
        List<ArrayList> queue = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            ArrayList queue1 = new ArrayList();
            queue.add(queue1);
        }
        // 进行times次分配和收集
        for (int i = 0; i < times; i++) {
            // 分配
            for (int j = 0; j < a.length; j++) {
                int x = a[j] % (int) Math.pow(10, i + 1) / (int) Math.pow(10, i);
                ArrayList queue2 = queue.get(x);
                queue2.add(a[j]);
                queue.set(x, queue2);
            }
            // 收集
            int count = 0;
            for (int j = 0; j < 10; j++) {
                while (queue.get(j).size() > 0) {
                    ArrayList<Integer> queue3 = queue.get(j);
                    a[count] = queue3.get(0);
                    queue3.remove(0);
                    count++;
                }
            }
        }
        return a;
    }	
	public void swap(int data[] ,int i , int j){
		if(i==j){
			return;
		}
		data[i] = data[i] + data[j];
		data[j] = data[i] - data[j];
		data[i] = data[i] - data[j];
	}
}

排序选择

排序算法选择:

1)当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序。

快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;
归并排序:它有一定数量的数据移动,所以我们可能过与插入排序组合,先获得一定长度的序列,然后再合并,在效率上将有所提高。

2) 当n较大,内存空间允许,且要求稳定性 =》归并排序
3) 当n较小,可采用直接插入或直接选择排序。

直接插入排序:当元素分布有序,直接插入排序将大大减少比较次数和移动记录的次数。
直接选择排序 :元素分布有序,如果不要求稳定性,选择直接选择排序

5)一般不使用或不直接使用传统的冒泡排序。
6)基数排序 :

它是一种稳定的排序算法,但有一定的局限性: 
关键字可分解
记录的关键字位数较少,如果密集更好

排序选择摘自:
作者:huaxun66
原文:https://blog.youkuaiyun.com/huaxun66/article/details/77847998


总结

以上代码中部分注释为方便自己理解及回忆的注释,方便理解,但不包含代码整体意义。
代码日记:2019/06/26
心得体会:一绕两绕三四绕,五绕六绕七八绕,千绕万绕无数绕,绕入循环看不见。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值