快速排序和希尔排序算法实现Java版

174 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

排序算法是软件开发中非常重要的一环。本期就来讨论关于算法在面试中会遇到的一些问题,其中包含个人一些理解,由于本人学识有限,难免会有纰漏之处,还望读者能多多包涵。

上一期我们讲了归并排序和插入排序。本期继续讲解其他的排序算法。

快速排序

与上一期的归并排序一样,快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。

快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要 O(nlogn)Ο(nlogn) 次比较。在最坏状况下则需要 O(n2)Ο(n^2) 次比较,但这种状况并不常见。事实上,快速排序通常明显比其他 O(nlogn)Ο(nlogn) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。它是处理大数据最快的排序算法之一。

image.png

文字描述过程就是(从小到大排序):在数组中随机选一个数(默认数组首个元素),数组中小于等于此数的放在左边,大于此数的放在右边,再对数组两边递归调用快速排序,重复这个过程。动图如下:

quickSort.gif

Java实现如下:

        public static void quickSort(int[] a, int left, int right)  {  
	    int i,j,t,temp;  
	    if(left>right)   //(递归过程先写结束条件)
	       return ;  

	    temp=a[left]; //temp中存的就是基准数  
	    i=left;  
	    j=right;  
	    while(i!=j)  
	    {  
	                   //顺序很重要,要先从右边开始找(最后交换基准时换过去的数要保证比基准小,因为基准                               
	                   //选取数组第一个数,在小数堆中) 
	                   while(a[j]>=temp && i<j)  
	                            j--;  
	                   //再找右边的  
	                   while(a[i]<=temp && i<j)  
	                            i++;  
	                   //交换两个数在数组中的位置  
	                   if(i<j)  
	                   {  
	                            t=a[i];  
	                            a[i]=a[j];  
	                            a[j]=t;  
	                   }  
	    }  
	    //最终将基准数归位 (之前已经temp=a[left]过了,交换只需要再进行两步)
	    a[left]=a[i];  
	    a[i]=temp;  

	    quickSort(a,left,i-1);//继续处理左边的,这里是一个递归的过程  
	    quickSort(a,i+1,right);//继续处理右边的 ,这里是一个递归的过程  
	}  
        public static void main(String[] args) throws IOException { 
		
		int[] array= {5,0,1,2,81,-4,21,9,11};
		quickSort(array, 0, array.length-1);
		 //输出
	    for (int j = 0; j < array.length; j++) {
			System.out.print(array[j]+" ");
		}
		
	}

总结一下就是先把一个大数组通过第一个元素将之分割成2个小的数组,并且以该轴点为界,小于它的在左边,大于它的在右边,然后递归对2个小数组执行步骤1、2操作,直到不能再分割。

希尔排序

希尔排序(Shell Sort)是插入排序的一种,它是针对直接插入排序算法的改进。希尔排序步长从大到小调整,第一次循环后面元素逐个和前面元素按间隔步长进行比较并交换,直至步长为1,步长选择是关键。

希尔排序时间复杂度是 O(n(1.32))O(n^{(1.3-2)}),空间复杂度为常数阶 O(1)O(1)。希尔排序没有时间复杂度为 O(n(logn))O(n(logn)) 的快速排序算法快 ,因此对中等大小规模表现良好,但对规模非常大的数据排序不是最优选择,总之比一般 O(n2)O(n^2 ) 复杂度的算法快得多。

image.png

文字描述其过程:为了加快速度,交换不相邻的元素对数组的局部进行排序,并最终用插入排序将局部有序的数组排序。在此我们选择增量 gap=length/2gap=length/2,缩小增量以 gap=gap/2gap = gap/2 的方式,用序列 {n/2,(n/2)/2...1}\{n/2,(n/2)/2...1\}来表示。

Java实现如下:

        public static void ShellSort(int[] array)  {  
            int n=array.length,i,j,temp;
	    int gap=n; 
	    do{
	        gap=gap/2;  
	        for( i=gap;i<n;i++ )
	        {
	            if(array[i]<array[i-gap])
	            {
	                temp=array[i];
	                for( j=i-gap; j>=0&&array[j]>temp; j-=gap)
	                {
	                    array[j+gap]=array[j];
	                }
	                array[j+gap]=temp;
	            }
	        }
	    }while(gap>1);
	  //输出
	    for (int k = 0; k < array.length; k++) {
			System.out.print(array[k]+" ");
		}

	}  
        
        public static void main(String[] args) throws IOException { 
		
		int[] array= {5,0,1,2,81,-4,21,9,11};
		ShellSort(array);	
	}

以上就是希尔排序,它利用分组粗调的方式减少了直接插入排序的工作量,使得算法的平均时间复杂度低于O(n2)O(n^2)