一、交换类(transposition)排序

1、冒泡(Bubble)排序:

时间复杂度:O(n^2); 空间复杂度:O(1)

原理:进行n-1次循环,每次循环可以排好一个最大值。相邻的两个数进行比较,是最大值不断后移(类似于气泡上浮)

代码实现:

public static void get_sort(int[] arr) {
		//冒泡排序
		for(int i=0;i<arr.length-1;i++) { //需要浮动n-1次(当n-1个值排好序后,剩下的一定为最小值)
			for(int j=0;j<arr.length-i-1;j++) {//对相邻的元素进行比较,筛选出最大值,上浮
				if(arr[j]>arr[j+1]) {  
					int temp=arr[j];
					arr[j]=arr[j+1];
					arr[j+1]=temp;
				}
			}
		}

是所有排序算法中最慢的算法

2、快速排序

时间复杂度:O(n*logN)空间复杂度:O(1)

原理: 它利用分治法来对待排序序列进行分治排序,其主要是通过一趟排序将待排记录分隔成独立的两部分,其中的一部分比关键字小,后面一部分比关键字大,然后再对这前后的两部分分别采用这种方式进行排序,通过递归的运算最终达到整个序列有序。

代码实现

public static void Quick(int[] arr,int begin,int end) { 
		if(begin<end) {
			int i,j,temp,temp2;
			temp=arr[begin];
			i=begin; //必须是begin,避免进行强制交换
			j=end;
			while(i<j) {
				while(i<j && arr[j]>=temp) { //必须先右面递减,必须得带等号,因为与temp交换的目的是 将temp调整到合适的额位置
					j--;
				}
				while(i<j && arr[i]<=temp) {
					i++;
				}
				
				if(i<j) {
					temp2=arr[i];
					arr[i]=arr[j];
					arr[j]=temp2;
				}
			}
			arr[begin]=arr[j]; //将关键字放于正确的位置上
			arr[j]=temp;
			
			Quick(arr,begin,j-1);
			Quick(arr,j+1,end);
		}
		
	}

参考链接:

二、插入类排序

3、插入排序

时间复杂度:O(n^2) 空间复杂度:O(1)

原理:它的工作原理是通过构建有序序列(数组),对于未排序数据(基准元素),在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

java 大数据 排序 java十大排序算法_i++


代码实现

public static void insert(int [] arr) {
		for(int i=1;i<arr.length;i++) {
			int temp=arr[i];  //保存基准元素
			int j=i-1;  //定义比较元素
			while(j>=0 && arr[j]>temp) {  //将所有元素分别于基准元素比较
				arr[j+1]=arr[j]; //元素后移
				j--;
			}
			arr[j+1]=temp; //将基准元素插入到合适的位置(因为第j个元素一定不大于temp)
			
		}
	}

4、希尔排序(Shell Sort)

空间复杂度:O(1) ; 时间复杂度:不确定,在平均状况下为O(n^1.3)

原理:是插入排序的一种是对插入排序的优化。将数组列在一个表中,并对列分别进行插入排序,重复这过程,不过每次用更长的列(步长更长了,列数更少了)来进行。最后整个表就只有一列了。

实现

public static void insert(int [] arr) {
		for(int gap=arr.length/2;gap>0;gap/=2) {  //进行间隔的分类
			for(int i=gap*1;i<arr.length;i++) { //选取第二个元素
				int temp=arr[i];
				int j=i-gap;
				while( j>=0 && arr[j]>temp ) { //进行判断
					arr[j+gap]=arr[j];
					j-=gap;
				}

				arr[j+gap]=temp;
			}
		}		
	}

参考链接:

三、选择类排序

5、选择排序(Selection Sort)

时间复杂度:O(n^2); 空间复杂度:O(1)

原理:进行n-1次循环,每次循环中可以排列出最小值放于最前面:选择X[i]与其以后的元素的最小值进行交换,来确保X[i]是最小值

代码实现

public static void selection(int[] arr) {
		for(int i=0;i<arr.length;i++) {
			int swap=i;  //依次对n个元素进行排序,共需要n-1次循环
			for(int j=i+1;j<arr.length;j++) {
				if(arr[swap]>arr[j]){
					swap=j;  //始终保证swap是最小值对应的索引
				}
			}
			if(swap!=i) {  //若不是原索引,则进行交换,确定最小的元素
				int temp=arr[swap];
				arr[swap]=arr[i];
				arr[i]=temp;
			}
		}
		for(int i:arr) {
			System.out.print(i+"  ");
		}
		System.out.println();
	}

6、堆排序(Heap Sort)

时间复杂度:O(nlogN); 空间复杂度:O(n)

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

堆排序:堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

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

java 大数据 排序 java十大排序算法_java 大数据 排序_02


代码实现:

public static void heap_sort(int[] arr) {
		for(int i=(arr.length-1)/2;i>=0;i--) {//首先将数据调整为一个堆
			adjust(arr,i,arr.length-1);
		}
		for(int i=arr.length-1;i>0;i--) {//,调换数据的顺序,并且将生育元素调整为一个堆
			int temp=arr[i];
			arr[i]=arr[0];
			arr[0]=temp;
			adjust(arr,0,i-1);
			
		}
	}
	public static void adjust(int[] arr,int start,int end) {
		int temp=arr[start];
		for(int i=start*2+1;i<=end;i=i*2+1) {
			if(i+1<=end &&arr[i+1]>arr[i]) { //取子节点的最大值,此处必须为&&以保证索引不会越界
				i++;
			}
			if(arr[i]>temp) { //将子节点的值,给父节点
				arr[start]=arr[i];
				start=i;
			}else {
				break;
			}
			
		}
		arr[start]=temp;//将temp放到合适的位置
	}

参考链接:

7、归并排序

时间复杂度:O(nlogN) 空间复杂度:O(n)

***原理:***:归并排序和快速排序一样,利用的是分治的思想实现的。归并排序对于给定的一组数据,利用递归与分治技术将数据序列划分成为越来越小的子序列,之后对子序列排序,最后再用递归方法将排好序的子序列合并成为有序序列。合并两个子序列时,需要申请两个子序列加起来长度的内存,临时存储新的生成序列,再将新生成的序列赋值到原数组相应的位置。

java 大数据 排序 java十大排序算法_i++_03

代码实现

public static void merge_sort(int[] arr,int start,int end) {  //此函数中的所有arr都是原arr,因为改变的是地址
		if(start<end) { //当只有一个元素的时候退出
			int mid=(start+end)/2;
			merge_sort(arr,start,mid);
			merge_sort(arr,mid+1,end);
			merge(arr,start,mid,end);//将数据按照顺序合并
		}
	}
	public static void merge(int[] arr,int start,int mid,int end) {
		int[] a=new int[end-start+1];  //辅助数组
		int p1=start;
		int p2=mid+1;
		int k=0;  
		while(p1<=mid && p2<=end) {
			if(arr[p1]<arr[p2]) {
				a[k++]=arr[p1++];
			}else {
				a[k++]=arr[p2++];
			}
		}
		while(p1<=mid) { //如果第一个序列未检测完,直接将后面所有元素加到合并的序列中
			a[k++]=arr[p1++];
		}
		while(p2<=end) {
			a[k++]=arr[p2++];
		}
		for( int i=0;i<a.length;i++) {//将已经比较好的值赋值给数组
			arr[i+start]=a[i];
		}
	}

参考链接:

五、线性时间非比较类排序

8、计数排序(Count Sort)

时间复杂度 O(n+k) 空间复杂度:O(k)

K为数组的最大值,因为空间复杂度与时间复杂度都与K有关,所以数组的最大值不能过大,且数组中元素都为自然数,所有元素不能有负数

原理:对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数。一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上。例如,如果输入序列中只有17个元素的值小于x的值,则x可以直接存放在输出序列的第18个位置上。当然,如果有多个元素具有相同的值时,我们不能将这些元素放在输出序列的同一个位置上,在代码中作适当的修改即可。

代码实现

public static void countingSort( int[] arr,int k) {//k为数组arr的最大值,所以最大值必须已知
    	int[] arr1=new int [k+1];
    	int [] arr2=new int[arr.length];
    	for(int i=0;i<arr.length;i++) { //统计每个值出现的次数
    		arr1[arr[i]]++;
    	}
    	int sum=0;
    	for(int i=0;i<arr1.length;i++) {//统计每个值的大小(从一开始)
    		sum+=arr1[i];
    		arr1[i]=sum;
    	}
    	print(arr1);
    	for(int i=0;i<arr.length;i++) {  //对数组赋值,不明白为什么要倒序赋值??????????个人觉得正序也可以
    		arr2[arr1[arr[i]]-1]=arr[i];
    		arr1[arr[i]] -= 1;  //一定要加,防止重复元素消失,这样可使重复的元素依次向前输出
    	}
    	print(arr2);
    }

参考链接:

为啥一定要逆序赋值???????????(没理解这样做的原因,欢迎解答!!!!)

9、基数排序(Radix Sort)

时间复杂度:O(N^k(最大数的位数)); 空间复杂度:O(n)

原理: :基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯(即通过个位,十位,百位…),将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog®m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。个人感觉很像计数排序,但是增加了循环次数,减少了空间复杂度

代码实现

public static int get_max(int[] arr) {
		int m=arr[0];
		for(int i=1;i<arr.length;i++) {
			if(m<arr[i]) {
				m=arr[i];
			}
		}
		return (m+"").length();
	}
	public static int get_num(int number,int num) {
		int[] a = {1,10,100};
        return (number/a[num-1])%10;
	}
	public static void countingSort( int[] arr,int k) {//k为数组arr的最大值的位数
    	int [] bucket=new int[arr.length];
    	int[] count=new int [10];
    	for( int w=1;w<=k;w++) {
    		for(int i=0;i<count.length;i++) {
    			count[i]=0;
    		}
    		for(int i=0;i<arr.length;i++) { //统计每个值出现的次数
        		count[get_num(arr[i],w)]++;
        	}
        	int sum=0;
        	for(int i=0;i<count.length;i++) {//统计每个值的大小(从一开始)
        		sum+=count[i];
        		count[i]=sum;
        	}
        	
        	for(int i=arr.length-1;i>=0;i--) {  //对数组赋值,不明白为什么要倒序赋值?个人觉得正序也可以
        		int x=get_num(arr[i],w);
        		bucket[count[x]-1]=arr[i];
        		count[x] -= 1;  //一定要加,防止重复元素消失,这样可使重复的元素依次向前输出
        	}
        	for( int i=0;i<arr.length;i++) { //将数组元素给arr,重新排序
        		arr[i]=bucket[i];
        	}
        
    	}
    	print(arr);
    }

参考链接:

10、桶排序

要求:数组的每个元素的位数必须相同

原理
桶排序的思想近乎彻底的分治思想。同样类似于计数排序。
桶排序假设待排序的一组数 均匀独立的分布在一个范围中 ,并将这一范围划分成几个子范围(桶)。然后基于某种映射函数f ,将待排序列的关键字 k 映射到第i个桶中 (即桶数组B 的下标i) ,那么该关键字k 就作为 B[i]中的元素 (每个桶B[i]都是一组大小为N/M 的序列 )。
接着将各个桶中的数据有序的合并起来 : 对每个桶B[i] 中的所有元素进行比较排序 (可以使用快排)。然后依次枚举输出 B[0]….B[M] 中的全部内容即是一个有序序列。
补充: 映射函数一般是 f = array[i] / k; k^2 = n; n是所有元素个数
为了使桶排序更加高效,我们需要做到这两点:
在额外空间充足的情况下,尽量增大桶的数量
使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中
同时,对于桶中元素的排序,选择何种比较排序算法对于性能的影响至关重要。

java 大数据 排序 java十大排序算法_空间复杂度_04

代码实现

public static double[] bucketSort(double arr[], int bucketCount) {
 
    int len = arr.length;
    double[] result = new double[len];
    double min = arr[0];
    double max = arr[0];
    //找到最大值和最小值
    for (int i = 1; i < len; i++) {
        min = min <= arr[i] ? min: arr[i];
        max = max >= arr[i] ? max: arr[i];
    }
    //求出每一个桶的数值范围
    double space = (max - min + 1) / bucketCount;
    //先创建好每一个桶的空间,这里使用了泛型数组
    ArrayList < Double > [] arrList = new ArrayList[bucketCount];
    //把arr中的数均匀的的分布到[0,1)上,每个桶是一个list,存放落在此桶上的元素   
    for (int i = 0; i < len; i++) {
        int index = (int) Math.floor((arr[i] - min) / space);
        if (arrList[index] == null) {
            //如果链表里没有东西
            arrList[index] = new ArrayList < Double > ();
            arrList[index].add(arr[i]);
        } else {
            //排序
            int k = arrList[index].size() - 1;
            while (k >= 0 && (Double) arrList[index].get(k) > arr[i]) {
                if (k + 1 > arrList[index].size() - 1) {
                    arrList[index].add(arrList[index].get(k));
                } else {
                    arrList[index].set(k + 1, arrList[index].get(k));
                }
                k--;
            }
            if (k + 1 > arrList[index].size() - 1) {
                arrList[index].add(arr[i]);
            } else {
                arrList[index].set(k + 1, arr[i]);
            }
        }
 
    }
 
    //把各个桶的排序结果合并  ,count是当前的数组下标
    int count = 0;
 
    for (int i = 0; i < bucketCount; i++) {
        if (null != arrList[i] && arrList[i].size() > 0) {
            Iterator < Double > iter = arrList[i].iterator();
            while (iter.hasNext()) {
                Double d = (Double) iter.next();
                result[count] = d;
                count++;
            }
        }
    }
    return result;
}
//开始排序,其中arr为需要排序的数组
double[] result = bucketSort(arr,bucketCount);

参考链接:

总结

java 大数据 排序 java十大排序算法_i++_05

疑问:计数排序与基数排序为什么要逆序遍历赋值,原因何在????????????????

若有不足,请多指教,定当洗耳恭听。