LeetCode专题 ——《程序员面试金典》

文章目录

    • [面试题 01.01. 判定字符是否唯一](https://leetcode-cn.com/problems/is-unique-lcci/)
    • [面试题 01.02. 判定是否互为字符重排](https://leetcode-cn.com/problems/check-permutation-lcci/)
    • [面试题 01.03. URL化](https://leetcode-cn.com/problems/string-to-url-lcci/)
    • [面试题 01.04. 回文排列](https://leetcode-cn.com/problems/palindrome-permutation-lcci/)
    • [面试题 01.05. 一次编辑](https://leetcode-cn.com/problems/one-away-lcci/)
    • [面试题 01.06. 字符串压缩](https://leetcode-cn.com/problems/compress-string-lcci/)
    • [面试题 01.07. 旋转矩阵](https://leetcode-cn.com/problems/rotate-matrix-lcci/)
    • [面试题 01.08. 零矩阵](https://leetcode-cn.com/problems/zero-matrix-lcci/)
    • [面试题 01.09. 字符串轮转](https://leetcode-cn.com/problems/string-rotation-lcci/)
    • [面试题 02.01. 移除重复节点](https://leetcode-cn.com/problems/remove-duplicate-node-lcci/)
    • [面试题 02.02. 返回倒数第 k 个节点](https://leetcode-cn.com/problems/kth-node-from-end-of-list-lcci/)
    • [面试题 02.03. 删除中间节点](https://leetcode-cn.com/problems/delete-middle-node-lcci/)
    • [面试题 02.04. 分割链表](https://leetcode-cn.com/problems/partition-list-lcci/)
    • [面试题 02.05. 链表求和](https://leetcode-cn.com/problems/sum-lists-lcci)
    • [面试题 02.06. 回文链表](https://leetcode-cn.com/problems/palindrome-linked-list-lcci/)
    • [面试题 02.07. 链表相交](https://leetcode-cn.com/problems/intersection-of-two-linked-lists-lcci/)
    • [面试题 02.08. 环路检测](https://leetcode-cn.com/problems/linked-list-cycle-lcci/)
    • [面试题 03.01. 三合一](https://leetcode-cn.com/problems/triple-in-one-lcci/)
    • [面试题 03.02. 栈的最小值](https://leetcode-cn.com/problems/min-stack-lcci/)
    • [面试题 03.03. 堆盘子](https://leetcode-cn.com/problems/stack-of-plates-lcci/)
    • [面试题 03.04. 化栈为队](https://leetcode-cn.com/problems/implement-queue-using-stacks-lcci/)
    • [面试题 03.05. 栈排序](https://leetcode-cn.com/problems/sort-of-stacks-lcci/)
    • [面试题 03.06. 动物收容所](https://leetcode-cn.com/problems/animal-shelter-lcci/)
    • [面试题 04.01. 节点间通路](https://leetcode-cn.com/problems/inter-node-access-lcci/)
    • [面试题 04.02. 最小高度树](https://leetcode-cn.com/problems/minimum-height-tree-lcci/)
    • [面试题 04.03. 特定深度节点链表](https://leetcode-cn.com/problems/list-of-depth-lcci/)
    • [面试题 04.04. 检查平衡性](https://leetcode-cn.com/problems/check-balance-lcci/)
    • [面试题 04.05. 合法二叉搜索树](https://leetcode-cn.com/problems/legal-binary-search-tree-lcci/)
    • [面试题 04.06. 后继者](https://leetcode-cn.com/problems/successor-lcci/)
    • [面试题 04.08. 首个共同祖先](https://leetcode-cn.com/problems/first-common-ancestor-lcci/)
    • [面试题 04.09. 二叉搜索树序列](https://leetcode-cn.com/problems/bst-sequences-lcci/)
    • [面试题 04.10. 检查子树](https://leetcode-cn.com/problems/check-subtree-lcci/)
    • [面试题 04.12. 求和路径](https://leetcode-cn.com/problems/paths-with-sum-lcci/)
    • [面试题 05.01. 插入](https://leetcode-cn.com/problems/insert-into-bits-lcci/)
    • [面试题 05.02. 二进制数转字符串](https://leetcode-cn.com/problems/bianry-number-to-string-lcci/)
    • [面试题 05.03. 翻转数位](https://leetcode-cn.com/problems/reverse-bits-lcci/)
    • [面试题 05.04. 下一个数](https://leetcode-cn.com/problems/closed-number-lcci/)
    • [面试题 05.06. 整数转换](https://leetcode-cn.com/problems/convert-integer-lcci/)
    • [面试题 05.07. 配对交换](https://leetcode-cn.com/problems/exchange-lcci/)
    • [面试题 05.08. 绘制直线](https://leetcode-cn.com/problems/draw-line-lcci/)
    • [面试题 08.01. 三步问题](https://leetcode-cn.com/problems/three-steps-problem-lcci/)
    • [面试题 08.02. 迷路的机器人](https://leetcode-cn.com/problems/robot-in-a-grid-lcci/)
    • [面试题 08.03. 魔术索引](https://leetcode-cn.com/problems/magic-index-lcci/)
    • [面试题 08.04. 幂集](https://leetcode-cn.com/problems/power-set-lcci/)
    • [面试题 08.05. 递归乘法](https://leetcode-cn.com/problems/recursive-mulitply-lcci/)
    • [面试题 08.06. 汉诺塔问题](https://leetcode-cn.com/problems/hanota-lcci/)
    • [面试题 08.07. 无重复字符串的排列组合](https://leetcode-cn.com/problems/permutation-i-lcci/)
    • [面试题 08.08. 有重复字符串的排列组合](https://leetcode-cn.com/problems/permutation-ii-lcci/)
    • [面试题 08.09. 括号](https://leetcode-cn.com/problems/bracket-lcci/)
    • [面试题 08.10. 颜色填充](https://leetcode-cn.com/problems/color-fill-lcci/)
    • [面试题 08.11. 硬币](https://leetcode-cn.com/problems/coin-lcci/)
    • [面试题 08.12. 八皇后](https://leetcode-cn.com/problems/eight-queens-lcci/)
    • [面试题 08.13. 堆箱子](https://leetcode-cn.com/problems/pile-box-lcci/)
    • [面试题 08.14. 布尔运算](https://leetcode-cn.com/problems/boolean-evaluation-lcci/)
    • [面试题 10.01. 合并排序的数组](https://leetcode-cn.com/problems/sorted-merge-lcci/)
    • [面试题 10.02. 变位词组](https://leetcode-cn.com/problems/group-anagrams-lcci/)
    • [面试题 10.03. 搜索旋转数组](https://leetcode-cn.com/problems/search-rotate-array-lcci/)
    • [面试题 10.05. 稀疏数组搜索](https://leetcode-cn.com/problems/sparse-array-search-lcci/)
    • [面试题 10.09. 排序矩阵查找](https://leetcode-cn.com/problems/sorted-matrix-search-lcci/)
    • [面试题 10.10. 数字流的秩](https://leetcode-cn.com/problems/rank-from-stream-lcci/)
    • [面试题 10.11. 峰与谷](https://leetcode-cn.com/problems/peaks-and-valleys-lcci/)
    • [面试题 16.01. 交换数字](https://leetcode-cn.com/problems/swap-numbers-lcci/)
    • [面试题 16.02. 单词频率](https://leetcode-cn.com/problems/words-frequency-lcci/)
    • [面试题 16.03. 交点](https://leetcode-cn.com/problems/intersection-lcci/)
    • [面试题 16.04. 井字游戏](https://leetcode-cn.com/problems/tic-tac-toe-lcci/)
    • [面试题 16.05. 阶乘尾数](https://leetcode-cn.com/problems/factorial-zeros-lcci/)
    • [面试题 16.06. 最小差](https://leetcode-cn.com/problems/smallest-difference-lcci/)
    • [面试题 16.07. 最大数值](https://leetcode-cn.com/problems/maximum-lcci/)
    • [面试题 17.16. 按摩师](https://leetcode-cn.com/problems/the-masseuse-lcci/)

下面只给出答案,具体题目请参考:LeetCode专题 ——《程序员面试金典》

面试题 01.01. 判定字符是否唯一

// 要求不使用额外的数据结构
// 排序 + 双指针
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;
    }
    
};

面试题 01.02. 判定是否互为字符重排

// 简单哈希(计数类)
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;
    }
};

面试题 01.03. URL化

// 从后往前
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);
	}
	
};

面试题 01.04. 回文排列

// 哈希表统计每个字符出现次数
// 如果是回文串的排列,则字符出现次数为奇数的个数不能大于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;
    }
};

面试题 01.05. 一次编辑

// 编辑距离 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));
    }
    
};

面试题 01.06. 字符串压缩

// 应该算双指针(快慢指针)算法思想吧
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;
    }
};

面试题 01.07. 旋转矩阵

// 先转置,再逐行旋转
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]);
        }
        
    }
};

面试题 01.08. 零矩阵

// 原位标记(用矩阵自己的第一行和第一列做标记),不使用额外空间
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;
        }
        
    }
};

面试题 01.09. 字符串轮转

// 如果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
    }
};

面试题 02.01. 移除重复节点

// 无序链表去除重复值,只能用哈希表来记录重复值了
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;
    }
};

面试题 02.02. 返回倒数第 k 个节点

// 快慢指针(快指针先走(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;
    }
};

面试题 02.03. 删除中间节点

// 删除链表节点(交换元素法)
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;
    }
};

面试题 02.04. 分割链表

// 按题意分割成两个链表之后再接起来
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;
    }
};

面试题 02.05. 链表求和

// 模拟加法
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;
        }
    }

};

面试题 02.06. 回文链表

// 快慢指针 + 边遍历边翻转(前半部分)
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;
    }
};

面试题 02.07. 链表相交

// 快慢指针(《剑指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;
    }
};

面试题 02.08. 环路检测

// 三个步骤:求环中某节点(先判断有没有环)、求环的长度、求环的入口节点。
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;
    }
 
};

面试题 03.01. 三合一

// 题解: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
    }
};

面试题 03.02. 栈的最小值

// 借用辅助栈
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;
};

面试题 03.03. 堆盘子

// 主要思路是使用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;
    }
};

面试题 03.04. 化栈为队

// 该题解有一些异常输入没处理,可用“全局变量”的方式处理一下就好
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;  // 出队列
};

面试题 03.05. 栈排序

// 本题与“面试题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();
    }
};

面试题 03.06. 动物收容所

// 复制的别人的代码,感觉不太可能考,暂时可以略过
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};
	}
};

面试题 04.01. 节点间通路

// 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;
    }
};

面试题 04.02. 最小高度树

// 将有序数组转换为二叉搜索树(递归二分建树)
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;
    }
    
};

面试题 04.03. 特定深度节点链表

// 广度优先搜索
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;
    }
    
};

面试题 04.04. 检查平衡性

// 基于“前序遍历”的方式,会有很多重复遍历。
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;
};

面试题 04.05. 合法二叉搜索树

// 利用“上界”与“下界”
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;
         
    }

};

面试题 04.06. 后继者

// 中序遍历,设置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;
    }
};

面试题 04.08. 首个共同祖先

// 此函数返回的就是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;
    }
    
};

面试题 04.09. 二叉搜索树序列

// 基于“后序遍历”
// 从根节点开始往下找,每个节点的左右子树互不干扰,只要每个节点在它子树节点前且“子树序列”中各节点的相对顺序不变就行
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();
		}
	}
	
};

面试题 04.10. 检查子树

// 递归遍历树(与《剑指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);
    }
    
};

面试题 04.12. 求和路径

// 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;
    }

};

面试题 05.01. 插入

// 位运算
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);
    }
};

面试题 05.02. 二进制数转字符串

// 位运算,不太懂,可暂时跳过
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;
    }
    
};

面试题 05.03. 翻转数位

// 位运算:把二进制字符按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;
    }
    
};

面试题 05.04. 下一个数

// 暴力解法即可
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;
	}
};

面试题 05.06. 整数转换

// 位运算
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;
	}
};

面试题 05.07. 配对交换

/*思路的话就是分别取出奇数位和偶数位,移动后做或运算。
题目规定 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;
    }
};

面试题 05.08. 绘制直线

// 题目描述不清,感觉不会考,可暂时跳过。
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;
    }
};

面试题 08.01. 三步问题

// 标准动态规划
#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;
    }
};

面试题 08.02. 迷路的机器人

// 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;
};

面试题 08.03. 魔术索引

// 如果没有重复数字,可用《剑指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;
    }
};

面试题 08.04. 幂集

// 回溯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;
};

面试题 08.05. 递归乘法

// 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);
    }
};

面试题 08.06. 汉诺塔问题

// 汉诺塔题解: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
//    }
};

面试题 08.07. 无重复字符串的排列组合

// 全排列-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]);
        }
    }
    
};

面试题 08.08. 有重复字符串的排列组合

// 全排列-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]);
        }
    }
    
};

面试题 08.09. 括号

// 基于“加法”实现
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();
        }
    }

};

面试题 08.10. 颜色填充

// 简单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;
};

面试题 08.11. 硬币

// 状态转移方程: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];
    }
    
};

面试题 08.12. 八皇后

// 经典回溯
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;
};

面试题 08.13. 堆箱子

// 首先我们需要对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];
    }
};

面试题 08.14. 布尔运算

/* 这个题其实也可以分算作卡特兰数的一种变形,在不同位置处分割,对两边的结果做乘积,然后累加得出最终结果。
每次遍历到运算符时开始拆分, 比如 "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]};
	}
	
};

面试题 10.01. 合并排序的数组

// 双指针
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--];
    }
};

面试题 10.02. 变位词组

// 字母异位词分组
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;
    }

};

面试题 10.03. 搜索旋转数组

// 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;
    }
};

面试题 10.05. 稀疏数组搜索

// 直接用封装好的二分查找函数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;
    }
};

面试题 10.09. 排序矩阵查找

// 这个题之前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;
    }
};

面试题 10.10. 数字流的秩

// 二分插入排序
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会对键排序
};

面试题 10.11. 峰与谷

// 直观的理解:先复制一个数组排个序,然后尾一个,头一个地交替填入原来的数组
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++];
        }
    }
};

面试题 16.01. 交换数字

// 交换两个数的 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;
    }
};

面试题 16.02. 单词频率

// 简单哈希
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;
};

面试题 16.03. 交点

// 不要把这个问题想得太复杂了,把他当成一个数学问题求解就很简单了。(此处是复制的别人代码,暂时可略过)
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 {};

    }
};

面试题 16.04. 井字游戏

// 这个题就是个体力活,可用作标记的方法做。可暂时跳过
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";
    }

};

面试题 16.05. 阶乘尾数

// 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;
    }
};

面试题 16.06. 最小差

// 先排序,然后用双指针求得结果。
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;
    }
    
};

面试题 16.07. 最大数值

// 数学解法: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);
    }
};

面试题 17.16. 按摩师

// 这道题其实就是经典的「力扣」第 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];
    }
};

你可能感兴趣的:(算法(UVa,+,LeetCode,+,OJ,+,……))