diff --git a/Recursion and Backtracking/Recursion_new.md b/Recursion and Backtracking/1.md similarity index 96% rename from Recursion and Backtracking/Recursion_new.md rename to Recursion and Backtracking/1.md index ada3db1..5c26801 100644 --- a/Recursion and Backtracking/Recursion_new.md +++ b/Recursion and Backtracking/1.md @@ -28,7 +28,8 @@ _For example_, we compute factorial n if we know factorial of (n-1). The base ca Power ----- -> Given n, k. Find $n^k$ +> Given n, k. +Find $n^k$ ```python def pow(n, k): @@ -199,9 +200,10 @@ Because we can have negative values in the array as well and this condition will For eg, target = 6 1, 2, 3 is good, but -1, 2, 3, -1, 1 is also good. -__Time Complexity__: $O(2^n)$ -__Space Complexity__: $O(n)$ +1, 2, 3, -1, 1 is also good.
+__Time Complexity__: O(2^n) +
+__Space Complexity__: O(n) Number of Subsets with a given Sum (Repetition Allowed) --------------- @@ -211,6 +213,8 @@ Number of Subsets with a given Sum (Repetition Allowed) The subsetSum2 problem can be divided into two subproblems. - Include the current element in the sum and recur for the rest of the array. Here the value of i is not incremented to incorporate the condition of including multiple occurances of a element. - Exclude the current element from the sum and recur (i = i + 1) for the rest of the array. + + ![IMG_0040](https://user-images.githubusercontent.com/35702912/66470200-c27db600-eaa6-11e9-8744-ca572d6000e1.jpg) ```python def subsetSum2(A,N,cur_sum, i, target): @@ -225,8 +229,9 @@ def subsetSum2(A,N,cur_sum, i, target): no_take = subsetSum2(A,N,cur_sum, i+1, target) return take + no_take ``` -__Time Complexity__ : $O(2 ** (Target/MinElement))$ -__Space Complexity__: $O(Target/Min Element)$ +__Time Complexity__ : _O(2 *(Target/MinElement))_ +
+__Space Complexity__: _O(Target/Min Element)_ diff --git a/Recursion and Backtracking/2.md b/Recursion and Backtracking/2.md new file mode 100644 index 0000000..ade3732 --- /dev/null +++ b/Recursion and Backtracking/2.md @@ -0,0 +1,164 @@ +Backtracking +------------ + +Backtracking is a methodical way of trying out various sequences of decisions, until you find one that “works”. It is a systematic way to go through all the possible configurations of a search space. +- do +- recurse +- undo + +Backtracking is easily implemented with recursion because: +- The run-time stack takes care of keeping track of the choices that got us to a given point. +- Upon failure we can get to the previous choice simply by returning a failure code from the recursive call. + +Backtracking can help reduce the space complexity, because we're reusing the same storage. + +__Backtracking Algorithm__: +Backtracking is really quite simple--we “explore” each node, as follows: +```python +To “explore” node N: +1. If N is a goal node, return “success” +2. If N is a leaf node, return “failure” +3. For each child C of N, + 3.1. Explore C + 3.1.1. If C was successful, return “success” + 4. Return “failure” +``` +Print all Permutations of a String +------------- +> A permutation, also called an “arrangement number” or “order,” is a rearrangement of the elements of an ordered string S into a one-to-one correspondence with S itself.
+String: ABC
+Permutations: ABC ACB BAC BCA CBA CAB + +Total permutations = n! + + + + + +```python +def permute(S, i): + if i == len(S): + print(S) + for j in range(i, len(S)): + S[i], S[j] = S[j], S[i] + permute(S, i+1) + S[i], S[j] = S[j], S[i] # backtrack +``` +__Time Complexity:__ _O(n*n!)_ because there are n! permutations and it requires _O(n)_ to print a permutation. +
+__Space Complexity:__ _O(n)_ + +_Note: Output not in Lexicographic Order._ + +Print all Unique Permutations of a String +-------------------- +> String: AAB +> Permutations: AAB ABA BAA + +Basically, if we're swapping S[i] with S[j], but S[j] already occured earlier from S[i] .. S[j-1], then swapping will result in repetition. + + + + +```python +def permute_distinct(S, i): +if i == len(S): + print(S) +for j in range(i, len(S)): + if S[j] in S[i:j]: + continue + S[i], S[j] = S[j], S[i] + permute_distinct(S, i+1) + S[i], S[j] = S[j], S[i] # backtrack +``` +__Time Complexity:__ _O(n*n!)_
+__Space Complexity:__ _O(n)_ + +Print Permutations Lexicographically +--- +> Given a string, print all permutations of it in sorted order.
+For example, if the input string is “ABC”, then output should be “ABC, ACB, BAC, BCA, CAB, CBA”. + +- Right shift the elements before making the recursive call. +- Left shift the elements while backtracking. + +```python +def permute(S, i): + if i == len(S): + print(S) + for j in range(i, len(S)): + S[i], S[j] = S[j], S[i] + permute(S, i+1) + S[i], S[j] = S[j], S[i] # backtrack +``` + +__Time Complexity:__ _O(n* n*n!)_
+__Space Complexity:__ _O(n)_ + +Kth Permutation Sequence (Optional) +---- +> Given a string of length n containing lowercase alphabets only. You have to find the k-th permutation of string lexicographically. + + +$\dfrac{k}{(n-1)!}$ will give us the index of the first digit. Remove that digit, and continue. + +```python +def get_perm(A, k): + perm = [] + while A: + # get the index of current digit + div = factorial(len(A)-1) + i, k = divmod(k, div) + perm.append(A[i]) + # remove handled number + del A[index] +return perm +``` + + + +Sorted Permutation Rank (Optional) +-- +> Given S, find the rank of the string amongst its permutations sorted lexicographically. +Assume that no characters are repeated. + +```python +Input : 'acb' +Output : 2 +The order permutations with letters 'a', 'c', and 'b' : +abc +acb +bac +bca +cab +cba +``` +**Hint:** +If the first character is X, all permutations which had the first character less than X would come before this permutation when sorted lexicographically. + +Number of permutation with a character C as the first character = number of permutation possible with remaining $N-1$ character = $(N-1)!$ + +**Approach:** +rank = number of characters less than first character * (N-1)! + rank of permutation of string with the first character removed +``` +Lets say out string is “VIEW”. +Character 1 : 'V' +All permutations which start with 'I', 'E' would come before 'VIEW'. +Number of such permutations = 3! * 2 = 12 +Lets now remove ‘V’ and look at the rank of the permutation ‘IEW’. + +Character 2 : ‘I’ +All permutations which start with ‘E’ will come before ‘IEW’ +Number of such permutation = 2! = 2. +Now, we will limit ourself to the rank of ‘EW’. + +Character 3: +‘EW’ is the first permutation when the 2 permutations are arranged. +So, we see that there are 12 + 2 = 14 permutations that would come before "VIEW". +Hence, rank of permutation = 15. +``` + + \ No newline at end of file diff --git a/Recursion and Backtracking/3.md b/Recursion and Backtracking/3.md new file mode 100644 index 0000000..bdd65b3 --- /dev/null +++ b/Recursion and Backtracking/3.md @@ -0,0 +1,111 @@ +Number of Squareful Arrays +-------------------------- +> Given A[N] +> array is squareful if for every pair of adjacent elements, their sum is a perfect square +> Find and return the number of permutations of A that are squareful +> +Example: +A = [2, 2, 2] +output: 1 +A = [1, 17, 8] +output: 2 +[1, 8, 17], [17, 8, 1] + + +```python +def check(a, b): +sq = int((a + b) ** 0.5) +return (sq * sq) == (a + b) + +if len(A) == 1: # corner case +return int(check(A[0], 0)) + +count = 0 +def permute_distinct(S, i): +global count +if i == len(S): +count += 1 + +for j in range(i, len(S)): +if S[j] in S[i:j]: # prevent duplicates +continue + +if i > 0 and (not check(S[j], S[i-1])): # invalid solution - branch and bound +continue + +S[i], S[j] = S[j], S[i] +permute_distinct(S, i+1) + +S[i], S[j] = S[j], S[i] # backtrack +permute_distinct(A, 0) +return count +``` + +Gray Code +--------- + +> Given a non-negative integer N representing the total number of bits in the code, print the sequence of gray code. A gray code sequence must begin with 0. + +> The gray code is a binary numeral system where two successive values differ in only one bit. + + +G(n+1) can be constructed as: +0 G(n) +1 R(n) + +``` +Example G(2) to G(3): +0 00 +0 01 +0 11 +0 10 +---- +1 10 +1 11 +1 01 +1 00 +``` +```python +def gray(self, n): +codes = [0, 1] # length 1 +for i in range(1, n): +new_codes = [s | (1 << i) for s in reversed(codes)] +codes += new_codes +return codes +``` + +N Queens +-------- + +[NQueens - InterviewBit](https://www.interviewbit.com/problems/nqueens/) +Backtracking +- Place one queen per row +- backtrack if failed + +Word Break II +------------- +> Given a string A and a dictionary of words B, add spaces in A to construct a sentence where each word is a valid dictionary word. +``` +Input 1: +A = "catsanddog", +B = ["cat", "cats", "and", "sand", "dog"] + +Output 1: +["cat sand dog", "cats and dog"] +``` +```python +def wordBreak(A, B): +B = set(B) +sents = [] +def foo(i, start, sent): +word = A[start:i+1] +if i == len(A): +if word in B: +sents.append((sent + ' ' + word).strip()) +return + +if word in B: +foo(i+1, i+1, sent + ' ' + word) +foo(i+1, start, sent) +foo(0, 0, '') +``` \ No newline at end of file