Problems Flashcards

1
Q

problems.leetcode_20_Valid Parentheses — Given a string s containing just the characters
‘(‘, ‘)’, ‘{‘, ‘}’, ‘[’ and ‘]’, determine if the input string is valid.

🎯 assert Solution().isValid(s=”()”) == True
🎯 assert Solution().isValid(s=”()[]{}”) == True
🎯 assert Solution().isValid(s=”(]”) == False
🎯 assert Solution().isValid(s=”([)]”) == False
🎯 assert Solution().isValid(s=”{[]}”) == True
🎯 assert Solution().isValid(s=”({{{{}}}))”) == False
🎯 assert Solution().isValid(s=”((“) == False
🎯 assert Solution().isValid(s=”){“) == False
🎯 assert Solution().isValid(s=”(}{)”) == False
🎯 assert Solution().isValid(s=”([}}])”) == False
🎯 assert Solution().isValid(s=”(([]){})”) == True

A
class Solution:
    def isValid(self, s: str) -> bool:
        answers = []
        for i in s:
            if i == '(' or i == '[' or i == '{':
                answers.append(i)
            elif len(answers):
                pop_item = answers.pop()
                if (pop_item == '(' and i != ')') or (pop_item == '[' and i != ']') or (pop_item == '{' and i != '}'):
                    return False
            else:
                return False

        if len(answers):
            return False
        else:
            return True
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

problems.leetcode_14_Longest Common Prefix — Write a function to find the longest common prefix string amongst an array of strings. If there is no common prefix, return an empty string. “”

A
class Solution:
    def longestCommonPrefix(self, strs) -> str:
        commonPrefix = ''
        for chars in zip(*strs):
            print(chars)
# Если есть повторяющиеся, то-есть длина больше 1, тогда неправильно и включается елс
            if len(set(chars)) == 1:
                commonPrefix += chars[0]
            else:
                break
        return commonPrefix
class Solution:
    def longestCommonPrefix(self, strs) -> str:
        if not isinstance(strs[0], str):
            return ""

        if len(strs) == 0:
            return ""

        if len(strs) == 1 or not strs[0].isalpha():
            return strs[0]

        biggest = max([len(i) for i in strs])
        test = biggest
        ind = -1
        prefix = ""

        while biggest > 0:
            ind = ind + 1
            biggest -= 1

            leters = [i[ind] for i in strs if ind < len(i)]

            if leters.count(leters[0]) == len(strs):
                prefix += leters[0]

                if len(prefix) == test:
                    return prefix
            else:
                if len(prefix) < 1:
                    return ""
                return prefix

if \_\_name\_\_ == '\_\_main\_\_':
    assert Solution().longestCommonPrefix(strs=["flower","flow","flight"]) == "fl"
    assert Solution().longestCommonPrefix(strs=["dog","racecar","car"]) == ""
    assert Solution().longestCommonPrefix(strs=[""]) == ""
    assert Solution().longestCommonPrefix(strs=["a"]) == "a"
    assert Solution().longestCommonPrefix(strs=[["",""]]) == ""
    assert Solution().longestCommonPrefix(strs=["flower","flower","flower","flower"]) == "flower"
    assert Solution().longestCommonPrefix(strs=["c","c"]) == "c"
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

problems.leetcode_141_Linked List Cycle — Given head, the head of a linked list, determine if the linked list has a cycle in it. There is a cycle in a linked list if there is some node in the list that can be reached again by continuously following the next pointer. Internally, pos is used to denote the index of the node that tail’s next pointer is connected to. Note that pos is not passed as a parameter.

🎯 Input: head = [3,2,0,-4], pos = 1 | Output: true
🎯 Input: head = [1,2], pos = 0 | Output: true
🎯 Input: head = [1], pos = -1 | Output: false

A
class Solution:
    def hasCycle(self, head: Optional[ListNode]) -> bool:
        slow, fast = head, head

        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            if slow == fast:
                return True
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

problems.leetcode_1_Two Sum — Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target. You may assume that each input would have exactly one solution, and you may not use the same element twice.

A
class Solution:
    def twoSum(self, nums, target: int):
        values = {}
        for idx, value in enumerate(nums):
            if target - value in values:
                return [values[target - value], idx]
            else:
                values[value] = idx

if \_\_name\_\_ == '\_\_main\_\_':
    assert Solution().twoSum(nums=[2, 7, 11, 15], target=9) == [0, 1]
    assert Solution().twoSum(nums=[3, 2, 4], target=6) == [1, 2]
    assert Solution().twoSum(nums=[3, 3], target=6) == [0, 1]
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

problems.leetcode_234_Palindrome Linked List — Given the head of a singly linked list, return true if it is a palindrome or false otherwise.

🎯 Input: head = [1,2,2,1] 👉 Output: true
🎯 Input: head = [1,2] 👉 Output: false

A
class Solution:
    def isPalindrome(self, head: Optional[ListNode]) -> bool:
        count = []
        while head:
            count.append(head.val)
            head=head.next
        return True if count == count[::-1] else False
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

problems.leetcode_70_Climbing Stairs — You are climbing a staircase. It takes n steps to
reach the top. Each time you can either climb 1 or 2 steps. In how many distinct ways can you
climb to the top?

🎯 Input: n = 2 👉 Output: 2 (1. 1 step + 1 | step 2. 2 steps)
🎯 Input: n = 2 👉 Output: 2 (1. 1 step + 1 step | 2. 2 steps)

A
from functools import lru_cache

class Solution:
    def climbStairs(self, n: int) -> int:
        @lru_cache(maxsize=32)
        def fib(n):
            if n < 2:
                return n
            return fib(n - 1) + fib(n - 2)
        return fib(n+1)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

problems.leetcode_102_Binary Tree Level Order Traversal — Given the root of a binary tree,
check whether it is a mirror of itself (i.e., symmetric around its center).

🎯 Input: root = [1,2,2,3,4,4,3] 👉 Output: true
🎯 Input: root = [1,2,2,null,3,null,3] 👉 Output: false

A
# Definition for a binary tree node.
# class TreeNode:
#     def \_\_init\_\_(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution:
    def isSymmetric(self, root: Optional[TreeNode]) -> bool:
        if root == None:
            return True
        else:
            return self.sym(root, root)

    def sym(self, left, right):
        if left is None and right is None:
            return True
        elif left is None or right == None:
            return False
        else:
            return left.val == right.val and self.sym(left.left, right.right) and self.sym(left.right, right.left)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

problems.leetcode_161_One Edit Distance — Given the heads of two singly linked-lists headA
and headB, return the node at which the two lists intersect. If the two linked lists have no intersection at all, return null.

🎯 Input: intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
🎯 Output: Intersected at ‘8’

A
# Definition for singly-linked list.
# class ListNode:
#     def \_\_init\_\_(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
        A, B = headA, headB
        visit = set()

        while A:
            visit.add(A)
            A = A.next

        while B:
            if B in visit:
                return B
            B = B.next
            
        return None
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

problems.leetcode_121_Best Time to Buy and Sell Stock — You are given an array prices where
prices[i] is the price of a given stock on the ith day. You want to maximize your profit by
choosing a single day to buy one stock and choosing a different day in the future to sell that
stock. Return the maximum profit you can achieve from this transaction. If you cannot achieve
any profit, return 0.

🎯 Input: prices = [7,1,5,3,6,4] 👉 Output: 5
🎯 Input: prices = [7,6,4,3,1] 👉 Output: 0

A
class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        profit = 0
        min_p = float("inf")

        for i in prices:
            min_p = min(min_p, i)
            profit = max(profit, i - min_p)

        return profit
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

problems.leetcode_543_Diameter of Binary Tree — Given the root of a binary tree, return the length of the diameter of the tree. The length of a path between two nodes is represented by the number of edges between them.

🎯 Input: root = [1,2,3,4,5] 👉 Output: 3 👉 Explanation: 3 is the length of the path [4,2,1,3] or [5,2,1,3].
🎯 Input: root = [1,2] 👉 Output: 1

A
# Definition for a binary tree node.
# class TreeNode:
#     def \_\_init\_\_(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int:
        answer = [0]

        def l_r_root(root):
            if not root:
                return - 1
            left = l_r_root(root.left)
            right = l_r_root(root.right)
            answer[0] = max(answer[0], 2 + left + right)
            
            return 1 + max(left, right)

        l_r_root(root)
        return answer[0]
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

problems.leetcode_21_Merge Two Sorted Lists — You are given the heads of two sorted linked
lists list1 and list2. Merge the two lists in a one sorted list. The list should be made by splicing together the nodes of the first two lists. Return the head of the merged linked list.

🎯Input: list1 = [1,2,4], list2 = [1,3,4] 👉 Output: [1,1,2,3,4,4]
🎯Input: list1 = [], list2 = [] 👉 Output: []
🎯Input: list1 = [], list2 = [0] 👉 Output: [0]

A
# Definition for singly-linked list.
# class ListNode:
#     def \_\_init\_\_(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
        dummy = ListNode()
        new = dummy

        while list1 and list2:
            if list1.val < list2.val:
                new.next = list1
                list1 = list1.next
            else:
                new.next = list2
                list2 = list2.next
            new = new.next

        if list1:
            new.next = list1
        elif list2:
            new.next = list2
        
        return dummy.next
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Фабричный метод

A

Любой метод который создает обьект.

from enum import Enum
from math import *

class CoordinateSystem(Enum):
    CARTESIAN = 1
    POLAR = 2

class Point:
    def \_\_init\_\_(self, x, y):
        self.x = x
        self.y = y

    def \_\_str\_\_(self):
        return f"{self.x}, {self.y}"

    @staticmethod
    def new_cartesian(x, y):
        return Point(x, y)

    @staticmethod
    def new_polar_point(rho, theta):
        return Point(rho * cos(theta), rho * sin(theta))

if \_\_name\_\_ == '\_\_main\_\_':
    p = Point.new_polar_point(1, 2)
    print(p)
👉 -0.4161468365471424, 0.9092974268256817
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Строитель

A

это порождающий паттерн проектирования, который позволяет создавать сложные объекты пошагово. Строитель даёт возможность использовать один и тот же код строительства для получения разных представлений объектов.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Прототип

A

это порождающий паттерн проектирования, который позволяет копировать объекты, не вдаваясь в подробности их реализации. (можно использовать deepcopy)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Итератор

A

это поведенческий паттерн проектирования, который даёт возможность последовательно обходить элементы составных объектов, не раскрывая их внутреннего представления.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Компоновщик

A

это структурный паттерн проектирования, который позволяет сгруппировать множество объектов в древовидную структуру, а затем работать с ней так, как будто это единичный
объект.

17
Q

Aбстрактная фабрика

A

это порождающий паттерн проектирования, который позволяет создавать
семейства связанных объектов, не привязываясь к конкретным классам создаваемых объектов.

18
Q

Синглтон

A

Singleton - это класс, который позволяет создавать только один его экземпляр и предоставляет доступ к этому созданному экземпляру.

19
Q

Bubble Sort Algorithm

🎯 Time complexity = O(n²) - The inner loop runs at least n times. Therefore, the entire operation will take at least n² time.

🎯 Space Complexity = O(1) - No additional memory is utilised as the interchange of items occurs on the original array.

A

If the item you’re looking at is greater than its adjacent value, then swap them

This algorithm is based on the comparison in which there is repeated swapping of adjacent elements if they are in an incorrect order.

def bubble_sort(array):
    n = len(array)

    for i in range(n):
        # Terminate early if there's nothing left to sort
        already_sorted = True

        for j in range(n - i - 1):
            if array[j] > array[j + 1]:
                array[j], array[j + 1] = array[j + 1], array[j]

Since you had to swap two elements, set the `already_sorted` flag to `False` so the
                already_sorted = False

If there were no swaps during the last iteration, the array is already sorted, and you can terminate
        if already_sorted:
            break

    return array

print(bubble_sort([3, 5, 6, 1, 7, 2, 4]))
👉 [1, 2, 3, 4, 5, 6, 7]
20
Q

Merge Sort Algorithm

🎯 Time complexity = O(n*log(n)) - Divide and conquer sorting algorithms have this time
complexity.⁴ This complexity is the worst-case scenario.

🎯 Space complexity = O(n) - suggesting that the memory allocation grows no faster than a
constant multiple of the dataset size³, i.e. k*N.

A

need to decide whether to get the next element from the first or the second array

very efficient sorting algorithm. Divides the Array into two halves, sorts them, and then combines them, a powerful algorithmic technique used to solve complex problems.

def merge(left, right):
    # If the first array is empty, then nothing needs
    # to be merged, and you can return the second array as the result

    if len(left) == 0:
        return right

    # If the second array is empty, then nothing needs
    # to be merged, and you can return the first array as the result

    if len(right) == 0:
        return left

    result = []
    index_left = index_right = 0

Now go through both arrays until all the elements make it into the resultant array
    while len(result) < len(left) + len(right):

The elements need to be sorted to add them to the resultant array, so you 

        if left[index_left] <= right[index_right]:
            result.append(left[index_left])
            index_left += 1
        else:
            result.append(right[index_right])
            index_right += 1

If you reach the end of either array, then you can add the remaining 
# the other array to the result and break the loop elements from

        if index_right == len(right):
            result += left[index_left:]
            break

        if index_left == len(left):
            result += right[index_right:]
            break

    return result

def merge_sort(array):
# If the input array contains fewer than two elements, then return it as the result of the function

    if len(array) < 2:
        return array

    midpoint = len(array) // 2

Sort the array by recursively splitting the input into two equal halves, 
# sorting each half and merging them together into the final result

    return merge(left=merge_sort(array[:midpoint]), right=merge_sort(array[midpoint:]))
21
Q

Quicksort Algorithm

🎯 Time complexity = O(n*log(n)) - Like merge sort, this notation is determined from the divide and conquer nature or quicksort. A worst-case scenario is O(n²).

A

applies the divide-and-conquer principle to divide the input array into two lists, the first with small items and the second with large items. The algorithm then sorts both lists recursively until the resultant list is completely sorted.

from random import randint

def quicksort(array):
    if len(array) < 2:
        return array

    low, same, high = [], [], []

    # Select your `pivot` element randomly
    pivot = array[randint(0, len(array) - 1)]

    for item in array:
# Elements that are smaller than the `pivot` go to the `low` list. Elements that are larger than
# `pivot` go to the `high` list. Elements that are equal to `pivot` go to the `same` list.

        if item < pivot:
            low.append(item)
        elif item == pivot:
            same.append(item)
        elif item > pivot:
            high.append(item)

    return quicksort(low) + same + quicksort(high)

print(quicksort([3, 2, 5, 7, 6, 1, 4]))
👉 [1, 2, 3, 4, 5, 6, 7]
22
Q

Insertion Sort Algorithm

🎯 Time complexity = O(n²). - The outer for loop runs approximately n times, and the inner while loop runs for a similar count.
🎯 Space Complexity = O(1). - Operations occur on the original array. Thus, additional memory requirements are not necessary.

A

This sorting begins with comparing and sorting the first two elements. Then, the third element is compared with the two previously sorted elements, and so on.

def insertion_sort(collection: list) -> list:
    for index, value in enumerate(collection[1:]):
        temp_index = index

        while index >= 0 and value < collection[index]:
            collection[index + 1] = collection[index]
            index -= 1
        if index != temp_index:
            collection[index + 1] = value
    return collection

print(insertion_sort([3, 2, 5, 7, 6, 1, 4]))
👉 [1, 2, 3, 4, 5, 6, 7]
23
Q

Selection Sort Algorithm

🎯 Time complexity = O(n²). n² - iterations are evident from the two nested for loops.
🎯 Space Complexity = O(1). - sorts happen in place; therefore, memory usage does not depend on the processing data.

A

This algorithm begins by finding the minimum value from a list of elements and puts it into a sorted list. The process is then repeated for each of the remaining unsorted elements in the list. The new element entering the sorted list is compared with its existing elements and placed in the correct position. The process goes on until all the elements are sorted.

def selection_sort(collection):
    length = len(collection)
    for i in range(length - 1):
        least = i
        for k in range(i + 1, length):
            if collection[k] < collection[least]:
                least = k
        if least != i:
            collection[least], collection[i] = (collection[i], collection[least])
    return collection

print(selection_sort([3, 2, 5, 7, 6, 1, 4]))
👉 [1, 2, 3, 4, 5, 6, 7]
24
Q

Binary Search Algorithm

🎯 Time complexity = O(log n). - as the search space is divided in half with each iteration, leading to a very efficient search.
🎯 Space Complexity = O(1). - as only a few constant variables are needed to keep track of the search.

A

is a search algorithm that works efficiently. It starts by comparing the middle element of the array with the target value. If the middle element is the target value,the search is complete. Otherwise, depending on whether the target is greater or less than the middle element, the search continues in the left or right half of the array. This process is repeated until the target value is found, or until it is determined that the target value is not present in the array.

def binary_search(arr, target):
    left = 0
    right = len(arr) - 1
    while left <= right:
        mid = (left + right) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return -1

arr = [2, 4, 6, 8, 10]
target = 6

print(binary_search(arr, target))
👉 2
25
Q

Heap Sort Algorithm

🎯 Time complexity = O(n log n) - the worst case, where n is the number of elements being sorted.
🎯 Space Complexity = O(1). - because the algorithm sorts the input list in place, without using any additional memory except for a constant amount of auxiliary space.

A

comparison-based sorting algorithm that involves building a binary heap data structure from the array being sorted, and then removing the maximum element from the heap and placing it at the end of the array. The heap is updated after each removal to maintain its heap property. The process repeats until the heap is empty and the array is sorted.

def heap_sort(arr):
    n = len(arr)
    for i in range(n // 2 - 1, -1, -1):
        heapify(arr, n, i)
    for i in range(n - 1, 0, -1):
        arr[i], arr[0] = arr[0], arr[i]
        heapify(arr, i, 0)

def heapify(arr, n, i):
    largest = i
    l = 2 * i + 1
    r = 2 * i + 2
    if l < n and arr[l] > arr[largest]:
        largest = l
    if r < n and arr[r] > arr[largest]:
        largest = r
    if largest != i:
        arr[i], arr[largest] = arr[largest], arr[i]
        heapify(arr, n, largest)
        
arr = [12, 11, 13, 5, 6, 7]
heap_sort(arr)

print("Sorted array is:", arr)
👉 [5, 6, 7, 11, 12, 13]
26
Q

Interpolation Search Algorithm

🎯 Time complexity = O(log log n) on average and O(n) in the worst case scenario.
🎯 Space Complexity = O(1). - algorithm uses a constant amount of extra space regardless of the size of the input data.

A

searching algorithm used to find a specific value in a sorted array. This algorithm works similar to Binary Search, but instead of dividing the search space into two equal parts, Interpolation Search uses an approximate position of the target value based on the value’s rank in the array.

def interpolation_search(arr, target):
    left, right = 0, len(arr) - 1
    while left <= right and target >= arr[left] and target <= arr[right]:
        pos = left + (target - arr[left]) * (right - left) // (arr[right] - arr[left])
        if arr[pos] == target:
            return pos
        elif arr[pos] < target:
            left = pos + 1
        else:
            right = pos - 1
    return -1

arr = [1, 3, 5, 7, 8, 10, 12]
target = 8
result = interpolation_search(arr, target)

print("Index of the target value:", result)
👉 Index of the target value: 4
27
Q

O(log n) - Logarithmic Time Complexity Algorithms

A

divide the input into smaller pieces and perform the same operation on each piece. Example: Binary search.

28
Q

O(n) - Linear Time Complexity Algorithms

A

have a number of operations proportional to the
size of the input.

29
Q

O(1) - Constant Time Complexity Algorithms

A

have a fixed number of operations, regardless of the size of the input. An example of an O(1) algorithm is accessing an element in an array by index.

30
Q

O(n log n) - Quasilinear Time Complexity Algorithms

A

faster than O(n^2) algorithms but slower O(n) than linear time algorithms. Example: merge sort.

31
Q

O(n^2) - Quadratic Time Complexity Algorithms

A

have a number of operations proportional to the square of the size of the input. Example: selection sort.

32
Q

O(2^n) - Exponential Time Complexity Algorithms

A

have a number of operations that doubles with each additional input element. Example: possible combinations.

O(2^n) - Exponential Time Complexity Algorithms

33
Q

O(n!) - Time Complexity Algorithms

A

number of operations grows factorialy with the size of the input. Typically considered to be the slowest among all the time complexities used in practice. Example: possible permutations or combinations of a given input.

Permutations: 
An algorithm to find all possible permutations of a set of n elements has a time complexity of O(n!). This is because the number of permutations grows factorially with the number of elements in the set. For example, if we have a set of 5 elements, there are 5! (i.e., 120) possible permutations.
Traveling Salesman Problem: 
The traveling salesman problem is a classic optimization problem that asks to find the shortest route that visits a set of cities and returns to the starting city. The brute-force solution to this problem has a time complexity of O(n!), as it involves checking all possible permutations of the cities. For example, if there are 10 cities, there are 10! (i.e., 3,628,800) possible routes to check.
34
Q

Refactoring

A

process of restructuring existing code without changing its external behavior. It is typically done to improve code readability, maintainability, and/or performance.

# Original code
def print_sum(numbers):
    total = 0
    for num in numbers:
        total += num
    print("The sum is:", total)

Refactored code
def calculate_sum(numbers):
    return sum(numbers)

def print_sum(numbers):
    print("The sum is:", calculate_sum(numbers))