HOME

Weikai Mao

Weikai Mao

maoweikai123@outlook.com

Categories:

© 2024

Implementation of Basic Algorithms in Python


Return the First Founded

def binarySearch(seq: list, target: int) -> int:
    """
    This function returns the index of the first founded element in seq that equals to target.
    """
    if target < seq[0] or target > seq[-1]:
        return -1

    low = 0
    upp = len(seq) - 1

    while low <= upp:
        mid = (low + upp) // 2
        if seq[mid] < target:
            low = mid + 1
        elif seq[mid] > target:
            upp = mid - 1
        else:
            return mid

    return -1

Return the Lowest Index

def binarySearch(seq: list, target: int) -> int:
    """
    If there are more than one elements in seq that equal to target, this function returns the lowest index.
    """
    if target < seq[0] or target > seq[-1]:
        return -1

    low = 0
    upp = len(seq) - 1

    while low < upp:
        mid = (low + upp) // 2
        if seq[mid] < target:
            low = mid + 1
        else:
            upp = mid
    
    if seq[low] == target:
        return low
    else:
        return -1

Return the Highest Index

def binarySearch(seq: list, target: int) -> int:
    """
    If there are more than one elements in seq that equal to target, this function returns the highest index.
    """
    if target < seq[0] or target > seq[-1]:
        return -1

    low = 0
    upp = len(seq) - 1

    while low < upp:
        mid = (low + upp) // 2 + 1
        if seq[mid] > target:
            upp = mid - 1
        else:
            low = mid
    
    if seq[low] == target:
        return low
    else:
        return -1

Sorting Algorithms

Merge Sort on Linked List

class ListNode(object):
    def __init__(self, x):
        self.val = x
        self.next = None
        
class Solution:
    def sortList(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head
        slow, fast = head, head.next
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        second = slow.next
        slow.next = None
        l = self.sortList(head)
        r = self.sortList(second)
        return self.merge(l, r)
    
    def merge(self, l: ListNode, r: ListNode) -> ListNode:
        if not l or not r:
            return l or r
        if l.val > r.val:
            l, r = r, l
        # get the return node "head"
        head = pre = l
        l = l.next
        while l and r:
            if l.val < r.val:
                l = l.next
            else:
                nxt = pre.next
                pre.next = r
                tmp = r.next
                r.next = nxt
                r = tmp
            pre = pre.next
        # l and r at least one is None
        pre.next = l or r
        return head

Quick-Sort on Array

def partition(arr: list, start: int, end: int) -> int:
    """returns the partition index"""
    j = start
    for i in range(start, end):
        # use arr[end] as pivot
        if arr[i] < arr[end]:
            # swap to make the elements in arr[:j] be smaller than pivot
            arr[j], arr[i] = arr[i], arr[j]
            j += 1
    arr[j], arr[end] = arr[end], arr[j]
    # now the elements in arr[:j] are smaller than arr[j],
    # and the elements in arr[j+1:] are larger than arr[j]
    return j

# another partition method
def partition(arr: list, start: int, end: int) -> int:
    """returns the partition index"""
    i, j = start, end # left index i, and right index j
    # use arr[end] as pivot
    while i < j:
        # find i that makes arr[i] >= pivot
        while i < j and arr[i] < arr[end]:
            i += 1
        # find j that makes arr[j] < pivot
        while i < j and arr[j] >= arr[end]:
            j -= 1
        # swap left large element arr[i] and right small element arr[j]
        arr[i], arr[j] = arr[j], arr[i]
    # now i == j, swap pivot and arr[j]
    arr[end], arr[j] = arr[j], arr[end]
    # now the elements in arr[:j] are < arr[j],
    # and the elements in arr[j+1:] are >= arr[j]
    return j
def quicksort(arr: list) -> list:
    """in-place quicksort function"""
    
    def helper(arr: list, start: int, end: int) -> None:
        """recursive helper"""
        if start >= end: return None
        j = partition(arr, start, end) # partition index
        helper(arr, start, j-1) # sort the left part arr[:j]
        helper(arr, j+1, end) # sort the right part arr[j+1:]
    
    helper(arr, 0, len(arr)-1)

Quick Sort on Linked List

# Definition for singly-linked list.
class ListNode(object):
    def __init__(self, x):
        self.val = x
        self.next = None

class Solution(object):
    def sortList(self, node):
        if not node or not node.next:
            return node
        pivot = node
        node = node.next
        pivot.next = None
        left = dummyLeft = ListNode(None)
        right = dummyRight = ListNode(None)
        
        while node:
            if node.val < pivot.val:
                left.next = node 
                left = left.next
            else:
                right.next = node
                right = right.next
            node = node.next
        left.next = None
        right.next = None
        
        headLeft = self.sortList(dummyLeft.next)
        
        if not headLeft:
            headLeft = pivot
        else:
            tailLeft = headLeft
            while tailLeft.next: 
                tailLeft = tailLeft.next
            tailLeft.next = pivot
        pivot.next = self.sortList(dummyRight.next) 
        
        return headLeft

Counting Sort on Array

def countingSort(input: list, k: int) -> list:
    count = k * [0]
    for x in input:
        count[key(x)] += 1
    for j in range(1, k):
        count[j] += count[j-1]
    output = len(input) * [None]
    for x in input[::-1]:
        count[key(x)] -= 1
        output[count[key(x)]] = x
    return output

Radix Sort on Array

# use the bucket sort internally.
def radixSort(self, nums: list) -> int:
    # Convert nums list to reversed bit array list
    nums = [bin(num)[2:][::-1] for num in nums] 
    for i in range(max(map(len, nums))):
        nums0 = [x for x in nums if i >= len(x) or x[i] == '0']
        nums1 = [x for x in nums if i < len(x) and x[i] == '1']
        # it will only have two buckets (0, 1) in radix sort.
        nums = nums0 + nums1
    # convert the number back to base 10 integer. 
    output = [int(num[::-1], 2) for num in nums]
    return output

Traversal

Binary Tree Traversals

  • Preorder: node -> left -> right;

  • Inorder: left -> node -> right;

  • Postorder: left -> right -> node.

# definition for a binary tree node.
class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None

# traverse a binary tree recursively.
def traverseTreeRecursively(self, root: TreeNode) -> tuple:
    preorder, inorder, postorder = list(), list(), list()
    def recursion(node: TreeNode):
        if node:
            preorder.append(node.val) # preorder traversal
            recursion(node.left)
            inorder.append(node.val) # inorder traversal
            recursion(node.right)
            postorder.append(node.val) # postorder traversal

        recursion(root)
        return preorder, inorder, postorder

Combinations by BFS or DFS

# Example input: [[1, 2], [3, 4, 5], [6]].
# Example output: [[1, 3, 6], [1, 4, 6], [1, 5, 6], [2, 3, 6], [2, 4, 6], [2, 5, 6]].
# method1: BFS
from collections import deque
def BFS(params):
    ans = deque([[]])
    for i, param in enumerate(params):
        while len(ans[0]) == i:
            temp = ans.popleft()
            for p in param:
                ans.append(temp + [p])
    return ans
# method2: DFS
def DFS(params):
    ans = list()
    def helper(path: list):
        if len(path) == len(params):
            ans.append(path)
            return None
        for p in params[len(path)]:
            helper(path + [p])
    helper([])
    return ans


Comments