视频链接:https://www.bilibili.com/video/BV1HQ4y1d7th
视频范围P179 - P183
1.二分查找算法【折半查找】
当要从一个序列中查找一个元素的时候,二分查找是一种非常快速的查找算法,二分查找又叫折半查找。
它对要查找的序列有两个要求
- 一是该序列必须是有序的(即该序列中的所有元素都是按照大小关系排好序的,升序和降序都可以,本文假设是升序排列的)
- 二是该序列必须是顺序存储的
package kruskal;
public class BinarySearch {
public static void main(String[] args) {
int[] array = {2,3,4,5,6,7,8,9};
int i = binarySearch(array,8);
System.out.println(i);//输出为:6
}
public static int binarySearch(int[] array,int target){
//指向最小位置的索引
int min = 0;
int max = array.length - 1;
while (min <= max){
//求出平均值索引位置
int mid = (min + max) / 2;
if (array[mid] == target){
return mid;
}else if (array[mid] > target){
max = mid - 1;
}else {
min = mid + 1;
}
}
return -1;
}
}
2.分治算法
基本思想:将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解
步骤:
- 分解,将要解决的问题划分成若干规模较小的同类问题;
- 求解,当子问题划分得足够小时,用较简单的方法解决;
- 合并,按原问题的要求,将子问题的解逐层合并构成原问题的解。
package kruskal;
public class HanoiTower {
public static void main(String[] args) {
hanoitower(3,'A','B','C');
}
public static void hanoitower(int num,char a,char b,char c){
if (num == 1){
System.out.println("第" + num + "个盘从" + a + "---->" + c);
}else{
//把a移动到了b
hanoitower(num - 1,a,c,b);
System.out.println("第" + num + "个盘从" + a + "---->" + c);
hanoitower(num - 1,b,a,c);
}
}
}
运行结果:
3.动态规划算法
动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。
动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有己解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。具体的动态规划算法多种多样,但它们具有相同的填表格式。
package kruskal;
public class Dyanmic {
public static void main(String[] args) {
int[][] arry = {{1,3,5,9},{8,1,3,4},{5,0,6,1},{8,8,4,0}};
System.out.println(myanmic(arry));
}
public static int myanmic(int[][] array){
if (array.length == 0){
return 0;
}
//声明一个新的二维数组,用来存储计算路径值
int[][] dp = new int[array.length][array[0].length];
dp[0][0] = array[0][0];
//从第一行左边开始计算路径值
for (int i = 1; i < dp[0].length; i++) {
dp[0][i] = dp[0][i - 1] + array[0][i];
}
for (int i = 1; i < array.length; i++) {//遍历每行
for (int j = 0; j < dp[i].length; j++) {
if (j == 0){
dp[i][j] = dp[i - 1][j] + array[i][j];
}else if (dp[i - 1][j] < dp[i][j - 1]){
//上面路径小
dp[i][j] = dp[i - 1][j] + array[i][j];
}else{
//左边路径小
dp[i][j] = dp[i][j - 1] + array[i][j];
}
}
}
return dp[dp.length - 1][dp[dp.length - 1].length - 1];
}
}
4.KMP算法
KMP算法:解决在字符串(也叫主串)中的模式(pattern)定位问题【关键字搜索】
模式串就是关键字(接下来称它为P),如果它在一个主串(接下来称为T)中出现,就返回它的具体位置,否则返回-1(常用手段)。
在进行字符串匹配时,KMP算法与朴素算法最大的区别就在于KMP算法省去了主串与子串不必要的回溯,这也是KMP算法(在主串有较多重复时)更加高效的关键
移动位数 = 已匹配的字符数 - 对应的部分匹配值
package kruskal;
public class KMP {
public static void main(String[] args) {
System.out.println(KMP("ABCDE","ABX",0));//输出为:-1
System.out.println(KMP("ABCDE","ABC",0));//输出为:0
System.out.println(KMP("ABCDE","CD",0));//输出为:2
System.out.println(KMP("CDABG","ABA",0));//输出为:-1
}
public static int KMP(String s,String t,int pos){
int i = pos - 1;
int j = -1;
int[] next = getNext(t);
while (i < s.length() && j < t.length()){
if (j == -1 || s.charAt(i) == t.charAt(j)){
j++;
i++;
}else {
j = next[j];
}
}
if (j == t.length()){
return i - t.length();
}else {
return -1;
}
}
//获取每次回溯的数组位置
public static int[] getNext(String t){
int[] next = new int[t.length()];
int m = 0;
int n = -1;
next[0] = -1;
while (m < t.length() - 1){
if (n == -1 || t.charAt(m) == t.charAt(n)){
//m++;
//n++;
//if (t.charAt(m) != t.charAt(n)){
//next[m] = n;
//}else {
//next[m] = next[n];
//}
next[++m] = ++n;
}else {
n = next[n];
}
}
return next;
}
}
以下面的例子来解析:
主串 = “ABCABBADE”
模式串 = “ABBA”
对于getNext函数:
初始化:
int[] next = new int[t.length()];
int m = 0;
int n = -1;
next[0] = -1;
对于KMP的初始化:
int i = pos - 1;
int j = -1;
int[] next = getNext(t);
5.贪心算法
贪婪算法(贪心算法)是指在对问题进行求解时,在每一步选择中都采取最好或者最优(即最有利)的选择,从而希望能够导致结果是最好或者最优的算法
贪婪算法所得到的结果不一定是最优的结果(有时候会是最优解),但是都是相对近似(接近)最优解的结果
假设存在下面需要付费的广播台,以及广播台信号可以覆盖的地区。如何选择最少的广播台,让所有的地区都可以接收到信号
package kruskal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
public class Greedy {
public static void main(String[] args) {
//创建一个集合,存储城市
ArrayList<String> areaList = new ArrayList<>();
areaList.add("北京");
areaList.add("上海");
areaList.add("天津");
areaList.add("广州");
areaList.add("深圳");
areaList.add("成都");
areaList.add("杭州");
areaList.add("大连");
HashSet<String> area1 = new HashSet<>();
area1.add("北京");
area1.add("上海");
area1.add("天津");
HashSet<String> area2 = new HashSet<>();
area2.add("广州");
area2.add("北京");
area2.add("深圳");
HashSet<String> area3 = new HashSet<>();
area3.add("成都");
area3.add("上海");
area3.add("杭州");
HashSet<String> area4 = new HashSet<>();
area4.add("上海");
area4.add("天津");
HashSet<String> area5 = new HashSet<>();
area5.add("杭州");
area5.add("大连");
//电台所覆盖城市的范围
HashMap<String,HashSet<String>> broadcasts = new HashMap<>();
broadcasts.put("k1",area1);
broadcasts.put("k2",area2);
broadcasts.put("k3",area3);
broadcasts.put("k4",area4);
broadcasts.put("k5",area5);
//用户存储符合要求的电台
ArrayList<String> keyList = new ArrayList<>();
//用于存储每一次遍历areaList交集最多电台
String key = null;
//临时用来存储电台所覆盖的城市
HashSet<String> temp = new HashSet<>();
//循环条件是areaList没有城市数据,说明每个城市都被覆盖
while (!areaList.isEmpty()){
for (String broadcastKey : broadcasts.keySet()) {
//先进行清空
temp.clear();
temp.addAll(broadcasts.get(broadcastKey));
//retainAll 取出交集
temp.retainAll(areaList);
if (temp.size() > 0 && (key == null || temp.size() > broadcasts.get(key).size())){
key = broadcastKey;
}
}
if (key != null){
keyList.add(key);
areaList.removeAll(broadcasts.get(key));
key = null;
}
}
System.out.println(keyList.toString());//运行结果:[k1, k2, k3, k5]
}
}