快速排序

  1. 快速排序

大多数情况下效率更高,所以应用非常广泛。再加上快速排序所采用的分治思想非常实用。

定义

  1. 从数组中取出一个数,称之为基数(pivot)
  2. 遍历数组,将比基数大的数字放到它的右边,比基数小的数字放到它的左边。遍历完成后,数组被分成了左右两个区域
  3. 将左右两个区域视为两个数组,重复前两个步骤,直到排序完成

特点

快速排序的每一次遍历,都将基数摆到了 最终位置 上。

实现

快速排序的实现需要两个函数,递归函数和分区函数。分区函数将基数摆到最终位置并返回其下标,递归函数则根据基数的下标将数组分成左右两个部分并进行下一轮排序,直到数组最终有序。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void exchange(int * arr, int i, int j);

void quickSort(int * arr, int start, int end);

int partition(int * arr, int start, int end);

int main()
{
  int* nums = NULL;
  int numsSize = 4;
  nums = (int*)malloc(4*sizeof(int));
  memset(nums,0,4*sizeof(int));
  nums[0] = 5;
  nums[1] = 2;
  nums[2] = 3;
  nums[3] = 1;
  //nums[4] = 6;
  //nums[5] = 4;

  //5 2 3 1

  quickSort(nums, 0, numsSize-1);

  int i;
  for(i=0;i<numsSize;i++) printf("[%d]: %d\n",i,nums[i]);

  free(nums);
  return 0;
}

void exchange(int * arr, int i, int j) {
  int temp = arr[i];
  arr[i] = arr[j];
  arr[j] = temp;
}

void quickSort(int * arr, int start, int end) {
  //元素个数小于等于2退出
  if(start == end || start == end+1) return;
  int middle = partition(arr, start, end);
  printf("middle=%d\n",middle);
  quickSort(arr, start, middle-1);
  quickSort(arr, middle+1, end);
}

int partition(int * arr, int start, int end) {
  int pivot = arr[start];
  printf("pivot=%d\n",pivot);
  int l = start+1;
  int r = end;

  while(l < r) {
    while(l<r && arr[l]<=pivot) l++;
    while(l<r && arr[r]>=pivot) r--;
    if(l < r) {
      exchange(arr,l,r);
      l++;
      r--;
    }
  }
  printf("l=%d,r=%d\n",l,r);
  if(l == r && arr[r]>pivot) r--;
  if(r != start) exchange(arr,start,r);
  return r;
}

几点疑问

  1. 下标 l 和 r 指向的元素何时发生交换?
    下标 l 逐渐增大,当指向的元素大于基数时停下;下标 r 逐渐减小,当指向的元素小于基数时停下;此时交换下标 l 和 r 指向的元素,使得元素基本有序,并且 l 自增,r 自减;继续遍历数据。
  2. 跳出循环的条件?
    数据排序时,基数,比基数小者,比基数大者。当且仅当下标 l 和 r 重合时,跳出循环并插入基数。
  3. 基数的位置如何确定?
    数据排序时,基数,比基数小者,比基数大者。难道后面两者数据的相对位置不变,在中间插入基数吗?类似,比基数小者,基数,比基数大者。
    最后的结果如此,但是不是单单整体移动 比基数小者 (不是平移其中的所有元素给基数一个正中间的位置)。
    而是直接将 基数 和 比基数小者 中的最后一个交换位置,这样仍然符合分区并且最方便。因此需要 比基数小者 中最后一个元素的下标。
  4. 如何获取 比基数小者 中最后一个元素的下标?
    刚跳出循环后,下标 l 和 r 指向的元素满足条件 l=r 时,才能说明找到基数的最终位置,计算上使用 比基数大者 的最小下标减一,必然是 比基数小者 中的最后一个,否则 r 应该自减。直接交换 基数 和 比基数小者 中的最后一个。
  5. 快速排序使用的参数是元素的下标,即从零开始到数组长度减一。

参考资料

  1. 快速排序
posted @ 2022-01-24 00:40  万载志  阅读(52)  评论(0)    收藏  举报