腾讯精选50题—Day1题目2,4,5

腾讯精选50题—Day1题目2,4,5

今天是刷腾讯精选50题的第一天,加油加油加油~~~

目录

腾讯精选50题—Day1题目2,4,5

1. 问题2:链表对应位置数字相加

(1)题目描述

(2) 题目思路

(3)题解

2. 问题4(这道题非常好,出现次数非常高)

(1)题目描述:一道非常经典的题目,二分法查找两个有序数组的中位数

(2)题目思路

(3)题解

3. 问题5 求最大回文子串

(1)题目描述

(2)题目思路

(3)题解

 参考


1. 问题2:链表对应位置数字相加

(1)题目描述

You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list.

You may assume the two numbers do not contain any leading zero, except the number 0 itself.

示例:

约束:

(2) 题目思路

两个非空倒序链表将各个位置的数字求和,并将结果返回,需要注意的点:

a. 进位问题和链表长度不对等;

b. 这道题如果将两个链表遍历分布遍历一遍,将代表的数字相加,时间复杂度非常高,而且会超出数字最大表示范围,所以行不通,示例的错误代码如下:

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
    
        long long int num1 = 0;
        long long int num2 = 0;
        int count1 = 0;
        int count2 = 0;

        ListNode* q1 = l1;
        ListNode* q2 = l2;

        while (q1 != NULL)
        {
            int data1 = q1->val;
            num1 += data1 * pow(10, count1);
            count1++;
            q1 = q1->next;
        }
        // cout << num1 << endl;

        while (q2 != NULL)
        {
            int data2 = q2->val;
            num2 += data2 * pow(10, count2);
            count2++;
            q2 = q2->next;
        }
        // cout << num2 << endl;

        long long int num3 = num1 + num2;

        int div_r = num3 / 10;
        int remainder = num3 % 10;
        num3 = div_r;

        ListNode* result = new ListNode(remainder);
        ListNode* p_r = result; // traverse the result List

        while (div_r != 0)
        {
            div_r = num3 / 10;
            remainder = num3 % 10;
            num3 = div_r;

            ListNode* temp_node = new ListNode(remainder);
            p_r->next = temp_node;
            p_r = p_r->next;
        }

        return result;
    }
};

(3)题解

a. 一次遍历两个链表,然后将数字相加,如果有进位则用标志位标志,最外面的循环条件为两个链表至少有一个不为空或者标志位为true(有进位);

b. 循环体里如果两个遍历指针都为空,那么什么也不做(data1和data2已经初始化过了),如果两个指针都不为空,那么分别读取数字并移动遍历指针,如果有一个指针为空,将对应的保存数字的变量赋值为0并且正常读取另外一个指针的数值;

c. 如果有进位(flag为true),那么计算该位的值要加1,并且置flag为false,否则正常求和;

d. 如果该位的值大于等于10,说明有进位,置flag为true,然后该位的值减10;

e. 创建新指针并加入。

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        
        ListNode* p1 = l1;
        ListNode* p2 = l2;
        int temp_num = 0;

        ListNode* result = new ListNode(0);
        ListNode* visit = result;
        bool flag = false;

        while (p1 != NULL || p2 != NULL || flag)
        {
            int data1 = 0;
            int data2 = 0;
            
            if (p1 == NULL && p2 == NULL)
            {
            }
            else if (p1 != NULL && p2 != NULL)
            {
                data1 = p1->val;
                data2 = p2->val;

                p1 = p1->next;
                p2 = p2->next;
            }
            else if (p1 == NULL)
            {
                data1 = 0;
                data2 = p2->val;
                p2 = p2->next;
            }
            else {
                data1 = p1->val;
                data2 = 0;
                p1 = p1->next;
            }

            if (flag)
            {
                temp_num = data1 + data2 + 1;
                flag = false;
            }
            else {
                temp_num = data1 + data2;
            }

            if (temp_num >= 10)
            {
                flag = true;
                temp_num = temp_num - 10;
            }

            ListNode* temp_node = new ListNode(temp_num);
            visit->next = temp_node;
            visit = visit->next;

        }
        return result;
    }
};

结果:

时间复杂度:O(max(m, n))

空间复杂度: O(max(m, n))

待优化....... 

2. 问题4(这道题非常好,出现次数非常高)

(1)题目描述:一道非常经典的题目,二分法查找两个有序数组的中位数

Given two sorted arrays nums1 and nums2 of size m and n respectively, return the median of the two sorted arrays.

Follow up: The overall run time complexity should be O(log (m+n)).

示例:

约束:

(2)题目思路

本题如果不考虑时间复杂度,直接对两个vector进行合并然后找中位数即可,时间复杂度为O(m+n),但需要控制时间复杂度在O(log(m+n)),需要使用二分法。问题本质上可以转为求第K大的数。假设数组A长度为m,数组B长度为n,在假设m+n为奇数的情况下,我们要寻找第K个数(k=(m+n)/ 2),首先我们可以寻找两个数组中第k/2 - 1的数,A[k/2 - 1]前面一共有k/2 - 2 个数,同理B[k/2 - 1]前面一共有A[0,...,k/2-2]共k/2 - 1 个数,如果A[k/2 - 1]小于等于B[k/2 -1],那么比B[k/2 -1]小的数一共有(k/2 -1 + 1 + k/2 - 1)= k-1个数,我们可以将A[0,...,k/2-1]剔除,反之则剔除B[0,...,k/2-1](注意!将等于的情况归于小于了);然后我们再次缩小k的值,在新的数组中进行查找,循环即可(如果 k=1,我们只要返回两个数组首元素的最小值即可,此时循环结束)。

几个需要注意的点:

a. 查找下标不能越界;

b. 根据排除数的个数缩小k值

(3)题解

a. 首先需要根据中位数的特性分类讨论,如果两个数组长度和为奇数,那么最中间的数即为中位数;如果两个数组长度和为偶数,那么最中间两个数求其平均值即为中位数;

b. 求第K大的数;

    b.0 循环

    b.1 判断第一个数组查找下标index1是否越界,如果越界则中位数在第二个数组;判断第二个数组查找下标index2是否越界,如果越界则中位数在第一个数组;

    b.2 如果k为1,那么返回此时index1和index2对应数字的最小值,循环退出;

    b.3 分别计算新下标new_index1和new_index2并保证不能越界;

    b.4 判断,并缩小k值。

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
    
        int length = nums1.size() + nums2.size();
        int find_k = 0;
        if (length % 2 == 1)
        {
            find_k = length / 2 + 1;
            return getKthData(nums1, nums2, find_k);
        }        
        else {
            find_k = length / 2;
            return (getKthData(nums1, nums2, find_k) + getKthData(nums1, nums2, find_k + 1)) / 2.0;
        }
    }

    int getKthData(const vector<int>& nums1, const vector<int>& nums2, int find_k)
    {
        int size1 = nums1.size();
        int size2 = nums2.size();
        int index1 = 0;
        int index2 = 0;

        while (true)
        {
            if (index1 == size1)
                return nums2[index2 + find_k - 1];
            if (index2 == size2)
                return nums1[index1 + find_k - 1];
            if (find_k == 1)
                return min(nums1[index1], nums2[index2]);

            int new_index1 = min(index1 + find_k / 2 - 1, size1 - 1);
            int new_index2 = min(index2 + find_k / 2 - 1, size2 - 1);

            if (nums1[new_index1] < nums2[new_index2])
            {
                find_k = find_k - (new_index1 - index1 + 1);
                index1 = new_index1 + 1;
            }
            else {
                find_k = find_k - (new_index2 - index2 + 1);
                index2 = new_index2 + 1; 
            }
        }


    }
};

结果: 

待优化.......  

时间复杂度:O(log(m+n))

空间复杂度: O(1)​​​​​​​

3. 问题5 求最大回文子串

(1)题目描述

Given a string s, return the longest palindromic substring in s.

示例:

约束:

(2)题目思路

这道题目求最长回文子串,问题可以转换为求最长公共子串问题,然后分析公共子串,分析最长公共子串需要用到动态规划,时间复杂度O(n^{2}),这里不赘述了。比较好的解法是Manacher算法,真的给Manacher(马拉车)算法跪了,大神真的太厉害了!!!算法真的非常巧妙,将动态规划O(n^{2})的时间复杂度一下子降到线性!!!真的神作啊~

一共有四个步骤:

a. 特殊值处理,如果长度小于2,那么本身就是最大回文串,返回即可;

b. 给字符串s的首尾及中间插分隔符,字符串必然变为长度为奇数的串~例如对于奇数串"aba",中间插入2个(偶数个),两端插入2个(偶数个),长度变为7(奇数个);对于偶数串"abcd",中间可插入3个(奇数个),两端可插入2个(偶数个),长度变为9(奇数个)......一般来说,长度为n的串,中间有n-1个空可插入,加上两端可插入2个,则长度变为2*n+1,必然为奇数~

c. 然后对经过a步骤处理后形成的字符串str进行中心扩散(以该位置为中心,判断两端字符是否相同,统计相同的个数),并记录最大长度和起始位置(起始位置=(当前位置-最大长度)/ 2)

d. 对原始字符串s进行分割,以起始位置和最大长度为标准处理。

(3)题解

// The algorithm of Manacher 

class Solution {
public:
    string longestPalindrome(string s) {

        int len = s.size();
        string result = "";

        if (len < 2)
            return s;

        string str = addBoundaries(s, '#');
        
        int str_len = 2 * len + 1;
        int max_len = 1;
        int start = 0;
        int *A = new int[str_len];
        int i = 0;

        for (i = 0; i < str_len; i++)
        {
            A[i] = centerSpread(str, i);

            if (A[i] > max_len)
            {
                max_len = A[i];
                start = (i - max_len) / 2;
            }
        }

        result = s.substr(start, max_len);        
        return result;
    }

    int centerSpread(string s, int key)
    {
        int count = 0;
        int len = s.size();

        for (int i = 1; i <= key; i++)
        {
            if (key - i >= 0 && key + i < len)
            {
                if (s[key - i] == s[key + i])
                {
                    count++;
                }
                else {
                    break;
                }
            }
        }
        return count;

    }

    string addBoundaries(string s, char sep)
    {
        int len = s.size();

        // throw exception about sep, sep isn't contained in s
        string add_result = "";

        for (int i = 0; i < len; i++)
        {
            add_result += "#";
            add_result += s[i];
        }
        add_result.append("#");
        return add_result;
    }
};

结果:

时间复杂度:O(n^{2})

空间复杂度: O(n)​​​​​​​​​​​​​​

 参考

1. http://www.cxyxiaowu.com/2869.html

2. https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/xun-zhao-liang-ge-you-xu-shu-zu-de-zhong-wei-s-114/

猜你喜欢

转载自blog.csdn.net/Fox_Alex/article/details/112463847
今日推荐