【leetcode】腾讯精选练习 50 题(更新中)

2. 两数相加

https://leetcode-cn.com/problems/add-two-numbers/

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

题目思路

总共三个数需要相加,l1取的节点的值,l2取的节点的值,以及上一轮的进位。
然后算得给下一轮的进位,并将当前值只保留个位。
注意:最后如果只剩一个进位,而l1和l2没有取到值,这时候也是要输出一个节点的。
时间和空间复杂度均为O(max(m,n))。

代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        int save = 0;
        ListNode* rst = new ListNode(0);
        ListNode* cur = rst;
        
        do {
            int left = 0;
            int right = 0;
            int all = 0;
            if (l1 != nullptr) {
                left = l1->val;
                l1 = l1->next;
            }
            if (l2 != nullptr) {
                right = l2->val;
                l2 = l2->next;
            }
            all = left + right + save;
            // cout << all << endl;
            save = all / 10;
            all = all % 10;
            
            ListNode* temp = new ListNode(all);
            cur->next = temp;
            cur = cur->next;
        } while (l1 != nullptr || l2 != nullptr);

        if (save != 0) {
            ListNode* temp = new ListNode(save);
            cur->next = temp;
            cur = cur->next;
        }

        return rst->next;
    }
};

4. 寻找两个正序数组的中位数

https://leetcode-cn.com/problems/median-of-two-sorted-arrays/

给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的中位数。
进阶:你能设计一个时间复杂度为 O(log (m+n)) 的算法解决此问题吗?

题目思路

首先是判断中位数的位置,总共为奇数个的话,是最中间的一个数,偶数的话,是最中间的两个数求平均。
偶数个的时候,划分两个数组,左边总共(m+n)/2个数,奇数个的时候,左边总共(m+n+1)/2个数(中位数人为定义放左边)。统一奇偶写法为(m+n+1)/2。
最终需要确定划分的位置即可,为了方便处理边界值,将较短的一个数组设为nums1,则nums1的划分确定,根据上述,nums2的划分也随之确定。
最终划分确定需要满足交叉判断条件,nums1[i-1] <= nums2[j] && nums2[j-1] <= nums1[i],注意考虑边界条件,具体在代码中有体现。
注意:二分查找中当看到left = i时,要考虑上取整,left = i-1时,不用上取整。

代码

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        // 保持nums1更短
        if (nums1.size() > nums2.size()) {
            return findMedianSortedArrays(nums2, nums1);
        }

        int m = nums1.size();
        int n = nums2.size();
        // 偶数 (m+n)/2 == (m+n+1)/2 
        // 奇数 (m+n+1)/2 中位数在左边
        int leftTotal = (m + n + 1) / 2;

        // 二分查找
        // 交叉判断条件:
        // nums1[i-1] <= nums2[j] && nums2[j-1] <= nums1[i]
        int left = 0;
        int right = m;
        while (left < right) { // 最终left == right退出,只有一个值,因此交叉判断条件取一个即可
            int i = left + (right - left + 1) / 2;
            int j = leftTotal - i;
            if (nums1[i-1] > nums2[j]) { // i-1不会溢出,因为有上取整,i != 0
                right = i - 1;
            } else {
                // 特殊情况:[left(i), right(i+1)], 区间无法缩小
                // 因此i定义的时候,需要上取整(+1)
                left = i;
            }
        }

        int i = left;
        int j = leftTotal - i;
        int leftNums1 = (i == 0 ? INT_MIN : nums1[i-1]);
        int rightNums1 = (i == m ? INT_MAX : nums1[i]);
        int leftNums2 = (j == 0 ? INT_MIN : nums2[j-1]);
        int rightNums2 = (j == n ? INT_MAX : nums2[j]);

        if ((m + n) % 2 == 1) {
            return max(leftNums1, leftNums2);
        } else {
            return (max(leftNums1, leftNums2) + min(rightNums1, rightNums2)) / 2.0;
        }     
    }
};

5. 最长回文子串

https://leetcode-cn.com/problems/longest-palindromic-substring/

给你一个字符串 s,找到 s 中最长的回文子串。

解题思路

中心扩散法:遍历字符,以当前字符向两边扩散,找到最长的回文串。时间复杂度O(n^2),空间复杂度O(1)。枚举子串个数O(n)。
动态规划法(空间换时间):保存状态dp[i][j],即从下标i到下标j的子串是否为回文。时间复杂度O(n2),空间复杂度O(n2)。枚举子串个数O(n^2),耗时更长。
Manacher算法:结合中心扩散法和动态规划,时间复杂度O(n),空间复杂度O(n)。(面试不用掌握这个方法)

当在位置 i 开始进行中心拓展时,我们可以先找到 i 关于 j 的对称点 2 * j - i。
那么如果点 2 * j - i 的臂长等于 n,我们就可以知道,点 i 的臂长至少为 min(j + length - i, n)。
那么我们就可以直接跳过 i 到 i + min(j + length - i, n) 这部分,从 i + min(j + length - i, n) + 1 开始拓展。

统一奇偶字符串的技巧:在字符中间插入#。

代码

class Solution {
public:
    int palindromeLen(string s, int left, int right) {
        while (left >= 0 && right < s.size() && s[left] == s[right]) {
            left -= 1;
            right += 1;
        }
        return right-left-1;
    }
    string longestPalindrome(string s) {
        int max_index = 0;
        int max_len = 0;
        string ret;
        for (int i = 0; i < s.size(); i++) {
            int temp_len = max(palindromeLen(s, i, i), palindromeLen(s, i, i+1));
            if (temp_len > max_len) {
                max_len = temp_len;
                max_index = i;
            }
        }
        if (max_len % 2 == 0) {
            ret = s.substr(max_index+1 - max_len/2, max_len);
        } else {
            ret = s.substr(max_index - max_len/2, max_len);
        }
        return ret;
    }
};

7. 整数反转

https://leetcode-cn.com/problems/reverse-integer/

给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。

注意:
假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−231,  231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。

解题思路

题目比较简单,不过要注意判断,避免溢出。
时间复杂度:O(log(x))。
空间复杂度:O(1)。

代码

class Solution {
public:
    int reverse(int x) {
        int rev = 0;
        while(x != 0) {
            int temp = x % 10;
            x /= 10;
            if (rev > INT_MAX/10 || (rev == INT_MAX / 10 && temp > 7)) return 0;
            if (rev < INT_MIN/10 || (rev == INT_MIN / 10 && temp < -8)) return 0;
            rev = rev * 10 + temp;
        }
        return rev;
    }
};

8. 字符串转换整数 (atoi)

https://leetcode-cn.com/problems/string-to-integer-atoi/

请你来实现一个 atoi 函数,使其能将字符串转换成整数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。接下来的转化规则如下:
如果第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字字符组合起来,形成一个有符号整数。
假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成一个整数。
该字符串在有效的整数部分之后也可能会存在多余的字符,那么这些字符可以被忽略,它们对函数不应该造成影响。
假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换,即无法进行有效转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0。

注意:
本题中的空白字符只包括空格字符 ' ' 。
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−2^31,  2^31 − 1]。如果数值超过这个范围,请返回  2^31 − 1 或 −2^31。

解题思路

代码

class Solution {
public:
    int myAtoi(string s) {
        int ret = 0;
        int start = true;
        int flag = 1;
        for (int i = 0; i < s.size(); i++) {
            if (start && s[i] == ' ')
                continue;
            if (start && (s[i] == '-' || s[i] == '+')) {
                if (s[i] == '-')
                    flag = -1;
                start = false;
                continue;
            }
            if (s[i] >= '0' && s[i] <= '9') {
                start = false;
                int temp = flag * (s[i]-'0');
                if (ret > INT_MAX/10 || (ret == INT_MAX/10 && temp > 7)) return INT_MAX;
                if (ret < INT_MIN/10 || (ret == INT_MIN/10 && temp < -8)) return INT_MIN;
                ret = ret*10 + temp;
            } else {
                break;
            }
        }
        return ret;
    }
};

9. 回文数

https://leetcode-cn.com/problems/palindrome-number/

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

解题思路

负数不满足条件,先过滤掉。然后反转数字,判断是否相等。(注意可能溢出)

代码

class Solution {
public:
    bool isPalindrome(int x) {
        if (x < 0) {
            return false;
        }
        
        int reverse = 0;
        int x_cp = x;
        while (x != 0) {
            int temp = x % 10;
            x /= 10;
            if (reverse > INT_MAX/10 || ((reverse == INT_MAX/10) && temp > 7)) return false;
            reverse = reverse * 10 + temp;
        }
        if (reverse == x_cp) {
            return true;
        }
        return false;
    }
};
posted @ 2021-01-11 23:32  Yanqiang  阅读(218)  评论(0编辑  收藏  举报