美文网首页程序视界算法
排序算法系列(1)——快速排序

排序算法系列(1)——快速排序

作者: 阿飞不理飞 | 来源:发表于2017-04-21 16:25 被阅读14次

    前几天被一哥们儿实力嘲讽,问我快速排序怎么实现,我只是记得自己学过,但是忘记了具体怎么实现,原理是什么ORZ,然后LL就努力去百度学习,汲取,大致将一些自己的感受:

    注意!文档只是以我的想法来进行阐述,只是尽量以我能理解的方式来解释算法原理,只是为了将来上了年纪,或者忘记了这个原理的时候来学习一下。

    废话不多说,套用之前那哥们讲话,冒泡是最简单的排序方法
    那么接下来
    讲一下快速排序

    情景拟定:

    一个数组array,需要将其从由小到大的顺序排列
    array = { 4 , 5 , 2 , 22 , 11 , 32 , 2 , 5 }
    快速排序原理是这样子的:

    分治的原理:

    首先选定一个元素a,然后将这个数组中比这个元素小的元素们放到该元素的左边,将数组中比这个元素大的元素放到该元素的右边。 至此a元素的位置已经排好了,剩下的就是由a将数组划成两半进行排序

    每一半排序方式同上。
    这样子最后肯定能排好序
    明显能够看出这是**递归**的思想

    然后问题来了,首先怎么具体的将a放到他应该的位置?
    是这样子的
    /(比较方便,确保了只是将除了该a之外的其余数字只比较一遍)→往下看就能理解我这句话了/

    首先我们需要给这个数组定义一个首、尾index,比如 i=0,j=array.length-1=7;
    假定我们需要排的元素 a=array[0]=4
    那么首先我们从array[j]处向前遍历比较
    array[7]=5>4 正常 j--
    array[6]=2<4 应该在4左边,所以将array[0]=2
    此时 j = 6 ;
    array现在是这样子的 array = {2 , 5 , 2 , 22 , 11 , 32 , 空 ,5}

    那么现在array[6] (也就是array[j]) 空出来了

    我们这时候应该看一下i往后了
    array[0] < a 正常 i++
    array[1] = 5 > a 不对哦 比4大应该在右边
    所以将5放到刚才上面缺的地方
    array[j] = 5
    那么现在是array = {2,空,2,22,11,32,5,5}
    i = 1
    现在array[1] 也就是array[i] 空了

    接下来接着从j的位置向前
    array[6] = 5 < 4 okay j--
    array[5] = 32 >4 okay j--
    array[4] = 11 > 4 okay j--
    array[3] = 22 > 4 okay j--
    array[2] = 2 < 4 换换换
    array[i] = array[1] = 2
    那么现在 j 为2了,
    array = {2,2,空,22,11,32,5,5}

    再从i开始
    i=1
    array[i] = 2 < 4 okay i++
    此时 i=2=j 交头了说明over了
    那么将刚才最开始的a 放到array[i] or array[j]位置上就好了 (反正 i 和 j 相等了)
    array = {2,2,4,22,11,32,5,5}

    那么接下来需要对array的两个子串
    array1={2,2}
    array2={22,11,32,5,5}
    然后对子串进行像刚才那样的顺序排序

    package quickSort;
    import java.util.Arrays;
    /**
     * 递归实现快速排序
     * @author mengf
     *
     */
    public class Qsort {
        
        public static int[] quickSort(int[] array,int start , int end){
            int i = start;
            int j = end;
            int a = array[i];
            
            //好的情况
            while (i!=j&&i<j) {
                
                while (j!=i) {
                    if (array[j]<a) {
                        array[i]=array[j];
                        i++;
                        break;
                    }else{
                        j--;
                    }
                }
                
                while (j!=i) {
                    if (array[i]>a) {
                        array[j]=array[i];
                        j--;
                        break;
                    }else{
                        i++;
                    }
                }
            }
            
            //然后最后i=j
            array[i]=a;
            //分成两半
            if (i>start) {
                quickSort(array, start, i-1);
            }
            if (i<end) {
                quickSort(array, i+1, end);
            }
            return array;
        }
        
        public static void main(String[] args) {
            int[] array = {12,10,22,5,6,8,11,7,3,4,5,33,32,23,34,32,43,34};
            quickSort(array, 0, array.length-1);
            System.out.println(Arrays.toString(array));
        }
    }
    

    所以不难看出如果要实现递归实现是很好理解的
    注:如果有大神看到这里肯定会觉得我上面写的有些问题
    我承认我顺着思路写完之后其实有地方需要改进的
    !就是需要替换的时候,比如j往前跟4对比,遇到需要替换的时候,将array[i]替换之后,可以将i++
    因为替换到i位置上肯定已经满足这个数字<4 所以将i++之后
    从i这边开始的时候就不需要重新再将刚替换的位置的数字和a再比较了。

    下面开始看一下递归的java代码实现:

    然后后来百度了一下百科是这样写的,原理是一样的

    package quickSort;
    import java.util.Arrays;
    public class QuickSort {
        
        
        /**
         * 无边界检查 
         * 完全专注于算法
         * @param arrays
         * @param start
         * @param end
         * @return
         */
        public int[] quickSort(int[] arrays,int low,int high){
            int start = low;
            int end = high;
            //只有一个元素的时候是排好序的了
            if (start==end) {
                return arrays;
            }
            
            //算法开始
            int temp = arrays[start];
            
            while (start<end) {
                
                while (start<end && arrays[end]>=temp) {
                    end--;
                }
                arrays[start]=arrays[end];
                while (start<end && arrays[start]<=temp) {
                    start++;
                }
                arrays[end]=arrays[start];
            }
            
            //start = end
            arrays[start]=temp;
            //迭代两边排序
            if (start>low) {
                quickSort(arrays, 0, start-1);
            }
            if (end<high) {
                quickSort(arrays,start+1,arrays.length-1);
            }
            
            return arrays;
        }
        public static void main(String[] args) {
            QuickSort quickSort = new QuickSort();
            int[] arrays = {1,11,35,2,3,5,323,213,22,3,1,2,3,4,5,3};
            quickSort.quickSort(arrays, 0, arrays.length-1);
            System.out.println(Arrays.toString(arrays));
        }
    }
    

    臭美一下,我觉得这个大概就是我之前没有在换位置之后将对应的位置的 i++或者 j-- 这样感觉会多一次判断,虽然对算法复杂度没有影响。
    再就是如果加上了的话这个写法比我写的好多了,判断写在while里面
    而我的是while中又有if,感觉效率没有这个高。

    算法复杂度: O(n*logn)

    但是写到这里,我有问题了
    递归对伐,大家都知道浪费空间的做法,但是递归对于程序的理解有着极大的帮助,而且我记得数据结构老师说过,所有的递归都能改为循环的,这就是对于这个问题的下一步探讨了,我先学习下,等搞懂了再搞一篇优化后的快速排序。

    现在说说我觉得这样的<u>局限</u>在哪里:

    如果排序对象较大,数目较多,内存可能不够,虽然很少有出现这种情况,但是如果能够改成循环,而放弃递归
    无疑是对能力的提升,像我这种懒人还是觉得递归好,起码理解起来简单,至于循环,最近看情况找找资料学学,要是没有下一篇了的话,可能LL已经忘记这件事情了,大家可以先用着递归,毕竟现在普通项目中也不太可能遇到占用很大内存来递归的情况。

    相关文章

      网友评论

        本文标题:排序算法系列(1)——快速排序

        本文链接:https://www.haomeiwen.com/subject/qmidzttx.html