算法设计与分析实验:动态规划与回溯

目录​​​​​​​

一、编辑距离

1.1 具体思路

1.2 思路展示

1.3 代码实现

1.4 复杂度分析

1.5 运行结果

二、买卖股票的最佳时机

2.1 具体思路

2.2 思路展示

2.3 代码实现

2.4 复杂度分析

2.5 运行结果

三、单词拆分

3.1 具体思路

3.2 思路展示

3.3 代码实现

3.4 复杂度分析

3.5 运行结果

四、打家劫舍

4.1 具体思路

4.2 思路展示

4.3 代码实现

4.4 复杂度分析

4.5 运行结果

结尾语


一、编辑距离

力扣第72题

本题采用动态规划的思路

1.1 具体思路

我们可以创建一个二维数组 dp,其中 dp[i][j] 表示将 word1 的前 i 个字符转换成 word2 的前 j 个字符所需的最少操作数。

步骤如下:

①初始化:首先,我们需要初始化 dp 数组。特别地,dp[0][j] 表示将空字符串转换成 word2 的前 j 个字符所需的操作数,显然需要 j 次插入操作;同样地,dp[i][0] 表示将 word1 的前 i 个字符转换成空字符串所需的操作数,需要 i 次删除操作。

②填充 dp 数组:接下来,我们按行填充 dp 数组。对于每一对字符 word1[i] 和 word2[j],我们有以下几种情况:

如果 word1[i] 等于 word2[j],则当前字符不需要任何操作,dp[i][j] 就等于 dp[i-1][j-1]。

如果 word1[i] 不等于 word2[j],则我们可以通过三种操作中的一种将 word1 的前 i 个字符转换为 word2 的前 j 个字符:

插入:从 dp[i][j-1] 转换过来,即在 word1 中插入 word2[j],因此操作数加一。

删除:从 dp[i-1][j] 转换过来,即删除 word1[i],因此操作数加一。

替换:从 dp[i-1][j-1] 转换过来,即将 word1[i] 替换为 word2[j],因此操作数加一。 我们选择这三种操作中的最小操作数作为 dp[i][j] 的值。

③返回结果:最后,dp[m][n] 就是将整个 word1 转换为整个 word2 所需的最少操作数,其中 m 和 n 分别是两个字符串的长度。

1.2 思路展示

假设我们有两个单词 word1 和 word2,长度分别为 m 和 n。我们将创建一个 (m+1) 行 (n+1) 列的二维数组 dp,其中 dp[i][j] 表示将 word1 的前 i 个字符转换成 word2 的前 j 个字符所需的最少操作数。

这里是 dp 数组的初始化和填充过程的文字描述:

    ""  w2[0]  w2[1]  w2[2]  ...  w2[n-1]

""   0    1      2      3    ...    n

w1[0] 1

w1[1] 2

w1[2] 3

...

w1[m-1] m

初始化:

第一行:dp[0][j] = j,意味着将空字符串转换为 word2 的前 j 个字符需要 j 次插入操作。

第一列:dp[i][0] = i,意味着将 word1 的前 i 个字符转换为空字符串需要 i 次删除操作。

填充:

对于每个 i(从 1 到 m)和 j(从 1 到 n):

如果 word1[i-1] == word2[j-1](注意数组下标从 0 开始),则 dp[i][j] = dp[i-1][j-1]。

否则,dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1,分别对应删除、插入和替换操作。

最后,dp[m][n] 就是从 word1 转换到 word2 所需的最少操作数。

如果我们用实际的单词例如 "horse" 和 "ros" 来填充一个 dp 表格,它可能看起来像这样:

    ""   r    o    s

""   0    1    2    3

h    1    1    2    3

o    2    2    1    2

r    3    2    2    2

s    4    3    3    2

e    5    4    4    3

在这个例子中,dp[5][3] 是最终结果,表示将 "horse" 转换为 "ros" 需要的最少操作数是 3。

1.3 代码实现

def minDistance(word1, word2):m, n = len(word1), len(word2)# 创建一个(m+1) x (n+1)的二维数组dp,用于保存从word1的前i个字符转换到word2的前j个字符需要的最小操作数dp = [[0] * (n + 1) for _ in range(m + 1)]# 初始化边界条件for i in range(m + 1):dp[i][0] = i  # word1的前i个字符转换到空字符串需要i步(全部删除)for j in range(n + 1):dp[0][j] = j  # 空字符串转换到word2的前j个字符需要j步(全部插入)# 动态规划填表for i in range(1, m + 1):for j in range(1, n + 1):if word1[i - 1] == word2[j - 1]:dp[i][j] = dp[i - 1][j - 1]  # 字符相同,无需操作,继承上一状态的操作数else:dp[i][j] = 1 + min(dp[i - 1][j],    # 删除dp[i][j - 1],    # 插入dp[i - 1][j - 1]) # 替换return dp[m][n]# 示例测试
word1 = "horse"
word2 = "ros"
print(minDistance(word1, word2))  # 输出应该是3word1 = "intention"
word2 = "execution"
print(minDistance(word1, word2))  # 输出应该是5

1.4 复杂度分析

我们使用了二维动态规划的方法来计算两个字符串之间的最小编辑距离。现在,我们来对这个函数的时间复杂度和空间复杂度进行分析。

①时间复杂度

时间复杂度主要由双重循环决定。外层循环遍历word1的长度m,内层循环遍历word2的长度n。每个循环体内的操作是常数时间复杂度。因此,总的时间复杂度为O(m * n)。

②空间复杂度

空间复杂度主要由用于存储动态规划状态的二维数组dp决定。这个数组的大小是(m+1) x (n+1),所以总的空间复杂度为O(m * n)。

综上所述,minDistance函数的时间复杂度和空间复杂度都是O(m * n),其中m是字符串word1的长度,n是字符串word2的长度。

1.5 运行结果

示例一

word1 = "horse"

word2 = "ros"

示例二

word1 = "intention"

word2 = "execution"

二、买卖股票的最佳时机

力扣第122题

本题采用贪心算法的思路求解

2.1 具体思路

贪心算法是一种在每一步选择中都采取当前状态下最好或最优(即最有利)的选择,从而希望导致结果是全局最好或最优的算法策略。

在这个问题中,贪心算法的策略是:

遍历价格数组,从第二天开始比较。

如果第i天的价格比第i-1天的价格高,那么我们就可以假设在第i-1天买入,在第i天卖出,所以我们将这个差值(prices[i] - prices[i-1])加到我们的总利润中。

重复这个过程,直到遍历完整个数组。

*如何理解此逻辑?

这种方法之所以有效,是因为它累加了所有连续的增长期间的利润,这与多次买入和卖出的总利润是相同的。换句话说,如果股票价格是连续上升的,那么在开始时买入并在结束时卖出与每天买入卖出获得的利润是相同的。如果我们把每天的小利润加起来,就等于最初买入和最后卖出的利润。

2.2 思路展示

假设我们有以下股票价格数组:

prices = [7, 1, 5, 3, 6, 4]

我们通过以下步骤来计算最大利润:

从第一天开始,比较相邻两天的价格。

第一天价格为7,第二天价格为1,价格下降,所以不进行交易。

第二天价格为1,第三天价格为5,价格上升,我们假设第二天买入,第三天卖出,获得利润5 - 1 = 4。

第三天价格为5,第四天价格为3,价格下降,所以不进行交易。

第四天价格为3,第五天价格为6,价格上升,我们假设第四天买入,第五天卖出,获得利润6 - 3 = 3。

第五天价格为6,第六天价格为4,价格下降,所以不进行交易。

总利润为第三天和第五天获得的利润之和:4 + 3 = 7。

这个过程可以用下面的图表来表示:

Day

Price

Action

Profit

Total Profit

1

7

-

-

0

2

1

Buy

-

0

3

5

Sell (5 - 1)

4

4

4

3

Buy

-

4

5

6

Sell (6 - 3)

3

7

6

4

-

-

7

在这个表格中,"Day"列代表天数,"Price"列代表当天的股票价格,"Action"列描述了我们采取的行动(买入、卖出或不操作),"Profit"列显示了当天的利润(如果有的话),而"Total Profit"列则累计了到目前为止的总利润。如表格所示,通过在价格上升时买入和卖出,我们最终获得了7元的最大利润。

2.3 代码实现

def maxProfit(prices):# 初始化最大利润为0max_profit = 0# 遍历价格数组,从第二天开始for i in range(1, len(prices)):# 如果今天的价格比昨天高if prices[i] > prices[i - 1]:# 我们可以获得昨天买入、今天卖出的利润max_profit += prices[i] - prices[i - 1]# 返回计算出的最大利润return max_profit# 示例测试
prices1 = [7,1,5,3,6,4]
print(maxProfit(prices1))  # 应输出7prices2 = [1,2,3,4,5]
print(maxProfit(prices2))  # 应输出4prices3 = [7,6,4,3,1]
print(maxProfit(prices3))  # 应输出0

2.4 复杂度分析

·时间复杂度

时间复杂度是指算法执行的时间与输入数据之间的关系。在这个函数中,我们有一个主要的循环(第5行),它遍历了一次输入数组prices。

循环从索引1开始,直到数组的长度len(prices)结束。因此,循环会运行(len(prices) - 1)次。

在循环体内(第6-9行),所有操作(包括比较和加法)都是常数时间操作。

因此,整个函数的时间复杂度为O(n),其中n是数组prices的长度。

·空间复杂度

空间复杂度度量的是算法在执行过程中临时占用存储空间的大小。在这个函数中:

我们只使用了一个变量max_profit来存储最大利润,它是一个整数类型,占用的空间是常数大小。

循环变量i也是常数空间。

因此,整个函数的空间复杂度为O(1),也就是说,它只需要常数的额外空间。

综上所述,maxProfit函数的时间复杂度是O(n),空间复杂度是O(1)。

2.5 运行结果

# 示例测试

prices1 = [7,1,5,3,6,4]

prices2 = [1,2,3,4,5]

prices3 = [7,6,4,3,1]

三、单词拆分

力扣第140题

本题将采用回溯法加上动态规划的剪枝进行求解

3.1 具体思路

首先,我们需要一个方法来判断字符串 s 的任意子串是否可以被分割成字典 wordDict 中的单词。这可以通过动态规划实现,创建一个布尔数组 dp,其中 dp[i] 表示字符串 s 的前 i 个字符是否可以用 wordDict 中的单词表示。

详细思路如下:

①动态规划预处理:

创建一个布尔数组 dp 长度为 len(s) + 1,并初始化 dp[0] = True,因为空字符串总是可以被表示。

对于每个 i 从 1 到 len(s),对于每个单词 word 在 wordDict 中:

如果 i >= len(word) 并且 s[i-len(word):i] == word 并且 dp[i-len(word)] 是 True,那么 dp[i] 应该被设置为 True。

这样,dp[len(s)] 就告诉我们 s 是否可以被分割成字典中的单词。

②回溯法构建句子:

定义一个回溯函数 backtrack(start, path),其中 start 是当前考虑的子串的起始索引,path 是到目前为止形成的单词列表。

如果 start == len(s),则将 path 加入结果列表中,因为我们已经到达了字符串的末尾。

否则,遍历字符串 s,从索引 start 到 len(s):

如果 s[start:i+1] 在 wordDict 中,并且 dp[start] 是 True(表示从 s[0] 到 s[start-1] 的子串可以被分割成字典中的单词),那么:

将 s[start:i+1] 添加到当前 path 中。

递归调用 backtrack(i+1, path + [s[start:i+1]])。

回溯,即从 path 中移除 s[start:i+1]。

③初始化和调用回溯:

创建一个结果列表 res 来存储所有可能的句子。

调用 backtrack(0, []) 开始回溯过程。

返回 res。

3.2 思路展示

假设我们有如下输入:

s = "catsanddog"

wordDict = ["cat", "cats", "and", "sand", "dog"]

①动态规划过程:

我们创建一个布尔数组dp,长度为len(s) + 1,初始化dp[0] = True(空字符串是可以被分割的)。

dp = [False] * (len(s) + 1)

dp[0] = True

我们的dp数组初始状态如下:

dp = [True, False, False, False, False, False, False, False, False, False, False, False]

现在我们开始填充dp数组:

遍历字符串s中的每个字符。

对于每个位置i,遍历wordDict中的每个单词word。

如果子串s[i-len(word):i]等于word并且dp[i-len(word)]是True,则将dp[i]设置为True。

完成这一步后,dp数组状态如下:

dp = [True, False, False, False, True, False, False, True, False, False, True, True]

dp[len(s)]为True,表示整个字符串s可以被分割成字典中的单词。

②回溯过程:

接下来,我们使用回溯法来构建所有可能的句子。我们定义一个辅助函数backtrack(start, path),其中start是当前考虑的子串的起始索引,path是到目前为止形成的单词列表。

我们从start = 0和空路径path = []开始调用backtrack函数。回溯函数的执行流程如下:

backtrack(0, [])

    -> s[0:3] == "cat" and dp[3] is True:

        -> backtrack(3, ["cat"])

            -> s[3:6] == "sand" and dp[6] is False: skip

            -> s[3:7] == "and" and dp[7] is True:

                -> backtrack(7, ["cat", "and"])

                    -> s[7:10] == "dog" and dp[10] is True:

                        -> add ["cat", "and", "dog"] to res

    -> s[0:4] == "cats" and dp[4] is True:

        -> backtrack(4, ["cats"])

            -> s[4:7] == "and" and dp[7] is True:

                -> backtrack(7, ["cats", "and"])

                    -> s[7:10] == "dog" and dp[10] is True:

                        -> add ["cats", "and", "dog"] to res

最终,我们得到两个可能的句子,即res = [["cat", "and", "dog"], ["cats", "and", "dog"]]。

3.3 代码实现

1.def wordBreak(s, wordDict):
2.    wordSet = set(wordDict)
3.    n = len(s)
4.    
5.    # 动态规划预处理
6.    dp = [False] * (n + 1)
7.    dp[0] = True
8.    for i in range(1, n + 1):
9.        for word in wordDict:
10.            if dp[i - len(word)] and s[i - len(word):i] == word:
11.                dp[i] = True
12.                break
13.    
14.    # 回溯法构建句子
15.    def backtrack(start, path):
16.        if start == n:
17.            res.append(' '.join(path))
18.            return
19.        for end in range(start + 1, n + 1):
20.            if dp[end] and s[start:end] in wordSet:
21.                backtrack(end, path + [s[start:end]])
22.    
23.    # 初始化和调用回溯
24.    res = []
25.    backtrack(0, [])
26.    return res
27.
28.# 示例
29.print(wordBreak("catsanddog", ["cat","cats","and","sand","dog"]))

3.4 复杂度分析

我们需要分析两部分的复杂度:动态规划预处理部分和回溯法构建句子部分。

①动态规划预处理的复杂度:

时间复杂度:我们有一个外层循环遍历字符串s的每个字符,这个循环的次数为n。内层循环遍历字典wordDict中的每个单词,假设字典中平均单词长度为m,最坏情况下,我们可能需要比较n次。因此,动态规划部分的时间复杂度大约为O(n * m * k),其中k是字典中单词的数量。

空间复杂度:我们使用了长度为n + 1的布尔数组dp来存储状态,因此空间复杂度为O(n)。

①回溯法构建句子的复杂度:

时间复杂度:在最坏情况下,如果字符串可以被分割成字典中所有单词的所有组合,我们可能需要探索所有可能的分割方式。这将导致指数级的时间复杂度,因为每个位置都可以是一个潜在的分割点。具体来说,时间复杂度为O(2^n),因为每个位置都有两种选择:分割或不分割。

空间复杂度:回溯法在递归时会占用额外的空间,最坏情况下,递归的深度可以达到n,因此递归栈的空间复杂度为O(n)。此外,我们还需要存储中间路径path和最终结果res。在最坏情况下,这些可能包含字符串s的所有子串,因此空间复杂度也可能相当高,取决于字符串s的分割方式。

综上所述,整个wordBreak函数的总时间复杂度是O(n * m * k + 2^n),总空间复杂度是O(n + n * L),其中L是结果列表res中所有字符串长度的总和。然而,在实际应用中,由于动态规划数组dp的存在,很多不可能的分割会被提前排除,实际的时间复杂度可能会低于理论的最坏情况。

3.5 运行结果

# 示例

print(wordBreak("catsanddog", ["cat", "cats", "and", "sand", "dog"]))

运行结果与预期结果一致

四、打家劫舍

力扣第337题

本题将使用树形动态规划(也称为后序遍历)的思想进行求解。

4.1 具体思路

我们可以为每个节点定义一个两元素数组,表示:

当前节点不被偷时,以该节点为根的子树能够盗取的最高金额。

当前节点被偷时,以该节点为根的子树能够盗取的最高金额。

对于二叉树中的任意节点 root,有两种情况:

如果偷 root 节点,那么不能偷它的左右子节点,但可以偷它的四个孙子节点。

如果不偷 root 节点,那么可以偷它的左右子节点。

详细思路如下:

①对于每个节点,返回一个大小为 2 的数组 amount,其中 amount[0] 表示不偷这个节点所能获得的最大金额,amount[1] 表示偷这个节点所能获得的最大金额。

②后序遍历这棵树,对于每个节点 root:

③先计算它的左子节点提供的两种选择,再计算它的右子节点提供的两种选择。

如果偷 root 节点,那么最大金额是 root.val + left[0] + right[0]。

如果不偷 root 节点,那么最大金额是 max(left[0], left[1]) + max(right[0], right[1])。

最后,根节点的最大金额就是 max(root[0], root[1])。

4.2 思路展示

假设我们有以下的二叉树结构:

在这个例子中,我们需要计算出不触发警报的情况下,小偷能够盗取的最高金额。按照上述算法,我们会后序遍历这棵树,并为每个节点计算两个值:amount[0](不偷这个节点时的最大金额)和 amount[1](偷这个节点时的最大金额)。

逐步分析

对于叶子节点(值为 3 和 1 的节点),它们没有孩子节点,所以 amount[0] = 0(不偷叶子节点时的最大金额为 0),amount[1] = node.val(偷叶子节点时的最大金额为节点本身的值)。

对于值为 2 的节点,它只有一个右孩子(值为 3 的节点):

如果不偷这个节点(即 amount[0]),我们可以选择偷或者不偷它的孩子节点,所以 amount[0] = max(孩子节点的 amount[0], 孩子节点的 amount[1])。

如果偷这个节点(即 amount[1]),我们不能偷它的孩子节点,所以 amount[1] = node.val + 孩子节点的 amount[0]。

对于值为 3 的根节点,它有两个孩子(值为 2 和 3 的节点):

如果不偷根节点(即 amount[0]),我们可以选择偷或者不偷它的左右孩子节点,所以 amount[0] = max(左孩子的 amount[0], 左孩子的 amount[1]) + max(右孩子的 amount[0], 右孩子的 amount[1])。

如果偷根节点(即 amount[1]),我们不能偷它的左右孩子节点,所以 amount[1] = node.val + 左孩子的 amount[0] + 右孩子的 amount[0]。

最后,我们比较根节点不被偷和被偷时的金额,取最大值即可。

以下是具体的计算过程:

叶子节点:

节点3:amount[0] = 0, amount[1] = 3

节点1:amount[0] = 0, amount[1] = 1

节点2:

amount[0] = max(3的 amount[0], 3的 amount[1]) = max(0, 3) = 3

amount[1] = 2 + 3的 amount[0] = 2 + 0 = 2

节点3:

amount[0] = max(2的 amount[0], 2的 amount[1]) + max(1的 amount[0], 1的 amount[1]) = max(3, 2) + max(0, 1) = 3 + 1 = 4

amount[1] = 3 + 2的 amount[0] + 1的 amount[0] = 3 + 3 + 0 = 6

根节点的最大金额:

max(amount[0], amount[1]) = max(4, 6) = 6

因此,小偷能够盗取的最高金额是 6。

4.3 代码实现

# Definition for a binary tree node.
class TreeNode:def __init__(self, val=0, left=None, right=None):self.val = valself.left = leftself.right = rightdef rob(root):# res[0] 表示不偷 root 的情况下,最大金额# res[1] 表示偷 root 的情况下,最大金额def dfs(node):if not node:return [0, 0]left = dfs(node.left)right = dfs(node.right)# 不偷当前节点,左右孩子可以偷或不偷,取最大值rob_current = node.val + left[0] + right[0]# 偷当前节点,左右孩子都不能偷not_rob_current = max(left[0], left[1]) + max(right[0], right[1])return [not_rob_current, rob_current]result = dfs(root)return max(result[0], result[1])# 示例
root = TreeNode(3)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.right = TreeNode(3)
root.right.right = TreeNode(1)
print(rob(root))  # 输出: 7

4.4 复杂度分析

代码中的 rob 函数使用了深度优先搜索(DFS)来实现树形动态规划。对于每个节点,dfs 函数都会计算两个值:不偷这个节点时的最大金额和偷这个节点时的最大金额。这个过程会对每个节点访问一次,因此时间复杂度和空间复杂度都与树中节点的数量有关。

时间复杂度:对于每个节点,我们只访问一次,并且在每次访问时只进行常数时间的操作(除了递归调用)。因此,时间复杂度是 O(N),其中 N 是树中节点的数量。

空间复杂度:空间复杂度主要由递归栈的深度决定,这取决于树的高度。在最坏的情况下(树完全不平衡,退化为链表),空间复杂度为 O(N)。在最好的情况下(树完全平衡),空间复杂度为 O(logN)。因此,空间复杂度介于 O(logN) 到 O(N) 之间,具体取决于输入树的形状。

4.5 运行结果

示例

输入: root = [3,2,3,null,3,null,1]

对应的树结构如下

运行结果与预期一致。

结尾语

2024-2-3 小年快乐
天气:🌧️,3~5摄氏度

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/449857.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Leetcode206:反转链表

一、题目 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表 示例: 输入:head [1,2,3,4,5] 输出:[5,4,3,2,1]输入:head [1,2] 输出:[2,1]输入:head [] 输出&#xff1…

Linux mount

挂载移动硬盘 1、通过 命令 fdisk -l 查看移动硬盘 2、创建 挂载点及文件 mkdir zen 3、mount -t ntfs /dev/sdb1 zen 报错:mount: unknown filesystem type ‘ntfs’ 需要安装ntfs-3g 如下才用编译安装方法: wget https://tuxera.com/opensource/ntf…

【Spark实践6】特征转换FeatureTransformers实践Scala版--补充算子

本节介绍了用于处理特征的算法,大致可以分为以下几组: 提取(Extraction):从“原始”数据中提取特征。转换(Transformation):缩放、转换或修改特征。选择(Selection&…

2024年第三届能源与环境工程国际会议(CFEEE 2024) | Ei&Scopus双检索

会议简介 Brief Introduction 2024年第三届能源与环境工程国际会议(CFEEE 2024) 会议时间:2024年12月12日-14日 召开地点:澳大利亚凯恩斯 大会官网:CFEEE 2024-2024 International Conference on Frontiers of Energy and Environment Engine…

【乳腺肿瘤诊断分类及预测】基于Elman神经网络

课题名称:基于Elman神经网络的乳腺肿瘤诊断分类及预测 版本日期:2023-05-15 运行方式: 直接运行Elman0501.m 文件即可 代码获取方式:私信博主或QQ:491052175 模型描述: 威斯康辛大学医学院经过多年的收集和整理&a…

20240202在WIN10下使用whisper.cpp

20240202在WIN10下使用whisper.cpp 2024/2/2 14:15 【结论:在Windows10下,确认large模式识别7分钟中文视频,需要83.7284 seconds,需要大概1.5分钟!效率太差!】 83.7284/4200.1993533333333333333333333333…

云计算基础(云计算概述)

目录 一、云计算概述 1.1 云计算的概念 1.1.1 云计算解决的问题 1.1.2 云计算的概念 1.1.3 云计算的组成 1.2 云计算主要特征 1.2.1 按需自助服务 1.2.2 泛在接入 1.2.3 资源池化 1.2.4 快速伸缩性 1.2.5 服务可度量 1.3 云计算服务模式 1.3.1 软件即服务(Softwar…

海外IP代理:解锁网络边界的实战利器

文章目录 引言:正文:一、Roxlabs全球IP代理服务概览特点:覆盖范围:住宅IP真实性:性价比:在网络数据采集中的重要性: 二、实战应用案例一:跨境电商竞品分析步骤介绍:代码示…

蓝桥杯备战——12.PCF8591芯片的使用

目录 1.芯片简介2.读写时序3.控制字4.代码封装库5.原理图分析6.使用示例 1.芯片简介 截取自NXP的PCF8591芯片数据手册,我把重点关注部分划出来了,请务必自行阅读一遍数据手册! 2.读写时序 ①器件地址: Bit0决定是读还是写操作&…

mcu短时间内发生多次中断,如何解决中断丢失问题?

问题 嵌入式开发中,如果中断A的处理函数执行时间长,某段时间内,快速来了2个中断A(例如:外部管脚输入信号变化),则会导致第2个中断丢失。 我有几个疑问: 1.目前市面上的芯片,是否支持缓存中断标志…

微信小程序实现吸顶、网格、瀑布流布局

微信小程序开发通常是在webview模式下编写,但是对小程序的渲染性能有一定的追求,就需要使用Skyline模式进行渲染,同时在这种模式下有也有一些特殊的组件,可以轻松的实现想要的效果,本文将介绍在Skyline模式下如何实现吸…

vue全家桶之路由管理Vue-Router

一、前端路由的发展历程 1.认识前端路由 路由其实是网络工程中的一个术语: 在架构一个网络时,非常重要的两个设备就是路由器和交换机。当然,目前在我们生活中路由器也是越来越被大家所熟知,因为我们生活中都会用到路由器&#…