剑指offer第二版(C++实现)

2023-11-19

剑指offer

2. 面试需要的基础知识

数据结构

数组:二维数组中的查找

https://leetcode.cn/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof/

根据题目描述:每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序
我们先从左下角 x 开始查找,比 x 小,就向上查找,比 x 大,就向右查找
class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
        int i = matrix.size() - 1;
        int j = 0;

        while (i >= 0 && j < matrix[0].size()) {
            if (matrix[i][j] > target) i--;
            else if (matrix[i][j] < target) j++;
            else return true;
        }
        return false;
    }
};

字符串:替换空格

https://leetcode.cn/problems/ti-huan-kong-ge-lcof/

在这里插入图片描述

class Solution {
public:
    string replaceSpace(string s) {
        int count = 0; // 记录空格数
        int oldSize = s.size(); // 原始字符串长度
        for (int i = 0; i < oldSize; i++) {
            if (s[i] == ' ') {
                count ++;
            }
        }

        s.resize(2 * count + oldSize);
        int newSize = s.size();

        for (int p1 = oldSize - 1, p2 = newSize - 1; p1 < p2; p1--, p2--) {
            if (s[p1] != ' ') {
                s[p2] = s[p1];
            } else {
                s[p2] = '0';
                s[p2 - 1] = '2';
                s[p2 - 2]  ='%';
                p2 -= 2;
            }
        }
        return s;
    }
};

链表:从尾到头打印链表

https://leetcode.cn/problems/cong-wei-dao-tou-da-yin-lian-biao-lcof/

从尾到头打印链表,先反转链表再遍历输出是可以的,但是会改变原有链表的结构
可以考虑用栈,先遍历链表,分别进栈,然后再出栈。
用递归也行,递归遍历到最后一个节点,然后再回溯输出,当链表很长时会导致函数调用层级很深,
从而导致函数调用栈移出,鲁棒性太差
class Solution {
public:
    vector<int> reversePrint(ListNode* head) {
        vector<int> res;
        stack<int> st;
        ListNode* node = head;
        while (node != NULL) {
            st.push(node->val);
            node = node->next;
        }

        while (!st.empty()) {
            res.push_back(st.top());
            st.pop();
        }
        return res;
    }
};

树:重建二叉树

https://leetcode.cn/problems/zhong-jian-er-cha-shu-lcof/

class Solution {
private:
    // 存储 inorder 中值到索引的映射
    unordered_map<int, int> map;
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        for (int i = 0; i < inorder.size(); i++) {
            map[inorder[i]] = i; 
        }
        return build(preorder, 0, preorder.size() - 1, 
                    inorder, 0, inorder.size() - 1);
    }

    /*
       定义:前序遍历数组为 preorder[preStart..preEnd],
       中序遍历数组为 inorder[inStart..inEnd],
       构造这个二叉树并返回该二叉树的根节点
    */
    TreeNode* build(vector<int>& preorder, int preStart, int preEnd, 
                    vector<int>& inorder, int inStart, int inEnd) {
        
        if (preStart > preEnd) {
            return nullptr;
        }
        // root 节点对应的值就是前序遍历数组的第一个元素
        int rootval = preorder[preStart];
        // rootVal 在中序遍历数组中的索引
        int index = map[rootval];
        // 先构造出当前根节点
        TreeNode* root = new TreeNode(rootval);
        
        int leftSize = index - inStart;
       
        root->left = build(preorder, preStart + 1, preStart + leftSize, 
                        inorder, inStart, index - 1);
        root->right = build(preorder, leftSize + preStart + 1, preEnd,
                        inorder, index + 1, inEnd);
        return root;
    }
};

栈和队列:用两个栈实现队列

https://leetcode.cn/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof/

用s1用来插入,s2用来删除,删除的时候,先把s1的弹出,放到s2,然后删除s2栈顶元素,再把剩下的放到s1
class CQueue {
private:
    stack<int> s1, s2;
public:
    CQueue() {

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

        int res = s2.top();
        s2.pop();
        while (!s2.empty()) {
            s1.push(s2.top());
            s2.pop();
        }
        return res;
    }
};

算法和数据结构

查找和排序:旋转数组的最小数字

https://leetcode.cn/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof/

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

递归和循环:斐波那契数列

https://leetcode.cn/problems/fei-bo-na-qi-shu-lie-lcof/

保存之前已经得到的数,避免重复计算

class Solution {
public:
    int fib(int n) {
        int mod = 1e9+7;
        if (n == 0 || n == 1) {
            return n;
        }
        int f1 = 1;
        int f0 = 0;
        for (int i = 2; i <= n; i++) {
            long fn = f1 + f0;
            f0 = f1;
            f1 = fn % mod;
        }
        return f1;
    }
};

青蛙跳台阶

class Solution {
public:
    int numWays(int n) {
        int mod = 1e9+7;
        if (n <= 1) return 1;
        int f1 = 1, f0 = 1;
        for (int i = 2; i <= n; i++) {
            long fn = f1 + f0;
            f0 = f1;
            f1 = fn % mod;
        }
        return f1;
    }
};

位运算:二进制中1的个数

https://leetcode.cn/problems/er-jin-zhi-zhong-1de-ge-shu-lcof/

在这里插入图片描述

class Solution {
public:
    int hammingWeight(uint32_t n) {
        int res = 0;
        while (n > 0) {
            n &= n - 1;
            res++;
        }
        return res;
    }
};

3. 高质量代码

数值的整数次方

https://leetcode.cn/problems/shu-zhi-de-zheng-shu-ci-fang-lcof/

class Solution {
public:
    double myPow(double x, int n) {
        long n1 = (long)n; // 防止下面负数处理时溢出
        if (x == 1 || n1 == 0) {
            return 1;
        }
        if (n1 < 0) { // 处理负数
            x = 1 / x;
            n1 = -n1;
        }
        double res = 1;
        while (n1 != 0) {
            if (n1 & 1) {
                res *= x;
            }
            // x = x * x; 就是上一步的 x 进行平方,比如 x = x^2(上一步的x) * x^2;
            x *= x; 
            n1 >>= 1;
        }
        return res;
    }
};

打印1到最大的n位数

https://leetcode.cn/problems/da-yin-cong-1dao-zui-da-de-nwei-shu-lcof/

class Solution {
public:
    vector<int> printNumbers(int n) {
        vector<int> res;
        int num = 1;

        for (int i = 0; i < n; i++) {
            num *= 10;
        }
        for (int i = 1; i < num; i++) {
            res.push_back(i);
        }
        return res;
    }
};

删除链表的节点

https://leetcode.cn/problems/shan-chu-lian-biao-de-jie-dian-lcof/

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

        if (head->val == val) {
            return head->next;
        }
        while (cur != NULL && cur->val != val) {
            pre = cur;
            cur = cur->next;
        }
        // 如果删除的节点在链表里面,则会触发第二个条件退出while 循环
        if (cur != NULL) {
            pre->next = cur->next;
        }
        // 如果不存在要删除的节点,触发第一个条件退出循环,直接返回头结点
        return head;
    } 
};

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

https://leetcode.cn/problems/diao-zheng-shu-zu-shun-xu-shi-qi-shu-wei-yu-ou-shu-qian-mian-lcof/

class Solution {
public:
    vector<int> exchange(vector<int>& nums) {
        int left = 0, right = nums.size() - 1;

        while (left < right) {
            while (left < right && nums[left] % 2 == 1) left++; // 找到偶数退出
            while (left < right && nums[right] % 2 == 0) right--; // 找到奇数退出
            if (left < right) swap(nums[left], nums[right]); // 交换
        }
        return nums;
    }
};

链表中的倒数第K个节点

https://leetcode.cn/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/

为增强鲁棒性
1. 如果输入的链表头指针为NULL,那么整个链表为空,此时查找倒数第K个节点自然应该返回空;
	如果输入的K是0,也即是试图查找倒数第0个节点,由于计数是从1开始的,所以查找0没有意义,返回空
2. 如果链表的节点数少于K,在while 循环中可能会出现指向空指针的下一个指针,所以加一层判断语句
class Solution {
public:
    ListNode* getKthFromEnd(ListNode* head, int k) {
        ListNode* slow = head, *fast = head;

        if (head == NULL || k == 0) { // 1
            return NULL;
        }

        while (k > 0) {
            if (fast != NULL) {  // 2
                fast = fast->next;
            }
            k--;
        }

        while (fast != NULL) {
            slow = slow->next;
            fast = fast->next;
        }

        return slow;
    }
};

反转链表

https://leetcode.cn/problems/fan-zhuan-lian-biao-lcof/

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* pre = NULL;
        ListNode* cur = head;

        while (cur != NULL) {
            ListNode* temp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;
    }
};

合并两个有序的链表

https://leetcode.cn/problems/he-bing-liang-ge-pai-xu-de-lian-biao-lcof/

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode* p1 = l1;
        ListNode* p2 = l2;
        ListNode* dummy = new ListNode(-1);
        ListNode* p = dummy;
        while (p1 != nullptr && p2 != nullptr) {
            if (p1->val > p2->val) {
                p->next = p2;
                p2 = p2->next;
            } else {
                p->next = p1;
                p1 = p1->next;
            }
            p = p->next;
        }
        if (p1 != nullptr) {
            p->next = p1;
        }
        if (p2 != nullptr) {
            p->next = p2;
        }
        return dummy->next;
    }
};

树的子结构

https://leetcode.cn/problems/shu-de-zi-jie-gou-lcof/

class Solution {
public:
    bool compare(TreeNode* A, TreeNode* B) {
        //如果B空A不空(B遍历完了,A还没遍历完,那么B是子结构),或者AB都空(AB都匹配对应上了)
        if(B==NULL){
            return true;
        }
        //如果A空,B不空(如果A遍历完了,B还没匹配完,那么肯定就不是子结构)
        if(A==NULL){
            return false;
        }
        //如果两个都不空,结点值也不同,那直接返回false
        if(A->val!=B->val){
            return false;
        }
        //如果现在结点值和子树结点值相同,再分别检查两个的左右孩子
        return compare(A->left, B->left) && compare(A->right, B->right);

    }
    bool isSubStructure(TreeNode* A, TreeNode* B) {
        //如果B一开始就是空树,而题目要求空树不是子结构,所以直接返回错。
        if(B == NULL) return false;

        //如果要检查的子树不空,但root是空的,那也不用查了,也是错的。
        if(A == NULL) return false;

        //要么是它本身,要么是它左子树,要么是它的右子树
        return compare(A, B) || isSubStructure(A->left, B) || isSubStructure(A->right, B);
    }
};

4. 解决面试题的思路

二叉树的镜像

https://leetcode.cn/problems/er-cha-shu-de-jing-xiang-lcof/

在这里插入图片描述

class Solution {
public:
    TreeNode* mirrorTree(TreeNode* root) {
        if (root == NULL) return NULL;
        // 避免交换空子树
        if (root != NULL && (root->left == NULL && root->right == NULL)) return root;
		
        TreeNode* temp = root->left;
        root->left = root->right;
        root->right = temp;

        if (root->left) mirrorTree(root->left);
        if (root->right) mirrorTree(root->right);
        return root;
    }
};

顺时针打印矩阵

https://leetcode.cn/problems/shun-shi-zhen-da-yin-ju-zhen-lcof/

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        if (matrix.empty()) return {};
        vector<int> res;
        int top = 0, bottom = matrix.size() - 1; // 上下边界
        int left = 0, right = matrix[0].size() - 1; // 左右边界
        while (true) {
            // 从左向右
            for (int i = left; i <= right; i++) {
                res.push_back(matrix[top][i]);
            }
            // 每次从左往右执行一次,则要往下移一层
            if (++top > bottom) break;

            //从上向下
            for (int i = top; i <= bottom; i++) {
                res.push_back(matrix[i][right]);
            }
            // 每次从上到下执行一次,则要往左移一列
            if (--right < left) break;

            // 从右向左
            for (int i = right; i >= left; i--) {
                res.push_back(matrix[bottom][i]);
            }
            // 每次从右到左执行一次,则要往上移一行
            if (--bottom < top) break;

            // 从下向上
            for (int i = bottom; i >= top; i--) {
                res.push_back(matrix[i][left]);
            }
            // 每次从下到上执行一次,则要往右移一列
            if (++left > right) break;
        }
        return res;
    }
};

包含min函数的栈

https://leetcode.cn/problems/bao-han-minhan-shu-de-zhan-lcof/

把每次最小的元素都保存起来放到另一个辅助栈里
class MinStack {
private:
    stack<int> dataS;
    stack<int> minS;
public:
    /** initialize your data structure here. */
    MinStack() {
        minS.push(INT_MAX);
    }
    
    void push(int x) {
        dataS.push(x);
        minS.push(std::min(minS.top(), x));
    }
    
    void pop() {
        dataS.pop();
        minS.pop();
    }
    
    int top() {
        return dataS.top();
    }
    
    int min() {
        return minS.top();
    }
};

栈的压入弹出序列

https://leetcode.cn/problems/zhan-de-ya-ru-dan-chu-xu-lie-lcof/

1. 如果下一个弹出的数字刚好是栈顶数字,那么直接弹出;
2. 如果下一个弹出的数字不在栈顶,把压栈序列中还没有入栈的数字压入辅助栈,
   直到把下一个需要弹出的数字压入栈顶为止。
3. 如果所有的数字都压入栈了,仍然没有找到下一个弹出的数字,那么该序列不可能是一个弹出序列。
class Solution {
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
        stack<int> helpS;
        int i = 0;
        for (int num : pushed) {
            helpS.push(num);
            while (!helpS.empty() && helpS.top() == popped[i]) {
                helpS.pop();
                i++;
            }
        }
        return helpS.empty();
    }
};

从上往下打印二叉树

https://leetcode.cn/problems/cong-shang-dao-xia-da-yin-er-cha-shu-lcof/

使用队列辅助打印,打印根节点时,将子节点存入队列中

在这里插入图片描述

class Solution {
private:
    vector<int> res;
    queue<TreeNode*> queue;
public:
    vector<int> levelOrder(TreeNode* root) {
        if (root != NULL) queue.push(root);

        while (!queue.empty()) {
            int size = queue.size();

            for (int i = 0; i < size; i++) {
                TreeNode* node = queue.front();
                queue.pop();

                res.push_back(node->val);
                if (node->left) queue.push(node->left);
                if (node->right) queue.push(node->right);
            }
        }
        return res;
    }
};

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

https://leetcode.cn/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof/

在这里插入图片描述

class Solution {
public:
    vector<int> res;
    bool verifyPostorder(vector<int>& postorder) {
        res = postorder;
        return dfs(0, postorder.size() - 1);
    }
    bool dfs(int l, int r)
    {
        if(l >= r) return true;//退出条件
        int root = res[r];//最后一个点是根结点
        int k = l;//从最左边开始
        while(k < r && res[k] < root) k++;//符合左子树的点
        for(int i = k; i < r; i++)//此时的k是右子树的第一个点
        {
            if(res[i] < root)//如果右子树小于根,说明不符合
            return false;
        }
        return dfs(l, k - 1) && dfs(k, r - 1);//逐步缩小范围
    }
};

二叉树中和为某一值的路径

https://leetcode.cn/problems/er-cha-shu-zhong-he-wei-mou-yi-zhi-de-lu-jing-lcof/

1. 当用前序的方式访问到某一节点时,我们把该节点添加到路径上,并累加该节点的值
2. 如果该节点为叶子节点并且路径中的节点值刚好等于目标值,则当前的路径符合要求。
3. 如果当前节点不是叶子节点,则继续访问它的子节点。
4. 当前节点访问结束后,递归函数将自动回到它的父节点。因此我们在函数退出之前要在路径上删除当前节点,
   并减去当前节点的值,以确保返回父节点时路径,刚好是从根节点到父节点的路径
class Solution {
public:
    vector<vector<int>> res; // 最终结果
    vector<int> path; // 一次结果的路径
    vector<vector<int>> pathSum(TreeNode* root, int target) {
        if (root == nullptr) return res;

        traverse(root, target, path);
        return res;
    }
    // 递归遍历寻找合适的路径
    void traverse(TreeNode* root, int target, vector<int>& path) {
        if (root == nullptr) return;

        int remain = target - root->val; // 减去当前节点值
        if (root->left == nullptr && root->right == nullptr) { // 到达叶子节点
            if (remain == 0) { // 判断是否刚好等于目标值
                path.push_back(root->val);
                res.push_back(path);
                path.pop_back();
            }
            return;
        }
        
        // 没有到达叶子节点,继续寻找子节点
        path.push_back(root->val);
        traverse(root->left, remain, path);
        path.pop_back(); // 返回父节点时要还原路径

        path.push_back(root->val);
        traverse(root->right, remain, path);
        path.pop_back();
    }
};

复杂链表的复制

https://leetcode.cn/problems/fu-za-lian-biao-de-fu-zhi-lcof/

class Solution {
public:
    Node* copyRandomList(Node* head) {
        if(head == nullptr) return nullptr;
        Node* cur = head;
        unordered_map<Node*, Node*> map;
        // 3. 复制各节点,并建立 “原节点 -> 新节点” 的 Map 映射
        while(cur != nullptr) {
            map[cur] = new Node(cur->val);
            cur = cur->next;
        }
        cur = head;
        // 4. 构建新链表的 next 和 random 指向
        while(cur != nullptr) {
            map[cur]->next = map[cur->next];
            map[cur]->random = map[cur->random];
            cur = cur->next;
        }
        // 5. 返回新链表的头节点
        return map[head];
    }
};

二叉搜索树与双向链表

https://leetcode.cn/problems/er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof/

class Solution {
public:
    Node* pre = nullptr, *head = nullptr;
    Node* treeToDoublyList(Node* root) {
        if (!root) return root;
        dfs(root);
        head->left = pre;
        pre->right = head;
        return head;
    }
    void dfs(Node* root){
        if (!root) return;// 递归边界: 叶子结点返回
        dfs(root->left);  //左子树
        if (pre) pre->right = root;
        else head = root; // 保存链表头结点
        root->left = pre; 
        pre = root;
        dfs(root->right); //右子树
    }
};

字符串的排列

https://leetcode.cn/problems/zi-fu-chuan-de-pai-lie-lcof/

class Solution {
public:
    vector<string> res;
    vector<string> permutation(string s) {
        dfs(s, 0); // 从第0个字符开始
        return res;
    }
    
    void dfs(string s, int start) {
        if(start == s.size() - 1) {
            res.push_back(s);                       // 添加排列方案
            return;
        }
        set<int> st; // 就起个去重的作用
        for(int i = start; i < s.size(); i++) {
            if(st.find(s[i]) != st.end()) continue; // 重复,因此剪枝
            st.insert(s[i]);
            swap(s[i], s[start]);                       // 交换,将 s[i] 固定在第 start 位
            dfs(s, start + 1);                          // 开启固定第 start + 1 位字符
            swap(s[i], s[start]);                       // 恢复交换
        }
    }
};

5. 优化时间和空间效率

数组中出现次数超过一般的数字

https://leetcode.cn/problems/shu-zu-zhong-chu-xian-ci-shu-chao-guo-yi-ban-de-shu-zi-lcof/

票数抵消法:

在这里插入图片描述

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

最小的K个数

https://leetcode.cn/problems/zui-xiao-de-kge-shu-lcof/

class Solution {
public:
    vector<int> res;
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        if (arr.empty() || k == 0) return res;
        sort(arr.begin(), arr.end());
        for (int num : arr) {
            if (k <= 0) break;
            res.push_back(num);
            k--;
        }
        return res;
    }
};

连续子数组的最大和

https://leetcode.cn/problems/lian-xu-zi-shu-zu-de-zui-da-he-lcof/

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int preSum = 0; // 前缀和
        int maxSum = nums[0];// 最大和
        for (int num : nums) {
            preSum = max(preSum + num, num);
            maxSum = max(preSum, maxSum);
        }
        return maxSum;
    }
};

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

https://leetcode.cn/problems/1nzheng-shu-zhong-1chu-xian-de-ci-shu-lcof/

class Solution {
public:
    int countDigitOne(int n) {
        long digit = 1;
        int res = 0;
        int high = n / 10, low = 0, cur = n % 10;
        while (high != 0 || cur != 0) {
            if (cur == 0) {
                res += high * digit;
            } else if (cur == 1) {
                res += high * digit + low + 1;
            } else {
                res += (high + 1) * digit;
            }
            low += cur * digit;
            cur = high % 10;
            high /= 10;
            digit *= 10;
        }
        return res;
    }
};

把数组排成最小的数

https://leetcode.cn/problems/ba-shu-zu-pai-cheng-zui-xiao-de-shu-lcof/

class Solution {
public:
    string minNumber(vector<int>& nums) {
        vector<string> strs;
        for (int i = 0; i < nums.size(); i++) {
            strs.push_back(to_string(nums[i])); // 将数字常量转换成字符串,再存入字符串数组里
        }
        quickSort(strs, 0, strs.size() - 1);
        string res;
        for (string str : strs) {
            res.append(str);
        }
        return res;
    }
private:
    void quickSort(vector<string>& strs, int l, int r) {
        if (l >= r) return;
        int i = l, j = r;
        while (i < j) {
            while (strs[j] + strs[l] >= strs[l] + strs[j] && i < j) j--;
            while (strs[i] + strs[l] <= strs[l] + strs[i] && i < j) i++;
            swap(strs[i], strs[j]);
        }
        swap(strs[i], strs[l]);
        quickSort(strs, l, i - 1);
        quickSort(strs, i + 1, r);
    }
};

丑数

https://leetcode.cn/problems/chou-shu-lcof/

丑数的递推性质: 丑数只包含因子 2, 3, 5
因此有 “丑数 = 某较小丑数 × 某因子” (例如:10 = 5×2)。
class Solution {
public:
    int nthUglyNumber(int n) {
        int a = 0, b = 0, c = 0;
        int dp[n];
        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] = min(min(n2, n3), n5);
            if (dp[i] == n2) a++;
            if (dp[i] == n3) b++;
            if (dp[i] == n5) c++;
        }
        return dp[n - 1];
    }
};

第一个只出现一次的字符

https://leetcode.cn/problems/di-yi-ge-zhi-chu-xian-yi-ci-de-zi-fu-lcof/

在这里插入图片描述

class Solution {
public:
    char firstUniqChar(string s) {
        unordered_map<char, int> map;
        for (int i = 0; i < s.size(); i++) {
            map[s[i]]++;
        }
        for (char str : s) {
            if (map[str] == 1)
                return str;
        }
        return ' ';
    }
};

两个链表的第一个公共节点

https://leetcode.cn/problems/liang-ge-lian-biao-de-di-yi-ge-gong-gong-jie-dian-lcof/

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* p1 = headA, *p2 = headB;

        while (p1 != p2) {
            if (p1 == nullptr) {
                p1 = headB;
            } else {
                p1 = p1->next;
            } 
            if (p2 == nullptr) {
                p2 = headA;
            } else {
                p2 = p2->next;
            }
        }
        return p1;
    }
};

6. 面试中的各项能力

https://leetcode.cn/problems/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-lcof/

数字在排序数组中出现的次数

class Solution {
public:
    int search(vector<int>& nums, int target) {
        
        // 找左边界
        int left = 0, right = nums.size() - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) left = mid + 1;
            else if (nums[mid] > target) right = mid - 1;
            else right = mid - 1;
        }
        // 当target 比所有的元素都大,left会越界
        if (left >= nums.size() || nums[left] != target) return 0;
        int left_bound = left;

        // 找右边界
        left = 0, right = nums.size() - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) left = mid + 1;
            else if (nums[mid] > target) right = mid - 1;
            else left = mid + 1;
        }
        //  当target 比所有的元素都小,right会越界
        if (right < 0 || nums[right] != target) return 0;
        int right_bound = right;

        return right_bound - left_bound + 1;
    }
};

二叉树的深度

https://leetcode.cn/problems/er-cha-shu-de-shen-du-lcof/

class Solution {
public:
    int maxDepth(TreeNode* root) {
        if (root == NULL) return 0;
        return max(maxDepth(root->left), maxDepth(root->right)) + 1;
    }
};

平衡二叉树

https://leetcode.cn/problems/ping-heng-er-cha-shu-lcof/

class Solution {
public:
    bool isBalanced(TreeNode* root) {
        if (root == nullptr) return true;
        return (abs(depth(root->left) - depth(root->right)) <= 1)
            && isBalanced(root->left) && isBalanced(root->right);
    }

    int depth(TreeNode* root) {
        if (root == 0) return 0;
        return max(depth(root->left), depth(root->right)) + 1;
    }
};

数组中只出现一次的数字

https://leetcode.cn/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/

在这里插入图片描述

class Solution {
public:
    vector<int> singleNumbers(vector<int>& nums) {
        int x = 0, y = 0; // 两个只出现一次的数字
        int n = 0; // 用来和数组中的每一位异或,最后得n = x ^ y
        int b = 1; // 用来配合 n 分出两个子数组
        for (int num : nums) {
            n ^= num;
        }
        // 此时 n = x ^ y, 现在把数组分成两个子数组
        while ((n & b) == 0) { // 找到第一个为1的位的位置
            b <<= 1;
        }

        // 遍历数组,让数字都与 b 异或,分离出两个数组
        for (int num : nums) {
            if (num & b) x ^= num;
            else y ^= num;
        }
        return {x, y};
    }
};

和为s的两个数字

https://leetcode.cn/problems/he-wei-sde-liang-ge-shu-zi-lcof/

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int i = 0, j = nums.size() - 1;
        while (i < j) {
            int s = nums[i] + nums[j];
            if (s < target) i++;
            else if (s > target) j--;
            else return {nums[i], nums[j]};
        }
        return {0};
    }
};

和为s的连续整数序列
https://leetcode.cn/problems/he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof/

class Solution {
public:
    vector<vector<int>> res;
    vector<vector<int>> findContinuousSequence(int target) {
        int i = 1, j = 2, s = 3;
        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;
    }
};

翻转单词顺序

https://leetcode.cn/problems/fan-zhuan-dan-ci-shun-xu-lcof/

class Solution {
public:
    string reverseWords(string s) {
        int i = s.size() - 1;
        string res;
        while (i >= 0) { // 遍历 s 
            int c = 0; // 记录一个单词的长度
            while (i >= 0 && s[i] == ' ') { // 遍历空格,
                i--;
            }
            while (i >= 0 && s[i] != ' ')  { // 遍历查找一个单词
                i--;
                c++;
            }
            if (c) {
                res += s.substr(i + 1, c) + " ";
                // substr 返回由 i+1 开始的 c 个字符组成的字符串
                // 返回一个单词,后面紧跟一个空格分隔
            }
        }
        return res.substr(0, res.size() - 1); // 最后一个单词后面的空格不要
    }
};

左旋转字符串

https://leetcode.cn/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/

局部翻转+整体翻转
class Solution {
public:
    string reverseLeftWords(string s, int n) {
        /* 反转n前面的字符串 */
        reverse(s.begin(), s.begin() + n);
        /* 反转n后面的字符串 */
        reverse(s.begin() + n, s.end());
        /* 反转整个字符串 */
        reverse(s.begin(), s.end());

        return s;
    }
};

扑克牌中的顺子

https://leetcode.cn/problems/bu-ke-pai-zhong-de-shun-zi-lcof/

1. 首先把数组排序
2. 统计数组中0的个数
3. 统计排序之后的数组中相邻数字之间的空缺总数
4. 如果空缺总数小于等于0的个数,那么这个数组就是连续的;反之不连续
5. 如果数组中的非0数字重复出现,则该数组是不连续的。
class Solution {
public:
    bool isStraight(vector<int>& nums) {
        int count = 0; // 0 的个数
        sort(nums.begin(), nums.end()); // 数组排序
        for (int i = 0; i < 4; i++) {
            if (nums[i] == 0) count++; // 统计大小王数量
            else if (nums[i] == nums[i + 1]) return false;  // 有重复数字不是顺子
        }
        return  nums[4] - nums[count] < 5; //  最大牌 - 最小牌 < 5 则可构成顺子
    }
};

圆圈中最后剩下的数字

https://leetcode.cn/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/

约瑟夫环

在这里插入图片描述

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

求1+2+3+…n

https://leetcode.cn/problems/qiu-12n-lcof/

class Solution {
public:
    int res = 0;
    int sumNums(int n) {
        bool x = n > 1 && sumNums(n - 1) > 0;
        res += n;
        return res;
    }
};

不用加减乘除做加法

https://leetcode.cn/problems/bu-yong-jia-jian-cheng-chu-zuo-jia-fa-lcof/

1. 不考虑进位。0加0,1加1的结果都是0,0加1,1加0的结果都是1。这和异或的结果是一样的
2. 考虑进位。对0加0,0加1,1加0而言,都不会产生进位,只有1加1会向前产生一个进位。
   此时可以想象成两个数先做位与运算,然后再左移一位。
3. 把前面两个数相加,重复前两步,直到不产生进位为止。
class Solution {
public:
    int add(int a, int b) {
        int sum = 0, carry = 0;
        while (b) {
            sum = a ^ b; 
            carry = (unsigned int)(a & b) << 1;

            a = sum;
            b = carry;
        }
        return a;
    }
};

7. 面试案例

二叉搜索树的最近公共祖先

https://leetcode.cn/problems/er-cha-sou-suo-shu-de-zui-jin-gong-gong-zu-xian-lcof/

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (root == nullptr) return nullptr;
        if (p->val > q->val) {
            // 保证 p.val <= q.val,便于后续情况讨论
            return lowestCommonAncestor(root, q, p);
        }

        if (root->val >= p->val && root->val <= q->val) {
            // p <= root <= q
            // 即 p 和 q 分别在 root 的左右子树,那么 root 就是 LCA
            return root;
        }
        if (root->val > q->val) {
            // p 和 q 都在 root 的左子树,那么 LCA 在左子树
            return lowestCommonAncestor(root->left, p, q);
        } else {
            // p 和 q 都在 root 的右子树,那么 LCA 在右子树
            return lowestCommonAncestor(root->right, p, q);
        }
    }
};

二叉树的最近公共祖先

https://leetcode.cn/problems/er-cha-shu-de-zui-jin-gong-gong-zu-xian-lcof/

在这里插入图片描述

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (root == NULL || root == p || root == q) return root;

        TreeNode* left = lowestCommonAncestor(root->left, p, q);
        TreeNode* right = lowestCommonAncestor(root->right, p, q);

        if (left != NULL && right != NULL) { //2
            return root;
        } 
        if (left == NULL && right == NULL) { //1
            return NULL;
        }
        return left == NULL ? right : left; //3,4
    }
};
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

剑指offer第二版(C++实现) 的相关文章

随机推荐

  • 芯片制造系列全流程:设计、制造、封测

    目录 芯片制造系列全流程 简 一 芯片制造全流程简介 二 芯片设计 三 芯片制造 四 封装测试 芯片目前分为三个主要环节 分别是设计 制程 封测 设计水平 制造这一块 最后说说封测这一块 芯片设计 芯片制造 封装测试完整解读 01 芯 片
  • 手把手教你安装CUDA(一看就会)

    1 背景 学习深度学习的话 肯定需要安装PyTorch和TensorFlow 安装这两个深度学习框架之前得安装CUDA CUDA是什么 CUDA是一个并行计算平台和编程模型 能够使得使用GPU进行通用计算变得简单和优雅 Nvidia官方提供
  • 树状数组笔记

    数组 前缀和 树状数组的区别 数组 修改某点O 1 求区间O n 前缀和 修改某点O n 求区间O 1 树状数组 修改某点O logn 求区间O logn 树状数组采取折中的方式 降低整体的时间复杂度 由于算法复杂度取决于最坏的情况的复杂度
  • 1.vs2019 配置Eigen

    目录 一 下载Eigen 二 创建工程 三 测试代码 四 运行结果 一 下载Eigen 下载地址 http eigen tuxfamily org index php title Main Page Download 下载后 将文件解压 二
  • Python--pytesseract验证码识别处理实例

    linux ubuntu系统 安装过程 pytesser 调用了 tesseract 因此需要安装 tesseract 安装 tesseract 需要安装 leptonica 否则编译tesseract 的时候出现 configure er
  • mysql 自定义函数 if not exists_IF配合AND、OR以及NOT函数使用,可以解决工作中的不少难题...

    前面小编已经分别介绍了逻辑判断函数IF AND OR及NOT的用法 同时也提到它们比较少单独使用 那么 这篇文章我们就来介绍一下IF分别和AND OR及NOT的配合用法 1 函数定义回顾 首先来回顾下这4个逻辑判断函数的定义 1 IF函数
  • 每日一题:整齐的数组

    整齐的数组 题目 Daimayuan Online Judge 每一次可以选择一个ai减去k 可以进行若干次操作 使得所有数变相同 说明跟顺序无关 可以从小到大排个序 k大于等于1 说明了每个数只能变小不能变大 那么每个数只能变得和最小的那
  • Android-系统分享使用小结

    Android 系统分享使用小结 概述 如何进行分享 如何筛选分享项 如何区分部分APP下不同分享界面 以微信为例 如何还原过滤前APP分享途径的描述 概述 说到分享 有很多第三方的SDK可供使用 比如友盟 mob都很好用 虽然集成相对容易
  • netcore 文件服务器,在 ASP.NET Core 中上传文件

    ASP NET Core 支持使用缓冲的模型绑定 针对较小文件 和无缓冲的流式传输 针对较大文件 上传一个或多个文件 安全注意事项 向用户提供向服务器上传文件的功能时 必须格外小心 攻击者可能会尝试执行以下操作 执行拒绝服务攻击 上传病毒或
  • supervisor

    使用 安装配置 待续 服务配置 program g7service command bin bash c dotnet YH TaskManager Collect dll directory home service g7 stderr
  • Ubuntu20.04美化成mac 系统样式

    一 效果 二 安装源 1 sudo gedit etc apt sources list deb http mirrors 163 com ubuntu focal main restricted deb http mirrors 163
  • STM32例程之USB HID双向数据传输

    http www viewtool com bbs forum php mod viewthread tid 199 extra page 3D1 程序功能 将STM32的USB枚举为HID设备 STM32使用3个端点 端点0用于枚举用 端
  • 磁盘性能测试相关基础知识

    fio name disk test ioengine libaio direct 1 thread 1 norandommap 1 randrepe at 0 runtime 100 ramp time 6 size 16g filena
  • 在Mac中设置Ctrl+C/V进行复制/粘贴

    从Windows世界走入Mac世界 最让不习惯的是在Mac中 复制 粘贴 的快捷键是Command C V 而且Command键与C V键靠得太近 只能用大拇指与食指进行操作 也让人不习惯 再加上远程桌面连接至Windows时 只能用Ctr
  • Qt中moc问题(qt moc 处理 cpp)

    Qt编译常见的错误 编译报错 1 gt Linking 1 gt cmmwindow obj error LNK2001 unresolved external symbol public virtual struct QMetaObjec
  • 【Flutter 问题系列第 75 篇】Flutter 中 pubspec.yaml 配置文件的说明

    这是 Flutter 问题系列第 75 篇 如果觉得有用的话 欢迎关注专栏 文章目录 一 问题描述 二 属性详解 name description version environment dependencies dev dependenc
  • 向数据库插入数据报错Error updating database. Cause: java.sql.SQLException: Incorrect string value: '\xE5\xA4\

    之前连接数据库都没问题 可是今天新加一个表之后 向这个表中加入数据就报错 2018 08 25 14 54 59 082 WARN 8136 nio 8090 exec 7 m m a ExceptionHandlerExceptionRe
  • unity game界面按下play会不断闪烁,按下暂停键(pause)或者中止/下一步(step),game界面的画面会接连变化

    没找到答案 改了两个下午的程序 改完还是这样 后来发现是FixedUpdate Update与OnDrawGizmos的问题 OnDrawGizmos是每帧都会绘制 用FixedUpdate理所当然就那啥了 分析的时候 就突然想到是不是这俩
  • JWT token心得与使用实例

    本文你能学到什么 token的组成 token串的生成流程 token在客户端与服务器端的交互流程 Token的优点和思考 参考代码 核心代码使用参考 不是全部代码 JWT token的组成 头部 Header 格式如下 typ JWT a
  • 剑指offer第二版(C++实现)

    剑指offer 2 面试需要的基础知识 数据结构 数组 二维数组中的查找 字符串 替换空格 链表 从尾到头打印链表 树 重建二叉树 栈和队列 用两个栈实现队列 算法和数据结构 查找和排序 旋转数组的最小数字 递归和循环 斐波那契数列 位运算