剑指Offer题解合集

剑指Offer题单及题解

题目顺序为牛客上剑指Offer专题

JZ3、数组中重复的数字

分析

可以直接对数组进行排序,通过判断首末数字大小来判断数字越界情况,注意数组为空的情况。发现 0nums[i]n1, 因此直接开一个数组判断是否有重复数字即可,返回第一个重复数字。

代码实现

class Solution {
public:
    int duplicateInArray(vector<int>& nums) {
        int n = size(nums);
        std::sort(nums.begin(), nums.end());
        if (n == 0 || nums.back() > n || nums[0] < 0) {
            return -1;
        }
        std::vector<int> buc(n);
        for (auto x : nums) {
            buc[x] += 1;
            if (buc[x] > 1) {
                return x;
            }
        }
        return -1;
    }
};

JZ5。替换空格

分析

直接调用std::string中的replace方法替换 即可。

代码实现

class Solution {
public:
    string replaceSpaces(string &str) {
        for (int i = 0; i < size(str); ++i) {
            if (str[i] == ' ') {
                str.replace(str.begin() + i, str.begin() + i + 1, "%20");
            }
        }
        return str;
    }
};

JZ6、从尾到头打印链表

分析

直接遍历链表,将链表中的数据存入vector,最后reverse一下返回即可。

代码实现

class Solution {
public:
    vector<int> printListReversingly(ListNode* head) {
        std::vector<int> ans;
        while (head != nullptr) {
            ans.push_back(head->val);
            head = head->next;
        }
        std::reverse(ans.begin(), ans.end());
        return ans;
    }
};

JZ7、重建二叉树

分析

前序:根左右
中序:左根右
按照规则两个棵树同时进行递归即可,每次通过前序确定根节点,然后通过中序确定左右子树大小来改变递归范围。

代码实现

class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int n = size(preorder);
        auto dfs = [&](auto &&self, int preL, int preR, int inL, int inR)->TreeNode* {
            if (inL > inR) return NULL;
            int root = preorder[preL], mid = inL;
            // std::cerr << root << ' ' << preL << ' ' << preR << ' ' << inL << ' ' << inR << '\n'; 
            TreeNode *node = new TreeNode(root);
            while (mid < inR && inorder[mid] != root) {
                mid += 1;
            }
            node->left = self(self, preL + 1, preL + mid - inL, inL, mid - 1);
            node->right = self(self, preL + mid - inL + 1, preR, mid + 1, inR);
            return node;
            
        };
        return dfs(dfs, 0, n - 1, 0, n - 1);
    }
};

JZ8、二叉树的下一个节点

分析

可以分为两种情况讨论:

  • 情况1:当前节点的右节点不为空,则下一个节点为其右子树的左子树的最下面的节点
  • 情况2:当前节点的右节点为空,则下一个节点为其右子树祖先节点第一个存在右子树节点

代码实现

class Solution {
public:
    TreeNode* inorderSuccessor(TreeNode* p) {
        if (p->right != nullptr) {
            p = p->right;
            while (p->left != nullptr) {
                p = p->left;
            }
            return p;
        }
        while (p->father != nullptr && p->father->right == p) {
            p = p->father;
        }
        return p->father;
    }
};

JZ9、用两个栈实现队列

分析

利用栈后进先出的特点,将stk1栈设置为入队栈只执行插入操作,stk2 栈设置为出队栈只执行出队操作,当stk2中元素出队完后,将stk1元素往stk2插即可,此时插过来的元素一定在栈底,满足队列条件。

代码实现

class MyQueue {
public:
    /** Initialize your data structure here. */
    std::stack<int> stk1, stk2;
    MyQueue() {
        
    }
    
    /** Push element x to the back of queue. */
    void push(int x) {
        stk1.push(x);
    }
    
    /** Removes the element from in front of queue and returns that element. */
    int pop() {
        if (!size(stk2)) {
            while (size(stk1)) {
                stk2.push(stk1.top());
                stk1.pop();
            }
        }
        int ans = stk2.top();
        stk2.pop();
        return ans;
    }
    
    /** Get the front element. */
    int peek() {
        if (!size(stk2)) {
            while (size(stk1)) {
                stk2.push(stk1.top());
                stk1.pop();
            }
        }
        return stk2.top();
    }
    
    /** Returns whether the queue is empty. */
    bool empty() {
        return !(size(stk1) || size(stk2));   
    }
};

/**
 * Your MyQueue object will be instantiated and called as such:
 * MyQueue obj = MyQueue();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.peek();
 * bool param_4 = obj.empty();
 */

JZ10、斐波那契数列

分析

没什么可说的,n39直接暴力即可,甚至不用开long long

代码实现

class Solution {
public:
    int Fibonacci(int n) {
        int n1 = 0, n2 = 1;
        for (int i = 0; i < n; ++i) {
            int n3 = n1 + n2;
            n1 = n2, n2 = n3;
        }
        return n1;
    }
};

JZ11、旋转数组的最小数字

分析

...直接乱写了,get不到这个题目的考点

代码实现

class Solution {
public:
    int findMin(vector<int>& nums) {
        if (size(nums) == 0) return -1;
        return *min_element(nums.begin(), nums.end());
    }
};

JZ12、矩阵中的路径

分析

直接根据枚举起点都搜一遍就行了。

代码实现

class Solution {
public:
    bool hasPath(vector<vector<char>>& matrix, string &str) {
        if (size(matrix) == 0) return false;
        int n = size(matrix), m = size(matrix[0]);
        std::vector<std::vector<bool>> vis(n, std::vector<bool>(m));
        auto dfs = [&](auto &&self, int u, int x, int y)->bool {
            if (u == size(str)) return true;
            for (auto [dx, dy] : {std::pair{x, y + 1}, {x, y - 1}, {x - 1, y}, {x + 1, y}}) {
                if (dx >= 0 && dx < n && dy >= 0 && dy < m 
                    && !vis[dx][dy] && str[u] == matrix[dx][dy]) {
                    vis[dx][dy] = true;
                    if (self(self, u + 1, dx, dy)) {
                        return true;
                    }
                    vis[dx][dy] = false;
                }
            }
            return false;
        };
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                if (matrix[i][j] == str[0]) {
                    vis[i][j] = true;
                    if (dfs(dfs, 1, i, j)) {
                        return true;
                    }
                    vis[i][j] = false;
                }
            }
        }
        return false;
    }
};

JZ13、机器人的运动范围

分析

直接从起点开始dfs搜一遍,记录哪些点可以到达即可。

代码实现

class Solution {
public:
    int movingCount(int threshold, int rows, int cols) {
        if (rows == 0 || cols == 0) return 0;
        std::vector<std::vector<bool>> vis(rows, std::vector<bool>(cols));
        auto calc = [&](int x, int y) {
            return x % 10 + x / 10 % 10 + y % 10 + y / 10 % 10; 
        };
        auto dfs = [&](auto &&self, int x, int y)->int {
            int cnt = 1;
            vis[x][y] = true;
            for (auto [dx, dy] : {std::pair{x + 1, y}, {x - 1, y}, {x, y + 1}, {x, y - 1}}) {
                if (dx >= 0 && dx < rows && dy >= 0 && dy < cols && !vis[dx][dy] && calc(dx, dy) <= threshold) {
                    cnt += self(self, dx, dy);
                }
            }
            return cnt;
        };
        return dfs(dfs, 0, 0);
    }
};

JZ14、剪绳子

分析

发现n非常小,因此可以选择直接枚举分成m段,显然的当各个位数越接近时他们的乘积越大

代码实现

class Solution {
public:
    int maxProductAfterCutting(int length) {
        int ans = 1;
        for (int m = 2; m < length; ++m) {
            int p = length / m;
            std::vector<int> num(m, p);
            for (int i = 0; i < length - p * m; ++i) {
                num[i] += 1;
            }
            int mul = 1;
            for (auto &x : num) {
                mul *= x;
            }
            ans = std::max(mul, ans);
        }
        return ans;
    }
};

JZ15、二进制中1的个数

分析

__builtin_popcount()直接秒了

代码实现

class Solution {
public:
    int NumberOf1(int n) {
        // int ans = 0;
        // while (n) {
        //     n &= n - 1;
        //     ans += 1;
        // }
        // return ans;
        return __builtin_popcount(n);
    }
};

JZ16、数值的整数次方

分析

快速幂模拟一下即可,如果exponent为负数最后被1除一下就行了

代码实现

class Solution {
public:
    double Power(double base, int exponent) {
        using i64 = long long;
        bool flag = exponent > 0;
        i64 c = std::abs((i64)exponent);
        double ans = 1;
        while (c) {
            if (c & 1) ans *= base;
            base *= base;
            c >>= 1;
        }
        if (!flag) {
            ans = 1. / ans;
        }
        return ans;
    }
};

JZ17、打印从1到最大的n位数

分析

直接开一个10n1大小的数组从头开始赋值即可。

代码实现

class Solution {
public:
    vector<int> printNumbers(int n) {
        std::vector<int> f((int)pow(10, n) - 1);
        for (int idx = 1; auto &x : f) {
            x = idx++;
        }
        // std::iota(f.begin(), f.end(), 1);
        return f;
    }
};

JZ18、在O(1)时间删除链表结点

分析

将当前节点的值拷贝为下一个节点的值,再将下一个节点删除等价于删除当前节点。

代码实现

class Solution {
public:
    void deleteNode(ListNode* node) {
        ListNode *now = node->next;
        node->val = now->val;
        node->next = node->next->next;
        delete now;
    }
};

JZ20、表示数值的字符串

分析

面向样例编程最爽的一集,模拟一下即可。

代码实现

class Solution {
public:
    bool isNumber(string s) {
        if (s == "") return false;
        int pe = -1, pE = -1;
        for (int idx = 0; auto &c : s) {
            if (!((c >= '0' && c <= '9') 
                || c == '+' || c == '-' || c == '.' 
                || c == 'E' || c == 'e')) {
                return false;
            }
            if (c == 'e') {
                if (pe != -1) {
                    return false;
                } else pe = idx;
            }
            if (c == 'E') {
                if (pE != -1) {
                    return false;
                } else pE = idx;
            }
            idx += 1;
        }
        auto check = [&](std::string &t) {
            int pAdd = t.rfind('+'), pSub = t.rfind('-');
            if (pAdd != -1 && pAdd != 0) return false;
            if (pSub != -1 && pSub != 0) return false;
            if (std::count(t.begin(), t.end(), '.') > 1) return false;
            if (t == "+." || t == "-." || t == ".-" || t == ".+" 
                || t == "+" || t == "-" || t == ".") return false;
            return true;
        };
        if (pe != -1 && pE != -1) {
            return false;
        } else if (pe != -1 || pE != -1) {
            int p = pe != -1 ? pe : pE;
            std::string s1 = s.substr(0, p), s2 = s.substr(p + 1);
            if (s1 == "" || s2 == "" || s2.find('.') != string::npos) return false;
            return check(s1) && check(s2);
        } else {
            return check(s);
        }
    }
};

JZ21、调整数组顺序使奇数位于偶数前面

分析

直接拿两个数组记录所有奇数和偶数再插入原数组中即可。

代码实现

class Solution {
public:
    void reOrderArray(vector<int> &array) {
        int n = size(array);
        std::vector<int> even, odd;
        for (auto x : array) {
            x % 2 ? odd.push_back(x) : even.push_back(x);
        }
        int idx = 0;
        for (auto x : odd) {
            array[idx++] = x;
        }
        for (auto x : even) {
            array[idx++] = x;
        }
        
    }
};

JZ22、链表中倒数最后k个结点

分析

记录两个间距为k的指针,然后同时往后跳到最后即可找到第k个节点

代码实现

class Solution {
public:
    ListNode* findKthToTail(ListNode* pHead, int k) {
        if (pHead == nullptr) {
            return nullptr;
        }
        ListNode *fast = pHead, *slow = pHead;
        while (k--) {
            fast = fast->next;
            if (fast == nullptr && k > 0) {
                return nullptr;
            }
        }
        while (fast != nullptr) {
            fast = fast->next;
            slow = slow->next;
        }
        return slow;
    }
};

JZ23、链表中环的入口结点

分析

快慢指针,一个指针每次走1步,一个指针每次走两步,如果能够相遇则说明存在环。随后将其中一个指针调到起点,接着走,相遇的节点即位入口节点。

代码实现

class Solution {
public:
    ListNode *entryNodeOfLoop(ListNode *pHead) {
        if (pHead == nullptr) {
            return nullptr;
        }
        ListNode *fast = pHead, *slow = pHead;
        do {
            fast = fast->next;
            slow = slow->next;
            if (fast != nullptr) {
                fast = fast->next;
            } else return nullptr;
            if (fast == slow) {
                break;
            }
        } while (fast != nullptr);
        if (fast == nullptr) {
            return nullptr;
        }
        while (pHead != slow) {
            slow = slow->next;
            pHead = pHead->next;
        }
        return pHead;
    }
};

JZ24、反转链表

分析

每次拿一个中间点转换一下两个点指针方向即可。

代码实现

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (head == nullptr || head->next == nullptr) {
            return head;
        }
        ListNode *n1 = head, *n2 = head->next;
        head->next = nullptr;
        while (n2 != nullptr) {
            ListNode *n3 = n2->next;
            n2->next = n1;
            n1 = n2;
            n2 = n3;
        }
        return n1;
    }
};

JZ25、合并两个排序的链表

分析

需要用一个新的指针来接受新链表,旧的两个指针每次比较头元素,然后将较小的元素插入新链表即可。

代码实现

class Solution {
public:
    ListNode* merge(ListNode* pHead1, ListNode* pHead2) {
        if (pHead1 == nullptr) {
            return pHead2;
        }
        if (pHead2 == nullptr) {
            return pHead1;
        }
        ListNode *ans;
        if (pHead1->val > pHead2->val) {
            std::swap(pHead1, pHead2);
        }
        ans = pHead1;
        while (pHead1->next != nullptr && pHead2 != nullptr) {
            // std::cout << pHead1->next->val << ' ' << pHead2->val << "!\n";
            if (pHead1->next->val > pHead2->val) {
                ListNode *temp = pHead2;
                pHead2 = pHead2->next;
                temp->next = pHead1->next;
                pHead1->next = temp;
            }
            pHead1 = pHead1->next;
            // std::cout << pHead1->val << "?\n";
        } 
        if (pHead1->next == nullptr) {
            pHead1->next = pHead2;
        }
        return ans;
    }
};

JZ26、树的子结构

分析

遍历A树,遇到和Bval相等的节点就到B中搜索一下看查是否为子树即可。

代码实现

class Solution {
public:
    bool hasSubtree(TreeNode* A, TreeNode* B) {
        if (B == NULL) return false;
        auto check = [&](auto &&self, TreeNode *a, TreeNode *b)->bool {
            if (a == nullptr && b == nullptr) return true;
            if (b == nullptr) return true;
            if (a == nullptr) return false;
            if (a->val != b->val) return false;
            return self(self, a->left, b->left) && self(self, a->right, b->right);
        };
        auto dfs = [&](auto &&self, TreeNode *root)->bool {
            if (root == NULL) return false;
            if (root->val == B->val) {
                if (check(check, root, B)) return true;
            }
            if (self(self, root->right)) return true;
            if (self(self, root->left)) return true;
            return false;
        };
        return dfs(dfs, A);
    }
};

JZ27、二叉树的镜像

分析

每次递归完交换左右子树即可

代码实现

class Solution {
public:
    void mirror(TreeNode* pRoot) {
        auto dfs = [&](auto &&self, TreeNode *root)->void {
            if (root == nullptr) return ;
            self(self, root->left);
            self(self, root->right);
            std::swap(root->left, root->right);
        };
        dfs(dfs, pRoot);
    }
};

JZ29、顺时针打印矩阵

分析

模拟,注意判断边界就行了

代码实现

class Solution {
public:
    vector<int> printMatrix(vector<vector<int> > matrix) {
        if (size(matrix) == 0) return {};
        int n = size(matrix), m = size(matrix[0]);
        std::array<int, 4> dir{m - 1, n - 1, 0, 1};
        std::vector<int> ans;
        int x = 0, y = 0, idx = 0;
        while (size(ans) != n * m) {
            // std::cerr << idx << ' ' << x << ' ' << y << '\n';
            if (idx % 4 == 0) {
                while (y <= dir[idx % 4]) {
                    ans.push_back(matrix[x][y++]);
                }
                dir[idx % 4] -= 1;
                y -= 1, x += 1;
            } else if (idx % 4 == 1) {
                while (x <= dir[idx % 4]) {
                    ans.push_back(matrix[x++][y]);
                }
                dir[idx % 4] -= 1;
                x -= 1, y -= 1;
            } else if (idx % 4 == 2) {
                while (y >= dir[idx % 4]) {
                    ans.push_back(matrix[x][y--]);
                }
                dir[idx % 4] += 1;
                y += 1, x -= 1;
            } else {
                while (x >= dir[idx % 4]) {
                    ans.push_back(matrix[x--][y]);
                }
                dir[idx % 4] += 1;
                x += 1, y += 1;
            }
            idx += 1;
        }
        return ans;
    }
};

JZ30、包含min函数的栈

分析

维护两个栈,其中一个单调存储元素即可。

代码实现

class MinStack {
public:
    std::stack<int> stk, stkMin;
    MinStack() {
        
    }
    
    void push(int x) {
        if (size(stkMin) == 0 || stkMin.top() >= x) {
            stkMin.push(x);
        }
        stk.push(x);
    }
    
    void pop() {
        if (stkMin.top() == stk.top()) {
            stkMin.pop();
        }
        stk.pop();
    }
    
    int top() {
        return stk.top();
    }
    
    int getMin() {
        return stkMin.top();
    }
};

JZ30、包含min函数的栈

分析

维护两个栈,其中一个单调存储元素即可。

代码实现

class MinStack {
public:
    std::stack<int> stk, stkMin;
    MinStack() {
        
    }
    
    void push(int x) {
        if (size(stkMin) == 0 || stkMin.top() >= x) {
            stkMin.push(x);
        }
        stk.push(x);
    }
    
    void pop() {
        if (stkMin.top() == stk.top()) {
            stkMin.pop();
        }
        stk.pop();
    }
    
    int top() {
        return stk.top();
    }
    
    int getMin() {
        return stkMin.top();
    }
};

JZ31、栈的压入、弹出序列

分析

使用一个栈模拟出入栈即可。

代码实现

class Solution {
public:
    bool isPopOrder(vector<int> pushV,vector<int> popV) {
        if (size(pushV) != size(popV)) return false;
        if (size(pushV) == 0) return true; 
        std::stack<int> stk;
        int puv = 0, pov = 0, n = size(pushV);
        while (puv != (int)size(pushV) && pov != (int)size(popV)) {
            // std::cerr << size(stk) << ' ' << pov << ' ' << puv << "?\n";
            while ((int)size(stk) == 0 || (puv < n && stk.top() != popV[pov])) {
                stk.push(pushV[puv++]);
            }
            while (size(stk) && pov < n && stk.top() == popV[pov]) {
                stk.pop();
                pov++;
            }
            if (puv == n && size(stk) != 0 && pov != n) {
                return false;
            }
        }
        return true;
    }
};

JZ32、从上往下打印二叉树

分析

即二叉树的层序遍历

代码实现

class Solution {
public:
    vector<int> printFromTopToBottom(TreeNode* root) {
        if (root == nullptr) return {};
        std::vector<TreeNode *> q;
        std::vector<int> ans;
        q.push_back(root);
        for (int i = 0; i < (int)size(q); ++i) {
            TreeNode *u = q[i];
            ans.push_back(u->val);
            if (u->left != nullptr) {
                q.push_back(u->left);
            }
            if(u->right != nullptr) {
                q.push_back(u->right);
            }
        }
        return ans;
    }
};

JZ33、二叉搜索树的后序遍历序列

分析

二叉搜索树的性质:左子树都比右子树小。
并且由于是后续遍历可以直接确定根节点,因此本题可以通过根节点来寻找左右子树边界,最后分别递归判断右子树是否全部大于根节点即可。

代码实现

class Solution {
public:
    bool verifySequenceOfBST(vector<int> sequence) {
        if (size(sequence) == 0) return true;
        int n = size(sequence);
        auto dfs = [&](auto &&self, int l, int r)->bool {
            if (l >= r) return true;
            int root = sequence[r], mid = l;
            while (mid < r && sequence[mid] < root) {
                mid += 1;
            }
            // std::cout << l << ' ' << r << ' ' << mid << '\n';
            for (int i = mid + 1; i < r; ++i) {
                if (sequence[i] < root) return false;
            }
            return self(self, l, mid - 1) && self(self, mid, r - 1);
        };
        return dfs(dfs, 0, n - 1);
    }
};

JZ35、复杂链表的复制

分析

使用map记录下被映射的节点,如果未创建则需要重新创建一个,最后就是遍历链表并记录新链表。

代码实现

class Solution {
public:
    ListNode *copyRandomList(ListNode *head) {
        std::map<ListNode *, ListNode *> mp;
        mp[nullptr] = nullptr;
        ListNode *dup = new ListNode(-1), *tail = dup;
        auto dfs = [&](auto &&self, ListNode *root)->void {
            if (root == nullptr) return ;
            if (!mp.count(root)) {
                mp[root] = new ListNode(root->val);
            }
            if (!mp.count(root->random)) {
                mp[root->random] = new ListNode(root->random->val);
            }
            tail->next = mp[root];
            tail->next->random = mp[root->random];
            tail = tail->next;
            self(self, root->next);
        };
        dfs(dfs, head);
        return dup->next;
    }
};

JZ36、二叉搜索树与双向链表

分析

dfs的时候注意记录下父亲节点就行了。

代码实现

class Solution {
public:
    TreeNode* convert(TreeNode* root) {
        if (root == nullptr) return nullptr;
        TreeNode *head = nullptr, *pre = nullptr;
		auto dfs = [&](auto &&self, TreeNode *root)->void {
			if (root == nullptr) return ;
			self(self, root->left);
			if (pre == nullptr) {
				head = root;
			} else {
				pre->right = root;
			}
			root->left = pre;
			pre = root;
			self(self, root->right);
		};
		dfs(dfs, root);
		return head;
    }
};

JZ37、序列化二叉树

分析

使用std::stringstream模拟std::cinstd::cout输入输出前序序列即可。

代码实现

class Solution {
public:

    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {
        std::stringstream cout;
        auto dfs = [&](auto &&self, TreeNode *root)->void {
            if (root == nullptr) {
                cout << (int)1e9 << ' ';
            } else {
                cout << root->val << ' ';
                self(self, root->left);
                self(self, root->right);
            }  
        };
        dfs(dfs, root);
        return cout.str();
    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
        std::stringstream cin(data);
        auto dfs = [&](auto &&self)->TreeNode* {
            int val; 
            cin >> val;
            if (val == 1e9) return nullptr; 
            TreeNode *node = new TreeNode(val);
            node->left = self(self);
            node->right = self(self);
            return node;
        };
        return dfs(dfs);
    }
};

JZ38、字符串的排列

分析

std::next_permutation秒了

代码实现

class Solution {
public:
    vector<string> Permutation(string str) {
       std::vector<std::string> ans;
        std::sort(str.begin(), str.end());
        do {
            ans.push_back(str);
        } while (std::next_permutation(str.begin(), str.end()));
        return ans; 
    }
};

JZ39、数组中出现次数超过一半的数字

分析

std::unordered_map记录一下所有数字出现次数即可

代码实现

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int>& nums) {
        std::unordered_map<int, int> mp;
        for (auto x : nums) {
            mp[x] += 1;
        }
        int ans = -1;
        for (auto [x, cnt] : mp) {
            if (cnt * 2 >= size(nums)) {
                ans = x;
                break;
            }
        }
        return ans;
    }
};

JZ40、最小的K个数

分析

排序一下让后删除最后的nk个数即可

代码实现

class Solution {
public:
    vector<int> getLeastNumbers_Solution(vector<int> input, int k) {
        std::sort(input.begin(), input.end());
        input.erase(input.begin() + k, input.end());
        return input;
    }
};

JZ41、数据流中的中位数

分析

使用两个std::priority_queue分别维护一半的数组,中位数即位两个堆顶的平均数,或者小根堆的堆顶。

代码实现

class Solution {
public:
    std::priority_queue<int, std::vector<int>, std::greater<>> pre;
    std::priority_queue<int> suf;
    void insert(int num){
        pre.push(num);
        while (size(pre) > size(suf) + 1) {
            int a = pre.top();
            pre.pop();
            suf.push(a);
        }
        while (size(pre) && size(suf) && pre.top() < suf.top()) {
            int a = pre.top(), b = suf.top();
            pre.pop(), suf.pop();
            suf.push(a), pre.push(b);
        }
    }

    double getMedian(){
        if (size(pre) == size(suf)) {
            return 1. * (pre.top() + suf.top()) / 2; 
        } else {
            return pre.top();
        }
    }
};

JZ43、从1到n整数中1出现的次数

分析

枚举每个位置上1出现的次数即可。

代码实现

class Solution {
public:
    int numberOf1Between1AndN_Solution(int n) {
        int ans = 0, p = 1, x = 0;
        while (n) {
            int t = n % 10;
            if (p == 1) {
                ans += n / 10 + (t != 0);
            } else {
                if (t <= 1) {
                    ans += n / 10 * p + (x + 1) * t;
                } else {
                    ans += (n / 10 + 1) * p;
                }
            }
            x = p * t + x;
            p *= 10, n /= 10;
        }
        return ans;
    }
};

JZ44、数字序列中某一位的数字

分析

优先找到这个下标所属的位数,然后就可以快速定位到是哪一个数字。

代码实现

class Solution {
public:
    int digitAtIndex(int n) {
        if (n <= 9) return n;
        using i64 = long long;
        n -= 10;
        i64 p = 90, len = 2;
        while (n >= p * len) {
            n -= p * len;
            p *= 10, len += 1;
        }
        int y = std::pow(10, len - 1) + n / len;
        return std::to_string(y)[n % len] - '0';
    }
};

JZ45、把数组排成最小的数

分析

直接进行排序即可判断哪两个数相加更小

代码实现

class Solution {
public:
    string printMinNumber(vector<int>& nums) {
        std::sort(nums.begin(), nums.end(), 
            [&](const int a, const int b) {
                return std::to_string(a) + std::to_string(b) < std::to_string(b) + std::to_string(a);
            });
        std::string ans;
        for (int i = 0; i < (int)size(nums); ++i) {
            ans += std::to_string(nums[i]);
        }
        return ans;
    }
};

JZ46、把数字翻译成字符串

代码实现

class Solution {
public:
    int getTranslationCount(std::string s) {
        int n = size(s);
        std::vector<int> f(n);
        f[n - 1] = 1;
        for (int i = n - 2; i >= 0; --i) {
            f[i] = f[i + 1];
            if (s[i] == '1' || (s[i] == '2' && s[i + 1] < '6')) {
                f[i] += f[i + 2];
            }
        }
        return f[0];
    }
};

JZ47、礼物的最大价值

代码实现

class Solution {
public:
    int getTranslationCount(std::string s) {
        int n = size(s);
        std::vector<int> f(n);
        f[n - 1] = 1;
        for (int i = n - 2; i >= 0; --i) {
            f[i] = f[i + 1];
            if (s[i] == '1' || (s[i] == '2' && s[i + 1] < '6')) {
                f[i] += f[i + 2];
            }
        }
        return f[0];
    }
};

JZ47、礼物的最大价值

分析

双指针维护一下集合中字符的重复性即可

代码实现

class Solution {
public:
    int longestSubstringWithoutDuplication(std::string s) {
        int n = size(s);
        std::vector<bool> used(26);
        int l = 0, r = 0, ans = 0;
        while (r < n) {
            while (l < r && used[s[r] - 'a']) {
                used[s[l] - 'a'] = false;
                l += 1;
            }
            used[s[r] - 'a'] = true;
            r += 1;
            ans = std::max(r - l, ans);
        }
        return ans;
    }
};

JZ48、最长不含重复字符的子字符串

分析

双指针维护一下集合中字符的重复性即可

代码实现

class Solution {
public:
    int longestSubstringWithoutDuplication(std::string s) {
        int n = size(s);
        std::vector<bool> used(26);
        int l = 0, r = 0, ans = 0;
        while (r < n) {
            while (l < r && used[s[r] - 'a']) {
                used[s[l] - 'a'] = false;
                l += 1;
            }
            used[s[r] - 'a'] = true;
            r += 1;
            ans = std::max(r - l, ans);
        }
        return ans;
    }
};

JZ49、丑数

分析

优先队列维护未被使用过的最小值并添加到std::set中去重

代码实现

class Solution {
public:
    int getUglyNumber(int n) {
        std::set<long long> st = {1};
        std::priority_queue<long long, std::vector<long long>, std::greater<>> heap;
        heap.push(1);
        while (size(st) < 3LL * n) {
            auto x = heap.top();
            heap.pop();
            for (auto p : {2, 3, 5}) {
                if (!st.count(x * p)) {
                    heap.push(x * p);
                    st.insert(x * p);
                }    
            }
        }
        while (size(st) != n) {
            st.erase(prev(st.end()));
        }
        return *st.rbegin();
    }
};

JZ50、第一个只出现一次的字符

代码实现

class Solution {
public:
    char firstNotRepeatingChar(string s) {
        if (size(s) == 0) return '#';
        std::unordered_map<char, std::vector<int>> mp;
        for (int i = 0; i < size(s); ++i) {
            mp[s[i]].push_back(i);
        }
        char ans = -1, pos = 1E9;
        for (char c = 'a'; c <= 'z'; ++c) {
            if (size(mp[c]) == 1) {
                if (mp[c][0] < pos) {
                    ans = c;
                    pos = mp[c][0];
                }
            }
        }
        for (char c = 'A'; c <= 'Z'; ++c) {
            if (size(mp[c]) == 1) {
                if (mp[c][0] < pos) {
                    ans = c;
                    pos = mp[c][0];
                }
            }
        }
        if ((int)ans == -1) {
            return '#';    
        } else {
            return ans;
        }
    }
};

JZ51、数组中的逆序对

代码实现

class Solution {
public:
    int inversePairs(vector<int>& nums) {
        using i64 = long long;
        auto mergeSort = [&](auto &&self, int l, int r)->i64 {
            if (l >= r) return 0;
            int mid = l + r >> 1;
            int ans = self(self, l, mid) + self(self, mid + 1, r);
            std::vector<int> tmp;
            int i = l, j = mid + 1;
            while (i <= mid && j <= r) {
                if (nums[i] <= nums[j]) {
                    tmp.push_back(nums[i++]);
                } else {
                    ans += mid - i + 1; 
                    tmp.push_back(nums[j++]);    
                }
            }
            while (i <= mid) {
                tmp.push_back(nums[i++]);
            }
            while (j <= r) {
                tmp.push_back(nums[j++]);    
            }
            for (int i = 0; i < size(tmp); ++i) {
                nums[l + i] = tmp[i];
            }
            return ans;
        };
        return mergeSort(mergeSort, 0, size(nums) - 1);
    }
};

JZ52、两个链表的第一个公共结点

分析

快慢指针跑一遍即可

代码实现

class Solution {
public:
    ListNode* FindFirstCommonNode(ListNode* pHead1, ListNode* pHead2) {
        if (pHead1 == nullptr || pHead2 == nullptr) {
			return nullptr;
		}
		ListNode *p1 = pHead1, *p2 = pHead2;
		while (p1 != p2) {
			p1 = p1->next;
			if (p1 == nullptr) {
				p1 = pHead2;
				pHead2 = nullptr;
			}
			p2 = p2->next;
			if (p2 == nullptr) {
				p2 = pHead1;
				pHead1 = nullptr;
			}
		}
		return p1;
    }
};

JZ53、数字在升序数组中出现的次数

分析

二分至相同数字的左右端点即可

代码实现

class Solution {
public:
    int getNumberOfK(vector<int> &nums , int k) {
        if (size(nums) == 0) return 0;
        int n = size(nums);
        int l = 0, r = n - 1;
        while (l < r) {
            int mid = l + r >> 1;
            if (nums[mid] < k) {
                l = mid + 1;
            } else {
                r = mid;
            }
        }
        if (nums[r] != k) {
            return 0;
        }
        int pl = r;
        l = 0, r = n - 1;
        while (l < r) {
            int mid = l + r + 1 >> 1;
            if (nums[mid] <= k) {
                l = mid;
            } else {
                r = mid - 1;
            }
        }
        return r - pl + 1;
    }
};

JZ54、二叉搜索树的第k个节点

分析

即求前序遍历的第k的点

代码实现

class Solution {
public:
    TreeNode* kthNode(TreeNode* root, int k) {
        int cnt = 0;
        std::vector<TreeNode *> ans;
        auto dfs = [&](auto &&self, TreeNode *u)->void {
            if (u == nullptr) return ;
            self(self, u->left);
            ans.push_back(u);
            self(self, u->right);
        };
        dfs(dfs, root);
        return ans[k - 1];
    }
};

JZ55、二叉树的深度

代码实现

class Solution {
public:
    int treeDepth(TreeNode* root) {
        int maxDep = 0;
        std::vector<TreeNode *> ans;
        auto dfs = [&](auto &&self, TreeNode *u, int dep)->void {
            if (u == nullptr) return ;
            maxDep = std::max(maxDep, dep);
            self(self, u->left, dep + 1);
            ans.push_back(u);
            self(self, u->right, dep + 1);
        };
        dfs(dfs, root, 1);
        return maxDep;
    }
};

JZ56、数组中只出现一次的两个数字

代码实现

class Solution {
public:
    vector<int> findNumsAppearOnce(vector<int>& nums) {
        int n = size(nums), xy = 0;
        for (int i = 0; i < n; ++i) {
            xy ^= nums[i];
        }
        int k = 31 - __builtin_clz(xy);
        int x = 0;
        for (int i = 0; i < n; ++i) {
            if ((nums[i] >> k & 1) == 1) {
                x ^= nums[i];
            }
        }
        return {x, x ^ xy};
    }
};

JZ57、数组中只出现一次的两个数字

分析

对原数组进行排序后枚举每个数字进行二分查找target - nums[i]是否存在。

代码实现

class Solution {
public:
    vector<int> findNumbersWithSum(vector<int>& nums, int target) {
        std::sort(nums.begin(), nums.end());
        for (int i = 0; i < size(nums); ++i) {
            auto pos = std::lower_bound(nums.begin(), nums.end(), target - nums[i]);
            if (pos != nums.end()) {
                if (nums[i] + (*pos) == target) {
                    return {nums[i], *pos};
                }
            }
        }
        return {};
    }
};

未完待续...

posted @   sleeeeeping  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程
  1. 1 吹梦到西洲 恋恋故人难,黄诗扶,妖扬
  2. 2 敢归云间宿 三无Marblue
敢归云间宿 - 三无Marblue
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

敢归云间宿 - 三无Marblue

词:怀袖

曲:KBShinya

编曲:向往

策划:杨颜

监制:似雨若离RainJaded/杨颜

吉他:大牛

混音:三无Marblue

和声:雾敛

母带:张锦亮

映像:似雨若离RainJaded

美术:阿尼鸭Any-a/乙配/雨谷/今风/米可/Eluan

题字:长安

酒 泼去群山眉头

酒 泼去群山眉头

月 悬在人世沧流

空杯如行舟 浪荡醉梦里走

心 生自混沌尽头

心 生自混沌尽头

对 天地自斟自酬

诗随我 遍历春秋

行流水 走笔形生意动

见珠玉 淙淙落纸成诵

拾得浮名有几声 云深处 却空空

耳畔丝竹 清商如雾

谈笑间 却是心兵穷途

飞觞醉月无归宿 便是孤独

不如就化身为风

卷狂沙 侵天幕

吹醒那 泉下望乡 的战骨

昨日边关犹灯火

眼前血海翻覆

千万人跌落青史 隔世号呼

于是沸血重剑共赴

斩以雷霆之怒

肩背相抵破阵开路

万古同歌哭

纵他春风不度 悲欢蚀骨

此去宁作吾

挣过命途 才敢写荣枯

望 云际群龙回首

望 云际群龙回首

任 飘蓬争逐身后

叹冥顽之俦 好景尽付恩仇

收 江声随酒入喉

收 江声随酒入喉

来 提笔御风同游

不觉已 换了春秋

真亦假 泼墨腾烟沉陆

有还无 蝶影纷堕幻目

我与天地周旋久

写尽梦 便成梦

夜雨浇熄 往事残烛

生死间 谁尽兴谁辜负

管他醒来归何处 心生万物

也曾对电光火雨

抛酒樽 镇天枢

护住了 人间多少 朝与暮

烧尽了阴云冥府

烧尽了阴云冥府

且看星斗尽出

浩荡荡尘埃野马 忘怀命数

于是信步鸿蒙之轻

也领苍生之重

与诗与剑颠倒与共

沉眠斜阳中

纵他世事汹涌 万类争渡

此去宁作吾

醉得糊涂 才梦得清楚

潮水 带着叹息轻抚

潮水 带着叹息轻抚

像光阴 漫过大地上幽微草木

有情世 见众生明灭往复

天生自在 何必回顾

晦暗中双掌一拊

立此身 照前路

与某个 阔别的我 决胜负

渺渺兮身外无物

无喜无悲无怖

不过是大梦一场 各自沉浮

于是纵横万相穷通

也守心底灵通

合眼识得星沉地动

也岿然不动

敢令岁月乌有 逍遥长驻

敢归云间宿

遥祝远行人 有道不孤

点击右上角即可分享
微信分享提示