剑指Offer-快速排序

剑指Offer上的快速排序的Partition函数与我在数据结构书上学到的不一样,因此就想要探索下这两种不同的处理方式。

1.基本思想

快速排序的基本思想是基于分治法。在待排序表L[1...n]中任取一个元素pivot作为基准,通过一趟排序将待排序表划分为独立的两部分L[1...k-1]和L[k+1...n],使得L[1...k-1]中所有元素小于pivot,L[k+1...n]中所有元素大于或等于pivot,则pivot放在了其最终位置L(k)上。而后递归地对两个子表重复上述过程,直至每部分内只有一个元素或空为止,即所有元素放在了其最终位置上。

2.剑指Offer上的Partition实现

 1 // Partition为分割函数, length表示data数组的长度
 2 int Partition(int data[], int length, int low, int high) {
 3     if (data == nullptr || length <= 0 || low < 0 || high >= length) {
 4         return 1;
 5     }
 6     
 7     // 在[low, high]区间中随机取一个基准值,并将其换到区间最末尾
 8     int index = RandomInRange(low, high);
 9     Swap(&data[index], &data[high]);
10     
11     // small在low的前面一位
12     int small = low - 1;
13        
14     for (index = low; index < high; ++index) {
15         if (data[index] < data[high]) {
16             // ++small后表示大于基准值的第一个数的下标
17             // 如果不存在大于基准值的数,则表示当前比较的数字下标
18             ++small;
19 
20             // 交换后的small表示小于基准值的最后一个数字的下标
21             if (small != index) {
22                 Swap(&data[index], &data[small]);
23             }
24         }
25     }
26     
27     // ++small后表示大于基准值的第一个数的下标
28     ++small;
29     Swap(&data[small], &data[high]);
30     
31     return small;
32     
33 }

 

快速排序其它函数实现如下:

 1 // 生成一个随机数,范围在[low,high),是一个前闭后开的区间
 2 int RandomInRange(int low, int high) {
 3     return rand() % (high - low) + low;
 4 }
 5 
 6 // 交换两个整数
 7 void Swap(int* left, int* right) {
 8     int temp  = *left;
 9     *left = *right;
10     *right = temp;
11 }
12 
13 // 快速排序函数
14 void QuickSort(int data[], int length, int low, int high) {
15     if (low == high) {
16         return;
17     }
18     
19     int index = Partition(data, length, low, high);
20     
21     if (index > low) {
22         QuickSort(data, length, low, index - 1);
23     }
24     
25     if (index < high) {
26         QuickSort(data, length, index + 1, high);
27     }
28 }
29 
30 // 数据打印
31 void PrintData(int data[], int length) {
32     for (int i = 0; i < length; ++i) {
33         std::cout << data[i] << " ";
34     }
35     
36     std::cout << std::highl;
37 }
38 
39 int main() {
40     // 以系统时间作为随机函数种子
41     srand((unsigned)time(nullptr));
42 
43     int const length = 10;
44     
45     int data[length];
46 
47     // 生成随机数
48     for (int i = 0; i < length; ++i) {
49         data[i] = RandomInRange(1, length*length);
50     }
51     
52     // 打印生成的随机数
53     PrintData(data, length);
54     
55     // 调用快排函数进行排序
56     QuickSort(data, length, 0, length - 1);
57   
58     // 打印快速排序后的数
59     PrintData(data, length);
60     
61     return 0;
62     
63 }

 

初始顺序为{49, 38, 65, 97, 76, 13, 27},基准值为49的数据的第一趟快速排序的实现如下所示:

 

剑指Offer-快速排序

 

第一趟排序完之后基准值49之前的{27, 38, 13}都比49小,基准值49之后的{76, 65, 97}都比49大。

3.一般教材上的Partition实现

 1 // Partition为分割函数
 2 int Partition(int data[], int low, int high) {
 3 
 4     // 将当前表中第一个元素设为基准值,对表进行划分
 5     int pivot = data[low]; 
 6 
 7     // 循环跳出条件
 8     while (low < high) {
 9         while (low < high && data[high] >= pivot) {
10             --high;
11         }
12 
13         // 将比基准值小的元素移动到左端
14         data[low] = data[high];
15 
16         while (low < high && data[low] <= pivot) {
17             ++low;
18         }
19 
20         // 将比基准值大的元素移动到右端
21         data[high] = data[low];
22     }
23 
24     // 基准元素存放到最终位置
25     data[low] = pivot;
26 
27     // 返回存放基准值的最终位置
28     return low;
29 }

 

快速排序其它函数实现如下:

 

 1 void QuickSort(int data[], int low, int high) {
 2     // 递归跳出条件
 3     if (low < high) {
 4         // 将data[low...high]一分为二,pivotpos是基准值
 5         int pivotpos = Partition(data, low, high);
 6 
 7         // 对左子表进行递归排序
 8         QuickSort(data, low, pivotpos - 1);
 9 
10         // 对右子表进行递归排序
11         QuickSort(data, pivotpos + 1, high);
12     }
13 }

 

初始顺序为{49, 38, 65, 97, 76, 13, 27},基准值为49的数据的第一趟快速排序的实现如下所示:

 

剑指Offer-快速排序

 

第一趟排序完之后基准值49之前的{27, 38, 13}都比49小,基准值49之后的{76, 97, 65}都比49大。

4.总结

可以看出,上述这两种思想导致的运行过程是不同的,前者是用small指代小于标准元素的最右位置,在遍历过程中进行交换操作来保证small左边都是小于标准元素的元素,而后者是用low,high分别表示小于和大于基准值的集合边界位置,所以后者更浅显易懂。

 

上一篇:放大镜效果


下一篇:Alias随机算法