一、基本原理
//选取一个基准值
//产生左右标识
//左右比基准
//满足则换位
//排完一次
//基准定位
//左右递归
//直到有序
二、图像化解释
先准备一个数组
每一轮都选出一个基准值 然后基准值的左右不断和他比较 例如 第一轮我们选择的是第零个元素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循环外定基准