代码随想录算法训练营第五十三天 | 110.字符串接龙 105.有向图的完全可达性 106.岛屿的周长

110.字符串接龙:

文章链接
题目链接:110.字符串接龙

思路

首先分析题目,要求是将beginStr转换为endStr,每步只能转换一个字符,且转换后的字符要在字典strList中,最后求最短转换序列长度。
beginStr、endStr和strList中的字符串可以构成一张无向图,判断节点是否连接的依据是,两个节点之间是否只相差一个字符
示例如下图
在这里插入图片描述
那么应当采用深搜,需要保存路径长度,同时因为是无向图,所以需要visited判断strList中的节点是否遍历过。
因为会出现endStr不在字典中的情况,因此队列中的操作,应当是,先出队一个节点,再判断该节点与endStr是否存在连线,存在就打印路径长度;否则遍历字典中的字符串,当字符串没有遍历过且存在连线时标记,和入队列

from collections import deque
def judge(str1, str2):  # 判断str1和str2之间是否相差一个字符
    count = 0
    for i in range(len(str1)):
        if str1[i] != str2[i]:
            count += 1
    return (count == 1)
def main():
    n = int(input())
    beginStr, endStr = input().split()
    strList = []
    for _ in range(n):
        strList.append(input())
        
    if beginStr == endStr:  # 开始字符串等于结束字符串
        print(1)
        return
    
    visited = [False] * n   # 标记字典中的单词是否被访问过
    que = deque()
    que.append([beginStr, 1])   # 队列中记录[字符串, 序列中字符串数量]
    while len(que) != 0:
        curStr, num = que.popleft()
        if judge(curStr, endStr):   # 当前字符串与目标字符串是否差一步
            print(num + 1)
            return 
        for i in range(n):
            if not visited[i] and judge(curStr, strList[i]):
                visited[i] = True
                que.append([strList[i], num + 1])
    print(0)    # 不存在这样的序列
    return
if __name__ == '__main__':
    main()  

同时需要注意会出现beginStr == endStr和不存在转换序列的情况,都要进行处理


105.有向图的完全可达性:

文章链接
题目链接:105.有向图的完全可达性

思路

分析题目:给定一个有向图,判断从1节点能否到达任何节点,能打印1,不能打印-1。那么从1节点开始搜索整张图,使用visited记录遍历到的节点,最后通过visited判断是否有没有到达的节点

深搜

递归三部曲

  • 参数和返回值
    参数是图graph, visited数组, x当前节点,没有返回值
  • 临界条件
    dfs可以分为处理当前节点和处理下一个节点,如果是处理当前节点,那么需要将临界条件独立出来;如果是处理下一个节点,那么临界条件被包含在正常处理情况中
if visited[x]:	# 如果遍历过x
	return 
  • 正常递归
    • 处理当前节点:
      visited[x] = True for i in graph[x]: # 以邻接表为例 dfs(graph, visited, i)
    • 处理下一个节点:临界条件在,如果x连接的节点都已经被访问过了,那么会判断完之后会直接返回上一层
for i in graph[x]:	# 以邻接表为例
	if not visited[i]:	# 没有访问过
	dfs(graph, visited, i)

此处没有用到回溯,假设用到了回溯,那么就是visited[i] = False,但是我们要求的visited记录节点1能访问到的所有节点为True,那么就出错了,因此本题的dfs不要求回溯
代码

"""
dfs处理下一个节点
"""
from collections import defaultdict
 
def dfs(graph, visited, x):
    for i in graph[x]:
        if not visited[i]:
            visited[i] = True
            dfs(graph, visited, i)
 
def main():
    n, k = map(int, input().split())
    graph = defaultdict(list)
    # 邻接表
    for _ in range(k):
        s, t = map(int, input().split())
        graph[s].append(t)
     
    visited = [False] * (n + 1) # 节点编号从1开始
    visited[1] = True
    dfs(graph, visited, 1)
    # 查看是否有不能到达的节点
    for i in range(2, n + 1):
        if visited[i] == False:
            print(-1)
            return
    print(1) # 可以到达
 
if __name__ == '__main__':
    main()

"""
dfs处理当前节点
"""
from collections import defaultdict
 
def dfs(graph, visited, x):
    if visited[x]:
        return
    visited[x] = True   # 处理当前节点
    for i in graph[x]:
        dfs(graph, visited, i)
 
def main():
    n, k = map(int, input().split())
    graph = defaultdict(list)
    # 邻接表
    for _ in range(k):
        s, t = map(int, input().split())
        graph[s].append(t)
     
    visited = [False] * (n + 1) # 节点编号从1开始
    #visited[1] = True
    dfs(graph, visited, 1)
    # 查看是否有不能到达的节点
    for i in range(2, n + 1):
        if visited[i] == False:
            print(-1)
            return
    print(1) # 可以到达
 
if __name__ == '__main__':
    main()
广搜

广搜代码如下

from collections import defaultdict
from collections import deque

def bfs(graph, visited, x):
    #visited[x] = True   # 入队前设置为True
    que = deque()
    que.append(x)
    while len(que) != 0:
        curx = que.popleft()
        for i in graph[curx]:
            if not visited[i]:  # 将没有访问过的节点设置为True和入队
                visited[i] = True
                que.append(i)
    

def main():
    n, k = map(int, input().split())
    graph = defaultdict(list)
    # 邻接表
    for _ in range(k):
        s, t = map(int, input().split())
        graph[s].append(t)
    
    visited = [False] * (n + 1) # 节点编号从1开始
    visited[1] = True
    bfs(graph, visited, 1)
    # 查看是否有不能到达的节点
    for i in range(2, n + 1):
        if visited[i] == False:
            print(-1)
            return
    print(1) # 可以到达

if __name__ == '__main__':
    main()

106.岛屿的周长:

文章链接
题目链接:106.岛屿的周长

思路

首先分析题目,矩阵中只有一个岛屿,且岛屿的周长不是临水的陆地数,而是临水的边数之和。
实际分析下来不需要用到深搜和广搜。

思路1

遍历矩阵,当遇到陆地单元格时,计算其面对水的边数。即求其上下左右的坐标,如果是水或者超出边界,就计入岛屿的周长中

direction = [[1, 0], [0, 1], [-1, 0], [0, -1]]
def curPerimeter(grid, x, y):   # 求当前单元格被水围的周长
    count = 0
    for i in range(4):
        nextx, nexty = x + direction[i][0], y + direction[i][1]
        if nextx < 0 or nextx >= len(grid) or nexty < 0 or nexty >= len(grid[0]):
            count += 1
            continue
        if grid[nextx][nexty] == 0: # 周围是水
            count += 1
    return count
     
 
def main():
    n, m = map(int, input().split())
    grid = []
    for _ in range(n):
        grid.append(list(map(int, input().split())))
     
    perimeter = 0
    for x in range(n):
        for y in range(m):
            if grid[x][y]:  # 记录陆地临水的周长
                perimeter += curPerimeter(grid, x, y)
    print(perimeter)
     
if __name__ == '__main__':
    main()
思路2

首先得到岛屿中的陆地数,陆地数4 - 相邻陆地的边数2
因为如果有一对陆地相邻,那么就有两个单元格就有重复的边被包括在了岛屿中,那么边数就要 - 2。
比如说下图中红色的线是一个示例
在这里插入图片描述

因此最终岛屿的周长 = 陆地数 * 4 - 相邻陆地数 * 2

direction = [[1, 0], [0, 1], [-1, 0], [0, -1]]

def main():
    n, m = map(int, input().split())
    grid = []
    for _ in range(n):
        grid.append(list(map(int, input().split())))
    
    landNum, cover = 0, 0   # 记录陆地数和相邻陆地的边数
    for x in range(n):
        for y in range(m):
            if grid[x][y]:  
                landNum += 1
                # 统计上边相邻的陆地的边数
                if x - 1 >= 0 and grid[x - 1][y]:
                    cover += 1
                # 统计左边的相邻的陆地的边数
                if y - 1 >= 0 and grid[x][y - 1]:
                    cover += 1
                # 不统计下边和右边的,避免重复,因为后面遍历的时候会计算上边和左边的,如果当前算下和右,相邻的边数就重复了
                
    print(landNum * 4 - cover * 2)
    
if __name__ == '__main__':
    main()

学习收获:

  • 字符串接龙:要先将beginStr、endStr和strList中的字符串联合成无向图,理解是求无向图中从某个节点到另一个节点的最短路径。但是不能将其转换为图,只是通过字符串之间相差的字符数来判断是否存在边。使用广搜。需要记录访问过的节点和路径长度
  • 有向图的完全可达性:就是节点1能否到达任意节点,注意是有向图,使用任意遍历方式均可(深搜/广搜)。以及深搜注意没有用到回溯
  • 岛屿的周长:两种方式,没有用到深搜 / 广搜。两种方式,要么直接计算临水的边数;要么计算岛屿内相邻的边数,然后再进行处理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值