第十七章 二分查找(非递归)和分治算法(汉诺塔)
一、二分查找
1.思路
int[] arr = {1,3,8,10,11,67,100}(默认数组升序)
- while 循环 left 不小于 right 时,退出
- 找到中间结点 mid,小于目标结点则向右(left = mid + 1)、大于则向左(right = mid - 1)
2.代码实现
package com.sisyphus.binarysearchnorecursion;
import java.util.Scanner;
/**
* @Description: 二分查找(非递归)$
* @Param: $
* @return: $
* @Author: Sisyphus
* @Date: 7/27$
*/
public class BinarySearchNoRecur {
public static void main(String[] args) {
int[] arr = {1,3,8,15,23,39};
Scanner scanner = new Scanner(System.in);
System.out.println("请输入你要查找的整数:");
int target = scanner.nextInt();
int index = binarySearch(arr,target);
System.out.println("要找的数的索引为:" + index);
}
private static int binarySearch(int[] arr, int target){
int left = 0;
int right = arr.length - 1;
while (left <= right){
int mid = (left + right) / 2;
if (target == arr[mid]){
return mid;
}else if(target > arr[mid]){
left = mid + 1;
}else{
right = mid - 1;
}
}
return -1;
}
}
二、分治算法(汉诺塔)
1.概述
分治算法,即分而治之:把一个复杂问题分成两个或更多的相同或相似子问题,直到最后子问题可以简单地直接求解,最后将子问题的解合并为原问题的解。归并排序就是一个典型的分治算法
分治算法遵循三个步骤:
- 分解:分解原问题为结构相同的子问题(即寻找子问题)
- 解决:当分解到容易求解的边界后,进行递归求解
- 合并:将子问题的解合并成原问题的解
伪代码:
Divide-and-Conquer(P)
if |P| ≤ n0
then return(ADHOC(P))
将 P 分解为较小的子问题 P1,P2,……,Pk
for i ← 1 to k
do yi ← Divide-and-Conquer(Pi) 🔺 递归解决 Pi
T ← MERGE(y1,y2,……,yk) 🔺 合并子问题
return(T)
其中,|P| 表示问题 P 的规模,n0 为一阈值,表示当问题 P 的规模不超过 n0 时,问题已经可以直接解出,不必再继续分解。ADHOC§ 是该分治法中的基本子算法,用于直接解小规模的问题 P。因此,当 P 的规模不超过 n0 时直接用算法 ADHOC§ 求解。算法 MERGE(y1,y2,…,yk) 是该分治法中的合并子算法,用于将 P 的子问题 P1,P2,…,Pk 的相应的解 y1,y2,…,yk 合并为 P 的解
归并排序很好地体现了这三步骤
2.汉诺塔
算法步骤
- 如果只有一个盘,直接 A->C
- 如果有 n 个盘(n ≥ 2),我们总是可以看作是两个盘(最下面的一个盘和上面的所有盘)
先把上面的盘 A->B
再把最下面的一个盘 A->C
最后把 B 所有的盘 B->C
理解这个递归原理的关键:当两个盘的时候,我们可以通过其余两个空塔把两个盘放到两个塔其中的任意一个塔。当 3 个盘的时候,我们忽略掉最下面那个盘,那么不就是又回到了刚刚我们所说的 2 个盘的情形?只是 2 个盘的时候我们要求全都放在 C 塔,但其实放在 B 塔也是一样的!由于无论几个盘都需要放在 C 塔,所以为了方便,3 个盘的时候我们把上面这两个盘放在 B 塔。接着被我们忽略的盘就可以顺利地放在 C 塔了。既然那个最大的盘已经就位,那么我们当然就可以当它不存在了,这个时候,我们又回到了刚刚我们所说的两个盘的情形,只不过这次两个盘在 B 塔,但是无论这两个盘在哪个塔,我们都可以通过其余两个空塔把两个盘放到两个塔其中的任意一个塔
明白了上面那个道理之后,我们再来想 4 个盘的情形。通过上面的推演,3 个盘是可以通过其余两个空塔把 3 个盘放到两个塔其中的任意一个塔的,只是需要先忽略掉最下面那个盘。那么既然 3 个盘可以忽略掉最下面那个盘,看作是两个盘的情形,为什么 4 个盘不可以忽略掉最下面那个盘,看作是 3 个盘的情形呢?答案是当然可以,4 个盘可以忽略掉最下面的盘看作是 3 个盘的情形,3 个盘又可以忽略掉最下面那个盘,看作是 2 个盘的情形,而 2 个盘的情形是很好解决的
代码实现
package com.sisyphus.divideandconquer;
/**
* @Description: 汉诺塔$
* @Param: $
* @return: $
* @Author: Sisyphus
* @Date: 7/27$
*/
public class HanoiTower {
public static void main(String[] args) {
hanoiTower(1,'A','B','C');
}
private static void hanoiTower(int num, char a, char b, char c) {
if (num == 1){
System.out.println("第 1 个盘从 " + a + "->" + c);
}else{
//等到第 n - 1 到第 2 个盘全部递归完,才会打印第 1 句话(if代码块里的那句),
//结束后会回到 num = 2 的那一层,打印第 2 句话(就是下面那句),
//这个时候我们又要进入下面的下面的那句话再次进入递归
hanoiTower(num - 1, a, c, b);
System.out.println("第 " + num + " 个盘从 " + a + "->" + c);
hanoiTower(num - 1, b, a, c);
}
}
}