前言
由于本人一边要实习,一边要上课,最近刷题也是零零散散。一直都是按 LeetCode 官方默认顺序刷,一段时间刷下来,认为效率略低或者成长性比较低下,还是少了对思路的解析和总结,决定今天开始按 Tag 刷,也是便于自己总结和温故,一天一天先给文章贴上题目,总结日后再补上。愿每个努力的人,都不悔当初。好了,开始切入正题
Next Permutation (Medium)
问题描述
题目还是之前的 排列组合, 不同的是,并不能将所有结果都推导一遍,然后再挑选,而是给出你排列组合中的某一位置,要求直接推导出下一位置。
Examples
Input
[1, 2, 5, 4, 3]
Output
[1, 3, 2, 4, 5]
算法思路
也让我想得有点晕,总结起来,这类题要按照 顺着平常的思维方式,将逻辑给理清楚,思路就相当清晰了
回想下我们是怎么排列组合的,在这里我们 按状态 把数组分为两队,我们认为 前一队是一定的,后一队是完全排列过的,这种情况下,在前一队保持不动的前提下,后一队已经达到排列组合的最后一个状态(降序),这时,我们需要从后一队中挑选出一个,比前一队最后一个数大一级的数,并和它交换,换句话说,前一队进入下一状态,后一队重置为第一状态(升序),也就是反转数组
1234 5 -> 1235 4
123 54 -> 124 35
1243 5 -> 1245 3
124 53 -> 125 34
1253 4 -> 1254 3
12 543 -> 13 245
......
5431 2 -> 5432 1
也就是相当于,在不采用回溯递归思想的前提下,靠人工逻辑来实现算法
AC 代码
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int len = nums.size();
if (len < 2) {
return;
}
// find the cut of two sides
int cut = len - 1;
while (cut > 0) {
if (nums[cut - 1] < nums[cut]) {
break;
}
cut--;
}
if (cut == 0) {
reverse(nums, 0, len - 1);
} else {
int upperClass = len - 1;
// find a upperClass class than the end of the first side
while (upperClass >= cut) {
if (nums[upperClass] > nums[cut - 1]) {
break;
}
upperClass--;
}
// change to a new arrangement
swap(nums, upperClass, cut - 1);
// reset the last side
reverse(nums, cut, len - 1);
}
}
void swap(vector<int>& nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
void reverse(vector<int>& nums, int start, int end) {
for (int i = start; i <= (end + start) / 2; i++) {
swap(nums, i, start + end - i);
}
}
};
Maximum Product Subarray (Medium)
问题描述
最大子数组之积
Examples
Input
[3, -2, 3, 0, -2, -4]
Output
8
方法一
算法思路
一开始本来想用 贪心,但遍历时,结果如果出现负数,并不会知道后面是否还有成对的负数让结果变正。如果给你一个这样的数组,按照人脑的思考习惯,你肯定会先把数组从头到尾看一遍,找出成队的负数。根据这个启示,我的思路如下展开
首先标记 第一个负数 和 最后一个负数
若负数 全成对,最大值肯定是 全部之积
若负数 单一个,要达到最大乘积,要么把 最左边的负数 抛掉,要么把 最右边的负数 抛掉
碰到零便 递归
AC 代码
class Solution {
public:
int maxProduct(vector<int>& nums) {
int len = nums.size();
int MAX = 0x80000000;
int startNeg = 0, endNeg = len - 1;
bool firstNeg = true;
int negCount = 0;
// find indeces of the first and last negtive
// start recursion when meets zero and refresh the length
for (int i = 0; i < len; i++) {
if (nums[i] == 0) {
// zero
vector<int> vec(nums.begin() + i + 1, nums.end());
int temp = maxProduct(vec);
MAX = max(MAX, temp);
len = i;
break;
} else if (nums[i] < 0) {
// negtive
negCount++;
if (firstNeg) {
startNeg = i;
firstNeg = false;
}
endNeg = i;
}
}
if (len <= 0) {
// it explains that the vector is cut by zero
// so MAX may be 0
MAX = max(MAX, 0);
} else if (len == 1) {
// avoid overflow
MAX = max(MAX, nums[0]);
} else if (negCount % 2 == 0) {
// the negative numbers are all in pairs
MAX = max(MAX, product(nums, 0, len - 1));
} else {
// before last negtive or after first negtive
int leftMul = product(nums, 0, endNeg - 1);
int leftOverlap = product(nums, 0, startNeg);
int rightOverlap = product(nums, endNeg, len - 1);
int rightMul = leftMul * rightOverlap / leftOverlap;
MAX = max(MAX, leftMul);
MAX = max(MAX, rightMul);
}
if (len != nums.size()) {
MAX = max(MAX, 0);
}
return MAX;
}
int product(vector<int>& nums, int low, int high) {
int mul = 1;
for (int i = low; i <= high; i++) {
mul *= nums[i];
}
return mul;
}
};
方法二
算法思路
方法一思路虽然清晰,但代码并不优雅,还是有很多细节要填坑,于是就有了下面的解法
前面说到,限制是遍历时并不知道后续是否存在负数,那我就把 正数 和 负数 都存起来不就行了?
动态规划 思路如下
需要记录的是正数最大值和负数最小值,两个极值
下一节点的极值,要么是顺着 上一状态的极值,要么就是 该节点
AC 代码
class Solution {
public int maxProduct(int[] A) {
if (A.length == 0) {
return 0;
}
int maxherepre = A[0];
int minherepre = A[0];
int maxsofar = A[0];
int maxhere, minhere;
for (int i = 1; i < A.length; i++) {
maxhere = Math.max(Math.max(maxherepre * A[i], minherepre * A[i]), A[i]);
minhere = Math.min(Math.min(maxherepre * A[i], minherepre * A[i]), A[i]);
maxsofar = Math.max(maxhere, maxsofar);
maxherepre = maxhere;
minherepre = minhere;
}
return maxsofar;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。