LeetCode---搜索算法

2023-10-31

LeetCode---搜索算法

搜索算法

图Graph的概念

图是一种由节点和边构成的数据结构,实际上树也是一种特殊的图。图可以用来表示现实世界中的很多事物:道路交通系统、航班线路、互联网连接等等。

顶点Vertex (节点Nond):是图的基本组成部分,顶点具有名称标识key,也可以携带数据项payload。
边Edge(弧Arc):作为两个顶点之间关系的表示,边连接两个顶点;边可以是无向或有向的,相应的图称为“无向图”和“有向图”。
权重Weight:为了表达从一个顶点到另一个顶点的代价,可以给边赋权;例如公交网中的两个站点之间的“距离”、“通行时间”和“票价”都可以作为权重。

在这里插入图片描述
路径Path:图中的路径,是由边依次连接起来的顶点序列;无权路径的长度为边的数量;带权路径的长度为所有边权重的和;如下图的一条路径(V3,V4,V0,V1),其边为{(V3,V4,7),(V4,V0,1),(V0,V1,5)}。
在这里插入图片描述
圈Cycle:圈是首尾顶点相同的路径,如下图中(V5,V2,V3,V5V)是一个圈。如果有向图中不存在任何圈,则称为“有向无圈图”(directed acylic graph:DAG)。
在这里插入图片描述

图的抽象数据类型

在这里插入图片描述
在这里插入图片描述

邻接矩阵

优点:简单。
缺点:空间复杂度太高。
在这里插入图片描述
在这里插入图片描述

邻接列表

在这里插入图片描述
在这里插入图片描述

class Node(object):
    def __init__(self, key):
        self.key = key
        self.connected_to = {}

    # 添加一个边
    def add_nbr(self, nbr, weight=0):
        self.connected_to[nbr] = weight

    # 获取当前节点所有连接
    def get_connections(self):
        return self.connected_to.keys()

    # 获取当前节点的key
    def get_key(self):
        return self.key

    # 获取指定边的权重
    def get_weight(self, nbr):
        return self.connected_to[nbr]

    # 打印当前节点连接的所有节点信息
    def __str__(self):
        return str(self.key) + ' connected to: ' + str([x.key for x in self.connected_to])


class Graph(object):
    def __init__(self):
        self.nodes = {}
        self.node_num = 0

    # 添加节点
    def add_node(self, key):
        self.node_num += 1
        new_node = Node(key)
        self.nodes[key] = new_node
        return new_node

    # 通过key获取节点
    def get_node(self, key):
        return self.nodes.get(key)

    # 添加一条边
    def add_edge(self, from_node, to_node, weight=0):
        if from_node not in self.nodes:
            self.add_node(from_node)
        if to_node not in self.nodes:
            self.add_node(to_node)
        self.nodes[from_node].add_nbr(self.nodes[to_node], weight)

    # 获取图中所有节点
    def get_nodes(self):
        return self.nodes.keys()

    def __iter__(self):
        return iter(self.nodes.values())

    # in操作符
    def __contains__(self, key):
        return key in self.nodes

图的搜索算法

广度优先BFS

给定图G,已经开始搜索的起始顶点S。BFS搜索所有从S可到达其他顶点的边,并且在到达更远的距离k+1的顶点之前,BFS会找到全部距离为k的顶点。

# 标准的广度优先的基本流程
def bfs():
	定义队列;
	
    定义备忘录,用于记录已经访问的位置;
    
    判断边界条件,是否能直接返回结果的;

    将起始位置加入到队列中,同时更新备忘录;

    while 队列不为空:
        获取当前队列中的元素个数。
        for (元素个数):
            取出一个位置节点。
            判断是否到达终点位置。
            获取它对应的下一个所有的节点。
            for (每个节点)
	            条件判断,过滤掉不符合条件的位置。
	            新位置重新加入队列。
深度优先DFS

深度优先于广度优先算法流程基本一致,只是将队列改为了栈存储检查的节点。

# 标准的广度优先的基本流程
def bfs():
	定义栈;
	
    定义备忘录,用于记录已经访问的位置;
    
    判断边界条件,是否能直接返回结果的;

    将起始位置加入到栈中,同时更新备忘录;

    while 栈不为空:
        获取当前栈中的元素个数。
        for (元素个数):
            取出一个位置节点。
            判断是否到达终点位置。
            获取它对应的下一个所有的节点。
            for (每个节点)
	            条件判断,过滤掉不符合条件的位置。
	            新位置重新加入栈。

LeetCode

BFS

1091. 二进制矩阵中的最短路径

题目链接

解法:广度优先搜索

def shortestPathBinaryMatrix(grid):
    # 边界情况
    if len(grid) == 0 or len(grid[0]) == 0 \
            or grid[0][0] == 1 or grid[-1][-1] == 1:
        return -1

    if len(grid) == 1:
        return 1

    row = len(grid)
    cow = len(grid[0])

    queue = [(0, 0)]
    ans = 0

    while len(queue) > 0:
        node_num = len(queue)
        for i in range(node_num):
            node = queue.pop(0)

            # 到达右下角节点时返回结果
            if node == [cow-1, row-1]:
                return ans + 1

            # 八个方向偏移量:左上、左、左下、下、右下、右、右上、上
            offsets = [[-1, -1], [-1, 0], [-1, 1], [0, 1],
                       [1, 1], [1, 0], [1, -1], [0, -1]]

            # 判断八个方向的节点是否满足要求
            for offset in offsets:
                x = node[0] + offset[0]
                y = node[1] + offset[1]
				
				# 越界
                if not 0 <= x <= cow-1:
                    continue
				# 越界
                if not 0 <= y <= row-1:
                    continue
				# 非零值
                if grid[x][y] != 0:
                    continue

                # 满足要求时加入队列,并且把当前节点置为1
                queue.append([x, y])
                grid[x][y] = 1
     
        # 遍历一次结束后,步数+1
        ans += 1

    return -1

如下图,因为广度优先是一层一层遍历,如果子节点找不到满足条件的子子节点,会走到相邻的兄弟节点,兄弟节点再去找合适的子节点。所以最终能找到到达目的地址的最短路径。
在这里插入图片描述

127. 单词接龙

题目链接

解法:广度优先搜索

def ladderLength(beginWord, endWord, wordList):

    if beginWord not in wordList:
        wordList.append(beginWord)

    """
    1. 4个字母的单子总共右5000+,如果wordList有5000个字母,
    循环对比每个字母是否只差1个字符,至少需要5000^2 * 4 = 2500W
    2. 采用建桶的方式,例如: 单词dot,可以建立3个桶,*ot、d*t、do*,*号作为通配符匹配所有字母,
    然后遍历所有单词,如果满足条件的都加入同一个桶中。时间复杂度:O(n*length)
    """
    buckets = {}
    for word in wordList:
        for i in range(len(word)):
            bucket = word[:i] + "*" + word[i+1:]
            if bucket in buckets:
                buckets[bucket].append(word)
            else:
                buckets[bucket] = [word]

    # 通过临接数组 构建无向图,相同桶中的单词添加相邻边
    graph = {}
    for key, words in buckets.items():
        for word in words:
            for word1 in words:
                if word != word1:
                    if word not in graph:
                        graph[word] = [word1]
                    else:
                        graph[word].append(word1)

    if not graph or beginWord not in graph:
        return 0

    # 以下为BFS的标准流程
    queue = [beginWord]
    seen = set()
    seen.add(beginWord)
    ans = 1

    while queue:
        for i in range(len(queue)):
            node = queue.pop(0)

            # 遍历相邻节点
            for nbr_node in graph.get(node, []):
                if nbr_node == endWord:
                    return ans + 1

                if nbr_node in seen:
                    continue

                queue.append(nbr_node)
                seen.add(nbr_node)

        ans += 1

    return 0

DFS

695. 岛屿的最大面积

题目链接

解法:深度优先搜索

def ladderLength(grid):
    row = len(grid)
    cow = len(grid[0])

    ans = 0

    for i in range(row):
        for j in range(cow):
            cur = 0
            # 深度优先搜索使用栈
            stack = [(i, j)]
            # 如果当前节点值为1,则cur+1,并且把当前节点值改为0,代表已经搜索过
            if grid[i][j] == 1:
                cur += 1
                grid[i][j] = 0
            # 如果当前节点值为0,因为值能查看上下左右4个方向,所以为0时,4个方向无法连接起来,
            # 直接continue
            else:
                continue

            while stack:
                node = stack.pop()

                # 遍历4个方向
                offsets = ((-1, 0), (0, 1), (1, 0), (0, -1))

                for offset in offsets:
                    x = node[0] + offset[0]
                    y = node[1] + offset[1]

                    if x < 0 or x >= row:
                        continue

                    if y < 0 or y >= cow:
                        continue

                    if grid[x][y] != 1:
                        continue

                    stack.append((x, y))
                    grid[x][y] = 0
                    cur += 1

            ans = max(ans, cur)

    return ans

200. 岛屿数量

题目链接

当前题目与695题类似。

解法:深度优先搜索

def numIslands(grid):
    row = len(grid)
    cow = len(grid[0])

    ans = 0
    for i in range(row):
        for j in range(cow):
            # 深度优先搜索使用栈
            stack = [(i, j)]
            # 如果当前节点值为1,则cur+1,并且把当前节点值改为0,代表已经搜索过
            if grid[i][j] == "1":
                grid[i][j] = "0"
            # 如果当前节点值为0,因为值能查看上下左右4个方向,所以为0时,4个方向无法连接起来,
            # 直接continue
            else:
                continue

            while stack:
                node = stack.pop()

                # 遍历4个方向
                offsets = ((-1, 0), (0, 1), (1, 0), (0, -1))

                for offset in offsets:
                    x = node[0] + offset[0]
                    y = node[1] + offset[1]

                    if x < 0 or x >= row:
                        continue

                    if y < 0 or y >= cow:
                        continue

                    if grid[x][y] != "1":
                        continue

                    stack.append((x, y))
                    grid[x][y] = "0"

            ans += 1

    return ans

130. 被围绕的区域

题目链接

解法:深度优先搜索

def solve(self, board: List[List[str]]) -> None:
       """
       Do not return anything, modify board in-place instead.
       """
       row = len(board)
       cow = len(board[0])
       seen = set()

       for i in range(row):
           for j in range(cow):

               if (i, j) in seen:
                   continue

               # 深度优先搜索使用栈
               stack = [(i, j)]
               seen.add((i, j))
               temp = set()
               edge_o_flag = False

               if board[i][j] == "O":
                   if judge_edge(i, j, row, cow):
                       edge_o_flag = True
                   temp.add((i, j))
               else:
                   continue

               while stack:
                   node = stack.pop()

                   # 遍历4个方向
                   offsets = ((-1, 0), (0, 1), (1, 0), (0, -1))

                   for offset in offsets:
                       x = node[0] + offset[0]
                       y = node[1] + offset[1]

                       if (x, y) in seen:
                           continue

                       if x < 0 or x >= row:
                           continue

                       if y < 0 or y >= cow:
                           continue
                       
                       seen.add((x, y))

                       if board[x][y] != "O":
                           continue

                       if judge_edge(x, y, row, cow) and board[x][y] == "O":
                           edge_o_flag = True

                       stack.append((x, y))
                       temp.add((x, y))

               if not edge_o_flag:
                   for (x, y) in temp:
                       board[x][y] = "X"

def judge_edge(x, y, row, cow):
    return (x == 0 or x == row - 1 or y == 0 or y == cow - 1)

回溯(Backtracking)

Backtracking(回溯)属于 DFS的一种。

普通 DFS 主要用在 可达性问题 ,这种问题只需要执行到特点的位置然后返回即可。
而 Backtracking 主要用于求解 排列组合 问题,例如有 { ‘a’,‘b’,‘c’ } 三个字符,求解所有由这三个字符排列得到的字符串,这种问题在执行到特定的位置返回之后还会继续执行求解过程。
因为 Backtracking 不是立即返回,而要继续求解,因此在程序实现时,需要注意对元素的标记问题:

在访问一个新元素进入新的递归调用时,需要将新元素标记为已经访问,这样才能在继续递归调用时不用重复访问该元素;
但是在递归返回时,需要将元素标记为未访问,因为只需要保证在一个递归链中不同时访问一个元素,可以访问已经访问过但是不在当前递归链中的元素。

回溯算法能解决如下问题:
组合问题:N个数按一定规则找出k个数的集合;
排列问题:N个数按一定规则全排列,有几种排列方式;
切割问题:一个字符串按一定规则有几种切割方式;
子集问题:一个N个数的集合有多少符合条件的子集;
棋盘问题:N皇后,解数独等等。

def backtracking(参数):
	if 终止条件:
 		保存结果
 		return
 
	for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)):
 		处理节点
 		backtracking(路径,选择列表) // 递归
 		回溯,撤销处理结果
 

全排列问题

输出自然数1到n所有不重复的排列,要求每个数字只能使用一次。
例如: 输入n=3, 输出 [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]。

def find_all_pacificAtlantic(n):

    def dfs(x):
        # 如果当前要选的下标x > 总的可选数字个数,说明已经选择完毕,将结果加入到result中
        if x > n:
            result.append(copy.deepcopy(ans))
            return

        # 每一位的数字范围为 1-n
        for i in range(1, n + 1):
            # 判断当前数字是否使用过
            if seen.get(i, 0) == 0:
                # 当前下标置为 i
                ans[x-1] = i
                # 已经使用过的数字加入到seen,其他下标不能再使用
                seen[i] = 1
                # 递归搜索下标为x+1的位置可选数字
                dfs(x + 1)
                # 一轮搜索结束要把使用的数据恢复为可选状态(回溯)
                seen[i] = 0

    # 由于不能重复使用数字,所以用seen记录每个数字是否在其他位置上已经使用过,
    # 1代表使用过,0代表未使用
    seen = {}
    # 用于保存排列组合数字
    ans = [0, 0, 0]
    # 保存最终结果
    result = []
    # 从第1位开始搜索,x代表第几位
    dfs(1)

    return result
  1. 从第1位开始选择,首选选择1,并把1置为已用状态;
  2. 递归调用dfs,第2位选择1时发现已经被使用,所以选择了2,并把2置为已用状态;
  3. 递归调用dfs,第3位选择1和2时发现已经被使用,所以选择了3,并把3置为已用状态;
  4. 递归调用dfs,选择第4位时已经超出n,所以递归退出,把结果[1,2,3]加入到result中;
  5. 递归退出后,回到选择第3位时的调用栈,将3置为可以用状态;
  6. 再次回到选择第2位时的调用栈,将2置为可用状态;
  7. 重新选择第2位,上次选到了2,这次循环选到3,将3置为已用状态;
  8. 选择第3位,由于1、3已经被使用,所以第3位只能使用2,将结果[1,3,2]加入到result中。

77. 组合

题目链接

解法:回溯

def combine(n, k):
	
	# start代表从x开始
    def backtracking(start):
        if len(temp) == k:
            result.append(copy.deepcopy(temp))
            return

        #for i in range(start, n+1):
        # 当前已经选择的元素len(temp); 还需要选择的元素k-len(temp)
        # 还可以选择的元素n-(k-len(temp))+1,如果小于start,则不需要再循环递归
		for i in range(start, (n-(k-len(temp))+1)+1):
            temp.append(i)
            # 因为是找组合,元素不能重复,当前选到了i,下次递归要从i+1开始选
            backtracking(i+1)
            # 回溯
            temp.pop()
	# 保存临时数据
    temp = []
    result = []
    backtracking(1)

    return result

在这里插入图片描述

第216题.组合总和III

题目链接

解法:回溯

def backtracking(start):
    # 如果temp中元素个数大于k,退出递归
    if len(temp) > k:
        return
    # 如果temp中元素个数等于k,并且和为n
    if sum(temp) == n and len(temp) == k:
        result.append(copy.deepcopy(temp))
        return

    for i in range(start, 10):
        temp.append(i)
        backtracking(i+1)
        temp.pop()

temp = []
result = []
backtracking(1)

return result

17. 电话号码的字母组合

题目链接

解法:回溯

def letterCombinations(digits):
    mapping = {
        "2": "abc",
        "3": "def",
        "4": "ghi",
        "5": "jkl",
        "6": "mno",
        "7": "pqrs",
        "8": "tuv",
        "9": "wxyz"
    }

    if digits == "":
        return []

    def backtracking(index):
        # 如果temp中元素个数等于k,并且和为n
        if len(temp) == len(digits):
            result.append("".join(temp))
            return

        for i in mapping[digits[index]]:
            temp.append(i)
            # 注意这里是index+1,不是i+1
            # 不同位置选择的元素属于不同集合,互不影响,所以只需要用index标记要给哪一位选
            backtracking(index+1)
            temp.pop()

    temp = []
    result = []
    backtracking(0)

    return result

39. 组合总和

题目链接

解法:回溯

def combinationSum(candidates, target):

    def backtracking(start):
        if sum(temp) > target:
            return
        # 如果temp中元素个数等于k,并且和为n
        if sum(temp) == target:
            result.append(copy.deepcopy(temp))
            return

        for i in range(start, len(candidates)):
            temp.append(candidates[i])
            # 注意这里是i,不是i+1
            # 同一个集合中选n个可以重复的数字时,不需要i+1,下次递归还可以选当前选择的元素
            backtracking(i)
            temp.pop()

    temp = []
    result = []
    backtracking(0)

    return result

注意

  1. 同一个集合中选n个数并且不允许重复时,需要通过start标记当前选到了哪个数,下次递归要从start+1开始选
  2. 同一个集合中选n个数并且允许重复时,需要通过start标记当前选到了哪个数,下次递归还是从start开始选,可以重复选当前元素。
  3. 从n个集合中选n个数,每个数字选择互不影响,只需标记当前给第几位选数字即可。(例如:17. 电话号码的字母组合)

40. 组合总和 II

题目链接

解法:回溯

def combinationSum2(candidates, target):

    def backtracking(start):
        if sum(temp) > target:
            return

        if sum(temp) == target:
            result.append(copy.deepcopy(temp))
            return

        for i in range(start, len(candidates)):
            # 排序后,如果当前数字等于前一个数字,说明存在重复数字
            # 如果used用于记录同一树层前一个重复数字有没有被使用过,如果为False则被使用过
            # 例如:nums=[1,1,2] target=3 第一层选了1,递归选第二个数字时,再次选到了1,
            # 但是used[1-1]=True,可以继续选1,即temp=[1,1],同一树枝可以重复选择数字

            # 例如:第一个树枝已经选到了temp=[1,2]满足条件;选第二条树枝时,第一层选到了1,
            # 等于前一个数字1,但是used[1-1]=False,即前一个1已经用过了。
            if i > 0 and candidates[i] == candidates[i-1] and not used[i-1]:
                continue
            used[i] = True
            temp.append(candidates[i])
            backtracking(i+1)
            used[i] = False
            temp.pop()

    used = [0] * len(candidates)
    temp = []
    result = []
    # 1.先对原数据排序
    candidates.sort()
    backtracking(0)

    return result

注意:这道题与前面的题主要区别是,原数据存在重复数字,最终结果不允许右重复集合,所以通过排序 + used记录同一树层已经使用过的数字。
在这里插入图片描述
在这里插入图片描述

131. 分割回文串

题目链接

解法:回溯

def partition(s):

    def backtracking(start):
        """
        :param start: 标记截取字符串的起始位置
        :return: 
        """
        # 如果起始位置大于等于len(s),说明已经遍历完字符串
        if start >= len(s):
            result.append(copy.deepcopy(temp))
            return

        for i in range(start, len(s)):
            # 判断s[start,i+1]是否是回文串
            if check_palindromic(s, start, i):
                # 由于截取字符串是左闭右开区间,所以这里是i+1,实际上截取到了i
                temp.append(s[start:i+1])
                backtracking(i+1)
                temp.pop()

    def check_palindromic(exp, left, right):
        """
        判断回文串
        :param exp:
        :return: Boolean
        """

        if left > right or exp == "":
            return False

        while left < right:
            if exp[left] == exp[right]:
                left += 1
                right -= 1
            else:
                return False
        return True

    temp = []
    result = []
    backtracking(0)

    return result

在这里插入图片描述

93. 复原 IP 地址

题目链接

解法:回溯

def restoreIpAddresses(s):

    def backtracking(start):
        """
        :param start: 标记截取字符串的起始位置
        :return:
        """
        if len(temp) > 4:
            return
        # 如果起始位置大于等于len(s),说明已经遍历完字符串
        if start >= len(s) and len(temp) == 4:
            result.append(".".join(temp))
            return

        for i in range(start, len(s)):
            # 值不大于255,并且首位不为0
            # TODO 剪枝
            if (len(s[start:i+1]) == 1 or s[start:i+1][0] != "0") and int(s[start:i+1]) <= 255:
                temp.append(s[start:i+1])
                backtracking(i+1)
                temp.pop()

    temp = []
    result = []
    backtracking(0)

    return result

在这里插入图片描述

78. 子集

题目链接

解法:回溯

def subsets(nums):

    def backtracking(start):
        """
        :param start: 标记截取字符串的起始位置
        :return:
        """
        # 子集是求所有节点,不是叶子节点,所以不需要判断条件
        result.append(copy.deepcopy(temp))

        for i in range(start, len(nums)):
            temp.append(nums[i])
            backtracking(i+1)
            temp.pop()

    temp = []
    result = []
    backtracking(0)

    return result

**注意:**如果把 子集问题、组合问题、分割问题都抽象为一棵树的话,那么组合问题和分割问题都是收集树的叶子节点,求子集问题是找树的所有节点!
在这里插入图片描述

491.递增子序列

题目链接

解法:回溯

def findSubsequences(self, nums: List[int]) -> List[List[int]]:
    def backtracking(start):
        """
        :param start: 标记截取字符串的起始位置
        :return:
        """
        # 判断是否已经存在
        if len(temp) >= 2 and temp not in result:
            result.append(copy.deepcopy(temp))

        for i in range(start, len(nums)):
            if len(temp) == 0 or nums[i] >= temp[-1]:
                temp.append(nums[i])
                backtracking(i+1)
                temp.pop()

    temp = []
    result = []
    backtracking(0)

    return result

排列问题

46.全排列

题目链接

解法:回溯
def permute(nums):

    def backtracking():
        if len(temp) == len(nums):
            result.append(copy.deepcopy(temp))

        for i in range(0, len(nums)):
            # 因为组合问题是从下标0开始遍历nums,并且没有重复原元素,
            # 需要判断是否当前元素已经加入过temp
            if nums[i] not in temp:
                temp.append(nums[i])
                backtracking()
                temp.pop()

    temp = []
    result = []
    backtracking()

    return result

注意:组合问题遍历nums时需要从0开始遍历;例如:nums=[1,2,3], [1,2,3]和[2,1,3]都是结果,第一个元素从2开始时,需要能获取到1,所以需要从下标0开始遍历,同时为了保证不会重复拿到2,所以判断nums[i]是否已经在temp中。

47.全排列 II

题目链接

解法:回溯
def permuteUnique(nums):

    def backtracking():
        if len(temp) == len(nums):
            result.append(copy.deepcopy(temp))
            return

        for i in range(0, len(nums)):
            # 同一树层去重
            if i > 0 and nums[i] == nums[i-1] and not used[i-1]:
                continue
            # 同一树枝去重
            if not used[i]:
                used[i] = True
                temp.append(nums[i])
                backtracking()
                temp.pop()
                used[i] = False

    nums.sort()
    used = [False] * len(nums)
    temp = []
    result = []
    backtracking()

    return result

注意:当前题目和 40. 组合总和 II 去重条件相似,区别就是40题是求组合,每次递归nums下标从start+1开始,同一树枝不会重复遇到同一个元素。当前题目是组合,递归遍历nums是从下标0开始,每次都会遇到当前元素,需要通过同一树层去重和同一树枝去重来避免重复的解。
在这里插入图片描述

332. 重新安排行程

题目链接

解法:回溯
def findItinerary(tickets):

    def backtracking(start):
        if len(temp) == len(tickets)+1:
            return True

        for index, end in enumerate(ticket_dict.get(start, [])):
            if not end.endswith("_use"):
                ticket_dict[start][index] += "_use"
                temp.append(end)
                if backtracking(end):
                    return True
                temp.pop()
                ticket_dict[start][index] = ticket_dict[start][index][:-4]

        # 当前树枝的叶子节点,在ticket_dict中找不到对应的key时,for循环不会执行,直接返回Fasle,此路不通
        return False

    # 1. 起点和终点映射关系
    ticket_dict = {}
    for ticket in tickets:
        if ticket[0] not in ticket_dict:
            ticket_dict[ticket[0]] = [ticket[1]]
        else:
            ticket_dict[ticket[0]].append(ticket[1])

    # 2. 因为最终结果与字符顺序有关,所以先升序排序,每次获取从第0个元素获取,即能保证最小路径
    for key, value in ticket_dict.items():
        value.sort()

    # 3. 起点为"JFK"
    temp = ["JFK"]
    backtracking("JFK")

    return temp

例如

  1. tickets=[[“JFK”, “KUL”], [“JFK”, “MUC”], [“MUC”, “JFK”]],整理起点和终点并排序后为:ticket_dict={‘JFK’: [‘KUL’, ‘MUC’], ‘MUC’: [‘JFK’]}。
  2. 进入第一次递归起点为’JFK’,其对应的两个终点为[‘KUL’, ‘MUC’];先从"KUL"开始,temp=[“JFK”, “KUL”], ticket_dict={‘JFK’: [‘KUL_use’, ‘MUC’], ‘MUC’: [‘JFK’]}进入第二次递归,ticket_dict中没有以"KUL"为起点的信息,所以直接返回False。
  3. 从temp中删除"KUL",temp=[“JFK”],并且将ticket_dict恢复为ticket_dict={‘JFK’: [‘KUL’, ‘MUC’], ‘MUC’: [‘JFK’]}。
  4. 从"MUC"进入第二层递归,此时temp=[“JFK”, “MUC”], ticket_dict={‘JFK’: [‘KUL’, ‘MUC_use’], ‘MUC’: [‘JFK’]};“MUC"对应的终点为"JFK”,temp=[“JFK”, “MUC”, “JFK”], ticket_dict={‘JFK’: [‘KUL’, ‘MUC_use’], ‘MUC’: [‘JFK_use’]}。
  5. 进入第三层递归,起点为"JFK",此时"JFK"对应的终点为[‘KUL’, ‘MUC_use’],只有"KUL"可以用,所以最终结果就是temp=[“JFK”, “MUC”, “JFK”, “KUL”]。
    在这里插入图片描述

51. N 皇后

题目链接

解法:回溯

待完成

37. 解数独

题目链接

解法:回溯

待完成

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

LeetCode---搜索算法 的相关文章

  • 01背包问题变种:从长度为n的数组里选出m个数使和为固定值sum

    这个问题是我从leetcode上一道问题所想到的 原题 如果是从数组中选出2个数相加使之成为固定的数sum 这当然很简单 把数组中的数字遍历一遍 判断另一个数字是否也在数组中即可 代码如下 vector
  • 50个c/c++源代码网站

    C C 是最主要的编程语言 这里列出了50名优秀网站和网页清单 这些网站提供c c 源代码 这份清单提供了源代码的链接以及它们的小说明 我已尽力包括最佳的C C 源代码的网站 这不是一个完整的清单 您有建议可以联系我 我将欢迎您的建 议 以
  • netty handler的执行顺序(3)

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 今天解决2个问题 1 handler在pipeline当中究竟是如何存储的 2 在遍历handler的过程中 会根据event的不同 调用不同的handler 这一点是如何
  • 白盒测试相关的一些知识

    在白盒测试中 可以使用各种测试方法进行测试 下面这篇文章 可能比较枯燥 如果不乐意读 可以先收藏 如果在你的工作中真遇到白盒测试的话 可以回过头再来看看 还是值得读一读 一般来说 白盒测试时要考虑以下5个问题 1 测试中尽量先用自动化工具来
  • Hash table and application in java

    查找的效率取决于在查找是比较的次数 次数越少效率越高 反之越低 最理想的情况是无需比较 一次存取便能找到所查找的记录 根据对应关系f找到给定值K的像f K hash function 应运而生 由此思想建的表称为hash table 集合h
  • findBug 错误修改指南

    FindBugs错误修改指南 1 EC UNRELATED TYPES Bug Call to equals comparing different types Pattern id EC UNRELATED TYPES type EC c
  • 微软2013暑假实习生笔试题

    自己mark一下 以作后备 下面提交原文链接 原文博客 部分题目答案不确定 会持续更新 1 Which of the following calling convention s support s supportvariable leng
  • 数据结构与算法学习总结(六)——字符串的模式匹配算法

    基本概念 字符串是一种特殊的线性表 即元素都是 字符 的线性表 字符是组成字符串的基本单位 字符的取值依赖于字符集 例如二进制的字符集为0 1 则取值只能为 0 1 再比如英语语言 则包括26个字母外加标点符号 例如 abcde 就是一个字
  • 手把手教你实现一个向量

    文章目录 什么是向量 向量提供哪些接口 实现 宏定义 定义类 成员变量 构造函数与析构函数 构造函数 析构函数 成员函数 size get r put r e expand insert r e remove lo hi remove r
  • Hash映射理解

    先说数组 数组优点之一 能通过索引很快定位到值 hashmap 就是利用了数组这个优点 对比 线性映射 定义一个数组 数组的元素是结构体 结构体包括 一对键 值 伪代码表示 a 0 struct Bill 5 a 1 struct KK 6
  • 以太坊系列之十五: 以太坊数据库

    以太坊数据库中都存了什么 以太坊使用的数据库是一个NOSQL数据库 是谷歌提供的开源数据leveldb 这里尝试通过分析以太坊数据库存储了什么来分析以太坊可能为我们提供哪些关于区块链的API 存储内容 NOSQL是一个key value数据
  • 数据结构——计算节点个数和二叉树高度(C语言版)

    摘自 数据结构 计算节点个数和二叉树高度 C语言版 作者 正弦定理 发布时间 2020 12 12 23 27 09 网址 https blog csdn net chinesekobe article details 111086664
  • 4Sum

    Given an array S of n integers are there elements a b c and d in S such that a b c d target Find all unique quadruplets
  • 浮生六记

    浮生六记 目录 浮生六记卷一 闺房记乐 002 浮生六记卷二 闲情记趣 015 浮生六记卷三 坎坷记愁 022 浮生六记卷四 浪游记快 034 浮生六记 2 浮生六记卷一 闺房记乐 余生乾隆癸未冬十一月二十有二日 正值太平盛世 且在 衣冠之
  • 算法学习——贪心算法之币种统计

    算法描述 币种统计 单位给每一位员工发工资 精确到元 为了保证不临时换零钱 使得每个员工取款的张数最少 在取工资前统计所有员工所需要的各种票面的张数 约定票种为100 50 20 10 5 2 1元 并验证币种统计是否正确 算法思路 算法描
  • 时间复杂度+常见复杂度解释

    前言 算法的效率 虽然计算机能快速的完成运算处理 但实际上 它也需要根据输入数据的大小和算法效率来消耗一定的处理器资源 要想编写出能高效运行的程序 我们就需要考虑到算法的效率 算法的效率主要由以下两个复杂度来评估 时间复杂度 评估执行程序所
  • Linux下进程退出的几种形式

    进程退出 Linux 下进程的退出分为正常退出和异常退出两种 1 正常退出 a 在main 函数中执行return b 调用exit 函数 c 调用 exit 函数 2 异常退出 a 调用about函数 b 进程收到某个信号 而该信号使程序
  • 【试题】排列组合

    在写一个远程的代码 如果本地有M个显示器 远程有N个显示器 M lt N 依据分辨率 显示器刷新频率等要求 需要对远程的N个显示器进行最佳分辨率修改 之后 需要从N个远程显示器中选择M个 跟本地显示器进行一对一的匹配 即从 A N M N
  • 数组实现循环队列(增设队列大小size)

    目录 一 前言 1 如何实现循环 2 如何判断队列为空 3 如何判断队列为满 二 循环队列的结构定义 三 循环队列的创建及其初始化 四 入队 五 出队 六 取队头元素 七 取队尾元素 八 循环队列判空 九 循环队列判满 十 循环队列销毁 一
  • 最大流-Dinic算法,原理详解,四大优化,详细代码

    文章目录 零 前言 一 概念回顾 可略过 1 1流网络 1 2流 1 3最大流 1 4残留网络 1 5增广路

随机推荐

  • 深入研究webpack之Tree Shaking相关属性sideEffects用处

    Tree Shaking我原来也只是了解 这次碰巧深入研究了下 就写个博客记录一下 网上有很多讲Tree Shaking的 我写的这篇跟他们侧重点不一样 Tree Shaking相关的基础知识 1 webpack会从入口文件开始不断的获取你
  • 中科院分区2020_一文读懂SCI期刊分区和实时影响因子计算方法

    作者 恺忻 排版 审核 恺忻 SCI分区 sci分区是一个sci基本常识 国内很多有sci论文发表要求的高校或者科研单位 在发表要求中对期刊分区一般都有明确要求 因为分区不同关系着影响因子高低 很多作者不知道如何查看sci期刊分区 目前sc
  • 文件操作(详细总结)

    文章目录 为什么要使用文件 什么是文件 文件的打开和关闭 文件顺序读写 流 文件的随机读写 文本文件和二进制文件 文件读取结束的判定 文件读取结束的原因 文件缓冲区 为什么要使用文件 为了更好的保存数据 可以将数据写到文件里 在硬盘中 什么
  • 1.4 顺序与选择结构

    第一关 顺序结构 任务描述 本关介绍顺序结构 程序最基本的结构就是顺序结构 顺序结构就是程序按照语句顺序 从上到下依次执行各条语句 本关要求读者理解顺序结构 对输入的三个数changeone changetwo plus能够先交换chang
  • 20行Python代码爬取网站美女图,哇太多了,我U盘装满了

    淘女郎爬虫 可动态抓取淘女郎的信息和照片 需要额外安装的第三方库 requests pip install requests pymongo pip install pymongo 模块功能 TaoLady py 负责发送POST请求和抓取
  • python一定要有主函数吗_python没有main函数吗

    相信很多初学python的人看代码的时候都会先找一下main 方法 从main往下看 但事实上python中是没有你理解中的 main 方法的 if name main 可以看成是python程序的入口 就像java中的main 方法 但不
  • 软件测试的技术深度一定比软件开发低吗?看完这篇文章秒懂

    之所以会有很多人普遍觉得 测试人员比开发人员要求低 一是国内行业现状造成的 因为国内软件企业对软件测试技术的认知比较晚 即使在发展了几年后测试行业变得相对成熟和正式 仍然很多企业公司的主观意识中觉得开发人员解决的是项目可用性问题 而测试人员
  • 使用tensorflow卷积神经网络实现mnist手写数字识别

    在实现mnist手写数字识别的时候 看了极客网上的例子 自己试着实现了一下 但是期间发现了很多问题 于是就把值得注意的地方写在注释里面了 以供后面查阅温习 import tensorflow as tf from tensorflow ex
  • MyBatis 解决模糊查询包含特殊字符

    第一块 MyBatis 实现模糊查询方式 1 1 sql中字符串拼接 SELECT FROM 表名 WHERE 字段名 LIKE CONCAT CONCAT 参数 1 2 使用 代替 SELECT FROM 表名 WHERE 字段名 LIK
  • Linux 嵌入式 BeagleBone 使用 Python 和 JavaScript

    特点 BeagleBone 是一款面向创客的嵌入式 Linux 开发板 它具有内置网络 许多输入和输出以及处理要求苛刻的任务的快速处理器 介绍原始的 BeagleBone 和新的 BeagleBone Black 并开始利用板的处理能力及其
  • PYTHON编程导论群问题汇总(五)

    Q15 改变对象与绑定 P54 Univs和Univs1被绑定到不同的对象的原理不是很清楚 bigjing Univs Techs Ivys Univs1 MIT Caltech Harvard Yale Brown Univs绑定的是含有
  • 【python】numpy随机抽样

    0 前言 numpy random 模块对 Python 内置的 random 进行了补充 增加了一些用于高效生成多种概率分布的样本值的函数 如正态分布 泊松分布等 1 随机模块 numpy random seed seed None se
  • Set结构的使用与实现

    Set Set是继承自Collection的一个接口类 Set中只存储了key 并且要求key一定要唯一 Set的底层是使用Map来实现的 其使用key与Object的一个默认对象作为键值对插入到Map中的 因为Set里面的key是不能够重
  • pyppeteer和selenium远程操控浏览器

    1 配置环境 Chrome浏览器是支持远程调试模式的 这个模式打开的情况下 Puppeteer或者Selenium可以通过websocket连上去 进而控制它 首先我们来启动Chrome的远程调试端口 你需要找到Chrome的安装位置 在C
  • linux ld 链接.o文件,Linux:控制`ld`搜索.o目标文件的位置?

    好吧 情况就是这样 我正在尝试使用一些较旧的软件 在Ubuntu Lucid上工作正常 在Natty上失败 所以 我徘徊了一下 事实证明这个软件调用ld 并且ld最终失败了 ld crt1 o No such file No such fi
  • 机器学习算法简介和代码(P&R语言)

    机器学习算法 P R语言 一般说来 机器学习有三种算法 1 监督式学习 监督式学习算法包括一个目标变量 因变量 和用来预测目标变量的预测变量 自变量 通过这些变量我们可以搭建一个模型 从而对于一个已知的预测变量值 我们可以得到对应的目标变量
  • Matlab学习:读取excel中数据

    Matlab中大部分功能都可以通过函数调用实现 在本文中所涉及的读取excel中数据这一功能可以通过下面的函数 1 实现 num xlsread fileURL n 1 其中 num 表示输出的数据 可以是矩阵也可以是数组 xlsread
  • 用Hadoop流实现mapreduce版推荐系统基于物品的协同过滤算法

    以个性化新闻推荐为例 整个过程分成两个mapreduce阶段 由于hadoop流不支持多个mapreduce过程的自动化 所以所有mapreduce过程命令必须人工一个一个的执行 1 首先需要将原始数据处理成如下形式的两个文件 文件一 It
  • 蓝桥杯JAVA B组 2020(1)第二题 寻找2020

    一 知识点 ToCharArray 的用法 将字符串对象中的字符转换为一个字符数组 二 题目描述小蓝有一个数字矩阵 里面只包含数字 0 和 2 小蓝很喜欢 2020 他想找到这个数字矩阵中有多少个 2020 小蓝只关注三种构成 2020 的
  • LeetCode---搜索算法

    LeetCode 搜索算法 搜索算法 图 图Graph的概念 图的抽象数据类型 邻接矩阵 邻接列表 图的搜索算法 广度优先BFS 深度优先DFS LeetCode BFS 1091 二进制矩阵中的最短路径 解法 广度优先搜索 127 单词接