分治法
特征
该问题的规模缩小到一定的程度就可以容易地解决;该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质利用该问题分解出的子问题的解可以合并为该问题的解;该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。
能否利用分治法完全取决于问题是否具有这条特征,如果具备了前两条特征,而不具备第三条特征,则可以考虑贪心算法或动态规划。
伪代码
divide
-and
-conquer(P
)
if(|P
|<= n0
) adhoc(P
);
divide P into smaller subinstances Pl
,P
....Pk
;
for(i
=1,i
<=k
,i
++)
yi
=divide
-and
-conquer(Pi
);
returnmrrge
....yk
);
人们从大量实践中发现,在用分治法设计算法时,最好使子问题的规模大致相同。即将一个问题分成大小相等的k个子问题的处理方法是行之有效的。这种使子问题规模大致相等的做法是出自一种平衡(balancing)子问题的思想,它几乎总是比子问题规模不等的做法要好。
例子
求指定整型数组的最大值和最小值。
传统做法就是遍历一遍下来求出最大值和最小值,时间复杂度是O(n)。
function divide_conquer(arr
,from,to
){
if(to
- from == 1){
return {"max":Math
.max(arr
[from],arr
[to
]),"min":Math
.min(arr
[from],arr
[to
])}
}else if(to
- from == 0){
return {"max":arr
[from],"min":arr
[to
]}
}else{
var middle
= parseInt(from+(to
-from)/2);
var result1
= divide_conquer(arr
,from,middle
);
var result2
= divide_conquer(arr
,middle
+1,to
);
var result
= {};
if(result1
["max"] > result2
["max"]){
result
["max"] = result1
["max"];
}else{
result
["max"] = result2
["max"];
}
if(result1
["min"] > result2
["min"]){
result
["min"] = result2
["min"];
}else{
result
["min"] = result1
["min"];
}
return result
;
}
}
var arr
= [34,5,6,7111,7,8,889,9];
console
.log(divide_conquer(arr
,0,arr
.length
-1));
二分搜索
给你一-个装有16个硬币的袋子。16个硬币中有一 个是伪造的,并且那个伪造的硬币比真的硬币要轻一些。你的任务是找出这个伪造的硬币。为了帮助你 完成这一任务,将提供台可用来比较两组硬币重量的仪器,利用这台仪器,可以知道两组硬币的重量是否相同。
算法描述
将16个硬币分成A、B两半;将A放仪器的一边, B放另-边,如果A袋轻,则表明伪币在A ,解子问题A即可,否则,解 子问题B。
实例
给定已按升序排好序的n个元素a|0:n-1|,现要在这n个元素中找出特定元素x。
function BinarySearch(arr
, target
) {
arr
.sort((a
, b
) => { return a
- b
});
let left
= 0;
let right
= arr
.length
- 1;
while (left
<= right
) {
let mid
= Math
.floor(left
+ (right
- left
) / 2);
if (arr
[mid
] > target
) {
right
= mid
- 1;
} else if (arr
[mid
] < target
) {
left
= mid
+ 1;
} else {
return "该数在数组中";
}
}
return "该数不存在"
}
BinarySearch([3, 2, 1, 3, 4, 6, 7], 1);
console
.log(BinarySearch([3, 2, 1, 3, 4, 6, 7], 1))
快速排序
思路
快速排序基于分治思想
Divide :分割Alp…r]成为2个子集合(可以空) Alp…q- 1]和A[q+ 1…r] 使得A[p…q- 1|的每个元素都小于等于A[q],A[q + 1…r’]中的每个元素都大于等于A[q],索引q作为分割点。
Conquer :通过递归调用快速排序对A[p…q-1]和A[q +1…r]排序。
Combine :每个子集合排序完成之后,整个数组的排序也完成。
伪代码
template
<class Type>
void QuickSort (Type a
], int p
, int r
)
{
if (p
<r
){
int q
=Partition(a
,p
,r
);
QuickSort (a
,p
,q
-1);
QuickSort (a
,q
+1,r
);
}
实例
function quick(arr
){
if(arr
.length
<=1){
return arr
;
}
let mid
= Math
.floor(arr
.length
/2);
let midvalue
= arr
.splice(mid
,1)[0];
let arrright
= [],
arrleft
= [];
for(let i
= 0;i
<arr
.length
;i
++){
let items
= arr
[i
];
items
<midvalue
?arrleft
.push(items
):arrright
.push(items
)
}
return quick(arrleft
).concat(midvalue
,quick(arrright
));
}
quick([23,54,67,57,87,798,56,1,2,4])
快排的进一步思考
快速排序算法的性能取决于划分的对称性。通过修改算法partition ,可以设计出采用随机选择策略的快速排序算法。
伪代码
template
<class Type>
int
RandomizedPartition (Type a
[], int p
, int r
)
{
int i
= Random(p
,r
);
Swap(a
[i
], a
[p
]);
return Partition(a
, p
, r
);
}
归并排序
算法描述
若n为1 ,算法终止;否则,将这一元素集合分割成两个或更多个子集合,对每- -个子集合分别排序然后将排好序的子集合归并为一个集合。
实例
function mergeSort(arr
) {
const length
= arr
.length
;
if (length
=== 1) {
return arr
;
}
const mid
= Math
.floor(length
/ 2);
const left
= arr
.slice(0, mid
);
const right
= arr
.slice(mid
, length
);
return merge(mergeSort(left
), mergeSort(right
));
}
function merge(left
, right
) {
const result
= [];
let il
= 0;
let ir
= 0;
while( il
< left
.length
&& ir
< right
.length
) {
if (left
[il
] < right
[ir
]) {
result
.push(left
[il
]);
il
++;
} else {
result
.push(right
[ir
]);
ir
++;
}
}
while (il
< left
.length
) {
result
.push(left
[il
]);
il
++;
}
while(ir
< right
.length
) {
result
.push(right
[ir
]);
ir
++;
}
return result
;
}
console
.log(mergeSort([2,9,1,8,3,]))
覆盖残缺棋盘
伪代码
void chessBoard(int tr
, int tc
, int dr
, int dc
, int size
)
if (size
== 1) return;
intt
= tile
++,
s
= size
/2;
if(dr
<tr
+ s
&&dc
<tc
+ s
)
chessBoard(tr
, tc
, dr
, dc
, s
);
else{l1此棋盘中无特殊方格
board
[tr
+ S- 1][tC
+ s
- 1]=t
;
chessBoard(tr
, tc
, tr
+s
-1, tc
+s
-1, s
);}
if(dr
<tr
+ s
&&dc
>=tc
+ s
)
/1特殊方格在此棋盘中
chessBoard(tr
, tc
+s
, dr
, dc
, s
);
else {
board
[tr
+s
- 1][tc
+ s
]=t
;
chessBoard(tr
, tc
+s
, tr
+s
-1, tc
+s
, s
);}
if (dr
>=tr
+ s
&&dc
<tc
+ s
)
chessBoard(tr
+s
, tc
, dr
, dc
, s
);
else {
board
[tr
+ s
][tc
+ S- 1]= t
;
chessBoard(tr
+s
, tc
, tr
+s
, tc
+s
-1, s
);}
if(dr
>=tr
+ s
&& dc
>=tc
+ s
)
chessBoard(tr
+s
, tc
+s
, dr
, dc
, s
);
else {
board
[tr
+ s
][tC
+ s
]=t
;
chessBoard(tr
+s
, tc
+s
, tr
+s
, tc
+s
, s
);}
}
复杂度分析
复杂度分析 0(1)k=0 T(k)= 4T(k-l)+O(1) k>0
T(n)=O(4k) 渐进意义下的最优算法