leetcode算法专题训练:十五.细节实现专题

十五.细节实现专题


66.加一

题目描述:给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/plus-one

解题思路:该题从后往前一次遍历即可求得。

时间复杂度:O(N)

空间复杂度:O(1)

class Solution {
public:
    vector<int> plusOne(vector<int>& digits) {
        int carry = 1;
        int i = digits.size() - 1;
        while (0 <= i)
        {
            int sum =  digits[i] + carry;
            digits[i] = sum % 10;
            carry = sum / 10;
            --i;
            if (!carry)
                break;
        }
        if (carry)
            digits.insert(digits.begin(), 1);
        return digits;
    }
};

7.整数反转

题目描述:给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−2^31, 2^31 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/reverse-integer

解题思路:不断对x%10 再/10,将结果累加,并判断有没有越界。

时间复杂度:O(logX)

空间复杂度:O(1)

class Solution {
public:
    int reverse(int x) {
        long long ans = 0;
        while (x)
        {
            ans = ans * 10 + x%10;
            x /= 10;
            if (ans > INT32_MAX)
                return 0;
            if (ans < INT32_MIN)
                return 0;
        }
        return ans;
    }
};



343. 整数拆分

题目描述:给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/integer-break

解题思路:动态规划, dp[i]代表拆分整数i能够获得的最大乘积

时间复杂度:O(N^2)

空间复杂度:O(N)

class Solution {
public:
    int integerBreak(int n) {
        if (n <= 1)
            return 0;
        vector<int> dp(n + 1, 0);
        dp[1] = 1;
        for (int i = 2; i <= n; ++i)
            for (int j = 1; j < i; ++j)
            {
                int l = std::max(j , dp[j]);
                int r = std::max(i-j, dp[i-j]);
                dp[i] = std::max(dp[i], l * r);
            }
        return dp[n];
    }
};


9.回文数

题目描述:判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
你能不将整数转为字符串来解决这个问题吗?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/palindrome-number

解题思路:该题可以用上一题的解法,将数字进行反转再判断是否相同;注意负数不是回文数。

时间复杂度:O(logX)

空间复杂度:O(1)

class Solution {
public:
    bool isPalindrome(int x) {
        if (x < 0)
            return false;
        long res = 0, old = x;
        while (x)
        {
            res = 10 * res + x % 10;
            x /= 10;
        }
        return res==old;
    }
};



56.合并区间

题目描述:给出一个区间的集合,请合并所有重叠的区间。
intervals[i][0] <= intervals[i][1]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-intervals

解题思路:直接遍历集合,比较ans.back() 是否与 遍历的元素相连,若相连接的话 修改, 没有连接的话则加入。

时间复杂度:O(NlogN)

空间复杂度:O(1)

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        sort(intervals.begin(), intervals.end());
        vector<vector<int>> ans;
        ans.push_back(intervals[0]);
        for (int i = 1; i < intervals.size(); ++i)  
        {
            if (intervals[i][0] > ans.back()[1])
                ans.push_back(intervals[i]);
            else if (intervals[i][1] <= ans.back()[1])
                continue;
            else
                ans.back()[1] = intervals[i][1];
        }
        return ans;
    }
};



57.插入区间

题目描述:给出一个无重叠的 ,按照区间起始端点排序的区间列表。
在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/insert-interval

解题思路:直接适用上一题的方法即可,并且可以不用排序,直接找到位置插入即可。

时间复杂度:O(N)

空间复杂度:O(1)

class Solution {
public:
    vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {
        int i = 0;
        for (; i < intervals.size(); ++i)
            if (newInterval[0] <= intervals[i][0])
                break;
        intervals.insert(intervals.begin()+i, newInterval);
        vector<vector<int>> ans;
        ans.push_back(intervals[0]);
        for (int i = 1; i < intervals.size(); ++i)  
        {
            if (intervals[i][0] > ans.back()[1])
                ans.push_back(intervals[i]);
            else if (intervals[i][1] <= ans.back()[1])
                continue;
            else
                ans.back()[1] = intervals[i][1];
        }
        return ans;
    }
};



68.文本左右对齐

题目描述:给定一个单词数组和一个长度 maxWidth,重新排版单词,使其成为每行恰好有 maxWidth 个字符,且左右两端对齐的文本。
你应该使用“贪心算法”来放置给定的单词;也就是说,尽可能多地往每行中放置单词。必要时可用空格 ’ ’ 填充,使得每行恰好有 maxWidth 个字符。
要求尽可能均匀分配单词间的空格数量。如果某一行单词间的空格不能均匀分配,则左侧放置的空格数要多于右侧的空格数。
文本的最后一行应为左对齐,且单词之间不插入额外的空格。
说明:
单词是指由非空格字符组成的字符序列。
每个单词的长度大于 0,小于等于 maxWidth。
输入单词数组 words 至少包含一个单词。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/text-justification

解题思路:

时间复杂度:

空间复杂度:






149.直线上最多的点数

题目描述:给定一个二维平面,平面上有 n 个点,求最多有多少个点在同一条直线上。
示例 1:
输入: [[1,1],[2,2],[3,3]]
输出: 3
解释:
^
|
| o
| o
| o
±------------>
0 1 2 3 4
示例 2:
输入: [[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]]
输出: 4
解释:
^
|
| o
| o o
| o
| o o
±------------------>
0 1 2 3 4 5 6

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/max-points-on-a-line

解题思路:依次遍历各个点,与后面的点进行计算斜率,找出同一个斜率中拥有的最多点数即可。需要注意两点:1.斜率可能出现与x轴或者y轴相平行,所以斜率的形式要用string表示;2.可能会出现重复的点的情况,所以用dup记录重复的点的个数。

时间复杂度:O(N^2)

空间复杂度:O(N)

class Solution {
public:
    int GCD(int a, int b)
    {
        while (b)
        {
            int t = a % b;
            a = b; 
            b = t;
        }
        return a;
    }

    int maxPoints(vector<vector<int>>& points) {
        if (points.empty())
            return 0;
        int n = points.size();
        int ans = 0;
        for (int i = 0; i < n; ++i)
        {
            unordered_map<string, int> hashmap; // <以第i个点为起点的斜率, 直线数>
            int dup = 0;
            int maxHashVal = 0;
            for (int j = i + 1; j < n; ++j)
            {
                int diffx = points[j][0] - points[i][0];
                int diffy = points[j][1] - points[i][1];
                if (diffx == 0 && diffy == 0)
                {
                    dup ++;
                    continue;
                }
                int gcd = GCD(diffx, diffy);
                string key = to_string(diffx / gcd) + "/" + to_string(diffy / gcd);
                hashmap[key] ++;
                maxHashVal = std::max(maxHashVal, hashmap[key]);                   
            }
            ans = std::max(ans, dup + 1 + maxHashVal);
        } 
        return ans;
    }
};



93.复原IP地址

题目描述:给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。
有效的 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 ‘.’ 分隔。
例如:“0.1.2.201” 和 “192.168.1.1” 是 有效的 IP 地址,但是 “0.011.255.245”、“192.168.1.312” j是 无效的 IP 地址。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/restore-ip-addresses

解题思路:DFS法,设置path辅助变量存储分割好的数字,设置start起始位置作为每一次数字的开头,剪枝操作实质上也就是将不符合规定的IP地址值跳过。

时间复杂度:O(N!)

空间复杂度:O(N)

class Solution {
    vector<string> ans;
public:
    void DFS(string& s, int start, vector<string>& path)
    {
        if (start == s.size() && 4 == path.size())
        {
            string res = path[0] + "." + path[1] + "." + path[2] + "." + path[3];
            ans.push_back(std::move(res));
            return;
        }
        if (path.size() > 4)
            return;

        for (int i = start; i < s.size(); ++i)
        {
            string str = s.substr(start, i-start + 1);
            if (stoi(str) > 255)
                break;
            path.push_back(str);
            DFS(s, i+1, path);
            path.pop_back();
            if ("0" == str)
                break;
        }
    }

    vector<string> restoreIpAddresses(string s) {
        vector<string> path;
        DFS(s, 0, path);
        return ans;
    }
};





12.整形转罗马数字

题目描述:罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 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 的范围内。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/integer-to-roman

解题思路:首先使用vector进行整形数字与罗马数字的映射(不用哈希表的原因是无法将数字进行从大到小的遍历),我们注意到 4, 9 这样的数是需要单独设置的,也就是这些需要有单独个性的数,需要添加到哈希表里面去;接下来再从最大的数字(1000)开始求余数,再将罗马数字累加即可得到结果。

时间复杂度:O(N)

空间复杂度:O(1)

class Solution {
public:
    string intToRoman(int num) {
        vector<int> iVec{1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000};
        vector<string> sVec{"I", "IV", "V", "IX", "X", "XL", "L", "XC", "C", "CD", "D", "CM", "M"};
        string ans;
        for (int i = iVec.size() - 1; i >= 0; --i)
        {
            int cnt = num / iVec[i];
            num %= iVec[i];
            for (int j = 0; j < cnt; ++j)
                ans = ans + sVec[i];
        }
        return ans;
    }
};



13.罗马数字转整形

题目描述:
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 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 的范围内。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/roman-to-integer

解题思路:创建哈希表实现 罗马数字->普通数字,从罗马数字的字符串开始遍历,注意罗马数字可能会是两个连续的字符。

时间复杂度:O(N)

空间复杂度:O(1)

class Solution {
public:
    int romanToInt(string s) {
        unordered_map<string, int> hashmap{{"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}};
        int i = 0;
        int ans = 0;
        while (i < s.size())
        {
            if (hashmap.count(s.substr(i, 2)))
            {
                ans += hashmap[s.substr(i, 2)];
                i += 2;
            }else
            {
                ans += hashmap[s.substr(i, 1)];
                i += 1;
            }
        }
        return ans;
    }
};



17.电话号码的字母组合

题目描述:给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。此题需要看原链接。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number

解题思路:回溯法

时间复杂度:O(K^N) K是数字对应字符串的平均长度

空间复杂度:O(N) N是数字字符串的长度

class Solution {
    vector<string> ans;
    unordered_map<char, string> hashmap{{'2',"abc"}, {'3',"def"},{'4',"ghi"},{'5',"jkl"},{'6',"mno"},{'7',"pqrs"},{'8',"tuv"}, {'9', "wxyz"}};
public:
    void DFS(string& digits, int start, string& path)
    {
        if (path.size() == digits.size())
        {
            ans.push_back(path);
            return;
        }

        string s = hashmap[digits[start]];
        for (int i = 0; i < s.size(); i++)
        {
            path.push_back(s[i]);
            DFS(digits, start+1, path);
            path.pop_back();
        }
    }

    vector<string> letterCombinations(string digits) {
        if (digits.empty())
            return {};
        string path;
        DFS(digits, 0, path);
        return ans;
    }
};



470. 用 Rand7() 实现 Rand10()

题目描述:给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。此题需要看原链接。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/implement-rand10-using-rand7

解题思路:rand7()能等概率生成 1~7;
rand7() - 1 能等概率生成 0 ~ 6;
(rand7() - 1) * 7 能够等概率生成 0, 7, 14, 21, 28, 35, 42;
(rand7() - 1) * 7 + rand7() 能等概率生成 1~49

时间复杂度:O(1) 当然最坏情况下会一直>40也就是O(无穷)

空间复杂度:O(1)

class Solution {
public:
    int rand10() {
        int num;
        do
        {
            num = (rand7() - 1) * 7 + rand7(); // 1~49
        } while (num > 40);
        return num % 10 + 1; //(1~40 -1) % 10 + 1
    }
};

如果是random5 实现 random7 的话:

    int random7()
    {
        int i;
        do
        {
            i = (rand5() - 1) * 5 + rand5();  // i is now uniformly random between 1 and 25
        } while(i > 21);
        // i is now uniformly random between 1 and 21
        return i % 7 + 1;  // result is now uniformly random between 1 and 7
    }

还有一种情景题实现:
30万个员工,工卡是1-30万,抽出10万个员工发奖。
已有一个随机数生成函数rand()能够生成0-65535的整数,写一个公平的抽奖程序,输出这10万个员工的工卡号码
(进阶:30万抽99999个员工)


int rand300000()
{
	int x = 0;
    do
    {
        x= rand() * 65536 + rand() + 1; //(0 ~ 65535) * 65536  + random65536() = (1~ 65536*65536)
    }while(x > 65536*65536/30w * 30w);
    return x % 30w + 1;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值