算法 算法分析
本书是基于算法导论第一节课的总结。主要总结算法分析中的时间复杂度,并通过排序算法中的插入排序和归并排序来分析。文章中的代码由go语言实现
算法的时间复杂度的计算
分析算法运行的快慢,主要有以下几个方面决定:输入数据的大小(data size),输入数据的好坏,在什么样的平台上运行。一个好的程序或者软件,需要对输入的对象有一定的研究。比如排序算法,在size<30的情况下插入排序是最为理想的排序算法,当size>100之后,归并排序的速度就非常的具有优势了。因此在当前高级语言的成熟的排序算法中,对于一个大数据量文本的排序,在里面处理小数据量片段的时候,会使用插入排序来实现。
考虑到以上几个方面的差异,在算法研究中,有一个很重要的思想,渐近分析(asymptotic analysis) 因为对于 和,即使后者运行在超级计算机上,前者运行在普通计算机上,当n大到一定程度之后,会比快。这就是渐近分析的作用,忽略了机器常数,又考虑了算法运行时间的增长。
插入排序
对于一个,将其进行排序,
先看排序问题的伪代码:
for j = 1 to n-1
do key = a[j]
i <- j-1
while i > 0 && a[i] > key
do a[i+1] <- a[i]
i <- i-1
a[i+1] = key
下面是使用go语言实现的排序算法:
func InsertSort(a []int) {
length := len(a)
if length < 2{
return
}
for j := 1; j < length; j++{
key := a[j]
i := j-1
for i >= 0 && a[i] > key{
a[i+1] = a[i]
i--
}
a[i+1] = key
}
}
像这样的代码,我们可以得到排除掉时间常数,每次循环需要最多需要遍历的次数为j,所以因此其时间复杂度为
归并排序
同样对于上面的代码,使用归并排序的伪代码为:
if n = 1 done
other recursirely sorting two part and merging A[0...n/2-1] and A[n/2...n-1]
devide the left and right part to smaller part, until n=1
下面是使用go语言实现的归并排序算法:
func mergeSort(a []int, left, right int) {
if left == right{
return
}
mid := (left+right)/2
mergeSort(a, left, mid)
mergeSort(a, mid+1, right)
var temp []int
temp = append(temp, a[left:right+1]...)
tempMid := (len(temp)-1)/2
tempRight := len(temp)-1
i, j:= 0, tempMid+1
for i <= tempMid && j <= tempRight{
if temp[i] > temp[j]{
a[left] = temp[j];left++;j++
}else{
a[left] = temp[i];left++;i++
}
}
if j > tempRight{
a[left] = temp[i];left++;i++
}else if(i > tempMid) {
a[left] = temp[j];left++;j++
}
}
func MergeSort(a []int){
length := len(a)
if length <= 1 {return}
mergeSort(a, 0, length-1)
}
我们来考虑将两个有序数组合为一个有序数组的过程,是需要变量这两个数组的所有元素的,因此在递归树的每一层所需要的时间损耗为 递归树有多少层呢 ? 因此算法的时间复杂度为 这个算法是比上面的插入排序在大数据量的情况下,是具有非常大的优势的。
两种算法的综合
需要声明的是,成熟的排序算法,会使用到快速排序等其他的排序算法。本文只是将归并和插入结合起来,也就是在数据量小于一定值之后,使用插入排序:
const thresholdValue int = 8
func mergeSort(a []int, left, right int) {
if left == right{
return
}
if right - left > thresholdValue{
InsertSort(a[left:right+1])
}
.........
}
借助于go语言test的支持,我们可以更为直接的得到程序运行的情况
package main
import "testing"
func BenchmarkMergeSort(b *testing.B) {
testData := make([]int, 100000)
for i := 0; i < 100000; i++{
testData[i] = rand.Intn(100000)
}
for i:= 0; i < b.N ;i++ {
MergeSort(testData)
}
}
func BenchmarkInsertSort(b *testing.B) {
testData := make([]int, 100000)
for i := 0; i < 100000; i++{
testData[i] = rand.Intn(100000)
}
for i:= 0; i < b.N ;i++ {
InsertSort(testData)
}
}
//运行结果
BenchmarkMergeSort-4 132 9127102 ns/op 14312409 B/op 99999 allocs/op
PASS
ok github.com/exerciseOfGolang/sorttest 1.590s
BenchmarkInsertSort-4 1 2040541200 ns/op 803208 B/op 3 allocs/op
PASS
ok github.com/exerciseOfGolang/sorttest 1.673s