快速排序

快速排序:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字比另一部记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。

快速排序的平均时间复杂度为O(nlogn)。

// 快速排序
#include <stdio.h>
#include <malloc.h>

#define N 10 // 数组最大值
#define MAX_LENGTH_INSERT_SORT 7 // 用于快速排序时判断是否选用插入排序阙值

// 顺序表结构
typedef struct {
    int value[N + 1]; // 顺序表中的值
    int length; // 顺序表长度
} SqList;

/**
 * 交换顺序表L中i和j元素的值
 * @param L 顺序表
 * @param i 元素值
 * @param j 元素值
 */
void swap(SqList *L, int i, int j) {
    int temp = L->value[i];
    L->value[i] = L->value[j];
    L->value[j] = temp;
}

/**
 * 初始化顺序表
 * @param L 顺序表
 * @param d 存初始值的数组
 */
void InitSqList(SqList *L, int *d) {
    int i;

    for (i = 0; i < N; ++i) {
        L->value[i + 1] = d[i];
    }
    L->length = N;
}

/**
 * 遍历顺序表中元素的值
 * @param L 顺序表元素
 */
void Print(SqList L) {
    int i;

    printf("顺序表中的值为:");
    for (i = 1; i < L.length; i++) {
        printf("%d, ", L.value[i]);
    }
    printf("%d", L.value[i]);
    printf("\n");
}

/************** 快速排序 **************/

/**
 * 对顺序表进行分割,使在关键字前面的元素都大于它,后面的元素都小于它
 * @param L 顺序表
 * @param low 起点下标
 * @param high 终点下标
 * @return 分割成两部分后,关键字的下标
 */
int Partition(SqList *L, int low, int high) {
    int pivotkey; // 存储关键字

    pivotkey = L->value[low]; // 设置关键字为最小值

    while (low < high) { // 低位下标小于高位下标
        // 低位下标小于高位下标,并且高位元素的值大于等于关键字
        while (low < high && L->value[high] >= pivotkey) {
            high--; // 高位下标向前移动
        }
        swap(L, low, high); // 将比关键字小的元素的值放到前面low的位置

        // 低位下标小于高位下标,并且低位元素的值小于等于关键字
        while (low < high && L->value[low] <= pivotkey) {
            low++; // 低位向后移动
        }
        swap(L, low, high); // 将比关键字大的元素的值放到后面high的位置
    }
    return low; // 返回关键字的下标
}

/**
 * 快速排序
 * @param L 顺序表
 * @param low 起点下标
 * @param high 终点下标
 */
void QSort(SqList *L, int low, int high) {
    int pivot; // 存储分割关键字下标

    // 低位小于高位
    if (low < high) {
        pivot = Partition(L, low, high); // 将L->value[low..high]一分为二,获取关键字下标
        QSort(L, low, pivot - 1); // 对前半部分递归排序
        QSort(L, pivot + 1, high); // 对后半部分递归排序
    }
}

/**
 * 调用快速排序
 * @param L 顺序表
 */
void QuickSort(SqList *L) {
    QSort(L, 1, L->length); // 对整个顺序表进行快速排序
}

/**************************************/

/************** 快速排序(改进版) **************/

/**
 * 对顺序表进行分割,使在关键字前面的元素都大于它,后面的元素都小于它
 * @param L 顺序表
 * @param low 起点下标
 * @param high 终点下标
 * @return 分割成两部分后,关键字的下标
 */
int Partition2(SqList *L, int low, int high) {
    int pivotkey; // 存储关键字

    int mid = low + (high - low) / 2; // 计算中间位置的下标

    // 使用三数取中法获取更适合的关键字(排序最左边、中间、最右边位置的值,取中间的数)
    if (L->value[low] > L->value[high]) {
        swap(L, low, high); // 交换最左边和最右边元素,保证左边较小
    }
    if (L->value[mid] > L->value[high]) {
        swap(L, mid, high); // 交换中间和最右边元素,保证中间较小
    }
    if (L->value[mid] < L->value[low]) {
        swap(L, mid, low); // 交换中间和最左边元素,保证左边较小
    }

    pivotkey = L->value[low]; // 设置关键字为三个数中的最小值
    L->value[0] = pivotkey; // 备份关键字到0下标位置

    while (low < high) { // 低位下标小于高位下标
        // 低位下标小于高位下标,并且高位元素的值大于等于关键字
        while (low < high && L->value[high] >= pivotkey) {
            high--; // 高位下标向前移动
        }
        L->value[low] = L->value[high]; // 将比关键字小的元素的值放到前面low的位置

        // 低位下标小于高位下标,并且低位元素的值小于等于关键字
        while (low < high && L->value[low] <= pivotkey) {
            low++; // 低位向后移动
        }
        L->value[high] = L->value[low]; // 将比关键字大的元素的值放到后面high的位置
    }

    L->value[low] = L->value[0]; // 将关键字存到低下标位置
    return low; // 返回关键字的下标
}

/**
 * 直接插入排序
 * @param L 顺序表
 */
void InsertSort(SqList *L) {
    int i, j;

    for (i = 2; i <= L->length; i++) {
        // 后面的元素比前面元素小,需将L->value[i]插入有序子表
        if (L->value[i] < L->value[i - 1]) {
            L->value[0] = L->value[i]; // 设置哨兵

            // 将有序子表中,比哨兵位置元素大的元素都向后移动一个位置
            for (j = i - 1; L->value[j] > L->value[0]; j--) {
                L->value[j + 1] = L->value[j];
            }
            // 将哨兵位置的元素插入正确的位置
            L->value[j + 1] = L->value[0];
        }
    }
}

/**
 * 快速排序(改进版)
 * @param L 顺序表
 * @param low 起点下标
 * @param high 终点下标
 */
void QSort2(SqList *L, int low, int high) {
    int pivot; //存储关键字

    // 高低位之差大于指定值,调用快速排序
    if ((high - low) > MAX_LENGTH_INSERT_SORT) {
        // 低位下标小于高位下标
        while (low < high) {
            // 将L->value[low..high]一分为二,获取关键字下标
            pivot = Partition2(L, low, high);
            QSort2(L, low, pivot - 1); // 对前半部分进行递归排序
//            QSort2(L, pivot + 1, high); // 对后半部分进行递归排序
            low = pivot + 1; // 尾递归
        }
    } else { // 高低位元素之差过小,调用插入排序
        InsertSort(L); // 插入排序
    }
}

/**
 * 调用快速排序(改进版)
 * @param L 顺序表
 */
void QuickSort2(SqList *L) {
    QSort2(L, 1, L->length); // 对整个顺序表进行快速排序
}

/************************************************/

int main() {
    int d[N] = {50, 10, 90, 30, 70, 40, 80, 60, 20, 102}; // 存初始值的数组
    SqList L; // 顺序表

    InitSqList(&L, d); // 初始化顺序表
    printf("在进行初始化之后");
    Print(L); // 遍历顺序表

    QuickSort(&L); // 对顺序表进行快速排序
    printf("快速排序(普通)后");
    Print(L); // 遍历顺序表

    InitSqList(&L, d); // 初始化顺序表
    QuickSort2(&L); // 对顺序表进行快速排序
    printf("快速排序(改进版)");
    Print(L); // 遍历顺序表

    return 0;
}
运行结果
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容