快速排序非递归版(代码+注释)

头文件

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;		// 栈顶
	int capacity;  // 容量 
}Stack,ST;
// 初始化栈 
void STInit(Stack* ps);
// 入栈 
void STPush(Stack* ps, STDataType data);
// 出栈 
void STPop(Stack* ps);
// 获取栈顶元素 
STDataType STTop(Stack* ps);
// 获取栈中有效元素个数 
int STSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
bool STEmpty(Stack* ps);
// 销毁栈 
void STDestroy(Stack* ps);
//交换
void Swap(int* p1, int* p2);
//打印
void _printf(int* a, int n);
//快排
void QuickSortNonR(int* a, int left, int right);

函数实现

#define _CRT_SECURE_NO_WARNINGS 1
#include "t.h"
// 初始化栈
void STInit(Stack* ps) {
    assert(ps);  // 确保栈指针不为空
    ps->a = NULL;  // 初始化栈底指针为空
    ps->top = 0;  // 栈顶索引初始化为0
    ps->capacity = 0;  // 初始容量为0
}
// 入栈
void STPush(Stack* ps, STDataType data) {
    assert(ps);  // 确保栈指针不为空
    // 如果栈满了,则扩容
    if (ps->top == ps->capacity) {
        int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;  // 初始化容量为4,之后容量翻倍
        STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));  // 重新分配内存
        if (tmp == NULL) {
            perror("realloc fail");  // 内存分配失败,打印错误信息
        }
        ps->a = tmp;  // 更新栈底指针
        ps->capacity = newcapacity;  // 更新栈容量
    }
    ps->a[ps->top] = data;  // 将数据压入栈顶
    ps->top++;  // 栈顶指针加1
}
// 出栈
void STPop(Stack* ps) {
    assert(ps);  // 确保栈指针不为空
    assert(!STEmpty(ps));  // 确保栈不为空
    ps->top--;  // 栈顶指针减1
}
// 获取栈顶元素
STDataType STTop(Stack* ps) {
    assert(ps);  // 确保栈指针不为空
    assert(!STEmpty(ps));  // 确保栈不为空
    return ps->a[ps->top - 1];  // 返回栈顶元素
}
// 获取栈中有效元素个数
int STSize(Stack* ps) {
    assert(ps);  // 确保栈指针不为空
    return ps->top;  // 返回栈顶指针,即栈中有效元素个数
}
// 检测栈是否为空
// 如果为空返回非零结果,如果不为空返回0
bool STEmpty(Stack* ps) {
    assert(ps);  // 确保栈指针不为空
    return ps->top == 0;  // 栈为空时返回1,否则返回0
}
// 销毁栈
void STDestroy(Stack* ps) {
    assert(ps);  // 确保栈指针不为空
    free(ps->a);  // 释放栈底指针的内存
    ps->capacity = ps->top = 0;  // 重置栈容量和栈顶指针
}
// 交换两个整数
void Swap(int* p1, int* p2) {
    int tmp = *p1;
    *p1 = *p2;
    *p2 = tmp;
}
// 打印数组
void _printf(int* a, int n) {
    for (int i = 0; i < n; i++) {
        printf("%d ", a[i]);  // 打印数组元素
    }
}
// 非递归快速排序
void QuickSortNonR(int* a, int left, int right) {
    ST st;
    STInit(&st);  // 初始化栈
    STPush(&st, right);  // 将右边界入栈
    STPush(&st, left);  // 将左边界入栈
    // 循环栈中的区间进行排序
    while (!STEmpty(&st)) {
        int begin = STTop(&st);  // 获取当前区间的左边界
        STPop(&st);  // 弹出左边界
        int end = STTop(&st);  // 获取当前区间的右边界
        STPop(&st);  // 弹出右边界
        int keyi = begin;  // 选择基准元素的初始位置
        int cur = begin + 1;  // 当前元素指针
        int prev = begin;  // 上一个小于基准元素的位置
        // 对区间内的元素进行排序
        while (cur <= end) {
            if (a[cur] < a[keyi] && ++prev != cur)  // 如果当前元素小于基准元素,则交换
                Swap(&a[prev], &a[cur]);
            ++cur;
        }
        // 将基准元素放到正确的位置
        Swap(&a[keyi], &a[prev]);
        keyi = prev;  // 更新基准元素位置
        // 如果基准元素右边还有元素,继续排序右边区间
        if (keyi + 1 < end) {
            STPush(&st, end);
            STPush(&st, keyi + 1);
        }
        // 如果基准元素左边还有元素,继续排序左边区间
        if (keyi - 1 > begin) {
            STPush(&st, keyi - 1);
            STPush(&st, begin);
        }
    }
    STDestroy(&st);  // 销毁栈
}

测试文件

#define _CRT_SECURE_NO_WARNINGS 1
#include "t.h"
int main() {
	int a[] = { 7,5,6,4,8,9,2,1,3,0 };
	_printf(a, sizeof(a) / sizeof(a[0]));
	printf("\n");
	QuickSortNonR(a, 0, sizeof(a) / sizeof(a[0]) - 1);
	_printf(a, sizeof(a) / sizeof(a[0]));
}

测试结果

详细可见排序学习整理(2)-优快云博客

<think>好的,我现在需要帮助用户查找快速排序的C语言实现代码。用户之前已经询问过堆排序的C代码,现在转向快速排序。首先,我要回顾之前的对话历史和相关引用,确保提供的代码符合用户的需求。 用户提供的引用中有多个快速排序的实现,包括递归本、挖坑法和单链表的实现。不过用户现在明确要求的是快速排序的C语言代码,所以应该优先选择标准的递归实现,并确保代码正确、清晰。 首先,我需要确认用户需要的是哪种快速排序的实现。引用中的第一个示例(引用[1])是一个基本的快速排序实现,使用递归和基准值分割数组。第二个引用(引用[2])是挖坑法,第三个(引用[3])是另一种分治实现,而第四个(引用[4])提到单链表,但代码不完整。用户可能想要一个标准且易于理解的本,所以选择引用1或3中的代码可能更合适。 接下来,我要检查这些代码的正确性。例如,引用1中的Quick_Sort函数是否正确处理了基准值前后的子数组?代码中使用了i和j两个指针,从左右两端向中间扫描,将小于基准的放到左边,大于的放到右边,最后将基准放到中间位置,然后递归处理左右子数组。这符合快速排序的基本思路,应该没问题。 另外,用户可能希望代码注释和测试用例,方便理解。引用1中的代码包括main函数进行测试,输出结果,这对用户来说是有帮助的。因此,我应该整合这些信息,提供一个结构清晰、注释明确的代码示例。 还需要注意用户之前提到的格式要求:行内数学公式用$...$,独立公式用$$...$$。虽然在代码示例中可能不需要太多公式,但解释时间复杂度时应该使用正确格式,例如总时间复杂度为$O(n \log n)$。 此外,用户要求生成相关问题,我需要根据快速排序的实现和特性提出相关问题,例如时间复杂度分析、优化方法、与其他算法的比较等。 最后,确保回答结构清晰,分步骤解释代码,并引用正确的来源。需要参考用户提供的引用,但根据问题历史,用户可能更关注标准实现,因此主要参考引用1和3的代码结构,同时确保代码语法正确,比如指针操作和递归调用是否正确处理。 总结步骤: 1. 选择一个标准的快速排序递归实现,如引用1中的代码。 2. 确保代码结构清晰,包含注释和测试用例。 3. 解释关键步骤,如基准选择、分区过程、递归调用。 4. 说明时间复杂度和空间复杂度,使用正确的LaTeX格式。 5. 生成相关问题,帮助用户进一步理解快速排序。</think>### 快速排序C语言实现(递归本) #### 核心原理 通过选定基准元素(pivot)将数组分为两个子序列: - 左子序列:所有元素 ≤ pivot - 右子序列:所有元素 ≥ pivot 递归处理子序列直至有序,时间复杂度$O(n \log n)$,空间复杂度$O(\log n)$(递归栈)[^1] #### 完整代码实现 ```c #include <stdio.h> // 快速排序核心函数 void QuickSort(int arr[], int left, int right) { if (left >= right) return; // 递归终止条件 int pivot = arr[left]; // 选择第一个元素为基准 int i = left, j = right; while (i < j) { // 从右向左找第一个小于基准的值 while (i < j && arr[j] >= pivot) j--; if (i < j) arr[i++] = arr[j]; // 从左向右找第一个大于基准的值 while (i < j && arr[i] <= pivot) i++; if (i < j) arr[j--] = arr[i]; } arr[i] = pivot; // 基准归位 // 递归处理子序列 QuickSort(arr, left, i - 1); QuickSort(arr, i + 1, right); } // 测试代码 int main() { int arr[] = {9, 5, 1, 6, 2, 3, 0, 4, 8, 7}; int n = sizeof(arr)/sizeof(arr[0]); printf("原始数组:"); for (int i = 0; i < n; i++) printf("%d ", arr[i]); QuickSort(arr, 0, n-1); printf("\n排序结果:"); for (int i = 0; i < n; i++) printf("%d ", arr[i]); return 0; } ``` #### 关键步骤说明 1. **基准选择**:默认取第一个元素$arr[left]$作为基准(可优化为三数取中) 2. **双指针扫描**: - `j`指针从右向左扫描,找到第一个**小于基准**的值填入左坑位 - `i`指针从左向右扫描,找到第一个**大于基准**的值填入右坑位 3. **递归分治**:每次划分后对左右子数组进行递归排序[^1] #### 性能特点 | 特性 | 说明 | |--------------|-------------------------| | 时间复杂度 | 平均$O(n \log n)$,最差$O(n^2)$ | | 空间复杂度 | $O(\log n)$(递归栈深度) | | 稳定性 | 不稳定(相同元素可能交换顺序) | | 适用场景 | 大规模随机数据排序 | --相关问题-- 1. 如何避免快速排序的最差时间复杂度? 2. 三数取中法优化基准选择的具体实现?[^1] 3. 快速排序与堆排序在内存访问模式上的差异? 4. 非递归实现快速排序需要哪些数据结构?[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值