求大佬帮忙看看这道题目的代码的思路

weixin_45906870 2020-08-18 01:10:14
打开转盘锁
你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有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"。

示例 3:

输入: deadends = ["8887","8889","8878","8898","8788","8988","7888","9888"], target = "8888"
输出:-1
解释:
无法旋转到目标数字且不被锁定。

示例 4:

输入: deadends = ["0000"], target = "8888"
输出:-1
 

提示:

1.死亡列表 deadends 的长度范围为 [1, 500]。
2.目标数字 target 不会在 deadends 之中。
3.每个 deadends 和 target 中的字符串的数字会在 10,000 个可能的情况 '0000' 到 '9999' 中产生。


代码:
#define WORD_LEN 4 //这两个宏我看懂了
#define MAX_CHAR_LEN 10000

// 向上旋转 9->0
char* moveUp(char* str, int pos) //这里作者注释了是向上旋转9->0,但是我不是很明白这个函数中的具体操作。这个函数中的两个参数又是什么意思?
{
char* tempStr = (char *)malloc(sizeof(char) * (WORD_LEN + 1)); //这里为什么要加1?
if (tempStr == NULL) { return NULL; }

strncpy(tempStr, str, strlen(str) + 1); //这里为什么要用到strncpy函数?第三个参数为什么要加一?
tempStr[pos] = (tempStr[pos] == '9') ? '0' : tempStr[pos] + 1;

return tempStr;
}

// 向下旋转 0->9
char* moveDown(char* str, int pos) //这个函数和上面的函数看起来差不多,我也没搞懂
{
char* tempStr = (char *)malloc(sizeof(char) * (WORD_LEN + 1));
if (tempStr == NULL) { return NULL; }

strncpy(tempStr, str, WORD_LEN + 1);
tempStr[pos] = (tempStr[pos] == '0') ? '9' : tempStr[pos] - 1;

return tempStr;
}

int openLock(char ** deadends, int deadendsSize, char * target)
{
int index = 0; //这里从index到下面的visited数组,这些一大堆参数是什么鬼???
int step = 0; //从参数开始往下看完我的脑袋已经迷迷糊糊的了..............
int head = 0;
int tail = 0;
int leavel = 0;
char* tempStr;
char* tempStrUp;
char* tempStrDown;
char* dataQuene[MAX_CHAR_LEN] = {};
int visited[MAX_CHAR_LEN] = {0};

// 将deadends作为下标的visited置为1
for (int i = 0; i < deadendsSize; i++) { //这个for循环我看懂了
index = atoi(deadends[i]);
visited[index] = 1;
}

//如果头已经在死亡序列中,直接返回-1
if (visited[head] == 1) { //这个if块的意思是如果‘0000’在死亡序列中,就直接返回-1,对吗?如果我想的是对的,那直接用if (visited[0]==1)来判断不就好了吗?所以head参数是干嘛用的?
return -1;
}

dataQuene[tail++] = "0000"; // 将头入队列 //这里开始往下又不懂了,请从tail这个参数开始解释
visited[head] = 1; //这里怎么又赋值为1了?

while (head != tail) { //这个while循环没看懂,请从各个参数开始解释
leavel = tail - head;
for (int k = 0; k < leavel; k++) {
tempStr = dataQuene[head++];
if (strcmp(tempStr, target) == 0) {
return step;
}
for (int pos = 0; pos < WORD_LEN; pos++) { //这个for循环里的参数把我搞的迷迷糊糊的....
// 向上旋转
tempStrUp = moveUp(tempStr, pos);
index = atoi(tempStrUp);
if (visited[index] == 0) {
dataQuene[tail] = tempStrUp;
visited[index] = 1;
tail++;
}

// 向下旋转
tempStrDown = moveDown(tempStr, pos);
index = atoi(tempStrDown);
if (visited[index] == 0) {
dataQuene[tail] = tempStrDown;
visited[index] = 1;
tail++;
}
}
}
step++;
}

return -1;
}

最后能说一下代码的总体思路吗?
...全文
188 2 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
weixin_45906870 2020-08-19
  • 打赏
  • 举报
回复
有大佬答疑解惑吗?是不是我问的太难了
qybao 2020-08-19
  • 打赏
  • 举报
回复
呵呵,怎么又是你?
代码太长,就在代码用注释给你解释吧

#define WORD_LEN 4 //这两个宏看懂了就不解释了
#define MAX_CHAR_LEN 10000

// 向上旋转 9->0
char* moveUp(char* str, int pos) //第一个参数是传入的字符串,比如传入“1234”,第二个参数是旋转的位置,比如1,旋转后的结果为“1334”
{
char* tempStr = (char *)malloc(sizeof(char) * (WORD_LEN + 1)); //这里加1是因为还多了个字符串结束符'\n'
if (tempStr == NULL) { return NULL; }

strncpy(tempStr, str, strlen(str) + 1); //这里+1也是因为str也有个字符串结束符,其实用strcpy也一样,只是strncpy可以指定长度拷贝,相对来说比strcpy安全,也就是更能保证拷贝后数组不越界
tempStr[pos] = (tempStr[pos] == '9') ? '0' : tempStr[pos] + 1; //这里就不用解释了吧,如果是9,向上转就回到0,否则就是转到当前数字+1

return tempStr;
}

// 向下旋转 0->9
char* moveDown(char* str, int pos) //这个函数和上面的一样,只是向下转,比如“1234”,位置1,旋转的结果就是“1134”
{
char* tempStr = (char *)malloc(sizeof(char) * (WORD_LEN + 1));
if (tempStr == NULL) { return NULL; }

strncpy(tempStr, str, WORD_LEN + 1);
tempStr[pos] = (tempStr[pos] == '0') ? '9' : tempStr[pos] - 1;

return tempStr;
}

int openLock(char ** deadends, int deadendsSize, char * target)
{
int index = 0; //index是visited的下标,用于记录该下标是否出现过(或者叫访问过)
int step = 0; //step是记录找到开始结果为止所用的旋转步数
int head = 0; //head是记录dataQuene的当前旋转可能结果的下标
int tail = 0; //tail是记录dataQuene的最后一个旋转可能结果的下标
int leavel = 0; //记录从head到tail一共有多少转旋转可能结果
char* tempStr; //记录当前旋转结果
char* tempStrUp; //记录向上旋转的可能结果
char* tempStrDown; //记录向下旋转的可能结果
char* dataQuene[MAX_CHAR_LEN] = {}; //保存旋转可能的结果(也就是下一步向上或向下转是什么样的,这些可能的结果都保存下来挨个访问)
int visited[MAX_CHAR_LEN] = {0}; //记录某个旋转结果是否出现过,没出现则为0,出现过则为1

// 将deadends作为下标的visited置为1
for (int i = 0; i < deadendsSize; i++) { //这个for循环看懂了就不解释了,就是把死亡数字都记录为出现过(或访问过)
index = atoi(deadends[i]);
visited[index] = 1;
}

//如果头已经在死亡序列中,直接返回-1
if (visited[head] == 1) { //你的理解是对的,就是判断“0000”是否在死亡数字里,你改成用visited[0]也没问题
return -1;
}

dataQuene[tail++] = "0000"; // 将头入队列 //这里就是把头放在下一步旋转的可能结果队列里,因为都是从“0000”出发,所以无条件放入,同时最后一个旋转可能结果的位置+1(表示队列里多了一个旋转可能)
visited[head] = 1; //这里赋值为1,是表示“0000”出现过了,因为从“0000”出发,把它放入队列的同时也记录它出现过

while (head != tail) { //这个while循环表示dataQuene的旋转可能结果还没转完就继续循环,如果过head和tail一样,则说明所有可能旋转的结果都已经转完了
leavel = tail - head; //这里是表示从head到tail一共有多少种旋转可能结果
for (int k = 0; k < leavel; k++) { //然后依次循环这些旋转可能结果,判断里面是否有开锁数字
tempStr = dataQuene[head++]; //这里是获得当前的旋转结果
if (strcmp(tempStr, target) == 0) { //判断当前旋转结果是否和开锁数字一样,如果一样,则返回旋转步数
return step;
}
for (int pos = 0; pos < WORD_LEN; pos++) { //如果当前旋转结果和开锁数字不一样,则每个位置依次旋转,并把旋转可能的结果保留到dataQuene
// 向上旋转
tempStrUp = moveUp(tempStr, pos); //这里首先获得向上旋转的可能结果
index = atoi(tempStrUp);
if (visited[index] == 0) {//如果该旋转可能结果的数字还没出现过,则放到dataQuene里等待访问
dataQuene[tail] = tempStrUp;
visited[index] = 1; //并记录该数字为出现过(或访问过),也就是控制出现过的数字不要再重复访问了
tail++;
}

// 向下旋转
tempStrDown = moveDown(tempStr, pos); //其次获得向下旋转的可能结果
index = atoi(tempStrDown);
if (visited[index] == 0) { //如果该旋转可能结果的数字还没出现过,则放到dataQuene里等待访问
dataQuene[tail] = tempStrDown;
visited[index] = 1; //并记录该数字为出现过(或访问过),也就是控制出现过的数字不要再重复访问了
tail++;
}
}
}
step++; //如果找不到开锁结果,就累加旋转步数
}

return -1;
}



总体思路很简单
就是循环一次访问每种旋转可能的结果(其中访问过的不需要在访问),然后在当前的旋转结果的基础上,派生出下一次旋转的所有可能,继续遍历访问,知道找到开锁数字为止,如果所有的旋转可能都遍历访问完了,还是没找到开锁数字,那就返回-1
举个例子
首先访问 “0000”,然后在“0000”的基础上,派生出下一次旋转可能的结果,就是 for (int pos = 0; pos < WORD_LEN; pos++) { 这个循环,得到的结果为
向上转:“1000”,“0100”,“0010”,“0001”,向下转:“9000”,“0900”,“0090”,“0009”

然后继续访问 “1000”,从“1000”又派生出了下一次旋转的可能结果
向上转:“2000”,“1100”,“1010”,“1001”,向下转:“0000”(重复,去掉),“1900”,“1090”,“1009”

画一个图就清楚了,就是个网络图


就这样依次从当前的旋转结果去访问派生出的旋转可能结果,直到找到开锁数字



70,022

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧