退役挺久了,现在写题竟是很慢了,不敢说自己打过ACM,LeetCode上的题似乎在面试的时候容易被问到,就写写,来记录一下。
1. Two Sum
水题,求数组中两个相加起来等于某个数的下标。用HashMap比较快。
2. Add Two Numbers
大数相加,一个大数以链表形式给出,太久没写链表了,写的很慢。主要考虑最后的进位可能不为0,以及两个链表不等长。
3. Longest Substring Without Repeating Characters
给一个字符串,求最长的没有重复字母的子串。用一个数组dp,dp[i]表示以第i个位置结尾的没有重复字母的最长子串,利用HashMap记录key字母最后一次出现的下标,转移方程为:
if (map.get(s.charAt(i)) == null) {
dp[i] = dp[i-1] + 1;
} else {
int prei = map.get(s.charAt(i));
dp[i] = (dp[i-1] + 1) < (i - prei)?(dp[i-1] + 1):(i - prei);
}
4.Median of Two Sorted Arrays(重)
求两个有序数组的中位数。这题是重点,似乎面试的时候有被问过。思想是二分,大概就是用短的数组二分,即,用一个i记录短数组的分割线,用一个j记录长数组的分割线,i的左边和j的左边应当等于(len1 + len2 + 1) / 2,于是可以二分i,j = ((len1 + len2 + 1) / 2 - i);然后就是一堆令人窒息的分类讨论:
int l = 0, r = len1;
int i, j;
while (l <= r) {
i = (l + r) / 2;
j = ((len1 + len2 + 1) / 2 - i);
if (i > 0 && nums2[j] < nums1[i - 1]) {
r = i - 1;
continue;
}
if (i <= len1 - 1 && j > 0 && nums2[j - 1] > nums1[i]) {
l = i + 1;
continue;
}
if (i == 0) {
if (isOdd) {
return nums2[j - 1];
} else {
if (j < len2) {
return (Math.min(nums2[j], nums1[i]) + nums2[j - 1]) / 2.0;
} else {
return (nums1[i] + nums2[j - 1]) / 2.0;
}
}
} else if (i <= len1 - 1) {
if (isOdd) {
return Math.max(nums2[j - 1], nums1[i - 1]);
} else {
return (Math.max(nums2[j - 1], nums1[i - 1]) + Math.min(nums1[i], nums2[j])) / 2.0;
}
} else {
if (isOdd) {
if (j > 0) {
return Math.max(nums2[j - 1], nums1[i - 1]);
} else {
return nums1[i - 1];
}
} else {
if (j > 0) {
return (Math.max(nums2[j - 1], nums1[i - 1]) + nums2[j]) / 2.0;
} else {
return (nums1[i - 1] + nums2[j]) / 2.0;
}
}
}
}
5.Longest Palindromic Substring(次重)
求最长回文子串。这也是一个经典问题,使用马拉车Manacher算法,可以O(n)地解决问题。马拉车算法的主要思想是:
1.给字符串添加特殊字符:
s = "google"
变为
newS = "#g#o#o#g#l#e#"
char* newStr = (char*)malloc((s.length() * 3 + 1)*sizeof(char));
int len = 0;
for(int i = 0;i < s.length();i++) {
newStr[len++] ='#';
newStr[len++] = s[i];
}
newStr[len++] = '#';
2.用maxLen[i]表示以i为中心,最长的回文串中i到回文串右端点的距离,相当于回文串长度的一半了。用maxRight记录之前出现过的最长回文串的右端点位置,maxIndex记录之前出现过的最长回文串的中心。
newStr[len++] = '#';
// printf("%s\n", newStr);
int* maxLen = new int[len+1];
int maxIndex = 0;
int maxRight = 0;
3.从0~n算maxLen:
如果i < maxRight,说明i在最长回文串里,那么maxLen[i] = min(maxLen[j], maxRight - i + 1);其中j为以maxIndex为中心i的对称位置,j = maxIndex - (i - maxIndex);
如果i >= maxRight,置maxLen[i] = 1;
接着再以i为中心,向两边比较算回文串。比较从i+maxLen[i]与i - maxLen[i]开始,这样就达到了节省时间的目的。
得到maxLen[i]后,如果>maxIndex处的maxLen,就更新maxIndex和maxRight;
maxLen[0] = 1;
for(int i = 1;i < len;i++) {
if(i < maxRight) {
int j = 2 * maxIndex - i;// 以maxIndex为中心对称的坐标
maxLen[i] = min(maxLen[j], maxRight - i + 1);
} else {
maxLen[i] = 1;
}
while(i - maxLen[i] >= 0 && i + maxLen[i] < len
&& newStr[i-maxLen[i]] == newStr[i+maxLen[i]]) {
maxLen[i]++;
}
if(maxLen[i] >= maxLen[maxIndex]) {
maxIndex = i;
maxRight = i + maxLen[i] - 1;
}
}
int maxLeft = maxIndex - maxLen[maxIndex] + 1;
int originLen = maxRight / 2 - 1 - maxLeft / 2 + 1;
return s.substr(maxLeft / 2, originLen);
6.ZigZag Conversion
字符串排成z字形后,按行输出
Input: s = "PAYPALISHIRING", numRows = 4
Output: "PINALSIGYAHRPI"
Explanation:
P I N
A L S I G
Y A H R
P I
就是个模拟题,速度够快就好。
7.Reverse Integer
将一个整数反转,123变成321。
8. String to Integer (atoi)
将字符串转为整形,注意一些条件即可。挺水。
9.Palindrome Number
判断一个数字是不是回文的,水题。
10. Regular Expression Matching(重)
匹配正则字符串,实际上只有两条规则:
'.' Matches any single character.
'*' Matches zero or more of the preceding element.
Note:
s could be empty and contains only lowercase letters a-z.
p could be empty and contains only lowercase letters a-z, and characters like . or *.
就是看s能不能被p匹配。
使用动态规划解决,可以达到O(s.length() * p.length())的复杂度。
用dp[i][j]表示用p的前i个字符匹配s的前j个字符是否可行。i,j从1开始计算,另外需要讨论i大于0,j等于0的情况的dp[i][j],即存在星号,p的前面一部分不匹配的情况。遇到星号的时候,如果dp[i-2][j]能匹配那么dp[i][j]必然能匹配(即前一个字母出现次数为0),dp[i-1][j]能匹配也必然能匹配,因为*可以相当于没出现,前一个字母仅出现一次,另外就是如果p前一个字母i-2能跟s[i-1]匹配,考虑dp[i-1][j-1]的情况。
代码为:
dp[0][0] = 1;
for(int i = 1;i <= plen;i++) {
if(p[i-1] == '*') {
dp[i][0] = dp[i-2][0];
}
}
for(int i = 1;i <= plen;i++) {
for(int j = 1;j <= slen;j++) {
if(p[i-1] == s[j-1] || p[i-1] == '.') {
dp[i][j] = max(dp[i][j], dp[i-1][j-1]);
} else if(p[i-1] == '*') {
dp[i][j] = max(dp[i-1][j], dp[i][j]);
dp[i][j] = i >= 2? max(dp[i-2][j], dp[i][j]):dp[i][j];
if(i >= 2 && (p[i-2] == s[j-1] || p[i-2] == '.')) {
dp[i][j] = max(dp[i-1][j-1], dp[i][j]);
dp[i][j] = max(dp[i][j-1], dp[i][j]);
}
} else {
dp[i][j] = 0;
}
// printf("%d %d %d ",i, j, dp[i][j]);
// printf("%c %c\n", p[i-1], s[j-1]);
}
}
11.Container With Most Water
如下图,给了高度,求能装的最多的水,有点像木桶原理。
用两个指针,i指向左边高度,j指向右边高度。
while(i < j) {
area = max(area,min(height[i],height[j]) * (j - i));
if(height[j] >= height[i]) {
i++;
} else {
j--;
}
}
12.Integer to Roman
将数字转换成罗马大写表示,给出了一些规则。挺简单,从高位算到低位分类讨论即可。譬如58 = 5 * 10 + 8*1,50为L,8为VII。水题。
13.Roman to Integer
将罗马大写表示转换成数字,水题。
14. Longest Common Prefix
最长公共前缀,每次比较当前字符串和前缀即可,最开始前缀等于第一个字符。
15.3Sum(次重)
从数组中找出相加等于0的三元组,要求不能重复。给数组排序,再用map记录每个数最后出现的index,然后用两重循环如下:
for(int i = 0;i < len;i++) {
for(int j = i+1;j < len;j++) {
int key = -(nums[i] + nums[j]);
if(map.containsKey(key)) {
int val = map.get(key);
if(val > j) {
res.add(Arrays.asList(nums[i], nums[j], nums[val]));
}
j = map.get(nums[j]);
}
}
i = map.get(nums[i]);
16.3Sum Closest
从数组中找出三个数相加起来离target最近。
解:从0到len-1遍历i,nums[i]作为固定的数,在i的右边找两个数与nums[i]相加离target最近:
for(int i = 0; i < len;i++) {
int l = i+1, r = len-1;
while(l < r) {
int nowSum = nums[i] + nums[l] + nums[r];
if(minAbs > Math.abs(nowSum - target)) {
minAbs = Math.abs(nowSum - target);
res = nowSum;
}
if(nowSum - target > 0) {
r--;
} else {
l++;
}
}
}
17.Letter Combinations of a Phone Number
就是给出一串数字,输出对应到老人机九键键盘上所有可能的字符组合。模拟题,应该不太会在面试的时候考。
18.4Sum(次重)
类似3Sum,不过换成四个数相加等于某个数,找出所有不重复的四元组。其实3Sum用map也没有很快,可以用i,j两重循环,再用p,q左右两个下标找其余两个数,视情况来挪动p和q。
for(int i = 0;i < len;i++) {
for(int j = i+1;j < len;j++) {
int p = j + 1,q = len - 1;
while(p < q) {
// System.out.println(nums[i] + " " + nums[j] + " " + nums[p] + " " + nums[q]);
int tempSum = nums[p] + nums[q] + nums[i] + nums[j];
if(tempSum == target) {
res.add(Arrays.asList(nums[i], nums[j], nums[p], nums[q]));
while(p < len - 1 && nums[p] == nums[p+1]) {
p++;
}
while(q > j && nums[q] == nums[q-1]) {
q--;
}
}
if(tempSum > target) {
q--;
} else if(tempSum < target){
p++;
} else {
p++;
q--;
}
}
while(j < len - 1 && nums[j] == nums[j+1]) {
j++;
}
}
while(i < len - 1 && nums[i] == nums[i+1]) {
i++;
}
}