Google面试题II

LeetCode 66. Plus One

给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储一个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头。

示例 1:

输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。

示例 2:

输入: [4,3,2,1]
输出: [4,3,2,2]
解释: 输入数组表示数字 4321。

class Solution {
public:
    //高精度加法
    vector<int> plusOne(vector<int>& digits) {
        int t = 1;//进位
        for(int i = digits.size() - 1; i >= 0; i--)
        {
            int &x = digits[i];
            t += x;
            x = t % 10;
            t /= 10;
        }
        //最后还有进位,给最高位补上一
        if(t)
        {
            digits.push_back(0);//在最后补上一位,下面数组往后移一位
            for(int i = digits.size() - 1; i > 0; i--) digits[i] = digits[i - 1];
            digits[0] = 1;
        }
        return digits;
    }
};

LeetCode 326. Power of Three

给定一个整数,写一个函数来判断它是否是 3 的幂次方。

示例 1:

输入: 27
输出: true

示例 2:

输入: 0
输出: false

示例 3:

输入: 9
输出: true

示例 4:

输入: 45
输出: false

进阶:
你能不使用循环或者递归来完成本题吗?

class Solution {
public:
    /*
    int范围内 3^19 = 1162261467 以内最大3的整数幂 能整除 n 就是3的整数幂 
    */
    bool isPowerOfThree(int n) {
        return n > 0 && 1162261467 % n == 0;
    }
};

LeetCode 883. Projection Area of 3D Shapes

在 N * N 的网格中,我们放置了一些与 x,y,z 三轴对齐的 1 * 1 * 1 立方体。

每个值 v = grid[i][j] 表示 v 个正方体叠放在单元格 (i, j) 上。

现在,我们查看这些立方体在 xy、yz 和 zx 平面上的投影。

投影就像影子,将三维形体映射到一个二维平面上。

在这里,从顶部、前面和侧面看立方体时,我们会看到“影子”。

返回所有三个投影的总面积。

示例 1:

输入:[[2]]
输出:5

示例 2:

输入:[[1,2],[3,4]]
输出:17
解释:
这里有该形体在三个轴对齐平面上的三个投影(“阴影部分”)。

(图)

示例 3:

输入:[[1,0],[0,2]]
输出:8

示例 4:

输入:[[1,1,1],[1,0,1],[1,1,1]]
输出:14

示例 5:

输入:[[2,2,2],[2,1,2],[2,2,2]]
输出:21

提示:

1 <= grid.length = grid[0].length <= 50
0 <= grid[i][j] <= 50

class Solution {
public:
    int projectionArea(vector<vector<int>>& grid) {
        int n = grid.size(), m = grid[0].size();
        if(!n || !m) return 0;
        int res = 0;
        for(int i = 0; i < n; i++)
            for(int j = 0; j < m; j++)
                //统计非0个数 从顶部看
                res += !!grid[i][j]; //第一次非 把0->1 非0->0 第二次非 1->0 0->1
        
        //从前看 累积求每一列的最大高度
        for(int i = 0; i < n; i ++)
        {
            int h = 0;
            for(int j = 0; j < m; j++) h = max(h, grid[i][j]);
            res += h;
        }
        
        //从侧面看 与上面对称
        for(int j = 0; j < m; j ++)
        {
            int h = 0;
            for(int i = 0; i < n; i++) h = max(h, grid[i][j]);
            res += h;
        }
        return res;
    }
};

LeetCode 230. Kth Smallest Element in a BST

给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素。

说明:
你可以假设 k 总是有效的,1 ≤ k ≤ 二叉搜索树元素个数。

示例 1:

输入: root = [3,1,4,null,2], k = 1

   3
  / \
 1   4
  \
   2

输出: 1

示例 2:

输入: root = [5,3,6,2,4,null,null,1], k = 3

       5
      / \
     3   6
    / \
   2   4
  /
 1

输出: 3

进阶:
如果二叉搜索树经常被修改(插入/删除操作)并且你需要频繁地查找第 k 小的值,你将如何优化 kthSmallest 函数?

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

left = 左子树节点个数

1.if(k <= left) dfs(root->left, k); 递归遍历左子树求第k大元素
2.if k == left + 1 return root
3.if k > left + 1 dfs(root_right, k - left - 1); k减去左子树 减去根节点


*/
class Solution {
public:
    //二叉搜索树 中序遍历从小到大排好序 即返回中序遍历的第k小元素
    int kthSmallest(TreeNode* root, int k) {
        return dfs(root, k);
    }
    
    int dfs(TreeNode* root, int &k) //加上&引用符号 没遍历一次减少k
    {
        if(!root) return 0;
        int left = dfs(root->left, k);
        if(k <= 0) return left; //k<0说明第k个点在左子树里面
        if(--k == 0) return root->val;//k = 1 答案根节点
        return dfs(root->right, k);
    }
};

LeetCode 139. Word Break

给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。

说明:

拆分时可以重复使用字典中的单词。
你可以假设字典中没有重复的单词。

示例 1:

输入: s = “leetcode”, wordDict = [“leet”, “code”]
输出: true
解释: 返回 true 因为 “leetcode” 可以被拆分成 “leet code”。

示例 2:

输入: s = “applepenapple”, wordDict = [“apple”, “pen”]
输出: true
解释: 返回 true 因为 “applepenapple” 可以被拆分成 “apple pen apple”。
注意你可以重复使用字典中的单词。

示例 3:

输入: s = “catsandog”, wordDict = [“cats”, “dog”, “sand”, “and”, “cat”]
输出: false

/*

初始化: f[0] = true

状态表示:f[i] 表示前i个字母,是否存在一种合法的划分方案

状态转移:f[i] = true 当且仅当,存在某个j,使得s[j ... i] in wordDict,且f[j - 1] = true  

*/
class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> S;
        for(auto word : wordDict) S.insert(word);        
        
        int n = s.size();        
        vector<bool> f(n + 1, false);
        f[0] = true;
        
        for(int i = 1; i <= n; i++)
            for(int j = 0; j < i; j++)
                if(S.count(s.substr(j, i - j)) && f[j])
                {
                    f[i] = true;
                    break;
                }
        return f[n];
    }
};

LeetCode 930. Binary Subarrays With Sum

在由若干 0 和 1 组成的数组 A 中,有多少个和为 S 的非空子数组。

示例:

输入:A = [1,0,1,0,1], S = 2
输出:4
解释:
如下面黑体所示,有 4 个满足题目要求的子数组:
[1,0,1,0,1]
[1,0,1,0,1]
[1,0,1,0,1]
[1,0,1,0,1]

提示:

A.length <= 30000
0 <= S <= A.length
A[i] 为 0 或 1

/*

1.求前缀和 s[i] = A[0] + A[1] +...+ A[i - 1]

f[i] 表示当前有多少个j 满足s[j] = i 即表示s[]里面有多少个数的值是i

2.枚举 s[i] - s[j] = s a[j + 1... i]里面1的个数
       s[i] - s = s[j] => f[s[i] - s]

*/
class Solution {
public:
    int numSubarraysWithSum(vector<int>& A, int S) {
        int n = A.size();
        vector<int> sum(n + 1, 0), f(n + 1, 0);
        
        for(int i = 0; i < n; i++) sum[i + 1] = sum[i] + A[i];
        f[0] = 1;
        
        int res = 0;
        for(int i = 1; i <= n; i++)
        {
            if(sum[i] >= S) res += f[sum[i] - S];
            f[sum[i]] ++;
        }
        return res;
    }
};

LeetCode 228. Summary Ranges

给定一个无重复元素的有序整数数组,返回数组区间范围的汇总。

示例 1:

输入: [0,1,2,4,5,7]
输出: [“0->2”,“4->5”,“7”]
解释: 0,1,2 可组成一个连续的区间; 4,5 可组成一个连续的区间。

示例 2:

输入: [0,2,3,4,6,8,9]
输出: [“0”,“2->4”,“6”,“8->9”]
解释: 2,3,4 可组成一个连续的区间; 8,9 可组成一个连续的区间。

class Solution {
public:
    
    string rangeToString(int st, int ed)
    {
        if(st == ed) return to_string(st);
        return to_string(st) + "->" + to_string(ed);
    }
    
    vector<string> summaryRanges(vector<int>& nums) {
        vector<string> res;
        if(nums.empty()) return res;
        int st = nums[0], ed = nums[0];
        for(int i = 1; i < nums.size(); i ++)
        {
            int x = nums[i];
            if(x > ed + 1)//断裂的区间
            {
                res.push_back(rangeToString(st, ed));
                st = ed = x;
            }
            else ed++;
        }
        res.push_back(rangeToString(st, ed));
        return res;
    }
};

LeetCode 57. Insert Interval

给出一个无重叠的 ,按照区间起始端点排序的区间列表。

在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。

示例 1:

输入: intervals = [[1,3],[6,9]], newInterval = [2,5]
输出: [[1,5],[6,9]]

示例 2:

输入: intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]
输出: [[1,2],[3,10],[12,16]]
解释: 这是因为新的区间 [4,8] 与 [3,5],[6,7],[8,10] 重叠。

class Solution {
public:
    vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {
        vector<vector<int>> res;
        bool has_in = false;
        for(auto interval : intervals)
        {
            if(interval[0] > newInterval[1]) //当前区间大于新区间右端点
            {                
                if(!has_in)//如果新区间没有被插入 就插入
                {
                    res.push_back(newInterval);
                    has_in = true;
                }
                res.push_back(interval);                    
            }
            else if(interval[1] < newInterval[0])//当前区间小于新区间左端点
            {
                res.push_back(interval);                 
            }
            else //中间求并集
            {
                newInterval[0] = min(newInterval[0], interval[0]);
                newInterval[1] = max(newInterval[1], interval[1]);
            }
        }
        if(!has_in) //新区间在所有区间的后面
            res.push_back(newInterval);
        return res;
    }
};

LeetCode 224. Basic Calculator

实现一个基本的计算器来计算一个简单的字符串表达式的值。

字符串表达式可以包含左括号 ( ,右括号 ),加号 + ,减号 -,非负整数和空格 。

示例 1:

输入: “1 + 1”
输出: 2

示例 2:

输入: " 2-1 + 2 "
输出: 3

示例 3:

输入: “(1+(4+5+2)-3)+(6+8)”
输出: 23

说明:

你可以假设所给定的表达式都是有效的。
请不要使用内置的库函数 eval。

class Solution {
public:
    //两个栈 一个操作符栈 一个数字栈
    
    void calc(stack<char> &op, stack<int> &num) //把栈顶两个元素拿出来操作
    {
        int y = num.top();
        num.pop();
        int x = num.top();
        num.pop();
        if(op.top() == '+') num.push(x + y);
        else num.push(x - y);
        op.pop();
    }
    
    int calculate(string s) {
        stack<char> op;
        stack<int> num;
        
        for(int i = 0; i < s.size(); i++)
        {
            char c = s[i];
            if(c == ' ') continue;
            if(c == '(' || c == '+' || c == '-') op.push(c);
            else if(c == ')')
            {
                op.pop();
                if(op.size() && op.top() != '(')
                {
                    calc(op, num);
                }
            }
            else
            {
                int j = i;
                while(j < s.size() && isdigit(s[j])) j++;
                num.push(atoi(s.substr(i, j -  i).c_str()));
                i = j - 1;
                if(op.size() && op.top() != '(')
                    calc(op, num);
            }            
        }
        return num.top();
    }
};

LeetCode 857. Minimum Cost to Hire K Workers

有 N 名工人。 第 i 名工人的工作质量为 quality[i] ,其最低期望工资为 wage[i] 。

现在我们想雇佣 K 名工人组成一个工资组。在雇佣 一组 K 名工人时,我们必须按照下述规则向他们支付工资:

1.对工资组中的每名工人,应当按其工作质量与同组其他工人的工作质量的比例来支付工资。
2.工资组中的每名工人至少应当得到他们的最低期望工资。

返回组成一个满足上述条件的工资组至少需要多少钱。

示例 1:

输入: quality = [10,20,5], wage = [70,50,30], K = 2
输出: 105.00000
解释: 我们向 0 号工人支付 70,向 2 号工人支付 35。

示例 2:

输入: quality = [3,1,10,10,1], wage = [4,8,2,2,7], K = 3
输出: 30.66667
解释: 我们向 0 号工人支付 4,向 2 号和 3 号分别支付 13.33333。

提示:
1.1 <= K <= N <= 10000,其中 N = quality.length = wage.length
2.1 <= quality[i] <= 10000
3.1 <= wage[i] <= 10000
4.与正确答案误差在 10^-5 之内的答案将被视为正确的。

/*
招的人
q'[0], q'[1], ... q'[k - 1]    
给的薪资
q'[0] * x, q'[1] * x, ..., q'[k - 1] * x
总工资
(q'[0] + q'[1] +...+ q'[k - 1]) * x

x满足的条件
q[0] * x[0] >= w[0] => x[0] >= w[0] / q[0]

x[i] >= w[i] / q[i]

最优解
把所有工人按x[i]从小到大排序
枚举每一个薪资 x = x[i] 此时可以满足所有0...i  x[j] <= x
要总工资最小 从q[i]里面选择最小的k个工人 用大根堆

*/
class Solution {
public:
    double mincostToHireWorkers(vector<int>& quality, vector<int>& wage, int K) {
        int n = quality.size();
        vector<pair<double, int>> workers;//第一元素记录比值 第二元素记录q
        for(int i = 0; i < n; i ++)
            workers.push_back({wage[i] * 1.0 / quality[i], quality[i]});
        sort(workers.begin(), workers.end());
        //priority_queue<int, vector<int>, greater<int>> heap;小根堆
        priority_queue<int> heap;// 大根堆
        double res = 1e9;
        int sum = 0;// q的和
        
        for(auto worker : workers) //把worker 插入堆里面
        {
            heap.push(worker.second);
            sum += worker.second;
            if(heap.size() > K) 
            {
                sum -= heap.top();
                heap.pop();
            }            
            if(heap.size() == K) res = min(res, sum * worker.first);
        }
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Google是全球最大的搜索引擎和互联网技术公司,它的运维团队扮演着至关重要的角色。以下是一些可能在Google运维面试中被提出的问和回答: 1. 如何处理大量的访问请求和数据? Google运维团队通过分布式系统架构来处理大规模的访问请求和数据。他们使用负载均衡技术来分散流量,并使用自动缩放策略以适应不断增长的用户需求。 2. 如何确保系统的高可用性? 为了确保系统的高可用性,Google运维团队采取了多种措施。他们将服务部署在多个数据中心,并使用冗余服务器和网络连接。另外,他们实施了容错和容灾机制,以便在发生故障时快速恢复。 3. 如何监控和调试系统的性能问Google运维团队使用自动化的监控工具来实时监测系统的性能。他们还使用日志分析技术来追踪和调试潜在的问。此外,他们定期进行性能测试和优化,以确保系统的高性能和稳定性。 4. 如何处理系统的安全问Google非常注重系统的安全性,他们的运维团队采取了一系列措施来保护系统和用户数据的安全。这包括使用防火墙、入侵检测系统和数据加密技术。他们还进行定期的安全审计和漏洞扫描来发现和弥补潜在的安全漏洞。 5. 如何处理大规模的数据存储和备份? Google运维团队使用分布式文件系统和数据库来处理大规模的数据存储需求。他们使用冗余存储和备份策略来确保数据的可靠性和可恢复性。此外,他们还使用数据压缩和优化技术来减少存储空间和提高读写性能。 总之,Google的运维团队在处理大规模访问和数据、提高系统的可用性和性能、保护系统安全以及处理数据存储和备份方面都有丰富的经验和技术。这些问和回答只是其中一部分,真正的面试可能会更加具体和深入。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值