快速排序 (MEDIUM)

流程

快速排序算法通过多次比较和交换来实现排序,其排序流程如下:
(1)首先设定一个分界值,通过该分界值将数组分成左右两部分。
(2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
(3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
(4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。

复杂度

快速排序的一次划分算法从两头交替搜索,直到low和hight重合,因此其时间复杂度是O(n);而整个快速排序算法的时间复杂度与划分的趟数有关。
理想的情况是,每次划分所选择的中间数恰好将当前序列几乎等分,经过log2n趟划分,便可得到长度为1的子表。这样,整个算法的时间复杂度为O(nlog2n)
最坏的情况是,每次所选的中间数是当前序列中的最大或最小元素,这使得每次划分所得的子表中一个为空表,另一子表的长度为原表的长度-1。这样,长度为n的数据表的快速排序需要经过n趟划分,使得整个排序算法的时间复杂度为O(n2)。
为改善最坏情况下的时间性能,可采用其他方法选取中间数。通常采用“三者值取中”方法,即比较H->r[low].key、H->r[high].key与H->r[(low+high)/2].key,取三者中关键字为中值的元素为中间数。
可以证明,快速排序的平均时间复杂度也是O(nlog2n)。因此,该排序方法被认为是目前最好的一种内部排序方法。
从空间性能上看,尽管快速排序只需要一个元素的辅助空间,但快速排序需要一个栈空间来实现递归。最好的情况下,即快速排序的每一趟排序都将元素序列均匀地分割成长度相近的两个子表,所需栈的最大深度为log2(n+1);但最坏的情况下,栈的最大深度为n。这样,快速排序的空间复杂度为O(log2n)

原理

快速排序,说白了就是给基准数据找其正确索引位置的过程.。
如下图所示,假设最开始的基准数据为数组第一个元素23,则首先用一个临时变量去存储基准数据,即tmp=23;然后分别从数组的两端扫描数组,设两个指示标志:low指向起始位置,high指向末尾.
在这里插入图片描述
   首先从后半部分开始,如果扫描到的值大于基准数据就让high减1,如果发现有元素比该基准数据的值小(如上图中18<=tmp),就将high位置的值赋值给low位置 ,结果如下:
在这里插入图片描述

然后开始从前往后扫描,如果扫描到的值小于基准数据就让low加1,如果发现有元素大于基准数据的值(如上图46=>tmp),就再将low位置的值赋值给high位置的值,指针移动并且数据交换后的结果如下:
在这里插入图片描述
然后再开始从后向前扫描,原理同上,发现上图11<=tmp,则将low位置的值赋值给high位置的值,结果如下:
在这里插入图片描述
然后再开始从前往后遍历,直到low=high结束循环,此时low或high的下标就是基准数据23在该数组中的正确索引位置.如下图所示.
在这里插入图片描述
  这样一遍走下来,可以很清楚的知道,其实快速排序的本质就是把基准数大的都放在基准数的左边,把比基准数小的放在基准数的右边,这样就找到了该数据在数组中的正确位置.
  以后采用递归的方式分别对前半部分和后半部分排序,当前半部分和后半部分均有序时该数组就自然有序了。

结论

从上面的过程中可以看到:

①先从队尾开始向前扫描且当low < high时,如果a[high] > tmp,则high–,但如果a[high] < tmp,则将high的值赋值给low,即arr[low] = a[high],同时要转换数组扫描的方式,即需要从队首开始向队尾进行扫描了
②同理,当从队首开始向队尾进行扫描时,如果a[low] < tmp,则low++,但如果a[low] > tmp了,则就需要将low位置的值赋值给high位置,即arr[low] = arr[high],同时将数组扫描方式换为由队尾向队首进行扫描.
③不断重复①和②,知道low>=high时(其实是low=high),low或high的位置就是该基准数据在数组中的正确索引位置.

源码

/**
 * https://blog.csdn.net/nrsc272420199/article/details/82587933
 * 快速排序,说白了就是给基准数据找其正确索引位置的过程.
 */
public class QuickSort {

    public static void quickSort(int s[], int l, int r) {
//        if (l < r) {
        if(l >= r){
            System.out.println(l +":"+r);
            return;
        }
            int i = l, j = r, x = s[l];
            while (i < j) {
                while (i < j && s[j] >= x)
                    j--;
                if (i < j)
                    s[i++] = s[j];
                while (i < j && s[i] < x)
                    i++;
                if (i < j)
                    s[j--] = s[i];
            }
            s[i] = x;
            quickSort(s, l, i - 1);
            quickSort(s, i + 1, r);
//        }
    }

    private static void quickSort2(int[] arr, int low, int high) {

//        if (low < high) {
        if (low >= high) {
            System.out.println(low +":"+high);
            //出口,基线条件
            return;
        }
        // 找寻基准数据的正确索引
        int index = getIndex(arr, low, high);

        // 进行迭代对index之前和之后的数组进行相同的操作使整个数组变成有序
        quickSort2(arr, low, index - 1);
        quickSort2(arr, index + 1, high);
//        }
    }



    private static int getIndex(int[] arr, int low, int high) {
        // 基准数据
        int tmp = arr[low];
        while (low < high) {
            // 当队尾的元素大于等于基准数据时,向前挪动high指针
            while (low < high && arr[high] >= tmp) {
                high--;
            }
            // 如果队尾元素小于tmp了,需要将其赋值给low
            arr[low] = arr[high];
            // 当队首元素小于等于tmp时,向前挪动low指针
            while (low < high && arr[low] <= tmp) {
                low++;
            }
            // 当队首元素大于tmp时,需要将其赋值给high
            arr[high] = arr[low];

        }
        // 跳出循环时low和high相等,此时的low或high就是tmp的正确索引位置
        // 由原理部分可以很清楚的知道low位置的值并不是tmp,所以需要将tmp赋值给arr[low]
        arr[low] = tmp;
        return low; // 返回tmp的正确位置
    }

    public static void main(String[] args) {
//        int []nums = {87, 45, 78, 32, 17, 65, 53, 9, 122};
        int []nums = {6, 1, 2, 7, 9, 3, 4, 5, 10, 8};
        quickSort(nums,0,nums.length-1);
        for (int i:nums){
            System.out.print(i+ " ");
        }
    }
}

参考:
http://blog.csdn.net/morewindows/article/details/6684558
https://blog.csdn.net/nrsc272420199/article/details/82587933 (快速排序—(面试碰到过好几次))
https://baike.baidu.com/item/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95/369842?fr=kg_qa

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
lambda函数可以用于快速排序快速排序是一种常用的排序算法,它通过分治法将一个大的序列分解成两个子序列,然后对子序列进行递归排序,最终将整个序列排序完成。lambda函数可以作为排序的key参数,用于指定按照哪个元素进行排序。通过lambda函数,我们可以很方便地实现快速排序。以下是一个使用lambda函数进行快速排序的例子: ```python def quicksort(arr): if len(arr) <= 1: return arr pivot = arr[len(arr) // 2] less = [x for x in arr if x < pivot] equal = [x for x in arr if x == pivot] greater = [x for x in arr if x > pivot] return quicksort(less) + equal + quicksort(greater) arr = [5, 3, 8, 4, 2] sorted_arr = quicksort(arr) print(sorted_arr) ``` 这段代码使用了lambda函数`x: x < pivot`作为列表推导式中的条件判断,将小于pivot的元素放入less列表,将等于pivot的元素放入equal列表,将大于pivot的元素放入greater列表。然后分别对less和greater列表进行递归排序,最后将排序好的三个列表合并起来。通过这样的操作,我们就实现了快速排序。<span class="em">1</span><span class="em">2</span> #### 引用[.reference_title] - *1* [详解Python的Lambda函数与排序](https://download.csdn.net/download/weixin_38691703/13762752)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [lambda实现对Python二维数组的快速排序](https://blog.csdn.net/zcm545186061/article/details/125356989)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值