diff --git a/problems/0070.爬楼梯.md b/problems/0070.爬楼梯.md index a2f664a4..6a13a21c 100644 --- a/problems/0070.爬楼梯.md +++ b/problems/0070.爬楼梯.md @@ -130,8 +130,8 @@ public: }; ``` -* 时间复杂度:$O(n)$ -* 空间复杂度:$O(n)$ +* 时间复杂度:O(n) +* 空间复杂度:O(n) 当然依然也可以,优化一下空间复杂度,代码如下: @@ -154,8 +154,8 @@ public: }; ``` -* 时间复杂度:$O(n)$ -* 空间复杂度:$O(1)$ +* 时间复杂度:O(n) +* 空间复杂度:O(1) 后面将讲解的很多动规的题目其实都是当前状态依赖前两个,或者前三个状态,都可以做空间上的优化,**但我个人认为面试中能写出版本一就够了哈,清晰明了,如果面试官要求进一步优化空间的话,我们再去优化**。 @@ -524,3 +524,4 @@ impl Solution { + diff --git a/problems/0111.二叉树的最小深度.md b/problems/0111.二叉树的最小深度.md index cd7096ac..708e0532 100644 --- a/problems/0111.二叉树的最小深度.md +++ b/problems/0111.二叉树的最小深度.md @@ -40,7 +40,7 @@ 本题依然是前序遍历和后序遍历都可以,前序求的是深度,后序求的是高度。 * 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始) -* 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数后者节点数(取决于高度从0开始还是从1开始) +* 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始) 那么使用后序遍历,其实求的是根节点到叶子节点的最小距离,就是求高度的过程,不过这个最小距离 也同样是最小深度。 diff --git a/problems/0150.逆波兰表达式求值.md b/problems/0150.逆波兰表达式求值.md index 5fb28c29..bc73f6da 100644 --- a/problems/0150.逆波兰表达式求值.md +++ b/problems/0150.逆波兰表达式求值.md @@ -489,6 +489,67 @@ impl Solution { } ``` +### C: + +```c +int str_to_int(char *str) { + // string转integer + int num = 0, tens = 1; + for (int i = strlen(str) - 1; i >= 0; i--) { + if (str[i] == '-') { + num *= -1; + break; + } + num += (str[i] - '0') * tens; + tens *= 10; + } + return num; +} + +int evalRPN(char** tokens, int tokensSize) { + + int *stack = (int *)malloc(tokensSize * sizeof(int)); + assert(stack); + int stackTop = 0; + + for (int i = 0; i < tokensSize; i++) { + char symbol = (tokens[i])[0]; + if (symbol < '0' && (tokens[i])[1] == '\0') { + + // pop两个数字 + int num1 = stack[--stackTop]; + int num2 = stack[--stackTop]; + + // 计算结果 + int result; + if (symbol == '+') { + result = num1 + num2; + } else if (symbol == '-') { + result = num2 - num1; + } else if (symbol == '/') { + result = num2 / num1; + } else { + result = num1 * num2; + } + + // push回stack + stack[stackTop++] = result; + + } else { + + // push数字进stack + int num = str_to_int(tokens[i]); + stack[stackTop++] = num; + + } + } + + int result = stack[0]; + free(stack); + return result; +} +``` +

diff --git a/problems/0225.用队列实现栈.md b/problems/0225.用队列实现栈.md index f0fe3a3c..73d9db1b 100644 --- a/problems/0225.用队列实现栈.md +++ b/problems/0225.用队列实现栈.md @@ -1277,6 +1277,95 @@ impl MyStack { } ``` +### C: + +> C:单队列 + +```c +typedef struct Node { + int val; + struct Node *next; +} Node_t; + +// 用单向链表实现queue +typedef struct { + Node_t *head; + Node_t *foot; + int size; +} MyStack; + +MyStack* myStackCreate() { + MyStack *obj = (MyStack *)malloc(sizeof(MyStack)); + assert(obj); + obj->head = NULL; + obj->foot = NULL; + obj->size = 0; + return obj; +} + +void myStackPush(MyStack* obj, int x) { + + Node_t *temp = (Node_t *)malloc(sizeof(Node_t)); + assert(temp); + temp->val = x; + temp->next = NULL; + + // 添加至queue末尾 + if (obj->foot) { + obj->foot->next = temp; + } else { + obj->head = temp; + } + obj->foot = temp; + obj->size++; +} + +int myStackPop(MyStack* obj) { + + // 获取末尾元素 + int target = obj->foot->val; + + if (obj->head == obj->foot) { + free(obj->foot); + obj->head = NULL; + obj->foot = NULL; + } else { + + Node_t *prev = obj->head; + // 移动至queue尾部节点前一个节点 + while (prev->next != obj->foot) { + prev = prev->next; + } + + free(obj->foot); + obj->foot = prev; + obj->foot->next = NULL; + } + + obj->size--; + return target; +} + +int myStackTop(MyStack* obj) { + return obj->foot->val; +} + +bool myStackEmpty(MyStack* obj) { + return obj->size == 0; +} + +void myStackFree(MyStack* obj) { + Node_t *curr = obj->head; + while (curr != NULL) { + Node_t *temp = curr->next; + free(curr); + curr = temp; + } + free(obj); +} + +``` +

diff --git a/problems/0239.滑动窗口最大值.md b/problems/0239.滑动窗口最大值.md index caa24d8d..9bb3494d 100644 --- a/problems/0239.滑动窗口最大值.md +++ b/problems/0239.滑动窗口最大值.md @@ -890,6 +890,38 @@ public: }; ``` +### C + +```c +int* maxSlidingWindow(int* nums, int numsSize, int k, int* returnSize) { + *returnSize = numsSize - k + 1; + int *res = (int*)malloc((*returnSize) * sizeof(int)); + assert(res); + int *deque = (int*)malloc(numsSize * sizeof(int)); + assert(deque); + int front = 0, rear = 0, idx = 0; + + for (int i = 0 ; i < numsSize ; i++) { + while (front < rear && deque[front] <= i - k) { + front++; + } + + while (front < rear && nums[deque[rear - 1]] <= nums[i]) { + rear--; + } + + deque[rear++] = i; + + if (i >= k - 1) { + res[idx++] = nums[deque[front]]; + } + } + + return res; +} + +``` +

diff --git a/problems/0459.重复的子字符串.md b/problems/0459.重复的子字符串.md index 2be8922b..de0e6e4d 100644 --- a/problems/0459.重复的子字符串.md +++ b/problems/0459.重复的子字符串.md @@ -879,6 +879,52 @@ public int[] GetNext(string s) } ``` +### C + +```c +// 前缀表不减一 +int *build_next(char* s, int len) { + + int *next = (int *)malloc(len * sizeof(int)); + assert(next); + + // 初始化前缀表 + next[0] = 0; + + // 构建前缀表表 + int i = 1, j = 0; + while (i < len) { + if (s[i] == s[j]) { + j++; + next[i] = j; + i++; + } else if (j > 0) { + j = next[j - 1]; + } else { + next[i] = 0; + i++; + } + } + return next; +} + +bool repeatedSubstringPattern(char* s) { + + int len = strlen(s); + int *next = build_next(s, len); + bool result = false; + + // 检查最小重复片段能否被长度整除 + if (next[len - 1]) { + result = len % (len - next[len - 1]) == 0; + } + + free(next); + return result; +} + +``` +

diff --git a/problems/0494.目标和.md b/problems/0494.目标和.md index 6ddf774d..4a1fc6ab 100644 --- a/problems/0494.目标和.md +++ b/problems/0494.目标和.md @@ -670,18 +670,26 @@ class Solution: # 创建二维动态规划数组,行表示选取的元素数量,列表示累加和 dp = [[0] * (target_sum + 1) for _ in range(len(nums) + 1)] + dp = [[0] * (target_sum + 1) for _ in range(len(nums))] # 初始化状态 dp[0][0] = 1 + if nums[0] <= target_sum: + dp[0][nums[0]] = 1 + numZero = 0 + for i in range(len(nums)): + if nums[i] == 0: + numZero += 1 + dp[i][0] = int(math.pow(2, numZero)) # 动态规划过程 - for i in range(1, len(nums) + 1): + for i in range(1, len(nums)): for j in range(target_sum + 1): dp[i][j] = dp[i - 1][j] # 不选取当前元素 if j >= nums[i - 1]: - dp[i][j] += dp[i - 1][j - nums[i - 1]] # 选取当前元素 + dp[i][j] += dp[i - 1][j - nums[i]] # 选取当前元素 - return dp[len(nums)][target_sum] # 返回达到目标和的方案数 + return dp[len(nums)-1][target_sum] # 返回达到目标和的方案数 ``` diff --git a/problems/0518.零钱兑换II.md b/problems/0518.零钱兑换II.md index 833d5ed6..835df852 100644 --- a/problems/0518.零钱兑换II.md +++ b/problems/0518.零钱兑换II.md @@ -546,7 +546,7 @@ object Solution { } } ``` -## C +### C ```c int change(int amount, int* coins, int coinsSize) { @@ -593,37 +593,3 @@ public class Solution - ----------- - - - -回归本题,动规五步曲来分析如下: - -1. 确定dp数组以及下标的含义 - -dp[j]:凑成总金额j的货币组合数为dp[j] - -2. 确定递推公式 - -dp[j] 就是所有的dp[j - coins[i]](考虑coins[i]的情况)相加。 - -所以递推公式:dp[j] += dp[j - coins[i]]; - -**这个递推公式大家应该不陌生了,我在讲解01背包题目的时候在这篇[494. 目标和](https://programmercarl.com/0494.目标和.html)中就讲解了,求装满背包有几种方法,公式都是:dp[j] += dp[j - nums[i]];** - -3. dp数组如何初始化 - -首先dp[0]一定要为1,dp[0] = 1是 递归公式的基础。如果dp[0] = 0 的话,后面所有推导出来的值都是0了。 - -那么 dp[0] = 1 有没有含义,其实既可以说 凑成总金额0的货币组合数为1,也可以说 凑成总金额0的货币组合数为0,好像都没有毛病。 - -但题目描述中,也没明确说 amount = 0 的情况,结果应该是多少。 - -这里我认为题目描述还是要说明一下,因为后台测试数据是默认,amount = 0 的情况,组合数为1的。 - -下标非0的dp[j]初始化为0,这样累计加dp[j - coins[i]]的时候才不会影响真正的dp[j] - -dp[0]=1还说明了一种情况:如果正好选了coins[i]后,也就是j-coins[i] == 0的情况表示这个硬币刚好能选,此时dp[0]为1表示只选coins[i]存在这样的一种选法。 - -----------------