Skip to content

Commit

Permalink
Merge pull request #55 from bovem/drafts
Browse files Browse the repository at this point in the history
Updating the RSS Feed link and adding Space complexity analysis
  • Loading branch information
bovem committed Nov 7, 2023
2 parents b4c85c8 + dfcc827 commit 860098b
Show file tree
Hide file tree
Showing 28 changed files with 224 additions and 167 deletions.
2 changes: 1 addition & 1 deletion content/posts/dsa/arrays-strings-hashmaps/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ func main(){
```

<hr>
Thank you for taking the time to read this blog post! If you found this content valuable and would like to stay updated with my latest posts consider subscribing to my <a href="https://www.bovem.in/index.xml" target="_blank">RSS Feed</a>.
Thank you for taking the time to read this blog post! If you found this content valuable and would like to stay updated with my latest posts consider subscribing to my <a href="https://www.avni.sh/index.xml" target="_blank">RSS Feed</a>.

# Resources
<a href="https://www.freecodecamp.org/news/hash-tables/" target="_blank">Hash Table Explained: What it Is and How to Implement It</a>
Expand Down
39 changes: 23 additions & 16 deletions content/posts/dsa/contains-duplicate/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,32 +34,35 @@ loop index1 in array
return true
return false
```

## Best Case Scenario
## Time Complexity Analysis
### Best Case Scenario
The best-case scenario for the brute-force solution would be when the first and second elements are duplicated. In this scenario, the outer and inner loop will execute only once so the time complexity will be $O(1)$.

<p align="center"><img src="contains-duplicate-best-case.png" alt="Best Case scenario for containsDuplicate"></p>

## Worst Case Scenario
### Worst Case Scenario
If all the elements in the array are unique then the brute-force algorithm will take $O(n^2)$ time for completion, where $n$ is the size of the array.

<p align="center"><img src="contains-duplicate-worst-case.png" alt="Worst Case scenario for containsDuplicate"></p>

## Code for Brute Force Solution
## Space Complexity Analysis
Since the brute-force solution does not use any data structures other than the input array, its space complexity will be $O(1)$.

## Code for the Brute Force Solution
```Go
package main

import "fmt"

func containsDuplicate(array []int)(bool){
for i:=0;i<len(array);i++{
for j:=0;j<len(array);j++{
func containsDuplicate(nums []int)(bool){
for i:=0;i<len(nums);i++{
for j:=0;j<len(nums);j++{
// Checking if the element selected by index j
// is not the same as the outer loop

// If the value of both elements is the same
// then exit the function with the value true
if i!=j && array[i]==array[j]{
if i!=j && nums[i]==nums[j]{
return true
}
}
Expand Down Expand Up @@ -96,32 +99,36 @@ loop value in array
return false
```

## Best Case Scenario
## Time Complexity Analysis
### Best Case Scenario
The best case scenario for the optimized solution is the same as the brute-force solution i.e. first and second elements are duplicated resulting in constant ($O(1)$) runtime.

## Worst Case Scenario
### Worst Case Scenario
If the input array contains unique elements, then the optimized solution will iterate over the complete array adding each element to HashMap but the time complexity of completing this would still be $O(n)$ which is much better than brute force solution ($O(n^2)$).

## Code for Optimized Solution
## Space Complexity Analysis
The hashmap in the optimized solution will use extra $O(n)$ memory space.

## Code for the Optimized Solution
```Go
package main

import "fmt"

func containsDuplicate(array []int)(bool){
func containsDuplicate(nums []int)(bool){
hashmap := make(map[int]int)

for i:=0;i<len(array);i++{
for i:=0;i<len(nums);i++{
// Check if a value at key exists in the hashmap
value, key_exists := hashmap[array[i]]
value, key_exists := hashmap[nums[i]]

if key_exists{
// If the value exists then exit the function
return true
} else {
// If the value does not exist
// add it to the hashmap
hashmap[array[i]] = value
hashmap[nums[i]] = value
}
}
return false
Expand All @@ -143,7 +150,7 @@ func main(){
```

<hr>
Thank you for taking the time to read this blog post! If you found this content valuable and would like to stay updated with my latest posts consider subscribing to my <a href="https://www.bovem.in/index.xml" target="_blank">RSS Feed</a>.
Thank you for taking the time to read this blog post! If you found this content valuable and would like to stay updated with my latest posts consider subscribing to my <a href="https://www.avni.sh/index.xml" target="_blank">RSS Feed</a>.

# Resources
<a href="https://leetcode.com/problems/contains-duplicate/" target="_blank">217. Contains Duplicate</a>
Expand Down
23 changes: 16 additions & 7 deletions content/posts/dsa/encoding-and-decoding-strings/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,20 @@ func decode(encodedString)
return decodedList
```
## Best Case Scenario

## Time Complexity Analysis
### Best Case Scenario
The best-case input for the brute force solution would be a string full of delimiters since the decoding process will finish earlier (the `index` is incremented by 2 while encountering the escape character `\`).

The time complexity of encoding and decoding in the best-case scenario would be $O(k \times n)$ where $k$ is the average size of the string and $n$ is the size of the input array.

## Worst Case Scenario
### Worst Case Scenario
For the worst-case time complexity of the brute-force solution, the input should not contain the delimiter or escape characters. The time complexity of encoding and decoding would still be $O(k \times n)$.

## Code for Brute Force Solution
## Space Complexity Analysis
The additional space required to store the `encodedString` and `decodedList` will be $O(kn)$.

## Code for the Brute Force Solution
```Go
package main

Expand Down Expand Up @@ -233,19 +238,23 @@ func decode(encodedString)
return decodedList
```
## Best Case Scenario
## Time Complexity Analysis
### Best Case Scenario
The best-case input for the optimized solution will contain strings with length < 9.

The time complexity of the encoding loop will be $O(n)$ because it will iterate over all the elements in the array.

For the decoding loop, the time complexity appears to be $O(kn)$ but we are incrementing the `index` by the length of string value ($k$) on every iteration. Thus, the time complexity of decoding is also $O({kn \over k}) = O(n)$.

## Worst Case Scenario
### Worst Case Scenario
The worst-case time complexity of encoding is the same as the best-case i.e. $O(n)$.

The time complexity of the decoding function will depend on the number of digits (in the length of the largest string). For example: If the length of the largest individual string is `199990` then the time complexity of decoding the string will be $O(6 \times n)$ (because `199990` has 6 digits).

## Code for Optimized Solution
## Space Complexity Analysis
The space complexity of the optimized solution will be the same as the brute force solution ($O(kn)$) as no additional data structures are used.

## Code for the Optimized Solution
```Go
package main

Expand Down Expand Up @@ -331,7 +340,7 @@ func main(){

<hr>

Thank you for taking the time to read this blog post! If you found this content valuable and would like to stay updated with my latest posts consider subscribing to my <a href="https://www.bovem.in/index.xml" target="_blank">RSS Feed</a>.
Thank you for taking the time to read this blog post! If you found this content valuable and would like to stay updated with my latest posts consider subscribing to my <a href="https://www.avni.sh/index.xml" target="_blank">RSS Feed</a>.

# Resources
<a href="https://www.lintcode.com/problem/659/" target="_blank">659 · Encode and Decode Strings</a>
Expand Down
48 changes: 28 additions & 20 deletions content/posts/dsa/group-anagrams/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,23 @@ loop key, value in hashmap
return returnedList
```

## Best Case Scenario
## Time Complexity Analysis
### Best Case Scenario
The time taken to sort individual strings is assumed to be $O(k \log(k))$ (the time complexity of the best sorting algorithm) where $k$ is the size of the string. Since we have to perform this operation on every element the sorting operation will be repeated $n$ time, where $n$ is the size of the array, resulting in $O(n \times k \log(k))$ time.

<p align="center"><img src="group-anagrams-brute-force-best.png" alt="Best case scenario for Brute Force solution for groupAnagrams"></p>

The best-case input will contain just a single set of anagrams i.e. the hashmap will contain just one key. The loop over hashmap values will be executed only once. Thus, the total time complexity of grouping anagrams will be $O(n \times k \log(k))$.

## Worst Case Scenario
### Worst Case Scenario
<p align="center"><img src="group-anagrams-brute-force-worst.png" alt="Worst case scenario for Brute Force solution for groupAnagrams"></p>

The worst-case input array will contain zero anagrams and the hashmap will contain a key for each array element i.e. $n$ keys. Thus, the total time complexity of the function in the worst-case scenario will be $O(n + n \times k \log(k))$, which could be simplified to $O(n \times k \log(k))$.

## Code for Brute Force Solution
## Space Complexity Analysis
The additional space required to store the `hashmap` and `returnedList` will be $O(n)+O(n)$ or simply $O(n)$.

## Code for the Brute Force Solution
```Go
package main

Expand All @@ -80,24 +84,24 @@ func sortString(inputString string)(string){
return strings.Join(listInputString, "")
}

func groupAnagrams(inputArray []string)([][]string){
func groupAnagrams(strs []string)([][]string){
hashmap := make(map[string][]string)

// Loop over the inputArray and sort every string element
for index:=0;index<len(inputArray);index++{
// Loop over the strs and sort every string element
for index:=0;index<len(strs);index++{

// The time complexity of the sorting operation
// is assumed to be O(nlog(n))
// where n is the size of the string
sortedString := sortString(inputArray[index])
sortedString := sortString(strs[index])

// Group elements by their sorted order
_, key_exists := hashmap[sortedString]
if key_exists{
hashmap[sortedString] = append(hashmap[sortedString],
inputArray[index])
strs[index])
} else {
hashmap[sortedString] = []string{inputArray[index]}
hashmap[sortedString] = []string{strs[index]}
}
}

Expand Down Expand Up @@ -150,13 +154,17 @@ for key, value in anagramHashmap
return returnedList
```

## Best Case Scenario
## Time Complexity Analysis
### Best Case Scenario
The best-case input will contain only one set of anagrams. Thus the total time complexity of the function will be $O(n \times k)$.

## Worst Case Scenario
### Worst Case Scenario
For the worst-case input (none of the strings are anagrams) the loop over `anagramHashMap` will take $O(n)$ time. Hence, the time complexity of the function will be $O(n \times k)$ (generalized from $O(n + n \times k)$).

## Code for Optimized Solution
## Space Complexity Analysis
The size of `countHashMap` is constant i.e. $O(26)$. After adding the space required by `anagramHashMap` and `returnedList`, the total space complexity of the optimized solution will be $O(n)+O(n)+O(26)$, which could be simplified to $O(n)$.

## Code for the Optimized Solution
First, we have to implement a `generateCountHashMap` function that will return a hashmap with keys ranging from `a` to `z` mapped to their initial count i.e. `0`.

```Go
Expand Down Expand Up @@ -206,11 +214,11 @@ func loadStringValue(countHashMap map[string]int,
Finally, we use `generateCountHashMap` and `loadStringValue` to implement the `groupAnagrams` function.

```Go
func groupAnagrams(inputArray []string)([][]string){
func groupAnagrams(strs []string)([][]string){
anagramHashMap := make(map[string][]string)

for index:=0;index<len(inputArray);index++{
stringValue := inputArray[index]
for index:=0;index<len(strs);index++{
stringValue := strs[index]

// Constant time complexity
countHashMap := generateCountHashMap()
Expand Down Expand Up @@ -287,11 +295,11 @@ func loadStringValue(countHashMap map[string]int,
return strings.Join(returnString, "")
}

func groupAnagrams(inputArray []string)([][]string){
func groupAnagrams(strs []string)([][]string){
anagramHashMap := make(map[string][]string)

for index:=0;index<len(inputArray);index++{
stringValue := inputArray[index]
for index:=0;index<len(strs);index++{
stringValue := strs[index]

// Constant time complexity
countHashMap := generateCountHashMap()
Expand Down Expand Up @@ -333,8 +341,8 @@ func main(){

<hr>

Thank you for taking the time to read this blog post! If you found this content valuable and would like to stay updated with my latest posts consider subscribing to my <a href="https://www.bovem.in/index.xml" target="_blank">RSS Feed</a>.
Thank you for taking the time to read this blog post! If you found this content valuable and would like to stay updated with my latest posts consider subscribing to my <a href="https://www.avni.sh/index.xml" target="_blank">RSS Feed</a>.

# Resources
<a href="https://leetcode.com/problems/group-anagrams/description/" target="_blank">49. Group Anagrams</a>
<a href="https://www.youtube.com/watch?v=vzdNOK2oB2E" target="_blank">Group Anagrams - Categorize Strings by Count - Leetcode 49</a>
<a href="https://www.youtube.com/watch?v=vzdNOK2oB2E" target="_blank">Group Anagrams - Categorize Strings by Count - Leetcode 49</a>
41 changes: 24 additions & 17 deletions content/posts/dsa/is-anagram/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,21 @@ loop index in string1
return true
```

## Best Case Scenario
## Time Complexity Analysis
### Best Case Scenario
The best time complexity of a sorting function is $O(n \log(n))$, so the total time complexity of sorting operations is $O(s \log(s)) + O(t \log(t))$ where $s$ and $t$ are the sizes of strings. Since both strings are of the same length (for anagrams) we can generalize time complexity to $O(s \log(s))$.

If the inputs are of different lengths (non-anagrams) then the time complexity of the complete function will be: $O(1)$ as the function will exit preemptively.

Even though we have to compare characters in a loop ($O(s)$) the sorting time will scale relatively slower. Thus, the total time complexity of the best-case scenario will be $O(s \log(s))$.

## Worst Case Scenario
### Worst Case Scenario
The worst-case scenario for a brute force solution will compare all the characters in sorted strings to find that they aren't anagrams. Its total time complexity will be $2*O(s \log(s)) + O(s)$ but we can generalize it to $O(s \log(s))$.

## Code for Brute Force Solution
## Space Complexity Analysis
The sorting of input strings is performed in-place. Thus, there is no requirement for additional memory space by the brute-force solution and its space complexity will be $O(1)$.

## Code for the Brute Force Solution
```Go
package main

Expand All @@ -84,7 +87,7 @@ func sortString(inputString string)(string){
return strings.Join(str, "")
}

func validAnagram(string1 string, string2 string)(bool){
func isAnagram(string1 string, string2 string)(bool){
if len(string1) != len(string2){
return false
}
Expand All @@ -107,15 +110,15 @@ func main(){
string1 := "counter"
string2 := "trounce"
fmt.Println(string1, "is an Anagram of",
string2, ":", validAnagram(string1, string2))
string2, ":", isAnagram(string1, string2))

string2 = "trouncee"
fmt.Println(string1, "is an Anagram of",
string2, ":", validAnagram(string1, string2))
string2, ":", isAnagram(string1, string2))

string2 = "trounc"
fmt.Println(string1, "is an Anagram of",
string2, ":", validAnagram(string1, string2))
string2, ":", isAnagram(string1, string2))
}

// Output
Expand Down Expand Up @@ -156,19 +159,23 @@ else
return false
```

## Best Case Scenario
## Time Complexity Analysis
### Best Case Scenario
In the best-case scenario for an optimized solution both strings will be anagram and the time complexity will be $O(s)$ (generalized from $O(s) + O(s)$).

## Worst Case Scenario
### Worst Case Scenario
The time complexity of the worst-case scenario will be the same as the best-case scenario i.e. $O(s)$ but the input strings won't be anagrams.

## Code for Optimized Solution
## Space Complexity Analysis
The `counter` hashmap will take additional $O(n)$ memory space.

## Code for the Optimized Solution
```Go
package main

import "fmt"

func validAnagram(inputString1 string, inputString2 string)(bool){
func isAnagram(inputString1 string, inputString2 string)(bool){
if len(inputString1) != len(inputString2){
return false
}
Expand Down Expand Up @@ -209,15 +216,15 @@ func main(){
string1 := "counter"
string2 := "trounce"
fmt.Println(string1, "is an Anagram of",
string2, ":", validAnagram(string1, string2))
string2, ":", isAnagram(string1, string2))

string2 = "trouncee"
fmt.Println(string1, "is an Anagram of",
string2, ":", validAnagram(string1, string2))
string2, ":", isAnagram(string1, string2))

string2 = "trounc"
fmt.Println(string1, "is an Anagram of",
string2, ":", validAnagram(string1, string2))
string2, ":", isAnagram(string1, string2))
}

// Output
Expand All @@ -227,8 +234,8 @@ func main(){
```

<hr>
Thank you for taking the time to read this blog post! If you found this content valuable and would like to stay updated with my latest posts consider subscribing to my <a href="https://www.bovem.in/index.xml" target="_blank">RSS Feed</a>.
Thank you for taking the time to read this blog post! If you found this content valuable and would like to stay updated with my latest posts consider subscribing to my <a href="https://www.avni.sh/index.xml" target="_blank">RSS Feed</a>.

# Resources
<a href="https://leetcode.com/problems/contains-duplicate/" target="_blank">242. Valid Anagram</a>
<a href="https://leetcode.com/problems/valid-anagram/" target="_blank">242. Valid Anagram</a>
<a href="https://www.youtube.com/watch?v=9UtInBqnCgA" target="_blank">Valid Anagram - Leetcode 242 - Python</a>
Loading

0 comments on commit 860098b

Please sign in to comment.