快速排序·三平均划分法

题目信息

要求根据给定输入,按照课堂给定的快速排序算法进行排序,输出排序结果和median3的返回值。

1 cutoff值为5,不足cutoff使用插入排序。
2 输入、输出格式参见测试用例。

测试样例

测试样例1

41
17
34
0
19
#
After Sorting:
0 17 19 34 41 
Median3 Value:
none

测试样例2

61
59
82
-10
31
-2
-3
10
2
108
12
80
-21
127
12
#
After Sorting:
-21 -10 -3 -2 2 10 12 12 31 59 61 80 82 108 127 
Median3 Value:
12 -3 61 

解答

#include<iostream>
#include<algorithm>

using namespace std;

int sz[100], median[100];
int mNum = 0;

int Median3(int lt, int rt)
{//三平均划分法
    int Middle = (lt + rt) / 2;
    if (sz[lt] > sz[Middle])
    {//满足中间的要比左边的大
        swap(sz[lt], sz[Middle]);
    }
    if (sz[lt] > sz[rt])
    {//满足右边的要比左边的大
        swap(sz[lt], sz[rt]);
    }
    if (sz[Middle] > sz[rt])
    {//满足右边的比中间的大
        swap(sz[Middle], sz[rt]);
    }
    swap(sz[Middle], sz[rt - 1]);//将中间的放置在倒数第二个的位置
    median[mNum++] = sz[rt - 1];//将中间值存在median数组中
    return sz[rt - 1];
}//实际上是我们抓取了三个数,第一个中间的最后一个按照大小排序后,取出中间的那个

void InsertSort(int lt, int rt)
{
    for (int i = lt + 1; i <= rt; i++)
    {
        int temp = sz[i];
        if (sz[i] < sz[i - 1])
        {
            int j = lt;
            while (sz[j] < temp)
            {
                j++;
            }
            for (int k = i; k > j; k--)
            {//后移
                sz[k] = sz[k - 1];
            }
            sz[j] = temp;
        }
    }
}

void QuickSort(int lt, int rt)
{
    if (rt - lt < 5)
    {//个数小于5的时候使用插入排序的方法
        InsertSort(lt, rt);
    }
    else if (lt < rt)
    {
        int pivot = Median3(lt, rt);//得到了中间的那个数
        int L = lt, R = rt - 1; //从左到右的扫描从第一个元素开始,从右到左的扫描从倒数第二个元素开始
        while (L < R)
        {//找到左边的比中数大,右边的比中数小,然后交换之
            while (L < R && sz[++L] < pivot);
            while (L < R && sz[--R] > pivot);
            swap(sz[L], sz[R]);
        }
        swap(sz[L], sz[rt - 1]);//将中数放置在中间的位置

        int Middle = L; // 中轴
        if (Middle)
        {//递归下去
            QuickSort(lt, Middle - 1);
            QuickSort(Middle + 1, rt);
        }
    }
}

int main()
{
    //freopen("/Users/zhj/Downloads/test.txt", "r", stdin);
    string s;
    int len = 0;
    while (true)
    {
        cin >> s;
        if (s[0] == '#')
        {
            break;
        }
        sz[len] = atoi(s.c_str());
        ++len;
    }
    QuickSort(0, len - 1);

    cout << "After Sorting:" << endl;
    for (int i = 0; i < len; ++i)
    {
        cout << sz[i] << " ";
    }
    cout << endl;

    cout << "Median3 Value:" << endl;
    if (len > 5)
    {
        for (int i = 0; i < mNum; ++i)
        {
            cout << median[i] << " ";
        }
    }
    else
    {
        cout << "none";
    }
    cout << endl;
}

想法

  1. 本题考察的就是快速排序的三平均划分法,实现一种分治的思想。
  2. 首先选出三个值,第一个数,中间的数,最后一个数,将这三个数按大小排好序,A < B < C
  3. 对剩余的数字,将小于B的数放置在左边,大于B的数放置在右边。
  4. 对左右两堆数实现递归,不断循环上述过程。
  5. 如果剩余的数小于等于5个的时候就是用插入排序的方式。
<think>嗯,用户想了解快速排序中的路划分,我得先回忆一下相关的知识点。首先,快速排序本身是基于分治法的,传统的实现是选取一个基准元素,然后把数组分成两部分,一部分比基准小,另一部分比基准大。不过当数组中有很多重复元素时,这种两路划分的效率可能会下降,因为重复元素会被反复处理。 那路划分应该就是解决这个问题的。对了,路划分会把数组分成个部分:小于基准、等于基准大于基准的部分。这样的话,在递归处理时,只需要处理小于大于的部分,等于基准的部分就不用再处理了,尤其是在有大量重复元素时,可以显著提高效率。 需要解释清楚路划分的基本思想,可能还要对比传统的双路划分。然后,步骤分解应该包括如何选择基准,如何分区,以及递归处理。可能还需要提到具体实现时的指针移动,比如使用lt、gti这样的指针,或者类似的变量来控制分区过程。 还有,最好举一个例子,比如一个包含多个重复元素的数组,展示路划分后的结果。这样用户能更直观地理解。另外,时间复杂度的分析也很重要,最好说明在重复元素多的情况下,路划分如何将复杂度从O(n log n)优化到接近O(n),或者避免最坏情况的发生。 可能还需要提到路划分的应用场景,比如处理包含大量重复元素的数据集时,或者作为快速排序的优化手段之一。同时,注意语言要通俗易懂,避免过于专业的术语,但又要准确。最后,总结一下它的优缺点,或者双路划分的对比,帮助用户全面理解。</think>### 快速排序中的路划分 #### 1. **基本思想** 快速排序的**路划分**(Three-way Partitioning)是一种优化策略,用于处理数组中存在**大量重复元素**的情况。它将数组划分为个区域: - **小于基准值**的部分(左段) - **等于基准值**的部分(中段) - **大于基准值**的部分(右段) 通过将重复元素集中到中间段,后续递归时**只需处理左右两段**,避免对重复元素重复操作,从而提升效率。 --- #### 2. **具体步骤** 假设数组为 $$arr$$,基准值为 $$pivot$$,路划分流程如下: 1. **初始化指针**: - $$lt$$(左指针):指向第一个元素,表示左段的末尾 - $$gt$$(右指针):指向最后一个元素,表示右段的开头 - $$i$$(遍历指针):从数组头部开始扫描 2. **遍历与交换**: - 若 $$arr[i] < pivot$$:交换 $$arr[i]$$ 与 $$arr[lt]$$,并移动 $$i$$ $$lt$$ - 若 $$arr[i] > pivot$$:交换 $$arr[i]$$ 与 $$arr[gt]$$,仅移动 $$gt$$ - 若 $$arr[i] = pivot$$:仅移动 $$i$$ 3. **终止条件**: - 当 $$i$$ 与 $$gt$$ 相遇时停止遍历 4. **递归处理**: - 对左段 $$arr[0..lt-1]$$ 右段 $$arr[gt+1..n-1]$$ 递归执行上述步骤 --- #### 3. **示例演示** 假设数组为 $$[3, 1, 2, 3, 5, 3, 4]$$,基准值 $$pivot = 3$$: - **划分后**: - 左段:$$[1, 2]$$ - 中段:$$[3, 3, 3]$$ - 右段:$$[5, 4]$$ - 后续递归仅需处理左段右段。 --- #### 4. **时间复杂度分析** - **最好情况**(大量重复元素):$$O(n)$$ - **平均情况**:$$O(n \log n)$$ - **最坏情况**:$$O(n^2)$$(但概率极低) 与传统双路划分相比,路划分在重复元素较多时**显著减少递归次数**。 --- #### 5. **代码框架(伪代码)** ```plaintext function quickSort3Way(arr, low, high): if low >= high: return pivot = arr[low] lt = low gt = high i = low while i <= gt: if arr[i] < pivot: swap(arr[i], arr[lt]) lt += 1 i += 1 elif arr[i] > pivot: swap(arr[i], arr[gt]) gt -= 1 else: i += 1 quickSort3Way(arr, low, lt - 1) quickSort3Way(arr, gt + 1, high) ``` --- #### 6. **适用场景** - 输入数据包含**大量重复元素** - 需要避免快速排序的最坏时间复杂度 - 例如:排序日志数据、用户评分等重复值较多的场景 --- #### 7. **总结** 路划分通过将重复元素集中处理,优化了传统快速排序的性能瓶颈。其核心是通过**动态调整个指针**($$lt$$、$$gt$$、$$i$$)将数组高效划分为段,特别适合处理实际场景中的非均匀分布数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhj12399

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值