Merge branch 'master' into master

This commit is contained in:
程序员Carl
2024-08-18 09:35:50 +08:00
committed by GitHub
19 changed files with 713 additions and 62 deletions

View File

@ -1,56 +0,0 @@
JAVA:
```Java
import java.util.*;
public class Main{
public static void main(String[] args) {
int N, M;
Scanner scanner = new Scanner(System.in);
N = scanner.nextInt();
M = scanner.nextInt();
DisJoint disJoint = new DisJoint(N + 1);
for (int i = 0; i < M; ++i) {
disJoint.join(scanner.nextInt(), scanner.nextInt());
}
if(disJoint.isSame(scanner.nextInt(), scanner.nextInt())) {
System.out.println("1");
} else {
System.out.println("0");
}
}
}
//并查集模板
class DisJoint{
private int[] father;
public DisJoint(int N) {
father = new int[N];
for (int i = 0; i < N; ++i){
father[i] = i;
}
}
public int find(int n) {
return n == father[n] ? n : (father[n] = find(father[n]));
}
public void join (int n, int m) {
n = find(n);
m = find(m);
if (n == m) return;
father[m] = n;
}
public boolean isSame(int n, int m){
n = find(n);
m = find(m);
return n == m;
}
}
```

View File

@ -267,6 +267,7 @@ class Solution {
break;
}
if (j > i + 1 && nums[j - 1] == nums[j]) { // 对nums[j]去重
continue;
}

View File

@ -371,6 +371,7 @@ class Solution:
```
### Go
动态规划
```Go
func uniquePaths(m int, n int) int {
dp := make([][]int, m)
@ -390,6 +391,26 @@ func uniquePaths(m int, n int) int {
}
```
数论方法
```Go
func uniquePaths(m int, n int) int {
numerator := 1
denominator := m - 1
count := m - 1
t := m + n - 2
for count > 0 {
numerator *= t
t--
for denominator != 0 && numerator % denominator == 0 {
numerator /= denominator
denominator--
}
count--
}
return numerator
}
```
### Javascript
```Javascript

View File

@ -310,6 +310,43 @@ class Solution:
```
### Go
使用used数组
```Go
var (
result [][]int
path []int
)
func subsetsWithDup(nums []int) [][]int {
result = make([][]int, 0)
path = make([]int, 0)
used := make([]bool, len(nums))
sort.Ints(nums) // 去重需要排序
backtracing(nums, 0, used)
return result
}
func backtracing(nums []int, startIndex int, used []bool) {
tmp := make([]int, len(path))
copy(tmp, path)
result = append(result, tmp)
for i := startIndex; i < len(nums); i++ {
// used[i - 1] == true说明同一树枝candidates[i - 1]使用过
// used[i - 1] == false说明同一树层candidates[i - 1]使用过
// 而我们要对同一树层使用过的元素进行跳过
if i > 0 && nums[i] == nums[i-1] && used[i-1] == false {
continue
}
path = append(path, nums[i])
used[i] = true
backtracing(nums, i + 1, used)
path = path[:len(path)-1]
used[i] = false
}
}
```
不使用used数组
```Go
var (
path []int

View File

@ -143,7 +143,7 @@ for (int i = startIndex; i < s.size(); i++) {
代码如下:
```CPP
// 判断字符串s在左闭闭区间[start, end]所组成的数字是否合法
// 判断字符串s在左闭闭区间[start, end]所组成的数字是否合法
bool isValid(const string& s, int start, int end) {
if (start > end) {
return false;
@ -208,7 +208,7 @@ private:
} else break; // 不合法,直接结束本层循环
}
}
// 判断字符串s在左闭闭区间[start, end]所组成的数字是否合法
// 判断字符串s在左闭闭区间[start, end]所组成的数字是否合法
bool isValid(const string& s, int start, int end) {
if (start > end) {
return false;

View File

@ -623,6 +623,8 @@ class Solution:
```
### Go:
递归法
```Go
func isBalanced(root *TreeNode) bool {
h := getHeight(root)
@ -653,6 +655,64 @@ func max(a, b int) int {
}
```
迭代法
```Go
func isBalanced(root *TreeNode) bool {
st := make([]*TreeNode, 0)
if root == nil {
return true
}
st = append(st, root)
for len(st) > 0 {
node := st[len(st)-1]
st = st[:len(st)-1]
if math.Abs(float64(getDepth(node.Left)) - float64(getDepth(node.Right))) > 1 {
return false
}
if node.Right != nil {
st = append(st, node.Right)
}
if node.Left != nil {
st = append(st, node.Left)
}
}
return true
}
func getDepth(cur *TreeNode) int {
st := make([]*TreeNode, 0)
if cur != nil {
st = append(st, cur)
}
depth := 0
result := 0
for len(st) > 0 {
node := st[len(st)-1]
if node != nil {
st = st[:len(st)-1]
st = append(st, node, nil)
depth++
if node.Right != nil {
st = append(st, node.Right)
}
if node.Left != nil {
st = append(st, node.Left)
}
} else {
st = st[:len(st)-1]
node = st[len(st)-1]
st = st[:len(st)-1]
depth--
}
if result < depth {
result = depth
}
}
return result
}
```
### JavaScript:
递归法:

View File

@ -727,6 +727,48 @@ class Solution:
```go
//递归法
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func hasPathSum(root *TreeNode, targetSum int) bool {
if root == nil {
return false
}
return traversal(root, targetSum - root.Val)
}
func traversal(cur *TreeNode, count int) bool {
if cur.Left == nil && cur.Right == nil && count == 0 {
return true
}
if cur.Left == nil && cur.Right == nil {
return false
}
if cur.Left != nil {
count -= cur.Left.Val
if traversal(cur.Left, count) {
return true
}
count += cur.Left.Val
}
if cur.Right != nil {
count -= cur.Right.Val
if traversal(cur.Right, count) {
return true
}
count += cur.Right.Val
}
return false
}
```
```go
//递归法精简
/**
* Definition for a binary tree node.
* type TreeNode struct {

View File

@ -527,6 +527,7 @@ class Solution:
```
### Go
回溯 基本版
```go
var (
path []string // 放已经回文的子串
@ -565,6 +566,63 @@ func isPalindrome(s string) bool {
}
```
回溯+动态规划优化回文串判断
```go
var (
result [][]string
path []string // 放已经回文的子串
isPalindrome [][]bool // 放事先计算好的是否回文子串的结果
)
func partition(s string) [][]string {
result = make([][]string, 0)
path = make([]string, 0)
computePalindrome(s)
backtracing(s, 0)
return result
}
func backtracing(s string, startIndex int) {
// 如果起始位置已经大于s的大小说明已经找到了一组分割方案了
if startIndex >= len(s) {
tmp := make([]string, len(path))
copy(tmp, path)
result = append(result, tmp)
return
}
for i := startIndex; i < len(s); i++ {
if isPalindrome[startIndex][i] { // 是回文子串
// 获取[startIndex,i]在s中的子串
path = append(path, s[startIndex:i+1])
} else { // 不是回文,跳过
continue
}
backtracing(s, i + 1) // 寻找i+1为起始位置的子串
path = path[:len(path)-1] // 回溯过程,弹出本次已经添加的子串
}
}
func computePalindrome(s string) {
// isPalindrome[i][j] 代表 s[i:j](双边包括)是否是回文字串
isPalindrome = make([][]bool, len(s))
for i := 0; i < len(isPalindrome); i++ {
isPalindrome[i] = make([]bool, len(s))
}
for i := len(s)-1; i >= 0; i-- {
// 需要倒序计算, 保证在i行时, i+1行已经计算好了
for j := i; j < len(s); j++ {
if j == i {
isPalindrome[i][j] = true
} else if j - i == 1 {
isPalindrome[i][j] = s[i] == s[j]
} else {
isPalindrome[i][j] = s[i] == s[j] && isPalindrome[i+1][j-1]
}
}
}
}
```
### JavaScript
```js

View File

@ -149,7 +149,35 @@ public:
* 时间复杂度: O(n)
* 空间复杂度: O(1)
**也可以通过递归的思路解决本题:**
基础情况:对于空链表,不需要移除元素。
递归情况:首先检查头节点的值是否为 val如果是则移除头节点答案即为在头节点的后续节点上递归的结果如果头节点的值不为 val则答案为头节点与在头节点的后续节点上递归得到的新链表拼接的结果。
```CPP
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
// 基础情况:空链表
if (head == nullptr) {
return nullptr;
}
// 递归处理
if (head->val == val) {
ListNode* newHead = removeElements(head->next, val);
delete head;
return newHead;
} else {
head->next = removeElements(head->next, val);
return head;
}
}
};
```
* 时间复杂度O(n)
* 空间复杂度O(n)
## 其他语言版本

View File

@ -14,7 +14,7 @@
![226.翻转二叉树](https://code-thinking-1253855093.file.myqcloud.com/pics/20210203192644329.png)
这道题目背后有一个让程序员心酸的故事,听说 Homebrew的作者Max Howell就是因为没在白板上写出翻转二叉树最后被Google拒绝了。真假不做判断当一个乐子哈)
这道题目背后有一个让程序员心酸的故事,听说 Homebrew的作者Max Howell就是因为没在白板上写出翻转二叉树最后被Google拒绝了。真假不做判断当一个乐子哈)
## 算法公开课
@ -1033,3 +1033,4 @@ public TreeNode InvertTree(TreeNode root) {
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
<img src="../pics/网站星球宣传海报.jpg" width="1000"/>
</a>

View File

@ -337,6 +337,21 @@ func sumOfLeftLeaves(root *TreeNode) int {
}
```
**递归精简版**
```go
func sumOfLeftLeaves(root *TreeNode) int {
if root == nil {
return 0
}
leftValue := 0
if root.Left != nil && root.Left.Left == nil && root.Left.Right == nil {
leftValue = root.Left.Val
}
return leftValue + sumOfLeftLeaves(root.Left) + sumOfLeftLeaves(root.Right)
}
```
**迭代法(前序遍历)**
```go

View File

@ -165,6 +165,140 @@ private:
## 其他语言版本
### C++双链表法:
```CPP
//采用循环虚拟结点的双链表实现
class MyLinkedList {
public:
// 定义双向链表节点结构体
struct DList {
int elem; // 节点存储的元素
DList *next; // 指向下一个节点的指针
DList *prev; // 指向上一个节点的指针
// 构造函数创建一个值为elem的新节点
DList(int elem) : elem(elem), next(nullptr), prev(nullptr) {};
};
// 构造函数,初始化链表
MyLinkedList() {
sentinelNode = new DList(0); // 创建哨兵节点,不存储有效数据
sentinelNode->next = sentinelNode; // 哨兵节点的下一个节点指向自身,形成循环
sentinelNode->prev = sentinelNode; // 哨兵节点的上一个节点指向自身,形成循环
size = 0; // 初始化链表大小为0
}
// 获取链表中第index个节点的值
int get(int index) {
if (index > (size - 1) || index < 0) { // 检查索引是否超出范围
return -1; // 如果超出范围,返回-1
}
int num;
int mid = size >> 1; // 计算链表中部位置
DList *curNode = sentinelNode; // 从哨兵节点开始
if (index < mid) { // 如果索引小于中部位置,从前往后遍历
for (int i = 0; i < index + 1; i++) {
curNode = curNode->next; // 移动到目标节点
}
} else { // 如果索引大于等于中部位置,从后往前遍历
for (int i = 0; i < size - index; i++) {
curNode = curNode->prev; // 移动到目标节点
}
}
num = curNode->elem; // 获取目标节点的值
return num; // 返回节点的值
}
// 在链表头部添加节点
void addAtHead(int val) {
DList *newNode = new DList(val); // 创建新节点
DList *next = sentinelNode->next; // 获取当前头节点的下一个节点
newNode->prev = sentinelNode; // 新节点的上一个节点指向哨兵节点
newNode->next = next; // 新节点的下一个节点指向原来的头节点
size++; // 链表大小加1
sentinelNode->next = newNode; // 哨兵节点的下一个节点指向新节点
next->prev = newNode; // 原来的头节点的上一个节点指向新节点
}
// 在链表尾部添加节点
void addAtTail(int val) {
DList *newNode = new DList(val); // 创建新节点
DList *prev = sentinelNode->prev; // 获取当前尾节点的上一个节点
newNode->next = sentinelNode; // 新节点的下一个节点指向哨兵节点
newNode->prev = prev; // 新节点的上一个节点指向原来的尾节点
size++; // 链表大小加1
sentinelNode->prev = newNode; // 哨兵节点的上一个节点指向新节点
prev->next = newNode; // 原来的尾节点的下一个节点指向新节点
}
// 在链表中的第index个节点之前添加值为val的节点
void addAtIndex(int index, int val) {
if (index > size) { // 检查索引是否超出范围
return; // 如果超出范围,直接返回
}
if (index <= 0) { // 如果索引为0或负数在头部添加节点
addAtHead(val);
return;
}
int num;
int mid = size >> 1; // 计算链表中部位置
DList *curNode = sentinelNode; // 从哨兵节点开始
if (index < mid) { // 如果索引小于中部位置,从前往后遍历
for (int i = 0; i < index; i++) {
curNode = curNode->next; // 移动到目标位置的前一个节点
}
DList *temp = curNode->next; // 获取目标位置的节点
DList *newNode = new DList(val); // 创建新节点
curNode->next = newNode; // 在目标位置前添加新节点
temp->prev = newNode; // 目标位置的节点的前一个节点指向新节点
newNode->next = temp; // 新节点的下一个节点指向目标位置的结点
newNode->prev = curNode; // 新节点的上一个节点指向当前节点
} else { // 如果索引大于等于中部位置,从后往前遍历
for (int i = 0; i < size - index; i++) {
curNode = curNode->prev; // 移动到目标位置的后一个节点
}
DList *temp = curNode->prev; // 获取目标位置的节点
DList *newNode = new DList(val); // 创建新节点
curNode->prev = newNode; // 在目标位置后添加新节点
temp->next = newNode; // 目标位置的节点的下一个节点指向新节点
newNode->prev = temp; // 新节点的上一个节点指向目标位置的节点
newNode->next = curNode; // 新节点的下一个节点指向当前节点
}
size++; // 链表大小加1
}
// 删除链表中的第index个节点
void deleteAtIndex(int index) {
if (index > (size - 1) || index < 0) { // 检查索引是否超出范围
return; // 如果超出范围,直接返回
}
int num;
int mid = size >> 1; // 计算链表中部位置
DList *curNode = sentinelNode; // 从哨兵节点开始
if (index < mid) { // 如果索引小于中部位置,从前往后遍历
for (int i = 0; i < index; i++) {
curNode = curNode->next; // 移动到目标位置的前一个节点
}
DList *next = curNode->next->next; // 获取目标位置的下一个节点
curNode->next = next; // 删除目标位置的节点
next->prev = curNode; // 目标位置的下一个节点的前一个节点指向当前节点
} else { // 如果索引大于等于中部位置,从后往前遍历
for (int i = 0; i < size - index - 1; i++) {
curNode = curNode->prev; // 移动到目标位置的后一个节点
}
DList *prev = curNode->prev->prev; // 获取目标位置的下一个节点
curNode->prev = prev; // 删除目标位置的节点
prev->next = curNode; // 目标位置的下一个节点的下一个节点指向当前节点
}
size--; // 链表大小减1
}
private:
int size; // 链表的大小
DList *sentinelNode; // 哨兵节点的指针
};
```
### C:
```C

View File

@ -100,6 +100,18 @@ public:
## 其他语言版本
### Java
排序法
```Java
class Solution {
public int[] sortedSquares(int[] nums) {
for (int i = 0; i < nums.length; i++) {
nums[i] = nums[i] * nums[i];
}
Arrays.sort(nums);
return nums;
}
}
```
```Java
class Solution {

View File

@ -366,6 +366,79 @@ public class Main {
### Python
```Python
import collections
directions = [[-1, 0], [0, 1], [0, -1], [1, 0]]
area = 0
def dfs(i, j, grid, visited, num):
global area
if visited[i][j]:
return
visited[i][j] = True
grid[i][j] = num # 标记岛屿号码
area += 1
for x, y in directions:
new_x = i + x
new_y = j + y
if (
0 <= new_x < len(grid)
and 0 <= new_y < len(grid[0])
and grid[new_x][new_y] == "1"
):
dfs(new_x, new_y, grid, visited, num)
def main():
global area
N, M = map(int, input().strip().split())
grid = []
for i in range(N):
grid.append(input().strip().split())
visited = [[False] * M for _ in range(N)]
rec = collections.defaultdict(int)
cnt = 2
for i in range(N):
for j in range(M):
if grid[i][j] == "1":
area = 0
dfs(i, j, grid, visited, cnt)
rec[cnt] = area # 纪录岛屿面积
cnt += 1
res = 0
for i in range(N):
for j in range(M):
if grid[i][j] == "0":
max_island = 1 # 将水变为陆地故从1开始计数
v = set()
for x, y in directions:
new_x = i + x
new_y = j + y
if (
0 <= new_x < len(grid)
and 0 <= new_y < len(grid[0])
and grid[new_x][new_y] != "0"
and grid[new_x][new_y] not in v # 岛屿不可重复
):
max_island += rec[grid[new_x][new_y]]
v.add(grid[new_x][new_y])
res = max(res, max_island)
if res == 0:
return max(rec.values()) # 无水的情况
return res
if __name__ == "__main__":
print(main())
```
### Go
### Rust

View File

@ -158,7 +158,62 @@ int main() {
## 其他语言版本
### Java
### Java
```Java
import java.util.*;
public class Main{
public static void main(String[] args) {
int N, M;
Scanner scanner = new Scanner(System.in);
N = scanner.nextInt();
M = scanner.nextInt();
DisJoint disJoint = new DisJoint(N + 1);
for (int i = 0; i < M; ++i) {
disJoint.join(scanner.nextInt(), scanner.nextInt());
}
if(disJoint.isSame(scanner.nextInt(), scanner.nextInt())) {
System.out.println("1");
} else {
System.out.println("0");
}
}
}
//并查集模板
class DisJoint{
private int[] father;
public DisJoint(int N) {
father = new int[N];
for (int i = 0; i < N; ++i){
father[i] = i;
}
}
public int find(int n) {
return n == father[n] ? n : (father[n] = find(father[n]));
}
public void join (int n, int m) {
n = find(n);
m = find(m);
if (n == m) return;
father[m] = n;
}
public boolean isSame(int n, int m){
n = find(n);
m = find(m);
return n == m;
}
}
```
### Python

View File

@ -141,6 +141,70 @@ int main() {
### Javascript
```javascript
const r1 = require('readline').createInterface({ input: process.stdin });
// 创建readline接口
let iter = r1[Symbol.asyncIterator]();
// 创建异步迭代器
const readline = async () => (await iter.next()).value;
let N // 节点数和边数
let father = [] // 并查集
// 并查集初始化
const init = () => {
for (let i = 1; i <= N; i++) father[i] = i;
}
// 并查集里寻根的过程
const find = (u) => {
return u == father[u] ? u : father[u] = find(father[u])
}
// 将v->u 这条边加入并查集
const join = (u, v) => {
u = find(u)
v = find(v)
if (u == v) return // 如果发现根相同,则说明在一个集合,不用两个节点相连直接返回
father[v] = u
}
// 判断 u 和 v是否找到同一个根
const isSame = (u, v) => {
u = find(u)
v = find(v)
return u == v
}
(async function () {
// 读取第一行输入
let line = await readline();
N = Number(line);
// 初始化并查集
father = new Array(N)
init()
// 读取边信息, 加入并查集
for (let i = 0; i < N; i++) {
line = await readline()
line = line.split(' ').map(Number)
if (!isSame(line[0], line[1])) {
join(line[0], line[1])
}else{
console.log(line[0], line[1]);
break
}
}
})()
```
### TypeScript
### PhP

View File

@ -217,6 +217,38 @@ public class Main {
```
### Python
```Python
def judge(s1,s2):
count=0
for i in range(len(s1)):
if s1[i]!=s2[i]:
count+=1
return count==1
if __name__=='__main__':
n=int(input())
beginstr,endstr=map(str,input().split())
if beginstr==endstr:
print(0)
exit()
strlist=[]
for i in range(n):
strlist.append(input())
# use bfs
visit=[False for i in range(n)]
queue=[[beginstr,1]]
while queue:
str,step=queue.pop(0)
if judge(str,endstr):
print(step+1)
exit()
for i in range(n):
if visit[i]==False and judge(strlist[i],str):
visit[i]=True
queue.append([strlist[i],step+1])
print(0)
```
### Go

View File

@ -193,7 +193,7 @@
理解思想后,确实不难,但代码写起来也不容易。
为了每次可以找到所有节点的入度信息,我们要在初始的时候,就把每个节点的入度 和 每个节点的依赖关系做统计。
为了每次可以找到所有节点的入度信息,我们要在初始的时候,就把每个节点的入度 和 每个节点的依赖关系做统计。
代码如下:
@ -451,6 +451,80 @@ if __name__ == "__main__":
### Javascript
```javascript
const r1 = require('readline').createInterface({ input: process.stdin });
// 创建readline接口
let iter = r1[Symbol.asyncIterator]();
// 创建异步迭代器
const readline = async () => (await iter.next()).value;
let N, M // 节点数和边数
let inDegrees = [] // 入度
let umap = new Map() // 记录文件依赖关系
let result = [] // 结果
// 根据输入, 初始化数据
const init = async () => {
// 读取第一行输入
let line = await readline();
[N, M] = line.split(' ').map(Number)
inDegrees = new Array(N).fill(0)
// 读取边集
while (M--) {
line = await readline();
let [x, y] = line.split(' ').map(Number)
// 记录入度
inDegrees[y]++
// 记录x指向哪些文件
if (!umap.has(x)) {
umap.set(x, [y])
} else {
umap.get(x).push(y)
}
}
}
(async function () {
// 根据输入, 初始化数据
await init()
let queue = [] // 入度为0的节点
for (let i = 0; i < N; i++) {
if (inDegrees[i] == 0) {
queue.push(i)
}
}
while (queue.length) {
let cur = queue.shift() //当前文件
result.push(cur)
let files = umap.get(cur) // 当前文件指向的文件
// 当前文件指向的文件入度减1
if (files && files.length) {
for (let i = 0; i < files.length; i++) {
inDegrees[files[i]]--
if (inDegrees[files[i]] == 0) queue.push(files[i])
}
}
}
// 这里result.length == N 一定要判断, 因为可能存在环
if (result.length == N) return console.log(result.join(' '))
console.log(-1)
})()
```
### TypeScript
### PhP

View File

@ -193,7 +193,7 @@ dp[0][j]i为0存放编号0的物品的时候各个容量的背包
代码初始化如下:
```
```CPP
for (int j = 0 ; j < weight[0]; j++) { // 当然这一步如果把dp数组预先初始化为0了这一步就可以省略但很多同学应该没有想清楚这一点。
dp[0][j] = 0;
}