leetcode 地铁看题

1.  5.Longest Palindromic Substring

Given astring s, find the longest palindromic substring in s.You may assume that the maximum length of s is 1000.

Example:

Input: "babad"

 

Output: "bab"

 

Note: "aba" is also a valid answer.

Example:

Input: "cbbd"

 

Output: "bb"

Quick Navigation

Summary

This article isfor intermediate readers. It introduces the following ideas: Palindrome,Dynamic Programming and String Manipulation. Make sure you understand what apalindrome means. A palindrome is a string which reads the same in bothdirections. For example, \textrm{''aba''}”aba” is a palindome, \textrm{''abc''}”abc” is not.

Solution


Approach #1 (Longest Common Substring)[Accepted]

Common mistake

Some people willbe tempted to come up with a quick solution, which is unfortunately flawed(however can be corrected easily):

Reverse SS andbecome S'S. Find thelongest common substring between SS and S'S, which mustalso be the longest palindromic substring.

This seemed towork, let’s see some examples below.

Forexample, S =\textrm{''caba"}S=”caba"S' =\textrm{''abac''}S​=”abac”.

The longestcommon substring between SS and S'S is \textrm{''aba''}”aba”, which is theanswer.

Let’s tryanother example: S = \textrm{''abacdfgdcaba''}S=”abacdfgdcaba”S' =\textrm{''abacdgfdcaba''}S​=”abacdgfdcaba”.

The longestcommon substring between SS and S'S is \textrm{''abacd''}”abacd”. Clearly, thisis not a valid palindrome.

Algorithm

We could seethat the longest common substring method fails when there exists a reversedcopy of a non-palindromic substring in some other part of SS. To rectifythis, each time we find a longest common substring candidate, we check if thesubstring’s indices are the same as the reversed substring’s original indices.If it is, then we attempt to update the longest palindrome found so far; ifnot, we skip this and find the next candidate.

This gives usan O(n^2)O(n2​) DynamicProgramming solution which uses O(n^2)O(n2​) space(could be improved to use O(n)O(n) space).Please read more about Longest Common Substring here.


Approach #2 (Brute Force) [Time LimitExceeded]

The obviousbrute force solution is to pick all possible starting and ending positions fora substring, and verify if it is a palindrome.

ComplexityAnalysis

  • Time complexity : O(n^3)O(n​3​​). Assume that nn is the length of the input string, there are a total of \binom{n}{2} = \frac{n(n-1)}{2}(​2​n​​)=​2​​n(n−1)​​ such substrings (excluding the trivial solution where a character itself is a palindrome). Since verifying each substring takes O(n)O(n) time, the run time complexity is O(n^3)O(n​3​​).
  • Space complexity : O(1)O(1).

Approach #3 (Dynamic Programming)[Accepted]

To improve overthe brute force solution, we first observe how we can avoid unnecessaryre-computation while validating palindromes. Consider the case \textrm{''ababa''}”ababa”. If we alreadyknew that \textrm{''bab''}”bab” is apalindrome, it is obvious that \textrm{''ababa''}”ababa” must be a palindrome since the twoleft and right end letters are the same.

We define P(i,j)P(i,j) asfollowing:

P(i,j)={true,false,if the substring Si…Sj is a palindromeotherwise. P(i,j)={true,if the substring Si…Sj is apalindromefalse,otherwise. 

Therefore,

P(i, j) = ( P(i+1, j-1) \text{ and } S_i ==S_j )P(i,j)=(P(i+1,j−1) and Si​​==Sj​​)

The base casesare:

P(i, i) = trueP(i,i)=true

P(i, i+1) = ( S_i == S_{i+1} )P(i,i+1)=(Si​​==Si+1​​)

This yields astraight forward DP solution, which we first initialize the one and two letterspalindromes, and work our way up finding all three letters palindromes, and soon...

ComplexityAnalysis

  • Time complexity : O(n^2)O(n​2​​). This gives us a runtime complexity of O(n^2)O(n​2​​).
  • Space complexity : O(n^2)O(n​2​​). It uses O(n^2)O(n​2​​) space to store the table.

AdditionalExercise

Could youimprove the above space complexity further and how?


Approach #4 (Expand Around Center)[Accepted]

In fact, wecould solve it in O(n^2)O(n2​) time usingonly constant space.

We observe thata palindrome mirrors around its center. Therefore, a palindrome can be expandedfrom its center, and there are only 2n - 12n−1 suchcenters.

You might beasking why there are 2n - 12n−1 butnot nn centers?The reason is the center of a palindrome can be in between two letters. Suchpalindromes have even number of letters (such as \textrm{''abba''}”abba”) and its center are between thetwo \textrm{'b'}’b’s.

public String longestPalindrome(String s) {

    int start = 0, end = 0;

    for(int i = 0; i < s.length(); i++) {

       int len1 = expandAroundCenter(s,i, i);

       int len2 = expandAroundCenter(s,i, i + 1);

       int len = Math.max(len1, len2);

       if (len > end - start) {

           start = i - (len - 1) / 2;

           end = i + len / 2;

       }

    }

    returns.substring(start, end + 1);

}

 

privateintexpandAroundCenter(String s, int left, int right) {

    int L = left, R = right;

    while(L >= 0 &&R < s.length() && s.charAt(L) == s.charAt(R)) {

       L--;

       R++;

    }

    returnR - L - 1;

}

ComplexityAnalysis

  • Time complexity : O(n^2)O(n​2​​). Since expanding a palindrome around its center could take O(n)O(n) time, the overall complexity is O(n^2)O(n​2​​).
  • Space complexity : O(1)O(1).

Approach #5 (Manacher's Algorithm)[Accepted]

There is evenan O(n)O(n) algorithmcalled Manacher's algorithm, explained here in detail. However, it isa non-trivial algorithm, and no one expects you to come up with this algorithmin a 45 minutes coding session. But, please go ahead and understand it, Ipromise it will be a lot of fun.

 

 

 

2.  97.Interleaving String

Given s1s2s3,find whether s3 is formed by the interleaving of s1 and s2.

For example,
Given:
s1 = 
"aabcc",
s2 = 
"dbbca",

When s3 = "aadbbcbcac", return true.
When s3 = 
"aadbbbaccc", return false.

 

Quick Navigation

Summary

We need todetermine whether a given string can be formed by interleaving the other twostrings.

Solution


Approach #1 Brute Force [Time LimitExceeded]

The most basicidea is to find every string possible by all interleavings of the two givenstrings(s1s1 and s2s2). In order toimplement this method, we are using recursion. We start by taking the currentcharacter of the first string(s1s1) and thenappending all possible interleavings of the remaining portion of the firststring(s1s1) and the secondstring(s2s2) and comparingeach result formed with the required interleaved string(s3s3). Similarly, wechoose one character from the second string(s2s2) and form allthe interleavings with the remaining portion of s2s2 and s1s1 to checkif the required string s1s1 can beformed.

For implementingthe recursive function, we make the function call recursively as is_Interleave(s1,i+1,s2,j,res+s1.charAt(i),s3)isInterleave(s1,i+1,s2,j,res+s1.charAt(i),s3) in whichwe have chosen the current character from s1s1 and then makeanother function call is_Interleave(s1,i,s2,j+1,res+s2.charAt(j),s3)isInterleave(s1,i,s2,j+1,res+s2.charAt(j),s3), in which thecurrent character of s2s2 is chosen.Here, resres refers tothat portion(interleaved) of s1s1 and s2s2 which hasalready been processed. If anyone of these calls return the result as TrueTrue, it means thatatleast one interleaving gives the required result s3s3. The recursivecalls end when both the strings s1s1 and s2s2 have beenfully processed.

Let's look at asmall example to see how the execution proceeds.

s1="abc"

s2="bcd"

s3="abcbdc"

Firstly wechoose 'a' of s1 as the processed part i.e. res and call the recursive functionconsidering the new strings as s1="bc", s2="bcd",s3="abcbdc". When this function returns a result, we again call therecursive function but with the new strings as s1="abc",s2="cd", s3="abcbdc"

Java

public class Solution {

    publicbooleanis_Interleave(String s1,int i,String s2,int j,String res,String s3)

    {

       if(res.equals(s3) && i==s1.length() && j==s2.length())

           return true;

       boolean ans=false;

       if(i<s1.length())

           ans|=is_Interleave(s1,i+1,s2,j,res+s1.charAt(i),s3);

       if(j<s2.length())

           ans|=is_Interleave(s1,i,s2,j+1,res+s2.charAt(j),s3);

       return ans;

 

    }

    publicbooleanisInterleave(String s1, String s2, Strings3) {

       return is_Interleave(s1,0,s2,0,"",s3);

    }

}

ComplexityAnalysis

  • Time complexity : O(2^(m+n))O(2​(​​m+n)). mm is the length of s1s1 and nn is the length of s2s2.
  • Space complexity : O(m+n)O(m+n). The size of stack for recursive calls can go upto m+nm+n.

Approach #2 Recursion with memoization[Accepted]

Algorithm

In the recursiveapproach discussed above, we are considering every possible string formed byinterleaving the two given strings. But, there will be cases encountered inwhich, the same portion of s1s1 and s2s2 would havebeen processed already but in different orders(permutations). But irrespectiveof the order of processing, if the resultant string formed till now is matchingwith the required string(s3s3), the finalresult is dependent only on the remaining portions of s1s1 and s2s2, but not on thealready processed portion. Thus, the recursive approach leads to redundantcomputations.

This redundancycan be removed by making use of memoization along with recursion. For this, wemaitain 3 pointers i, j, ki,j,k whichcorrespond to the index of the current character of s1, s2, s3s1,s2,s3 respectively.Also, we maintain a 2-d memo array to keep a track of the substrings processedso far. memo[i][j]memo[i][j] stores a1/0 or -1 depending on whether the current portion of strings i.e. upto i^{th}ith indexfor s1s1 andupto j^{th}jth index fors2 has already been evaluated. Again, we start by selecting the currentcharacter of s1s1(pointed by$$i$). If it matches the current character of s3s3(pointedby kk), we include itin the processed string and call the same function recurively as: is\_Interleave(s1,i+1, s2, j, s3, k+1,memo)is_Interleave(s1,i+1,s2,j,s3,k+1,memo)

Thus, here wehave called the function by incrementing the pointers ii and kk since theportion of strings upto those indices has already been processed. Similarly, wechoose one character from the second string and continue. The recursivefunction ends when either of the two strings s1s1 or s2s2 has beenfully processed. If, let's say, the string s1s1 has beenfully processed, we directly compare the remaining portion of s2s2 with theremaining portion of s3s3. When thebacktrack occurs from the recursive calls, we store the value returned by therecursive functions in the memoization array memo appropriatelys so that whenit is encountered the next time, the recursive function won't be called, butthe memoization array itself will return the previous generated result.

Java

public class Solution {

    publicbooleanis_Interleave(String s1, int i, String s2, int j, String s3, int k, int[][] memo) {

       if (i == s1.length()) {

           return s2.substring(j).equals(s3.substring(k));

       }

       if (j == s2.length()) {

           return s1.substring(i).equals(s3.substring(k));

       }

       if (memo[i][j] >= 0) {

           return memo[i][j] == 1 ? true : false;

       }

       boolean ans = false;

       if (s3.charAt(k) == s1.charAt(i) && is_Interleave(s1,i + 1, s2, j, s3, k +1, memo)

                || s3.charAt(k) == s2.charAt(j) && is_Interleave(s1,i, s2, j + 1, s3, k + 1, memo)) {

           ans = true;

       }

       memo[i][j] = ans ? 1 : 0;

       return ans;

    }

    publicbooleanisInterleave(String s1, String s2, Strings3) {

       int memo[][] = new int[s1.length()][s2.length()];

       for (int i = 0; i < s1.length(); i++) {

           for (int j = 0; j < s2.length(); j++) {

                memo[i][j]= -1;

           }

       }

       return is_Interleave(s1, 0, s2, 0, s3, 0, memo);

    }

}

ComplexityAnalysis

  • Time complexity : O(2^(m+n))O(2​(​​m+n)). mm is the length of s1s1 and nn is the length of s2s2.
  • Space complexity : O(m+n)O(m+n). The size of stack for recursive calls can go upto m+nm+n.

Approach #3 Using 2-d Dynamic Programming[Accepted]

Algorithm

The recursiveapproach discussed in above solution included a character from one of thestrings s1s1 or s2s2 in theresultant interleaved string and called a recursive function to check whetherthe remaining portions of s1s1 and s2s2 can beinterleaved to form the remaining portion of s3s3. In the currentapproach, we look at the same problem the other way around. Here, we includeone character from s1s1 or s2s2 and checkwhether the resultant string formed so far by one particular interleaving ofthe the current prefix of s1s1 and s2s2 form aprefix of s3s3.

Thus, thisapproach relies on the fact that the in order to determine whether a substringof s3s3(uptoindex kk), can be formedby interleaving strings s1s1 and s2s2uptoindices ii and jj respectively,solely depends on the characters of s1s1 and s2s2 uptoindices ii and jj only andnot on the characters coming afterwards.

To implementthis method, we'll make use of a 2-d boolean array dpdp. In thisarray dp[i][j]dp[i][j] implies ifit is possible to obtain a substring of length (i+j+2)(i+j+2)which is aprefix of s3s3 by someinterleaving of prefixes of strings s1s1 and s2s2 havinglengths (i+1)(i+1) and (j+1)(j+1) respectively.For filling in the entry of dp[i][j]dp[i][j], we need toconsider two cases:

  1. The character just included i.e. either at i^{th}ith​​ index of s1s1 or at j^{th}jth​​ index of s2s2 doesn't match the character at k^{th}kth​​ index of s3s3, where k=i+j+1k=i+j+1. In this case, the resultant string formed using some interleaving of prefixes of s1s1 and s2s2 can never result in a prefix of length k+1k+1 in s3s3. Thus, we enter FalseFalseat the character dp[i][j]dp[i][j].
  2. The character just included i.e. either at i^{th}ith​​ index of s1s1 or at j^{th}jth​​ index of s2s2 matches the character at k^{th}kth​​ index of s3s3, where k=i+j+1k=i+j+1. Now, if the character just included(say xx) which matches the character at k^{th}kth​​ index of s3s3, is the character at i^{th}ith​​ index of s1s1, we need to keep xx at the last position in the resultant interleaved string formed so far. Thus, in order to use string s1s1 and s2s2 upto indices ii and jj to form a resultant string of length (i+j+2)(i+j+2)which is a prefix of s3s3, we need to ensure that the strings s1s1 and s2s2 upto indices (i-1)(i−1) and jj respectively obey the same property.

Similarly, if wejust included the j^{th}jth characterof s2s2, which matcheswith the k^{th}kth characterof s3s3, we need toensure that the strings s1s1 and s2s2 uptoindices ii and (j-1)(j−1) also obeythe same property to enter a truetrue at dp[i][j]dp[i][j].

This can be madeclear with the following example:

s1="aabcc"

s2="dbbca"

s3="aadbbcbcac"

1 / 36

Java

public class Solution {

    publicbooleanisInterleave(String s1, String s2, Strings3) {

       if (s3.length() != s1.length() + s2.length()) {

           return false;

       }

       boolean dp[][] = new boolean[s1.length() + 1][s2.length() + 1];

       for (int i = 0; i <= s1.length(); i++) {

           for (int j = 0; j <= s2.length(); j++) {

                if (i == 0 && j == 0) {

                    dp[i][j]= true;

                } else if (i== 0) {

                    dp[i][j]= dp[i][j - 1] && s2.charAt(j - 1) == s3.charAt(i + j - 1);

                } else if (j== 0) {

                    dp[i][j]= dp[i - 1][j] && s1.charAt(i - 1) == s3.charAt(i + j - 1);

                } else {

                    dp[i][j]= (dp[i - 1][j] &&s1.charAt(i - 1) == s3.charAt(i + j - 1)) || (dp[i][j -1] && s2.charAt(j - 1) == s3.charAt(i + j - 1));

                }

           }

       }

       return dp[s1.length()][s2.length()];

    }

}

ComplexityAnalysis

  • Time complexity : O(m*n)O(mn). dp array of size m*nmn is filled.
  • Space complexity : O(m*n)O(mn). 2-d DP of size (m+1)*(n+1)(m+1)∗(n+1) is required. mm and nn are the lengths of strings s1s1 and s2s2 repectively.

Approach #4 Using 1-d Dynamic Programming[Accepted]:

Algorithm

This approach isthe same as the previous approach except that we have used only 1-d dpdp array tostore the results of the prefixes of the strings processed so far. Instead ofmaintaining a 2-d array, we can maintain a 1-d array only and update thearray's element dp[i]dp[i] whenneeded using dp[i-1]dp[i−1] and theprevious value of dp[i]dp[i].

Java

public class Solution {

    publicbooleanisInterleave(String s1, String s2, Strings3) {

       if (s3.length() != s1.length() + s2.length()) {

           return false;

       }

       boolean dp[] = new boolean[s2.length() + 1];

       for (int i = 0; i <= s1.length(); i++) {

           for (int j = 0; j <= s2.length(); j++) {

                if (i == 0 && j == 0) {

                    dp[j] =true;

                } else if (i== 0) {

                    dp[j] =dp[j - 1] && s2.charAt(j - 1) == s3.charAt(i + j - 1);

                } else if (j== 0) {

                    dp[j] =dp[j] && s1.charAt(i - 1) == s3.charAt(i + j - 1);

                } else {

                    dp[j] =(dp[j] && s1.charAt(i - 1) == s3.charAt(i + j - 1)) || (dp[j - 1] && s2.charAt(j - 1) == s3.charAt(i + j - 1));

                }

           }

       }

       return dp[s2.length()];

    }

}

ComplexityAnalysis

  • Time complexity : O(m*n)O(mn). dp array of size nn is filled mm times.
  • Space complexity : O(n)O(n). nn is the length of the string s1s1.

 

3.  31.Next Permutation

Implement next permutation,which rearranges numbers into the lexicographically next greater permutation ofnumbers.

If sucharrangement is not possible, it must rearrange it as the lowest possible order(ie, sorted in ascending order).

The replacementmust be in-place, do not allocate extra memory.

Here are someexamples. Inputs are in the left-hand column and its corresponding outputs arein the right-hand column.
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1

 

Quick Navigation

Summary

We need to findthe next lexicographic permutation of the given list of numbers than the numberformed by the given array.

Solution


Approach #1 Brute Force [Time Limit Exceeded]

Algorithm

In thisapproach, we find out every possible permutation of list formed by the elementsof the given array and find out the permutation which is just larger than thegiven one. But this one will be a very naive approach, since it requires us tofind out every possible permutation which will take really long time and theimplementation is complex. Thus, this approach is not acceptable at all. Hence,we move on directly to the correct approach.

ComplexityAnalysis

  • Time complexity : O(n!)O(n!). Total possible permutations is n!n!.
  • Space complexity : O(n)O(n). Since an array will be used to store the permutations.

Approach #2 Single Pass Approach [Accepted]

Algorithm

First, weobserve that for any given sequence that is in descending order, no next largerpermutation is possible. For example, no next permutation is possible for thefollowing array: [9, 5, 4, 3, 1]

We need to findthe first pair of two successive numbers a[i]a[i] and a[i-1]a[i−1], from theright, which satisfy a[i] > a[i-1]a[i]>a[i−1]. Now, norearrangements to the right of a[i-1]a[i−1] can createa larger permutation since that subarray consists of numbers in descendingorder. Thus, we need to rearrange the numbers to the right of a[i-1]a[i−1] includingitself.

Now, what kindof rearrangement will produce the next larger number? We want to create thepermutation just larger than the current one. Therefore, we need to replace thenumber a[i-1]a[i−1] with thenumber which is just larger than itself among the numbers lying to its rightsection, say a[j]a[j].

We swap thenumbers a[i-1]a[i−1] and a[j]a[j]. We now havethe correct number at index i-1i−1. But still thecurrent permutation isn't the permutation that we are looking for. We need thesmallest permutation that can be formed by using the numbers only to the rightof a[i-1]a[i−1]. Therefore, weneed to place those numbers in ascending order to get their smallestpermutation.

But, recall thatwhile scanning the numbers from the right, we simply kept decrementing theindex until we found the pair a[i]a[i] and a[i-1]a[i−1] where, a[i] >a[i-1]a[i]>a[i−1]. Thus, allnumbers to the right of a[i-1]a[i−1] werealready sorted in descending order. Furthermore, swapping a[i-1]a[i−1] and a[j]a[j] didn'tchange that order. Therefore, we simply need to reverse the numbersfollowing a[i-1]a[i−1] to get thenext smallest lexicographic permutation.

The followinganimation will make things clearer:

Java

public class Solution {

    publicvoidnextPermutation(int[] nums) {

       int i = nums.length - 2;

       while (i >= 0 && nums[i + 1] <= nums[i]) {

           i--;

       }

       if (i >= 0) {

           int j = nums.length - 1;

           while (j >= 0 && nums[j] <=nums[i]) {

                j--;

           }

           swap(nums, i, j);

       }

       reverse(nums, i + 1);

    }

 

    privatevoidreverse(int[] nums, int start) {

       int i = start, j =nums.length - 1;

       while (i < j) {

           swap(nums, i, j);

           i++;

           j--;

       }

    }

 

    privatevoidswap(int[] nums, int i, int j) {

       int temp = nums[i];

       nums[i] = nums[j];

       nums[j] = temp;

    }

}

ComplexityAnalysis

  • Time complexity : O(n)O(n). In worst case, only two scans of the whole array are needed.
  • Space complexity : O(1)O(1). No extra space is used. In place replacements are done.

 

4.  32.Longest Valid Parentheses

Quick Navigation

Summary

We need todetermine the length of the largest valid substring of parentheses from a givenstring.

Solution


Approach #1 Brute Force [Time LimitExceeded]

Algorithm

In thisapproach, we consider every possible non-empty even length substring from thegiven string and check whether it's a valid string of parentheses or not. Inorder to check the validity, we use the Stack's Method.

Every time weencounter a \text{‘(’}‘(’, we push itonto the stack. For every \text{‘)’}‘)’ encountered, we pop a \text{‘(’}‘(’ from the stack. If \text{‘(’}‘(’ isn'tavailable on the stack for popping at anytime or if stack contains someelements after processing complete substring, the substring of parentheses isinvalid. In this way, we repeat the process for every possible substring and wekeep on storing the length of the longest valid string found so far.

Example:

"((())"

 

(( --> invalid

(( --> invalid

() --> valid, length=2

)) --> invalid

((()--> invalid

(())--> valid, length=4

maxlength=4

Java

public class Solution {

    publicbooleanisValid(String s) {

       Stack<Character> stack = new Stack<Character>();

       for (int i = 0; i < s.length(); i++) {

           if (s.charAt(i) == '(') {

                stack.push('(');

           } else if (!stack.empty() && stack.peek() == '(') {

                stack.pop();

           } else {

                return false;

           }

       }

       return stack.empty();

    }

    publicintlongestValidParentheses(String s) {

       int maxlen = 0;

       for (int i = 0; i < s.length(); i++) {

           for (int j = i +2; j <= s.length(); j+=2) {

                if (isValid(s.substring(i, j))) {

                    maxlen = Math.max(maxlen, j - i);

                }

           }

       }

       return maxlen;

    }

}

ComplexityAnalysis

  • Time complexity : O(n^3)O(n​3​​). Generating every possible substring from a string of length nn requires O(n^2)O(n​2​​). Checking validity of a string of length nn requires O(n)O(n).
  • Space complexity : O(n)O(n). A stack of depth nn will be required for the longest substring.

Approach #2 Using Dynamic Programming[Accepted]

Algorithm

This problem canbe solved by using Dynamic Programming. We make use of a \text{dp}dp arraywhere iith elementof \text{dp}dp representsthe length of the longest valid substring ending at iith index. Weinitialize the complete \text{dp}dp array with 0's. Now, it's obvious that the validsubstrings must end with \text{‘)’}‘)’. This further leads to the conclusion that thesubstrings ending with \text{‘(’}‘(’ will always contain '0' at theircorresponding \text{dp}dp indices.Thus, we update the \text{dp}dp array only when \text{‘)’}‘)’ is encountered.

To fill \text{dp}dp array wewill check every two consecutive characters of the string and if

  1. \text{s}[i] = \text{‘)’}s[i]=‘)’ and \text{s}[i - 1] = \text{‘(’}s[i−1]=‘(’, i.e. string looks like ``.......()" \Rightarrow‘‘.......()"⇒

\text{dp}[i]=\text{dp}[i-2]+2dp[i]=dp[i−2]+2

We do so because the ending"()" portion is a valid substring anyhow and leads to an increment of2 in the length of the just previous valid substring's length.

  1. \text{s}[i] = \text{‘)’}s[i]=‘)’ and \text{s}[i - 1] = \text{‘)’}s[i−1]=‘)’, i.e. string looks like ``.......))" \Rightarrow‘‘.......))"⇒

if \text{s}[i - \text{dp}[i - 1] - 1] =\text{‘(’}s[i−dp[i−1]−1]=‘(’ then

\text{dp}[i]=\text{dp}[i-1]+\text{dp}[i-\text{dp}[i-1]-2]+2dp[i]=dp[i−1]+dp[i−dp[i−1]−2]+2

The reasonbehind this is that if the 2nd last \text{‘)’}‘)’ was a part of a valid substring(say sub_ssubs), for thelast \text{‘)’}‘)’ to be apart of a larger substring, there must be a corresponding starting \text{‘(’}‘(’ which liesbefore the valid substring of which the 2nd last \text{‘)’}‘)’ is a part (i.e. before sub_ssubs). Thus, if thecharacter before sub_ssubs happens tobe \text{‘(’}‘(’, we updatethe \text{dp}[i]dp[i] as anaddition of 22 in thelength of sub_ssubs whichis \text{dp}[i-1]dp[i−1]. To this, wealso add the length of the valid substring just before the term"(,sub_s,)" , i.e. \text{dp}[i-\text{dp}[i-1]-2]dp[i−dp[i−1]−2].

For betterunderstanding of this method, see this example:

1 / 8

Java

public class Solution {

    publicintlongestValidParentheses(String s) {

       int maxans = 0;

       int dp[] = new int[s.length()];

       for (int i = 1; i < s.length(); i++) {

           if (s.charAt(i) == ')') {

                if (s.charAt(i - 1) == '(') {

                    dp[i] =(i >= 2 ? dp[i- 2] : 0) + 2;

                } else if (i- dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') {

                    dp[i] =dp[i - 1] + ((i - dp[i- 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;

                }

                maxans = Math.max(maxans, dp[i]);

           }

       }

       return maxans;

    }

}

ComplexityAnalysis

  • Time complexity : O(n)O(n). Single traversal of string to fill dp array is done.
  • Space complexity : O(n)O(n). dp array of size nn is used.

Approach #3 Using Stack [Accepted]

Algorithm

Instead offinding every possible string and checking its validity, we can make use ofstack while scanning the given string to check if the string scanned so far isvalid, and also the length of the longest valid string. In order to do so, westart by pushing -1−1 onto the stack.

For every \text{‘(’}‘(’ encountered,we push its index onto the stack.

For every \text{‘)’}‘)’ encountered,we pop the topmost element and subtract the current element's index from thetop element of the stack, which gives the length of the currently encounteredvalid string of parentheses. If while popping the element, the stack becomesempty, we push the current element's index onto the stack. In this way, we keepon calculating the lengths of the valid substrings, and return the length ofthe longest valid string at the end.

See this examplefor better understanding.

1 / 11

Java

public class Solution {

 

    publicintlongestValidParentheses(String s) {

       int maxans = 0;

       Stack<Integer> stack = new Stack<>();

       stack.push(-1);

       for (int i = 0; i < s.length(); i++) {

           if (s.charAt(i) == '(') {

                stack.push(i);

           } else {

                stack.pop();

                if (stack.empty()) {

                    stack.push(i);

                } else {

                    maxans = Math.max(maxans, i - stack.peek());

                }

           }

       }

       return maxans;

    }

}

ComplexityAnalysis

  • Time complexity : O(n)O(n). nn is the length of the given string..
  • Space complexity : O(n)O(n). The size of stack can go up to nn.

Approach #4 Without extra space [Accepted]

Algorithm

In thisapproach, we make use of two counters leftleft and rightright. First, westart traversing the string from the left towards the right and for every \text{‘(’}‘(’ encountered,we increment the leftleft counterand for every \text{‘)’}‘)’ encountered,we increment the rightright counter.Whenever leftleft becomesequal to rightright, we calculatethe length of the current valid string and keep track of maximum lengthsubstring found so far. If rightright becomesgreater than leftleft wereset leftleft and rightright to 00.

Next, we starttraversing the string from right to left and similar procedure is applied.

Example of thisapproach:

1 / 21

Java

public class Solution {

    publicintlongestValidParentheses(String s) {

       int left = 0, right = 0, maxlength = 0;

       for (int i = 0; i < s.length(); i++) {

           if (s.charAt(i) == '(') {

                left++;

           } else {

                right++;

           }

           if (left == right) {

                maxlength = Math.max(maxlength, 2 * right);

           } else if (right >= left) {

                left = right = 0;

           }

       }

       left = right = 0;

       for (int i = s.length() - 1; i >= 0; i--) {

           if (s.charAt(i) == '(') {

                left++;

           } else {

                right++;

           }

           if (left == right) {

                maxlength = Math.max(maxlength, 2 * left);

           } else if (left >= right) {

                left = right = 0;

           }

       }

       return maxlength;

    }

}

ComplexityAnalysis

  • Time complexity : O(n)O(n). Two traversals of the string.
  • Space complexity : O(1)O(1). Only two extra variables leftleft and rightright are needed.

5.  42.Trapping Rain Water

Given n non-negativeintegers representing an elevation map where the width of each bar is 1,compute how much water it is able to trap after raining.

Forexample, 
Given 
[0,1,0,2,1,0,1,3,2,1,2,1], return 6.

The above elevation map is representedby array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (bluesection) are being trapped. Thanks Marcos for contributingthis image!

Quick Navigation

Solution


Approach #1 Brute force [Accepted]

Intuition

Do as directedin question. For each element in the array, we find the maximum level of water itcan trap after the rain, which is equal to the minimum of maximum height ofbars on both the sides minus its own height.

Algorithm

  • Initialize ans=0ans=0
  • Iterate the array from left to right:
  • Initialize max_left=0max_left=0 and max_right=0max_right=0
  • Iterate from the current element to the beginning of array updating: max_left=max(max_left,height[j])max_left=max(max_left,height[j])
  • Iterate from the current element to the end of array updating: max_right=max(max_right,height[j])max_right=max(max_right,height[j])
  • Add min(max_left,max_right)−height[i]min(max_left,max_right)−height[i] to \text{ans}ans

C++

inttrap(vector<int>& height)

{

    int ans = 0;

    int size = height.size();

    for(int i = 1; i < size - 1; i++) {

       int max_left = 0, max_right = 0;

       for (int j = i; j>= 0; j--) { //Search the left part for max bar size

           max_left = max(max_left, height[j]);

       }

       for (int j = i; j< size; j++) { //Search the right part for max bar size

           max_right = max(max_right, height[j]);

       }

       ans += min(max_left, max_right) - height[i];

    }

    returnans;

}

ComplexityAnalysis

  • Time complexity: O(n^2)O(n​2​​). For each element of array, we iterate the left and right parts.
  • Space complexity: O(1)O(1) extra space.

Approach #2 Dynamic Programming [Accepted]

Intuition

In brute force,we iterate over the left and right parts again and again just to find thehighest bar size upto that index. But, this could be stored. Voila, dynamicprogramming.

The concept isillustrated as shown:

Algorithm

  • Find maximum height of bar from the left end upto an index i in the array left_maxleft_max.
  • Find maximum height of bar from the right end upto an index i in the array right_maxright_max.
  • Iterate over the \text{height}height array and update ans:
  • Add min(max_left[i],max_right[i])−height[i]min(max_left[i],max_right[i])−height[i] to ansans

C++

inttrap(vector<int>& height)

{

    if(height== null)

       return 0;

    int ans = 0;

    int size = height.size();

   vector<int> left_max(size), right_max(size);

   left_max[0] = height[0];

    for(int i = 1; i < size; i++) {

       left_max[i] = max(height[i], left_max[i - 1]);

    }

   right_max[size - 1] =height[size - 1];

    for(int i = size - 2; i >= 0; i--) {

       right_max[i] = max(height[i], right_max[i + 1]);

    }

    for(int i = 1; i < size - 1; i++) {

       ans += min(left_max[i], right_max[i]) - height[i];

    }

    returnans;

}

Complexityanalysis

  • Time complexity: O(n)O(n).
  • We store the maximum heights upto a point using 2 iterations of O(n) each.
  • We finally update \text{ans}ans using the stored values in O(n).
  • Space complexity: O(n)O(n) extra space.
  • Additional O(n)O(n) space for left_maxleft_max and right_maxright_max arrays than in Approach #1.

Approach #3 Using stacks [Accepted]

Intuition

Instead ofstoring the largest bar upto an index as in Approach #2, we can use stack tokeep track of the bars that are bounded by longer bars and hence, may storewater. Using the stack, we can do the calculations in only one iteration.

We keep a stackand iterate over the array. We add the index of the bar to the stack if bar issmaller than or equal to the bar at top of stack, which means that the currentbar is bounded by the previous bar in the stack. If we found a bar longer thanthat at the top, we are sure that the bar at the top of the stack is bounded bythe current bar and a previous bar in the stack, hence, we can pop it and addresulting trapped water to \text{ans}ans.

Algorithm

  • Use stack to store the indices of the bars.
  • Iterate the array:
    • While stack is not empty and \text{height}[current]>\text{height}[st.top()]height[current]>height[st.top()]
      • It means that the stack element can be popped. Pop the top element as \text{top}top.
      • Find the distance between the current element and the element at top of stack, which is to be filled. \text{distance} = \text{current} - \text{st.top}() - 1distance=current−st.top()−1
      • Find the bounded height bounded_height=min(height[current],height[st.top()])−height[top]bounded_height=min(height[current],height[st.top()])−height[top]
      • Add resulting trapped water to answer ans+=distance∗bounded_heightans+=distance∗bounded_height
    • Push current index to top of the stack
    • Move \text{current}current to the next position

C++

inttrap(vector<int>& height)

{

    int ans = 0, current = 0;

   stack<int> st;

    while(current < height.size()) {

       while (!st.empty() && height[current] >height[st.top()]) {

           int top = st.top();

           st.pop();

           if (st.empty())

                break;

           int distance = current -st.top() - 1;

           int bounded_height =min(height[current], height[st.top()]) - height[top];

           ans += distance * bounded_height;

       }

       st.push(current++);

    }

    returnans;

}

Complexityanalysis

  • Time complexity: O(n)O(n).
    • Single iteration of O(n)O(n) in which each bar can be touched at most twice(due to insertion and deletion from stack) and insertion and deletion from stack takes O(1)O(1) time.
  • Space complexity: O(n)O(n). Stack can take upto O(n)O(n) space in case of stairs-like or flat structure.

Approach #4 Using 2 pointers [Accepted]

Intuition As in Approach #2, instead ofcomputing the left and right parts seperately, we may think of some way to doit in one iteration. From the figure in dynamic programming approach, noticethat as long as right_max[i]>left_max[i]right_max[i]>left_max[i](from element 0to 6), the water trapped depends upon the left_max, and similar is the casewhen left_max[i]>right_max[i]left_max[i]>right_max[i](from element 8to 11). So, we can say that if there is a larger bar at one end(say right), weare assured that the water trapped would be dependant on height of bar incurrent direction(from left to right). As soon as we find the bar at otherend(right) is smaller, we start iterating in opposite direction(from right toleft). We must maintain left_maxleft_max and right_maxright_max during theiteration, but now we can do it in one iteration using 2 pointers, switchingbetween the two.

Algorithm

  • Initialize \text{left}left pointer to 0 and \text{right}right pointer to size-1
  • While \text{left}< \text{right}left<right, do:
    • If \text{height[left]}height[left] is smaller than \text{height[right]}height[right]
      • If height[left]>=left_maxheight[left]>=left_max, update left_maxleft_max
      • Else add left_max−height[left]left_max−height[left] to \text{ans}ans
      • Add 1 to \text{left}left.
    • Else
      • If height[right]>=right_maxheight[right]>=right_max, update right_maxright_max
      • Else add right_max−height[right]right_max−height[right] to \text{ans}ans
      • Subtract 1 from \text{right}right.

1 / 11

C++

inttrap(vector<int>& height)

{

    int left = 0, right = height.size() - 1;

    int ans = 0;

    int left_max = 0, right_max = 0;

    while(left < right) {

       if (height[left] < height[right]) {

           height[left] >= left_max ? (left_max =height[left]) : ans += (left_max - height[left]);

           ++left;

       }

       else {

           height[right] >= right_max ? (right_max =height[right]) : ans += (right_max - height[right]);

           --right;

       }

    }

    returnans;

}

Complexityanalysis

  • Time complexity: O(n)O(n). Single iteration of O(n)O(n).
  • Space complexity: O(1)O(1) extra space. Only constant space required for \text{left}left, \text{right}right, left_maxleft_max and right_maxright_max.

6.  135.Candy

Quick Navigation

Solution


Approach #1 Brute Force [Time LimitExceeded]

The simplestapproach makes use of a 1-d array, candiescandies to keep a trackof the candies given to the students. Firstly, we give 1 candy to each student.Then, we start scanning the array from left-to-right. At every elementencountered, firstly, if the current element's ratings, ratings[i]ratings[i], is larger thanthe previous element(ratings[i-1]ratings[i−1]) and candies[i]<=candies[i-1]candies[i]<=candies[i−1], then weupdate candies[i]candies[i] as candies[i]=candies[i-1]+ 1candies[i]=candies[i−1]+1.Thus, now thecandy distribution for these two elements candies[i-1]candies[i−1] and candies[i]candies[i] becomescorrect for the time being(locally). In the same step, we also check if thecurrent element's ratings, ratings[i]ratings[i], is larger thanthe next element's ratings, i.e. ratings[i]>ratings[i+1]ratings[i]>ratings[i+1]. If so, weagain update candies[i]=candies[i+1]+ 1candies[i]=candies[i+1]+1. We continuethis process for the whole ratingsratings array. Ifin any traversal, no updation of the candiescandies arrayoccurs, it means we've reached at the final required distribution of thecandies and we can stop the traversals. To keep a track of this we make use ofa flagflag which isset to \text{True}True if anyupdation occurs in a traversal.

At the end, wecan sum up all the elements of the candiescandies array toobtain the required minimum number of candies.

Java

public class Solution {

    publicintcandy(int[] ratings) {

       int[] candies = new int[ratings.length];

       Arrays.fill(candies, 1);

       boolean flag = true;

       int sum = 0;

       while (flag) {

           flag = false;

           for (int i = 0; i < ratings.length; i++) {

                if (i !=ratings.length - 1 && ratings[i] >ratings[i + 1] && candies[i]<= candies[i + 1]) {

                    candies[i] =candies[i + 1] + 1;

                    flag = true;

                }

                if (i >0 && ratings[i]> ratings[i - 1] &&candies[i] <= candies[i - 1]) {

                    candies[i] =candies[i - 1] + 1;

                    flag = true;

                }

           }

       }

       for (int candy :candies) {

           sum += candy;

       }

       return sum;

    }

}

ComplexityAnalysis

  • Time complexity : O(n^2)O(n​2​​). We need to traverse the array at most nn times.
  • Space complexity : O(n)O(n). One candiescandies array of size nn is used.

Approach #2 Using two arrays [Accepted]

Algorithm

In thisapproach, we make use of two 1-d arrays left2rightleft2right and right2leftright2left. The left2rightleft2right array isused to store the number of candies required by the current student taking careof the distribution relative to the left neighbours only. i.e. Assuming thedistribution rule is: The student with a higher ratings than its left neighbourshould always get more candies than its left neighbour. Similarly, the right2leftright2left array isused to store the number of candies candies required by the current studenttaking care of the distribution relative to the right neighbours only. i.e.Assuming the distribution rule to be: The student with a higher ratings thanits right neighbour should always get more candies than its right neighbour. Todo so, firstly we assign 1 candy to each student in both left2rightleft2right and right2leftright2left array. Then,we traverse the array from left-to-right and whenever the current element'sratings is larger than the left neighbour we update the current element'scandies in the left2rightleft2right arrayas left2right[i]= left2right[i-1] + 1left2right[i]=left2right[i−1]+1, since thecurrent element's candies are always less than or equal candies than its leftneighbour before updation. After the forward traversal, we traverse the arrayfrom left-to-right and update right2left[i]right2left[i] as right2left[i]= right2left[i + 1] + 1right2left[i]=right2left[i+1]+1, whenever thecurrent(i^{th}ith) element has ahigher ratings than the right((i+1)^{th}(i+1)th) element.

Now, forthe i^{th}ith student inthe array, we need to give \text{max}(left2right[i], right2left[i])max(left2right[i],right2left[i]) to it, inorder to satisfy both the left and the right neighbour relationship. Thus, atthe end, we obtain the minimum number of candies required as:

minimum_candies=∑i=0n−1max(left2right[i],right2left[i]),where n=length of the ratings array.minimum_candies=∑i=0n−1max(left2right[i],right2left[i]),where n=lengthof the ratings array.

The followinganimation illustrates the method:

Java

public class Solution {

    publicintcandy(int[] ratings) {

       int sum = 0;

       int[] left2right = new int[ratings.length];

       int[] right2left = new int[ratings.length];

       Arrays.fill(left2right, 1);

       Arrays.fill(right2left, 1);

       for (int i = 1; i < ratings.length; i++) {

           if (ratings[i] > ratings[i -1]) {

                left2right[i] =left2right[i - 1] + 1;

           }

       }

       for (int i =ratings.length - 2; i >= 0; i--) {

           if (ratings[i] > ratings[i +1]) {

                right2left[i] =right2left[i + 1] + 1;

           }

       }

       for (int i = 0; i < ratings.length; i++) {

           sum += Math.max(left2right[i], right2left[i]);

       }

       return sum;

    }

}

ComplexityAnalysis

  • Time complexity : O(n)O(n). left2rightleft2right and right2leftright2left arrays are traversed thrice.
  • Space complexity : O(n)O(n). Two arrays left2rightleft2right and right2leftright2left of size nn are used.

Approach #3 Using one array [Accepted]

Algorithm

In the previous approach,we used two arrays to keep track of the left neighbour and the right neighbourrelation individually and later on combined these two. Instead of this, we canmake use of a single array candiescandies to keepthe count of the number of candies to be allocated to the current student. Inorder to do so, firstly we assign 1 candy to each student. Then, we traversethe array from left-to-right and distribute the candies following only the leftneighbour relation i.e. whenever the current element's ratings is larger thanthe left neighbour and has less than or equal candies than its left neighbour,we update the current element's candies in the candiescandies arrayas candies[i]= candies[i-1] + 1candies[i]=candies[i−1]+1. While updatingwe need not compare candies[i]candies[i] and candies[i- 1]candies[i−1], since candies[i]\leq candies[i - 1]candies[i]≤candies[i−1] beforeupdation. After this, we traverse the array from right-to-left. Now, we need toupdate the i^{th}ith element'scandies in order to satisfy both the left neighbour and the right neighbourrelation. Now, during the backward traversal, if ratings[i]>ratings[i + 1]ratings[i]>ratings[i+1], consideringonly the right neighbour criteria, we could've updated candies[i]candies[i] as candies[i]= candies[i + 1] + 1candies[i]=candies[i+1]+1. But, this timewe need to update the candies[i]candies[i] onlyif candies[i]\leq candies[i + 1]candies[i]≤candies[i+1]. This happensbecause, this time we've already altered the candiescandies array duringthe forward traversal and thus candies[i]candies[i] isn'tnecessarily less than or equal to candies[i + 1]candies[i+1]. Thus, if ratings[i]> ratings[i + 1]ratings[i]>ratings[i+1], we canupdate candies[i]candies[i] as candies[i]= \text{max}(candies[i], candies[i + 1] + 1)candies[i]=max(candies[i],candies[i+1]+1), whichmakes candies[i]candies[i] satisfyboth the left neighbour and the right neighbour criteria.

Again, we needsum up all the elements of the candiescandies array toobtain the required result.

minimum_candies=∑i=0n−1candies[i],where n=length of the ratings array.minimum_candies=∑i=0n−1candies[i],where n=lengthof the ratings array.

java

public class Solution {

    publicintcandy(int[] ratings) {

       int[] candies = new int[ratings.length];

       Arrays.fill(candies, 1);

       for (int i = 1; i < ratings.length; i++) {

           if (ratings[i] > ratings[i -1]) {

                candies[i] =candies[i - 1] + 1;

           }

       }

       int sum = candies[ratings.length - 1];

       for (int i =ratings.length - 2; i >= 0; i--) {

           if (ratings[i] > ratings[i +1]) {

                candies[i] =Math.max(candies[i], candies[i +1] + 1);

           }

           sum += candies[i];

       }

       return sum;

    }

}

ComplexityAnalysis

  • Time complexity : O(n)O(n). The array candiescandies of size nn is traversed thrice.
  • Space complexity : O(n)O(n). An array candiescandies of size nn is used.

Approach #4 Single Pass Approach withConstant Space [Accepted]

Algorithm

This approachrelies on the observation(as demonstrated in the figure below as well) that inorder to distribute the candies as per the given criteria using the minimumnumber of candies, the candies are always distributed in terms of increments of1. Further, while distributing the candies, the local minimum number of candiesgiven to a student is 1. Thus, the sub-distributions always take theform: \text{1,2, 3, ..., n}1, 2, 3, ..., n or \text{n,...,2, 1}n,..., 2, 1, whose sum issimply given by n(n+1)/2n(n+1)/2.

Now, we can viewthe given rankingsrankings as somerising and falling slopes. Whenever the slope is rising, the distribution takesthe form: \text{1,2, 3, ..., m}1, 2, 3, ..., m. Similarly, afalling slope takes the form: \text{k,..., 2, 1}k,..., 2, 1. An issue thatarises now is that the local peak point can be included in only one of theslopes. Whether to include the local peak point(\text{n}n) in the rising slope or the fallingslope?

In order todecide it, we can observe that in order to satisfy both the right neighbour andthe left neighbour criteria, the peak point's count needs to be the max. of thecounts determined by the rising and the falling slopes. Thus, in order todetermine the number of candies required, the peak point needs to be includedin the slope which contains more number of points. The local valley point canalso be included in only one of the slopes, but this issue can be resolvedeasily, since the local valley point will always be assigned a candy count of 1(whichcan be subtracted from the next slope's count calculations).

Coming to theimplementation, we maintain two variables old\_slopeold_slope and new\_slopenew_slope todetermine the occurence of a peak or a valley. We also use upup and downdown variables tokeep a track of the count of elements on the rising slope and on the fallingslope respectively(without including the peak element). We always update thetotal count of candiescandies at the endof a falling slope following a rising slope (or a mountain). The leveling ofthe points in rankingsrankings also worksas the end of a mountain. At the end of the mountain, we determine whether toinclude the peak point in the rising slope or in the falling slope by comparingthe upup and downdown variablesup to that point. Thus, the count assigned to the peak element becomes: \text{max}(up,down) + 1max(up,down)+1. At this point,we can reset the upup and downdownvariablesindicating the start of a new mountain.

The followingfigure shows the cases that need to be handled for this example:

rankings: [1 2 3 4 53 2 1 2 6 5 4 3 3 2 1 1 3 3 3 4 2]

From thisfigure, we can see that the candy distributions in the subregions always takethe form \text{1,2, ...n}1, 2, ...n or \text{n,..., 2, 1}n, ..., 2, 1. For the firstmountain comprised by the regions aa and bb, whileassigning candies to the local peak point(pt. 5pt.5), it needs tobe included in aa to satisfythe left neighbour criteria. The local valley point at the end of region bb(pt. 8pt.8) marks the endof the first mountain(region cc). Whileperforming the calculations, we can include this point in either the current orthe following mountain. The pt. 13pt.13 marks theend of the second mountain due to levelling of the pt. 13pt.13 and pt. 14pt.14. Since, region ee has morepoints than region dd, the localpeak(pt. 10pt.10) needs to beincluded in region ee to satisfythe right neighbour criteria. Now, the third mountain ff can beconsidered as a mountian with no rising slope(up=0up=0) but only afalling slope. Similarly, pt. 16, 18, 19pt.16,18,19 also actas the mountain ends due to the levelling of the points.

java

public class Solution {

    publicintcount(int n) {

       return (n * (n + 1)) / 2;

    }

    publicintcandy(int[] ratings) {

       if (ratings.length <= 1) {

           return ratings.length;

       }

       int candies = 0;

       int up = 0;

       int down = 0;

       int old_slope = 0;

       for (int i = 1; i < ratings.length; i++) {

           int new_slope = (ratings[i]> ratings[i - 1]) ? 1 : (ratings[i] <ratings[i - 1] ? -1 : 0);

           if ((old_slope > 0 && new_slope == 0) || (old_slope < 0 && new_slope >= 0)) {

                candies += count(up)+ count(down) + Math.max(up, down);

                up = 0;

                down = 0;

           }

           if (new_slope > 0)

                up++;

           if (new_slope < 0)

                down++;

           if (new_slope == 0)

                candies++;

 

           old_slope = new_slope;

       }

       candies += count(up) + count(down)+ Math.max(up, down) + 1;

       return candies;

    }

};

ComplexityAnalysis

  • Time complexity : O(n)O(n). We traverse the rankingsrankings array once only.
  • Space complexity : O(1)O(1). Constant Extra Space is used.

7.  162.Find Peak Element

A peak elementis an element that is greater than its neighbors.

Given an inputarray where num[i] ≠ num[i+1], find a peak element and return itsindex.

The array maycontain multiple peaks, in that case return the index to any one of the peaksis fine.

You may imaginethat num[-1] = num[n] = -∞.

For example, inarray [1, 2, 3, 1], 3 is a peak element and your function should return theindex number 2.

clickto show spoilers.

 

Quick Navigation

Solution


Approach #1 Linear Scan [Accepted]

In thisapproach, we make use of the fact that two consecutive numbers nums[j]nums[j] and nums[j +1]nums[j+1] are neverequal. Thus, we can traverse over the numsnumsarray startingfrom the beginning. Whenever, we find a number nums[i]nums[i], we only needto check if it is larger than the next number nums[i+1]nums[i+1] fordetermining if nums[i]nums[i] is thepeak element. The reasoning behind this can be understood by taking thefollowing three cases which cover every case into which any problem can bedivided.

Case 1: All thenumbers appear in a descending order. In this case, the first elementcorresponds to the peak element. We start off by checking if the currentelement is larger than the next one. The first element satisfies this criteria,and is hence identified as the peak correctly. In this case, we didn't reach apoint where we needed to compare nums[i]nums[i] with nums[i-1]nums[i−1] also, todetermine if it is the peak element or not.

Case 2: All theelements appear in ascending order. In this case, we keep on comparing nums[i]nums[i] with nums[i+1]nums[i+1] todetermine if nums[i]nums[i] is thepeak element or not. None of the elements satisfy this criteria, indicatingthat we are currently on a rising slope and not on a peak. Thus, at the end, weneed to return the last element as the peak element, which turns out to becorrect. In this case also, we need not compare nums[i]nums[i] with nums[i-1]nums[i−1], since being onthe rising slope is a sufficient condition to ensure that nums[i]nums[i] isn't thepeak element.

Case 3: The peakappears somewhere in the middle. In this case, when we are traversing on therising edge, as in Case 2, none of the elements will satisfy nums[i]> nums[i + 1]nums[i]>nums[i+1]. We need notcompare nums[i]nums[i] with nums[i-1]nums[i−1] on therising slope as discussed above. When we finally reach the peak element, thecondition nums[i]> nums[i + 1]nums[i]>nums[i+1] issatisfied. We again, need not compare nums[i]nums[i] with nums[i-1]nums[i−1]. This isbecause, we could reach nums[i]nums[i] as the currentelement only when the check nums[i] > nums[i + 1]nums[i]>nums[i+1] failed forthe previous((i-1)^{th}(i−1)th element,indicating that nums[i-1] < nums[i]nums[i−1]<nums[i]. Thus, we areable to identify the peak element correctly in this case as well.

Java

public class Solution {

    publicintfindPeakElement(int[] nums) {

       for (int i = 0; i < nums.length - 1; i++) {

           if (nums[i] > nums[i +1])

                return i;

       }

       return nums.length - 1;

    }

}

ComplexityAnalysis

  • Time complexity : O(n)O(n). We traverse the numsnums array of size nn once only.
  • Space complexity : O(1)O(1). Constant extra space is used.

Approach #2 Recursive Binary Search[Accepted]

Algorithm

We can view anygiven sequence in numsnums array asalternating ascending and descending sequences. By making use of this, and thefact that we can return any peak as the result, we can make use of BinarySearch to find the required peak element.

In case ofsimple Binary Search, we work on a sorted sequence of numbers and try to findout the required number by reducing the search space at every step. In thiscase, we use a modification of this simple Binary Search to our advantage. Westart off by finding the middle element, midmid from thegiven numsnums array. Ifthis element happens to be lying in a descending sequence of numbers. or alocal falling slope(found by comparing nums[i]nums[i] to itsright neighbour), it means that the peak will always lie towards the left ofthis element. Thus, we reduce the search space to the left of midmid(includingitself) and perform the same process on left subarray.

If the middleelement, midmid lies in anascending sequence of numbers, or a rising slope(found by comparing nums[i]nums[i] to itsright neighbour), it obviously implies that the peak lies towards the right ofthis element. Thus, we reduce the search space to the right of midmid andperform the same process on the right subarray.

In this way, wekeep on reducing the search space till we eventually reach a state where onlyone element is remaining in the search space. This single element is the peakelement.

To see how itworks, let's consider the three cases discussed above again.

Case 1. In thiscase, we firstly find 33 as the middle element. Since it lies on a fallingslope, we reduce the search space to [1, 2, 3]. For thissubarray, 22happens to bethe middle element, which again lies on a falling slope, reducing the searchspace to [1, 2]. Now, 11 acts asthe middle element and it lies on a falling slope, reducing the search spaceto [1] only.Thus, 11 isreturned as the peak correctly.

1 / 7

Case 2. In thiscase, we firstly find 33 as the middle element. Since it lies on a risingslope, we reduce the search space to [4, 5]. Now, 44 acts asthe middle element for this subarray and it lies on a rising slope, reducingthe search space to [5] only. Thus, 55 is returned as the peak correctly.

1 / 6

Case 3. In thiscase, the peak lies somewhere in the middle. The first middle element is 44. It lies on arising slope, indicating that the peak lies towards its right. Thus, the searchspace is reduced to [5, 1]. Now, 55 happens to be the on a fallingslope(relative to its right neighbour), reducing the search space to [5] only. Thus, 55 isidentified as the peak element correctly.

1 / 6

Java

public class Solution {

    publicintfindPeakElement(int[] nums) {

       return search(nums, 0, nums.length - 1);

    }

    publicintsearch(int[] nums, int l, int r) {

       if (l == r)

           return l;

       int mid = (l + r)/ 2;

       if (nums[mid] > nums[mid +1])

           return search(nums, l, mid);

       return search(nums, mid + 1, r);

    }

}

ComplexityAnalysis

  • Time complexity : O\big(log_2(n)\big)O(log​2​​(n)). We reduce the search space in half at every step. Thus, the total search space will be consumed in log_2(n)log​2​​(n) steps. Here, nnrefers to the size of numsnums array.
  • Space complexity : O\big(log_2(n)\big)O(log​2​​(n)). We reduce the search space in half at every step. Thus, the total search space will be consumed in log_2(n)log​2​​(n) steps. Thus, the depth of recursion tree will go upto log_2(n)log​2​​(n).

Approach #3 Iterative Binary Search[Accepted]

Algorithm

The binarysearch discussed in the previous approach used a recursive method. We can dothe same process in an iterative fashion also. This is done in the currentapproach.

Java

public class Solution {

    publicintfindPeakElement(int[] nums) {

       int l = 0, r = nums.length - 1;

       while (l < r) {

           int mid = (l + r)/ 2;

           if (nums[mid] > nums[mid +1])

                r = mid;

           else

                l = mid + 1;

       }

       return l;

    }

}

ComplexityAnalysis

  • Time complexity : O\big(log_2(n)\big)O(log​2​​(n)). We reduce the search space in half at every step. Thus, the total search space will be consumed in log_2(n)log​2​​(n) steps. Here, nnrefers to the size of numsnums array.
  • Space complexity : O(1)O(1). Constant extra space is used.

8.  164.Maximum Gap

Given anunsorted array, find the maximum difference between the successive elements inits sorted form.

Try to solve itin linear time/space.

Return 0 if thearray contains less than 2 elements.

You may assumeall elements in the array are non-negative integers and fit in the 32-bitsigned integer range.

Quick Navigation

Solution


Approach #1 Comparison Sorting [Accepted]

Intuition

Do what thequestion says.

Algorithm

Sort the entirearray. Then iterate over it to find the maximum gap between two successiveelements.

C++

intmaximumGap(vector<int>& nums)

{

    if(nums.empty() || nums.size() < 2)            // check if array is empty or smallsized

       return 0;

 

   sort(nums.begin(), nums.end());                 // sort the array

 

    int maxGap = 0;

 

    for(int i = 0; i < nums.size() - 1; i++)

       maxGap = max(nums[i + 1] - nums[i], maxGap);

 

    returnmaxGap;

}

ComplexityAnalysis

  • Time complexity: O(n \cdot log(n))O(nlog(n)).

Time taken to sort the array is O(n \cdotlog(n))O(nlog(n)) (averagecase). Time taken for linear iteration through the array is of O(n)O(n) complexity.Hence overall time complexity is O(n \cdot log(n))O(nlog(n)).

  • Space complexity: No extra space needed, other than the input array (since sorting can usually be done in-place).

Approach #2 Radix Sort [Accepted]

Algorithm

This approach issimilar to Approach #1, except weuse Radix Sort instead of a traditionalcomparison sort.

C++

intmaximumGap(vector<int>& nums)

{

    if(nums.empty() || nums.size() < 2)

       return 0;

 

    int maxVal = *max_element(nums.begin(),nums.end());

 

    int exp = 1;                                 // 1, 10, 100, 1000 ...

    int radix = 10;                              // base 10 system

 

   vector<int> aux(nums.size());

 

    /* LSD Radix Sort */

    while(maxVal / exp > 0) {                   // Go through all digits from LSD to MSD

       vector<int> count(radix, 0);

 

       for (int i = 0; i < nums.size(); i++)    // Counting sort

           count[(nums[i] / exp) % 10]++;

 

       for (int i = 1; i < count.size(); i++)   // you could also use partial_sum()

           count[i] += count[i - 1];

 

       for (int i =nums.size() - 1; i >=0; i--)

           aux[--count[(nums[i] / exp) % 10]] = nums[i];

 

       for (int i = 0; i < nums.size(); i++)

           nums[i] = aux[i];

 

       exp *= 10;

    }

 

    int maxGap = 0;

 

    for(int i = 0; i < nums.size() - 1; i++)

       maxGap = max(nums[i + 1] - nums[i], maxGap);

 

    returnmaxGap;

}

ComplexityAnalysis

  • Time complexity: O(d \cdot (n + k)) \approx O(n)O(d⋅(n+k))≈O(n).

Since a linear iteration over the array(once it is sorted) is of linear (i.e. O(n)O(n)) complexity,the performance of this approach is limited by the performance of Radix sort.

Radix sort uses Counting sort as asubroutine.

    • Counting sort runs in O(n + k)O(n+k) time (where kk is the radix or base of the digits comprising the nn elements in the array). If k \leq O(n)kO(n), Counting sort would run in linear time. In our case, the radix is fixed (i.e. k = 10k=10). Hence our Counting sort subroutine runs in O(n)O(n) linear time.
    • Radix sort works by running dd passes of the Counting sort subroutine (where the elements are composed of, maximally, dd digits). Hence effective runtime of Radix sort would be O(d \cdot (n + k))O(d⋅(n+k)). However, in our case an element can, maximally, be the maximum 32-bit signed integer2,147,483,647. Hence d \leq 10d≤10 is a constant.

Thus Radix sort has a runtimeperformance complexity of about O(n)O(n) forreasonably large input.

  • Space complexity: O(n + k) \approx O(n)O(n+k)≈O(n) extra space.

Counting sort requires O(k)O(k) extraspace. Radix sort requires an auxiliary array of the same size as input array.However given that kk is a smallfixed constant, the space required by Counting sort can be ignored forreasonably large input.


Approach #3 Buckets and The PigeonholePrinciple [Accepted]

Intuition

Sorting anentire array can be costly. At worst, it requires comparing each elementwith every other element. What if we didn't need to compareall pairs of elements? That would be possible if we could somehow divide the elementsinto representative groups, or rather, buckets. Then we would onlyneed to compare these buckets.

Digression: The Pigeonhole Principle The Pigeonhole Principle statesthat if nn items areput into mm containers,with n > mn>m, then at leastone container must contain more than one item.

Suppose for eachof the nn elementsin our array, there was a bucket. Then each element would occupy one bucket.Now what if we reduced, the number of buckets? Some buckets would have toaccommodate more than one element.

Now let's talkabout the gaps between the elements. Let's take the best case, where allelements of the array are sorted and have a uniform gap between them. Thismeans every adjacent pair of elements differ by the same constant value. Sofor nn elementsof the array, there are n-1n−1 gaps, eachof width, say, tt. It is trivialto deduce that t = (max -min)/(n-1)t=(maxmin)/(n−1) (where maxmax and minmin are theminimum and maximum elements of the array). This width is the maximal width/gapbetween two adjacent elements in the array; precisely the quantity we arelooking for!

One can safelyargue that this value of tt, is in fact,the smallest value that tt can everaccomplish of any array with the same number of elements (i.e. nn) and the samerange (i.e. (max -min)(maxmin)). To test thisfact, you can start with a uniform width array (as described above) and try toreduce the gap between any two adjacent elements. If you reduce the gapbetween arr[i-1]arr[i−1] and arr[i]arr[i] to somevalue t - ptp, then you willnotice that the gap between arr[i]arr[i] and arr[i+1]arr[i+1]would haveincreased to t + pt+p. Hence themaximum attainable gap would have become t + pt+p from tt. Thus the valueof the maximum gap tt can onlyincrease.

Buckets!

Coming back toour problem, we have already established by application of the PigeonholePrinciple, that if we used buckets instead of individualelements as our base for comparison, the number of comparisons would reduce ifwe could accommodate more than one element in a single bucket. That does notimmediately solve the problem though. What if we had to compare elements within abucket? We would end up no better.

So the currentmotivation remains: somehow, if we only had to compare among the buckets,and not the elements within the buckets, wewould be good. It would also solve our sorting problem: we would justdistribute the elements to the right buckets. Since the buckets can be already ordered,and we only compare among buckets, we wouldn't have to compare all elements tosort them!

But if we onlyhad buckets to compare, we would have to ensure, that the gapbetween the buckets itself represent the maximal gap in the input array. How dowe go about doing that?

We could do thatjust by setting the buckets to be smaller than t = (max - min)/(n-1)t=(maxmin)/(n−1) (asdescribed above). Since the gaps (between elements) within the same bucketwould only be \leq tt, we coulddeduce that the maximal gap would indeed occur onlybetween two adjacent buckets.

Hence by settingbucket size bb tobe 1 < b\leq (max - min)/(n-1)1<b≤(maxmin)/(n−1), we can ensurethat at least one of the gaps between adjacent buckets would serve as the maximalgap.

Clarifications

A fewclarifications are in order:

  • Would the buckets be of uniform size? Yes. Each of them would be of the same size bb.
  • But, then wouldn't the gap between them be uniform/constant as well? Yes it would be. The gap between them would be 11 integer unit wide. That means a two adjacent buckets of size 33 could hold integers with values, say, 3 - 63−6 and 7 - 97−9. We avoid overlapping buckets.
  • Then what are you talking about when you say the gap between two adjacent buckets could be the maximal gap? When we are talking about the size of a bucket, we are talking about its holding capacity. That is the range of values the bucket can represent (or hold). However the actual extent of the bucket are determined by the values of the maximum and minimum element a bucket holds. For example a bucket of size 55 could have a capacity to hold values between 6 - 106−10. However, if it only holds the elements 7, 87,8 and 99, then its actual extent is only (9 - 7) + 1 = 3(9−7)+1=3 which is not the same as the capacity of the bucket.
  • Then how do you compare adjacent buckets? We do that by comparing their extents. Thus we compare the minimum element of the next bucket to the maximum element of the current bucket. For example: if we have two buckets of size 55 each, holding elements [1, 2, 3][1,2,3] and [9, 10][9,10] respectively, then the gap between the buckets would essentially refer to the value 9 - 3 = 69−3=6 (which is larger than the size of either bucket).
  • But then aren't we comparing elements again?! We are, yes! But only compare about twice the elements as the number of buckets (i.e. the minimum and maximum elements of each bucket). If you followed the above, you would realize that this amount is certainly less than the actual number of elements in the array, given a suitable bucket size was chosen.

Algorithm

  • We choose a bucket size bb such that 1 < b \leq (max - min)/(n-1)1<b≤(maxmin)/(n−1). Let's just choose b = \lfloor (max - min)/(n-1) \rfloorb=⌊(maxmin)/(n−1)⌋.
  • Thus all the nn elements would be divided among k = \lceil (max - min)/b \rceilk=⌈(maxmin)/b⌉ buckets.
  • Hence the i^{th}ith​​ bucket would hold the range of values: \bigg [min + (i-1) * b, \space min + i*b \bigg )[min+(i−1)∗b, min+ib) (1-based indexing).
  • It is trivial to calculate the index of the bucket to which a particular element belongs. That is given by \lfloor (num - min)/b \rfloor⌊(nummin)/b⌋ (0-based indexing) where numnumis the element in question.
  • Once all nn elements have been distributed, we compare k-1k−1 adjacent bucket pairs to find the maximum gap.

C++

classBucket {

public:

    bool used = false;

    int minval = numeric_limits<int>::max();        // same as INT_MAX

    int maxval = numeric_limits<int>::min();        // same as INT_MIN

};

 

intmaximumGap(vector<int>& nums)

{

    if(nums.empty() || nums.size() < 2)

       return 0;

 

    int mini = *min_element(nums.begin(),nums.end()),

       maxi = *max_element(nums.begin(), nums.end());

 

    int bucketSize = max(1, (maxi - mini) / ((int)nums.size() - 1));       // bucket sizeor capacity

    int bucketNum = (maxi - mini) /bucketSize + 1;                         // number of buckets

   vector<Bucket> buckets(bucketNum);

 

    for(auto&& num : nums) {

       int bucketIdx = (num - mini) /bucketSize;                          // locating correct bucket

       buckets[bucketIdx].used = true;

       buckets[bucketIdx].minval = min(num, buckets[bucketIdx].minval);

       buckets[bucketIdx].maxval = max(num, buckets[bucketIdx].maxval);

    }

 

    int prevBucketMax = mini, maxGap =0;

    for(auto&& bucket : buckets) {

       if (!bucket.used)

           continue;

 

       maxGap = max(maxGap, bucket.minval - prevBucketMax);

       prevBucketMax = bucket.maxval;

    }

 

    returnmaxGap;

}

ComplexityAnalysis

  • Time complexity: O(n + b) \approx O(n)O(n+b)≈O(n).

Distributing the elements of the arraytakes one linear pass (i.e. O(n)O(n) complexity).Finding the maximum gap among the buckets takes a linear pass over the bucketstorage (i.e. O(b)O(b) complexity).Hence overall process takes linear runtime.

  • Space complexity: O(2*b) \approx O(b)O(2∗b)≈O(b) extra space.

Each bucket stores a maximum and aminimum element. Hence extra space linear to the number of buckets is required.

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值