Go-Python-Java-C-LeetCode高分解法-第九周合集

news2025/1/9 0:08:17

前言

本题解Go语言部分基于 LeetCode-Go
其他部分基于本人实践学习
个人题解GitHub连接:LeetCode-Go-Python-Java-C
欢迎订阅CSDN专栏,每日一题,和博主一起进步
LeetCode专栏
我搜集到了50道精选题,适合速成概览大部分常用算法
突破算法迷宫:精选50道-算法刷题指南在这里插入图片描述

文章目录

  • 前言
  • [57. Insert Interval](https://leetcode.com/problems/insert-interval/)
    • 题目
    • 题目大意
    • 解题思路
    • 代码
    • Go
    • Python
    • Java
    • Cpp
  • [58. Length of Last Word](https://leetcode.com/problems/length-of-last-word/)
    • 题目
    • 题目大意
    • 解题思路
    • 代码
    • Python
    • Java
    • Cpp
  • [59. Spiral Matrix II](https://leetcode.com/problems/spiral-matrix-ii/)
    • 题目
    • 题目大意
    • 解题思路
    • 代码
    • Go
    • Python
    • Java
    • Cpp
  • [60. Permutation Sequence](https://leetcode.com/problems/permutation-sequence/)
    • 题目
    • 题目大意
    • 解题思路
    • Go
    • Python
    • Java
    • Cpp
  • [61. Rotate List](https://leetcode.com/problems/rotate-list/description/)
    • 题目
    • 题目大意
    • 解题思路
    • 代码
    • Go
    • Python
    • Java
    • Cpp
  • [62. Unique Paths](https://leetcode.com/problems/unique-paths/)
    • 题目
    • 题目大意
    • 解题思路
    • 代码
    • Go
    • Python
    • Java
    • Cpp
  • [63. Unique Paths II](https://leetcode.com/problems/unique-paths-ii/)
    • 题目
    • 题目大意
    • 解题思路
    • 代码
    • Go
    • Python
    • Java
    • Cpp

57. Insert Interval

题目

Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary).

You may assume that the intervals were initially sorted according to their start times.

Example 1:

Input: intervals = [[1,3],[6,9]], newInterval = [2,5]
Output: [[1,5],[6,9]]

Example 2:

Input: intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]
Output: [[1,2],[3,10],[12,16]]
Explanation: Because the new interval [4,8] overlaps with [3,5],[6,7],[8,10].

题目大意

这一题是第 56 题的加强版。给出多个没有重叠的区间,然后再给一个区间,要求把如果有重叠的区间进行合并。

解题思路

可以分 3 段处理,先添加原来的区间,即在给的 newInterval 之前的区间。然后添加 newInterval ,注意这里可能需要合并多个区间。最后把原来剩下的部分添加到最终结果中即可。
以下是每个版本的解题思路:

Go 版本解题思路:

  1. 初始化 leftright 分别为 newInterval 的起始和结束值,以及一个 merged 标志用于跟踪是否已经合并了 newInterval
  2. 使用 for 循环遍历原始区间 intervals
  3. 对于每个区间,检查三种情况:
    • 如果该区间在 newInterval 的右侧且无交集,则将 newInterval 添加到结果中,并将 merged 设置为 true。然后将当前区间添加到结果中。
    • 如果该区间在 newInterval 的左侧且无交集,则直接将当前区间添加到结果中。
    • 如果存在交集,更新 leftright 以合并区间。
  4. 循环结束后,如果 newInterval 没有与任何区间合并(!merged),则将其添加到结果中。
  5. 返回最终结果。

Python 版本解题思路:

  1. 初始化一个空列表 res 用于存储合并后的区间。
  2. 使用 for 循环遍历原始区间列表 intervals
  3. 对于每个区间,检查三种情况:
    • 如果当前区间的结束位置小于 newInterval 的起始位置,直接将当前区间添加到 res
    • 如果当前区间的起始位置大于 newInterval 的结束位置,将 newInterval 和后面的区间都添加到 res
    • 否则,合并当前区间和 newInterval,更新 newInterval 的起始和结束位置。
  4. newInterval 添加到 res
  5. 返回最终结果 res

Java 版本解题思路:

  1. 初始化一个 ArrayList 类型的结果列表 ret
  2. 使用 for 循环遍历原始区间数组 intervals
  3. 处理三种情况:
    • 左侧区间:如果当前区间的结束位置小于 newInterval 的起始位置,将当前区间添加到 ret
    • 区间重叠:如果当前区间与 newInterval 有重叠,合并它们并更新 newInterval 的起始和结束值。
    • 右侧区间:如果当前区间的起始位置大于 newInterval 的结束位置,将当前区间添加到 ret
  4. newInterval 添加到 ret
  5. ret 转换为数组形式并返回最终结果。

C++ 版本解题思路:

  1. 初始化一个 vector<vector<int>> 类型的结果向量 result
  2. 使用 for 循环遍历原始区间向量 intervals
  3. 处理三种情况:
    • 左侧区间:如果当前区间的结束位置小于 newInterval 的起始位置,将当前区间添加到 result
    • 区间重叠:如果当前区间与 newInterval 有重叠,合并它们并更新 newInterval 的起始和结束值。
    • 右侧区间:如果当前区间的起始位置大于 newInterval 的结束位置,将当前区间添加到 result
  4. newInterval 添加到 result
  5. 返回最终结果 result

这些解题思路的核心思想是遍历原始区间,处理不同情况下的区间合并和添加操作,最终得到合并后的结果。要解决这个问题,需要理解如何比较区间的位置关系、如何合并区间以及如何使用循环和条件语句来控制程序逻辑。

代码

Go

func insert(intervals [][]int, newInterval []int) (ans [][]int) {
    left, right := newInterval[0], newInterval[1]
    merged := false
    for _, interval := range intervals {
        if interval[0] > right {
            // 在插入区间的右侧且无交集
            if !merged {
                ans = append(ans, []int{left, right})
                merged = true
            }
            ans = append(ans, interval)
        } else if interval[1] < left {
            // 在插入区间的左侧且无交集
            ans = append(ans, interval)
        } else {
            // 与插入区间有交集,计算它们的并集
            left = min(left, interval[0])
            right = max(right, interval[1])
        }
    }
    if !merged {
        ans = append(ans, []int{left, right})
    }
    return
}

func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

Python

class Solution:
    def insert(self, intervals, newInterval):
        # 初始化结果列表
        res = []
        
        # 遍历区间列表
        for interval in intervals:
            # 如果当前区间的结束位置小于新区间的起始位置,直接将当前区间加入结果
            if interval[1] < newInterval[0]:
                res.append(interval)
            # 如果当前区间的起始位置大于新区间的结束位置,将新区间和后面的区间都加入结果
            elif interval[0] > newInterval[1]:
                res.append(newInterval)
                newInterval = interval
            else:
                # 否则,合并当前区间和新区间,更新新区间的起始和结束位置
                newInterval[0] = min(newInterval[0], interval[0])
                newInterval[1] = max(newInterval[1], interval[1])
        
        # 将新区间加入结果
        res.append(newInterval)
        
        return res

Java

class Solution {
    public int[][] insert(int[][] intervals, int[] newInterval) {
        List<int[]> ret = new ArrayList<>();

        int i = 0;
        int n = intervals.length;

        // 左侧区间:将结束位置小于新区间的起始位置的区间加入结果
        while (i < n && intervals[i][1] < newInterval[0]) {
            ret.add(intervals[i]);
            i++;
        }

        // 区间重叠:合并重叠的区间,更新新区间的起始和结束值
        while (i < n && intervals[i][0] <= newInterval[1] && intervals[i][1] >= newInterval[0]) {
            newInterval[0] = Math.min(intervals[i][0], newInterval[0]);
            newInterval[1] = Math.max(newInterval[1], intervals[i][1]);
            i++;
        }
        ret.add(newInterval);

        // 右侧区间:将起始位置大于新区间的结束位置的区间加入结果
        while (i < n && intervals[i][0] > newInterval[1]) {
            ret.add(intervals[i]);
            i++;
        }

        // 将结果转换为数组形式
        int[][] ans = new int[ret.size()][];
        for (int k = 0; k < ret.size(); ++k) {
            ans[k] = ret.get(k);
        }
        return ans;
    }
}

Cpp

class Solution {
public:
    vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {
        vector<vector<int>> result;
        int n = intervals.size();
        int i = 0;

        // 左侧区间:将结束位置小于新区间的起始位置的区间加入结果
        while (i < n && intervals[i][1] < newInterval[0]) {
            result.push_back(intervals[i]);
            i++;
        }

        // 区间重叠:合并重叠的区间,更新新区间的起始和结束值
        while (i < n && intervals[i][0] <= newInterval[1] && intervals[i][1] >= newInterval[0]) {
            newInterval[0] = min(intervals[i][0], newInterval[0]);
            newInterval[1] = max(intervals[i][1], newInterval[1]);
            i++;
        }
        result.push_back(newInterval);

        // 右侧区间:将起始位置大于新区间的结束位置的区间加入结果
        while (i < n) {
            result.push_back(intervals[i]);
            i++;
        }

        return result;
    }
};

以下是每个版本所需要掌握的详细基础知识:

Go 版本:

  1. Slice(切片):Go 中的切片是动态数组,这个版本中使用切片来处理 intervals 和结果集。
  2. For 循环:了解 Go 中的 for 循环语法,它用于遍历切片和执行循环操作。
  3. 条件语句:理解 ifelse 条件语句,这里使用条件语句来判断区间的位置关系和重叠。
  4. 函数定义和调用:了解如何定义和调用函数,这里有两个自定义函数 minmax,用于求最小值和最大值。
  5. 切片操作:了解如何使用切片来追加元素,这里使用 append 函数将元素添加到结果集中。

Python 版本:

  1. 列表(List):Python 中的列表是动态数组,这个版本中使用列表来处理 intervals 和结果集。
  2. For 循环:了解 Python 中的 for 循环语法,它用于遍历列表和执行循环操作。
  3. 条件语句:理解 ifelse 条件语句,这里使用条件语句来判断区间的位置关系和重叠。
  4. 类和方法:这个版本使用一个类 Solution,需要了解如何定义类和类方法,并且如何调用类方法。

Java 版本:

  1. 数组:Java 中的数组是固定大小的数据结构,这个版本中使用二维数组来处理 intervals 和结果集。
  2. For 循环:了解 Java 中的 for 循环语法,它用于遍历数组和执行循环操作。
  3. 条件语句:理解 ifelse 条件语句,这里使用条件语句来判断区间的位置关系和重叠。
  4. 列表(List):Java 中有 List 接口和 ArrayList 类,但这里没有使用它们。你需要了解如何使用数组来存储和操作数据。
  5. 类和方法:这个版本使用一个类 Solution,需要了解如何定义类和类方法,并且如何调用类方法。

C++ 版本:

  1. 数组:C++ 中的数组是固定大小的数据结构,这个版本中使用二维数组来处理 intervals 和结果集。
  2. For 循环:了解 C++ 中的 for 循环语法,它用于遍历数组和执行循环操作。
  3. 条件语句:理解 ifelse 条件语句,这里使用条件语句来判断区间的位置关系和重叠。
  4. 向量(Vector):C++ 中的 vector 是动态数组,但这里没有使用它。你需要了解如何使用数组来存储和操作数据。
  5. 类和函数:这个版本没有使用类,但使用了函数。需要了解如何定义函数和如何调用函数。

总体来说,无论你选择哪个版本,你需要了解数组或列表的基本操作、循环和条件语句的使用,以及如何定义和调用函数或方法。此外,不同编程语言有不同的语法和特性,所以你需要熟悉所选语言的语法和特点来理解和修改这些代码。

58. Length of Last Word

题目

Given a string s consisting of some words separated by some number of spaces, return the length of the last word in the string.

A word is a maximal substring consisting of non-space characters only.

Example 1:

Input: s = "Hello World"
Output: 5
Explanation: The last word is "World" with length 5.

Example 2:

Input: s = "   fly me   to   the moon  "
Output: 4
Explanation: The last word is "moon" with length 4.

Example 3:

Input: s = "luffy is still joyboy"
Output: 6
Explanation: The last word is "joyboy" with length 6.

Constraints:

  • 1 <= s.length <= 104
  • s consists of only English letters and spaces ' '.
  • There will be at least one word in s.

题目大意

给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中最后一个单词的长度。单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。

解题思路

  • 先从后过滤掉空格找到单词尾部,再从尾部向前遍历,找到单词头部,最后两者相减,即为单词的长度。
    以下是每个版本的解题思路的详细介绍:

Go 版本解题思路:

  1. 首先,计算输入字符串的长度,以便后续的操作。

  2. 初始化一个变量 last,将其设置为字符串的最后一个字符的索引,即 length - 1

  3. 从字符串的末尾开始向前遍历,通过逐步减少 last 的值来跳过末尾的空格字符,直到找到最后一个单词的末尾。

  4. 如果整个字符串都是空格,那么直接返回 0。

  5. 接下来,从 last 开始向前遍历,找到最后一个单词的开头,通过逐步减少 first 的值,直到找到空格字符或达到字符串的开头。

  6. 最后,通过计算 lastfirst 之间的距离,即 last - first,得到最后一个单词的长度,然后返回这个长度作为结果。

Python 版本解题思路:

  1. 首先,计算输入字符串的长度,以便后续的操作。

  2. 初始化一个变量 last,将其设置为字符串的最后一个字符的索引,即 length - 1

  3. 从字符串的末尾开始向前遍历,通过逐步减少 last 的值来跳过末尾的空格字符,直到找到最后一个单词的末尾。

  4. 如果整个字符串都是空格,那么直接返回 0。

  5. 接下来,从 last 开始向前遍历,找到最后一个单词的开头,通过逐步减少 first 的值,直到找到空格字符或达到字符串的开头。

  6. 最后,通过计算 lastfirst 之间的距离,即 last - first,得到最后一个单词的长度,然后返回这个长度作为结果。

Java 版本解题思路:

  1. 首先,计算输入字符串的长度,以便后续的操作。

  2. 初始化一个变量 last,将其设置为字符串的最后一个字符的索引,即 length - 1

  3. 从字符串的末尾开始向前遍历,通过逐步减少 last 的值来跳过末尾的空格字符,直到找到最后一个单词的末尾。

  4. 如果整个字符串都是空格,那么直接返回 0。

  5. 接下来,从 last 开始向前遍历,找到最后一个单词的开头,通过逐步减少 first 的值,直到找到空格字符或达到字符串的开头。

  6. 最后,通过计算 lastfirst 之间的距离,即 last - first,得到最后一个单词的长度,然后返回这个长度作为结果。

C++ 版本解题思路:

  1. 首先,计算输入字符串的长度,以便后续的操作。

  2. 初始化一个变量 last,将其设置为字符串的最后一个字符的索引,即 length - 1

  3. 从字符串的末尾开始向前遍历,通过逐步减少 last 的值来跳过末尾的空格字符,直到找到最后一个单词的末尾。

  4. 如果整个字符串都是空格,那么直接返回 0。

  5. 接下来,从 last 开始向前遍历,找到最后一个单词的开头,通过逐步减少 first 的值,直到找到空格字符或达到字符串的开头。

  6. 最后,通过计算 lastfirst 之间的距离,即 last - first,得到最后一个单词的长度,然后返回这个长度作为结果。

总的来说,这四个版本的解题思路都是基本相同的,都是通过从字符串末尾向前遍历来寻找最后一个单词的末尾和开头,然后计算长度并返回。关键是了解如何操作字符串的长度、索引和循环,以及如何处理边界条件。

代码

func lengthOfLastWord(s string) int {
	// 获取字符串的最后一个字符的索引
	last := len(s) - 1
	// 循环直到找到字符串末尾不是空格的字符位置
	for last >= 0 && s[last] == ' ' {
		last--
	}
	// 如果字符串全是空格,则返回0
	if last < 0 {
		return 0
	}
	// 从最后一个字符开始,向前查找第一个空格之前的字符
	first := last
	for first >= 0 && s[first] != ' ' {
		first--
	}
	// 返回最后一个单词的长度(最后一个字符的位置 - 第一个字符的位置)
	return last - first
}

Python

class Solution:
    def lengthOfLastWord(self, s: str) -> int:
        # 获取字符串的长度
        length = len(s)
        # 初始化最后一个字符的索引
        last = length - 1
        
        # 从字符串末尾向前查找,跳过末尾的空格字符
        while last >= 0 and s[last] == ' ':
            last -= 1
        
        # 如果字符串全是空格,则返回0
        if last < 0:
            return 0
        
        # 从最后一个字符开始,向前查找第一个空格之前的字符
        first = last
        while first >= 0 and s[first] != ' ':
            first -= 1
        
        # 返回最后一个单词的长度(最后一个字符的位置 - 第一个字符的位置)
        return last - first

Java

class Solution {
    public int lengthOfLastWord(String s) {
        // 获取字符串的长度
        int length = s.length();
        // 初始化最后一个字符的索引
        int last = length - 1;
        
        // 从字符串末尾向前查找,跳过末尾的空格字符
        while (last >= 0 && s.charAt(last) == ' ') {
            last--;
        }
        
        // 如果字符串全是空格,则返回0
        if (last < 0) {
            return 0;
        }
        
        // 从最后一个字符开始,向前查找第一个空格之前的字符
        int first = last;
        while (first >= 0 && s.charAt(first) != ' ') {
            first--;
        }
        
        // 返回最后一个单词的长度(最后一个字符的位置 - 第一个字符的位置)
        return last - first;
    }
}

Cpp

class Solution {
public:
    int lengthOfLastWord(string s) {
        // 获取字符串的长度
        int length = s.length();
        // 初始化最后一个字符的索引
        int last = length - 1;
        
        // 从字符串末尾向前查找,跳过末尾的空格字符
        while (last >= 0 && s[last] == ' ') {
            last--;
        }
        
        // 如果字符串全是空格,则返回0
        if (last < 0) {
            return 0;
        }
        
        // 从最后一个字符开始,向前查找第一个空格之前的字符
        int first = last;
        while (first >= 0 && s[first] != ' ') {
            first--;
        }
        
        // 返回最后一个单词的长度(最后一个字符的位置 - 第一个字符的位置)
        return last - first;
    }
};

每个版本的代码所需的基础知识。

Go 版本:

  1. 字符串操作: 了解如何获取字符串的长度,访问字符串中的字符以及字符串切片操作。

  2. 循环和条件语句: 理解 Go 中的 forif 条件语句的使用,以及如何在循环中逐步处理字符串。

Python 版本:

  1. 字符串操作: 熟悉字符串的长度计算(使用 len() 函数)和字符串索引访问。

  2. 字符串遍历: 了解如何使用 for 循环遍历字符串中的字符。

  3. 条件语句: 了解如何使用 if 条件语句来进行条件判断。

Java 版本:

  1. 字符串操作: 了解如何获取字符串的长度(使用 length() 方法)以及如何使用 charAt() 方法访问字符串中的字符。

  2. 循环和条件语句: 理解 Java 中的 while 循环和 if 条件语句的使用,以及如何在循环中逐步处理字符串。

C++ 版本:

  1. 字符串操作: 熟悉 C++ 中的字符串类(std::string)的基本操作,包括获取字符串长度(使用 length() 方法)和访问字符串中的字符。

  2. 循环和条件语句: 了解 C++ 中的 while 循环和 if 条件语句的使用,以及如何在循环中逐步处理字符串。

在这些版本的代码中,最关键的基础知识包括字符串操作、循环和条件语句的使用。同时,还需要了解如何进行字符串的索引访问和遍历,以及如何处理边界条件,例如字符串为空或仅包含空格的情况。这些是解决这个问题所需的基本概念和技能。

59. Spiral Matrix II

题目

Given a positive integer n, generate a square matrix filled with elements from 1 to n2 in spiral order.

Example:

Input: 3
Output:
[
 [ 1, 2, 3 ],
 [ 8, 9, 4 ],
 [ 7, 6, 5 ]
]

题目大意

给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。

解题思路

题目要求生成一个正方形矩阵,矩阵的元素按照螺旋顺序从1到n^2依次填充。

首先,我们需要初始化一个n×n的矩阵,并定义四个方向,分别是向右、向下、向左、向上。然后按照螺旋顺序依次填充矩阵。

具体步骤如下:

定义一个n×n的矩阵,初始化所有元素为0。
定义四个方向的偏移量,分别是向右、向下、向左、向上,用来控制填充顺序。
根据题目要求,依次填充矩阵中的元素,同时更新当前位置和下一个位置的坐标,以及判断是否需要改变方向。
继续填充直到所有位置都被填充。

这些版本的解题思路共同点是通过模拟螺旋填充的过程,依次填充矩阵的每个位置。在填充的过程中,根据规定的螺旋顺序不断更新坐标,并在达到边界或已访问过的位置时改变填充方向。

代码

Go

// 定义一个名为 generateMatrix 的函数,接收一个整数参数 n,表示要生成的螺旋矩阵的大小
func generateMatrix(n int) [][]int {
    // 如果 n 为 0,返回一个空的二维整数数组
    if n == 0 {
        return [][]int{}
    }
    // 如果 n 为 1,返回一个包含元素 1 的二维整数数组
    if n == 1 {
        return [][]int{[]int{1}}
    }
    
    // 初始化结果矩阵 res,访问标记矩阵 visit,螺旋方向 round,以及当前坐标 x 和 y
    res, visit, round, x, y, spDir := make([][]int, n), make([][]int, n), 0, 0, 0, [][]int{
        []int{0, 1},  // 朝右
        []int{1, 0},  // 朝下
        []int{0, -1}, // 朝左
        []int{-1, 0}, // 朝上
    }
    
    // 初始化结果矩阵和访问标记矩阵
    for i := 0; i < n; i++ {
        visit[i] = make([]int, n)
        res[i] = make([]int, n)
    }
    
    // 标记起始点已访问
    visit[x][y] = 1
    res[x][y] = 1
    
    // 循环填充矩阵
    for i := 0; i < n*n; i++ {
        // 根据当前螺旋方向更新坐标
        x += spDir[round%4][0]
        y += spDir[round%4][1]
        
        // 检查是否需要改变螺旋方向
        if (x == 0 && y == n-1) || (x == n-1 && y == n-1) || (y == 0 && x == n-1) {
            round++
        }
        
        // 如果坐标越界,返回结果矩阵
        if x > n-1 || y > n-1 || x < 0 || y < 0 {
            return res
        }
        
        // 如果当前坐标未被访问过,标记为已访问并填充值
        if visit[x][y] == 0 {
            visit[x][y] = 1
            res[x][y] = i + 2
        }
        
        // 根据当前螺旋方向检查下一个位置是否已经访问,如果访问过则改变方向
        switch round % 4 {
        case 0:
            if y+1 <= n-1 && visit[x][y+1] == 1 {
                round++
                continue
            }
        case 1:
            if x+1 <= n-1 && visit[x+1][y] == 1 {
                round++
                continue
            }
        case 2:
            if y-1 >= 0 && visit[x][y-1] == 1 {
                round++
                continue
            }
        case 3:
            if x-1 >= 0 && visit[x-1][y] == 1 {
                round++
                continue
            }
        }
    }
    
    // 返回生成的螺旋矩阵
    return res
}

Python

class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        # 初始化结果矩阵和访问标记矩阵
        result = [[0] * n for _ in range(n)]
        visited = [[False] * n for _ in range(n)]
        
        # 定义方向数组,表示右、下、左、上四个方向
        directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
        
        x, y, direction = 0, 0, 0
        
        for num in range(1, n * n + 1):
            result[x][y] = num
            visited[x][y] = True
            
            # 计算下一个坐标
            next_x, next_y = x + directions[direction][0], y + directions[direction][1]
            
            # 如果下一个坐标越界或已访问过,则改变方向
            if next_x < 0 or next_x >= n or next_y < 0 or next_y >= n or visited[next_x][next_y]:
                direction = (direction + 1) % 4
                next_x, next_y = x + directions[direction][0], y + directions[direction][1]
            
            x, y = next_x, next_y
        
        return result

Java

class Solution {
    public int[][] generateMatrix(int n) {
        // 初始化结果矩阵和访问标记矩阵
        int[][] result = new int[n][n];
        boolean[][] visited = new boolean[n][n];
        
        // 定义方向数组,表示右、下、左、上四个方向
        int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
        
        int x = 0, y = 0, direction = 0;
        
        for (int num = 1; num <= n * n; num++) {
            result[x][y] = num;
            visited[x][y] = true;
            
            // 计算下一个坐标
            int nextX = x + directions[direction][0];
            int nextY = y + directions[direction][1];
            
            // 如果下一个坐标越界或已访问过,则改变方向
            if (nextX < 0 || nextX >= n || nextY < 0 || nextY >= n || visited[nextX][nextY]) {
                direction = (direction + 1) % 4;
                nextX = x + directions[direction][0];
                nextY = y + directions[direction][1];
            }
            
            x = nextX;
            y = nextY;
        }
        
        return result;
    }
}

Cpp

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        // 初始化结果矩阵和访问标记矩阵
        vector<vector<int>> result(n, vector<int>(n, 0));
        vector<vector<bool>> visited(n, vector<bool>(n, false));
        
        // 定义方向数组,表示右、下、左、上四个方向
        vector<vector<int>> directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
        
        int x = 0, y = 0, direction = 0;
        
        for (int num = 1; num <= n * n; num++) {
            result[x][y] = num;
            visited[x][y] = true;
            
            // 计算下一个坐标
            int nextX = x + directions[direction][0];
            int nextY = y + directions[direction][1];
            
            // 如果下一个坐标越界或已访问过,则改变方向
            if (nextX < 0 || nextX >= n || nextY < 0 || nextY >= n || visited[nextX][nextY]) {
                direction = (direction + 1) % 4;
                nextX = x + directions[direction][0];
                nextY = y + directions[direction][1];
            }
            
            x = nextX;
            y = nextY;
        }
        
        return result;
    }
};

好的,我会分别介绍每个版本的代码涉及的基础知识和算法思路。

Go 版本
基础知识:

  1. 函数定义与调用: func generateMatrix(n int) [][]int 定义了一个接受整数参数 n 的函数。
  2. 条件判断: 使用 if 条件判断语句进行不同情况的处理。
  3. 数组操作: 使用二维数组表示矩阵,通过数组索引进行访问和修改。

Python 版本
基础知识:

  1. 类和方法: 使用 class Solution 定义一个类,其中的 generateMatrix 方法是解决问题的主要逻辑。
  2. 列表(List)操作: 使用列表表示矩阵,通过索引访问和修改元素。
  3. 循环与条件判断: 使用 for 循环和 if 条件判断进行逻辑控制。

Java 版本
基础知识:

  1. 类和方法: 使用 class Solution 定义一个类,其中的 generateMatrix 方法是解决问题的主要逻辑。
  2. 二维数组操作: 使用二维数组表示矩阵,通过数组索引进行访问和修改。
  3. 循环与条件判断: 使用 for 循环和 if 条件判断进行逻辑控制。

C++ 版本
基础知识:

  1. 类和方法: 使用 class Solution 定义一个类,其中的 generateMatrix 方法是解决问题的主要逻辑。
  2. 二维数组操作: 使用二维数组表示矩阵,通过数组索引进行访问和修改。
  3. 循环与条件判断: 使用 for 循环和 if 条件判断进行逻辑控制。

60. Permutation Sequence

题目

The set [1,2,3,...,*n*] contains a total of n! unique permutations.

By listing and labeling all of the permutations in order, we get the following sequence for n = 3:

  1. "123"
  2. "132"
  3. "213"
  4. "231"
  5. "312"
  6. "321"

Given n and k, return the kth permutation sequence.

Note:

  • Given n will be between 1 and 9 inclusive.
  • Given k will be between 1 and n! inclusive.

Example 1:

Input: n = 3, k = 3
Output: "213"

Example 2:

Input: n = 4, k = 9
Output: "2314"

题目大意

给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。

按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:“123”,“132”,“213”,“231”,“312”,“321”,给定 n 和 k,返回第 k 个排列。

解题思路

下面分别介绍每个版本的解题思路:

Go 版本解题思路:

  1. 创建一个存储阶乘结果的数组 factorial,并初始化第一个元素为 1。
  2. 计算从 1 到 n 的阶乘值,并将结果存储在 factorial 数组中。
  3. 将 k 减去 1,将其转换为从 0 开始的索引,以便在数组中访问。
  4. 创建一个空字符串 ans 用于存储最终的排列结果。
  5. 创建一个 valid 数组,用于标记数字是否已经被使用。初始化所有数字为可用(1)。
  6. 循环计算每个位置上的数字:
    • 计算当前位置上的数字在当前剩余排列中的顺序(order)。
    • 寻找符合顺序的数字,遍历 valid 数组,减去已经使用的数字的数量,找到对应的数字。
    • 将找到的数字添加到 ans 中,并将该数字标记为不可用。
    • 更新 k,去除已经确定的数字对应的排列数。
  7. 返回 ans 作为最终的排列结果。

Python 版本解题思路:

  1. 计算阶乘数组 factorial,其中存储从 1 到 n 的阶乘值。
  2. 将 k 减去 1,将其转换为从 0 开始的索引。
  3. 创建一个空列表 ans 用于存储最终的排列结果。
  4. 创建一个 valid 列表,用于标记数字是否已经被使用。初始化所有数字为可用。
  5. 循环计算每个位置上的数字:
    • 计算当前位置上的数字在当前剩余排列中的顺序(order)。
    • 寻找符合顺序的数字,遍历 valid 列表,减去已经使用的数字的数量,找到对应的数字。
    • 将找到的数字添加到 ans 中,并将该数字标记为不可用。
    • 更新 k,去除已经确定的数字对应的排列数。
  6. 返回 ans 作为最终的排列结果。

Java 版本解题思路:

  1. 计算阶乘数组 factorial,其中存储从 1 到 n 的阶乘值。
  2. 将 k 减去 1,将其转换为从 0 开始的索引。
  3. 创建一个空字符串 ans 用于存储最终的排列结果。
  4. 创建一个整数数组 valid,用于标记数字是否已经被使用。初始化所有数字为可用。
  5. 循环计算每个位置上的数字:
    • 计算当前位置上的数字在当前剩余排列中的顺序(order)。
    • 寻找符合顺序的数字,遍历 valid 数组,减去已经使用的数字的数量,找到对应的数字。
    • 将找到的数字添加到 ans 中,并将该数字标记为不可用。
    • 更新 k,去除已经确定的数字对应的排列数。
  6. 返回 ans 作为最终的排列结果。

C++ 版本解题思路:

  1. 计算阶乘数组 factorial,其中存储从 1 到 n 的阶乘值。
  2. 将 k 减去 1,将其转换为从 0 开始的索引。
  3. 创建一个空字符串 ans 用于存储最终的排列结果。
  4. 创建一个整数数组 valid,用于标记数字是否已经被使用。初始化所有数字为可用。
  5. 循环计算每个位置上的数字:
    • 计算当前位置上的数字在当前剩余排列中的顺序(order)。
    • 寻找符合顺序的数字,遍历 valid 数组,减去已经使用的数字的数量,找到对应的数字。
    • 将找到的数字添加到 ans 中,并将该数字标记为不可用。
    • 更新 k,去除已经确定的数字对应的排列数。
  6. 返回 ans 作为最终的排列结果。

无论使用哪种编程语言版本,核心思路都是相同的,只是具体的语法和数据结构会有所不同。通过理解这些思路,你可以实现解决这个问题的代码。## 代码

Go

func getPermutation(n int, k int) string {
    // 创建一个数组 factorial 用于存储阶乘结果
    factorial := make([]int, n)
    factorial[0] = 1

    // 计算 1 到 n 的阶乘值并存储在 factorial 数组中
    for i := 1; i < n; i++ {
        factorial[i] = factorial[i - 1] * i
    }

    k-- // 减去 1,将 k 转换为从 0 开始的索引

    ans := "" // 存储最终的排列结果
    valid := make([]int, n + 1) // 创建一个数组 valid 用于标记数字是否已经被使用
    for i := 0; i < len(valid); i++ {
        valid[i] = 1 // 初始化 valid 数组,所有数字都可用
    }

    // 循环计算每个位置上的数字
    for i := 1; i <= n; i++ {
        // 计算当前位置上的数字在当前剩余排列中的顺序
        order := k / factorial[n - i] + 1
        
        // 寻找符合顺序的数字
        for j := 1; j <= n; j++ {
            order -= valid[j]
            if order == 0 {
                ans += strconv.Itoa(j) // 将找到的数字添加到结果中
                valid[j] = 0 // 将已经使用的数字标记为不可用
                break
            }
        }
        
        k %= factorial[n - i] // 更新 k,去除已经确定的数字对应的排列数
    }

    return ans // 返回最终的排列结果
}

Python

class Solution:
    def getPermutation(self, n: int, k: int) -> str:
        # 计算阶乘数组
        factorial = [1]
        for i in range(1, n):
            factorial.append(factorial[-1] * i)

        k -= 1  # 将 k 转换为从 0 开始的索引

        ans = []  # 存储最终的排列结果
        valid = [1] * (n + 1)  # 创建一个数组用于标记数字是否已经被使用

        # 循环计算每个位置上的数字
        for i in range(1, n + 1):
            # 计算当前位置上的数字在当前剩余排列中的顺序
            order = k // factorial[n - i] + 1
            
            # 寻找符合顺序的数字
            for j in range(1, n + 1):
                order -= valid[j]
                if order == 0:
                    ans.append(str(j))  # 将找到的数字添加到结果中
                    valid[j] = 0  # 将已经使用的数字标记为不可用
                    break
            
            k %= factorial[n - i]  # 更新 k,去除已经确定的数字对应的排列数

        return ''.join(ans)  # 返回最终的排列结果

Java

class Solution {
    public String getPermutation(int n, int k) {
        // 计算阶乘数组
        int[] factorial = new int[n];
        factorial[0] = 1;
        for (int i = 1; i < n; i++) {
            factorial[i] = factorial[i - 1] * i;
        }

        k--;  // 将 k 转换为从 0 开始的索引

        StringBuilder ans = new StringBuilder();  // 存储最终的排列结果
        int[] valid = new int[n + 1];
        Arrays.fill(valid, 1);  // 初始化数组,所有数字都可用

        // 循环计算每个位置上的数字
        for (int i = 1; i <= n; i++) {
            // 计算当前位置上的数字在当前剩余排列中的顺序
            int order = k / factorial[n - i] + 1;
            
            // 寻找符合顺序的数字
            for (int j = 1; j <= n; j++) {
                order -= valid[j];
                if (order == 0) {
                    ans.append(j);  // 将找到的数字添加到结果中
                    valid[j] = 0;  // 将已经使用的数字标记为不可用
                    break;
                }
            }
            
            k %= factorial[n - i];  // 更新 k,去除已经确定的数字对应的排列数
        }

        return ans.toString();  // 返回最终的排列结果
    }
}

Cpp

class Solution {
public:
    string getPermutation(int n, int k) {
        // 计算阶乘数组
        vector<int> factorial(n);
        factorial[0] = 1;
        for (int i = 1; i < n; i++) {
            factorial[i] = factorial[i - 1] * i;
        }

        k--;  // 将 k 转换为从 0 开始的索引

        string ans;  // 存储最终的排列结果
        vector<int> valid(n + 1, 1);  // 创建一个数组用于标记数字是否已经被使用

        // 循环计算每个位置上的数字
        for (int i = 1; i <= n; i++) {
            // 计算当前位置上的数字在当前剩余排列中的顺序
            int order = k / factorial[n - i] + 1;
            
            // 寻找符合顺序的数字
            for (int j = 1; j <= n; j++) {
                order -= valid[j];
                if (order == 0) {
                    ans += to_string(j);  // 将找到的数字添加到结果中
                    valid[j] = 0;  // 将已经使用的数字标记为不可用
                    break;
                }
            }
            
            k %= factorial[n - i];  // 更新 k,去除已经确定的数字对应的排列数
        }

        return ans;  // 返回最终的排列结果
    }
};

你需要具备以下基础知识:

Go 版本:

  1. Go 语言基础: 理解 Go 语言的基本语法,包括变量声明、循环、条件语句等。
  2. 切片(Slice): Go 中的切片是一种动态数组,你需要了解如何使用切片来处理和拼接字符串。
  3. 函数: 了解如何声明和调用函数,以及函数参数和返回值的概念。

Python 版本:

  1. Python 基础: 理解 Python 的基本语法,包括变量、列表、循环、条件语句等。
  2. 列表(List): Python 中的列表是一种常用的数据结构,你需要知道如何处理和操作列表。
  3. 字符串操作: 了解如何连接字符串和将整数转换为字符串。

Java 版本:

  1. Java 基础: 了解 Java 的基本语法,包括类、方法、变量声明和循环。
  2. 数组: Java 中的数组是一种常见的数据结构,你需要知道如何声明、初始化和使用数组。
  3. StringBuilder: 了解如何使用 StringBuilder 类来高效地构建字符串。

C++ 版本:

  1. C++ 基础: 了解 C++ 的基本语法,包括变量、数组、循环、条件语句等。
  2. 字符串操作: 了解如何连接字符串和将整数转换为字符串。在 C++ 中,你可以使用字符串流(stringstream)来转换整数为字符串。
  3. 类和对象: 了解如何创建类和对象,以及如何在类中定义成员函数。

不管你选择哪个编程语言版本,你需要理解问题的核心思想,即如何计算第 k 个排列。你还需要了解每个编程语言的语法和特性,以便正确实现算法。不过,问题的核心解决思路在不同版本中是相同的。

61. Rotate List

题目

Given a linked list, rotate the list to the right by k places, where k is non-negative.

Example 1:

Input: 1->2->3->4->5->NULL, k = 2
Output: 4->5->1->2->3->NULL
Explanation:
rotate 1 steps to the right: 5->1->2->3->4->NULL
rotate 2 steps to the right: 4->5->1->2->3->NULL 

Example 2:

Input: 0->1->2->NULL, k = 4
Output: 2->0->1->NULL
Explanation:
rotate 1 steps to the right: 2->0->1->NULL
rotate 2 steps to the right: 1->2->0->NULL
rotate 3 steps to the right: 0->1->2->NULL
rotate 4 steps to the right: 2->0->1->NULL 

题目大意

旋转链表 K 次。

解题思路

这道题需要注意的点是,K 可能很大,K = 2000000000 ,如果是循环肯定会超时。应该找出 O(n) 的复杂度的算法才行。由于是循环旋转,最终状态其实是确定的,利用链表的长度取余可以得到链表的最终旋转结果。

这道题也不能用递归,递归解法会超时。
解决这个问题的思路:

Go 版本解题思路:

  1. 首先,检查是否需要旋转。如果链表为空,只包含一个节点,或者旋转次数 k 为 0,则直接返回原始链表。

  2. 计算链表的长度。使用一个循环遍历链表,计算链表中的节点数量。

  3. 检查是否需要旋转。如果 k 对链表长度取模等于 0,表示不需要旋转,直接返回原始链表。

  4. 找到新头节点的位置。这是通过链表长度和 k 的关系来确定的,即链表长度减去 k 对链表长度取模的结果。

  5. 创建一个新的头节点,并将其指向新头节点的下一个节点。然后断开循环链表,将新链表的尾部指向空。

  6. 返回新链表的头节点。

Python 版本解题思路:

  1. 同样地,首先检查是否需要旋转。如果链表为空,只包含一个节点,或者旋转次数 k 为 0,则直接返回原始链表。

  2. 计算链表的长度。使用一个循环遍历链表,计算链表中的节点数量。

  3. 检查是否需要旋转。如果 k 对链表长度取模等于 0,表示不需要旋转,直接返回原始链表。

  4. 找到新头节点的位置。这是通过链表长度和 k 的关系来确定的,即链表长度减去 k 对链表长度取模的结果。

  5. 更新链表的头尾连接关系,形成新的旋转链表。

  6. 返回新链表的头节点。

Java 版本解题思路:

  1. 同样地,首先检查是否需要旋转。如果链表为空,只包含一个节点,或者旋转次数 k 为 0,则直接返回原始链表。

  2. 计算链表的长度。使用一个循环遍历链表,计算链表中的节点数量。

  3. 检查是否需要旋转。如果 k 对链表长度取模等于 0,表示不需要旋转,直接返回原始链表。

  4. 找到新头节点的位置。这是通过链表长度和 k 的关系来确定的,即链表长度减去 k 对链表长度取模的结果。

  5. 更新链表的头尾连接关系,形成新的旋转链表。

  6. 返回新链表的头节点。

C++ 版本解题思路:

  1. 同样地,首先检查是否需要旋转。如果链表为空,只包含一个节点,或者旋转次数 k 为 0,则直接返回原始链表。

  2. 计算链表的长度。使用一个循环遍历链表,计算链表中的节点数量。

  3. 检查是否需要旋转。如果 k 对链表长度取模等于 0,表示不需要旋转,直接返回原始链表。

  4. 找到新头节点的位置。这是通过链表长度和 k 的关系来确定的,即链表长度减去 k 对链表长度取模的结果。

  5. 更新链表的头尾连接关系,形成新的旋转链表。

  6. 返回新链表的头节点。

无论使用哪种编程语言,解决这个问题的思路都是相似的,都是基于链表的长度和旋转次数 k 来确定新头节点的位置,然后重新连接链表节点以得到旋转后的链表。

代码

Go

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
// ListNode 结构体定义了链表节点,包括一个整数值 Val 和指向下一个节点的指针 Next。

func rotateRight(head *ListNode, k int) *ListNode {
	// 如果链表为空,或者只包含一个节点,或者旋转次数 k 为 0,则直接返回原始链表。
	if head == nil || head.Next == nil || k == 0 {
		return head
	}
	// 创建一个新的头节点 newHead,将其指向原始链表的头节点。
	newHead := &ListNode{Val: 0, Next: head}
	// 计算链表的长度 len。
	len := 0
	cur := newHead
	for cur.Next != nil {
		len++
		cur = cur.Next
	}
	// 如果 k 对链表长度取模等于 0,表示不需要旋转,直接返回原始链表。
	if (k % len) == 0 {
		return head
	}
	// 将链表首尾相连,形成一个循环链表。
	cur.Next = head
	cur = newHead
	// 计算新头节点的位置,即链表长度减去 k 对链表长度取模的结果。
	for i := len - k%len; i > 0; i-- {
		cur = cur.Next
	}
	// 创建一个新的结果链表 res,将其指向新头节点的下一个节点,然后断开循环链表。
	res := &ListNode{Val: 0, Next: cur.Next}
	cur.Next = nil
	// 返回结果链表的头节点。
	return res.Next
}

Python

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution:
    def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
        # 如果链表为空,或者只包含一个节点,或者旋转次数 k 为 0,则直接返回原始链表。
        if not head or not head.next or k == 0:
            return head
        
        # 计算链表的长度 len。
        cur = head
        length = 1
        while cur.next:
            cur = cur.next
            length += 1
        
        # 如果 k 对链表长度取模等于 0,表示不需要旋转,直接返回原始链表。
        if k % length == 0:
            return head
        
        # 找到新头节点的位置,即链表长度减去 k 对链表长度取模的结果。
        cur = head
        for _ in range(length - k % length - 1):
            cur = cur.next
        
        # 更新链表的头尾连接关系,并返回新的头节点。
        new_head = cur.next
        cur.next = None
        cur = new_head
        while cur.next:
            cur = cur.next
        cur.next = head
        
        return new_head

Java

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode rotateRight(ListNode head, int k) {
        // 如果链表为空,或者只包含一个节点,或者旋转次数 k 为 0,则直接返回原始链表。
        if (head == null || head.next == null || k == 0) {
            return head;
        }
        
        // 计算链表的长度 len。
        ListNode cur = head;
        int length = 1;
        while (cur.next != null) {
            cur = cur.next;
            length++;
        }
        
        // 如果 k 对链表长度取模等于 0,表示不需要旋转,直接返回原始链表。
        if (k % length == 0) {
            return head;
        }
        
        // 找到新头节点的位置,即链表长度减去 k 对链表长度取模的结果。
        cur = head;
        for (int i = 0; i < length - k % length - 1; i++) {
            cur = cur.next;
        }
        
        // 更新链表的头尾连接关系,并返回新的头节点。
        ListNode newHead = cur.next;
        cur.next = null;
        cur = newHead;
        while (cur.next != null) {
            cur = cur.next;
        }
        cur.next = head;
        
        return newHead;
    }
}

Cpp

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        // 如果链表为空,或者只包含一个节点,或者旋转次数 k 为 0,则直接返回原始链表。
        if (!head || !head->next || k == 0) {
            return head;
        }
        
        // 计算链表的长度 len。
        ListNode* cur = head;
        int length = 1;
        while (cur->next) {
            cur = cur->next;
            length++;
        }
        
        // 如果 k 对链表长度取模等于 0,表示不需要旋转,直接返回原始链表。
        if (k % length == 0) {
            return head;
        }
        
        // 找到新头节点的位置,即链表长度减去 k 对链表长度取模的结果。
        cur = head;
        for (int i = 0; i < length - k % length - 1; i++) {
            cur = cur->next;
        }
        
        // 更新链表的头尾连接关系,并返回新的头节点。
        ListNode* newHead = cur->next;
        cur->next = nullptr;
        cur = newHead;
        while (cur->next) {
            cur = cur->next;
        }
        cur->next = head;
        
        return newHead;
    }
};

每个版本的基础知识要点:

Go 版本:

  1. 链表:你需要了解链表数据结构,这是问题的核心数据结构。在 Go 中,通常使用 struct 来定义链表节点,包含一个整数值 Val 和指向下一个节点的指针 Next

  2. 条件语句和循环:你需要了解 Go 中的条件语句(if语句)和循环(for语句),因为代码中有一些条件检查和循环遍历链表。

  3. 指针:了解如何在 Go 中使用指针是非常重要的,因为链表的节点是通过指针连接的。你需要知道如何创建和使用指针,以及如何访问指针指向的值。

Python 版本:

  1. 链表:与 Go 版本一样,你需要了解链表数据结构。在 Python 中,通常使用类来定义链表节点,每个节点有一个整数值和一个指向下一个节点的引用。

  2. 条件语句和循环:你需要了解 Python 中的条件语句(if语句)和循环(for语句),因为代码中有一些条件检查和循环遍历链表。

  3. 类和对象:在 Python 中,链表节点通常是类的实例。了解如何定义类、创建对象和访问对象属性是必要的。

Java 版本:

  1. 链表:与 Go 和 Python 版本一样,你需要了解链表数据结构。在 Java 中,链表节点通常是一个自定义类,包含整数值和下一个节点的引用。

  2. 条件语句和循环:了解 Java 中的条件语句(if语句)和循环(for循环)是必要的,因为代码中有条件检查和循环遍历链表。

  3. 类和对象:在 Java 中,链表节点是类的实例。你需要了解如何定义类、创建对象和访问对象的属性和方法。

C++ 版本:

  1. 链表:你需要了解链表数据结构,这是问题的核心数据结构。在 C++ 中,链表节点通常是一个自定义的结构体,包含整数值和下一个节点的指针。

  2. 条件语句和循环:了解 C++ 中的条件语句(if语句)和循环(for循环)是必要的,因为代码中有条件检查和循环遍历链表。

  3. 结构体和指针:了解如何在 C++ 中定义结构体和使用指针非常重要,因为链表节点是结构体,通过指针连接。

在理解这些基础知识的基础上,你将能够理解和修改提供的代码以解决 “Rotate List” 这道题目。链表操作是这个问题的核心,所以对链表的理解至关重要。如果你对链表操作不熟悉,建议先学习链表的基本操作,然后再尝试解决这个问题。

62. Unique Paths

题目

A robot is located at the top-left corner of a m x n grid (marked ‘Start’ in the diagram below).

The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked ‘Finish’ in the diagram below).

How many possible unique paths are there?

Above is a 7 x 3 grid. How many possible unique paths are there?

Note: m and n will be at most 100.

Example 1:

Input: m = 3, n = 2
Output: 3
Explanation:
From the top-left corner, there are a total of 3 ways to reach the bottom-right corner:
1. Right -> Right -> Down
2. Right -> Down -> Right
3. Down -> Right -> Right

Example 2:

Input: m = 7, n = 3
Output: 28

题目大意

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。问总共有多少条不同的路径?

解题思路

  • 这是一道简单的 DP 题。输出地图上从左上角走到右下角的走法数。
  • 由于机器人只能向右走和向下走,所以地图的第一行和第一列的走法数都是 1,地图中任意一点的走法数是 dp[i][j] = dp[i-1][j] + dp[i][j-1]
    当然,让我为每个版本详细介绍解题思路:

Go 版本解题思路

  1. 创建二维切片: 首先,我们创建一个大小为 n 的二维切片 dp,用于存储每个位置的唯一路径数。Go 中的切片是动态数组,可以方便地动态扩展。

  2. 初始化第一行和第一列: 遍历二维切片,初始化第一行和第一列的路径数为 1,因为在这些位置上,机器人只有一种路径可以到达,即向右或向下移动。

  3. 填充二维切片: 从第二行和第二列开始,遍历每个位置 (i, j),用动态规划的思想计算出到达这个位置的唯一路径数。路径数等于上方格子的路径数加上左边格子的路径数,即 dp[i][j] = dp[i-1][j] + dp[i][j-1]

  4. 返回结果: 最终,右下角格子的路径数就是从左上角到右下角的唯一路径数,即 dp[n-1][m-1]

Python 版本解题思路

  1. 创建二维列表: 首先,我们创建一个大小为 n x m 的二维列表 dp,用于存储每个位置的唯一路径数。Python 中的列表是动态数组,可以方便地动态扩展。

  2. 初始化第一行和第一列: 遍历二维列表,初始化第一行和第一列的路径数为 1,因为在这些位置上,机器人只有一种路径可以到达,即向右或向下移动。

  3. 填充二维列表: 从第二行和第二列开始,遍历每个位置 (i, j),用动态规划的思想计算出到达这个位置的唯一路径数。路径数等于上方格子的路径数加上左边格子的路径数,即 dp[i][j] = dp[i-1][j] + dp[i][j-1]

  4. 返回结果: 最终,右下角格子的路径数就是从左上角到右下角的唯一路径数,即 dp[n-1][m-1]

Java 版本解题思路

  1. 创建二维数组: 首先,我们创建一个大小为 n x m 的二维数组 dp,用于存储每个位置的唯一路径数。Java 中的二维数组可以表示矩阵。

  2. 初始化第一行和第一列: 遍历二维数组,初始化第一行和第一列的路径数为 1,因为在这些位置上,机器人只有一种路径可以到达,即向右或向下移动。

  3. 填充二维数组: 从第二行和第二列开始,遍历每个位置 (i, j),用动态规划的思想计算出到达这个位置的唯一路径数。路径数等于上方格子的路径数加上左边格子的路径数,即 dp[i][j] = dp[i-1][j] + dp[i][j-1]

  4. 返回结果: 最终,右下角格子的路径数就是从左上角到右下角的唯一路径数,即 dp[n-1][m-1]

C++ 版本解题思路

  1. 创建二维向量: 首先,我们创建一个大小为 n x m 的二维向量 dp,用于存储每个位置的唯一路径数。C++ 中的向量可以动态地增加或减少元素。

  2. 初始化第一行和第一列: 遍历二维向量,初始化第一行和第一列的路径数为 1,因为在这些位置上,机器人只有一种路径可以到达,即向右或向下移动。

  3. 填充二维向量: 从第二行和第二列开始,遍历每个位置 (i, j),用动态规划的思想计算出到达这个位置的唯一路径数。路径数等于上方格子的路径数加上左边格子的路径数,即 dp[i][j] = dp[i-1][j] + dp[i][j-1]

  4. 返回结果: 最终,右下角格子的路径数就是从左上角到右下角的唯一路径数,即 dp[n-1][m-1]

希望这些解题思路能够帮助你更好地理解每个版本的代码!如果有任何进一步的问题,请随时向我提问。

代码

Go

func uniquePaths(m int, n int) int {
    // 创建一个大小为 n 的二维切片 dp,用于存储每个位置的唯一路径数
    dp := make([][]int, n)
    for i := 0; i < n; i++ {
        // 对每一行都创建一个大小为 m 的切片
        dp[i] = make([]int, m)
    }

    // 遍历矩形网格的每个位置
    for i := 0; i < n; i++ {
        for j := 0; j < m; j++ {
            // 如果当前位置是第一行或第一列,那么路径数为1,因为只能向右或向下移动一步
            if i == 0 || j == 0 {
                dp[i][j] = 1
                continue
            }
            // 对于其他位置,路径数等于上方格子的路径数加上左边格子的路径数
            dp[i][j] = dp[i-1][j] + dp[i][j-1]
        }
    }

    // 返回右下角格子的路径数,这就是从左上角到右下角的唯一路径数
    return dp[n-1][m-1]
}

Python

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        # 创建一个大小为 n 的二维数组 dp,用于存储每个位置的唯一路径数
        dp = [[0] * m for _ in range(n)]
        
        # 初始化第一行和第一列的路径数为1,因为只有一种方式可以到达这些位置
        for i in range(n):
            dp[i][0] = 1
        for j in range(m):
            dp[0][j] = 1
        
        # 从第二行第二列开始填充数组,每个位置的路径数等于上方格子和左边格子的路径数之和
        for i in range(1, n):
            for j in range(1, m):
                dp[i][j] = dp[i-1][j] + dp[i][j-1]
        
        # 返回右下角格子的路径数,即从左上角到右下角的唯一路径数
        return dp[n-1][m-1]

Java

class Solution {
    public int uniquePaths(int m, int n) {
        // 创建一个大小为 n 的二维数组 dp,用于存储每个位置的唯一路径数
        int[][] dp = new int[n][m];
        
        // 初始化第一行和第一列的路径数为1,因为只有一种方式可以到达这些位置
        for (int i = 0; i < n; i++) {
            dp[i][0] = 1;
        }
        for (int j = 0; j < m; j++) {
            dp[0][j] = 1;
        }
        
        // 从第二行第二列开始填充数组,每个位置的路径数等于上方格子和左边格子的路径数之和
        for (int i = 1; i < n; i++) {
            for (int j = 1; j < m; j++) {
                dp[i][j] = dp[i-1][j] + dp[i][j-1];
            }
        }
        
        // 返回右下角格子的路径数,即从左上角到右下角的唯一路径数
        return dp[n-1][m-1];
    }
}

Cpp

class Solution {
public:
    int uniquePaths(int m, int n) {
        // 创建一个大小为 n 的二维数组 dp,用于存储每个位置的唯一路径数
        vector<vector<int>> dp(n, vector<int>(m, 0));
        
        // 初始化第一行和第一列的路径数为1,因为只有一种方式可以到达这些位置
        for (int i = 0; i < n; i++) {
            dp[i][0] = 1;
        }
        for (int j = 0; j < m; j++) {
            dp[0][j] = 1;
        }
        
        // 从第二行第二列开始填充数组,每个位置的路径数等于上方格子和左边格子的路径数之和
        for (int i = 1; i < n; i++) {
            for (int j = 1; j < m; j++) {
                dp[i][j] = dp[i-1][j] + dp[i][j-1];
            }
        }
        
        // 返回右下角格子的路径数,即从左上角到右下角的唯一路径数
        return dp[n-1][m-1];
    }
};

当然,让我们分开介绍每个版本的代码所需的基础知识。

Go 版本

  1. 基础语法: 了解 Go 语言的基本语法,包括变量、循环、条件语句等。了解切片(slices)的使用,它是 Go 中动态数组的数据结构。

  2. 二维切片(Two-dimensional slices): 学习如何创建和操作二维切片,即切片的切片,用于表示二维数据结构。

  3. 动态规划(Dynamic Programming): 理解动态规划的基本概念,了解如何通过填充二维数组来解决动态规划问题。在这个问题中,二维数组表示了网格中各个位置的唯一路径数。

Python 版本

  1. 基础语法: 了解 Python 的基本语法,包括变量、循环、条件语句等。

  2. 列表(Lists): 了解列表的使用,列表是 Python 中的动态数组,可以存储多个元素。

  3. 二维列表(Two-dimensional lists): 学习如何创建和操作二维列表,它是嵌套列表的概念,用于表示二维数据结构。

  4. 动态规划(Dynamic Programming): 理解动态规划的基本概念,了解如何通过填充二维数组来解决动态规划问题。在这个问题中,二维数组表示了网格中各个位置的唯一路径数。

Java 版本

  1. 基础语法: 了解 Java 的基本语法,包括变量、循环、条件语句等。

  2. 二维数组(Two-dimensional arrays): 学习如何创建和操作二维数组,它是 Java 中表示矩阵的数据结构。

  3. 动态规划(Dynamic Programming): 理解动态规划的基本概念,了解如何通过填充二维数组来解决动态规划问题。在这个问题中,二维数组表示了网格中各个位置的唯一路径数。

C++ 版本

  1. 基础语法: 了解 C++ 的基本语法,包括变量、循环、条件语句等。

  2. 向量(Vectors): 了解向量的使用,它是 C++ 中的动态数组,可以存储多个元素。

  3. 二维向量(Two-dimensional vectors): 学习如何创建和操作二维向量,它是嵌套向量的概念,用于表示二维数据结构。

  4. 动态规划(Dynamic Programming): 理解动态规划的基本概念,了解如何通过填充二维数组来解决动态规划问题。在这个问题中,二维数组表示了网格中各个位置的唯一路径数。

以上是每个版本代码所需的基础知识。如果你在其中的任何一个方面有疑问,都可以随时问我!

63. Unique Paths II

题目

A robot is located at the top-left corner of a m x n grid (marked ‘Start’ in the diagram below).

The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked ‘Finish’ in the diagram below).

Now consider if some obstacles are added to the grids. How many unique paths would there be?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

An obstacle and empty space is marked as 1 and 0 respectively in the grid.

Note: m and n will be at most 100.

Example 1:

Input:
[
  [0,0,0],
  [0,1,0],
  [0,0,0]
]
Output: 2
Explanation:
There is one obstacle in the middle of the 3x3 grid above.
There are two ways to reach the bottom-right corner:
1. Right -> Right -> Down -> Down
2. Down -> Down -> Right -> Right

题目大意

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

解题思路

  • 这一题是第 62 题的加强版。也是一道考察 DP 的简单题。
  • 这一题比第 62 题增加的条件是地图中会出现障碍物,障碍物的处理方法是 dp[i][j]=0
  • 需要注意的一种情况是,起点就是障碍物,那么这种情况直接输出 0 。

每个版本的解题思路:

Go 版本解题思路:

  1. 首先,检查输入网格是否为空或者起始格子就是障碍物,如果是则返回0,因为没有有效的路径。

  2. 获取网格的行数和列数,分别存储在 mn 变量中。

  3. 创建一个二维数组 dp 用于存储路径数,初始化为0,其大小为 m x n

  4. 设置起始格子的路径数为1,因为只有一种方式可以到达起始格子。

  5. 初始化第一行,如果前一个格子的路径数不为0且当前格子不是障碍物,则路径数为1。

  6. 初始化第一列,如果上一个格子的路径数不为0且当前格子不是障碍物,则路径数为1。

  7. 计算其余格子的路径数,如果当前格子不是障碍物,则路径数为上方格子和左方格子的路径数之和。

  8. 返回右下角格子的路径数,即从左上角到右下角的不同路径数。

Python 版本解题思路:

  1. 首先,检查输入网格是否为空或者起始格子就是障碍物,如果是则返回0,因为没有有效的路径。

  2. 获取网格的行数和列数,分别存储在 mn 变量中。

  3. 创建一个二维列表 dp 用于存储路径数,初始化为0,其大小为 m x n

  4. 设置起始格子的路径数为1,因为只有一种方式可以到达起始格子。

  5. 初始化第一行,如果前一个格子的路径数不为0且当前格子不是障碍物,则路径数为1。

  6. 初始化第一列,如果上一个格子的路径数不为0且当前格子不是障碍物,则路径数为1。

  7. 计算其余格子的路径数,如果当前格子不是障碍物,则路径数为上方格子和左方格子的路径数之和。

  8. 返回右下角格子的路径数,即从左上角到右下角的不同路径数。

Java 版本解题思路:

  1. 首先,检查输入网格是否为空或者起始格子就是障碍物,如果是则返回0,因为没有有效的路径。

  2. 获取网格的行数和列数,分别存储在 mn 变量中。

  3. 创建一个二维数组 dp 用于存储路径数,初始化为0,其大小为 m x n

  4. 设置起始格子的路径数为1,因为只有一种方式可以到达起始格子。

  5. 初始化第一行,如果前一个格子的路径数不为0且当前格子不是障碍物,则路径数为1。

  6. 初始化第一列,如果上一个格子的路径数不为0且当前格子不是障碍物,则路径数为1。

  7. 计算其余格子的路径数,如果当前格子不是障碍物,则路径数为上方格子和左方格子的路径数之和。

  8. 返回右下角格子的路径数,即从左上角到右下角的不同路径数。

C++ 版本解题思路:

  1. 首先,检查输入网格是否为空或者起始格子就是障碍物,如果是则返回0,因为没有有效的路径。

  2. 获取网格的行数和列数,分别存储在 mn 变量中。

  3. 创建一个二维向量 dp 用于存储路径数,初始化为0,其大小为 m x n

  4. 设置起始格子的路径数为1,因为只有一种方式可以到达起始格子。

  5. 初始化第一行,如果前一个格子的路径数不为0且当前格子不是障碍物,则路径数为1。

  6. 初始化第一列,如果上一个格子的路径数不为0且当前格子不是障碍物,则路径数为1。

  7. 计算其余格子的路径数,如果当前格子不是障碍物,则路径数为上方格子和左方格子的路径数之和。

  8. 返回右下角格子的路径数,即从左上角到右下角的不同路径数。

这些解题思路都遵循动态规划的原理,通过填充一个二维数组来记录每个格子的路径数,最终计算出从起始点到目标点的不同路径数。

代码

Go

func uniquePathsWithObstacles(obstacleGrid [][]int) int {
    // 检查输入网格是否为空或者起始格子就是障碍物,如果是则返回0
    if len(obstacleGrid) == 0 || obstacleGrid[0][0] == 1 {
        return 0
    }
    
    // 获取网格的行数和列数
    m, n := len(obstacleGrid), len(obstacleGrid[0])
    
    // 创建一个二维数组 dp 用于存储路径数,初始化为0
    dp := make([][]int, m)
    for i := 0; i < m; i++ {
        dp[i] = make([]int, n)
    }
    
    // 设置起始格子的路径数为1,因为只有一种方式可以到达起始格子
    dp[0][0] = 1
    
    // 初始化第一行,如果前一个格子的路径数不为0且当前格子不是障碍物,则路径数为1
    for i := 1; i < n; i++ {
        if dp[0][i-1] != 0 && obstacleGrid[0][i] != 1 {
            dp[0][i] = 1
        }
    }
    
    // 初始化第一列,如果上一个格子的路径数不为0且当前格子不是障碍物,则路径数为1
    for i := 1; i < m; i++ {
        if dp[i-1][0] != 0 && obstacleGrid[i][0] != 1 {
            dp[i][0] = 1
        }
    }
    
    // 计算其余格子的路径数,如果当前格子不是障碍物,则路径数为上方格子和左方格子的路径数之和
    for i := 1; i < m; i++ {
        for j := 1; j < n; j++ {
            if obstacleGrid[i][j] != 1 {
                dp[i][j] = dp[i-1][j] + dp[i][j-1]
            }
        }
    }
    
    // 返回右下角格子的路径数,即从左上角到右下角的不同路径数
    return dp[m-1][n-1]
}

Python

class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        if not obstacleGrid or obstacleGrid[0][0] == 1:
            return 0
        
        m, n = len(obstacleGrid), len(obstacleGrid[0])
        
        dp = [[0] * n for _ in range(m)]
        dp[0][0] = 1
        
        for i in range(1, n):
            if dp[0][i-1] != 0 and obstacleGrid[0][i] != 1:
                dp[0][i] = 1
        
        for i in range(1, m):
            if dp[i-1][0] != 0 and obstacleGrid[i][0] != 1:
                dp[i][0] = 1
        
        for i in range(1, m):
            for j in range(1, n):
                if obstacleGrid[i][j] != 1:
                    dp[i][j] = dp[i-1][j] + dp[i][j-1]
        
        return dp[-1][-1]

Java

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        if (obstacleGrid == null || obstacleGrid[0][0] == 1) {
            return 0;
        }
        
        int m = obstacleGrid.length;
        int n = obstacleGrid[0].length;
        
        int[][] dp = new int[m][n];
        dp[0][0] = 1;
        
        for (int i = 1; i < n; i++) {
            if (dp[0][i-1] != 0 && obstacleGrid[0][i] != 1) {
                dp[0][i] = 1;
            }
        }
        
        for (int i = 1; i < m; i++) {
            if (dp[i-1][0] != 0 && obstacleGrid[i][0] != 1) {
                dp[i][0] = 1;
            }
        }
        
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (obstacleGrid[i][j] != 1) {
                    dp[i][j] = dp[i-1][j] + dp[i][j-1];
                }
            }
        }
        
        return dp[m-1][n-1];
    }
}

Cpp

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        if (obstacleGrid.empty() || obstacleGrid[0][0] == 1) {
            return 0;
        }
        
        int m = obstacleGrid.size();
        int n = obstacleGrid[0].size();
        
        vector<vector<int>> dp(m, vector<int>(n, 0));
        dp[0][0] = 1;
        
        for (int i = 1; i < n; i++) {
            if (dp[0][i-1] != 0 && obstacleGrid[0][i] != 1) {
                dp[0][i] = 1;
            }
        }
        
        for (int i = 1; i < m; i++) {
            if (dp[i-1][0] != 0 && obstacleGrid[i][0] != 1) {
                dp[i][0] = 1;
            }
        }
        
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (obstacleGrid[i][j] != 1) {
                    dp[i][j] = dp[i-1][j] + dp[i][j-1];
                }
            }
        }
        
        return dp[m-1][n-1];
    }
};

基础知识。

Go 版本:

  1. 基本语法和数据结构:了解 Go 的基本语法、变量声明、条件语句(if)、循环语句(for)、数组和切片的使用。

  2. 二维数组:理解如何声明和使用二维数组,以表示网格。

  3. 动态规划(DP):了解动态规划的基本概念,包括如何使用DP数组解决问题以及如何进行状态转移。

  4. 条件语句:理解如何使用条件语句(if)来处理特殊情况,例如检查输入是否为空或者起始点是否是障碍物。

Python 版本:

  1. 基本语法:了解 Python 的基本语法,包括变量声明、条件语句(if)、循环语句(for)等。

  2. 列表和二维列表:掌握 Python 中列表和二维列表的使用,用于表示网格和DP数组。

  3. 动态规划(DP):了解动态规划的概念和原理,包括如何定义状态和状态转移方程。

  4. 类和方法:Python 版本的解法使用了类和方法,了解如何定义类和实现方法。

Java 版本:

  1. 基本语法:掌握 Java 的基本语法,包括变量声明、条件语句(if)、循环语句(for)等。

  2. 二维数组:了解如何声明和使用二维数组,以表示网格。

  3. 动态规划(DP):理解动态规划的概念,包括如何定义DP数组和状态转移。

  4. 类和方法:Java 版本的解法使用了类和方法,了解如何定义类和实现方法。

C++ 版本:

  1. 基本语法:了解 C++ 的基本语法,包括变量声明、条件语句(if)、循环语句(for)等。

  2. 二维向量:了解如何使用 C++ 的二维向量(vector<vector>) 来表示网格和DP数组。

  3. 动态规划(DP):理解动态规划的概念,包括如何定义DP数组和状态转移。

  4. 类和方法:C++ 版本的解法没有使用类,但了解如何定义和使用函数是有帮助的。

以上是每个版本解法所需要的基础知识,但不必担心一开始完全掌握所有这些概念。通过学习和实践,逐渐积累经验和技能,能够更好地理解和编写这些代码。如果需要深入了解任何特定概念,可以参考相关的编程教程和文档。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

双机并联逆变器自适应虚拟阻抗下垂控制(Droop)策略Simulink仿真模型

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

深挖 Python 元组 pt.1

哈喽大家好&#xff0c;我是咸鱼 好久不见甚是想念&#xff0c;2023 年最后一次法定节假日已经结束了&#xff0c;不知道各位小伙伴是不是跟咸鱼一样今天就开始“搬砖”了呢&#xff1f; 我们知道元组&#xff08;tuple&#xff09;是 Python 的内置数据类型&#xff0c;tupl…

案例解读【淘宝API接口的运用:抓取用户数据从而驱动精准营销

我国网络购物用户规模8.12亿占网民整体80.3%&#xff08;来源&#xff1a;中商产业研究院&#xff09;。由此可见&#xff0c;网络购物逐渐成为人们普遍选择的一种消费方式。作为连接买卖双方的服务方&#xff0c;电商平台掌握了海量的用户数据&#xff0c;用户数据作为一种宝贵…

electron.js入门-为生产环境构建应用程序

在本章中&#xff0c;我们将学习如何使用可执行文件生成生产应用程序&#xff1b;为此&#xff0c;我们将使用以下软件包&#xff1a; https://www.electron.build/ 需要注意的是&#xff0c;当您有兴趣生成应用程序的可执行文件时&#xff0c;必须在每个Electron.js项目中安装…

pmql基本使用

简介 Prometheus 通过指标名称&#xff08;metrics name&#xff09;以及对应的一组标签&#xff08;labelset&#xff09;唯一 定义一条时间序列。指标名称反映了监控样本的基本标识&#xff0c;而 label 则在这个基本特征上为 采集到的数据提供了多种特征维度。用户可以基于…

练[极客大挑战 2019]BuyFlag

[极客大挑战 2019]BuyFlag 文章目录 [极客大挑战 2019]BuyFlag掌握知识解题思路关键paylaod 掌握知识 ​ cookie字段的修改&#xff0c;代码审计&#xff0c;数字和字符串弱等于绕过&#xff0c;科学计数法替换长数字&#xff0c;GET请求包转换POST请求包 解题思路 打开题目…

php递归生成树形结构 - 无限分类 - 构建树形结构 - 省市区三级联动

直接上代码 示例 <?php/*** php递归生成树形结构 - 无限分类 - 构建树形结构 - 省市区三级联动* * param array $lists 一维数组&#xff0c;包括不同级别的各行数据* param int $parentId 目标节点的父类ID (可以是顶级分类的父ID&#xff0c;也可以是任意节点的父ID)* …

EfficientViT: 高效视觉transformer与级联组注意力

文章目录 摘要1、简介2、用视觉transformer加速2.1. 内存效率2.2. 计算效率2.3. 参数效率 3、高效视觉Transformer3.1、EfficientViT的构建块3.2、EfficientViT网络架构 4、实验4.1、实现细节4.2、ImageNet上的结果4.3、迁移学习结果4.4、 消融实验 5、相关工作6、结论 摘要 2…

手机投屏电脑软件AirServer5.6.3.0最新免费版本下载

随着智能手机的普及&#xff0c;越来越多的人喜欢用手机观看视频、玩游戏、办公等。但是&#xff0c;有时候手机屏幕太小&#xff0c;不够清晰&#xff0c;也不方便操作。这时候&#xff0c;如果能把手机屏幕投射到电脑上&#xff0c;就可以享受更大的视野&#xff0c;更流畅的…

【C++设计模式之命令模式:行为型】分析及示例

简介 命令模式是一种行为型设计模式&#xff0c;它将请求封装成一个对象&#xff0c;从而使不同的请求可以被参数化、队列化或记录化。这种模式允许请求的发送者和接收者进行解耦&#xff0c;同时提供更高的灵活性和可扩展性。 描述 命令模式的核心思想是通过命令对象来封装…

WIN11右键菜单更改为WIN7风格

1.按键盘上的 Win R 组合键&#xff0c;打开运行&#xff0c;输入 regedit 命令&#xff0c;按确定或回车&#xff0c;打开注册表编辑器&#xff1b; 2.注册表编辑器窗口中&#xff0c;依次展开到以下路径&#xff1a; HKEY_CURRENT_USER\Software\Classes\CLSID\ 3.左侧定位…

ICE综述

ICE综述 ICE(Internet Communications Engine)是ZeroC提供的一款高性能的中间件&#xff0c;基于ICE可以实现电信级的解决方案。在设计网站架构的时候可以使用ICE实现对网站应用的基础对象操作&#xff0c;将基础对象操作和数据库操作封装在这一层&#xff0c;在业务逻辑层以及…

notepad++ 如何去除换行

选中下方的“扩展” “查找目标”输入&#xff1a;\r\n&#xff0c;替换为:空白 最后全部替换。

钡铼DLT645和IEC104转Modbus协议网关BL120DT:构建智能电力网的关键角色

在电力行业中&#xff0c;DLT645和IEC104转Modbus协议网关已成为重要的通信工具&#xff0c;用于将电力设备的数据和状态信息转换为Modbus协议&#xff0c;以便于远程监控和管理。以下是关于钡铼DLT645和IEC104转Modbus协议网关BL120DT在电力行业应用的案例介绍。 某电力公司需…

记一次地市hw从供应商-目标站-百万信息泄露

起因&#xff1a;某市hw、给了某医院的资产&#xff0c;根据前期进行的信息收集就开始打&#xff0c;奈何目标单位资产太少&#xff0c;唯有一个IP资产稍微多一点点&#xff0c;登录框就两个&#xff0c;屡次尝试弱口令、未授权等均失败。 事件型-通用性-反编译jar-Naocs-后台-…

node-red opc-ua节点操作

node-red opc-ua节点操作 一、使用OPC UA客户端工具Softing OPC Client读写OPC节点数据二、 NodeRed通过OPC UA读取数据并写入mysql三、NodeRed订阅opcua数据&#xff0c;并通过mqtt发布 一、使用OPC UA客户端工具Softing OPC Client读写OPC节点数据 使用OPC UA客户端工具Soft…

超级扫描-专业、快速、安全的二维码和条形码工

官网下载地址&#xff1a; 安果移动 视频介绍&#xff1a;超级扫描-专业、快速、安全的二维码和条形码工具_哔哩哔哩_bilibili 在今天的数字化时代&#xff0c;无论您身处哪里&#xff0c;二维码和条形码无处不在。从商店的货架到在线广告&#xff0c;从名片到活动海报 &…

长江存储YMTC 232L QLC量产,低调上架

有粉丝朋友提醒小编&#xff0c;反馈长江存储232L QLC已经量产&#xff0c;并已经开始售卖了。小编一搜&#xff0c;还真是&#xff0c;“当季新品”。 技术规格信息显示&#xff0c;来自长江存储原厂QLC颗粒。 评论区有用户反馈确认是232层的QLC SSD。 但是&#xff0c;有个奇…

Delphi编程:pagecontrol组件的tab字体变大

1、将pagecontrol组件属性中的font的字体变成四号。 2、将tabsheet1属性中的font的字体设置成八号。 结果如下&#xff1a;

Excel插件:StatPlus Pro 7.7.0 Crack

Windows 版 StatPlus 借助 StatPlus&#xff0c;人们可以获得一套强大的统计工具和图形分析方法&#xff0c;可以通过简单直观的界面轻松访问。StatPlus 的可能应用范围几乎是无限的 - 社会学、金融分析、生物统计学、经济学、保险业、医疗保健和临床研究 - 仅举几个该程序已被…