2018.4.12更新:
//增加8种Sorting算法的过程和动图展示。
如同昨天笔记中记录的,刷题的过程中,我渐渐意识到,学习编程的一个重点是算法,于是在某乎上寻找参考教材,有许多人推荐了这本入门书目,是由Robert Sedgewick和Kevin Wayne编写的Algorithms,第4版,他们还为此制作了一个网站,网站链接也一并放上来吧。
网站上放了许多与书本内容相关的习题,补充阅读资料等。因为时间紧迫,我动用某电子书下载网站获取了这本书的PDF档,并且在Coursera公开课网站上找到了配套的视频教程,由作者亲自讲解。Cousera的课程链接也放上来好了。
在了解了这个公开课的大纲后,我准备先学习视频,然后对照书本去了解每一种算法的具体实现,有一点好的是,这本书给出的代码是用Java实现的,而我也正好要学习Java。
今天的主要笔记是排序问题的算法,排序问题其实从大学学习计算机基础的时候,就讲过一种,叫冒泡法排序,但是这种方法的复杂度较大,因为它要针对数组的每一个元素进行逐个比较。我前一篇笔记给出的网站上提供了一共8种排序算法的动图演示。借助这个动图,我们可以清楚地了解各算法的差异。
第一种是冒泡法排序,它的算法过程如下:
do swapped = false
for i = 1 to indexOfLastUnsortedElement-1
if leftElement > rightElement
swap(leftElement, rightElement)
swapped = true
while swapped
它的方法是使用for循环,将序列的元素两两比较,如果左边元素小于右边,则保持顺序不变,否则交换两个元素位置,让小的放左边。如GIF动图所显示的,第一轮交换后,还要继续N轮交换,每次交换都会把较小的往前挪,这个算法很费明。
第二种是选择法排序,它的算法过程如下:
repeat (numOfElements - 1) times
set the first unsorted element as the minimum
for each of the unsorted elements
if element < currentMinimum
set element as new minimum
swap minimum with first unsorted position
先假定第一个元素是最小的,然后使用for循环遍历其他元素,与"最小值"进行比较,如果找到比最小值小的元素,则交接两元素位置。这时我们已经能确认找到了真的最小值,最左边的元素不用再动了。此时需要排序的元素数量为N-1.这
接下来对N-1长度的序列再重复此过程,直至所有元素按顺序排列完成。
第三种是插入排序,它的算法过程如下:
mark first element as sorted
for each unsorted element X
'extract' the element X
for j = lastSortedIndex down to 0
if current element j > X
move sorted element to the right by 1
break loop and insert X here
方法是默认第一个元素已经排序完成,从最后一个元素开始,假定名叫arr[j],与前面的元素逐一比较,如果arr[j]比前面的元素小,则继续往前找,如果比该元素大,则在该位置插入arr[j],重复下去即可。这样让序列的小数值逐步挪到前面,大数值挪到后面,最终完成排序。
第四种是合并排序,它的算法过程如下:
split each element into partitions of size 1
recursively merge adjancent partitions
for i = leftPartStartIndex to rightPartLastIndex inclusive
if leftPartHeadValue <= rightPartHeadValue
copy leftPartHeadValue
else: copy rightPartHeadValue
copy elements back to original array
方法是先将序列拆分为子序列,然后每个子序列先从小到大排序,比如分为三部分,排序完成后再逐一比较每个子序列,每个序列的1号元素和1号元素比,2号元素和2号元素比,确定最后的先后顺序,最后只剩下两个子序列,再比较一次就可以完成排序。
第五种是Quick Sort,它的算法过程如下:
for each (unsorted) partition
set first element as pivot
storeIndex = pivotIndex + 1
for i = pivotIndex + 1 to rightmostIndex
if element[i] < element[pivot]
swap(i, storeIndex);
storeIndex++ swap(pivot, storeIndex - 1)
默认第一个元素不动,从第二个元素开始,与后面的其他元素比较,如果大于其他元素,则两者位置互换,这样渐渐把小的放在前面,大的放在后面。
第六种是Random Quick Sort,它的算法过程如下:
for each (unsorted) partition
randomly select pivot, swap with first element
storeIndex = pivotIndex + 1
for i = pivotIndex + 1 to rightmostIndex
if element[i] < element[pivot]
swap(i, storeIndex);
storeIndex++
swap(pivot, storeIndex - 1)
与Qucik Sort相比,它只是随机选择了一个数作为第一个元素,其他一样。
第七种是Counting Sort,它的算法过程如下:
create key (counting) array
for each element in list
increase the respective counter by 1
for each counter, starting from smallest key
while counter is non-zero restore element to list
decrease counter by 1
方法是创建一个计数数组,用于统计每个相同大小的元素个数,在对所有数计数完成后,按从小到大的顺序取数,每取一个数,相应的计数减1。这种适用于有许多重复元素的序列。
第八种是Randix Sort,它的算法过程如下:
Create 10 buckets (queues) for each digit (0 to 9)
for each digit placing
for each element in list
move element into respective bucket
for each bucket, starting from smallest digit
while bucket is non-empty
restore element to list
方法是先创建标记0-9的十个桶,用于装数字,然后按照低位排序,先排个位数,将所有个位数为0的放在一个桶中,个位数为1的放在一个桶中。然后是十位数排序,直到最大的数所在的位数。具体每步的排序结果如下:
最后按0-9的序列把每个桶里的元素提取出来放回原序列即可。看懂了吗?