🛸~~ 🚁🚁🚁🛩️🛩️�|

n1ce2cv

园龄:5年2个月粉丝:4关注:1

Top100(下)

Top100(下)

20. 有效的括号

bool isValid(char *s) {
int len = strlen(s);
if (len % 2 == 1) return false;
// 符号栈
char *stack = (char *) malloc(sizeof(char) * (len + 1));
int top = 0;
for (int i = 0; i < len; ++i) {
char cur = s[i];
// 栈空直接插入
if (top == 0) {
if (cur == ')' || cur == ']' || cur == '}') return false;
stack[top++] = cur;
continue;
}
if ((cur == ')' && stack[top - 1] == '(')
|| (cur == ']' && stack[top - 1] == '[')
|| (cur == '}' && stack[top - 1] == '{')) {
// 与栈顶抵消
top--;
} else {
// 抵消不了,就入栈
stack[top++] = cur;
}
}
return top == 0;
}

155. 最小栈

// 使用辅助栈记录当前最小值
typedef struct {
// 数据栈
int *stack;
int top;
int maxSize;
// 最小栈,保存当前位置时,数据栈中最小元素
int *minStack;
} MinStack;
MinStack *minStackCreate() {
MinStack *s = (MinStack *) malloc(sizeof(MinStack));
s->stack = (int *) malloc(sizeof(int) * 10);
s->minStack = (int *) malloc(sizeof(int) * 10);
s->top = 0;
s->maxSize = 10;
return s;
}
void minStackPush(MinStack *obj, int x) {
// 动态扩容到1.75倍
if (obj->top == obj->maxSize) {
int newSize = (obj->maxSize << 1) - (obj->maxSize >> 2);
// realloc重新调整内存大小
obj->stack = (int *) realloc(obj->stack, sizeof(int) * newSize);
obj->minStack = (int *) realloc(obj->minStack, sizeof(int) * newSize);
obj->maxSize = newSize;
}
// 同步修改最小栈,栈顶保存当前数据栈中最小元素
if (obj->top == 0 || x < obj->minStack[obj->top - 1])
// 栈空或者新元素比上个最小元素还小
obj->minStack[obj->top] = x;
else
// 否则使用上个最小元素
obj->minStack[obj->top] = obj->minStack[obj->top - 1];
// 把数据压入数据栈
obj->stack[obj->top++] = x;
}
void minStackPop(MinStack *obj) {
obj->top--;
}
int minStackTop(MinStack *obj) {
return obj->stack[obj->top - 1];
}
int minStackGetMin(MinStack *obj) {
return obj->minStack[obj->top - 1];
}
void minStackFree(MinStack *obj) {
free(obj);
obj = NULL;
}

394. 字符串解码

class Solution {
public:
string decodeString(string s) {
stack<char> stk;
deque<char> deq;
for (int i = 0; i < s.size(); ++i) {
while (i < s.size() && s[i] != ']') {
stk.emplace(s[i]);
i++;
}
// 已经到结尾了
if (s[i] != ']') break;
// 记录括号内的字符串
string tempStr;
while (stk.top() != '[') {
deq.emplace_front(stk.top());
stk.pop();
}
// 弹出 '['
stk.pop();
while (!deq.empty()) {
tempStr += deq.front();
deq.pop_front();
}
// 记录重复次数
int count = 0;
while (!stk.empty() && stk.top() >= '0' && stk.top() <= '9') {
deq.emplace_front(stk.top());
stk.pop();
}
while (!deq.empty()) {
count *= 10;
count += deq.front() - '0';
deq.pop_front();
}
// 记录重复 count 次的字符串
string repeated;
for (int j = 0; j < count; ++j)
repeated.append(tempStr);
// 重新入栈
for (auto &c: repeated)
stk.emplace(c);
}
// 全部出栈
string res;
deq.clear();
while (!stk.empty()) {
deq.emplace_front(stk.top());
stk.pop();
}
while (!deq.empty()) {
res += deq.front();
deq.pop_front();
}
return res;
}
};
class Solution {
public:
// "abc2[a2[c]t]2[k]xyz";
// abcacctacctkkxyz
string decodeString(const string &s) {
// 临时记录数字和数字前的字符串
int multi = 0;
string res;
stack<int> stack_multi;
stack<string> stack_res;
for (char c: s) {
if (c == '[') {
// 遇到左括号,说明左括号前的数字已经被确定,存入栈中
stack_multi.emplace(multi);
// 数字之前的字符串也确定了,存入栈中
stack_res.emplace(res);
// 清空这两个临时变量
multi = 0;
res.clear();
} else if (c == ']') {
// 取出当前括号内字符串应该重复的次数
int cur_multi = stack_multi.top();
stack_multi.pop();
// 重复对应的次数后记录到tmp中
string tmp;
for (int i = 0; i < cur_multi; i++) tmp += res;
// 再接到之前数字前已经出现的字符串后面
res = stack_res.top() + tmp;
stack_res.pop();
} else if (c >= '0' && c <= '9') {
// 确定重复次数
multi = multi * 10 + (c - '0');
} else {
// 记录数字前的字符串,或者是追加右括号后面的字符串
res.push_back(c);
}
}
return res;
}
};
class Solution {
public:
// 当前处理位置的下标
int index = 0;
// 递归
string decodeString(string s) {
string res;
int multi = 0;
while (index < s.size()) {
if (s[index] >= '0' && s[index] <= '9') {
// 计算重复次数
multi = 10 * multi + s[index] - '0';
} else if (s[index] == '[') {
// 递归处理括号内的字符串
index++;
string temp = decodeString(s);
// 重复 multi 次
while (multi > 0) {
res += temp;
multi--;
}
} else if (s[index] == ']') {
// 结束递归,返回括号内的字符串
break;
} else {
res += s[index];
}
index++;
}
return res;
}
};

739. 每日温度

class Solution {
public:
vector<int> dailyTemperatures(vector<int> &temperatures) {
vector<int> res(temperatures.size(), 0);
// 单调递减栈,存下标
stack<int> stk;
for (int i = 0; i < temperatures.size(); ++i) {
// 把栈顶比当前元素小的全都弹出
while (!stk.empty() && temperatures[stk.top()] < temperatures[i]) {
int index = stk.top();
stk.pop();
// 记录下标差
res[index] = i - index;
}
// 直接入栈,栈底到栈顶递减
stk.emplace(i);
}
return res;
}
};

84. 柱状图中最大的矩形

#include <iostream>
#include <vector>
#include <map>
#include <stack>
using namespace std;
class Solution {
public:
int largestRectangleArea(vector<int> &heights) {
int len = heights.size();
int res = 0;
// 栈底到栈顶递增
stack<int> stk;
// 以当前位置的高度为矩形的高度,向两侧扩展,直到两侧第一个更低的位置,构成矩形的宽
for (int i = 0; i < len; ++i) {
while (!stk.empty() && (heights[stk.top()] >= heights[i])) {
int popIndex = stk.top();
stk.pop();
int left = stk.empty() ? -1 : stk.top();
int width = i - left - 1;
res = max(res, heights[popIndex] * width);
}
stk.emplace(i);
}
while (!stk.empty()) {
int popIndex = stk.top();
stk.pop();
int left = stk.empty() ? -1 : stk.top();
int width = len - left - 1;
res = max(res, heights[popIndex] * width);
}
return res;
}
};
class Solution {
public:
int largestRectangleArea(vector<int> &heights) {
int result = 0;
// 单调递增栈,存放下标
stack<int> stk;
// 数组头部加入元素0
heights.insert(begin(heights), 0);
// 数组尾部加入元素0
heights.push_back(0);
for (int i = 0; i < heights.size(); i++) {
// 依次弹出大于当前元素的栈顶元素
// 等于当前元素的栈顶右边界是暂时无法确定的
// 因为它可以经过当前元素继续向右边扩展,直到遇到第一个严格小于它的元素,所有先不用出栈
while (!stk.empty() && heights[i] < heights[stk.top()]) {
int mid = stk.top();
stk.pop();
// 每弹出一个栈顶,都计算一下区间 [弹出的栈顶, 当前元素) 内能形成的最大矩形面积
int w = i - stk.top() - 1;
int h = heights[mid];
result = max(result, w * h);
}
stk.emplace(i);
}
return result;
}
};

215. 数组中的第K个最大元素

// 用整个数组来建立大顶堆,然后从中取k次堆顶,第k次取出的就是结果
class Solution {
public:
// 自顶向下调整堆,len 是当前堆的大小,复杂度 O(logn)
void adjustHeap(vector<int> &array, int currentIndex, int len) {
// 要调整位置的元素
int temp = array[currentIndex];
// 左孩子下标
int leftChildIndex = 2 * currentIndex + 1;
// 自顶向下,调整到最后一个节点
while (leftChildIndex <= (len - 1)) {
// 把左右孩子中较大者的下标赋给 leftChildIndex
if (leftChildIndex < (len - 1)
&& (array[leftChildIndex] < array[leftChildIndex + 1]))
leftChildIndex++;
// 和当前元素比较大小,决定要不要调整堆
// 1. 不需要调整
if (array[leftChildIndex] <= temp) break;
// 2. 需要调整
array[currentIndex] = array[leftChildIndex];
currentIndex = leftChildIndex;
leftChildIndex = 2 * currentIndex + 1;
}
// 放在最终的位置
array[currentIndex] = temp;
}
int findKthLargest(vector<int> &nums, int k) {
// 建堆:从最后一个非叶子节点开始向上调整每个节点,复杂度 O(n)
// 最后一个非叶子节点下标为 n/2-1,下标从0开始
for (int i = nums.size() / 2 - 1; i >= 0; i--) {
adjustHeap(nums, i, nums.size());
}
// 每次把大顶堆的堆顶移到末尾,并且堆的大小减一,最终形成升序列表
for (int len = nums.size() - 1, count = k; count > 0; len--, count--) {
swap(nums[0], nums[len]);
adjustHeap(nums, 0, len);
}
return nums[nums.size() - k];
}
};
// 用整个数组来建立大顶堆,然后从中取k次堆顶,第k次取出的就是结果
class Solution {
public:
int findKthLargest(vector<int> &nums, int k) {
priority_queue<int> heap{begin(nums), end(nums)};
while (k > 1) {
heap.pop();
k--;
}
return heap.top();
}
};
// 用前 k 个元素建立小顶堆,后续的元素于堆顶比较,更大的话就加入堆
class Solution {
public:
// 小顶堆的调整
void adjustHeap(vector<int> &nums, int currentIndex, int len) {
int temp = nums[currentIndex];
int leftChildIndex = 2 * currentIndex + 1;
while (leftChildIndex <= len - 1) {
if (leftChildIndex < len - 1
&& (nums[leftChildIndex] > nums[leftChildIndex + 1]))
leftChildIndex++;
if (nums[leftChildIndex] >= temp)break;
nums[currentIndex] = nums[leftChildIndex];
currentIndex = leftChildIndex;
leftChildIndex = 2 * currentIndex + 1;
}
nums[currentIndex] = temp;
}
int findKthLargest(vector<int> &nums, int k) {
// 用前 k 个元素建立小顶堆
for (int i = k / 2 - 1; i >= 0; i--) {
adjustHeap(nums, i, k);
}
for (int i = k; i < nums.size(); ++i) {
if (nums[0] < nums[i]) {
// 比堆顶大就替换堆顶,然后调整小顶堆
nums[0] = nums[i];
adjustHeap(nums, 0, k);
}
}
// 最终包含 k 个元素的小顶堆堆顶就是第 k 个大的元素
return nums[0];
}
};
// 用前 k 个元素建立小顶堆,后续的元素于堆顶比较,更大的话就加入堆
class Solution {
public:
int findKthLargest(vector<int> &nums, int k) {
// 小顶堆
priority_queue<int, vector<int>, greater<>> heap{begin(nums), begin(nums) + k};
for (int i = k; i < nums.size(); ++i) {
if (nums[i] > heap.top()) {
heap.pop();
heap.emplace(nums[i]);
}
}
return heap.top();
}
};
class Solution {
public:
int res;
int target;
// 快速选择
void quickSelect(vector<int> &nums, int left, int right) {
if (left > right) return;
if (left == right) {
res = nums[left];
return;
}
int i = left;
int j = right;
int pivot = nums[left];
while (i < j) {
while (i < j && pivot <= nums[j]) {
j--;
}
if (i < j) {
nums[i] = nums[j];
i++;
}
while (i < j && pivot >= nums[i]) {
i++;
}
if (i < j) {
nums[j] = nums[i];
j--;
}
}
nums[i] = pivot;
if (i < target) quickSelect(nums, i + 1, right);
if (i > target) quickSelect(nums, left, i - 1);
if (i == target) res = nums[i];
}
int findKthLargest(vector<int> &nums, int k) {
// 找第 k 大的就是找递增序列中下标为 target 的
target = nums.size() - k;
quickSelect(nums, 0, nums.size() - 1);
return res;
}
};

347. 前 K 个高频元素

class Solution {
public:
void adjustHeap(vector<pair<int, int>> &array, int currentIndex, int len) {
pair<int, int> temp = array[currentIndex];
int leftChildIndex = 2 * currentIndex + 1;
while (leftChildIndex <= (len - 1)) {
// 从左右孩子中选取频率更小的
if (leftChildIndex < (len - 1)
&& (array[leftChildIndex].second > array[leftChildIndex + 1].second))
leftChildIndex++;
if (array[leftChildIndex].second >= temp.second) break;
array[currentIndex] = array[leftChildIndex];
array[currentIndex] = array[leftChildIndex];
currentIndex = leftChildIndex;
leftChildIndex = 2 * currentIndex + 1;
}
array[currentIndex] = temp;
}
vector<int> topKFrequent(vector<int> &nums, int k) {
// 统计频率
unordered_map<int, int> map;
for (auto &num: nums) {
map[num]++;
}
// 用 map 统计出的数据初始化 heap
vector<pair<int, int>> heap(begin(map), end(map));
// 对前 k 个元素建立小顶堆
for (int i = k / 2 - 1; i >= 0; i--) {
adjustHeap(heap, i, k);
}
// 将后面的元素与当前小顶堆堆顶比较
for (int i = k; i < heap.size(); ++i) {
// 出现频率更大的话,才能进入小顶堆
if (heap[i].second > heap[0].second) {
heap[0] = heap[i];
adjustHeap(heap, 0, k);
}
}
vector<int> res(k);
for (int i = 0; i < k; ++i) {
res[i] = heap[i].first;
}
return res;
}
};
class Solution {
public:
// 小顶堆
struct cmp {
bool operator()(pair<int, int> &lhs, pair<int, int> &rhs) {
return lhs.second > rhs.second;
}
};
vector<int> topKFrequent(vector<int> &nums, int k) {
// 记录出现次数
unordered_map<int, int> map;
for (int i = 0; i < nums.size(); i++) {
map[nums[i]]++;
}
// 用 map 前 k 个元素初始化大小为 k 的小顶堆
auto it = begin(map);
// 迭代器后移 k 步
advance(it, k);
priority_queue<pair<int, int>, vector<pair<int, int>>, cmp> heap{begin(map), it};
while (it != map.end()) {
// 比小顶堆的堆顶元素出现的频率大的话就替换掉堆顶,并调整堆
if (it->second > heap.top().second) {
heap.pop();
heap.emplace(it->first, it->second);
}
it++;
}
// 小顶堆中最终剩下的就是数组中出现频率前 k 高的元素
vector<int> res;
while (!heap.empty()) {
res.emplace_back(heap.top().first);
heap.pop();
}
return res;
}
};
class Solution {
public:
// 桶排序:空间换时间
vector<int> topKFrequent(vector<int> &nums, int k) {
// 记录频率,<元素,元素出现次数>
unordered_map<int, int> map;
for (auto &num: nums)
map[num]++;
// 记录最大频率,方便设置桶的总数
int maxFrequency;
for (auto it = begin(map); it != end(map); it++)
maxFrequency = max(maxFrequency, it->second);
// 桶:下标作为出现的次数,值为元素数组
vector<vector<int>> bucket(maxFrequency + 1);
// 根据元素出现次数,把元素放进对应的桶里
for (auto it = begin(map); it != end(map); it++) {
bucket[it->second].emplace_back(it->first);
}
vector<int> res;
for (int i = bucket.size() - 1; i >= 0 && k > 0; i--) {
// 根据出现频率由高到低,把有元素的桶里的元素加入到最终结果
while (!bucket[i].empty()) {
res.emplace_back(bucket[i].back());
bucket[i].pop_back();
k--;
}
}
return res;
}
};
class Solution {
public:
int target;
vector<int> res;
// 根据后面 k 个最大的元素生产最终结果
void generate(vector<pair<int, int>> &array) {
for (int i = target; i < array.size(); ++i)
res.emplace_back(array[i].first);
}
// 快速选择
void quickSelect(vector<pair<int, int>> &array, int left, int right) {
if (left > right) return;
if (left == right) {
generate(array);
return;
}
pair<int, int> pivot = array[left];
int i = left;
int j = right;
while (i < j) {
while (i < j && pivot.second <= array[j].second) {
j--;
}
if (i < j) {
array[i] = array[j];
i++;
}
while (i < j && pivot.second >= array[i].second) {
i++;
}
if (i < j) {
array[j] = array[i];
j--;
}
}
array[i] = pivot;
if (i < target) quickSelect(array, i + 1, right);
if (i > target) quickSelect(array, left, i - 1);
if (i == target) generate(array);
}
vector<int> topKFrequent(vector<int> &nums, int k) {
// 记录频率,<元素,元素出现次数>
unordered_map<int, int> map;
for (auto &num: nums)
map[num]++;
// 用统计出的频率初始化数组
vector<pair<int, int>> array{begin(map), end(map)};
// 找到第 k 个大的元素所在的下标 target 即可
target = array.size() - k;
quickSelect(array, 0, array.size() - 1);
return res;
}
};

295. 数据流的中位数

class MedianFinder {
public:
// 大顶堆存左边序列
priority_queue<int, vector<int>, less<>> maxHeap;
// 小顶堆存右边序列
priority_queue<int, vector<int>, greater<>> minHeap;
MedianFinder() {}
void addNum(int num) {
// 大顶堆空或者比大顶堆堆顶小,加入大顶堆
if (maxHeap.empty() || num <= maxHeap.top()) {
maxHeap.push(num);
// 控制两个堆元素一样多
if (minHeap.size() == maxHeap.size() - 2) {
minHeap.push(maxHeap.top());
maxHeap.pop();
}
} else {
minHeap.push(num);
// 如果不能一样多,就把多出的一个元素放到左边的大顶堆中
if (minHeap.size() - 1 == maxHeap.size()) {
maxHeap.push(minHeap.top());
minHeap.pop();
}
}
}
double findMedian() {
if (maxHeap.size() > minHeap.size()) return maxHeap.top();
return (maxHeap.top() + minHeap.top()) / 2.0;
}
};
// todo
class MedianFinder {
multiset<int> nums;
multiset<int>::iterator left, right;
public:
MedianFinder() : left(nums.end()), right(nums.end()) {}
void addNum(int num) {
const size_t n = nums.size();
nums.insert(num);
if (!n) {
left = right = nums.begin();
} else if (n & 1) {
if (num < *left) {
left--;
} else {
right++;
}
} else {
if (num > *left && num < *right) {
left++;
right--;
} else if (num >= *right) {
left++;
} else {
right--;
left = right;
}
}
}
double findMedian() {
return (*left + *right) / 2.0;
}
};
// todo 进阶1、2

贪心

121. 买卖股票的最佳时机

int maxProfit(int *prices, int pricesSize) {
if (pricesSize == 1) return 0;
int res = 0;
int min = prices[0];
for (int i = 1; i < pricesSize; ++i) {
if (prices[i] < min) {
// 找到更少的购买价格
min = prices[i];
} else if (prices[i] - min > res) {
// 检查能否获得更高的利润
res = prices[i] - min;
}
}
return res;
}

55. 跳跃游戏

bool res;
// 标记是否已经确定最终结果了
bool flag;
void recursive(int *nums, int numsSize, int left, int right) {
if (flag) return;
// 记录从[left, right]能到达的最远处
int maxRight = right;
for (int i = left; i <= right; ++i) {
// 从当前位置能到的最远处
int nextIndex = nums[i] + i;
if (nextIndex > maxRight) maxRight = nextIndex;
}
if (maxRight >= numsSize - 1) {
// 能到末尾
res = true;
flag = true;
} else if (maxRight == right) {
// 连自己的区间都跳不出去
res = false;
flag = true;
} else {
// 能跳出自己的区间,但还没到达末尾
recursive(nums, numsSize, right + 1, maxRight);
}
}
bool canJump(int *nums, int numsSize) {
flag = false;
recursive(nums, numsSize, 0, 0);
return res;
}
bool canJump(int *nums, int numsSize) {
// 右边能到达的最远处
int rightMax = 0;
for (int i = 0; i < numsSize; ++i) {
// 到不了i这个位置
if (rightMax < i) return false;
// 能到i的位置,检查从i处最远到哪
if (rightMax < i + nums[i]) rightMax = i + nums[i];
// 能到末尾,直接返回
if (rightMax >= numsSize - 1) return true;
}
return true;
}

45. 跳跃游戏 II

// 动态规划
int jump(int *nums, int numsSize) {
// 到达nums[i]的最小步数
int dp[numsSize];
for (int i = 0; i < numsSize; ++i) dp[i] = 0x7fffffff;
dp[0] = 0;
for (int i = 0; i < numsSize; ++i) {
// 从i处能到达的范围
int start = i + 1;
int end = nums[i] + i;
if (end >= numsSize) end = numsSize - 1;
for (int j = start; j <= end; ++j)
// 从当前i处多跳一步就到了
if (dp[i] + 1 < dp[j]) dp[j] = dp[i] + 1;
}
return dp[numsSize - 1];
}
// 递归写法
int steps;
// 标记是否已经确定最终结果了
bool flag;
void recursive(int *nums, int numsSize, int left, int right) {
if (flag) return;
if (right >= numsSize - 1) {
// 能到末尾
flag = true;
return;
}
// 到不了,至少还得跳一次
steps++;
// 记录从[left, right]能到达的最远处
int maxRight = right;
for (int i = left; i <= right; ++i) {
// 从当前位置能到的最远处
int nextIndex = nums[i] + i;
if (nextIndex > maxRight) maxRight = nextIndex;
}
// 处理子问题,从下个区间能到哪些位置
recursive(nums, numsSize, right + 1, maxRight);
}
int jump(int *nums, int numsSize) {
steps = 0;
flag = false;
recursive(nums, numsSize, 0, 0);
return steps;
}
// 迭代写法
int jump(int *nums, int numsSize) {
int left = 0, right = 0;
int steps = 0;
while (right <= numsSize - 2) {
// 到不了,至少还有跳一次
steps++;
// 右边能到的最远处
int rightMax = right;
for (int i = left; i <= right; ++i)
if (rightMax < nums[i] + i) rightMax = nums[i] + i;
left = right + 1;
right = rightMax;
}
return steps;
}
// 迭代优化版,少了个while循环处理step++的过程,放到for中处理
int jump(int *nums, int numsSize) {
int right = 0;
int steps = 0;
int rightMax = 0;
for (int i = 0; i <= numsSize - 2; ++i) {
// 更新右边能到达的最远距离
if (rightMax < nums[i] + i) rightMax = nums[i] + i;
// [i, right]区间处理完了,更新right,处理下一步能到达的区间[right+1, xxx]
if (i == right) {
right = rightMax;
steps++;
}
}
return steps;
}

763. 划分字母区间

// 与56题合并区间类似
struct Pair {
// 记录字符第一次出现的位置
int firstPosition;
// 记录是哪个字符
char ch;
};
int cmp(const void *a, const void *b) {
return (*(struct Pair *) a).firstPosition - (*(struct Pair *) b).firstPosition;
}
int *partitionLabels(char *s, int *returnSize) {
const int sizeOfChar = 26;
// 记录第一次出现的位置
int firstPositions[sizeOfChar];
// 记录最后一次出现的位置
int lastPositions[sizeOfChar];
memset(firstPositions, -1, sizeof(firstPositions));
memset(lastPositions, -1, sizeof(lastPositions));
// strlen不要放到for中,不然每次循环都要计算下长度
int len = strlen(s);
for (int i = 0; i < len; ++i) {
if (firstPositions[s[i] - 'a'] == -1) firstPositions[s[i] - 'a'] = i;
lastPositions[s[i] - 'a'] = i;
}
// 对于每个在s中出现的字符,都有一个区间[firstPositions, lastPosition]
// 不同两个字符要是区间相交了,就把区间合并,最终把无法合并的区间追加到结果中
struct Pair *pair = (struct Pair *) malloc(sizeof(struct Pair) * sizeOfChar);
// 实际出现的字母种数
int pairSize = 0;
for (int i = 0; i < sizeOfChar; ++i) {
// 这个字符没出现就不处理
if (firstPositions[i] == -1) continue;
pair[pairSize].firstPosition = firstPositions[i];
pair[pairSize].ch = i + 'a';
pairSize++;
}
// 按字符首次出现的位置排序,方便合并区间
qsort(pair, pairSize, sizeof(struct Pair), cmp);
*returnSize = 0;
int *res = (int *) malloc(sizeof(int) * len);
int index = 0;
while (index < pairSize) {
// 第一个区间的左端点
int left = pair[index].firstPosition;
// 第一个区间的待定右端点,可能会和其他字符的区间合并
int right = lastPositions[pair[index].ch - 'a'];
// 移到pair中的下个字符
index++;
while (index < pairSize && pair[index].firstPosition < right) {
// 和下个字符的区间有重叠,就合并区间,右端点取较大者
if (lastPositions[pair[index].ch - 'a'] > right)
right = lastPositions[pair[index].ch - 'a'];
index++;
}
// 记录区间长度
res[(*returnSize)++] = right - left + 1;
}
return res;
}
int *partitionLabels(char *s, int *returnSize) {
int len = strlen(s);
// 第一次遍历字符串记录最后一次出现的位置
int lastPositions[26];
for (int i = 0; i < len; i++)
lastPositions[s[i] - 'a'] = i;
*returnSize = 0;
int *res = malloc(sizeof(int) * len);
int left = 0, right = 0;
// 第二次遍历字符串
// 从left遍历到right,若right更新了,就继续遍历到新的right
// 否则,遍历到right时,表明其中所有字符的右边界都小于等于right,可以归为一个区间
for (int i = 0; i < len; i++) {
if (lastPositions[s[i] - 'a'] > right)
right = lastPositions[s[i] - 'a'];
if (i == right) {
res[(*returnSize)++] = right - left + 1;
left = right + 1;
}
}
return res;
}

动态规划

70. 爬楼梯

int climbStairs(int n) {
int dp[46];
// 一次爬一层,只有一种方法
dp[1] = 1;
// 两次爬一层或者一次爬两层,一共两种方法
dp[2] = 2;
int i = 3;
while (i <= n) {
// i层可由i-2层爬两个台阶到达,或者由i-1层爬一个台阶到达
// 爬到i层的方法总数为这两种爬法的方法总数和
dp[i] = dp[i - 2] + dp[i - 1];
i++;
}
return dp[n];
}
// 空间复杂度O(1)
int climbStairs(int n) {
if (n < 3) return n;
int left = 1;
int mid = 2;
int right;
int count = n - 2;
while (count-- > 0) {
right = left + mid;
left = mid;
mid = right;
}
return right;
}

118. 杨辉三角

int **generate(int numRows, int *returnSize, int **returnColumnSizes) {
*returnSize = numRows;
*returnColumnSizes = (int *) malloc(sizeof(int) * numRows);
int **res = (int **) malloc(sizeof(int *) * numRows);
for (int i = 0; i < numRows; ++i) {
res[i] = (int *) malloc(sizeof(int) * (i + 1));
(*returnColumnSizes)[i] = i + 1;
}
for (int i = 0; i < numRows; ++i) {
res[i][0] = 1;
res[i][i] = 1;
for (int j = 1; j <= i - 1; ++j) {
res[i][j] = res[i - 1][j - 1] + res[i - 1][j];
}
}
return res;
}

198. 打家劫舍

int max(int a, int b) {
return a > b ? a : b;
}
int rob(int *nums, int numsSize) {
if (numsSize == 1) return nums[0];
// dp[i]表示偷i间房子的最大金额
int dp[numsSize];
dp[0] = nums[0];
dp[1] = max(nums[0], nums[1]);
for (int i = 2; i < numsSize; ++i) {
// 当前位置偷,dp[i] = dp[i-2] + nums[i]
// 当前位置不偷,dp[i] = dp[i-1]
dp[i] = max(dp[i-1], dp[i-2] + nums[i]);
}
return dp[numsSize - 1];
}
int max(int a, int b) {
return a > b ? a : b;
}
// 空间优化
int rob(int *nums, int numsSize) {
int left = 0;
int mid = 0;
int right = 0;
for (int i = 0; i < numsSize; ++i) {
right = max(mid, left + nums[i]);
left = mid;
mid = right;
}
return right;
}

279. 完全平方数

int min(int a, int b) {
return a > b ? b : a;
}
int numSquares(int n) {
int dp[n + 1];
dp[0] = 0;
for (int i = 1; i <= n; ++i) {
// 最坏情况是i个1
dp[i] = i;
// 枚举1到sqrt(i)
for (int j = 1; j * j <= i; ++j) {
// i可有一个完全平方数j*j与余数i-j*j构成
// i-j*j最少需要dp[i-j*j]个平方数相加构成,j*j只需一个平方数就能构成
dp[i] = min(dp[i], dp[i - j * j] + 1);
}
}
return dp[n];
}
// todo 四平方和定理
// 判断是否为完全平方数
bool isPerfectSquare(int x) {
int y = sqrt(x);
return y * y == x;
}
// 判断是否能表示为 4^k*(8m+7)
bool checkAnswer4(int x) {
while (x % 4 == 0) {
x /= 4;
}
return x % 8 == 7;
}
int numSquares(int n) {
if (isPerfectSquare(n)) {
return 1;
}
if (checkAnswer4(n)) {
return 4;
}
for (int i = 1; i * i <= n; i++) {
int j = n - i * i;
if (isPerfectSquare(j)) {
return 2;
}
}
return 3;
}

322. 零钱兑换

class Solution {
public:
// 记忆化搜索
vector<int> count;
int recursive(vector<int> &coins, int rem) {
if (rem < 0) return -1;
if (rem == 0) return 0;
// 已经有总额为 rem 的最少硬币数,直接返回
if (count[rem - 1] != 0) return count[rem - 1];
// 计算总额为 rem 的最少硬币数
int Min = INT_MAX;
for (int coin: coins) {
// 组成金额 rem 最少的硬币数就是组成金额 rem - coin 的最少硬币数量 + 1
int res = recursive(coins, rem - coin);
// 从中取最小值
if (res >= 0) Min = min(Min, res + 1);
}
count[rem - 1] = Min == INT_MAX ? -1 : Min;
return count[rem - 1];
}
int coinChange(vector<int> &coins, int amount) {
if (amount < 1) return 0;
count.resize(amount);
return recursive(coins, amount);
}
};
class Solution {
public:
int coinChange(vector<int> &coins, int amount) {
// dp[i] 为总额为 i 的最少硬币数
vector<int> dp(amount + 1, amount + 1);
// 总额为 0 的硬币数为 0
dp[0] = 0;
// 自下而上
for (int sum = 1; sum <= amount; ++sum) {
for (auto &coin: coins) {
// 硬币面额比总额还大
if (coin > sum) continue;
// 更新最小值
dp[sum] = min(dp[sum], dp[sum - coin] + 1);
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
};

139. 单词拆分

class Solution {
public:
bool wordBreak(string s, vector<string> &wordDict) {
// 存入集合,方便判断
unordered_set<string> dict;
for (auto &word: wordDict) dict.insert(word);
// dp[i] 表示能否成功拆分 s[0,i-1]
vector<bool> dp(s.size() + 1, false);
dp[0] = true;
for (int i = 1; i <= s.size(); ++i) {
for (int j = 0; j < i; ++j) {
// s[0,j-1] 这 j 个字符构成的字符串能被拆分的情况下,判断子串 s[j, i) 是否在字典中
if (dp[j] && dict.find(s.substr(j, i - j)) != dict.end()) {
dp[i] = true;
break;
}
}
}
return dp[s.size()];
}
};

300. 最长递增子序列

class Solution {
public:
int lengthOfLIS(vector<int> &nums) {
int len = nums.size();
if (len == 0) return 0;
vector<int> dp(len, 0);
for (int i = 0; i < len; ++i) {
dp[i] = 1;
for (int j = 0; j < i; ++j) {
if (nums[j] < nums[i])
dp[i] = max(dp[i], dp[j] + 1);
}
}
return *max_element(dp.begin(), dp.end());
}
};
// 贪心 + 二分
class Solution {
public:
int lengthOfLIS(vector<int> &nums) {
int length = 1, len = nums.size();
if (len == 0) return 0;
vector<int> d(len + 1, 0);
d[length] = nums[0];
for (int i = 1; i < len; ++i) {
if (nums[i] > d[length]) {
d[++length] = nums[i];
} else {
// 如果找不到说明所有的数都比 nums[i] 大,此时要更新 d[1],所以这里将 pos 设为 0
int l = 1, r = length, pos = 0;
while (l <= r) {
int mid = (l + r) >> 1;
if (d[mid] < nums[i]) {
pos = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
d[pos + 1] = nums[i];
}
}
return length;
}
};

152. 乘积最大子数组

int min(int a, int b) {
return a > b ? b : a;
}
int max(int a, int b) {
return a > b ? a : b;
}
int maxProduct(int *nums, int numsSize) {
int res = nums[0];
// 以i位置结尾的子数组乘积的最大值和最小值
int minDP = nums[0];
int maxDP = nums[0];
// 最大值可能是当前元素,或者是以i-1位置结尾的最小乘积与nums[i]相乘(负数乘负数),或者以i-1位置结尾的最大乘积与nums[i]相乘
// 在递推过程中,记录以i-1结尾的子数组乘积的最值
for (int i = 1, curMin, curMax; i < numsSize; ++i) {
curMin = min(nums[i], min(nums[i] * minDP, nums[i] * maxDP));
curMax = max(nums[i], max(nums[i] * minDP, nums[i] * maxDP));
minDP = curMin;
maxDP = curMax;
res = max(res, maxDP);
}
return res;
}

416. 分割等和子集

class Solution {
public:
bool canPartition(vector<int> &nums) {
int len = nums.size();
if (len < 2) return false;
int sum = accumulate(nums.begin(), nums.end(), 0);
int maxNum = *max_element(nums.begin(), nums.end());
if (sum & 1) return false;
int target = sum / 2;
if (maxNum > target) return false;
vector<vector<int>> dp(len, vector<int>(target + 1, 0));
for (int i = 0; i < len; i++) dp[i][0] = true;
dp[0][nums[0]] = true;
for (int i = 1; i < len; i++) {
int num = nums[i];
for (int j = 1; j <= target; j++) {
if (j >= num) {
dp[i][j] = dp[i - 1][j] | dp[i - 1][j - num];
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[len - 1][target];
}
};

32. 最长有效括号

int longestValidParentheses(char *s) {
int len = strlen(s);
if (len <= 1) return 0;
// dp[i]表示以s[i]结尾的最长有效括号的长度
int dp[len];
for (int i = 0; i < len; ++i) dp[i] = 0;
int max = 0;
for (int i = 1; i < len; ++i) {
// 以'('结尾,无法形成有效括号
if (s[i] == '(') {
dp[i] = 0;
continue;
}
// 以')'结尾
int index = i - 1 - dp[i - 1];
// 以s[i-1]为结尾的最长有效括号的开头的左边一个字符
if (index >= 0) {
char ch = s[index];
if (ch == '(') {
dp[i] = dp[i - 1] + 2;
if (index - 1 >= 0) dp[i] += dp[index - 1];
} else {
dp[i] = 0;
}
} else {
dp[i] = 0;
}
// 记录最大
if (dp[i] > max) max = dp[i];
}
return max;
}

多维动态规划

62. 不同路径

int uniquePaths(int m, int n) {
// 记录到格子(i,j)的路径总数
int dp[m][n];
// 初始条件
// 从(0,0)到第一列的任何一个格子的路径只有一条,就是从上往下
for (int i = 0; i < m; ++i) dp[i][0] = 1;
// 从(0,0)到第一行的任何一个格子的路径只有一条,就是从左往右
for (int i = 0; i < n; ++i) dp[0][i] = 1;
for (int i = 1; i < m; ++i) {
for (int j = 1; j < n; ++j) {
// 状态转移方程
// (i,j)只能由左边的格子或者上面的格子走过来,dp[i][j]就是这两种途径的路径和
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[m - 1][n - 1];
}
// todo 空间优化
// 排列组合

64. 最小路径和

int min(int a, int b) {
return a > b ? b : a;
}
// 自底向上+空间压缩
int minPathSum(int **grid, int gridSize, int *gridColSize) {
int rowSize = gridSize;
int columnSize = *gridColSize;
// 一行一行保存最小路径和
int dp[columnSize];
dp[0] = grid[0][0];
// 第一行只和左边的一个元素有关
for (int i = 1; i < columnSize; ++i)
dp[i] = dp[i - 1] + grid[0][i];
for (int i = 1; i < rowSize; ++i) {
// 每行的首元素只和上一行同位置元素有关
dp[0] += grid[i][0];
// 其他行的非首元素和当前行左边的元素和上一行的同位置元素有关
for (int j = 1; j < columnSize; ++j)
dp[j] = min(dp[j - 1], dp[j]) + grid[i][j];
}
return dp[columnSize - 1];
}

5. 最长回文子串

class Solution {
public:
string longestPalindrome(string s) {
int len = s.length();
// 动态规划表,dp[i][j] 为 true 表示 s[i,j] 是回文串
vector<vector<bool>> dp(len, vector<bool>(len, false));
// 主对角线上的 dp[i][i] 是单个元素,都是回文
for (int i = 0; i < len; ++i) dp[i][i] = true;
// 最长回文子串的起始下标和长度
int start{}, length{1};
// 判断是否是回文的子串的长度
for (int gap = 1; gap < len; ++gap) {
for (int i = 0; i + gap < len; ++i) {
int j = i + gap;
// 内部是否回文
bool inner = gap == 1 || dp[i + 1][j - 1];
// 两端相等且内部也是回文时,才是回文
dp[i][j] = (s[i] == s[j]) && inner;
// 是回文就更新长度
if (dp[i][j]) {
start = i;
length = gap + 1;
}
}
}
return s.substr(start, length);
}
};
class Solution {
public:
int start{}, length{1};
int len{};
// 从中间向两边扩展
void expand(string s, int left, int right) {
while (left >= 0 && right < len) {
// 构不成回文
if (s[left] != s[right]) break;
// 能构成就继续向外扩展
left--;
right++;
}
if (right - left - 1 > length) {
start = left + 1;
length = right - left - 1;
}
}
string longestPalindrome(string s) {
len = s.length();
for (int i = 0; i < len; ++i) {
// 中心只有一个元素
expand(s, i, i);
// 中心有两个元素
expand(s, i, i + 1);
}
return s.substr(start, length);
}
};
// todo: Manacher 算法
class Solution {
public:
int expand(const string &s, int left, int right) {
while (left >= 0 && right < s.size() && s[left] == s[right]) {
--left;
++right;
}
return (right - left - 2) / 2;
}
string longestPalindrome(string s) {
int start = 0, end = -1;
string t = "#";
for (char c: s) {
t += c;
t += '#';
}
t += '#';
s = t;
vector<int> arm_len;
int right = -1, j = -1;
for (int i = 0; i < s.size(); ++i) {
int cur_arm_len;
if (right >= i) {
int i_sym = j * 2 - i;
int min_arm_len = min(arm_len[i_sym], right - i);
cur_arm_len = expand(s, i - min_arm_len, i + min_arm_len);
} else {
cur_arm_len = expand(s, i, i);
}
arm_len.push_back(cur_arm_len);
if (i + cur_arm_len > right) {
j = i;
right = i + cur_arm_len;
}
if (cur_arm_len * 2 + 1 > end - start) {
start = i - cur_arm_len;
end = i + cur_arm_len;
}
}
string ans;
for (int i = start; i <= end; ++i) {
if (s[i] != '#') {
ans += s[i];
}
}
return ans;
}
};

1143. 最长公共子序列

int max(int a, int b) {
return a > b ? a : b;
}
int **dp;
int length1;
int length2;
// 返回前缀长为len1和len2的最长公共子序列长度
int recursive(char *s1, char *s2, int len1, int len2) {
if (len1 == 0 || len2 == 0) return 0;
if (dp[len1][len2] != -1) return dp[len1][len2];
if (s1[len1 - 1] == s2[len2 - 1]) {
dp[len1][len2] = recursive(s1, s2, len1 - 1, len2 - 1) + 1;
} else {
// 优化:省去了recursive(s1, s2, len1 - 1, len2 - 1),因为范围包括在下面的两个范围内了
dp[len1][len2] = max(recursive(s1, s2, len1 - 1, len2), recursive(s1, s2, len1, len2 - 1));
}
return dp[len1][len2];
}
int longestCommonSubsequence(char *text1, char *text2) {
length1 = strlen(text1);
length2 = strlen(text2);
dp = (int **) malloc(sizeof(int *) * (length1 + 1));
for (int i = 0; i <= length1; ++i) {
dp[i] = (int *) malloc(sizeof(int) * (length2 + 1));
memset(dp[i], -1, sizeof(int) * (length2 + 1));
}
return recursive(text1, text2, length1, length2);
}
int max(int a, int b) {
return a > b ? a : b;
}
int longestCommonSubsequence(char *text1, char *text2) {
int length1 = strlen(text1);
int length2 = strlen(text2);
int dp[length1 + 1][length2 + 1];
// 第一行和第一列都是0
for (int i = 0; i <= length1; ++i) dp[i][0] = 0;
for (int i = 0; i <= length2; ++i) dp[0][i] = 0;
// 和左边,上边,左上角的元素有关
for (int len1 = 1; len1 <= length1; ++len1) {
for (int len2 = 1; len2 <= length2; ++len2) {
if (text1[len1 - 1] == text2[len2 - 1])
dp[len1][len2] = dp[len1 - 1][len2 - 1] + 1;
else
dp[len1][len2] = max(dp[len1 - 1][len2], dp[len1][len2 - 1]);
}
}
return dp[length1][length2];
}
int max(int a, int b) {
return a > b ? a : b;
}
int longestCommonSubsequence(char *text1, char *text2) {
char *s1, *s2;
// s2为较短
if (strlen(text1) < strlen(text2)) {
s1 = text2;
s2 = text1;
} else {
s1 = text1;
s2 = text2;
}
int length1 = strlen(s1);
int length2 = strlen(s2);
// 长度较短但滚动次数较多的数组,
int dp[length2 + 1];
// 第0行全0
memset(dp, 0, sizeof(int) * (length2 + 1));
for (int len1 = 1; len1 <= length1; ++len1) {
// 左上角元素
int leftUp = dp[0];
// 修改前暂存当前元素
int temp;
for (int len2 = 1; len2 <= length2; ++len2) {
temp = dp[len2];
if (s1[len1 - 1] == s2[len2 - 1])
// 与左上角有关
dp[len2] = leftUp + 1;
else
// 与左边和上边的最大值有关
dp[len2] = max(dp[len2 - 1], dp[len2]);
leftUp = temp;
}
}
return dp[length2];
}

72. 编辑距离

int min(int a, int b) {
return a > b ? b : a;
}
// 插入、删除、替换一个字符的代价分别为a,b,c
int editDistance(char *s1, char *s2, int a, int b, int c) {
int len1 = strlen(s1);
int len2 = strlen(s2);
// dp[i][j]表示s1[前缀长度i]变成s2[前缀长度j]的最小代价
int dp[len1 + 1][len2 + 1];
// 空字符串到空字符串没有代价
dp[0][0] = 0;
// 空字符串到s2[前缀长度i]需要插入相应字符
for (int i = 1; i <= len2; ++i)
dp[0][i] = i * a;
// s1[前缀长度i]到空字符串需要删除相应字符
for (int i = 1; i <= len1; ++i)
dp[i][0] = i * b;
// 首行首列已经填完,剩下的格子和左侧、上方、左上方的格子有关
for (int i = 1; i <= len1; ++i) {
for (int j = 1; j <= len2; ++j) {
// 1. s1[i-1]参与
// 1.1 s1[i-1]变成s2[j-1]
// 1.1.1 末尾字符相同,即s1[i-1]等于s2[j-1]时,代价等于dp[i-1][j-1](情况p1)
// 1.1.2 末尾字符不相同,代价等于dp[i-1][j-1]加上替换字符的代价(情况p2)
// 1.2 s1[i-1]不变成s2[j-1]
// 1.2.1 s1[0~i-1]变成s2[0~j-2]最后再插入字符s2[j-1],代价等于dp[i][j-1]加上插入字符的代价(情况p3)
// 2. s1[i-1]不参与
// 2.1 即删除s1[i-1],让s1[0~i-2]变成s2[0~j-1],代价等于dp[i-1][j]加上删除字符的代价(情况p4)
int p1 = 0x7fffffff;
if (s1[i - 1] == s2[j - 1])
p1 = dp[i - 1][j - 1];
int p2 = 0x7fffffff;
if (s1[i - 1] != s2[j - 1])
p2 = dp[i - 1][j - 1] + c;
int p3 = dp[i][j - 1] + a;
int p4 = dp[i - 1][j] + b;
dp[i][j] = min(min(p1, p2), min(p3, p4));
}
}
return dp[len1][len2];
}
int minDistance(char *word1, char *word2) {
return editDistance(word1, word2, 1, 1, 1);
}
int min(int a, int b) {
return a > b ? b : a;
}
// 插入、删除、替换一个字符的代价分别为a,b,c
int editDistance(char *s1, char *s2, int a, int b, int c) {
int len1 = strlen(s1);
int len2 = strlen(s2);
// dp[i][j]表示s1[前缀长度i]变成s2[前缀长度j]的最小代价
int dp[len1 + 1][len2 + 1];
// 空字符串到空字符串没有代价
dp[0][0] = 0;
// 空字符串到s2[前缀长度i]需要插入相应字符
for (int i = 1; i <= len2; ++i)
dp[0][i] = i * a;
// s1[前缀长度i]到空字符串需要删除相应字符
for (int i = 1; i <= len1; ++i)
dp[i][0] = i * b;
// 首行首列已经填完,剩下的格子和左侧、上方、左上方的格子有关
for (int i = 1; i <= len1; ++i) {
for (int j = 1; j <= len2; ++j) {
// 根据末尾字符是否相同划分
if (s1[i - 1] == s2[j - 1]) {
// 相等时,代价就是之前dp[i - 1][j - 1]的代价
dp[i][j] = dp[i - 1][j - 1];
} else {
// 不等时,分为插入删除和替换三种可能
dp[i][j] = min(min(dp[i - 1][j - 1] + c, dp[i][j - 1] + a), dp[i - 1][j] + b);
}
}
}
return dp[len1][len2];
}
// 易理解版
int minDistance(char *word1, char *word2) {
return editDistance(word1, word2, 1, 1, 1);
}
int min(int a, int b) {
return a > b ? b : a;
}
// 插入、删除、替换一个字符的代价分别为a,b,c
int editDistance(char *s1, char *s2, int a, int b, int c) {
int len1 = strlen(s1);
int len2 = strlen(s2);
int dp[len2 + 1];
// 空字符串到空字符串没有代价
dp[0] = 0;
// 第一行除了首元素外,其他位置表示从空字符串到s2[前缀长度i]需要插入相应字符的总代价
for (int i = 1; i <= len2; ++i)
dp[i] = i * a;
int leftUp;
int backup;
for (int i = 1; i <= len1; ++i) {
leftUp = (i - 1) * b;
// s1[前缀长度i]到空字符串需要删除相应字符
dp[0] = i * b;
for (int j = 1; j <= len2; ++j) {
backup = dp[j];
// 根据末尾字符是否相同划分
if (s1[i - 1] == s2[j - 1]) {
// 相等时,代价就是之前dp[i - 1][j - 1]的代价
dp[j] = leftUp;
} else {
// 不等时,分为插入删除和替换三种可能
dp[j] = min(min(leftUp + c, dp[j - 1] + a), dp[j] + b);
}
leftUp = backup;
}
}
return dp[len2];
}
// 空间压缩,类似于最长公共子序列的空间压缩
int minDistance(char *word1, char *word2) {
return editDistance(word1, word2, 1, 1, 1);
}

技巧

136. 只出现一次的数字

int singleNumber(int *nums, int numsSize) {
int XOR = nums[0];
for (int i = 1; i < numsSize; ++i) {
XOR ^= nums[i];
}
return XOR;
}

169. 多数元素

// todo Boyer-Moore 投票算法
// Boyer-Moore 算法的正确性较难证明
int majorityElement(int *nums, int numsSize) {
int candidate = nums[0];
int count = 0;
for (int i = 0; i < numsSize; ++i) {
// 如果投的人是候选人,则计数加一
// 然后检查下一张选票
if (nums[i] == candidate) {
count++;
continue;
}
// 如果投的人不是候选人,并且计数器为0
// 则这个人就是新的候选人,计数加一
if (count == 0) {
candidate = nums[i];
count++;
continue;
}
// 如果投的人不是候选人,并且计数器不为0
// 说明这个人和候选人不同,则进行抵消(两两相消)
count--;
}
return candidate;
}
// todo 分治
// todo 随机化

75. 颜色分类

void swap(int *nums, int left, int right) {
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
}
void sortColors(int *nums, int numsSize) {
int left = 0, right = numsSize - 1;
while (left <= right) {
while (left <= right && nums[left] == 0) left++;
while (left <= right && nums[right] == 2) right--;
if (left > right) break;
// left不是0且right也不是2
if (nums[left] == 1) {
int index = left + 1;
// 找到下一个不是1的位置
while (index <= right && nums[index] == 1) index++;
if (index > right) break;
// 和left或者right交换一次,然后进入下次循环
if (nums[index] == 0) {
swap(nums, left, index);
left++;
} else if (nums[index] == 2) {
swap(nums, right, index);
right--;
}
} else if (nums[left] == 2) {
swap(nums, left, right);
right--;
}
}
}
void swap(int *nums, int left, int right) {
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
}
void sortColors(int *nums, int numsSize) {
if (numsSize == 1) return;
// 下个0应该放的位置
int left = 0;
// 下个2应该放到位置
int right = numsSize - 1;
int index = 0;
while (index <= right) {
if (nums[index] == 0) {
// 所有的0移到左边
swap(nums, left, index);
left++;
// index至少时从left开始的,left左边全都是0
if (left > index) index = left;
} else if (nums[index] == 2) {
// 所有的2移到右边
swap(nums, right, index);
right--;
} else if (nums[index] == 1) {
// 当前位置时1才会后移index,否则就把当前位置的0或者2放到两端
index++;
}
}
}
void swap(int *nums, int left, int right) {
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
}
void sortColors(int *nums, int numsSize) {
int left = 0;
int right = numsSize - 1;
int index = 0;
while (index <= right) {
// 是2就全移到右边
while (index <= right && nums[index] == 2) {
swap(nums, index, right);
right--;
}
// 是0就移到左边
if (nums[index] == 0) {
swap(nums, index, left);
left++;
}
index++;
}
}

31. 下一个排列

void swap(int *nums, int left, int right) {
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
}
void reverseArray(int *nums, int left, int right) {
while (left < right)
swap(nums, left++, right--);
}
// todo
// 需要用一个左边的较小数和一个右边的较大数交换,才能得到后面的一个排列
// 同时,较小数要尽量靠右,较大数尽量小,尽量减小变大的幅度
// 交换后,还要将换位后的较大的数右边重新升序排列,尽量减小变大的幅度
void nextPermutation(int *nums, int numsSize) {
int left = numsSize - 2;
// 从右往左找较小数,左右边的一个升序对的两个元素中,左边的就是较小数
while (left >= 0 && nums[left] >= nums[left + 1])
left--;
// 能找到这个较小数时
if (left >= 0) {
int right = numsSize - 1;
// 从右往左找较大数,找第一个大于较小数的数
while (right >= 0 && nums[left] >= nums[right])
right--;
swap(nums, left, right);
}
// 1.交换较小数和较大数后,区间[left+1, numsSize-1]必为降序
// 2.找不到较小数较大数时,说明已经时降序序列,直接改成最小的升序序列
reverseArray(nums, left + 1, numsSize - 1);
}

287. 寻找重复数

// 类似环形链表找入口节点,nums数组类似静态链表
int findDuplicate(int *nums, int numsSize) {
int slow = 0, fast = 0;
slow = nums[slow];
fast = nums[nums[fast]];
while (slow != fast) {
// slow = slow.next
slow = nums[slow];
// fast = fast.next.next
fast = nums[nums[fast]];
}
fast = 0;
while (slow != fast) {
slow = nums[slow];
fast = nums[fast];
}
return slow;
}

本文作者:n1ce2cv

本文链接:https://www.cnblogs.com/sprinining/p/17975946

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   n1ce2cv  阅读(6)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起