详解快速排序--java版

在所有的排序算法中,快速排序当然是非常重要的,毕竟速度如其名—快速。
在最开始接触快速排序的时候很难理解他的思想,经过我认真琢磨终于知道是怎么回事了,下面就介绍一下关于快速排序的一些基本内容。

快速排序的思想

要了解一个算法,那么算法的思想首先要知道。
假定给定了一个整型的数组,要将数组升序排列。现在使用快速排序的思想来解决这个问题:
分为以下几个步骤:

1.从数组里面取出一个key值。
2.大于key的值就放在key的后面。
3.小于key的值就放在key的前面。

经过上123步骤一次之后,key值前面的都比key小,key值后面的都比key大。所以key的位置就不用动了,也不需要再次对key进行排序了。记数组的第一个元素的下标为lowindex,数组最后一个元素的下标记为highindex,key值的下标为keyIndex。那么就接下来需要排序的数组元素的下标为[lowindex,keyIndex-1]和[keyIndex+1,highIndex](key的位置不需要再动了)

然后在对这两个区间的元素做上面的123操作,最终这两个区间也会得到两个已经确定好位置的key值,继续划分区间重复123操作,一直到被划分的区间里面只有1个或者0个元素,这样就表示这个小区间已经有序了。然后调整到所有的小区间已经有序。那么整个数组就是有序的了。

上面的基本思想我们可以构思一下代码如何写?从基本描述来看,是不是很像递归的思想,那么如果使用递归,哪一个关键的值是让递归里面进行操作的对象不断变化的呢?仔细想想,那就只有keyIndex了。这样每一次递归要被操作的区间就是不同的。
所以我们可以写出一个递归的方案如下:

public static void quickSort(int[] arr){
    
    
        sortInternal(arr,0,arr.length-1);
    }
    public static void sortInternal(int[] arr,int lowIndex,int highIndex){
    
    
        int size=highIndex-lowIndex+1;
        if(size<=1){
    
    
            return;
        }
        int keyIndex=partition(arr,lowIndex,highIndex);
        sortInternal(arr,lowIndex ,keyIndex-1);
        sortInternal(arr,keyIndex+1,highIndex);
    }

框架已经有了,那么如何确定这个key值的位置呢。

确定key的下标的方法

确定key具体下标的位置的方法有挺多的,这里先描述最基础的hover法。后面有机会会补充其他方法。
由于不知道选哪一个值作为key合适,所以就干脆选择数组的第一个元素作为比较的key值(选择其他的值也可以,这里只是为了描述思想方便选了第一个)。
这就变成了在一个数组里,小于或等于key值的元素放在前面,大于key值的元素放在后面-----假设这个过程叫比较key。最终就形成一个分界,前面全部是小于等于key的,后面全部是大于等于key的,然后把最初选定的key的位置与属于小于等于区间的最后一个元素交换----假设这个过程叫做交换key。这就确保了key前面的值都小于或等于他,key后面的值大于他。
下面用图来描述一下过程
在这里插入图片描述
图中省略了每一个循环的结果,只画出了循环结束时的状态,一定要自己手动手动走每一层循环。这样会理解的更加深入。
上面图中已经将“key交换阶段操”作完毕。所以这个key的位置已经是固定的了,前面的都比他小,后边的都比他大。所以只用得到他的下标。然后对他两边相邻着但不包括他的两个区间继续做重复操作。
下面是partition的代码,可以参考:

public static int partition(int[] arr, int lowIndex, int highIndex){
    
    
        int headIndex=lowIndex;
        int tailIndex=highIndex;
        int key=arr[lowIndex];
        while(headIndex<tailIndex){
    
    
            if(arr[tailIndex]>key){
    
    
                tailIndex--;
            }else if(arr[tailIndex]<=key){
    
    
                if(arr[headIndex]>key){
    
    
                    swap(arr,headIndex,tailIndex);
                    headIndex++;
                    tailIndex--;
                }
                else if(arr[headIndex]<=key){
    
    
                    headIndex++;
                }
            }
        }
        swap(arr,lowIndex,tailIndex);
        return tailIndex;
    }

快排的相关性质和特点

1.快速排序是一下好难过比较快的排序方法,原因是因为他的比较次数较少,并且他交换元素的幅度更大一些。他的时间复杂度是O(nlog(n)).
并且快速排序比较适合大量数据。当数据量比较小的时候使用他就不太合适。所以当数字小于16个的时候,使用插入排序更加高效。在java中sort方法里小于16个数的比较是使用插入排序。
2.关于partition()这个方法。假如把一个数组想想成为一棵树。那么partition方法就是拿出了一颗二叉搜索树 的根(第一次不规范,后来慢慢变得规范)。所以他的平均时间复杂度就是O(logn)。如果是逆序数组,那就是O(n)的时间复杂度。
3.partition()的方法也有其他的,下次补充。

最后,如果觉得理解了欢迎一键三连,也欢迎指点。

猜你喜欢

转载自blog.csdn.net/weixin_43815275/article/details/109398316