数据结构:第五章(一)

视频链接: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个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解

步骤

  1. 分解,将要解决的问题划分成若干规模较小的同类问题;
  2. 求解,当子问题划分得足够小时,用较简单的方法解决;
  3. 合并,按原问题的要求,将子问题的解逐层合并构成原问题的解。
    在这里插入图片描述
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]
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值