diff --git a/Arrays/find_el_smaller_left_bigger_right.py b/Arrays/find_el_smaller_left_bigger_right.py index 0fd8789..0285f5d 100644 --- a/Arrays/find_el_smaller_left_bigger_right.py +++ b/Arrays/find_el_smaller_left_bigger_right.py @@ -1,18 +1,18 @@ ''' Find the element before which all the elements are smaller than it, and after which all are greater -Given an array, find an element before which all elements are smaller than it, and after which all are greater than it. -Return the index of the element if there is such an element, otherwise, return -1. +Given an unsorted array of size N. Find the first element in array such that all of its left elements are smaller and all right elements to it are greater than it. +Note: Left and right side elements can be equal to required element. And extreme elements cannot be required element. Input: [5, 1, 4, 3, 6, 8, 10, 7, 9] -Output: 4 +Output: 6 ========================================= -Traverse the array starting from left and find all max elements till that element. -Also traverse the array starting from right and find all min elements till that element. -In the end only compare mins and maxs with the curent element. +Traverse the array starting and check if the current element is smaller than the wanted one. If it's smaller then reset the result. +In meantime keep track of the maximum value till the current position. This maximum value will be used for finding a new "middle" element. +If the maximum value Time Complexity: O(N) - Space Complexity: O(N) + Space Complexity: O(1) ''' @@ -20,37 +20,48 @@ # Solution # ############ -import math - def find_element_smaller_left_bigger_right(arr): n = len(arr) - left_maxs = [-math.inf] - right_min = math.inf - - # find all mins from the front - for i in range(n - 1): - left_maxs.append(max(left_maxs[-1], arr[i])) + curr_max = arr[0] + result = -1 - for i in range(n - 1, -1, -1): - # check if all left are smaller - # and all right are bigger - if (left_maxs[i] < arr[i]) and (right_min > arr[i]): - return i + for i in range(1, n): + curr_el = arr[i] - # don't need a separate for loop for this as mins - right_min = min(right_min, arr[i]) + if result == -1 and curr_el >= curr_max and i != n - 1: + result = curr_el + elif curr_el < result: + result = -1 - return -1 + if curr_el > curr_max: + curr_max = curr_el + return result ########### # Testing # ########### # Test 1 -# Correct result => 4 +# Correct result => 6 print(find_element_smaller_left_bigger_right([5, 1, 4, 3, 6, 8, 10, 7, 9])) # Test 2 # Correct result => -1 -print(find_element_smaller_left_bigger_right([5, 1, 4, 4])) \ No newline at end of file +print(find_element_smaller_left_bigger_right([5, 1, 4, 4])) + +# Test 3 +# Correct result => 7 +print(find_element_smaller_left_bigger_right([5, 1, 4, 6, 4, 7, 14, 8, 19])) + +# Test 4 +# Correct result => 5 +print(find_element_smaller_left_bigger_right([4, 2, 5, 7])) + +# Test 5 +# Correct result => -1 +print(find_element_smaller_left_bigger_right([11, 9, 12])) + +# Test 6 +# Correct result => 234 +print(find_element_smaller_left_bigger_right([177, 234, 236, 276, 519, 606, 697, 842, 911, 965, 1086, 1135, 1197, 1273, 1392, 1395, 1531, 1542, 1571, 1682, 2007, 2177, 2382, 2410, 2432, 2447, 2598, 2646, 2672, 2826, 2890, 2899, 2916, 2955, 3278, 3380, 3623, 3647, 3690, 4186, 4300, 4395, 4468, 4609, 4679, 4712, 4725, 4790, 4851, 4912, 4933, 4942, 5156, 5186, 5188, 5244, 5346, 5538, 5583, 5742, 5805, 5830, 6010, 6140, 6173, 6357, 6412, 6414, 6468, 6582, 6765, 7056, 7061, 7089, 7250, 7275, 7378, 7381, 7396, 7410, 7419, 7511, 7625, 7639, 7655, 7776, 7793, 8089, 8245, 8622, 8758, 8807, 8969, 9022, 9149, 9150, 9240, 9273, 9573, 9938])) diff --git a/Arrays/find_el_where_k_greater_or_equal.py b/Arrays/find_el_where_k_greater_or_equal.py index 3e5e497..4c56747 100644 --- a/Arrays/find_el_where_k_greater_or_equal.py +++ b/Arrays/find_el_where_k_greater_or_equal.py @@ -7,12 +7,16 @@ Input: [3,8,5,1,10,3,20,24], 2 Output: 11 -Output explanation: Only 20 and 24 are greater or smaller from 11 (11 is the smallest solution, also 12, 13...20 are solutions). +Output explanation: Only 20 and 24 are equal or smaller from 11 (11 is the smallest solution, also 12, 13...20 are solutions). ========================================= Sort the array and check the Kth element from the end. Time Complexity: O(NLogN) Space Complexity: O(1) +QuickSelect can be used (find the K+1th number + 1). https://en.wikipedia.org/wiki/Quickselect +See kth_smallest.py, very similar solution. + Time Complexity: O(N) + Space Complexity: O(1) ''' @@ -46,4 +50,4 @@ def get_minimum_X(arr, k): # Test 1 # Correct result => 11 -print(get_minimum_X([3, 8, 5, 1, 10, 3, 20, 24], 2)) \ No newline at end of file +print(get_minimum_X([3, 8, 5, 1, 10, 3, 20, 24], 2)) diff --git a/Arrays/k_closest_points.py b/Arrays/k_closest_points.py index e1e4971..fde0701 100644 --- a/Arrays/k_closest_points.py +++ b/Arrays/k_closest_points.py @@ -13,6 +13,9 @@ Time Complexity: O(N) , O(N + N/2 + N/4 + N/8 + ... + 1 = 2*N = N) Space Complexity: O(K) , length of the output array Completely the same algorithm as the previous one, but without recursion. This solution is cleaner. +This algorithm is called: QucikSelect - The quicksort pivoting logic but for searching kth smallest (not sorting the whole array) - O(n) complexity (n + n/2 + n/4 + ... + 1 = 2n) +https://en.wikipedia.org/wiki/Quickselect +Same solution as kth_smallest.py. Time Complexity: O(N) Space Complexity: O(K) ''' @@ -111,4 +114,4 @@ def find_k_closes(arr, pt, k): # Test 2 # Correct result => [(1, 2), (2, 1)] print(find_k_closes_recursive([(0, 1), (2, 1), (3, 3), (1, 2)], (2, 2), 2)) -print(find_k_closes([(0, 1), (2, 1), (3, 3), (1, 2)], (2, 2), 2)) \ No newline at end of file +print(find_k_closes([(0, 1), (2, 1), (3, 3), (1, 2)], (2, 2), 2)) diff --git a/Arrays/kth_smallest.py b/Arrays/kth_smallest.py index e28869d..a1f1353 100644 --- a/Arrays/kth_smallest.py +++ b/Arrays/kth_smallest.py @@ -18,6 +18,8 @@ Time Complexity: O(N) , O(N + N/2 + N/4 + N/8 + ... + 1 = 2*N = N) Space Complexity: O(LogN) , because of the recursion stack Completely the same algorithm as the previous one, but without recursion. This solution is cleaner. +This algorithm is called: QucikSelect - The quicksort pivoting logic but for searching kth smallest (not sorting the whole array) - O(n) complexity (n + n/2 + n/4 + ... + 1 = 2n) +https://en.wikipedia.org/wiki/Quickselect Time Complexity: O(N) Space Complexity: O(1) ''' @@ -119,4 +121,4 @@ def find_kth_smallest(arr, k): # Correct result => 1 2 3 4 5 arr = [5, 4, 3, 2, 1] print([find_kth_smallest_recursive(arr, i) for i in range(1, len(arr) + 1)]) -print([find_kth_smallest(arr, i) for i in range(1, len(arr) + 1)]) \ No newline at end of file +print([find_kth_smallest(arr, i) for i in range(1, len(arr) + 1)]) diff --git a/Arrays/top_k_frequent_elements.py b/Arrays/top_k_frequent_elements.py index 0c94ec3..890dc02 100644 --- a/Arrays/top_k_frequent_elements.py +++ b/Arrays/top_k_frequent_elements.py @@ -16,7 +16,9 @@ Time Complexity: O(U LogK) , U in this case is the number of unique elements (but all elements from the array could be unique, so because of that U can be equal to N) Space Complexity: O(N) Using pivoting, this solution is based on the quick sort algorithm (divide and conquer). -Same pivoting solution as the nth_smallest.py. +This algorithm is called: QucikSelect - The quicksort pivoting logic but for searching kth smallest (not sorting the whole array) - O(n) complexity (n + n/2 + n/4 + ... + 1 = 2n) +https://en.wikipedia.org/wiki/Quickselect +Same solution as kth_smallest.py. Time Complexity: O(U) Space Complexity: O(N) ''' @@ -161,4 +163,4 @@ def swap(arr, i, j): # Test 2 # Correct result => [1] print(top_k_frequent_1([1], 1)) -print(top_k_frequent_2([1], 1)) \ No newline at end of file +print(top_k_frequent_2([1], 1)) diff --git a/Math/number_of_digit_one.py b/Math/number_of_digit_one.py new file mode 100644 index 0000000..d68b697 --- /dev/null +++ b/Math/number_of_digit_one.py @@ -0,0 +1,95 @@ +''' +Number of Digit One + +Given an integer n, count the total number of digit 1 appearing in all non-negative integers less than or equal to n. + +Input: 13 +Output: 6 + +Input: 0 +Output: 0 + +========================================= +Count the occurence of 1s for each digit, count the occurance for 1s on each position in the number (example of position xx1x). +Example, if we have 4 digits number, we'll have 4 counts: + 1. count of xxx1 + 2. count of xx1x + 3. count of x1xx + 4. count of 1xxx +How we could count the occurences? +xxx1 +- On the last position, 1 occurs once in each 10 (1, 11, 21, 31, 41...91) +xx1x +- On the second from behind position, 1 occurs 10 times in each 100 (10, 11, 12, 13...19) +x1xx +- On the second from front position, 1 occurs 100 times in each 1000 (100, 101, 102...199) +1xxx +- On the first position, 1 occurs 1000 times in each 10000 (1000, 1001, 1002...1999) + +Examples: +Position x1 +15 = 2 times +11 = 2 times +10 = 1 time +Position x1x +155 = 2 times +115 = 2 times +105 = 1 tim + +That means we'll iterate all the digits, and search for these cases in all the digits: +if the current digit is 0 (in this case we know that Y1xxx didn't happend in the last/current Y) +if the current digit is 1 (will need to count the previous number, Y1xxx -> from 1000 till 1xxx) +(else) if the current digit is greater than 1 (we know that the whole interval Y1000-Y1999 occured) + + Time Complexity: O(digitsCount(N)) + Space Complexity: O(1) +''' + +############ +# Solution # +############ + +def countDigitOne(n: int) -> int: + base = 1 + sum = 0 + + while n >= base: + digit = (n // base) % 10 + occurrence = n // (base * 10) + + if digit == 0: + sum += occurrence * base + elif digit == 1: + previous = n % base + sum += occurrence * base + (previous + 1) + elif digit > 1: + sum += (occurrence + 1) * base + + sum += 0 + base = base * 10 + + return sum + +########### +# Testing # +########### + +# Test 1 +# Correct result => 6 +n = 13 +print(countDigitOne(n)) + +# Test 2 +# Correct result => 1 +n = 2 +print(countDigitOne(n)) + +# Test 3 +# Correct result => 92 +n = 155 +print(countDigitOne(n)) + +# Test 4 +# Correct result => 2 +n = 10 +print(countDigitOne(n)) \ No newline at end of file diff --git a/Other/fancy_sequence.py b/Other/fancy_sequence.py new file mode 100644 index 0000000..84b730c --- /dev/null +++ b/Other/fancy_sequence.py @@ -0,0 +1,119 @@ +''' +Fancy Sequence + +Write an API that generates fancy sequences using the append, addAll, and multAll operations. + +Implement the Fancy class: +- Fancy() Initializes the object with an empty sequence. +- void append(val) Appends an integer val to the end of the sequence. +- void addAll(inc) Increments all existing values in the sequence by an integer inc. +- void multAll(m) Multiplies all existing values in the sequence by an integer m. +- int getIndex(idx) Gets the current value at index idx (0-indexed) of the sequence modulo 109 + 7. If the index is greater or equal than the length of the sequence, return -1. + +Input: ["Fancy", "append", "addAll", "append", "multAll", "getIndex", "addAll", "append", "multAll", "getIndex", "getIndex", "getIndex"] +[[], [2], [3], [7], [2], [0], [3], [10], [2], [0], [1], [2]] +Output: [null, null, null, null, null, 10, null, null, null, 26, 34, 20] +Output explanation: + Fancy fancy = new Fancy(); + fancy.append(2); // fancy sequence: [2] + fancy.addAll(3); // fancy sequence: [2+3] -> [5] + fancy.append(7); // fancy sequence: [5, 7] + fancy.multAll(2); // fancy sequence: [5*2, 7*2] -> [10, 14] + fancy.getIndex(0); // return 10 + fancy.addAll(3); // fancy sequence: [10+3, 14+3] -> [13, 17] + fancy.append(10); // fancy sequence: [13, 17, 10] + fancy.multAll(2); // fancy sequence: [13*2, 17*2, 10*2] -> [26, 34, 20] + fancy.getIndex(0); // return 26 + fancy.getIndex(1); // return 34 + fancy.getIndex(2); // return 20 + +========================================= +The program is meant to be called many times (more than a million), and because of that, each operation should be constant time. +When getIndex is called all we need to do is to take the first (getIndex index) adding+multiplying values and the last adding+multiplying values +and using these 2 values do math and determine the real adding+multlplying values. +When there is a new adding value, just increase the adding value with that one. +When there is a new multiplying value, multiply the both adding and multiplying values. +Example x=append(8): (((x + 5) + 6) + 7) * 2 = (x + 17) * 2 = x * 2 + 34 +So total multiplayer is 2. +And total adder is 18 (from 36/2). +The result is (8+18)*2 = 52. +Example y=append(9) after addAll(6) operation: +(y + 7) * 2 = y * 2 + 14 +The total multiplayer is the same, 2 (the previous is 1, the last is 2 that's equal to 2/1=2). +But the total adder is 7, the total last adder is 18 (from 36/2) minus the previous 11 (from 5+6 with no multiplyers till that moment). +The result is (9+7)*2 = 32. + Time Complexity (The whole program, N operations): O(N) + Time Complexity (Only one operation): O(1) + Space Complexity: O(N) +''' + +############ +# Solution # +############ + +class Fancy: + def __init__(self): + self.appending = [] + self.adding = [] + self.multiplying = [] + + + def append(self, val: int) -> None: + self.appending.append(val) + + if len(self.appending) > 1: + self.adding.append(self.adding[-1]) + self.multiplying.append(self.multiplying[-1]) + else: + self.adding.append(0) + self.multiplying.append(1) + + def addAll(self, inc: int) -> None: + if len(self.appending) == 0: + return + + self.adding[-1] += inc + + + def multAll(self, m: int) -> None: + if len(self.appending) == 0: + return + + self.adding[-1] *= m + self.multiplying[-1] *= m + + + def getIndex(self, idx: int) -> int: + length = len(self.appending) + if idx >= length: + return -1 + + prevAdding = 0 + prevMultiplying = 1 + if idx > 0: + prevMultiplying = self.multiplying[idx-1] + prevAdding = self.adding[idx-1] + + currMultiplying = self.multiplying[-1] // prevMultiplying + currAdding = self.adding[-1] - (prevAdding * currMultiplying) + + return (self.appending[idx] * currMultiplying + currAdding) % 1000000007 + + +########### +# Testing # +########### + +# Test 1 +fancy = Fancy() +fancy.append(2) # fancy sequence: [2] +fancy.addAll(3) # fancy sequence: [2+3] -> [5] +fancy.append(7) # fancy sequence: [5, 7] +fancy.multAll(2) # fancy sequence: [5*2, 7*2] -> [10, 14] +print(fancy.getIndex(0)) # return 10 +fancy.addAll(3) # fancy sequence: [10+3, 14+3] -> [13, 17] +fancy.append(10) # fancy sequence: [13, 17, 10] +fancy.multAll(2) # fancy sequence: [13*2, 17*2, 10*2] -> [26, 34, 20] +print(fancy.getIndex(0)) # return 26 +print(fancy.getIndex(1)) # return 34 +print(fancy.getIndex(2)) # return 20 diff --git a/README.md b/README.md index 93a410c..de462c7 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ As I said previously, all solutions are written in [Python](https://www.python.o So, to execute these solutions there is no need from installing any external packages. \ Coding style and name conventions are described in the official [PEP8](https://www.python.org/dev/peps/pep-0008) page. -*Note that I'm **not** the author of these problems, they are from sites like [LeetCode](https://leetcode.com/) (you can find more than 40 sites like this in the [Training Sites](#Training-Sites) section). **Only** the solutions and explanations are mine.* +*Note that I'm **not** the author of these problems, they are from sites like [LeetCode](https://leetcode.com/) (you can find more than 40 sites like this in the [Training Sites](#Training-Sites) section). **Only** the solutions and explanations are mine. If you find any **bug** or incorrect implementation (or faster/better implementation) in this repo, please let me know by opening an issue or pull request.* ### Template @@ -107,7 +107,7 @@ The learning resources are divided into 4 categories: [Courses](#Courses), [Book Collection of free courses from one of the best CS universities. -1. Standford University +1. Stanford University - [Algorithms Specialization (Coursera)](https://www.coursera.org/specializations/algorithms) * [Divide and Conquer, Sorting and Searching, and Randomized Algorithms](https://www.coursera.org/learn/algorithms-divide-conquer) * [Graph Search, Shortest Paths, and Data Structures](https://www.coursera.org/learn/algorithms-graphs-data-structures) @@ -157,7 +157,7 @@ Several books that have made an impression on me: 1. [Grokking Algorithms by Aditya Bhargava](https://www.goodreads.com/book/show/22847284-grokking-algorithms-an-illustrated-guide-for-programmers-and-other-curio) - **The best** book for complete beginners in algorithms! I wish this book existed when I started learning algorithms. 2. [Introduction to Algorithms by CLRS](https://www.goodreads.com/book/show/6752187-introduction-to-algorithms) - This book is called the "bible textbook of algorithms" by many programmers. 3. [Algorithms by Robert Sedgewick & Kevin Wayne](https://www.goodreads.com/book/show/10803540-algorithms) - These authors are instructors of the previously mentioned Coursera courses: [Algorithms Part 1](https://www.coursera.org/learn/algorithms-part1) and [Algorithms Part 2](https://www.coursera.org/learn/algorithms-part2). Also, this book has an excellent and free [site](http://algs4.cs.princeton.edu) with exercises, presentations, and examples. -4. [The Algorithm Design Manual by Steven Skiena](https://www.goodreads.com/book/show/425208.The_Algorithm_Design_Manual) - The book describes many advanced topics and algorithms and it focuses on real-life practical examples. This book has [one](http://www.algorist.com) of the best sites with resources ([solutions](http://www.algorist.com/algowiki/index.php/The_Algorithms_Design_Manual_(Second_Edition)), [algorithms and data structures](http://www.algorist.com/algorist.html), [python implementations](http://www.algorist.com/languages/Python.html)). +4. [The Algorithm Design Manual by Steven Skiena](https://www.goodreads.com/book/show/425208.The_Algorithm_Design_Manual) - The book describes many advanced topics and algorithms and it focuses on real-life practical examples. This book has [one](http://www.algorist.com) of the best sites with resources ([solution wiki](https://algorist.com/algowiki/index.php/), [algorithms and data structures](http://www.algorist.com/algorist.html), [python implementations](http://www.algorist.com/languages/Python.html)). 5. [Algorithms by S. Dasgupta, C. Papadimitriou, and U. Vazirani](https://www.goodreads.com/book/show/138563.Algorithms) - This book is an official book for algorithms and data structures classes in several famous universities. 6. [Competitive Programming 3 by Steven Halim & Felix Halim](https://www.goodreads.com/book/show/22820968-competitive-programming-3) - A great book that prepares you for competitive programming (not for complete beginners). You can learn many things and tricks about competitive programming. *But if your goal is to prepare for competitive programming then choose a faster language than Python, **C/C++** (or Java, it's faster than Python but not like C/C++).* 7. [Cracking the Coding Interview by Gayle Laakmann McDowell](https://www.goodreads.com/book/show/29350585-cracking-the-coding-interview) - A bit different from the previous books. Prepares you for coding interviews using great coding problems. @@ -167,23 +167,23 @@ Several books that have made an impression on me: If the problems from [LeetCode](https://leetcode.com/) are not enough and you need more problems like those, you can find much more on these platforms: -- [HackerRank](http://hackerrank.com/) - [CodeChef](http://codechef.com/) - [HackerEarth](http://hackerearth.com/) - [CodeForces](http://codeforces.com/) - [Topcoder](http://topcoder.com/) -- [Project Euler](https://projecteuler.net/) +- [AtCoder](https://atcoder.jp/) +- [HackerRank](http://hackerrank.com/) - [SPOJ](http://www.spoj.com/) -- [A2OJ](https://a2oj.com/) - [PEG](https://wcipeg.com/) - [Online Judge](https://onlinejudge.org/) +- [AIZU OJ](https://onlinejudge.u-aizu.ac.jp/) - [E-Olymp](https://www.e-olymp.com/en/) - [VJudge](https://vjudge.net/) - [DMOJ](https://dmoj.ca/) - [USA CO](http://www.usaco.org/) +- [POJ](http://poj.org/) +- [Project Euler](https://projecteuler.net/) - [Rosetta Code](http://rosettacode.org/) -- [AtCoder](https://atcoder.jp/) -- [Russian Code Cup](https://www.russiancodecup.ru/en/) - [LintCode](http://www.lintcode.com/en/) - [Kattis](https://www.kattis.com/developers) - [CodeAbbey](http://codeabbey.com/) @@ -192,28 +192,27 @@ If the problems from [LeetCode](https://leetcode.com/) are not enough and you ne - [Exercism](https://exercism.io/) - [CodeFu](https://codefu.mk/) - [Mendo](https://mendo.mk/Welcome.do) -- [Z-Training](http://www.codah.club/) - [Codewars](http://www.codewars.com/) - [Wolfram Challenges](https://challenges.wolfram.com/) - [Google's Coding Competitions](https://codingcompetitions.withgoogle.com/) - [Cyber-dojo](https://cyber-dojo.org/) - [CodingBat](http://codingbat.com/) - [CodeKata](http://codekata.com/) -- [BinarySearch](https://binarysearch.io/) - [Daily Coding Problem](https://www.dailycodingproblem.com/) - [Daily Interview Pro](http://dailyinterviewpro.com/) - [AlgoDaily](https://algodaily.com/) - [Codility](https://codility.com/) - [CoderByte](https://coderbyte.com/) - [AlgoExpert](https://www.algoexpert.io/) +- [CodeSignal](https://codesignal.com/) - [Edabit](https://edabit.com/) - [DevPost](https://devpost.com/) - [Brilliant](http://brilliant.org/) - [Codingame](https://www.codingame.com/) - [CheckiO](http://www.checkio.org/) -- [FightCode](http://fightcodegame.com/) - [Kaggle](http://kaggle.com/) - [Rosalind](http://rosalind.info/problems/locations/) +- [workat.tech](https://workat.tech/problem-solving/practice/) ### Other Resources @@ -227,12 +226,12 @@ If the problems from [LeetCode](https://leetcode.com/) are not enough and you ne - [Algorithm Visualizer](https://algorithm-visualizer.org/) - Interactive online platform that visualizes algorithms from code. This platform is an open-source project, [here](https://github.com/algorithm-visualizer/algorithm-visualizer) you can find the source code. 5. Courses and tutorials (but not from universities like the [Courses](#Courses) section): - [Google - Intro to Data Structures and Algorithms](https://www.udacity.com/course/data-structures-and-algorithms-in-python--ud513) - Free course on Udacity offered by Google. - - [Microsoft - Algorithms and Data Structures](https://www.edx.org/course/algorithms-and-data-structures) - Free course on edX offered by Microsoft. - [HackerEarth - Tutorials and Practice](https://www.hackerearth.com/practice/) - Practice problems and learn about many algorithms and data structures needed for competitive programming. - [KhanAcademy - Algorithms](https://www.khanacademy.org/computing/computer-science/algorithms) - Good explanations for some basic algorithms. - [Tutorialspoint - Data Structures and Algorithms](https://www.tutorialspoint.com/data_structures_algorithms/index.htm) - Another platform with good explanations, also Tutorialspoint has free tutorials for almost everything related to CS! + - [Programiz - Data Structures and Algorithms](https://www.programiz.com/dsa) - One more platform which explains the data structures and algorithms in a simple and interesting way. - [Hackr.io - Data Structures and Algorithms Tutorials and Courses](https://hackr.io/tutorials/learn-data-structures-algorithms) - Big collection of tutorials and courses. - - [Classpert - Data Structures and Algorithms Courses](https://classpert.com/data-structures-and-algorithms) - List of over 80 Online Courses (free and paid). + - [Scaler - Data Structures Tutorial](https://www.scaler.com/topics/data-structures/) - Interesting and interactive explanations of some basic data structures. 6. YouTube playlists with tutorials: - [Data Structures by mycodeschool](https://www.youtube.com/playlist?list=PL2_aWCzGMAwI3W_JlcBbtYTwiQSsOTa6P) - [Data Structures by HackerRank](https://www.youtube.com/playlist?list=PLI1t_8YX-Apv-UiRlnZwqqrRT8D1RhriX) diff --git a/Strings/strong_password_checker.py b/Strings/strong_password_checker.py new file mode 100644 index 0000000..cb58eea --- /dev/null +++ b/Strings/strong_password_checker.py @@ -0,0 +1,150 @@ +''' +Strong Password Checker + +A password is considered strong if the below conditions are all met: +- It has at least 6 characters and at most 20 characters. +- It contains at least one lowercase letter, at least one uppercase letter, and at least one digit. +- It does not contain three repeating characters in a row (i.e., "...aaa..." is weak, but "...aa...a..." is strong, assuming other conditions are met). +- Given a string password, return the minimum number of steps required to make password strong. if password is already strong, return 0. + +In one step, you can: +- Insert one character to password, +- Delete one character from password, or +- Replace one character of password with another character. + +Input: password = "a" +Output: 5 + +Input: password = "aA1" +Output: 3 + +Input: password = "1337C0d3" +Output: 0 + +========================================= +Explanation in some kind of pseudo code: +if length < 6 + (3 different types characters, if all 5 are same, than 2 new separators should be added or 1 new added and 1 changed; or if all are the same type, 2 new types should be added, or 1 new and 1 changed) + if length == 5 && (5 repeating or 1 char type) + return 2 + return 6-length + +if length > 20 + delete all till 20 + +*now you're in legnth > 5 && length <=20 + separate groups by changing a character (dividing all groups >=3 with 3) + if char type 1 and changeChar here are <2 + changeChar += 2 - changeChar + +return deletingChar + changeChar + +first, make the string between 6 and 20 characters (when making this, separate/lower the groups bigger than 3) + - when less than 6 characters Array.add((group+1)/2, 1, group/2) - ADDING CHARACTERS - or just use the upper logic when length < 3; + - when more than 6 characters Array.add(group-1) - DELETING CHARACTERS, start from the one with groups bigger than 3, after that delete any)!!! Deleting priority 1. mod with 3 = 0 -> 2. mod with 3 = 1 -> 3. mod with 3 = 2 + +second, when the string is between 6 and 20 characters, change character in groups with same characters +3 -> 1 operation (*** -> *|*) => 3/3 = 1 +4 -> 1 operation (**** -> **|*) => 4/3 = 1 +5 -> 1 operations (***** -> **|**) => 5/3 = 1 +6 -> 2 operations (****** -> **|*** -> **|**|) => 6/3 = 2 +7 -> 2 operations (******* -> **|**** -> **|**|*) => 7/3 = 2 +8 -> 2 operations (******** -> **|***** -> **|**|**) => 8/3 = 2 +9 -> 3 operations (********* -> **|****** -> **|**|**|) => 9/3 = 3 + Time Complexity: O(N) + Space Complexity: O(N) +''' + + +############ +# Solution # +############ + +def strongPasswordChecker(password): + groups = [] + length = len(password) + last = 0 + lower = upper = digit = 0 + + for curr in range(length): + if password[curr].islower(): + lower = 1 + elif password[curr].isupper(): + upper = 1 + elif password[curr].isdigit(): + digit = 1 + + if password[last] != password[curr]: + groups.append(curr - last); + last = curr + + groups.append(curr + 1 - last); + lud = lower + upper + digit + addSteps = 0 + + # adding: under 6 chars + if length < 6: + if length == 5 and groups[0] == 5: + addSteps = 2 # add + change, or add + add - it's same + else: + addSteps = 6 - length + + ludLeft = 3 - lud + if addSteps < ludLeft: + addSteps = ludLeft + + return addSteps + + # deleting: over 20 chars + deleteSteps = 0 + groupsLength = len(groups) + while length > 20: + found = False + + for priority in range(3): + for gidx in range(groupsLength): + if groups[gidx] > 2 and groups[gidx] % 3 == priority: + groups[gidx] -= 1 + found = True + break + if found: + break + + if not found: + lastGroupIdx = groupsLength - 1 + groups[lastGroupIdx] -= 1 + if groups[lastGroupIdx] == 0: + groups.pop(lastGroupIdx) + groupsLength -= 1 + + length -= 1 + deleteSteps += 1 + + # changing: between 6 and 20 chars + changeSteps = 0 + + for gidx in range(groupsLength): + changeSteps += groups[gidx] // 3 + + ludLeft = 3 - lud + if changeSteps < ludLeft: + changeSteps = ludLeft + + return deleteSteps + changeSteps + + +########### +# Testing # +########### + +# Test 1 +# Correct result => 5 +print(strongPasswordChecker('a')) + +# Test 2 +# Correct result => 3 +print(strongPasswordChecker('aA1')) + +# Test 3 +# Correct result => 0 +print(strongPasswordChecker('1337C0d3')) diff --git a/Strings/zigzag_conversion.py b/Strings/zigzag_conversion.py index 6923152..9c85eca 100644 --- a/Strings/zigzag_conversion.py +++ b/Strings/zigzag_conversion.py @@ -16,12 +16,15 @@ Middle rows have 2 times more elements than the first and last row. Time Complexity: O(N) Space Complexity: O(N) +Collect all parts in separate bucket, in the end merge these buckets. + Time Complexity: O(N) + Space Complexity: O(N) ''' -############ -# Solution # -############ +############## +# Solution 1 # +############## def convert(s, num_rows): if num_rows == 1: @@ -48,6 +51,32 @@ def convert(s, num_rows): return res +############## +# Solution 2 # +############## + +def convert_2(word, numRows): + numLetters = len(word) + bucket = [""] * numRows + fullCycle = 2 * (numRows - 1) + if numRows == 1: + fullCycle = 1 + + for pos in range(0, numLetters): + posCycle = pos % fullCycle + + if posCycle >= numRows: + posCycle = fullCycle - posCycle + + bucket[posCycle] += word[pos] + + result = "" + for part in bucket: + result += part + + return result + + ########### # Testing # ########### @@ -55,7 +84,9 @@ def convert(s, num_rows): # Test 1 # Correct result => 'PAHNAPLSIIGYIR' print(convert('PAYPALISHIRING', 3)) +print(convert_2('PAYPALISHIRING', 3)) # Test 2 # Correct result => 'PINALSIGYAHRPI' print(convert('PAYPALISHIRING', 4)) +print(convert_2('PAYPALISHIRING', 4)) diff --git a/Trees/diameter_of_binary_tree.py b/Trees/diameter_of_binary_tree.py new file mode 100644 index 0000000..d91a7c6 --- /dev/null +++ b/Trees/diameter_of_binary_tree.py @@ -0,0 +1,71 @@ +''' +Diameter of Binary Tree + +Given a binary tree, you need to compute the length of the diameter of the tree. +The diameter of a binary tree is the length of the longest path between any two nodes in a tree. +This path may or may not pass through the root. +Note: The length of path between two nodes is represented by the number of nodes. + +Input: 3 + / \ + 1 4 + \ \ + 2 5 + / + 7 +Output: 6 +Output Explanation: [7, 2, 1, 3, 4, 5] is the diameter of the binary tree. + +Input: 5 + / \ + 3 6 + / \ + 2 4 + / \ + 1 8 +Output: 5 +Output Explanation: [1, 2, 3, 4, 8] is the diameter of the binary tree. + +========================================= +Traverse the tree and keep/return information about the longest/max branch and longest/max diameter. + Time Complexity: O(N) + Space Complexity: O(N) , because of the recursion stack (but this is if the tree is one branch), O(LogN) if the tree is balanced. +''' + + +############ +# Solution # +############ + +# import TreeNode class from tree_helpers.py +from tree_helpers import TreeNode + +def diameter(root): + return find_diameter(root)[1] + +def find_diameter(root): + ''' returns (max branch length, max diameter) ''' + if not root: + return 0, 0 + + # traverse left and right subtrees + left, right = find_diameter(root.left), find_diameter(root.right) + + # return the max branch from the left and right subtrees plus the current node + # and find the max diameter till now (using the current node and the max left and right subtree branches) + return max(left[0], right[0]) + 1, max(left[1], right[1], left[0] + right[0] + 1) + + +########### +# Testing # +########### + +# Test 1 +# Correct result => 6 +tree = TreeNode(3, TreeNode(1, None, TreeNode(2, TreeNode(7))), TreeNode(4, None, TreeNode(5))) +print(diameter(tree)) + +# Test 2 +# Correct result => 5 +tree = TreeNode(5, TreeNode(3, TreeNode(2, TreeNode(1)), TreeNode(4, None, TreeNode(8))), TreeNode(6)) +print(diameter(tree)) \ No newline at end of file