下面只给出答案,具体题目请参考:LeetCode专题 ——《程序员面试金典》
// 要求不使用额外的数据结构
// 排序 + 双指针
class Solution {
public:
bool isUnique(string astr) {
int length = astr.size();
if(length <= 1)
return true;
else if(length == 2)
return astr[0] != astr[1];
sort(astr.begin(), astr.end(), cmp);
int left = 0, right = 1;
while(right < length){
if(astr[left] == astr[right])
return false;
left++, right++;
}
return true;
}
static bool cmp(char a, char b){
return a < b;
}
};
// 位运算
class Solution {
public:
bool isUnique(string astr) {
int length = astr.size();
unsigned int checker = 0;
for(int i = 0; i < length; i++){
int val = astr[i] - 'a'; // 把a-z的字符, 转换成0-25的数字
if((checker & (1 << val)) != 0) // 当前字符已经出现过
return false;
checker |= (1 << val); // 若当前字符没出现过,则把checker的二进制表示的对应位置1
}
return true;
}
};
// 简单哈希(计数类)
class Solution {
public:
bool CheckPermutation(string s1, string s2) {
if (s1.size() != s2.size())
return false;
vector<int> umap(256, 0); // 用数组实现简单的哈希表(用unordered_map也行)
for (int i = 0; i < s1.size(); ++i) {
++umap[s1[i]];
--umap[s2[i]];
}
for (int i = 0; i < umap.size(); ++i) {
if (umap[i] != 0)
return false;
}
return true;
}
};
// 从后往前
class Solution {
public:
string replaceSpaces(string str, int length) {
if(length <= 0)
return "";
int numOfBlank = 0;
for(int i = 0; i < length; i++){
if(str[i] == ' ')
numOfBlank++;
}
int newLength = length + 2 * numOfBlank;
int indexOfOriginalStr = length - 1;
int indexOfNewStr = newLength - 1;
while(indexOfOriginalStr >=0 && indexOfNewStr >= 0){
if(str[indexOfOriginalStr] == ' '){
str[indexOfNewStr--] = '0';
str[indexOfNewStr--] = '2';
str[indexOfNewStr--] = '%';
indexOfOriginalStr--;
}else{
str[indexOfNewStr--] = str[indexOfOriginalStr--];
}
}
return str.substr(0, newLength);
}
};
// 哈希表统计每个字符出现次数
// 如果是回文串的排列,则字符出现次数为奇数的个数不能大于1。
class Solution {
public:
bool canPermutePalindrome(string s) {
vector<int> umap(256, 0);
for (auto c : s) {
++umap[c];
}
int cnt = 0;
for (int i = 0; i < umap.size(); ++i) {
if (umap[i] % 2 == 1) ++cnt;
}
return cnt <= 1;
}
};
// 编辑距离 dp
class Solution {
public:
bool oneEditAway(string word1, string word2) {
int len1 = word1.size();
int len2 = word2.size();
int dp[len1 + 1][len2 + 1];
// base case 是 i 走完 s1 或 j 走完 s2,可以直接返回另一个字符串剩下的长度。
for(int j = 1; j <= len2; j++)
dp[0][j] = j;
for(int i = 1; i <= len1; i++)
dp[i][0] = i;
dp[0][0] = 0; // 超级关键,dp[0][0] = 0 没有初始化会导致错误。
// 自底向上求解
for(int i = 1; i <= len1; i++){
for(int j = 1; j <= len2; j++){
if(word1[i-1] == word2[j-1])
dp[i][j] = dp[i-1][j-1];
else
dp[i][j] = mMin(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1; // 别忘记 +1
}
}
// 储存着整个 s1 和 s2 的最小编辑距离
return dp[len1][len2] <= 1;
}
int mMin(int a, int b, int c){
return min(a, min(b, c));
}
};
// 应该算双指针(快慢指针)算法思想吧
class Solution {
public:
string compressString(string str) {
string res = "";
int length = str.size();
int i = 0, j = 0; // 精妙...
while(j < length - 1){
if(str[j] != str[j+1]){
res += str[i];
res += to_string(j - i + 1);
i = j + 1;
}
j++;
}
// 最后余下的部分也要记得处理
res += str[i];
res += to_string(j - i + 1);
return res.size() < length? res: str;
}
};
// 先转置,再逐行旋转
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int n = matrix.size();
for(int i = 0; i < n; i++){
for(int j = i+1; j < n; j++)
swap(matrix[i][j], matrix[j][i]);
}
for(int i = 0; i < n; i++){
for(int j = 0; j < (n >> 1); j++)
swap(matrix[i][j], matrix[i][n-1-j]);
}
}
};
// 原位标记(用矩阵自己的第一行和第一列做标记),不使用额外空间
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int rows = matrix.size();
if(rows == 0)
return;
int cols = matrix[0].size();
if(cols == 0)
return;
bool row_flag = false;
bool col_flag = false;
for(int j = 0; j < cols; j++){
if(matrix[0][j] == 0){
row_flag = true;
break;
}
}
for(int i = 0; i < rows; i++){
if(matrix[i][0] == 0){
col_flag = true;
break;
}
}
for(int i = 1; i < rows; i++){
for(int j = 1; j < cols; j++){
if(matrix[i][j] == 0){
matrix[0][j] = matrix[i][0] = 0;
}
}
}
for(int i = 1; i < rows; i++){
for(int j = 1; j < cols; j++){
if(matrix[0][j] == 0 || matrix[i][0] == 0)
matrix[i][j] = 0;
}
}
if(row_flag){
for(int j = 0; j < cols; j++)
matrix[0][j] = 0;
}
if(col_flag){
for(int i = 0; i < rows; i++)
matrix[i][0] = 0;
}
}
};
// 如果s2是s1的子串,那么s2必然在s1+s1里面
class Solution {
public:
bool isFlipedString(string s1, string s2) {
string temp = s1 + s1;
return s1.size() == s2.size() && temp.find(s2) != string::npos; // 这里不能用 >= 0
}
};
// 无序链表去除重复值,只能用哈希表来记录重复值了
class Solution {
public:
ListNode* removeDuplicateNodes(ListNode* head) {
if(head == nullptr)
return nullptr;
ListNode* dummyHead = new ListNode(-1);
ListNode* pPre = dummyHead;
ListNode* pNode = head;
unordered_set<int> uset;
while(pNode != nullptr){
if(uset.count(pNode->val) > 0){
pNode = pNode->next;
continue;
}
uset.insert(pNode->val);
ListNode* pNext = pNode->next;
pPre->next = pNode;
pPre = pNode;
pNode = pNext;
}
pPre->next = nullptr;
return dummyHead->next;
}
};
// 快慢指针(快指针先走(k-1)步)
class Solution {
public:
int kthToLast(ListNode* pListHead, unsigned int k) {
if(pListHead == nullptr || k <= 0) // k <= 0 不要忘记加
return -1;
int length = 0;
ListNode* pNode = pListHead;
while(pNode != nullptr){
length++;
pNode = pNode->next;
}
if(k > length)
return -1;
ListNode* pQuickNode = pListHead;
ListNode* pSlowNode = pListHead;
for(int i = 1; i <= k-1; i++){
pQuickNode = pQuickNode->next;
}
while(pQuickNode->next != nullptr){
pSlowNode = pSlowNode->next;
pQuickNode = pQuickNode->next;
}
return pSlowNode->val;
}
};
// 删除链表节点(交换元素法)
class Solution {
public:
void deleteNode(ListNode* pNode) {
pNode->val = pNode->next->val;
ListNode* pToBeDeleted = pNode->next;
pNode->next = pNode->next->next;
delete pToBeDeleted;
pToBeDeleted = nullptr;
}
};
// 按题意分割成两个链表之后再接起来
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
ListNode* dummyHead_1 = new ListNode(-1);
ListNode* dummyHead_2 = new ListNode(-1);
ListNode* p1 = dummyHead_1, *p2 = dummyHead_2;
while(head != nullptr){
if(head->val < x){
p1->next = head;
p1 = p1->next;
}else{
p2->next = head;
p2 = p2->next;
}
head = head->next;
}
p1->next = dummyHead_2->next;
p2->next = nullptr; // 重要,别忘记
return dummyHead_1->next;
}
};
// 模拟加法
class Solution {
public:
ListNode* addTwoNumbers(ListNode* pHead1, ListNode* pHead2) {
ListNode* dummyHead = new ListNode(-1);
ListNode* pCur = dummyHead;
int carry = 0;
while(pHead1 != nullptr || pHead2 != nullptr){
int x1 = pHead1 == nullptr? 0: pHead1->val;
int x2 = pHead2 == nullptr? 0: pHead2->val;
int sum = x1 + x2 + carry;
carry = sum / 10;
sum %= 10;
pCur->next = new ListNode(sum);
pCur = pCur->next;
if(pHead1 != nullptr)
pHead1 = pHead1->next;
if(pHead2 != nullptr)
pHead2 = pHead2->next;
}
if(carry == 1)
pCur->next = new ListNode(carry);
return dummyHead->next;
}
};
// 进阶题:假设这些数位是正向存放的,请再做一遍。(leetcode 445)
// 双栈法
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
vector<ListNode*> l1v, l2v;
to_vector(l1, l1v); // 此题用vector模拟栈,更方便
to_vector(l2, l2v);
int i = l1v.size()-1, j = l2v.size()-1;
int tempNum = 0;
ListNode *dummyHead = new ListNode(-1);
dummyHead->next = nullptr;
while(i >= 0 || j >= 0){
if(i >= 0) tempNum += l1v[i--]->val;
if(j >= 0) tempNum += l2v[j--]->val;
ListNode* tempNode = new ListNode(tempNum % 10); // 头插法
tempNode->next = dummyHead->next;
dummyHead->next = tempNode;
tempNum /= 10;
}
if(tempNum != 0) { // 最后最高位是否有进位
ListNode* tempNode = new ListNode(tempNum);
tempNode->next = dummyHead->next;
dummyHead->next = tempNode;
}
return dummyHead->next;
}
void to_vector(ListNode* head,vector<ListNode*>& v){
while(head){
v.push_back(head);
head = head->next;
}
}
};
// 快慢指针 + 边遍历边翻转(前半部分)
class Solution {
public:
bool isPalindrome(ListNode* pHead) {
if(pHead == nullptr || pHead->next == nullptr)
return true;
ListNode* pSlow = pHead;
ListNode* pQuick = pHead;
ListNode* pPre = nullptr;
while(pQuick != nullptr && pQuick->next != nullptr){
ListNode* pNext = pSlow->next;
pQuick = pQuick->next->next; //这句一定要在前面,不然可能会出现引用空指针的情况
pSlow->next = pPre;
pPre = pSlow;
pSlow = pNext;
}
// 一共有奇数个节点
if(pQuick != nullptr)
pSlow = pSlow->next;
// 此时分别以pPre和pSlow为两个子链表的头,遍历链表看是否相等。
while(pPre != nullptr && pSlow != nullptr){
if(pPre->val != pSlow->val)
return false;
pPre = pPre->next;
pSlow = pSlow->next;
}
return true;
}
};
// 快慢指针(《剑指offer》上的解法)
class Solution {
public:
ListNode* getIntersectionNode(ListNode* pHead1, ListNode* pHead2) {
if(pHead1 == nullptr || pHead2 == nullptr)
return nullptr;
ListNode* pFirstCommonNode = nullptr; // 默认值(重要!)
ListNode* pNodeInLongList = pHead1;
int lengthOfLongList = 0;
while(pNodeInLongList != nullptr){
lengthOfLongList++;
pNodeInLongList = pNodeInLongList->next;
}
ListNode* pNodeOfShortList = pHead2;
int lengthOfShortList = 0;
while(pNodeOfShortList != nullptr){
lengthOfShortList++;
pNodeOfShortList = pNodeOfShortList->next;
}
pNodeInLongList = pHead1;
pNodeOfShortList = pHead2;
if(lengthOfLongList < lengthOfShortList){
swap(lengthOfLongList, lengthOfShortList);
swap(pNodeInLongList, pNodeOfShortList);
}
int diff = lengthOfLongList - lengthOfShortList;
for(int i = 0; i < diff; i++)
pNodeInLongList = pNodeInLongList->next;
while(pNodeInLongList && pNodeOfShortList){
if(pNodeInLongList == pNodeOfShortList){
pFirstCommonNode = pNodeInLongList;
break;
}
pNodeInLongList = pNodeInLongList->next;
pNodeOfShortList = pNodeOfShortList->next;
}
return pFirstCommonNode;
}
};
// 双链表合并解法(推荐)
class Solution {
public:
ListNode* getIntersectionNode(ListNode* pHead1, ListNode* pHead2) {
if(pHead1 == nullptr || pHead2 == nullptr)
return nullptr;
ListNode* pA = pHead1, *pB = pHead2;
while(pA != pB){
pA = pA == nullptr? pHead2: pA->next;
pB = pB == nullptr? pHead1: pB->next;
}
return pA;
}
};
// 三个步骤:求环中某节点(先判断有没有环)、求环的长度、求环的入口节点。
class Solution {
public:
ListNode* detectCycle(ListNode* pHead){
if(pHead == nullptr)
return nullptr;
ListNode* pNodeInLoop = nullptr;
if(!hasLoop(pHead, &pNodeInLoop))
return nullptr;
ListNode* pNode = pNodeInLoop->next;
int numOfLoop = 1;
while(pNode != pNodeInLoop){
numOfLoop++;
pNode = pNode->next;
}
ListNode* pSlowNode = pHead;
ListNode* pQuickNode = pHead;
for(int i = 1; i <= numOfLoop; i++)
pQuickNode = pQuickNode->next;
while(pSlowNode != pQuickNode){
pSlowNode = pSlowNode->next;
pQuickNode = pQuickNode->next;
}
return pQuickNode;
}
bool hasLoop(ListNode* pHead, ListNode** pNodeInLoop){
ListNode* pSlowNode = pHead;
ListNode* pQuickNode = pHead->next;
while(pSlowNode != nullptr && pQuickNode != nullptr){
if(pSlowNode == pQuickNode){
*pNodeInLoop = pSlowNode;
return true;
}
pSlowNode = pSlowNode->next;
pQuickNode = pQuickNode->next;
if(pQuickNode != nullptr)
pQuickNode = pQuickNode->next;
}
return false;
}
};
// 题解:https://leetcode-cn.com/problems/triple-in-one-lcci/solution/java-shu-zu-wei-zhi-fen-pei-by-npe_tle/
// 定义一个数组arr,数组的位置分配规则如下:
// 1.数组的下标为[0, 0 + 3, ... , 0 + 3 * (stackSize - 1)] 存放stack0;
// 2.数组的下标为[1, 1 + 3, ... , 1 + 3 * (stackSize - 1)] 存放stack1;
// 3.数组的下标为[2, 2 + 3, ... , 2 + 3 * (stackSize - 1)] 存放stack2;
// 然后,新建一个数组top,用来标记每个栈的栈顶可插入元素的下标(在arr中的下标)
// 这里是另一个思路:将arr等距地分为3部分,分别存储3个栈。
// 注:top指向栈顶元素的下一个位置
class TripleInOne {
public:
int *arr; // 这里用vector也行
int top[3];
int stackSize;
TripleInOne(int stackSize): stackSize(stackSize) {
arr = new int[stackSize*3];
top[0] = top[1] = top[2] = 0;
}
void push(int stackNum, int value) {
if(top[stackNum] < stackSize){
arr[stackSize*stackNum + top[stackNum]] = value;
top[stackNum]++;
}
}
int pop(int stackNum) {
if(top[stackNum] <= 0){
return -1;
}else{
top[stackNum]--;
return arr[stackSize*stackNum + top[stackNum]];
}
}
int peek(int stackNum) {
if(top[stackNum] <= 0)
return -1;
else
return arr[stackSize*stackNum + (top[stackNum] - 1)];
}
bool isEmpty(int stackNum) {
return top[stackNum] == 0; // 栈满:top[stackNum] == stackSize
}
};
// 借用辅助栈
int g_invalidInput = false;
class MinStack {
public:
/** initialize your data structure here. */
MinStack() {
// nothing
}
void push(int value) {
data.push(value);
if(minInData.empty() || value < minInData.top())
minInData.push(value);
else
minInData.push(minInData.top());
}
void pop() {
if(!data.empty() && !minInData.empty()){
data.pop();
minInData.pop();
}
}
int top() {
if(!data.empty())
return data.top();
g_invalidInput = true;
return -1;
}
int getMin() {
if(!minInData.empty())
return minInData.top();
g_invalidInput = true;
return -1;
}
private:
stack<int> data;
stack<int> minInData;
};
// 主要思路是使用vector的可变特性建立栈(不难,注意细节即可)
class StackOfPlates {
private:
int capacity;
vector<stack<int> > stack_sets;
public:
StackOfPlates(int cap) {
capacity = cap;
}
void push(int val) {
if(capacity == 0) // capacity == 0的时候禁止入栈!!!
return;
if(stack_sets.size() == 0 || stack_sets[stack_sets.size()-1].size() == capacity){
stack<int> tmp;
tmp.push(val);
stack_sets.push_back(tmp);
}else{
stack_sets[stack_sets.size()-1].push(val);
}
}
int pop() {
if(stack_sets.size() == 0)
return -1;
int ret = stack_sets[stack_sets.size()-1].top();
stack_sets[stack_sets.size()-1].pop();
if(stack_sets[stack_sets.size()-1].empty()){
stack_sets.pop_back();
}
return ret;
}
int popAt(int index) {
if(stack_sets.size() == 0|| index < 0 ||index >= stack_sets.size())
return -1;
int ret = stack_sets[index].top();
stack_sets[index].pop();
if(stack_sets[index].empty()){
stack_sets.erase(stack_sets.begin() + index);
}
return ret;
}
};
// 该题解有一些异常输入没处理,可用“全局变量”的方式处理一下就好
class MyQueue{
public:
/** Initialize your data structure here. */
MyQueue() {
// nothing
}
/** Push element x to the back of queue. */
void push(int x){
stack1.push(x);
}
/** Removes the element from in front of queue and returns that element. */
int pop(){
if(stack2.empty()){
while(!stack1.empty()){
stack2.push(stack1.top());
stack1.pop();
}
}
if(!stack2.empty()){
int res = stack2.top();
stack2.pop();
return res;
}
return -1;
}
/** Get the front element. */
int peek() {
if(stack2.empty()){
while(!stack1.empty()){
stack2.push(stack1.top());
stack1.pop();
}
}
if(!stack2.empty())
return stack2.top();
return -1;
}
/** Returns whether the queue is empty. */
bool empty() {
if(stack1.empty() && stack2.empty())
return true;
else
return false;
}
private:
stack<int> stack1; // 进队列
stack<int> stack2; // 出队列
};
// 本题与“面试题59 - II. 队列的最大值”的解法类似,用辅助栈
// 注意本题与“面试题 03.02. 栈的最小值”的解法不同
class SortedStack {
public:
stack<int> data, aux;
SortedStack() {
// nothing
}
void push(int val) {
while(!data.empty() && data.top() < val){
aux.push(data.top());
data.pop();
}
data.push(val);
while(!aux.empty()){
data.push(aux.top());
aux.pop();
}
}
void pop() {
if(!data.empty())
data.pop();
}
int peek() {
if(!data.empty())
return data.top();
return -1; // 栈为空时peek()返回-1,题目没说清楚
}
bool isEmpty() {
return data.empty();
}
};
// 复制的别人的代码,感觉不太可能考,暂时可以略过
class AnimalShelf {
public:
queue<pair<int, int>> catQueue;
queue<int>queueCat;
queue<pair<int, int>> dogQueue;
queue<int>queueDog;
int index = 0;
AnimalShelf() {
// nothing
}
void enqueue(vector<int> animal) {
if (animal[1] == 0){
catQueue.push(make_pair(animal[0], animal[1]));
index++;
queueCat.push(index);
}
else{
dogQueue.push(make_pair(animal[0], animal[1]));
index++;
queueDog.push(index);
}
}
vector<int> dequeueAny() {
if (!dogQueue.empty() && catQueue.empty()){
pair<int, int> temp = dogQueue.front();
dogQueue.pop();
queueDog.pop();
return {temp.first, temp.second};
}
if (!catQueue.empty() && dogQueue.empty()){
pair<int, int> temp = catQueue.front();
catQueue.pop();
queueCat.pop();
return {temp.first, temp.second};
}
if (!catQueue.empty() && !dogQueue.empty()){
if (queueDog.front() < queueCat.front()){
pair<int, int> temp = dogQueue.front();
dogQueue.pop();
queueDog.pop();
return {temp.first, temp.second};
}
else if (queueDog.front() > queueCat.front()){
pair<int, int> temp = catQueue.front();
catQueue.pop();
queueCat.pop();
return {temp.first, temp.second};
}
}
return {-1, -1};
}
vector<int> dequeueDog() {
if (!dogQueue.empty()){
pair<int, int> temp = dogQueue.front();
dogQueue.pop();
queueDog.pop();
return {temp.first, temp.second};
}
return {-1, -1};
}
vector<int> dequeueCat() {
if (!catQueue.empty()){
pair<int, int> temp = catQueue.front();
catQueue.pop();
queueCat.pop();
return {temp.first, temp.second};
}
return {-1, -1};
}
};
// dfs
// 需要注意的就是要把图由边表示改为邻接表表示。
class Solution {
public:
bool findWhetherExistsPath(int n, vector<vector<int> >& graph, int start, int target) {
vector<vector<int> > vec(n);
vector<bool> visited(n, 0); // 解决自环和平行边的问题
for(auto g: graph){
vec[g[0]].push_back(g[1]);
}
return dfs(vec, visited, start, target);
}
bool dfs(vector<vector<int> > &vec, vector<bool> visited, int start, int target){
if(start == target)
return true;
visited[start] = true;
for(auto v: vec[start]){
if(!visited[v]){
if(dfs(vec, visited, v, target))
return true;
}
}
return false;
}
};
// bfs
class Solution {
public:
bool findWhetherExistsPath(int n, vector<vector<int>>& graph, int start, int target) {
vector<bool> vis(n); // 解决自环和平行边的问题
vector<set<int>> adj(n);
for (auto e : graph)
adj[e[0]].insert(e[1]);
queue<int> q;
q.push(start);
vis[start] = true;
while (!q.empty()) {
int u = q.front();
q.pop();
if (u == target)
return true;
for (int v : adj[u])
if (!vis[v]) {
vis[v] = true;
q.push(v);
}
}
return false;
}
};
// 将有序数组转换为二叉搜索树(递归二分建树)
class Solution {
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
int length = nums.size();
if(length == 0)
return nullptr;
TreeNode* pHead = nullptr;
pHead = buildTree(nums, 0, length - 1);
return pHead;
}
TreeNode* buildTree(vector<int>& nums, int left, int right){
if(left > right) //不要忘记这句
return nullptr;
int mid = (left + right) >> 1;
TreeNode* pNode = new TreeNode(nums[mid]);
pNode->left = buildTree(nums, left, mid - 1);
pNode->right = buildTree(nums, mid + 1, right);
return pNode;
}
};
// 广度优先搜索
class Solution {
public:
vector<ListNode*> listOfDepth(TreeNode* tree) {
vector<ListNode*> res;
if(tree == nullptr)
return res;
queue<TreeNode*> q;
q.push(tree);
while(!q.empty()){
int num = q.size();
ListNode* dummyHead = new ListNode(-1);
ListNode* pNode = dummyHead;
while(num--){
TreeNode* temp = q.front();
q.pop();
ListNode* pCurrNode = new ListNode(temp->val);
pNode->next = pCurrNode;
pNode = pNode->next;
if(temp->left != nullptr)
q.push(temp->left);
if(temp->right != nullptr)
q.push(temp->right);
}
res.push_back(dummyHead->next);
}
return res;
}
};
// 基于“前序遍历”的方式,会有很多重复遍历。
class Solution {
public:
bool isBalanced(TreeNode* pRoot) {
if(pRoot == nullptr)
return true;
int left = TreeDepth(pRoot->left);
int right = TreeDepth(pRoot->right);
int diff = left - right;
if(diff > 1 || diff < -1)
return false;
return isBalanced(pRoot->left) && isBalanced(pRoot->right);
}
int TreeDepth(TreeNode* pRoot){
if(pRoot == nullptr)
return 0;
int left = TreeDepth(pRoot->left);
int right = TreeDepth(pRoot->right);
return max(left, right) + 1;
}
};
// 基于后序遍历的方式(推荐)
// 后续遍历时,每遍历到一个节点,其左右子树已经遍历,依次自底向上判断,每个节点只需要遍历一次。
class Solution {
public:
bool isBalanced(TreeNode* root) {
res = true;
getDepth(root);
return res;
}
int getDepth(TreeNode* root){
if(root == nullptr)
return 0;
int left = getDepth(root->left);
int right = getDepth(root->right);
if(abs(left - right) > 1)
res = false;
return right > left? right+1: left+1;
}
private:
bool res;
};
// 利用“上界”与“下界”
class Solution {
public:
bool isValidBST(TreeNode* root) {
if(root == nullptr)
return true;
return verifyRecursively(root, LONG_MIN, LONG_MAX);
}
bool verifyRecursively(TreeNode* pNode, long left_min, long right_max){
if(pNode == nullptr)
return true;
if(pNode->val <= left_min || pNode->val >= right_max)
return false;
return verifyRecursively(pNode->left, left_min, pNode->val) &&
verifyRecursively(pNode->right, pNode->val, right_max);
}
};
// 利用“中序遍历”的解法
class Solution {
public:
TreeNode* pre;
bool isValidBST(TreeNode* root) {
//方法一:非递归
stack<TreeNode*> s;
TreeNode* cur = root;
while(!s.empty() || cur){
if(cur){
s.push(cur);
cur = cur->left;
}else{
cur = s.top();
s.pop();
if(pre && pre->val >= cur->val) return false;
pre = cur;
cur = cur->right;
}
}
return true;
//方法二:递归
if(root == NULL)
return true;
if(!isValidBST(root->left))
return false;
if(pre && pre->val >= root->val)
return false;
pre = root;
if(!isValidBST(root->right))
return false;
return true;
}
};
// 中序遍历,设置pPrev指针
class Solution {
TreeNode *pPrev = nullptr;
TreeNode *pNext = nullptr;
public:
TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
if(!root || !p) return nullptr;
inorder(root, p);
return pNext;
}
void inorder(TreeNode* root, TreeNode *p){
if(!root) return;
inorder(root->left, p);
if(pPrev == p)
pNext = root;
pPrev = root;
inorder(root->right, p);
}
};
// 进阶:《剑指offer》第8题
// 任意二叉树的中序遍历的下一个节点(TreeLinkNode有指向父节点的指针 next)
class Solution {
public:
TreeLinkNode* inorderSuccessor(TreeLinkNode* pNode){
if(pNode == nullptr)
return nullptr;
TreeLinkNode* pNextNode = nullptr;
if(pNode->right != nullptr){
TreeLinkNode* pRightNode = pNode->right;
while(pRightNode->left != nullptr)
pRightNode = pRightNode->left;
pNextNode = pRightNode;
return pNextNode; // 之前这里没加return,导致错误。
}
TreeLinkNode* pParent = pNode->next;
while(pParent != nullptr){
if(pParent->left == pNode){
pNextNode = pParent;
break;
}else if(pParent->right == pNode){
pNode = pParent;
pParent = pParent->next;
}
}
return pNextNode;
}
};
// 此函数返回的就是p和q的最近公共祖先
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
// 递归终止条件:如果当前节点为空或等于p或q,则返回当前节点
if(root == nullptr || root == p || root == q)
return root;
// 递归遍历左右子树
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
// 如果左右子树查到节点都不为空,则表明p和q分别在左右子树中,
// 因此,当前节点即为最近公共祖先;
if(left != nullptr && right != nullptr)
return root;
// 如果左右子树其中一个不为空,则返回非空节点。
return left == nullptr? right: left;
}
};
// 基于“后序遍历”
// 从根节点开始往下找,每个节点的左右子树互不干扰,只要每个节点在它子树节点前且“子树序列”中各节点的相对顺序不变就行
class Solution {
public:
vector<vector<int>> BSTSequences(TreeNode* root) {
if(root == nullptr)
return {{}};
if(root->left == nullptr && root->right == nullptr)
return {{root->val}};
vector<vector<int> > res;
vector<vector<int> > resl = BSTSequences(root->left);
vector<vector<int> > resr = BSTSequences(root->right);
/*一一组合
在保持相对顺序下合并left与right并添加prefix,如[1,2][4,5] ->
[[0, 1, 2, 4, 5], [0, 1, 4, 2, 5], [0, 1, 4, 5, 2], [0, 4, 1, 2, 5], [0, 4, 1, 5, 2], [0, 4, 5, 1, 2]]
*/
for(auto vl: resl){
vector<int> trace = {root->val};
for(auto vr: resr){
dfs(res, trace, vl, vr, 0, 0);
}
}
return res;
}
// dfs:搜索在两个数组保持相对顺序不变的情况下,有多少种组合
void dfs(vector<vector<int>>& res, vector<int>& trace, vector<int>& left, vector<int>& right, int posl, int posr){
if(posl == left.size()){
vector<int> temp = trace;
auto beg = right.begin() + posr;
temp.insert(temp.end(), beg, right.end());
res.push_back(temp);
return;
}else if(posr == right.size()){
vector<int> temp = trace;
auto beg = left.begin() + posl;
temp.insert(temp.end(), beg, left.end());
res.push_back(temp);
return;
}
// 选择1
trace.push_back(left[posl]);
dfs(res, trace, left, right, posl+1, posr);
trace.pop_back();
// 选择2
trace.push_back(right[posr]);
dfs(res, trace, left, right, posl, posr+1);
trace.pop_back();
}
};
// 更清晰的代码(二叉搜索树不能有重复元素,所以不用考虑去重)
class Solution {
public:
vector<vector<int>> BSTSequences(TreeNode* root) {
vector<vector<int> > res;
if (!root)
return {{}};
vector<vector<int> > lefts = BSTSequences(root->left);
vector<vector<int> > rights = BSTSequences(root->right);
vector<int> cur = {root->val};
for (auto left: lefts) {
for (auto right: rights) {
dfs(left, 0, right, 0, res, cur); // 该函数可以获取由left和right所组成的所有子数组。
}
}
return res;
}
// 用dfs做插入,保持相对顺序
void dfs(vector<int> &left, int l, vector<int>& right, int r, vector<vector<int> > &res, vector<int>& cur) {
if (l == left.size() && r == right.size()) {
res.push_back(cur);
}
//两种选择,要么l+1,要么r+1;
if (l < left.size()) {
cur.push_back(left[l]);
dfs(left, l + 1, right, r, res, cur);
cur.pop_back();
}
if (r < right.size()) {
cur.push_back(right[r]);
dfs(left, l, right, r + 1, res, cur);
cur.pop_back();
}
}
};
// 递归遍历树(与《剑指offer》第26题稍有不同)
class Solution {
public:
bool checkSubTree(TreeNode* pRoot1, TreeNode* pRoot2)
{
if(pRoot1 == nullptr || pRoot2 == nullptr)
return false;
bool res = false; // 默认值(重要)
if(pRoot1->val == pRoot2->val)
res = hasSubTreeRecusively(pRoot1, pRoot2);
if(!res)
res = checkSubTree(pRoot1->left, pRoot2);
if(!res)
res = checkSubTree(pRoot1->right, pRoot2);
return res;
}
bool hasSubTreeRecusively(TreeNode* pRoot1, TreeNode* pRoot2){
if(pRoot1 == nullptr && pRoot2 == nullptr)
return true;
if(pRoot1 == nullptr || pRoot2 == nullptr)
return false;
if(pRoot1->val != pRoot2->val)
return false;
return hasSubTreeRecusively(pRoot1->left, pRoot2->left) &&
hasSubTreeRecusively(pRoot1->right, pRoot2->right);
}
};
// leetcode 437。利用“前缀和”的思路可大大减少重复遍历
class Solution {
public:
int pathSum(TreeNode* root, int sum) {
if(root== nullptr)
return 0;
unordered_map<int, int> umap;
umap[0] = 1; // 必加
return helper(root, sum, umap, 0);
}
int helper(TreeNode* root, int sum, unordered_map<int, int> &umap, int pathSum){
if(root == nullptr)
return 0;
int res = 0;
pathSum += root->val;
// umap[pathSum]++; // 写到这里不对,必须写在下面
if(umap.count(pathSum - sum) > 0)
res += umap[pathSum - sum];
umap[pathSum]++;
res += helper(root->left, sum, umap, pathSum) + helper(root->right, sum, umap, pathSum);
// 回溯
umap[pathSum]--;
pathSum -= root->val; // // 值传递可以不写,但是最好写上,体现回溯
return res;
}
};
// 位运算
class Solution {
public:
int insertBits(int N, int M, int i, int j) {
// 计算掩码
uint32_t mask1 = UINT32_MAX << i;
uint32_t mask2 = UINT32_MAX >> (32 - j - 1); // 逻辑右移,左边补0 (uint32_t == unsigned int)
uint32_t mask = mask1 & mask2;
// 保留 N 中 [i, j] 以外的位,保留 M [i, j] 之间的位
return (N & (~mask)) + ((M << i) & mask);
}
};
// 位运算,不太懂,可暂时跳过
class Solution {
public:
string printBin(double num) {
string res = "0.";
while(num != 0){
if(res.size() == 32)
return "ERROR";
num *= 2;
if(num >= 1){
num -= 1;
res += "1";
} else {
res += "0";
}
}
return res;
}
};
// 位运算:把二进制字符按0切割,取相邻长度和的最大值
class Solution {
public:
int reverseBits(int num) {
int temp = num, len = 0, res = 0;
vector<int> vec;
while(temp){ // 计算num的二进制表示的“有效位数”
temp /= 2;
len++;
}
temp = num;
for(int i = 0; i <= len; i++){ // 记录所有0的位置,包括最高位的上一位
if(temp % 2 == 0)
vec.push_back(i);
temp /= 2;
}
for(auto digit: vec){
int left = digit + 1, right = digit - 1;
int tempRes = 1; // 这里tempRes要初始化为1
while(left <= len && (num & (1<<left)) != 0){
tempRes++;
left++;
}
while(right >= 0 && (num & (1<<right)) != 0){
right--;
tempRes++;
}
res = max(res, tempRes);
}
return res;
}
};
// 暴力解法即可
class Solution {
public:
vector<int> findClosedNumbers(int num) {
int cntOf1 = count1(num);
vector<int> res = {-1, -1}; // 题目中规定:如果找不到前一个或者后一个满足条件的正数,那么输出 -1
for (int i = num + 1; i <= 2147483647; i++) {
if (count1(i) == cntOf1) {
res[0] = i;
break;
}
}
for (int i = num - 1; i >= 0; i--) {
if (count1(i) == cntOf1) {
res[1] = i;
break;
}
}
return res;
}
int count1(int num) {
int cnt = 0;
while(num){
num = (num - 1) & num;
cnt++;
}
return cnt;
}
};
// 位运算
class Solution {
public:
int convertInteger(int A, int B) {
int num = A ^ B;
return count1(num);
}
// // 本题用此方法会有越界错误(因为num可能为INT_MIN)
// int count1(int num) {
// int cnt = 0;
// while(num){
// num = (num - 1) & num;
// cnt++;
// }
// return cnt;
// }
int count1(int num) {
int cnt = 0;
unsigned int bitMask = 1;
for(int i = 0; i < 32; i++){
cnt += (num & bitMask) != 0? 1: 0;
bitMask <<= 1;
}
return cnt;
}
};
/*思路的话就是分别取出奇数位和偶数位,移动后做或运算。
题目规定 num 是int范围的数
0x55555555 = 0b0101_0101_0101_0101_0101_0101_0101_0101
0xaaaaaaaa = 0b1010_1010_1010_1010_1010_1010_1010_1010
用这两个数做与运算,就可以把奇数位和偶数位取出来,
然后位左移奇数位,右移偶数位,
再把 奇数位和偶数位做或运算。*/
class Solution {
public:
int exchangeBits(int num) {
//奇数
int odd = num & 0x55555555;
//偶数
int even = num & 0xaaaaaaaa;
odd = odd << 1;
even = (uint32_t)even >> 1;
return odd | even;
}
};
// 题目描述不清,感觉不会考,可暂时跳过。
class Solution {
public:
vector<int> drawLine(int length, int w, int x1, int x2, int y) {
vector<int> p(length,0);
if (length == 0) return p;
int row = w / 32; //一行有多少int
int start = row * y + x1 / 32; //线段头所在的int
int start_s = x1 % 32; //线段头所在的int的左边需要空出0的个数
int end = row * y + x2 / 32; //线段尾所在的int
int end_s = 31 - x2 % 32; //线段尾所在的int的右边需要空出0的个数
for (int i = start; i <= end; i++) {
p[i] = 0xffffffff; //置为-1;
}
if (start == end) { //对头尾在一个int里时单独处理
p[start] = (unsigned int)p[start] >> start_s+ end_s;
p[end] = (unsigned int)p[end] << end_s;
}
else {
p[start] = (unsigned int)p[start] >> start_s;
p[end] = (unsigned int)p[end] << end_s;
}
return p;
}
};
// 标准动态规划
#define MOD 1000000007
class Solution {
public:
int waysToStep(int n) {
if (n < 1 || n > 1000000)
return 0;
if (n == 1)
return 1;
else if (n == 2)
return 2;
else if (n == 3)
return 4;
long curMinus1 = 4;
long curMinus2 = 2;
long curMinus3 = 1;
int res = 0;
for (int i = 4; i <= n; i++) {
res = (curMinus1 + curMinus2 + curMinus3) % MOD;
curMinus3 = curMinus2 % MOD;
curMinus2 = curMinus1 % MOD;
curMinus1 = res % MOD;
}
return res;
}
};
// dfs(回溯) + 剪枝
class Solution {
public:
vector<vector<int> > pathWithObstacles(vector<vector<int>>& grid) {
found = false;
vector<vector<int> > res;
rows = grid.size();
if(rows == 0)
return res;
cols = grid[0].size();
if(grid[0][0] == 1 || grid[rows-1][cols-1] == 1){
return res;
}
//res.push_back({0, 0}); // 提前把[0, 0]点的特殊情况给处理了
dfs(grid, 0, 0, res);
// 下面这些是根据本题的特殊处理(不重要)
if(res.size() == 1){
if(rows == 1 && cols == 1)
return res;
else
return {};
}
return res;
}
void dfs(vector<vector<int> > &grid, int row, int col, vector<vector<int> > &res){
res.push_back({row, col}); // res.push_back()写到这里就不会超时了
if(found) return;
if(row == rows - 1 && col == cols - 1){
found = true;
return;
}
int dirR[] = {0, 1};
int dirC[] = {1, 0};
for(int i = 0; i < 2; i++){
int new_row = row + dirR[i];
int new_col = col + dirC[i];
if(new_row < 0 || new_row >= rows || new_col < 0 || new_col >= cols || grid[new_row][new_col] == 1)
continue;
// 做选择
//res.push_back({new_row, new_col}); // res.push_back()写到这里超时(不太明白为啥)
dfs(grid, new_row, new_col, res);
// 撤销选择
if(found)
return; // 这里必须为return,不能为continue。(表示若已经找到一条路径后,保存路径,不再pop_back)
else
res.pop_back();
}
}
private:
bool found; // 定义一个全局变量来控制是否找到
int rows, cols;
};
// 如果没有重复数字,可用《剑指offer》第53题的解法
class Solution {
public:
int findMagicIndex(vector<int>& nums) {
int length = nums.size();
if(length == 0)
return -1;
int left = 0, right = length - 1;
while(left < right){
int mid = left + ((right - left) >> 1);
if(nums[mid] < mid) // 写这个判断条件时要慎重!我们得明确我们需要找的是索引最小的满足条件的点
left = mid + 1;
else
right = mid;
}
if(nums[left] == left)
return left;
else
return -1;
}
};
// 本题可能存在重复元素,暂时无法用二分,下面是暴力法
class Solution {
public:
int findMagicIndex(vector<int>& nums) {
for(int i = 0; i < nums.size(); i++){
if(nums[i] == i)
return i;
}
return -1;
}
};
// 回溯1
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int> > res;
int length = nums.size();
if(length == 0){
vector<int> temp; // 空集
res.push_back(temp);
return res;
}
vector<int> temp;
dfs(nums, 0, res, temp);
return res;
}
// 个人偏爱这种写法
void dfs(vector<int>& nums, int start, vector<vector<int> > &res, vector<int> &temp){
if(start == nums.size()){
res.push_back(temp);
return;
}
// 当前的数字 可选,也可以不选(就这两种情况)
// 不选,直接进入下一层
dfs(nums, start + 1, res, temp); // 刚开始这一行没写,导致错误(只写了下面三行)
// 选了,进入下一层
temp.push_back(nums[start]);
dfs(nums, start + 1, res, temp);
temp.pop_back(); // temp传的是引用,所以pop_back()必写。如果是值传递的话,其实可以不写
}
}
// 回溯2
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int> > res;
vector<int> tmp;
length = nums.size();
dfs(nums, res, tmp, 0);
return res;
}
void dfs(vector<int>& nums, vector<vector<int> > &res, vector<int> &tmp, int startNum){
// 没有显式的递归终止
res.push_back(tmp);
// 相当于遍历选择列表
for(int i = startNum; i < length; i++){
tmp.push_back(nums[i]);
dfs(nums, res, tmp, i + 1);
tmp.pop_back();
}
}
private:
int length;
};
// A * B 就是A个B相加(让A < B会减少递归次数)
class Solution {
public:
int multiply(int A, int B) {
if(A > B)
swap(A, B);
if(A == 0)
return 0;
if(A == 1)
return B;
return B + multiply(A - 1, B);
}
};
// 汉诺塔题解:https://www.jianshu.com/p/6e925f3d6078 和 https://leetcode-cn.com/problems/hanota-lcci/solution/tu-jie-yi-nuo-ta-de-gu-shi-ju-shuo-dang-64ge-pan-z/
/* n = 1 时,直接把盘子从 A 移到 C;
n > 1 时,
先把上面 n - 1 个盘子从 A 移到 B(子问题,递归);
再将最大的盘子从 A 移到 C;
再将 B 上 n - 1 个盘子从 B 移到 C(子问题,递归)。
*/
class Solution {
public:
void hanota(vector<int>& A, vector<int>& B, vector<int>& C) {
int n = A.size();
move(n, A, B, C);
}
void move(int n, vector<int>& A, vector<int>& B, vector<int>& C){
if (n == 1){
C.push_back(A.back());
A.pop_back();
return;
}
move(n-1, A, C, B); // 将A上面n-1个通过C移到B
C.push_back(A.back()); // 将A最后一个移到C
A.pop_back(); // 这时,A空了
move(n-1, B, A, C); // 将B上面n-1个通过空的A移到C
}
// // 这样也行
// void move(int n, vector& A, vector& B, vector& C){
// if (n == 1){
// C.push_back(A.back());
// A.pop_back();
// return;
// }
//
// move(n-1, A, C, B); // 将A上面n-1个通过C移到B
// move(1, A, B, C); // 将A上面n-1个通过C移到B
// move(n-1, B, A, C); // 将B上面n-1个通过空的A移到C
// }
};
// 全排列-1
class Solution {
public:
vector<string> permutation(string str) {
int length = str.size();
vector<string> res;
if(length == 0)
return res;
permutationRecursively(str, 0, length, res);
return res;
}
void permutationRecursively(string &str, int mStart, int length, vector<string> &res){
if(mStart == length){
res.push_back(str);
return;
}
for(int i = mStart; i < length; i++){
swap(str[mStart], str[i]);
permutationRecursively(str, mStart + 1, length, res); // 注意这里是“mStart + 1”,不是“i + 1”
swap(str[mStart], str[i]);
}
}
};
// 全排列-2
class Solution {
public:
vector<string> permutation(string str) {
int length = str.size();
vector<string> res;
if(length == 0)
return res;
permutationRecursively(str, 0, length, res);
return res;
}
void permutationRecursively(string &str, int mStart, int length, vector<string> &res){
if(mStart == length){
res.push_back(str);
return;
}
for(int i = mStart; i < length; i++){
// 考虑重复数字
bool duplication = false;
for(int j = mStart; j < i; j++){
if(str[j] == str[i]){
duplication = true;
break;
}
}
if(duplication) continue;
swap(str[mStart], str[i]);
permutationRecursively(str, mStart + 1, length, res); // 注意这里是“mStart + 1”,不是“i + 1”
swap(str[mStart], str[i]);
}
}
};
// 基于“加法”实现
class Solution {
public:
vector<string> generateParenthesis(int n) {
vector<string> res;
string s;
backtrace(0, 0, n, s, res);
return res;
}
void backtrace(int left, int right, int n, string& s, vector<string>& res) {
if(left == n && right == n){
res.push_back(s);
return;
}
if(left < n){
s += "(";
backtrace(left + 1, right, n, s, res);
s.pop_back(); // 若s传的是引用,则pop_back()必须写!
}
if(right < left){ // 剪枝
s += ")";
backtrace(left, right + 1, n, s, res);
s.pop_back();
}
}
};
// 简单dfs
class Solution {
public:
vector<vector<int>> floodFill(vector<vector<int> >& image, int sr, int sc, int newColor) {
rows = image.size();
cols = image[0].size();
dfs(image, sr, sc, image[sr][sc], newColor);
return image;
}
void dfs(vector<vector<int> >& image, int row, int col, int oldColor, int newColor) {
if (row < 0 || row >= rows || col < 0 || col >= cols || image[row][col] != oldColor || image[row][col] == newColor)
return;
// 染色
image[row][col] = newColor;
dfs(image, row - 1, col, oldColor, newColor);
dfs(image, row, col - 1, oldColor, newColor);
dfs(image, row, col + 1, oldColor, newColor);
dfs(image, row + 1, col, oldColor, newColor);
}
private:
int rows, cols;
};
// 状态转移方程:dp[i][j] = dp[i-1][j] + dp[i][j-coins[i]]
class Solution {
public:
int waysToChange(int amount) {
vector<int> coins({1, 5, 10, 25});
int length = coins.size();
vector<vector<long> > dp(length + 1, vector<long>(amount+1, 0));
dp[0][0] = 1;
for(int i = 1; i <= length; i++){
dp[i][0] = 1; // 根据状态转移方程,我们要初始化dp table的第一行和第一列
for(int j = 1; j <= amount; j++){
dp[i][j] = (dp[i-1][j] + ((j - coins[i-1] >= 0)? dp[i][j - coins[i-1]]: 0)) % 1000000007; // 这里最外层的括号要加
}
}
return dp[length][amount];
}
};
// 压缩状态空间到O(n)
class Solution {
public:
int waysToChange(int amount) {
vector<int> coins({1, 5, 10, 25});
int length = coins.size();
vector<long> dp(amount+1, 0);
dp[0] = 1;
for(int i = 1; i <= length; i++){
for(int j = 1; j <= amount; j++){
if(j - coins[i-1] >= 0)
dp[j] = (dp[j] + dp[j - coins[i-1]]) % 1000000007;
}
}
return dp[amount];
}
};
// 经典回溯
class Solution {
public:
vector<vector<string>> solveNQueens(int n) {
vector<string> board(n, string(n, '.'));
backtrack(board, 0, n);
return res;
}
// 路径:board 中小于 row 的那些行都已经成功放置了皇后
void backtrack(vector<string> &board, int row, int n){
if(row == n){ // 触发结束条件
res.push_back(board);
return;
}
// 选择列表:第 row 行的所有列都是放置皇后的选择
for(int col = 0; col < n; col++){
// 排除不合法选择
if(!isValid(board, row, col, n))
continue;
board[row][col] = 'Q'; // 做选择
backtrack(board, row+1, n); // 进入下一行决策
board[row][col] = '.'; // 撤销选择
}
}
/* 是否可以在 board[row][col] 放置皇后? */
bool isValid(vector<string> &board, int row, int col, int n){
// 检查列是否有皇后互相冲突
for(int i = 0; i < n; i++){
if(board[i][col] == 'Q')
return false;
}
// 检查右上方是否有皇后互相冲突
for(int i = row-1, j = col+1; i >= 0 && j < n; i--, j++){
if(board[i][j] == 'Q')
return false;
}
// 检查左上方是否有皇后互相冲突
for(int i = row-1, j = col-1; i >= 0 && j >= 0; i--, j--){
if(board[i][j] == 'Q')
return false;
}
return true;
}
private:
vector<vector<string>> res;
};
// 首先我们需要对box进行排序,排序的目的:需要从小箱子到大箱子 => 排好序之后就转化为最长上升子序列问题。
// dp[i] 代表以第 i 个箱子放在最底下时,可堆叠的最大高度。
// 每当遍历到一个箱子,就去这个箱子前面找能放在它上面的箱子j,dp[i] = max(dp[i], dp[j]+box[i][2]);
class Solution{
public:
int pileBox(vector<vector<int>>& box){
sort(box.begin(), box.end(),cmp);
int dp[3003];
int ans = 0;
for (int i = 0; i < box.size(); i++){
dp[i] = box[i][2]; // 初始化:不管怎么样,自己总是能放的
for (int j = 0; j < i; j++){
if (box[j][0] < box[i][0] && box[j][1] < box[i][1] && box[j][2] < box[i][2]){
dp[i] = max(dp[i], dp[j] + box[i][2]);
}
}
ans = max(ans, dp[i]);
}
return ans;
}
// 由于下面的三围要比上面的大,那么我们可以先对其中一围升序排序。
// 那么现在可以确定的是,题目的答案一定是排序后的序列的子序列。
// 那么很显然,这个时候就可以动态规划了。
static bool cmp(vector<int> &a, vector<int> &b){
return a[0] < b[0];
}
};
/* 这个题其实也可以分算作卡特兰数的一种变形,在不同位置处分割,对两边的结果做乘积,然后累加得出最终结果。
每次遍历到运算符时开始拆分, 比如 "1^0|0|1",就会被拆成
"1" ^ "0|0|1"
"1^0" | "0|1"
"1^0|0" | "1"
你会发现完美地拆成了两个子任务,可以用递归继续下去(带备忘录的递归,否则超时)
*/
class Solution {
public:
unordered_map<string, pair<int, int> > dp;
int countEval(string s, int result) {
pair<int, int> ans = helper(s);
//pair 中第一个数表示结果中0的出现次数,第二个数表示1的出现次数
if (result == 0)
return ans.first;
else
return ans.second;
}
pair<int, int> helper(string s) {
int len = s.size();
// 记忆化回溯
if (dp.count(s) != 0)
return dp[s];
int ans[2] = {0, 0};
// 递归出口(关键!)
if (len == 1){
if (s == "1")
ans[1]++;
else
ans[0]++;
return dp[s] = {ans[0], ans[1]};
}
// 遍历选择列表
for (int i = 0; i < len; i++) {
if(s[i] == '0' || s[i] == '1')
continue;
//不断的找分割点,类似求卡特兰数的思想(leetcode 96:不同的二叉搜索树)
//递归的获得:分割点前后字符串可以生成多少个括号位置不同的0或者1
pair<int, int> left = helper(s.substr(0, i));
pair<int, int> right = helper(s.substr(i+1));
// 思路:分割 + 相乘 + 累加
switch (s[i]) {
case '&': //分割点为 & ,获得0 有 0&0 ,1&0 , 0&1 三种情况,交叉相乘后累加这三种情况
ans[0] += left.first * right.first + left.second * right.first + right.second * left.first;
ans[1] += left.second * right.second; // 要想获得 1 只有 1&1 这一种情况
break;
case '|':// 同理
ans[0] += left.first * right.first;
ans[1] += left.second * right.second + left.first * right.second + right.first * left.second;;
break;
case '^':// 同理
ans[0] += left.first * right.first + left.second * right.second;
ans[1] += left.first * right.second + right.first * left.second;
break;
}
}
dp[s] = make_pair(ans[0], ans[1]);
return {ans[0], ans[1]};
}
};
// 双指针
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int tail_1 = m - 1;
int tail_2 = n - 1;
int indexOfNewArr = m + n - 1;
while(tail_1 >= 0 && tail_2 >= 0 && indexOfNewArr >= 0){
if(nums1[tail_1] > nums2[tail_2])
nums1[indexOfNewArr--] = nums1[tail_1--];
else
nums1[indexOfNewArr--] = nums2[tail_2--];
}
while(tail_1 >= 0)
nums1[indexOfNewArr--] = nums1[tail_1--];
while(tail_2 >= 0)
nums1[indexOfNewArr--] = nums2[tail_2--];
}
};
// 字母异位词分组
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
vector<vector<string> > res;
if(strs.size() == 0)
return res;
unordered_map<string, vector<string> > umap;
for(auto str: strs){
string temp = str;
sort(temp.begin(), temp.end());
umap[temp].push_back(str);
}
for(auto temp: umap) // 注意这里
res.push_back(temp.second);
return res;
}
};
// leetcode 33 81的进阶题目(不是很好)
class Solution {
public:
int search(vector<int>& nums, int target) {
int len = nums.size();
if (len == 0) {
return -1;
}
int left = 0;
int right = len - 1;
while (left < right) {
int mid = left + ((right - left + 1) >> 1); // 取右中位数
// 这种情况无法二分 => 因为至少要找到一部分有序区间。此时只能顺序查找。
if(nums[mid] == nums[left] && nums[mid] == nums[right]){
int aa = findNum(nums, left, right, target);
int res = -1;
for(int i = aa; i >= 0; i--){
if(nums[i] == target)
res = i;
else
break;
}
return res;
}
if (nums[mid] <= nums[right]) { // 这里这个等号至关重要(记住“旋转数组”题目,判断nums[mid]和两边界的大小时,要加个等号)!! 不加等号就错了,特例:{3, 1} 3
// 使用上取整的中间数,必须在上面的 mid 表达式的括号里 + 1
// 重要:把比较好写的判断(如 target 落在有序区间的那部分)放在 if 的开头考虑,把剩下的情况放在 else 里面。(即 先写好写的条件)
if (nums[mid] <= target && target <= nums[right]) {
// 下一轮搜索区间是 [mid, right]
left = mid; // 这里决定了我们的mid要选“右中位数”
} else {
// 只要上面对了,这里不用思考,可以一下子写出来
right = mid - 1;
}
} else {
// [left, mid] 有序,但是为了和上一个 if 有同样的收缩行为,(收缩右边界)
// 我们故意只认为 [left, mid - 1] 有序
// 当区间只有 2 个元素的时候 int mid = (left + right + 1) >>> 1; 一定会取到右边
// 此时 mid - 1 不会越界,就是这么刚刚好
//if (nums[left] <= target && target <= nums[mid - 1]) { // 这是大佬写法
if (nums[left] <= target && target < nums[mid]) { // 这样写是有条件的
// 下一轮搜索区间是 [left, mid - 1]
right = mid - 1; // (保证了)和上面同样的收缩行为
} else {
// 同理,只要上面对了,这里不用思考,可以一下子写出来
left = mid;
}
}
}
// 有可能区间内不存在目标元素,因此还需做一次判断
if (nums[left] == target) {
int res = -1;
for(int i = left; i >= 0; i--){
if(nums[i] == target)
res = i;
else
break;
}
return res;
}
return -1;
}
int findNum(vector<int>& nums, int left, int right, int target){
for(int i = left; i <= right; i++){
if(nums[i] == target)
return i;
}
return -1;
}
};
// 直接用封装好的二分查找函数find
class Solution {
public:
int findString(vector<string>& words, string s){
auto it = find(words.begin(), words.end(), s);
if (it != words.end())
return it - words.begin();
else
return -1;
}
};
// 这个题之前matrix前忘了加引用号& 导致老是报内存超限的错误
class Solution {
public:
bool searchMatrix(vector<vector<int> > &matrix, int target) {
if(matrix.empty() || matrix[0].empty())
return false;
int rows = matrix.size();
int cols = matrix[0].size();
int row = 0, col = cols - 1;
while(row < rows && col >= 0){
if(matrix[row][col] == target)
return true;
else if(matrix[row][col] < target)
row++;
else if(matrix[row][col] > target)
col--;
}
return false;
}
};
// 二分插入排序
class StreamRank {
public:
StreamRank() {
// nothing
}
void track(int x) {
if(nums.empty() || x >= nums.back())
nums.push_back(x);
else
nums.insert(nums.begin() + find(x), x);
}
int getRankOfNumber(int x) {
if(nums.empty())
return 0;
if(x >= nums.back())
return nums.size();
else
return find(x);
}
vector<int> nums;
int find(int x){
int l = 0, r = nums.size() - 1;
while(l < r){ // 注意这里不是找第一个大于等于x的位置(应该是找尽可能靠后的可插入位置)
int mid = l + r>>1;
if(nums[mid] > x)
r = mid;
else
l = mid + 1;
}
return l;
}
};
// 直接利用map存储
class StreamRank {
public:
StreamRank() {
}
void track(int x) {
cnt[x]++;
}
int getRankOfNumber(int x) {
int res = 0;
for(auto i: cnt){
if(i.first <= x)
res += i.second;
else
break;
}
return res;
}
private:
map<int, int> cnt; // map会对键排序
};
// 直观的理解:先复制一个数组排个序,然后尾一个,头一个地交替填入原来的数组
class Solution {
public:
void wiggleSort(vector<int>& nums) {
vector<int> tmp = nums;
sort(tmp.begin(), tmp.end());
int head = 0, tail = tmp.size() - 1;
for(int i = 0; i < nums.size(); i++){ // 类似摆动排序那道题
if(i%2 == 0) nums[i] = tmp[tail--];
else nums[i] = tmp[head++];
}
}
};
// 交换两个数的 3 种方法:用临时变量、求和相减法、异或运算法
// 求和相减法(求和可能会溢出)
class Solution {
public:
vector<int> swapNumbers(vector<int>& nums) {
nums[0] = nums[0] + nums[1];
nums[1] = nums[0] - nums[1];
nums[0] = nums[0] - nums[1];
return nums;
}
};
// 异或运算法(当a==b时会有问题)
class Solution {
public:
vector<int> swapNumbers(vector<int>& nums) {
nums[0] = nums[0] ^ nums[1];
nums[1] = nums[0] ^ nums[1];
nums[0] = nums[0] ^ nums[1];
return nums;
}
};
// 简单哈希
class WordsFrequency {
public:
WordsFrequency(vector<string>& book) {
for(auto word: book){
umap[word]++;
}
}
int get(string word) {
if (umap.count(word) == 0)
return 0;
return umap[word];
}
private:
unordered_map<string, int> umap;
};
// 不要把这个问题想得太复杂了,把他当成一个数学问题求解就很简单了。(此处是复制的别人代码,暂时可略过)
class Solution {
public:
//比较函数
bool less(const vector<int> &v1, const vector<int> &v2){
if(v1[0] > v2[0]) return false;
else if(v1[0] < v2[0]) return true;
return v1[1] <= v2[1];
}
//判断共线是否有交点
bool no_intersect(vector<int>& p1,vector<int>& p2){
if(p1[0] != p2[0]) return p1[0] < p2[0];
if(p1[1] != p2[1]) return p1[1] < p2[1];
return false;
}
//自定义的类,表示一个向量(x,y)
struct MyVector{
int x;
int y;
MyVector(int _x,int _y):x(_x),
y(_y){
};
int Cross_Product(const MyVector& vec) const{
return x*vec.y - y*vec.x;
};
};
vector<double> intersection(vector<int>& start1, vector<int>& end1, vector<int>& start2, vector<int>& end2) {
if(!less(start1,end1)) swap(start1,end1);
if(!less(start2,end2)) swap(start2,end2);
MyVector vAB(end1[0] - start1[0],end1[1] - start1[1]);
MyVector vCD(end2[0] - start2[0],end2[1] - start2[1]);
MyVector vAC(start2[0] - start1[0],start2[1] - start1[1]);
//计算AB和CD的叉积的模
int Cross_AB_CD = vAB.Cross_Product(vCD);
//如果模为0,说明平行
if(Cross_AB_CD == 0){
//接下来判断是否共线
int Cross_AC_CD = vAC.Cross_Product(vCD);
//如果不共线,则没有交点
if( Cross_AC_CD != 0 ) return { };
//如果共线,但是线段没有重合,也没有交点
if(no_intersect(end1,start2) || no_intersect(end2,start1)) return {};
if( start2[0] > start1[0]) return {start2[0],start2[1]};
return {start1[0],start1[1]};
}
//如果不平行
double ratio = double(vAC.Cross_Product(vCD))/vAB.Cross_Product(vCD);
if(ratio < 0) ratio *= -1;
double resx = start1[0] + ratio*(end1[0] - start1[0]);
double resy = start1[1] + ratio*(end1[1] - start1[1]);
if(resx >= start1[0] && resx <= end1[0] && resx >= start2[0] && resx <= end2[0] ) return {resx,resy};
return {};
}
};
// 这个题就是个体力活,可用作标记的方法做。可暂时跳过
class Solution {
public:
string tictactoe(vector<string>& board){
int n = board.size();
vector<int> row_sum(n, 0);
vector<int> col_sum(n, 0);
int diag_1 = 0;
int diag_2 = 0;
bool have_blank = false;
for(int i = 0 ; i < n ; ++i){
for(int j = 0 ; j < n ; ++j){
if(board[i][j] == ' ') {
have_blank = true;
continue;
}
row_sum[i] += (board[i][j] == 'O' ? 1 : -1);
col_sum[j] += (board[i][j] == 'O' ? 1 : -1);
if(i == j) diag_1 += (board[i][j] == 'O' ? 1 : -1);
if(i == n - j - 1) diag_2 += (board[i][j] == 'O' ? 1 : -1);
}
}
for(int i = 0 ; i < n ; ++i){
if(row_sum[i] == n) return "O";
if(row_sum[i] == -n) return "X";
if(col_sum[i] == n) return "O";
if(col_sum[i] == -n) return "X";
}
if(diag_1 == n || diag_2 == n) return "O";
if(diag_1 == -n || diag_2 == -n) return "X";
return have_blank? "Pending": "Draw";
}
};
// leetcode 172题,算“阶乘里面5的个数”即可。
class Solution {
public:
int trailingZeroes(int n) {
if(n < 5) return 0;
int res = 0;
while(n >= 5){
res += n/5;
n /= 5;
}
return res;
}
};
// 先排序,然后用双指针求得结果。
class Solution {
public:
int smallestDifference(vector<int>& a, vector<int>& b) {
sort(a.begin(), a.end());
sort(b.begin(), b.end());
long ret = INT_MAX;
for(int i = 0, j = 0; i < a.size() && j < b.size(); ){
ret = min(ret, abs(long(a[i]) - long(b[j])));
if(a[i] < b[j])
i++;
else
j++;
}
return ret;
}
};
// 数学解法:max(a, b)的本质是补齐a, b之间的相对距离
typedef long long LL;
class Solution {
public:
int maximum(int a, int b) {
return (((LL)a + b) + labs((LL)a - b)) >> 1;
}
};
// 位运算(max, abs, sort的内部都用了if-else或比较运算符)
class Solution {
public:
int maximum(int a, int b) {
long k = (((long)a - (long)b) >> 63) & 1; // 取出(a-b)的符号位,long类型右移63位即可
return b * k + a * (k ^ 1);
}
};
// 这道题其实就是经典的「力扣」第 198 题:打家劫舍。题目只问最优值,并没有问最优解,因此绝大多数情况下可以考虑使用「动态规划」的方法。
class Solution {
public:
int massage(vector<int>& nums) {
int length = nums.size();
if(length == 0) return 0;
if(length == 1) return nums[0];
if(length == 2) return max(nums[0], nums[1]);
vector<int> dp(length, 0);
dp[0] = nums[0], dp[1] = max(nums[0], nums[1]);
for(int i = 2; i < length; i++){
dp[i] = max(dp[i-1], dp[i-2] + nums[i]);
}
return dp[length-1];
}
};