LeetCode 刷题记(一)

退役挺久了,现在写题竟是很慢了,不敢说自己打过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

如下图,给了高度,求能装的最多的水,有点像木桶原理。


屏幕快照 2019-05-07 下午4.23.57.png

用两个指针,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++;
            }
        }

你可能感兴趣的:(LeetCode 刷题记(一))