腾讯精选50题—Day1题目2,4,5
今天是刷腾讯精选50题的第一天,加油加油加油~~~
目录
(1)题目描述:一道非常经典的题目,二分法查找两个有序数组的中位数
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;
}
};
结果:
时间复杂度:
空间复杂度:
待优化.......
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;
}
}
}
};
结果:
待优化.......
时间复杂度:
空间复杂度:
3. 问题5 求最大回文子串
(1)题目描述
Given a string s
, return the longest palindromic substring in s
.
示例:
约束:
(2)题目思路
这道题目求最长回文子串,问题可以转换为求最长公共子串问题,然后分析公共子串,分析最长公共子串需要用到动态规划,时间复杂度,这里不赘述了。比较好的解法是Manacher算法,真的给Manacher(马拉车)算法跪了,大神真的太厉害了!!!算法真的非常巧妙,将动态规划的时间复杂度一下子降到线性!!!真的神作啊~
一共有四个步骤:
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;
}
};
结果:
时间复杂度:
空间复杂度: