快速排序

参考:https://www.bilibili.com/video/BV17s41197Yy?spm_id_from=333.999.0.0

 

 

一,partition 函数

1,算法名称

  我不知道这种算法叫什么,但我愿称之为 —— 区间移动

 

2,算法功能

  解释

    ① 从过程看:将某一区间的数,以区间最右边的数为支点,将其余的数根据与支点的大小比较放在支点的左右两边。

      如果是升序序列,则将小于支点的数放在支点左边,将大于支点的数放在支点的右边。

      如果是降序序列,则将大于支点的数放在支点左边,将小于支点的数放在支点的右边。

    ② 从结果上看:找到支点在对该区间元素排序后应该存在的位置。

  注意点:

    ① 支点可以选择区间内的任意一个数。

    ② 支点的左右两边的数允许是乱序的。

 

3,算法思想

  将一个空的移动区间从全局区间进行从左到右的移动,如果遍历到的数属于这个支点右边的数,则将这个数放入区间中,否则移动到区间前面。这样遍历一遍全局区间后,就能完成上述函数功能。

 

4,步骤

  ① 定

    苏菲:支点的右边区间

      用 i 指向苏菲的最后一个元素的位置, j 指向苏菲的第一个元素的位置,所以苏菲是全闭区间,表示为【 j,i 】

      苏菲的右边为苏菲的前面,苏菲的左边为苏菲的后面。

    哈尔:全局区间 

    支点:取哈尔中的最右边的元素

      当苏菲遍历支点的时候,此时支点不满足苏菲的条件,被替换到苏菲后面,正好处于支点左右两个区间的交界处,天衣无缝。

  ② 初始化

    将 i 初始化为 -1 ,j 初始化为 0,此时苏菲内并无元素。

  ③ 循环遍历

    苏菲通过 i++ 向前移动,遍历哈尔的每一个元素,i 的取值在遍历过程从 -1 移动到 哈尔的最后一个元素的位置。

      如果 i 遍历到的元素属于苏菲,则用 i++ 将其包括在苏菲里面。

      如果 i 遍历到的元素不属于苏菲,则 将这个元素和苏菲的第一个元素交换位置,再 j++。

    注意点:

      当 i 遍历到的元素属于苏菲时,所进行的 i++,其实和苏菲通过 i 遍历哈尔的 i++ 是同一个步骤,所以此时可以不做任何处理。

      当 i 遍历到的元素不属于苏菲时,所进行的 交换元素,是不稳定的移动,易得。

      当 i 遍历到的元素不属于苏菲时,之所以要 j++,是因为交换元素后,j 指向的元素不属于苏菲。其中,j++ 和 i++ 从所达到的效果从宏观起来看,就像是苏菲在向前移动时,将遇到的障碍物搬到自己后面,然后再往前走。

  ④ 遍历结果

    左边的区间为【0,j-1】, j 为支点,苏菲为【j+1,n-1】。

 

 

二,quick_sort 函数

  利用递归的搜索功能

    边搜索边使用 partition。

    每次利用 partition 找到某一个元素排序后的位置后,就对剩余位置的元素进行细分搜索。

    在不断缩小搜索范围的过程中,就能找到所有元素排序后的位置。

 

 

三,快排代码

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#define N 100
int a[N];
int partition(int L, int R)
{
    int i = L - 1, j = L;
    while (i + 1 <= R)
    {
        i++;
        if (a[i] <= a[R])
        {
            int t = a[i];
            a[i] = a[j];
            a[j++] = t;
        }
    }
    return j - 1;
}
void quickSort(int L, int R)
{
    if (L >= R)
        return;
    int m = partition(L, R);
    quickSort(L, m - 1);
    quickSort(m + 1, R);
}
int main(void)
{
    int n;
    while (scanf("%d", &n) != EOF)
    {
        for (int i = 0; i < n; i++)
            scanf("%d", &a[i]);
        quickSort(0, n - 1);
        for (int i = 0; i < n; i++)
            printf("%d ", a[i]);
        puts("");
    }
    return 0;
}
View Code

 

 

四,算法分析

① 算法的稳定性

  见一的④步骤,所以快速排序是一种不稳定的算法。

② 算法的时间复杂度

  对于长度为 n 的数组,由于其递归搜索分成左右两边,所以递归树的深度为 log2(n),而每次搜索时使用的 partition 函数的时间复杂度为 O(n),所以快速排序的时间复杂度为 O(n*log2(n))

 

 

五,快排与归并的比较

① 总体思想

  快排:利用递归的搜索分治,在搜索时使用 partition

  归并:利用递归的搜索分治,在回溯时使用 merge

② 功能函数  

  partition:将数组根据大小划分在支点两边。

  merge:将左右两边的数组归并到一起。

 

 

六,选择快排

1,例题

  https://leetcode-cn.com/problems/kth-largest-element-in-an-array/

2,思路

  要求解序列中的第 k 大的数,即只需找到某个元素降序排序后的位置刚好是 k-1 的。所以,我们可以简化快速排序,即在搜索的时候限定条件,只搜索包含 k -1 位置的区间。

3,代码

#include<vector>
using namespace std;
class Solution {
public:
    int partition(int L, int R, vector<int>& a)
    {
        int p = a[R];
        int i = L - 1, j = L;
        while (i + 1 <= R)
        {
            i++;
            if (a[i] >= p)
            {
                int t = a[i];
                a[i] = a[j];
                a[j] = t;
                j++;
            }
        }
        return j - 1;
    }
    void qs(int L, int R, int k, vector<int>& a)
    {
        if (L >= R)
            return;

        int m = partition(L, R, a);
        if (k - 1 == m)  // 快排的有选择的进行排序
            return;
        if (m > k - 1)
            qs(L, m - 1, k, a);
        else
            qs(m + 1, R, k, a);
    }
    int findKthLargest(vector<int>& nums, int k) {
        qs(0, nums.size() - 1, k, nums);
        return nums[k - 1];
    }
};
View Code

 

 

七,非递归的实现

1,概述

  因为快排的递归实现的函数尾部存在两个递归调用,所以无法单纯的用循环实现快排的非递归,需要借用栈来存储两个递归调用

2,代码

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stack>
using namespace std;
#define N 110
int a[N];
struct Node{
    int l, r;
};
int partition(int l, int r)
{
    int i = l - 1, j = l;
    while (i + 1 <= r)
    {
        i++;
        if (a[i] <= a[r])
        {
            int t = a[i];
            a[i] = a[j];
            a[j++] = t;
        }
    }
    return j - 1;
}
void qortSort(int l, int r)
{
    // 初始化
    stack<Node>s;
    s.push(Node{ l, r });

    // 栈中存在元素则循环
    while (s.size())
    {
        Node vertex = s.top();
        s.pop();
        if (vertex.l >= vertex.r) // 判断边界
            continue;

        int m = partition(vertex.l, vertex.r);
        Node n1 = { vertex.l, m - 1 };
        Node n2 = { m + 1, vertex.r };
        s.push(n2);
        s.push(n1);
    }
}
int main(void)
{
    int n;
    while (scanf("%d", &n) != EOF)
    {
        for (int i = 0; i < n; i++)
            scanf("%d", &a[i]);

        qortSort(0, n - 1);
        for (int i = 0; i < n; i++)
            printf("%d ", a[i]);
        puts("");
    }
    return 0;
}
View Code

 

 

========== ========= ======== ======= ====== ===== ==== === == =

  菩萨蛮  韦庄 唐

如今却忆江南乐,当时年少春衫薄。骑马倚斜桥,满楼红袖招。
翠屏金屈曲,醉入花丛宿。此度见花枝,白头誓不归。 

 

posted @ 2020-02-27 20:43  叫我妖道  阅读(259)  评论(0编辑  收藏  举报
~~加载中~~