算法入门基础——前缀数、最小字典序问题、切金条问题、会议室安排问题、项目收益问题、随机中位数问题

package com.zuoshen.jichurumen.class07;

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

/**
 * @author ShiZhe
 * @create 2022-03-08 23:14
 */
public class code01 {

    /**
     * 前缀数节点结构
     */
    public static class TrieNode {
        // 通过次数
        public int pass;
        // 作为结尾节点的次数
        public int end;
        // 可能包涵的节点数组
        public TrieNode[] nexts;

        public TrieNode() {
            pass = 0;
            end = 0;
            // 题目是26个字母的小写
            nexts = new TrieNode[26];
        }
    }

    /**
     * 前缀树
     */
    public static class Trie {
        // 头结点
        public TrieNode root;
        // 初始化
        public Trie() {
            root = new TrieNode();
        }
        // 字符串插入
        public void insert(String word) {
            if (word == null) {
                return;
            }
            // 将字符串转为字符数组
            char[] chars = word.toCharArray();
            // 将前缀树根节点赋值给node
            TrieNode node = root;
            // 节点数组的下标
            int index = 0;
            // 循环
            for (int i = 0; i < chars.length; i++) {
                index = chars[i] - 'a';
                if (node.nexts[index] == null) {
                    node.nexts[index] = new TrieNode();
                }
                node = node.nexts[index];
                node.pass++;
            }
            node.end++;
        }

        // 查看某字符串是否存在,存在返回字符串插入次数即end
        public int search(String word) {
            if (word == null) {
                return 0;
            }
            char[] chars = word.toCharArray();
            TrieNode node = root;
            int index = 0;
            for (int i = 0; i < chars.length; i++) {
                index = chars[i] - 'a';
                if (node.nexts[index] == null) {
                    return 0;
                }
                node = node.nexts[index];
            }
            return node.end;
        }

        // 删除某字符串
        public void delete(String word) {
            if (search(word) != 0) {
                char[] chars = word.toCharArray();
                TrieNode node = root;
                int index = 0;
                for (int i = 0; i < chars.length; i++) {
                    index = chars[i] - 'a';
                    // 当该路径只有该字符串时,后面不需要遍历,直接删除
                    if (--node.nexts[index].pass == 0) {
                        node.nexts[index] = null;
                        return;
                    }
                    node = node.nexts[i];
                }
                node.end--;
            }
        }

        // 查找以某字符串为前缀的数量
        public int prefixNumber(String pre) {
            if (pre == null) {
                return 0;
            }
            char[] chars = pre.toCharArray();
            TrieNode node = root;
            int index = 0;
            for (int i = 0; i < chars.length; i++) {
                index = chars[i] - 'a';
                if (node.nexts[index] == null) {
                    return 0;
                }
                node = node.nexts[index];
            }
            return node.pass;
        }
    }

    /**
     * 字符串拼接的最小字典序问题——贪心
     * 字典序:字符串在字典中的位置
     * @param strings
     * @return
     */
    public static String lowestString(String[] strings) {
        if (strings == null || strings.length == 0) {
            return "";
        }
        Arrays.sort(strings, new MyComparator());
        String result = "";
        for (int i = 0; i < strings.length; i++) {
            result += strings[i];
        }
        return result;
    }

    /**
     * 字符串拼接问题的比较器
     */
    public static class MyComparator implements Comparator<String> {

        @Override
        public int compare(String o1, String o2) {
            return (o1 + o2).compareTo(o2 + o1);
        }
    }

    /**
     * 切金条问题
     * 哈弗曼编码
     * @param arr
     * @return
     */
    public static int lessMoney(int[] arr) {
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
        for (int i = 0; i < arr.length; i++) {
            priorityQueue.add(arr[i]);
        }
        int sum = 0;
        int cur;
        while (priorityQueue.size() > 1) {
            cur = priorityQueue.poll() + priorityQueue.poll();
            sum += cur;
            priorityQueue.add(cur);
        }
        return sum;
    }

    /**
     * 会议室安排问题的项目结构
     */
    public static class Program {
        // 开始时间
        public int start;
        // 结束时间
        public int end;

        // 构造函数
        public Program(int start, int end) {
            this.start = start;
            this.end = end;
        }
    }
    /**
     * 会议室安排——贪心
     * 按照结束时间排序,持续时间越短,安排越多
     * @param programs
     * @param start
     * @return
     */
    public static int bestArrange(Program[] programs, int start) {
        Arrays.sort(programs, new ProgramComparator());
        int result = 0;
        for (int i = 0; i < programs.length; i++) {
            if (start <= programs[i].start) {
                result++;
                start = programs[i].end;
            }
        }
        return result;
    }

    /**
     * 会议室安排问题定义的比较器,按照end排列
     */
    public static class ProgramComparator implements Comparator<Program> {

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

    /**
     * IPO对应的节点结构
     */
    public static class CostNode {
        // 花费
        public int cost;
        // 利润
        public int profit;

        // 构造函数
        public CostNode(int cost, int profit) {
            this.cost = cost;
            this.profit =  profit;
        }
    }

    /**
     * 每做完一个项目,马上获得的收益,可以支持你去做下一个项目
     * @param costs 表示i号项目的花费
     * @param profits 表示i号项目在扣除花费之后还能挣到的钱(利润)
     * @param k 表示你只能串行的最多做k个项目
     * @param m 表示你初始的资金
     * @return 最后获得的最大钱数
     */
    public static int findMaximizedCapital(int[] costs, int[] profits, int k, int m) {
        // 节点初始化
        CostNode[] costNodes = new CostNode[costs.length];
        // 节点填充数据
        for (int i = 0; i < costs.length; i++) {
            costNodes[i] = new CostNode(costs[i], profits[i]);
        }
        // 项目的cost小根堆
        PriorityQueue<CostNode> minCostQueue = new PriorityQueue<>(new MinCostComparator());
        // 可进行项目的profit大根堆
        PriorityQueue<CostNode> maxProfitQueue = new PriorityQueue<>(new MaxProfitComparator());
        // 小根堆初始化
        for (int i = 0; i < costNodes.length; i++) {
            minCostQueue.add(costNodes[i]);
        }
        // k个项目限制
        for (int i = 0; i < k; i++) {
            // peek()获取堆顶元素但是不弹出
            while (!minCostQueue.isEmpty() && minCostQueue.peek().cost <= m) {
                maxProfitQueue.add(minCostQueue.poll());
            }
            // 无满足当前资金的花费的项目
            if (maxProfitQueue.isEmpty()) {
                return m;
            }
            // 利润叠加
            m += maxProfitQueue.poll().profit;
        }
        return m;
    }

    /**
     * cost从小到大排序
     */
    public static class MinCostComparator implements Comparator<CostNode> {

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

    /**
     * profit从大到小排序
     */
    public static class MaxProfitComparator implements Comparator<CostNode> {

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

    /**
     * 一个数据流中,随时可以取得中位数
     * 2个堆,一个从小到大,一个从大到小
     */
    public static class MedianHolder {
        // 小根堆保存的是大值
        private PriorityQueue<Integer> minQueue = new PriorityQueue<>(new MinHeapComparator());
        // 大根堆保存的是小值
        private PriorityQueue<Integer> maxQueue = new PriorityQueue<>(new MaxHeapComparator());

        // 调整,保证2个堆的size差不大于2
        private void modifyTwoHeapsSize() {
            if (this.minQueue.size() == this.maxQueue.size() + 2) {
                this.maxQueue.add(this.minQueue.poll());
            }
            if (this.maxQueue.size() == this.minQueue.size() + 2) {
                this.minQueue.add(this.maxQueue.poll());
            }
        }

        // 添加数
        private void addNumber(int num) {
            if (maxQueue.isEmpty() || num <= maxQueue.peek()) {
                maxQueue.add(num);
            } else {
                minQueue.add(num);
            }
            modifyTwoHeapsSize();
        }

        // 获取中位数
        public Integer getMedian() {
            int maxSize = this.maxQueue.size();
            int minSize = this.minQueue.size();
            // 无数
            if (maxSize + minSize == 0) {
                return null;
            }
            Integer maxHead = this.maxQueue.peek();
            Integer minHead = this.minQueue.peek();
            // 总数为偶数,中位数是中间2个数之和的一半
            // 条件为maxSize == minSize也行
            if (((maxSize + minSize) & 1) == 0) {
                return (maxHead + minHead) / 2;
            }
            return maxSize > minSize ? maxHead :minHead;
        }
    }

    /**
     * 小根堆
     */
    public static class MinHeapComparator implements Comparator<Integer> {

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

    /**
     * 大根堆
     */
    public static class MaxHeapComparator implements Comparator<Integer> {

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

    public static void main(String[] args) {
        // 前缀树
        Trie trie = new Trie();
        System.out.println(trie.search("zuo"));
        trie.insert("zuo");
        System.out.println(trie.search("zuo"));
        trie.delete("zuo");
        System.out.println(trie.search("zuo"));
        trie.insert("zuo");
        trie.insert("zuo");
        trie.delete("zuo");
        System.out.println(trie.search("zuo"));
        trie.delete("zuo");
        System.out.println(trie.search("zuo"));
        trie.insert("zuoa");
        trie.insert("zuoac");
        trie.insert("zuoab");
        trie.insert("zuoad");
        trie.delete("zuoa");
        System.out.println(trie.search("zuoa"));
        System.out.println(trie.prefixNumber("zuo"));

        // 字符串拼接最小字典序
        String[] strs1 = { "jibw", "ji", "jp", "bw", "jibw" };
        System.out.println(lowestString(strs1));

        String[] strs2 = { "ba", "b" };
        System.out.println(lowestString(strs2));

        // 切金条问题
        int[] arr = { 6, 7, 8, 9 };
        System.out.println(lessMoney(arr));
    }
}

 

posted @ 2022-03-12 13:43  北漂的尘埃  阅读(42)  评论(0编辑  收藏  举报