【7】前缀树和贪心算法

目录

一.前缀树

二.贪心算法(一般只要求找到满意解)

三.贪心算法在笔试时的解题套路

四.分金条

五.最大钱数

六.一个数据流中,随时可以取得中位数(用堆实现)


一.前缀树

前缀树:

1. 初始化树节点:开始时有一个根节点,通常为空节点或者包含一个空字符的节点。

2. 插入单词:对于要插入的每个单词,从根节点开始:
   - 检查当前字符是否已经是节点的子节点。
   - 如果是,则移动到该子节点继续;如果不是,则创建一个新节点并添加到当前节点的子节点中。
   - 继续处理单词的下一个字符,直到单词的所有字符都插入到树中。

3.标记字符出现次数pass:在将单词所有字符插入树中的过程中,将路过的节点的pass++

4. 标记单词结尾end:在单词的最后一个字符的节点,将其end++,表示从根节点到当前节点形成了一个完整的单词的数量加1。

功能:

  1. 查询一共有多少个字符串。 => 看根节点的pass
  2. 查询某个字符串是否出现,或者有多少个。=> 直接看该字符串最后一个字符下边的节点的end
  3. 查询以某个前缀字符串作为前缀的字符串有多少 => 利用前缀字符串的最后一个字符下的节点的pass可以只知晓。如果到不了前缀字符串最后一个字符下的节点,那么说明不存在,返回0.

Nodej节点:

  1. path:在某条路径上,经过当前字符的次数
  2. end:在某条路径上,以当前字符结尾的单词数量
  3. nexts[]:路径a-z => nexts[0-25]

函数:

  1. insert(String word):向前缀树中插入单词word
  2. delete(String word):删除单词word
  3. search(String word):查询单词word
  4. prefixNumber(String word):查询以word为前缀的单词数量
	public static class TrieNode {
		public int path;
		public int end;
		public TrieNode[] nexts;

		public TrieNode() {
			path = 0;
			end = 0;
			nexts = new TrieNode[26];
		}
	}

	public static class Trie {
		private TrieNode root;

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

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

		public void delete(String word) {
			if (search(word) != 0) {
				char[] chs = word.toCharArray();
				TrieNode node = root;
				int index = 0;
				for (int i = 0; i < chs.length; i++) {
					index = chs[i] - 'a';
					if (--node.nexts[index].path == 0) {
						node.nexts[index] = null;
						return;
					}
					node = node.nexts[index];
				}
				node.end--;
			}
		}

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

		public int prefixNumber(String pre) {
			if (pre == null) {
				return 0;
			}
			char[] chs = pre.toCharArray();
			TrieNode node = root;
			int index = 0;
			for (int i = 0; i < chs.length; i++) {
				index = chs[i] - 'a';
				if (node.nexts[index] == null) {
					return 0;
				}
				node = node.nexts[index];
			}
			return node.path;
		}
	}

	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"));

	}

二.贪心算法(一般只要求找到满意解)

在某一个标准下,优先考虑最满足标准的样本,最后考虑最不满足标准的样本,最终的得到一个答案的算法,叫做贪心算法。

也就是说,不从整体最优上加以考虑,所作出的是在某种意义上的局部最优解。

局部最优不一定是整体最优。

问题

一些项目要占用一个会议室宣讲,会议室不能 同时容纳两个项目的宣讲。给你一个项目开始的时间和结束的时间(给你一个数组,里面是一个个具体项目),你来安排宣讲的日程,要求会议室进行的宣讲的场次最多,返回这个最多的宣讲场次。

哪一个会议结束时间早就先安排谁。

  1. 先安排结束时间最早的会议
  2. 然后把不能安排的会议删除
  3. 重复1-2直至得到最优解
	public static class Program{
		int start;
		int end;
		public Program(int a, int b) {
			start=a;end=b;
		}
	}
	
	public static class ProgramComparator implements Comparator<Program>{
		
		public int compare(Program o1, Program o2) {
			return o1.end-o2.end;
		}
		
	}
	
	public static int bestArrange(Program[] program, int timePoint) {
		if(program==null)return 0;
		int res=0;
		Arrays.sort(program, new ProgramComparator());
		for(int i=0;i<program.length;i++) {
			if(program[i].start>=timePoint) {
				timePoint=program[i].end;
				res++;
			}
		}
		return res;
	}

三.贪心算法在笔试时的解题套路

1.实现一个不依靠贪心策略的解法X,可以用最暴力的尝试

2.脑部处贪心策略A、贪心策略B、贪心策略C

3.用解法X和对数器,取验证每一个贪心策略,用实验的方式得知那个贪心策略正确

4.不要纠结贪心策略的证明

四.分金条

题目

一块金条切成两半,是需要花费和长度数值一样的铜板的。比如长度20的金条,不管切成长度多大的两半,都是花费20个铜板。

一群人想整分整块金条,怎么分最省铜板?

例如,给定数组[10, 20, 30],代表一共三人,整条金条长度为10+20+30=60。金条要分成10、20、30三部分。如果先把长度60的金条分成10、50,花费60;再把长度50的分为20和30,花费50;一共花费110铜板。但是如果先把长度60的金条分成30和30,花费60;再把长度30的金条分成10和20,花费30;一共花费90铜板。

输入一个数组,返回分割的最小代价。

	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;
	}

五.最大钱数

题目

这里需要维护一个小根堆和一个大根堆

小根堆是按照花费来排序的

大根堆是按照利润来排序的

每次将小根堆中<=当前资金的项目弹出到大根堆中,然后取大根堆中的利润最高的,周而复始......直至大根堆为空或者达到要求的项目数量 

	public static class Node {
		public int p;
		public int c;

		public Node(int p, int c) {
			this.p = p;
			this.c = c;
		}
	}

	public static class MinCostComparator implements Comparator<Node> {

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

	}

	public static class MaxProfitComparator implements Comparator<Node> {

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

	}

	public static int findMaximizedCapital(int k, int W, int[] Profits, int[] Capital) {
		Node[] nodes = new Node[Profits.length];
		for (int i = 0; i < Profits.length; i++) {
			nodes[i] = new Node(Profits[i], Capital[i]);
		}

		PriorityQueue<Node> minCostQ = new PriorityQueue<>(new MinCostComparator());
		PriorityQueue<Node> maxProfitQ = new PriorityQueue<>(new MaxProfitComparator());
		for (int i = 0; i < nodes.length; i++) {
			minCostQ.add(nodes[i]);
		}
		for (int i = 0; i < k; i++) {
			while (!minCostQ.isEmpty() && minCostQ.peek().c <= W) {
				maxProfitQ.add(minCostQ.poll());
			}
			if (maxProfitQ.isEmpty()) {
				return W;
			}
			W += maxProfitQ.poll().p;
		}
		return W;
	}

六.一个数据流中,随时可以取得中位数(用堆实现)

 

 步骤

  1. 看当前cur是否满足cur<=大根堆顶
  2. 若是,cur进入大根堆;若不是,cur进小根堆
  3. 如果|大根堆的size - 小根堆的size| == 2,那么就从size大的那个堆中取出堆顶元素,扔到size小的那个堆中。
public class Code06_MadianQuick {

	public static class MedianHolder {
		private PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(new MaxHeapComparator());
		private PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>(new MinHeapComparator());

		private void modifyTwoHeapsSize() {
			if (this.maxHeap.size() == this.minHeap.size() + 2) {
				this.minHeap.add(this.maxHeap.poll());
			}
			if (this.minHeap.size() == this.maxHeap.size() + 2) {
				this.maxHeap.add(this.minHeap.poll());
			}
		}

		public void addNumber(int num) {
			if (maxHeap.isEmpty() || num <= maxHeap.peek()) {
				maxHeap.add(num);
			} else {
				minHeap.add(num);
			}
			modifyTwoHeapsSize();
		}

		public Integer getMedian() {
			int maxHeapSize = this.maxHeap.size();
			int minHeapSize = this.minHeap.size();
			if (maxHeapSize + minHeapSize == 0) {
				return null;
			}
			Integer maxHeapHead = this.maxHeap.peek();
			Integer minHeapHead = this.minHeap.peek();
			if (((maxHeapSize + minHeapSize) & 1) == 0) {
				return (maxHeapHead + minHeapHead) / 2;
			}
			return maxHeapSize > minHeapSize ? maxHeapHead : minHeapHead;
		}

	}

	public static class MaxHeapComparator implements Comparator<Integer> {
		@Override
		public int compare(Integer o1, Integer o2) {
			if (o2 > o1) {
				return 1;
			} else {
				return -1;
			}
		}
	}

	public static class MinHeapComparator implements Comparator<Integer> {
		@Override
		public int compare(Integer o1, Integer o2) {
			if (o2 < o1) {
				return 1;
			} else {
				return -1;
			}
		}
	}

}

  • 10
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

看未来捏

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

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

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

打赏作者

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

抵扣说明:

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

余额充值