C语言排序之快速排序

       快速排序是一种高效的排序算法。它采用了分治的策略,通过选择一个基准元素,将待排序的序列划分为两部分,一部分的元素都比基准元素小,另一部分的元素都比基准元素大,然后对这两部分分别进行快速排序,从而实现整个序列的有序排列。

以下是快速排序的基本步骤:

  1. 选择一个基准元素:通常可以选择序列的第一个元素、最后一个元素或者中间元素作为基准。
  2. 分区操作:通过一系列的比较和交换,将序列中小于基准的元素移到基准的左边,大于基准的元素移到基准的右边。
  3. 对分区后的左右两部分子序列,分别重复步骤 1 和 2,直到整个序列有序。

例如,对于序列 [12, 11, 13, 5, 6] ,选择第一个元素 12 作为基准:

  • 经过分区操作后,序列变为 [5, 11, 6, 12, 13] 。
  • 然后对 [5, 11, 6] 和 [12, 13] 分别继续进行快速排序。

快速排序的平均时间复杂度为 (O(nlogn)) ,空间复杂度为(Ologn)  。在大多数情况下,它的性能非常出色,但在最坏情况下,时间复杂度会退化为(On2)  。不过,通过合理选择基准元素,可以很大程度上避免最坏情况的发生。

快速排序的单趟排序

       通过一趟排序将数据分割成独立的两部分,其中一部分的所有数据都比另一组数据的数值小,然后再次使用相同的方法分别将这两部分再分成两部分,从而完成快速排序。

1.霍尔法

“霍尔法”通常指的是快速排序(Quick Sort)中的一种实现方式,也被称为霍尔划分。

快速排序是一种分治算法,其基本思想是通过一趟排序将待排序的序列划分成两部分,然后对这两部分分别进行排序,以实现整个序列的有序。

霍尔法的具体实现步骤如下:

  1. 选取一个基准元素 key,通常选择数组的第一个元素、中间元素或最后一个元素,也可以选择这三个元素的中间值等,然后将这个基准元素与数组第一个元素进行交换。
  2. 定义两个指针 left 和 right,left 从数组的第一个元素开始(即左边)向右遍历,right 从数组的最后一个元素开始(即右边)向左遍历。
  3. 首先,right 指针从右向左移动,找到比 key 小的值时停下来。然后,left 指针从左向右移动,找到比 key 大的值时停下来。
  4. 交换 left 和 right 所指向的值,这一步的目的是将比 key 小的值往左放,将比 key 大的值往右放。
  5. 重复步骤 3 和 4,直到 left 和 right 相遇。
  6. 当 left 和 right 相遇后,将第一个元素(即 key)与它们相遇位置的值交换。此时,key 左边的元素都比它小,右边的元素都比它大,但左右两边的子序列并不一定是有序的。
  7. 对 key 左边的子序列和右边的子序列,分别重复上述步骤,进行递归排序,直到子序列不存在或者只有一个元素时结束递归。

key——>基准下标值;

指针 left指向最左端的元素也就是第一个元素(找比key大的元素)

        right指向最右端的元素也就是最后一个元素(找比key小的元素)

<1>

      left记录左下标,从左向右遍历

      right记录右下标,从右向左遍历,以第一个数作为基准值

    5        2      7       3      1      4      8    6

^left                                                                                                            ^right                         

 key=5                                                                       

<2>

      right先出发,找比key小的值,找到停下

    5        2      7       3      1      4      8    6

^left                                                                                ^right

key=5                                                                       

<3>

      left出发,找比key大的值,找到停下并与right进行交换

    5        2      7       3      1      4      8    6

                                       ^left                                         ^right                                                                    key=5                                                                       

    5        2      4       3      1      7      8    6

                                       ^left                                         ^right                                                                    key=5                                                                       

<4>

     重复步骤<2>,<3>,直到left与right指向同一个数

    5        2      4       3      1      7      8    6

                                                                      ^right   

                                                                       ^left                                                                                                                          key=5     

<5>

    将left/right与key交换,完成单趟排序

    1        2      4       3      5      7      8    6

                                                                      ^right   

                                                                       ^left                                                                                                                          key=5     

总的来说就是把比key小的放在key左边,比key大的放在key右边,以下是代码实现单趟排序

#include <stdio.h>

// 交换两个整数的值
void swap(int* a, int* b) {
    int tmp = 0;
    tmp = *a;
    *a = *b;
    *b = tmp;
}

// 获取数组中 begin、mid、end 三个位置元素的中间值,并与数组第一个元素交换
int getMid(int* a, int begin, int end) {
    int mid = (begin + end) / 2;
    if (a[begin] > a[mid]) {
        if (a[mid] > a[end]) {
            return mid;
        } else if (a[end] > a[begin]) {
            return begin;
        } else {
            return end;
        }
    } else {
        if (a[begin] > a[end]) {
            return begin;
        } else if (a[end] > a[mid]) {
            return mid;
        } else {
            return end;
        }
    }
}

// 霍尔法快速排序的核心函数
void quickSortHoare(int* a, int begin, int end) {
    int left = begin;
    int right = end;
    if (left >= right) {
        return;
    }
    int mid = getMid(a, begin, end);
    swap(&a[begin], &a[mid]); 
    int keyi = begin; 
    while (left < right) {
        while (left < right && a[right] >= a[keyi]) {
            right--;
        }
        while (left < right && a[left] < a[keyi]) {
            left++;
        }
        swap(&a[left], &a[right]);
    }
    swap(&a[left], &a[keyi]); 
    keyi = left; 
    quickSortHoare(a, begin, keyi - 1); 
    quickSortHoare(a, keyi + 1, end); 
}

// 打印数组函数
void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

int main() {
    int arr[] = {12, 11, 13, 5, 6};
    int n = sizeof(arr) / sizeof(arr[0]);

    printf("排序前的数组为:");
    printArray(arr, n);

    quickSortHoare(arr, 0, n - 1); 

    printf("排序后的数组为:");
    printArray(arr, n);

    return 0;
}

2.挖坑法

        与霍尔法不同的是,将key基准值用变量保存起来,然后将key空出来,也就是形成一个坑,left先指向这个坑right找比key小的值放进坑里,并将自己的位置设为坑,left找比key大的放进坑里,自己又变成坑,如此循环。

<1>

     先定义变量key,存储数组第一个值作为key,此时left指向坑

5 2 7 3 1 4 8 6

  ^key       ^left                                                                                            ^right

<2>

     right开始找比key小的数,放进坑里,自己变为坑

5 2 7 3 1 4 8 6

  ^key       ^left                                                                 ^right

5 4 2 7 3 1 8 6

  ^key       ^left                                                                 ^right

<3>

     left开始找比key大的数,同上

5 4 2 7 3 1 8 6

  ^key                                   ^left                                      ^right

5 4 2 3 1 7 8 6

  ^key                                   ^left                                   ^right

<4>

     重复步骤<2>,<3>,直到left与right相遇

5 4 2 1 3      7 8 6

  ^key                                                              ^left                 

                                                                       ^right

<5>

     将key放在相遇的坑里,排序完毕,将key下标返回

4 2 1 3     5 7 8 6

                                                                       ^left   

                                                                       ^key 

                                                                       ^right

以下是代码实现:

int PartSort(int *arr,int left,int right){
int key=arr[left];
int hole=left;
while(left<right){
while(arr[right]>=key&&left<right){
right--;
}
arr[hole]=arr[right];
hole=right;
while(left<right&&arr[left]<=key){
left++;
}
arr[hole]=arr[left];
hole=left;
}
arr[hole]=key;
return hole;
}

 3.前后指针

       将数组的第一个元素作为基准值key,定义前指针prev指向数组第一个元素,后指针cur指向数组第二个数,由cur遍历数组,找出比key小的数,prev在cur找到后向后移动一位并与cur交换,保证较小数在prev之前。

<1>

开始时prev指向数组第一个元素,cur指向第二个元素,此时cur的值比key小,prev向后移动一位后与cur交换,无变化

    5        2      7       1      3      4      8    6

^prev          ^cur                     

 key=5        ^prev+1                                 

<2>

cur找到比key大的数此时cur继续向后移动,prev不变

    5        2      7       1      3      4      8    6

                                      ^cur                     

 key=5        ^prev                                 

<3>

cur找到比key小的数此时prev向后移动一位,并与cur交换

    5        2      7       1      3      4      8    6

                                                      ^cur                     

 key=5        ^prev                   

 

    5        2      7       1      3      4      8    6

                                                      ^cur                     

 key=5                          ^prev                   

 
    5        2      1       7      3      4      8    6

                                                      ^ cur                   

 key=5                          ^prev                   

<4>

     重复步骤<2>,<3>,直到cur完成遍历

    5        2      1       7      3      4      8    6

                                                                       ^cur                     

 key=5                                           ^prev                   

                                        交换3和7

    5        2      1       3      7      4      8    6

                                                                                       ^cur                     

 key=5                                                          ^prev                   

                                       交换4和7

    5        2      1       3      4      7      8   6

                   

 key=5                                                           ^prev                   

 

<5>

     cur完成遍历后,将prev与key进行交换,返回key的下标

    4        2      1       3      5      7      8   6

                                                                         key=5                                                                                                                      ^prev                   

 以下是代码实现:

int PastSort(int *arr,int  left,int right){
int key=left;
int prev=left;
int cur=left+1;
while(cur<=right){
if(arr[cur]<=arr[key]&&++prev!=cur){
swap(&arr[cur],&arr[prev];
}
cur++;
}
swap(&arr[key],&arr[prev]);
return prev;
}

 其实快速排序最核心的代码就是单趟排序,以上三种是单趟排序的三种方法,大家任选一种牢记就好,主要函数的代码如下(递归实现)

void QuickSort(int *arr,int begin,int end){
if(begin>=end){
return;
}
int key=PartSort(arr,begin,end);
QuickSort(arr,begin,key-1);
QuickSort(arr,key+1,end);
}

相关推荐

  1. C语言排序快速排序

    2024-07-13 06:06:03       27 阅读
  2. 快速排序--C语言

    2024-07-13 06:06:03       39 阅读
  3. 排序快速排序

    2024-07-13 06:06:03       52 阅读
  4. C#算法快速排序

    2024-07-13 06:06:03       29 阅读
  5. C语言经典算法快速排序算法

    2024-07-13 06:06:03       48 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-13 06:06:03       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-13 06:06:03       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-13 06:06:03       58 阅读
  4. Python语言-面向对象

    2024-07-13 06:06:03       69 阅读

热门阅读

  1. 基于Go1.19的站点模板爬虫详细介绍

    2024-07-13 06:06:03       24 阅读
  2. c++_文件解析_读取_每行用字符分割_去除两头空格

    2024-07-13 06:06:03       21 阅读
  3. 使用 OpenCV 的 inRange 函数进行颜色分割

    2024-07-13 06:06:03       22 阅读
  4. Web控件进阶交互

    2024-07-13 06:06:03       29 阅读
  5. iOS开发-Xcode

    2024-07-13 06:06:03       21 阅读
  6. Xcode依赖管理大师:精通项目依赖的艺术与实践

    2024-07-13 06:06:03       22 阅读
  7. 数据结构笔记之特殊矩阵压缩

    2024-07-13 06:06:03       26 阅读
  8. 交换机的二三层原理

    2024-07-13 06:06:03       25 阅读
  9. pdf工具

    pdf工具

    2024-07-13 06:06:03      24 阅读
  10. Xcode项目文件与资源管理:精通技巧与实践指南

    2024-07-13 06:06:03       26 阅读
  11. ChatGPT对话:如何制作静态网页?

    2024-07-13 06:06:03       24 阅读
  12. QLabel控件

    2024-07-13 06:06:03       23 阅读
  13. deepstream读取mp4文件及不同类型视频输入bug解决

    2024-07-13 06:06:03       27 阅读