C#学习笔记本--第四篇(排序算法之快速排序)

一、基本原理

 //选取一个基准值
 //产生左右标识
 //左右比基准
 //满足则换位

 //排完一次
 //基准定位

 //左右递归
 //直到有序

二、图像化解释

先准备一个数组

        每一轮都选出一个基准值 然后基准值的左右不断和他比较 例如 第一轮我们选择的是第零个元素8作为基准值,我们以升序排为例,由于基准值是8,你可以将8放在中间,方便比较。简单介绍这几个参数,left和right你可以理解为边界值,就是每次比较的数组长度。这个tempLeft和tempRight你可以理解为一个可变的游标来回移动,当左右游标相等时,即确定了基准值在原数组中的下标,然后将他移动到这个位置即是他在有序数组中的位置。

        这里从右边开始和8进行比较,由于9大于8,9的自身位置不会发生改变,但是在前面我们说过,这个tempRight起到了一个游标的作用,他会移动,于是它会移动到6上的位置,也就是下标为7,见下一张图

这张图中,tempRight向左移动了一个位置,此时比较6和8的大小,6<8,没有资格放在8的右边,于是将6放在tempLeft=0的位置,也就是之前8的位置,见下一张图

看这张图,现在6移动到了tempLeft  = 0的位置,然后从这个位置开始又和基准值8进行比较,这就是左右和基准比较,由于6<8,满足我们升序的条件,然后tempLeft开始向右移动,变成一,见下一张图

看这张图,现在是下标tempLeft=1的元素开始和基准值进行比较,显然小于8放在左边,不会移动位置,于是在下一张图中继续tempLeft++变成2

7也小于8于是在下一张图中继续tempLeft++变成3

1也小于8于是在下一张图中继续 tempLeft++变成4

5也小于8于是tempLeft继续++在下一张图中变成5

4仍然小于8于是继续tempLeft++在下一张图中变成6

2还是小于8,于是继续tempLeft++在下一张图中变成7

注意此时tempLeft和tempRight值相等了,这说明我们已经找到了8在原数组中按照升序排列的正确下标

于是将8放进去,并以此为边界,将原数组划分为两个新的数组,到这里第一轮排序结束了。

        然后开始下一轮排序:上面已经说到8将原数组分为了两个部分,于是左边的数组为6 3 7 1 5 4 2,他,他们的left right tempLeft tempRight分别为 0 6 0 6,见下图,注意此时右边的元素9,由于他只有一个,实际上他的左右temp是相等的,也就是确定了他的在原数组的下标8。左边的数组我们仍然以第一个为基准值进行比较,这里和上面的步骤是完全一样的

见下图,将基准值3取出来,从右边开始比较,6>3有资格放在3的右边,于是tempRght--,在下一张图中变为5

这里7仍然大于3有资格放在3的右边,于是位置不变,tempRight--,在下一张图中变为4

4仍旧>3于是有资格放在3的右边,4的位置不变,继续tempRight--,在下一张图中变为3

5还是>3,于是位置不变,tempRight--

在这里2<3没有资格放在3的右边。有资格放在3的左边,于是将2移动到tempLeft==0的位置,见下一张图

这个又从tempLeft==0开始和3进行比较,2<3有资格放在左边,于是tempLeft++变成1见下一张图,

在这里1有资格放在3的左边于是位置不变,tempLeft继续++,变为2见下一张图

此时由于左右游标下标相等,此时就确定了基准值3在原数组中的位置即2,然后数组的剩余部分,和这里原理相同,就是不断的分和排序,边分边排,下面就不赘述了

三、代码实现

       ① 由于要不断的分数组,所以我们肯定会不断的使用自身的代码,会递归,所以肯定需要申明一个函数。仔细想想需要用到哪些参数呢,一个数组和左右边界就行了,需要输出什么吗,不需要,于是可以这样申明。

public static void QuickSort(int[] arr, int left, int right)
{
    
}

        ②先确定前面提到的几个重要的参数,基准值,左右游标

 //记录基准值
 //左游标
 //右游标
 int tempLeft, tempRight, temp;
 temp = arr[left];
 tempLeft = left;
 tempRight = right;

        ③在这里我们就可以开始写交换的核心逻辑了 不断的交换肯定是需要用到循环的对吧,三种循环会用到哪种呢,for循环就会一直跑,只好选择while循环比较方便。什么时候交换停止呢,就是前面提到的tempLeft和tempRight只要不相等就会一直进行比较,直到相等确定出下标为止。

//核心交换逻辑
//左右游标会不断变化 要不相同时才能继续变化
while (tempLeft != tempRight)
{ 
    
}

        ④在循环里面就可以写逻辑了,怎么交换呢,前面说了,是以数组的第0个元素作为基准值,然后从右边开始,依次和基准值比较,比基准值大的话就放在基准值的右边,不移动其位置,比基准值小的话就放到基准值的位置,然后又从左边开始比较。

进行比较位置交换
 //首先从右边开始比较 看 值有么有资格放到标识的右侧
 while(tempLeft < tempRight && arr[tempRight] > temp)
 {
     tempRight--;
 }
 //移动结束证明可以交换位置
 arr[tempLeft] = arr[tempRight];

 //上面是移动右侧游标
 //接着移动完右侧游标 就要来移动左侧游标
 while (tempLeft < tempRight && arr[tempLeft] < temp)
 {
     tempLeft++;
 
 }
 //移动结束证明可以交换位置
 arr[tempRight] = arr[tempLeft];

⑤完成了移动位置之后,跳出循环,此时的tempLeft和tempRight值是相等的,也就是基准值的位置,此时给基准值安下他的位置

//跳出循环后 放基准值放在中间位置
 //此时templeft和tempright相等
 arr[tempLeft] = temp;//左右边相等

⑥到这里已经完成了一轮的排序,而我们还需要不断的排序,周而复始,于是就需要递归自己,你看他完成了第一次排序之后,就是执行了前面的代码,是不是就已经需要分为两个部分来再次排序,直到等会递归结束(结束条件是左右边界相等了嘛),所以这里第一个使用自己的函数是相当于左边的数组,也就是在图形解释中的3 1 2 5 4 7 6,第二个使用自己的函数也就相当于前面的右边的数组9

//递归继续
 QuickSort(arr, left, tempRight - 1);
 QuickSort(arr, tempLeft + 1, right);

⑦递归也要结束嘛 不然就会一直跑,停不下来

//递归函数的结束条件
if (left >= right)
{
    return;
}

该排序函数完整代码:

 //第一步 申明用于快速排序的函数
 public static void QuickSort(int[] arr, int left, int right)
 {
     //第七步:输出结果
     //递归函数的结束条件
     if (left >= right)
     {
         return;
     }
     //第二步 
     //记录基准值
     //左游标
     //右游标
     int tempLeft, tempRight, temp;
     temp = arr[left];
     tempLeft = left;
     tempRight = right;

     //第三步:
     //核心交换逻辑
     //左右游标会不断变化 要不相同时才能继续变化
     while (tempLeft != tempRight)
     { 
         //第四步:进行比较位置交换
         //首先从右边开始比较 看 值有么有资格放到标识的右侧
         while(tempLeft < tempRight && arr[tempRight] > temp)
         {
             tempRight--;
         }
         //移动结束证明可以交换位置
         arr[tempLeft] = arr[tempRight];

         //上面是移动右侧游标
         //接着移动完右侧游标 就要来移动左侧游标
         while (tempLeft < tempRight && arr[tempLeft] < temp)
         {
             tempLeft++;
         
         }
         //移动结束证明可以交换位置
         arr[tempRight] = arr[tempLeft];
     }
     //第五步: 放置基准值
     //跳出循环后 放基准值放在中间位置
     //此时templeft和tempright相等
     arr[tempLeft] = temp;//左右边相等

     //第六步: 递归继续
     QuickSort(arr, left, tempRight - 1);
     QuickSort(arr, tempLeft + 1, right);

     
 }

四、总结

 //归并排序和快速排序都会用到递归
 //二者的区别
 //相同点:
 //1.他们都会用到递归
 //2.都会把数组分为几个部分
 //不同点:
 //1.归并排序递归过程中会不断的产生新数组用于合并;快速排序不会产生新数组
 //2.归并排序是拆分数组完毕再进行排序;快速排序是边排序边拆分

 //基本原理
 //选取基准值
 //产生左右标识
 //左右比基准
 //满足则换位
 //排完一次 基准定位
 //基准左右递归
 //直到有序

 //套路写法
 //基准值变量
 //左右游标记录

 //3层while循环
 //游标不停左右移动
 //重合则结束
 //结束定基准

 //注意事项
 //左右互访
 //while循环外定基准

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值