对于一个给定的数组,我们让数组其中一个元素为枢纽值,其他元素都与其进行比较,比枢纽值小
的放在左边,比枢纽值大的放在右边,这样的话,数组被分为两个部分,左半部分都是比枢纽值小
的,右半部分都是比枢纽值大的。而根据此思路一直递归,最后便可得到排序好的数组。
//快速排序--分治
void quick_sort(int q[],int l,int r){
if(l >= r)//判边界,l为左边界,r为右边界。
return;
int m = q[l],i = l - 1,j = r + 1;//定义两个指针,i为左指针,j为右指针,两指针分别向中间走,q[l]为比较的枢纽。
while(i < j){//循环进行的条件:左指针小于右指针
do i++;while (q[i] < m);//如果q[i]小于枢纽值,说明其位置符合条件,左指针向右移动。
do j--;while(q[j] > m);//如果q[j]大于枢纽值,说明其位置也符合条件,右指针向左移动。
if(i < j)//i < j说明遇到了不符合条件的情况,则直接交换两指针指向的元素,进入下一轮循环。
swap(q[i],q[j]);
}
quick_sort(q,l,j);//递归处理小于x的左半部分
quick_sort(q,j + 1,r);//递归处理大于x的右半部分
}
说明:
l 和 r 为数组的左边界和右边界,因为每次递归处理,区间都相当于减半,需要重新规定排序的范围。
i 和 j 分别为数组的左指针和右指针(不是传统意义上的指针,可以理解为下标),他们的值分别
为数组左右边界的下标值,算法进行时,两指针分别向数组中间走,看所指向的值是否满足小于枢
纽值或是大于枢纽值的(左小,右大)。如果两指针遍历完发现 i 本身的值仍小于 j 本身的值,说
明 i 此时指向的值是大于枢纽值的,或是 j 指向的值是小于枢纽值的,不符合条件,则直接将两数
组值进行交换。至此递归体完成,再对左半部分和右半部分分别递归即可。
给定数组:
5 2 7 6 8
执行流程
对于两个已经排好序的数组,我们只需要每次将两个数组的左值依次比较,最后一个一个赋值到新
的数组里,新的数组即为排好序的数组。
也就是说将原数组分成的两个子数组按上述方法进行遍历,最后得到的新的数组即为排好序的数组。
这说明该算法要先对数组的左半部分和右半部分进行递归,最后再进行遍历赋值新数组。
//归并排序:分治
void merge_sort(int q[],int l,int r){
if(l >= r)//分别找到左边界和右边界
return;
int mid = (l + r) / 2;//找到中间值(数组中间)
merge_sort(q, l ,mid);//递归排序中间值的左半边
merge_sort(q, mid + 1, r);//递归排序中间值的右半边
int k = 0,i = l, j = mid + 1;//k用来记录每个数存到了temp数组的哪个下标,i是左半部分的左指针,j是右半部分的左指针,现在这两个子数组都是有序的,同时遍历两个子数组进行数组的重构
while(i <= mid && j <= r){
if(q[i] <= q[j])
temp[k ++] = q[i ++];
else
temp[k ++] = q[j ++];
}
while(i <= mid)//如果左半部分的数组没有遍历完,则将未遍历完的数组加到temp数组的后面去
temp[k++] = q[i++];
while(j <= r)//如果右半部分的数组没有遍历完,则将未遍历完的数组加到temp数组的后面去
temp[k++] = q[j++];
for(i = l,j = 0;i <= r;i++,j++)//将结果数组复制到原数组里
q[i] = temp[j];
}
思路与快速排序有些相似之处,只不过该算法是先递归,后进行排序操作,具体解读在注释里。
给定数组
5 2 7 6 8
执行流程
两种算法的本质是相同的,思想都是分治,即分而治之。将一个大数组分成多个小数组分别进行处
理。最后合起来得到的数组即为目标数组。
注意:两种排序算法不仅仅能用于排序,稍加修改便可以去解其他的题,如归并排序稍加修改就可
以去求一个序列中的逆序数。
所以,我们对算法的执行流程需要高度理解,不仅仅是背过代码就可以。