题目一
实现字典树
Trie树,又称为字典树、单词查找树或者前缀树,是一种用于快速检索的多叉数结构。例如,英文字母的字典树是26叉数,数字的字典树是10叉树。
Trie树的基本性质有三点,归纳为:
- 根节点不包含字符,根节点外每一个节点都只包含一个字符。
- 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
每个节点的所有子节点包含的字符串不相同。
定义Trie树节点
class TrieNode {
boolean isWord;
HashMap<Character, TrieNode> nexts;
public TrieNode() {
nexts = new HashMap<Character, TrieNode>();
}
}
添加操作
我们向Trie树中添加一个字符串word,具体步骤如下:
// Inserts a word into the trie.
public void insert(String word) {
char[] s = word.toCharArray();
TrieNode p = root;
int i = 0, n = s.length;
// traverse existing
while (i < n) {
TrieNode next = p.nexts.get(s[i]);
if (next != null) {
p = next;
i ++;
} else {
break;
}
}
// append new nodes
while (i < n) {
TrieNode newTrie = new TrieNode();
p.nexts.put(s[i], newTrie);
p = newTrie;
i ++;
}
// set word end
p.isWord = true;
}
//查询word是否在Trie树中
// Returns if the word is in the trie.
public boolean search(String word) {
TrieNode p = root;
for (int i = 0; i < word.length(); i ++) {
TrieNode child = p.nexts.get(word.charAt(i));
if (child == null) {
return false;
}
p = child;
}
return p.isWord;
}
// Returns if there is any word in the trie
// that starts with the given prefix.
public boolean startsWith(String prefix) {
TrieNode p = root;
for (int i = 0; i < prefix.length(); i ++) {
TrieNode child = p.nexts.get(prefix.charAt(i));
if (child == null) {
return false;
}
p = child;
}
return true;
}
//AC完整代码
import java.util.HashMap;
class TrieNode {
boolean isWord;
HashMap<Character, TrieNode> nexts;
public TrieNode() {
nexts = new HashMap<Character, TrieNode>();
}
}
public class Trie {
private TrieNode root;
public Trie() {
root = new TrieNode();
}
// Inserts a word into the trie.
public void insert(String word) {
char[] s = word.toCharArray();
TrieNode p = root;
int i = 0, n = s.length;
// traverse existing
while (i < n) {
TrieNode next = p.nexts.get(s[i]);
if (next != null) {
p = next;
i ++;
} else {
break;
}
}
// append new nodes
while (i < n) {
TrieNode newTrie = new TrieNode();
p.nexts.put(s[i], newTrie);
p = newTrie;
i ++;
}
// set word end
p.isWord = true;
}
// Returns if the word is in the trie.
public boolean search(String word) {
TrieNode p = root;
for (int i = 0; i < word.length(); i ++) {
TrieNode child = p.nexts.get(word.charAt(i));
if (child == null) {
return false;
}
p = child;
}
return p.isWord;
}
// Returns if there is any word in the trie
// that starts with the given prefix.
public boolean startsWith(String prefix) {
TrieNode p = root;
for (int i = 0; i < prefix.length(); i ++) {
TrieNode child = p.nexts.get(prefix.charAt(i));
if (child == null) {
return false;
}
p = child;
}
return true;
}
public static void main(String[] args) {
Trie trie = new Trie();
trie.insert("keydsdsds");
System.out.println(trie.startsWith("key"));
}
}
题目二
一块金条切成两半,是需要花费和长度数值一样的铜板的。比如长度为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铜板。
输入一个数组,返回分割的最小代价。
这其实是一个哈夫曼树,只要把10,20,30组成一颗哈夫曼树就ok。
建立图中的哈夫曼树,可以使用java中的优先队列,首先建立一个小顶堆{10,20,30}。然后建立一个大顶堆{60,30,30,10,20}只要返回大顶堆的头就可以。
public static int lessMoney(int[] arr) {
PriorityQueue<Integer> minQ2 = new PriorityQueue<>(10,new MinheapComparator());
PriorityQueue<Integer> maxQ = new PriorityQueue<>(10,new MaxheapComparator());
for (int i = 0; i < arr.length; i++) {
minQ2.add(arr[i]);
}
while (!minQ2.isEmpty()) {
int first=minQ2.poll();
if (!minQ2.isEmpty()) {
int second=minQ2.poll();
int sum=first+second;
maxQ.add(first);
maxQ.add(second);
minQ2.add(sum);
}else {
maxQ.add(first);
}
}
return maxQ.peek();
}
public static class MinheapComparator implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2; // < 0 o1 < o2 负数
}
}
public static class MaxheapComparator implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1; // < o2 < o1
}
}
第三题
输入: 参数1:正数数组costs, 参数2:正数数组profits, 参数3:正数k, 参数4:正数m
costs[i]:表示i号项目的花费
profits[i]:表示i号项目在扣除花费之后还能挣到的钱(利润)
k:表示你不能并行、只能串行的最多做k个项目 m表示你初始的资金
说明:你每做完一个项目,马上获得的收益,可以支持你去做下一个项目。
输出: 你最后获得的最大钱数
使用cost数组的值建立一个小根堆,将cost堆中小于m的值弹出,将弹出的值以profits[i]建立一个大根堆。在将profits中的头弹出,接着m加上获得的利润。循环执行。
package lesson7;
import java.util.Comparator;
import java.util.PriorityQueue;
public class Code_03_IPO {
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<>(11,new MinCostComparator());
PriorityQueue<Node> maxProfitQ = new PriorityQueue<>(11,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;
}
}
题目四
中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
示例:
addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3)
findMedian() -> 2
使用两个堆,一个最大堆和一个最小堆。数组中较小部分放在最大堆中,数组中比较大的部分放在最小堆中。
package lesson7;
import java.util.Comparator;
import java.util.PriorityQueue;
public class MedianFinder {
private PriorityQueue<Integer> min;
private PriorityQueue<Integer> max;
/** initialize your data structure here. */
public MedianFinder() {
min=new PriorityQueue<>(11, new MinheapComparator());
max=new PriorityQueue<>(11,new MaxheapComparator());
}
public void addNum(int num) {
if ((min.size()==0)&&(max.size()==0)) {max.add(num); return;}
if (max.size()<=min.size()) {
if (num>min.peek()) {
int tmp=min.poll();
min.add(num);
max.add(tmp);
}else {
max.add(num);
}
}else{
if (num<max.peek()) {
int tmp=max.poll();
max.add(num);
min.add(tmp);
}else {
min.add(num);
}
}
}
public double findMedian() {
if ((min.size()==0)&&(max.size()==0)) return 0.0;
if (max.size()==min.size()) {
return ((double)(max.peek()+min.peek()))/2.0;
}else{
return (double)max.peek();
}
}
public static class MinheapComparator implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2; // < 0 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) {
MedianFinder medianFinder=new MedianFinder();
medianFinder.addNum(1);
medianFinder.addNum(2);
medianFinder.addNum(3);
System.out.println(medianFinder.findMedian());
}
}
题目五
给定一个字符串类型的数组strs,请找到一种拼接顺序,使得将所有的字符串拼接起来组成的大字符串是所有可能性中字典顺序最小的,并返回这个大字符串。
【举例】
strs = [“abc”, “de”],可以拼接成 “abcde”,也可以拼接成 “deabc”,但前者的字典顺序更小,所以返回 “abcde”
strs = [“b”, “ba”],可以拼成 “bba”,也可以拼成 “bab”,但前者的字典顺序更小,所以返回 “bab”。
【基本思路】
有一种思路为:先把strs中的字符串按照字典顺序排序,然后将串起来的结果返回。这么做是错误的,例如【举例】中的第二条,按照字典顺序排应该是b、ba,串起来的结果是bba,但是正确答案是bab。所以这个思路行不通。正确的排序方式如下:
假设两个字符分别是a,b。a和b拼起来的字符串表示为a.b,那么如果a.b的字典顺序小于b.a,就把a放在前面,否则把b放在前面。每两个字符之间都按照这个标准进行比较,以此标准排序后,最后串起来的结果就是正确答案。证明太复杂,省略。
package class_07;
import java.util.Arrays;
import java.util.Comparator;
public class Code_05_LowestLexicography {
public static class MyComparator implements Comparator<String> {
@Override
public int compare(String a, String b) {
return (a + b).compareTo(b + a);
}
}
public static String lowestString(String[] strs) {
if (strs == null || strs.length == 0) {
return "";
}
Arrays.sort(strs, new MyComparator());
String res = "";
for (int i = 0; i < strs.length; i++) {
res += strs[i];
}
return res;
}
public static void main(String[] args) {
String[] strs1 = { "jibw", "ji", "jp", "bw", "jibw" };
System.out.println(lowestString(strs1));
String[] strs2 = { "ba", "b" };
System.out.println(lowestString(strs2));
}
}
题目六
一些项目要占用一个会议室宣讲,会议室不能同时容纳两个项目的宣讲。 给你每一个项目开始的时间和结束的时间(给你一个数组,里面 是一个个具体的项目),你来安排宣讲的日程,要求会议室进行 的宣讲的场次最多。返回这个最多的宣讲场次。
import java.util.Arrays;
import java.util.Comparator;
public class C05_BestArrange {
public static class Node{
public int start;
public int end;
public Node(int start,int end){
this.start = start;
this.end = end;
}
}
public static int bestArray(Node[]node){
if(node==null || node.length==0){
return 0;
}
Arrays.sort(node,new endComparator());
for (int i = 0; i < node.length; i++) {
System.out.println(node[i].start+" "+node[i].end);
}
int res = 0;
int cur=node[0].start;
for (int i = 0; i < node.length; i++) {
if(cur<=node[i].start){
res++;
cur = node[i].end;
}
}
return res;
}
public static class endComparator implements Comparator<Node>{
@Override
public int compare(Node o1, Node o2) {
return o1.end-o2.end;
}
}
public static void main(String[] args) {
Node node[] = new Node[10];
node[0] = new Node(1, 3);
node[1] = new Node(0, 7);
node[2] = new Node(15, 20);
node[3] = new Node(3, 4);
node[4] = new Node(3, 8);
node[5] = new Node(5, 10);
node[6] = new Node(6, 12);
node[7] = new Node(4, 14);
node[8] = new Node(10, 15);
node[9] = new Node(15, 18);
System.out.println(bestArray(node));//5
}