🔏
Tech
  • 🟢App aspects
    • Software architecture
      • Caching
      • Anti-patterns
      • System X-ability
      • Coupling
      • Event driven architecture
        • Command Query Responsibility Segregation (CQRS)
        • Change Data Capture (CDC)
      • Distributed transactions
      • App dev notes
        • Architecture MVP
      • TEMP. Check list
      • Hexagonal arch
      • Communication
        • REST vs messaging
        • gRPC
        • WebSocket
      • Load balancers
      • Storage limits
      • Event storming
    • Authentication
    • Deployment strategy
  • Databases
    • Classification
    • DB migration tools
    • PostreSQL
    • Decision guidance
    • Index
      • Hash indexes
      • SSTable, LSM-Trees
      • B-Tree
      • Engines, internals
    • Performance
  • System design
    • Interview preparation
      • Plan
        • Instagram
        • Tinder
        • Digital wallet
        • Dropbox
        • Live video streaming
        • Uber
        • Whatsup
        • Tiktok
        • Twitter
        • Proximity service
    • Algorithms
    • Acronyms
  • 🟢Programming languages
    • Java
      • Features
        • Field hiding
        • HashCode() and Equals()
        • Reference types
        • Pass by value
        • Atomic variables
      • Types
      • IO / NIO
        • Java NIO
          • Buffer
          • Channel
        • Java IO: Streams
          • Input streams
            • BufferedInputStream
            • DataInputStream
            • ObjectInputStream
            • FilterInputStream
            • ByteArrayInputStream
        • Java IO: Pipes
        • Java IO: Byte & Char Arrays
        • Java IO: Input Parsing
          • PushbackReader
          • StreamTokenizer
          • LineNumberReader
          • PushbackInputStream
        • System.in, System.out, System.error
        • Java IO: Files
          • FileReader
          • FileWriter
          • FileOutputStream
          • FileInputStream
      • Multithreading
        • Thread liveness
        • False sharing
        • Actor model
        • Singleton
        • Future, CompletableFuture
        • Semaphore
      • Coursera: parallel programming
      • Coursera: concurrent programming
      • Serialization
      • JVM internals
      • Features track
        • Java 8
      • Distributed programming
      • Network
      • Patterns
        • Command
      • Garbage Collectors
        • GC Types
        • How GC works
        • Tools for GC
    • Kotlin
      • Scope functions
      • Inline value classes
      • Coroutines
      • Effective Kotlin
    • Javascript
      • Javascript vs Java
      • TypeScript
    • SQL
      • select for update
    • Python
      • __init.py__
  • OS components
    • Network
      • TCP/IP model
        • IP address in action
      • OSI model
  • 🟢Specifications
    • JAX-RS
    • REST
      • Multi part
  • 🟢Protocols
    • HTTP
    • OAuth 2.0
    • LDAP
    • SAML
  • 🟢Testing
    • Selenium anatomy
    • Testcafe
  • 🟢Tools
    • JDBC
      • Connection pool
    • Gradle
    • vim
    • git
    • IntelliJ Idea
    • Elastic search
    • Docker
    • Terraform
    • CDK
    • Argo CD
      • app-of-app setup
    • OpenTelemetry
    • Prometheus
    • Kafka
      • Consumer lag
  • 🟢CI
    • CircleCi
  • 🟢Platforms
    • AWS
      • VPC
      • EC2
      • RDS
      • S3
      • IAM
      • CloudWatch
      • CloudTrail
      • ELB
      • SNS
      • Route 53
      • CloudFront
      • Athena
      • EKS
    • Kubernetes
      • Networking
      • RBAC
      • Architecture
      • Pod
        • Resources
      • How to try
      • Kubectl
      • Service
      • Tooling
        • ArgoCD
        • Helm
        • Istio
    • GraalVM
    • Node.js
    • Camunda
      • Service tasks
      • Transactions
      • Performance
      • How it executes
  • 🟢Frameworks
    • Hibernate
      • JPA vs Spring Data
    • Micronaut
    • Spring
      • Security
      • JDBC, JPA, Hibernate
      • Transactions
      • Servlet containers, clients
  • 🟢Awesome
    • Нейробиология
    • Backend
      • System design
    • DevOps
    • Data
    • AI
    • Frontend
    • Mobile
    • Testing
    • Mac
    • Books & courses
      • Path: Java Concurrency
    • Algorithms
      • Competitive programming
    • Processes
    • Finance
    • Electronics
  • 🟢Electronics
    • Arduino
    • IoT
  • Artificial intelligence
    • Artificial Intelligence (AI)
  • 🚀Performance
    • BE
  • 📘Computer science
    • Data structures
      • Array
      • String
      • LinkedList
      • Tree
    • Algorithms
      • HowTo algorithms for interview
  • 🕸️Web dev (Frontend)
    • Trends
    • Web (to change)
  • 📈Data science
    • Time series
Powered by GitBook
On this page
  • Big O
  • Time complexity
  • Space complexity
  • Iteration over strings (Перебор строк)
  • Permutation
  • Correct brace sequence
  • Implementation of correct brace sequence using recursion
  • Binary search
  • Template 1
  • Breadth first search
  • Depth first search (DFS)
  • Dijkstra's Algorithm
  • Logic
  • Dynamic connectivity problem
  • Union find
  • Quick-find algorithm (eager approach)
  • Quick-union (lazy approach)
  • Quick-union (weighted)
  • Quick-union with path compression
  • Percolation
  • Useful math
  • Explained tasks

Was this helpful?

  1. Computer science

Algorithms

PreviousTreeNextHowTo algorithms for interview

Last updated 1 year ago

Was this helpful?

Disclaimer: current page is not for educational purposes, it has a intention of notes for myself about algorithms.

Big O

Asymptotic runtime.

Time complexity

BigTheta is a merge of BigO and BigOmega =>

the algorithm is BigTheta(N) if it is O(N) && BigOmega(N).

In industry BigO is close to BigTheta => the tightest description of the time.

The way to describe bigO for particular input:

  • Best case (not useful)

  • Worst case

  • expected case (useful to consider as worst, but sometimes.

Space complexity

To create a array n => O(n) space

To create two-dimentional array n*n => O(n2)O(n^2) O(n2) space

What affects: data structure size and call stack size.

Iteration over strings (Перебор строк)

Lexicographical order: (abc < acb) Strict definition:

s < t  
s[i] = t[i], i=0,1..k-1, s[k]<t[k]

For three chars 'a', 'b', 'c' the code to produce all combinations in lexicographical order is:

for (char i = 'a'; i <= 'c'; i++) {
    for (char j = 'a'; j <= 'c'; j++) {
        for (char k = 'a'; k <= 'c'; k++) {
            System.out.println(i+""+j+k);
        }
    }
}
Result printout

aaa aab aac aba abb abc aca acb acc baa bab bac bba bbb bbc bca bcb bcc caa cab cac cba cbb cbc cca ccb ccc

In more general case, when it is required to iterate over all strings with length n which consist of first m chars of alphabet. The task can be reformulated differently: Output all sequences with a length on n which consist of numbers from 0 to m.

private static Integer[] array;
private static Integer m = 3; // max integer in the sequence
private static Integer n = 3; // length of the sequence

public static void main(String[] args) {
    array = new Integer[m];
    rec(0);
}

private static void rec(int idx) {
    if (idx == n) {
        Arrays.stream(array).forEach(System.out::print);
        System.out.println();
        return;
    }
    for (int i = 1; i <= m; i++) {
        array[idx]=i;
        rec(idx+1);
    }
}
111
112
113
121
...
333

Permutation

All sequences of integers from 1 to n, where each integer is used only once. Such sequence is called - permutation.

public class IntegerPermutation {
    static int size = 3;
    static Integer[] array = new Integer[size];
    static Boolean[] used = new Boolean[size+1];

    public static void main(String[] args) {
        initUsed();
        rec(0);
    }

    private static void rec(int idx) {
        if (idx == size) {
            Arrays.stream(array).forEach(System.out::print);
            System.out.println();
        }
        for (int i = 1; i<=size; i++) {
            if (used[i] == true) {
                continue;
            }
            used[i] = true;
            array[idx] = i;
            rec(idx+1);
            used[i] = false;
        }
    }

    private static void initUsed() {
        for (int i=0; i<size+1; i++){
            used[i]=false;
        }
    }
}

Array used is needed to restrict having permutation with same integers. Output is:

123
132
213
231
312
321

Correct brace sequence

Lets consider a correct brace sequence from Math point of view. E.g. ((())), ()()(), (())(). One of implementations:

boolean isCorrect(String s) {
    int balance = 0;
    for (int i=0; i<s.length(); i++) {
        if (s.charAt(i) == '(') {
            balance++;
        } else {
            balance--;
        }
        if (balance<0){
            return false;
        }
    }
    return (balance == 0);
}

Also there is a well-known implementation of this algorithm with a stack.

Implementation of correct brace sequence using recursion

public class IsBraceSequencedCorrectRecursion {
	
	private static int n = 2;
	static char[] s = new char[2*n];
	
	public static void main(String[] args) {
		rec(0,0);
	}
	
	private static void rec(int idx, int bal) {
		if (idx == 2*n) {
			if (bal == 0) {
				out();
			}
			return;
		}
		
		s[idx] = '(';
		rec(idx + 1, bal + 1);
		
		if (bal == 0) {
			return;
		}
		
		s[idx] = ')';
		rec(idx + 1, bal - 1);
	}
	
	
	private static void out() {
		new String(s).chars().mapToObj(i->(char)i).forEach(System.out::print);
		System.out.println("");
	}
}

Binary search

time efficiency O(log(n)). (base of logarithm is 2, not 10.)

3 parts of successful binary search:

  • pre-processing. Sort if collection is not sorted.

  • Binary search. Using loop or recursion divide search space in half each comparison.

  • post-processing. Determine viable candidates in remaining space.

Template 1

// Template #1 
int binarySearch(int[] nums, int target){
  if(nums == null || nums.length == 0)
    return -1;

  int left = 0, right = nums.length - 1;
  while(left <= right){
    // Prevent (left + right) overflow
    int mid = left + (right - left) / 2;
    
    if (nums[mid] == target) {
      return mid; 
    } else if (nums[mid] < target) { 
      left = mid + 1; 
    } else { 
      right = mid - 1; 
    }
  }

  // End Condition: left > right
  return -1;
}

Distinguishing Syntax:

  • Initial Condition: left = 0, right = length-1

  • Termination: left > right

  • Searching Left: right = mid-1

  • Searching Right: left = mid+1

Another template

Suppose we have a search space. It could be an array, a range, etc. Usually it's sorted in ascending order. For most tasks, we can transform the requirement into the following generalized form:

Minimize k , s.t. condition(k) is True

The following code is the most generalised binary search template:

def binary_search(array) -> int:
    def condition(value) -> bool:
        pass

    left, right = min(search_space), max(search_space) # could be [0, n], [1, n] etc. Depends on problem
    while left < right:
        mid = left + (right - left) // 2
        if condition(mid):
            right = mid
        else:
            left = mid + 1
    return left

Breadth first search

Used to explore nodes and edges of the graph.

Time complexity O(Verticies + Edges).

Finding shortest path on unweighted graphs.

Depth first search (DFS)

Goes deep.

Dijkstra's Algorithm

The basic goal of the algorithm is to determine the shortest path between a starting node, and the rest of the graph. This is a greedy algorithm.

Logic

  • Settled nodes are the ones with a known minimum distance from the source.

  • The unsettled nodes set gathers nodes that we can reach from the source, but we don't know the minimum distance from the starting node.

Here's a list of steps to follow in order to solve the SPP with Dijkstra:

  • Set distance to startNode to zero.

  • Set all other distances to an infinite value.

  • We add the startNode to the unsettled nodes set.

  • While the unsettled nodes set is not empty we:

    • Choose an evaluation node from the unsettled nodes set, the evaluation node should be the one with the lowest distance from the source.

    • Calculate new distances to direct neighbours by keeping the lowest distance at each evaluation.

    • Add neighbours that are not yet settled to the unsettled nodes set.

These steps can be aggregated into two stages, Initialisation and Evaluation.

Dynamic connectivity problem

Union find

This is an algorithm which answers a question whether two nodes are connected to each other (see dynamic connectivity problem).

  • Find query

    • check if two objects are in the same component

  • Union command

    • Replace components containing two objects with their union

public class UnionFind {
    // init UF data structure with N objects (O to N-1)
    UnionFind(int N);
    
    // add connection between p and q
    void union(int p, int q);
    
    // are p and q in the same component?
    boolean connected(int p, int q);
}

Quick-find algorithm (eager approach)

algorithm

init

union

find

quick-find

N

N (too slow)

1

quick-union

N

N

N

quick-union (weighted)

N

logN

logN

Data structure:

  • Integer array id[] of size N

  • Interpretation: p and q are connected iff (if and only if) they have the same id

Find. Check if p and q have the same id. Union. To merge components containing p and q change all entries whose id equals id[p] to id[q].

public class QuickFindUF {
    
    private int[] id;
    
    // set id of each object to itself. 
    // initially all objects are in diff components
    // N array accesses
    public QuickFindUF(int N) {
        id = new int[N];
        for (int i=0; i<N; i++) {
            id[i]=i;        
        }    
    }
    
    // check whether p and q are in the same component
    // 2 array accesses
    public boolean connected(int p, int q) {
        return id[p] == id[q];
    }
    
    // change all entries with id[p] to id[q]
    // at most 2N+2 array accesses
    public void union(int p, int q) {
        int pid = id[p];
        int qid = id[q];
        for (int i=0; i<id.length; i++) {
            if (id[i] == pid) {
                id[i] = qid;
            }        
        }
    }
}

Quick-union (lazy approach)

Data structure:

  • Integer array id[] of size N

  • Interpretation: id[i] is parent of i

Find. Check if p and q have the same root.

Union. To merge components containing p and q - set the id of p's root to the id of q's root. (e.g. union(3,4) - 3 goes under 4. The order is important)

public class QuickUnionUF {

    private int[] id;

    // set id of each object to itself. 
    // initially all objects are in diff components
    // N array accesses
    public QuickUnionUF(int N) {
        id = new int[N];
        for (int i=0; i<N; i++) {
            id[i]=i;        
        }    
    } 
    
    private int root(int i) {
        while (i != id[i]) {
            i = id[i];
        }
        return i;
    }
    
    public boolean connected(int p, int q) {
        return root(p) == root(q);
    }
    
    public void union(int p, int q) {
        int i = root(p);
        int j = root(q);
        id[i] = j;
    }    
}

Quick-union (weighted)

  • Modify quick-union to avoid tall trees

  • Keep track of size of each tree

  • Balance by linking root of smaller tree to root of larger tree

public class QuickUnionWeightedUF {

    private int[] id;
    // num of items  in the tree rooted at i
    private int[] sz;
    
    // set id of each object to itself. 
    // initially all objects are in diff components
    // N array accesses
    public QuickUnionUF(int N) {
        id = new int[N];
        for (int i=0; i<N; i++) {
            id[i]=i;        
        }    
    } 
    
    private int root(int i) {
        while (i != id[i]) {
            i = id[i];
        }
        return i;
    }
    
    public boolean connected(int p, int q) {
        return root(p) == root(q);
    }
    
    public void union(int p, int q) {
        int i = root(p);
        int j = root(q);
        if (i == j) return;
        if (sz[i] < sz[j]) {
            id[i] = j; 
            sz[j] += sz[i];
        } else {
            id[j] = i;
            sz[i] += sz[j];
        }   
    }    
}

Depth of any node x is at most lgN (base 2 logarithm).

Quick-union with path compression

Percolation

Useful math

1+2+3+...+n=n(n+1)21 + 2 + 3 + ... + n = {n(n+1) \over 2 }1+2+3+...+n=2n(n+1)​

20+21+22+...+2n=2n+1−12^0 + 2^1 + 2^2 + ... + 2^n = 2^{n+1}-120+21+22+...+2n=2n+1−1

log⁡10x=log⁡2xlog⁡210\log_{10} x = {\log_2 x \over \log_2 10}log10​x=log2​10log2​x​ => for BigO notation it is not important what is a base for logarithm

Explained tasks

When you see a problem where number of elements is halved each time, it will be most likely O(logN)O(logN)O(logN) runtime.

When recursive function makes multiple calls =often=> O(branchesdepth)O(branches^{depth})O(branchesdepth)

Below template inspired by known article

This is non optimal solution. The better is .

There is a better solution . With explanation is the .

. Check that moves right, left, down, up have equal number and you return to the same initial point.

.

Jewels and stones.

📘
ℹ️
ℹ️
⭐
LINK
First explanation
Shortest path from source to all vertices in given graph + code
Really nice explanation of Dijkstra`s algorithm
Really good article about algorithm + java implementation
First unique character in a string (LeetCode)
first non repeating character
Single number among others being twice
here
Is a number a power of two
here
comment
Robot return to origin
Return a missing number in the monotonically increasing array.
Sum of the tree path matches an expected value
Return element from array and return new length.
Lowest common ancestor of BST
How many chars from one string are contained in another string.
Most frequent word in string except banned words
K closest points to origin
Best time to buy and sell stock II
Climbing stairs