LeetCode总结

终于把LeetCode免费题目都做完了,接下来要做第二遍,想把第一遍不会做的题目整理一下思路,为接下来的google面试准备一下,万一过了呢,哈哈。


315 Count of Smaller Numbers After Self
题意:计算每个元素右面有多少个元素小于它
从后向前遍历,维护一个排好序的数组,每次插入新元素时确定其在有序数组的位置,就是要求的右面有多少个元素小于它。在插入新元素的时候就可以用二分了。

int binarySearch(vector<int>& nums,  int target){
        int low = 0;
        int high = nums.size()-1;
        while (low<=high){
            int mid = low+((high-low)>>1);
            if (nums[mid]<target) low = mid+1;
            else high = mid - 1;
        }
        return low;
    }

368 Largest Divisible Subset
题意:给定一个数组,找出可整除的最大集合,也就是在这个集合中所有数字都能互相整除。
思路:比较基本的dp题,但要保存每个数的上一个可整除的数,需要维护一个parent数组

vector<int> largestDivisibleSubset(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        vector<int> ret;
        int n = nums.size();
        if (n==0) return ret;
        vector<int> dp(n,1);
        vector<int> parent(n,-1);
        int maxP = 0;
        int maxL = -1;
        for (int i = 1; i < n; ++i){
            for (int j = 0; j < i; ++j){
                if (nums[i]%nums[j]==0 && dp[i]<dp[j]+1){
                    dp[i] = dp[j]+1;
                    parent[i] = j;
                    if (dp[i]>maxL){
                        maxP = i;
                        maxL = dp[i];
                    }
                }
            }
        }
        while (maxP!=-1){
            ret.push_back(nums[maxP]);
            maxP = parent[maxP];
        }
        reverse(ret.begin(),ret.end());
        return ret;
    }

372 Super Pow
题意:求a的b次方,b是一个很大的数,用数组保存
思路:递归+快速幂取模。设b为[1,2,3], superPow(a,b)=a123=a120a3=(a12)10a3 ,b弹出最后一个元素后变为[1,2],即可递归求解。

int quickMod(int a, int b){
        int ans = 1;
        a = a%1337;
        while (b){
            if (b & 0x1) ans = (ans*a)%1337;
            b >>= 1;
            a = (a*a)%1337;
        }
        return ans%1337;
    }
    int superPow(int a, vector<int>& b) {
        if (b.size()==0) return 1;
        int last = b.back();
        b.pop_back();
        return (quickMod(superPow(a,b),10)*quickMod(a,last))%1337;
    }

375 Guess Number Higher or Lower II
题意:猜数字,保证赢的情况下总金额最少
思路:基本dp,但是一维dp不行,必须dp[start][end]保存

int getMoneyAmount(int n) {
        vector<vector<int>> table(n+1,vector<int>(n+1,0));
        return dp(table,1,n);
    }
    int dp(vector<vector<int>> &table, int s, int e){
        if (s>=e) return 0;
        if (table[s][e]!=0) return table[s][e];
        int amin = INT_MAX;
        for (int i=s;i<=e;i++){
            int tmp = i+max(dp(table,s,i-1),dp(table,i+1,e));
            amin = min(amin,tmp);
        }
        table[s][e]=amin;
        return amin;
    }

330 Patching Array
题意:给一数组和整数n,确保数组中的数能组合出1~n的所有整数,找出缺失的个数
思路:维护一个变量保存现在能累加到的值a,也就是1到a所有值都已经可以得到,那么miss = a+1即是缺失的值。对下一个数字num[i],如果其小于等于a+1,那么我们依然可以利用已有的数获得它,a也就更新为a+num[i],miss=miss+num[i],如果其大于a+1,那么说明a+1是我们获取不到的,就是我们缺失的值,a更新为a+a+1,miss=miss+a+1=miss+miss;
坑:miss类型要注意,如果n为INT_MAX,那么miss必须设置为long才可以过corner case.所以不管任何题目都要做边界测试,INT_MAX,INT_MIN必须能过才行。

int minPatches(vector<int>& nums, int n) {
        long miss = 1;
        int i = 0;
        int count = 0;
        while (miss <= n){
            if (i < nums.size() && nums[i] <= miss){
                miss += nums[i++];
            }else{
                count++;
                miss += miss;
            }
        }
        return count;
    }

264 Ugly Number II
题意:找出第n个丑数,丑数为因数只包含2,3,5的数字,比如1,2,3,4,5,6,8,9,10
思路:丑数*2,3,5仍然是丑数,维护三个变量p2,p3,p5,分别表示乘以2,3,5的三个丑数在ugly数组中的位置。最开始都指向第一个丑数1,分别乘以2,3,5,取乘积最小的2作为下一个丑数放入ugly数组,同时p2++

int nthUglyNumber(int n) {
        int p2,p3,p5;
        p2 = p3 = p5 = 0;
        vector<int> ugly(n);
        ugly[0]=1;
        for (int i=1;i<n;++i){
            int tmp = min(ugly[p2]*2,min(ugly[p3]*3,ugly[p5]*5));
            if (tmp == ugly[p2]*2) p2++;
            if (tmp == ugly[p3]*3) p3++;
            if (tmp == ugly[p5]*5) p5++;
            ugly[i] = tmp;
        }
        return ugly[n-1];
    }

363 Max Sum of Rectangle No Larger Than K
题意:找出一个矩形区域,使得其和为不大于K的最大值
思路:此题可拆分成两题,第一是矩形区域的遍历,维护两个变量left,right分别表示左右列,再保存一个sum的数组,维数等于矩形的行数。sum[i]表示left至right这块区域内从迪一行累加到第i行的和,那么sum[j] - sum[i]即可表示行i到行j这段区域的和。
第二题是给定一个数组,求子数组的和不大于K的最大值。方法是维护一个set,对于每个新插入的值sum[j],先计算大于等于sum[j]-k的最小值,然后k-那个最小值就是不大于k的最大值。具体见https://www.quora.com/Given-an-array-of-integers-A-and-an-integer-k-find-a-subarray-that-contains-the-largest-sum-subject-to-a-constraint-that-the-sum-is-less-than-k

int maxSumSubmatrix(vector<vector<int>>& matrix, int k) {
        int row = matrix.size();
        int col = matrix[0].size();
        int re=INT_MIN;
        for (int l=0;l<col;++l){
            vector<int> sum(row,0);
            for (int r=l;r<col;++r){
                for (int i=0;i<row;++i){
                    sum[i]+=matrix[i][r];
                }
                int curSum = 0, m_maxSum = INT_MIN;
                set<int> curSet;
                curSet.insert(0);
                for (int s:sum){
                    curSum += s;

                    auto it = curSet.lower_bound(curSum-k);
                    if (it!=curSet.end()) m_maxSum=max(m_maxSum,curSum-*it);
                    curSet.insert(curSum);
                }
                re = max(re,m_maxSum);
            }

        }
        return re;
    }

33&81 Search in Rotated Sorted Array(1,2)
题意:一个数组有移位,找出给定目标值的下标,不存在返回-1
思路:二分查找,多判断一次中间元素是否大于队头(或队尾),大于队头(队尾),说明中间往左有序,否则中间往右有序。但是注意二分边界。有重复的数字情况稍有不同,需要判断中间元素和末尾元素是否相等。

int search(vector<int>& nums, int target) {
        if (nums.size()==0) return -1;
        int low = 0, high = nums.size()-1;
        while (low <= high){
            int mid = low + (high - low)/2;
            if (nums[mid] == target) return mid;
            if (nums[mid]>nums[high]){
                if (target<nums[mid] && target>=nums[low]){
                    high = mid - 1;
                }else low = mid + 1;
            }else{
                if (target>nums[mid] && target <= nums[high]){
                    low = mid +1;
                }else{
                    high = mid - 1;
                }
            }
        }
        return -1;
    }
bool search(vector<int>& nums, int target) {
        int low = 0;
        int high = nums.size()-1;
        while (low <= high){
            int mid = low + ((high-low)>>1);
            if (nums[mid]==target) return true;
            if (nums[mid]>nums[high]){
                if (nums[mid]>target && target>=nums[low]){
                    high = mid - 1;
                }else low = mid + 1;
            }else if (nums[mid]<nums[high]){
                if (target>nums[mid] && target<=nums[high])
                    low = mid+1;
                else
                    high = mid - 1;
            }else
                high--;
        }
        return false;
    }

287 Find the Duplicate Number
题意:n+1个数,含1~n,肯定至少有一个数字是重复的,找出重复数字。
思路:此题有两种要求,一种可以改变数组,采用数组下标寻地址法存,1就存在位置0,,2存在位置1,n就存在n-1位置,这样在n位置上的数一定是多出来的。
第二种不能改变数组,那就采用快慢双指针法。
做法一:

int findDuplicate(vector<int>& nums) {
        for (int i=0;i<nums.size();++i){
            while (nums[i]!= nums[nums[i]-1] && nums[i]-1!=i){
                swap(nums[i],nums[nums[i]-1]);
            }
        }
        return nums[nums.size()-1];
    }

做法二:

int findDuplicate(vector<int>& nums) {
        int slow = nums[0];
        int fast = nums[nums[0]];
        while (slow!=fast){
            slow = nums[slow];
            fast = nums[nums[fast]];
        }
        slow = 0;
        while (slow != fast){
            slow = nums[slow];
            fast = nums[fast];
        }
        return slow;
    }

283 Move Zeroes
题意:将一个数组中的所有0都移到最后
题目不难,我的思路是将非零数与最前面的0进行交换,每一次交换前都需要遍历寻找最前面的零的位置,复杂度特别高。好的思路:设置一个插入下标,把非零值存入原数组,最后再补上所有零。

public void moveZeroes(int[] nums) {
    if (nums == null || nums.length == 0) return;        

    int insertPos = 0;
    for (int num: nums) {
        if (num != 0) nums[insertPos++] = num;
    }        

    while (insertPos < nums.length) {
        nums[insertPos++] = 0;
    }
}

268 Missing Number
题意:0~n一共n+1个数,现在缺少了一个,找出数组中缺少的数
我的做法是通过补充一个数(n+1),每个数都放到它该放的地方,然后result记录n+1这个数被交换到数组中的哪一个位置,来判断缺少哪个数。

int missingNumber(vector<int>& nums) {
        int ret = 0;
        int n = nums.size();
        nums.push_back(n+1);
        for (int i=0;i<=n;++i){
            while (nums[i]!=n+1 && nums[i]!=i){
                swap(nums[i],nums[nums[i]]);
            }
            if (nums[i]==n+1) ret = i;
        }
        return ret;
    }

最佳答案的思路是异或,因为a xor b xor a = b,所以可以通过异或抵消掉出现两次的

public int missingNumber(int[] nums) {

    int xor = 0, i = 0;
    for (i = 0; i < nums.length; i++) {
        xor = xor ^ i ^ nums[i];
    }

    return xor ^ i;
}

238 Product of Array Except Self
题意:除自己之外的乘积,不能用除法,不用额外空间
思路:左右两趟遍历,巧妙

vector<int> productExceptSelf(vector<int>& nums) {
        vector<int> ret(nums.size(),0);
        ret[0] = 1;
        int n = nums.size();
        for (int i=1;i<n;++i){
            ret[i] = ret[i-1]*nums[i-1];
        }
        int right = 1;
        for (int i=n-1;i>=0;i--){
            ret[i] *= right;
            right *= nums[i];
        }
        return ret;
    }

229 Majority Element II
题意:找出出现次数超过n/3次的数,提示:这样的数最多有两个
思路:类似找超过一半次数的数,维护两个变量,分别存候选数,另外两个cnt计数器,保存候选数出现的次数。类似的如果找超过n/4的数就维护三个变量。

vector<int> majorityElement(vector<int>& nums) {
        int can1,can2,cnt1(0),cnt2(0);
        for (int num:nums){
            if (num==can1) cnt1++;
            else if (num==can2) cnt2++;
            else if (cnt1==0) {can1=num;cnt1=1;}
            else if (cnt2==0) {can2=num;cnt2=1;}
            else{
                cnt1--;cnt2--;
            }
        }
        cnt1=cnt2=0;
        for (int num:nums){
            if (num==can1) cnt1++;
            else if(num==can2) cnt2++;
        }
        vector<int> re;

        if (cnt1>(nums.size()/3)) re.push_back(can1);
        if (cnt2>(nums.size()/3)) re.push_back(can2);
        return re;
    }

209 Minimum Size Subarray Sum
题意:给一个数组,求数组中最小长度的子数组的和大于等于给定数s
思路:我的做法是纪录sum数组,子数组的和就用sum数组减,然后gap从0开始遍历。但这样做的最差情况复杂度有 O(n2) 了。
正确思路:两个指针i,j,i指向子数组头,j指向子数组的尾,j先移动到子数组的和大于s,然后再移动i,并纪录和大于s时的最小数组长度

public int minSubArrayLen(int s, int[] a) {
  if (a == null || a.length == 0)
    return 0;

  int i = 0, j = 0, sum = 0, min = Integer.MAX_VALUE;

  while (j < a.length) {
    sum += a[j++];

    while (sum >= s) {
      min = Math.min(min, j - i);
      sum -= a[i++];
    }
  }

  return min == Integer.MAX_VALUE ? 0 : min;
}

162 Find Peak Element
题意:找到一个数组中的局部极大值点,相邻元素不相等
思路:二分查找,比较mid指向的和mid+1指向的大小,如果A[mid]>A[mid+1],那么high = mid,否则low=mid+1

int findPeakElement(vector<int>& nums) {
        int n = nums.size();
        int low = 0;
        int high = n - 1;
        while (low < high){
            int mid = low + ((high-low)>>1);
            if (nums[mid] < nums[mid+1]){
                low = mid + 1;
            }else{
                high = mid;
            }
        }
        return low;
    }

Maximum Product Subarray
题意:乘积最大的子数组
思路:再做又错了,保存最大最小值,有两个地方没有注意:1,初始化curmax,curmin,maxP用nums[0]初始化,可以避免只有一个元素的情况出错。2,max函数求最大时要将curmax*nums[i]与nums[i]本身进行比较,这点很重要!

int maxProduct(vector<int>& nums) {
        int curmax = nums[0];
        int curmin = nums[0];
        int ret = nums[0];
        for (int i = 1; i < nums.size(); ++i){
            if (nums[i] > 0){
                curmax = max(nums[i],curmax*nums[i]);
                curmin = min(nums[i],curmin*nums[i]);
            }else{
                int tmp = curmax;
                curmax = max(nums[i],curmin*nums[i]);
                curmin = min(nums[i],tmp*nums[i]);
            }
            if (curmax > ret) ret = curmax;

        }
        return ret;
    }

82 Remove Duplicates from Sorted List II
题意:删除单链表中的重复节点,要求只要重复的就删除
这题还是比较难的,简直offer上也有这个题,但是那个做法太过于复杂。比较好的思路是用一个多余节点放在head前,然后维护一个pre指针,pre指向的是确定不重复的一个节点,pre->next指向下一个可能重复也可能不重复的节点,必须判断下一个节点确实和下下个节点不重复,才可以把pre向前移动一位。

ListNode* deleteDuplicates(ListNode* head) {
        ListNode dummy(0);
        ListNode* pre = &dummy;
        pre->next = head;
        ListNode* cur = head;
        while (cur){
            while (cur->next && cur->next->val == cur->val){
                cur = cur -> next;
            }
            //cur == pre->next说明没有进入上面的循环
            if (cur == pre->next) pre = pre -> next;
            else{
                pre -> next = cur -> next;
            }
            cur = cur -> next;
        }
        return dummy.next;
    }

对比两题,下面的算法是针对只删除重复的节点,保留第一个。思路都是相似的,不过这里找到第一个不重复的节点就可把pre指向它并且向前移动一位。

ListNode* deleteDuplicates(ListNode* head) {
        ListNode* cur = head;
        ListNode* pre = head;
        while (cur){
            while (cur->next && cur->val == cur->next->val){
                cur = cur->next;
            }
            pre->next = cur->next;
            pre = pre->next;
            cur = cur -> next;
        }
        return head;
    }

84 Largest Rectangle in Histogram
思路: O(n) 做法是用栈保存各个bar的index,注意压栈的时候要保证压入的index对应的bar的高度一定要大于等于栈内所有index对应的bar的高度,这样可以保证在以栈顶元素为高计算面积时,向右的高度都是大于等于当前栈顶元素的高度的。

int largestRectangleArea(vector<int>& height) {
        int n = height.size();
        if (n==0) return 0;
        height.push_back(0);
        stack<int> st;
        //st.push(height[0]);
        int maxS = 0;
        int i = 0;
        while (i<=n){
            if (st.empty() || height[i] >= height[st.top()]){
                st.push(i++);
            }else{
                int h = height[st.top()];
                st.pop();
                int j = st.empty()? -1:st.top();

                maxS = max(maxS,h*(i-j-1));

            }
        }
        return maxS;
    }

更新:谷歌面试没有过,还是编程能力不够强,需要继续努力!
336. Palindrome Pairs
题意:Given a list of unique words. Find all pairs of distinct indices (i, j) in the given list, so that the concatenation of the two words, i.e. words[i] + words[j] is a palindrome.

Example 1:
Given words = [“bat”, “tab”, “cat”]
Return [[0, 1], [1, 0]]
The palindromes are [“battab”, “tabbat”]
Example 2:
Given words = [“abcd”, “dcba”, “lls”, “s”, “sssll”]
Return [[0, 1], [1, 0], [3, 2], [2, 4]]
The palindromes are [“dcbaabcd”, “abcddcba”, “slls”, “llssssll”]
思路:乍看挺难的,好像没有解决的好办法,仔细分析可以发现其实回文可以拆分,分成左边和右边,如果左边已经是回文子串,那么只需要在左边拼接上右边子串的翻转就可以拼成一个回文串。比如abcd拆分成”a” and “bcd”,a是回文的,所以只要在a前面拼上”dcb”就可以构成”dcbabcd”了。
基于这个思路,用map存所有字符串,然后对每一个字符串进行拆分,然后在map中寻找是否存在子串的翻转串即可。

java代码:

public List<List<Integer>> palindromePairs(String[] words) {
        List<List<Integer>> result = new ArrayList<>();
        if (words == null || words.length<2) return result;
        Map<String,Integer> wordMap = new HashMap<String, Integer>();
        for (int i=0;i<words.length;i++){
            wordMap.put(words[i], i);
        }
        for (int i=0;i<words.length;i++){
            for (int j=0; j<=words[i].length(); ++j){
                String pre = words[i].substring(0,j);
                String pos = words[i].substring(j);
                if (isPalindrome(pre)){
                    String toMatch = new StringBuilder(pos).reverse().toString();
                    if (wordMap.containsKey(toMatch) && wordMap.get(toMatch)!= i){
                        List<Integer> list = new ArrayList<Integer>();
                        list.add(wordMap.get(toMatch));
                        list.add(i);
                        result.add(list);
                    }
                }
                if (isPalindrome(pos)){
                    String toMatch = new StringBuilder(pre).reverse().toString();
                    if (wordMap.containsKey(toMatch) && wordMap.get(toMatch)!= i && pos.length()!=0){
                        List<Integer> list = new ArrayList<Integer>();
                        list.add(i);
                        list.add(wordMap.get(toMatch));
                        result.add(list);
                    }
                }
            }
        }
        return result;
    }

    public boolean isPalindrome(String str){
        int st=0,end=str.length()-1;
        while (st<end){
            if (str.charAt(st++)!=str.charAt(end--)) return false;
        }
        return true;
    }

c++代码

vector<vector<int>> palindromePairs(vector<string>& words) {
            unordered_map<string, int> dict;
            vector<vector<int>> ans;
            // build dictionary
            for(int i = 0; i < words.size(); i++) {
                string key = words[i];
                reverse(key.begin(), key.end());
                dict[key] = i;
            }
            // edge case: if empty string "" exists, find all palindromes to become pairs ("", self)
            if(dict.find("")!=dict.end()){
                for(int i = 0; i < words.size(); i++){
                    if(i == dict[""]) continue;
                    if(isPalindrome(words[i])) ans.push_back({dict[""], i}); // 1) if self is palindrome, here ans covers concatenate("", self) 
                }
            }

            for(int i = 0; i < words.size(); i++) {
                for(int j = 0; j < words[i].size(); j++) {
                    string left = words[i].substr(0, j);
                    string right = words[i].substr(j, words[i].size() - j);

                    if(dict.find(left) != dict.end() && isPalindrome(right) && dict[left] != i) {
                        ans.push_back({i, dict[left]});     // 2) when j = 0, left = "", right = self, so here covers concatenate(self, "")
                    }

                    if(dict.find(right) != dict.end() && isPalindrome(left) && dict[right] != i) {
                        ans.push_back({dict[right], i});
                    }
                }
            }

            return ans;        
        }

        bool isPalindrome(string str){
            int i = 0;
            int j = str.size() - 1; 

            while(i < j) {
                if(str[i++] != str[j--]) return false;
            }

            return true;
        }

214 Shortest Palindrome
题意:给定一个字符串,求出在前面补最少的字母使之构成回文
思路:翻转字符串然后拼到原串的后面,利用kmp求出翻转串的末几位和原串的头几位匹配,这一部分是一定会构成回文的(因为翻转后还能和原串匹配上),所以只需要在原串的头部补上翻转串没有匹配上的头几位就可以了(因为翻转串没匹配上的头几位就是原串没匹配上的末位翻转的结果)

string shortestPalindrome(string s) {
        string rev_s = s;
        reverse(rev_s.begin(),rev_s.end());
        string str = s + '#' + rev_s;
        vector<int> next(str.size()+1,0);
        next[0] = -1;
        for (int i = 1; i <= str.size(); ++i){
            int q = next[i-1];
            while (q != -1 && str[i-1]!=str[q])
                q = next[q];
            next[i] = q + 1;
        }
        int k = s.size() - next[str.size()];
        return rev_s.substr(0,k)+s;
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值