快速排序改进(来自:算法:C语言实现)

本文介绍了一种利用三者取中法改进的快速排序算法,通过选取数组首、中、末三个元素的中值作为划分基准,并结合插入排序优化小规模数组的排序过程,提高了整体排序效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

利用三者取中法改进快速排序

实现取数组中第一个,中间和最后一个元素的中间元素作为划分元素(否则将这些元素排除在划分过程之外).大小为11或更小的数组在划分过程中被忽略,然后使用插入排序来完成排序.

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <stack>
#include <queue>
#include <malloc.h>
using namespace std;
#define OK 1
#define ERROR -1
#define TRUE 1
#define FALSE 0
typedef int Status;
//输出函数
void Print(int a[], int l, int r)
{
    int i;
    for(i = l; i <= r; i++)
    {
        printf("%d ", a[i]);
    }
    printf("\n");
}
//插入排序的改进
void Insertion(int a[], int l, int r)
{
    int i, j;
    //循环找到数组中的最小值
    for(i = r; i > l; i--)
    {
        if(a[i-1] > a[i])
        {
            swap(a[i-1], a[i]);
        }
    }
    //由于上面的循环,a[0]a[1]已经有序
    for(i = l+2; i <= r; i++)
    {
        int temp = a[i];
        j = i;
        //此时a[j]的位置已被记录
        //while循环比较进行移位操作
        while(temp < a[j-1])
        {
            a[j] = a[j-1];
            j--;
        }
        //将记录下的值放到应当的位置
        a[j] = temp;
    }
}
//划分函数
int partion(int a[], int left, int right)
{
    //取最右边的元素作划分元素
    int temp = a[right];
    //记录 i = left, j = right
    int i = left, j = right-1;
    //循环直到左右指针相遇
    while(true)
    {
        //从左边开始扫描,当出现比划分元素大的元素,扫描停止
        while(temp > a[i])
        {
            i++;
        }
        //从右边进行扫描,当出现比划分元素小的元素,扫描停止
        while(temp < a[j]  && j >= left)
        {
            j--;
        }
        //如果 i >= j, 循环截止,下面的交换不执行
        if(i >= j) break;
        //交换停止时的元素
        swap(a[i], a[j]);
    }
    //交换该元素与划分元素
    swap(a[i], a[right]);
    //printf("i = %d\n", i);
    //Print(a, 0, 6);
    //划分过程结束
    return i;
}

void qsort(int a[], int left, int right)
{
    int i;
    if(right-left <= 10)
        return;
    swap(a[(left+right)/2], a[right-1]);
    if(a[left] > a[right-1])
        swap(a[left], a[right-1]);
    if(a[left] > a[right])
        swap(a[left], a[right]);
    if(a[right] > a[right-1])
        swap(a[right-1], a[right]);
    i = partion(a, left+1, right-1);
    qsort(a, left, i-1);
    qsort(a, i+1, right);
}
void Sort(int a[], int left, int right)
{
    qsort(a, left, right);
    Insertion(a, left, right);
}

int main()
{
    int a[12] = {2, 5, 3, 7, 6, 1, 4, 11, 8, 10, 9, 12};
    //快速排序改进
    printf("对0~11排序\n");
    Sort(a, 0, 11);
    Print(a, 0, 11);
    return 0;
}

 

<think>我们面对的问题是如何在C#中取消或中止一个正在进行的Task.Delay操作。根据引用资料,我们可以使用CancellationToken机制来实现这一目标。 核心思路: 1. 使用CancellationTokenSource创建取消令牌。 2. 将取消令牌传递给Task.Delay方法。 3. 在需要取消延迟时,调用CancellationTokenSource的Cancel方法。 具体步骤: 根据引用[1]和[4],我们知道Task.Delay有一个重载方法,可以接受一个CancellationToken参数。当我们在等待期间取消令牌时,Task.Delay会抛出TaskCanceledException(或OperationCanceledException)异常,我们可以捕获这个异常来处理取消操作。 示例代码: 引用[3]提供了一个使用CancellationToken取消任务的例子,我们可以类似地应用到Task.Delay上。 下面是一个具体的示例: ```csharp using System; using System.Threading; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { // 创建CancellationTokenSource,用于产生取消令牌 var cts = new CancellationTokenSource(); try { Console.WriteLine("开始等待5秒..."); // 启动一个延迟5秒的任务,并传入取消令牌 Task delayTask = Task.Delay(5000, cts.Token); // 假设我们在2秒后取消延迟 Console.WriteLine("2秒后取消延迟..."); // 这里我们启动一个定时器,2秒后执行取消操作 cts.CancelAfter(2000); // 等待延迟任务完成(或者被取消) await delayTask; Console.WriteLine("延迟正常完成。"); } catch (TaskCanceledException) { Console.WriteLine("延迟被取消!"); } finally { cts.Dispose(); } } } ``` 解释: - 我们创建了一个CancellationTokenSource,用于生成取消令牌。 - 调用Task.Delay(5000, cts.Token)创建一个延迟5秒的任务,并传递取消令牌。 - 使用cts.CancelAfter(2000)在2秒后发出取消信号。 - 当取消信号发出时,Task.Delay会抛出TaskCanceledException,我们在catch块中捕获并处理。 另一种写法(不抛出异常): 如果我们不想通过捕获异常来处理取消,可以检查任务的状态。但是,Task.Delay被取消时,任务会变为取消状态(IsCanceled为true),如果我们使用await等待,它仍然会抛出异常。如果我们不使用await,而是用其他方式等待,可以通过检查任务状态来判断。 但是,通常我们使用await,所以捕获异常是标准做法。 注意事项: 1. 如果取消令牌在Task.Delay开始之前就已经被取消了,那么Task.Delay会立即进入取消状态。 2. 如果取消令牌在延迟期间被取消,Task.Delay会抛出异常。 3. 在finally块中释放CancellationTokenSource(或者使用using语句)是一个好习惯。 使用using语句的改进版本: ```csharp using System; using System.Threading; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { using (var cts = new CancellationTokenSource()) { try { Console.WriteLine("开始等待5秒..."); // 在2秒后自动取消 cts.CancelAfter(2000); await Task.Delay(5000, cts.Token); Console.WriteLine("延迟正常完成。"); } catch (TaskCanceledException) { Console.WriteLine("延迟被取消!"); } } } } ``` 在这个版本中,我们使用using语句确保CancellationTokenSource被正确释放。同时,我们调用了cts.CancelAfter(2000)来在2秒后取消延迟。 总结: 通过使用CancellationToken,我们可以有效地取消Task.Delay操作。当取消发生时,我们需要处理TaskCanceledException异常,以执行取消后的逻辑。 §§相关问题§§ 1.C#中,除了Task.Delay,还有哪些异步操作可以使用CancellationToken取消? 2. 如何组合多个CancellationTokenSource? 3. 在取消Task.Delay后,如何清理资源? 4. 在ASP.NET Core中取消Task.Delay有什么注意事项? 5. 如何避免在取消Task.Delay时出现异常?</think>### 如何取消或中止 C# 中的 Task.Delay 操作 在 C# 中,取消 `Task.Delay` 的核心方法是使用 **`CancellationToken`** 机制。以下是详细实现步骤: #### 1. 基础实现方案 ```csharp using System.Threading; using System.Threading.Tasks; async Task CancelableDelayAsync() { // 创建取消令牌源 var cts = new CancellationTokenSource(); // 启动延迟任务(传入取消令牌) Task delayTask = Task.Delay(5000, cts.Token); // 计划5秒延迟 // 模拟其他操作(例如UI按钮点击) await Task.Delay(2000); cts.Cancel(); // 2秒后取消延迟 try { await delayTask; // 等待延迟完成(可能被取消) Console.WriteLine("延迟正常完成"); } catch (TaskCanceledException) { Console.WriteLine("延迟已取消!"); // 捕获取消异常 } finally { cts.Dispose(); // 清理资源 } } ``` **关键点说明**: - 📌 当 `cts.Cancel()` 被调用时,`Task.Delay` 会立即抛出 `TaskCanceledException` - ✅ 使用 `try-catch` 处理取消操作 - ⚡ 延迟任务会在取消后立即结束(无需等待原定时长) #### 2. 使用 `CancelAfter` 自动取消 ```csharp async Task AutoCancelDelayAsync() { using var cts = new CancellationTokenSource(); // 设置3秒后自动取消 cts.CancelAfter(3000); try { await Task.Delay(5000, cts.Token); // 计划5秒延迟 Console.WriteLine("延迟完成"); } catch (OperationCanceledException) { Console.WriteLine("超时自动取消!"); } } ``` #### 3. 组合多个取消令牌 ```csharp async Task CombinedCancellationAsync(CancellationToken externalToken) { using var internalCts = CancellationTokenSource.CreateLinkedTokenSource(externalToken); try { // 组合外部令牌和内部超时(3秒) internalCts.CancelAfter(3000); await Task.Delay(5000, internalCts.Token); } catch (TaskCanceledException ex) { Console.WriteLine($"取消原因: {ex.CancellationToken == externalToken ? "外部请求" : "内部超时"}"); } } ``` #### ⚠️ 重要注意事项 1. **资源释放** 始终使用 `using` 或手动调用 `Dispose()` 释放 `CancellationTokenSource`[^4] 2. **异常类型** - 手动取消抛出 `TaskCanceledException` - 超时自动取消抛出 `OperationCanceledException` 3. **UI线程安全** 在 WPF/WinForms 中,通过 `Dispatcher.Invoke` 更新 UI: ```csharp catch (TaskCanceledException) { Dispatcher.Invoke(() => statusLabel.Content = "已取消"); } ``` 4. **ASP.NET Core 特殊处理** 使用 `HttpContext.RequestAborted` 获取请求级取消令牌: ```csharp public async Task GetData(HttpContext context) { await Task.Delay(5000, context.RequestAborted); } ``` --- ### 常见问题解答 **Q:为什么不用 `Thread.Sleep`?** A:`Thread.Sleep` 会阻塞线程且无法取消,而 `Task.Delay` 是异步操作,支持取消且不阻塞线程[^2]。 **Q:取消后如何执行清理操作?** A:在 `finally` 块中处理: ```csharp finally { resource.Cleanup(); // 释放文件句柄/网络连接等 } ``` **Q:如何检测任务是否已被取消?** A:检查令牌状态: ```csharp if (cts.Token.IsCancellationRequested) { // 执行取消后逻辑 } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值