算法设计与分析基础(四):蛮力法
蛮力法是一种简单直接处理问题但是不一定高效的方法。蛮力法具有以下的特点:
- 应用范围广,不受到实例规模的限制
- 当要解决的问题低频率出现,并且高效算法很难设计的时候可以选用蛮力法
- 对解决一些小规模的问题实例仍然有效
- 可以作为衡量其他算法的参照物
选择排序和冒泡排序
选择排序
- 原理
-
扫描整个列表,找出n个元素中的最小元素;
-
扫描最小元素以外的其他n-1个元素,找出它们中的最小元素;
-
重复上述操作,直到所剩元素个数为1。
-
算法的形式化描述
Algorithm SelectionSort(A[0..n-1]) for i=0 to n-2 do //最后剩下的数不用再进行比较 min := i; for j := i+1 to n-1 do if A[j]<A[min] then min := j ; swap A[i] and A[min]
基本操作:比较A[j]<A[min],交换数据swap
比较次数:C(n):
于是C(n)∈θ (n2)
数据交换次数:最坏情况O (n)
冒泡排序
- 原理
- 比较相邻的元素,将大的元素交换到右边的位置,重复多次后,最大元素就“沉淀”到列表的最后一个位置。
- 第二遍将第二大元素沉下去,n-1遍后结束。
-
算法形式化描述:
Algorithm BubbleSort(A[0..n-1]) for i:=0 to n-2 do for j:=0 to n-2-i do if A[j+1]<A[j] then swap A[j] and A[j+1]
基本操作:A[j]<A[j+1]
比较次数C(n):
于是C(n)∈θ (n2)
结论:选择排序和冒泡排序的时间函数均是属于θ(n2)
顺序查找和蛮力字符串匹配
顺序查找问题
已知有n个元素的数组A[0…n]. 给定元素K,要求找出一个下标i, 使得A[i]=K. 输出第一个值等于K的元素的位置,找不到返回-1.
算法:
算法 SequentialSearch(A[0..n-1],K)
i:=0;
while A[i]≠K and i<n do
i:=i+1;
if A[i]=K then return i
else return -1
改进后的算法:
改进算法 SequentialSearch2(A[0..n-1],K)
A[n]=K;//把查找键添加到列表的末尾,那么查找一定成功,所以不必每次循环都检查是否到了表的末尾。
i:=0;
while A[i]≠K do
i:=i+1;
if i<n then return i
else return -1
另一个简单的改进:对于有序数组,只要遇到一个大于或等于查找键的元素,查找就可以停止了。
蛮力字符匹配问题
将模式对准文本的前m个字符从左往右进行比对,如果其中有一个字符不匹配,模式往右移动一位继续下一个m个字符的比对。
从文本中寻找匹配模式的子串,即求出第一个匹配模式的子串在文本中的开始位置(子串最左元素的下标)。
其中:文本——给定的由n个字符组成的串
模式——指定的由m个字符组成的串
算法描述:
Algorithm BFSM (T[0..n-1], P[0..m-1])
// Brute Force String Match
//文本T[0..n-1]长度为n; 模式P[0..m-1]长度为m
//查找不成功时返回-1
begin
for i :=0 to n-m do
j := 0
while j<m and P[j]=T[i+j] do
j := j+1
if i=m return i
return -1
end
蛮力字符串匹配算法分析:
最坏的情形是模式须移动n-m+1次,每次移动模式之前,做足m次比对才发现不匹配(即第i+m位不匹配),
因此,在最坏情况下,该算法属于Θ(nm)。平均效率可达到Θ(n+m) =Θ(n)。
最近点对和凸包问题
最近点对
给定平面S上n个点,找其中的一对点,使得在n(n-1)/2个点对中,该点对的距离最小。
记(P.x,P.y)为点P的坐标值,则两个点Pi , Pj之间的距离公式为:
-
算法描述:
Algorithm BFCP ( P ) // Brute Force Closest Points // 输入:n个点的数组P,Pi(xi , yi), i=1, 2, …, n // 输出:两个最近点的下标index1和index2 begin dmin ← ∞; for i ← 1 to n-1 do for j ← i+1 to n do d ← sqrt((xi-xj)2+(yi-yj)2); if d < dmin then dmin ← d; index1 ← i; index2 ← j; return index1, index2 end.
凸包问题
- 凸集定义:
设S是平面点集,如果S中任意两点的连线都属于该集合,则称S是凸的 pp. 84-85。 - 凸包定义:
一个点集S的凸包是指包含S的最小凸集。显然,如果S是凸的,则S的凸包就是S本身(橡皮筋圈解释)。 - 凸包问题:
给定一个n个点的点集S,求S的凸包。
-
定理:
任意包含n>2个点的集合S的凸包,是以S中的某些点位顶点的凸多边形。特别,如果所有点共线,则多边形退
化为一条直线,它的两个端点仍在S中。
-
极值
凸集的极点是指不会出现在集合中任何线段的中间的点。凸多边形的顶点是极点。
-
解法
设点集大小为n,首先将其中的点两两配对,得到直线段条;然后对每一条直线段检查其余的n-2个点的相对该
直线段的正负性,如果它们都有相同的正负性,那么这条直线段属于凸多边形的边,其顶点是极点。
设直线方程为 ax + by = c
则
使ax+by-c > 0 的点 位于直线右(上)方
使ax+by-c < 0 的点 位于直线左(下)方
穷举法
穷举法是一种简单的蛮力方法,它要求生成所有可能的可行解,再从中选择最优解。
原理简单,大多数情况下是不可行的。
旅行商问题
TSP问题:
给定n个城市相互之间的距离,求一条能走遍n个城市的最短路径,使我们能从任一城市开始,访问每个城市只一次后回到起点。
等价于 “Hamilton回路问题” – (无权图)。
背包问题
给定n 个重量为w1 , w2, …, wn,价值为v1 , v2 , … , vn的物品和一个承重为W的背包,如何将物品装入背包使背包所获的价值最大。
分配问题
-
有n个任务需要分配给n个人执行,一人一个任务。将第j个任务分配给第i个人的成本是C[i, j],求总成本最小的分配方案。
-
穷举法要列出所有n! 个可能的分配方案。
深度优先和广度优先查找
深度优先查找
一个DFS输出序列是?a-c-d-f-b-e-g-h-i-j,需要用到栈结构
用下面的图进行理解:
广度优先查找
一个BFS输出序列是? a-c-d-e-f-b-g-h-j-i,需要用到队列结构
下面这个图可以更好理解广度遍历: