1 题目理解
一个钟表有4个槽,每个槽可以停在0-9,10个状态。钟表每个槽的轮子可以转,例如可以从0转到9,也可以从0转到1。
钟表的起始状态是"0000"。每个数字代表一个槽的状态。
输入:字符串数组deadends,表示不能死亡状态,进入这个状态钟表就被锁住了,不能动了。输入字符串target表示想要达到的状态。
输出:到达最终状态的最少需要多少步。如果不能达到则为-1。
规则:每一步,钟表只能转动一个槽,只能转一下,
例子:
Input: deadends = [“0201”,“0101”,“0102”,“1212”,“2002”], target = “0202”
Output: 6
Explanation:
A sequence of valid moves would be “0000” -> “1000” -> “1100” -> “1200” -> “1201” -> “1202” -> “0202”.
Note that a sequence like “0000” -> “0001” -> “0002” -> “0102” -> “0202” would be invalid,
because the wheels of the lock become stuck after the display becomes the dead end “0102”.
2 BFS
要求最短路径长度,所以使用BFS。
在某一种状态下,要先判断这个状态能不能动。如果deadends包含target,则不能达到。
在正常状态下,某一状态的变化,可以变化其中一个槽,变化的动作可以是加1,也可以是减1。变化之后进入新的状态。
class Solution {
public int openLock(String[] deadends, String target) {
Set<String> set = new HashSet<String>();
for(String str : deadends){
set.add(str);
}
String start = "0000";
if(set.contains(target) || set.contains(start)) return -1;
Queue<String> queue = new LinkedList<String>();
queue.offer(start);
set.add(start);
int step = 0;
while(!queue.isEmpty()){
int size = queue.size();
//System.out.println(queue);
for(int r=0;r<size;r++){
if(target.equals(queue.peek())){
return step;
}
char[] current = queue.poll().toCharArray();
for(int i=0;i<4;i++){
char ch = current[i];
int v = current[i]-48;
current[i] = (char)(((v+1+10)%10)+48);
String newState = new String(current);
if(!set.contains(newState)){
queue.offer(newState);
set.add(newState);
}
current[i] = (char)(((v-1+10)%10)+48);
String newState2 = new String(current);
if(!set.contains(newState2)){
queue.offer(newState2);
set.add(newState2);
}
current[i] = ch;
}
}
step++;
}
return -1;
}
}
时间复杂度:可以选择的数据范围n=10,状态位数x=4, O ( 1 0 4 ∗ 4 ∗ 4 ) O(10^4*4*4) O(104∗4∗4)。最坏情况下有 1 0 4 10^4 104种状态。每个状态可以有4*2=8种变化,所以是O(x)。枚举后得到新的状态,构建字符串,需要O(x)时间。所以最终结果是: O ( n x ∗ x ∗ x ) O(n^x*x*x) O(nx∗x∗x)
3 用int构建状态
因为每个位置的值在0-9之间,可以用4位二进制表示,我们可以用int表示状态,这样速度方面会更快。
class Solution {
private final static int[] increments = new int[]{ 1, -1 };
public int openLock(String[] deadends, String target) {
int step = 0;
Set<Integer> used = new HashSet<Integer>();
String start = "0000";
int startInt = 0;
int targetInt = buildState(target);
for(String deadend : deadends){
used.add(buildState(deadend));
}
if(used.contains(startInt) || used.contains(targetInt)) return -1;
used.add(startInt);
Queue<Integer> queue = new LinkedList<Integer>();
queue.offer(startInt);
while(!queue.isEmpty()){
int size =queue.size();
for(int i=0;i<size;i++){
int nodeInt = queue.poll();
if(nodeInt==targetInt) return step;
for(int increment : increments){
for(int j =0;j<4;j++){
int newNode = updateState(nodeInt,j,increment);
if(!used.contains(newNode)){
used.add(newNode);
queue.offer(newNode);
}
}
}
}
step++;
}
return -1;
}
private int updateState(int state, int d, int inc) {
int mask = (1 << 4) - 1;
int[] num = new int[]{
state & mask,
(state >> 4) & mask,
(state >> 8) & mask,
(state >> 12) & mask
};
int n = num[d];
if (n == 0 && inc == -1) {
num[d] = 9;
} else if (n == 9 && inc == 1) {
num[d] = 0;
} else {
num[d] += inc;
}
int res = 0;
for (int i = 3; i >= 0; i--) {
res <<= 4;
res |= num[i];
}
return res;
}
private int buildState(String state) {
char[] c = state.toCharArray();
int res = 0;
for (int i = 0; i < c.length; i++) {
int d = c[i] - '0';
res <<= 4;
res |= d;
}
return res;
}
}