详解前缀树和贪心算法

目录

前缀树: 

 贪心算法:​编辑

贪心算法解题思路: 

 安排会议宣讲的最好方法(使用贪心算法):

 分金条,求最小代价:​编辑

求做项目的最大收益: 

 利用大根堆和小根堆来得到一个数据流的中位数​编辑

八皇后问题(N皇后问题):   时间复杂度为O(N^N)

// 第一种方法,含打印信息 

// 第二种方法 不含打印信息

// 第三种方法, 使用位运算优化常数时间(位运算 运算速度最快)


前缀树: 

 生成前缀树方法,将字符作为路径的权值,挂上一个不为空的结点。没有此路径就新建,否则就复用。

案例展示:

/**
 * @ProjectName: study3
 * @FileName: TrieNode
 * @author:HWJ
 * @Data: 2023/6/10 17:18
 */
public class TrieNode {
    public int pass;
    public int end;
    // HashMap<Char, Node> nexts;
    // TreeMap<Char, Node> nexts;  如果字符种类特别多,可以用此方式来添加路
    public TrieNode[] nexts;


    public TrieNode() {
        this.pass = 0;
        this.end = 0;
        // nexts[0] = null 没有走向‘a’的路
        // nexts[0] != null 有走向‘a’的路
        // ...
        // nexts[25] != null 有走向‘a’的路
        this.nexts = new TrieNode[26];
    }
}

/**
 * @ProjectName: study3
 * @FileName: Trie
 * @author:HWJ
 * @Data: 2023/6/10 17:20
 */
public class Trie {
    private TrieNode root;

    public Trie() {
        this.root = new TrieNode();
    }

    public void insert(String word) {
        if (word == null) {
            return;
        }
        char[] charArray = word.toCharArray();
        TrieNode node = root;
        node.pass++;
        int index = 0;
        for (int i = 0; i < charArray.length; i++) {
            index = charArray[i] - 'a';
            if (node.nexts[index] == null) {
                node.nexts[index] = new TrieNode();
            }
            node = node.nexts[index];
            node.pass++;
        }
        node.end++;
    }

    // word 这个单词之前加入过几次
    public int search(String word) {
        if (word == null) {
            return 0;
        }
        char[] charArray = word.toCharArray();
        TrieNode node = root;
        int index = 0;
        for (int i = 0; i < charArray.length; i++) {
            index = charArray[i] - 'a';
            if (node.nexts[index] == null){
                return 0;
            }
            node = node.nexts[index];
        }
        return node.end;
    }

    // 所有加入的字符串中,有几个是以pre这个字符串作为前缀的
    public int prefixNumber(String pre) {
        if (pre == null) {
            return 0;
        }
        char[] charArray = pre.toCharArray();
        TrieNode node = root;
        int index = 0;
        for (int i = 0; i < charArray.length; i++) {
            index = charArray[i] - 'a';
            if (node.nexts[index] == null){
                return 0;
            }
            node = node.nexts[index];
        }
        return node.pass;
    }

    public void delete(String word){
        if (search(word) != 0) { // 确定树中确实加入过word,才删除
            int index = 0;
            TrieNode node = root;
            node.pass--;
            char[] charArray = word.toCharArray();
            for (int i = 0; i < charArray.length; i++) {
                index = charArray[i] - 'a';
                if (--node.nexts[index].pass == 0){
                    node.nexts[index] = null;
                    return;
                }
                node = node.nexts[index];
            }
            node.end--;
        }
    }

}

 贪心算法:

贪心算法解题思路: 

 贪心算法是得到的局部最优解  , 如何通过贪心算法得到全部最优解, 需要一定的技巧和练习;

 安排会议宣讲的最好方法(使用贪心算法):

import java.util.Arrays;
import java.util.Comparator;

/**
 * @ProjectName: study3
 * @FileName: BestArrangement
 * @author:HWJ
 * @Data: 2023/6/10 20:18
 */
public class BestArrangement {
    public static class Program{
        public int start;
        public int end;

        public Program(int start, int end) {
            this.start = start;
            this.end = end;
        }
    }

    public static class ProgramComparator implements Comparator<Program> {

        @Override
        public int compare(Program o1, Program o2) { // 以结束时间来升序排列, 先结束的项目先做
            return o1.end - o2.end;
        }
    }

    public static int bestArrange(Program[] programs, int timeStart){
        int result = 0;
        Arrays.sort(programs, new ProgramComparator());
        for (Program program : programs) {
            if (timeStart <= program.start){
                timeStart = program.end;
                result++;
            }
        }
        return result;
    }

}

import java.util.Arrays;
import java.util.Comparator;

/**
 * @ProjectName: study3
 * @FileName: StringMerge
 * @author:HWJ
 * @Data: 2023/6/10 20:58
 */
public class StringMerge {
    public static void main(String[] args) {
        String result = StringMerging(new String[]{"b", "ba"});
        System.out.println(result);
    }
    public static class StringComparator implements Comparator<String>{

        @Override
        public int compare(String o1, String o2) {
            return (o1 + o2).compareTo(o2 + o1); // o1 和 o2 拼接 和 o2 和 o1 拼接进行比较,谁的字典序小,那个就排前面
        }


    }
    public static String StringMerging(String[] strings){
        Arrays.sort(strings, new StringComparator()); // 排完序后直接相加即可
        StringBuilder result = new StringBuilder();
        for (String s :strings) {
            result.append(s);
        }
        return result.toString();
    }
}

 

 分金条,求最小代价:

public class LessMoney {
    public static void main(String[] args) {
        int[] arr = {20, 30, 10};
        System.out.println(lessMoney(arr));
    }

    public static int lessMoney(int[] arr) {
        PriorityQueue<Integer> pq = new PriorityQueue<>();
        for (int i = 0; i < arr.length; i++) {
            pq.add(arr[i]);
        }
        int sum = 0;
        int cur = 0;
        while (pq.size() > 1) {
            cur = pq.poll() + pq.poll();
            sum += cur;
            pq.add(cur);
        }
        return sum;
    }
}

求做项目的最大收益: 

import java.util.Comparator;
import java.util.PriorityQueue;

/**
 * @ProjectName: study3
 * @FileName: IPO
 * @author:HWJ
 * @Data: 2023/6/10 21:29
 */
public class IPO {
    public static class Node{
        public int cost;
        public int interest;

        public Node(int cost, int interest) {
            this.cost = cost;
            this.interest = interest;
        }
    }

    public static class minCost implements Comparator<Node>{

        @Override
        public int compare(Node o1, Node o2) {
            return o1.cost - o2.cost;
        }
    }

    public static class maxInterest implements Comparator<Node>{

        @Override
        public int compare(Node o1, Node o2) {
            return o2.interest - o1.interest;
        }
    }

    public static int getMaxMoney(int[] costs, int[] profits, int k, int m){
        // k 表示你最多做的项目个数
        // m 表示你的现有资金、
        // costs 表示所有项目的花费
        // profits 表示所有项目的收益
        PriorityQueue<Node> minCost = new PriorityQueue<>(new minCost());
        for (int i = 0; i < costs.length; i++) {
            minCost.add(new Node(costs[i], profits[i])); // 以花销加入来创建小根堆
        }
        PriorityQueue<Node> maxProfit = new PriorityQueue<>(new maxInterest());
        while (k != 0){
            while (!minCost.isEmpty()) {
                Node node = minCost.poll();
                if (node.cost <= m) { // 如果能够完成的项目就加入大根堆(以收益来作为比较)
                    maxProfit.add(node);
                }
            }
            if (maxProfit.isEmpty()){ // 如果此时还能做项目,但是没有满足条件的项目就返回
                break;
            }else {
                k--;
                Node node = maxProfit.poll();
                m += node.interest;  // 完成收益最高的项目,然后将其移除
            }
        }
        return m;
    }
}

 利用大根堆和小根堆来得到一个数据流的中位数

 // 利用大根堆和小根堆来得到一个数据流的中位数

// 如果新加入的数 <=大根堆,就进入大根堆,否则进入小根堆

//如果大根堆和小根堆数组大小相差达到二,则多的那个堆顶进入另一个堆

import java.util.Comparator;
import java.util.PriorityQueue;

/**
 * @ProjectName: study3
 * @FileName: searchAverage
 * @author:HWJ
 * @Data: 2023/6/10 21:54
 */
public class searchAverage {
    public static void main(String[] args) {
        double[] arr = {2,3,1,4,5,0};
        System.out.println(average(arr));
    }

    public static class minNumber implements Comparator<Double> { // 实现大根堆

        @Override
        public int compare(Double o1, Double o2) {
            return (int) (o2 - o1);
        } // 这里返回整数,所以需要转型
    }

    public static class maxNumber implements Comparator<Double> { // 实现小根堆

        @Override
        public int compare(Double o1, Double o2) {
            return (int) (o1 - o2);
        }
    }

    public static double average(double[] arr) {
        if (arr == null) {
            return 0;
        }
        PriorityQueue<Double> min = new PriorityQueue<>(new minNumber()); // 实现大根堆, 放小数
        PriorityQueue<Double> max = new PriorityQueue<>(new maxNumber()); // 实现小根堆, 放大数
        min.add(arr[0]);
        for (int i = 1; i < arr.length; i++) {
            // 比较要加入的数,与大根堆的堆顶进行比较,如果小于等于堆顶就加入大根堆,否则加入小根堆
            if (arr[i] <= min.peek()) { 
                min.add(arr[i]);
            } else {
                max.add(arr[i]);
            }
            // 如果两个的大小达到了2 就将多的那个的堆顶加入少的
            if (max.size() - min.size() == 2) {
                min.add(max.poll());
            }else if (max.size() - min.size() == -2){
                max.add(min.poll());
            }
        }
        // 两个堆大小相等就返回两个堆顶的平均值
        // 否则返回多的那个堆顶
        if (min.size() == max.size()){ 
            return (min.poll() + max.poll()) / 2;
        }else if(min.size() > max.size()){
            return min.poll();
        }else {
            return max.poll();
        }
    }
}

八皇后问题(N皇后问题):   时间复杂度为O(N^N)

没法对时间复杂度进行优化,但可以进行对常数时间的优化

// 第一种方法,含打印信息 

/**
 * @ProjectName: study3
 * @FileName: Queen
 * @author:HWJ
 * @Data: 2023/6/11 9:41
 */
class Queen {
    int count = 0; // 记录有多少方法

    Queen() {
    }

    public boolean judge(int[] var1, int var2) {
        // 判断加入的此行 和以前行是冲突
        for(int var3 = 0; var3 < var2; var3++) {
            // var1[var2] == var1[var3] 判断是否在同列
            // Math.abs(var2 - var3) == Math.abs(var1[var2] - var1[var3] 判断是否在同斜线上
            // 因为如果在同行会顶出数据 所以不做判断
            if (var1[var2] == var1[var3] || Math.abs(var2 - var3) == Math.abs(var1[var2] - var1[var3])) {
                return false;
            }
        }

        return true;
    }

    public void putQueen(int[] var1, int var2, int n) {
        //判断第n-1行和当前行是否冲突
        if (this.judge(var1, n-1)) {
            ++this.count;
            System.out.println("\n第" + this.count + "种方法为:  ");
            this.print(var1);
        } else {
            for(int var3 = 0; var3 < n; ++var3) {
                var1[var2] = var3;
                if (this.judge(var1, var2)) {// 如果不冲突就加入,然后继续寻找下一行的有效位置
                    this.putQueen(var1, var2 + 1, n); // 使用递归和循环的策略找到所有放置方法
                }
            }
        }

    }

    public void print(int[] var1) {
        // 构建一个棋盘列表
        char[][] var2 = new char[var1.length][var1.length];

        for(int var3 = 0; var3 < var2.length; ++var3) { // 遍历棋盘赋值,并打印
            for(int var4 = 0; var4 < var2[var3].length; ++var4) {
                var2[var3][var4] = '*';
                var2[var3][var1[var3]] = 'Q';
                System.out.print(var2[var3][var4] + " ");
            }

            System.out.println();
        }

    }
}

import java.util.Scanner;

/**
 * @ProjectName: study3
 * @FileName: QueenProblem
 * @author:HWJ
 * @Data: 2023/6/11 9:42
 */
public class QueenProblem {
    public QueenProblem() {
    }

    public static void main(String[] var0) {
        Scanner input = new Scanner(System.in);
        int N = input.nextInt(); // 输入N 表示N*N的棋盘
        int[] var1 = new int[N];
        Queen var2 = new Queen();
        // putQueen(var1, var2, N) var1表示棋盘 var1[2]=0, 表示当前第三行第一列已经有一个皇后
        // var2 表示当前需要在第几行添加数据
        // N 表示一共有多少行
        var2.putQueen(var1, 0, N);
        System.out.println("一共有" + var2.count + "种方法");
    }
}

// 第二种方法 不含打印信息

import java.util.Date;

/**
 * @ProjectName: study3
 * @FileName: Queen2
 * @author:HWJ
 * @Data: 2023/6/11 10:27
 */
public class Queen2 {
    public static void main(String[] args) {
        Date date = new Date();
        System.out.println(num1(14));
        Date date1 = new Date();
        System.out.println(date1.getTime() - date.getTime());
    }

    public static int num1(int n){
        if(n < 1){ // 如果传入数据为0或者负数,直接返回0;
            return 0;
        }
        int[] record = new int[n];
        // 0 表示当前需要加入数据的行,record表示棋盘 record[2]=0, 表示当前第三行第一列已经有一个皇后
        return process1(0, record, n);
    }

    public static int process1(int i, int[] record, int n){
        if (i == n){ // 如果所有行都加入了,则找到了一种方法。
            return 1;
        }
        int res = 0;
        for (int j = 0; j < n; j++) {
            if (isValid(record, i, j)){ // 先判断j这个数是否能加入到第i行,如果能加入就加入,并寻找下一行
                record[i] = j;
                res += process1(i+1, record, n);
            }
        }
        return res;
    }

    public static boolean isValid(int[] record, int i, int j){
        // 判断加入的此行 和以前行是冲突
        for (int k = 0; k < i; k++) {
            if (record[k] == j || Math.abs(i - k) == Math.abs(j - record[k])){
                return false;
            }
        }
        return true;
    }
}

// 第三种方法, 使用位运算优化常数时间(位运算 运算速度最快)

import java.util.Date;

/**
 * @ProjectName: study3
 * @FileName: Queen3
 * @author:HWJ
 * @Data: 2023/6/11 11:27
 */
public class Queen3 {
    public static void main(String[] args) {
        Date date = new Date();
        System.out.println(num(14));
        Date date1 = new Date();
        System.out.println(date1.getTime() - date.getTime());
    }

    // 这里使用位运算 限制不能超过32皇后问题, 如果想求更大的问题,可以将int换为long即可求不超过64皇后的问题
    public static int num(int n){
        if (n < 1 || n > 32){
            return 0;
        }
        int limit = n == 32? -1:(1<<n)-1;
        return process(limit, 0, 0, 0);
    }

    public static int process(int limit, //总限制,将放皇后的位置进行限制
                              int colLim, // 列限制
                              int leftDiaLim, // 左斜线限制
                              int rightDiaLim )   { // 右斜线限制,均是1不能放皇后 ,0可以放皇后
        if (colLim == limit){
            return 1;
        }
        // 得到所有候选皇后的位置
        int pos = limit & (~(colLim | leftDiaLim | rightDiaLim)); // 用于寻找当前行的限制
        int mostRightOne = 0; // 找到当前行可以放皇后的最右位置
        int res = 0;
        while (pos != 0){
            mostRightOne = pos & (~pos + 1);
            pos -= mostRightOne;
            res += process(limit, colLim | mostRightOne, // 得到下一行的列限制, 左斜线限制, 右斜线限制
                    (leftDiaLim | mostRightOne)<<1,
                    (rightDiaLim | mostRightOne)>> 1);

        }
        return res;
    }

}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Studying~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值