做快速排序的时候:选择一个基准值,最左边 left, 最右边 right;然后从 right -> left, 找比基准值小的值A往前挪,填上基准值的位置,此时A处空白,right 指向A处;接着从 left ->right, 找比基准值大的值B往后挪,填上A的位置。
例如下图数组:
序列 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
值 | 19 | 14 | 23 | 5 | 90 | 34 | 56 | 12 | 33 | 66 | 88 | 34 |
left | right |
1.设 基准值 temp = 19,left 指向19 (序列=0),right指向34(序列=11)。
2.把基准值的位置 空出来。
3.从 right - > left,找到比基准值小的值A往前挪,填上基准值的位置。 变为如下:
序列 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
值 | 12 | 14 | 23 | 5 | 90 | 34 | 56 | 19 | 33 | 66 | 88 | 34 |
left | right |
此时 left + 1 变为如下:
序列 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
值 | 12 | 14 | 23 | 5 | 90 | 34 | 56 | 19 | 33 | 66 | 88 | 34 |
left | right |
4.从 left - > right,找到基准值大的值B往后挪,填上A的位置。变为如下:
序列 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
值 | 12 | 14 | 19 | 5 | 90 | 34 | 56 | 23 | 33 | 66 | 88 | 34 |
left | right |
此时 right + 1 变为如下:
序列 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
值 | 12 | 14 | 19 | 5 | 90 | 34 | 56 | 23 | 33 | 66 | 88 | 34 |
left | right |
5.接着循环 3,4 步;如下图
序列 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
值 | 12 | 14 | 19 | 5 | 90 | 34 | 56 | 23 | 33 | 66 | 88 | 34 |
left | right |
序列 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
值 | 12 | 14 | 5 | 19 | 90 | 34 | 56 | 23 | 33 | 66 | 88 | 34 |
left | right |
序列 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
值 | 12 | 14 | 5 | 19 | 90 | 34 | 56 | 23 | 33 | 66 | 88 | 34 |
left right |
当 left = right 时,就是第一轮的快速排序结束了,基准值 19 填入 这个 left = right 的位置。
基准值左边的比基准值小;右边的比基准值大。
这个 left=right的位置把列表分为了左右两个子列表,这个位置设为 mid,作为分区函数的返回值;
那下一轮的快速排序就会变成 left ~ mid - 1 以及 mid + 1 ~ right
分区函数有两种写法(Delphi):
第一种: 直接赋值(为了方便理解,把 19 当成空白,这个不会出现死循环的情况)
没考虑 arr[right] = temp 或者 arr[left] = temp,有可能赋值之前已经arr[left] = arr[right]。
function TForm3.Test_Partition(var arr: array of Integer; left,
right: Integer): Integer;
var
temp: Integer;
begin
Result := 0;
temp := arr[left];
while left < right do
begin
while (left < right) and (arr[right] > temp) do
begin
Dec(right);
end;
if left < right then
begin
arr[left] := arr[right];
Inc(left);
end;
if left = right then Break;
while (left < right) and (arr[left] < temp) do
begin
Inc(left);
end;
if left < right then
begin
arr[right] := arr[left];
Dec(right);
end;
if left = right then Break;
end;
Result := left;
arr[left] := temp;
end;
或者 考虑到了 arr[right] = temp 或者 arr[left] = temp, 只允许出现一种情况的赋值
function TForm3.Test_Partition_1(var arr: array of Integer; left,
right: Integer): Integer;
var
temp: Integer;
begin
Result := 0;
temp := arr[left];
while left < right do
begin
while (left < right) and (arr[right] >= temp) do
begin
Dec(right);
end;
//把只有小于基准值的值往前挪
if left < right then
begin
arr[left] := arr[right];
end;
while (left < right) and (arr[left] <= temp) do
begin
Inc(left);
end;
//把只有大于基准值的值往后挪
if left < right then
begin
arr[right] := arr[left];
end;
end;
Result := left;
arr[left] := temp;
end;
第二种:可以理解为互换
function TForm3.Test_Partition1(var arr: array of Integer; left,
right: integer): Integer;
var
temp, temp1: Integer;
str: string;
i: Integer;
begin
Result := -1;
temp := arr[left];
while left < right do
begin
while (left < right) and (arr[right] > temp) do
begin
Dec(right);
end;
while (left < right) and (arr[left] < temp) do
begin
Inc(left);
end;
if (arr[right] = arr[left]) and (arr[right] = temp) then
Dec(right); //或者 Inc(left),都可以
//此处是作为特殊情况处理,当有重复的数值,解决掉死循环的问题。
if left < right then
begin
temp1 := arr[right];
arr[right] := arr[left];
arr[left] := temp1;
end;
end;
if arr[left] = temp then Result := left
else if arr[right] = temp then Result := right
end;
---------------------- 分割线 2021/07/16 ----------------------
最近一直在研究 “十大经典排序算法”,特别是里面的快速排序。为了更好的理解,打算分解画个图(分区函数)。只做记录。
有数组如下图:
序列 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
值 | 4 | 9 | 5 | 3 | 1 | 2 | 9 |
left | index | right |
1.首先第一步还是确定基准值 设基准值 temp := arr[left],index:= left + 1
2.要循环基准值所有右边的数据, index -> right 找到第一个 比 temp 小的数据
序列 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
值 | 4 | 9 | 5 | 3 | 1 | 2 | 9 |
left | index i | right |
序列 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
值 | 4 | 9 | 5 | 3 | 1 | 2 | 9 |
left | index | i | right |
当 i = 3 时,发现 arr[3] = 3 < temp,所以此时,arr[i] 和 arr[index]互换,并且 Inc(index),同时继续第2步。
序列 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
值 | 4 | 3 | 5 | 9 | 1 | 2 | 9 |
left | index i | right |
序列 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
值 | 4 | 3 | 5 | 9 | 1 | 2 | 9 |
left | index | i | right |
序列 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
值 | 4 | 3 | 1 | 9 | 5 | 2 | 9 |
left | index i | right |
序列 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
值 | 4 | 3 | 1 | 9 | 5 | 2 | 9 |
left | index | i | right |
序列 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
值 | 4 | 3 | 1 | 2 | 5 | 9 | 9 |
left | index i | right |
3.当从 index->right中未能找出 arr[i] < temp,则退出循环。并且替换 arr[left] 和 arr[index - 1]。
序列 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
值 | 2 | 3 | 1 | 4 | 5 | 9 | 9 |
left | index i | right |
此时分区的中间位置 Result = index - 1。