几道算法题
2016 Feb 21
See all posts
几道算法作业题,之前对算法题颇有怨念,总觉得有点浪费时间,有那么多更好玩的事情可以发掘。一分钟默写出KMP
算法又怎么样?自己琢磨出Boyer-Moore
算法才是真的厉害。
既然是不得不做的作业,真花时间做出来了其实还挺有意思,基本的算法素养(常见的算法,复杂度,势分析)还是应该有的,记录一下免得忘
1. 无限循环小数循环节计算
输入:正整数 a 和 b
(只需要处理32位或64位整数即可,不需要高精度计算)
输出:a/b的除法结果。循环节用大括号包围起来。
举例:
输入 10/5 输出 2
输入 1/4 输出 0.25
输入 1/3 输出 0.{3}
public class Fraction2DecimalAlgo {
/**
* 保存每一次除法的余数,当出现余数重复时,就发现了循环节
* @param N 分子
* @param D 分母
* @return 小数形式, 循环节用括号包围
*/
public static String fraction2decimal(int N, int D) {
HashMap<Integer, Integer> remainder = new HashMap<Integer, Integer>();
StringBuilder sb = new StringBuilder();
sb.append(N / D);
sb.append('.');
N = N % D;
int i = 0; //计数器
while (remainder.get(N) == null) { // a.发现循环节终止循环 b.余数为0就是非循环的小数,也终止循环
remainder.put(N, i++);
N = N * 10;
sb.append(N / D);
N = N % D;
if (N == 0)
break;
}
if (N != 0) { //对于循环小数,当前计数器位置 - 循环节开始的地方,插入括号
int len = i - remainder.get(N);
sb.insert(sb.length() - len, '(');
sb.append(')');
}
return sb.toString();
}
public static void main(String[] args) {
System.out.println(fraction2decimal(1, 7));
System.out.println(fraction2decimal(1, 2));
}
}
2.蛋疼的联合国电梯
3名反恐士兵(counter-terrorists)将3名恐怖分子(terrorists)从电梯底层押送至电梯顶层,
押送过程中电梯最多只能容纳2人,最少1人;
试问有多少种走法,并给出过程
/**
* 蛋疼的联合国电梯
* M = 3 时候
* 初始态 (3, 3) <==> (0, 0)
* 结果态 (0, 0) <==> (3, 3)
* 每次可以 {{0, 2}, {1, 1}, {2, 0}, {0, 1}, {1, 0}}, 且往返交替
* 求所有可能的走法
*
*/
public class BackTrackAlgo {
private final int M;
private boolean[][] m;
private boolean turn; // go true back false
private int[][] record; // 000000 - 111111 size = 2 << (M * 2)
private String[] action;
private boolean[] check;
private int pos = 1;
private static int c = 1; // 解法计数
public BackTrackAlgo(int M) {
this.M = M;
int px = M, py = M;
m = new boolean[M+1][M + 1];
int totalState = 2 << (M * 2);
record = new int[totalState][2];
check = new boolean[totalState];
action = new String[totalState];
for (int i = 0; i < M + 1; i++)
for (int j = 0; j < M + 1; j++) {
if (i == j || i == 0 || i == M) ;
else m[i][j] = true;
}
record[0][0] = record[0][1] = M;
check[0] = true;
search(px, py);
}
/*判断这一状态是否在前面已经出现过*/
boolean isRepeat(int x, int y, boolean turn) {
for (int i = 0; i < pos; i++)
if (x == record[i][0] && y == record[i][1] && turn == check[i])
return true;
return false;
}
void search(int px, int py) {
int[][] goStep = {{0, 2}, {1, 1}, {2, 0}, {0, 1}, {1, 0}}; //每次可能有5种走法
String [] desc = {"T>T>", "T>C>", "C>C>", "T>", "C>"};
int i;
if (px == 0 && py == 0) {
System.out.println(String.format("method %d:", c++));
for (i = 0; i < pos; i++) {
String str = String.format("(%d, %d) <==> (%d, %d) action:%s", record[i][0], record[i][1],
M - record[i][0], M - record[i][1], action[i]);
System.out.println(str);
}
} else if (!turn) {
for (i = 0; i < 5; i++) {
px -= goStep[i][0];
py -= goStep[i][1];
if (px < 0 || px > 3 || py < 0 || py > 3 || m[px][py] || isRepeat(px, py, turn)) {
px += goStep[i][0];
py += goStep[i][1];
continue;
}
record[pos][0] = px;
record[pos][1] = py;
action[pos] = desc[i];
check[pos] = turn;
pos++;
turn = !turn;
search(px, py);
pos--; //回溯
turn = !turn;
px += goStep[i][0];
py += goStep[i][1];
}
} else {
for (i = 0; i < 5; i++) {
px += goStep[i][0];
py += goStep[i][1];
if (px < 0 || px > 3 || py < 0 || py > 3 || m[px][py] || isRepeat(px, py, turn)) {
px -= goStep[i][0];
py -= goStep[i][1];
continue;
}
record[pos][0] = px;
record[pos][1] = py;
action[pos] = desc[i].replaceAll(">", "<");
check[pos] = turn;
pos++;
turn = !turn;
search(px, py);
pos--; //回溯
turn = !turn;
px -= goStep[i][0];
py -= goStep[i][1];
}
}
}
public static void main(String[] args) {
new BackTrackAlgo(3);
}
}
同事的解法更简洁好懂:
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class UnionElevator {
static class State implements Cloneable {
int BT, BC; //bottom terrorists, bottom counter-terrorists
int TT, TC; //top ...
boolean EB; //is elevator at bottom?
public String toString() {
return "T" + BT + "C" + BC + ":" + "T" + TT + "C" + TC + ":" + (EB ? "B" : "T");
}
public State clone() {
State s = new State();
s.BT = BT;
s.BC = BC;
s.TT = TT;
s.TC = TC;
s.EB = EB;
return s;
}
}
Set<String> states = new HashSet<>();
List<String> steps = new ArrayList<>();
void doStep(State s, String step, int dBT, int dBC) {
State next;
next = s.clone();
next.BT += dBT;
next.BC += dBC;
next.TT -= dBT;
next.TC -= dBC;
if(next.BT >= 0 && next.BC >= 0 && next.TT >= 0 && next.TC >= 0) {
if((next.BT <= next.BC || next.BC == 0) && (next.TT <= next.TC || next.TC == 0)) {
String nextStateStr = next.toString();
//如果满足约束条件,而且这个状态之前没出现过,那么纪录下来并从这个状态继续穷举所有可能性
if(!states.contains(nextStateStr)) {
states.add(nextStateStr);
steps.add(step);
act(next);
steps.remove(steps.size() - 1);
states.remove(nextStateStr);
}
}
}
}
void act(State s) {
if(s.TT == 3 && s.TC == 3) {
System.out.println("----");
for (String step : steps) {
System.out.println(step);
}
}
else {
if(s.EB) {
//move up
s.EB = false;
doStep(s, "T> T>", -2, 0);
doStep(s, "C> C>", 0, -2);
doStep(s, "T> C>", -1, -1);
doStep(s, "T> ", -1, 0);
doStep(s, " C>", 0, -1);
}
else {
//move down
s.EB = true;
doStep(s, "T< T<", +2, 0);
doStep(s, "C< C<", 0, +2);
doStep(s, "T< C<", +1, +1);
doStep(s, "T< ", +1, 0);
doStep(s, " C<", 0, +1);
}
}
}
void solve() {
State s = new State();
s.BT = 3;
s.BC = 3;
s.EB = true;
states.add(s.toString());
act(s);
}
public static void main(String[] args) {
new UnionElevator().solve();
}
}
3.关键字检索
比如我们有很多句子: "hello world", "nice to meet you", "how are you",
"help yourself"
老师只需要输入其中某个单词的前缀,我们要快速找到含有这个单词的句子。
举例:
输入 "h" 找到 "hello world", "how are you", "help yourself"
输入 "how" 找到 "how are you"
输入 "you" 找到 "how are you",
"help yourself"
输入 "yourself" 找到 "help yourself"
为了简化这个问题,我们假设所有句子都是由小写字母和空格组成,不含其它字符。
要求:请实现一个trie树和倒排文档,实现以上搜索功能。
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 关键字检索, Trie树来支持前缀查找, 倒排索引来支持全词匹配
*/
public class InvertedIndexAlgo {
public class Trie {
private boolean isLeaf;
private HashMap<Character, Trie> map;
public Trie() {
map = new HashMap<Character, Trie>();
isLeaf = false;
}
public void put(String s) {
if (s.length() == 0) {
isLeaf = true;
return;
}
if (!map.containsKey(s.charAt(0))) {
map.put(s.charAt(0), new Trie());
}
map.get(s.charAt(0)).put(s.substring(1));
}
public void travelTree(String perm) {
if (isLeaf) {
System.out.println(perm);
}
if (map.size() == 0) return;
for (Character c : map.keySet()) {
map.get(c).travelTree(perm + c);
}
}
public void travelTree(String perm, List<String> sl) {
if (isLeaf) {
sl.add(perm);
}
if (map.size() == 0) return;
for (Character c : map.keySet()) {
map.get(c).travelTree(perm + c, sl);
}
}
public List<String> getSuggestions(String q) {
List<String> suggestions = new ArrayList<>();
getSuggestions(q, "", suggestions);
return suggestions;
}
public void getSuggestions(String q, String perm, List<String> sl) {
if (q.length() == 0) {
travelTree(perm, sl);
}
if (map.size() == 0) return;
if (q.length() > 0 && map.containsKey(q.charAt(0))) {
map.get(q.charAt(0)).getSuggestions(q.substring(1), perm + q.charAt(0), sl);
}
}
// public static void main(String[] args) {
// Trie pt=new Trie();
// pt.put("hello");
// pt.put("hell");
// pt.put("how");
// //pt.travelTree("");
// System.out.println(pt.getSuggestions("h"));
// System.out.println(pt.getSuggestions("he"));
// }
}
private final List<String> inputList;
private Map<String, Set<Integer>> map;
private Trie trie;
public InvertedIndexAlgo(List<String> inputList) {
this.inputList = inputList;
map = new HashMap<>();
}
public void build() {
for (int i = 0; i < inputList.size(); i++) {
String str = inputList.get(i);
String[] words = str.split("\\s");
for (String word : words) {
Set<Integer> value = map.getOrDefault(word, new HashSet<>());
value.add(i);
map.put(word, value);
}
}
trie = new Trie();
map.keySet().forEach(trie::put);
}
public List<String> search(String str) {
Set<Integer> indexSet = map.getOrDefault(str, new HashSet<>());
trie.getSuggestions(str).forEach(s -> {
Set<Integer> prefixIndexSet = map.getOrDefault(s, Collections.emptySet());
indexSet.addAll(prefixIndexSet);
});
return indexSet.stream().map(inputList::get).collect(Collectors.toList());
}
public static void main(String[] args) {
List<String> inputList = Stream.of("hello world",
"nice to meet you",
"how are you",
"help yourself"
).collect(Collectors.toList());
InvertedIndexAlgo invertedIndexAlgo = new InvertedIndexAlgo(inputList);
invertedIndexAlgo.build();
Stream.of("h", "hello", "you", "yourself").forEach(s -> {
System.out.println(String.format("search:[%s] results:", s));
invertedIndexAlgo.search(s).forEach(System.out::println);
System.out.println("------");
});
}
}
4.路径规划
最优路径的定义:从起点开始到所有可能的终点,节点权重之和最高的路径。
输入:n个节点,m个路径,起点
输出:最优路径的权重值之和
举例:
3个节点:
A 1
B 2
C 2
3条路径:
A->B
B->C
A->C
起点:
A
输出:5
(最优路径是 A->B->C , 权重之和是 1+2+2=5)
提示:如果输入的学习路径有环,请输出
-1。数据量较多请注意算法复杂度。
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
/**
* 规划学习路径
* topsort + dp
*/
public class DAGLongestPathAlgo {
public static void main(String[] args) {
int[][] m = new int[6][6];
m[0][1] = 3;
m[1][2] = 3;
// m[2][0] = 3; //加一个环
m[2][4] = 3;
m[4][5] = 3;
m[3][4] = 3;
m[0][3] = 3;
m[3][5] = 3;
int[] dp_max = new int[6];
Arrays.fill(dp_max, 0);
int[] dp_min = new int[6];
Arrays.fill(dp_min, Integer.MAX_VALUE);
int[] commingEdges = new int[6];
int edgesCount = 0;
Arrays.fill(commingEdges, 0);
for(int i = 0; i < m.length; i++){
for(int j = 0; j< m.length; j++){
if (m[i][j] != 0){
commingEdges[j]++;
edgesCount++;
}
}
}
boolean[] v = new boolean[m.length];
Queue<Integer> q = new LinkedList<Integer>();
for (int i = 0; i < commingEdges.length; i++)
if (commingEdges[i] == 0) {
q.add(i);
dp_max[i] = 0;
dp_min[i] = 0;
}
while (!q.isEmpty()) {
int i = q.poll();
if (v[i])
continue;
v[i] = true;
for (int j = 0; j < m.length; j++)
if (m[i][j] != 0) {
edgesCount--;
commingEdges[j]--;
if (commingEdges[j] == 0)
q.add(j);
dp_max[j] = Math.max(dp_max[j], dp_max[i] + m[i][j]);
dp_min[j] = Math.min(dp_min[j], dp_min[i] + m[i][j]);
}
}
if (edgesCount > 0) {
System.out.println("found circle! -1");
} else
System.out.println(dp_max[5] + " " + dp_min[5]);
}
}
几道算法题
2016 Feb 21 See all posts几道算法作业题,之前对算法题颇有怨念,总觉得有点浪费时间,有那么多更好玩的事情可以发掘。一分钟默写出
KMP
算法又怎么样?自己琢磨出Boyer-Moore
算法才是真的厉害。 既然是不得不做的作业,真花时间做出来了其实还挺有意思,基本的算法素养(常见的算法,复杂度,势分析)还是应该有的,记录一下免得忘1. 无限循环小数循环节计算
2.蛋疼的联合国电梯
同事的解法更简洁好懂:
3.关键字检索
4.路径规划