Skip to content

Commit 6be0c92

Browse files
authored
Simpler backtracking example (#165)
1 parent 22518b3 commit 6be0c92

File tree

2 files changed

+40
-16
lines changed

2 files changed

+40
-16
lines changed

Diff for: backtracking/README.md

+39-15
Original file line numberDiff line numberDiff line change
@@ -11,36 +11,60 @@ Backtracking algorithms are typically implemented in these steps:
1111
1. Prune invalid approaches when possible.
1212
2. Generate a partial solution by iterating through available alternatives.
1313
3. Check the validity of the selected alternative according to the problem conditions and rules.
14-
4. Check for solution completion when required.
14+
4. Check base cases for solution completion when required.
1515

1616
After finding a backtracking approach to a problem we can start coding by creating a driver function and a recursive function.
1717

18-
The recursive function should receive as input the current state of the progress and uses it to checks for solution completion and to make the next incremental step by recursively calling itself. The recursive function should also have a way to communicate the solution back to the driver. The driver is responsible for making the first call to the recursive function and interpreting the final solution or failure in finding it back to the caller function. To illustrate this lets solve a problem that we will later solve more efficiently as one of the rehearsals in the [Dynamic Programming](../dp/) section.
19-
20-
Sum Up to a Number: Given a set of integers and a positive integer n return true if there is a subset of numbers that can sum up to n and false otherwise. For example for {1,2,3,4,5} and n=10 it should return true because 2+3+5=10 and for n 50 it should return false because there is no subset that can be summed up to 50.
18+
The recursive function should receive current state of the progress as input and uses it to checks the base cases for solution completion and to make the next incremental step by recursively calling itself. The recursive function should also have a way to communicate the solution back to the driver. The driver is responsible for making the first call to the recursive function and interpreting the final solution or failure in finding it back to the caller function. Lets review an example to illustrate this process.
2119

2220
```Go
2321
package main
2422

25-
func SumUpToNumber(numbers []int, n int) bool {
26-
return sumpUpToNumberRecursive(numbers, n, 0)
23+
import "fmt"
24+
25+
// Contains returns true if text contains pattern and false otherwise. For example:
26+
// Contains("abracadabra", "cad") returns true
27+
// Contains("abracadabra", "ra") returns true
28+
// Contains("abracadabra", "zebra") returns false
29+
func Contains(text, pattern string) bool {
30+
return containsRecursive(text, pattern, 0, 0)
2731
}
2832

29-
func sumpUpToNumberRecursive(numbers []int, n int, index int) bool {
30-
if n == 0 {
31-
return true
33+
func containsRecursive(text, pattern string, textIndex, patternIndex int) bool {
34+
if patternIndex == len(pattern) {
35+
return true
36+
}
37+
if textIndex == len(text) {
38+
return false
3239
}
33-
if n < 0 || index == len(numbers) {
34-
return false
40+
41+
if text[textIndex] == pattern[patternIndex] {
42+
if containsRecursive(text, pattern, textIndex+1, patternIndex+1) {
43+
return true
44+
}
3545
}
36-
return sumpUpToNumberRecursive(numbers, n-numbers[index], index+1) || // Include the number
37-
sumpUpToNumberRecursive(numbers, n, index+1) // Exclude the number
46+
47+
return containsRecursive(text, pattern, textIndex+1, 0)
3848
}
3949
```
4050

41-
The driver function `SumUpToNumber` prepares the first call to the recursive function by sending it all the input it has received and an index number which starts from 0.
51+
To solve this problem using the backtracking technique:
52+
53+
* Recursively iterate through each character of `text` at `textIndex` and compare it with the first character of the `pattern`:
54+
55+
* If they match compare every remaining character in text with every remaining character in pattern and if they are all equal then success base case condition is satisfied.
56+
* If they are not equal then increment the `textIndex` repeat
57+
58+
The driver function `Contains` prepares the first call to the recursive function by sending it all the inputs it has received and to index numbers as `textIndex` and `patternIndex` as 0.
59+
60+
The recursive function `containsRecursive` then checks for base cases. If `patternIndex` equals the length of `pattern` it means every character of the pattern has been compared to a substring of text and they have all matched so the function returns true. If `textIndex`equals the length of `text` then all characters have been explored and the pattern has not been found so it returns false.
61+
62+
Then it checks to see if the character at `textIndex` in `text` matches the character at `patternIndex` in `pattern`:
63+
64+
* If they match it will recursively call the same function to check the remaining characters.
65+
* If they do not match it backtracks to the next character in text by recursively calling the same function with an incremented textIndex.
4266

43-
The recursive function `sumpUpToNumberRecursive` then checks to see if we have reached a satisfying problem condition i.e. a final success or failure and returns it. Then it calls itself in two ways by checking to see if the sum can be achieved by including the value at current index or excluding it.
67+
For example for inputs `abracadabra` and `cad` it will check to see if `a` equals `c`, if it doesn't then it looks at the next character in text which is `b`. Since it does not match it will keep incrementing `textIndex` until it equals 4 at which point it will match `c`. Then it will check to see if text and pattern match at index 5 and 1. If they do then 6 and 2, and if they do since `cad` has three letters and we have found 3 matching elements we can conclude the text contains the pattern.
4468

4569
## Complexity
4670

Diff for: graph/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ For any vertex S that is reachable from V, the simple path in the BFS tree from
175175

176176
#### Depth First Search - DFS
177177

178-
Depth First Search (DFS) is a graph traversal algorithm that explores a graph as far as possible along each branch before backtracking. When implemented iteratively, it uses a [stack](../stack) data structure, is [recursive](../recursion), and is a generalization of pre-order traversal in trees.
178+
Depth First Search (DFS) is a graph traversal algorithm that explores a graph as far as possible along each branch before [backtracking](../backtracking/). When implemented iteratively, it uses a [stack](../stack) data structure, is [recursive](../recursion), and is a generalization of pre-order traversal in trees.
179179

180180
When given a graph G and a vertex S, DFS systematically discovers all nodes in G reachable from S. It is typically implemented using a driver that discovers the edges of the most recently discovered vertex V with unexplored edges. Once all of V's edges have been explored, the search [backtracks](../backtracking/) to explore all edges leaving the vertex from which V was discovered. This process continues until all the edges are discovered.
181181

0 commit comments

Comments
 (0)