LeetCode专题 ——《剑指offer》

文章目录


下面只给出答案,具体题目请参考: LeetCode专题 ——《剑指offer》

面试题03. 数组中重复的数字

// 基于桶排序
class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        int length = nums.size();
        if(length == 0)
            return false;
            
        for(int i = 0; i < length; i++){
            if(i == nums[i])
                continue;
            while(i != nums[i]){
                if(nums[i] == nums[nums[i]])
                    return nums[i];
                else
                    swap(nums[i], nums[nums[i]]);
            }
        }

        return -1;
    }

};

// 进阶题目是用二分法,见《剑指offer》

面试题04. 二维数组中的查找

// 从右上角开始找,每次排除一行或者一列
class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int> > matrix, int target) {
        if(matrix.size() == 0 || matrix[0].size() == 0)
            return false;
    
        int rows = matrix.size();
        int cols = matrix[0].size();
        int row = 0;
        int col = cols - 1;
        while(row < rows && col >= 0){
            if(matrix[row][col] == target)
                return true;
            else if(matrix[row][col] < target)
                row++;
            else
                col--;
        }
        
        return false;
    }
};

// 或者 逐行二分
class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int> > matrix, int target) {
        for(int i = 0; i < matrix.size(); i++){
            int low = 0;
            int high = matrix[i].size() - 1;
            while(low <= high){
                int mid = (low + high) / 2;
                if(target > matrix[i][mid])
                    low = mid + 1;
                else if(target < matrix[i][mid])
                    high = mid - 1;
                else
                    return true;
            }
        }
        
        return false;
    }
    
};

面试题05. 替换空格

// 从后往前
class Solution {
public:
	string replaceSpace(string str) {
	    int length = str.size();
        if(length <= 0)
            return "";
            
        int numOfBlank = 0;
        for(int i = 0; i < length; i++){
            if(str[i] == ' ')
                numOfBlank++;
        }
        
        int newLength = length + 2 * numOfBlank;
        string res(newLength, ' ');
        
        int indexOfOriginalStr = length - 1;
        int indexOfNewStr = newLength - 1;
        while(indexOfOriginalStr >=0 && indexOfNewStr >=0){
            if(str[indexOfOriginalStr] == ' '){
                res[indexOfNewStr--] = '0';
                res[indexOfNewStr--] = '2';
                res[indexOfNewStr--] = '%';
                indexOfOriginalStr--;
            }else{
                res[indexOfNewStr--] = str[indexOfOriginalStr--];
            }
        }
        
        return res;
	}
	
};

面试题06. 从尾到头打印链表

// 递归
class Solution {
public:
    vector<int> reversePrint(ListNode* head) {
        reversePrintRecursively(head);
        return myVector;  // 注意这里的写法
    }

    void reversePrintRecursively(ListNode* head){
        if(head != nullptr){
            reversePrint(head->next);  // 先递归打印完后面的节点元素
            myVector.push_back(head->val);  // 再打印当前元素
        }
    }

private:
    vector<int> myVector;
};

// 用栈也可以

面试题07. 重建二叉树

// 经典分治算法
class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if(preorder.size() <= 0 || inorder.size() <= 0 || preorder.size() != inorder.size()){
            return nullptr;
        }
        
        int length = preorder.size();
        return reConstructRecursively(preorder, 0, length - 1, inorder, 0, length - 1);
    }
    
    
    TreeNode* reConstructRecursively(vector<int> &preorder, int preStart, int preEnd, 
                                    vector<int> &inorder, int inStart, int inEnd){
        int rootVal = preorder[preStart];
        TreeNode* root = new TreeNode(rootVal);
        root->left = root->right = nullptr;
        
        if(preStart == preEnd && inStart == inEnd && preorder[preStart] == inorder[inStart])  //递归出口
            return root;
        
        int inRoot = inStart;
        for(; inRoot <= inEnd; inRoot++){
            if(inorder[inRoot] == rootVal)
                break;
        }
        
        int leftLength = inRoot - inStart;
        if(leftLength > 0){
            root->left = reConstructRecursively(preorder, preStart+1, preStart+leftLength, inorder, inStart, inRoot-1);
        }
        if(inEnd - inRoot > 0){
            root->right = reConstructRecursively(preorder, preStart+leftLength+1, preEnd, inorder, inRoot+1, inEnd);
        }
        
        return root;
    }
    
};

面试题08. 二叉树的下一个结点

// 力扣《剑指offer》专题中没有这个题
class Solution {
public:
    TreeLinkNode* GetNext(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;
    }
};

面试题09. 用两个栈实现队列

// 用全局变量处理异常
int g_invalidInput = false;

class CQueue{
public:
    CQueue() {
        // nothing
    }
        
    void appendTail(int value){
        stack1.push(value);
    }

    int deleteHead(){
        if(stack2.empty()){
            while(!stack1.empty()){
                stack2.push(stack1.top());
                stack1.pop();
            }
        }
        
        if(!stack2.empty()){
            int res = stack2.top();
            stack2.pop();
            return res;
        }
        
        g_invalidInput = true;  // 区别是正常返回-1,还是异常返回-1
        return -1;
    }

private:
    stack<int> stack1;
    stack<int> stack2;
};

// 补充:用两个队列实现栈
bool g_invalidInput = false;
class MyStack {
public:
    /** Initialize your data structure here. */
    MyStack() {
        // nothing
    }
    
    /** Push element x onto stack. */
    void push(int x) {
        if(!q1.empty())
            q1.push(x);
        else
            q2.push(x);
    }
    
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        if(!q1.empty()){
            int num = q1.size();
            while(num != 1){
                q2.push(q1.front());
                q1.pop();
                num--;
            }
            
            int res = q1.front();
            q1.pop();
            return res;
        }else{
            int num = q2.size();
            while(num != 1){
                q1.push(q2.front());
                q2.pop();
                num--;
            }
            
            int res = q2.front();
            q2.pop();
            return res;            
        }
        
        g_invalidInput = true;
        return -1;
    }
    
    /** Get the top element. */
    int top() {
        if(!q1.empty()){
            return q1.back();
        }else{
            return q2.back();
        }
        
        g_invalidInput = true;
        return -1;        
    }
    
    /** Returns whether the stack is empty. */
    bool empty() {
        if(q1.empty() && q2.empty())
            return true;
        else
            return false;
    }
    
private:
    queue<int> q1, q2;
};

面试题10- I. 斐波那契数列

// 经典动规(可压缩空间到O(1))
typedef long long LL;
class Solution {
public:
    int fib(int n) {
        if(n == 0)
            return 0;
        if(n == 1)
            return 1;
            
        LL curMinus1 = 1;
        LL curMinus2 = 0;
        LL res = 0;
        for(int i = 2; i <= n; i++){
            res = (curMinus1 + curMinus2) % 1000000007;   // 其实并不需要 LL
            curMinus2 = curMinus1;
            curMinus1 = res;
        }
        return res;
    }
};

面试题10- II. 青蛙跳台阶问题

// 同上
typedef long long LL;
class Solution {
public:
    int numWays(int n) {
        if(n == 0)
            return 1;
        if(n == 1)
            return 1;
            
        LL curMinus1 = 1;
        LL curMinus2 = 1;
        LL res = 0;
        for(int i = 2; i <= n; i++){
            res = (curMinus1 + curMinus2) % 1000000007;
            curMinus2 = curMinus1;
            curMinus1 = res;
        }
        return res;
    }
};

面试题11. 旋转数组的最小数字

// 复杂二分,需要考虑各种特殊情况。比如:完全有序数组,不同位置的三个元素相等。
class Solution {
public:
    int minArray(vector<int> rotateArray) {        
        int length = rotateArray.size();
        if(length == 0)
            return 0;
            
        int low = 0, high = length-1;
        int mid = low;  // 特殊情况:排序数组
        while(rotateArray[low] >= rotateArray[high]){  // 这个循环条件记不清了
            if(high - low == 1){
                mid = high;
                break;
            }
            
            mid = (low + high) >> 1;
            if(rotateArray[low] == rotateArray[mid] && rotateArray[mid] == rotateArray[high]){
                return minInArray(rotateArray, low, high);
            }
            
            if(rotateArray[mid] >= rotateArray[low])
                low = mid;
            else
                high = mid;
        }
        return rotateArray[mid];
    }
    
    int minInArray(vector<int> rotateArray, int low, int high){
        int _Min = 0x7FFFFFFF;
        for(int i=0; i<rotateArray.size(); i++){
            if(rotateArray[i] < _Min)
                _Min = rotateArray[i];
        }
        return _Min;
    }
};

面试题12. 矩阵中的路径

// 经典回溯
class Solution {
public:
//    bool exist(char* matrix, int rows, int cols, char* str)
    bool exist(vector<vector<char>>& matrix, string str){
        int rows = matrix.size();
        if(rows == 0)
            return false;
        int cols = matrix[0].size();
        if(cols == 0)
            return false;
        int strLength = str.length();
        if(strLength == 0)
            return false;
            
        bool visited[rows * cols];  // 用二维的vector更方便
        memset(visited, 0, sizeof(visited));
        int strPosition = 0;
        for(int i = 0; i < rows; i++){
            for(int j = 0; j < cols; j++){
                if(hashPathRecusively(matrix, rows, cols, i, j, str, strPosition, visited)){
                    return true;
                }
            }
        }
        
        return false;
    }
    
    // 扯淡一般的结果,在这个函数形参列表里,之前没加引用,报超时错误。
    bool hashPathRecusively(vector<vector<char>> &matrix, int rows, int cols, int row, int col, 
                            string &str, int strPosition, bool* visited){
        if(strPosition == str.length())
            return true;
        
        bool hasPath = false;  // 默认值(重要!)
        if(row >= 0 && row < rows && col >= 0 && col < cols && 
           matrix[row][col] == str[strPosition] && !visited[row*cols + col]){
               
            strPosition++;
            visited[row*cols + col] = true;
            
            hasPath = hashPathRecusively(matrix, rows, cols, row-1, col, str, strPosition, visited) ||
                      hashPathRecusively(matrix, rows, cols, row+1, col, str, strPosition, visited) ||
                      hashPathRecusively(matrix, rows, cols, row, col-1, str, strPosition, visited) ||
                      hashPathRecusively(matrix, rows, cols, row, col+1, str, strPosition, visited);
                      
            if(!hasPath){
                strPosition--;
                visited[row*cols + col] = false;
            }
        }
        
        return hasPath;
    }
};

面试题13. 机器人的运动范围

// 回溯(计数类别忘记 +1)
class Solution {
public:
    int movingCount(int rows, int cols, int threshold){
        if(rows <= 0 || cols <= 0 || threshold < 0)
            return 0;
        
        bool visited[rows * cols];
        memset(visited, 0, rows*cols);
        return arriveNumOfGrid(threshold, rows, cols, 0, 0, visited);
    }
    
    int arriveNumOfGrid(int threshold, int rows, int cols, int row, int col, bool* visited){
        int mSum = 0;  // 默认值(重要!)
        if(row >= 0 && row < rows && col >= 0 && col < cols && sumOfTwoNum(row, col) <= threshold 
           && !visited[row*cols + col]){
            
            visited[row*cols + col] = true;    
            mSum = 1 + arriveNumOfGrid(threshold, rows, cols, row-1, col, visited)
                     + arriveNumOfGrid(threshold, rows, cols, row+1, col, visited)
                     + arriveNumOfGrid(threshold, rows, cols, row, col-1, visited)
                     + arriveNumOfGrid(threshold, rows, cols, row, col+1, visited);  // 这里的 +1 不要忘记
        }
        
        return mSum;
    }
    
    int sumOfTwoNum(int a, int b){
        return sumOfOneNum(a) + sumOfOneNum(b);
    }
    
    int sumOfOneNum(int n){
        int _sum = 0;
        while(n){
            _sum += n%10;
            n /= 10;
        }
        return _sum;
    }
};

面试题14- I. 剪绳子

// dp
class Solution {
public:
    int cuttingRope(int number) {
        if(number == 0)
            return 0;
        if(number == 1)
            return 1;
        if(number == 2)
            return 1;
        if(number == 3)
            return 2;

        int res[number + 1];  // 使用变量定义长度时,不可在定义时同时进行初始化赋值,需要在之后进行赋值
        for(int i = 0; i <= 3; i++)
            res[i] = i;

        for(int length = 4; length <= number; length++){
            int iMax = INT_MIN;
            for(int left = 1; left <= length/2; left++){
                int right = length - left;
                iMax = max(iMax, res[left] * res[right]);
            }
            res[length] = iMax;
        }
        return res[number];
    }
};

// 其次还有贪心解法:尽可能剪更多长度为3的绳子,当剩下的绳子长度为4时,将其剪为两段长度都是2的绳子。(见下)

面试题14- II. 剪绳子 II

// 这里只能用贪心,否则可能会溢出。
typedef long long LL;
class Solution {
public:
    int cuttingRope(int n) {
        // 先处理特例
        if(n == 0)
            return 0;
        if(n == 1)
            return 1;
        if (n == 2)
            return 1;
        if (n == 3)
            return 2;
        
        int remainder = n % 3;  // 余数
        LL res = 1;
        for(int i = 0; i < n / 3 - 1; i++) {
            res = (res * 3) % 1000000007;
        }
        
        if (remainder == 0) {
            res = (res * 3) % 1000000007;
        } else if (remainder == 1) {
            res =  (res * 4) % 1000000007;
        } else if(remainder == 2){
            res = (res * 6) % 1000000007;
        }
        
        return (int)res;
    }
};

面试题15. 二进制中1的个数

// 位运算  小技巧: x & (x-1)  可将x的二进制表示中最右侧的1变为0
class Solution {
public:
    int hammingWeight(uint32_t n) {
        int sum = 0;
        while(n){
            n = (n-1)&n;
            sum++;
        }
        return sum;
    }
};

面试题16. 数值的整数次方

// 理解递归
class Solution {
public:
    double myPow(double x, int n) {
        if(doubleEqualTo0(x) && n < 0)
            return 0;  // 这里其实是非法输入
        if(n == 0)
            return 1;
        if(n == 1)
            return x;
        if(n == -1)
            return 1/x;
        
        double half = myPow(x, n/2);
        double rest = myPow(x, n%2);
        return half * half * rest;
    }
    
    bool doubleEqualTo0(double a){         // 注意这里!
        if(a>-0.0000001 && a < 0.0000001)  // 用 &&
            return true;
        return false;
    }
    
};

面试题17. 打印从1到最大的n位数

// 这道题其实被leetcode简化了。这是一个隐藏的“大数问题”,应该用dfs(回溯)解决。
class Solution {
public:
    vector<int> printNumbers(int n) {
        vector<int> res;
        int i = 1;
        int imax = pow(10,n);
        while(i < imax){
            res.push_back(i++);
        }
        return res;
    }
};

// dfs(回溯)
class Solution {
public:
    vector<int> printNumbers(int n) {
        string temp(n, '0');
        printNumbersRecursively(n, temp, 0);
        
    }
    
    void printNumbersRecursively(int n, string temp, int index){
        if(index == n){
            int num = stoi(temp);
            if(num != 0)
                res.push_back(stoi(temp));
            return;
        }
        
        for(int i = 0; i <= 9; i++){
            temp[index] = i + '0';
            printNumbersRecursively(n, temp, index + 1);
        }
        
    }

private:
    vector<int> res;
};

面试题18. 删除链表的节点

// 简单题(没有考虑非法输入问题,面试写代码时可能得处理下非法输入问题)
class Solution {
public:
    ListNode* deleteNode(ListNode* head, int val) {
        ListNode* dummyHead = new ListNode(-1);
        ListNode* pNode = dummyHead;
        dummyHead->next = head;
        
        while(pNode && pNode->next && pNode->next->val != val)
            pNode = pNode->next;
        pNode->next = pNode->next->next;
        
        return dummyHead->next;
    }
};

// 进阶题目
class Solution {
public:
    ListNode* deleteDuplicates(ListNode* pHead)
    {
        if(pHead == nullptr)
            return nullptr;
        
        ListNode* dummyHead = new ListNode(-1);
        dummyHead->next = pHead;
        ListNode* pPre = dummyHead;
        ListNode* pNode = dummyHead->next;
        while(pNode != nullptr){
            ListNode* pNext = pNode -> next;
            
            bool toBeDeleted = false;
            if(pNext != nullptr && pNode->val == pNext->val)
                toBeDeleted = true;
                
            if(!toBeDeleted){
                pPre = pNode;
                pNode = pNext;
            }else{
                int duplication = pNode->val;
                ListNode* deleteNode = pNode;
                while(deleteNode->val == duplication){  // 这个while卡了好久,没有仔细考虑清楚。
                    pNext = deleteNode->next;
                    
                    delete deleteNode;
                    // deleteNode = nullptr;
                    deleteNode = pNext;
                    
                    if(pNext == nullptr)
                        break;
                }
                
                pPre->next = pNext;
                pNode = pNext;
            }
            
        }
        
        return dummyHead->next;
    }
    
};

面试题19. 正则表达式匹配

// 递归
class Solution {
public:
	bool isMatch(string str, string pattern)
	{
		if (str.length() == 0 && pattern.length() == 0)
			return true;
		if (pattern.length() == 0)
			return false;

		return matchRecursively(str, pattern, 0, 0);
	}

	bool matchRecursively(string &str, string &pattern, int indexOfStr, int indexOfPattern) {
		if (indexOfStr == str.length() && indexOfPattern == pattern.length())
			return true;
		if (indexOfPattern == pattern.length())
			return false;
		
		// 当模式串中的第二个字符是*时,情况会复杂一些。(注意防止越界)
		if (indexOfPattern < pattern.size()-1 && pattern[indexOfPattern + 1] == '*') {
			if (str[indexOfStr] == pattern[indexOfPattern] || (indexOfStr != str.length() && pattern[indexOfPattern] == '.'))
				// 不管*匹配此字符几次,当前的选择只有两个:不匹配/匹配(即匹配0次/匹配1次)。所以可以这样处理
				return matchRecursively(str, pattern, indexOfStr + 1, indexOfPattern) ||  // *匹配,但是此时模式串并不向后移动字符。这样的话就包含了匹配1次和匹配多次的所有情况了。(递归思想)
				//matchRecursively(str, pattern, indexOfStr + 1, indexOfPattern + 2) ||    // 注释掉这一行 运行用时从1716ms降低到了40ms,太迷了
				matchRecursively(str, pattern, indexOfStr, indexOfPattern+2);  // *不匹配
			else
				return matchRecursively(str, pattern, indexOfStr, indexOfPattern+2);
				
		}else {
			if (str[indexOfStr] == pattern[indexOfPattern] || (indexOfStr != str.length() && pattern[indexOfPattern] == '.'))
				return matchRecursively(str, pattern, indexOfStr + 1, indexOfPattern + 1);
			else
				return false;
		}

	}

};

// 动态规划
class Solution {
public:
	bool isMatch(string s, string p){
		int m = s.size();
		int n = p.size();
		if(m == 0 && n == 0)
            return true;
        if(n == 0)
            return false;
        
        // 初始化
        bool dp[m + 1][n + 1] = {0};
        memset(dp, 0, sizeof(dp));  // 不加这一行就出错了,说明上面的方式并不能全部初始化数组为0。(数组长度是变量时,不建议在定义时初始化)
        dp[0][0] = true;
        for(int j = 2; j <= n; j++)   // 这里的初始化方式类似 第44题
            dp[0][j] = dp[0][j-2] && p[j-1] == '*';
        
        for(int i = 1; i <= m; i++){
            for(int j = 1; j <= n; j++){
                if(s[i-1] == p[j-1] || p[j-1] == '.'){
                    dp[i][j] = dp[i-1][j-1];
                }else if(p[j-1] == '*'){
                    if(s[i-1] == p[j-2] || p[j-2] == '.')  
                        dp[i][j] = dp[i][j-2] || dp[i-1][j];  // 不匹配/匹配
                    else
                        dp[i][j] = dp[i][j-2];
                }
            }
        }
        
        return dp[m][n];
	}

};

面试题20. 表示数值的字符串

// 这里还是建议看书上的解答
class Solution {
public:
    bool isNumber(string str){
        int low = 0, high = str.size() - 1;
        while(low <= high && str[low] == ' ') low++;
        while(low <= high && str[high] == ' ') high--;
        if(high < low) 
            return false;
        
        str = str.substr(low, high - low + 1);  // 取出字符串前后部分的空格
        length = str.size(); 
        if(length == 0)
            return false;
        
        int indexOfStr = 0;
        bool res = isInteger(str, indexOfStr);
        if(indexOfStr < length && str[indexOfStr] == '.'){
            indexOfStr++;
            res = isUnsignedInteger(str, indexOfStr) || res;  // 顺序一定不能错,注意逻辑运算的截断效应
//            res = res || isUnsignedInteger(str, indexOfStr);  // 会出错!
        }
        
        if(indexOfStr < length && (str[indexOfStr] == 'e' || str[indexOfStr] == 'E')){
            indexOfStr++;
            res = res && isInteger(str, indexOfStr); 
        }
        
        return res && indexOfStr == length;
    }

    bool isInteger(string str, int &indexOfStr){
        if(indexOfStr < length && (str[indexOfStr] == '+' || str[indexOfStr] == '-'))
            indexOfStr++;
        
        return isUnsignedInteger(str, indexOfStr);
    }
    
    bool isUnsignedInteger(string str, int &indexOfStr){
        if(indexOfStr >= length || str[indexOfStr] < '0' || str[indexOfStr] > '9')
            return false;
        while(indexOfStr < length && str[indexOfStr] >= '0' && str[indexOfStr] <= '9')
            indexOfStr++;
        
        return true;
    }

private:
    int length;
};

面试题21. 调整数组顺序使奇数位于偶数前面

// 有点像快排的思路
class Solution {
public:
    vector<int> exchange(vector<int>& nums) {
        int length = nums.size();
        if(length <= 1)
            return nums;
        
        int left = 0, right = length - 1;
        while(left < right){
            while(left < right && nums[right] % 2 == 0)
                right--;
            while(left < right && nums[left] % 2 == 1)
                left++;
            if(left < right)
                swap(nums[left], nums[right]);
        }
        
        return nums;
    }
};

// 更进阶的解法是用函数指针,解耦。

面试题22. 链表中倒数第k个节点

// 快慢指针
class Solution {
public:
    ListNode* getKthFromEnd(ListNode* pListHead, int k) {
        if(pListHead == nullptr || k <= 0)  // k <= 0 不要忘记加
            return nullptr;
        
        int length = 0;
        ListNode* pNode = pListHead;
        while(pNode != nullptr){
            length++;
            pNode = pNode->next;
        }
        if(k > length)
            return nullptr;
        
        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;
    }
};

面试题23. 链表中环的入口节点

// 这个题力扣《剑指offer》专题中没有
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;
    }
 
};

面试题24. 反转链表

// 迭代法
class Solution {
public:
    ListNode* reverseList(ListNode* pHead) {
        if(pHead == nullptr)
            return nullptr;
        
        ListNode* pPre = nullptr;
        ListNode* pNode = pHead;
        while(pNode != nullptr){
            ListNode* pNext = pNode->next;
            pNode->next = pPre;
            pPre = pNode;
            pNode = pNext;
        }
        
        return pPre;
    }
    
};

// 递归法(这个不强求,看看就好,不太好理解)
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (head == NULL || head->next == NULL) return head;
        ListNode* p = reverseList(head->next);
        head->next->next = head; 
        head->next = NULL;  // 断链,防止成环(死循环)
        return p;
    }
};

面试题25. 合并两个排序的链表

// 迭代法
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* pHead1, ListNode* pHead2)
    {  
        ListNode* pDummy = new ListNode(-1);
        ListNode* pNode = pDummy;
        while(pHead1 != nullptr && pHead2 != nullptr){  // 用&&
            if(pHead1->val < pHead2->val){
                pNode->next = pHead1;
                pHead1 = pHead1->next;
            }else{
                pNode->next = pHead2;
                pHead2 = pHead2->next;
            }
            
            pNode = pNode->next;
        }
        
        pNode->next = pHead1 == nullptr? pHead2: pHead1;        
        return pDummy->next;
    }
    
};

// 递归法
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* pHead1, ListNode* pHead2)
    {
        if(pHead1 == nullptr && pHead2 == nullptr)  // 这里注意是 &&
            return nullptr;
        
        ListNode* pHead = mergeRecursively(pHead1, pHead2);
        
        return pHead;
    }
    
    ListNode* mergeRecursively(ListNode* pHead1, ListNode* pHead2){
        if(pHead1 == nullptr)
            return pHead2;
        if(pHead2 == nullptr)
            return pHead1;
        
        ListNode* pHead = nullptr;
        if(pHead1->val <= pHead2->val){
            pHead = pHead1;
            pHead->next = mergeRecursively(pHead1->next, pHead2);
        }else{
            pHead = pHead2;
            pHead->next = mergeRecursively(pHead1, pHead2->next);
        }
        return pHead;
    }
    
};

面试题26. 树的子结构

// 递归遍历树
class Solution {
public:
    bool isSubStructure(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 = isSubStructure(pRoot1->left, pRoot2);
        if(!res)
            res = isSubStructure(pRoot1->right, pRoot2);
        return res;
    }
    
    bool hasSubTreeRecusively(TreeNode* pRoot1, TreeNode* pRoot2){
        if(pRoot2 == nullptr)
            return true;
        if(pRoot1 == nullptr)
            return false;
        if(pRoot1->val != pRoot2->val)
            return false;
            
        return hasSubTreeRecusively(pRoot1->left, pRoot2->left) &&
               hasSubTreeRecusively(pRoot1->right, pRoot2->right);
    }
    
};

面试题27. 二叉树的镜像

// 树的遍历
class Solution {
public:
    TreeNode* mirrorTree(TreeNode *pRoot) {
        if(pRoot != nullptr){
            TreeNode* pTemp = pRoot->left;
            pRoot->left = pRoot->right;
            pRoot->right = pTemp;
            
            mirrorTree(pRoot->left);
            mirrorTree(pRoot->right);
        }
        
        return pRoot;
    }
};

面试题28. 对称的二叉树

// 树的遍历
class Solution {
public:
    bool isSymmetric(TreeNode* pRoot)
    {   
        if(pRoot == nullptr)  //树空返回true
            return true;
        
        bool res = false;
        res = isSymmetricalRecursively(pRoot, pRoot);
        return res;
    }
    
    bool isSymmetricalRecursively(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 isSymmetricalRecursively(pRoot1->left, pRoot2->right) &&
               isSymmetricalRecursively(pRoot1->right, pRoot2->left);
    }

};

面试题29. 顺时针打印矩阵

// 分解复杂问题
class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        int rows = matrix.size();
        int cols = 0;
        if(rows != 0)
            cols = matrix[0].size();
        
        vector<int> clockwise;
        if(rows == 0 || cols == 0)
            return clockwise;
            
        for(int start = 0; start*2 < rows && start*2 < cols; start++){  // 用&&
            printMatrixClockwise(matrix, rows, cols, start, clockwise);
        }
        
        return clockwise;
    }
    
    void printMatrixClockwise(vector<vector<int> > matrix, int rows, int cols, int start, vector<int> &clockwise){
        int endX = cols - 1 - start;
        int endY = rows - 1 - start;
        
        for(int j = start; j <= endX; j++){
            clockwise.push_back(matrix[start][j]);
        }
        
        if(endY-start > 0){
            for(int i = start+1; i <= endY; i++){
                clockwise.push_back(matrix[i][endX]);
            }
        }
        
        if(endX-start > 0 && endY-start > 0){
            for(int j = endX-1; j>=start; j--){
                clockwise.push_back(matrix[endY][j]);
            }
        }
        
        if(endX-start > 0 && endY-start > 1){
            for(int i = endY-1; i > start; i--){
                clockwise.push_back(matrix[i][start]);
            }
        }
    }
    
};

面试题30. 包含min函数的栈

// 举例模拟
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 min() {
        if(!minInData.empty())
            return minInData.top();
            
        g_invalidInput = true;
        return -1;
    }

private:
    stack<int> data;
    stack<int> minInData;
};

面试题31. 栈的压入、弹出序列

// 考察堆栈,那么就用堆栈模拟咯
class Solution {
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
        if(pushed.size() == 0 && popped.size() == 0)
            return true;
        
        stack<int> s;
        //用于标识弹出序列的位置
        int popIndex = 0;
        for(int i = 0; i < pushed.size(); i++){
            s.push(pushed[i]);
            //如果栈不为空,且栈顶元素等于弹出序列
            while(!s.empty() && s.top() == popped[popIndex]){
                //出栈
                s.pop();
                //弹出序列向后一位
                popIndex++;
            }
        }
        
        return s.empty();
    }
    
};

面试题32 - I. 从上到下打印二叉树

// 层次遍历(广度优先搜索)
class Solution {
public:
    vector<int> levelOrder(TreeNode* root) {
        vector<int> res;
        if(root == nullptr)
            return res;
        
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty()){
            TreeNode* node = q.front();
            q.pop();
            res.push_back(node->val);
            
            if(node->left)
                q.push(node->left);
            if(node->right)
                q.push(node->right);
        }
        
        return res;
    }
};

面试题32 - II. 从上到下打印二叉树 II

// 层次遍历的变形
class Solution {
public:
    vector<vector<int> > levelOrder(TreeNode* pRoot) {
        vector<vector<int> > res;
        if(pRoot == nullptr)
            return res;
        
        queue<TreeNode*> q;
        q.push(pRoot);
        while(!q.empty()){
            int num = q.size();  // 关键:这个size可以保证逐层遍历二叉树
            vector<int> temp;
            while(num--){
                TreeNode* pNode = q.front(); q.pop();
                temp.push_back(pNode->val);
                if(pNode->left)
                    q.push(pNode->left);
                if(pNode->right)
                    q.push(pNode->right);
            }
            res.push_back(temp);
        }
        
        return res;
    }
    
};

面试题32 - III. 从上到下打印二叉树 III

// 其实这里完全可以不用栈,用上面“32-II”的那个方法改进一下就行。(懒得改了)
class Solution {
public:
    vector<vector<int> > levelOrder(TreeNode* pRoot) {
        vector<vector<int> > res;
        if(pRoot == nullptr)
            return res;
        
        stack<TreeNode*> mStack[2];
        vector<int> mVector;
        int flag = 0;
        mStack[flag].push(pRoot);
        
        while(!mStack[0].empty() || !mStack[1].empty()){
                
            if(flag == 0){
                while(!mStack[0].empty()){
                    TreeNode* node = mStack[0].top();
                    mVector.push_back(node->val);
                    mStack[0].pop();
                    
                    if(node->left)
                        mStack[1].push(node->left);
                    if(node->right)
                        mStack[1].push(node->right);
                }
                
                res.push_back(mVector);
                mVector.clear();
                flag = 1;
            }else if(flag == 1){
                while(!mStack[1].empty()){
                    TreeNode* node = mStack[1].top();
                    mVector.push_back(node->val);
                    mStack[1].pop();
        
                    if(node->right)
                        mStack[0].push(node->right);
                    if(node->left)
                        mStack[0].push(node->left);
                }
                
                res.push_back(mVector);
                mVector.clear();
                flag = 0;
            }  
        }
            
        return res;
    }
    
};

面试题33. 二叉搜索树的后序遍历序列

// 分治
class Solution {
public:
    bool verifyPostorder(vector<int> &sequence) {
        int length = sequence.size();
        if(length == 0)
            return true;
        
        bool res = false;
        res = VerifySquenceOfBSTRecursively(sequence, 0, length-1);
        
        return res;
    }
    
    bool VerifySquenceOfBSTRecursively(vector<int> sequence, int indexOfStart, int indexOfEnd){
        int lastNum = sequence[indexOfEnd];
        int i;
        for(i = indexOfStart; i <= indexOfEnd-1; i++){
            if(sequence[i] > lastNum)
                break;
        }
            
        int j = i;
        for(; j <= indexOfEnd-1; j++){
            if(sequence[j] < lastNum)
                return false;
        }
        
        int lengthOfLeft = i - indexOfStart;
        int lengthOfRight = indexOfEnd - i;
        bool left = false;  // 默认值(重要!)
        bool right = false;
        if(lengthOfLeft  == 0)
            left = true;
        else
            left = VerifySquenceOfBSTRecursively(sequence, indexOfStart, i-1);
        
        if(lengthOfRight == 0)
            right = true;
        else
            right = VerifySquenceOfBSTRecursively(sequence, i, indexOfEnd-1);
        
        return left && right;
    }
    
};

面试题34. 二叉树中和为某一值的路径

// 基于树的前序遍历(“回溯”)
class Solution {
public:
    vector<vector<int> > pathSum(TreeNode* root, int expectNumber) {
        if(root == nullptr)
            return res;
            
        vector<TreeNode*> path;  // 用vector实现“栈”
        int currentSum = 0;
        getPathRecursively(root, expectNumber, path, currentSum);
        
        return res;
    }
    
    void getPathRecursively(TreeNode* root, int expectNumber, vector<TreeNode*> &path, int currentSum){
        if(root != nullptr){
            currentSum += root->val;
            path.push_back(root);
            
            bool isLeaf = root->left == nullptr && root->right == nullptr;
            if(isLeaf && currentSum == expectNumber){
                vector<int> temp;
                for(auto iter = path.begin(); iter < path.end(); iter++)
                    temp.push_back((*iter)->val);
                res.push_back(temp);
            }
            
            if(root->left){
                getPathRecursively(root->left, expectNumber, path, currentSum);
            }
            if(root->right){
                getPathRecursively(root->right, expectNumber, path, currentSum);
            }
            
            currentSum -= root->val;  // 若currentSum传的不是引用,这行不加也行。(最好还是加上,更能体现“回溯”)
            path.pop_back();
        }
        
    }

private:
    vector<vector<int> > res;
};

面试题35. 复杂链表的复制

// 拆解问题,三步法:空间O(1)
class Solution {
public:
    Node* copyRandomList(Node* pHead)
    {
        if(pHead == nullptr)
            return nullptr;
        
        Node* pNode = pHead;
        while(pNode != nullptr){
            Node* pCloneNode = new Node(pNode->val, nullptr, nullptr);
            pCloneNode->next = pNode->next;
            pNode->next = pCloneNode;
            
            pNode = pCloneNode->next;
        }
        
        pNode = pHead;
        while(pNode != nullptr){
            Node* pCloneNode = pNode->next;
            if(pNode->random != nullptr)  // 在这里debug了好久
                pCloneNode->random = pNode->random->next;
            
            pNode = pCloneNode->next;
        }
        
        pNode = pHead;
        Node* pCloneHead = pHead->next;
        while(pNode != nullptr){
            Node* pCloneNode = pNode->next;
            pNode->next = pCloneNode->next;
            pNode = pNode->next;
            
            if(pNode == nullptr)
                break;
            pCloneNode->next = pNode->next;
        }
        
        return pCloneHead;
    }
    
};

// 用哈希表,空间O(n)
class Solution {
public:
    Node* copyRandomList(Node* head) {
        if (head == nullptr) {
            return head;
        }
        Node *cur = head;
        unordered_map<Node*, Node*> ump;
        //1. 遍历链表,将原节点作为key,拷贝节点作为value保存在map中
        while (cur != nullptr) {
            Node *copy = new Node(cur->val);
            ump[cur] = copy;
            cur = cur->next;
        }
        //2. 复制链表next和random指针
        cur = head;
        while (cur != nullptr) {
            ump[cur]->next = ump[cur->next];
            ump[cur]->random = ump[cur->random];
            cur = cur->next;
        }
        return ump[head];
    }
};

面试题36. 二叉搜索树与双向链表

// 注意相比原题,略有改动。   递归法
class Solution {
public:
    Node* treeToDoublyList(Node* pTreeRoot)
    {
        if(pTreeRoot == nullptr)
            return nullptr;
            
        Node* pFirstNodeInList = pTreeRoot;
        while(pFirstNodeInList->left != nullptr)
            pFirstNodeInList = pFirstNodeInList->left;
        
        Node* pLastNodeInList = nullptr;
        convertTreeRecursively(pTreeRoot, pLastNodeInList);
        
        pLastNodeInList->right = pFirstNodeInList;
        pFirstNodeInList->left = pLastNodeInList;
        return pFirstNodeInList;
    }
    
    // 传*& 更方便
    void convertTreeRecursively(Node* pRoot, Node* &pLastNodeInList){
        if(pRoot != nullptr){
            convertTreeRecursively(pRoot->left, pLastNodeInList);
            
            pRoot->left = pLastNodeInList;
            if(pLastNodeInList != nullptr){
                pLastNodeInList->right = pRoot;
            }
            pLastNodeInList = pRoot;
            
            convertTreeRecursively(pRoot->right, pLastNodeInList);
        }
    }
    
};

// 迭代法
class Solution {
public:
    Node* treeToDoublyList(Node* root) {
        if(!root) return root;
       // 排序双向链表,因此使用中序遍历
       stack<Node*> st;
       Node* pre=nullptr, *head=root;
       while(root || !st.empty()) {
           if(root) {
               st.push(root);
               cout<<root->val<<endl;
               root = root->left;
           } else {
               root = st.top();
               st.pop();
               
               // 具体处理过程,其他的都是中序遍历的模板
               if(pre == nullptr) {
                   pre = root;
                   head = root;  // 此时是最小值
               } else {
                   // 更新节点
                   pre->right = root;
                   root->left = pre;
                   pre = root;
               }

               root = root->right;
           }
       }
       // 连接收尾元素
       pre->right = head;
       head->left = pre;
       return head; 
    }
};

面试题37. 序列化二叉树

// 背过这个实现!“流”在这里特别好用
class Codec {
public:
    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {
        ostringstream out;
        serialize(root, out);
        return out.str();
    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
        istringstream in(data);
        return deserialize(in);
    }
    
    void serialize(TreeNode* root, ostringstream& out){
        if(root){
            out << root->val << ' ';
            serialize(root->left, out);
            serialize(root->right, out);
        }else{
            out << "# ";
        }
    }
    
    TreeNode* deserialize(istringstream& in){
        string val;
        in >> val;
        if(val == "#"){
            return nullptr;
        }
        TreeNode* root = new TreeNode(stoi(val));
        root->left = deserialize(in);
        root->right = deserialize(in);
        return root;
    }
};

面试题38. 字符串的排列

// 需要考虑去重(其实就是第47题“全排列 II”)
class Solution {
public:
    vector<string> permutation(string str) {
        int length = str.length();
        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);
            swap(str[mStart], str[i]);
        }
    }
    
};

面试题39. 数组中出现次数超过一半的数字

// 投票法(用快排的partition()函数的思路也行,这里懒得写了)
class Solution {
public:
    int majorityElement(vector<int> &numbers) {
        int length = numbers.size();
        if(length == 0)
            return 0;
        
        int resNum = numbers[0];
        int mCount = 1;
        for(int i=1; i < length; i++){
            if(numbers[i] == resNum)
                mCount++;
            else
                mCount--;
            
            if(mCount == 0){
                resNum = numbers[i];
                mCount = 1;
            }
        }
        
        if(verifyNum(numbers, length, resNum))
            return resNum;
        
        return 0;
    }
    
    // 这部分只是为了验证是不是非法输入
    bool verifyNum(vector<int> numbers, int length, int resNum){
        vector<int>::iterator iter;
        int sum = 0;
        for(iter = numbers.begin(); iter < numbers.end(); iter++){
            if(*iter == resNum)
                sum++;
        }
        
        if(sum > length/2)
            return true;
        else    
            return false;
    }
    
};

面试题40. 最小的k个数

// 用快排的partition()函数的思路实现   O(n)   在能修改原数组的情况下,面试优先写这个
class Solution {
public:
    vector<int> getLeastNumbers(vector<int> &input, int k) {
        vector<int> res;
        int length = input.size();
        if(length == 0 || k <= 0 || k > length)
            return res;
        
        int low = 0;
        int high = length - 1;
        while(low <= high){
            int indexOfk = mPartition(input, low, high);
            
            if(indexOfk == k-1)
                break;
            if(indexOfk < k-1)
                low = indexOfk + 1;
            else
                high = indexOfk - 1;
        }
        
        for(int i=0; i < k; i++)
            res.push_back(input[i]);
        
        return res;
    }
     
    int mPartition(vector<int> &data, int low, int high){
        int temp = data[low];
        while(low < high){
            while(low < high && data[high] >= temp)
                high--;
            data[low] = data[high];
            
            while(low < high && data[low] <= temp)
                low++;
            data[high] = data[low];
        }
        data[low] = temp;
        
        return low;
    }
};

// 用优先队列/堆 (找第K大/小数字的题目一般都可用优先队列实现)   O(nlogk),适合处理海量数据
class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& nums, int k) {
        vector<int> ans;
        if(k == 0) 
            return ans;
        
        priority_queue<int, vector<int>, less<int> > pq;
        for(auto num: nums){
            pq.push(num);
            if(pq.size() > k)
                pq.pop();
        }

        while(!pq.empty()){
            ans.push_back(pq.top());
            pq.pop();
        }
        return ans;
    }
};

面试题41. 数据流中的中位数

// 优先队列实现(注意“优先队列”用法类似stack,和普通队列不一样)
class MedianFinder {
public:
    /** initialize your data structure here. */
    MedianFinder() {
        // nothing
    }

    void addNum(int num){
        int grossSum = mMax.size() + mMin.size();
        if(grossSum & 1){
            if(!mMin.empty() && num > mMin.top()){
                int temp = mMin.top();
                mMin.pop();
                mMin.push(num);
                num = temp;
            }
            mMax.push(num);
            
        }else{
            if(!mMax.empty() && num < mMax.top()){
                int temp = mMax.top();
                mMax.pop();
                mMax.push(num);
                num = temp;
            }
            mMin.push(num);
        }

    }

    double findMedian(){
        int grossSum = mMax.size() + mMin.size();
        if(grossSum & 1)
            return (double)(mMin.top());
        else
            return (double)(mMax.top() + mMin.top()) / 2;
    }

private:
    // 注意这里less默认最大堆,而greater是最小堆。
    priority_queue<int, vector<int>, less<int> > mMax;     // 大根堆
    priority_queue<int, vector<int>, greater<int> > mMin;  // 小根堆
};

面试题42. 连续子数组的最大和

// 动态规划(压缩空间复杂度到O(1))
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int length = nums.size();
        if(length == 0)
            return 0;

        int res = nums[0];
        int temp = nums[0];
        for(int i = 1; i < length; i++){
            temp = max(temp + nums[i], nums[i]);
            res = max(res, temp);
        }

        return res;
    }
};

面试题43. 1~n整数中1出现的次数

// 递归求解,分成三类问题考虑:考虑最高位1出现的个数、考虑非最高位1出现的个数、考虑除去最高位外的数字中1出现的个数。
class Solution {
public:
    int countDigitOne(int n){
        if(n <= 0)
            return 0;
        
        string str = to_string(n);
        return mySolutionRecursively(str);
    }
    
    int mySolutionRecursively(string str){  // eg: 21345
        int length = str.size();
        if(length == 1){   // 递归出口
            if(str[0] == '0')
                return 0;
            else
                return 1;
        }
        
        int firstNum = str[0] - '0';
        int amountOfFirstDigit = 0;  // 需指定默认值为 0 
        if(firstNum == 1)  // 当firstNum == 0时,此时amountOfFirstDigit == 0
            amountOfFirstDigit = stoi(str.substr(1)) + 1;
        else if(firstNum > 1)
            amountOfFirstDigit = (int)pow(10, length-1);
        
        int amountOfOtherDigit = 0;  // 需指定默认值为 0 
        amountOfOtherDigit = firstNum * (length-1) * (int)pow(10, length-2);
        
        return amountOfFirstDigit + amountOfOtherDigit + mySolutionRecursively(str.substr(1));
    }
    
};

面试题44. 数字序列中某一位的数字

// 本质就是个数学题,举例仔细分析找规律即可。
class Solution {
public:
    int findNthDigit(int index) {
        int digits = 1;
        while(true){
            long long nums = cntOfNum(digits);  // 防止溢出
            if(index < nums * digits)
                return findNthDigit(index, digits);
                
            index -= nums * digits;
            digits++;
        }
        
        return -1;  // 非法输入
    }
    
    int cntOfNum(int digits){
        if(digits == 1)
            return 10;
        else
            return 9 * (int)pow(10, digits-1);
    }
    
    int findNthDigit(int index, int digits){
        int number = beginNumber(digits) + index / digits;
        int indexFromRight = digits - index % digits;
        while(--indexFromRight){
            number /= 10;
        }
        return number % 10;
    }
    
    int beginNumber(int digits){
        return digits == 1? 0: (int)pow(10, digits-1);
    }
};

面试题45. 把数组排成最小的数

// 自定义“排序规则”
class Solution {
public:
    string minNumber(vector<int>& nums) {
        if(nums.size() == 0)
            return "";
        
        sort(nums.begin(), nums.end(), cmp);
        
        string ret = "";
        for(auto elem: nums)
            ret += to_string(elem);
        
        return ret;  // 题目规定,最后结果不需要去掉前导0,不同于第179题
    }
    
    static bool cmp(int a, int b){
        string str_a = to_string(a) + to_string(b);
        string str_b = to_string(b) + to_string(a);
        return str_a < str_b;
    }
    
};

面试题46. 把数字翻译成字符串

// 动态规划,自底向上,消除重复的子问题
class Solution {
public:
    int translateNum(int num) {
        string str = to_string(num); 
        int length = str.size();
        if(length == 0)
            return 0;
        
        int dp[length] = {0};
        for(int i = length-1; i >= 0; i--){
            int temp = 0;
            if(i == length-1){ 
                dp[i] = ++temp;
            }
            
            if(i <= length-2){
                temp += dp[i+1];
                int twoDigitsNum = 10 * (str[i] - '0') + (str[i+1] - '0');
                if(twoDigitsNum >= 10 && twoDigitsNum <= 25){
                    if(i == length-2)
                        temp += 1;
                    else
                        temp += dp[i+2];
                }
            }
            
            dp[i] = temp;
        }
        
        return dp[0];
    }
    
};

面试题47. 礼物的最大价值

// 常规动态规划
class Solution {
public:
    int maxValue(vector<vector<int>>& grid) {
        int m = grid.size();
        if(m == 0)
            return 0;
        int n = grid[0].size();
        
        vector<vector<int> > dp(m + 1, vector<int>(n + 1, 0));  // 小技巧
        for(int i = 1; i <= m; i++){
            for(int j = 1; j <= n; j++){
                if(i == 1 && j == 1){
                    dp[i][j] = grid[0][0];
                    continue;
                }
                
                dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + grid[i-1][j-1];
            }
        }
        
        return dp[m][n];
    }
};

// 由状态转移方程可以发现,我们可以进行“状态空间的压缩”
class Solution {
public:
    int maxValue(vector<vector<int>>& grid) {
        int m = grid.size();
        if(m == 0)
            return 0;
        int n = grid[0].size();
        
        vector<int> dp(n, 0);
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                int left = 0;  // 默认值(重要)
                int up = 0;
                if(i > 0)
                    up = dp[j];
                if(j > 0)
                    left = dp[j-1];
                
                dp[j] = max(left, up) + grid[i][j];
            }
        }
        
        return dp[n-1];
    }
};

面试题48. 最长不含重复字符的子字符串

// dp解法
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int length = s.length();
        if(length <= 1)
            return length;
        
        unordered_map<char, int> uMap;  // 其实这算O(1)的存储空间,因为只有26个字母(用数组模拟简单的哈希表也可以)
        int curMax = 1;
        uMap[s[0]] = 0;
        int res = 0;
        for(int i = 1; i < length; i++){
            if(uMap.count(s[i]) == 0 || i - uMap[s[i]] > curMax){
                curMax++;
            }else{
                curMax = i - uMap[s[i]];
            }
            
            res = max(res, curMax);
            uMap[s[i]] = i;
        }
        
        return res;
    }
};

// 滑动窗口解法
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int left = 0, right = 0;
        unordered_map<char, int> window;
        int res = 0; // 记录最长长度
        while (right < s.size()) {
            char c1 = s[right];
            window[c1]++;
            // 如果 window 中出现重复字符
            // 开始移动 left 缩小窗口
            while (window[c1] > 1) {
                char c2 = s[left];
                window[c2]--;
                left++;
            }
            res = max(res, right - left + 1);
            right++;
        }

        return res;
    }

};

面试题49. 丑数

// 动态规划 + 三指针(用空间换时间)
class Solution {
public:
    int nthUglyNumber(int index) {
        if(index <= 0)
            return 0;
        int res[index+1];  // 这里不能直接初始化
        res[1] = 1;  // 规定 1 是丑数。
        
        int indexOf2 = 1;
        int indexOf3 = 1;
        int indexOf5 = 1;
        for(int i = 2; i <= index; i++){
            while(res[indexOf2]*2 <= res[i-1])
                indexOf2++;
            while(res[indexOf3]*3 <= res[i-1])
                indexOf3++;
            while(res[indexOf5]*5 <= res[i-1])
                indexOf5++;
                
            res[i] = mMin(res[indexOf2]*2, res[indexOf3]*3, res[indexOf5]*5);
        }
        
        return res[index];
    }
    
    int mMin(int a, int b, int c){
        int temp = a < b? a: b;
        return temp < c? temp: c;
    }
};

面试题50. 第一个只出现一次的字符

// 用数组实现简单的哈希表(直接用unordered_map也行)
class Solution {
public:
    char firstUniqChar(string str) {
        int length = str.length();
        if(length <= 0)
            return ' ';   // 其实还需要设置g_invalidInput = True

        int pos[256];  // 空间复杂度其实是O(1)
        memset(pos, -1, sizeof(pos));  // 这里注意
        for(int i = 0; i < length; i++){
            if(pos[str[i]] == -1)
                pos[str[i]] = i;
            else if(pos[str[i]] >= 0)
                pos[str[i]] = -2;
        }

        for(int i=0; i < length; i++){
            if(pos[str[i]] >= 0)
                return str[i];
        }

        return ' ';
    }
};

// 进阶版题目见《剑指offer》

面试题51. 数组中的逆序对

// 基于“归并排序”
typedef long long LL;
class Solution {
public:
    int reversePairs(vector<int> mData) {
        int length = mData.size();
        if(length == 0)
            return 0;
        
        vector<int> mCopy(mData);  // 拷贝
        
        return amountOfInversePairs(mData, mCopy, 0, length-1);
    }
    
    LL amountOfInversePairs(vector<int> &mData, vector<int> &mCopy, int mStart, int mEnd){
        if(mStart == mEnd){
            mCopy[mStart] = mData[mStart];
            return 0;
        }
        
        LL halfLength = (mEnd - mStart) / 2;
        LL left = amountOfInversePairs(mCopy, mData, mStart, mStart + halfLength);  // 重要!(参数顺序)
        LL right = amountOfInversePairs(mCopy, mData, mStart + halfLength + 1, mEnd);
        
        LL i = mStart + halfLength;
        LL j = mEnd;
        LL indexOfmCopy = mEnd;
        LL mCount = 0;
        while(i >= mStart && j >= mStart + halfLength + 1){
            if(mData[i] > mData[j]){
                mCount += j-mStart-halfLength;
                mCopy[indexOfmCopy--] = mData[i--];
            }else{
                mCopy[indexOfmCopy--] = mData[j--];
            }
        }
        
        while(i >= mStart)
            mCopy[indexOfmCopy--] = mData[i--];
        while(j >= mStart + halfLength + 1)
            mCopy[indexOfmCopy--] = mData[j--];
        
        return left + right + mCount;
    }
    
};

面试题52. 两个链表的第一个公共节点

// 先计算两链表长度 + 双指针
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 *headA, ListNode *headB) {
        if (headA == nullptr || headB == nullptr) 
            return nullptr;
        ListNode *pA = headA, *pB = headB;
        while (pA != pB) {
            pA = pA == nullptr ? headB : pA->next;
            pB = pB == nullptr ? headA : pB->next;
        }
        
        return pA;
    }
};

面试题53 - I. 在排序数组中查找数字 I

// 二分
class Solution {
public:
    int search(vector<int> data ,int k) {
        int length = data.size();
        if(length == 0)
            return 0;
        
        int indexOfFirstK = getIndexOfFirstK(data, k);
        int indexOfLastK = getIndexOfLastK(data, k);
        if(indexOfFirstK >= 0 && indexOfLastK >= 0){
            return indexOfLastK - indexOfFirstK + 1;
        }
        
        return 0;
    }
    
    
    int getIndexOfFirstK(vector<int> data ,int k){
        int low = 0;
        int high = data.size() - 1;
        while(low <= high){
            int mid = (low + high) >> 1;
            
            if(data[mid] == k){
                if(mid == 0 || data[mid-1] != k)
                    return mid;
                high = mid - 1;
            }else if(data[mid] < k){
                low = mid + 1;
            }else{
                high = mid - 1;
            }
        }
        
        return -1;
    }
    
    int getIndexOfLastK(vector<int> data ,int k){
        int low = 0;
        int high = data.size() - 1;
        while(low <= high){
            int mid = (low + high) >> 1;
            
            if(data[mid] == k){
                if(mid == data.size()-1 || data[mid+1] != k)
                    return mid;
                low = mid + 1;
            }else if(data[mid] < k){
                low = mid + 1;
            }else{
                high = mid - 1;
            }
        }
        
        return -1;
    }
        
};

面试题53 - II. 0~n-1中缺失的数字

// 二分,找数组中第一个值和下标不相等的元素  时间复杂度为O(nlogn)
class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int length = nums.size();
        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(left != nums[left])
            return left;
        else
            return length;
    }
    
};

// 可以利用求和公式,求和后S1 - S2。但是这样可能会有溢出问题,而且时间复杂度为O(n)

面试题54. 二叉搜索树的第k大节点

// 注意是第K大,所以要用改进版的中序遍历  R->D->L
class Solution {
public:
    int kthLargest(TreeNode* root, int k) {
        if(root == nullptr || k <= 0)
            return 0;
            
        return KthNode(root, k)->val;
    }
    
    TreeNode* KthNode(TreeNode* pRoot, int &k)  // 这里一定要用 引用!!
    {
        if(pRoot == nullptr || k <= 0)
            return nullptr;
        
        TreeNode* pAns = nullptr;
        pAns = KthNode(pRoot->right, k);
        
        if(pAns == nullptr){
            k--;
            if(k == 0)
                pAns = pRoot;
        }
        
        if(pAns == nullptr)
            pAns = KthNode(pRoot->left, k);
        
        return pAns;
    }

};

// 另解
class Solution {
public:
    int n=0;
    int res;
    int kthLargest(TreeNode* root, int k) {
        //递归
        dfs(root, k);
        return res;
    }

    void dfs(TreeNode* root, int k){
          if(!root)
            return ;
          
          dfs(root->right, k);
          n++;
          if(n == k) 
            res = root->val;
          dfs(root->left, k);
    }
};

// 当然了,改成非递归版的“中序遍历”也是可以的,这里就懒得改了。

面试题55 - I. 二叉树的深度

// 要明确,求树的深度是基于树的“后序遍历”改进而来的。(后序遍历一般都可减少很多重复遍历)
class Solution {
public:
    int maxDepth(TreeNode* pRoot)
    {
        if(pRoot == nullptr)
            return 0;
        
        int left = maxDepth(pRoot->left);
        int right = maxDepth(pRoot->right);
        return max(left, right) + 1;
    }
};

面试题55 - II. 平衡二叉树

// 这是基于“前序遍历”的方式,会有很多重复遍历。
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 max(left, right) + 1;
    }
    
private:
    bool res;
};

面试题56 - I. 数组中数字出现的次数

// 位运算(异或)
class Solution {
public:
    vector<int> singleNumbers(vector<int> &nums) {
        int length = nums.size();
        vector<int> res;
        if(length == 0)
            return res;
        
        int temp = 0;
        for(int i = 0; i < length; i++)
            temp ^= nums[i];
        
        if(temp == 0)
            return res;
        
        int posOfLast1 = getPosOfLast1(temp);
        int num1 = 0, num2 = 0;
        for(int i = 0; i < length; i++){
            if(nums[i] & (1<<posOfLast1))
                num1 ^= nums[i];
            else
                num2 ^= nums[i];
        }
        
        res.push_back(num1), res.push_back(num2);
        return res;
    }
    
    int getPosOfLast1(int a){
        int posOfLast1 = 0;
        int bitMask = 1;
        while((a&bitMask) == 0){  // 这里要加括号啊,防止运算符优先级出错。
            bitMask = bitMask << 1;
            posOfLast1++;
        }
        
        return posOfLast1;
    }
    
};

面试题56 - II. 数组中数字出现的次数 II

// 位运算(求所有数字二进制表示的每一位之和)
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int length = nums.size();
        int bitSum[32] = {0};
        
        for(int i = 0; i < length; i++){
            unsigned int bitMask = 1;
            for(int j = 31; j >= 0; j--){
                if((nums[i] & bitMask) != 0)
                    bitSum[j]++;
                bitMask <<= 1;
            }
        }
        
        int res = 0;
        for(int i = 0; i < 32; i++){
            res <<= 1;
            res += bitSum[i] % 3;
        }
        
        return res;
    }
};

面试题57 - I. 和为s的两个数字

// 双指针
class Solution {
public:
    vector<int> twoSum(vector<int> data, int sum) {
        int length = data.size();
        vector<int> res;
        if(length == 0)
            return res;
            
        int low = 0;
        int high = length - 1;
        while(low < high){
            if(data[low] + data[high] == sum){
                res.push_back(data[low]);
                res.push_back(data[high]);
                return res;
            }
            
            if(data[low] + data[high] > sum)
                high--;
            else
                low++;
        }
        
        return res;
    }
};

面试题57 - II. 和为s的连续正数序列

// 双指针
class Solution {
public:
    vector<vector<int> > findContinuousSequence(int k) {
        vector<vector<int> > res;
        if(k <= 2)
            return res;
        
        int low = 1;
        int high = 2;
        int midVal = (k + 1) / 2;
        int sum = low + high;
        while(low <= midVal){
            if(sum == k){
                vector<int> temp;
                for(int i = low; i <= high; i++)
                    temp.push_back(i);
                res.push_back(temp);
                
                sum -= low;
                low++;
                continue;
            }
            
            if(sum > k){
                sum -= low;
                low++;
            }else{
                high++;
                sum += high;
            }
        }
        
        return res;
    }
    
};

面试题58 - I. 翻转单词顺序

// 翻转两次(此题对比原题有改动,就是得去除多余空格)
class Solution {
public:
    string reverseWords(string str) {
        string res = str;
        int length = str.length();
        if(length == 0)
            return res;

        mReverse(res, 0, length-1);

        int start = 0;
        for (int i = 0; i < res.length(); i++) {
            if (res[i] == ' ') {
                mReverse(res, start, i - 1);
                start = i + 1;//更新初始位置,变为‘ ’后面的一个字符
            }
        }      
        mReverse(res, start, res.length()-1);//对最后一个单词的反转
        
        // 去前后空格
        int left = 0;
        while(left < res.size() && res[left] == ' ') left++;
        int right = res.size() - 1;
        while(right >= 0 && res[right] == ' ') right--;
        res = res.substr(left, right-left+1);
        // 去中间多余空格
        int tail = 0;
        for(int i = 0; i < res.size(); i++){
            if(i < res.size()-1 && res[i] == ' ' && res[i+1] == ' ')
                continue;
            res[tail++] = res[i];
        }
        
        return res.substr(0, tail);
    }

    void mReverse(string &str, int low, int high){
        while(low < high){
            swap(str[low], str[high]);
            low++;
            high--;
        }
    }

};

面试题58 - II. 左旋转字符串

// 翻转两次
class Solution {
public:
    string reverseLeftWords(string str, int n) {
        int length = str.size();
        n = n % length;
        mReverse(str, 0, length - 1);
        mReverse(str, 0, length - 1 - n);
        mReverse(str, length - n, length - 1);
        
        return str;
    }
        
    void mReverse(string &str, int low, int high){
        while(low < high){
            swap(str[low], str[high]);
            low++;
            high--;
        }
    }
};

面试题59 - I. 滑动窗口的最大值

// 用deque实现(一个滑动窗口可以看成一个队列)
class Solution {
public:
    vector<int> maxSlidingWindow(const vector<int>& data, unsigned int sizeOfWindows){
        vector<int> res;
        int length = data.size();
        if(length == 0 || sizeOfWindows <= 0 || sizeOfWindows > length)
            return res;
        
        // 只把有可能成为滑动窗口最大值的数值存入一个两端开口的队列
        deque<int> maxQue;
        for(int i = 0; i < sizeOfWindows; i++){
            while(!maxQue.empty() && data[i] >= data[maxQue.back()])  // 这里用严格大于号也行
                maxQue.pop_back();
            maxQue.push_back(i);
        }
        
        res.push_back(data[maxQue.front()]);
        for(int i = sizeOfWindows; i < length; i++){
            while(!maxQue.empty() && i-maxQue.front() >= sizeOfWindows)
                maxQue.pop_front();
            
            while(!maxQue.empty() && data[i] >= data[maxQue.back()])  // 这里用严格大于号也行
                maxQue.pop_back();
            maxQue.push_back(i);
            
            res.push_back(data[maxQue.front()]);
        }
        
        return res;
    }
    
};

面试题59 - II. 队列的最大值

// 滑动窗口最大值的变形,《剑指offer》上的解法可以,但下面的解法更简洁。(维护一个递减的双端队列deque)
class MaxQueue {
public:
    MaxQueue() {
        // nothing
    }
    
    int max_value() {
        if(dq.empty())
            return -1;
        return dq.front();
    }
    
    void push_back(int value) {
        q.push(value);
        while(!dq.empty() && value > dq.back())  // 这里用大于等于号也行,同上。
            dq.pop_back();
        dq.push_back(value);
    }
    
    int pop_front() {
        if(q.empty())
            return -1;
        int res = q.front();
        q.pop();
        if(!dq.empty() && res == dq.front())
            dq.pop_front();
        return res;
    }

private:
    queue<int> q;
    deque<int> dq;
};

面试题60. n个骰子的点数

// 《剑指offer》上的第一个解法:dfs(超时)
int g_maxValue = 6;  // 便于扩展
class Solution {
public:
    vector<double> twoSum(int n) {
        int maxSum = g_maxValue * n;
        vector<int> cnts(maxSum - n + 1, 0);
        
        cntOfDifferentSum(n, 0, 0, cnts);  // dfs
        
        vector<double> res(maxSum - n + 1);
        int total = (int)pow(g_maxValue, n);
        for(int i = 0; i < cnts.size(); i++){
            res[i] = (double)cnts[i] / total;
        }
        
        return res;
    }
    
    void cntOfDifferentSum(int n, int curSum, int index, vector<int> &cnts){
        if(index == n){
            cnts[curSum-n]++;
            return;
        }
        
        for(int i = 1; i <= g_maxValue; i++){    // 遍历选择列表
            cntOfDifferentSum(n, curSum + i, index + 1, cnts);  
        }
    }
};

// 动态规划
int g_maxValue = 6;
class Solution {
public:
    vector<double> twoSum(int n) {
        int maxSum = g_maxValue * n;
        vector<int> cnts(maxSum - n + 1, 0);
        
        vector<vector<int> > dp(2, vector<int>(n * g_maxValue + 1, 0));
        int flag = 0;  // 用于两个数组之间的转换
        // 当只有一个骰子时
        for(int i = 1; i <= g_maxValue; i++){
            dp[flag][i] = 1;
        }
        
        // 当新加上第k个骰子时
        for(int k = 2; k <= n; k++){
            // 先清零(k个骰子的和只能为k到6k)
            for(int i = 0; i < k; i++)
                dp[1-flag][i] = 0;
            
            for(int i = k; i <= g_maxValue * k; i++){
                dp[1-flag][i] = 0;  // 先清零
                for(int j = 1; j <= g_maxValue && j <= i; j++)
                    dp[1-flag][i] += dp[flag][i-j];
            }
            
            flag = 1 - flag;  // flag用于在两个数组之间转换
        }
        
        vector<double> res(maxSum - n + 1);
        int total = (int)pow(g_maxValue, n);
        for(int i = n; i <= g_maxValue * n; i++){
            res[i-n] = (double)dp[flag][i] / total;
        }
        
        return res;
    }
    
};

面试题61. 扑克牌中的顺子

// 排序部分可以用“计数排序”优化时间复杂度为O(n)。 当n足够大时才有意义
class Solution {
public:
    bool isStraight(vector<int> data) {
        int length = data.size();
        if(length == 0)
            return false;
        
        int numOfKing = 0;
        for(int i = 0; i < length; i++){
            if(data[i] == 0)
                numOfKing++;
        }
        
        sort(data.begin(), data.end());
        
        int mBegin = 0;
        for(; mBegin < length; mBegin++){
            if(data[mBegin] != 0)
                break;
        }
        if(mBegin == length)
            return true;
        
        int numOfGap = 0;
        int mEnd = mBegin + 1;
        while(mEnd <= length-1){
            if(data[mBegin] == data[mEnd])  // 注意有对子就不可能是顺子
                return false;
            numOfGap += (data[mEnd] - data[mBegin] - 1);
            mBegin++;
            mEnd++;
        }
        
        if(numOfGap <= numOfKing)
            return true;
        else
            return false;
    }
    
};

面试题62. 圆圈中最后剩下的数字

// 最直接的想法是模拟,但是此处会报超时错误(后台数据问题,不用纠结,直接看数学解法即可)
class Solution {
public:
    int lastRemaining(int n, int m){
        if(n <= 0 || m <= 0)
            return -1;
        
        list<int> mList;
        for(int i = 0; i < n; i++)
            mList.push_back(i);
        
        list<int>::iterator current = mList.begin();
        while(mList.size() != 1){
            for(int i = 0; i < m-1; i++){
                current++;
                if(current == mList.end()){
                    current = mList.begin();
                }
            }
            
            list<int>::iterator next = ++current;
            if(next == mList.end())
                next = mList.begin();

            current--;
            mList.erase(current);
            current = next;
        }
        
        return *mList.begin();
    }
    
};

// 数学方法(具体推导请百度,参考:https://leetcode-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/solution/huan-ge-jiao-du-ju-li-jie-jue-yue-se-fu-huan-by-as/)
class Solution {
public:
    int lastRemaining(int n, int m){
        if(n <= 0 || m <= 0)
            return -1;
        
        int last = 0;
        for(int i = 2; i <= n; i++)
            last = (last+m) % i;
        return last;
    }
    
};

面试题63. 股票的最大利润

// 贪心
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int maxDiff = 0;
        int length = prices.size();
        if(length < 2)
            return maxDiff;
        
        if(prices[1] >= prices[0])
            maxDiff = prices[1] - prices[0];
        int preMin = prices[0]; 
        for(int i = 2; i < length; i++){
            preMin = min(preMin, prices[i-1]);
            maxDiff = max(maxDiff, prices[i] - preMin);
        }
        
        return maxDiff;
    }
};

// 动态规划
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int length = prices.size();
        if(length <= 1)
            return 0;

        int dp_i_0 = 0, dp_i_1 = -prices[0];
        for (int i = 1; i < length; i++) {
            dp_i_0 = max(dp_i_0, dp_i_1 + prices[i]);
            dp_i_1 = max(dp_i_1, -prices[i]);    // 注意这里和股票第二题的状态转移方程不一样(理解:这里其实变相保证了只能买卖一次) 
        }
        
        return dp_i_0;
    }
};

面试题64. 求1+2+…+n

// 利用构造函数求解
class mAdd{
public:
    mAdd(){
        i++;
        sum += i;
    }

    static void mReset(){  // 都要有!
        i = 0;
        sum = 0;
    }

    static int getSum(){
        return sum;
    }

private:
    static int i;
    static int sum;
};

int mAdd::i = 0;  // 都要有!
int mAdd::sum = 0;

class Solution {
public:
    int sumNums(int n) {
        mAdd::mReset();

        mAdd a[n];

        return mAdd::getSum();
    }
};

// 利用逻辑运算的截断效应求解
class Solution {
public:
    int sumNums(int n) {
        n && (n += sumNums(n-1));
        return n;    // 短路特性:若&&前面为假,后面不计算
    }
};

面试题65. 不用加减乘除做加法

// 位运算
class Solution {
public:
    int add(int num1, int num2){
        do{
            int temp = num1 ^ num2;
            int carry = (unsigned int)(num1 & num2) << 1;  // 移位运算,unsigned int必加
            num1 = temp;
            num2 = carry;
        }while(num2);

        return num1;
    }
};

面试题66. 构建乘积数组

// 用数组保存左积和右积
class Solution {
public:
    vector<int> constructArr(const vector<int>& A) {
        vector<int> B;
        int length = A.size();
        if(length == 0)
            return B;
        
        int curProduct = 1;
        for(int i = 0; i < length; i++){
            if(i == 0){
                B.push_back(curProduct);
                curProduct *= A[i];
                continue;
            }
            
            B.push_back(curProduct);
            curProduct *= A[i];
        }
        
        curProduct = 1;
        for(int i = length-1; i >= 0; i--){
            if(i == length-1){
                curProduct *= A[i];
                continue;
            }
            
            B[i] *= curProduct;
            curProduct *= A[i];
        }
        
        return B;
    }
    
};

面试题67. 把字符串转换成整数

// 主要是处理溢出
// 解法一
class Solution {
public:
    int strToInt(string str) {
        int res = 0;
        int i = 0;
        int flag = 1;
        // 1. 检查空格
        while (str[i] == ' ') { i++; }
        // 2. 检查符号
        if (str[i] == '-') { flag = -1; }
        if (str[i] == '+' || str[i] == '-') { i++; }
        // 3. 计算数字
        while (i < str.size() && isdigit(str[i])) {
            int r = str[i] - '0';
            // ------ 4. 处理溢出,这是关键步骤 ------
            if (res > INT_MAX / 10 || (res == INT_MAX / 10 && r > 7)) { 
                return flag > 0 ? INT_MAX : INT_MIN;
            }
            // ------------------------------------
            res = res * 10 + r;
            i++;
        }
        return flag > 0 ? res : -res;
    }
};

// 解法二(思路同上)
class Solution {
public:
    int strToInt(string str) {
        int res = 0;
        int i = 0;
        int flag = 1;
        // 1. 检查空格
        while (str[i] == ' ') { i++; }
        // 2. 检查符号
        if (str[i] == '-') { flag = -1; }
        if (str[i] == '+' || str[i] == '-') { i++; }
        // 3. 计算数字
        while (i < str.size() && isdigit(str[i])) {
            int r = str[i] - '0';
            // ------ 4. 处理溢出,这是关键步骤 ------
            if (flag == 1 && (res > INT_MAX / 10 || (res == INT_MAX / 10 && r >= 7))) { 
                return INT_MAX;
            }
            if (flag == -1 && (res > INT_MAX / 10 || (res == INT_MAX / 10 && r >= 8))) { 
                return INT_MIN;
            }
            // ------------------------------------
            res = res * 10 + r;
            i++;
        }
        return flag > 0 ? res : -res;
    }
};

面试题68 - I. 二叉搜索树的最近公共祖先

// 利用二叉搜索树的性质
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root == nullptr || p == nullptr || q == nullptr)
            return nullptr;
            
        if((root->val >= p->val && root->val <= q->val) || (root->val >= q->val && root->val <= p->val))
            return root;
        else if(root->val > p->val && root->val > q->val)
            return lowestCommonAncestor(root->left, p, q);
        else
            return lowestCommonAncestor(root->right, p, q);
        
    }
    
};

面试题68 - II. 二叉树的最近公共祖先

// 基于树的“后序遍历”(会少很多重复遍历)
class Solution {
public:
	// 此函数返回的就是p和q的最近公共祖先
    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;
    }
    
};
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值