Merge branch 'master' into backtracking_updates_3

This commit is contained in:
David Leal
2020-10-16 15:33:13 -05:00
committed by GitHub
56 changed files with 5560 additions and 862 deletions

167
.clang-format Normal file
View File

@@ -0,0 +1,167 @@
---
Language: Cpp
AccessModifierOffset: -3
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: true
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: true
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
SortPriority: 0
- Regex: '^<.*\.h>'
Priority: 1
SortPriority: 0
- Regex: '^<.*'
Priority: 2
SortPriority: 0
- Regex: '.*'
Priority: 3
SortPriority: 0
IncludeIsMainRegex: '([-_](test|unittest))?$'
IncludeIsMainSourceRegex: ''
IndentCaseLabels: true
IndentGotoLabels: true
IndentPPDirectives: None
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
RawStringFormats:
- Language: Cpp
Delimiters:
- cc
- CC
- cpp
- Cpp
- CPP
- 'c++'
- 'C++'
CanonicalDelimiter: ''
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
CanonicalDelimiter: ''
BasedOnStyle: google
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
Standard: Auto
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 4
UseCRLF: false
UseTab: Never
...

View File

@@ -1,6 +1,6 @@
---
Checks: '-*,google-*,clang-analyzer-*,-clang-analyzer-security.insecureAPI.*,cppcoreguidelines-*,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-pro-bounds-*,openmp-*,performance-*,portability-*,modernize-*,-modernize-use-trailing-*'
WarningsAsErrors: '*,-google-readability-*,-google-explicit-constructor,-modernize-*,modernize-avoid-c-arrays,-performance-move-const-arg,-performance-noexcept-move-constructor,-cppcoreguidelines-init-variables,-cppcoreguidelines-pro-*,-cppcoreguidelines-owning-memory,-clang-analyzer-cplusplus.Move'
WarningsAsErrors: '*,-google-readability-*,-google-explicit-constructor,-modernize-*,modernize-avoid-c-arrays,-performance-move-const-arg,-performance-noexcept-move-constructor,-performance-unnecessary-value-param,-cppcoreguidelines-init-variables,-cppcoreguidelines-pro-*,-cppcoreguidelines-owning-memory,-clang-analyzer-cplusplus.Move'
HeaderFilterRegex: ''
AnalyzeTemporaryDtors: false
FormatStyle: '{ BasedOnStyle: Google, UseTab: Never, IndentWidth: 4, TabWidth: 4, AllowShortIfStatementsOnASingleLine: false, IndentCaseLabels: true, ColumnLimit: 80, AccessModifierOffset: -3, AlignConsecutiveMacros: true }'

View File

@@ -16,7 +16,7 @@ jobs:
- name: requirements
run: |
sudo apt -qq -y update
sudo apt -qq install clang-tidy-10
sudo apt -qq install clang-tidy-10 clang-format-10
# checks are passing with less errors when used with this version.
# The default installs v6.0 which did not work out well in my tests
- name: Setup Git Specs
@@ -44,7 +44,7 @@ jobs:
git "mv" "${fname}" ${new_fname}
fi
done
git commit -am "formatting filenames $GITHUB_SHA" || true
git commit -am "formatting filenames ${GITHUB_SHA::8}" || true
- name: Update DIRECTORY.md
shell: python
@@ -124,15 +124,9 @@ jobs:
subprocess.run(["clang-tidy-10", "--fix", "-p=build", "--extra-arg=-std=c++11", *cpp_files, "--"],
check=True, text=True, stderr=subprocess.STDOUT)
# for cpp_file in cpp_files:
# subprocess.run(["clang-tidy-10", "--fix", "-p=build", cpp_file, "--"],
# check=True, text=True, stderr=subprocess.STDOUT)
# print("g++:")
# compile_exts = tuple(".c .c++ .cc .cpp .cu .cxx".split())
# compile_files = [file for file in cpp_files if file.lower().endswith(compile_exts)]
# for cpp_file in cpp_files:
# subprocess.run(["g++", cpp_file], check=True, text=True)
subprocess.run(["clang-format-10", "-i", "-style=file", *cpp_files],
check=True, text=True, stderr=subprocess.STDOUT)
upper_files = [file for file in cpp_files if file != file.lower()]
if upper_files:
@@ -155,7 +149,7 @@ jobs:
- name: Commit and push changes
run: |
git diff DIRECTORY.md
git commit -am "clang-tidy fixes for $GITHUB_SHA" || true
git commit -am "clang-format and clang-tidy fixes for ${GITHUB_SHA::8}" || true
git push --force origin HEAD:$GITHUB_REF || true
build:

View File

@@ -40,7 +40,8 @@ add_subdirectory(backtracking)
add_subdirectory(data_structures)
add_subdirectory(machine_learning)
add_subdirectory(numerical_methods)
add_subdirectory(graph)
cmake_policy(SET CMP0054 NEW)
cmake_policy(SET CMP0057 NEW)
find_package(Doxygen OPTIONAL_COMPONENTS dot dia)

76
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at 1anuppanwar@gmail.com, dynamitechetan@gmail.com, nikhilkala8@gmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at <https://www.contributor-covenant.org/version/1/4/code-of-conduct.html>
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
<https://www.contributor-covenant.org/faq>

View File

@@ -23,7 +23,8 @@ We are very happy that you consider implementing algorithms and data structures
- Please use the directory structure of the repository.
- File extension for code should be *.h *.cpp.
- Don't use **bits/stdc++.h** because this is quite Linux specific and slows down the compilation process.
- Avoid using **struct** and instead use the **class** keyword.
- Organize your code using **`struct`**, **`class`** and/or **`namespace`** keywords
- If an implementation of the algorithm already exists, please refer to the [file-name section below](#new-file-name-guidelines).
- You can suggest reasonable changes to existing algorithms.
- Strictly use snake_case (underscore_separated) in filenames.
- If you have added or modified code, please make sure the code compiles before submitting.
@@ -58,7 +59,7 @@ We are very happy that you consider implementing algorithms and data structures
#include
/**
/**
* @namespace <check from other files in this repo>
*/
namespace name {
@@ -66,42 +67,44 @@ namespace name {
/**
* Class documentation
*/
class cls_name{
class class_name {
private:
int var1; ///< short info of this variable
char *msg; ///< short info
int variable; ///< short info of this variable
char *message; ///< short info
public:
// other members also documented as below
// other members also documented as below
}
/**
* Function documentation
* Function documentation
* @tparam T this is a one-line info about T
* @param param1 on-line info about param1
* @param param2 on-line info about param2
* @returns `true` if ...
* @returns `false` if ...
* @returns `false` if ...
*/
template<class T>
bool func(int param1, T param2) {
// function statements here
if(/*something bad*/)
// function statements here
if (/*something bad*/) {
return false;
}
return true;
}
/** Test function */
void test() {
/* some statements */
assert(func(...) == ...); // this ensures that the algorithm works as expected
static void test() {
/* desciptions of the following test */
assert(func(...) == ...); // this ensures that the algorithm works as expected
// can have multiple checks
// can have multiple checks
}
/** Main function */
int main(int argc, char *argv[]) {
test(); // execute the tests
// code here
return 0;
}
@@ -109,15 +112,16 @@ int main(int argc, char *argv[]) {
#### New File Name guidelines
- Use lowercase words with ``"_"`` as separator
- For instance
- For instance
```
MyNewCppClass.CPP is incorrect
my_new_cpp_class.cpp is correct format
```
- It will be used to dynamically create a directory of files and implementation.
- File name validation will run on docker to ensure the validity.
- If an implementation of the algorithm already exists and your version is different from that implemented, please use incremental numeric digit as a suffix. For example, if `median_search.cpp` already exists in the `search` folder and you are contributing a new implementation, the filename should be `median_search2.cpp` and for a third implementation, `median_search3.cpp`.
#### New Directory guidelines
#### New Directory guidelines
- We recommend adding files to existing directories as much as possible.
- Use lowercase words with ``"_"`` as separator ( no spaces or ```"-"``` allowed )
- For instance
@@ -150,19 +154,33 @@ Common prefixes:
### Pull Requests
- Checkout our [pull request template](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/.github/pull_request_template.md)
#### cpplint
To see if [__cpplint__](https://github.com/cpplint/cpplint) is already installed, do:
* `cpplint --version` # currently returns "cpplint 1.4.4"
If cpplint is ___not___ installed then do:
* `python3 -m pip install cpplint` # If that does not work then try...
* `py -m pip install cpplint` # If that does not work then try...
* `pip install cpplint`
Once cpplint is installed, test your file(s) with:
* `cpplint --filter=-legal my_file.cpp my_other_file.cpp` # Fix any issues and try again.
#### Building Locally
Before submitting a pull request, build the code locally or using the convenient [![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/TheAlgorithms/C-Plus-Plus) service.
```
cmake -B build -S .
```
The [__clang-format__](https://clang.llvm.org/docs/ClangFormat.html) tool can fix whitespace related _cpplint_ issues.
* On Macs only: `brew install clang-format` # Only needs to be installed once.
* All platforms: `clang-format -i -style="{IndentWidth: 4}" my_file.cpp`
#### Static Code Analyzer
We use [clang-tidy](https://clang.llvm.org/extra/clang-tidy/) as a static code analyzer with a configuration in [.clang-tidy](.clang-tidy).
```
clang-tidy --fix --quiet -p build subfolder/file_to_check.cpp --
```
#### Code Formatter
[__clang-format__](https://clang.llvm.org/docs/ClangFormat.html) is used for code forrmating.
* Installation (Only needs to be installed once.)
* Mac (using home-brew): `brew install clang-format`
* Mac (using macports): `sudo port install clang-10 +analyzer`
* Windows (MSYS2 64-bit): `pacman -S mingw-w64-x86_64-clang-tools-extra`
* Linux (Debian): `sudo apt-get install clang-format-10 clang-tidy-10`
* Running (all platforms): `clang-format -i -style="file" my_file.cpp`
#### GitHub Actions
Enable GitHub Actions on your fork of the repository.
After enabling it will execute `clang-tidy` and `clang-format` after every a push (not a commit).
The result can create another commit if the actions made any changes on your behalf.
Hence, it is better to wait and check the results of GitHub Actions after every push.
Run `git pull` in your local clone if these actions made many changes in order to avoid merge conflicts.
Most importantly,
- Happy coding!

View File

@@ -58,9 +58,9 @@
* [Edit Distance](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/edit_distance.cpp)
* [Egg Dropping Puzzle](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/egg_dropping_puzzle.cpp)
* [Fibonacci Bottom Up](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/fibonacci_bottom_up.cpp)
* [Fibonacci Top Down](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/fibonacci_top_down.cpp)
* [Floyd Warshall](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/floyd_warshall.cpp)
* [Kadane](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/kadane.cpp)
* [Kadane2](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/kadane2.cpp)
* [Longest Common String](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/longest_common_string.cpp)
* [Longest Common Subsequence](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/longest_common_subsequence.cpp)
* [Longest Increasing Subsequence](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/longest_increasing_subsequence.cpp)
@@ -69,6 +69,7 @@
* [Searching Of Element In Dynamic Array](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/searching_of_element_in_dynamic_array.cpp)
* [Shortest Common Supersequence](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/shortest_common_supersequence.cpp)
* [Tree Height](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/tree_height.cpp)
* [Word Break](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/word_break.cpp)
## Geometry
* [Jarvis Algorithm](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/geometry/jarvis_algorithm.cpp)
@@ -80,10 +81,11 @@
* [Connected Components](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/connected_components.cpp)
* [Connected Components With Dsu](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/connected_components_with_dsu.cpp)
* [Cycle Check Directed Graph](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/cycle_check_directed_graph.cpp)
* [Dfs](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/dfs.cpp)
* [Dfs With Stack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/dfs_with_stack.cpp)
* [Depth First Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/depth_first_search.cpp)
* [Depth First Search With Stack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/depth_first_search_with_stack.cpp)
* [Dijkstra](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/dijkstra.cpp)
* [Hamiltons Cycle](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/hamiltons_cycle.cpp)
* [Hopcroft Karp](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/hopcroft_karp.cpp)
* [Is Graph Bipartite](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/is_graph_bipartite.cpp)
* [Kosaraju](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/kosaraju.cpp)
* [Kruskal](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/kruskal.cpp)
@@ -99,6 +101,7 @@
## Greedy Algorithms
* [Dijkstra](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/greedy_algorithms/dijkstra.cpp)
* [Huffman](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/greedy_algorithms/huffman.cpp)
* [Jumpgame](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/greedy_algorithms/jumpgame.cpp)
* [Knapsack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/greedy_algorithms/knapsack.cpp)
* [Kruskals Minimum Spanning Tree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/greedy_algorithms/kruskals_minimum_spanning_tree.cpp)
* [Prims Minimum Spanning Tree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/greedy_algorithms/prims_minimum_spanning_tree.cpp)
@@ -113,12 +116,15 @@
* [Adaline Learning](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/machine_learning/adaline_learning.cpp)
* [Kohonen Som Topology](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/machine_learning/kohonen_som_topology.cpp)
* [Kohonen Som Trace](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/machine_learning/kohonen_som_trace.cpp)
* [Neural Network](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/machine_learning/neural_network.cpp)
* [Ordinary Least Squares Regressor](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/machine_learning/ordinary_least_squares_regressor.cpp)
* [Vector Ops](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/machine_learning/vector_ops.hpp)
## Math
* [Armstrong Number](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/armstrong_number.cpp)
* [Binary Exponent](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/binary_exponent.cpp)
* [Check Amicable Pair](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/check_amicable_pair.cpp)
* [Check Factorial](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/check_factorial.cpp)
* [Check Prime](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/check_prime.cpp)
* [Complex Numbers](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/complex_numbers.cpp)
* [Double Factorial](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/math/double_factorial.cpp)
@@ -182,12 +188,13 @@
* [Decimal To Binary](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/decimal_to_binary.cpp)
* [Decimal To Hexadecimal](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/decimal_to_hexadecimal.cpp)
* [Decimal To Roman Numeral](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/decimal_to_roman_numeral.cpp)
* [Fast Interger Input](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/fast_interger_input.cpp)
* [Fast Integer Input](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/fast_integer_input.cpp)
* [Happy Number](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/happy_number.cpp)
* [Matrix Exponentiation](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/matrix_exponentiation.cpp)
* [Palindrome Of Number](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/palindrome_of_number.cpp)
* [Paranthesis Matching](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/paranthesis_matching.cpp)
* [Pascal Triangle](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/pascal_triangle.cpp)
* [Postfix Evaluation](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/postfix_evaluation.cpp)
* [Primality Test](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/primality_test.cpp)
* [Smallest Circle](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/smallest_circle.cpp)
* [Sparse Matrix](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/others/sparse_matrix.cpp)
@@ -204,8 +211,10 @@
## Range Queries
* [Fenwick Tree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/fenwick_tree.cpp)
* [Heavy Light Decomposition](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/heavy_light_decomposition.cpp)
* [Mo](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/mo.cpp)
* [Segtree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/segtree.cpp)
* [Sparse Table](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/range_queries/sparse_table.cpp)
## Search
* [Binary Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/search/binary_search.cpp)
@@ -234,10 +243,13 @@
* [Heap Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/heap_sort.cpp)
* [Insertion Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/insertion_sort.cpp)
* [Library Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/library_sort.cpp)
* [Merge Insertion Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/merge_insertion_sort.cpp)
* [Merge Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/merge_sort.cpp)
* [Non Recursive Merge Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/non_recursive_merge_sort.cpp)
* [Numeric String Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/numeric_string_sort.cpp)
* [Odd Even Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/odd_even_sort.cpp)
* [Pancake Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/pancake_sort.cpp)
* [Pigeonhole Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/pigeonhole_sort.cpp)
* [Quick Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/quick_sort.cpp)
* [Quick Sort 3](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/quick_sort_3.cpp)
* [Radix Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/radix_sort.cpp)
@@ -245,10 +257,12 @@
* [Shell Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/shell_sort.cpp)
* [Shell Sort2](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/shell_sort2.cpp)
* [Slow Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/slow_sort.cpp)
* [Strand Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/strand_sort.cpp)
* [Swap Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/swap_sort.cpp)
* [Tim Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/sorting/tim_sort.cpp)
## Strings
* [Brute Force String Searching](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/strings/brute_force_string_searching.cpp)
* [Horspool](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/strings/horspool.cpp)
* [Knuth Morris Pratt](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/strings/knuth_morris_pratt.cpp)
* [Rabin Karp](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/strings/rabin_karp.cpp)

13
REVIEWER_CODE.md Normal file
View File

@@ -0,0 +1,13 @@
# Guidelines for reviewers and maintainers
Following are some guidelines for contributors who are providing reviews to the pull-requests.
1. On any given pull-request, there only one reviewer should be active at a time. Once the reviewer is done, others may add short comments or any further reviews as needed. Again, one at a time.
2. Assigning reviewers should be avoided unless the pull-request is for a particular task the reviewer is more proficient in.
3. Any contributor who has had their code merged into the repo can provide with reviews as they have gone through the repo standards at least once before. The reviewer will be on a first-come-first serve basis.
4. Most repositories have a check-list in the description for pull-requests. Many times, the contributors are not following them and simply remove the checklist or checkthem without taking the time to review the checklist items. These contributors are almost always copying the code from somewhere. These should be pointed out politely and reviews should be blocked until the contributor updates the basic code structure per the checklist and the repo standards.
5. The reviewers should label every pull-request appropriately - including "invalid" as the case may be.
6. Some pull-requests have existing duplicate code or duplicate pull-requests or sometimes, a novice might create a new pull-request for every new commit. This is a daunting task but one of the responsibility of a reviewer.
7. Discourage creating branches on the repo but rather fork the repo to the respective userspace and contribute from that fork.
8. Some repos - C & C++ - have collaboration with GitPod wherein the code and the contribution can be executed and tested online with relative simplicity. It also contains tools necessary to perform debug and CI checks without installing any tools. Encourage contributors to utilize the feature. Reviewers can test the contributed algorithms online without worrying about forks and branches.
9. There should not be any hurry to merge pull-requests. Since the repos are educational, better to get the contributions right even if it takes a bit longer to review. Encourage patience and develop debugging skills of contributors.

View File

@@ -1,50 +1,103 @@
/**
* @file
* @brief [Eight Queens](https://en.wikipedia.org/wiki/Eight_queens_puzzle)
* puzzle, printing all solutions
*
* @author [Himani Negi](https://github.com/Himani2000)
* @author [David Leal](https://github.com/Panquesito7)
*
*/
#include <iostream>
#define n 4
#include <array>
void PrintSol(int Board[n][n]) {
/**
* @namespace backtracking
* @brief Backtracking algorithms
*/
namespace backtracking {
/**
* @namespace n_queens_all_solutions
* @brief Functions for [Eight
* Queens](https://en.wikipedia.org/wiki/Eight_queens_puzzle) puzzle with all solutions.
*/
namespace n_queens_all_solutions {
/**
* Utility function to print matrix
* @tparam n number of matrix size
* @param board matrix where numbers are saved
*/
template <size_t n>
void PrintSol(const std::array<std::array<int, n>, n>& board) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
std::cout << Board[i][j] << " ";
std::cout << board[i][j] << " ";
}
std::cout << std::endl;
}
std::cout << std::endl;
}
bool CanIMove(int Board[n][n], int row, int col) {
/**
* Check if a queen can be placed on matrix
* @tparam n number of matrix size
* @param board matrix where numbers are saved
* @param row current index in rows
* @param col current index in columns
* @returns `true` if queen can be placed on matrix
* @returns `false` if queen can't be placed on matrix
*/
template <size_t n>
bool CanIMove(const std::array<std::array<int, n>, n>& board, int row, int col) {
/// check in the row
for (int i = 0; i < col; i++) {
if (Board[row][i] == 1)
if (board[row][i] == 1) {
return false;
}
}
/// check the first diagonal
for (int i = row, j = col; i >= 0 && j >= 0; i--, j--) {
if (Board[i][j] == 1)
if (board[i][j] == 1) {
return false;
}
}
/// check the second diagonal
for (int i = row, j = col; i <= n - 1 && j >= 0; i++, j--) {
if (Board[i][j] == 1)
if (board[i][j] == 1) {
return false;
}
}
return true;
}
void NQueenSol(int Board[n][n], int col) {
/**
* Solve n queens problem
* @tparam n number of matrix size
* @param board matrix where numbers are saved
* @param col current index in columns
*/
template <size_t n>
void NQueenSol(std::array<std::array<int, n>, n> board, int col) {
if (col >= n) {
PrintSol(Board);
PrintSol(board);
return;
}
for (int i = 0; i < n; i++) {
if (CanIMove(Board, i, col)) {
Board[i][col] = 1;
NQueenSol(Board, col + 1);
Board[i][col] = 0;
if (CanIMove(board, i, col)) {
board[i][col] = 1;
NQueenSol(board, col + 1);
board[i][col] = 0;
}
}
}
} // namespace n_queens_all_solutions
} // namespace backtracking
/**
* Main function
*/
int main() {
int Board[n][n] = {0};
NQueenSol(Board, 0);
const int n = 4;
std::array<std::array<int, n>, n> board{0};
backtracking::n_queens_all_solutions::NQueenSol(board, 0);
}

View File

@@ -1,62 +1,113 @@
/*
A Maze is given as N*N binary matrix of blocks where source block is the
upper left most block i.e., maze[0][0] and destination block is lower
rightmost block i.e., maze[N-1][N-1]. A rat starts from source and has to
reach destination. The rat can move only in two directions: forward and down.
In the maze matrix, 0 means the block is dead end and 1 means the block can
be used in the path from source to destination.
*/
/**
* @file
* @brief Implements [Rat in a
* Maze](https://www.codesdope.com/blog/article/backtracking-to-
* solve-a-rat-in-a-maze-c-java-pytho/) algorithm
*
* @details
* A Maze is given as N*N binary matrix of blocks where source block is the
* upper left most block i.e., maze[0][0] and destination block is lower
* rightmost block i.e., maze[N-1][N-1]. A rat starts from source and has to
* reach destination. The rat can move only in two directions: forward and down.
* In the maze matrix, 0 means the block is dead end and 1 means the block can
* be used in the path from source to destination.
*
* @author [Vaibhav Thakkar](https://github.com/vaithak)
* @author [David Leal](https://github.com/Panquesito7)
*/
#include <array>
#include <iostream>
#define size 4
#include <cassert>
using namespace std;
int solveMaze(int currposrow, int currposcol, int maze[size][size],
int soln[size][size]) {
/**
* @namespace backtracking
* @brief Backtracking algorithms
*/
namespace backtracking {
/**
* @namespace rat_maze
* @brief Functions for [Rat in a
* Maze](https://www.codesdope.com/blog/article/backtracking-to-
* solve-a-rat-in-a-maze-c-java-pytho/) algorithm
*/
namespace rat_maze {
/**
* @brief Solve rat maze problem
* @tparam size number of matrix size
* @param currposrow current position in rows
* @param currposcol current position in columns
* @param maze matrix where numbers are saved
* @param soln matrix to problem solution
* @returns 0 on end
*/
template <size_t size>
bool solveMaze(int currposrow, int currposcol,
const std::array<std::array<int, size>, size> &maze,
std::array<std::array<int, size>, size> soln) {
if ((currposrow == size - 1) && (currposcol == size - 1)) {
soln[currposrow][currposcol] = 1;
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
cout << soln[i][j];
std::cout << soln[i][j] << " ";
}
cout << endl;
std::cout << std::endl;
}
return 1;
return true;
} else {
soln[currposrow][currposcol] = 1;
// if there exist a solution by moving one step ahead in a collumn
// if there exist a solution by moving one step ahead in a column
if ((currposcol < size - 1) && maze[currposrow][currposcol + 1] == 1 &&
solveMaze(currposrow, currposcol + 1, maze, soln)) {
return 1;
return true;
}
// if there exists a solution by moving one step ahead in a row
if ((currposrow < size - 1) && maze[currposrow + 1][currposcol] == 1 &&
solveMaze(currposrow + 1, currposcol, maze, soln)) {
return 1;
return true;
}
// the backtracking part
soln[currposrow][currposcol] = 0;
return 0;
return false;
}
}
} // namespace rat_maze
} // namespace backtracking
int main(int argc, char const *argv[]) {
int maze[size][size] = {
{1, 0, 1, 0}, {1, 0, 1, 1}, {1, 0, 0, 1}, {1, 1, 1, 1}};
/**
* @brief Test implementations
* @returns void
*/
static void test(){
const int size = 4;
std::array<std::array<int, size>, size> maze = {
std::array<int, size>{1, 0, 1, 0}, std::array<int, size>{1, 0, 1, 1},
std::array<int, size>{1, 0, 0, 1}, std::array<int, size>{1, 1, 1, 1}};
int soln[size][size];
std::array<std::array<int, size>, size> soln{};
// Backtracking: setup matrix solution to zero
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
soln[i][j] = 0;
}
}
int currposrow = 0;
int currposcol = 0;
solveMaze(currposrow, currposcol, maze, soln);
int currposrow = 0; // Current position in rows
int currposcol = 0; // Current position in columns
assert(backtracking::rat_maze::solveMaze<size>(currposrow, currposcol, maze,
soln) == 1);
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
test(); // run the tests
return 0;
}

View File

@@ -22,7 +22,7 @@
*/
namespace backtracking {
/**
* Checks if it's possible to place a 'no'
* Checks if it's possible to place a number 'no'
* @tparam V number of vertices in the array
* @param mat matrix where numbers are saved
* @param i current index in rows
@@ -34,14 +34,14 @@ namespace backtracking {
*/
template <size_t V>
bool isPossible(const std::array <std::array <int, V>, V> &mat, int i, int j, int no, int n) {
/// Row or col nahin hona chahiye
/// 'no' shouldn't be present in either row i or column j
for (int x = 0; x < n; x++) {
if (mat[x][j] == no || mat[i][x] == no) {
return false;
}
}
/// Subgrid mein nahi hona chahiye
/// 'no' shouldn't be present in the 3*3 subgrid
int sx = (i / 3) * 3;
int sy = (j / 3) * 3;
@@ -91,7 +91,7 @@ namespace backtracking {
bool solveSudoku(std::array <std::array <int, V>, V> &mat, int i, int j) {
/// Base Case
if (i == 9) {
/// Solve kr chuke hain for 9 rows already
/// Solved for 9 rows already
backtracking::printMat<V>(mat, 9);
return true;
}
@@ -109,17 +109,17 @@ namespace backtracking {
/// Try to place every possible no
for (int no = 1; no <= 9; no++) {
if (backtracking::isPossible<V>(mat, i, j, no, 9)) {
/// Place the no - assuming solution aa jayega
/// Place the 'no' - assuming a solution will exist
mat[i][j] = no;
bool aageKiSolveHui = backtracking::solveSudoku<V>(mat, i, j + 1);
if (aageKiSolveHui) {
bool solution_found = backtracking::solveSudoku<V>(mat, i, j + 1);
if (solution_found) {
return true;
}
/// Nahin solve hui
/// Couldn't find a solution
/// loop will place the next no.
}
}
/// Sare no try kr liey, kisi se bhi solve nahi hui
/// Solution couldn't be found for any of the numbers provided
mat[i][j] = 0;
return false;
}

View File

@@ -1,134 +1,275 @@
/**
* @file
* @brief Implementation of singly linked list algorithm.
* @details
* The linked list is a data structure used for holding a sequence of
* values, which can be added, removed and displayed.
* ### Algorithm
* Values can be added by iterating to the end of a list(by following
* the pointers) starting from the first link. Whichever link points to null
* is considered the last link and is pointed to the new value.
*
* Values can be removed by also iterating through the list. When the node
* containing the value is found, the node pointing to the current node is made
* to point to the node that the current node is pointing to, and then returning
* the current node to heap store.
*/
#include <iostream>
#include <memory>
#include <string>
struct node {
int val;
node *next;
/**
* @namespace data_structures
* @brief Data Structures algorithms
*/
namespace data_structures {
/**
* @namespace linked_list
* @brief Functions for singly linked list algorithm
*/
namespace linked_list {
/**
* This function checks if the string passed consists
* of only digits.
* @param s To be checked if s contains only integers
* @returns true if there are only only digits present in the string
* @returns false if any other character is found
*/
bool isDigit(const std::string& s) {
// function statements here
for (char i : s) {
if (!isdigit(i)) {
return false;
}
}
return true;
}
/**
* A link class containing a value and pointer to another link
*/
class link {
private:
int pvalue; ///< value of the current link
std::shared_ptr<link> psucc; ///< pointer to the next value on the list
public:
/**
* function returns the integer value stored in the link.
* @returns the integer value stored in the link.
*/
int val() { return pvalue; }
/**
* function returns the pointer to next link
* @returns the pointer to the next link
* */
std::shared_ptr<link>& succ() { return psucc; }
/**
* Creates link with provided value and pointer to next link
* @param value is the integer stored in the link
*/
explicit link(int value = 0) : pvalue(value), psucc(nullptr) {}
};
node *start;
/**
* A list class containing a sequence of links
*/
class list {
private:
std::shared_ptr<link> first; ///< link before the actual first element
std::shared_ptr<link> last; ///< last link on the list
public:
/**
* List constructor. Initializes the first and last link.
*/
list() {
// Initialize the first link
first = std::make_shared<link>();
// Initialize the last link with the first link
last = nullptr;
}
void insert(int x) {
node *t = start;
node *n = new node;
n->val = x;
n->next = NULL;
if (start != NULL) {
while (t->next != NULL) {
t = t->next;
}
t->next = n;
bool isEmpty();
void push_back(int new_elem);
void push_front(int new_elem);
void erase(int old_elem);
void display();
std::shared_ptr<link> search(int find_elem);
void reverse();
};
/**
* function checks if list is empty
* @returns true if list is empty
* @returns false if list is not empty
*/
bool list::isEmpty() {
if (last == nullptr) {
return true;
} else {
start = n;
return false;
}
}
void remove(int x) {
if (start == NULL) {
std::cout << "\nLinked List is empty\n";
return;
} else if (start->val == x) {
node *temp = start;
start = start->next;
delete temp;
return;
}
node *temp = start, *parent = start;
while (temp != NULL && temp->val != x) {
parent = temp;
temp = temp->next;
}
if (temp == NULL) {
std::cout << std::endl << x << " not found in list\n";
return;
}
parent->next = temp->next;
delete temp;
}
void search(int x) {
node *t = start;
int found = 0;
while (t != NULL) {
if (t->val == x) {
std::cout << "\nFound";
found = 1;
break;
}
t = t->next;
}
if (found == 0) {
std::cout << "\nNot Found";
}
}
void show() {
node *t = start;
while (t != NULL) {
std::cout << t->val << "\t";
t = t->next;
}
}
void reverse() {
node *first = start;
if (first != NULL) {
node *second = first->next;
while (second != NULL) {
node *tem = second->next;
second->next = first;
first = second;
second = tem;
}
start->next = NULL;
start = first;
/**
* function adds new element to the end of the list
* @param new_elem to be added to the end of the list
*/
void list::push_back(int new_elem) {
if (isEmpty()) {
first->succ() = std::make_shared<link>(new_elem);
last = first->succ();
} else {
std::cout << "\nEmpty list";
last->succ() = std::make_shared<link>(new_elem);
last = last->succ();
}
}
/**
* function adds new element to the beginning of the list
* @param new_elem to be added to front of the list
*/
void list::push_front(int new_elem) {
if (isEmpty()) {
first->succ() = std::make_shared<link>(new_elem);
last = first->succ();
} else {
std::shared_ptr<link> t = std::make_shared<link>(new_elem);
t->succ() = first->succ();
first->succ() = t;
}
}
/**
* function erases old element from the list
* @param old_elem to be erased from the list
*/
void list::erase(int old_elem) {
if (isEmpty()) {
std::cout << "List is Empty!";
return;
}
std::shared_ptr<link> t = first;
std::shared_ptr<link> to_be_removed = nullptr;
while (t != last && t->succ()->val() != old_elem) {
t = t->succ();
}
if (t == last) {
std::cout << "Element not found\n";
return;
}
to_be_removed = t->succ();
t->succ() = t->succ()->succ();
to_be_removed.reset();
if (t->succ() == nullptr) {
last = nullptr;
}
}
/**
* function displays all the elements in the list
* @returns 'void'
*/
void list::display() {
if (isEmpty()) {
std::cout << "List is Empty!";
return;
}
std::shared_ptr<link> t = first;
while (t->succ() != nullptr) {
std::cout << t->succ()->val() << "\t";
t = t->succ();
}
}
/**
* function searchs for @param find_elem in the list
* @param find_elem to be searched for in the list
*/
std::shared_ptr<link> list::search(int find_elem) {
if (isEmpty()) {
std::cout << "List is Empty!";
return nullptr;
}
std::shared_ptr<link> t = first;
while (t != last && t->succ()->val() != find_elem) {
t = t->succ();
}
if (t == last) {
std::cout << "Element not found\n";
return nullptr;
}
std::cout << "Element was found\n";
return t->succ();
}
} // namespace linked_list
} // namespace data_structures
/**
* Main function:
* Allows the user add and delete values from the list.
* Also allows user to search for and display values in the list.
* @returns 0 on exit
*/
int main() {
int choice, x;
data_structures::linked_list::list l;
int choice = 0;
int x = 0;
std::string s;
do {
std::cout << "\n1. Insert";
std::cout << "\n2. Delete";
std::cout << "\n3. Search";
std::cout << "\n4. Print";
std::cout << "\n5. Reverse";
std::cout << "\n0. Exit";
std::cout << "\n\nEnter you choice : ";
std::cin >> choice;
switch (choice) {
case 1:
std::cout << "\nEnter the element to be inserted : ";
std::cin >> x;
insert(x);
break;
case 2:
std::cout << "\nEnter the element to be removed : ";
std::cin >> x;
remove(x);
break;
case 3:
std::cout << "\nEnter the element to be searched : ";
std::cin >> x;
search(x);
break;
case 4:
show();
std::cout << "\n";
break;
case 5:
std::cout << "The reversed list: \n";
reverse();
show();
std::cout << "\n";
break;
case 1:
std::cout << "\nEnter the element to be inserted : ";
std::cin >> s;
if (data_structures::linked_list::isDigit(s)) {
x = std::stoi(s);
l.push_back(x);
} else {
std::cout << "Wrong Input!\n";
}
break;
case 2:
std::cout << "\nEnter the element to be removed : ";
std::cin >> s;
if (data_structures::linked_list::isDigit(s)) {
x = std::stoi(s);
l.erase(x);
} else {
std::cout << "Wrong Input!\n";
}
break;
case 3:
std::cout << "\nEnter the element to be searched : ";
std::cin >> s;
if (data_structures::linked_list::isDigit(s)) {
x = std::stoi(s);
std::shared_ptr<data_structures::linked_list::link> found =
l.search(x);
} else {
std::cout << "Wrong Input!\n";
}
break;
case 4:
l.display();
std::cout << "\n";
break;
default:
std::cout << "Invalid Input\n" << std::endl;
break;
}
} while (choice != 0);
return 0;
}

View File

@@ -20,10 +20,10 @@
#include <memory>
#include <vector>
/** \namespace data_structure
/** \namespace data_structures
* \brief Data-structure algorithms
*/
namespace data_structure {
namespace data_structures {
constexpr int MAX_LEVEL = 2; ///< Maximum level of skip list
constexpr float PROBABILITY = 0.5; ///< Current probability for "coin toss"
@@ -64,7 +64,7 @@ class SkipList {
SkipList() {
level = 0;
// Header initialization
header = std::shared_ptr<Node>(new Node(-1, MAX_LEVEL));
header = std::make_shared<Node>(-1, MAX_LEVEL);
}
/**
@@ -75,8 +75,9 @@ class SkipList {
int randomLevel() {
int lvl = 0;
while (static_cast<float>(std::rand()) / RAND_MAX < PROBABILITY &&
lvl < MAX_LEVEL)
lvl < MAX_LEVEL) {
lvl++;
}
return lvl;
}
@@ -93,8 +94,9 @@ class SkipList {
update.fill(nullptr);
for (int i = level; i >= 0; i--) {
while (x->forward[i] != nullptr && x->forward[i]->key < key)
while (x->forward[i] != nullptr && x->forward[i]->key < key) {
x = x->forward[i];
}
update[i] = x;
}
@@ -112,7 +114,7 @@ class SkipList {
}
std::shared_ptr<Node> n =
std::shared_ptr<Node>(new Node(key, rlevel, value));
std::make_shared<Node>(key, rlevel, value);
for (int i = 0; i <= rlevel; i++) {
n->forward[i] = update[i]->forward[i];
update[i]->forward[i] = n;
@@ -135,8 +137,9 @@ class SkipList {
update.fill(nullptr);
for (int i = level; i >= 0; i--) {
while (x->forward[i] != nullptr && x->forward[i]->key < key)
while (x->forward[i] != nullptr && x->forward[i]->key < key) {
x = x->forward[i];
}
update[i] = x;
}
@@ -146,8 +149,9 @@ class SkipList {
if (!doesnt_exist) {
for (int i = 0; i <= level; i++) {
if (update[i]->forward[i] != x)
if (update[i]->forward[i] != x) {
break;
}
update[i]->forward[i] = x->forward[i];
}
/* Remove empty levels*/
@@ -198,7 +202,7 @@ class SkipList {
}
};
} // namespace data_structure
} // namespace data_structures
/**
* Main function:
@@ -208,14 +212,14 @@ class SkipList {
int main() {
std::srand(std::time(nullptr));
data_structure::SkipList lst;
data_structures::SkipList lst;
for (int j = 0; j < (1 << (data_structure::MAX_LEVEL + 1)); j++) {
int k = (std::rand() % (1 << (data_structure::MAX_LEVEL + 2)) + 1);
for (int j = 0; j < (1 << (data_structures::MAX_LEVEL + 1)); j++) {
int k = (std::rand() % (1 << (data_structures::MAX_LEVEL + 2)) + 1);
lst.insertElement(k, &j);
}
lst.displayList();
return 0;
}

View File

@@ -26,6 +26,7 @@ void show() {
}
void topmost() { std::cout << "\nTopmost element: " << stack[stack_idx - 1]; }
void bottom() { std::cout << "\nBottom element: " << stack[0]; } // If we need access to first element without using pop command
int main() {
std::cout << "\nEnter stack_size of stack : ";
std::cin >> stack_size;
@@ -37,6 +38,7 @@ int main() {
std::cout << "\n2. Pop";
std::cout << "\n3. Print";
std::cout << "\n4. Print topmost element:";
std::cout << "\n5. Print Bottom element:";
std::cout << "\nEnter Your Choice : ";
std::cin >> ch;
if (ch == 1) {
@@ -49,6 +51,8 @@ int main() {
show();
} else if (ch == 4) {
topmost();
} else if(ch == 5) {
bottom();
}
} while (ch != 0);

View File

@@ -1,91 +1,209 @@
#include <stdbool.h>
#include <stdio.h>
/**
* @file
* @author [@Arctic2333](https://github.com/Arctic2333)
* @author [Krishna Vedala](https://github.com/kvedala)
* @brief Implementation of [Trie](https://en.wikipedia.org/wiki/Trie) data
* structure for English alphabets in small characters.
* @note the function ::data_structure::trie::deleteString might be erroneous
* @see trie_modern.cpp
*/
#include <array>
#include <cassert>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
// structure definition
typedef struct trie {
struct trie* arr[26];
bool isEndofWord;
} trie;
/** \namespace data_structures
* \brief Data-structure algorithms
*/
namespace data_structures {
/**
* @brief [Trie](https://en.wikipedia.org/wiki/Trie) implementation for
* small-case English alphabets `a-z`
*/
class trie {
private:
static constexpr uint8_t NUM_CHARS = 26; ///< Number of alphabets
/** @brief Recursive tree nodes as an array of shared-pointers */
std::array<std::shared_ptr<trie>, NUM_CHARS << 1> arr;
bool isEndofWord = false; ///< identifier if a node is terminal node
// create a new node for trie
trie* createNode() {
trie* nn = new trie();
for (int i = 0; i < 26; i++) nn->arr[i] = NULL;
nn->isEndofWord = false;
return nn;
}
// insert string into the trie
void insert(trie* root, std::string str) {
for (int i = 0; i < str.length(); i++) {
int j = str[i] - 'a';
if (root->arr[j]) {
root = root->arr[j];
} else {
root->arr[j] = createNode();
root = root->arr[j];
/**
* @brief Convert a character to integer for indexing
*
* @param ch character to index
* @return unsigned integer index
*/
uint8_t char_to_int(const char& ch) const {
if (ch >= 'A' && ch <= 'Z') {
return ch - 'A';
} else if (ch >= 'a' && ch <= 'z') {
return ch - 'a' + NUM_CHARS;
}
}
root->isEndofWord = true;
}
// search a string exists inside the trie
bool search(trie* root, std::string str, int index) {
if (index == str.length()) {
if (!root->isEndofWord)
return false;
return true;
std::cerr << "Invalid character present. Exiting...";
std::exit(EXIT_FAILURE);
return 0;
}
int j = str[index] - 'a';
if (!root->arr[j])
return false;
return search(root->arr[j], str, index + 1);
}
/*
removes the string if it is not a prefix of any other
string, if it is then just sets the endofword to false, else
removes the given string
*/
bool deleteString(trie* root, std::string str, int index) {
if (index == str.length()) {
if (!root->isEndofWord)
return false;
root->isEndofWord = false;
for (int i = 0; i < 26; i++) return false;
return true;
}
int j = str[index] - 'a';
if (!root->arr[j])
return false;
bool var = deleteString(root, str, index + 1);
if (var) {
root->arr[j] = NULL;
if (root->isEndofWord) {
return false;
} else {
int i;
for (i = 0; i < 26; i++)
if (root->arr[i])
return false;
/** search a string exists inside a given root trie
* @param str string to search for
* @param index start index to search from
* @returns `true` if found
* @returns `false` if not found
*/
bool search(const std::shared_ptr<trie>& root, const std::string& str,
int index) {
if (index == str.length()) {
if (!root->isEndofWord) {
return false;
}
return true;
}
int j = char_to_int(str[index]);
if (!root->arr[j]) {
return false;
}
return search(root->arr[j], str, index + 1);
}
/* should not return here */
std::cout << __func__ << ":" << __LINE__ << "Should not reach this line\n";
return false;
public:
trie() = default; ///< Class default constructor
/** insert string into the trie
* @param str String to insert in the tree
*/
void insert(const std::string& str) {
std::shared_ptr<trie> root(nullptr);
for (const char& ch : str) {
int j = char_to_int(ch);
if (root) {
if (root->arr[j]) {
root = root->arr[j];
} else {
std::shared_ptr<trie> temp(new trie());
root->arr[j] = temp;
root = temp;
}
} else if (arr[j]) {
root = arr[j];
} else {
std::shared_ptr<trie> temp(new trie());
arr[j] = temp;
root = temp;
}
}
root->isEndofWord = true;
}
/** search a string exists inside the trie
* @param str string to search for
* @param index start index to search from
* @returns `true` if found
* @returns `false` if not found
*/
bool search(const std::string& str, int index) {
if (index == str.length()) {
if (!isEndofWord) {
return false;
}
return true;
}
int j = char_to_int(str[index]);
if (!arr[j]) {
return false;
}
return search(arr[j], str, index + 1);
}
/**
* removes the string if it is not a prefix of any other
* string, if it is then just sets the ::data_structure::trie::isEndofWord
* to false, else removes the given string
* @note the function ::data_structure::trie::deleteString might be
* erroneous
* @todo review the function ::data_structure::trie::deleteString and the
* commented lines
* @param str string to remove
* @param index index to remove from
* @returns `true` if successful
* @returns `false` if unsuccessful
*/
bool deleteString(const std::string& str, int index) {
if (index == str.length()) {
if (!isEndofWord) {
return false;
}
isEndofWord = false;
// following lines - possible source of error?
// for (int i = 0; i < NUM_CHARS; i++)
// if (!arr[i])
// return false;
return true;
}
int j = char_to_int(str[index]);
if (!arr[j]) {
return false;
}
bool var = deleteString(str, index + 1);
if (var) {
arr[j].reset();
if (isEndofWord) {
return false;
} else {
int i = 0;
for (i = 0; i < NUM_CHARS; i++) {
if (arr[i]) {
return false;
}
}
return true;
}
}
/* should not return here */
std::cout << __func__ << ":" << __LINE__
<< "Should not reach this line\n";
return false;
}
};
} // namespace data_structures
/**
* @brief Testing function
* @returns void
*/
static void test() {
data_structures::trie root;
root.insert("Hello");
root.insert("World");
assert(!root.search("hello", 0));
std::cout << "hello - " << root.search("hello", 0) << "\n";
assert(root.search("Hello", 0));
std::cout << "Hello - " << root.search("Hello", 0) << "\n";
assert(!root.search("Word", 0));
std::cout << "Word - " << root.search("Word", 0) << "\n";
assert(root.search("World", 0));
std::cout << "World - " << root.search("World", 0) << "\n";
// Following lines of code give erroneous output
// root.deleteString("hello", 0);
// assert(!root.search("hello", 0));
// std::cout << "hello - " << root.search("world", 0) << "\n";
}
/**
* @brief Main function
* @return 0 on exit
*/
int main() {
trie* root = createNode();
insert(root, "hello");
insert(root, "world");
int a = search(root, "hello", 0);
int b = search(root, "word", 0);
printf("%d %d ", a, b);
test();
return 0;
}

View File

@@ -1,25 +1,104 @@
/*Given a rod of length n inches and an array of prices that
contains prices of all pieces of size smaller than n. Determine
the maximum value obtainable by cutting up the rod and selling
the pieces.*/
/**
* @file
* @brief Implementation of cutting a rod problem
*
* @details
* Given a rod of length n inches and an array of prices that
* contains prices of all pieces of size<=n. Determine
* the maximum profit obtainable by cutting up the rod and selling
* the pieces.
*
* ### Algorithm
* The idea is to break the given rod into every smaller piece as possible
* and then check profit for each piece, by calculating maximum profit for
* smaller pieces we will build the solution for larger pieces in bottom-up
* manner.
*
* @author [Anmol](https://github.com/Anmol3299)
* @author [Pardeep](https://github.com/Pardeep009)
*/
#include <array>
#include <cassert>
#include <climits>
#include <iostream>
using namespace std;
int cutrod(int p[], int n) {
int r[n + 1];
r[0] = 0;
for (int j = 0; j < n; j++) {
/**
* @namespace dynamic_programming
* @brief Dynamic Programming algorithms
*/
namespace dynamic_programming {
/**
* @namespace cut_rod
* @brief Implementation of cutting a rod problem
*/
namespace cut_rod {
/**
* @brief Cuts the rod in different pieces and
* stores the maximum profit for each piece of the rod.
* @tparam T size of the price array
* @param n size of the rod in inches
* @param price an array of prices that contains prices of all pieces of size<=n
* @return maximum profit obtainable for @param n inch rod.
*/
template <size_t T>
int maxProfitByCuttingRod(const std::array<int, T> &price, const int n) {
int *profit =
new int[n + 1]; // profit[i] will hold maximum profit for i inch rod
profit[0] = 0; // if length of rod is zero, then no profit
// outer loop will select size of rod, starting from 1 inch to n inch rod.
// inner loop will evaluate the maximum profit we can get for i inch rod by
// making every possible cut on it and will store it in profit[i].
for (size_t i = 1; i <= n; i++) {
int q = INT_MIN;
for (int i = 0; i <= j; i++) {
q = max(q, p[i] + r[j - i]);
for (size_t j = 1; j <= i; j++) {
q = std::max(q, price[j - 1] + profit[i - j]);
}
r[j + 1] = q;
profit[i] = q;
}
return r[n];
int ans = profit[n];
delete[] profit;
return ans; // returning maximum profit
}
} // namespace cut_rod
} // namespace dynamic_programming
/**
* @brief Function to test above algorithm
* @returns void
*/
static void test() {
// Test 1
const int n1 = 8; // size of rod
std::array<int, n1> price1 = {1, 5, 8, 9, 10, 17, 17, 20}; // price array
const int max_profit1 =
dynamic_programming::cut_rod::maxProfitByCuttingRod(price1, n1);
const int expected_max_profit1 = 22;
assert(max_profit1 == expected_max_profit1);
std::cout << "Maximum profit with " << n1 << " inch road is " << max_profit1
<< std::endl;
// Test 2
const int n2 = 30; // size of rod
std::array<int, n2> price2 = {
1, 5, 8, 9, 10, 17, 17, 20, 24, 30, // price array
31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50};
const int max_profit2 =
dynamic_programming::cut_rod::maxProfitByCuttingRod(price2, n2);
const int expected_max_profit2 = 90;
assert(max_profit2 == expected_max_profit2);
std::cout << "Maximum profit with " << n2 << " inch road is " << max_profit2
<< std::endl;
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
int price[] = {1, 5, 8, 9, 10, 17, 17, 20, 24, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50};
cout << cutrod(price, 30);
// Testing
test();
return 0;
}

View File

@@ -1,22 +0,0 @@
#include <iostream>
using namespace std;
int arr[1000000];
int fib(int n) {
if (arr[n] == -1) {
if (n <= 1)
arr[n] = n;
else
arr[n] = fib(n - 1) + fib(n - 2);
}
return arr[n];
}
int main(int argc, char const *argv[]) {
int n;
cout << "Enter n: ";
cin >> n;
for (int i = 0; i < n + 1; ++i) {
arr[i] = -1;
}
cout << "Fibonacci number is " << fib(n) << endl;
return 0;
}

View File

@@ -0,0 +1,74 @@
/**
* @file
* @brief Implementation of [Kadane
* Algorithm] (https://en.wikipedia.org/wiki/Kadane%27s_algorithm)
*
* @details
* Kadane algorithm is used to find the maximum sum subarray in an array and
* maximum sum subarray problem is the task of finding a contiguous subarray
* with the largest sum
*
* ### Algorithm
* The simple idea of the algorithm is to search for all positive
* contiguous segments of the array and keep track of maximum sum contiguous
* segment among all positive segments(curr_sum is used for this)
* Each time we get a positive sum we compare it with max_sum and update max_sum
* if it is greater than curr_sum
*
* @author [Ayush Singh](https://github.com/ayush523)
*/
#include <array>
#include <climits>
#include <iostream>
/**
* @namespace dynamic_programming
* @brief Dynamic Programming algorithms
*/
namespace dynamic_programming {
/**
* @namespace kadane
* @brief Functions for
* [Kadane](https://en.wikipedia.org/wiki/Kadane%27s_algorithm) algorithm.
*/
namespace kadane {
/**
* @brief maxSubArray function is used to calculate the maximum sum subarray
* and returns the value of maximum sum which is stored in the variable max_sum
* @tparam N number of array size
* @param n array where numbers are saved
* @returns the value of maximum subarray sum
*/
template <size_t N>
int maxSubArray(const std::array<int, N> &n) {
int curr_sum =
0; // declaring a variable named as curr_sum and initialized it to 0
int max_sum = INT_MIN; // Initialized max_sum to INT_MIN
for (int i : n) { // for loop to iterate over the elements of the array
curr_sum += n[i];
max_sum = std::max(max_sum, curr_sum); // getting the maximum value
curr_sum = std::max(curr_sum, 0); // updating the value of curr_sum
}
return max_sum; // returning the value of max_sum
}
} // namespace kadane
} // namespace dynamic_programming
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
const int N = 5;
std::array<int, N> n{}; // declaring array
// taking values of elements from user
for (int i = 0; i < n.size(); i++) {
std::cout << "Enter value of n[" << i << "]"
<< "\n";
std::cin >> n[i];
}
int max_sum = dynamic_programming::kadane::maxSubArray<N>(
n); // calling maxSubArray function
std::cout << "Maximum subarray sum is " << max_sum; // Printing the answer
return 0;
}

View File

@@ -0,0 +1,186 @@
/**
* @file
* @brief [Word Break Problem](https://leetcode.com/problems/word-break/)
* @details
* Given a non-empty string s and a dictionary wordDict containing a list of
* non-empty words, determine if s can be segmented into a space-separated
* sequence of one or more dictionary words.
*
* Note:
* The same word in the dictionary may be reused multiple times in the
* segmentation. You may assume the dictionary does not contain duplicate words.
*
* Example 1:
* Input: s = "leetcode", wordDict = ["leet", "code"]
* Output: true
* Explanation: Return true because "leetcode" can be segmented as "leet code".
*
* Example 2:
* Input: s = "applepenapple", wordDict = ["apple", "pen"]
* Output: true
* Explanation: Return true because "applepenapple" can be segmented as "apple
* pen apple". Note that you are allowed to reuse a dictionary word.
*
* Example 3:
* Input: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
* Output: false
*
* @author [Akshay Anand] (https://github.com/axayjha)
*/
#include <cassert>
#include <climits>
#include <iostream>
#include <string>
#include <unordered_set>
#include <vector>
/**
* @namespace dynamic_programming
* @brief Dynamic programming algorithms
*/
namespace dynamic_programming {
/**
* @namespace word_break
* @brief Functions for [Word Break](https://leetcode.com/problems/word-break/)
* problem
*/
namespace word_break {
/**
* @brief Function that checks if the string passed in param is present in
* the the unordered_set passed
*
* @param str the string to be searched
* @param strSet unordered set of string, that is to be looked into
* @returns `true` if str is present in strSet
* @returns `false` if str is not present in strSet
*/
bool exists(const std::string &str,
const std::unordered_set<std::string> &strSet) {
return strSet.find(str) != strSet.end();
}
/**
* @brief Function that checks if the string passed in param can be
* segmented from position 'pos', and then correctly go on to segment the
* rest of the string correctly as well to reach a solution
*
* @param s the complete string to be segmented
* @param strSet unordered set of string, that is to be used as the
* reference dictionary
* @param pos the index value at which we will segment string and test
* further if it is correctly segmented at pos
* @param dp the vector to memoize solution for each position
* @returns `true` if a valid solution/segmentation is possible by segmenting at
* index pos
* @returns `false` otherwise
*/
bool check(const std::string &s, const std::unordered_set<std::string> &strSet,
int pos, std::vector<int> *dp) {
if (pos == s.length()) {
// if we have reached till the end of the string, means we have
// segmented throughout correctly hence we have a solution, thus
// returning true
return true;
}
if (dp->at(pos) != INT_MAX) {
// if dp[pos] is not INT_MAX, means we must have saved a solution
// for the position pos; then return if the solution at pos is true
// or not
return dp->at(pos) == 1;
}
std::string wordTillNow =
""; // string to save the prefixes of word till different positons
for (int i = pos; i < s.length(); i++) {
// Loop starting from pos to end, to check valid set of
// segmentations if any
wordTillNow +=
std::string(1, s[i]); // storing the prefix till the position i
// if the prefix till current position is present in the dictionary
// and the remaining substring can also be segmented legally, then
// set solution at position pos in the memo, and return true
if (exists(wordTillNow, strSet) and check(s, strSet, i + 1, dp)) {
dp->at(pos) = 1;
return true;
}
}
// if function has still not returned, then there must be no legal
// segmentation possible after segmenting at pos
dp->at(pos) = 0; // so set solution at pos as false
return false; // and return no solution at position pos
}
/**
* @brief Function that checks if the string passed in param can be
* segmented into the strings present in the vector.
* In others words, it checks if any permutation of strings in
* the vector can be concatenated to form the final string.
*
* @param s the complete string to be segmented
* @param wordDict a vector of words to be used as dictionary to look into
* @returns `true` if s can be formed by a combination of strings present in
* wordDict
* @return `false` otherwise
*/
bool wordBreak(const std::string &s, const std::vector<std::string> &wordDict) {
// unordered set to store words in the dictionary for constant time
// search
std::unordered_set<std::string> strSet;
for (const auto &s : wordDict) {
strSet.insert(s);
}
// a vector to be used for memoization, whose value at index i will
// tell if the string s can be segmented (correctly) at position i.
// initializing it with INT_MAX (which will denote no solution)
std::vector<int> dp(s.length(), INT_MAX);
// calling check method with position = 0, to check from left
// from where can be start segmenting the complete string in correct
// manner
return check(s, strSet, 0, &dp);
}
} // namespace word_break
} // namespace dynamic_programming
/**
* @brief Test implementations
* @returns void
*/
static void test() {
// the complete string
const std::string s = "applepenapple";
// the dictionary to be used
const std::vector<std::string> wordDict = {"apple", "pen"};
assert(dynamic_programming::word_break::wordBreak(s, wordDict));
// should return true, as applepenapple can be segmented as apple + pen +
// apple
std::cout << dynamic_programming::word_break::wordBreak(s, wordDict)
<< std::endl;
std::cout << "Test implementation passed!\n";
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
test(); // call the test function :)
// the complete string
const std::string s = "applepenapple";
// the dictionary to be used
const std::vector<std::string> wordDict = {"apple", "pen"};
// should return true, as applepenapple can be segmented as apple + pen +
// apple
std::cout << dynamic_programming::word_break::wordBreak(s, wordDict)
<< std::endl;
}

20
graph/CMakeLists.txt Normal file
View File

@@ -0,0 +1,20 @@
#If necessary, use the RELATIVE flag, otherwise each source file may be listed
#with full pathname.RELATIVE may makes it easier to extract an executable name
#automatically.
file(GLOB APP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp)
#file(GLOB APP_SOURCES ${CMAKE_SOURCE_DIR}/*.c )
# AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} APP_SOURCES)
foreach( testsourcefile ${APP_SOURCES} )
# I used a simple string replace, to cut off .cpp.
string( REPLACE ".cpp" "" testname ${testsourcefile} )
add_executable( ${testname} ${testsourcefile} )
set_target_properties(${testname} PROPERTIES
LINKER_LANGUAGE CXX
)
if(OpenMP_CXX_FOUND)
target_link_libraries(${testname} OpenMP::OpenMP_CXX)
endif()
install(TARGETS ${testname} DESTINATION "bin/graph")
endforeach( testsourcefile ${APP_SOURCES} )

View File

@@ -54,6 +54,17 @@
* \brief Graph algorithms
*/
namespace graph {
/**
* \brief Representation of the graph as an adjacency list.
*
* For every vertex, there is a list of its neighbors in the order in which
* they were added to the graph. By default, the edges are directed, but
* an undirected graph can be represented simply by storing each each as
* two directed edges in both directions.
*/
using adjacency_list = std::vector<std::vector<int>>;
/**
* \brief
* Adds a directed edge from vertex u to vertex v.
@@ -63,7 +74,7 @@ namespace graph {
* @param v second vertex
*
*/
void add_directed_edge(std::vector<std::vector<int>> *graph, int u, int v) {
void add_directed_edge(adjacency_list *graph, int u, int v) {
(*graph)[u].push_back(v);
}
@@ -78,7 +89,7 @@ void add_directed_edge(std::vector<std::vector<int>> *graph, int u, int v) {
* @param v second vertex
*
*/
void add_undirected_edge(std::vector<std::vector<int>> *graph, int u, int v) {
void add_undirected_edge(adjacency_list *graph, int u, int v) {
add_directed_edge(graph, u, v);
add_directed_edge(graph, v, u);
}
@@ -89,14 +100,14 @@ void add_undirected_edge(std::vector<std::vector<int>> *graph, int u, int v) {
*
* @param graph Adjacency list representation of graph
* @param start vertex from where traversing starts
* @returns a binary vector indicating which vertices were visited during the search.
* @returns a binary vector indicating which vertices were visited during the
* search.
*
*/
std::vector<bool> breadth_first_search(const std::vector<std::vector<int>> &graph,
int start) {
std::vector<bool> breadth_first_search(const adjacency_list &graph, int start) {
/// vector to keep track of visited vertices
std::vector<bool> visited(graph.size(), false);
/// a queue that stores vertices that need to be further explored
/// queue that stores vertices that need to be further explored
std::queue<int> tracker;
/// mark the starting vertex as visited
@@ -116,11 +127,13 @@ std::vector<bool> breadth_first_search(const std::vector<std::vector<int>> &grap
}
return visited;
}
} // namespace graph
void tests() {
/** Test function */
static void tests() {
/// Test 1 Begin
std::vector<std::vector<int>> graph(4, std::vector<int>());
graph::adjacency_list graph(4, std::vector<int>());
graph::add_undirected_edge(&graph, 0, 1);
graph::add_undirected_edge(&graph, 1, 2);
graph::add_undirected_edge(&graph, 2, 3);
@@ -167,7 +180,7 @@ int main() {
std::cout << "Enter the number of edges: ";
std::cin >> edges;
std::vector<std::vector<int>> graph(vertices);
graph::adjacency_list graph(vertices);
std::cout << "Enter space-separated pairs of vertices that form edges: "
<< std::endl;

View File

@@ -27,14 +27,14 @@ class Solution {
bridge.push_back({itr, current_node});
}
}
out_time[current_node] = std::min(out_time[current_node], out_time[itr]);
out_time[current_node] =
std::min(out_time[current_node], out_time[itr]);
}
}
public:
std::vector<std::vector<int>> search_bridges(
int n,
const std::vector<std::vector<int>>& connections) {
int n, const std::vector<std::vector<int>>& connections) {
timer = 0;
graph.resize(n);
in_time.assign(n, 0);
@@ -73,7 +73,8 @@ int main() {
* I assumed that the graph is bi-directional and connected.
*
*/
std::vector<std::vector<int>> bridges = s1.search_bridges(number_of_node, node);
std::vector<std::vector<int>> bridges =
s1.search_bridges(number_of_node, node);
std::cout << bridges.size() << " bridges found!\n";
for (auto& itr : bridges) {
std::cout << itr[0] << " --> " << itr[1] << '\n';

View File

@@ -53,7 +53,7 @@ using AdjList = std::map<unsigned int, std::vector<unsigned int>>;
*/
class Graph {
public:
Graph() : m_vertices(0), m_adjList({}) {}
Graph() : m_adjList({}) {}
~Graph() = default;
Graph(Graph&&) = default;
Graph& operator=(Graph&&) = default;
@@ -65,8 +65,8 @@ class Graph {
* @param vertices specify the number of vertices the graph would contain.
* @param adjList is the adjacency list representation of graph.
*/
Graph(unsigned int vertices, AdjList const& adjList)
: m_vertices(vertices), m_adjList(adjList) {}
Graph(unsigned int vertices, AdjList adjList)
: m_vertices(vertices), m_adjList(std::move(adjList)) {}
/** Create a graph from vertices and adjacency list.
*
@@ -142,7 +142,7 @@ class Graph {
}
private:
unsigned int m_vertices;
unsigned int m_vertices = 0;
AdjList m_adjList;
};

View File

@@ -0,0 +1,133 @@
/**
*
* \file
* \brief [Depth First Search Algorithm
* (Depth First Search)](https://en.wikipedia.org/wiki/Depth-first_search)
*
* \author [Ayaan Khan](http://github.com/ayaankhan98)
*
* \details
* Depth First Search also quoted as DFS is a Graph Traversal Algorithm.
* Time Complexity O(|V| + |E|) where V is number of vertices and E
* is number of edges in graph.
*
* Application of Depth First Search are
*
* 1. Finding connected components
* 2. Finding 2-(edge or vertex)-connected components.
* 3. Finding 3-(edge or vertex)-connected components.
* 4. Finding the bridges of a graph.
* 5. Generating words in order to plot the limit set of a group.
* 6. Finding strongly connected components.
*
* And there are many more...
*
* <h4>Working</h4>
* 1. Mark all vertices as unvisited first
* 2. start exploring from some starting vertex.
*
* While exploring vertex we mark the vertex as visited
* and start exploring the vertices connected to this
* vertex in recursive way.
*
*/
#include <algorithm>
#include <iostream>
#include <vector>
/**
*
* \namespace graph
* \brief Graph Algorithms
*
*/
namespace graph {
/**
* \brief
* Adds and edge between two vertices of graph say u and v in this
* case.
*
* @param adj Adjacency list representation of graph
* @param u first vertex
* @param v second vertex
*
*/
void addEdge(std::vector<std::vector<size_t>> *adj, size_t u, size_t v) {
/*
*
* Here we are considering undirected graph that's the
* reason we are adding v to the adjacency list representation of u
* and also adding u to the adjacency list representation of v
*
*/
(*adj)[u - 1].push_back(v - 1);
(*adj)[v - 1].push_back(u - 1);
}
/**
*
* \brief
* Explores the given vertex, exploring a vertex means traversing
* over all the vertices which are connected to the vertex that is
* currently being explored.
*
* @param adj garph
* @param v vertex to be explored
* @param visited already visited vertices
*
*/
void explore(const std::vector<std::vector<size_t>> &adj, size_t v,
std::vector<bool> *visited) {
std::cout << v + 1 << " ";
(*visited)[v] = true;
for (auto x : adj[v]) {
if (!(*visited)[x]) {
explore(adj, x, visited);
}
}
}
/**
* \brief
* initiates depth first search algorithm.
*
* @param adj adjacency list of graph
* @param start vertex from where DFS starts traversing.
*
*/
void depth_first_search(const std::vector<std::vector<size_t>> &adj,
size_t start) {
size_t vertices = adj.size();
std::vector<bool> visited(vertices, false);
explore(adj, start, &visited);
}
} // namespace graph
/** Main function */
int main() {
size_t vertices = 0, edges = 0;
std::cout << "Enter the Vertices : ";
std::cin >> vertices;
std::cout << "Enter the Edges : ";
std::cin >> edges;
/// creating graph
std::vector<std::vector<size_t>> adj(vertices, std::vector<size_t>());
/// taking input for edges
std::cout << "Enter the vertices which have edges between them : "
<< std::endl;
while (edges--) {
size_t u = 0, v = 0;
std::cin >> u >> v;
graph::addEdge(&adj, u, v);
}
/// running depth first search over graph
graph::depth_first_search(adj, 2);
std::cout << std::endl;
return 0;
}

View File

@@ -0,0 +1,46 @@
#include <iostream>
#include <list>
#include <stack>
#include <vector>
constexpr int WHITE = 0;
constexpr int GREY = 1;
constexpr int BLACK = 2;
constexpr int INF = 99999;
void dfs(const std::vector<std::list<int> > &graph, int start) {
std::vector<int> checked(graph.size(), WHITE);
checked[start] = GREY;
std::stack<int> stack;
stack.push(start);
while (!stack.empty()) {
int act = stack.top();
stack.pop();
if (checked[act] == GREY) {
std::cout << act << ' ';
for (auto it : graph[act]) {
stack.push(it);
if (checked[it] != BLACK) {
checked[it] = GREY;
}
}
checked[act] = BLACK; // nodo controllato
}
}
}
int main() {
int n = 0;
std::cin >> n;
std::vector<std::list<int> > graph(INF);
for (int i = 0; i < n; ++i) {
int u = 0, w = 0;
std::cin >> u >> w;
graph[u].push_back(w);
}
dfs(graph, 0);
return 0;
}

View File

@@ -1,26 +0,0 @@
#include <iostream>
using namespace std;
int v = 4;
void DFSUtil_(int graph[4][4], bool visited[], int s) {
visited[s] = true;
cout << s << " ";
for (int i = 0; i < v; i++) {
if (graph[s][i] == 1 && visited[i] == false) {
DFSUtil_(graph, visited, i);
}
}
}
void DFS_(int graph[4][4], int s) {
bool visited[v];
memset(visited, 0, sizeof(visited));
DFSUtil_(graph, visited, s);
}
int main() {
int graph[4][4] = {{0, 1, 1, 0}, {0, 0, 1, 0}, {1, 0, 0, 1}, {0, 0, 0, 1}};
cout << "DFS: ";
DFS_(graph, 2);
cout << endl;
return 0;
}

View File

@@ -1,51 +0,0 @@
#include <iostream>
#include <list>
#include <stack>
#define WHITE 0
#define GREY 1
#define BLACK 2
#define INF 99999
using namespace std;
int checked[999] = {WHITE};
void dfs(const list<int> lista[], int start) {
stack<int> stack;
int checked[999] = {WHITE};
stack.push(start);
checked[start] = GREY;
while (!stack.empty()) {
int act = stack.top();
stack.pop();
if (checked[act] == GREY) {
cout << act << ' ';
for (auto it = lista[act].begin(); it != lista[act].end(); ++it) {
stack.push(*it);
if (checked[*it] != BLACK)
checked[*it] = GREY;
}
checked[act] = BLACK; // nodo controllato
}
}
}
int main() {
int u, w;
int n;
cin >> n;
list<int> lista[INF];
for (int i = 0; i < n; ++i) {
cin >> u >> w;
lista[u].push_back(w);
}
dfs(lista, 0);
return 0;
}

View File

@@ -26,10 +26,10 @@
#include <cassert>
#include <iostream>
#include <limits>
#include <memory>
#include <queue>
#include <utility>
#include <vector>
#include <memory>
constexpr int64_t INF = std::numeric_limits<int64_t>::max();
@@ -39,31 +39,31 @@ constexpr int64_t INF = std::numeric_limits<int64_t>::max();
*/
namespace graph {
/**
* @brief Function that add edge between two nodes or vertices of graph
*
* @param u any node or vertex of graph
* @param v any node or vertex of graph
*/
void addEdge(std::vector<std::vector<std::pair<int, int>>> *adj, int u, int v,
int w) {
/**
* @brief Function that add edge between two nodes or vertices of graph
*
* @param u any node or vertex of graph
* @param v any node or vertex of graph
*/
void addEdge(std::vector<std::vector<std::pair<int, int>>> *adj, int u, int v,
int w) {
(*adj)[u - 1].push_back(std::make_pair(v - 1, w));
// (*adj)[v - 1].push_back(std::make_pair(u - 1, w));
}
}
/**
* @brief Function runs the dijkstra algorithm for some source vertex and
* target vertex in the graph and returns the shortest distance of target
* from the source.
*
* @param adj input graph
* @param s source vertex
* @param t target vertex
*
* @return shortest distance if target is reachable from source else -1 in
* case if target is not reachable from source.
*/
int dijkstra(std::vector<std::vector<std::pair<int, int>>> *adj, int s, int t) {
/**
* @brief Function runs the dijkstra algorithm for some source vertex and
* target vertex in the graph and returns the shortest distance of target
* from the source.
*
* @param adj input graph
* @param s source vertex
* @param t target vertex
*
* @return shortest distance if target is reachable from source else -1 in
* case if target is not reachable from source.
*/
int dijkstra(std::vector<std::vector<std::pair<int, int>>> *adj, int s, int t) {
/// n denotes the number of vertices in graph
int n = adj->size();
@@ -74,7 +74,7 @@ namespace graph {
/// first element of pair contains the distance
/// second element of pair contains the vertex
std::priority_queue<std::pair<int, int>, std::vector<std::pair<int, int>>,
std::greater<std::pair<int, int>>>
std::greater<std::pair<int, int>>>
pq;
/// pushing the source vertex 's' with 0 distance in min heap
@@ -84,97 +84,97 @@ namespace graph {
dist[s] = 0;
while (!pq.empty()) {
/// second element of pair denotes the node / vertex
int currentNode = pq.top().second;
/// second element of pair denotes the node / vertex
int currentNode = pq.top().second;
/// first element of pair denotes the distance
int currentDist = pq.top().first;
/// first element of pair denotes the distance
int currentDist = pq.top().first;
pq.pop();
pq.pop();
/// for all the reachable vertex from the currently exploring vertex
/// we will try to minimize the distance
for (std::pair<int, int> edge : (*adj)[currentNode]) {
/// minimizing distances
if (currentDist + edge.second < dist[edge.first]) {
dist[edge.first] = currentDist + edge.second;
pq.push(std::make_pair(dist[edge.first], edge.first));
/// for all the reachable vertex from the currently exploring vertex
/// we will try to minimize the distance
for (std::pair<int, int> edge : (*adj)[currentNode]) {
/// minimizing distances
if (currentDist + edge.second < dist[edge.first]) {
dist[edge.first] = currentDist + edge.second;
pq.push(std::make_pair(dist[edge.first], edge.first));
}
}
}
}
if (dist[t] != INF) {
return dist[t];
return dist[t];
}
return -1;
}
}
} // namespace graph
/** Function to test the Algorithm */
void tests() {
std::cout << "Initiatinig Predefined Tests..." << std::endl;
std::cout << "Initiating Test 1..." << std::endl;
std::vector<std::vector<std::pair<int, int>>> adj1(
4, std::vector<std::pair<int, int>>());
graph::addEdge(&adj1, 1, 2, 1);
graph::addEdge(&adj1, 4, 1, 2);
graph::addEdge(&adj1, 2, 3, 2);
graph::addEdge(&adj1, 1, 3, 5);
std::cout << "Initiatinig Predefined Tests..." << std::endl;
std::cout << "Initiating Test 1..." << std::endl;
std::vector<std::vector<std::pair<int, int>>> adj1(
4, std::vector<std::pair<int, int>>());
graph::addEdge(&adj1, 1, 2, 1);
graph::addEdge(&adj1, 4, 1, 2);
graph::addEdge(&adj1, 2, 3, 2);
graph::addEdge(&adj1, 1, 3, 5);
int s = 1, t = 3;
assert(graph::dijkstra(&adj1, s - 1, t - 1) == 3);
std::cout << "Test 1 Passed..." << std::endl;
int s = 1, t = 3;
assert(graph::dijkstra(&adj1, s - 1, t - 1) == 3);
std::cout << "Test 1 Passed..." << std::endl;
s = 4, t = 3;
std::cout << "Initiating Test 2..." << std::endl;
assert(graph::dijkstra(&adj1, s - 1, t - 1) == 5);
std::cout << "Test 2 Passed..." << std::endl;
s = 4, t = 3;
std::cout << "Initiating Test 2..." << std::endl;
assert(graph::dijkstra(&adj1, s - 1, t - 1) == 5);
std::cout << "Test 2 Passed..." << std::endl;
std::vector<std::vector<std::pair<int, int>>> adj2(
5, std::vector<std::pair<int, int>>());
graph::addEdge(&adj2, 1, 2, 4);
graph::addEdge(&adj2, 1, 3, 2);
graph::addEdge(&adj2, 2, 3, 2);
graph::addEdge(&adj2, 3, 2, 1);
graph::addEdge(&adj2, 2, 4, 2);
graph::addEdge(&adj2, 3, 5, 4);
graph::addEdge(&adj2, 5, 4, 1);
graph::addEdge(&adj2, 2, 5, 3);
graph::addEdge(&adj2, 3, 4, 4);
std::vector<std::vector<std::pair<int, int>>> adj2(
5, std::vector<std::pair<int, int>>());
graph::addEdge(&adj2, 1, 2, 4);
graph::addEdge(&adj2, 1, 3, 2);
graph::addEdge(&adj2, 2, 3, 2);
graph::addEdge(&adj2, 3, 2, 1);
graph::addEdge(&adj2, 2, 4, 2);
graph::addEdge(&adj2, 3, 5, 4);
graph::addEdge(&adj2, 5, 4, 1);
graph::addEdge(&adj2, 2, 5, 3);
graph::addEdge(&adj2, 3, 4, 4);
s = 1, t = 5;
std::cout << "Initiating Test 3..." << std::endl;
assert(graph::dijkstra(&adj2, s - 1, t - 1) == 6);
std::cout << "Test 3 Passed..." << std::endl;
std::cout << "All Test Passed..." << std::endl << std::endl;
s = 1, t = 5;
std::cout << "Initiating Test 3..." << std::endl;
assert(graph::dijkstra(&adj2, s - 1, t - 1) == 6);
std::cout << "Test 3 Passed..." << std::endl;
std::cout << "All Test Passed..." << std::endl << std::endl;
}
/** Main function */
int main() {
// running predefined tests
tests();
// running predefined tests
tests();
int vertices = int(), edges = int();
std::cout << "Enter the number of vertices : ";
std::cin >> vertices;
std::cout << "Enter the number of edges : ";
std::cin >> edges;
int vertices = int(), edges = int();
std::cout << "Enter the number of vertices : ";
std::cin >> vertices;
std::cout << "Enter the number of edges : ";
std::cin >> edges;
std::vector<std::vector<std::pair<int, int>>> adj(
vertices, std::vector<std::pair<int, int>>());
std::vector<std::vector<std::pair<int, int>>> adj(
vertices, std::vector<std::pair<int, int>>());
int u = int(), v = int(), w = int();
while (edges--) {
std::cin >> u >> v >> w;
graph::addEdge(&adj, u, v, w);
}
int u = int(), v = int(), w = int();
while (edges--) {
std::cin >> u >> v >> w;
graph::addEdge(&adj, u, v, w);
}
int s = int(), t = int();
std::cin >> s >> t;
int dist = graph::dijkstra(&adj, s - 1, t - 1);
if (dist == -1) {
std::cout << "Target not reachable from source" << std::endl;
} else {
std::cout << "Shortest Path Distance : " << dist << std::endl;
}
return 0;
int s = int(), t = int();
std::cin >> s >> t;
int dist = graph::dijkstra(&adj, s - 1, t - 1);
if (dist == -1) {
std::cout << "Target not reachable from source" << std::endl;
} else {
std::cout << "Shortest Path Distance : " << dist << std::endl;
}
return 0;
}

View File

@@ -28,49 +28,49 @@
* @return `false` if there is no Hamiltonian cycle in the graph
*/
bool hamilton_cycle(const std::vector<std::vector<bool>> &routes) {
const size_t n = routes.size();
// height of dp array which is 2^n
const size_t height = 1 << n;
std::vector<std::vector<bool>> dp(height, std::vector<bool>(n, false));
const size_t n = routes.size();
// height of dp array which is 2^n
const size_t height = 1 << n;
std::vector<std::vector<bool>> dp(height, std::vector<bool>(n, false));
// to fill in the [2^i, i] cells with true
for (size_t i = 0; i < n; ++i) {
dp[1 << i][i] = true;
}
for (size_t i = 1; i < height; i++) {
std::vector<size_t> zeros, ones;
// finding positions with 1s and 0s and separate them
for (size_t pos = 0; pos < n; ++pos) {
if ((1 << pos) & i) {
ones.push_back(pos);
} else {
zeros.push_back(pos);
}
// to fill in the [2^i, i] cells with true
for (size_t i = 0; i < n; ++i) {
dp[1 << i][i] = true;
}
for (auto &o : ones) {
if (!dp[i][o]) {
continue;
}
for (auto &z : zeros) {
if (!routes[o][z]) {
continue;
for (size_t i = 1; i < height; i++) {
std::vector<size_t> zeros, ones;
// finding positions with 1s and 0s and separate them
for (size_t pos = 0; pos < n; ++pos) {
if ((1 << pos) & i) {
ones.push_back(pos);
} else {
zeros.push_back(pos);
}
}
dp[i + (1 << z)][z] = true;
}
}
}
bool is_cycle = false;
for (size_t i = 0; i < n; i++) {
is_cycle |= dp[height - 1][i];
if (is_cycle) { // if true, all subsequent loop will be true. hence
// break
break;
for (auto &o : ones) {
if (!dp[i][o]) {
continue;
}
for (auto &z : zeros) {
if (!routes[o][z]) {
continue;
}
dp[i + (1 << z)][z] = true;
}
}
}
}
return is_cycle;
bool is_cycle = false;
for (size_t i = 0; i < n; i++) {
is_cycle |= dp[height - 1][i];
if (is_cycle) { // if true, all subsequent loop will be true. hence
// break
break;
}
}
return is_cycle;
}
/**
@@ -79,16 +79,16 @@ bool hamilton_cycle(const std::vector<std::vector<bool>> &routes) {
* @return None
*/
static void test1() {
std::vector<std::vector<bool>> arr{
std::vector<bool>({true, true, false, false}),
std::vector<bool>({false, true, true, false}),
std::vector<bool>({false, false, true, true}),
std::vector<bool>({false, false, false, true})};
std::vector<std::vector<bool>> arr{
std::vector<bool>({true, true, false, false}),
std::vector<bool>({false, true, true, false}),
std::vector<bool>({false, false, true, true}),
std::vector<bool>({false, false, false, true})};
bool ans = hamilton_cycle(arr);
std::cout << "Test 1... ";
assert(ans);
std::cout << "passed\n";
bool ans = hamilton_cycle(arr);
std::cout << "Test 1... ";
assert(ans);
std::cout << "passed\n";
}
/**
@@ -101,17 +101,17 @@ static void test1() {
* @return None
*/
static void test2() {
std::vector<std::vector<bool>> arr{
std::vector<bool>({true, true, false, false}),
std::vector<bool>({false, true, true, true}),
std::vector<bool>({false, false, true, false}),
std::vector<bool>({false, false, false, true})};
std::vector<std::vector<bool>> arr{
std::vector<bool>({true, true, false, false}),
std::vector<bool>({false, true, true, true}),
std::vector<bool>({false, false, true, false}),
std::vector<bool>({false, false, false, true})};
bool ans = hamilton_cycle(arr);
bool ans = hamilton_cycle(arr);
std::cout << "Test 2... ";
assert(!ans); // not a cycle
std::cout << "passed\n";
std::cout << "Test 2... ";
assert(!ans); // not a cycle
std::cout << "passed\n";
}
/**
@@ -120,17 +120,17 @@ static void test2() {
* @return None
*/
static void test3() {
std::vector<std::vector<bool>> arr{
std::vector<bool>({true, true, true, true}),
std::vector<bool>({true, true, true, true}),
std::vector<bool>({true, true, true, true}),
std::vector<bool>({true, true, true, true})};
std::vector<std::vector<bool>> arr{
std::vector<bool>({true, true, true, true}),
std::vector<bool>({true, true, true, true}),
std::vector<bool>({true, true, true, true}),
std::vector<bool>({true, true, true, true})};
bool ans = hamilton_cycle(arr);
bool ans = hamilton_cycle(arr);
std::cout << "Test 3... ";
assert(ans);
std::cout << "passed\n";
std::cout << "Test 3... ";
assert(ans);
std::cout << "passed\n";
}
/**
@@ -140,8 +140,8 @@ static void test3() {
* @param argv commandline array of arguments (ignored)
*/
int main(int argc, char **argv) {
test1();
test2();
test3();
return 0;
test1();
test2();
test3();
return 0;
}

325
graph/hopcroft_karp.cpp Normal file
View File

@@ -0,0 +1,325 @@
/**
* @file
* @brief Implementation of [HopcroftKarp](https://en.wikipedia.org/wiki/Hopcroft%E2%80%93Karp_algorithm) algorithm.
* @details
* The HopcroftKarp algorithm is an algorithm that takes as input a bipartite graph
* and produces as output a maximum cardinality matching, it runs in O(E√V) time in worst case.
*
* ### Bipartite graph
* A bipartite graph (or bigraph) is a graph whose vertices can be divided into two disjoint
* and independent sets U and V such that every edge connects a vertex in U to one in V.
* Vertex sets U and V are usually called the parts of the graph.
* Equivalently, a bipartite graph is a graph that does not contain any odd-length cycles.
*
* ### Matching and Not-Matching edges
* Given a matching M, edges that are part of matching are called Matching edges and edges that are not part
* of M (or connect free nodes) are called Not-Matching edges.
*
* ### Maximum cardinality matching
* Given a bipartite graphs G = ( V = ( X , Y ) , E ) whose partition has the parts X and Y,
* with E denoting the edges of the graph, the goal is to find a matching with as many edges as possible.
* Equivalently, a matching that covers as many vertices as possible.
*
* ### Augmenting paths
* Given a matching M, an augmenting path is an alternating path that starts from and ends on free vertices.
* All single edge paths that start and end with free vertices are augmenting paths.
*
*
* ### Concept
* A matching M is not maximum if there exists an augmenting path. It is also true other way,
* i.e, a matching is maximum if no augmenting path exists.
*
*
* ### Algorithm
* 1) Initialize the Maximal Matching M as empty.
* 2) While there exists an Augmenting Path P
* Remove matching edges of P from M and add not-matching edges of P to M
* (This increases size of M by 1 as P starts and ends with a free vertex
* i.e. a node that is not part of matching.)
* 3) Return M.
*
*
*
* @author [Krishna Pal Deora](https://github.com/Krishnapal4050)
*
*/
#include <iostream>
#include <cstdlib>
#include <queue>
#include <list>
#include <climits>
#include <memory>
#include <cassert>
/**
* @namespace graph
* @brief Graph algorithms
*/
namespace graph {
/**
* @brief Represents Bipartite graph for
* Hopcroft Karp implementation
*/
class HKGraph
{
int m{}; ///< m is the number of vertices on left side of Bipartite Graph
int n{}; ///< n is the number of vertices on right side of Bipartite Graph
const int NIL{0};
const int INF{INT_MAX};
std::vector<std::list<int> >adj; ///< adj[u] stores adjacents of left side and 0 is used for dummy vertex
std::vector<int> pair_u; ///< value of vertex 'u' ranges from 1 to m
std::vector<int> pair_v; ///< value of vertex 'v' ranges from 1 to n
std::vector<int> dist; ///< dist represents the distance between vertex 'u' and vertex 'v'
public:
HKGraph(); // Default Constructor
HKGraph(int m, int n); // Constructor
void addEdge(int u, int v); // To add edge
bool bfs(); // Returns true if there is an augmenting path
bool dfs(int u); // Adds augmenting path if there is one beginning with u
int hopcroftKarpAlgorithm(); // Returns size of maximum matching
};
/**
* @brief This function counts the number of augmenting paths between left and right sides of the Bipartite graph
* @returns size of maximum matching
*/
int HKGraph::hopcroftKarpAlgorithm()
{
// pair_u[u] stores pair of u in matching on left side of Bipartite Graph.
// If u doesn't have any pair, then pair_u[u] is NIL
pair_u = std::vector<int>(m + 1,NIL);
// pair_v[v] stores pair of v in matching on right side of Biparite Graph.
// If v doesn't have any pair, then pair_u[v] is NIL
pair_v = std::vector<int>(n + 1,NIL);
dist = std::vector<int>(m + 1); // dist[u] stores distance of left side vertices
int result = 0; // Initialize result
// Keep updating the result while there is an augmenting path possible.
while (bfs())
{
// Find a free vertex to check for a matching
for (int u = 1; u <= m; u++){
// If current vertex is free and there is
// an augmenting path from current vertex
// then increment the result
if (pair_u[u] == NIL && dfs(u)){
result++;
}
}
}
return result;
}
/**
* @brief This function checks for the possibility of augmented path availability
* @returns `true` if there is an augmenting path available
* @returns `false` if there is no augmenting path available
*/
bool HKGraph::bfs()
{
std::queue<int> q; // an integer queue for bfs
// First layer of vertices (set distance as 0)
for (int u = 1; u <= m; u++)
{
// If this is a free vertex, add it to queue
if (pair_u[u] == NIL){
dist[u] = 0; // u is not matched so distance is 0
q.push(u);
}
else{
dist[u] = INF; // set distance as infinite so that this vertex is considered next time for availibility
}
}
dist[NIL] = INF; // Initialize distance to NIL as infinite
// q is going to contain vertices of left side only.
while (!q.empty())
{
int u = q.front(); // dequeue a vertex
q.pop();
// If this node is not NIL and can provide a shorter path to NIL then
if (dist[u] < dist[NIL])
{
// Get all the adjacent vertices of the dequeued vertex u
std::list<int>::iterator it;
for (it = adj[u].begin(); it != adj[u].end(); ++it)
{
int v = *it;
// If pair of v is not considered so far i.e. (v, pair_v[v]) is not yet explored edge.
if (dist[pair_v[v]] == INF)
{
dist[pair_v[v]] = dist[u] + 1;
q.push(pair_v[v]); // Consider the pair and push it to queue
}
}
}
}
return (dist[NIL] != INF); // If we could come back to NIL using alternating path of distinct vertices then there is an augmenting path available
}
/**
* @brief This functions checks whether an augmenting path is available exists beginning with free vertex u
* @param u represents position of vertex
* @returns `true` if there is an augmenting path beginning with free vertex u
* @returns `false` if there is no augmenting path beginning with free vertex u
*/
bool HKGraph::dfs(int u)
{
if (u != NIL)
{
std::list<int>::iterator it;
for (it = adj[u].begin(); it != adj[u].end(); ++it)
{
int v = *it; // Adjacent vertex of u
// Follow the distances set by BFS search
if (dist[pair_v[v]] == dist[u] + 1)
{
// If dfs for pair of v also return true then new matching possible, store the matching
if (dfs(pair_v[v]) == true)
{
pair_v[v] = u;
pair_u[u] = v;
return true;
}
}
}
dist[u] = INF; // If there is no augmenting path beginning with u then set distance to infinite.
return false;
}
return true;
}
/**
* @brief Default Constructor for initialization
*/
HKGraph::HKGraph() = default;
/**
* @brief Constructor for initialization
* @param m is the number of vertices on left side of Bipartite Graph
* @param n is the number of vertices on right side of Bipartite Graph
*/
HKGraph::HKGraph(int m, int n) {
this->m = m;
this->n = n;
adj = std::vector<std::list<int> >(m + 1);
}
/**
* @brief function to add edge from u to v
* @param u is the position of first vertex
* @param v is the position of second vertex
*/
void HKGraph::addEdge(int u, int v)
{
adj[u].push_back(v); // Add v to us list.
}
} // namespace graph
using graph::HKGraph;
/**
* Self-test implementation
* @returns none
*/
void tests(){
// Sample test case 1
int v1a = 3, v1b = 5, e1 = 2; // vertices of left side, right side and edges
HKGraph g1(v1a, v1b); // execute the algorithm
g1.addEdge(0,1);
g1.addEdge(1,4);
int expected_res1 = 0; // for the above sample data, this is the expected output
int res1 = g1.hopcroftKarpAlgorithm();
assert(res1 == expected_res1); // assert check to ensure that the algorithm executed correctly for test 1
// Sample test case 2
int v2a = 4, v2b = 4, e2 = 6; // vertices of left side, right side and edges
HKGraph g2(v2a, v2b); // execute the algorithm
g2.addEdge(1,1);
g2.addEdge(1,3);
g2.addEdge(2,3);
g2.addEdge(3,4);
g2.addEdge(4,3);
g2.addEdge(4,2);
int expected_res2 = 0; // for the above sample data, this is the expected output
int res2 = g2.hopcroftKarpAlgorithm();
assert(res2 == expected_res2); // assert check to ensure that the algorithm executed correctly for test 2
// Sample test case 3
int v3a = 6, v3b = 6, e3 = 4; // vertices of left side, right side and edges
HKGraph g3(v3a, v3b); // execute the algorithm
g3.addEdge(0,1);
g3.addEdge(1,4);
g3.addEdge(1,5);
g3.addEdge(5,0);
int expected_res3 = 0; // for the above sample data, this is the expected output
int res3 = g3.hopcroftKarpAlgorithm();
assert(res3 == expected_res3); // assert check to ensure that the algorithm executed correctly for test 3
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main()
{
tests(); // perform self-tests
int v1 = 0, v2 = 0, e = 0;
std::cin >> v1 >> v2 >> e; // vertices of left side, right side and edges
HKGraph g(v1, v2);
int u = 0, v = 0;
for (int i = 0; i < e; ++i)
{
std::cin >> u >> v;
g.addEdge(u, v);
}
int res = g.hopcroftKarpAlgorithm();
std::cout << "Maximum matching is " << res <<"\n";
return 0;
}

View File

@@ -1,163 +1,171 @@
/**
* @file
* @file
*
* @brief Algorithm to check whether a graph is [bipartite](https://en.wikipedia.org/wiki/Bipartite_graph)
*
* @details
* A graph is a collection of nodes also called vertices and these vertices
* are connected by edges.A bipartite graph is a graph whose vertices can be
* divided into two disjoint and independent sets U and V such that every edge
* connects a vertex in U to one in V.
*
* The given Algorithm will determine whether the given graph is bipartite or not
*
* <pre>
* Example - Here is a graph g1 with 5 vertices and is bipartite
*
* 1 4
* / \ / \
* 2 3 5
*
* Example - Here is a graph G2 with 3 vertices and is not bipartite
*
* 1 --- 2
* \ /
* 3
*
* </pre>
* @brief Algorithm to check whether a graph is
* [bipartite](https://en.wikipedia.org/wiki/Bipartite_graph)
*
* @details
* A graph is a collection of nodes also called vertices and these vertices
* are connected by edges. A graph is bipartite if its vertices can be
* divided into two disjoint and independent sets U and V such that every edge
* connects a vertex in U to one in V.
*
* The algorithm implemented in this file determines whether the given graph
* is bipartite or not.
*
* <pre>
* Example - Here is a graph g1 with 5 vertices and is bipartite
*
* 1 4
* / \ / \
* 2 3 5
*
* Example - Here is a graph G2 with 3 vertices and is not bipartite
*
* 1 --- 2
* \ /
* 3
*
* </pre>
*
* @author [Akshat Vaya](https://github.com/AkVaya)
*
* @author [Akshat Vaya](https://github.com/AkVaya)
*
*/
#include <iostream>
#include <vector>
#include <queue>
#include <vector>
/**
* @namespace graph
* @brief Graph algorithms
*/
namespace graph{
/**
* @namespace is_graph_bipartite
* @brief Functions for checking whether a graph is bipartite or not
*/
namespace is_graph_bipartite{
/**
* @brief Class for representing graph as an adjacency list.
*/
class Graph {
private:
int n; /// size of the graph
std::vector<std::vector <int> > adj; /// adj stores the graph as an adjacency list
std::vector<int> side; ///stores the side of the vertex
static const int nax = 5e5 + 1;
public:
/**
* @brief Constructor that initializes the graph on creation
*/
explicit Graph(int size = nax){
n = size;
adj.resize(n);
side.resize(n,-1);
}
void addEdge(int u, int v); /// function to add edges to our graph
bool is_bipartite(); /// function to check whether the graph is bipartite or not
};
/**
* @brief Function that add an edge between two nodes or vertices of graph
*
* @param u is a node or vertex of graph
* @param v is a node or vertex of graph
*/
void Graph::addEdge(int u, int v) {
adj[u-1].push_back(v-1);
adj[v-1].push_back(u-1);
}
/**
* @brief function that checks whether the graph is bipartite or not
* the function returns true if the graph is a bipartite graph
* the function returns false if the graph is not a bipartite graph
*
* @details
* Here, side refers to the two disjoint subsets of the bipartite graph.
* Initially, the values of side are set to -1 which is an unassigned state. A for loop is run for every vertex of the graph.
* If the current edge has no side assigned to it, then a Breadth First Search operation is performed.
* If two neighbours have the same side then the graph will not be bipartite and the value of check becomes false.
* If and only if each pair of neighbours have different sides, the value of check will be true and hence the graph bipartite.
*
*/
bool Graph::is_bipartite(){
bool check = true;
std::queue<int> q;
for (int current_edge = 0; current_edge < n; ++current_edge)
{
if(side[current_edge] == -1){
q.push(current_edge);
side[current_edge] = 0;
while(q.size()){
int current = q.front();
q.pop();
for(auto neighbour : adj[current]){
if(side[neighbour] == -1){
side[neighbour] = (1 ^ side[current]);
q.push(neighbour);
}
else{
check &= (side[neighbour] != side[current]);
}
}
}
}
}
return check;
}
} /// namespace is_graph_bipartite
} /// namespace graph
namespace graph {
/**
* Function to test the above algorithm
* @returns none
* @namespace is_graph_bipartite
* @brief Functions for checking whether a graph is bipartite or not
*/
static void test(){
graph::is_graph_bipartite::Graph G1(5); /// creating graph G1 with 5 vertices
/// adding edges to the graphs as per the illustrated example
G1.addEdge(1,2);
G1.addEdge(1,3);
G1.addEdge(3,4);
G1.addEdge(4,5);
namespace is_graph_bipartite {
/**
* @brief Class for representing graph as an adjacency list.
*/
class Graph {
private:
int n; ///< size of the graph
graph::is_graph_bipartite::Graph G2(3); /// creating graph G2 with 3 vertices
/// adding edges to the graphs as per the illustrated example
G2.addEdge(1,2);
G2.addEdge(1,3);
G2.addEdge(2,3);
std::vector<std::vector<int> >
adj; ///< adj stores the graph as an adjacency list
/// checking whether the graphs are bipartite or not
if(G1.is_bipartite()){
std::cout<<"The given graph G1 is a bipartite graph\n";
}
else{
std::cout<<"The given graph G1 is not a bipartite graph\n";
}
if(G2.is_bipartite()){
std::cout<<"The given graph G2 is a bipartite graph\n";
}
else{
std::cout<<"The given graph G2 is not a bipartite graph\n";
}
std::vector<int> side; ///< stores the side of the vertex
public:
/**
* @brief Constructor that initializes the graph on creation
* @param size number of vertices of the graph
*/
explicit Graph(int size) {
n = size;
adj.resize(n);
side.resize(n, -1);
}
void addEdge(int u, int v); /// function to add edges to our graph
bool
is_bipartite(); /// function to check whether the graph is bipartite or not
};
/**
* @brief Function that add an edge between two nodes or vertices of graph
*
* @param u is a node or vertex of graph
* @param v is a node or vertex of graph
*/
void Graph::addEdge(int u, int v) {
adj[u - 1].push_back(v - 1);
adj[v - 1].push_back(u - 1);
}
/**
* @brief function that checks whether the graph is bipartite or not
* the function returns true if the graph is a bipartite graph
* the function returns false if the graph is not a bipartite graph
*
* @details
* Here, side refers to the two disjoint subsets of the bipartite graph.
* Initially, the values of side are set to -1 which is an unassigned state. A
* for loop is run for every vertex of the graph. If the current edge has no
* side assigned to it, then a Breadth First Search operation is performed. If
* two neighbours have the same side then the graph will not be bipartite and
* the value of check becomes false. If and only if each pair of neighbours have
* different sides, the value of check will be true and hence the graph
* bipartite.
*
* @returns `true` if th graph is bipartite
* @returns `false` otherwise
*/
bool Graph::is_bipartite() {
bool check = true;
std::queue<int> q;
for (int current_edge = 0; current_edge < n; ++current_edge) {
if (side[current_edge] == -1) {
q.push(current_edge);
side[current_edge] = 0;
while (q.size()) {
int current = q.front();
q.pop();
for (auto neighbour : adj[current]) {
if (side[neighbour] == -1) {
side[neighbour] = (1 ^ side[current]);
q.push(neighbour);
} else {
check &= (side[neighbour] != side[current]);
}
}
}
}
}
return check;
}
} // namespace is_graph_bipartite
} // namespace graph
/**
* Function to test the above algorithm
* @returns none
*/
static void test() {
graph::is_graph_bipartite::Graph G1(
5); /// creating graph G1 with 5 vertices
/// adding edges to the graphs as per the illustrated example
G1.addEdge(1, 2);
G1.addEdge(1, 3);
G1.addEdge(3, 4);
G1.addEdge(4, 5);
graph::is_graph_bipartite::Graph G2(
3); /// creating graph G2 with 3 vertices
/// adding edges to the graphs as per the illustrated example
G2.addEdge(1, 2);
G2.addEdge(1, 3);
G2.addEdge(2, 3);
/// checking whether the graphs are bipartite or not
if (G1.is_bipartite()) {
std::cout << "The given graph G1 is a bipartite graph\n";
} else {
std::cout << "The given graph G1 is not a bipartite graph\n";
}
if (G2.is_bipartite()) {
std::cout << "The given graph G2 is a bipartite graph\n";
} else {
std::cout << "The given graph G2 is not a bipartite graph\n";
}
}
/**
* Main function
*/
int main(){
test(); ///Testing
return 0;
int main() {
test(); /// Testing
return 0;
}

View File

@@ -3,9 +3,8 @@
*/
#include <iostream>
#include <vector>
#include <stack>
#include <vector>
/**
* Iterative function/method to print graph:
@@ -13,7 +12,7 @@
* @param V number of vertices
* @return void
**/
void print(const std::vector< std::vector<int> > &a, int V) {
void print(const std::vector<std::vector<int> > &a, int V) {
for (int i = 0; i < V; i++) {
if (!a[i].empty()) {
std::cout << "i=" << i << "-->";
@@ -35,7 +34,8 @@ void print(const std::vector< std::vector<int> > &a, int V) {
* @param adj adjacency list representation of the graph
* @return void
**/
void push_vertex(int v, std::stack<int> *st, std::vector<bool> *vis, const std::vector< std::vector<int> > &adj) {
void push_vertex(int v, std::stack<int> *st, std::vector<bool> *vis,
const std::vector<std::vector<int> > &adj) {
(*vis)[v] = true;
for (auto i = adj[v].begin(); i != adj[v].end(); i++) {
if ((*vis)[*i] == false) {
@@ -52,7 +52,8 @@ void push_vertex(int v, std::stack<int> *st, std::vector<bool> *vis, const std::
* @param grev graph with reversed edges
* @return void
**/
void dfs(int v, std::vector<bool> *vis, const std::vector< std::vector<int> > &grev) {
void dfs(int v, std::vector<bool> *vis,
const std::vector<std::vector<int> > &grev) {
(*vis)[v] = true;
// cout<<v<<" ";
for (auto i = grev[v].begin(); i != grev[v].end(); i++) {
@@ -72,7 +73,7 @@ no SCCs i.e. none(0) or there will be x no. of SCCs (x>0)) i.e. it returns the
count of (number of) strongly connected components (SCCs) in the graph.
(variable 'count_scc' within function)
**/
int kosaraju(int V, const std::vector< std::vector<int> > &adj) {
int kosaraju(int V, const std::vector<std::vector<int> > &adj) {
std::vector<bool> vis(V, false);
std::stack<int> st;
for (int v = 0; v < V; v++) {
@@ -81,7 +82,7 @@ int kosaraju(int V, const std::vector< std::vector<int> > &adj) {
}
}
// making new graph (grev) with reverse edges as in adj[]:
std::vector< std::vector<int> > grev(V);
std::vector<std::vector<int> > grev(V);
for (int i = 0; i < V + 1; i++) {
for (auto j = adj[i].begin(); j != adj[i].end(); j++) {
grev[*j].push_back(i);
@@ -114,7 +115,7 @@ int main() {
int a = 0, b = 0; // a->number of nodes, b->directed edges.
std::cin >> a >> b;
int m = 0, n = 0;
std::vector< std::vector<int> > adj(a + 1);
std::vector<std::vector<int> > adj(a + 1);
for (int i = 0; i < b; i++) // take total b inputs of 2 vertices each
// required to form an edge.
{

View File

@@ -1,7 +1,7 @@
#include <iostream>
#include <vector>
#include <algorithm>
#include <array>
#include <iostream>
#include <vector>
//#include <boost/multiprecision/cpp_int.hpp>
// using namespace boost::multiprecision;
const int mx = 1e6 + 5;
@@ -12,7 +12,7 @@ ll node, edge;
std::vector<std::pair<ll, std::pair<ll, ll>>> edges;
void initial() {
for (int i = 0; i < node + edge; ++i) {
parent[i] = i;
parent[i] = i;
}
}

View File

@@ -9,7 +9,8 @@
* Algorithm: https://cp-algorithms.com/graph/lca_binary_lifting.html
*
* Complexity:
* - Precomputation: \f$O(N \log N)\f$ where \f$N\f$ is the number of vertices in the tree
* - Precomputation: \f$O(N \log N)\f$ where \f$N\f$ is the number of vertices
* in the tree
* - Query: \f$O(\log N)\f$
* - Space: \f$O(N \log N)\f$
*
@@ -34,11 +35,11 @@
* lowest_common_ancestor(x, y) = lowest_common_ancestor(y, x)
*/
#include <cassert>
#include <iostream>
#include <queue>
#include <utility>
#include <vector>
#include <queue>
#include <cassert>
/**
* \namespace graph
@@ -50,7 +51,7 @@ namespace graph {
* Its vertices are indexed 0, 1, ..., N - 1.
*/
class Graph {
public:
public:
/**
* \brief Populate the adjacency list for each vertex in the graph.
* Assumes that evey edge is a pair of valid vertex indices.
@@ -58,7 +59,7 @@ class Graph {
* @param N number of vertices in the graph
* @param undirected_edges list of graph's undirected edges
*/
Graph(size_t N, const std::vector< std::pair<int, int> > &undirected_edges) {
Graph(size_t N, const std::vector<std::pair<int, int> > &undirected_edges) {
neighbors.resize(N);
for (auto &edge : undirected_edges) {
neighbors[edge.first].push_back(edge.second);
@@ -70,19 +71,18 @@ class Graph {
* Function to get the number of vertices in the graph
* @return the number of vertices in the graph.
*/
int number_of_vertices() const {
return neighbors.size();
}
int number_of_vertices() const { return neighbors.size(); }
/** \brief for each vertex it stores a list indicies of its neighbors */
std::vector< std::vector<int> > neighbors;
std::vector<std::vector<int> > neighbors;
};
/**
* Representation of a rooted tree. For every vertex its parent is precalculated.
* Representation of a rooted tree. For every vertex its parent is
* precalculated.
*/
class RootedTree : public Graph {
public:
public:
/**
* \brief Constructs the tree by calculating parent for every vertex.
* Assumes a valid description of a tree is provided.
@@ -90,7 +90,8 @@ class RootedTree : public Graph {
* @param undirected_edges list of graph's undirected edges
* @param root_ index of the root vertex
*/
RootedTree(const std::vector< std::pair<int, int> > &undirected_edges, int root_)
RootedTree(const std::vector<std::pair<int, int> > &undirected_edges,
int root_)
: Graph(undirected_edges.size() + 1, undirected_edges), root(root_) {
populate_parents();
}
@@ -106,7 +107,7 @@ class RootedTree : public Graph {
/** \brief Index of the root vertex. */
int root;
protected:
protected:
/**
* \brief Calculate the parents for all the vertices in the tree.
* Implements the breadth first search algorithm starting from the root
@@ -135,7 +136,6 @@ class RootedTree : public Graph {
}
}
}
};
/**
@@ -143,13 +143,13 @@ class RootedTree : public Graph {
* queries of the lowest common ancestor of two given vertices in the tree.
*/
class LowestCommonAncestor {
public:
public:
/**
* \brief Stores the tree and precomputs "up lifts".
* @param tree_ rooted tree.
*/
explicit LowestCommonAncestor(const RootedTree& tree_) : tree(tree_) {
populate_up();
explicit LowestCommonAncestor(const RootedTree &tree_) : tree(tree_) {
populate_up();
}
/**
@@ -196,16 +196,16 @@ class LowestCommonAncestor {
}
/* \brief reference to the rooted tree this structure allows to query */
const RootedTree& tree;
const RootedTree &tree;
/**
* \brief for every vertex stores a list of its ancestors by powers of two
* For each vertex, the first element of the corresponding list contains
* the index of its parent. The i-th element of the list is an index of
* the (2^i)-th ancestor of the vertex.
*/
std::vector< std::vector<int> > up;
std::vector<std::vector<int> > up;
protected:
protected:
/**
* Populate the "up" structure. See above.
*/
@@ -241,9 +241,8 @@ static void tests() {
* |
* 9
*/
std::vector< std::pair<int, int> > edges = {
{7, 1}, {1, 5}, {1, 3}, {3, 6}, {6, 2}, {2, 9}, {6, 8}, {4, 3}, {0, 4}
};
std::vector<std::pair<int, int> > edges = {
{7, 1}, {1, 5}, {1, 3}, {3, 6}, {6, 2}, {2, 9}, {6, 8}, {4, 3}, {0, 4}};
graph::RootedTree t(edges, 3);
graph::LowestCommonAncestor lca(t);
assert(lca.lowest_common_ancestor(7, 4) == 3);

View File

@@ -16,7 +16,7 @@
// std::max capacity of node in graph
const int MAXN = 505;
class Graph {
std::vector< std::vector<int> > residual_capacity, capacity;
std::vector<std::vector<int> > residual_capacity, capacity;
int total_nodes = 0;
int total_edges = 0, source = 0, sink = 0;
std::vector<int> parent;
@@ -50,8 +50,10 @@ class Graph {
void set_graph() {
std::cin >> total_nodes >> total_edges >> source >> sink;
parent = std::vector<int>(total_nodes, -1);
capacity = residual_capacity = std::vector< std::vector<int> >(total_nodes, std::vector<int>(total_nodes));
for (int start = 0, destination = 0, capacity_ = 0, i = 0; i < total_edges; ++i) {
capacity = residual_capacity = std::vector<std::vector<int> >(
total_nodes, std::vector<int>(total_nodes));
for (int start = 0, destination = 0, capacity_ = 0, i = 0;
i < total_edges; ++i) {
std::cin >> start >> destination >> capacity_;
residual_capacity[start][destination] = capacity_;
capacity[start][destination] = capacity_;

View File

@@ -5,7 +5,7 @@
using PII = std::pair<int, int>;
int prim(int x, const std::vector< std::vector<PII> > &graph) {
int prim(int x, const std::vector<std::vector<PII> > &graph) {
// priority queue to maintain edges with respect to weights
std::priority_queue<PII, std::vector<PII>, std::greater<PII> > Q;
std::vector<bool> marked(graph.size(), false);
@@ -40,7 +40,7 @@ int main() {
return 0;
}
std::vector< std::vector<PII> > graph(nodes);
std::vector<std::vector<PII> > graph(nodes);
// Edges with their nodes & weight
for (int i = 0; i < edges; ++i) {

View File

@@ -2,7 +2,8 @@
#include <iostream>
#include <vector>
int number_of_vertices, number_of_edges; // For number of Vertices (V) and number of edges (E)
int number_of_vertices,
number_of_edges; // For number of Vertices (V) and number of edges (E)
std::vector<std::vector<int>> graph;
std::vector<bool> visited;
std::vector<int> topological_order;
@@ -28,7 +29,8 @@ void topological_sort() {
reverse(topological_order.begin(), topological_order.end());
}
int main() {
std::cout << "Enter the number of vertices and the number of directed edges\n";
std::cout
<< "Enter the number of vertices and the number of directed edges\n";
std::cin >> number_of_vertices >> number_of_edges;
int x = 0, y = 0;
graph.resize(number_of_vertices, std::vector<int>());
@@ -41,7 +43,7 @@ int main() {
std::cout << "Topological Order : \n";
for (int v : topological_order) {
std::cout << v + 1
<< ' '; // converting zero based indexing back to one based.
<< ' '; // converting zero based indexing back to one based.
}
std::cout << '\n';
return 0;

View File

@@ -4,7 +4,7 @@
#include <queue>
#include <vector>
std::vector<int> topoSortKahn(int N, const std::vector< std::vector<int> > &adj);
std::vector<int> topoSortKahn(int N, const std::vector<std::vector<int> > &adj);
int main() {
int nodes = 0, edges = 0;
@@ -14,7 +14,7 @@ int main() {
}
int u = 0, v = 0;
std::vector< std::vector<int> > graph(nodes);
std::vector<std::vector<int> > graph(nodes);
// create graph
// example
// 6 6
@@ -32,7 +32,8 @@ int main() {
}
}
std::vector<int> topoSortKahn(int V, const std::vector< std::vector<int> > &adj) {
std::vector<int> topoSortKahn(int V,
const std::vector<std::vector<int> > &adj) {
std::vector<bool> vis(V + 1, false);
std::vector<int> deg(V + 1, 0);
for (int i = 0; i < V; i++) {

View File

@@ -0,0 +1,68 @@
/**
* @file
* @brief Implementation of an algorithm to solve the [jumping game]((https://leetcode.com/problems/jump-game/)) problem
* @details
* **Problem statement:** Given an array of non-negative integers, you are initially positioned at the first index of the array. Each element in the array represents your maximum jump length at that position. Determine if you are able to reach the last index.
* This solution takes in input as a vector and output as a boolean to check if you can reach the last position.
* We name the indices good and bad based on whether we can reach the destination if we start at that position.
* We initialize the last index as lastPos.
* Here, we start from the end of the array and check if we can ever reach the first index.
* We check if the sum of the index and the maximum jump count given is greater than or equal to the lastPos.
* If yes, then that is the last position you can reach starting from the back.
* After the end of the loop, if we reach the lastPos as 0, then the destination can be reached from the start position.
* @author [Rakshaa Viswanathan](https://github.com/rakshaa2000)
*/
#include <vector>
#include <iostream>
#include <cassert>
/**
* @brief This function implements the above algorithm
* @param array of numbers containing the maximum jump (in steps) from that index
* @returns bool value whether final index can be reached or not
*/
bool canJump(const std::vector<int> &nums) {
auto lastPos = nums.size() - 1;
for (auto i = nums.size() - 1; i >= 0; i--) {
if (i + nums[i] >= lastPos) {
lastPos = i;
}
}
return lastPos == 0;
}
/**
* @brief Function to test above algorithm
* @returns void
*/
static void test(){
// Test 1
std::vector<int> num1={4,3,1,0,5};
assert(canJump(num1)==true);
std::cout<<"Input: ";
for(auto i: num1){
std::cout<<i<<" ";
}
std::cout<<"Output: true"<<std::endl;
// Test 2
std::vector<int> num2={3,2,1,0,4};
assert(canJump(num2)==false);
std::cout<<"Input: ";
for(auto i: num2){
std::cout<<i<<" ";
}
std::cout<<"Output: false"<<std::endl;
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main(){
test();
return 0;
}

152
machine_learning/iris.csv Normal file
View File

@@ -0,0 +1,152 @@
https://archive.ics.uci.edu/ml/datasets/iris
sepal length in cm,sepal width in cm,petal length in cm,petal width in cm
5.1,3.5,1.4,.2,0
4.9,3,1.4,.2,0
4.7,3.2,1.3,.2,0
4.6,3.1,1.5,.2,0
5,3.6,1.4,.2,0
5.4,3.9,1.7,.4,0
4.6,3.4,1.4,.3,0
5,3.4,1.5,.2,0
4.4,2.9,1.4,.2,0
4.9,3.1,1.5,.1,0
5.4,3.7,1.5,.2,0
4.8,3.4,1.6,.2,0
4.8,3,1.4,.1,0
4.3,3,1.1,.1,0
5.8,4,1.2,.2,0
5.7,4.4,1.5,.4,0
5.4,3.9,1.3,.4,0
5.1,3.5,1.4,.3,0
5.7,3.8,1.7,.3,0
5.1,3.8,1.5,.3,0
5.4,3.4,1.7,.2,0
5.1,3.7,1.5,.4,0
4.6,3.6,1,.2,0
5.1,3.3,1.7,.5,0
4.8,3.4,1.9,.2,0
5,3,1.6,.2,0
5,3.4,1.6,.4,0
5.2,3.5,1.5,.2,0
5.2,3.4,1.4,.2,0
4.7,3.2,1.6,.2,0
4.8,3.1,1.6,.2,0
5.4,3.4,1.5,.4,0
5.2,4.1,1.5,.1,0
5.5,4.2,1.4,.2,0
4.9,3.1,1.5,.2,0
5,3.2,1.2,.2,0
5.5,3.5,1.3,.2,0
4.9,3.6,1.4,.1,0
4.4,3,1.3,.2,0
5.1,3.4,1.5,.2,0
5,3.5,1.3,.3,0
4.5,2.3,1.3,.3,0
4.4,3.2,1.3,.2,0
5,3.5,1.6,.6,0
5.1,3.8,1.9,.4,0
4.8,3,1.4,.3,0
5.1,3.8,1.6,.2,0
4.6,3.2,1.4,.2,0
5.3,3.7,1.5,.2,0
5,3.3,1.4,.2,0
7,3.2,4.7,1.4,1
6.4,3.2,4.5,1.5,1
6.9,3.1,4.9,1.5,1
5.5,2.3,4,1.3,1
6.5,2.8,4.6,1.5,1
5.7,2.8,4.5,1.3,1
6.3,3.3,4.7,1.6,1
4.9,2.4,3.3,1,1
6.6,2.9,4.6,1.3,1
5.2,2.7,3.9,1.4,1
5,2,3.5,1,1
5.9,3,4.2,1.5,1
6,2.2,4,1,1
6.1,2.9,4.7,1.4,1
5.6,2.9,3.6,1.3,1
6.7,3.1,4.4,1.4,1
5.6,3,4.5,1.5,1
5.8,2.7,4.1,1,1
6.2,2.2,4.5,1.5,1
5.6,2.5,3.9,1.1,1
5.9,3.2,4.8,1.8,1
6.1,2.8,4,1.3,1
6.3,2.5,4.9,1.5,1
6.1,2.8,4.7,1.2,1
6.4,2.9,4.3,1.3,1
6.6,3,4.4,1.4,1
6.8,2.8,4.8,1.4,1
6.7,3,5,1.7,1
6,2.9,4.5,1.5,1
5.7,2.6,3.5,1,1
5.5,2.4,3.8,1.1,1
5.5,2.4,3.7,1,1
5.8,2.7,3.9,1.2,1
6,2.7,5.1,1.6,1
5.4,3,4.5,1.5,1
6,3.4,4.5,1.6,1
6.7,3.1,4.7,1.5,1
6.3,2.3,4.4,1.3,1
5.6,3,4.1,1.3,1
5.5,2.5,4,1.3,1
5.5,2.6,4.4,1.2,1
6.1,3,4.6,1.4,1
5.8,2.6,4,1.2,1
5,2.3,3.3,1,1
5.6,2.7,4.2,1.3,1
5.7,3,4.2,1.2,1
5.7,2.9,4.2,1.3,1
6.2,2.9,4.3,1.3,1
5.1,2.5,3,1.1,1
5.7,2.8,4.1,1.3,1
6.3,3.3,6,2.5,2
5.8,2.7,5.1,1.9,2
7.1,3,5.9,2.1,2
6.3,2.9,5.6,1.8,2
6.5,3,5.8,2.2,2
7.6,3,6.6,2.1,2
4.9,2.5,4.5,1.7,2
7.3,2.9,6.3,1.8,2
6.7,2.5,5.8,1.8,2
7.2,3.6,6.1,2.5,2
6.5,3.2,5.1,2,2
6.4,2.7,5.3,1.9,2
6.8,3,5.5,2.1,2
5.7,2.5,5,2,2
5.8,2.8,5.1,2.4,2
6.4,3.2,5.3,2.3,2
6.5,3,5.5,1.8,2
7.7,3.8,6.7,2.2,2
7.7,2.6,6.9,2.3,2
6,2.2,5,1.5,2
6.9,3.2,5.7,2.3,2
5.6,2.8,4.9,2,2
7.7,2.8,6.7,2,2
6.3,2.7,4.9,1.8,2
6.7,3.3,5.7,2.1,2
7.2,3.2,6,1.8,2
6.2,2.8,4.8,1.8,2
6.1,3,4.9,1.8,2
6.4,2.8,5.6,2.1,2
7.2,3,5.8,1.6,2
7.4,2.8,6.1,1.9,2
7.9,3.8,6.4,2,2
6.4,2.8,5.6,2.2,2
6.3,2.8,5.1,1.5,2
6.1,2.6,5.6,1.4,2
7.7,3,6.1,2.3,2
6.3,3.4,5.6,2.4,2
6.4,3.1,5.5,1.8,2
6,3,4.8,1.8,2
6.9,3.1,5.4,2.1,2
6.7,3.1,5.6,2.4,2
6.9,3.1,5.1,2.3,2
5.8,2.7,5.1,1.9,2
6.8,3.2,5.9,2.3,2
6.7,3.3,5.7,2.5,2
6.7,3,5.2,2.3,2
6.3,2.5,5,1.9,2
6.5,3,5.2,2,2
6.2,3.4,5.4,2.3,2
5.9,3,5.1,1.8,2
1 https://archive.ics.uci.edu/ml/datasets/iris
2 sepal length in cm,sepal width in cm,petal length in cm,petal width in cm
3 5.1,3.5,1.4,.2,0
4 4.9,3,1.4,.2,0
5 4.7,3.2,1.3,.2,0
6 4.6,3.1,1.5,.2,0
7 5,3.6,1.4,.2,0
8 5.4,3.9,1.7,.4,0
9 4.6,3.4,1.4,.3,0
10 5,3.4,1.5,.2,0
11 4.4,2.9,1.4,.2,0
12 4.9,3.1,1.5,.1,0
13 5.4,3.7,1.5,.2,0
14 4.8,3.4,1.6,.2,0
15 4.8,3,1.4,.1,0
16 4.3,3,1.1,.1,0
17 5.8,4,1.2,.2,0
18 5.7,4.4,1.5,.4,0
19 5.4,3.9,1.3,.4,0
20 5.1,3.5,1.4,.3,0
21 5.7,3.8,1.7,.3,0
22 5.1,3.8,1.5,.3,0
23 5.4,3.4,1.7,.2,0
24 5.1,3.7,1.5,.4,0
25 4.6,3.6,1,.2,0
26 5.1,3.3,1.7,.5,0
27 4.8,3.4,1.9,.2,0
28 5,3,1.6,.2,0
29 5,3.4,1.6,.4,0
30 5.2,3.5,1.5,.2,0
31 5.2,3.4,1.4,.2,0
32 4.7,3.2,1.6,.2,0
33 4.8,3.1,1.6,.2,0
34 5.4,3.4,1.5,.4,0
35 5.2,4.1,1.5,.1,0
36 5.5,4.2,1.4,.2,0
37 4.9,3.1,1.5,.2,0
38 5,3.2,1.2,.2,0
39 5.5,3.5,1.3,.2,0
40 4.9,3.6,1.4,.1,0
41 4.4,3,1.3,.2,0
42 5.1,3.4,1.5,.2,0
43 5,3.5,1.3,.3,0
44 4.5,2.3,1.3,.3,0
45 4.4,3.2,1.3,.2,0
46 5,3.5,1.6,.6,0
47 5.1,3.8,1.9,.4,0
48 4.8,3,1.4,.3,0
49 5.1,3.8,1.6,.2,0
50 4.6,3.2,1.4,.2,0
51 5.3,3.7,1.5,.2,0
52 5,3.3,1.4,.2,0
53 7,3.2,4.7,1.4,1
54 6.4,3.2,4.5,1.5,1
55 6.9,3.1,4.9,1.5,1
56 5.5,2.3,4,1.3,1
57 6.5,2.8,4.6,1.5,1
58 5.7,2.8,4.5,1.3,1
59 6.3,3.3,4.7,1.6,1
60 4.9,2.4,3.3,1,1
61 6.6,2.9,4.6,1.3,1
62 5.2,2.7,3.9,1.4,1
63 5,2,3.5,1,1
64 5.9,3,4.2,1.5,1
65 6,2.2,4,1,1
66 6.1,2.9,4.7,1.4,1
67 5.6,2.9,3.6,1.3,1
68 6.7,3.1,4.4,1.4,1
69 5.6,3,4.5,1.5,1
70 5.8,2.7,4.1,1,1
71 6.2,2.2,4.5,1.5,1
72 5.6,2.5,3.9,1.1,1
73 5.9,3.2,4.8,1.8,1
74 6.1,2.8,4,1.3,1
75 6.3,2.5,4.9,1.5,1
76 6.1,2.8,4.7,1.2,1
77 6.4,2.9,4.3,1.3,1
78 6.6,3,4.4,1.4,1
79 6.8,2.8,4.8,1.4,1
80 6.7,3,5,1.7,1
81 6,2.9,4.5,1.5,1
82 5.7,2.6,3.5,1,1
83 5.5,2.4,3.8,1.1,1
84 5.5,2.4,3.7,1,1
85 5.8,2.7,3.9,1.2,1
86 6,2.7,5.1,1.6,1
87 5.4,3,4.5,1.5,1
88 6,3.4,4.5,1.6,1
89 6.7,3.1,4.7,1.5,1
90 6.3,2.3,4.4,1.3,1
91 5.6,3,4.1,1.3,1
92 5.5,2.5,4,1.3,1
93 5.5,2.6,4.4,1.2,1
94 6.1,3,4.6,1.4,1
95 5.8,2.6,4,1.2,1
96 5,2.3,3.3,1,1
97 5.6,2.7,4.2,1.3,1
98 5.7,3,4.2,1.2,1
99 5.7,2.9,4.2,1.3,1
100 6.2,2.9,4.3,1.3,1
101 5.1,2.5,3,1.1,1
102 5.7,2.8,4.1,1.3,1
103 6.3,3.3,6,2.5,2
104 5.8,2.7,5.1,1.9,2
105 7.1,3,5.9,2.1,2
106 6.3,2.9,5.6,1.8,2
107 6.5,3,5.8,2.2,2
108 7.6,3,6.6,2.1,2
109 4.9,2.5,4.5,1.7,2
110 7.3,2.9,6.3,1.8,2
111 6.7,2.5,5.8,1.8,2
112 7.2,3.6,6.1,2.5,2
113 6.5,3.2,5.1,2,2
114 6.4,2.7,5.3,1.9,2
115 6.8,3,5.5,2.1,2
116 5.7,2.5,5,2,2
117 5.8,2.8,5.1,2.4,2
118 6.4,3.2,5.3,2.3,2
119 6.5,3,5.5,1.8,2
120 7.7,3.8,6.7,2.2,2
121 7.7,2.6,6.9,2.3,2
122 6,2.2,5,1.5,2
123 6.9,3.2,5.7,2.3,2
124 5.6,2.8,4.9,2,2
125 7.7,2.8,6.7,2,2
126 6.3,2.7,4.9,1.8,2
127 6.7,3.3,5.7,2.1,2
128 7.2,3.2,6,1.8,2
129 6.2,2.8,4.8,1.8,2
130 6.1,3,4.9,1.8,2
131 6.4,2.8,5.6,2.1,2
132 7.2,3,5.8,1.6,2
133 7.4,2.8,6.1,1.9,2
134 7.9,3.8,6.4,2,2
135 6.4,2.8,5.6,2.2,2
136 6.3,2.8,5.1,1.5,2
137 6.1,2.6,5.6,1.4,2
138 7.7,3,6.1,2.3,2
139 6.3,3.4,5.6,2.4,2
140 6.4,3.1,5.5,1.8,2
141 6,3,4.8,1.8,2
142 6.9,3.1,5.4,2.1,2
143 6.7,3.1,5.6,2.4,2
144 6.9,3.1,5.1,2.3,2
145 5.8,2.7,5.1,1.9,2
146 6.8,3.2,5.9,2.3,2
147 6.7,3.3,5.7,2.5,2
148 6.7,3,5.2,2.3,2
149 6.3,2.5,5,1.9,2
150 6.5,3,5.2,2,2
151 6.2,3.4,5.4,2.3,2
152 5.9,3,5.1,1.8,2

View File

@@ -0,0 +1,834 @@
/**
* @file
* @author [Deep Raval](https://github.com/imdeep2905)
*
* @brief Implementation of [Multilayer Perceptron]
* (https://en.wikipedia.org/wiki/Multilayer_perceptron).
*
* @details
* A multilayer perceptron (MLP) is a class of feedforward artificial neural
* network (ANN). The term MLP is used ambiguously, sometimes loosely to any
* feedforward ANN, sometimes strictly to refer to networks composed of multiple
* layers of perceptrons (with threshold activation). Multilayer perceptrons are
* sometimes colloquially referred to as "vanilla" neural networks, especially
* when they have a single hidden layer.
*
* An MLP consists of at least three layers of nodes: an input layer, a hidden
* layer and an output layer. Except for the input nodes, each node is a neuron
* that uses a nonlinear activation function. MLP utilizes a supervised learning
* technique called backpropagation for training. Its multiple layers and
* non-linear activation distinguish MLP from a linear perceptron. It can
* distinguish data that is not linearly separable.
*
* See [Backpropagation](https://en.wikipedia.org/wiki/Backpropagation) for
* training algorithm.
*
* \note This implementation uses mini-batch gradient descent as optimizer and
* MSE as loss function. Bias is also not included.
*/
#include <algorithm>
#include <cassert>
#include <chrono>
#include <cmath>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <valarray>
#include <vector>
#include "vector_ops.hpp" // Custom header file for vector operations
/** \namespace machine_learning
* \brief Machine learning algorithms
*/
namespace machine_learning {
/** \namespace neural_network
* \brief Neural Network or Multilayer Perceptron
*/
namespace neural_network {
/** \namespace activations
* \brief Various activation functions used in Neural network
*/
namespace activations {
/**
* Sigmoid function
* @param X Value
* @return Returns sigmoid(x)
*/
double sigmoid(const double &x) { return 1.0 / (1.0 + std::exp(-x)); }
/**
* Derivative of sigmoid function
* @param X Value
* @return Returns derivative of sigmoid(x)
*/
double dsigmoid(const double &x) { return x * (1 - x); }
/**
* Relu function
* @param X Value
* @returns relu(x)
*/
double relu(const double &x) { return std::max(0.0, x); }
/**
* Derivative of relu function
* @param X Value
* @returns derivative of relu(x)
*/
double drelu(const double &x) { return x >= 0.0 ? 1.0 : 0.0; }
/**
* Tanh function
* @param X Value
* @return Returns tanh(x)
*/
double tanh(const double &x) { return 2 / (1 + std::exp(-2 * x)) - 1; }
/**
* Derivative of Sigmoid function
* @param X Value
* @return Returns derivative of tanh(x)
*/
double dtanh(const double &x) { return 1 - x * x; }
} // namespace activations
/** \namespace util_functions
* \brief Various utility functions used in Neural network
*/
namespace util_functions {
/**
* Square function
* @param X Value
* @return Returns x * x
*/
double square(const double &x) { return x * x; }
/**
* Identity function
* @param X Value
* @return Returns x
*/
double identity_function(const double &x) { return x; }
} // namespace util_functions
/** \namespace layers
* \brief This namespace contains layers used
* in MLP.
*/
namespace layers {
/**
* neural_network::layers::DenseLayer class is used to store all necessary
* information about the layers (i.e. neurons, activation and kernel). This
* class is used by NeuralNetwork class to store layers.
*
*/
class DenseLayer {
public:
// To store activation function and it's derivative
double (*activation_function)(const double &);
double (*dactivation_function)(const double &);
int neurons; // To store number of neurons (used in summary)
std::string activation; // To store activation name (used in summary)
std::vector<std::valarray<double>> kernel; // To store kernel (aka weights)
/**
* Constructor for neural_network::layers::DenseLayer class
* @param neurons number of neurons
* @param activation activation function for layer
* @param kernel_shape shape of kernel
* @param random_kernel flag for whether to intialize kernel randomly
*/
DenseLayer(const int &neurons, const std::string &activation,
const std::pair<size_t, size_t> &kernel_shape,
const bool &random_kernel) {
// Choosing activation (and it's derivative)
if (activation == "sigmoid") {
activation_function = neural_network::activations::sigmoid;
dactivation_function = neural_network::activations::sigmoid;
} else if (activation == "relu") {
activation_function = neural_network::activations::relu;
dactivation_function = neural_network::activations::drelu;
} else if (activation == "tanh") {
activation_function = neural_network::activations::tanh;
dactivation_function = neural_network::activations::dtanh;
} else if (activation == "none") {
// Set identity function in casse of none is supplied
activation_function =
neural_network::util_functions::identity_function;
dactivation_function =
neural_network::util_functions::identity_function;
} else {
// If supplied activation is invalid
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr << "Invalid argument. Expected {none, sigmoid, relu, "
"tanh} got ";
std::cerr << activation << std::endl;
std::exit(EXIT_FAILURE);
}
this->activation = activation; // Setting activation name
this->neurons = neurons; // Setting number of neurons
// Initialize kernel according to flag
if (random_kernel) {
uniform_random_initialization(kernel, kernel_shape, -1.0, 1.0);
} else {
unit_matrix_initialization(kernel, kernel_shape);
}
}
/**
* Constructor for neural_network::layers::DenseLayer class
* @param neurons number of neurons
* @param activation activation function for layer
* @param kernel values of kernel (useful in loading model)
*/
DenseLayer(const int &neurons, const std::string &activation,
const std::vector<std::valarray<double>> &kernel) {
// Choosing activation (and it's derivative)
if (activation == "sigmoid") {
activation_function = neural_network::activations::sigmoid;
dactivation_function = neural_network::activations::sigmoid;
} else if (activation == "relu") {
activation_function = neural_network::activations::relu;
dactivation_function = neural_network::activations::drelu;
} else if (activation == "tanh") {
activation_function = neural_network::activations::tanh;
dactivation_function = neural_network::activations::dtanh;
} else if (activation == "none") {
// Set identity function in casse of none is supplied
activation_function =
neural_network::util_functions::identity_function;
dactivation_function =
neural_network::util_functions::identity_function;
} else {
// If supplied activation is invalid
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr << "Invalid argument. Expected {none, sigmoid, relu, "
"tanh} got ";
std::cerr << activation << std::endl;
std::exit(EXIT_FAILURE);
}
this->activation = activation; // Setting activation name
this->neurons = neurons; // Setting number of neurons
this->kernel = kernel; // Setting supplied kernel values
}
/**
* Copy Constructor for class DenseLayer.
*
* @param model instance of class to be copied.
*/
DenseLayer(const DenseLayer &layer) = default;
/**
* Destructor for class DenseLayer.
*/
~DenseLayer() = default;
/**
* Copy assignment operator for class DenseLayer
*/
DenseLayer &operator=(const DenseLayer &layer) = default;
/**
* Move constructor for class DenseLayer
*/
DenseLayer(DenseLayer &&) = default;
/**
* Move assignment operator for class DenseLayer
*/
DenseLayer &operator=(DenseLayer &&) = default;
};
} // namespace layers
/**
* NeuralNetwork class is implements MLP. This class is
* used by actual user to create and train networks.
*
*/
class NeuralNetwork {
private:
std::vector<neural_network::layers::DenseLayer> layers; // To store layers
/**
* Private Constructor for class NeuralNetwork. This constructor
* is used internally to load model.
* @param config vector containing pair (neurons, activation)
* @param kernels vector containing all pretrained kernels
*/
NeuralNetwork(
const std::vector<std::pair<int, std::string>> &config,
const std::vector<std::vector<std::valarray<double>>> &kernels) {
// First layer should not have activation
if (config.begin()->second != "none") {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr
<< "First layer can't have activation other than none got "
<< config.begin()->second;
std::cerr << std::endl;
std::exit(EXIT_FAILURE);
}
// Network should have atleast two layers
if (config.size() <= 1) {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr << "Invalid size of network, ";
std::cerr << "Atleast two layers are required";
std::exit(EXIT_FAILURE);
}
// Reconstructing all pretrained layers
for (size_t i = 0; i < config.size(); i++) {
layers.emplace_back(neural_network::layers::DenseLayer(
config[i].first, config[i].second, kernels[i]));
}
std::cout << "INFO: Network constructed successfully" << std::endl;
}
/**
* Private function to get detailed predictions (i.e.
* activated neuron values). This function is used in
* backpropagation, single predict and batch predict.
* @param X input vector
*/
std::vector<std::vector<std::valarray<double>>>
__detailed_single_prediction(const std::vector<std::valarray<double>> &X) {
std::vector<std::vector<std::valarray<double>>> details;
std::vector<std::valarray<double>> current_pass = X;
details.emplace_back(X);
for (const auto &l : layers) {
current_pass = multiply(current_pass, l.kernel);
current_pass = apply_function(current_pass, l.activation_function);
details.emplace_back(current_pass);
}
return details;
}
public:
/**
* Default Constructor for class NeuralNetwork. This constructor
* is used to create empty variable of type NeuralNetwork class.
*/
NeuralNetwork() = default;
/**
* Constructor for class NeuralNetwork. This constructor
* is used by user.
* @param config vector containing pair (neurons, activation)
*/
explicit NeuralNetwork(
const std::vector<std::pair<int, std::string>> &config) {
// First layer should not have activation
if (config.begin()->second != "none") {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr
<< "First layer can't have activation other than none got "
<< config.begin()->second;
std::cerr << std::endl;
std::exit(EXIT_FAILURE);
}
// Network should have atleast two layers
if (config.size() <= 1) {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr << "Invalid size of network, ";
std::cerr << "Atleast two layers are required";
std::exit(EXIT_FAILURE);
}
// Separately creating first layer so it can have unit matrix
// as kernel.
layers.push_back(neural_network::layers::DenseLayer(
config[0].first, config[0].second,
{config[0].first, config[0].first}, false));
// Creating remaining layers
for (size_t i = 1; i < config.size(); i++) {
layers.push_back(neural_network::layers::DenseLayer(
config[i].first, config[i].second,
{config[i - 1].first, config[i].first}, true));
}
std::cout << "INFO: Network constructed successfully" << std::endl;
}
/**
* Copy Constructor for class NeuralNetwork.
*
* @param model instance of class to be copied.
*/
NeuralNetwork(const NeuralNetwork &model) = default;
/**
* Destructor for class NeuralNetwork.
*/
~NeuralNetwork() = default;
/**
* Copy assignment operator for class NeuralNetwork
*/
NeuralNetwork &operator=(const NeuralNetwork &model) = default;
/**
* Move constructor for class NeuralNetwork
*/
NeuralNetwork(NeuralNetwork &&) = default;
/**
* Move assignment operator for class NeuralNetwork
*/
NeuralNetwork &operator=(NeuralNetwork &&) = default;
/**
* Function to get X and Y from csv file (where X = data, Y = label)
* @param file_name csv file name
* @param last_label flag for whether label is in first or last column
* @param normalize flag for whether to normalize data
* @param slip_lines number of lines to skip
* @return returns pair of X and Y
*/
std::pair<std::vector<std::vector<std::valarray<double>>>,
std::vector<std::vector<std::valarray<double>>>>
get_XY_from_csv(const std::string &file_name, const bool &last_label,
const bool &normalize, const int &slip_lines = 1) {
std::ifstream in_file; // Ifstream to read file
in_file.open(file_name.c_str(), std::ios::in); // Open file
// If there is any problem in opening file
if (!in_file.is_open()) {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr << "Unable to open file: " << file_name << std::endl;
std::exit(EXIT_FAILURE);
}
std::vector<std::vector<std::valarray<double>>> X,
Y; // To store X and Y
std::string line; // To store each line
// Skip lines
for (int i = 0; i < slip_lines; i++) {
std::getline(in_file, line, '\n'); // Ignore line
}
// While file has information
while (!in_file.eof() && std::getline(in_file, line, '\n')) {
std::valarray<double> x_data,
y_data; // To store single sample and label
std::stringstream ss(line); // Constructing stringstream from line
std::string token; // To store each token in line (seprated by ',')
while (std::getline(ss, token, ',')) { // For each token
// Insert numerical value of token in x_data
x_data = insert_element(x_data, std::stod(token));
}
// If label is in last column
if (last_label) {
y_data.resize(this->layers.back().neurons);
// If task is classification
if (y_data.size() > 1) {
y_data[x_data[x_data.size() - 1]] = 1;
}
// If task is regrssion (of single value)
else {
y_data[0] = x_data[x_data.size() - 1];
}
x_data = pop_back(x_data); // Remove label from x_data
} else {
y_data.resize(this->layers.back().neurons);
// If task is classification
if (y_data.size() > 1) {
y_data[x_data[x_data.size() - 1]] = 1;
}
// If task is regrssion (of single value)
else {
y_data[0] = x_data[x_data.size() - 1];
}
x_data = pop_front(x_data); // Remove label from x_data
}
// Push collected X_data and y_data in X and Y
X.push_back({x_data});
Y.push_back({y_data});
}
// Normalize training data if flag is set
if (normalize) {
// Scale data between 0 and 1 using min-max scaler
X = minmax_scaler(X, 0.01, 1.0);
}
in_file.close(); // Closing file
return make_pair(X, Y); // Return pair of X and Y
}
/**
* Function to get prediction of model on single sample.
* @param X array of feature vectors
* @return returns predictions as vector
*/
std::vector<std::valarray<double>> single_predict(
const std::vector<std::valarray<double>> &X) {
// Get activations of all layers
auto activations = this->__detailed_single_prediction(X);
// Return activations of last layer (actual predicted values)
return activations.back();
}
/**
* Function to get prediction of model on batch
* @param X array of feature vectors
* @return returns predicted values as vector
*/
std::vector<std::vector<std::valarray<double>>> batch_predict(
const std::vector<std::vector<std::valarray<double>>> &X) {
// Store predicted values
std::vector<std::vector<std::valarray<double>>> predicted_batch(
X.size());
for (size_t i = 0; i < X.size(); i++) { // For every sample
// Push predicted values
predicted_batch[i] = this->single_predict(X[i]);
}
return predicted_batch; // Return predicted values
}
/**
* Function to fit model on supplied data
* @param X array of feature vectors
* @param Y array of target values
* @param epochs number of epochs (default = 100)
* @param learning_rate learning rate (default = 0.01)
* @param batch_size batch size for gradient descent (default = 32)
* @param shuffle flag for whether to shuffle data (default = true)
*/
void fit(const std::vector<std::vector<std::valarray<double>>> &X_,
const std::vector<std::vector<std::valarray<double>>> &Y_,
const int &epochs = 100, const double &learning_rate = 0.01,
const size_t &batch_size = 32, const bool &shuffle = true) {
std::vector<std::vector<std::valarray<double>>> X = X_, Y = Y_;
// Both label and input data should have same size
if (X.size() != Y.size()) {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr << "X and Y in fit have different sizes" << std::endl;
std::exit(EXIT_FAILURE);
}
std::cout << "INFO: Training Started" << std::endl;
for (int epoch = 1; epoch <= epochs; epoch++) { // For every epoch
// Shuffle X and Y if flag is set
if (shuffle) {
equal_shuffle(X, Y);
}
auto start =
std::chrono::high_resolution_clock::now(); // Start clock
double loss = 0,
acc = 0; // Intialize performance metrics with zero
// For each starting index of batch
for (size_t batch_start = 0; batch_start < X.size();
batch_start += batch_size) {
for (size_t i = batch_start;
i < std::min(X.size(), batch_start + batch_size); i++) {
std::vector<std::valarray<double>> grad, cur_error,
predicted;
auto activations = this->__detailed_single_prediction(X[i]);
// Gradients vector to store gradients for all layers
// They will be averaged and applied to kernel
std::vector<std::vector<std::valarray<double>>> gradients;
gradients.resize(this->layers.size());
// First intialize gradients to zero
for (size_t i = 0; i < gradients.size(); i++) {
zeroes_initialization(
gradients[i], get_shape(this->layers[i].kernel));
}
predicted = activations.back(); // Predicted vector
cur_error = predicted - Y[i]; // Absoulute error
// Calculating loss with MSE
loss += sum(apply_function(
cur_error, neural_network::util_functions::square));
// If prediction is correct
if (argmax(predicted) == argmax(Y[i])) {
acc += 1;
}
// For every layer (except first) starting from last one
for (size_t j = this->layers.size() - 1; j >= 1; j--) {
// Backpropogating errors
cur_error = hadamard_product(
cur_error,
apply_function(
activations[j + 1],
this->layers[j].dactivation_function));
// Calculating gradient for current layer
grad = multiply(transpose(activations[j]), cur_error);
// Change error according to current kernel values
cur_error = multiply(cur_error,
transpose(this->layers[j].kernel));
// Adding gradient values to collection of gradients
gradients[j] = gradients[j] + grad / double(batch_size);
}
// Applying gradients
for (size_t j = this->layers.size() - 1; j >= 1; j--) {
// Updating kernel (aka weights)
this->layers[j].kernel = this->layers[j].kernel -
gradients[j] * learning_rate;
}
}
}
auto stop =
std::chrono::high_resolution_clock::now(); // Stoping the clock
// Calculate time taken by epoch
auto duration =
std::chrono::duration_cast<std::chrono::microseconds>(stop -
start);
loss /= X.size(); // Averaging loss
acc /= X.size(); // Averaging accuracy
std::cout.precision(4); // set output precision to 4
// Printing training stats
std::cout << "Training: Epoch " << epoch << '/' << epochs;
std::cout << ", Loss: " << loss;
std::cout << ", Accuracy: " << acc;
std::cout << ", Taken time: " << duration.count() / 1e6
<< " seconds";
std::cout << std::endl;
}
return;
}
/**
* Function to fit model on data stored in csv file
* @param file_name csv file name
* @param last_label flag for whether label is in first or last column
* @param epochs number of epochs
* @param learning_rate learning rate
* @param normalize flag for whether to normalize data
* @param slip_lines number of lines to skip
* @param batch_size batch size for gradient descent (default = 32)
* @param shuffle flag for whether to shuffle data (default = true)
*/
void fit_from_csv(const std::string &file_name, const bool &last_label,
const int &epochs, const double &learning_rate,
const bool &normalize, const int &slip_lines = 1,
const size_t &batch_size = 32,
const bool &shuffle = true) {
// Getting training data from csv file
auto data =
this->get_XY_from_csv(file_name, last_label, normalize, slip_lines);
// Fit the model on training data
this->fit(data.first, data.second, epochs, learning_rate, batch_size,
shuffle);
return;
}
/**
* Function to evaluate model on supplied data
* @param X array of feature vectors (input data)
* @param Y array of target values (label)
*/
void evaluate(const std::vector<std::vector<std::valarray<double>>> &X,
const std::vector<std::vector<std::valarray<double>>> &Y) {
std::cout << "INFO: Evaluation Started" << std::endl;
double acc = 0, loss = 0; // intialize performance metrics with zero
for (size_t i = 0; i < X.size(); i++) { // For every sample in input
// Get predictions
std::vector<std::valarray<double>> pred =
this->single_predict(X[i]);
// If predicted class is correct
if (argmax(pred) == argmax(Y[i])) {
acc += 1; // Increment accuracy
}
// Calculating loss - Mean Squared Error
loss += sum(apply_function((Y[i] - pred),
neural_network::util_functions::square) *
0.5);
}
acc /= X.size(); // Averaging accuracy
loss /= X.size(); // Averaging loss
// Prinitng performance of the model
std::cout << "Evaluation: Loss: " << loss;
std::cout << ", Accuracy: " << acc << std::endl;
return;
}
/**
* Function to evaluate model on data stored in csv file
* @param file_name csv file name
* @param last_label flag for whether label is in first or last column
* @param normalize flag for whether to normalize data
* @param slip_lines number of lines to skip
*/
void evaluate_from_csv(const std::string &file_name, const bool &last_label,
const bool &normalize, const int &slip_lines = 1) {
// Getting training data from csv file
auto data =
this->get_XY_from_csv(file_name, last_label, normalize, slip_lines);
// Evaluating model
this->evaluate(data.first, data.second);
return;
}
/**
* Function to save current model.
* @param file_name file name to save model (*.model)
*/
void save_model(const std::string &_file_name) {
std::string file_name = _file_name;
// Adding ".model" extension if it is not already there in name
if (file_name.find(".model") == file_name.npos) {
file_name += ".model";
}
std::ofstream out_file; // Ofstream to write in file
// Open file in out|trunc mode
out_file.open(file_name.c_str(),
std::ofstream::out | std::ofstream::trunc);
// If there is any problem in opening file
if (!out_file.is_open()) {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr << "Unable to open file: " << file_name << std::endl;
std::exit(EXIT_FAILURE);
}
/**
Format in which model is saved:
total_layers
neurons(1st neural_network::layers::DenseLayer) activation_name(1st
neural_network::layers::DenseLayer) kernel_shape(1st
neural_network::layers::DenseLayer) kernel_values
.
.
.
neurons(Nth neural_network::layers::DenseLayer) activation_name(Nth
neural_network::layers::DenseLayer) kernel_shape(Nth
neural_network::layers::DenseLayer) kernel_value
For Example, pretrained model with 3 layers:
<pre>
3
4 none
4 4
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
6 relu
4 6
-1.88963 -3.61165 1.30757 -0.443906 -2.41039 -2.69653
-0.684753 0.0891452 0.795294 -2.39619 2.73377 0.318202
-2.91451 -4.43249 -0.804187 2.51995 -6.97524 -1.07049
-0.571531 -1.81689 -1.24485 1.92264 -2.81322 1.01741
3 sigmoid
6 3
0.390267 -0.391703 -0.0989607
0.499234 -0.564539 -0.28097
0.553386 -0.153974 -1.92493
-2.01336 -0.0219682 1.44145
1.72853 -0.465264 -0.705373
-0.908409 -0.740547 0.376416
</pre>
*/
// Saving model in the same format
out_file << layers.size();
out_file << std::endl;
for (const auto &layer : this->layers) {
out_file << layer.neurons << ' ' << layer.activation << std::endl;
const auto shape = get_shape(layer.kernel);
out_file << shape.first << ' ' << shape.second << std::endl;
for (const auto &row : layer.kernel) {
for (const auto &val : row) {
out_file << val << ' ';
}
out_file << std::endl;
}
}
std::cout << "INFO: Model saved successfully with name : ";
std::cout << file_name << std::endl;
out_file.close(); // Closing file
return;
}
/**
* Function to load earlier saved model.
* @param file_name file from which model will be loaded (*.model)
* @return instance of NeuralNetwork class with pretrained weights
*/
NeuralNetwork load_model(const std::string &file_name) {
std::ifstream in_file; // Ifstream to read file
in_file.open(file_name.c_str()); // Openinig file
// If there is any problem in opening file
if (!in_file.is_open()) {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr << "Unable to open file: " << file_name << std::endl;
std::exit(EXIT_FAILURE);
}
std::vector<std::pair<int, std::string>> config; // To store config
std::vector<std::vector<std::valarray<double>>>
kernels; // To store pretrained kernels
// Loading model from saved file format
size_t total_layers = 0;
in_file >> total_layers;
for (size_t i = 0; i < total_layers; i++) {
int neurons = 0;
std::string activation;
size_t shape_a = 0, shape_b = 0;
std::vector<std::valarray<double>> kernel;
in_file >> neurons >> activation >> shape_a >> shape_b;
for (size_t r = 0; r < shape_a; r++) {
std::valarray<double> row(shape_b);
for (size_t c = 0; c < shape_b; c++) {
in_file >> row[c];
}
kernel.push_back(row);
}
config.emplace_back(make_pair(neurons, activation));
;
kernels.emplace_back(kernel);
}
std::cout << "INFO: Model loaded successfully" << std::endl;
in_file.close(); // Closing file
return NeuralNetwork(
config, kernels); // Return instance of NeuralNetwork class
}
/**
* Function to print summary of the network.
*/
void summary() {
// Printing Summary
std::cout
<< "==============================================================="
<< std::endl;
std::cout << "\t\t+ MODEL SUMMARY +\t\t\n";
std::cout
<< "==============================================================="
<< std::endl;
for (size_t i = 1; i <= layers.size(); i++) { // For every layer
std::cout << i << ")";
std::cout << " Neurons : "
<< layers[i - 1].neurons; // number of neurons
std::cout << ", Activation : "
<< layers[i - 1].activation; // activation
std::cout << ", kernel Shape : "
<< get_shape(layers[i - 1].kernel); // kernel shape
std::cout << std::endl;
}
std::cout
<< "==============================================================="
<< std::endl;
return;
}
};
} // namespace neural_network
} // namespace machine_learning
/**
* Function to test neural network
* @returns none
*/
static void test() {
// Creating network with 3 layers for "iris.csv"
machine_learning::neural_network::NeuralNetwork myNN =
machine_learning::neural_network::NeuralNetwork({
{4, "none"}, // First layer with 3 neurons and "none" as activation
{6,
"relu"}, // Second layer with 6 neurons and "relu" as activation
{3, "sigmoid"} // Third layer with 3 neurons and "sigmoid" as
// activation
});
// Printing summary of model
myNN.summary();
// Training Model
myNN.fit_from_csv("iris.csv", true, 100, 0.3, false, 2, 32, true);
// Testing predictions of model
assert(machine_learning::argmax(
myNN.single_predict({{5, 3.4, 1.6, 0.4}})) == 0);
assert(machine_learning::argmax(
myNN.single_predict({{6.4, 2.9, 4.3, 1.3}})) == 1);
assert(machine_learning::argmax(
myNN.single_predict({{6.2, 3.4, 5.4, 2.3}})) == 2);
return;
}
/** Driver Code */
int main() {
// Testing
test();
return 0;
}

View File

@@ -0,0 +1,514 @@
/**
* @file vector_ops.hpp
* @author [Deep Raval](https://github.com/imdeep2905)
*
* @brief Various functions for vectors associated with [NeuralNetwork (aka
* Multilayer Perceptron)]
* (https://en.wikipedia.org/wiki/Multilayer_perceptron).
*
*/
#ifndef VECTOR_OPS_FOR_NN
#define VECTOR_OPS_FOR_NN
#include <algorithm>
#include <chrono>
#include <iostream>
#include <random>
#include <valarray>
#include <vector>
/**
* @namespace machine_learning
* @brief Machine Learning algorithms
*/
namespace machine_learning {
/**
* Overloaded operator "<<" to print 2D vector
* @tparam T typename of the vector
* @param out std::ostream to output
* @param A 2D vector to be printed
*/
template <typename T>
std::ostream &operator<<(std::ostream &out,
std::vector<std::valarray<T>> const &A) {
// Setting output precision to 4 in case of floating point numbers
out.precision(4);
for (const auto &a : A) { // For each row in A
for (const auto &x : a) { // For each element in row
std::cout << x << ' '; // print element
}
std::cout << std::endl;
}
return out;
}
/**
* Overloaded operator "<<" to print a pair
* @tparam T typename of the pair
* @param out std::ostream to output
* @param A Pair to be printed
*/
template <typename T>
std::ostream &operator<<(std::ostream &out, const std::pair<T, T> &A) {
// Setting output precision to 4 in case of floating point numbers
out.precision(4);
// printing pair in the form (p, q)
std::cout << "(" << A.first << ", " << A.second << ")";
return out;
}
/**
* Overloaded operator "<<" to print a 1D vector
* @tparam T typename of the vector
* @param out std::ostream to output
* @param A 1D vector to be printed
*/
template <typename T>
std::ostream &operator<<(std::ostream &out, const std::valarray<T> &A) {
// Setting output precision to 4 in case of floating point numbers
out.precision(4);
for (const auto &a : A) { // For every element in the vector.
std::cout << a << ' '; // Print element
}
std::cout << std::endl;
return out;
}
/**
* Function to insert element into 1D vector
* @tparam T typename of the 1D vector and the element
* @param A 1D vector in which element will to be inserted
* @param ele element to be inserted
* @return new resultant vector
*/
template <typename T>
std::valarray<T> insert_element(const std::valarray<T> &A, const T &ele) {
std::valarray<T> B; // New 1D vector to store resultant vector
B.resize(A.size() + 1); // Resizing it accordingly
for (size_t i = 0; i < A.size(); i++) { // For every element in A
B[i] = A[i]; // Copy element in B
}
B[B.size() - 1] = ele; // Inserting new element in last position
return B; // Return resultant vector
}
/**
* Function to remove first element from 1D vector
* @tparam T typename of the vector
* @param A 1D vector from which first element will be removed
* @return new resultant vector
*/
template <typename T>
std::valarray<T> pop_front(const std::valarray<T> &A) {
std::valarray<T> B; // New 1D vector to store resultant vector
B.resize(A.size() - 1); // Resizing it accordingly
for (size_t i = 1; i < A.size();
i++) { // // For every (except first) element in A
B[i - 1] = A[i]; // Copy element in B with left shifted position
}
return B; // Return resultant vector
}
/**
* Function to remove last element from 1D vector
* @tparam T typename of the vector
* @param A 1D vector from which last element will be removed
* @return new resultant vector
*/
template <typename T>
std::valarray<T> pop_back(const std::valarray<T> &A) {
std::valarray<T> B; // New 1D vector to store resultant vector
B.resize(A.size() - 1); // Resizing it accordingly
for (size_t i = 0; i < A.size() - 1;
i++) { // For every (except last) element in A
B[i] = A[i]; // Copy element in B
}
return B; // Return resultant vector
}
/**
* Function to equally shuffle two 3D vectors (used for shuffling training data)
* @tparam T typename of the vector
* @param A First 3D vector
* @param B Second 3D vector
*/
template <typename T>
void equal_shuffle(std::vector<std::vector<std::valarray<T>>> &A,
std::vector<std::vector<std::valarray<T>>> &B) {
// If two vectors have different sizes
if (A.size() != B.size()) {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr
<< "Can not equally shuffle two vectors with different sizes: ";
std::cerr << A.size() << " and " << B.size() << std::endl;
std::exit(EXIT_FAILURE);
}
for (size_t i = 0; i < A.size(); i++) { // For every element in A and B
// Genrating random index < size of A and B
std::srand(std::chrono::system_clock::now().time_since_epoch().count());
size_t random_index = std::rand() % A.size();
// Swap elements in both A and B with same random index
std::swap(A[i], A[random_index]);
std::swap(B[i], B[random_index]);
}
return;
}
/**
* Function to initialize given 2D vector using uniform random initialization
* @tparam T typename of the vector
* @param A 2D vector to be initialized
* @param shape required shape
* @param low lower limit on value
* @param high upper limit on value
*/
template <typename T>
void uniform_random_initialization(std::vector<std::valarray<T>> &A,
const std::pair<size_t, size_t> &shape,
const T &low, const T &high) {
A.clear(); // Making A empty
// Uniform distribution in range [low, high]
std::default_random_engine generator(
std::chrono::system_clock::now().time_since_epoch().count());
std::uniform_real_distribution<T> distribution(low, high);
for (size_t i = 0; i < shape.first; i++) { // For every row
std::valarray<T>
row; // Making empty row which will be inserted in vector
row.resize(shape.second);
for (auto &r : row) { // For every element in row
r = distribution(generator); // copy random number
}
A.push_back(row); // Insert new row in vector
}
return;
}
/**
* Function to Intialize 2D vector as unit matrix
* @tparam T typename of the vector
* @param A 2D vector to be initialized
* @param shape required shape
*/
template <typename T>
void unit_matrix_initialization(std::vector<std::valarray<T>> &A,
const std::pair<size_t, size_t> &shape) {
A.clear(); // Making A empty
for (size_t i = 0; i < shape.first; i++) {
std::valarray<T>
row; // Making empty row which will be inserted in vector
row.resize(shape.second);
row[i] = T(1); // Insert 1 at ith position
A.push_back(row); // Insert new row in vector
}
return;
}
/**
* Function to Intialize 2D vector as zeroes
* @tparam T typename of the vector
* @param A 2D vector to be initialized
* @param shape required shape
*/
template <typename T>
void zeroes_initialization(std::vector<std::valarray<T>> &A,
const std::pair<size_t, size_t> &shape) {
A.clear(); // Making A empty
for (size_t i = 0; i < shape.first; i++) {
std::valarray<T>
row; // Making empty row which will be inserted in vector
row.resize(shape.second); // By default all elements are zero
A.push_back(row); // Insert new row in vector
}
return;
}
/**
* Function to get sum of all elements in 2D vector
* @tparam T typename of the vector
* @param A 2D vector for which sum is required
* @return returns sum of all elements of 2D vector
*/
template <typename T>
T sum(const std::vector<std::valarray<T>> &A) {
T cur_sum = 0; // Initially sum is zero
for (const auto &a : A) { // For every row in A
cur_sum += a.sum(); // Add sum of that row to current sum
}
return cur_sum; // Return sum
}
/**
* Function to get shape of given 2D vector
* @tparam T typename of the vector
* @param A 2D vector for which shape is required
* @return shape as pair
*/
template <typename T>
std::pair<size_t, size_t> get_shape(const std::vector<std::valarray<T>> &A) {
const size_t sub_size = (*A.begin()).size();
for (const auto &a : A) {
// If supplied vector don't have same shape in all rows
if (a.size() != sub_size) {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr << "Supplied vector is not 2D Matrix" << std::endl;
std::exit(EXIT_FAILURE);
}
}
return std::make_pair(A.size(), sub_size); // Return shape as pair
}
/**
* Function to scale given 3D vector using min-max scaler
* @tparam T typename of the vector
* @param A 3D vector which will be scaled
* @param low new minimum value
* @param high new maximum value
* @return new scaled 3D vector
*/
template <typename T>
std::vector<std::vector<std::valarray<T>>> minmax_scaler(
const std::vector<std::vector<std::valarray<T>>> &A, const T &low,
const T &high) {
std::vector<std::vector<std::valarray<T>>> B =
A; // Copying into new vector B
const auto shape = get_shape(B[0]); // Storing shape of B's every element
// As this function is used for scaling training data vector should be of
// shape (1, X)
if (shape.first != 1) {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr
<< "Supplied vector is not supported for minmax scaling, shape: ";
std::cerr << shape << std::endl;
std::exit(EXIT_FAILURE);
}
for (size_t i = 0; i < shape.second; i++) {
T min = B[0][0][i], max = B[0][0][i];
for (size_t j = 0; j < B.size(); j++) {
// Updating minimum and maximum values
min = std::min(min, B[j][0][i]);
max = std::max(max, B[j][0][i]);
}
for (size_t j = 0; j < B.size(); j++) {
// Applying min-max scaler formula
B[j][0][i] =
((B[j][0][i] - min) / (max - min)) * (high - low) + low;
}
}
return B; // Return new resultant 3D vector
}
/**
* Function to get index of maximum element in 2D vector
* @tparam T typename of the vector
* @param A 2D vector for which maximum index is required
* @return index of maximum element
*/
template <typename T>
size_t argmax(const std::vector<std::valarray<T>> &A) {
const auto shape = get_shape(A);
// As this function is used on predicted (or target) vector, shape should be
// (1, X)
if (shape.first != 1) {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr << "Supplied vector is ineligible for argmax" << std::endl;
std::exit(EXIT_FAILURE);
}
// Return distance of max element from first element (i.e. index)
return std::distance(std::begin(A[0]),
std::max_element(std::begin(A[0]), std::end(A[0])));
}
/**
* Function which applys supplied function to every element of 2D vector
* @tparam T typename of the vector
* @param A 2D vector on which function will be applied
* @param func Function to be applied
* @return new resultant vector
*/
template <typename T>
std::vector<std::valarray<T>> apply_function(
const std::vector<std::valarray<T>> &A, T (*func)(const T &)) {
std::vector<std::valarray<double>> B =
A; // New vector to store resultant vector
for (auto &b : B) { // For every row in vector
b = b.apply(func); // Apply function to that row
}
return B; // Return new resultant 2D vector
}
/**
* Overloaded operator "*" to multiply given 2D vector with scaler
* @tparam T typename of both vector and the scaler
* @param A 2D vector to which scaler will be multiplied
* @param val Scaler value which will be multiplied
* @return new resultant vector
*/
template <typename T>
std::vector<std::valarray<T>> operator*(const std::vector<std::valarray<T>> &A,
const T &val) {
std::vector<std::valarray<double>> B =
A; // New vector to store resultant vector
for (auto &b : B) { // For every row in vector
b = b * val; // Multiply row with scaler
}
return B; // Return new resultant 2D vector
}
/**
* Overloaded operator "/" to divide given 2D vector with scaler
* @tparam T typename of the vector and the scaler
* @param A 2D vector to which scaler will be divided
* @param val Scaler value which will be divided
* @return new resultant vector
*/
template <typename T>
std::vector<std::valarray<T>> operator/(const std::vector<std::valarray<T>> &A,
const T &val) {
std::vector<std::valarray<double>> B =
A; // New vector to store resultant vector
for (auto &b : B) { // For every row in vector
b = b / val; // Divide row with scaler
}
return B; // Return new resultant 2D vector
}
/**
* Function to get transpose of 2D vector
* @tparam T typename of the vector
* @param A 2D vector which will be transposed
* @return new resultant vector
*/
template <typename T>
std::vector<std::valarray<T>> transpose(
const std::vector<std::valarray<T>> &A) {
const auto shape = get_shape(A); // Current shape of vector
std::vector<std::valarray<T>> B; // New vector to store result
// Storing transpose values of A in B
for (size_t j = 0; j < shape.second; j++) {
std::valarray<T> row;
row.resize(shape.first);
for (size_t i = 0; i < shape.first; i++) {
row[i] = A[i][j];
}
B.push_back(row);
}
return B; // Return new resultant 2D vector
}
/**
* Overloaded operator "+" to add two 2D vectors
* @tparam T typename of the vector
* @param A First 2D vector
* @param B Second 2D vector
* @return new resultant vector
*/
template <typename T>
std::vector<std::valarray<T>> operator+(
const std::vector<std::valarray<T>> &A,
const std::vector<std::valarray<T>> &B) {
const auto shape_a = get_shape(A);
const auto shape_b = get_shape(B);
// If vectors don't have equal shape
if (shape_a.first != shape_b.first || shape_a.second != shape_b.second) {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr << "Supplied vectors have different shapes ";
std::cerr << shape_a << " and " << shape_b << std::endl;
std::exit(EXIT_FAILURE);
}
std::vector<std::valarray<T>> C;
for (size_t i = 0; i < A.size(); i++) { // For every row
C.push_back(A[i] + B[i]); // Elementwise addition
}
return C; // Return new resultant 2D vector
}
/**
* Overloaded operator "-" to add subtract 2D vectors
* @tparam T typename of the vector
* @param A First 2D vector
* @param B Second 2D vector
* @return new resultant vector
*/
template <typename T>
std::vector<std::valarray<T>> operator-(
const std::vector<std::valarray<T>> &A,
const std::vector<std::valarray<T>> &B) {
const auto shape_a = get_shape(A);
const auto shape_b = get_shape(B);
// If vectors don't have equal shape
if (shape_a.first != shape_b.first || shape_a.second != shape_b.second) {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr << "Supplied vectors have different shapes ";
std::cerr << shape_a << " and " << shape_b << std::endl;
std::exit(EXIT_FAILURE);
}
std::vector<std::valarray<T>> C; // Vector to store result
for (size_t i = 0; i < A.size(); i++) { // For every row
C.push_back(A[i] - B[i]); // Elementwise substraction
}
return C; // Return new resultant 2D vector
}
/**
* Function to multiply two 2D vectors
* @tparam T typename of the vector
* @param A First 2D vector
* @param B Second 2D vector
* @return new resultant vector
*/
template <typename T>
std::vector<std::valarray<T>> multiply(const std::vector<std::valarray<T>> &A,
const std::vector<std::valarray<T>> &B) {
const auto shape_a = get_shape(A);
const auto shape_b = get_shape(B);
// If vectors are not eligible for multiplication
if (shape_a.second != shape_b.first) {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr << "Vectors are not eligible for multiplication ";
std::cerr << shape_a << " and " << shape_b << std::endl;
std::exit(EXIT_FAILURE);
}
std::vector<std::valarray<T>> C; // Vector to store result
// Normal matrix multiplication
for (size_t i = 0; i < shape_a.first; i++) {
std::valarray<T> row;
row.resize(shape_b.second);
for (size_t j = 0; j < shape_b.second; j++) {
for (size_t k = 0; k < shape_a.second; k++) {
row[j] += A[i][k] * B[k][j];
}
}
C.push_back(row);
}
return C; // Return new resultant 2D vector
}
/**
* Function to get hadamard product of two 2D vectors
* @tparam T typename of the vector
* @param A First 2D vector
* @param B Second 2D vector
* @return new resultant vector
*/
template <typename T>
std::vector<std::valarray<T>> hadamard_product(
const std::vector<std::valarray<T>> &A,
const std::vector<std::valarray<T>> &B) {
const auto shape_a = get_shape(A);
const auto shape_b = get_shape(B);
// If vectors are not eligible for hadamard product
if (shape_a.first != shape_b.first || shape_a.second != shape_b.second) {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr << "Vectors have different shapes ";
std::cerr << shape_a << " and " << shape_b << std::endl;
std::exit(EXIT_FAILURE);
}
std::vector<std::valarray<T>> C; // Vector to store result
for (size_t i = 0; i < A.size(); i++) {
C.push_back(A[i] * B[i]); // Elementwise multiplication
}
return C; // Return new resultant 2D vector
}
} // namespace machine_learning
#endif

64
math/check_factorial.cpp Normal file
View File

@@ -0,0 +1,64 @@
/**
* @file
* @brief A simple program to check if the given number is a factorial of some
* number or not.
* @author [Divyajyoti Ukirde](https://github.com/divyajyotiuk)
*/
#include <cassert>
#include <iostream>
/**
* Function to check if the given number is factorial of some number or not.
* @param n number to be checked.
* @return if number is a factorial, returns true, else false.
*/
bool is_factorial(uint64_t n) {
if (n <= 0) {
return false;
}
for (uint32_t i = 1;; i++) {
if (n % i != 0) {
break;
}
n = n / i;
}
if (n == 1) {
return true;
} else {
return false;
}
}
/** Test function
* @returns void
*/
void tests() {
std::cout << "Test 1:\t n=50\n";
assert(is_factorial(50) == false);
std::cout << "passed\n";
std::cout << "Test 2:\t n=720\n";
assert(is_factorial(720) == true);
std::cout << "passed\n";
std::cout << "Test 3:\t n=0\n";
assert(is_factorial(0) == false);
std::cout << "passed\n";
std::cout << "Test 4:\t n=479001600\n";
assert(is_factorial(479001600) == true);
std::cout << "passed\n";
std::cout << "Test 5:\t n=-24\n";
assert(is_factorial(-24) == false);
std::cout << "passed\n";
}
/** Main function
* @returns 0 on exit
*/
int main() {
tests();
return 0;
}

View File

@@ -13,12 +13,15 @@
/**
* Recursively compute sequences
* @param n input
* @returns n-th element of the Fbinacci's sequence
*/
unsigned int fibonacci(unsigned int n) {
uint64_t fibonacci(uint64_t n) {
/* If the input is 0 or 1 just return the same
This will set the first 2 values of the sequence */
if (n <= 1)
if (n <= 1) {
return n;
}
/* Add the last 2 values of the sequence to get next */
return fibonacci(n - 1) + fibonacci(n - 2);
@@ -30,27 +33,27 @@ unsigned int fibonacci(unsigned int n) {
* @returns `void`
*/
static void test() {
unsigned int test_case_1 = fibonacci(0);
uint64_t test_case_1 = fibonacci(0);
assert(test_case_1 == 0);
std::cout << "Passed Test 1!" << std::endl;
unsigned int test_case_2 = fibonacci(1);
uint64_t test_case_2 = fibonacci(1);
assert(test_case_2 == 1);
std::cout << "Passed Test 2!" << std::endl;
unsigned int test_case_3 = fibonacci(2);
uint64_t test_case_3 = fibonacci(2);
assert(test_case_3 == 1);
std::cout << "Passed Test 3!" << std::endl;
unsigned int test_case_4 = fibonacci(3);
uint64_t test_case_4 = fibonacci(3);
assert(test_case_4 == 2);
std::cout << "Passed Test 4!" << std::endl;
unsigned int test_case_5 = fibonacci(4);
uint64_t test_case_5 = fibonacci(4);
assert(test_case_5 == 3);
std::cout << "Passed Test 5!" << std::endl;
unsigned int test_case_6 = fibonacci(15);
uint64_t test_case_6 = fibonacci(15);
assert(test_case_6 == 610);
std::cout << "Passed Test 6!" << std::endl << std::endl;
}
@@ -58,7 +61,7 @@ static void test() {
/// Main function
int main() {
test();
int n;
int n = 0;
std::cin >> n;
assert(n >= 0);
std::cout << "F(" << n << ")= " << fibonacci(n) << std::endl;

View File

@@ -0,0 +1,178 @@
/**
* @file
* @brief Evaluation of [Postfix Expression](https://en.wikipedia.org/wiki/Reverse_Polish_notation)
* @author [Darshana Sarma](https://github.com/Darshana-Sarma)
* @details
* Create a stack to store operands (or values).
* Scan the given expression and do following for every scanned element.
* If the element is a number, push it into the stack
* If the element is a operator, pop operands for the operator from stack.
* Evaluate the operator and push the result back to the stack
* When the expression is ended, the number in the stack is the final answer
*/
#include <algorithm> // for all_of
#include <array> // for std::array
#include <cassert> // for assert
#include <iostream> // for io operations
#include <string> // for stof
/**
* @namespace others
* @brief Other algorithms
*/
namespace others {
/**
* @namespace postfix_expression
* @brief Functions for Postfix Expression algorithm
*/
namespace postfix_expression {
/**
* @brief Creates an array to be used as stack for storing values
*/
class Stack {
public:
std::array<float, 20> stack{}; ///< Array which will be used to store numbers in the input
int stackTop = -1; ///< Represents the index of the last value added to array. -1 means array is empty
};
/**
* @brief Pushing operand, also called the number in the array to the stack
* @param operand float value from the input array or evaluation
* @param stack stack containing numbers
* @returns none
*/
void push(float operand, Stack *stack) {
stack->stackTop++;
stack->stack[stack->stackTop] = operand;
}
/**
* @brief Popping operand, also called the number from the stack
* @param stack stack containing numbers
* @returns operand float on top of stack
*/
float pop(Stack *stack) {
float operand = stack->stack[stack->stackTop];
stack->stackTop--;
return operand;
}
/**
* @brief Checks if scanned string is a number
* @param s scanned string
* @returns bool boolean value if string is number
*/
bool is_number(const std::string &s) {
return !s.empty() && std::all_of(s.begin(), s.end(), ::isdigit);
}
/**
* @brief Evaluate answer using given last two operands from and operation
* @param a second last added operand which will be used for evaluation
* @param b last added operand which will be used for evaluation
* @param operation to be performed with respective floats
* @param stack containing numbers
* @returns none
*/
void evaluate(float a, float b, const std::string &operation, Stack *stack) {
float c = 0;
const char *op = operation.c_str();
switch (*op) {
case '+':
c = a + b; // Addition of numbers
others::postfix_expression::push(c, stack);
break;
case '-':
c = a - b; // Subtraction of numbers
others::postfix_expression::push(c, stack);
break;
case '*':
c = a * b; // Multiplication of numbers
others::postfix_expression::push(c, stack);
break;
case '/':
c = a / b; // Division of numbers
others::postfix_expression::push(c, stack);
break;
default:
std::cout << "Operator not defined\n";
break;
}
}
/**
* @brief Postfix Evaluation algorithm to compute the value from given input
* array
* @tparam N number of array size
* @param input Array of characters consisting of numbers and operations
* @returns stack[stackTop] returns the top value from the stack
*/
template <std::size_t N>
float postfix_evaluation(std::array<std::string, N> input) {
Stack stack;
int j = 0;
while (j < N) {
std::string scan = input[j];
if (is_number(scan)) {
push(std::stof(scan), &stack);
} else {
float op2 = pop(&stack);
float op1 = pop(&stack);
evaluate(op1, op2, scan, &stack);
}
j++;
}
std::cout << stack.stack[stack.stackTop] << "\n";
return stack.stack[stack.stackTop];
}
} // namespace postfix_expression
} // namespace others
/**
* @brief Test function 1 with input array
* {'2', '3', '1', '*', '+', '9', '-'}
* @returns none
*/
static void test_function_1() {
std::array<std::string, 7> input = {"2", "3", "1", "*", "+", "9", "-"};
float answer = others::postfix_expression::postfix_evaluation(input);
assert(answer == -4);
}
/**
* @brief Test function 2 with input array
* {'1', '2', '+', '2', '/', '5', '*', '7', '+'}
* @returns none
*/
static void test_function_2() {
std::array<std::string, 9> input = {"100", "200", "+", "2", "/",
"5", "*", "7", "+"};
float answer = others::postfix_expression::postfix_evaluation(input);
assert(answer == 757);
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
test_function_1();
test_function_2();
std::cout << "\nTest implementations passed!\n";
return 0;
}

View File

@@ -0,0 +1,639 @@
/**
* @file
* @brief [Heavy Light
* Decomposition](https://en.wikipedia.org/wiki/Heavy_path_decomposition)
* implementation
* @author [Aniruthan R](https://github.com/aneee004)
*
* @details
* Heavy-Light Decomposition is a technique on trees, that supports the
* following:
* 1. Update node s, with a value v
* 2. Return the (sum) of all node values on the simple path from a to b
* (sum) can also be replced with XOR, OR, AND, min, or max
*
* The update is done in O(log n) time, and
* the query is done in O(log^2 n) time with HLD
* where, n is the number of nodes
*
* The template type is the data type of the value stored in the nodes.
* If a non-primitive data-type is used as a template,
* the coressponding operators must be overloaded.
*
* An HLD object can only be created with a constant number of nodes, and
* it cannot be changed later. Creaty an empty instance is not supported.
*
* To start answering updates and queries,
* 1. Create an instance of HLD<X> object (obj), with the required data type.
* 2. Read in the edge/parent information and update it with obj.add_edge().
* Note: The edges addes must be 0 indexed.
* 3. Create a vector with initial node values, and call set_node_val() with it.
* 4. Call obj.init() to populate the required information for supporting
* operations.
* 5. Call obj.update(node, new_val), to update the value at index 'node' to the
* new value. Note: node must be 0 indexed
* 6. Call obj.query(a, b) to get the (sum) of node values in the simple path
* from a to b. Note: a and b, must be 0 indexed.
*
* Sample I/O at the bottom.
* @todo Support edge weight queries, by storing the edge weight value in it's
* child algorithm verified by testing in CSES path queries:
* https://cses.fi/problemset/task/1138
*/
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstring>
#include <iostream>
#include <list>
#include <numeric>
#include <string>
#include <vector>
/**
* @namespace range_queries
* @brief Algorithms and Data Structures that support range queries and updates.
*/
namespace range_queries {
/**
* @namespace heavy_light_decomposition
* @brief Heavy light decomposition algorithm
*/
namespace heavy_light_decomposition {
/**
* @brief A Basic Tree, which supports binary lifting
* @tparam the data type of the values stored in the tree nodes
* @details Deleting the default constructor
* An instance can only be created with the number of nodes
* Defaults:
* t_node indexing are zero based
* t_root is 0
* depth of root_node is 0
* Supports:
* lift :- lift a node k units up the tree
* kth_ancestor :- returns the kth ancestor
* lca :- returns the least common ancestor
*/
template <typename X> class Tree {
//
private:
std::vector<std::list<int>>
t_adj; ///< an adjacency list to stores the tree edges
const int t_nodes, ///< number of nodes
t_maxlift; ///< maximum possible height of the tree
std::vector<std::vector<int>>
t_par; ///< a matrix to store every node's 2^kth parent
std::vector<int> t_depth, ///< a vector to store the depth of a node,
t_size; ///< a vector to store the subtree size rooted at node
int t_root; ///< the root of the tree
std::vector<X> t_val; ///< values of nodes
template <typename T> friend class HLD;
/**
* @brief Utility function to compute sub-tree sizes
* @param u current dfs node
* @param p the parent of node @param u
* @returns void
*/
void dfs_size(int u, int p = -1) {
for (const int &v : t_adj[u]) {
if (v ^ p) {
dfs_size(v, u);
t_size[u] += t_size[v];
}
}
}
/**
* @brief Utility function to populate the t_par vector
* @param u current dfs node
* @param p the parent of node u
* @returns void
*/
void dfs_lca(int u, int p = -1) {
t_par[u][0] = p;
if (p != -1) {
t_depth[u] = 1 + t_depth[p];
}
for (int k = 1; k < t_maxlift; k++) {
if (t_par[u][k - 1] != -1) {
t_par[u][k] = t_par[t_par[u][k - 1]][k - 1];
}
}
for (const int &v : t_adj[u]) {
if (v ^ p) {
dfs_lca(v, u);
}
}
}
public:
/**
* @brief Class parameterized constructor, resizes the and initializes the
* data members
* @param nodes the total number of nodes in the tree
*/
explicit Tree(int nodes)
: t_nodes(nodes), t_maxlift(static_cast<int>(floor(log2(nodes))) + 1) {
/* Initialize and resize all the vectors */
t_root = 0; /* Default */
t_adj.resize(t_nodes);
t_par.assign(t_nodes, std::vector<int>(t_maxlift, -1));
t_depth.assign(t_nodes, 0);
t_size.assign(t_nodes, 1);
t_val.resize(t_nodes);
}
/**
* @brief Adds an undirected edge from node u to node v in the tree
* @param u the node where the edge is from
* @param v the node where the edge is to
* @returns void
*/
void add_edge(const int u, const int v) {
t_adj[u].push_back(v);
t_adj[v].push_back(u);
}
/**
* @brief Set the root for the tree
* @param new_root the new root
* @returns void
*/
void change_root(int new_root) { t_root = new_root; }
/**
* @brief Set the values for all the nodes
* @param node_val a vector of size n, with all the node values where, n is
* the number of nodes
* @returns void
*/
void set_node_val(const std::vector<X> &node_val) {
assert(static_cast<int>(node_val.size()) == t_nodes);
t_val = node_val;
}
/**
* @brief This function must be called after the tree adjacency list and node
* values are populated The function initializes the required parameters, and
* populates the segment tree
* @returns void
*/
void init() {
assert(t_nodes > 0);
dfs_size(t_root);
dfs_lca(t_root);
}
/**
* @brief The function lifts a node, k units up the tree.
* The lifting is done in place, and the result is stored in the address
* pointed by p.
* @param p a pointer to the variable that stores the node id
* @param dist the distance to move up the tree
* @returns void
*/
void lift(int *const p, int dist) {
for (int k = 0; k < t_maxlift; k++) {
if (*p == -1) {
return;
}
if (dist & 1) {
*p = t_par[*p][k];
}
dist >>= 1;
}
}
/**
* @brief The function returns the kth ancestor of a node
* @param p the node id whose kth ancestor is to be found
* @param dist the distance to move up the tree
* @returns the kth ancestor of node
*/
int kth_ancestor(int p, const int &dist) {
lift(&p, dist);
return p;
}
/**
* @brief The function returns the least common ancestor of two nodes
* @param a node id_1
* @param b node id_2
* @returns the least common ancestor of node a, and node b
*/
int lca(int a, int b) {
assert(a >= 0 and b >= 0 and a < t_nodes and b < t_nodes);
if (t_depth[a] > t_depth[b]) {
lift(&a, t_depth[a] - t_depth[b]);
}
if (t_depth[b] > t_depth[a]) {
lift(&b, t_depth[b] - t_depth[a]);
}
if (a == b) {
return a;
}
for (int k = t_maxlift - 1; k >= 0; k--) {
if (t_par[a][k] != t_par[b][k]) {
a = t_par[a][k];
b = t_par[b][k];
}
}
return t_par[a][0];
}
};
/**
* @brief Segment Tree, to store heavy chains
* @tparam the data type of the values stored in the tree nodes
*/
template <typename X> class SG {
private:
/**
* @brief Everything here is private,
* and can only be accessed through the methods,
* in the derived class (HLD)
*/
std::vector<X> s_tree; ///< the segment tree, stored as a vector
int s_size; ///< number of leaves in the segment tree
X sret_init = 0; ///< inital query return value
template <typename T> friend class HLD;
/**
* @brief Function that specifies the type of operation involved when segments
* are combined
* @param lhs the left segment
* @param rhs the right segment
* @returns the combined result
*/
X combine(X lhs, X rhs) { return lhs + rhs; }
/**
* @brief Class parameterized constructor. Resizes the and initilizes the data
* members.
* @param nodes the total number of nodes in the tree
* @returns void
*/
explicit SG(int size) {
s_size = size;
s_tree.assign(2 * s_size, 0ll);
}
/**
* @brief Update the value at a node
* @param p the node to be udpated
* @param v the update value
* @returns void
*/
void update(int p, X v) {
for (p += s_size; p > 0; p >>= 1) {
s_tree[p] += v;
}
}
/**
* @brief Make a range query from node label l to node label r
* @param l node label where the path starts
* @param r node label where the path ends
* @returns void
*/
X query(int l, int r) {
X lhs = sret_init, rhs = sret_init;
for (l += s_size, r += s_size + 1; l < r; l >>= 1, r >>= 1) {
if (l & 1) {
lhs = combine(lhs, s_tree[l++]);
}
if (r & 1) {
rhs = combine(s_tree[--r], rhs);
}
}
return combine(lhs, rhs);
}
/**
* @brief Set the initialization for the query data type, based on requirement
*
* @details
* Change the sret_init, based on requirement:
* * Sum Query: 0 (Default)
* * XOR Query: 0 (Default)
* * Min Query: Infinity
* * Max Query: -Infinity
* @param new_sret_init the new init
*/
void set_sret_init(X new_sret_init) { sret_init = new_sret_init; }
};
/**
* @brief The Heavy-Light Decomposition class
* @tparam the data type of the values stored in the tree nodes
*/
template <typename X> class HLD : public Tree<X>, public SG<X> {
private:
int label; ///< utility member to assign labels in dfs_labels()
std::vector<int> h_label, ///< stores the label of a node
h_heavychlid, ///< stores the heavy child of a node
h_parent; ///< stores the top of the heavy chain from a node
/**
* @brief Utility function to assign heavy child to each node (-1 for a leaf
* node)
* @param u current dfs node
* @param p the parent of node u
* @returns void
*/
void dfs_hc(int u, int p = -1) {
int hc_size = -1, hc_id = -1;
for (const int &v : Tree<X>::t_adj[u]) {
if (v ^ p) {
dfs_hc(v, u);
if (Tree<X>::t_size[v] > hc_size) {
hc_size = Tree<X>::t_size[v];
hc_id = v;
}
}
}
h_heavychlid[u] = hc_id;
}
/**
* @brief Utility function to assign highest parent that can be reached though
* heavy chains
* @param u current dfs node
* @param p the parent of node u
* @returns void
*/
void dfs_par(int u, int p = -1) {
if (h_heavychlid[u] != -1) {
h_parent[h_heavychlid[u]] = h_parent[u];
dfs_par(h_heavychlid[u], u);
}
for (const int &v : Tree<X>::t_adj[u]) {
if (v ^ p and v ^ h_heavychlid[u]) {
dfs_par(v, u);
}
}
}
/**
* @brief Utility function to lable the nodes so that heavy chains have a
* contigous lable
* @param u current dfs node
* @param p the parent of node u
* @returns void
*/
void dfs_labels(int u, int p = -1) {
h_label[u] = label++;
if (h_heavychlid[u] != -1) {
dfs_labels(h_heavychlid[u], u);
}
for (const int &v : Tree<X>::t_adj[u]) {
if (v ^ p and v ^ h_heavychlid[u]) {
dfs_labels(v, u);
}
}
}
/**
* @brief Utility function to break down a path query into two chain queries
* @param a node where the path starts
* @param b node where the path ends
* a and b must belong to a single root to leaf chain
* @returns the sum of ndoe values in the simple path from a to b
*/
X chain_query(int a, int b) {
X ret = SG<X>::sret_init;
if (Tree<X>::t_depth[a] < Tree<X>::t_depth[b]) {
std::swap(a, b);
}
while (Tree<X>::t_depth[a] >= Tree<X>::t_depth[b]) {
int l = h_label[h_parent[a]];
int r = h_label[a];
if (Tree<X>::t_depth[h_parent[a]] < Tree<X>::t_depth[b]) {
l += Tree<X>::t_depth[b] - Tree<X>::t_depth[h_parent[a]];
}
ret = SG<X>::combine(ret, SG<X>::query(l, r));
a = Tree<X>::t_par[h_parent[a]][0];
if (a == -1) {
break;
}
}
return ret;
}
public:
/**
* @brief Class parameterized constructor. Resizes the and initilizes the data
* members.
* @param nodes the total number of nodes in the tree
*/
explicit HLD<X>(int nodes) : Tree<X>(nodes), SG<X>(nodes) {
/* Initialization and resize vectors */
label = 0;
h_label.assign(Tree<X>::t_nodes, -1);
h_heavychlid.assign(Tree<X>::t_nodes, -1);
h_parent.resize(Tree<X>::t_nodes);
iota(h_parent.begin(), h_parent.end(), 0);
}
/**
* @brief This function must be called after the tree adjacency list and node
* values are populated The function initializes the required parametes, and
* populates the segment tree
* @returns void
*/
void init() {
Tree<X>::init();
// Fill the heavy child, greatest parent, and labels
label = 0;
dfs_hc(Tree<X>::t_root);
dfs_par(Tree<X>::t_root);
dfs_labels(Tree<X>::t_root);
// Segment Tree Initialization
for (int i = 0; i < Tree<X>::t_nodes; i++) {
SG<X>::s_tree[h_label[i] + Tree<X>::t_nodes] = Tree<X>::t_val[i];
}
for (int i = Tree<X>::t_nodes - 1; i > 0; i--) {
SG<X>::s_tree[i] =
SG<X>::combine(SG<X>::s_tree[i << 1], SG<X>::s_tree[i << 1 | 1]);
}
}
/**
* @brief This function updates the value at node with val
* @param node the node where the update is done
* @param val the value that is being updated
* @returns void
*/
void update(int node, X val) {
X diff = val - Tree<X>::t_val[node];
SG<X>::update(h_label[node], diff);
Tree<X>::t_val[node] = val;
}
/**
* @brief This function returns the sum of node values in the simple path from
* from node_1 to node_2
* @param a the node where the simple path starts
* @param b the node where the simple path ends
* (parameters are interchangeable, i.e., the function is commutative)
* @returns the sum of node values in the simple path from a to b
*/
X query(int a, int b) {
int lc = Tree<X>::lca(a, b);
X ret = SG<X>::sret_init;
assert(lc != -1);
ret += chain_query(a, lc);
ret += chain_query(b, lc);
return ret - Tree<X>::t_val[lc];
}
};
} // namespace heavy_light_decomposition
} // namespace range_queries
/**
* Test implementations
* @returns none
*/
static void test_1() {
std::cout << "Test 1:\n";
// Test details
int n = 5;
std::vector<int64_t> node_values = {4, 2, 5, 2, 1};
std::vector<std::vector<int>> edges = {{1, 2}, {1, 3}, {3, 4}, {3, 5}};
std::vector<std::vector<int>> queries = {
{2, 1, 4},
{1, 3, 2},
{2, 1, 4},
};
std::vector<int> expected_result = {11, 8};
std::vector<int> code_result;
range_queries::heavy_light_decomposition::HLD<int64_t> hld(n);
hld.set_node_val(node_values);
for (int i = 0; i < n - 1; i++) {
int u = edges[i][0], v = edges[i][1];
hld.add_edge(u - 1, v - 1);
}
hld.init();
for (const auto &q : queries) {
int type = q[0];
if (type == 1) {
int p = q[1], x = q[2];
hld.update(p - 1, x);
} else if (type == 2) {
int a = q[1], b = q[2];
code_result.push_back(hld.query(a - 1, b - 1));
} else {
continue;
}
}
for (int i = 0; i < static_cast<int>(expected_result.size()); i++) {
assert(expected_result[i] == code_result[i]);
}
std::cout << "\nTest 1 passed!\n";
}
/**
* Second test implementations
* @returns void
*/
static void test_2() {
std::cout << "Test 2:\n";
// Test details (Bamboo)
int n = 10;
std::vector<int64_t> node_values = {1, 8, 6, 8, 6, 2, 9, 2, 3, 2};
std::vector<std::vector<int>> edges = {
{10, 5}, {6, 2}, {10, 7}, {5, 2}, {3, 9}, {8, 3}, {1, 4}, {6, 4}, {8, 7}};
std::vector<std::vector<int>> queries = {
{2, 1, 10}, {2, 1, 6}, {1, 3, 4}, {2, 1, 9}, {1, 5, 3},
{1, 7, 8}, {2, 1, 4}, {2, 1, 8}, {1, 1, 4}, {1, 2, 7}};
std::vector<int> expected_result = {27, 11, 45, 9, 34};
std::vector<int> code_result;
range_queries::heavy_light_decomposition::HLD<int64_t> hld(n);
hld.set_node_val(node_values);
for (int i = 0; i < n - 1; i++) {
int u = edges[i][0], v = edges[i][1];
hld.add_edge(u - 1, v - 1);
}
hld.init();
for (const auto &q : queries) {
int type = q[0];
if (type == 1) {
int p = q[1], x = q[2];
hld.update(p - 1, x);
} else if (type == 2) {
int a = q[1], b = q[2];
code_result.push_back(hld.query(a - 1, b - 1));
} else {
continue;
}
}
for (int i = 0; i < static_cast<int>(expected_result.size()); i++) {
assert(expected_result[i] == code_result[i]);
}
std::cout << "\nTest2 passed!\n";
}
/**
* Third test implementations
* @returns void
*/
static void test_3() {
std::cout << "Test 3:\n";
// Test details
int n = 8;
std::vector<int64_t> node_values = {1, 8, 6, 8, 6, 2, 9, 2};
std::vector<std::vector<int>> edges = {{1, 2}, {2, 3}, {3, 4}, {1, 5},
{6, 3}, {7, 5}, {8, 7}};
std::vector<std::vector<int>> queries = {
{2, 6, 8}, {2, 3, 6}, {1, 3, 4}, {2, 7, 1}, {1, 5, 3},
{1, 7, 8}, {2, 6, 4}, {2, 7, 8}, {1, 1, 4}, {1, 2, 7}};
std::vector<int> expected_result = {34, 8, 16, 14, 10};
std::vector<int> code_result;
range_queries::heavy_light_decomposition::HLD<int64_t> hld(n);
hld.set_node_val(node_values);
for (int i = 0; i < n - 1; i++) {
int u = edges[i][0], v = edges[i][1];
hld.add_edge(u - 1, v - 1);
}
hld.init();
for (const auto &q : queries) {
int type = q[0];
if (type == 1) {
int p = q[1], x = q[2];
hld.update(p - 1, x);
} else if (type == 2) {
int a = q[1], b = q[2];
code_result.push_back(hld.query(a - 1, b - 1));
} else {
continue;
}
}
for (int i = 0; i < static_cast<int>(expected_result.size()); i++) {
assert(expected_result[i] == code_result[i]);
}
std::cout << "\nTest3 passed!\n";
}
/**
* Main function
*/
int main() {
test_1();
test_2();
test_3();
return 0;
}

101
range_queries/sparse_table.cpp Executable file
View File

@@ -0,0 +1,101 @@
/**
* @file sparse_table.cpp
* @brief Implementation of [Sparse Table](https://en.wikipedia.org/wiki/Range_minimum_query) data structure
*
* @details
* Sparse Table is a data structure, that allows answering range queries.
* It can answer most range queries in O(logn), but its true power is answering range minimum queries
* or equivalent range maximum queries). For those queries it can compute the answer in O(1) time.
*
* * Running Time Complexity \n
* * Build : O(NlogN) \n
* * Range Query : O(1) \n
*/
#include <vector>
#include <cassert>
#include <iostream>
#include <algorithm>
/**
* @namespace range_queries
* @brief Range Queries algorithms
*/
namespace range_queries {
/**
* @namespace sparse_table
* @brief Range queries using sparse-tables
*/
namespace sparse_table {
/**
* This function precomputes intial log table for further use.
* @param n value of the size of the input array
* @return corresponding vector of the log table
*/
template<typename T>
std::vector<T> computeLogs(const std::vector<T>& A) {
int n = A.size();
std::vector<T> logs(n);
logs[1] = 0;
for (int i = 2 ; i < n ; i++) {
logs[i] = logs[i/2] + 1;
}
return logs;
}
/**
* This functions builds the primary data structure sparse table
* @param n value of the size of the input array
* @param A array of the input integers
* @param logs array of the log table
* @return created sparse table data structure
*/
template<typename T>
std::vector<std::vector<T> > buildTable(const std::vector<T>& A, const std::vector<T>& logs) {
int n = A.size();
std::vector<std::vector<T> > table(20, std::vector<T>(n+5, 0));
int curLen = 0;
for (int i = 0 ; i <= logs[n] ; i++) {
curLen = 1 << i;
for (int j = 0 ; j + curLen < n ; j++) {
if (curLen == 1) {
table[i][j] = A[j];
}
else {
table[i][j] = std::min(table[i-1][j], table[i-1][j + curLen/2]);
}
}
}
return table;
}
/**
* This function is the query function to get the range minimum value
* @param beg beginning index of the query range
* @param end ending index of the query range
* @param logs array of the log table
* @param table sparse table data structure for the input array
* @return minimum value for the [beg, end] range for the input array
*/
template<typename T>
int getMinimum(int beg, int end, const std::vector<T>& logs, const std::vector<std::vector<T> >& table) {
int p = logs[end - beg + 1];
int pLen = 1 << p;
return std::min(table[p][beg], table[p][end - pLen + 1]);
}
}
} // namespace range_queries
/**
* Main function
*/
int main() {
std::vector<int> A{1, 2, 0, 3, 9};
std::vector<int> logs = range_queries::sparse_table::computeLogs(A);
std::vector<std::vector<int> > table = range_queries::sparse_table::buildTable(A, logs);
assert(range_queries::sparse_table::getMinimum(0, 0, logs, table) == 1);
assert(range_queries::sparse_table::getMinimum(0, 4, logs, table) == 0);
assert(range_queries::sparse_table::getMinimum(2, 4, logs, table) == 0);
return 0;
}

View File

@@ -1,78 +1,156 @@
/**
* \file
* \brief [Median search](https://en.wikipedia.org/wiki/Median_search) algorithm
* \warning This core is erroneous and gives invorrect answers. Tested using
* cases from [here](https://brilliant.org/wiki/median-finding-algorithm/)
* \ingroup median search
* \{
* @file median_search.cpp
* @brief Implementation of [Median search](https://en.wikipedia.org/wiki/Median_of_medians) algorithm.
* @cases from [here](https://brilliant.org/wiki/median-finding-algorithm/)
*
* @details
* Given an array A[1,...,n] of n numbers and an index i, where 1 ≤ i ≤ n, find the i-th smallest element of A.
* median_of_medians(A, i):
* #divide A into sublists of len 5
* sublists = [A[j:j+5] for j in range(0, len(A), 5)]
* medians = [sorted(sublist)[len(sublist)/2] for sublist in sublists]
* if len(medians) <= 5:
* pivot = sorted(medians)[len(medians)/2]
* else:
* #the pivot is the median of the medians
* pivot = median_of_medians(medians, len(medians)/2)
* #partitioning step
* low = [j for j in A if j < pivot]
* high = [j for j in A if j > pivot]
* k = len(low)
* if i < k:
* return median_of_medians(low,i)
* elif i > k:
* return median_of_medians(high,i-k-1)
* else: #pivot = k
* return pivot
*
* \note this algorithm implements median search for only arrays which have distinct elements
*
* Here are some example lists you can use to see how the algorithm works
* A = [1,2,3,4,5,1000,8,9,99] (Contain Unique Elements)
* B = [1,2,3,4,5,6] (Contains Unique Elements)
* print median_of_medians(A, 0) #should be 1
* print median_of_medians(A,7) #should be 99
* print median_of_medians(B,4) #should be 5
*
* @author Unknown author
* @author [Sushil Kumar](https://github.com/Rp-sushil)
*/
#include <algorithm>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cassert>
/**
* @todo add documentation
* @namespace search
* @brief Search algorithms
*/
template <class X>
void comp(X x, std::vector<int> *s1, std::vector<int> *s2,
std::vector<int> *s3) {
if (s1->size() >= x && s1->size() + s2->size() < x) {
std::cout << (*s2)[0] << " is the " << x + 1 << "th element from front";
} else if (s1->size() > x) {
std::sort(s1->begin(), s1->end());
std::cout << (*s1)[x] << " is the " << x + 1 << "th element from front";
} else if (s1->size() + s2->size() <= x && s3->size() > x) {
std::sort(s3->begin(), s3->end());
std::cout << (*s3)[x - s1->size() - s2->size()] << " is the " << x + 1
<< "th element from front";
} else {
std::cout << x + 1 << " is invalid location";
}
namespace search {
/**
* @namespace median_search
* @brief Functions for [Median search](https://en.wikipedia.org/wiki/Median_search) algorithm
*/
namespace median_search {
/**
* This function search the element in an array for the given index.
* @param A array where numbers are saved
* @param idx current index in array
* @returns corresponding element which we want to search.
*/
int median_of_medians(const std::vector<int>& A, const int& idx) {
int pivot = 0; // initialized with zero
std::vector<int> a(A.begin(), A.end());
std::vector<int> m;
int r = a.size();
for(int i = 0; i < r; i += 5){
std::sort(a.begin() + i, a.begin() + std::min(r, i + 5));
int mid = (i + std::min(r, i + 5)) / 2;
m.push_back(a[mid]);
}
int sz = int(m.size());
if(sz <= 5){
std::sort(m.begin(), m.end());
pivot = m[(sz- 1) / 2];
}
else{
pivot = median_of_medians(m, idx);
}
std::vector<int> low;
std::vector<int> high;
for(int i = 0; i < r; i++){
if(a[i] < pivot){
low.push_back(a[i]);
}
else if(a[i] > pivot){
high.push_back(a[i]);
}
}
int k = int(low.size());
if(idx < k){
return median_of_medians(low, idx);
}
else if(idx > k){
return median_of_medians(high, idx-k-1);
}
else{
return pivot;
}
}
} // namespace median_search
} // namespace search
#define MAX_NUM 20 ///< maximum number of values to sort from
/**
* Function to test above algorithm
*/
void test(){
std::vector<int> A{25,21,98,100,76,22,43,60,89,87};
int i = 3;
assert(A[6] == search::median_search::median_of_medians(A, i)); // A[6] = 43, is the fourth smallest element.
std::cout << "test case:1 passed\n";
std::vector<int> B{1,2,3,4,5,6};
int j = 4;
assert(B[4] == search::median_search::median_of_medians(B, j)); // B[4] = 5, is the fifth smallest element.
std::cout << "test case:2 passed\n";
std::vector<int> C{1,2,3,4,5,1000,8,9,99};
int k = 3;
assert(C[3] == search::median_search::median_of_medians(C, k)); // C[3] = 4, is the fourth smallest element.
std::cout << "test case:3 passed\n";
std::cout << "--All tests passed--\n";
}
/**
* Main function
*/
int main() {
std::vector<int> v{25, 21, 98, 100, 76, 22, 43, 60, 89, 87};
std::vector<int> s1;
std::vector<int> s2;
std::vector<int> s3;
// creates an array of random numbers
// for (int i = 0; i < MAX_NUM; i++) {
// int r = std::rand() % 1000;
// v.push_back(r);
// std::cout << r << " ";
// }
for (int r : v) std::cout << r << " ";
int median = std::rand() % 1000; // initialize to a random numnber
std::cout << "\nmedian=" << median << std::endl;
int avg1, avg2, avg3, sum1 = 0, sum2 = 0, sum3 = 0;
for (int i = 0; i < v.size(); i++) { // iterate through all numbers
if (v.back() == v[median]) {
avg1 = sum1 + v.back();
s2.push_back(v.back());
} else if (v.back() < v[median]) {
avg2 = sum2 + v.back();
s1.push_back(v.back());
} else {
avg3 = sum3 + v.back();
s3.push_back(v.back());
}
v.pop_back();
}
int x;
std::cout << "enter the no. to be searched form begining:- ";
std::cin >> x;
comp(x - 1, &s1, &s2, &s3);
return 0;
int main()
{
test();
int n = 0;
std::cout << "Enter Size of Array: ";
std::cin >> n;
std::vector<int> a(n);
std::cout << "Enter Array: ";
for(int i = 0; i < n; i++){
std::cin >> a[i];
}
std::cout << "Median: "; // Median defination: https://en.wikipedia.org/wiki/Median
int x = search::median_search::median_of_medians(a, (n - 1) / 2);
if(n % 2 == 0){
int y = search::median_search::median_of_medians(a, n / 2);
std::cout << (float(x) + float(y))/2.0;
}
else{
std::cout << x;
}
std::cout << "\nTo find i-th smallest element ";
std::cout << "\nEnter i: ";
int idx = 0;
std::cin >> idx;
idx--;
std::cout << idx + 1<< "-th smallest element: " << search::median_search::median_of_medians(a, idx) << '\n';
return 0;
}
/// }

View File

@@ -0,0 +1,163 @@
/**
* @file
* @author [@sinkyoungdeok](https://github.com/sinkyoungdeok)
* @author [Krishna Vedala](https://github.com/kvedala)
* @brief Algorithm that combines insertion sort and merge sort. [Wiki
* link](https://en.wikipedia.org/wiki/Merge-insertion_sort)
*
* @see Individual algorithms: insertion_sort.cpp and merge_sort.cpp
*/
#include <algorithm>
#include <array>
#include <cassert>
#include <ctime>
#include <iostream>
#include <memory>
/** \namespace sorting
* \brief Sorting algorithms
*/
namespace sorting {
/** \namespace merge_insertion
* \brief Combined Intersion-Merge sorting algorithm
*/
namespace merge_insertion {
/**
* @brief Insertion merge algorithm
* @see insertion_sort.cpp
*
* @tparam T array data type
* @tparam N length of array
* @param A pointer to array to sort
* @param start start index of sorting window
* @param end end index of sorting window
*/
template <typename T, size_t N>
static void InsertionSort(std::array<T, N> *A, size_t start, size_t end) {
size_t i = 0, j = 0;
T *ptr = A->data();
for (i = start; i < end; i++) {
T temp = ptr[i];
j = i;
while (j > start && temp < ptr[j - 1]) {
ptr[j] = ptr[j - 1];
j--;
}
// for (j = i; j > start && temp < ptr[j - 1]; --j) {
// ptr[j] = ptr[j - 1];
// }
ptr[j] = temp;
}
}
/**
* @brief Perform merge of data in a window
*
* @tparam T array data type
* @tparam N length of array
* @param A pointer to array to sort
* @param min start index of window
* @param max end index of window
* @param mid mid-point of window
*/
template <typename T, size_t N>
static void merge(std::array<T, N> *array, size_t min, size_t max, size_t mid) {
size_t firstIndex = min;
size_t secondIndex = mid + 1;
auto ptr = array->data();
std::array<T, N + 1> tempArray{0};
// While there are elements in the left or right runs
for (size_t index = min; index <= max; index++) {
// If left run head exists and is <= existing right run head.
if (firstIndex <= mid &&
(secondIndex > max || ptr[firstIndex] <= ptr[secondIndex])) {
tempArray[index] = ptr[firstIndex];
firstIndex++;
} else {
tempArray[index] = ptr[secondIndex];
secondIndex++;
}
}
// transfer to the initial array
memcpy(ptr + min, tempArray.data() + min, (max - min) * sizeof(T));
// for (int index = min; index <= max; index++) ptr[index] =
// tempArray[index];
}
/**
* @brief Final combined algorithm.
* Algorithm utilizes ::sorting::merge_insertion::InsertionSort if window length
* is less than threshold, else performs merge sort recursively using
* ::sorting::merge_insertion::mergeSort
*
* @tparam T array data type
* @tparam N length of array
* @param A pointer to array to sort
* @param min start index of sort window
* @param max end index of sort window
* @param threshold window length threshold
*/
template <typename T, size_t N>
void mergeSort(std::array<T, N> *array, size_t min, size_t max,
size_t threshold) {
// prerequisite
if ((max - min) <= threshold) {
InsertionSort(array, min, max);
} else {
// get the middle point
size_t mid = (max + min) >> 1;
// apply merge sort to both parts of this
mergeSort(array, min, mid, threshold);
mergeSort(array, mid, max, threshold);
// and finally merge all that sorted stuff
merge(array, min, max, mid);
}
}
} // namespace merge_insertion
} // namespace sorting
/**
* @brief Function to test code using random arrays
* @returns none
*/
static void test() {
constexpr size_t size = 30;
std::array<int, size> array{0};
// input
for (int i = 0; i < size; i++) {
array[i] = std::rand() % 100 - 50;
std::cout << array[i] << " ";
}
std::cout << std::endl;
sorting::merge_insertion::InsertionSort(&array, 0, size);
// sorting::merge_insertion::mergeSort(&array, 0, size, 10);
// output
for (int i = 0; i < size; ++i) {
std::cout << array[i] << " ";
}
std::cout << std::endl;
assert(std::is_sorted(std::begin(array), std::end(array)));
std::cout << "Test passed\n";
}
/**
* @brief Main function
* @return 0 on exit
*/
int main() {
std::srand(std::time(nullptr));
test();
return 0;
}

View File

@@ -43,8 +43,8 @@ void merge(int *arr, int l, int m, int r) {
i = 0;
j = 0;
k = l;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
while (i < n1 || j < n2) {
if (j >= n2 || (i < n1 && L[i] <= R[j])) {
arr[k] = L[i];
i++;
} else {
@@ -54,18 +54,6 @@ void merge(int *arr, int l, int m, int r) {
k++;
}
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
delete[] L;
delete[] R;
}
@@ -110,7 +98,7 @@ int main() {
}
mergeSort(arr, 0, size - 1);
std::cout << "Sorted array : ";
show(arr, size - 1);
show(arr, size);
delete[] arr;
return 0;
}

129
sorting/pancake_sort.cpp Normal file
View File

@@ -0,0 +1,129 @@
/**
* @file
* @brief pancake sort sorts a disordered stack of pancakes by flipping any number of pancakes using a spatula using minimum number of flips.
*
* @details
* Unlike a traditional sorting algorithm, which attempts to sort with the fewest comparisons possible,
* the goal is to sort the sequence in as few reversals as possible.
* Overall time complexity of pancake sort is O(n^2)
* For example: example 1:-
* Disordered pancake sizes: {2,5,3,7,8}
* Sorted: {2,3,5,7,8}
* For example: example 2:-
* Disordered pancake sizes: {22,51,37,73,81}
* Sorted: {22,37,51,73,81}
* @author [Divyansh Gupta](https://github.com/divyansh12323)
* @see more on [Pancake sort](https://en.wikipedia.org/wiki/Pancake_sorting)
* @see related problem at [Leetcode](https://leetcode.com/problems/pancake-sorting/)
*/
#include <iostream> // for io operations
#include <vector> // for std::vector
#include <algorithm> // for std::is_sorted
#include <cassert> // for std::assert
/**
* @namespace sorting
* @brief Sorting algorithms
*/
namespace sorting {
/**
* @namespace pancake_sort
* @brief Functions for [Pancake sort](https://en.wikipedia.org/wiki/Pancake_sorting) algorithm
*/
namespace pancake_sort {
/**
* @brief This implementation is for reversing elements in a a C-style array .
* @param [start,end] arr our vector of elements.
* @param start starting index of array
* @param end ending index of array
* @returns void
*/
template<typename T>
void reverse(std::vector<T> &arr, int start, int end) {
T temp; //Temporary variable
while (start <= end) {
temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
start++;
end--;
}
}
/**
* @brief This implementation is for a C-style array input that gets modified in place.
* @param [start,end] arr our vector of elements.
* @param size size of given array
* @returns 0 on exit
*/
template<typename T>
int pancakeSort(std::vector<T> &arr, int size) {
for (int i = size; i > 1; --i) {
int max_index = 0, j; //intialize some variables.
T max_value = 0;
for (j = 0; j < i; j++) {
if (arr[j] >= max_value) {
max_value = arr[j];
max_index = j;
}
}
if (max_index != i - 1) //check for reversing
{
reverse(arr, 0, max_index);
reverse(arr, 0, i - 1);
}
}
return 0;
}
} // namespace pancake_sort
} // namespace sorting
/**
* @brief Test implementations
* @returns void
*/
static void test() {
// example 1: vector of int
const int size1 = 7;
std::cout << "\nTest 1- as std::vector<int>...";
std::vector<int> arr1 = {23, 10, 20, 11, 12, 6, 7};
sorting::pancake_sort::pancakeSort(arr1, size1);
assert(std::is_sorted(arr1.begin(), arr1.end()));
std::cout << "Passed\n";
for (int i = 0; i < size1; i++) {
std::cout << arr1[i] << " ,";
}
std::cout << std::endl;
// example 2: vector of double
const int size2 = 8;
std::cout << "\nTest 2- as std::vector<double>...";
std::vector<double> arr2 = {23.56, 10.62, 200.78, 111.484, 3.9, 1.2, 61.77, 79.6};
sorting::pancake_sort::pancakeSort(arr2, size2);
assert(std::is_sorted(arr2.begin(), arr2.end()));
std::cout << "Passed\n";
for (int i = 0; i < size2; i++) {
std::cout << arr2[i] << ", ";
}
std::cout << std::endl;
// example 3:vector of float
const int size3 = 7;
std::cout << "\nTest 3- as std::vector<float>...";
std::vector<float> arr3 = {6.56, 12.62, 200.78, 768.484, 19.27, 68.87, 9.6};
sorting::pancake_sort::pancakeSort(arr3, size3);
assert(std::is_sorted(arr3.begin(), arr3.end()));
std::cout << "Passed\n";
for (int i = 0; i < size3; i++) {
std::cout << arr3[i] << ", ";
}
std::cout << std::endl;
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
test();
return 0;
}

133
sorting/pigeonhole_sort.cpp Normal file
View File

@@ -0,0 +1,133 @@
/**
* @file
* @brief Implementation of [Pigeonhole Sort algorithm]
* (https://en.wikipedia.org/wiki/Pigeonhole_sort)
* @author [Lownish](https://github.com/Lownish)
* @details
* Pigeonhole sorting is a sorting algorithm that is suitable for sorting lists
* of elements where the number of elements and the number of possible key
* values are approximately the same. It requires O(n + Range) time where n is
* number of elements in input array and Range is number of possible values in
* array.
*
* The time Complexity of the algorithm is \f$O(n+N)\f$.
*/
#include <algorithm> //for std::is_sorted
#include <array> //for std::array
#include <cassert> //for assert
#include <iostream> //for io operations
/**
* @namespace sorting
* @brief Sorting algorithms
*/
namespace sorting {
/**
* Pigeonhole sorting of array of size n
* The function will sort the array through Pigeonhole algorithm and print
* @param arr unsorted array of elements
* @returns sorted array of elements
*/
template <std::size_t N>
std::array<int, N> pigeonSort(std::array<int, N> arr) {
// Finding min and max*
auto min = std::min_element(std::begin(arr), std::end(arr));
auto max = std::max_element(std::begin(arr), std::end(arr));
// Range refers to the number of holes required
int range = *max - *min + 1;
int *hole = new int[range]();
// Copying all array values to pigeonhole
for (int i = 0; i < N; i++) {
hole[arr[i] - *min] = arr[i];
}
// Deleting elements from list and storing to original array
int count = 0;
for (int i = 0; i < range; i++) {
while (hole[i] != '\0') {
arr[count] = hole[i];
hole[i] = {};
count++;
}
}
delete[] hole;
return arr;
}
} // namespace sorting
/**
* Test function 1 with unsorted array
* {8, 3, 2, 7, 4, 6, 8}
* @returns none
*/
static void test_1() {
const int n = 7;
std::array<int, n> test_array = {8, 3, 2, 7, 4, 6, 8};
test_array = sorting::pigeonSort<n>(test_array);
assert(std::is_sorted(std::begin(test_array), std::end(test_array)));
// Printing sorted array
for (int i = 0; i < n; i++) {
std::cout << test_array.at(i) << " ";
}
std::cout << "\nPassed\n";
}
/**
* Test function 2 with unsorted array
* {802, 630, 20, 745, 52, 300, 612, 932, 78, 187}
* @returns none
*/
static void test_2() {
const int n = 10;
std::array<int, n> test_array = {802, 630, 20, 745, 52,
300, 612, 932, 78, 187};
test_array = sorting::pigeonSort<n>(test_array);
assert(std::is_sorted(std::begin(test_array), std::end(test_array)));
// Printing sorted array
for (int i = 0; i < n; i++) {
std::cout << test_array.at(i) << " ";
}
std::cout << "\nPassed\n";
}
/**
* Test function 1 with unsorted array
* {11,13,12,14}
* @returns none
*/
static void test_3() {
const int n = 4;
std::array<int, n> test_array = {11, 13, 12, 14};
test_array = sorting::pigeonSort<n>(test_array);
assert(std::is_sorted(std::begin(test_array), std::end(test_array)));
// Printing sorted array
for (int i = 0; i < n; i++) {
std::cout << test_array.at(i) << " ";
}
std::cout << "\nPassed\n";
}
/**
* Main function
*/
int main() {
test_1();
test_2();
test_3();
return 0;
}

87
sorting/strand_sort.cpp Normal file
View File

@@ -0,0 +1,87 @@
/**
* @file strand_sort.cpp
* @brief Implementation of [Strand Sort](https://en.wikipedia.org/wiki/Strand_sort) algorithm.
*
* @details
* Strand Sort is a sorting algorithm that works in \f$O(n)\f$ time if list is already sorted and works in \f$O(n^2)\f$ in worst case.
*
* It is passed over the array to be sorted once and the ascending (sequential) numbers are taken.
* After the first iteration, the sequential sub-array is put on the empty sorted array.
* The main sequence is passed over again and a new sub-sequence is created in order.
* Now that the sorted array is not empty, the newly extracted substring is merged with the sorted array.
* Repeat types 3 and 4 until the sub-sequence and main sequence are empty.
*
* @author [Mertcan Davulcu](https://github.com/mertcandav)
*/
#include <iostream>
#include <list>
/**
* @namespace sorting
* @brief Sorting algorithms
*/
namespace sorting {
/**
* @namespace strand
* @brief Functions for [Strand Sort](https://en.wikipedia.org/wiki/Strand_sort) algorithm
*/
namespace strand {
/**
* @brief Apply sorting
* @tparam element type of list
* @param lst List to be sorted
* @returns Sorted list<T> instance
*/
template <typename T>
std::list<T> strand_sort(std::list<T> lst) {
if (lst.size() < 2) { // Returns list if empty or contains only one element
return lst; // Returns list
}
std::list<T> result; // Define new "result" named list instance.
std::list<T> sorted; // Define new "sorted" named list instance.
while(!lst.empty()) /* if lst is not empty */ {
sorted.push_back(lst.front()); // Adds the first element of "lst" list to the bottom of the "sorted" list.
lst.pop_front(); // Remove first element of "lst" list.
for (auto it = lst.begin(); it != lst.end(); ) { // Return the loop as long as the current iterator is not equal to the last literator of the "lst" list.
if (sorted.back() <= *it) { // If the last reference of the "sorted" list is less than or equal to the current iterator reference.
sorted.push_back(*it); // Adds the iterator retrieved in the loop under the "sorted" list.
it = lst.erase(it); // Deletes the element with the current iterator and assigns the deleted element to the iterator.
} else {
it++; // Next iterator.
}
}
result.merge(sorted); // Merge "result" list with "sorted" list.
}
return result; // Returns sorted list
}
} // namespace strand
} // namespace sorting
/**
* @brief Function for testing
* @return N/A
*/
static void test() {
std::list<int> lst = { -333, 525, 1, 0, 94, 52, 33 };
std::cout << "Before: ";
for(auto item: lst) {
std::cout << item << " ";
}
lst = sorting::strand::strand_sort(lst); // Sort list.
std::cout << "\nAfter: ";
for(auto item: lst) {
std::cout << item << " ";
}
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
test();
return 0;
}

122
strings/horspool.cpp Normal file
View File

@@ -0,0 +1,122 @@
/**
* @file
* @brief Horspool's algorithm that finds if a string contains a substring (https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm)
* @author [Harry Kontakis](https://github.com/ckontakis)
*/
#include <iostream>
#include <unordered_map>
#include <cassert>
/**
* @namespace strings
* @brief Algorithms with strings
*/
namespace strings {
/**
* @namespace horspool
* @brief Functions for [Horspool's](https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm) algorithm
*/
namespace horspool {
/**
* A function that finds the shift table of the given prototype string that we need in Horpool's algorithm.
* @param prototype is the substring that we use to find shift table
* @return Shift Table of Horspool's algorithm
*/
std::unordered_map<char, int> findShiftTable(const std::string &prototype) {
std::unordered_map<char, int>
shiftTable; // A HashMap for shift table that has characters for keys and integers for values
for (int i = 0; i < prototype.size();
i++) { // Checking all characters of prototype string
if (shiftTable.find(prototype[i]) ==
shiftTable.end()) { // If character does not exist in HashMap
if (i != prototype.size() - 1) {
shiftTable.insert(std::make_pair(
prototype[i], prototype.size() - i -
1)); // Insert the character as key and the size of prototype string - index of character - 1 as value
} else {
shiftTable.insert(std::make_pair(
prototype[i],
prototype.size())); // Insert the character as key and the size of prototype string as value
}
} else {
if (i != prototype.size() - 1) {
shiftTable[prototype[i]] = prototype.size() - i - 1;
}
}
}
return shiftTable;
}
/**
* A function that implements Horspool's algorithm.
* @param text is the string that we are searching if there is a substring
* @param prototype is the substring that we are searching in text
* @returns true if text string contains prototype string
* @returns false if text string does not contain prototype string
*/
bool horspool(const std::string &text, const std::string &prototype) {
std::unordered_map<char, int> shiftTable = findShiftTable(
prototype); // Initialise shift table calling findShiftTable function
int i = static_cast<int>(
prototype.size() -
1); // Index that we shift in text to find the substring
while (i < text.size()) {
int j = i, k = 0;
bool flag = true;
for (int z = static_cast<int>(prototype.size() - 1); z >= 0 && flag;
z--) { // Checking if all characters of substring are equal with all characters of string
if (text[j] == prototype[z]) {
k++;
j--;
} else {
flag = false; // If two characters are not equal set flag to false and break from loop
}
}
if (k ==
prototype.size()) { // If all characters match then return true
return true;
} else {
if (shiftTable.find(text[i]) != shiftTable.end()) {
i += shiftTable[text[i]]; // If shift table contains the character then shift index as many steps as value
} else {
i += prototype.size(); // If character does not exist in shift table then shift index as many steps as size of prototype string
}
}
}
return false;
}
} // namespace horspool
} // namespace strings
/**
* @brief Function with test cases for Horspool's algorithm
* @returns void
*/
static void test(){
assert(strings::horspool::horspool("Hello World","World") == true);
assert(strings::horspool::horspool("Hello World"," World") == true);
assert(strings::horspool::horspool("Hello World","ello") == true);
assert(strings::horspool::horspool("Hello World","rld") == true);
assert(strings::horspool::horspool("Hello","Helo") == false);
assert(strings::horspool::horspool("c++_algorithms","c++_algorithms") == true);
assert(strings::horspool::horspool("c++_algorithms","c++_") == true);
assert(strings::horspool::horspool("Hello","Hello World") == false);
assert(strings::horspool::horspool("c++_algorithms","") == false);
assert(strings::horspool::horspool("c++","c") == true);
assert(strings::horspool::horspool("3458934793","4793") == true);
assert(strings::horspool::horspool("3458934793","123") == false);
}
/**
* @brief Main Function that calls test function
* @returns 0 on exit
*/
int main(){
test();
return 0;
}

View File

@@ -14,7 +14,7 @@
#include <iostream>
#ifdef _MSC_VER
#include <string> // use this for MS Visucal C++
#include <string> // use this for MS Visual C++
#else
#include <cstring>
#endif