寻找两个有序数组的中位数
给定两个大小为 m 和 n 的有序数组 nums1
和 nums2
。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1
和 nums2
不会同时为空。
示例 1:
nums1 = [1, 3] nums2 = [2] 则中位数是 2.0
示例 2:
nums1 = [1, 2] nums2 = [3, 4] 则中位数是 (2 + 3)/2 = 2.5
思路:
两个数组的总长度分别为m,n,求两个数值的中位数,可以把问题归结为求两个有序数组的第k个值的大小,如果m+n为奇数,直接求第mid个数的大小,如果m+n为偶数,则求第中间位的的平均值,运用了分治的方法
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
// 找两向量中最中间的一个或两个数
int k = (nums1.size() + nums2.size() + 1) / 2;
double fMid = getKth(nums1, 0, nums1.size() - 1, nums2, 0, nums2.size() - 1, k);
if ((nums1.size() + nums2.size()) % 2 == 1)
return fMid;
else
return (fMid + getKth(nums1, 0, nums1.size() - 1, nums2, 0, nums2.size() - 1, k + 1)) / 2;
}
double getKth(vector<int>& nums1, int l1, int r1, vector<int>& nums2, int l2,int r2, int k)
{
if (l1 > r1)
return nums2[l2 + k - 1];
if (l2 > r2)
return nums1[l1 + k - 1];
int m1 = l1 + (r1 - l1) / 2;
int m2 = l2 + (r2 - l2) / 2;
int halfLen = m1 - l1 + m2 - l2 + 2;
if (nums1[m1] < nums2[m2])
if (halfLen > k)
return getKth(nums1, l1, r1, nums2, l2, m2 - 1, k);
else
return getKth(nums1, m1 + 1, r1, nums2, l2, r2, k - (m1 - l1 + 1));
else
if (halfLen > k)
return getKth(nums1, l1, m1 - 1, nums2, l2, r2, k);
else
return getKth(nums1, l1, r1, nums2, m2 + 1, r2, k - (m2 - l2 + 1));
}
};
结果:
最长回文子串
给定一个字符串 s
,找到 s
中最长的回文子串。你可以假设 s
的最大长度为 1000。
示例 1:
输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd" 输出: "bb"
思路:
顺序遍历给定字符串,遍历到的每个位置都有可能为回文中心,此时有两种情况:
一种情况是,当回文串为奇数时,直接以当前字符为中心,向前驱与后继扩展。
第二种情况是,当回文串为偶数时,以当前字符与当前字符的后继的中间位置为中心,向前驱与后继扩展。
奇数回文串n个位置,偶数回文串(n-1)个位置,共有2n-1个位置
时间复杂度,2n-1个位置与以每个位置为中心的向前向后访问,复杂度为O(n^2)。
class Solution {
public:
string longestPalindrome(string s) {
if (s == "")
return "";
int maxLen = 0;
int centerIndex = -1;
for (int i = 0; i < s.length(); ++i)
{
int len1 = CenterExtension(s, i, i);
int len2 = CenterExtension(s, i, i + 1);
int tempMaxLen = max(len1, len2);
if (centerIndex == -1 || maxLen < tempMaxLen)
{
maxLen = tempMaxLen;
centerIndex = i;
}
}
return s.substr(centerIndex - (maxLen - 1) / 2, maxLen);
}
int CenterExtension(string s, int left, int right)
{
int L = left;
int R = right;
while (L >= 0 && right < s.length() && s[L] == s[R])
{
L--;
R++;
}
return R - L - 1;
}
};
结果:
Z字形变换
将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 "LEETCODEISHIRING"
行数为 3 时,排列如下:
L C I R E T O E S I I G E D H N
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"LCIRETOESIIGEDHN"
。
请你实现这个将字符串进行指定行数变换的函数:
string convert(string s, int numRows);
示例 1:
输入: s = "LEETCODEISHIRING", numRows = 3 输出: "LCIRETOESIIGEDHN"
示例 2:
输入: s = "LEETCODEISHIRING", numRows = 4 输出: "LDREOEIIECIHNTSG" 解释: L D R E O E I I E C I H N T S G
思路:
将Z字变换看成是V字变换,除去V的起点,其余点都以V的顶点对称,以顶点的下标向-1与+1扩散,与对应的行也能相匹配。
当最后一个V字形不完整时,计算时按照完整的来计算,当计算所得下标超出所给字符串最大下标时,不放进每行对应的字符串内。
最后将每行字符串拼接。
class Solution {
public:
string convert(string s, int numRows) {
if (s.empty())
return "";
if (numRows == 1)
return s;
// 每个Z是3 * numRows - 2
// 可以将Z看成是V排序,每个V的长度是,2 * numRows - 2
const int VLen = 2 * numRows - 2;
const int size = s.length();
const int numV = size / VLen + 1;
// 第一行始终是i * VLen;
// 第numRows行始终是i * VLen + numRows - 1
// 中间的就是
vector<string> zstr(numRows, "");
for (int j = 0; j < numV; ++j)
{
int LBIndex = j * VLen;
if (LBIndex < size)
zstr[0].push_back(s[LBIndex]);
int LEIndex = j * VLen + numRows - 1;
if (LEIndex < size)
zstr[numRows - 1].push_back(s[j * VLen + numRows - 1]);
// 基准点为最后一行
for (int i = 1; i <= numRows - 2; ++i)
{
// 第j * VLen + numRows - 1行
if (LEIndex - i < size)
zstr[numRows - 1 - i].push_back(s[LEIndex - i]);
if (LEIndex + i < size)
zstr[numRows - 1 - i].push_back(s[LEIndex + i]);
}
}
string rel;
for (int i = 0; i < numRows; ++i)
rel += zstr[i];
return rel;
}
};
结果: