快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:
从数列中挑出一个元素,称为 “基准”(本例中使用了第一个元素作为基准);
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
#!/bin/bash
arr=(101 0 35 27 87 99 51 6 33 37 28 62 90 111 222 333 550 22 18 5 11 17 23 50 78 30 61)
icount=0
low0=0 #获得数组最小坐标
arrlen=${#arr[*]} #获得数组长度
let high0=arrlen-1 #获得数组最大坐标
function getIndex()
{
#递归函数调用,以数组第一个元素作为标准值,实现一次分组
s=$1 #起始坐标,起始坐标数组元素值作为标准值
e=$2 #终止坐标
tmp=${arr[$s]} #按坐标取数组元素值
f=2
while [ $s -lt $e ]; do
#循环条件为起始坐标小于终止坐标
if [ $f -eq 2 ]; then
if [ $tmp -le ${arr[$e]} ]; then #如果标准值小于最大坐标内的元素值,那么最大坐标收敛1
let e=e-1
elif [ $tmp -gt ${arr[$e]} ]; then #如果标准值大于最大坐标内的元素值,那么将其赋值给标准值数组内位置,起始点收敛1;此时标准值在临时变量tmp中
arr[$s]=${arr[$e]}
let s=s+1
f=1 #修改此值,if条件将会变为false,下次循环走另一个分支
fi
else
if [ $tmp -ge ${arr[$s]} ]; then #果标准值等于最大坐标内的元素值,那么直接收敛起始值1
let s=s+1
elif [ $tmp -lt ${arr[$s]} ]; then
arr[$e]=${arr[$s]}
let e=e-1
f=2 修改此值,if条件将会变为true,下次循环走另一个分支
fi
fi
done
arr[$s]=$tmp
return $s
}
function quicksort()
{
r[$icount]=0
low=$1
high=$2
let y0=high-low #获得初始坐标差
if [ $y0 -gt 1 ]; then
getIndex $low $high
x=$?
let icount=icount+1
low1=$low
let low2=x+1
let high1=x-1
high2=$high0
let y1=high1-low1
let y2=high2-low2
if [ $y1 -gt 1 ]; then
quicksort $low1 $high1
elif [ $y1 -eq 1 ]; then
tt=${arr[$low1]}
ttt=${arr[$high1]}
if [ $tt -gt $ttt ]; then
arr[$low1]=$ttt
arr[$high1]=$tt
return 0
fi
fi
if [ $y2 -gt 0 ]; then
quicksort $low2 $high2
elif [ $y2 -eq 1 ]; then
tt=${arr[$low2]}
ttt=${arr[$high2]}
if [ $tt -gt $ttt ]; then
arr[$low2]=$ttt
arr[$high2]=$tt
return 0
fi
fi
else
return 0
fi
}
echo ${arr[@]}
echo "--->>>"
quicksort $low0 $high0
echo ${arr[@]}
echo "END"
总结:
本例与之前的冒泡排序、插入排序、选择排序不同,其使用递归方式获得所有元素的顺序。
递归是本例的重点,也是难点。需要设定合理的终止条件,否则无法得到正确结果。
调试过程中也花了很多时间,代码中遗留了一些无用的代码。
另一个问题就是函数返回值和参数使用以及应用范围。这里给出本例实践结论:
1.主程序中的变量,在函数中可以使用,包括修改
2.函数返回值使用$?,使用中每次调用会覆盖前次执行返回值,但递归执行中只要再次调用前取出该值不影响最后结果。
3.参数可以是一个变量,变量可以是数组,但本例未这么实用,只是测试可以这么用。