记排序算法——快速排序

做快速排序的时候:选择一个基准值,最左边 left, 最右边 right;然后从 right -> left, 找比基准值的值A往前挪,填上基准值的位置,此时A处空白,right 指向A处;接着从 left ->right, 找比基准值的值B往后挪,填上A的位置。

例如下图数组:

序列01234567891011
19142359034561233668834
leftright

1.设 基准值 temp = 19,left 指向19 (序列=0),right指向34(序列=11)。

2.把基准值的位置 空出来。

3.从 right - > left,找到比基准值的值A往前挪,填上基准值的位置。 变为如下:

序列01234567891011
12142359034561933668834
leftright

此时 left + 1 变为如下:

序列01234567891011
12142359034561933668834
leftright

4.从 left - > right,找到基准值的值B往后挪,填上A的位置。变为如下:

序列01234567891011
12141959034562333668834
leftright

此时 right + 1 变为如下:

序列01234567891011
12141959034562333668834
leftright

5.接着循环 3,4 步;如下图

序列01234567891011
12141959034562333668834
leftright
序列01234567891011
12145199034562333668834
leftright
序列01234567891011
12145199034562333668834

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 ----------------------

最近一直在研究 “十大经典排序算法”,特别是里面的快速排序。为了更好的理解,打算分解画个图(分区函数)。只做记录。

有数组如下图:

序列0123456
4953129
leftindexright

1.首先第一步还是确定基准值 设基准值 temp := arr[left],index:= left + 1

2.要循环基准值所有右边的数据, index -> right 找到第一个 比 temp 小的数据

序列0123456
4953129
left

index

i

right

序列0123456
4953129
left

index

iright

当 i = 3 时,发现 arr[3] = 3 < temp,所以此时,arr[i] 和 arr[index]互换,并且 Inc(index),同时继续第2步。

序列0123456
4359129
left

index

i

right

序列0123456
4359129
left

index

iright

序列0123456
4319529
left

index

i

right

序列0123456
4319529
left

index

iright

序列0123456
4312599
left

index

i

right

3.当从 index->right中未能找出 arr[i] < temp,则退出循环。并且替换 arr[left] 和 arr[index - 1]。

序列0123456
2314599
left

index

i

right

此时分区的中间位置 Result = index - 1。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值