(1)LeetCode4:寻找两个正序数组的中位数
两个有序数组,要求这两个数组合并之后(也是排好序的)的中位数(如果总个数为偶数,则去中间两个数的平均值;为奇数则取中间的数)。
对这个问题的解决,最简单的做法就是把两个数组合并后排序,但这样的时间复杂度较高,所以不采用!
我们可以在不对其进行排序的状态下找到中位数,首先将问题转化为这两个数组合并后从小到大排,求第k个数等于多少? 到时候要求中位数时,只需令 K = (m+n)/2即可,m,n分别为两个数组的元素个数。
○ 求第k个数的基本思路是:
①.首先,我们不合并,分别找到这A,B两个数组中索引值为( k/2)的元素,并记为k1,k2
②.k1和k2的大小比较有三种情况:
1)k1 < k2
2)k1 > k2 ,小的元素所在的数组的前(k/2)的部分一定不会有合并之后的第k个数,它们一定排在第k个数的前面,所以在考虑第k个数的位置时不用再考虑它们了,可以删掉它们
3)k1 = k2 ,两值说明两个(k/2)位置的数都可以作为第k个数,所以这里我们可以随便删掉一边,也就删掉了(k/2)个数
③.综合起来,我们每次都能删掉(k/2)个数,这样在总的数组中原来k的位置就变成了第(k/2)的位置,这样就形成了一个递归,每次我们都能够不断缩小范围,知道原来第k个位置的元素前面的元素全都被“删”完了,最后它 k -> 1时就找到了(即k在删完后变成了数组上的第一个元素),递归结束!
【注】:
1.每次新形成的数组的长度都是大的数据+小的数组减去(k/2)
2.每次砍掉的(k/2)的长度实际上都是在变化的,k = k/2,k在不断地缩小,因为原来的第k个元素在新数组中已经成为了第(k/2)个,所以每次砍掉的距离也都缩小了,在不断地向k所在位置逼近。
○ 由于每次都看去(k/2),所以该算法的时间复杂度就是log(k/2),即为1/2 log(m+n) ====》 log(m+n)
代码展示:
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int tot = nums1.size() + nums2.size(); // 新数组的元素总数
if(tot % 2 == 0) {
// 如果总数为偶数的话中位数就是中间两个数的平均数
int left = find (nums1, 0, nums2, 0, tot / 2);
int right = find (nums1, 0, nums2, 0, tot / 2 + 1); // 通过能够根据索引找到任意元素的find函数找到相应位置的值
return (left + right) / 2.0; // 这里必须除以2.0,否则相除会出现结果截断为0的情况
} else{ // 奇数的话就直接返回中间的元素
return find(nums1, 0, nums2, 0, tot / 2 + 1);
}
}
int find(vector<int>& nums1, int i, vector<int>& nums2, int j, int k) {
// 定义本算法中最核心的算法,通过索引位置寻找相应的元素;其中i,j分别为新数组的起点
if(nums1.size() - i > nums2.size() - j) return find(nums2, j, nums1, i, k); // 优化一下:我们现在只考虑一种情况,默认nums1更小,如果实际上更大,就将其与nums2的位置替换一下(且每砍一次都会比较替换一次)
if (nums1.size() == i) return nums2[j + k - 1]; // 边界情况,如果nums1足够小,只有一个元素,那么k就可以在nums2数组中直接找到,只需去掉一个nums1元素即可
if (k == 1) return min(nums1[i], nums2[j]); // 这是递归的终止条件,当k被砍到为1的时候,就结束递归
// 正式处理逻辑
// 确定起点;在nums1数组中(k/2)的位置确定比较特殊,因为可能它的总长度都没有(k/2),可以全部被砍掉
// nums2数组中(k/2)的位置即为:起点+(k/2)
int si = min(i + k / 2, int(nums1.size())),
sj = j + k / 2 ; // 数组的起点从0开始,所以si,sj是k/2位置的后一个元素,砍掉之后新数组从它开始
if(nums1[si - 1] > nums2[sj - 1]) {
// nums2的(k/2)的位置值小,所以要把它砍掉
// 砍掉在代码上体现为:递归时的开始位置发生了改变
return find(nums1, i, nums2, sj, k - (sj - j));
// sj - j 表示砍掉的数的个数
} else {
// 否则就是两个数组中(k/2)位置的数nums1 <= nums2
return find(nums1, si, nums2, j, k - (si - i));
}
}
};
【小结】
1、本题最重要的思路之一就是将寻找中位数的一部分方法抽象出来,即:根据索引寻找数组中的第k个数的值
(2)LeetCode5:最长回文子串
本题总体思路采用暴力枚举,对于每一个字符,都以之作为中间元素往左右寻找。选定中心点后,分奇偶,如果回文串为奇数个,那么中心元素不需要有对称相同的元素;如果是偶数个,那么每个对应的对称元素都要相同,对每个字符都把其考虑一下奇偶情况,最后选出最长的回文串。
假设把i作为中心点,那么:
①.偶数:
起点: i , 终点:i + 1
②.奇数:
起点:i - 1 ,终点:i + 1
代码展示:
class Solution {
public:
string longestPalindrome(string s) {
string res;
for(int i = 0; i < s.size(); i ++) {
// 奇数
int l = i - 1,
r = i + 1;
while (l >= 0 && r < s.size() && s[l] == s[r]) l --, r ++; // 当两者不相等的时候,循环结束
// 由于是while循环,所以指针会到下一个不符合条件的索引时才跳出循环
if(res.size() < ((r - 1) - (l + 1) + 1)) res = s.substr(l + 1, ((r - 1) - (l + 1) + 1));
//如果目标数组小于新的回文数组,那么就取新的部分
// 偶数
l = i;
r = i + 1;
while (l >= 0 && r < s.size() && s[l] == s[r]) l --, r ++; // 当两者不相等的时候,循环结束
// 由于是while循环,所以指针会到下一个不符合条件的索引时才跳出循环
if(res.size() < ((r - 1) - (l + 1) + 1)) res = s.substr(l + 1, ((r - 1) - (l + 1) + 1));
}
return res;
}
};
【小结】这题主要在如何分奇偶上,在索引上如何分奇偶呢?偶数数在于以中心点为原点,向前移动,后一个数向后一直移动,这样能够保证所有数都遍历到,包括中心店;奇数则是不要管中心点,每次在外循环管中心点即可
(3)LeetCode6:Z字形变换
这类似于对一个字符串进行加密,不过它的排列不是一个标准的Z字形,类似这样:
最后读取字符加密的时候是按照S形去读的。本题可以用几何的图形思想去考虑,定义上下/左右两个方向变量,让它按照题目所给的移动规律去移动,然后最后去读一下生成的图形的加密字符串;当然,我们也可以根据某种数字规律来寻找最后的结果。
○ 数字规律:
对于行数是 n 的情况:
①.对于第一行和最后一行,是公差为 2(n−1) 的等差数列,首项是 0 (第一行)和 n−1(最后一行);
②.对于第 i 行(0<i<n−1,即中间行的元素),是两个公差为 2(n−1) 的等差数列交替排列,首项分别是 i 和 2n−i−2;
○ 所以对于同一个循环,我们要分情况讨论:
代码展示:
class Solution {
public:
string convert(string s, int numRows) {
string res; // 最终的目标字符串
int m = (numRows-1) * 2; // 这是公差,单独提出来
if (numRows == 1) return s; // 极端情况,无法区分顶端行与中间行,只有一行元素
for (int i = 0; i < numRows; i ++) { // i是用来控制行数的
if (i == 0 || i == numRows - 1) { // 如果是第一行或最后一行
for (int j = i; j < s.size(); j += m) // 这是对这一行的元素进行遍历
res += s[j]; // 将相应的内容加到字符串的末尾,由于最后读的时候是一行一行读的
} else {
for (int k = i, j = numRows * 2 - 1 - i - 1;
j < s.size() || k < s.size();
j += m, k += m) { // 中间元素的首项有两种情况,把它们放到同一个循环中去遍历
if (k < s.size()) res += s[k]; // k循环完就循环j,交替进行
if (j < s.size()) res += s[j];
}
}
}
return res;
}
};