数据结构和算法

DSA - 主页 DSA - 概述 DSA - 环境设置 DSA - 算法基础 DSA - 渐近分析

数据结构

DSA - 数据结构基础 DSA - 数据结构和类型 DSA - 数组数据结构

链接列表

DSA - 链接列表数据结构 DSA - 双向链接列表数据结构 DSA - 循环链表数据结构

堆栈 &队列

DSA - 堆栈数据结构 DSA - 表达式解析 DSA - 队列数据结构

搜索算法

DSA - 搜索算法 DSA - 线性搜索算法 DSA - 二分搜索算法 DSA - 插值搜索 DSA - 跳跃搜索算法 DSA - 指数搜索 DSA - 斐波那契搜索 DSA - 子列表搜索 DSA - 哈希表

排序算法

DSA - 排序算法 DSA - 冒泡排序算法 DSA - 插入排序算法 DSA - 选择排序算法 DSA - 归并排序算法 DSA - 希尔排序算法 DSA - 堆排序 DSA - 桶排序算法 DSA - 计数排序算法 DSA - 基数排序算法 DSA - 快速排序算法

图形数据结构

DSA - 图形数据结构 DSA - 深度优先遍历 DSA - 广度优先遍历 DSA - 生成树

树数据结构

DSA - 树数据结构 DSA - 树遍历 DSA - 二叉搜索树 DSA - AVL 树 DSA - 红黑树 DSA - B树 DSA - B+ 树 DSA - 伸展树 DSA - 尝试 DSA - 堆数据结构

递归

DSA - 递归算法 DSA - 使用递归的汉诺塔 DSA - 使用递归的斐波那契数列

分而治之

DSA - 分而治之 DSA - 最大最小问题 DSA - 施特拉森矩阵乘法 DSA - Karatsuba 算法

贪婪算法

DSA - 贪婪算法 DSA - 旅行商问题(贪婪方法) DSA - Prim 最小生成树 DSA - Kruskal 最小生成树 DSA - Dijkstra 最短路径算法 DSA - 地图着色算法 DSA - 分数背包问题 DSA - 作业排序截止日期 DSA - 最佳合并模式算法

动态规划

DSA - 动态规划 DSA - 矩阵链乘法 DSA - Floyd Warshall 算法 DSA - 0-1 背包问题 DSA - 最长公共子序列算法 DSA - 旅行商问题(动态方法)

近似算法

DSA - 近似算法 DSA - 顶点覆盖算法 DSA - 集合覆盖问题 DSA - 旅行商问题(近似方法)

随机算法

DSA - 随机算法 DSA - 随机快速排序算法 DSA - Karger 最小割算法 DSA - Fisher-Yates 洗牌算法

DSA 有用资源

DSA - 问答 DSA - 快速指南 DSA - 有用资源 DSA - 讨论


DSA - Mathematical Algorithms

Mathematical algorithms are well-defined procedures that are used in solving mathematical problems related to data structures. We can see its application in competitive programming, data science and other complex mathematical concepts. These algorithms teach us how to solve a given problem by thinking logically and efficiently.

Important Mathematical Algorithms

Some of the important mathematical algorithms are −

  • Euclid's Algorithm

  • Sieve of Eratosthenes

  • Binary Exponentiation

  • Modular Arithmetic

Here is the detailed explanation −

Euclid's Algorithm

Euclid's algorithm, also referred to as the Euclidean algorithm, is a method that is used to compute GCD of two given integers. The term GCD is an abbreviation that stands for greatest common divisor. The GCD is also termed as highest common factor(HCF). It is defined as the largest integer that divides a given set of numbers.

Suppose, a given set of integers are A and B. Here's how the Euclidean Algorithm works to find their GCD −

  • If A is equal to 0 and B is the non-zero integer, then GCD(A, B) is B.

  • The GCD of two integers A and B remains unchanged on subtracting the smaller integer from the larger one. Therefore, repeating this process several times will result in GCD.

  • Recursively compute A mod B and when it becomes 0, we will get our GCD which is B.

Example

In the following example, we are going to illustrate how Euclid's algorithm works.

#include <stdio.h>
int findGrtCmFact(int a, int b) {
   if (b == 0) {
      return a;
   } else {
      return findGrtCmFact(b, a % b);
   }
}
int main() {
   int valOne = 52;
   int valTwo = 28;
   printf("The GCD of %d and %d is: %d
", valOne, valTwo, findGrtCmFact(valOne, valTwo));
   return 0;
}
#include <iostream>
using namespace std;
int findGrtCmFact(int a, int b) {
   if (b == 0) {
      return a;
   } else {
      return findGrtCmFact(b, a % b);
   }
}
int main() {
   int valOne = 52;
   int valTwo = 28;
   cout << "The GCD of " << valOne << " and " << valTwo << " is: " << findGrtCmFact(valOne, valTwo) << endl;
   return 0;
}
public class GrtComFactr {
   public static int findGrtCmFact(int a, int b) {
      if (b == 0) {
         return a;
      } else {
         return findGrtCmFact(b, a % b);
      }
   }
   public static void main(String[] args) {
      int valOne = 52;
      int valTwo = 28;
      System.out.println("The GCD of " + valOne + " and " + valTwo + " is: " + findGrtCmFact(valOne, valTwo));
   }
}
def findGrtCmFact(a, b):
   if b == 0:
      return a
   else:
      return findGrtCmFact(b, a % b)

valOne = 52
valTwo = 28
print("The GCD of {} and {} is: {}".format(valOne, valTwo, findGrtCmFact(valOne, valTwo)))

Output

The GCD of 52 and 28 is: 4

Sieve of Eratosthenes Algorithm

The Sieve of Eratosthenes algorithm is a way used for identifying prime numbers within a given range. The naive approach to finding prime numbers is by iterating over each number and checking whether the current number is prime or not. However, it is not the optimal solution.

Here's how the Sieve of Eratosthenes works −

  • Start with a list of numbers from 2 up to N. Initially, consider all these numbers as potential primes.

  • Begin with the first prime number, which is 2. Mark all multiples of 2 as composite. Move on to the next unmarked number in the list, which is the 3. Now, mark all multiples of 3 as composite.

  • After completing this process for all numbers up to n, we will be left with only the prime numbers that remain unmarked.

Example

The following example illustrates the working of Sieve of Eratosthenes algorithm.

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
// method to find primes
void sieveOfEratos(int n) {
   // initially assuming all values are prime
   bool prm[n+1];
   memset(prm, true, sizeof(prm));
   // Loop over all numbers from 2 to sqrt(n)
   for(int currPrm = 2; currPrm*currPrm <= n; currPrm++) {
      // If current prime is still true, then it is a prime
      if(prm[currPrm] == true) {
         // Update all multiples of current prime
         for(int i = currPrm*currPrm; i <= n; i += currPrm)
            // Marking factor as not prime
            prm[i] = false;  
      }
   }
   // to print the list of prime numbers
   printf("List of first 50 prime numbers: 
");
   for(int i = 2; i <= n; i++) {
      if(prm[i] == true)
         printf("%d ", i);  
   }
}
int main() {
   int lmt = 50; 
   sieveOfEratos(lmt);
   return 0;
}
#include <iostream>
#include <vector>
using namespace std;
class SvEratos {
public:
   // method to find primes
   static void sieveOfEratos(int n) {
      // initially assuming all values are prime
      vector<bool> prm(n+1, true);
      // Loop over all numbers from 2 to sqrt(n)
      for(int currPrm = 2; currPrm*currPrm <= n; currPrm++) {
         // If current prime is still true, then it is a prime
         if(prm[currPrm] == true) {
            // Update all multiples of current prime
            for(int i = currPrm*currPrm; i <= n; i += currPrm)
               // Marking factor as not prime
               prm[i] = false;  
            }
      }
      // to print the list of prime numbers
      cout << "List of first 50 prime numbers: " << endl;
      for(int i = 2; i <= n; i++) {
         if(prm[i] == true)
            cout << i << " ";  
      }
      cout << endl;
   }
};
int main() {
   int lmt = 50; 
   SvEratos::sieveOfEratos(lmt);
   return 0;
}
public class SvEratos {
   // method to find primes
   public static void sieveOfEratos(int n) {
      // initially assuming all values are prime
      boolean prm[] = new boolean[n+1];
      for(int i=0; i<=n; i++)
         prm[i] = true;
      // Loop over all numbers from 2 to sqrt(n)
      for(int currPrm = 2; currPrm*currPrm <=n; currPrm++) {
         // If current prime is still true, then it is a prime
         if(prm[currPrm] == true) {
            // Update all multiples of current prime
            for(int i = currPrm*currPrm; i <= n; i += currPrm)
               // Marking factor as not prime
               prm[i] = false;  
         }
      }
      // to print the list of prime numbers
      System.out.println("List of first 50 prime numbers: ");
      for(int i = 2; i <= n; i++) {
         if(prm[i] == true)
            System.out.print(i + " ");  
      }
   }
   public static void main(String[] args) {
      int lmt = 50; 
      sieveOfEratos(lmt);
   }
}
def sieveOfEratos(n):
    # initially assuming all values are prime
    prm = [True for _ in range(n+1)]
    # Loop over all numbers from 2 to sqrt(n)
    currPrm = 2
    while currPrm * currPrm <= n:
        # If current prime is still true, then it is a prime
        if prm[currPrm] == True:
            # Update all multiples of current prime
            for i in range(currPrm * currPrm, n+1, currPrm):
                # Marking factor as not prime
                prm[i] = False
        currPrm += 1
    
    # to print the list of prime numbers
    print("List of first 50 prime numbers: ")
    for i in range(2, n):
        if prm[i] == True:
            print(i, end=" ")

# test the function
lmt = 50
sieveOfEratos(lmt)

Output

List of first 50 prime numbers: 
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 

Binary Exponentiation Algorithm

The binary exponentiation algorithm is a procedure used to calculate the power of a given number. The naive approach to solve this type of problem i.e. np is by multiplying the number with itself p-1 times. However, it is a time-consuming process and not an efficient process.

Instead of the above approach, we can use the binary exponentiation algorithm which works as follows −

  • When the power of any given number is 0, the result will be 1.

  • If the number itself is even, use the following equation: ((n2)p/2), where n is the number and p is the power of that given number.

  • If the given number is odd, use the equation: (n*(n(p-1)/2)2).

Example

In this example, we will show how Binary Exponentiation algorithm works.

#include <stdio.h>
// function to calculate power
int bnryExp(int bs, int ex) {
   int output = 1;
   while (ex > 0) {
      if (ex % 2 == 1) {
         output *= bs;
      }
      // Square of base
      bs *= bs; 
      // Divide power by 2
      ex >>= 1; 
   }
   // Return the result stored in output
   return output;
}
int main() {
   int bs = 3;
   int ex = 6;
   // Print the result
   int result = bnryExp(bs, ex);
   printf("The output of %d to the power %d is: %d
", bs, ex, result);
   return 0;
}
#include <iostream>
using namespace std;
// method to calculate power
int bnryExp(int bs, int ex) {
   // to store output
   int output = 1;
   while (ex > 0) {
      if (ex % 2 == 1) {
         output *= bs;
      }
      // Square of base
      bs *= bs; 
      // Divide power by 2
      ex /= 2; 
   }
   // Return the result stored in output
   return output;
}
int main() {
   int bs = 3;
   int ex = 6;
   int result = bnryExp(bs, ex);
   // Print the result
   cout << "The output of " << bs << " to the power " << ex << " is: " << result << endl;
   return 0;
}
public class Exponentiation {
    // method to calculate power
    public static int bnryExp(int bs, int ex) {
        // to store output
        int output = 1;
        while (ex > 0) {
            if (ex % 2 == 1) {
                output *= bs;
            }
            // Square of base
            bs *= bs;
            // Divide power by 2
            ex /= 2;
        }
        // Return the result stored in output
        return output;
    }
    public static void main(String[] args) {
        int bs = 3;
        int ex = 6;
        // Print the result
        System.out.println("The output of " + bs + " to the power " + ex + " is: " + bnryExp(bs, ex));
    }
}
# method to calculate power
def bnryExp(bs, ex):
   # to store output
   output = 1
   while ex > 0:
      if ex % 2 == 1:
         output *= bs
      bs *= bs  
      ex //= 2  
   return output

bs = 3
ex = 6
result = bnryExp(bs, ex)
print(f"The output of {bs} to the power {ex} is: {result}")

Output

The output of 3 to the power 6 is: 729

Modular Arithmetic

Modular arithmetic is a set of rules that is applicable in computing modulus expression. This concept becomes important when handling large numbers in competitive programming. The core idea of modular arithmetic is to find the remainder of a number upon division by another number.

Important properties of modulo are as follows −

  • (m mod n) mod n is equal to m mod n

  • (m*n) mod m is equal to 0

  • (P / Q) mod m ≠ ((P mod m) / (Q mod m)) mod m

  • (P + Q) mod m = ((P mod m) + (Q mod m)) mod m

  • (P – Q) mod m = ((P mod m) – (Q mod m) + m) mod m

  • (P * Q) mod m = ((P mod m) * (Q mod m)) mod m

Example

The following example demonstrates the working of modular arithmetic.

#include <stdio.h>
// function to perform addition
int modAddition(int valOne, int valTwo, int mod) {
   // to handle negative values
   if (valOne < 0) {
      valOne = (valOne % mod + mod) % mod;
   }
   if (valTwo < 0) {
      valTwo = (valTwo % mod + mod) % mod;
   }
   // addition
   int sum = (valOne + valTwo) % mod;
   // to ensure output is non-negative
   return (sum + mod) % mod;
}
int main() {
   int valOne = 22;
   int valTwo = 26;
   int mod = 5;
   int output = modAddition(valOne, valTwo, mod);
   printf("Modular addition of %d and %d modulo %d is: %d
", valOne, valTwo, mod, output);
   return 0;
}
#include <iostream>
using namespace std;
int modAddition(int valOne, int valTwo, int mod) {
   // to handle negative values
   if (valOne < 0) {
      valOne = (valOne % mod + mod) % mod;
   }
   if (valTwo < 0) {
      valTwo = (valTwo % mod + mod) % mod;
   }
   // addition
   int sum = (valOne + valTwo) % mod;
   // to ensure the result is non-negative
   return (sum + mod) % mod;
}
int main() {
   int valOne = 22;
   int valTwo = 26;
   int mod = 5;
   int output = modAddition(valOne, valTwo, mod);
   cout << "Modular addition of " << valOne << " and " << valTwo << " modulo " << mod << " is: " << output << endl;
  return 0;
}
public class ModAdd {
    public static int modAddition(int valOne, int valTwo, int mod) {
        // to handle negative values
        if (valOne < 0) {
            valOne = (valOne % mod + mod) % mod;
        }
        if (valTwo < 0) {
            valTwo = (valTwo % mod + mod) % mod;
        }
        // addition
        int sum = (valOne + valTwo) % mod;
        // to ensure output is non-negative
        return (sum + mod) % mod;
    }
    public static void main(String[] args) {
        int valOne = 22;
        int valTwo = 26;
        int mod = 5;
        int output = modAddition(valOne, valTwo, mod);
        System.out.println("Modular addition of " + valOne + " and " + valTwo + " modulo " + mod + " is: " + output);
    }
}
def mod_addition(val_one, val_two, mod):
  # to handle negative values
  if val_one < 0:
    val_one = (val_one % mod + mod) % mod
  if val_two < 0:
    val_two = (val_two % mod + mod) % mod

  # addition 
  sum = (val_one + val_two) % mod
  # to ensure output is non-negative
  return (sum + mod) % mod

val_one = 22
val_two = 26
mod = 5
output = mod_addition(val_one, val_two, mod)
print(f"Modular addition of {val_one} and {val_two} modulo {mod} is: {output}")

Output

Modular addition of 22 and 26 modulo 5 is: 3