mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-10 04:06:51 +08:00
Merge branch 'youngyangyang04:master' into master
This commit is contained in:
@ -313,54 +313,36 @@ func threeSum(nums []int)[][]int{
|
||||
javaScript:
|
||||
|
||||
```js
|
||||
/**
|
||||
* @param {number[]} nums
|
||||
* @return {number[][]}
|
||||
*/
|
||||
|
||||
// 循环内不考虑去重
|
||||
var threeSum = function(nums) {
|
||||
const len = nums.length;
|
||||
if(len < 3) return [];
|
||||
nums.sort((a, b) => a - b);
|
||||
const resSet = new Set();
|
||||
for(let i = 0; i < len - 2; i++) {
|
||||
if(nums[i] > 0) break;
|
||||
let l = i + 1, r = len - 1;
|
||||
const res = [], len = nums.length
|
||||
// 将数组排序
|
||||
nums.sort((a, b) => a - b)
|
||||
for (let i = 0; i < len; i++) {
|
||||
let l = i + 1, r = len - 1, iNum = nums[i]
|
||||
// 数组排过序,如果第一个数大于0直接返回res
|
||||
if (iNum > 0) return res
|
||||
// 去重
|
||||
if (iNum == nums[i - 1]) continue
|
||||
while(l < r) {
|
||||
const sum = nums[i] + nums[l] + nums[r];
|
||||
if(sum < 0) { l++; continue };
|
||||
if(sum > 0) { r--; continue };
|
||||
resSet.add(`${nums[i]},${nums[l]},${nums[r]}`);
|
||||
l++;
|
||||
r--;
|
||||
let lNum = nums[l], rNum = nums[r], threeSum = iNum + lNum + rNum
|
||||
// 三数之和小于0,则左指针向右移动
|
||||
if (threeSum < 0) l++
|
||||
else if (threeSum > 0) r--
|
||||
else {
|
||||
res.push([iNum, lNum, rNum])
|
||||
// 去重
|
||||
while(l < r && nums[l] == nums[l + 1]){
|
||||
l++
|
||||
}
|
||||
while(l < r && nums[r] == nums[r - 1]) {
|
||||
r--
|
||||
}
|
||||
l++
|
||||
r--
|
||||
}
|
||||
}
|
||||
}
|
||||
return Array.from(resSet).map(i => i.split(","));
|
||||
};
|
||||
|
||||
// 去重优化
|
||||
var threeSum = function(nums) {
|
||||
const len = nums.length;
|
||||
if(len < 3) return [];
|
||||
nums.sort((a, b) => a - b);
|
||||
const res = [];
|
||||
for(let i = 0; i < len - 2; i++) {
|
||||
if(nums[i] > 0) break;
|
||||
// a去重
|
||||
if(i > 0 && nums[i] === nums[i - 1]) continue;
|
||||
let l = i + 1, r = len - 1;
|
||||
while(l < r) {
|
||||
const sum = nums[i] + nums[l] + nums[r];
|
||||
if(sum < 0) { l++; continue };
|
||||
if(sum > 0) { r--; continue };
|
||||
res.push([nums[i], nums[l], nums[r]])
|
||||
// b c 去重
|
||||
while(l < r && nums[l] === nums[++l]);
|
||||
while(l < r && nums[r] === nums[--r]);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
return res
|
||||
};
|
||||
```
|
||||
TypeScript:
|
||||
|
@ -31,7 +31,7 @@
|
||||
|
||||
四数之和,和[15.三数之和](https://programmercarl.com/0015.三数之和.html)是一个思路,都是使用双指针法, 基本解法就是在[15.三数之和](https://programmercarl.com/0015.三数之和.html) 的基础上再套一层for循环。
|
||||
|
||||
但是有一些细节需要注意,例如: 不要判断`nums[k] > target` 就返回了,三数之和 可以通过 `nums[i] > 0` 就返回了,因为 0 已经是确定的数了,四数之和这道题目 target是任意值。(大家亲自写代码就能感受出来)
|
||||
但是有一些细节需要注意,例如: 不要判断`nums[k] > target` 就返回了,三数之和 可以通过 `nums[i] > 0` 就返回了,因为 0 已经是确定的数了,四数之和这道题目 target是任意值。比如:数组是`[-4, -3, -2, -1]`,`target`是`-10`,不能因为`-4 > -10`而跳过。但是我们依旧可以去做剪枝,逻辑变成`nums[i] > target && (nums[i] >=0 || target >= 0)`就可以了。
|
||||
|
||||
[15.三数之和](https://programmercarl.com/0015.三数之和.html)的双指针解法是一层for循环num[i]为确定值,然后循环内有left和right下标作为双指针,找到nums[i] + nums[left] + nums[right] == 0。
|
||||
|
||||
@ -72,15 +72,20 @@ public:
|
||||
vector<vector<int>> result;
|
||||
sort(nums.begin(), nums.end());
|
||||
for (int k = 0; k < nums.size(); k++) {
|
||||
// 这种剪枝是错误的,这道题目target 是任意值
|
||||
// if (nums[k] > target) {
|
||||
// return result;
|
||||
// }
|
||||
// 剪枝处理
|
||||
if (nums[k] > target && (nums[k] >= 0 || target >= 0)) {
|
||||
break; // 这里使用break,统一通过最后的return返回
|
||||
}
|
||||
// 去重
|
||||
if (k > 0 && nums[k] == nums[k - 1]) {
|
||||
continue;
|
||||
}
|
||||
for (int i = k + 1; i < nums.size(); i++) {
|
||||
// 2级剪枝处理
|
||||
if (nums[k] + nums[i] > target && (nums[k] + nums[i] >= 0 || target >= 0)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 正确去重方法
|
||||
if (i > k + 1 && nums[i] == nums[i - 1]) {
|
||||
continue;
|
||||
|
@ -266,6 +266,26 @@ var merge = function(intervals) {
|
||||
};
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
```typescript
|
||||
function merge(intervals: number[][]): number[][] {
|
||||
const resArr: number[][] = [];
|
||||
intervals.sort((a, b) => a[0] - b[0]);
|
||||
resArr[0] = [...intervals[0]]; // 避免修改原intervals
|
||||
for (let i = 1, length = intervals.length; i < length; i++) {
|
||||
let interval: number[] = intervals[i];
|
||||
let last: number[] = resArr[resArr.length - 1];
|
||||
if (interval[0] <= last[1]) {
|
||||
last[1] = Math.max(interval[1], last[1]);
|
||||
} else {
|
||||
resArr.push([...intervals[i]]);
|
||||
}
|
||||
}
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -30,7 +30,7 @@
|
||||
|
||||
相信很多同学刚开始做这种题目的时候,上来就是一波判断猛如虎。
|
||||
|
||||
结果运行的时候各种问题,然后开始各种修修补补,最后发现改了这里哪里有问题,改了那里这里又跑不起来了。
|
||||
结果运行的时候各种问题,然后开始各种修修补补,最后发现改了这里那里有问题,改了那里这里又跑不起来了。
|
||||
|
||||
大家还记得我们在这篇文章[数组:每次遇到二分法,都是一看就会,一写就废](https://programmercarl.com/0704.二分查找.html)中讲解了二分法,提到如果要写出正确的二分法一定要坚持**循环不变量原则**。
|
||||
|
||||
@ -47,7 +47,7 @@
|
||||
|
||||
可以发现这里的边界条件非常多,在一个循环中,如此多的边界条件,如果不按照固定规则来遍历,那就是**一进循环深似海,从此offer是路人**。
|
||||
|
||||
这里一圈下来,我们要画每四条边,这四条边怎么画,每画一条边都要坚持一致的左闭右开,或者左开又闭的原则,这样这一圈才能按照统一的规则画下来。
|
||||
这里一圈下来,我们要画每四条边,这四条边怎么画,每画一条边都要坚持一致的左闭右开,或者左开右闭的原则,这样这一圈才能按照统一的规则画下来。
|
||||
|
||||
那么我按照左闭右开的原则,来画一圈,大家看一下:
|
||||
|
||||
@ -59,7 +59,7 @@
|
||||
|
||||
一些同学做这道题目之所以一直写不好,代码越写越乱。
|
||||
|
||||
就是因为在画每一条边的时候,一会左开又闭,一会左闭右闭,一会又来左闭右开,岂能不乱。
|
||||
就是因为在画每一条边的时候,一会左开右闭,一会左闭右闭,一会又来左闭右开,岂能不乱。
|
||||
|
||||
代码如下,已经详细注释了每一步的目的,可以看出while循环里判断的情况是很多的,代码里处理的原则也是统一的左闭右开。
|
||||
|
||||
|
@ -454,31 +454,36 @@ var partition = function(s) {
|
||||
|
||||
```typescript
|
||||
function partition(s: string): string[][] {
|
||||
function isPalindromeStr(s: string, left: number, right: number): boolean {
|
||||
while (left < right) {
|
||||
if (s[left++] !== s[right--]) {
|
||||
return false;
|
||||
const res: string[][] = []
|
||||
const path: string[] = []
|
||||
const isHuiwen = (
|
||||
str: string,
|
||||
startIndex: number,
|
||||
endIndex: number
|
||||
): boolean => {
|
||||
for (; startIndex < endIndex; startIndex++, endIndex--) {
|
||||
if (str[startIndex] !== str[endIndex]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
function backTracking(s: string, startIndex: number, route: string[]): void {
|
||||
let length: number = s.length;
|
||||
if (length === startIndex) {
|
||||
resArr.push(route.slice());
|
||||
return;
|
||||
const rec = (str: string, index: number): void => {
|
||||
if (index >= str.length) {
|
||||
res.push([...path])
|
||||
return
|
||||
}
|
||||
for (let i = startIndex; i < length; i++) {
|
||||
if (isPalindromeStr(s, startIndex, i)) {
|
||||
route.push(s.slice(startIndex, i + 1));
|
||||
backTracking(s, i + 1, route);
|
||||
route.pop();
|
||||
for (let i = index; i < str.length; i++) {
|
||||
if (!isHuiwen(str, index, i)) {
|
||||
continue
|
||||
}
|
||||
path.push(str.substring(index, i + 1))
|
||||
rec(str, i + 1)
|
||||
path.pop()
|
||||
}
|
||||
}
|
||||
const resArr: string[][] = [];
|
||||
backTracking(s, 0, []);
|
||||
return resArr;
|
||||
rec(s, 0)
|
||||
return res
|
||||
};
|
||||
```
|
||||
|
||||
|
@ -408,6 +408,9 @@ function canCompleteCircuit(gas: number[], cost: number[]): number {
|
||||
|
||||
### C
|
||||
|
||||
贪心算法:方法一
|
||||
|
||||
|
||||
```c
|
||||
int canCompleteCircuit(int* gas, int gasSize, int* cost, int costSize){
|
||||
int curSum = 0;
|
||||
@ -437,5 +440,36 @@ int canCompleteCircuit(int* gas, int gasSize, int* cost, int costSize){
|
||||
}
|
||||
```
|
||||
|
||||
贪心算法:方法二
|
||||
```c
|
||||
int canCompleteCircuit(int* gas, int gasSize, int* cost, int costSize){
|
||||
int curSum = 0;
|
||||
int totalSum = 0;
|
||||
int start = 0;
|
||||
|
||||
int i;
|
||||
for(i = 0; i < gasSize; ++i) {
|
||||
// 当前i站中加油量与耗油量的差
|
||||
int diff = gas[i] - cost[i];
|
||||
|
||||
curSum += diff;
|
||||
totalSum += diff;
|
||||
|
||||
// 若0到i的加油量都为负,则开始位置应为i+1
|
||||
if(curSum < 0) {
|
||||
curSum = 0;
|
||||
// 当i + 1 == gasSize时,totalSum < 0(此时i为gasSize - 1),油车不可能返回原点
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 若总和小于0,加油车无论如何都无法返回原点。返回-1
|
||||
if(totalSum < 0)
|
||||
return -1;
|
||||
|
||||
return start;
|
||||
}
|
||||
```
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -109,7 +109,7 @@ public:
|
||||
};
|
||||
```
|
||||
|
||||
# 题外话
|
||||
## 题外话
|
||||
|
||||
我们习惯看到的表达式都是中缀表达式,因为符合我们的习惯,但是中缀表达式对于计算机来说就不是很友好了。
|
||||
|
||||
@ -128,7 +128,7 @@ public:
|
||||
|
||||
|
||||
|
||||
# 其他语言版本
|
||||
## 其他语言版本
|
||||
|
||||
java:
|
||||
|
||||
|
@ -145,6 +145,38 @@ public:
|
||||
|
||||
## 其他语言版本
|
||||
C:
|
||||
用原来的链表操作:
|
||||
```c
|
||||
struct ListNode* removeElements(struct ListNode* head, int val){
|
||||
struct ListNode* temp;
|
||||
// 当头结点存在并且头结点的值等于val时
|
||||
while(head && head->val == val) {
|
||||
temp = head;
|
||||
// 将新的头结点设置为head->next并删除原来的头结点
|
||||
head = head->next;
|
||||
free(temp);
|
||||
}
|
||||
|
||||
struct ListNode *cur = head;
|
||||
// 当cur存在并且cur->next存在时
|
||||
// 此解法需要判断cur存在因为cur指向head。若head本身为NULL或者原链表中元素都为val的话,cur也会为NULL
|
||||
while(cur && (temp = cur->next)) {
|
||||
// 若cur->next的值等于val
|
||||
if(temp->val == val) {
|
||||
// 将cur->next设置为cur->next->next并删除cur->next
|
||||
cur->next = temp->next;
|
||||
free(temp);
|
||||
}
|
||||
// 若cur->next不等于val,则将cur后移一位
|
||||
else
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
// 返回头结点
|
||||
return head;
|
||||
}
|
||||
```
|
||||
设置一个虚拟头结点:
|
||||
```c
|
||||
/**
|
||||
* Definition for singly-linked list.
|
||||
|
@ -112,7 +112,7 @@ public:
|
||||
|
||||
**一些录友会疑惑为什么时间复杂度是O(n)**。
|
||||
|
||||
不要以为for里放一个while就以为是O(n^2)啊, 主要是看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被被操作两次,所以时间复杂度是 2 × n 也就是O(n)。
|
||||
不要以为for里放一个while就以为是O(n^2)啊, 主要是看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)。
|
||||
|
||||
## 相关题目推荐
|
||||
|
||||
|
@ -214,8 +214,8 @@ class Solution {
|
||||
|
||||
for (int i = 2; i <= prices.length; i++) {
|
||||
/*
|
||||
dp[i][0] 第i天未持有股票收益;
|
||||
dp[i][1] 第i天持有股票收益;
|
||||
dp[i][0] 第i天持有股票收益;
|
||||
dp[i][1] 第i天不持有股票收益;
|
||||
情况一:第i天是冷静期,不能以dp[i-1][1]购买股票,所以以dp[i - 2][1]买股票,没问题
|
||||
情况二:第i天不是冷静期,理论上应该以dp[i-1][1]购买股票,但是第i天不是冷静期说明,第i-1天没有卖出股票,
|
||||
则dp[i-1][1]=dp[i-2][1],所以可以用dp[i-2][1]买股票,没问题
|
||||
|
@ -466,6 +466,55 @@ func sumOfLeftLeaves(_ root: TreeNode?) -> Int {
|
||||
}
|
||||
```
|
||||
|
||||
## C
|
||||
递归法:
|
||||
```c
|
||||
int sumOfLeftLeaves(struct TreeNode* root){
|
||||
// 递归结束条件:若当前结点为空,返回0
|
||||
if(!root)
|
||||
return 0;
|
||||
|
||||
// 递归取左子树的左结点和和右子树的左结点和
|
||||
int leftValue = sumOfLeftLeaves(root->left);
|
||||
int rightValue = sumOfLeftLeaves(root->right);
|
||||
|
||||
// 若当前结点的左结点存在,且其为叶子结点。取它的值
|
||||
int midValue = 0;
|
||||
if(root->left && (!root->left->left && !root->left->right))
|
||||
midValue = root->left->val;
|
||||
|
||||
return leftValue + rightValue + midValue;
|
||||
}
|
||||
```
|
||||
|
||||
迭代法:
|
||||
```c
|
||||
int sumOfLeftLeaves(struct TreeNode* root){
|
||||
struct TreeNode* stack[1000];
|
||||
int stackTop = 0;
|
||||
|
||||
// 若传入root结点不为空,将其入栈
|
||||
if(root)
|
||||
stack[stackTop++] = root;
|
||||
|
||||
int sum = 0;
|
||||
//若栈不为空,进行循环
|
||||
while(stackTop) {
|
||||
// 出栈栈顶元素
|
||||
struct TreeNode *topNode = stack[--stackTop];
|
||||
// 若栈顶元素的左孩子为左叶子结点,将其值加入sum中
|
||||
if(topNode->left && (!topNode->left->left && !topNode->left->right))
|
||||
sum += topNode->left->val;
|
||||
|
||||
// 若当前栈顶结点有左右孩子。将他们加入栈中进行遍历
|
||||
if(topNode->right)
|
||||
stack[stackTop++] = topNode->right;
|
||||
if(topNode->left)
|
||||
stack[stackTop++] = topNode->left;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -290,6 +290,54 @@ var reconstructQueue = function(people) {
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
### C
|
||||
```c
|
||||
int cmp(const void *p1, const void *p2) {
|
||||
int *pp1 = *(int**)p1;
|
||||
int *pp2 = *(int**)p2;
|
||||
// 若身高相同,则按照k从小到大排列
|
||||
// 若身高不同,按身高从大到小排列
|
||||
return pp1[0] == pp2[0] ? pp1[1] - pp2[1] : pp2[0] - pp1[0];
|
||||
}
|
||||
|
||||
// 将start与end中间的元素都后移一位
|
||||
// start为将要新插入元素的位置
|
||||
void moveBack(int **people, int peopleSize, int start, int end) {
|
||||
int i;
|
||||
for(i = end; i > start; i--) {
|
||||
people[i] = people[i-1];
|
||||
}
|
||||
}
|
||||
|
||||
int** reconstructQueue(int** people, int peopleSize, int* peopleColSize, int* returnSize, int** returnColumnSizes){
|
||||
int i;
|
||||
// 将people按身高从大到小排列(若身高相同,按k从小到大排列)
|
||||
qsort(people, peopleSize, sizeof(int*), cmp);
|
||||
|
||||
for(i = 0; i < peopleSize; ++i) {
|
||||
// people[i]要插入的位置
|
||||
int position = people[i][1];
|
||||
int *temp = people[i];
|
||||
// 将position到i中间的元素后移一位
|
||||
// 注:因为已经排好序,position不会比i大。(举例:排序后people最后一位元素最小,其可能的k最大值为peopleSize-2,小于此时的i)
|
||||
moveBack(people, peopleSize, position, i);
|
||||
// 将temp放置到position处
|
||||
people[position] = temp;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 设置返回二维数组的大小以及里面每个一维数组的长度
|
||||
*returnSize = peopleSize;
|
||||
*returnColumnSizes = (int*)malloc(sizeof(int) * peopleSize);
|
||||
for(i = 0; i < peopleSize; ++i) {
|
||||
(*returnColumnSizes)[i] = 2;
|
||||
}
|
||||
return people;
|
||||
}
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
```typescript
|
||||
@ -309,5 +357,6 @@ function reconstructQueue(people: number[][]): number[][] {
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -184,13 +184,14 @@ public:
|
||||
class Solution {
|
||||
public int eraseOverlapIntervals(int[][] intervals) {
|
||||
Arrays.sort(intervals, (a, b) -> {
|
||||
if (a[0] == a[0]) return a[1] - b[1];
|
||||
return a[0] - b[0];
|
||||
// 按照区间右边界升序排序
|
||||
return a[1] - b[1];
|
||||
});
|
||||
|
||||
int count = 0;
|
||||
int edge = Integer.MIN_VALUE;
|
||||
for (int i = 0; i < intervals.length; i++) {
|
||||
// 若上一个区间的右边界小于当前区间的左边界,说明无交集
|
||||
if (edge <= intervals[i][0]) {
|
||||
edge = intervals[i][1];
|
||||
} else {
|
||||
@ -263,7 +264,7 @@ func min(a,b int)int{
|
||||
}
|
||||
return a
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
### Javascript:
|
||||
- 按右边界排序
|
||||
@ -306,6 +307,55 @@ var eraseOverlapIntervals = function(intervals) {
|
||||
}
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
> 按右边界排序,从左往右遍历
|
||||
|
||||
```typescript
|
||||
function eraseOverlapIntervals(intervals: number[][]): number {
|
||||
const length = intervals.length;
|
||||
if (length === 0) return 0;
|
||||
intervals.sort((a, b) => a[1] - b[1]);
|
||||
let right: number = intervals[0][1];
|
||||
let count: number = 1;
|
||||
for (let i = 1; i < length; i++) {
|
||||
if (intervals[i][0] >= right) {
|
||||
count++;
|
||||
right = intervals[i][1];
|
||||
}
|
||||
}
|
||||
return length - count;
|
||||
};
|
||||
```
|
||||
|
||||
> 按左边界排序,从左往右遍历
|
||||
|
||||
```typescript
|
||||
function eraseOverlapIntervals(intervals: number[][]): number {
|
||||
if (intervals.length === 0) return 0;
|
||||
intervals.sort((a, b) => a[0] - b[0]);
|
||||
let right: number = intervals[0][1];
|
||||
let tempInterval: number[];
|
||||
let resCount: number = 0;
|
||||
for (let i = 1, length = intervals.length; i < length; i++) {
|
||||
tempInterval = intervals[i];
|
||||
if (tempInterval[0] >= right) {
|
||||
// 未重叠
|
||||
right = tempInterval[1];
|
||||
} else {
|
||||
// 有重叠,移除当前interval和前一个interval中右边界更大的那个
|
||||
right = Math.min(right, tempInterval[1]);
|
||||
resCount++;
|
||||
}
|
||||
}
|
||||
return resCount;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -293,6 +293,50 @@ var maxProfit = function(prices, fee) {
|
||||
};
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
|
||||
> 贪心
|
||||
|
||||
```typescript
|
||||
function maxProfit(prices: number[], fee: number): number {
|
||||
if (prices.length === 0) return 0;
|
||||
let minPrice: number = prices[0];
|
||||
let profit: number = 0;
|
||||
for (let i = 1, length = prices.length; i < length; i++) {
|
||||
if (minPrice > prices[i]) {
|
||||
minPrice = prices[i];
|
||||
}
|
||||
if (minPrice + fee < prices[i]) {
|
||||
profit += prices[i] - minPrice - fee;
|
||||
minPrice = prices[i] - fee;
|
||||
}
|
||||
}
|
||||
return profit;
|
||||
};
|
||||
```
|
||||
|
||||
> 动态规划
|
||||
|
||||
```typescript
|
||||
function maxProfit(prices: number[], fee: number): number {
|
||||
/**
|
||||
dp[i][1]: 第i天不持有股票的最大所剩现金
|
||||
dp[i][0]: 第i天持有股票的最大所剩现金
|
||||
*/
|
||||
const length: number = prices.length;
|
||||
const dp: number[][] = new Array(length).fill(0).map(_ => []);
|
||||
dp[0][1] = 0;
|
||||
dp[0][0] = -prices[0];
|
||||
for (let i = 1, length = prices.length; i < length; i++) {
|
||||
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i] - fee);
|
||||
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
|
||||
}
|
||||
return Math.max(dp[length - 1][0], dp[length - 1][1]);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -225,6 +225,28 @@ var monotoneIncreasingDigits = function(n) {
|
||||
};
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
```typescript
|
||||
function monotoneIncreasingDigits(n: number): number {
|
||||
let strArr: number[] = String(n).split('').map(i => parseInt(i));
|
||||
const length = strArr.length;
|
||||
let flag: number = length;
|
||||
for (let i = length - 2; i >= 0; i--) {
|
||||
if (strArr[i] > strArr[i + 1]) {
|
||||
strArr[i] -= 1;
|
||||
flag = i + 1;
|
||||
}
|
||||
}
|
||||
for (let i = flag; i < length; i++) {
|
||||
strArr[i] = 9;
|
||||
}
|
||||
return parseInt(strArr.join(''));
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -229,6 +229,31 @@ var partitionLabels = function(s) {
|
||||
};
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
```typescript
|
||||
function partitionLabels(s: string): number[] {
|
||||
const length: number = s.length;
|
||||
const resArr: number[] = [];
|
||||
const helperMap: Map<string, number> = new Map();
|
||||
for (let i = 0; i < length; i++) {
|
||||
helperMap.set(s[i], i);
|
||||
}
|
||||
let left: number = 0;
|
||||
let right: number = 0;
|
||||
for (let i = 0; i < length; i++) {
|
||||
right = Math.max(helperMap.get(s[i])!, right);
|
||||
if (i === right) {
|
||||
resArr.push(i - left + 1);
|
||||
left = i + 1;
|
||||
}
|
||||
}
|
||||
return resArr;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
|
||||
|
@ -250,6 +250,49 @@ var lemonadeChange = function(bills) {
|
||||
return true
|
||||
};
|
||||
|
||||
```
|
||||
### C
|
||||
```c
|
||||
bool lemonadeChange(int* bills, int billsSize){
|
||||
// 分别记录五元、十元的数量(二十元不用记录,因为不会用到20元找零)
|
||||
int fiveCount = 0; int tenCount = 0;
|
||||
|
||||
int i;
|
||||
for(i = 0; i < billsSize; ++i) {
|
||||
// 分情况讨论每位顾客的付款
|
||||
switch(bills[i]) {
|
||||
// 情况一:直接收款五元
|
||||
case 5:
|
||||
fiveCount++;
|
||||
break;
|
||||
// 情况二:收款十元
|
||||
case 10:
|
||||
// 若没有五元找零,返回false
|
||||
if(fiveCount == 0)
|
||||
return false;
|
||||
// 收款十元并找零五元
|
||||
fiveCount--;
|
||||
tenCount++;
|
||||
break;
|
||||
// 情况三:收款二十元
|
||||
case 20:
|
||||
// 若可以,优先用十元和五元找零(因为十元只能找零20,所以需要尽量用掉。而5元能找零十元和二十元)
|
||||
if(fiveCount > 0 && tenCount > 0) {
|
||||
fiveCount--;
|
||||
tenCount--;
|
||||
}
|
||||
// 若没有十元,但是有三张五元。用三张五元找零
|
||||
else if(fiveCount >= 3)
|
||||
fiveCount-=3;
|
||||
// 无法找开,返回false
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 全部可以找开,返回true
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
@ -266,7 +266,61 @@ public class Solution {
|
||||
|
||||
## Python
|
||||
|
||||
```Python
|
||||
```Python3
|
||||
class TreeNode:
|
||||
def __init__(self, val = 0, left = None, right = None):
|
||||
self.val = val
|
||||
self.left = left
|
||||
self.right = right
|
||||
|
||||
|
||||
# 根据数组构建二叉树
|
||||
|
||||
def construct_binary_tree(nums: []) -> TreeNode:
|
||||
if not nums:
|
||||
return None
|
||||
# 用于存放构建好的节点
|
||||
root = TreeNode(-1)
|
||||
Tree = []
|
||||
# 将数组元素全部转化为树节点
|
||||
for i in range(len(nums)):
|
||||
if nums[i]!= -1:
|
||||
node = TreeNode(nums[i])
|
||||
else:
|
||||
node = None
|
||||
Tree.append(node)
|
||||
if i == 0:
|
||||
root = node
|
||||
for i in range(len(Tree)):
|
||||
node = Tree[i]
|
||||
if node and (2 * i + 2) < len(Tree):
|
||||
node.left = Tree[i * 2 + 1]
|
||||
node.right = Tree[i * 2 + 2]
|
||||
return root
|
||||
|
||||
|
||||
|
||||
# 算法:中序遍历二叉树
|
||||
|
||||
class Solution:
|
||||
def __init__(self):
|
||||
self.T = []
|
||||
def inorder(self, root: TreeNode) -> []:
|
||||
if not root:
|
||||
return
|
||||
self.inorder(root.left)
|
||||
self.T.append(root.val)
|
||||
self.inorder(root.right)
|
||||
return self.T
|
||||
|
||||
|
||||
|
||||
# 验证创建二叉树的有效性,二叉排序树的中序遍历应为顺序排列
|
||||
|
||||
test_tree = [3, 1, 5, -1, 2, 4 ,6]
|
||||
root = construct_binary_tree(test_tree)
|
||||
A = Solution()
|
||||
print(A.inorder(root))
|
||||
```
|
||||
|
||||
|
||||
|
@ -43,19 +43,19 @@
|
||||
|
||||
**那么二维数组在内存的空间地址是连续的么?**
|
||||
|
||||
我们来举一个例子,例如: `int[][] rating = new int[3][4];` , 这个二维数据在内存空间可不是一个 `3*4` 的连续地址空间
|
||||
我们来举一个Java的例子,例如: `int[][] rating = new int[3][4];` , 这个二维数组在内存空间可不是一个 `3*4` 的连续地址空间
|
||||
|
||||
看了下图,就应该明白了:
|
||||
|
||||
<img src='https://code-thinking.cdn.bcebos.com/pics/%E7%AE%97%E6%B3%95%E9%80%9A%E5%85%B3%E6%95%B0%E7%BB%843.png' width=600> </img></div>
|
||||
|
||||
所以**二维数据在内存中不是 `3*4` 的连续地址空间,而是四条连续的地址空间组成!**
|
||||
所以**Java的二维数组在内存中不是 `3*4` 的连续地址空间,而是四条连续的地址空间组成!**
|
||||
|
||||
# 数组的经典题目
|
||||
|
||||
在面试中,数组是必考的基础数据结构。
|
||||
|
||||
其实数据的题目在思想上一般比较简单的,但是如果想高效,并不容易。
|
||||
其实数组的题目在思想上一般比较简单的,但是如果想高效,并不容易。
|
||||
|
||||
我们之前一共讲解了四道经典数组题目,每一道题目都代表一个类型,一种思想。
|
||||
|
||||
@ -115,7 +115,7 @@
|
||||
|
||||
在这道题目中,我们再一次介绍到了**循环不变量原则**,其实这也是写程序中的重要原则。
|
||||
|
||||
相信大家又遇到过这种情况: 感觉题目的边界调节超多,一波接着一波的判断,找边界,踩了东墙补西墙,好不容易运行通过了,代码写的十分冗余,毫无章法,其实**真正解决题目的代码都是简洁的,或者有原则性的**,大家可以在这道题目中体会到这一点。
|
||||
相信大家有遇到过这种情况: 感觉题目的边界调节超多,一波接着一波的判断,找边界,拆了东墙补西墙,好不容易运行通过了,代码写的十分冗余,毫无章法,其实**真正解决题目的代码都是简洁的,或者有原则性的**,大家可以在这道题目中体会到这一点。
|
||||
|
||||
|
||||
# 总结
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
## 双链表
|
||||
|
||||
单链表中的节点只能指向节点的下一个节点。
|
||||
单链表中的指针域只能指向节点的下一个节点。
|
||||
|
||||
双链表:每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点。
|
||||
|
||||
@ -56,7 +56,7 @@
|
||||
|
||||

|
||||
|
||||
这个链表起始节点为2, 终止节点为7, 各个节点分布在内存个不同地址空间上,通过指针串联在一起。
|
||||
这个链表起始节点为2, 终止节点为7, 各个节点分布在内存的不同地址空间上,通过指针串联在一起。
|
||||
|
||||
# 链表的定义
|
||||
|
||||
|
Reference in New Issue
Block a user