leetcode 752. 打开转盘锁 c代码

先看题目:

你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有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),感兴趣的同学可以加群讨论、交流、资料查找等,前进的道路上,你不是一个人奥^_^。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值