LeetCode 初级 设计问题

Shuffle an Array

  • 打乱一个没有重复元素的数组。
  • 示例:
    // 以数字集合 1, 2 和 3 初始化数组。
    int[] nums = {1,2,3};
    Solution solution = new Solution(nums);
    // 打乱数组 [1,2,3] 并返回结果。任何 [1,2,3]的排列返回的概率应该相同。
    solution.shuffle();
    // 重设数组到它的初始状态[1,2,3]。
    solution.reset();
    // 随机返回数组[1,2,3]打乱后的结果。
    solution.shuffle();
    

  • 方法一: 暴力摸鱼
    • 思路:
      • 模拟对一个袋子里的物品每次不放回的取。
      • 具体而言
  • 方法二: 优化摸鱼法
    • 思路:
      • 和暴力摸鱼类似,区别在于我们取是在同一个数组中进行,这样可以降低空间复杂度
      • [0 ~ n] 之间摸鱼,摸到的和n+1进行互换
    • Code
    class Solution {
    public:
        Solution(vector<int>& nums):nums_(nums) {}
        
        /** Resets the array to its original configuration and return it. */
        vector<int> reset() {
            return nums_;
        }
        
        /** Returns a random shuffling of the array. */
        vector<int> shuffle() {
            vector<int> nums(nums_);
            for(int i=nums.size()-1; i>=0; --i){
                srand(clock());
                swap(nums[rand() % (i + 1)], nums[i]);
            }
            return nums;
        }
        vector<int> nums_;
    };
    
    /**
     * Your Solution object will be instantiated and called as such:
     * Solution* obj = new Solution(nums);
     * vector<int> param_1 = obj->reset();
     * vector<int> param_2 = obj->shuffle();
     */
    

最小栈

  • 设计一个支持 pushpoptop 操作,并能在常数时间内检索到最小元素的栈。
    • push(x) – 将元素 x 推入栈中。
    • pop()– 删除栈顶的元素。
    • top() – 获取栈顶元素。
    • getMin() – 检索栈中的最小元素。
  • 示例:
    MinStack minStack = new MinStack();
    minStack.push(-2);
    minStack.push(0);
    minStack.push(-3);
    minStack.getMin();   --> 返回 -3.
    minStack.pop();
    minStack.top();      --> 返回 0.
    minStack.getMin();   --> 返回 -2.
    

  • 方法一: 空间换时间法
    • 思路:(动态规划思想)
      • 状态:当前栈顶元素 和 栈顶到栈底的最小值
      • 状态转移:
        • 下一刻栈顶元素push进来的值
        • 下一刻的最小值为: 上一刻的最小值与当前push进来的值的最小值
      • 输出:
        • pop 直接pop栈顶
        • top直接栈顶的first
        • getMin 直接栈顶的second
    • Code
    class MinStack {
    public:
        /** initialize your data structure here. */
        MinStack() {
            
        }
        
        void push(int x) {
            if(s.empty()){
                s.push({x, x});
            }else{
                int tmp = s.top().second;
                tmp = min(tmp, x);
                s.push({x, tmp});
            }
        }
        
        void pop() {
            s.pop();
        }
        
        int top() {
            return s.top().first;
        }
        
        int getMin() {
            return s.top().second;
        }
        stack<pair<int, int>> s; // pair<int, int> (cur, min)
    };
    
    /**
     * Your MinStack object will be instantiated and called as such:
     * MinStack* obj = new MinStack();
     * obj->push(x);
     * obj->pop();
     * int param_3 = obj->top();
     * int param_4 = obj->getMin();
     */
    

Fizz Buzz

  • 写一个程序,输出从 1 到 n 数字的字符串表示。
    • 如果 n 是3的倍数,输出“Fizz”;
    • 如果 n 是5的倍数,输出“Buzz”;
    • 如果 n 同时是3和5的倍数,输出 “FizzBuzz”。
  • 示例:
    n = 15,
    返回:
    [
        "1",
        "2",
        "Fizz",
        "4",
        "Buzz",
        "Fizz",
        "7",
        "8",
        "Fizz",
        "Buzz",
        "11",
        "Fizz",
        "13",
        "14",
        "FizzBuzz"
    ]
    

  • 方法: 没啥好说的
class Solution {
public:
    vector<string> fizzBuzz(int n) {
        vector<string> s(n, "");
        for(int i=1; i<=n; ++i){
            if(i%3==0){s[i-1]+="Fizz";}
            if(i%5==0){s[i-1]+="Buzz";}
            if(s[i-1]==""){s[i-1]+=to_string(i);}
        }
        return s;
    }
};

计数质数

  • 统计所有小于非负整数 n 的质数的数量。
  • 示例:
    • 输入: 10
    • 输出: 4
    • 解释: 小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。

  • 思路:
    class Solution {
    public:
        int countPrimes(int n) {
            // 厄拉多塞筛法
            if(n < 2){return 0;}
            int result=0;
            vector<char> s(n, 1);
            s[0] = 0; // id 0 -> 0
            s[1] = 0; // id 1 -> 0
            for(int i=2; i < n;++i){
                if(!s[i]){continue;}
                ++result;
                for(int j=2; j*i < n; ++j){
                    s[j*i]=0;
                }
            }
            return result;
        }
    };
    

3的幂

  • 给定一个整数,写一个函数来判断它是否是 3 的幂次方。
  • 示例
    • 示例 1:
      • 输入: 27
      • 输出: true
    • 示例 2:
      • 输入: 0
      • 输出: false
    • 示例 3:
      • 输入: 9
      • 输出: true
    • 示例 4:
      • 输入: 45
      • 输出: false
  • 进阶:
    • 你能不使用循环或者递归来完成本题吗?

  • 参考: 3 的幂
  • 方法一: 递归法
  • 方法二: 迭代法
  • 方法三: 因子法
    • 思路:
      • 先找到int最大的幂次
      • 此数的因子就是在int范围类的所有3的幂次
class Solution {
public:
    int max;
    Solution():max{1}{
        while(max <= INT_MAX/3){
            max *=3;
        }
    }
    
    bool isPowerOfThree(int n) {
        //return recursion(n);
        // return iter(n);
        return perfect(n);
    }
    bool recursion(int n){
        if(n==1){return true;}
        if(n==0){return false;}
        return recursion(n/3) && n%3==0;
    }
    bool iter(int n){
        if(n==1){return true;}
        
        bool flag=false;
        long a=1;
        while(a < n){
            a*=3;
            if(a==n){flag=true; break;}
        }
        return flag;
    }
    bool perfect(int n){
        if(n==1){return true;}
        return n > 0 && max%n == 0;
    }
};

罗马数字转整数

  • 罗马数字包含以下七种字符: IVXLCDM
    字符          数值
    I             1
    V             5
    X             10
    L             50
    C             100
    D             500
    M             1000
    
  • 例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II27 写做 XXVII, 即为 XX + V + II
  • 通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
    I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
    X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。 
    C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
    
  • 给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。
  • 示例 1:
    输入: "III"
    输出: 3
    
  • 示例 2:
    输入: "IV"
    输出: 4
    
  • 示例 3:
    输入: "IX"
    输出: 9
    
  • 示例 4:
    输入: "LVIII"
    输出: 58
    解释: L = 50, V= 5, III = 3.
    
  • 示例 5:
    输入: "MCMXCIV"
    输出: 1994
    解释: M = 1000, CM = 900, XC = 90, IV = 4.
    
  • 思路:
  • 罗马数字转换为阿拉伯数字的时候,实际上是从左到右将每个字符对应的值累加的过程。例如“XVI”,实际就是10(X)+5(V)+1(I)。
  • 只不过由于存在特殊规则增加了这个过程的复杂,不过同样可以用上面的思路解决。举个例子,“IX”,可以看作是-1+10=9;“XIX”可以看作是10-1+10=19。
  • 题目又告诉我们,通常情况下,罗马数字中小的数字在大的数字的右边。所以,如果s[i]>s[i+1],那s[i]在累加时需要加一个负号,这就完成了特殊规则的处理。
class Solution {
public:
    int romanToInt(string s) {
        unordered_map<char, int> mp = {{'I', 1}, {'V',5}, {'X',10}, {'L',50}, {'C',100}, {'D',500}, {'M',1000}};
        int pos = 0;
        for (int i = 0;i < s.size()-1;++i){
            if (mp[s[i]] < mp[s[i+1]])
                pos -= mp[s[i]];
            else
                pos += mp[s[i]];
        }
        pos += mp[s.back()];
        
        return pos;
    }
};

整数转罗马数字

  • 罗马数字包含以下七种字符: IVXLCDM
    字符          数值
    I             1
    V             5
    X             10
    L             50
    C             100
    D             500
    M             1000
    
  • 例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II27 写做 XXVII, 即为 XX + V + II
  • 通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
    I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
    X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。 
    C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
    
  • 给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。
  • 示例 1:
    输入: 3
    输出: "III"
    
  • 示例 2:
    输入: 4
    输出: "IV"
    
  • 示例 3:
    输入: 9
    输出: "IX"
    
  • 示例 4:
    输入: 58
    输出: "LVIII"
    解释: L = 50, V = 5, III = 3.
    
  • 示例 5:
    输入: 1994
    输出: "MCMXCIV"
    解释: M = 1000, CM = 900, XC = 90, IV = 4.
    

  • 方法一: 暴力法:
    • 思路:
      • 将可能的键值列出来
      • 从大到小进行遍历,依次剔除
      • num的更新规则: 每次减去键值
      • 键值的更新规划: num小于键值时,键值向前遍历, 从最大开始
  • 方法一: 优化暴力法:
    • 思路
    • 暴力法,最大开始遍历,有点浪费,可是先检索出第一个大于num的键值,注意剔除最大的情况
    • Code
    class Solution {
    public:
        string intToRoman(int num) {
            if(num<1||num>3999){return "";}
            
            vector<pair<string, int>> m{{"I", 1}, 
                                          {"IV", 4}, {"V",5}, 
                                          {"IX", 9}, {"X",10}, 
                                          {"XL", 40}, {"L",50}, 
                                          {"XC", 90}, {"C",100}, 
                                          {"CD", 400}, {"D",500}, 
                                          {"CM", 900}, {"M",1000}};        
            
            auto t = find_if(m.begin(), m.end(), [&](pair<string, int> &a){return a.second >= num;});
            int idx = t- m.begin();
            if(idx==m.size()){--idx;}
            string result="";
            while(num>0 && idx>=0){
                if((num>=m[idx].second)!=0){
                    result += m[idx].first;
                    num -= m[idx].second;
                }else{
                    --idx;
                }
            }
            return result;
        }
    };
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值