先看题目:
你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' 。
每个拨轮可以自由旋转:例如把 '9' 变为 '0','0' 变为 '9' 。每次旋转都只能旋转一个拨轮的一位数字。
锁的初始数字为 '0000' ,一个代表四个拨轮的数字的字符串。
列表 deadends 包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。
字符串 target 代表可以解锁的数字,你需要给出最小的旋转次数,如果无论如何不能解锁,返回 -1。
例子1:
输入:deadends = ["0201","0101","0102","1212","2002"], target = "0202"
输出:6
解释:
可能的移动序列为 "0000" -> "1000" -> "1100" -> "1200" -> "1201" -> "1202" -> "0202"。
注意 "0000" -> "0001" -> "0002" -> "0102" -> "0202" 这样的序列是不能解锁的,
因为当拨动到 "0102" 时这个锁就会被锁定。
例子2:
输入: deadends = ["8888"], target = "0009"
输出:1
解释:
把最后一位反向旋转一次即可 "0000" -> "0009"。
在做队列的专项的时候遇到,思考了20分钟左右无任何方案,搜了B站花花酱的视频讲解才搞懂(https://www.bilibili.com/video/av31636797?t=385)。
这道题算是典型的无向图问题,每个数字表示图中一个节点,"0000"表示根节点,每个节点由四位数组成,其中每个数每次转动存在两种变形,所以一次转动,节点有八个子节点,如下图:
将待遍历的节点添加到队列中,遍历完成,则将节点添加到死节点数组中。从图中可看出,第一次遍历有八种可能,如果某个节点和目标节点相等,则返回当前深度即可,不等的话,如果没有访问过它或者死节点数组中没有它,即我们需要去遍历它的子节点,这时候将它加入到队列里,以待访问它的子节点。同时将它加入到死节点数组中,防止后续重复访问该节点。因为遍历每一层的时候,都会更新队列,所以一开始就要知道每层遍历的数量。c语言不像其它高级语言,有现成的队列结构拿来使用,需要自己实现,此外,死节点数组使用了哈希数组,因为每个节点都是10000以内的一个数字,如果使用strcmp的话耗时可能更久。
下面是c代码的解法,只看最下面的核心函数即可,前面代码时关于队列和哈希表的,可略过:
/* BFS求解 */
#define MAX 10001
//循环队列
struct queue {
int head, end, length;
char a[10001][5];
};
//入队
int enQueue(struct queue *q, char *n)
{
if (q->length >= MAX - 1)
return -1;
else
{
strcpy(q->a[q->end], n);
q->end = (q->end + 1) % MAX;
q->length++;
return 0;
}
}
//出队
int deQueue(struct queue *q, char *n)
{
if (q->length == 0)
return -1;
else
{
strcpy(n, q->a[q->head]);
q->length--;
q->head = (q->head + 1) % MAX;
}
return 0;
}
//获取队列长度
int queueLength(struct queue *q)
{
return q->length;
}
//判断队列是否为空
int isQueueEmpty(struct queue *q)
{
if (q->length == 0)
return 1;
else
return 0;
}
//判断t是否在head中
int inDead(int *dead, char *t)
{
int i, v;
for (i = 0, v = 0; i < 4; i++)
v = v * 10 + (t[i] - '0');
if (dead[v] == 1)
return 1;
else
return 0;
}
//添加到哈希队列中
void enDead(int *dead, char *t)
{
int i, v;
for (i = 0, v = 0; i < 4; i++)
v = v * 10 + (t[i] - '0');
dead[v] = 1;
return ;
}
int openLock(char ** deadends, int deadendsSize, char * target){
int i,j,k, ret, len, steps = 0;
char start[] = "0000", n[5] = {0}, curr[5] = {0};
struct queue *q;
q = (struct queue *)malloc(sizeof(struct queue));
if (!q) exit(1);
q->length = q->head = q->end = 0;
int dead[10000] = {0};
for (i = 0; i < deadendsSize; i++)
enDead(dead, deadends[i]);
if (inDead(dead, start))
return -1;
enQueue(q, start);
while(!isQueueEmpty(q))
{
++steps;
len = queueLength(q);
for (k = 0; k < len; k++)
{
deQueue(q, curr);
for (i = 0; i < 4; i++)
{
for (j = -1; j < 2; j+=2)
{
strcpy(n, curr);
n[i] = (n[i] - '0' + j + 10)%10 + '0';
if (!strcmp(n, target)) return steps;
if (inDead(dead, n)) continue;
enQueue(q, n);
enDead(dead,n);
}
}
}
}
return -1;
}
参考资料:
1. https://www.bilibili.com/video/av31636797?t=385
=============================================================================================
Linux应用程序、内核、驱动开发交流讨论群(745510310),感兴趣的同学可以加群讨论、交流、资料查找等,前进的道路上,你不是一个人奥^_^。