剑指Offer题解合集

剑指Offer题单及题解

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

JZ3、数组中重复的数字

分析

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

代码实现

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、斐波那契数列

分析

没什么可说的,\(n \leq 39\)直接暴力即可,甚至不用开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位数

分析

直接开一个\(10^n - 1\)大小的数组从头开始赋值即可。

代码实现

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个数

分析

排序一下让后删除最后的\(n-k\)个数即可

代码实现

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 @ 2024-07-29 18:55  sleeeeeping  阅读(6)  评论(0编辑  收藏  举报