diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..2711cbb0b --- /dev/null +++ b/.clang-format @@ -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: '^' + 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 +... + diff --git a/.clang-tidy b/.clang-tidy index 113d688e7..99d867ab4 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -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 }' diff --git a/.github/workflows/awesome_workflow.yml b/.github/workflows/awesome_workflow.yml index 9cff1c48d..78ea62c7b 100644 --- a/.github/workflows/awesome_workflow.yml +++ b/.github/workflows/awesome_workflow.yml @@ -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: diff --git a/CMakeLists.txt b/CMakeLists.txt index 57104aa9c..f75d558f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..535cbef32 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -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 + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 35d875d43..dd70b7b3e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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 */ 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 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! diff --git a/DIRECTORY.md b/DIRECTORY.md index 5e4ecd8b1..1aaf9dade 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -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) diff --git a/REVIEWER_CODE.md b/REVIEWER_CODE.md new file mode 100644 index 000000000..933d43e68 --- /dev/null +++ b/REVIEWER_CODE.md @@ -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. diff --git a/backtracking/nqueen_print_all_solutions.cpp b/backtracking/nqueen_print_all_solutions.cpp index e6736da1e..5aa5c71a2 100644 --- a/backtracking/nqueen_print_all_solutions.cpp +++ b/backtracking/nqueen_print_all_solutions.cpp @@ -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 -#define n 4 +#include -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 +void PrintSol(const std::array, 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 +bool CanIMove(const std::array, 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 +void NQueenSol(std::array, 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, n> board{0}; + + backtracking::n_queens_all_solutions::NQueenSol(board, 0); } diff --git a/backtracking/rat_maze.cpp b/backtracking/rat_maze.cpp index fb3be4451..6dfda965c 100644 --- a/backtracking/rat_maze.cpp +++ b/backtracking/rat_maze.cpp @@ -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 #include -#define size 4 +#include -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 +bool solveMaze(int currposrow, int currposcol, + const std::array, size> &maze, + std::array, 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, size> maze = { + std::array{1, 0, 1, 0}, std::array{1, 0, 1, 1}, + std::array{1, 0, 0, 1}, std::array{1, 1, 1, 1}}; - int soln[size][size]; + std::array, 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(currposrow, currposcol, maze, + soln) == 1); +} + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + test(); // run the tests return 0; } diff --git a/backtracking/sudoku_solve.cpp b/backtracking/sudoku_solve.cpp index 57c6b5274..ccde36383 100644 --- a/backtracking/sudoku_solve.cpp +++ b/backtracking/sudoku_solve.cpp @@ -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 bool isPossible(const std::array , 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 , 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(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(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(mat, i, j + 1); - if (aageKiSolveHui) { + bool solution_found = backtracking::solveSudoku(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; } diff --git a/data_structures/linked_list.cpp b/data_structures/linked_list.cpp index 8eb6e586d..72003ba0c 100644 --- a/data_structures/linked_list.cpp +++ b/data_structures/linked_list.cpp @@ -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 +#include +#include -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 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& 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 first; ///< link before the actual first element + std::shared_ptr last; ///< last link on the list + public: + /** + * List constructor. Initializes the first and last link. + */ + list() { + // Initialize the first link + first = std::make_shared(); + // 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 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(new_elem); + last = first->succ(); } else { - std::cout << "\nEmpty list"; + last->succ() = std::make_shared(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(new_elem); + last = first->succ(); + } else { + std::shared_ptr t = std::make_shared(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 t = first; + std::shared_ptr 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 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 list::search(int find_elem) { + if (isEmpty()) { + std::cout << "List is Empty!"; + return nullptr; + } + std::shared_ptr 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 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; } diff --git a/data_structures/skip_list.cpp b/data_structures/skip_list.cpp index cc679a31c..9cf8c430f 100644 --- a/data_structures/skip_list.cpp +++ b/data_structures/skip_list.cpp @@ -20,10 +20,10 @@ #include #include -/** \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(new Node(-1, MAX_LEVEL)); + header = std::make_shared(-1, MAX_LEVEL); } /** @@ -75,8 +75,9 @@ class SkipList { int randomLevel() { int lvl = 0; while (static_cast(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 n = - std::shared_ptr(new Node(key, rlevel, value)); + std::make_shared(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; } diff --git a/data_structures/stack_using_array.cpp b/data_structures/stack_using_array.cpp index 0c0813d5e..8483d6ad7 100644 --- a/data_structures/stack_using_array.cpp +++ b/data_structures/stack_using_array.cpp @@ -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); diff --git a/data_structures/trie_tree.cpp b/data_structures/trie_tree.cpp index a613ebd8f..e966b2dfd 100644 --- a/data_structures/trie_tree.cpp +++ b/data_structures/trie_tree.cpp @@ -1,91 +1,209 @@ -#include -#include - +/** + * @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 +#include #include +#include #include +#include -// 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, 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& 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 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 temp(new trie()); + root->arr[j] = temp; + root = temp; + } + } else if (arr[j]) { + root = arr[j]; + } else { + std::shared_ptr 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; } diff --git a/dynamic_programming/cut_rod.cpp b/dynamic_programming/cut_rod.cpp index 136c78dbb..9513e487e 100644 --- a/dynamic_programming/cut_rod.cpp +++ b/dynamic_programming/cut_rod.cpp @@ -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 +#include +#include #include -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 +int maxProfitByCuttingRod(const std::array &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 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 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; } diff --git a/dynamic_programming/fibonacci_top_down.cpp b/dynamic_programming/fibonacci_top_down.cpp deleted file mode 100644 index 3c0c9a1a3..000000000 --- a/dynamic_programming/fibonacci_top_down.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include -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; -} \ No newline at end of file diff --git a/dynamic_programming/kadane2.cpp b/dynamic_programming/kadane2.cpp new file mode 100644 index 000000000..c4732e202 --- /dev/null +++ b/dynamic_programming/kadane2.cpp @@ -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 +#include +#include +/** + * @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 +int maxSubArray(const std::array &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 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); // calling maxSubArray function + std::cout << "Maximum subarray sum is " << max_sum; // Printing the answer + + return 0; +} diff --git a/dynamic_programming/word_break.cpp b/dynamic_programming/word_break.cpp new file mode 100644 index 000000000..5291f665f --- /dev/null +++ b/dynamic_programming/word_break.cpp @@ -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 +#include +#include +#include +#include +#include + +/** + * @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 &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 &strSet, + int pos, std::vector *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 &wordDict) { + // unordered set to store words in the dictionary for constant time + // search + std::unordered_set 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 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 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 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; +} diff --git a/graph/CMakeLists.txt b/graph/CMakeLists.txt new file mode 100644 index 000000000..7f6ea0e69 --- /dev/null +++ b/graph/CMakeLists.txt @@ -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} ) diff --git a/graph/breadth_first_search.cpp b/graph/breadth_first_search.cpp index 0f4f46160..28cad4930 100644 --- a/graph/breadth_first_search.cpp +++ b/graph/breadth_first_search.cpp @@ -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>; + /** * \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> *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> *graph, int u, int v) { * @param v second vertex * */ -void add_undirected_edge(std::vector> *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> *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 breadth_first_search(const std::vector> &graph, - int start) { +std::vector breadth_first_search(const adjacency_list &graph, int start) { /// vector to keep track of visited vertices std::vector 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 tracker; /// mark the starting vertex as visited @@ -116,11 +127,13 @@ std::vector breadth_first_search(const std::vector> &grap } return visited; } + } // namespace graph -void tests() { +/** Test function */ +static void tests() { /// Test 1 Begin - std::vector> graph(4, std::vector()); + graph::adjacency_list graph(4, std::vector()); 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> graph(vertices); + graph::adjacency_list graph(vertices); std::cout << "Enter space-separated pairs of vertices that form edges: " << std::endl; diff --git a/graph/bridge_finding_with_tarjan_algorithm.cpp b/graph/bridge_finding_with_tarjan_algorithm.cpp index a9f76c033..c4443dd5d 100644 --- a/graph/bridge_finding_with_tarjan_algorithm.cpp +++ b/graph/bridge_finding_with_tarjan_algorithm.cpp @@ -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> search_bridges( - int n, - const std::vector>& connections) { + int n, const std::vector>& 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> bridges = s1.search_bridges(number_of_node, node); + std::vector> 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'; diff --git a/graph/cycle_check_directed_graph.cpp b/graph/cycle_check_directed_graph.cpp index a9e4f2c8b..8a651cfc5 100644 --- a/graph/cycle_check_directed_graph.cpp +++ b/graph/cycle_check_directed_graph.cpp @@ -53,7 +53,7 @@ using AdjList = std::map>; */ 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; }; diff --git a/graph/depth_first_search.cpp b/graph/depth_first_search.cpp new file mode 100644 index 000000000..e99d44fc9 --- /dev/null +++ b/graph/depth_first_search.cpp @@ -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... + * + *

Working

+ * 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 +#include +#include + +/** + * + * \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> *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> &adj, size_t v, + std::vector *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> &adj, + size_t start) { + size_t vertices = adj.size(); + + std::vector 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> adj(vertices, std::vector()); + + /// 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; +} diff --git a/graph/depth_first_search_with_stack.cpp b/graph/depth_first_search_with_stack.cpp new file mode 100644 index 000000000..9810edeb1 --- /dev/null +++ b/graph/depth_first_search_with_stack.cpp @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +constexpr int WHITE = 0; +constexpr int GREY = 1; +constexpr int BLACK = 2; +constexpr int INF = 99999; + +void dfs(const std::vector > &graph, int start) { + std::vector checked(graph.size(), WHITE); + checked[start] = GREY; + std::stack 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 > 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; +} diff --git a/graph/dfs.cpp b/graph/dfs.cpp deleted file mode 100644 index 2d38c8725..000000000 --- a/graph/dfs.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include -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; -} \ No newline at end of file diff --git a/graph/dfs_with_stack.cpp b/graph/dfs_with_stack.cpp deleted file mode 100644 index 193f3f291..000000000 --- a/graph/dfs_with_stack.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include -#include - -#define WHITE 0 -#define GREY 1 -#define BLACK 2 -#define INF 99999 - -using namespace std; - -int checked[999] = {WHITE}; - -void dfs(const list lista[], int start) { - stack 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 lista[INF]; - for (int i = 0; i < n; ++i) { - cin >> u >> w; - lista[u].push_back(w); - } - - dfs(lista, 0); - - return 0; -} diff --git a/graph/dijkstra.cpp b/graph/dijkstra.cpp index ac269de18..a76214560 100644 --- a/graph/dijkstra.cpp +++ b/graph/dijkstra.cpp @@ -26,10 +26,10 @@ #include #include #include +#include #include #include #include -#include constexpr int64_t INF = std::numeric_limits::max(); @@ -39,31 +39,31 @@ constexpr int64_t INF = std::numeric_limits::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>> *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>> *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>> *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>> *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::vector>, - std::greater>> + std::greater>> 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 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 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>> adj1( - 4, std::vector>()); - 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>> adj1( + 4, std::vector>()); + 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>> adj2( - 5, std::vector>()); - 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>> adj2( + 5, std::vector>()); + 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>> adj( - vertices, std::vector>()); + std::vector>> adj( + vertices, std::vector>()); - 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; } diff --git a/graph/hamiltons_cycle.cpp b/graph/hamiltons_cycle.cpp index 06243dadc..1506b78d2 100644 --- a/graph/hamiltons_cycle.cpp +++ b/graph/hamiltons_cycle.cpp @@ -28,49 +28,49 @@ * @return `false` if there is no Hamiltonian cycle in the graph */ bool hamilton_cycle(const std::vector> &routes) { - const size_t n = routes.size(); - // height of dp array which is 2^n - const size_t height = 1 << n; - std::vector> dp(height, std::vector(n, false)); + const size_t n = routes.size(); + // height of dp array which is 2^n + const size_t height = 1 << n; + std::vector> dp(height, std::vector(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 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 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> &routes) { * @return None */ static void test1() { - std::vector> arr{ - std::vector({true, true, false, false}), - std::vector({false, true, true, false}), - std::vector({false, false, true, true}), - std::vector({false, false, false, true})}; + std::vector> arr{ + std::vector({true, true, false, false}), + std::vector({false, true, true, false}), + std::vector({false, false, true, true}), + std::vector({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> arr{ - std::vector({true, true, false, false}), - std::vector({false, true, true, true}), - std::vector({false, false, true, false}), - std::vector({false, false, false, true})}; + std::vector> arr{ + std::vector({true, true, false, false}), + std::vector({false, true, true, true}), + std::vector({false, false, true, false}), + std::vector({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> arr{ - std::vector({true, true, true, true}), - std::vector({true, true, true, true}), - std::vector({true, true, true, true}), - std::vector({true, true, true, true})}; + std::vector> arr{ + std::vector({true, true, true, true}), + std::vector({true, true, true, true}), + std::vector({true, true, true, true}), + std::vector({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; } diff --git a/graph/hopcroft_karp.cpp b/graph/hopcroft_karp.cpp new file mode 100644 index 000000000..d4e002948 --- /dev/null +++ b/graph/hopcroft_karp.cpp @@ -0,0 +1,325 @@ +/** + * @file + * @brief Implementation of [Hopcroft–Karp](https://en.wikipedia.org/wiki/Hopcroft%E2%80%93Karp_algorithm) algorithm. + * @details + * The Hopcroft–Karp 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 +#include +#include +#include +#include +#include +#include + +/** + * @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 >adj; ///< adj[u] stores adjacents of left side and 0 is used for dummy vertex + + std::vector pair_u; ///< value of vertex 'u' ranges from 1 to m + std::vector pair_v; ///< value of vertex 'v' ranges from 1 to n + std::vector 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(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(n + 1,NIL); + + dist = std::vector(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 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::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::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 >(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 u’s 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; + +} diff --git a/graph/is_graph_bipartite.cpp b/graph/is_graph_bipartite.cpp index baadf71fa..2772117fa 100644 --- a/graph/is_graph_bipartite.cpp +++ b/graph/is_graph_bipartite.cpp @@ -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 - * - *
- * 	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
- *	
- *	
+ * @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. + * + *
+ *  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
+ *
+ * 
+ * + * @author [Akshat Vaya](https://github.com/AkVaya) * - * @author [Akshat Vaya](https://github.com/AkVaya) - * */ #include -#include #include +#include /** * @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 > adj; /// adj stores the graph as an adjacency list - - std::vector 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 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 > + 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 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 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; } diff --git a/graph/kosaraju.cpp b/graph/kosaraju.cpp index 5949c0bb8..3b7fdacbb 100644 --- a/graph/kosaraju.cpp +++ b/graph/kosaraju.cpp @@ -3,9 +3,8 @@ */ #include -#include #include - +#include /** * Iterative function/method to print graph: @@ -13,7 +12,7 @@ * @param V number of vertices * @return void **/ -void print(const std::vector< std::vector > &a, int V) { +void print(const std::vector > &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 > &a, int V) { * @param adj adjacency list representation of the graph * @return void **/ -void push_vertex(int v, std::stack *st, std::vector *vis, const std::vector< std::vector > &adj) { +void push_vertex(int v, std::stack *st, std::vector *vis, + const std::vector > &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 *st, std::vector *vis, const std:: * @param grev graph with reversed edges * @return void **/ -void dfs(int v, std::vector *vis, const std::vector< std::vector > &grev) { +void dfs(int v, std::vector *vis, + const std::vector > &grev) { (*vis)[v] = true; // cout<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 > &adj) { +int kosaraju(int V, const std::vector > &adj) { std::vector vis(V, false); std::stack st; for (int v = 0; v < V; v++) { @@ -81,7 +82,7 @@ int kosaraju(int V, const std::vector< std::vector > &adj) { } } // making new graph (grev) with reverse edges as in adj[]: - std::vector< std::vector > grev(V); + std::vector > 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 > adj(a + 1); + std::vector > adj(a + 1); for (int i = 0; i < b; i++) // take total b inputs of 2 vertices each // required to form an edge. { diff --git a/graph/kruskal.cpp b/graph/kruskal.cpp index e179131a1..39f695cb0 100644 --- a/graph/kruskal.cpp +++ b/graph/kruskal.cpp @@ -1,7 +1,7 @@ -#include -#include #include #include +#include +#include //#include // using namespace boost::multiprecision; const int mx = 1e6 + 5; @@ -12,7 +12,7 @@ ll node, edge; std::vector>> edges; void initial() { for (int i = 0; i < node + edge; ++i) { - parent[i] = i; + parent[i] = i; } } diff --git a/graph/lowest_common_ancestor.cpp b/graph/lowest_common_ancestor.cpp index 11029a5b8..7d5ab42b4 100644 --- a/graph/lowest_common_ancestor.cpp +++ b/graph/lowest_common_ancestor.cpp @@ -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 #include +#include #include #include -#include -#include /** * \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 > &undirected_edges) { + Graph(size_t N, const std::vector > &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 > neighbors; + std::vector > 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 > &undirected_edges, int root_) + RootedTree(const std::vector > &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 > up; + std::vector > up; - protected: + protected: /** * Populate the "up" structure. See above. */ @@ -241,9 +241,8 @@ static void tests() { * | * 9 */ - std::vector< std::pair > edges = { - {7, 1}, {1, 5}, {1, 3}, {3, 6}, {6, 2}, {2, 9}, {6, 8}, {4, 3}, {0, 4} - }; + std::vector > 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); diff --git a/graph/max_flow_with_ford_fulkerson_and_edmond_karp_algo.cpp b/graph/max_flow_with_ford_fulkerson_and_edmond_karp_algo.cpp index 87b2fa101..20571cdc5 100644 --- a/graph/max_flow_with_ford_fulkerson_and_edmond_karp_algo.cpp +++ b/graph/max_flow_with_ford_fulkerson_and_edmond_karp_algo.cpp @@ -16,7 +16,7 @@ // std::max capacity of node in graph const int MAXN = 505; class Graph { - std::vector< std::vector > residual_capacity, capacity; + std::vector > residual_capacity, capacity; int total_nodes = 0; int total_edges = 0, source = 0, sink = 0; std::vector parent; @@ -50,8 +50,10 @@ class Graph { void set_graph() { std::cin >> total_nodes >> total_edges >> source >> sink; parent = std::vector(total_nodes, -1); - capacity = residual_capacity = std::vector< std::vector >(total_nodes, std::vector(total_nodes)); - for (int start = 0, destination = 0, capacity_ = 0, i = 0; i < total_edges; ++i) { + capacity = residual_capacity = std::vector >( + total_nodes, std::vector(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_; diff --git a/graph/prim.cpp b/graph/prim.cpp index 0432926ef..f1def6161 100644 --- a/graph/prim.cpp +++ b/graph/prim.cpp @@ -5,7 +5,7 @@ using PII = std::pair; -int prim(int x, const std::vector< std::vector > &graph) { +int prim(int x, const std::vector > &graph) { // priority queue to maintain edges with respect to weights std::priority_queue, std::greater > Q; std::vector marked(graph.size(), false); @@ -40,7 +40,7 @@ int main() { return 0; } - std::vector< std::vector > graph(nodes); + std::vector > graph(nodes); // Edges with their nodes & weight for (int i = 0; i < edges; ++i) { diff --git a/graph/topological_sort.cpp b/graph/topological_sort.cpp index 3827b9bcf..5de8ed69e 100644 --- a/graph/topological_sort.cpp +++ b/graph/topological_sort.cpp @@ -2,7 +2,8 @@ #include #include -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> graph; std::vector visited; std::vector 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()); @@ -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; diff --git a/graph/topological_sort_by_kahns_algo.cpp b/graph/topological_sort_by_kahns_algo.cpp index c29c0fc2d..d491fa80f 100644 --- a/graph/topological_sort_by_kahns_algo.cpp +++ b/graph/topological_sort_by_kahns_algo.cpp @@ -4,7 +4,7 @@ #include #include -std::vector topoSortKahn(int N, const std::vector< std::vector > &adj); +std::vector topoSortKahn(int N, const std::vector > &adj); int main() { int nodes = 0, edges = 0; @@ -14,7 +14,7 @@ int main() { } int u = 0, v = 0; - std::vector< std::vector > graph(nodes); + std::vector > graph(nodes); // create graph // example // 6 6 @@ -32,7 +32,8 @@ int main() { } } -std::vector topoSortKahn(int V, const std::vector< std::vector > &adj) { +std::vector topoSortKahn(int V, + const std::vector > &adj) { std::vector vis(V + 1, false); std::vector deg(V + 1, 0); for (int i = 0; i < V; i++) { diff --git a/greedy_algorithms/jumpgame.cpp b/greedy_algorithms/jumpgame.cpp new file mode 100644 index 000000000..8a8903191 --- /dev/null +++ b/greedy_algorithms/jumpgame.cpp @@ -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 +#include +#include + + + /** + * @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 &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 num1={4,3,1,0,5}; + assert(canJump(num1)==true); + std::cout<<"Input: "; + for(auto i: num1){ + std::cout< num2={3,2,1,0,4}; + assert(canJump(num2)==false); + std::cout<<"Input: "; + for(auto i: num2){ + std::cout<