剑指offer

剑指 Offer 03:数组中重复的数字

题目:找出数组中重复的数字

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        int i = 0;
        while(i < nums.size()) {
            if(nums[i] == i) {
                i++;
                continue;
            }
            if(nums[nums[i]] == nums[i])
                return nums[i];
            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 rowMax = matrix.size();
        int colMax = matrix[0].size();
        int row = 0;
        int col = colMax - 1;
        while(row < rowMax && col >= 0)
        {
            if(matrix[row][col] == target)
            {
                return true;
            }
            else if(matrix[row][col] < target)
            {
                row++;
            }
            else
            {
                col--;
            }
        }
        return false;
    }
};

剑指 Offer 05:替换空格

题目:请实现一个函数,把字符串中的每个空格替换成“%20”。例如,输入“We are happy”,则输出“We%20are%20happy”

class Solution 
{
public:
    string replaceSpace(string s) //字符数组
    {     
        string result;   //存储结果
        
        for(auto &c : s)
        {   
            if(c == ' ') //遍历原字符串
            {
                result.push_back('%');
                result.push_back('2');
                result.push_back('0');
            }
            else
            {
                result.push_back(c);
            }
        }
        return result;
    }
};

剑指 Offer 06.:从尾到头打印链表

题目:输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

/*反转链表*/
class Solution {
public:
    vector<int> reversePrint(ListNode* head) {
        vector<int> result;
        // 空链表
        if (head == nullptr) return result;
        // 反转链表
        ListNode *curr = head;
        ListNode *pre = nullptr;
        while(curr) 
        {
            ListNode *next = curr->next;
            curr->next = pre;
            pre = curr;
            curr = next;
        }
        // 取出链表中的值
        while(pre)
        {
            result.push_back(pre->val);
            pre = pre->next;
        }
        return result;
    }
};

剑指 Offer 07.:重建二叉树

题目:输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */

class Solution {
public:
    TreeNode* Traversal(vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& preorder, int preorderBegin, int preorderEnd)
    {
        if(preorderBegin == preorderEnd) return nullptr;
        TreeNode* root = new TreeNode(preorder[preorderBegin]);
        if(preorderBegin - preorderEnd == 1) return root;

        //寻找分割点
        int delimiterIndex = 0;
        for(delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++)
        {
            if(inorder[delimiterIndex] == preorder[preorderBegin]) break;
        }

        //切割中序数组
        int leftInorderBegin = inorderBegin;
        int leftInorderEnd = delimiterIndex;
        int rightInorderBegin = delimiterIndex + 1;
        int rightInorderEnd = inorderEnd;

        //切割前序数组
        int leftPreorderBegin = preorderBegin + 1;
        int leftPreorderEnd = preorderBegin + 1 + (delimiterIndex - inorderBegin);
        int rightPreorderBegin = preorderBegin + 1 + (delimiterIndex - inorderBegin);
        int rightPreorderEnd = preorderEnd;

        root->left = Traversal(inorder, leftInorderBegin, leftInorderEnd, preorder, leftPreorderBegin, leftPreorderEnd);
        root->right = Traversal(inorder, rightInorderBegin, rightInorderEnd, preorder, rightPreorderBegin, rightPreorderEnd);
        return root;
    } 

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) 
    {
        if(preorder.size() == 0 || inorder.size() == 0) return nullptr;
        return Traversal(inorder, 0, inorder.size(), preorder, 0, preorder.size());
    }
};

剑指 Offer 09.:用两个栈实现队列

题目:用两个栈实现一个队列。队列的声明如下,请实现它的两个函数appendTail和deleteHead,分别完成在队列尾部插入节点和在队列头部删除节点的功能。

class CQueue {
public:
    stack<int> inStack;
    stack<int> outStack;
    CQueue() {

    }
    
    void appendTail(int value) {
        inStack.push(value);
    }
    
    int deleteHead() {
        if(outStack.empty())
        {
            if(inStack.empty()) return -1;
            while(!inStack.empty())
            {
                outStack.push(inStack.top());
                inStack.pop();
            }
        }
        int value = outStack.top();
        outStack.pop();
        return value;
    }
};

剑指 Offer 10.:斐波那契数列 I

题目:求斐波那契数列的第n项。写一个函数,输入n,求斐波那契数列的第n项,该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:

F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1

给定 n ,请计算 F(n) 。
答案需要取模 1e9+7(1000000007) ,如计算初始结果为:1000000008,请返回 1。

class Solution {
public:
    int fib(int n) {
        if(n <= 1) return n;
        int dp[2];
        dp[0] = 0;
        dp[1] = 1;
        for(int i = 2; i <= n; i++)
        {
            int sum = (dp[0] + dp[1]) % 1000000007;
            dp[0] = dp[1];
            dp[1] = sum;
        }
        return dp[1];
    }
};

剑指 Offer 10.:青蛙跳台阶问题 II

题目:一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个n级的台阶总共有多少种跳法。

class Solution {
public:
    int numWays(int n) 
    {
        if(n <= 1) return 1;
        vector<int> dp(3, 0);
        dp[1] = 1;
        dp[2] = 2;
        for(int i = 3; i <= n; i++)
        {
            int sum = (dp[1] + dp[2])  % 1000000007;
            dp[1] = dp[2];
            dp[2] = sum;
        }
        return dp[2];
    }
};

剑指 Offer 11.:旋转数组的最小数字

题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。

class Solution {
public:
    int minArray(vector<int>& numbers) {
        int size = numbers.size();
        int low = 0;
        int high = size - 1;
        while(low < high)
        {
            int pivot = low + (high - low) / 2;
            if(numbers[pivot] < numbers[high])
            {
                high = pivot;
            }
            else if(numbers[pivot] > numbers[high])
            {
                low = pivot + 1;
            }
            else
            {
                high -= 1;
            }
        }
        return numbers[low];
    }
};

剑指 Offer 12.:矩阵中的路径

题目:请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。

class Solution {
private:
    int row;
    int col;

public:
    bool BackTracking(vector<vector<char>>& board, int i, int j, string word, int k)
    {
        if(i < 0 || i >= row || j < 0 || j >= col || board[i][j] != word[k]) return false;
        if(k == word.size() - 1) return true;
        board[i][j] = '\0';
        bool res = BackTracking(board, i - 1, j, word, k + 1) || BackTracking(board, i + 1, j, word, k + 1)
                    || BackTracking(board, i, j - 1, word,  k + 1) || BackTracking(board, i, j + 1, word, k + 1);
        board[i][j] = word[k];
        return res;
    }

    bool exist(vector<vector<char>>& board, string word) {
        row = board.size();
        col = board[0].size();
        for(int i = 0; i < row; i++)
        {
            for(int j = 0; j < col; j++)
            {
                if(BackTracking(board, i, j, word, 0)) return true;
            }
        }
        return false;
    }
};

剑指 Offer 13.:机器人的运动范围

题目:地上有一个m行n列的方格。一个机器人从坐标(0,0)的格子开始移动,它每次可以向左、右、上、下移动一格,但不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7=18。但它不能进入方格(35,38),因为3+5+3+8=19。请问该机器人能够到达多少个格子?

class Solution {
public:
    int movingCount(int m, int n, int k)
    {
        vector<vector<bool>> visited(m, vector<bool>(n, 0));
        return dfs(0, 0, 0, 0, visited, m, n, k);
    }
private:
    int dfs(int i, int j, int si, int sj, vector<vector<bool>> &visited, int m, int n, int k)
    {
        if(i >= m || j >= n || k < si + sj || visited[i][j]) return 0;
        visited[i][j] = true;
        return 1 + dfs(i + 1, j, (i + 1) % 10 != 0 ? si + 1 : si - 8, sj, visited, m, n, k) +
                   dfs(i, j + 1, si, (j + 1) % 10 != 0 ? sj + 1 : sj - 8, visited, m, n, k);
    }
};

剑指 Offer 14.:剪绳子

题目:给你一根长度为n的绳子,请把绳子剪成m段(m、n都是整数,n>1 并且 m>1),每段绳子的长度记为k[0],k[1],···,k[m]。请问k[0]xk[1]x···xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

class Solution {
public:
    int cuttingRope(int n) {
        if(n == 2) return 1;
        if(n == 3) return 2;

        int a = n / 3;
        int b = n % 3;
        long ans = 1;
        switch(b)
        {
            case 0:
                for(int i = 0; i < a; i++)
                {
                    ans = ans * 3;
                }
                break;
            case 1:
                for(int i = 0; i < a - 1; i++)
                {
                    ans = ans * 3;
                }
                ans = ans * 4 ;
                break;
            case 2:
                for(int i = 0; i < a; i++)
                {
                    ans = ans * 3;
                }
                ans = ans * 2;
                break;
        }
        return (int)ans;
    }
};

剑指 Offer 16.:数值的整数次方

题目:实现pow(w,n) ,即计算 x 的整数 n 次幂函数。

class Solution {
public:
    double myPow(double x, int n) {
        double result = 1.0; 
        long a = n;
        if(a < 0)
        {
            x = 1 / x;
            a = -a;
        }

        while(a > 0)
        {
            if((a & 1) == 1)
            {
                result = result * x;
            }
            x = x * x;
            a = a >> 1;
        }
        return result;
    }
};

剑指 Offer 17.:打印从1到最大的n位数

题目:输入数字n,按顺序打印出从1到最大的n位十进制数。比如输入3,则打印出1、2、3一直到最大的3位数999。

/*回溯*/
class Solution {
public:
    vector<int> res;

    void dfs(int n, int idx, string path) 
    {
        if (idx == n) 
        {
            int val = std::stoi(path);
            if (val != 0) 
            {
                res.push_back(val);
            }
            return;
        }
        for (int i = 0; i < 10; ++i) 
        {
            path[idx] = i + '0';
            dfs(n, idx+1, path);
            path[idx] -= '0';
        }
    }

    vector<int> printNumbers(int n) 
    {
        string path(n, '0');
        dfs(n, 0, path);
        return res;
    }
};

剑指 Offer 18.:删除链表的节点 I

题目:在O(1)时间内删除链表节点。
给定单向链表的头指针和一个节点指针,定义一个函数在O(1)时间内删除该节点。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */

class Solution {
public:
    ListNode* deleteNode(ListNode* head, int val) {
        ListNode* dummyHead = new ListNode(-1);
        dummyHead->next = head;
        ListNode* pre = dummyHead;
        ListNode* cur = head;

        while(cur != nullptr)
        {
            if(cur->val == val)
            {
                pre->next = cur->next;
                break;
            }
            cur = cur->next;
            pre = pre->next;
        }
        ListNode* result = dummyHead->next;
        delete(dummyHead);
        return result;
    }
};

剑指 Offer 19.:正则表达式匹配

题目:请实现一个函数用来匹配包含'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串中的所有字符匹配整个模式。例如,字符串"aaa"与模式及"a.a"和"ab*ac*a"匹配,但与"aa.a"和"ab*a"均不匹配。

/*动态规划*/
class Solution {
public:
    bool isMatch(string s, string p) {
        int sSzie = s.size();
        int pSize = p.size();
        vector<vector<bool>> dp(sSzie + 1, vector<bool>(pSize + 1, false));
        for(int i = 0; i <= sSzie; i++)
        {
            for(int j = 0; j <= pSize; j++)
            {
                //当模式串是空时
                if(j == 0)
                {
                    dp[i][j] = (i == 0);
                }
                else
                {
                    //非空正则表达式非"*"情况
                    if(j > 0 && p[j - 1] != '*')
                    {
                        if(i > 0 && (s[i - 1] == p[j - 1] || p[j - 1] == '.'))
                        {
                            dp[i][j] = dp[i - 1][j - 1];
                        }
                    }
                    else
                    {
                        //碰到*分看 和 不看
                        //不看
                        if(j >= 2)
                        {
                            dp[i][j] = dp[i][j - 2];
                        }
                        //看
                        if(i >= 1 && j >= 2 && (s[i - 1] == p[j - 2]  || p[j - 2] == '.'))
                        {
                            dp[i][j] = dp[i][j] || dp[i - 1][j];
                        }
                    }
                }
            }
        }
        return dp[sSzie][pSize];
    }
};

剑指 Offer 20:表示数值的字符串

题目:请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串“+100”、“5e2”、“-123”、“3.1416”及"-1E-16"都表示数值,但"12e"、"1a3.14"、"1.2.3"、"+-5"及"12e+5.4"都不是。

class Solution {
private:
    // 整数的格式可以用[+|-]B表示, 其中B为无符号整数
    bool scanInteger(const string s, int& index){

        if(s[index] == '+' || s[index] == '-')
            ++index;

        return scanUnsignedInteger(s, index);
    }
    
    bool scanUnsignedInteger(const string s, int& index){

        int befor = index;
        while(index != s.size() && s[index] >= '0' && s[index] <= '9')
            index ++;

        return index > befor;
    }
public:
    // 数字的格式可以用A[.[B]][e|EC]或者.B[e|EC]表示,
    // 其中A和C都是整数(可以有正负号,也可以没有),而B是一个无符号整数
    bool isNumber(string s) {

        if(s.size() == 0)
            return false;
        int index = 0;

        //字符串开始有空格,可以返回true
        while(s[index] == ' ')  //书中代码没有该项测试
            ++index;

        bool numeric = scanInteger(s, index);

        // 如果出现'.',接下来是数字的小数部分
        if(s[index] == '.'){

            ++index;

            // 下面一行代码用||的原因:
            // 1. 小数可以没有整数部分,例如.123等于0.123;
            // 2. 小数点后面可以没有数字,例如233.等于233.0;
            // 3. 当然小数点前面和后面可以有数字,例如233.666
            numeric = scanUnsignedInteger(s, index) || numeric;
        }

        // 如果出现'e'或者'E',接下来跟着的是数字的指数部分
        if(s[index] == 'e' || s[index] == 'E'){

            ++index;

            // 下面一行代码用&&的原因:
            // 1. 当e或E前面没有数字时,整个字符串不能表示数字,例如.e1、e1;
            // 2. 当e或E后面没有整数时,整个字符串不能表示数字,例如12e、12e+5.4
            numeric = numeric && scanInteger(s ,index);
        }

        //字符串结尾有空格,可以返回true
        while(s[index] == ' ')
            ++index;
        cout << s.size() << " " << index;   //调试用

        return numeric && index == s.size();
    }
};

剑指 Offer 21:调整数组顺序使奇数位于偶数前面

题目:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。

class Solution {
public:
    vector<int> exchange(vector<int>& nums) {
        int i = 0;
        int j = 0;
        int size = nums.size();
        for(int j = 0; j < size; j++)
        {
            if(nums[j] % 2 == 1)
            {
                swap(nums[i], nums[j]);
                i++;
            }
        }
        return nums;
    }
};

剑指 Offer 24:链表中倒数第k个节点

题目:输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

class Solution {
public:
    ListNode* getKthFromEnd(ListNode* head, int k) {
        ListNode* former = head;
        ListNode* latter = head;
        for(int i = 0; i < k; i++)
            former = former->next;
        while(former != nullptr) {
            former = former->next;
            latter = latter->next;
        }
        return latter;
    }
};

 剑指 Offer 24:反转链表

题目:定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* cur = head;
        ListNode* pre = nullptr;
        while(cur != nullptr)
        {
            ListNode* next = cur->next;
            cur->next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }
};

剑指 Offer 25:合并两个排序的链表

题目:输入两个递增排序的链表,合并这两个链表并使新的链表中的结点仍然是递增排序的。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode* dummyHead = new ListNode(0);
        ListNode* cur = dummyHead;
        while(l1 != nullptr && l2 != nullptr)
        {
            if(l1->val < l2->val)
            {
                cur->next = l1;
                l1 = l1->next;
            }
            else
            {
                cur->next = l2;
                l2 = l2->next;
            }
            cur = cur->next;
        }
        cur->next = (l1 == nullptr ? l2 : l1);
        ListNode* result = dummyHead->next;
        delete(dummyHead);
        return result;
    }
};

剑指 Offer 26:树的子结构

题目:输入两颗二叉树A和B,判断B是不是A的子结构。

class Solution {
public:
    bool Traversal(TreeNode* A, TreeNode* B)
    {
        if(B == nullptr) return true; //B结束,还未报错则匹配成功
        if(A == nullptr || A->val != B->val) return false; //A结束B还由未匹配
        bool isLeftSame = Traversal(A->left, B->left);
        bool isRightSame = Traversal(A->right, B->right);
        return (isLeftSame && isRightSame);
    }

    bool isSubStructure(TreeNode* A, TreeNode* B) {
        return (A != nullptr && B != nullptr) && (Traversal(A, B) || isSubStructure(A->left, B) || isSubStructure(A->right, B)); 
    }
};

剑指 Offer 27:二叉树的镜像

题目:请完成一个函数,输入一颗二叉树,该函数输出它的镜像。

class Solution {
public:
    void Traversal(TreeNode* node)
    {
        if(node == nullptr) return;
        TreeNode* tmpNode = node->left;
        node->left = node->right;
        node->right = tmpNode;
        Traversal(node->left);
        Traversal(node->right);
    }

    TreeNode* mirrorTree(TreeNode* root) {
        Traversal(root);
        return root;
    }
};

剑指 Offer 28:对称的二叉树

题目:请实现一个函数,用来判断一颗二叉树是不是对称的。如果一颗二叉树和它的镜像一样,那么它是对称的。

class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        if(root == nullptr) return true;
        return Traversal(root->left, root->right);
    }

    bool Traversal(TreeNode* leftNode, TreeNode* rightNode)
    {
        if(leftNode == nullptr && rightNode == nullptr) return true;
        if(leftNode == nullptr || rightNode == nullptr || leftNode->val != rightNode->val) return false;
        return Traversal(leftNode->left, rightNode->right) && Traversal(leftNode->right, rightNode->left);
    }   
};

剑指 Offer 29:顺时针打印矩阵

题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。

class Solution 
{
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) 
    {
        if (matrix.empty()) return {};
        vector<int> res;
        int lelfBorder = 0;                      //左边界
        int rightBorder = matrix[0].size() - 1;   //右边界
        int topBorder = 0;                      //上边界
        int bottomBorder = matrix.size() - 1;      //下边界
        while (true)
        {
            //left -> right
            for (int j = lelfBorder; j <= rightBorder; j++)
            {
                res.push_back(matrix[topBorder][j]);
            } 
            if (++topBorder > bottomBorder) break;              
            //top -> bottom
            for (int i = topBorder; i <= bottomBorder; i++)
            {
                res.push_back(matrix[i][rightBorder]);
            }
            if (--rightBorder < lelfBorder) break;
            //right -> left
            for (int j = rightBorder; j >= lelfBorder; j--) 
            {
                res.push_back(matrix[bottomBorder][j]);
            }
            if (--bottomBorder < topBorder) break;
            //bottom -> top
            for (int i = bottomBorder; i >= topBorder; i--) 
            {
                res.push_back(matrix[i][lelfBorder]);
            }
            if (++lelfBorder > rightBorder) break;
        }
        return res;
    }
};

剑指 Offer 30:包含min函数的栈

题目:定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的min函数。在该栈中,调用min、push及pop的时间复杂度都是O(1)。

class MinStack {
public:
    /** initialize your data structure here. */
    stack<int> MinSt;
    stack<int> st;
    MinStack() {
        MinSt.push(INT_MAX); //防止第一次MinSt.top()出现异常
    }
    
    void push(int x) {
        st.push(x);
        MinSt.push(std::min(x, MinSt.top()));
    }
    
    void pop() {
        if(st.empty()) return;
        st.pop();
        MinSt.pop();
    }
    
    int top() {
        if(st.empty()) return -1;
        return st.top();
    }
    
    int min() {
        return MinSt.top();
    }
};

剑指 Offer 31:栈的压入、弹出序列

题目:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出序列。假设压入栈的所有数字均不相等。例如序列{1,2,3,4,5}是某栈的压栈序列,序列{4,5,3,2,1}是该压栈序列对应的一个弹出序列,但{4,3,5,1,2}就不可能是该压栈序列的弹出序列。

class Solution {
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
        stack<int> st;
        int size = pushed.size();
        int j = 0;
        for(int i = 0; i < size; i++)
        {
            st.push(pushed[i]);
            while(!st.empty() && st.top() == popped[j])
            {
                st.pop();
                j++;
            }
        }
        return st.empty();
    }
};

剑指 Offer 32:从上到下打印二叉树 I

题目:从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。

class Solution {
public:
    vector<int> levelOrder(TreeNode* root) {
        queue<TreeNode*> que;
        if(root != nullptr) que.push(root);
        vector<int> result;
        while(!que.empty())
        {
            int size = que.size();
            for(int i = 0; i < size; i++)
            {
                TreeNode* node = que.front();
                que.pop();
                result.push_back(node->val);
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
        } 
        return result;
    }
};

剑指 Offer 32:从上到下打印二叉树 II

题目:从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*> que;
        if(root != nullptr) que.push(root);
        vector<vector<int>> result;
        while(!que.empty())
        {
            int size = que.size();
            vector<int> vec;
            for(int i = 0; i < size; i++)
            {
                TreeNode* node = que.front();
                que.pop();
                vec.push_back(node->val);
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
            result.push_back(vec);
        } 
        return result;
    }
};

剑指 Offer 32:从上到下打印二叉树 III

题目:请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*> que;
        if(root != nullptr) que.push(root);
        vector<vector<int>> result;
        int flag = 0;
        while(!que.empty())
        {
            int size = que.size();
            vector<int> vec;
            flag++;
            for(int i = 0; i < size; i++)
            {
                TreeNode* node = que.front();
                que.pop();
                vec.push_back(node->val);
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
            if(flag & 1)
            {
                result.push_back(vec);
            }
            else
            {
                reverse(vec.begin(), vec.end());
                result.push_back(vec);
            }
        } 
        return result;
    }
};

剑指 Offer 33:二叉搜索树的后序遍历序列

题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回true,否则返回false。假设输入的数组的任意两个数字都不相同。

class Solution {
public:
    bool verify(vector<int>& postorder, int start, int end)
    {
        if(start >= end) return true;
        int pivot = start;
        while(postorder[pivot] < postorder[end]) pivot++;
        int mid = pivot;
        while(postorder[pivot] > postorder[end]) pivot++;
        return (pivot == end) && verify(postorder, start, mid - 1) && verify(postorder, mid, end - 1);
        
    }

    bool verifyPostorder(vector<int>& postorder) {
        return verify(postorder, 0, postorder.size() - 1);
    }
};

剑指 Offer 34:二叉树中和为某一值的路径

题目:输入一颗二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。

/*回溯*/
class Solution {
private:
    vector<int> path;
    vector<vector<int>> result;
public:
    void Traversal(TreeNode* node, int target)
    {
        if(node == nullptr) return;
        path.push_back(node->val);
        target -= node->val;
        if (node->left == nullptr && node->right == nullptr && target == 0) 
        {
            result.push_back(path);
        }
        Traversal(node->left, target);
        Traversal(node->right, target);
        path.pop_back();
    }

    vector<vector<int>> pathSum(TreeNode* root, int target) {
        Traversal(root, target);
        return result;
    }
};

剑指 Offer 35:复杂链表的复制

题目:请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;
    Node* random;
    
    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};
*/
/*使用map映射*/
class Solution {
public:
    Node* copyRandomList(Node* head) {
        if(head == nullptr) return nullptr;
        unordered_map<Node*, Node*> umap;
        Node* cur = head;
        while(cur != nullptr)
        {
            umap[cur] = new Node(cur->val);
            cur = cur->next;
        }
        cur = head;
        while(cur != nullptr)
        {
            umap[cur]->next = umap[cur->next];
            umap[cur]->random = umap[cur->random];
            cur = cur->next;
        }
        return umap[head];
    }
};

剑指 Offer 36:二叉搜索树与双向链表

题目:输入一颗二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的节点。只能调整树中节点指针的指向。

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* left;
    Node* right;

    Node() {}

    Node(int _val) {
        val = _val;
        left = NULL;
        right = NULL;
    }

    Node(int _val, Node* _left, Node* _right) {
        val = _val;
        left = _left;
        right = _right;
    }
};
*/
class Solution {
private:
    Node* pre;
    Node* head;
public:
    Node* treeToDoublyList(Node* root) {
        if(root == nullptr) return nullptr;
        Traversal(root);
        head->left = pre;
        pre->right = head;
        return head;
    }

    void Traversal(Node* cur)
    {
        if(cur == nullptr) return;
        Traversal(cur->left);
        if(pre != nullptr)
        {
            pre->right = cur;
        }
        else
        {
            head = cur;
        }
        cur->left = pre;
        pre = cur;
        Traversal(cur->right);
    }
};

剑指 Offer 37:序列化与反序列化二叉树

题目:请设计一个算法来实现二叉树的序列化与反序列化。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */

class Codec {
public:

    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {
        string ret;
        reserialize(root, ret);
        return ret;
    }

    void reserialize(TreeNode* root, string& str)
    {
        if(root == nullptr)
        {
            str += "None,";
        }
        else
        {
            str += to_string(root->val) + ",";
            reserialize(root->left, str);
            reserialize(root->right, str);
        }
    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
        list<string> dataArry;
        string str;
        for(auto& tmp : data)
        {
            if(tmp == ',')
            {
                dataArry.push_back(str);    
                str.clear();
            }
            else
            {
                str.push_back(tmp);
            }
        }
        return rdeserialize(dataArry);
    }

    TreeNode* rdeserialize(list<string>& dataArray)
    {
        if (dataArray.front() == "None") 
        {
            dataArray.erase(dataArray.begin());
            return nullptr;
        }

        TreeNode* root = new TreeNode(stoi(dataArray.front()));
        dataArray.erase(dataArray.begin());
        root->left = rdeserialize(dataArray);
        root->right = rdeserialize(dataArray);
        return root;
    }
};

// Your Codec object will be instantiated and called as such:
// Codec codec;
// codec.deserialize(codec.serialize(root));

剑指 Offer 38:字符串的排列

题目:输入一个字符串,打印出该字符串中字符的所有排列。例如,输入字符串abc,则打印出由字符串a、b、c所有能排列出来的所有字符串abc、acb、bac、bca、cab和cba。

class Solution {
public:
    vector<string> result;

    void BackTracking(string s, int start, int size)
    {
        if(start == size - 1)
        {
            result.push_back(s);
            return;
        }
        unordered_set<int> uset;
        for(int i = start; i < size; i++)
        {
            if(uset.find(s[i]) != uset.end()) continue;
            uset.insert(s[i]);
            swap(s[i], s[start]);//固定start位置
            BackTracking(s, start + 1, size);
            swap(s[i], s[start]);
        }

    }

    vector<string> permutation(string s) {
         BackTracking(s, 0, s.size());
         return result;
    }
};

剑指 Offer 39:数组中出现次数超过一半的数字

题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如,输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int votes = 0;
        int x = 0;
        for(auto& num : nums)
        {
            if(votes == 0) x = num;
            votes += (x == num ? 1 : -1);
        }
        return x;
    }
};

剑指 Offer 40:最小的k个数

题目:输入n个整数,找出其中最小的k个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

class Solution {
public:
    int Patition(vector<int>& arr, int start, int end)
    {
        int pivot = arr[end];
        int i = start;
        for(int j = start; j < end; j++)
        {
            if(arr[j] < pivot)
            {
                swap(arr[i], arr[j]);
                i++;
            }
        }
        swap(arr[i], arr[end]);
        return i;
    }

    void QuckSort(vector<int>& arr, int start, int end)
    {
        if(start >= end) return;
        int pivot = Patition(arr, start, end);
        QuckSort(arr, start, pivot - 1);
        QuckSort(arr, pivot + 1, end);
    }

    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        QuckSort(arr, 0, arr.size() - 1);
        vector<int> result;
        for(int i = 0; i < k; i++)
        {
            result.push_back(arr[i]);
        }
        return result;   
    }
};

剑指 Offer 41:数据流中的中位数

题目:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

class MedianFinder {
public:
    // 最小堆
    priority_queue<int, vector<int>, greater<int>> minHeap;
    // 最大堆
    priority_queue<int, vector<int>, less<int>> maxHeap;
    /** initialize your data structure here. */
    MedianFinder() {
    }

    // 维持堆数据平衡,并保证左边堆的最大值小于或等于右边堆的最小值
    void addNum(int num) 
    {
        if (maxHeap.size() == minHeap.size())
        {
            maxHeap.push(num);
            int top = maxHeap.top();
            maxHeap.pop();
            minHeap.push(top);
        } 
        else
        {
            minHeap.push(num);
            int top = minHeap.top(); 
            minHeap.pop();
            maxHeap.push(top);
        }
    }
    
    double findMedian() {
        if (maxHeap.size() == minHeap.size())
        {
            return (maxHeap.top()+minHeap.top()) * 1.0 / 2;
        } 
        else 
        {
            return minHeap.top() * 1.0;
        }
    }
};

剑指 Offer 42:连续子数组的最大和

题目:输入一个整形数组,数组里有正数也有负数。数组种的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n)。

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        if(nums.size() == 1) return nums[0]; 
        int result = nums[0]; //注意初始化
        for(int i = 1; i < nums.size(); i++)
        {
            nums[i] += max(nums[i - 1], 0);
            result = max(result, nums[i]);
        }
        return result;
    }
};

剑指 Offer 45:把数组排成最小的数

题目:输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如,输入数组{3,32,321},则打印出这3个数字能排成的最小数字321323。

/*快排*/
class Solution {
public:
    string minNumber(vector<int>& nums) 
    {
        srand(time(0));
        vector<string> strs;
        for(auto& num : nums)
        {
            strs.push_back(to_string(num));
        }
        QuickSort(strs, 0, strs.size() - 1);
        string result;
        for(auto& str : strs)
        {
            result += str;
        }
        return result;
    }
private:
    void QuickSort(vector<string>& strs, int left, int right)
    {
        if(left >= right) return;
        int pivot = left + rand() % (right - left);
        swap(strs[pivot], strs[right]);
        int i = left;
        for(int j = left; j < right; j++)
        {
            if(strs[j] + strs[right] < strs[right] + strs[j]) // 通过这里比较 x + y > y + x 则 x > y
            {
                swap(strs[i], strs[j]);
                i++;
            } 
        }
        swap(strs[i], strs[right]);
        QuickSort(strs, left, i - 1);
        QuickSort(strs, i + 1, right);
    }
};

剑指 Offer 46:把数字翻译成字符串

题目:给定一个数字,我们按照如下规则把它翻译为字符串:0翻译成"a",1翻译成"b",·····,11翻译成"1",······,25翻译成"z"。一个数字可能有多个翻译。例如,12258有5种不同的翻译,分别是"bccfi"、"bwfi"、"bczi"、"mcfi"和"mzi"。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。

class Solution {
public:
    int translateNum(int num) 
    {
        string str = to_string(num);
        int len = str.size();
        if(len < 2) return len;
        vector<int> dp(len + 1);
        dp[1] = 1;
        dp[0] = 1;
        // int a = 1;
        // int b = 1;
        for(int i = 2; i <= len; i++)
        {
            if((str[i - 2] == '1' || ((str[i - 2] == '2') && (str[i - 1] <= '5'))))
            {
                dp[i] = dp[i - 2] + dp[i - 1];
                // int c = a + b;
                // b = a;
                // a = c;
            }
            else
            {
                dp[i] = dp[i - 1];
                // int c = a;
                // b = a;
                // a = c;
            } 
        }
        // return a;
        return dp[len];
    }
};

剑指 Offer 47:礼物的最大价值

题目:在一个mXn的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向左或者向下移动一格,直到到达棋盘的右下角。给定一个棋盘及其上面的礼物,请计算你最多能拿到多少价值的礼物?

class Solution {
public:
    int maxValue(vector<vector<int>>& grid)
    {
        int row = grid.size();
        int col = grid[0].size();
        if(row == 0 || col == 0) return -1;
        vector<vector<int>> dp(row, vector<int>(col, 0));
        dp[0][0] = grid[0][0];
        for(int i = 1; i < row; i++) dp[i][0] = dp[i - 1][0] + grid[i][0];
        for(int j = 1; j < col; j++) dp[0][j] = dp[0][j - 1] + grid[0][j];
        for(int i = 1; i < row; i++)
        {
            for(int j = 1; j < col; j++)
            {
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
            }
        }
        return dp[row - 1][col - 1];
    }
};

剑指 Offer 48:最长不含重复字符的子字符串

题目:请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。假设字符串中只包含'a'~'z'的字符。例如,在字符串"arabcacfr"中,最长的不含重复字符的子字符串是"acfr",长度是4。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int maxLength = 0;
        unordered_set<char> uset;
        int left = 0;
        for(int i = 0; i < s.size(); i++)
        {
            while(uset.find(s[i]) != uset.end())
            {
                uset.erase(s[left]);
                left++;
            }
            uset.insert(s[i]);
            maxLength = max(maxLength, i - left + 1);
        }
        return maxLength;
    }
};

剑指 Offer 49:丑数

题目:给你一个整数 n ,请你找出并返回第 n 个 丑数 。
说明:丑数是只包含质因数 2、3 和/或 5 的正整数;1 是丑数。

class Solution {
public:
    int nthUglyNumber(int n) {
        int a = 0;
        int b = 0;
        int c = 0;
        vector<int> dp(n, 0);
        dp[0] = 1;
        for(int i = 1; i < n; i++)
        {
            int n2 = dp[a] * 2;
            int n3 = dp[b] * 3;
            int n5 = dp[c] * 5;
            dp[i] = std::min({n2, n3, n5});
            if(dp[i] == n2) a++;
            if(dp[i] == n3) b++;
            if(dp[i] == n5) c++;
        }
        return dp[n - 1];
    }
};

剑指 Offer 50:第一个只出现一次的字符

题目:在字符串中找出第一个只出现一次的字符。如输入“abaccdeff”,则输出'b'。

class Solution {
public:
    char firstUniqChar(string s) {
        unordered_map<char, bool> umap;
        for(char c : s)
        {
            umap[c] = (umap.find(c) == umap.end());
        }
        for(char c : s)
        {
            if(umap[c]) return c;
        }
        return ' ';
    }
};

剑指 Offer 51:数组中的逆序对

题目:在数组中的两个数字,如果前面一个数字大于后边的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。例如,在数组{7,5,6,4}中,一共存在5个逆序对,分别是(7,6)、(7,5)、(7,4)、(6,4)和(5,4)。

/*利用归并排序特性*/
class Solution {
public:
    int reversePairs(vector<int>& nums) {
        vector<int> tmp(nums.size());
        return mergeSort(0, nums.size() - 1, nums, tmp);
    }
private:
    int mergeSort(int l, int r, vector<int>& nums, vector<int>& tmp) {
        // 终止条件
        if (l >= r) return 0;
        // 递归划分
        int m = (l + r) / 2;
        int res = mergeSort(l, m, nums, tmp) + mergeSort(m + 1, r, nums, tmp);

        for (int k = l; k <= r; k++)
        {
            tmp[k] = nums[k];
        }
        
        // 合并阶段
        int i = l, j = m + 1;
        for (int k = l; k <= r; k++) 
        {
            if (i == m + 1)
            {
                nums[k] = tmp[j++];
            }
            else if (j == r + 1 || tmp[i] <= tmp[j])
            {
                nums[k] = tmp[i++];
            }
            else 
            {
                nums[k] = tmp[j++];
                res += m - i + 1; // 统计逆序对
            }
        }
        return res;
    }
};

剑指 Offer 52:两个链表的第一个公共节点

题目:输入两个链表,找出他们的第一个公共节点。

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* A = headA;
        ListNode* B = headB;
        while(A != B)
        {
            A = (A == nullptr ? headB : A->next);
            B = (B == nullptr ? headA : B->next);
        }
        return A;
    }
};

剑指 Offer 53:在排序数组中查找数字 I

题目:统计一个数字在排序数组中出现的次数。例如,输入排序数组{1,2,3,3,3,3,4,5}和数字3,由于3在这个数组中出现了4次,因此输出4。

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int i = 0;
        int j = nums.size() - 1;
        //找右边界
        while(i <= j)
        {
            int m = (i + j) / 2;
            if(nums[m] < target){
                i = m + 1;
            }
            else if(nums[m] > target){
                j = m - 1;
            }
            else{
                 i = m + 1;
            }
        }
        int right = i;
        if(j >= 0 && nums[j] != target) return 0;

        i = 0;
        j = nums.size() - 1;
        //找左边界
        while(i <= j)
        {
            int m = (i + j) / 2;
            if(nums[m] < target){
                i = m + 1;
            }
            else if(nums[m] > target){
                j = m - 1;
            }
            else{
                j = m - 1;
            }
        }
        int left = j;
        return right - left - 1;
    }
};

剑指 Offer 53:0~n-1中缺失的数字 II

题目:一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

class Solution {
public:
    int missingNumber(vector<int>& nums) 
    {
        int l = 0; 
        int r = nums.size() - 1;
        while(l <= r)
        {
            int mid = l + (r - l) / 2;
            if(nums[mid] == mid)
            {
                l = mid + 1;
            }
            else
            {
                r = mid - 1;
            }
        }   
        return l; 
    }
};

剑指 Offer 54:二叉搜索树的第k大节点

题目:给定一颗二叉搜索树,请找出其中第k大的节点。

class Solution {
private:
    int result;
    int num;
    void Traversal(TreeNode* node)
    {
        if(node == nullptr) return;
        Traversal(node->right);
        num--;
        if(num == 0) 
        {
            result = node->val;
            return;
        }
        Traversal(node->left);
    }
public:

    int kthLargest(TreeNode* root, int k) {
        num = k; //注意不能给递归函数传参k,否则计算出错
        Traversal(root);
        return result;
    }
};

剑指 Offer 55:二叉树的深度 I

题目:输入一颗二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */

class Solution {
public:
    int maxDepth(TreeNode* root) {
        if(root == nullptr) return 0;  
        int result = 0;     
        queue<TreeNode*> que;
        que.push(root);
        while(!que.empty())
        {
            int size = que.size();
            for(int i = 0; i < size; i++)
            {
                TreeNode* node = que.front();
                que.pop();
                if(node->left)  que.push(node->left);  //注意空指针保护
                if(node->right) que.push(node->right);  
            }
            result++;
        }
        return result;
    }
};

剑指 Offer 55:平衡二叉树 II

题目:输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */

class Solution {
public:

    int Traversal(TreeNode* node)
    {
        if(node == nullptr) return 0;
        int leftValue = Traversal(node->left);
        if(leftValue == -1) return -1;
        int rightValue = Traversal(node->right);
        if(rightValue == -1) return -1;
        if(leftValue - rightValue > 1 || rightValue - leftValue > 1) return -1;
        return max(leftValue, rightValue) + 1;
    }
    bool isBalanced(TreeNode* root) {
        return (Traversal(root) != -1);
    }
};

剑指 Offer 56:数组中数字出的次数 I

题目:数组中只出现一次的两个数字。
一个整形数组里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。

class Solution {
public:
    vector<int> singleNumbers(vector<int>& nums) {
        int x = 0;
        int y = 0;
        int n = 0;
        int size = nums.size();
        for(auto& num : nums)
        {
            n = n ^ num;
        }
        int m = 1;
        while((n & m) == 0)
        {
            m = m << 1;
        }
        for(auto& num : nums)
        {
            if(num & m) 
            {
                x = x ^ num; 
            }
            else
            {
                y = y ^ num;
            }
        }
        vector<int> result;
        result.push_back(x);
        result.push_back(y);
        return result; 
    }
};

剑指 Offer 56:数组中唯一只出现一次的数字 II

题目:在一个数组中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int bits[32] = {0};
        for(int i = 0; i < nums.size(); i++){
            int j = 0;
            //得到各个二进制位为1的有多少个
            while(nums[i]){
                bits[j] += nums[i] % 2;
                nums[i] /= 2;
                j++;
            }
        }
        int ans = 0;
        for(int i = 0; i < 32; i++){
            //利用%3 来求得对应位置上有没有1 有的话乘对应的 2 的i次方
            ans += (1 << i) *(bits[i] % 3);
        }
        return ans;
    }
};

剑指 Offer 57:和为s的数字 I

题目:输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int size = nums.size();
        int left = 0;
        int right = size - 1;
        vector<int> result;
        while(left < right)
        {
            if(nums[left] + nums[right] > target)
            {
                right--;
            }
            else if(nums[left] + nums[right] < target)
            {
                left++;
            }
            else
            {
                result.push_back(nums[left]);
                result.push_back(nums[right]);
                break;
            }
        }
        return result;
    }
};

剑指 Offer 57:和为s的连续正数序列 II

题目:输入一个正数s,打印出所有和为s的连续正数(至少含有两个数)。例如,输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以打印出3个连续序列1~5、4~6和7~8。

/*滑动窗口*/
class Solution {
public:
    vector<vector<int>> findContinuousSequence(int target) {
        int i = 1, j = 2, s = 3;
        vector<vector<int>> res;
        while(i < j) 
        {
            if(s == target) 
            {
                vector<int> ans;
                for(int k = i; k <= j; k++)
                    ans.push_back(k);
                res.push_back(ans);
            }
            if(s >= target) 
            {
                s -= i;
                i++;
            }
            else 
            {
                j++;
                s += j;
            }
        }
        return res;
    }
};

剑指 Offer 58:翻转单词顺序

题目:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student.",则输出"student. a am I"。

class Solution {
public:
    string reverseWords(string s) {
        int size = s.size();
        if(size == 0) return "";
        string result;
        int end = 0;
        for(int i = size - 1; i >= 0; i--)
        {
            if(s[i] != ' ')
            {
                end = i;
                while(s[i] != ' ')
                {
                    i--;
                    if(i < 0) break;
                }
                result = result + s.substr(i + 1, end - (i + 1) + 1) + ' ';
            }
        } 
        return result.substr(0, result.size() - 1);
    }
};

 剑指 Offer 58:左旋转字符串 II

题目:字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"

class Solution 
{
public:
    string reverseLeftWords(string s, int n) 
    {
        string result;
        for(int i = 0; i < s.size(); i++)
        {
            result += s[(i + n) % s.size()];
        }
        return result;
    }
};

剑指 Offer 59:队列的最大值 I

题目一:滑动窗口的最大值
给定一个数组和滑动窗口的大小,请找出所有滑动窗口里的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,它们的最大值分别为{4,4,6,6,6,5}。

/*单调队列*/
class Solution {
private:
    class MyQueue
    {
    public:
        deque<int> que;
        void pop(int value)
        {
            if(!que.empty() && value == que.front())
            {
                que.pop_front();
            }
        }

        void push(int value)
        {
            while(!que.empty() && value > que.back())
            {
                que.pop_back();
            }
            que.push_back(value);
        }

        int front()
        {
            return que.front();
        }
    };
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        MyQueue que;
        vector<int> result;
        if(nums.size() == 0) return result;
        for(int i = 0; i < k; i++)
        {
            que.push(nums[i]);
        }
        result.push_back(que.front());
        for(int i = k; i < nums.size(); i++)
        {
            que.pop(nums[i - k]);
            que.push(nums[i]);
            result.push_back(que.front());
        }
        return result;
    }
};

剑指 Offer 59:队列的最大值 II

题目二:队列的最大值
请定义一个队列并实现函数max得到队列里的最大值,要求函数max、push_back和pop_front的时间复杂度都是O(1)。

class MaxQueue {
public:
    deque<int> que;
    deque<int> deq;
    MaxQueue() {
    }
    
    int max_value() {
        if(deq.empty()) return -1;
        return deq.front();
    }
    
    void push_back(int value) {
        que.push_back(value);
        while(!deq.empty() && deq.back() < value)
        {
            deq.pop_back();
        }
        deq.push_back(value);
    }
    
    int pop_front() {
        if(que.empty()) return -1;
        if(!deq.empty() && deq.front() == que.front())
        {
            deq.pop_front();
        }
        int value = que.front();
        que.pop_front();
        return value;
    }
};

剑指 Offer 60:n个骰子的点数

题目:把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。

/*动态规划*/
class Solution {
public:
    vector<double> dicesProbability(int n) {
        vector<double> dp(6, 1.0 / 6.0);
        for(int i = 2; i <= n; i++)
        {
            vector<double> tmp(6 * i - (i - 1));
            for(int j = 0; j < dp.size(); j++)
            {
                //比当前骰子个数少一个的情况,会对此情况下取到值的后六个数有贡献作用。理解为f(n - 1, x)对f(n, x + 1 ... 6)有贡献,每次占比六分之一
                for(int k = 0; k < 6; k++)
                {
                    //为什么tmp从j + k开始? 因为每增加一个骰子起始下标表示最小总点数值是增加骰子之前的最小总点数值加一
                    tmp[j + k] += dp[j] / 6.0;
                }
            }
            dp = tmp;
        }
        return dp;
    }
};

剑指 Offer 61:扑克牌中的顺子

题目:从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王可以看成任意数字。

class Solution {
public:
    bool isStraight(vector<int>& nums) {
        int size = nums.size();
        if(size == 0) return false;
        unordered_set<int> uset;
        int maxValue = 0; 
        int minValue = 14;
        for(int i = 0; i < size; i++)
        {
            if(nums[i] == 0) continue;
            maxValue = max(maxValue, nums[i]);
            minValue = min(minValue, nums[i]);
            if(uset.find(nums[i]) != uset.end()) return false;
            uset.insert(nums[i]);
        } 
        return (maxValue - minValue < 5);
    }
};

剑指 Offer 62:圆圈中最后剩下的数字

题目:0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。

class Solution {
public:
    int lastRemaining(int n, int m) {
        int x = 0;
        for (int i = 2; i <= n; i++) {
            x = (x + m) % i;
        }
        return x;
    }
};

剑指 Offer 63:股票的最大利润

题目:假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?例如,一只股票在某些时间节点的价格为{9,11,8,5,7,12,16,14}。如果我们能在价格为5的时候买入并在价格为16时卖出,则能收获最大的利润11。

/*动态规划*/
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int size = prices.size();
        if(size == 0) return 0;
        vector<vector<int>> dp(size, vector<int>(2));
        //dp[i][0]表示第i天持有股票最大收益,dp[i][1]表示第i天不持有股票最大收益
        dp[0][0] = -prices[0];
        dp[0][1] = 0;
        for(int i = 1; i < size; i++)
        {
            dp[i][0] = max(dp[i - 1][0], -prices[i]);
            dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i]);
        }
        return dp[size - 1][1];
    }
};

剑指 Offer 64:求 1+2+···+n

题目:求 1+2+···+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)

class Solution {
public:
    int result = 0;
    int sumNums(int n) {
        bool flag  = ((n > 1) && sumNums(n - 1)); //使用n > 1作为递归终止条件
        result += n;
        return result;
    }
};

剑指 Offer 65:不用加减乘除做加法

题目:写一个函数,求两个整数之和,要求在函数体内不得使用"+"、"-"、"x"、"÷"四则运算符号。

class Solution {
public:
    int add(int a, int b) {
        while(b != 0) 
        {   // 当进位为 0 时跳出
            int c = (unsigned int)(a & b) << 1;  // c = 进位,负数不支持左移
            a ^= b; // a = 非进位和
            b = c; // b = 进位
        }
        return a;
    }
};

剑指 Offer 66:构建乘积数组

题目:给定一个数组A[0,1,···,n-1],请构建一个数组B[0,1,···,n-1],其中B中的元素B[i] = A[0]xA[1]x···xA[i-1]xA[i+1]x···xA[n-1]。不能使用除法。

class Solution {
public:
    vector<int> constructArr(vector<int>& nums) {
        int size = nums.size();
        if(size == 0) return {};
        vector<int> result(size, 0);
        result[0] = 1;
        int tmp = 1;
        for(int i = 0; i < size; i++)
        {
            result[i] = tmp;
            tmp *= nums[i]; 
        }

        tmp = 1;
        for(int i = size - 2; i >= 0; i--)
        {
            tmp = tmp * nums[i + 1];
            result[i] = result[i] * tmp;
        }   
        return result;
    }
};

剑指 Offer 67:把字符串转换成整数

题目:实现一个 strToInt(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。

class Solution {
public:
    int strToInt(string str) {
        long int ans = 0;
        bool sign = false;
        int l = str.length();
        int i = 0;
        while(str[i] == ' ' && i < l)
        {
            i++;
        }
        if(str[i] == '-' || str[i] == '+')
        {
            str[i] == '-' ? sign = true : sign = false;
            i++;
        }

        while(i < l)
        {
            if(str[i] < '0' || str[i] > '9')
            {
                break;
            }
            ans *= 10;
            ans += str[i] - '0';
            i++;
            if(ans > INT_MAX)
            {
                return sign ? INT_MIN : INT_MAX;
            }
        }
        return sign ? -ans : ans;
    }
};

剑指 Offer 68:二叉搜索树的最近公共祖先 I

题目:给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

class Solution {
public:
    TreeNode* Traversal(TreeNode* cur, TreeNode* p, TreeNode* q)
    {
        if(cur == nullptr) return cur;

        if(cur->val > p->val && cur->val > q->val)
        {
            TreeNode* left = Traversal(cur->left, p, q);
            if(left != nullptr) return left;
        }

        if(cur->val < p->val && cur->val < q->val)
        {
            TreeNode* right = Traversal(cur->right, p, q);
            if(right != nullptr) return right;
        }
        return cur;
    }

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        return Traversal(root, p, q);
    }
};

剑指 Offer 68:二叉树的最近公共祖先 II

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root == q || root == p || root == nullptr) return root;
        TreeNode* left = lowestCommonAncestor(root->left, p, q);
        TreeNode* right = lowestCommonAncestor(root->right, p, q);
        if(left != nullptr && right != nullptr) return root;

        if(left == nullptr && right != nullptr) return right;
        else if(left != nullptr && right == nullptr) return left;
        else
        {
            // left == nullptr && right == nullptr
            return nullptr;
        }
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值