diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000000..3d685994a1aa --- /dev/null +++ b/.clang-format @@ -0,0 +1,136 @@ +--- +Language: Java +AccessModifierOffset: -4 +AlignAfterOpenBracket: DontAlign +AlignConsecutiveMacros: false +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: false +AlignTrailingComments: false +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: false +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +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: All +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 300 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + - Regex: '.*' + Priority: 1 + SortPriority: 0 +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentCaseLabels: false +IndentGotoLabels: true +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertNewlineAtEOF: true +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: Inner +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +Standard: Latest +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseCRLF: false +UseTab: Never +... diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000000..bcea8e797ffb --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,25 @@ +# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.238.0/containers/java/.devcontainer/base.Dockerfile + +# [Choice] Java version (use -bullseye variants on local arm64/Apple Silicon): 11, 17, 11-bullseye, 17-bullseye, 11-buster, 17-buster +ARG VARIANT="21-bullseye" +FROM mcr.microsoft.com/vscode/devcontainers/java:1.1.0-${VARIANT} + +# [Option] Install Maven +ARG INSTALL_MAVEN="false" +ARG MAVEN_VERSION="" +# [Option] Install Gradle +ARG INSTALL_GRADLE="false" +ARG GRADLE_VERSION="" +RUN if [ "${INSTALL_MAVEN}" = "true" ]; then su vscode -c "umask 0002 && . /usr/local/sdkman/bin/sdkman-init.sh && sdk install maven \"${MAVEN_VERSION}\""; fi \ + && if [ "${INSTALL_GRADLE}" = "true" ]; then su vscode -c "umask 0002 && . /usr/local/sdkman/bin/sdkman-init.sh && sdk install gradle \"${GRADLE_VERSION}\""; fi + +# [Choice] Node.js version: none, lts/*, 16, 14, 12, 10 +ARG NODE_VERSION="none" +RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi + +# [Optional] Uncomment this section to install additional OS packages. +# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ +# && apt-get -y install --no-install-recommends + +# [Optional] Uncomment this line to install global node packages. +# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000000..fdc7cdbd25f9 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,47 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.238.0/containers/java +{ + "name": "Java", + "build": { + "dockerfile": "Dockerfile", + "args": { + // Update the VARIANT arg to pick a Java version: 11, 17 + // Append -bullseye or -buster to pin to an OS version. + // Use the -bullseye variants on local arm64/Apple Silicon. + "VARIANT": "21-bullseye", + // Options + "INSTALL_MAVEN": "true", + "INSTALL_GRADLE": "true", + "NODE_VERSION": "lts/*" + } + }, + + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + // Set *default* container specific settings.json values on container create. + "settings": { + }, + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "vscjava.vscode-java-pack", + "GitHub.copilot", + ] + } + }, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + "postCreateCommand": "java -version", + + // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode", + "features": { + "git": "os-provided", + "github-cli": "latest" + } +} diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000000..0a19890aed44 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @yanglbme @vil02 @BamaCharanChhandogi @alxkm @siriak diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000000..9c906c381608 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,45 @@ +name: "Bug report" +description: "Create a report to help us improve" +title: "[BUG] " +labels: ["bug"] +body: + - type: textarea + id: description + attributes: + label: "Description" + description: "A clear and concise description of what the bug is." + validations: + required: true + - type: textarea + id: steps + attributes: + label: "Steps to reproduce" + description: "Steps to reproduce the behavior (if applicable)" + placeholder: | + 1. Go to '...' + 2. Click on '....' + 3. Scroll down to '....' + 4. See error + validations: + required: false + - type: textarea + id: exceptedbhv + attributes: + label: "Excepted behavior" + description: "A clear and concise description of what you expected to happen." + validations: + required: true + - type: textarea + id: screenshots + attributes: + label: "Screenshots" + description: "If applicable, add screenshots to help explain your problem." + validations: + required: false + - type: textarea + id: context + attributes: + label: "Additional context" + description: "Is there anything else we should know about this bug report?" + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000000..875cc4efab00 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Discord community + url: https://the-algorithms.com/discord/ + about: Have any questions or found any bugs? Please contact us via Discord diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 000000000000..98ff394158be --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,34 @@ +name: "Feature Request" +description: "Suggest an idea for this project" +title: "[FEATURE REQUEST] <title>" +labels: ["enhancement"] +body: + - type: textarea + id: description + attributes: + label: What would you like to Propose? + description: Provide a clear and concise explanation of your Proposal. + validations: + required: true + - type: markdown + attributes: + value: | + For new implementations, please specify the name and problem statement for the algorithm. + For algorithm enhancements, specify what needs to be changed and why. For example: + - Adding tests. + - Optimizing logic. + - Refactoring the file and folders for better structure. + - type: textarea + id: needdetails + attributes: + label: "Issue details" + description: "Write down all the issue/algorithm details description mentioned above." + validations: + required: true + - type: textarea + id: extrainfo + attributes: + label: "Additional Information" + description: "Add any other information or screenshots about the request here." + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/other.yml b/.github/ISSUE_TEMPLATE/other.yml new file mode 100644 index 000000000000..bf8b29f481c8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/other.yml @@ -0,0 +1,19 @@ +name: Other +description: Use this for any other issues. Do NOT create blank issues +title: "[OTHER]" +labels: ["awaiting triage"] +body: + - type: textarea + id: issuedescription + attributes: + label: What would you like to share? + description: Provide a clear and concise explanation of your issue. + validations: + required: true + - type: textarea + id: extrainfo + attributes: + label: Additional information + description: Is there anything else we should know about this issue? + validations: + required: false diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..2e5622f7b51d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,18 @@ +--- +version: 2 +updates: + - package-ecosystem: "docker" + directory: "/" + schedule: + interval: "weekly" + + - package-ecosystem: "github-actions" + directory: "/.github/workflows/" + schedule: + interval: "daily" + + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "daily" +... diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000000..d9cc4c3c35c5 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,16 @@ +<!-- +Thank you for your contribution! +In order to reduce the number of notifications sent to the maintainers, please: +- create your PR as draft, cf. https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests#draft-pull-requests, +- make sure that all of the CI checks pass, +- mark your PR as ready for review, cf. https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request#marking-a-pull-request-as-ready-for-review +--> + +<!-- For completed items, change [ ] to [x] --> + +- [ ] I have read [CONTRIBUTING.md](https://github.com/TheAlgorithms/Java/blob/master/CONTRIBUTING.md). +- [ ] This pull request is all my own work -- I have not plagiarized it. +- [ ] All filenames are in PascalCase. +- [ ] All functions and variable names follow Java naming conventions. +- [ ] All new algorithms have a URL in their comments that points to Wikipedia or other similar explanations. +- [ ] All new code is formatted with `clang-format -i --style=file path/to/your/file.java` \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000000..b3969075d668 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,37 @@ +name: Build +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: 21 + distribution: 'temurin' + - name: Build with Maven + run: mvn --batch-mode --update-snapshots verify + - name: Upload coverage to codecov (tokenless) + if: >- + github.event_name == 'pull_request' && + github.event.pull_request.head.repo.full_name != github.repository + uses: codecov/codecov-action@v5 + with: + fail_ci_if_error: true + - name: Upload coverage to codecov (with token) + if: > + github.repository == 'TheAlgorithms/Java' && + (github.event_name != 'pull_request' || + github.event.pull_request.head.repo.full_name == github.repository) + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: true + - name: Checkstyle + run: mvn checkstyle:check + - name: SpotBugs + run: mvn spotbugs:check + - name: PMD + run: mvn pmd:check diff --git a/.github/workflows/clang-format-lint.yml b/.github/workflows/clang-format-lint.yml new file mode 100644 index 000000000000..dac697511de1 --- /dev/null +++ b/.github/workflows/clang-format-lint.yml @@ -0,0 +1,16 @@ +name: Clang format linter +on: + push: {} + pull_request: {} + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: DoozyX/clang-format-lint-action@v0.20 + with: + source: './src' + extensions: 'java' + clangFormatVersion: 16 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 000000000000..a0908a2b57d9 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,47 @@ +--- +name: "CodeQL" + +on: + workflow_dispatch: + push: + branches: + - master + pull_request: + schedule: + - cron: '53 3 * * 0' + +env: + LANGUAGE: 'java-kotlin' + +jobs: + analyze: + name: Analyze + runs-on: 'ubuntu-latest' + permissions: + actions: read + contents: read + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: 21 + distribution: 'temurin' + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ env.LANGUAGE }} + + - name: Build + run: mvn --batch-mode --update-snapshots verify + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{env.LANGUAGE}}" +... diff --git a/.github/workflows/infer.yml b/.github/workflows/infer.yml new file mode 100644 index 000000000000..a07c6f458083 --- /dev/null +++ b/.github/workflows/infer.yml @@ -0,0 +1,60 @@ +--- +name: Infer + +'on': + workflow_dispatch: + push: + branches: + - master + pull_request: + +jobs: + run_infer: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: 21 + distribution: 'temurin' + + - name: Set up OCaml + uses: ocaml/setup-ocaml@v3 + with: + ocaml-compiler: 5 + + - name: Get current year/weak + run: echo "year_week=$(date +'%Y_%U')" >> $GITHUB_ENV + + - name: Cache infer build + id: cache-infer + uses: actions/cache@v4 + with: + path: infer + key: ${{ runner.os }}-infer-${{ env.year_week }} + + - name: Build infer + if: steps.cache-infer.outputs.cache-hit != 'true' + run: | + cd .. + git clone https://github.com/facebook/infer.git + cd infer + ./build-infer.sh java + cp -r infer ../Java + + - name: Add infer to PATH + run: | + echo "infer/bin" >> $GITHUB_PATH + + - name: Display infer version + run: | + which infer + infer --version + + - name: Run infer + run: | + mvn clean + infer --fail-on-issue --print-logs --no-progress-bar -- mvn test +... diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 000000000000..6fb47c5d2dc9 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,18 @@ +name: 'Close stale issues and PRs' +on: + schedule: + - cron: '0 0 * * *' +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v9 + with: + stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contribution!' + close-issue-message: 'Please reopen this issue once you have made the required changes. If you need help, feel free to ask in our [Discord](https://the-algorithms.com/discord) server or ping one of the maintainers here. Thank you for your contribution!' + stale-pr-message: 'This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contribution!' + close-pr-message: 'Please reopen this pull request once you have made the required changes. If you need help, feel free to ask in our [Discord](https://the-algorithms.com/discord) server or ping one of the maintainers here. Thank you for your contribution!' + exempt-issue-labels: 'dont-close' + exempt-pr-labels: 'dont-close' + days-before-stale: 30 + days-before-close: 7 diff --git a/.github/workflows/update_directory.yml b/.github/workflows/update_directory.yml new file mode 100644 index 000000000000..c811d244e54b --- /dev/null +++ b/.github/workflows/update_directory.yml @@ -0,0 +1,92 @@ +# This GitHub Action updates the DIRECTORY.md file (if needed) when doing a git push or pull_request +name: Update Directory +permissions: + contents: write +on: + push: + paths: + - 'src/**' + pull_request: + paths: + - 'src/**' + workflow_dispatch: + inputs: + logLevel: + description: 'Log level' + required: true + default: 'info' + type: choice + options: + - info + - warning + - debug +jobs: + update_directory_md: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + - name: Update Directory + shell: python + run: | + import os + from typing import Iterator + + URL_BASE = "https://github.com/TheAlgorithms/Java/blob/master" + g_output = [] + + + def good_filepaths(top_dir: str = ".") -> Iterator[str]: + for dirpath, dirnames, filenames in os.walk(top_dir): + dirnames[:] = [d for d in dirnames if d[0] not in "._"] + for filename in filenames: + if os.path.splitext(filename)[1].lower() == ".java": + yield os.path.join(dirpath, filename).lstrip("./") + + + def md_prefix(i): + return f"{i * ' '}*" if i else "\n##" + + + def print_path(old_path: str, new_path: str) -> str: + global g_output + old_parts = old_path.split(os.sep) + mid_diff = False + new_parts = new_path.split(os.sep) + for i, new_part in enumerate(new_parts): + if i + 1 > len(old_parts) or old_parts[i] != new_part or mid_diff: + if i + 1 < len(new_parts): + mid_diff = True + if new_part: + g_output.append(f"{md_prefix(i)} {new_part.replace('_', ' ')}") + return new_path + + + def build_directory_md(top_dir: str = ".") -> str: + global g_output + old_path = "" + for filepath in sorted(good_filepaths(top_dir), key=str.lower): + filepath, filename = os.path.split(filepath) + if filepath != old_path: + old_path = print_path(old_path, filepath) + indent = (filepath.count(os.sep) + 1) if filepath else 0 + url = "/".join((URL_BASE, filepath, filename)).replace(" ", "%20") + filename = os.path.splitext(filename.replace("_", " "))[0] + g_output.append(f"{md_prefix(indent)} [{filename}]({url})") + return "\n".join(g_output) + + + with open("DIRECTORY.md", "w") as out_file: + out_file.write(build_directory_md(".") + "\n") + + - name: Update DIRECTORY.md + run: | + cat DIRECTORY.md + git config --global user.name "$GITHUB_ACTOR" + git config --global user.email "$GITHUB_ACTOR@users.noreply.github.com" + git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY + git add DIRECTORY.md + git commit -am "Update directory" || true + git push --force origin HEAD:$GITHUB_REF || true diff --git a/.gitignore b/.gitignore index b1dfd9119628..eb9d33c78a33 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ /gradle/wrapper/gradle-wrapper.properties + ##----------Android---------- -# build *.apk *.ap_ *.dex @@ -10,27 +10,37 @@ gen/ build/ out/ -# gradle +# Ignoring Gradle build artifacts and project files +##----------Gradle---------- .gradle/ gradle-app.setting !gradle-wrapper.jar build/ +# Ignoring Maven build artifacts and project files +##----------Maven---------- +*.classpath +*.project +*.settings +/target/ local.properties -##----------idea---------- +# Ignoring IntelliJ IDEA project files and configurations +##----------IDEA---------- *.iml .idea/ *.ipr *.iws -# Android Studio Navigation editor temp files +# Ignoring Android Studio Navigation editor temporary files .navigation/ +# Ignoring common system and editor-generated files ##----------Other---------- -# osx *~ .DS_Store gradle.properties +.vscode +*.log -.vscode \ No newline at end of file +/infer-out/ diff --git a/.gitpod.dockerfile b/.gitpod.dockerfile new file mode 100644 index 000000000000..ea6d32a5a377 --- /dev/null +++ b/.gitpod.dockerfile @@ -0,0 +1,22 @@ +FROM gitpod/workspace-java-21:2025-02-10-10-54-28 + +ENV LLVM_SCRIPT="tmp_llvm.sh" + +RUN test ! -f "$LLVM_SCRIPT" \ + && wget https://apt.llvm.org/llvm.sh -O "$LLVM_SCRIPT" \ + && chmod +x "$LLVM_SCRIPT" + +USER root + +RUN ./"$LLVM_SCRIPT" 16 \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + clang-format-16=1:16.0.6~++20231112100510+7cbf1a259152-1~exp1~20231112100554.106 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +RUN ln -s "$(command -v clang-format-16)" "/usr/bin/clang-format" + +USER gitpod + +RUN rm "$LLVM_SCRIPT" diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 000000000000..21d69f6e2122 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,13 @@ +--- +image: + file: .gitpod.dockerfile + +tasks: + - init: | + mvn dependency:resolve + mvn compile + +vscode: + extensions: + - xaver.clang-format + diff --git a/.inferconfig b/.inferconfig new file mode 100644 index 000000000000..6af4f9e2e818 --- /dev/null +++ b/.inferconfig @@ -0,0 +1,24 @@ +{ + "report-block-list-path-regex": [ + "src/main/java/com/thealgorithms/ciphers/a5/CompositeLFSR.java", + "src/main/java/com/thealgorithms/datastructures/crdt/GCounter.java", + "src/main/java/com/thealgorithms/datastructures/crdt/PNCounter.java", + "src/main/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithm.java", + "src/main/java/com/thealgorithms/datastructures/heaps/GenericHeap.java", + "src/main/java/com/thealgorithms/datastructures/lists/DoublyLinkedList.java", + "src/main/java/com/thealgorithms/datastructures/trees/CreateBinaryTreeFromInorderPreorder.java", + "src/main/java/com/thealgorithms/divideandconquer/ClosestPair.java", + "src/main/java/com/thealgorithms/dynamicprogramming/Fibonacci.java", + "src/main/java/com/thealgorithms/maths/SimpsonIntegration.java", + "src/main/java/com/thealgorithms/others/Dijkstra.java", + "src/main/java/com/thealgorithms/sorts/TopologicalSort.java", + "src/main/java/com/thealgorithms/strings/AhoCorasick.java", + "src/test/java/com/thealgorithms/datastructures/caches/LRUCacheTest.java", + "src/test/java/com/thealgorithms/datastructures/lists/SkipListTest.java", + "src/test/java/com/thealgorithms/datastructures/trees/KDTreeTest.java", + "src/test/java/com/thealgorithms/datastructures/trees/LazySegmentTreeTest.java", + "src/test/java/com/thealgorithms/searches/QuickSelectTest.java", + "src/test/java/com/thealgorithms/stacks/PostfixToInfixTest.java", + "src/test/java/com/thealgorithms/strings/HorspoolSearchTest.java" + ] +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000000..f2f8dd9ffdea --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,31 @@ +## How to contribute? + +NOTE: *We DO NOT add leetcode problems. They are just applications of basic principles that can be found in other algorithms included in the repository.* + +### Did you find a bug? + +**Ensure the bug was not already reported** by searching on GitHub under [Project Issues](https://github.com/TheAlgorithms/Java/issues). + - If it is mentioned in the issues and you want to fix it, [fork](https://github.com/TheAlgorithms/Java/fork) the repository and submit your implementation in a pull request. The project maintainers will evaluate it. + - If the bug is **NOT** mentioned in the issues, [open a new issue](https://github.com/TheAlgorithms/Java/issues/new). Be sure to include a **title**, a clear **description** and a **test case** demonstrating the expected behavior that is not occurring. + +NOTE: *Please avoid opening issues asking to be "assigned" to a particular algorithm. This merely creates unnecessary noise for maintainers. Instead, please submit your implementation in a pull request and project maintainers will evaluate it.* + + + +### Do you want to contribute to the documentation? + - [Fork](https://github.com/TheAlgorithms/Java/fork) the repository and make necessary changes. + - Create a pull request. + - It will be put under review for approval. + - If approved, the requested changes will be merged to the repository. + +### Do you want to add a new feature? + +- [Open a new issue](https://github.com/TheAlgorithms/Java/issues/new). +- Be sure to include a **title**, a clear **description** and a **test case** demonstrating the new feature you want to add to the project. + + +### Do you have questions about the source code? + +- Ask any question about how to use the repository in the [TheAlgorithms room in GITTER](https://gitter.im/TheAlgorithms/community?source=orgpage#) or [open a new issue](https://github.com/TheAlgorithms/Java/issues/new) + +:+1::tada: That's all you need to know about the process now it's your turn to help us improve the repository, thank you again! :+1::tada: \ No newline at end of file diff --git a/Conversions/AnyBaseToAnyBase.java b/Conversions/AnyBaseToAnyBase.java deleted file mode 100644 index cfcf0fe9ad61..000000000000 --- a/Conversions/AnyBaseToAnyBase.java +++ /dev/null @@ -1,133 +0,0 @@ -package Conversions; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.InputMismatchException; -import java.util.Scanner; - -/** - * Class for converting from "any" base to "any" other base, when "any" means from 2-36. - * Works by going from base 1 to decimal to base 2. Includes auxiliary method for - * determining whether a number is valid for a given base. - * - * @author Michael Rolland - * @version 2017.10.10 - */ -public class AnyBaseToAnyBase { - - /** - * Smallest and largest base you want to accept as valid input - */ - static final int MINIMUM_BASE = 2; - static final int MAXIMUM_BASE = 36; - - public static void main(String[] args) { - Scanner in = new Scanner(System.in); - String n; - int b1, b2; - while (true) { - try { - System.out.print("Enter number: "); - n = in.next(); - System.out.print("Enter beginning base (between " + MINIMUM_BASE + " and " + MAXIMUM_BASE + "): "); - b1 = in.nextInt(); - if (b1 > MAXIMUM_BASE || b1 < MINIMUM_BASE) { - System.out.println("Invalid base!"); - continue; - } - if (!validForBase(n, b1)) { - System.out.println("The number is invalid for this base!"); - continue; - } - System.out.print("Enter end base (between " + MINIMUM_BASE + " and " + MAXIMUM_BASE + "): "); - b2 = in.nextInt(); - if (b2 > MAXIMUM_BASE || b2 < MINIMUM_BASE) { - System.out.println("Invalid base!"); - continue; - } - break; - } catch (InputMismatchException e) { - System.out.println("Invalid input."); - in.next(); - } - } - System.out.println(base2base(n, b1, b2)); - } - - /** - * Checks if a number (as a String) is valid for a given base. - */ - public static boolean validForBase(String n, int base) { - char[] validDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', - 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', - 'W', 'X', 'Y', 'Z'}; - // digitsForBase contains all the valid digits for the base given - char[] digitsForBase = Arrays.copyOfRange(validDigits, 0, base); - - // Convert character array into set for convenience of contains() method - HashSet<Character> digitsList = new HashSet<>(); - for (int i = 0; i < digitsForBase.length; i++) - digitsList.add(digitsForBase[i]); - - // Check that every digit in n is within the list of valid digits for that base. - for (char c : n.toCharArray()) - if (!digitsList.contains(c)) - return false; - - return true; - } - - /** - * Method to convert any integer from base b1 to base b2. Works by converting from b1 to decimal, - * then decimal to b2. - * - * @param n The integer to be converted. - * @param b1 Beginning base. - * @param b2 End base. - * @return n in base b2. - */ - public static String base2base(String n, int b1, int b2) { - // Declare variables: decimal value of n, - // character of base b1, character of base b2, - // and the string that will be returned. - int decimalValue = 0, charB2; - char charB1; - String output = ""; - // Go through every character of n - for (int i = 0; i < n.length(); i++) { - // store the character in charB1 - charB1 = n.charAt(i); - // if it is a non-number, convert it to a decimal value >9 and store it in charB2 - if (charB1 >= 'A' && charB1 <= 'Z') - charB2 = 10 + (charB1 - 'A'); - // Else, store the integer value in charB2 - else - charB2 = charB1 - '0'; - // Convert the digit to decimal and add it to the - // decimalValue of n - decimalValue = decimalValue * b1 + charB2; - } - - // Converting the decimal value to base b2: - // A number is converted from decimal to another base - // by continuously dividing by the base and recording - // the remainder until the quotient is zero. The number in the - // new base is the remainders, with the last remainder - // being the left-most digit. - - // While the quotient is NOT zero: - while (decimalValue != 0) { - // If the remainder is a digit < 10, simply add it to - // the left side of the new number. - if (decimalValue % b2 < 10) - output = Integer.toString(decimalValue % b2) + output; - // If the remainder is >= 10, add a character with the - // corresponding value to the new number. (A = 10, B = 11, C = 12, ...) - else - output = (char) ((decimalValue % b2) + 55) + output; - // Divide by the new base again - decimalValue /= b2; - } - return output; - } -} diff --git a/Conversions/AnyBaseToDecimal.java b/Conversions/AnyBaseToDecimal.java deleted file mode 100644 index 46451bb86184..000000000000 --- a/Conversions/AnyBaseToDecimal.java +++ /dev/null @@ -1,61 +0,0 @@ -package Conversions; - -import java.io.BufferedReader; -import java.io.InputStreamReader; - -/** - * - * @author Varun Upadhyay (https://github.com/varunu28) - * - */ - -// Driver program -public class AnyBaseToDecimal { - public static void main (String[] args) throws Exception{ - BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); - - String inp = br.readLine(); - int base = Integer.parseInt(br.readLine()); - - System.out.println("Input in base " + base + " is: " + inp); - System.out.println("Decimal value of " + inp + " is: " + convertToDecimal(inp, base)); - - br.close(); - } - - /** - * This method produces a decimal value of any given input number of any base - * @param inp_num String of which we need the decimal value and base in integer format - * @return string format of the decimal value - */ - - public static String convertToDecimal(String inp_num, int base) { - int len = inp_num.length(); - int num = 0; - int pow = 1; - - for (int i=len-1; i>=0; i--) { - if (valOfChar(inp_num.charAt(i)) >= base) { - return "Invalid Number"; - } - num += valOfChar(inp_num.charAt(i))*pow; - pow *= base; - } - return String.valueOf(num); - } - - /** - * This method produces integer value of the input character and returns it - * @param c Char of which we need the integer value of - * @return integer value of input char - */ - - public static int valOfChar(char c) { - if (c >= '0' && c <= '9') { - return (int)c - '0'; - } - else { - return (int)c - 'A' + 10; - } - } -} diff --git a/Conversions/AnytoAny.java b/Conversions/AnytoAny.java deleted file mode 100644 index 5fc744a9936c..000000000000 --- a/Conversions/AnytoAny.java +++ /dev/null @@ -1,29 +0,0 @@ -package Conversions; - -import java.util.Scanner; -//given a source number , source base, destination base, this code can give you the destination number. -//sn ,sb,db ---> ()dn . this is what we have to do . - -public class AnytoAny { - - public static void main(String[] args) { - Scanner scn = new Scanner(System.in); - int sn = scn.nextInt(); - int sb = scn.nextInt(); - int db = scn.nextInt(); - int m = 1, dec = 0, dn = 0; - while (sn != 0) { - dec = dec + (sn % 10) * m; - m *= sb; - sn /= 10; - } - m = 1; - while (dec != 0) { - dn = dn + (dec % db) * m; - m *= 10; - dec /= db; - } - System.out.println(dn); - } - -} diff --git a/Conversions/BinaryToDecimal.java b/Conversions/BinaryToDecimal.java deleted file mode 100644 index 6db926d3b932..000000000000 --- a/Conversions/BinaryToDecimal.java +++ /dev/null @@ -1,30 +0,0 @@ -package Conversions; - -import java.util.Scanner; - -/** - * This class converts a Binary number to a Decimal number - * - */ -class BinaryToDecimal { - - /** - * Main Method - * - * @param args Command line arguments - */ - public static void main(String args[]) { - Scanner sc = new Scanner(System.in); - int binNum, binCopy, d, s = 0, power = 0; - System.out.print("Binary number: "); - binNum = sc.nextInt(); - binCopy = binNum; - while (binCopy != 0) { - d = binCopy % 10; - s += d * (int) Math.pow(2, power++); - binCopy /= 10; - } - System.out.println("Decimal equivalent:" + s); - sc.close(); - } -} diff --git a/Conversions/BinaryToHexadecimal.java b/Conversions/BinaryToHexadecimal.java deleted file mode 100644 index d923c1d8041d..000000000000 --- a/Conversions/BinaryToHexadecimal.java +++ /dev/null @@ -1,55 +0,0 @@ -package Conversions; - -import java.util.*; - -/** - * Converts any Binary Number to a Hexadecimal Number - * - * @author Nishita Aggarwal - */ -public class BinaryToHexadecimal { - - /** - * This method converts a binary number to - * a hexadecimal number. - * - * @param binary The binary number - * @return The hexadecimal number - */ - static String binToHex(int binary) { - //hm to store hexadecimal codes for binary numbers within the range: 0000 to 1111 i.e. for decimal numbers 0 to 15 - HashMap<Integer, String> hm = new HashMap<>(); - //String to store hexadecimal code - String hex = ""; - int i; - for (i = 0; i < 10; i++) { - hm.put(i, String.valueOf(i)); - } - for (i = 10; i < 16; i++) hm.put(i, String.valueOf((char) ('A' + i - 10))); - int currbit; - while (binary != 0) { - int code4 = 0; //to store decimal equivalent of number formed by 4 decimal digits - for (i = 0; i < 4; i++) { - currbit = binary % 10; - binary = binary / 10; - code4 += currbit * Math.pow(2, i); - } - hex = hm.get(code4) + hex; - } - return hex; - } - - /** - * Main method - * - * @param args Command line arguments - */ - public static void main(String[] args) { - Scanner sc = new Scanner(System.in); - System.out.println("Enter binary number:"); - int binary = sc.nextInt(); - String hex = binToHex(binary); - System.out.println("Hexadecimal Code:" + hex); - sc.close(); - } -} diff --git a/Conversions/BinaryToOctal.java b/Conversions/BinaryToOctal.java deleted file mode 100644 index 833ab3a221f7..000000000000 --- a/Conversions/BinaryToOctal.java +++ /dev/null @@ -1,50 +0,0 @@ -package Conversions; - -import java.util.Scanner; - -/** - * Converts any Binary number to an Octal Number - * - * @author Zachary Jones - */ -public class BinaryToOctal { - - /** - * Main method - * - * @param args Command line arguments - */ - public static void main(String args[]) { - Scanner sc = new Scanner(System.in); - System.out.println("Input the binary number: "); - int b = sc.nextInt(); - System.out.println("Octal equivalent: " + convertBinaryToOctal(b)); - sc.close(); - - } - - /** - * This method converts a binary number to - * an octal number. - * - * @param binary The binary number - * @return The octal number - */ - public static String convertBinaryToOctal(int binary) { - String octal = ""; - int currBit = 0, j = 1; - while (binary != 0) { - int code3 = 0; - for (int i = 0; i < 3; i++) { - currBit = binary % 10; - binary = binary / 10; - code3 += currBit * j; - j *= 2; - } - octal = code3 + octal; - j = 1; - } - return octal; - } - -} diff --git a/Conversions/DecimalToAnyBase.java b/Conversions/DecimalToAnyBase.java deleted file mode 100644 index 4b23fc6bbef3..000000000000 --- a/Conversions/DecimalToAnyBase.java +++ /dev/null @@ -1,67 +0,0 @@ -package Conversions; - -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.util.ArrayList; - -/** - * - * @author Varun Upadhyay (https://github.com/varunu28) - * - */ - -// Driver Program -public class DecimalToAnyBase { - public static void main (String[] args) throws Exception{ - BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); - System.out.println("Enter the decimal input below: "); - int decInput = Integer.parseInt(br.readLine()); - System.out.println(); - - System.out.println("Enter the base below: "); - int base = Integer.parseInt(br.readLine()); - System.out.println(); - - System.out.println("Decimal Input" + " is: " + decInput); - System.out.println("Value of " + decInput + " in base " + base + " is: " + convertToAnyBase(decInput, base)); - - br.close(); - } - - /** - * This method produces a String value of any given input decimal in any base - * @param inp Decimal of which we need the value in base in String format - * @return string format of the converted value in the given base - */ - - public static String convertToAnyBase(int inp, int base) { - ArrayList<Character> charArr = new ArrayList<>(); - - while (inp > 0) { - charArr.add(reVal(inp%base)); - inp /= base; - } - - StringBuilder str = new StringBuilder(charArr.size()); - - for(Character ch: charArr) - { - str.append(ch); - } - - return str.reverse().toString(); - } - - /** - * This method produces character value of the input integer and returns it - * @param num integer of which we need the character value of - * @return character value of input integer - */ - - public static char reVal(int num) { - if (num >= 0 && num <= 9) - return (char)(num + '0'); - else - return (char)(num - 10 + 'A'); - } -} diff --git a/Conversions/DecimalToBinary.java b/Conversions/DecimalToBinary.java deleted file mode 100644 index 2c8d3218fdfe..000000000000 --- a/Conversions/DecimalToBinary.java +++ /dev/null @@ -1,58 +0,0 @@ -package Conversions; - -import java.util.Scanner; - -/** - * This class converts a Decimal number to a Binary number - * - * @author Unknown - */ -class DecimalToBinary { - - /** - * Main Method - * - * @param args Command Line Arguments - */ - public static void main(String args[]) { - conventionalConversion(); - bitwiseConversion(); - } - - /** - * This method converts a decimal number - * to a binary number using a conventional - * algorithm. - */ - public static void conventionalConversion() { - int n, b = 0, c = 0, d; - Scanner input = new Scanner(System.in); - System.out.printf("Conventional conversion.\n\tEnter the decimal number: "); - n = input.nextInt(); - while (n != 0) { - d = n % 2; - b = b + d * (int) Math.pow(10, c++); - n /= 2; - } //converting decimal to binary - System.out.println("\tBinary number: " + b); - } - - /** - * This method converts a decimal number - * to a binary number using a bitwise - * algorithm - */ - public static void bitwiseConversion() { - int n, b = 0, c = 0, d; - Scanner input = new Scanner(System.in); - System.out.printf("Bitwise conversion.\n\tEnter the decimal number: "); - n = input.nextInt(); - while (n != 0) { - d = (n & 1); - b += d * (int) Math.pow(10, c++); - n >>= 1; - } - System.out.println("\tBinary number: " + b); - } - -} diff --git a/Conversions/DecimalToHexaDecimal.java b/Conversions/DecimalToHexaDecimal.java deleted file mode 100644 index c9c4e434417c..000000000000 --- a/Conversions/DecimalToHexaDecimal.java +++ /dev/null @@ -1,31 +0,0 @@ -package Conversions; - -class DecimalToHexaDecimal { - private static final int sizeOfIntInHalfBytes = 8; - private static final int numberOfBitsInAHalfByte = 4; - private static final int halfByte = 0x0F; - private static final char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', - 'F' }; - - // Returns the hex value of the dec entered in the parameter. - public static String decToHex(int dec) { - StringBuilder hexBuilder = new StringBuilder(sizeOfIntInHalfBytes); - hexBuilder.setLength(sizeOfIntInHalfBytes); - for (int i = sizeOfIntInHalfBytes - 1; i >= 0; --i) { - int j = dec & halfByte; - hexBuilder.setCharAt(i, hexDigits[j]); - dec >>= numberOfBitsInAHalfByte; - } - return hexBuilder.toString().toLowerCase(); - } - - // Test above function. - public static void main(String[] args) { - System.out.println("Test..."); - int dec = 305445566; - String libraryDecToHex = Integer.toHexString(dec); - String decToHex = decToHex(dec); - System.out.println("Result from the library : " + libraryDecToHex); - System.out.println("Result decToHex method : " + decToHex); - } -} \ No newline at end of file diff --git a/Conversions/DecimalToOctal.java b/Conversions/DecimalToOctal.java deleted file mode 100644 index 017ab33321b5..000000000000 --- a/Conversions/DecimalToOctal.java +++ /dev/null @@ -1,31 +0,0 @@ -package Conversions; - -import java.util.Scanner; - -/** - * This class converts Decimal numbers to Octal Numbers - * - * @author Unknown - */ -public class DecimalToOctal { - /** - * Main Method - * - * @param args Command line Arguments - */ - public static void main(String[] args) { - Scanner sc = new Scanner(System.in); - int n, k, d, s = 0, c = 0; - System.out.print("Decimal number: "); - n = sc.nextInt(); - k = n; - while (k != 0) { - d = k % 8; - s += d * (int) Math.pow(10, c++); - k /= 8; - } - - System.out.println("Octal equivalent:" + s); - sc.close(); - } -} diff --git a/Conversions/HexToOct.java b/Conversions/HexToOct.java deleted file mode 100644 index a46debfe9992..000000000000 --- a/Conversions/HexToOct.java +++ /dev/null @@ -1,70 +0,0 @@ -package Conversions; - -import java.util.Scanner; - -/** - * Converts any Hexadecimal Number to Octal - * - * @author Tanmay Joshi - */ -public class HexToOct { - /** - * This method converts a Hexadecimal number to a decimal number - * - * @param s The Hexadecimal Number - * @return The Decimal number - */ - public static int hex2decimal(String s) { - String str = "0123456789ABCDEF"; - s = s.toUpperCase(); - int val = 0; - for (int i = 0; i < s.length(); i++) { - char a = s.charAt(i); - int n = str.indexOf(a); - val = 16 * val + n; - } - return val; - } - - /** - * This method converts a Decimal number to a octal number - * - * @param q The Decimal Number - * @return The Octal number - */ - public static int decimal2octal(int q) { - int now; - int i = 1; - int octnum = 0; - while (q > 0) { - now = q % 8; - octnum = (now * (int) (Math.pow(10, i))) + octnum; - q /= 8; - i++; - } - octnum /= 10; - return octnum; - } - - /** - * Main method that gets the hex input from user and converts it into octal. - * @param args arguments - */ - public static void main(String args[]) { - String hexadecnum; - int decnum, octalnum; - Scanner scan = new Scanner(System.in); - - System.out.print("Enter Hexadecimal Number : "); - hexadecnum = scan.nextLine(); - - // first convert hexadecimal to decimal - decnum = hex2decimal(hexadecnum); //Pass the string to the hex2decimal function and get the decimal form in variable decnum - - // convert decimal to octal - octalnum = decimal2octal(decnum); - System.out.println("Number in octal: " + octalnum); - - - } -} diff --git a/Conversions/HexaDecimalToBinary.java b/Conversions/HexaDecimalToBinary.java deleted file mode 100644 index 8a79e9b4f00c..000000000000 --- a/Conversions/HexaDecimalToBinary.java +++ /dev/null @@ -1,34 +0,0 @@ -package Conversions; - -public class HexaDecimalToBinary { - - private final int LONG_BITS = 8; - - public void convert(String numHex) { - // String a HexaDecimal: - int conHex = Integer.parseInt(numHex, 16); - // Hex a Binary: - String binary = Integer.toBinaryString(conHex); - // Presentation: - System.out.println(numHex + " = " + completeDigits(binary)); - } - - public String completeDigits(String binNum) { - for (int i = binNum.length(); i < LONG_BITS; i++) { - binNum = "0" + binNum; - } - return binNum; - } - - public static void main(String[] args) { - - //Testing Numbers: - String[] hexNums = {"1", "A1", "ef", "BA", "AA", "BB", - "19", "01", "02", "03", "04"}; - HexaDecimalToBinary objConvert = new HexaDecimalToBinary(); - - for (String num : hexNums) { - objConvert.convert(num); - } - } -} diff --git a/Conversions/HexaDecimalToDecimal.java b/Conversions/HexaDecimalToDecimal.java deleted file mode 100644 index 533009b8ae16..000000000000 --- a/Conversions/HexaDecimalToDecimal.java +++ /dev/null @@ -1,40 +0,0 @@ -package Conversions; - -import java.util.Scanner; - -public class HexaDecimalToDecimal { - - // convert hexadecimal to decimal - public static int getHexaToDec(String hex) { - String digits = "0123456789ABCDEF"; - hex = hex.toUpperCase(); - int val = 0; - for (int i = 0; i < hex.length(); i++) { - int d = digits.indexOf(hex.charAt(i)); - val = 16 * val + d; - } - return val; - } - - // Main method gets the hexadecimal input from user and converts it into Decimal output. - - public static void main(String args[]) { - String hexa_Input; - int dec_output; - Scanner scan = new Scanner(System.in); - - System.out.print("Enter Hexadecimal Number : "); - hexa_Input = scan.nextLine(); - - // convert hexadecimal to decimal - - dec_output = getHexaToDec(hexa_Input); - /* - Pass the string to the getHexaToDec function - and it returns the decimal form in the variable dec_output. - */ - System.out.println("Number in Decimal: " + dec_output); - - - } -} diff --git a/Conversions/IntegerToRoman.java b/Conversions/IntegerToRoman.java deleted file mode 100644 index e979eb536336..000000000000 --- a/Conversions/IntegerToRoman.java +++ /dev/null @@ -1,29 +0,0 @@ -package Conversions; - -public class IntegerToRoman { - private static int[] allArabianRomanNumbers = new int[]{1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}; - private static String[] allRomanNumbers = new String[]{"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}; - - public static String integerToRoman(int num) { - if (num <= 0) { - return ""; - } - - StringBuilder builder = new StringBuilder(); - - for (int a = 0; a < allArabianRomanNumbers.length; a++) { - int times = num / allArabianRomanNumbers[a]; - for (int b = 0; b < times; b++) { - builder.append(allRomanNumbers[a]); - } - - num -= times * allArabianRomanNumbers[a]; - } - - return builder.toString(); - } - - public static void main(String[] args) { - System.out.println(IntegerToRoman.integerToRoman(2131)); - } -} diff --git a/Conversions/OctalToDecimal.java b/Conversions/OctalToDecimal.java deleted file mode 100644 index 3b8c996bc200..000000000000 --- a/Conversions/OctalToDecimal.java +++ /dev/null @@ -1,49 +0,0 @@ -package Conversions; - -import java.util.Scanner; - -/** - * Converts any Octal Number to a Decimal Number - * - * @author Zachary Jones - * - */ -public class OctalToDecimal { - - /** - * Main method - * - * @param args - * Command line arguments - */ - public static void main(String args[]) { - Scanner sc = new Scanner(System.in); - System.out.print("Octal Input: "); - String inputOctal = sc.nextLine(); - int result = convertOctalToDecimal(inputOctal); - if (result != -1) - System.out.println("Result convertOctalToDecimal : " + result); - sc.close(); - } - - /** - * This method converts an octal number to a decimal number. - * - * @param inputOctal - * The octal number - * @return The decimal number - */ - public static int convertOctalToDecimal(String inputOctal) { - - try { - // Actual conversion of Octal to Decimal: - Integer outputDecimal = Integer.parseInt(inputOctal, 8); - return outputDecimal; - } catch (NumberFormatException ne) { - // Printing a warning message if the input is not a valid octal - // number: - System.out.println("Invalid Input, Expecting octal number 0-7"); - return -1; - } - } -} \ No newline at end of file diff --git a/Conversions/OctalToHexadecimal.java b/Conversions/OctalToHexadecimal.java deleted file mode 100644 index 2cefc92002ca..000000000000 --- a/Conversions/OctalToHexadecimal.java +++ /dev/null @@ -1,64 +0,0 @@ -package Conversions; - -import java.util.Scanner; - -/** - * Converts any Octal Number to HexaDecimal - * - * @author Tanmay Joshi - */ -public class OctalToHexadecimal { - - /** - * This method converts a Octal number to a decimal number - * - * @param s The Octal Number - * @return The Decimal number - */ - public static int OctToDec(String s) { - int i = 0; - for (int j = 0; j < s.length(); j++) { - char num = s.charAt(j); - num -= '0'; - i *= 8; - i += num; - } - return i; - } - - /** - * This method converts a Decimal number to a Hexadecimal number - * - * @param d The Decimal Number - * @return The Hexadecimal number - */ - public static String DecimalToHex(int d) { - String digits = "0123456789ABCDEF"; - if (d <= 0) - return "0"; - String hex = ""; - while (d > 0) { - int digit = d % 16; - hex = digits.charAt(digit) + hex; - d = d / 16; - } - return hex; - } - - - public static void main(String args[]) { - - Scanner input = new Scanner(System.in); - System.out.print("Enter the Octal number: "); - // Take octal number as input from user in a string - String oct = input.next(); - - // Pass the octal number to function and get converted deciaml form - int decimal = OctToDec(oct); - - // Pass the decimla number to function and get converted Hex form of the number - String hex = DecimalToHex(decimal); - System.out.println("The Hexadecimal equivalant is: " + hex); - } -} - diff --git a/Conversions/RomanToInteger.java b/Conversions/RomanToInteger.java deleted file mode 100644 index 7d6e0650ab46..000000000000 --- a/Conversions/RomanToInteger.java +++ /dev/null @@ -1,58 +0,0 @@ -package Conversions; - -import java.util.*; - -public class RomanToInteger { - - private static Map<Character, Integer> map = new HashMap<Character, Integer>() {{ - put('I', 1); - put('V', 5); - put('X', 10); - put('L', 50); - put('C', 100); - put('D', 500); - put('M', 1000); - }}; - - /** - * This function convert Roman number into Integer - * - * @param A Roman number string - * @return integer - */ - public static int romanToInt(String A) { - - char prev = ' '; - - int sum = 0; - - int newPrev = 0; - for (int i = A.length() - 1; i >= 0; i--) { - char c = A.charAt(i); - - if (prev != ' ') { - // checking current Number greater then previous or not - newPrev = map.get(prev) > newPrev ? map.get(prev) : newPrev; - } - - int currentNum = map.get(c); - - // if current number greater then prev max previous then add - if (currentNum >= newPrev) { - sum += currentNum; - } else { - // subtract upcoming number until upcoming number not greater then prev max - sum -= currentNum; - } - - prev = c; - } - - return sum; - } - - public static void main(String[] args) { - int sum = romanToInt("MDCCCIV"); - System.out.println(sum); - } -} diff --git a/DIRECTORY.md b/DIRECTORY.md new file mode 100644 index 000000000000..fe9e440da3e2 --- /dev/null +++ b/DIRECTORY.md @@ -0,0 +1,1382 @@ + +## src + * main + * java + * com + * thealgorithms + * audiofilters + * [EMAFilter](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/audiofilters/EMAFilter.java) + * [IIRFilter](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/audiofilters/IIRFilter.java) + * backtracking + * [AllPathsFromSourceToTarget](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/AllPathsFromSourceToTarget.java) + * [ArrayCombination](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/ArrayCombination.java) + * [Combination](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/Combination.java) + * [CrosswordSolver](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/CrosswordSolver.java) + * [FloodFill](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/FloodFill.java) + * [KnightsTour](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/KnightsTour.java) + * [MazeRecursion](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/MazeRecursion.java) + * [MColoring](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/MColoring.java) + * [NQueens](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/NQueens.java) + * [ParenthesesGenerator](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/ParenthesesGenerator.java) + * [Permutation](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/Permutation.java) + * [PowerSum](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/PowerSum.java) + * [SubsequenceFinder](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/SubsequenceFinder.java) + * [WordPatternMatcher](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/WordPatternMatcher.java) + * [WordSearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/WordSearch.java) + * bitmanipulation + * [BcdConversion](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/BcdConversion.java) + * [BinaryPalindromeCheck](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/BinaryPalindromeCheck.java) + * [BitSwap](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/BitSwap.java) + * [BooleanAlgebraGates](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/BooleanAlgebraGates.java) + * [ClearLeftmostSetBit](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/ClearLeftmostSetBit.java) + * [CountLeadingZeros](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/CountLeadingZeros.java) + * [CountSetBits](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/CountSetBits.java) + * [FindNthBit](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/FindNthBit.java) + * [FirstDifferentBit](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/FirstDifferentBit.java) + * [GenerateSubsets](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/GenerateSubsets.java) + * [GrayCodeConversion](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/GrayCodeConversion.java) + * [HammingDistance](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/HammingDistance.java) + * [HigherLowerPowerOfTwo](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/HigherLowerPowerOfTwo.java) + * [HighestSetBit](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/HighestSetBit.java) + * [IndexOfRightMostSetBit](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/IndexOfRightMostSetBit.java) + * [IsEven](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/IsEven.java) + * [IsPowerTwo](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/IsPowerTwo.java) + * [LowestSetBit](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/LowestSetBit.java) + * [ModuloPowerOfTwo](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/ModuloPowerOfTwo.java) + * [NextHigherSameBitCount](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/NextHigherSameBitCount.java) + * [NonRepeatingNumberFinder](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/NonRepeatingNumberFinder.java) + * [NumberAppearingOddTimes](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/NumberAppearingOddTimes.java) + * [NumbersDifferentSigns](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/NumbersDifferentSigns.java) + * [OneBitDifference](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/OneBitDifference.java) + * [OnesComplement](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/OnesComplement.java) + * [ParityCheck](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/ParityCheck.java) + * [ReverseBits](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/ReverseBits.java) + * [SingleBitOperations](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/SingleBitOperations.java) + * [SingleElement](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/SingleElement.java) + * [SwapAdjacentBits](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/SwapAdjacentBits.java) + * [TwosComplement](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/TwosComplement.java) + * [Xs3Conversion](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/Xs3Conversion.java) + * ciphers + * a5 + * [A5Cipher](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/a5/A5Cipher.java) + * [A5KeyStreamGenerator](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/a5/A5KeyStreamGenerator.java) + * [BaseLFSR](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/a5/BaseLFSR.java) + * [CompositeLFSR](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/a5/CompositeLFSR.java) + * [LFSR](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/a5/LFSR.java) + * [Utils](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/a5/Utils.java) + * [ADFGVXCipher](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/ADFGVXCipher.java) + * [AES](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/AES.java) + * [AESEncryption](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/AESEncryption.java) + * [AffineCipher](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/AffineCipher.java) + * [AtbashCipher](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/AtbashCipher.java) + * [Autokey](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/Autokey.java) + * [BaconianCipher](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/BaconianCipher.java) + * [Blowfish](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/Blowfish.java) + * [Caesar](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/Caesar.java) + * [ColumnarTranspositionCipher](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/ColumnarTranspositionCipher.java) + * [DES](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/DES.java) + * [DiffieHellman](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/DiffieHellman.java) + * [ECC](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/ECC.java) + * [HillCipher](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/HillCipher.java) + * [MonoAlphabetic](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/MonoAlphabetic.java) + * [PlayfairCipher](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/PlayfairCipher.java) + * [Polybius](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/Polybius.java) + * [ProductCipher](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/ProductCipher.java) + * [RailFenceCipher](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/RailFenceCipher.java) + * [RSA](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/RSA.java) + * [SimpleSubCipher](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/SimpleSubCipher.java) + * [Vigenere](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/Vigenere.java) + * [XORCipher](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/XORCipher.java) + * conversions + * [AffineConverter](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/AffineConverter.java) + * [AnyBaseToAnyBase](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/AnyBaseToAnyBase.java) + * [AnyBaseToDecimal](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/AnyBaseToDecimal.java) + * [AnytoAny](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/AnytoAny.java) + * [BinaryToDecimal](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/BinaryToDecimal.java) + * [BinaryToHexadecimal](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/BinaryToHexadecimal.java) + * [BinaryToOctal](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/BinaryToOctal.java) + * [DecimalToAnyBase](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/DecimalToAnyBase.java) + * [DecimalToBinary](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/DecimalToBinary.java) + * [DecimalToHexadecimal](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/DecimalToHexadecimal.java) + * [DecimalToOctal](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/DecimalToOctal.java) + * [EndianConverter](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/EndianConverter.java) + * [HexaDecimalToBinary](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/HexaDecimalToBinary.java) + * [HexaDecimalToDecimal](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/HexaDecimalToDecimal.java) + * [HexToOct](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/HexToOct.java) + * [IntegerToEnglish](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/IntegerToEnglish.java) + * [IntegerToRoman](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/IntegerToRoman.java) + * [IPConverter](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/IPConverter.java) + * [IPv6Converter](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/IPv6Converter.java) + * [MorseCodeConverter](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/MorseCodeConverter.java) + * [NumberToWords](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/NumberToWords.java) + * [OctalToBinary](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/OctalToBinary.java) + * [OctalToDecimal](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/OctalToDecimal.java) + * [OctalToHexadecimal](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/OctalToHexadecimal.java) + * [PhoneticAlphabetConverter](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/PhoneticAlphabetConverter.java) + * [RgbHsvConversion](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/RgbHsvConversion.java) + * [RomanToInteger](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/RomanToInteger.java) + * [TurkishToLatinConversion](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/TurkishToLatinConversion.java) + * [UnitConversions](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/UnitConversions.java) + * [UnitsConverter](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/UnitsConverter.java) + * [WordsToNumber](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/conversions/WordsToNumber.java) + * datastructures + * bags + * [Bag](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/bags/Bag.java) + * bloomfilter + * [BloomFilter](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/bloomfilter/BloomFilter.java) + * buffers + * [CircularBuffer](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/buffers/CircularBuffer.java) + * caches + * [LFUCache](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/caches/LFUCache.java) + * [LRUCache](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/caches/LRUCache.java) + * [MRUCache](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/caches/MRUCache.java) + * crdt + * [GCounter](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/crdt/GCounter.java) + * [GSet](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/crdt/GSet.java) + * [LWWElementSet](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/crdt/LWWElementSet.java) + * [ORSet](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/crdt/ORSet.java) + * [PNCounter](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/crdt/PNCounter.java) + * [TwoPSet](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/crdt/TwoPSet.java) + * disjointsetunion + * [DisjointSetUnion](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/disjointsetunion/DisjointSetUnion.java) + * [Node](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/disjointsetunion/Node.java) + * dynamicarray + * [DynamicArray](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/dynamicarray/DynamicArray.java) + * graphs + * [AStar](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/AStar.java) + * [BellmanFord](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/BellmanFord.java) + * [BipartiteGraphDFS](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/BipartiteGraphDFS.java) + * [BoruvkaAlgorithm](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/BoruvkaAlgorithm.java) + * [ConnectedComponent](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/ConnectedComponent.java) + * [Cycles](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/Cycles.java) + * [DijkstraAlgorithm](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/DijkstraAlgorithm.java) + * [DijkstraOptimizedAlgorithm](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/DijkstraOptimizedAlgorithm.java) + * [EdmondsBlossomAlgorithm](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/EdmondsBlossomAlgorithm.java) + * [FloydWarshall](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/FloydWarshall.java) + * [FordFulkerson](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/FordFulkerson.java) + * [Graphs](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/Graphs.java) + * [HamiltonianCycle](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/HamiltonianCycle.java) + * [JohnsonsAlgorithm](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/JohnsonsAlgorithm.java) + * [KahnsAlgorithm](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithm.java) + * [Kosaraju](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/Kosaraju.java) + * [Kruskal](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/Kruskal.java) + * [MatrixGraphs](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/MatrixGraphs.java) + * [PrimMST](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/PrimMST.java) + * [TarjansAlgorithm](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/TarjansAlgorithm.java) + * [UndirectedAdjacencyListGraph](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/UndirectedAdjacencyListGraph.java) + * [WelshPowell](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/WelshPowell.java) + * hashmap + * hashing + * [GenericHashMapUsingArray](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArray.java) + * [GenericHashMapUsingArrayList](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayList.java) + * [HashMap](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/HashMap.java) + * [HashMapCuckooHashing](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapCuckooHashing.java) + * [Intersection](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/Intersection.java) + * [LinearProbingHashMap](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/LinearProbingHashMap.java) + * [MainCuckooHashing](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/MainCuckooHashing.java) + * [MajorityElement](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/MajorityElement.java) + * [Map](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/Map.java) + * heaps + * [EmptyHeapException](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/heaps/EmptyHeapException.java) + * [FibonacciHeap](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/heaps/FibonacciHeap.java) + * [GenericHeap](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/heaps/GenericHeap.java) + * [Heap](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/heaps/Heap.java) + * [HeapElement](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/heaps/HeapElement.java) + * [KthElementFinder](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/heaps/KthElementFinder.java) + * [LeftistHeap](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/heaps/LeftistHeap.java) + * [MaxHeap](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/heaps/MaxHeap.java) + * [MedianFinder](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/heaps/MedianFinder.java) + * [MergeKSortedArrays](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/heaps/MergeKSortedArrays.java) + * [MinHeap](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/heaps/MinHeap.java) + * [MinPriorityQueue](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/heaps/MinPriorityQueue.java) + * lists + * [CircleLinkedList](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/lists/CircleLinkedList.java) + * [CountSinglyLinkedListRecursion](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/lists/CountSinglyLinkedListRecursion.java) + * [CreateAndDetectLoop](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/lists/CreateAndDetectLoop.java) + * [CursorLinkedList](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/lists/CursorLinkedList.java) + * [DoublyLinkedList](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/lists/DoublyLinkedList.java) + * [MergeKSortedLinkedList](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/lists/MergeKSortedLinkedList.java) + * [MergeSortedArrayList](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/lists/MergeSortedArrayList.java) + * [MergeSortedSinglyLinkedList](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/lists/MergeSortedSinglyLinkedList.java) + * [QuickSortLinkedList](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/lists/QuickSortLinkedList.java) + * [RandomNode](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/lists/RandomNode.java) + * [ReverseKGroup](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/lists/ReverseKGroup.java) + * [RotateSinglyLinkedLists](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/lists/RotateSinglyLinkedLists.java) + * [SearchSinglyLinkedListRecursion](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/lists/SearchSinglyLinkedListRecursion.java) + * [SinglyLinkedList](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/lists/SinglyLinkedList.java) + * [SkipList](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/lists/SkipList.java) + * [SortedLinkedList](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/lists/SortedLinkedList.java) + * [Node](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/Node.java) + * queues + * [CircularQueue](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/queues/CircularQueue.java) + * [Deque](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/queues/Deque.java) + * [GenericArrayListQueue](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/queues/GenericArrayListQueue.java) + * [LinkedQueue](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/queues/LinkedQueue.java) + * [PriorityQueues](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/queues/PriorityQueues.java) + * [Queue](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/queues/Queue.java) + * [QueueByTwoStacks](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/queues/QueueByTwoStacks.java) + * [SlidingWindowMaximum](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/queues/SlidingWindowMaximum.java) + * [TokenBucket](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/queues/TokenBucket.java) + * stacks + * [NodeStack](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/stacks/NodeStack.java) + * [ReverseStack](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/stacks/ReverseStack.java) + * [Stack](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/stacks/Stack.java) + * [StackArray](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/stacks/StackArray.java) + * [StackArrayList](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/stacks/StackArrayList.java) + * [StackOfLinkedList](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/stacks/StackOfLinkedList.java) + * trees + * [AVLSimple](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/AVLSimple.java) + * [AVLTree](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/AVLTree.java) + * [BinaryTree](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/BinaryTree.java) + * [BoundaryTraversal](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/BoundaryTraversal.java) + * [BSTFromSortedArray](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/BSTFromSortedArray.java) + * [BSTIterative](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/BSTIterative.java) + * [BSTRecursive](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/BSTRecursive.java) + * [BSTRecursiveGeneric](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/BSTRecursiveGeneric.java) + * [CeilInBinarySearchTree](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/CeilInBinarySearchTree.java) + * [CheckBinaryTreeIsValidBST](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/CheckBinaryTreeIsValidBST.java) + * [CheckIfBinaryTreeBalanced](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/CheckIfBinaryTreeBalanced.java) + * [CheckTreeIsSymmetric](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/CheckTreeIsSymmetric.java) + * [CreateBinaryTreeFromInorderPreorder](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/CreateBinaryTreeFromInorderPreorder.java) + * [FenwickTree](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/FenwickTree.java) + * [GenericTree](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/GenericTree.java) + * [InorderTraversal](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/InorderTraversal.java) + * [KDTree](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/KDTree.java) + * [LazySegmentTree](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/LazySegmentTree.java) + * [LCA](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/LCA.java) + * [LevelOrderTraversal](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/LevelOrderTraversal.java) + * [nearestRightKey](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/nearestRightKey.java) + * [PostOrderTraversal](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/PostOrderTraversal.java) + * [PreOrderTraversal](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/PreOrderTraversal.java) + * [PrintTopViewofTree](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/PrintTopViewofTree.java) + * [QuadTree](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/QuadTree.java) + * [RedBlackBST](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/RedBlackBST.java) + * [SameTreesCheck](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/SameTreesCheck.java) + * [SegmentTree](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/SegmentTree.java) + * [SplayTree](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/SplayTree.java) + * [Treap](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/Treap.java) + * [TreeRandomNode](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/TreeRandomNode.java) + * [Trie](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/Trie.java) + * [VerticalOrderTraversal](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/VerticalOrderTraversal.java) + * [ZigzagTraversal](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/trees/ZigzagTraversal.java) + * devutils + * entities + * [ProcessDetails](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/devutils/entities/ProcessDetails.java) + * nodes + * [LargeTreeNode](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/devutils/nodes/LargeTreeNode.java) + * [Node](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/devutils/nodes/Node.java) + * [SimpleNode](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/devutils/nodes/SimpleNode.java) + * [SimpleTreeNode](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/devutils/nodes/SimpleTreeNode.java) + * [TreeNode](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/devutils/nodes/TreeNode.java) + * searches + * [MatrixSearchAlgorithm](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/devutils/searches/MatrixSearchAlgorithm.java) + * [SearchAlgorithm](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/devutils/searches/SearchAlgorithm.java) + * divideandconquer + * [BinaryExponentiation](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/divideandconquer/BinaryExponentiation.java) + * [ClosestPair](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/divideandconquer/ClosestPair.java) + * [CountingInversions](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/divideandconquer/CountingInversions.java) + * [MedianOfTwoSortedArrays](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/divideandconquer/MedianOfTwoSortedArrays.java) + * [SkylineAlgorithm](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/divideandconquer/SkylineAlgorithm.java) + * [StrassenMatrixMultiplication](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/divideandconquer/StrassenMatrixMultiplication.java) + * [TilingProblem](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/divideandconquer/TilingProblem.java) + * dynamicprogramming + * [Abbreviation](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/Abbreviation.java) + * [AllConstruct](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/AllConstruct.java) + * [AssignmentUsingBitmask](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/AssignmentUsingBitmask.java) + * [BoardPath](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/BoardPath.java) + * [BoundaryFill](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/BoundaryFill.java) + * [BruteForceKnapsack](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/BruteForceKnapsack.java) + * [CatalanNumber](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/CatalanNumber.java) + * [ClimbingStairs](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/ClimbingStairs.java) + * [CoinChange](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/CoinChange.java) + * [CountFriendsPairing](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/CountFriendsPairing.java) + * [DiceThrow](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/DiceThrow.java) + * [EditDistance](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/EditDistance.java) + * [EggDropping](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/EggDropping.java) + * [Fibonacci](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/Fibonacci.java) + * [KadaneAlgorithm](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/KadaneAlgorithm.java) + * [Knapsack](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/Knapsack.java) + * [KnapsackMemoization](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/KnapsackMemoization.java) + * [LevenshteinDistance](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/LevenshteinDistance.java) + * [LongestAlternatingSubsequence](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/LongestAlternatingSubsequence.java) + * [LongestArithmeticSubsequence](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/LongestArithmeticSubsequence.java) + * [LongestCommonSubsequence](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequence.java) + * [LongestIncreasingSubsequence](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/LongestIncreasingSubsequence.java) + * [LongestPalindromicSubsequence](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/LongestPalindromicSubsequence.java) + * [LongestPalindromicSubstring](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/LongestPalindromicSubstring.java) + * [LongestValidParentheses](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/LongestValidParentheses.java) + * [MatrixChainMultiplication](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/MatrixChainMultiplication.java) + * [MatrixChainRecursiveTopDownMemoisation](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/MatrixChainRecursiveTopDownMemoisation.java) + * [MaximumSumOfNonAdjacentElements](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/MaximumSumOfNonAdjacentElements.java) + * [MinimumPathSum](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/MinimumPathSum.java) + * [MinimumSumPartition](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/MinimumSumPartition.java) + * [NewManShanksPrime](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/NewManShanksPrime.java) + * [OptimalJobScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/OptimalJobScheduling.java) + * [PalindromicPartitioning](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/PalindromicPartitioning.java) + * [PartitionProblem](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/PartitionProblem.java) + * [RegexMatching](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/RegexMatching.java) + * [RodCutting](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/RodCutting.java) + * [ShortestCommonSupersequenceLength](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/ShortestCommonSupersequenceLength.java) + * [SubsetCount](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/SubsetCount.java) + * [SubsetSum](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/SubsetSum.java) + * [SubsetSumSpaceOptimized](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/SubsetSumSpaceOptimized.java) + * [SumOfSubset](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/SumOfSubset.java) + * [TreeMatching](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/TreeMatching.java) + * [Tribonacci](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/Tribonacci.java) + * [UniquePaths](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/UniquePaths.java) + * [UniqueSubsequencesCount](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/UniqueSubsequencesCount.java) + * [WildcardMatching](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/WildcardMatching.java) + * [WineProblem](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/dynamicprogramming/WineProblem.java) + * geometry + * [BresenhamLine](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/geometry/BresenhamLine.java) + * [ConvexHull](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/geometry/ConvexHull.java) + * [GrahamScan](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/geometry/GrahamScan.java) + * [MidpointCircle](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/geometry/MidpointCircle.java) + * [MidpointEllipse](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/geometry/MidpointEllipse.java) + * [Point](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/geometry/Point.java) + * graph + * [ConstrainedShortestPath](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/graph/ConstrainedShortestPath.java) + * [StronglyConnectedComponentOptimized](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/graph/StronglyConnectedComponentOptimized.java) + * greedyalgorithms + * [ActivitySelection](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/greedyalgorithms/ActivitySelection.java) + * [BandwidthAllocation](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/greedyalgorithms/BandwidthAllocation.java) + * [BinaryAddition](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/greedyalgorithms/BinaryAddition.java) + * [CoinChange](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/greedyalgorithms/CoinChange.java) + * [DigitSeparation](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/greedyalgorithms/DigitSeparation.java) + * [EgyptianFraction](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/greedyalgorithms/EgyptianFraction.java) + * [FractionalKnapsack](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/greedyalgorithms/FractionalKnapsack.java) + * [GaleShapley](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/greedyalgorithms/GaleShapley.java) + * [JobSequencing](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/greedyalgorithms/JobSequencing.java) + * [KCenters](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/greedyalgorithms/KCenters.java) + * [MergeIntervals](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/greedyalgorithms/MergeIntervals.java) + * [MinimizingLateness](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/greedyalgorithms/MinimizingLateness.java) + * [MinimumWaitingTime](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/greedyalgorithms/MinimumWaitingTime.java) + * [OptimalFileMerging](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/greedyalgorithms/OptimalFileMerging.java) + * [StockProfitCalculator](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/greedyalgorithms/StockProfitCalculator.java) + * io + * [BufferedReader](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/io/BufferedReader.java) + * lineclipping + * [CohenSutherland](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/lineclipping/CohenSutherland.java) + * [LiangBarsky](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/lineclipping/LiangBarsky.java) + * utils + * [Line](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/lineclipping/utils/Line.java) + * [Point](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/lineclipping/utils/Point.java) + * maths + * [AbsoluteMax](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/AbsoluteMax.java) + * [AbsoluteMin](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/AbsoluteMin.java) + * [AbsoluteValue](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/AbsoluteValue.java) + * [ADTFraction](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/ADTFraction.java) + * [AliquotSum](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/AliquotSum.java) + * [AmicableNumber](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/AmicableNumber.java) + * [Area](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Area.java) + * [Armstrong](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Armstrong.java) + * [AutoCorrelation](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/AutoCorrelation.java) + * [AutomorphicNumber](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/AutomorphicNumber.java) + * [Average](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Average.java) + * [BinaryPow](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/BinaryPow.java) + * [BinomialCoefficient](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/BinomialCoefficient.java) + * [CatalanNumbers](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/CatalanNumbers.java) + * [Ceil](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Ceil.java) + * [ChineseRemainderTheorem](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/ChineseRemainderTheorem.java) + * [CircularConvolutionFFT](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/CircularConvolutionFFT.java) + * [CollatzConjecture](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/CollatzConjecture.java) + * [Combinations](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Combinations.java) + * [Convolution](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Convolution.java) + * [ConvolutionFFT](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/ConvolutionFFT.java) + * [CrossCorrelation](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/CrossCorrelation.java) + * [DeterminantOfMatrix](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/DeterminantOfMatrix.java) + * [DigitalRoot](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/DigitalRoot.java) + * [DistanceFormula](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/DistanceFormula.java) + * [DudeneyNumber](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/DudeneyNumber.java) + * [EulerMethod](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/EulerMethod.java) + * [EulersFunction](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/EulersFunction.java) + * [Factorial](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Factorial.java) + * [FactorialRecursion](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/FactorialRecursion.java) + * [FastExponentiation](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/FastExponentiation.java) + * [FastInverseSqrt](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/FastInverseSqrt.java) + * [FFT](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/FFT.java) + * [FFTBluestein](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/FFTBluestein.java) + * [FibonacciJavaStreams](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/FibonacciJavaStreams.java) + * [FibonacciLoop](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/FibonacciLoop.java) + * [FibonacciNumberCheck](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/FibonacciNumberCheck.java) + * [FibonacciNumberGoldenRation](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/FibonacciNumberGoldenRation.java) + * [FindKthNumber](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/FindKthNumber.java) + * [FindMax](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/FindMax.java) + * [FindMaxRecursion](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/FindMaxRecursion.java) + * [FindMin](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/FindMin.java) + * [FindMinRecursion](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/FindMinRecursion.java) + * [Floor](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Floor.java) + * [FrizzyNumber](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/FrizzyNumber.java) + * [Gaussian](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Gaussian.java) + * [GCD](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/GCD.java) + * [GCDRecursion](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/GCDRecursion.java) + * [GenericRoot](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/GenericRoot.java) + * [GoldbachConjecture](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/GoldbachConjecture.java) + * [HarshadNumber](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/HarshadNumber.java) + * [HeronsFormula](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/HeronsFormula.java) + * [JosephusProblem](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/JosephusProblem.java) + * [JugglerSequence](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/JugglerSequence.java) + * [KaprekarNumbers](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/KaprekarNumbers.java) + * [KaratsubaMultiplication](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/KaratsubaMultiplication.java) + * [KeithNumber](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/KeithNumber.java) + * [KrishnamurthyNumber](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/KrishnamurthyNumber.java) + * [LeastCommonMultiple](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/LeastCommonMultiple.java) + * [LeonardoNumber](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/LeonardoNumber.java) + * [LinearDiophantineEquationsSolver](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/LinearDiophantineEquationsSolver.java) + * [LongDivision](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/LongDivision.java) + * [LucasSeries](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/LucasSeries.java) + * [MagicSquare](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/MagicSquare.java) + * [MathBuilder](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/MathBuilder.java) + * [MaxValue](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/MaxValue.java) + * [Means](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Means.java) + * [Median](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Median.java) + * [MinValue](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/MinValue.java) + * [Mode](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Mode.java) + * [NonRepeatingElement](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/NonRepeatingElement.java) + * [NthUglyNumber](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/NthUglyNumber.java) + * [NumberOfDigits](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/NumberOfDigits.java) + * [PalindromeNumber](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/PalindromeNumber.java) + * [ParseInteger](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/ParseInteger.java) + * [PascalTriangle](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/PascalTriangle.java) + * [PerfectCube](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/PerfectCube.java) + * [PerfectNumber](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/PerfectNumber.java) + * [PerfectSquare](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/PerfectSquare.java) + * [Perimeter](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Perimeter.java) + * [PiNilakantha](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/PiNilakantha.java) + * [PollardRho](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/PollardRho.java) + * [Pow](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Pow.java) + * [PowerOfTwoOrNot](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/PowerOfTwoOrNot.java) + * [PowerUsingRecursion](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/PowerUsingRecursion.java) + * Prime + * [LiouvilleLambdaFunction](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Prime/LiouvilleLambdaFunction.java) + * [MillerRabinPrimalityCheck](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Prime/MillerRabinPrimalityCheck.java) + * [MobiusFunction](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Prime/MobiusFunction.java) + * [PrimeCheck](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Prime/PrimeCheck.java) + * [PrimeFactorization](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Prime/PrimeFactorization.java) + * [SquareFreeInteger](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Prime/SquareFreeInteger.java) + * [PronicNumber](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/PronicNumber.java) + * [PythagoreanTriple](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/PythagoreanTriple.java) + * [QuadraticEquationSolver](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/QuadraticEquationSolver.java) + * [ReverseNumber](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/ReverseNumber.java) + * [RomanNumeralUtil](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/RomanNumeralUtil.java) + * [SecondMinMax](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/SecondMinMax.java) + * [SieveOfEratosthenes](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/SieveOfEratosthenes.java) + * [SimpsonIntegration](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/SimpsonIntegration.java) + * [SolovayStrassenPrimalityTest](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/SolovayStrassenPrimalityTest.java) + * [SquareRootWithBabylonianMethod](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/SquareRootWithBabylonianMethod.java) + * [SquareRootWithNewtonRaphsonMethod](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/SquareRootWithNewtonRaphsonMethod.java) + * [StandardDeviation](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/StandardDeviation.java) + * [StandardScore](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/StandardScore.java) + * [StrobogrammaticNumber](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/StrobogrammaticNumber.java) + * [SumOfArithmeticSeries](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/SumOfArithmeticSeries.java) + * [SumOfDigits](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/SumOfDigits.java) + * [SumOfOddNumbers](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/SumOfOddNumbers.java) + * [SumWithoutArithmeticOperators](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/SumWithoutArithmeticOperators.java) + * [TrinomialTriangle](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/TrinomialTriangle.java) + * [TwinPrime](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/TwinPrime.java) + * [UniformNumbers](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/UniformNumbers.java) + * [VampireNumber](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/VampireNumber.java) + * [VectorCrossProduct](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/VectorCrossProduct.java) + * [Volume](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Volume.java) + * matrix + * [InverseOfMatrix](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/matrix/InverseOfMatrix.java) + * matrixexponentiation + * [Fibonacci](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/matrix/matrixexponentiation/Fibonacci.java) + * [MatrixRank](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/matrix/MatrixRank.java) + * [MatrixTranspose](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/matrix/MatrixTranspose.java) + * [MedianOfMatrix](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/matrix/MedianOfMatrix.java) + * [MirrorOfMatrix](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/matrix/MirrorOfMatrix.java) + * [PrintAMatrixInSpiralOrder](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/matrix/PrintAMatrixInSpiralOrder.java) + * [RotateMatrixBy90Degrees](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/matrix/RotateMatrixBy90Degrees.java) + * utils + * [MatrixUtil](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/matrix/utils/MatrixUtil.java) + * misc + * [ColorContrastRatio](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/misc/ColorContrastRatio.java) + * [MapReduce](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/misc/MapReduce.java) + * [MedianOfRunningArray](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/misc/MedianOfRunningArray.java) + * [MedianOfRunningArrayByte](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/misc/MedianOfRunningArrayByte.java) + * [MedianOfRunningArrayDouble](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/misc/MedianOfRunningArrayDouble.java) + * [MedianOfRunningArrayFloat](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/misc/MedianOfRunningArrayFloat.java) + * [MedianOfRunningArrayInteger](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/misc/MedianOfRunningArrayInteger.java) + * [MedianOfRunningArrayLong](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/misc/MedianOfRunningArrayLong.java) + * [PalindromePrime](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/misc/PalindromePrime.java) + * [PalindromeSinglyLinkedList](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/misc/PalindromeSinglyLinkedList.java) + * [RangeInSortedArray](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/misc/RangeInSortedArray.java) + * [ShuffleArray](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/misc/ShuffleArray.java) + * [Sparsity](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/misc/Sparsity.java) + * [ThreeSumProblem](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/misc/ThreeSumProblem.java) + * [TwoSumProblem](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/misc/TwoSumProblem.java) + * others + * [ArrayLeftRotation](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/ArrayLeftRotation.java) + * [ArrayRightRotation](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/ArrayRightRotation.java) + * [BankersAlgorithm](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/BankersAlgorithm.java) + * [BFPRT](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/BFPRT.java) + * [BoyerMoore](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/BoyerMoore.java) + * [BrianKernighanAlgorithm](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/BrianKernighanAlgorithm.java) + * cn + * [HammingDistance](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/cn/HammingDistance.java) + * [Conway](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/Conway.java) + * [CRC16](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/CRC16.java) + * [CRC32](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/CRC32.java) + * [CRCAlgorithm](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/CRCAlgorithm.java) + * [Damm](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/Damm.java) + * [Dijkstra](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/Dijkstra.java) + * [FloydTriangle](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/FloydTriangle.java) + * [GaussLegendre](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/GaussLegendre.java) + * [HappyNumbersSeq](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/HappyNumbersSeq.java) + * [Huffman](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/Huffman.java) + * [Implementing auto completing features using trie](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/Implementing_auto_completing_features_using_trie.java) + * [InsertDeleteInArray](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/InsertDeleteInArray.java) + * [KochSnowflake](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/KochSnowflake.java) + * [Krishnamurthy](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/Krishnamurthy.java) + * [LinearCongruentialGenerator](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/LinearCongruentialGenerator.java) + * [LineSweep](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/LineSweep.java) + * [LowestBasePalindrome](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/LowestBasePalindrome.java) + * [Luhn](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/Luhn.java) + * [Mandelbrot](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/Mandelbrot.java) + * [MaximumSlidingWindow](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/MaximumSlidingWindow.java) + * [MaximumSumOfDistinctSubarraysWithLengthK](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthK.java) + * [MemoryManagementAlgorithms](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/MemoryManagementAlgorithms.java) + * [MiniMaxAlgorithm](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/MiniMaxAlgorithm.java) + * [PageRank](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/PageRank.java) + * [PasswordGen](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/PasswordGen.java) + * [PerlinNoise](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/PerlinNoise.java) + * [PrintAMatrixInSpiralOrder](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/PrintAMatrixInSpiralOrder.java) + * [QueueUsingTwoStacks](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/QueueUsingTwoStacks.java) + * [RemoveDuplicateFromString](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/RemoveDuplicateFromString.java) + * [ReverseStackUsingRecursion](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/ReverseStackUsingRecursion.java) + * [SkylineProblem](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/SkylineProblem.java) + * [TwoPointers](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/TwoPointers.java) + * [Verhoeff](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/others/Verhoeff.java) + * puzzlesandgames + * [Sudoku](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/puzzlesandgames/Sudoku.java) + * [TowerOfHanoi](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/puzzlesandgames/TowerOfHanoi.java) + * [WordBoggle](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/puzzlesandgames/WordBoggle.java) + * recursion + * [FibonacciSeries](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/recursion/FibonacciSeries.java) + * [GenerateSubsets](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/recursion/GenerateSubsets.java) + * scheduling + * [AgingScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/AgingScheduling.java) + * diskscheduling + * [CircularLookScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/diskscheduling/CircularLookScheduling.java) + * [CircularScanScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/diskscheduling/CircularScanScheduling.java) + * [LookScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/diskscheduling/LookScheduling.java) + * [ScanScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/diskscheduling/ScanScheduling.java) + * [SSFScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/diskscheduling/SSFScheduling.java) + * [EDFScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/EDFScheduling.java) + * [FairShareScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/FairShareScheduling.java) + * [FCFSScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/FCFSScheduling.java) + * [GangScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/GangScheduling.java) + * [HighestResponseRatioNextScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/HighestResponseRatioNextScheduling.java) + * [JobSchedulingWithDeadline](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/JobSchedulingWithDeadline.java) + * [LotteryScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/LotteryScheduling.java) + * [MLFQScheduler](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/MLFQScheduler.java) + * [MultiAgentScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/MultiAgentScheduling.java) + * [NonPreemptivePriorityScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/NonPreemptivePriorityScheduling.java) + * [PreemptivePriorityScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/PreemptivePriorityScheduling.java) + * [ProportionalFairScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/ProportionalFairScheduling.java) + * [RandomScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/RandomScheduling.java) + * [RRScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/RRScheduling.java) + * [SelfAdjustingScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/SelfAdjustingScheduling.java) + * [SJFScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/SJFScheduling.java) + * [SlackTimeScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/SlackTimeScheduling.java) + * [SRTFScheduling](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/scheduling/SRTFScheduling.java) + * searches + * [BinarySearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/BinarySearch.java) + * [BinarySearch2dArray](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/BinarySearch2dArray.java) + * [BM25InvertedIndex](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/BM25InvertedIndex.java) + * [BreadthFirstSearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/BreadthFirstSearch.java) + * [DepthFirstSearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/DepthFirstSearch.java) + * [ExponentalSearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/ExponentalSearch.java) + * [FibonacciSearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/FibonacciSearch.java) + * [HowManyTimesRotated](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/HowManyTimesRotated.java) + * [InterpolationSearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/InterpolationSearch.java) + * [IterativeBinarySearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/IterativeBinarySearch.java) + * [IterativeTernarySearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/IterativeTernarySearch.java) + * [JumpSearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/JumpSearch.java) + * [KMPSearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/KMPSearch.java) + * [LinearSearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/LinearSearch.java) + * [LinearSearchThread](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/LinearSearchThread.java) + * [LowerBound](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/LowerBound.java) + * [MonteCarloTreeSearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/MonteCarloTreeSearch.java) + * [OrderAgnosticBinarySearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/OrderAgnosticBinarySearch.java) + * [PerfectBinarySearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/PerfectBinarySearch.java) + * [QuickSelect](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/QuickSelect.java) + * [RabinKarpAlgorithm](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/RabinKarpAlgorithm.java) + * [RandomSearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/RandomSearch.java) + * [RecursiveBinarySearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/RecursiveBinarySearch.java) + * [RowColumnWiseSorted2dArrayBinarySearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/RowColumnWiseSorted2dArrayBinarySearch.java) + * [SaddlebackSearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/SaddlebackSearch.java) + * [SearchInARowAndColWiseSortedMatrix](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/SearchInARowAndColWiseSortedMatrix.java) + * [SortOrderAgnosticBinarySearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearch.java) + * [SquareRootBinarySearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/SquareRootBinarySearch.java) + * [TernarySearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/TernarySearch.java) + * [UnionFind](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/UnionFind.java) + * [UpperBound](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/searches/UpperBound.java) + * slidingwindow + * [LongestSubarrayWithSumLessOrEqualToK](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/slidingwindow/LongestSubarrayWithSumLessOrEqualToK.java) + * [LongestSubstringWithoutRepeatingCharacters](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/slidingwindow/LongestSubstringWithoutRepeatingCharacters.java) + * [MaxSumKSizeSubarray](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/slidingwindow/MaxSumKSizeSubarray.java) + * [MinSumKSizeSubarray](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/slidingwindow/MinSumKSizeSubarray.java) + * sorts + * [AdaptiveMergeSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/AdaptiveMergeSort.java) + * [BeadSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/BeadSort.java) + * [BinaryInsertionSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/BinaryInsertionSort.java) + * [BitonicSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/BitonicSort.java) + * [BogoSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/BogoSort.java) + * [BubbleSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/BubbleSort.java) + * [BubbleSortRecursive](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/BubbleSortRecursive.java) + * [BucketSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/BucketSort.java) + * [CircleSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/CircleSort.java) + * [CocktailShakerSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/CocktailShakerSort.java) + * [CombSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/CombSort.java) + * [CountingSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/CountingSort.java) + * [CycleSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/CycleSort.java) + * [DarkSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/DarkSort.java) + * [DualPivotQuickSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/DualPivotQuickSort.java) + * [DutchNationalFlagSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/DutchNationalFlagSort.java) + * [ExchangeSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/ExchangeSort.java) + * [FlashSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/FlashSort.java) + * [GnomeSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/GnomeSort.java) + * [HeapSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/HeapSort.java) + * [InsertionSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/InsertionSort.java) + * [IntrospectiveSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/IntrospectiveSort.java) + * [LinkListSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/LinkListSort.java) + * [MergeSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/MergeSort.java) + * [MergeSortNoExtraSpace](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/MergeSortNoExtraSpace.java) + * [MergeSortRecursive](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/MergeSortRecursive.java) + * [OddEvenSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/OddEvenSort.java) + * [PancakeSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/PancakeSort.java) + * [PatienceSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/PatienceSort.java) + * [PigeonholeSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/PigeonholeSort.java) + * [QuickSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/QuickSort.java) + * [RadixSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/RadixSort.java) + * [SelectionSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/SelectionSort.java) + * [SelectionSortRecursive](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/SelectionSortRecursive.java) + * [ShellSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/ShellSort.java) + * [SimpleSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/SimpleSort.java) + * [SlowSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/SlowSort.java) + * [SortAlgorithm](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/SortAlgorithm.java) + * [SortUtils](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/SortUtils.java) + * [SortUtilsRandomGenerator](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/SortUtilsRandomGenerator.java) + * [SpreadSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/SpreadSort.java) + * [StalinSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/StalinSort.java) + * [StoogeSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/StoogeSort.java) + * [StrandSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/StrandSort.java) + * [SwapSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/SwapSort.java) + * [TimSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/TimSort.java) + * [TopologicalSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/TopologicalSort.java) + * [TreeSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/TreeSort.java) + * [WaveSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/WaveSort.java) + * [WiggleSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/WiggleSort.java) + * stacks + * [BalancedBrackets](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/BalancedBrackets.java) + * [CelebrityFinder](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/CelebrityFinder.java) + * [DecimalToAnyUsingStack](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/DecimalToAnyUsingStack.java) + * [DuplicateBrackets](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/DuplicateBrackets.java) + * [GreatestElementConstantTime](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/GreatestElementConstantTime.java) + * [InfixToPostfix](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/InfixToPostfix.java) + * [InfixToPrefix](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/InfixToPrefix.java) + * [LargestRectangle](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/LargestRectangle.java) + * [MaximumMinimumWindow](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/MaximumMinimumWindow.java) + * [MinStackUsingSingleStack](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/MinStackUsingSingleStack.java) + * [MinStackUsingTwoStacks](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/MinStackUsingTwoStacks.java) + * [NextGreaterElement](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/NextGreaterElement.java) + * [NextSmallerElement](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/NextSmallerElement.java) + * [PalindromeWithStack](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/PalindromeWithStack.java) + * [PostfixEvaluator](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/PostfixEvaluator.java) + * [PostfixToInfix](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/PostfixToInfix.java) + * [PrefixEvaluator](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/PrefixEvaluator.java) + * [PrefixToInfix](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/PrefixToInfix.java) + * [SmallestElementConstantTime](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/SmallestElementConstantTime.java) + * [SortStack](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/SortStack.java) + * [StackPostfixNotation](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/StackPostfixNotation.java) + * [StackUsingTwoQueues](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/stacks/StackUsingTwoQueues.java) + * strings + * [AhoCorasick](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/AhoCorasick.java) + * [Alphabetical](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/Alphabetical.java) + * [Anagrams](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/Anagrams.java) + * [CharactersSame](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/CharactersSame.java) + * [CheckAnagrams](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/CheckAnagrams.java) + * [CheckVowels](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/CheckVowels.java) + * [CountChar](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/CountChar.java) + * [CountWords](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/CountWords.java) + * [HammingDistance](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/HammingDistance.java) + * [HorspoolSearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/HorspoolSearch.java) + * [Isomorphic](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/Isomorphic.java) + * [KMP](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/KMP.java) + * [LetterCombinationsOfPhoneNumber](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/LetterCombinationsOfPhoneNumber.java) + * [LongestCommonPrefix](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/LongestCommonPrefix.java) + * [LongestNonRepetitiveSubstring](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/LongestNonRepetitiveSubstring.java) + * [LongestPalindromicSubstring](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/LongestPalindromicSubstring.java) + * [Lower](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/Lower.java) + * [Manacher](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/Manacher.java) + * [MyAtoi](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/MyAtoi.java) + * [Palindrome](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/Palindrome.java) + * [Pangram](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/Pangram.java) + * [PermuteString](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/PermuteString.java) + * [RabinKarp](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/RabinKarp.java) + * [ReturnSubsequence](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/ReturnSubsequence.java) + * [ReverseString](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/ReverseString.java) + * [ReverseStringRecursive](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/ReverseStringRecursive.java) + * [ReverseWordsInString](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/ReverseWordsInString.java) + * [Rotation](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/Rotation.java) + * [StringCompression](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/StringCompression.java) + * [StringMatchFiniteAutomata](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/StringMatchFiniteAutomata.java) + * [Upper](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/Upper.java) + * [ValidParentheses](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/ValidParentheses.java) + * [WordLadder](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/WordLadder.java) + * zigZagPattern + * [ZigZagPattern](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/strings/zigZagPattern/ZigZagPattern.java) + * tree + * [HeavyLightDecomposition](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java) + * test + * java + * com + * thealgorithms + * audiofilters + * [EMAFilterTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/audiofilters/EMAFilterTest.java) + * [IIRFilterTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/audiofilters/IIRFilterTest.java) + * backtracking + * [AllPathsFromSourceToTargetTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/AllPathsFromSourceToTargetTest.java) + * [ArrayCombinationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/ArrayCombinationTest.java) + * [CombinationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/CombinationTest.java) + * [CrosswordSolverTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/CrosswordSolverTest.java) + * [FloodFillTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/FloodFillTest.java) + * [KnightsTourTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/KnightsTourTest.java) + * [MazeRecursionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/MazeRecursionTest.java) + * [MColoringTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/MColoringTest.java) + * [NQueensTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/NQueensTest.java) + * [ParenthesesGeneratorTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/ParenthesesGeneratorTest.java) + * [PermutationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/PermutationTest.java) + * [PowerSumTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/PowerSumTest.java) + * [SubsequenceFinderTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/SubsequenceFinderTest.java) + * [WordPatternMatcherTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/WordPatternMatcherTest.java) + * [WordSearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/WordSearchTest.java) + * bitmanipulation + * [BcdConversionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/BcdConversionTest.java) + * [BinaryPalindromeCheckTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/BinaryPalindromeCheckTest.java) + * [BitSwapTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/BitSwapTest.java) + * [BooleanAlgebraGatesTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/BooleanAlgebraGatesTest.java) + * [ClearLeftmostSetBitTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/ClearLeftmostSetBitTest.java) + * [CountLeadingZerosTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/CountLeadingZerosTest.java) + * [CountSetBitsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/CountSetBitsTest.java) + * [FindNthBitTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/FindNthBitTest.java) + * [FirstDifferentBitTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/FirstDifferentBitTest.java) + * [GenerateSubsetsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/GenerateSubsetsTest.java) + * [GrayCodeConversionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/GrayCodeConversionTest.java) + * [HammingDistanceTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/HammingDistanceTest.java) + * [HigherLowerPowerOfTwoTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/HigherLowerPowerOfTwoTest.java) + * [HighestSetBitTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/HighestSetBitTest.java) + * [IndexOfRightMostSetBitTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/IndexOfRightMostSetBitTest.java) + * [IsEvenTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/IsEvenTest.java) + * [IsPowerTwoTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/IsPowerTwoTest.java) + * [LowestSetBitTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/LowestSetBitTest.java) + * [ModuloPowerOfTwoTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/ModuloPowerOfTwoTest.java) + * [NextHigherSameBitCountTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/NextHigherSameBitCountTest.java) + * [NonRepeatingNumberFinderTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/NonRepeatingNumberFinderTest.java) + * [NumberAppearingOddTimesTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/NumberAppearingOddTimesTest.java) + * [NumbersDifferentSignsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/NumbersDifferentSignsTest.java) + * [OneBitDifferenceTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/OneBitDifferenceTest.java) + * [OnesComplementTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/OnesComplementTest.java) + * [ParityCheckTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/ParityCheckTest.java) + * [ReverseBitsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/ReverseBitsTest.java) + * [SingleBitOperationsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/SingleBitOperationsTest.java) + * [SingleElementTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/SingleElementTest.java) + * [SwapAdjacentBitsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/SwapAdjacentBitsTest.java) + * [TwosComplementTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/TwosComplementTest.java) + * [Xs3ConversionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/Xs3ConversionTest.java) + * ciphers + * a5 + * [A5CipherTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/a5/A5CipherTest.java) + * [A5KeyStreamGeneratorTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/a5/A5KeyStreamGeneratorTest.java) + * [LFSRTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/a5/LFSRTest.java) + * [ADFGVXCipherTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/ADFGVXCipherTest.java) + * [AESEncryptionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/AESEncryptionTest.java) + * [AffineCipherTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/AffineCipherTest.java) + * [AtbashTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/AtbashTest.java) + * [AutokeyTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/AutokeyTest.java) + * [BaconianCipherTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/BaconianCipherTest.java) + * [BlowfishTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/BlowfishTest.java) + * [CaesarTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/CaesarTest.java) + * [ColumnarTranspositionCipherTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/ColumnarTranspositionCipherTest.java) + * [DESTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/DESTest.java) + * [DiffieHellmanTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/DiffieHellmanTest.java) + * [ECCTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/ECCTest.java) + * [HillCipherTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/HillCipherTest.java) + * [MonoAlphabeticTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/MonoAlphabeticTest.java) + * [PlayfairTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/PlayfairTest.java) + * [PolybiusTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/PolybiusTest.java) + * [RailFenceTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/RailFenceTest.java) + * [RSATest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/RSATest.java) + * [SimpleSubCipherTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/SimpleSubCipherTest.java) + * [VigenereTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/VigenereTest.java) + * [XORCipherTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/XORCipherTest.java) + * conversions + * [AffineConverterTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/AffineConverterTest.java) + * [AnyBaseToDecimalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/AnyBaseToDecimalTest.java) + * [AnytoAnyTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/AnytoAnyTest.java) + * [BinaryToDecimalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/BinaryToDecimalTest.java) + * [BinaryToHexadecimalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/BinaryToHexadecimalTest.java) + * [BinaryToOctalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/BinaryToOctalTest.java) + * [DecimalToAnyBaseTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/DecimalToAnyBaseTest.java) + * [DecimalToBinaryTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/DecimalToBinaryTest.java) + * [DecimalToHexadecimalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/DecimalToHexadecimalTest.java) + * [DecimalToOctalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/DecimalToOctalTest.java) + * [EndianConverterTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/EndianConverterTest.java) + * [HexaDecimalToBinaryTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/HexaDecimalToBinaryTest.java) + * [HexaDecimalToDecimalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/HexaDecimalToDecimalTest.java) + * [HexToOctTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/HexToOctTest.java) + * [IntegerToEnglishTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/IntegerToEnglishTest.java) + * [IntegerToRomanTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/IntegerToRomanTest.java) + * [IPConverterTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/IPConverterTest.java) + * [IPv6ConverterTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/IPv6ConverterTest.java) + * [MorseCodeConverterTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/MorseCodeConverterTest.java) + * [NumberToWordsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/NumberToWordsTest.java) + * [OctalToBinaryTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/OctalToBinaryTest.java) + * [OctalToDecimalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/OctalToDecimalTest.java) + * [OctalToHexadecimalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/OctalToHexadecimalTest.java) + * [PhoneticAlphabetConverterTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/PhoneticAlphabetConverterTest.java) + * [RomanToIntegerTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/RomanToIntegerTest.java) + * [TurkishToLatinConversionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/TurkishToLatinConversionTest.java) + * [UnitConversionsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/UnitConversionsTest.java) + * [UnitsConverterTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/UnitsConverterTest.java) + * [WordsToNumberTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/conversions/WordsToNumberTest.java) + * datastructures + * bag + * [BagTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/bag/BagTest.java) + * bloomfilter + * [BloomFilterTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/bloomfilter/BloomFilterTest.java) + * buffers + * [CircularBufferTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/buffers/CircularBufferTest.java) + * caches + * [LFUCacheTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/caches/LFUCacheTest.java) + * [LRUCacheTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/caches/LRUCacheTest.java) + * [MRUCacheTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/caches/MRUCacheTest.java) + * crdt + * [GCounterTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/crdt/GCounterTest.java) + * [GSetTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/crdt/GSetTest.java) + * [LWWElementSetTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/crdt/LWWElementSetTest.java) + * [ORSetTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/crdt/ORSetTest.java) + * [PNCounterTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/crdt/PNCounterTest.java) + * [TwoPSetTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/crdt/TwoPSetTest.java) + * disjointsetunion + * [DisjointSetUnionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/disjointsetunion/DisjointSetUnionTest.java) + * dynamicarray + * [DynamicArrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/dynamicarray/DynamicArrayTest.java) + * graphs + * [AStarTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/AStarTest.java) + * [BipartiteGraphDFSTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/BipartiteGraphDFSTest.java) + * [BoruvkaAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/BoruvkaAlgorithmTest.java) + * [DijkstraAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/DijkstraAlgorithmTest.java) + * [DijkstraOptimizedAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/DijkstraOptimizedAlgorithmTest.java) + * [EdmondsBlossomAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/EdmondsBlossomAlgorithmTest.java) + * [FloydWarshallTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/FloydWarshallTest.java) + * [FordFulkersonTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/FordFulkersonTest.java) + * [HamiltonianCycleTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/HamiltonianCycleTest.java) + * [JohnsonsAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/JohnsonsAlgorithmTest.java) + * [KahnsAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithmTest.java) + * [KosarajuTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/KosarajuTest.java) + * [KruskalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/KruskalTest.java) + * [MatrixGraphsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/MatrixGraphsTest.java) + * [PrimMSTTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/PrimMSTTest.java) + * [TarjansAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/TarjansAlgorithmTest.java) + * [WelshPowellTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/WelshPowellTest.java) + * hashmap + * hashing + * [GenericHashMapUsingArrayListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayListTest.java) + * [GenericHashMapUsingArrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayTest.java) + * [HashMapCuckooHashingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapCuckooHashingTest.java) + * [HashMapTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapTest.java) + * [IntersectionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/IntersectionTest.java) + * [LinearProbingHashMapTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/LinearProbingHashMapTest.java) + * [MajorityElementTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/MajorityElementTest.java) + * [MapTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/MapTest.java) + * heaps + * [FibonacciHeapTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/FibonacciHeapTest.java) + * [GenericHeapTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/GenericHeapTest.java) + * [HeapElementTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/HeapElementTest.java) + * [KthElementFinderTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/KthElementFinderTest.java) + * [LeftistHeapTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/LeftistHeapTest.java) + * [MaxHeapTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/MaxHeapTest.java) + * [MedianFinderTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/MedianFinderTest.java) + * [MergeKSortedArraysTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/MergeKSortedArraysTest.java) + * [MinHeapTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/MinHeapTest.java) + * [MinPriorityQueueTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/MinPriorityQueueTest.java) + * lists + * [CircleLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/CircleLinkedListTest.java) + * [CountSinglyLinkedListRecursionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/CountSinglyLinkedListRecursionTest.java) + * [CreateAndDetectLoopTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/CreateAndDetectLoopTest.java) + * [CursorLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/CursorLinkedListTest.java) + * [MergeKSortedLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/MergeKSortedLinkedListTest.java) + * [MergeSortedArrayListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/MergeSortedArrayListTest.java) + * [MergeSortedSinglyLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/MergeSortedSinglyLinkedListTest.java) + * [QuickSortLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/QuickSortLinkedListTest.java) + * [ReverseKGroupTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/ReverseKGroupTest.java) + * [RotateSinglyLinkedListsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/RotateSinglyLinkedListsTest.java) + * [SearchSinglyLinkedListRecursionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/SearchSinglyLinkedListRecursionTest.java) + * [SinglyLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/SinglyLinkedListTest.java) + * [SkipListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/SkipListTest.java) + * [SortedLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/lists/SortedLinkedListTest.java) + * queues + * [CircularQueueTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/queues/CircularQueueTest.java) + * [DequeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/queues/DequeTest.java) + * [GenericArrayListQueueTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/queues/GenericArrayListQueueTest.java) + * [LinkedQueueTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/queues/LinkedQueueTest.java) + * [PriorityQueuesTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/queues/PriorityQueuesTest.java) + * [QueueByTwoStacksTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/queues/QueueByTwoStacksTest.java) + * [QueueTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/queues/QueueTest.java) + * [SlidingWindowMaximumTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/queues/SlidingWindowMaximumTest.java) + * [TokenBucketTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/queues/TokenBucketTest.java) + * stacks + * [NodeStackTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/stacks/NodeStackTest.java) + * [ReverseStackTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/stacks/ReverseStackTest.java) + * [StackArrayListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/stacks/StackArrayListTest.java) + * [StackArrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/stacks/StackArrayTest.java) + * [StackOfLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/stacks/StackOfLinkedListTest.java) + * trees + * [AVLTreeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/AVLTreeTest.java) + * [BinaryTreeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/BinaryTreeTest.java) + * [BoundaryTraversalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/BoundaryTraversalTest.java) + * [BSTFromSortedArrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/BSTFromSortedArrayTest.java) + * [BSTIterativeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/BSTIterativeTest.java) + * [BSTRecursiveTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/BSTRecursiveTest.java) + * [CeilInBinarySearchTreeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/CeilInBinarySearchTreeTest.java) + * [CheckBinaryTreeIsValidBSTTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/CheckBinaryTreeIsValidBSTTest.java) + * [CheckIfBinaryTreeBalancedTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/CheckIfBinaryTreeBalancedTest.java) + * [CheckTreeIsSymmetricTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/CheckTreeIsSymmetricTest.java) + * [CreateBinaryTreeFromInorderPreorderTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/CreateBinaryTreeFromInorderPreorderTest.java) + * [InorderTraversalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/InorderTraversalTest.java) + * [KDTreeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/KDTreeTest.java) + * [LazySegmentTreeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/LazySegmentTreeTest.java) + * [LevelOrderTraversalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/LevelOrderTraversalTest.java) + * [PostOrderTraversalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/PostOrderTraversalTest.java) + * [PreOrderTraversalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/PreOrderTraversalTest.java) + * [QuadTreeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/QuadTreeTest.java) + * [SameTreesCheckTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/SameTreesCheckTest.java) + * [SplayTreeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/SplayTreeTest.java) + * [TreapTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/TreapTest.java) + * [TreeTestUtils](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/TreeTestUtils.java) + * [TrieTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/TrieTest.java) + * [VerticalOrderTraversalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/VerticalOrderTraversalTest.java) + * [ZigzagTraversalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/ZigzagTraversalTest.java) + * divideandconquer + * [BinaryExponentiationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/divideandconquer/BinaryExponentiationTest.java) + * [ClosestPairTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/divideandconquer/ClosestPairTest.java) + * [CountingInversionsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/divideandconquer/CountingInversionsTest.java) + * [MedianOfTwoSortedArraysTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/divideandconquer/MedianOfTwoSortedArraysTest.java) + * [SkylineAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/divideandconquer/SkylineAlgorithmTest.java) + * [StrassenMatrixMultiplicationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/divideandconquer/StrassenMatrixMultiplicationTest.java) + * [TilingProblemTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/divideandconquer/TilingProblemTest.java) + * dynamicprogramming + * [AbbreviationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/AbbreviationTest.java) + * [AllConstructTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/AllConstructTest.java) + * [AssignmentUsingBitmaskTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/AssignmentUsingBitmaskTest.java) + * [BoardPathTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/BoardPathTest.java) + * [BoundaryFillTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/BoundaryFillTest.java) + * [BruteForceKnapsackTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/BruteForceKnapsackTest.java) + * [CatalanNumberTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/CatalanNumberTest.java) + * [ClimbStairsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/ClimbStairsTest.java) + * [CoinChangeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/CoinChangeTest.java) + * [CountFriendsPairingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/CountFriendsPairingTest.java) + * [DPTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/DPTest.java) + * [EditDistanceTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/EditDistanceTest.java) + * [EggDroppingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/EggDroppingTest.java) + * [FibonacciTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/FibonacciTest.java) + * [KadaneAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/KadaneAlgorithmTest.java) + * [KnapsackMemoizationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/KnapsackMemoizationTest.java) + * [KnapsackTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/KnapsackTest.java) + * [LevenshteinDistanceTests](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LevenshteinDistanceTests.java) + * [LongestAlternatingSubsequenceTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LongestAlternatingSubsequenceTest.java) + * [LongestArithmeticSubsequenceTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LongestArithmeticSubsequenceTest.java) + * [LongestCommonSubsequenceTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequenceTest.java) + * [LongestIncreasingSubsequenceTests](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LongestIncreasingSubsequenceTests.java) + * [LongestPalindromicSubstringTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LongestPalindromicSubstringTest.java) + * [LongestValidParenthesesTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/LongestValidParenthesesTest.java) + * [MatrixChainMultiplicationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/MatrixChainMultiplicationTest.java) + * [MatrixChainRecursiveTopDownMemoisationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/MatrixChainRecursiveTopDownMemoisationTest.java) + * [MaximumSumOfNonAdjacentElementsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/MaximumSumOfNonAdjacentElementsTest.java) + * [MinimumPathSumTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/MinimumPathSumTest.java) + * [MinimumSumPartitionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/MinimumSumPartitionTest.java) + * [NewManShanksPrimeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/NewManShanksPrimeTest.java) + * [OptimalJobSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/OptimalJobSchedulingTest.java) + * [PalindromicPartitioningTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/PalindromicPartitioningTest.java) + * [PartitionProblemTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/PartitionProblemTest.java) + * [RegexMatchingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/RegexMatchingTest.java) + * [RodCuttingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/RodCuttingTest.java) + * [ShortestCommonSupersequenceLengthTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/ShortestCommonSupersequenceLengthTest.java) + * [SubsetCountTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/SubsetCountTest.java) + * [SubsetSumSpaceOptimizedTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/SubsetSumSpaceOptimizedTest.java) + * [SubsetSumTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/SubsetSumTest.java) + * [SumOfSubsetTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/SumOfSubsetTest.java) + * [TreeMatchingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/TreeMatchingTest.java) + * [TribonacciTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/TribonacciTest.java) + * [UniquePathsTests](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/UniquePathsTests.java) + * [UniqueSubsequencesCountTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/UniqueSubsequencesCountTest.java) + * [WildcardMatchingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/WildcardMatchingTest.java) + * [WineProblemTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/dynamicprogramming/WineProblemTest.java) + * geometry + * [BresenhamLineTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/geometry/BresenhamLineTest.java) + * [ConvexHullTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/geometry/ConvexHullTest.java) + * [GrahamScanTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/geometry/GrahamScanTest.java) + * [MidpointCircleTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/geometry/MidpointCircleTest.java) + * [MidpointEllipseTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/geometry/MidpointEllipseTest.java) + * graph + * [ConstrainedShortestPathTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/graph/ConstrainedShortestPathTest.java) + * [StronglyConnectedComponentOptimizedTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/graph/StronglyConnectedComponentOptimizedTest.java) + * greedyalgorithms + * [ActivitySelectionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/greedyalgorithms/ActivitySelectionTest.java) + * [BandwidthAllocationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/greedyalgorithms/BandwidthAllocationTest.java) + * [BinaryAdditionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/greedyalgorithms/BinaryAdditionTest.java) + * [CoinChangeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/greedyalgorithms/CoinChangeTest.java) + * [DigitSeparationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/greedyalgorithms/DigitSeparationTest.java) + * [EgyptianFractionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/greedyalgorithms/EgyptianFractionTest.java) + * [FractionalKnapsackTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/greedyalgorithms/FractionalKnapsackTest.java) + * [GaleShapleyTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/greedyalgorithms/GaleShapleyTest.java) + * [JobSequencingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/greedyalgorithms/JobSequencingTest.java) + * [KCentersTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/greedyalgorithms/KCentersTest.java) + * [MergeIntervalsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/greedyalgorithms/MergeIntervalsTest.java) + * [MinimizingLatenessTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/greedyalgorithms/MinimizingLatenessTest.java) + * [MinimumWaitingTimeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/greedyalgorithms/MinimumWaitingTimeTest.java) + * [OptimalFileMergingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/greedyalgorithms/OptimalFileMergingTest.java) + * [StockProfitCalculatorTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/greedyalgorithms/StockProfitCalculatorTest.java) + * io + * [BufferedReaderTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/io/BufferedReaderTest.java) + * lineclipping + * [CohenSutherlandTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/lineclipping/CohenSutherlandTest.java) + * [LiangBarskyTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/lineclipping/LiangBarskyTest.java) + * maths + * [AbsoluteMaxTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/AbsoluteMaxTest.java) + * [AbsoluteMinTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/AbsoluteMinTest.java) + * [AbsoluteValueTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/AbsoluteValueTest.java) + * [ADTFractionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/ADTFractionTest.java) + * [AliquotSumTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/AliquotSumTest.java) + * [AmicableNumberTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/AmicableNumberTest.java) + * [AreaTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/AreaTest.java) + * [ArmstrongTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/ArmstrongTest.java) + * [AutoCorrelationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/AutoCorrelationTest.java) + * [AutomorphicNumberTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/AutomorphicNumberTest.java) + * [AverageTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/AverageTest.java) + * [BinaryPowTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/BinaryPowTest.java) + * [BinomialCoefficientTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/BinomialCoefficientTest.java) + * [CatalanNumbersTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/CatalanNumbersTest.java) + * [CeilTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/CeilTest.java) + * [ChineseRemainderTheoremTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/ChineseRemainderTheoremTest.java) + * [CollatzConjectureTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/CollatzConjectureTest.java) + * [CombinationsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/CombinationsTest.java) + * [ConvolutionFFTTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/ConvolutionFFTTest.java) + * [ConvolutionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/ConvolutionTest.java) + * [CrossCorrelationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/CrossCorrelationTest.java) + * [DeterminantOfMatrixTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/DeterminantOfMatrixTest.java) + * [DigitalRootTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/DigitalRootTest.java) + * [DistanceFormulaTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/DistanceFormulaTest.java) + * [DudeneyNumberTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/DudeneyNumberTest.java) + * [EulerMethodTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/EulerMethodTest.java) + * [EulersFunctionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/EulersFunctionTest.java) + * [FactorialRecursionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/FactorialRecursionTest.java) + * [FactorialTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/FactorialTest.java) + * [FastExponentiationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/FastExponentiationTest.java) + * [FastInverseSqrtTests](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/FastInverseSqrtTests.java) + * [FFTTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/FFTTest.java) + * [FibonacciJavaStreamsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/FibonacciJavaStreamsTest.java) + * [FibonacciLoopTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/FibonacciLoopTest.java) + * [FibonacciNumberCheckTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/FibonacciNumberCheckTest.java) + * [FibonacciNumberGoldenRationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/FibonacciNumberGoldenRationTest.java) + * [FindKthNumberTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/FindKthNumberTest.java) + * [FindMaxRecursionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/FindMaxRecursionTest.java) + * [FindMaxTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/FindMaxTest.java) + * [FindMinRecursionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/FindMinRecursionTest.java) + * [FindMinTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/FindMinTest.java) + * [FloorTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/FloorTest.java) + * [FrizzyNumberTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/FrizzyNumberTest.java) + * [GaussianTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/GaussianTest.java) + * [GCDRecursionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/GCDRecursionTest.java) + * [GCDTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/GCDTest.java) + * [GenericRootTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/GenericRootTest.java) + * [GoldbachConjectureTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/GoldbachConjectureTest.java) + * [HarshadNumberTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/HarshadNumberTest.java) + * [HeronsFormulaTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/HeronsFormulaTest.java) + * [JosephusProblemTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/JosephusProblemTest.java) + * [KaprekarNumbersTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/KaprekarNumbersTest.java) + * [KaratsubaMultiplicationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/KaratsubaMultiplicationTest.java) + * [KrishnamurthyNumberTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/KrishnamurthyNumberTest.java) + * [LeastCommonMultipleTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/LeastCommonMultipleTest.java) + * [LeonardoNumberTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/LeonardoNumberTest.java) + * [LongDivisionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/LongDivisionTest.java) + * [LucasSeriesTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/LucasSeriesTest.java) + * [MathBuilderTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/MathBuilderTest.java) + * [MaxValueTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/MaxValueTest.java) + * [MeansTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/MeansTest.java) + * [MedianTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/MedianTest.java) + * [MinValueTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/MinValueTest.java) + * [ModeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/ModeTest.java) + * [NonRepeatingElementTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/NonRepeatingElementTest.java) + * [NthUglyNumberTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/NthUglyNumberTest.java) + * [NumberOfDigitsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/NumberOfDigitsTest.java) + * [PalindromeNumberTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/PalindromeNumberTest.java) + * [ParseIntegerTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/ParseIntegerTest.java) + * [PascalTriangleTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/PascalTriangleTest.java) + * [PerfectCubeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/PerfectCubeTest.java) + * [PerfectNumberTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/PerfectNumberTest.java) + * [PerfectSquareTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/PerfectSquareTest.java) + * [PerimeterTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/PerimeterTest.java) + * [PollardRhoTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/PollardRhoTest.java) + * [PowerOfTwoOrNotTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/PowerOfTwoOrNotTest.java) + * [PowerUsingRecursionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/PowerUsingRecursionTest.java) + * [PowTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/PowTest.java) + * prime + * [LiouvilleLambdaFunctionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/prime/LiouvilleLambdaFunctionTest.java) + * [MillerRabinPrimalityCheckTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/prime/MillerRabinPrimalityCheckTest.java) + * [MobiusFunctionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/prime/MobiusFunctionTest.java) + * [PrimeCheckTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/prime/PrimeCheckTest.java) + * [PrimeFactorizationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/prime/PrimeFactorizationTest.java) + * [PronicNumberTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/PronicNumberTest.java) + * [PythagoreanTripleTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/PythagoreanTripleTest.java) + * [QuadraticEquationSolverTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/QuadraticEquationSolverTest.java) + * [ReverseNumberTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/ReverseNumberTest.java) + * [SecondMinMaxTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/SecondMinMaxTest.java) + * [SieveOfEratosthenesTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/SieveOfEratosthenesTest.java) + * [SolovayStrassenPrimalityTestTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/SolovayStrassenPrimalityTestTest.java) + * [SquareFreeIntegerTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/SquareFreeIntegerTest.java) + * [SquareRootwithBabylonianMethodTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/SquareRootwithBabylonianMethodTest.java) + * [SquareRootWithNewtonRaphsonTestMethod](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/SquareRootWithNewtonRaphsonTestMethod.java) + * [StandardDeviationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/StandardDeviationTest.java) + * [StandardScoreTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/StandardScoreTest.java) + * [StrobogrammaticNumberTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/StrobogrammaticNumberTest.java) + * [SumOfArithmeticSeriesTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/SumOfArithmeticSeriesTest.java) + * [SumOfDigitsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/SumOfDigitsTest.java) + * [SumOfOddNumbersTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/SumOfOddNumbersTest.java) + * [SumWithoutArithmeticOperatorsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/SumWithoutArithmeticOperatorsTest.java) + * [TestArmstrong](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/TestArmstrong.java) + * [TwinPrimeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/TwinPrimeTest.java) + * [UniformNumbersTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/UniformNumbersTest.java) + * [VampireNumberTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/VampireNumberTest.java) + * [VolumeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/VolumeTest.java) + * matrix + * [InverseOfMatrixTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/matrix/InverseOfMatrixTest.java) + * [MatrixRankTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/matrix/MatrixRankTest.java) + * [MatrixTransposeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/matrix/MatrixTransposeTest.java) + * [MatrixUtilTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/matrix/MatrixUtilTest.java) + * [MedianOfMatrixTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/matrix/MedianOfMatrixTest.java) + * [MirrorOfMatrixTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/matrix/MirrorOfMatrixTest.java) + * [TestPrintMatrixInSpiralOrder](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/matrix/TestPrintMatrixInSpiralOrder.java) + * misc + * [ColorContrastRatioTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/misc/ColorContrastRatioTest.java) + * [MapReduceTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/misc/MapReduceTest.java) + * [MedianOfRunningArrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/misc/MedianOfRunningArrayTest.java) + * [PalindromePrimeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/misc/PalindromePrimeTest.java) + * [PalindromeSinglyLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/misc/PalindromeSinglyLinkedListTest.java) + * [RangeInSortedArrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/misc/RangeInSortedArrayTest.java) + * [ShuffleArrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/misc/ShuffleArrayTest.java) + * [SparsityTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/misc/SparsityTest.java) + * [ThreeSumProblemTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/misc/ThreeSumProblemTest.java) + * [TwoSumProblemTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/misc/TwoSumProblemTest.java) + * others + * [ArrayLeftRotationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/ArrayLeftRotationTest.java) + * [ArrayRightRotationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/ArrayRightRotationTest.java) + * [BestFitCPUTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/BestFitCPUTest.java) + * [BFPRTTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/BFPRTTest.java) + * [BoyerMooreTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/BoyerMooreTest.java) + * cn + * [HammingDistanceTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/cn/HammingDistanceTest.java) + * [ConwayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/ConwayTest.java) + * [CountFriendsPairingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/CountFriendsPairingTest.java) + * [CRC16Test](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/CRC16Test.java) + * [CRCAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/CRCAlgorithmTest.java) + * [FirstFitCPUTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/FirstFitCPUTest.java) + * [FloydTriangleTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/FloydTriangleTest.java) + * [KadaneAlogrithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/KadaneAlogrithmTest.java) + * [LineSweepTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/LineSweepTest.java) + * [LinkListSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/LinkListSortTest.java) + * [LowestBasePalindromeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/LowestBasePalindromeTest.java) + * [MaximumSlidingWindowTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/MaximumSlidingWindowTest.java) + * [MaximumSumOfDistinctSubarraysWithLengthKTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java) + * [NewManShanksPrimeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/NewManShanksPrimeTest.java) + * [NextFitTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/NextFitTest.java) + * [PasswordGenTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/PasswordGenTest.java) + * [QueueUsingTwoStacksTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/QueueUsingTwoStacksTest.java) + * [RemoveDuplicateFromStringTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/RemoveDuplicateFromStringTest.java) + * [ReverseStackUsingRecursionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/ReverseStackUsingRecursionTest.java) + * [SkylineProblemTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/SkylineProblemTest.java) + * [TestPrintMatrixInSpiralOrder](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/TestPrintMatrixInSpiralOrder.java) + * [TwoPointersTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/TwoPointersTest.java) + * [WorstFitCPUTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/WorstFitCPUTest.java) + * puzzlesandgames + * [SudokuTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/puzzlesandgames/SudokuTest.java) + * [TowerOfHanoiTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/puzzlesandgames/TowerOfHanoiTest.java) + * [WordBoggleTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/puzzlesandgames/WordBoggleTest.java) + * recursion + * [FibonacciSeriesTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/recursion/FibonacciSeriesTest.java) + * [GenerateSubsetsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/recursion/GenerateSubsetsTest.java) + * scheduling + * [AgingSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/AgingSchedulingTest.java) + * diskscheduling + * [CircularLookSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/diskscheduling/CircularLookSchedulingTest.java) + * [CircularScanSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/diskscheduling/CircularScanSchedulingTest.java) + * [LookSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/diskscheduling/LookSchedulingTest.java) + * [ScanSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/diskscheduling/ScanSchedulingTest.java) + * [SSFSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/diskscheduling/SSFSchedulingTest.java) + * [EDFSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/EDFSchedulingTest.java) + * [FairShareSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/FairShareSchedulingTest.java) + * [FCFSSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/FCFSSchedulingTest.java) + * [GangSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/GangSchedulingTest.java) + * [HighestResponseRatioNextSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/HighestResponseRatioNextSchedulingTest.java) + * [JobSchedulingWithDeadlineTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/JobSchedulingWithDeadlineTest.java) + * [LotterySchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/LotterySchedulingTest.java) + * [MLFQSchedulerTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/MLFQSchedulerTest.java) + * [MultiAgentSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/MultiAgentSchedulingTest.java) + * [NonPreemptivePrioritySchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/NonPreemptivePrioritySchedulingTest.java) + * [PreemptivePrioritySchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/PreemptivePrioritySchedulingTest.java) + * [ProportionalFairSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/ProportionalFairSchedulingTest.java) + * [RandomSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/RandomSchedulingTest.java) + * [RRSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/RRSchedulingTest.java) + * [SelfAdjustingSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/SelfAdjustingSchedulingTest.java) + * [SJFSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/SJFSchedulingTest.java) + * [SlackTimeSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/SlackTimeSchedulingTest.java) + * [SRTFSchedulingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/scheduling/SRTFSchedulingTest.java) + * searches + * [BinarySearch2dArrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/BinarySearch2dArrayTest.java) + * [BinarySearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/BinarySearchTest.java) + * [BM25InvertedIndexTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/BM25InvertedIndexTest.java) + * [BreadthFirstSearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/BreadthFirstSearchTest.java) + * [DepthFirstSearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/DepthFirstSearchTest.java) + * [ExponentialSearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/ExponentialSearchTest.java) + * [FibonacciSearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/FibonacciSearchTest.java) + * [HowManyTimesRotatedTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/HowManyTimesRotatedTest.java) + * [InterpolationSearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/InterpolationSearchTest.java) + * [IterativeBinarySearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/IterativeBinarySearchTest.java) + * [IterativeTernarySearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/IterativeTernarySearchTest.java) + * [JumpSearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/JumpSearchTest.java) + * [KMPSearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/KMPSearchTest.java) + * [LinearSearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/LinearSearchTest.java) + * [LinearSearchThreadTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/LinearSearchThreadTest.java) + * [LowerBoundTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/LowerBoundTest.java) + * [MonteCarloTreeSearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/MonteCarloTreeSearchTest.java) + * [OrderAgnosticBinarySearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/OrderAgnosticBinarySearchTest.java) + * [PerfectBinarySearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/PerfectBinarySearchTest.java) + * [QuickSelectTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/QuickSelectTest.java) + * [RabinKarpAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/RabinKarpAlgorithmTest.java) + * [RandomSearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/RandomSearchTest.java) + * [RecursiveBinarySearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/RecursiveBinarySearchTest.java) + * [RowColumnWiseSorted2dArrayBinarySearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/RowColumnWiseSorted2dArrayBinarySearchTest.java) + * [SaddlebackSearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/SaddlebackSearchTest.java) + * [SearchInARowAndColWiseSortedMatrixTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/SearchInARowAndColWiseSortedMatrixTest.java) + * [SortOrderAgnosticBinarySearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearchTest.java) + * [SquareRootBinarySearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/SquareRootBinarySearchTest.java) + * [TernarySearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/TernarySearchTest.java) + * [TestSearchInARowAndColWiseSortedMatrix](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/TestSearchInARowAndColWiseSortedMatrix.java) + * [UnionFindTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/UnionFindTest.java) + * [UpperBoundTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/searches/UpperBoundTest.java) + * slidingwindow + * [LongestSubarrayWithSumLessOrEqualToKTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/slidingwindow/LongestSubarrayWithSumLessOrEqualToKTest.java) + * [LongestSubstringWithoutRepeatingCharactersTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/slidingwindow/LongestSubstringWithoutRepeatingCharactersTest.java) + * [MaxSumKSizeSubarrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/slidingwindow/MaxSumKSizeSubarrayTest.java) + * [MinSumKSizeSubarrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/slidingwindow/MinSumKSizeSubarrayTest.java) + * sorts + * [AdaptiveMergeSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/AdaptiveMergeSortTest.java) + * [BeadSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/BeadSortTest.java) + * [BinaryInsertionSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/BinaryInsertionSortTest.java) + * [BitonicSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/BitonicSortTest.java) + * [BogoSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/BogoSortTest.java) + * [BubbleSortRecursiveTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/BubbleSortRecursiveTest.java) + * [BubbleSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/BubbleSortTest.java) + * [BucketSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/BucketSortTest.java) + * [CircleSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/CircleSortTest.java) + * [CocktailShakerSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/CocktailShakerSortTest.java) + * [CombSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/CombSortTest.java) + * [CountingSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/CountingSortTest.java) + * [CycleSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/CycleSortTest.java) + * [DarkSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/DarkSortTest.java) + * [DualPivotQuickSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/DualPivotQuickSortTest.java) + * [DutchNationalFlagSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/DutchNationalFlagSortTest.java) + * [ExchangeSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/ExchangeSortTest.java) + * [FlashSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/FlashSortTest.java) + * [GnomeSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/GnomeSortTest.java) + * [HeapSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/HeapSortTest.java) + * [InsertionSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/InsertionSortTest.java) + * [IntrospectiveSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/IntrospectiveSortTest.java) + * [MergeSortNoExtraSpaceTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/MergeSortNoExtraSpaceTest.java) + * [MergeSortRecursiveTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/MergeSortRecursiveTest.java) + * [MergeSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/MergeSortTest.java) + * [OddEvenSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/OddEvenSortTest.java) + * [PancakeSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/PancakeSortTest.java) + * [PatienceSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/PatienceSortTest.java) + * [PigeonholeSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/PigeonholeSortTest.java) + * [QuickSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/QuickSortTest.java) + * [RadixSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/RadixSortTest.java) + * [SelectionSortRecursiveTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/SelectionSortRecursiveTest.java) + * [SelectionSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/SelectionSortTest.java) + * [ShellSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/ShellSortTest.java) + * [SimpleSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/SimpleSortTest.java) + * [SlowSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/SlowSortTest.java) + * [SortingAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/SortingAlgorithmTest.java) + * [SortUtilsRandomGeneratorTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/SortUtilsRandomGeneratorTest.java) + * [SortUtilsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/SortUtilsTest.java) + * [SpreadSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/SpreadSortTest.java) + * [StalinSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/StalinSortTest.java) + * [StoogeSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/StoogeSortTest.java) + * [StrandSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/StrandSortTest.java) + * [SwapSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/SwapSortTest.java) + * [TimSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/TimSortTest.java) + * [TopologicalSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/TopologicalSortTest.java) + * [TreeSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/TreeSortTest.java) + * [WaveSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/WaveSortTest.java) + * [WiggleSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/WiggleSortTest.java) + * stacks + * [BalancedBracketsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/stacks/BalancedBracketsTest.java) + * [CelebrityFinderTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/stacks/CelebrityFinderTest.java) + * [DecimalToAnyUsingStackTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/stacks/DecimalToAnyUsingStackTest.java) + * [DuplicateBracketsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/stacks/DuplicateBracketsTest.java) + * [GreatestElementConstantTimeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/stacks/GreatestElementConstantTimeTest.java) + * [InfixToPostfixTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/stacks/InfixToPostfixTest.java) + * [InfixToPrefixTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/stacks/InfixToPrefixTest.java) + * [LargestRectangleTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/stacks/LargestRectangleTest.java) + * [MinStackUsingSingleStackTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/stacks/MinStackUsingSingleStackTest.java) + * [MinStackUsingTwoStacksTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/stacks/MinStackUsingTwoStacksTest.java) + * [NextGreaterElementTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/stacks/NextGreaterElementTest.java) + * [NextSmallerElementTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/stacks/NextSmallerElementTest.java) + * [PalindromeWithStackTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/stacks/PalindromeWithStackTest.java) + * [PostfixEvaluatorTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/stacks/PostfixEvaluatorTest.java) + * [PostfixToInfixTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/stacks/PostfixToInfixTest.java) + * [PrefixEvaluatorTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/stacks/PrefixEvaluatorTest.java) + * [PrefixToInfixTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/stacks/PrefixToInfixTest.java) + * [SmallestElementConstantTimeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/stacks/SmallestElementConstantTimeTest.java) + * [SortStackTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/stacks/SortStackTest.java) + * [StackPostfixNotationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/stacks/StackPostfixNotationTest.java) + * [StackUsingTwoQueuesTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/stacks/StackUsingTwoQueuesTest.java) + * strings + * [AhoCorasickTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/AhoCorasickTest.java) + * [AlphabeticalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/AlphabeticalTest.java) + * [AnagramsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/AnagramsTest.java) + * [CharacterSameTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/CharacterSameTest.java) + * [CheckAnagramsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/CheckAnagramsTest.java) + * [CheckVowelsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/CheckVowelsTest.java) + * [CountCharTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/CountCharTest.java) + * [CountWordsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/CountWordsTest.java) + * [HammingDistanceTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/HammingDistanceTest.java) + * [HorspoolSearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/HorspoolSearchTest.java) + * [IsomorphicTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/IsomorphicTest.java) + * [LetterCombinationsOfPhoneNumberTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/LetterCombinationsOfPhoneNumberTest.java) + * [LongestCommonPrefixTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/LongestCommonPrefixTest.java) + * [LongestNonRepetitiveSubstringTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/LongestNonRepetitiveSubstringTest.java) + * [LongestPalindromicSubstringTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/LongestPalindromicSubstringTest.java) + * [LowerTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/LowerTest.java) + * [ManacherTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/ManacherTest.java) + * [MyAtoiTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/MyAtoiTest.java) + * [PalindromeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/PalindromeTest.java) + * [PangramTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/PangramTest.java) + * [PermuteStringTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/PermuteStringTest.java) + * [ReturnSubsequenceTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/ReturnSubsequenceTest.java) + * [ReverseStringRecursiveTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/ReverseStringRecursiveTest.java) + * [ReverseStringTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/ReverseStringTest.java) + * [ReverseWordsInStringTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/ReverseWordsInStringTest.java) + * [RotationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/RotationTest.java) + * [StringCompressionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/StringCompressionTest.java) + * [StringMatchFiniteAutomataTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/StringMatchFiniteAutomataTest.java) + * [UpperTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/UpperTest.java) + * [ValidParenthesesTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/ValidParenthesesTest.java) + * [WordLadderTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/WordLadderTest.java) + * zigZagPattern + * [ZigZagPatternTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/strings/zigZagPattern/ZigZagPatternTest.java) + * tree + * [HeavyLightDecompositionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java) diff --git a/DataStructures/Bags/Bag.java b/DataStructures/Bags/Bag.java deleted file mode 100644 index 2fd17ed65b04..000000000000 --- a/DataStructures/Bags/Bag.java +++ /dev/null @@ -1,126 +0,0 @@ -package DataStructures.Bags; - -import java.util.Iterator; -import java.util.NoSuchElementException; - -/** - * Collection which does not allow removing elements (only collect and iterate) - * - * @param <Element> - the generic type of an element in this bag - */ -public class Bag<Element> implements Iterable<Element> { - - private Node<Element> firstElement; // first element of the bag - private int size; // size of bag - - private static class Node<Element> { - private Element content; - private Node<Element> nextElement; - } - - /** - * Create an empty bag - */ - public Bag() { - firstElement = null; - size = 0; - } - - /** - * @return true if this bag is empty, false otherwise - */ - public boolean isEmpty() { - return firstElement == null; - } - - /** - * @return the number of elements - */ - public int size() { - return size; - } - - /** - * @param element - the element to add - */ - public void add(Element element) { - Node<Element> oldfirst = firstElement; - firstElement = new Node<>(); - firstElement.content = element; - firstElement.nextElement = oldfirst; - size++; - } - - /** - * Checks if the bag contains a specific element - * - * @param element which you want to look for - * @return true if bag contains element, otherwise false - */ - public boolean contains(Element element) { - Iterator<Element> iterator = this.iterator(); - while (iterator.hasNext()) { - if (iterator.next().equals(element)) { - return true; - } - } - return false; - } - - /** - * @return an iterator that iterates over the elements in this bag in arbitrary order - */ - public Iterator<Element> iterator() { - return new ListIterator<>(firstElement); - } - - @SuppressWarnings("hiding") - private class ListIterator<Element> implements Iterator<Element> { - private Node<Element> currentElement; - - public ListIterator(Node<Element> firstElement) { - currentElement = firstElement; - } - - public boolean hasNext() { - return currentElement != null; - } - - /** - * remove is not allowed in a bag - */ - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - - public Element next() { - if (!hasNext()) - throw new NoSuchElementException(); - Element element = currentElement.content; - currentElement = currentElement.nextElement; - return element; - } - } - - /** - * main-method for testing - */ - public static void main(String[] args) { - Bag<String> bag = new Bag<>(); - - bag.add("1"); - bag.add("1"); - bag.add("2"); - - System.out.println("size of bag = " + bag.size()); - for (String s : bag) { - System.out.println(s); - } - - System.out.println(bag.contains(null)); - System.out.println(bag.contains("1")); - System.out.println(bag.contains("3")); - } - -} diff --git a/DataStructures/Buffers/CircularBuffer.java b/DataStructures/Buffers/CircularBuffer.java deleted file mode 100644 index ae047b81568f..000000000000 --- a/DataStructures/Buffers/CircularBuffer.java +++ /dev/null @@ -1,129 +0,0 @@ -package DataStructures.Buffers; - -import java.util.Random; -import java.util.concurrent.atomic.AtomicInteger; - -public class CircularBuffer { - private char[] _buffer; - public final int _buffer_size; - private int _write_index = 0; - private int _read_index = 0; - private AtomicInteger _readable_data = new AtomicInteger(0); - - public CircularBuffer(int buffer_size) { - if (!IsPowerOfTwo(buffer_size)) { - throw new IllegalArgumentException(); - } - this._buffer_size = buffer_size; - _buffer = new char[buffer_size]; - } - - private boolean IsPowerOfTwo(int i) { - return (i & (i - 1)) == 0; - } - - private int getTrueIndex(int i) { - return i % _buffer_size; - } - - public Character readOutChar() { - Character result = null; - - //if we have data to read - if (_readable_data.get() > 0) { - result = new Character(_buffer[getTrueIndex(_read_index)]); - _readable_data.decrementAndGet(); - _read_index++; - } - - return result; - } - - public boolean writeToCharBuffer(char c) { - boolean result = false; - - //if we can write to the buffer - if (_readable_data.get() < _buffer_size) { - //write to buffer - _buffer[getTrueIndex(_write_index)] = c; - _readable_data.incrementAndGet(); - _write_index++; - result = true; - } - - return result; - } - - private static class TestWriteWorker implements Runnable { - String _alphabet = "abcdefghijklmnopqrstuvwxyz0123456789"; - Random _random = new Random(); - CircularBuffer _buffer; - - public TestWriteWorker(CircularBuffer cb) { - this._buffer = cb; - } - - private char getRandomChar() { - return _alphabet.charAt(_random.nextInt(_alphabet.length())); - } - - public void run() { - while (!Thread.interrupted()) { - if (!_buffer.writeToCharBuffer(getRandomChar())) { - Thread.yield(); - try { - Thread.sleep(10); - } catch (InterruptedException e) { - return; - } - } - } - } - } - - private static class TestReadWorker implements Runnable { - CircularBuffer _buffer; - - public TestReadWorker(CircularBuffer cb) { - this._buffer = cb; - } - - @Override - public void run() { - System.out.println("Printing Buffer:"); - while (!Thread.interrupted()) { - Character c = _buffer.readOutChar(); - if (c != null) { - System.out.print(c.charValue()); - } else { - Thread.yield(); - try { - Thread.sleep(10); - } catch (InterruptedException e) { - System.out.println(); - return; - } - } - } - } - } - - public static void main(String[] args) throws InterruptedException { - int buffer_size = 1024; - //create circular buffer - CircularBuffer cb = new CircularBuffer(buffer_size); - - //create threads that read and write the buffer. - Thread write_thread = new Thread(new TestWriteWorker(cb)); - Thread read_thread = new Thread(new TestReadWorker(cb)); - read_thread.start(); - write_thread.start(); - - //wait some amount of time - Thread.sleep(10000); - - //interrupt threads and exit - write_thread.interrupt(); - read_thread.interrupt(); - } -} diff --git a/DataStructures/Graphs/BellmanFord.java b/DataStructures/Graphs/BellmanFord.java deleted file mode 100644 index 717cb2803031..000000000000 --- a/DataStructures/Graphs/BellmanFord.java +++ /dev/null @@ -1,158 +0,0 @@ -import java.util.*; -class BellmanFord -/*Implementation of Bellman ford to detect negative cycles. Graph accepts inputs in form of edges which have -start vertex, end vertes and weights. Vertices should be labelled with a number between 0 and total number of vertices-1,both inclusive*/ -{ - int vertex,edge; - private Edge edges[]; - private int index=0; - BellmanFord(int v,int e) - { - vertex=v; - edge=e; - edges=new Edge[e]; - } - class Edge - { - int u,v; - int w; - /** - *@param u Source Vertex - * @param v End vertex - * @param c Weight - */ - Edge(int a,int b,int c) - { - u=a; - v=b; - w=c; - } - } - /** - * @param p[] Parent array which shows updates in edges - * @param i Current vertex under consideration - */ - void printPath(int p[],int i) - { - if(p[i]==-1)//Found the path back to parent - return; - printPath(p,p[i]); - System.out.print(i+" "); - } - public static void main(String args[]) - { - BellmanFord obj=new BellmanFord(0,0);//Dummy object to call nonstatic variables - obj.go(); - } - public void go()//Interactive run for understanding the class first time. Assumes source vertex is 0 and shows distaance to all vertices - { - Scanner sc=new Scanner(System.in);//Grab scanner object for user input - int i,v,e,u,ve,w,j,neg=0; - System.out.println("Enter no. of vertices and edges please"); - v=sc.nextInt(); - e=sc.nextInt(); - Edge arr[]=new Edge[e];//Array of edges - System.out.println("Input edges"); - for(i=0;i<e;i++) - { - u=sc.nextInt(); - ve=sc.nextInt(); - w=sc.nextInt(); - arr[i]=new Edge(u,ve,w); - } - int dist[]=new int[v];//Distance array for holding the finalized shortest path distance between source and all vertices - int p[]=new int[v];//Parent array for holding the paths - for(i=0;i<v;i++) - dist[i]=Integer.MAX_VALUE;//Initializing distance values - dist[0]=0; - p[0]=-1; - for(i=0;i<v-1;i++) - { - for(j=0;j<e;j++) - { - if((int)dist[arr[j].u]!=Integer.MAX_VALUE&&dist[arr[j].v]>dist[arr[j].u]+arr[j].w) - { - dist[arr[j].v]=dist[arr[j].u]+arr[j].w;//Update - p[arr[j].v]=arr[j].u; - } - } - } - //Final cycle for negative checking - for(j=0;j<e;j++) - if((int)dist[arr[j].u]!=Integer.MAX_VALUE&&dist[arr[j].v]>dist[arr[j].u]+arr[j].w) - { - neg=1; - System.out.println("Negative cycle"); - break; - } - if(neg==0)//Go ahead and show results of computaion - { - System.out.println("Distances are: "); - for(i=0;i<v;i++) - System.out.println(i+" "+dist[i]); - System.out.println("Path followed:"); - for(i=0;i<v;i++) - { - System.out.print("0 "); - printPath(p,i); - System.out.println(); - } - } - } - /** - * @param source Starting vertex - * @param end Ending vertex - * @param Edge Array of edges - */ - public void show(int source,int end, Edge arr[])//Just shows results of computation, if graph is passed to it. The graph should - //be created by using addEdge() method and passed by calling getEdgeArray() method - { - int i,j,v=vertex,e=edge,neg=0; - double dist[]=new double[v];//Distance array for holding the finalized shortest path distance between source and all vertices - int p[]=new int[v];//Parent array for holding the paths - for(i=0;i<v;i++) - dist[i]=Integer.MAX_VALUE;//Initializing distance values - dist[source]=0; - p[source]=-1; - for(i=0;i<v-1;i++) - { - for(j=0;j<e;j++) - { - if((int)dist[arr[j].u]!=Integer.MAX_VALUE&&dist[arr[j].v]>dist[arr[j].u]+arr[j].w) - { - dist[arr[j].v]=dist[arr[j].u]+arr[j].w;//Update - p[arr[j].v]=arr[j].u; - } - } - } - //Final cycle for negative checking - for(j=0;j<e;j++) - if((int)dist[arr[j].u]!=Integer.MAX_VALUE&&dist[arr[j].v]>dist[arr[j].u]+arr[j].w) - { - neg=1; - System.out.println("Negative cycle"); - break; - } - if(neg==0)//Go ahead and show results of computaion - { - System.out.println("Distance is: "+dist[end]); - System.out.println("Path followed:"); - System.out.print(source+" "); - printPath(p,end); - System.out.println(); - } - } - /** - *@param x Source Vertex - * @param y End vertex - * @param z Weight - */ - public void addEdge(int x,int y,int z)//Adds unidirectionl Edge - { - edges[index++]=new Edge(x,y,z); - } - public Edge[] getEdgeArray() - { - return edges; - } -} \ No newline at end of file diff --git a/DataStructures/Graphs/FloydWarshall.java b/DataStructures/Graphs/FloydWarshall.java deleted file mode 100644 index d6a6b48d4770..000000000000 --- a/DataStructures/Graphs/FloydWarshall.java +++ /dev/null @@ -1,73 +0,0 @@ -package DataStructures.Graphs; - -import java.util.Arrays; -import java.util.Scanner; - -public class FloydWarshall { - private int DistanceMatrix[][]; - private int numberofvertices;//number of vertices in the graph - public static final int INFINITY = 999; - - public FloydWarshall(int numberofvertices) { - DistanceMatrix = new int[numberofvertices + 1][numberofvertices + 1];//stores the value of distance from all the possible path form the source vertex to destination vertex - Arrays.fill(DistanceMatrix, 0); - this.numberofvertices = numberofvertices; - } - - public void floydwarshall(int AdjacencyMatrix[][])//calculates all the distances from source to destination vertex - { - for (int source = 1; source <= numberofvertices; source++) { - for (int destination = 1; destination <= numberofvertices; destination++) { - DistanceMatrix[source][destination] = AdjacencyMatrix[source][destination]; - } - } - for (int intermediate = 1; intermediate <= numberofvertices; intermediate++) { - for (int source = 1; source <= numberofvertices; source++) { - for (int destination = 1; destination <= numberofvertices; destination++) { - if (DistanceMatrix[source][intermediate] + DistanceMatrix[intermediate][destination] - < DistanceMatrix[source][destination]) - // if the new distance calculated is less then the earlier shortest - // calculated distance it get replaced as new shortest distance - { - DistanceMatrix[source][destination] = DistanceMatrix[source][intermediate] - + DistanceMatrix[intermediate][destination]; - } - } - } - } - for (int source = 1; source <= numberofvertices; source++) - System.out.print("\t" + source); - System.out.println(); - for (int source = 1; source <= numberofvertices; source++) { - System.out.print(source + "\t"); - for (int destination = 1; destination <= numberofvertices; destination++) { - System.out.print(DistanceMatrix[source][destination] + "\t"); - } - System.out.println(); - } - } - - public static void main(String... arg) { - Scanner scan = new Scanner(System.in); - System.out.println("Enter the number of vertices"); - int numberOfVertices = scan.nextInt(); - int[][] adjacencyMatrix = new int[numberOfVertices + 1][numberOfVertices + 1]; - System.out.println("Enter the Weighted Matrix for the graph"); - for (int source = 1; source <= numberOfVertices; source++) { - for (int destination = 1; destination <= numberOfVertices; destination++) { - adjacencyMatrix[source][destination] = scan.nextInt(); - if (source == destination) { - adjacencyMatrix[source][destination] = 0; - continue; - } - if (adjacencyMatrix[source][destination] == 0) { - adjacencyMatrix[source][destination] = INFINITY; - } - } - } - System.out.println("The Transitive Closure of the Graph"); - FloydWarshall floydwarshall = new FloydWarshall(numberOfVertices); - floydwarshall.floydwarshall(adjacencyMatrix); - scan.close(); - } -} diff --git a/DataStructures/Graphs/Graphs.java b/DataStructures/Graphs/Graphs.java deleted file mode 100644 index ea03b05f246b..000000000000 --- a/DataStructures/Graphs/Graphs.java +++ /dev/null @@ -1,133 +0,0 @@ -package DataStructures.Graphs; - -import java.util.ArrayList; -import java.lang.StringBuilder; - -class AdjacencyListGraph<E extends Comparable<E>> { - - ArrayList<Vertex> verticies; - - public AdjacencyListGraph() { - verticies = new ArrayList<>(); - } - - private class Vertex { - E data; - ArrayList<Vertex> adjacentVerticies; - - public Vertex(E data) { - adjacentVerticies = new ArrayList<>(); - this.data = data; - } - - public boolean addAdjacentVertex(Vertex to) { - for (Vertex v : adjacentVerticies) { - if (v.data.compareTo(to.data) == 0) { - return false; // the edge already exists - } - } - return adjacentVerticies.add(to); // this will return true; - } - - public boolean removeAdjacentVertex(E to) { - // use indexes here so it is possible to - // remove easily without implementing - // equals method that ArrayList.remove(Object o) uses - for (int i = 0; i < adjacentVerticies.size(); i++) { - if (adjacentVerticies.get(i).data.compareTo(to) == 0) { - adjacentVerticies.remove(i); - return true; - } - } - return false; - } - } - - /** - * this method removes an edge from the graph between two specified - * verticies - * - * @param from the data of the vertex the edge is from - * @param to the data of the vertex the edge is going to - * @return returns false if the edge doesn't exist, returns true if the edge exists and is removed - */ - public boolean removeEdge(E from, E to) { - Vertex fromV = null; - for (Vertex v : verticies) { - if (from.compareTo(v.data) == 0) { - fromV = v; - break; - } - } - if (fromV == null) return false; - return fromV.removeAdjacentVertex(to); - } - - /** - * this method adds an edge to the graph between two specified - * verticies - * - * @param from the data of the vertex the edge is from - * @param to the data of the vertex the edge is going to - * @return returns true if the edge did not exist, return false if it already did - */ - public boolean addEdge(E from, E to) { - Vertex fromV = null, toV = null; - for (Vertex v : verticies) { - if (from.compareTo(v.data) == 0) { // see if from vertex already exists - fromV = v; - } else if (to.compareTo(v.data) == 0) { // see if to vertex already exists - toV = v; - } - if (fromV != null && toV != null) break; // both nodes exist so stop searching - } - if (fromV == null) { - fromV = new Vertex(from); - verticies.add(fromV); - } - if (toV == null) { - toV = new Vertex(to); - verticies.add(toV); - } - return fromV.addAdjacentVertex(toV); - } - - /** - * this gives a list of verticies in the graph and their adjacencies - * - * @return returns a string describing this graph - */ - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - for (Vertex v : verticies) { - sb.append("Vertex: "); - sb.append(v.data); - sb.append("\n"); - sb.append("Adjacent verticies: "); - for (Vertex v2 : v.adjacentVerticies) { - sb.append(v2.data); - sb.append(" "); - } - sb.append("\n"); - } - return sb.toString(); - } -} - -public class Graphs { - - public static void main(String args[]) { - AdjacencyListGraph<Integer> graph = new AdjacencyListGraph<>(); - assert graph.addEdge(1, 2); - assert graph.addEdge(1, 5); - assert graph.addEdge(2, 5); - assert !graph.addEdge(1, 2); - assert graph.addEdge(2, 3); - assert graph.addEdge(3, 4); - assert graph.addEdge(4, 1); - assert !graph.addEdge(2, 3); - System.out.println(graph); - } - -} diff --git a/DataStructures/Graphs/MatrixGraphs.java b/DataStructures/Graphs/MatrixGraphs.java deleted file mode 100644 index e7fb9cc4fb2f..000000000000 --- a/DataStructures/Graphs/MatrixGraphs.java +++ /dev/null @@ -1,147 +0,0 @@ -package DataStructures.Graphs; - -public class MatrixGraphs { - - public static void main(String args[]) { - AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(10); - graph.addEdge(1, 2); - graph.addEdge(1, 5); - graph.addEdge(2, 5); - graph.addEdge(1, 2); - graph.addEdge(2, 3); - graph.addEdge(3, 4); - graph.addEdge(4, 1); - graph.addEdge(2, 3); - System.out.println(graph); - } - -} - -class AdjacencyMatrixGraph { - private int _numberOfVertices; - private int _numberOfEdges; - private int[][] _adjacency; - - static final int EDGE_EXIST = 1; - static final int EDGE_NONE = 0; - - public AdjacencyMatrixGraph(int givenNumberOfVertices) { - this.setNumberOfVertices(givenNumberOfVertices); - this.setNumberOfEdges(0); - this.setAdjacency(new int[givenNumberOfVertices][givenNumberOfVertices]); - for (int i = 0; i < givenNumberOfVertices; i++) { - for (int j = 0; j < givenNumberOfVertices; j++) { - this.adjacency()[i][j] = AdjacencyMatrixGraph.EDGE_NONE; - } - } - } - - private void setNumberOfVertices(int newNumberOfVertices) { - this._numberOfVertices = newNumberOfVertices; - } - - public int numberOfVertices() { - return this._numberOfVertices; - } - - private void setNumberOfEdges(int newNumberOfEdges) { - this._numberOfEdges = newNumberOfEdges; - } - - public int numberOfEdges() { - return this._numberOfEdges; - } - - private void setAdjacency(int[][] newAdjacency) { - this._adjacency = newAdjacency; - } - - private int[][] adjacency() { - return this._adjacency; - } - - private boolean adjacencyOfEdgeDoesExist(int from, int to) { - return (this.adjacency()[from][to] != AdjacencyMatrixGraph.EDGE_NONE); - } - - public boolean vertexDoesExist(int aVertex) { - if (aVertex >= 0 && aVertex < this.numberOfVertices()) { - return true; - } else { - return false; - } - } - - public boolean edgeDoesExist(int from, int to) { - if (this.vertexDoesExist(from) && this.vertexDoesExist(to)) { - return (this.adjacencyOfEdgeDoesExist(from, to)); - } - - return false; - } - - /** - * This method adds an edge to the graph between two specified - * vertices - * - * @param from the data of the vertex the edge is from - * @param to the data of the vertex the edge is going to - * @return returns true if the edge did not exist, return false if it already did - */ - public boolean addEdge(int from, int to) { - if (this.vertexDoesExist(from) && this.vertexDoesExist(to)) { - if (!this.adjacencyOfEdgeDoesExist(from, to)) { - this.adjacency()[from][to] = AdjacencyMatrixGraph.EDGE_EXIST; - this.adjacency()[to][from] = AdjacencyMatrixGraph.EDGE_EXIST; - this.setNumberOfEdges(this.numberOfEdges() + 1); - return true; - } - } - - return false; - } - - /** - * this method removes an edge from the graph between two specified - * vertices - * - * @param from the data of the vertex the edge is from - * @param to the data of the vertex the edge is going to - * @return returns false if the edge doesn't exist, returns true if the edge exists and is removed - */ - public boolean removeEdge(int from, int to) { - if (!this.vertexDoesExist(from) || !this.vertexDoesExist(to)) { - if (this.adjacencyOfEdgeDoesExist(from, to)) { - this.adjacency()[from][to] = AdjacencyMatrixGraph.EDGE_NONE; - this.adjacency()[to][from] = AdjacencyMatrixGraph.EDGE_NONE; - this.setNumberOfEdges(this.numberOfEdges() - 1); - return true; - } - } - return false; - } - - /** - * this gives a list of vertices in the graph and their adjacencies - * - * @return returns a string describing this graph - */ - public String toString() { - String s = new String(); - s = " "; - for (int i = 0; i < this.numberOfVertices(); i++) { - s = s + String.valueOf(i) + " "; - } - s = s + " \n"; - - for (int i = 0; i < this.numberOfVertices(); i++) { - s = s + String.valueOf(i) + " : "; - for (int j = 0; j < this.numberOfVertices(); j++) { - s = s + String.valueOf(this._adjacency[i][j]) + " "; - } - s = s + "\n"; - } - return s; - } - -} diff --git a/DataStructures/Graphs/PrimMST.java b/DataStructures/Graphs/PrimMST.java deleted file mode 100644 index 32487feddefb..000000000000 --- a/DataStructures/Graphs/PrimMST.java +++ /dev/null @@ -1,108 +0,0 @@ -package DataStructures.Graphs; - -import java.lang.*; - -/** - * A Java program for Prim's Minimum Spanning Tree (MST) algorithm. - * adjacency matrix representation of the graph - */ -class PrimMST { - // Number of vertices in the graph - private static final int V = 5; - - // A utility function to find the vertex with minimum key - // value, from the set of vertices not yet included in MST - int minKey(int key[], Boolean mstSet[]) { - // Initialize min value - int min = Integer.MAX_VALUE, min_index = -1; - - for (int v = 0; v < V; v++) - if (mstSet[v] == false && key[v] < min) { - min = key[v]; - min_index = v; - } - - return min_index; - } - - // A utility function to print the constructed MST stored in - // parent[] - void printMST(int parent[], int n, int graph[][]) { - System.out.println("Edge Weight"); - for (int i = 1; i < V; i++) - System.out.println(parent[i] + " - " + i + " " + - graph[i][parent[i]]); - } - - // Function to construct and print MST for a graph represented - // using adjacency matrix representation - void primMST(int graph[][]) { - // Array to store constructed MST - int parent[] = new int[V]; - - // Key values used to pick minimum weight edge in cut - int key[] = new int[V]; - - // To represent set of vertices not yet included in MST - Boolean mstSet[] = new Boolean[V]; - - // Initialize all keys as INFINITE - for (int i = 0; i < V; i++) { - key[i] = Integer.MAX_VALUE; - mstSet[i] = false; - } - - // Always include first 1st vertex in MST. - key[0] = 0; // Make key 0 so that this vertex is - // picked as first vertex - parent[0] = -1; // First node is always root of MST - - // The MST will have V vertices - for (int count = 0; count < V - 1; count++) { - // Pick thd minimum key vertex from the set of vertices - // not yet included in MST - int u = minKey(key, mstSet); - - // Add the picked vertex to the MST Set - mstSet[u] = true; - - // Update key value and parent index of the adjacent - // vertices of the picked vertex. Consider only those - // vertices which are not yet included in MST - for (int v = 0; v < V; v++) - - // graph[u][v] is non zero only for adjacent vertices of m - // mstSet[v] is false for vertices not yet included in MST - // Update the key only if graph[u][v] is smaller than key[v] - if (graph[u][v] != 0 && mstSet[v] == false && - graph[u][v] < key[v]) { - parent[v] = u; - key[v] = graph[u][v]; - } - } - - // print the constructed MST - printMST(parent, V, graph); - } - - public static void main(String[] args) { - /* Let us create the following graph - 2 3 - (0)--(1)--(2) - | / \ | - 6| 8/ \5 |7 - | / \ | - (3)-------(4) - 9 */ - PrimMST t = new PrimMST(); - int graph[][] = new int[][]{{0, 2, 0, 6, 0}, - {2, 0, 3, 8, 5}, - {0, 3, 0, 0, 7}, - {6, 8, 0, 0, 9}, - {0, 5, 7, 9, 0}, - }; - - // Print the solution - t.primMST(graph); - } -} diff --git a/DataStructures/HashMap/Hashing/HashMap.java b/DataStructures/HashMap/Hashing/HashMap.java deleted file mode 100644 index c4668b4ab925..000000000000 --- a/DataStructures/HashMap/Hashing/HashMap.java +++ /dev/null @@ -1,43 +0,0 @@ -package DataStructures.HashMap.Hashing; - - -class HashMap { - private int hsize; - private LinkedList[] buckets; - - public HashMap(int hsize) { - buckets = new LinkedList[hsize]; - for (int i = 0; i < hsize; i++) { - buckets[i] = new LinkedList(); - // Java requires explicit initialisaton of each object - } - this.hsize = hsize; - } - - public int hashing(int key) { - int hash = key % hsize; - if (hash < 0) - hash += hsize; - return hash; - } - - public void insertHash(int key) { - int hash = hashing(key); - buckets[hash].insert(key); - } - - - public void deleteHash(int key) { - int hash = hashing(key); - - buckets[hash].delete(key); - } - - public void displayHashtable() { - for (int i = 0; i < hsize; i++) { - System.out.printf("Bucket %d :", i); - buckets[i].display(); - } - } - -} \ No newline at end of file diff --git a/DataStructures/HashMap/Hashing/LinkedList.java b/DataStructures/HashMap/Hashing/LinkedList.java deleted file mode 100644 index 8c4ec0f732da..000000000000 --- a/DataStructures/HashMap/Hashing/LinkedList.java +++ /dev/null @@ -1,64 +0,0 @@ -package DataStructures.HashMap.Hashing; - -class LinkedList { - - private Node Head; - private int size; - - public LinkedList() { - Head = null; - size = 0; - } - - public void insert(int data) { - - Node temp = Head; - Node newnode = new Node(data); - - size++; - - if(Head == null) { - Head = newnode; - } - else { - newnode.next = Head; - Head = newnode; - } - } - - public void delete(int data) { - if(size == 0) { - System.out.println("UnderFlow!"); - return; - } - - else { - Node curr = Head; - if (curr.data == data) { - Head = curr.next; - size--; - return; - } - else { - - while(curr.next.next != null) { - if(curr.next.data == data){ - curr.next = curr.next.next; - return; - } - } - - System.out.println("Key not Found"); - } - } - } - - public void display() { - Node temp = Head; - while(temp != null) { - System.out.printf("%d ",temp.data); - temp = temp.next; - } - System.out.println(); - } -} \ No newline at end of file diff --git a/DataStructures/HashMap/Hashing/Main.java b/DataStructures/HashMap/Hashing/Main.java deleted file mode 100644 index 66f65c1d9064..000000000000 --- a/DataStructures/HashMap/Hashing/Main.java +++ /dev/null @@ -1,47 +0,0 @@ -package DataStructures.HashMap.Hashing; - -import java.util.Scanner; - -public class Main { - public static void main(String[] args) { - - int choice, key; - - HashMap h = new HashMap(7); - - while (true) { - System.out.println("Enter your Choice :"); - System.out.println("1. Add Key"); - System.out.println("2. Delete Key"); - System.out.println("3. Print Table"); - System.out.println("4. Exit"); - - Scanner In = new Scanner(System.in); - - choice = In.nextInt(); - - switch (choice) { - case 1: { - System.out.println("Enter the Key: "); - key = In.nextInt(); - h.insertHash(key); - break; - } - case 2: { - System.out.println("Enter the Key delete: "); - key = In.nextInt(); - h.deleteHash(key); - break; - } - case 3: { - System.out.println("Print table"); - h.displayHashtable(); - break; - } - case 4: { - return; - } - } - } - } -} \ No newline at end of file diff --git a/DataStructures/HashMap/Hashing/Node.java b/DataStructures/HashMap/Hashing/Node.java deleted file mode 100644 index 74ab01f9d2a9..000000000000 --- a/DataStructures/HashMap/Hashing/Node.java +++ /dev/null @@ -1,11 +0,0 @@ -package DataStructures.HashMap.Hashing; - -class Node { - int data; - Node next; - - public Node(int data) { - this.data = data; - this.next = null; - } -} \ No newline at end of file diff --git a/DataStructures/Heaps/EmptyHeapException.java b/DataStructures/Heaps/EmptyHeapException.java deleted file mode 100644 index a0fda5f53786..000000000000 --- a/DataStructures/Heaps/EmptyHeapException.java +++ /dev/null @@ -1,14 +0,0 @@ -package DataStructures.Heaps; - -/** - * @author Nicolas Renard - * Exception to be thrown if the getElement method is used on an empty heap. - */ -@SuppressWarnings("serial") -public class EmptyHeapException extends Exception { - - public EmptyHeapException(String message) { - super(message); - } - -} diff --git a/DataStructures/Heaps/Heap.java b/DataStructures/Heaps/Heap.java deleted file mode 100644 index 0b7da3436581..000000000000 --- a/DataStructures/Heaps/Heap.java +++ /dev/null @@ -1,40 +0,0 @@ -package DataStructures.Heaps; - -/** - * Interface common to heap data structures.<br> - * <p>Heaps are tree-like data structures that allow storing elements in a specific - * way. Each node corresponds to an element and has one parent node (except for the root) and - * at most two children nodes. Every element contains a key, and those keys - * indicate how the tree shall be built. For instance, for a min-heap, the key of a node shall - * be greater than or equal to its parent's and lower than or equal to its children's (the opposite rule applies to a - * max-heap).</p> - * <p>All heap-related operations (inserting or deleting an element, extracting the min or max) are performed in - * O(log n) time.</p> - * - * @author Nicolas Renard - */ -public interface Heap { - - /** - * @return the top element in the heap, the one with lowest key for min-heap or with - * the highest key for max-heap - * @throws EmptyHeapException if heap is empty - */ - HeapElement getElement() throws EmptyHeapException; - - /** - * Inserts an element in the heap. Adds it to then end and toggle it until it finds its - * right position. - * - * @param element an instance of the HeapElement class. - */ - void insertElement(HeapElement element); - - /** - * Delete an element in the heap. - * - * @param elementIndex int containing the position in the heap of the element to be deleted. - */ - void deleteElement(int elementIndex); - -} \ No newline at end of file diff --git a/DataStructures/Heaps/HeapElement.java b/DataStructures/Heaps/HeapElement.java deleted file mode 100644 index 113146012918..000000000000 --- a/DataStructures/Heaps/HeapElement.java +++ /dev/null @@ -1,123 +0,0 @@ -package DataStructures.Heaps; - -import java.lang.Double; -import java.lang.Object; - -/** - * Class for heap elements.<br> - * <p>A heap element contains two attributes: a key which will be used to build the tree (int - * or double, either primitive type or object) and any kind of IMMUTABLE object the user sees fit - * to carry any information he/she likes. Be aware that the use of a mutable object might - * jeopardize the integrity of this information. </p> - * - * @author Nicolas Renard - */ -public class HeapElement { - private final double key; - private final Object additionalInfo; - - // Constructors - - /** - * @param key : a number of primitive type 'double' - * @param info : any kind of IMMUTABLE object. May be null, since the purpose is only to carry - * additional information of use for the user - */ - public HeapElement(double key, Object info) { - this.key = key; - this.additionalInfo = info; - } - - /** - * @param key : a number of primitive type 'int' - * @param info : any kind of IMMUTABLE object. May be null, since the purpose is only to carry - * additional information of use for the user - */ - public HeapElement(int key, Object info) { - this.key = key; - this.additionalInfo = info; - } - - /** - * @param key : a number of object type 'Integer' - * @param info : any kind of IMMUTABLE object. May be null, since the purpose is only to carry - * additional information of use for the user - */ - public HeapElement(Integer key, Object info) { - this.key = key; - this.additionalInfo = info; - } - - /** - * @param key : a number of object type 'Double' - * @param info : any kind of IMMUTABLE object. May be null, since the purpose is only to carry - * additional information of use for the user - */ - public HeapElement(Double key, Object info) { - this.key = key; - this.additionalInfo = info; - } - - /** - * @param key : a number of primitive type 'double' - */ - public HeapElement(double key) { - this.key = key; - this.additionalInfo = null; - } - - /** - * @param key : a number of primitive type 'int' - */ - public HeapElement(int key) { - this.key = key; - this.additionalInfo = null; - } - - /** - * @param key : a number of object type 'Integer' - */ - public HeapElement(Integer key) { - this.key = key; - this.additionalInfo = null; - } - - /** - * @param key : a number of object type 'Double' - */ - public HeapElement(Double key) { - this.key = key; - this.additionalInfo = null; - } - - // Getters - - /** - * @return the object containing the additional info provided by the user. - */ - public Object getInfo() { - return additionalInfo; - } - - /** - * @return the key value of the element - */ - public double getKey() { - return key; - } - - // Overridden object methods - - public String toString() { - return "Key: " + key + " - " + additionalInfo.toString(); - } - - /** - * @param otherHeapElement - * @return true if the keys on both elements are identical and the additional info objects - * are identical. - */ - public boolean equals(HeapElement otherHeapElement) { - return (this.key == otherHeapElement.key) && (this.additionalInfo.equals(otherHeapElement.additionalInfo)); - } -} diff --git a/DataStructures/Heaps/MaxHeap.java b/DataStructures/Heaps/MaxHeap.java deleted file mode 100644 index fed09bcba045..000000000000 --- a/DataStructures/Heaps/MaxHeap.java +++ /dev/null @@ -1,119 +0,0 @@ -package DataStructures.Heaps; - -import java.util.ArrayList; -import java.util.List; - -/** - * Heap tree where a node's key is higher than or equal to its parent's and lower than or equal - * to its children's. - * - * @author Nicolas Renard - */ -public class MaxHeap implements Heap { - - private final List<HeapElement> maxHeap; - - public MaxHeap(List<HeapElement> listElements) { - maxHeap = new ArrayList<>(); - for (HeapElement heapElement : listElements) { - if (heapElement != null) insertElement(heapElement); - else System.out.println("Null element. Not added to heap"); - } - if (maxHeap.size() == 0) System.out.println("No element has been added, empty heap."); - } - - /** - * Get the element at a given index. The key for the list is equal to index value - 1 - * - * @param elementIndex index - * @return heapElement - */ - public HeapElement getElement(int elementIndex) { - if ((elementIndex <= 0) || (elementIndex > maxHeap.size())) - throw new IndexOutOfBoundsException("Index out of heap range"); - return maxHeap.get(elementIndex - 1); - } - - // Get the key of the element at a given index - private double getElementKey(int elementIndex) { - return maxHeap.get(elementIndex - 1).getKey(); - } - - // Swaps two elements in the heap - private void swap(int index1, int index2) { - HeapElement temporaryElement = maxHeap.get(index1 - 1); - maxHeap.set(index1 - 1, maxHeap.get(index2 - 1)); - maxHeap.set(index2 - 1, temporaryElement); - } - - // Toggle an element up to its right place as long as its key is lower than its parent's - private void toggleUp(int elementIndex) { - double key = maxHeap.get(elementIndex - 1).getKey(); - while (getElementKey((int) Math.floor(elementIndex / 2)) < key) { - swap(elementIndex, (int) Math.floor(elementIndex / 2)); - elementIndex = (int) Math.floor(elementIndex / 2); - } - } - - // Toggle an element down to its right place as long as its key is higher - // than any of its children's - private void toggleDown(int elementIndex) { - double key = maxHeap.get(elementIndex - 1).getKey(); - boolean wrongOrder = (key < getElementKey(elementIndex * 2)) || (key < getElementKey(Math.min(elementIndex * 2, maxHeap.size()))); - while ((2 * elementIndex <= maxHeap.size()) && wrongOrder) { - // Check whether it shall swap the element with its left child or its right one if any. - if ((2 * elementIndex < maxHeap.size()) && (getElementKey(elementIndex * 2 + 1) > getElementKey(elementIndex * 2))) { - swap(elementIndex, 2 * elementIndex + 1); - elementIndex = 2 * elementIndex + 1; - } else { - swap(elementIndex, 2 * elementIndex); - elementIndex = 2 * elementIndex; - } - wrongOrder = (key < getElementKey(elementIndex * 2)) || (key < getElementKey(Math.min(elementIndex * 2, maxHeap.size()))); - - } - } - - private HeapElement extractMax() { - HeapElement result = maxHeap.get(0); - deleteElement(0); - return result; - } - - @Override - public void insertElement(HeapElement element) { - maxHeap.add(element); - toggleUp(maxHeap.size()); - - } - - @Override - public void deleteElement(int elementIndex) { - if (maxHeap.isEmpty()) - try { - throw new EmptyHeapException("Attempt to delete an element from an empty heap"); - } catch (EmptyHeapException e) { - e.printStackTrace(); - } - if ((elementIndex > maxHeap.size()) || (elementIndex <= 0)) - throw new IndexOutOfBoundsException("Index out of heap range"); - // The last element in heap replaces the one to be deleted - maxHeap.set(elementIndex - 1, getElement(maxHeap.size())); - maxHeap.remove(maxHeap.size()); - // Shall the new element be moved up... - if (getElementKey(elementIndex) > getElementKey((int) Math.floor(elementIndex / 2))) toggleUp(elementIndex); - // ... or down ? - else if (((2 * elementIndex <= maxHeap.size()) && (getElementKey(elementIndex) < getElementKey(elementIndex * 2))) || - ((2 * elementIndex < maxHeap.size()) && (getElementKey(elementIndex) < getElementKey(elementIndex * 2)))) - toggleDown(elementIndex); - } - - @Override - public HeapElement getElement() throws EmptyHeapException { - try { - return extractMax(); - } catch (Exception e) { - throw new EmptyHeapException("Heap is empty. Error retrieving element"); - } - } -} \ No newline at end of file diff --git a/DataStructures/Heaps/MinHeap.java b/DataStructures/Heaps/MinHeap.java deleted file mode 100644 index 4b435e6d81d7..000000000000 --- a/DataStructures/Heaps/MinHeap.java +++ /dev/null @@ -1,114 +0,0 @@ -package DataStructures.Heaps; - -import java.util.ArrayList; -import java.util.List; - -/** - * Heap tree where a node's key is higher than or equal to its parent's and lower than or equal - * to its children's. - * - * @author Nicolas Renard - */ -public class MinHeap implements Heap { - - private final List<HeapElement> minHeap; - - public MinHeap(List<HeapElement> listElements) { - minHeap = new ArrayList<>(); - for (HeapElement heapElement : listElements) { - if (heapElement != null) insertElement(heapElement); - else System.out.println("Null element. Not added to heap"); - } - if (minHeap.size() == 0) System.out.println("No element has been added, empty heap."); - } - - // Get the element at a given index. The key for the list is equal to index value - 1 - public HeapElement getElement(int elementIndex) { - if ((elementIndex <= 0) || (elementIndex > minHeap.size())) - throw new IndexOutOfBoundsException("Index out of heap range"); - return minHeap.get(elementIndex - 1); - } - - // Get the key of the element at a given index - private double getElementKey(int elementIndex) { - return minHeap.get(elementIndex - 1).getKey(); - } - - // Swaps two elements in the heap - private void swap(int index1, int index2) { - HeapElement temporaryElement = minHeap.get(index1 - 1); - minHeap.set(index1 - 1, minHeap.get(index2 - 1)); - minHeap.set(index2 - 1, temporaryElement); - } - - // Toggle an element up to its right place as long as its key is lower than its parent's - private void toggleUp(int elementIndex) { - double key = minHeap.get(elementIndex - 1).getKey(); - while (getElementKey((int) Math.floor(elementIndex / 2)) > key) { - swap(elementIndex, (int) Math.floor(elementIndex / 2)); - elementIndex = (int) Math.floor(elementIndex / 2); - } - } - - // Toggle an element down to its right place as long as its key is higher - // than any of its children's - private void toggleDown(int elementIndex) { - double key = minHeap.get(elementIndex - 1).getKey(); - boolean wrongOrder = (key > getElementKey(elementIndex * 2)) || (key > getElementKey(Math.min(elementIndex * 2, minHeap.size()))); - while ((2 * elementIndex <= minHeap.size()) && wrongOrder) { - // Check whether it shall swap the element with its left child or its right one if any. - if ((2 * elementIndex < minHeap.size()) && (getElementKey(elementIndex * 2 + 1) < getElementKey(elementIndex * 2))) { - swap(elementIndex, 2 * elementIndex + 1); - elementIndex = 2 * elementIndex + 1; - } else { - swap(elementIndex, 2 * elementIndex); - elementIndex = 2 * elementIndex; - } - wrongOrder = (key > getElementKey(elementIndex * 2)) || (key > getElementKey(Math.min(elementIndex * 2, minHeap.size()))); - - } - } - - private HeapElement extractMin() { - HeapElement result = minHeap.get(0); - deleteElement(0); - return result; - } - - @Override - public void insertElement(HeapElement element) { - minHeap.add(element); - toggleUp(minHeap.size()); - - } - - @Override - public void deleteElement(int elementIndex) { - if (minHeap.isEmpty()) - try { - throw new EmptyHeapException("Attempt to delete an element from an empty heap"); - } catch (EmptyHeapException e) { - e.printStackTrace(); - } - if ((elementIndex > minHeap.size()) || (elementIndex <= 0)) - throw new IndexOutOfBoundsException("Index out of heap range"); - // The last element in heap replaces the one to be deleted - minHeap.set(elementIndex - 1, getElement(minHeap.size())); - minHeap.remove(minHeap.size()); - // Shall the new element be moved up... - if (getElementKey(elementIndex) < getElementKey((int) Math.floor(elementIndex / 2))) toggleUp(elementIndex); - // ... or down ? - else if (((2 * elementIndex <= minHeap.size()) && (getElementKey(elementIndex) > getElementKey(elementIndex * 2))) || - ((2 * elementIndex < minHeap.size()) && (getElementKey(elementIndex) > getElementKey(elementIndex * 2)))) - toggleDown(elementIndex); - } - - @Override - public HeapElement getElement() throws EmptyHeapException { - try { - return extractMin(); - } catch (Exception e) { - throw new EmptyHeapException("Heap is empty. Error retrieving element"); - } - } -} \ No newline at end of file diff --git a/DataStructures/Heaps/MinPriorityQueue.java b/DataStructures/Heaps/MinPriorityQueue.java deleted file mode 100644 index 52e475abf4be..000000000000 --- a/DataStructures/Heaps/MinPriorityQueue.java +++ /dev/null @@ -1,133 +0,0 @@ -package DataStructures.Heaps; - -/** - * Minimum Priority Queue - * It is a part of heap data structure - * A heap is a specific tree based data structure - * in which all the nodes of tree are in a specific order. - * that is the children are arranged in some - * respect of their parents, can either be greater - * or less than the parent. This makes it a min priority queue - * or max priority queue. - * <p> - * <p> - * Functions: insert, delete, peek, isEmpty, print, heapSort, sink - */ -public class MinPriorityQueue { - private int[] heap; - private int capacity; - private int size; - - // calss the constructor and initializes the capacity - MinPriorityQueue(int c) { - this.capacity = c; - this.size = 0; - this.heap = new int[c + 1]; - } - - // inserts the key at the end and rearranges it - // so that the binary heap is in appropriate order - public void insert(int key) { - if (this.isFull()) - return; - this.heap[this.size + 1] = key; - int k = this.size + 1; - while (k > 1) { - if (this.heap[k] < this.heap[k / 2]) { - int temp = this.heap[k]; - this.heap[k] = this.heap[k / 2]; - this.heap[k / 2] = temp; - } - k = k / 2; - } - this.size++; - } - - // returns the highest priority value - public int peek() { - return this.heap[1]; - } - - // returns boolean value whether the heap is empty or not - public boolean isEmpty() { - if (0 == this.size) - return true; - return false; - } - - // returns boolean value whether the heap is full or not - public boolean isFull() { - if (this.size == this.capacity) - return true; - return false; - } - - // prints the heap - public void print() { - for (int i = 1; i <= this.capacity; i++) - System.out.print(this.heap[i] + " "); - System.out.println(); - } - - // heap sorting can be done by performing - // delete function to the number of times of the size of the heap - // it returns reverse sort because it is a min priority queue - public void heapSort() { - for (int i = 1; i < this.capacity; i++) - this.delete(); - } - - // this function reorders the heap after every delete function - private void sink() { - int k = 1; - while (2 * k <= this.size || 2 * k + 1 <= this.size) { - int minIndex; - if (this.heap[2 * k] >= this.heap[k]) { - if (2 * k + 1 <= this.size && this.heap[2 * k + 1] >= this.heap[k]) { - break; - } else if (2 * k + 1 > this.size) { - break; - } - } - if (2 * k + 1 > this.size) { - minIndex = this.heap[2 * k] < this.heap[k] ? 2 * k : k; - } else { - if (this.heap[k] > this.heap[2 * k] || this.heap[k] > this.heap[2 * k + 1]) { - minIndex = this.heap[2 * k] < this.heap[2 * k + 1] ? 2 * k : 2 * k + 1; - } else { - minIndex = k; - } - } - int temp = this.heap[k]; - this.heap[k] = this.heap[minIndex]; - this.heap[minIndex] = temp; - k = minIndex; - } - } - - // deletes the highest priority value from the heap - public int delete() { - int min = this.heap[1]; - this.heap[1] = this.heap[this.size]; - this.heap[this.size] = min; - this.size--; - this.sink(); - return min; - } - - public static void main(String[] args) { - // testing - MinPriorityQueue q = new MinPriorityQueue(8); - q.insert(5); - q.insert(2); - q.insert(4); - q.insert(1); - q.insert(7); - q.insert(6); - q.insert(3); - q.insert(8); - q.print(); // [ 1, 2, 3, 5, 7, 6, 4, 8 ] - q.heapSort(); - q.print(); // [ 8, 7, 6, 5, 4, 3, 2, 1 ] - } -} \ No newline at end of file diff --git a/DataStructures/Lists/CircleLinkedList.java b/DataStructures/Lists/CircleLinkedList.java deleted file mode 100644 index 67235172dfb7..000000000000 --- a/DataStructures/Lists/CircleLinkedList.java +++ /dev/null @@ -1,63 +0,0 @@ -package DataStructures.Lists; - -public class CircleLinkedList<E> { - private static class Node<E> { - Node<E> next; - E value; - - private Node(E value, Node<E> next) { - this.value = value; - this.next = next; - } - } - - //For better O.O design this should be private allows for better black box design - private int size; - //this will point to dummy node; - private Node<E> head; - - //constructer for class.. here we will make a dummy node for circly linked list implementation with reduced error catching as our list will never be empty; - public CircleLinkedList() { - //creation of the dummy node - head = new Node<E>(null, head); - size = 0; - } - - // getter for the size... needed because size is private. - public int getSize() { - return size; - } - - // for the sake of simplistiy this class will only contain the append function or addLast other add functions can be implemented however this is the basses of them all really. - public void append(E value) { - if (value == null) { - // we do not want to add null elements to the list. - throw new NullPointerException("Cannot add null element to the list"); - } - //head.next points to the last element; - head.next = new Node<E>(value, head); - size++; - } - - public E remove(int pos) { - if (pos > size || pos < 0) { - //catching errors - throw new IndexOutOfBoundsException("position cannot be greater than size or negative"); - } - //we need to keep track of the element before the element we want to remove we can see why bellow. - Node<E> before = head; - for (int i = 1; i <= pos; i++) { - before = before.next; - } - Node<E> destroy = before.next; - E saved = destroy.value; - // assigning the next reference to the the element following the element we want to remove... the last element will be assigned to the head. - before.next = before.next.next; - // scrubbing - destroy = null; - size--; - return saved; - - } - -} diff --git a/DataStructures/Lists/CursorLinkedList.java b/DataStructures/Lists/CursorLinkedList.java deleted file mode 100644 index 6e1caefc620b..000000000000 --- a/DataStructures/Lists/CursorLinkedList.java +++ /dev/null @@ -1,219 +0,0 @@ -package DataStructures.Lists; - -import java.util.Objects; - -public class CursorLinkedList<T> { - - private static class Node<T> { - - T element; - int next; - - Node(T element, int next) { - this.element = element; - this.next = next; - } - - boolean isEmpty() { - return element == null; - } - } - - - private final int os; - private int head; - private final Node<T>[] cursorSpace; - private int count; - private final static int CURSOR_SPACE_SIZE = 100; - - - { - // init at loading time - cursorSpace = new Node[CURSOR_SPACE_SIZE]; - for (int i = 0; i < CURSOR_SPACE_SIZE; i++) { - cursorSpace[i] = new Node<>(null, i + 1); - } - cursorSpace[CURSOR_SPACE_SIZE - 1].next = 0; - } - - - public CursorLinkedList() { - os = 0; - count = 0; - head = -1; - } - - public void printList() { - - if (head != -1) { - - - int start = head; - while (start != -1) { - - T element = cursorSpace[start].element; - System.out.println(element.toString()); - start = cursorSpace[start].next; - } - } - - } - - - /** - * @return the logical index of the element within the list , not the actual - * index of the [cursorSpace] array - */ - public int indexOf(T element) { - - - Objects.requireNonNull(element); - Node<T> iterator = cursorSpace[head]; - for (int i = 0; i < count; i++) { - if (iterator.element.equals(element)) { - return i; - } - iterator = cursorSpace[iterator.next]; - } - - - return -1; - } - - - /** - * @param position , the logical index of the element , not the actual one - * within the [cursorSpace] array . - * this method should be used to get the index give by indexOf() method. - * @return - */ - - public T get(int position) { - - if (position >= 0 && position < count) { - - int start = head; - int counter = 0; - while (start != -1) { - - T element = cursorSpace[start].element; - if (counter == position) { - return element; - } - - start = cursorSpace[start].next; - counter++; - } - - } - - return null; - } - - - public void removeByIndex(int index) { - - if (index >= 0 && index < count) { - - T element = get(index); - remove(element); - } - - } - - public void remove(T element) { - - - Objects.requireNonNull(element); - - // case element is in the head - T temp_element = cursorSpace[head].element; - int temp_next = cursorSpace[head].next; - if (temp_element.equals(element)) { - free(head); - head = temp_next; - } else { // otherwise cases - - int prev_index = head; - int current_index = cursorSpace[prev_index].next; - - while (current_index != -1) { - - T current_element = cursorSpace[current_index].element; - if (current_element.equals(element)) { - cursorSpace[prev_index].next = cursorSpace[current_index].next; - free(current_index); - break; - } - - prev_index = current_index; - current_index = cursorSpace[prev_index].next; - } - - } - - - count--; - - } - - private void free(int index) { - - Node os_node = cursorSpace[os]; - int os_next = os_node.next; - cursorSpace[os].next = index; - cursorSpace[index].element = null; - cursorSpace[index].next = os_next; - - } - - - public void append(T element) { - - Objects.requireNonNull(element); - int availableIndex = alloc(); - cursorSpace[availableIndex].element = element; - - if (head == -1) { - head = availableIndex; - } - - int iterator = head; - while (cursorSpace[iterator].next != -1) { - iterator = cursorSpace[iterator].next; - } - - cursorSpace[iterator].next = availableIndex; - cursorSpace[availableIndex].next = -1; - - - count++; - } - - /** - * @return the index of the next available node - */ - private int alloc() { - - - //1- get the index at which the os is pointing - int availableNodeIndex = cursorSpace[os].next; - - if (availableNodeIndex == 0) { - throw new OutOfMemoryError(); - } - - //2- make the os point to the next of the @var{availableNodeIndex} - int availableNext = cursorSpace[availableNodeIndex].next; - cursorSpace[os].next = availableNext; - - // this to indicate an end of the list , helpful at testing since any err - // would throw an outOfBoundException - cursorSpace[availableNodeIndex].next = -1; - - return availableNodeIndex; - - } - - -} diff --git a/DataStructures/Lists/DoublyLinkedList.java b/DataStructures/Lists/DoublyLinkedList.java deleted file mode 100644 index 677487e1d2b0..000000000000 --- a/DataStructures/Lists/DoublyLinkedList.java +++ /dev/null @@ -1,248 +0,0 @@ -package DataStructures.Lists; - -/** - * This class implements a DoublyLinkedList. This is done using the classes - * LinkedList and Link. - * <p> - * A linked list is similar to an array, it holds values. However, - * links in a linked list do not have indexes. With a linked list - * you do not need to predetermine it's size as it grows and shrinks - * as it is edited. This is an example of a double ended, doubly - * linked list. Each link references the next link and the previous - * one. - * - * @author Unknown - */ - -public class DoublyLinkedList { - /** - * Head refers to the front of the list - */ - private Link head; - /** - * Tail refers to the back of the list - */ - private Link tail; - - /** - * Default Constructor - */ - public DoublyLinkedList() { - head = null; - tail = null; - } - - /** - * Constructs a list containing the elements of the array - * - * @param array the array whose elements are to be placed into this list - * @throws NullPointerException if the specified collection is null - */ - public DoublyLinkedList(int[] array) { - if (array == null) throw new NullPointerException(); - for (int i : array) { - insertTail(i); - } - } - - /** - * Insert an element at the head - * - * @param x Element to be inserted - */ - public void insertHead(int x) { - Link newLink = new Link(x); // Create a new link with a value attached to it - if (isEmpty()) // Set the first element added to be the tail - tail = newLink; - else - head.previous = newLink; // newLink <-- currenthead(head) - newLink.next = head; // newLink <--> currenthead(head) - head = newLink; // newLink(head) <--> oldhead - } - - /** - * Insert an element at the tail - * - * @param x Element to be inserted - */ - public void insertTail(int x) { - Link newLink = new Link(x); - newLink.next = null; // currentTail(tail) newlink --> - if (isEmpty()) { // Check if there are no elements in list then it adds first element - tail = newLink; - head = tail; - } else { - tail.next = newLink; // currentTail(tail) --> newLink --> - newLink.previous = tail; // currentTail(tail) <--> newLink --> - tail = newLink; // oldTail <--> newLink(tail) --> - } - } - - /** - * Delete the element at the head - * - * @return The new head - */ - public Link deleteHead() { - Link temp = head; - head = head.next; // oldHead <--> 2ndElement(head) - head.previous = null; // oldHead --> 2ndElement(head) nothing pointing at old head so will be removed - if (head == null) - tail = null; - return temp; - } - - /** - * Delete the element at the tail - * - * @return The new tail - */ - public Link deleteTail() { - Link temp = tail; - tail = tail.previous; // 2ndLast(tail) <--> oldTail --> null - tail.next = null; // 2ndLast(tail) --> null - if (tail == null) { - head = null; - } - return temp; - } - - /** - * Delete the element from somewhere in the list - * - * @param x element to be deleted - * @return Link deleted - */ - public void delete(int x) { - Link current = head; - - while (current.value != x) {// Find the position to delete - if (current != tail) { - current = current.next; - } else {// If we reach the tail and the element is still not found - throw new RuntimeException("The element to be deleted does not exist!"); - } - } - - if (current == head) - deleteHead(); - - else if (current == tail) - deleteTail(); - - else { // Before: 1 <--> 2(current) <--> 3 - current.previous.next = current.next; // 1 --> 3 - current.next.previous = current.previous; // 1 <--> 3 - } - } - - /** - * Inserts element and reorders - * - * @param x Element to be added - */ - public void insertOrdered(int x) { - Link newLink = new Link(x); - Link current = head; - while (current != null && x > current.value) // Find the position to insert - current = current.next; - - if (current == head) - insertHead(x); - - else if (current == null) - insertTail(x); - - else { // Before: 1 <--> 2(current) <--> 3 - newLink.previous = current.previous; // 1 <-- newLink - current.previous.next = newLink; // 1 <--> newLink - newLink.next = current; // 1 <--> newLink --> 2(current) <--> 3 - current.previous = newLink; // 1 <--> newLink <--> 2(current) <--> 3 - } - } - - /** - * Returns true if list is empty - * - * @return true if list is empty - */ - public boolean isEmpty() { - return (head == null); - } - - /** - * Prints contents of the list - */ - public void display() { // Prints contents of the list - Link current = head; - while (current != null) { - current.displayLink(); - current = current.next; - } - System.out.println(); - } -} - -/** - * This class is used to implement the nodes of the - * linked list. - * - * @author Unknown - */ -class Link { - /** - * Value of node - */ - public int value; - /** - * This points to the link in front of the new link - */ - public Link next; - /** - * This points to the link behind the new link - */ - public Link previous; - - /** - * Constructor - * - * @param value Value of node - */ - public Link(int value) { - this.value = value; - } - - /** - * Displays the node - */ - public void displayLink() { - System.out.print(value + " "); - } - - /** - * Main Method - * - * @param args Command line arguments - */ - public static void main(String args[]) { - DoublyLinkedList myList = new DoublyLinkedList(); - myList.insertHead(13); - myList.insertHead(7); - myList.insertHead(10); - myList.display(); // <-- 10(head) <--> 7 <--> 13(tail) --> - - myList.insertTail(11); - myList.display(); // <-- 10(head) <--> 7 <--> 13 <--> 11(tail) --> - - myList.deleteTail(); - myList.display(); // <-- 10(head) <--> 7 <--> 13(tail) --> - - myList.delete(7); - myList.display(); // <-- 10(head) <--> 13(tail) --> - - myList.insertOrdered(23); - myList.insertOrdered(67); - myList.insertOrdered(3); - myList.display(); // <-- 3(head) <--> 10 <--> 13 <--> 23 <--> 67(tail) --> - } -} diff --git a/DataStructures/Lists/MergeSortedArrayList.java b/DataStructures/Lists/MergeSortedArrayList.java deleted file mode 100644 index ea32e031f7dc..000000000000 --- a/DataStructures/Lists/MergeSortedArrayList.java +++ /dev/null @@ -1,60 +0,0 @@ -package DataStructures.Lists; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author https://github.com/shellhub - */ - -public class MergeSortedArrayList { - public static void main(String[] args) { - List<Integer> listA = new ArrayList<>(); - List<Integer> listB = new ArrayList<>(); - List<Integer> listC = new ArrayList<>(); - - /* init ListA and List B */ - for (int i = 1; i <= 10; i += 2) { - listA.add(i); /* listA: [1, 3, 5, 7, 9] */ - listB.add(i + 1); /* listB: [2, 4, 6, 8, 10] */ - } - - /* merge listA and listB to listC */ - merge(listA, listB, listC); - - System.out.println("listA: " + listA); - System.out.println("listB: " + listB); - System.out.println("listC: " + listC); - } - - /** - * merge two sorted ArrayList - * - * @param listA the first list to merge - * @param listB the second list to merge - * @param listC the result list after merging - */ - public static void merge(List<Integer> listA, List<Integer> listB, List<Integer> listC) { - int pa = 0; /* the index of listA */ - int pb = 0; /* the index of listB */ - - while (pa < listA.size() && pb < listB.size()) { - if (listA.get(pa) <= listB.get(pb)) { - listC.add(listA.get(pa++)); - } else { - listC.add(listB.get(pb++)); - } - } - - /* copy left element of listA to listC */ - while (pa < listA.size()) { - listC.add(listA.get(pa++)); - } - - /* copy left element of listB to listC */ - while (pb < listB.size()) { - listC.add(listB.get(pb++)); - } - } - -} diff --git a/DataStructures/Lists/Merge_K_SortedLinkedlist.java b/DataStructures/Lists/Merge_K_SortedLinkedlist.java deleted file mode 100644 index bdee79f8f5f7..000000000000 --- a/DataStructures/Lists/Merge_K_SortedLinkedlist.java +++ /dev/null @@ -1,56 +0,0 @@ -package DataStructures.Lists; - -import java.util.Arrays; -import java.util.Comparator; -import java.util.PriorityQueue; - -/** - * @author Arun Pandey (https://github.com/pandeyarun709) - */ -public class Merge_K_SortedLinkedlist { - - /** - * This function merge K sorted LinkedList - * - * @param a array of LinkedList - * @param N size of array - * @return node - */ - Node mergeKList(Node[] a, int N) { - // Min Heap - PriorityQueue<Node> min = new PriorityQueue<>(Comparator.comparingInt(x -> x.data)); - - // adding head of all linkedList in min heap - min.addAll(Arrays.asList(a).subList(0, N)); - - // Make new head among smallest heads in K linkedList - Node head = min.poll(); - min.add(head.next); - Node curr = head; - - // merging LinkedList - while (!min.isEmpty()) { - - Node temp = min.poll(); - curr.next = temp; - curr = temp; - - // Add Node in min Heap only if temp.next is not null - if (temp.next != null) { - min.add(temp.next); - } - } - - return head; - } - - private class Node { - private int data; - private Node next; - - public Node(int d) { - this.data = d; - next = null; - } - } -} diff --git a/DataStructures/Lists/SinglyLinkedList.java b/DataStructures/Lists/SinglyLinkedList.java deleted file mode 100644 index a478d7a5fddb..000000000000 --- a/DataStructures/Lists/SinglyLinkedList.java +++ /dev/null @@ -1,224 +0,0 @@ -package DataStructures.Lists; - -/** - * This class implements a SinglyLinked List. This is done - * using SinglyLinkedList class and a LinkForLinkedList Class. - * <p> - * A linked list is similar to an array, it hold values. - * However, links in a linked list do not have indexes. With - * a linked list you do not need to predetermine it's size as - * it grows and shrinks as it is edited. This is an example of - * a singly linked list. Elements can only be added/removed - * at the head/front of the list. - */ -public class SinglyLinkedList { - /** - * Head refer to the front of the list - */ - private Node head; - - /** - * size of SinglyLinkedList - */ - private int size; - - /** - * init SinglyLinkedList - */ - public SinglyLinkedList() { - head = new Node(0); - size = 0; - } - - /** - * This method inserts an element at the head - * - * @param x Element to be added - */ - public void insertHead(int x) { - insertNth(x, 0); - } - - /** - * insert an element at the tail of list - * - * @param data Element to be added - */ - public void insert(int data) { - insertNth(data, size); - } - - /** - * Inserts a new node at a specified position - * - * @param data data to be stored in a new node - * @param position position at which a new node is to be inserted - */ - public void insertNth(int data, int position) { - checkBounds(position, 0, size); - Node cur = head; - for (int i = 0; i < position; ++i) { - cur = cur.next; - } - Node node = new Node(data); - node.next = cur.next; - cur.next = node; - size++; - } - - /** - * This method deletes an element at the head - * - * @return The element deleted - */ - public void deleteHead() { - deleteNth(0); - } - - /** - * This method deletes an element at the tail - */ - public void delete() { - deleteNth(size - 1); - } - - /** - * This method deletes an element at Nth position - */ - public void deleteNth(int position) { - checkBounds(position, 0, size - 1); - Node cur = head; - for (int i = 0; i < position; ++i) { - cur = cur.next; - } - - Node destroy = cur.next; - cur.next = cur.next.next; - destroy = null; // clear to let GC do its work - - size--; - } - - /** - * @param position to check position - * @param low low index - * @param high high index - * @throws IndexOutOfBoundsException if {@code position} not in range {@code low} to {@code high} - */ - public void checkBounds(int position, int low, int high) { - if (position < low || position > high) { - throw new IndexOutOfBoundsException(position + ""); - } - } - - /** - * clear all nodes in list - */ - public void clear() { - if (size == 0) { - return; - } - Node prev = head.next; - Node cur = prev.next; - while (cur != null) { - prev = null; // clear to let GC do its work - prev = cur; - cur = cur.next; - } - prev = null; - head.next = null; - size = 0; - } - - /** - * Checks if the list is empty - * - * @return true is list is empty - */ - public boolean isEmpty() { - return size == 0; - } - - /** - * Returns the size of the linked list - */ - public int getSize() { - return size; - } - - @Override - public String toString() { - if (size == 0) { - return ""; - } - StringBuilder builder = new StringBuilder(); - Node cur = head.next; - while (cur != null) { - builder.append(cur.value).append("->"); - cur = cur.next; - } - return builder.replace(builder.length() - 2, builder.length(), "").toString(); - } - - /** - * Main method - * - * @param args Command line arguments - */ - public static void main(String args[]) { - SinglyLinkedList myList = new SinglyLinkedList(); - - assert myList.isEmpty(); - - myList.insertHead(5); - myList.insertHead(7); - myList.insertHead(10); - - System.out.println(myList);; // 10 -> 7 -> 5 - - myList.deleteHead(); - - System.out.println(myList);; // 7 -> 5 - - myList.insertNth(11, 2); - - System.out.println(myList);; // 7 -> 5 -> 11 - - myList.deleteNth(1); - - System.out.println(myList);; // 7-> 11 - - myList.clear(); - assert myList.isEmpty(); - - System.out.println(myList); // "" - - } -} - -/** - * This class is the nodes of the SinglyLinked List. - * They consist of a value and a pointer to the node - * after them. - */ -class Node { - /** - * The value of the node - */ - int value; - - /** - * Point to the next node - */ - Node next; - - /** - * Constructor - * - * @param value Value to be put in the node - */ - Node(int value) { - this.value = value; - this.next = null; - } -} diff --git a/DataStructures/Queues/GenericArrayListQueue.java b/DataStructures/Queues/GenericArrayListQueue.java deleted file mode 100644 index 9c2f2658800d..000000000000 --- a/DataStructures/Queues/GenericArrayListQueue.java +++ /dev/null @@ -1,83 +0,0 @@ -package DataStructures.Queues; - -import java.util.ArrayList; - -/** - * This class implements a GenericArrayListQueue. - * <p> - * A GenericArrayListQueue data structure functions the same as any specific-typed queue. - * The GenericArrayListQueue holds elemets of types to-be-specified at runtime. - * The elements that are added first are the first to be removed (FIFO) - * New elements are added to the back/rear of the queue. - * - */ -public class GenericArrayListQueue<T> { - /** - * The generic ArrayList for the queue - * T is the generic element - */ - ArrayList<T> _queue = new ArrayList<T>(); - - /** - * Checks if the queue has elements (not empty) - * - * @return True if the queue has elements. False otherwise. - */ - private boolean hasElements() { - return !_queue.isEmpty(); - } - - /** - * Checks what's at the front of the queue - * - * @return If queue is not empty, element at the front of the queue. Otherwise, null - */ - public T peek() { - T result = null; - if(this.hasElements()) { result = _queue.get(0); } - return result; - } - - /** - * Inserts an element of type T to the queue. - * - * @param element of type T to be added - * @return True if the element was added successfully - */ - public boolean add(T element) { - return _queue.add(element); - } - - /** - * Retrieve what's at the front of the queue - * - * @return If queue is not empty, element retrieved. Otherwise, null - */ - public T poll() { - T result = null; - if(this.hasElements()) { result = _queue.remove(0); } - return result; - } - - /** - * Main method - * - * @param args Command line arguments - */ - public static void main(String[] args) { - GenericArrayListQueue<Integer> queue = new GenericArrayListQueue<Integer>(); - System.out.println("Running..."); - assert queue.peek() == null; - assert queue.poll() == null; - assert queue.add(1) == true; - assert queue.peek() == 1; - assert queue.add(2) == true; - assert queue.peek() == 1; - assert queue.poll() == 1; - assert queue.peek() == 2; - assert queue.poll() == 2; - assert queue.peek() == null; - assert queue.poll() == null; - System.out.println("Finished."); - } -} diff --git a/DataStructures/Queues/LinkedQueue.java b/DataStructures/Queues/LinkedQueue.java deleted file mode 100644 index 1c85937b9a06..000000000000 --- a/DataStructures/Queues/LinkedQueue.java +++ /dev/null @@ -1,169 +0,0 @@ -package DataStructures; - -import java.util.NoSuchElementException; - -public class LinkedQueue { - class Node { - int data; - Node next; - - public Node() { - this(0); - } - - public Node(int data) { - this(data, null); - } - - public Node(int data, Node next) { - this.data = data; - this.next = next; - } - } - - /** - * Front of Queue - */ - private Node front; - - /** - * Rear of Queue - */ - private Node rear; - - /** - * Size of Queue - */ - private int size; - - /** - * Init LinkedQueue - */ - public LinkedQueue() { - front = rear = new Node(); - } - - /** - * Check if queue is empty - * - * @return <tt>true</tt> if queue is empty, otherwise <tt>false</tt> - */ - public boolean isEmpty() { - return size == 0; - } - - /** - * Add element to rear of queue - * - * @param data insert value - * @return <tt>true</tt> if add successfully - */ - public boolean enqueue(int data) { - Node newNode = new Node(data); - rear.next = newNode; - rear = newNode; /* make rear point at last node */ - size++; - return true; - } - - /** - * Remove element at the front of queue - * - * @return element at the front of queue - */ - public int dequeue() { - if (isEmpty()) { - throw new NoSuchElementException("queue is empty"); - } - Node destroy = front.next; - int retValue = destroy.data; - front.next = front.next.next; - destroy = null; /* clear let GC do it's work */ - size--; - - if (isEmpty()) { - front = rear; - } - - return retValue; - } - - /** - * Peek element at the front of queue without removing - * - * @return element at the front - */ - public int peekFront() { - if (isEmpty()) { - throw new NoSuchElementException("queue is empty"); - } - return front.next.data; - } - - /** - * Peek element at the rear of queue without removing - * - * @return element at the front - */ - public int peekRear() { - if (isEmpty()) { - throw new NoSuchElementException("queue is empty"); - } - return rear.data; - } - - /** - * Return size of queue - * - * @return size of queue - */ - public int size() { - return size; - } - - /** - * Clear all nodes in queue - */ - public void clear() { - while (!isEmpty()) { - dequeue(); - } - } - - @Override - public String toString() { - if (isEmpty()) { - return "[]"; - } - StringBuilder builder = new StringBuilder(); - Node cur = front.next; - builder.append("["); - while (cur != null) { - builder.append(cur.data).append(", "); - cur = cur.next; - } - builder.replace(builder.length() - 2, builder.length(), "]"); - return builder.toString(); - } - - /* Driver Code */ - public static void main(String[] args) { - LinkedQueue queue = new LinkedQueue(); - assert queue.isEmpty(); - - queue.enqueue(1); /* 1 */ - queue.enqueue(2); /* 1 2 */ - queue.enqueue(3); /* 1 2 3 */ - System.out.println(queue); /* [1, 2, 3] */ - - assert queue.size() == 3; - assert queue.dequeue() == 1; - assert queue.peekFront() == 2; - assert queue.peekRear() == 3; - - queue.clear(); - assert queue.isEmpty(); - - System.out.println(queue); /* [] */ - } -} diff --git a/DataStructures/Queues/PriorityQueues.java b/DataStructures/Queues/PriorityQueues.java deleted file mode 100644 index b2b959113490..000000000000 --- a/DataStructures/Queues/PriorityQueues.java +++ /dev/null @@ -1,126 +0,0 @@ -package DataStructures.Queues; - -/** - * This class implements a PriorityQueue. - * <p> - * A priority queue adds elements into positions based on their priority. - * So the most important elements are placed at the front/on the top. - * In this example I give numbers that are bigger, a higher priority. - * Queues in theory have no fixed size but when using an array - * implementation it does. - */ -class PriorityQueue { - /** - * The max size of the queue - */ - private int maxSize; - /** - * The array for the queue - */ - private int[] queueArray; - /** - * How many items are in the queue - */ - private int nItems; - - /** - * Constructor - * - * @param size Size of the queue - */ - public PriorityQueue(int size) { - maxSize = size; - queueArray = new int[size]; - nItems = 0; - } - - /** - * Inserts an element in it's appropriate place - * - * @param value Value to be inserted - */ - public void insert(int value) { - if (isFull()) { - throw new RuntimeException("Queue is full"); - } else { - int j = nItems - 1; // index of last element - while (j >= 0 && queueArray[j] > value) { - queueArray[j + 1] = queueArray[j]; // Shifts every element up to make room for insertion - j--; - } - queueArray[j + 1] = value; // Once the correct position is found the value is inserted - nItems++; - } - } - - /** - * Remove the element from the front of the queue - * - * @return The element removed - */ - public int remove() { - return queueArray[--nItems]; - } - - /** - * Checks what's at the front of the queue - * - * @return element at the front of the queue - */ - public int peek() { - return queueArray[nItems - 1]; - } - - /** - * Returns true if the queue is empty - * - * @return true if the queue is empty - */ - public boolean isEmpty() { - return (nItems == 0); - } - - /** - * Returns true if the queue is full - * - * @return true if the queue is full - */ - public boolean isFull() { - return (nItems == maxSize); - } - - /** - * Returns the number of elements in the queue - * - * @return number of elements in the queue - */ - public int getSize() { - return nItems; - } -} - -/** - * This class implements the PriorityQueue class above. - * - * @author Unknown - */ -public class PriorityQueues { - /** - * Main method - * - * @param args Command Line Arguments - */ - public static void main(String[] args) { - PriorityQueue myQueue = new PriorityQueue(4); - myQueue.insert(10); - myQueue.insert(2); - myQueue.insert(5); - myQueue.insert(3); - // [2, 3, 5, 10] Here higher numbers have higher priority, so they are on the top - - for (int i = 3; i >= 0; i--) - System.out.print(myQueue.remove() + " "); // will print the queue in reverse order [10, 5, 3, 2] - - // As you can see, a Priority Queue can be used as a sorting algotithm - } -} diff --git a/DataStructures/Queues/Queues.java b/DataStructures/Queues/Queues.java deleted file mode 100644 index 2f820d70cf71..000000000000 --- a/DataStructures/Queues/Queues.java +++ /dev/null @@ -1,180 +0,0 @@ -package DataStructures.Queues; - -/** - * This implements Queues by using the class Queue. - * <p> - * A queue data structure functions the same as a real world queue. - * The elements that are added first are the first to be removed. - * New elements are added to the back/rear of the queue. - * - */ -class Queue { - /** - * Default initial capacity. - */ - private static final int DEFAULT_CAPACITY = 10; - - /** - * Max size of the queue - */ - private int maxSize; - /** - * The array representing the queue - */ - private int[] queueArray; - /** - * Front of the queue - */ - private int front; - /** - * Rear of the queue - */ - private int rear; - /** - * How many items are in the queue - */ - private int nItems; - - /** - * init with DEFAULT_CAPACITY - */ - public Queue() { - this(DEFAULT_CAPACITY); - } - - /** - * Constructor - * - * @param size Size of the new queue - */ - public Queue(int size) { - maxSize = size; - queueArray = new int[size]; - front = 0; - rear = -1; - nItems = 0; - } - - /** - * Inserts an element at the rear of the queue - * - * @param x element to be added - * @return True if the element was added successfully - */ - public boolean insert(int x) { - if (isFull()) - return false; - // If the back of the queue is the end of the array wrap around to the front - rear = (rear + 1) % maxSize; - queueArray[rear] = x; - nItems++; - return true; - } - - /** - * Remove an element from the front of the queue - * - * @return the new front of the queue - */ - public int remove() { - if (isEmpty()) { - return -1; - } - int temp = queueArray[front]; - front = (front + 1) % maxSize; - nItems--; - return temp; - } - - /** - * Checks what's at the front of the queue - * - * @return element at the front of the queue - */ - public int peekFront() { - return queueArray[front]; - } - - /** - * Checks what's at the rear of the queue - * - * @return element at the rear of the queue - */ - public int peekRear() { - return queueArray[rear]; - } - - /** - * Returns true if the queue is empty - * - * @return true if the queue is empty - */ - public boolean isEmpty() { - return nItems == 0; - } - - /** - * Returns true if the queue is full - * - * @return true if the queue is full - */ - public boolean isFull() { - return nItems == maxSize; - } - - /** - * Returns the number of elements in the queue - * - * @return number of elements in the queue - */ - public int getSize() { - return nItems; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("["); - for (int i = front; ; i = ++i % maxSize) { - sb.append(queueArray[i]).append(", "); - if (i == rear) { - break; - } - } - sb.replace(sb.length() - 2, sb.length(), "]"); - return sb.toString(); - } -} - -/** - * This class is the example for the Queue class - * - * @author Unknown - */ -public class Queues { - /** - * Main method - * - * @param args Command line arguments - */ - public static void main(String[] args) { - Queue myQueue = new Queue(4); - myQueue.insert(10); - myQueue.insert(2); - myQueue.insert(5); - myQueue.insert(3); - // [10(front), 2, 5, 3(rear)] - - System.out.println(myQueue.isFull()); // Will print true - - myQueue.remove(); // Will make 2 the new front, making 10 no longer part of the queue - // [10, 2(front), 5, 3(rear)] - - myQueue.insert(7); // Insert 7 at the rear which will be index 0 because of wrap around - // [7(rear), 2(front), 5, 3] - - System.out.println(myQueue.peekFront()); // Will print 2 - System.out.println(myQueue.peekRear()); // Will print 7 - System.out.println(myQueue.toString()); // Will print [2, 5, 3, 7] - } -} diff --git a/DataStructures/Stacks/BalancedBrackets.java b/DataStructures/Stacks/BalancedBrackets.java deleted file mode 100644 index ea342219cffa..000000000000 --- a/DataStructures/Stacks/BalancedBrackets.java +++ /dev/null @@ -1,81 +0,0 @@ -package DataStructures.Stacks; - -import java.util.Stack; - -/** - * The nested brackets problem is a problem that determines if a sequence of - * brackets are properly nested. A sequence of brackets s is considered properly - * nested if any of the following conditions are true: - s is empty - s has the - * form (U) or [U] or {U} where U is a properly nested string - s has the form - * VW where V and W are properly nested strings For example, the string - * "()()[()]" is properly nested but "[(()]" is not. The function called - * is_balanced takes as input a string S which is a sequence of brackets and - * returns true if S is nested and false otherwise. - * - * @author akshay sharma - * @author <a href="https://github.com/khalil2535">khalil2535<a> - * @author shellhub - */ -class BalancedBrackets { - - /** - * Check if {@code leftBracket} and {@code rightBracket} is paired or not - * - * @param leftBracket left bracket - * @param rightBracket right bracket - * @return {@code true} if {@code leftBracket} and {@code rightBracket} is paired, - * otherwise {@code false} - */ - public static boolean isPaired(char leftBracket, char rightBracket) { - char[][] pairedBrackets = { - {'(', ')'}, - {'[', ']'}, - {'{', '}'}, - {'<', '>'} - }; - for (char[] pairedBracket : pairedBrackets) { - if (pairedBracket[0] == leftBracket && pairedBracket[1] == rightBracket) { - return true; - } - } - return false; - } - - /** - * Check if {@code brackets} is balanced - * - * @param brackets the brackets - * @return {@code true} if {@code brackets} is balanced, otherwise {@code false} - */ - public static boolean isBalanced(String brackets) { - if (brackets == null) { - throw new IllegalArgumentException("brackets is null"); - } - Stack<Character> bracketsStack = new Stack<>(); - for (char bracket : brackets.toCharArray()) { - switch (bracket) { - case '(': - case '[': - case '{': - bracketsStack.push(bracket); - break; - case ')': - case ']': - case '}': - if (!(!bracketsStack.isEmpty() && isPaired(bracketsStack.pop(), bracket))) { - return false; - } - break; - default: /* other character is invalid */ - return false; - } - } - return bracketsStack.isEmpty(); - } - - - public static void main(String[] args) { - assert isBalanced("[()]{}{[()()]()}"); - assert !isBalanced("[(])"); - } -} diff --git a/DataStructures/Stacks/DecimalToAnyUsingStack.java b/DataStructures/Stacks/DecimalToAnyUsingStack.java deleted file mode 100644 index 6e129c32c6d9..000000000000 --- a/DataStructures/Stacks/DecimalToAnyUsingStack.java +++ /dev/null @@ -1,44 +0,0 @@ -package DataStructures.Stacks; - -import java.util.Stack; - -public class DecimalToAnyUsingStack { - public static void main(String[] args) { - assert convert(0, 2).equals("0"); - assert convert(30, 2).equals("11110"); - assert convert(30, 8).equals("36"); - assert convert(30, 10).equals("30"); - assert convert(30, 16).equals("1E"); - } - - /** - * Convert decimal number to another radix - * - * @param number the number to be converted - * @param radix the radix - * @return another radix - * @throws ArithmeticException if <tt>number</tt> or <tt>radius</tt> is invalid - */ - private static String convert(int number, int radix) { - if (radix < 2 || radix > 16) { - throw new ArithmeticException( - String.format("Invalid input -> number:%d,radius:%d", number, radix)); - } - char[] tables = { - '0', '1', '2', '3', '4', - '5', '6', '7', '8', '9', - 'A', 'B', 'C', 'D', 'E', 'F' - }; - Stack<Character> bits = new Stack<>(); - do { - bits.push(tables[number % radix]); - number = number / radix; - } while (number != 0); - - StringBuilder result = new StringBuilder(); - while (!bits.isEmpty()) { - result.append(bits.pop()); - } - return result.toString(); - } -} diff --git a/DataStructures/Stacks/NodeStack.java b/DataStructures/Stacks/NodeStack.java deleted file mode 100644 index c598bb8fcba1..000000000000 --- a/DataStructures/Stacks/NodeStack.java +++ /dev/null @@ -1,183 +0,0 @@ -/** -* Implementation of a stack using nodes. -* Unlimited size, no arraylist. -* -* @author Kyler Smith, 2017 -*/ - - -public class NodeStack<Item> { - - /** - * Entry point for the program. - */ - public static void main(String[] args) { - NodeStack<Integer> Stack = new NodeStack<Integer>(); - - Stack.push(3); - Stack.push(4); - Stack.push(5); - System.out.println("Testing :"); - Stack.print(); // prints : 5 4 3 - - Integer x = Stack.pop(); // x = 5 - Stack.push(1); - Stack.push(8); - Integer y = Stack.peek(); // y = 8 - System.out.println("Testing :"); - Stack.print(); // prints : 8 1 4 3 - - System.out.println("Testing :"); - System.out.println("x : " + x); - System.out.println("y : " + y); - } - - /** - * Information each node should contain. - * @value data : information of the value in the node - * @value head : the head of the stack - * @value next : the next value from this node - * @value previous : the last value from this node - * @value size : size of the stack - */ - private Item data; - private static NodeStack<?> head; - private NodeStack<?> next; - private NodeStack<?> previous; - private static int size = 0; - - - /** - * Constructors for the NodeStack. - */ - public NodeStack() { - } - - private NodeStack(Item item) { - this.data = item; - } - - /** - * Put a value onto the stack. - * - * @param item : value to be put on the stack. - */ - public void push(Item item) { - - NodeStack<Item> newNs = new NodeStack<Item>(item); - - if(this.isEmpty()) { - NodeStack.setHead(new NodeStack<>(item)); - newNs.setNext(null); - newNs.setPrevious(null); - } else { - newNs.setPrevious(NodeStack.head); - NodeStack.head.setNext(newNs); - NodeStack.head = newNs; - } - - NodeStack.setSize(NodeStack.getSize() + 1); - } - - /** - * Value to be taken off the stack. - * - * @return item : value that is returned. - */ - public Item pop() { - - Item item = (Item) NodeStack.head.getData(); - - NodeStack.head = NodeStack.head.getPrevious(); - NodeStack.head.setNext(null); - - NodeStack.setSize(NodeStack.getSize() - 1); - - return item; - } - - /** - * Value that is next to be taken off the stack. - * - * @return item : the next value that would be popped off the stack. - */ - public Item peek() { - return (Item) NodeStack.head.getData(); - } - - /** - * If the stack is empty or there is a value in. - * - * @return boolean : whether or not the stack has anything in it. - */ - public boolean isEmpty() { - return NodeStack.getSize() == 0; - } - - /** - * Returns the size of the stack. - * - * @return int : number of values in the stack. - */ - public int size() { - return NodeStack.getSize(); - } - - /** - * Print the contents of the stack in the following format. - * - * x <- head (next out) - * y - * z <- tail (first in) - * . - * . - * . - * - */ - public void print() { - for(NodeStack<?> n = NodeStack.head; n != null; n = n.previous) { - System.out.println(n.getData().toString()); - } - } - - /** Getters and setters (private) */ - private NodeStack<?> getHead() { - return NodeStack.head; - } - - private static void setHead(NodeStack<?> ns) { - NodeStack.head = ns; - } - - private NodeStack<?> getNext() { - return next; - } - - private void setNext(NodeStack<?> next) { - this.next = next; - } - - private NodeStack<?> getPrevious() { - return previous; - } - - private void setPrevious(NodeStack<?> previous) { - this.previous = previous; - } - - private static int getSize() { - return size; - } - - private static void setSize(int size) { - NodeStack.size = size; - } - - private Item getData() { - return this.data; - } - - private void setData(Item item) { - this.data = item; - } -} diff --git a/DataStructures/Stacks/StackArray.java b/DataStructures/Stacks/StackArray.java deleted file mode 100644 index 88da42cd3dcb..000000000000 --- a/DataStructures/Stacks/StackArray.java +++ /dev/null @@ -1,163 +0,0 @@ -/** - * This class implements a Stack using a regular array. - * <p> - * A stack is exactly what it sounds like. An element gets added to the top of - * the stack and only the element on the top may be removed. This is an example - * of an array implementation of a Stack. So an element can only be added/removed - * from the end of the array. In theory stack have no fixed size, but with an - * array implementation it does. - * - * @author Unknown - */ -public class StackArray { - - /** - * Main method - * - * @param args Command line arguments - */ - public static void main(String[] args) { - // Declare a stack of maximum size 4 - StackArray myStackArray = new StackArray(4); - - // Populate the stack - myStackArray.push(5); - myStackArray.push(8); - myStackArray.push(2); - myStackArray.push(9); - - System.out.println("*********************Stack Array Implementation*********************"); - System.out.println(myStackArray.isEmpty()); // will print false - System.out.println(myStackArray.isFull()); // will print true - System.out.println(myStackArray.peek()); // will print 9 - System.out.println(myStackArray.pop()); // will print 9 - System.out.println(myStackArray.peek()); // will print 2 - } - - /** - * Default initial capacity. - */ - private static final int DEFAULT_CAPACITY = 10; - - /** - * The max size of the Stack - */ - private int maxSize; - - /** - * The array representation of the Stack - */ - private int[] stackArray; - - /** - * The top of the stack - */ - private int top; - - /** - * init Stack with DEFAULT_CAPACITY - */ - public StackArray() { - this(DEFAULT_CAPACITY); - } - - /** - * Constructor - * - * @param size Size of the Stack - */ - public StackArray(int size) { - maxSize = size; - stackArray = new int[maxSize]; - top = -1; - } - - /** - * Adds an element to the top of the stack - * - * @param value The element added - */ - public void push(int value) { - if (!isFull()) { // Checks for a full stack - top++; - stackArray[top] = value; - } else { - resize(maxSize * 2); - push(value); // don't forget push after resizing - } - } - - /** - * Removes the top element of the stack and returns the value you've removed - * - * @return value popped off the Stack - */ - public int pop() { - if (!isEmpty()) { // Checks for an empty stack - return stackArray[top--]; - } - - if (top < maxSize / 4) { - resize(maxSize / 2); - return pop();// don't forget pop after resizing - } else { - System.out.println("The stack is already empty"); - return -1; - } - } - - /** - * Returns the element at the top of the stack - * - * @return element at the top of the stack - */ - public int peek() { - if (!isEmpty()) { // Checks for an empty stack - return stackArray[top]; - } else { - System.out.println("The stack is empty, cant peek"); - return -1; - } - } - - private void resize(int newSize) { - int[] transferArray = new int[newSize]; - - for (int i = 0; i < stackArray.length; i++) { - transferArray[i] = stackArray[i]; - } - // This reference change might be nice in here - stackArray = transferArray; - maxSize = newSize; - } - - /** - * Returns true if the stack is empty - * - * @return true if the stack is empty - */ - public boolean isEmpty() { - return (top == -1); - } - - /** - * Returns true if the stack is full - * - * @return true if the stack is full - */ - public boolean isFull() { - return (top + 1 == maxSize); - } - - /** - * Deletes everything in the Stack - * <p> - * Doesn't delete elements in the array - * but if you call push method after calling - * makeEmpty it will overwrite previous - * values - */ - public void makeEmpty() { // Doesn't delete elements in the array but if you call - top = -1; // push method after calling makeEmpty it will overwrite previous values - } -} diff --git a/DataStructures/Stacks/StackArrayList.java b/DataStructures/Stacks/StackArrayList.java deleted file mode 100644 index afc804440403..000000000000 --- a/DataStructures/Stacks/StackArrayList.java +++ /dev/null @@ -1,95 +0,0 @@ -import java.util.ArrayList; - -/** - * This class implements a Stack using an ArrayList. - * <p> - * A stack is exactly what it sounds like. An element gets added to the top of - * the stack and only the element on the top may be removed. - * <p> - * This is an ArrayList Implementation of a stack, where size is not - * a problem we can extend the stack as much as we want. - * - * @author Unknown - */ -public class StackArrayList { - - /** - * Main method - * - * @param args Command line arguments - */ - public static void main(String[] args) { - - StackArrayList myStackArrayList = new StackArrayList(); - - myStackArrayList.push(5); - myStackArrayList.push(8); - myStackArrayList.push(2); - myStackArrayList.push(9); - - System.out.println("*********************Stack List Implementation*********************"); - System.out.println(myStackArrayList.isEmpty()); // will print false - System.out.println(myStackArrayList.peek()); // will print 9 - System.out.println(myStackArrayList.pop()); // will print 9 - System.out.println(myStackArrayList.peek()); // will print 2 - System.out.println(myStackArrayList.pop()); // will print 2 - } - - /** - * ArrayList representation of the stack - */ - private ArrayList<Integer> stackList; - - /** - * Constructor - */ - public StackArrayList() { - stackList = new ArrayList<>(); - } - - /** - * Adds value to the end of list which - * is the top for stack - * - * @param value value to be added - */ - public void push(int value) { - stackList.add(value); - } - - /** - * Pops last element of list which is indeed - * the top for Stack - * - * @return Element popped - */ - public int pop() { - - if (!isEmpty()) { // checks for an empty Stack - int popValue = stackList.get(stackList.size() - 1); - stackList.remove(stackList.size() - 1); // removes the poped element from the list - return popValue; - } - - System.out.print("The stack is already empty!"); - return -1; - } - - /** - * Checks for empty Stack - * - * @return true if stack is empty - */ - public boolean isEmpty() { - return stackList.isEmpty(); - } - - /** - * Top element of stack - * - * @return top element of stack - */ - public int peek() { - return stackList.get(stackList.size() - 1); - } -} diff --git a/DataStructures/Stacks/StackOfLinkedList.java b/DataStructures/Stacks/StackOfLinkedList.java deleted file mode 100644 index 5a2b8fa08258..000000000000 --- a/DataStructures/Stacks/StackOfLinkedList.java +++ /dev/null @@ -1,142 +0,0 @@ -package DataStructures.Stacks; - -/** - * @author Varun Upadhyay (https://github.com/varunu28) - */ - -// An implementation of a Stack using a Linked List - -class StackOfLinkedList { - - public static void main(String[] args) { - - LinkedListStack stack = new LinkedListStack(); - stack.push(1); - stack.push(2); - stack.push(3); - stack.push(4); - stack.push(5); - - System.out.println(stack); - - System.out.println("Size of stack currently is: " + stack.getSize()); - - assert stack.pop() == 5; - assert stack.pop() == 4; - - System.out.println("Top element of stack currently is: " + stack.peek()); - } -} - -// A node class - -class Node { - public int data; - public Node next; - - public Node(int data) { - this.data = data; - this.next = null; - } -} - -/** - * A class which implements a stack using a linked list - * <p> - * Contains all the stack methods : push, pop, printStack, isEmpty - **/ - -class LinkedListStack { - - /** - * Top of stack - */ - Node head; - - /** - * Size of stack - */ - private int size; - - /** - * Init properties - */ - public LinkedListStack() { - head = null; - size = 0; - } - - /** - * Add element at top - * - * @param x to be added - * @return <tt>true</tt> if add successfully - */ - public boolean push(int x) { - Node newNode = new Node(x); - newNode.next = head; - head = newNode; - size++; - return true; - } - - /** - * Pop element at top of stack - * - * @return element at top of stack - * @throws NoSuchElementException if stack is empty - */ - public int pop() { - if (size == 0) { - throw new NoSuchElementException("Empty stack. Nothing to pop"); - } - Node destroy = head; - head = head.next; - int retValue = destroy.data; - destroy = null; // clear to let GC do it's work - size--; - return retValue; - } - - /** - * Peek element at top of stack - * - * @return element at top of stack - * @throws NoSuchElementException if stack is empty - */ - public int peek() { - if (size == 0) { - throw new NoSuchElementException("Empty stack. Nothing to pop"); - } - return head.data; - } - - @Override - public String toString() { - Node cur = head; - StringBuilder builder = new StringBuilder(); - while (cur != null) { - builder.append(cur.data).append("->"); - cur = cur.next; - } - return builder.replace(builder.length() - 2, builder.length(), "").toString(); - } - - /** - * Check if stack is empty - * - * @return <tt>true</tt> if stack is empty, otherwise <tt>false</tt> - */ - public boolean isEmpty() { - return size == 0; - } - - /** - * Return size of stack - * - * @return size of stack - */ - public int getSize() { - return size; - } -} diff --git a/DataStructures/Trees/AVLTree.java b/DataStructures/Trees/AVLTree.java deleted file mode 100644 index b496be6961fa..000000000000 --- a/DataStructures/Trees/AVLTree.java +++ /dev/null @@ -1,215 +0,0 @@ -package DataStructures.Trees; - -public class AVLTree { - - private Node root; - - private class Node { - private int key; - private int balance; - private int height; - private Node left, right, parent; - - Node(int k, Node p) { - key = k; - parent = p; - } - } - - public boolean insert(int key) { - if (root == null) - root = new Node(key, null); - else { - Node n = root; - Node parent; - while (true) { - if (n.key == key) - return false; - - parent = n; - - boolean goLeft = n.key > key; - n = goLeft ? n.left : n.right; - - if (n == null) { - if (goLeft) { - parent.left = new Node(key, parent); - } else { - parent.right = new Node(key, parent); - } - rebalance(parent); - break; - } - } - } - return true; - } - - private void delete(Node node) { - if (node.left == null && node.right == null) { - if (node.parent == null) root = null; - else { - Node parent = node.parent; - if (parent.left == node) { - parent.left = null; - } else parent.right = null; - rebalance(parent); - } - return; - } - if (node.left != null) { - Node child = node.left; - while (child.right != null) child = child.right; - node.key = child.key; - delete(child); - } else { - Node child = node.right; - while (child.left != null) child = child.left; - node.key = child.key; - delete(child); - } - } - - public void delete(int delKey) { - if (root == null) - return; - Node node = root; - Node child = root; - - while (child != null) { - node = child; - child = delKey >= node.key ? node.right : node.left; - if (delKey == node.key) { - delete(node); - return; - } - } - } - - private void rebalance(Node n) { - setBalance(n); - - if (n.balance == -2) { - if (height(n.left.left) >= height(n.left.right)) - n = rotateRight(n); - else - n = rotateLeftThenRight(n); - - } else if (n.balance == 2) { - if (height(n.right.right) >= height(n.right.left)) - n = rotateLeft(n); - else - n = rotateRightThenLeft(n); - } - - if (n.parent != null) { - rebalance(n.parent); - } else { - root = n; - } - } - - private Node rotateLeft(Node a) { - - Node b = a.right; - b.parent = a.parent; - - a.right = b.left; - - if (a.right != null) - a.right.parent = a; - - b.left = a; - a.parent = b; - - if (b.parent != null) { - if (b.parent.right == a) { - b.parent.right = b; - } else { - b.parent.left = b; - } - } - - setBalance(a, b); - - return b; - } - - private Node rotateRight(Node a) { - - Node b = a.left; - b.parent = a.parent; - - a.left = b.right; - - if (a.left != null) - a.left.parent = a; - - b.right = a; - a.parent = b; - - if (b.parent != null) { - if (b.parent.right == a) { - b.parent.right = b; - } else { - b.parent.left = b; - } - } - - setBalance(a, b); - - return b; - } - - private Node rotateLeftThenRight(Node n) { - n.left = rotateLeft(n.left); - return rotateRight(n); - } - - private Node rotateRightThenLeft(Node n) { - n.right = rotateRight(n.right); - return rotateLeft(n); - } - - private int height(Node n) { - if (n == null) - return -1; - return n.height; - } - - private void setBalance(Node... nodes) { - for (Node n : nodes) { - reheight(n); - n.balance = height(n.right) - height(n.left); - } - } - - public void printBalance() { - printBalance(root); - } - - private void printBalance(Node n) { - if (n != null) { - printBalance(n.left); - System.out.printf("%s ", n.balance); - printBalance(n.right); - } - } - - private void reheight(Node node) { - if (node != null) { - node.height = 1 + Math.max(height(node.left), height(node.right)); - } - } - - public static void main(String[] args) { - AVLTree tree = new AVLTree(); - - System.out.println("Inserting values 1 to 10"); - for (int i = 1; i < 10; i++) - tree.insert(i); - - System.out.print("Printing balance: "); - tree.printBalance(); - } -} diff --git a/DataStructures/Trees/BinaryTree.java b/DataStructures/Trees/BinaryTree.java deleted file mode 100644 index 75d3af18e56b..000000000000 --- a/DataStructures/Trees/BinaryTree.java +++ /dev/null @@ -1,275 +0,0 @@ -package DataStructures.Trees; - -/** - * This entire class is used to build a Binary Tree data structure. - * There is the Node Class and the Tree Class, both explained below. - */ - - -/** - * A binary tree is a data structure in which an element - * has two successors(children). The left child is usually - * smaller than the parent, and the right child is usually - * bigger. - * - * @author Unknown - * - */ -public class BinaryTree { - - /** - * This class implements the nodes that will go on the Binary Tree. - * They consist of the data in them, the node to the left, the node - * to the right, and the parent from which they came from. - * - * @author Unknown - * - */ - class Node { - /** Data for the node */ - public int data; - /** The Node to the left of this one */ - public Node left; - /** The Node to the right of this one */ - public Node right; - /** The parent of this node */ - public Node parent; - - /** - * Constructor of Node - * - * @param value Value to put in the node - */ - public Node(int value) { - data = value; - left = null; - right = null; - parent = null; - } - } - - - /** The root of the Binary Tree */ - private Node root; - - /** - * Constructor - */ - public BinaryTree() { - root = null; - } - - /** - * Method to find a Node with a certain value - * - * @param key Value being looked for - * @return The node if it finds it, otherwise returns the parent - */ - public Node find(int key) { - Node current = root; - while (current != null) { - if (key < current.data) { - if (current.left == null) - return current; //The key isn't exist, returns the parent - current = current.left; - } else if (key > current.data) { - if (current.right == null) - return current; - current = current.right; - } else { // If you find the value return it - return current; - } - } - return null; - } - - /** - * Inserts certain value into the Binary Tree - * - * @param value Value to be inserted - */ - public void put(int value) { - Node newNode = new Node(value); - if (root == null) - root = newNode; - else { - //This will return the soon to be parent of the value you're inserting - Node parent = find(value); - - //This if/else assigns the new node to be either the left or right child of the parent - if (value < parent.data) { - parent.left = newNode; - parent.left.parent = parent; - return; - } else { - parent.right = newNode; - parent.right.parent = parent; - return; - } - } - } - - /** - * Deletes a given value from the Binary Tree - * - * @param value Value to be deleted - * @return If the value was deleted - */ - public boolean remove(int value) { - //temp is the node to be deleted - Node temp = find(value); - - //If the value doesn't exist - if (temp.data != value) - return false; - - //No children - if (temp.right == null && temp.left == null) { - if (temp == root) - root = null; - - //This if/else assigns the new node to be either the left or right child of the parent - else if (temp.parent.data < temp.data) - temp.parent.right = null; - else - temp.parent.left = null; - return true; - } - - //Two children - else if (temp.left != null && temp.right != null) { - Node successor = findSuccessor(temp); - - //The left tree of temp is made the left tree of the successor - successor.left = temp.left; - successor.left.parent = successor; - - //If the successor has a right child, the child's grandparent is it's new parent - if (successor.right != null && successor.parent != temp) { - successor.right.parent = successor.parent; - successor.parent.left = successor.right; - successor.right = temp.right; - successor.right.parent = successor; - } - if (temp == root) { - successor.parent = null; - root = successor; - return true; - } - - //If you're not deleting the root - else { - successor.parent = temp.parent; - - //This if/else assigns the new node to be either the left or right child of the parent - if (temp.parent.data < temp.data) - temp.parent.right = successor; - else - temp.parent.left = successor; - return true; - } - } - //One child - else { - //If it has a right child - if (temp.right != null) { - if (temp == root) { - root = temp.right; - return true; - } - - temp.right.parent = temp.parent; - - //Assigns temp to left or right child - if (temp.data < temp.parent.data) - temp.parent.left = temp.right; - else - temp.parent.right = temp.right; - return true; - } - //If it has a left child - else { - if (temp == root) { - root = temp.left; - return true; - } - - temp.left.parent = temp.parent; - - //Assigns temp to left or right side - if (temp.data < temp.parent.data) - temp.parent.left = temp.left; - else - temp.parent.right = temp.left; - return true; - } - } - } - - /** - * This method finds the Successor to the Node given. - * Move right once and go left down the tree as far as you can - * - * @param n Node that you want to find the Successor of - * @return The Successor of the node - */ - public Node findSuccessor(Node n) { - if (n.right == null) - return n; - Node current = n.right; - Node parent = n.right; - while (current != null) { - parent = current; - current = current.left; - } - return parent; - } - - /** - * Returns the root of the Binary Tree - * - * @return the root of the Binary Tree - */ - public Node getRoot() { - return root; - } - - /** - * Prints leftChild - root - rightChild - * - * @param localRoot The local root of the binary tree - */ - public void inOrder(Node localRoot) { - if (localRoot != null) { - inOrder(localRoot.left); - System.out.print(localRoot.data + " "); - inOrder(localRoot.right); - } - } - - /** - * Prints root - leftChild - rightChild - * - * @param localRoot The local root of the binary tree - */ - public void preOrder(Node localRoot) { - if (localRoot != null) { - System.out.print(localRoot.data + " "); - preOrder(localRoot.left); - preOrder(localRoot.right); - } - } - - /** - * Prints rightChild - leftChild - root - * - * @param localRoot The local root of the binary tree - */ - public void postOrder(Node localRoot) { - if (localRoot != null) { - postOrder(localRoot.left); - postOrder(localRoot.right); - System.out.print(localRoot.data + " "); - } - } -} diff --git a/DataStructures/Trees/LevelOrderTraversal.java b/DataStructures/Trees/LevelOrderTraversal.java deleted file mode 100644 index bcf172495d13..000000000000 --- a/DataStructures/Trees/LevelOrderTraversal.java +++ /dev/null @@ -1,55 +0,0 @@ -package DataStructures.Trees; - -public class LevelOrderTraversal { - - class Node { - int data; - Node left, right; - - public Node(int item) { - data = item; - left = right = null; - } - } - - // Root of the Binary Tree - Node root; - - public LevelOrderTraversal() { - root = null; - } - - /* function to print level order traversal of tree*/ - void printLevelOrder() { - int h = height(root); - int i; - for (i = 1; i <= h; i++) - printGivenLevel(root, i); - } - - /* Compute the "height" of a tree -- the number of - nodes along the longest path from the root node - down to the farthest leaf node.*/ - int height(Node root) { - if (root == null) - return 0; - else { - /** - * Return the height of larger subtree - */ - return Math.max(height(root.left), height(root.right)) + 1; - } - } - - /* Print nodes at the given level */ - void printGivenLevel(Node root, int level) { - if (root == null) - return; - if (level == 1) - System.out.print(root.data + " "); - else if (level > 1) { - printGivenLevel(root.left, level - 1); - printGivenLevel(root.right, level - 1); - } - } -} diff --git a/DataStructures/Trees/LevelOrderTraversalQueue.java b/DataStructures/Trees/LevelOrderTraversalQueue.java deleted file mode 100644 index d43d62d68f5f..000000000000 --- a/DataStructures/Trees/LevelOrderTraversalQueue.java +++ /dev/null @@ -1,48 +0,0 @@ -package DataStructures.Trees; - -import java.util.Queue; -import java.util.LinkedList; - - -/* Class to print Level Order Traversal */ -public class LevelOrderTraversalQueue { - - /* Class to represent Tree node */ - class Node { - int data; - Node left, right; - - public Node(int item) { - data = item; - left = null; - right = null; - } - } - - Node root; - - /* Given a binary tree. Print its nodes in level order - using array for implementing queue */ - void printLevelOrder() { - Queue<Node> queue = new LinkedList<Node>(); - queue.add(root); - while (!queue.isEmpty()) { - - /* poll() removes the present head. - For more information on poll() visit - http://www.tutorialspoint.com/java/util/linkedlist_poll.htm */ - Node tempNode = queue.poll(); - System.out.print(tempNode.data + " "); - - /*Enqueue left child */ - if (tempNode.left != null) { - queue.add(tempNode.left); - } - - /*Enqueue right child */ - if (tempNode.right != null) { - queue.add(tempNode.right); - } - } - } -} \ No newline at end of file diff --git a/DataStructures/Trees/PrintTopViewofTree.java b/DataStructures/Trees/PrintTopViewofTree.java deleted file mode 100644 index 58f611305c51..000000000000 --- a/DataStructures/Trees/PrintTopViewofTree.java +++ /dev/null @@ -1,106 +0,0 @@ -package DataStructures.Trees;// Java program to print top view of Binary tree - -import java.util.HashSet; -import java.util.LinkedList; -import java.util.Queue; - -// Class for a tree node -class TreeNode { - // Members - int key; - TreeNode left, right; - - // Constructor - public TreeNode(int key) { - this.key = key; - left = right = null; - } -} - -// A class to represent a queue item. The queue is used to do Level -// order traversal. Every Queue item contains node and horizontal -// distance of node from root -class QItem { - TreeNode node; - int hd; - - public QItem(TreeNode n, int h) { - node = n; - hd = h; - } -} - -// Class for a Binary Tree -class Tree { - TreeNode root; - - // Constructors - public Tree() { - root = null; - } - - public Tree(TreeNode n) { - root = n; - } - - // This method prints nodes in top view of binary tree - public void printTopView() { - // base case - if (root == null) { - return; - } - - // Creates an empty hashset - HashSet<Integer> set = new HashSet<>(); - - // Create a queue and add root to it - Queue<QItem> Q = new LinkedList<QItem>(); - Q.add(new QItem(root, 0)); // Horizontal distance of root is 0 - - // Standard BFS or level order traversal loop - while (!Q.isEmpty()) { - // Remove the front item and get its details - QItem qi = Q.remove(); - int hd = qi.hd; - TreeNode n = qi.node; - - // If this is the first node at its horizontal distance, - // then this node is in top view - if (!set.contains(hd)) { - set.add(hd); - System.out.print(n.key + " "); - } - - // Enqueue left and right children of current node - if (n.left != null) - Q.add(new QItem(n.left, hd - 1)); - if (n.right != null) - Q.add(new QItem(n.right, hd + 1)); - } - } -} - -// Driver class to test above methods -public class PrintTopViewofTree { - public static void main(String[] args) { - /* Create following Binary Tree - 1 - / \ - 2 3 - \ - 4 - \ - 5 - \ - 6*/ - TreeNode root = new TreeNode(1); - root.left = new TreeNode(2); - root.right = new TreeNode(3); - root.left.right = new TreeNode(4); - root.left.right.right = new TreeNode(5); - root.left.right.right.right = new TreeNode(6); - Tree t = new Tree(root); - System.out.println("Following are nodes in top view of Binary Tree"); - t.printTopView(); - } -} \ No newline at end of file diff --git a/DataStructures/Trees/RedBlackBST.java b/DataStructures/Trees/RedBlackBST.java deleted file mode 100644 index 97a88e9a43de..000000000000 --- a/DataStructures/Trees/RedBlackBST.java +++ /dev/null @@ -1,333 +0,0 @@ -package DataStructures.Trees; - -import java.util.Scanner; - -/** - * @author jack870131 - */ -public class RedBlackBST { - - private final int R = 0; - private final int B = 1; - - private class Node { - - int key = -1, color = B; - Node left = nil, right = nil, p = nil; - - Node(int key) { - this.key = key; - } - } - - private final Node nil = new Node(-1); - private Node root = nil; - - public void printTree(Node node) { - if (node == nil) { - return; - } - printTree(node.left); - System.out.print(((node.color == R) ? " R " : " B ") + "Key: " + node.key + " Parent: " + node.p.key + "\n"); - printTree(node.right); - } - - public void printTreepre(Node node) { - if (node == nil) { - return; - } - System.out.print(((node.color == R) ? " R " : " B ") + "Key: " + node.key + " Parent: " + node.p.key + "\n"); - printTree(node.left); - printTree(node.right); - } - - private Node findNode(Node findNode, Node node) { - if (root == nil) { - return null; - } - if (findNode.key < node.key) { - if (node.left != nil) { - return findNode(findNode, node.left); - } - } else if (findNode.key > node.key) { - if (node.right != nil) { - return findNode(findNode, node.right); - } - } else if (findNode.key == node.key) { - return node; - } - return null; - } - - private void insert(Node node) { - Node temp = root; - if (root == nil) { - root = node; - node.color = B; - node.p = nil; - } else { - node.color = R; - while (true) { - if (node.key < temp.key) { - if (temp.left == nil) { - temp.left = node; - node.p = temp; - break; - } else { - temp = temp.left; - } - } else if (node.key >= temp.key) { - if (temp.right == nil) { - temp.right = node; - node.p = temp; - break; - } else { - temp = temp.right; - } - } - } - fixTree(node); - } - } - - private void fixTree(Node node) { - while (node.p.color == R) { - Node y = nil; - if (node.p == node.p.p.left) { - y = node.p.p.right; - - if (y != nil && y.color == R) { - node.p.color = B; - y.color = B; - node.p.p.color = R; - node = node.p.p; - continue; - } - if (node == node.p.right) { - node = node.p; - rotateLeft(node); - } - node.p.color = B; - node.p.p.color = R; - rotateRight(node.p.p); - } else { - y = node.p.p.left; - if (y != nil && y.color == R) { - node.p.color = B; - y.color = B; - node.p.p.color = R; - node = node.p.p; - continue; - } - if (node == node.p.left) { - node = node.p; - rotateRight(node); - } - node.p.color = B; - node.p.p.color = R; - rotateLeft(node.p.p); - } - } - root.color = B; - } - - void rotateLeft(Node node) { - if (node.p != nil) { - if (node == node.p.left) { - node.p.left = node.right; - } else { - node.p.right = node.right; - } - node.right.p = node.p; - node.p = node.right; - if (node.right.left != nil) { - node.right.left.p = node; - } - node.right = node.right.left; - node.p.left = node; - } else { - Node right = root.right; - root.right = right.left; - right.left.p = root; - root.p = right; - right.left = root; - right.p = nil; - root = right; - } - } - - void rotateRight(Node node) { - if (node.p != nil) { - if (node == node.p.left) { - node.p.left = node.left; - } else { - node.p.right = node.left; - } - - node.left.p = node.p; - node.p = node.left; - if (node.left.right != nil) { - node.left.right.p = node; - } - node.left = node.left.right; - node.p.right = node; - } else { - Node left = root.left; - root.left = root.left.right; - left.right.p = root; - root.p = left; - left.right = root; - left.p = nil; - root = left; - } - } - - void transplant(Node target, Node with) { - if (target.p == nil) { - root = with; - } else if (target == target.p.left) { - target.p.left = with; - } else - target.p.right = with; - with.p = target.p; - } - - Node treeMinimum(Node subTreeRoot) { - while (subTreeRoot.left != nil) { - subTreeRoot = subTreeRoot.left; - } - return subTreeRoot; - } - - boolean delete(Node z) { - if ((z = findNode(z, root)) == null) - return false; - Node x; - Node y = z; - int yorigcolor = y.color; - - if (z.left == nil) { - x = z.right; - transplant(z, z.right); - } else if (z.right == nil) { - x = z.left; - transplant(z, z.left); - } else { - y = treeMinimum(z.right); - yorigcolor = y.color; - x = y.right; - if (y.p == z) - x.p = y; - else { - transplant(y, y.right); - y.right = z.right; - y.right.p = y; - } - transplant(z, y); - y.left = z.left; - y.left.p = y; - y.color = z.color; - } - if (yorigcolor == B) - deleteFixup(x); - return true; - } - - void deleteFixup(Node x) { - while (x != root && x.color == B) { - if (x == x.p.left) { - Node w = x.p.right; - if (w.color == R) { - w.color = B; - x.p.color = R; - rotateLeft(x.p); - w = x.p.right; - } - if (w.left.color == B && w.right.color == B) { - w.color = R; - x = x.p; - continue; - } else if (w.right.color == B) { - w.left.color = B; - w.color = R; - rotateRight(w); - w = x.p.right; - } - if (w.right.color == R) { - w.color = x.p.color; - x.p.color = B; - w.right.color = B; - rotateLeft(x.p); - x = root; - } - } else { - Node w = x.p.left; - if (w.color == R) { - w.color = B; - x.p.color = R; - rotateRight(x.p); - w = x.p.left; - } - if (w.right.color == B && w.left.color == B) { - w.color = R; - x = x.p; - continue; - } else if (w.left.color == B) { - w.right.color = B; - w.color = R; - rotateLeft(w); - w = x.p.left; - } - if (w.left.color == R) { - w.color = x.p.color; - x.p.color = B; - w.left.color = B; - rotateRight(x.p); - x = root; - } - } - } - x.color = B; - } - - public void insertDemo() { - Scanner scan = new Scanner(System.in); - while (true) { - System.out.println("Add items"); - - int item; - Node node; - - item = scan.nextInt(); - while (item != -999) { - node = new Node(item); - insert(node); - item = scan.nextInt(); - } - printTree(root); - System.out.println("Pre order"); - printTreepre(root); - break; - } - } - - public void deleteDemo() { - Scanner scan = new Scanner(System.in); - System.out.println("Delete items"); - int item; - Node node; - item = scan.nextInt(); - node = new Node(item); - System.out.print("Deleting item " + item); - if (delete(node)) { - System.out.print(": deleted!"); - } else { - System.out.print(": does not exist!"); - } - - System.out.println(); - printTree(root); - System.out.println("Pre order"); - printTreepre(root); - } -} \ No newline at end of file diff --git a/DataStructures/Trees/TreeTraversal.java b/DataStructures/Trees/TreeTraversal.java deleted file mode 100644 index 1c5f8a43715b..000000000000 --- a/DataStructures/Trees/TreeTraversal.java +++ /dev/null @@ -1,121 +0,0 @@ -package DataStructures.Trees; - -import java.util.LinkedList; - -/** - * @author Varun Upadhyay (https://github.com/varunu28) - */ - - -// Driver Program -public class TreeTraversal { - public static void main(String[] args) { - Node tree = new Node(5); - tree.insert(3); - tree.insert(2); - tree.insert(7); - tree.insert(4); - tree.insert(6); - tree.insert(8); - - // Prints 5 3 2 4 7 6 8 - System.out.println("Pre order traversal:"); - tree.printPreOrder(); - System.out.println(); - // Prints 2 3 4 5 6 7 8 - System.out.println("In order traversal:"); - tree.printInOrder(); - System.out.println(); - // Prints 2 4 3 6 8 7 5 - System.out.println("Post order traversal:"); - tree.printPostOrder(); - System.out.println(); - // Prints 5 3 7 2 4 6 8 - System.out.println("Level order traversal:"); - tree.printLevelOrder(); - System.out.println(); - } -} - -/** - * The Node class which initializes a Node of a tree - * Consists of all 4 traversal methods: printInOrder, printPostOrder, printPreOrder & printLevelOrder - * printInOrder: LEFT -> ROOT -> RIGHT - * printPreOrder: ROOT -> LEFT -> RIGHT - * printPostOrder: LEFT -> RIGHT -> ROOT - * printLevelOrder: Prints by level (starting at root), from left to right. - */ -class Node { - Node left, right; - int data; - - public Node(int data) { - this.data = data; - } - - public void insert(int value) { - if (value < data) { - if (left == null) { - left = new Node(value); - } else { - left.insert(value); - } - } else { - if (right == null) { - right = new Node(value); - } else { - right.insert(value); - } - } - } - - public void printInOrder() { - if (left != null) { - left.printInOrder(); - } - System.out.print(data + " "); - if (right != null) { - right.printInOrder(); - } - } - - public void printPreOrder() { - System.out.print(data + " "); - if (left != null) { - left.printPreOrder(); - } - if (right != null) { - right.printPreOrder(); - } - } - - public void printPostOrder() { - if (left != null) { - left.printPostOrder(); - } - if (right != null) { - right.printPostOrder(); - } - System.out.print(data + " "); - } - - /** - * O(n) time algorithm. - * Uses O(n) space to store nodes in a queue to aid in traversal. - */ - public void printLevelOrder() { - LinkedList<Node> queue = new LinkedList<>(); - queue.add(this); - while (queue.size() > 0) { - Node head = queue.remove(); - System.out.print(head.data + " "); - // Add children of recently-printed node to queue, if they exist. - if (head.left != null) { - queue.add(head.left); - } - if (head.right != null) { - queue.add(head.right); - } - } - } -} diff --git a/DataStructures/Trees/TrieImp.java b/DataStructures/Trees/TrieImp.java deleted file mode 100644 index 24450f16c2cb..000000000000 --- a/DataStructures/Trees/TrieImp.java +++ /dev/null @@ -1,141 +0,0 @@ -package DataStructures.Trees; - -/** - * Trie Data structure implementation without any libraries - * - * @author Dheeraj Kumar Barnwal (https://github.com/dheeraj92) - */ - -import java.util.Scanner; - -public class TrieImp { - - public class TrieNode { - TrieNode[] child; - boolean end; - - public TrieNode() { - child = new TrieNode[26]; - end = false; - } - } - - private final TrieNode root; - - public TrieImp() { - root = new TrieNode(); - } - - public void insert(String word) { - TrieNode currentNode = root; - for (int i = 0; i < word.length(); i++) { - TrieNode node = currentNode.child[word.charAt(i) - 'a']; - if (node == null) { - node = new TrieNode(); - currentNode.child[word.charAt(i) - 'a'] = node; - } - currentNode = node; - } - currentNode.end = true; - } - - public boolean search(String word) { - TrieNode currentNode = root; - for (int i = 0; i < word.length(); i++) { - char ch = word.charAt(i); - TrieNode node = currentNode.child[ch - 'a']; - if (node == null) { - return false; - } - currentNode = node; - } - return currentNode.end; - } - - public boolean delete(String word) { - TrieNode currentNode = root; - for (int i = 0; i < word.length(); i++) { - char ch = word.charAt(i); - TrieNode node = currentNode.child[ch - 'a']; - if (node == null) { - return false; - } - currentNode = node; - } - if (currentNode.end == true) { - currentNode.end = false; - return true; - } - return false; - } - - public static void sop(String print) { - System.out.println(print); - } - - /** - * Regex to check if word contains only a-z character - */ - public static boolean isValid(String word) { - return word.matches("^[a-z]+$"); - } - - public static void main(String[] args) { - TrieImp obj = new TrieImp(); - String word; - @SuppressWarnings("resource") - Scanner scan = new Scanner(System.in); - sop("string should contain only a-z character for all operation"); - while (true) { - sop("1. Insert\n2. Search\n3. Delete\n4. Quit"); - try { - int t = scan.nextInt(); - switch (t) { - case 1: - word = scan.next(); - if (isValid(word)) - obj.insert(word); - else - sop("Invalid string: allowed only a-z"); - break; - case 2: - word = scan.next(); - boolean resS = false; - if (isValid(word)) - resS = obj.search(word); - else - sop("Invalid string: allowed only a-z"); - if (resS) - sop("word found"); - else - sop("word not found"); - break; - case 3: - word = scan.next(); - boolean resD = false; - if (isValid(word)) - resD = obj.delete(word); - else - sop("Invalid string: allowed only a-z"); - if (resD) { - sop("word got deleted successfully"); - } else { - sop("word not found"); - } - break; - case 4: - sop("Quit successfully"); - System.exit(1); - break; - default: - sop("Input int from 1-4"); - break; - } - } catch (Exception e) { - String badInput = scan.next(); - sop("This is bad input: " + badInput); - } - } - } - -} diff --git a/DataStructures/Trees/ValidBSTOrNot.java b/DataStructures/Trees/ValidBSTOrNot.java deleted file mode 100644 index a1a737fe4fe9..000000000000 --- a/DataStructures/Trees/ValidBSTOrNot.java +++ /dev/null @@ -1,45 +0,0 @@ -package DataStructures.Trees; - -public class ValidBSTOrNot { - - class Node { - int data; - Node left, right; - - public Node(int item) { - data = item; - left = right = null; - } - } - - //Root of the Binary Tree - Node root; - - /* can give min and max value according to your code or - can write a function to find min and max value of tree. */ - - /* returns true if given search tree is binary - search tree (efficient version) */ - boolean isBST() { - return isBSTUtil(root, Integer.MIN_VALUE, - Integer.MAX_VALUE); - } - - /* Returns true if the given tree is a BST and its - values are >= min and <= max. */ - boolean isBSTUtil(Node node, int min, int max) { - /* an empty tree is BST */ - if (node == null) - return true; - - /* false if this node violates the min/max constraints */ - if (node.data < min || node.data > max) - return false; - - /* otherwise check the subtrees recursively - tightening the min/max constraints */ - // Allow only distinct values - return (isBSTUtil(node.left, min, node.data - 1) && - isBSTUtil(node.right, node.data + 1, max)); - } -} \ No newline at end of file diff --git a/DynamicProgramming/CoinChange.java b/DynamicProgramming/CoinChange.java deleted file mode 100644 index 7e4e6181ccc9..000000000000 --- a/DynamicProgramming/CoinChange.java +++ /dev/null @@ -1,79 +0,0 @@ -package DynamicProgramming; - -/** - * @author Varun Upadhyay (https://github.com/varunu28) - */ -public class CoinChange { - - // Driver Program - public static void main(String[] args) { - - int amount = 12; - int[] coins = {2, 4, 5}; - - System.out.println("Number of combinations of getting change for " + amount + " is: " + change(coins, amount)); - System.out.println("Minimum number of coins required for amount :" + amount + " is: " + minimumCoins(coins, amount)); - - } - - /** - * This method finds the number of combinations of getting change for a given amount and change coins - * - * @param coins The list of coins - * @param amount The amount for which we need to find the change - * Finds the number of combinations of change - **/ - public static int change(int[] coins, int amount) { - - int[] combinations = new int[amount + 1]; - combinations[0] = 1; - - for (int coin : coins) { - for (int i = coin; i < amount + 1; i++) { - combinations[i] += combinations[i - coin]; - } - // Uncomment the below line to see the state of combinations for each coin - // printAmount(combinations); - } - - return combinations[amount]; - } - - /** - * This method finds the minimum number of coins needed for a given amount. - * - * @param coins The list of coins - * @param amount The amount for which we need to find the minimum number of coins. - * Finds the the minimum number of coins that make a given value. - **/ - public static int minimumCoins(int[] coins, int amount) { - //minimumCoins[i] will store the minimum coins needed for amount i - int[] minimumCoins = new int[amount + 1]; - - minimumCoins[0] = 0; - - for (int i = 1; i <= amount; i++) { - minimumCoins[i] = Integer.MAX_VALUE; - } - for (int i = 1; i <= amount; i++) { - for (int coin : coins) { - if (coin <= i) { - int sub_res = minimumCoins[i - coin]; - if (sub_res != Integer.MAX_VALUE && sub_res + 1 < minimumCoins[i]) - minimumCoins[i] = sub_res + 1; - } - } - } - // Uncomment the below line to see the state of combinations for each coin - //printAmount(minimumCoins); - return minimumCoins[amount]; - } - - // A basic print method which prints all the contents of the array - public static void printAmount(int[] arr) { - for (int i = 0; i < arr.length; i++) { - System.out.print(arr[i] + " "); - } - System.out.println(); - } -} \ No newline at end of file diff --git a/DynamicProgramming/EditDistance.java b/DynamicProgramming/EditDistance.java deleted file mode 100644 index 210469b84108..000000000000 --- a/DynamicProgramming/EditDistance.java +++ /dev/null @@ -1,78 +0,0 @@ -package DynamicProgramming; - -/** - * A DynamicProgramming based solution for Edit Distance problem In Java - * Description of Edit Distance with an Example: - * <p> - * Edit distance is a way of quantifying how dissimilar two strings (e.g., words) are to one another, - * by counting the minimum number of operations required to transform one string into the other. The - * distance operations are the removal, insertion, or substitution of a character in the string. - * <p> - * <p> - * The Distance between "kitten" and "sitting" is 3. A minimal edit script that transforms the former into the latter is: - * <p> - * kitten → sitten (substitution of "s" for "k") - * sitten → sittin (substitution of "i" for "e") - * sittin → sitting (insertion of "g" at the end). - * - * @author SUBHAM SANGHAI - **/ - -import java.util.Scanner; - -public class EditDistance { - - public static int minDistance(String word1, String word2) { - int len1 = word1.length(); - int len2 = word2.length(); - // len1+1, len2+1, because finally return dp[len1][len2] - int[][] dp = new int[len1 + 1][len2 + 1]; - /* If second string is empty, the only option is to - insert all characters of first string into second*/ - for (int i = 0; i <= len1; i++) { - dp[i][0] = i; - } - /* If first string is empty, the only option is to - insert all characters of second string into first*/ - for (int j = 0; j <= len2; j++) { - dp[0][j] = j; - } - //iterate though, and check last char - for (int i = 0; i < len1; i++) { - char c1 = word1.charAt(i); - for (int j = 0; j < len2; j++) { - char c2 = word2.charAt(j); - //if last two chars equal - if (c1 == c2) { - //update dp value for +1 length - dp[i + 1][j + 1] = dp[i][j]; - } else { - /* if two characters are different , - then take the minimum of the various operations(i.e insertion,removal,substitution)*/ - int replace = dp[i][j] + 1; - int insert = dp[i][j + 1] + 1; - int delete = dp[i + 1][j] + 1; - - int min = replace > insert ? insert : replace; - min = delete > min ? min : delete; - dp[i + 1][j + 1] = min; - } - } - } - /* return the final answer , after traversing through both the strings*/ - return dp[len1][len2]; - } - - - public static void main(String[] args) { - Scanner input = new Scanner(System.in); - String s1, s2; - System.out.println("Enter the First String"); - s1 = input.nextLine(); - System.out.println("Enter the Second String"); - s2 = input.nextLine(); - //ans stores the final Edit Distance between the two strings - int ans = minDistance(s1, s2); - System.out.println("The minimum Edit Distance between \"" + s1 + "\" and \"" + s2 + "\" is " + ans); - } -} diff --git a/DynamicProgramming/EggDropping.java b/DynamicProgramming/EggDropping.java deleted file mode 100644 index f53712bd9304..000000000000 --- a/DynamicProgramming/EggDropping.java +++ /dev/null @@ -1,49 +0,0 @@ -package DynamicProgramming; - -/** - * DynamicProgramming solution for the Egg Dropping Puzzle - */ -public class EggDropping { - - // min trials with n eggs and m floors - - private static int minTrials(int n, int m) { - - int[][] eggFloor = new int[n + 1][m + 1]; - int result, x; - - for (int i = 1; i <= n; i++) { - eggFloor[i][0] = 0; // Zero trial for zero floor. - eggFloor[i][1] = 1; // One trial for one floor - } - - // j trials for only 1 egg - - for (int j = 1; j <= m; j++) - eggFloor[1][j] = j; - - // Using bottom-up approach in DP - - for (int i = 2; i <= n; i++) { - for (int j = 2; j <= m; j++) { - eggFloor[i][j] = Integer.MAX_VALUE; - for (x = 1; x <= j; x++) { - result = 1 + Math.max(eggFloor[i - 1][x - 1], eggFloor[i][j - x]); - - // choose min of all values for particular x - if (result < eggFloor[i][j]) - eggFloor[i][j] = result; - } - } - } - - return eggFloor[n][m]; - } - - public static void main(String args[]) { - int n = 2, m = 4; - // result outputs min no. of trials in worst case for n eggs and m floors - int result = minTrials(n, m); - System.out.println(result); - } -} diff --git a/DynamicProgramming/Fibonacci.java b/DynamicProgramming/Fibonacci.java deleted file mode 100644 index 112a014a378a..000000000000 --- a/DynamicProgramming/Fibonacci.java +++ /dev/null @@ -1,99 +0,0 @@ -package DynamicProgramming; - -import java.util.HashMap; -import java.util.Map; -import java.util.Scanner; - -/** - * @author Varun Upadhyay (https://github.com/varunu28) - */ - -public class Fibonacci { - - private static Map<Integer, Integer> map = new HashMap<>(); - - - public static void main(String[] args) { - - // Methods all returning [0, 1, 1, 2, 3, 5, ...] for n = [0, 1, 2, 3, 4, 5, ...] - Scanner sc = new Scanner(System.in); - int n = sc.nextInt(); - - System.out.println(fibMemo(n)); - System.out.println(fibBotUp(n)); - System.out.println(fibOptimized(n)); - } - - /** - * This method finds the nth fibonacci number using memoization technique - * - * @param n The input n for which we have to determine the fibonacci number - * Outputs the nth fibonacci number - **/ - public static int fibMemo(int n) { - if (map.containsKey(n)) { - return map.get(n); - } - - int f; - - if (n <= 1) { - f = n; - } else { - f = fibMemo(n - 1) + fibMemo(n - 2); - map.put(n, f); - } - return f; - } - - /** - * This method finds the nth fibonacci number using bottom up - * - * @param n The input n for which we have to determine the fibonacci number - * Outputs the nth fibonacci number - **/ - public static int fibBotUp(int n) { - - Map<Integer, Integer> fib = new HashMap<>(); - - for (int i = 0; i <= n; i++) { - int f; - if (i <= 1) { - f = i; - } else { - f = fib.get(i - 1) + fib.get(i - 2); - } - fib.put(i, f); - } - - return fib.get(n); - } - - - /** - * This method finds the nth fibonacci number using bottom up - * - * @param n The input n for which we have to determine the fibonacci number - * Outputs the nth fibonacci number - * <p> - * This is optimized version of Fibonacci Program. Without using Hashmap and recursion. - * It saves both memory and time. - * Space Complexity will be O(1) - * Time Complexity will be O(n) - * <p> - * Whereas , the above functions will take O(n) Space. - * @author Shoaib Rayeen (https://github.com/shoaibrayeen) - **/ - public static int fibOptimized(int n) { - if (n == 0) { - return 0; - } - int prev = 0, res = 1, next; - for (int i = 2; i <= n; i++) { - next = prev + res; - prev = res; - res = next; - } - return res; - } -} diff --git a/DynamicProgramming/FordFulkerson.java b/DynamicProgramming/FordFulkerson.java deleted file mode 100644 index 7a162fbb06d1..000000000000 --- a/DynamicProgramming/FordFulkerson.java +++ /dev/null @@ -1,72 +0,0 @@ -package DynamicProgramming; - -import java.util.LinkedList; -import java.util.Queue; -import java.util.Vector; - -public class FordFulkerson { - final static int INF = 987654321; - // edges - static int V; - static int[][] capacity, flow; - - public static void main(String[] args) { - System.out.println("V : 6"); - V = 6; - capacity = new int[V][V]; - - capacity[0][1] = 12; - capacity[0][3] = 13; - capacity[1][2] = 10; - capacity[2][3] = 13; - capacity[2][4] = 3; - capacity[2][5] = 15; - capacity[3][2] = 7; - capacity[3][4] = 15; - capacity[4][5] = 17; - - System.out.println("Max capacity in networkFlow : " + networkFlow(0, 5)); - } - - private static int networkFlow(int source, int sink) { - flow = new int[V][V]; - int totalFlow = 0; - while (true) { - Vector<Integer> parent = new Vector<>(V); - for (int i = 0; i < V; i++) - parent.add(-1); - Queue<Integer> q = new LinkedList<>(); - parent.set(source, source); - q.add(source); - while (!q.isEmpty() && parent.get(sink) == -1) { - int here = q.peek(); - q.poll(); - for (int there = 0; there < V; ++there) - if (capacity[here][there] - flow[here][there] > 0 && parent.get(there) == -1) { - q.add(there); - parent.set(there, here); - } - } - if (parent.get(sink) == -1) - break; - - int amount = INF; - String printer = "path : "; - StringBuilder sb = new StringBuilder(); - for (int p = sink; p != source; p = parent.get(p)) { - amount = Math.min(capacity[parent.get(p)][p] - flow[parent.get(p)][p], amount); - sb.append(p + "-"); - } - sb.append(source); - for (int p = sink; p != source; p = parent.get(p)) { - flow[parent.get(p)][p] += amount; - flow[p][parent.get(p)] -= amount; - } - totalFlow += amount; - printer += sb.reverse() + " / max flow : " + totalFlow; - System.out.println(printer); - } - - return totalFlow; - } -} diff --git a/DynamicProgramming/KadaneAlgorithm.java b/DynamicProgramming/KadaneAlgorithm.java deleted file mode 100644 index 8d77c7a7c556..000000000000 --- a/DynamicProgramming/KadaneAlgorithm.java +++ /dev/null @@ -1,55 +0,0 @@ -package DynamicProgramming; - -import java.util.Scanner; - -/** - * Program to implement Kadane’s Algorithm to - * calculate maximum contiguous subarray sum of an array - * Time Complexity: O(n) - * - * @author Nishita Aggarwal - */ - -public class KadaneAlgorithm { - - /** - * This method implements Kadane's Algorithm - * - * @param arr The input array - * @return The maximum contiguous subarray sum of the array - */ - static int largestContiguousSum(int arr[]) { - int i, len = arr.length, cursum = 0, maxsum = Integer.MIN_VALUE; - if (len == 0) //empty array - return 0; - for (i = 0; i < len; i++) { - cursum += arr[i]; - if (cursum > maxsum) { - maxsum = cursum; - } - if (cursum <= 0) { - cursum = 0; - } - } - return maxsum; - } - - /** - * Main method - * - * @param args Command line arguments - */ - public static void main(String[] args) { - Scanner sc = new Scanner(System.in); - int n, arr[], i; - n = sc.nextInt(); - arr = new int[n]; - for (i = 0; i < n; i++) { - arr[i] = sc.nextInt(); - } - int maxContSum = largestContiguousSum(arr); - System.out.println(maxContSum); - sc.close(); - } - -} diff --git a/DynamicProgramming/Knapsack.java b/DynamicProgramming/Knapsack.java deleted file mode 100644 index 1652dc63b14d..000000000000 --- a/DynamicProgramming/Knapsack.java +++ /dev/null @@ -1,39 +0,0 @@ -package DynamicProgramming; - -/** - * A DynamicProgramming based solution for 0-1 Knapsack problem - */ - -public class Knapsack { - - private static int knapSack(int W, int wt[], int val[], int n) throws IllegalArgumentException { - if(wt == null || val == null) - throw new IllegalArgumentException(); - int i, w; - int rv[][] = new int[n + 1][W + 1]; //rv means return value - - // Build table rv[][] in bottom up manner - for (i = 0; i <= n; i++) { - for (w = 0; w <= W; w++) { - if (i == 0 || w == 0) - rv[i][w] = 0; - else if (wt[i - 1] <= w) - rv[i][w] = Math.max(val[i - 1] + rv[i - 1][w - wt[i - 1]], rv[i - 1][w]); - else - rv[i][w] = rv[i - 1][w]; - } - } - - return rv[n][W]; - } - - - // Driver program to test above function - public static void main(String args[]) { - int val[] = new int[]{50, 100, 130}; - int wt[] = new int[]{10, 20, 40}; - int W = 50; - int n = val.length; - System.out.println(knapSack(W, wt, val, n)); - } -} diff --git a/DynamicProgramming/LevenshteinDistance.java b/DynamicProgramming/LevenshteinDistance.java deleted file mode 100644 index c4d53143f80b..000000000000 --- a/DynamicProgramming/LevenshteinDistance.java +++ /dev/null @@ -1,56 +0,0 @@ -package DynamicProgramming; - -/** - * @author Kshitij VERMA (github.com/kv19971) - * LEVENSHTEIN DISTANCE dyamic programming implementation to show the difference between two strings (https://en.wikipedia.org/wiki/Levenshtein_distance) - */ - -public class LevenshteinDistance { - private static int minimum(int a, int b, int c) { - if (a < b && a < c) { - return a; - } else if (b < a && b < c) { - return b; - } else { - return c; - } - } - - private static int calculate_distance(String a, String b) { - int len_a = a.length() + 1; - int len_b = b.length() + 1; - int[][] distance_mat = new int[len_a][len_b]; - for (int i = 0; i < len_a; i++) { - distance_mat[i][0] = i; - } - for (int j = 0; j < len_b; j++) { - distance_mat[0][j] = j; - } - for (int i = 0; i < len_a; i++) { - for (int j = 0; j < len_b; j++) { - int cost; - if (a.charAt(i) == b.charAt(j)) { - cost = 0; - } else { - cost = 1; - } - distance_mat[i][j] = minimum(distance_mat[i - 1][j], distance_mat[i - 1][j - 1], distance_mat[i][j - 1]) + cost; - - - } - - } - return distance_mat[len_a - 1][len_b - 1]; - - } - - public static void main(String[] args) { - String a = ""; // enter your string here - String b = ""; // enter your string here - - System.out.print("Levenshtein distance between " + a + " and " + b + " is: "); - System.out.println(calculate_distance(a, b)); - - - } -} diff --git a/DynamicProgramming/LongestCommonSubsequence.java b/DynamicProgramming/LongestCommonSubsequence.java deleted file mode 100644 index fad223748bdd..000000000000 --- a/DynamicProgramming/LongestCommonSubsequence.java +++ /dev/null @@ -1,68 +0,0 @@ -package DynamicProgramming; - -class LongestCommonSubsequence { - - public static String getLCS(String str1, String str2) { - - //At least one string is null - if (str1 == null || str2 == null) - return null; - - //At least one string is empty - if (str1.length() == 0 || str2.length() == 0) - return ""; - - String[] arr1 = str1.split(""); - String[] arr2 = str2.split(""); - - //lcsMatrix[i][j] = LCS of first i elements of arr1 and first j characters of arr2 - int[][] lcsMatrix = new int[arr1.length + 1][arr2.length + 1]; - - for (int i = 0; i < arr1.length + 1; i++) - lcsMatrix[i][0] = 0; - for (int j = 1; j < arr2.length + 1; j++) - lcsMatrix[0][j] = 0; - for (int i = 1; i < arr1.length + 1; i++) { - for (int j = 1; j < arr2.length + 1; j++) { - if (arr1[i - 1].equals(arr2[j - 1])) { - lcsMatrix[i][j] = lcsMatrix[i - 1][j - 1] + 1; - } else { - lcsMatrix[i][j] = lcsMatrix[i - 1][j] > lcsMatrix[i][j - 1] ? lcsMatrix[i - 1][j] : lcsMatrix[i][j - 1]; - } - } - } - return lcsString(str1, str2, lcsMatrix); - } - - public static String lcsString(String str1, String str2, int[][] lcsMatrix) { - StringBuilder lcs = new StringBuilder(); - int i = str1.length(), - j = str2.length(); - while (i > 0 && j > 0) { - if (str1.charAt(i - 1) == str2.charAt(j - 1)) { - lcs.append(str1.charAt(i - 1)); - i--; - j--; - } else if (lcsMatrix[i - 1][j] > lcsMatrix[i][j - 1]) { - i--; - } else { - j--; - } - } - return lcs.reverse().toString(); - } - - public static void main(String[] args) { - String str1 = "DSGSHSRGSRHTRD"; - String str2 = "DATRGAGTSHS"; - String lcs = getLCS(str1, str2); - - //Print LCS - if (lcs != null) { - System.out.println("String 1: " + str1); - System.out.println("String 2: " + str2); - System.out.println("LCS: " + lcs); - System.out.println("LCS length: " + lcs.length()); - } - } -} \ No newline at end of file diff --git a/DynamicProgramming/LongestIncreasingSubsequence.java b/DynamicProgramming/LongestIncreasingSubsequence.java deleted file mode 100644 index 2b07e040db13..000000000000 --- a/DynamicProgramming/LongestIncreasingSubsequence.java +++ /dev/null @@ -1,64 +0,0 @@ -package DynamicProgramming; - -import java.util.Scanner; - -/** - * @author Afrizal Fikri (https://github.com/icalF) - */ -public class LongestIncreasingSubsequence { - public static void main(String[] args) { - - Scanner sc = new Scanner(System.in); - int n = sc.nextInt(); - - int ar[] = new int[n]; - for (int i = 0; i < n; i++) { - ar[i] = sc.nextInt(); - } - - System.out.println(LIS(ar)); - } - - private static int upperBound(int[] ar, int l, int r, int key) { - while (l < r - 1) { - int m = (l + r) / 2; - if (ar[m] >= key) - r = m; - else - l = m; - } - - return r; - } - - private static int LIS(int[] array) { - int N = array.length; - if (N == 0) - return 0; - - int[] tail = new int[N]; - - // always points empty slot in tail - int length = 1; - - tail[0] = array[0]; - for (int i = 1; i < N; i++) { - - // new smallest value - if (array[i] < tail[0]) - tail[0] = array[i]; - - // array[i] extends largest subsequence - else if (array[i] > tail[length - 1]) - tail[length++] = array[i]; - - // array[i] will become end candidate of an existing subsequence or - // Throw away larger elements in all LIS, to make room for upcoming grater elements than array[i] - // (and also, array[i] would have already appeared in one of LIS, identify the location and replace it) - else - tail[upperBound(tail, -1, length - 1, array[i])] = array[i]; - } - - return length; - } -} \ No newline at end of file diff --git a/DynamicProgramming/LongestValidParentheses.java b/DynamicProgramming/LongestValidParentheses.java deleted file mode 100644 index bc5f18ae0593..000000000000 --- a/DynamicProgramming/LongestValidParentheses.java +++ /dev/null @@ -1,61 +0,0 @@ -package DynamicProgramming; - -import java.util.Scanner; - -/** - * Given a string containing just the characters '(' and ')', find the length of - * the longest valid (well-formed) parentheses substring. - * - * @author Libin Yang (https://github.com/yanglbme) - * @since 2018/10/5 - */ - -public class LongestValidParentheses { - - public static int getLongestValidParentheses(String s) { - if (s == null || s.length() < 2) { - return 0; - } - char[] chars = s.toCharArray(); - int n = chars.length; - int[] res = new int[n]; - res[0] = 0; - res[1] = chars[1] == ')' && chars[0] == '(' ? 2 : 0; - - int max = res[1]; - - for (int i = 2; i < n; ++i) { - if (chars[i] == ')') { - if (chars[i - 1] == '(') { - res[i] = res[i - 2] + 2; - } else { - int index = i - res[i - 1] - 1; - if (index >= 0 && chars[index] == '(') { - // ()(()) - res[i] = res[i - 1] + 2 + (index - 1 >= 0 ? res[index - 1] : 0); - } - } - } - max = Math.max(max, res[i]); - } - - return max; - - } - - public static void main(String[] args) { - Scanner sc = new Scanner(System.in); - - while (true) { - String str = sc.nextLine(); - if ("quit".equals(str)) { - break; - } - int len = getLongestValidParentheses(str); - System.out.println(len); - - } - - sc.close(); - } -} diff --git a/DynamicProgramming/MatrixChainMultiplication.java b/DynamicProgramming/MatrixChainMultiplication.java deleted file mode 100644 index 66b2a35824e2..000000000000 --- a/DynamicProgramming/MatrixChainMultiplication.java +++ /dev/null @@ -1,136 +0,0 @@ -package DynamicProgramming; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Scanner; - -public class MatrixChainMultiplication { - private static Scanner scan = new Scanner(System.in); - private static ArrayList<Matrix> mArray = new ArrayList<>(); - private static int size; - private static int[][] m; - private static int[][] s; - private static int[] p; - - public static void main(String[] args) { - int count = 1; - while (true) { - String[] mSize = input("input size of matrix A(" + count + ") ( ex. 10 20 ) : "); - int col = Integer.parseInt(mSize[0]); - if (col == 0) break; - int row = Integer.parseInt(mSize[1]); - - Matrix matrix = new Matrix(count, col, row); - mArray.add(matrix); - count++; - } - for (Matrix m : mArray) { - System.out.format("A(%d) = %2d x %2d\n", m.count(), m.col(), m.row()); - } - - size = mArray.size(); - m = new int[size + 1][size + 1]; - s = new int[size + 1][size + 1]; - p = new int[size + 1]; - - for (int i = 0; i < size + 1; i++) { - Arrays.fill(m[i], -1); - Arrays.fill(s[i], -1); - } - - for (int i = 0; i < p.length; i++) { - p[i] = i == 0 ? mArray.get(i).col() : mArray.get(i - 1).row(); - } - - matrixChainOrder(); - for (int i = 0; i < size; i++) { - System.out.print("-------"); - } - System.out.println(); - printArray(m); - for (int i = 0; i < size; i++) { - System.out.print("-------"); - } - System.out.println(); - printArray(s); - for (int i = 0; i < size; i++) { - System.out.print("-------"); - } - System.out.println(); - - System.out.println("Optimal solution : " + m[1][size]); - System.out.print("Optimal parens : "); - printOptimalParens(1, size); - } - - private static void printOptimalParens(int i, int j) { - if (i == j) { - System.out.print("A" + i); - } else { - System.out.print("("); - printOptimalParens(i, s[i][j]); - printOptimalParens(s[i][j] + 1, j); - System.out.print(")"); - } - } - - private static void printArray(int[][] array) { - for (int i = 1; i < size + 1; i++) { - for (int j = 1; j < size + 1; j++) { - System.out.print(String.format("%7d", array[i][j])); - } - System.out.println(); - } - } - - private static void matrixChainOrder() { - for (int i = 1; i < size + 1; i++) { - m[i][i] = 0; - } - - for (int l = 2; l < size + 1; l++) { - for (int i = 1; i < size - l + 2; i++) { - int j = i + l - 1; - m[i][j] = Integer.MAX_VALUE; - - for (int k = i; k < j; k++) { - int q = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j]; - if (q < m[i][j]) { - m[i][j] = q; - s[i][j] = k; - } - } - } - } - } - - private static String[] input(String string) { - System.out.print(string); - return (scan.nextLine().split(" ")); - } - -} - -class Matrix { - private int count; - private int col; - private int row; - - Matrix(int count, int col, int row) { - this.count = count; - this.col = col; - this.row = row; - } - - int count() { - return count; - } - - int col() { - return col; - } - - int row() { - return row; - } -} diff --git a/DynamicProgramming/RodCutting.java b/DynamicProgramming/RodCutting.java deleted file mode 100644 index 0913dfd80b1f..000000000000 --- a/DynamicProgramming/RodCutting.java +++ /dev/null @@ -1,33 +0,0 @@ -package DynamicProgramming; - -/** - * A DynamicProgramming solution for Rod cutting problem - * Returns the best obtainable price for a rod of - * length n and price[] as prices of different pieces - */ -public class RodCutting { - - private static int cutRod(int[] price, int n) { - int val[] = new int[n + 1]; - val[0] = 0; - - for (int i = 1; i <= n; i++) { - int max_val = Integer.MIN_VALUE; - for (int j = 0; j < i; j++) - max_val = Math.max(max_val, price[j] + val[i - j - 1]); - - val[i] = max_val; - } - - return val[n]; - } - - // main function to test - public static void main(String args[]) { - int[] arr = new int[]{2, 5, 13, 19, 20}; - int size = arr.length; - int result = cutRod(arr,size); - System.out.println("Maximum Obtainable Value is " + - result); - } -} diff --git a/LICENSE b/LICENSE index a20869d96300..f6bcf04e7773 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 The Algorithms +Copyright (c) 2021 The Algorithms Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Maths/AbsoluteMax.java b/Maths/AbsoluteMax.java deleted file mode 100644 index 18634bc3582f..000000000000 --- a/Maths/AbsoluteMax.java +++ /dev/null @@ -1,32 +0,0 @@ -package Maths; - -import java.util.Arrays; - -/** - * description: - * <p> - * absMax([0, 5, 1, 11]) = 11, absMax([3 , -10, -2]) = -10 - * </p> - */ -public class AbsoluteMax { - public static void main(String[] args) { - int[] numbers = new int[]{3, -10, -2}; - System.out.println("absMax(" + Arrays.toString(numbers) + ") = " + absMax(numbers)); - } - - /** - * get the value, it's absolute value is max - * - * @param numbers contains elements - * @return the absolute max value - */ - public static int absMax(int[] numbers) { - int absMaxValue = numbers[0]; - for (int i = 1, length = numbers.length; i < length; ++i) { - if (Math.abs(numbers[i]) > Math.abs(absMaxValue)) { - absMaxValue = numbers[i]; - } - } - return absMaxValue; - } -} diff --git a/Maths/AbsoluteMin.java b/Maths/AbsoluteMin.java deleted file mode 100644 index 4af43c5c08ac..000000000000 --- a/Maths/AbsoluteMin.java +++ /dev/null @@ -1,32 +0,0 @@ -package Maths; - -import java.util.Arrays; - -/** - * description: - * <p> - * absMin([0, 5, 1, 11]) = 0, absMin([3 , -10, -2]) = -2 - * </p> - */ -public class AbsoluteMin { - public static void main(String[] args) { - int[] numbers = new int[]{3, -10, -2}; - System.out.println("absMin(" + Arrays.toString(numbers) + ") = " + absMin(numbers)); - } - - /** - * get the value, it's absolute value is min - * - * @param numbers contains elements - * @return the absolute min value - */ - public static int absMin(int[] numbers) { - int absMinValue = numbers[0]; - for (int i = 1, length = numbers.length; i < length; ++i) { - if (Math.abs(numbers[i]) < Math.abs(absMinValue)) { - absMinValue = numbers[i]; - } - } - return absMinValue; - } -} diff --git a/Maths/AbsoluteValue.java b/Maths/AbsoluteValue.java deleted file mode 100644 index dc17213d6089..000000000000 --- a/Maths/AbsoluteValue.java +++ /dev/null @@ -1,24 +0,0 @@ -package Maths; - -/** - * @author PatOnTheBack - */ - -public class AbsoluteValue { - - public static void main(String[] args) { - int value = -34; - System.out.println("The absolute value of " + value + " is " + absVal(value)); - } - - /** - * If value is less than zero, make value positive. - * - * @param value a number - * @return the absolute value of a number - */ - public static int absVal(int value) { - return value < 0 ? -value : value; - } - -} diff --git a/Maths/Factorial.java b/Maths/Factorial.java deleted file mode 100644 index 1734e93fb83d..000000000000 --- a/Maths/Factorial.java +++ /dev/null @@ -1,25 +0,0 @@ -package Maths; - -public class Factorial { - public static void main(String[] args) { - int n = 5; - System.out.println(n + "! = " + factorial(n)); - } - - /** - * Calculate factorial - * - * @param n the number - * @return the factorial of {@code n} - */ - public static long factorial(int n) { - if (n < 0) { - throw new ArithmeticException("n < 0"); - } - long fac = 1; - for (int i = 1; i <= n; ++i) { - fac *= i; - } - return fac; - } -} diff --git a/Maths/FindMax.java b/Maths/FindMax.java deleted file mode 100644 index 739cda39bf5d..000000000000 --- a/Maths/FindMax.java +++ /dev/null @@ -1,26 +0,0 @@ -package Maths; - -public class FindMax { - - //Driver - public static void main(String[] args) { - int[] array = {2, 4, 9, 7, 19, 94, 5}; - System.out.println("max = " + findMax(array)); - } - - /** - * find max of array - * - * @param array the array contains element - * @return max value - */ - public static int findMax(int[] array) { - int max = array[0]; - for (int i = 1; i < array.length; ++i) { - if (array[i] > max) { - max = array[i]; - } - } - return max; - } -} diff --git a/Maths/FindMaxRecursion.java b/Maths/FindMaxRecursion.java deleted file mode 100644 index 3ebcaf392d95..000000000000 --- a/Maths/FindMaxRecursion.java +++ /dev/null @@ -1,32 +0,0 @@ -package Maths; - -public class FindMaxRecursion { - public static void main(String[] args) { - int[] array = {2, 4, 9, 7, 19, 94, 5}; - int low = 0; - int high = array.length - 1; - - System.out.println("max value is " + max(array, low, high)); - } - - /** - * Get max of array using divide and conquer algorithm - * - * @param array contains elements - * @param low the index of the first element - * @param high the index of the last element - * @return max of {@code array} - */ - public static int max(int[] array, int low, int high) { - if (low == high) { - return array[low]; //or array[high] - } - - int mid = (low + high) >>> 1; - - int leftMax = max(array, low, mid); //get max in [low, mid] - int rightMax = max(array, mid + 1, high); //get max in [mid+1, high] - - return leftMax >= rightMax ? leftMax : rightMax; - } -} diff --git a/Maths/FindMin.java b/Maths/FindMin.java deleted file mode 100644 index 9097c0179f39..000000000000 --- a/Maths/FindMin.java +++ /dev/null @@ -1,26 +0,0 @@ -package Maths; - -public class FindMin { - - //Driver - public static void main(String[] args) { - int[] array = {2, 4, 9, 7, 19, 94, 5}; - System.out.println("min = " + findMax(array)); - } - - /** - * Find the minimum number of an array of numbers. - * - * @param array the array contains element - * @return min value - */ - public static int findMax(int[] array) { - int min = array[0]; - for (int i = 1; i < array.length; ++i) { - if (array[i] < min) { - min = array[i]; - } - } - return min; - } -} diff --git a/Maths/FindMinRecursion.java b/Maths/FindMinRecursion.java deleted file mode 100644 index 292ffc30e83d..000000000000 --- a/Maths/FindMinRecursion.java +++ /dev/null @@ -1,32 +0,0 @@ -package Maths; - -public class FindMinRecursion { - public static void main(String[] args) { - int[] array = {2, 4, 9, 7, 19, 94, 5}; - int low = 0; - int high = array.length - 1; - - System.out.println("min value is " + min(array, low, high)); - } - - /** - * Get min of array using divide and conquer algorithm - * - * @param array contains elements - * @param low the index of the first element - * @param high the index of the last element - * @return min of {@code array} - */ - public static int min(int[] array, int low, int high) { - if (low == high) { - return array[low]; //or array[high] - } - - int mid = (low + high) >>> 1; - - int leftMin = min(array, low, mid); //get min in [low, mid] - int rightMin = min(array, mid + 1, high); //get min in [mid+1, high] - - return leftMin <= rightMin ? leftMin : rightMin; - } -} diff --git a/Maths/GCD.java b/Maths/GCD.java deleted file mode 100644 index fb9aeb21ee1b..000000000000 --- a/Maths/GCD.java +++ /dev/null @@ -1,57 +0,0 @@ -package Maths; - -/** - * This is Euclid's algorithm which is used to find the greatest common denominator - * Overide function name gcd - * - * @author Oskar Enmalm 3/10/17 - */ -public class GCD { - - /** - * get greatest common divisor - * - * @param num1 the first number - * @param num2 the second number - * @return gcd - */ - public static int gcd(int num1, int num2) { - if (num1 < 0 || num2 < 0) { - throw new ArithmeticException(); - } - - if (num1 == 0 || num2 == 0) { - return Math.abs(num1 - num2); - } - - while (num1 % num2 != 0) { - int remainder = num1 % num2; - num1 = num2; - num2 = remainder; - } - return num2; - } - - /** - * get greatest common divisor in array - * - * @param number contains number - * @return gcd - */ - public static int gcd(int[] number) { - int result = number[0]; - for (int i = 1; i < number.length; i++) - // call gcd function (input two value) - result = gcd(result, number[i]); - - return result; - } - - public static void main(String[] args) { - int[] myIntArray = {4, 16, 32}; - - // call gcd function (input array) - System.out.println(gcd(myIntArray)); // => 4 - System.out.printf("gcd(40,24)=%d gcd(24,40)=%d\n", gcd(40, 24), gcd(24, 40)); // => 8 - } -} diff --git a/Maths/GCDRecursion.java b/Maths/GCDRecursion.java deleted file mode 100644 index be5e2904733e..000000000000 --- a/Maths/GCDRecursion.java +++ /dev/null @@ -1,36 +0,0 @@ -package Maths; - -/** - * @author https://github.com/shellhub/ - */ -public class GCDRecursion { - public static void main(String[] args) { - System.out.println(gcd(20, 15)); /* output: 5 */ - System.out.println(gcd(10, 8)); /* output: 2 */ - System.out.println(gcd(gcd(10, 5), gcd(5, 10))); /* output: 5 */ - } - - /** - * get greatest common divisor - * - * @param a the first number - * @param b the second number - * @return gcd - */ - public static int gcd(int a, int b) { - - if (a < 0 || b < 0) { - throw new ArithmeticException(); - } - - if (a == 0 || b == 0) { - return Math.abs(a - b); - } - - if (a % b == 0) { - return b; - } else { - return gcd(b, a % b); - } - } -} diff --git a/Maths/MaxValue.java b/Maths/MaxValue.java deleted file mode 100644 index b1b258685f10..000000000000 --- a/Maths/MaxValue.java +++ /dev/null @@ -1,24 +0,0 @@ -package Maths; - -public class MaxValue { - - /** - * Returns the greater of two {@code int} values. That is, the - * result is the argument closer to the value of - * {@link Integer#MAX_VALUE}. If the arguments have the same value, - * the result is that same value. - * - * @param a an argument. - * @param b another argument. - * @return the larger of {@code a} and {@code b}. - */ - public static int max(int a, int b) { - return a >= b ? a : b; - } - - public static void main(String[] args) { - int a = 3; - int b = 4; - System.out.format("max:%d between %d and %d", max(a, b), a, b); - } -} diff --git a/Maths/MinValue.java b/Maths/MinValue.java deleted file mode 100644 index ef1d6a085362..000000000000 --- a/Maths/MinValue.java +++ /dev/null @@ -1,24 +0,0 @@ -package Maths; - -public class MinValue { - - /** - * Returns the smaller of two {@code int} values. That is, - * the result the argument closer to the value of - * {@link Integer#MIN_VALUE}. If the arguments have the same - * value, the result is that same value. - * - * @param a an argument. - * @param b another argument. - * @return the smaller of {@code a} and {@code b}. - */ - public static int min(int a, int b) { - return a <= b ? a : b; - } - - public static void main(String[] args) { - int a = 3; - int b = 4; - System.out.format("min:%d between %d and %d", min(a, b), a, b); - } -} diff --git a/Maths/PalindromeNumber.java b/Maths/PalindromeNumber.java deleted file mode 100644 index 2916c753e67a..000000000000 --- a/Maths/PalindromeNumber.java +++ /dev/null @@ -1,30 +0,0 @@ -package Maths; - -public class PalindromeNumber { - public static void main(String[] args) { - - assert isPalindrome(12321); - assert !isPalindrome(1234); - assert isPalindrome(1); - } - - /** - * Check if {@code n} is palindrome number or not - * - * @param number the number - * @return {@code true} if {@code n} is palindrome number, otherwise {@code false} - */ - public static boolean isPalindrome(int number) { - if (number < 0) { - throw new IllegalArgumentException(number + ""); - } - int numberCopy = number; - int reverseNumber = 0; - while (numberCopy != 0) { - int remainder = numberCopy % 10; - reverseNumber = reverseNumber * 10 + remainder; - numberCopy /= 10; - } - return number == reverseNumber; - } -} diff --git a/Maths/ParseInteger.java b/Maths/ParseInteger.java deleted file mode 100644 index 91177bb49dd6..000000000000 --- a/Maths/ParseInteger.java +++ /dev/null @@ -1,33 +0,0 @@ -package Maths; - -public class ParseInteger { - public static void main(String[] args) { - assert parseInt("123") == Integer.parseInt("123"); - assert parseInt("-123") == Integer.parseInt("-123"); - assert parseInt("0123") == Integer.parseInt("0123"); - assert parseInt("+123") == Integer.parseInt("+123"); - } - - /** - * Parse a string to integer - * - * @param s the string - * @return the integer value represented by the argument in decimal. - * @throws NumberFormatException if the {@code string} does not contain a parsable integer. - */ - public static int parseInt(String s) { - if (s == null) { - throw new NumberFormatException("null"); - } - boolean isNegative = s.charAt(0) == '-'; - boolean isPositive = s.charAt(0) == '+'; - int number = 0; - for (int i = isNegative ? 1 : isPositive ? 1 : 0, length = s.length(); i < length; ++i) { - if (!Character.isDigit(s.charAt(i))) { - throw new NumberFormatException("s=" + s); - } - number = number * 10 + s.charAt(i) - '0'; - } - return isNegative ? -number : number; - } -} diff --git a/Maths/PerfectNumber.java b/Maths/PerfectNumber.java deleted file mode 100644 index ceaf0b2bca3e..000000000000 --- a/Maths/PerfectNumber.java +++ /dev/null @@ -1,33 +0,0 @@ -package Maths; - -/** - * In number theory, a perfect number is a positive integer that is equal to the sum of - * its positive divisors, excluding the number itself. For instance, 6 has divisors 1, 2 and 3 - * (excluding itself), and 1 + 2 + 3 = 6, so 6 is a perfect number. - * <p> - * link:https://en.wikipedia.org/wiki/Perfect_number - * </p> - */ -public class PerfectNumber { - public static void main(String[] args) { - assert isPerfectNumber(6); /* 1 + 2 + 3 == 6 */ - assert !isPerfectNumber(8); /* 1 + 2 + 4 != 8 */ - assert isPerfectNumber(28); /* 1 + 2 + 4 + 7 + 14 == 28 */ - } - - /** - * Check if {@code number} is perfect number or not - * - * @param number the number - * @return {@code true} if {@code number} is perfect number, otherwise false - */ - public static boolean isPerfectNumber(int number) { - int sum = 0; /* sum of its positive divisors */ - for (int i = 1; i < number; ++i) { - if (number % i == 0) { - sum += i; - } - } - return sum == number; - } -} diff --git a/Maths/Pow.java b/Maths/Pow.java deleted file mode 100644 index 605e01d98234..000000000000 --- a/Maths/Pow.java +++ /dev/null @@ -1,26 +0,0 @@ -package maths; - -public class Pow { - public static void main(String[] args) { - assert pow(2, 0) == Math.pow(2, 0); - assert pow(0, 2) == Math.pow(0, 2); - assert pow(2, 10) == Math.pow(2, 10); - assert pow(10, 2) == Math.pow(10, 2); - } - - /** - * Returns the value of the first argument raised to the power of the - * second argument - * - * @param a the base. - * @param b the exponent. - * @return the value {@code a}<sup>{@code b}</sup>. - */ - public static long pow(int a, int b) { - long result = 1; - for (int i = 1; i <= b; i++) { - result *= a; - } - return result; - } -} diff --git a/Maths/PowRecursion.java b/Maths/PowRecursion.java deleted file mode 100644 index 243548708771..000000000000 --- a/Maths/PowRecursion.java +++ /dev/null @@ -1,26 +0,0 @@ -package Maths; - -public class PowRecursion { - public static void main(String[] args) { - assert pow(2, 0) == Math.pow(2, 0); - assert pow(0, 2) == Math.pow(0, 2); - assert pow(2, 10) == Math.pow(2, 10); - assert pow(10, 2) == Math.pow(10, 2); - } - - /** - * Returns the value of the first argument raised to the power of the - * second argument - * - * @param a the base. - * @param b the exponent. - * @return the value {@code a}<sup>{@code b}</sup>. - */ - public static long pow(int a, int b) { - if (b == 0) { - return 1; - } else { - return a * pow(a, b - 1); - } - } -} diff --git a/Maths/PrimeCheck.java b/Maths/PrimeCheck.java deleted file mode 100644 index 3093894a3a3f..000000000000 --- a/Maths/PrimeCheck.java +++ /dev/null @@ -1,37 +0,0 @@ -package Maths; - -import java.util.Scanner; - -public class PrimeCheck { - public static void main(String[] args) { - Scanner scanner = new Scanner(System.in); - - System.out.print("Enter a number: "); - int n = scanner.nextInt(); - if (isPrime(n)) { - System.out.println(n + " is a prime number"); - } else { - System.out.println(n + " is not a prime number"); - } - } - - /*** - * Checks if a number is prime or not - * @param n the number - * @return {@code true} if {@code n} is prime - */ - public static boolean isPrime(int n) { - if (n == 2) { - return true; - } - if (n < 2 || n % 2 == 0) { - return false; - } - for (int i = 3, limit = (int) Math.sqrt(n); i <= limit; i += 2) { - if (n % i == 0) { - return false; - } - } - return true; - } -} diff --git a/MinimizingLateness/MinimizingLateness.java b/MinimizingLateness/MinimizingLateness.java deleted file mode 100644 index e5f71e9ef44a..000000000000 --- a/MinimizingLateness/MinimizingLateness.java +++ /dev/null @@ -1,57 +0,0 @@ -package MinimizingLateness; - - -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.IOException; -import java.util.StringTokenizer; - -public class MinimizingLateness { - - private static class Schedule { // Schedule class - int t = 0; // Time required for the operation to be performed - int d = 0; // Time the job should be completed - int s = 0; // Start time of the task - int f = 0; // End time of the operation - - public Schedule(int t, int d) { - this.t = t; - this.d = d; - } - } - - public static void main(String[] args) throws IOException { - StringTokenizer token; - - BufferedReader in = new BufferedReader(new FileReader("MinimizingLateness/lateness_data.txt")); - String ch = in.readLine(); - if (ch == null || ch.isEmpty()) { - return; - } - int indexCount = Integer.parseInt(ch); - System.out.println("Input Data : "); - System.out.println(indexCount); // number of operations - Schedule[] array = new Schedule[indexCount]; // Create an array to hold the operation - int i = 0; - while ((ch = in.readLine()) != null) { - token = new StringTokenizer(ch, " "); - // Include the time required for the operation to be performed in the array and the time it should be completed. - array[i] = new Schedule(Integer.parseInt(token.nextToken()), Integer.parseInt(token.nextToken())); - i++; - System.out.println(array[i - 1].t + " " + array[i - 1].d); - } - - int tryTime = 0; // Total time worked - int lateness = 0; // Lateness - for (int j = 0; j < indexCount - 1; j++) { - array[j].s = tryTime; // Start time of the task - array[j].f = tryTime + array[j].t; // Time finished - tryTime = tryTime + array[j].t; // Add total work time - // Lateness - lateness = lateness + Math.max(0, tryTime - array[j].d); - } - System.out.println(); - System.out.println("Output Data : "); - System.out.println(lateness); - } -} diff --git a/MinimizingLateness/lateness_data.txt b/MinimizingLateness/lateness_data.txt deleted file mode 100644 index e2bac0d1cbd0..000000000000 --- a/MinimizingLateness/lateness_data.txt +++ /dev/null @@ -1,7 +0,0 @@ -6 -3 6 -2 8 -1 9 -4 9 -3 14 -2 15 \ No newline at end of file diff --git a/Misc/MedianOfRunningArray.java b/Misc/MedianOfRunningArray.java deleted file mode 100644 index 32bbf51947a1..000000000000 --- a/Misc/MedianOfRunningArray.java +++ /dev/null @@ -1,52 +0,0 @@ -package Misc; - -import java.util.Collections; -import java.util.PriorityQueue; - - -/** - * @author shrutisheoran - */ -public class MedianOfRunningArray { - private PriorityQueue<Integer> p1; - private PriorityQueue<Integer> p2; - - //Constructor - public MedianOfRunningArray() { - this.p1 = new PriorityQueue<>(Collections.reverseOrder()); //Max Heap - this.p2 = new PriorityQueue<>(); //Min Heap - } - - /* - Inserting lower half of array to max Heap - and upper half to min heap - */ - public void insert(Integer e) { - p2.add(e); - if (p2.size() - p1.size() > 1) - p1.add(p2.remove()); - } - - /* - Returns median at any given point - */ - public Integer median() { - if (p1.size() == p2.size()) - return (p1.peek() + p2.peek()) / 2; - return p1.size() > p2.size() ? p1.peek() : p2.peek(); - } - - public static void main(String[] args) { - /* - Testing the median function - */ - - MedianOfRunningArray p = new MedianOfRunningArray(); - int arr[] = {10, 7, 4, 9, 2, 3, 11, 17, 14}; - for (int i = 0; i < 9; i++) { - p.insert(arr[i]); - System.out.print(p.median() + " "); - } - } - -} \ No newline at end of file diff --git a/Misc/PalindromePrime.java b/Misc/PalindromePrime.java deleted file mode 100644 index b13a23d83792..000000000000 --- a/Misc/PalindromePrime.java +++ /dev/null @@ -1,46 +0,0 @@ -package Misc; - -import java.util.Scanner; - -public class PalindromePrime { - - public static void main(String[] args) { // Main funtion - Scanner in = new Scanner(System.in); - System.out.println("Enter the quantity of First Palindromic Primes you want"); - int n = in.nextInt(); // Input of how many first pallindromic prime we want - functioning(n); // calling function - functioning - } - - public static boolean prime(int num) { // checking if number is prime or not - for (int divisor = 3; divisor <= Math.sqrt(num); divisor += 2) { - if (num % divisor == 0) { - return false; // false if not prime - } - } - return true; // True if prime - } - - public static int reverse(int n) { // Returns the reverse of the number - int reverse = 0; - while (n != 0) { - reverse *= 10; - reverse += n % 10; - n /= 10; - } - return reverse; - } - - public static void functioning(int y) { - if (y == 0) return; - System.out.print(2 + "\n"); // print the first Palindromic Prime - int count = 1; - int num = 3; - while (count < y) { - if (num == reverse(num) && prime(num)) { // number is prime and it's reverse is same - count++; // counts check when to terminate while loop - System.out.print(num + "\n"); // print the Palindromic Prime - } - num += 2; // inrease iterator value by two - } - } -} diff --git a/Misc/heap_sort.java b/Misc/heap_sort.java deleted file mode 100644 index 5222be4093d1..000000000000 --- a/Misc/heap_sort.java +++ /dev/null @@ -1,68 +0,0 @@ -package Misc; - -public class heap_sort { - public void sort(int[] arr) { - int n = arr.length; - - // Build heap (rearrange array) - for (int i = n / 2 - 1; i >= 0; i--) - heapify(arr, n, i); - - // One by one extract an element from heap - for (int i = n - 1; i >= 0; i--) { - // Move current root to end - int temp = arr[0]; - arr[0] = arr[i]; - arr[i] = temp; - - // call max heapify on the reduced heap - heapify(arr, i, 0); - } - } - - // To heapify a subtree rooted with node i which is - // an index in arr[]. n is size of heap - void heapify(int[] arr, int n, int i) { - int largest = i; // Initialize largest as root - int l = 2 * i + 1; // left = 2*i + 1 - int r = 2 * i + 2; // right = 2*i + 2 - - // If left child is larger than root - if (l < n && arr[l] > arr[largest]) - largest = l; - - // If right child is larger than largest so far - if (r < n && arr[r] > arr[largest]) - largest = r; - - // If largest is not root - if (largest != i) { - int swap = arr[i]; - arr[i] = arr[largest]; - arr[largest] = swap; - - // Recursively heapify the affected sub-tree - heapify(arr, n, largest); - } - } - - /* A utility function to print array of size n */ - static void printArray(int[] arr) { - int n = arr.length; - for (int i = 0; i < n; ++i) - System.out.print(arr[i] + " "); - System.out.println(); - } - - // Driver program - public static void main(String args[]) { - int arr[] = {12, 11, 13, 5, 6, 7}; - int n = arr.length; - - heap_sort ob = new heap_sort(); - ob.sort(arr); - - System.out.println("Sorted array is"); - printArray(arr); - } -} diff --git a/Others/Abecedarian.java b/Others/Abecedarian.java deleted file mode 100644 index 13c9b7286389..000000000000 --- a/Others/Abecedarian.java +++ /dev/null @@ -1,24 +0,0 @@ -package Others; - -/** - * An Abecadrian is a word where each letter is in alphabetical order - * - * @author Oskar Enmalm - */ -class Abecedarian { - - public static boolean isAbecedarian(String s) { - int index = s.length() - 1; - - for (int i = 0; i < index; i++) { - - if (s.charAt(i) <= s.charAt(i + 1)) { - } //Need to check if each letter for the whole word is less than the one before it - - else { - return false; - } - } - return true; - } -} diff --git a/Others/Armstrong.java b/Others/Armstrong.java deleted file mode 100644 index bfdde2b0e1cd..000000000000 --- a/Others/Armstrong.java +++ /dev/null @@ -1,49 +0,0 @@ -package Others; - -import java.util.Scanner; - -/** - * A utility to check if a given number is armstrong or not. Armstrong number is - * a number that is equal to the sum of cubes of its digits for example 0, 1, - * 153, 370, 371, 407 etc. For example 153 = 1^3 + 5^3 +3^3 - * - * @author mani manasa mylavarapu - */ -public class Armstrong { - static Scanner scan; - - public static void main(String[] args) { - scan = new Scanner(System.in); - int n = inputInt("please enter the number"); - boolean isArmstrong = checkIfANumberIsAmstrongOrNot(n); - if (isArmstrong) { - System.out.println("the number is armstrong"); - } else { - System.out.println("the number is not armstrong"); - } - } - - /** - * Checks whether a given number is an armstrong number or not. Armstrong - * number is a number that is equal to the sum of cubes of its digits for - * example 0, 1, 153, 370, 371, 407 etc. - * - * @param number - * @return boolean - */ - public static boolean checkIfANumberIsAmstrongOrNot(int number) { - int remainder, sum = 0, temp = 0; - temp = number; - while (number > 0) { - remainder = number % 10; - sum = sum + (remainder * remainder * remainder); - number = number / 10; - } - return sum == temp; - } - - private static int inputInt(String string) { - System.out.print(string); - return Integer.parseInt(scan.nextLine()); - } -} diff --git a/Others/BrianKernighanAlgorithm.java b/Others/BrianKernighanAlgorithm.java deleted file mode 100644 index a35234f0e2af..000000000000 --- a/Others/BrianKernighanAlgorithm.java +++ /dev/null @@ -1,49 +0,0 @@ -package Others; - -import java.util.Scanner; - -/** - * @author Nishita Aggarwal - * <p> - * Brian Kernighan’s Algorithm - * <p> - * algorithm to count the number of set bits in a given number - * <p> - * Subtraction of 1 from a number toggles all the bits (from right to left) till the rightmost set bit(including the - * rightmost set bit). - * So if we subtract a number by 1 and do bitwise & with itself i.e. (n & (n-1)), we unset the rightmost set bit. - * <p> - * If we do n & (n-1) in a loop and count the no of times loop executes we get the set bit count. - * <p> - * <p> - * Time Complexity: O(logn) - */ - - -public class BrianKernighanAlgorithm { - - /** - * @param num: number in which we count the set bits - * @return int: Number of set bits - */ - static int countSetBits(int num) { - int cnt = 0; - while (num != 0) { - num = num & (num - 1); - cnt++; - } - return cnt; - } - - - /** - * @param args : command line arguments - */ - public static void main(String args[]) { - Scanner sc = new Scanner(System.in); - int num = sc.nextInt(); - int setBitCount = countSetBits(num); - System.out.println(setBitCount); - sc.close(); - } -} diff --git a/Others/CRCAlgorithm.java b/Others/CRCAlgorithm.java deleted file mode 100644 index d53e3a02c2e3..000000000000 --- a/Others/CRCAlgorithm.java +++ /dev/null @@ -1,203 +0,0 @@ -package Others; - -import java.util.ArrayList; -import java.util.Random; -import java.util.concurrent.ThreadLocalRandom; - -/** - * @author dimgrichr - */ -public class CRCAlgorithm { - - private int correctMess; - - private int wrongMess; - - private int wrongMessCaught; - - private int wrongMessNotCaught; - - private int messSize; - - private double ber; - - private boolean messageChanged; - - private ArrayList<Integer> message; - - private ArrayList<Integer> dividedMessage; - - private ArrayList<Integer> p; - - private Random randomGenerator; - - - /** - * The algorithm's main constructor. - * The most significant variables, used in the algorithm, - * are set in their initial values. - * - * @param str The binary number P, in a string form, which is used by the CRC algorithm - * @param size The size of every transmitted message - * @param ber The Bit Error Rate - */ - public CRCAlgorithm(String str, int size, double ber) { - messageChanged = false; - message = new ArrayList<>(); - messSize = size; - dividedMessage = new ArrayList<>(); - p = new ArrayList<>(); - for (int i = 0; i < str.length(); i++) { - p.add(Character.getNumericValue(str.charAt(i))); - } - randomGenerator = new Random(); - correctMess = 0; - wrongMess = 0; - wrongMessCaught = 0; - wrongMessNotCaught = 0; - this.ber = ber; - } - - - /** - * Returns the counter wrongMess - * - * @return wrongMess, the number of Wrong Messages - */ - public int getWrongMess() { - return wrongMess; - } - - /** - * Returns the counter wrongMessCaught - * - * @return wrongMessCaught, the number of wrong messages, which are caught by the CRC algoriithm - */ - public int getWrongMessCaught() { - return wrongMessCaught; - } - - /** - * Returns the counter wrongMessNotCaught - * - * @return wrongMessNotCaught, the number of wrong messages, which are not caught by the CRC algorithm - */ - public int getWrongMessNotCaught() { - return wrongMessNotCaught; - } - - /** - * Returns the counter correctMess - * - * @return correctMess, the number of the Correct Messages - */ - public int getCorrectMess() { - return correctMess; - } - - /** - * Resets some of the object's values, used on the main function, - * so that it can be re-used, in order not to waste too much memory and time, - * by creating new objects. - */ - public void refactor() { - messageChanged = false; - message = new ArrayList<>(); - dividedMessage = new ArrayList<>(); - } - - /** - * Random messages, consisted of 0's and 1's, - * are generated, so that they can later be transmitted - */ - public void generateRandomMess() { - for (int i = 0; i < messSize; i++) { - int x = ThreadLocalRandom.current().nextInt(0, 2); - message.add(x); - } - } - - /** - * The most significant part of the CRC algorithm. - * The message is divided by P, so the dividedMessage ArrayList<Integer> is created. - * If check == true, the dividedMessaage is examined, in order to see if it contains any 1's. - * If it does, the message is considered to be wrong by the receiver,so the variable wrongMessCaught changes. - * If it does not, it is accepted, so one of the variables correctMess, wrongMessNotCaught, changes. - * If check == false, the diviided Message is added at the end of the ArrayList<integer> message. - * - * @param check the variable used to determine, if the message is going to be checked from the receiver - * if true, it is checked - * otherwise, it is not - */ - public void divideMessageWithP(boolean check) { - ArrayList<Integer> x = new ArrayList<>(); - ArrayList<Integer> k = (ArrayList<Integer>) message.clone(); - if (!check) { - for (int i = 0; i < p.size() - 1; i++) { - k.add(0); - } - } - while (!k.isEmpty()) { - while (x.size() < p.size() && !k.isEmpty()) { - x.add(k.get(0)); - k.remove(0); - } - if (x.size() == p.size()) { - for (int i = 0; i < p.size(); i++) { - if (x.get(i) == p.get(i)) { - x.set(i, 0); - } else { - x.set(i, 1); - } - } - for (int i = 0; i < x.size() && x.get(i) != 1; i++) { - x.remove(0); - } - } - } - dividedMessage = (ArrayList<Integer>) x.clone(); - if (!check) { - for (int z : dividedMessage) { - message.add(z); - } - } else { - if (dividedMessage.contains(1) && messageChanged) { - wrongMessCaught++; - } else if (!dividedMessage.contains(1) && messageChanged) { - wrongMessNotCaught++; - } else if (!messageChanged) { - correctMess++; - } - } - } - - /** - * Once the message is transmitted, some of it's elements, - * is possible to change from 1 to 0, or from 0 to 1, - * because of the Bit Error Rate (ber). - * For every element of the message, a random double number is created. - * If that number is smaller than ber, then the spesific element changes. - * On the other hand, if it's bigger than ber, it does not. - * Based on these changes. the boolean variable messageChanged, gets the value: - * true, or false. - */ - public void changeMess() { - for (int y : message) { - double x = randomGenerator.nextDouble(); - while (x < 0.0000 || x > 1.00000) { - x = randomGenerator.nextDouble(); - } - if (x < ber) { - messageChanged = true; - if (y == 1) { - message.set(message.indexOf(y), 0); - } else { - message.set(message.indexOf(y), 1); - } - } - } - if (messageChanged) { - wrongMess++; - } - } -} diff --git a/Others/CountChar.java b/Others/CountChar.java deleted file mode 100644 index ee83bce06a51..000000000000 --- a/Others/CountChar.java +++ /dev/null @@ -1,30 +0,0 @@ -package Others; - -import java.util.Scanner; - - -/** - * @author blast314 - * <p> - * Counts the number of characters in the text. - */ - -public class CountChar { - - public static void main(String[] args) { - Scanner input = new Scanner(System.in); - System.out.print("Enter your text: "); - String str = input.nextLine(); - input.close(); - System.out.println("There are " + CountCharacters(str) + " characters."); - } - - /** - * @param str: String to count the characters - * @return int: Number of characters in the passed string - */ - private static int CountCharacters(String str) { - str = str.replaceAll("\\s",""); - return str.length(); - } -} diff --git a/Others/CountWords.java b/Others/CountWords.java deleted file mode 100644 index f1caf9a87734..000000000000 --- a/Others/CountWords.java +++ /dev/null @@ -1,48 +0,0 @@ -package Others; - -import java.util.Scanner; - -/** - * You enter a string into this program, and it will return how many words were - * in that particular string - * - * @author Marcus - */ -public class CountWords { - - public static void main(String[] args) { - Scanner input = new Scanner(System.in); - System.out.println("Enter your text: "); - String str = input.nextLine(); - - System.out.println("Your text has " + wordCount(str) + " word(s)"); - System.out.println("Your text has " + secondaryWordCount(str) + " word(s)"); - input.close(); - } - - private static int wordCount(String s) { - if (s == null || s.isEmpty()) - return 0; - return s.trim().split("[\\s]+").length; - } - - /** - * counts the number of words in a sentence but ignores all potential - * non-alphanumeric characters that do not represent a word. runs in O(n) where - * n is the length of s - * - * @param s String: sentence with word(s) - * @return int: number of words - */ - private static int secondaryWordCount(String s) { - if (s == null || s.isEmpty()) - return 0; - StringBuilder sb = new StringBuilder(); - for (char c : s.toCharArray()) { - if (Character.isLetter(c) || Character.isDigit(c)) - sb.append(c); - } - s = sb.toString(); - return s.trim().split("[\\s]+").length; - } -} diff --git a/Others/Dijkshtra.java b/Others/Dijkshtra.java deleted file mode 100644 index c2b1558e88db..000000000000 --- a/Others/Dijkshtra.java +++ /dev/null @@ -1,84 +0,0 @@ -package Others; - -import java.util.Arrays; -import java.util.Scanner; -import java.util.Stack; - -/** - * @author Mayank K Jha - */ - -public class Dijkshtra { - - public static void main(String[] args) { - Scanner in = new Scanner(System.in); - - // n = Number of nodes or vertices - int n = in.nextInt(); - // m = Number of Edges - int m = in.nextInt(); - - // Adjacency Matrix - long[][] w = new long[n + 1][n + 1]; - - // Initializing Matrix with Certain Maximum Value for path b/w any two vertices - for (long[] row : w) { - Arrays.fill(row, 1000000L); - } - - /* From above,we Have assumed that,initially path b/w any two Pair of vertices is Infinite such that Infinite = 1000000l - For simplicity , We can also take path Value = Long.MAX_VALUE , but i have taken Max Value = 1000000l */ - - // Taking Input as Edge Location b/w a pair of vertices - for (int i = 0; i < m; i++) { - int x = in.nextInt(), y = in.nextInt(); - long cmp = in.nextLong(); - - // Comparing previous edge value with current value - Cycle Case - if (w[x][y] > cmp) { - w[x][y] = cmp; - w[y][x] = cmp; - } - } - - // Implementing Dijkshtra's Algorithm - Stack<Integer> t = new Stack<>(); - int src = in.nextInt(); - - for (int i = 1; i <= n; i++) { - if (i != src) { - t.push(i); - } - } - - Stack<Integer> p = new Stack<>(); - p.push(src); - w[src][src] = 0; - - while (!t.isEmpty()) { - int min = 989997979; - int loc = -1; - - for (int i = 0; i < t.size(); i++) { - w[src][t.elementAt(i)] = Math.min(w[src][t.elementAt(i)], w[src][p.peek()] + w[p.peek()][t.elementAt(i)]); - if (w[src][t.elementAt(i)] <= min) { - min = (int) w[src][t.elementAt(i)]; - loc = i; - } - } - p.push(t.elementAt(loc)); - t.removeElementAt(loc); - } - - // Printing shortest path from the given source src - for (int i = 1; i <= n; i++) { - if (i != src && w[src][i] != 1000000L) { - System.out.print(w[src][i] + " "); - } - // Printing -1 if there is no path b/w given pair of edges - else if (i != src) { - System.out.print("-1" + " "); - } - } - } -} \ No newline at end of file diff --git a/Others/Dijkstra.java b/Others/Dijkstra.java deleted file mode 100644 index af8e33b0b320..000000000000 --- a/Others/Dijkstra.java +++ /dev/null @@ -1,192 +0,0 @@ -package Others; - - -/** - * Dijkstra's algorithm,is a graph search algorithm that solves the single-source - * shortest path problem for a graph with nonnegative edge path costs, producing - * a shortest path tree. - * <p> - * NOTE: The inputs to Dijkstra's algorithm are a directed and weighted graph consisting - * of 2 or more nodes, generally represented by an adjacency matrix or list, and a start node. - * <p> - * Original source of code: https://rosettacode.org/wiki/Dijkstra%27s_algorithm#Java - * Also most of the comments are from RosettaCode. - */ - -import java.util.*; - -public class Dijkstra { - private static final Graph.Edge[] GRAPH = { - // Distance from node "a" to node "b" is 7. - // In the current Graph there is no way to move the other way (e,g, from "b" to "a"), - // a new edge would be needed for that - new Graph.Edge("a", "b", 7), - new Graph.Edge("a", "c", 9), - new Graph.Edge("a", "f", 14), - new Graph.Edge("b", "c", 10), - new Graph.Edge("b", "d", 15), - new Graph.Edge("c", "d", 11), - new Graph.Edge("c", "f", 2), - new Graph.Edge("d", "e", 6), - new Graph.Edge("e", "f", 9), - }; - private static final String START = "a"; - private static final String END = "e"; - - /** - * main function - * Will run the code with "GRAPH" that was defined above. - */ - public static void main(String[] args) { - Graph g = new Graph(GRAPH); - g.dijkstra(START); - g.printPath(END); - //g.printAllPaths(); - } -} - -class Graph { - // mapping of vertex names to Vertex objects, built from a set of Edges - private final Map<String, Vertex> graph; - - /** - * One edge of the graph (only used by Graph constructor) - */ - public static class Edge { - public final String v1, v2; - public final int dist; - - public Edge(String v1, String v2, int dist) { - this.v1 = v1; - this.v2 = v2; - this.dist = dist; - } - } - - /** - * One vertex of the graph, complete with mappings to neighbouring vertices - */ - public static class Vertex implements Comparable<Vertex> { - public final String name; - // MAX_VALUE assumed to be infinity - public int dist = Integer.MAX_VALUE; - public Vertex previous = null; - public final Map<Vertex, Integer> neighbours = new HashMap<>(); - - public Vertex(String name) { - this.name = name; - } - - private void printPath() { - if (this == this.previous) { - System.out.printf("%s", this.name); - } else if (this.previous == null) { - System.out.printf("%s(unreached)", this.name); - } else { - this.previous.printPath(); - System.out.printf(" -> %s(%d)", this.name, this.dist); - } - } - - public int compareTo(Vertex other) { - if (dist == other.dist) - return name.compareTo(other.name); - - return Integer.compare(dist, other.dist); - } - - @Override - public String toString() { - return "(" + name + ", " + dist + ")"; - } - } - - /** - * Builds a graph from a set of edges - */ - public Graph(Edge[] edges) { - graph = new HashMap<>(edges.length); - - // one pass to find all vertices - for (Edge e : edges) { - if (!graph.containsKey(e.v1)) graph.put(e.v1, new Vertex(e.v1)); - if (!graph.containsKey(e.v2)) graph.put(e.v2, new Vertex(e.v2)); - } - - // another pass to set neighbouring vertices - for (Edge e : edges) { - graph.get(e.v1).neighbours.put(graph.get(e.v2), e.dist); - // graph.get(e.v2).neighbours.put(graph.get(e.v1), e.dist); // also do this for an undirected graph - } - } - - /** - * Runs dijkstra using a specified source vertex - */ - public void dijkstra(String startName) { - if (!graph.containsKey(startName)) { - System.err.printf("Graph doesn't contain start vertex \"%s\"\n", startName); - return; - } - final Vertex source = graph.get(startName); - NavigableSet<Vertex> q = new TreeSet<>(); - - // set-up vertices - for (Vertex v : graph.values()) { - v.previous = v == source ? source : null; - v.dist = v == source ? 0 : Integer.MAX_VALUE; - q.add(v); - } - - dijkstra(q); - } - - /** - * Implementation of dijkstra's algorithm using a binary heap. - */ - private void dijkstra(final NavigableSet<Vertex> q) { - Vertex u, v; - while (!q.isEmpty()) { - // vertex with shortest distance (first iteration will return source) - u = q.pollFirst(); - if (u.dist == Integer.MAX_VALUE) - break; // we can ignore u (and any other remaining vertices) since they are unreachable - - // look at distances to each neighbour - for (Map.Entry<Vertex, Integer> a : u.neighbours.entrySet()) { - v = a.getKey(); // the neighbour in this iteration - - final int alternateDist = u.dist + a.getValue(); - if (alternateDist < v.dist) { // shorter path to neighbour found - q.remove(v); - v.dist = alternateDist; - v.previous = u; - q.add(v); - } - } - } - } - - /** - * Prints a path from the source to the specified vertex - */ - public void printPath(String endName) { - if (!graph.containsKey(endName)) { - System.err.printf("Graph doesn't contain end vertex \"%s\"\n", endName); - return; - } - - graph.get(endName).printPath(); - System.out.println(); - } - - /** - * Prints the path from the source to every vertex (output order is not guaranteed) - */ - public void printAllPaths() { - for (Vertex v : graph.values()) { - v.printPath(); - System.out.println(); - } - } -} \ No newline at end of file diff --git a/Others/EulersFunction.java b/Others/EulersFunction.java deleted file mode 100644 index 0f848442bc92..000000000000 --- a/Others/EulersFunction.java +++ /dev/null @@ -1,27 +0,0 @@ -package Others; - -/** - * You can read more about Euler's totient function - * <p> - * See https://en.wikipedia.org/wiki/Euler%27s_totient_function - */ -public class EulersFunction { - // This method returns us number of x that (x < n) and gcd(x, n) == 1 in O(sqrt(n)) time complexity; - public static int getEuler(int n) { - int result = n; - for (int i = 2; i * i <= n; i++) { - if (n % i == 0) { - while (n % i == 0) n /= i; - result -= result / i; - } - } - if (n > 1) result -= result / n; - return result; - } - - public static void main(String[] args) { - for (int i = 1; i < 100; i++) { - System.out.println(getEuler(i)); - } - } -} diff --git a/Others/Factorial.java b/Others/Factorial.java deleted file mode 100644 index 652607e5107b..000000000000 --- a/Others/Factorial.java +++ /dev/null @@ -1,50 +0,0 @@ -package Others; - -import java.util.Scanner; - -/** - * This program will print out the factorial of any non-negative - * number that you input into it. - * - * @author Marcus - */ -public class Factorial { - - /** - * The main method - * - * @param args Command line arguments - */ - public static void main(String[] args) { - Scanner input = new Scanner(System.in); - System.out.print("Enter a non-negative integer: "); - - //If user does not enter an Integer, we want program to fail gracefully, letting the user know why it terminated - try { - int number = input.nextInt(); - - //We keep prompting the user until they enter a positive number - while (number < 0) { - System.out.println("Your input must be non-negative. Please enter a positive number: "); - number = input.nextInt(); - } - //Display the result - System.out.println("The factorial of " + number + " will yield: " + factorial(number)); - - } catch (Exception e) { - System.out.println("Error: You did not enter an integer. Program has terminated."); - } - input.close(); - } - - /** - * Recursive Factorial Method - * - * @param n The number to factorial - * @return The factorial of the number - */ - public static long factorial(int n) { - if (n == 0 || n == 1) return 1; - return n * factorial(n - 1); - } -} diff --git a/Others/FibToN.java b/Others/FibToN.java deleted file mode 100644 index 03aad56fd833..000000000000 --- a/Others/FibToN.java +++ /dev/null @@ -1,33 +0,0 @@ -package Others; - -import java.util.Scanner; - -/** - * Fibonacci sequence, and characterized by the fact that every number - * after the first two is the sum of the two preceding ones. - * <p> - * Fibonacci sequence: 0, 1, 1, 2, 3, 5, 8, 13, 21,... - * <p> - * Source for the explanation: https://en.wikipedia.org/wiki/Fibonacci_number - */ - -public class FibToN { - public static void main(String[] args) { - //take input - Scanner scn = new Scanner(System.in); - int N = scn.nextInt(); - // print all Fibonacci numbers that are smaller than your given input N - int first = 0, second = 1; - scn.close(); - while (first <= N) { - //print first fibo 0 then add second fibo into it while updating second as well - - System.out.println(first); - - int next = first + second; - first = second; - second = next; - } - } - -} diff --git a/Others/FloydTriangle.java b/Others/FloydTriangle.java deleted file mode 100644 index 70479dd24e63..000000000000 --- a/Others/FloydTriangle.java +++ /dev/null @@ -1,19 +0,0 @@ -package Others; - -import java.util.Scanner; - - -class FloydTriangle { - public static void main(String[] args) { - Scanner sc = new Scanner(System.in); - System.out.println("Enter the number of rows which you want in your Floyd Triangle: "); - int r = sc.nextInt(), n = 0; - sc.close(); - for (int i = 0; i < r; i++) { - for (int j = 0; j <= i; j++) { - System.out.print(++n + " "); - } - System.out.println(); - } - } -} diff --git a/Others/GuassLegendre.java b/Others/GuassLegendre.java deleted file mode 100644 index 409f626478ba..000000000000 --- a/Others/GuassLegendre.java +++ /dev/null @@ -1,46 +0,0 @@ -package Others; - -import java.lang.Math; - -/** - * Guass Legendre Algorithm - * ref https://en.wikipedia.org/wiki/Gauss–Legendre_algorithm - * - * @author AKS1996 - */ -public class GuassLegendre { - - public static void main(String[] args) { - for (int i = 1; i <= 3; ++i) - System.out.println(pi(i)); - - } - - static double pi(int l) { - /* - * l: No of loops to run - */ - - double a = 1, b = Math.pow(2, -0.5), t = 0.25, p = 1; - for (int i = 0; i < l; ++i) { - double temp[] = update(a, b, t, p); - a = temp[0]; - b = temp[1]; - t = temp[2]; - p = temp[3]; - } - - return Math.pow(a + b, 2) / (4 * t); - } - - static double[] update(double a, double b, double t, double p) { - double values[] = new double[4]; - values[0] = (a + b) / 2; - values[1] = Math.sqrt(a * b); - values[2] = t - p * Math.pow(a - values[0], 2); - values[3] = 2 * p; - - return values; - } - -} diff --git a/Others/InsertDeleteInArray.java b/Others/InsertDeleteInArray.java deleted file mode 100644 index 40fd43b2e769..000000000000 --- a/Others/InsertDeleteInArray.java +++ /dev/null @@ -1,48 +0,0 @@ -package Others; - -import java.util.*; - -public class InsertDeleteInArray { - - public static void main(String[] args) { - Scanner s = new Scanner(System.in); // Input statement - System.out.println("Enter the size of the array"); - int size = s.nextInt(); - int a[] = new int[size]; - int i; - - // To enter the initial elements - for (i = 0; i < size; i++) { - System.out.println("Enter the element"); - a[i] = s.nextInt(); - } - - // To insert a new element(we are creating a new array) - System.out.println("Enter the index at which the element should be inserted"); - int insert_pos = s.nextInt(); - System.out.println("Enter the element to be inserted"); - int ins = s.nextInt(); - int size2 = size + 1; - int b[] = new int[size2]; - for (i = 0; i < size2; i++) { - if (i <= insert_pos) { - b[i] = a[i]; - } else { - b[i] = a[i - 1]; - } - } - b[insert_pos] = ins; - for (i = 0; i < size2; i++) { - System.out.println(b[i]); - } - - // To delete an element given the index - System.out.println("Enter the index at which element is to be deleted"); - int del_pos = s.nextInt(); - for (i = del_pos; i < size2 - 1; i++) { - b[i] = b[i + 1]; - } - for (i = 0; i < size2 - 1; i++) - System.out.println(b[i]); - } -} diff --git a/Others/KMP.java b/Others/KMP.java deleted file mode 100644 index e883076759ff..000000000000 --- a/Others/KMP.java +++ /dev/null @@ -1,57 +0,0 @@ -package Others; - -/** - * Implementation of Knuth–Morris–Pratt algorithm - * Usage: see the main function for an example - */ -public class KMP { - //a working example - public static void main(String[] args) { - final String haystack = "AAAAABAAABA"; //This is the full string - final String needle = "AAAA"; //This is the substring that we want to find - KMPmatcher(haystack, needle); - } - - // find the starting index in string haystack[] that matches the search word P[] - public static void KMPmatcher(final String haystack, final String needle) { - final int m = haystack.length(); - final int n = needle.length(); - final int[] pi = computePrefixFunction(needle); - int q = 0; - for (int i = 0; i < m; i++) { - while (q > 0 && haystack.charAt(i) != needle.charAt(q)) { - q = pi[q - 1]; - } - - if (haystack.charAt(i) == needle.charAt(q)) { - q++; - } - - if (q == n) { - System.out.println("Pattern starts: " + (i + 1 - n)); - q = pi[q - 1]; - } - } - } - - // return the prefix function - private static int[] computePrefixFunction(final String P) { - final int n = P.length(); - final int[] pi = new int[n]; - pi[0] = 0; - int q = 0; - for (int i = 1; i < n; i++) { - while (q > 0 && P.charAt(q) != P.charAt(i)) { - q = pi[q - 1]; - } - - if (P.charAt(q) == P.charAt(i)) { - q++; - } - - pi[i] = q; - - } - return pi; - } -} \ No newline at end of file diff --git a/Others/Krishnamurthy.java b/Others/Krishnamurthy.java deleted file mode 100644 index f2f3533a7549..000000000000 --- a/Others/Krishnamurthy.java +++ /dev/null @@ -1,30 +0,0 @@ -package Others; - -import java.util.Scanner; - -class Krishnamurthy { - static int fact(int n) { - int i, p = 1; - for (i = n; i >= 1; i--) - p = p * i; - return p; - } - - public static void main(String args[]) { - Scanner sc = new Scanner(System.in); - int a, b, s = 0; - System.out.print("Enter the number : "); - a = sc.nextInt(); - int n = a; - while (a > 0) { - b = a % 10; - s = s + fact(b); - a = a / 10; - } - if (s == n) - System.out.print(n + " is a krishnamurthy number"); - else - System.out.print(n + " is not a krishnamurthy number"); - sc.close(); - } -} diff --git a/Others/LowestBasePalindrome.java b/Others/LowestBasePalindrome.java deleted file mode 100644 index 97b445b2ff61..000000000000 --- a/Others/LowestBasePalindrome.java +++ /dev/null @@ -1,146 +0,0 @@ -package Others; - -import java.util.InputMismatchException; -import java.util.Scanner; - -/** - * Class for finding the lowest base in which a given integer is a palindrome. - * Includes auxiliary methods for converting between bases and reversing strings. - * <p> - * NOTE: There is potential for error, see note at line 63. - * - * @author RollandMichael - * @version 2017.09.28 - */ -public class LowestBasePalindrome { - - public static void main(String[] args) { - Scanner in = new Scanner(System.in); - int n = 0; - while (true) { - try { - System.out.print("Enter number: "); - n = in.nextInt(); - break; - } catch (InputMismatchException e) { - System.out.println("Invalid input!"); - in.next(); - } - } - System.out.println(n + " is a palindrome in base " + lowestBasePalindrome(n)); - System.out.println(base2base(Integer.toString(n), 10, lowestBasePalindrome(n))); - } - - /** - * Given a number in base 10, returns the lowest base in which the - * number is represented by a palindrome (read the same left-to-right - * and right-to-left). - * - * @param num A number in base 10. - * @return The lowest base in which num is a palindrome. - */ - public static int lowestBasePalindrome(int num) { - int base, num2 = num; - int digit; - char digitC; - boolean foundBase = false; - String newNum = ""; - String digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - while (!foundBase) { - // Try from bases 2 to num-1 - for (base = 2; base < num2; base++) { - newNum = ""; - while (num > 0) { - // Obtain the first digit of n in the current base, - // which is equivalent to the integer remainder of (n/base). - // The next digit is obtained by dividing n by the base and - // continuing the process of getting the remainder. This is done - // until n is <=0 and the number in the new base is obtained. - digit = (num % base); - num /= base; - // If the digit isn't in the set of [0-9][A-Z] (beyond base 36), its character - // form is just its value in ASCII. - - // NOTE: This may cause problems, as the capital letters are ASCII values - // 65-90. It may cause false positives when one digit is, for instance 10 and assigned - // 'A' from the character array and the other is 65 and also assigned 'A'. - - // Regardless, the character is added to the representation of n - // in the current base. - if (digit >= digits.length()) { - digitC = (char) (digit); - newNum += digitC; - continue; - } - newNum += digits.charAt(digit); - } - // Num is assigned back its original value for the next iteration. - num = num2; - // Auxiliary method reverses the number. - String reverse = reverse(newNum); - // If the number is read the same as its reverse, then it is a palindrome. - // The current base is returned. - if (reverse.equals(newNum)) { - foundBase = true; - return base; - } - } - } - // If all else fails, n is always a palindrome in base n-1. ("11") - return num - 1; - } - - private static String reverse(String str) { - String reverse = ""; - for (int i = str.length() - 1; i >= 0; i--) { - reverse += str.charAt(i); - } - return reverse; - } - - private static String base2base(String n, int b1, int b2) { - // Declare variables: decimal value of n, - // character of base b1, character of base b2, - // and the string that will be returned. - int decimalValue = 0, charB2; - char charB1; - String output = ""; - // Go through every character of n - for (int i = 0; i < n.length(); i++) { - // store the character in charB1 - charB1 = n.charAt(i); - // if it is a non-number, convert it to a decimal value >9 and store it in charB2 - if (charB1 >= 'A' && charB1 <= 'Z') - charB2 = 10 + (charB1 - 'A'); - // Else, store the integer value in charB2 - else - charB2 = charB1 - '0'; - // Convert the digit to decimal and add it to the - // decimalValue of n - decimalValue = decimalValue * b1 + charB2; - } - - // Converting the decimal value to base b2: - // A number is converted from decimal to another base - // by continuously dividing by the base and recording - // the remainder until the quotient is zero. The number in the - // new base is the remainders, with the last remainder - // being the left-most digit. - - // While the quotient is NOT zero: - while (decimalValue != 0) { - // If the remainder is a digit < 10, simply add it to - // the left side of the new number. - if (decimalValue % b2 < 10) - output = Integer.toString(decimalValue % b2) + output; - // If the remainder is >= 10, add a character with the - // corresponding value to the new number. (A = 10, B = 11, C = 12, ...) - else - output = (char) ((decimalValue % b2) + 55) + output; - // Divide by the new base again - decimalValue /= b2; - } - return output; - } -} diff --git a/Others/Palindrome.java b/Others/Palindrome.java deleted file mode 100644 index af4a72fdede3..000000000000 --- a/Others/Palindrome.java +++ /dev/null @@ -1,50 +0,0 @@ -package Others; - -class Palindrome { - - private String reverseString(String x) { // *helper method - StringBuilder output = new StringBuilder(x); - return output.reverse().toString(); - } - - public boolean FirstWay(String x) { // *palindrome method, returns true if palindrome - if (x == null || x.length() <= 1) - return true; - return x.equalsIgnoreCase(reverseString(x)); - } - - public boolean SecondWay(String x) { - if (x.length() == 0 || x.length() == 1) - return true; - - if (x.charAt(0) != x.charAt(x.length() - 1)) - return false; - - return SecondWay(x.substring(1, x.length() - 1)); - } - - /** - * This method ignores all non-alphanumeric characters and case runs in O(n) - * where n is the length of s - * - * @param s String to check - * @return true if s is palindrome else false - */ - public boolean isPalindrome(String s) { - s = s.toLowerCase().trim(); - StringBuilder sb = new StringBuilder(); - for (char c : s.toCharArray()) { - if (Character.isLetter(c) || Character.isDigit(c)) - sb.append(c); - } - s = sb.toString(); - int start = 0; - int end = s.length() - 1; - while (start <= end) { - if (s.charAt(start++) != s.charAt(end--)) - return false; - - } - return true; - } -} diff --git a/Others/PasswordGen.java b/Others/PasswordGen.java deleted file mode 100644 index 8d49e6c65633..000000000000 --- a/Others/PasswordGen.java +++ /dev/null @@ -1,47 +0,0 @@ -package Others; - -import java.util.Collections; -import java.util.Random; -import java.util.List; -import java.util.ArrayList; - - -/** - * Creates a random password from ASCII letters - * Given password length bounds - * - * @author AKS1996 - * @date 2017.10.25 - */ -class PasswordGen { - public static void main(String args[]) { - String password = generatePassword(8, 16); - System.out.print("Password: " + password); - } - - static String generatePassword(int min_length, int max_length) { - Random random = new Random(); - - String upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - String lower = "abcdefghijklmnopqrstuvwxyz"; - String numbers = "0123456789"; - String specialChars = "!@#$%^&*(){}?"; - - String allChars = upper + lower + numbers + specialChars; - - List<Character> letters = new ArrayList<Character>(); - for (char c : allChars.toCharArray()) - letters.add(c); - - // Inbuilt method to randomly shuffle a elements of a list - Collections.shuffle(letters); - String password = ""; - - // Note that size of the password is also random - for (int i = random.nextInt(max_length - min_length) + min_length; i > 0; --i) { - password += letters.get(random.nextInt(letters.size())); - } - - return password; - } -} diff --git a/Others/PowerOfTwoOrNot.java b/Others/PowerOfTwoOrNot.java deleted file mode 100644 index 349eb9de5807..000000000000 --- a/Others/PowerOfTwoOrNot.java +++ /dev/null @@ -1,36 +0,0 @@ -package Others; - -import java.util.Scanner; - -/** - * A utility to check if a given number is power of two or not. - * For example 8,16 etc. - */ - -public class PowerOfTwoOrNot { - - public static void main(String[] args) { - - Scanner sc = new Scanner(System.in); - System.out.println("Enter the number"); - int num = sc.nextInt(); - boolean isPowerOfTwo = checkIfPowerOfTwoOrNot(num); - if (isPowerOfTwo) { - System.out.println("Number is a power of two"); - } else { - System.out.println("Number is not a power of two"); - } - } - - - /** - * Checks whether given number is power of two or not. - * - * @param number - * @return boolean - */ - public static boolean checkIfPowerOfTwoOrNot(int number) { - return number != 0 && ((number & (number - 1)) == 0); - } - -} diff --git a/Others/QueueUsingTwoStacks.java b/Others/QueueUsingTwoStacks.java deleted file mode 100644 index d138f71aabfb..000000000000 --- a/Others/QueueUsingTwoStacks.java +++ /dev/null @@ -1,160 +0,0 @@ -package Others; - -import java.util.Stack; - -/** - * This implements Queue using two Stacks. - * - * Big O Runtime: - * insert(): O(1) - * remove(): O(1) amortized - * isEmpty(): O(1) - * - * A queue data structure functions the same as a real world queue. - * The elements that are added first are the first to be removed. - * New elements are added to the back/rear of the queue. - * - * @author sahilb2 (https://www.github.com/sahilb2) - * - */ -class QueueWithStack { - - // Stack to keep track of elements inserted into the queue - private Stack inStack; - // Stack to keep track of elements to be removed next in queue - private Stack outStack; - - /** - * Constructor - */ - public QueueWithStack() { - this.inStack = new Stack(); - this.outStack = new Stack(); - } - - /** - * Inserts an element at the rear of the queue - * - * @param x element to be added - */ - public void insert(Object x) { - // Insert element into inStack - this.inStack.push(x); - } - - /** - * Remove an element from the front of the queue - * - * @return the new front of the queue - */ - public Object remove() { - if(this.outStack.isEmpty()) { - // Move all elements from inStack to outStack (preserving the order) - while(!this.inStack.isEmpty()) { - this.outStack.push( this.inStack.pop() ); - } - } - return this.outStack.pop(); - } - - /** - * Peek at the element from the front of the queue - * - * @return the front element of the queue - */ - public Object peekFront() { - if(this.outStack.isEmpty()) { - // Move all elements from inStack to outStack (preserving the order) - while(!this.inStack.isEmpty()) { - this.outStack.push( this.inStack.pop() ); - } - } - return this.outStack.peek(); - } - - /** - * Peek at the element from the back of the queue - * - * @return the back element of the queue - */ - public Object peekBack() { - return this.inStack.peek(); - } - - /** - * Returns true if the queue is empty - * - * @return true if the queue is empty - */ - public boolean isEmpty() { - return (this.inStack.isEmpty() && this.outStack.isEmpty()); - } - -} - -/** - * This class is the example for the Queue class - * - * @author sahilb2 (https://www.github.com/sahilb2) - * - */ -public class QueueUsingTwoStacks { - - /** - * Main method - * - * @param args Command line arguments - */ - public static void main(String args[]){ - QueueWithStack myQueue = new QueueWithStack(); - myQueue.insert(1); - System.out.println(myQueue.peekBack()); //Will print 1 - // instack: [(top) 1] - // outStack: [] - myQueue.insert(2); - System.out.println(myQueue.peekBack()); //Will print 2 - // instack: [(top) 2, 1] - // outStack: [] - myQueue.insert(3); - System.out.println(myQueue.peekBack()); //Will print 3 - // instack: [(top) 3, 2, 1] - // outStack: [] - myQueue.insert(4); - System.out.println(myQueue.peekBack()); //Will print 4 - // instack: [(top) 4, 3, 2, 1] - // outStack: [] - - System.out.println(myQueue.isEmpty()); //Will print false - - System.out.println(myQueue.remove()); //Will print 1 - System.out.println(myQueue.peekBack()); //Will print NULL - // instack: [] - // outStack: [(top) 2, 3, 4] - - myQueue.insert(5); - System.out.println(myQueue.peekFront()); //Will print 2 - // instack: [(top) 5] - // outStack: [(top) 2, 3, 4] - - myQueue.remove(); - System.out.println(myQueue.peekFront()); //Will print 3 - // instack: [(top) 5] - // outStack: [(top) 3, 4] - myQueue.remove(); - System.out.println(myQueue.peekFront()); //Will print 4 - // instack: [(top) 5] - // outStack: [(top) 4] - myQueue.remove(); - // instack: [(top) 5] - // outStack: [] - System.out.println(myQueue.peekFront()); //Will print 5 - // instack: [] - // outStack: [(top) 5] - myQueue.remove(); - // instack: [] - // outStack: [] - - System.out.println(myQueue.isEmpty()); //Will print true - - } -} diff --git a/Others/RemoveDuplicateFromString.java b/Others/RemoveDuplicateFromString.java deleted file mode 100644 index 2e4b11285c89..000000000000 --- a/Others/RemoveDuplicateFromString.java +++ /dev/null @@ -1,46 +0,0 @@ -package Others; - -import java.io.BufferedReader; -import java.io.InputStreamReader; - -/** - * @author Varun Upadhyay (https://github.com/varunu28) - */ - -public class RemoveDuplicateFromString { - public static void main(String[] args) throws Exception { - BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); - String inpStr = br.readLine(); - - System.out.println("Actual string is: " + inpStr); - System.out.println("String after removing duplicates: " + removeDuplicate(inpStr)); - - br.close(); - } - - /** - * This method produces a string after removing all the duplicate characters from input string and returns it - * Example: Input String - "aabbbccccddddd" - * Output String - "abcd" - * - * @param s String from which duplicate characters have to be removed - * @return string with only unique characters - */ - - public static String removeDuplicate(String s) { - if (s == null || s.isEmpty()) { - return s; - } - - StringBuilder sb = new StringBuilder(); - int n = s.length(); - - for (int i = 0; i < n; i++) { - if (sb.toString().indexOf(s.charAt(i)) == -1) { - sb.append(String.valueOf(s.charAt(i))); - } - } - - return sb.toString(); - } -} diff --git a/Others/ReturnSubsequence.java b/Others/ReturnSubsequence.java deleted file mode 100644 index bb1b413afd1d..000000000000 --- a/Others/ReturnSubsequence.java +++ /dev/null @@ -1,42 +0,0 @@ -package Others; - -import java.util.Scanner; - -public class ReturnSubsequence { - public static void main(String[] args) { - System.out.println("Enter String: "); - Scanner s = new Scanner(System.in); - String givenString = s.next(); //given string - String[] subsequence = returnSubsequence(givenString); //calling returnSubsequence() function - System.out.println("Subsequences : "); - //print the given array of subsequences - for (int i = 0; i < subsequence.length; i++) { - System.out.println(subsequence[i]); - } - } - - /** - * @param givenString - * @return subsequence - */ - private static String[] returnSubsequence(String givenString) { - if (givenString.length() == 0) // If string is empty we will create an array of size=1 and insert "" (Empty string) in it - { - String[] ans = new String[1]; - ans[0] = ""; - return ans; - - } - String[] SmallAns = returnSubsequence(givenString.substring(1)); //recursive call to get subsequences of substring starting from index position=1 - - String[] ans = new String[2 * SmallAns.length];// Our answer will be an array off string of size=2*SmallAns - int i = 0; - for (; i < SmallAns.length; i++) { - ans[i] = SmallAns[i]; //Copying all the strings present in SmallAns to ans string array - } - for (int k = 0; k < SmallAns.length; k++) { - ans[k + SmallAns.length] = givenString.charAt(0) + SmallAns[k]; // Insert character at index=0 of the given substring in front of every string in SmallAns - } - return ans; - } -} diff --git a/Others/ReverseStackUsingRecursion.java b/Others/ReverseStackUsingRecursion.java deleted file mode 100644 index 31edb3b747e6..000000000000 --- a/Others/ReverseStackUsingRecursion.java +++ /dev/null @@ -1,67 +0,0 @@ -package Others; - -/* Program to reverse a Stack using Recursion*/ - - -import java.util.Stack; - -public class ReverseStackUsingRecursion { - - //Stack - private static Stack<Integer> stack = new Stack<>(); - - //Main function - public static void main(String[] args) { - //To Create a Dummy Stack containing integers from 0-9 - for (int i = 0; i < 10; i++) { - stack.push(i); - } - System.out.println("STACK"); - - //To print that dummy Stack - for (int k = 9; k >= 0; k--) { - System.out.println(k); - } - - //Reverse Function called - reverseUsingRecursion(stack); - - System.out.println("REVERSED STACK : "); - //To print reversed stack - while (!stack.isEmpty()) { - System.out.println(stack.pop()); - } - - - } - - //Function Used to reverse Stack Using Recursion - private static void reverseUsingRecursion(Stack<Integer> stack) { - if (stack.isEmpty()) // If stack is empty then return - { - return; - } - /* All items are stored in call stack until we reach the end*/ - - int temptop = stack.peek(); - stack.pop(); - reverseUsingRecursion(stack); //Recursion call - insertAtEnd(temptop); // Insert items held in call stack one by one into stack - } - - //Function used to insert element at the end of stack - private static void insertAtEnd(int temptop) { - if (stack.isEmpty()) { - stack.push(temptop); // If stack is empty push the element - } else { - int temp = stack.peek(); /* All the items are stored in call stack until we reach end*/ - stack.pop(); - - insertAtEnd(temptop); //Recursive call - - stack.push(temp); - } - - } - -} diff --git a/Others/ReverseString.java b/Others/ReverseString.java deleted file mode 100644 index 76bbfb5da93a..000000000000 --- a/Others/ReverseString.java +++ /dev/null @@ -1,46 +0,0 @@ -package Others; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; - -/** - * This method produces a reversed version of a string - * - * @author Unknown - */ -public class ReverseString { - - /** - * This method reverses the string str and returns it - * - * @param str String to be reversed - * @return Reversed string - */ - public static String reverse(String str) { - if (str == null || str.isEmpty()) return str; - - char[] arr = str.toCharArray(); - for (int i = 0, j = str.length() - 1; i < j; i++, j--) { - char temp = arr[i]; - arr[i] = arr[j]; - arr[j] = temp; - } - return new String(arr); - } - - /** - * Main Method - * - * @param args Command line arguments - * @throws IOException Exception thrown because of BufferedReader - */ - public static void main(String[] args) throws IOException { - BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); - System.out.println("Enter the string"); - String srr = br.readLine(); - System.out.println("Reverse=" + reverse(srr)); - br.close(); - } -} - diff --git a/Others/RootPrecision.java b/Others/RootPrecision.java deleted file mode 100644 index 388f1b2ec7b6..000000000000 --- a/Others/RootPrecision.java +++ /dev/null @@ -1,34 +0,0 @@ -package Others; - -import java.util.Scanner; - -public class RootPrecision { - - public static void main(String[] args) { - // take input - Scanner scn = new Scanner(System.in); - - // N is the input number - int N = scn.nextInt(); - - // P is precision value for eg - P is 3 in 2.564 and 5 in 3.80870. - int P = scn.nextInt(); - System.out.println(squareRoot(N, P)); - } - - public static double squareRoot(int N, int P) { - // rv means return value - double rv; - - double root = Math.pow(N, 0.5); - - // calculate precision to power of 10 and then multiply it with root value. - int precision = (int) Math.pow(10, P); - root = root * precision; - /*typecast it into integer then divide by precision and again typecast into double - so as to have decimal points upto P precision */ - - rv = (int) root; - return rv / precision; - } -} \ No newline at end of file diff --git a/Others/SJF.java b/Others/SJF.java deleted file mode 100644 index e6b995f4846c..000000000000 --- a/Others/SJF.java +++ /dev/null @@ -1,181 +0,0 @@ -package Others; -/** - * <h2>Shortest job first.</h2> - * <p>Shortest job first (SJF) or shortest job next, is a scheduling policy - * that selects the waiting process with the smallest execution time to execute next - * Shortest Job first has the advantage of having minimum average waiting time among all scheduling algorithms. - * It is a Greedy Algorithm. - * It may cause starvation if shorter processes keep coming. - * This problem has been solved using the concept of aging.</p> - * - * @author shivg7706 - * @since 2018/10/27 - */ - -import java.util.Scanner; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.*; - -class Process { - - public int pid; - public int arrivalTime; - public int burstTime; - public int priority; - public int turnAroundTime; - public int waitTime; - public int remainingTime; -} - -class Schedule { - - private int noOfProcess; - private int timer = 0; - private ArrayList<Process> processes; - private ArrayList<Process> remainingProcess; - private ArrayList<Integer> gantChart; - private float burstAll; - private Map<Integer, ArrayList<Process>> arrivals; - - Schedule() { - Scanner in = new Scanner(System.in); - - processes = new ArrayList<Process>(); - remainingProcess = new ArrayList<Process>(); - - gantChart = new ArrayList<>(); - arrivals = new HashMap<>(); - - System.out.print("Enter the no. of processes: "); - noOfProcess = in.nextInt(); - System.out.println("Enter the arrival, burst and priority of processes"); - for (int i = 0; i < noOfProcess; i++) { - Process p = new Process(); - p.pid = i; - p.arrivalTime = in.nextInt(); - p.burstTime = in.nextInt(); - p.priority = in.nextInt(); - p.turnAroundTime = 0; - p.waitTime = 0; - p.remainingTime = p.burstTime; - - if (arrivals.get(p.arrivalTime) == null) { - arrivals.put(p.arrivalTime, new ArrayList<Process>()); - } - arrivals.get(p.arrivalTime).add(p); - processes.add(p); - burstAll += p.burstTime; - } - - } - - - void startScheduling() { - - - processes.sort(new Comparator<Process>() { - @Override - public int compare(Process a, Process b) { - return a.arrivalTime - b.arrivalTime; - } - }); - - while (!(arrivals.size() == 0 && remainingProcess.size() == 0)) { - removeFinishedProcess(); - if (arrivals.get(timer) != null) { - remainingProcess.addAll(arrivals.get(timer)); - arrivals.remove(timer); - } - - remainingProcess.sort(new Comparator<Process>() { - private int alpha = 6; - private int beta = 1; - - @Override - public int compare(Process a, Process b) { - int aRem = a.remainingTime; - int bRem = b.remainingTime; - int aprior = a.priority; - int bprior = b.priority; - return (alpha * aRem + beta * aprior) - (alpha * bRem + beta * bprior); - } - }); - - int k = timeElapsed(timer); - ageing(k); - timer++; - } - - System.out.println("Total time required: " + (timer - 1)); - } - - void removeFinishedProcess() { - ArrayList<Integer> completed = new ArrayList<Integer>(); - for (int i = 0; i < remainingProcess.size(); i++) { - if (remainingProcess.get(i).remainingTime == 0) { - completed.add(i); - } - } - - for (int i = 0; i < completed.size(); i++) { - int pid = remainingProcess.get(completed.get(i)).pid; - processes.get(pid).waitTime = remainingProcess.get(completed.get(i)).waitTime; - remainingProcess.remove(remainingProcess.get(completed.get(i))); - } - - - } - - public int timeElapsed(int i) { - if (!remainingProcess.isEmpty()) { - gantChart.add(i, remainingProcess.get(0).pid); - remainingProcess.get(0).remainingTime--; - return 1; - } - return 0; - } - - public void ageing(int k) { - for (int i = k; i < remainingProcess.size(); i++) { - remainingProcess.get(i).waitTime++; - if (remainingProcess.get(i).waitTime % 7 == 0) { - remainingProcess.get(i).priority--; - } - } - } - - - public void solve() { - System.out.println("Gant chart "); - for (int i = 0; i < gantChart.size(); i++) { - System.out.print(gantChart.get(i) + " "); - } - System.out.println(); - - float waitTimeTot = 0; - float tatTime = 0; - - for (int i = 0; i < noOfProcess; i++) { - processes.get(i).turnAroundTime = processes.get(i).waitTime + processes.get(i).burstTime; - - waitTimeTot += processes.get(i).waitTime; - tatTime += processes.get(i).turnAroundTime; - - System.out.println("Process no.: " + i + " Wait time: " + processes.get(i).waitTime + " Turn Around Time: " + processes.get(i).turnAroundTime); - } - - System.out.println("Average Waiting Time: " + waitTimeTot / noOfProcess); - System.out.println("Average TAT Time: " + tatTime / noOfProcess); - System.out.println("Throughput: " + (float) noOfProcess / (timer - 1)); - } - -} - -public class SJF { - public static void main(String[] args) { - Schedule s = new Schedule(); - s.startScheduling(); - s.solve(); - } -} \ No newline at end of file diff --git a/Others/SieveOfEratosthenes.java b/Others/SieveOfEratosthenes.java deleted file mode 100644 index 465e600a59e9..000000000000 --- a/Others/SieveOfEratosthenes.java +++ /dev/null @@ -1,49 +0,0 @@ -package Others; - -/** - * @author Varun Upadhyay (https://github.com/varunu28) - */ -public class SieveOfEratosthenes { - - /** - * This method implements the Sieve of Eratosthenes Algorithm - * - * @param n The number till which we have to check for prime - * Prints all the prime numbers till n - **/ - - public static void findPrimesTillN(int n) { - int[] arr = new int[n + 1]; - - for (int i = 0; i <= n; i++) { - arr[i] = 1; - } - - arr[0] = arr[1] = 0; - - for (int i = 2; i <= Math.sqrt(n); i++) { - if (arr[i] == 1) { - for (int j = 2; i * j <= n; j++) { - arr[i * j] = 0; - } - } - } - - for (int i = 0; i < n + 1; i++) { - if (arr[i] == 1) { - System.out.print(i + " "); - } - } - - System.out.println(); - } - - // Driver Program - public static void main(String[] args) { - int n = 100; - - // Prints 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 - findPrimesTillN(n); - } - -} diff --git a/Others/SkylineProblem.java b/Others/SkylineProblem.java deleted file mode 100644 index 31dc961133aa..000000000000 --- a/Others/SkylineProblem.java +++ /dev/null @@ -1,133 +0,0 @@ -package Others; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Scanner; - -public class SkylineProblem { - Building[] building; - int count; - - public void run() { - Scanner sc = new Scanner(System.in); - - int num = sc.nextInt(); - this.building = new Building[num]; - - for (int i = 0; i < num; i++) { - String input = sc.next(); - String[] data = input.split(","); - this.add(Integer.parseInt(data[0]), Integer.parseInt(data[1]), Integer.parseInt(data[2])); - } - this.print(this.findSkyline(0, num - 1)); - - sc.close(); - } - - public void add(int left, int height, int right) { - building[count++] = new Building(left, height, right); - } - - public void print(ArrayList<Skyline> skyline) { - Iterator<Skyline> it = skyline.iterator(); - - while (it.hasNext()) { - Skyline temp = it.next(); - System.out.print(temp.coordinates + "," + temp.height); - if (it.hasNext()) { - System.out.print(","); - } - } - - } - - public ArrayList<Skyline> findSkyline(int start, int end) { - if (start == end) { - ArrayList<Skyline> list = new ArrayList<>(); - list.add(new Skyline(building[start].left, building[start].height)); - list.add(new Skyline(building[end].right, 0)); - - return list; - } - - int mid = (start + end) / 2; - - ArrayList<Skyline> sky1 = this.findSkyline(start, mid); - ArrayList<Skyline> sky2 = this.findSkyline(mid + 1, end); - - return this.mergeSkyline(sky1, sky2); - } - - public ArrayList<Skyline> mergeSkyline(ArrayList<Skyline> sky1, ArrayList<Skyline> sky2) { - int currentH1 = 0, currentH2 = 0; - ArrayList<Skyline> skyline = new ArrayList<>(); - int maxH = 0; - - while (!sky1.isEmpty() && !sky2.isEmpty()) { - if (sky1.get(0).coordinates < sky2.get(0).coordinates) { - int currentX = sky1.get(0).coordinates; - currentH1 = sky1.get(0).height; - - if (currentH1 < currentH2) { - sky1.remove(0); - if (maxH != currentH2) skyline.add(new Skyline(currentX, currentH2)); - } else { - maxH = currentH1; - sky1.remove(0); - skyline.add(new Skyline(currentX, currentH1)); - } - } else { - int currentX = sky2.get(0).coordinates; - currentH2 = sky2.get(0).height; - - if (currentH2 < currentH1) { - sky2.remove(0); - if (maxH != currentH1) skyline.add(new Skyline(currentX, currentH1)); - } else { - maxH = currentH2; - sky2.remove(0); - skyline.add(new Skyline(currentX, currentH2)); - } - } - } - - while (!sky1.isEmpty()) { - skyline.add(sky1.get(0)); - sky1.remove(0); - } - - while (!sky2.isEmpty()) { - skyline.add(sky2.get(0)); - sky2.remove(0); - } - - return skyline; - } - - public class Skyline { - public int coordinates; - public int height; - - public Skyline(int coordinates, int height) { - this.coordinates = coordinates; - this.height = height; - } - } - - public class Building { - public int left; - public int height; - public int right; - - public Building(int left, int height, int right) { - this.left = left; - this.height = height; - this.right = right; - } - } - - public static void main(String[] args) { - SkylineProblem skylineProblem = new SkylineProblem(); - skylineProblem.run(); - } -} diff --git a/Others/StackPostfixNotation.java b/Others/StackPostfixNotation.java deleted file mode 100644 index 01e4c8a8eb1c..000000000000 --- a/Others/StackPostfixNotation.java +++ /dev/null @@ -1,40 +0,0 @@ -package Others; - -import java.util.*; - -public class StackPostfixNotation { - public static void main(String[] args) { - Scanner scanner = new Scanner(System.in); - String post = scanner.nextLine(); // Takes input with spaces in between eg. "1 21 +" - System.out.println(postfixEvaluate(post)); - } - - // Evaluates the given postfix expression string and returns the result. - public static int postfixEvaluate(String exp) { - Stack<Integer> s = new Stack<Integer>(); - Scanner tokens = new Scanner(exp); - - while (tokens.hasNext()) { - if (tokens.hasNextInt()) { - s.push(tokens.nextInt()); // If int then push to stack - } else { // else pop top two values and perform the operation - int num2 = s.pop(); - int num1 = s.pop(); - String op = tokens.next(); - - if (op.equals("+")) { - s.push(num1 + num2); - } else if (op.equals("-")) { - s.push(num1 - num2); - } else if (op.equals("*")) { - s.push(num1 * num2); - } else { - s.push(num1 / num2); - } - - // "+", "-", "*", "/" - } - } - return s.pop(); - } -} diff --git a/Others/TopKWords.java b/Others/TopKWords.java deleted file mode 100644 index 840c3c2f7010..000000000000 --- a/Others/TopKWords.java +++ /dev/null @@ -1,87 +0,0 @@ -package Others; - -import java.io.*; -import java.util.*; - -/* display the most frequent K words in the file and the times it appear - in the file – shown in order (ignore case and periods) */ - -public class TopKWords { - static class CountWords { - private String fileName; - - public CountWords(String fileName) { - this.fileName = fileName; - } - - public Map<String, Integer> getDictionary() { - Map<String, Integer> dictionary = new HashMap<>(); - FileInputStream fis = null; - - try { - - fis = new FileInputStream(fileName); // open the file - int in = 0; - String s = ""; // init a empty word - in = fis.read(); // read one character - - while (-1 != in) { - if (Character.isLetter((char) in)) { - s += (char) in; //if get a letter, append to s - } else { - // this branch means an entire word has just been read - if (s.length() > 0) { - // see whether word exists or not - if (dictionary.containsKey(s)) { - // if exist, count++ - dictionary.put(s, dictionary.get(s) + 1); - } else { - // if not exist, initiate count of this word with 1 - dictionary.put(s, 1); - } - } - s = ""; // reInit a empty word - } - in = fis.read(); - } - return dictionary; - } catch (IOException e) { - e.printStackTrace(); - } finally { - try { - // you always have to close the I/O streams - fis.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - return null; - } - } - - public static void main(String[] args) { - // you can replace the filePath with yours - CountWords cw = new CountWords("/Users/lisanaaa/Desktop/words.txt"); - Map<String, Integer> dictionary = cw.getDictionary(); // get the words dictionary: {word: frequency} - - // we change the map to list for convenient sort - List<Map.Entry<String, Integer>> list = new ArrayList<>(dictionary.entrySet()); - - // sort by lambda valueComparator - list.sort(Comparator.comparing( - m -> m.getValue()) - ); - - Scanner input = new Scanner(System.in); - int k = input.nextInt(); - while (k > list.size()) { - System.out.println("Retype a number, your number is too large"); - input = new Scanner(System.in); - k = input.nextInt(); - } - for (int i = 0; i < k; i++) { - System.out.println(list.get(list.size() - i - 1)); - } - } -} - diff --git a/Others/TowerOfHanoi.java b/Others/TowerOfHanoi.java deleted file mode 100644 index 2eca7fc744a0..000000000000 --- a/Others/TowerOfHanoi.java +++ /dev/null @@ -1,26 +0,0 @@ -package Others; - -import java.util.Scanner; - -class TowerOfHanoi { - public static void shift(int n, String startPole, String intermediatePole, String endPole) { - // if n becomes zero the program returns thus ending the loop. - if (n == 0) { - return; - } - - - // Shift function is called in recursion for swapping the n-1 disc from the startPole to the intermediatePole - shift(n - 1, startPole, endPole, intermediatePole); - System.out.println("\nMove \"" + n + "\" from " + startPole + " --> " + endPole); // Result Printing - // Shift function is called in recursion for swapping the n-1 disc from the intermediatePole to the endPole - shift(n - 1, intermediatePole, startPole, endPole); - } - - public static void main(String[] args) { - System.out.print("Enter number of discs on Pole 1: "); - Scanner scanner = new Scanner(System.in); - int numberOfDiscs = scanner.nextInt(); //input of number of discs on pole 1 - shift(numberOfDiscs, "Pole1", "Pole2", "Pole3"); //Shift function called - } -} diff --git a/README-ko.md b/README-ko.md index c53868cc1310..4f8cab92fc42 100644 --- a/README-ko.md +++ b/README-ko.md @@ -8,80 +8,87 @@ ## 정렬 알고리즘 - ### Bubble(버블 정렬) + ![alt text][bubble-image] From [Wikipedia][bubble-wiki]: 버블 소트(sinking sor라고도 불리움)는 리스트를 반복적인 단계로 접근하여 정렬한다. 각각의 짝을 비교하며, 순서가 잘못된 경우 그접한 아이템들을 스왑하는 알고리즘이다. 더 이상 스왑할 것이 없을 때까지 반복하며, 반복이 끝남음 리스트가 정렬되었음을 의미한다. -__속성__ -* 최악의 성능 O(n^2) -* 최고의 성능 O(n) -* 평균 성능 O(n^2) - -###### View the algorithm in [action][bubble-toptal] +**속성** +- 최악의 성능 O(n^2) +- 최고의 성능 O(n) +- 평균 성능 O(n^2) +###### View the algorithm in [action][bubble-toptal] ### Insertion(삽입 정렬) + ![alt text][insertion-image] -From [Wikipedia][insertion-wiki]: 삽입 정렬은 최종 정렬된 배열(또는 리스트)을 한번에 하나씩 구축하는 알고리즘이다. 이것은 큰 리스트에서 더 나은 알고리즘인 퀵 소트, 힙 소트, 또는 머지 소트보다 훨씬 안좋은 효율을 가진다. 그림에서 각 막대는 정렬해야 하는 배열의 요소를 나타낸다. 상단과 두 번째 상단 막대의 첫 번째 교차점에서 발생하는 것은 두 번째 요소가 첫 번째 요소보다 더 높은 우선 순위를 가지기 떄문에 막대로 표시되는 이러한 요소를 교환한 것이다. 이 방법을 반복하면 삽입 정렬이 완료된다. +From [Wikipedia][insertion-wiki]: 삽입 정렬은 최종 정렬된 배열(또는 리스트)을 한번에 하나씩 구축하는 알고리즘이다. 이것은 큰 리스트에서 더 나은 알고리즘인 퀵 소트, 힙 소트, 또는 머지 소트보다 훨씬 안좋은 효율을 가진다. 그림에서 각 막대는 정렬해야 하는 배열의 요소를 나타낸다. 상단과 두 번째 상단 막대의 첫 번째 교차점에서 발생하는 것은 두 번째 요소가 첫 번째 요소보다 더 높은 우선 순위를 가지기 때문에 막대로 표시되는 이러한 요소를 교환한 것이다. 이 방법을 반복하면 삽입 정렬이 완료된다. -__속성__ -* 최악의 성능 O(n^2) -* 최고의 성능 O(n) -* 평균 O(n^2) +**속성** -###### View the algorithm in [action][insertion-toptal] +- 최악의 성능 O(n^2) +- 최고의 성능 O(n) +- 평균 O(n^2) +###### View the algorithm in [action][insertion-toptal] ### Merge(합병 정렬) + ![alt text][merge-image] From [Wikipedia][merge-wiki]: 컴퓨터 과학에서, 합병 정렬은 효율적인, 범용적인, 비교 기반 정렬 알고리즘이다. 대부분의 구현은 안정적인 분류를 이루는데, 이것은 구현이 정렬된 출력에 동일한 요소의 입력 순서를 유지한다는 것을 의미한다. 합병 정렬은 1945년에 John von Neumann이 발명한 분할 정복 알고리즘이다. -__속성__ -* 최악의 성능 O(n log n) (일반적) -* 최고의 성능 O(n log n) -* 평균 O(n log n) +**속성** +- 최악의 성능 O(n log n) (일반적) +- 최고의 성능 O(n log n) +- 평균 O(n log n) ###### View the algorithm in [action][merge-toptal] ### Quick(퀵 정렬) + ![alt text][quick-image] From [Wikipedia][quick-wiki]: 퀵 정렬sometimes called partition-exchange sort)은 효율적인 정렬 알고리즘으로, 배열의 요소를 순서대로 정렬하는 체계적인 방법 역활을 한다. -__속성__ -* 최악의 성능 O(n^2) -* 최고의 성능 O(n log n) or O(n) with three-way partition -* 평균 O(n log n) +**속성** + +- 최악의 성능 O(n^2) +- 최고의 성능 O(n log n) or O(n) with three-way partition +- 평균 O(n log n) ###### View the algorithm in [action][quick-toptal] ### Selection(선택 정렬) + ![alt text][selection-image] From [Wikipedia][selection-wiki]: 알고리즘 입력 리스트를 두 부분으로 나눈다 : 첫 부분은 아이템들이 이미 왼쪽에서 오른쪽으로 정렬되었다. 그리고 남은 부분의 아이템들은 나머지 항목을 차지하는 리스트이다. 처음에는 정렬된 리스트는 공백이고 나머지가 전부이다. 오르차순(또는 내림차순) 알고리즘은 가장 작은 요소를 정렬되지 않은 리스트에서 찾고 정렬이 안된 가장 왼쪽(정렬된 리스트) 리스트와 바꾼다. 이렇게 오른쪽으로 나아간다. -__속성__ -* 최악의 성능 O(n^2) -* 최고의 성능 O(n^2) -* 평균 O(n^2) +**속성** + +- 최악의 성능 O(n^2) +- 최고의 성능 O(n^2) +- 평균 O(n^2) ###### View the algorithm in [action][selection-toptal] ### Shell(쉘 정렬) + ![alt text][shell-image] -From [Wikipedia][shell-wiki]: 쉘 정렬은 멀리 떨어져 있는 항목의 교환을 허용하는 삽입 종류의 일반화이다. 그 아이디어는 모든 n번째 요소가 정렬된 목록을 제공한다는 것을 고려하여 어느 곳에서든지 시작하도록 요소의 목록을 배열하는 것이다. 이러한 목록은 h-sorted로 알려져 있다. 마찬가지로, 각각 개별적으로 정렬된 h 인터리브 목록으로 간주될 수 있다. +From [Wikipedia][shell-wiki]: 쉘 정렬은 멀리 떨어져 있는 항목의 교환을 허용하는 삽입 종류의 일반화이다. 그 아이디어는 모든 n번째 요소가 정렬된 목록을 제공한다는 것을 고려하여 어느 곳에서든지 시작하도록 요소의 목록을 배열하는 것이다. 이러한 목록은 h-sorted로 알려져 있다. 마찬가지로, 각각 개별적으로 정렬된 h 인터리브 목록으로 간주할 수 있다. + +**속성** -__속성__ -* 최악의 성능 O(nlog2 2n) -* 최고의 성능 O(n log n) -* Average case performance depends on gap sequence +- 최악의 성능 O(nlog2 2n) +- 최고의 성능 O(n log n) +- Average case performance depends on gap sequence ###### View the algorithm in [action][shell-toptal] @@ -91,97 +98,94 @@ __속성__ [복잡성 그래프](https://github.com/prateekiiest/Python/blob/master/sorts/sortinggraphs.png) ----------------------------------------------------------------------------------- +--- ## 검색 알고리즘 ### Linear (선형 탐색) + ![alt text][linear-image] From [Wikipedia][linear-wiki]: 선형 탐색 또는 순차 탐색은 목록 내에서 목표값을 찾는 방법이다. 일치 항목이 발견되거나 모든 요소가 탐색될 때까지 목록의 각 요소에 대해 목표값을 순차적으로 검사한다. - 선형 검색은 최악의 선형 시간으로 실행되며 최대 n개의 비교에서 이루어진다. 여기서 n은 목록의 길이다. +선형 검색은 최악의 선형 시간으로 실행되며 최대 n개의 비교에서 이루어진다. 여기서 n은 목록의 길이다. + +**속성** -__속성__ -* 최악의 성능 O(n) -* 최고의 성능 O(1) -* 평균 O(n) -* 최악의 경우 공간 복잡성 O(1) iterative +- 최악의 성능 O(n) +- 최고의 성능 O(1) +- 평균 O(n) +- 최악의 경우 공간 복잡성 O(1) iterative ### Binary (이진 탐색) + ![alt text][binary-image] -From [Wikipedia][binary-wiki]: 이진 탐색, (also known as half-interval search or logarithmic search), 은 정렬된 배열 내에서 목표값의 위치를 찾는 검색 알고리즘이다. 목표값을 배열의 중간 요소와 비교한다; 만약 목표값이 동일하지 않으면, 목표물의 절반이 제거되고 검색이 성공할 때까지 나머지 절반에서 게속된다. +From [Wikipedia][binary-wiki]: 이진 탐색, (also known as half-interval search or logarithmic search), 은 정렬된 배열 내에서 목표값의 위치를 찾는 검색 알고리즘이다. 목표값을 배열의 중간 요소와 비교한다; 만약 목표값이 동일하지 않으면, 목표물의 절반이 제거되고 검색이 성공할 때까지 나머지 절반에서 속된다. -__속성__ -* 최악의 성능 O(log n) -* 최고의 성능 O(1) -* 평균 O(log n) -* 최악의 경우 공간 복잡성 O(1) +**속성** +- 최악의 성능 O(log n) +- 최고의 성능 O(1) +- 평균 O(log n) +- 최악의 경우 공간 복잡성 O(1) [bubble-toptal]: https://www.toptal.com/developers/sorting-algorithms/bubble-sort [bubble-wiki]: https://en.wikipedia.org/wiki/Bubble_sort [bubble-image]: https://upload.wikimedia.org/wikipedia/commons/thumb/8/83/Bubblesort-edited-color.svg/220px-Bubblesort-edited-color.svg.png "Bubble Sort" - [insertion-toptal]: https://www.toptal.com/developers/sorting-algorithms/insertion-sort [insertion-wiki]: https://en.wikipedia.org/wiki/Insertion_sort [insertion-image]: https://upload.wikimedia.org/wikipedia/commons/7/7e/Insertionsort-edited.png "Insertion Sort" - [quick-toptal]: https://www.toptal.com/developers/sorting-algorithms/quick-sort [quick-wiki]: https://en.wikipedia.org/wiki/Quicksort [quick-image]: https://upload.wikimedia.org/wikipedia/commons/6/6a/Sorting_quicksort_anim.gif "Quick Sort" - [merge-toptal]: https://www.toptal.com/developers/sorting-algorithms/merge-sort [merge-wiki]: https://en.wikipedia.org/wiki/Merge_sort [merge-image]: https://upload.wikimedia.org/wikipedia/commons/c/cc/Merge-sort-example-300px.gif "Merge Sort" - [selection-toptal]: https://www.toptal.com/developers/sorting-algorithms/selection-sort [selection-wiki]: https://en.wikipedia.org/wiki/Selection_sort [selection-image]: https://upload.wikimedia.org/wikipedia/commons/thumb/b/b0/Selection_sort_animation.gif/250px-Selection_sort_animation.gif "Selection Sort Sort" - [shell-toptal]: https://www.toptal.com/developers/sorting-algorithms/shell-sort [shell-wiki]: https://en.wikipedia.org/wiki/Shellsort [shell-image]: https://upload.wikimedia.org/wikipedia/commons/d/d8/Sorting_shellsort_anim.gif "Shell Sort" - [linear-wiki]: https://en.wikipedia.org/wiki/Linear_search [linear-image]: http://www.tutorialspoint.com/data_structures_algorithms/images/linear_search.gif - [binary-wiki]: https://en.wikipedia.org/wiki/Binary_search_algorithm [binary-image]: https://upload.wikimedia.org/wikipedia/commons/f/f7/Binary_search_into_array.png +--- --------------------------------------------------------------------- ## 나머지 알고리즘에 대한 링크 -전환 | 다이나믹프로그래밍(DP) |암호|그 외 것들| ------------ |----------------------------------------------------------------|-------|-------------| -[Any Base to Any Base](Conversions/AnyBaseToAnyBase.java)| [Coin Change](Dynamic%20Programming/CoinChange.java)|[Caesar](ciphers/Caesar.java)|[Heap Sort](misc/heap_sort.java)| -[Any Base to Decimal](Conversions/AnyBaseToDecimal.java)|[Egg Dropping](Dynamic%20Programming/EggDropping.java)|[Columnar Transposition Cipher](ciphers/ColumnarTranspositionCipher.java)|[Palindromic Prime Checker](misc/PalindromicPrime.java)| -[Binary to Decimal](Conversions/BinaryToDecimal.java)|[Fibonacci](Dynamic%20Programming/Fibonacci.java)|[RSA](ciphers/RSA.java)|More soon...| -[Binary to HexaDecimal](Conversions/BinaryToHexadecimal.java)|[Kadane Algorithm](Dynamic%20Programming/KadaneAlgorithm.java)|more coming soon...| -[Binary to Octal](Conversions/BinaryToOctal.java)|[Knapsack](Dynamic%20Programming/Knapsack.java)| -[Decimal To Any Base](Conversions/DecimalToAnyBase.java)|[Longest Common Subsequence](Dynamic%20Programming/LongestCommonSubsequence.java)| -[Decimal To Binary](Conversions/DecimalToBinary.java)|[Longest Increasing Subsequence](Dynamic%20Programming/LongestIncreasingSubsequence.java)| -[Decimal To Hexadecimal](Conversions/DecimalToHexaDecimal.java)|[Rod Cutting](Dynamic%20Programming/RodCutting.java)| -and much more...| and more...| +| 전환 | 다이나믹프로그래밍(DP) | 암호 | 그 외 것들 | +| --------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------ | +| [Any Base to Any Base](Conversions/AnyBaseToAnyBase.java) | [Coin Change](DynamicProgramming/CoinChange.java) | [Caesar](Ciphers/Caesar.java) | [Heap Sort](Sorts/HeapSort.java) | +| [Any Base to Decimal](Conversions/AnyBaseToDecimal.java) | [Egg Dropping](DynamicProgramming/EggDropping.java) | [Columnar Transposition Cipher](Ciphers/ColumnarTranspositionCipher.java) | [Palindromic Prime Checker](Misc/PalindromePrime.java) | +| [Binary to Decimal](Conversions/BinaryToDecimal.java) | [Fibonacci](DynamicProgramming/Fibonacci.java) | [RSA](Ciphers/RSA.java) | More soon... | +| [Binary to HexaDecimal](Conversions/BinaryToHexadecimal.java) | [Kadane Algorithm](DynamicProgramming/KadaneAlgorithm.java) | more coming soon... | +| [Binary to Octal](Conversions/BinaryToOctal.java) | [Knapsack](DynamicProgramming/Knapsack.java) | +| [Decimal To Any Base](Conversions/DecimalToAnyBase.java) | [Longest Common Subsequence](DynamicProgramming/LongestCommonSubsequence.java) | +| [Decimal To Binary](Conversions/DecimalToBinary.java) | [Longest Increasing Subsequence](DynamicProgramming/LongestIncreasingSubsequence.java) | +| [Decimal To Hexadecimal](Conversions/DecimalToHexaDecimal.java) | [Rod Cutting](DynamicProgramming/RodCutting.java) | +| and much more... | and more... | ### 자료 구조 -그래프|힙|리스트|큐| -------|-----|-----|------| -[너비우선탐색](DataStructures/Graphs/BFS.java)|[빈 힙 예외처리](DataStructures/Heaps/EmptyHeapException.java)|[원형 연결리스트](DataStructures/Lists/CircleLinkedList.java)|[제너릭 어레이 리스트 큐](DataStructures/Queues/GenericArrayListQueue.java)| -[깊이우선탐색](DataStructures/Graphs/DFS.java)|[힙](DataStructures/Heaps/Heap.java)|[이중 연결리스트](DataStructures/Lists/DoublyLinkedList.java)|[큐](DataStructures/Queues/Queues.java)| -[그래프](DataStructures/Graphs/Graphs.java)|[힙 요소](DataStructures/Heaps/HeapElement.java)|[단순 연결리스트](DataStructures/Lists/SinglyLinkedList.java)| -[크루스칼 알고리즘](DataStructures/Graphs/KruskalsAlgorithm.java)|[최대힙](Data%Structures/Heaps/MaxHeap.java)| -[행렬 그래프](DataStructures/Graphs/MatrixGraphs.java)|[최소힙](DataStructures/Heaps/MinHeap.java)| -[프림 최소신장트리](DataStructures/Graphs/PrimMST.java)| - -스택|트리| -------|-----| -[노드 스택](DataStructures/Stacks/NodeStack.java)|[AVL 트리](DataStructures/Trees/AVLTree.java)| -[연결리스트 스택](DataStructures/Stacks/StackOfLinkedList.java)|[이진 트리](DataStructures/Trees/BinaryTree.java)| -[스택](DataStructures/Stacks)|And much more...| - -* [Bags](DataStructures/Bags/Bag.java) -* [Buffer](DataStructures/Buffers/CircularBuffer.java) -* [HashMap](DataStructures/HashMap/HashMap.java) -* [Matrix](DataStructures/Matrix/Matrix.java) + +| 그래프 | 힙 | 리스트 | 큐 | +| ------------------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------------- | +| | [빈 힙 예외처리](DataStructures/Heaps/EmptyHeapException.java) | [원형 연결리스트](DataStructures/Lists/CircleLinkedList.java) | [제너릭 어레이 리스트 큐](DataStructures/Queues/GenericArrayListQueue.java) | +| | [힙](DataStructures/Heaps/Heap.java) | [이중 연결리스트](DataStructures/Lists/DoublyLinkedList.java) | [큐](DataStructures/Queues/Queues.java) | +| [그래프](DataStructures/Graphs/Graphs.java) | [힙 요소](DataStructures/Heaps/HeapElement.java) | [단순 연결리스트](DataStructures/Lists/SinglyLinkedList.java) | +| [크루스칼 알고리즘](DataStructures/Graphs/Kruskal.java) | [최대힙](DataStructures/Heaps/MaxHeap.java) | +| [행렬 그래프](DataStructures/Graphs/MatrixGraphs.java) | [최소힙](DataStructures/Heaps/MinHeap.java) | +| [프림 최소신장트리](DataStructures/Graphs/PrimMST.java) | + +| 스택 | 트리 | +| --------------------------------------------------------------- | ------------------------------------------------- | +| [노드 스택](DataStructures/Stacks/NodeStack.java) | [AVL 트리](DataStructures/Trees/AVLTree.java) | +| [연결리스트 스택](DataStructures/Stacks/StackOfLinkedList.java) | [이진 트리](DataStructures/Trees/BinaryTree.java) | +| [스택](DataStructures/Stacks) | And much more... | + +- [Bags](DataStructures/Bags/Bag.java) +- [Buffer](DataStructures/Buffers/CircularBuffer.java) +- [HashMap](DataStructures/HashMap/Hashing/HashMap.java) +- diff --git a/README.md b/README.md index d0dd123e4d4e..d60d5104c385 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,20 @@ # The Algorithms - Java -[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/TheAlgorithms/100) +[![Build](https://github.com/TheAlgorithms/Java/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/TheAlgorithms/Java/actions/workflows/build.yml) +[![codecov](https://codecov.io/gh/TheAlgorithms/Java/graph/badge.svg?token=XAdPyqTIqR)](https://codecov.io/gh/TheAlgorithms/Java) +[![Discord chat](https://img.shields.io/discord/808045925556682782.svg?logo=discord&colorB=7289DA&style=flat-square)](https://discord.gg/c7MnfGFGa6) +[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/TheAlgorithms/Java) -NOTE: A [Development](https://github.com/TheAlgorithms/Java/tree/Development) branch is made for this repo where we are trying to migrate the existing project to a Java project structure. You can switch to [Development](https://github.com/TheAlgorithms/Java/tree/Development) branch for contributions. Please refer [this issue](https://github.com/TheAlgorithms/Java/issues/474) for more info. -You can play around (run and edit) the Algorithms or contribute to them using Gitpod.io a free online dev environment with a single click. No need to worry about the Dev enviroment. +You can run and edit the algorithms, or contribute to them using Gitpod.io (a free online development environment) with a single click. [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/TheAlgorithms/Java) - -### All algorithms implemented in Java (for education) -These implementations are for learning purposes. They may be less efficient than the implementations in the Java standard library. +### All algorithms are implemented in Java (for educational purposes) +These implementations are intended for learning purposes. As such, they may be less efficient than the Java standard library. ## Contribution Guidelines -Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute. - -## Community Channel -We're on [Gitter](https://gitter.im/TheAlgorithms)! Please join us. +Please read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute to this project. ## Algorithms -See our [directory](DIRECTORY.md). +Our [directory](DIRECTORY.md) has the full list of applications. diff --git a/Searches/BinarySearch.java b/Searches/BinarySearch.java deleted file mode 100644 index 8c7e113cc885..000000000000 --- a/Searches/BinarySearch.java +++ /dev/null @@ -1,95 +0,0 @@ -package Searches; - -import java.util.Arrays; -import java.util.Random; -import java.util.concurrent.ThreadLocalRandom; -import java.util.stream.IntStream; - -import static java.lang.String.format; - -/** - * - * - * - * Binary search is one of the most popular algorithms - * The algorithm finds the position of a target value within a sorted array - * - * Worst-case performance O(log n) - * Best-case performance O(1) - * Average performance O(log n) - * Worst-case space complexity O(1) - * - * - * @author Varun Upadhyay (https://github.com/varunu28) - * @author Podshivalov Nikita (https://github.com/nikitap492) - * - * @see SearchAlgorithm - * @see IterativeBinarySearch - * - */ - -class BinarySearch implements SearchAlgorithm { - - /** - * - * @param array is an array where the element should be found - * @param key is an element which should be found - * @param <T> is any comparable type - * @return index of the element - */ - @Override - public <T extends Comparable<T>> int find(T[] array, T key) { - return search(array, key, 0, array.length); - } - - /** - * This method implements the Generic Binary Search - * - * @param array The array to make the binary search - * @param key The number you are looking for - * @param left The lower bound - * @param right The upper bound - * @return the location of the key - **/ - private <T extends Comparable<T>> int search(T array[], T key, int left, int right){ - if (right < left) return -1; // this means that the key not found - - // find median - int median = (left + right) >>> 1; - int comp = key.compareTo(array[median]); - - if (comp == 0) { - return median; - } else if (comp < 0) { - return search(array, key, left, median - 1); - } else { - return search(array, key, median + 1, right); - } - } - - // Driver Program - public static void main(String[] args) { - // Just generate data - Random r = ThreadLocalRandom.current(); - - int size = 100; - int maxElement = 100000; - - Integer[] integers = IntStream.generate(() -> r.nextInt(maxElement)).limit(size).sorted().boxed().toArray(Integer[]::new); - - - // The element that should be found - int shouldBeFound = integers[r.nextInt(size - 1)]; - - BinarySearch search = new BinarySearch(); - int atIndex = search.find(integers, shouldBeFound); - - System.out.println(format( - "Should be found: %d. Found %d at index %d. An array length %d", - shouldBeFound, integers[atIndex], atIndex, size - )); - - int toCheck = Arrays.binarySearch(integers, shouldBeFound); - System.out.println(format("Found by system method at an index: %d. Is equal: %b", toCheck, toCheck == atIndex)); - } -} diff --git a/Searches/InterpolationSearch.java b/Searches/InterpolationSearch.java deleted file mode 100644 index 1b4f64a818b5..000000000000 --- a/Searches/InterpolationSearch.java +++ /dev/null @@ -1,75 +0,0 @@ -package Searches; - -import java.util.Arrays; -import java.util.Random; -import java.util.stream.IntStream; - -import static java.lang.String.format; - -/** - * Interpolation search algorithm implementation - * <p> - * Worst-case performance O(n) - * Best-case performance O(1) - * Average performance O(log(log(n))) if the elements are uniformly distributed if not O(n) - * Worst-case space complexity O(1) - * - * @author Podshivalov Nikita (https://github.com/nikitap492) - */ -class InterpolationSearch { - - - /** - * @param array is a sorted array - * @param key is a value what shoulb be found in the array - * @return an index if the array contains the key unless -1 - */ - public int find(int array[], int key) { - // Find indexes of two corners - int start = 0, end = (array.length - 1); - - // Since array is sorted, an element present - // in array must be in range defined by corner - while (start <= end && key >= array[start] && key <= array[end]) { - // Probing the position with keeping - // uniform distribution in mind. - int pos = start + (((end - start) / (array[end] - array[start])) * (key - array[start])); - - // Condition of target found - if (array[pos] == key) - return pos; - - // If key is larger, key is in upper part - if (array[pos] < key) - start = pos + 1; - - // If key is smaller, x is in lower part - else - end = pos - 1; - } - return -1; - } - - // Driver method - public static void main(String[] args) { - Random r = new Random(); - int size = 100; - int maxElement = 100000; - int[] integers = IntStream.generate(() -> r.nextInt(maxElement)).limit(size).sorted().toArray(); - - - //the element that should be found - Integer shouldBeFound = integers[r.nextInt(size - 1)]; - - InterpolationSearch search = new InterpolationSearch(); - int atIndex = search.find(integers, shouldBeFound); - - System.out.println(String.format("Should be found: %d. Found %d at index %d. An array length %d" - , shouldBeFound, integers[atIndex], atIndex, size)); - - - int toCheck = Arrays.binarySearch(integers, shouldBeFound); - System.out.println(format("Found by system method at an index: %d. Is equal: %b", toCheck, toCheck == atIndex)); - } -} - diff --git a/Searches/IterativeBinarySearch.java b/Searches/IterativeBinarySearch.java deleted file mode 100644 index 8df51a7d789b..000000000000 --- a/Searches/IterativeBinarySearch.java +++ /dev/null @@ -1,79 +0,0 @@ -package Searches; - -import java.util.Arrays; -import java.util.Random; -import java.util.stream.Stream; - -import static java.lang.String.format; - -/** - * Binary search is one of the most popular algorithms - * This class represents iterative version {@link BinarySearch} - * Iterative binary search is likely to have lower constant factors because it doesn't involve the overhead of manipulating the call stack. - * But in java the recursive version can be optimized by the compiler to this version. - * <p> - * Worst-case performance O(log n) - * Best-case performance O(1) - * Average performance O(log n) - * Worst-case space complexity O(1) - * - * @author Gabriele La Greca : https://github.com/thegabriele97 - * @author Podshivalov Nikita (https://github.com/nikitap492) - * @see SearchAlgorithm - * @see BinarySearch - */ - -public final class IterativeBinarySearch implements SearchAlgorithm { - - /** - * This method implements an iterative version of binary search algorithm - * - * @param array a sorted array - * @param key the key to search in array - * @return the index of key in the array or -1 if not found - */ - @Override - public <T extends Comparable<T>> int find(T[] array, T key) { - int l, r, k, cmp; - - l = 0; - r = array.length - 1; - - while (l <= r) { - k = (l + r) / 2; - cmp = key.compareTo(array[k]); - - if (cmp == 0) { - return k; - } else if (cmp < 0) { - r = --k; - } else { - l = ++k; - } - } - - return -1; - } - - //Only a main method for test purpose - public static void main(String[] args) { - Random r = new Random(); - int size = 100; - int maxElement = 100000; - Integer[] integers = Stream.generate(() -> r.nextInt(maxElement)).limit(size).sorted().toArray(Integer[]::new); - - - //the element that should be found - Integer shouldBeFound = integers[r.nextInt(size - 1)]; - - IterativeBinarySearch search = new IterativeBinarySearch(); - int atIndex = search.find(integers, shouldBeFound); - - System.out.println(String.format("Should be found: %d. Found %d at index %d. An array length %d" - , shouldBeFound, integers[atIndex], atIndex, size)); - - - int toCheck = Arrays.binarySearch(integers, shouldBeFound); - System.out.println(format("Found by system method at an index: %d. Is equal: %b", toCheck, toCheck == atIndex)); - } -} diff --git a/Searches/IterativeTernarySearch.java b/Searches/IterativeTernarySearch.java deleted file mode 100644 index 798cf444f47f..000000000000 --- a/Searches/IterativeTernarySearch.java +++ /dev/null @@ -1,83 +0,0 @@ -package Searches; - -import java.util.Arrays; -import java.util.Random; -import java.util.stream.Stream; - -import static java.lang.String.format; - -/** - * A iterative version of a ternary search algorithm - * This is better way to implement the ternary search, because a recursive version adds some overhead to a stack. - * But in java the compile can transform the recursive version to iterative implicitly, - * so there are no much differences between these two algorithms - * <p> - * Worst-case performance Θ(log3(N)) - * Best-case performance O(1) - * Average performance Θ(log3(N)) - * Worst-case space complexity O(1) - * - * @author Podshivalov Nikita (https://github.com/nikitap492) - * @see SearchAlgorithm - * @see TernarySearch - * @since 2018-04-13 - */ - -public class IterativeTernarySearch implements SearchAlgorithm { - - - @Override - public <T extends Comparable<T>> int find(T[] array, T key) { - int left = 0; - int right = array.length - 1; - - while (right > left) { - - int leftCmp = array[left].compareTo(key); - int rightCmp = array[right].compareTo(key); - if (leftCmp == 0) return left; - if (rightCmp == 0) return right; - - int leftThird = left + (right - left) / 3 + 1; - int rightThird = right - (right - left) / 3 - 1; - - - if (array[leftThird].compareTo(key) <= 0) { - left = leftThird; - } else { - right = rightThird; - } - } - - return -1; - } - - - public static void main(String[] args) { - //just generate data - Random r = new Random(); - int size = 100; - int maxElement = 100000; - Integer[] integers = Stream.generate(() -> r.nextInt(maxElement)) - .limit(size) - .sorted() - .toArray(Integer[]::new); - - - //the element that should be found - Integer shouldBeFound = integers[r.nextInt(size - 1)]; - - IterativeTernarySearch search = new IterativeTernarySearch(); - int atIndex = search.find(integers, shouldBeFound); - - System.out.println(format("Should be found: %d. Found %d at index %d. An array length %d", - shouldBeFound, integers[atIndex], atIndex, size)); - - int toCheck = Arrays.binarySearch(integers, shouldBeFound); - System.out.println(format("Found by system method at an index: %d. Is equal: %b", - toCheck, toCheck == atIndex)); - - } - - -} diff --git a/Searches/LinearSearch.java b/Searches/LinearSearch.java deleted file mode 100644 index 8a69b758d6dd..000000000000 --- a/Searches/LinearSearch.java +++ /dev/null @@ -1,60 +0,0 @@ -package Searches; - -import java.util.Random; -import java.util.stream.Stream; - -/** - * Linear search is the easiest search algorithm - * It works with sorted and unsorted arrays (an binary search works only with sorted array) - * This algorithm just compares all elements of an array to find a value - * <p> - * Worst-case performance O(n) - * Best-case performance O(1) - * Average performance O(n) - * Worst-case space complexity - * - * @author Varun Upadhyay (https://github.com/varunu28) - * @author Podshivalov Nikita (https://github.com/nikitap492) - * @see BinarySearch - * @see SearchAlgorithm - */ - -public class LinearSearch implements SearchAlgorithm { - - /** - * Generic Linear search method - * - * @param array List to be searched - * @param value Key being searched for - * @return Location of the key - */ - @Override - public <T extends Comparable<T>> int find(T[] array, T value) { - for (int i = 0; i < array.length; i++) { - if (array[i].compareTo(value) == 0) { - return i; - } - } - return -1; - } - - - public static void main(String[] args) { - //just generate data - Random r = new Random(); - int size = 200; - int maxElement = 100; - Integer[] integers = Stream.generate(() -> r.nextInt(maxElement)).limit(size).toArray(Integer[]::new); - - - //the element that should be found - Integer shouldBeFound = integers[r.nextInt(size - 1)]; - - LinearSearch search = new LinearSearch(); - int atIndex = search.find(integers, shouldBeFound); - - System.out.println(String.format("Should be found: %d. Found %d at index %d. An array length %d" - , shouldBeFound, integers[atIndex], atIndex, size)); - } - -} diff --git a/Searches/SaddlebackSearch.java b/Searches/SaddlebackSearch.java deleted file mode 100644 index eee2f0bb6559..000000000000 --- a/Searches/SaddlebackSearch.java +++ /dev/null @@ -1,80 +0,0 @@ -package Searches; - -import java.util.Scanner; - -/** - * Program to perform Saddleback Search - * Given a sorted 2D array(elements are sorted across every row and column, assuming ascending order) - * of size n*m we can search a given element in O(n+m) - * <p> - * we start from bottom left corner - * if the current element is greater than the given element then we move up - * else we move right - * Sample Input: - * 5 5 ->Dimensions - * -10 -5 -3 4 9 - * -6 -2 0 5 10 - * -4 -1 1 6 12 - * 2 3 7 8 13 - * 100 120 130 140 150 - * 140 ->element to be searched - * output: 4 3 // first value is row, second one is column - * - * @author Nishita Aggarwal - */ -public class SaddlebackSearch { - - /** - * This method performs Saddleback Search - * - * @param arr The **Sorted** array in which we will search the element. - * @param row the current row. - * @param col the current column. - * @param key the element that we want to search for. - * @return The index(row and column) of the element if found. - * Else returns -1 -1. - */ - private static int[] find(int arr[][], int row, int col, int key) { - - //array to store the answer row and column - int ans[] = {-1, -1}; - if (row < 0 || col >= arr[row].length) { - return ans; - } - if (arr[row][col] == key) { - ans[0] = row; - ans[1] = col; - return ans; - } - //if the current element is greater than the given element then we move up - else if (arr[row][col] > key) { - return find(arr, row - 1, col, key); - } - //else we move right - return find(arr, row, col + 1, key); - } - - /** - * Main method - * - * @param args Command line arguments - */ - public static void main(String[] args) { - // TODO Auto-generated method stub - Scanner sc = new Scanner(System.in); - int arr[][]; - int i, j, rows = sc.nextInt(), col = sc.nextInt(); - arr = new int[rows][col]; - for (i = 0; i < rows; i++) { - for (j = 0; j < col; j++) { - arr[i][j] = sc.nextInt(); - } - } - int ele = sc.nextInt(); - //we start from bottom left corner - int ans[] = find(arr, rows - 1, 0, ele); - System.out.println(ans[0] + " " + ans[1]); - sc.close(); - } - -} diff --git a/Searches/SearchAlgorithm.java b/Searches/SearchAlgorithm.java deleted file mode 100644 index 00dce17f0226..000000000000 --- a/Searches/SearchAlgorithm.java +++ /dev/null @@ -1,18 +0,0 @@ -package Searches; - -/** - * The common interface of most searching algorithms - * - * @author Podshivalov Nikita (https://github.com/nikitap492) - **/ -public interface SearchAlgorithm { - - /** - * @param key is an element which should be found - * @param array is an array where the element should be found - * @param <T> Comparable type - * @return first found index of the element - */ - <T extends Comparable<T>> int find(T array[], T key); - -} diff --git a/Searches/TernarySearch.java b/Searches/TernarySearch.java deleted file mode 100644 index ced67b0f56e7..000000000000 --- a/Searches/TernarySearch.java +++ /dev/null @@ -1,99 +0,0 @@ -package Searches; - - -import java.util.Arrays; -import java.util.Random; -import java.util.stream.Stream; - -import static java.lang.String.format; - -/** - * A ternary search algorithm is a technique in computer science for finding the minimum or maximum of a unimodal function - * The algorithm determines either that the minimum or maximum cannot be in the first third of the domain - * or that it cannot be in the last third of the domain, then repeats on the remaining third. - * <p> - * Worst-case performance Θ(log3(N)) - * Best-case performance O(1) - * Average performance Θ(log3(N)) - * Worst-case space complexity O(1) - * - * @author Podshivalov Nikita (https://github.com/nikitap492) - * @see SearchAlgorithm - * @see IterativeBinarySearch - */ - -public class TernarySearch implements SearchAlgorithm { - - /** - * @param arr The **Sorted** array in which we will search the element. - * @param value The value that we want to search for. - * @return The index of the element if found. - * Else returns -1. - */ - @Override - public <T extends Comparable<T>> int find(T[] arr, T value) { - return ternarySearch(arr, value, 0, arr.length - 1); - } - - /** - * @param arr The **Sorted** array in which we will search the element. - * @param key The value that we want to search for. - * @param start The starting index from which we will start Searching. - * @param end The ending index till which we will Search. - * @return Returns the index of the Element if found. - * Else returns -1. - */ - private <T extends Comparable<T>> int ternarySearch(T[] arr, T key, int start, int end) { - if (start > end) { - return -1; - } - /* First boundary: add 1/3 of length to start */ - int mid1 = start + (end - start) / 3; - /* Second boundary: add 2/3 of length to start */ - int mid2 = start + 2 * (end - start) / 3; - - if (key.compareTo(arr[mid1]) == 0) { - return mid1; - } else if (key.compareTo(arr[mid2]) == 0) { - return mid2; - } - - /* Search the first (1/3) rd part of the array.*/ - - else if (key.compareTo(arr[mid1]) < 0) { - return ternarySearch(arr, key, start, --mid1); - } - /* Search 3rd (1/3)rd part of the array */ - - else if (key.compareTo(arr[mid2]) > 0) { - return ternarySearch(arr, key, ++mid2, end); - } - /* Search middle (1/3)rd part of the array */ - - else { - return ternarySearch(arr, key, mid1, mid2); - } - } - - public static void main(String[] args) { - //just generate data - Random r = new Random(); - int size = 100; - int maxElement = 100000; - Integer[] integers = Stream.generate(() -> r.nextInt(maxElement)).limit(size).sorted().toArray(Integer[]::new); - - - //the element that should be found - Integer shouldBeFound = integers[r.nextInt(size - 1)]; - - TernarySearch search = new TernarySearch(); - int atIndex = search.find(integers, shouldBeFound); - - System.out.println(format("Should be found: %d. Found %d at index %d. An array length %d" - , shouldBeFound, integers[atIndex], atIndex, size)); - - int toCheck = Arrays.binarySearch(integers, shouldBeFound); - System.out.println(format("Found by system method at an index: %d. Is equal: %b", toCheck, toCheck == atIndex)); - - } -} \ No newline at end of file diff --git a/Sorts/BogoSort.java b/Sorts/BogoSort.java deleted file mode 100644 index 299c6b90ec5d..000000000000 --- a/Sorts/BogoSort.java +++ /dev/null @@ -1,54 +0,0 @@ -package Sorts; - -import java.util.Random; - - -/** - * @author Podshivalov Nikita (https://github.com/nikitap492) - * @see SortAlgorithm - */ -public class BogoSort implements SortAlgorithm { - - private static final Random random = new Random(); - - - private static <T extends Comparable<T>> boolean isSorted(T array[]) { - for (int i = 0; i < array.length - 1; i++) { - if (SortUtils.less(array[i + 1], array[i])) return false; - } - return true; - } - - // Randomly shuffles the array - private static <T> void nextPermutation(T array[]) { - int length = array.length; - - for (int i = 0; i < array.length; i++) { - int randomIndex = i + random.nextInt(length - i); - SortUtils.swap(array, randomIndex, i); - } - } - - public <T extends Comparable<T>> T[] sort(T array[]) { - while (!isSorted(array)) { - nextPermutation(array); - } - return array; - } - - // Driver Program - public static void main(String[] args) { - // Integer Input - Integer[] integers = {4, 23, 6, 78, 1, 54, 231, 9, 12}; - - BogoSort bogoSort = new BogoSort(); - - // print a sorted array - SortUtils.print(bogoSort.sort(integers)); - - // String Input - String[] strings = {"c", "a", "e", "b", "d"}; - - SortUtils.print(bogoSort.sort(strings)); - } -} diff --git a/Sorts/BubbleSort.java b/Sorts/BubbleSort.java deleted file mode 100644 index 29d588932c8f..000000000000 --- a/Sorts/BubbleSort.java +++ /dev/null @@ -1,50 +0,0 @@ -package Sorts; - -import static Sorts.SortUtils.*; - -/** - * @author Varun Upadhyay (https://github.com/varunu28) - * @author Podshivalov Nikita (https://github.com/nikitap492) - * @see SortAlgorithm - */ - -class BubbleSort implements SortAlgorithm { - /** - * This method implements the Generic Bubble Sort - * - * @param array The array to be sorted - * Sorts the array in increasing order - **/ - - @Override - public <T extends Comparable<T>> T[] sort(T array[]) { - for (int i = 0, size = array.length; i < size - 1; ++i) { - boolean swapped = false; - for (int j = 0; j < size - 1 - i; ++j) { - swapped = less(array[j], array[j + 1]) && swap(array, j, j + 1); - } - if (!swapped) { - break; - } - } - return array; - } - - // Driver Program - public static void main(String[] args) { - - // Integer Input - Integer[] integers = {4, 23, 6, 78, 1, 54, 231, 9, 12}; - BubbleSort bubbleSort = new BubbleSort(); - bubbleSort.sort(integers); - - // Output => 231, 78, 54, 23, 12, 9, 6, 4, 1 - print(integers); - - // String Input - String[] strings = {"c", "a", "e", "b", "d"}; - //Output => e, d, c, b, a - print(bubbleSort.sort(strings)); - - } -} diff --git a/Sorts/CocktailShakerSort.java b/Sorts/CocktailShakerSort.java deleted file mode 100644 index c3b5df999d23..000000000000 --- a/Sorts/CocktailShakerSort.java +++ /dev/null @@ -1,63 +0,0 @@ -package Sorts; - -/** - * @author Mateus Bizzo (https://github.com/MattBizzo) - * @author Podshivalov Nikita (https://github.com/nikitap492) - */ - -class CocktailShakerSort implements SortAlgorithm { - - /** - * This method implements the Generic Cocktail Shaker Sort - * - * @param array The array to be sorted - * Sorts the array in increasing order - **/ - - @Override - public <T extends Comparable<T>> T[] sort(T[] array) { - - int length = array.length; - int left = 0; - int right = length - 1; - int swappedLeft, swappedRight; - while (left < right) { - // front - swappedRight = 0; - for (int i = left; i < right; i++) { - if (SortUtils.less(array[i + 1], array[i])) { - SortUtils.swap(array, i, i + 1); - swappedRight = i; - } - } - // back - right = swappedRight; - swappedLeft = length - 1; - for (int j = right; j > left; j--) { - if (SortUtils.less(array[j], array[j - 1])) { - SortUtils.swap(array, j - 1, j); - swappedLeft = j; - } - } - left = swappedLeft; - } - return array; - - } - - // Driver Program - public static void main(String[] args) { - // Integer Input - Integer[] integers = {4, 23, 6, 78, 1, 54, 231, 9, 12}; - CocktailShakerSort shakerSort = new CocktailShakerSort(); - - // Output => 1 4 6 9 12 23 54 78 231 - SortUtils.print(shakerSort.sort(integers)); - - // String Input - String[] strings = {"c", "a", "e", "b", "d"}; - SortUtils.print(shakerSort.sort(strings)); - } - - -} diff --git a/Sorts/CombSort.java b/Sorts/CombSort.java deleted file mode 100644 index 23e38323ef36..000000000000 --- a/Sorts/CombSort.java +++ /dev/null @@ -1,73 +0,0 @@ -package Sorts; - -import static Sorts.SortUtils.*; - - -/** - * Comb Sort algorithm implementation - * <p> - * Best-case performance O(n * log(n)) - * Worst-case performance O(n ^ 2) - * Worst-case space complexity O(1) - * <p> - * Comb sort improves on bubble sort. - * - * @author Sandeep Roy (https://github.com/sandeeproy99) - * @author Podshivalov Nikita (https://github.com/nikitap492) - * @see BubbleSort - * @see SortAlgorithm - */ -class CombSort implements SortAlgorithm { - - // To find gap between elements - private int nextGap(int gap) { - // Shrink gap by Shrink factor - gap = (gap * 10) / 13; - return (gap < 1) ? 1 : gap; - } - - /** - * Function to sort arr[] using Comb - * - * @param arr - an array should be sorted - * @return sorted array - */ - @Override - public <T extends Comparable<T>> T[] sort(T arr[]) { - int size = arr.length; - - // initialize gap - int gap = size; - - // Initialize swapped as true to make sure that loop runs - boolean swapped = true; - - // Keep running while gap is more than 1 and last iteration caused a swap - while (gap != 1 || swapped) { - // Find next gap - gap = nextGap(gap); - - // Initialize swapped as false so that we can check if swap happened or not - swapped = false; - - // Compare all elements with current gap - for (int i = 0; i < size - gap; i++) { - if (less(arr[i + gap], arr[i])) { - // Swap arr[i] and arr[i+gap] - swapped = swap(arr, i, i + gap); - } - } - } - return arr; - } - - // Driver method - public static void main(String args[]) { - CombSort ob = new CombSort(); - Integer arr[] = {8, 4, 1, 56, 3, -44, -1, 0, 36, 34, 8, 12, -66, -78, 23, -6, 28, 0}; - ob.sort(arr); - - System.out.println("sorted array"); - print(arr); - } -} diff --git a/Sorts/CountingSort.java b/Sorts/CountingSort.java deleted file mode 100644 index 7f10da6cca76..000000000000 --- a/Sorts/CountingSort.java +++ /dev/null @@ -1,97 +0,0 @@ -package Sorts; - -import java.util.*; -import java.util.stream.IntStream; -import java.util.stream.Stream; - -import static java.util.stream.Collectors.toList; -import static java.util.stream.Collectors.toMap; -import static Sorts.SortUtils.print; - -/** - * @author Youssef Ali (https://github.com/youssefAli11997) - * @author Podshivalov Nikita (https://github.com/nikitap492) - */ -class CountingSort implements SortAlgorithm { - - @Override - public <T extends Comparable<T>> T[] sort(T[] unsorted) { - return sort(Arrays.asList(unsorted)).toArray(unsorted); - } - - /** - * This method implements the Generic Counting Sort - * - * @param list The list to be sorted - * <p> - * Sorts the list in increasing order - * The method uses list elements as keys in the frequency map - **/ - @Override - public <T extends Comparable<T>> List<T> sort(List<T> list) { - - Map<T, Integer> frequency = new TreeMap<>(); - // The final output array - List<T> sortedArray = new ArrayList<>(list.size()); - - // Counting the frequency of @param array elements - list.forEach(v -> frequency.put(v, frequency.getOrDefault(v, 0) + 1)); - - // Filling the sortedArray - for (Map.Entry<T, Integer> element : frequency.entrySet()) { - for (int j = 0; j < element.getValue(); j++) { - sortedArray.add(element.getKey()); - } - } - - return sortedArray; - } - - - /** - * Stream Counting Sort - * The same as method {@link CountingSort#sort(List)} } but this method uses stream API - * - * @param list The list to be sorted - **/ - private static <T extends Comparable<T>> List<T> streamSort(List<T> list) { - return list.stream() - .collect(toMap(k -> k, v -> 1, (v1, v2) -> v1 + v2, TreeMap::new)) - .entrySet() - .stream() - .flatMap(entry -> IntStream.rangeClosed(1, entry.getValue()).mapToObj(t -> entry.getKey())) - .collect(toList()); - } - - // Driver Program - public static void main(String[] args) { - // Integer Input - List<Integer> unsortedInts = Stream.of(4, 23, 6, 78, 1, 54, 23, 1, 9, 231, 9, 12).collect(toList()); - CountingSort countingSort = new CountingSort(); - - System.out.println("Before Sorting:"); - print(unsortedInts); - - // Output => 1 1 4 6 9 9 12 23 23 54 78 231 - System.out.println("After Sorting:"); - print(countingSort.sort(unsortedInts)); - System.out.println("After Sorting By Streams:"); - print(streamSort(unsortedInts)); - - System.out.println("\n------------------------------\n"); - - // String Input - List<String> unsortedStrings = Stream.of("c", "a", "e", "b", "d", "a", "f", "g", "c").collect(toList()); - - System.out.println("Before Sorting:"); - print(unsortedStrings); - - //Output => a a b c c d e f g - System.out.println("After Sorting:"); - print(countingSort.sort(unsortedStrings)); - - System.out.println("After Sorting By Streams:"); - print(streamSort(unsortedStrings)); - - } -} diff --git a/Sorts/CycleSort.java b/Sorts/CycleSort.java deleted file mode 100644 index 837c3c320454..000000000000 --- a/Sorts/CycleSort.java +++ /dev/null @@ -1,79 +0,0 @@ -package Sorts; - -import static Sorts.SortUtils.less; -import static Sorts.SortUtils.print; - -/** - * @author Podshivalov Nikita (https://github.com/nikitap492) - */ -class CycleSort implements SortAlgorithm { - - @Override - public <T extends Comparable<T>> T[] sort(T[] arr) { - int n = arr.length; - - // traverse array elements - for (int j = 0; j <= n - 2; j++) { - // initialize item as starting point - T item = arr[j]; - - // Find position where we put the item. - int pos = j; - for (int i = j + 1; i < n; i++) - if (less(arr[i], item)) pos++; - - // If item is already in correct position - if (pos == j) continue; - - // ignore all duplicate elements - while (item.compareTo(arr[pos]) == 0) - pos += 1; - - // put the item to it's right position - if (pos != j) { - item = replace(arr, pos, item); - } - - // Rotate rest of the cycle - while (pos != j) { - pos = j; - - // Find position where we put the element - for (int i = j + 1; i < n; i++) - if (less(arr[i], item)) { - pos += 1; - } - - - // ignore all duplicate elements - while (item.compareTo(arr[pos]) == 0) - pos += 1; - - // put the item to it's right position - if (item != arr[pos]) { - item = replace(arr, pos, item); - } - } - } - - return arr; - } - - private <T extends Comparable<T>> T replace(T[] arr, int pos, T item) { - T temp = item; - item = arr[pos]; - arr[pos] = temp; - return item; - } - - - public static void main(String[] args) { - Integer arr[] = {4, 23, 6, 78, 1, 26, 11, 23, 0, -6, 3, 54, 231, 9, 12}; - CycleSort cycleSort = new CycleSort(); - cycleSort.sort(arr); - - System.out.println("After sort : "); - print(arr); - } - -} diff --git a/Sorts/GnomeSort.java b/Sorts/GnomeSort.java deleted file mode 100644 index 8d5f62d438d2..000000000000 --- a/Sorts/GnomeSort.java +++ /dev/null @@ -1,45 +0,0 @@ -package Sorts; - -import static Sorts.SortUtils.*; - -/** - * Implementation of gnome sort - * - * @author Podshivalov Nikita (https://github.com/nikitap492) - * @since 2018-04-10 - **/ -public class GnomeSort implements SortAlgorithm { - - @Override - public <T extends Comparable<T>> T[] sort(T[] arr) { - int i = 1; - int j = 2; - while (i < arr.length) { - if (less(arr[i - 1], arr[i])) i = j++; - else { - swap(arr, i - 1, i); - if (--i == 0) { - i = j++; - } - } - } - - return null; - } - - public static void main(String[] args) { - Integer[] integers = {4, 23, 6, 78, 1, 26, 11, 23, 0, -6, 3, 54, 231, 9, 12}; - String[] strings = {"c", "a", "e", "b", "d", "dd", "da", "zz", "AA", "aa", "aB", "Hb", "Z"}; - GnomeSort gnomeSort = new GnomeSort(); - - gnomeSort.sort(integers); - gnomeSort.sort(strings); - - System.out.println("After sort : "); - print(integers); - print(strings); - - - } - -} diff --git a/Sorts/HeapSort.java b/Sorts/HeapSort.java deleted file mode 100644 index 77e63c7085b8..000000000000 --- a/Sorts/HeapSort.java +++ /dev/null @@ -1,129 +0,0 @@ -package Sorts; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import static Sorts.SortUtils.*; - -/** - * Heap Sort Algorithm - * Implements MinHeap - * - * @author Podshivalov Nikita (https://github.com/nikitap492) - */ -public class HeapSort implements SortAlgorithm { - - - private static class Heap<T extends Comparable<T>> { - /** - * Array to store heap - */ - private T[] heap; - - /** - * Constructor - * - * @param heap array of unordered integers - */ - public Heap(T[] heap) { - this.heap = heap; - } - - /** - * Heapifies subtree from top as root to last as last child - * - * @param rootIndex index of root - * @param lastChild index of last child - */ - private void heapSubtree(int rootIndex, int lastChild) { - int leftIndex = rootIndex * 2 + 1; - int rightIndex = rootIndex * 2 + 2; - T root = heap[rootIndex]; - if (rightIndex <= lastChild) { // if has right and left children - T left = heap[leftIndex]; - T right = heap[rightIndex]; - if (less(left, right) && less(left, root)) { - swap(heap, leftIndex, rootIndex); - heapSubtree(leftIndex, lastChild); - } else if (less(right, root)) { - swap(heap, rightIndex, rootIndex); - heapSubtree(rightIndex, lastChild); - } - } else if (leftIndex <= lastChild) { // if no right child, but has left child - T left = heap[leftIndex]; - if (less(left, root)) { - swap(heap, leftIndex, rootIndex); - heapSubtree(leftIndex, lastChild); - } - } - } - - - /** - * Makes heap with root as root - * - * @param root index of root of heap - */ - private void makeMinHeap(int root) { - int leftIndex = root * 2 + 1; - int rightIndex = root * 2 + 2; - boolean hasLeftChild = leftIndex < heap.length; - boolean hasRightChild = rightIndex < heap.length; - if (hasRightChild) { //if has left and right - makeMinHeap(leftIndex); - makeMinHeap(rightIndex); - heapSubtree(root, heap.length - 1); - } else if (hasLeftChild) { - heapSubtree(root, heap.length - 1); - } - } - - /** - * Gets the root of heap - * - * @return root of heap - */ - private T getRoot(int size) { - swap(heap, 0, size); - heapSubtree(0, size - 1); - return heap[size]; // return old root - } - - - } - - @Override - public <T extends Comparable<T>> T[] sort(T[] unsorted) { - return sort(Arrays.asList(unsorted)).toArray(unsorted); - } - - @Override - public <T extends Comparable<T>> List<T> sort(List<T> unsorted) { - int size = unsorted.size(); - - @SuppressWarnings("unchecked") - Heap<T> heap = new Heap<>(unsorted.toArray((T[]) new Comparable[unsorted.size()])); - - heap.makeMinHeap(0); // make min heap using index 0 as root. - List<T> sorted = new ArrayList<>(size); - while (size > 0) { - T min = heap.getRoot(--size); - sorted.add(min); - } - - return sorted; - } - - /** - * Main method - * - * @param args the command line arguments - */ - public static void main(String[] args) { - Integer[] heap = {4, 23, 6, 78, 1, 54, 231, 9, 12}; - HeapSort heapSort = new HeapSort(); - print(heapSort.sort(heap)); - } - -} diff --git a/Sorts/InsertionSort.java b/Sorts/InsertionSort.java deleted file mode 100644 index 9a7169f77dfa..000000000000 --- a/Sorts/InsertionSort.java +++ /dev/null @@ -1,58 +0,0 @@ -package Sorts; - -import static Sorts.SortUtils.less; -import static Sorts.SortUtils.print; - -/** - * @author Varun Upadhyay (https://github.com/varunu28) - * @author Podshivalov Nikita (https://github.com/nikitap492) - */ - -class InsertionSort implements SortAlgorithm { - - /** - * This method implements the Generic Insertion Sort - * Sorts the array in increasing order - * - * @param array The array to be sorted - **/ - - @Override - public <T extends Comparable<T>> T[] sort(T[] array) { - for (int j = 1; j < array.length; j++) { - - // Picking up the key(Card) - T key = array[j]; - int i = j - 1; - - while (i >= 0 && less(key, array[i])) { - array[i + 1] = array[i]; - i--; - } - // Placing the key (Card) at its correct position in the sorted subarray - array[i + 1] = key; - } - return array; - } - - // Driver Program - public static void main(String[] args) { - // Integer Input - Integer[] integers = {4, 23, 6, 78, 1, 54, 231, 9, 12}; - - InsertionSort sort = new InsertionSort(); - - sort.sort(integers); - - // Output => 1 4 6 9 12 23 54 78 231 - print(integers); - - // String Input - String[] strings = {"c", "a", "e", "b", "d"}; - - sort.sort(strings); - - //Output => a b c d e - print(strings); - } -} diff --git a/Sorts/MergeSort.java b/Sorts/MergeSort.java deleted file mode 100644 index 90392293b58f..000000000000 --- a/Sorts/MergeSort.java +++ /dev/null @@ -1,100 +0,0 @@ -package Sorts; - -import static Sorts.SortUtils.print; - -/** - * This method implements the Generic Merge Sort - * - * @author Varun Upadhyay (https://github.com/varunu28) - * @author Podshivalov Nikita (https://github.com/nikitap492) - * @see SortAlgorithm - */ - -class MergeSort implements SortAlgorithm { - - /** - * This method implements the Generic Merge Sort - * - * @param unsorted the array which should be sorted - * @param <T> Comparable class - * @return sorted array - */ - @Override - @SuppressWarnings("unchecked") - public <T extends Comparable<T>> T[] sort(T[] unsorted) { - T[] tmp = (T[]) new Comparable[unsorted.length]; - doSort(unsorted, tmp, 0, unsorted.length - 1); - return unsorted; - } - - /** - * @param arr The array to be sorted - * @param temp The copy of the actual array - * @param left The first index of the array - * @param right The last index of the array - * Recursively sorts the array in increasing order - **/ - private static <T extends Comparable<T>> void doSort(T[] arr, T[] temp, int left, int right) { - if (left < right) { - int mid = left + (right - left) / 2; - doSort(arr, temp, left, mid); - doSort(arr, temp, mid + 1, right); - merge(arr, temp, left, mid, right); - } - - } - - /** - * This method implements the merge step of the merge sort - * - * @param arr The array to be sorted - * @param temp The copy of the actual array - * @param left The first index of the array - * @param mid The middle index of the array - * @param right The last index of the array - * merges two parts of an array in increasing order - **/ - - private static <T extends Comparable<T>> void merge(T[] arr, T[] temp, int left, int mid, int right) { - System.arraycopy(arr, left, temp, left, right - left + 1); - - - int i = left; - int j = mid + 1; - int k = left; - - while (i <= mid && j <= right) { - if (temp[i].compareTo(temp[j]) <= 0) { - arr[k++] = temp[i++]; - } else { - arr[k++] = temp[j++]; - } - } - - while (i <= mid) { - arr[k++] = temp[i++]; - } - - while (j <= right) { - arr[k++] = temp[j++]; - } - } - - // Driver program - public static void main(String[] args) { - - // Integer Input - Integer[] arr = {4, 23, 6, 78, 1, 54, 231, 9, 12}; - MergeSort mergeSort = new MergeSort(); - mergeSort.sort(arr); - - // Output => 1 4 6 9 12 23 54 78 231 - print(arr); - - // String Inpu - String[] stringArray = {"c", "a", "e", "b", "d"}; - mergeSort.sort(stringArray); - //Output => a b c d e - print(stringArray); - } -} diff --git a/Sorts/PancakeSort.java b/Sorts/PancakeSort.java deleted file mode 100644 index 330e9d0f6a60..000000000000 --- a/Sorts/PancakeSort.java +++ /dev/null @@ -1,42 +0,0 @@ -package Sorts; - -import static Sorts.SortUtils.*; - -/** - * Implementation of gnome sort - * - * @author Podshivalov Nikita (https://github.com/nikitap492) - * @since 2018-04-10 - **/ -public class PancakeSort implements SortAlgorithm { - - @Override - public <T extends Comparable<T>> T[] sort(T[] array) { - int size = array.length; - - for (int i = 0; i < size; i++) { - T max = array[0]; - int index = 0; - for (int j = 0; j < size - i; j++) { - if (less(max, array[j])) { - max = array[j]; - index = j; - } - } - flip(array, index, array.length - 1 - i); - } - return array; - } - - - public static void main(String[] args) { - - Integer[] arr = {10, 9, 8, 7, 6, 15, 14, 7, 4, 3, 8, 6, 3, 1, 2, -2, -5, -8, -3, -1, 13, 12, 11, 5, 4, 3, 2, 1}; - PancakeSort pancakeSort = new PancakeSort(); - System.out.println("After sorting:"); - pancakeSort.sort(arr); - print(arr); - } - - -} diff --git a/Sorts/QuickSort.java b/Sorts/QuickSort.java deleted file mode 100644 index 47f79de0c35d..000000000000 --- a/Sorts/QuickSort.java +++ /dev/null @@ -1,105 +0,0 @@ -package Sorts; - -import static Sorts.SortUtils.*; - -/** - * @author Varun Upadhyay (https://github.com/varunu28) - * @author Podshivalov Nikita (https://github.com/nikitap492) - * @see SortAlgorithm - */ -class QuickSort implements SortAlgorithm { - - /** - * This method implements the Generic Quick Sort - * - * @param array The array to be sorted - * Sorts the array in increasing order - **/ - - @Override - public <T extends Comparable<T>> T[] sort(T[] array) { - doSort(array, 0, array.length - 1); - return array; - } - - - /** - * The sorting process - * - * @param left The first index of an array - * @param right The last index of an array - * @param array The array to be sorted - **/ - - private static <T extends Comparable<T>> void doSort(T[] array, int left, int right) { - if (left < right) { - int pivot = randomPartition(array, left, right); - doSort(array, left, pivot - 1); - doSort(array, pivot, right); - } - } - - /** - * Ramdomize the array to avoid the basically ordered sequences - * - * @param array The array to be sorted - * @param left The first index of an array - * @param right The last index of an array - * @return the partition index of the array - */ - - private static <T extends Comparable<T>> int randomPartition(T[] array, int left, int right) { - int randomIndex = left + (int)(Math.random()*(right - left + 1)); - swap(array, randomIndex, right); - return partition(array, left, right); - } - - /** - * This method finds the partition index for an array - * - * @param array The array to be sorted - * @param left The first index of an array - * @param right The last index of an array - * Finds the partition index of an array - **/ - - private static <T extends Comparable<T>> int partition(T[] array, int left, int right) { - int mid = (left + right) / 2; - T pivot = array[mid]; - - while (left <= right) { - while (less(array[left], pivot)) { - ++left; - } - while (less(pivot, array[right])) { - --right; - } - if (left <= right) { - swap(array, left, right); - ++left; - --right; - } - } - return left; - } - - // Driver Program - public static void main(String[] args) { - - // For integer input - Integer[] array = {3, 4, 1, 32, 0, 1, 5, 12, 2, 5, 7, 8, 9, 2, 44, 111, 5}; - - QuickSort quickSort = new QuickSort(); - quickSort.sort(array); - - //Output => 0 1 1 2 2 3 4 5 5 5 7 8 9 12 32 44 111 - print(array); - - String[] stringArray = {"c", "a", "e", "b", "d"}; - quickSort.sort(stringArray); - - //Output => a b c d e - print(stringArray); - } -} - diff --git a/Sorts/RadixSort.java b/Sorts/RadixSort.java deleted file mode 100644 index a207f6bbdda3..000000000000 --- a/Sorts/RadixSort.java +++ /dev/null @@ -1,59 +0,0 @@ -package Sorts; - -import java.util.Arrays; - -class RadixSort { - - private static int getMax(int arr[], int n) { - int mx = arr[0]; - for (int i = 1; i < n; i++) - if (arr[i] > mx) - mx = arr[i]; - return mx; - } - - private static void countSort(int arr[], int n, int exp) { - int output[] = new int[n]; - int i; - int count[] = new int[10]; - Arrays.fill(count, 0); - - for (i = 0; i < n; i++) - count[(arr[i] / exp) % 10]++; - - for (i = 1; i < 10; i++) - count[i] += count[i - 1]; - - for (i = n - 1; i >= 0; i--) { - output[count[(arr[i] / exp) % 10] - 1] = arr[i]; - count[(arr[i] / exp) % 10]--; - } - - for (i = 0; i < n; i++) - arr[i] = output[i]; - } - - private static void radixsort(int arr[], int n) { - - int m = getMax(arr, n); - - - for (int exp = 1; m / exp > 0; exp *= 10) - countSort(arr, n, exp); - } - - - static void print(int arr[], int n) { - for (int i = 0; i < n; i++) - System.out.print(arr[i] + " "); - } - - - public static void main(String[] args) { - int arr[] = {170, 45, 75, 90, 802, 24, 2, 66}; - int n = arr.length; - radixsort(arr, n); - print(arr, n); - } -} -// Written by James Mc Dermott(theycallmemac) diff --git a/Sorts/SelectionSort.java b/Sorts/SelectionSort.java deleted file mode 100644 index 9b5b0cbcdd67..000000000000 --- a/Sorts/SelectionSort.java +++ /dev/null @@ -1,58 +0,0 @@ -package Sorts; - -/** - * @author Varun Upadhyay (https://github.com/varunu28) - * @author Podshivalov Nikita (https://github.com/nikitap492) - * @see SortAlgorithm - */ - -public class SelectionSort implements SortAlgorithm { - - /** - * This method implements the Generic Selection Sort - * - * @param arr The array to be sorted - * Sorts the array in increasing order - **/ - @Override - public <T extends Comparable<T>> T[] sort(T[] arr) { - int n = arr.length; - for (int i = 0; i < n - 1; i++) { - // Initial index of min - int min = i; - - for (int j = i + 1; j < n; j++) { - if (SortUtils.less(arr[j], arr[min])) { - min = j; - } - } - - // Swapping if index of min is changed - if (min != i) { - SortUtils.swap(arr, i, min); - } - } - - return arr; - } - - // Driver Program - public static void main(String[] args) { - - Integer[] arr = {4, 23, 6, 78, 1, 54, 231, 9, 12}; - - SelectionSort selectionSort = new SelectionSort(); - - Integer[] sorted = selectionSort.sort(arr); - - // Output => 1 4 6 9 12 23 54 78 231 - SortUtils.print(sorted); - - // String Input - String[] strings = {"c", "a", "e", "b", "d"}; - String[] sortedStrings = selectionSort.sort(strings); - - //Output => a b c d e - SortUtils.print(sortedStrings); - } -} diff --git a/Sorts/ShellSort.java b/Sorts/ShellSort.java deleted file mode 100644 index 49be29b9d200..000000000000 --- a/Sorts/ShellSort.java +++ /dev/null @@ -1,49 +0,0 @@ -package Sorts; - -import static Sorts.SortUtils.*; - - -/** - * @author dpunosevac - * @author Podshivalov Nikita (https://github.com/nikitap492) - * @see SortAlgorithm - */ -public class ShellSort implements SortAlgorithm { - - /** - * This method implements Generic Shell Sort. - * - * @param array The array to be sorted - */ - @Override - public <T extends Comparable<T>> T[] sort(T[] array) { - int N = array.length; - int h = 1; - - while (h < N / 3) { - h = 3 * h + 1; - } - - while (h >= 1) { - for (int i = h; i < N; i++) { - for (int j = i; j >= h && less(array[j], array[j - h]); j -= h) { - swap(array, j, j - h); - } - } - - h /= 3; - } - - return array; - } - - public static void main(String[] args) { - Integer[] toSort = {4, 23, 6, 78, 1, 54, 231, 9, 12}; - - ShellSort sort = new ShellSort(); - Integer[] sorted = sort.sort(toSort); - - print(sorted); - - } -} diff --git a/Sorts/SortUtils.java b/Sorts/SortUtils.java deleted file mode 100644 index d5f592ad984d..000000000000 --- a/Sorts/SortUtils.java +++ /dev/null @@ -1,77 +0,0 @@ -package Sorts; - -import java.util.Arrays; -import java.util.List; - -/** - * The class contains util methods - * - * @author Podshivalov Nikita (https://github.com/nikitap492) - **/ -final class SortUtils { - - /** - * Helper method for swapping places in array - * - * @param array The array which elements we want to swap - * @param idx index of the first element - * @param idy index of the second element - */ - static <T> boolean swap(T[] array, int idx, int idy) { - T swap = array[idx]; - array[idx] = array[idy]; - array[idy] = swap; - return true; - } - - - /** - * This method checks if first element is less then the other element - * - * @param v first element - * @param w second element - * @return true if the first element is less then the second element - */ - static <T extends Comparable<T>> boolean less(T v, T w) { - return v.compareTo(w) < 0; - } - - - /** - * Just print list - * - * @param toPrint - a list which should be printed - */ - static void print(List<?> toPrint) { - toPrint.stream() - .map(Object::toString) - .map(str -> str + " ") - .forEach(System.out::print); - - System.out.println(); - } - - - /** - * Prints an array - * - * @param toPrint - the array which should be printed - */ - static void print(Object[] toPrint) { - System.out.println(Arrays.toString(toPrint)); - } - - - /** - * Swaps all position from {@param left} to @{@param right} for {@param array} - * - * @param array is an array - * @param left is a left flip border of the array - * @param right is a right flip border of the array - */ - static <T extends Comparable<T>> void flip(T[] array, int left, int right) { - while (left <= right) { - swap(array, left++, right--); - } - } -} diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 000000000000..d78724455af7 --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,198 @@ +<?xml version="1.0"?> +<!DOCTYPE module PUBLIC + "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" + "https://checkstyle.org/dtds/configuration_1_3.dtd"> + +<!-- + + Checkstyle configuration that checks the sun coding conventions from: + + - the Java Language Specification at + https://docs.oracle.com/javase/specs/jls/se11/html/index.html + + - the Sun Code Conventions at https://www.oracle.com/java/technologies/javase/codeconventions-contents.html + + - the Javadoc guidelines at + https://www.oracle.com/technical-resources/articles/java/javadoc-tool.html + + - the JDK Api documentation https://docs.oracle.com/en/java/javase/11/ + + - some best practices + + Checkstyle is very configurable. Be sure to read the documentation at + https://checkstyle.org (or in your downloaded distribution). + + Most Checks are configurable, be sure to consult the documentation. + + To completely disable a check, just comment it out or delete it from the file. + To suppress certain violations please review suppression filters. + + Finally, it is worth reading the documentation. + +--> + +<module name="Checker"> + <!-- + If you set the basedir property below, then all reported file + names will be relative to the specified directory. See + https://checkstyle.org/config.html#Checker + + <property name="basedir" value="${basedir}"/> + --> + <property name="severity" value="error"/> + + <property name="fileExtensions" value="java, properties, xml"/> + + <!-- Excludes all 'module-info.java' files --> + <!-- See https://checkstyle.org/filefilters/index.html --> + <module name="BeforeExecutionExclusionFileFilter"> + <property name="fileNamePattern" value="module\-info\.java$"/> + </module> + + <!-- https://checkstyle.org/filters/suppressionfilter.html --> + <module name="SuppressionFilter"> + <property name="file" value="${org.checkstyle.sun.suppressionfilter.config}" + default="checkstyle-suppressions.xml" /> + <property name="optional" value="true"/> + </module> + + <!-- Checks that a package-info.java file exists for each package. --> + <!-- See https://checkstyle.org/checks/javadoc/javadocpackage.html#JavadocPackage --> + <!-- TODO <module name="JavadocPackage"/> --> + + <!-- Checks whether files end with a new line. --> + <!-- See https://checkstyle.org/checks/misc/newlineatendoffile.html --> + <module name="NewlineAtEndOfFile"/> + + <!-- Checks that property files contain the same keys. --> + <!-- See https://checkstyle.org/checks/misc/translation.html --> + <module name="Translation"/> + + <!-- Checks for Size Violations. --> + <!-- See https://checkstyle.org/checks/sizes/index.html --> + <!-- TODO <module name="FileLength"/> --> + <!-- TODO <module name="LineLength"> + <property name="fileExtensions" value="java"/> + </module> --> + + <!-- Checks for whitespace --> + <!-- See https://checkstyle.org/checks/whitespace/index.html --> + <!-- TODO <module name="FileTabCharacter"/> --> + + <!-- Miscellaneous other checks. --> + <!-- See https://checkstyle.org/checks/misc/index.html --> + <module name="RegexpSingleline"> + <property name="format" value="\s+$"/> + <property name="minimum" value="0"/> + <property name="maximum" value="0"/> + <property name="message" value="Line has trailing spaces."/> + </module> + + <!-- Checks for Headers --> + <!-- See https://checkstyle.org/checks/header/index.html --> + <!-- <module name="Header"> --> + <!-- <property name="headerFile" value="${checkstyle.header.file}"/> --> + <!-- <property name="fileExtensions" value="java"/> --> + <!-- </module> --> + + <module name="TreeWalker"> + + <!-- Checks for Javadoc comments. --> + <!-- See https://checkstyle.org/checks/javadoc/index.html --> + <module name="InvalidJavadocPosition"/> + <!-- TODO <module name="JavadocMethod"/> --> + <!-- TODO <module name="JavadocType"/> --> + <!-- TODO <module name="JavadocVariable"/> --> + <!-- TODO <module name="JavadocStyle"/> --> + <!-- TODO <module name="MissingJavadocMethod"/> --> + + <!-- Checks for Naming Conventions. --> + <!-- See https://checkstyle.org/checks/naming/index.html --> + <module name="ConstantName"/> + <module name="LocalFinalVariableName"/> + <module name="LocalVariableName"/> + <module name="MemberName"/> + <module name="MethodName"/> + <module name="PackageName"/> + <module name="ParameterName"/> + <module name="StaticVariableName"/> + <module name="TypeName"/> + + <!-- Checks for imports --> + <!-- See https://checkstyle.org/checks/imports/index.html --> + <module name="AvoidStarImport"/> + <module name="IllegalImport"/> <!-- defaults to sun.* packages --> + <module name="RedundantImport"/> + <module name="UnusedImports"> + <property name="processJavadoc" value="false"/> + </module> + + <!-- Checks for Size Violations. --> + <!-- See https://checkstyle.org/checks/sizes/index.html --> + <module name="MethodLength"/> + <module name="ParameterNumber"/> + + <!-- Checks for whitespace --> + <!-- See https://checkstyle.org/checks/whitespace/index.html --> + <module name="EmptyForIteratorPad"/> + <!-- TODO <module name="GenericWhitespace"/> --> + <module name="MethodParamPad"/> + <!-- TODO <module name="NoWhitespaceAfter"/> --> + <module name="NoWhitespaceBefore"/> + <module name="OperatorWrap"/> + <module name="ParenPad"/> + <module name="TypecastParenPad"/> + <module name="WhitespaceAfter"/> + <module name="WhitespaceAround"/> + + <!-- Modifier Checks --> + <!-- See https://checkstyle.org/checks/modifier/index.html --> + <module name="ModifierOrder"/> + <module name="RedundantModifier"/> + + <!-- Checks for blocks. You know, those {}'s --> + <!-- See https://checkstyle.org/checks/blocks/index.html --> + <module name="AvoidNestedBlocks"/> + <!-- TODO <module name="EmptyBlock"/> --> + <!-- TODO <module name="LeftCurly"/> --> + <module name="NeedBraces"/> + <!-- TODO <module name="RightCurly"/> --> + + <!-- Checks for common coding problems --> + <!-- See https://checkstyle.org/checks/coding/index.html --> + <module name="EmptyStatement"/> + <module name="EqualsHashCode"/> + <!-- TODO <module name="HiddenField"/> --> + <module name="IllegalInstantiation"/> + <module name="InnerAssignment"/> + <!-- TODO <module name="MagicNumber"/> --> + <module name="MissingSwitchDefault"/> + <module name="MultipleVariableDeclarations"/> + <module name="SimplifyBooleanExpression"/> + <module name="SimplifyBooleanReturn"/> + + <!-- Checks for class design --> + <!-- See https://checkstyle.org/checks/design/index.html --> + <!-- TODO <module name="DesignForExtension"/> --> + <module name="FinalClass"/> + <module name="HideUtilityClassConstructor"/> + <module name="InterfaceIsType"/> + <!-- TODO <module name="VisibilityModifier"/> --> + + <!-- Miscellaneous other checks. --> + <!-- See https://checkstyle.org/checks/misc/index.html --> + <module name="ArrayTypeStyle"/> + <!-- TODO <module name="FinalParameters"/> --> + <module name="TodoComment"/> + <module name="UpperEll"/> + + <!-- https://checkstyle.org/filters/suppressionxpathfilter.html --> + <module name="SuppressionXpathFilter"> + <property name="file" value="${org.checkstyle.sun.suppressionxpathfilter.config}" + default="checkstyle-xpath-suppressions.xml" /> + <property name="optional" value="true"/> + </module> + + </module> + +</module> diff --git a/ciphers/AES.java b/ciphers/AES.java deleted file mode 100644 index 1bef34f0d4f3..000000000000 --- a/ciphers/AES.java +++ /dev/null @@ -1,601 +0,0 @@ -package ciphers; - -import java.math.BigInteger; -import java.util.Scanner; - -/** - * This class is build to demonstrate the application of the AES-algorithm on a - * single 128-Bit block of data. - * - */ -public class AES { - - /** - * Precalculated values for x to the power of 2 in Rijndaels galois field. Used - * as 'RCON' during the key expansion. - */ - private static final int[] RCON = { 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, - 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, - 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, - 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, - 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, - 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, - 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, - 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, - 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, - 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, - 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, - 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, - 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, - 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d }; - - /** - * Rijndael S-box Substitution table used for encryption in the subBytes step, - * as well as the key expansion. - */ - private static final int[] SBOX = { 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, - 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, - 0xC0, 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 0x04, - 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, - 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x00, 0xED, 0x20, - 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, - 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, - 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, - 0x3D, 0x64, 0x5D, 0x19, 0x73, 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, - 0x5E, 0x0B, 0xDB, 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, - 0x79, 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, 0xBA, - 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, - 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11, 0x69, - 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, - 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 }; - - /** - * Inverse Rijndael S-box Substitution table used for decryption in the - * subBytesDec step. - */ - private static final int[] INVERSE_SBOX = { 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, - 0x81, 0xF3, 0xD7, 0xFB, 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, - 0xE9, 0xCB, 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, - 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, 0x72, 0xF8, - 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, 0x6C, 0x70, 0x48, 0x50, - 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, - 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, - 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, - 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, - 0x1C, 0x75, 0xDF, 0x6E, 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, - 0xBE, 0x1B, 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, - 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, 0x60, 0x51, - 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, 0xA0, 0xE0, 0x3B, 0x4D, - 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, - 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D }; - - /** - * Precalculated lookup table for galois field multiplication by 2 used in the - * MixColums step during encryption. - */ - private static final int[] MULT2 = { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, - 0x1a, 0x1c, 0x1e, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, - 0x3e, 0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, - 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e, 0x80, 0x82, 0x84, - 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e, 0xa0, 0xa2, 0xa4, 0xa6, 0xa8, - 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe, 0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, - 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde, 0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, - 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe, 0x1b, 0x19, 0x1f, 0x1d, 0x13, 0x11, 0x17, 0x15, 0x0b, 0x09, 0x0f, - 0x0d, 0x03, 0x01, 0x07, 0x05, 0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35, 0x2b, 0x29, 0x2f, 0x2d, 0x23, - 0x21, 0x27, 0x25, 0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55, 0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, - 0x45, 0x7b, 0x79, 0x7f, 0x7d, 0x73, 0x71, 0x77, 0x75, 0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65, 0x9b, - 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d, 0x83, 0x81, 0x87, 0x85, 0xbb, 0xb9, 0xbf, - 0xbd, 0xb3, 0xb1, 0xb7, 0xb5, 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5, 0xdb, 0xd9, 0xdf, 0xdd, 0xd3, - 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5, 0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, - 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5 }; - - /** - * Precalculated lookup table for galois field multiplication by 3 used in the - * MixColums step during encryption. - */ - private static final int[] MULT3 = { 0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, - 0x17, 0x12, 0x11, 0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, - 0x21, 0x60, 0x63, 0x66, 0x65, 0x6c, 0x6f, 0x6a, 0x69, 0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71, 0x50, - 0x53, 0x56, 0x55, 0x5c, 0x5f, 0x5a, 0x59, 0x48, 0x4b, 0x4e, 0x4d, 0x44, 0x47, 0x42, 0x41, 0xc0, 0xc3, 0xc6, - 0xc5, 0xcc, 0xcf, 0xca, 0xc9, 0xd8, 0xdb, 0xde, 0xdd, 0xd4, 0xd7, 0xd2, 0xd1, 0xf0, 0xf3, 0xf6, 0xf5, 0xfc, - 0xff, 0xfa, 0xf9, 0xe8, 0xeb, 0xee, 0xed, 0xe4, 0xe7, 0xe2, 0xe1, 0xa0, 0xa3, 0xa6, 0xa5, 0xac, 0xaf, 0xaa, - 0xa9, 0xb8, 0xbb, 0xbe, 0xbd, 0xb4, 0xb7, 0xb2, 0xb1, 0x90, 0x93, 0x96, 0x95, 0x9c, 0x9f, 0x9a, 0x99, 0x88, - 0x8b, 0x8e, 0x8d, 0x84, 0x87, 0x82, 0x81, 0x9b, 0x98, 0x9d, 0x9e, 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, - 0x86, 0x8f, 0x8c, 0x89, 0x8a, 0xab, 0xa8, 0xad, 0xae, 0xa7, 0xa4, 0xa1, 0xa2, 0xb3, 0xb0, 0xb5, 0xb6, 0xbf, - 0xbc, 0xb9, 0xba, 0xfb, 0xf8, 0xfd, 0xfe, 0xf7, 0xf4, 0xf1, 0xf2, 0xe3, 0xe0, 0xe5, 0xe6, 0xef, 0xec, 0xe9, - 0xea, 0xcb, 0xc8, 0xcd, 0xce, 0xc7, 0xc4, 0xc1, 0xc2, 0xd3, 0xd0, 0xd5, 0xd6, 0xdf, 0xdc, 0xd9, 0xda, 0x5b, - 0x58, 0x5d, 0x5e, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, 0x4f, 0x4c, 0x49, 0x4a, 0x6b, 0x68, 0x6d, - 0x6e, 0x67, 0x64, 0x61, 0x62, 0x73, 0x70, 0x75, 0x76, 0x7f, 0x7c, 0x79, 0x7a, 0x3b, 0x38, 0x3d, 0x3e, 0x37, - 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2f, 0x2c, 0x29, 0x2a, 0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, - 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a }; - - /** - * Precalculated lookup table for galois field multiplication by 9 used in the - * MixColums step during decryption. - */ - private static final int[] MULT9 = { 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, - 0x65, 0x7e, 0x77, 0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, - 0xe7, 0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c, 0xab, - 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94, 0xe3, 0xea, 0xf1, 0xf8, 0xc7, 0xce, 0xd5, 0xdc, 0x76, 0x7f, 0x64, - 0x6d, 0x52, 0x5b, 0x40, 0x49, 0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01, 0xe6, 0xef, 0xf4, 0xfd, 0xc2, - 0xcb, 0xd0, 0xd9, 0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91, 0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, - 0x72, 0x05, 0x0c, 0x17, 0x1e, 0x21, 0x28, 0x33, 0x3a, 0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2, 0x95, - 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa, 0xec, 0xe5, 0xfe, 0xf7, 0xc8, 0xc1, 0xda, 0xd3, 0xa4, 0xad, 0xb6, - 0xbf, 0x80, 0x89, 0x92, 0x9b, 0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43, 0x34, 0x3d, 0x26, 0x2f, 0x10, - 0x19, 0x02, 0x0b, 0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8, 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, - 0xa0, 0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30, 0x9a, - 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9, 0xf6, 0xff, 0xe4, 0xed, 0x0a, 0x03, 0x18, - 0x11, 0x2e, 0x27, 0x3c, 0x35, 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d, 0xa1, 0xa8, 0xb3, 0xba, 0x85, - 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6, 0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, - 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46 }; - - /** - * Precalculated lookup table for galois field multiplication by 11 used in the - * MixColums step during decryption. - */ - private static final int[] MULT11 = { 0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, - 0x7f, 0x62, 0x69, 0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, - 0xd9, 0x7b, 0x70, 0x6d, 0x66, 0x57, 0x5c, 0x41, 0x4a, 0x23, 0x28, 0x35, 0x3e, 0x0f, 0x04, 0x19, 0x12, 0xcb, - 0xc0, 0xdd, 0xd6, 0xe7, 0xec, 0xf1, 0xfa, 0x93, 0x98, 0x85, 0x8e, 0xbf, 0xb4, 0xa9, 0xa2, 0xf6, 0xfd, 0xe0, - 0xeb, 0xda, 0xd1, 0xcc, 0xc7, 0xae, 0xa5, 0xb8, 0xb3, 0x82, 0x89, 0x94, 0x9f, 0x46, 0x4d, 0x50, 0x5b, 0x6a, - 0x61, 0x7c, 0x77, 0x1e, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2f, 0x8d, 0x86, 0x9b, 0x90, 0xa1, 0xaa, 0xb7, - 0xbc, 0xd5, 0xde, 0xc3, 0xc8, 0xf9, 0xf2, 0xef, 0xe4, 0x3d, 0x36, 0x2b, 0x20, 0x11, 0x1a, 0x07, 0x0c, 0x65, - 0x6e, 0x73, 0x78, 0x49, 0x42, 0x5f, 0x54, 0xf7, 0xfc, 0xe1, 0xea, 0xdb, 0xd0, 0xcd, 0xc6, 0xaf, 0xa4, 0xb9, - 0xb2, 0x83, 0x88, 0x95, 0x9e, 0x47, 0x4c, 0x51, 0x5a, 0x6b, 0x60, 0x7d, 0x76, 0x1f, 0x14, 0x09, 0x02, 0x33, - 0x38, 0x25, 0x2e, 0x8c, 0x87, 0x9a, 0x91, 0xa0, 0xab, 0xb6, 0xbd, 0xd4, 0xdf, 0xc2, 0xc9, 0xf8, 0xf3, 0xee, - 0xe5, 0x3c, 0x37, 0x2a, 0x21, 0x10, 0x1b, 0x06, 0x0d, 0x64, 0x6f, 0x72, 0x79, 0x48, 0x43, 0x5e, 0x55, 0x01, - 0x0a, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30, 0x59, 0x52, 0x4f, 0x44, 0x75, 0x7e, 0x63, 0x68, 0xb1, 0xba, 0xa7, - 0xac, 0x9d, 0x96, 0x8b, 0x80, 0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8, 0x7a, 0x71, 0x6c, 0x67, 0x56, - 0x5d, 0x40, 0x4b, 0x22, 0x29, 0x34, 0x3f, 0x0e, 0x05, 0x18, 0x13, 0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, - 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3 }; - - /** - * Precalculated lookup table for galois field multiplication by 13 used in the - * MixColums step during decryption. - */ - private static final int[] MULT13 = { 0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, - 0x51, 0x46, 0x4b, 0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, - 0x9b, 0xbb, 0xb6, 0xa1, 0xac, 0x8f, 0x82, 0x95, 0x98, 0xd3, 0xde, 0xc9, 0xc4, 0xe7, 0xea, 0xfd, 0xf0, 0x6b, - 0x66, 0x71, 0x7c, 0x5f, 0x52, 0x45, 0x48, 0x03, 0x0e, 0x19, 0x14, 0x37, 0x3a, 0x2d, 0x20, 0x6d, 0x60, 0x77, - 0x7a, 0x59, 0x54, 0x43, 0x4e, 0x05, 0x08, 0x1f, 0x12, 0x31, 0x3c, 0x2b, 0x26, 0xbd, 0xb0, 0xa7, 0xaa, 0x89, - 0x84, 0x93, 0x9e, 0xd5, 0xd8, 0xcf, 0xc2, 0xe1, 0xec, 0xfb, 0xf6, 0xd6, 0xdb, 0xcc, 0xc1, 0xe2, 0xef, 0xf8, - 0xf5, 0xbe, 0xb3, 0xa4, 0xa9, 0x8a, 0x87, 0x90, 0x9d, 0x06, 0x0b, 0x1c, 0x11, 0x32, 0x3f, 0x28, 0x25, 0x6e, - 0x63, 0x74, 0x79, 0x5a, 0x57, 0x40, 0x4d, 0xda, 0xd7, 0xc0, 0xcd, 0xee, 0xe3, 0xf4, 0xf9, 0xb2, 0xbf, 0xa8, - 0xa5, 0x86, 0x8b, 0x9c, 0x91, 0x0a, 0x07, 0x10, 0x1d, 0x3e, 0x33, 0x24, 0x29, 0x62, 0x6f, 0x78, 0x75, 0x56, - 0x5b, 0x4c, 0x41, 0x61, 0x6c, 0x7b, 0x76, 0x55, 0x58, 0x4f, 0x42, 0x09, 0x04, 0x13, 0x1e, 0x3d, 0x30, 0x27, - 0x2a, 0xb1, 0xbc, 0xab, 0xa6, 0x85, 0x88, 0x9f, 0x92, 0xd9, 0xd4, 0xc3, 0xce, 0xed, 0xe0, 0xf7, 0xfa, 0xb7, - 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8, 0xeb, 0xe6, 0xf1, 0xfc, 0x67, 0x6a, 0x7d, - 0x70, 0x53, 0x5e, 0x49, 0x44, 0x0f, 0x02, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c, 0x0c, 0x01, 0x16, 0x1b, 0x38, - 0x35, 0x22, 0x2f, 0x64, 0x69, 0x7e, 0x73, 0x50, 0x5d, 0x4a, 0x47, 0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, - 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97 }; - - /** - * Precalculated lookup table for galois field multiplication by 14 used in the - * MixColums step during decryption. - */ - private static final int[] MULT14 = { 0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, - 0x46, 0x54, 0x5a, 0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, - 0xba, 0xdb, 0xd5, 0xc7, 0xc9, 0xe3, 0xed, 0xff, 0xf1, 0xab, 0xa5, 0xb7, 0xb9, 0x93, 0x9d, 0x8f, 0x81, 0x3b, - 0x35, 0x27, 0x29, 0x03, 0x0d, 0x1f, 0x11, 0x4b, 0x45, 0x57, 0x59, 0x73, 0x7d, 0x6f, 0x61, 0xad, 0xa3, 0xb1, - 0xbf, 0x95, 0x9b, 0x89, 0x87, 0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7, 0x4d, 0x43, 0x51, 0x5f, 0x75, - 0x7b, 0x69, 0x67, 0x3d, 0x33, 0x21, 0x2f, 0x05, 0x0b, 0x19, 0x17, 0x76, 0x78, 0x6a, 0x64, 0x4e, 0x40, 0x52, - 0x5c, 0x06, 0x08, 0x1a, 0x14, 0x3e, 0x30, 0x22, 0x2c, 0x96, 0x98, 0x8a, 0x84, 0xae, 0xa0, 0xb2, 0xbc, 0xe6, - 0xe8, 0xfa, 0xf4, 0xde, 0xd0, 0xc2, 0xcc, 0x41, 0x4f, 0x5d, 0x53, 0x79, 0x77, 0x65, 0x6b, 0x31, 0x3f, 0x2d, - 0x23, 0x09, 0x07, 0x15, 0x1b, 0xa1, 0xaf, 0xbd, 0xb3, 0x99, 0x97, 0x85, 0x8b, 0xd1, 0xdf, 0xcd, 0xc3, 0xe9, - 0xe7, 0xf5, 0xfb, 0x9a, 0x94, 0x86, 0x88, 0xa2, 0xac, 0xbe, 0xb0, 0xea, 0xe4, 0xf6, 0xf8, 0xd2, 0xdc, 0xce, - 0xc0, 0x7a, 0x74, 0x66, 0x68, 0x42, 0x4c, 0x5e, 0x50, 0x0a, 0x04, 0x16, 0x18, 0x32, 0x3c, 0x2e, 0x20, 0xec, - 0xe2, 0xf0, 0xfe, 0xd4, 0xda, 0xc8, 0xc6, 0x9c, 0x92, 0x80, 0x8e, 0xa4, 0xaa, 0xb8, 0xb6, 0x0c, 0x02, 0x10, - 0x1e, 0x34, 0x3a, 0x28, 0x26, 0x7c, 0x72, 0x60, 0x6e, 0x44, 0x4a, 0x58, 0x56, 0x37, 0x39, 0x2b, 0x25, 0x0f, - 0x01, 0x13, 0x1d, 0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d, 0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, - 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d }; - - /** - * Subroutine of the Rijndael key expansion. - * - * @param t - * @param rconCounter - * @return - */ - public static BigInteger scheduleCore(BigInteger t, int rconCounter) { - String rBytes = t.toString(16); - - // Add zero padding - while (rBytes.length() < 8) { - rBytes = "0" + rBytes; - } - - // rotate the first 16 bits to the back - String rotatingBytes = rBytes.substring(0, 2); - String fixedBytes = rBytes.substring(2); - - rBytes = fixedBytes + rotatingBytes; - - // apply S-Box to all 8-Bit Substrings - for (int i = 0; i < 4; i++) { - String currentByteBits = rBytes.substring(i * 2, (i + 1) * 2); - - int currentByte = Integer.parseInt(currentByteBits, 16); - currentByte = SBOX[currentByte]; - - // add the current RCON value to the first byte - if (i == 0) { - currentByte = currentByte ^ RCON[rconCounter]; - } - - currentByteBits = Integer.toHexString(currentByte); - - // Add zero padding - - while (currentByteBits.length() < 2) { - currentByteBits = '0' + currentByteBits; - } - - // replace bytes in original string - rBytes = rBytes.substring(0, i * 2) + currentByteBits + rBytes.substring((i + 1) * 2); - } - - // t = new BigInteger(rBytes, 16); - // return t; - return new BigInteger(rBytes, 16); - } - - /** - * - * Returns an array of 10 + 1 round keys that are calculated by using Rijndael - * key schedule - * - * @param initialKey - * @return array of 10 + 1 round keys - */ - public static BigInteger[] keyExpansion(BigInteger initialKey) { - BigInteger[] roundKeys = { initialKey, new BigInteger("0"), new BigInteger("0"), new BigInteger("0"), - new BigInteger("0"), new BigInteger("0"), new BigInteger("0"), new BigInteger("0"), new BigInteger("0"), - new BigInteger("0"), new BigInteger("0"), }; - - // initialize rcon iteration - int rconCounter = 1; - - for (int i = 1; i < 11; i++) { - - // get the previous 32 bits the key - BigInteger t = roundKeys[i - 1].remainder(new BigInteger("100000000", 16)); - - // split previous key into 8-bit segments - BigInteger[] prevKey = { roundKeys[i - 1].remainder(new BigInteger("100000000", 16)), - roundKeys[i - 1].remainder(new BigInteger("10000000000000000", 16)) - .divide(new BigInteger("100000000", 16)), - roundKeys[i - 1].remainder(new BigInteger("1000000000000000000000000", 16)) - .divide(new BigInteger("10000000000000000", 16)), - roundKeys[i - 1].divide(new BigInteger("1000000000000000000000000", 16)), }; - - // run schedule core - t = scheduleCore(t, rconCounter); - rconCounter += 1; - - // Calculate partial round key - BigInteger t0 = t.xor(prevKey[3]); - BigInteger t1 = t0.xor(prevKey[2]); - BigInteger t2 = t1.xor(prevKey[1]); - BigInteger t3 = t2.xor(prevKey[0]); - - // Join round key segments - t2 = t2.multiply(new BigInteger("100000000", 16)); - t1 = t1.multiply(new BigInteger("10000000000000000", 16)); - t0 = t0.multiply(new BigInteger("1000000000000000000000000", 16)); - roundKeys[i] = t0.add(t1).add(t2).add(t3); - - } - return roundKeys; - } - - /** - * representation of the input 128-bit block as an array of 8-bit integers. - * - * @param block - * of 128-bit integers - * @return array of 8-bit integers - */ - public static int[] splitBlockIntoCells(BigInteger block) { - - int[] cells = new int[16]; - String blockBits = block.toString(2); - - // Append leading 0 for full "128-bit" string - while (blockBits.length() < 128) { - blockBits = '0' + blockBits; - } - - // split 128 to 8 bit cells - for (int i = 0; i < cells.length; i++) { - String cellBits = blockBits.substring(8 * i, 8 * (i + 1)); - cells[i] = Integer.parseInt(cellBits, 2); - } - - return cells; - } - - /** - * Returns the 128-bit BigInteger representation of the input of an array of - * 8-bit integers. - * - * @param cells - * that we need to merge - * @return block of merged cells - */ - public static BigInteger mergeCellsIntoBlock(int[] cells) { - - String blockBits = ""; - for (int i = 0; i < 16; i++) { - String cellBits = Integer.toBinaryString(cells[i]); - - // Append leading 0 for full "8-bit" strings - while (cellBits.length() < 8) { - cellBits = '0' + cellBits; - } - - blockBits += cellBits; - } - - return new BigInteger(blockBits, 2); - } - - /** - * - * @param ciphertext - * @param key - * @return ciphertext XOR key - */ - public static BigInteger addRoundKey(BigInteger ciphertext, BigInteger key) { - return ciphertext.xor(key); - } - - /** - * substitutes 8-Bit long substrings of the input using the S-Box and returns - * the result. - * - * @param ciphertext - * @return subtraction Output - */ - public static BigInteger subBytes(BigInteger ciphertext) { - - int[] cells = splitBlockIntoCells(ciphertext); - - for (int i = 0; i < 16; i++) { - cells[i] = SBOX[cells[i]]; - } - - return mergeCellsIntoBlock(cells); - } - - /** - * substitutes 8-Bit long substrings of the input using the inverse S-Box for - * decryption and returns the result. - * - * @param ciphertext - * @return subtraction Output - */ - public static BigInteger subBytesDec(BigInteger ciphertext) { - - int[] cells = splitBlockIntoCells(ciphertext); - - for (int i = 0; i < 16; i++) { - cells[i] = INVERSE_SBOX[cells[i]]; - } - - return mergeCellsIntoBlock(cells); - } - - /** - * Cell permutation step. Shifts cells within the rows of the input and returns - * the result. - * - * @param ciphertext - */ - public static BigInteger shiftRows(BigInteger ciphertext) { - int[] cells = splitBlockIntoCells(ciphertext); - int[] output = new int[16]; - - // do nothing in the first row - output[0] = cells[0]; - output[4] = cells[4]; - output[8] = cells[8]; - output[12] = cells[12]; - - // shift the second row backwards by one cell - output[1] = cells[5]; - output[5] = cells[9]; - output[9] = cells[13]; - output[13] = cells[1]; - - // shift the third row backwards by two cell - output[2] = cells[10]; - output[6] = cells[14]; - output[10] = cells[2]; - output[14] = cells[6]; - - // shift the forth row backwards by tree cell - output[3] = cells[15]; - output[7] = cells[3]; - output[11] = cells[7]; - output[15] = cells[11]; - - return mergeCellsIntoBlock(output); - } - - /** - * Cell permutation step for decryption . Shifts cells within the rows of the - * input and returns the result. - * - * @param ciphertext - */ - public static BigInteger shiftRowsDec(BigInteger ciphertext) { - int[] cells = splitBlockIntoCells(ciphertext); - int[] output = new int[16]; - - // do nothing in the first row - output[0] = cells[0]; - output[4] = cells[4]; - output[8] = cells[8]; - output[12] = cells[12]; - - // shift the second row forwards by one cell - output[1] = cells[13]; - output[5] = cells[1]; - output[9] = cells[5]; - output[13] = cells[9]; - - // shift the third row forwards by two cell - output[2] = cells[10]; - output[6] = cells[14]; - output[10] = cells[2]; - output[14] = cells[6]; - - // shift the forth row forwards by tree cell - output[3] = cells[7]; - output[7] = cells[11]; - output[11] = cells[15]; - output[15] = cells[3]; - - return mergeCellsIntoBlock(output); - } - - /** - * Applies the Rijndael MixColumns to the input and returns the result. - * - * @param ciphertext - */ - public static BigInteger mixColumns(BigInteger ciphertext) { - - int[] cells = splitBlockIntoCells(ciphertext); - int[] outputCells = new int[16]; - - for (int i = 0; i < 4; i++) { - int[] row = { cells[i * 4], cells[i * 4 + 1], cells[i * 4 + 2], cells[i * 4 + 3] }; - - outputCells[i * 4] = MULT2[row[0]] ^ MULT3[row[1]] ^ row[2] ^ row[3]; - outputCells[i * 4 + 1] = row[0] ^ MULT2[row[1]] ^ MULT3[row[2]] ^ row[3]; - outputCells[i * 4 + 2] = row[0] ^ row[1] ^ MULT2[row[2]] ^ MULT3[row[3]]; - outputCells[i * 4 + 3] = MULT3[row[0]] ^ row[1] ^ row[2] ^ MULT2[row[3]]; - } - return mergeCellsIntoBlock(outputCells); - } - - /** - * Applies the inverse Rijndael MixColumns for decryption to the input and - * returns the result. - * - * @param ciphertext - */ - public static BigInteger mixColumnsDec(BigInteger ciphertext) { - - int[] cells = splitBlockIntoCells(ciphertext); - int[] outputCells = new int[16]; - - for (int i = 0; i < 4; i++) { - int[] row = { cells[i * 4], cells[i * 4 + 1], cells[i * 4 + 2], cells[i * 4 + 3] }; - - outputCells[i * 4] = MULT14[row[0]] ^ MULT11[row[1]] ^ MULT13[row[2]] ^ MULT9[row[3]]; - outputCells[i * 4 + 1] = MULT9[row[0]] ^ MULT14[row[1]] ^ MULT11[row[2]] ^ MULT13[row[3]]; - outputCells[i * 4 + 2] = MULT13[row[0]] ^ MULT9[row[1]] ^ MULT14[row[2]] ^ MULT11[row[3]]; - outputCells[i * 4 + 3] = MULT11[row[0]] ^ MULT13[row[1]] ^ MULT9[row[2]] ^ MULT14[row[3]]; - } - return mergeCellsIntoBlock(outputCells); - } - - /** - * Encrypts the plaintext with the key and returns the result - * - * @param plainText - * which we want to encrypt - * @param key - * the key for encrypt - * @return EncryptedText - */ - public static BigInteger encrypt(BigInteger plainText, BigInteger key) { - BigInteger[] roundKeys = keyExpansion(key); - - // Initial round - plainText = addRoundKey(plainText, roundKeys[0]); - - // Main rounds - for (int i = 1; i < 10; i++) { - plainText = subBytes(plainText); - plainText = shiftRows(plainText); - plainText = mixColumns(plainText); - plainText = addRoundKey(plainText, roundKeys[i]); - } - - // Final round - plainText = subBytes(plainText); - plainText = shiftRows(plainText); - plainText = addRoundKey(plainText, roundKeys[10]); - - return plainText; - } - - /** - * Decrypts the ciphertext with the key and returns the result - * - * @param cipherText - * The Encrypted text which we want to decrypt - * @param key - * @return decryptedText - */ - public static BigInteger decrypt(BigInteger cipherText, BigInteger key) { - - BigInteger[] roundKeys = keyExpansion(key); - - // Invert final round - cipherText = addRoundKey(cipherText, roundKeys[10]); - cipherText = shiftRowsDec(cipherText); - cipherText = subBytesDec(cipherText); - - // Invert main rounds - for (int i = 9; i > 0; i--) { - cipherText = addRoundKey(cipherText, roundKeys[i]); - cipherText = mixColumnsDec(cipherText); - cipherText = shiftRowsDec(cipherText); - cipherText = subBytesDec(cipherText); - } - - // Invert initial round - cipherText = addRoundKey(cipherText, roundKeys[0]); - - return cipherText; - } - - public static void main(String[] args) { - - try (Scanner input = new Scanner(System.in)) { - System.out.println("Enter (e) letter for encrpyt or (d) letter for decrypt :"); - char choice = input.nextLine().charAt(0); - String in; - switch (choice) { - case 'E': - case 'e': - System.out.println("Choose a plaintext block (128-Bit Integer in base 16):"); - in = input.nextLine(); - BigInteger plaintext = new BigInteger(in, 16); - System.out.println("Choose a Key (128-Bit Integer in base 16):"); - in = input.nextLine(); - BigInteger encryptionKey = new BigInteger(in, 16); - System.out.println("The encrypted message is: \n" + encrypt(plaintext, encryptionKey).toString(16)); - break; - case 'D': - case 'd': - System.out.println("Enter your ciphertext block (128-Bit Integer in base 16):"); - in = input.nextLine(); - BigInteger ciphertext = new BigInteger(in, 16); - System.out.println("Choose a Key (128-Bit Integer in base 16):"); - in = input.nextLine(); - BigInteger decryptionKey = new BigInteger(in, 16); - System.out.println("The deciphered message is:\n" + decrypt(ciphertext, decryptionKey).toString(16)); - break; - default: - System.out.println("** End **"); - } - } - - } -} diff --git a/ciphers/AESEncryption.java b/ciphers/AESEncryption.java deleted file mode 100644 index 871bd52e2d14..000000000000 --- a/ciphers/AESEncryption.java +++ /dev/null @@ -1,114 +0,0 @@ -package ciphers; - -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.KeyGenerator; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.SecretKey; -import javax.xml.bind.DatatypeConverter; - -/** - * This example program shows how AES encryption and decryption can be done in - * Java. Please note that secret key and encrypted text is unreadable binary and - * hence in the following program we display it in hexadecimal format of the - * underlying bytes. - * - */ -public class AESEncryption { - - /** - * 1. Generate a plain text for encryption 2. Get a secret key (printed in - * hexadecimal form). In actual use this must by encrypted and kept safe. The - * same key is required for decryption. - * - */ - public static void main(String[] args) throws Exception { - String plainText = "Hello World"; - SecretKey secKey = getSecretEncryptionKey(); - byte[] cipherText = encryptText(plainText, secKey); - String decryptedText = decryptText(cipherText, secKey); - - System.out.println("Original Text:" + plainText); - System.out.println("AES Key (Hex Form):" + bytesToHex(secKey.getEncoded())); - System.out.println("Encrypted Text (Hex Form):" + bytesToHex(cipherText)); - System.out.println("Descrypted Text:" + decryptedText); - - } - - /** - * gets the AES encryption key. In your actual programs, this should be safely - * stored. - * - * @return secKey (Secret key that we encrypt using it) - * @throws NoSuchAlgorithmException - * (from KeyGenrator) - * - */ - public static SecretKey getSecretEncryptionKey() throws NoSuchAlgorithmException { - KeyGenerator aesKeyGenerator = KeyGenerator.getInstance("AES"); - aesKeyGenerator.init(128); // The AES key size in number of bits - SecretKey secKey = aesKeyGenerator.generateKey(); - return secKey; - } - - /** - * Encrypts plainText in AES using the secret key - * - * @param plainText - * @param secKey - * @return byteCipherText (The encrypted text) - * @throws NoSuchPaddingException - * (from Cipher) - * @throws NoSuchAlgorithmException - * (from Cipher) - * @throws InvalidKeyException - * (from Cipher) - * @throws BadPaddingException - * (from Cipher) - * @throws IllegalBlockSizeException - * (from Cipher) - */ - public static byte[] encryptText(String plainText, SecretKey secKey) throws NoSuchAlgorithmException, - NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { - // AES defaults to AES/ECB/PKCS5Padding in Java 7 - Cipher aesCipher = Cipher.getInstance("AES"); - aesCipher.init(Cipher.ENCRYPT_MODE, secKey); - byte[] byteCipherText = aesCipher.doFinal(plainText.getBytes()); - return byteCipherText; - } - - /** - * Decrypts encrypted byte array using the key used for encryption. - * - * @param byteCipherText - * @param secKey - * @return plainText - * @throws NoSuchPaddingException - * @throws NoSuchAlgorithmException - * @throws InvalidKeyException - * @throws BadPaddingException - * @throws IllegalBlockSizeException - */ - public static String decryptText(byte[] byteCipherText, SecretKey secKey) throws NoSuchAlgorithmException, - NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { - // AES defaults to AES/ECB/PKCS5Padding in Java 7 - Cipher aesCipher = Cipher.getInstance("AES"); - aesCipher.init(Cipher.DECRYPT_MODE, secKey); - byte[] bytePlainText = aesCipher.doFinal(byteCipherText); - return new String(bytePlainText); - } - - /** - * Convert a binary byte array into readable hex form - * - * @param hash - * (in binary) - * @return hexHash - */ - private static String bytesToHex(byte[] hash) { - return DatatypeConverter.printHexBinary(hash); - } -} diff --git a/ciphers/Caesar.java b/ciphers/Caesar.java deleted file mode 100644 index 06319f79df49..000000000000 --- a/ciphers/Caesar.java +++ /dev/null @@ -1,131 +0,0 @@ -package ciphers; - -import java.util.Scanner; - -/** - * - * A Java implementation of Caesar Cipher. /It is a type of substitution cipher - * in which each letter in the plaintext is replaced by a letter some fixed - * number of positions down the alphabet. / - * - * @author FAHRI YARDIMCI - * @author khalil2535 - */ -public class Caesar { - - /** - * Encrypt text by shifting every Latin char by add number shift for ASCII - * Example : A + 1 -> B - * - * @param message - * @param shift - * @return Encrypted message - */ - public static String encode(String message, int shift) { - String encoded = ""; - - - shift %= 26; - - - final int length = message.length(); - for (int i = 0; i < length; i++) { - -// int current = message.charAt(i); //using char to shift characters because ascii is in-order latin alphabet - char current = message.charAt(i); // Java law : char + int = char - - if (IsCapitalLatinLetter(current)) { - - current += shift; - encoded += (char) (current > 'Z' ? current - 26 : current); // 26 = number of latin letters - - } else if (IsSmallLatinLetter(current)) { - - current += shift; - encoded += (char) (current > 'z' ? current - 26 : current); // 26 = number of latin letters - - } else { - encoded += current; - } - } - return encoded; - } - - /** - * Decrypt message by shifting back every Latin char to previous the ASCII - * Example : B - 1 -> A - * - * @param encryptedMessage - * @param shift - * @return message - */ - public static String decode(String encryptedMessage, int shift) { - String decoded = ""; - - - shift %= 26; - - - final int length = encryptedMessage.length(); - for (int i = 0; i < length; i++) { - char current = encryptedMessage.charAt(i); - if (IsCapitalLatinLetter(current)) { - - current -= shift; - decoded += (char) (current < 'A' ? current + 26 : current);// 26 = number of latin letters - - } else if (IsSmallLatinLetter(current)) { - - current -= shift; - decoded += (char) (current < 'a' ? current + 26 : current);// 26 = number of latin letters - - } else { - decoded += current; - } - } - return decoded; - } - - /** - * - * @param c - * @return true if character is capital Latin letter or false for others - */ - private static boolean IsCapitalLatinLetter(char c) { - return c >= 'A' && c <= 'Z'; - } - - /** - * - * @param c - * @return true if character is small Latin letter or false for others - */ - private static boolean IsSmallLatinLetter(char c) { - return c >= 'a' && c <= 'z'; - } - - /** - * - * @deprecated TODO remove main and make JUnit Testing - */ - public static void main(String[] args) { - Scanner input = new Scanner(System.in); - System.out.println("Please enter the message (Latin Alphabet)"); - String message = input.nextLine(); - System.out.println(message); - System.out.println("Please enter the shift number"); - int shift = input.nextInt() % 26; - System.out.println("(E)ncode or (D)ecode ?"); - char choice = input.next().charAt(0); - switch (choice) { - case 'E': - case 'e': - System.out.println("ENCODED MESSAGE IS \n" + encode(message, shift)); //send our function to handle - break; - case 'D': - case 'd': - System.out.println("DECODED MESSAGE IS \n" + decode(message, shift)); - } - } - -} diff --git a/ciphers/ColumnarTranspositionCipher.java b/ciphers/ColumnarTranspositionCipher.java deleted file mode 100644 index 26acc628e393..000000000000 --- a/ciphers/ColumnarTranspositionCipher.java +++ /dev/null @@ -1,224 +0,0 @@ -package ciphers; - -/** - * Columnar Transposition Cipher Encryption and Decryption. - * - * @author <a href="https://github.com/freitzzz">freitzzz</a> - */ -public class ColumnarTranspositionCipher { - - private static String keyword; - private static Object[][] table; - private static String abecedarium; - public static final String ABECEDARIUM = "abcdefghijklmnopqrstuvwxyzABCDEFG" - + "HIJKLMNOPQRSTUVWXYZ0123456789,.;:-@"; - private static final String ENCRYPTION_FIELD = "≈"; - private static final char ENCRYPTION_FIELD_CHAR = '≈'; - - /** - * Encrypts a certain String with the Columnar Transposition Cipher Rule - * - * @param word Word being encrypted - * @param keyword String with keyword being used - * @return a String with the word encrypted by the Columnar Transposition - * Cipher Rule - */ - public static String encrpyter(String word, String keyword) { - ColumnarTranspositionCipher.keyword = keyword; - abecedariumBuilder(500); - table = tableBuilder(word); - Object[][] sortedTable = sortTable(table); - String wordEncrypted = ""; - for (int i = 0; i < sortedTable[i].length; i++) { - for (int j = 1; j < sortedTable.length; j++) { - wordEncrypted += sortedTable[j][i]; - } - } - return wordEncrypted; - } - - /** - * Encrypts a certain String with the Columnar Transposition Cipher Rule - * - * @param word Word being encrypted - * @param keyword String with keyword being used - * @param abecedarium String with the abecedarium being used. null for - * default one - * @return a String with the word encrypted by the Columnar Transposition - * Cipher Rule - */ - public static String encrpyter(String word, String keyword, - String abecedarium) { - ColumnarTranspositionCipher.keyword = keyword; - if (abecedarium != null) { - ColumnarTranspositionCipher.abecedarium = abecedarium; - } else { - ColumnarTranspositionCipher.abecedarium = ABECEDARIUM; - } - table = tableBuilder(word); - Object[][] sortedTable = sortTable(table); - String wordEncrypted = ""; - for (int i = 0; i < sortedTable[0].length; i++) { - for (int j = 1; j < sortedTable.length; j++) { - wordEncrypted += sortedTable[j][i]; - } - } - return wordEncrypted; - } - - /** - * Decrypts a certain encrypted String with the Columnar Transposition - * Cipher Rule - * - * @return a String decrypted with the word encrypted by the Columnar - * Transposition Cipher Rule - */ - public static String decrypter() { - String wordDecrypted = ""; - for (int i = 1; i < table.length; i++) { - for (Object item : table[i]) { - wordDecrypted += item; - } - } - return wordDecrypted.replaceAll(ENCRYPTION_FIELD, ""); - } - - /** - * Builds a table with the word to be encrypted in rows by the Columnar - * Transposition Cipher Rule - * - * @return An Object[][] with the word to be encrypted filled in rows and - * columns - */ - private static Object[][] tableBuilder(String word) { - Object[][] table = new Object[numberOfRows(word) + 1][keyword.length()]; - char[] wordInChards = word.toCharArray(); - //Fils in the respective numbers - table[0] = findElements(); - int charElement = 0; - for (int i = 1; i < table.length; i++) { - for (int j = 0; j < table[i].length; j++) { - if (charElement < wordInChards.length) { - table[i][j] = wordInChards[charElement]; - charElement++; - } else { - table[i][j] = ENCRYPTION_FIELD_CHAR; - } - } - } - return table; - } - - /** - * Determines the number of rows the table should have regarding the - * Columnar Transposition Cipher Rule - * - * @return an int with the number of rows that the table should have in - * order to respect the Columnar Transposition Cipher Rule. - */ - private static int numberOfRows(String word) { - if ((double) word.length() / keyword.length() > word.length() / keyword.length()) { - return (word.length() / keyword.length()) + 1; - } else { - return word.length() / keyword.length(); - } - } - - /** - * - * @return charValues - */ - private static Object[] findElements() { - Object[] charValues = new Object[keyword.length()]; - for (int i = 0; i < charValues.length; i++) { - int charValueIndex = abecedarium.indexOf(keyword.charAt(i)); - charValues[i] = charValueIndex > -1 ? charValueIndex : null; - } - return charValues; - } - - /** - * - * @param table - * @return tableSorted - */ - private static Object[][] sortTable(Object[][] table) { - Object[][] tableSorted = new Object[table.length][table[0].length]; - for (int i = 0; i < tableSorted.length; i++) { - System.arraycopy(table[i], 0, tableSorted[i], 0, tableSorted[i].length); - } - for (int i = 0; i < tableSorted[0].length; i++) { - for (int j = i + 1; j < tableSorted[0].length; j++) { - if ((int) tableSorted[0][i] > (int) table[0][j]) { - Object[] column = getColumn(tableSorted, tableSorted.length, i); - switchColumns(tableSorted, j, i, column); - } - } - } - return tableSorted; - } - - /** - * - * @param table - * @param rows - * @param column - * @return columnArray - */ - private static Object[] getColumn(Object[][] table, int rows, int column) { - Object[] columnArray = new Object[rows]; - for (int i = 0; i < rows; i++) { - columnArray[i] = table[i][column]; - } - return columnArray; - } - - /** - * - * @param table - * @param firstColumnIndex - * @param secondColumnIndex - * @param columnToSwitch - */ - private static void switchColumns(Object[][] table, int firstColumnIndex, - int secondColumnIndex, Object[] columnToSwitch) { - for (int i = 0; i < table.length; i++) { - table[i][secondColumnIndex] = table[i][firstColumnIndex]; - table[i][firstColumnIndex] = columnToSwitch[i]; - } - } - - /** - * Creates an abecedarium with a specified ascii inded - * - * @param value Number of characters being used based on the ASCII Table - */ - private static void abecedariumBuilder(int value) { - abecedarium = ""; - for (int i = 0; i < value; i++) { - abecedarium += (char) i; - } - } - - private static void showTable() { - for (Object[] table1 : table) { - for (Object item : table1) { - System.out.print(item + " "); - } - System.out.println(); - } - } - - public static void main(String[] args) { - String keywordForExample = "asd215"; - String wordBeingEncrypted = "This is a test of the Columnar Transposition Cipher"; - System.out.println("### Example of Columnar Transposition Cipher ###\n"); - System.out.println("Word being encryped ->>> " + wordBeingEncrypted); - System.out.println("Word encrypted ->>> " + ColumnarTranspositionCipher - .encrpyter(wordBeingEncrypted, keywordForExample)); - System.out.println("Word decryped ->>> " + ColumnarTranspositionCipher - .decrypter()); - System.out.println("\n### Encrypted Table ###"); - showTable(); - } -} diff --git a/ciphers/RSA.java b/ciphers/RSA.java deleted file mode 100644 index acb99f0f664a..000000000000 --- a/ciphers/RSA.java +++ /dev/null @@ -1,98 +0,0 @@ -package ciphers; - -import java.math.BigInteger; -import java.security.SecureRandom; -import javax.swing.JOptionPane; - -/** - * @author Nguyen Duy Tiep on 23-Oct-17. - */ -public final class RSA { - - /** - * Trivial test program. - * - * @param args - * @deprecated TODO remove main and make JUnit Testing or any other - * methodology - */ - public static void main(String[] args) { - - RSA rsa = new RSA(1024); - String text1 = JOptionPane.showInputDialog("Enter a message to encrypt :"); - - String ciphertext = rsa.encrypt(text1); - JOptionPane.showMessageDialog(null, "Your encrypted message : " + ciphertext); - - JOptionPane.showMessageDialog(null, "Your message after decrypt : " + rsa.decrypt(ciphertext)); - } - - private BigInteger modulus, privateKey, publicKey; - - /** - * - * @param bits - */ - public RSA(int bits) { - generateKeys(bits); - } - - /** - * - * @param message - * @return encrypted message - */ - public synchronized String encrypt(String message) { - return (new BigInteger(message.getBytes())).modPow(publicKey, modulus).toString(); - } - - /** - * - * @param message - * @return encrypted message as big integer - */ - public synchronized BigInteger encrypt(BigInteger message) { - return message.modPow(publicKey, modulus); - } - - /** - * - * @param encryptedMessage - * @return plain message - */ - public synchronized String decrypt(String encryptedMessage) { - return new String((new BigInteger(encryptedMessage)).modPow(privateKey, modulus).toByteArray()); - } - - /** - * - * @param encryptedMessage - * @return plain message as big integer - */ - public synchronized BigInteger decrypt(BigInteger encryptedMessage) { - return encryptedMessage.modPow(privateKey, modulus); - } - - /** - * Generate a new public and private key set. - * - * @param bits - */ - public synchronized void generateKeys(int bits) { - SecureRandom r = new SecureRandom(); - BigInteger p = new BigInteger(bits / 2, 100, r); - BigInteger q = new BigInteger(bits / 2, 100, r); - modulus = p.multiply(q); - - BigInteger m = (p.subtract(BigInteger.ONE)).multiply(q.subtract(BigInteger.ONE)); - - publicKey = new BigInteger("3"); - - while (m.gcd(publicKey).intValue() > 1) { - publicKey = publicKey.add(new BigInteger("2")); - } - - privateKey = publicKey.modInverse(m); - } - -} diff --git a/ciphers/Vigenere.java b/ciphers/Vigenere.java deleted file mode 100644 index 48c54288dcf8..000000000000 --- a/ciphers/Vigenere.java +++ /dev/null @@ -1,67 +0,0 @@ -package ciphers; - -/** - * A Java implementation of Vigenere Cipher. - * @author straiffix - */ - - -public class Vigenere { - - public static String encrypt(final String message, final String key) - { - - String result = ""; - - for (int i = 0, j = 0; i < message.length(); i++) { - char c = message.charAt(i); - if (Character.isLetter(c)){ - if(Character.isUpperCase(c)) { - result += (char) ((c + key.toUpperCase().charAt(j) - 2 * 'A') % 26 + 'A'); - - } else { - result += (char) ((c + key.toLowerCase().charAt(j) - 2 * 'a') % 26 + 'a'); - - } - } else { - result+=c; - } - j = ++j % key.length(); - } - return result; - } - - public static String decrypt( final String message, final String key) - { - String result =""; - - for(int i = 0, j = 0; i < message.length(); i++){ - - char c = message.charAt(i); - if (Character.isLetter(c)){ - if(Character.isUpperCase(c)) { - result += ((char)('Z'-(25-(c-key.toUpperCase().charAt(j)))%26)); - - } else { - result += ((char)('z'-(25-(c-key.toLowerCase().charAt(j)))%26)); - - } - } else { - result+=c; - } - - j = ++j % key.length(); - - } - return result; - } - public static void main (String [] args){ - String text="Hello World!"; - String key="itsakey"; - System.out.println(text); - String ciphertext=encrypt(text, key); - System.out.println(ciphertext); - System.out.println(decrypt(ciphertext, key)); - - } -} diff --git a/divideconquer/ClosestPair.java b/divideconquer/ClosestPair.java deleted file mode 100644 index 375d3e1a949c..000000000000 --- a/divideconquer/ClosestPair.java +++ /dev/null @@ -1,372 +0,0 @@ -package divideconquer; - -/** - * For a set of points in a coordinates system (10000 maximum), - * ClosestPair class calculates the two closest points. - * - * @author: anonymous - * @author: Marisa Afuera - */ - -public final class ClosestPair { - - - /** - * Number of points - */ - int numberPoints = 0; - /** - * Input data, maximum 10000. - */ - private Location[] array; - /** - * Minimum point coordinate. - */ - Location point1 = null; - /** - * Minimum point coordinate. - */ - Location point2 = null; - /** - * Minimum point length. - */ - private static double minNum = Double.MAX_VALUE; - /** - * secondCount - */ - private static int secondCount = 0; - - /** - * Constructor. - */ - ClosestPair(int points) { - numberPoints = points; - array = new Location[numberPoints]; - } - - /** - * Location class is an auxiliary type to keep points coordinates. - */ - - public static class Location { - - double x = 0; - double y = 0; - - /** - * @param xpar (IN Parameter) x coordinate <br/> - * @param ypar (IN Parameter) y coordinate <br/> - */ - - Location(final double xpar, final double ypar) { //Save x, y coordinates - this.x = xpar; - this.y = ypar; - } - - } - - public Location[] createLocation(int numberValues) { - return new Location[numberValues]; - - } - - public Location buildLocation(double x, double y) { - return new Location(x, y); - } - - - /** - * xPartition function: arrange x-axis. - * - * @param a (IN Parameter) array of points <br/> - * @param first (IN Parameter) first point <br/> - * @param last (IN Parameter) last point <br/> - * @return pivot index - */ - - public int xPartition( - final Location[] a, final int first, final int last) { - - Location pivot = a[last]; // pivot - int pIndex = last; - int i = first - 1; - Location temp; // Temporarily store value for position transformation - for (int j = first; j <= last - 1; j++) { - if (a[j].x <= pivot.x) { // Less than or less than pivot - i++; - temp = a[i]; // array[i] <-> array[j] - a[i] = a[j]; - a[j] = temp; - } - } - i++; - temp = a[i]; // array[pivot] <-> array[i] - a[i] = a[pIndex]; - a[pIndex] = temp; - return i; // pivot index - } - - /** - * yPartition function: arrange y-axis. - * - * @param a (IN Parameter) array of points <br/> - * @param first (IN Parameter) first point <br/> - * @param last (IN Parameter) last point <br/> - * @return pivot index - */ - - public int yPartition( - final Location[] a, final int first, final int last) { - - Location pivot = a[last]; // pivot - int pIndex = last; - int i = first - 1; - Location temp; // Temporarily store value for position transformation - for (int j = first; j <= last - 1; j++) { - if (a[j].y <= pivot.y) { // Less than or less than pivot - i++; - temp = a[i]; // array[i] <-> array[j] - a[i] = a[j]; - a[j] = temp; - } - } - i++; - temp = a[i]; // array[pivot] <-> array[i] - a[i] = a[pIndex]; - a[pIndex] = temp; - return i; // pivot index - } - - /** - * xQuickSort function: //x-axis Quick Sorting. - * - * @param a (IN Parameter) array of points <br/> - * @param first (IN Parameter) first point <br/> - * @param last (IN Parameter) last point <br/> - */ - - public void xQuickSort( - final Location[] a, final int first, final int last) { - - if (first < last) { - int q = xPartition(a, first, last); // pivot - xQuickSort(a, first, q - 1); // Left - xQuickSort(a, q + 1, last); // Right - } - } - - /** - * yQuickSort function: //y-axis Quick Sorting. - * - * @param a (IN Parameter) array of points <br/> - * @param first (IN Parameter) first point <br/> - * @param last (IN Parameter) last point <br/> - */ - - public void yQuickSort( - final Location[] a, final int first, final int last) { - - if (first < last) { - int q = yPartition(a, first, last); // pivot - yQuickSort(a, first, q - 1); // Left - yQuickSort(a, q + 1, last); // Right - } - } - - /** - * closestPair function: find closest pair. - * - * @param a (IN Parameter) array stored before divide <br/> - * @param indexNum (IN Parameter) number coordinates divideArray <br/> - * @return minimum distance <br/> - */ - - public double closestPair(final Location[] a, final int indexNum) { - - Location[] divideArray = new Location[indexNum]; - System.arraycopy(a, 0, divideArray, 0, indexNum); // Copy previous array - int totalNum = indexNum; // number of coordinates in the divideArray - int divideX = indexNum / 2; // Intermediate value for divide - Location[] leftArray = new Location[divideX]; //divide - left array - //divide-right array - Location[] rightArray = new Location[totalNum - divideX]; - if (indexNum <= 3) { // If the number of coordinates is 3 or less - return bruteForce(divideArray); - } - //divide-left array - System.arraycopy(divideArray, 0, leftArray, 0, divideX); - //divide-right array - System.arraycopy( - divideArray, divideX, rightArray, 0, totalNum - divideX); - - double minLeftArea = 0; //Minimum length of left array - double minRightArea = 0; //Minimum length of right array - double minValue = 0; //Minimum lengt - - minLeftArea = closestPair(leftArray, divideX); // recursive closestPair - minRightArea = closestPair(rightArray, totalNum - divideX); - // window size (= minimum length) - minValue = Math.min(minLeftArea, minRightArea); - - // Create window. Set the size for creating a window - // and creating a new array for the coordinates in the window - for (int i = 0; i < totalNum; i++) { - double xGap = Math.abs(divideArray[divideX].x - divideArray[i].x); - if (xGap < minValue) { - secondCount++; // size of the array - } else { - if (divideArray[i].x > divideArray[divideX].x) { - break; - } - } - } - // new array for coordinates in window - Location[] firstWindow = new Location[secondCount]; - int k = 0; - for (int i = 0; i < totalNum; i++) { - double xGap = Math.abs(divideArray[divideX].x - divideArray[i].x); - if (xGap < minValue) { // if it's inside a window - firstWindow[k] = divideArray[i]; // put in an array - k++; - } else { - if (divideArray[i].x > divideArray[divideX].x) { - break; - } - } - } - yQuickSort(firstWindow, 0, secondCount - 1); // Sort by y coordinates - /* Coordinates in Window */ - double length = 0; - // size comparison within window - for (int i = 0; i < secondCount - 1; i++) { - for (int j = (i + 1); j < secondCount; j++) { - double xGap = Math.abs(firstWindow[i].x - firstWindow[j].x); - double yGap = Math.abs(firstWindow[i].y - firstWindow[j].y); - if (yGap < minValue) { - length = Math.sqrt(Math.pow(xGap, 2) + Math.pow(yGap, 2)); - // If measured distance is less than current min distance - if (length < minValue) { - // Change minimum distance to current distance - minValue = length; - // Conditional for registering final coordinate - if (length < minNum) { - minNum = length; - point1 = firstWindow[i]; - point2 = firstWindow[j]; - } - } - } else { - break; - } - } - } - secondCount = 0; - return minValue; - } - - /** - * bruteForce function: When the number of coordinates is less than 3. - * - * @param arrayParam (IN Parameter) array stored before divide <br/> - * @return <br/> - */ - - public double bruteForce(final Location[] arrayParam) { - - double minValue = Double.MAX_VALUE; // minimum distance - double length = 0; - double xGap = 0; // Difference between x coordinates - double yGap = 0; // Difference between y coordinates - double result = 0; - - if (arrayParam.length == 2) { - // Difference between x coordinates - xGap = (arrayParam[0].x - arrayParam[1].x); - // Difference between y coordinates - yGap = (arrayParam[0].y - arrayParam[1].y); - // distance between coordinates - length = Math.sqrt(Math.pow(xGap, 2) + Math.pow(yGap, 2)); - // Conditional statement for registering final coordinate - if (length < minNum) { - minNum = length; - - } - point1 = arrayParam[0]; - point2 = arrayParam[1]; - result = length; - } - if (arrayParam.length == 3) { - for (int i = 0; i < arrayParam.length - 1; i++) { - for (int j = (i + 1); j < arrayParam.length; j++) { - // Difference between x coordinates - xGap = (arrayParam[i].x - arrayParam[j].x); - // Difference between y coordinates - yGap = (arrayParam[i].y - arrayParam[j].y); - // distance between coordinates - length = - Math.sqrt(Math.pow(xGap, 2) + Math.pow(yGap, 2)); - // If measured distance is less than current min distance - if (length < minValue) { - // Change minimum distance to current distance - minValue = length; - if (length < minNum) { - // Registering final coordinate - minNum = length; - point1 = arrayParam[i]; - point2 = arrayParam[j]; - } - } - } - } - result = minValue; - - } - return result; // If only one point returns 0. - } - - /** - * main function: execute class. - * - * @param args (IN Parameter) <br/> - * @throws IOException If an input or output - * exception occurred - */ - - public static void main(final String[] args) { - - //Input data consists of one x-coordinate and one y-coordinate - - ClosestPair cp = new ClosestPair(12); - cp.array[0] = cp.buildLocation(2, 3); - cp.array[1] = cp.buildLocation(2, 16); - cp.array[2] = cp.buildLocation(3, 9); - cp.array[3] = cp.buildLocation(6, 3); - cp.array[4] = cp.buildLocation(7, 7); - cp.array[5] = cp.buildLocation(19, 4); - cp.array[6] = cp.buildLocation(10, 11); - cp.array[7] = cp.buildLocation(15, 2); - cp.array[8] = cp.buildLocation(15, 19); - cp.array[9] = cp.buildLocation(16, 11); - cp.array[10] = cp.buildLocation(17, 13); - cp.array[11] = cp.buildLocation(9, 12); - - System.out.println("Input data"); - System.out.println("Number of points: " + cp.array.length); - for (int i = 0; i < cp.array.length; i++) { - System.out.println("x: " + cp.array[i].x + ", y: " + cp.array[i].y); - } - - cp.xQuickSort(cp.array, 0, cp.array.length - 1); // Sorting by x value - - double result; // minimum distance - - result = cp.closestPair(cp.array, cp.array.length); - // ClosestPair start - // minimum distance coordinates and distance output - System.out.println("Output Data"); - System.out.println("(" + cp.point1.x + ", " + cp.point1.y + ")"); - System.out.println("(" + cp.point2.x + ", " + cp.point2.y + ")"); - System.out.println("Minimum Distance : " + result); - - } -} diff --git a/divideconquer/SkylineAlgorithm.java b/divideconquer/SkylineAlgorithm.java deleted file mode 100644 index 081cc7800bc3..000000000000 --- a/divideconquer/SkylineAlgorithm.java +++ /dev/null @@ -1,186 +0,0 @@ -package divideconquer; - -import java.util.ArrayList; -import java.util.Comparator; - -/** - * @author dimgrichr - * <p> - * Space complexity: O(n) - * Time complexity: O(nlogn), because it is a divide and conquer algorithm - */ -public class SkylineAlgorithm { - private ArrayList<Point> points; - - /** - * Main constructor of the application. - * ArrayList points gets created, which represents the sum of all edges. - */ - public SkylineAlgorithm() { - points = new ArrayList<>(); - } - - - /** - * @return points, the ArrayList that includes all points. - */ - public ArrayList<Point> getPoints() { - return points; - } - - - /** - * The main divide and conquer, and also recursive algorithm. - * It gets an ArrayList full of points as an argument. - * If the size of that ArrayList is 1 or 2, - * the ArrayList is returned as it is, or with one less point - * (if the initial size is 2 and one of it's points, is dominated by the other one). - * On the other hand, if the ArrayList's size is bigger than 2, - * the function is called again, twice, - * with arguments the corresponding half of the initial ArrayList each time. - * Once the flashback has ended, the function produceFinalSkyLine gets called, - * in order to produce the final skyline, and return it. - * - * @param list, the initial list of points - * @return leftSkyLine, the combination of first half's and second half's skyline - * @see Point - */ - public ArrayList<Point> produceSubSkyLines(ArrayList<Point> list) { - - // part where function exits flashback - int size = list.size(); - if (size == 1) { - return list; - } else if (size == 2) { - if (list.get(0).dominates(list.get(1))) { - list.remove(1); - } else { - if (list.get(1).dominates(list.get(0))) { - list.remove(0); - } - } - return list; - } - - // recursive part of the function - ArrayList<Point> leftHalf = new ArrayList<>(); - ArrayList<Point> rightHalf = new ArrayList<>(); - for (int i = 0; i < list.size(); i++) { - if (i < list.size() / 2) { - leftHalf.add(list.get(i)); - } else { - rightHalf.add(list.get(i)); - } - } - ArrayList<Point> leftSubSkyLine = produceSubSkyLines(leftHalf); - ArrayList<Point> rightSubSkyLine = produceSubSkyLines(rightHalf); - - // skyline is produced - return produceFinalSkyLine(leftSubSkyLine, rightSubSkyLine); - } - - - /** - * The first half's skyline gets cleared - * from some points that are not part of the final skyline - * (Points with same x-value and different y=values. The point with the smallest y-value is kept). - * Then, the minimum y-value of the points of first half's skyline is found. - * That helps us to clear the second half's skyline, because, the points - * of second half's skyline that have greater y-value of the minimum y-value that we found before, - * are dominated, so they are not part of the final skyline. - * Finally, the "cleaned" first half's and second half's skylines, are combined, - * producing the final skyline, which is returned. - * - * @param left the skyline of the left part of points - * @param right the skyline of the right part of points - * @return left the final skyline - */ - public ArrayList<Point> produceFinalSkyLine(ArrayList<Point> left, ArrayList<Point> right) { - - // dominated points of ArrayList left are removed - for (int i = 0; i < left.size() - 1; i++) { - if (left.get(i).x == left.get(i + 1).x && left.get(i).y > left.get(i + 1).y) { - left.remove(i); - i--; - } - } - - // minimum y-value is found - int min = left.get(0).y; - for (int i = 1; i < left.size(); i++) { - if (min > left.get(i).y) { - min = left.get(i).y; - if (min == 1) { - i = left.size(); - } - } - } - - // dominated points of ArrayList right are removed - for (int i = 0; i < right.size(); i++) { - if (right.get(i).y >= min) { - right.remove(i); - i--; - } - } - - // final skyline found and returned - left.addAll(right); - return left; - } - - - public static class Point { - private int x; - private int y; - - /** - * The main constructor of Point Class, used to represent the 2 Dimension points. - * - * @param x the point's x-value. - * @param y the point's y-value. - */ - public Point(int x, int y) { - this.x = x; - this.y = y; - } - - /** - * @return x, the x-value - */ - public int getX() { - return x; - } - - /** - * @return y, the y-value - */ - public int getY() { - return y; - } - - /** - * Based on the skyline theory, - * it checks if the point that calls the function dominates the argument point. - * - * @param p1 the point that is compared - * @return true if the point wich calls the function dominates p1 - * false otherwise. - */ - public boolean dominates(Point p1) { - // checks if p1 is dominated - return (this.x < p1.x && this.y <= p1.y) || (this.x <= p1.x && this.y < p1.y); - } - } - - /** - * It is used to compare the 2 Dimension points, - * based on their x-values, in order get sorted later. - */ - class XComparator implements Comparator<Point> { - @Override - public int compare(Point a, Point b) { - return Integer.compare(a.x, b.x); - } - } -} diff --git a/pmd-exclude.properties b/pmd-exclude.properties new file mode 100644 index 000000000000..1848412c9d30 --- /dev/null +++ b/pmd-exclude.properties @@ -0,0 +1,64 @@ +com.thealgorithms.ciphers.AffineCipher=UselessParentheses +com.thealgorithms.ciphers.DES=UselessParentheses +com.thealgorithms.ciphers.RSA=UselessParentheses +com.thealgorithms.conversions.AnyBaseToAnyBase=UselessParentheses +com.thealgorithms.conversions.AnytoAny=UselessParentheses +com.thealgorithms.datastructures.crdt.Pair=UnusedPrivateField +com.thealgorithms.datastructures.graphs.AStar=UselessParentheses +com.thealgorithms.datastructures.graphs.AdjacencyMatrixGraph=CollapsibleIfStatements,UnnecessaryFullyQualifiedName,UselessParentheses +com.thealgorithms.datastructures.graphs.BipartiteGraphDFS=CollapsibleIfStatements +com.thealgorithms.datastructures.hashmap.hashing.HashMapCuckooHashing=UselessParentheses +com.thealgorithms.datastructures.heaps.FibonacciHeap=UselessParentheses +com.thealgorithms.datastructures.heaps.HeapNode=UselessParentheses +com.thealgorithms.datastructures.lists.DoublyLinkedList=UselessParentheses +com.thealgorithms.datastructures.lists.SearchSinglyLinkedListRecursion=UselessParentheses +com.thealgorithms.datastructures.lists.SinglyLinkedList=UnusedLocalVariable +com.thealgorithms.datastructures.queues.PriorityQueue=UselessParentheses +com.thealgorithms.datastructures.trees.CheckBinaryTreeIsValidBST=UselessParentheses +com.thealgorithms.datastructures.trees.SegmentTree=UselessParentheses +com.thealgorithms.devutils.nodes.LargeTreeNode=UselessParentheses +com.thealgorithms.devutils.nodes.SimpleNode=UselessParentheses +com.thealgorithms.devutils.nodes.SimpleTreeNode=UselessParentheses +com.thealgorithms.devutils.nodes.TreeNode=UselessParentheses +com.thealgorithms.divideandconquer.ClosestPair=UnnecessaryFullyQualifiedName,UselessParentheses +com.thealgorithms.divideandconquer.Point=UselessParentheses +com.thealgorithms.dynamicprogramming.WineProblem=UselessParentheses +com.thealgorithms.maths.BinomialCoefficient=UselessParentheses +com.thealgorithms.maths.Complex=UselessParentheses +com.thealgorithms.maths.DistanceFormulaTest=UnnecessaryFullyQualifiedName +com.thealgorithms.maths.Gaussian=UselessParentheses +com.thealgorithms.maths.GcdSolutionWrapper=UselessParentheses +com.thealgorithms.maths.HeronsFormula=UselessParentheses +com.thealgorithms.maths.KaprekarNumbers=UselessParentheses +com.thealgorithms.maths.KeithNumber=UselessParentheses +com.thealgorithms.maths.LeonardoNumber=UselessParentheses +com.thealgorithms.maths.LinearDiophantineEquationsSolver=UselessParentheses +com.thealgorithms.maths.RomanNumeralUtil=UselessParentheses +com.thealgorithms.maths.SecondMinMax=UselessParentheses +com.thealgorithms.maths.SecondMinMaxTest=UnnecessaryFullyQualifiedName +com.thealgorithms.maths.StandardDeviation=UselessParentheses +com.thealgorithms.maths.SumOfArithmeticSeries=UselessParentheses +com.thealgorithms.maths.TrinomialTriangle=UselessParentheses +com.thealgorithms.maths.Volume=UselessParentheses +com.thealgorithms.misc.Sparsity=UselessParentheses +com.thealgorithms.others.CRC16=UselessParentheses +com.thealgorithms.others.Damm=UnnecessaryFullyQualifiedName +com.thealgorithms.others.Luhn=UnnecessaryFullyQualifiedName +com.thealgorithms.others.Mandelbrot=UselessParentheses +com.thealgorithms.others.MiniMaxAlgorithm=UselessParentheses +com.thealgorithms.others.PageRank=UselessParentheses +com.thealgorithms.others.PerlinNoise=UselessParentheses +com.thealgorithms.others.QueueUsingTwoStacks=UselessParentheses +com.thealgorithms.others.Trieac=UselessParentheses +com.thealgorithms.others.Verhoeff=UnnecessaryFullyQualifiedName +com.thealgorithms.searches.InterpolationSearch=UselessParentheses +com.thealgorithms.searches.KMPSearch=UselessParentheses +com.thealgorithms.searches.RabinKarpAlgorithm=UselessParentheses +com.thealgorithms.sorts.CircleSort=EmptyControlStatement +com.thealgorithms.sorts.DutchNationalFlagSort=UselessParentheses +com.thealgorithms.sorts.MergeSortNoExtraSpace=UselessParentheses +com.thealgorithms.sorts.RadixSort=UselessParentheses +com.thealgorithms.sorts.WiggleSort=UselessParentheses +com.thealgorithms.stacks.PostfixToInfix=UselessParentheses +com.thealgorithms.strings.HorspoolSearch=UnnecessaryFullyQualifiedName,UselessParentheses +com.thealgorithms.strings.Palindrome=UselessParentheses diff --git a/pom.xml b/pom.xml new file mode 100644 index 000000000000..04128a7a3430 --- /dev/null +++ b/pom.xml @@ -0,0 +1,156 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0%20http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>com.thealgorithms</groupId> + <artifactId>Java</artifactId> + <version>1.0-SNAPSHOT</version> + <packaging>jar</packaging> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <maven.compiler.source>21</maven.compiler.source> + <maven.compiler.target>21</maven.compiler.target> + <assertj.version>3.27.3</assertj.version> + </properties> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.junit</groupId> + <artifactId>junit-bom</artifactId> + <version>5.12.2</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <dependencies> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <version>${assertj.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>5.17.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <version>3.17.0</version> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-collections4</artifactId> + <version>4.5.0</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.5.3</version> + <configuration> + <forkNode implementation="org.apache.maven.plugin.surefire.extensions.SurefireForkNodeFactory"/> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.14.0</version> + <configuration> + <source>21</source> + <target>21</target> + <compilerArgs> + <arg>-Xlint:all</arg> + <arg>-Xlint:-auxiliaryclass</arg> + <arg>-Xlint:-rawtypes</arg> + <arg>-Xlint:-unchecked</arg> + <arg>-Werror</arg> + </compilerArgs> + </configuration> + </plugin> + <plugin> + <groupId>org.jacoco</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + <version>0.8.13</version> + <executions> + <execution> + <goals> + <goal>prepare-agent</goal> + </goals> + </execution> + <execution> + <id>generate-code-coverage-report</id> + <phase>test</phase> + <goals> + <goal>report</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-checkstyle-plugin</artifactId> + <version>3.6.0</version> + <configuration> + <configLocation>checkstyle.xml</configLocation> + <consoleOutput>true</consoleOutput> + <includeTestSourceDirectory>true</includeTestSourceDirectory> + <violationSeverity>warning</violationSeverity> + </configuration> + <dependencies> + <dependency> + <groupId>com.puppycrawl.tools</groupId> + <artifactId>checkstyle</artifactId> + <version>10.23.1</version> + </dependency> + </dependencies> + </plugin> + <plugin> + <groupId>com.github.spotbugs</groupId> + <artifactId>spotbugs-maven-plugin</artifactId> + <version>4.9.3.0</version> + <configuration> + <excludeFilterFile>spotbugs-exclude.xml</excludeFilterFile> + <includeTests>true</includeTests> + <plugins> + <plugin> + <groupId>com.mebigfatguy.fb-contrib</groupId> + <artifactId>fb-contrib</artifactId> + <version>7.6.9</version> + </plugin> + <plugin> + <groupId>com.h3xstream.findsecbugs</groupId> + <artifactId>findsecbugs-plugin</artifactId> + <version>1.14.0</version> + </plugin> + </plugins> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-pmd-plugin</artifactId> + <version>3.26.0</version> + <configuration> + <printFailingErrors>true</printFailingErrors> + <includeTests>true</includeTests> + <linkXRef>false</linkXRef> + <excludeFromFailureFile>pmd-exclude.properties</excludeFromFailureFile> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml new file mode 100644 index 000000000000..3b77ced1a13e --- /dev/null +++ b/spotbugs-exclude.xml @@ -0,0 +1,211 @@ +<FindBugsFilter> + <Match> + <Bug pattern="DM_DEFAULT_ENCODING" /> + </Match> + <Match> + <Bug pattern="EI_EXPOSE_REP2" /> + </Match> + <Match> + <Bug pattern="DMI_RANDOM_USED_ONLY_ONCE" /> + </Match> + <Match> + <Bug pattern="SF_SWITCH_NO_DEFAULT" /> + </Match> + <Match> + <Bug pattern="RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT" /> + </Match> + <Match> + <Bug pattern="DM_NEXTINT_VIA_NEXTDOUBLE" /> + </Match> + <Match> + <Bug pattern="SIC_INNER_SHOULD_BE_STATIC" /> + </Match> + <Match> + <Bug pattern="EI_EXPOSE_REP" /> + </Match> + <Match> + <Bug pattern="EI_EXPOSE_REP" /> + </Match> + <Match> + <Bug pattern="SBSC_USE_STRINGBUFFER_CONCATENATION" /> + </Match> + <Match> + <Bug pattern="PA_PUBLIC_PRIMITIVE_ATTRIBUTE" /> + </Match> + <Match> + <Bug pattern="MS_PKGPROTECT" /> + </Match> + <Match> + <Bug pattern="SE_COMPARATOR_SHOULD_BE_SERIALIZABLE" /> + </Match> + <Match> + <Bug pattern="INT_BAD_REM_BY_1" /> + </Match> + <Match> + <Bug pattern="FE_FLOATING_POINT_EQUALITY" /> + </Match> + <Match> + <Bug pattern="CT_CONSTRUCTOR_THROW" /> + </Match> + <Match> + <Bug pattern="URF_UNREAD_FIELD" /> + </Match> + <Match> + <Bug pattern="RC_REF_COMPARISON" /> + </Match> + <Match> + <Bug pattern="MS_EXPOSE_REP" /> + </Match> + <Match> + <Bug pattern="DM_BOXED_PRIMITIVE_FOR_PARSING" /> + </Match> + <Match> + <Bug pattern="UWF_UNWRITTEN_FIELD" /> + </Match> + <Match> + <Bug pattern="UWF_NULL_FIELD" /> + </Match> + <Match> + <Bug pattern="NP_UNWRITTEN_FIELD" /> + </Match> + <Match> + <Bug pattern="URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD" /> + </Match> + <Match> + <Bug pattern="NP_IMMEDIATE_DEREFERENCE_OF_READLINE" /> + </Match> + <Match> + <Bug pattern="EQ_COMPARETO_USE_OBJECT_EQUALS" /> + </Match> + <Match> + <Bug pattern="SA_FIELD_SELF_ASSIGNMENT" /> + </Match> + <Match> + <Bug pattern="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE" /> + </Match> + <Match> + <Bug pattern="AT_STALE_THREAD_WRITE_OF_PRIMITIVE" /> + </Match> + <!-- fb-contrib --> + <Match> + <Bug pattern="LSC_LITERAL_STRING_COMPARISON" /> + </Match> + <Match> + <Bug pattern="CE_CLASS_ENVY" /> + </Match> + <Match> + <Bug pattern="PSC_PRESIZE_COLLECTIONS" /> + </Match> + <Match> + <Bug pattern="SACM_STATIC_ARRAY_CREATED_IN_METHOD" /> + </Match> + <Match> + <Bug pattern="LUI_USE_SINGLETON_LIST" /> + </Match> + <Match> + <Bug pattern="CLI_CONSTANT_LIST_INDEX" /> + </Match> + <Match> + <Bug pattern="CNC_COLLECTION_NAMING_CONFUSION" /> + </Match> + <Match> + <Bug pattern="TR_TAIL_RECURSION" /> + </Match> + <Match> + <Bug pattern="USBR_UNNECESSARY_STORE_BEFORE_RETURN" /> + </Match> + <Match> + <Bug pattern="BL_BURYING_LOGIC" /> + </Match> + <Match> + <Bug pattern="UTWR_USE_TRY_WITH_RESOURCES" /> + </Match> + <Match> + <Bug pattern="MUI_CONTAINSKEY_BEFORE_GET" /> + </Match> + <Match> + <Bug pattern="IMC_IMMATURE_CLASS_PRINTSTACKTRACE" /> + </Match> + <Match> + <Bug pattern="UCPM_USE_CHARACTER_PARAMETERIZED_METHOD" /> + </Match> + <Match> + <Bug pattern="SUA_SUSPICIOUS_UNINITIALIZED_ARRAY" /> + </Match> + <Match> + <Bug pattern="SPP_USE_MATH_CONSTANT" /> + </Match> + <Match> + <Bug pattern="UJM_UNJITABLE_METHOD" /> + </Match> + <Match> + <Bug pattern="SEC_SIDE_EFFECT_CONSTRUCTOR" /> + </Match> + <Match> + <Bug pattern="MDM_STRING_BYTES_ENCODING" /> + </Match> + <Match> + <Bug pattern="PMB_POSSIBLE_MEMORY_BLOAT" /> + </Match> + <Match> + <Bug pattern="LSYC_LOCAL_SYNCHRONIZED_COLLECTION" /> + </Match> + <Match> + <Bug pattern="IOI_USE_OF_FILE_STREAM_CONSTRUCTORS" /> + </Match> + <Match> + <Bug pattern="UP_UNUSED_PARAMETER" /> + </Match> + <Match> + <Bug pattern="DSOC_DUBIOUS_SET_OF_COLLECTIONS" /> + </Match> + <Match> + <Bug pattern="FPL_FLOATING_POINT_LOOPS" /> + </Match> + <Match> + <Bug pattern="ITU_INAPPROPRIATE_TOSTRING_USE" /> + </Match> + <Match> + <Bug pattern="SPP_PASSING_THIS_AS_PARM" /> + </Match> + <Match> + <Bug pattern="FCBL_FIELD_COULD_BE_LOCAL" /> + </Match> + <Match> + <Bug pattern="CFS_CONFUSING_FUNCTION_SEMANTICS" /> + </Match> + <Match> + <Bug pattern="PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS" /> + </Match> + <Match> + <Bug pattern="FCCD_FIND_CLASS_CIRCULAR_DEPENDENCY" /> + </Match> + <Match> + <Bug pattern="PL_PARALLEL_LISTS" /> + </Match> + <Match> + <Bug pattern="PCAIL_POSSIBLE_CONSTANT_ALLOCATION_IN_LOOP" /> + </Match> + <Match> + <Bug pattern="STT_STRING_PARSING_A_FIELD" /> + </Match> + <Match> + <Bug pattern="IMC_IMMATURE_CLASS_BAD_SERIALVERSIONUID" /> + </Match> + <Match> + <Bug pattern="DRE_DECLARED_RUNTIME_EXCEPTION" /> + </Match> + <Match> + <Bug pattern="SLS_SUSPICIOUS_LOOP_SEARCH" /> + </Match> + <Match> + <Bug pattern="BAS_BLOATED_ASSIGNMENT_SCOPE" /> + </Match> + <!-- find-sec-bugs --> + <Match> + <Bug pattern="PREDICTABLE_RANDOM" /> + </Match> + <Match> + <Bug pattern="HARD_CODE_KEY" /> + </Match> +</FindBugsFilter> diff --git a/src/main/java/com/thealgorithms/audiofilters/EMAFilter.java b/src/main/java/com/thealgorithms/audiofilters/EMAFilter.java new file mode 100644 index 000000000000..0dd23e937953 --- /dev/null +++ b/src/main/java/com/thealgorithms/audiofilters/EMAFilter.java @@ -0,0 +1,48 @@ +package com.thealgorithms.audiofilters; + +/** + * Exponential Moving Average (EMA) Filter for smoothing audio signals. + * + * <p>This filter applies an exponential moving average to a sequence of audio + * signal values, making it useful for smoothing out rapid fluctuations. + * The smoothing factor (alpha) controls the degree of smoothing. + * + * <p>Based on the definition from + * <a href="https://en.wikipedia.org/wiki/Moving_average">Wikipedia link</a>. + */ +public class EMAFilter { + private final double alpha; + private double emaValue; + /** + * Constructs an EMA filter with a given smoothing factor. + * + * @param alpha Smoothing factor (0 < alpha <= 1) + * @throws IllegalArgumentException if alpha is not in (0, 1] + */ + public EMAFilter(double alpha) { + if (alpha <= 0 || alpha > 1) { + throw new IllegalArgumentException("Alpha must be between 0 and 1."); + } + this.alpha = alpha; + this.emaValue = 0.0; + } + /** + * Applies the EMA filter to an audio signal array. + * + * @param audioSignal Array of audio samples to process + * @return Array of processed (smoothed) samples + */ + public double[] apply(double[] audioSignal) { + if (audioSignal.length == 0) { + return new double[0]; + } + double[] emaSignal = new double[audioSignal.length]; + emaValue = audioSignal[0]; + emaSignal[0] = emaValue; + for (int i = 1; i < audioSignal.length; i++) { + emaValue = alpha * audioSignal[i] + (1 - alpha) * emaValue; + emaSignal[i] = emaValue; + } + return emaSignal; + } +} diff --git a/src/main/java/com/thealgorithms/audiofilters/IIRFilter.java b/src/main/java/com/thealgorithms/audiofilters/IIRFilter.java new file mode 100644 index 000000000000..fbc095909541 --- /dev/null +++ b/src/main/java/com/thealgorithms/audiofilters/IIRFilter.java @@ -0,0 +1,93 @@ +package com.thealgorithms.audiofilters; + +/** + * N-Order IIR Filter Assumes inputs are normalized to [-1, 1] + * + * Based on the difference equation from + * <a href="https://en.wikipedia.org/wiki/Infinite_impulse_response">Wikipedia link</a> + */ +public class IIRFilter { + + private final int order; + private final double[] coeffsA; + private final double[] coeffsB; + private final double[] historyX; + private final double[] historyY; + + /** + * Construct an IIR Filter + * + * @param order the filter's order + * @throws IllegalArgumentException if order is zero or less + */ + public IIRFilter(int order) throws IllegalArgumentException { + if (order < 1) { + throw new IllegalArgumentException("order must be greater than zero"); + } + + this.order = order; + coeffsA = new double[order + 1]; + coeffsB = new double[order + 1]; + + // Sane defaults + coeffsA[0] = 1.0; + coeffsB[0] = 1.0; + + historyX = new double[order]; + historyY = new double[order]; + } + + /** + * Set coefficients + * + * @param aCoeffs Denominator coefficients + * @param bCoeffs Numerator coefficients + * @throws IllegalArgumentException if {@code aCoeffs} or {@code bCoeffs} is + * not of size {@code order}, or if {@code aCoeffs[0]} is 0.0 + */ + public void setCoeffs(double[] aCoeffs, double[] bCoeffs) throws IllegalArgumentException { + if (aCoeffs.length != order) { + throw new IllegalArgumentException("aCoeffs must be of size " + order + ", got " + aCoeffs.length); + } + + if (aCoeffs[0] == 0.0) { + throw new IllegalArgumentException("aCoeffs.get(0) must not be zero"); + } + + if (bCoeffs.length != order) { + throw new IllegalArgumentException("bCoeffs must be of size " + order + ", got " + bCoeffs.length); + } + + for (int i = 0; i < order; i++) { + coeffsA[i] = aCoeffs[i]; + coeffsB[i] = bCoeffs[i]; + } + } + + /** + * Process a single sample + * + * @param sample the sample to process + * @return the processed sample + */ + public double process(double sample) { + double result = 0.0; + + // Process + for (int i = 1; i <= order; i++) { + result += (coeffsB[i] * historyX[i - 1] - coeffsA[i] * historyY[i - 1]); + } + result = (result + coeffsB[0] * sample) / coeffsA[0]; + + // Feedback + for (int i = order - 1; i > 0; i--) { + historyX[i] = historyX[i - 1]; + historyY[i] = historyY[i - 1]; + } + + historyX[0] = sample; + historyY[0] = result; + + return result; + } +} diff --git a/src/main/java/com/thealgorithms/backtracking/AllPathsFromSourceToTarget.java b/src/main/java/com/thealgorithms/backtracking/AllPathsFromSourceToTarget.java new file mode 100644 index 000000000000..6f93b704ffb2 --- /dev/null +++ b/src/main/java/com/thealgorithms/backtracking/AllPathsFromSourceToTarget.java @@ -0,0 +1,100 @@ +package com.thealgorithms.backtracking; + +import java.util.ArrayList; +import java.util.List; + +/** + * Program description - To find all possible paths from source to destination + * <a href="https://en.wikipedia.org/wiki/Shortest_path_problem">Wikipedia</a> + * + * @author <a href="https://github.com/siddhant2002">Siddhant Swarup Mallick</a> + */ +public class AllPathsFromSourceToTarget { + + // No. of vertices in graph + private final int v; + + // To store the paths from source to destination + static List<List<Integer>> nm = new ArrayList<>(); + // adjacency list + private ArrayList<Integer>[] adjList; + + // Constructor + public AllPathsFromSourceToTarget(int vertices) { + + // initialise vertex count + this.v = vertices; + + // initialise adjacency list + initAdjList(); + } + + // utility method to initialise adjacency list + private void initAdjList() { + adjList = new ArrayList[v]; + + for (int i = 0; i < v; i++) { + adjList[i] = new ArrayList<>(); + } + } + + // add edge from u to v + public void addEdge(int u, int v) { + // Add v to u's list. + adjList[u].add(v); + } + + public void storeAllPaths(int s, int d) { + boolean[] isVisited = new boolean[v]; + ArrayList<Integer> pathList = new ArrayList<>(); + + // add source to path[] + pathList.add(s); + // Call recursive utility + storeAllPathsUtil(s, d, isVisited, pathList); + } + + // A recursive function to print all paths from 'u' to 'd'. + // isVisited[] keeps track of vertices in current path. + // localPathList<> stores actual vertices in the current path + private void storeAllPathsUtil(Integer u, Integer d, boolean[] isVisited, List<Integer> localPathList) { + + if (u.equals(d)) { + nm.add(new ArrayList<>(localPathList)); + return; + } + + // Mark the current node + isVisited[u] = true; + + // Recursion for all the vertices adjacent to current vertex + + for (Integer i : adjList[u]) { + if (!isVisited[i]) { + // store current node in path[] + localPathList.add(i); + storeAllPathsUtil(i, d, isVisited, localPathList); + + // remove current node in path[] + localPathList.remove(i); + } + } + + // Mark the current node + isVisited[u] = false; + } + + // Driver program + public static List<List<Integer>> allPathsFromSourceToTarget(int vertices, int[][] a, int source, int destination) { + // Create a sample graph + AllPathsFromSourceToTarget g = new AllPathsFromSourceToTarget(vertices); + for (int[] i : a) { + g.addEdge(i[0], i[1]); + // edges are added + } + g.storeAllPaths(source, destination); + // method call to store all possible paths + return nm; + // returns all possible paths from source to destination + } +} diff --git a/src/main/java/com/thealgorithms/backtracking/ArrayCombination.java b/src/main/java/com/thealgorithms/backtracking/ArrayCombination.java new file mode 100644 index 000000000000..f8cd0c40c20e --- /dev/null +++ b/src/main/java/com/thealgorithms/backtracking/ArrayCombination.java @@ -0,0 +1,54 @@ +package com.thealgorithms.backtracking; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class provides methods to find all combinations of integers from 0 to n-1 + * of a specified length k using backtracking. + */ +public final class ArrayCombination { + private ArrayCombination() { + } + + /** + * Generates all possible combinations of length k from the integers 0 to n-1. + * + * @param n The total number of elements (0 to n-1). + * @param k The desired length of each combination. + * @return A list containing all combinations of length k. + * @throws IllegalArgumentException if n or k are negative, or if k is greater than n. + */ + public static List<List<Integer>> combination(int n, int k) { + if (n < 0 || k < 0 || k > n) { + throw new IllegalArgumentException("Invalid input: n must be non-negative, k must be non-negative and less than or equal to n."); + } + + List<List<Integer>> combinations = new ArrayList<>(); + combine(combinations, new ArrayList<>(), 0, n, k); + return combinations; + } + + /** + * A helper method that uses backtracking to find combinations. + * + * @param combinations The list to store all valid combinations found. + * @param current The current combination being built. + * @param start The starting index for the current recursion. + * @param n The total number of elements (0 to n-1). + * @param k The desired length of each combination. + */ + private static void combine(List<List<Integer>> combinations, List<Integer> current, int start, int n, int k) { + // Base case: combination found + if (current.size() == k) { + combinations.add(new ArrayList<>(current)); + return; + } + + for (int i = start; i < n; i++) { + current.add(i); + combine(combinations, current, i + 1, n, k); + current.remove(current.size() - 1); // Backtrack + } + } +} diff --git a/src/main/java/com/thealgorithms/backtracking/Combination.java b/src/main/java/com/thealgorithms/backtracking/Combination.java new file mode 100644 index 000000000000..ecaf7428f986 --- /dev/null +++ b/src/main/java/com/thealgorithms/backtracking/Combination.java @@ -0,0 +1,67 @@ +package com.thealgorithms.backtracking; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.TreeSet; + +/** + * Finds all permutations of given array + * @author Alan Piao (<a href="https://github.com/cpiao3">git-Alan Piao</a>) + */ +public final class Combination { + private Combination() { + } + + /** + * Find all combinations of given array using backtracking + * @param arr the array. + * @param n length of combination + * @param <T> the type of elements in the array. + * @return a list of all combinations of length n. If n == 0, return null. + */ + public static <T> List<TreeSet<T>> combination(T[] arr, int n) { + if (n < 0) { + throw new IllegalArgumentException("The combination length cannot be negative."); + } + + if (n == 0) { + return Collections.emptyList(); + } + T[] array = arr.clone(); + Arrays.sort(array); + + List<TreeSet<T>> result = new LinkedList<>(); + backtracking(array, n, 0, new TreeSet<T>(), result); + return result; + } + + /** + * Backtrack all possible combinations of a given array + * @param arr the array. + * @param n length of the combination + * @param index the starting index. + * @param currSet set that tracks current combination + * @param result the list contains all combination. + * @param <T> the type of elements in the array. + */ + private static <T> void backtracking(T[] arr, int n, int index, TreeSet<T> currSet, List<TreeSet<T>> result) { + if (index + n - currSet.size() > arr.length) { + return; + } + if (currSet.size() == n - 1) { + for (int i = index; i < arr.length; i++) { + currSet.add(arr[i]); + result.add(new TreeSet<>(currSet)); + currSet.remove(arr[i]); + } + return; + } + for (int i = index; i < arr.length; i++) { + currSet.add(arr[i]); + backtracking(arr, n, i + 1, currSet, result); + currSet.remove(arr[i]); + } + } +} diff --git a/src/main/java/com/thealgorithms/backtracking/CrosswordSolver.java b/src/main/java/com/thealgorithms/backtracking/CrosswordSolver.java new file mode 100644 index 000000000000..6bfb026c7de9 --- /dev/null +++ b/src/main/java/com/thealgorithms/backtracking/CrosswordSolver.java @@ -0,0 +1,125 @@ +package com.thealgorithms.backtracking; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * A class to solve a crossword puzzle using backtracking. + * Example: + * Input: + * puzzle = { + * {' ', ' ', ' '}, + * {' ', ' ', ' '}, + * {' ', ' ', ' '} + * } + * words = List.of("cat", "dog") + * + * Output: + * { + * {'c', 'a', 't'}, + * {' ', ' ', ' '}, + * {'d', 'o', 'g'} + * } + */ +public final class CrosswordSolver { + private CrosswordSolver() { + } + + /** + * Checks if a word can be placed at the specified position in the crossword. + * + * @param puzzle The crossword puzzle represented as a 2D char array. + * @param word The word to be placed. + * @param row The row index where the word might be placed. + * @param col The column index where the word might be placed. + * @param vertical If true, the word is placed vertically; otherwise, horizontally. + * @return true if the word can be placed, false otherwise. + */ + public static boolean isValid(char[][] puzzle, String word, int row, int col, boolean vertical) { + for (int i = 0; i < word.length(); i++) { + if (vertical) { + if (row + i >= puzzle.length || puzzle[row + i][col] != ' ') { + return false; + } + } else { + if (col + i >= puzzle[0].length || puzzle[row][col + i] != ' ') { + return false; + } + } + } + return true; + } + + /** + * Places a word at the specified position in the crossword. + * + * @param puzzle The crossword puzzle represented as a 2D char array. + * @param word The word to be placed. + * @param row The row index where the word will be placed. + * @param col The column index where the word will be placed. + * @param vertical If true, the word is placed vertically; otherwise, horizontally. + */ + public static void placeWord(char[][] puzzle, String word, int row, int col, boolean vertical) { + for (int i = 0; i < word.length(); i++) { + if (vertical) { + puzzle[row + i][col] = word.charAt(i); + } else { + puzzle[row][col + i] = word.charAt(i); + } + } + } + + /** + * Removes a word from the specified position in the crossword. + * + * @param puzzle The crossword puzzle represented as a 2D char array. + * @param word The word to be removed. + * @param row The row index where the word is placed. + * @param col The column index where the word is placed. + * @param vertical If true, the word was placed vertically; otherwise, horizontally. + */ + public static void removeWord(char[][] puzzle, String word, int row, int col, boolean vertical) { + for (int i = 0; i < word.length(); i++) { + if (vertical) { + puzzle[row + i][col] = ' '; + } else { + puzzle[row][col + i] = ' '; + } + } + } + + /** + * Solves the crossword puzzle using backtracking. + * + * @param puzzle The crossword puzzle represented as a 2D char array. + * @param words The list of words to be placed. + * @return true if the crossword is solved, false otherwise. + */ + public static boolean solveCrossword(char[][] puzzle, Collection<String> words) { + // Create a mutable copy of the words list + List<String> remainingWords = new ArrayList<>(words); + + for (int row = 0; row < puzzle.length; row++) { + for (int col = 0; col < puzzle[0].length; col++) { + if (puzzle[row][col] == ' ') { + for (String word : new ArrayList<>(remainingWords)) { + for (boolean vertical : new boolean[] {true, false}) { + if (isValid(puzzle, word, row, col, vertical)) { + placeWord(puzzle, word, row, col, vertical); + remainingWords.remove(word); + if (solveCrossword(puzzle, remainingWords)) { + return true; + } + remainingWords.add(word); + removeWord(puzzle, word, row, col, vertical); + } + } + } + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/com/thealgorithms/backtracking/FloodFill.java b/src/main/java/com/thealgorithms/backtracking/FloodFill.java new file mode 100644 index 000000000000..c8219ca8ba7e --- /dev/null +++ b/src/main/java/com/thealgorithms/backtracking/FloodFill.java @@ -0,0 +1,62 @@ +package com.thealgorithms.backtracking; + +/** + * Java program for Flood fill algorithm. + * @author Akshay Dubey (<a href="https://github.com/itsAkshayDubey">Git-Akshay Dubey</a>) + */ +public final class FloodFill { + private FloodFill() { + } + + /** + * Get the color at the given coordinates of a 2D image + * + * @param image The image to be filled + * @param x The x co-ordinate of which color is to be obtained + * @param y The y co-ordinate of which color is to be obtained + */ + + public static int getPixel(final int[][] image, final int x, final int y) { + return image[x][y]; + } + + /** + * Put the color at the given coordinates of a 2D image + * + * @param image The image to be filled + * @param x The x co-ordinate at which color is to be filled + * @param y The y co-ordinate at which color is to be filled + */ + public static void putPixel(final int[][] image, final int x, final int y, final int newColor) { + image[x][y] = newColor; + } + + /** + * Fill the 2D image with new color + * + * @param image The image to be filled + * @param x The x co-ordinate at which color is to be filled + * @param y The y co-ordinate at which color is to be filled + * @param newColor The new color which to be filled in the image + * @param oldColor The old color which is to be replaced in the image + */ + public static void floodFill(final int[][] image, final int x, final int y, final int newColor, final int oldColor) { + if (newColor == oldColor || x < 0 || x >= image.length || y < 0 || y >= image[x].length || getPixel(image, x, y) != oldColor) { + return; + } + + putPixel(image, x, y, newColor); + + /* Recursively check for horizontally & vertically adjacent coordinates */ + floodFill(image, x + 1, y, newColor, oldColor); + floodFill(image, x - 1, y, newColor, oldColor); + floodFill(image, x, y + 1, newColor, oldColor); + floodFill(image, x, y - 1, newColor, oldColor); + + /* Recursively check for diagonally adjacent coordinates */ + floodFill(image, x + 1, y - 1, newColor, oldColor); + floodFill(image, x - 1, y + 1, newColor, oldColor); + floodFill(image, x + 1, y + 1, newColor, oldColor); + floodFill(image, x - 1, y - 1, newColor, oldColor); + } +} diff --git a/src/main/java/com/thealgorithms/backtracking/KnightsTour.java b/src/main/java/com/thealgorithms/backtracking/KnightsTour.java new file mode 100644 index 000000000000..2c2da659f3aa --- /dev/null +++ b/src/main/java/com/thealgorithms/backtracking/KnightsTour.java @@ -0,0 +1,156 @@ +package com.thealgorithms.backtracking; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +/** + * The KnightsTour class solves the Knight's Tour problem using backtracking. + * + * Problem Statement: + * Given an N*N board with a knight placed on the first block, the knight must + * move according to chess rules and visit each square on the board exactly once. + * The class outputs the sequence of moves for the knight. + * + * Example: + * Input: N = 8 (8x8 chess board) + * Output: The sequence of numbers representing the order in which the knight visits each square. + */ +public final class KnightsTour { + private KnightsTour() { + } + + // The size of the chess board (12x12 grid, with 2 extra rows/columns as a buffer around a 8x8 area) + private static final int BASE = 12; + + // Possible moves for a knight in chess + private static final int[][] MOVES = { + {1, -2}, + {2, -1}, + {2, 1}, + {1, 2}, + {-1, 2}, + {-2, 1}, + {-2, -1}, + {-1, -2}, + }; + + // Chess grid representing the board + static int[][] grid; + + // Total number of cells the knight needs to visit + static int total; + + /** + * Resets the chess board to its initial state. + * Initializes the grid with boundary cells marked as -1 and internal cells as 0. + * Sets the total number of cells the knight needs to visit. + */ + public static void resetBoard() { + grid = new int[BASE][BASE]; + total = (BASE - 4) * (BASE - 4); + for (int r = 0; r < BASE; r++) { + for (int c = 0; c < BASE; c++) { + if (r < 2 || r > BASE - 3 || c < 2 || c > BASE - 3) { + grid[r][c] = -1; // Mark boundary cells + } + } + } + } + + /** + * Recursive method to solve the Knight's Tour problem. + * + * @param row The current row of the knight + * @param column The current column of the knight + * @param count The current move number + * @return True if a solution is found, False otherwise + */ + static boolean solve(int row, int column, int count) { + if (count > total) { + return true; + } + + List<int[]> neighbor = neighbors(row, column); + + if (neighbor.isEmpty() && count != total) { + return false; + } + + // Sort neighbors by Warnsdorff's rule (fewest onward moves) + neighbor.sort(Comparator.comparingInt(a -> a[2])); + + for (int[] nb : neighbor) { + int nextRow = nb[0]; + int nextCol = nb[1]; + grid[nextRow][nextCol] = count; + if (!orphanDetected(count, nextRow, nextCol) && solve(nextRow, nextCol, count + 1)) { + return true; + } + grid[nextRow][nextCol] = 0; // Backtrack + } + + return false; + } + + /** + * Returns a list of valid neighboring cells where the knight can move. + * + * @param row The current row of the knight + * @param column The current column of the knight + * @return A list of arrays representing valid moves, where each array contains: + * {nextRow, nextCol, numberOfPossibleNextMoves} + */ + static List<int[]> neighbors(int row, int column) { + List<int[]> neighbour = new ArrayList<>(); + + for (int[] m : MOVES) { + int x = m[0]; + int y = m[1]; + if (row + y >= 0 && row + y < BASE && column + x >= 0 && column + x < BASE && grid[row + y][column + x] == 0) { + int num = countNeighbors(row + y, column + x); + neighbour.add(new int[] {row + y, column + x, num}); + } + } + return neighbour; + } + + /** + * Counts the number of possible valid moves for a knight from a given position. + * + * @param row The row of the current position + * @param column The column of the current position + * @return The number of valid neighboring moves + */ + static int countNeighbors(int row, int column) { + int num = 0; + for (int[] m : MOVES) { + int x = m[0]; + int y = m[1]; + if (row + y >= 0 && row + y < BASE && column + x >= 0 && column + x < BASE && grid[row + y][column + x] == 0) { + num++; + } + } + return num; + } + + /** + * Detects if moving to a given position will create an orphan (a position with no further valid moves). + * + * @param count The current move number + * @param row The row of the current position + * @param column The column of the current position + * @return True if an orphan is detected, False otherwise + */ + static boolean orphanDetected(int count, int row, int column) { + if (count < total - 1) { + List<int[]> neighbor = neighbors(row, column); + for (int[] nb : neighbor) { + if (countNeighbors(nb[0], nb[1]) == 0) { + return true; + } + } + } + return false; + } +} diff --git a/src/main/java/com/thealgorithms/backtracking/MColoring.java b/src/main/java/com/thealgorithms/backtracking/MColoring.java new file mode 100644 index 000000000000..d0188dfd13aa --- /dev/null +++ b/src/main/java/com/thealgorithms/backtracking/MColoring.java @@ -0,0 +1,96 @@ +package com.thealgorithms.backtracking; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Queue; +import java.util.Set; + +/** + * Node class represents a graph node. Each node is associated with a color + * (initially 1) and contains a set of edges representing its adjacent nodes. + * + * @author Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi) + */ +class Node { + int color = 1; // Initial color for each node + Set<Integer> edges = new HashSet<Integer>(); // Set of edges representing adjacent nodes +} + +/** + * MColoring class solves the M-Coloring problem where the goal is to determine + * if it's possible to color a graph using at most M colors such that no two + * adjacent nodes have the same color. + */ +public final class MColoring { + + private MColoring() { + } // Prevent instantiation of utility class + + /** + * Determines whether it is possible to color the graph using at most M colors. + * + * @param nodes List of nodes representing the graph. + * @param n The total number of nodes in the graph. + * @param m The maximum number of allowed colors. + * @return true if the graph can be colored using M colors, false otherwise. + */ + static boolean isColoringPossible(ArrayList<Node> nodes, int n, int m) { + + // Visited array keeps track of whether each node has been processed. + ArrayList<Integer> visited = new ArrayList<Integer>(); + for (int i = 0; i < n + 1; i++) { + visited.add(0); // Initialize all nodes as unvisited (0) + } + + // The number of colors used so far (initially set to 1, since all nodes + // start with color 1). + int maxColors = 1; + + // Loop through all the nodes to ensure every node is visited, in case the + // graph is disconnected. + for (int sv = 1; sv <= n; sv++) { + if (visited.get(sv) > 0) { + continue; // Skip nodes that are already visited + } + + // If the node is unvisited, mark it as visited and add it to the queue for BFS. + visited.set(sv, 1); + Queue<Integer> q = new LinkedList<>(); + q.add(sv); + + // Perform BFS to process all nodes and their adjacent nodes + while (q.size() != 0) { + int top = q.peek(); // Get the current node from the queue + q.remove(); + + // Check all adjacent nodes of the current node + for (int it : nodes.get(top).edges) { + + // If the adjacent node has the same color as the current node, increment its + // color to avoid conflict. + if (nodes.get(top).color == nodes.get(it).color) { + nodes.get(it).color += 1; + } + + // Keep track of the maximum number of colors used so far + maxColors = Math.max(maxColors, Math.max(nodes.get(top).color, nodes.get(it).color)); + + // If the number of colors used exceeds the allowed limit M, return false. + if (maxColors > m) { + return false; + } + + // If the adjacent node hasn't been visited yet, mark it as visited and add it + // to the queue for further processing. + if (visited.get(it) == 0) { + visited.set(it, 1); + q.add(it); + } + } + } + } + + return true; // Possible to color the entire graph with M or fewer colors. + } +} diff --git a/src/main/java/com/thealgorithms/backtracking/MazeRecursion.java b/src/main/java/com/thealgorithms/backtracking/MazeRecursion.java new file mode 100644 index 000000000000..8247172e7ee0 --- /dev/null +++ b/src/main/java/com/thealgorithms/backtracking/MazeRecursion.java @@ -0,0 +1,125 @@ +package com.thealgorithms.backtracking; + +/** + * This class contains methods to solve a maze using recursive backtracking. + * The maze is represented as a 2D array where walls, paths, and visited/dead + * ends are marked with different integers. + * + * The goal is to find a path from a starting position to the target position + * (map[6][5]) while navigating through the maze. + */ +public final class MazeRecursion { + + private MazeRecursion() { + } + + /** + * This method solves the maze using the "down -> right -> up -> left" + * movement strategy. + * + * @param map The 2D array representing the maze (walls, paths, etc.) + * @return The solved maze with paths marked, or null if no solution exists. + */ + public static int[][] solveMazeUsingFirstStrategy(int[][] map) { + if (setWay(map, 1, 1)) { + return map; + } + return null; + } + + /** + * This method solves the maze using the "up -> right -> down -> left" + * movement strategy. + * + * @param map The 2D array representing the maze (walls, paths, etc.) + * @return The solved maze with paths marked, or null if no solution exists. + */ + public static int[][] solveMazeUsingSecondStrategy(int[][] map) { + if (setWay2(map, 1, 1)) { + return map; + } + return null; + } + + /** + * Attempts to find a path through the maze using a "down -> right -> up -> left" + * movement strategy. The path is marked with '2' for valid paths and '3' for dead ends. + * + * @param map The 2D array representing the maze (walls, paths, etc.) + * @param i The current x-coordinate of the ball (row index) + * @param j The current y-coordinate of the ball (column index) + * @return True if a path is found to (6,5), otherwise false + */ + private static boolean setWay(int[][] map, int i, int j) { + if (map[6][5] == 2) { + return true; + } + + // If the current position is unvisited (0), explore it + if (map[i][j] == 0) { + // Mark the current position as '2' + map[i][j] = 2; + + // Move down + if (setWay(map, i + 1, j)) { + return true; + } + // Move right + else if (setWay(map, i, j + 1)) { + return true; + } + // Move up + else if (setWay(map, i - 1, j)) { + return true; + } + // Move left + else if (setWay(map, i, j - 1)) { + return true; + } + + map[i][j] = 3; // Mark as dead end (3) if no direction worked + return false; + } + return false; + } + + /** + * Attempts to find a path through the maze using an alternative movement + * strategy "up -> right -> down -> left". + * + * @param map The 2D array representing the maze (walls, paths, etc.) + * @param i The current x-coordinate of the ball (row index) + * @param j The current y-coordinate of the ball (column index) + * @return True if a path is found to (6,5), otherwise false + */ + private static boolean setWay2(int[][] map, int i, int j) { + if (map[6][5] == 2) { + return true; + } + + if (map[i][j] == 0) { + map[i][j] = 2; + + // Move up + if (setWay2(map, i - 1, j)) { + return true; + } + // Move right + else if (setWay2(map, i, j + 1)) { + return true; + } + // Move down + else if (setWay2(map, i + 1, j)) { + return true; + } + // Move left + else if (setWay2(map, i, j - 1)) { + return true; + } + + map[i][j] = 3; // Mark as dead end (3) if no direction worked + return false; + } + return false; + } +} diff --git a/src/main/java/com/thealgorithms/backtracking/NQueens.java b/src/main/java/com/thealgorithms/backtracking/NQueens.java new file mode 100644 index 000000000000..1a8e453e34cb --- /dev/null +++ b/src/main/java/com/thealgorithms/backtracking/NQueens.java @@ -0,0 +1,111 @@ +package com.thealgorithms.backtracking; + +import java.util.ArrayList; +import java.util.List; + +/** + * Problem statement: Given a N x N chess board. Return all arrangements in + * which N queens can be placed on the board such no two queens attack each + * other. Ex. N = 6 Solution= There are 4 possible ways Arrangement: 1 ".Q....", + * "...Q..", ".....Q", "Q.....", "..Q...", "....Q." + * + * Arrangement: 2 "..Q...", ".....Q", ".Q....", "....Q.", "Q.....", "...Q.." + * + * Arrangement: 3 "...Q..", "Q.....", "....Q.", ".Q....", ".....Q", "..Q..." + * + * Arrangement: 4 "....Q.", "..Q...", "Q.....", ".....Q", "...Q..", ".Q...." + * + * Solution: Brute Force approach: + * + * Generate all possible arrangement to place N queens on N*N board. Check each + * board if queens are placed safely. If it is safe, include arrangement in + * solution set. Otherwise, ignore it + * + * Optimized solution: This can be solved using backtracking in below steps + * + * Start with first column and place queen on first row Try placing queen in a + * row on second column If placing second queen in second column attacks any of + * the previous queens, change the row in second column otherwise move to next + * column and try to place next queen In case if there is no rows where a queen + * can be placed such that it doesn't attack previous queens, then go back to + * previous column and change row of previous queen. Keep doing this until last + * queen is not placed safely. If there is no such way then return an empty list + * as solution + */ +public final class NQueens { + private NQueens() { + } + + public static List<List<String>> getNQueensArrangements(int queens) { + List<List<String>> arrangements = new ArrayList<>(); + getSolution(queens, arrangements, new int[queens], 0); + return arrangements; + } + + public static void placeQueens(final int queens) { + List<List<String>> arrangements = new ArrayList<List<String>>(); + getSolution(queens, arrangements, new int[queens], 0); + if (arrangements.isEmpty()) { + System.out.println("There is no way to place " + queens + " queens on board of size " + queens + "x" + queens); + } else { + System.out.println("Arrangement for placing " + queens + " queens"); + } + for (List<String> arrangement : arrangements) { + arrangement.forEach(System.out::println); + System.out.println(); + } + } + + /** + * This is backtracking function which tries to place queen recursively + * + * @param boardSize: size of chess board + * @param solutions: this holds all possible arrangements + * @param columns: columns[i] = rowId where queen is placed in ith column. + * @param columnIndex: This is the column in which queen is being placed + */ + private static void getSolution(int boardSize, List<List<String>> solutions, int[] columns, int columnIndex) { + if (columnIndex == boardSize) { + // this means that all queens have been placed + List<String> sol = new ArrayList<String>(); + for (int i = 0; i < boardSize; i++) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < boardSize; j++) { + sb.append(j == columns[i] ? "Q" : "."); + } + sol.add(sb.toString()); + } + solutions.add(sol); + return; + } + + // This loop tries to place queen in a row one by one + for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) { + columns[columnIndex] = rowIndex; + if (isPlacedCorrectly(columns, rowIndex, columnIndex)) { + // If queen is placed successfully at rowIndex in column=columnIndex then try + // placing queen in next column + getSolution(boardSize, solutions, columns, columnIndex + 1); + } + } + } + + /** + * This function checks if queen can be placed at row = rowIndex in column = + * columnIndex safely + * + * @param columns: columns[i] = rowId where queen is placed in ith column. + * @param rowIndex: row in which queen has to be placed + * @param columnIndex: column in which queen is being placed + * @return true: if queen can be placed safely false: otherwise + */ + private static boolean isPlacedCorrectly(int[] columns, int rowIndex, int columnIndex) { + for (int i = 0; i < columnIndex; i++) { + int diff = Math.abs(columns[i] - rowIndex); + if (diff == 0 || columnIndex - i == diff) { + return false; + } + } + return true; + } +} diff --git a/src/main/java/com/thealgorithms/backtracking/ParenthesesGenerator.java b/src/main/java/com/thealgorithms/backtracking/ParenthesesGenerator.java new file mode 100644 index 000000000000..bf93f946ab7b --- /dev/null +++ b/src/main/java/com/thealgorithms/backtracking/ParenthesesGenerator.java @@ -0,0 +1,50 @@ +package com.thealgorithms.backtracking; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class generates all valid combinations of parentheses for a given number of pairs using backtracking. + */ +public final class ParenthesesGenerator { + private ParenthesesGenerator() { + } + + /** + * Generates all valid combinations of parentheses for a given number of pairs. + * + * @param n The number of pairs of parentheses. + * @return A list of strings representing valid combinations of parentheses. + * @throws IllegalArgumentException if n is less than 0. + */ + public static List<String> generateParentheses(final int n) { + if (n < 0) { + throw new IllegalArgumentException("The number of pairs of parentheses cannot be negative"); + } + List<String> result = new ArrayList<>(); + generateParenthesesHelper(result, "", 0, 0, n); + return result; + } + + /** + * Helper function for generating all valid combinations of parentheses recursively. + * + * @param result The list to store valid combinations. + * @param current The current combination being formed. + * @param open The number of open parentheses. + * @param close The number of closed parentheses. + * @param n The total number of pairs of parentheses. + */ + private static void generateParenthesesHelper(List<String> result, final String current, final int open, final int close, final int n) { + if (current.length() == n * 2) { + result.add(current); + return; + } + if (open < n) { + generateParenthesesHelper(result, current + "(", open + 1, close, n); + } + if (close < open) { + generateParenthesesHelper(result, current + ")", open, close + 1, n); + } + } +} diff --git a/src/main/java/com/thealgorithms/backtracking/Permutation.java b/src/main/java/com/thealgorithms/backtracking/Permutation.java new file mode 100644 index 000000000000..21d26e53980f --- /dev/null +++ b/src/main/java/com/thealgorithms/backtracking/Permutation.java @@ -0,0 +1,57 @@ +package com.thealgorithms.backtracking; + +import java.util.LinkedList; +import java.util.List; + +/** + * Finds all permutations of given array + * @author Alan Piao (<a href="https://github.com/cpiao3">Git-Alan Piao</a>) + */ +public final class Permutation { + private Permutation() { + } + + /** + * Find all permutations of given array using backtracking + * @param arr the array. + * @param <T> the type of elements in the array. + * @return a list of all permutations. + */ + public static <T> List<T[]> permutation(T[] arr) { + T[] array = arr.clone(); + List<T[]> result = new LinkedList<>(); + backtracking(array, 0, result); + return result; + } + + /** + * Backtrack all possible orders of a given array + * @param arr the array. + * @param index the starting index. + * @param result the list contains all permutations. + * @param <T> the type of elements in the array. + */ + private static <T> void backtracking(T[] arr, int index, List<T[]> result) { + if (index == arr.length) { + result.add(arr.clone()); + } + for (int i = index; i < arr.length; i++) { + swap(index, i, arr); + backtracking(arr, index + 1, result); + swap(index, i, arr); + } + } + + /** + * Swap two element for a given array + * @param a first index + * @param b second index + * @param arr the array. + * @param <T> the type of elements in the array. + */ + private static <T> void swap(int a, int b, T[] arr) { + T temp = arr[a]; + arr[a] = arr[b]; + arr[b] = temp; + } +} diff --git a/src/main/java/com/thealgorithms/backtracking/PowerSum.java b/src/main/java/com/thealgorithms/backtracking/PowerSum.java new file mode 100644 index 000000000000..b34ba660ebd7 --- /dev/null +++ b/src/main/java/com/thealgorithms/backtracking/PowerSum.java @@ -0,0 +1,51 @@ +package com.thealgorithms.backtracking; + +/** + * Problem Statement: + * Find the number of ways that a given integer, N, can be expressed as the sum of the Xth powers + * of unique, natural numbers. + * For example, if N=100 and X=3, we have to find all combinations of unique cubes adding up to 100. + * The only solution is 1^3 + 2^3 + 3^3 + 4^3. Therefore, the output will be 1. + * + * N is represented by the parameter 'targetSum' in the code. + * X is represented by the parameter 'power' in the code. + */ +public class PowerSum { + + /** + * Calculates the number of ways to express the target sum as a sum of Xth powers of unique natural numbers. + * + * @param targetSum The target sum to achieve (N in the problem statement) + * @param power The power to raise natural numbers to (X in the problem statement) + * @return The number of ways to express the target sum + */ + public int powSum(int targetSum, int power) { + // Special case: when both targetSum and power are zero + if (targetSum == 0 && power == 0) { + return 1; // by convention, one way to sum to zero: use nothing + } + return sumRecursive(targetSum, power, 1, 0); + } + + /** + * Recursively calculates the number of ways to express the remaining sum as a sum of Xth powers. + * + * @param remainingSum The remaining sum to achieve + * @param power The power to raise natural numbers to (X in the problem statement) + * @param currentNumber The current natural number being considered + * @param currentSum The current sum of powered numbers + * @return The number of valid combinations + */ + private int sumRecursive(int remainingSum, int power, int currentNumber, int currentSum) { + int newSum = currentSum + (int) Math.pow(currentNumber, power); + + if (newSum == remainingSum) { + return 1; + } + if (newSum > remainingSum) { + return 0; + } + + return sumRecursive(remainingSum, power, currentNumber + 1, newSum) + sumRecursive(remainingSum, power, currentNumber + 1, currentSum); + } +} diff --git a/src/main/java/com/thealgorithms/backtracking/SubsequenceFinder.java b/src/main/java/com/thealgorithms/backtracking/SubsequenceFinder.java new file mode 100644 index 000000000000..4a159dbfe0b1 --- /dev/null +++ b/src/main/java/com/thealgorithms/backtracking/SubsequenceFinder.java @@ -0,0 +1,54 @@ +package com.thealgorithms.backtracking; + +import java.util.ArrayList; +import java.util.List; + +/** + * Class generates all subsequences for a given list of elements using backtracking + */ +public final class SubsequenceFinder { + private SubsequenceFinder() { + } + + /** + * Find all subsequences of given list using backtracking + * + * @param sequence a list of items on the basis of which we need to generate all subsequences + * @param <T> the type of elements in the array + * @return a list of all subsequences + */ + public static <T> List<List<T>> generateAll(List<T> sequence) { + List<List<T>> allSubSequences = new ArrayList<>(); + if (sequence.isEmpty()) { + allSubSequences.add(new ArrayList<>()); + return allSubSequences; + } + List<T> currentSubsequence = new ArrayList<>(); + backtrack(sequence, currentSubsequence, 0, allSubSequences); + return allSubSequences; + } + + /** + * Iterate through each branch of states + * We know that each state has exactly two branching + * It terminates when it reaches the end of the given sequence + * + * @param sequence all elements + * @param currentSubsequence current subsequence + * @param index current index + * @param allSubSequences contains all sequences + * @param <T> the type of elements which we generate + */ + private static <T> void backtrack(List<T> sequence, List<T> currentSubsequence, final int index, List<List<T>> allSubSequences) { + assert index <= sequence.size(); + if (index == sequence.size()) { + allSubSequences.add(new ArrayList<>(currentSubsequence)); + return; + } + + backtrack(sequence, currentSubsequence, index + 1, allSubSequences); + currentSubsequence.add(sequence.get(index)); + backtrack(sequence, currentSubsequence, index + 1, allSubSequences); + currentSubsequence.removeLast(); + } +} diff --git a/src/main/java/com/thealgorithms/backtracking/WordPatternMatcher.java b/src/main/java/com/thealgorithms/backtracking/WordPatternMatcher.java new file mode 100644 index 000000000000..1854cab20a7f --- /dev/null +++ b/src/main/java/com/thealgorithms/backtracking/WordPatternMatcher.java @@ -0,0 +1,86 @@ +package com.thealgorithms.backtracking; + +import java.util.HashMap; +import java.util.Map; + +/** + * Class to determine if a pattern matches a string using backtracking. + * + * Example: + * Pattern: "abab" + * Input String: "JavaPythonJavaPython" + * Output: true + * + * Pattern: "aaaa" + * Input String: "JavaJavaJavaJava" + * Output: true + * + * Pattern: "aabb" + * Input String: "JavaPythonPythonJava" + * Output: false + */ +public final class WordPatternMatcher { + private WordPatternMatcher() { + } + + /** + * Determines if the given pattern matches the input string using backtracking. + * + * @param pattern The pattern to match. + * @param inputString The string to match against the pattern. + * @return True if the pattern matches the string, False otherwise. + */ + public static boolean matchWordPattern(String pattern, String inputString) { + Map<Character, String> patternMap = new HashMap<>(); + Map<String, Character> strMap = new HashMap<>(); + return backtrack(pattern, inputString, 0, 0, patternMap, strMap); + } + + /** + * Backtracking helper function to check if the pattern matches the string. + * + * @param pattern The pattern string. + * @param inputString The string to match against the pattern. + * @param patternIndex Current index in the pattern. + * @param strIndex Current index in the input string. + * @param patternMap Map to store pattern characters to string mappings. + * @param strMap Map to store string to pattern character mappings. + * @return True if the pattern matches, False otherwise. + */ + private static boolean backtrack(String pattern, String inputString, int patternIndex, int strIndex, Map<Character, String> patternMap, Map<String, Character> strMap) { + if (patternIndex == pattern.length() && strIndex == inputString.length()) { + return true; + } + if (patternIndex == pattern.length() || strIndex == inputString.length()) { + return false; + } + + char currentChar = pattern.charAt(patternIndex); + if (patternMap.containsKey(currentChar)) { + String mappedStr = patternMap.get(currentChar); + if (inputString.startsWith(mappedStr, strIndex)) { + return backtrack(pattern, inputString, patternIndex + 1, strIndex + mappedStr.length(), patternMap, strMap); + } else { + return false; + } + } + + for (int end = strIndex + 1; end <= inputString.length(); end++) { + String substring = inputString.substring(strIndex, end); + if (strMap.containsKey(substring)) { + continue; + } + + patternMap.put(currentChar, substring); + strMap.put(substring, currentChar); + if (backtrack(pattern, inputString, patternIndex + 1, end, patternMap, strMap)) { + return true; + } + + patternMap.remove(currentChar); + strMap.remove(substring); + } + + return false; + } +} diff --git a/src/main/java/com/thealgorithms/backtracking/WordSearch.java b/src/main/java/com/thealgorithms/backtracking/WordSearch.java new file mode 100644 index 000000000000..174ca90ccaab --- /dev/null +++ b/src/main/java/com/thealgorithms/backtracking/WordSearch.java @@ -0,0 +1,109 @@ +package com.thealgorithms.backtracking; + +/** + * Word Search Problem + * + * This class solves the word search problem where given an m x n grid of characters (board) + * and a target word, the task is to check if the word exists in the grid. + * The word can be constructed from sequentially adjacent cells (horizontally or vertically), + * and the same cell may not be used more than once in constructing the word. + * + * Example: + * - For board = + * [ + * ['A','B','C','E'], + * ['S','F','C','S'], + * ['A','D','E','E'] + * ] + * and word = "ABCCED", -> returns true + * and word = "SEE", -> returns true + * and word = "ABCB", -> returns false + * + * Solution: + * - Depth First Search (DFS) with backtracking is used to explore possible paths from any cell + * matching the first letter of the word. DFS ensures that we search all valid paths, while + * backtracking helps in reverting decisions when a path fails to lead to a solution. + * + * Time Complexity: O(m * n * 3^L) + * - m = number of rows in the board + * - n = number of columns in the board + * - L = length of the word + * - For each cell, we look at 3 possible directions (since we exclude the previously visited direction), + * and we do this for L letters. + * + * Space Complexity: O(L) + * - Stack space for the recursive DFS function, where L is the maximum depth of recursion (length of the word). + */ +public class WordSearch { + private final int[] dx = {0, 0, 1, -1}; + private final int[] dy = {1, -1, 0, 0}; + private boolean[][] visited; + private char[][] board; + private String word; + + /** + * Checks if the given (x, y) coordinates are valid positions in the board. + * + * @param x The row index. + * @param y The column index. + * @return True if the coordinates are within the bounds of the board; false otherwise. + */ + private boolean isValid(int x, int y) { + return x >= 0 && x < board.length && y >= 0 && y < board[0].length; + } + + /** + * Performs Depth First Search (DFS) from the cell (x, y) + * to search for the next character in the word. + * + * @param x The current row index. + * @param y The current column index. + * @param nextIdx The index of the next character in the word to be matched. + * @return True if a valid path is found to match the remaining characters of the word; false otherwise. + */ + private boolean doDFS(int x, int y, int nextIdx) { + visited[x][y] = true; + if (nextIdx == word.length()) { + return true; + } + + for (int i = 0; i < 4; ++i) { + int xi = x + dx[i]; + int yi = y + dy[i]; + if (isValid(xi, yi) && board[xi][yi] == word.charAt(nextIdx) && !visited[xi][yi]) { + boolean exists = doDFS(xi, yi, nextIdx + 1); + if (exists) { + return true; + } + } + } + + visited[x][y] = false; // Backtrack + return false; + } + + /** + * Main function to check if the word exists in the board. It initiates DFS from any + * cell that matches the first character of the word. + * + * @param board The 2D grid of characters (the board). + * @param word The target word to search for in the board. + * @return True if the word exists in the board; false otherwise. + */ + public boolean exist(char[][] board, String word) { + this.board = board; + this.word = word; + for (int i = 0; i < board.length; ++i) { + for (int j = 0; j < board[0].length; ++j) { + if (board[i][j] == word.charAt(0)) { + visited = new boolean[board.length][board[0].length]; + boolean exists = doDFS(i, j, 1); + if (exists) { + return true; + } + } + } + } + return false; + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/BcdConversion.java b/src/main/java/com/thealgorithms/bitmanipulation/BcdConversion.java new file mode 100644 index 000000000000..e6bd35720d9f --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/BcdConversion.java @@ -0,0 +1,82 @@ +package com.thealgorithms.bitmanipulation; + +/** + * This class provides methods to convert between BCD (Binary-Coded Decimal) and decimal numbers. + * + * BCD is a class of binary encodings of decimal numbers where each decimal digit is represented by a fixed number of binary digits, usually four or eight. + * + * For more information, refer to the + * <a href="https://en.wikipedia.org/wiki/Binary-coded_decimal">Binary-Coded Decimal</a> Wikipedia page. + * + * <b>Example usage:</b> + * <pre> + * int decimal = BcdConversion.bcdToDecimal(0x1234); + * System.out.println("BCD 0x1234 to decimal: " + decimal); // Output: 1234 + * + * int bcd = BcdConversion.decimalToBcd(1234); + * System.out.println("Decimal 1234 to BCD: " + Integer.toHexString(bcd)); // Output: 0x1234 + * </pre> + */ +public final class BcdConversion { + private BcdConversion() { + } + + /** + * Converts a BCD (Binary-Coded Decimal) number to a decimal number. + * <p>Steps: + * <p>1. Validate the BCD number to ensure all digits are between 0 and 9. + * <p>2. Extract the last 4 bits (one BCD digit) from the BCD number. + * <p>3. Multiply the extracted digit by the corresponding power of 10 and add it to the decimal number. + * <p>4. Shift the BCD number right by 4 bits to process the next BCD digit. + * <p>5. Repeat steps 1-4 until the BCD number is zero. + * + * @param bcd The BCD number. + * @return The corresponding decimal number. + * @throws IllegalArgumentException if the BCD number contains invalid digits. + */ + public static int bcdToDecimal(int bcd) { + int decimal = 0; + int multiplier = 1; + + // Validate BCD digits + while (bcd > 0) { + int digit = bcd & 0xF; + if (digit > 9) { + throw new IllegalArgumentException("Invalid BCD digit: " + digit); + } + decimal += digit * multiplier; + multiplier *= 10; + bcd >>= 4; + } + return decimal; + } + + /** + * Converts a decimal number to BCD (Binary-Coded Decimal). + * <p>Steps: + * <p>1. Check if the decimal number is within the valid range for BCD (0 to 9999). + * <p>2. Extract the last decimal digit from the decimal number. + * <p>3. Shift the digit to the correct BCD position and add it to the BCD number. + * <p>4. Remove the last decimal digit from the decimal number. + * <p>5. Repeat steps 2-4 until the decimal number is zero. + * + * @param decimal The decimal number. + * @return The corresponding BCD number. + * @throws IllegalArgumentException if the decimal number is greater than 9999. + */ + public static int decimalToBcd(int decimal) { + if (decimal < 0 || decimal > 9999) { + throw new IllegalArgumentException("Value out of bounds for BCD representation: " + decimal); + } + + int bcd = 0; + int shift = 0; + while (decimal > 0) { + int digit = decimal % 10; + bcd |= (digit << (shift * 4)); + decimal /= 10; + shift++; + } + return bcd; + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/BinaryPalindromeCheck.java b/src/main/java/com/thealgorithms/bitmanipulation/BinaryPalindromeCheck.java new file mode 100644 index 000000000000..0d6fd140c720 --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/BinaryPalindromeCheck.java @@ -0,0 +1,43 @@ +package com.thealgorithms.bitmanipulation; + +/** + * This class contains a method to check if the binary representation of a number is a palindrome. + * <p> + * A binary palindrome is a number whose binary representation is the same when read from left to right and right to left. + * For example, the number 9 has a binary representation of 1001, which is a palindrome. + * The number 10 has a binary representation of 1010, which is not a palindrome. + * </p> + * + * @author Hardvan + */ +public final class BinaryPalindromeCheck { + private BinaryPalindromeCheck() { + } + + /** + * Checks if the binary representation of a number is a palindrome. + * + * @param x The number to check. + * @return True if the binary representation is a palindrome, otherwise false. + */ + public static boolean isBinaryPalindrome(int x) { + int reversed = reverseBits(x); + return x == reversed; + } + + /** + * Helper function to reverse all the bits of an integer. + * + * @param x The number to reverse the bits of. + * @return The number with reversed bits. + */ + private static int reverseBits(int x) { + int result = 0; + while (x > 0) { + result <<= 1; + result |= (x & 1); + x >>= 1; + } + return result; + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/BitSwap.java b/src/main/java/com/thealgorithms/bitmanipulation/BitSwap.java new file mode 100644 index 000000000000..40b3097b1276 --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/BitSwap.java @@ -0,0 +1,15 @@ +package com.thealgorithms.bitmanipulation; + +public final class BitSwap { + private BitSwap() { + } + /* + * @brief Swaps the bits at the position posA and posB from data + */ + public static int bitSwap(int data, final int posA, final int posB) { + if (SingleBitOperations.getBit(data, posA) != SingleBitOperations.getBit(data, posB)) { + data ^= (1 << posA) ^ (1 << posB); + } + return data; + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/BooleanAlgebraGates.java b/src/main/java/com/thealgorithms/bitmanipulation/BooleanAlgebraGates.java new file mode 100644 index 000000000000..869466320831 --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/BooleanAlgebraGates.java @@ -0,0 +1,111 @@ +package com.thealgorithms.bitmanipulation; + +import java.util.List; + +/** + * Implements various Boolean algebra gates (AND, OR, NOT, XOR, NAND, NOR) + */ +public final class BooleanAlgebraGates { + + private BooleanAlgebraGates() { + // Prevent instantiation + } + + /** + * Represents a Boolean gate that takes multiple inputs and returns a result. + */ + interface BooleanGate { + /** + * Evaluates the gate with the given inputs. + * + * @param inputs The input values for the gate. + * @return The result of the evaluation. + */ + boolean evaluate(List<Boolean> inputs); + } + + /** + * AND Gate implementation. + * Returns true if all inputs are true; otherwise, false. + */ + static class ANDGate implements BooleanGate { + @Override + public boolean evaluate(List<Boolean> inputs) { + for (boolean input : inputs) { + if (!input) { + return false; + } + } + return true; + } + } + + /** + * OR Gate implementation. + * Returns true if at least one input is true; otherwise, false. + */ + static class ORGate implements BooleanGate { + @Override + public boolean evaluate(List<Boolean> inputs) { + for (boolean input : inputs) { + if (input) { + return true; + } + } + return false; + } + } + + /** + * NOT Gate implementation (Unary operation). + * Negates a single input value. + */ + static class NOTGate { + /** + * Evaluates the negation of the input. + * + * @param input The input value to be negated. + * @return The negated value. + */ + public boolean evaluate(boolean input) { + return !input; + } + } + + /** + * XOR Gate implementation. + * Returns true if an odd number of inputs are true; otherwise, false. + */ + static class XORGate implements BooleanGate { + @Override + public boolean evaluate(List<Boolean> inputs) { + boolean result = false; + for (boolean input : inputs) { + result ^= input; + } + return result; + } + } + + /** + * NAND Gate implementation. + * Returns true if at least one input is false; otherwise, false. + */ + static class NANDGate implements BooleanGate { + @Override + public boolean evaluate(List<Boolean> inputs) { + return !new ANDGate().evaluate(inputs); // Equivalent to negation of AND + } + } + + /** + * NOR Gate implementation. + * Returns true if all inputs are false; otherwise, false. + */ + static class NORGate implements BooleanGate { + @Override + public boolean evaluate(List<Boolean> inputs) { + return !new ORGate().evaluate(inputs); // Equivalent to negation of OR + } + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/ClearLeftmostSetBit.java b/src/main/java/com/thealgorithms/bitmanipulation/ClearLeftmostSetBit.java new file mode 100644 index 000000000000..3e9a4a21183f --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/ClearLeftmostSetBit.java @@ -0,0 +1,39 @@ +package com.thealgorithms.bitmanipulation; + +/** + * ClearLeftmostSetBit class contains a method to clear the leftmost set bit of a number. + * The leftmost set bit is the leftmost bit that is set to 1 in the binary representation of a number. + * + * Example: + * 26 (11010) -> 10 (01010) + * 1 (1) -> 0 (0) + * 7 (111) -> 3 (011) + * 6 (0110) -> 2 (0010) + * + * @author Hardvan + */ +public final class ClearLeftmostSetBit { + private ClearLeftmostSetBit() { + } + + /** + * Clears the leftmost set bit (1) of a given number. + * Step 1: Find the position of the leftmost set bit + * Step 2: Create a mask with all bits set except for the leftmost set bit + * Step 3: Clear the leftmost set bit using AND with the mask + * + * @param num The input number. + * @return The number after clearing the leftmost set bit. + */ + public static int clearLeftmostSetBit(int num) { + int pos = 0; + int temp = num; + while (temp > 0) { + temp >>= 1; + pos++; + } + + int mask = ~(1 << (pos - 1)); + return num & mask; + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/CountLeadingZeros.java b/src/main/java/com/thealgorithms/bitmanipulation/CountLeadingZeros.java new file mode 100644 index 000000000000..318334f0b951 --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/CountLeadingZeros.java @@ -0,0 +1,39 @@ +package com.thealgorithms.bitmanipulation; + +/** + * CountLeadingZeros class contains a method to count the number of leading zeros in the binary representation of a number. + * The number of leading zeros is the number of zeros before the leftmost 1 bit. + * For example, the number 5 has 29 leading zeros in its 32-bit binary representation. + * The number 0 has 32 leading zeros. + * The number 1 has 31 leading zeros. + * The number -1 has no leading zeros. + * + * @author Hardvan + */ +public final class CountLeadingZeros { + private CountLeadingZeros() { + } + + /** + * Counts the number of leading zeros in the binary representation of a number. + * Method: Keep shifting the mask to the right until the leftmost bit is 1. + * The number of shifts is the number of leading zeros. + * + * @param num The input number. + * @return The number of leading zeros. + */ + public static int countLeadingZeros(int num) { + if (num == 0) { + return 32; + } + + int count = 0; + int mask = 1 << 31; + while ((mask & num) == 0) { + count++; + mask >>>= 1; + } + + return count; + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/CountSetBits.java b/src/main/java/com/thealgorithms/bitmanipulation/CountSetBits.java new file mode 100644 index 000000000000..242f35fc35f2 --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/CountSetBits.java @@ -0,0 +1,79 @@ +package com.thealgorithms.bitmanipulation; + +public class CountSetBits { + + /** + * The below algorithm is called as Brian Kernighan's algorithm + * We can use Brian Kernighan’s algorithm to improve the above naive algorithm’s performance. + The idea is to only consider the set bits of an integer by turning off its rightmost set bit + (after counting it), so the next iteration of the loop considers the next rightmost bit. + + The expression n & (n-1) can be used to turn off the rightmost set bit of a number n. This + works as the expression n-1 flips all the bits after the rightmost set bit of n, including the + rightmost set bit itself. Therefore, n & (n-1) results in the last bit flipped of n. + + For example, consider number 52, which is 00110100 in binary, and has a total 3 bits set. + + 1st iteration of the loop: n = 52 + + 00110100 & (n) + 00110011 (n-1) + ~~~~~~~~ + 00110000 + + + 2nd iteration of the loop: n = 48 + + 00110000 & (n) + 00101111 (n-1) + ~~~~~~~~ + 00100000 + + + 3rd iteration of the loop: n = 32 + + 00100000 & (n) + 00011111 (n-1) + ~~~~~~~~ + 00000000 (n = 0) + + * @param num takes Long number whose number of set bit is to be found + * @return the count of set bits in the binary equivalent + */ + public long countSetBits(long num) { + long cnt = 0; + while (num > 0) { + cnt++; + num &= (num - 1); + } + return cnt; + } + + /** + * This approach takes O(1) running time to count the set bits, but requires a pre-processing. + * + * So, we divide our 32-bit input into 8-bit chunks, with four chunks. We have 8 bits in each chunk. + * + * Then the range is from 0-255 (0 to 2^7). + * So, we may need to count set bits from 0 to 255 in individual chunks. + * + * @param num takes a long number + * @return the count of set bits in the binary equivalent + */ + public int lookupApproach(int num) { + int[] table = new int[256]; + table[0] = 0; + + for (int i = 1; i < 256; i++) { + table[i] = (i & 1) + table[i >> 1]; // i >> 1 equals to i/2 + } + + int res = 0; + for (int i = 0; i < 4; i++) { + res += table[num & 0xff]; + num >>= 8; + } + + return res; + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/FindNthBit.java b/src/main/java/com/thealgorithms/bitmanipulation/FindNthBit.java new file mode 100644 index 000000000000..7a35fc3feebf --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/FindNthBit.java @@ -0,0 +1,46 @@ +package com.thealgorithms.bitmanipulation; + +/** + * A utility class to find the Nth bit of a given number. + * + * <p>This class provides a method to extract the value of the Nth bit (either 0 or 1) + * from the binary representation of a given integer. + * + * <p>Example: + * <pre>{@code + * int result = FindNthBit.findNthBit(5, 2); // returns 0 as the 2nd bit of 5 (binary 101) is 0. + * }</pre> + * + * <p>Author: <a href="https://github.com/Tuhinm2002">Tuhinm2002</a> + */ +public final class FindNthBit { + + /** + * Private constructor to prevent instantiation. + * + * <p>This is a utility class, and it should not be instantiated. + * Attempting to instantiate this class will throw an UnsupportedOperationException. + */ + private FindNthBit() { + throw new UnsupportedOperationException("Utility class"); + } + + /** + * Finds the value of the Nth bit of the given number. + * + * <p>This method uses bitwise operations to extract the Nth bit from the + * binary representation of the given integer. + * + * @param num the integer number whose Nth bit is to be found + * @param n the bit position (1-based) to retrieve + * @return the value of the Nth bit (0 or 1) + * @throws IllegalArgumentException if the bit position is less than 1 + */ + public static int findNthBit(int num, int n) { + if (n < 1) { + throw new IllegalArgumentException("Bit position must be greater than or equal to 1."); + } + // Shifting the number to the right by (n - 1) positions and checking the last bit + return (num & (1 << (n - 1))) >> (n - 1); + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/FirstDifferentBit.java b/src/main/java/com/thealgorithms/bitmanipulation/FirstDifferentBit.java new file mode 100644 index 000000000000..9a761c572e2c --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/FirstDifferentBit.java @@ -0,0 +1,33 @@ +package com.thealgorithms.bitmanipulation; + +/** + * This class provides a method to find the first differing bit + * between two integers. + * + * Example: + * x = 10 (1010 in binary) + * y = 12 (1100 in binary) + * The first differing bit is at index 1 (0-based) + * So, the output will be 1 + * + * @author Hardvan + */ +public final class FirstDifferentBit { + private FirstDifferentBit() { + } + + /** + * Identifies the index of the first differing bit between two integers. + * Steps: + * 1. XOR the two integers to get the differing bits + * 2. Find the index of the first set bit in XOR result + * + * @param x the first integer + * @param y the second integer + * @return the index of the first differing bit (0-based) + */ + public static int firstDifferentBit(int x, int y) { + int diff = x ^ y; + return Integer.numberOfTrailingZeros(diff); + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/GenerateSubsets.java b/src/main/java/com/thealgorithms/bitmanipulation/GenerateSubsets.java new file mode 100644 index 000000000000..f1b812495c1b --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/GenerateSubsets.java @@ -0,0 +1,44 @@ +package com.thealgorithms.bitmanipulation; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class provides a method to generate all subsets (power set) + * of a given set using bit manipulation. + * + * @author Hardvan + */ +public final class GenerateSubsets { + private GenerateSubsets() { + } + + /** + * Generates all subsets of a given set using bit manipulation. + * Steps: + * 1. Iterate over all numbers from 0 to 2^n - 1. + * 2. For each number, iterate over all bits from 0 to n - 1. + * 3. If the i-th bit of the number is set, add the i-th element of the set to the current subset. + * 4. Add the current subset to the list of subsets. + * 5. Return the list of subsets. + * + * @param set the input set of integers + * @return a list of all subsets represented as lists of integers + */ + public static List<List<Integer>> generateSubsets(int[] set) { + int n = set.length; + List<List<Integer>> subsets = new ArrayList<>(); + + for (int mask = 0; mask < (1 << n); mask++) { + List<Integer> subset = new ArrayList<>(); + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) != 0) { + subset.add(set[i]); + } + } + subsets.add(subset); + } + + return subsets; + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/GrayCodeConversion.java b/src/main/java/com/thealgorithms/bitmanipulation/GrayCodeConversion.java new file mode 100644 index 000000000000..83cd30c7d50a --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/GrayCodeConversion.java @@ -0,0 +1,44 @@ +package com.thealgorithms.bitmanipulation; + +/** + * Gray code is a binary numeral system where two successive values differ in only one bit. + * This is a simple conversion between binary and Gray code. + * Example: + * 7 -> 0111 -> 0100 -> 4 + * 4 -> 0100 -> 0111 -> 7 + * 0 -> 0000 -> 0000 -> 0 + * 1 -> 0001 -> 0000 -> 0 + * 2 -> 0010 -> 0011 -> 3 + * 3 -> 0011 -> 0010 -> 2 + * + * @author Hardvan + */ +public final class GrayCodeConversion { + private GrayCodeConversion() { + } + + /** + * Converts a binary number to Gray code. + * + * @param num The binary number. + * @return The corresponding Gray code. + */ + public static int binaryToGray(int num) { + return num ^ (num >> 1); + } + + /** + * Converts a Gray code number back to binary. + * + * @param gray The Gray code number. + * @return The corresponding binary number. + */ + public static int grayToBinary(int gray) { + int binary = gray; + while (gray > 0) { + gray >>= 1; + binary ^= gray; + } + return binary; + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/HammingDistance.java b/src/main/java/com/thealgorithms/bitmanipulation/HammingDistance.java new file mode 100644 index 000000000000..4c24909ef234 --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/HammingDistance.java @@ -0,0 +1,29 @@ +package com.thealgorithms.bitmanipulation; + +/** + * The Hamming distance between two integers is the number of positions at which the corresponding bits are different. + * Given two integers x and y, calculate the Hamming distance. + * Example: + * Input: x = 1, y = 4 + * Output: 2 + * Explanation: 1 (0001) and 4 (0100) have 2 differing bits. + * + * @author Hardvan + */ +public final class HammingDistance { + private HammingDistance() { + } + + /** + * Calculates the Hamming distance between two integers. + * The Hamming distance is the number of differing bits between the two integers. + * + * @param x The first integer. + * @param y The second integer. + * @return The Hamming distance (number of differing bits). + */ + public static int hammingDistance(int x, int y) { + int xor = x ^ y; + return Integer.bitCount(xor); + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/HigherLowerPowerOfTwo.java b/src/main/java/com/thealgorithms/bitmanipulation/HigherLowerPowerOfTwo.java new file mode 100644 index 000000000000..0fb058b2b8a3 --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/HigherLowerPowerOfTwo.java @@ -0,0 +1,54 @@ +package com.thealgorithms.bitmanipulation; + +/** + * HigherLowerPowerOfTwo class has two methods to find the next higher and lower power of two. + * <p> + * nextHigherPowerOfTwo method finds the next higher power of two. + * nextLowerPowerOfTwo method finds the next lower power of two. + * Both methods take an integer as input and return the next higher or lower power of two. + * If the input is less than 1, the next higher power of two is 1. + * If the input is less than or equal to 1, the next lower power of two is 0. + * nextHigherPowerOfTwo method uses bitwise operations to find the next higher power of two. + * nextLowerPowerOfTwo method uses Integer.highestOneBit method to find the next lower power of two. + * The time complexity of both methods is O(1). + * The space complexity of both methods is O(1). + * </p> + * + * @author Hardvan + */ +public final class HigherLowerPowerOfTwo { + private HigherLowerPowerOfTwo() { + } + + /** + * Finds the next higher power of two. + * + * @param x The given number. + * @return The next higher power of two. + */ + public static int nextHigherPowerOfTwo(int x) { + if (x < 1) { + return 1; + } + x--; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return x + 1; + } + + /** + * Finds the next lower power of two. + * + * @param x The given number. + * @return The next lower power of two. + */ + public static int nextLowerPowerOfTwo(int x) { + if (x < 1) { + return 0; + } + return Integer.highestOneBit(x); + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/HighestSetBit.java b/src/main/java/com/thealgorithms/bitmanipulation/HighestSetBit.java new file mode 100644 index 000000000000..2398b8214371 --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/HighestSetBit.java @@ -0,0 +1,54 @@ +package com.thealgorithms.bitmanipulation; + +import java.util.Optional; + +/** + * Find Highest Set Bit + * + * This class provides a utility method to calculate the position of the highest + * (most significant) bit that is set to 1 in a given non-negative integer. + * It is often used in bit manipulation tasks to find the left-most set bit in binary + * representation of a number. + * + * Example: + * - For input 18 (binary 10010), the highest set bit is at position 4 (zero-based index). + * + * @author Bama Charan Chhandogi + * @version 1.0 + * @since 2021-06-23 + */ +public final class HighestSetBit { + + private HighestSetBit() { + } + + /** + * Finds the highest (most significant) set bit in the given integer. + * The method returns the position (index) of the highest set bit as an {@link Optional}. + * + * - If the number is 0, no bits are set, and the method returns {@link Optional#empty()}. + * - If the number is negative, the method throws {@link IllegalArgumentException}. + * + * @param num The input integer for which the highest set bit is to be found. It must be non-negative. + * @return An {@link Optional} containing the index of the highest set bit (zero-based). + * Returns {@link Optional#empty()} if the number is 0. + * @throws IllegalArgumentException if the input number is negative. + */ + public static Optional<Integer> findHighestSetBit(int num) { + if (num < 0) { + throw new IllegalArgumentException("Input cannot be negative"); + } + + if (num == 0) { + return Optional.empty(); + } + + int position = 0; + while (num > 0) { + num >>= 1; + position++; + } + + return Optional.of(position - 1); // Subtract 1 to convert to zero-based index + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/IndexOfRightMostSetBit.java b/src/main/java/com/thealgorithms/bitmanipulation/IndexOfRightMostSetBit.java new file mode 100644 index 000000000000..1b8962344ea7 --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/IndexOfRightMostSetBit.java @@ -0,0 +1,44 @@ +package com.thealgorithms.bitmanipulation; + +/** + * Utility class for bit manipulation operations. + * This class provides methods to work with bitwise operations. + * Specifically, it includes a method to find the index of the rightmost set bit + * in an integer. + * This class is not meant to be instantiated. + * + * Author: Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi) + */ +public final class IndexOfRightMostSetBit { + + private IndexOfRightMostSetBit() { + } + + /** + * Finds the index of the rightmost set bit in the given integer. + * The index is zero-based, meaning the rightmost bit has an index of 0. + * + * @param n the integer to check for the rightmost set bit + * @return the index of the rightmost set bit; -1 if there are no set bits + * (i.e., the input integer is 0) + */ + public static int indexOfRightMostSetBit(int n) { + if (n == 0) { + return -1; // No set bits + } + + // Handle negative numbers by finding the two's complement + if (n < 0) { + n = -n; + n = n & (~n + 1); // Isolate the rightmost set bit + } + + int index = 0; + while ((n & 1) == 0) { + n = n >> 1; + index++; + } + + return index; + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/IsEven.java b/src/main/java/com/thealgorithms/bitmanipulation/IsEven.java new file mode 100644 index 000000000000..09d5383322ff --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/IsEven.java @@ -0,0 +1,14 @@ +package com.thealgorithms.bitmanipulation; + +/** + * Checks whether a number is even + * @author Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi) + */ + +public final class IsEven { + private IsEven() { + } + public static boolean isEven(int number) { + return (number & 1) == 0; + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/IsPowerTwo.java b/src/main/java/com/thealgorithms/bitmanipulation/IsPowerTwo.java new file mode 100644 index 000000000000..4cdf3c6faa3e --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/IsPowerTwo.java @@ -0,0 +1,32 @@ +package com.thealgorithms.bitmanipulation; + +/** + * Utility class for checking if a number is a power of two. + * A power of two is a number that can be expressed as 2^n where n is a non-negative integer. + * This class provides a method to determine if a given integer is a power of two using bit manipulation. + * + * @author Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi) + */ +public final class IsPowerTwo { + private IsPowerTwo() { + } + + /** + * Checks if the given integer is a power of two. + * + * A number is considered a power of two if it is greater than zero and + * has exactly one '1' bit in its binary representation. This method + * uses the property that for any power of two (n), the expression + * (n & (n - 1)) will be zero. + * + * @param number the integer to check + * @return true if the number is a power of two, false otherwise + */ + public static boolean isPowerTwo(int number) { + if (number <= 0) { + return false; + } + int ans = number & (number - 1); + return ans == 0; + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/LowestSetBit.java b/src/main/java/com/thealgorithms/bitmanipulation/LowestSetBit.java new file mode 100644 index 000000000000..127b6fa2c0b1 --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/LowestSetBit.java @@ -0,0 +1,34 @@ +package com.thealgorithms.bitmanipulation; + +/** + * Lowest Set Bit + * @author Prayas Kumar (https://github.com/prayas7102) + */ + +public final class LowestSetBit { + // Private constructor to hide the default public one + private LowestSetBit() { + } + /** + * Isolates the lowest set bit of the given number. For example, if n = 18 + * (binary: 10010), the result will be 2 (binary: 00010). + * + * @param n the number whose lowest set bit will be isolated + * @return the isolated lowest set bit of n + */ + public static int isolateLowestSetBit(int n) { + // Isolate the lowest set bit using n & -n + return n & -n; + } + /** + * Clears the lowest set bit of the given number. + * For example, if n = 18 (binary: 10010), the result will be 16 (binary: 10000). + * + * @param n the number whose lowest set bit will be cleared + * @return the number after clearing its lowest set bit + */ + public static int clearLowestSetBit(int n) { + // Clear the lowest set bit using n & (n - 1) + return n & (n - 1); + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/ModuloPowerOfTwo.java b/src/main/java/com/thealgorithms/bitmanipulation/ModuloPowerOfTwo.java new file mode 100644 index 000000000000..537a046f77e4 --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/ModuloPowerOfTwo.java @@ -0,0 +1,28 @@ +package com.thealgorithms.bitmanipulation; + +/** + * This class provides a method to compute the remainder + * of a number when divided by a power of two (2^n) + * without using division or modulo operations. + * + * @author Hardvan + */ +public final class ModuloPowerOfTwo { + private ModuloPowerOfTwo() { + } + + /** + * Computes the remainder of a given integer when divided by 2^n. + * + * @param x the input number + * @param n the exponent (power of two) + * @return the remainder of x divided by 2^n + */ + public static int moduloPowerOfTwo(int x, int n) { + if (n <= 0) { + throw new IllegalArgumentException("The exponent must be positive"); + } + + return x & ((1 << n) - 1); + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/NextHigherSameBitCount.java b/src/main/java/com/thealgorithms/bitmanipulation/NextHigherSameBitCount.java new file mode 100644 index 000000000000..6a764d806279 --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/NextHigherSameBitCount.java @@ -0,0 +1,30 @@ +package com.thealgorithms.bitmanipulation; + +/** + * This class provides a method to find the next higher number + * with the same number of set bits as the given number. + * + * @author Hardvan + */ +public final class NextHigherSameBitCount { + private NextHigherSameBitCount() { + } + + /** + * Finds the next higher integer with the same number of set bits. + * Steps: + * 1. Find {@code c}, the rightmost set bit of {@code n}. + * 2. Find {@code r}, the rightmost set bit of {@code n + c}. + * 3. Swap the bits of {@code r} and {@code n} to the right of {@code c}. + * 4. Shift the bits of {@code r} and {@code n} to the right of {@code c} to the rightmost. + * 5. Combine the results of steps 3 and 4. + * + * @param n the input number + * @return the next higher integer with the same set bit count + */ + public static int nextHigherSameBitCount(int n) { + int c = n & -n; + int r = n + c; + return (((r ^ n) >> 2) / c) | r; + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/NonRepeatingNumberFinder.java b/src/main/java/com/thealgorithms/bitmanipulation/NonRepeatingNumberFinder.java new file mode 100644 index 000000000000..17e1a73ec062 --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/NonRepeatingNumberFinder.java @@ -0,0 +1,35 @@ +package com.thealgorithms.bitmanipulation; + +/** + * A utility class to find the non-repeating number in an array where every other number repeats. + * This class contains a method to identify the single unique number using bit manipulation. + * + * The solution leverages the properties of the XOR operation, which states that: + * - x ^ x = 0 for any integer x (a number XORed with itself is zero) + * - x ^ 0 = x for any integer x (a number XORed with zero is the number itself) + * + * Using these properties, we can find the non-repeating number in linear time with constant space. + * + * Example: + * Given the input array [2, 3, 5, 2, 3], the output will be 5 since it does not repeat. + * + * @author Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi) + */ +public final class NonRepeatingNumberFinder { + private NonRepeatingNumberFinder() { + } + + /** + * Finds the non-repeating number in the given array. + * + * @param arr an array of integers where every number except one appears twice + * @return the integer that appears only once in the array or 0 if the array is empty + */ + public static int findNonRepeatingNumber(int[] arr) { + int result = 0; + for (int num : arr) { + result ^= num; + } + return result; + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/NumberAppearingOddTimes.java b/src/main/java/com/thealgorithms/bitmanipulation/NumberAppearingOddTimes.java new file mode 100644 index 000000000000..bd4868d4dbd5 --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/NumberAppearingOddTimes.java @@ -0,0 +1,41 @@ +package com.thealgorithms.bitmanipulation; + +/** + * This class provides a method to find the element that appears an + * odd number of times in an array. All other elements in the array + * must appear an even number of times for the logic to work. + * + * The solution uses the XOR operation, which has the following properties: + * - a ^ a = 0 (XOR-ing the same numbers cancels them out) + * - a ^ 0 = a + * - XOR is commutative and associative. + * + * Time Complexity: O(n), where n is the size of the array. + * Space Complexity: O(1), as no extra space is used. + * + * Usage Example: + * int result = NumberAppearingOddTimes.findOddOccurrence(new int[]{1, 2, 1, 2, 3}); + * // result will be 3 + * + * @author Lakshyajeet Singh Goyal (https://github.com/DarkMatter-999) + */ + +public final class NumberAppearingOddTimes { + private NumberAppearingOddTimes() { + } + + /** + * Finds the element in the array that appears an odd number of times. + * + * @param arr the input array containing integers, where all elements + * except one appear an even number of times. + * @return the integer that appears an odd number of times. + */ + public static int findOddOccurrence(int[] arr) { + int result = 0; + for (int num : arr) { + result ^= num; + } + return result; + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/NumbersDifferentSigns.java b/src/main/java/com/thealgorithms/bitmanipulation/NumbersDifferentSigns.java new file mode 100644 index 000000000000..a2da37aa81ee --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/NumbersDifferentSigns.java @@ -0,0 +1,30 @@ +package com.thealgorithms.bitmanipulation; + +/** + * This class provides a method to determine whether two integers have + * different signs. It utilizes the XOR operation on the two numbers: + * + * - If two numbers have different signs, their most significant bits + * (sign bits) will differ, resulting in a negative XOR result. + * - If two numbers have the same sign, the XOR result will be non-negative. + * + * Time Complexity: O(1) - Constant time operation. + * Space Complexity: O(1) - No extra space used. + * + * @author Bama Charan Chhandogi + */ +public final class NumbersDifferentSigns { + private NumbersDifferentSigns() { + } + + /** + * Determines if two integers have different signs using bitwise XOR. + * + * @param num1 the first integer + * @param num2 the second integer + * @return true if the two numbers have different signs, false otherwise + */ + public static boolean differentSigns(int num1, int num2) { + return (num1 ^ num2) < 0; + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/OneBitDifference.java b/src/main/java/com/thealgorithms/bitmanipulation/OneBitDifference.java new file mode 100644 index 000000000000..afec0188e299 --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/OneBitDifference.java @@ -0,0 +1,32 @@ +package com.thealgorithms.bitmanipulation; + +/** + * This class provides a method to detect if two integers + * differ by exactly one bit flip. + * + * Example: + * 1 (0001) and 2 (0010) differ by exactly one bit flip. + * 7 (0111) and 3 (0011) differ by exactly one bit flip. + * + * @author Hardvan + */ +public final class OneBitDifference { + private OneBitDifference() { + } + + /** + * Checks if two integers differ by exactly one bit. + * + * @param x the first integer + * @param y the second integer + * @return true if x and y differ by exactly one bit, false otherwise + */ + public static boolean differByOneBit(int x, int y) { + if (x == y) { + return false; + } + + int xor = x ^ y; + return (xor & (xor - 1)) == 0; + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/OnesComplement.java b/src/main/java/com/thealgorithms/bitmanipulation/OnesComplement.java new file mode 100644 index 000000000000..c5c068422113 --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/OnesComplement.java @@ -0,0 +1,28 @@ +package com.thealgorithms.bitmanipulation; + +/** + * @author - https://github.com/Monk-AbhinayVerma + * @Wikipedia - https://en.wikipedia.org/wiki/Ones%27_complement + * The class OnesComplement computes the complement of binary number + * and returns + * the complemented binary string. + * @return the complimented binary string + */ +public final class OnesComplement { + private OnesComplement() { + } + + // Function to get the 1's complement of a binary number + public static String onesComplement(String binary) { + StringBuilder complement = new StringBuilder(); + // Invert each bit to get the 1's complement + for (int i = 0; i < binary.length(); i++) { + if (binary.charAt(i) == '0') { + complement.append('1'); + } else { + complement.append('0'); + } + } + return complement.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/ParityCheck.java b/src/main/java/com/thealgorithms/bitmanipulation/ParityCheck.java new file mode 100644 index 000000000000..5acab4d4a362 --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/ParityCheck.java @@ -0,0 +1,34 @@ +package com.thealgorithms.bitmanipulation; + +/** + * The ParityCheck class provides a method to check the parity of a given number. + * <p> + * Parity is a mathematical term that describes the property of an integer's binary representation. + * The parity of a binary number is the number of 1s in its binary representation. + * If the number of 1s is even, the parity is even; otherwise, it is odd. + * <p> + * For example, the binary representation of 5 is 101, which has two 1s, so the parity of 5 is even. + * The binary representation of 6 is 110, which has two 1s, so the parity of 6 is even. + * The binary representation of 7 is 111, which has three 1s, so the parity of 7 is odd. + * + * @author Hardvan + */ +public final class ParityCheck { + private ParityCheck() { + } + + /** + * This method checks the parity of the given number. + * + * @param n the number to check the parity of + * @return true if the number has even parity, false otherwise + */ + public static boolean checkParity(int n) { + int count = 0; + while (n > 0) { + count += n & 1; + n >>= 1; + } + return count % 2 == 0; + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/ReverseBits.java b/src/main/java/com/thealgorithms/bitmanipulation/ReverseBits.java new file mode 100644 index 000000000000..12c269d9be48 --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/ReverseBits.java @@ -0,0 +1,41 @@ +package com.thealgorithms.bitmanipulation; + +/** + * This class provides a method to reverse the bits of a 32-bit integer. + * Reversing the bits means that the least significant bit (LSB) becomes + * the most significant bit (MSB) and vice versa. + * + * Example: + * Input (binary): 00000010100101000001111010011100 (43261596) + * Output (binary): 00111001011110000010100101000000 (964176192) + * + * Time Complexity: O(32) - A fixed number of 32 iterations + * Space Complexity: O(1) - No extra space used + * + * Note: + * - If the input is negative, Java handles it using two’s complement representation. + * - This function works on 32-bit integers by default. + * + * @author Bama Charan Chhandogi + */ +public final class ReverseBits { + private ReverseBits() { + } + + /** + * Reverses the bits of a 32-bit integer. + * + * @param n the integer whose bits are to be reversed + * @return the integer obtained by reversing the bits of the input + */ + public static int reverseBits(int n) { + int result = 0; + int bitCount = 32; + for (int i = 0; i < bitCount; i++) { + result <<= 1; // Left shift the result to make space for the next bit + result |= (n & 1); // OR operation to set the least significant bit of result with the current bit of n + n >>= 1; // Right shift n to move on to the next bit + } + return result; + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/SingleBitOperations.java b/src/main/java/com/thealgorithms/bitmanipulation/SingleBitOperations.java new file mode 100644 index 000000000000..624a4e2b858a --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/SingleBitOperations.java @@ -0,0 +1,68 @@ +package com.thealgorithms.bitmanipulation; + +/** + * A utility class for performing single-bit operations on integers. + * These operations include flipping, setting, clearing, and getting + * individual bits at specified positions. + * + * Bit positions are zero-indexed (i.e., the least significant bit is at position 0). + * These methods leverage bitwise operations for optimal performance. + * + * Examples: + * - `flipBit(3, 1)` flips the bit at index 1 in binary `11` (result: `1`). + * - `setBit(4, 0)` sets the bit at index 0 in `100` (result: `101` or 5). + * - `clearBit(7, 1)` clears the bit at index 1 in `111` (result: `101` or 5). + * - `getBit(6, 0)` checks if the least significant bit is set (result: `0`). + * + * Time Complexity: O(1) for all operations. + * + * Author: lukasb1b (https://github.com/lukasb1b) + */ +public final class SingleBitOperations { + private SingleBitOperations() { + } + + /** + * Flips (toggles) the bit at the specified position. + * + * @param num the input number + * @param bit the position of the bit to flip (0-indexed) + * @return the new number after flipping the specified bit + */ + public static int flipBit(final int num, final int bit) { + return num ^ (1 << bit); + } + + /** + * Sets the bit at the specified position to 1. + * + * @param num the input number + * @param bit the position of the bit to set (0-indexed) + * @return the new number after setting the specified bit to 1 + */ + public static int setBit(final int num, final int bit) { + return num | (1 << bit); + } + + /** + * Clears the bit at the specified position (sets it to 0). + * + * @param num the input number + * @param bit the position of the bit to clear (0-indexed) + * @return the new number after clearing the specified bit + */ + public static int clearBit(final int num, final int bit) { + return num & ~(1 << bit); + } + + /** + * Gets the bit value (0 or 1) at the specified position. + * + * @param num the input number + * @param bit the position of the bit to retrieve (0-indexed) + * @return 1 if the bit is set, 0 otherwise + */ + public static int getBit(final int num, final int bit) { + return (num >> bit) & 1; + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/SingleElement.java b/src/main/java/com/thealgorithms/bitmanipulation/SingleElement.java new file mode 100644 index 000000000000..85ebdf02db25 --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/SingleElement.java @@ -0,0 +1,39 @@ +package com.thealgorithms.bitmanipulation; + +/** + * Utility class to find the single non-duplicate element from an array + * where all other elements appear twice. + * <p> + * The algorithm runs in O(n) time complexity and O(1) space complexity + * using bitwise XOR. + * </p> + * + * @author <a href="http://github.com/tuhinm2002">Tuhin M</a> + */ +public final class SingleElement { + + /** + * Private constructor to prevent instantiation of this utility class. + * Throws an UnsupportedOperationException if attempted. + */ + private SingleElement() { + throw new UnsupportedOperationException("Utility Class"); + } + + /** + * Finds the single non-duplicate element in an array where every other + * element appears exactly twice. Uses bitwise XOR to achieve O(n) time + * complexity and O(1) space complexity. + * + * @param arr the input array containing integers where every element + * except one appears exactly twice + * @return the single non-duplicate element + */ + public static int findSingleElement(int[] arr) { + int ele = 0; + for (int i = 0; i < arr.length; i++) { + ele ^= arr[i]; + } + return ele; + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/SwapAdjacentBits.java b/src/main/java/com/thealgorithms/bitmanipulation/SwapAdjacentBits.java new file mode 100644 index 000000000000..98a7de8bdf1a --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/SwapAdjacentBits.java @@ -0,0 +1,57 @@ +package com.thealgorithms.bitmanipulation; + +/** + * A utility class to swap every pair of adjacent bits in a given integer. + * This operation shifts the even-positioned bits to odd positions and vice versa. + * + * Example: + * - Input: 2 (binary: `10`) → Output: 1 (binary: `01`) + * - Input: 43 (binary: `101011`) → Output: 23 (binary: `010111`) + * + * **Explanation of the Algorithm:** + * 1. Mask even-positioned bits: Using `0xAAAAAAAA` (binary: `101010...`), + * which selects bits in even positions. + * 2. Mask odd-positioned bits: Using `0x55555555` (binary: `010101...`), + * which selects bits in odd positions. + * 3. Shift bits: + * - Right-shift even-positioned bits by 1 to move them to odd positions. + * - Left-shift odd-positioned bits by 1 to move them to even positions. + * 4. Combine both shifted results using bitwise OR (`|`) to produce the final result. + * + * Use Case: This algorithm can be useful in applications involving low-level bit manipulation, + * such as encoding, data compression, or cryptographic transformations. + * + * Time Complexity: O(1) (constant time, since operations are bitwise). + * + * Author: Lakshyajeet Singh Goyal (https://github.com/DarkMatter-999) + */ +public final class SwapAdjacentBits { + private SwapAdjacentBits() { + } + + /** + * Swaps every pair of adjacent bits of a given integer. + * Steps: + * 1. Mask the even-positioned bits. + * 2. Mask the odd-positioned bits. + * 3. Shift the even bits to the right and the odd bits to the left. + * 4. Combine the shifted bits. + * + * @param num the integer whose bits are to be swapped + * @return the integer after swapping every pair of adjacent bits + */ + public static int swapAdjacentBits(int num) { + // mask the even bits (0xAAAAAAAA => 10101010...) + int evenBits = num & 0xAAAAAAAA; + + // mask the odd bits (0x55555555 => 01010101...) + int oddBits = num & 0x55555555; + + // right shift even bits and left shift odd bits + evenBits >>= 1; + oddBits <<= 1; + + // combine shifted bits + return evenBits | oddBits; + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/TwosComplement.java b/src/main/java/com/thealgorithms/bitmanipulation/TwosComplement.java new file mode 100644 index 000000000000..9b8cecd791a6 --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/TwosComplement.java @@ -0,0 +1,62 @@ +package com.thealgorithms.bitmanipulation; + +/** + * This class provides a method to compute the Two's Complement of a given binary number. + * + * <p>In two's complement representation, a binary number's negative value is obtained + * by taking the one's complement (inverting all bits) and then adding 1 to the result. + * This method handles both small and large binary strings and ensures the output is + * correct for all binary inputs, including edge cases like all zeroes and all ones. + * + * <p>For more information on Two's Complement: + * @see <a href="https://en.wikipedia.org/wiki/Two%27s_complement">Wikipedia - Two's Complement</a> + * + * <p>Algorithm originally suggested by Jon von Neumann. + * + * @author Abhinay Verma (https://github.com/Monk-AbhinayVerma) + */ +public final class TwosComplement { + private TwosComplement() { + } + + /** + * Computes the Two's Complement of the given binary string. + * Steps: + * 1. Compute the One's Complement (invert all bits). + * 2. Add 1 to the One's Complement to get the Two's Complement. + * 3. Iterate from the rightmost bit to the left, adding 1 and carrying over as needed. + * 4. If a carry is still present after the leftmost bit, prepend '1' to handle overflow. + * + * @param binary The binary number as a string (only '0' and '1' characters allowed). + * @return The two's complement of the input binary string as a new binary string. + * @throws IllegalArgumentException If the input contains non-binary characters. + */ + public static String twosComplement(String binary) { + if (!binary.matches("[01]+")) { + throw new IllegalArgumentException("Input must contain only '0' and '1'."); + } + + StringBuilder onesComplement = new StringBuilder(); + for (char bit : binary.toCharArray()) { + onesComplement.append(bit == '0' ? '1' : '0'); + } + + StringBuilder twosComplement = new StringBuilder(onesComplement); + boolean carry = true; + + for (int i = onesComplement.length() - 1; i >= 0 && carry; i--) { + if (onesComplement.charAt(i) == '1') { + twosComplement.setCharAt(i, '0'); + } else { + twosComplement.setCharAt(i, '1'); + carry = false; + } + } + + if (carry) { + twosComplement.insert(0, '1'); + } + + return twosComplement.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/bitmanipulation/Xs3Conversion.java b/src/main/java/com/thealgorithms/bitmanipulation/Xs3Conversion.java new file mode 100644 index 000000000000..b22abc0c04ff --- /dev/null +++ b/src/main/java/com/thealgorithms/bitmanipulation/Xs3Conversion.java @@ -0,0 +1,58 @@ +package com.thealgorithms.bitmanipulation; + +/** + * This class provides methods to convert between XS-3 (Excess-3) and binary. + * + * Excess-3, also called XS-3, is a binary-coded decimal (BCD) code in which each decimal digit is represented by its corresponding 4-bit binary value plus 3. + * + * For more information, refer to the + * <a href="https://en.wikipedia.org/wiki/Excess-3">Excess-3</a> Wikipedia page. + * + * <b>Example usage:</b> + * <pre> + * int binary = Xs3Conversion.xs3ToBinary(0x4567); + * System.out.println("XS-3 0x4567 to binary: " + binary); // Output: 1234 + * + * int xs3 = Xs3Conversion.binaryToXs3(1234); + * System.out.println("Binary 1234 to XS-3: " + Integer.toHexString(xs3)); // Output: 0x4567 + * </pre> + */ +public final class Xs3Conversion { + private Xs3Conversion() { + } + /** + * Converts an XS-3 (Excess-3) number to binary. + * + * @param xs3 The XS-3 number. + * @return The corresponding binary number. + */ + public static int xs3ToBinary(int xs3) { + int binary = 0; + int multiplier = 1; + while (xs3 > 0) { + int digit = (xs3 & 0xF) - 3; // Extract the last 4 bits (one XS-3 digit) and subtract 3 + binary += digit * multiplier; + multiplier *= 10; + xs3 >>= 4; // Shift right by 4 bits to process the next XS-3 digit + } + return binary; + } + + /** + * Converts a binary number to XS-3 (Excess-3). + * + * @param binary The binary number. + * @return The corresponding XS-3 number. + */ + public static int binaryToXs3(int binary) { + int xs3 = 0; + int shift = 0; + while (binary > 0) { + int digit = (binary % 10) + 3; // Extract the last decimal digit and add 3 + xs3 |= (digit << (shift * 4)); // Shift the digit to the correct XS-3 position + binary /= 10; // Remove the last decimal digit + shift++; + } + return xs3; + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/ADFGVXCipher.java b/src/main/java/com/thealgorithms/ciphers/ADFGVXCipher.java new file mode 100644 index 000000000000..d915858f9e6f --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/ADFGVXCipher.java @@ -0,0 +1,167 @@ +package com.thealgorithms.ciphers; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * The ADFGVX cipher is a fractionating transposition cipher that was used by + * the German Army during World War I. It combines a **Polybius square substitution** + * with a **columnar transposition** to enhance encryption strength. + * <p> + * The name "ADFGVX" refers to the six letters (A, D, F, G, V, X) used as row and + * column labels in the Polybius square. This cipher was designed to secure + * communication and create complex, hard-to-break ciphertexts. + * <p> + * Learn more: <a href="https://en.wikipedia.org/wiki/ADFGVX_cipher">ADFGVX Cipher - Wikipedia</a>. + * <p> + * Example usage: + * <pre> + * ADFGVXCipher cipher = new ADFGVXCipher(); + * String encrypted = cipher.encrypt("attack at 1200am", "PRIVACY"); + * String decrypted = cipher.decrypt(encrypted, "PRIVACY"); + * </pre> + * + * @author bennybebo + */ +public class ADFGVXCipher { + + // Constants used in the Polybius square + private static final char[] POLYBIUS_LETTERS = {'A', 'D', 'F', 'G', 'V', 'X'}; + private static final char[][] POLYBIUS_SQUARE = {{'N', 'A', '1', 'C', '3', 'H'}, {'8', 'T', 'B', '2', 'O', 'M'}, {'E', '5', 'W', 'R', 'P', 'D'}, {'4', 'F', '6', 'G', '7', 'I'}, {'9', 'J', '0', 'K', 'L', 'Q'}, {'S', 'U', 'V', 'X', 'Y', 'Z'}}; + + // Maps for fast substitution lookups + private static final Map<String, Character> POLYBIUS_MAP = new HashMap<>(); + private static final Map<Character, String> REVERSE_POLYBIUS_MAP = new HashMap<>(); + + // Static block to initialize the lookup tables from the Polybius square + static { + for (int i = 0; i < POLYBIUS_SQUARE.length; i++) { + for (int j = 0; j < POLYBIUS_SQUARE[i].length; j++) { + String key = "" + POLYBIUS_LETTERS[i] + POLYBIUS_LETTERS[j]; + POLYBIUS_MAP.put(key, POLYBIUS_SQUARE[i][j]); + REVERSE_POLYBIUS_MAP.put(POLYBIUS_SQUARE[i][j], key); + } + } + } + + /** + * Encrypts a given plaintext using the ADFGVX cipher with the provided keyword. + * Steps: + * 1. Substitute each letter in the plaintext with a pair of ADFGVX letters. + * 2. Perform a columnar transposition on the fractionated text using the keyword. + * + * @param plaintext The message to be encrypted (can contain letters and digits). + * @param key The keyword for columnar transposition. + * @return The encrypted message as ciphertext. + */ + public String encrypt(String plaintext, String key) { + plaintext = plaintext.toUpperCase().replaceAll("[^A-Z0-9]", ""); // Sanitize input + StringBuilder fractionatedText = new StringBuilder(); + + for (char c : plaintext.toCharArray()) { + fractionatedText.append(REVERSE_POLYBIUS_MAP.get(c)); + } + + return columnarTransposition(fractionatedText.toString(), key); + } + + /** + * Decrypts a given ciphertext using the ADFGVX cipher with the provided keyword. + * Steps: + * 1. Reverse the columnar transposition performed during encryption. + * 2. Substitute each pair of ADFGVX letters with the corresponding plaintext letter. + * The resulting text is the decrypted message. + * + * @param ciphertext The encrypted message. + * @param key The keyword used during encryption. + * @return The decrypted plaintext message. + */ + public String decrypt(String ciphertext, String key) { + String fractionatedText = reverseColumnarTransposition(ciphertext, key); + + StringBuilder plaintext = new StringBuilder(); + for (int i = 0; i < fractionatedText.length(); i += 2) { + String pair = fractionatedText.substring(i, i + 2); + plaintext.append(POLYBIUS_MAP.get(pair)); + } + + return plaintext.toString(); + } + + /** + * Helper method: Performs columnar transposition during encryption + * + * @param text The fractionated text to be transposed + * @param key The keyword for columnar transposition + * @return The transposed text + */ + private String columnarTransposition(String text, String key) { + int numRows = (int) Math.ceil((double) text.length() / key.length()); + char[][] table = new char[numRows][key.length()]; + for (char[] row : table) { // Fill empty cells with underscores + Arrays.fill(row, '_'); + } + + // Populate the table row by row + for (int i = 0; i < text.length(); i++) { + table[i / key.length()][i % key.length()] = text.charAt(i); + } + + // Read columns based on the alphabetical order of the key + StringBuilder ciphertext = new StringBuilder(); + char[] sortedKey = key.toCharArray(); + Arrays.sort(sortedKey); + + for (char keyChar : sortedKey) { + int column = key.indexOf(keyChar); + for (char[] row : table) { + if (row[column] != '_') { + ciphertext.append(row[column]); + } + } + } + + return ciphertext.toString(); + } + + /** + * Helper method: Reverses the columnar transposition during decryption + * + * @param ciphertext The transposed text to be reversed + * @param key The keyword used during encryption + * @return The reversed text + */ + private String reverseColumnarTransposition(String ciphertext, String key) { + int numRows = (int) Math.ceil((double) ciphertext.length() / key.length()); + char[][] table = new char[numRows][key.length()]; + + char[] sortedKey = key.toCharArray(); + Arrays.sort(sortedKey); + + int index = 0; + // Populate the table column by column according to the sorted key + for (char keyChar : sortedKey) { + int column = key.indexOf(keyChar); + for (int row = 0; row < numRows; row++) { + if (index < ciphertext.length()) { + table[row][column] = ciphertext.charAt(index++); + } else { + table[row][column] = '_'; + } + } + } + + // Read the table row by row to reconstruct the fractionated text + StringBuilder fractionatedText = new StringBuilder(); + for (char[] row : table) { + for (char cell : row) { + if (cell != '_') { + fractionatedText.append(cell); + } + } + } + + return fractionatedText.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/AES.java b/src/main/java/com/thealgorithms/ciphers/AES.java new file mode 100644 index 000000000000..1c283f6b7655 --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/AES.java @@ -0,0 +1,2781 @@ +package com.thealgorithms.ciphers; + +import java.math.BigInteger; +import java.util.Scanner; + +/** + * This class is build to demonstrate the application of the AES-algorithm on a + * single 128-Bit block of data. + */ +public final class AES { + private AES() { + } + + /** + * Precalculated values for x to the power of 2 in Rijndaels galois field. + * Used as 'RCON' during the key expansion. + */ + private static final int[] RCON = { + 0x8d, + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80, + 0x1b, + 0x36, + 0x6c, + 0xd8, + 0xab, + 0x4d, + 0x9a, + 0x2f, + 0x5e, + 0xbc, + 0x63, + 0xc6, + 0x97, + 0x35, + 0x6a, + 0xd4, + 0xb3, + 0x7d, + 0xfa, + 0xef, + 0xc5, + 0x91, + 0x39, + 0x72, + 0xe4, + 0xd3, + 0xbd, + 0x61, + 0xc2, + 0x9f, + 0x25, + 0x4a, + 0x94, + 0x33, + 0x66, + 0xcc, + 0x83, + 0x1d, + 0x3a, + 0x74, + 0xe8, + 0xcb, + 0x8d, + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80, + 0x1b, + 0x36, + 0x6c, + 0xd8, + 0xab, + 0x4d, + 0x9a, + 0x2f, + 0x5e, + 0xbc, + 0x63, + 0xc6, + 0x97, + 0x35, + 0x6a, + 0xd4, + 0xb3, + 0x7d, + 0xfa, + 0xef, + 0xc5, + 0x91, + 0x39, + 0x72, + 0xe4, + 0xd3, + 0xbd, + 0x61, + 0xc2, + 0x9f, + 0x25, + 0x4a, + 0x94, + 0x33, + 0x66, + 0xcc, + 0x83, + 0x1d, + 0x3a, + 0x74, + 0xe8, + 0xcb, + 0x8d, + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80, + 0x1b, + 0x36, + 0x6c, + 0xd8, + 0xab, + 0x4d, + 0x9a, + 0x2f, + 0x5e, + 0xbc, + 0x63, + 0xc6, + 0x97, + 0x35, + 0x6a, + 0xd4, + 0xb3, + 0x7d, + 0xfa, + 0xef, + 0xc5, + 0x91, + 0x39, + 0x72, + 0xe4, + 0xd3, + 0xbd, + 0x61, + 0xc2, + 0x9f, + 0x25, + 0x4a, + 0x94, + 0x33, + 0x66, + 0xcc, + 0x83, + 0x1d, + 0x3a, + 0x74, + 0xe8, + 0xcb, + 0x8d, + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80, + 0x1b, + 0x36, + 0x6c, + 0xd8, + 0xab, + 0x4d, + 0x9a, + 0x2f, + 0x5e, + 0xbc, + 0x63, + 0xc6, + 0x97, + 0x35, + 0x6a, + 0xd4, + 0xb3, + 0x7d, + 0xfa, + 0xef, + 0xc5, + 0x91, + 0x39, + 0x72, + 0xe4, + 0xd3, + 0xbd, + 0x61, + 0xc2, + 0x9f, + 0x25, + 0x4a, + 0x94, + 0x33, + 0x66, + 0xcc, + 0x83, + 0x1d, + 0x3a, + 0x74, + 0xe8, + 0xcb, + 0x8d, + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80, + 0x1b, + 0x36, + 0x6c, + 0xd8, + 0xab, + 0x4d, + 0x9a, + 0x2f, + 0x5e, + 0xbc, + 0x63, + 0xc6, + 0x97, + 0x35, + 0x6a, + 0xd4, + 0xb3, + 0x7d, + 0xfa, + 0xef, + 0xc5, + 0x91, + 0x39, + 0x72, + 0xe4, + 0xd3, + 0xbd, + 0x61, + 0xc2, + 0x9f, + 0x25, + 0x4a, + 0x94, + 0x33, + 0x66, + 0xcc, + 0x83, + 0x1d, + 0x3a, + 0x74, + 0xe8, + 0xcb, + 0x8d, + }; + + /** + * Rijndael S-box Substitution table used for encryption in the subBytes + * step, as well as the key expansion. + */ + private static final int[] SBOX = { + 0x63, + 0x7C, + 0x77, + 0x7B, + 0xF2, + 0x6B, + 0x6F, + 0xC5, + 0x30, + 0x01, + 0x67, + 0x2B, + 0xFE, + 0xD7, + 0xAB, + 0x76, + 0xCA, + 0x82, + 0xC9, + 0x7D, + 0xFA, + 0x59, + 0x47, + 0xF0, + 0xAD, + 0xD4, + 0xA2, + 0xAF, + 0x9C, + 0xA4, + 0x72, + 0xC0, + 0xB7, + 0xFD, + 0x93, + 0x26, + 0x36, + 0x3F, + 0xF7, + 0xCC, + 0x34, + 0xA5, + 0xE5, + 0xF1, + 0x71, + 0xD8, + 0x31, + 0x15, + 0x04, + 0xC7, + 0x23, + 0xC3, + 0x18, + 0x96, + 0x05, + 0x9A, + 0x07, + 0x12, + 0x80, + 0xE2, + 0xEB, + 0x27, + 0xB2, + 0x75, + 0x09, + 0x83, + 0x2C, + 0x1A, + 0x1B, + 0x6E, + 0x5A, + 0xA0, + 0x52, + 0x3B, + 0xD6, + 0xB3, + 0x29, + 0xE3, + 0x2F, + 0x84, + 0x53, + 0xD1, + 0x00, + 0xED, + 0x20, + 0xFC, + 0xB1, + 0x5B, + 0x6A, + 0xCB, + 0xBE, + 0x39, + 0x4A, + 0x4C, + 0x58, + 0xCF, + 0xD0, + 0xEF, + 0xAA, + 0xFB, + 0x43, + 0x4D, + 0x33, + 0x85, + 0x45, + 0xF9, + 0x02, + 0x7F, + 0x50, + 0x3C, + 0x9F, + 0xA8, + 0x51, + 0xA3, + 0x40, + 0x8F, + 0x92, + 0x9D, + 0x38, + 0xF5, + 0xBC, + 0xB6, + 0xDA, + 0x21, + 0x10, + 0xFF, + 0xF3, + 0xD2, + 0xCD, + 0x0C, + 0x13, + 0xEC, + 0x5F, + 0x97, + 0x44, + 0x17, + 0xC4, + 0xA7, + 0x7E, + 0x3D, + 0x64, + 0x5D, + 0x19, + 0x73, + 0x60, + 0x81, + 0x4F, + 0xDC, + 0x22, + 0x2A, + 0x90, + 0x88, + 0x46, + 0xEE, + 0xB8, + 0x14, + 0xDE, + 0x5E, + 0x0B, + 0xDB, + 0xE0, + 0x32, + 0x3A, + 0x0A, + 0x49, + 0x06, + 0x24, + 0x5C, + 0xC2, + 0xD3, + 0xAC, + 0x62, + 0x91, + 0x95, + 0xE4, + 0x79, + 0xE7, + 0xC8, + 0x37, + 0x6D, + 0x8D, + 0xD5, + 0x4E, + 0xA9, + 0x6C, + 0x56, + 0xF4, + 0xEA, + 0x65, + 0x7A, + 0xAE, + 0x08, + 0xBA, + 0x78, + 0x25, + 0x2E, + 0x1C, + 0xA6, + 0xB4, + 0xC6, + 0xE8, + 0xDD, + 0x74, + 0x1F, + 0x4B, + 0xBD, + 0x8B, + 0x8A, + 0x70, + 0x3E, + 0xB5, + 0x66, + 0x48, + 0x03, + 0xF6, + 0x0E, + 0x61, + 0x35, + 0x57, + 0xB9, + 0x86, + 0xC1, + 0x1D, + 0x9E, + 0xE1, + 0xF8, + 0x98, + 0x11, + 0x69, + 0xD9, + 0x8E, + 0x94, + 0x9B, + 0x1E, + 0x87, + 0xE9, + 0xCE, + 0x55, + 0x28, + 0xDF, + 0x8C, + 0xA1, + 0x89, + 0x0D, + 0xBF, + 0xE6, + 0x42, + 0x68, + 0x41, + 0x99, + 0x2D, + 0x0F, + 0xB0, + 0x54, + 0xBB, + 0x16, + }; + + /** + * Inverse Rijndael S-box Substitution table used for decryption in the + * subBytesDec step. + */ + private static final int[] INVERSE_SBOX = { + 0x52, + 0x09, + 0x6A, + 0xD5, + 0x30, + 0x36, + 0xA5, + 0x38, + 0xBF, + 0x40, + 0xA3, + 0x9E, + 0x81, + 0xF3, + 0xD7, + 0xFB, + 0x7C, + 0xE3, + 0x39, + 0x82, + 0x9B, + 0x2F, + 0xFF, + 0x87, + 0x34, + 0x8E, + 0x43, + 0x44, + 0xC4, + 0xDE, + 0xE9, + 0xCB, + 0x54, + 0x7B, + 0x94, + 0x32, + 0xA6, + 0xC2, + 0x23, + 0x3D, + 0xEE, + 0x4C, + 0x95, + 0x0B, + 0x42, + 0xFA, + 0xC3, + 0x4E, + 0x08, + 0x2E, + 0xA1, + 0x66, + 0x28, + 0xD9, + 0x24, + 0xB2, + 0x76, + 0x5B, + 0xA2, + 0x49, + 0x6D, + 0x8B, + 0xD1, + 0x25, + 0x72, + 0xF8, + 0xF6, + 0x64, + 0x86, + 0x68, + 0x98, + 0x16, + 0xD4, + 0xA4, + 0x5C, + 0xCC, + 0x5D, + 0x65, + 0xB6, + 0x92, + 0x6C, + 0x70, + 0x48, + 0x50, + 0xFD, + 0xED, + 0xB9, + 0xDA, + 0x5E, + 0x15, + 0x46, + 0x57, + 0xA7, + 0x8D, + 0x9D, + 0x84, + 0x90, + 0xD8, + 0xAB, + 0x00, + 0x8C, + 0xBC, + 0xD3, + 0x0A, + 0xF7, + 0xE4, + 0x58, + 0x05, + 0xB8, + 0xB3, + 0x45, + 0x06, + 0xD0, + 0x2C, + 0x1E, + 0x8F, + 0xCA, + 0x3F, + 0x0F, + 0x02, + 0xC1, + 0xAF, + 0xBD, + 0x03, + 0x01, + 0x13, + 0x8A, + 0x6B, + 0x3A, + 0x91, + 0x11, + 0x41, + 0x4F, + 0x67, + 0xDC, + 0xEA, + 0x97, + 0xF2, + 0xCF, + 0xCE, + 0xF0, + 0xB4, + 0xE6, + 0x73, + 0x96, + 0xAC, + 0x74, + 0x22, + 0xE7, + 0xAD, + 0x35, + 0x85, + 0xE2, + 0xF9, + 0x37, + 0xE8, + 0x1C, + 0x75, + 0xDF, + 0x6E, + 0x47, + 0xF1, + 0x1A, + 0x71, + 0x1D, + 0x29, + 0xC5, + 0x89, + 0x6F, + 0xB7, + 0x62, + 0x0E, + 0xAA, + 0x18, + 0xBE, + 0x1B, + 0xFC, + 0x56, + 0x3E, + 0x4B, + 0xC6, + 0xD2, + 0x79, + 0x20, + 0x9A, + 0xDB, + 0xC0, + 0xFE, + 0x78, + 0xCD, + 0x5A, + 0xF4, + 0x1F, + 0xDD, + 0xA8, + 0x33, + 0x88, + 0x07, + 0xC7, + 0x31, + 0xB1, + 0x12, + 0x10, + 0x59, + 0x27, + 0x80, + 0xEC, + 0x5F, + 0x60, + 0x51, + 0x7F, + 0xA9, + 0x19, + 0xB5, + 0x4A, + 0x0D, + 0x2D, + 0xE5, + 0x7A, + 0x9F, + 0x93, + 0xC9, + 0x9C, + 0xEF, + 0xA0, + 0xE0, + 0x3B, + 0x4D, + 0xAE, + 0x2A, + 0xF5, + 0xB0, + 0xC8, + 0xEB, + 0xBB, + 0x3C, + 0x83, + 0x53, + 0x99, + 0x61, + 0x17, + 0x2B, + 0x04, + 0x7E, + 0xBA, + 0x77, + 0xD6, + 0x26, + 0xE1, + 0x69, + 0x14, + 0x63, + 0x55, + 0x21, + 0x0C, + 0x7D, + }; + + /** + * Precalculated lookup table for galois field multiplication by 2 used in + * the MixColums step during encryption. + */ + private static final int[] MULT2 = { + 0x00, + 0x02, + 0x04, + 0x06, + 0x08, + 0x0a, + 0x0c, + 0x0e, + 0x10, + 0x12, + 0x14, + 0x16, + 0x18, + 0x1a, + 0x1c, + 0x1e, + 0x20, + 0x22, + 0x24, + 0x26, + 0x28, + 0x2a, + 0x2c, + 0x2e, + 0x30, + 0x32, + 0x34, + 0x36, + 0x38, + 0x3a, + 0x3c, + 0x3e, + 0x40, + 0x42, + 0x44, + 0x46, + 0x48, + 0x4a, + 0x4c, + 0x4e, + 0x50, + 0x52, + 0x54, + 0x56, + 0x58, + 0x5a, + 0x5c, + 0x5e, + 0x60, + 0x62, + 0x64, + 0x66, + 0x68, + 0x6a, + 0x6c, + 0x6e, + 0x70, + 0x72, + 0x74, + 0x76, + 0x78, + 0x7a, + 0x7c, + 0x7e, + 0x80, + 0x82, + 0x84, + 0x86, + 0x88, + 0x8a, + 0x8c, + 0x8e, + 0x90, + 0x92, + 0x94, + 0x96, + 0x98, + 0x9a, + 0x9c, + 0x9e, + 0xa0, + 0xa2, + 0xa4, + 0xa6, + 0xa8, + 0xaa, + 0xac, + 0xae, + 0xb0, + 0xb2, + 0xb4, + 0xb6, + 0xb8, + 0xba, + 0xbc, + 0xbe, + 0xc0, + 0xc2, + 0xc4, + 0xc6, + 0xc8, + 0xca, + 0xcc, + 0xce, + 0xd0, + 0xd2, + 0xd4, + 0xd6, + 0xd8, + 0xda, + 0xdc, + 0xde, + 0xe0, + 0xe2, + 0xe4, + 0xe6, + 0xe8, + 0xea, + 0xec, + 0xee, + 0xf0, + 0xf2, + 0xf4, + 0xf6, + 0xf8, + 0xfa, + 0xfc, + 0xfe, + 0x1b, + 0x19, + 0x1f, + 0x1d, + 0x13, + 0x11, + 0x17, + 0x15, + 0x0b, + 0x09, + 0x0f, + 0x0d, + 0x03, + 0x01, + 0x07, + 0x05, + 0x3b, + 0x39, + 0x3f, + 0x3d, + 0x33, + 0x31, + 0x37, + 0x35, + 0x2b, + 0x29, + 0x2f, + 0x2d, + 0x23, + 0x21, + 0x27, + 0x25, + 0x5b, + 0x59, + 0x5f, + 0x5d, + 0x53, + 0x51, + 0x57, + 0x55, + 0x4b, + 0x49, + 0x4f, + 0x4d, + 0x43, + 0x41, + 0x47, + 0x45, + 0x7b, + 0x79, + 0x7f, + 0x7d, + 0x73, + 0x71, + 0x77, + 0x75, + 0x6b, + 0x69, + 0x6f, + 0x6d, + 0x63, + 0x61, + 0x67, + 0x65, + 0x9b, + 0x99, + 0x9f, + 0x9d, + 0x93, + 0x91, + 0x97, + 0x95, + 0x8b, + 0x89, + 0x8f, + 0x8d, + 0x83, + 0x81, + 0x87, + 0x85, + 0xbb, + 0xb9, + 0xbf, + 0xbd, + 0xb3, + 0xb1, + 0xb7, + 0xb5, + 0xab, + 0xa9, + 0xaf, + 0xad, + 0xa3, + 0xa1, + 0xa7, + 0xa5, + 0xdb, + 0xd9, + 0xdf, + 0xdd, + 0xd3, + 0xd1, + 0xd7, + 0xd5, + 0xcb, + 0xc9, + 0xcf, + 0xcd, + 0xc3, + 0xc1, + 0xc7, + 0xc5, + 0xfb, + 0xf9, + 0xff, + 0xfd, + 0xf3, + 0xf1, + 0xf7, + 0xf5, + 0xeb, + 0xe9, + 0xef, + 0xed, + 0xe3, + 0xe1, + 0xe7, + 0xe5, + }; + + /** + * Precalculated lookup table for galois field multiplication by 3 used in + * the MixColums step during encryption. + */ + private static final int[] MULT3 = { + 0x00, + 0x03, + 0x06, + 0x05, + 0x0c, + 0x0f, + 0x0a, + 0x09, + 0x18, + 0x1b, + 0x1e, + 0x1d, + 0x14, + 0x17, + 0x12, + 0x11, + 0x30, + 0x33, + 0x36, + 0x35, + 0x3c, + 0x3f, + 0x3a, + 0x39, + 0x28, + 0x2b, + 0x2e, + 0x2d, + 0x24, + 0x27, + 0x22, + 0x21, + 0x60, + 0x63, + 0x66, + 0x65, + 0x6c, + 0x6f, + 0x6a, + 0x69, + 0x78, + 0x7b, + 0x7e, + 0x7d, + 0x74, + 0x77, + 0x72, + 0x71, + 0x50, + 0x53, + 0x56, + 0x55, + 0x5c, + 0x5f, + 0x5a, + 0x59, + 0x48, + 0x4b, + 0x4e, + 0x4d, + 0x44, + 0x47, + 0x42, + 0x41, + 0xc0, + 0xc3, + 0xc6, + 0xc5, + 0xcc, + 0xcf, + 0xca, + 0xc9, + 0xd8, + 0xdb, + 0xde, + 0xdd, + 0xd4, + 0xd7, + 0xd2, + 0xd1, + 0xf0, + 0xf3, + 0xf6, + 0xf5, + 0xfc, + 0xff, + 0xfa, + 0xf9, + 0xe8, + 0xeb, + 0xee, + 0xed, + 0xe4, + 0xe7, + 0xe2, + 0xe1, + 0xa0, + 0xa3, + 0xa6, + 0xa5, + 0xac, + 0xaf, + 0xaa, + 0xa9, + 0xb8, + 0xbb, + 0xbe, + 0xbd, + 0xb4, + 0xb7, + 0xb2, + 0xb1, + 0x90, + 0x93, + 0x96, + 0x95, + 0x9c, + 0x9f, + 0x9a, + 0x99, + 0x88, + 0x8b, + 0x8e, + 0x8d, + 0x84, + 0x87, + 0x82, + 0x81, + 0x9b, + 0x98, + 0x9d, + 0x9e, + 0x97, + 0x94, + 0x91, + 0x92, + 0x83, + 0x80, + 0x85, + 0x86, + 0x8f, + 0x8c, + 0x89, + 0x8a, + 0xab, + 0xa8, + 0xad, + 0xae, + 0xa7, + 0xa4, + 0xa1, + 0xa2, + 0xb3, + 0xb0, + 0xb5, + 0xb6, + 0xbf, + 0xbc, + 0xb9, + 0xba, + 0xfb, + 0xf8, + 0xfd, + 0xfe, + 0xf7, + 0xf4, + 0xf1, + 0xf2, + 0xe3, + 0xe0, + 0xe5, + 0xe6, + 0xef, + 0xec, + 0xe9, + 0xea, + 0xcb, + 0xc8, + 0xcd, + 0xce, + 0xc7, + 0xc4, + 0xc1, + 0xc2, + 0xd3, + 0xd0, + 0xd5, + 0xd6, + 0xdf, + 0xdc, + 0xd9, + 0xda, + 0x5b, + 0x58, + 0x5d, + 0x5e, + 0x57, + 0x54, + 0x51, + 0x52, + 0x43, + 0x40, + 0x45, + 0x46, + 0x4f, + 0x4c, + 0x49, + 0x4a, + 0x6b, + 0x68, + 0x6d, + 0x6e, + 0x67, + 0x64, + 0x61, + 0x62, + 0x73, + 0x70, + 0x75, + 0x76, + 0x7f, + 0x7c, + 0x79, + 0x7a, + 0x3b, + 0x38, + 0x3d, + 0x3e, + 0x37, + 0x34, + 0x31, + 0x32, + 0x23, + 0x20, + 0x25, + 0x26, + 0x2f, + 0x2c, + 0x29, + 0x2a, + 0x0b, + 0x08, + 0x0d, + 0x0e, + 0x07, + 0x04, + 0x01, + 0x02, + 0x13, + 0x10, + 0x15, + 0x16, + 0x1f, + 0x1c, + 0x19, + 0x1a, + }; + + /** + * Precalculated lookup table for galois field multiplication by 9 used in + * the MixColums step during decryption. + */ + private static final int[] MULT9 = { + 0x00, + 0x09, + 0x12, + 0x1b, + 0x24, + 0x2d, + 0x36, + 0x3f, + 0x48, + 0x41, + 0x5a, + 0x53, + 0x6c, + 0x65, + 0x7e, + 0x77, + 0x90, + 0x99, + 0x82, + 0x8b, + 0xb4, + 0xbd, + 0xa6, + 0xaf, + 0xd8, + 0xd1, + 0xca, + 0xc3, + 0xfc, + 0xf5, + 0xee, + 0xe7, + 0x3b, + 0x32, + 0x29, + 0x20, + 0x1f, + 0x16, + 0x0d, + 0x04, + 0x73, + 0x7a, + 0x61, + 0x68, + 0x57, + 0x5e, + 0x45, + 0x4c, + 0xab, + 0xa2, + 0xb9, + 0xb0, + 0x8f, + 0x86, + 0x9d, + 0x94, + 0xe3, + 0xea, + 0xf1, + 0xf8, + 0xc7, + 0xce, + 0xd5, + 0xdc, + 0x76, + 0x7f, + 0x64, + 0x6d, + 0x52, + 0x5b, + 0x40, + 0x49, + 0x3e, + 0x37, + 0x2c, + 0x25, + 0x1a, + 0x13, + 0x08, + 0x01, + 0xe6, + 0xef, + 0xf4, + 0xfd, + 0xc2, + 0xcb, + 0xd0, + 0xd9, + 0xae, + 0xa7, + 0xbc, + 0xb5, + 0x8a, + 0x83, + 0x98, + 0x91, + 0x4d, + 0x44, + 0x5f, + 0x56, + 0x69, + 0x60, + 0x7b, + 0x72, + 0x05, + 0x0c, + 0x17, + 0x1e, + 0x21, + 0x28, + 0x33, + 0x3a, + 0xdd, + 0xd4, + 0xcf, + 0xc6, + 0xf9, + 0xf0, + 0xeb, + 0xe2, + 0x95, + 0x9c, + 0x87, + 0x8e, + 0xb1, + 0xb8, + 0xa3, + 0xaa, + 0xec, + 0xe5, + 0xfe, + 0xf7, + 0xc8, + 0xc1, + 0xda, + 0xd3, + 0xa4, + 0xad, + 0xb6, + 0xbf, + 0x80, + 0x89, + 0x92, + 0x9b, + 0x7c, + 0x75, + 0x6e, + 0x67, + 0x58, + 0x51, + 0x4a, + 0x43, + 0x34, + 0x3d, + 0x26, + 0x2f, + 0x10, + 0x19, + 0x02, + 0x0b, + 0xd7, + 0xde, + 0xc5, + 0xcc, + 0xf3, + 0xfa, + 0xe1, + 0xe8, + 0x9f, + 0x96, + 0x8d, + 0x84, + 0xbb, + 0xb2, + 0xa9, + 0xa0, + 0x47, + 0x4e, + 0x55, + 0x5c, + 0x63, + 0x6a, + 0x71, + 0x78, + 0x0f, + 0x06, + 0x1d, + 0x14, + 0x2b, + 0x22, + 0x39, + 0x30, + 0x9a, + 0x93, + 0x88, + 0x81, + 0xbe, + 0xb7, + 0xac, + 0xa5, + 0xd2, + 0xdb, + 0xc0, + 0xc9, + 0xf6, + 0xff, + 0xe4, + 0xed, + 0x0a, + 0x03, + 0x18, + 0x11, + 0x2e, + 0x27, + 0x3c, + 0x35, + 0x42, + 0x4b, + 0x50, + 0x59, + 0x66, + 0x6f, + 0x74, + 0x7d, + 0xa1, + 0xa8, + 0xb3, + 0xba, + 0x85, + 0x8c, + 0x97, + 0x9e, + 0xe9, + 0xe0, + 0xfb, + 0xf2, + 0xcd, + 0xc4, + 0xdf, + 0xd6, + 0x31, + 0x38, + 0x23, + 0x2a, + 0x15, + 0x1c, + 0x07, + 0x0e, + 0x79, + 0x70, + 0x6b, + 0x62, + 0x5d, + 0x54, + 0x4f, + 0x46, + }; + + /** + * Precalculated lookup table for galois field multiplication by 11 used in + * the MixColums step during decryption. + */ + private static final int[] MULT11 = { + 0x00, + 0x0b, + 0x16, + 0x1d, + 0x2c, + 0x27, + 0x3a, + 0x31, + 0x58, + 0x53, + 0x4e, + 0x45, + 0x74, + 0x7f, + 0x62, + 0x69, + 0xb0, + 0xbb, + 0xa6, + 0xad, + 0x9c, + 0x97, + 0x8a, + 0x81, + 0xe8, + 0xe3, + 0xfe, + 0xf5, + 0xc4, + 0xcf, + 0xd2, + 0xd9, + 0x7b, + 0x70, + 0x6d, + 0x66, + 0x57, + 0x5c, + 0x41, + 0x4a, + 0x23, + 0x28, + 0x35, + 0x3e, + 0x0f, + 0x04, + 0x19, + 0x12, + 0xcb, + 0xc0, + 0xdd, + 0xd6, + 0xe7, + 0xec, + 0xf1, + 0xfa, + 0x93, + 0x98, + 0x85, + 0x8e, + 0xbf, + 0xb4, + 0xa9, + 0xa2, + 0xf6, + 0xfd, + 0xe0, + 0xeb, + 0xda, + 0xd1, + 0xcc, + 0xc7, + 0xae, + 0xa5, + 0xb8, + 0xb3, + 0x82, + 0x89, + 0x94, + 0x9f, + 0x46, + 0x4d, + 0x50, + 0x5b, + 0x6a, + 0x61, + 0x7c, + 0x77, + 0x1e, + 0x15, + 0x08, + 0x03, + 0x32, + 0x39, + 0x24, + 0x2f, + 0x8d, + 0x86, + 0x9b, + 0x90, + 0xa1, + 0xaa, + 0xb7, + 0xbc, + 0xd5, + 0xde, + 0xc3, + 0xc8, + 0xf9, + 0xf2, + 0xef, + 0xe4, + 0x3d, + 0x36, + 0x2b, + 0x20, + 0x11, + 0x1a, + 0x07, + 0x0c, + 0x65, + 0x6e, + 0x73, + 0x78, + 0x49, + 0x42, + 0x5f, + 0x54, + 0xf7, + 0xfc, + 0xe1, + 0xea, + 0xdb, + 0xd0, + 0xcd, + 0xc6, + 0xaf, + 0xa4, + 0xb9, + 0xb2, + 0x83, + 0x88, + 0x95, + 0x9e, + 0x47, + 0x4c, + 0x51, + 0x5a, + 0x6b, + 0x60, + 0x7d, + 0x76, + 0x1f, + 0x14, + 0x09, + 0x02, + 0x33, + 0x38, + 0x25, + 0x2e, + 0x8c, + 0x87, + 0x9a, + 0x91, + 0xa0, + 0xab, + 0xb6, + 0xbd, + 0xd4, + 0xdf, + 0xc2, + 0xc9, + 0xf8, + 0xf3, + 0xee, + 0xe5, + 0x3c, + 0x37, + 0x2a, + 0x21, + 0x10, + 0x1b, + 0x06, + 0x0d, + 0x64, + 0x6f, + 0x72, + 0x79, + 0x48, + 0x43, + 0x5e, + 0x55, + 0x01, + 0x0a, + 0x17, + 0x1c, + 0x2d, + 0x26, + 0x3b, + 0x30, + 0x59, + 0x52, + 0x4f, + 0x44, + 0x75, + 0x7e, + 0x63, + 0x68, + 0xb1, + 0xba, + 0xa7, + 0xac, + 0x9d, + 0x96, + 0x8b, + 0x80, + 0xe9, + 0xe2, + 0xff, + 0xf4, + 0xc5, + 0xce, + 0xd3, + 0xd8, + 0x7a, + 0x71, + 0x6c, + 0x67, + 0x56, + 0x5d, + 0x40, + 0x4b, + 0x22, + 0x29, + 0x34, + 0x3f, + 0x0e, + 0x05, + 0x18, + 0x13, + 0xca, + 0xc1, + 0xdc, + 0xd7, + 0xe6, + 0xed, + 0xf0, + 0xfb, + 0x92, + 0x99, + 0x84, + 0x8f, + 0xbe, + 0xb5, + 0xa8, + 0xa3, + }; + + /** + * Precalculated lookup table for galois field multiplication by 13 used in + * the MixColums step during decryption. + */ + private static final int[] MULT13 = { + 0x00, + 0x0d, + 0x1a, + 0x17, + 0x34, + 0x39, + 0x2e, + 0x23, + 0x68, + 0x65, + 0x72, + 0x7f, + 0x5c, + 0x51, + 0x46, + 0x4b, + 0xd0, + 0xdd, + 0xca, + 0xc7, + 0xe4, + 0xe9, + 0xfe, + 0xf3, + 0xb8, + 0xb5, + 0xa2, + 0xaf, + 0x8c, + 0x81, + 0x96, + 0x9b, + 0xbb, + 0xb6, + 0xa1, + 0xac, + 0x8f, + 0x82, + 0x95, + 0x98, + 0xd3, + 0xde, + 0xc9, + 0xc4, + 0xe7, + 0xea, + 0xfd, + 0xf0, + 0x6b, + 0x66, + 0x71, + 0x7c, + 0x5f, + 0x52, + 0x45, + 0x48, + 0x03, + 0x0e, + 0x19, + 0x14, + 0x37, + 0x3a, + 0x2d, + 0x20, + 0x6d, + 0x60, + 0x77, + 0x7a, + 0x59, + 0x54, + 0x43, + 0x4e, + 0x05, + 0x08, + 0x1f, + 0x12, + 0x31, + 0x3c, + 0x2b, + 0x26, + 0xbd, + 0xb0, + 0xa7, + 0xaa, + 0x89, + 0x84, + 0x93, + 0x9e, + 0xd5, + 0xd8, + 0xcf, + 0xc2, + 0xe1, + 0xec, + 0xfb, + 0xf6, + 0xd6, + 0xdb, + 0xcc, + 0xc1, + 0xe2, + 0xef, + 0xf8, + 0xf5, + 0xbe, + 0xb3, + 0xa4, + 0xa9, + 0x8a, + 0x87, + 0x90, + 0x9d, + 0x06, + 0x0b, + 0x1c, + 0x11, + 0x32, + 0x3f, + 0x28, + 0x25, + 0x6e, + 0x63, + 0x74, + 0x79, + 0x5a, + 0x57, + 0x40, + 0x4d, + 0xda, + 0xd7, + 0xc0, + 0xcd, + 0xee, + 0xe3, + 0xf4, + 0xf9, + 0xb2, + 0xbf, + 0xa8, + 0xa5, + 0x86, + 0x8b, + 0x9c, + 0x91, + 0x0a, + 0x07, + 0x10, + 0x1d, + 0x3e, + 0x33, + 0x24, + 0x29, + 0x62, + 0x6f, + 0x78, + 0x75, + 0x56, + 0x5b, + 0x4c, + 0x41, + 0x61, + 0x6c, + 0x7b, + 0x76, + 0x55, + 0x58, + 0x4f, + 0x42, + 0x09, + 0x04, + 0x13, + 0x1e, + 0x3d, + 0x30, + 0x27, + 0x2a, + 0xb1, + 0xbc, + 0xab, + 0xa6, + 0x85, + 0x88, + 0x9f, + 0x92, + 0xd9, + 0xd4, + 0xc3, + 0xce, + 0xed, + 0xe0, + 0xf7, + 0xfa, + 0xb7, + 0xba, + 0xad, + 0xa0, + 0x83, + 0x8e, + 0x99, + 0x94, + 0xdf, + 0xd2, + 0xc5, + 0xc8, + 0xeb, + 0xe6, + 0xf1, + 0xfc, + 0x67, + 0x6a, + 0x7d, + 0x70, + 0x53, + 0x5e, + 0x49, + 0x44, + 0x0f, + 0x02, + 0x15, + 0x18, + 0x3b, + 0x36, + 0x21, + 0x2c, + 0x0c, + 0x01, + 0x16, + 0x1b, + 0x38, + 0x35, + 0x22, + 0x2f, + 0x64, + 0x69, + 0x7e, + 0x73, + 0x50, + 0x5d, + 0x4a, + 0x47, + 0xdc, + 0xd1, + 0xc6, + 0xcb, + 0xe8, + 0xe5, + 0xf2, + 0xff, + 0xb4, + 0xb9, + 0xae, + 0xa3, + 0x80, + 0x8d, + 0x9a, + 0x97, + }; + + /** + * Precalculated lookup table for galois field multiplication by 14 used in + * the MixColums step during decryption. + */ + private static final int[] MULT14 = { + 0x00, + 0x0e, + 0x1c, + 0x12, + 0x38, + 0x36, + 0x24, + 0x2a, + 0x70, + 0x7e, + 0x6c, + 0x62, + 0x48, + 0x46, + 0x54, + 0x5a, + 0xe0, + 0xee, + 0xfc, + 0xf2, + 0xd8, + 0xd6, + 0xc4, + 0xca, + 0x90, + 0x9e, + 0x8c, + 0x82, + 0xa8, + 0xa6, + 0xb4, + 0xba, + 0xdb, + 0xd5, + 0xc7, + 0xc9, + 0xe3, + 0xed, + 0xff, + 0xf1, + 0xab, + 0xa5, + 0xb7, + 0xb9, + 0x93, + 0x9d, + 0x8f, + 0x81, + 0x3b, + 0x35, + 0x27, + 0x29, + 0x03, + 0x0d, + 0x1f, + 0x11, + 0x4b, + 0x45, + 0x57, + 0x59, + 0x73, + 0x7d, + 0x6f, + 0x61, + 0xad, + 0xa3, + 0xb1, + 0xbf, + 0x95, + 0x9b, + 0x89, + 0x87, + 0xdd, + 0xd3, + 0xc1, + 0xcf, + 0xe5, + 0xeb, + 0xf9, + 0xf7, + 0x4d, + 0x43, + 0x51, + 0x5f, + 0x75, + 0x7b, + 0x69, + 0x67, + 0x3d, + 0x33, + 0x21, + 0x2f, + 0x05, + 0x0b, + 0x19, + 0x17, + 0x76, + 0x78, + 0x6a, + 0x64, + 0x4e, + 0x40, + 0x52, + 0x5c, + 0x06, + 0x08, + 0x1a, + 0x14, + 0x3e, + 0x30, + 0x22, + 0x2c, + 0x96, + 0x98, + 0x8a, + 0x84, + 0xae, + 0xa0, + 0xb2, + 0xbc, + 0xe6, + 0xe8, + 0xfa, + 0xf4, + 0xde, + 0xd0, + 0xc2, + 0xcc, + 0x41, + 0x4f, + 0x5d, + 0x53, + 0x79, + 0x77, + 0x65, + 0x6b, + 0x31, + 0x3f, + 0x2d, + 0x23, + 0x09, + 0x07, + 0x15, + 0x1b, + 0xa1, + 0xaf, + 0xbd, + 0xb3, + 0x99, + 0x97, + 0x85, + 0x8b, + 0xd1, + 0xdf, + 0xcd, + 0xc3, + 0xe9, + 0xe7, + 0xf5, + 0xfb, + 0x9a, + 0x94, + 0x86, + 0x88, + 0xa2, + 0xac, + 0xbe, + 0xb0, + 0xea, + 0xe4, + 0xf6, + 0xf8, + 0xd2, + 0xdc, + 0xce, + 0xc0, + 0x7a, + 0x74, + 0x66, + 0x68, + 0x42, + 0x4c, + 0x5e, + 0x50, + 0x0a, + 0x04, + 0x16, + 0x18, + 0x32, + 0x3c, + 0x2e, + 0x20, + 0xec, + 0xe2, + 0xf0, + 0xfe, + 0xd4, + 0xda, + 0xc8, + 0xc6, + 0x9c, + 0x92, + 0x80, + 0x8e, + 0xa4, + 0xaa, + 0xb8, + 0xb6, + 0x0c, + 0x02, + 0x10, + 0x1e, + 0x34, + 0x3a, + 0x28, + 0x26, + 0x7c, + 0x72, + 0x60, + 0x6e, + 0x44, + 0x4a, + 0x58, + 0x56, + 0x37, + 0x39, + 0x2b, + 0x25, + 0x0f, + 0x01, + 0x13, + 0x1d, + 0x47, + 0x49, + 0x5b, + 0x55, + 0x7f, + 0x71, + 0x63, + 0x6d, + 0xd7, + 0xd9, + 0xcb, + 0xc5, + 0xef, + 0xe1, + 0xf3, + 0xfd, + 0xa7, + 0xa9, + 0xbb, + 0xb5, + 0x9f, + 0x91, + 0x83, + 0x8d, + }; + + /** + * Subroutine of the Rijndael key expansion. + */ + public static BigInteger scheduleCore(BigInteger t, int rconCounter) { + StringBuilder rBytes = new StringBuilder(t.toString(16)); + + // Add zero padding + while (rBytes.length() < 8) { + rBytes.insert(0, "0"); + } + + // rotate the first 16 bits to the back + String rotatingBytes = rBytes.substring(0, 2); + String fixedBytes = rBytes.substring(2); + + rBytes = new StringBuilder(fixedBytes + rotatingBytes); + + // apply S-Box to all 8-Bit Substrings + for (int i = 0; i < 4; i++) { + StringBuilder currentByteBits = new StringBuilder(rBytes.substring(i * 2, (i + 1) * 2)); + + int currentByte = Integer.parseInt(currentByteBits.toString(), 16); + currentByte = SBOX[currentByte]; + + // add the current RCON value to the first byte + if (i == 0) { + currentByte = currentByte ^ RCON[rconCounter]; + } + + currentByteBits = new StringBuilder(Integer.toHexString(currentByte)); + + // Add zero padding + while (currentByteBits.length() < 2) { + currentByteBits.insert(0, '0'); + } + + // replace bytes in original string + rBytes = new StringBuilder(rBytes.substring(0, i * 2) + currentByteBits + rBytes.substring((i + 1) * 2)); + } + + return new BigInteger(rBytes.toString(), 16); + } + + /** + * Returns an array of 10 + 1 round keys that are calculated by using + * Rijndael key schedule + * + * @return array of 10 + 1 round keys + */ + public static BigInteger[] keyExpansion(BigInteger initialKey) { + BigInteger[] roundKeys = { + initialKey, + BigInteger.ZERO, + BigInteger.ZERO, + BigInteger.ZERO, + BigInteger.ZERO, + BigInteger.ZERO, + BigInteger.ZERO, + BigInteger.ZERO, + BigInteger.ZERO, + BigInteger.ZERO, + BigInteger.ZERO, + }; + + // initialize rcon iteration + int rconCounter = 1; + + for (int i = 1; i < 11; i++) { + // get the previous 32 bits the key + BigInteger t = roundKeys[i - 1].remainder(new BigInteger("100000000", 16)); + + // split previous key into 8-bit segments + BigInteger[] prevKey = { + roundKeys[i - 1].remainder(new BigInteger("100000000", 16)), + roundKeys[i - 1].remainder(new BigInteger("10000000000000000", 16)).divide(new BigInteger("100000000", 16)), + roundKeys[i - 1].remainder(new BigInteger("1000000000000000000000000", 16)).divide(new BigInteger("10000000000000000", 16)), + roundKeys[i - 1].divide(new BigInteger("1000000000000000000000000", 16)), + }; + + // run schedule core + t = scheduleCore(t, rconCounter); + rconCounter += 1; + + // Calculate partial round key + BigInteger t0 = t.xor(prevKey[3]); + BigInteger t1 = t0.xor(prevKey[2]); + BigInteger t2 = t1.xor(prevKey[1]); + BigInteger t3 = t2.xor(prevKey[0]); + + // Join round key segments + t2 = t2.multiply(new BigInteger("100000000", 16)); + t1 = t1.multiply(new BigInteger("10000000000000000", 16)); + t0 = t0.multiply(new BigInteger("1000000000000000000000000", 16)); + roundKeys[i] = t0.add(t1).add(t2).add(t3); + } + return roundKeys; + } + + /** + * representation of the input 128-bit block as an array of 8-bit integers. + * + * @param block of 128-bit integers + * @return array of 8-bit integers + */ + public static int[] splitBlockIntoCells(BigInteger block) { + int[] cells = new int[16]; + StringBuilder blockBits = new StringBuilder(block.toString(2)); + + // Append leading 0 for full "128-bit" string + while (blockBits.length() < 128) { + blockBits.insert(0, '0'); + } + + // split 128 to 8 bit cells + for (int i = 0; i < cells.length; i++) { + String cellBits = blockBits.substring(8 * i, 8 * (i + 1)); + cells[i] = Integer.parseInt(cellBits, 2); + } + + return cells; + } + + /** + * Returns the 128-bit BigInteger representation of the input of an array of + * 8-bit integers. + * + * @param cells that we need to merge + * @return block of merged cells + */ + public static BigInteger mergeCellsIntoBlock(int[] cells) { + StringBuilder blockBits = new StringBuilder(); + for (int i = 0; i < 16; i++) { + StringBuilder cellBits = new StringBuilder(Integer.toBinaryString(cells[i])); + + // Append leading 0 for full "8-bit" strings + while (cellBits.length() < 8) { + cellBits.insert(0, '0'); + } + + blockBits.append(cellBits); + } + + return new BigInteger(blockBits.toString(), 2); + } + + /** + * @return ciphertext XOR key + */ + public static BigInteger addRoundKey(BigInteger ciphertext, BigInteger key) { + return ciphertext.xor(key); + } + + /** + * substitutes 8-Bit long substrings of the input using the S-Box and + * returns the result. + * + * @return subtraction Output + */ + public static BigInteger subBytes(BigInteger ciphertext) { + int[] cells = splitBlockIntoCells(ciphertext); + + for (int i = 0; i < 16; i++) { + cells[i] = SBOX[cells[i]]; + } + + return mergeCellsIntoBlock(cells); + } + + /** + * substitutes 8-Bit long substrings of the input using the inverse S-Box + * for decryption and returns the result. + * + * @return subtraction Output + */ + public static BigInteger subBytesDec(BigInteger ciphertext) { + int[] cells = splitBlockIntoCells(ciphertext); + + for (int i = 0; i < 16; i++) { + cells[i] = INVERSE_SBOX[cells[i]]; + } + + return mergeCellsIntoBlock(cells); + } + + /** + * Cell permutation step. Shifts cells within the rows of the input and + * returns the result. + */ + public static BigInteger shiftRows(BigInteger ciphertext) { + int[] cells = splitBlockIntoCells(ciphertext); + int[] output = new int[16]; + + // do nothing in the first row + output[0] = cells[0]; + output[4] = cells[4]; + output[8] = cells[8]; + output[12] = cells[12]; + + // shift the second row backwards by one cell + output[1] = cells[5]; + output[5] = cells[9]; + output[9] = cells[13]; + output[13] = cells[1]; + + // shift the third row backwards by two cell + output[2] = cells[10]; + output[6] = cells[14]; + output[10] = cells[2]; + output[14] = cells[6]; + + // shift the forth row backwards by tree cell + output[3] = cells[15]; + output[7] = cells[3]; + output[11] = cells[7]; + output[15] = cells[11]; + + return mergeCellsIntoBlock(output); + } + + /** + * Cell permutation step for decryption . Shifts cells within the rows of + * the input and returns the result. + */ + public static BigInteger shiftRowsDec(BigInteger ciphertext) { + int[] cells = splitBlockIntoCells(ciphertext); + int[] output = new int[16]; + + // do nothing in the first row + output[0] = cells[0]; + output[4] = cells[4]; + output[8] = cells[8]; + output[12] = cells[12]; + + // shift the second row forwards by one cell + output[1] = cells[13]; + output[5] = cells[1]; + output[9] = cells[5]; + output[13] = cells[9]; + + // shift the third row forwards by two cell + output[2] = cells[10]; + output[6] = cells[14]; + output[10] = cells[2]; + output[14] = cells[6]; + + // shift the forth row forwards by tree cell + output[3] = cells[7]; + output[7] = cells[11]; + output[11] = cells[15]; + output[15] = cells[3]; + + return mergeCellsIntoBlock(output); + } + + /** + * Applies the Rijndael MixColumns to the input and returns the result. + */ + public static BigInteger mixColumns(BigInteger ciphertext) { + int[] cells = splitBlockIntoCells(ciphertext); + int[] outputCells = new int[16]; + + for (int i = 0; i < 4; i++) { + int[] row = { + cells[i * 4], + cells[i * 4 + 1], + cells[i * 4 + 2], + cells[i * 4 + 3], + }; + + outputCells[i * 4] = MULT2[row[0]] ^ MULT3[row[1]] ^ row[2] ^ row[3]; + outputCells[i * 4 + 1] = row[0] ^ MULT2[row[1]] ^ MULT3[row[2]] ^ row[3]; + outputCells[i * 4 + 2] = row[0] ^ row[1] ^ MULT2[row[2]] ^ MULT3[row[3]]; + outputCells[i * 4 + 3] = MULT3[row[0]] ^ row[1] ^ row[2] ^ MULT2[row[3]]; + } + return mergeCellsIntoBlock(outputCells); + } + + /** + * Applies the inverse Rijndael MixColumns for decryption to the input and + * returns the result. + */ + public static BigInteger mixColumnsDec(BigInteger ciphertext) { + int[] cells = splitBlockIntoCells(ciphertext); + int[] outputCells = new int[16]; + + for (int i = 0; i < 4; i++) { + int[] row = { + cells[i * 4], + cells[i * 4 + 1], + cells[i * 4 + 2], + cells[i * 4 + 3], + }; + + outputCells[i * 4] = MULT14[row[0]] ^ MULT11[row[1]] ^ MULT13[row[2]] ^ MULT9[row[3]]; + outputCells[i * 4 + 1] = MULT9[row[0]] ^ MULT14[row[1]] ^ MULT11[row[2]] ^ MULT13[row[3]]; + outputCells[i * 4 + 2] = MULT13[row[0]] ^ MULT9[row[1]] ^ MULT14[row[2]] ^ MULT11[row[3]]; + outputCells[i * 4 + 3] = MULT11[row[0]] ^ MULT13[row[1]] ^ MULT9[row[2]] ^ MULT14[row[3]]; + } + return mergeCellsIntoBlock(outputCells); + } + + /** + * Encrypts the plaintext with the key and returns the result + * + * @param plainText which we want to encrypt + * @param key the key for encrypt + * @return EncryptedText + */ + public static BigInteger encrypt(BigInteger plainText, BigInteger key) { + BigInteger[] roundKeys = keyExpansion(key); + + // Initial round + plainText = addRoundKey(plainText, roundKeys[0]); + + // Main rounds + for (int i = 1; i < 10; i++) { + plainText = subBytes(plainText); + plainText = shiftRows(plainText); + plainText = mixColumns(plainText); + plainText = addRoundKey(plainText, roundKeys[i]); + } + + // Final round + plainText = subBytes(plainText); + plainText = shiftRows(plainText); + plainText = addRoundKey(plainText, roundKeys[10]); + + return plainText; + } + + /** + * Decrypts the ciphertext with the key and returns the result + * + * @param cipherText The Encrypted text which we want to decrypt + * @return decryptedText + */ + public static BigInteger decrypt(BigInteger cipherText, BigInteger key) { + BigInteger[] roundKeys = keyExpansion(key); + + // Invert final round + cipherText = addRoundKey(cipherText, roundKeys[10]); + cipherText = shiftRowsDec(cipherText); + cipherText = subBytesDec(cipherText); + + // Invert main rounds + for (int i = 9; i > 0; i--) { + cipherText = addRoundKey(cipherText, roundKeys[i]); + cipherText = mixColumnsDec(cipherText); + cipherText = shiftRowsDec(cipherText); + cipherText = subBytesDec(cipherText); + } + + // Invert initial round + cipherText = addRoundKey(cipherText, roundKeys[0]); + + return cipherText; + } + + public static void main(String[] args) { + try (Scanner input = new Scanner(System.in)) { + System.out.println("Enter (e) letter for encrpyt or (d) letter for decrypt :"); + char choice = input.nextLine().charAt(0); + String in; + switch (choice) { + case 'E', 'e' -> { + System.out.println( + "Choose a plaintext block (128-Bit Integer in base 16):" + ); + in = input.nextLine(); + BigInteger plaintext = new BigInteger(in, 16); + System.out.println( + "Choose a Key (128-Bit Integer in base 16):" + ); + in = input.nextLine(); + BigInteger encryptionKey = new BigInteger(in, 16); + System.out.println( + "The encrypted message is: \n" + + encrypt(plaintext, encryptionKey).toString(16) + ); + } + case 'D', 'd' -> { + System.out.println( + "Enter your ciphertext block (128-Bit Integer in base 16):" + ); + in = input.nextLine(); + BigInteger ciphertext = new BigInteger(in, 16); + System.out.println( + "Choose a Key (128-Bit Integer in base 16):" + ); + in = input.nextLine(); + BigInteger decryptionKey = new BigInteger(in, 16); + System.out.println( + "The deciphered message is:\n" + + decrypt(ciphertext, decryptionKey).toString(16) + ); + } + default -> System.out.println("** End **"); + } + } + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/AESEncryption.java b/src/main/java/com/thealgorithms/ciphers/AESEncryption.java new file mode 100644 index 000000000000..14582205442f --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/AESEncryption.java @@ -0,0 +1,104 @@ +package com.thealgorithms.ciphers; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.spec.GCMParameterSpec; + +/** + * This example program shows how AES encryption and decryption can be done in + * Java. Please note that secret key and encrypted text is unreadable binary and + * hence in the following program we display it in hexadecimal format of the + * underlying bytes. + */ +public final class AESEncryption { + private AESEncryption() { + } + + private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); + private static Cipher aesCipher; + + /** + * 1. Generate a plain text for encryption 2. Get a secret key (printed in + * hexadecimal form). In actual use this must be encrypted and kept safe. + * The same key is required for decryption. + */ + public static void main(String[] args) throws Exception { + String plainText = "Hello World"; + SecretKey secKey = getSecretEncryptionKey(); + byte[] cipherText = encryptText(plainText, secKey); + String decryptedText = decryptText(cipherText, secKey); + + System.out.println("Original Text:" + plainText); + System.out.println("AES Key (Hex Form):" + bytesToHex(secKey.getEncoded())); + System.out.println("Encrypted Text (Hex Form):" + bytesToHex(cipherText)); + System.out.println("Descrypted Text:" + decryptedText); + } + + /** + * gets the AES encryption key. In your actual programs, this should be + * safely stored. + * + * @return secKey (Secret key that we encrypt using it) + * @throws NoSuchAlgorithmException (from KeyGenrator) + */ + public static SecretKey getSecretEncryptionKey() throws NoSuchAlgorithmException { + KeyGenerator aesKeyGenerator = KeyGenerator.getInstance("AES"); + aesKeyGenerator.init(128); // The AES key size in number of bits + return aesKeyGenerator.generateKey(); + } + + /** + * Encrypts plainText in AES using the secret key + * + * @return byteCipherText (The encrypted text) + * @throws NoSuchPaddingException (from Cipher) + * @throws NoSuchAlgorithmException (from Cipher) + * @throws InvalidKeyException (from Cipher) + * @throws BadPaddingException (from Cipher) + * @throws IllegalBlockSizeException (from Cipher) + */ + public static byte[] encryptText(String plainText, SecretKey secKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { + // AES defaults to AES/ECB/PKCS5Padding in Java 7 + aesCipher = Cipher.getInstance("AES/GCM/NoPadding"); + aesCipher.init(Cipher.ENCRYPT_MODE, secKey); + return aesCipher.doFinal(plainText.getBytes()); + } + + /** + * Decrypts encrypted byte array using the key used for encryption. + * + * @return plainText + */ + public static String decryptText(byte[] byteCipherText, SecretKey secKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException { + // AES defaults to AES/ECB/PKCS5Padding in Java 7 + Cipher decryptionCipher = Cipher.getInstance("AES/GCM/NoPadding"); + GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, aesCipher.getIV()); + decryptionCipher.init(Cipher.DECRYPT_MODE, secKey, gcmParameterSpec); + byte[] bytePlainText = decryptionCipher.doFinal(byteCipherText); + return new String(bytePlainText); + } + + /** + * Convert a binary byte array into readable hex form Old library is + * deprecated on OpenJdk 11 and this is faster regarding other solution is + * using StringBuilder + * + * @return hexHash + */ + public static String bytesToHex(byte[] bytes) { + char[] hexChars = new char[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = HEX_ARRAY[v >>> 4]; + hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; + } + return new String(hexChars); + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/AffineCipher.java b/src/main/java/com/thealgorithms/ciphers/AffineCipher.java new file mode 100644 index 000000000000..b82681372f2b --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/AffineCipher.java @@ -0,0 +1,98 @@ +package com.thealgorithms.ciphers; + +/** + * The AffineCipher class implements the Affine cipher, a type of monoalphabetic substitution cipher. + * It encrypts and decrypts messages using a linear transformation defined by the formula: + * + * E(x) = (a * x + b) mod m + * D(y) = a^-1 * (y - b) mod m + * + * where: + * - E(x) is the encrypted character, + * - D(y) is the decrypted character, + * - a is the multiplicative key (must be coprime to m), + * - b is the additive key, + * - x is the index of the plaintext character, + * - y is the index of the ciphertext character, + * - m is the size of the alphabet (26 for the English alphabet). + * + * The class provides methods for encrypting and decrypting messages, as well as a main method to demonstrate its usage. + */ +final class AffineCipher { + private AffineCipher() { + } + + // Key values of a and b + static int a = 17; + static int b = 20; + + /** + * Encrypts a message using the Affine cipher. + * + * @param msg the plaintext message as a character array + * @return the encrypted ciphertext + */ + static String encryptMessage(char[] msg) { + // Cipher Text initially empty + StringBuilder cipher = new StringBuilder(); + for (int i = 0; i < msg.length; i++) { + // Avoid space to be encrypted + /* applying encryption formula ( a * x + b ) mod m + {here x is msg[i] and m is 26} and added 'A' to + bring it in the range of ASCII alphabet [65-90 | A-Z] */ + if (msg[i] != ' ') { + cipher.append((char) ((((a * (msg[i] - 'A')) + b) % 26) + 'A')); + } else { // else simply append space character + cipher.append(msg[i]); + } + } + return cipher.toString(); + } + + /** + * Decrypts a ciphertext using the Affine cipher. + * + * @param cipher the ciphertext to decrypt + * @return the decrypted plaintext message + */ + static String decryptCipher(String cipher) { + StringBuilder msg = new StringBuilder(); + int aInv = 0; + int flag; + + // Find a^-1 (the multiplicative inverse of a in the group of integers modulo m.) + for (int i = 0; i < 26; i++) { + flag = (a * i) % 26; + + // Check if (a * i) % 26 == 1, + // then i will be the multiplicative inverse of a + if (flag == 1) { + aInv = i; + } + } + for (int i = 0; i < cipher.length(); i++) { + /* Applying decryption formula a^-1 * (x - b) mod m + {here x is cipher[i] and m is 26} and added 'A' + to bring it in the range of ASCII alphabet [65-90 | A-Z] */ + if (cipher.charAt(i) != ' ') { + msg.append((char) (((aInv * ((cipher.charAt(i) - 'A') - b + 26)) % 26) + 'A')); + } else { // else simply append space character + msg.append(cipher.charAt(i)); + } + } + + return msg.toString(); + } + + // Driver code + public static void main(String[] args) { + String msg = "AFFINE CIPHER"; + + // Calling encryption function + String cipherText = encryptMessage(msg.toCharArray()); + System.out.println("Encrypted Message is : " + cipherText); + + // Calling Decryption function + System.out.println("Decrypted Message is: " + decryptCipher(cipherText)); + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/AtbashCipher.java b/src/main/java/com/thealgorithms/ciphers/AtbashCipher.java new file mode 100644 index 000000000000..9169aa82bd75 --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/AtbashCipher.java @@ -0,0 +1,101 @@ +package com.thealgorithms.ciphers; + +/** + * The Atbash cipher is a classic substitution cipher that substitutes each letter + * with its opposite letter in the alphabet. + * + * For example: + * - 'A' becomes 'Z', 'B' becomes 'Y', 'C' becomes 'X', and so on. + * - Similarly, 'a' becomes 'z', 'b' becomes 'y', and so on. + * + * The cipher works identically for both uppercase and lowercase letters. + * Non-alphabetical characters remain unchanged in the output. + * + * This cipher is symmetric, meaning that applying the cipher twice will return + * the original text. Therefore, the same function is used for both encryption and decryption. + * + * <p>Usage Example:</p> + * <pre> + * AtbashCipher cipher = new AtbashCipher("Hello World!"); + * String encrypted = cipher.convert(); // Output: "Svool Dliow!" + * </pre> + * + * @author <a href="https://github.com/Krounosity">Krounosity</a> + * @see <a href="https://en.wikipedia.org/wiki/Atbash">Atbash Cipher (Wikipedia)</a> + */ +public class AtbashCipher { + + private String toConvert; + + public AtbashCipher() { + } + + /** + * Constructor with a string parameter. + * + * @param str The string to be converted using the Atbash cipher + */ + public AtbashCipher(String str) { + this.toConvert = str; + } + + /** + * Returns the current string set for conversion. + * + * @return The string to be converted + */ + public String getString() { + return toConvert; + } + + /** + * Sets the string to be converted using the Atbash cipher. + * + * @param str The new string to convert + */ + public void setString(String str) { + this.toConvert = str; + } + + /** + * Checks if a character is uppercase. + * + * @param ch The character to check + * @return {@code true} if the character is uppercase, {@code false} otherwise + */ + private boolean isCapital(char ch) { + return ch >= 'A' && ch <= 'Z'; + } + + /** + * Checks if a character is lowercase. + * + * @param ch The character to check + * @return {@code true} if the character is lowercase, {@code false} otherwise + */ + private boolean isSmall(char ch) { + return ch >= 'a' && ch <= 'z'; + } + + /** + * Converts the input string using the Atbash cipher. + * Alphabetic characters are substituted with their opposite in the alphabet, + * while non-alphabetic characters remain unchanged. + * + * @return The converted string after applying the Atbash cipher + */ + public String convert() { + StringBuilder convertedString = new StringBuilder(); + + for (char ch : toConvert.toCharArray()) { + if (isSmall(ch)) { + convertedString.append((char) ('z' - (ch - 'a'))); + } else if (isCapital(ch)) { + convertedString.append((char) ('Z' - (ch - 'A'))); + } else { + convertedString.append(ch); + } + } + return convertedString.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/Autokey.java b/src/main/java/com/thealgorithms/ciphers/Autokey.java new file mode 100644 index 000000000000..bb67f512accf --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/Autokey.java @@ -0,0 +1,55 @@ +package com.thealgorithms.ciphers; + +/** + * The Autokey Cipher is an interesting and historically significant encryption method, + * as it improves upon the classic Vigenère Cipher by using the plaintext itself to + * extend the key. This makes it harder to break using frequency analysis, as it + * doesn’t rely solely on a repeated key. + * https://en.wikipedia.org/wiki/Autokey_cipher + * + * @author bennybebo + */ +public class Autokey { + + // Encrypts the plaintext using the Autokey cipher + public String encrypt(String plaintext, String keyword) { + plaintext = plaintext.toUpperCase().replaceAll("[^A-Z]", ""); // Sanitize input + keyword = keyword.toUpperCase(); + + StringBuilder extendedKey = new StringBuilder(keyword); + extendedKey.append(plaintext); // Extend key with plaintext + + StringBuilder ciphertext = new StringBuilder(); + + for (int i = 0; i < plaintext.length(); i++) { + char plainChar = plaintext.charAt(i); + char keyChar = extendedKey.charAt(i); + + int encryptedChar = (plainChar - 'A' + keyChar - 'A') % 26 + 'A'; + ciphertext.append((char) encryptedChar); + } + + return ciphertext.toString(); + } + + // Decrypts the ciphertext using the Autokey cipher + public String decrypt(String ciphertext, String keyword) { + ciphertext = ciphertext.toUpperCase().replaceAll("[^A-Z]", ""); // Sanitize input + keyword = keyword.toUpperCase(); + + StringBuilder plaintext = new StringBuilder(); + StringBuilder extendedKey = new StringBuilder(keyword); + + for (int i = 0; i < ciphertext.length(); i++) { + char cipherChar = ciphertext.charAt(i); + char keyChar = extendedKey.charAt(i); + + int decryptedChar = (cipherChar - 'A' - (keyChar - 'A') + 26) % 26 + 'A'; + plaintext.append((char) decryptedChar); + + extendedKey.append((char) decryptedChar); // Extend key with each decrypted char + } + + return plaintext.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/BaconianCipher.java b/src/main/java/com/thealgorithms/ciphers/BaconianCipher.java new file mode 100644 index 000000000000..16dfd6e674af --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/BaconianCipher.java @@ -0,0 +1,71 @@ +package com.thealgorithms.ciphers; + +import java.util.HashMap; +import java.util.Map; + +/** + * The Baconian Cipher is a substitution cipher where each letter is represented + * by a group of five binary digits (A's and B's). It can also be used to hide + * messages within other texts, making it a simple form of steganography. + * https://en.wikipedia.org/wiki/Bacon%27s_cipher + * + * @author Bennybebo + */ +public class BaconianCipher { + + private static final Map<Character, String> BACONIAN_MAP = new HashMap<>(); + private static final Map<String, Character> REVERSE_BACONIAN_MAP = new HashMap<>(); + + static { + // Initialize the Baconian cipher mappings + String[] baconianAlphabet = {"AAAAA", "AAAAB", "AAABA", "AAABB", "AABAA", "AABAB", "AABBA", "AABBB", "ABAAA", "ABAAB", "ABABA", "ABABB", "ABBAA", "ABBAB", "ABBBA", "ABBBB", "BAAAA", "BAAAB", "BAABA", "BAABB", "BABAA", "BABAB", "BABBA", "BABBB", "BBAAA", "BBAAB"}; + char letter = 'A'; + for (String code : baconianAlphabet) { + BACONIAN_MAP.put(letter, code); + REVERSE_BACONIAN_MAP.put(code, letter); + letter++; + } + + // Handle I/J as the same letter + BACONIAN_MAP.put('I', BACONIAN_MAP.get('J')); + REVERSE_BACONIAN_MAP.put(BACONIAN_MAP.get('I'), 'I'); + } + + /** + * Encrypts the given plaintext using the Baconian cipher. + * + * @param plaintext The plaintext message to encrypt. + * @return The ciphertext as a binary (A/B) sequence. + */ + public String encrypt(String plaintext) { + StringBuilder ciphertext = new StringBuilder(); + plaintext = plaintext.toUpperCase().replaceAll("[^A-Z]", ""); // Remove non-letter characters + + for (char letter : plaintext.toCharArray()) { + ciphertext.append(BACONIAN_MAP.get(letter)); + } + + return ciphertext.toString(); + } + + /** + * Decrypts the given ciphertext encoded in binary (A/B) format using the Baconian cipher. + * + * @param ciphertext The ciphertext to decrypt. + * @return The decrypted plaintext message. + */ + public String decrypt(String ciphertext) { + StringBuilder plaintext = new StringBuilder(); + + for (int i = 0; i < ciphertext.length(); i += 5) { + String code = ciphertext.substring(i, i + 5); + if (REVERSE_BACONIAN_MAP.containsKey(code)) { + plaintext.append(REVERSE_BACONIAN_MAP.get(code)); + } else { + throw new IllegalArgumentException("Invalid Baconian code: " + code); + } + } + + return plaintext.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/Blowfish.java b/src/main/java/com/thealgorithms/ciphers/Blowfish.java new file mode 100644 index 000000000000..ea1807e62710 --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/Blowfish.java @@ -0,0 +1,1244 @@ +package com.thealgorithms.ciphers; + +/* + * Java program for Blowfish Algorithm + * Wikipedia: https://en.wikipedia.org/wiki/Blowfish_(cipher) + * + * Author: Akshay Dubey (https://github.com/itsAkshayDubey) + * + * */ + +public class Blowfish { + + // Initializing substitution boxes + String[][] sBox = { + { + "d1310ba6", + "98dfb5ac", + "2ffd72db", + "d01adfb7", + "b8e1afed", + "6a267e96", + "ba7c9045", + "f12c7f99", + "24a19947", + "b3916cf7", + "0801f2e2", + "858efc16", + "636920d8", + "71574e69", + "a458fea3", + "f4933d7e", + "0d95748f", + "728eb658", + "718bcd58", + "82154aee", + "7b54a41d", + "c25a59b5", + "9c30d539", + "2af26013", + "c5d1b023", + "286085f0", + "ca417918", + "b8db38ef", + "8e79dcb0", + "603a180e", + "6c9e0e8b", + "b01e8a3e", + "d71577c1", + "bd314b27", + "78af2fda", + "55605c60", + "e65525f3", + "aa55ab94", + "57489862", + "63e81440", + "55ca396a", + "2aab10b6", + "b4cc5c34", + "1141e8ce", + "a15486af", + "7c72e993", + "b3ee1411", + "636fbc2a", + "2ba9c55d", + "741831f6", + "ce5c3e16", + "9b87931e", + "afd6ba33", + "6c24cf5c", + "7a325381", + "28958677", + "3b8f4898", + "6b4bb9af", + "c4bfe81b", + "66282193", + "61d809cc", + "fb21a991", + "487cac60", + "5dec8032", + "ef845d5d", + "e98575b1", + "dc262302", + "eb651b88", + "23893e81", + "d396acc5", + "0f6d6ff3", + "83f44239", + "2e0b4482", + "a4842004", + "69c8f04a", + "9e1f9b5e", + "21c66842", + "f6e96c9a", + "670c9c61", + "abd388f0", + "6a51a0d2", + "d8542f68", + "960fa728", + "ab5133a3", + "6eef0b6c", + "137a3be4", + "ba3bf050", + "7efb2a98", + "a1f1651d", + "39af0176", + "66ca593e", + "82430e88", + "8cee8619", + "456f9fb4", + "7d84a5c3", + "3b8b5ebe", + "e06f75d8", + "85c12073", + "401a449f", + "56c16aa6", + "4ed3aa62", + "363f7706", + "1bfedf72", + "429b023d", + "37d0d724", + "d00a1248", + "db0fead3", + "49f1c09b", + "075372c9", + "80991b7b", + "25d479d8", + "f6e8def7", + "e3fe501a", + "b6794c3b", + "976ce0bd", + "04c006ba", + "c1a94fb6", + "409f60c4", + "5e5c9ec2", + "196a2463", + "68fb6faf", + "3e6c53b5", + "1339b2eb", + "3b52ec6f", + "6dfc511f", + "9b30952c", + "cc814544", + "af5ebd09", + "bee3d004", + "de334afd", + "660f2807", + "192e4bb3", + "c0cba857", + "45c8740f", + "d20b5f39", + "b9d3fbdb", + "5579c0bd", + "1a60320a", + "d6a100c6", + "402c7279", + "679f25fe", + "fb1fa3cc", + "8ea5e9f8", + "db3222f8", + "3c7516df", + "fd616b15", + "2f501ec8", + "ad0552ab", + "323db5fa", + "fd238760", + "53317b48", + "3e00df82", + "9e5c57bb", + "ca6f8ca0", + "1a87562e", + "df1769db", + "d542a8f6", + "287effc3", + "ac6732c6", + "8c4f5573", + "695b27b0", + "bbca58c8", + "e1ffa35d", + "b8f011a0", + "10fa3d98", + "fd2183b8", + "4afcb56c", + "2dd1d35b", + "9a53e479", + "b6f84565", + "d28e49bc", + "4bfb9790", + "e1ddf2da", + "a4cb7e33", + "62fb1341", + "cee4c6e8", + "ef20cada", + "36774c01", + "d07e9efe", + "2bf11fb4", + "95dbda4d", + "ae909198", + "eaad8e71", + "6b93d5a0", + "d08ed1d0", + "afc725e0", + "8e3c5b2f", + "8e7594b7", + "8ff6e2fb", + "f2122b64", + "8888b812", + "900df01c", + "4fad5ea0", + "688fc31c", + "d1cff191", + "b3a8c1ad", + "2f2f2218", + "be0e1777", + "ea752dfe", + "8b021fa1", + "e5a0cc0f", + "b56f74e8", + "18acf3d6", + "ce89e299", + "b4a84fe0", + "fd13e0b7", + "7cc43b81", + "d2ada8d9", + "165fa266", + "80957705", + "93cc7314", + "211a1477", + "e6ad2065", + "77b5fa86", + "c75442f5", + "fb9d35cf", + "ebcdaf0c", + "7b3e89a0", + "d6411bd3", + "ae1e7e49", + "00250e2d", + "2071b35e", + "226800bb", + "57b8e0af", + "2464369b", + "f009b91e", + "5563911d", + "59dfa6aa", + "78c14389", + "d95a537f", + "207d5ba2", + "02e5b9c5", + "83260376", + "6295cfa9", + "11c81968", + "4e734a41", + "b3472dca", + "7b14a94a", + "1b510052", + "9a532915", + "d60f573f", + "bc9bc6e4", + "2b60a476", + "81e67400", + "08ba6fb5", + "571be91f", + "f296ec6b", + "2a0dd915", + "b6636521", + "e7b9f9b6", + "ff34052e", + "c5855664", + "53b02d5d", + "a99f8fa1", + "08ba4799", + "6e85076a", + }, + { + "4b7a70e9", + "b5b32944", + "db75092e", + "c4192623", + "ad6ea6b0", + "49a7df7d", + "9cee60b8", + "8fedb266", + "ecaa8c71", + "699a17ff", + "5664526c", + "c2b19ee1", + "193602a5", + "75094c29", + "a0591340", + "e4183a3e", + "3f54989a", + "5b429d65", + "6b8fe4d6", + "99f73fd6", + "a1d29c07", + "efe830f5", + "4d2d38e6", + "f0255dc1", + "4cdd2086", + "8470eb26", + "6382e9c6", + "021ecc5e", + "09686b3f", + "3ebaefc9", + "3c971814", + "6b6a70a1", + "687f3584", + "52a0e286", + "b79c5305", + "aa500737", + "3e07841c", + "7fdeae5c", + "8e7d44ec", + "5716f2b8", + "b03ada37", + "f0500c0d", + "f01c1f04", + "0200b3ff", + "ae0cf51a", + "3cb574b2", + "25837a58", + "dc0921bd", + "d19113f9", + "7ca92ff6", + "94324773", + "22f54701", + "3ae5e581", + "37c2dadc", + "c8b57634", + "9af3dda7", + "a9446146", + "0fd0030e", + "ecc8c73e", + "a4751e41", + "e238cd99", + "3bea0e2f", + "3280bba1", + "183eb331", + "4e548b38", + "4f6db908", + "6f420d03", + "f60a04bf", + "2cb81290", + "24977c79", + "5679b072", + "bcaf89af", + "de9a771f", + "d9930810", + "b38bae12", + "dccf3f2e", + "5512721f", + "2e6b7124", + "501adde6", + "9f84cd87", + "7a584718", + "7408da17", + "bc9f9abc", + "e94b7d8c", + "ec7aec3a", + "db851dfa", + "63094366", + "c464c3d2", + "ef1c1847", + "3215d908", + "dd433b37", + "24c2ba16", + "12a14d43", + "2a65c451", + "50940002", + "133ae4dd", + "71dff89e", + "10314e55", + "81ac77d6", + "5f11199b", + "043556f1", + "d7a3c76b", + "3c11183b", + "5924a509", + "f28fe6ed", + "97f1fbfa", + "9ebabf2c", + "1e153c6e", + "86e34570", + "eae96fb1", + "860e5e0a", + "5a3e2ab3", + "771fe71c", + "4e3d06fa", + "2965dcb9", + "99e71d0f", + "803e89d6", + "5266c825", + "2e4cc978", + "9c10b36a", + "c6150eba", + "94e2ea78", + "a5fc3c53", + "1e0a2df4", + "f2f74ea7", + "361d2b3d", + "1939260f", + "19c27960", + "5223a708", + "f71312b6", + "ebadfe6e", + "eac31f66", + "e3bc4595", + "a67bc883", + "b17f37d1", + "018cff28", + "c332ddef", + "be6c5aa5", + "65582185", + "68ab9802", + "eecea50f", + "db2f953b", + "2aef7dad", + "5b6e2f84", + "1521b628", + "29076170", + "ecdd4775", + "619f1510", + "13cca830", + "eb61bd96", + "0334fe1e", + "aa0363cf", + "b5735c90", + "4c70a239", + "d59e9e0b", + "cbaade14", + "eecc86bc", + "60622ca7", + "9cab5cab", + "b2f3846e", + "648b1eaf", + "19bdf0ca", + "a02369b9", + "655abb50", + "40685a32", + "3c2ab4b3", + "319ee9d5", + "c021b8f7", + "9b540b19", + "875fa099", + "95f7997e", + "623d7da8", + "f837889a", + "97e32d77", + "11ed935f", + "16681281", + "0e358829", + "c7e61fd6", + "96dedfa1", + "7858ba99", + "57f584a5", + "1b227263", + "9b83c3ff", + "1ac24696", + "cdb30aeb", + "532e3054", + "8fd948e4", + "6dbc3128", + "58ebf2ef", + "34c6ffea", + "fe28ed61", + "ee7c3c73", + "5d4a14d9", + "e864b7e3", + "42105d14", + "203e13e0", + "45eee2b6", + "a3aaabea", + "db6c4f15", + "facb4fd0", + "c742f442", + "ef6abbb5", + "654f3b1d", + "41cd2105", + "d81e799e", + "86854dc7", + "e44b476a", + "3d816250", + "cf62a1f2", + "5b8d2646", + "fc8883a0", + "c1c7b6a3", + "7f1524c3", + "69cb7492", + "47848a0b", + "5692b285", + "095bbf00", + "ad19489d", + "1462b174", + "23820e00", + "58428d2a", + "0c55f5ea", + "1dadf43e", + "233f7061", + "3372f092", + "8d937e41", + "d65fecf1", + "6c223bdb", + "7cde3759", + "cbee7460", + "4085f2a7", + "ce77326e", + "a6078084", + "19f8509e", + "e8efd855", + "61d99735", + "a969a7aa", + "c50c06c2", + "5a04abfc", + "800bcadc", + "9e447a2e", + "c3453484", + "fdd56705", + "0e1e9ec9", + "db73dbd3", + "105588cd", + "675fda79", + "e3674340", + "c5c43465", + "713e38d8", + "3d28f89e", + "f16dff20", + "153e21e7", + "8fb03d4a", + "e6e39f2b", + "db83adf7", + }, + { + "e93d5a68", + "948140f7", + "f64c261c", + "94692934", + "411520f7", + "7602d4f7", + "bcf46b2e", + "d4a20068", + "d4082471", + "3320f46a", + "43b7d4b7", + "500061af", + "1e39f62e", + "97244546", + "14214f74", + "bf8b8840", + "4d95fc1d", + "96b591af", + "70f4ddd3", + "66a02f45", + "bfbc09ec", + "03bd9785", + "7fac6dd0", + "31cb8504", + "96eb27b3", + "55fd3941", + "da2547e6", + "abca0a9a", + "28507825", + "530429f4", + "0a2c86da", + "e9b66dfb", + "68dc1462", + "d7486900", + "680ec0a4", + "27a18dee", + "4f3ffea2", + "e887ad8c", + "b58ce006", + "7af4d6b6", + "aace1e7c", + "d3375fec", + "ce78a399", + "406b2a42", + "20fe9e35", + "d9f385b9", + "ee39d7ab", + "3b124e8b", + "1dc9faf7", + "4b6d1856", + "26a36631", + "eae397b2", + "3a6efa74", + "dd5b4332", + "6841e7f7", + "ca7820fb", + "fb0af54e", + "d8feb397", + "454056ac", + "ba489527", + "55533a3a", + "20838d87", + "fe6ba9b7", + "d096954b", + "55a867bc", + "a1159a58", + "cca92963", + "99e1db33", + "a62a4a56", + "3f3125f9", + "5ef47e1c", + "9029317c", + "fdf8e802", + "04272f70", + "80bb155c", + "05282ce3", + "95c11548", + "e4c66d22", + "48c1133f", + "c70f86dc", + "07f9c9ee", + "41041f0f", + "404779a4", + "5d886e17", + "325f51eb", + "d59bc0d1", + "f2bcc18f", + "41113564", + "257b7834", + "602a9c60", + "dff8e8a3", + "1f636c1b", + "0e12b4c2", + "02e1329e", + "af664fd1", + "cad18115", + "6b2395e0", + "333e92e1", + "3b240b62", + "eebeb922", + "85b2a20e", + "e6ba0d99", + "de720c8c", + "2da2f728", + "d0127845", + "95b794fd", + "647d0862", + "e7ccf5f0", + "5449a36f", + "877d48fa", + "c39dfd27", + "f33e8d1e", + "0a476341", + "992eff74", + "3a6f6eab", + "f4f8fd37", + "a812dc60", + "a1ebddf8", + "991be14c", + "db6e6b0d", + "c67b5510", + "6d672c37", + "2765d43b", + "dcd0e804", + "f1290dc7", + "cc00ffa3", + "b5390f92", + "690fed0b", + "667b9ffb", + "cedb7d9c", + "a091cf0b", + "d9155ea3", + "bb132f88", + "515bad24", + "7b9479bf", + "763bd6eb", + "37392eb3", + "cc115979", + "8026e297", + "f42e312d", + "6842ada7", + "c66a2b3b", + "12754ccc", + "782ef11c", + "6a124237", + "b79251e7", + "06a1bbe6", + "4bfb6350", + "1a6b1018", + "11caedfa", + "3d25bdd8", + "e2e1c3c9", + "44421659", + "0a121386", + "d90cec6e", + "d5abea2a", + "64af674e", + "da86a85f", + "bebfe988", + "64e4c3fe", + "9dbc8057", + "f0f7c086", + "60787bf8", + "6003604d", + "d1fd8346", + "f6381fb0", + "7745ae04", + "d736fccc", + "83426b33", + "f01eab71", + "b0804187", + "3c005e5f", + "77a057be", + "bde8ae24", + "55464299", + "bf582e61", + "4e58f48f", + "f2ddfda2", + "f474ef38", + "8789bdc2", + "5366f9c3", + "c8b38e74", + "b475f255", + "46fcd9b9", + "7aeb2661", + "8b1ddf84", + "846a0e79", + "915f95e2", + "466e598e", + "20b45770", + "8cd55591", + "c902de4c", + "b90bace1", + "bb8205d0", + "11a86248", + "7574a99e", + "b77f19b6", + "e0a9dc09", + "662d09a1", + "c4324633", + "e85a1f02", + "09f0be8c", + "4a99a025", + "1d6efe10", + "1ab93d1d", + "0ba5a4df", + "a186f20f", + "2868f169", + "dcb7da83", + "573906fe", + "a1e2ce9b", + "4fcd7f52", + "50115e01", + "a70683fa", + "a002b5c4", + "0de6d027", + "9af88c27", + "773f8641", + "c3604c06", + "61a806b5", + "f0177a28", + "c0f586e0", + "006058aa", + "30dc7d62", + "11e69ed7", + "2338ea63", + "53c2dd94", + "c2c21634", + "bbcbee56", + "90bcb6de", + "ebfc7da1", + "ce591d76", + "6f05e409", + "4b7c0188", + "39720a3d", + "7c927c24", + "86e3725f", + "724d9db9", + "1ac15bb4", + "d39eb8fc", + "ed545578", + "08fca5b5", + "d83d7cd3", + "4dad0fc4", + "1e50ef5e", + "b161e6f8", + "a28514d9", + "6c51133c", + "6fd5c7e7", + "56e14ec4", + "362abfce", + "ddc6c837", + "d79a3234", + "92638212", + "670efa8e", + "406000e0", + }, + { + "3a39ce37", + "d3faf5cf", + "abc27737", + "5ac52d1b", + "5cb0679e", + "4fa33742", + "d3822740", + "99bc9bbe", + "d5118e9d", + "bf0f7315", + "d62d1c7e", + "c700c47b", + "b78c1b6b", + "21a19045", + "b26eb1be", + "6a366eb4", + "5748ab2f", + "bc946e79", + "c6a376d2", + "6549c2c8", + "530ff8ee", + "468dde7d", + "d5730a1d", + "4cd04dc6", + "2939bbdb", + "a9ba4650", + "ac9526e8", + "be5ee304", + "a1fad5f0", + "6a2d519a", + "63ef8ce2", + "9a86ee22", + "c089c2b8", + "43242ef6", + "a51e03aa", + "9cf2d0a4", + "83c061ba", + "9be96a4d", + "8fe51550", + "ba645bd6", + "2826a2f9", + "a73a3ae1", + "4ba99586", + "ef5562e9", + "c72fefd3", + "f752f7da", + "3f046f69", + "77fa0a59", + "80e4a915", + "87b08601", + "9b09e6ad", + "3b3ee593", + "e990fd5a", + "9e34d797", + "2cf0b7d9", + "022b8b51", + "96d5ac3a", + "017da67d", + "d1cf3ed6", + "7c7d2d28", + "1f9f25cf", + "adf2b89b", + "5ad6b472", + "5a88f54c", + "e029ac71", + "e019a5e6", + "47b0acfd", + "ed93fa9b", + "e8d3c48d", + "283b57cc", + "f8d56629", + "79132e28", + "785f0191", + "ed756055", + "f7960e44", + "e3d35e8c", + "15056dd4", + "88f46dba", + "03a16125", + "0564f0bd", + "c3eb9e15", + "3c9057a2", + "97271aec", + "a93a072a", + "1b3f6d9b", + "1e6321f5", + "f59c66fb", + "26dcf319", + "7533d928", + "b155fdf5", + "03563482", + "8aba3cbb", + "28517711", + "c20ad9f8", + "abcc5167", + "ccad925f", + "4de81751", + "3830dc8e", + "379d5862", + "9320f991", + "ea7a90c2", + "fb3e7bce", + "5121ce64", + "774fbe32", + "a8b6e37e", + "c3293d46", + "48de5369", + "6413e680", + "a2ae0810", + "dd6db224", + "69852dfd", + "09072166", + "b39a460a", + "6445c0dd", + "586cdecf", + "1c20c8ae", + "5bbef7dd", + "1b588d40", + "ccd2017f", + "6bb4e3bb", + "dda26a7e", + "3a59ff45", + "3e350a44", + "bcb4cdd5", + "72eacea8", + "fa6484bb", + "8d6612ae", + "bf3c6f47", + "d29be463", + "542f5d9e", + "aec2771b", + "f64e6370", + "740e0d8d", + "e75b1357", + "f8721671", + "af537d5d", + "4040cb08", + "4eb4e2cc", + "34d2466a", + "0115af84", + "e1b00428", + "95983a1d", + "06b89fb4", + "ce6ea048", + "6f3f3b82", + "3520ab82", + "011a1d4b", + "277227f8", + "611560b1", + "e7933fdc", + "bb3a792b", + "344525bd", + "a08839e1", + "51ce794b", + "2f32c9b7", + "a01fbac9", + "e01cc87e", + "bcc7d1f6", + "cf0111c3", + "a1e8aac7", + "1a908749", + "d44fbd9a", + "d0dadecb", + "d50ada38", + "0339c32a", + "c6913667", + "8df9317c", + "e0b12b4f", + "f79e59b7", + "43f5bb3a", + "f2d519ff", + "27d9459c", + "bf97222c", + "15e6fc2a", + "0f91fc71", + "9b941525", + "fae59361", + "ceb69ceb", + "c2a86459", + "12baa8d1", + "b6c1075e", + "e3056a0c", + "10d25065", + "cb03a442", + "e0ec6e0e", + "1698db3b", + "4c98a0be", + "3278e964", + "9f1f9532", + "e0d392df", + "d3a0342b", + "8971f21e", + "1b0a7441", + "4ba3348c", + "c5be7120", + "c37632d8", + "df359f8d", + "9b992f2e", + "e60b6f47", + "0fe3f11d", + "e54cda54", + "1edad891", + "ce6279cf", + "cd3e7e6f", + "1618b166", + "fd2c1d05", + "848fd2c5", + "f6fb2299", + "f523f357", + "a6327623", + "93a83531", + "56cccd02", + "acf08162", + "5a75ebb5", + "6e163697", + "88d273cc", + "de966292", + "81b949d0", + "4c50901b", + "71c65614", + "e6c6c7bd", + "327a140a", + "45e1d006", + "c3f27b9a", + "c9aa53fd", + "62a80f00", + "bb25bfe2", + "35bdd2f6", + "71126905", + "b2040222", + "b6cbcf7c", + "cd769c2b", + "53113ec0", + "1640e3d3", + "38abbd60", + "2547adf0", + "ba38209c", + "f746ce76", + "77afa1c5", + "20756060", + "85cbfe4e", + "8ae88dd8", + "7aaaf9b0", + "4cf9aa7e", + "1948c25c", + "02fb8a8c", + "01c36ae4", + "d6ebe1f9", + "90d4f869", + "a65cdea0", + "3f09252d", + "c208e69f", + "b74e6132", + "ce77e25b", + "578fdfe3", + "3ac372e6", + }, + }; + + // Initializing subkeys with digits of pi + String[] subKeys = { + "243f6a88", + "85a308d3", + "13198a2e", + "03707344", + "a4093822", + "299f31d0", + "082efa98", + "ec4e6c89", + "452821e6", + "38d01377", + "be5466cf", + "34e90c6c", + "c0ac29b7", + "c97c50dd", + "3f84d5b5", + "b5470917", + "9216d5d9", + "8979fb1b", + }; + + // Initializing modVal to 2^32 + long modVal = 4294967296L; + + /** + * This method returns binary representation of the hexadecimal number passed as parameter + * + * @param hex Number for which binary representation is required + * @return String object which is a binary representation of the hex number passed as parameter + */ + private String hexToBin(String hex) { + StringBuilder binary = new StringBuilder(); + long num; + String binary4B; + int n = hex.length(); + for (int i = 0; i < n; i++) { + num = Long.parseUnsignedLong(hex.charAt(i) + "", 16); + binary4B = Long.toBinaryString(num); + + binary4B = "0000" + binary4B; + + binary4B = binary4B.substring(binary4B.length() - 4); + binary.append(binary4B); + } + return binary.toString(); + } + + /** + * This method returns hexadecimal representation of the binary number passed as parameter + * + * @param binary Number for which hexadecimal representation is required + * @return String object which is a hexadecimal representation of the binary number passed as + * parameter + */ + private String binToHex(String binary) { + long num = Long.parseUnsignedLong(binary, 2); + StringBuilder hex = new StringBuilder(Long.toHexString(num)); + while (hex.length() < (binary.length() / 4)) { + hex.insert(0, "0"); + } + + return hex.toString(); + } + + /** + * This method returns a string obtained by XOR-ing two strings of same length passed a method + * parameters + * + * @param String a and b are string objects which will be XORed and are to be of same length + * @return String object obtained by XOR operation on String a and String b + * */ + private String xor(String a, String b) { + a = hexToBin(a); + b = hexToBin(b); + StringBuilder ans = new StringBuilder(); + for (int i = 0; i < a.length(); i++) { + ans.append((char) (((a.charAt(i) - '0') ^ (b.charAt(i) - '0')) + '0')); + } + ans = new StringBuilder(binToHex(ans.toString())); + return ans.toString(); + } + + /** + * This method returns addition of two hexadecimal numbers passed as parameters and moded with + * 2^32 + * + * @param String a and b are hexadecimal numbers + * @return String object which is a is addition that is then moded with 2^32 of hex numbers + * passed as parameters + */ + private String addBin(String a, String b) { + String ans = ""; + long n1 = Long.parseUnsignedLong(a, 16); + long n2 = Long.parseUnsignedLong(b, 16); + n1 = (n1 + n2) % modVal; + ans = Long.toHexString(n1); + ans = "00000000" + ans; + return ans.substring(ans.length() - 8); + } + + /*F-function splits the 32-bit input into four 8-bit quarters + and uses the quarters as input to the S-boxes. + The S-boxes accept 8-bit input and produce 32-bit output. + The outputs are added modulo 232 and XORed to produce the final 32-bit output + */ + private String f(String plainText) { + String[] a = new String[4]; + String ans = ""; + for (int i = 0; i < 8; i += 2) { + // column number for S-box is a 8-bit value + long col = Long.parseUnsignedLong(hexToBin(plainText.substring(i, i + 2)), 2); + a[i / 2] = sBox[i / 2][(int) col]; + } + ans = addBin(a[0], a[1]); + ans = xor(ans, a[2]); + ans = addBin(ans, a[3]); + return ans; + } + + // generate subkeys + private void keyGenerate(String key) { + int j = 0; + for (int i = 0; i < subKeys.length; i++) { + // XOR-ing 32-bit parts of the key with initial subkeys + subKeys[i] = xor(subKeys[i], key.substring(j, j + 8)); + + j = (j + 8) % key.length(); + } + } + + // round function + private String round(int time, String plainText) { + String left; + String right; + left = plainText.substring(0, 8); + right = plainText.substring(8, 16); + left = xor(left, subKeys[time]); + + // output from F function + String fOut = f(left); + + right = xor(fOut, right); + + // swap left and right + return right + left; + } + + /** + * This method returns cipher text for the plaintext passed as the first parameter generated + * using the key passed as the second parameter + * + * @param String plainText is the text which is to be encrypted + * @param String key is the key which is to be used for generating cipher text + * @return String cipherText is the encrypted value + */ + String encrypt(String plainText, String key) { + // generating key + keyGenerate(key); + + for (int i = 0; i < 16; i++) { + plainText = round(i, plainText); + } + + // postprocessing + String right = plainText.substring(0, 8); + String left = plainText.substring(8, 16); + right = xor(right, subKeys[16]); + left = xor(left, subKeys[17]); + return left + right; + } + + /** + * This method returns plaintext for the ciphertext passed as the first parameter decoded + * using the key passed as the second parameter + * + * @param String ciphertext is the text which is to be decrypted + * @param String key is the key which is to be used for generating cipher text + * @return String plainText is the decrypted text + */ + String decrypt(String cipherText, String key) { + // generating key + keyGenerate(key); + + for (int i = 17; i > 1; i--) { + cipherText = round(i, cipherText); + } + + // postprocessing + String right = cipherText.substring(0, 8); + String left = cipherText.substring(8, 16); + right = xor(right, subKeys[1]); + left = xor(left, subKeys[0]); + return left + right; + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/Caesar.java b/src/main/java/com/thealgorithms/ciphers/Caesar.java new file mode 100644 index 000000000000..23535bc2b5d2 --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/Caesar.java @@ -0,0 +1,99 @@ +package com.thealgorithms.ciphers; + +/** + * A Java implementation of Caesar Cipher. /It is a type of substitution cipher + * in which each letter in the plaintext is replaced by a letter some fixed + * number of positions down the alphabet. / + * + * @author FAHRI YARDIMCI + * @author khalil2535 + */ +public class Caesar { + private static char normalizeShift(final int shift) { + return (char) (shift % 26); + } + + /** + * Encrypt text by shifting every Latin char by add number shift for ASCII + * Example : A + 1 -> B + * + * @return Encrypted message + */ + public String encode(String message, int shift) { + StringBuilder encoded = new StringBuilder(); + + final char shiftChar = normalizeShift(shift); + + final int length = message.length(); + for (int i = 0; i < length; i++) { + // int current = message.charAt(i); //using char to shift characters because + // ascii + // is in-order latin alphabet + char current = message.charAt(i); // Java law : char + int = char + + if (isCapitalLatinLetter(current)) { + current += shiftChar; + encoded.append((char) (current > 'Z' ? current - 26 : current)); // 26 = number of latin letters + } else if (isSmallLatinLetter(current)) { + current += shiftChar; + encoded.append((char) (current > 'z' ? current - 26 : current)); // 26 = number of latin letters + } else { + encoded.append(current); + } + } + return encoded.toString(); + } + + /** + * Decrypt message by shifting back every Latin char to previous the ASCII + * Example : B - 1 -> A + * + * @return message + */ + public String decode(String encryptedMessage, int shift) { + StringBuilder decoded = new StringBuilder(); + + final char shiftChar = normalizeShift(shift); + + final int length = encryptedMessage.length(); + for (int i = 0; i < length; i++) { + char current = encryptedMessage.charAt(i); + if (isCapitalLatinLetter(current)) { + current -= shiftChar; + decoded.append((char) (current < 'A' ? current + 26 : current)); // 26 = number of latin letters + } else if (isSmallLatinLetter(current)) { + current -= shiftChar; + decoded.append((char) (current < 'a' ? current + 26 : current)); // 26 = number of latin letters + } else { + decoded.append(current); + } + } + return decoded.toString(); + } + + /** + * @return true if character is capital Latin letter or false for others + */ + private static boolean isCapitalLatinLetter(char c) { + return c >= 'A' && c <= 'Z'; + } + + /** + * @return true if character is small Latin letter or false for others + */ + private static boolean isSmallLatinLetter(char c) { + return c >= 'a' && c <= 'z'; + } + + /** + * @return string array which contains all the possible decoded combination. + */ + public String[] bruteforce(String encryptedMessage) { + String[] listOfAllTheAnswers = new String[27]; + for (int i = 0; i <= 26; i++) { + listOfAllTheAnswers[i] = decode(encryptedMessage, i); + } + + return listOfAllTheAnswers; + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/ColumnarTranspositionCipher.java b/src/main/java/com/thealgorithms/ciphers/ColumnarTranspositionCipher.java new file mode 100644 index 000000000000..b6b889b079ca --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/ColumnarTranspositionCipher.java @@ -0,0 +1,186 @@ +package com.thealgorithms.ciphers; + +import java.util.Objects; + +/** + * Columnar Transposition Cipher Encryption and Decryption. + * + * @author <a href="https://github.com/freitzzz">freitzzz</a> + */ +public final class ColumnarTranspositionCipher { + private ColumnarTranspositionCipher() { + } + + private static String keyword; + private static Object[][] table; + private static String abecedarium; + public static final String ABECEDARIUM = "abcdefghijklmnopqrstuvwxyzABCDEFG" + + "HIJKLMNOPQRSTUVWXYZ0123456789,.;:-@"; + private static final String ENCRYPTION_FIELD = "≈"; + private static final char ENCRYPTION_FIELD_CHAR = '≈'; + + /** + * Encrypts a certain String with the Columnar Transposition Cipher Rule + * + * @param word Word being encrypted + * @param keyword String with keyword being used + * @return a String with the word encrypted by the Columnar Transposition + * Cipher Rule + */ + public static String encrypt(final String word, final String keyword) { + ColumnarTranspositionCipher.keyword = keyword; + abecedariumBuilder(); + table = tableBuilder(word); + Object[][] sortedTable = sortTable(table); + StringBuilder wordEncrypted = new StringBuilder(); + for (int i = 0; i < sortedTable[0].length; i++) { + for (int j = 1; j < sortedTable.length; j++) { + wordEncrypted.append(sortedTable[j][i]); + } + } + return wordEncrypted.toString(); + } + + /** + * Encrypts a certain String with the Columnar Transposition Cipher Rule + * + * @param word Word being encrypted + * @param keyword String with keyword being used + * @param abecedarium String with the abecedarium being used. null for + * default one + * @return a String with the word encrypted by the Columnar Transposition + * Cipher Rule + */ + public static String encrypt(String word, String keyword, String abecedarium) { + ColumnarTranspositionCipher.keyword = keyword; + ColumnarTranspositionCipher.abecedarium = Objects.requireNonNullElse(abecedarium, ABECEDARIUM); + table = tableBuilder(word); + Object[][] sortedTable = sortTable(table); + + StringBuilder wordEncrypted = new StringBuilder(); + for (int i = 0; i < sortedTable[0].length; i++) { + for (int j = 1; j < sortedTable.length; j++) { + wordEncrypted.append(sortedTable[j][i]); + } + } + return wordEncrypted.toString(); + } + + /** + * Decrypts a certain encrypted String with the Columnar Transposition + * Cipher Rule + * + * @return a String decrypted with the word encrypted by the Columnar + * Transposition Cipher Rule + */ + public static String decrypt() { + StringBuilder wordDecrypted = new StringBuilder(); + for (int i = 1; i < table.length; i++) { + for (Object item : table[i]) { + wordDecrypted.append(item); + } + } + return wordDecrypted.toString().replaceAll(ENCRYPTION_FIELD, ""); + } + + /** + * Builds a table with the word to be encrypted in rows by the Columnar + * Transposition Cipher Rule + * + * @return An Object[][] with the word to be encrypted filled in rows and + * columns + */ + private static Object[][] tableBuilder(String word) { + Object[][] table = new Object[numberOfRows(word) + 1][keyword.length()]; + char[] wordInChars = word.toCharArray(); + // Fills in the respective numbers for the column + table[0] = findElements(); + int charElement = 0; + for (int i = 1; i < table.length; i++) { + for (int j = 0; j < table[i].length; j++) { + if (charElement < wordInChars.length) { + table[i][j] = wordInChars[charElement]; + charElement++; + } else { + table[i][j] = ENCRYPTION_FIELD_CHAR; + } + } + } + return table; + } + + /** + * Determines the number of rows the table should have regarding the + * Columnar Transposition Cipher Rule + * + * @return an int with the number of rows that the table should have in + * order to respect the Columnar Transposition Cipher Rule. + */ + private static int numberOfRows(String word) { + if (word.length() % keyword.length() != 0) { + return (word.length() / keyword.length()) + 1; + } else { + return word.length() / keyword.length(); + } + } + + /** + * @return charValues + */ + private static Object[] findElements() { + Object[] charValues = new Object[keyword.length()]; + for (int i = 0; i < charValues.length; i++) { + int charValueIndex = abecedarium.indexOf(keyword.charAt(i)); + charValues[i] = charValueIndex > -1 ? charValueIndex : null; + } + return charValues; + } + + /** + * @return tableSorted + */ + private static Object[][] sortTable(Object[][] table) { + Object[][] tableSorted = new Object[table.length][table[0].length]; + for (int i = 0; i < tableSorted.length; i++) { + System.arraycopy(table[i], 0, tableSorted[i], 0, tableSorted[i].length); + } + for (int i = 0; i < tableSorted[0].length; i++) { + for (int j = i + 1; j < tableSorted[0].length; j++) { + if ((int) tableSorted[0][i] > (int) table[0][j]) { + Object[] column = getColumn(tableSorted, tableSorted.length, i); + switchColumns(tableSorted, j, i, column); + } + } + } + return tableSorted; + } + + /** + * @return columnArray + */ + private static Object[] getColumn(Object[][] table, int rows, int column) { + Object[] columnArray = new Object[rows]; + for (int i = 0; i < rows; i++) { + columnArray[i] = table[i][column]; + } + return columnArray; + } + + private static void switchColumns(Object[][] table, int firstColumnIndex, int secondColumnIndex, Object[] columnToSwitch) { + for (int i = 0; i < table.length; i++) { + table[i][secondColumnIndex] = table[i][firstColumnIndex]; + table[i][firstColumnIndex] = columnToSwitch[i]; + } + } + + /** + * Creates an abecedarium with all available ascii values. + */ + private static void abecedariumBuilder() { + StringBuilder t = new StringBuilder(); + for (int i = 0; i < 256; i++) { + t.append((char) i); + } + abecedarium = t.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/DES.java b/src/main/java/com/thealgorithms/ciphers/DES.java new file mode 100644 index 000000000000..7f3eed70f3c2 --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/DES.java @@ -0,0 +1,250 @@ +package com.thealgorithms.ciphers; + +/** + * This class is build to demonstrate the application of the DES-algorithm + * (https://en.wikipedia.org/wiki/Data_Encryption_Standard) on a plain English message. The supplied + * key must be in form of a 64 bit binary String. + */ +public class DES { + + private String key; + private final String[] subKeys; + + private void sanitize(String key) { + int length = key.length(); + if (length != 64) { + throw new IllegalArgumentException("DES key must be supplied as a 64 character binary string"); + } + } + + DES(String key) { + sanitize(key); + this.key = key; + subKeys = getSubkeys(key); + } + + public String getKey() { + return this.key; + } + + public void setKey(String key) { + sanitize(key); + this.key = key; + } + + // Permutation table to convert initial 64-bit key to 56 bit key + private static final int[] PC1 = {57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4}; + + // Lookup table used to shift the initial key, in order to generate the subkeys + private static final int[] KEY_SHIFTS = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1}; + + // Table to convert the 56 bit subkeys to 48 bit subkeys + private static final int[] PC2 = {14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32}; + + // Initial permutation of each 64 but message block + private static final int[] IP = {58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7}; + + // Expansion table to convert right half of message blocks from 32 bits to 48 bits + private static final int[] EXPANSION = {32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, 8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17, 16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25, 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1}; + + // The eight substitution boxes are defined below + private static final int[][] S1 = {{14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7}, {0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8}, {4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0}, {15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}}; + + private static final int[][] S2 = {{15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10}, {3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5}, {0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15}, {13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}}; + + private static final int[][] S3 = {{10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8}, {13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1}, {13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7}, {1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}}; + + private static final int[][] S4 = {{7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15}, {13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9}, {10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4}, {3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}}; + + private static final int[][] S5 = {{2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9}, {14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6}, {4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14}, {11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}}; + + private static final int[][] S6 = {{12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11}, {10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8}, {9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6}, {4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13}}; + + private static final int[][] S7 = {{4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1}, {13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6}, {1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2}, {6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}}; + + private static final int[][] S8 = {{13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7}, {1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2}, {7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8}, {2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11}}; + + private static final int[][][] S = {S1, S2, S3, S4, S5, S6, S7, S8}; + + // Permutation table, used in the Feistel function post s-box usage + static final int[] PERMUTATION = {16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25}; + + // Table used for final inversion of the message box after 16 rounds of Feistel Function + static final int[] IP_INVERSE = {40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25}; + + private String[] getSubkeys(String originalKey) { + StringBuilder permutedKey = new StringBuilder(); // Initial permutation of keys via pc1 + int i; + int j; + for (i = 0; i < 56; i++) { + permutedKey.append(originalKey.charAt(PC1[i] - 1)); + } + String[] subKeys = new String[16]; + String initialPermutedKey = permutedKey.toString(); + String c0 = initialPermutedKey.substring(0, 28); + String d0 = initialPermutedKey.substring(28); + + // We will now operate on the left and right halves of the permutedKey + for (i = 0; i < 16; i++) { + String cN = c0.substring(KEY_SHIFTS[i]) + c0.substring(0, KEY_SHIFTS[i]); + String dN = d0.substring(KEY_SHIFTS[i]) + d0.substring(0, KEY_SHIFTS[i]); + subKeys[i] = cN + dN; + c0 = cN; // Re-assign the values to create running permutation + d0 = dN; + } + + // Let us shrink the keys to 48 bits (well, characters here) using pc2 + for (i = 0; i < 16; i++) { + String key = subKeys[i]; + permutedKey.setLength(0); + for (j = 0; j < 48; j++) { + permutedKey.append(key.charAt(PC2[j] - 1)); + } + subKeys[i] = permutedKey.toString(); + } + + return subKeys; + } + + private String xOR(String a, String b) { + int i; + int l = a.length(); + StringBuilder xor = new StringBuilder(); + for (i = 0; i < l; i++) { + int firstBit = a.charAt(i) - 48; // 48 is '0' in ascii + int secondBit = b.charAt(i) - 48; + xor.append((firstBit ^ secondBit)); + } + return xor.toString(); + } + + private String createPaddedString(String s, int desiredLength, char pad) { + int i; + int l = s.length(); + StringBuilder paddedString = new StringBuilder(); + int diff = desiredLength - l; + for (i = 0; i < diff; i++) { + paddedString.append(pad); + } + return paddedString.toString(); + } + + private String pad(String s, int desiredLength) { + return createPaddedString(s, desiredLength, '0') + s; + } + + private String padLast(String s, int desiredLength) { + return s + createPaddedString(s, desiredLength, '\u0000'); + } + + private String feistel(String messageBlock, String key) { + int i; + StringBuilder expandedKey = new StringBuilder(); + for (i = 0; i < 48; i++) { + expandedKey.append(messageBlock.charAt(EXPANSION[i] - 1)); + } + String mixedKey = xOR(expandedKey.toString(), key); + StringBuilder substitutedString = new StringBuilder(); + + // Let us now use the s-boxes to transform each 6 bit (length here) block to 4 bits + for (i = 0; i < 48; i += 6) { + String block = mixedKey.substring(i, i + 6); + int row = (block.charAt(0) - 48) * 2 + (block.charAt(5) - 48); + int col = (block.charAt(1) - 48) * 8 + (block.charAt(2) - 48) * 4 + (block.charAt(3) - 48) * 2 + (block.charAt(4) - 48); + String substitutedBlock = pad(Integer.toBinaryString(S[i / 6][row][col]), 4); + substitutedString.append(substitutedBlock); + } + + StringBuilder permutedString = new StringBuilder(); + for (i = 0; i < 32; i++) { + permutedString.append(substitutedString.charAt(PERMUTATION[i] - 1)); + } + + return permutedString.toString(); + } + + private String encryptBlock(String message, String[] keys) { + StringBuilder permutedMessage = new StringBuilder(); + int i; + for (i = 0; i < 64; i++) { + permutedMessage.append(message.charAt(IP[i] - 1)); + } + String e0 = permutedMessage.substring(0, 32); + String f0 = permutedMessage.substring(32); + + // Iterate 16 times + for (i = 0; i < 16; i++) { + String eN = f0; // Previous Right block + String fN = xOR(e0, feistel(f0, keys[i])); + e0 = eN; + f0 = fN; + } + + String combinedBlock = f0 + e0; // Reverse the 16th block + permutedMessage.setLength(0); + for (i = 0; i < 64; i++) { + permutedMessage.append(combinedBlock.charAt(IP_INVERSE[i] - 1)); + } + return permutedMessage.toString(); + } + + // To decode, we follow the same process as encoding, but with reversed keys + private String decryptBlock(String message, String[] keys) { + String[] reversedKeys = new String[keys.length]; + for (int i = 0; i < keys.length; i++) { + reversedKeys[i] = keys[keys.length - i - 1]; + } + return encryptBlock(message, reversedKeys); + } + + /** + * @param message Message to be encrypted + * @return The encrypted message, as a binary string + */ + public String encrypt(String message) { + StringBuilder encryptedMessage = new StringBuilder(); + int l = message.length(); + int i; + int j; + if (l % 8 != 0) { + int desiredLength = (l / 8 + 1) * 8; + l = desiredLength; + message = padLast(message, desiredLength); + } + + for (i = 0; i < l; i += 8) { + String block = message.substring(i, i + 8); + StringBuilder bitBlock = new StringBuilder(); + byte[] bytes = block.getBytes(); + for (j = 0; j < 8; j++) { + bitBlock.append(pad(Integer.toBinaryString(bytes[j]), 8)); + } + encryptedMessage.append(encryptBlock(bitBlock.toString(), subKeys)); + } + return encryptedMessage.toString(); + } + + /** + * @param message The encrypted string. Expects it to be a multiple of 64 bits, in binary format + * @return The decrypted String, in plain English + */ + public String decrypt(String message) { + StringBuilder decryptedMessage = new StringBuilder(); + int l = message.length(); + int i; + int j; + if (l % 64 != 0) { + throw new IllegalArgumentException("Encrypted message should be a multiple of 64 characters in length"); + } + for (i = 0; i < l; i += 64) { + String block = message.substring(i, i + 64); + String result = decryptBlock(block, subKeys); + byte[] res = new byte[8]; + for (j = 0; j < 64; j += 8) { + res[j / 8] = (byte) Integer.parseInt(result.substring(j, j + 8), 2); + } + decryptedMessage.append(new String(res)); + } + return decryptedMessage.toString().replace("\0", ""); // Get rid of the null bytes used for padding + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/DiffieHellman.java b/src/main/java/com/thealgorithms/ciphers/DiffieHellman.java new file mode 100644 index 000000000000..7470b40e001a --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/DiffieHellman.java @@ -0,0 +1,36 @@ +package com.thealgorithms.ciphers; + +import java.math.BigInteger; + +public final class DiffieHellman { + + private final BigInteger base; + private final BigInteger secret; + private final BigInteger prime; + + // Constructor to initialize base, secret, and prime + public DiffieHellman(BigInteger base, BigInteger secret, BigInteger prime) { + // Check for non-null and positive values + if (base == null || secret == null || prime == null || base.signum() <= 0 || secret.signum() <= 0 || prime.signum() <= 0) { + throw new IllegalArgumentException("Base, secret, and prime must be non-null and positive values."); + } + this.base = base; + this.secret = secret; + this.prime = prime; + } + + // Method to calculate public value (g^x mod p) + public BigInteger calculatePublicValue() { + // Returns g^x mod p + return base.modPow(secret, prime); + } + + // Method to calculate the shared secret key (otherPublic^secret mod p) + public BigInteger calculateSharedSecret(BigInteger otherPublicValue) { + if (otherPublicValue == null || otherPublicValue.signum() <= 0) { + throw new IllegalArgumentException("Other public value must be non-null and positive."); + } + // Returns b^x mod p or a^y mod p + return otherPublicValue.modPow(secret, prime); + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/ECC.java b/src/main/java/com/thealgorithms/ciphers/ECC.java new file mode 100644 index 000000000000..7b1e37f0e1e1 --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/ECC.java @@ -0,0 +1,236 @@ +package com.thealgorithms.ciphers; + +import java.math.BigInteger; +import java.security.SecureRandom; + +/** + * ECC - Elliptic Curve Cryptography + * Elliptic Curve Cryptography is a public-key cryptography method that uses the algebraic structure of + * elliptic curves over finite fields. ECC provides a higher level of security with smaller key sizes compared + * to other public-key methods like RSA, making it particularly suitable for environments where computational + * resources are limited, such as mobile devices and embedded systems. + * + * This class implements elliptic curve cryptography, providing encryption and decryption + * functionalities based on public and private key pairs. + * + * @author xuyang + */ +public class ECC { + + private BigInteger privateKey; // Private key used for decryption + private ECPoint publicKey; // Public key used for encryption + private EllipticCurve curve; // Elliptic curve used in cryptography + private ECPoint basePoint; // Base point G on the elliptic curve + + public ECC(int bits) { + generateKeys(bits); // Generates public-private key pair + } + + public EllipticCurve getCurve() { + return curve; // Returns the elliptic curve + } + + public void setCurve(EllipticCurve curve) { + this.curve = curve; + } + + // Getter and Setter for private key + public BigInteger getPrivateKey() { + return privateKey; + } + + public void setPrivateKey(BigInteger privateKey) { + this.privateKey = privateKey; + } + + /** + * Encrypts the message using the public key. + * The message is transformed into an ECPoint and encrypted with elliptic curve operations. + * + * @param message The plain message to be encrypted + * @return The encrypted message as an array of ECPoints (R, S) + */ + public ECPoint[] encrypt(String message) { + BigInteger m = new BigInteger(message.getBytes()); // Convert message to BigInteger + SecureRandom r = new SecureRandom(); // Generate random value for k + BigInteger k = new BigInteger(curve.getFieldSize(), r); // Generate random scalar k + + // Calculate point r = k * G, where G is the base point + ECPoint rPoint = basePoint.multiply(k, curve.getP(), curve.getA()); + + // Calculate point s = k * publicKey + encodedMessage + ECPoint sPoint = publicKey.multiply(k, curve.getP(), curve.getA()).add(curve.encodeMessage(m), curve.getP(), curve.getA()); + + return new ECPoint[] {rPoint, sPoint}; // Return encrypted message as two ECPoints + } + + /** + * Decrypts the encrypted message using the private key. + * The decryption process is the reverse of encryption, recovering the original message. + * + * @param encryptedMessage The encrypted message as an array of ECPoints (R, S) + * @return The decrypted plain message as a String + */ + public String decrypt(ECPoint[] encryptedMessage) { + ECPoint rPoint = encryptedMessage[0]; // First part of ciphertext + ECPoint sPoint = encryptedMessage[1]; // Second part of ciphertext + + // Perform decryption: s - r * privateKey + ECPoint decodedMessage = sPoint.subtract(rPoint.multiply(privateKey, curve.getP(), curve.getA()), curve.getP(), curve.getA()); + + BigInteger m = curve.decodeMessage(decodedMessage); // Decode the message from ECPoint + + return new String(m.toByteArray()); // Convert BigInteger back to String + } + + /** + * Generates a new public-private key pair for encryption and decryption. + * + * @param bits The size (in bits) of the keys to generate + */ + public final void generateKeys(int bits) { + SecureRandom r = new SecureRandom(); + curve = new EllipticCurve(bits); // Initialize a new elliptic curve + basePoint = curve.getBasePoint(); // Set the base point G + + // Generate private key as a random BigInteger + privateKey = new BigInteger(bits, r); + + // Generate public key as the point publicKey = privateKey * G + publicKey = basePoint.multiply(privateKey, curve.getP(), curve.getA()); + } + + /** + * Class representing an elliptic curve with the form y^2 = x^3 + ax + b. + */ + public static class EllipticCurve { + private final BigInteger a; // Coefficient a in the curve equation + private final BigInteger b; // Coefficient b in the curve equation + private final BigInteger p; // Prime number p, defining the finite field + private final ECPoint basePoint; // Base point G on the curve + + // Constructor with explicit parameters for a, b, p, and base point + public EllipticCurve(BigInteger a, BigInteger b, BigInteger p, ECPoint basePoint) { + this.a = a; + this.b = b; + this.p = p; + this.basePoint = basePoint; + } + + // Constructor that randomly generates the curve parameters + public EllipticCurve(int bits) { + SecureRandom r = new SecureRandom(); + this.p = BigInteger.probablePrime(bits, r); // Random prime p + this.a = new BigInteger(bits, r); // Random coefficient a + this.b = new BigInteger(bits, r); // Random coefficient b + this.basePoint = new ECPoint(BigInteger.valueOf(4), BigInteger.valueOf(8)); // Fixed base point G + } + + public ECPoint getBasePoint() { + return basePoint; + } + + public BigInteger getP() { + return p; + } + + public BigInteger getA() { + return a; + } + + public BigInteger getB() { + return b; + } + + public int getFieldSize() { + return p.bitLength(); + } + + public ECPoint encodeMessage(BigInteger message) { + // Simple encoding of a message as an ECPoint (this is a simplified example) + return new ECPoint(message, message); + } + + public BigInteger decodeMessage(ECPoint point) { + return point.getX(); // Decode the message from ECPoint (simplified) + } + } + + /** + * Class representing a point on the elliptic curve. + */ + public static class ECPoint { + private final BigInteger x; // X-coordinate of the point + private final BigInteger y; // Y-coordinate of the point + + public ECPoint(BigInteger x, BigInteger y) { + this.x = x; + this.y = y; + } + + public BigInteger getX() { + return x; + } + + public BigInteger getY() { + return y; + } + + @Override + public String toString() { + return "ECPoint(x=" + x.toString() + ", y=" + y.toString() + ")"; + } + + /** + * Add two points on the elliptic curve. + */ + public ECPoint add(ECPoint other, BigInteger p, BigInteger a) { + if (this.x.equals(BigInteger.ZERO) && this.y.equals(BigInteger.ZERO)) { + return other; // If this point is the identity, return the other point + } + if (other.x.equals(BigInteger.ZERO) && other.y.equals(BigInteger.ZERO)) { + return this; // If the other point is the identity, return this point + } + + BigInteger lambda; + if (this.equals(other)) { + // Special case: point doubling + lambda = this.x.pow(2).multiply(BigInteger.valueOf(3)).add(a).multiply(this.y.multiply(BigInteger.valueOf(2)).modInverse(p)).mod(p); + } else { + // General case: adding two different points + lambda = other.y.subtract(this.y).multiply(other.x.subtract(this.x).modInverse(p)).mod(p); + } + + BigInteger xr = lambda.pow(2).subtract(this.x).subtract(other.x).mod(p); + BigInteger yr = lambda.multiply(this.x.subtract(xr)).subtract(this.y).mod(p); + + return new ECPoint(xr, yr); + } + + /** + * Subtract two points on the elliptic curve. + */ + public ECPoint subtract(ECPoint other, BigInteger p, BigInteger a) { + ECPoint negOther = new ECPoint(other.x, p.subtract(other.y)); // Negate the Y coordinate + return this.add(negOther, p, a); // Add the negated point + } + + /** + * Multiply a point by a scalar (repeated addition). + */ + public ECPoint multiply(BigInteger k, BigInteger p, BigInteger a) { + ECPoint result = new ECPoint(BigInteger.ZERO, BigInteger.ZERO); // Identity point + ECPoint addend = this; + + while (k.signum() > 0) { + if (k.testBit(0)) { + result = result.add(addend, p, a); // Add the current point + } + addend = addend.add(addend, p, a); // Double the point + k = k.shiftRight(1); // Divide k by 2 + } + + return result; + } + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/HillCipher.java b/src/main/java/com/thealgorithms/ciphers/HillCipher.java new file mode 100644 index 000000000000..01b1aeb8bc6c --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/HillCipher.java @@ -0,0 +1,103 @@ +package com.thealgorithms.ciphers; + +public class HillCipher { + + // Encrypts the message using the key matrix + public String encrypt(String message, int[][] keyMatrix) { + message = message.toUpperCase().replaceAll("[^A-Z]", ""); + int matrixSize = keyMatrix.length; + validateDeterminant(keyMatrix, matrixSize); + + StringBuilder cipherText = new StringBuilder(); + int[] messageVector = new int[matrixSize]; + int[] cipherVector = new int[matrixSize]; + int index = 0; + + while (index < message.length()) { + for (int i = 0; i < matrixSize; i++) { + if (index < message.length()) { + messageVector[i] = message.charAt(index++) - 'A'; + } else { + messageVector[i] = 'X' - 'A'; // Padding with 'X' if needed + } + } + + for (int i = 0; i < matrixSize; i++) { + cipherVector[i] = 0; + for (int j = 0; j < matrixSize; j++) { + cipherVector[i] += keyMatrix[i][j] * messageVector[j]; + } + cipherVector[i] = cipherVector[i] % 26; + cipherText.append((char) (cipherVector[i] + 'A')); + } + } + + return cipherText.toString(); + } + + // Decrypts the message using the inverse key matrix + public String decrypt(String message, int[][] inverseKeyMatrix) { + message = message.toUpperCase().replaceAll("[^A-Z]", ""); + int matrixSize = inverseKeyMatrix.length; + validateDeterminant(inverseKeyMatrix, matrixSize); + + StringBuilder plainText = new StringBuilder(); + int[] messageVector = new int[matrixSize]; + int[] plainVector = new int[matrixSize]; + int index = 0; + + while (index < message.length()) { + for (int i = 0; i < matrixSize; i++) { + if (index < message.length()) { + messageVector[i] = message.charAt(index++) - 'A'; + } else { + messageVector[i] = 'X' - 'A'; // Padding with 'X' if needed + } + } + + for (int i = 0; i < matrixSize; i++) { + plainVector[i] = 0; + for (int j = 0; j < matrixSize; j++) { + plainVector[i] += inverseKeyMatrix[i][j] * messageVector[j]; + } + plainVector[i] = plainVector[i] % 26; + plainText.append((char) (plainVector[i] + 'A')); + } + } + + return plainText.toString(); + } + + // Validates that the determinant of the key matrix is not zero modulo 26 + private void validateDeterminant(int[][] keyMatrix, int n) { + int det = determinant(keyMatrix, n) % 26; + if (det == 0) { + throw new IllegalArgumentException("Invalid key matrix. Determinant is zero modulo 26."); + } + } + + // Computes the determinant of a matrix recursively + private int determinant(int[][] matrix, int n) { + int det = 0; + if (n == 1) { + return matrix[0][0]; + } + int sign = 1; + int[][] subMatrix = new int[n - 1][n - 1]; + for (int x = 0; x < n; x++) { + int subI = 0; + for (int i = 1; i < n; i++) { + int subJ = 0; + for (int j = 0; j < n; j++) { + if (j != x) { + subMatrix[subI][subJ++] = matrix[i][j]; + } + } + subI++; + } + det += sign * matrix[0][x] * determinant(subMatrix, n - 1); + sign = -sign; + } + return det; + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/MonoAlphabetic.java b/src/main/java/com/thealgorithms/ciphers/MonoAlphabetic.java new file mode 100644 index 000000000000..1d5b7110a6f3 --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/MonoAlphabetic.java @@ -0,0 +1,48 @@ +package com.thealgorithms.ciphers; + +public final class MonoAlphabetic { + + private MonoAlphabetic() { + throw new UnsupportedOperationException("Utility class"); + } + + // Encryption method + public static String encrypt(String data, String key) { + if (!data.matches("[A-Z]+")) { + throw new IllegalArgumentException("Input data contains invalid characters. Only uppercase A-Z are allowed."); + } + StringBuilder sb = new StringBuilder(); + + // Encrypt each character + for (char c : data.toCharArray()) { + int idx = charToPos(c); // Get the index of the character + sb.append(key.charAt(idx)); // Map to the corresponding character in the key + } + return sb.toString(); + } + + // Decryption method + public static String decrypt(String data, String key) { + StringBuilder sb = new StringBuilder(); + + // Decrypt each character + for (char c : data.toCharArray()) { + int idx = key.indexOf(c); // Find the index of the character in the key + if (idx == -1) { + throw new IllegalArgumentException("Input data contains invalid characters."); + } + sb.append(posToChar(idx)); // Convert the index back to the original character + } + return sb.toString(); + } + + // Helper method: Convert a character to its position in the alphabet + private static int charToPos(char c) { + return c - 'A'; // Subtract 'A' to get position (0 for A, 1 for B, etc.) + } + + // Helper method: Convert a position in the alphabet to a character + private static char posToChar(int pos) { + return (char) (pos + 'A'); // Add 'A' to convert position back to character + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/PlayfairCipher.java b/src/main/java/com/thealgorithms/ciphers/PlayfairCipher.java new file mode 100644 index 000000000000..76ceb6dbce31 --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/PlayfairCipher.java @@ -0,0 +1,128 @@ +package com.thealgorithms.ciphers; + +public class PlayfairCipher { + + private char[][] matrix; + private String key; + + public PlayfairCipher(String key) { + this.key = key; + generateMatrix(); + } + + public String encrypt(String plaintext) { + plaintext = prepareText(plaintext.replace("J", "I")); + StringBuilder ciphertext = new StringBuilder(); + for (int i = 0; i < plaintext.length(); i += 2) { + char char1 = plaintext.charAt(i); + char char2 = plaintext.charAt(i + 1); + int[] pos1 = findPosition(char1); + int[] pos2 = findPosition(char2); + int row1 = pos1[0]; + int col1 = pos1[1]; + int row2 = pos2[0]; + int col2 = pos2[1]; + if (row1 == row2) { + ciphertext.append(matrix[row1][(col1 + 1) % 5]); + ciphertext.append(matrix[row2][(col2 + 1) % 5]); + } else if (col1 == col2) { + ciphertext.append(matrix[(row1 + 1) % 5][col1]); + ciphertext.append(matrix[(row2 + 1) % 5][col2]); + } else { + ciphertext.append(matrix[row1][col2]); + ciphertext.append(matrix[row2][col1]); + } + } + return ciphertext.toString(); + } + + public String decrypt(String ciphertext) { + StringBuilder plaintext = new StringBuilder(); + for (int i = 0; i < ciphertext.length(); i += 2) { + char char1 = ciphertext.charAt(i); + char char2 = ciphertext.charAt(i + 1); + int[] pos1 = findPosition(char1); + int[] pos2 = findPosition(char2); + int row1 = pos1[0]; + int col1 = pos1[1]; + int row2 = pos2[0]; + int col2 = pos2[1]; + if (row1 == row2) { + plaintext.append(matrix[row1][(col1 + 4) % 5]); + plaintext.append(matrix[row2][(col2 + 4) % 5]); + } else if (col1 == col2) { + plaintext.append(matrix[(row1 + 4) % 5][col1]); + plaintext.append(matrix[(row2 + 4) % 5][col2]); + } else { + plaintext.append(matrix[row1][col2]); + plaintext.append(matrix[row2][col1]); + } + } + return plaintext.toString(); + } + + private void generateMatrix() { + String keyWithoutDuplicates = removeDuplicateChars(key + "ABCDEFGHIKLMNOPQRSTUVWXYZ"); + matrix = new char[5][5]; + int index = 0; + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 5; j++) { + matrix[i][j] = keyWithoutDuplicates.charAt(index); + index++; + } + } + } + + private String removeDuplicateChars(String str) { + StringBuilder result = new StringBuilder(); + for (int i = 0; i < str.length(); i++) { + if (result.indexOf(String.valueOf(str.charAt(i))) == -1) { + result.append(str.charAt(i)); + } + } + return result.toString(); + } + + private String prepareText(String text) { + text = text.toUpperCase().replaceAll("[^A-Z]", ""); + StringBuilder preparedText = new StringBuilder(); + char prevChar = '\0'; + for (char c : text.toCharArray()) { + if (c != prevChar) { + preparedText.append(c); + prevChar = c; + } else { + preparedText.append('X').append(c); + prevChar = '\0'; + } + } + if (preparedText.length() % 2 != 0) { + preparedText.append('X'); + } + return preparedText.toString(); + } + + private int[] findPosition(char c) { + int[] pos = new int[2]; + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 5; j++) { + if (matrix[i][j] == c) { + pos[0] = i; + pos[1] = j; + return pos; + } + } + } + return pos; + } + + public void printMatrix() { + System.out.println("\nPlayfair Cipher Matrix:"); + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 5; j++) { + System.out.print(matrix[i][j] + " "); + } + System.out.println(); + } + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/Polybius.java b/src/main/java/com/thealgorithms/ciphers/Polybius.java new file mode 100644 index 000000000000..6b3cd6ccae81 --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/Polybius.java @@ -0,0 +1,62 @@ +package com.thealgorithms.ciphers; + +/** + * A Java implementation of Polybius Cipher + * Polybius is a substitution cipher method + * It was invented by a greek philosopher that name is Polybius + * Letters in alphabet takes place to two dimension table. + * Encrypted text is created according to row and column in two dimension table + * Decrypted text is generated by looking at the row and column respectively + * Additionally, some letters in english alphabet deliberately throws such as U because U is very + * similar with V + * + * @author Hikmet ÇAKIR + * @since 08-07-2022+03:00 + */ +public final class Polybius { + private Polybius() { + } + + private static final char[][] KEY = { + // 0 1 2 3 4 + /* 0 */ {'A', 'B', 'C', 'D', 'E'}, + /* 1 */ {'F', 'G', 'H', 'I', 'J'}, + /* 2 */ {'K', 'L', 'M', 'N', 'O'}, + /* 3 */ {'P', 'Q', 'R', 'S', 'T'}, + /* 4 */ {'V', 'W', 'X', 'Y', 'Z'}, + }; + + private static String findLocationByCharacter(final char character) { + final StringBuilder location = new StringBuilder(); + for (int i = 0; i < KEY.length; i++) { + for (int j = 0; j < KEY[i].length; j++) { + if (character == KEY[i][j]) { + location.append(i).append(j); + break; + } + } + } + return location.toString(); + } + + public static String encrypt(final String plaintext) { + final char[] chars = plaintext.toUpperCase().toCharArray(); + final StringBuilder ciphertext = new StringBuilder(); + for (char aChar : chars) { + String location = findLocationByCharacter(aChar); + ciphertext.append(location); + } + return ciphertext.toString(); + } + + public static String decrypt(final String ciphertext) { + final char[] chars = ciphertext.toCharArray(); + final StringBuilder plaintext = new StringBuilder(); + for (int i = 0; i < chars.length; i += 2) { + int pozitionX = Character.getNumericValue(chars[i]); + int pozitionY = Character.getNumericValue(chars[i + 1]); + plaintext.append(KEY[pozitionX][pozitionY]); + } + return plaintext.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/ProductCipher.java b/src/main/java/com/thealgorithms/ciphers/ProductCipher.java new file mode 100644 index 000000000000..d7eaea757001 --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/ProductCipher.java @@ -0,0 +1,73 @@ +package com.thealgorithms.ciphers; + +import java.util.Scanner; + +final class ProductCipher { + private ProductCipher() { + } + + public static void main(String[] args) { + try (Scanner sc = new Scanner(System.in)) { + System.out.println("Enter the input to be encrypted: "); + String substitutionInput = sc.nextLine(); + System.out.println(" "); + System.out.println("Enter a number: "); + int n = sc.nextInt(); + + // Substitution encryption + StringBuffer substitutionOutput = new StringBuffer(); + for (int i = 0; i < substitutionInput.length(); i++) { + char c = substitutionInput.charAt(i); + substitutionOutput.append((char) (c + 5)); + } + System.out.println(" "); + System.out.println("Substituted text: "); + System.out.println(substitutionOutput); + + // Transposition encryption + String transpositionInput = substitutionOutput.toString(); + int modulus = transpositionInput.length() % n; + if (modulus != 0) { + modulus = n - modulus; + + for (; modulus != 0; modulus--) { + transpositionInput += "/"; + } + } + StringBuffer transpositionOutput = new StringBuffer(); + System.out.println(" "); + System.out.println("Transposition Matrix: "); + for (int i = 0; i < n; i++) { + for (int j = 0; j < transpositionInput.length() / n; j++) { + char c = transpositionInput.charAt(i + (j * n)); + System.out.print(c); + transpositionOutput.append(c); + } + System.out.println(); + } + System.out.println(" "); + System.out.println("Final encrypted text: "); + System.out.println(transpositionOutput); + + // Transposition decryption + n = transpositionOutput.length() / n; + StringBuffer transpositionPlaintext = new StringBuffer(); + for (int i = 0; i < n; i++) { + for (int j = 0; j < transpositionOutput.length() / n; j++) { + char c = transpositionOutput.charAt(i + (j * n)); + transpositionPlaintext.append(c); + } + } + + // Substitution decryption + StringBuffer plaintext = new StringBuffer(); + for (int i = 0; i < transpositionPlaintext.length(); i++) { + char c = transpositionPlaintext.charAt(i); + plaintext.append((char) (c - 5)); + } + + System.out.println("Plaintext: "); + System.out.println(plaintext); + } + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/RSA.java b/src/main/java/com/thealgorithms/ciphers/RSA.java new file mode 100644 index 000000000000..28af1a62032a --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/RSA.java @@ -0,0 +1,119 @@ +package com.thealgorithms.ciphers; + +import java.math.BigInteger; +import java.security.SecureRandom; + +/** + * RSA is an asymmetric cryptographic algorithm used for secure data encryption and decryption. + * It relies on a pair of keys: a public key (used for encryption) and a private key + * (used for decryption). The algorithm is based on the difficulty of factoring large prime numbers. + * + * This implementation includes key generation, encryption, and decryption methods that can handle both + * text-based messages and BigInteger inputs. For more details on RSA: + * <a href="https://en.wikipedia.org/wiki/RSA_(cryptosystem)">RSA Cryptosystem - Wikipedia</a>. + * + * Example Usage: + * <pre> + * RSA rsa = new RSA(1024); + * String encryptedMessage = rsa.encrypt("Hello RSA!"); + * String decryptedMessage = rsa.decrypt(encryptedMessage); + * System.out.println(decryptedMessage); // Output: Hello RSA! + * </pre> + * + * Note: The key size directly affects the security and performance of the RSA algorithm. + * Larger keys are more secure but slower to compute. + * + * @author Nguyen Duy Tiep + * @version 23-Oct-17 + */ +public class RSA { + + private BigInteger modulus; + private BigInteger privateKey; + private BigInteger publicKey; + + /** + * Constructor that generates RSA keys with the specified number of bits. + * + * @param bits The bit length of the keys to be generated. Common sizes include 512, 1024, 2048, etc. + */ + public RSA(int bits) { + generateKeys(bits); + } + + /** + * Encrypts a text message using the RSA public key. + * + * @param message The plaintext message to be encrypted. + * @throws IllegalArgumentException If the message is empty. + * @return The encrypted message represented as a String. + */ + public synchronized String encrypt(String message) { + if (message.isEmpty()) { + throw new IllegalArgumentException("Message is empty"); + } + return (new BigInteger(message.getBytes())).modPow(publicKey, modulus).toString(); + } + + /** + * Encrypts a BigInteger message using the RSA public key. + * + * @param message The plaintext message as a BigInteger. + * @return The encrypted message as a BigInteger. + */ + public synchronized BigInteger encrypt(BigInteger message) { + return message.modPow(publicKey, modulus); + } + + /** + * Decrypts an encrypted message (as String) using the RSA private key. + * + * @param encryptedMessage The encrypted message to be decrypted, represented as a String. + * @throws IllegalArgumentException If the message is empty. + * @return The decrypted plaintext message as a String. + */ + public synchronized String decrypt(String encryptedMessage) { + if (encryptedMessage.isEmpty()) { + throw new IllegalArgumentException("Message is empty"); + } + return new String((new BigInteger(encryptedMessage)).modPow(privateKey, modulus).toByteArray()); + } + + /** + * Decrypts an encrypted BigInteger message using the RSA private key. + * + * @param encryptedMessage The encrypted message as a BigInteger. + * @return The decrypted plaintext message as a BigInteger. + */ + public synchronized BigInteger decrypt(BigInteger encryptedMessage) { + return encryptedMessage.modPow(privateKey, modulus); + } + + /** + * Generates a new RSA key pair (public and private keys) with the specified bit length. + * Steps: + * 1. Generate two large prime numbers p and q. + * 2. Compute the modulus n = p * q. + * 3. Compute Euler's totient function: φ(n) = (p-1) * (q-1). + * 4. Choose a public key e (starting from 3) that is coprime with φ(n). + * 5. Compute the private key d as the modular inverse of e mod φ(n). + * The public key is (e, n) and the private key is (d, n). + * + * @param bits The bit length of the keys to be generated. + */ + public final synchronized void generateKeys(int bits) { + SecureRandom random = new SecureRandom(); + BigInteger p = new BigInteger(bits / 2, 100, random); + BigInteger q = new BigInteger(bits / 2, 100, random); + modulus = p.multiply(q); + + BigInteger phi = (p.subtract(BigInteger.ONE)).multiply(q.subtract(BigInteger.ONE)); + + publicKey = BigInteger.valueOf(3L); + while (phi.gcd(publicKey).intValue() > 1) { + publicKey = publicKey.add(BigInteger.TWO); + } + + privateKey = publicKey.modInverse(phi); + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/RailFenceCipher.java b/src/main/java/com/thealgorithms/ciphers/RailFenceCipher.java new file mode 100644 index 000000000000..f81252980468 --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/RailFenceCipher.java @@ -0,0 +1,147 @@ +package com.thealgorithms.ciphers; + +import java.util.Arrays; + +/** + * The rail fence cipher (also called a zigzag cipher) is a classical type of transposition cipher. + * It derives its name from the manner in which encryption is performed, in analogy to a fence built with horizontal rails. + * https://en.wikipedia.org/wiki/Rail_fence_cipher + * @author https://github.com/Krounosity + */ + +public class RailFenceCipher { + + // Encrypts the input string using the rail fence cipher method with the given number of rails. + public String encrypt(String str, int rails) { + + // Base case of single rail or rails are more than the number of characters in the string + if (rails == 1 || rails >= str.length()) { + return str; + } + + // Boolean flag to determine if the movement is downward or upward in the rail matrix. + boolean down = true; + // Create a 2D array to represent the rails (rows) and the length of the string (columns). + char[][] strRail = new char[rails][str.length()]; + + // Initialize all positions in the rail matrix with a placeholder character ('\n'). + for (int i = 0; i < rails; i++) { + Arrays.fill(strRail[i], '\n'); + } + + int row = 0; // Start at the first row + int col = 0; // Start at the first column + + int i = 0; + + // Fill the rail matrix with characters from the string based on the rail pattern. + while (col < str.length()) { + // Change direction to down when at the first row. + if (row == 0) { + down = true; + } + // Change direction to up when at the last row. + else if (row == rails - 1) { + down = false; + } + + // Place the character in the current position of the rail matrix. + strRail[row][col] = str.charAt(i); + col++; // Move to the next column. + // Move to the next row based on the direction. + if (down) { + row++; + } else { + row--; + } + + i++; + } + + // Construct the encrypted string by reading characters row by row. + StringBuilder encryptedString = new StringBuilder(); + for (char[] chRow : strRail) { + for (char ch : chRow) { + if (ch != '\n') { + encryptedString.append(ch); + } + } + } + return encryptedString.toString(); + } + // Decrypts the input string using the rail fence cipher method with the given number of rails. + public String decrypt(String str, int rails) { + + // Base case of single rail or rails are more than the number of characters in the string + if (rails == 1 || rails >= str.length()) { + return str; + } + // Boolean flag to determine if the movement is downward or upward in the rail matrix. + boolean down = true; + + // Create a 2D array to represent the rails (rows) and the length of the string (columns). + char[][] strRail = new char[rails][str.length()]; + + int row = 0; // Start at the first row + int col = 0; // Start at the first column + + // Mark the pattern on the rail matrix using '*'. + while (col < str.length()) { + // Change direction to down when at the first row. + if (row == 0) { + down = true; + } + // Change direction to up when at the last row. + else if (row == rails - 1) { + down = false; + } + + // Mark the current position in the rail matrix. + strRail[row][col] = '*'; + col++; // Move to the next column. + // Move to the next row based on the direction. + if (down) { + row++; + } else { + row--; + } + } + + int index = 0; // Index to track characters from the input string. + // Fill the rail matrix with characters from the input string based on the marked pattern. + for (int i = 0; i < rails; i++) { + for (int j = 0; j < str.length(); j++) { + if (strRail[i][j] == '*') { + strRail[i][j] = str.charAt(index++); + } + } + } + + // Construct the decrypted string by following the zigzag pattern. + StringBuilder decryptedString = new StringBuilder(); + row = 0; // Reset to the first row + col = 0; // Reset to the first column + + while (col < str.length()) { + // Change direction to down when at the first row. + if (row == 0) { + down = true; + } + // Change direction to up when at the last row. + else if (row == rails - 1) { + down = false; + } + // Append the character from the rail matrix to the decrypted string. + decryptedString.append(strRail[row][col]); + col++; // Move to the next column. + // Move to the next row based on the direction. + if (down) { + row++; + } else { + row--; + } + } + + return decryptedString.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/SimpleSubCipher.java b/src/main/java/com/thealgorithms/ciphers/SimpleSubCipher.java new file mode 100644 index 000000000000..f6c88ef730ec --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/SimpleSubCipher.java @@ -0,0 +1,85 @@ +package com.thealgorithms.ciphers; + +import java.util.HashMap; +import java.util.Map; + +/** + * The simple substitution cipher is a cipher that has been in use for many + * hundreds of years (an excellent history is given in Simon Singhs 'the Code + * Book'). It basically consists of substituting every plaintext character for a + * different ciphertext character. It differs from the Caesar cipher in that the + * cipher alphabet is not simply the alphabet shifted, it is completely jumbled. + */ +public class SimpleSubCipher { + + /** + * Encrypt text by replacing each element with its opposite character. + * + * @param message + * @param cipherSmall + * @return Encrypted message + */ + public String encode(String message, String cipherSmall) { + StringBuilder encoded = new StringBuilder(); + + // This map is used to encode + Map<Character, Character> cipherMap = new HashMap<>(); + + char beginSmallLetter = 'a'; + char beginCapitalLetter = 'A'; + + cipherSmall = cipherSmall.toLowerCase(); + String cipherCapital = cipherSmall.toUpperCase(); + + // To handle Small and Capital letters + for (int i = 0; i < cipherSmall.length(); i++) { + cipherMap.put(beginSmallLetter++, cipherSmall.charAt(i)); + cipherMap.put(beginCapitalLetter++, cipherCapital.charAt(i)); + } + + for (int i = 0; i < message.length(); i++) { + if (Character.isAlphabetic(message.charAt(i))) { + encoded.append(cipherMap.get(message.charAt(i))); + } else { + encoded.append(message.charAt(i)); + } + } + + return encoded.toString(); + } + + /** + * Decrypt message by replacing each element with its opposite character in + * cipher. + * + * @param encryptedMessage + * @param cipherSmall + * @return message + */ + public String decode(String encryptedMessage, String cipherSmall) { + StringBuilder decoded = new StringBuilder(); + + Map<Character, Character> cipherMap = new HashMap<>(); + + char beginSmallLetter = 'a'; + char beginCapitalLetter = 'A'; + + cipherSmall = cipherSmall.toLowerCase(); + String cipherCapital = cipherSmall.toUpperCase(); + + for (int i = 0; i < cipherSmall.length(); i++) { + cipherMap.put(cipherSmall.charAt(i), beginSmallLetter++); + cipherMap.put(cipherCapital.charAt(i), beginCapitalLetter++); + } + + for (int i = 0; i < encryptedMessage.length(); i++) { + if (Character.isAlphabetic(encryptedMessage.charAt(i))) { + decoded.append(cipherMap.get(encryptedMessage.charAt(i))); + } else { + decoded.append(encryptedMessage.charAt(i)); + } + } + + return decoded.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/Vigenere.java b/src/main/java/com/thealgorithms/ciphers/Vigenere.java new file mode 100644 index 000000000000..0f117853bb85 --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/Vigenere.java @@ -0,0 +1,106 @@ +package com.thealgorithms.ciphers; + +/** + * A Java implementation of the Vigenère Cipher. + * + * The Vigenère Cipher is a polyalphabetic substitution cipher that uses a + * keyword to shift letters in the plaintext by different amounts, depending + * on the corresponding character in the keyword. It wraps around the alphabet, + * ensuring the shifts are within 'A'-'Z' or 'a'-'z'. + * + * Non-alphabetic characters (like spaces, punctuation) are kept unchanged. + * + * Encryption Example: + * - Plaintext: "Hello World!" + * - Key: "suchsecret" + * - Encrypted Text: "Zynsg Yfvev!" + * + * Decryption Example: + * - Ciphertext: "Zynsg Yfvev!" + * - Key: "suchsecret" + * - Decrypted Text: "Hello World!" + * + * Wikipedia Reference: + * <a href="https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher">Vigenère Cipher - Wikipedia</a> + * + * @author straiffix + * @author beingmartinbmc + */ +public class Vigenere { + + /** + * Encrypts a given message using the Vigenère Cipher with the specified key. + * Steps: + * 1. Iterate over each character in the message. + * 2. If the character is a letter, shift it by the corresponding character in the key. + * 3. Preserve the case of the letter. + * 4. Preserve non-alphabetic characters. + * 5. Move to the next character in the key (cyclic). + * 6. Return the encrypted message. + * + * @param message The plaintext message to encrypt. + * @param key The keyword used for encryption. + * @throws IllegalArgumentException if the key is empty. + * @return The encrypted message. + */ + public String encrypt(final String message, final String key) { + if (key.isEmpty()) { + throw new IllegalArgumentException("Key cannot be empty."); + } + + StringBuilder result = new StringBuilder(); + int j = 0; + for (int i = 0; i < message.length(); i++) { + char c = message.charAt(i); + if (Character.isLetter(c)) { + if (Character.isUpperCase(c)) { + result.append((char) ((c + key.toUpperCase().charAt(j) - 2 * 'A') % 26 + 'A')); + } else { + result.append((char) ((c + key.toLowerCase().charAt(j) - 2 * 'a') % 26 + 'a')); + } + j = ++j % key.length(); + } else { + result.append(c); + } + } + return result.toString(); + } + + /** + * Decrypts a given message encrypted with the Vigenère Cipher using the specified key. + * Steps: + * 1. Iterate over each character in the message. + * 2. If the character is a letter, shift it back by the corresponding character in the key. + * 3. Preserve the case of the letter. + * 4. Preserve non-alphabetic characters. + * 5. Move to the next character in the key (cyclic). + * 6. Return the decrypted message. + * + * @param message The encrypted message to decrypt. + * @param key The keyword used for decryption. + * @throws IllegalArgumentException if the key is empty. + * @return The decrypted plaintext message. + */ + public String decrypt(final String message, final String key) { + if (key.isEmpty()) { + throw new IllegalArgumentException("Key cannot be empty."); + } + + StringBuilder result = new StringBuilder(); + int j = 0; + for (int i = 0; i < message.length(); i++) { + char c = message.charAt(i); + if (Character.isLetter(c)) { + if (Character.isUpperCase(c)) { + result.append((char) ('Z' - (25 - (c - key.toUpperCase().charAt(j))) % 26)); + } else { + result.append((char) ('z' - (25 - (c - key.toLowerCase().charAt(j))) % 26)); + } + j = ++j % key.length(); + } else { + result.append(c); + } + } + return result.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/XORCipher.java b/src/main/java/com/thealgorithms/ciphers/XORCipher.java new file mode 100644 index 000000000000..a612ccfbcdef --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/XORCipher.java @@ -0,0 +1,95 @@ +package com.thealgorithms.ciphers; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.HexFormat; + +/** + * A simple implementation of the XOR cipher that allows both encryption and decryption + * using a given key. This cipher works by applying the XOR bitwise operation between + * the bytes of the input text and the corresponding bytes of the key (repeating the key + * if necessary). + * + * Usage: + * - Encryption: Converts plaintext into a hexadecimal-encoded ciphertext. + * - Decryption: Converts the hexadecimal ciphertext back into plaintext. + * + * Characteristics: + * - Symmetric: The same key is used for both encryption and decryption. + * - Simple but vulnerable: XOR encryption is insecure for real-world cryptography, + * especially when the same key is reused. + * + * Example: + * Plaintext: "Hello!" + * Key: "key" + * Encrypted: "27090c03120b" + * Decrypted: "Hello!" + * + * Reference: <a href="https://en.wikipedia.org/wiki/XOR_cipher">XOR Cipher - Wikipedia</a> + * + * @author <a href="https://github.com/lcsjunior">lcsjunior</a> + */ +public final class XORCipher { + + // Default character encoding for string conversion + private static final Charset CS_DEFAULT = StandardCharsets.UTF_8; + + private XORCipher() { + } + + /** + * Applies the XOR operation between the input bytes and the key bytes. + * If the key is shorter than the input, it wraps around (cyclically). + * + * @param inputBytes The input byte array (plaintext or ciphertext). + * @param keyBytes The key byte array used for XOR operation. + * @return A new byte array containing the XOR result. + */ + public static byte[] xor(final byte[] inputBytes, final byte[] keyBytes) { + byte[] outputBytes = new byte[inputBytes.length]; + for (int i = 0; i < inputBytes.length; ++i) { + outputBytes[i] = (byte) (inputBytes[i] ^ keyBytes[i % keyBytes.length]); + } + return outputBytes; + } + + /** + * Encrypts the given plaintext using the XOR cipher with the specified key. + * The result is a hexadecimal-encoded string representing the ciphertext. + * + * @param plainText The input plaintext to encrypt. + * @param key The encryption key. + * @throws IllegalArgumentException if the key is empty. + * @return A hexadecimal string representing the encrypted text. + */ + public static String encrypt(final String plainText, final String key) { + if (key.isEmpty()) { + throw new IllegalArgumentException("Key must not be empty"); + } + + byte[] plainTextBytes = plainText.getBytes(CS_DEFAULT); + byte[] keyBytes = key.getBytes(CS_DEFAULT); + byte[] xorResult = xor(plainTextBytes, keyBytes); + return HexFormat.of().formatHex(xorResult); + } + + /** + * Decrypts the given ciphertext (in hexadecimal format) using the XOR cipher + * with the specified key. The result is the original plaintext. + * + * @param cipherText The hexadecimal string representing the encrypted text. + * @param key The decryption key (must be the same as the encryption key). + * @throws IllegalArgumentException if the key is empty. + * @return The decrypted plaintext. + */ + public static String decrypt(final String cipherText, final String key) { + if (key.isEmpty()) { + throw new IllegalArgumentException("Key must not be empty"); + } + + byte[] cipherBytes = HexFormat.of().parseHex(cipherText); + byte[] keyBytes = key.getBytes(CS_DEFAULT); + byte[] xorResult = xor(cipherBytes, keyBytes); + return new String(xorResult, CS_DEFAULT); + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/a5/A5Cipher.java b/src/main/java/com/thealgorithms/ciphers/a5/A5Cipher.java new file mode 100644 index 000000000000..cc2e9105229a --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/a5/A5Cipher.java @@ -0,0 +1,63 @@ +package com.thealgorithms.ciphers.a5; + +import java.util.BitSet; + +/** + * The A5Cipher class implements the A5/1 stream cipher, which is a widely used + * encryption algorithm, particularly in mobile communications. + * + * This implementation uses a key stream generator to produce a stream of bits + * that are XORed with the plaintext bits to produce the ciphertext. + * + * <p> + * For more details about the A5/1 algorithm, refer to + * <a href="https://en.wikipedia.org/wiki/A5/1">Wikipedia</a>. + * </p> + */ +public class A5Cipher { + + private final A5KeyStreamGenerator keyStreamGenerator; + private static final int KEY_STREAM_LENGTH = 228; // Length of the key stream in bits (28.5 bytes) + + /** + * Constructs an A5Cipher instance with the specified session key and frame counter. + * + * @param sessionKey a BitSet representing the session key used for encryption. + * @param frameCounter a BitSet representing the frame counter that helps in key stream generation. + */ + public A5Cipher(BitSet sessionKey, BitSet frameCounter) { + keyStreamGenerator = new A5KeyStreamGenerator(); + keyStreamGenerator.initialize(sessionKey, frameCounter); + } + + /** + * Encrypts the given plaintext bits using the A5/1 cipher algorithm. + * + * This method generates a key stream and XORs it with the provided plaintext + * bits to produce the ciphertext. + * + * @param plainTextBits a BitSet representing the plaintext bits to be encrypted. + * @return a BitSet containing the encrypted ciphertext bits. + */ + public BitSet encrypt(BitSet plainTextBits) { + // create a copy + var result = new BitSet(KEY_STREAM_LENGTH); + result.xor(plainTextBits); + + var key = keyStreamGenerator.getNextKeyStream(); + result.xor(key); + + return result; + } + + /** + * Resets the internal counter of the key stream generator. + * + * This method can be called to re-initialize the state of the key stream + * generator, allowing for new key streams to be generated for subsequent + * encryptions. + */ + public void resetCounter() { + keyStreamGenerator.reInitialize(); + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/a5/A5KeyStreamGenerator.java b/src/main/java/com/thealgorithms/ciphers/a5/A5KeyStreamGenerator.java new file mode 100644 index 000000000000..ee837ef4241a --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/a5/A5KeyStreamGenerator.java @@ -0,0 +1,121 @@ +package com.thealgorithms.ciphers.a5; + +import java.util.BitSet; + +/** + * The A5KeyStreamGenerator class is responsible for generating key streams + * for the A5/1 encryption algorithm using a combination of Linear Feedback Shift Registers (LFSRs). + * + * <p> + * This class extends the CompositeLFSR and initializes a set of LFSRs with + * a session key and a frame counter to produce a pseudo-random key stream. + * </p> + * + * <p> + * Note: Proper exception handling for invalid usage is to be implemented. + * </p> + */ +public class A5KeyStreamGenerator extends CompositeLFSR { + + private BitSet initialFrameCounter; + private BitSet frameCounter; + private BitSet sessionKey; + private static final int INITIAL_CLOCKING_CYCLES = 100; + private static final int KEY_STREAM_LENGTH = 228; + + /** + * Initializes the A5KeyStreamGenerator with the specified session key and frame counter. + * + * <p> + * This method sets up the internal state of the LFSRs using the provided + * session key and frame counter. It creates three LFSRs with specific + * configurations and initializes them. + * </p> + * + * @param sessionKey a BitSet representing the session key used for key stream generation. + * @param frameCounter a BitSet representing the frame counter that influences the key stream. + */ + @Override + public void initialize(BitSet sessionKey, BitSet frameCounter) { + this.sessionKey = sessionKey; + this.frameCounter = (BitSet) frameCounter.clone(); + this.initialFrameCounter = (BitSet) frameCounter.clone(); + registers.clear(); + LFSR lfsr1 = new LFSR(19, 8, new int[] {13, 16, 17, 18}); + LFSR lfsr2 = new LFSR(22, 10, new int[] {20, 21}); + LFSR lfsr3 = new LFSR(23, 10, new int[] {7, 20, 21, 22}); + registers.add(lfsr1); + registers.add(lfsr2); + registers.add(lfsr3); + registers.forEach(lfsr -> lfsr.initialize(sessionKey, frameCounter)); + } + + /** + * Re-initializes the key stream generator with the original session key + * and frame counter. This method restores the generator to its initial + * state. + */ + public void reInitialize() { + this.initialize(sessionKey, initialFrameCounter); + } + + /** + * Generates the next key stream of bits. + * + * <p> + * This method performs an initial set of clocking cycles and then retrieves + * a key stream of the specified length. After generation, it re-initializes + * the internal registers. + * </p> + * + * @return a BitSet containing the generated key stream bits. + */ + public BitSet getNextKeyStream() { + for (int cycle = 1; cycle <= INITIAL_CLOCKING_CYCLES; ++cycle) { + this.clock(); + } + + BitSet result = new BitSet(KEY_STREAM_LENGTH); + for (int cycle = 1; cycle <= KEY_STREAM_LENGTH; ++cycle) { + boolean outputBit = this.clock(); + result.set(cycle - 1, outputBit); + } + + reInitializeRegisters(); + return result; + } + + /** + * Re-initializes the registers for the LFSRs. + * + * <p> + * This method increments the frame counter and re-initializes each LFSR + * with the current session key and frame counter. + * </p> + */ + private void reInitializeRegisters() { + incrementFrameCounter(); + registers.forEach(lfsr -> lfsr.initialize(sessionKey, frameCounter)); + } + + /** + * Increments the current frame counter. + * + * <p> + * This method uses a utility function to increment the frame counter, + * which influences the key stream generation process. + * </p> + */ + private void incrementFrameCounter() { + Utils.increment(frameCounter, FRAME_COUNTER_LENGTH); + } + + /** + * Retrieves the current frame counter. + * + * @return a BitSet representing the current state of the frame counter. + */ + public BitSet getFrameCounter() { + return frameCounter; + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/a5/BaseLFSR.java b/src/main/java/com/thealgorithms/ciphers/a5/BaseLFSR.java new file mode 100644 index 000000000000..18ad913784dc --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/a5/BaseLFSR.java @@ -0,0 +1,10 @@ +package com.thealgorithms.ciphers.a5; + +import java.util.BitSet; + +public interface BaseLFSR { + void initialize(BitSet sessionKey, BitSet frameCounter); + boolean clock(); + int SESSION_KEY_LENGTH = 64; + int FRAME_COUNTER_LENGTH = 22; +} diff --git a/src/main/java/com/thealgorithms/ciphers/a5/CompositeLFSR.java b/src/main/java/com/thealgorithms/ciphers/a5/CompositeLFSR.java new file mode 100644 index 000000000000..029a93848c28 --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/a5/CompositeLFSR.java @@ -0,0 +1,69 @@ +package com.thealgorithms.ciphers.a5; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * The CompositeLFSR class represents a composite implementation of + * Linear Feedback Shift Registers (LFSRs) for cryptographic purposes. + * + * <p> + * This abstract class manages a collection of LFSR instances and + * provides a mechanism for irregular clocking based on the + * majority bit among the registers. It implements the BaseLFSR + * interface, requiring subclasses to define specific LFSR behaviors. + * </p> + */ +public abstract class CompositeLFSR implements BaseLFSR { + + protected final List<LFSR> registers = new ArrayList<>(); + + /** + * Performs a clocking operation on the composite LFSR. + * + * <p> + * This method determines the majority bit across all registers and + * clocks each register based on its clock bit. If a register's + * clock bit matches the majority bit, it is clocked (shifted). + * The method also computes and returns the XOR of the last bits + * of all registers. + * </p> + * + * @return the XOR value of the last bits of all registers. + */ + @Override + public boolean clock() { + boolean majorityBit = getMajorityBit(); + boolean result = false; + for (var register : registers) { + result ^= register.getLastBit(); + if (register.getClockBit() == majorityBit) { + register.clock(); + } + } + return result; + } + + /** + * Calculates the majority bit among all registers. + * + * <p> + * This private method counts the number of true and false clock bits + * across all LFSR registers. It returns true if the count of true + * bits is greater than or equal to the count of false bits; otherwise, + * it returns false. + * </p> + * + * @return true if the majority clock bits are true; false otherwise. + */ + private boolean getMajorityBit() { + Map<Boolean, Integer> bitCount = new TreeMap<>(); + bitCount.put(Boolean.FALSE, 0); + bitCount.put(Boolean.TRUE, 0); + + registers.forEach(lfsr -> bitCount.put(lfsr.getClockBit(), bitCount.get(lfsr.getClockBit()) + 1)); + return bitCount.get(Boolean.FALSE) <= bitCount.get(Boolean.TRUE); + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/a5/LFSR.java b/src/main/java/com/thealgorithms/ciphers/a5/LFSR.java new file mode 100644 index 000000000000..dc42ae0a7a5e --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/a5/LFSR.java @@ -0,0 +1,79 @@ +package com.thealgorithms.ciphers.a5; + +import java.util.BitSet; + +public class LFSR implements BaseLFSR { + + private final BitSet register; + private final int length; + private final int clockBitIndex; + private final int[] tappingBitsIndices; + + public LFSR(int length, int clockBitIndex, int[] tappingBitsIndices) { + this.length = length; + this.clockBitIndex = clockBitIndex; + this.tappingBitsIndices = tappingBitsIndices; + register = new BitSet(length); + } + + @Override + public void initialize(BitSet sessionKey, BitSet frameCounter) { + register.clear(); + clock(sessionKey, SESSION_KEY_LENGTH); + clock(frameCounter, FRAME_COUNTER_LENGTH); + } + + private void clock(BitSet key, int keyLength) { + // We start from reverse because LFSR 0 index is the left most bit + // while key 0 index is right most bit, so we reverse it + for (int i = keyLength - 1; i >= 0; --i) { + var newBit = key.get(i) ^ xorTappingBits(); + pushBit(newBit); + } + } + + @Override + public boolean clock() { + return pushBit(xorTappingBits()); + } + + public boolean getClockBit() { + return register.get(clockBitIndex); + } + + public boolean get(int bitIndex) { + return register.get(bitIndex); + } + + public boolean getLastBit() { + return register.get(length - 1); + } + + private boolean xorTappingBits() { + boolean result = false; + for (int i : tappingBitsIndices) { + result ^= register.get(i); + } + return result; + } + + private boolean pushBit(boolean bit) { + boolean discardedBit = rightShift(); + register.set(0, bit); + return discardedBit; + } + + private boolean rightShift() { + boolean discardedBit = get(length - 1); + for (int i = length - 1; i > 0; --i) { + register.set(i, get(i - 1)); + } + register.set(0, false); + return discardedBit; + } + + @Override + public String toString() { + return register.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/ciphers/a5/Utils.java b/src/main/java/com/thealgorithms/ciphers/a5/Utils.java new file mode 100644 index 000000000000..b4addf18dd9d --- /dev/null +++ b/src/main/java/com/thealgorithms/ciphers/a5/Utils.java @@ -0,0 +1,25 @@ +package com.thealgorithms.ciphers.a5; + +// Source +// http://www.java2s.com/example/java-utility-method/bitset/increment-bitset-bits-int-size-9fd84.html +// package com.java2s; +// License from project: Open Source License + +import java.util.BitSet; + +public final class Utils { + private Utils() { + } + + public static boolean increment(BitSet bits, int size) { + int i = size - 1; + while (i >= 0 && bits.get(i)) { + bits.set(i--, false); /*from w w w . j a v a 2s .c o m*/ + } + if (i < 0) { + return false; + } + bits.set(i, true); + return true; + } +} diff --git a/src/main/java/com/thealgorithms/conversions/AffineConverter.java b/src/main/java/com/thealgorithms/conversions/AffineConverter.java new file mode 100644 index 000000000000..199a6dd517d5 --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/AffineConverter.java @@ -0,0 +1,64 @@ +package com.thealgorithms.conversions; + +/** + * A utility class to perform affine transformations of the form: + * y = slope * x + intercept. + * + * This class supports inversion and composition of affine transformations. + * It is immutable, meaning each instance represents a fixed transformation. + */ +public final class AffineConverter { + private final double slope; + private final double intercept; + + /** + * Constructs an AffineConverter with the given slope and intercept. + * + * @param inSlope The slope of the affine transformation. + * @param inIntercept The intercept (constant term) of the affine transformation. + * @throws IllegalArgumentException if either parameter is NaN. + */ + public AffineConverter(final double inSlope, final double inIntercept) { + if (Double.isNaN(inSlope) || Double.isNaN(inIntercept)) { + throw new IllegalArgumentException("Slope and intercept must be valid numbers."); + } + slope = inSlope; + intercept = inIntercept; + } + + /** + * Converts the given input value using the affine transformation: + * result = slope * inValue + intercept. + * + * @param inValue The input value to convert. + * @return The transformed value. + */ + public double convert(final double inValue) { + return slope * inValue + intercept; + } + + /** + * Returns a new AffineConverter representing the inverse of the current transformation. + * The inverse of y = slope * x + intercept is x = (y - intercept) / slope. + * + * @return A new AffineConverter representing the inverse transformation. + * @throws AssertionError if the slope is zero, as the inverse would be undefined. + */ + public AffineConverter invert() { + assert slope != 0.0 : "Slope cannot be zero for inversion."; + return new AffineConverter(1.0 / slope, -intercept / slope); + } + + /** + * Composes this affine transformation with another, returning a new AffineConverter. + * If this transformation is f(x) and the other is g(x), the result is f(g(x)). + * + * @param other Another AffineConverter to compose with. + * @return A new AffineConverter representing the composition of the two transformations. + */ + public AffineConverter compose(final AffineConverter other) { + double newSlope = slope * other.slope; + double newIntercept = slope * other.intercept + intercept; + return new AffineConverter(newSlope, newIntercept); + } +} diff --git a/src/main/java/com/thealgorithms/conversions/AnyBaseToAnyBase.java b/src/main/java/com/thealgorithms/conversions/AnyBaseToAnyBase.java new file mode 100644 index 000000000000..7a9448fd8fe7 --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/AnyBaseToAnyBase.java @@ -0,0 +1,181 @@ +package com.thealgorithms.conversions; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.InputMismatchException; +import java.util.Scanner; + +/** + * Class for converting from "any" base to "any" other base, when "any" means + * from 2-36. Works by going from base 1 to decimal to base 2. Includes + * auxiliary method for determining whether a number is valid for a given base. + * + * @author Michael Rolland + * @version 2017.10.10 + */ +public final class AnyBaseToAnyBase { + private AnyBaseToAnyBase() { + } + + /** + * Smallest and largest base you want to accept as valid input + */ + static final int MINIMUM_BASE = 2; + + static final int MAXIMUM_BASE = 36; + + public static void main(String[] args) { + Scanner in = new Scanner(System.in); + String n; + int b1; + int b2; + while (true) { + try { + System.out.print("Enter number: "); + n = in.next(); + System.out.print("Enter beginning base (between " + MINIMUM_BASE + " and " + MAXIMUM_BASE + "): "); + b1 = in.nextInt(); + if (b1 > MAXIMUM_BASE || b1 < MINIMUM_BASE) { + System.out.println("Invalid base!"); + continue; + } + if (!validForBase(n, b1)) { + System.out.println("The number is invalid for this base!"); + continue; + } + System.out.print("Enter end base (between " + MINIMUM_BASE + " and " + MAXIMUM_BASE + "): "); + b2 = in.nextInt(); + if (b2 > MAXIMUM_BASE || b2 < MINIMUM_BASE) { + System.out.println("Invalid base!"); + continue; + } + break; + } catch (InputMismatchException e) { + System.out.println("Invalid input."); + in.next(); + } + } + System.out.println(base2base(n, b1, b2)); + in.close(); + } + + /** + * Checks if a number (as a String) is valid for a given base. + */ + public static boolean validForBase(String n, int base) { + char[] validDigits = { + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + 'A', + 'B', + 'C', + 'D', + 'E', + 'F', + 'G', + 'H', + 'I', + 'J', + 'K', + 'L', + 'M', + 'N', + 'O', + 'P', + 'Q', + 'R', + 'S', + 'T', + 'U', + 'V', + 'W', + 'X', + 'Y', + 'Z', + }; + // digitsForBase contains all the valid digits for the base given + char[] digitsForBase = Arrays.copyOfRange(validDigits, 0, base); + + // Convert character array into set for convenience of contains() method + HashSet<Character> digitsList = new HashSet<>(); + for (int i = 0; i < digitsForBase.length; i++) { + digitsList.add(digitsForBase[i]); + } + + // Check that every digit in n is within the list of valid digits for that base. + for (char c : n.toCharArray()) { + if (!digitsList.contains(c)) { + return false; + } + } + + return true; + } + + /** + * Method to convert any integer from base b1 to base b2. Works by + * converting from b1 to decimal, then decimal to b2. + * + * @param n The integer to be converted. + * @param b1 Beginning base. + * @param b2 End base. + * @return n in base b2. + */ + public static String base2base(String n, int b1, int b2) { + // Declare variables: decimal value of n, + // character of base b1, character of base b2, + // and the string that will be returned. + int decimalValue = 0; + int charB2; + char charB1; + StringBuilder output = new StringBuilder(); + // Go through every character of n + for (int i = 0; i < n.length(); i++) { + // store the character in charB1 + charB1 = n.charAt(i); + // if it is a non-number, convert it to a decimal value >9 and store it in charB2 + if (charB1 >= 'A' && charB1 <= 'Z') { + charB2 = 10 + (charB1 - 'A'); + } // Else, store the integer value in charB2 + else { + charB2 = charB1 - '0'; + } + // Convert the digit to decimal and add it to the + // decimalValue of n + decimalValue = decimalValue * b1 + charB2; + } + + // Converting the decimal value to base b2: + // A number is converted from decimal to another base + // by continuously dividing by the base and recording + // the remainder until the quotient is zero. The number in the + // new base is the remainders, with the last remainder + // being the left-most digit. + if (0 == decimalValue) { + return "0"; + } + // While the quotient is NOT zero: + while (decimalValue != 0) { + // If the remainder is a digit < 10, simply add it to + // the left side of the new number. + if (decimalValue % b2 < 10) { + output.insert(0, decimalValue % b2); + } // If the remainder is >= 10, add a character with the + // corresponding value to the new number. (A = 10, B = 11, C = 12, ...) + else { + output.insert(0, (char) ((decimalValue % b2) + 55)); + } + // Divide by the new base again + decimalValue /= b2; + } + return output.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/conversions/AnyBaseToDecimal.java b/src/main/java/com/thealgorithms/conversions/AnyBaseToDecimal.java new file mode 100644 index 000000000000..cdab98c7c28a --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/AnyBaseToDecimal.java @@ -0,0 +1,52 @@ +package com.thealgorithms.conversions; + +/** + * @author Varun Upadhyay (<a href="https://github.com/varunu28">...</a>) + */ +public final class AnyBaseToDecimal { + private static final int CHAR_OFFSET_FOR_DIGIT = '0'; + private static final int CHAR_OFFSET_FOR_UPPERCASE = 'A' - 10; + + private AnyBaseToDecimal() { + } + + /** + * Convert any radix to a decimal number. + * + * @param input the string to be converted + * @param radix the radix (base) of the input string + * @return the decimal equivalent of the input string + * @throws NumberFormatException if the input string or radix is invalid + */ + public static int convertToDecimal(String input, int radix) { + int result = 0; + int power = 1; + + for (int i = input.length() - 1; i >= 0; i--) { + int digit = valOfChar(input.charAt(i)); + if (digit >= radix) { + throw new NumberFormatException("For input string: " + input); + } + result += digit * power; + power *= radix; + } + return result; + } + + /** + * Convert a character to its integer value. + * + * @param character the character to be converted + * @return the integer value represented by the character + * @throws NumberFormatException if the character is not an uppercase letter or a digit + */ + private static int valOfChar(char character) { + if (Character.isDigit(character)) { + return character - CHAR_OFFSET_FOR_DIGIT; + } else if (Character.isUpperCase(character)) { + return character - CHAR_OFFSET_FOR_UPPERCASE; + } else { + throw new NumberFormatException("invalid character:" + character); + } + } +} diff --git a/src/main/java/com/thealgorithms/conversions/AnytoAny.java b/src/main/java/com/thealgorithms/conversions/AnytoAny.java new file mode 100644 index 000000000000..e7bdbc2b79c4 --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/AnytoAny.java @@ -0,0 +1,68 @@ +package com.thealgorithms.conversions; + +/** + * A utility class for converting numbers from any base to any other base. + * + * This class provides a method to convert a source number from a given base + * to a destination number in another base. Valid bases range from 2 to 10. + */ +public final class AnytoAny { + private AnytoAny() { + } + + /** + * Converts a number from a source base to a destination base. + * + * @param sourceNumber The number in the source base (as an integer). + * @param sourceBase The base of the source number (between 2 and 10). + * @param destBase The base to which the number should be converted (between 2 and 10). + * @throws IllegalArgumentException if the bases are not between 2 and 10. + * @return The converted number in the destination base (as an integer). + */ + public static int convertBase(int sourceNumber, int sourceBase, int destBase) { + if (sourceBase < 2 || sourceBase > 10 || destBase < 2 || destBase > 10) { + throw new IllegalArgumentException("Bases must be between 2 and 10."); + } + + int decimalValue = toDecimal(sourceNumber, sourceBase); + return fromDecimal(decimalValue, destBase); + } + + /** + * Converts a number from a given base to its decimal representation (base 10). + * + * @param number The number in the original base. + * @param base The base of the given number. + * @return The decimal representation of the number. + */ + private static int toDecimal(int number, int base) { + int decimalValue = 0; + int multiplier = 1; + + while (number != 0) { + decimalValue += (number % 10) * multiplier; + multiplier *= base; + number /= 10; + } + return decimalValue; + } + + /** + * Converts a decimal (base 10) number to a specified base. + * + * @param decimal The decimal number to convert. + * @param base The destination base for conversion. + * @return The number in the specified base. + */ + private static int fromDecimal(int decimal, int base) { + int result = 0; + int multiplier = 1; + + while (decimal != 0) { + result += (decimal % base) * multiplier; + multiplier *= 10; + decimal /= base; + } + return result; + } +} diff --git a/src/main/java/com/thealgorithms/conversions/BinaryToDecimal.java b/src/main/java/com/thealgorithms/conversions/BinaryToDecimal.java new file mode 100644 index 000000000000..36c0790e565f --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/BinaryToDecimal.java @@ -0,0 +1,33 @@ +package com.thealgorithms.conversions; + +/** + * This class converts a Binary number to a Decimal number + */ +final class BinaryToDecimal { + private static final int BINARY_BASE = 2; + + private BinaryToDecimal() { + } + + /** + * Converts a binary number to its decimal equivalent. + * + * @param binaryNumber The binary number to convert. + * @return The decimal equivalent of the binary number. + * @throws IllegalArgumentException If the binary number contains digits other than 0 and 1. + */ + public static long binaryToDecimal(long binaryNumber) { + long decimalValue = 0; + long power = 0; + + while (binaryNumber != 0) { + long digit = binaryNumber % 10; + if (digit > 1) { + throw new IllegalArgumentException("Incorrect binary digit: " + digit); + } + decimalValue += (long) (digit * Math.pow(BINARY_BASE, power++)); + binaryNumber /= 10; + } + return decimalValue; + } +} diff --git a/src/main/java/com/thealgorithms/conversions/BinaryToHexadecimal.java b/src/main/java/com/thealgorithms/conversions/BinaryToHexadecimal.java new file mode 100644 index 000000000000..9ff2f593fe1f --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/BinaryToHexadecimal.java @@ -0,0 +1,63 @@ +package com.thealgorithms.conversions; + +import java.util.HashMap; +import java.util.Map; + +/** + * Converts any Binary Number to a Hexadecimal Number + * + * @author Nishita Aggarwal + */ +public final class BinaryToHexadecimal { + private static final int BITS_IN_HEX_DIGIT = 4; + private static final int BASE_BINARY = 2; + private static final int BASE_DECIMAL = 10; + private static final int HEX_START_DECIMAL = 10; + private static final int HEX_END_DECIMAL = 15; + + private BinaryToHexadecimal() { + } + + /** + * Converts a binary number to a hexadecimal number. + * + * @param binary The binary number to convert. + * @return The hexadecimal representation of the binary number. + * @throws IllegalArgumentException If the binary number contains digits other than 0 and 1. + */ + public static String binToHex(int binary) { + Map<Integer, String> hexMap = initializeHexMap(); + StringBuilder hex = new StringBuilder(); + + while (binary != 0) { + int decimalValue = 0; + for (int i = 0; i < BITS_IN_HEX_DIGIT; i++) { + int currentBit = binary % BASE_DECIMAL; + if (currentBit > 1) { + throw new IllegalArgumentException("Incorrect binary digit: " + currentBit); + } + binary /= BASE_DECIMAL; + decimalValue += (int) (currentBit * Math.pow(BASE_BINARY, i)); + } + hex.insert(0, hexMap.get(decimalValue)); + } + + return !hex.isEmpty() ? hex.toString() : "0"; + } + + /** + * Initializes the hexadecimal map with decimal to hexadecimal mappings. + * + * @return The initialized map containing mappings from decimal numbers to hexadecimal digits. + */ + private static Map<Integer, String> initializeHexMap() { + Map<Integer, String> hexMap = new HashMap<>(); + for (int i = 0; i < BASE_DECIMAL; i++) { + hexMap.put(i, String.valueOf(i)); + } + for (int i = HEX_START_DECIMAL; i <= HEX_END_DECIMAL; i++) { + hexMap.put(i, String.valueOf((char) ('A' + i - HEX_START_DECIMAL))); + } + return hexMap; + } +} diff --git a/src/main/java/com/thealgorithms/conversions/BinaryToOctal.java b/src/main/java/com/thealgorithms/conversions/BinaryToOctal.java new file mode 100644 index 000000000000..5407c8525a23 --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/BinaryToOctal.java @@ -0,0 +1,45 @@ +package com.thealgorithms.conversions; + +public final class BinaryToOctal { + private static final int BITS_PER_OCTAL_DIGIT = 3; + private static final int BINARY_BASE = 2; + private static final int DECIMAL_BASE = 10; + + private BinaryToOctal() { + } + + /** + * This method converts a binary number to an octal number. + * + * @param binary The binary number + * @return The octal number + * @throws IllegalArgumentException if the input is not a valid binary number + */ + public static String convertBinaryToOctal(int binary) { + if (binary == 0) { + return "0"; + } + + if (!String.valueOf(binary).matches("[01]+")) { + throw new IllegalArgumentException("Input is not a valid binary number."); + } + + StringBuilder octal = new StringBuilder(); + int currentBit; + int bitValueMultiplier = 1; + + while (binary != 0) { + int octalDigit = 0; + for (int i = 0; i < BITS_PER_OCTAL_DIGIT && binary != 0; i++) { + currentBit = binary % DECIMAL_BASE; + binary /= DECIMAL_BASE; + octalDigit += currentBit * bitValueMultiplier; + bitValueMultiplier *= BINARY_BASE; + } + octal.insert(0, octalDigit); + bitValueMultiplier = 1; // Reset multiplier for the next group + } + + return octal.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/conversions/DecimalToAnyBase.java b/src/main/java/com/thealgorithms/conversions/DecimalToAnyBase.java new file mode 100644 index 000000000000..a5615dc002f5 --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/DecimalToAnyBase.java @@ -0,0 +1,69 @@ +package com.thealgorithms.conversions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Class that provides methods to convert a decimal number to a string representation + * in any specified base between 2 and 36. + * + * @author Varun Upadhyay (<a href="https://github.com/varunu28">...</a>) + */ +public final class DecimalToAnyBase { + private static final int MIN_BASE = 2; + private static final int MAX_BASE = 36; + private static final char ZERO_CHAR = '0'; + private static final char A_CHAR = 'A'; + private static final int DIGIT_OFFSET = 10; + + private DecimalToAnyBase() { + } + + /** + * Converts a decimal number to a string representation in the specified base. + * For example, converting the decimal number 10 to base 2 would return "1010". + * + * @param decimal the decimal number to convert + * @param base the base to convert to (must be between {@value #MIN_BASE} and {@value #MAX_BASE}) + * @return the string representation of the number in the specified base + * @throws IllegalArgumentException if the base is out of the supported range + */ + public static String convertToAnyBase(int decimal, int base) { + if (base < MIN_BASE || base > MAX_BASE) { + throw new IllegalArgumentException("Base must be between " + MIN_BASE + " and " + MAX_BASE); + } + + if (decimal == 0) { + return String.valueOf(ZERO_CHAR); + } + + List<Character> digits = new ArrayList<>(); + while (decimal > 0) { + digits.add(convertToChar(decimal % base)); + decimal /= base; + } + + StringBuilder result = new StringBuilder(digits.size()); + for (int i = digits.size() - 1; i >= 0; i--) { + result.append(digits.get(i)); + } + + return result.toString(); + } + + /** + * Converts an integer value to its corresponding character in the specified base. + * This method is used to convert values from 0 to 35 into their appropriate character representation. + * For example, 0-9 are represented as '0'-'9', and 10-35 are represented as 'A'-'Z'. + * + * @param value the integer value to convert (should be less than the base value) + * @return the character representing the value in the specified base + */ + private static char convertToChar(int value) { + if (value >= 0 && value <= 9) { + return (char) (ZERO_CHAR + value); + } else { + return (char) (A_CHAR + value - DIGIT_OFFSET); + } + } +} diff --git a/src/main/java/com/thealgorithms/conversions/DecimalToBinary.java b/src/main/java/com/thealgorithms/conversions/DecimalToBinary.java new file mode 100644 index 000000000000..e8d033e0093c --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/DecimalToBinary.java @@ -0,0 +1,49 @@ +package com.thealgorithms.conversions; + +/** + * This class provides methods to convert a decimal number to a binary number. + */ +final class DecimalToBinary { + private static final int BINARY_BASE = 2; + private static final int DECIMAL_MULTIPLIER = 10; + + private DecimalToBinary() { + } + + /** + * Converts a decimal number to a binary number using a conventional algorithm. + * @param decimalNumber the decimal number to convert + * @return the binary representation of the decimal number + */ + public static int convertUsingConventionalAlgorithm(int decimalNumber) { + int binaryNumber = 0; + int position = 1; + + while (decimalNumber > 0) { + int remainder = decimalNumber % BINARY_BASE; + binaryNumber += remainder * position; + position *= DECIMAL_MULTIPLIER; + decimalNumber /= BINARY_BASE; + } + + return binaryNumber; + } + + /** + * Converts a decimal number to a binary number using a bitwise algorithm. + * @param decimalNumber the decimal number to convert + * @return the binary representation of the decimal number + */ + public static int convertUsingBitwiseAlgorithm(int decimalNumber) { + int binaryNumber = 0; + int position = 1; + + while (decimalNumber > 0) { + int leastSignificantBit = decimalNumber & 1; + binaryNumber += leastSignificantBit * position; + position *= DECIMAL_MULTIPLIER; + decimalNumber >>= 1; + } + return binaryNumber; + } +} diff --git a/src/main/java/com/thealgorithms/conversions/DecimalToHexadecimal.java b/src/main/java/com/thealgorithms/conversions/DecimalToHexadecimal.java new file mode 100644 index 000000000000..47a1e36b27e3 --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/DecimalToHexadecimal.java @@ -0,0 +1,42 @@ +package com.thealgorithms.conversions; + +/** + * This class provides a method to convert a decimal number to a hexadecimal string. + */ +final class DecimalToHexadecimal { + private static final int SIZE_OF_INT_IN_HALF_BYTES = 8; + private static final int NUMBER_OF_BITS_IN_HALF_BYTE = 4; + private static final int HALF_BYTE_MASK = 0x0F; + private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + private DecimalToHexadecimal() { + } + + /** + * Converts a decimal number to a hexadecimal string. + * @param decimal the decimal number to convert + * @return the hexadecimal representation of the decimal number + */ + public static String decToHex(int decimal) { + StringBuilder hexBuilder = new StringBuilder(SIZE_OF_INT_IN_HALF_BYTES); + for (int i = SIZE_OF_INT_IN_HALF_BYTES - 1; i >= 0; --i) { + int currentHalfByte = decimal & HALF_BYTE_MASK; + hexBuilder.insert(0, HEX_DIGITS[currentHalfByte]); + decimal >>= NUMBER_OF_BITS_IN_HALF_BYTE; + } + return removeLeadingZeros(hexBuilder.toString().toLowerCase()); + } + + private static String removeLeadingZeros(String str) { + if (str == null || str.isEmpty()) { + return str; + } + + int i = 0; + while (i < str.length() && str.charAt(i) == '0') { + i++; + } + + return i == str.length() ? "0" : str.substring(i); + } +} diff --git a/src/main/java/com/thealgorithms/conversions/DecimalToOctal.java b/src/main/java/com/thealgorithms/conversions/DecimalToOctal.java new file mode 100644 index 000000000000..75687fc589ae --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/DecimalToOctal.java @@ -0,0 +1,38 @@ +package com.thealgorithms.conversions; + +/** + * This class converts Decimal numbers to Octal Numbers + */ +public final class DecimalToOctal { + private static final int OCTAL_BASE = 8; + private static final int INITIAL_OCTAL_VALUE = 0; + private static final int INITIAL_PLACE_VALUE = 1; + + private DecimalToOctal() { + } + + /** + * Converts a decimal number to its octal equivalent. + * + * @param decimal The decimal number to convert. + * @return The octal equivalent as an integer. + * @throws IllegalArgumentException if the decimal number is negative. + */ + public static int convertToOctal(int decimal) { + if (decimal < 0) { + throw new IllegalArgumentException("Decimal number cannot be negative."); + } + + int octal = INITIAL_OCTAL_VALUE; + int placeValue = INITIAL_PLACE_VALUE; + + while (decimal != 0) { + int remainder = decimal % OCTAL_BASE; + octal += remainder * placeValue; + decimal /= OCTAL_BASE; + placeValue *= 10; + } + + return octal; + } +} diff --git a/src/main/java/com/thealgorithms/conversions/EndianConverter.java b/src/main/java/com/thealgorithms/conversions/EndianConverter.java new file mode 100644 index 000000000000..0d69098e8255 --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/EndianConverter.java @@ -0,0 +1,47 @@ +package com.thealgorithms.conversions; + +/** + * Utility class for converting integers between big-endian and little-endian formats. + * <p> + * Endianness defines how byte sequences represent multi-byte data types: + * <ul> + * <li><b>Big-endian</b>: The most significant byte (MSB) comes first.</li> + * <li><b>Little-endian</b>: The least significant byte (LSB) comes first.</li> + * </ul> + * <p> + * Example conversion: + * <ul> + * <li>Big-endian to little-endian: {@code 0x12345678} → {@code 0x78563412}</li> + * <li>Little-endian to big-endian: {@code 0x78563412} → {@code 0x12345678}</li> + * </ul> + * + * <p>Note: Both conversions in this utility are equivalent since reversing the bytes is symmetric.</p> + * + * <p>This class only supports 32-bit integers.</p> + * + * @author Hardvan + */ +public final class EndianConverter { + private EndianConverter() { + } + + /** + * Converts a 32-bit integer from big-endian to little-endian. + * + * @param value the integer in big-endian format + * @return the integer in little-endian format + */ + public static int bigToLittleEndian(int value) { + return Integer.reverseBytes(value); + } + + /** + * Converts a 32-bit integer from little-endian to big-endian. + * + * @param value the integer in little-endian format + * @return the integer in big-endian format + */ + public static int littleToBigEndian(int value) { + return Integer.reverseBytes(value); + } +} diff --git a/src/main/java/com/thealgorithms/conversions/HexToOct.java b/src/main/java/com/thealgorithms/conversions/HexToOct.java new file mode 100644 index 000000000000..d3a672d37424 --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/HexToOct.java @@ -0,0 +1,62 @@ +package com.thealgorithms.conversions; + +/** + * Converts any Hexadecimal Number to Octal + * + * @author Tanmay Joshi + */ +public final class HexToOct { + private HexToOct() { + } + + /** + * Converts a Hexadecimal number to a Decimal number. + * + * @param hex The Hexadecimal number as a String. + * @return The Decimal equivalent as an integer. + */ + public static int hexToDecimal(String hex) { + String hexDigits = "0123456789ABCDEF"; + hex = hex.toUpperCase(); + int decimalValue = 0; + + for (int i = 0; i < hex.length(); i++) { + char hexChar = hex.charAt(i); + int digitValue = hexDigits.indexOf(hexChar); + decimalValue = 16 * decimalValue + digitValue; + } + + return decimalValue; + } + + /** + * Converts a Decimal number to an Octal number. + * + * @param decimal The Decimal number as an integer. + * @return The Octal equivalent as an integer. + */ + public static int decimalToOctal(int decimal) { + int octalValue = 0; + int placeValue = 1; + + while (decimal > 0) { + int remainder = decimal % 8; + octalValue += remainder * placeValue; + decimal /= 8; + placeValue *= 10; + } + + return octalValue; + } + + /** + * Converts a Hexadecimal number to an Octal number. + * + * @param hex The Hexadecimal number as a String. + * @return The Octal equivalent as an integer. + */ + public static int hexToOctal(String hex) { + int decimalValue = hexToDecimal(hex); + return decimalToOctal(decimalValue); + } +} diff --git a/src/main/java/com/thealgorithms/conversions/HexaDecimalToBinary.java b/src/main/java/com/thealgorithms/conversions/HexaDecimalToBinary.java new file mode 100644 index 000000000000..c0eb9a01ba17 --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/HexaDecimalToBinary.java @@ -0,0 +1,62 @@ +package com.thealgorithms.conversions; + +/** + * Utility class for converting hexadecimal numbers to binary representation. + * <p> + * A hexadecimal number consists of digits from {@code [0-9]} and {@code [A-F]} (case-insensitive), + * while binary representation uses only {@code [0, 1]}. + * <p> + * This class provides methods to: + * <ul> + * <li>Convert a hexadecimal string to its binary string equivalent.</li> + * <li>Ensure the binary output is padded to 8 bits (1 byte).</li> + * </ul> + * <p> + * Example: + * <ul> + * <li>{@code "A1"} → {@code "10100001"}</li> + * <li>{@code "1"} → {@code "00000001"}</li> + * </ul> + * + * <p>This class assumes that the input hexadecimal string is valid.</p> + */ +public class HexaDecimalToBinary { + + /** + * Converts a hexadecimal string to its binary string equivalent. + * The binary output is padded to a minimum of 8 bits (1 byte). + * Steps: + * <ol> + * <li>Convert the hexadecimal string to an integer.</li> + * <li>Convert the integer to a binary string.</li> + * <li>Pad the binary string to ensure it is at least 8 bits long.</li> + * <li>Return the padded binary string.</li> + * </ol> + * + * @param numHex the hexadecimal string (e.g., "A1", "7F") + * @throws NumberFormatException if the input string is not a valid hexadecimal number + * @return the binary string representation, padded to 8 bits (e.g., "10100001") + */ + public String convert(String numHex) { + int conHex = Integer.parseInt(numHex, 16); + String binary = Integer.toBinaryString(conHex); + return completeDigits(binary); + } + + /** + * Pads the binary string to ensure it is at least 8 bits long. + * If the binary string is shorter than 8 bits, it adds leading zeros. + * + * @param binNum the binary string to pad + * @return the padded binary string with a minimum length of 8 + */ + public String completeDigits(String binNum) { + final int byteSize = 8; + StringBuilder binNumBuilder = new StringBuilder(binNum); + while (binNumBuilder.length() < byteSize) { + binNumBuilder.insert(0, "0"); + } + binNum = binNumBuilder.toString(); + return binNum; + } +} diff --git a/src/main/java/com/thealgorithms/conversions/HexaDecimalToDecimal.java b/src/main/java/com/thealgorithms/conversions/HexaDecimalToDecimal.java new file mode 100644 index 000000000000..2cf6024d90a3 --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/HexaDecimalToDecimal.java @@ -0,0 +1,45 @@ +package com.thealgorithms.conversions; + +/** + * Utility class for converting a hexadecimal string to its decimal representation. + * <p> + * A hexadecimal number uses the base-16 numeral system, with the following characters: + * <ul> + * <li>Digits: 0-9</li> + * <li>Letters: A-F (case-insensitive)</li> + * </ul> + * Each character represents a power of 16. For example: + * <pre> + * Hexadecimal "A1" = 10*16^1 + 1*16^0 = 161 (decimal) + * </pre> + * + * <p>This class provides a method to perform the conversion without using built-in Java utilities.</p> + */ +public final class HexaDecimalToDecimal { + private HexaDecimalToDecimal() { + } + + /** + * Converts a hexadecimal string to its decimal integer equivalent. + * <p>The input string is case-insensitive, and must contain valid hexadecimal characters [0-9, A-F].</p> + * + * @param hex the hexadecimal string to convert + * @return the decimal integer representation of the input hexadecimal string + * @throws IllegalArgumentException if the input string contains invalid characters + */ + public static int getHexaToDec(String hex) { + String digits = "0123456789ABCDEF"; + hex = hex.toUpperCase(); + int val = 0; + + for (int i = 0; i < hex.length(); i++) { + int d = digits.indexOf(hex.charAt(i)); + if (d == -1) { + throw new IllegalArgumentException("Invalid hexadecimal character: " + hex.charAt(i)); + } + val = 16 * val + d; + } + + return val; + } +} diff --git a/src/main/java/com/thealgorithms/conversions/IPConverter.java b/src/main/java/com/thealgorithms/conversions/IPConverter.java new file mode 100644 index 000000000000..765cb0201dd5 --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/IPConverter.java @@ -0,0 +1,58 @@ +package com.thealgorithms.conversions; + +/** + * Converts an IPv4 address to its binary equivalent and vice-versa. + * IP to Binary: Converts an IPv4 address to its binary equivalent. + * Example: 127.3.4.5 -> 01111111.00000011.00000100.00000101 + * + * Binary to IP: Converts a binary equivalent to an IPv4 address. + * Example: 01111111.00000011.00000100.00000101 -> 127.3.4.5 + * + * @author Hardvan + */ +public final class IPConverter { + private IPConverter() { + } + + /** + * Converts an IPv4 address to its binary equivalent. + * @param ip The IPv4 address to convert. + * @return The binary equivalent of the IPv4 address. + */ + public static String ipToBinary(String ip) { + StringBuilder binary = new StringBuilder(); + for (String octet : ip.split("\\.")) { + binary.append(octetToBinary(Integer.parseInt(octet))).append("."); + } + return binary.substring(0, binary.length() - 1); + } + + /** + * Converts a single octet to its 8-bit binary representation. + * @param octet The octet to convert (0-255). + * @return The 8-bit binary representation as a String. + */ + private static String octetToBinary(int octet) { + char[] binary = {'0', '0', '0', '0', '0', '0', '0', '0'}; + for (int i = 7; i >= 0; i--) { + if ((octet & 1) == 1) { + binary[i] = '1'; + } + octet >>>= 1; + } + return new String(binary); + } + + /** + * Converts a binary equivalent to an IPv4 address. + * @param binary The binary equivalent to convert. + * @return The IPv4 address of the binary equivalent. + */ + public static String binaryToIP(String binary) { + StringBuilder ip = new StringBuilder(); + for (String octet : binary.split("\\.")) { + ip.append(Integer.parseInt(octet, 2)).append("."); + } + return ip.substring(0, ip.length() - 1); + } +} diff --git a/src/main/java/com/thealgorithms/conversions/IPv6Converter.java b/src/main/java/com/thealgorithms/conversions/IPv6Converter.java new file mode 100644 index 000000000000..d42ffd027514 --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/IPv6Converter.java @@ -0,0 +1,98 @@ +package com.thealgorithms.conversions; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; + +/** + * A utility class for converting between IPv6 and IPv4 addresses. + * + * - Converts IPv4 to IPv6-mapped IPv6 address. + * - Extracts IPv4 address from IPv6-mapped IPv6. + * - Handles exceptions for invalid inputs. + * + * @author Hardvan + */ +public final class IPv6Converter { + private IPv6Converter() { + } + + /** + * Converts an IPv4 address (e.g., "192.0.2.128") to an IPv6-mapped IPv6 address. + * Example: IPv4 "192.0.2.128" -> IPv6 "::ffff:192.0.2.128" + * + * @param ipv4Address The IPv4 address in string format. + * @return The corresponding IPv6-mapped IPv6 address. + * @throws UnknownHostException If the IPv4 address is invalid. + * @throws IllegalArgumentException If the IPv6 address is not a mapped IPv4 address. + */ + public static String ipv4ToIpv6(String ipv4Address) throws UnknownHostException { + if (ipv4Address == null || ipv4Address.isEmpty()) { + throw new UnknownHostException("IPv4 address is empty."); + } + + InetAddress ipv4 = InetAddress.getByName(ipv4Address); + byte[] ipv4Bytes = ipv4.getAddress(); + + // Create IPv6-mapped IPv6 address (starts with ::ffff:) + byte[] ipv6Bytes = new byte[16]; + ipv6Bytes[10] = (byte) 0xff; + ipv6Bytes[11] = (byte) 0xff; + System.arraycopy(ipv4Bytes, 0, ipv6Bytes, 12, 4); + + // Manually format to "::ffff:x.x.x.x" format + StringBuilder ipv6String = new StringBuilder("::ffff:"); + for (int i = 12; i < 16; i++) { + ipv6String.append(ipv6Bytes[i] & 0xFF); + if (i < 15) { + ipv6String.append('.'); + } + } + return ipv6String.toString(); + } + + /** + * Extracts the IPv4 address from an IPv6-mapped IPv6 address. + * Example: IPv6 "::ffff:192.0.2.128" -> IPv4 "192.0.2.128" + * + * @param ipv6Address The IPv6 address in string format. + * @return The extracted IPv4 address. + * @throws UnknownHostException If the IPv6 address is invalid or not a mapped IPv4 address. + */ + public static String ipv6ToIpv4(String ipv6Address) throws UnknownHostException { + InetAddress ipv6 = InetAddress.getByName(ipv6Address); + byte[] ipv6Bytes = ipv6.getAddress(); + + // Check if the address is an IPv6-mapped IPv4 address + if (isValidIpv6MappedIpv4(ipv6Bytes)) { + byte[] ipv4Bytes = Arrays.copyOfRange(ipv6Bytes, 12, 16); + InetAddress ipv4 = InetAddress.getByAddress(ipv4Bytes); + return ipv4.getHostAddress(); + } else { + throw new IllegalArgumentException("Not a valid IPv6-mapped IPv4 address."); + } + } + + /** + * Helper function to check if the given byte array represents + * an IPv6-mapped IPv4 address (prefix 0:0:0:0:0:ffff). + * + * @param ipv6Bytes Byte array representation of the IPv6 address. + * @return True if the address is IPv6-mapped IPv4, otherwise false. + */ + private static boolean isValidIpv6MappedIpv4(byte[] ipv6Bytes) { + // IPv6-mapped IPv4 addresses are 16 bytes long, with the first 10 bytes set to 0, + // followed by 0xff, 0xff, and the last 4 bytes representing the IPv4 address. + if (ipv6Bytes.length != 16) { + return false; + } + + for (int i = 0; i < 10; i++) { + if (ipv6Bytes[i] != 0) { + return false; + } + } + + return ipv6Bytes[10] == (byte) 0xff && ipv6Bytes[11] == (byte) 0xff; + } +} diff --git a/src/main/java/com/thealgorithms/conversions/IntegerToEnglish.java b/src/main/java/com/thealgorithms/conversions/IntegerToEnglish.java new file mode 100644 index 000000000000..e85c608af5d0 --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/IntegerToEnglish.java @@ -0,0 +1,108 @@ +package com.thealgorithms.conversions; + +import java.util.Map; + +/** + * A utility class to convert integers to their English word representation. + * + * <p>The class supports conversion of numbers from 0 to 2,147,483,647 + * (the maximum value of a 32-bit signed integer). It divides the number + * into groups of three digits (thousands, millions, billions, etc.) and + * translates each group into words.</p> + * + * <h2>Example Usage</h2> + * <pre> + * IntegerToEnglish.integerToEnglishWords(12345); + * // Output: "Twelve Thousand Three Hundred Forty Five" + * </pre> + * + * <p>This class uses two maps:</p> + * <ul> + * <li>BASE_NUMBERS_MAP: Holds English words for numbers 0-20, multiples of 10 up to 90, and 100.</li> + * <li>THOUSAND_POWER_MAP: Maps powers of 1000 (e.g., Thousand, Million, Billion).</li> + * </ul> + */ +public final class IntegerToEnglish { + + private static final Map<Integer, String> BASE_NUMBERS_MAP = Map.ofEntries(Map.entry(0, ""), Map.entry(1, "One"), Map.entry(2, "Two"), Map.entry(3, "Three"), Map.entry(4, "Four"), Map.entry(5, "Five"), Map.entry(6, "Six"), Map.entry(7, "Seven"), Map.entry(8, "Eight"), Map.entry(9, "Nine"), + Map.entry(10, "Ten"), Map.entry(11, "Eleven"), Map.entry(12, "Twelve"), Map.entry(13, "Thirteen"), Map.entry(14, "Fourteen"), Map.entry(15, "Fifteen"), Map.entry(16, "Sixteen"), Map.entry(17, "Seventeen"), Map.entry(18, "Eighteen"), Map.entry(19, "Nineteen"), Map.entry(20, "Twenty"), + Map.entry(30, "Thirty"), Map.entry(40, "Forty"), Map.entry(50, "Fifty"), Map.entry(60, "Sixty"), Map.entry(70, "Seventy"), Map.entry(80, "Eighty"), Map.entry(90, "Ninety"), Map.entry(100, "Hundred")); + + private static final Map<Integer, String> THOUSAND_POWER_MAP = Map.ofEntries(Map.entry(1, "Thousand"), Map.entry(2, "Million"), Map.entry(3, "Billion")); + + private IntegerToEnglish() { + } + + /** + * Converts numbers less than 1000 into English words. + * + * @param number the integer value (0-999) to convert + * @return the English word representation of the input number + */ + private static String convertToWords(int number) { + int remainder = number % 100; + StringBuilder result = new StringBuilder(); + + if (remainder <= 20) { + result.append(BASE_NUMBERS_MAP.get(remainder)); + } else if (BASE_NUMBERS_MAP.containsKey(remainder)) { + result.append(BASE_NUMBERS_MAP.get(remainder)); + } else { + int tensDigit = remainder / 10; + int onesDigit = remainder % 10; + String tens = BASE_NUMBERS_MAP.getOrDefault(tensDigit * 10, ""); + String ones = BASE_NUMBERS_MAP.getOrDefault(onesDigit, ""); + result.append(tens); + if (ones != null && !ones.isEmpty()) { + result.append(" ").append(ones); + } + } + + int hundredsDigit = number / 100; + if (hundredsDigit > 0) { + if (result.length() > 0) { + result.insert(0, " "); + } + result.insert(0, String.format("%s Hundred", BASE_NUMBERS_MAP.get(hundredsDigit))); + } + + return result.toString().trim(); + } + + /** + * Converts a non-negative integer to its English word representation. + * + * @param number the integer to convert (0-2,147,483,647) + * @return the English word representation of the input number + */ + public static String integerToEnglishWords(int number) { + if (number == 0) { + return "Zero"; + } + + StringBuilder result = new StringBuilder(); + int index = 0; + + while (number > 0) { + int remainder = number % 1000; + number /= 1000; + + if (remainder > 0) { + String subResult = convertToWords(remainder); + if (!subResult.isEmpty()) { + if (index > 0) { + subResult += " " + THOUSAND_POWER_MAP.get(index); + } + if (result.length() > 0) { + result.insert(0, " "); + } + result.insert(0, subResult); + } + } + + index++; + } + + return result.toString().trim(); + } +} diff --git a/src/main/java/com/thealgorithms/conversions/IntegerToRoman.java b/src/main/java/com/thealgorithms/conversions/IntegerToRoman.java new file mode 100644 index 000000000000..fec437668fe6 --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/IntegerToRoman.java @@ -0,0 +1,68 @@ +package com.thealgorithms.conversions; + +/** + * A utility class to convert integers into Roman numerals. + * + * <p>Roman numerals follow these rules: + * <ul> + * <li>I = 1</li> + * <li>IV = 4</li> + * <li>V = 5</li> + * <li>IX = 9</li> + * <li>X = 10</li> + * <li>XL = 40</li> + * <li>L = 50</li> + * <li>XC = 90</li> + * <li>C = 100</li> + * <li>D = 500</li> + * <li>M = 1000</li> + * </ul> + * + * <p>Conversion is based on repeatedly subtracting the largest possible Roman numeral value + * from the input number until it reaches zero. For example, 1994 is converted as: + * <pre> + * 1994 -> MCMXCIV (1000 + 900 + 90 + 4) + * </pre> + */ +public final class IntegerToRoman { + + // Array of Roman numeral values in descending order + private static final int[] ALL_ROMAN_NUMBERS_IN_ARABIC = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}; + + // Corresponding Roman numeral symbols + private static final String[] ALL_ROMAN_NUMBERS = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}; + + private IntegerToRoman() { + } + + /** + * Converts an integer to its Roman numeral representation. + * Steps: + * <ol> + * <li>Iterate over the Roman numeral values in descending order</li> + * <li>Calculate how many times a numeral fits</li> + * <li>Append the corresponding symbol</li> + * <li>Subtract the value from the number</li> + * <li>Repeat until the number is zero</li> + * <li>Return the Roman numeral representation</li> + * </ol> + * + * @param num the integer value to convert (must be greater than 0) + * @return the Roman numeral representation of the input integer + * or an empty string if the input is non-positive + */ + public static String integerToRoman(int num) { + if (num <= 0) { + return ""; + } + + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < ALL_ROMAN_NUMBERS_IN_ARABIC.length; i++) { + int times = num / ALL_ROMAN_NUMBERS_IN_ARABIC[i]; + builder.append(ALL_ROMAN_NUMBERS[i].repeat(Math.max(0, times))); + num -= times * ALL_ROMAN_NUMBERS_IN_ARABIC[i]; + } + + return builder.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/conversions/MorseCodeConverter.java b/src/main/java/com/thealgorithms/conversions/MorseCodeConverter.java new file mode 100644 index 000000000000..a3973da0c586 --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/MorseCodeConverter.java @@ -0,0 +1,98 @@ +package com.thealgorithms.conversions; + +import java.util.HashMap; +import java.util.Map; + +/** + * Converts text to Morse code and vice-versa. + * Text to Morse code: Each letter is separated by a space and each word is separated by a pipe (|). + * Example: "HELLO WORLD" -> ".... . .-.. .-.. --- | .-- --- .-. .-.. -.." + * + * Morse code to text: Each letter is separated by a space and each word is separated by a pipe (|). + * Example: ".... . .-.. .-.. --- | .-- --- .-. .-.. -.." -> "HELLO WORLD" + * + * Applications: Used in radio communications and algorithmic challenges. + * + * @author Hardvan + */ +public final class MorseCodeConverter { + private MorseCodeConverter() { + } + + private static final Map<Character, String> MORSE_MAP = new HashMap<>(); + private static final Map<String, Character> REVERSE_MAP = new HashMap<>(); + + static { + MORSE_MAP.put('A', ".-"); + MORSE_MAP.put('B', "-..."); + MORSE_MAP.put('C', "-.-."); + MORSE_MAP.put('D', "-.."); + MORSE_MAP.put('E', "."); + MORSE_MAP.put('F', "..-."); + MORSE_MAP.put('G', "--."); + MORSE_MAP.put('H', "...."); + MORSE_MAP.put('I', ".."); + MORSE_MAP.put('J', ".---"); + MORSE_MAP.put('K', "-.-"); + MORSE_MAP.put('L', ".-.."); + MORSE_MAP.put('M', "--"); + MORSE_MAP.put('N', "-."); + MORSE_MAP.put('O', "---"); + MORSE_MAP.put('P', ".--."); + MORSE_MAP.put('Q', "--.-"); + MORSE_MAP.put('R', ".-."); + MORSE_MAP.put('S', "..."); + MORSE_MAP.put('T', "-"); + MORSE_MAP.put('U', "..-"); + MORSE_MAP.put('V', "...-"); + MORSE_MAP.put('W', ".--"); + MORSE_MAP.put('X', "-..-"); + MORSE_MAP.put('Y', "-.--"); + MORSE_MAP.put('Z', "--.."); + + // Build reverse map for decoding + MORSE_MAP.forEach((k, v) -> REVERSE_MAP.put(v, k)); + } + + /** + * Converts text to Morse code. + * Each letter is separated by a space and each word is separated by a pipe (|). + * + * @param text The text to convert to Morse code. + * @return The Morse code representation of the text. + */ + public static String textToMorse(String text) { + StringBuilder morse = new StringBuilder(); + String[] words = text.toUpperCase().split(" "); + for (int i = 0; i < words.length; i++) { + for (char c : words[i].toCharArray()) { + morse.append(MORSE_MAP.getOrDefault(c, "")).append(" "); + } + if (i < words.length - 1) { + morse.append("| "); + } + } + return morse.toString().trim(); + } + + /** + * Converts Morse code to text. + * Each letter is separated by a space and each word is separated by a pipe (|). + * + * @param morse The Morse code to convert to text. + * @return The text representation of the Morse code. + */ + public static String morseToText(String morse) { + StringBuilder text = new StringBuilder(); + String[] words = morse.split(" \\| "); + for (int i = 0; i < words.length; i++) { + for (String code : words[i].split(" ")) { + text.append(REVERSE_MAP.getOrDefault(code, '?')); + } + if (i < words.length - 1) { + text.append(" "); + } + } + return text.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/conversions/NumberToWords.java b/src/main/java/com/thealgorithms/conversions/NumberToWords.java new file mode 100644 index 000000000000..e39c5b2dea86 --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/NumberToWords.java @@ -0,0 +1,100 @@ +package com.thealgorithms.conversions; + +import java.math.BigDecimal; + +/** + A Java-based utility for converting numeric values into their English word + representations. Whether you need to convert a small number, a large number + with millions and billions, or even a number with decimal places, this utility + has you covered. + * + */ +public final class NumberToWords { + + private NumberToWords() { + } + + private static final String[] UNITS = {"", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"}; + + private static final String[] TENS = {"", "", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"}; + + private static final String[] POWERS = {"", "Thousand", "Million", "Billion", "Trillion"}; + + private static final String ZERO = "Zero"; + private static final String POINT = " Point"; + private static final String NEGATIVE = "Negative "; + + public static String convert(BigDecimal number) { + if (number == null) { + return "Invalid Input"; + } + + // Check for negative sign + boolean isNegative = number.signum() < 0; + + // Split the number into whole and fractional parts + BigDecimal[] parts = number.abs().divideAndRemainder(BigDecimal.ONE); + BigDecimal wholePart = parts[0]; // Keep whole part as BigDecimal + String fractionalPartStr = parts[1].compareTo(BigDecimal.ZERO) > 0 ? parts[1].toPlainString().substring(2) : ""; // Get fractional part only if it exists + + // Convert whole part to words + StringBuilder result = new StringBuilder(); + if (isNegative) { + result.append(NEGATIVE); + } + result.append(convertWholeNumberToWords(wholePart)); + + // Convert fractional part to words + if (!fractionalPartStr.isEmpty()) { + result.append(POINT); + for (char digit : fractionalPartStr.toCharArray()) { + int digitValue = Character.getNumericValue(digit); + result.append(" ").append(digitValue == 0 ? ZERO : UNITS[digitValue]); + } + } + + return result.toString().trim(); + } + + private static String convertWholeNumberToWords(BigDecimal number) { + if (number.compareTo(BigDecimal.ZERO) == 0) { + return ZERO; + } + + StringBuilder words = new StringBuilder(); + int power = 0; + + while (number.compareTo(BigDecimal.ZERO) > 0) { + // Get the last three digits + BigDecimal[] divisionResult = number.divideAndRemainder(BigDecimal.valueOf(1000)); + int chunk = divisionResult[1].intValue(); + + if (chunk > 0) { + String chunkWords = convertChunk(chunk); + if (power > 0) { + words.insert(0, POWERS[power] + " "); + } + words.insert(0, chunkWords + " "); + } + + number = divisionResult[0]; // Continue with the remaining part + power++; + } + + return words.toString().trim(); + } + + private static String convertChunk(int number) { + String chunkWords; + + if (number < 20) { + chunkWords = UNITS[number]; + } else if (number < 100) { + chunkWords = TENS[number / 10] + (number % 10 > 0 ? " " + UNITS[number % 10] : ""); + } else { + chunkWords = UNITS[number / 100] + " Hundred" + (number % 100 > 0 ? " " + convertChunk(number % 100) : ""); + } + + return chunkWords; + } +} diff --git a/src/main/java/com/thealgorithms/conversions/OctalToBinary.java b/src/main/java/com/thealgorithms/conversions/OctalToBinary.java new file mode 100644 index 000000000000..a66db97633b4 --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/OctalToBinary.java @@ -0,0 +1,82 @@ +package com.thealgorithms.conversions; + +/** + * A utility class to convert an octal (base-8) number into its binary (base-2) representation. + * + * <p>This class provides methods to: + * <ul> + * <li>Convert an octal number to its binary equivalent</li> + * <li>Convert individual octal digits to binary</li> + * </ul> + * + * <h2>Octal to Binary Conversion:</h2> + * <p>An octal number is converted to binary by converting each octal digit to its 3-bit binary equivalent. + * The result is a long representing the full binary equivalent of the octal number.</p> + * + * <h2>Example Usage</h2> + * <pre> + * long binary = OctalToBinary.convertOctalToBinary(52); // Output: 101010 (52 in octal is 101010 in binary) + * </pre> + * + * @author Bama Charan Chhandogi + * @see <a href="https://en.wikipedia.org/wiki/Octal">Octal Number System</a> + * @see <a href="https://en.wikipedia.org/wiki/Binary_number">Binary Number System</a> + */ +public final class OctalToBinary { + private OctalToBinary() { + } + + /** + * Converts an octal number to its binary representation. + * + * <p>Each octal digit is individually converted to its 3-bit binary equivalent, and the binary + * digits are concatenated to form the final binary number.</p> + * + * @param octalNumber the octal number to convert (non-negative integer) + * @return the binary equivalent as a long + */ + public static long convertOctalToBinary(int octalNumber) { + long binaryNumber = 0; + int digitPosition = 1; + + while (octalNumber != 0) { + int octalDigit = octalNumber % 10; + long binaryDigit = convertOctalDigitToBinary(octalDigit); + + binaryNumber += binaryDigit * digitPosition; + + octalNumber /= 10; + digitPosition *= 1000; + } + + return binaryNumber; + } + + /** + * Converts a single octal digit (0-7) to its binary equivalent. + * + * <p>For example: + * <ul> + * <li>Octal digit 7 is converted to binary 111</li> + * <li>Octal digit 3 is converted to binary 011</li> + * </ul> + * </p> + * + * @param octalDigit a single octal digit (0-7) + * @return the binary equivalent as a long + */ + public static long convertOctalDigitToBinary(int octalDigit) { + long binaryDigit = 0; + int binaryMultiplier = 1; + + while (octalDigit != 0) { + int octalDigitRemainder = octalDigit % 2; + binaryDigit += octalDigitRemainder * binaryMultiplier; + + octalDigit /= 2; + binaryMultiplier *= 10; + } + + return binaryDigit; + } +} diff --git a/src/main/java/com/thealgorithms/conversions/OctalToDecimal.java b/src/main/java/com/thealgorithms/conversions/OctalToDecimal.java new file mode 100644 index 000000000000..d91ce6eb3634 --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/OctalToDecimal.java @@ -0,0 +1,42 @@ +package com.thealgorithms.conversions; + +/** + * Class for converting an octal number to a decimal number. Octal numbers are based on 8, using digits from 0 to 7. + * + */ +public final class OctalToDecimal { + private static final int OCTAL_BASE = 8; + + private OctalToDecimal() { + } + + /** + * Converts a given octal number (as a string) to its decimal representation. + * If the input is not a valid octal number (i.e., contains characters other than 0-7), + * the method throws an IllegalArgumentException. + * + * @param inputOctal The octal number as a string + * @return The decimal equivalent of the octal number + * @throws IllegalArgumentException if the input is not a valid octal number + */ + public static int convertOctalToDecimal(String inputOctal) { + if (inputOctal == null || inputOctal.isEmpty()) { + throw new IllegalArgumentException("Input cannot be null or empty"); + } + + int decimalValue = 0; + + for (int i = 0; i < inputOctal.length(); i++) { + char currentChar = inputOctal.charAt(i); + + if (currentChar < '0' || currentChar > '7') { + throw new IllegalArgumentException("Incorrect input: Expecting an octal number (digits 0-7)"); + } + + int currentDigit = currentChar - '0'; + decimalValue = decimalValue * OCTAL_BASE + currentDigit; + } + + return decimalValue; + } +} diff --git a/src/main/java/com/thealgorithms/conversions/OctalToHexadecimal.java b/src/main/java/com/thealgorithms/conversions/OctalToHexadecimal.java new file mode 100644 index 000000000000..bac56dc2e221 --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/OctalToHexadecimal.java @@ -0,0 +1,61 @@ +package com.thealgorithms.conversions; + +/** + * Class for converting an Octal number to its Hexadecimal equivalent. + * + * @author Tanmay Joshi + */ +public final class OctalToHexadecimal { + private static final int OCTAL_BASE = 8; + private static final int HEX_BASE = 16; + private static final String HEX_DIGITS = "0123456789ABCDEF"; + + private OctalToHexadecimal() { + } + + /** + * Converts an Octal number (as a string) to its Decimal equivalent. + * + * @param octalNumber The Octal number as a string + * @return The Decimal equivalent of the Octal number + * @throws IllegalArgumentException if the input contains invalid octal digits + */ + public static int octalToDecimal(String octalNumber) { + if (octalNumber == null || octalNumber.isEmpty()) { + throw new IllegalArgumentException("Input cannot be null or empty"); + } + + int decimalValue = 0; + for (int i = 0; i < octalNumber.length(); i++) { + char currentChar = octalNumber.charAt(i); + if (currentChar < '0' || currentChar > '7') { + throw new IllegalArgumentException("Incorrect octal digit: " + currentChar); + } + int currentDigit = currentChar - '0'; + decimalValue = decimalValue * OCTAL_BASE + currentDigit; + } + + return decimalValue; + } + + /** + * Converts a Decimal number to its Hexadecimal equivalent. + * + * @param decimalNumber The Decimal number + * @return The Hexadecimal equivalent of the Decimal number + */ + public static String decimalToHexadecimal(int decimalNumber) { + if (decimalNumber == 0) { + return "0"; + } + + StringBuilder hexValue = new StringBuilder(); + while (decimalNumber > 0) { + int digit = decimalNumber % HEX_BASE; + hexValue.insert(0, HEX_DIGITS.charAt(digit)); + decimalNumber /= HEX_BASE; + } + + return hexValue.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/conversions/PhoneticAlphabetConverter.java b/src/main/java/com/thealgorithms/conversions/PhoneticAlphabetConverter.java new file mode 100644 index 000000000000..730ce2214e2d --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/PhoneticAlphabetConverter.java @@ -0,0 +1,84 @@ +package com.thealgorithms.conversions; + +import java.util.HashMap; +import java.util.Map; + +/** + * Converts text to the NATO phonetic alphabet. + * Examples: + * "ABC" -> "Alpha Bravo Charlie" + * "Hello" -> "Hotel Echo Lima Lima Oscar" + * "123" -> "One Two Three" + * "A1B2C3" -> "Alpha One Bravo Two Charlie Three" + * + * @author Hardvan + */ +public final class PhoneticAlphabetConverter { + private PhoneticAlphabetConverter() { + } + + private static final Map<Character, String> PHONETIC_MAP = new HashMap<>(); + + static { + PHONETIC_MAP.put('A', "Alpha"); + PHONETIC_MAP.put('B', "Bravo"); + PHONETIC_MAP.put('C', "Charlie"); + PHONETIC_MAP.put('D', "Delta"); + PHONETIC_MAP.put('E', "Echo"); + PHONETIC_MAP.put('F', "Foxtrot"); + PHONETIC_MAP.put('G', "Golf"); + PHONETIC_MAP.put('H', "Hotel"); + PHONETIC_MAP.put('I', "India"); + PHONETIC_MAP.put('J', "Juliett"); + PHONETIC_MAP.put('K', "Kilo"); + PHONETIC_MAP.put('L', "Lima"); + PHONETIC_MAP.put('M', "Mike"); + PHONETIC_MAP.put('N', "November"); + PHONETIC_MAP.put('O', "Oscar"); + PHONETIC_MAP.put('P', "Papa"); + PHONETIC_MAP.put('Q', "Quebec"); + PHONETIC_MAP.put('R', "Romeo"); + PHONETIC_MAP.put('S', "Sierra"); + PHONETIC_MAP.put('T', "Tango"); + PHONETIC_MAP.put('U', "Uniform"); + PHONETIC_MAP.put('V', "Victor"); + PHONETIC_MAP.put('W', "Whiskey"); + PHONETIC_MAP.put('X', "X-ray"); + PHONETIC_MAP.put('Y', "Yankee"); + PHONETIC_MAP.put('Z', "Zulu"); + PHONETIC_MAP.put('0', "Zero"); + PHONETIC_MAP.put('1', "One"); + PHONETIC_MAP.put('2', "Two"); + PHONETIC_MAP.put('3', "Three"); + PHONETIC_MAP.put('4', "Four"); + PHONETIC_MAP.put('5', "Five"); + PHONETIC_MAP.put('6', "Six"); + PHONETIC_MAP.put('7', "Seven"); + PHONETIC_MAP.put('8', "Eight"); + PHONETIC_MAP.put('9', "Nine"); + } + + /** + * Converts text to the NATO phonetic alphabet. + * Steps: + * 1. Convert the text to uppercase. + * 2. Iterate over each character in the text. + * 3. Get the phonetic equivalent of the character from the map. + * 4. Append the phonetic equivalent to the result. + * 5. Append a space to separate the phonetic equivalents. + * 6. Return the result. + * + * @param text the text to convert + * @return the NATO phonetic alphabet + */ + public static String textToPhonetic(String text) { + StringBuilder phonetic = new StringBuilder(); + for (char c : text.toUpperCase().toCharArray()) { + if (Character.isWhitespace(c)) { + continue; + } + phonetic.append(PHONETIC_MAP.getOrDefault(c, String.valueOf(c))).append(" "); + } + return phonetic.toString().trim(); + } +} diff --git a/src/main/java/com/thealgorithms/conversions/RgbHsvConversion.java b/src/main/java/com/thealgorithms/conversions/RgbHsvConversion.java new file mode 100644 index 000000000000..84cbff09db6b --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/RgbHsvConversion.java @@ -0,0 +1,168 @@ +package com.thealgorithms.conversions; + +import java.util.Arrays; + +/** + * The RGB color model is an additive color model in which red, green, and blue + * light are added together in various ways to reproduce a broad array of + * colors. The name of the model comes from the initials of the three additive + * primary colors, red, green, and blue. Meanwhile, the HSV representation + * models how colors appear under light. In it, colors are represented using + * three components: hue, saturation and (brightness-)value. This class provides + * methods for converting colors from one representation to the other. + * (description adapted from <a href="https://en.wikipedia.org/wiki/RGB_color_model">[1]</a> and + * <a href="https://en.wikipedia.org/wiki/HSL_and_HSV">[2]</a>). + */ +public final class RgbHsvConversion { + private RgbHsvConversion() { + } + + public static void main(String[] args) { + // Expected RGB-values taken from https://www.rapidtables.com/convert/color/hsv-to-rgb.html + + // Test hsvToRgb-method + assert Arrays.equals(hsvToRgb(0, 0, 0), new int[] {0, 0, 0}); + assert Arrays.equals(hsvToRgb(0, 0, 1), new int[] {255, 255, 255}); + assert Arrays.equals(hsvToRgb(0, 1, 1), new int[] {255, 0, 0}); + assert Arrays.equals(hsvToRgb(60, 1, 1), new int[] {255, 255, 0}); + assert Arrays.equals(hsvToRgb(120, 1, 1), new int[] {0, 255, 0}); + assert Arrays.equals(hsvToRgb(240, 1, 1), new int[] {0, 0, 255}); + assert Arrays.equals(hsvToRgb(300, 1, 1), new int[] {255, 0, 255}); + assert Arrays.equals(hsvToRgb(180, 0.5, 0.5), new int[] {64, 128, 128}); + assert Arrays.equals(hsvToRgb(234, 0.14, 0.88), new int[] {193, 196, 224}); + assert Arrays.equals(hsvToRgb(330, 0.75, 0.5), new int[] {128, 32, 80}); + + // Test rgbToHsv-method + // approximate-assertions needed because of small deviations due to converting between + // int-values and double-values. + assert approximatelyEqualHsv(rgbToHsv(0, 0, 0), new double[] {0, 0, 0}); + assert approximatelyEqualHsv(rgbToHsv(255, 255, 255), new double[] {0, 0, 1}); + assert approximatelyEqualHsv(rgbToHsv(255, 0, 0), new double[] {0, 1, 1}); + assert approximatelyEqualHsv(rgbToHsv(255, 255, 0), new double[] {60, 1, 1}); + assert approximatelyEqualHsv(rgbToHsv(0, 255, 0), new double[] {120, 1, 1}); + assert approximatelyEqualHsv(rgbToHsv(0, 0, 255), new double[] {240, 1, 1}); + assert approximatelyEqualHsv(rgbToHsv(255, 0, 255), new double[] {300, 1, 1}); + assert approximatelyEqualHsv(rgbToHsv(64, 128, 128), new double[] {180, 0.5, 0.5}); + assert approximatelyEqualHsv(rgbToHsv(193, 196, 224), new double[] {234, 0.14, 0.88}); + assert approximatelyEqualHsv(rgbToHsv(128, 32, 80), new double[] {330, 0.75, 0.5}); + } + + /** + * Conversion from the HSV-representation to the RGB-representation. + * + * @param hue Hue of the color. + * @param saturation Saturation of the color. + * @param value Brightness-value of the color. + * @return The tuple of RGB-components. + */ + public static int[] hsvToRgb(double hue, double saturation, double value) { + if (hue < 0 || hue > 360) { + throw new IllegalArgumentException("hue should be between 0 and 360"); + } + + if (saturation < 0 || saturation > 1) { + throw new IllegalArgumentException("saturation should be between 0 and 1"); + } + + if (value < 0 || value > 1) { + throw new IllegalArgumentException("value should be between 0 and 1"); + } + + double chroma = value * saturation; + double hueSection = hue / 60; + double secondLargestComponent = chroma * (1 - Math.abs(hueSection % 2 - 1)); + double matchValue = value - chroma; + + return getRgbBySection(hueSection, chroma, matchValue, secondLargestComponent); + } + + /** + * Conversion from the RGB-representation to the HSV-representation. + * + * @param red Red-component of the color. + * @param green Green-component of the color. + * @param blue Blue-component of the color. + * @return The tuple of HSV-components. + */ + public static double[] rgbToHsv(int red, int green, int blue) { + if (red < 0 || red > 255) { + throw new IllegalArgumentException("red should be between 0 and 255"); + } + + if (green < 0 || green > 255) { + throw new IllegalArgumentException("green should be between 0 and 255"); + } + + if (blue < 0 || blue > 255) { + throw new IllegalArgumentException("blue should be between 0 and 255"); + } + + double dRed = (double) red / 255; + double dGreen = (double) green / 255; + double dBlue = (double) blue / 255; + double value = Math.max(Math.max(dRed, dGreen), dBlue); + double chroma = value - Math.min(Math.min(dRed, dGreen), dBlue); + double saturation = value == 0 ? 0 : chroma / value; + double hue; + + if (chroma == 0) { + hue = 0; + } else if (value == dRed) { + hue = 60 * (0 + (dGreen - dBlue) / chroma); + } else if (value == dGreen) { + hue = 60 * (2 + (dBlue - dRed) / chroma); + } else { + hue = 60 * (4 + (dRed - dGreen) / chroma); + } + + hue = (hue + 360) % 360; + + return new double[] {hue, saturation, value}; + } + + private static boolean approximatelyEqualHsv(double[] hsv1, double[] hsv2) { + boolean bHue = Math.abs(hsv1[0] - hsv2[0]) < 0.2; + boolean bSaturation = Math.abs(hsv1[1] - hsv2[1]) < 0.002; + boolean bValue = Math.abs(hsv1[2] - hsv2[2]) < 0.002; + + return bHue && bSaturation && bValue; + } + + private static int[] getRgbBySection(double hueSection, double chroma, double matchValue, double secondLargestComponent) { + int red; + int green; + int blue; + + if (hueSection >= 0 && hueSection <= 1) { + red = convertToInt(chroma + matchValue); + green = convertToInt(secondLargestComponent + matchValue); + blue = convertToInt(matchValue); + } else if (hueSection > 1 && hueSection <= 2) { + red = convertToInt(secondLargestComponent + matchValue); + green = convertToInt(chroma + matchValue); + blue = convertToInt(matchValue); + } else if (hueSection > 2 && hueSection <= 3) { + red = convertToInt(matchValue); + green = convertToInt(chroma + matchValue); + blue = convertToInt(secondLargestComponent + matchValue); + } else if (hueSection > 3 && hueSection <= 4) { + red = convertToInt(matchValue); + green = convertToInt(secondLargestComponent + matchValue); + blue = convertToInt(chroma + matchValue); + } else if (hueSection > 4 && hueSection <= 5) { + red = convertToInt(secondLargestComponent + matchValue); + green = convertToInt(matchValue); + blue = convertToInt(chroma + matchValue); + } else { + red = convertToInt(chroma + matchValue); + green = convertToInt(matchValue); + blue = convertToInt(secondLargestComponent + matchValue); + } + + return new int[] {red, green, blue}; + } + + private static int convertToInt(double input) { + return (int) Math.round(255 * input); + } +} diff --git a/src/main/java/com/thealgorithms/conversions/RomanToInteger.java b/src/main/java/com/thealgorithms/conversions/RomanToInteger.java new file mode 100644 index 000000000000..a634c720326f --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/RomanToInteger.java @@ -0,0 +1,91 @@ +package com.thealgorithms.conversions; + +import java.util.HashMap; +import java.util.Map; + +/** + * A utility class to convert Roman numerals into integers. + * + * <p>Roman numerals are based on seven symbols given below: + * <ul> + * <li>I = 1</li> + * <li>V = 5</li> + * <li>X = 10</li> + * <li>L = 50</li> + * <li>C = 100</li> + * <li>D = 500</li> + * <li>M = 1000</li> + * </ul> + * + * <p>If a smaller numeral appears before a larger numeral, it is subtracted. + * Otherwise, it is added. For example: + * <pre> + * MCMXCIV = 1000 + (1000 - 100) + (100 - 10) + (5 - 1) = 1994 + * </pre> + */ +public final class RomanToInteger { + + private static final Map<Character, Integer> ROMAN_TO_INT = new HashMap<>() { + { + put('I', 1); + put('V', 5); + put('X', 10); + put('L', 50); + put('C', 100); + put('D', 500); + put('M', 1000); + } + }; + + private RomanToInteger() { + } + + /** + * Converts a single Roman numeral character to its integer value. + * + * @param symbol the Roman numeral character + * @return the corresponding integer value + * @throws IllegalArgumentException if the symbol is not a valid Roman numeral + */ + private static int romanSymbolToInt(final char symbol) { + return ROMAN_TO_INT.computeIfAbsent(symbol, c -> { throw new IllegalArgumentException("Unknown Roman symbol: " + c); }); + } + + /** + * Converts a Roman numeral string to its integer equivalent. + * Steps: + * <ol> + * <li>Iterate over the string from right to left.</li> + * <li>For each character, convert it to an integer value.</li> + * <li>If the current value is greater than or equal to the max previous value, add it.</li> + * <li>Otherwise, subtract it from the sum.</li> + * <li>Update the max previous value.</li> + * <li>Return the sum.</li> + * </ol> + * + * @param roman the Roman numeral string + * @return the integer value of the Roman numeral + * @throws IllegalArgumentException if the input contains invalid Roman characters + * @throws NullPointerException if the input is {@code null} + */ + public static int romanToInt(String roman) { + if (roman == null) { + throw new NullPointerException("Input cannot be null"); + } + + roman = roman.toUpperCase(); + int sum = 0; + int maxPrevValue = 0; + for (int i = roman.length() - 1; i >= 0; i--) { + int currentValue = romanSymbolToInt(roman.charAt(i)); + if (currentValue >= maxPrevValue) { + sum += currentValue; + maxPrevValue = currentValue; + } else { + sum -= currentValue; + } + } + + return sum; + } +} diff --git a/src/main/java/com/thealgorithms/conversions/TurkishToLatinConversion.java b/src/main/java/com/thealgorithms/conversions/TurkishToLatinConversion.java new file mode 100644 index 000000000000..30030de6c1bd --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/TurkishToLatinConversion.java @@ -0,0 +1,56 @@ +package com.thealgorithms.conversions; + +/** + * Converts turkish character to latin character + * + * @author Özgün Gökşenli + */ +public final class TurkishToLatinConversion { + private TurkishToLatinConversion() { + } + + /** + * This method converts a turkish character to latin character. + * Steps: + * 1. Define turkish characters and their corresponding latin characters + * 2. Replace all turkish characters with their corresponding latin characters + * 3. Return the converted string + * + * @param param String paramter + * @return String + */ + public static String convertTurkishToLatin(String param) { + char[] turkishChars = new char[] { + 0x131, + 0x130, + 0xFC, + 0xDC, + 0xF6, + 0xD6, + 0x15F, + 0x15E, + 0xE7, + 0xC7, + 0x11F, + 0x11E, + }; + char[] latinChars = new char[] { + 'i', + 'I', + 'u', + 'U', + 'o', + 'O', + 's', + 'S', + 'c', + 'C', + 'g', + 'G', + }; + for (int i = 0; i < turkishChars.length; i++) { + param = param.replaceAll(String.valueOf(turkishChars[i]), String.valueOf(latinChars[i])); + } + return param; + } +} diff --git a/src/main/java/com/thealgorithms/conversions/UnitConversions.java b/src/main/java/com/thealgorithms/conversions/UnitConversions.java new file mode 100644 index 000000000000..15f74a21a17e --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/UnitConversions.java @@ -0,0 +1,51 @@ +package com.thealgorithms.conversions; + +import static java.util.Map.entry; + +import java.util.Map; +import org.apache.commons.lang3.tuple.Pair; + +/** + * A utility class to perform unit conversions between different measurement systems. + * + * <p>Currently, the class supports temperature conversions between several scales: + * Celsius, Fahrenheit, Kelvin, Réaumur, Delisle, and Rankine. + * + * <h2>Example Usage</h2> + * <pre> + * double result = UnitConversions.TEMPERATURE.convert("Celsius", "Fahrenheit", 100.0); + * // Output: 212.0 (Celsius to Fahrenheit conversion of 100°C) + * </pre> + * + * <p>This class makes use of an {@link UnitsConverter} that handles the conversion logic + * based on predefined affine transformations. These transformations include scaling factors + * and offsets for temperature conversions. + * + * <h2>Temperature Scales Supported</h2> + * <ul> + * <li>Celsius</li> + * <li>Fahrenheit</li> + * <li>Kelvin</li> + * <li>Réaumur</li> + * <li>Delisle</li> + * <li>Rankine</li> + * </ul> + */ +public final class UnitConversions { + private UnitConversions() { + } + + /** + * A preconfigured instance of {@link UnitsConverter} for temperature conversions. + * The converter handles conversions between the following temperature units: + * <ul> + * <li>Kelvin to Celsius</li> + * <li>Celsius to Fahrenheit</li> + * <li>Réaumur to Celsius</li> + * <li>Delisle to Celsius</li> + * <li>Rankine to Kelvin</li> + * </ul> + */ + public static final UnitsConverter TEMPERATURE = new UnitsConverter(Map.ofEntries(entry(Pair.of("Kelvin", "Celsius"), new AffineConverter(1.0, -273.15)), entry(Pair.of("Celsius", "Fahrenheit"), new AffineConverter(9.0 / 5.0, 32.0)), + entry(Pair.of("Réaumur", "Celsius"), new AffineConverter(5.0 / 4.0, 0.0)), entry(Pair.of("Delisle", "Celsius"), new AffineConverter(-2.0 / 3.0, 100.0)), entry(Pair.of("Rankine", "Kelvin"), new AffineConverter(5.0 / 9.0, 0.0)))); +} diff --git a/src/main/java/com/thealgorithms/conversions/UnitsConverter.java b/src/main/java/com/thealgorithms/conversions/UnitsConverter.java new file mode 100644 index 000000000000..00690b2c0f9b --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/UnitsConverter.java @@ -0,0 +1,147 @@ +package com.thealgorithms.conversions; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import org.apache.commons.lang3.tuple.Pair; + +/** + * A class that handles unit conversions using affine transformations. + * + * <p>The {@code UnitsConverter} allows converting values between different units using + * pre-defined affine conversion formulas. Each conversion is represented by an + * {@link AffineConverter} that defines the scaling and offset for the conversion. + * + * <p>For each unit, both direct conversions (e.g., Celsius to Fahrenheit) and inverse + * conversions (e.g., Fahrenheit to Celsius) are generated automatically. It also computes + * transitive conversions (e.g., Celsius to Kelvin via Fahrenheit if both conversions exist). + * + * <p>Key features include: + * <ul> + * <li>Automatic handling of inverse conversions (e.g., Fahrenheit to Celsius).</li> + * <li>Compositional conversions, meaning if conversions between A -> B and B -> C exist, + * it can automatically generate A -> C conversion.</li> + * <li>Supports multiple unit systems as long as conversions are provided in pairs.</li> + * </ul> + * + * <h2>Example Usage</h2> + * <pre> + * Map<Pair<String, String>, AffineConverter> basicConversions = Map.ofEntries( + * entry(Pair.of("Celsius", "Fahrenheit"), new AffineConverter(9.0 / 5.0, 32.0)), + * entry(Pair.of("Kelvin", "Celsius"), new AffineConverter(1.0, -273.15)) + * ); + * + * UnitsConverter converter = new UnitsConverter(basicConversions); + * double result = converter.convert("Celsius", "Fahrenheit", 100.0); + * // Output: 212.0 (Celsius to Fahrenheit conversion of 100°C) + * </pre> + * + * <h2>Exception Handling</h2> + * <ul> + * <li>If the input unit and output unit are the same, an {@link IllegalArgumentException} is thrown.</li> + * <li>If a conversion between the requested units does not exist, a {@link NoSuchElementException} is thrown.</li> + * </ul> + */ +public final class UnitsConverter { + private final Map<Pair<String, String>, AffineConverter> conversions; + private final Set<String> units; + + private static void putIfNeeded(Map<Pair<String, String>, AffineConverter> conversions, final String inputUnit, final String outputUnit, final AffineConverter converter) { + if (!inputUnit.equals(outputUnit)) { + final var key = Pair.of(inputUnit, outputUnit); + conversions.putIfAbsent(key, converter); + } + } + + private static Map<Pair<String, String>, AffineConverter> addInversions(final Map<Pair<String, String>, AffineConverter> knownConversions) { + Map<Pair<String, String>, AffineConverter> res = new HashMap<Pair<String, String>, AffineConverter>(); + for (final var curConversion : knownConversions.entrySet()) { + final var inputUnit = curConversion.getKey().getKey(); + final var outputUnit = curConversion.getKey().getValue(); + putIfNeeded(res, inputUnit, outputUnit, curConversion.getValue()); + putIfNeeded(res, outputUnit, inputUnit, curConversion.getValue().invert()); + } + return res; + } + + private static Map<Pair<String, String>, AffineConverter> addCompositions(final Map<Pair<String, String>, AffineConverter> knownConversions) { + Map<Pair<String, String>, AffineConverter> res = new HashMap<Pair<String, String>, AffineConverter>(); + for (final var first : knownConversions.entrySet()) { + final var firstKey = first.getKey(); + putIfNeeded(res, firstKey.getKey(), firstKey.getValue(), first.getValue()); + for (final var second : knownConversions.entrySet()) { + final var secondKey = second.getKey(); + if (firstKey.getValue().equals(secondKey.getKey())) { + final var newConversion = second.getValue().compose(first.getValue()); + putIfNeeded(res, firstKey.getKey(), secondKey.getValue(), newConversion); + } + } + } + return res; + } + + private static Map<Pair<String, String>, AffineConverter> addAll(final Map<Pair<String, String>, AffineConverter> knownConversions) { + final var res = addInversions(knownConversions); + return addCompositions(res); + } + + private static Map<Pair<String, String>, AffineConverter> computeAllConversions(final Map<Pair<String, String>, AffineConverter> basicConversions) { + var tmp = basicConversions; + var res = addAll(tmp); + while (res.size() != tmp.size()) { + tmp = res; + res = addAll(tmp); + } + return res; + } + + private static Set<String> extractUnits(final Map<Pair<String, String>, AffineConverter> conversions) { + Set<String> res = new HashSet<>(); + for (final var conversion : conversions.entrySet()) { + res.add(conversion.getKey().getKey()); + } + return res; + } + + /** + * Constructor for {@code UnitsConverter}. + * + * <p>Accepts a map of basic conversions and automatically generates inverse and + * transitive conversions. + * + * @param basicConversions the initial set of unit conversions to add. + */ + public UnitsConverter(final Map<Pair<String, String>, AffineConverter> basicConversions) { + conversions = computeAllConversions(basicConversions); + units = extractUnits(conversions); + } + + /** + * Converts a value from one unit to another. + * + * @param inputUnit the unit of the input value. + * @param outputUnit the unit to convert the value into. + * @param value the value to convert. + * @return the converted value in the target unit. + * @throws IllegalArgumentException if inputUnit equals outputUnit. + * @throws NoSuchElementException if no conversion exists between the units. + */ + public double convert(final String inputUnit, final String outputUnit, final double value) { + if (inputUnit.equals(outputUnit)) { + throw new IllegalArgumentException("inputUnit must be different from outputUnit."); + } + final var conversionKey = Pair.of(inputUnit, outputUnit); + return conversions.computeIfAbsent(conversionKey, k -> { throw new NoSuchElementException("No converter for: " + k); }).convert(value); + } + + /** + * Retrieves the set of all units supported by this converter. + * + * @return a set of available units. + */ + public Set<String> availableUnits() { + return units; + } +} diff --git a/src/main/java/com/thealgorithms/conversions/WordsToNumber.java b/src/main/java/com/thealgorithms/conversions/WordsToNumber.java new file mode 100644 index 000000000000..e2b81a0f4b47 --- /dev/null +++ b/src/main/java/com/thealgorithms/conversions/WordsToNumber.java @@ -0,0 +1,343 @@ +package com.thealgorithms.conversions; + +import java.io.Serial; +import java.math.BigDecimal; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + A Java-based utility for converting English word representations of numbers + into their numeric form. This utility supports whole numbers, decimals, + large values up to trillions, and even scientific notation where applicable. + It ensures accurate parsing while handling edge cases like negative numbers, + improper word placements, and ambiguous inputs. + * + */ + +public final class WordsToNumber { + + private WordsToNumber() { + } + + private enum NumberWord { + ZERO("zero", 0), + ONE("one", 1), + TWO("two", 2), + THREE("three", 3), + FOUR("four", 4), + FIVE("five", 5), + SIX("six", 6), + SEVEN("seven", 7), + EIGHT("eight", 8), + NINE("nine", 9), + TEN("ten", 10), + ELEVEN("eleven", 11), + TWELVE("twelve", 12), + THIRTEEN("thirteen", 13), + FOURTEEN("fourteen", 14), + FIFTEEN("fifteen", 15), + SIXTEEN("sixteen", 16), + SEVENTEEN("seventeen", 17), + EIGHTEEN("eighteen", 18), + NINETEEN("nineteen", 19), + TWENTY("twenty", 20), + THIRTY("thirty", 30), + FORTY("forty", 40), + FIFTY("fifty", 50), + SIXTY("sixty", 60), + SEVENTY("seventy", 70), + EIGHTY("eighty", 80), + NINETY("ninety", 90); + + private final String word; + private final int value; + + NumberWord(String word, int value) { + this.word = word; + this.value = value; + } + + public static Integer getValue(String word) { + for (NumberWord num : values()) { + if (word.equals(num.word)) { + return num.value; + } + } + return null; + } + } + + private enum PowerOfTen { + THOUSAND("thousand", new BigDecimal("1000")), + MILLION("million", new BigDecimal("1000000")), + BILLION("billion", new BigDecimal("1000000000")), + TRILLION("trillion", new BigDecimal("1000000000000")); + + private final String word; + private final BigDecimal value; + + PowerOfTen(String word, BigDecimal value) { + this.word = word; + this.value = value; + } + + public static BigDecimal getValue(String word) { + for (PowerOfTen power : values()) { + if (word.equals(power.word)) { + return power.value; + } + } + return null; + } + } + + public static String convert(String numberInWords) { + if (numberInWords == null) { + throw new WordsToNumberException(WordsToNumberException.ErrorType.NULL_INPUT, ""); + } + + ArrayDeque<String> wordDeque = preprocessWords(numberInWords); + BigDecimal completeNumber = convertWordQueueToBigDecimal(wordDeque); + + return completeNumber.toString(); + } + + public static BigDecimal convertToBigDecimal(String numberInWords) { + String conversionResult = convert(numberInWords); + return new BigDecimal(conversionResult); + } + + private static ArrayDeque<String> preprocessWords(String numberInWords) { + String[] wordSplitArray = numberInWords.trim().split("[ ,-]"); + ArrayDeque<String> wordDeque = new ArrayDeque<>(); + for (String word : wordSplitArray) { + if (word.isEmpty()) { + continue; + } + wordDeque.add(word.toLowerCase()); + } + if (wordDeque.isEmpty()) { + throw new WordsToNumberException(WordsToNumberException.ErrorType.NULL_INPUT, ""); + } + return wordDeque; + } + + private static void handleConjunction(boolean prevNumWasHundred, boolean prevNumWasPowerOfTen, ArrayDeque<String> wordDeque) { + if (wordDeque.isEmpty()) { + throw new WordsToNumberException(WordsToNumberException.ErrorType.INVALID_CONJUNCTION, ""); + } + + String nextWord = wordDeque.pollFirst(); + String afterNextWord = wordDeque.peekFirst(); + + wordDeque.addFirst(nextWord); + + Integer number = NumberWord.getValue(nextWord); + + boolean isPrevWordValid = prevNumWasHundred || prevNumWasPowerOfTen; + boolean isNextWordValid = number != null && (number >= 10 || afterNextWord == null || "point".equals(afterNextWord)); + + if (!isPrevWordValid || !isNextWordValid) { + throw new WordsToNumberException(WordsToNumberException.ErrorType.INVALID_CONJUNCTION, ""); + } + } + + private static BigDecimal handleHundred(BigDecimal currentChunk, String word, boolean prevNumWasPowerOfTen) { + boolean currentChunkIsZero = currentChunk.compareTo(BigDecimal.ZERO) == 0; + if (currentChunk.compareTo(BigDecimal.TEN) >= 0 || prevNumWasPowerOfTen) { + throw new WordsToNumberException(WordsToNumberException.ErrorType.UNEXPECTED_WORD, word); + } + if (currentChunkIsZero) { + currentChunk = currentChunk.add(BigDecimal.ONE); + } + return currentChunk.multiply(BigDecimal.valueOf(100)); + } + + private static void handlePowerOfTen(List<BigDecimal> chunks, BigDecimal currentChunk, BigDecimal powerOfTen, String word, boolean prevNumWasPowerOfTen) { + boolean currentChunkIsZero = currentChunk.compareTo(BigDecimal.ZERO) == 0; + if (currentChunkIsZero || prevNumWasPowerOfTen) { + throw new WordsToNumberException(WordsToNumberException.ErrorType.UNEXPECTED_WORD, word); + } + BigDecimal nextChunk = currentChunk.multiply(powerOfTen); + + if (!(chunks.isEmpty() || isAdditionSafe(chunks.getLast(), nextChunk))) { + throw new WordsToNumberException(WordsToNumberException.ErrorType.UNEXPECTED_WORD, word); + } + chunks.add(nextChunk); + } + + private static BigDecimal handleNumber(Collection<BigDecimal> chunks, BigDecimal currentChunk, String word, Integer number) { + boolean currentChunkIsZero = currentChunk.compareTo(BigDecimal.ZERO) == 0; + if (number == 0 && !(currentChunkIsZero && chunks.isEmpty())) { + throw new WordsToNumberException(WordsToNumberException.ErrorType.UNEXPECTED_WORD, word); + } + BigDecimal bigDecimalNumber = BigDecimal.valueOf(number); + + if (!currentChunkIsZero && !isAdditionSafe(currentChunk, bigDecimalNumber)) { + throw new WordsToNumberException(WordsToNumberException.ErrorType.UNEXPECTED_WORD, word); + } + return currentChunk.add(bigDecimalNumber); + } + + private static void handlePoint(Collection<BigDecimal> chunks, BigDecimal currentChunk, ArrayDeque<String> wordDeque) { + boolean currentChunkIsZero = currentChunk.compareTo(BigDecimal.ZERO) == 0; + if (!currentChunkIsZero) { + chunks.add(currentChunk); + } + + String decimalPart = convertDecimalPart(wordDeque); + chunks.add(new BigDecimal(decimalPart)); + } + + private static void handleNegative(boolean isNegative) { + if (isNegative) { + throw new WordsToNumberException(WordsToNumberException.ErrorType.MULTIPLE_NEGATIVES, ""); + } + throw new WordsToNumberException(WordsToNumberException.ErrorType.INVALID_NEGATIVE, ""); + } + + private static BigDecimal convertWordQueueToBigDecimal(ArrayDeque<String> wordDeque) { + BigDecimal currentChunk = BigDecimal.ZERO; + List<BigDecimal> chunks = new ArrayList<>(); + + boolean isNegative = "negative".equals(wordDeque.peek()); + if (isNegative) { + wordDeque.poll(); + } + + boolean prevNumWasHundred = false; + boolean prevNumWasPowerOfTen = false; + + while (!wordDeque.isEmpty()) { + String word = wordDeque.poll(); + + switch (word) { + case "and" -> { + handleConjunction(prevNumWasHundred, prevNumWasPowerOfTen, wordDeque); + continue; + } + case "hundred" -> { + currentChunk = handleHundred(currentChunk, word, prevNumWasPowerOfTen); + prevNumWasHundred = true; + continue; + } + default -> { + + } + } + prevNumWasHundred = false; + + BigDecimal powerOfTen = PowerOfTen.getValue(word); + if (powerOfTen != null) { + handlePowerOfTen(chunks, currentChunk, powerOfTen, word, prevNumWasPowerOfTen); + currentChunk = BigDecimal.ZERO; + prevNumWasPowerOfTen = true; + continue; + } + prevNumWasPowerOfTen = false; + + Integer number = NumberWord.getValue(word); + if (number != null) { + currentChunk = handleNumber(chunks, currentChunk, word, number); + continue; + } + + switch (word) { + case "point" -> { + handlePoint(chunks, currentChunk, wordDeque); + currentChunk = BigDecimal.ZERO; + continue; + } + case "negative" -> { + handleNegative(isNegative); + } + default -> { + + } + } + + throw new WordsToNumberException(WordsToNumberException.ErrorType.UNKNOWN_WORD, word); + } + + if (currentChunk.compareTo(BigDecimal.ZERO) != 0) { + chunks.add(currentChunk); + } + + BigDecimal completeNumber = combineChunks(chunks); + return isNegative ? completeNumber.multiply(BigDecimal.valueOf(-1)) + : + completeNumber; + } + + private static boolean isAdditionSafe(BigDecimal currentChunk, BigDecimal number) { + int chunkDigitCount = currentChunk.toString().length(); + int numberDigitCount = number.toString().length(); + return chunkDigitCount > numberDigitCount; + } + + private static String convertDecimalPart(ArrayDeque<String> wordDeque) { + StringBuilder decimalPart = new StringBuilder("."); + + while (!wordDeque.isEmpty()) { + String word = wordDeque.poll(); + Integer number = NumberWord.getValue(word); + if (number == null) { + throw new WordsToNumberException(WordsToNumberException.ErrorType.UNEXPECTED_WORD_AFTER_POINT, word); + } + decimalPart.append(number); + } + + boolean missingNumbers = decimalPart.length() == 1; + if (missingNumbers) { + throw new WordsToNumberException(WordsToNumberException.ErrorType.MISSING_DECIMAL_NUMBERS, ""); + } + return decimalPart.toString(); + } + + private static BigDecimal combineChunks(List<BigDecimal> chunks) { + BigDecimal completeNumber = BigDecimal.ZERO; + for (BigDecimal chunk : chunks) { + completeNumber = completeNumber.add(chunk); + } + return completeNumber; + } + } + + class WordsToNumberException extends RuntimeException { + + @Serial private static final long serialVersionUID = 1L; + + enum ErrorType { + NULL_INPUT("'null' or empty input provided"), + UNKNOWN_WORD("Unknown Word: "), + UNEXPECTED_WORD("Unexpected Word: "), + UNEXPECTED_WORD_AFTER_POINT("Unexpected Word (after Point): "), + MISSING_DECIMAL_NUMBERS("Decimal part is missing numbers."), + MULTIPLE_NEGATIVES("Multiple 'Negative's detected."), + INVALID_NEGATIVE("Incorrect 'negative' placement"), + INVALID_CONJUNCTION("Incorrect 'and' placement"); + + private final String message; + + ErrorType(String message) { + this.message = message; + } + + public String formatMessage(String details) { + return "Invalid Input. " + message + (details.isEmpty() ? "" : details); + } + } + + public final ErrorType errorType; + + WordsToNumberException(ErrorType errorType, String details) { + super(errorType.formatMessage(details)); + this.errorType = errorType; + } + + public ErrorType getErrorType() { + return errorType; + } + } diff --git a/src/main/java/com/thealgorithms/datastructures/Node.java b/src/main/java/com/thealgorithms/datastructures/Node.java new file mode 100644 index 000000000000..c8d0e6cb4f7d --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/Node.java @@ -0,0 +1,32 @@ +package com.thealgorithms.datastructures; + +import java.util.ArrayList; +import java.util.List; + +public class Node<T> { + + private final T value; + private final List<Node<T>> children; + + public Node(final T value) { + this.value = value; + this.children = new ArrayList<>(); + } + + public Node(final T value, final List<Node<T>> children) { + this.value = value; + this.children = children; + } + + public T getValue() { + return value; + } + + public void addChild(Node<T> child) { + children.add(child); + } + + public List<Node<T>> getChildren() { + return children; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/bags/Bag.java b/src/main/java/com/thealgorithms/datastructures/bags/Bag.java new file mode 100644 index 000000000000..afc3bbe40cce --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/bags/Bag.java @@ -0,0 +1,138 @@ +package com.thealgorithms.datastructures.bags; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * A generic collection that allows adding and iterating over elements but does not support + * element removal. This class implements a simple bag data structure, which can hold duplicate + * elements and provides operations to check for membership and the size of the collection. + * + * <p>Bag is not thread-safe and should not be accessed by multiple threads concurrently. + * + * @param <E> the type of elements in this bag + */ +public class Bag<E> implements Iterable<E> { + + private Node<E> firstElement; // Reference to the first element in the bag + private int size; // Count of elements in the bag + + // Node class representing each element in the bag + private static final class Node<E> { + private E content; + private Node<E> nextElement; + } + + /** + * Constructs an empty bag. + * <p>This initializes the bag with zero elements. + */ + public Bag() { + firstElement = null; + size = 0; + } + + /** + * Checks if the bag is empty. + * + * @return {@code true} if the bag contains no elements; {@code false} otherwise + */ + public boolean isEmpty() { + return size == 0; + } + + /** + * Returns the number of elements in the bag. + * + * @return the number of elements currently in the bag + */ + public int size() { + return size; + } + + /** + * Adds an element to the bag. + * + * <p>This method adds the specified element to the bag. Duplicates are allowed, and the + * bag will maintain the order in which elements are added. + * + * @param element the element to add; must not be {@code null} + */ + public void add(E element) { + Node<E> newNode = new Node<>(); + newNode.content = element; + newNode.nextElement = firstElement; + firstElement = newNode; + size++; + } + + /** + * Checks if the bag contains a specific element. + * + * <p>This method uses the {@code equals} method of the element to determine membership. + * + * @param element the element to check for; must not be {@code null} + * @return {@code true} if the bag contains the specified element; {@code false} otherwise + */ + public boolean contains(E element) { + for (E value : this) { + if (value.equals(element)) { + return true; + } + } + return false; + } + + /** + * Returns an iterator over the elements in this bag. + * + * <p>The iterator provides a way to traverse the elements in the order they were added. + * + * @return an iterator that iterates over the elements in the bag + */ + @Override + public Iterator<E> iterator() { + return new ListIterator<>(firstElement); + } + + // Private class for iterating over elements + private static class ListIterator<E> implements Iterator<E> { + + private Node<E> currentElement; + + /** + * Constructs a ListIterator starting from the given first element. + * + * @param firstElement the first element of the bag to iterate over + */ + ListIterator(Node<E> firstElement) { + this.currentElement = firstElement; + } + + /** + * Checks if there are more elements to iterate over. + * + * @return {@code true} if there are more elements; {@code false} otherwise + */ + @Override + public boolean hasNext() { + return currentElement != null; + } + + /** + * Returns the next element in the iteration. + * + * @return the next element in the bag + * @throws NoSuchElementException if there are no more elements to return + */ + @Override + public E next() { + if (!hasNext()) { + throw new NoSuchElementException("No more elements in the bag."); + } + E element = currentElement.content; + currentElement = currentElement.nextElement; + return element; + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/bloomfilter/BloomFilter.java b/src/main/java/com/thealgorithms/datastructures/bloomfilter/BloomFilter.java new file mode 100644 index 000000000000..a2edd3db2d8e --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/bloomfilter/BloomFilter.java @@ -0,0 +1,138 @@ +package com.thealgorithms.datastructures.bloomfilter; + +import java.util.BitSet; + +/** + * A generic BloomFilter implementation for probabilistic membership checking. + * <p> + * Bloom filters are space-efficient data structures that provide a fast way to test whether an + * element is a member of a set. They may produce false positives, indicating an element is + * in the set when it is not, but they will never produce false negatives. + * </p> + * + * @param <T> The type of elements to be stored in the Bloom filter. + */ +public class BloomFilter<T> { + + private final int numberOfHashFunctions; + private final BitSet bitArray; + private final Hash<T>[] hashFunctions; + + /** + * Constructs a BloomFilter with a specified number of hash functions and bit array size. + * + * @param numberOfHashFunctions the number of hash functions to use + * @param bitArraySize the size of the bit array, which determines the capacity of the filter + * @throws IllegalArgumentException if numberOfHashFunctions or bitArraySize is less than 1 + */ + @SuppressWarnings("unchecked") + public BloomFilter(int numberOfHashFunctions, int bitArraySize) { + if (numberOfHashFunctions < 1 || bitArraySize < 1) { + throw new IllegalArgumentException("Number of hash functions and bit array size must be greater than 0"); + } + this.numberOfHashFunctions = numberOfHashFunctions; + this.bitArray = new BitSet(bitArraySize); + this.hashFunctions = new Hash[numberOfHashFunctions]; + initializeHashFunctions(); + } + + /** + * Initializes the hash functions with unique indices to ensure different hashing. + */ + private void initializeHashFunctions() { + for (int i = 0; i < numberOfHashFunctions; i++) { + hashFunctions[i] = new Hash<>(i); + } + } + + /** + * Inserts an element into the Bloom filter. + * <p> + * This method hashes the element using all defined hash functions and sets the corresponding + * bits in the bit array. + * </p> + * + * @param key the element to insert into the Bloom filter + */ + public void insert(T key) { + for (Hash<T> hash : hashFunctions) { + int position = Math.abs(hash.compute(key) % bitArray.size()); + bitArray.set(position); + } + } + + /** + * Checks if an element might be in the Bloom filter. + * <p> + * This method checks the bits at the positions computed by each hash function. If any of these + * bits are not set, the element is definitely not in the filter. If all bits are set, the element + * might be in the filter. + * </p> + * + * @param key the element to check for membership in the Bloom filter + * @return {@code true} if the element might be in the Bloom filter, {@code false} if it is definitely not + */ + public boolean contains(T key) { + for (Hash<T> hash : hashFunctions) { + int position = Math.abs(hash.compute(key) % bitArray.size()); + if (!bitArray.get(position)) { + return false; + } + } + return true; + } + + /** + * Inner class representing a hash function used by the Bloom filter. + * <p> + * Each instance of this class represents a different hash function based on its index. + * </p> + * + * @param <T> The type of elements to be hashed. + */ + private static class Hash<T> { + + private final int index; + + /** + * Constructs a Hash function with a specified index. + * + * @param index the index of this hash function, used to create a unique hash + */ + Hash(int index) { + this.index = index; + } + + /** + * Computes the hash of the given key. + * <p> + * The hash value is calculated by multiplying the index of the hash function + * with the ASCII sum of the string representation of the key. + * </p> + * + * @param key the element to hash + * @return the computed hash value + */ + public int compute(T key) { + return index * asciiString(String.valueOf(key)); + } + + /** + * Computes the ASCII value sum of the characters in a string. + * <p> + * This method iterates through each character of the string and accumulates + * their ASCII values to produce a single integer value. + * </p> + * + * @param word the string to compute + * @return the sum of ASCII values of the characters in the string + */ + private int asciiString(String word) { + int sum = 0; + for (char c : word.toCharArray()) { + sum += c; + } + return sum; + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/buffers/CircularBuffer.java b/src/main/java/com/thealgorithms/datastructures/buffers/CircularBuffer.java new file mode 100644 index 000000000000..b709e16fd1f6 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/buffers/CircularBuffer.java @@ -0,0 +1,132 @@ +package com.thealgorithms.datastructures.buffers; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * The {@code CircularBuffer} class implements a generic circular (or ring) buffer. + * A circular buffer is a fixed-size data structure that operates in a FIFO (First In, First Out) manner. + * The buffer allows you to overwrite old data when the buffer is full and efficiently use limited memory. + * When the buffer is full, adding a new item will overwrite the oldest data. + * + * @param <Item> The type of elements stored in the circular buffer. + */ +public class CircularBuffer<Item> { + private final Item[] buffer; + private final CircularPointer putPointer; + private final CircularPointer getPointer; + private final AtomicInteger size = new AtomicInteger(0); + + /** + * Constructor to initialize the circular buffer with a specified size. + * + * @param size The size of the circular buffer. + * @throws IllegalArgumentException if the size is zero or negative. + */ + public CircularBuffer(int size) { + if (size <= 0) { + throw new IllegalArgumentException("Buffer size must be positive"); + } + // noinspection unchecked + this.buffer = (Item[]) new Object[size]; + this.putPointer = new CircularPointer(0, size); + this.getPointer = new CircularPointer(0, size); + } + + /** + * Checks if the circular buffer is empty. + * This method is based on the current size of the buffer. + * + * @return {@code true} if the buffer is empty, {@code false} otherwise. + */ + public boolean isEmpty() { + return size.get() == 0; + } + + /** + * Checks if the circular buffer is full. + * The buffer is considered full when its size equals its capacity. + * + * @return {@code true} if the buffer is full, {@code false} otherwise. + */ + public boolean isFull() { + return size.get() == buffer.length; + } + + /** + * Retrieves and removes the item at the front of the buffer (FIFO). + * This operation will move the {@code getPointer} forward. + * + * @return The item at the front of the buffer, or {@code null} if the buffer is empty. + */ + public Item get() { + if (isEmpty()) { + return null; + } + + Item item = buffer[getPointer.getAndIncrement()]; + size.decrementAndGet(); + return item; + } + + /** + * Adds an item to the end of the buffer (FIFO). + * If the buffer is full, this operation will overwrite the oldest data. + * + * @param item The item to be added. + * @throws IllegalArgumentException if the item is null. + * @return {@code true} if the item was successfully added, {@code false} if the buffer was full and the item overwrote existing data. + */ + public boolean put(Item item) { + if (item == null) { + throw new IllegalArgumentException("Null items are not allowed"); + } + + boolean wasEmpty = isEmpty(); + if (isFull()) { + getPointer.getAndIncrement(); // Move get pointer to discard oldest item + } else { + size.incrementAndGet(); + } + + buffer[putPointer.getAndIncrement()] = item; + return wasEmpty; + } + + /** + * The {@code CircularPointer} class is a helper class used to track the current index (pointer) + * in the circular buffer. + * The max value represents the capacity of the buffer. + * The `CircularPointer` class ensures that the pointer automatically wraps around to 0 + * when it reaches the maximum index. + * This is achieved in the `getAndIncrement` method, where the pointer + * is incremented and then taken modulo the maximum value (`max`). + * This operation ensures that the pointer always stays within the bounds of the buffer. + */ + private static class CircularPointer { + private int pointer; + private final int max; + + /** + * Constructor to initialize the circular pointer. + * + * @param pointer The initial position of the pointer. + * @param max The maximum size (capacity) of the circular buffer. + */ + CircularPointer(int pointer, int max) { + this.pointer = pointer; + this.max = max; + } + + /** + * Increments the pointer by 1 and wraps it around to 0 if it reaches the maximum value. + * This ensures the pointer always stays within the buffer's bounds. + * + * @return The current pointer value before incrementing. + */ + public int getAndIncrement() { + int tmp = pointer; + pointer = (pointer + 1) % max; + return tmp; + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/caches/LFUCache.java b/src/main/java/com/thealgorithms/datastructures/caches/LFUCache.java new file mode 100644 index 000000000000..f0d8ea8f7ff3 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/caches/LFUCache.java @@ -0,0 +1,185 @@ +package com.thealgorithms.datastructures.caches; + +import java.util.HashMap; +import java.util.Map; + +/** + * The {@code LFUCache} class implements a Least Frequently Used (LFU) cache. + * An LFU cache evicts the least frequently used item when the cache reaches its capacity. + * It maintains a mapping of keys to nodes, where each node contains the key, its associated value, + * and a frequency count that tracks how many times the item has been accessed. A doubly linked list + * is used to efficiently manage the ordering of items based on their usage frequency. + * + * <p>This implementation is designed to provide O(1) time complexity for both the {@code get} and + * {@code put} operations, which is achieved through the use of a hashmap for quick access and a + * doubly linked list for maintaining the order of item frequencies.</p> + * + * <p> + * Reference: <a href="https://en.wikipedia.org/wiki/Least_frequently_used">LFU Cache - Wikipedia</a> + * </p> + * + * @param <K> The type of keys maintained by this cache. + * @param <V> The type of mapped values. + * + * @author Akshay Dubey (https://github.com/itsAkshayDubey) + */ +public class LFUCache<K, V> { + + /** + * The {@code Node} class represents an element in the LFU cache. + * Each node contains a key, a value, and a frequency count. + * It also has pointers to the previous and next nodes in the doubly linked list. + */ + private class Node { + private final K key; + private V value; + private int frequency; + private Node previous; + private Node next; + + /** + * Constructs a new {@code Node} with the specified key, value, and frequency. + * + * @param key The key associated with this node. + * @param value The value stored in this node. + * @param frequency The frequency of usage of this node. + */ + Node(K key, V value, int frequency) { + this.key = key; + this.value = value; + this.frequency = frequency; + } + } + + private Node head; + private Node tail; + private final Map<K, Node> cache; + private final int capacity; + private static final int DEFAULT_CAPACITY = 100; + + /** + * Constructs an LFU cache with the default capacity. + */ + public LFUCache() { + this(DEFAULT_CAPACITY); + } + + /** + * Constructs an LFU cache with the specified capacity. + * + * @param capacity The maximum number of items that the cache can hold. + * @throws IllegalArgumentException if the specified capacity is less than or equal to zero. + */ + public LFUCache(int capacity) { + if (capacity <= 0) { + throw new IllegalArgumentException("Capacity must be greater than zero."); + } + this.capacity = capacity; + this.cache = new HashMap<>(); + } + + /** + * Retrieves the value associated with the given key from the cache. + * If the key exists, the node's frequency is incremented, and the node is repositioned + * in the linked list based on its updated frequency. + * + * @param key The key whose associated value is to be returned. + * @return The value associated with the key, or {@code null} if the key is not present in the cache. + */ + public V get(K key) { + Node node = cache.get(key); + if (node == null) { + return null; + } + removeNode(node); + node.frequency += 1; + addNodeWithUpdatedFrequency(node); + return node.value; + } + + /** + * Inserts or updates a key-value pair in the cache. + * If the key already exists, the value is updated and its frequency is incremented. + * If the cache is full, the least frequently used item is removed before inserting the new item. + * + * @param key The key associated with the value to be inserted or updated. + * @param value The value to be inserted or updated. + */ + public void put(K key, V value) { + if (cache.containsKey(key)) { + Node node = cache.get(key); + node.value = value; + node.frequency += 1; + removeNode(node); + addNodeWithUpdatedFrequency(node); + } else { + if (cache.size() >= capacity) { + cache.remove(this.head.key); // Evict least frequently used item + removeNode(head); + } + Node node = new Node(key, value, 1); + addNodeWithUpdatedFrequency(node); + cache.put(key, node); + } + } + + /** + * Adds a node to the linked list in the correct position based on its frequency. + * The linked list is ordered by frequency, with the least frequently used node at the head. + * + * @param node The node to be inserted into the list. + */ + private void addNodeWithUpdatedFrequency(Node node) { + if (tail != null && head != null) { + Node temp = this.head; + while (temp != null) { + if (temp.frequency > node.frequency) { + if (temp == head) { + node.next = temp; + temp.previous = node; + this.head = node; + break; + } else { + node.next = temp; + node.previous = temp.previous; + temp.previous.next = node; + temp.previous = node; + break; + } + } else { + temp = temp.next; + if (temp == null) { + tail.next = node; + node.previous = tail; + node.next = null; + tail = node; + break; + } + } + } + } else { + tail = node; + head = tail; + } + } + + /** + * Removes a node from the doubly linked list. + * This method ensures that the pointers of neighboring nodes are properly updated. + * + * @param node The node to be removed from the list. + */ + private void removeNode(Node node) { + if (node.previous != null) { + node.previous.next = node.next; + } else { + this.head = node.next; + } + + if (node.next != null) { + node.next.previous = node.previous; + } else { + this.tail = node.previous; + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/caches/LRUCache.java b/src/main/java/com/thealgorithms/datastructures/caches/LRUCache.java new file mode 100644 index 000000000000..ec39d2a6ed28 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/caches/LRUCache.java @@ -0,0 +1,235 @@ +package com.thealgorithms.datastructures.caches; + +import java.util.HashMap; +import java.util.Map; + +/** + * A Least Recently Used (LRU) Cache implementation. + * + * <p>An LRU cache is a fixed-size cache that maintains items in order of use. When the cache reaches + * its capacity and a new item needs to be added, it removes the least recently used item first. + * This implementation provides O(1) time complexity for both get and put operations.</p> + * + * <p>Features:</p> + * <ul> + * <li>Fixed-size cache with configurable capacity</li> + * <li>Constant time O(1) operations for get and put</li> + * <li>Thread-unsafe - should be externally synchronized if used in concurrent environments</li> + * <li>Supports null values but not null keys</li> + * </ul> + * + * <p>Implementation Details:</p> + * <ul> + * <li>Uses a HashMap for O(1) key-value lookups</li> + * <li>Maintains a doubly-linked list for tracking access order</li> + * <li>The head of the list contains the least recently used item</li> + * <li>The tail of the list contains the most recently used item</li> + * </ul> + * + * <p>Example usage:</p> + * <pre> + * LRUCache<String, Integer> cache = new LRUCache<>(3); // Create cache with capacity 3 + * cache.put("A", 1); // Cache: A=1 + * cache.put("B", 2); // Cache: A=1, B=2 + * cache.put("C", 3); // Cache: A=1, B=2, C=3 + * cache.get("A"); // Cache: B=2, C=3, A=1 (A moved to end) + * cache.put("D", 4); // Cache: C=3, A=1, D=4 (B evicted) + * </pre> + * + * @param <K> the type of keys maintained by this cache + * @param <V> the type of mapped values + */ +public class LRUCache<K, V> { + + private final Map<K, Entry<K, V>> data = new HashMap<>(); + private Entry<K, V> head; + private Entry<K, V> tail; + private int cap; + private static final int DEFAULT_CAP = 100; + + public LRUCache() { + setCapacity(DEFAULT_CAP); + } + + public LRUCache(int cap) { + setCapacity(cap); + } + + /** + * Returns the current capacity of the cache. + * + * @param newCapacity the new capacity of the cache + */ + private void setCapacity(int newCapacity) { + checkCapacity(newCapacity); + for (int i = data.size(); i > newCapacity; i--) { + Entry<K, V> evicted = evict(); + data.remove(evicted.getKey()); + } + this.cap = newCapacity; + } + + /** + * Evicts the least recently used item from the cache. + * + * @return the evicted entry + */ + private Entry<K, V> evict() { + if (head == null) { + throw new RuntimeException("cache cannot be empty!"); + } + Entry<K, V> evicted = head; + head = evicted.getNextEntry(); + head.setPreEntry(null); + evicted.setNextEntry(null); + return evicted; + } + + /** + * Checks if the capacity is valid. + * + * @param capacity the capacity to check + */ + private void checkCapacity(int capacity) { + if (capacity <= 0) { + throw new RuntimeException("capacity must greater than 0!"); + } + } + + /** + * Returns the value to which the specified key is mapped, or null if this cache contains no + * mapping for the key. + * + * @param key the key whose associated value is to be returned + * @return the value to which the specified key is mapped, or null if this cache contains no + * mapping for the key + */ + public V get(K key) { + if (!data.containsKey(key)) { + return null; + } + final Entry<K, V> entry = data.get(key); + moveNodeToLast(entry); + return entry.getValue(); + } + + /** + * Moves the specified entry to the end of the list. + * + * @param entry the entry to move + */ + private void moveNodeToLast(Entry<K, V> entry) { + if (tail == entry) { + return; + } + final Entry<K, V> preEntry = entry.getPreEntry(); + final Entry<K, V> nextEntry = entry.getNextEntry(); + if (preEntry != null) { + preEntry.setNextEntry(nextEntry); + } + if (nextEntry != null) { + nextEntry.setPreEntry(preEntry); + } + if (head == entry) { + head = nextEntry; + } + tail.setNextEntry(entry); + entry.setPreEntry(tail); + entry.setNextEntry(null); + tail = entry; + } + + /** + * Associates the specified value with the specified key in this cache. + * + * @param key the key with which the specified value is to be associated + * @param value the value to be associated with the specified key + */ + public void put(K key, V value) { + if (data.containsKey(key)) { + final Entry<K, V> existingEntry = data.get(key); + existingEntry.setValue(value); + moveNodeToLast(existingEntry); + return; + } + Entry<K, V> newEntry; + if (data.size() == cap) { + newEntry = evict(); + data.remove(newEntry.getKey()); + } else { + newEntry = new Entry<>(); + } + + newEntry.setKey(key); + newEntry.setValue(value); + addNewEntry(newEntry); + data.put(key, newEntry); + } + + /** + * Adds a new entry to the end of the list. + * + * @param newEntry the entry to add + */ + private void addNewEntry(Entry<K, V> newEntry) { + if (data.isEmpty()) { + head = newEntry; + tail = newEntry; + return; + } + tail.setNextEntry(newEntry); + newEntry.setPreEntry(tail); + newEntry.setNextEntry(null); + tail = newEntry; + } + + static final class Entry<I, J> { + + private Entry<I, J> preEntry; + private Entry<I, J> nextEntry; + private I key; + private J value; + + Entry() { + } + + Entry(Entry<I, J> preEntry, Entry<I, J> nextEntry, I key, J value) { + this.preEntry = preEntry; + this.nextEntry = nextEntry; + this.key = key; + this.value = value; + } + + public Entry<I, J> getPreEntry() { + return preEntry; + } + + public void setPreEntry(Entry<I, J> preEntry) { + this.preEntry = preEntry; + } + + public Entry<I, J> getNextEntry() { + return nextEntry; + } + + public void setNextEntry(Entry<I, J> nextEntry) { + this.nextEntry = nextEntry; + } + + public I getKey() { + return key; + } + + public void setKey(I key) { + this.key = key; + } + + public J getValue() { + return value; + } + + public void setValue(J value) { + this.value = value; + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/caches/MRUCache.java b/src/main/java/com/thealgorithms/datastructures/caches/MRUCache.java new file mode 100644 index 000000000000..93b13e6ad654 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/caches/MRUCache.java @@ -0,0 +1,230 @@ +package com.thealgorithms.datastructures.caches; + +import java.util.HashMap; +import java.util.Map; + +/** + * Represents a Most Recently Used (MRU) Cache. + * <p> + * In contrast to the Least Recently Used (LRU) strategy, the MRU caching policy + * evicts the most recently accessed items first. This class provides methods to + * store key-value pairs and manage cache eviction based on this policy. + * + * For more information, refer to: + * <a href="https://en.wikipedia.org/wiki/Cache_replacement_policies#Most_recently_used_(MRU)">MRU on Wikipedia</a>. + * + * @param <K> the type of keys maintained by this cache + * @param <V> the type of values associated with the keys + */ +public class MRUCache<K, V> { + + private final Map<K, Entry<K, V>> data = new HashMap<>(); + private Entry<K, V> head; + private Entry<K, V> tail; + private int cap; + private static final int DEFAULT_CAP = 100; + + /** + * Creates an MRUCache with the default capacity. + */ + public MRUCache() { + setCapacity(DEFAULT_CAP); + } + + /** + * Creates an MRUCache with a specified capacity. + * + * @param cap the maximum number of items the cache can hold + */ + public MRUCache(int cap) { + setCapacity(cap); + } + + /** + * Sets the capacity of the cache and evicts items if the new capacity + * is less than the current number of items. + * + * @param newCapacity the new capacity to set + */ + private void setCapacity(int newCapacity) { + checkCapacity(newCapacity); + while (data.size() > newCapacity) { + Entry<K, V> evicted = evict(); + data.remove(evicted.getKey()); + } + this.cap = newCapacity; + } + + /** + * Checks if the specified capacity is valid. + * + * @param capacity the capacity to check + * @throws IllegalArgumentException if the capacity is less than or equal to zero + */ + private void checkCapacity(int capacity) { + if (capacity <= 0) { + throw new IllegalArgumentException("Capacity must be greater than 0!"); + } + } + + /** + * Evicts the most recently used entry from the cache. + * + * @return the evicted entry + * @throws RuntimeException if the cache is empty + */ + private Entry<K, V> evict() { + if (head == null) { + throw new RuntimeException("Cache cannot be empty!"); + } + final Entry<K, V> evicted = this.tail; + tail = evicted.getPreEntry(); + if (tail != null) { + tail.setNextEntry(null); + } + evicted.setNextEntry(null); + return evicted; + } + + /** + * Retrieves the value associated with the specified key. + * + * @param key the key whose associated value is to be returned + * @return the value associated with the specified key, or null if the key does not exist + */ + public V get(K key) { + if (!data.containsKey(key)) { + return null; + } + final Entry<K, V> entry = data.get(key); + moveEntryToLast(entry); + return entry.getValue(); + } + + /** + * Associates the specified value with the specified key in the cache. + * If the key already exists, its value is updated and the entry is moved to the most recently used position. + * If the cache is full, the most recently used entry is evicted before adding the new entry. + * + * @param key the key with which the specified value is to be associated + * @param value the value to be associated with the specified key + */ + public void put(K key, V value) { + if (data.containsKey(key)) { + final Entry<K, V> existingEntry = data.get(key); + existingEntry.setValue(value); + moveEntryToLast(existingEntry); + return; + } + Entry<K, V> newEntry; + if (data.size() == cap) { + newEntry = evict(); + data.remove(newEntry.getKey()); + } else { + newEntry = new Entry<>(); + } + newEntry.setKey(key); + newEntry.setValue(value); + addNewEntry(newEntry); + data.put(key, newEntry); + } + + /** + * Adds a new entry to the cache and updates the head and tail pointers accordingly. + * + * @param newEntry the new entry to be added + */ + private void addNewEntry(Entry<K, V> newEntry) { + if (data.isEmpty()) { + head = newEntry; + tail = newEntry; + return; + } + tail.setNextEntry(newEntry); + newEntry.setPreEntry(tail); + newEntry.setNextEntry(null); + tail = newEntry; + } + + /** + * Moves the specified entry to the most recently used position in the cache. + * + * @param entry the entry to be moved + */ + private void moveEntryToLast(Entry<K, V> entry) { + if (tail == entry) { + return; + } + final Entry<K, V> preEntry = entry.getPreEntry(); + final Entry<K, V> nextEntry = entry.getNextEntry(); + if (preEntry != null) { + preEntry.setNextEntry(nextEntry); + } + if (nextEntry != null) { + nextEntry.setPreEntry(preEntry); + } + if (head == entry) { + head = nextEntry; + } + tail.setNextEntry(entry); + entry.setPreEntry(tail); + entry.setNextEntry(null); + tail = entry; + } + + /** + * A nested class representing an entry in the cache, which holds a key-value pair + * and references to the previous and next entries in the linked list structure. + * + * @param <I> the type of the key + * @param <J> the type of the value + */ + static final class Entry<I, J> { + private Entry<I, J> preEntry; + private Entry<I, J> nextEntry; + private I key; + private J value; + + Entry() { + } + + Entry(Entry<I, J> preEntry, Entry<I, J> nextEntry, I key, J value) { + this.preEntry = preEntry; + this.nextEntry = nextEntry; + this.key = key; + this.value = value; + } + + public Entry<I, J> getPreEntry() { + return preEntry; + } + + public void setPreEntry(Entry<I, J> preEntry) { + this.preEntry = preEntry; + } + + public Entry<I, J> getNextEntry() { + return nextEntry; + } + + public void setNextEntry(Entry<I, J> nextEntry) { + this.nextEntry = nextEntry; + } + + public I getKey() { + return key; + } + + public void setKey(I key) { + this.key = key; + } + + public J getValue() { + return value; + } + + public void setValue(J value) { + this.value = value; + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/crdt/GCounter.java b/src/main/java/com/thealgorithms/datastructures/crdt/GCounter.java new file mode 100644 index 000000000000..25b01bce19f3 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/crdt/GCounter.java @@ -0,0 +1,84 @@ +package com.thealgorithms.datastructures.crdt; + +import java.util.HashMap; +import java.util.Map; + +/** + * G-Counter (Grow-only Counter) is a state-based CRDT (Conflict-free Replicated Data Type) + * designed for tracking counts in a distributed and concurrent environment. + * Each process maintains its own counter, allowing only increments. The total count + * is obtained by summing individual process counts. + * This implementation supports incrementing, querying the total count, + * comparing with other G-Counters, and merging with another G-Counter + * to compute the element-wise maximum. + * (https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type) + * + * @author itakurah (https://github.com/itakurah) + */ + +class GCounter { + private final Map<Integer, Integer> counterMap; + private final int myId; + private final int n; + + /** + * Constructs a G-Counter for a cluster of n nodes. + * + * @param n The number of nodes in the cluster. + */ + GCounter(int myId, int n) { + this.myId = myId; + this.n = n; + this.counterMap = new HashMap<>(); + + for (int i = 0; i < n; i++) { + counterMap.put(i, 0); + } + } + + /** + * Increments the counter for the current node. + */ + public void increment() { + counterMap.put(myId, counterMap.get(myId) + 1); + } + + /** + * Gets the total value of the counter by summing up values from all nodes. + * + * @return The total value of the counter. + */ + public int value() { + int sum = 0; + for (int v : counterMap.values()) { + sum += v; + } + return sum; + } + + /** + * Compares the state of this G-Counter with another G-Counter. + * + * @param other The other G-Counter to compare with. + * @return True if the state of this G-Counter is less than or equal to the state of the other G-Counter. + */ + public boolean compare(GCounter other) { + for (int i = 0; i < n; i++) { + if (this.counterMap.get(i) > other.counterMap.get(i)) { + return false; + } + } + return true; + } + + /** + * Merges the state of this G-Counter with another G-Counter. + * + * @param other The other G-Counter to merge with. + */ + public void merge(GCounter other) { + for (int i = 0; i < n; i++) { + this.counterMap.put(i, Math.max(this.counterMap.get(i), other.counterMap.get(i))); + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/crdt/GSet.java b/src/main/java/com/thealgorithms/datastructures/crdt/GSet.java new file mode 100644 index 000000000000..2b8959ed0136 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/crdt/GSet.java @@ -0,0 +1,65 @@ +package com.thealgorithms.datastructures.crdt; + +import java.util.HashSet; +import java.util.Set; + +/** + * GSet (Grow-only Set) is a state-based CRDT (Conflict-free Replicated Data Type) + * that allows only the addition of elements and ensures that once an element is added, + * it cannot be removed. The merge operation of two G-Sets is their union. + * This implementation supports adding elements, looking up elements, comparing with other G-Sets, + * and merging with another G-Set to create a new G-Set containing all unique elements from both sets. + * (https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type) + * + * @author itakurah (Niklas Hoefflin) (https://github.com/itakurah) + */ + +public class GSet<T> { + private final Set<T> elements; + + /** + * Constructs an empty G-Set. + */ + public GSet() { + this.elements = new HashSet<>(); + } + + /** + * Adds an element to the G-Set. + * + * @param e the element to be added + */ + public void addElement(T e) { + elements.add(e); + } + + /** + * Checks if the given element is present in the G-Set. + * + * @param e the element to be checked + * @return true if the element is present, false otherwise + */ + public boolean lookup(T e) { + return elements.contains(e); + } + + /** + * Compares the G-Set with another G-Set to check if it is a subset. + * + * @param other the other G-Set to compare with + * @return true if the current G-Set is a subset of the other, false otherwise + */ + public boolean compare(GSet<T> other) { + return other.elements.containsAll(elements); + } + + /** + * Merges the current G-Set with another G-Set, creating a new G-Set + * containing all unique elements from both sets. + * + * @param other the G-Set to merge with + */ + public void merge(GSet<T> other) { + elements.addAll(other.elements); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/crdt/LWWElementSet.java b/src/main/java/com/thealgorithms/datastructures/crdt/LWWElementSet.java new file mode 100644 index 000000000000..d33bd3ee84d9 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/crdt/LWWElementSet.java @@ -0,0 +1,126 @@ +package com.thealgorithms.datastructures.crdt; + +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; + +/** + * Last-Write-Wins Element Set (LWWElementSet) is a state-based CRDT (Conflict-free Replicated Data + * Type) designed for managing sets in a distributed and concurrent environment. It supports the + * addition and removal of elements, using timestamps to determine the order of operations. The set + * is split into two subsets: the add set for elements to be added and the remove set for elements + * to be removed. The LWWElementSet ensures that the most recent operation (based on the timestamp) + * wins in the case of concurrent operations. + * + * @param <T> The type of the elements in the LWWElementSet. + * @author <a href="https://github.com/itakurah">itakurah (GitHub)</a>, <a + * href="https://www.linkedin.com/in/niklashoefflin/">Niklas Hoefflin (LinkedIn)</a> + * @see <a href="https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type">Conflict free + * replicated data type (Wikipedia)</a> + * @see <a href="https://inria.hal.science/inria-00555588v1/document">A comprehensive study of + * Convergent and Commutative Replicated Data Types</a> + */ +class LWWElementSet<T> { + final Map<T, Element<T>> addSet; + final Map<T, Element<T>> removeSet; + + /** + * Constructs an empty LWWElementSet. This constructor initializes the addSet and removeSet as + * empty HashMaps. The addSet stores elements that are added, and the removeSet stores elements + * that are removed. + */ + LWWElementSet() { + this.addSet = new HashMap<>(); + this.removeSet = new HashMap<>(); + } + + /** + * Adds an element to the addSet with the current timestamp. This method stores the element in the + * addSet, ensuring that the element is added to the set with an associated timestamp that + * represents the time of the addition. + * + * @param key The key of the element to be added. + */ + public void add(T key) { + addSet.put(key, new Element<>(key, Instant.now())); + } + + /** + * Removes an element by adding it to the removeSet with the current timestamp. This method adds + * the element to the removeSet, marking it as removed with the current timestamp. + * + * @param key The key of the element to be removed. + */ + public void remove(T key) { + removeSet.put(key, new Element<>(key, Instant.now())); + } + + /** + * Checks if an element is in the LWWElementSet. An element is considered present if it exists in + * the addSet and either does not exist in the removeSet, or its add timestamp is later than any + * corresponding remove timestamp. + * + * @param key The key of the element to be checked. + * @return {@code true} if the element is present in the set (i.e., its add timestamp is later + * than its remove timestamp, or it is not in the remove set), {@code false} otherwise (i.e., + * the element has been removed or its remove timestamp is later than its add timestamp). + */ + public boolean lookup(T key) { + Element<T> inAddSet = addSet.get(key); + Element<T> inRemoveSet = removeSet.get(key); + + return inAddSet != null && (inRemoveSet == null || inAddSet.timestamp.isAfter(inRemoveSet.timestamp)); + } + + /** + * Merges another LWWElementSet into this set. This method takes the union of both the add-sets + * and remove-sets from the two sets, resolving conflicts by keeping the element with the latest + * timestamp. If an element appears in both the add-set and remove-set of both sets, the one with + * the later timestamp will be retained. + * + * @param other The LWWElementSet to merge with the current set. + */ + public void merge(LWWElementSet<T> other) { + for (Map.Entry<T, Element<T>> entry : other.addSet.entrySet()) { + addSet.merge(entry.getKey(), entry.getValue(), this::resolveConflict); + } + for (Map.Entry<T, Element<T>> entry : other.removeSet.entrySet()) { + removeSet.merge(entry.getKey(), entry.getValue(), this::resolveConflict); + } + } + + /** + * Resolves conflicts between two elements by selecting the one with the later timestamp. This + * method is used when merging two LWWElementSets to ensure that the most recent operation (based + * on timestamps) is kept. + * + * @param e1 The first element. + * @param e2 The second element. + * @return The element with the later timestamp. + */ + private Element<T> resolveConflict(Element<T> e1, Element<T> e2) { + return e1.timestamp.isAfter(e2.timestamp) ? e1 : e2; + } +} + +/** + * Represents an element in the LWWElementSet, consisting of a key and a timestamp. This class is + * used to store the elements in both the add and remove sets with their respective timestamps. + * + * @param <T> The type of the key associated with the element. + */ +class Element<T> { + T key; + Instant timestamp; + + /** + * Constructs a new Element with the specified key and timestamp. + * + * @param key The key of the element. + * @param timestamp The timestamp associated with the element. + */ + Element(T key, Instant timestamp) { + this.key = key; + this.timestamp = timestamp; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/crdt/ORSet.java b/src/main/java/com/thealgorithms/datastructures/crdt/ORSet.java new file mode 100644 index 000000000000..a4cc2ffdd4a6 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/crdt/ORSet.java @@ -0,0 +1,191 @@ +package com.thealgorithms.datastructures.crdt; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * ORSet (Observed-Removed Set) is a state-based CRDT (Conflict-free Replicated Data Type) + * that supports both addition and removal of elements. This particular implementation follows + * the Add-Wins strategy, meaning that in case of conflicting add and remove operations, + * the add operation takes precedence. The merge operation of two OR-Sets ensures that + * elements added at any replica are eventually observed at all replicas. Removed elements, + * once observed, are never reintroduced. + * This OR-Set implementation provides methods for adding elements, removing elements, + * checking for element existence, retrieving the set of elements, comparing with other OR-Sets, + * and merging with another OR-Set to create a new OR-Set containing all unique elements + * from both sets. + * + * @author itakurah (Niklas Hoefflin) (https://github.com/itakurah) + * @see <a href="https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type">Conflict-free_replicated_data_type</a> + * @see <a href="https://github.com/itakurah">itakurah (Niklas Hoefflin)</a> + */ + +public class ORSet<T> { + + private final Set<Pair<T>> elements; + private final Set<Pair<T>> tombstones; + + /** + * Constructs an empty OR-Set. + */ + public ORSet() { + this.elements = new HashSet<>(); + this.tombstones = new HashSet<>(); + } + + /** + * Checks if the set contains the specified element. + * + * @param element the element to check for + * @return true if the set contains the element, false otherwise + */ + public boolean contains(T element) { + return elements.stream().anyMatch(pair -> pair.getElement().equals(element)); + } + + /** + * Retrieves the elements in the set. + * + * @return a set containing the elements + */ + public Set<T> elements() { + Set<T> result = new HashSet<>(); + elements.forEach(pair -> result.add(pair.getElement())); + return result; + } + + /** + * Adds the specified element to the set. + * + * @param element the element to add + */ + public void add(T element) { + String n = prepare(); + effect(element, n); + } + + /** + * Removes the specified element from the set. + * + * @param element the element to remove + */ + public void remove(T element) { + Set<Pair<T>> pairsToRemove = prepare(element); + effect(pairsToRemove); + } + + /** + * Collect all pairs with the specified element. + * + * @param element the element to collect pairs for + * @return a set of pairs with the specified element to be removed + */ + private Set<Pair<T>> prepare(T element) { + Set<Pair<T>> pairsToRemove = new HashSet<>(); + for (Pair<T> pair : elements) { + if (pair.getElement().equals(element)) { + pairsToRemove.add(pair); + } + } + return pairsToRemove; + } + + /** + * Generates a unique tag for the element. + * + * @return the unique tag + */ + private String prepare() { + return generateUniqueTag(); + } + + /** + * Adds the element with the specified unique tag to the set. + * + * @param element the element to add + * @param n the unique tag associated with the element + */ + private void effect(T element, String n) { + Pair<T> pair = new Pair<>(element, n); + elements.add(pair); + elements.removeAll(tombstones); + } + + /** + * Removes the specified pairs from the set. + * + * @param pairsToRemove the pairs to remove + */ + private void effect(Set<Pair<T>> pairsToRemove) { + elements.removeAll(pairsToRemove); + tombstones.addAll(pairsToRemove); + } + + /** + * Generates a unique tag. + * + * @return the unique tag + */ + private String generateUniqueTag() { + return UUID.randomUUID().toString(); + } + + /** + * Compares this Add-Wins OR-Set with another OR-Set to check if elements and tombstones are a subset. + * + * @param other the other OR-Set to compare + * @return true if the sets are subset, false otherwise + */ + public boolean compare(ORSet<T> other) { + Set<Pair<T>> union = new HashSet<>(elements); + union.addAll(tombstones); + + Set<Pair<T>> otherUnion = new HashSet<>(other.elements); + otherUnion.addAll(other.tombstones); + + return otherUnion.containsAll(union) && other.tombstones.containsAll(tombstones); + } + + /** + * Merges this Add-Wins OR-Set with another OR-Set. + * + * @param other the other OR-Set to merge + */ + public void merge(ORSet<T> other) { + elements.removeAll(other.tombstones); + other.elements.removeAll(tombstones); + elements.addAll(other.elements); + tombstones.addAll(other.tombstones); + } + + /** + * Represents a pair containing an element and a unique tag. + * + * @param <T> the type of the element in the pair + */ + public static class Pair<T> { + private final T element; + private final String uniqueTag; + + /** + * Constructs a pair with the specified element and unique tag. + * + * @param element the element in the pair + * @param uniqueTag the unique tag associated with the element + */ + public Pair(T element, String uniqueTag) { + this.element = element; + this.uniqueTag = uniqueTag; + } + + /** + * Gets the element from the pair. + * + * @return the element + */ + public T getElement() { + return element; + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/crdt/PNCounter.java b/src/main/java/com/thealgorithms/datastructures/crdt/PNCounter.java new file mode 100644 index 000000000000..53c21dcbd108 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/crdt/PNCounter.java @@ -0,0 +1,100 @@ +package com.thealgorithms.datastructures.crdt; + +import java.util.HashMap; +import java.util.Map; + +/** + * PN-Counter (Positive-Negative Counter) is a state-based CRDT (Conflict-free Replicated Data Type) + * designed for tracking counts with both increments and decrements in a distributed and concurrent environment. + * It combines two G-Counters, one for increments (P) and one for decrements (N). + * The total count is obtained by subtracting the value of the decrement counter from the increment counter. + * This implementation supports incrementing, decrementing, querying the total count, + * comparing with other PN-Counters, and merging with another PN-Counter + * to compute the element-wise maximum for both increment and decrement counters. + * (https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type) + * + * @author itakurah (Niklas Hoefflin) (https://github.com/itakurah) + */ + +class PNCounter { + private final Map<Integer, Integer> pCounter; + private final Map<Integer, Integer> nCounter; + private final int myId; + private final int n; + + /** + * Constructs a PN-Counter for a cluster of n nodes. + * + * @param myId The identifier of the current node. + * @param n The number of nodes in the cluster. + */ + PNCounter(int myId, int n) { + this.myId = myId; + this.n = n; + this.pCounter = new HashMap<>(); + this.nCounter = new HashMap<>(); + + for (int i = 0; i < n; i++) { + pCounter.put(i, 0); + nCounter.put(i, 0); + } + } + + /** + * Increments the increment counter for the current node. + */ + public void increment() { + pCounter.put(myId, pCounter.get(myId) + 1); + } + + /** + * Increments the decrement counter for the current node. + */ + public void decrement() { + nCounter.put(myId, nCounter.get(myId) + 1); + } + + /** + * Gets the total value of the counter by subtracting the decrement counter from the increment counter. + * + * @return The total value of the counter. + */ + public int value() { + int sumP = pCounter.values().stream().mapToInt(Integer::intValue).sum(); + int sumN = nCounter.values().stream().mapToInt(Integer::intValue).sum(); + return sumP - sumN; + } + + /** + * Compares the state of this PN-Counter with another PN-Counter. + * + * @param other The other PN-Counter to compare with. + * @return True if the state of this PN-Counter is less than or equal to the state of the other PN-Counter. + */ + public boolean compare(PNCounter other) { + if (this.n != other.n) { + throw new IllegalArgumentException("Cannot compare PN-Counters with different number of nodes"); + } + for (int i = 0; i < n; i++) { + if (this.pCounter.get(i) > other.pCounter.get(i) && this.nCounter.get(i) > other.nCounter.get(i)) { + return false; + } + } + return true; + } + + /** + * Merges the state of this PN-Counter with another PN-Counter. + * + * @param other The other PN-Counter to merge with. + */ + public void merge(PNCounter other) { + if (this.n != other.n) { + throw new IllegalArgumentException("Cannot merge PN-Counters with different number of nodes"); + } + for (int i = 0; i < n; i++) { + this.pCounter.put(i, Math.max(this.pCounter.get(i), other.pCounter.get(i))); + this.nCounter.put(i, Math.max(this.nCounter.get(i), other.nCounter.get(i))); + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/crdt/TwoPSet.java b/src/main/java/com/thealgorithms/datastructures/crdt/TwoPSet.java new file mode 100644 index 000000000000..c0ce17b2802b --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/crdt/TwoPSet.java @@ -0,0 +1,84 @@ +package com.thealgorithms.datastructures.crdt; + +import java.util.HashSet; +import java.util.Set; + +/** + * TwoPhaseSet (2P-Set) is a state-based CRDT (Conflict-free Replicated Data Type) designed for managing sets + * with support for both addition and removal operations in a distributed and concurrent environment. + * It combines two G-Sets (grow-only sets) - one set for additions and another set (tombstone set) for removals. + * Once an element is removed and placed in the tombstone set, it cannot be re-added, adhering to "remove-wins" semantics. + * This implementation supports querying the presence of elements, adding elements, removing elements, + * comparing with other 2P-Sets, and merging two 2P-Sets while preserving the remove-wins semantics. + * (https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type) + * + * @author itakurah (Niklas Hoefflin) (https://github.com/itakurah) + */ + +public class TwoPSet<T> { + private final Set<T> setA; + private final Set<T> setR; + + /** + * Constructs an empty Two-Phase Set. + */ + public TwoPSet() { + this.setA = new HashSet<>(); + this.setR = new HashSet<>(); + } + + /** + * Checks if an element is in the set and has not been removed. + * + * @param element The element to be checked. + * @return True if the element is in the set and has not been removed, otherwise false. + */ + public boolean lookup(T element) { + return setA.contains(element) && !setR.contains(element); + } + + /** + * Adds an element to the set. + * + * @param element The element to be added. + */ + public void add(T element) { + setA.add(element); + } + + /** + * Removes an element from the set. The element will be placed in the tombstone set. + * + * @param element The element to be removed. + */ + public void remove(T element) { + if (lookup(element)) { + setR.add(element); + } + } + + /** + * Compares the current 2P-Set with another 2P-Set. + * + * @param otherSet The other 2P-Set to compare with. + * @return True if both SetA and SetR are subset, otherwise false. + */ + public boolean compare(TwoPSet<T> otherSet) { + return otherSet.setA.containsAll(setA) && otherSet.setR.containsAll(setR); + } + + /** + * Merges the current 2P-Set with another 2P-Set. + * + * @param otherSet The other 2P-Set to merge with. + * @return A new 2P-Set containing the merged elements. + */ + public TwoPSet<T> merge(TwoPSet<T> otherSet) { + TwoPSet<T> mergedSet = new TwoPSet<>(); + mergedSet.setA.addAll(this.setA); + mergedSet.setA.addAll(otherSet.setA); + mergedSet.setR.addAll(this.setR); + mergedSet.setR.addAll(otherSet.setR); + return mergedSet; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/disjointsetunion/DisjointSetUnion.java b/src/main/java/com/thealgorithms/datastructures/disjointsetunion/DisjointSetUnion.java new file mode 100644 index 000000000000..583800998c81 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/disjointsetunion/DisjointSetUnion.java @@ -0,0 +1,53 @@ +package com.thealgorithms.datastructures.disjointsetunion; + +/** + * Disjoint Set Union or DSU is useful for solving problems related to connected components, + * cycle detection in graphs, and maintaining relationships in disjoint sets of data. + * It is commonly employed in graph algorithms and problems. + * + * @see <a href="https://en.wikipedia.org/wiki/Disjoint-set_data_structure">Disjoint Set Union</a> + */ +public class DisjointSetUnion<T> { + + /** + * Creates a new node of DSU with parent initialised as same node + */ + public Node<T> makeSet(final T x) { + return new Node<T>(x); + } + + /** + * Finds and returns the representative (root) element of the set to which a given element belongs. + * This operation uses path compression to optimize future findSet operations. + */ + public Node<T> findSet(Node<T> node) { + while (node != node.parent) { + node = node.parent; + } + return node; + } + + /** + * Unions two sets by merging their representative elements. The merge is performed based on the rank of each set + * to ensure efficient merging and path compression to optimize future findSet operations. + */ + public void unionSets(final Node<T> x, final Node<T> y) { + Node<T> nx = findSet(x); + Node<T> ny = findSet(y); + + if (nx == ny) { + return; // Both elements already belong to the same set. + } + // Merging happens based on rank of node, this is done to avoid long chaining of nodes and reduce time + // to find root of the component. Idea is to attach small components in big, instead of other way around. + if (nx.rank > ny.rank) { + ny.parent = nx; + } else if (ny.rank > nx.rank) { + nx.parent = ny; + } else { + // Both sets have the same rank; choose one as the parent and increment the rank. + ny.parent = nx; + nx.rank++; + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/disjointsetunion/Node.java b/src/main/java/com/thealgorithms/datastructures/disjointsetunion/Node.java new file mode 100644 index 000000000000..260f297bd713 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/disjointsetunion/Node.java @@ -0,0 +1,25 @@ +package com.thealgorithms.datastructures.disjointsetunion; + +public class Node<T> { + + /** + * The rank of the node, used for optimizing union operations. + */ + public int rank; + + /** + * Reference to the parent node in the set. + * Initially, a node is its own parent (represents a singleton set). + */ + public Node<T> parent; + + /** + * The data element associated with the node. + */ + public T data; + + public Node(final T data) { + this.data = data; + parent = this; // Initially, a node is its own parent. + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/dynamicarray/DynamicArray.java b/src/main/java/com/thealgorithms/datastructures/dynamicarray/DynamicArray.java new file mode 100644 index 000000000000..cd5dc580b694 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/dynamicarray/DynamicArray.java @@ -0,0 +1,267 @@ +package com.thealgorithms.datastructures.dynamicarray; + +import java.util.Arrays; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +/** + * This class implements a dynamic array, which can grow or shrink in size + * as elements are added or removed. It provides an array-like interface + * with methods to add, remove, and access elements, along with iterators + * to traverse the elements. + * + * @param <E> the type of elements that this array can hold + */ +public class DynamicArray<E> implements Iterable<E> { + + private static final int DEFAULT_CAPACITY = 16; + private int size; + private int modCount; // Tracks structural modifications for iterator integrity + private Object[] elements; + + /** + * Constructs a new DynamicArray with the specified initial capacity. + * + * @param capacity the initial capacity of the array + * @throws IllegalArgumentException if the specified capacity is negative + */ + public DynamicArray(final int capacity) { + if (capacity < 0) { + throw new IllegalArgumentException("Capacity cannot be negative."); + } + this.size = 0; + this.modCount = 0; + this.elements = new Object[capacity]; + } + + /** + * Constructs a new DynamicArray with a default initial capacity. + */ + public DynamicArray() { + this(DEFAULT_CAPACITY); + } + + /** + * Adds an element to the end of the array. If the array is full, it + * creates a new array with double the size to accommodate the new element. + * + * @param element the element to be added to the array + */ + public void add(final E element) { + ensureCapacity(size + 1); + elements[size++] = element; + modCount++; // Increment modification count + } + + /** + * Places an element at the specified index, expanding capacity if necessary. + * + * @param index the index at which the element is to be placed + * @param element the element to be inserted at the specified index + * @throws IndexOutOfBoundsException if index is less than 0 or greater than or equal to the number of elements + */ + public void put(final int index, E element) { + if (index < 0) { + throw new IndexOutOfBoundsException("Index cannot be negative."); + } + ensureCapacity(index + 1); + elements[index] = element; + if (index >= size) { + size = index + 1; + } + modCount++; // Increment modification count + } + + /** + * Retrieves the element at the specified index. + * + * @param index the index of the element to retrieve + * @return the element at the specified index + * @throws IndexOutOfBoundsException if index is less than 0 or greater than or equal to the current size + */ + @SuppressWarnings("unchecked") + public E get(final int index) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); + } + return (E) elements[index]; + } + + /** + * Removes and returns the element at the specified index. + * + * @param index the index of the element to be removed + * @return the element that was removed from the array + * @throws IndexOutOfBoundsException if index is less than 0 or greater than or equal to the current size + */ + public E remove(final int index) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); + } + @SuppressWarnings("unchecked") E oldElement = (E) elements[index]; + fastRemove(index); + modCount++; // Increment modification count + return oldElement; + } + + /** + * Returns the current number of elements in the array. + * + * @return the number of elements in the array + */ + public int getSize() { + return size; + } + + /** + * Checks whether the array is empty. + * + * @return true if the array contains no elements, false otherwise + */ + public boolean isEmpty() { + return size == 0; + } + + /** + * Returns a sequential stream with this collection as its source. + * + * @return a stream of the elements in the array + */ + public Stream<E> stream() { + return StreamSupport.stream(spliterator(), false); + } + + /** + * Ensures that the array has enough capacity to hold the specified number of elements. + * + * @param minCapacity the minimum capacity required + */ + private void ensureCapacity(int minCapacity) { + if (minCapacity > elements.length) { + int newCapacity = Math.max(elements.length * 2, minCapacity); + elements = Arrays.copyOf(elements, newCapacity); + } + } + + /** + * Removes the element at the specified index without resizing the array. + * This method shifts any subsequent elements to the left and clears the last element. + * + * @param index the index of the element to remove + */ + private void fastRemove(int index) { + int numMoved = size - index - 1; + if (numMoved > 0) { + System.arraycopy(elements, index + 1, elements, index, numMoved); + } + elements[--size] = null; // Clear to let GC do its work + } + + /** + * Returns a string representation of the array, including only the elements that are currently stored. + * + * @return a string containing the elements in the array + */ + @Override + public String toString() { + return Arrays.toString(Arrays.copyOf(elements, size)); + } + + /** + * Returns an iterator over the elements in this array in proper sequence. + * + * @return an Iterator over the elements in the array + */ + @Override + public Iterator<E> iterator() { + return new DynamicArrayIterator(); + } + + /** + * Private iterator class for the DynamicArray. + */ + private final class DynamicArrayIterator implements Iterator<E> { + + private int cursor; + private int expectedModCount; + + /** + * Constructs a new iterator for the dynamic array. + */ + DynamicArrayIterator() { + this.expectedModCount = modCount; + } + + /** + * Checks if there are more elements in the iteration. + * + * @return true if there are more elements, false otherwise + */ + @Override + public boolean hasNext() { + checkForComodification(); + return cursor < size; + } + + /** + * Returns the next element in the iteration. + * + * @return the next element in the iteration + * @throws NoSuchElementException if the iteration has no more elements + */ + @Override + @SuppressWarnings("unchecked") + public E next() { + checkForComodification(); + if (cursor >= size) { + throw new NoSuchElementException(); + } + return (E) elements[cursor++]; + } + + /** + * Removes the last element returned by this iterator. + * + * @throws IllegalStateException if the next method has not yet been called, or the remove method has already been called after the last call to the next method + */ + @Override + public void remove() { + if (cursor <= 0) { + throw new IllegalStateException("Cannot remove element before calling next()"); + } + checkForComodification(); + DynamicArray.this.remove(--cursor); + expectedModCount = modCount; + } + + /** + * Checks for concurrent modifications to the array during iteration. + * + * @throws ConcurrentModificationException if the array has been modified structurally + */ + private void checkForComodification() { + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + /** + * Performs the given action for each remaining element in the iterator until all elements have been processed. + * + * @param action the action to be performed for each element + * @throws NullPointerException if the specified action is null + */ + @Override + public void forEachRemaining(Consumer<? super E> action) { + Objects.requireNonNull(action); + while (hasNext()) { + action.accept(next()); + } + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/AStar.java b/src/main/java/com/thealgorithms/datastructures/graphs/AStar.java new file mode 100644 index 000000000000..741caa59c5b5 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/graphs/AStar.java @@ -0,0 +1,144 @@ +package com.thealgorithms.datastructures.graphs; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.PriorityQueue; + +/** + * AStar class implements the A* pathfinding algorithm to find the shortest path in a graph. + * The graph is represented using an adjacency list, and the algorithm uses a heuristic to estimate + * the cost to reach the destination node. + * Time Complexity = O(E), where E is equal to the number of edges + */ +public final class AStar { + private AStar() { + } + + /** + * Represents a graph using an adjacency list. + */ + static class Graph { + private ArrayList<ArrayList<Edge>> graph; + + Graph(int size) { + this.graph = new ArrayList<>(); + for (int i = 0; i < size; i++) { + this.graph.add(new ArrayList<>()); + } + } + + private ArrayList<Edge> getNeighbours(int from) { + return this.graph.get(from); + } + + // Add a bidirectional edge to the graph + private void addEdge(Edge edge) { + this.graph.get(edge.getFrom()).add(new Edge(edge.getFrom(), edge.getTo(), edge.getWeight())); + this.graph.get(edge.getTo()).add(new Edge(edge.getTo(), edge.getFrom(), edge.getWeight())); + } + } + + /** + * Represents an edge in the graph with a start node, end node, and weight. + */ + private static class Edge { + private int from; + private int to; + private int weight; + + Edge(int from, int to, int weight) { + this.from = from; + this.to = to; + this.weight = weight; + } + + public int getFrom() { + return from; + } + + public int getTo() { + return to; + } + + public int getWeight() { + return weight; + } + } + + /** + * Contains information about the path and its total distance. + */ + static class PathAndDistance { + private int distance; // total distance from the start node + private ArrayList<Integer> path; // list of nodes in the path + private int estimated; // heuristic estimate for reaching the destination + + PathAndDistance(int distance, ArrayList<Integer> path, int estimated) { + this.distance = distance; + this.path = path; + this.estimated = estimated; + } + + public int getDistance() { + return distance; + } + + public ArrayList<Integer> getPath() { + return path; + } + + public int getEstimated() { + return estimated; + } + } + + // Initializes the graph with edges defined in the input data + static void initializeGraph(Graph graph, List<Integer> data) { + for (int i = 0; i < data.size(); i += 4) { + graph.addEdge(new Edge(data.get(i), data.get(i + 1), data.get(i + 2))); + } + } + + /** + * Implements the A* pathfinding algorithm to find the shortest path from a start node to a destination node. + * + * @param from the starting node + * @param to the destination node + * @param graph the graph representation of the problem + * @param heuristic the heuristic estimates for each node + * @return a PathAndDistance object containing the shortest path and its distance + */ + public static PathAndDistance aStar(int from, int to, Graph graph, int[] heuristic) { + // PriorityQueue to explore nodes based on their distance and estimated cost to reach the destination + PriorityQueue<PathAndDistance> queue = new PriorityQueue<>(Comparator.comparingInt(a -> (a.getDistance() + a.getEstimated()))); + + // Start with the initial node + queue.add(new PathAndDistance(0, new ArrayList<>(List.of(from)), heuristic[from])); + + boolean solutionFound = false; + PathAndDistance currentData = new PathAndDistance(-1, null, -1); + + while (!queue.isEmpty() && !solutionFound) { + currentData = queue.poll(); // get the best node from the queue + int currentPosition = currentData.getPath().get(currentData.getPath().size() - 1); // current node + + // Check if the destination has been reached + if (currentPosition == to) { + solutionFound = true; + } else { + for (Edge edge : graph.getNeighbours(currentPosition)) { + // Avoid cycles by checking if the next node is already in the path + if (!currentData.getPath().contains(edge.getTo())) { + ArrayList<Integer> updatedPath = new ArrayList<>(currentData.getPath()); + updatedPath.add(edge.getTo()); + + // Update the distance and heuristic for the new path + queue.add(new PathAndDistance(currentData.getDistance() + edge.getWeight(), updatedPath, heuristic[edge.getTo()])); + } + } + } + } + return (solutionFound) ? currentData : new PathAndDistance(-1, null, -1); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/BellmanFord.java b/src/main/java/com/thealgorithms/datastructures/graphs/BellmanFord.java new file mode 100644 index 000000000000..47c5f0d0b98e --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/graphs/BellmanFord.java @@ -0,0 +1,184 @@ +package com.thealgorithms.datastructures.graphs; + +import java.util.Scanner; + +class BellmanFord /* + * Implementation of Bellman ford to detect negative cycles. Graph accepts + * inputs + * in form of edges which have start vertex, end vertex and weights. Vertices + * should be labelled with a + * number between 0 and total number of vertices-1,both inclusive + */ +{ + + int vertex; + int edge; + private Edge[] edges; + private int index = 0; + + BellmanFord(int v, int e) { + vertex = v; + edge = e; + edges = new Edge[e]; + } + + class Edge { + + int u; + int v; + int w; + + /** + * @param u Source Vertex + * @param v End vertex + * @param c Weight + */ + Edge(int a, int b, int c) { + u = a; + v = b; + w = c; + } + } + + /** + * @param p[] Parent array which shows updates in edges + * @param i Current vertex under consideration + */ + void printPath(int[] p, int i) { + if (p[i] == -1) { // Found the path back to parent + return; + } + printPath(p, p[i]); + System.out.print(i + " "); + } + + public static void main(String[] args) { + BellmanFord obj = new BellmanFord(0, 0); // Dummy object to call nonstatic variables + obj.go(); + } + + public void go() { + // shows distance to all vertices + // Interactive run for understanding the + // class first time. Assumes source vertex is 0 and + try (Scanner sc = new Scanner(System.in)) { + int i; + int v; + int e; + int u; + int ve; + int w; + int j; + int neg = 0; + System.out.println("Enter no. of vertices and edges please"); + v = sc.nextInt(); + e = sc.nextInt(); + Edge[] arr = new Edge[e]; // Array of edges + System.out.println("Input edges"); + for (i = 0; i < e; i++) { + u = sc.nextInt(); + ve = sc.nextInt(); + w = sc.nextInt(); + arr[i] = new Edge(u, ve, w); + } + int[] dist = new int[v]; // Distance array for holding the finalized shortest path distance + // between source + // and all vertices + int[] p = new int[v]; // Parent array for holding the paths + for (i = 0; i < v; i++) { + dist[i] = Integer.MAX_VALUE; // Initializing distance values + } + dist[0] = 0; + p[0] = -1; + for (i = 0; i < v - 1; i++) { + for (j = 0; j < e; j++) { + if (dist[arr[j].u] != Integer.MAX_VALUE && dist[arr[j].v] > dist[arr[j].u] + arr[j].w) { + dist[arr[j].v] = dist[arr[j].u] + arr[j].w; // Update + p[arr[j].v] = arr[j].u; + } + } + } + // Final cycle for negative checking + for (j = 0; j < e; j++) { + if (dist[arr[j].u] != Integer.MAX_VALUE && dist[arr[j].v] > dist[arr[j].u] + arr[j].w) { + neg = 1; + System.out.println("Negative cycle"); + break; + } + } + if (neg == 0) { // Go ahead and show results of computation + System.out.println("Distances are: "); + for (i = 0; i < v; i++) { + System.out.println(i + " " + dist[i]); + } + System.out.println("Path followed:"); + for (i = 0; i < v; i++) { + System.out.print("0 "); + printPath(p, i); + System.out.println(); + } + } + } + } + + /** + * @param source Starting vertex + * @param end Ending vertex + * @param Edge Array of edges + */ + public void show(int source, int end, + Edge[] arr) { // be created by using addEdge() method and passed by calling getEdgeArray() + // method // Just shows results of computation, if graph is passed to it. The + // graph should + int i; + int j; + int v = vertex; + int e = edge; + int neg = 0; + double[] dist = new double[v]; // Distance array for holding the finalized shortest path + // distance between source + // and all vertices + int[] p = new int[v]; // Parent array for holding the paths + for (i = 0; i < v; i++) { + dist[i] = Integer.MAX_VALUE; // Initializing distance values + } + dist[source] = 0; + p[source] = -1; + for (i = 0; i < v - 1; i++) { + for (j = 0; j < e; j++) { + if ((int) dist[arr[j].u] != Integer.MAX_VALUE && dist[arr[j].v] > dist[arr[j].u] + arr[j].w) { + dist[arr[j].v] = dist[arr[j].u] + arr[j].w; // Update + p[arr[j].v] = arr[j].u; + } + } + } + // Final cycle for negative checking + for (j = 0; j < e; j++) { + if ((int) dist[arr[j].u] != Integer.MAX_VALUE && dist[arr[j].v] > dist[arr[j].u] + arr[j].w) { + neg = 1; + System.out.println("Negative cycle"); + break; + } + } + if (neg == 0) { // Go ahead and show results of computaion + System.out.println("Distance is: " + dist[end]); + System.out.println("Path followed:"); + System.out.print(source + " "); + printPath(p, end); + System.out.println(); + } + } + + /** + * @param x Source Vertex + * @param y End vertex + * @param z Weight + */ + public void addEdge(int x, int y, int z) { // Adds unidirectional edge + edges[index++] = new Edge(x, y, z); + } + + public Edge[] getEdgeArray() { + return edges; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/BipartiteGraphDFS.java b/src/main/java/com/thealgorithms/datastructures/graphs/BipartiteGraphDFS.java new file mode 100644 index 000000000000..15ae5225533c --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/graphs/BipartiteGraphDFS.java @@ -0,0 +1,83 @@ +package com.thealgorithms.datastructures.graphs; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * This class provides a method to check if a given undirected graph is bipartite using Depth-First Search (DFS). + * A bipartite graph is a graph whose vertices can be divided into two disjoint sets such that no two vertices + * within the same set are adjacent. In other words, all edges must go between the two sets. + * + * The implementation leverages DFS to attempt to color the graph using two colors. If we can color the graph such + * that no two adjacent vertices have the same color, the graph is bipartite. + * + * Example: + * Input (Adjacency Matrix): + * {{0, 1, 0, 1}, + * {1, 0, 1, 0}, + * {0, 1, 0, 1}, + * {1, 0, 1, 0}} + * + * Output: YES (This graph is bipartite) + * + * Input (Adjacency Matrix): + * {{0, 1, 1, 0}, + * {1, 0, 1, 0}, + * {1, 1, 0, 1}, + * {0, 0, 1, 0}} + * + * Output: NO (This graph is not bipartite) + */ +public final class BipartiteGraphDFS { + private BipartiteGraphDFS() { + } + + /** + * Helper method to perform DFS and check if the graph is bipartite. + * + * During DFS traversal, this method attempts to color each vertex in such a way + * that no two adjacent vertices share the same color. + * + * @param v Number of vertices in the graph + * @param adj Adjacency list of the graph where each index i contains a list of adjacent vertices + * @param color Array to store the color assigned to each vertex (-1 indicates uncolored) + * @param node Current vertex being processed + * @return True if the graph (or component of the graph) is bipartite, otherwise false + */ + private static boolean bipartite(int v, ArrayList<ArrayList<Integer>> adj, int[] color, int node) { + if (color[node] == -1) { + color[node] = 1; + } + for (Integer it : adj.get(node)) { + if (color[it] == -1) { + color[it] = 1 - color[node]; + if (!bipartite(v, adj, color, it)) { + return false; + } + } else if (color[it] == color[node]) { + return false; + } + } + return true; + } + + /** + * Method to check if the graph is bipartite. + * + * @param v Number of vertices in the graph + * @param adj Adjacency list of the graph + * @return True if the graph is bipartite, otherwise false + */ + public static boolean isBipartite(int v, ArrayList<ArrayList<Integer>> adj) { + int[] color = new int[v + 1]; + Arrays.fill(color, -1); + for (int i = 0; i < v; i++) { + if (color[i] == -1) { + if (!bipartite(v, adj, color, i)) { + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/BoruvkaAlgorithm.java b/src/main/java/com/thealgorithms/datastructures/graphs/BoruvkaAlgorithm.java new file mode 100644 index 000000000000..dcdb08ad133e --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/graphs/BoruvkaAlgorithm.java @@ -0,0 +1,217 @@ +package com.thealgorithms.datastructures.graphs; + +import java.util.ArrayList; +import java.util.List; + +/** + * Boruvka's algorithm to find Minimum Spanning Tree + * (https://en.wikipedia.org/wiki/Bor%C5%AFvka%27s_algorithm) + * + * @author itakurah (https://github.com/itakurah) + */ + +final class BoruvkaAlgorithm { + private BoruvkaAlgorithm() { + } + + /** + * Represents an edge in the graph + */ + static class Edge { + final int src; + final int dest; + final int weight; + + Edge(final int src, final int dest, final int weight) { + this.src = src; + this.dest = dest; + this.weight = weight; + } + } + + /** + * Represents the graph + */ + static class Graph { + final int vertex; + final List<Edge> edges; + + /** + * Constructor for the graph + * + * @param vertex number of vertices + * @param edges list of edges + */ + Graph(final int vertex, final List<Edge> edges) { + if (vertex < 0) { + throw new IllegalArgumentException("Number of vertices must be positive"); + } + if (edges == null || edges.isEmpty()) { + throw new IllegalArgumentException("Edges list must not be null or empty"); + } + for (final var edge : edges) { + checkEdgeVertices(edge.src, vertex); + checkEdgeVertices(edge.dest, vertex); + } + + this.vertex = vertex; + this.edges = edges; + } + } + + /** + * Represents a subset for Union-Find operations + */ + private static class Component { + int parent; + int rank; + + Component(final int parent, final int rank) { + this.parent = parent; + this.rank = rank; + } + } + + /** + * Represents the state of Union-Find components and the result list + */ + private static class BoruvkaState { + List<Edge> result; + Component[] components; + final Graph graph; + + BoruvkaState(final Graph graph) { + this.result = new ArrayList<>(); + this.components = initializeComponents(graph); + this.graph = graph; + } + + /** + * Adds the cheapest edges to the result list and performs Union operation on the subsets. + * + * @param cheapest Array containing the cheapest edge for each subset. + */ + void merge(final Edge[] cheapest) { + for (int i = 0; i < graph.vertex; ++i) { + if (cheapest[i] != null) { + final var component1 = find(components, cheapest[i].src); + final var component2 = find(components, cheapest[i].dest); + + if (component1 != component2) { + result.add(cheapest[i]); + union(components, component1, component2); + } + } + } + } + + /** + * Checks if there are more edges to add to the result list + * + * @return true if there are more edges to add, false otherwise + */ + boolean hasMoreEdgesToAdd() { + return result.size() < graph.vertex - 1; + } + + /** + * Computes the cheapest edges for each subset in the Union-Find structure. + * + * @return an array containing the cheapest edge for each subset. + */ + private Edge[] computeCheapestEdges() { + Edge[] cheapest = new Edge[graph.vertex]; + for (final var edge : graph.edges) { + final var set1 = find(components, edge.src); + final var set2 = find(components, edge.dest); + + if (set1 != set2) { + if (cheapest[set1] == null || edge.weight < cheapest[set1].weight) { + cheapest[set1] = edge; + } + if (cheapest[set2] == null || edge.weight < cheapest[set2].weight) { + cheapest[set2] = edge; + } + } + } + return cheapest; + } + + /** + * Initializes subsets for Union-Find + * + * @param graph the graph + * @return the initialized subsets + */ + private static Component[] initializeComponents(final Graph graph) { + Component[] components = new Component[graph.vertex]; + for (int v = 0; v < graph.vertex; ++v) { + components[v] = new Component(v, 0); + } + return components; + } + } + + /** + * Finds the parent of the subset using path compression + * + * @param components array of subsets + * @param i index of the subset + * @return the parent of the subset + */ + static int find(final Component[] components, final int i) { + if (components[i].parent != i) { + components[i].parent = find(components, components[i].parent); + } + return components[i].parent; + } + + /** + * Performs the Union operation for Union-Find + * + * @param components array of subsets + * @param x index of the first subset + * @param y index of the second subset + */ + static void union(Component[] components, final int x, final int y) { + final int xroot = find(components, x); + final int yroot = find(components, y); + + if (components[xroot].rank < components[yroot].rank) { + components[xroot].parent = yroot; + } else if (components[xroot].rank > components[yroot].rank) { + components[yroot].parent = xroot; + } else { + components[yroot].parent = xroot; + components[xroot].rank++; + } + } + + /** + * Boruvka's algorithm to find the Minimum Spanning Tree + * + * @param graph the graph + * @return list of edges in the Minimum Spanning Tree + */ + static List<Edge> boruvkaMST(final Graph graph) { + var boruvkaState = new BoruvkaState(graph); + + while (boruvkaState.hasMoreEdgesToAdd()) { + final var cheapest = boruvkaState.computeCheapestEdges(); + boruvkaState.merge(cheapest); + } + return boruvkaState.result; + } + + /** + * Checks if the edge vertices are in a valid range + * + * @param vertex the vertex to check + * @param upperBound the upper bound for the vertex range + */ + private static void checkEdgeVertices(final int vertex, final int upperBound) { + if (vertex < 0 || vertex >= upperBound) { + throw new IllegalArgumentException("Edge vertex out of range"); + } + } +} diff --git a/DataStructures/Graphs/ConnectedComponent.java b/src/main/java/com/thealgorithms/datastructures/graphs/ConnectedComponent.java similarity index 84% rename from DataStructures/Graphs/ConnectedComponent.java rename to src/main/java/com/thealgorithms/datastructures/graphs/ConnectedComponent.java index 7029a8d811f2..520a1681774a 100644 --- a/DataStructures/Graphs/ConnectedComponent.java +++ b/src/main/java/com/thealgorithms/datastructures/graphs/ConnectedComponent.java @@ -1,141 +1,146 @@ -package DataStructures.Graphs; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; - -/** - * A class that counts the number of different connected components in a graph - * - * @author Lukas Keul, Florian Mercks - */ -class Graph<E extends Comparable<E>> { - - class Node { - E name; - - public Node(E name) { - this.name = name; - } - } - - class Edge { - Node startNode, endNode; - - public Edge(Node startNode, Node endNode) { - this.startNode = startNode; - this.endNode = endNode; - } - } - - ArrayList<Edge> edgeList; - ArrayList<Node> nodeList; - - public Graph() { - edgeList = new ArrayList<Edge>(); - nodeList = new ArrayList<Node>(); - } - - /** - * Adds a new Edge to the graph. If the nodes aren't yet in nodeList, they - * will be added to it. - * - * @param startNode the starting Node from the edge - * @param endNode the ending Node from the edge - */ - public void addEdge(E startNode, E endNode) { - Node start = null, end = null; - for (Node node : nodeList) { - if (startNode.compareTo(node.name) == 0) { - start = node; - } else if (endNode.compareTo(node.name) == 0) { - end = node; - } - } - if (start == null) { - start = new Node(startNode); - nodeList.add(start); - } - if (end == null) { - end = new Node(endNode); - nodeList.add(end); - } - - edgeList.add(new Edge(start, end)); - } - - /** - * Main method used for counting the connected components. Iterates through - * the array of nodes to do a depth first search to get all nodes of the - * graph from the actual node. These nodes are added to the array - * markedNodes and will be ignored if they are chosen in the nodeList. - * - * @return returns the amount of unconnected graphs - */ - public int countGraphs() { - int count = 0; - Set<Node> markedNodes = new HashSet<Node>(); - - for (Node n : nodeList) { - if (!markedNodes.contains(n)) { - markedNodes.add(n); - markedNodes.addAll(depthFirstSearch(n, new ArrayList<Node>())); - count++; - } - } - - return count; - } - - /** - * Implementation of depth first search. - * - * @param n the actual visiting node - * @param visited A list of already visited nodes in the depth first search - * @return returns a set of visited nodes - */ - public ArrayList<Node> depthFirstSearch(Node n, ArrayList<Node> visited) { - visited.add(n); - for (Edge e : edgeList) { - if (e.startNode.equals(n) && !visited.contains(e.endNode)) { - depthFirstSearch(e.endNode, visited); - } - } - return visited; - } -} - -public class ConnectedComponent { - - public static void main(String[] args) { - Graph graphChars = new Graph(); - - // Graph 1 - graphChars.addEdge('a', 'b'); - graphChars.addEdge('a', 'e'); - graphChars.addEdge('b', 'e'); - graphChars.addEdge('b', 'c'); - graphChars.addEdge('c', 'd'); - graphChars.addEdge('d', 'a'); - - graphChars.addEdge('x', 'y'); - graphChars.addEdge('x', 'z'); - - graphChars.addEdge('w', 'w'); - - Graph graphInts = new Graph(); - - // Graph 2 - graphInts.addEdge(1, 2); - graphInts.addEdge(2, 3); - graphInts.addEdge(2, 4); - graphInts.addEdge(3, 5); - - graphInts.addEdge(7, 8); - graphInts.addEdge(8, 10); - graphInts.addEdge(10, 8); - - System.out.println("Amount of different char-graphs: " + graphChars.countGraphs()); - System.out.println("Amount of different int-graphs: " + graphInts.countGraphs()); - } -} +package com.thealgorithms.datastructures.graphs; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +/** + * A class that counts the number of different connected components in a graph + * + * @author Lukas Keul, Florian Mercks + */ +class Graph<E extends Comparable<E>> { + + class Node { + + E name; + + Node(E name) { + this.name = name; + } + } + + class Edge { + + Node startNode; + Node endNode; + + Edge(Node startNode, Node endNode) { + this.startNode = startNode; + this.endNode = endNode; + } + } + + ArrayList<Edge> edgeList; + ArrayList<Node> nodeList; + + Graph() { + edgeList = new ArrayList<Edge>(); + nodeList = new ArrayList<Node>(); + } + + /** + * Adds a new Edge to the graph. If the nodes aren't yet in nodeList, they + * will be added to it. + * + * @param startNode the starting Node from the edge + * @param endNode the ending Node from the edge + */ + public void addEdge(E startNode, E endNode) { + Node start = null; + Node end = null; + for (Node node : nodeList) { + if (startNode.compareTo(node.name) == 0) { + start = node; + } else if (endNode.compareTo(node.name) == 0) { + end = node; + } + } + if (start == null) { + start = new Node(startNode); + nodeList.add(start); + } + if (end == null) { + end = new Node(endNode); + nodeList.add(end); + } + + edgeList.add(new Edge(start, end)); + } + + /** + * Main method used for counting the connected components. Iterates through + * the array of nodes to do a depth first search to get all nodes of the + * graph from the actual node. These nodes are added to the array + * markedNodes and will be ignored if they are chosen in the nodeList. + * + * @return returns the amount of unconnected graphs + */ + public int countGraphs() { + int count = 0; + Set<Node> markedNodes = new HashSet<Node>(); + + for (Node n : nodeList) { + if (markedNodes.add(n)) { + markedNodes.addAll(depthFirstSearch(n, new ArrayList<Node>())); + count++; + } + } + + return count; + } + + /** + * Implementation of depth first search. + * + * @param n the actual visiting node + * @param visited A list of already visited nodes in the depth first search + * @return returns a set of visited nodes + */ + public ArrayList<Node> depthFirstSearch(Node n, ArrayList<Node> visited) { + visited.add(n); + for (Edge e : edgeList) { + if (e.startNode.equals(n) && !visited.contains(e.endNode)) { + depthFirstSearch(e.endNode, visited); + } + } + return visited; + } +} + +public final class ConnectedComponent { + private ConnectedComponent() { + } + + public static void main(String[] args) { + Graph<Character> graphChars = new Graph<>(); + + // Graph 1 + graphChars.addEdge('a', 'b'); + graphChars.addEdge('a', 'e'); + graphChars.addEdge('b', 'e'); + graphChars.addEdge('b', 'c'); + graphChars.addEdge('c', 'd'); + graphChars.addEdge('d', 'a'); + + graphChars.addEdge('x', 'y'); + graphChars.addEdge('x', 'z'); + + graphChars.addEdge('w', 'w'); + + Graph<Integer> graphInts = new Graph<>(); + + // Graph 2 + graphInts.addEdge(1, 2); + graphInts.addEdge(2, 3); + graphInts.addEdge(2, 4); + graphInts.addEdge(3, 5); + + graphInts.addEdge(7, 8); + graphInts.addEdge(8, 10); + graphInts.addEdge(10, 8); + + System.out.println("Amount of different char-graphs: " + graphChars.countGraphs()); + System.out.println("Amount of different int-graphs: " + graphInts.countGraphs()); + } +} diff --git a/DataStructures/Graphs/Cycles.java b/src/main/java/com/thealgorithms/datastructures/graphs/Cycles.java similarity index 89% rename from DataStructures/Graphs/Cycles.java rename to src/main/java/com/thealgorithms/datastructures/graphs/Cycles.java index b6fd8b4c58c5..aea2b74bd13b 100644 --- a/DataStructures/Graphs/Cycles.java +++ b/src/main/java/com/thealgorithms/datastructures/graphs/Cycles.java @@ -1,23 +1,21 @@ -package DataStructures.Graphs; +package com.thealgorithms.datastructures.graphs; -import java.util.Scanner; import java.util.ArrayList; - +import java.util.Scanner; class Cycle { - private int nodes, edges; + private final int nodes; private int[][] adjacencyMatrix; private boolean[] visited; ArrayList<ArrayList<Integer>> cycles = new ArrayList<ArrayList<Integer>>(); - private boolean[] finalCycles; - public Cycle() { + Cycle() { Scanner in = new Scanner(System.in); System.out.print("Enter the no. of nodes: "); nodes = in.nextInt(); System.out.print("Enter the no. of Edges: "); - edges = in.nextInt(); + final int edges = in.nextInt(); adjacencyMatrix = new int[nodes][nodes]; visited = new boolean[nodes]; @@ -29,12 +27,13 @@ public Cycle() { System.out.println("Enter the details of each edges <Start Node> <End Node>"); for (int i = 0; i < edges; i++) { - int start, end; + int start; + int end; start = in.nextInt(); end = in.nextInt(); adjacencyMatrix[start][end] = 1; } - + in.close(); } public void start() { @@ -77,15 +76,16 @@ public void printAll() { System.out.println(cycles.get(i).get(0)); System.out.println(); } - } - } -public class Cycles { +public final class Cycles { + private Cycles() { + } + public static void main(String[] args) { Cycle c = new Cycle(); c.start(); c.printAll(); } -} \ No newline at end of file +} diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/DijkstraAlgorithm.java b/src/main/java/com/thealgorithms/datastructures/graphs/DijkstraAlgorithm.java new file mode 100644 index 000000000000..70699a9461f7 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/graphs/DijkstraAlgorithm.java @@ -0,0 +1,91 @@ +package com.thealgorithms.datastructures.graphs; + +import java.util.Arrays; + +/** + * Dijkstra's algorithm for finding the shortest path from a single source vertex to all other vertices in a graph. + */ +public class DijkstraAlgorithm { + + private final int vertexCount; + + /** + * Constructs a Dijkstra object with the given number of vertices. + * + * @param vertexCount The number of vertices in the graph. + */ + public DijkstraAlgorithm(int vertexCount) { + this.vertexCount = vertexCount; + } + + /** + * Executes Dijkstra's algorithm on the provided graph to find the shortest paths from the source vertex to all other vertices. + * + * The graph is represented as an adjacency matrix where {@code graph[i][j]} represents the weight of the edge from vertex {@code i} + * to vertex {@code j}. A value of 0 indicates no edge exists between the vertices. + * + * @param graph The graph represented as an adjacency matrix. + * @param source The source vertex. + * @return An array where the value at each index {@code i} represents the shortest distance from the source vertex to vertex {@code i}. + * @throws IllegalArgumentException if the source vertex is out of range. + */ + public int[] run(int[][] graph, int source) { + if (source < 0 || source >= vertexCount) { + throw new IllegalArgumentException("Incorrect source"); + } + + int[] distances = new int[vertexCount]; + boolean[] processed = new boolean[vertexCount]; + + Arrays.fill(distances, Integer.MAX_VALUE); + Arrays.fill(processed, false); + distances[source] = 0; + + for (int count = 0; count < vertexCount - 1; count++) { + int u = getMinDistanceVertex(distances, processed); + processed[u] = true; + + for (int v = 0; v < vertexCount; v++) { + if (!processed[v] && graph[u][v] != 0 && distances[u] != Integer.MAX_VALUE && distances[u] + graph[u][v] < distances[v]) { + distances[v] = distances[u] + graph[u][v]; + } + } + } + + printDistances(distances); + return distances; + } + + /** + * Finds the vertex with the minimum distance value from the set of vertices that have not yet been processed. + * + * @param distances The array of current shortest distances from the source vertex. + * @param processed The array indicating whether each vertex has been processed. + * @return The index of the vertex with the minimum distance value. + */ + private int getMinDistanceVertex(int[] distances, boolean[] processed) { + int min = Integer.MAX_VALUE; + int minIndex = -1; + + for (int v = 0; v < vertexCount; v++) { + if (!processed[v] && distances[v] <= min) { + min = distances[v]; + minIndex = v; + } + } + + return minIndex; + } + + /** + * Prints the shortest distances from the source vertex to all other vertices. + * + * @param distances The array of shortest distances. + */ + private void printDistances(int[] distances) { + System.out.println("Vertex \t Distance"); + for (int i = 0; i < vertexCount; i++) { + System.out.println(i + " \t " + distances[i]); + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/DijkstraOptimizedAlgorithm.java b/src/main/java/com/thealgorithms/datastructures/graphs/DijkstraOptimizedAlgorithm.java new file mode 100644 index 000000000000..a686b808a970 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/graphs/DijkstraOptimizedAlgorithm.java @@ -0,0 +1,66 @@ +package com.thealgorithms.datastructures.graphs; + +import java.util.Arrays; +import java.util.Set; +import java.util.TreeSet; +import org.apache.commons.lang3.tuple.Pair; + +/** + * Dijkstra's algorithm for finding the shortest path from a single source vertex to all other vertices in a graph. + */ +public class DijkstraOptimizedAlgorithm { + + private final int vertexCount; + + /** + * Constructs a Dijkstra object with the given number of vertices. + * + * @param vertexCount The number of vertices in the graph. + */ + public DijkstraOptimizedAlgorithm(int vertexCount) { + this.vertexCount = vertexCount; + } + + /** + * Executes Dijkstra's algorithm on the provided graph to find the shortest paths from the source vertex to all other vertices. + * + * The graph is represented as an adjacency matrix where {@code graph[i][j]} represents the weight of the edge from vertex {@code i} + * to vertex {@code j}. A value of 0 indicates no edge exists between the vertices. + * + * @param graph The graph represented as an adjacency matrix. + * @param source The source vertex. + * @return An array where the value at each index {@code i} represents the shortest distance from the source vertex to vertex {@code i}. + * @throws IllegalArgumentException if the source vertex is out of range. + */ + public int[] run(int[][] graph, int source) { + if (source < 0 || source >= vertexCount) { + throw new IllegalArgumentException("Incorrect source"); + } + + int[] distances = new int[vertexCount]; + boolean[] processed = new boolean[vertexCount]; + Set<Pair<Integer, Integer>> unprocessed = new TreeSet<>(); + + Arrays.fill(distances, Integer.MAX_VALUE); + Arrays.fill(processed, false); + distances[source] = 0; + unprocessed.add(Pair.of(0, source)); + + while (!unprocessed.isEmpty()) { + Pair<Integer, Integer> distanceAndU = unprocessed.iterator().next(); + unprocessed.remove(distanceAndU); + int u = distanceAndU.getRight(); + processed[u] = true; + + for (int v = 0; v < vertexCount; v++) { + if (!processed[v] && graph[u][v] != 0 && distances[u] != Integer.MAX_VALUE && distances[u] + graph[u][v] < distances[v]) { + unprocessed.remove(Pair.of(distances[v], v)); + distances[v] = distances[u] + graph[u][v]; + unprocessed.add(Pair.of(distances[v], v)); + } + } + } + + return distances; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/EdmondsBlossomAlgorithm.java b/src/main/java/com/thealgorithms/datastructures/graphs/EdmondsBlossomAlgorithm.java new file mode 100644 index 000000000000..db716580d689 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/graphs/EdmondsBlossomAlgorithm.java @@ -0,0 +1,251 @@ +package com.thealgorithms.datastructures.graphs; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + * The EdmondsBlossomAlgorithm class implements Edmonds' Blossom Algorithm + * to find the maximum matching in a general graph. The algorithm efficiently + * handles cases where the graph contains odd-length cycles by contracting + * "blossoms" and finding augmenting paths. + *<p> + * <a href="https://stanford.edu/~rezab/classes/cme323/S16/projects_reports/shoemaker_vare.pdf">Documentation of Algorithm (Stanford University)</a> + * <p></p> + * <a href="https://en.wikipedia.org/wiki/Blossom_algorithm">Wikipedia Documentation</a> + */ +public final class EdmondsBlossomAlgorithm { + + private EdmondsBlossomAlgorithm() { + } + + private static final int UNMATCHED = -1; // Constant to represent unmatched vertices + + /** + * Finds the maximum matching in a general graph (Edmonds Blossom Algorithm). + * + * @param edges A list of edges in the graph. + * @param vertexCount The number of vertices in the graph. + * @return A list of matched pairs of vertices. + */ + public static List<int[]> maximumMatching(Iterable<int[]> edges, int vertexCount) { + List<List<Integer>> graph = new ArrayList<>(vertexCount); + + // Initialize each vertex's adjacency list. + for (int i = 0; i < vertexCount; i++) { + graph.add(new ArrayList<>()); + } + + // Populate the graph with the edges + for (int[] edge : edges) { + int u = edge[0]; + int v = edge[1]; + graph.get(u).add(v); + graph.get(v).add(u); + } + + // Initial matching array and auxiliary data structures + int[] match = new int[vertexCount]; + Arrays.fill(match, UNMATCHED); // All vertices are initially unmatched + int[] parent = new int[vertexCount]; + int[] base = new int[vertexCount]; + boolean[] inBlossom = new boolean[vertexCount]; // Indicates if a vertex is part of a blossom + boolean[] inQueue = new boolean[vertexCount]; // Tracks vertices in the BFS queue + + // Main logic for finding maximum matching + for (int u = 0; u < vertexCount; u++) { + if (match[u] == UNMATCHED) { + // BFS initialization + Arrays.fill(parent, UNMATCHED); + for (int i = 0; i < vertexCount; i++) { + base[i] = i; // Each vertex is its own base initially + } + Arrays.fill(inBlossom, false); + Arrays.fill(inQueue, false); + + Queue<Integer> queue = new LinkedList<>(); + queue.add(u); + inQueue[u] = true; + + boolean augmentingPathFound = false; + + // BFS to find augmenting paths + while (!queue.isEmpty() && !augmentingPathFound) { + int current = queue.poll(); // Use a different name for clarity + for (int y : graph.get(current)) { + // Skip if we are looking at the same edge as the current match + if (match[current] == y) { + continue; + } + + if (base[current] == base[y]) { + continue; // Avoid self-loops + } + + if (parent[y] == UNMATCHED) { + // Case 1: y is unmatched, we've found an augmenting path + if (match[y] == UNMATCHED) { + parent[y] = current; + augmentingPathFound = true; + updateMatching(match, parent, y); // Augment along this path + break; + } + + // Case 2: y is matched, add y's match to the queue + int z = match[y]; + parent[y] = current; + parent[z] = y; + if (!inQueue[z]) { + queue.add(z); + inQueue[z] = true; + } + } else { + // Case 3: Both x and y have a parent; check for a cycle/blossom + int baseU = findBase(base, parent, current, y); + if (baseU != UNMATCHED) { + contractBlossom(new BlossomData(new BlossomAuxData(queue, parent, base, inBlossom, match, inQueue), current, y, baseU)); + } + } + } + } + } + } + + // Create result list of matched pairs + List<int[]> matchingResult = new ArrayList<>(); + for (int v = 0; v < vertexCount; v++) { + if (match[v] != UNMATCHED && v < match[v]) { + matchingResult.add(new int[] {v, match[v]}); + } + } + + return matchingResult; + } + + /** + * Updates the matching along the augmenting path found. + * + * @param match The matching array. + * @param parent The parent array used during the BFS. + * @param u The starting node of the augmenting path. + */ + private static void updateMatching(int[] match, int[] parent, int u) { + while (u != UNMATCHED) { + int v = parent[u]; + int next = match[v]; + match[v] = u; + match[u] = v; + u = next; + } + } + + /** + * Finds the base of a node in the blossom. + * + * @param base The base array. + * @param parent The parent array. + * @param u One end of the edge. + * @param v The other end of the edge. + * @return The base of the node or UNMATCHED. + */ + private static int findBase(int[] base, int[] parent, int u, int v) { + boolean[] visited = new boolean[base.length]; + + // Mark ancestors of u + int currentU = u; + while (true) { + currentU = base[currentU]; // Move assignment out of the condition + visited[currentU] = true; + if (parent[currentU] == UNMATCHED) { + break; + } + currentU = parent[currentU]; // Move assignment out of the condition + } + + // Find the common ancestor of v + int currentV = v; + while (true) { + currentV = base[currentV]; // Move assignment out of the condition + if (visited[currentV]) { + return currentV; + } + currentV = parent[currentV]; // Move assignment out of the condition + } + } + + /** + * Contracts a blossom and updates the base array. + * + * @param blossomData The data containing the parameters related to the blossom contraction. + */ + private static void contractBlossom(BlossomData blossomData) { + for (int x = blossomData.u; blossomData.auxData.base[x] != blossomData.lca; x = blossomData.auxData.parent[blossomData.auxData.match[x]]) { + int baseX = blossomData.auxData.base[x]; + int matchBaseX = blossomData.auxData.base[blossomData.auxData.match[x]]; + + // Split the inner assignment into two separate assignments + blossomData.auxData.inBlossom[baseX] = true; + blossomData.auxData.inBlossom[matchBaseX] = true; + } + + for (int x = blossomData.v; blossomData.auxData.base[x] != blossomData.lca; x = blossomData.auxData.parent[blossomData.auxData.match[x]]) { + int baseX = blossomData.auxData.base[x]; + int matchBaseX = blossomData.auxData.base[blossomData.auxData.match[x]]; + + // Split the inner assignment into two separate assignments + blossomData.auxData.inBlossom[baseX] = true; + blossomData.auxData.inBlossom[matchBaseX] = true; + } + + // Update the base for all marked vertices + for (int i = 0; i < blossomData.auxData.base.length; i++) { + if (blossomData.auxData.inBlossom[blossomData.auxData.base[i]]) { + blossomData.auxData.base[i] = blossomData.lca; // Contract to the lowest common ancestor + if (!blossomData.auxData.inQueue[i]) { + blossomData.auxData.queue.add(i); // Add to queue if not already present + blossomData.auxData.inQueue[i] = true; + } + } + } + } + + /** + * Auxiliary data class to encapsulate common parameters for the blossom operations. + */ + static class BlossomAuxData { + Queue<Integer> queue; // Queue for BFS traversal + int[] parent; // Parent array to store the paths + int[] base; // Base array to track the base of each vertex + boolean[] inBlossom; // Flags to indicate if a vertex is in a blossom + int[] match; // Array to store matches for each vertex + boolean[] inQueue; // Flags to track vertices in the BFS queue + + BlossomAuxData(Queue<Integer> queue, int[] parent, int[] base, boolean[] inBlossom, int[] match, boolean[] inQueue) { + this.queue = queue; + this.parent = parent; + this.base = base; + this.inBlossom = inBlossom; + this.match = match; + this.inQueue = inQueue; + } + } + + /** + * BlossomData class with reduced parameters. + */ + static class BlossomData { + BlossomAuxData auxData; // Use the auxiliary data class + int u; // One vertex in the edge + int v; // Another vertex in the edge + int lca; // Lowest Common Ancestor + + BlossomData(BlossomAuxData auxData, int u, int v, int lca) { + this.auxData = auxData; + this.u = u; + this.v = v; + this.lca = lca; + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/FloydWarshall.java b/src/main/java/com/thealgorithms/datastructures/graphs/FloydWarshall.java new file mode 100644 index 000000000000..e5e673a21794 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/graphs/FloydWarshall.java @@ -0,0 +1,85 @@ +package com.thealgorithms.datastructures.graphs; + +/** + * The {@code FloydWarshall} class provides an implementation of the Floyd-Warshall algorithm + * to compute the shortest paths between all pairs of vertices in a weighted graph. + * It handles both positive and negative edge weights but does not support negative cycles. + * The algorithm is based on dynamic programming and runs in O(V^3) time complexity, + * where V is the number of vertices in the graph. + * + * <p> + * The distance matrix is updated iteratively to find the shortest distance between any two vertices + * by considering each vertex as an intermediate step. + * </p> + * + * Reference: <a href="https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm">Floyd-Warshall Algorithm</a> + */ +public class FloydWarshall { + + private int[][] distanceMatrix; + private int numberofvertices; + public static final int INFINITY = 999; + + /** + * Constructs a Floyd-Warshall instance for a graph with the given number of vertices. + * Initializes the distance matrix for the graph. + * + * @param numberofvertices The number of vertices in the graph. + */ + public FloydWarshall(int numberofvertices) { + distanceMatrix = new int[numberofvertices + 1][numberofvertices + 1]; + // The matrix is initialized with 0's by default + this.numberofvertices = numberofvertices; + } + + /** + * Executes the Floyd-Warshall algorithm to compute the shortest path between all pairs of vertices. + * It uses an adjacency matrix to calculate the distance matrix by considering each vertex as an intermediate point. + * + * @param adjacencyMatrix The weighted adjacency matrix representing the graph. + * A value of 0 means no direct edge between the vertices, except for diagonal elements which are 0 (distance to self). + */ + public void floydwarshall(int[][] adjacencyMatrix) { + // Initialize the distance matrix with the adjacency matrix. + for (int source = 1; source <= numberofvertices; source++) { + System.arraycopy(adjacencyMatrix[source], 1, distanceMatrix[source], 1, numberofvertices); + } + for (int intermediate = 1; intermediate <= numberofvertices; intermediate++) { + for (int source = 1; source <= numberofvertices; source++) { + for (int destination = 1; destination <= numberofvertices; destination++) { + // Update distance if a shorter path through the intermediate vertex exists. + if (distanceMatrix[source][intermediate] + distanceMatrix[intermediate][destination] < distanceMatrix[source][destination]) { + distanceMatrix[source][destination] = distanceMatrix[source][intermediate] + distanceMatrix[intermediate][destination]; + } + } + } + } + + printDistanceMatrix(); + } + + /** + * Prints the distance matrix representing the shortest paths between all pairs of vertices. + * The rows and columns correspond to the source and destination vertices. + */ + private void printDistanceMatrix() { + // Print header for vertices + for (int source = 1; source <= numberofvertices; source++) { + System.out.print("\t" + source); + } + System.out.println(); + + // Print the distance matrix + for (int source = 1; source <= numberofvertices; source++) { + System.out.print(source + "\t"); + for (int destination = 1; destination <= numberofvertices; destination++) { + System.out.print(distanceMatrix[source][destination] + "\t"); + } + System.out.println(); + } + } + + public Object[] getDistanceMatrix() { + return distanceMatrix; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/FordFulkerson.java b/src/main/java/com/thealgorithms/datastructures/graphs/FordFulkerson.java new file mode 100644 index 000000000000..b3a2053d54b5 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/graphs/FordFulkerson.java @@ -0,0 +1,75 @@ +package com.thealgorithms.datastructures.graphs; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * This class implements the Ford-Fulkerson algorithm to compute the maximum flow + * in a flow network. + * + * <p>The algorithm uses breadth-first search (BFS) to find augmenting paths from + * the source vertex to the sink vertex, updating the flow in the network until + * no more augmenting paths can be found.</p> + */ +public final class FordFulkerson { + private static final int INF = Integer.MAX_VALUE; + + private FordFulkerson() { + } + + /** + * Computes the maximum flow in a flow network using the Ford-Fulkerson algorithm. + * + * @param vertexCount the number of vertices in the flow network + * @param capacity a 2D array representing the capacity of edges in the network + * @param flow a 2D array representing the current flow in the network + * @param source the source vertex in the flow network + * @param sink the sink vertex in the flow network + * @return the total maximum flow from the source to the sink + */ + public static int networkFlow(int vertexCount, int[][] capacity, int[][] flow, int source, int sink) { + int totalFlow = 0; + + while (true) { + int[] parent = new int[vertexCount]; + boolean[] visited = new boolean[vertexCount]; + Queue<Integer> queue = new LinkedList<>(); + + queue.add(source); + visited[source] = true; + parent[source] = -1; + + while (!queue.isEmpty() && !visited[sink]) { + int current = queue.poll(); + + for (int next = 0; next < vertexCount; next++) { + if (!visited[next] && capacity[current][next] - flow[current][next] > 0) { + queue.add(next); + visited[next] = true; + parent[next] = current; + } + } + } + + if (!visited[sink]) { + break; // No more augmenting paths + } + + int pathFlow = INF; + for (int v = sink; v != source; v = parent[v]) { + int u = parent[v]; + pathFlow = Math.min(pathFlow, capacity[u][v] - flow[u][v]); + } + + for (int v = sink; v != source; v = parent[v]) { + int u = parent[v]; + flow[u][v] += pathFlow; + flow[v][u] -= pathFlow; + } + + totalFlow += pathFlow; + } + + return totalFlow; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/Graphs.java b/src/main/java/com/thealgorithms/datastructures/graphs/Graphs.java new file mode 100644 index 000000000000..b0970f36ddc5 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/graphs/Graphs.java @@ -0,0 +1,140 @@ +package com.thealgorithms.datastructures.graphs; + +import java.util.ArrayList; + +class AdjacencyListGraph<E extends Comparable<E>> { + + ArrayList<Vertex> vertices; + + AdjacencyListGraph() { + vertices = new ArrayList<>(); + } + + private class Vertex { + + E data; + ArrayList<Vertex> adjacentVertices; + + Vertex(E data) { + adjacentVertices = new ArrayList<>(); + this.data = data; + } + + public boolean addAdjacentVertex(Vertex to) { + for (Vertex v : adjacentVertices) { + if (v.data.compareTo(to.data) == 0) { + return false; // the edge already exists + } + } + return adjacentVertices.add(to); // this will return true; + } + + public boolean removeAdjacentVertex(E to) { + // use indexes here so it is possible to + // remove easily without implementing + // equals method that ArrayList.remove(Object o) uses + for (int i = 0; i < adjacentVertices.size(); i++) { + if (adjacentVertices.get(i).data.compareTo(to) == 0) { + adjacentVertices.remove(i); + return true; + } + } + return false; + } + } + + /** + * this method removes an edge from the graph between two specified + * vertices + * + * @param from the data of the vertex the edge is from + * @param to the data of the vertex the edge is going to + * @return returns false if the edge doesn't exist, returns true if the edge + * exists and is removed + */ + public boolean removeEdge(E from, E to) { + Vertex fromV = null; + for (Vertex v : vertices) { + if (from.compareTo(v.data) == 0) { + fromV = v; + break; + } + } + if (fromV == null) { + return false; + } + return fromV.removeAdjacentVertex(to); + } + + /** + * this method adds an edge to the graph between two specified vertices + * + * @param from the data of the vertex the edge is from + * @param to the data of the vertex the edge is going to + * @return returns true if the edge did not exist, return false if it + * already did + */ + public boolean addEdge(E from, E to) { + Vertex fromV = null; + Vertex toV = null; + for (Vertex v : vertices) { + if (from.compareTo(v.data) == 0) { // see if from vertex already exists + fromV = v; + } else if (to.compareTo(v.data) == 0) { // see if to vertex already exists + toV = v; + } + if (fromV != null && toV != null) { + break; // both nodes exist so stop searching + } + } + if (fromV == null) { + fromV = new Vertex(from); + vertices.add(fromV); + } + if (toV == null) { + toV = new Vertex(to); + vertices.add(toV); + } + return fromV.addAdjacentVertex(toV); + } + + /** + * this gives a list of vertices in the graph and their adjacencies + * + * @return returns a string describing this graph + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (Vertex v : vertices) { + sb.append("Vertex: "); + sb.append(v.data); + sb.append("\n"); + sb.append("Adjacent vertices: "); + for (Vertex v2 : v.adjacentVertices) { + sb.append(v2.data); + sb.append(" "); + } + sb.append("\n"); + } + return sb.toString(); + } +} + +public final class Graphs { + private Graphs() { + } + + public static void main(String[] args) { + AdjacencyListGraph<Integer> graph = new AdjacencyListGraph<>(); + assert graph.addEdge(1, 2); + assert graph.addEdge(1, 5); + assert graph.addEdge(2, 5); + assert !graph.addEdge(1, 2); + assert graph.addEdge(2, 3); + assert graph.addEdge(3, 4); + assert graph.addEdge(4, 1); + assert !graph.addEdge(2, 3); + System.out.println(graph); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/HamiltonianCycle.java b/src/main/java/com/thealgorithms/datastructures/graphs/HamiltonianCycle.java new file mode 100644 index 000000000000..5c95850c4971 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/graphs/HamiltonianCycle.java @@ -0,0 +1,106 @@ +package com.thealgorithms.datastructures.graphs; + +import java.util.Arrays; + +/** + * Java program to find a Hamiltonian Cycle in a graph. + * A Hamiltonian Cycle is a cycle that visits every vertex exactly once + * and returns to the starting vertex. + * + * <p>For more details, see the + * <a href="https://en.wikipedia.org/wiki/Hamiltonian_path">Wikipedia article</a>. + * + * @author <a href="https://github.com/itsAkshayDubey">Akshay Dubey</a> + */ +public class HamiltonianCycle { + + private int vertex; + private int pathCount; + private int[] cycle; + private int[][] graph; + + /** + * Finds a Hamiltonian Cycle for the given graph. + * + * @param graph Adjacency matrix representing the graph G(V, E), where V is + * the set of vertices and E is the set of edges. + * @return An array representing the Hamiltonian cycle if found, otherwise an + * array filled with -1 indicating no Hamiltonian cycle exists. + */ + public int[] findHamiltonianCycle(int[][] graph) { + // Single vertex graph + if (graph.length == 1) { + return new int[] {0, 0}; + } + + this.vertex = graph.length; + this.cycle = new int[this.vertex + 1]; + + // Initialize the cycle array with -1 to represent unvisited vertices + Arrays.fill(this.cycle, -1); + + this.graph = graph; + this.cycle[0] = 0; + this.pathCount = 1; + if (!isPathFound(0)) { + Arrays.fill(this.cycle, -1); + } else { + this.cycle[this.cycle.length - 1] = this.cycle[0]; + } + + return cycle; + } + + /** + * Recursively searches for a Hamiltonian cycle from the given vertex. + * + * @param vertex The current vertex from which to explore paths. + * @return {@code true} if a Hamiltonian cycle is found, otherwise {@code false}. + */ + public boolean isPathFound(int vertex) { + boolean isLastVertexConnectedToStart = this.graph[vertex][0] == 1 && this.pathCount == this.vertex; + if (isLastVertexConnectedToStart) { + return true; + } + + // If all vertices are visited but the last vertex is not connected to the start + if (this.pathCount == this.vertex) { + return false; + } + + for (int v = 0; v < this.vertex; v++) { + if (this.graph[vertex][v] == 1) { // Check if there is an edge + this.cycle[this.pathCount++] = v; // Add the vertex to the cycle + this.graph[vertex][v] = 0; + this.graph[v][vertex] = 0; + + // Recursively attempt to complete the cycle + if (!isPresent(v)) { + return isPathFound(v); + } + + // Restore the edge if the path does not work + this.graph[vertex][v] = 1; + this.graph[v][vertex] = 1; + + this.cycle[--this.pathCount] = -1; + } + } + return false; + } + + /** + * Checks if a vertex is already part of the current Hamiltonian path. + * + * @param vertex The vertex to check. + * @return {@code true} if the vertex is already in the path, otherwise {@code false}. + */ + public boolean isPresent(int vertex) { + for (int i = 0; i < pathCount - 1; i++) { + if (cycle[i] == vertex) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/JohnsonsAlgorithm.java b/src/main/java/com/thealgorithms/datastructures/graphs/JohnsonsAlgorithm.java new file mode 100644 index 000000000000..351bd5b009e8 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/graphs/JohnsonsAlgorithm.java @@ -0,0 +1,200 @@ +package com.thealgorithms.datastructures.graphs; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * This class implements Johnson's algorithm for finding all-pairs shortest paths in a weighted, + * directed graph that may contain negative edge weights. + * + * Johnson's algorithm works by using the Bellman-Ford algorithm to compute a transformation of the + * input graph that removes all negative weights, allowing Dijkstra's algorithm to be used for + * efficient shortest path computations. + * + * Time Complexity: O(V^2 * log(V) + V*E) + * Space Complexity: O(V^2) + * + * Where V is the number of vertices and E is the number of edges in the graph. + * + * For more information, please visit {@link https://en.wikipedia.org/wiki/Johnson%27s_algorithm} + */ +public final class JohnsonsAlgorithm { + + private static final double INF = Double.POSITIVE_INFINITY; + + private JohnsonsAlgorithm() { + } + + /** + * Executes Johnson's algorithm on the given graph. + * Steps: + * 1. Add a new vertex to the graph and run Bellman-Ford to compute modified weights + * 2. t the graph using the modified weights + * 3. Run Dijkstra's algorithm for each vertex to compute the shortest paths + * The final result is a 2D array of shortest distances between all pairs of vertices. + * + * @param graph The input graph represented as an adjacency matrix. + * @return A 2D array representing the shortest distances between all pairs of vertices. + */ + public static double[][] johnsonAlgorithm(double[][] graph) { + int numVertices = graph.length; + double[][] edges = convertToEdgeList(graph); + + double[] modifiedWeights = bellmanFord(edges, numVertices); + + double[][] reweightedGraph = reweightGraph(graph, modifiedWeights); + + double[][] shortestDistances = new double[numVertices][numVertices]; + for (int source = 0; source < numVertices; source++) { + shortestDistances[source] = dijkstra(reweightedGraph, source, modifiedWeights); + } + + return shortestDistances; + } + + /** + * Converts the adjacency matrix representation of the graph to an edge list. + * + * @param graph The input graph as an adjacency matrix. + * @return An array of edges, where each edge is represented as [from, to, weight]. + */ + public static double[][] convertToEdgeList(double[][] graph) { + int numVertices = graph.length; + List<double[]> edgeList = new ArrayList<>(); + + for (int i = 0; i < numVertices; i++) { + for (int j = 0; j < numVertices; j++) { + if (i != j && !Double.isInfinite(graph[i][j])) { + // Only add edges that are not self-loops and have a finite weight + edgeList.add(new double[] {i, j, graph[i][j]}); + } + } + } + + return edgeList.toArray(new double[0][]); + } + + /** + * Implements the Bellman-Ford algorithm to compute the shortest paths from a new vertex + * to all other vertices. This is used to calculate the weight function h(v) for reweighting. + * + * @param edges The edge list of the graph. + * @param numVertices The number of vertices in the original graph. + * @return An array of modified weights for each vertex. + */ + private static double[] bellmanFord(double[][] edges, int numVertices) { + double[] dist = new double[numVertices + 1]; + Arrays.fill(dist, INF); + dist[numVertices] = 0; + + // Add edges from the new vertex to all original vertices + double[][] allEdges = Arrays.copyOf(edges, edges.length + numVertices); + for (int i = 0; i < numVertices; i++) { + allEdges[edges.length + i] = new double[] {numVertices, i, 0}; + } + + // Relax all edges V times + for (int i = 0; i < numVertices; i++) { + for (double[] edge : allEdges) { + int u = (int) edge[0]; + int v = (int) edge[1]; + double weight = edge[2]; + if (dist[u] != INF && dist[u] + weight < dist[v]) { + dist[v] = dist[u] + weight; + } + } + } + + // Check for negative weight cycles + for (double[] edge : allEdges) { + int u = (int) edge[0]; + int v = (int) edge[1]; + double weight = edge[2]; + if (dist[u] + weight < dist[v]) { + throw new IllegalArgumentException("Graph contains a negative weight cycle"); + } + } + + return Arrays.copyOf(dist, numVertices); + } + + /** + * Reweights the graph using the modified weights computed by Bellman-Ford. + * + * @param graph The original graph. + * @param modifiedWeights The modified weights from Bellman-Ford. + * @return The reweighted graph. + */ + public static double[][] reweightGraph(double[][] graph, double[] modifiedWeights) { + int numVertices = graph.length; + double[][] reweightedGraph = new double[numVertices][numVertices]; + + for (int i = 0; i < numVertices; i++) { + for (int j = 0; j < numVertices; j++) { + if (graph[i][j] != 0) { + // New weight = original weight + h(u) - h(v) + reweightedGraph[i][j] = graph[i][j] + modifiedWeights[i] - modifiedWeights[j]; + } + } + } + + return reweightedGraph; + } + + /** + * Implements Dijkstra's algorithm for finding shortest paths from a source vertex. + * + * @param reweightedGraph The reweighted graph to run Dijkstra's on. + * @param source The source vertex. + * @param modifiedWeights The modified weights from Bellman-Ford. + * @return An array of shortest distances from the source to all other vertices. + */ + public static double[] dijkstra(double[][] reweightedGraph, int source, double[] modifiedWeights) { + int numVertices = reweightedGraph.length; + double[] dist = new double[numVertices]; + boolean[] visited = new boolean[numVertices]; + Arrays.fill(dist, INF); + dist[source] = 0; + + for (int count = 0; count < numVertices - 1; count++) { + int u = minDistance(dist, visited); + visited[u] = true; + + for (int v = 0; v < numVertices; v++) { + if (!visited[v] && reweightedGraph[u][v] != 0 && dist[u] != INF && dist[u] + reweightedGraph[u][v] < dist[v]) { + dist[v] = dist[u] + reweightedGraph[u][v]; + } + } + } + + // Adjust distances back to the original graph weights + for (int i = 0; i < numVertices; i++) { + if (dist[i] != INF) { + dist[i] = dist[i] - modifiedWeights[source] + modifiedWeights[i]; + } + } + + return dist; + } + + /** + * Finds the vertex with the minimum distance value from the set of vertices + * not yet included in the shortest path tree. + * + * @param dist Array of distances. + * @param visited Array of visited vertices. + * @return The index of the vertex with minimum distance. + */ + public static int minDistance(double[] dist, boolean[] visited) { + double min = INF; + int minIndex = -1; + for (int v = 0; v < dist.length; v++) { + if (!visited[v] && dist[v] <= min) { + min = dist[v]; + minIndex = v; + } + } + return minIndex; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithm.java b/src/main/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithm.java new file mode 100644 index 000000000000..9a97bc3f4808 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithm.java @@ -0,0 +1,166 @@ +package com.thealgorithms.datastructures.graphs; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; +import java.util.Set; + +/** + * A class representing the adjacency list of a directed graph. The adjacency list + * maintains a mapping of vertices to their adjacent vertices. + * + * @param <E> the type of vertices, extending Comparable to ensure that vertices + * can be compared + */ +class AdjacencyList<E extends Comparable<E>> { + + Map<E, ArrayList<E>> adj; + + /** + * Constructor to initialize the adjacency list. + */ + AdjacencyList() { + adj = new LinkedHashMap<>(); + } + + /** + * Adds a directed edge from one vertex to another in the adjacency list. + * If the vertex does not exist, it will be added to the list. + * + * @param from the starting vertex of the directed edge + * @param to the destination vertex of the directed edge + */ + void addEdge(E from, E to) { + if (!adj.containsKey(from)) { + adj.put(from, new ArrayList<>()); + } + adj.get(from).add(to); + if (!adj.containsKey(to)) { + adj.put(to, new ArrayList<>()); + } + } + + /** + * Retrieves the list of adjacent vertices for a given vertex. + * + * @param v the vertex whose adjacent vertices are to be fetched + * @return an ArrayList of adjacent vertices for vertex v + */ + ArrayList<E> getAdjacents(E v) { + return adj.get(v); + } + + /** + * Retrieves the set of all vertices present in the graph. + * + * @return a set containing all vertices in the graph + */ + Set<E> getVertices() { + return adj.keySet(); + } +} + +/** + * A class that performs topological sorting on a directed graph using Kahn's algorithm. + * + * @param <E> the type of vertices, extending Comparable to ensure that vertices + * can be compared + */ +class TopologicalSort<E extends Comparable<E>> { + + AdjacencyList<E> graph; + Map<E, Integer> inDegree; + + /** + * Constructor to initialize the topological sorting class with a given graph. + * + * @param graph the directed graph represented as an adjacency list + */ + TopologicalSort(AdjacencyList<E> graph) { + this.graph = graph; + } + + /** + * Calculates the in-degree of all vertices in the graph. The in-degree is + * the number of edges directed into a vertex. + */ + void calculateInDegree() { + inDegree = new HashMap<>(); + for (E vertex : graph.getVertices()) { + inDegree.putIfAbsent(vertex, 0); + for (E adjacent : graph.getAdjacents(vertex)) { + inDegree.put(adjacent, inDegree.getOrDefault(adjacent, 0) + 1); + } + } + } + + /** + * Returns an ArrayList containing the vertices of the graph arranged in + * topological order. Topological sorting ensures that for any directed edge + * (u, v), vertex u appears before vertex v in the ordering. + * + * @return an ArrayList of vertices in topological order + * @throws IllegalStateException if the graph contains a cycle + */ + ArrayList<E> topSortOrder() { + calculateInDegree(); + Queue<E> q = new LinkedList<>(); + + for (var entry : inDegree.entrySet()) { + if (entry.getValue() == 0) { + q.add(entry.getKey()); + } + } + + ArrayList<E> answer = new ArrayList<>(); + int processedVertices = 0; + + while (!q.isEmpty()) { + E current = q.poll(); + answer.add(current); + processedVertices++; + + for (E adjacent : graph.getAdjacents(current)) { + inDegree.put(adjacent, inDegree.get(adjacent) - 1); + if (inDegree.get(adjacent) == 0) { + q.add(adjacent); + } + } + } + + if (processedVertices != graph.getVertices().size()) { + throw new IllegalStateException("Graph contains a cycle, topological sort not possible"); + } + + return answer; + } +} + +/** + * A driver class that sorts a given graph in topological order using Kahn's algorithm. + */ +public final class KahnsAlgorithm { + private KahnsAlgorithm() { + } + + public static void main(String[] args) { + // Graph definition and initialization + AdjacencyList<String> graph = new AdjacencyList<>(); + graph.addEdge("a", "b"); + graph.addEdge("c", "a"); + graph.addEdge("a", "d"); + graph.addEdge("b", "d"); + graph.addEdge("c", "u"); + graph.addEdge("u", "b"); + + TopologicalSort<String> topSort = new TopologicalSort<>(graph); + + // Printing the topological order + for (String s : topSort.topSortOrder()) { + System.out.print(s + " "); + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/Kosaraju.java b/src/main/java/com/thealgorithms/datastructures/graphs/Kosaraju.java new file mode 100644 index 000000000000..78a184f042b5 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/graphs/Kosaraju.java @@ -0,0 +1,169 @@ +package com.thealgorithms.datastructures.graphs; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * This class implements the Kosaraju Algorithm to find all the Strongly Connected Components (SCCs) + * of a directed graph. Kosaraju's algorithm runs in linear time and leverages the concept that + * the SCCs of a directed graph remain the same in its transpose (reverse) graph. + * + * <p> + * A strongly connected component (SCC) of a directed graph is a subgraph where every vertex + * is reachable from every other vertex in the subgraph. The Kosaraju algorithm is particularly + * efficient for finding SCCs because it performs two Depth First Search (DFS) passes on the + * graph and its transpose. + * </p> + * + * <p><strong>Algorithm:</strong></p> + * <ol> + * <li>Perform DFS on the original graph and push nodes to a stack in the order of their finishing time.</li> + * <li>Generate the transpose (reversed edges) of the original graph.</li> + * <li>Perform DFS on the transpose graph, using the stack from the first DFS. Each DFS run on the transpose graph gives a SCC.</li> + * </ol> + * + * <p><strong>Example Graph:</strong></p> + * <pre> + * 0 <--- 2 -------> 3 -------- > 4 ---- > 7 + * | ^ | ^ ^ + * | / | \ / + * | / | \ / + * v / v \ / + * 1 5 --> 6 + * </pre> + * + * <p><strong>SCCs in the example:</strong></p> + * <ul> + * <li>{0, 1, 2}</li> + * <li>{3}</li> + * <li>{4, 5, 6}</li> + * <li>{7}</li> + * </ul> + * + * <p>The order of nodes in an SCC does not matter because every node in an SCC is reachable from every other node within the same SCC.</p> + * + * <p><strong>Graph Transpose Example:</strong></p> + * <pre> + * 0 ---> 2 <------- 3 <------- 4 <------ 7 + * ^ / ^ \ / + * | / | \ / + * | / | \ / + * | v | v v + * 1 5 <--- 6 + * </pre> + * + * The SCCs of this transpose graph are the same as the original graph. + */ +public class Kosaraju { + + // Stack to sort edges by the lowest finish time (used in the first DFS) + private final Stack<Integer> stack = new Stack<>(); + + // Store each strongly connected component + private List<Integer> scc = new ArrayList<>(); + + // List of all SCCs + private final List<List<Integer>> sccsList = new ArrayList<>(); + + /** + * Main function to perform Kosaraju's Algorithm. + * Steps: + * 1. Sort nodes by the lowest finishing time + * 2. Create the transpose (reverse edges) of the original graph + * 3. Find SCCs by performing DFS on the transpose graph + * 4. Return the list of SCCs + * + * @param v the number of vertices in the graph + * @param list the adjacency list representing the directed graph + * @return a list of SCCs where each SCC is a list of vertices + */ + public List<List<Integer>> kosaraju(int v, List<List<Integer>> list) { + sortEdgesByLowestFinishTime(v, list); + List<List<Integer>> transposeGraph = createTransposeMatrix(v, list); + findStronglyConnectedComponents(v, transposeGraph); + return sccsList; + } + + /** + * Performs DFS on the original graph to sort nodes by their finishing times. + * @param v the number of vertices in the graph + * @param list the adjacency list representing the original graph + */ + private void sortEdgesByLowestFinishTime(int v, List<List<Integer>> list) { + int[] vis = new int[v]; + for (int i = 0; i < v; i++) { + if (vis[i] == 0) { + dfs(i, vis, list); + } + } + } + + /** + * Creates the transpose (reverse) of the original graph. + * @param v the number of vertices in the graph + * @param list the adjacency list representing the original graph + * @return the adjacency list representing the transposed graph + */ + private List<List<Integer>> createTransposeMatrix(int v, List<List<Integer>> list) { + List<List<Integer>> transposeGraph = new ArrayList<>(v); + for (int i = 0; i < v; i++) { + transposeGraph.add(new ArrayList<>()); + } + for (int i = 0; i < v; i++) { + for (Integer neigh : list.get(i)) { + transposeGraph.get(neigh).add(i); + } + } + return transposeGraph; + } + + /** + * Finds the strongly connected components (SCCs) by performing DFS on the transposed graph. + * @param v the number of vertices in the graph + * @param transposeGraph the adjacency list representing the transposed graph + */ + public void findStronglyConnectedComponents(int v, List<List<Integer>> transposeGraph) { + int[] vis = new int[v]; + while (!stack.isEmpty()) { + int node = stack.pop(); + if (vis[node] == 0) { + dfs2(node, vis, transposeGraph); + sccsList.add(scc); + scc = new ArrayList<>(); + } + } + } + + /** + * Performs DFS on the original graph and pushes nodes onto the stack in order of their finish time. + * @param node the current node being visited + * @param vis array to keep track of visited nodes + * @param list the adjacency list of the graph + */ + private void dfs(int node, int[] vis, List<List<Integer>> list) { + vis[node] = 1; + for (Integer neighbour : list.get(node)) { + if (vis[neighbour] == 0) { + dfs(neighbour, vis, list); + } + } + stack.push(node); + } + + /** + * Performs DFS on the transposed graph to find the strongly connected components. + * @param node the current node being visited + * @param vis array to keep track of visited nodes + * @param list the adjacency list of the transposed graph + */ + private void dfs2(int node, int[] vis, List<List<Integer>> list) { + vis[node] = 1; + for (Integer neighbour : list.get(node)) { + if (vis[neighbour] == 0) { + dfs2(neighbour, vis, list); + } + } + scc.add(node); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/Kruskal.java b/src/main/java/com/thealgorithms/datastructures/graphs/Kruskal.java new file mode 100644 index 000000000000..25c4548daa7a --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/graphs/Kruskal.java @@ -0,0 +1,92 @@ +package com.thealgorithms.datastructures.graphs; + +import java.util.Comparator; +import java.util.HashSet; +import java.util.PriorityQueue; + +/** + * The Kruskal class implements Kruskal's Algorithm to find the Minimum Spanning Tree (MST) + * of a connected, undirected graph. The algorithm constructs the MST by selecting edges + * with the least weight, ensuring no cycles are formed, and using union-find to track the + * connected components. + * + * <p><strong>Key Features:</strong></p> + * <ul> + * <li>The graph is represented using an adjacency list, where each node points to a set of edges.</li> + * <li>Each edge is processed in ascending order of weight using a priority queue.</li> + * <li>The algorithm stops when all nodes are connected or no more edges are available.</li> + * </ul> + * + * <p><strong>Time Complexity:</strong> O(E log V), where E is the number of edges and V is the number of vertices.</p> + */ +public class Kruskal { + + /** + * Represents an edge in the graph with a source, destination, and weight. + */ + static class Edge { + + int from; + int to; + int weight; + + Edge(int from, int to, int weight) { + this.from = from; + this.to = to; + this.weight = weight; + } + } + + /** + * Adds an edge to the graph. + * + * @param graph the adjacency list representing the graph + * @param from the source vertex of the edge + * @param to the destination vertex of the edge + * @param weight the weight of the edge + */ + static void addEdge(HashSet<Edge>[] graph, int from, int to, int weight) { + graph[from].add(new Edge(from, to, weight)); + } + + /** + * Kruskal's algorithm to find the Minimum Spanning Tree (MST) of a graph. + * + * @param graph the adjacency list representing the input graph + * @return the adjacency list representing the MST + */ + public HashSet<Edge>[] kruskal(HashSet<Edge>[] graph) { + int nodes = graph.length; + int[] captain = new int[nodes]; // Stores the "leader" of each node's connected component + HashSet<Integer>[] connectedGroups = new HashSet[nodes]; + HashSet<Edge>[] minGraph = new HashSet[nodes]; + PriorityQueue<Edge> edges = new PriorityQueue<>(Comparator.comparingInt(edge -> edge.weight)); + for (int i = 0; i < nodes; i++) { + minGraph[i] = new HashSet<>(); + connectedGroups[i] = new HashSet<>(); + connectedGroups[i].add(i); + captain[i] = i; + edges.addAll(graph[i]); + } + int connectedElements = 0; + while (connectedElements != nodes && !edges.isEmpty()) { + Edge edge = edges.poll(); + + // Avoid forming cycles by checking if the nodes belong to different connected components + if (!connectedGroups[captain[edge.from]].contains(edge.to) && !connectedGroups[captain[edge.to]].contains(edge.from)) { + // Merge the two sets of nodes connected by the edge + connectedGroups[captain[edge.from]].addAll(connectedGroups[captain[edge.to]]); + + // Update the captain for each merged node + connectedGroups[captain[edge.from]].forEach(i -> captain[i] = captain[edge.from]); + + // Add the edge to the resulting MST graph + addEdge(minGraph, edge.from, edge.to, edge.weight); + + // Update the count of connected nodes + connectedElements = connectedGroups[captain[edge.from]].size(); + } + } + return minGraph; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/MatrixGraphs.java b/src/main/java/com/thealgorithms/datastructures/graphs/MatrixGraphs.java new file mode 100644 index 000000000000..c1d47df457da --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/graphs/MatrixGraphs.java @@ -0,0 +1,345 @@ +package com.thealgorithms.datastructures.graphs; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + * Implementation of a graph in a matrix form Also known as an adjacency matrix + * representation [Adjacency matrix - + * Wikipedia](https://en.wikipedia.org/wiki/Adjacency_matrix) + * + * @author Unknown + */ +public final class MatrixGraphs { + private MatrixGraphs() { + } + + public static void main(String[] args) { + AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(10); + graph.addEdge(1, 2); + graph.addEdge(1, 5); + graph.addEdge(2, 5); + graph.addEdge(1, 2); + graph.addEdge(2, 3); + graph.addEdge(3, 4); + graph.addEdge(4, 1); + graph.addEdge(2, 3); + graph.addEdge(3, 9); + graph.addEdge(9, 1); + graph.addEdge(9, 8); + graph.addEdge(1, 8); + graph.addEdge(5, 6); + System.out.println("The graph matrix:"); + System.out.println(graph); + System.out.println("Depth first order beginning at node '1':"); + System.out.println(graph.depthFirstOrder(1)); + System.out.println("Breadth first order beginning at node '1':"); + System.out.println(graph.breadthFirstOrder(1)); + } +} + +/** + * AdjacencyMatrixGraph Implementation + */ +class AdjacencyMatrixGraph { + + /** + * The number of vertices in the graph + */ + private int vertexCount; + + /** + * The number of edges in the graph + */ + private int edgeCount; + + /** + * The adjacency matrix for the graph + */ + private int[][] adjMatrix; + + /** + * Static variables to define whether or not an edge exists in the adjacency + * matrix + */ + static final int EDGE_EXIST = 1; + static final int EDGE_NONE = 0; + + /** + * Constructor + */ + AdjacencyMatrixGraph(int givenNumberOfVertices) { + this.setNumberOfVertices(givenNumberOfVertices); + this.setNumberOfEdges(0); + this.setAdjacency(new int[givenNumberOfVertices][givenNumberOfVertices]); + for (int i = 0; i < givenNumberOfVertices; i++) { + for (int j = 0; j < givenNumberOfVertices; j++) { + this.adjacency()[i][j] = AdjacencyMatrixGraph.EDGE_NONE; + } + } + } + + /** + * Updates the number of vertices in the graph + * + * @param newNumberOfVertices the new number of vertices + */ + private void setNumberOfVertices(int newNumberOfVertices) { + this.vertexCount = newNumberOfVertices; + } + + /** + * Getter for `this.vertexCount` + * + * @return the number of vertices in the graph + */ + public int numberOfVertices() { + return this.vertexCount; + } + + /** + * Updates the number of edges in the graph + * + * @param newNumberOfEdges the new number of edges + * + */ + private void setNumberOfEdges(int newNumberOfEdges) { + this.edgeCount = newNumberOfEdges; + } + + /** + * Getter for `this.edgeCount` + * + * @return the number of edges + */ + public int numberOfEdges() { + return this.edgeCount; + } + + /** + * Sets a new matrix as the adjacency matrix + * + * @param newAdjacency the new adjaceny matrix + */ + private void setAdjacency(int[][] newAdjacency) { + this.adjMatrix = newAdjacency; + } + + /** + * Getter for the adjacency matrix + * + * @return the adjacency matrix + */ + private int[][] adjacency() { + return this.adjMatrix; + } + + /** + * Checks if two vertices are connected by an edge + * + * @param from the parent vertex to check for adjacency + * @param to the child vertex to check for adjacency + * @return whether or not the vertices are adjancent + */ + private boolean adjacencyOfEdgeDoesExist(int from, int to) { + return (this.adjacency()[from][to] != AdjacencyMatrixGraph.EDGE_NONE); + } + + /** + * Checks if a particular vertex exists in a graph + * + * @param aVertex the vertex to check for existence + * @return whether or not the vertex exists + */ + public boolean vertexDoesExist(int aVertex) { + return aVertex >= 0 && aVertex < this.numberOfVertices(); + } + + /** + * Checks if two vertices are connected by an edge + * + * @param from the parent vertex to check for adjacency + * @param to the child vertex to check for adjacency + * @return whether or not the vertices are adjancent + */ + public boolean edgeDoesExist(int from, int to) { + if (this.vertexDoesExist(from) && this.vertexDoesExist(to)) { + return (this.adjacencyOfEdgeDoesExist(from, to)); + } + + return false; + } + + /** + * This method adds an edge to the graph between two specified vertices + * + * @param from the data of the vertex the edge is from + * @param to the data of the vertex the edge is going to + * @return returns true if the edge did not exist, return false if it + * already did + */ + public boolean addEdge(int from, int to) { + if (this.vertexDoesExist(from) && this.vertexDoesExist(to)) { + if (!this.adjacencyOfEdgeDoesExist(from, to)) { + this.adjacency()[from][to] = AdjacencyMatrixGraph.EDGE_EXIST; + this.adjacency()[to][from] = AdjacencyMatrixGraph.EDGE_EXIST; + this.setNumberOfEdges(this.numberOfEdges() + 1); + return true; + } + } + + return false; + } + + /** + * this method removes an edge from the graph between two specified vertices + * + * @param from the data of the vertex the edge is from + * @param to the data of the vertex the edge is going to + * @return returns false if the edge doesn't exist, returns true if the edge + * exists and is removed + */ + public boolean removeEdge(int from, int to) { + if (this.vertexDoesExist(from) && this.vertexDoesExist(to)) { + if (this.adjacencyOfEdgeDoesExist(from, to)) { + this.adjacency()[from][to] = AdjacencyMatrixGraph.EDGE_NONE; + this.adjacency()[to][from] = AdjacencyMatrixGraph.EDGE_NONE; + this.setNumberOfEdges(this.numberOfEdges() - 1); + return true; + } + } + return false; + } + + /** + * This method returns a list of the vertices in a depth first order + * beginning with the specified vertex + * + * @param startVertex the vertex to begin the traversal + * @return the list of the ordered vertices + */ + public List<Integer> depthFirstOrder(int startVertex) { + // If the startVertex is invalid, return an empty list + if (startVertex >= vertexCount || startVertex < 0) { + return new ArrayList<>(); + } + + // Create an array to track the visited vertices + boolean[] visited = new boolean[vertexCount]; + + // Create a list to keep track of the order of our traversal + ArrayList<Integer> orderList = new ArrayList<>(); + + // Perform our DFS algorithm + depthFirstOrder(startVertex, visited, orderList); + + return orderList; + } + + /** + * Helper method for public depthFirstOrder(int) that will perform a depth + * first traversal recursively on the graph + * + * @param currentVertex the currently exploring vertex + * @param visited the array of values denoting whether or not that vertex + * has been visited + * @param orderList the list to add vertices to as they are visited + */ + private void depthFirstOrder(int currentVertex, boolean[] visited, List<Integer> orderList) { + // If this vertex has already been visited, do nothing and return + if (visited[currentVertex]) { + return; + } + + // Visit the currentVertex by marking it as visited and adding it + // to the orderList + visited[currentVertex] = true; + orderList.add(currentVertex); + + // Get the adjacency array for this vertex + int[] adjacent = adjMatrix[currentVertex]; + for (int i = 0; i < adjacent.length; i++) { // we are considering exploring, recurse on it // If an edge exists between the + // currentVertex and the vertex + if (adjacent[i] == AdjacencyMatrixGraph.EDGE_EXIST) { + depthFirstOrder(i, visited, orderList); + } + } + } + + /** + * This method returns a list of the vertices in a breadth first order + * beginning with the specified vertex + * + * @param startVertex the vertext to begin the traversal + * @return the list of the ordered vertices + */ + public List<Integer> breadthFirstOrder(int startVertex) { + // If the specified startVertex is invalid, return an empty list + if (startVertex >= vertexCount || startVertex < 0) { + return new ArrayList<>(); + } + + // Create an array to keep track of the visited vertices + boolean[] visited = new boolean[vertexCount]; + + // Create a list to keep track of the ordered vertices + ArrayList<Integer> orderList = new ArrayList<>(); + + // Create a queue for our BFS algorithm and add the startVertex + // to the queue + Queue<Integer> queue = new LinkedList<>(); + queue.add(startVertex); + + // Continue until the queue is empty + while (!queue.isEmpty()) { + // Remove the first vertex in the queue + int currentVertex = queue.poll(); + + // If we've visited this vertex, skip it + if (visited[currentVertex]) { + continue; + } + + // We now visit this vertex by adding it to the orderList and + // marking it as visited + orderList.add(currentVertex); + visited[currentVertex] = true; + + // Get the adjacency array for the currentVertex and + // check each node + int[] adjacent = adjMatrix[currentVertex]; + for (int vertex = 0; vertex < adjacent.length; vertex++) { // vertex we are considering exploring, we add it to the queue // If an + // edge exists between the current vertex and the + if (adjacent[vertex] == AdjacencyMatrixGraph.EDGE_EXIST) { + queue.add(vertex); + } + } + } + + return orderList; + } + + /** + * this gives a list of vertices in the graph and their adjacencies + * + * @return returns a string describing this graph + */ + public String toString() { + StringBuilder s = new StringBuilder(" "); + for (int i = 0; i < this.numberOfVertices(); i++) { + s.append(i).append(" "); + } + s.append(" \n"); + + for (int i = 0; i < this.numberOfVertices(); i++) { + s.append(i).append(" : "); + for (int j = 0; j < this.numberOfVertices(); j++) { + s.append(this.adjMatrix[i][j]).append(" "); + } + s.append("\n"); + } + return s.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/PrimMST.java b/src/main/java/com/thealgorithms/datastructures/graphs/PrimMST.java new file mode 100644 index 000000000000..8017c18ce6ac --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/graphs/PrimMST.java @@ -0,0 +1,61 @@ +package com.thealgorithms.datastructures.graphs; + +/** + * A Java program for Prim's Minimum Spanning Tree (MST) algorithm. + * Adjacency matrix representation of the graph. + */ +public class PrimMST { + + // Number of vertices in the graph + private static final int V = 5; + + // A utility function to find the vertex with the minimum key + // value, from the set of vertices not yet included in the MST + int minKey(int[] key, Boolean[] mstSet) { + int min = Integer.MAX_VALUE; + int minIndex = -1; + + for (int v = 0; v < V; v++) { + if (!mstSet[v] && key[v] < min) { + min = key[v]; + minIndex = v; + } + } + + return minIndex; + } + + // Function to construct MST for a graph using adjacency matrix representation + public int[] primMST(int[][] graph) { + int[] parent = new int[V]; // Array to store constructed MST + int[] key = new int[V]; // Key values to pick minimum weight edge + Boolean[] mstSet = new Boolean[V]; // Vertices not yet included in MST + + // Initialize all keys as INFINITE and mstSet[] as false + for (int i = 0; i < V; i++) { + key[i] = Integer.MAX_VALUE; + mstSet[i] = Boolean.FALSE; + } + + // Always include the first vertex in MST + key[0] = 0; // Make key 0 to pick the first vertex + parent[0] = -1; // First node is always root of MST + + // The MST will have V vertices + for (int count = 0; count < V - 1; count++) { + // Pick the minimum key vertex not yet included in MST + int u = minKey(key, mstSet); + mstSet[u] = Boolean.TRUE; + + // Update key value and parent index of adjacent vertices of the picked vertex + for (int v = 0; v < V; v++) { + if (graph[u][v] != 0 && !mstSet[v] && graph[u][v] < key[v]) { + parent[v] = u; + key[v] = graph[u][v]; + } + } + } + + return parent; // Return the MST parent array + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/README.md b/src/main/java/com/thealgorithms/datastructures/graphs/README.md new file mode 100644 index 000000000000..4798e372667b --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/graphs/README.md @@ -0,0 +1,126 @@ +## Graphs +Graph is a useful data structure for representing most of the real-world problems involving a set of users/candidates/nodes and their relations. A graph consists of two parameters: + +``` +V = a set of vertices +E = a set of edges +``` + +Each edge in `E` connects any two vertices from `V`. Based on the type of edge, graphs can be of two types: + +1. **Directed**: The edges are directed in nature, which means that when there is an edge from node `A` to `B`, it does not imply that there is an edge from `B` to `A`. An example of a directed edge graph is the **follow** feature of social media. If you follow a celebrity, it doesn't imply that they follow you. + +2. **Undirected**: The edges don't have any direction. So if `A` and `B` are connected, we can assume that there is an edge from both `A` to `B` and `B` to `A`. For example, in a social media graph, if two persons are friends, it implies that both are friends with each other. + +### Components of a Graph + +**Vertices:** Vertices are the fundamental units of the graph. Sometimes, vertices are also known as vertex or nodes. Every node/vertex can be labeled or unlabelled. + +**Edges:** Edges are used to connect two nodes of the graph. They can be an ordered pair of nodes in a directed graph. Edges can connect any two nodes in any possible way. There are no rules. Sometimes, edges are also known as arcs. Every edge can be labeled/unlabeled. + +Graphs are used to solve many real-life problems. Graphs are used to represent networks. The networks may include paths in a city, telephone network, or circuit network. Graphs are also used in social networks like LinkedIn, Facebook. For example, on Facebook, each person is represented with a vertex (or node). Each node is a structure and contains information like person id, name, gender, locale, etc. + +### Graph Representation + +Graph can be represented in the following ways: + +**Set Representation:** Set representation of a graph involves two sets: Set of vertices V = {V1, V2, V3, V4} and set of edges E = {{V1, V2}, {V2, V3}, {V3, V4}, {V4, V1}}. This representation is efficient for memory but does not allow parallel edges. + +**Sequential Representation:** This representation of a graph can be represented by means of matrices: Adjacency Matrix, Incidence matrix, and Path matrix. + +**Adjacency Matrix:** This matrix includes information about the adjacent nodes. Here, aij = 1 if there is an edge from Vi to Vj; otherwise, it's 0. It is a matrix of order V×V. + +**Incidence Matrix:** This matrix includes information about the incidence of edges on the nodes. Here, aij = 1 if the jth edge Ej is incident on the ith vertex Vi; otherwise, it's 0. It is a matrix of order V×E. + +**Path Matrix:** This matrix includes information about the simple path between two vertices. Here, Pij = 1 if there is a path from Vi to Vj; otherwise, it's 0. It is also called the reachability matrix of graph G. + +**Linked Representation:** This representation gives information about the nodes to which a specific node is connected, i.e., adjacency lists. This representation gives the adjacency lists of the vertices with the help of arrays and linked lists. In the adjacency lists, the vertices connected to the specific vertex are arranged in the form of lists that are connected to that vertex. + +### Real-Time Applications of Graph + +Graphs are used to represent the flow of control in computers. +Graphs are used in social networking sites where users act as nodes, and connections between them act as edges. +In an operating system, graphs are used as resource allocation graphs. +Graphs are used in Google Maps to find the shortest route. +Graphs are also used in the airline system for effective route optimization. +In state transition diagrams, the graph is used to represent states and their transitions. +In transportation, graphs are used to find the shortest path. +In circuits, graphs can be used to represent circuit points as nodes and wires as edges. +Graphs are used in solving puzzles with only one solution, such as mazes. +Graphs are used in computer networks for Peer to Peer (P2P) applications. +Graphs, basically in the form of DAG (Directed Acyclic Graph), are used as an alternative to blockchain for cryptocurrency. For example, cryptocurrencies like IOTA and Nano are mainly based on DAG. + +### Advantages of Graph + +Using graphs, we can easily find the shortest path, neighbors of the nodes, and many more. +Graphs are used to implement algorithms like DFS and BFS. +They are used to find minimum spanning trees, which have many practical applications. +Graphs help in organizing data. +Because of their non-linear structure, graphs help in understanding complex problems and their visualization. + +### Disadvantages of Graph + +Graphs use lots of pointers, which can be complex to handle. +They can have large memory complexity. +If the graph is represented with an adjacency matrix, then it does not allow parallel edges, and multiplication of the graph is also difficult. + +### Representation + +1. **Adjacency Lists**: Each node is represented as an entry, and all the edges are represented as a list emerging from the corresponding node. So, if vertex 1 has edges to 2, 3, and 6, the list corresponding to 1 will have 2, 3, and 6 as entries. Consider the following graph: + +``` +0: 1-->2-->3 +1: 0-->2 +2: 0-->1 +3: 0-->4 +4: 3 +``` + +It means there are edges from 0 to 1, 2, and 3; from 1 to 0 and 2, and so on. + +2. **Adjacency Matrix**: The graph is represented as a matrix of size |V| x |V|, and an entry 1 in cell (i, j) implies that there is an edge from i to j. 0 represents no edge. The matrix for the above graph: + +``` + 0 1 2 3 4 + +0 0 1 1 1 0 +1 1 0 1 0 0 +2 1 1 0 0 0 +3 1 0 0 0 1 +4 0 0 0 1 0 + +###Graph Terminologies + +Degree of a vertex: Number of edges that are incident at a vertex. +Weighted graph: A graph that has weights assigned for each of the edges (used in cases such as shortest path problems). +Connected components: A set of vertices that can reach others from it but not to those outside this connected component. +Cycle: A path that begins and ends at the same vertex. +Bipartite Graph: A graph whose vertices can be partitioned into two disjoint sets, with every edge connecting a vertex in one set to a vertex in the other set. + +###Graph Algorithms + +Breadth-First Search: It explores neighbors in layer after layer and applies on shortest path problems for unweighted graphs. +Depth-First Search (DFS): It continues moving up as far along each branch as possible before backtracking. DFS is typically used for traversing all nodes and testing connectivity. +Dijkstra's Algorithm: This algorithm finds the shortest path from a single starting vertex to all other vertices in a weighted graph. +Prim's and Kruskal's Algorithm: To find the minimum spanning tree. +Bellman-Ford Algorithm: This algorithm solves shortest path problems even when there are negative weights. +Graph Types +Multigraphs: Graphs with more edges between the same set of vertices. +Complete Graphs: A graph in which there is a unique edge between each pair of vertices. +Planar Graphs: A graph that can be drawn in a plane such that no two edges cross. + +###Graph Algorithm Applications + +Google Maps (Dijkstra's Algorithm): How maps apps find shortest routes. +Job Scheduling: Topological Sort A real application of DAG (Directed Acyclic Graph) to manage the dependency of jobs between tasks. +Web Crawling: How to use BFS for web crawlers to index pages in search engines. +Big-O Complexity of Graph Operations +Adjacency List vs Adjacency Matrix : Provide comparison tables of time complexity for operations such as addition of an edge, checking if an edge exists, etc. +BFS and DFS Complexity : Describe their computational cost + +###Common Graph Problems + +Graph Coloring +Finding Bridges and Articulation Points +Finding Strongly Connected Components +Maximum Flow (Ford-Fulkerson algorithm) diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/TarjansAlgorithm.java b/src/main/java/com/thealgorithms/datastructures/graphs/TarjansAlgorithm.java new file mode 100644 index 000000000000..91974ba13319 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/graphs/TarjansAlgorithm.java @@ -0,0 +1,143 @@ +package com.thealgorithms.datastructures.graphs; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * Java program that implements Tarjan's Algorithm to find Strongly Connected Components (SCCs) in a directed graph. + * + * <p> + * Tarjan's algorithm is a linear time algorithm (O(V + E)) that identifies the SCCs of a directed graph. + * An SCC is a maximal subgraph where every vertex is reachable from every other vertex within the subgraph. + * + * <h3>Algorithm Overview:</h3> + * <ul> + * <li>DFS Search: A depth-first search (DFS) is performed on the graph to generate a DFS tree.</li> + * <li>Identification of SCCs: SCCs correspond to subtrees within this DFS tree.</li> + * <li>Low-Link Values: For each node, a low-link value is maintained, which indicates the earliest visited + * vertex (the one with the minimum insertion time) that can be reached from that subtree.</li> + * <li>Stack Usage: Nodes are stored in a stack during DFS. When an SCC is identified, nodes are popped from + * the stack until the head of the SCC is reached.</li> + * </ul> + * + * <p> + * Example of a directed graph: + * <pre> + * 0 --------> 1 -------> 3 --------> 4 + * ^ / + * | / + * | / + * | / + * | / + * | / + * | / + * | / + * | / + * | / + * V + * 2 + * </pre> + * + * <p> + * For the above graph, the SCC list is as follows: + * <ul> + * <li>1, 2, 0</li> + * <li>3</li> + * <li>4</li> + * </ul> + * The order of nodes in an SCC does not matter as they form cycles. + * + * <h3>Comparison with Kosaraju's Algorithm:</h3> + * <p> + * Kosaraju's algorithm also identifies SCCs but does so using two DFS traversals. + * In contrast, Tarjan's algorithm achieves this in a single DFS traversal, leading to improved performance + * in terms of constant factors. + * </p> + */ +public class TarjansAlgorithm { + + // Timer for tracking low time and insertion time + private int time; + + // List to store all strongly connected components + private final List<List<Integer>> sccList = new ArrayList<>(); + + /** + * Finds and returns the strongly connected components (SCCs) of the directed graph. + * + * @param v the number of vertices in the graph + * @param graph the adjacency list representation of the graph + * @return a list of lists, where each inner list represents a strongly connected component + */ + public List<List<Integer>> stronglyConnectedComponents(int v, List<List<Integer>> graph) { + // Initialize arrays for insertion time and low-link values + int[] lowTime = new int[v]; + int[] insertionTime = new int[v]; + for (int i = 0; i < v; i++) { + insertionTime[i] = -1; + lowTime[i] = -1; + } + + // Track if vertices are in the stack + boolean[] isInStack = new boolean[v]; + + // Stack to hold nodes during DFS + Stack<Integer> st = new Stack<>(); + + for (int i = 0; i < v; i++) { + if (insertionTime[i] == -1) { + stronglyConnCompsUtil(i, lowTime, insertionTime, isInStack, st, graph); + } + } + + return sccList; + } + + /** + * A utility function to perform DFS and find SCCs. + * + * @param u the current vertex being visited + * @param lowTime array to keep track of the low-link values + * @param insertionTime array to keep track of the insertion times + * @param isInStack boolean array indicating if a vertex is in the stack + * @param st the stack used for DFS + * @param graph the adjacency list representation of the graph + */ + private void stronglyConnCompsUtil(int u, int[] lowTime, int[] insertionTime, boolean[] isInStack, Stack<Integer> st, List<List<Integer>> graph) { + // Set insertion time and low-link value + insertionTime[u] = time; + lowTime[u] = time; + time++; + + // Push current node onto the stack + isInStack[u] = true; + st.push(u); + + // Explore adjacent vertices + for (Integer vertex : graph.get(u)) { + if (insertionTime[vertex] == -1) { + stronglyConnCompsUtil(vertex, lowTime, insertionTime, isInStack, st, graph); + // Update low-link value + lowTime[u] = Math.min(lowTime[u], lowTime[vertex]); + } else if (isInStack[vertex]) { + // Vertex is in the stack; update low-link value + lowTime[u] = Math.min(lowTime[u], insertionTime[vertex]); + } + } + + // Check if the current vertex is the root of an SCC + if (lowTime[u] == insertionTime[u]) { + int w = -1; + List<Integer> scc = new ArrayList<>(); + + // Pop vertices from the stack until the root is found + while (w != u) { + w = st.pop(); + scc.add(w); + isInStack[w] = false; + } + sccList.add(scc); + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/UndirectedAdjacencyListGraph.java b/src/main/java/com/thealgorithms/datastructures/graphs/UndirectedAdjacencyListGraph.java new file mode 100644 index 000000000000..8aafc1ef3368 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/graphs/UndirectedAdjacencyListGraph.java @@ -0,0 +1,69 @@ +package com.thealgorithms.datastructures.graphs; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; + +public class UndirectedAdjacencyListGraph { + private ArrayList<HashMap<Integer, Integer>> adjacencyList = new ArrayList<>(); + + /** + * Adds a new node to the graph by adding an empty HashMap for its neighbors. + * @return the index of the newly added node in the adjacency list + */ + public int addNode() { + adjacencyList.add(new HashMap<>()); + return adjacencyList.size() - 1; + } + + /** + * Adds an undirected edge between the origin node (@orig) and the destination node (@dest) with the specified weight. + * If the edge already exists, no changes are made. + * @param orig the index of the origin node + * @param dest the index of the destination node + * @param weight the weight of the edge between @orig and @dest + * @return true if the edge was successfully added, false if the edge already exists or if any node index is invalid + */ + public boolean addEdge(int orig, int dest, int weight) { + int numNodes = adjacencyList.size(); + if (orig >= numNodes || dest >= numNodes || orig < 0 || dest < 0) { + return false; + } + + if (adjacencyList.get(orig).containsKey(dest)) { + return false; + } + + adjacencyList.get(orig).put(dest, weight); + adjacencyList.get(dest).put(orig, weight); + return true; + } + + /** + * Returns the set of all adjacent nodes (neighbors) for the given node. + * @param node the index of the node whose neighbors are to be retrieved + * @return a HashSet containing the indices of all neighboring nodes + */ + public HashSet<Integer> getNeighbors(int node) { + return new HashSet<>(adjacencyList.get(node).keySet()); + } + + /** + * Returns the weight of the edge between the origin node (@orig) and the destination node (@dest). + * If no edge exists, returns null. + * @param orig the index of the origin node + * @param dest the index of the destination node + * @return the weight of the edge between @orig and @dest, or null if no edge exists + */ + public Integer getEdgeWeight(int orig, int dest) { + return adjacencyList.get(orig).getOrDefault(dest, null); + } + + /** + * Returns the number of nodes currently in the graph. + * @return the number of nodes in the graph + */ + public int size() { + return adjacencyList.size(); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/WelshPowell.java b/src/main/java/com/thealgorithms/datastructures/graphs/WelshPowell.java new file mode 100644 index 000000000000..26ca97736fe9 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/graphs/WelshPowell.java @@ -0,0 +1,211 @@ +package com.thealgorithms.datastructures.graphs; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashSet; +import java.util.stream.IntStream; + +/** + * The Welsh-Powell algorithm is a graph coloring algorithm that aims to color a graph + * using the minimum number of colors such that no two adjacent vertices share the same color. + * + * <p> + * The algorithm works by: + * <ol> + * <li>Sorting the vertices in descending order based on their degrees (number of edges connected).</li> + * <li>Iterating through each vertex and assigning it the smallest available color that has not been used by its adjacent vertices.</li> + * <li>Coloring adjacent vertices with the same color is avoided.</li> + * </ol> + * </p> + * + * <p> + * For more information, see <a href="https://en.wikipedia.org/wiki/Graph_coloring">Graph Coloring</a>. + * </p> + */ +public final class WelshPowell { + private static final int BLANK_COLOR = -1; // Constant representing an uncolored state + + private WelshPowell() { + } + + /** + * Represents a graph using an adjacency list. + */ + static final class Graph { + private final HashSet<Integer>[] adjacencyLists; + + /** + * Initializes a graph with a specified number of vertices. + * + * @param vertices the number of vertices in the graph + * @throws IllegalArgumentException if the number of vertices is negative + */ + private Graph(int vertices) { + if (vertices < 0) { + throw new IllegalArgumentException("Number of vertices cannot be negative"); + } + + adjacencyLists = new HashSet[vertices]; + Arrays.setAll(adjacencyLists, i -> new HashSet<>()); + } + + /** + * Adds an edge between two vertices in the graph. + * + * @param nodeA one end of the edge + * @param nodeB the other end of the edge + * @throws IllegalArgumentException if the vertices are out of bounds or if a self-loop is attempted + */ + private void addEdge(int nodeA, int nodeB) { + validateVertex(nodeA); + validateVertex(nodeB); + if (nodeA == nodeB) { + throw new IllegalArgumentException("Self-loops are not allowed"); + } + adjacencyLists[nodeA].add(nodeB); + adjacencyLists[nodeB].add(nodeA); + } + + /** + * Validates that the vertex index is within the bounds of the graph. + * + * @param vertex the index of the vertex to validate + * @throws IllegalArgumentException if the vertex is out of bounds + */ + private void validateVertex(int vertex) { + if (vertex < 0 || vertex >= getNumVertices()) { + throw new IllegalArgumentException("Vertex " + vertex + " is out of bounds"); + } + } + + /** + * Returns the adjacency list for a specific vertex. + * + * @param vertex the index of the vertex + * @return the set of adjacent vertices + */ + HashSet<Integer> getAdjacencyList(int vertex) { + return adjacencyLists[vertex]; + } + + /** + * Returns the number of vertices in the graph. + * + * @return the number of vertices + */ + int getNumVertices() { + return adjacencyLists.length; + } + } + + /** + * Creates a graph with the specified number of vertices and edges. + * + * @param numberOfVertices the total number of vertices + * @param listOfEdges a 2D array representing edges where each inner array contains two vertex indices + * @return a Graph object representing the created graph + * @throws IllegalArgumentException if the edge array is invalid or vertices are out of bounds + */ + public static Graph makeGraph(int numberOfVertices, int[][] listOfEdges) { + Graph graph = new Graph(numberOfVertices); + for (int[] edge : listOfEdges) { + if (edge.length != 2) { + throw new IllegalArgumentException("Edge array must have exactly two elements"); + } + graph.addEdge(edge[0], edge[1]); + } + return graph; + } + + /** + * Finds the coloring of the given graph using the Welsh-Powell algorithm. + * + * @param graph the input graph to color + * @return an array of integers where each index represents a vertex and the value represents the color assigned + */ + public static int[] findColoring(Graph graph) { + int[] colors = initializeColors(graph.getNumVertices()); + Integer[] sortedVertices = getSortedNodes(graph); + for (int vertex : sortedVertices) { + if (isBlank(colors[vertex])) { + boolean[] usedColors = computeUsedColors(graph, vertex, colors); + final var newColor = firstUnusedColor(usedColors); + colors[vertex] = newColor; + Arrays.stream(sortedVertices).forEach(otherVertex -> { + if (isBlank(colors[otherVertex]) && !isAdjacentToColored(graph, otherVertex, colors)) { + colors[otherVertex] = newColor; + } + }); + } + } + return colors; + } + + /** + * Helper method to check if a color is unassigned + * + * @param color the color to check + * @return {@code true} if the color is unassigned, {@code false} otherwise + */ + private static boolean isBlank(int color) { + return color == BLANK_COLOR; + } + + /** + * Checks if a vertex has adjacent colored vertices + * + * @param graph the input graph + * @param vertex the vertex to check + * @param colors the array of colors assigned to the vertices + * @return {@code true} if the vertex has adjacent colored vertices, {@code false} otherwise + */ + private static boolean isAdjacentToColored(Graph graph, int vertex, int[] colors) { + return graph.getAdjacencyList(vertex).stream().anyMatch(otherVertex -> !isBlank(colors[otherVertex])); + } + + /** + * Initializes the colors array with blank color + * + * @param numberOfVertices the number of vertices in the graph + * @return an array of integers representing the colors assigned to the vertices + */ + private static int[] initializeColors(int numberOfVertices) { + int[] colors = new int[numberOfVertices]; + Arrays.fill(colors, BLANK_COLOR); + return colors; + } + + /** + * Sorts the vertices by their degree in descending order + * + * @param graph the input graph + * @return an array of integers representing the vertices sorted by degree + */ + private static Integer[] getSortedNodes(final Graph graph) { + return IntStream.range(0, graph.getNumVertices()).boxed().sorted(Comparator.comparingInt(v -> - graph.getAdjacencyList(v).size())).toArray(Integer[] ::new); + } + + /** + * Computes the colors already used by the adjacent vertices + * + * @param graph the input graph + * @param vertex the vertex to check + * @param colors the array of colors assigned to the vertices + * @return an array of booleans representing the colors used by the adjacent vertices + */ + private static boolean[] computeUsedColors(final Graph graph, final int vertex, final int[] colors) { + boolean[] usedColors = new boolean[graph.getNumVertices()]; + graph.getAdjacencyList(vertex).stream().map(neighbor -> colors[neighbor]).filter(color -> !isBlank(color)).forEach(color -> usedColors[color] = true); + return usedColors; + } + + /** + * Finds the first unused color + * + * @param usedColors the array of colors used by the adjacent vertices + * @return the first unused color + */ + private static int firstUnusedColor(boolean[] usedColors) { + return IntStream.range(0, usedColors.length).filter(color -> !usedColors[color]).findFirst().getAsInt(); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/hashmap/Readme.md b/src/main/java/com/thealgorithms/datastructures/hashmap/Readme.md new file mode 100644 index 000000000000..252b06ea59b0 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/hashmap/Readme.md @@ -0,0 +1,65 @@ +# HASHMAP DATA STRUCTURE + +A hash map organizes data so you can quickly look up values for a given key. + +## Strengths: +- **Fast lookups**: Lookups take O(1) time on average. +- **Flexible keys**: Most data types can be used for keys, as long as they're hashable. + +## Weaknesses: +- **Slow worst-case**: Lookups take O(n) time in the worst case. +- **Unordered**: Keys aren't stored in a special order. If you're looking for the smallest key, the largest key, or all the keys in a range, you'll need to look through every key to find it. +- **Single-directional lookups**: While you can look up the value for a given key in O(1) time, looking up the keys for a given value requires looping through the whole dataset—O(n) time. +- **Not cache-friendly**: Many hash table implementations use linked lists, which don't put data next to each other in memory. + +## Time Complexity +| | AVERAGE | WORST | +|--------|---------|-------| +| Space | O(n) | O(n) | +| Insert | O(1) | O(n) | +| Lookup | O(1) | O(n) | +| Delete | O(1) | O(n) | + +## Internal Structure of HashMap +Internally HashMap contains an array of Node and a node is represented as a class that contains 4 fields: +- int hash +- K key +- V value +- Node next + +It can be seen that the node contains a reference to its object. So it’s a linked list. + +## Performance of HashMap +The performance of HashMap depends on 2 parameters which are named as follows: +- Initial Capacity +- Load Factor + + +**Initial Capacity**: It is the capacity of HashMap at the time of its creation (It is the number of buckets a HashMap can hold when the HashMap is instantiated). In Java, it is 2^4=16 initially, meaning it can hold 16 key-value pairs. + +**Load Factor**: It is the percent value of the capacity after which the capacity of Hashmap is to be increased (It is the percentage fill of buckets after which Rehashing takes place). In Java, it is 0.75f by default, meaning the rehashing takes place after filling 75% of the capacity. + +**Threshold**: It is the product of Load Factor and Initial Capacity. In Java, by default, it is (16 * 0.75 = 12). That is, Rehashing takes place after inserting 12 key-value pairs into the HashMap. + +**Rehashing** : It is the process of doubling the capacity of the HashMap after it reaches its Threshold. In Java, HashMap continues to rehash(by default) in the following sequence – 2^4, 2^5, 2^6, 2^7, …. so on. + +If the initial capacity is kept higher then rehashing will never be done. But keeping it higher increases the time complexity of iteration. So it should be chosen very cleverly to increase performance. The expected number of values should be taken into account to set the initial capacity. The most generally preferred load factor value is 0.75 which provides a good deal between time and space costs. The load factor’s value varies between 0 and 1. + +``` +Note: From Java 8 onward, Java has started using Self Balancing BST instead of a linked list for chaining. +The advantage of self-balancing bst is, that we get the worst case (when every key maps to the same slot) search time is O(Log n). +``` + +Java has two hash table classes: HashTable and HashMap. In general, you should use a HashMap. + +While both classes use keys to look up values, there are some important differences, including: + +- A HashTable doesn't allow null keys or values; a HashMap does. +- A HashTable is synchronized to prevent multiple threads from accessing it at once; a HashMap isn't. + +## When Hash Map operations cost O(n) time? + +**Hash collisions**: If all our keys caused hash collisions, we'd be at risk of having to walk through all of our values for a single lookup (in the example above, we'd have one big linked list). This is unlikely, but it could happen. That's the worst case. + +**Dynamic array resizing**: Suppose we keep adding more items to our hash map. As the number of keys and values in our hash map exceeds the number of indices in the underlying array, hash collisions become inevitable. To mitigate this, we could expand our underlying array whenever things start to get crowded. That requires allocating a larger array and rehashing all of our existing keys to figure out their new position—O(n) time. + diff --git a/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArray.java b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArray.java new file mode 100644 index 000000000000..3637e323f097 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArray.java @@ -0,0 +1,203 @@ +package com.thealgorithms.datastructures.hashmap.hashing; + +import java.util.LinkedList; + +/** + * A generic implementation of a hash map using an array of linked lists for collision resolution. + * This class provides a way to store key-value pairs efficiently, allowing for average-case + * constant time complexity for insertion, deletion, and retrieval operations. + * + * <p> + * The hash map uses separate chaining for collision resolution. Each bucket in the hash map is a + * linked list that stores nodes containing key-value pairs. When a collision occurs (i.e., when + * two keys hash to the same index), the new key-value pair is simply added to the corresponding + * linked list. + * </p> + * + * <p> + * The hash map automatically resizes itself when the load factor exceeds 0.75. The load factor is + * defined as the ratio of the number of entries to the number of buckets. When resizing occurs, + * all existing entries are rehashed and inserted into the new buckets. + * </p> + * + * @param <K> the type of keys maintained by this hash map + * @param <V> the type of mapped values + */ +public class GenericHashMapUsingArray<K, V> { + + private int size; // Total number of key-value pairs + private LinkedList<Node>[] buckets; // Array of linked lists (buckets) for storing entries + + /** + * Constructs a new empty hash map with an initial capacity of 16. + */ + public GenericHashMapUsingArray() { + initBuckets(16); + size = 0; + } + + /** + * Initializes the buckets for the hash map with the specified number of buckets. + * + * @param n the number of buckets to initialize + */ + private void initBuckets(int n) { + buckets = new LinkedList[n]; + for (int i = 0; i < buckets.length; i++) { + buckets[i] = new LinkedList<>(); + } + } + + /** + * Associates the specified value with the specified key in this map. + * If the map previously contained a mapping for the key, the old value is replaced. + * + * @param key the key with which the specified value is to be associated + * @param value the value to be associated with the specified key + */ + public void put(K key, V value) { + int bucketIndex = hashFunction(key); + LinkedList<Node> nodes = buckets[bucketIndex]; + // Update existing key's value if present + for (Node node : nodes) { + if (node.key.equals(key)) { + node.value = value; + return; + } + } + + // Insert new key-value pair + nodes.add(new Node(key, value)); + size++; + + // Check if rehashing is needed + // Load factor threshold for resizing + float loadFactorThreshold = 0.75f; + if ((float) size / buckets.length > loadFactorThreshold) { + reHash(); + } + } + + /** + * Returns the index of the bucket in which the key would be stored. + * + * @param key the key whose bucket index is to be computed + * @return the bucket index + */ + private int hashFunction(K key) { + return Math.floorMod(key.hashCode(), buckets.length); + } + + /** + * Rehashes the map by doubling the number of buckets and re-inserting all entries. + */ + private void reHash() { + LinkedList<Node>[] oldBuckets = buckets; + initBuckets(oldBuckets.length * 2); + this.size = 0; + + for (LinkedList<Node> nodes : oldBuckets) { + for (Node node : nodes) { + put(node.key, node.value); + } + } + } + + /** + * Removes the mapping for the specified key from this map if present. + * + * @param key the key whose mapping is to be removed from the map + */ + public void remove(K key) { + int bucketIndex = hashFunction(key); + LinkedList<Node> nodes = buckets[bucketIndex]; + + Node target = null; + for (Node node : nodes) { + if (node.key.equals(key)) { + target = node; + break; + } + } + + if (target != null) { + nodes.remove(target); + size--; + } + } + + /** + * Returns the number of key-value pairs in this map. + * + * @return the number of key-value pairs + */ + public int size() { + return this.size; + } + + /** + * Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key. + * + * @param key the key whose associated value is to be returned + * @return the value associated with the specified key, or null if no mapping exists + */ + public V get(K key) { + int bucketIndex = hashFunction(key); + LinkedList<Node> nodes = buckets[bucketIndex]; + for (Node node : nodes) { + if (node.key.equals(key)) { + return node.value; + } + } + return null; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("{"); + for (LinkedList<Node> nodes : buckets) { + for (Node node : nodes) { + builder.append(node.key); + builder.append(" : "); + builder.append(node.value); + builder.append(", "); + } + } + // Remove trailing comma and space + if (builder.length() > 1) { + builder.setLength(builder.length() - 2); + } + builder.append("}"); + return builder.toString(); + } + + /** + * Returns true if this map contains a mapping for the specified key. + * + * @param key the key whose presence in this map is to be tested + * @return true if this map contains a mapping for the specified key + */ + public boolean containsKey(K key) { + return get(key) != null; + } + + /** + * A private class representing a key-value pair (node) in the hash map. + */ + public class Node { + K key; + V value; + + /** + * Constructs a new Node with the specified key and value. + * + * @param key the key of the key-value pair + * @param value the value of the key-value pair + */ + public Node(K key, V value) { + this.key = key; + this.value = value; + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayList.java b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayList.java new file mode 100644 index 000000000000..89e25f4eb0f7 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayList.java @@ -0,0 +1,188 @@ +package com.thealgorithms.datastructures.hashmap.hashing; + +import java.util.ArrayList; +import java.util.LinkedList; + +/** + * A generic implementation of a hash map using an array list of linked lists for collision resolution. + * This class allows storage of key-value pairs with average-case constant time complexity for insertion, + * deletion, and retrieval operations. + * + * <p> + * The hash map uses separate chaining to handle collisions. Each bucket in the hash map is represented + * by a linked list that holds nodes containing key-value pairs. When multiple keys hash to the same index, + * they are stored in the same linked list. + * </p> + * + * <p> + * The hash map automatically resizes itself when the load factor exceeds 0.5. The load factor is defined + * as the ratio of the number of entries to the number of buckets. When resizing occurs, all existing entries + * are rehashed and inserted into the new buckets. + * </p> + * + * @param <K> the type of keys maintained by this hash map + * @param <V> the type of mapped values + */ +public class GenericHashMapUsingArrayList<K, V> { + + private ArrayList<LinkedList<Node>> buckets; // Array list of buckets (linked lists) + private int size; // Number of key-value pairs in the hash map + + /** + * Constructs a new empty hash map with an initial capacity of 10 buckets. + */ + public GenericHashMapUsingArrayList() { + buckets = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + buckets.add(new LinkedList<>()); + } + size = 0; + } + + /** + * Associates the specified value with the specified key in this map. + * If the map previously contained a mapping for the key, the old value is replaced. + * + * @param key the key with which the specified value is to be associated + * @param value the value to be associated with the specified key + */ + public void put(K key, V value) { + int hash = Math.abs(key.hashCode() % buckets.size()); + LinkedList<Node> nodes = buckets.get(hash); + + for (Node node : nodes) { + if (node.key.equals(key)) { + node.val = value; + return; + } + } + + nodes.add(new Node(key, value)); + size++; + + // Load factor threshold for resizing + float loadFactorThreshold = 0.5f; + if ((float) size / buckets.size() > loadFactorThreshold) { + reHash(); + } + } + + /** + * Resizes the hash map by doubling the number of buckets and rehashing existing entries. + */ + private void reHash() { + ArrayList<LinkedList<Node>> oldBuckets = buckets; + buckets = new ArrayList<>(); + size = 0; + for (int i = 0; i < oldBuckets.size() * 2; i++) { + buckets.add(new LinkedList<>()); + } + for (LinkedList<Node> nodes : oldBuckets) { + for (Node node : nodes) { + put(node.key, node.val); + } + } + } + + /** + * Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key. + * + * @param key the key whose associated value is to be returned + * @return the value associated with the specified key, or null if no mapping exists + */ + public V get(K key) { + int hash = Math.abs(key.hashCode() % buckets.size()); + LinkedList<Node> nodes = buckets.get(hash); + for (Node node : nodes) { + if (node.key.equals(key)) { + return node.val; + } + } + return null; + } + + /** + * Removes the mapping for the specified key from this map if present. + * + * @param key the key whose mapping is to be removed from the map + */ + public void remove(K key) { + int hash = Math.abs(key.hashCode() % buckets.size()); + LinkedList<Node> nodes = buckets.get(hash); + + Node target = null; + for (Node node : nodes) { + if (node.key.equals(key)) { + target = node; + break; + } + } + if (target != null) { + nodes.remove(target); + size--; + } + } + + /** + * Returns true if this map contains a mapping for the specified key. + * + * @param key the key whose presence in this map is to be tested + * @return true if this map contains a mapping for the specified key + */ + public boolean containsKey(K key) { + return get(key) != null; + } + + /** + * Returns the number of key-value pairs in this map. + * + * @return the number of key-value pairs + */ + public int size() { + return this.size; + } + + /** + * Returns a string representation of the map, containing all key-value pairs. + * + * @return a string representation of the map + */ + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("{"); + for (LinkedList<Node> nodes : buckets) { + for (Node node : nodes) { + builder.append(node.key); + builder.append(" : "); + builder.append(node.val); + builder.append(", "); + } + } + // Remove trailing comma and space if there are any elements + if (builder.length() > 1) { + builder.setLength(builder.length() - 2); + } + builder.append("}"); + return builder.toString(); + } + + /** + * A private inner class representing a key-value pair (node) in the hash map. + */ + private class Node { + K key; + V val; + + /** + * Constructs a new Node with the specified key and value. + * + * @param key the key of the key-value pair + * @param val the value of the key-value pair + */ + Node(K key, V val) { + this.key = key; + this.val = val; + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/HashMap.java b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/HashMap.java new file mode 100644 index 000000000000..aed39c941430 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/HashMap.java @@ -0,0 +1,298 @@ +package com.thealgorithms.datastructures.hashmap.hashing; + +/** + * A generic HashMap implementation that uses separate chaining with linked lists + * to handle collisions. The class supports basic operations such as insert, delete, + * and search, as well as displaying the contents of the hash map. + * + * @param <K> the type of keys maintained by this map + * @param <V> the type of mapped values + */ +public class HashMap<K, V> { + private final int hashSize; + private final LinkedList<K, V>[] buckets; + + /** + * Constructs a HashMap with the specified hash size. + * + * @param hashSize the number of buckets in the hash map + */ + @SuppressWarnings("unchecked") + public HashMap(int hashSize) { + this.hashSize = hashSize; + // Safe to suppress warning because we are creating an array of generic type + this.buckets = new LinkedList[hashSize]; + for (int i = 0; i < hashSize; i++) { + buckets[i] = new LinkedList<>(); + } + } + + /** + * Computes the hash code for the specified key. + * Null keys are hashed to bucket 0. + * + * @param key the key for which the hash code is to be computed + * @return the hash code corresponding to the key + */ + private int computeHash(K key) { + if (key == null) { + return 0; // Use a special bucket (e.g., bucket 0) for null keys + } + int hash = key.hashCode() % hashSize; + return hash < 0 ? hash + hashSize : hash; + } + + /** + * Inserts the specified key-value pair into the hash map. + * If the key already exists, the value is updated. + * + * @param key the key to be inserted + * @param value the value to be associated with the key + */ + public void insert(K key, V value) { + int hash = computeHash(key); + buckets[hash].insert(key, value); + } + + /** + * Deletes the key-value pair associated with the specified key from the hash map. + * + * @param key the key whose key-value pair is to be deleted + */ + public void delete(K key) { + int hash = computeHash(key); + buckets[hash].delete(key); + } + + /** + * Searches for the value associated with the specified key in the hash map. + * + * @param key the key whose associated value is to be returned + * @return the value associated with the specified key, or null if the key does not exist + */ + public V search(K key) { + int hash = computeHash(key); + Node<K, V> node = buckets[hash].findKey(key); + return node != null ? node.getValue() : null; + } + + /** + * Displays the contents of the hash map, showing each bucket and its key-value pairs. + */ + public void display() { + for (int i = 0; i < hashSize; i++) { + System.out.printf("Bucket %d: %s%n", i, buckets[i].display()); + } + } + + /** + * Clears the contents of the hash map by reinitializing each bucket. + */ + public void clear() { + for (int i = 0; i < hashSize; i++) { + buckets[i] = new LinkedList<>(); + } + } + + /** + * Gets the number of key-value pairs in the hash map. + * + * @return the number of key-value pairs in the hash map + */ + public int size() { + int size = 0; + for (int i = 0; i < hashSize; i++) { + size += buckets[i].isEmpty() ? 0 : 1; + } + return size; + } + + /** + * A nested static class that represents a linked list used for separate chaining in the hash map. + * + * @param <K> the type of keys maintained by this linked list + * @param <V> the type of mapped values + */ + public static class LinkedList<K, V> { + private Node<K, V> head; + + /** + * Inserts the specified key-value pair into the linked list. + * If the linked list is empty, the pair becomes the head. + * Otherwise, the pair is added to the end of the list. + * + * @param key the key to be inserted + * @param value the value to be associated with the key + */ + public void insert(K key, V value) { + Node<K, V> existingNode = findKey(key); + if (existingNode != null) { + existingNode.setValue(value); // Update the value, even if it's null + } else { + if (isEmpty()) { + head = new Node<>(key, value); + } else { + Node<K, V> temp = findEnd(head); + temp.setNext(new Node<>(key, value)); + } + } + } + + /** + * Finds the last node in the linked list. + * + * @param node the starting node + * @return the last node in the linked list + */ + private Node<K, V> findEnd(Node<K, V> node) { + while (node.getNext() != null) { + node = node.getNext(); + } + return node; + } + + /** + * Finds the node associated with the specified key in the linked list. + * + * @param key the key to search for + * @return the node associated with the specified key, or null if not found + */ + public Node<K, V> findKey(K key) { + Node<K, V> temp = head; + while (temp != null) { + if ((key == null && temp.getKey() == null) || (temp.getKey() != null && temp.getKey().equals(key))) { + return temp; + } + temp = temp.getNext(); + } + return null; + } + + /** + * Deletes the node associated with the specified key from the linked list. + * Handles the case where the key could be null. + * + * @param key the key whose associated node is to be deleted + */ + public void delete(K key) { + if (isEmpty()) { + return; + } + + // Handle the case where the head node has the key to delete + if ((key == null && head.getKey() == null) || (head.getKey() != null && head.getKey().equals(key))) { + head = head.getNext(); + return; + } + + // Traverse the list to find and delete the node + Node<K, V> current = head; + while (current.getNext() != null) { + if ((key == null && current.getNext().getKey() == null) || (current.getNext().getKey() != null && current.getNext().getKey().equals(key))) { + current.setNext(current.getNext().getNext()); + return; + } + current = current.getNext(); + } + } + + /** + * Displays the contents of the linked list as a string. + * + * @return a string representation of the linked list + */ + public String display() { + return display(head); + } + + /** + * Constructs a string representation of the linked list non-recursively. + * + * @param node the starting node + * @return a string representation of the linked list starting from the given node + */ + private String display(Node<K, V> node) { + StringBuilder sb = new StringBuilder(); + while (node != null) { + sb.append(node.getKey()).append("=").append(node.getValue()); + node = node.getNext(); + if (node != null) { + sb.append(" -> "); + } + } + return sb.toString().isEmpty() ? "null" : sb.toString(); + } + + /** + * Checks if the linked list is empty. + * + * @return true if the linked list is empty, false otherwise + */ + public boolean isEmpty() { + return head == null; + } + } + + /** + * A nested static class representing a node in the linked list. + * + * @param <K> the type of key maintained by this node + * @param <V> the type of value maintained by this node + */ + public static class Node<K, V> { + private final K key; + private V value; + private Node<K, V> next; + + /** + * Constructs a Node with the specified key and value. + * + * @param key the key associated with this node + * @param value the value associated with this node + */ + public Node(K key, V value) { + this.key = key; + this.value = value; + } + + /** + * Gets the key associated with this node. + * + * @return the key associated with this node + */ + public K getKey() { + return key; + } + + /** + * Gets the value associated with this node. + * + * @return the value associated with this node + */ + public V getValue() { + return value; + } + + public void setValue(V value) { // This method allows updating the value + this.value = value; + } + + /** + * Gets the next node in the linked list. + * + * @return the next node in the linked list + */ + public Node<K, V> getNext() { + return next; + } + + /** + * Sets the next node in the linked list. + * + * @param next the next node to be linked + */ + public void setNext(Node<K, V> next) { + this.next = next; + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapCuckooHashing.java b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapCuckooHashing.java new file mode 100644 index 000000000000..8bcf00730acb --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapCuckooHashing.java @@ -0,0 +1,269 @@ +package com.thealgorithms.datastructures.hashmap.hashing; + +import java.util.Objects; + +/** + * This class implements a hash table using Cuckoo Hashing. + * Cuckoo hashing is a type of open-addressing hash table that resolves collisions + * by relocating existing keys. It utilizes two hash functions to minimize collisions + * and automatically resizes the table when the load factor exceeds 0.7. + * + * For more information on cuckoo hashing, refer to + * <a href="https://en.wikipedia.org/wiki/Cuckoo_hashing">this Wikipedia page</a>. + */ +public class HashMapCuckooHashing { + + private int tableSize; // Size of the hash table + private Integer[] buckets; // Array representing the hash table + private final Integer emptySlot; // Placeholder for deleted slots + private int size; // Number of elements in the hash table + private int thresh; // Threshold for detecting infinite loops during insertion + + /** + * Constructs a HashMapCuckooHashing object with the specified initial table size. + * + * @param tableSize the initial size of the hash map + */ + public HashMapCuckooHashing(int tableSize) { + this.buckets = new Integer[tableSize]; + this.tableSize = tableSize; + this.emptySlot = Integer.MIN_VALUE; + this.size = 0; + this.thresh = (int) (Math.log(tableSize) / Math.log(2)) + 2; + } + + /** + * Computes the first hash index for a given key using the modulo operation. + * + * @param key the key for which the hash index is computed + * @return an integer index corresponding to the key + */ + public int hashFunction1(int key) { + int hash = key % tableSize; + if (hash < 0) { + hash += tableSize; + } + return hash; + } + + /** + * Computes the second hash index for a given key using integer division. + * + * @param key the key for which the hash index is computed + * @return an integer index corresponding to the key + */ + public int hashFunction2(int key) { + int hash = key / tableSize; + hash %= tableSize; + if (hash < 0) { + hash += tableSize; + } + return hash; + } + + /** + * Inserts a key into the hash table using cuckoo hashing. + * If the target bucket is occupied, it relocates the existing key and attempts to insert + * it into its alternate location. If the insertion process exceeds the threshold, + * the table is resized. + * + * @param key the key to be inserted into the hash table + * @throws IllegalArgumentException if the key already exists in the table + */ + public void insertKey2HashTable(int key) { + Integer wrappedInt = key; + Integer temp; + int hash; + int loopCounter = 0; + + if (isFull()) { + System.out.println("Hash table is full, lengthening & rehashing table"); + reHashTableIncreasesTableSize(); + } + + if (checkTableContainsKey(key)) { + throw new IllegalArgumentException("Key already exists; duplicates are not allowed."); + } + + while (loopCounter <= thresh) { + loopCounter++; + hash = hashFunction1(key); + + if ((buckets[hash] == null) || Objects.equals(buckets[hash], emptySlot)) { + buckets[hash] = wrappedInt; + size++; + checkLoadFactor(); + return; + } + + temp = buckets[hash]; + buckets[hash] = wrappedInt; + wrappedInt = temp; + hash = hashFunction2(temp); + if (Objects.equals(buckets[hash], emptySlot)) { + buckets[hash] = wrappedInt; + size++; + checkLoadFactor(); + return; + } else if (buckets[hash] == null) { + buckets[hash] = wrappedInt; + size++; + checkLoadFactor(); + return; + } + + temp = buckets[hash]; + buckets[hash] = wrappedInt; + wrappedInt = temp; + } + System.out.println("Infinite loop occurred, lengthening & rehashing table"); + reHashTableIncreasesTableSize(); + insertKey2HashTable(key); + } + + /** + * Rehashes the current table to a new size (double the current size) and reinserts existing keys. + */ + public void reHashTableIncreasesTableSize() { + HashMapCuckooHashing newT = new HashMapCuckooHashing(tableSize * 2); + for (int i = 0; i < tableSize; i++) { + if (buckets[i] != null && !Objects.equals(buckets[i], emptySlot)) { + newT.insertKey2HashTable(this.buckets[i]); + } + } + this.tableSize *= 2; + this.buckets = newT.buckets; + this.thresh = (int) (Math.log(tableSize) / Math.log(2)) + 2; + } + + /** + * Deletes a key from the hash table, marking its position as available. + * + * @param key the key to be deleted from the hash table + * @throws IllegalArgumentException if the table is empty or if the key is not found + */ + public void deleteKeyFromHashTable(int key) { + Integer wrappedInt = key; + int hash = hashFunction1(key); + if (isEmpty()) { + throw new IllegalArgumentException("Table is empty, cannot delete."); + } + + if (Objects.equals(buckets[hash], wrappedInt)) { + buckets[hash] = emptySlot; + size--; + return; + } + + hash = hashFunction2(key); + if (Objects.equals(buckets[hash], wrappedInt)) { + buckets[hash] = emptySlot; + size--; + return; + } + throw new IllegalArgumentException("Key " + key + " not found in the table."); + } + + /** + * Displays the hash table contents, bucket by bucket. + */ + public void displayHashtable() { + for (int i = 0; i < tableSize; i++) { + if ((buckets[i] == null) || Objects.equals(buckets[i], emptySlot)) { + System.out.println("Bucket " + i + ": Empty"); + } else { + System.out.println("Bucket " + i + ": " + buckets[i].toString()); + } + } + System.out.println(); + } + + /** + * Finds the index of a given key in the hash table. + * + * @param key the key to be found + * @return the index where the key is located + * @throws IllegalArgumentException if the table is empty or the key is not found + */ + public int findKeyInTable(int key) { + Integer wrappedInt = key; + int hash = hashFunction1(key); + + if (isEmpty()) { + throw new IllegalArgumentException("Table is empty; cannot find keys."); + } + + if (Objects.equals(buckets[hash], wrappedInt)) { + return hash; + } + + hash = hashFunction2(key); + if (!Objects.equals(buckets[hash], wrappedInt)) { + throw new IllegalArgumentException("Key " + key + " not found in the table."); + } else { + return hash; + } + } + + /** + * Checks if the given key is present in the hash table. + * + * @param key the key to be checked + * @return true if the key exists, false otherwise + */ + public boolean checkTableContainsKey(int key) { + return ((buckets[hashFunction1(key)] != null && buckets[hashFunction1(key)].equals(key)) || (buckets[hashFunction2(key)] != null && buckets[hashFunction2(key)].equals(key))); + } + + /** + * Checks the load factor of the hash table. If the load factor exceeds 0.7, + * the table is resized to prevent further collisions. + * + * @return the current load factor of the hash table + */ + public double checkLoadFactor() { + double factor = (double) size / tableSize; + if (factor > .7) { + System.out.printf("Load factor is %.2f, rehashing table.%n", factor); + reHashTableIncreasesTableSize(); + } + return factor; + } + + /** + * Checks if the hash map is full. + * + * @return true if the hash map is full, false otherwise + */ + public boolean isFull() { + for (int i = 0; i < tableSize; i++) { + if (buckets[i] == null || Objects.equals(buckets[i], emptySlot)) { + return false; + } + } + return true; + } + + /** + * Checks if the hash map is empty. + * + * @return true if the hash map is empty, false otherwise + */ + public boolean isEmpty() { + for (int i = 0; i < tableSize; i++) { + if (buckets[i] != null) { + return false; + } + } + return true; + } + + /** + * Returns the current number of keys in the hash table. + * + * @return the number of keys present in the hash table + */ + public int getNumberOfKeysInTable() { + return size; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/Intersection.java b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/Intersection.java new file mode 100644 index 000000000000..0e49218d6348 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/Intersection.java @@ -0,0 +1,67 @@ +package com.thealgorithms.datastructures.hashmap.hashing; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * The {@code Intersection} class provides a method to compute the intersection of two integer arrays. + * The intersection is defined as the set of common elements present in both arrays. + * <p> + * This class utilizes a HashMap to efficiently count occurrences of elements in the first array, + * allowing for an efficient lookup of common elements in the second array. + * </p> + * + * <p> + * Example: + * <pre> + * int[] array1 = {1, 2, 2, 1}; + * int[] array2 = {2, 2}; + * List<Integer> result = Intersection.intersection(array1, array2); // result will contain [2, 2] + * </pre> + * </p> + * + * <p> + * Note: The order of the returned list may vary since it depends on the order of elements + * in the input arrays. + * </p> + */ +public final class Intersection { + + /** + * Computes the intersection of two integer arrays. + * Steps: + * 1. Count the occurrences of each element in the first array using a HashMap. + * 2. Iterate over the second array and check if the element is present in the HashMap. + * If it is, add it to the result list and decrement the count in the HashMap. + * 3. Return the result list containing the intersection of the two arrays. + * + * @param arr1 the first array of integers + * @param arr2 the second array of integers + * @return a list containing the intersection of the two arrays, or an empty list if either array is null or empty + */ + public static List<Integer> intersection(int[] arr1, int[] arr2) { + if (arr1 == null || arr2 == null || arr1.length == 0 || arr2.length == 0) { + return Collections.emptyList(); + } + + Map<Integer, Integer> cnt = new HashMap<>(16); + for (int v : arr1) { + cnt.put(v, cnt.getOrDefault(v, 0) + 1); + } + + List<Integer> res = new ArrayList<>(); + for (int v : arr2) { + if (cnt.containsKey(v) && cnt.get(v) > 0) { + res.add(v); + cnt.put(v, cnt.get(v) - 1); + } + } + return res; + } + + private Intersection() { + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/LinearProbingHashMap.java b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/LinearProbingHashMap.java new file mode 100644 index 000000000000..10d5dc7decae --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/LinearProbingHashMap.java @@ -0,0 +1,168 @@ +package com.thealgorithms.datastructures.hashmap.hashing; + +import java.util.ArrayList; + +/** + * This class implements a hash table using linear probing to resolve collisions. + * Linear probing is a collision resolution method where each slot in the hash table is checked in a sequential manner + * until an empty slot is found. + * + * <p> + * The class allows for storing key-value pairs, where both the key and value are generic types. + * The key must be of a type that implements the Comparable interface to ensure that the keys can be compared for sorting. + * </p> + * + * <p> + * This implementation supports basic operations such as: + * <ul> + * <li><b>put(Key key, Value value)</b>: Adds a key-value pair to the hash table. If the key already exists, its value is updated.</li> + * <li><b>get(Key key)</b>: Retrieves the value associated with the given key.</li> + * <li><b>delete(Key key)</b>: Removes the key and its associated value from the hash table.</li> + * <li><b>contains(Key key)</b>: Checks if the hash table contains a given key.</li> + * <li><b>size()</b>: Returns the number of key-value pairs in the hash table.</li> + * <li><b>keys()</b>: Returns an iterable collection of keys stored in the hash table.</li> + * </ul> + * </p> + * + * <p> + * The internal size of the hash table is automatically resized when the load factor exceeds 0.5 or falls below 0.125, + * ensuring efficient space utilization. + * </p> + * + * @see <a href="https://en.wikipedia.org/wiki/Linear_probing">Linear Probing Hash Table</a> + * + * @param <Key> the type of keys maintained by this map + * @param <Value> the type of mapped values + */ +public class LinearProbingHashMap<Key extends Comparable<Key>, Value> extends Map<Key, Value> { + private int hsize; // size of the hash table + private Key[] keys; // array to store keys + private Value[] values; // array to store values + private int size; // number of elements in the hash table + + // Default constructor initializes the table with a default size of 16 + public LinearProbingHashMap() { + this(16); + } + + @SuppressWarnings("unchecked") + // Constructor to initialize the hash table with a specified size + public LinearProbingHashMap(int size) { + this.hsize = size; + keys = (Key[]) new Comparable[size]; + values = (Value[]) new Object[size]; + } + + @Override + public boolean put(Key key, Value value) { + if (key == null) { + return false; + } + + if (size > hsize / 2) { + resize(2 * hsize); + } + + int keyIndex = hash(key, hsize); + for (; keys[keyIndex] != null; keyIndex = increment(keyIndex)) { + if (key.equals(keys[keyIndex])) { + values[keyIndex] = value; + return true; + } + } + + keys[keyIndex] = key; + values[keyIndex] = value; + size++; + return true; + } + + @Override + public Value get(Key key) { + if (key == null) { + return null; + } + + for (int i = hash(key, hsize); keys[i] != null; i = increment(i)) { + if (key.equals(keys[i])) { + return values[i]; + } + } + + return null; + } + + @Override + public boolean delete(Key key) { + if (key == null || !contains(key)) { + return false; + } + + int i = hash(key, hsize); + while (!key.equals(keys[i])) { + i = increment(i); + } + + keys[i] = null; + values[i] = null; + + i = increment(i); + while (keys[i] != null) { + // Save the key and value for rehashing + Key keyToRehash = keys[i]; + Value valToRehash = values[i]; + keys[i] = null; + values[i] = null; + size--; + put(keyToRehash, valToRehash); + i = increment(i); + } + + size--; + if (size > 0 && size <= hsize / 8) { + resize(hsize / 2); + } + + return true; + } + + @Override + public boolean contains(Key key) { + return get(key) != null; + } + + @Override + int size() { + return size; + } + + @Override + Iterable<Key> keys() { + ArrayList<Key> listOfKeys = new ArrayList<>(size); + for (int i = 0; i < hsize; i++) { + if (keys[i] != null) { + listOfKeys.add(keys[i]); + } + } + + listOfKeys.sort(Comparable::compareTo); + return listOfKeys; + } + + private int increment(int i) { + return (i + 1) % hsize; + } + + private void resize(int newSize) { + LinearProbingHashMap<Key, Value> tmp = new LinearProbingHashMap<>(newSize); + for (int i = 0; i < hsize; i++) { + if (keys[i] != null) { + tmp.put(keys[i], values[i]); + } + } + + this.keys = tmp.keys; + this.values = tmp.values; + this.hsize = newSize; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/MainCuckooHashing.java b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/MainCuckooHashing.java new file mode 100644 index 000000000000..ff4c69a5ec78 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/MainCuckooHashing.java @@ -0,0 +1,70 @@ +package com.thealgorithms.datastructures.hashmap.hashing; + +import java.util.Scanner; + +public final class MainCuckooHashing { + private MainCuckooHashing() { + } + + public static void main(String[] args) { + int choice; + int key; + + HashMapCuckooHashing h = new HashMapCuckooHashing(7); + Scanner scan = new Scanner(System.in); + + while (true) { + System.out.println("_________________________"); + System.out.println("Enter your Choice :"); + System.out.println("1. Add Key"); + System.out.println("2. Delete Key"); + System.out.println("3. Print Table"); + System.out.println("4. Exit"); + System.out.println("5. Search and print key index"); + System.out.println("6. Check load factor"); + System.out.println("7. Rehash Current Table"); + + choice = scan.nextInt(); + + switch (choice) { + case 1: + System.out.println("Enter the Key: "); + key = scan.nextInt(); + h.insertKey2HashTable(key); + break; + + case 2: + System.out.println("Enter the Key delete: "); + key = scan.nextInt(); + h.deleteKeyFromHashTable(key); + break; + + case 3: + System.out.println("Print table:\n"); + h.displayHashtable(); + break; + + case 4: + scan.close(); + return; + + case 5: + System.out.println("Enter the Key to find and print: "); + key = scan.nextInt(); + System.out.println("Key: " + key + " is at index: " + h.findKeyInTable(key) + "\n"); + break; + + case 6: + System.out.printf("Load factor is: %.2f%n", h.checkLoadFactor()); + break; + + case 7: + h.reHashTableIncreasesTableSize(); + break; + + default: + throw new IllegalArgumentException("Unexpected value: " + choice); + } + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/MajorityElement.java b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/MajorityElement.java new file mode 100644 index 000000000000..915e4228b618 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/MajorityElement.java @@ -0,0 +1,36 @@ +package com.thealgorithms.datastructures.hashmap.hashing; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * This class provides a method to find the majority element(s) in an array of integers. + * A majority element is defined as an element that appears at least ⌊n/2⌋ times, + * where n is the length of the array. If multiple elements qualify as majority elements, + * they are all returned in a list. + */ +public final class MajorityElement { + private MajorityElement() { + } + + /** + * Returns a list of majority element(s) from the given array of integers. + * + * @param nums an array of integers + * @return a list containing the majority element(s); returns an empty list if none exist + */ + public static List<Integer> majority(int[] nums) { + HashMap<Integer, Integer> numToCount = new HashMap<>(); + for (final var num : nums) { + numToCount.merge(num, 1, Integer::sum); + } + List<Integer> majorityElements = new ArrayList<>(); + for (final var entry : numToCount.entrySet()) { + if (entry.getValue() >= nums.length / 2) { + majorityElements.add(entry.getKey()); + } + } + return majorityElements; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/Map.java b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/Map.java new file mode 100644 index 000000000000..4605883fbbb1 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/Map.java @@ -0,0 +1,22 @@ +package com.thealgorithms.datastructures.hashmap.hashing; + +public abstract class Map<Key, Value> { + + abstract boolean put(Key key, Value value); + + abstract Value get(Key key); + + abstract boolean delete(Key key); + + abstract Iterable<Key> keys(); + + abstract int size(); + + public boolean contains(Key key) { + return get(key) != null; + } + + protected int hash(Key key, int size) { + return (key.hashCode() & Integer.MAX_VALUE) % size; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/heaps/EmptyHeapException.java b/src/main/java/com/thealgorithms/datastructures/heaps/EmptyHeapException.java new file mode 100644 index 000000000000..11af3f39981c --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/heaps/EmptyHeapException.java @@ -0,0 +1,17 @@ +package com.thealgorithms.datastructures.heaps; + +/** + * @author Nicolas Renard Exception to be thrown if the getElement method is + * used on an empty heap. + */ +@SuppressWarnings("serial") +public class EmptyHeapException extends Exception { + + public EmptyHeapException(String message) { + super(message); + } + + public EmptyHeapException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/heaps/FibonacciHeap.java b/src/main/java/com/thealgorithms/datastructures/heaps/FibonacciHeap.java new file mode 100644 index 000000000000..7a263fc08ac5 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/heaps/FibonacciHeap.java @@ -0,0 +1,454 @@ +package com.thealgorithms.datastructures.heaps; + +/** + * The {@code FibonacciHeap} class implements a Fibonacci Heap data structure, + * which is a collection of trees that satisfy the minimum heap property. + * This heap allows for efficient merging of heaps, as well as faster + * decrease-key and delete operations compared to other heap data structures. + * + * <p>Key features of the Fibonacci Heap include: + * <ul> + * <li>Amortized O(1) time complexity for insert and decrease-key operations.</li> + * <li>Amortized O(log n) time complexity for delete and delete-min operations.</li> + * <li>Meld operation that combines two heaps in O(1) time.</li> + * <li>Potential function that helps analyze the amortized time complexity.</li> + * </ul> + * + * <p>This implementation maintains additional statistics such as the total number + * of link and cut operations performed during the lifetime of the heap, which can + * be accessed through static methods. + * + * <p>The Fibonacci Heap is composed of nodes represented by the inner class + * {@code HeapNode}. Each node maintains a key, rank, marked status, and pointers + * to its children and siblings. Nodes can be linked and cut as part of the heap + * restructuring processes. + * + * @see HeapNode + */ +public class FibonacciHeap { + + private static final double GOLDEN_RATIO = (1 + Math.sqrt(5)) / 2; + private HeapNode min; + private static int totalLinks = 0; + private static int totalCuts = 0; + private int numOfTrees = 0; + private int numOfHeapNodes = 0; + private int markedHeapNodesCounter = 0; + + /* + * a constructor for an empty Heap + * set the min to be null + */ + public FibonacciHeap() { + this.min = null; + } + + /* + * a constructor for a Heap with one element + * set the min to be the HeapNode with the given key + * @pre key>=0 + * @post empty == false + */ + public FibonacciHeap(int key) { + this.min = new HeapNode(key); + this.numOfTrees++; + this.numOfHeapNodes++; + } + + /* + * check if the heap is empty + * $ret == true - if the tree is empty + */ + public boolean empty() { + return (this.min == null); + } + + /** + * Creates a node (of type HeapNode) which contains the given key, and inserts it into the heap. + * + * @pre key>=0 + * @post (numOfnodes = = $prev numOfnodes + 1) + * @post empty == false + * $ret = the HeapNode we inserted + */ + public HeapNode insert(int key) { + HeapNode toInsert = new HeapNode(key); // creates the node + if (this.empty()) { + this.min = toInsert; + } else { // tree is not empty + min.setNext(toInsert); + this.updateMin(toInsert); + } + this.numOfHeapNodes++; + this.numOfTrees++; + return toInsert; + } + + /** + * Delete the node containing the minimum key in the heap + * updates new min + * + * @post (numOfnodes = = $prev numOfnodes - 1) + */ + public void deleteMin() { + if (this.empty()) { + return; + } + if (this.numOfHeapNodes == 1) { // if there is only one tree + this.min = null; + this.numOfTrees--; + this.numOfHeapNodes--; + return; + } + // change all children's parent to null// + if (this.min.child != null) { // min has a child + HeapNode child = this.min.child; + HeapNode tmpChild = child; + child.parent = null; + while (child.next != tmpChild) { + child = child.next; + child.parent = null; + } + } + // delete the node// + if (this.numOfTrees > 1) { + (this.min.prev).next = this.min.next; + (this.min.next).prev = this.min.prev; + if (this.min.child != null) { + (this.min.prev).setNext(this.min.child); + } + } else { // this.numOfTrees = 1 + this.min = this.min.child; + } + this.numOfHeapNodes--; + this.successiveLink(this.min.getNext()); + } + + /** + * Return the node of the heap whose key is minimal. + * $ret == null if (empty==true) + */ + public HeapNode findMin() { + return this.min; + } + + /** + * Meld the heap with heap2 + * + * @pre heap2 != null + * @post (numOfnodes = = $prev numOfnodes + heap2.numOfnodes) + */ + public void meld(FibonacciHeap heap2) { + if (heap2.empty()) { + return; + } + if (this.empty()) { + this.min = heap2.min; + } else { + this.min.setNext(heap2.min); + this.updateMin(heap2.min); + } + this.numOfTrees += heap2.numOfTrees; + this.numOfHeapNodes += heap2.numOfHeapNodes; + } + + /** + * Return the number of elements in the heap + * $ret == 0 if heap is empty + */ + public int size() { + return this.numOfHeapNodes; + } + + /** + * Return a counters array, where the value of the i-th index is the number of trees with rank i + * in the heap. returns an empty array for an empty heap + */ + public int[] countersRep() { + if (this.empty()) { + return new int[0]; /// return an empty array + } + int[] rankArray = new int[(int) Math.floor(Math.log(this.size()) / Math.log(GOLDEN_RATIO)) + 1]; // creates the array + rankArray[this.min.rank]++; + HeapNode curr = this.min.next; + while (curr != this.min) { + rankArray[curr.rank]++; + curr = curr.next; + } + return rankArray; + } + + /** + * Deletes the node x from the heap (using decreaseKey(x) to -1) + * + * @pre heap contains x + * @post (numOfnodes = = $prev numOfnodes - 1) + */ + public void delete(HeapNode x) { + this.decreaseKey(x, x.getKey() + 1); // change key to be the minimal (-1) + this.deleteMin(); // delete it + } + + /** + * The function decreases the key of the node x by delta. + * + * @pre x.key >= delta (we don't realize it when calling from delete()) + * @pre heap contains x + */ + private void decreaseKey(HeapNode x, int delta) { + int newKey = x.getKey() - delta; + x.key = newKey; + if (x.isRoot()) { // no parent to x + this.updateMin(x); + return; + } + if (x.getKey() >= x.parent.getKey()) { + return; + } // we don't need to cut + HeapNode prevParent = x.parent; + this.cut(x); + this.cascadingCuts(prevParent); + } + + /** + * returns the current potential of the heap, which is: + * Potential = #trees + 2*#markedNodes + */ + public int potential() { + return numOfTrees + (2 * markedHeapNodesCounter); + } + + /** + * This static function returns the total number of link operations made during the run-time of + * the program. A link operation is the operation which gets as input two trees of the same + * rank, and generates a tree of rank bigger by one. + */ + public static int totalLinks() { + return totalLinks; + } + + /** + * This static function returns the total number of cut operations made during the run-time of + * the program. A cut operation is the operation which disconnects a subtree from its parent + * (during decreaseKey/delete methods). + */ + public static int totalCuts() { + return totalCuts; + } + + /* + * updates the min of the heap (if needed) + * @pre this.min == @param (posMin) if and only if (posMin.key < this.min.key) + */ + private void updateMin(HeapNode posMin) { + if (posMin.getKey() < this.min.getKey()) { + this.min = posMin; + } + } + + /* + * Recursively "runs" all the way up from @param (curr) and mark the nodes. + * stop the recursion if we had arrived to a marked node or to a root. + * if we arrived to a marked node, we cut it and continue recursively. + * called after a node was cut. + * @post (numOfnodes == $prev numOfnodes) + */ + private void cascadingCuts(HeapNode curr) { + if (!curr.isMarked()) { // stop the recursion + curr.mark(); + if (!curr.isRoot()) { + this.markedHeapNodesCounter++; + } + } else { + if (curr.isRoot()) { + return; + } + HeapNode prevParent = curr.parent; + this.cut(curr); + this.cascadingCuts(prevParent); + } + } + + /* + * cut a node (and his "subtree") from his origin tree and connect it to the heap as a new tree. + * called after a node was cut. + * @post (numOfnodes == $prev numOfnodes) + */ + private void cut(HeapNode curr) { + curr.parent.rank--; + if (curr.marked) { + this.markedHeapNodesCounter--; + curr.marked = false; + } + if (curr.parent.child == curr) { // we should change the parent's child + if (curr.next == curr) { // curr do not have brothers + curr.parent.child = null; + } else { // curr have brothers + curr.parent.child = curr.next; + } + } + curr.prev.next = curr.next; + curr.next.prev = curr.prev; + curr.next = curr; + curr.prev = curr; + curr.parent = null; + this.min.setNext(curr); + this.updateMin(curr); + this.numOfTrees++; + totalCuts++; + } + + /* + * + */ + private void successiveLink(HeapNode curr) { + HeapNode[] buckets = this.toBuckets(curr); + this.min = this.fromBuckets(buckets); + } + + /* + * + */ + private HeapNode[] toBuckets(HeapNode curr) { + HeapNode[] buckets = new HeapNode[(int) Math.floor(Math.log(this.size()) / Math.log(GOLDEN_RATIO)) + 1]; + curr.prev.next = null; + HeapNode tmpCurr; + while (curr != null) { + tmpCurr = curr; + curr = curr.next; + tmpCurr.next = tmpCurr; + tmpCurr.prev = tmpCurr; + while (buckets[tmpCurr.rank] != null) { + tmpCurr = this.link(tmpCurr, buckets[tmpCurr.rank]); + buckets[tmpCurr.rank - 1] = null; + } + buckets[tmpCurr.rank] = tmpCurr; + } + return buckets; + } + + /* + * + */ + private HeapNode fromBuckets(HeapNode[] buckets) { + HeapNode tmpMin = null; + this.numOfTrees = 0; + for (int i = 0; i < buckets.length; i++) { + if (buckets[i] != null) { + this.numOfTrees++; + if (tmpMin == null) { + tmpMin = buckets[i]; + tmpMin.next = tmpMin; + tmpMin.prev = tmpMin; + } else { + tmpMin.setNext(buckets[i]); + if (buckets[i].getKey() < tmpMin.getKey()) { + tmpMin = buckets[i]; + } + } + } + } + return tmpMin; + } + + /* + * link between two nodes (and their trees) + * defines the smaller node to be the parent + */ + private HeapNode link(HeapNode c1, HeapNode c2) { + if (c1.getKey() > c2.getKey()) { + HeapNode c3 = c1; + c1 = c2; + c2 = c3; + } + if (c1.child == null) { + c1.child = c2; + } else { + c1.child.setNext(c2); + } + c2.parent = c1; + c1.rank++; + totalLinks++; + return c1; + } + + /** + * public class HeapNode + * each HeapNode belongs to a heap (Inner class) + */ + public class HeapNode { + + public int key; + private int rank; + private boolean marked; + private HeapNode child; + private HeapNode next; + private HeapNode prev; + private HeapNode parent; + + /* + * a constructor for a heapNode withe key @param (key) + * prev == next == this + * parent == child == null + */ + public HeapNode(int key) { + this.key = key; + this.marked = false; + this.next = this; + this.prev = this; + } + + /* + * returns the key of the node. + */ + public int getKey() { + return this.key; + } + + /* + * checks whether the node is marked + * $ret = true if one child has been cut + */ + private boolean isMarked() { + return this.marked; + } + + /* + * mark a node (after a child was cut) + * @inv root.mark() == false. + */ + private void mark() { + if (this.isRoot()) { + return; + } // check if the node is a root + this.marked = true; + } + + /* + * add the node @param (newNext) to be between this and this.next + * works fine also if @param (newNext) does not "stands" alone + */ + private void setNext(HeapNode newNext) { + HeapNode tmpNext = this.next; + this.next = newNext; + this.next.prev.next = tmpNext; + tmpNext.prev = newNext.prev; + this.next.prev = this; + } + + /* + * returns the next node to this node + */ + private HeapNode getNext() { + return this.next; + } + + /* + * check if the node is a root + * root definition - this.parent == null (uppest in his tree) + */ + private boolean isRoot() { + return (this.parent == null); + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/heaps/GenericHeap.java b/src/main/java/com/thealgorithms/datastructures/heaps/GenericHeap.java new file mode 100644 index 000000000000..b8a289db60b9 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/heaps/GenericHeap.java @@ -0,0 +1,149 @@ +package com.thealgorithms.datastructures.heaps; + +import java.util.ArrayList; +import java.util.HashMap; + +/** + * A generic implementation of a max heap data structure. + * + * @param <T> the type of elements in this heap, must extend Comparable. + */ +public class GenericHeap<T extends Comparable<T>> { + + private final ArrayList<T> data = new ArrayList<>(); + private final HashMap<T, Integer> map = new HashMap<>(); + + /** + * Adds an item to the heap, maintaining the heap property. + * + * @param item the item to be added + */ + public void add(T item) { + if (item == null) { + throw new IllegalArgumentException("Cannot insert null into the heap."); + } + + this.data.add(item); + map.put(item, this.data.size() - 1); + upHeapify(this.data.size() - 1); + } + + /** + * Restores the heap property by moving the item at the given index upwards. + * + * @param ci the index of the current item + */ + private void upHeapify(int ci) { + int pi = (ci - 1) / 2; + if (ci > 0 && isLarger(this.data.get(ci), this.data.get(pi)) > 0) { + swap(pi, ci); + upHeapify(pi); + } + } + + /** + * Returns the number of elements in the heap. + * + * @return the size of the heap + */ + public int size() { + return this.data.size(); + } + + /** + * Checks if the heap is empty. + * + * @return true if the heap is empty, false otherwise + */ + public boolean isEmpty() { + return this.size() == 0; + } + + /** + * Removes and returns the maximum item from the heap. + * + * @return the maximum item + */ + public T remove() { + if (isEmpty()) { + throw new IllegalStateException("Heap is empty"); + } + this.swap(0, this.size() - 1); + T rv = this.data.remove(this.size() - 1); + map.remove(rv); + downHeapify(0); + return rv; + } + + /** + * Restores the heap property by moving the item at the given index downwards. + * + * @param pi the index of the current item + */ + private void downHeapify(int pi) { + int lci = 2 * pi + 1; + int rci = 2 * pi + 2; + int mini = pi; + if (lci < this.size() && isLarger(this.data.get(lci), this.data.get(mini)) > 0) { + mini = lci; + } + if (rci < this.size() && isLarger(this.data.get(rci), this.data.get(mini)) > 0) { + mini = rci; + } + if (mini != pi) { + this.swap(pi, mini); + downHeapify(mini); + } + } + + /** + * Retrieves the maximum item from the heap without removing it. + * + * @return the maximum item + */ + public T get() { + if (isEmpty()) { + throw new IllegalStateException("Heap is empty"); + } + return this.data.getFirst(); + } + + /** + * Compares two items to determine their order. + * + * @param t the first item + * @param o the second item + * @return a positive integer if t is greater than o, negative if t is less, and zero if they are equal + */ + private int isLarger(T t, T o) { + return t.compareTo(o); + } + + /** + * Swaps two items in the heap and updates their indices in the map. + * + * @param i index of the first item + * @param j index of the second item + */ + private void swap(int i, int j) { + T ith = this.data.get(i); + T jth = this.data.get(j); + this.data.set(i, jth); + this.data.set(j, ith); + map.put(ith, j); + map.put(jth, i); + } + + /** + * Updates the priority of the specified item by restoring the heap property. + * + * @param item the item whose priority is to be updated + */ + public void updatePriority(T item) { + if (!map.containsKey(item)) { + throw new IllegalArgumentException("Item not found in the heap"); + } + int index = map.get(item); + upHeapify(index); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/heaps/Heap.java b/src/main/java/com/thealgorithms/datastructures/heaps/Heap.java new file mode 100644 index 000000000000..8cb93edf78f3 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/heaps/Heap.java @@ -0,0 +1,44 @@ +package com.thealgorithms.datastructures.heaps; + +/** + * Interface common to heap data structures.<br> + * + * <p> + * Heaps are tree-like data structures that allow storing elements in a specific + * way. Each node corresponds to an element and has one parent node (except for + * the root) and at most two children nodes. Every element contains a key, and + * those keys indicate how the tree shall be built. For instance, for a + * min-heap, the key of a node shall be greater than or equal to its parent's + * and lower than or equal to its children's (the opposite rule applies to a + * max-heap). + * + * <p> + * All heap-related operations (inserting or deleting an element, extracting the + * min or max) are performed in O(log n) time. + * + * @author Nicolas Renard + */ +public interface Heap { + /** + * @return the top element in the heap, the one with lowest key for min-heap + * or with the highest key for max-heap + * @throws EmptyHeapException if heap is empty + */ + HeapElement getElement() throws EmptyHeapException; + + /** + * Inserts an element in the heap. Adds it to then end and toggle it until + * it finds its right position. + * + * @param element an instance of the HeapElement class. + */ + void insertElement(HeapElement element); + + /** + * Delete an element in the heap. + * + * @param elementIndex int containing the position in the heap of the + * element to be deleted. + */ + void deleteElement(int elementIndex) throws EmptyHeapException; +} diff --git a/src/main/java/com/thealgorithms/datastructures/heaps/HeapElement.java b/src/main/java/com/thealgorithms/datastructures/heaps/HeapElement.java new file mode 100644 index 000000000000..a9cfac7036eb --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/heaps/HeapElement.java @@ -0,0 +1,173 @@ +package com.thealgorithms.datastructures.heaps; + +/** + * Class representing an element in a heap. + * + * <p> + * A heap element contains two attributes: a key used for ordering in the heap + * (which can be of type int or double, either as primitive types or as wrapper objects) + * and an additional immutable object that can store any supplementary information the user desires. + * Note that using mutable objects may compromise the integrity of this information. + * </p> + * + * <p> + * The key attribute is used to determine the order of elements in the heap, + * while the additionalInfo attribute can carry user-defined data associated with the key. + * </p> + * + * <p> + * This class provides multiple constructors to accommodate various key types and includes + * methods to retrieve the key and additional information. + * </p> + * + * @author Nicolas Renard + */ +public class HeapElement { + + private final double key; + private final Object additionalInfo; + + // Constructors + /** + * Creates a HeapElement with the specified key and additional information. + * + * @param key the key of the element (primitive type double) + * @param info any immutable object containing additional information, may be null + */ + public HeapElement(double key, Object info) { + this.key = key; + this.additionalInfo = info; + } + + /** + * Creates a HeapElement with the specified key and additional information. + * + * @param key the key of the element (primitive type int) + * @param info any immutable object containing additional information, may be null + */ + public HeapElement(int key, Object info) { + this.key = key; + this.additionalInfo = info; + } + + /** + * Creates a HeapElement with the specified key and additional information. + * + * @param key the key of the element (object type Integer) + * @param info any immutable object containing additional information, may be null + */ + public HeapElement(Integer key, Object info) { + this.key = key; + this.additionalInfo = info; + } + + /** + * Creates a HeapElement with the specified key and additional information. + * + * @param key the key of the element (object type Double) + * @param info any immutable object containing additional information, may be null + */ + public HeapElement(Double key, Object info) { + this.key = key; + this.additionalInfo = info; + } + + /** + * Creates a HeapElement with the specified key. + * + * @param key the key of the element (primitive type double) + */ + public HeapElement(double key) { + this.key = key; + this.additionalInfo = null; + } + + /** + * Creates a HeapElement with the specified key. + * + * @param key the key of the element (primitive type int) + */ + public HeapElement(int key) { + this.key = key; + this.additionalInfo = null; + } + + /** + * Creates a HeapElement with the specified key. + * + * @param key the key of the element (object type Integer) + */ + public HeapElement(Integer key) { + this.key = key; + this.additionalInfo = null; + } + + /** + * Creates a HeapElement with the specified key. + * + * @param key the key of the element (object type Double) + */ + public HeapElement(Double key) { + this.key = key; + this.additionalInfo = null; + } + + // Getters + /** + * Returns the object containing the additional information provided by the user. + * + * @return the additional information + */ + public Object getInfo() { + return additionalInfo; + } + + /** + * Returns the key value of the element. + * + * @return the key of the element + */ + public double getKey() { + return key; + } + + // Overridden object methods + /** + * Returns a string representation of the heap element. + * + * @return a string describing the key and additional information + */ + @Override + public String toString() { + return "Key: " + key + " - " + (additionalInfo != null ? additionalInfo.toString() : "No additional info"); + } + + /** + * @param o : an object to compare with the current element + * @return true if the keys on both elements are identical and the + * additional info objects are identical. + */ + @Override + public boolean equals(Object o) { + if (o instanceof HeapElement otherHeapElement) { + return this.key == otherHeapElement.key && (this.additionalInfo != null ? this.additionalInfo.equals(otherHeapElement.additionalInfo) : otherHeapElement.additionalInfo == null); + } + return false; + } + + /** + * Returns a hash code value for the heap element. + * + * @return a hash code value for this heap element + */ + @Override + public int hashCode() { + int result = 31 * (int) key; + result += (additionalInfo != null) ? additionalInfo.hashCode() : 0; + return result; + } + + public String getValue() { + return additionalInfo.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/heaps/KthElementFinder.java b/src/main/java/com/thealgorithms/datastructures/heaps/KthElementFinder.java new file mode 100644 index 000000000000..7ad92e8ba3c1 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/heaps/KthElementFinder.java @@ -0,0 +1,60 @@ + +package com.thealgorithms.datastructures.heaps; + +import java.util.PriorityQueue; + +/** + * This class provides methods to find the Kth largest or Kth smallest element + * in an array using heaps. It leverages a min-heap to find the Kth largest element + * and a max-heap to find the Kth smallest element efficiently. + * + * @author Hardvan + */ +public final class KthElementFinder { + private KthElementFinder() { + } + + /** + * Finds the Kth largest element in the given array. + * Uses a min-heap of size K to track the largest K elements. + * + * Time Complexity: O(n * log(k)), where n is the size of the input array. + * Space Complexity: O(k), as we maintain a heap of size K. + * + * @param nums the input array of integers + * @param k the desired Kth position (1-indexed, i.e., 1 means the largest element) + * @return the Kth largest element in the array + */ + public static int findKthLargest(int[] nums, int k) { + PriorityQueue<Integer> minHeap = new PriorityQueue<>(k); + for (int num : nums) { + minHeap.offer(num); + if (minHeap.size() > k) { + minHeap.poll(); + } + } + return minHeap.peek(); + } + + /** + * Finds the Kth smallest element in the given array. + * Uses a max-heap of size K to track the smallest K elements. + * + * Time Complexity: O(n * log(k)), where n is the size of the input array. + * Space Complexity: O(k), as we maintain a heap of size K. + * + * @param nums the input array of integers + * @param k the desired Kth position (1-indexed, i.e., 1 means the smallest element) + * @return the Kth smallest element in the array + */ + public static int findKthSmallest(int[] nums, int k) { + PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a); + for (int num : nums) { + maxHeap.offer(num); + if (maxHeap.size() > k) { + maxHeap.poll(); + } + } + return maxHeap.peek(); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/heaps/LeftistHeap.java b/src/main/java/com/thealgorithms/datastructures/heaps/LeftistHeap.java new file mode 100644 index 000000000000..1c91d24f0fb5 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/heaps/LeftistHeap.java @@ -0,0 +1,164 @@ +package com.thealgorithms.datastructures.heaps; + +import java.util.ArrayList; + +/** + * This class implements a Leftist Heap, which is a type of priority queue + * that follows similar operations to a binary min-heap but allows for + * unbalanced structures based on the leftist property. + * + * <p> + * A Leftist Heap maintains the leftist property, which ensures that the + * left subtree is heavier than the right subtree based on the + * null-path length (npl) values. This allows for efficient merging + * of heaps and supports operations like insertion, extraction of + * the minimum element, and in-order traversal. + * </p> + * + * <p> + * For more information on Leftist Heaps, visit: + * <a href="https://iq.opengenus.org/leftist-heap/">OpenGenus</a> + * </p> + */ +public class LeftistHeap { + // Node class representing each element in the Leftist Heap + private static final class Node { + private final int element; + private int npl; + private Node left; + private Node right; + + // Node constructor that initializes the element and sets child pointers to null + private Node(int element) { + this.element = element; + left = null; + right = null; + npl = 0; + } + } + + private Node root; + + // Constructor initializing an empty Leftist Heap + public LeftistHeap() { + root = null; + } + + /** + * Checks if the heap is empty. + * + * @return true if the heap is empty; false otherwise + */ + public boolean isEmpty() { + return root == null; + } + + /** + * Resets the heap to its initial state, effectively clearing all elements. + */ + public void clear() { + root = null; // Set root to null to clear the heap + } + + /** + * Merges the contents of another Leftist Heap into this one. + * + * @param h1 the LeftistHeap to be merged into this heap + */ + public void merge(LeftistHeap h1) { + // Merge the current heap with the provided heap and set the provided heap's root to null + root = merge(root, h1.root); + h1.root = null; + } + + /** + * Merges two nodes, maintaining the leftist property. + * + * @param a the first node + * @param b the second node + * @return the merged node maintaining the leftist property + */ + public Node merge(Node a, Node b) { + if (a == null) { + return b; + } + + if (b == null) { + return a; + } + + // Ensure that the leftist property is maintained + if (a.element > b.element) { + Node temp = a; + a = b; + b = temp; + } + + // Merge the right child of node a with node b + a.right = merge(a.right, b); + + // If left child is null, make right child the left child + if (a.left == null) { + a.left = a.right; + a.right = null; + } else { + if (a.left.npl < a.right.npl) { + Node temp = a.left; + a.left = a.right; + a.right = temp; + } + a.npl = a.right.npl + 1; + } + return a; + } + + /** + * Inserts a new element into the Leftist Heap. + * + * @param a the element to be inserted + */ + public void insert(int a) { + root = merge(new Node(a), root); + } + + /** + * Extracts and removes the minimum element from the heap. + * + * @return the minimum element in the heap, or -1 if the heap is empty + */ + public int extractMin() { + if (isEmpty()) { + return -1; + } + + int min = root.element; + root = merge(root.left, root.right); + return min; + } + + /** + * Returns a list of the elements in the heap in in-order traversal. + * + * @return an ArrayList containing the elements in in-order + */ + public ArrayList<Integer> inOrder() { + ArrayList<Integer> lst = new ArrayList<>(); + inOrderAux(root, lst); + return new ArrayList<>(lst); + } + + /** + * Auxiliary function for in-order traversal + * + * @param n the current node + * @param lst the list to store the elements in in-order + */ + private void inOrderAux(Node n, ArrayList<Integer> lst) { + if (n == null) { + return; + } + inOrderAux(n.left, lst); + lst.add(n.element); + inOrderAux(n.right, lst); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/heaps/MaxHeap.java b/src/main/java/com/thealgorithms/datastructures/heaps/MaxHeap.java new file mode 100644 index 000000000000..5b4b29cf1c2d --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/heaps/MaxHeap.java @@ -0,0 +1,248 @@ +package com.thealgorithms.datastructures.heaps; + +import java.util.ArrayList; +import java.util.List; + +/** + * A Max Heap implementation where each node's key is higher than or equal to its children's keys. + * This data structure provides O(log n) time complexity for insertion and deletion operations, + * and O(1) for retrieving the maximum element. + * + * Properties: + * 1. Complete Binary Tree + * 2. Parent node's key ≥ Children nodes' keys + * 3. Root contains the maximum element + * + * Example usage: + * <pre> + * List<HeapElement> elements = Arrays.asList( + * new HeapElement(5, "Five"), + * new HeapElement(2, "Two") + * ); + * MaxHeap heap = new MaxHeap(elements); + * heap.insertElement(new HeapElement(7, "Seven")); + * HeapElement max = heap.getElement(); // Returns and removes the maximum element + * </pre> + * + * @author Nicolas Renard + */ +public class MaxHeap implements Heap { + + /** The internal list that stores heap elements */ + private final List<HeapElement> maxHeap; + + /** + * Constructs a new MaxHeap from a list of elements. + * Null elements in the input list are ignored. + * + * @param listElements List of HeapElement objects to initialize the heap + * @throws IllegalArgumentException if the input list is null + */ + public MaxHeap(List<HeapElement> listElements) { + if (listElements == null) { + throw new IllegalArgumentException("Input list cannot be null"); + } + + maxHeap = new ArrayList<>(); + + // Safe initialization: directly add non-null elements first + for (HeapElement heapElement : listElements) { + if (heapElement != null) { + maxHeap.add(heapElement); + } + } + + // Heapify the array bottom-up + for (int i = maxHeap.size() / 2; i >= 0; i--) { + heapifyDown(i + 1); // +1 because heapifyDown expects 1-based index + } + } + + /** + * Maintains heap properties by moving an element down the heap. + * Similar to toggleDown but used specifically during initialization. + * + * @param elementIndex 1-based index of the element to heapify + */ + private void heapifyDown(int elementIndex) { + int largest = elementIndex - 1; + int leftChild = 2 * elementIndex - 1; + int rightChild = 2 * elementIndex; + + if (leftChild < maxHeap.size() && maxHeap.get(leftChild).getKey() > maxHeap.get(largest).getKey()) { + largest = leftChild; + } + + if (rightChild < maxHeap.size() && maxHeap.get(rightChild).getKey() > maxHeap.get(largest).getKey()) { + largest = rightChild; + } + + if (largest != elementIndex - 1) { + HeapElement swap = maxHeap.get(elementIndex - 1); + maxHeap.set(elementIndex - 1, maxHeap.get(largest)); + maxHeap.set(largest, swap); + + heapifyDown(largest + 1); + } + } + + /** + * Retrieves the element at the specified index without removing it. + * Note: The index is 1-based for consistency with heap operations. + * + * @param elementIndex 1-based index of the element to retrieve + * @return HeapElement at the specified index + * @throws IndexOutOfBoundsException if the index is invalid + */ + public HeapElement getElement(int elementIndex) { + if ((elementIndex <= 0) || (elementIndex > maxHeap.size())) { + throw new IndexOutOfBoundsException("Index " + elementIndex + " is out of heap range [1, " + maxHeap.size() + "]"); + } + return maxHeap.get(elementIndex - 1); + } + + /** + * Retrieves the key value of an element at the specified index. + * + * @param elementIndex 1-based index of the element + * @return double value representing the key + * @throws IndexOutOfBoundsException if the index is invalid + */ + private double getElementKey(int elementIndex) { + if ((elementIndex <= 0) || (elementIndex > maxHeap.size())) { + throw new IndexOutOfBoundsException("Index " + elementIndex + " is out of heap range [1, " + maxHeap.size() + "]"); + } + return maxHeap.get(elementIndex - 1).getKey(); + } + + /** + * Swaps two elements in the heap. + * + * @param index1 1-based index of first element + * @param index2 1-based index of second element + */ + private void swap(int index1, int index2) { + HeapElement temporaryElement = maxHeap.get(index1 - 1); + maxHeap.set(index1 - 1, maxHeap.get(index2 - 1)); + maxHeap.set(index2 - 1, temporaryElement); + } + + /** + * Moves an element up the heap until heap properties are satisfied. + * This operation is called after insertion to maintain heap properties. + * + * @param elementIndex 1-based index of the element to move up + */ + private void toggleUp(int elementIndex) { + double key = maxHeap.get(elementIndex - 1).getKey(); + while (elementIndex > 1 && getElementKey((int) Math.floor(elementIndex / 2.0)) < key) { + swap(elementIndex, (int) Math.floor(elementIndex / 2.0)); + elementIndex = (int) Math.floor(elementIndex / 2.0); + } + } + + /** + * Moves an element down the heap until heap properties are satisfied. + * This operation is called after deletion to maintain heap properties. + * + * @param elementIndex 1-based index of the element to move down + */ + private void toggleDown(int elementIndex) { + double key = maxHeap.get(elementIndex - 1).getKey(); + boolean wrongOrder = (2 * elementIndex <= maxHeap.size() && key < getElementKey(elementIndex * 2)) || (2 * elementIndex + 1 <= maxHeap.size() && key < getElementKey(elementIndex * 2 + 1)); + + while (2 * elementIndex <= maxHeap.size() && wrongOrder) { + int largerChildIndex; + if (2 * elementIndex + 1 <= maxHeap.size() && getElementKey(elementIndex * 2 + 1) > getElementKey(elementIndex * 2)) { + largerChildIndex = 2 * elementIndex + 1; + } else { + largerChildIndex = 2 * elementIndex; + } + + swap(elementIndex, largerChildIndex); + elementIndex = largerChildIndex; + + wrongOrder = (2 * elementIndex <= maxHeap.size() && key < getElementKey(elementIndex * 2)) || (2 * elementIndex + 1 <= maxHeap.size() && key < getElementKey(elementIndex * 2 + 1)); + } + } + + /** + * Extracts and returns the maximum element from the heap. + * + * @return HeapElement with the highest key + * @throws EmptyHeapException if the heap is empty + */ + private HeapElement extractMax() throws EmptyHeapException { + if (maxHeap.isEmpty()) { + throw new EmptyHeapException("Cannot extract from an empty heap"); + } + HeapElement result = maxHeap.getFirst(); + deleteElement(1); + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public void insertElement(HeapElement element) { + if (element == null) { + throw new IllegalArgumentException("Cannot insert null element"); + } + maxHeap.add(element); + toggleUp(maxHeap.size()); + } + + /** + * {@inheritDoc} + */ + @Override + public void deleteElement(int elementIndex) throws EmptyHeapException { + if (maxHeap.isEmpty()) { + throw new EmptyHeapException("Cannot delete from an empty heap"); + } + if ((elementIndex > maxHeap.size()) || (elementIndex <= 0)) { + throw new IndexOutOfBoundsException("Index " + elementIndex + " is out of heap range [1, " + maxHeap.size() + "]"); + } + + // Replace with last element and remove last position + maxHeap.set(elementIndex - 1, maxHeap.getLast()); + maxHeap.removeLast(); + + // No need to toggle if we just removed the last element + if (!maxHeap.isEmpty() && elementIndex <= maxHeap.size()) { + // Determine whether to toggle up or down + if (elementIndex > 1 && getElementKey(elementIndex) > getElementKey((int) Math.floor(elementIndex / 2.0))) { + toggleUp(elementIndex); + } else { + toggleDown(elementIndex); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public HeapElement getElement() throws EmptyHeapException { + return extractMax(); + } + + /** + * Returns the current size of the heap. + * + * @return number of elements in the heap + */ + public int size() { + return maxHeap.size(); + } + + /** + * Checks if the heap is empty. + * + * @return true if the heap contains no elements + */ + public boolean isEmpty() { + return maxHeap.isEmpty(); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/heaps/MedianFinder.java b/src/main/java/com/thealgorithms/datastructures/heaps/MedianFinder.java new file mode 100644 index 000000000000..4e74aaec4a10 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/heaps/MedianFinder.java @@ -0,0 +1,59 @@ +package com.thealgorithms.datastructures.heaps; + +import java.util.PriorityQueue; + +/** + * This class maintains the median of a dynamically changing data stream using + * two heaps: a max-heap and a min-heap. The max-heap stores the smaller half + * of the numbers, and the min-heap stores the larger half. + * This data structure ensures that retrieving the median is efficient. + * + * Time Complexity: + * - Adding a number: O(log n) due to heap insertion. + * - Finding the median: O(1). + * + * Space Complexity: O(n), where n is the total number of elements added. + * + * @author Hardvan + */ +public final class MedianFinder { + MedianFinder() { + } + + private PriorityQueue<Integer> minHeap = new PriorityQueue<>(); + private PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a); + + /** + * Adds a new number to the data stream. The number is placed in the appropriate + * heap to maintain the balance between the two heaps. + * + * @param num the number to be added to the data stream + */ + public void addNum(int num) { + if (maxHeap.isEmpty() || num <= maxHeap.peek()) { + maxHeap.offer(num); + } else { + minHeap.offer(num); + } + + if (maxHeap.size() > minHeap.size() + 1) { + minHeap.offer(maxHeap.poll()); + } else if (minHeap.size() > maxHeap.size()) { + maxHeap.offer(minHeap.poll()); + } + } + + /** + * Finds the median of the numbers added so far. If the total number of elements + * is even, the median is the average of the two middle elements. If odd, the + * median is the middle element from the max-heap. + * + * @return the median of the numbers in the data stream + */ + public double findMedian() { + if (maxHeap.size() == minHeap.size()) { + return (maxHeap.peek() + minHeap.peek()) / 2.0; + } + return maxHeap.peek(); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/heaps/MergeKSortedArrays.java b/src/main/java/com/thealgorithms/datastructures/heaps/MergeKSortedArrays.java new file mode 100644 index 000000000000..e41711f05914 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/heaps/MergeKSortedArrays.java @@ -0,0 +1,60 @@ +package com.thealgorithms.datastructures.heaps; + +import java.util.Comparator; +import java.util.PriorityQueue; + +/** + * This class provides a method to merge multiple sorted arrays into a single sorted array. + * It utilizes a min-heap to efficiently retrieve the smallest elements from each array. + * + * Time Complexity: O(n * log k), where n is the total number of elements across all arrays + * and k is the number of arrays. + * + * Space Complexity: O(k) for the heap, where k is the number of arrays. + * + * @author Hardvan + */ +public final class MergeKSortedArrays { + private MergeKSortedArrays() { + } + + /** + * Merges k sorted arrays into one sorted array using a min-heap. + * Steps: + * 1. Create a min-heap to store elements in the format: {value, array index, element index} + * 2. Add the first element from each array to the heap + * 3. While the heap is not empty, remove the smallest element from the heap + * and add it to the result array. If there are more elements in the same array, + * add the next element to the heap. + * Continue until all elements have been processed. + * The result array will contain all elements in sorted order. + * 4. Return the result array. + * + * @param arrays a 2D array, where each subarray is sorted in non-decreasing order + * @return a single sorted array containing all elements from the input arrays + */ + public static int[] mergeKArrays(int[][] arrays) { + PriorityQueue<int[]> minHeap = new PriorityQueue<>(Comparator.comparingInt(a -> a[0])); + + int totalLength = 0; + for (int i = 0; i < arrays.length; i++) { + if (arrays[i].length > 0) { + minHeap.offer(new int[] {arrays[i][0], i, 0}); + totalLength += arrays[i].length; + } + } + + int[] result = new int[totalLength]; + int index = 0; + while (!minHeap.isEmpty()) { + int[] top = minHeap.poll(); + result[index++] = top[0]; + + if (top[2] + 1 < arrays[top[1]].length) { + minHeap.offer(new int[] {arrays[top[1]][top[2] + 1], top[1], top[2] + 1}); + } + } + + return result; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/heaps/MinHeap.java b/src/main/java/com/thealgorithms/datastructures/heaps/MinHeap.java new file mode 100644 index 000000000000..3a4822142b5f --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/heaps/MinHeap.java @@ -0,0 +1,271 @@ +package com.thealgorithms.datastructures.heaps; + +import java.util.ArrayList; +import java.util.List; + +/** + * A Min Heap implementation where each node's key is lower than or equal to its children's keys. + * This data structure provides O(log n) time complexity for insertion and deletion operations, + * and O(1) for retrieving the minimum element. + * + * Properties: + * 1. Complete Binary Tree + * 2. Parent node's key ≤ Children nodes' keys + * 3. Root contains the minimum element + * + * Example usage: + * ```java + * List<HeapElement> elements = Arrays.asList( + * new HeapElement(5, "Five"), + * new HeapElement(2, "Two") + * ); + * MinHeap heap = new MinHeap(elements); + * heap.insertElement(new HeapElement(1, "One")); + * HeapElement min = heap.getElement(); // Returns and removes the minimum element + * ``` + * + * @author Nicolas Renard + */ +public class MinHeap implements Heap { + + private final List<HeapElement> minHeap; + + /** + * Constructs a new MinHeap from a list of elements. + * Null elements in the input list are ignored with a warning message. + * + * @param listElements List of HeapElement objects to initialize the heap + * @throws IllegalArgumentException if the input list is null + */ + public MinHeap(List<HeapElement> listElements) { + if (listElements == null) { + throw new IllegalArgumentException("Input list cannot be null"); + } + + minHeap = new ArrayList<>(); + + // Safe initialization: directly add elements first + for (HeapElement heapElement : listElements) { + if (heapElement != null) { + minHeap.add(heapElement); + } else { + System.out.println("Null element. Not added to heap"); + } + } + + // Heapify the array bottom-up + for (int i = minHeap.size() / 2; i >= 0; i--) { + heapifyDown(i + 1); + } + + if (minHeap.isEmpty()) { + System.out.println("No element has been added, empty heap."); + } + } + + /** + * Retrieves the element at the specified index without removing it. + * Note: The index is 1-based for consistency with heap operations. + * + * @param elementIndex 1-based index of the element to retrieve + * @return HeapElement at the specified index + * @throws IndexOutOfBoundsException if the index is invalid + */ + public HeapElement getElement(int elementIndex) { + if ((elementIndex <= 0) || (elementIndex > minHeap.size())) { + throw new IndexOutOfBoundsException("Index " + elementIndex + " is out of heap range [1, " + minHeap.size() + "]"); + } + return minHeap.get(elementIndex - 1); + } + + /** + * Retrieves the key value of an element at the specified index. + * + * @param elementIndex 1-based index of the element + * @return double value representing the key + * @throws IndexOutOfBoundsException if the index is invalid + */ + private double getElementKey(int elementIndex) { + if ((elementIndex <= 0) || (elementIndex > minHeap.size())) { + throw new IndexOutOfBoundsException("Index " + elementIndex + " is out of heap range [1, " + minHeap.size() + "]"); + } + return minHeap.get(elementIndex - 1).getKey(); + } + + /** + * Swaps two elements in the heap. + * + * @param index1 1-based index of first element + * @param index2 1-based index of second element + */ + private void swap(int index1, int index2) { + HeapElement temporaryElement = minHeap.get(index1 - 1); + minHeap.set(index1 - 1, minHeap.get(index2 - 1)); + minHeap.set(index2 - 1, temporaryElement); + } + + /** + * Maintains heap properties by moving an element down the heap. + * Used specifically during initialization. + * + * @param elementIndex 1-based index of the element to heapify + */ + private void heapifyDown(int elementIndex) { + int smallest = elementIndex - 1; // Convert to 0-based index + int leftChild = 2 * elementIndex - 1; + int rightChild = 2 * elementIndex; + + // Check if left child is smaller than root + if (leftChild < minHeap.size() && minHeap.get(leftChild).getKey() < minHeap.get(smallest).getKey()) { + smallest = leftChild; + } + + // Check if right child is smaller than smallest so far + if (rightChild < minHeap.size() && minHeap.get(rightChild).getKey() < minHeap.get(smallest).getKey()) { + smallest = rightChild; + } + + // If smallest is not root + if (smallest != elementIndex - 1) { + HeapElement swap = minHeap.get(elementIndex - 1); + minHeap.set(elementIndex - 1, minHeap.get(smallest)); + minHeap.set(smallest, swap); + + // Recursively heapify the affected sub-tree + heapifyDown(smallest + 1); // Convert back to 1-based index + } + } + + /** + * Moves an element up the heap until heap properties are satisfied. + * This operation is called after insertion to maintain heap properties. + * + * @param elementIndex 1-based index of the element to move up + */ + private void toggleUp(int elementIndex) { + if (elementIndex <= 1) { + return; + } + + double key = minHeap.get(elementIndex - 1).getKey(); + int parentIndex = (int) Math.floor(elementIndex / 2.0); + + while (elementIndex > 1 && getElementKey(parentIndex) > key) { + swap(elementIndex, parentIndex); + elementIndex = parentIndex; + parentIndex = (int) Math.floor(elementIndex / 2.0); + } + } + + /** + * Moves an element down the heap until heap properties are satisfied. + * This operation is called after deletion to maintain heap properties. + * + * @param elementIndex 1-based index of the element to move down + */ + private void toggleDown(int elementIndex) { + double key = minHeap.get(elementIndex - 1).getKey(); + int size = minHeap.size(); + + while (true) { + int smallest = elementIndex; + int leftChild = 2 * elementIndex; + int rightChild = 2 * elementIndex + 1; + + if (leftChild <= size && getElementKey(leftChild) < key) { + smallest = leftChild; + } + + if (rightChild <= size && getElementKey(rightChild) < getElementKey(smallest)) { + smallest = rightChild; + } + + if (smallest == elementIndex) { + break; + } + + swap(elementIndex, smallest); + elementIndex = smallest; + } + } + + /** + * Extracts and returns the minimum element from the heap. + * + * @return HeapElement with the lowest key + * @throws EmptyHeapException if the heap is empty + */ + private HeapElement extractMin() throws EmptyHeapException { + if (minHeap.isEmpty()) { + throw new EmptyHeapException("Cannot extract from empty heap"); + } + HeapElement result = minHeap.getFirst(); + deleteElement(1); + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public void insertElement(HeapElement element) { + if (element == null) { + throw new IllegalArgumentException("Cannot insert null element"); + } + minHeap.add(element); + toggleUp(minHeap.size()); + } + + /** + * {@inheritDoc} + */ + @Override + public void deleteElement(int elementIndex) throws EmptyHeapException { + if (minHeap.isEmpty()) { + throw new EmptyHeapException("Cannot delete from empty heap"); + } + if ((elementIndex > minHeap.size()) || (elementIndex <= 0)) { + throw new IndexOutOfBoundsException("Index " + elementIndex + " is out of heap range [1, " + minHeap.size() + "]"); + } + + // Replace with last element and remove last position + minHeap.set(elementIndex - 1, minHeap.getLast()); + minHeap.removeLast(); + + // No need to toggle if we just removed the last element + if (!minHeap.isEmpty() && elementIndex <= minHeap.size()) { + // Determine whether to toggle up or down + if (elementIndex > 1 && getElementKey(elementIndex) < getElementKey((int) Math.floor(elementIndex / 2.0))) { + toggleUp(elementIndex); + } else { + toggleDown(elementIndex); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public HeapElement getElement() throws EmptyHeapException { + return extractMin(); + } + + /** + * Returns the current size of the heap. + * + * @return number of elements in the heap + */ + public int size() { + return minHeap.size(); + } + + /** + * Checks if the heap is empty. + * + * @return true if the heap contains no elements + */ + public boolean isEmpty() { + return minHeap.isEmpty(); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/heaps/MinPriorityQueue.java b/src/main/java/com/thealgorithms/datastructures/heaps/MinPriorityQueue.java new file mode 100644 index 000000000000..a1360b14dc5a --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/heaps/MinPriorityQueue.java @@ -0,0 +1,155 @@ +package com.thealgorithms.datastructures.heaps; + +/** + * A MinPriorityQueue is a specialized data structure that maintains the + * min-heap property, where the smallest element has the highest priority. + * + * <p>In a min-priority queue, every parent node is less than or equal + * to its child nodes, which ensures that the smallest element can + * always be efficiently retrieved.</p> + * + * <p>Functions:</p> + * <ul> + * <li><b>insert(int key)</b>: Inserts a new key into the queue.</li> + * <li><b>delete()</b>: Removes and returns the highest priority value (the minimum).</li> + * <li><b>peek()</b>: Returns the highest priority value without removing it.</li> + * <li><b>isEmpty()</b>: Checks if the queue is empty.</li> + * <li><b>isFull()</b>: Checks if the queue is full.</li> + * <li><b>heapSort()</b>: Sorts the elements in ascending order.</li> + * <li><b>print()</b>: Prints the current elements in the queue.</li> + * </ul> + */ +public class MinPriorityQueue { + + private final int[] heap; + private final int capacity; + private int size; + + /** + * Initializes a new MinPriorityQueue with a specified capacity. + * + * @param c the maximum number of elements the queue can hold + */ + public MinPriorityQueue(int c) { + this.capacity = c; + this.size = 0; + this.heap = new int[c + 1]; + } + + /** + * Inserts a new key into the min-priority queue. + * + * @param key the value to be inserted + */ + public void insert(int key) { + if (this.isFull()) { + throw new IllegalStateException("MinPriorityQueue is full. Cannot insert new element."); + } + this.heap[this.size + 1] = key; + int k = this.size + 1; + while (k > 1) { + if (this.heap[k] < this.heap[k / 2]) { + int temp = this.heap[k]; + this.heap[k] = this.heap[k / 2]; + this.heap[k / 2] = temp; + } + k = k / 2; + } + this.size++; + } + + /** + * Retrieves the highest priority value (the minimum) without removing it. + * + * @return the minimum value in the queue + * @throws IllegalStateException if the queue is empty + */ + public int peek() { + if (isEmpty()) { + throw new IllegalStateException("MinPriorityQueue is empty. Cannot peek."); + } + return this.heap[1]; + } + + /** + * Checks whether the queue is empty. + * + * @return true if the queue is empty, false otherwise + */ + public boolean isEmpty() { + return size == 0; + } + + /** + * Checks whether the queue is full. + * + * @return true if the queue is full, false otherwise + */ + public boolean isFull() { + return size == capacity; + } + + /** + * Prints the elements of the queue. + */ + public void print() { + for (int i = 1; i <= this.size; i++) { + System.out.print(this.heap[i] + " "); + } + System.out.println(); + } + + /** + * Sorts the elements in the queue using heap sort. + */ + public void heapSort() { + for (int i = 1; i <= this.size; i++) { + this.delete(); + } + } + + /** + * Reorders the heap after a deletion to maintain the heap property. + */ + private void sink() { + int k = 1; + while (2 * k <= this.size) { + int minIndex = k; // Assume current index is the minimum + + if (2 * k <= this.size && this.heap[2 * k] < this.heap[minIndex]) { + minIndex = 2 * k; // Left child is smaller + } + if (2 * k + 1 <= this.size && this.heap[2 * k + 1] < this.heap[minIndex]) { + minIndex = 2 * k + 1; // Right child is smaller + } + + if (minIndex == k) { + break; // No swap needed, heap property is satisfied + } + + // Swap with the smallest child + int temp = this.heap[k]; + this.heap[k] = this.heap[minIndex]; + this.heap[minIndex] = temp; + + k = minIndex; // Move down to the smallest child + } + } + + /** + * Deletes and returns the highest priority value (the minimum) from the queue. + * + * @return the minimum value from the queue + * @throws IllegalStateException if the queue is empty + */ + public int delete() { + if (isEmpty()) { + throw new IllegalStateException("MinPriorityQueue is empty. Cannot delete."); + } + int min = this.heap[1]; + this.heap[1] = this.heap[this.size]; // Move last element to the root + this.size--; + this.sink(); + return min; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/heaps/Readme.md b/src/main/java/com/thealgorithms/datastructures/heaps/Readme.md new file mode 100644 index 000000000000..5e77c68dee8c --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/heaps/Readme.md @@ -0,0 +1,128 @@ +<b><h1 align=center> HEAP DATA STRUCTURE</h1></b> +<p>A Heap is a special Tree-based data structure in which the tree is a complete binary tree. + +## <h2>Complete Binary Tree</h2> +<p>A complete binary tree is a binary tree +in which all the levels except the last level, i.e., leaf node should be completely filled, and all the nodes should be left-justified.</p> + + +``` + 10 + / \ + 20 30 + / \ + 40 50 + + COMPLETE BINARY TREE +``` + + +## <h2>Types of Heap</h2> +<p>Generally, Heaps can be of two types: +<br> +<strong>Max-Heap:</strong> In a Max-Heap the key present at the root node must be greatest among the keys present at all of it’s children. The same property must be recursively true for all sub-trees in that Binary Tree. +<br> +<strong>Min-Heap:</strong> In a Min-Heap the key present at the root node must be minimum among the keys present at all of it’s children. The same property must be recursively true for all sub-trees in that Binary Tree. +</p> + + + +``` + 10 + / \ + 20 30 + / \ / \ + 40 50 60 70 + + MIN HEAP +``` + +``` + 70 + / \ + 50 60 + / \ / \ + 40 30 10 20 + + MAX HEAP +``` + +## <h2>Min Heap Construction Algorithm</h2> +``` +Step 1 − Create a new node at the end of heap. +Step 2 − Assign new value to the node. +Step 3 − Compare the value of this child node with its parent. +Step 4 − If value of parent is more than child, then swap them. +Step 5 − Repeat step 3 & 4 until Heap property holds. +``` + +``` +Add 15 + + 10 10 10 + / \ / \ / \ + 20 30 ------> 20 30 ------> 20 15 + / \ / \ / / \ / + 40 50 40 50 15 40 50 30 + + +``` + +## <h2>Min Heap Deletion Algorithm</h2> +``` +Step 1 − Remove root node. +Step 2 − Move the last element of last level to root. +Step 3 − Compare the value of this child node with its parent. +Step 4 − If value of parent is more than child, then swap them. +Step 5 − Repeat step 3 & 4 until Heap property holds. +``` + +``` +Delete 10 + + 10 50 20 20 + / \ / \ / \ / \ + 20 30 ------> 20 30 ------> 50 30 ------> 40 30 + / \ / / / + 40 50 40 40 50 + + +``` + +## <h2>Time Complexity (Min Heap)</h2> +<table border=1> + <tr> + <th>Operations</th> + <th>Sorted Array</th> + <th>UnSorted Array</th> + <th>Heap</th> + </tr> + <tr> + <td>Add</td> + <td>O(N)</td> + <td>O(1)</td> + <td>O(logN)</td> + </tr> + <tr> + <td>Delete Minimum</td> + <td>O(N)</td> + <td>O(N)</td> + <td>O(logN)</td> + </tr> + <tr> + <td>Get Minimum</td> + <td>O(1)</td> + <td>O(N)</td> + <td>O(1)</td> + </tr> +</table> + +## <h2>Applications of Heap Data Structure</h2> + +<p> +<strong>Heapsort:</strong> Heapsort algorithm has limited uses because Quicksort is better in practice. Nevertheless, the Heap data structure itself is enormously used. + +<strong>Priority Queues:</strong> Priority queues can be efficiently implemented using Binary Heap because it supports insert(), delete() and extractmax(), decreaseKey() operations in O(logn) time. Binomoial Heap and Fibonacci Heap are variations of Binary Heap. These variations perform union also in O(logn) time which is a O(n) operation in Binary Heap. Heap Implemented priority queues are used in Graph algorithms like Prim’s Algorithm and Dijkstra’s algorithm. + +<strong>Order statistics:</strong> The Heap data structure can be used to efficiently find the kth smallest (or largest) element in an array. +</p> diff --git a/src/main/java/com/thealgorithms/datastructures/lists/CircleLinkedList.java b/src/main/java/com/thealgorithms/datastructures/lists/CircleLinkedList.java new file mode 100644 index 000000000000..422e8953625f --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/lists/CircleLinkedList.java @@ -0,0 +1,126 @@ +package com.thealgorithms.datastructures.lists; + +/** + * This class is a circular singly linked list implementation. In a circular linked list, + * the last node points back to the first node, creating a circular chain. + * + * <p>This implementation includes basic operations such as appending elements + * to the end, removing elements from a specified position, and converting + * the list to a string representation. + * + * @param <E> the type of elements held in this list + */ +public class CircleLinkedList<E> { + + /** + * A static nested class representing a node in the circular linked list. + * + * @param <E> the type of element stored in the node + */ + static final class Node<E> { + + Node<E> next; + E value; + + private Node(E value, Node<E> next) { + this.value = value; + this.next = next; + } + } + + private int size; + Node<E> head = null; + private Node<E> tail; + + /** + * Initializes a new circular linked list. A dummy head node is used for simplicity, + * pointing initially to itself to ensure the list is never empty. + */ + public CircleLinkedList() { + head = new Node<>(null, head); + tail = head; + size = 0; + } + + /** + * Returns the current size of the list. + * + * @return the number of elements in the list + */ + public int getSize() { + return size; + } + + /** + * Appends a new element to the end of the list. Throws a NullPointerException if + * a null value is provided. + * + * @param value the value to append to the list + * @throws NullPointerException if the value is null + */ + public void append(E value) { + if (value == null) { + throw new NullPointerException("Cannot add null element to the list"); + } + if (tail == null) { + tail = new Node<>(value, head); + head.next = tail; + } else { + tail.next = new Node<>(value, head); + tail = tail.next; + } + size++; + } + + /** + * Returns a string representation of the list in the format "[ element1, element2, ... ]". + * An empty list is represented as "[]". + * + * @return the string representation of the list + */ + public String toString() { + if (size == 0) { + return "[]"; + } + StringBuilder sb = new StringBuilder("[ "); + Node<E> current = head.next; + while (current != head) { + sb.append(current.value); + if (current.next != head) { + sb.append(", "); + } + current = current.next; + } + sb.append(" ]"); + return sb.toString(); + } + + /** + * Removes and returns the element at the specified position in the list. + * Throws an IndexOutOfBoundsException if the position is invalid. + * + * @param pos the position of the element to remove + * @return the value of the removed element + * @throws IndexOutOfBoundsException if the position is out of range + */ + public E remove(int pos) { + if (pos >= size || pos < 0) { + throw new IndexOutOfBoundsException("Position out of bounds"); + } + + Node<E> before = head; + for (int i = 1; i <= pos; i++) { + before = before.next; + } + Node<E> destroy = before.next; + E saved = destroy.value; + before.next = destroy.next; + + if (destroy == tail) { + tail = before; + } + destroy = null; + size--; + return saved; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/lists/CountSinglyLinkedListRecursion.java b/src/main/java/com/thealgorithms/datastructures/lists/CountSinglyLinkedListRecursion.java new file mode 100644 index 000000000000..b58d51e7e5fe --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/lists/CountSinglyLinkedListRecursion.java @@ -0,0 +1,29 @@ +package com.thealgorithms.datastructures.lists; + +/** + * CountSinglyLinkedListRecursion extends a singly linked list to include a + * recursive count method, which calculates the number of nodes in the list. + */ +public class CountSinglyLinkedListRecursion extends SinglyLinkedList { + + /** + * Recursively calculates the number of nodes in the list. + * + * @param head the head node of the list segment being counted. + * @return the count of nodes from the given head node onward. + */ + private int countRecursion(SinglyLinkedListNode head) { + return head == null ? 0 : 1 + countRecursion(head.next); + } + + /** + * Returns the total number of nodes in the list by invoking the recursive + * count helper method. + * + * @return the total node count in the list. + */ + @Override + public int count() { + return countRecursion(getHead()); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/lists/CreateAndDetectLoop.java b/src/main/java/com/thealgorithms/datastructures/lists/CreateAndDetectLoop.java new file mode 100644 index 000000000000..3902d08bfd14 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/lists/CreateAndDetectLoop.java @@ -0,0 +1,92 @@ +package com.thealgorithms.datastructures.lists; + +/** + * CreateAndDetectLoop provides utility methods for creating and detecting loops + * (cycles) in a singly linked list. Loops in a linked list are created by + * connecting the "next" pointer of one node to a previous node in the list, + * forming a cycle. + */ +public final class CreateAndDetectLoop { + + /** + * Private constructor to prevent instantiation of this utility class. + */ + private CreateAndDetectLoop() { + throw new UnsupportedOperationException("Utility class"); + } + + /** + * Node represents an individual element in the linked list, containing + * data and a reference to the next node. + */ + static final class Node { + int data; + Node next; + + Node(int data) { + this.data = data; + next = null; + } + } + + /** + * Creates a loop in a linked list by connecting the next pointer of a node + * at a specified starting position (position2) to another node at a specified + * destination position (position1). If either position is invalid, no loop + * will be created. + * + * @param head the head node of the linked list + * @param position1 the position in the list where the loop should end + * @param position2 the position in the list where the loop should start + */ + static void createLoop(Node head, int position1, int position2) { + if (position1 == 0 || position2 == 0) { + return; + } + + Node node1 = head; + Node node2 = head; + + int count1 = 1; + int count2 = 1; + // Traverse to the node at position1 + while (count1 < position1 && node1 != null) { + node1 = node1.next; + count1++; + } + + // Traverse to the node at position2 + while (count2 < position2 && node2 != null) { + node2 = node2.next; + count2++; + } + + // If both nodes are valid, create the loop + if (node1 != null && node2 != null) { + node2.next = node1; + } + } + + /** + * Detects the presence of a loop in the linked list using Floyd's cycle-finding + * algorithm, also known as the "tortoise and hare" method. + * + * @param head the head node of the linked list + * @return true if a loop is detected, false otherwise + * @see <a href="https://en.wikipedia.org/wiki/Cycle_detection#Floyd's_tortoise_and_hare"> + * Floyd's Cycle Detection Algorithm</a> + */ + static boolean detectLoop(Node head) { + Node sptr = head; + Node fptr = head; + + while (fptr != null && fptr.next != null) { + sptr = sptr.next; + fptr = fptr.next.next; + if (sptr == fptr) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/lists/CursorLinkedList.java b/src/main/java/com/thealgorithms/datastructures/lists/CursorLinkedList.java new file mode 100644 index 000000000000..ff3d39115c3b --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/lists/CursorLinkedList.java @@ -0,0 +1,208 @@ +package com.thealgorithms.datastructures.lists; + +import java.util.Objects; + +/** + * CursorLinkedList is an array-based implementation of a singly linked list. + * Each node in the array simulates a linked list node, storing an element and + * the index of the next node. This structure allows for efficient list operations + * without relying on traditional pointers. + * + * @param <T> the type of elements in this list + */ +public class CursorLinkedList<T> { + + /** + * Node represents an individual element in the list, containing the element + * itself and a pointer (index) to the next node. + */ + private static class Node<T> { + T element; + int next; + + Node(T element, int next) { + this.element = element; + this.next = next; + } + } + + private final int os; + private int head; + private final Node<T>[] cursorSpace; + private int count; + private static final int CURSOR_SPACE_SIZE = 100; + + { + // Initialize cursor space array and free list pointers + cursorSpace = new Node[CURSOR_SPACE_SIZE]; + for (int i = 0; i < CURSOR_SPACE_SIZE; i++) { + cursorSpace[i] = new Node<>(null, i + 1); + } + cursorSpace[CURSOR_SPACE_SIZE - 1].next = 0; + } + + /** + * Constructs an empty CursorLinkedList with the default capacity. + */ + public CursorLinkedList() { + os = 0; + count = 0; + head = -1; + } + + /** + * Prints all elements in the list in their current order. + */ + public void printList() { + if (head != -1) { + int start = head; + while (start != -1) { + T element = cursorSpace[start].element; + System.out.println(element.toString()); + start = cursorSpace[start].next; + } + } + } + + /** + * Finds the logical index of a specified element in the list. + * + * @param element the element to search for in the list + * @return the logical index of the element, or -1 if not found + * @throws NullPointerException if element is null + */ + public int indexOf(T element) { + if (element == null) { + throw new NullPointerException("Element cannot be null"); + } + try { + Objects.requireNonNull(element); + Node<T> iterator = cursorSpace[head]; + for (int i = 0; i < count; i++) { + if (iterator.element.equals(element)) { + return i; + } + iterator = cursorSpace[iterator.next]; + } + } catch (Exception e) { + return -1; + } + return -1; + } + + /** + * Retrieves an element at a specified logical index in the list. + * + * @param position the logical index of the element + * @return the element at the specified position, or null if index is out of bounds + */ + public T get(int position) { + if (position >= 0 && position < count) { + int start = head; + int counter = 0; + while (start != -1) { + T element = cursorSpace[start].element; + if (counter == position) { + return element; + } + start = cursorSpace[start].next; + counter++; + } + } + return null; + } + + /** + * Removes the element at a specified logical index from the list. + * + * @param index the logical index of the element to remove + */ + public void removeByIndex(int index) { + if (index >= 0 && index < count) { + T element = get(index); + remove(element); + } + } + + /** + * Removes a specified element from the list. + * + * @param element the element to be removed + * @throws NullPointerException if element is null + */ + public void remove(T element) { + Objects.requireNonNull(element); + T tempElement = cursorSpace[head].element; + int tempNext = cursorSpace[head].next; + if (tempElement.equals(element)) { + free(head); + head = tempNext; + } else { + int prevIndex = head; + int currentIndex = cursorSpace[prevIndex].next; + while (currentIndex != -1) { + T currentElement = cursorSpace[currentIndex].element; + if (currentElement.equals(element)) { + cursorSpace[prevIndex].next = cursorSpace[currentIndex].next; + free(currentIndex); + break; + } + prevIndex = currentIndex; + currentIndex = cursorSpace[prevIndex].next; + } + } + count--; + } + + /** + * Allocates a new node index for storing an element. + * + * @return the index of the newly allocated node + * @throws OutOfMemoryError if no space is available in cursor space + */ + private int alloc() { + int availableNodeIndex = cursorSpace[os].next; + if (availableNodeIndex == 0) { + throw new OutOfMemoryError(); + } + cursorSpace[os].next = cursorSpace[availableNodeIndex].next; + cursorSpace[availableNodeIndex].next = -1; + return availableNodeIndex; + } + + /** + * Releases a node back to the free list. + * + * @param index the index of the node to release + */ + private void free(int index) { + Node<T> osNode = cursorSpace[os]; + int osNext = osNode.next; + cursorSpace[os].next = index; + cursorSpace[index].element = null; + cursorSpace[index].next = osNext; + } + + /** + * Appends an element to the end of the list. + * + * @param element the element to append + * @throws NullPointerException if element is null + */ + public void append(T element) { + Objects.requireNonNull(element); + int availableIndex = alloc(); + cursorSpace[availableIndex].element = element; + if (head == -1) { + head = availableIndex; + } else { + int iterator = head; + while (cursorSpace[iterator].next != -1) { + iterator = cursorSpace[iterator].next; + } + cursorSpace[iterator].next = availableIndex; + } + cursorSpace[availableIndex].next = -1; + count++; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/lists/DoublyLinkedList.java b/src/main/java/com/thealgorithms/datastructures/lists/DoublyLinkedList.java new file mode 100644 index 000000000000..58898ddc0fcf --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/lists/DoublyLinkedList.java @@ -0,0 +1,410 @@ +package com.thealgorithms.datastructures.lists; + +/** + * This class implements a DoublyLinkedList. This is done using the classes + * LinkedList and Link. + * + * <p> + * A linked list is similar to an array, it holds values. However, links in a + * linked list do not have indexes. With a linked list you do not need to + * predetermine it's size as it grows and shrinks as it is edited. This is an + * example of a double ended, doubly linked list. Each link references the next + * link and the previous one. + * + * @author Unknown + */ +public final class DoublyLinkedList { + + /** + * Head refers to the front of the list + */ + protected Link head; + /** + * Tail refers to the back of the list + */ + private Link tail; + /** + * Link Operations to perform operations on nodes of the list + */ + private LinkOperations linkOperations; + + /** + * Default Constructor + */ + public DoublyLinkedList() { + head = null; + tail = null; + } + + /** + * Constructs a list containing the elements of the array + * + * @param array the array whose elements are to be placed into this list + * @throws NullPointerException if the specified collection is null + */ + public DoublyLinkedList(int[] array) { + if (array == null) { + throw new NullPointerException(); + } + for (int i : array) { + linkOperations.insertTail(i, this); + } + } + + /** + * Returns true if list is empty + * + * @return true if list is empty + */ + public boolean isEmpty() { + return (head == null); + } + + /** + * Prints contents of the list + */ + public void display() { // Prints contents of the list + Link current = head; + while (current != null) { + current.displayLink(); + current = current.next; + } + System.out.println(); + } + + /** + * Prints the contents of the list in reverse order + */ + public void displayBackwards() { + Link current = tail; + while (current != null) { + current.displayLink(); + current = current.previous; + } + System.out.println(); + } +} + +/** + * This class is used to implement the nodes of the linked list. + * + * @author Unknown + */ +class Link { + + /** + * Value of node + */ + public int value; + /** + * This points to the link in front of the new link + */ + public Link next; + /** + * This points to the link behind the new link + */ + public Link previous; + + /** + * Constructor + * + * @param value Value of node + */ + Link(int value) { + this.value = value; + } + + /** + * Displays the node + */ + public void displayLink() { + System.out.print(value + " "); + } + + /** + * Main Method + * + * @param args Command line arguments + */ + public static void main(String[] args) { + DoublyLinkedList myList = new DoublyLinkedList(); + LinkOperations linkOperations = new LinkOperations(); + linkOperations.insertHead(13, myList); + linkOperations.insertHead(7, myList); + linkOperations.insertHead(10, myList); + myList.display(); // <-- 10(head) <--> 7 <--> 13(tail) --> + myList.displayBackwards(); + + linkOperations.insertTail(11, myList); + myList.display(); // <-- 10(head) <--> 7 <--> 13 <--> 11(tail) --> + myList.displayBackwards(); + + linkOperations.deleteTail(); + myList.display(); // <-- 10(head) <--> 7 <--> 13(tail) --> + myList.displayBackwards(); + + linkOperations.delete(7); + myList.display(); // <-- 10(head) <--> 13(tail) --> + myList.displayBackwards(); + + linkOperations.insertOrdered(23, myList); + linkOperations.insertOrdered(67, myList); + linkOperations.insertOrdered(3, myList); + myList.display(); // <-- 3(head) <--> 10 <--> 13 <--> 23 <--> 67(tail) --> + linkOperations.insertElementByIndex(5, 1, myList); + myList.display(); // <-- 3(head) <--> 5 <--> 10 <--> 13 <--> 23 <--> 67(tail) --> + myList.displayBackwards(); + linkOperations.reverse(); // <-- 67(head) <--> 23 <--> 13 <--> 10 <--> 5 <--> 3(tail) --> + myList.display(); + + linkOperations.clearList(); + myList.display(); + myList.displayBackwards(); + linkOperations.insertHead(20, myList); + myList.display(); + myList.displayBackwards(); + } +} + +/* + * This class implements the operations of the Link nodes. + */ +class LinkOperations { + + /** + * Head refers to the front of the list + */ + private Link head; + /** + * Tail refers to the back of the list + */ + private Link tail; + + /** + * Size refers to the number of elements present in the list + */ + private int size; + + /** + * Insert an element at the head + * + * @param x Element to be inserted + */ + public void insertHead(int x, DoublyLinkedList doublyLinkedList) { + Link newLink = new Link(x); // Create a new link with a value attached to it + if (doublyLinkedList.isEmpty()) { // Set the first element added to be the tail + tail = newLink; + } else { + head.previous = newLink; // newLink <-- currenthead(head) + } + newLink.next = head; // newLink <--> currenthead(head) + head = newLink; // newLink(head) <--> oldhead + ++size; + } + + /** + * Insert an element at the tail + * + * @param x Element to be inserted + */ + public void insertTail(int x, DoublyLinkedList doublyLinkedList) { + Link newLink = new Link(x); + newLink.next = null; // currentTail(tail) newlink --> + if (doublyLinkedList.isEmpty()) { // Check if there are no elements in list then it adds first element + tail = newLink; + head = tail; + } else { + tail.next = newLink; // currentTail(tail) --> newLink --> + newLink.previous = tail; // currentTail(tail) <--> newLink --> + tail = newLink; // oldTail <--> newLink(tail) --> + } + ++size; + } + + /** + * Insert an element at the index + * + * @param x Element to be inserted + * @param index Index(from start) at which the element x to be inserted + */ + public void insertElementByIndex(int x, int index, DoublyLinkedList doublyLinkedList) { + if (index > size) { + throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); + } + if (index == 0) { + insertHead(x, doublyLinkedList); + } else { + if (index == size) { + insertTail(x, doublyLinkedList); + } else { + Link newLink = new Link(x); + Link previousLink = head; // + for (int i = 1; i < index; i++) { // Loop to reach the index + previousLink = previousLink.next; + } + // previousLink is the Link at index - 1 from start + previousLink.next.previous = newLink; + newLink.next = previousLink.next; + newLink.previous = previousLink; + previousLink.next = newLink; + } + } + ++size; + } + + /** + * Delete the element at the head + * + * @return The new head + */ + public Link deleteHead() { + Link temp = head; + head = head.next; // oldHead <--> 2ndElement(head) + + if (head == null) { + tail = null; + } else { + head.previous = null; // oldHead --> 2ndElement(head) nothing pointing at old head so + // will be removed + } + --size; + return temp; + } + + /** + * Delete the element at the tail + * + * @return The new tail + */ + public Link deleteTail() { + Link temp = tail; + tail = tail.previous; // 2ndLast(tail) <--> oldTail --> null + + if (tail == null) { + head = null; + } else { + tail.next = null; // 2ndLast(tail) --> null + } + --size; + return temp; + } + + /** + * Delete the element from somewhere in the list + * + * @param x element to be deleted + * @return Link deleted + */ + public void delete(int x) { + Link current = head; + + while (current.value != x) { // Find the position to delete + if (current != tail) { + current = current.next; + } else { // If we reach the tail and the element is still not found + throw new RuntimeException("The element to be deleted does not exist!"); + } + } + + if (current == head) { + deleteHead(); + } else if (current == tail) { + deleteTail(); + } else { // Before: 1 <--> 2(current) <--> 3 + current.previous.next = current.next; // 1 --> 3 + current.next.previous = current.previous; // 1 <--> 3 + } + --size; + } + + /** + * Inserts element and reorders + * + * @param x Element to be added + */ + public void insertOrdered(int x, DoublyLinkedList doublyLinkedList) { + Link newLink = new Link(x); + Link current = head; + while (current != null && x > current.value) { // Find the position to insert + current = current.next; + } + + if (current == head) { + insertHead(x, doublyLinkedList); + } else if (current == null) { + insertTail(x, doublyLinkedList); + } else { // Before: 1 <--> 2(current) <--> 3 + newLink.previous = current.previous; // 1 <-- newLink + current.previous.next = newLink; // 1 <--> newLink + newLink.next = current; // 1 <--> newLink --> 2(current) <--> 3 + current.previous = newLink; // 1 <--> newLink <--> 2(current) <--> 3 + } + ++size; + } + + /** + * Deletes the passed node from the current list + * + * @param z Element to be deleted + */ + public void deleteNode(Link z) { + if (z.next == null) { + deleteTail(); + } else if (z == head) { + deleteHead(); + } else { // before <-- 1 <--> 2(z) <--> 3 --> + z.previous.next = z.next; // 1 --> 3 + z.next.previous = z.previous; // 1 <--> 3 + } + --size; + } + + public void removeDuplicates(DoublyLinkedList l) { + Link linkOne = l.head; + while (linkOne.next != null) { // list is present + Link linkTwo = linkOne.next; // second link for comparison + while (linkTwo.next != null) { + if (linkOne.value == linkTwo.value) { // if there are duplicates values then + delete(linkTwo.value); // delete the link + } + linkTwo = linkTwo.next; // go to next link + } + linkOne = linkOne.next; // go to link link to iterate the whole list again + } + } + + /** + * Reverses the list in place + */ + public void reverse() { + // Keep references to the head and tail + Link thisHead = this.head; + Link thisTail = this.tail; + + // Flip the head and tail references + this.head = thisTail; + this.tail = thisHead; + + // While the link we're visiting is not null, flip the + // next and previous links + Link nextLink = thisHead; + while (nextLink != null) { + Link nextLinkNext = nextLink.next; + Link nextLinkPrevious = nextLink.previous; + nextLink.next = nextLinkPrevious; + nextLink.previous = nextLinkNext; + + // Now, we want to go to the next link + nextLink = nextLinkNext; + } + } + + /** + * Clears List + */ + public void clearList() { + head = null; + tail = null; + size = 0; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/lists/MergeKSortedLinkedList.java b/src/main/java/com/thealgorithms/datastructures/lists/MergeKSortedLinkedList.java new file mode 100644 index 000000000000..0f5c50530d92 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/lists/MergeKSortedLinkedList.java @@ -0,0 +1,94 @@ +package com.thealgorithms.datastructures.lists; + +import java.util.Comparator; +import java.util.PriorityQueue; + +/** + * The MergeKSortedLinkedList class provides a method to merge multiple sorted linked lists + * into a single sorted linked list. + * This implementation uses a min-heap (priority queue) to efficiently + * find the smallest node across all lists, thus optimizing the merge process. + * + * <p>Example usage: + * <pre> + * Node list1 = new Node(1, new Node(4, new Node(5))); + * Node list2 = new Node(1, new Node(3, new Node(4))); + * Node list3 = new Node(2, new Node(6)); + * Node[] lists = { list1, list2, list3 }; + * + * MergeKSortedLinkedList merger = new MergeKSortedLinkedList(); + * Node mergedHead = merger.mergeKList(lists, lists.length); + * </pre> + * </p> + * + * <p>This class is designed to handle nodes of integer linked lists and can be expanded for additional data types if needed.</p> + * + * @author Arun Pandey (https://github.com/pandeyarun709) + */ +public class MergeKSortedLinkedList { + + /** + * Merges K sorted linked lists into a single sorted linked list. + * + * <p>This method uses a priority queue (min-heap) to repeatedly extract the smallest node from the heads of all the lists, + * then inserts the next node from that list into the heap. The process continues until all nodes have been processed, + * resulting in a fully merged and sorted linked list.</p> + * + * @param a Array of linked list heads to be merged. + * @param n Number of linked lists. + * @return Head of the merged sorted linked list. + */ + Node mergeKList(Node[] a, int n) { + if (a == null || n == 0) { + return null; + } + + // Min Heap to store nodes based on their values for efficient retrieval of the smallest element. + PriorityQueue<Node> minHeap = new PriorityQueue<>(Comparator.comparingInt(x -> x.data)); + + // Initialize the min-heap with the head of each non-null linked list + for (Node node : a) { + if (node != null) { + minHeap.add(node); + } + } + + // Start merging process + Node head = minHeap.poll(); // Smallest head is the initial head of the merged list + if (head != null && head.next != null) { + minHeap.add(head.next); + } + + Node curr = head; + while (!minHeap.isEmpty()) { + Node temp = minHeap.poll(); + curr.next = temp; + curr = temp; + + // Add the next node in the current list to the heap if it exists + if (temp.next != null) { + minHeap.add(temp.next); + } + } + + return head; + } + + /** + * Represents a node in the linked list. + */ + static class Node { + int data; + Node next; + + Node(int data) { + this.data = data; + this.next = null; + } + + Node(int data, Node next) { + this.data = data; + this.next = next; + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/lists/MergeSortedArrayList.java b/src/main/java/com/thealgorithms/datastructures/lists/MergeSortedArrayList.java new file mode 100644 index 000000000000..09eb854c8dc2 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/lists/MergeSortedArrayList.java @@ -0,0 +1,71 @@ +package com.thealgorithms.datastructures.lists; + +import java.util.Collection; +import java.util.List; + +/** + * Utility class for merging two sorted ArrayLists of integers into a single sorted collection. + * + * <p>This class provides a static `merge` method to combine two pre-sorted lists of integers into a + * single sorted list. It does so without modifying the input lists by adding elements from both lists in sorted order + * into the result list.</p> + * + * <p>Example usage:</p> + * <pre> + * List<Integer> listA = Arrays.asList(1, 3, 5, 7, 9); + * List<Integer> listB = Arrays.asList(2, 4, 6, 8, 10); + * List<Integer> result = new ArrayList<>(); + * MergeSortedArrayList.merge(listA, listB, result); + * </pre> + * + * <p>The resulting `result` list will be [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].</p> + * + * <p>Note: This class cannot be instantiated as it is designed to be used only with its static `merge` method.</p> + * + * <p>This implementation assumes the input lists are already sorted in ascending order.</p> + * + * @author https://github.com/shellhub + * @see List + */ +public final class MergeSortedArrayList { + + private MergeSortedArrayList() { + } + + /** + * Merges two sorted lists of integers into a single sorted collection. + * + * <p>This method does not alter the original lists (`listA` and `listB`). Instead, it inserts elements from both + * lists into `listC` in a way that maintains ascending order.</p> + * + * @param listA The first sorted list of integers. + * @param listB The second sorted list of integers. + * @param listC The collection to hold the merged result, maintaining sorted order. + * @throws NullPointerException if any of the input lists or result collection is null. + */ + public static void merge(List<Integer> listA, List<Integer> listB, Collection<Integer> listC) { + if (listA == null || listB == null || listC == null) { + throw new NullPointerException("Input lists and result collection must not be null."); + } + + int pa = 0; + int pb = 0; + + while (pa < listA.size() && pb < listB.size()) { + if (listA.get(pa) <= listB.get(pb)) { + listC.add(listA.get(pa++)); + } else { + listC.add(listB.get(pb++)); + } + } + + // Add remaining elements from listA, if any + while (pa < listA.size()) { + listC.add(listA.get(pa++)); + } + // Add remaining elements from listB, if any + while (pb < listB.size()) { + listC.add(listB.get(pb++)); + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/lists/MergeSortedSinglyLinkedList.java b/src/main/java/com/thealgorithms/datastructures/lists/MergeSortedSinglyLinkedList.java new file mode 100644 index 000000000000..4e99642fccd8 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/lists/MergeSortedSinglyLinkedList.java @@ -0,0 +1,67 @@ +package com.thealgorithms.datastructures.lists; + +/** + * Utility class for merging two sorted singly linked lists. + * + * <p>This class extends the {@link SinglyLinkedList} class to support the merging of two sorted linked lists. + * It provides a static method, `merge`, that takes two sorted singly linked lists, merges them into a single sorted linked list, + * and returns the result.</p> + * + * <p>Example usage:</p> + * <pre> + * SinglyLinkedList listA = new SinglyLinkedList(); + * SinglyLinkedList listB = new SinglyLinkedList(); + * for (int i = 2; i <= 10; i += 2) { + * listA.insert(i); // listA: 2->4->6->8->10 + * listB.insert(i - 1); // listB: 1->3->5->7->9 + * } + * SinglyLinkedList mergedList = MergeSortedSinglyLinkedList.merge(listA, listB); + * System.out.println(mergedList); // Output: 1->2->3->4->5->6->7->8->9->10 + * </pre> + * + * <p>The `merge` method assumes that both input lists are already sorted in ascending order. + * It returns a new singly linked list that contains all elements from both lists in sorted order.</p> + * + * @see SinglyLinkedList + */ +public class MergeSortedSinglyLinkedList extends SinglyLinkedList { + + /** + * Merges two sorted singly linked lists into a single sorted singly linked list. + * + * <p>This method does not modify the input lists; instead, it creates a new merged linked list + * containing all elements from both lists in sorted order.</p> + * + * @param listA The first sorted singly linked list. + * @param listB The second sorted singly linked list. + * @return A new singly linked list containing all elements from both lists in sorted order. + * @throws NullPointerException if either input list is null. + */ + public static SinglyLinkedList merge(SinglyLinkedList listA, SinglyLinkedList listB) { + if (listA == null || listB == null) { + throw new NullPointerException("Input lists must not be null."); + } + + SinglyLinkedListNode headA = listA.getHead(); + SinglyLinkedListNode headB = listB.getHead(); + int size = listA.size() + listB.size(); + + SinglyLinkedListNode head = new SinglyLinkedListNode(); + SinglyLinkedListNode tail = head; + while (headA != null && headB != null) { + if (headA.value <= headB.value) { + tail.next = headA; + headA = headA.next; + } else { + tail.next = headB; + headB = headB.next; + } + tail = tail.next; + } + + // Attach remaining nodes + tail.next = (headA == null) ? headB : headA; + + return new SinglyLinkedList(head.next, size); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/lists/QuickSortLinkedList.java b/src/main/java/com/thealgorithms/datastructures/lists/QuickSortLinkedList.java new file mode 100644 index 000000000000..f018781ada70 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/lists/QuickSortLinkedList.java @@ -0,0 +1,183 @@ +package com.thealgorithms.datastructures.lists; +/* + * + * @aurthor - Prabhat-Kumar-42 + * @github - https://github.com/Prabhat-Kumar-42 + * + * Problem : + * QuickSort on Linked List + * + * Note: Taking head as pivot in current implementation. + * N represents NULL node + * Example: + * + * -> Given Linked List : + * 5 -> 3 -> 8 -> 1 -> 10 -> 2 -> 7 -> 4 -> 9 -> 6 + * + * -> How Sorting will work according to the QuickSort Algo written below + * + * current pivot : 5 + * List lessThanPivot : 3 -> 1 -> 2 -> 4 + * List greaterThanPivot : 5 -> 8 -> 10 -> 7 -> 9 -> 6 + * + * -> reccur for lessThanPivot and greaterThanPivot + * + * lessThanPivot : + * current pivot : 3 + * lessThanPivot : 1 -> 2 + * greaterThanPivot : 4 + * + * greaterThanPivot: + * current pivot : 5 + * lessThanPivot : null + * greaterThanPivot : 8 -> 10 -> 7 -> 9 -> 6 + * + * By following the above pattern, reccuring tree will form like below : + * + * List-> 5 -> 3 -> 8 -> 1 -> 10 -> 2 -> 7 -> 4 -> 9 -> 6 + * + * Pivot : 5 + * /\ + * / \ + * / \ + * / \ + * / \ + * List: (3 -> 1 -> 2 -> 4) (5 -> 8 -> 10 -> 7 -> 9 -> 6) + * Pivot : 3 5 + * /\ /\ + * / \ / \ + * / \ / \ + * / \ / \ + * List: (1 -> 2) (4) (N) (8 -> 10 -> 7 -> 9 -> 6) + * Pivot: 1 4 8 + * /\ /\ /\ + * / \ / \ / \ + * / \ / \ / \ + * List: (N) (2) (N) (N) (6 -> 7) (9 -> 10) + * Pivot: 2 6 9 + * /\ /\ /\ + * / \ / \ / \ + * / \ / \ / \ + * List: (N) (N) (N) (7) (N) (10) + * Pivot: 7 10 + * /\ /\ + * / \ / \ + * / \ / \ + * (N) (N) (N) (N) + * + * + * -> After this the tree will reccur back (or backtrack) + * and the returning list from left and right subtree will attach + * themselves around pivot. + * i.e. , + * (listFromLeftSubTree) -> (Pivot) -> (listFromRightSubtree) + * + * This will continue until whole list is merged back + * + * eg : + * Megring the above Tree back we get : + * + * List: (1 -> 2) (4) (6 -> 7) (9 -> 10) + * \ / \ / + * \ / \ / + * \ / \ / + * \ / \ / + * \ / \ / + * \ / \ / + * \ / \ / + * Pivot: 3 8 + * List: (1 -> 2 -> 3 -> 4) (6 -> 7 -> 8 -> 9 -> 10) + * \ / + * \ / + * \ / + * \ / + * \ / + * \ / + * \ / + * \/ + * Pivot: 5 + * List: (1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10) + * + * + * -> This will result in a sorted Linked List + */ + +public class QuickSortLinkedList { + + private final SinglyLinkedList list; // The linked list to be sorted + private SinglyLinkedListNode head; // Head of the list + + /** + * Constructor that initializes the QuickSortLinkedList with a given linked list. + * + * @param list The singly linked list to be sorted + */ + public QuickSortLinkedList(SinglyLinkedList list) { + this.list = list; + this.head = list.getHead(); + } + + /** + * Sorts the linked list using the QuickSort algorithm. + * The sorted list replaces the original list within the SinglyLinkedList instance. + */ + public void sortList() { + head = sortList(head); + list.setHead(head); + } + + /** + * Recursively sorts a linked list by partitioning it around a pivot element. + * + * <p>Each recursive call selects a pivot, partitions the list into elements less + * than the pivot and elements greater than or equal to the pivot, then combines + * the sorted sublists around the pivot.</p> + * + * @param head The head node of the list to sort + * @return The head node of the sorted linked list + */ + private SinglyLinkedListNode sortList(SinglyLinkedListNode head) { + if (head == null || head.next == null) { + return head; + } + + SinglyLinkedListNode pivot = head; + head = head.next; + pivot.next = null; + + SinglyLinkedListNode lessHead = new SinglyLinkedListNode(); + SinglyLinkedListNode lessTail = lessHead; + SinglyLinkedListNode greaterHead = new SinglyLinkedListNode(); + SinglyLinkedListNode greaterTail = greaterHead; + + while (head != null) { + if (head.value < pivot.value) { + lessTail.next = head; + lessTail = lessTail.next; + } else { + greaterTail.next = head; + greaterTail = greaterTail.next; + } + head = head.next; + } + + lessTail.next = null; + greaterTail.next = null; + + SinglyLinkedListNode sortedLess = sortList(lessHead.next); + SinglyLinkedListNode sortedGreater = sortList(greaterHead.next); + + if (sortedLess == null) { + pivot.next = sortedGreater; + return pivot; + } else { + SinglyLinkedListNode current = sortedLess; + while (current.next != null) { + current = current.next; + } + current.next = pivot; + pivot.next = sortedGreater; + return sortedLess; + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/lists/README.md b/src/main/java/com/thealgorithms/datastructures/lists/README.md new file mode 100644 index 000000000000..6aefa4c98e6d --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/lists/README.md @@ -0,0 +1,32 @@ +## Linked List +### Description + +LinkedList is a data structure in which data is stored in a linear manner. It usually contains a data field and a link to the memory location of the next node. + +### Structure + +``` +class LinkedList<E>{ + E value; + LinkedList next; +} +``` + +The `next` variable points to the next node in the data structure and value stores the data. Any number of nodes can be linked in this manner. The structure will be: + + +### Properties +1. Linked list does not provide indexing like an array. For accessing a node at position `p` , θ(p) nodes need to be accessed. +2. Main advantage of linked list is addition and removal of nodes near the end and beginning of lists. It can be done just by updating the link (O(1) time) +3. Unlike an array, its size is not predefined. So any number of nodes can be appended. + +### File descriptions: + +1. `CircleLinkedList.java` : A circular linked list where next pointer of last node points to first node of linked list. +2. `SinglyLinkedList.java` : The classic case of single links. +3. `CountSinglyLinkedListRecursion.java`: Recursively counts the size of a list. +4. `CreateAndDetectLoop.java` : Create and detect a loop in a linked list. +5. `DoublyLinkedList.java` : A modification of singly linked list which has a `prev` pointer to point to the previous node. +6. `MergeKSortedLinkedlist.java` : Merges K sorted linked list with mergesort (mergesort is also the most efficient sorting algorithm for linked list). +7. `RandomNode.java` : Selects a random node from given linked list and diplays it. +8. `SkipList.java` : Data Structure used for storing a sorted list of elements with help of a Linked list hierarchy that connects to subsequences of elements. diff --git a/src/main/java/com/thealgorithms/datastructures/lists/RandomNode.java b/src/main/java/com/thealgorithms/datastructures/lists/RandomNode.java new file mode 100644 index 000000000000..dac88dd9f241 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/lists/RandomNode.java @@ -0,0 +1,89 @@ +package com.thealgorithms.datastructures.lists; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** + * @author <a href="https://github.com/skmodi649">Suraj Kumar</a> + * <p> + * PROBLEM DESCRIPTION : + * There is a single linked list and we are supposed to find a random node in the given linked list + * <p> + * ALGORITHM : + * Step 1 : START + * Step 2 : Create an arraylist of type integer + * Step 3 : Declare an integer type variable for size and linked list type for head + * Step 4 : We will use two methods, one for traversing through the linked list using while loop and + * also increase the size by 1 + * <p> + * (a) RandomNode(head) + * (b) run a while loop till null; + * (c) add the value to arraylist; + * (d) increase the size; + * <p> + * Step 5 : Now use another method for getting random values using Math.random() and return the + * value present in arraylist for the calculated index Step 6 : Now in main() method we will simply + * insert nodes in the linked list and then call the appropriate method and then print the random + * node generated Step 7 : STOP + */ +public class RandomNode { + + private final List<Integer> list; + private int size; + private static final Random RAND = new Random(); + + static class ListNode { + + int val; + ListNode next; + + ListNode(int val) { + this.val = val; + } + } + + public RandomNode(ListNode head) { + list = new ArrayList<>(); + ListNode temp = head; + + // Now using while loop to traverse through the linked list and + // go on adding values and increasing the size value by 1 + while (temp != null) { + list.add(temp.val); + temp = temp.next; + size++; + } + } + + public int getRandom() { + int index = RAND.nextInt(size); + return list.get(index); + } + + /** + * OUTPUT : + * First output : + * Random Node : 25 + * Second output : + * Random Node : 78 + * Time Complexity : O(n) + * Auxiliary Space Complexity : O(1) + * Time Complexity : O(n) + * Auxiliary Space Complexity : O(1) + * Time Complexity : O(n) + * Auxiliary Space Complexity : O(1) + */ + // Driver program to test above functions + public static void main(String[] args) { + ListNode head = new ListNode(15); + head.next = new ListNode(25); + head.next.next = new ListNode(4); + head.next.next.next = new ListNode(1); + head.next.next.next.next = new ListNode(78); + head.next.next.next.next.next = new ListNode(63); + RandomNode list = new RandomNode(head); + int randomNum = list.getRandom(); + System.out.println("Random Node : " + randomNum); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/lists/ReverseKGroup.java b/src/main/java/com/thealgorithms/datastructures/lists/ReverseKGroup.java new file mode 100644 index 000000000000..9b9464d388b5 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/lists/ReverseKGroup.java @@ -0,0 +1,92 @@ +package com.thealgorithms.datastructures.lists; + +/** + * The ReverseKGroup class provides functionality to reverse nodes in a + * linked list in groups of k nodes. + * <p> + * This algorithm follows the approach of reversing the linked list in segments of + * size k. If the remaining nodes are fewer than k, they remain unchanged. + * </p> + * <p> + * Example: + * Given a linked list: 1 -> 2 -> 3 -> 4 -> 5 and k = 3, + * the output will be: 3 -> 2 -> 1 -> 4 -> 5. + * </p> + * <p> + * The implementation contains: + * - {@code length(SinglyLinkedListNode head)}: A method to calculate the length of the linked list. + * - {@code reverse(SinglyLinkedListNode head, int count, int k)}: A helper method that reverses the nodes + * in the linked list in groups of k. + * - {@code reverseKGroup(SinglyLinkedListNode head, int k)}: The main method that initiates the reversal + * process by calling the reverse method. + * </p> + * <p> + * Complexity: + * <ul> + * <li>Time Complexity: O(n), where n is the number of nodes in the linked list.</li> + * <li>Space Complexity: O(1), as we are reversing in place.</li> + * </ul> + * </p> + * + * Author: Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi) + */ +public class ReverseKGroup { + + /** + * Calculates the length of the linked list. + * + * @param head The head node of the linked list. + * @return The total number of nodes in the linked list. + */ + public int length(SinglyLinkedListNode head) { + SinglyLinkedListNode curr = head; + int count = 0; + while (curr != null) { + curr = curr.next; + count++; + } + return count; + } + + /** + * Reverses the linked list in groups of k nodes. + * + * @param head The head node of the linked list. + * @param count The remaining number of nodes. + * @param k The size of the group to reverse. + * @return The new head of the reversed linked list segment. + */ + public SinglyLinkedListNode reverse(SinglyLinkedListNode head, int count, int k) { + if (count < k) { + return head; + } + SinglyLinkedListNode prev = null; + int count1 = 0; + SinglyLinkedListNode curr = head; + SinglyLinkedListNode next = null; + while (curr != null && count1 < k) { + next = curr.next; + curr.next = prev; + prev = curr; + curr = next; + count1++; + } + + if (next != null) { + head.next = reverse(next, count - k, k); + } + return prev; + } + + /** + * Reverses the linked list in groups of k nodes. + * + * @param head The head node of the linked list. + * @param k The size of the group to reverse. + * @return The head of the modified linked list after reversal. + */ + public SinglyLinkedListNode reverseKGroup(SinglyLinkedListNode head, int k) { + int count = length(head); + return reverse(head, count, k); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/lists/RotateSinglyLinkedLists.java b/src/main/java/com/thealgorithms/datastructures/lists/RotateSinglyLinkedLists.java new file mode 100644 index 000000000000..47ee5397097c --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/lists/RotateSinglyLinkedLists.java @@ -0,0 +1,65 @@ +package com.thealgorithms.datastructures.lists; + +/** + * The RotateSinglyLinkedLists class provides a method to rotate a singly linked list + * to the right by a specified number of positions. + * <p> + * In a right rotation by `k` steps, each node in the list moves `k` positions to the right. + * Nodes that are rotated off the end of the list are placed back at the beginning. + * </p> + * <p> + * Example: + * Given linked list: 1 -> 2 -> 3 -> 4 -> 5 and k = 2, the output will be: + * 4 -> 5 -> 1 -> 2 -> 3. + * </p> + * <p> + * Edge Cases: + * <ul> + * <li>If the list is empty, returns null.</li> + * <li>If `k` is 0 or a multiple of the list length, the list remains unchanged.</li> + * </ul> + * </p> + * <p> + * Complexity: + * <ul> + * <li>Time Complexity: O(n), where n is the number of nodes in the linked list.</li> + * <li>Space Complexity: O(1), as we only use a constant amount of additional space.</li> + * </ul> + * </p> + * + * Author: Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi) + */ +public class RotateSinglyLinkedLists { + + /** + * Rotates a singly linked list to the right by `k` positions. + * + * @param head The head node of the singly linked list. + * @param k The number of positions to rotate the list to the right. + * @return The head of the rotated linked list. + */ + public SinglyLinkedListNode rotateRight(SinglyLinkedListNode head, int k) { + if (head == null || head.next == null || k == 0) { + return head; + } + + SinglyLinkedListNode curr = head; + int len = 1; + while (curr.next != null) { + curr = curr.next; + len++; + } + + curr.next = head; + k = k % len; + k = len - k; + while (k > 0) { + curr = curr.next; + k--; + } + + head = curr.next; + curr.next = null; + return head; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/lists/SearchSinglyLinkedListRecursion.java b/src/main/java/com/thealgorithms/datastructures/lists/SearchSinglyLinkedListRecursion.java new file mode 100644 index 000000000000..4ac2de422595 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/lists/SearchSinglyLinkedListRecursion.java @@ -0,0 +1,47 @@ +package com.thealgorithms.datastructures.lists; + +/** + * The SearchSinglyLinkedListRecursion class extends SinglyLinkedList and provides + * a method to search for a value in a singly linked list using recursion. + * <p> + * This class demonstrates a recursive approach to check if a given integer value is + * present in the linked list. The search method calls a private recursive helper method + * `searchRecursion`, which checks each node's value and moves to the next node if necessary. + * </p> + * <p> + * Example: + * Given a list containing the values 1 -> 2 -> 3 -> 4, calling search(3) will return `true`, + * while calling search(5) will return `false`. + * </p> + * <p> + * Complexity: + * <ul> + * <li>Time Complexity: O(n), where n is the number of nodes in the linked list.</li> + * <li>Space Complexity: O(n), due to the recursive call stack in the worst case.</li> + * </ul> + * </p> + */ +public class SearchSinglyLinkedListRecursion extends SinglyLinkedList { + + /** + * Recursively searches for a given value in the linked list. + * + * @param node the head node to start the search. + * @param key the integer value to be searched for. + * @return {@code true} if the value `key` is present in the list; otherwise, {@code false}. + */ + private boolean searchRecursion(SinglyLinkedListNode node, int key) { + return (node != null && (node.value == key || searchRecursion(node.next, key))); + } + + /** + * Public search method to determine if a key is present in the linked list. + * + * @param key the integer value to be searched for. + * @return {@code true} if the value `key` is present in the list; otherwise, {@code false}. + */ + @Override + public boolean search(int key) { + return searchRecursion(getHead(), key); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/lists/SinglyLinkedList.java b/src/main/java/com/thealgorithms/datastructures/lists/SinglyLinkedList.java new file mode 100644 index 000000000000..ff4af4437cc7 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/lists/SinglyLinkedList.java @@ -0,0 +1,476 @@ +package com.thealgorithms.datastructures.lists; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.StringJoiner; + +/** + * <a href="https://en.wikipedia.org/wiki/Linked_list">wikipedia</a> + */ +public class SinglyLinkedList implements Iterable<Integer> { + + /** + * Head refer to the front of the list + */ + private SinglyLinkedListNode head; + + /** + * Size of SinglyLinkedList + */ + private int size; + + /** + * Init SinglyLinkedList + */ + public SinglyLinkedList() { + head = null; + size = 0; + } + + /** + * Init SinglyLinkedList with specified head node and size + * + * @param head the head node of list + * @param size the size of list + */ + public SinglyLinkedList(SinglyLinkedListNode head, int size) { + this.head = head; + this.size = size; + } + + /** + * Detects if there is a loop in the singly linked list using floy'd turtle + * and hare algorithm. + * + */ + public boolean detectLoop() { + SinglyLinkedListNode currentNodeFast = head; + SinglyLinkedListNode currentNodeSlow = head; + while (currentNodeFast != null && currentNodeFast.next != null) { + currentNodeFast = currentNodeFast.next.next; + currentNodeSlow = currentNodeSlow.next; + if (currentNodeFast == currentNodeSlow) { + return true; + } + } + return false; + } + + /** + * Return the node in the middle of the list + * If the length of the list is even then return item number length/2 + * @return middle node of the list + */ + public SinglyLinkedListNode middle() { + if (head == null) { + return null; + } + SinglyLinkedListNode firstCounter = head; + SinglyLinkedListNode secondCounter = firstCounter.next; + while (secondCounter != null && secondCounter.next != null) { + firstCounter = firstCounter.next; + secondCounter = secondCounter.next.next; + } + return firstCounter; + } + + /** + * Swaps nodes of two given values a and b. + * + */ + public void swapNodes(int valueFirst, int valueSecond) { + if (valueFirst == valueSecond) { + return; + } + SinglyLinkedListNode previousA = null; + SinglyLinkedListNode currentA = head; + while (currentA != null && currentA.value != valueFirst) { + previousA = currentA; + currentA = currentA.next; + } + + SinglyLinkedListNode previousB = null; + SinglyLinkedListNode currentB = head; + while (currentB != null && currentB.value != valueSecond) { + previousB = currentB; + currentB = currentB.next; + } + /* If either of 'a' or 'b' is not present, then return */ + if (currentA == null || currentB == null) { + return; + } + + // If 'a' is not head node of list + if (previousA != null) { + previousA.next = currentB; + } else { + // make 'b' as the new head + head = currentB; + } + + // If 'b' is not head node of list + if (previousB != null) { + previousB.next = currentA; + } else { + // Make 'a' as new head + head = currentA; + } + // Swap next pointer + + var temp = currentA.next; + currentA.next = currentB.next; + currentB.next = temp; + } + + /** + * Reverse a singly linked list[Iterative] from a given node till the end + * + */ + public SinglyLinkedListNode reverseListIter(SinglyLinkedListNode node) { + SinglyLinkedListNode prev = null; + SinglyLinkedListNode curr = node; + + while (curr != null && curr.next != null) { + var next = curr.next; + curr.next = prev; + prev = curr; + curr = next; + } + // when curr.next==null, the current element is left without pointing it to its prev,so + if (curr != null) { + curr.next = prev; + prev = curr; + } + // prev will be pointing to the last element in the Linkedlist, it will be the new head of + // the reversed linkedlist + return prev; + } + /** + * Reverse a singly linked list[Recursive] from a given node till the end + * + */ + public SinglyLinkedListNode reverseListRec(SinglyLinkedListNode head) { + if (head == null || head.next == null) { + return head; + } + + SinglyLinkedListNode prev = null; + SinglyLinkedListNode h2 = reverseListRec(head.next); + + head.next.next = head; + head.next = prev; + + return h2; + } + + /** + * Clear all nodes in the list + */ + public void clear() { + SinglyLinkedListNode cur = head; + while (cur != null) { + cur = cur.next; + } + head = null; + size = 0; + } + + /** + * Checks if the list is empty + * + * @return {@code true} if list is empty, otherwise {@code false}. + */ + public boolean isEmpty() { + return size == 0; + } + + /** + * Returns the size of the linked list. + * + * @return the size of the list. + */ + public int size() { + return size; + } + + /** + * Get head of the list. + * + * @return head of the list. + */ + public SinglyLinkedListNode getHead() { + return head; + } + + /** + * Set head of the list. + * + */ + public void setHead(SinglyLinkedListNode head) { + this.head = head; + } + + /** + * Calculate the count of the list manually + * + * @return count of the list + */ + public int count() { + int count = 0; + for (final var element : this) { + ++count; + } + return count; + } + + /** + * Test if the value key is present in the list. + * + * @param key the value to be searched. + * @return {@code true} if key is present in the list, otherwise + * {@code false}. + */ + public boolean search(final int key) { + for (final var element : this) { + if (element == key) { + return true; + } + } + return false; + } + + @Override + public String toString() { + StringJoiner joiner = new StringJoiner("->"); + for (final var element : this) { + joiner.add(element + ""); + } + return joiner.toString(); + } + + public void deleteDuplicates() { + SinglyLinkedListNode pred = head; + // predecessor = the node + // having sublist of its duplicates + SinglyLinkedListNode newHead = head; + while (newHead != null) { + // if it's a beginning of duplicates sublist + // skip all duplicates + if (newHead.next != null && newHead.value == newHead.next.value) { + // move till the end of duplicates sublist + while (newHead.next != null && newHead.value == newHead.next.value) { + newHead = newHead.next; + } + // skip all duplicates + pred.next = newHead.next; + newHead = null; + // otherwise, move predecessor + } + // move forward + pred = pred.next; + newHead = pred; + } + } + + public void print() { + SinglyLinkedListNode temp = head; + while (temp != null && temp.next != null) { + System.out.print(temp.value + "->"); + temp = temp.next; + } + if (temp != null) { + System.out.print(temp.value); + System.out.println(); + } + } + + /** + * Inserts an element at the head of the list + * + * @param x element to be added + */ + public void insertHead(int x) { + insertNth(x, 0); + } + + /** + * Insert an element at the tail of the list + * + * @param data element to be added + */ + public void insert(int data) { + insertNth(data, size); + } + + /** + * Inserts a new node at a specified position of the list + * + * @param data data to be stored in a new node + * @param position position at which a new node is to be inserted + */ + public void insertNth(int data, int position) { + checkBounds(position, 0, size); + SinglyLinkedListNode newNode = new SinglyLinkedListNode(data); + if (head == null) { + /* the list is empty */ + head = newNode; + size++; + return; + } + if (position == 0) { + /* insert at the head of the list */ + newNode.next = head; + head = newNode; + size++; + return; + } + + SinglyLinkedListNode cur = head; + for (int i = 0; i < position - 1; ++i) { + cur = cur.next; + } + newNode.next = cur.next; + cur.next = newNode; + size++; + } + + /** + * Deletes a node at the head + */ + public void deleteHead() { + deleteNth(0); + } + + /** + * Deletes an element at the tail + */ + public void delete() { + deleteNth(size - 1); + } + + /** + * Deletes an element at Nth position + */ + public void deleteNth(int position) { + checkBounds(position, 0, size - 1); + if (position == 0) { + head = head.next; + /* clear to let GC do its work */ + size--; + return; + } + SinglyLinkedListNode cur = head; + for (int i = 0; i < position - 1; ++i) { + cur = cur.next; + } + + cur.next = cur.next.next; + size--; + } + + /** + * Return element at special index. + * + * @param index given index of element + * @return element at special index. + */ + public int getNth(int index) { + checkBounds(index, 0, size - 1); + SinglyLinkedListNode cur = head; + for (int i = 0; i < index; ++i) { + cur = cur.next; + } + return cur.value; + } + + /** + * @param position to check position + * @param low low index + * @param high high index + * @throws IndexOutOfBoundsException if {@code position} not in range + * {@code low} to {@code high} + */ + public void checkBounds(int position, int low, int high) { + if (position > high || position < low) { + throw new IndexOutOfBoundsException(position + ""); + } + } + + /** + * Driver Code + */ + public static void main(String[] arg) { + SinglyLinkedList list = new SinglyLinkedList(); + assert list.isEmpty(); + assert list.size() == 0 && list.count() == 0; + assert list.toString().isEmpty(); + + /* Test insert function */ + list.insertHead(5); + list.insertHead(7); + list.insertHead(10); + list.insert(3); + list.insertNth(1, 4); + assert list.toString().equals("10->7->5->3->1"); + System.out.println(list); + /* Test search function */ + assert list.search(10) && list.search(5) && list.search(1) && !list.search(100); + + /* Test get function */ + assert list.getNth(0) == 10 && list.getNth(2) == 5 && list.getNth(4) == 1; + + /* Test delete function */ + list.deleteHead(); + list.deleteNth(1); + list.delete(); + assert list.toString().equals("7->3"); + System.out.println(list); + assert list.size == 2 && list.size() == list.count(); + + list.clear(); + assert list.isEmpty(); + + try { + list.delete(); + assert false; + /* this should not happen */ + } catch (Exception e) { + assert true; + /* this should happen */ + } + + SinglyLinkedList instance = new SinglyLinkedList(); + SinglyLinkedListNode head = new SinglyLinkedListNode(0, new SinglyLinkedListNode(2, new SinglyLinkedListNode(3, new SinglyLinkedListNode(3, new SinglyLinkedListNode(4))))); + instance.setHead(head); + instance.deleteDuplicates(); + instance.print(); + } + + @Override + public Iterator<Integer> iterator() { + return new SinglyLinkedListIterator(); + } + + private class SinglyLinkedListIterator implements Iterator<Integer> { + private SinglyLinkedListNode current; + + SinglyLinkedListIterator() { + current = head; + } + + @Override + public boolean hasNext() { + return current != null; + } + + @Override + public Integer next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + final var value = current.value; + current = current.next; + return value; + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/lists/SinglyLinkedListNode.java b/src/main/java/com/thealgorithms/datastructures/lists/SinglyLinkedListNode.java new file mode 100644 index 000000000000..d0a06369215a --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/lists/SinglyLinkedListNode.java @@ -0,0 +1,34 @@ +package com.thealgorithms.datastructures.lists; + +/** + * This class is the nodes of the SinglyLinked List. They consist of a value and + * a pointer to the node after them. + */ +class SinglyLinkedListNode { + + int value; + SinglyLinkedListNode next = null; + + SinglyLinkedListNode() { + } + + /** + * Constructor + * + * @param value Value to be put in the node + */ + SinglyLinkedListNode(int value) { + this(value, null); + } + + /** + * Constructor + * + * @param value Value to be put in the node + * @param next Reference to the next node + */ + SinglyLinkedListNode(int value, SinglyLinkedListNode next) { + this.value = value; + this.next = next; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/lists/SkipList.java b/src/main/java/com/thealgorithms/datastructures/lists/SkipList.java new file mode 100644 index 000000000000..3309ab24917d --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/lists/SkipList.java @@ -0,0 +1,329 @@ +package com.thealgorithms.datastructures.lists; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Random; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * Skip list is a data structure that allows {@code O(log n)} search complexity + * as well as {@code O(log n)} insertion complexity within an ordered sequence + * of {@code n} elements. Thus it can get the best features of a sorted array + * (for searching) while maintaining a linked list-like structure that allows + * insertion, which is not possible with a static array. + * <p> + * A skip list is built in layers. The bottom layer is an ordinary ordered + * linked list. Each higher layer acts as an "express lane" for the lists + * below. + * <pre> + * [ ] ------> [ ] --> [ ] + * [ ] --> [ ] [ ] --> [ ] + * [ ] [ ] [ ] [ ] [ ] [ ] + * H 0 1 2 3 4 + * </pre> + * + * @param <E> type of elements + * @see <a href="https://en.wikipedia.org/wiki/Skip_list">Wiki. Skip list</a> + */ +public class SkipList<E extends Comparable<E>> { + + /** + * Node before first node. + */ + private final Node<E> head; + + /** + * Maximum layers count. + * Calculated by {@link #heightStrategy}. + */ + private final int height; + + /** + * Function for determining height of new nodes. + * @see HeightStrategy + */ + private final HeightStrategy heightStrategy; + + /** + * Current count of elements in list. + */ + private int size; + + private static final int DEFAULT_CAPACITY = 100; + + public SkipList() { + this(DEFAULT_CAPACITY, new BernoulliHeightStrategy()); + } + + public SkipList(int expectedCapacity, HeightStrategy heightStrategy) { + this.heightStrategy = heightStrategy; + this.height = heightStrategy.height(expectedCapacity); + this.head = new Node<>(null, this.height); + this.size = 0; + } + + public void add(E e) { + Objects.requireNonNull(e); + Node<E> current = head; + int layer = height; + Node<E>[] toFix = new Node[height + 1]; + + while (layer >= 0) { + Node<E> next = current.next(layer); + if (next == null || next.getValue().compareTo(e) > 0) { + toFix[layer] = current; + layer--; + } else { + current = next; + } + } + int nodeHeight = heightStrategy.nodeHeight(height); + Node<E> node = new Node<>(e, nodeHeight); + for (int i = 0; i <= nodeHeight; i++) { + if (toFix[i].next(i) != null) { + node.setNext(i, toFix[i].next(i)); + toFix[i].next(i).setPrevious(i, node); + } + + toFix[i].setNext(i, node); + node.setPrevious(i, toFix[i]); + } + size++; + } + + public E get(int index) { + int counter = -1; // head index + Node<E> current = head; + while (counter != index) { + current = current.next(0); + counter++; + } + return current.value; + } + + public void remove(E e) { + Objects.requireNonNull(e); + Node<E> current = head; + int layer = height; + + while (layer >= 0) { + Node<E> next = current.next(layer); + if (e.equals(current.getValue())) { + break; + } else if (next == null || next.getValue().compareTo(e) > 0) { + layer--; + } else { + current = next; + } + } + for (int i = 0; i <= layer; i++) { + current.previous(i).setNext(i, current.next(i)); + if (current.next(i) != null) { + current.next(i).setPrevious(i, current.previous(i)); + } + } + size--; + } + + /** + * A search for a target element begins at the head element in the top + * list, and proceeds horizontally until the current element is greater + * than or equal to the target. If the current element is equal to the + * target, it has been found. If the current element is greater than the + * target, or the search reaches the end of the linked list, the procedure + * is repeated after returning to the previous element and dropping down + * vertically to the next lower list. + * + * @param e element whose presence in this list is to be tested + * @return true if this list contains the specified element + */ + public boolean contains(E e) { + Objects.requireNonNull(e); + Node<E> current = head; + int layer = height; + + while (layer >= 0) { + Node<E> next = current.next(layer); + if (e.equals(current.getValue())) { + return true; + } else if (next == null || next.getValue().compareTo(e) > 0) { + layer--; + } else { + current = next; + } + } + return false; + } + + public int size() { + return size; + } + + /** + * Print height distribution of the nodes in a manner: + * <pre> + * [ ] --- --- [ ] --- [ ] + * [ ] --- [ ] [ ] --- [ ] + * [ ] [ ] [ ] [ ] [ ] [ ] + * H 0 1 2 3 4 + * </pre> + * Values of nodes is not presented. + * + * @return string representation + */ + @Override + public String toString() { + List<boolean[]> layers = new ArrayList<>(); + int sizeWithHeader = size + 1; + for (int i = 0; i <= height; i++) { + layers.add(new boolean[sizeWithHeader]); + } + + Node<E> current = head; + int position = 0; + while (current != null) { + for (int i = 0; i <= current.height; i++) { + layers.get(i)[position] = true; + } + current = current.next(0); + position++; + } + + Collections.reverse(layers); + String result = layers.stream() + .map(layer -> { + StringBuilder acc = new StringBuilder(); + for (boolean b : layer) { + if (b) { + acc.append("[ ]"); + } else { + acc.append("---"); + } + acc.append(" "); + } + return acc.toString(); + }) + .collect(Collectors.joining("\n")); + String positions = IntStream.range(0, sizeWithHeader - 1).mapToObj(i -> String.format("%3d", i)).collect(Collectors.joining(" ")); + + return result + String.format("%n H %s%n", positions); + } + + /** + * Value container. + * Each node have pointers to the closest nodes left and right from current + * on each layer of nodes height. + * @param <E> type of elements + */ + private static class Node<E> { + + private final E value; + private final int height; + private final List<Node<E>> forward; + private final List<Node<E>> backward; + + @SuppressWarnings("unchecked") + Node(E value, int height) { + this.value = value; + this.height = height; + + // predefined size lists with null values in every cell + this.forward = Arrays.asList(new Node[height + 1]); + this.backward = Arrays.asList(new Node[height + 1]); + } + + public Node<E> next(int layer) { + checkLayer(layer); + return forward.get(layer); + } + + public void setNext(int layer, Node<E> node) { + forward.set(layer, node); + } + + public void setPrevious(int layer, Node<E> node) { + backward.set(layer, node); + } + + public Node<E> previous(int layer) { + checkLayer(layer); + return backward.get(layer); + } + + public E getValue() { + return value; + } + + private void checkLayer(int layer) { + if (layer < 0 || layer > height) { + throw new IllegalArgumentException(); + } + } + } + + /** + * Height strategy is a way of calculating maximum height for skip list + * and height for each node. + * @see BernoulliHeightStrategy + */ + public interface HeightStrategy { + int height(int expectedSize); + int nodeHeight(int heightCap); + } + + /** + * In most common skip list realisation element in layer {@code i} appears + * in layer {@code i+1} with some fixed probability {@code p}. + * Two commonly used values for {@code p} are 1/2 and 1/4. + * Probability of appearing element in layer {@code i} could be calculated + * with <code>P = p<sup>i</sup>(1 - p)</code> + * <p> + * Maximum height that would give the best search complexity + * calculated by <code>log<sub>1/p</sub>n</code> + * where {@code n} is an expected count of elements in list. + */ + public static class BernoulliHeightStrategy implements HeightStrategy { + + private final double probability; + + private static final double DEFAULT_PROBABILITY = 0.5; + private static final Random RANDOM = new Random(); + + public BernoulliHeightStrategy() { + this.probability = DEFAULT_PROBABILITY; + } + + public BernoulliHeightStrategy(double probability) { + if (probability <= 0 || probability >= 1) { + throw new IllegalArgumentException("Probability should be from 0 to 1. But was: " + probability); + } + this.probability = probability; + } + + @Override + public int height(int expectedSize) { + long height = Math.round(Math.log10(expectedSize) / Math.log10(1 / probability)); + if (height > Integer.MAX_VALUE) { + throw new IllegalArgumentException(); + } + return (int) height; + } + + @Override + public int nodeHeight(int heightCap) { + int level = 0; + double border = 100 * (1 - probability); + while (((RANDOM.nextInt(Integer.MAX_VALUE) % 100) + 1) > border) { + if (level + 1 >= heightCap) { + return level; + } + level++; + } + return level; + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/lists/SortedLinkedList.java b/src/main/java/com/thealgorithms/datastructures/lists/SortedLinkedList.java new file mode 100644 index 000000000000..e515c9e4adc4 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/lists/SortedLinkedList.java @@ -0,0 +1,159 @@ +package com.thealgorithms.datastructures.lists; + +import java.util.ArrayList; +import java.util.List; + +/** + * The SortedLinkedList class represents a singly linked list that maintains its elements in sorted order. + * Elements are ordered based on their natural ordering, with smaller elements at the head and larger elements toward the tail. + * The class provides methods for inserting, deleting, and searching elements, as well as checking if the list is empty. + * <p> + * This implementation utilizes a singly linked list to maintain a dynamically sorted list. + * </p> + * <p> + * Further information can be found here: + * https://runestone.academy/ns/books/published/cppds/LinearLinked/ImplementinganOrderedList.html + * </p> + * + * <b>Usage Example:</b> + * <pre> + * SortedLinkedList list = new SortedLinkedList(); + * list.insert(10); + * list.insert(5); + * list.insert(20); + * System.out.println(list); // Outputs: [5, 10, 20] + * </pre> + */ +public class SortedLinkedList { + private Node head; + private Node tail; + + /** + * Initializes an empty sorted linked list. + */ + public SortedLinkedList() { + this.head = null; + this.tail = null; + } + + /** + * Inserts a new integer into the list, maintaining sorted order. + * + * @param value the integer to insert + */ + public void insert(int value) { + Node newNode = new Node(value); + if (head == null) { + this.head = newNode; + this.tail = newNode; + } else if (value < head.value) { + newNode.next = this.head; + this.head = newNode; + } else if (value > tail.value) { + this.tail.next = newNode; + this.tail = newNode; + } else { + Node temp = head; + while (temp.next != null && temp.next.value < value) { + temp = temp.next; + } + newNode.next = temp.next; + temp.next = newNode; + if (newNode.next == null) { + this.tail = newNode; + } + } + } + + /** + * Deletes the first occurrence of a specified integer in the list. + * + * @param value the integer to delete + * @return {@code true} if the element was found and deleted; {@code false} otherwise + */ + public boolean delete(int value) { + if (this.head == null) { + return false; + } else if (this.head.value == value) { + if (this.head.next == null) { + this.head = null; + this.tail = null; + } else { + this.head = this.head.next; + } + return true; + } else { + Node temp = this.head; + while (temp.next != null) { + if (temp.next.value == value) { + if (temp.next == this.tail) { + this.tail = temp; + } + temp.next = temp.next.next; + return true; + } + temp = temp.next; + } + return false; + } + } + + /** + * Searches for a specified integer in the list. + * + * @param value the integer to search for + * @return {@code true} if the value is present in the list; {@code false} otherwise + */ + public boolean search(int value) { + Node temp = this.head; + while (temp != null) { + if (temp.value == value) { + return true; + } + temp = temp.next; + } + return false; + } + + /** + * Checks if the list is empty. + * + * @return {@code true} if the list is empty; {@code false} otherwise + */ + public boolean isEmpty() { + return head == null; + } + + /** + * Returns a string representation of the sorted linked list in the format [element1, element2, ...]. + * + * @return a string representation of the sorted linked list + */ + @Override + public String toString() { + if (this.head != null) { + List<String> elements = new ArrayList<>(); + Node temp = this.head; + while (temp != null) { + elements.add(String.valueOf(temp.value)); + temp = temp.next; + } + return "[" + String.join(", ", elements) + "]"; + } else { + return "[]"; + } + } + + /** + * Node represents an element in the sorted linked list. + */ + public final class Node { + public final int value; + public Node next; + + public Node(int value) { + this.value = value; + this.next = null; + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/queues/CircularQueue.java b/src/main/java/com/thealgorithms/datastructures/queues/CircularQueue.java new file mode 100644 index 000000000000..74ee06ca92e4 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/queues/CircularQueue.java @@ -0,0 +1,138 @@ +package com.thealgorithms.datastructures.queues; + +/** + * The CircularQueue class represents a generic circular queue data structure that uses an array to + * store elements. This queue allows efficient utilization of space by wrapping around the array, + * thus avoiding the need to shift elements during enqueue and dequeue operations. + * + * <p>When the queue reaches its maximum capacity, further enqueues will raise an exception. + * Similarly, attempts to dequeue or peek from an empty queue will also result in an exception. + * + * <p>Reference: <a href="https://en.wikipedia.org/wiki/Circular_buffer">Circular Buffer</a> + * + * <p>Usage Example: + * <pre> + * CircularQueue<Integer> queue = new CircularQueue<>(3); + * queue.enQueue(1); + * queue.enQueue(2); + * queue.enQueue(3); + * queue.deQueue(); // Removes 1 + * queue.enQueue(4); // Wraps around and places 4 at the position of removed 1 + * </pre> + * + * @param <T> the type of elements in this queue + */ +public class CircularQueue<T> { + private T[] array; + private int topOfQueue; + private int beginningOfQueue; + private final int size; + private int currentSize; + + /** + * Constructs a CircularQueue with a specified capacity. + * + * @param size the maximum number of elements this queue can hold + * @throws IllegalArgumentException if the size is less than 1 + */ + @SuppressWarnings("unchecked") + public CircularQueue(int size) { + if (size < 1) { + throw new IllegalArgumentException("Size must be greater than 0"); + } + this.array = (T[]) new Object[size]; + this.topOfQueue = -1; + this.beginningOfQueue = -1; + this.size = size; + this.currentSize = 0; + } + + /** + * Checks if the queue is empty. + * + * @return {@code true} if the queue is empty; {@code false} otherwise + */ + public boolean isEmpty() { + return currentSize == 0; + } + + /** + * Checks if the queue is full. + * + * @return {@code true} if the queue has reached its maximum capacity; {@code false} otherwise + */ + public boolean isFull() { + return currentSize == size; + } + + /** + * Adds a new element to the queue. If the queue is full, an exception is thrown. + * + * @param value the element to be added to the queue + * @throws IllegalStateException if the queue is already full + */ + public void enQueue(T value) { + if (isFull()) { + throw new IllegalStateException("Queue is full"); + } + if (isEmpty()) { + beginningOfQueue = 0; + } + topOfQueue = (topOfQueue + 1) % size; + array[topOfQueue] = value; + currentSize++; + } + + /** + * Removes and returns the element at the front of the queue. + * + * @return the element at the front of the queue + * @throws IllegalStateException if the queue is empty + */ + public T deQueue() { + if (isEmpty()) { + throw new IllegalStateException("Queue is empty"); + } + T removedValue = array[beginningOfQueue]; + array[beginningOfQueue] = null; // Optional: Nullify to help garbage collection + beginningOfQueue = (beginningOfQueue + 1) % size; + currentSize--; + if (isEmpty()) { + beginningOfQueue = -1; + topOfQueue = -1; + } + return removedValue; + } + + /** + * Returns the element at the front of the queue without removing it. + * + * @return the element at the front of the queue + * @throws IllegalStateException if the queue is empty + */ + public T peek() { + if (isEmpty()) { + throw new IllegalStateException("Queue is empty"); + } + return array[beginningOfQueue]; + } + + /** + * Deletes the entire queue by resetting all elements and pointers. + */ + public void deleteQueue() { + array = null; + beginningOfQueue = -1; + topOfQueue = -1; + currentSize = 0; + } + + /** + * Returns the current number of elements in the queue. + * + * @return the number of elements currently in the queue + */ + public int size() { + return currentSize; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/queues/Deque.java b/src/main/java/com/thealgorithms/datastructures/queues/Deque.java new file mode 100644 index 000000000000..4cfa2b442ca0 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/queues/Deque.java @@ -0,0 +1,212 @@ +package com.thealgorithms.datastructures.queues; + +import java.util.NoSuchElementException; + +/** + * A [deque](https://en.wikipedia.org/wiki/Double-ended_queue) is short for a + * double ended queue pronounced "deck" and sometimes referred to as a head-tail + * linked list. A deque is a data structure based on a doubly linked list, but + * only supports adding and removal of nodes from the beginning and the end of + * the list. + * + * @author [Ian Cowan](https://github.com/iccowan) + */ +public class Deque<T> { + + /** + * Node for the deque + */ + private static class DequeNode<S> { + S val; + DequeNode<S> next = null; + DequeNode<S> prev = null; + + DequeNode(S val) { + this.val = val; + } + } + + private DequeNode<T> head = null; + private DequeNode<T> tail = null; + private int size = 0; + + /** + * Adds the specified value to the head of the deque + * + * @param val Value to add to the deque + */ + public void addFirst(T val) { + DequeNode<T> newNode = new DequeNode<>(val); + + if (isEmpty()) { + head = newNode; + tail = newNode; + } else { + newNode.next = head; + head.prev = newNode; + head = newNode; + } + + size++; + } + + /** + * Adds the specified value to the tail of the deque + * + * @param val Value to add to the deque + */ + public void addLast(T val) { + DequeNode<T> newNode = new DequeNode<>(val); + if (tail == null) { + head = newNode; + tail = newNode; + } else { + newNode.prev = tail; + tail.next = newNode; + tail = newNode; + } + size++; + } + + /** + * Removes and returns the first (head) value in the deque + * + * @return the value of the head of the deque + * @throws NoSuchElementException if the deque is empty + */ + public T pollFirst() { + if (head == null) { + throw new NoSuchElementException("Deque is empty"); + } + + T oldHeadVal = head.val; + if (head == tail) { + head = null; + tail = null; + } else { + head = head.next; + head.prev = null; + } + size--; + return oldHeadVal; + } + + /** + * Removes and returns the last (tail) value in the deque + * + * @return the value of the tail of the deque + * @throws NoSuchElementException if the deque is empty + */ + public T pollLast() { + if (tail == null) { + throw new NoSuchElementException("Deque is empty"); + } + + T oldTailVal = tail.val; + if (head == tail) { + head = null; + tail = null; + } else { + tail = tail.prev; + tail.next = null; + } + size--; + return oldTailVal; + } + + /** + * Returns the first (head) value of the deque WITHOUT removing + * + * @return the value of the head of the deque, or null if empty + */ + public T peekFirst() { + return head != null ? head.val : null; + } + + /** + * Returns the last (tail) value of the deque WITHOUT removing + * + * @return the value of the tail of the deque, or null if empty + */ + public T peekLast() { + return tail != null ? tail.val : null; + } + + /** + * Returns the size of the deque + * + * @return the size of the deque + */ + public int size() { + return size; + } + + /** + * Returns whether or not the deque is empty + * + * @return whether or not the deque is empty + */ + public boolean isEmpty() { + return size == 0; + } + + /** + * Returns a stringified deque in a pretty form: + * + * <p> + * Head -> 1 <-> 2 <-> 3 <- Tail + * + * @return the stringified deque + */ + @Override + public String toString() { + StringBuilder dequeString = new StringBuilder("Head -> "); + DequeNode<T> currNode = head; + while (currNode != null) { + dequeString.append(currNode.val); + if (currNode.next != null) { + dequeString.append(" <-> "); + } + currNode = currNode.next; + } + dequeString.append(" <- Tail"); + return dequeString.toString(); + } + + public static void main(String[] args) { + Deque<Integer> myDeque = new Deque<>(); + for (int i = 0; i < 42; i++) { + if (i / 42.0 < 0.5) { + myDeque.addFirst(i); + } else { + myDeque.addLast(i); + } + } + + System.out.println(myDeque); + System.out.println("Size: " + myDeque.size()); + System.out.println(); + + myDeque.pollFirst(); + myDeque.pollFirst(); + myDeque.pollLast(); + System.out.println(myDeque); + System.out.println("Size: " + myDeque.size()); + System.out.println(); + + int dequeSize = myDeque.size(); + for (int i = 0; i < dequeSize; i++) { + int removing = -1; + if (i / 39.0 < 0.5) { + removing = myDeque.pollFirst(); + } else { + removing = myDeque.pollLast(); + } + + System.out.println("Removing: " + removing); + } + + System.out.println(myDeque); + System.out.println(myDeque.size()); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/queues/GenericArrayListQueue.java b/src/main/java/com/thealgorithms/datastructures/queues/GenericArrayListQueue.java new file mode 100644 index 000000000000..865da7bffc9f --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/queues/GenericArrayListQueue.java @@ -0,0 +1,60 @@ +package com.thealgorithms.datastructures.queues; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class implements a GenericArrayListQueue, a queue data structure that + * holds elements of any type specified at runtime, allowing flexibility in the type + * of elements it stores. + * + * <p>The GenericArrayListQueue operates on a First-In-First-Out (FIFO) basis, where + * elements added first are the first to be removed. New elements are added to the back + * (or rear) of the queue, while removal of elements occurs from the front. + * + * @param <T> The type of elements held in this queue. + */ +public class GenericArrayListQueue<T> { + + /** + * A list that stores the queue's elements in insertion order. + */ + private final List<T> elementList = new ArrayList<>(); + + /** + * Checks if the queue is empty. + * + * @return {@code true} if the queue has no elements; {@code false} otherwise. + */ + public boolean isEmpty() { + return elementList.isEmpty(); + } + + /** + * Retrieves, but does not remove, the element at the front of the queue. + * + * @return The element at the front of the queue, or {@code null} if the queue is empty. + */ + public T peek() { + return isEmpty() ? null : elementList.getFirst(); + } + + /** + * Inserts an element at the back of the queue. + * + * @param element The element to be added to the queue. + * @return {@code true} if the element was successfully added. + */ + public boolean add(T element) { + return elementList.add(element); + } + + /** + * Retrieves and removes the element at the front of the queue. + * + * @return The element removed from the front of the queue, or {@code null} if the queue is empty. + */ + public T poll() { + return isEmpty() ? null : elementList.removeFirst(); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/queues/LinkedQueue.java b/src/main/java/com/thealgorithms/datastructures/queues/LinkedQueue.java new file mode 100644 index 000000000000..6ba16199dbb8 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/queues/LinkedQueue.java @@ -0,0 +1,201 @@ +package com.thealgorithms.datastructures.queues; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +public class LinkedQueue<T> implements Iterable<T> { + + /** + * Node class representing each element in the queue. + */ + private static class Node<T> { + T data; + Node<T> next; + + Node(T data) { + this.data = data; + this.next = null; + } + } + + private Node<T> front; // Front of the queue + private Node<T> rear; // Rear of the queue + private int size; // Size of the queue + + /** + * Initializes an empty LinkedQueue. + */ + public LinkedQueue() { + front = null; + rear = null; + size = 0; + } + + /** + * Checks if the queue is empty. + * + * @return true if the queue is empty, otherwise false. + */ + public boolean isEmpty() { + return size == 0; + } + + /** + * Adds an element to the rear of the queue. + * + * @param data the element to insert. + * @throws IllegalArgumentException if data is null. + */ + public void enqueue(T data) { + if (data == null) { + throw new IllegalArgumentException("Cannot enqueue null data"); + } + + Node<T> newNode = new Node<>(data); + + if (isEmpty()) { + front = newNode; + } else { + rear.next = newNode; + } + rear = newNode; + size++; + } + + /** + * Removes and returns the element at the front of the queue. + * + * @return the element at the front of the queue. + * @throws NoSuchElementException if the queue is empty. + */ + public T dequeue() { + if (isEmpty()) { + throw new NoSuchElementException("Queue is empty"); + } + + T retValue = front.data; + front = front.next; + size--; + + if (isEmpty()) { + rear = null; + } + + return retValue; + } + + /** + * Returns the element at the front of the queue without removing it. + * + * @return the element at the front of the queue. + * @throws NoSuchElementException if the queue is empty. + */ + public T peekFront() { + if (isEmpty()) { + throw new NoSuchElementException("Queue is empty"); + } + return front.data; + } + + /** + * Returns the element at the rear of the queue without removing it. + * + * @return the element at the rear of the queue. + * @throws NoSuchElementException if the queue is empty. + */ + public T peekRear() { + if (isEmpty()) { + throw new NoSuchElementException("Queue is empty"); + } + return rear.data; + } + + /** + * Returns the element at the specified position (1-based index). + * + * @param pos the position to peek at (1-based index). + * @return the element at the specified position. + * @throws IndexOutOfBoundsException if the position is out of range. + */ + public T peek(int pos) { + if (pos < 1 || pos > size) { + throw new IndexOutOfBoundsException("Position " + pos + " out of range!"); + } + + Node<T> node = front; + for (int i = 1; i < pos; i++) { + node = node.next; + } + return node.data; + } + + /** + * Returns an iterator over the elements in the queue. + * + * @return an iterator over the elements in the queue. + */ + @Override + public Iterator<T> iterator() { + return new Iterator<>() { + private Node<T> current = front; + + @Override + public boolean hasNext() { + return current != null; + } + + @Override + public T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + + T data = current.data; + current = current.next; + return data; + } + }; + } + + /** + * Returns the size of the queue. + * + * @return the size of the queue. + */ + public int size() { + return size; + } + + /** + * Clears all elements from the queue. + */ + public void clear() { + front = null; + rear = null; + size = 0; + } + + /** + * Returns a string representation of the queue. + * + * @return a string representation of the queue. + */ + @Override + public String toString() { + if (isEmpty()) { + return "[]"; + } + + StringBuilder sb = new StringBuilder("["); + Node<T> current = front; + while (current != null) { + sb.append(current.data); + if (current.next != null) { + sb.append(", "); + } + current = current.next; + } + sb.append(']'); + return sb.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/queues/PriorityQueues.java b/src/main/java/com/thealgorithms/datastructures/queues/PriorityQueues.java new file mode 100644 index 000000000000..a5ca48670f2c --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/queues/PriorityQueues.java @@ -0,0 +1,179 @@ +package com.thealgorithms.datastructures.queues; + +/** + * This class implements a PriorityQueue. + * + * <p> + * A priority queue adds elements into positions based on their priority. So the + * most important elements are placed at the front/on the top. In this example I + * give numbers that are bigger, a higher priority. Queues in theory have no + * fixed size but when using an array implementation it does. + * <p> + * Additional contibutions made by: PuneetTri(https://github.com/PuneetTri) + */ +class PriorityQueue { + + /** + * The max size of the queue + */ + private int maxSize; + /** + * The array for the queue + */ + private int[] queueArray; + /** + * How many items are in the queue + */ + private int nItems; + + /** + * Default Constructor + */ + + PriorityQueue() { + /* If capacity is not defined, default size of 11 would be used + * capacity=max+1 because we cant access 0th element of PQ, and to + * accomodate (max)th elements we need capacity to be max+1. + * Parent is at position k, child at position (k*2,k*2+1), if we + * use position 0 in our queue, its child would be at: + * (0*2, 0*2+1) -> (0,0). This is why we start at position 1 + */ + int size = 11; // Default value of 11 + maxSize = size + 1; + queueArray = new int[maxSize]; + nItems = 0; + } + + /** + * Parameterized Constructor + * + * @param size Size of the queue + */ + + PriorityQueue(int size) { + maxSize = size + 1; + queueArray = new int[maxSize]; + nItems = 0; + } + + /** + * Helper function for the max-heap implementation of PQ + * Function would help demote parent node to their correct + * position + * + * @param pos Position of newly added element at bottom + */ + private void swim(int pos) { + // Check if parent is smaller than child node + while (pos > 1 && (queueArray[pos / 2] < queueArray[pos])) { + // In such case swap value of child with parent + int temp = queueArray[pos]; + queueArray[pos] = queueArray[pos / 2]; + queueArray[pos / 2] = temp; + pos = pos / 2; // Jump to position of parent node + } + // Promotion of child node will go on until it becomes smaller than the parent + } + + /** + * Helper function for the max-heap implementation of PQ + * Function would help demote parent node to their correct + * position + * + * @param pos Position of element at top + */ + private void sink(int pos) { + // Check if node's position is that of parent node + while (2 * pos <= nItems) { + int current = 2 * pos; // Jump to the positon of child node + // Compare both the children for the greater one + if (current < nItems && queueArray[current] < queueArray[current + 1]) { + current++; + } + // If the parent node is greater, sink operation is complete. Break the loop + if (queueArray[pos] >= queueArray[current]) { + break; + } + + // If not exchange the value of parent with child + int temp = queueArray[pos]; + queueArray[pos] = queueArray[current]; + queueArray[current] = temp; + pos = current; // Exchange parent position to child position in the array + } + } + + /** + * Inserts an element in it's appropriate place + * + * @param value Value to be inserted + */ + public void insert(int value) { + // Print overflow message if the capacity is full + if (isFull()) { + throw new RuntimeException("Queue is full"); + } else { + queueArray[++nItems] = value; + swim(nItems); // Swim up the element to its correct position + } + } + + /** + * Dequeue the element with the max priority from PQ + * + * @return The element removed + */ + public int remove() { + if (isEmpty()) { + throw new RuntimeException("Queue is Empty"); + } else { + int max = queueArray[1]; // By defintion of our max-heap, value at queueArray[1] pos is + // the greatest + + // Swap max and last element + int temp = queueArray[1]; + queueArray[1] = queueArray[nItems]; + queueArray[nItems] = temp; + queueArray[nItems--] = 0; // Nullify the last element from the priority queue + sink(1); // Sink the element in order + + return max; + } + } + + /** + * Checks what's at the front of the queue + * + * @return element at the front of the queue + */ + public int peek() { + return queueArray[1]; + } + + /** + * Returns true if the queue is empty + * + * @return true if the queue is empty + */ + public boolean isEmpty() { + return (nItems == 0); + } + + /** + * Returns true if the queue is full + * + * @return true if the queue is full + */ + public boolean isFull() { + return (nItems == maxSize - 1); + } + + /** + * Returns the number of elements in the queue + * + * @return number of elements in the queue + */ + public int getSize() { + return nItems; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/queues/Queue.java b/src/main/java/com/thealgorithms/datastructures/queues/Queue.java new file mode 100644 index 000000000000..046b79a020ed --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/queues/Queue.java @@ -0,0 +1,153 @@ +package com.thealgorithms.datastructures.queues; + +/** + * This class implements a Queue data structure using an array. + * A queue is a first-in-first-out (FIFO) data structure where elements are + * added to the rear and removed from the front. + * + * Note: This implementation is not thread-safe. + */ +public final class Queue<T> { + + private static final int DEFAULT_CAPACITY = 10; + + private final int maxSize; + private final Object[] queueArray; + private int front; + private int rear; + private int nItems; + + /** + * Initializes a queue with a default capacity. + */ + public Queue() { + this(DEFAULT_CAPACITY); + } + + /** + * Constructor to initialize a queue with a specified capacity. + * + * @param capacity The initial size of the queue. + * @throws IllegalArgumentException if the capacity is less than or equal to zero. + */ + public Queue(int capacity) { + if (capacity <= 0) { + throw new IllegalArgumentException("Queue capacity must be greater than 0"); + } + this.maxSize = capacity; + this.queueArray = new Object[capacity]; + this.front = 0; + this.rear = -1; + this.nItems = 0; + } + + /** + * Inserts an element at the rear of the queue. + * + * @param element Element to be added. + * @return True if the element was added successfully, false if the queue is full. + */ + public boolean insert(T element) { + if (isFull()) { + return false; + } + rear = (rear + 1) % maxSize; + queueArray[rear] = element; + nItems++; + return true; + } + + /** + * Removes and returns the element from the front of the queue. + * + * @return The element removed from the front of the queue. + * @throws IllegalStateException if the queue is empty. + */ + @SuppressWarnings("unchecked") + public T remove() { + if (isEmpty()) { + throw new IllegalStateException("Queue is empty, cannot remove element"); + } + T removedElement = (T) queueArray[front]; + queueArray[front] = null; // Optional: Clear the reference for garbage collection + front = (front + 1) % maxSize; + nItems--; + return removedElement; + } + + /** + * Checks the element at the front of the queue without removing it. + * + * @return Element at the front of the queue. + * @throws IllegalStateException if the queue is empty. + */ + @SuppressWarnings("unchecked") + public T peekFront() { + if (isEmpty()) { + throw new IllegalStateException("Queue is empty, cannot peek front"); + } + return (T) queueArray[front]; + } + + /** + * Checks the element at the rear of the queue without removing it. + * + * @return Element at the rear of the queue. + * @throws IllegalStateException if the queue is empty. + */ + @SuppressWarnings("unchecked") + public T peekRear() { + if (isEmpty()) { + throw new IllegalStateException("Queue is empty, cannot peek rear"); + } + return (T) queueArray[rear]; + } + + /** + * Returns true if the queue is empty. + * + * @return True if the queue is empty. + */ + public boolean isEmpty() { + return nItems == 0; + } + + /** + * Returns true if the queue is full. + * + * @return True if the queue is full. + */ + public boolean isFull() { + return nItems == maxSize; + } + + /** + * Returns the number of elements currently in the queue. + * + * @return Number of elements in the queue. + */ + public int getSize() { + return nItems; + } + + /** + * Returns a string representation of the queue. + * + * @return String representation of the queue. + */ + @Override + public String toString() { + if (isEmpty()) { + return "[]"; + } + StringBuilder sb = new StringBuilder(); + sb.append("["); + for (int i = 0; i < nItems; i++) { + int index = (front + i) % maxSize; + sb.append(queueArray[index]).append(", "); + } + sb.setLength(sb.length() - 2); // Remove the last comma and space + sb.append("]"); + return sb.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/queues/QueueByTwoStacks.java b/src/main/java/com/thealgorithms/datastructures/queues/QueueByTwoStacks.java new file mode 100644 index 000000000000..11e5e9b83892 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/queues/QueueByTwoStacks.java @@ -0,0 +1,87 @@ +package com.thealgorithms.datastructures.queues; + +import java.util.NoSuchElementException; +import java.util.Stack; + +/** + * A queue implementation using two stacks. This class provides methods to + * enqueue (add) elements to the end of the queue and dequeue (remove) + * elements from the front, while utilizing two internal stacks to manage + * the order of elements. + * + * @param <T> The type of elements held in this queue. + */ +public class QueueByTwoStacks<T> { + + private final Stack<T> enqueueStk; + private final Stack<T> dequeueStk; + + /** + * Constructor that initializes two empty stacks for the queue. + * The `enqueueStk` is used to push elements when enqueuing, and + * the `dequeueStk` is used to pop elements when dequeuing. + */ + public QueueByTwoStacks() { + enqueueStk = new Stack<>(); + dequeueStk = new Stack<>(); + } + + /** + * Adds an element to the end of the queue. This method pushes the element + * onto the `enqueueStk`. + * + * @param item The element to be added to the queue. + */ + public void put(T item) { + enqueueStk.push(item); + } + + /** + * Removes and returns the element at the front of the queue. + * If `dequeueStk` is empty, it transfers all elements from + * `enqueueStk` to `dequeueStk` to maintain the correct FIFO + * (First-In-First-Out) order before popping. + * + * @return The element at the front of the queue. + * @throws NoSuchElementException If the queue is empty. + */ + public T get() { + if (dequeueStk.isEmpty()) { + while (!enqueueStk.isEmpty()) { + dequeueStk.push(enqueueStk.pop()); + } + } + if (dequeueStk.isEmpty()) { + throw new NoSuchElementException("Queue is empty"); + } + return dequeueStk.pop(); + } + + /** + * Returns the total number of elements currently in the queue. + * This is the sum of the sizes of both stacks. + * + * @return The number of elements in the queue. + */ + public int size() { + return enqueueStk.size() + dequeueStk.size(); + } + + /** + * Returns a string representation of the queue, showing the elements + * in the correct order (from front to back). + * The `dequeueStk` is first cloned, and then all elements from the + * `enqueueStk` are added to the cloned stack in reverse order to + * represent the queue accurately. + * + * @return A string representation of the queue. + */ + @Override + public String toString() { + Stack<T> tempStack = (Stack<T>) dequeueStk.clone(); + while (!enqueueStk.isEmpty()) { + tempStack.push(enqueueStk.pop()); + } + return "Queue(" + tempStack + ")"; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/queues/README.md b/src/main/java/com/thealgorithms/datastructures/queues/README.md new file mode 100644 index 000000000000..ef1b89b5c9a4 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/queues/README.md @@ -0,0 +1,89 @@ +# Queue +- The Queue interface is present in the `java.util` package. +- It is an ordered list of objects that follows the **FIFO** (First-In-First-Out) principle. + +## Characteristics of a Queue +- The Queue is used to insert elements at the end of the queue and removes elements from the beginning of the queue. +- It supports all methods of Collection interface including insertion, deletion etc. +- LinkedList, ArrayBlockingQueue and PriorityQueue are the most commonly used implementations. + + +## Types Of Queue:- + +- **FIFO Queue (First-In-First-Out):** This is the most common type of queue where the first item added is the first one to be removed. It follows a strict order of insertion and removal. + + +- **Priority Queue:** Elements in this queue are assigned priorities, and the item with the highest priority is dequeued first. It doesn't strictly follow the FIFO order. + + +- **Double-ended Queue (Deque):** A queue that allows elements to be added and removed from both ends. It can function as both a FIFO queue and a LIFO stack. + + +- **Circular Queue:** In this type, the last element is connected to the first element, forming a circular structure. It's often used for tasks like managing memory buffers. + + +- **Blocking Queue:** Designed for multithreaded applications, it provides thread-safety and blocking operations. Threads can wait until an element is available or space is free. + + +- **Priority Blocking Queue:** Similar to a priority queue but thread-safe, it allows multiple threads to access and modify the queue concurrently while maintaining priority. + + +- **Delay Queue:** Used for scheduling tasks to run after a specific delay or at a certain time. Elements are removed from the queue when their delay expires. + +## Declaration +`Queue<Obj> queue = new PriorityQueue<Obj>();` + +## Important operations + +| Operations | Description |Time Complexity +| ----------- | ----------- |----------- +|Enqueue|Adds an item to the queue|O(1) +|Dequeue|Removes an item from the queue|O(1) +|Front|Gets the front item from the queue|O(1) +|Rear|Gets the last item from the queue|O(n) + +## Enqueue + It adds an item to the rear of the queue. + + For example: If we have `1, 2, 3, 4, 5` in queue, and if we call Enqueue(8), + +`8` will be added to last index of queue -> `1, 2, 3, 4, 5, 8`. +## Dequeue + + It removes an item to the front of the queue. + + For example: If we have `1, 2, 3, 4, 5` in queue, and we call Dequeue(), + +`1` will be removed from front of queue and returned -> `2, 3, 4, 5`. + +## Front + It returns an item to the front of the queue. + +For example: If we have `1, 2, 3, 5` in queue, and we call Front(), + +`1` will be returned (without removing it from the queue). + +## Rear + It returns an item to the rear of the queue. + + For example: If we have `1, 2, 3, 5` in queue, and we call Rear(), + +`5` will be returned (without removing it from the queue). + +# Real Life Applications +`Task Scheduling in Operating Systems:` + +Processes in a multitasking system are often scheduled using queues. For example, the ready queue contains processes ready to be executed. + +`Multi-threaded Programming:` + +Queues are often used to facilitate communication and synchronization between different threads. + +`Breadth-First Search (BFS) in Graphs:` + +Queues are used in algorithms like BFS to explore a graph level by level. + + + + + diff --git a/src/main/java/com/thealgorithms/datastructures/queues/SlidingWindowMaximum.java b/src/main/java/com/thealgorithms/datastructures/queues/SlidingWindowMaximum.java new file mode 100644 index 000000000000..d6720dd01e29 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/queues/SlidingWindowMaximum.java @@ -0,0 +1,63 @@ +package com.thealgorithms.datastructures.queues; + +import java.util.Deque; +import java.util.LinkedList; + +/** + * The {@code SlidingWindowMaximum} class provides a method to efficiently compute + * the maximum element within every sliding window of size {@code k} in a given array. + * + * <p>The algorithm uses a deque to maintain the indices of useful elements within + * the current sliding window. The time complexity of this approach is O(n) since + * each element is processed at most twice. + * + * @author Hardvan + */ +public final class SlidingWindowMaximum { + private SlidingWindowMaximum() { + } + + /** + * Returns an array of the maximum values for each sliding window of size {@code k}. + * <p>If {@code nums} has fewer elements than {@code k}, the result will be an empty array. + * <p>Example: + * <pre> + * Input: nums = [1, 3, -1, -3, 5, 3, 6, 7], k = 3 + * Output: [3, 3, 5, 5, 6, 7] + * </pre> + * + * @param nums the input array of integers + * @param k the size of the sliding window + * @return an array containing the maximum element for each sliding window + */ + public static int[] maxSlidingWindow(int[] nums, int k) { + int n = nums.length; + if (n < k || k == 0) { + return new int[0]; + } + + int[] result = new int[n - k + 1]; + Deque<Integer> deque = new LinkedList<>(); + for (int i = 0; i < n; i++) { + // Remove elements from the front of the deque if they are out of the current window + if (!deque.isEmpty() && deque.peekFirst() < i - k + 1) { + deque.pollFirst(); + } + + // Remove elements from the back if they are smaller than the current element + while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) { + deque.pollLast(); + } + + // Add the current element's index to the deque + deque.offerLast(i); + + // Store the maximum element for the current window (starting from the k-1th element) + if (i >= k - 1) { + result[i - k + 1] = nums[deque.peekFirst()]; + } + } + + return result; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/queues/TokenBucket.java b/src/main/java/com/thealgorithms/datastructures/queues/TokenBucket.java new file mode 100644 index 000000000000..999c963fab93 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/queues/TokenBucket.java @@ -0,0 +1,61 @@ +package com.thealgorithms.datastructures.queues; + +import java.util.concurrent.TimeUnit; + +/** + * TokenBucket implements a token bucket rate limiter algorithm. + * This class is used to control the rate of requests in a distributed system. + * It allows a certain number of requests (tokens) to be processed in a time frame, + * based on the defined refill rate. + * + * Applications: Computer networks, API rate limiting, distributed systems, etc. + * + * @author Hardvan + */ +public final class TokenBucket { + private final int maxTokens; + private final int refillRate; // tokens per second + private int tokens; + private long lastRefill; // Timestamp in nanoseconds + + /** + * Constructs a TokenBucket instance. + * + * @param maxTokens Maximum number of tokens the bucket can hold. + * @param refillRate The rate at which tokens are refilled (tokens per second). + */ + public TokenBucket(int maxTokens, int refillRate) { + this.maxTokens = maxTokens; + this.refillRate = refillRate; + this.tokens = maxTokens; + this.lastRefill = System.nanoTime(); + } + + /** + * Attempts to allow a request based on the available tokens. + * If a token is available, it decrements the token count and allows the request. + * Otherwise, the request is denied. + * + * @return true if the request is allowed, false if the request is denied. + */ + public synchronized boolean allowRequest() { + refillTokens(); + if (tokens > 0) { + tokens--; + return true; + } + return false; + } + + /** + * Refills the tokens based on the time elapsed since the last refill. + * The number of tokens to be added is calculated based on the elapsed time + * and the refill rate, ensuring the total does not exceed maxTokens. + */ + private void refillTokens() { + long now = System.nanoTime(); + long tokensToAdd = (now - lastRefill) / TimeUnit.SECONDS.toNanos(1) * refillRate; + tokens = Math.min(maxTokens, tokens + (int) tokensToAdd); + lastRefill = now; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/stacks/NodeStack.java b/src/main/java/com/thealgorithms/datastructures/stacks/NodeStack.java new file mode 100644 index 000000000000..bbcdfe1cc2a8 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/stacks/NodeStack.java @@ -0,0 +1,98 @@ +package com.thealgorithms.datastructures.stacks; + +/** + * A stack implementation using linked nodes, supporting unlimited size without an ArrayList. + * + * <p>Each node in the stack contains data of generic type {@code Item}, along with references + * to the next and previous nodes, supporting typical stack operations. + * + * <p>The stack follows a Last-In-First-Out (LIFO) order where elements added last are + * removed first. Supported operations include push, pop, and peek. + * + * @param <Item> the type of elements held in this stack + */ +public class NodeStack<Item> { + + /** + * Node class representing each element in the stack. + */ + private class Node { + Item data; + Node previous; + + Node(Item data) { + this.data = data; + this.previous = null; + } + } + + private Node head; // Top node in the stack + private int size; // Number of elements in the stack + + /** + * Constructs an empty NodeStack. + */ + public NodeStack() { + head = null; + size = 0; + } + + /** + * Pushes an item onto the stack. + * + * @param item the item to be pushed onto the stack + */ + public void push(Item item) { + Node newNode = new Node(item); + newNode.previous = head; + head = newNode; + size++; + } + + /** + * Removes and returns the item at the top of the stack. + * + * @return the item at the top of the stack, or {@code null} if the stack is empty + * @throws IllegalStateException if the stack is empty + */ + public Item pop() { + if (isEmpty()) { + throw new IllegalStateException("Cannot pop from an empty stack."); + } + Item data = head.data; + head = head.previous; + size--; + return data; + } + + /** + * Returns the item at the top of the stack without removing it. + * + * @return the item at the top of the stack, or {@code null} if the stack is empty + * @throws IllegalStateException if the stack is empty + */ + public Item peek() { + if (isEmpty()) { + throw new IllegalStateException("Cannot peek from an empty stack."); + } + return head.data; + } + + /** + * Checks whether the stack is empty. + * + * @return {@code true} if the stack has no elements, {@code false} otherwise + */ + public boolean isEmpty() { + return head == null; + } + + /** + * Returns the number of elements currently in the stack. + * + * @return the size of the stack + */ + public int size() { + return size; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/stacks/README.md b/src/main/java/com/thealgorithms/datastructures/stacks/README.md new file mode 100644 index 000000000000..55c3ffd7de64 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/stacks/README.md @@ -0,0 +1,45 @@ +# STACK + +- Stack is an ADT (abstract data type) that is a collection of elements where items are added and removed from the end, known as the "top" of the stack. + +- Stack works on the principle of _LIFO_ (Last In First Out), it means that the last item added to the stack will be the first item to be removed. + +## Declaration + `Stack<Obj> stack=new Stack<Obj>();` + +# Functionalities +Stack is based on two functions (methods)- + +## push(element) + +It adds "element" to the top of the stack. + +For example: If we have `1, 3, 5` in stack, and we call push(9), + +`9` will be added to last index of stack -> `1, 3, 5 , 9`. + +## peek() or top() + +It returns element at the top of the stack. + +For example: If we have `1, 3, 5` in stack, and we call peek(), + +`5` will be returned (without removing it from the stack). + +## pop() + +It removes the last element (i.e. top of stack) from stack. + +For example: If we have `1, 3, 5 , 9` in stack, and we call pop(), + +the function will return `9` and the stack will change to `1, 3, 5`. + +# Real Life Applications + - `Undo mechanisms:` + Many software applications use stacks to implement an "undo" feature. + + - `Browser history:` + The "back" button in a web browser is implemented using a stack, allowing users to navigate through previously visited pages. + + - `Function calls and recursion:` + The computer's call stack keeps track of function calls, allowing programs to remember where to return after a function finishes execution. diff --git a/src/main/java/com/thealgorithms/datastructures/stacks/ReverseStack.java b/src/main/java/com/thealgorithms/datastructures/stacks/ReverseStack.java new file mode 100644 index 000000000000..e9de14b53302 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/stacks/ReverseStack.java @@ -0,0 +1,74 @@ +package com.thealgorithms.datastructures.stacks; + +import java.util.Stack; + +/** + * Provides methods to reverse a stack using recursion. + * + * <p>This class includes methods to reverse the order of elements in a stack + * without using additional data structures. Elements are inserted at the bottom + * of the stack to achieve the reverse order. + * + * <p>Example usage: + * <pre> + * Stack<Integer> stack = new Stack<>(); + * stack.push(1); + * stack.push(2); + * stack.push(3); + * ReverseStack.reverseStack(stack); + * </pre> + * After calling {@code reverseStack(stack)}, the stack's order is reversed. + * + * <p>This class is final and has a private constructor to prevent instantiation. + * + * @author Ishika Agarwal, 2021 + */ +public final class ReverseStack { + private ReverseStack() { + } + + /** + * Reverses the order of elements in the given stack using recursion. + * Steps: + * 1. Check if the stack is empty. If so, return. + * 2. Pop the top element from the stack. + * 3. Recursively reverse the remaining stack. + * 4. Insert the originally popped element at the bottom of the reversed stack. + * + * @param stack the stack to reverse; should not be null + */ + public static void reverseStack(Stack<Integer> stack) { + if (stack.isEmpty()) { + return; + } + + int element = stack.pop(); + reverseStack(stack); + insertAtBottom(stack, element); + } + + /** + * Inserts the specified element at the bottom of the stack. + * + * <p>This method is a helper for {@link #reverseStack(Stack)}. + * + * Steps: + * 1. If the stack is empty, push the element and return. + * 2. Remove the top element from the stack. + * 3. Recursively insert the new element at the bottom of the stack. + * 4. Push the removed element back onto the stack. + * + * @param stack the stack in which to insert the element; should not be null + * @param element the element to insert at the bottom of the stack + */ + private static void insertAtBottom(Stack<Integer> stack, int element) { + if (stack.isEmpty()) { + stack.push(element); + return; + } + + int topElement = stack.pop(); + insertAtBottom(stack, element); + stack.push(topElement); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/stacks/Stack.java b/src/main/java/com/thealgorithms/datastructures/stacks/Stack.java new file mode 100644 index 000000000000..87058bd9750f --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/stacks/Stack.java @@ -0,0 +1,51 @@ +package com.thealgorithms.datastructures.stacks; + +/** + * A generic interface for Stack data structures. + * + * @param <T> the type of elements in this stack + */ +public interface Stack<T> { + + /** + * Adds an element to the top of the stack. + * + * @param value The element to add. + */ + void push(T value); + + /** + * Removes the element at the top of this stack and returns it. + * + * @return The element popped from the stack. + * @throws IllegalStateException if the stack is empty. + */ + T pop(); + + /** + * Returns the element at the top of this stack without removing it. + * + * @return The element at the top of this stack. + * @throws IllegalStateException if the stack is empty. + */ + T peek(); + + /** + * Tests if this stack is empty. + * + * @return {@code true} if this stack is empty; {@code false} otherwise. + */ + boolean isEmpty(); + + /** + * Returns the size of this stack. + * + * @return The number of elements in this stack. + */ + int size(); + + /** + * Removes all elements from this stack. + */ + void makeEmpty(); +} diff --git a/src/main/java/com/thealgorithms/datastructures/stacks/StackArray.java b/src/main/java/com/thealgorithms/datastructures/stacks/StackArray.java new file mode 100644 index 000000000000..9369b3fc9a64 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/stacks/StackArray.java @@ -0,0 +1,160 @@ +package com.thealgorithms.datastructures.stacks; + +/** + * Implements a generic stack using an array. + * + * <p>This stack automatically resizes when necessary, growing to accommodate additional elements and + * shrinking to conserve memory when its size significantly decreases. + * + * <p>Elements are pushed and popped in LIFO (last-in, first-out) order, where the last element added + * is the first to be removed. + * + * @param <T> the type of elements in this stack + */ +public class StackArray<T> implements Stack<T> { + + private static final int DEFAULT_CAPACITY = 10; + + private int maxSize; + private T[] stackArray; + private int top; + + /** + * Creates a stack with a default capacity. + */ + @SuppressWarnings("unchecked") + public StackArray() { + this(DEFAULT_CAPACITY); + } + + /** + * Creates a stack with a specified initial capacity. + * + * @param size the initial capacity of the stack, must be greater than 0 + * @throws IllegalArgumentException if size is less than or equal to 0 + */ + @SuppressWarnings("unchecked") + public StackArray(int size) { + if (size <= 0) { + throw new IllegalArgumentException("Stack size must be greater than 0"); + } + this.maxSize = size; + this.stackArray = (T[]) new Object[size]; + this.top = -1; + } + + /** + * Pushes an element onto the top of the stack. Resizes the stack if it is full. + * + * @param value the element to push + */ + @Override + public void push(T value) { + if (isFull()) { + resize(maxSize * 2); + } + stackArray[++top] = value; + } + + /** + * Removes and returns the element from the top of the stack. Shrinks the stack if + * its size is below a quarter of its capacity, but not below the default capacity. + * + * @return the element removed from the top of the stack + * @throws IllegalStateException if the stack is empty + */ + @Override + public T pop() { + if (isEmpty()) { + throw new IllegalStateException("Stack is empty, cannot pop element"); + } + T value = stackArray[top--]; + if (top + 1 < maxSize / 4 && maxSize > DEFAULT_CAPACITY) { + resize(maxSize / 2); + } + return value; + } + + /** + * Returns the element at the top of the stack without removing it. + * + * @return the top element of the stack + * @throws IllegalStateException if the stack is empty + */ + @Override + public T peek() { + if (isEmpty()) { + throw new IllegalStateException("Stack is empty, cannot peek element"); + } + return stackArray[top]; + } + + /** + * Resizes the internal array to a new capacity. + * + * @param newSize the new size of the stack array + */ + private void resize(int newSize) { + @SuppressWarnings("unchecked") T[] newArray = (T[]) new Object[newSize]; + System.arraycopy(stackArray, 0, newArray, 0, top + 1); + stackArray = newArray; + maxSize = newSize; + } + + /** + * Checks if the stack is full. + * + * @return true if the stack is full, false otherwise + */ + public boolean isFull() { + return top + 1 == maxSize; + } + + /** + * Checks if the stack is empty. + * + * @return true if the stack is empty, false otherwise + */ + @Override + public boolean isEmpty() { + return top == -1; + } + + /** + * Empties the stack, marking it as empty without deleting elements. Elements are + * overwritten on subsequent pushes. + */ + @Override + public void makeEmpty() { + top = -1; + } + + /** + * Returns the number of elements currently in the stack. + * + * @return the size of the stack + */ + @Override + public int size() { + return top + 1; + } + + /** + * Returns a string representation of the stack. + * + * @return a string representation of the stack + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("StackArray ["); + for (int i = 0; i <= top; i++) { + sb.append(stackArray[i]); + if (i < top) { + sb.append(", "); + } + } + sb.append("]"); + return sb.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/stacks/StackArrayList.java b/src/main/java/com/thealgorithms/datastructures/stacks/StackArrayList.java new file mode 100644 index 000000000000..bd400adea317 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/stacks/StackArrayList.java @@ -0,0 +1,91 @@ +package com.thealgorithms.datastructures.stacks; + +import java.util.ArrayList; +import java.util.EmptyStackException; + +/** + * A stack implementation backed by an {@link ArrayList}, offering dynamic resizing + * and LIFO (Last-In-First-Out) behavior. + * + * <p>The stack grows dynamically as elements are added, and elements are removed + * in reverse order of their addition. + * + * @param <T> the type of elements stored in this stack + */ +public class StackArrayList<T> implements Stack<T> { + + private final ArrayList<T> stack; + + /** + * Constructs an empty stack. + */ + public StackArrayList() { + stack = new ArrayList<>(); + } + + /** + * Adds an element to the top of the stack. + * + * @param value the element to be added + */ + @Override + public void push(T value) { + stack.add(value); + } + + /** + * Removes and returns the element from the top of the stack. + * + * @return the element removed from the top of the stack + * @throws EmptyStackException if the stack is empty + */ + @Override + public T pop() { + if (isEmpty()) { + throw new EmptyStackException(); + } + return stack.removeLast(); + } + + /** + * Returns the element at the top of the stack without removing it. + * + * @return the top element of the stack + * @throws EmptyStackException if the stack is empty + */ + @Override + public T peek() { + if (isEmpty()) { + throw new EmptyStackException(); + } + return stack.getLast(); + } + + /** + * Checks if the stack is empty. + * + * @return {@code true} if the stack is empty, {@code false} otherwise + */ + @Override + public boolean isEmpty() { + return stack.isEmpty(); + } + + /** + * Empties the stack, removing all elements. + */ + @Override + public void makeEmpty() { + stack.clear(); + } + + /** + * Returns the number of elements in the stack. + * + * @return the current size of the stack + */ + @Override + public int size() { + return stack.size(); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/stacks/StackOfLinkedList.java b/src/main/java/com/thealgorithms/datastructures/stacks/StackOfLinkedList.java new file mode 100644 index 000000000000..c12097dfa28c --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/stacks/StackOfLinkedList.java @@ -0,0 +1,135 @@ +package com.thealgorithms.datastructures.stacks; + +import java.util.NoSuchElementException; + +/** + * A stack implementation using a singly linked list. + * + * <p>This class provides methods to push, pop, and peek elements in a Last-In-First-Out (LIFO) manner. + * It keeps track of the number of elements in the stack and allows checking if the stack is empty. + * + * <p>This implementation does not allow null elements to be pushed onto the stack. + */ +final class StackOfLinkedList { + private StackOfLinkedList() { + } +} + +// A node class for the linked list +class Node { + public int data; + public Node next; + + Node(int data) { + this.data = data; + this.next = null; + } +} + +/** + * A class that implements a stack using a linked list. + * + * <p>This stack supports basic operations: + * <ul> + * <li>push: Adds an element to the top of the stack</li> + * <li>pop: Removes and returns the top element of the stack</li> + * <li>peek: Returns the top element without removing it</li> + * <li>isEmpty: Checks if the stack is empty</li> + * <li>getSize: Returns the current size of the stack</li> + * </ul> + */ +class LinkedListStack { + + private Node head; // Top of the stack + private int size; // Number of elements in the stack + + /** + * Initializes an empty stack. + */ + LinkedListStack() { + head = null; + size = 0; + } + + /** + * Adds an element to the top of the stack. + * + * @param x the element to be added + * @return <tt>true</tt> if the element is added successfully + */ + public boolean push(int x) { + Node newNode = new Node(x); + newNode.next = head; + head = newNode; + size++; + return true; + } + + /** + * Removes and returns the top element of the stack. + * + * @return the element at the top of the stack + * @throws NoSuchElementException if the stack is empty + */ + public int pop() { + if (size == 0) { + throw new NoSuchElementException("Empty stack. Nothing to pop"); + } + Node destroy = head; + head = head.next; + int retValue = destroy.data; + destroy = null; // Help garbage collection + size--; + return retValue; + } + + /** + * Returns the top element of the stack without removing it. + * + * @return the element at the top of the stack + * @throws NoSuchElementException if the stack is empty + */ + public int peek() { + if (size == 0) { + throw new NoSuchElementException("Empty stack. Nothing to peek"); + } + return head.data; + } + + @Override + public String toString() { + Node cur = head; + StringBuilder builder = new StringBuilder(); + while (cur != null) { + builder.append(cur.data).append("->"); + cur = cur.next; + } + return builder.replace(builder.length() - 2, builder.length(), "").toString(); // Remove the last "->" + } + + /** + * Checks if the stack is empty. + * + * @return <tt>true</tt> if the stack is empty, <tt>false</tt> otherwise + */ + public boolean isEmpty() { + return size == 0; + } + + /** + * Returns the current size of the stack. + * + * @return the number of elements in the stack + */ + public int getSize() { + return size; + } + + /** + * Removes all elements from the stack. + */ + public void makeEmpty() { + head = null; + size = 0; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/AVLSimple.java b/src/main/java/com/thealgorithms/datastructures/trees/AVLSimple.java new file mode 100644 index 000000000000..e0309122cc12 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/AVLSimple.java @@ -0,0 +1,146 @@ +package com.thealgorithms.datastructures.trees; + +/* +* Avl is algo that balance itself while adding new alues to tree +* by rotating branches of binary tree and make itself Binary seaarch tree +* there are four cases which has to tackle +* rotating - left right ,left left,right right,right left + +Test Case: + +AVLTree tree=new AVLTree(); + tree.insert(20); + tree.insert(25); + tree.insert(30); + tree.insert(10); + tree.insert(5); + tree.insert(15); + tree.insert(27); + tree.insert(19); + tree.insert(16); + + tree.display(); + + + + +*/ + +public class AVLSimple { + + private class Node { + + int data; + int height; + Node left; + Node right; + + Node(int data) { + this.data = data; + this.height = 1; + } + } + + private Node root; + + public void insert(int data) { + this.root = insert(this.root, data); + } + + private Node insert(Node node, int item) { + if (node == null) { + return new Node(item); + } + if (node.data > item) { + node.left = insert(node.left, item); + } + if (node.data < item) { + node.right = insert(node.right, item); + } + node.height = Math.max(height(node.left), height(node.right)) + 1; + int bf = bf(node); + // LL case + if (bf > 1 && item < node.left.data) { + return rightRotate(node); + } + // RR case + if (bf < -1 && item > node.right.data) { + return leftRotate(node); + } + // RL case + if (bf < -1 && item < node.right.data) { + node.right = rightRotate(node.right); + return leftRotate(node); + } + // LR case + if (bf > 1 && item > node.left.data) { + node.left = leftRotate(node.left); + return rightRotate(node); + } + + return node; + } + + public void display() { + this.display(this.root); + System.out.println(this.root.height); + } + + private void display(Node node) { + String str = ""; + if (node.left != null) { + str += node.left.data + "=>"; + } else { + str += "END=>"; + } + str += node.data + ""; + if (node.right != null) { + str += "<=" + node.right.data; + } else { + str += "<=END"; + } + System.out.println(str); + if (node.left != null) { + display(node.left); + } + if (node.right != null) { + display(node.right); + } + } + + private int height(Node node) { + if (node == null) { + return 0; + } + return node.height; + } + + private int bf(Node node) { + if (node == null) { + return 0; + } + return height(node.left) - height(node.right); + } + + private Node rightRotate(Node c) { + Node b = c.left; + Node t3 = b.right; + + b.right = c; + c.left = t3; + c.height = Math.max(height(c.left), height(c.right)) + 1; + b.height = Math.max(height(b.left), height(b.right)) + 1; + return b; + } + + private Node leftRotate(Node c) { + Node b = c.right; + Node t3 = b.left; + + b.left = c; + c.right = t3; + c.height = Math.max(height(c.left), height(c.right)) + 1; + b.height = Math.max(height(b.left), height(b.right)) + 1; + return b; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/AVLTree.java b/src/main/java/com/thealgorithms/datastructures/trees/AVLTree.java new file mode 100644 index 000000000000..77ee5d5fa23e --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/AVLTree.java @@ -0,0 +1,269 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents an AVL Tree, a self-balancing binary search tree. + * In an AVL tree, the heights of the two child subtrees of any node + * differ by at most one. If they differ by more than one at any time, + * rebalancing is performed to restore this property. + */ +public class AVLTree { + + private Node root; + + private static class Node { + private int key; + private int balance; + private int height; + private Node left; + private Node right; + private Node parent; + + Node(int k, Node p) { + key = k; + parent = p; + } + + public Integer getBalance() { + return balance; + } + } + + /** + * Inserts a new key into the AVL tree. + * + * @param key the key to be inserted + * @return {@code true} if the key was inserted, {@code false} if the key already exists + */ + public boolean insert(int key) { + if (root == null) { + root = new Node(key, null); + } else { + Node n = root; + Node parent; + while (true) { + if (n.key == key) { + return false; + } + + parent = n; + boolean goLeft = n.key > key; + n = goLeft ? n.left : n.right; + + if (n == null) { + if (goLeft) { + parent.left = new Node(key, parent); + } else { + parent.right = new Node(key, parent); + } + rebalance(parent); + break; + } + } + } + return true; + } + + /** + * Deletes a key from the AVL tree. + * + * @param delKey the key to be deleted + */ + public void delete(int delKey) { + if (root == null) { + return; + } + + // Find the node to be deleted + Node node = root; + Node child = root; + while (child != null) { + node = child; + child = delKey >= node.key ? node.right : node.left; + if (delKey == node.key) { + delete(node); + return; + } + } + } + + private void delete(Node node) { + if (node.left == null && node.right == null) { + // Leaf node + if (node.parent == null) { + root = null; + } else { + Node parent = node.parent; + if (parent.left == node) { + parent.left = null; + } else { + parent.right = null; + } + rebalance(parent); + } + return; + } + + // Node has one or two children + Node child; + if (node.left != null) { + child = node.left; + while (child.right != null) { + child = child.right; + } + } else { + child = node.right; + while (child.left != null) { + child = child.left; + } + } + node.key = child.key; + delete(child); + } + + /** + * Returns a list of balance factors for each node in the tree. + * + * @return a list of integers representing the balance factors of the nodes + */ + public List<Integer> returnBalance() { + List<Integer> balances = new ArrayList<>(); + returnBalance(root, balances); + return balances; + } + + private void returnBalance(Node n, List<Integer> balances) { + if (n != null) { + returnBalance(n.left, balances); + balances.add(n.getBalance()); + returnBalance(n.right, balances); + } + } + + /** + * Searches for a key in the AVL tree. + * + * @param key the key to be searched + * @return true if the key is found, false otherwise + */ + public boolean search(int key) { + Node result = searchHelper(this.root, key); + return result != null; + } + + private Node searchHelper(Node root, int key) { + if (root == null || root.key == key) { + return root; + } + + if (root.key > key) { + return searchHelper(root.left, key); + } + return searchHelper(root.right, key); + } + + private void rebalance(Node n) { + setBalance(n); + if (n.balance == -2) { + if (height(n.left.left) >= height(n.left.right)) { + n = rotateRight(n); + } else { + n = rotateLeftThenRight(n); + } + } else if (n.balance == 2) { + if (height(n.right.right) >= height(n.right.left)) { + n = rotateLeft(n); + } else { + n = rotateRightThenLeft(n); + } + } + + if (n.parent != null) { + rebalance(n.parent); + } else { + root = n; + } + } + + private Node rotateLeft(Node a) { + Node b = a.right; + b.parent = a.parent; + + a.right = b.left; + + if (a.right != null) { + a.right.parent = a; + } + + b.left = a; + a.parent = b; + + if (b.parent != null) { + if (b.parent.right == a) { + b.parent.right = b; + } else { + b.parent.left = b; + } + } + + setBalance(a, b); + return b; + } + + private Node rotateRight(Node a) { + Node b = a.left; + b.parent = a.parent; + + a.left = b.right; + + if (a.left != null) { + a.left.parent = a; + } + + b.right = a; + a.parent = b; + + if (b.parent != null) { + if (b.parent.right == a) { + b.parent.right = b; + } else { + b.parent.left = b; + } + } + + setBalance(a, b); + return b; + } + + private Node rotateLeftThenRight(Node n) { + n.left = rotateLeft(n.left); + return rotateRight(n); + } + + private Node rotateRightThenLeft(Node n) { + n.right = rotateRight(n.right); + return rotateLeft(n); + } + + private int height(Node n) { + if (n == null) { + return -1; + } + return n.height; + } + + private void setBalance(Node... nodes) { + for (Node n : nodes) { + reheight(n); + n.balance = height(n.right) - height(n.left); + } + } + + private void reheight(Node node) { + if (node != null) { + node.height = 1 + Math.max(height(node.left), height(node.right)); + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/BSTFromSortedArray.java b/src/main/java/com/thealgorithms/datastructures/trees/BSTFromSortedArray.java new file mode 100644 index 000000000000..1962eaa0a106 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/BSTFromSortedArray.java @@ -0,0 +1,35 @@ +package com.thealgorithms.datastructures.trees; + +import com.thealgorithms.datastructures.trees.BinaryTree.Node; + +/** + * Given a sorted array. Create a balanced binary search tree from it. + * + * Steps: 1. Find the middle element of array. This will act as root 2. Use the + * left half recursively to create left subtree 3. Use the right half + * recursively to create right subtree + */ +public final class BSTFromSortedArray { + private BSTFromSortedArray() { + } + public static Node createBST(int[] array) { + if (array == null || array.length == 0) { + return null; + } + return createBST(array, 0, array.length - 1); + } + + private static Node createBST(int[] array, int startIdx, int endIdx) { + // No element left. + if (startIdx > endIdx) { + return null; + } + int mid = startIdx + (endIdx - startIdx) / 2; + + // middle element will be the root + Node root = new Node(array[mid]); + root.left = createBST(array, startIdx, mid - 1); + root.right = createBST(array, mid + 1, endIdx); + return root; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/BSTIterative.java b/src/main/java/com/thealgorithms/datastructures/trees/BSTIterative.java new file mode 100644 index 000000000000..97c2667002b6 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/BSTIterative.java @@ -0,0 +1,191 @@ +package com.thealgorithms.datastructures.trees; + +import com.thealgorithms.datastructures.trees.BinaryTree.Node; + +/** + * + * + * <h1>Binary Search Tree (Iterative)</h1> + * + * <p> + * An implementation of BST iteratively. Binary Search Tree is a binary tree + * which satisfies three properties: left child is less than root node, right + * child is grater than root node, both left and right child must themselves be + * a BST. + * + * @author [Lakhan Nad](<a href="https://github.com/Lakhan-Nad">git-Lakhan Nad</a>) + */ + +public class BSTIterative { + + /** + * Reference for the node of BST. + */ + private Node root; + + /** + * Default Constructor Initializes the root of BST with null. + */ + BSTIterative() { + root = null; + } + + public Node getRoot() { + return root; + } + + /** + * A method to insert a new value in BST. If the given value is already + * present in BST the insertion is ignored. + * + * @param data the value to be inserted + */ + public void add(int data) { + Node parent = null; + Node temp = this.root; + int rightOrLeft = -1; + /* Finds the proper place this node can + * be placed in according to rules of BST. + */ + while (temp != null) { + if (temp.data > data) { + parent = temp; + temp = parent.left; + rightOrLeft = 0; + } else if (temp.data < data) { + parent = temp; + temp = parent.right; + rightOrLeft = 1; + } else { + System.out.println(data + " is already present in BST."); + return; // if data already present we ignore insertion + } + } + /* Creates a newNode with the value passed + * Since this data doesn't already exists + */ + Node newNode = new Node(data); + /* If the parent node is null + * then the insertion is to be done in + * root itself. + */ + if (parent == null) { + this.root = newNode; + } else { + /* Check if insertion is to be made in + * left or right subtree. + */ + if (rightOrLeft == 0) { + parent.left = newNode; + } else { + parent.right = newNode; + } + } + } + + /** + * A method to delete the node in BST. If node is present it will be deleted + * + * @param data the value that needs to be deleted + */ + public void remove(int data) { + Node parent = null; + Node temp = this.root; + int rightOrLeft = -1; + /* Find the parent of the node and node itself + * That is to be deleted. + * parent variable store parent + * temp stores node itself. + * rightOrLeft use to keep track weather child + * is left or right subtree + */ + while (temp != null) { + if (temp.data == data) { + break; + } else if (temp.data > data) { + parent = temp; + temp = parent.left; + rightOrLeft = 0; + } else { + parent = temp; + temp = parent.right; + rightOrLeft = 1; + } + } + /* If temp is null than node with given value is not + * present in our tree. + */ + if (temp != null) { + Node replacement; // used to store the new values for replacing nodes + if (temp.right == null && temp.left == null) { // Leaf node Case + replacement = null; + } else if (temp.right == null) { // Node with only right child + replacement = temp.left; + temp.left = null; + } else if (temp.left == null) { // Node with only left child + replacement = temp.right; + temp.right = null; + } else { + /* If both left and right child are present + * we replace this nodes data with + * leftmost node's data in its right subtree + * to maintain the balance of BST. + * And then delete that node + */ + if (temp.right.left == null) { + temp.data = temp.right.data; + replacement = temp; + temp.right = temp.right.right; + } else { + Node parent2 = temp.right; + Node child = temp.right.left; + while (child.left != null) { + parent2 = child; + child = parent2.left; + } + temp.data = child.data; + parent2.left = child.right; + replacement = temp; + } + } + /* Change references of parent after + * deleting the child. + */ + if (parent == null) { + this.root = replacement; + } else { + if (rightOrLeft == 0) { + parent.left = replacement; + } else { + parent.right = replacement; + } + } + } + } + + /** + * A method to check if given data exists in out Binary Search Tree. + * + * @param data the value that needs to be searched for + * @return boolean representing if the value was find + */ + public boolean find(int data) { + Node temp = this.root; + /* Check if node exists + */ + while (temp != null) { + if (temp.data > data) { + temp = temp.left; + } else if (temp.data < data) { + temp = temp.right; + } else { + /* If found return true + */ + System.out.println(data + " is present in the BST."); + return true; + } + } + System.out.println(data + " not found."); + return false; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/BSTRecursive.java b/src/main/java/com/thealgorithms/datastructures/trees/BSTRecursive.java new file mode 100644 index 000000000000..6c1f4f672ff0 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/BSTRecursive.java @@ -0,0 +1,148 @@ +package com.thealgorithms.datastructures.trees; + +import com.thealgorithms.datastructures.trees.BinaryTree.Node; + +/** + * + * + * <h1>Binary Search Tree (Recursive)</h1> + * + * An implementation of BST recursively. In recursive implementation the checks + * are down the tree First root is checked if not found then its children are + * checked Binary Search Tree is a binary tree which satisfies three properties: + * left child is less than root node, right child is grater than root node, both + * left and right children must themselves be a BST. + * + * <p> + * I have made public functions as methods and to actually implement recursive + * approach I have used private methods + * + * @author [Lakhan Nad](<a href="https://github.com/Lakhan-Nad">git-Lakhan Nad</a>) + */ +public class BSTRecursive { + + /** + * only data member is root of BST + */ + private Node root; + + /** + * Constructor use to initialize node as null + */ + BSTRecursive() { + root = null; + } + + public Node getRoot() { + return root; + } + + /** + * Recursive method to delete a data if present in BST. + * + * @param node the current node to search for data + * @param data the value to be deleted + * @return Node the updated value of root parameter after delete operation + */ + private Node delete(Node node, int data) { + if (node == null) { + System.out.println("No such data present in BST."); + } else if (node.data > data) { + node.left = delete(node.left, data); + } else if (node.data < data) { + node.right = delete(node.right, data); + } else { + if (node.right == null && node.left == null) { // If it is leaf node + node = null; + } else if (node.left == null) { // If only right node is present + Node temp = node.right; + node.right = null; + node = temp; + } else if (node.right == null) { // Only left node is present + Node temp = node.left; + node.left = null; + node = temp; + } else { // both children are present + Node temp = node.right; + // Find leftmost child of right subtree + while (temp.left != null) { + temp = temp.left; + } + node.data = temp.data; + node.right = delete(node.right, temp.data); + } + } + return node; + } + + /** + * Recursive insertion of value in BST. + * + * @param node to check if the data can be inserted in current node or its + * subtree + * @param data the value to be inserted + * @return the modified value of the root parameter after insertion + */ + private Node insert(Node node, int data) { + if (node == null) { + node = new Node(data); + } else if (node.data > data) { + node.left = insert(node.left, data); + } else if (node.data < data) { + node.right = insert(node.right, data); + } + return node; + } + + /** + * Search recursively if the given value is present in BST or not. + * + * @param node the current node to check + * @param data the value to be checked + * @return boolean if data is present or not + */ + private boolean search(Node node, int data) { + if (node == null) { + return false; + } else if (node.data == data) { + return true; + } else if (node.data > data) { + return search(node.left, data); + } else { + return search(node.right, data); + } + } + + /** + * add in BST. if the value is not already present it is inserted or else no + * change takes place. + * + * @param data the value to be inserted + */ + public void add(int data) { + this.root = insert(this.root, data); + } + + /** + * If data is present in BST delete it else do nothing. + * + * @param data the value to be removed + */ + public void remove(int data) { + this.root = delete(this.root, data); + } + + /** + * To check if given value is present in tree or not. + * + * @param data the data to be found for + */ + public boolean find(int data) { + if (search(this.root, data)) { + System.out.println(data + " is present in given BST."); + return true; + } + System.out.println(data + " not found."); + return false; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/BSTRecursiveGeneric.java b/src/main/java/com/thealgorithms/datastructures/trees/BSTRecursiveGeneric.java new file mode 100644 index 000000000000..5c1334ffa0e8 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/BSTRecursiveGeneric.java @@ -0,0 +1,328 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.ArrayList; +import java.util.List; + +/** + * <h1>Binary Search Tree (Recursive) Generic Type Implementation</h1> + * + * <p> + * A recursive implementation of generic type BST. + * + * Reference: <a href="https://en.wikipedia.org/wiki/Binary_search_tree">Wiki links for BST</a> + * </p> + * + * @author [Madhur Panwar](<a href="https://github.com/mdrpanwar">git-Madhur Panwar</a>) + */ +public class BSTRecursiveGeneric<T extends Comparable<T>> { + + /** + * only data member is root of BST + */ + private Node<T> root; + + /** + * Constructor use to initialize node as null + */ + public BSTRecursiveGeneric() { + root = null; + } + + /** + * main function for testing + */ + public static void main(String[] args) { + System.out.println("Testing for integer data..."); + // Integer + BSTRecursiveGeneric<Integer> integerTree = new BSTRecursiveGeneric<Integer>(); + + integerTree.add(5); + integerTree.add(10); + integerTree.add(9); + assert !integerTree.find(4) + : "4 is not yet present in BST"; + assert integerTree.find(10) + : "10 should be present in BST"; + integerTree.remove(9); + assert !integerTree.find(9) + : "9 was just deleted from BST"; + integerTree.remove(1); + assert !integerTree.find(1) + : "Since 1 was not present so find deleting would do no change"; + integerTree.add(20); + integerTree.add(70); + assert integerTree.find(70) + : "70 was inserted but not found"; + /* + Will print in following order + 5 10 20 70 + */ + integerTree.inorder(); + System.out.println(); + System.out.println("Testing for string data..."); + // String + BSTRecursiveGeneric<String> stringTree = new BSTRecursiveGeneric<String>(); + + stringTree.add("banana"); + stringTree.add("pineapple"); + stringTree.add("date"); + assert !stringTree.find("girl") + : "girl is not yet present in BST"; + assert stringTree.find("pineapple") + : "10 should be present in BST"; + stringTree.remove("date"); + assert !stringTree.find("date") + : "date was just deleted from BST"; + stringTree.remove("boy"); + assert !stringTree.find("boy") + : "Since boy was not present so deleting would do no change"; + stringTree.add("india"); + stringTree.add("hills"); + assert stringTree.find("hills") + : "hills was inserted but not found"; + /* + Will print in following order + banana hills india pineapple + */ + stringTree.inorder(); + } + + /** + * Recursive method to delete a data if present in BST. + * + * @param node the node under which to (recursively) search for data + * @param data the value to be deleted + * @return Node the updated value of root parameter after delete operation + */ + private Node<T> delete(Node<T> node, T data) { + if (node == null) { + System.out.println("No such data present in BST."); + } else if (node.data.compareTo(data) > 0) { + node.left = delete(node.left, data); + } else if (node.data.compareTo(data) < 0) { + node.right = delete(node.right, data); + } else { + if (node.right == null && node.left == null) { // If it is leaf node + node = null; + } else if (node.left == null) { // If only right node is present + Node<T> temp = node.right; + node.right = null; + node = temp; + } else if (node.right == null) { // Only left node is present + Node<T> temp = node.left; + node.left = null; + node = temp; + } else { // both child are present + Node<T> temp = node.right; + // Find leftmost child of right subtree + while (temp.left != null) { + temp = temp.left; + } + node.data = temp.data; + node.right = delete(node.right, temp.data); + } + } + return node; + } + + /** + * Recursive insertion of value in BST. + * + * @param node to check if the data can be inserted in current node or its + * subtree + * @param data the value to be inserted + * @return the modified value of the root parameter after insertion + */ + private Node<T> insert(Node<T> node, T data) { + if (node == null) { + node = new Node<>(data); + } else if (node.data.compareTo(data) > 0) { + node.left = insert(node.left, data); + } else if (node.data.compareTo(data) < 0) { + node.right = insert(node.right, data); + } + return node; + } + + /** + * Recursively print Preorder traversal of the BST + * + * @param node the root node + */ + private void preOrder(Node<T> node) { + if (node == null) { + return; + } + System.out.print(node.data + " "); + if (node.left != null) { + preOrder(node.left); + } + if (node.right != null) { + preOrder(node.right); + } + } + + /** + * Recursively print Postorder traversal of BST. + * + * @param node the root node + */ + private void postOrder(Node<T> node) { + if (node == null) { + return; + } + if (node.left != null) { + postOrder(node.left); + } + if (node.right != null) { + postOrder(node.right); + } + System.out.print(node.data + " "); + } + + /** + * Recursively print Inorder traversal of BST. + * + * @param node the root node + */ + private void inOrder(Node<T> node) { + if (node == null) { + return; + } + if (node.left != null) { + inOrder(node.left); + } + System.out.print(node.data + " "); + if (node.right != null) { + inOrder(node.right); + } + } + + /** + * Recursively traverse the tree using inorder traversal and keep adding + * elements to argument list. + * + * @param node the root node + * @param sortedList the list to add the srted elements into + */ + private void inOrderSort(Node<T> node, List<T> sortedList) { + if (node == null) { + return; + } + if (node.left != null) { + inOrderSort(node.left, sortedList); + } + sortedList.add(node.data); + if (node.right != null) { + inOrderSort(node.right, sortedList); + } + } + + /** + * Search recursively if the given value is present in BST or not. + * + * @param node the node under which to check + * @param data the value to be checked + * @return boolean if data is present or not + */ + private boolean search(Node<T> node, T data) { + if (node == null) { + return false; + } else if (node.data.compareTo(data) == 0) { + return true; + } else if (node.data.compareTo(data) > 0) { + return search(node.left, data); + } else { + return search(node.right, data); + } + } + + /** + * add in BST. if the value is not already present it is inserted or else no + * change takes place. + * + * @param data the value to be inserted + */ + public void add(T data) { + this.root = insert(this.root, data); + } + + /** + * If data is present in BST delete it else do nothing. + * + * @param data the value to be removed + */ + public void remove(T data) { + this.root = delete(this.root, data); + } + + /** + * To call inorder traversal on tree + */ + public void inorder() { + System.out.println("Inorder traversal of this tree is:"); + inOrder(this.root); + System.out.println(); // for next line + } + + /** + * return a sorted list by traversing the tree elements using inorder + * traversal + */ + public List<T> inorderSort() { + List<T> sortedList = new ArrayList<>(); + inOrderSort(this.root, sortedList); + return sortedList; + } + + /** + * To call postorder traversal on tree + */ + public void postorder() { + System.out.println("Postorder traversal of this tree is:"); + postOrder(this.root); + System.out.println(); // for next line + } + + /** + * To call preorder traversal on tree. + */ + public void preorder() { + System.out.println("Preorder traversal of this tree is:"); + preOrder(this.root); + System.out.println(); // for next line + } + + /** + * To check if given value is present in tree or not. + * + * @param data the data to be found for + */ + public boolean find(T data) { + if (search(this.root, data)) { + System.out.println(data + " is present in given BST."); + return true; + } + System.out.println(data + " not found."); + return false; + } + + /** + * The generic Node class used for building binary search tree + */ + private static class Node<T> { + + T data; + Node<T> left; + Node<T> right; + + /** + * Constructor with data as parameter + */ + Node(T d) { + data = d; + left = null; + right = null; + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/BinaryTree.java b/src/main/java/com/thealgorithms/datastructures/trees/BinaryTree.java new file mode 100644 index 000000000000..ff02fe38970b --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/BinaryTree.java @@ -0,0 +1,330 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.LinkedList; +import java.util.Queue; + +/* + * This entire class is used to build a Binary Tree data structure. There is the + * Node Class and the Tree Class, both explained below. + */ +/** + * A binary tree is a data structure in which an element has two + * successors(children). The left child is usually smaller than the parent, and + * the right child is usually bigger. + * + * @author Unknown + */ +public class BinaryTree { + + /** + * This class implements the nodes that will go on the Binary Tree. They + * consist of the data in them, the node to the left, the node to the right, + * and the parent from which they came from. + * + * @author Unknown + */ + static class Node { + + /** + * Data for the node + */ + public int data; + /** + * The Node to the left of this one + */ + public Node left; + /** + * The Node to the right of this one + */ + public Node right; + /** + * The parent of this node + */ + public Node parent; + + /** + * Constructor of Node + * + * @param value Value to put in the node + */ + Node(int value) { + data = value; + left = null; + right = null; + parent = null; + } + } + + /** + * The root of the Binary Tree + */ + private Node root; + + /** + * Constructor + */ + public BinaryTree() { + root = null; + } + + /** + * Parameterized Constructor + */ + public BinaryTree(Node root) { + this.root = root; + } + + /** + * Method to find a Node with a certain value + * + * @param key Value being looked for + * @return The node if it finds it, otherwise returns the parent + */ + public Node find(int key) { + Node current = root; + while (current != null) { + if (key < current.data) { + if (current.left == null) { + return current; // The key isn't exist, returns the parent + } + current = current.left; + } else if (key > current.data) { + if (current.right == null) { + return current; + } + current = current.right; + } else { // If you find the value return it + return current; + } + } + return null; + } + + /** + * Inserts certain value into the Binary Tree + * + * @param value Value to be inserted + */ + public void put(int value) { + Node newNode = new Node(value); + if (root == null) { + root = newNode; + } else { + // This will return the soon to be parent of the value you're inserting + Node parent = find(value); + + // This if/else assigns the new node to be either the left or right child of the parent + if (value < parent.data) { + parent.left = newNode; + parent.left.parent = parent; + } else { + parent.right = newNode; + parent.right.parent = parent; + } + } + } + + /** + * Deletes a given value from the Binary Tree + * + * @param value Value to be deleted + * @return If the value was deleted + */ + public boolean remove(int value) { + // temp is the node to be deleted + Node temp = find(value); + + // If the value doesn't exist + if (temp.data != value) { + return false; + } + + // No children + if (temp.right == null && temp.left == null) { + if (temp == root) { + root = null; + } // This if/else assigns the new node to be either the left or right child of the + // parent + else if (temp.parent.data < temp.data) { + temp.parent.right = null; + } else { + temp.parent.left = null; + } + return true; + } // Two children + else if (temp.left != null && temp.right != null) { + Node successor = findSuccessor(temp); + + // The left tree of temp is made the left tree of the successor + successor.left = temp.left; + successor.left.parent = successor; + + // If the successor has a right child, the child's grandparent is it's new parent + if (successor.parent != temp) { + if (successor.right != null) { + successor.right.parent = successor.parent; + successor.parent.left = successor.right; + } else { + successor.parent.left = null; + } + successor.right = temp.right; + successor.right.parent = successor; + } + + if (temp == root) { + successor.parent = null; + root = successor; + } // If you're not deleting the root + else { + successor.parent = temp.parent; + + // This if/else assigns the new node to be either the left or right child of the + // parent + if (temp.parent.data < temp.data) { + temp.parent.right = successor; + } else { + temp.parent.left = successor; + } + } + return true; + } // One child + else { + // If it has a right child + if (temp.right != null) { + if (temp == root) { + root = temp.right; + return true; + } + + temp.right.parent = temp.parent; + + // Assigns temp to left or right child + if (temp.data < temp.parent.data) { + temp.parent.left = temp.right; + } else { + temp.parent.right = temp.right; + } + } // If it has a left child + else { + if (temp == root) { + root = temp.left; + return true; + } + + temp.left.parent = temp.parent; + + // Assigns temp to left or right side + if (temp.data < temp.parent.data) { + temp.parent.left = temp.left; + } else { + temp.parent.right = temp.left; + } + } + return true; + } + } + + /** + * This method finds the Successor to the Node given. Move right once and go + * left down the tree as far as you can + * + * @param n Node that you want to find the Successor of + * @return The Successor of the node + */ + public Node findSuccessor(Node n) { + if (n.right == null) { + return n; + } + Node current = n.right; + Node parent = n.right; + while (current != null) { + parent = current; + current = current.left; + } + return parent; + } + + /** + * Returns the root of the Binary Tree + * + * @return the root of the Binary Tree + */ + public Node getRoot() { + return root; + } + + /** + * Prints leftChild - root - rightChild This is the equivalent of a depth + * first search + * + * @param localRoot The local root of the binary tree + */ + public void inOrder(Node localRoot) { + if (localRoot != null) { + inOrder(localRoot.left); + System.out.print(localRoot.data + " "); + inOrder(localRoot.right); + } + } + + /** + * Prints root - leftChild - rightChild + * + * @param localRoot The local root of the binary tree + */ + public void preOrder(Node localRoot) { + if (localRoot != null) { + System.out.print(localRoot.data + " "); + preOrder(localRoot.left); + preOrder(localRoot.right); + } + } + + /** + * Prints leftChild - rightChild - root + * + * @param localRoot The local root of the binary tree + */ + public void postOrder(Node localRoot) { + if (localRoot != null) { + postOrder(localRoot.left); + postOrder(localRoot.right); + System.out.print(localRoot.data + " "); + } + } + + /** + * Prints the tree in a breadth first search order This is similar to + * pre-order traversal, but instead of being implemented with a stack (or + * recursion), it is implemented with a queue + * + * @param localRoot The local root of the binary tree + */ + public void bfs(Node localRoot) { + // Create a queue for the order of the nodes + Queue<Node> queue = new LinkedList<>(); + + // If the give root is null, then we don't add to the queue + // and won't do anything + if (localRoot != null) { + queue.add(localRoot); + } + + // Continue until the queue is empty + while (!queue.isEmpty()) { + // Get the next node on the queue to visit + localRoot = queue.remove(); + + // Print the data from the node we are visiting + System.out.print(localRoot.data + " "); + + // Add the children to the queue if not null + if (localRoot.right != null) { + queue.add(localRoot.right); + } + if (localRoot.left != null) { + queue.add(localRoot.left); + } + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/BoundaryTraversal.java b/src/main/java/com/thealgorithms/datastructures/trees/BoundaryTraversal.java new file mode 100644 index 000000000000..9a79902cc598 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/BoundaryTraversal.java @@ -0,0 +1,168 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.ArrayList; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; + +/** + * BoundaryTraversal + * <p> + * Start with the Root: + * Add the root node to the boundary list. + * Traverse the Left Boundary (Excluding Leaf Nodes): + * Move down the left side of the tree, adding each non-leaf node to the boundary list. + * If a node has a left child, go left; otherwise, go right. + * Visit All Leaf Nodes: + * Traverse the tree and add all leaf nodes to the boundary list, from left to right. + * Traverse the Right Boundary (Excluding Leaf Nodes) in Reverse Order: + * Move up the right side of the tree, adding each non-leaf node to a temporary list. + * If a node has a right child, go right; otherwise, go left. + * Reverse the temporary list and add it to the boundary list. + * Combine and Output: + * The final boundary list contains the root, left boundary, leaf nodes, and reversed right boundary in that order. + */ +public final class BoundaryTraversal { + private BoundaryTraversal() { + } + + // Main function for boundary traversal, returns a list of boundary nodes in order + public static List<Integer> boundaryTraversal(BinaryTree.Node root) { + List<Integer> result = new ArrayList<>(); + if (root == null) { + return result; + } + + // Add root node if it's not a leaf node + if (!isLeaf(root)) { + result.add(root.data); + } + + // Add left boundary + addLeftBoundary(root, result); + + // Add leaf nodes + addLeaves(root, result); + + // Add right boundary + addRightBoundary(root, result); + + return result; + } + + // Adds the left boundary, including nodes that have no left child but have a right child + private static void addLeftBoundary(BinaryTree.Node node, List<Integer> result) { + BinaryTree.Node cur = node.left; + + // If there is no left child but there is a right child, treat the right child as part of the left boundary + if (cur == null && node.right != null) { + cur = node.right; + } + + while (cur != null) { + if (!isLeaf(cur)) { + result.add(cur.data); // Add non-leaf nodes to result + } + if (cur.left != null) { + cur = cur.left; // Move to the left child + } else if (cur.right != null) { + cur = cur.right; // If left child is null, move to the right child + } else { + break; // Stop if there are no children + } + } + } + + // Adds leaf nodes (nodes without children) + private static void addLeaves(BinaryTree.Node node, List<Integer> result) { + if (node == null) { + return; + } + if (isLeaf(node)) { + result.add(node.data); // Add leaf node + } else { + addLeaves(node.left, result); // Recur for left subtree + addLeaves(node.right, result); // Recur for right subtree + } + } + + // Adds the right boundary, excluding leaf nodes + private static void addRightBoundary(BinaryTree.Node node, List<Integer> result) { + BinaryTree.Node cur = node.right; + List<Integer> temp = new ArrayList<>(); + + // If no right boundary is present and there is no left subtree, skip + if (cur != null && node.left == null) { + return; + } + while (cur != null) { + if (!isLeaf(cur)) { + temp.add(cur.data); // Store non-leaf nodes temporarily + } + if (cur.right != null) { + cur = cur.right; // Move to the right child + } else if (cur.left != null) { + cur = cur.left; // If right child is null, move to the left child + } else { + break; // Stop if there are no children + } + } + + // Add the right boundary nodes in reverse order + for (int i = temp.size() - 1; i >= 0; i--) { + result.add(temp.get(i)); + } + } + + // Checks if a node is a leaf node + private static boolean isLeaf(BinaryTree.Node node) { + return node.left == null && node.right == null; + } + + // Iterative boundary traversal + public static List<Integer> iterativeBoundaryTraversal(BinaryTree.Node root) { + List<Integer> result = new ArrayList<>(); + if (root == null) { + return result; + } + + // Add root node if it's not a leaf node + if (!isLeaf(root)) { + result.add(root.data); + } + + // Handle the left boundary + BinaryTree.Node cur = root.left; + if (cur == null && root.right != null) { + cur = root.right; + } + while (cur != null) { + if (!isLeaf(cur)) { + result.add(cur.data); // Add non-leaf nodes to result + } + cur = (cur.left != null) ? cur.left : cur.right; // Prioritize left child, move to right if left is null + } + + // Add leaf nodes + addLeaves(root, result); + + // Handle the right boundary using a stack (reverse order) + cur = root.right; + Deque<Integer> stack = new LinkedList<>(); + if (cur != null && root.left == null) { + return result; + } + while (cur != null) { + if (!isLeaf(cur)) { + stack.push(cur.data); // Temporarily store right boundary nodes in a stack + } + cur = (cur.right != null) ? cur.right : cur.left; // Prioritize right child, move to left if right is null + } + + // Add the right boundary nodes from the stack to maintain the correct order + while (!stack.isEmpty()) { + result.add(stack.pop()); + } + return result; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/CeilInBinarySearchTree.java b/src/main/java/com/thealgorithms/datastructures/trees/CeilInBinarySearchTree.java new file mode 100644 index 000000000000..214e111b9f1a --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/CeilInBinarySearchTree.java @@ -0,0 +1,70 @@ +package com.thealgorithms.datastructures.trees; + +import com.thealgorithms.datastructures.trees.BinaryTree.Node; + +/** + * Problem Statement Ceil value for any number x in a collection is a number y + * which is either equal to x or the least greater number than x. + * + * Problem: Given a binary search tree containing positive integer values. Find + * ceil value for a given key in O(lg(n)) time. In case if it is not present + * return -1. + * + * Ex.1. [30,20,40,10,25,35,50] represents level order traversal of a binary + * search tree. Find ceil for 10. Answer: 20 + * + * Ex.2. [30,20,40,10,25,35,50] represents level order traversal of a binary + * search tree. Find ceil for 22 Answer: 25 + * + * Ex.2. [30,20,40,10,25,35,50] represents level order traversal of a binary + * search tree. Find ceil for 52 Answer: -1 + * + * Solution 1: Brute Force Solution: Do an inorder traversal and save result + * into an array. Iterate over the array to get an element equal to or greater + * than current key. Time Complexity: O(n) Space Complexity: O(n) for auxillary + * array to save inorder representation of tree. + * <p> + * <p> + * Solution 2: Brute Force Solution: Do an inorder traversal and save result + * into an array.Since array is sorted do a binary search over the array to get + * an element equal to or greater than current key. Time Complexity: O(n) for + * traversal of tree and O(lg(n)) for binary search in array. Total = O(n) Space + * Complexity: O(n) for auxillary array to save inorder representation of tree. + * <p> + * <p> + * Solution 3: Optimal We can do a DFS search on given tree in following + * fashion. i) if root is null then return null because then ceil doesn't exist + * ii) If key is lesser than root value than ceil will be in right subtree so + * call recursively on right subtree iii) if key is greater than current root, + * then either a) the root is ceil b) ceil is in left subtree: call for left + * subtree. If left subtree returns a non-null value then that will be ceil + * otherwise the root is ceil + */ +public final class CeilInBinarySearchTree { + private CeilInBinarySearchTree() { + } + + public static Node getCeil(Node root, int key) { + if (root == null) { + return null; + } + + // if root value is same as key than root is the ceiling + if (root.data == key) { + return root; + } + + // if root value is lesser than key then ceil must be in right subtree + if (root.data < key) { + return getCeil(root.right, key); + } + + // if root value is greater than key then ceil can be in left subtree or if + // it is not in left subtree then current node will be ceil + Node result = getCeil(root.left, key); + + // if result is null it means that there is no ceil in children subtrees + // and the root is the ceil otherwise the returned node is the ceil. + return result == null ? root : result; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/CheckBinaryTreeIsValidBST.java b/src/main/java/com/thealgorithms/datastructures/trees/CheckBinaryTreeIsValidBST.java new file mode 100644 index 000000000000..0c2b44d78d74 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/CheckBinaryTreeIsValidBST.java @@ -0,0 +1,30 @@ +package com.thealgorithms.datastructures.trees; + +/** + * This code recursively validates whether given Binary Search Tree (BST) is balanced or not. + * Trees with only distinct values are supported. + * Key points: + * 1. According to the definition of a BST, each node in a tree must be in range [min, max], + * where 'min' and 'max' values represent the child nodes (left, right). + * 2. The smallest possible node value is Integer.MIN_VALUE, the biggest - Integer.MAX_VALUE. + */ +public final class CheckBinaryTreeIsValidBST { + private CheckBinaryTreeIsValidBST() { + } + public static boolean isBST(BinaryTree.Node root) { + return isBSTUtil(root, Integer.MIN_VALUE, Integer.MAX_VALUE); + } + + private static boolean isBSTUtil(BinaryTree.Node node, int min, int max) { + // empty tree is a BST + if (node == null) { + return true; + } + + if (node.data < min || node.data > max) { + return false; + } + + return (isBSTUtil(node.left, min, node.data - 1) && isBSTUtil(node.right, node.data + 1, max)); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/CheckIfBinaryTreeBalanced.java b/src/main/java/com/thealgorithms/datastructures/trees/CheckIfBinaryTreeBalanced.java new file mode 100644 index 000000000000..9aa4d5a1fef2 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/CheckIfBinaryTreeBalanced.java @@ -0,0 +1,157 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.HashMap; +import java.util.Stack; + +/** + * This class will check if a BinaryTree is balanced. A balanced binary tree is + * defined as a binary tree where the difference in height between the left and + * right subtree of each node differs by at most one. + * <p> + * This can be done in both an iterative and recursive fashion. Below, + * `isBalancedRecursive()` is implemented in a recursive fashion, and + * `isBalancedIterative()` is implemented in an iterative fashion. + * + * @author [Ian Cowan](<a href="https://github.com/iccowan">Git-Ian Cowan</a>) + */ +public final class CheckIfBinaryTreeBalanced { + private CheckIfBinaryTreeBalanced() { + } + /** + * Recursive is BT balanced implementation + * + * @param root The binary tree to check if balanced + */ + public static boolean isBalancedRecursive(BinaryTree.Node root) { + if (root == null) { + return true; + } + // Create an array of length 1 to keep track of our balance + // Default to true. We use an array, so we have an efficient mutable object + boolean[] isBalanced = new boolean[1]; + isBalanced[0] = true; + + // Check for balance and return whether we are balanced + isBalancedRecursive(root, 0, isBalanced); + return isBalanced[0]; + } + + /** + * Private helper method to keep track of the depth and balance during + * recursion. We effectively perform a modified post-order traversal where + * we are looking at the heights of both children of each node in the tree + * + * @param node The current node to explore + * @param depth The current depth of the node + * @param isBalanced The array of length 1 keeping track of our balance + */ + private static int isBalancedRecursive(BinaryTree.Node node, int depth, boolean[] isBalanced) { + // If the node is null, we should not explore it and the height is 0 + // If the tree is already not balanced, might as well stop because we + // can't make it balanced now! + if (node == null || !isBalanced[0]) { + return 0; + } + + // Visit the left and right children, incrementing their depths by 1 + int leftHeight = isBalancedRecursive(node.left, depth + 1, isBalanced); + int rightHeight = isBalancedRecursive(node.right, depth + 1, isBalanced); + + // If the height of either of the left or right subtrees differ by more + // than 1, we cannot be balanced + if (Math.abs(leftHeight - rightHeight) > 1) { + isBalanced[0] = false; + } + + // The height of our tree is the maximum of the heights of the left + // and right subtrees plus one + return Math.max(leftHeight, rightHeight) + 1; + } + + /** + * Iterative is BT balanced implementation + */ + public static boolean isBalancedIterative(BinaryTree.Node root) { + if (root == null) { + return true; + } + + // Default that we are balanced and our algo will prove it wrong + boolean isBalanced = true; + + // Create a stack for our post order traversal + Stack<BinaryTree.Node> nodeStack = new Stack<>(); + + // For post order traversal, we'll have to keep track of where we + // visited last + BinaryTree.Node lastVisited = null; + + // Create a HashMap to keep track of the subtree heights for each node + HashMap<BinaryTree.Node, Integer> subtreeHeights = new HashMap<>(); + + // We begin at the root of the tree + BinaryTree.Node node = root; + + // We loop while: + // - the node stack is empty and the node we explore is null + // AND + // - the tree is still balanced + while (!(nodeStack.isEmpty() && node == null) && isBalanced) { + // If the node is not null, we push it to the stack and continue + // to the left + if (node != null) { + nodeStack.push(node); + node = node.left; + // Once we hit a node that is null, we are as deep as we can go + // to the left + } else { + // Find the last node we put on the stack + node = nodeStack.peek(); + + // If the right child of the node has either been visited or + // is null, we visit this node + if (node.right == null || node.right == lastVisited) { + // We assume the left and right heights are 0 + int leftHeight = 0; + int rightHeight = 0; + + // If the right and left children are not null, we must + // have already explored them and have a height + // for them so let's get that + if (node.left != null) { + leftHeight = subtreeHeights.get(node.left); + } + + if (node.right != null) { + rightHeight = subtreeHeights.get(node.right); + } + + // If the difference in the height of the right subtree + // and left subtree differs by more than 1, we cannot be + // balanced + if (Math.abs(rightHeight - leftHeight) > 1) { + isBalanced = false; + } + + // The height of the subtree containing this node is the + // max of the left and right subtree heights plus 1 + subtreeHeights.put(node, Math.max(rightHeight, leftHeight) + 1); + + // We've now visited this node, so we pop it from the stack + nodeStack.pop(); + lastVisited = node; + + // Current visiting node is now null + node = null; + // If the right child node of this node has not been visited + // and is not null, we need to get that child node on the stack + } else { + node = node.right; + } + } + } + + // Return whether the tree is balanced + return isBalanced; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/CheckTreeIsSymmetric.java b/src/main/java/com/thealgorithms/datastructures/trees/CheckTreeIsSymmetric.java new file mode 100644 index 000000000000..17d84bf11d54 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/CheckTreeIsSymmetric.java @@ -0,0 +1,59 @@ +package com.thealgorithms.datastructures.trees; + +import com.thealgorithms.datastructures.trees.BinaryTree.Node; + +/** + * Check if a binary tree is symmetric or not. + * A binary tree is a symmetric tree if the left and right subtree of root are mirror image. + * Below is a symmetric tree + * 1 + * / \ + * 2 2 + * / \ / \ + * 3 4 4 3 + * + * Below is not symmetric because values is different in last level + * 1 + * / \ + * 2 2 + * / \ / \ + * 3 5 4 3 + * <p> + * Approach: + * Recursively check for left and right subtree of root + * 1. left subtrees root's values should be equal right subtree's root value + * 2. recursively check with left subtrees' left child VS right subtree's right child AND + * left subtree's right child VS right subtree left child + * Complexity + * 1. Time: O(n) + * 2. Space: O(lg(n)) for height of tree + * + * @author kumanoit on 10/10/22 IST 12:52 AM + */ +public final class CheckTreeIsSymmetric { + private CheckTreeIsSymmetric() { + } + + public static boolean isSymmetric(Node root) { + if (root == null) { + return true; + } + return isSymmetric(root.left, root.right); + } + + private static boolean isSymmetric(Node leftSubtreeRoot, Node rightSubtreeRoot) { + if (leftSubtreeRoot == null && rightSubtreeRoot == null) { + return true; + } + + if (isInvalidSubtree(leftSubtreeRoot, rightSubtreeRoot)) { + return false; + } + + return isSymmetric(leftSubtreeRoot.right, rightSubtreeRoot.left) && isSymmetric(leftSubtreeRoot.left, rightSubtreeRoot.right); + } + + private static boolean isInvalidSubtree(Node leftSubtreeRoot, Node rightSubtreeRoot) { + return leftSubtreeRoot == null || rightSubtreeRoot == null || leftSubtreeRoot.data != rightSubtreeRoot.data; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/CreateBinaryTreeFromInorderPreorder.java b/src/main/java/com/thealgorithms/datastructures/trees/CreateBinaryTreeFromInorderPreorder.java new file mode 100644 index 000000000000..04caa0b9e9d2 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/CreateBinaryTreeFromInorderPreorder.java @@ -0,0 +1,71 @@ +package com.thealgorithms.datastructures.trees; + +import com.thealgorithms.datastructures.trees.BinaryTree.Node; +import java.util.HashMap; +import java.util.Map; + +/** + * Approach: Naive Solution: Create root node from first value present in + * preorder traversal. Look for the index of root node's value in inorder + * traversal. That will tell total nodes present in left subtree and right + * subtree. Based on that index create left and right subtree. Complexity: Time: + * O(n^2) for each node there is iteration to find index in inorder array Space: + * Stack size = O(height) = O(lg(n)) + * <p> + * Optimized Solution: Instead of iterating over inorder array to find index of + * root value, create a hashmap and find out the index of root value. + * Complexity: Time: O(n) hashmap reduced iteration to find index in inorder + * array Space: O(n) space taken by hashmap + */ +public final class CreateBinaryTreeFromInorderPreorder { + private CreateBinaryTreeFromInorderPreorder() { + } + public static Node createTree(final Integer[] preorder, final Integer[] inorder) { + if (preorder == null || inorder == null) { + return null; + } + return createTree(preorder, inorder, 0, 0, inorder.length); + } + + public static Node createTreeOptimized(final Integer[] preorder, final Integer[] inorder) { + if (preorder == null || inorder == null) { + return null; + } + Map<Integer, Integer> inorderMap = new HashMap<>(); + for (int i = 0; i < inorder.length; i++) { + inorderMap.put(inorder[i], i); + } + return createTreeOptimized(preorder, inorderMap, 0, 0, inorder.length); + } + + private static Node createTree(final Integer[] preorder, final Integer[] inorder, final int preStart, final int inStart, final int size) { + if (size == 0) { + return null; + } + + Node root = new Node(preorder[preStart]); + int i = inStart; + while (!preorder[preStart].equals(inorder[i])) { + i++; + } + int leftNodesCount = i - inStart; + int rightNodesCount = size - leftNodesCount - 1; + root.left = createTree(preorder, inorder, preStart + 1, inStart, leftNodesCount); + root.right = createTree(preorder, inorder, preStart + leftNodesCount + 1, i + 1, rightNodesCount); + return root; + } + + private static Node createTreeOptimized(final Integer[] preorder, final Map<Integer, Integer> inorderMap, final int preStart, final int inStart, final int size) { + if (size == 0) { + return null; + } + + Node root = new Node(preorder[preStart]); + int i = inorderMap.get(preorder[preStart]); + int leftNodesCount = i - inStart; + int rightNodesCount = size - leftNodesCount - 1; + root.left = createTreeOptimized(preorder, inorderMap, preStart + 1, inStart, leftNodesCount); + root.right = createTreeOptimized(preorder, inorderMap, preStart + leftNodesCount + 1, i + 1, rightNodesCount); + return root; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/FenwickTree.java b/src/main/java/com/thealgorithms/datastructures/trees/FenwickTree.java new file mode 100644 index 000000000000..5378a01f6642 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/FenwickTree.java @@ -0,0 +1,35 @@ +package com.thealgorithms.datastructures.trees; + +public class FenwickTree { + + private int n; + private int[] fenTree; + + /* Constructor which takes the size of the array as a parameter */ + public FenwickTree(int n) { + this.n = n; + this.fenTree = new int[n + 1]; + } + + /* A function which will add the element val at index i*/ + public void update(int i, int val) { + // As index starts from 0, increment the index by 1 + i += 1; + while (i <= n) { + fenTree[i] += val; + i += i & (-i); + } + } + + /* A function which will return the cumulative sum from index 1 to index i*/ + public int query(int i) { + // As index starts from 0, increment the index by 1 + i += 1; + int cumSum = 0; + while (i > 0) { + cumSum += fenTree[i]; + i -= i & (-i); + } + return cumSum; + } +} diff --git a/DataStructures/Trees/GenericTree.java b/src/main/java/com/thealgorithms/datastructures/trees/GenericTree.java similarity index 76% rename from DataStructures/Trees/GenericTree.java rename to src/main/java/com/thealgorithms/datastructures/trees/GenericTree.java index 107a072cf965..7d969a59def0 100644 --- a/DataStructures/Trees/GenericTree.java +++ b/src/main/java/com/thealgorithms/datastructures/trees/GenericTree.java @@ -1,48 +1,48 @@ -package DataStructures.Trees; +package com.thealgorithms.datastructures.trees; import java.util.ArrayList; import java.util.LinkedList; import java.util.Scanner; /** - * A generic tree is a tree which can have as many children as it can be - * It might be possible that every node present is directly connected to - * root node. + * A generic tree is a tree which can have as many children as it can be It + * might be possible that every node present is directly connected to root node. + * * <p> - * In this code - * Every function has two copies: one function is helper function which can be called from - * main and from that function a private function is called which will do the actual work. - * I have done this, while calling from main one have to give minimum parameters. + * In this code Every function has two copies: one function is helper function + * which can be called from main and from that function a private function is + * called which will do the actual work. I have done this, while calling from + * main one have to give minimum parameters. */ public class GenericTree { - private class Node { + + private static final class Node { + int data; ArrayList<Node> child = new ArrayList<>(); } - private Node root; - private int size; + private final Node root; - public GenericTree() { //Constructor + public GenericTree() { // Constructor Scanner scn = new Scanner(System.in); - root = create_treeG(null, 0, scn); + root = createTreeG(null, 0, scn); } - private Node create_treeG(Node node, int childindx, Scanner scn) { + private Node createTreeG(Node node, int childIndex, Scanner scanner) { // display if (node == null) { System.out.println("Enter root's data"); } else { - System.out.println("Enter data of parent of index " + node.data + " " + childindx); + System.out.println("Enter data of parent of index " + node.data + " " + childIndex); } // input node = new Node(); - node.data = scn.nextInt(); + node.data = scanner.nextInt(); System.out.println("number of children"); - int number = scn.nextInt(); + int number = scanner.nextInt(); for (int i = 0; i < number; i++) { - Node child = create_treeG(node, i, scn); - size++; + Node child = createTreeG(node, i, scanner); node.child.add(child); } return node; @@ -51,24 +51,24 @@ private Node create_treeG(Node node, int childindx, Scanner scn) { /** * Function to display the generic tree */ - public void display() { //Helper function - display_1(root); + public void display() { // Helper function + display1(root); } - private void display_1(Node parent) { + private void display1(Node parent) { System.out.print(parent.data + "=>"); for (int i = 0; i < parent.child.size(); i++) { System.out.print(parent.child.get(i).data + " "); } System.out.println("."); for (int i = 0; i < parent.child.size(); i++) { - display_1(parent.child.get(i)); + display1(parent.child.get(i)); } } /** - * One call store the size directly but if you are asked compute size this function to calculate - * size goes as follows + * One call store the size directly but if you are asked compute size this + * function to calculate size goes as follows * * @return size */ @@ -95,8 +95,9 @@ public int maxcall() { } private int max(Node roott, int maxi) { - if (maxi < roott.data) + if (maxi < roott.data) { maxi = roott.data; + } for (int i = 0; i < roott.child.size(); i++) { maxi = max(roott.child.get(i), maxi); } @@ -117,8 +118,9 @@ private int height(Node node) { int h = 0; for (int i = 0; i < node.child.size(); i++) { int k = height(node.child.get(i)); - if (k > h) + if (k > h) { h = k; + } } return h + 1; } @@ -134,16 +136,17 @@ public boolean findcall(int info) { } private boolean find(Node node, int info) { - if (node.data == info) + if (node.data == info) { return true; + } for (int i = 0; i < node.child.size(); i++) { - if (find(node.child.get(i), info)) + if (find(node.child.get(i), info)) { return true; + } } return false; } - /** * Function to calculate depth of generic tree * @@ -158,9 +161,9 @@ public void depth(Node node, int dep) { System.out.println(node.data); return; } - for (int i = 0; i < node.child.size(); i++) + for (int i = 0; i < node.child.size(); i++) { depth(node.child.get(i), dep - 1); - return; + } } /** @@ -173,8 +176,9 @@ public void preordercall() { private void preorder(Node node) { System.out.print(node.data + " "); - for (int i = 0; i < node.child.size(); i++) + for (int i = 0; i < node.child.size(); i++) { preorder(node.child.get(i)); + } } /** @@ -186,8 +190,9 @@ public void postordercall() { } private void postorder(Node node) { - for (int i = 0; i < node.child.size(); i++) + for (int i = 0; i < node.child.size(); i++) { postorder(node.child.get(i)); + } System.out.print(node.data + " "); } @@ -221,14 +226,12 @@ private void removeleaves(Node node) { for (int i = 0; i < node.child.size(); i++) { if (node.child.get(i).child.size() == 0) { arr.add(i); - // node.child.remove(i); - // i--; - } else + } else { removeleaves(node.child.get(i)); + } } for (int i = arr.size() - 1; i >= 0; i--) { node.child.remove(arr.get(i) + 0); } } - } diff --git a/src/main/java/com/thealgorithms/datastructures/trees/InorderTraversal.java b/src/main/java/com/thealgorithms/datastructures/trees/InorderTraversal.java new file mode 100644 index 000000000000..5a001ff9ab9f --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/InorderTraversal.java @@ -0,0 +1,64 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; + +/** + * Given tree is traversed in an 'inorder' way: LEFT -> ROOT -> RIGHT. + * Below are given the recursive and iterative implementations. + * + * Complexities: + * Recursive: O(n) - time, O(n) - space, where 'n' is the number of nodes in a tree. + * + * Iterative: O(n) - time, O(h) - space, where 'n' is the number of nodes in a tree + * and 'h' is the height of a binary tree. + * In the worst case 'h' can be O(n) if tree is completely unbalanced, for instance: + * 5 + * \ + * 6 + * \ + * 7 + * \ + * 8 + * + * @author Albina Gimaletdinova on 21/02/2023 + */ +public final class InorderTraversal { + private InorderTraversal() { + } + public static List<Integer> recursiveInorder(BinaryTree.Node root) { + List<Integer> result = new ArrayList<>(); + recursiveInorder(root, result); + return result; + } + + public static List<Integer> iterativeInorder(BinaryTree.Node root) { + List<Integer> result = new ArrayList<>(); + if (root == null) { + return result; + } + + Deque<BinaryTree.Node> stack = new ArrayDeque<>(); + while (!stack.isEmpty() || root != null) { + while (root != null) { + stack.push(root); + root = root.left; + } + root = stack.pop(); + result.add(root.data); + root = root.right; + } + return result; + } + + private static void recursiveInorder(BinaryTree.Node root, List<Integer> result) { + if (root == null) { + return; + } + recursiveInorder(root.left, result); + result.add(root.data); + recursiveInorder(root.right, result); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/KDTree.java b/src/main/java/com/thealgorithms/datastructures/trees/KDTree.java new file mode 100644 index 000000000000..5190e82f74ef --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/KDTree.java @@ -0,0 +1,456 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.Objects; +import java.util.Optional; + +/* + * K-D Tree Implementation + * Wikipedia: https://en.wikipedia.org/wiki/K-d_tree + * + * Author: Amir Hosseini (https://github.com/itsamirhn) + * + * */ + +public class KDTree { + + private Node root; + + private final int k; // Dimensions of the points + + /** + * Constructor for empty KDTree + * + * @param k Number of dimensions + */ + KDTree(int k) { + this.k = k; + } + + /** + * Builds the KDTree from the specified points + * + * @param points Array of initial points + */ + KDTree(Point[] points) { + if (points.length == 0) { + throw new IllegalArgumentException("Points array cannot be empty"); + } + this.k = points[0].getDimension(); + for (Point point : points) { + if (point.getDimension() != k) { + throw new IllegalArgumentException("Points must have the same dimension"); + } + } + this.root = build(points, 0); + } + + /** + * Builds the KDTree from the specified coordinates of the points + * + * @param pointsCoordinates Array of initial points coordinates + * + */ + KDTree(int[][] pointsCoordinates) { + if (pointsCoordinates.length == 0) { + throw new IllegalArgumentException("Points array cannot be empty"); + } + this.k = pointsCoordinates[0].length; + Point[] points = Arrays.stream(pointsCoordinates).map(Point::new).toArray(Point[] ::new); + for (Point point : points) { + if (point.getDimension() != k) { + throw new IllegalArgumentException("Points must have the same dimension"); + } + } + this.root = build(points, 0); + } + + static class Point { + + int[] coordinates; + + public int getCoordinate(int i) { + return coordinates[i]; + } + + public int getDimension() { + return coordinates.length; + } + + Point(int[] coordinates) { + this.coordinates = coordinates; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Point other) { + return Arrays.equals(other.coordinates, this.coordinates); + } + return false; + } + + @Override + public int hashCode() { + return Arrays.hashCode(coordinates); + } + + @Override + public String toString() { + return Arrays.toString(coordinates); + } + + /** + * Find the comparable distance between two points (without SQRT) + * + * @param p1 First point + * @param p2 Second point + * + * @return The comparable distance between the two points + */ + public static int comparableDistance(Point p1, Point p2) { + int distance = 0; + for (int i = 0; i < p1.getDimension(); i++) { + int t = p1.getCoordinate(i) - p2.getCoordinate(i); + distance += t * t; + } + return distance; + } + + /** + * Find the comparable distance between two points with ignoring specified axis + * + * @param p1 First point + * @param p2 Second point + * @param axis The axis to ignore + * + * @return The distance between the two points + */ + public static int comparableDistanceExceptAxis(Point p1, Point p2, int axis) { + int distance = 0; + for (int i = 0; i < p1.getDimension(); i++) { + if (i == axis) { + continue; + } + int t = p1.getCoordinate(i) - p2.getCoordinate(i); + distance += t * t; + } + return distance; + } + } + + static class Node { + + private Point point; + private int axis; // 0 for x, 1 for y, 2 for z, etc. + + private Node left = null; // Left child + private Node right = null; // Right child + + Node(Point point, int axis) { + this.point = point; + this.axis = axis; + } + + public Point getPoint() { + return point; + } + + public Node getLeft() { + return left; + } + + public Node getRight() { + return right; + } + + public int getAxis() { + return axis; + } + + /** + * Get the nearest child according to the specified point + * + * @param point The point to find the nearest child to + * + * @return The nearest child Node + */ + public Node getNearChild(Point point) { + if (point.getCoordinate(axis) < this.point.getCoordinate(axis)) { + return left; + } else { + return right; + } + } + + /** + * Get the farthest child according to the specified point + * + * @param point The point to find the farthest child to + * + * @return The farthest child Node + */ + public Node getFarChild(Point point) { + if (point.getCoordinate(axis) < this.point.getCoordinate(axis)) { + return right; + } else { + return left; + } + } + + /** + * Get the node axis coordinate of point + * + * @return The axis coordinate of the point + */ + public int getAxisCoordinate() { + return point.getCoordinate(axis); + } + } + + public Node getRoot() { + return root; + } + + /** + * Builds the KDTree from the specified points + * + * @param points Array of initial points + * @param depth The current depth of the tree + * + * @return The root of the KDTree + */ + private Node build(Point[] points, int depth) { + if (points.length == 0) { + return null; + } + int axis = depth % k; + if (points.length == 1) { + return new Node(points[0], axis); + } + Arrays.sort(points, Comparator.comparingInt(o -> o.getCoordinate(axis))); + int median = points.length >> 1; + Node node = new Node(points[median], axis); + node.left = build(Arrays.copyOfRange(points, 0, median), depth + 1); + node.right = build(Arrays.copyOfRange(points, median + 1, points.length), depth + 1); + return node; + } + + /** + * Insert a point into the KDTree + * + * @param point The point to insert + * + */ + public void insert(Point point) { + if (point.getDimension() != k) { + throw new IllegalArgumentException("Point has wrong dimension"); + } + root = insert(root, point, 0); + } + + /** + * Insert a point into a subtree + * + * @param root The root of the subtree + * @param point The point to insert + * @param depth The current depth of the tree + * + * @return The root of the KDTree + */ + private Node insert(Node root, Point point, int depth) { + int axis = depth % k; + if (root == null) { + return new Node(point, axis); + } + if (point.getCoordinate(axis) < root.getAxisCoordinate()) { + root.left = insert(root.left, point, depth + 1); + } else { + root.right = insert(root.right, point, depth + 1); + } + + return root; + } + + /** + * Search for Node corresponding to the specified point in the KDTree + * + * @param point The point to search for + * + * @return The Node corresponding to the specified point + */ + public Optional<Node> search(Point point) { + if (point.getDimension() != k) { + throw new IllegalArgumentException("Point has wrong dimension"); + } + return search(root, point); + } + + /** + * Search for Node corresponding to the specified point in a subtree + * + * @param root The root of the subtree to search in + * @param point The point to search for + * + * @return The Node corresponding to the specified point + */ + public Optional<Node> search(Node root, Point point) { + if (root == null) { + return Optional.empty(); + } + if (root.point.equals(point)) { + return Optional.of(root); + } + return search(root.getNearChild(point), point); + } + + /** + * Find a point with minimum value in specified axis in the KDTree + * + * @param axis The axis to find the minimum value in + * + * @return The point with minimum value in the specified axis + */ + public Point findMin(int axis) { + return findMin(root, axis).point; + } + + /** + * Find a point with minimum value in specified axis in a subtree + * + * @param root The root of the subtree to search in + * @param axis The axis to find the minimum value in + * + * @return The Node with minimum value in the specified axis of the point + */ + public Node findMin(Node root, int axis) { + if (root == null) { + return null; + } + if (root.getAxis() == axis) { + if (root.left == null) { + return root; + } + return findMin(root.left, axis); + } else { + Node left = findMin(root.left, axis); + Node right = findMin(root.right, axis); + Node[] candidates = {left, root, right}; + return Arrays.stream(candidates).filter(Objects::nonNull).min(Comparator.comparingInt(a -> a.point.getCoordinate(axis))).orElse(null); + } + } + + /** + * Find a point with maximum value in specified axis in the KDTree + * + * @param axis The axis to find the maximum value in + * + * @return The point with maximum value in the specified axis + */ + public Point findMax(int axis) { + return findMax(root, axis).point; + } + + /** + * Find a point with maximum value in specified axis in a subtree + * + * @param root The root of the subtree to search in + * @param axis The axis to find the maximum value in + * + * @return The Node with maximum value in the specified axis of the point + */ + public Node findMax(Node root, int axis) { + if (root == null) { + return null; + } + if (root.getAxis() == axis) { + if (root.right == null) { + return root; + } + return findMax(root.right, axis); + } else { + Node left = findMax(root.left, axis); + Node right = findMax(root.right, axis); + Node[] candidates = {left, root, right}; + return Arrays.stream(candidates).filter(Objects::nonNull).max(Comparator.comparingInt(a -> a.point.getCoordinate(axis))).orElse(null); + } + } + + /** + * Delete the node with the given point. + * + * @param point the point to delete + * */ + public void delete(Point point) { + Node node = search(point).orElseThrow(() -> new IllegalArgumentException("Point not found")); + root = delete(root, node); + } + + /** + * Delete the specified node from a subtree. + * + * @param root The root of the subtree to delete from + * @param node The node to delete + * + * @return The new root of the subtree + */ + private Node delete(Node root, Node node) { + if (root == null) { + return null; + } + if (root.equals(node)) { + if (root.right != null) { + Node min = findMin(root.right, root.getAxis()); + root.point = min.point; + root.right = delete(root.right, min); + } else if (root.left != null) { + Node min = findMin(root.left, root.getAxis()); + root.point = min.point; + root.left = delete(root.left, min); + } else { + return null; + } + } + if (root.getAxisCoordinate() < node.point.getCoordinate(root.getAxis())) { + root.left = delete(root.left, node); + } else { + root.right = delete(root.right, node); + } + return root; + } + + /** + * Finds the nearest point in the tree to the given point. + * + * @param point The point to find the nearest neighbor to. + * */ + public Point findNearest(Point point) { + return findNearest(root, point, root).point; + } + + /** + * Finds the nearest point in a subtree to the given point. + * + * @param root The root of the subtree to search in. + * @param point The point to find the nearest neighbor to. + * @param nearest The nearest neighbor found so far. + * */ + private Node findNearest(Node root, Point point, Node nearest) { + if (root == null) { + return nearest; + } + if (root.point.equals(point)) { + return root; + } + int distance = Point.comparableDistance(root.point, point); + int distanceExceptAxis = Point.comparableDistanceExceptAxis(root.point, point, root.getAxis()); + if (distance < Point.comparableDistance(nearest.point, point)) { + nearest = root; + } + nearest = findNearest(root.getNearChild(point), point, nearest); + if (distanceExceptAxis < Point.comparableDistance(nearest.point, point)) { + nearest = findNearest(root.getFarChild(point), point, nearest); + } + return nearest; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/LCA.java b/src/main/java/com/thealgorithms/datastructures/trees/LCA.java new file mode 100644 index 000000000000..95a289493007 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/LCA.java @@ -0,0 +1,114 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.ArrayList; +import java.util.Scanner; + +public final class LCA { + private LCA() { + } + + private static final Scanner SCANNER = new Scanner(System.in); + + public static void main(String[] args) { + // The adjacency list representation of a tree: + ArrayList<ArrayList<Integer>> adj = new ArrayList<>(); + + // v is the number of vertices and e is the number of edges + int v = SCANNER.nextInt(); + int e = v - 1; + + for (int i = 0; i < v; i++) { + adj.add(new ArrayList<Integer>()); + } + + // Storing the given tree as an adjacency list + int to; + int from; + for (int i = 0; i < e; i++) { + to = SCANNER.nextInt(); + from = SCANNER.nextInt(); + + adj.get(to).add(from); + adj.get(from).add(to); + } + + // parent[v1] gives parent of a vertex v1 + int[] parent = new int[v]; + + // depth[v1] gives depth of vertex v1 with respect to the root + int[] depth = new int[v]; + + // Assuming the tree to be rooted at 0, hence calculating parent and depth of every vertex + dfs(adj, 0, -1, parent, depth); + + // Inputting the two vertices whose LCA is to be calculated + int v1 = SCANNER.nextInt(); + int v2 = SCANNER.nextInt(); + + // Outputting the LCA + System.out.println(getLCA(v1, v2, depth, parent)); + } + + /** + * Depth first search to calculate parent and depth of every vertex + * + * @param adj The adjacency list representation of the tree + * @param s The source vertex + * @param p Parent of source + * @param parent An array to store parents of all vertices + * @param depth An array to store depth of all vertices + */ + private static void dfs(ArrayList<ArrayList<Integer>> adj, int s, int p, int[] parent, int[] depth) { + for (int adjacent : adj.get(s)) { + if (adjacent != p) { + parent[adjacent] = s; + depth[adjacent] = 1 + depth[s]; + dfs(adj, adjacent, s, parent, depth); + } + } + } + + /** + * Method to calculate Lowest Common Ancestor + * + * @param v1 The first vertex + * @param v2 The second vertex + * @param depth An array with depths of all vertices + * @param parent An array with parents of all vertices + * @return Returns a vertex that is LCA of v1 and v2 + */ + private static int getLCA(int v1, int v2, int[] depth, int[] parent) { + if (depth[v1] < depth[v2]) { + int temp = v1; + v1 = v2; + v2 = temp; + } + while (depth[v1] != depth[v2]) { + v1 = parent[v1]; + } + if (v1 == v2) { + return v1; + } + while (v1 != v2) { + v1 = parent[v1]; + v2 = parent[v2]; + } + return v1; + } +} +/* + * Input: + * 10 + * 0 1 + * 0 2 + * 1 5 + * 5 6 + * 2 4 + * 2 3 + * 3 7 + * 7 9 + * 7 8 + * 9 4 + * Output: + * 2 + */ diff --git a/src/main/java/com/thealgorithms/datastructures/trees/LazySegmentTree.java b/src/main/java/com/thealgorithms/datastructures/trees/LazySegmentTree.java new file mode 100644 index 000000000000..e7a8e23d6610 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/LazySegmentTree.java @@ -0,0 +1,174 @@ +package com.thealgorithms.datastructures.trees; + +public class LazySegmentTree { + + /** + * Lazy Segment Tree + * + * @see + * <a href="https://www.geeksforgeeks.org/lazy-propagation-in-segment-tree/"> + */ + static class Node { + + private final int start; + private final int end; // start and end of the segment represented by this node + private int value; // value is the sum of all elements in the range [start, end) + private int lazy; // lazied value that should be added to children nodes + Node left; + Node right; // left and right children + + Node(int start, int end, int value) { + this.start = start; + this.end = end; + this.value = value; + this.lazy = 0; + this.left = null; + this.right = null; + } + + /** + * Update the value of this node with the given value diff. + * + * @param diff The value to add to every index of this node range. + */ + public void applyUpdate(int diff) { + this.lazy += diff; + this.value += (this.end - this.start) * diff; + } + + /** + * Shift the lazy value of this node to its children. + */ + public void shift() { + if (lazy == 0) { + return; + } + if (this.left == null && this.right == null) { + return; + } + this.value += this.lazy; + if (this.left != null) { + this.left.applyUpdate(this.lazy); + } + if (this.right != null) { + this.right.applyUpdate(this.lazy); + } + this.lazy = 0; + } + + /** + * Create a new node that is the sum of this node and the given node. + * + * @param left The left Node of merging + * @param right The right Node of merging + * @return The new Node. + */ + static Node merge(Node left, Node right) { + if (left == null) { + return right; + } + if (right == null) { + return left; + } + Node result = new Node(left.start, right.end, left.value + right.value); + result.left = left; + result.right = right; + return result; + } + + public int getValue() { + return value; + } + + public Node getLeft() { + return left; + } + + public Node getRight() { + return right; + } + } + + private final Node root; + + /** + * Create a new LazySegmentTree with the given array. + * + * @param array The array to create the LazySegmentTree from. + */ + public LazySegmentTree(int[] array) { + this.root = buildTree(array, 0, array.length); + } + + /** + * Build a new LazySegmentTree from the given array in O(n) time. + * + * @param array The array to build the LazySegmentTree from. + * @param start The start index of the current node. + * @param end The end index of the current node. + * @return The root of the new LazySegmentTree. + */ + private Node buildTree(int[] array, int start, int end) { + if (end - start < 2) { + return new Node(start, end, array[start]); + } + int mid = (start + end) >> 1; + Node left = buildTree(array, start, mid); + Node right = buildTree(array, mid, end); + return Node.merge(left, right); + } + + /** + * Update the value of given range with the given value diff in O(log n) time. + * + * @param left The left index of the range to update. + * @param right The right index of the range to update. + * @param diff The value to add to every index of the range. + * @param curr The current node. + */ + private void updateRange(int left, int right, int diff, Node curr) { + if (left <= curr.start && curr.end <= right) { + curr.applyUpdate(diff); + return; + } + if (left >= curr.end || right <= curr.start) { + return; + } + curr.shift(); + updateRange(left, right, diff, curr.left); + updateRange(left, right, diff, curr.right); + Node merge = Node.merge(curr.left, curr.right); + curr.value = merge.value; + } + + /** + * Get Node of given range in O(log n) time. + * + * @param left The left index of the range to update. + * @param right The right index of the range to update. + * @return The Node representing the sum of the given range. + */ + private Node getRange(int left, int right, Node curr) { + if (left <= curr.start && curr.end <= right) { + return curr; + } + if (left >= curr.end || right <= curr.start) { + return null; + } + curr.shift(); + return Node.merge(getRange(left, right, curr.left), getRange(left, right, curr.right)); + } + + public int getRange(int left, int right) { + Node result = getRange(left, right, root); + return result == null ? 0 : result.getValue(); + } + + public void updateRange(int left, int right, int diff) { + updateRange(left, right, diff, root); + } + + public Node getRoot() { + return root; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/LevelOrderTraversal.java b/src/main/java/com/thealgorithms/datastructures/trees/LevelOrderTraversal.java new file mode 100644 index 000000000000..f91aa303f66c --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/LevelOrderTraversal.java @@ -0,0 +1,54 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +public final class LevelOrderTraversal { + private LevelOrderTraversal() { + } + + public static List<List<Integer>> traverse(BinaryTree.Node root) { + if (root == null) { + return List.of(); + } + + List<List<Integer>> result = new ArrayList<>(); + + Queue<BinaryTree.Node> q = new LinkedList<>(); + q.add(root); + while (!q.isEmpty()) { + int nodesOnLevel = q.size(); + List<Integer> level = new LinkedList<>(); + for (int i = 0; i < nodesOnLevel; i++) { + BinaryTree.Node tempNode = q.poll(); + level.add(tempNode.data); + + if (tempNode.left != null) { + q.add(tempNode.left); + } + + if (tempNode.right != null) { + q.add(tempNode.right); + } + } + result.add(level); + } + return result; + } + + /* Print nodes at the given level */ + public static void printGivenLevel(BinaryTree.Node root, int level) { + if (root == null) { + System.out.println("Root node must not be null! Exiting."); + return; + } + if (level == 1) { + System.out.print(root.data + " "); + } else if (level > 1) { + printGivenLevel(root.left, level - 1); + printGivenLevel(root.right, level - 1); + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/PostOrderTraversal.java b/src/main/java/com/thealgorithms/datastructures/trees/PostOrderTraversal.java new file mode 100644 index 000000000000..50dc88381a24 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/PostOrderTraversal.java @@ -0,0 +1,68 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; + +/** + * Given tree is traversed in a 'post-order' way: LEFT -> RIGHT -> ROOT. + * Below are given the recursive and iterative implementations. + * <p> + * Complexities: + * Recursive: O(n) - time, O(n) - space, where 'n' is the number of nodes in a tree. + * <p> + * Iterative: O(n) - time, O(h) - space, where 'n' is the number of nodes in a tree + * and 'h' is the height of a binary tree. + * In the worst case 'h' can be O(n) if tree is completely unbalanced, for instance: + * 5 + * \ + * 6 + * \ + * 7 + * \ + * 8 + * + * @author Albina Gimaletdinova on 21/02/2023 + */ +public final class PostOrderTraversal { + private PostOrderTraversal() { + } + public static List<Integer> recursivePostOrder(BinaryTree.Node root) { + List<Integer> result = new ArrayList<>(); + recursivePostOrder(root, result); + return result; + } + + public static List<Integer> iterativePostOrder(BinaryTree.Node root) { + LinkedList<Integer> result = new LinkedList<>(); + if (root == null) { + return result; + } + + Deque<BinaryTree.Node> stack = new ArrayDeque<>(); + stack.push(root); + while (!stack.isEmpty()) { + BinaryTree.Node node = stack.pop(); + result.addFirst(node.data); + if (node.left != null) { + stack.push(node.left); + } + if (node.right != null) { + stack.push(node.right); + } + } + + return result; + } + + private static void recursivePostOrder(BinaryTree.Node root, List<Integer> result) { + if (root == null) { + return; + } + recursivePostOrder(root.left, result); + recursivePostOrder(root.right, result); + result.add(root.data); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/PreOrderTraversal.java b/src/main/java/com/thealgorithms/datastructures/trees/PreOrderTraversal.java new file mode 100644 index 000000000000..3aceac4d1852 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/PreOrderTraversal.java @@ -0,0 +1,67 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.ArrayList; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; + +/** + * Given tree is traversed in a 'pre-order' way: ROOT -> LEFT -> RIGHT. + * Below are given the recursive and iterative implementations. + * + * Complexities: + * Recursive: O(n) - time, O(n) - space, where 'n' is the number of nodes in a tree. + * + * Iterative: O(n) - time, O(h) - space, where 'n' is the number of nodes in a tree + * and 'h' is the height of a binary tree. + * In the worst case 'h' can be O(n) if tree is completely unbalanced, for instance: + * 5 + * \ + * 6 + * \ + * 7 + * \ + * 8 + * + * @author Albina Gimaletdinova on 17/02/2023 + */ +public final class PreOrderTraversal { + private PreOrderTraversal() { + } + public static List<Integer> recursivePreOrder(BinaryTree.Node root) { + List<Integer> result = new ArrayList<>(); + recursivePreOrder(root, result); + return result; + } + + public static List<Integer> iterativePreOrder(BinaryTree.Node root) { + List<Integer> result = new ArrayList<>(); + if (root == null) { + return result; + } + + Deque<BinaryTree.Node> stack = new LinkedList<>(); + stack.push(root); + while (!stack.isEmpty()) { + BinaryTree.Node node = stack.pop(); + result.add(node.data); + if (node.right != null) { + stack.push(node.right); + } + if (node.left != null) { + stack.push(node.left); + } + } + + return result; + } + + private static void recursivePreOrder(BinaryTree.Node root, List<Integer> result) { + if (root == null) { + return; + } + result.add(root.data); + recursivePreOrder(root.left, result); + recursivePreOrder(root.right, result); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/PrintTopViewofTree.java b/src/main/java/com/thealgorithms/datastructures/trees/PrintTopViewofTree.java new file mode 100644 index 000000000000..3ef664f3fa7d --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/PrintTopViewofTree.java @@ -0,0 +1,117 @@ +package com.thealgorithms.datastructures.trees; // Java program to print top view of Binary tree + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Queue; + +// Class for a tree node +class TreeNode { + + // Members + + int key; + TreeNode left; + TreeNode right; + + // Constructor + TreeNode(int key) { + this.key = key; + left = null; + right = null; + } +} + +// A class to represent a queue item. The queue is used to do Level +// order traversal. Every Queue item contains node and horizontal +// distance of node from root +class QItem { + + TreeNode node; + int hd; + + QItem(TreeNode n, int h) { + node = n; + hd = h; + } +} + +// Class for a Binary Tree +class Tree { + + TreeNode root; + + // Constructors + Tree() { + root = null; + } + + Tree(TreeNode n) { + root = n; + } + + // This method prints nodes in top view of binary tree + public void printTopView() { + // base case + if (root == null) { + return; + } + + // Creates an empty hashset + HashSet<Integer> set = new HashSet<>(); + + // Create a queue and add root to it + Queue<QItem> queue = new LinkedList<QItem>(); + queue.add(new QItem(root, 0)); // Horizontal distance of root is 0 + + // Standard BFS or level order traversal loop + while (!queue.isEmpty()) { + // Remove the front item and get its details + QItem qi = queue.remove(); + int hd = qi.hd; + TreeNode n = qi.node; + + // If this is the first node at its horizontal distance, + // then this node is in top view + if (!set.contains(hd)) { + set.add(hd); + System.out.print(n.key + " "); + } + + // Enqueue left and right children of current node + if (n.left != null) { + queue.add(new QItem(n.left, hd - 1)); + } + if (n.right != null) { + queue.add(new QItem(n.right, hd + 1)); + } + } + } +} + +// Driver class to test above methods +public final class PrintTopViewofTree { + private PrintTopViewofTree() { + } + + public static void main(String[] args) { + /* Create following Binary Tree + 1 + / \ + 2 3 + \ + 4 + \ + 5 + \ + 6*/ + TreeNode root = new TreeNode(1); + root.left = new TreeNode(2); + root.right = new TreeNode(3); + root.left.right = new TreeNode(4); + root.left.right.right = new TreeNode(5); + root.left.right.right.right = new TreeNode(6); + Tree t = new Tree(root); + System.out.println("Following are nodes in top view of Binary Tree"); + t.printTopView(); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/QuadTree.java b/src/main/java/com/thealgorithms/datastructures/trees/QuadTree.java new file mode 100644 index 000000000000..e0d255b1e784 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/QuadTree.java @@ -0,0 +1,176 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.ArrayList; +import java.util.List; + +/** + * Point is a simple class that represents a point in 2D space. + * + * @see <a href="https://en.wikipedia.org/wiki/Point_(geometry)">Point</a> + * @author <a href="https://github.com/sailok">Sailok Chinta</a> + */ +class Point { + public double x; + public double y; + + Point(double x, double y) { + this.x = x; + this.y = y; + } +} + +/** + * BoundingBox is a simple class that represents a bounding box in 2D space. + * + * @see <a href="https://en.wikipedia.org/wiki/Bounding_box">Bounding Box</a> + * @author <a href="https://github.com/sailok">Sailok Chinta</a> + */ +class BoundingBox { + public Point center; + public double halfWidth; + + BoundingBox(Point center, double halfWidth) { + this.center = center; + this.halfWidth = halfWidth; + } + + /** + * Checks if the point is inside the bounding box + * + * @param point The point to check + * @return true if the point is inside the bounding box, false otherwise + */ + public boolean containsPoint(Point point) { + return point.x >= center.x - halfWidth && point.x <= center.x + halfWidth && point.y >= center.y - halfWidth && point.y <= center.y + halfWidth; + } + + /** + * Checks if the bounding box intersects with the other bounding box + * + * @param otherBoundingBox The other bounding box + * @return true if the bounding box intersects with the other bounding box, false otherwise + */ + public boolean intersectsBoundingBox(BoundingBox otherBoundingBox) { + return otherBoundingBox.center.x - otherBoundingBox.halfWidth <= center.x + halfWidth && otherBoundingBox.center.x + otherBoundingBox.halfWidth >= center.x - halfWidth && otherBoundingBox.center.y - otherBoundingBox.halfWidth <= center.y + halfWidth + && otherBoundingBox.center.y + otherBoundingBox.halfWidth >= center.y - halfWidth; + } +} + +/** + * QuadTree is a tree data structure that is used to store spatial information + * in an efficient way. + * + * This implementation is specific to Point QuadTrees + * + * @see <a href="https://en.wikipedia.org/wiki/Quadtree">Quad Tree</a> + * @author <a href="https://github.com/sailok">Sailok Chinta</a> + */ +public class QuadTree { + private final BoundingBox boundary; + private final int capacity; + + private List<Point> pointList; + private boolean divided; + private QuadTree northWest; + private QuadTree northEast; + private QuadTree southWest; + private QuadTree southEast; + + public QuadTree(BoundingBox boundary, int capacity) { + this.boundary = boundary; + this.capacity = capacity; + + this.pointList = new ArrayList<>(); + this.divided = false; + this.northWest = null; + this.northEast = null; + this.southWest = null; + this.southEast = null; + } + + /** + * Inserts a point into the tree + * + * @param point The point to insert + * @return true if the point is successfully inserted, false otherwise + */ + public boolean insert(Point point) { + if (point == null) { + return false; + } + + // Ignore points that don't belong to this quad tree + if (!boundary.containsPoint(point)) { + return false; + } + + // if the space is not already occupied, add it to the list + if (pointList.size() < capacity) { + pointList.add(point); + return true; + } + + // if subdivision hasn't happened, divide the tree + if (!divided) { + subDivide(); + } + + // try to add the point in one of the four quadrants + if (northWest.insert(point)) { + return true; + } + + if (northEast.insert(point)) { + return true; + } + + if (southWest.insert(point)) { + return true; + } + + if (southEast.insert(point)) { + return true; + } + + return false; + } + + /** + * Create four children that fully divide this quad into four quads of equal area + */ + private void subDivide() { + double quadrantHalfWidth = boundary.halfWidth / 2; + + northWest = new QuadTree(new BoundingBox(new Point(boundary.center.x - quadrantHalfWidth, boundary.center.y + quadrantHalfWidth), quadrantHalfWidth), this.capacity); + northEast = new QuadTree(new BoundingBox(new Point(boundary.center.x + quadrantHalfWidth, boundary.center.y + quadrantHalfWidth), quadrantHalfWidth), this.capacity); + southWest = new QuadTree(new BoundingBox(new Point(boundary.center.x - quadrantHalfWidth, boundary.center.y - quadrantHalfWidth), quadrantHalfWidth), this.capacity); + southEast = new QuadTree(new BoundingBox(new Point(boundary.center.x + quadrantHalfWidth, boundary.center.y - quadrantHalfWidth), quadrantHalfWidth), this.capacity); + divided = true; + } + + /** + * Queries all the points that intersect with the other bounding box + * + * @param otherBoundingBox The other bounding box + * @return List of points that intersect with the other bounding box + */ + public List<Point> query(BoundingBox otherBoundingBox) { + List<Point> points = new ArrayList<>(); + + if (!boundary.intersectsBoundingBox(otherBoundingBox)) { + return points; + } + + // filter the points that intersect with the other bounding box + points.addAll(pointList.stream().filter(otherBoundingBox::containsPoint).toList()); + + if (divided) { + points.addAll(northWest.query(otherBoundingBox)); + points.addAll(northEast.query(otherBoundingBox)); + points.addAll(southWest.query(otherBoundingBox)); + points.addAll(southEast.query(otherBoundingBox)); + } + + return points; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/README.md b/src/main/java/com/thealgorithms/datastructures/trees/README.md new file mode 100644 index 000000000000..89eeb35c5917 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/README.md @@ -0,0 +1,30 @@ +## Tree +### Description + +Tree is a data structure where the data is organized in a hierarchial structure. There should be one root node (which does not have any parent) and all subsequent nodes are represented as children of the root node and its children. If a node has at least one child, it is called `internal` node and nodes with no children are called `leaf` nodes. + +### Basic Structure + +``` +class Tree<E>{ + E value; + Tree left; + Tree right; +} +``` + +This basic structure is for a binary tree where each internal tree has at least one and at most two children. `left` and `right` represent the two children and `value` is the placeholder for data. + + +### Properties +1. Tree data structure gives the facility to organize data in a hierarchial structure +2. Tree nodes can be inserted in a sorted order which can be used for searching and inserting data in O(logN) time where N is the number of nodes. + +### Types of Trees +1. **Binary Search Tree:** A binary tree where the elements are inserted in asorted order. Here the searching can be done in O(logN) time in (depending on the structure) +2. **AVL Tree and Red-Black Tree:** Binary search trees where the height is balanced. Here, searching is guaranteed to be in O(logN) time. +3. **Traversal algorithms:** <br> +a. **BFS:** Breadth-first-search where all the children at each level are traversed at once. <br> +b. **DFS:** Depth-first-search where the first discovered child is traversed first. +4. **MultiWay Search Tree:** Tree in sorted order, but more than two children in each internal node. +5. **Trie:** A character based multiway search tree where words can be retrieved based on their prefix. Useful for implementing prefix based search algorithm. \ No newline at end of file diff --git a/src/main/java/com/thealgorithms/datastructures/trees/RedBlackBST.java b/src/main/java/com/thealgorithms/datastructures/trees/RedBlackBST.java new file mode 100644 index 000000000000..01222b739ff0 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/RedBlackBST.java @@ -0,0 +1,339 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.Scanner; + +/** + * @author jack870131 + */ +public class RedBlackBST { + + private static final int RED = 0; + private static final int BLACK = 1; + + private class Node { + + int key = -1; + int color = BLACK; + Node left = nil; + Node right = nil; + Node p = nil; + + Node(int key) { + this.key = key; + } + } + + private final Node nil = new Node(-1); + private Node root = nil; + + public void printTree(Node node) { + if (node == nil) { + return; + } + printTree(node.left); + System.out.print(((node.color == RED) ? " R " : " B ") + "Key: " + node.key + " Parent: " + node.p.key + "\n"); + printTree(node.right); + } + + public void printTreepre(Node node) { + if (node == nil) { + return; + } + System.out.print(((node.color == RED) ? " R " : " B ") + "Key: " + node.key + " Parent: " + node.p.key + "\n"); + printTreepre(node.left); + printTreepre(node.right); + } + + private Node findNode(Node findNode, Node node) { + if (root == nil) { + return null; + } + if (findNode.key < node.key) { + if (node.left != nil) { + return findNode(findNode, node.left); + } + } else if (findNode.key > node.key) { + if (node.right != nil) { + return findNode(findNode, node.right); + } + } else if (findNode.key == node.key) { + return node; + } + return null; + } + + private void insert(Node node) { + Node temp = root; + if (root == nil) { + root = node; + node.color = BLACK; + node.p = nil; + } else { + node.color = RED; + while (true) { + if (node.key < temp.key) { + if (temp.left == nil) { + temp.left = node; + node.p = temp; + break; + } else { + temp = temp.left; + } + } else if (node.key >= temp.key) { + if (temp.right == nil) { + temp.right = node; + node.p = temp; + break; + } else { + temp = temp.right; + } + } + } + fixTree(node); + } + } + + private void fixTree(Node node) { + while (node.p.color == RED) { + Node y = nil; + if (node.p == node.p.p.left) { + y = node.p.p.right; + + if (y != nil && y.color == RED) { + node.p.color = BLACK; + y.color = BLACK; + node.p.p.color = RED; + node = node.p.p; + continue; + } + if (node == node.p.right) { + node = node.p; + rotateLeft(node); + } + node.p.color = BLACK; + node.p.p.color = RED; + rotateRight(node.p.p); + } else { + y = node.p.p.left; + if (y != nil && y.color == RED) { + node.p.color = BLACK; + y.color = BLACK; + node.p.p.color = RED; + node = node.p.p; + continue; + } + if (node == node.p.left) { + node = node.p; + rotateRight(node); + } + node.p.color = BLACK; + node.p.p.color = RED; + rotateLeft(node.p.p); + } + } + root.color = BLACK; + } + + void rotateLeft(Node node) { + if (node.p != nil) { + if (node == node.p.left) { + node.p.left = node.right; + } else { + node.p.right = node.right; + } + node.right.p = node.p; + node.p = node.right; + if (node.right.left != nil) { + node.right.left.p = node; + } + node.right = node.right.left; + node.p.left = node; + } else { + Node right = root.right; + root.right = right.left; + right.left.p = root; + root.p = right; + right.left = root; + right.p = nil; + root = right; + } + } + + void rotateRight(Node node) { + if (node.p != nil) { + if (node == node.p.left) { + node.p.left = node.left; + } else { + node.p.right = node.left; + } + + node.left.p = node.p; + node.p = node.left; + if (node.left.right != nil) { + node.left.right.p = node; + } + node.left = node.left.right; + node.p.right = node; + } else { + Node left = root.left; + root.left = root.left.right; + left.right.p = root; + root.p = left; + left.right = root; + left.p = nil; + root = left; + } + } + + void transplant(Node target, Node with) { + if (target.p == nil) { + root = with; + } else if (target == target.p.left) { + target.p.left = with; + } else { + target.p.right = with; + } + with.p = target.p; + } + + Node treeMinimum(Node subTreeRoot) { + while (subTreeRoot.left != nil) { + subTreeRoot = subTreeRoot.left; + } + return subTreeRoot; + } + + boolean delete(Node z) { + Node result = findNode(z, root); + if (result == null) { + return false; + } + Node x; + Node y = z; + int yorigcolor = y.color; + + if (z.left == nil) { + x = z.right; + transplant(z, z.right); + } else if (z.right == nil) { + x = z.left; + transplant(z, z.left); + } else { + y = treeMinimum(z.right); + yorigcolor = y.color; + x = y.right; + if (y.p == z) { + x.p = y; + } else { + transplant(y, y.right); + y.right = z.right; + y.right.p = y; + } + transplant(z, y); + y.left = z.left; + y.left.p = y; + y.color = z.color; + } + if (yorigcolor == BLACK) { + deleteFixup(x); + } + return true; + } + + void deleteFixup(Node x) { + while (x != root && x.color == BLACK) { + if (x == x.p.left) { + Node w = x.p.right; + if (w.color == RED) { + w.color = BLACK; + x.p.color = RED; + rotateLeft(x.p); + w = x.p.right; + } + if (w.left.color == BLACK && w.right.color == BLACK) { + w.color = RED; + x = x.p; + continue; + } else if (w.right.color == BLACK) { + w.left.color = BLACK; + w.color = RED; + rotateRight(w); + w = x.p.right; + } + if (w.right.color == RED) { + w.color = x.p.color; + x.p.color = BLACK; + w.right.color = BLACK; + rotateLeft(x.p); + x = root; + } + } else { + Node w = x.p.left; + if (w.color == RED) { + w.color = BLACK; + x.p.color = RED; + rotateRight(x.p); + w = x.p.left; + } + if (w.right.color == BLACK && w.left.color == BLACK) { + w.color = RED; + x = x.p; + continue; + } else if (w.left.color == BLACK) { + w.right.color = BLACK; + w.color = RED; + rotateLeft(w); + w = x.p.left; + } + if (w.left.color == RED) { + w.color = x.p.color; + x.p.color = BLACK; + w.left.color = BLACK; + rotateRight(x.p); + x = root; + } + } + } + x.color = BLACK; + } + + public void insertDemo() { + Scanner scan = new Scanner(System.in); + System.out.println("Add items"); + + int item; + Node node; + + item = scan.nextInt(); + while (item != -999) { + node = new Node(item); + insert(node); + item = scan.nextInt(); + } + printTree(root); + System.out.println("Pre order"); + printTreepre(root); + scan.close(); + } + + public void deleteDemo() { + Scanner scan = new Scanner(System.in); + System.out.println("Delete items"); + int item; + Node node; + item = scan.nextInt(); + node = new Node(item); + System.out.print("Deleting item " + item); + if (delete(node)) { + System.out.print(": deleted!"); + } else { + System.out.print(": does not exist!"); + } + + System.out.println(); + printTree(root); + System.out.println("Pre order"); + printTreepre(root); + scan.close(); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/SameTreesCheck.java b/src/main/java/com/thealgorithms/datastructures/trees/SameTreesCheck.java new file mode 100644 index 000000000000..cff27c12f1ca --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/SameTreesCheck.java @@ -0,0 +1,89 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * Given 2 binary trees. + * This code checks whether they are the same (structurally identical and have the same values) or + * not. <p> Example: + * 1. Binary trees: + * 1 1 + * / \ / \ + * 2 3 2 3 + * /\ /\ /\ /\ + * 4 5 6 7 4 5 6 7 + * These trees are the same, so the code returns 'true'. + * <p> + * 2. Binary trees: + * 1 1 + * / \ + * 2 2 + * These trees are NOT the same (the structure differs), so the code returns 'false'. + * <p> + * This solution implements the breadth-first search (BFS) algorithm. + * For each tree we create a queue and iterate the trees using these queues. + * On each step we check the nodes for equality, and if the nodes are not the same, return false. + * Otherwise, add children nodes to the queues and continue traversing the trees. + * <p> + * Complexities: + * O(N) - time, where N is the number of nodes in a binary tree, + * O(N) - space, where N is the number of nodes in a binary tree. + * + * @author Albina Gimaletdinova on 13/01/2023 + */ +public final class SameTreesCheck { + private SameTreesCheck() { + } + public static boolean check(BinaryTree.Node p, BinaryTree.Node q) { + if (p == null && q == null) { + return true; + } + if (p == null || q == null) { + return false; + } + + Deque<BinaryTree.Node> q1 = new ArrayDeque<>(); + Deque<BinaryTree.Node> q2 = new ArrayDeque<>(); + q1.add(p); + q2.add(q); + while (!q1.isEmpty() && !q2.isEmpty()) { + BinaryTree.Node first = q1.poll(); + BinaryTree.Node second = q2.poll(); + // check that some node can be null + // if the check is true: both nodes are null or both nodes are not null + if (!equalNodes(first, second)) { + return false; + } + + if (first != null) { + if (!equalNodes(first.left, second.left)) { + return false; + } + if (first.left != null) { + q1.add(first.left); + q2.add(second.left); + } + + if (!equalNodes(first.right, second.right)) { + return false; + } + if (first.right != null) { + q1.add(first.right); + q2.add(second.right); + } + } + } + return true; + } + + private static boolean equalNodes(BinaryTree.Node p, BinaryTree.Node q) { + if (p == null && q == null) { + return true; + } + if (p == null || q == null) { + return false; + } + return p.data == q.data; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/SegmentTree.java b/src/main/java/com/thealgorithms/datastructures/trees/SegmentTree.java new file mode 100644 index 000000000000..57b3edc163ca --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/SegmentTree.java @@ -0,0 +1,81 @@ +package com.thealgorithms.datastructures.trees; + +public class SegmentTree { + + private int[] segTree; + private int n; + private int[] arr; + + /* Constructor which takes the size of the array and the array as a parameter*/ + public SegmentTree(int n, int[] arr) { + this.n = n; + int x = (int) (Math.ceil(Math.log(n) / Math.log(2))); + int segSize = 2 * (int) Math.pow(2, x) - 1; + + this.segTree = new int[segSize]; + this.arr = arr; + this.n = n; + constructTree(arr, 0, n - 1, 0); + } + + /* A function which will create the segment tree*/ + public final int constructTree(int[] arr, int start, int end, int index) { + if (start == end) { + this.segTree[index] = arr[start]; + return arr[start]; + } + + int mid = start + (end - start) / 2; + this.segTree[index] = constructTree(arr, start, mid, index * 2 + 1) + constructTree(arr, mid + 1, end, index * 2 + 2); + return this.segTree[index]; + } + + /* A function which will update the value at a index i. This will be called by the + update function internally*/ + private void updateTree(int start, int end, int index, int diff, int segIndex) { + if (index < start || index > end) { + return; + } + + this.segTree[segIndex] += diff; + if (start != end) { + int mid = start + (end - start) / 2; + updateTree(start, mid, index, diff, segIndex * 2 + 1); + updateTree(mid + 1, end, index, diff, segIndex * 2 + 2); + } + } + + /* A function to update the value at a particular index*/ + public void update(int index, int value) { + if (index < 0 || index > n) { + return; + } + + int diff = value - arr[index]; + arr[index] = value; + updateTree(0, n - 1, index, diff, 0); + } + + /* A function to get the sum of the elements from index l to index r. This will be called + * internally*/ + private int getSumTree(int start, int end, int qStart, int qEnd, int segIndex) { + if (qStart <= start && qEnd >= end) { + return this.segTree[segIndex]; + } + + if (qStart > end || qEnd < start) { + return 0; + } + + int mid = start + (end - start) / 2; + return (getSumTree(start, mid, qStart, qEnd, segIndex * 2 + 1) + getSumTree(mid + 1, end, qStart, qEnd, segIndex * 2 + 2)); + } + + /* A function to query the sum of the subarray [start...end]*/ + public int getSum(int start, int end) { + if (start < 0 || end > n || start > end) { + return 0; + } + return getSumTree(0, n - 1, start, end, 0); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/SplayTree.java b/src/main/java/com/thealgorithms/datastructures/trees/SplayTree.java new file mode 100644 index 000000000000..2668b609aedc --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/SplayTree.java @@ -0,0 +1,324 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.LinkedList; +import java.util.List; + +/** + * Implementation of a Splay Tree data structure. + * + * A splay tree is a self-adjusting binary search tree with the additional + * property + * that recently accessed elements are quick to access again. It performs basic + * operations such as insertion, deletion, and searching in O(log n) amortized + * time, + * where n is the number of elements in the tree. + * + * The key feature of splay trees is the splay operation, which moves a node + * closer + * to the root of the tree when it is accessed. This operation helps to maintain + * good balance and improves the overall performance of the tree. After + * performing + * a splay operation, the accessed node becomes the new root of the tree. + * + * Splay trees have applications in various areas, including caching, network + * routing, + * and dynamic optimality analysis. + */ +public class SplayTree { + public static final TreeTraversal PRE_ORDER = new PreOrderTraversal(); + public static final TreeTraversal IN_ORDER = new InOrderTraversal(); + public static final TreeTraversal POST_ORDER = new PostOrderTraversal(); + + private Node root; + + /** + * Checks if the tree is empty. + * + * @return True if the tree is empty, otherwise false. + */ + public boolean isEmpty() { + return root == null; + } + + /** + * Insert a key into the SplayTree. + * + * @param key The key to insert. + */ + public void insert(final int key) { + root = insertRec(root, key); + root = splay(root, key); + } + + /** + * Search for a key in the SplayTree. + * + * @param key The key to search for. + * @return True if the key is found, otherwise false. + */ + public boolean search(int key) { + root = splay(root, key); + return root != null && root.key == key; + } + + /** + * Deletes a key from the SplayTree. + * + * @param key The key to delete. + * @throws IllegalArgumentException If the tree is empty. + */ + public void delete(final int key) { + if (isEmpty()) { + throw new EmptyTreeException("Cannot delete from an empty tree"); + } + + root = splay(root, key); + + if (root.key != key) { + return; + } + + if (root.left == null) { + root = root.right; + } else { + Node temp = root; + root = splay(root.left, findMax(root.left).key); + root.right = temp.right; + } + } + + /** + * Perform a traversal of the SplayTree. + * + * @param traversal The type of traversal method. + * @return A list containing the keys in the specified traversal order. + */ + public List<Integer> traverse(TreeTraversal traversal) { + List<Integer> result = new LinkedList<>(); + traversal.traverse(root, result); + return result; + } + + /** + * Finds the node with the maximum key in a given subtree. + * + * <p> + * This method traverses the right children of the subtree until it finds the + * rightmost node, which contains the maximum key. + * </p> + * + * @param root The root node of the subtree. + * @return The node with the maximum key in the subtree. + */ + private Node findMax(Node root) { + while (root.right != null) { + root = root.right; + } + return root; + } + + /** + * Zig operation. + * + * <p> + * The zig operation is used to perform a single rotation on a node to move it + * closer to + * the root of the tree. It is typically applied when the node is a left child + * of its parent + * and needs to be rotated to the right. + * </p> + * + * @param x The node to perform the zig operation on. + * @return The new root node after the operation. + */ + private Node rotateRight(Node x) { + Node y = x.left; + x.left = y.right; + y.right = x; + return y; + } + + /** + * Zag operation. + * + * <p> + * The zag operation is used to perform a single rotation on a node to move it + * closer to + * the root of the tree. It is typically applied when the node is a right child + * of its parent + * and needs to be rotated to the left. + * </p> + * + * @param x The node to perform the zag operation on. + * @return The new root node after the operation. + */ + private Node rotateLeft(Node x) { + Node y = x.right; + x.right = y.left; + y.left = x; + return y; + } + + /** + * Splay operation. + * + * <p> + * The splay operation is the core operation of a splay tree. It moves a + * specified node + * closer to the root of the tree by performing a series of rotations. The goal + * of the splay + * operation is to improve the access time for frequently accessed nodes by + * bringing them + * closer to the root. + * </p> + * + * <p> + * The splay operation consists of three main cases: + * <ul> + * <li>Zig-Zig case: Perform two consecutive rotations.</li> + * <li>Zig-Zag case: Perform two consecutive rotations in opposite + * directions.</li> + * <li>Zag-Zag case: Perform two consecutive rotations.</li> + * </ul> + * </p> + * + * <p> + * After performing the splay operation, the accessed node becomes the new root + * of the tree. + * </p> + * + * @param root The root of the subtree to splay. + * @param key The key to splay around. + * @return The new root of the splayed subtree. + */ + private Node splay(Node root, final int key) { + if (root == null || root.key == key) { + return root; + } + + if (root.key > key) { + if (root.left == null) { + return root; + } + // Zig-Zig case + if (root.left.key > key) { + root.left.left = splay(root.left.left, key); + root = rotateRight(root); + } else if (root.left.key < key) { + root.left.right = splay(root.left.right, key); + if (root.left.right != null) { + root.left = rotateLeft(root.left); + } + } + return (root.left == null) ? root : rotateRight(root); + } else { + if (root.right == null) { + return root; + } + // Zag-Zag case + if (root.right.key > key) { + root.right.left = splay(root.right.left, key); + if (root.right.left != null) { + root.right = rotateRight(root.right); + } + } else if (root.right.key < key) { + root.right.right = splay(root.right.right, key); + root = rotateLeft(root); + } + return (root.right == null) ? root : rotateLeft(root); + } + } + + private Node insertRec(Node root, final int key) { + if (root == null) { + return new Node(key); + } + + if (key < root.key) { + root.left = insertRec(root.left, key); + } else if (key > root.key) { + root.right = insertRec(root.right, key); + } else { + throw new DuplicateKeyException("Duplicate key: " + key); + } + + return root; + } + + public static class EmptyTreeException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public EmptyTreeException(String message) { + super(message); + } + } + + public static class DuplicateKeyException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public DuplicateKeyException(String message) { + super(message); + } + } + + private static class Node { + final int key; + Node left; + Node right; + + Node(int key) { + this.key = key; + left = null; + right = null; + } + } + + public interface TreeTraversal { + /** + * Recursive function for a specific order traversal. + * + * @param root The root of the subtree to traverse. + * @param result The list to store the traversal result. + */ + void traverse(Node root, List<Integer> result); + } + + private static final class InOrderTraversal implements TreeTraversal { + private InOrderTraversal() { + } + + public void traverse(Node root, List<Integer> result) { + if (root != null) { + traverse(root.left, result); + result.add(root.key); + traverse(root.right, result); + } + } + } + + private static final class PreOrderTraversal implements TreeTraversal { + private PreOrderTraversal() { + } + + public void traverse(Node root, List<Integer> result) { + if (root != null) { + result.add(root.key); + traverse(root.left, result); + traverse(root.right, result); + } + } + } + + private static final class PostOrderTraversal implements TreeTraversal { + private PostOrderTraversal() { + } + + public void traverse(Node root, List<Integer> result) { + if (root != null) { + traverse(root.left, result); + traverse(root.right, result); + result.add(root.key); + } + } + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/Treap.java b/src/main/java/com/thealgorithms/datastructures/trees/Treap.java new file mode 100644 index 000000000000..1e5d551cc40b --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/Treap.java @@ -0,0 +1,357 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.Random; + +/** + * Treap -> Tree + Heap + * Also called as cartesian tree + * + * @see + * <a href = "https://cp-algorithms.com/data_structures/treap.html" /> + */ + +public class Treap { + + public static class TreapNode { + /** + * TreapNode class defines the individual nodes in the Treap + * + * value -> holds the value of the node. + * Binary Search Tree is built based on value. + * + * priority -> holds the priority of the node. + * Heaps are maintained based on priority. + * It is randomly assigned + * + * size -> holds the size of the subtree with current node as root + * + * left -> holds the left subtree + * right -> holds the right subtree + */ + public int value; + private int priority; + private int size; + public TreapNode left; + public TreapNode right; + + public TreapNode(int valueParam, int priorityParam) { + value = valueParam; + priority = priorityParam; + size = 1; + left = null; + right = null; + } + + /** + * updateSize -> updates the subtree size of the current node + */ + private void updateSize() { + size = 1; + if (left != null) { + size += left.size; + } + if (right != null) { + size += right.size; + } + } + } + + /** + * root -> holds the root node in the Treap + * random -> to generate random priority for the nodes in the Treap + */ + private TreapNode root; + private Random random = new Random(); + + /** + * Constructors + * + * Treap() -> create an empty Treap + * Treap(int[] nodeValues) -> add the elements given in the array to the Treap + */ + public Treap() { + root = null; + } + + /** + * merges two Treaps left and right into a single Treap + * + * @param left left Treap + * @param right right Treap + * @return root of merged Treap + */ + private TreapNode merge(TreapNode left, TreapNode right) { + if (left == null) { + return right; + } + if (right == null) { + return left; + } + + if (left.priority > right.priority) { + left.right = merge(left.right, right); + left.updateSize(); + return left; + } else { + right.left = merge(left, right.left); + right.updateSize(); + return right; + } + } + + /** + * split the Treap into two Treaps where left Treap has nodes <= key and right Treap has nodes > key + * + * @param node root node to be split + * @param key key to compare the nodes + * @return TreapNode array of size 2. + * TreapNode[0] contains the root of left Treap after split + * TreapNode[1] contains the root of right Treap after split + */ + private TreapNode[] split(TreapNode node, int key) { + if (node == null) { + return new TreapNode[] {null, null}; + } + + TreapNode[] result; + + if (node.value <= key) { + result = split(node.right, key); + node.right = result[0]; + node.updateSize(); + result[0] = node; + } else { + result = split(node.left, key); + node.left = result[1]; + node.updateSize(); + result[1] = node; + } + + return result; + } + + /** + * insert a node into the Treap + * + * @param value value to be inserted into the Treap + * @return root of the Treap where the value is inserted + */ + public TreapNode insert(int value) { + if (root == null) { + root = new TreapNode(value, random.nextInt()); + return root; + } + + TreapNode[] splitted = split(root, value); + + TreapNode node = new TreapNode(value, random.nextInt()); + + TreapNode tempMerged = merge(splitted[0], node); + tempMerged.updateSize(); + + TreapNode merged = merge(tempMerged, splitted[1]); + merged.updateSize(); + + root = merged; + + return root; + } + + /** + * delete a value from root if present + * + * @param value value to be deleted from the Treap + * @return root of the Treap where delete has been performed + */ + public TreapNode delete(int value) { + root = deleteNode(root, value); + return root; + } + + private TreapNode deleteNode(TreapNode root, int value) { + if (root == null) { + return null; + } + + if (value < root.value) { + root.left = deleteNode(root.left, value); + } else if (value > root.value) { + root.right = deleteNode(root.right, value); + } else { + root = merge(root.left, root.right); + } + + if (root != null) { + root.updateSize(); + } + return root; + } + + /** + * print inorder traversal of the Treap + */ + public void inOrder() { + System.out.print("{"); + printInorder(root); + System.out.print("}"); + } + + private void printInorder(TreapNode root) { + if (root == null) { + return; + } + printInorder(root.left); + System.out.print(root.value + ","); + printInorder(root.right); + } + + /** + * print preOrder traversal of the Treap + */ + public void preOrder() { + System.out.print("{"); + printPreOrder(root); + System.out.print("}"); + } + + private void printPreOrder(TreapNode root) { + if (root == null) { + return; + } + System.out.print(root.value + ","); + printPreOrder(root.left); + printPreOrder(root.right); + } + + /** + * print postOrder traversal of the Treap + */ + public void postOrder() { + System.out.print("{"); + printPostOrder(root); + System.out.print("}"); + } + + private void printPostOrder(TreapNode root) { + if (root == null) { + return; + } + printPostOrder(root.left); + printPostOrder(root.right); + System.out.print(root.value + ","); + } + + /** + * Search a value in the Treap + * + * @param value value to be searched for + * @return node containing the value + * null if not found + */ + public TreapNode search(int value) { + return searchVal(root, value); + } + + private TreapNode searchVal(TreapNode root, int value) { + if (root == null) { + return null; + } + + if (root.value == value) { + return root; + } else if (root.value < value) { + return searchVal(root.right, value); + } else { + return searchVal(root.left, value); + } + } + + /** + * find the lowerBound of a value in the Treap + * + * @param value value for which lowerBound is to be found + * @return node which is the lowerBound of the value passed + */ + public TreapNode lowerBound(int value) { + TreapNode lowerBoundNode = null; + TreapNode current = root; + + while (current != null) { + if (current.value >= value) { + lowerBoundNode = current; + current = current.left; + } else { + current = current.right; + } + } + + return lowerBoundNode; + } + + /** + * find the upperBound of a value in the Treap + * + * @param value value for which upperBound is to be found + * @return node which is the upperBound of the value passed + */ + public TreapNode upperBound(int value) { + TreapNode upperBoundNode = null; + TreapNode current = root; + + while (current != null) { + if (current.value > value) { + upperBoundNode = current; + current = current.left; + } else { + current = current.right; + } + } + + return upperBoundNode; + } + + /** + * returns size of the Treap + */ + public int size() { + if (root == null) { + return 0; + } + return root.size; + } + + /** + * returns if Treap is empty + */ + public boolean isEmpty() { + return root == null; + } + + /** + * returns root node of the Treap + */ + public TreapNode getRoot() { + return root; + } + + /** + * returns left node of the TreapNode + */ + public TreapNode getLeft(TreapNode node) { + return node.left; + } + + /** + * returns the right node of the TreapNode + */ + public TreapNode getRight(TreapNode node) { + return node.right; + } + + /** + * prints the value, priority, size of the subtree of the TreapNode, left TreapNode and right TreapNode of the node + */ + public String toString(TreapNode node) { + return "{value : " + node.value + ", priority : " + node.priority + ", subTreeSize = " + node.size + ", left = " + node.left + ", right = " + node.right + "}"; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/TreeRandomNode.java b/src/main/java/com/thealgorithms/datastructures/trees/TreeRandomNode.java new file mode 100644 index 000000000000..cf56731fb079 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/TreeRandomNode.java @@ -0,0 +1,87 @@ +package com.thealgorithms.datastructures.trees; + +/* Author : Suraj Kumar + Github : https://github.com/skmodi649 + */ + +/* PROBLEM DESCRIPTION : + There is a Binary Search Tree given, and we are supposed to find a random node in the given binary + tree. + */ + +/* ALGORITHM : + Step 1: START + Step 2: First create a binary tree using the steps mentioned in the first approach + Step 3: Now use a method inOrder() that takes a node as input parameter to traverse through the + binary tree in inorder fashion as also store the values in a ArrayList simultaneously. + Step 4: Now define a method getRandom() that takes a node as input parameter, in this first call + the inOrder() method to store the values in the arraylist, then find the size of the + binary tree and now just generate a random number between 0 to n-1. Step 5: After generating the + number display the value of the ArrayList at the generated index Step 6: STOP + */ + +import java.util.ArrayList; + +// Using auxiliary array to find the random node in a given binary tree + +public class TreeRandomNode { + + private final class Node { + + int item; + Node left; + Node right; + } + + // Using an arraylist to store the inorder traversal of the given binary tree + static ArrayList<Integer> list = new ArrayList<>(); + // root of Tree + Node root; + + TreeRandomNode() { + root = null; + } + + // Now lets find the inorder traversal of the given binary tree + static void inOrder(Node node) { + if (node == null) { + return; + } + + // traverse the left child + inOrder(node.left); + + list.add(node.item); + // traverse the right child + inOrder(node.right); + } + + public void getRandom(Node val) { + inOrder(val); + // getting the count of node of the binary tree + int n = list.size(); + int min = 0; + int max = n - 1; + // Generate random int value from 0 to n-1 + int b = (int) (Math.random() * (max - min + 1) + min); + // displaying the value at the generated index + int random = list.get(b); + System.out.println("Random Node : " + random); + } +} +/* Explanation of the Approach : + (a) Form the required binary tree + (b) Now use the inOrder() method to get the nodes in inOrder fashion and also store them in the + given arraylist 'list' (c) Using the getRandom() method generate a random number between 0 to n-1, + then get the value at the generated random number from the arraylist using get() method and + finally display the result. + */ +/* OUTPUT : + First output : + Random Node : 15 + Second output : + Random Node : 99 + */ +/* Time Complexity : O(n) + Auxiliary Space Complexity : O(1) + */ diff --git a/src/main/java/com/thealgorithms/datastructures/trees/Trie.java b/src/main/java/com/thealgorithms/datastructures/trees/Trie.java new file mode 100644 index 000000000000..02f28d4d83ad --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/Trie.java @@ -0,0 +1,202 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.HashMap; + +/** + * Represents a Trie Node that stores a character and pointers to its children. + * Each node has a hashmap which can point to all possible characters. + * Each node also has a boolean value to indicate if it is the end of a word. + */ +class TrieNode { + char value; + HashMap<Character, TrieNode> child; + boolean end; + + /** + * Constructor to initialize a TrieNode with an empty hashmap + * and set end to false. + */ + TrieNode(char value) { + this.value = value; + this.child = new HashMap<>(); + this.end = false; + } +} + +/** + * Trie Data structure implementation without any libraries. + * <p> + * The Trie (also known as a prefix tree) is a special tree-like data structure + * that is used to store a dynamic set or associative array where the keys are + * usually strings. It is highly efficient for prefix-based searches. + * <p> + * This implementation supports basic Trie operations like insertion, search, + * and deletion. + * <p> + * Each node of the Trie represents a character and has child nodes for each + * possible character. + * + * @author <a href="https://github.com/dheeraj92">Dheeraj Kumar Barnwal</a> + * @author <a href="https://github.com/sailok">Sailok Chinta</a> + */ + +public class Trie { + private static final char ROOT_NODE_VALUE = '*'; + + private final TrieNode root; + + /** + * Constructor to initialize the Trie. + * The root node is created but doesn't represent any character. + */ + public Trie() { + root = new TrieNode(ROOT_NODE_VALUE); + } + + /** + * Inserts a word into the Trie. + * <p> + * The method traverses the Trie from the root, character by character, and adds + * nodes if necessary. It marks the last node of the word as an end node. + * + * @param word The word to be inserted into the Trie. + */ + public void insert(String word) { + TrieNode currentNode = root; + for (int i = 0; i < word.length(); i++) { + TrieNode node = currentNode.child.getOrDefault(word.charAt(i), null); + + if (node == null) { + node = new TrieNode(word.charAt(i)); + currentNode.child.put(word.charAt(i), node); + } + currentNode = node; + } + + currentNode.end = true; + } + + /** + * Searches for a word in the Trie. + * <p> + * This method traverses the Trie based on the input word and checks whether + * the word exists. It returns true if the word is found and its end flag is + * true. + * + * @param word The word to search in the Trie. + * @return true if the word exists in the Trie, false otherwise. + */ + public boolean search(String word) { + TrieNode currentNode = root; + for (int i = 0; i < word.length(); i++) { + TrieNode node = currentNode.child.getOrDefault(word.charAt(i), null); + + if (node == null) { + return false; + } + currentNode = node; + } + + return currentNode.end; + } + + /** + * Deletes a word from the Trie. + * <p> + * The method traverses the Trie to find the word and marks its end flag as + * false. + * It returns true if the word was successfully deleted, false if the word + * wasn't found. + * + * @param word The word to be deleted from the Trie. + * @return true if the word was found and deleted, false if it was not found. + */ + public boolean delete(String word) { + TrieNode currentNode = root; + for (int i = 0; i < word.length(); i++) { + TrieNode node = currentNode.child.getOrDefault(word.charAt(i), null); + if (node == null) { + return false; + } + + currentNode = node; + } + + if (currentNode.end) { + currentNode.end = false; + return true; + } + + return false; + } + + /** + * Counts the number of words in the trie + *<p> + * The method traverses the Trie and counts the number of words. + * + * @return count of words + */ + public int countWords() { + return countWords(root); + } + + private int countWords(TrieNode node) { + if (node == null) { + return 0; + } + + int count = 0; + if (node.end) { + count++; + } + + for (TrieNode child : node.child.values()) { + count += countWords(child); + } + + return count; + } + + /** + * Check if the prefix exists in the trie + * + * @param prefix the prefix to be checked in the Trie + * @return true / false depending on the prefix if exists in the Trie + */ + public boolean startsWithPrefix(String prefix) { + TrieNode currentNode = root; + + for (int i = 0; i < prefix.length(); i++) { + TrieNode node = currentNode.child.getOrDefault(prefix.charAt(i), null); + if (node == null) { + return false; + } + + currentNode = node; + } + + return true; + } + + /** + * Count the number of words starting with the given prefix in the trie + * + * @param prefix the prefix to be checked in the Trie + * @return count of words + */ + public int countWordsWithPrefix(String prefix) { + TrieNode currentNode = root; + + for (int i = 0; i < prefix.length(); i++) { + TrieNode node = currentNode.child.getOrDefault(prefix.charAt(i), null); + if (node == null) { + return 0; + } + + currentNode = node; + } + + return countWords(currentNode); + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/VerticalOrderTraversal.java b/src/main/java/com/thealgorithms/datastructures/trees/VerticalOrderTraversal.java new file mode 100644 index 000000000000..c1d15390d4b9 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/VerticalOrderTraversal.java @@ -0,0 +1,96 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; + +/* The following class implements a vertical order traversal +in a tree from top to bottom and left to right, so for a tree : + 1 + / \ + 2 3 + / \ \ + 4 5 6 + \ / \ + 7 8 10 + \ + 9 + the sequence will be : + 4 2 7 1 5 9 3 8 6 10 + */ +public final class VerticalOrderTraversal { + private VerticalOrderTraversal() { + } + + /*Function that receives a root Node and prints the tree + in Vertical Order.*/ + public static ArrayList<Integer> verticalTraversal(BinaryTree.Node root) { + if (root == null) { + return new ArrayList<>(); + } + + /*Queue to store the Nodes.*/ + Queue<BinaryTree.Node> queue = new LinkedList<>(); + + /*Queue to store the index of particular vertical + column of a tree , with root at 0, Nodes on left + with negative index and Nodes on right with positive + index. */ + Queue<Integer> index = new LinkedList<>(); + + /*Map of Integer and ArrayList to store all the + elements in a particular index in a single arrayList + that will have a key equal to the index itself. */ + Map<Integer, ArrayList<Integer>> map = new HashMap<>(); + + /* min and max stores leftmost and right most index to + later print the tree in vertical fashion.*/ + int max = 0; + int min = 0; + queue.offer(root); + index.offer(0); + + while (!queue.isEmpty()) { + if (queue.peek().left != null) { + /*Adding the left Node if it is not null + and its index by subtracting 1 from it's + parent's index*/ + queue.offer(queue.peek().left); + index.offer(index.peek() - 1); + } + if (queue.peek().right != null) { + /*Adding the right Node if it is not null + and its index by adding 1 from it's + parent's index*/ + queue.offer(queue.peek().right); + index.offer(index.peek() + 1); + } + /*If the map does not contains the index a new + ArrayList is created with the index as key.*/ + if (!map.containsKey(index.peek())) { + ArrayList<Integer> a = new ArrayList<>(); + map.put(index.peek(), a); + } + /*For a index, corresponding Node data is added + to the respective ArrayList present at that + index. */ + map.get(index.peek()).add(queue.peek().data); + max = Math.max(max, index.peek()); + min = Math.min(min, index.peek()); + /*The Node and its index are removed + from their respective queues.*/ + index.poll(); + queue.poll(); + } + /*Finally map data is printed here which has keys + from min to max. Each ArrayList represents a + vertical column that is added in ans ArrayList.*/ + ArrayList<Integer> ans = new ArrayList<>(); + for (int i = min; i <= max; i++) { + ans.addAll(map.get(i)); + } + return ans; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/ZigzagTraversal.java b/src/main/java/com/thealgorithms/datastructures/trees/ZigzagTraversal.java new file mode 100644 index 000000000000..84fe0eb2c42a --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/ZigzagTraversal.java @@ -0,0 +1,77 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; + +/** + * Given a binary tree. + * This code returns the zigzag level order traversal of its nodes' values. + * Binary tree: + * 7 + * / \ + * 6 3 + * / \ / \ + * 2 4 10 19 + * Zigzag traversal: + * [[7], [3, 6], [2, 4, 10, 19]] + * <p> + * This solution implements the breadth-first search (BFS) algorithm using a queue. + * 1. The algorithm starts with a root node. This node is added to a queue. + * 2. While the queue is not empty: + * - each time we enter the while-loop we get queue size. Queue size refers to the number of nodes + * at the current level. + * - we traverse all the level nodes in 2 ways: from left to right OR from right to left + * (this state is stored on `prevLevelFromLeftToRight` variable) + * - if the current node has children we add them to a queue + * - add level with nodes to a result. + * <p> + * Complexities: + * O(N) - time, where N is the number of nodes in a binary tree + * O(N) - space, where N is the number of nodes in a binary tree + * + * @author Albina Gimaletdinova on 11/01/2023 + */ +public final class ZigzagTraversal { + private ZigzagTraversal() { + } + public static List<List<Integer>> traverse(BinaryTree.Node root) { + if (root == null) { + return List.of(); + } + + List<List<Integer>> result = new ArrayList<>(); + + // create a queue + Deque<BinaryTree.Node> q = new ArrayDeque<>(); + q.offer(root); + // start with writing nodes from left to right + boolean prevLevelFromLeftToRight = false; + + while (!q.isEmpty()) { + int nodesOnLevel = q.size(); + List<Integer> level = new LinkedList<>(); + // traverse all the level nodes + for (int i = 0; i < nodesOnLevel; i++) { + BinaryTree.Node node = q.poll(); + if (prevLevelFromLeftToRight) { + level.add(0, node.data); + } else { + level.add(node.data); + } + if (node.left != null) { + q.offer(node.left); + } + if (node.right != null) { + q.offer(node.right); + } + } + // the next level node traversal will be from the other side + prevLevelFromLeftToRight = !prevLevelFromLeftToRight; + result.add(level); + } + return result; + } +} diff --git a/src/main/java/com/thealgorithms/datastructures/trees/nearestRightKey.java b/src/main/java/com/thealgorithms/datastructures/trees/nearestRightKey.java new file mode 100644 index 000000000000..6c53666e5856 --- /dev/null +++ b/src/main/java/com/thealgorithms/datastructures/trees/nearestRightKey.java @@ -0,0 +1,83 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.Scanner; +import java.util.concurrent.ThreadLocalRandom; + +final class NearestRightKey { + private NearestRightKey() { + } + + public static void main(String[] args) { + NRKTree root = buildTree(); + Scanner sc = new Scanner(System.in); + System.out.print("Enter first number: "); + int inputX0 = sc.nextInt(); + int toPrint = nearestRightKey(root, inputX0); + System.out.println("Key: " + toPrint); + sc.close(); + } + + public static NRKTree buildTree() { + int randomX = ThreadLocalRandom.current().nextInt(0, 100 + 1); + NRKTree root = new NRKTree(null, null, randomX); + + for (int i = 0; i < 1000; i++) { + randomX = ThreadLocalRandom.current().nextInt(0, 100 + 1); + root = root.insertKey(root, randomX); + } + + return root; + } + + public static int nearestRightKey(NRKTree root, int x0) { + // Check whether tree is empty + if (root == null) { + return 0; + } else { + if (root.data - x0 > 0) { + // Go left + int temp = nearestRightKey(root.left, x0); + if (temp == 0) { + return root.data; + } + return temp; + } else { + // Go right + return nearestRightKey(root.right, x0); + } + } + } +} + +class NRKTree { + + public NRKTree left; + public NRKTree right; + public int data; + + NRKTree(int x) { + this.left = null; + this.right = null; + this.data = x; + } + + NRKTree(NRKTree right, NRKTree left, int x) { + this.left = left; + this.right = right; + this.data = x; + } + + public NRKTree insertKey(NRKTree current, int value) { + if (current == null) { + return new NRKTree(value); + } + + if (value < current.data) { + current.left = insertKey(current.left, value); + } else if (value > current.data) { + current.right = insertKey(current.right, value); + } + + return current; + } +} diff --git a/src/main/java/com/thealgorithms/devutils/entities/ProcessDetails.java b/src/main/java/com/thealgorithms/devutils/entities/ProcessDetails.java new file mode 100644 index 000000000000..b8b863eda3fc --- /dev/null +++ b/src/main/java/com/thealgorithms/devutils/entities/ProcessDetails.java @@ -0,0 +1,67 @@ +package com.thealgorithms.devutils.entities; + +public class ProcessDetails { + private String processId; + private int arrivalTime; + private int burstTime; + private int waitingTime; + private int turnAroundTime; + private int priority; + + public ProcessDetails(final String processId, final int arrivalTime, final int burstTime, int priority) { + this.processId = processId; + this.arrivalTime = arrivalTime; + this.burstTime = burstTime; + this.priority = priority; + } + + public ProcessDetails(final String processId, final int arrivalTime, final int burstTime) { + this.processId = processId; + this.arrivalTime = arrivalTime; + this.burstTime = burstTime; + } + + public String getProcessId() { + return processId; + } + + public int getArrivalTime() { + return arrivalTime; + } + + public int getBurstTime() { + return burstTime; + } + + public int getWaitingTime() { + return waitingTime; + } + + public int getTurnAroundTimeTime() { + return turnAroundTime; + } + + public int getPriority() { + return priority; + } + + public void setProcessId(final String processId) { + this.processId = processId; + } + + public void setArrivalTime(final int arrivalTime) { + this.arrivalTime = arrivalTime; + } + + public void setBurstTime(final int burstTime) { + this.burstTime = burstTime; + } + + public void setWaitingTime(final int waitingTime) { + this.waitingTime = waitingTime; + } + + public void setTurnAroundTimeTime(final int turnAroundTime) { + this.turnAroundTime = turnAroundTime; + } +} diff --git a/src/main/java/com/thealgorithms/devutils/nodes/LargeTreeNode.java b/src/main/java/com/thealgorithms/devutils/nodes/LargeTreeNode.java new file mode 100644 index 000000000000..95d53ecb1f7a --- /dev/null +++ b/src/main/java/com/thealgorithms/devutils/nodes/LargeTreeNode.java @@ -0,0 +1,77 @@ +package com.thealgorithms.devutils.nodes; + +import java.util.Collection; + +/** + * {@link TreeNode} extension that holds a {@link Collection} of refrences to + * child Nodes. + * + * @param <E> The type of the data held in the Node. + * + * @author <a href="https://github.com/aitorfi">aitorfi</a> + */ +public class LargeTreeNode<E> extends TreeNode<E> { + + /** + * {@link Collection} that holds the Nodes' child nodes. + */ + private Collection<LargeTreeNode<E>> childNodes; + + /** + * Empty contructor. + */ + public LargeTreeNode() { + super(); + } + + /** + * Initializes the Nodes' data. + * + * @param data Value to which data will be initialized. + * @see TreeNode#TreeNode(Object) + */ + public LargeTreeNode(E data) { + super(data); + } + + /** + * Initializes the Nodes' data and parent node reference. + * + * @param data Value to which data will be initialized. + * @param parentNode Value to which the nodes' parent reference will be set. + * @see TreeNode#TreeNode(Object, Node) + */ + public LargeTreeNode(E data, LargeTreeNode<E> parentNode) { + super(data, parentNode); + } + + /** + * Initializes the Nodes' data and parent and child nodes references. + * + * @param data Value to which data will be initialized. + * @param parentNode Value to which the nodes' parent reference will be set. + * @param childNodes {@link Collection} of child Nodes. + * @see TreeNode#TreeNode(Object, Node) + */ + public LargeTreeNode(E data, LargeTreeNode<E> parentNode, Collection<LargeTreeNode<E>> childNodes) { + super(data, parentNode); + this.childNodes = childNodes; + } + + /** + * @return True if the node is a leaf node, otherwise false. + * @see TreeNode#isLeafNode() + */ + @Override + public boolean isLeafNode() { + return (childNodes == null || childNodes.isEmpty()); + } + + public Collection<LargeTreeNode<E>> getChildNodes() { + return childNodes; + } + + public void setChildNodes(Collection<LargeTreeNode<E>> childNodes) { + this.childNodes = childNodes; + } +} diff --git a/src/main/java/com/thealgorithms/devutils/nodes/Node.java b/src/main/java/com/thealgorithms/devutils/nodes/Node.java new file mode 100644 index 000000000000..a10817830962 --- /dev/null +++ b/src/main/java/com/thealgorithms/devutils/nodes/Node.java @@ -0,0 +1,42 @@ +package com.thealgorithms.devutils.nodes; + +/** + * Base class for any node implementation which contains a generic type + * variable. + * + * All known subclasses: {@link TreeNode}, {@link SimpleNode}. + * + * @param <E> The type of the data held in the Node. + * + * @author <a href="https://github.com/aitorfi">aitorfi</a> + */ +public abstract class Node<E> { + + /** + * Generic type data stored in the Node. + */ + private E data; + + /** + * Empty constructor. + */ + public Node() { + } + + /** + * Initializes the Nodes' data. + * + * @param data Value to which data will be initialized. + */ + public Node(E data) { + this.data = data; + } + + public E getData() { + return data; + } + + public void setData(E data) { + this.data = data; + } +} diff --git a/src/main/java/com/thealgorithms/devutils/nodes/SimpleNode.java b/src/main/java/com/thealgorithms/devutils/nodes/SimpleNode.java new file mode 100644 index 000000000000..769ffc2a9a96 --- /dev/null +++ b/src/main/java/com/thealgorithms/devutils/nodes/SimpleNode.java @@ -0,0 +1,59 @@ +package com.thealgorithms.devutils.nodes; + +/** + * Simple Node implementation that holds a reference to the next Node. + * + * @param <E> The type of the data held in the Node. + * + * @author <a href="https://github.com/aitorfi">aitorfi</a> + */ +public class SimpleNode<E> extends Node<E> { + + /** + * Reference to the next Node. + */ + private SimpleNode<E> nextNode; + + /** + * Empty contructor. + */ + public SimpleNode() { + super(); + } + + /** + * Initializes the Nodes' data. + * + * @param data Value to which data will be initialized. + * @see Node#Node(Object) + */ + public SimpleNode(E data) { + super(data); + } + + /** + * Initializes the Nodes' data and next node reference. + * + * @param data Value to which data will be initialized. + * @param nextNode Value to which the next node reference will be set. + */ + public SimpleNode(E data, SimpleNode<E> nextNode) { + super(data); + this.nextNode = nextNode; + } + + /** + * @return True if there is a next node, otherwise false. + */ + public boolean hasNext() { + return (nextNode != null); + } + + public SimpleNode<E> getNextNode() { + return nextNode; + } + + public void setNextNode(SimpleNode<E> nextNode) { + this.nextNode = nextNode; + } +} diff --git a/src/main/java/com/thealgorithms/devutils/nodes/SimpleTreeNode.java b/src/main/java/com/thealgorithms/devutils/nodes/SimpleTreeNode.java new file mode 100644 index 000000000000..215f01a6ef59 --- /dev/null +++ b/src/main/java/com/thealgorithms/devutils/nodes/SimpleTreeNode.java @@ -0,0 +1,90 @@ +package com.thealgorithms.devutils.nodes; + +/** + * Simple TreeNode extension that holds references to two child Nodes (left and + * right). + * + * @param <E> The type of the data held in the Node. + * + * @author <a href="https://github.com/aitorfi">aitorfi</a> + */ +public class SimpleTreeNode<E> extends TreeNode<E> { + + /** + * Refrence to the child Node on the left. + */ + private SimpleTreeNode<E> leftNode; + /** + * Refrence to the child Node on the right. + */ + private SimpleTreeNode<E> rightNode; + + /** + * Empty contructor. + */ + public SimpleTreeNode() { + super(); + } + + /** + * Initializes the Nodes' data. + * + * @param data Value to which data will be initialized. + * @see TreeNode#TreeNode(Object) + */ + public SimpleTreeNode(E data) { + super(data); + } + + /** + * Initializes the Nodes' data and parent node reference. + * + * @param data Value to which data will be initialized. + * @param parentNode Value to which the nodes' parent reference will be set. + * @see TreeNode#TreeNode(Object, Node) + */ + public SimpleTreeNode(E data, SimpleTreeNode<E> parentNode) { + super(data, parentNode); + } + + /** + * Initializes the Nodes' data and parent and child nodes references. + * + * @param data Value to which data will be initialized. + * @param parentNode Value to which the nodes' parent reference will be set. + * @param leftNode Value to which the nodes' left child reference will be + * set. + * @param rightNode Value to which the nodes' right child reference will be + * set. + */ + public SimpleTreeNode(E data, SimpleTreeNode<E> parentNode, SimpleTreeNode<E> leftNode, SimpleTreeNode<E> rightNode) { + super(data, parentNode); + this.leftNode = leftNode; + this.rightNode = rightNode; + } + + /** + * @return True if the node is a leaf node, otherwise false. + * @see TreeNode#isLeafNode() + */ + @Override + public boolean isLeafNode() { + return (leftNode == null && rightNode == null); + } + + public SimpleTreeNode<E> getLeftNode() { + return leftNode; + } + + public void setLeftNode(SimpleTreeNode<E> leftNode) { + this.leftNode = leftNode; + } + + public SimpleTreeNode<E> getRightNode() { + return rightNode; + } + + public void setRightNode(SimpleTreeNode<E> rightNode) { + this.rightNode = rightNode; + } +} diff --git a/src/main/java/com/thealgorithms/devutils/nodes/TreeNode.java b/src/main/java/com/thealgorithms/devutils/nodes/TreeNode.java new file mode 100644 index 000000000000..13c9212306c1 --- /dev/null +++ b/src/main/java/com/thealgorithms/devutils/nodes/TreeNode.java @@ -0,0 +1,78 @@ +package com.thealgorithms.devutils.nodes; + +/** + * Base class for any tree node which holds a reference to the parent node. + * + * All known subclasses: {@link SimpleTreeNode}, {@link LargeTreeNode}. + * + * @param <E> The type of the data held in the Node. + * + * @author <a href="https://github.com/aitorfi">aitorfi</a> + */ +public abstract class TreeNode<E> extends Node<E> { + + /** + * Refernce to the parent Node. + */ + private TreeNode<E> parentNode; + /** + * Indicates the depth at which this node is in the tree. + */ + private int depth; + + /** + * Empty contructor. + */ + public TreeNode() { + super(); + depth = 0; + } + + /** + * Initializes the Nodes' data. + * + * @param data Value to which data will be initialized. + * @see Node#Node(Object) + */ + public TreeNode(E data) { + super(data); + depth = 0; + } + + /** + * Initializes the Nodes' data and parent node reference. + * + * @param data Value to which data will be initialized. + * @param parentNode Value to which the nodes' parent reference will be set. + */ + public TreeNode(E data, TreeNode<E> parentNode) { + super(data); + this.parentNode = parentNode; + depth = this.parentNode.getDepth() + 1; + } + + /** + * @return True if the node is a leaf node, otherwise false. + */ + public abstract boolean isLeafNode(); + + /** + * @return True if the node is the root node, otherwise false. + */ + public boolean isRootNode() { + return (parentNode == null); + } + + public TreeNode<E> getParent() { + return parentNode; + } + + public void setParent(TreeNode<E> parentNode) { + this.parentNode = parentNode; + depth = this.parentNode.getDepth() + 1; + } + + public int getDepth() { + return depth; + } +} diff --git a/src/main/java/com/thealgorithms/devutils/searches/MatrixSearchAlgorithm.java b/src/main/java/com/thealgorithms/devutils/searches/MatrixSearchAlgorithm.java new file mode 100644 index 000000000000..36587a21c863 --- /dev/null +++ b/src/main/java/com/thealgorithms/devutils/searches/MatrixSearchAlgorithm.java @@ -0,0 +1,16 @@ +package com.thealgorithms.devutils.searches; + +/** + * The common interface of most searching algorithms that search in matrixes. + * + * @author Aitor Fidalgo (https://github.com/aitorfi) + */ +public interface MatrixSearchAlgorithm { + /** + * @param key is an element which should be found + * @param matrix is a matrix where the element should be found + * @param <T> Comparable type + * @return array containing the first found coordinates of the element + */ + <T extends Comparable<T>> int[] find(T[][] matrix, T key); +} diff --git a/src/main/java/com/thealgorithms/devutils/searches/SearchAlgorithm.java b/src/main/java/com/thealgorithms/devutils/searches/SearchAlgorithm.java new file mode 100644 index 000000000000..eb5b42756958 --- /dev/null +++ b/src/main/java/com/thealgorithms/devutils/searches/SearchAlgorithm.java @@ -0,0 +1,16 @@ +package com.thealgorithms.devutils.searches; + +/** + * The common interface of most searching algorithms + * + * @author Podshivalov Nikita (https://github.com/nikitap492) + */ +public interface SearchAlgorithm { + /** + * @param key is an element which should be found + * @param array is an array where the element should be found + * @param <T> Comparable type + * @return first found index of the element + */ + <T extends Comparable<T>> int find(T[] array, T key); +} diff --git a/src/main/java/com/thealgorithms/divideandconquer/BinaryExponentiation.java b/src/main/java/com/thealgorithms/divideandconquer/BinaryExponentiation.java new file mode 100644 index 000000000000..de829585891a --- /dev/null +++ b/src/main/java/com/thealgorithms/divideandconquer/BinaryExponentiation.java @@ -0,0 +1,42 @@ +package com.thealgorithms.divideandconquer; + +// Java Program to Implement Binary Exponentiation (power in log n) + +// Reference Link: https://en.wikipedia.org/wiki/Exponentiation_by_squaring + +/* + * Binary Exponentiation is a method to calculate a to the power of b. + * It is used to calculate a^n in O(log n) time. + * + * Reference: + * https://iq.opengenus.org/binary-exponentiation/ + */ + +public class BinaryExponentiation { + + // recursive function to calculate a to the power of b + public static long calculatePower(long x, long y) { + // Base Case + if (y == 0) { + return 1; + } + if (y % 2 == 1) { // odd power + return x * calculatePower(x, y - 1); + } + return calculatePower(x * x, y / 2); // even power + } + + // iterative function to calculate a to the power of b + long power(long n, long m) { + long power = n; + long sum = 1; + while (m > 0) { + if ((m & 1) == 1) { + sum *= power; + } + power = power * power; + m = m >> 1; + } + return sum; + } +} diff --git a/src/main/java/com/thealgorithms/divideandconquer/ClosestPair.java b/src/main/java/com/thealgorithms/divideandconquer/ClosestPair.java new file mode 100644 index 000000000000..cd26f9213651 --- /dev/null +++ b/src/main/java/com/thealgorithms/divideandconquer/ClosestPair.java @@ -0,0 +1,342 @@ +package com.thealgorithms.divideandconquer; + +/** + * For a set of points in a coordinates system (10000 maximum), ClosestPair + * class calculates the two closest points. + */ +public final class ClosestPair { + + /** + * Number of points + */ + int numberPoints; + /** + * Input data, maximum 10000. + */ + Location[] array; + /** + * Minimum point coordinate. + */ + Location point1 = null; + /** + * Minimum point coordinate. + */ + Location point2 = null; + /** + * Minimum point length. + */ + private static double minNum = Double.MAX_VALUE; + + public static void setMinNum(double minNum) { + ClosestPair.minNum = minNum; + } + + public static void setSecondCount(int secondCount) { + ClosestPair.secondCount = secondCount; + } + + /** + * secondCount + */ + private static int secondCount = 0; + + /** + * Constructor. + */ + ClosestPair(int points) { + numberPoints = points; + array = new Location[numberPoints]; + } + + /** + * Location class is an auxiliary type to keep points coordinates. + */ + public static class Location { + + double x; + double y; + + /** + * @param xpar (IN Parameter) x coordinate <br> + * @param ypar (IN Parameter) y coordinate <br> + */ + Location(final double xpar, final double ypar) { // Save x, y coordinates + this.x = xpar; + this.y = ypar; + } + } + + public Location[] createLocation(int numberValues) { + return new Location[numberValues]; + } + + public Location buildLocation(double x, double y) { + return new Location(x, y); + } + + /** + * xPartition function: arrange x-axis. + * + * @param a (IN Parameter) array of points <br> + * @param first (IN Parameter) first point <br> + * @param last (IN Parameter) last point <br> + * @return pivot index + */ + public int xPartition(final Location[] a, final int first, final int last) { + Location pivot = a[last]; // pivot + int i = first - 1; + Location temp; // Temporarily store value for position transformation + for (int j = first; j <= last - 1; j++) { + if (a[j].x <= pivot.x) { // Less than or less than pivot + i++; + temp = a[i]; // array[i] <-> array[j] + a[i] = a[j]; + a[j] = temp; + } + } + i++; + temp = a[i]; // array[pivot] <-> array[i] + a[i] = a[last]; + a[last] = temp; + return i; // pivot index + } + + /** + * yPartition function: arrange y-axis. + * + * @param a (IN Parameter) array of points <br> + * @param first (IN Parameter) first point <br> + * @param last (IN Parameter) last point <br> + * @return pivot index + */ + public int yPartition(final Location[] a, final int first, final int last) { + Location pivot = a[last]; // pivot + int i = first - 1; + Location temp; // Temporarily store value for position transformation + for (int j = first; j <= last - 1; j++) { + if (a[j].y <= pivot.y) { // Less than or less than pivot + i++; + temp = a[i]; // array[i] <-> array[j] + a[i] = a[j]; + a[j] = temp; + } + } + i++; + temp = a[i]; // array[pivot] <-> array[i] + a[i] = a[last]; + a[last] = temp; + return i; // pivot index + } + + /** + * xQuickSort function: //x-axis Quick Sorting. + * + * @param a (IN Parameter) array of points <br> + * @param first (IN Parameter) first point <br> + * @param last (IN Parameter) last point <br> + */ + public void xQuickSort(final Location[] a, final int first, final int last) { + if (first < last) { + int q = xPartition(a, first, last); // pivot + xQuickSort(a, first, q - 1); // Left + xQuickSort(a, q + 1, last); // Right + } + } + + /** + * yQuickSort function: //y-axis Quick Sorting. + * + * @param a (IN Parameter) array of points <br> + * @param first (IN Parameter) first point <br> + * @param last (IN Parameter) last point <br> + */ + public void yQuickSort(final Location[] a, final int first, final int last) { + if (first < last) { + int q = yPartition(a, first, last); // pivot + yQuickSort(a, first, q - 1); // Left + yQuickSort(a, q + 1, last); // Right + } + } + + /** + * closestPair function: find closest pair. + * + * @param a (IN Parameter) array stored before divide <br> + * @param indexNum (IN Parameter) number coordinates divideArray <br> + * @return minimum distance <br> + */ + public double closestPair(final Location[] a, final int indexNum) { + Location[] divideArray = new Location[indexNum]; + System.arraycopy(a, 0, divideArray, 0, indexNum); // Copy previous array + int divideX = indexNum / 2; // Intermediate value for divide + Location[] leftArray = new Location[divideX]; // divide - left array + // divide-right array + Location[] rightArray = new Location[indexNum - divideX]; + if (indexNum <= 3) { // If the number of coordinates is 3 or less + return bruteForce(divideArray); + } + // divide-left array + System.arraycopy(divideArray, 0, leftArray, 0, divideX); + // divide-right array + System.arraycopy(divideArray, divideX, rightArray, 0, indexNum - divideX); + + double minLeftArea; // Minimum length of left array + double minRightArea; // Minimum length of right array + double minValue; // Minimum lengt + + minLeftArea = closestPair(leftArray, divideX); // recursive closestPair + minRightArea = closestPair(rightArray, indexNum - divideX); + // window size (= minimum length) + minValue = Math.min(minLeftArea, minRightArea); + + // Create window. Set the size for creating a window + // and creating a new array for the coordinates in the window + for (int i = 0; i < indexNum; i++) { + double xGap = Math.abs(divideArray[divideX].x - divideArray[i].x); + if (xGap < minValue) { + ClosestPair.setSecondCount(secondCount + 1); // size of the array + } else { + if (divideArray[i].x > divideArray[divideX].x) { + break; + } + } + } + // new array for coordinates in window + Location[] firstWindow = new Location[secondCount]; + int k = 0; + for (int i = 0; i < indexNum; i++) { + double xGap = Math.abs(divideArray[divideX].x - divideArray[i].x); + if (xGap < minValue) { // if it's inside a window + firstWindow[k] = divideArray[i]; // put in an array + k++; + } else { + if (divideArray[i].x > divideArray[divideX].x) { + break; + } + } + } + yQuickSort(firstWindow, 0, secondCount - 1); // Sort by y coordinates + /* Coordinates in Window */ + double length; + // size comparison within window + for (int i = 0; i < secondCount - 1; i++) { + for (int j = (i + 1); j < secondCount; j++) { + double xGap = Math.abs(firstWindow[i].x - firstWindow[j].x); + double yGap = Math.abs(firstWindow[i].y - firstWindow[j].y); + if (yGap < minValue) { + length = Math.sqrt(Math.pow(xGap, 2) + Math.pow(yGap, 2)); + // If measured distance is less than current min distance + if (length < minValue) { + // Change minimum distance to current distance + minValue = length; + // Conditional for registering final coordinate + if (length < minNum) { + ClosestPair.setMinNum(length); + point1 = firstWindow[i]; + point2 = firstWindow[j]; + } + } + } else { + break; + } + } + } + ClosestPair.setSecondCount(0); + return minValue; + } + + /** + * bruteForce function: When the number of coordinates is less than 3. + * + * @param arrayParam (IN Parameter) array stored before divide <br> + * @return <br> + */ + public double bruteForce(final Location[] arrayParam) { + double minValue = Double.MAX_VALUE; // minimum distance + double length; + double xGap; // Difference between x coordinates + double yGap; // Difference between y coordinates + double result = 0; + + if (arrayParam.length == 2) { + // Difference between x coordinates + xGap = (arrayParam[0].x - arrayParam[1].x); + // Difference between y coordinates + yGap = (arrayParam[0].y - arrayParam[1].y); + // distance between coordinates + length = Math.sqrt(Math.pow(xGap, 2) + Math.pow(yGap, 2)); + // Conditional statement for registering final coordinate + if (length < minNum) { + ClosestPair.setMinNum(length); + } + point1 = arrayParam[0]; + point2 = arrayParam[1]; + result = length; + } + if (arrayParam.length == 3) { + for (int i = 0; i < arrayParam.length - 1; i++) { + for (int j = (i + 1); j < arrayParam.length; j++) { + // Difference between x coordinates + xGap = (arrayParam[i].x - arrayParam[j].x); + // Difference between y coordinates + yGap = (arrayParam[i].y - arrayParam[j].y); + // distance between coordinates + length = Math.sqrt(Math.pow(xGap, 2) + Math.pow(yGap, 2)); + // If measured distance is less than current min distance + if (length < minValue) { + // Change minimum distance to current distance + minValue = length; + if (length < minNum) { + // Registering final coordinate + ClosestPair.setMinNum(length); + point1 = arrayParam[i]; + point2 = arrayParam[j]; + } + } + } + } + result = minValue; + } + return result; // If only one point returns 0. + } + + /** + * main function: execute class. + * + * @param args (IN Parameter) <br> + */ + public static void main(final String[] args) { + // Input data consists of one x-coordinate and one y-coordinate + ClosestPair cp = new ClosestPair(12); + cp.array[0] = cp.buildLocation(2, 3); + cp.array[1] = cp.buildLocation(2, 16); + cp.array[2] = cp.buildLocation(3, 9); + cp.array[3] = cp.buildLocation(6, 3); + cp.array[4] = cp.buildLocation(7, 7); + cp.array[5] = cp.buildLocation(19, 4); + cp.array[6] = cp.buildLocation(10, 11); + cp.array[7] = cp.buildLocation(15, 2); + cp.array[8] = cp.buildLocation(15, 19); + cp.array[9] = cp.buildLocation(16, 11); + cp.array[10] = cp.buildLocation(17, 13); + cp.array[11] = cp.buildLocation(9, 12); + + System.out.println("Input data"); + System.out.println("Number of points: " + cp.array.length); + for (int i = 0; i < cp.array.length; i++) { + System.out.println("x: " + cp.array[i].x + ", y: " + cp.array[i].y); + } + + cp.xQuickSort(cp.array, 0, cp.array.length - 1); // Sorting by x value + + double result; // minimum distance + + result = cp.closestPair(cp.array, cp.array.length); + // ClosestPair start + // minimum distance coordinates and distance output + System.out.println("Output Data"); + System.out.println("(" + cp.point1.x + ", " + cp.point1.y + ")"); + System.out.println("(" + cp.point2.x + ", " + cp.point2.y + ")"); + System.out.println("Minimum Distance : " + result); + } +} diff --git a/src/main/java/com/thealgorithms/divideandconquer/CountingInversions.java b/src/main/java/com/thealgorithms/divideandconquer/CountingInversions.java new file mode 100644 index 000000000000..15c8bc33b58f --- /dev/null +++ b/src/main/java/com/thealgorithms/divideandconquer/CountingInversions.java @@ -0,0 +1,101 @@ +package com.thealgorithms.divideandconquer; + +/** + * A utility class for counting the number of inversions in an array. + * <p> + * An inversion is a pair (i, j) such that i < j and arr[i] > arr[j]. + * This class implements a divide-and-conquer approach, similar to merge sort, + * to count the number of inversions efficiently. + * <p> + * Time Complexity: O(n log n) + * Space Complexity: O(n) (due to temporary arrays during merge step) + * + * <p>Applications: + * - Used in algorithms related to sorting and permutation analysis. + * - Helps in determining how far an array is from being sorted. + * - Applicable in bioinformatics and signal processing. + * + * <p>This class cannot be instantiated, as it is intended to provide + * only static utility methods. + * + * @author Hardvan + */ +public final class CountingInversions { + private CountingInversions() { + } + + /** + * Counts the number of inversions in the given array. + * + * @param arr The input array of integers. + * @return The total number of inversions in the array. + */ + public static int countInversions(int[] arr) { + return mergeSortAndCount(arr, 0, arr.length - 1); + } + + /** + * Recursively divides the array into two halves, sorts them, and counts + * the number of inversions. Uses a modified merge sort approach. + * + * @param arr The input array. + * @param left The starting index of the current segment. + * @param right The ending index of the current segment. + * @return The number of inversions within the segment [left, right]. + */ + private static int mergeSortAndCount(int[] arr, int left, int right) { + if (left >= right) { + return 0; + } + + int mid = left + (right - left) / 2; + int inversions = 0; + + inversions += mergeSortAndCount(arr, left, mid); + inversions += mergeSortAndCount(arr, mid + 1, right); + inversions += mergeAndCount(arr, left, mid, right); + return inversions; + } + + /** + * Merges two sorted subarrays and counts the cross-inversions between them. + * A cross-inversion occurs when an element from the right subarray is + * smaller than an element from the left subarray. + * + * @param arr The input array. + * @param left The starting index of the first subarray. + * @param mid The ending index of the first subarray and midpoint of the segment. + * @param right The ending index of the second subarray. + * @return The number of cross-inversions between the two subarrays. + */ + private static int mergeAndCount(int[] arr, int left, int mid, int right) { + int[] leftArr = new int[mid - left + 1]; + int[] rightArr = new int[right - mid]; + + System.arraycopy(arr, left, leftArr, 0, mid - left + 1); + System.arraycopy(arr, mid + 1, rightArr, 0, right - mid); + + int i = 0; + int j = 0; + int k = left; + int inversions = 0; + + while (i < leftArr.length && j < rightArr.length) { + if (leftArr[i] <= rightArr[j]) { + arr[k++] = leftArr[i++]; + } else { + arr[k++] = rightArr[j++]; + inversions += mid + 1 - left - i; + } + } + + while (i < leftArr.length) { + arr[k++] = leftArr[i++]; + } + while (j < rightArr.length) { + arr[k++] = rightArr[j++]; + } + + return inversions; + } +} diff --git a/src/main/java/com/thealgorithms/divideandconquer/MedianOfTwoSortedArrays.java b/src/main/java/com/thealgorithms/divideandconquer/MedianOfTwoSortedArrays.java new file mode 100644 index 000000000000..d9e51442253c --- /dev/null +++ b/src/main/java/com/thealgorithms/divideandconquer/MedianOfTwoSortedArrays.java @@ -0,0 +1,53 @@ +package com.thealgorithms.divideandconquer; + +public final class MedianOfTwoSortedArrays { + + private MedianOfTwoSortedArrays() { + } + + /** + * Finds the median of two sorted arrays in logarithmic time. + * + * @param nums1 the first sorted array + * @param nums2 the second sorted array + * @return the median of the combined sorted array + * @throws IllegalArgumentException if the input arrays are not sorted + */ + public static double findMedianSortedArrays(int[] nums1, int[] nums2) { + if (nums1.length > nums2.length) { + return findMedianSortedArrays(nums2, nums1); // Ensure nums1 is the smaller array + } + + int m = nums1.length; + int n = nums2.length; + int low = 0; + int high = m; + while (low <= high) { + int partition1 = (low + high) / 2; // Partition in the first array + int partition2 = (m + n + 1) / 2 - partition1; // Partition in the second array + + int maxLeft1 = (partition1 == 0) ? Integer.MIN_VALUE : nums1[partition1 - 1]; + int minRight1 = (partition1 == m) ? Integer.MAX_VALUE : nums1[partition1]; + int maxLeft2 = (partition2 == 0) ? Integer.MIN_VALUE : nums2[partition2 - 1]; + int minRight2 = (partition2 == n) ? Integer.MAX_VALUE : nums2[partition2]; + + // Check if partition is valid + if (maxLeft1 <= minRight2 && maxLeft2 <= minRight1) { + // If combined array length is odd + if (((m + n) & 1) == 1) { + return Math.max(maxLeft1, maxLeft2); + } + // If combined array length is even + else { + return (Math.max(maxLeft1, maxLeft2) + Math.min(minRight1, minRight2)) / 2.0; + } + } else if (maxLeft1 > minRight2) { + high = partition1 - 1; + } else { + low = partition1 + 1; + } + } + + throw new IllegalArgumentException("Input arrays are not sorted"); + } +} diff --git a/src/main/java/com/thealgorithms/divideandconquer/SkylineAlgorithm.java b/src/main/java/com/thealgorithms/divideandconquer/SkylineAlgorithm.java new file mode 100644 index 000000000000..610b1b78a36a --- /dev/null +++ b/src/main/java/com/thealgorithms/divideandconquer/SkylineAlgorithm.java @@ -0,0 +1,184 @@ +package com.thealgorithms.divideandconquer; + +import java.util.ArrayList; +import java.util.Comparator; + +/** + * @author dimgrichr + * <p> + * Space complexity: O(n) Time complexity: O(nlogn), because it is a divide and + * conquer algorithm + */ +public class SkylineAlgorithm { + + private ArrayList<Point> points; + + /** + * Main constructor of the application. ArrayList points gets created, which + * represents the sum of all edges. + */ + public SkylineAlgorithm() { + points = new ArrayList<>(); + } + + /** + * @return points, the ArrayList that includes all points. + */ + public ArrayList<Point> getPoints() { + return points; + } + + /** + * The main divide and conquer, and also recursive algorithm. It gets an + * ArrayList full of points as an argument. If the size of that ArrayList is + * 1 or 2, the ArrayList is returned as it is, or with one less point (if + * the initial size is 2 and one of it's points, is dominated by the other + * one). On the other hand, if the ArrayList's size is bigger than 2, the + * function is called again, twice, with arguments the corresponding half of + * the initial ArrayList each time. Once the flashback has ended, the + * function produceFinalSkyLine gets called, in order to produce the final + * skyline, and return it. + * + * @param list, the initial list of points + * @return leftSkyLine, the combination of first half's and second half's + * skyline + * @see Point + */ + public ArrayList<Point> produceSubSkyLines(ArrayList<Point> list) { + // part where function exits flashback + int size = list.size(); + if (size == 1) { + return list; + } else if (size == 2) { + if (list.get(0).dominates(list.get(1))) { + list.remove(1); + } else { + if (list.get(1).dominates(list.get(0))) { + list.remove(0); + } + } + return list; + } + + // recursive part of the function + ArrayList<Point> leftHalf = new ArrayList<>(); + ArrayList<Point> rightHalf = new ArrayList<>(); + for (int i = 0; i < list.size(); i++) { + if (i < list.size() / 2) { + leftHalf.add(list.get(i)); + } else { + rightHalf.add(list.get(i)); + } + } + ArrayList<Point> leftSubSkyLine = produceSubSkyLines(leftHalf); + ArrayList<Point> rightSubSkyLine = produceSubSkyLines(rightHalf); + + // skyline is produced + return produceFinalSkyLine(leftSubSkyLine, rightSubSkyLine); + } + + /** + * The first half's skyline gets cleared from some points that are not part + * of the final skyline (Points with same x-value and different y=values. + * The point with the smallest y-value is kept). Then, the minimum y-value + * of the points of first half's skyline is found. That helps us to clear + * the second half's skyline, because, the points of second half's skyline + * that have greater y-value of the minimum y-value that we found before, + * are dominated, so they are not part of the final skyline. Finally, the + * "cleaned" first half's and second half's skylines, are combined, + * producing the final skyline, which is returned. + * + * @param left the skyline of the left part of points + * @param right the skyline of the right part of points + * @return left the final skyline + */ + public ArrayList<Point> produceFinalSkyLine(ArrayList<Point> left, ArrayList<Point> right) { + // dominated points of ArrayList left are removed + for (int i = 0; i < left.size() - 1; i++) { + if (left.get(i).x == left.get(i + 1).x && left.get(i).y > left.get(i + 1).y) { + left.remove(i); + i--; + } + } + + // minimum y-value is found + int min = left.get(0).y; + for (int i = 1; i < left.size(); i++) { + if (min > left.get(i).y) { + min = left.get(i).y; + if (min == 1) { + i = left.size(); + } + } + } + + // dominated points of ArrayList right are removed + for (int i = 0; i < right.size(); i++) { + if (right.get(i).y >= min) { + right.remove(i); + i--; + } + } + + // final skyline found and returned + left.addAll(right); + return left; + } + + public static class Point { + + private int x; + private int y; + + /** + * The main constructor of Point Class, used to represent the 2 + * Dimension points. + * + * @param x the point's x-value. + * @param y the point's y-value. + */ + public Point(int x, int y) { + this.x = x; + this.y = y; + } + + /** + * @return x, the x-value + */ + public int getX() { + return x; + } + + /** + * @return y, the y-value + */ + public int getY() { + return y; + } + + /** + * Based on the skyline theory, it checks if the point that calls the + * function dominates the argument point. + * + * @param p1 the point that is compared + * @return true if the point wich calls the function dominates p1 false + * otherwise. + */ + public boolean dominates(Point p1) { + // checks if p1 is dominated + return ((this.x < p1.x && this.y <= p1.y) || (this.x <= p1.x && this.y < p1.y)); + } + } + + /** + * It is used to compare the 2 Dimension points, based on their x-values, in + * order get sorted later. + */ + class XComparator implements Comparator<Point> { + + @Override + public int compare(Point a, Point b) { + return Integer.compare(a.x, b.x); + } + } +} diff --git a/src/main/java/com/thealgorithms/divideandconquer/StrassenMatrixMultiplication.java b/src/main/java/com/thealgorithms/divideandconquer/StrassenMatrixMultiplication.java new file mode 100644 index 000000000000..86a6f3e11483 --- /dev/null +++ b/src/main/java/com/thealgorithms/divideandconquer/StrassenMatrixMultiplication.java @@ -0,0 +1,142 @@ +package com.thealgorithms.divideandconquer; + +// Java Program to Implement Strassen Algorithm for Matrix Multiplication + +/* + * Uses the divide and conquer approach to multiply two matrices. + * Time Complexity: O(n^2.8074) better than the O(n^3) of the standard matrix multiplication + * algorithm. Space Complexity: O(n^2) + * + * This Matrix multiplication can be performed only on square matrices + * where n is a power of 2. Order of both of the matrices are n × n. + * + * Reference: + * https://www.tutorialspoint.com/design_and_analysis_of_algorithms/design_and_analysis_of_algorithms_strassens_matrix_multiplication.htm#:~:text=Strassen's%20Matrix%20multiplication%20can%20be,matrices%20are%20n%20%C3%97%20n. + * https://www.geeksforgeeks.org/strassens-matrix-multiplication/ + */ + +public class StrassenMatrixMultiplication { + + // Function to multiply matrices + public int[][] multiply(int[][] a, int[][] b) { + int n = a.length; + + int[][] mat = new int[n][n]; + + if (n == 1) { + mat[0][0] = a[0][0] * b[0][0]; + } else { + // Dividing Matrix into parts + // by storing sub-parts to variables + int[][] a11 = new int[n / 2][n / 2]; + int[][] a12 = new int[n / 2][n / 2]; + int[][] a21 = new int[n / 2][n / 2]; + int[][] a22 = new int[n / 2][n / 2]; + int[][] b11 = new int[n / 2][n / 2]; + int[][] b12 = new int[n / 2][n / 2]; + int[][] b21 = new int[n / 2][n / 2]; + int[][] b22 = new int[n / 2][n / 2]; + + // Dividing matrix A into 4 parts + split(a, a11, 0, 0); + split(a, a12, 0, n / 2); + split(a, a21, n / 2, 0); + split(a, a22, n / 2, n / 2); + + // Dividing matrix B into 4 parts + split(b, b11, 0, 0); + split(b, b12, 0, n / 2); + split(b, b21, n / 2, 0); + split(b, b22, n / 2, n / 2); + + // Using Formulas as described in algorithm + // m1:=(A1+A3)×(B1+B2) + int[][] m1 = multiply(add(a11, a22), add(b11, b22)); + + // m2:=(A2+A4)×(B3+B4) + int[][] m2 = multiply(add(a21, a22), b11); + + // m3:=(A1−A4)×(B1+A4) + int[][] m3 = multiply(a11, sub(b12, b22)); + + // m4:=A1×(B2−B4) + int[][] m4 = multiply(a22, sub(b21, b11)); + + // m5:=(A3+A4)×(B1) + int[][] m5 = multiply(add(a11, a12), b22); + + // m6:=(A1+A2)×(B4) + int[][] m6 = multiply(sub(a21, a11), add(b11, b12)); + + // m7:=A4×(B3−B1) + int[][] m7 = multiply(sub(a12, a22), add(b21, b22)); + + // P:=m2+m3−m6−m7 + int[][] c11 = add(sub(add(m1, m4), m5), m7); + + // Q:=m4+m6 + int[][] c12 = add(m3, m5); + + // mat:=m5+m7 + int[][] c21 = add(m2, m4); + + // S:=m1−m3−m4−m5 + int[][] c22 = add(sub(add(m1, m3), m2), m6); + + join(c11, mat, 0, 0); + join(c12, mat, 0, n / 2); + join(c21, mat, n / 2, 0); + join(c22, mat, n / 2, n / 2); + } + + return mat; + } + + // Function to subtract two matrices + public int[][] sub(int[][] a, int[][] b) { + int n = a.length; + + int[][] c = new int[n][n]; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + c[i][j] = a[i][j] - b[i][j]; + } + } + + return c; + } + + // Function to add two matrices + public int[][] add(int[][] a, int[][] b) { + int n = a.length; + + int[][] c = new int[n][n]; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + c[i][j] = a[i][j] + b[i][j]; + } + } + + return c; + } + + // Function to split parent matrix into child matrices + public void split(int[][] p, int[][] c, int iB, int jB) { + for (int i1 = 0, i2 = iB; i1 < c.length; i1++, i2++) { + for (int j1 = 0, j2 = jB; j1 < c.length; j1++, j2++) { + c[i1][j1] = p[i2][j2]; + } + } + } + + // Function to join child matrices into (to) parent matrix + public void join(int[][] c, int[][] p, int iB, int jB) { + for (int i1 = 0, i2 = iB; i1 < c.length; i1++, i2++) { + for (int j1 = 0, j2 = jB; j1 < c.length; j1++, j2++) { + p[i2][j2] = c[i1][j1]; + } + } + } +} diff --git a/src/main/java/com/thealgorithms/divideandconquer/TilingProblem.java b/src/main/java/com/thealgorithms/divideandconquer/TilingProblem.java new file mode 100644 index 000000000000..ff2e1678ab48 --- /dev/null +++ b/src/main/java/com/thealgorithms/divideandconquer/TilingProblem.java @@ -0,0 +1,99 @@ +package com.thealgorithms.divideandconquer; + +/** + * This class provides a solution to the Tiling Problem using divide-and-conquer. + * <p> + * The Tiling Problem involves filling a 2^n x 2^n board with a single missing + * square using L-shaped tiles (each tile covers exactly three squares). + * The algorithm recursively divides the board into four quadrants, places an + * L-shaped tile in the appropriate quadrant, and fills the remaining areas. + * + * <p>Applications: + * - Used in graphics and image processing. + * - Helpful in solving puzzles and tiling problems in competitive programming. + * + * @author Hardvan + */ +public final class TilingProblem { + private TilingProblem() { + } + + /** + * A counter used to label the L-shaped tiles placed on the board. + */ + private static int tile = 1; + + /** + * A 2D array representing the board to be tiled. + */ + private static int[][] board; + + /** + * Solves the tiling problem for a 2^n x 2^n board with one missing square. + * + * @param size The size of the board (must be a power of 2). + * @param missingRow The row index of the missing square. + * @param missingCol The column index of the missing square. + * @return A 2D array representing the tiled board with L-shaped tiles. + */ + public static int[][] solveTiling(int size, int missingRow, int missingCol) { + board = new int[size][size]; + fillBoard(size, 0, 0, missingRow, missingCol); + return board; + } + + /** + * Recursively fills the board with L-shaped tiles. + * + * <p>The board is divided into four quadrants. Depending on the location of + * the missing square, an L-shaped tile is placed at the center of the board + * to cover three of the four quadrants. The process is then repeated for + * each quadrant until the entire board is filled. + * + * @param size The current size of the sub-board. + * @param row The starting row index of the current sub-board. + * @param col The starting column index of the current sub-board. + * @param missingRow The row index of the missing square within the board. + * @param missingCol The column index of the missing square within the board. + */ + private static void fillBoard(int size, int row, int col, int missingRow, int missingCol) { + if (size == 1) { + return; + } + + int half = size / 2; + int t = tile++; + + // Top-left quadrant + if (missingRow < row + half && missingCol < col + half) { + fillBoard(half, row, col, missingRow, missingCol); + } else { + board[row + half - 1][col + half - 1] = t; + fillBoard(half, row, col, row + half - 1, col + half - 1); + } + + // Top-right quadrant + if (missingRow < row + half && missingCol >= col + half) { + fillBoard(half, row, col + half, missingRow, missingCol); + } else { + board[row + half - 1][col + half] = t; + fillBoard(half, row, col + half, row + half - 1, col + half); + } + + // Bottom-left quadrant + if (missingRow >= row + half && missingCol < col + half) { + fillBoard(half, row + half, col, missingRow, missingCol); + } else { + board[row + half][col + half - 1] = t; + fillBoard(half, row + half, col, row + half, col + half - 1); + } + + // Bottom-right quadrant + if (missingRow >= row + half && missingCol >= col + half) { + fillBoard(half, row + half, col + half, missingRow, missingCol); + } else { + board[row + half][col + half] = t; + fillBoard(half, row + half, col + half, row + half, col + half); + } + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/Abbreviation.java b/src/main/java/com/thealgorithms/dynamicprogramming/Abbreviation.java new file mode 100644 index 000000000000..60c0fd0a3cde --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/Abbreviation.java @@ -0,0 +1,56 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * A class that provides a solution to the abbreviation problem. + * + * Problem: Given two strings, `a` and `b`, determine if string `a` can be + * transformed into string `b` by performing the following operations: + * 1. Capitalize zero or more of `a`'s lowercase letters (i.e., convert them to uppercase). + * 2. Delete any of the remaining lowercase letters from `a`. + * + * The task is to determine whether it is possible to make string `a` equal to string `b`. + * + * @author Hardvan + */ +public final class Abbreviation { + private Abbreviation() { + } + + /** + * Determines if string `a` can be transformed into string `b` by capitalizing + * some of its lowercase letters and deleting the rest. + * + * @param a The input string which may contain both uppercase and lowercase letters. + * @param b The target string containing only uppercase letters. + * @return {@code true} if string `a` can be transformed into string `b`, + * {@code false} otherwise. + * + * Time Complexity: O(n * m) where n = length of string `a` and m = length of string `b`. + * Space Complexity: O(n * m) due to the dynamic programming table. + */ + public static boolean abbr(String a, String b) { + int n = a.length(); + int m = b.length(); + + boolean[][] dp = new boolean[n + 1][m + 1]; + + dp[0][0] = true; + + for (int i = 0; i < n; i++) { + for (int j = 0; j <= m; j++) { + if (dp[i][j]) { + // Case 1: If the current characters match (or can be capitalized to match) + if (j < m && Character.toUpperCase(a.charAt(i)) == b.charAt(j)) { + dp[i + 1][j + 1] = true; + } + // Case 2: If the character in `a` is lowercase, we can skip it + if (Character.isLowerCase(a.charAt(i))) { + dp[i + 1][j] = true; + } + } + } + } + + return dp[n][m]; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/AllConstruct.java b/src/main/java/com/thealgorithms/dynamicprogramming/AllConstruct.java new file mode 100644 index 000000000000..e7712b13a2b7 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/AllConstruct.java @@ -0,0 +1,61 @@ +package com.thealgorithms.dynamicprogramming; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class provides a solution to the "All Construct" problem. + * + * The problem is to determine all the ways a target string can be constructed + * from a given list of substrings. Each substring in the word bank can be used + * multiple times, and the order of substrings matters. + * + * @author Hardvan + */ +public final class AllConstruct { + private AllConstruct() { + } + + /** + * Finds all possible ways to construct the target string using substrings + * from the given word bank. + * Time Complexity: O(n * m * k), where n = length of the target, + * m = number of words in wordBank, and k = average length of a word. + * + * Space Complexity: O(n * m) due to the size of the table storing combinations. + * + * @param target The target string to construct. + * @param wordBank An iterable collection of substrings that can be used to construct the target. + * @return A list of lists, where each inner list represents one possible + * way of constructing the target string using the given word bank. + */ + public static List<List<String>> allConstruct(String target, Iterable<String> wordBank) { + List<List<List<String>>> table = new ArrayList<>(target.length() + 1); + + for (int i = 0; i <= target.length(); i++) { + table.add(new ArrayList<>()); + } + + table.get(0).add(new ArrayList<>()); + + for (int i = 0; i <= target.length(); i++) { + if (!table.get(i).isEmpty()) { + for (String word : wordBank) { + if (i + word.length() <= target.length() && target.substring(i, i + word.length()).equals(word)) { + + List<List<String>> newCombinations = new ArrayList<>(); + for (List<String> combination : table.get(i)) { + List<String> newCombination = new ArrayList<>(combination); + newCombination.add(word); + newCombinations.add(newCombination); + } + + table.get(i + word.length()).addAll(newCombinations); + } + } + } + } + + return table.get(target.length()); + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/AssignmentUsingBitmask.java b/src/main/java/com/thealgorithms/dynamicprogramming/AssignmentUsingBitmask.java new file mode 100644 index 000000000000..5a894ca004b7 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/AssignmentUsingBitmask.java @@ -0,0 +1,91 @@ +package com.thealgorithms.dynamicprogramming; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * The AssignmentUsingBitmask class is used to calculate the total number of ways + * tasks can be distributed among people, given specific constraints on who can perform which tasks. + * The approach uses bitmasking and dynamic programming to efficiently solve the problem. + * + * @author Hardvan + */ +public final class AssignmentUsingBitmask { + + private final int totalTasks; + private final int[][] dp; + private final List<List<Integer>> task; + private final int finalMask; + + /** + * Constructor for the AssignmentUsingBitmask class. + * + * @param taskPerformed a list of lists, where each inner list contains the tasks that a person can perform. + * @param total the total number of tasks. + */ + public AssignmentUsingBitmask(List<List<Integer>> taskPerformed, int total) { + this.totalTasks = total; + this.dp = new int[1 << taskPerformed.size()][total + 1]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + + this.task = new ArrayList<>(totalTasks + 1); + for (int i = 0; i <= totalTasks; i++) { + this.task.add(new ArrayList<>()); + } + + // Final mask to check if all persons are included + this.finalMask = (1 << taskPerformed.size()) - 1; + + // Fill the task list + for (int i = 0; i < taskPerformed.size(); i++) { + for (int j : taskPerformed.get(i)) { + this.task.get(j).add(i); + } + } + } + + /** + * Counts the ways to assign tasks until the given task number with the specified mask. + * + * @param mask the bitmask representing the current state of assignments. + * @param taskNo the current task number being processed. + * @return the number of ways to assign tasks. + */ + private int countWaysUntil(int mask, int taskNo) { + if (mask == finalMask) { + return 1; + } + if (taskNo > totalTasks) { + return 0; + } + if (dp[mask][taskNo] != -1) { + return dp[mask][taskNo]; + } + + int totalWays = countWaysUntil(mask, taskNo + 1); + + // Assign tasks to all possible persons + for (int p : task.get(taskNo)) { + // If the person is already assigned a task + if ((mask & (1 << p)) != 0) { + continue; + } + totalWays += countWaysUntil(mask | (1 << p), taskNo + 1); + } + + dp[mask][taskNo] = totalWays; + return dp[mask][taskNo]; + } + + /** + * Counts the total number of ways to distribute tasks among persons. + * + * @return the total number of ways to distribute tasks. + */ + public int countNoOfWays() { + return countWaysUntil(0, 1); + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/BoardPath.java b/src/main/java/com/thealgorithms/dynamicprogramming/BoardPath.java new file mode 100644 index 000000000000..cd761f96019c --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/BoardPath.java @@ -0,0 +1,71 @@ +package com.thealgorithms.dynamicprogramming; + +public final class BoardPath { + private BoardPath() { + } + + /** + * Recursive solution without memoization + * + * @param start - the current position + * @param end - the target position + * @return the number of ways to reach the end from the start + */ + public static int bpR(int start, int end) { + if (start == end) { + return 1; + } else if (start > end) { + return 0; + } + int count = 0; + for (int dice = 1; dice <= 6; dice++) { + count += bpR(start + dice, end); + } + return count; + } + + /** + * Recursive solution with memoization + * + * @param curr - the current position + * @param end - the target position + * @param strg - memoization array + * @return the number of ways to reach the end from the start + */ + public static int bpRS(int curr, int end, int[] strg) { + if (curr == end) { + return 1; + } else if (curr > end) { + return 0; + } + if (strg[curr] != 0) { + return strg[curr]; + } + int count = 0; + for (int dice = 1; dice <= 6; dice++) { + count += bpRS(curr + dice, end, strg); + } + strg[curr] = count; + return count; + } + + /** + * Iterative solution with tabulation + * + * @param curr - the current position (always starts from 0) + * @param end - the target position + * @param strg - memoization array + * @return the number of ways to reach the end from the start + */ + public static int bpIS(int curr, int end, int[] strg) { + strg[end] = 1; + for (int i = end - 1; i >= 0; i--) { + int count = 0; + for (int dice = 1; dice <= 6 && dice + i <= end; dice++) { + count += strg[i + dice]; + } + strg[i] = count; + } + return strg[curr]; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/BoundaryFill.java b/src/main/java/com/thealgorithms/dynamicprogramming/BoundaryFill.java new file mode 100644 index 000000000000..8494492f293f --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/BoundaryFill.java @@ -0,0 +1,55 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * Java program for Boundary fill algorithm. + * @author Akshay Dubey (https://github.com/itsAkshayDubey) + */ +public final class BoundaryFill { + private BoundaryFill() { + } + + /** + * Get the color at the given co-odrinates of a 2D image + * + * @param image The image to be filled + * @param xCoordinate The x co-ordinate of which color is to be obtained + * @param yCoordinate The y co-ordinate of which color is to be obtained + */ + public static int getPixel(int[][] image, int xCoordinate, int yCoordinate) { + return image[xCoordinate][yCoordinate]; + } + + /** + * Put the color at the given co-odrinates of a 2D image + * + * @param image The image to be filed + * @param xCoordinate The x co-ordinate at which color is to be filled + * @param yCoordinate The y co-ordinate at which color is to be filled + */ + public static void putPixel(int[][] image, int xCoordinate, int yCoordinate, int newColor) { + image[xCoordinate][yCoordinate] = newColor; + } + + /** + * Fill the 2D image with new color + * + * @param image The image to be filed + * @param xCoordinate The x co-ordinate at which color is to be filled + * @param yCoordinate The y co-ordinate at which color is to be filled + * @param newColor The new color which to be filled in the image + * @param boundaryColor The old color which is to be replaced in the image + */ + public static void boundaryFill(int[][] image, int xCoordinate, int yCoordinate, int newColor, int boundaryColor) { + if (xCoordinate >= 0 && yCoordinate >= 0 && getPixel(image, xCoordinate, yCoordinate) != newColor && getPixel(image, xCoordinate, yCoordinate) != boundaryColor) { + putPixel(image, xCoordinate, yCoordinate, newColor); + boundaryFill(image, xCoordinate + 1, yCoordinate, newColor, boundaryColor); + boundaryFill(image, xCoordinate - 1, yCoordinate, newColor, boundaryColor); + boundaryFill(image, xCoordinate, yCoordinate + 1, newColor, boundaryColor); + boundaryFill(image, xCoordinate, yCoordinate - 1, newColor, boundaryColor); + boundaryFill(image, xCoordinate + 1, yCoordinate - 1, newColor, boundaryColor); + boundaryFill(image, xCoordinate - 1, yCoordinate + 1, newColor, boundaryColor); + boundaryFill(image, xCoordinate + 1, yCoordinate + 1, newColor, boundaryColor); + boundaryFill(image, xCoordinate - 1, yCoordinate - 1, newColor, boundaryColor); + } + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/BruteForceKnapsack.java b/src/main/java/com/thealgorithms/dynamicprogramming/BruteForceKnapsack.java new file mode 100644 index 000000000000..3c1851a8c46c --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/BruteForceKnapsack.java @@ -0,0 +1,67 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * A naive recursive implementation of the 0-1 Knapsack problem. + * + * <p>The 0-1 Knapsack problem is a classic optimization problem where you are + * given a set of items, each with a weight and a value, and a knapsack with a + * fixed capacity. The goal is to determine the maximum value that can be + * obtained by selecting a subset of the items such that the total weight does + * not exceed the knapsack's capacity. Each item can either be included (1) or + * excluded (0), hence the name "0-1" Knapsack.</p> + * + * <p>This class provides a brute-force recursive approach to solving the + * problem. It evaluates all possible combinations of items to find the optimal + * solution, but this approach has exponential time complexity and is not + * suitable for large input sizes.</p> + * + * <p><b>Time Complexity:</b> O(2^n), where n is the number of items.</p> + * + * <p><b>Space Complexity:</b> O(n), due to the recursive function call stack.</p> + */ +public final class BruteForceKnapsack { + private BruteForceKnapsack() { + } + + /** + * Solves the 0-1 Knapsack problem using a recursive brute-force approach. + * + * @param w the total capacity of the knapsack + * @param wt an array where wt[i] represents the weight of the i-th item + * @param val an array where val[i] represents the value of the i-th item + * @param n the number of items available for selection + * @return the maximum value that can be obtained with the given capacity + * + * <p>The function uses recursion to explore all possible subsets of items. + * For each item, it has two choices: either include it in the knapsack + * (if it fits) or exclude it. It returns the maximum value obtainable + * through these two choices.</p> + * + * <p><b>Base Cases:</b> + * <ul> + * <li>If no items are left (n == 0), the maximum value is 0.</li> + * <li>If the knapsack's remaining capacity is 0 (w == 0), no more items can + * be included, and the value is 0.</li> + * </ul></p> + * + * <p><b>Recursive Steps:</b> + * <ul> + * <li>If the weight of the n-th item exceeds the current capacity, it is + * excluded from the solution, and the function proceeds with the remaining + * items.</li> + * <li>Otherwise, the function considers two possibilities: include the n-th + * item or exclude it, and returns the maximum value of these two scenarios.</li> + * </ul></p> + */ + static int knapSack(int w, int[] wt, int[] val, int n) { + if (n == 0 || w == 0) { + return 0; + } + + if (wt[n - 1] > w) { + return knapSack(w, wt, val, n - 1); + } else { + return Math.max(knapSack(w, wt, val, n - 1), val[n - 1] + knapSack(w - wt[n - 1], wt, val, n - 1)); + } + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/CatalanNumber.java b/src/main/java/com/thealgorithms/dynamicprogramming/CatalanNumber.java new file mode 100644 index 000000000000..d01066f611bd --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/CatalanNumber.java @@ -0,0 +1,57 @@ +package com.thealgorithms.dynamicprogramming; + +import java.util.Scanner; +/** + * This file contains an implementation of finding the nth CATALAN NUMBER using + * dynamic programming : <a href="https://en.wikipedia.org/wiki/Catalan_number">Wikipedia</a> + * + * Time Complexity: O(n^2) Space Complexity: O(n) + * + * @author <a href="https://github.com/amritesh19">AMRITESH ANAND</a> + */ +public final class CatalanNumber { + private CatalanNumber() { + } + + /** + * This method finds the nth Catalan number + * + * @param n input n which determines the nth Catalan number n should be less + * than equal to 50 as 50th Catalan number is 6,533,841,209,031,609,592 for + * n > 50, BigInteger class should be used instead long + * + * @return catalanArray[n] the nth Catalan number + */ + static long findNthCatalan(int n) { + // Array to store the results of subproblems i.e Catalan numbers from [1...n-1] + long[] catalanArray = new long[n + 1]; + + // Initialising C₀ = 1 and C₁ = 1 + catalanArray[0] = 1; + catalanArray[1] = 1; + + /* + * The Catalan numbers satisfy the recurrence relation C₀=1 and Cn = Σ + * (Ci * Cn-1-i), i = 0 to n-1 , n > 0 + */ + for (int i = 2; i <= n; i++) { + catalanArray[i] = 0; + for (int j = 0; j < i; j++) { + catalanArray[i] += catalanArray[j] * catalanArray[i - j - 1]; + } + } + + return catalanArray[n]; + } + + // Main method + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + + System.out.println("Enter the number n to find nth Catalan number (n <= 50)"); + int n = sc.nextInt(); + System.out.println(n + "th Catalan number is " + findNthCatalan(n)); + + sc.close(); + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/ClimbingStairs.java b/src/main/java/com/thealgorithms/dynamicprogramming/ClimbingStairs.java new file mode 100644 index 000000000000..0f0f5375ba82 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/ClimbingStairs.java @@ -0,0 +1,55 @@ +package com.thealgorithms.dynamicprogramming; + +/* + * A dynamic programming solution for the "Climbing Stairs" problem. + * Returns the no. of distinct ways to climb to the top + * of a staircase when you can climb either 1 or 2 steps at a time. + * + * For example, if there are 5 steps, the possible ways to climb the + * staircase are: + * 1. 1-1-1-1-1 + * 2. 1-1-1-2 + * 3. 1-2-1-1 + * 4. 2-1-1-1 + * 5. 2-2-1 + * 6. 1-1-2-1 + * 7. 1-2-2 + * 8. 2-1-2 + * Ans: 8 ways + */ +public final class ClimbingStairs { + + private ClimbingStairs() { + } + + /** + * Calculates the no. of distinct ways to climb a staircase with n steps. + * + * @param n the no. of steps in the staircase (non-negative integer) + * @return the no. of distinct ways to climb to the top + * - Returns 0 if n is 0 (no steps to climb). + * - Returns 1 if n is 1 (only one way to climb). + * - For n > 1, it returns the total no. of ways to climb. + */ + public static int numberOfWays(int n) { + + // Base case: if there are no steps or only one step, return n. + if (n == 1 || n == 0) { + return n; + } + + int prev = 1; // Ways to reach the step before the current one (step 1) + int curr = 1; // Ways to reach the current step (step 2) + int next; // Total ways to reach the next step + + for (int i = 2; i <= n; i++) { // step 2 to n + next = curr + prev; + + // Move the pointers to the next step + prev = curr; + curr = next; + } + + return curr; // Ways to reach the nth step + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/CoinChange.java b/src/main/java/com/thealgorithms/dynamicprogramming/CoinChange.java new file mode 100644 index 000000000000..7edc9603dc8b --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/CoinChange.java @@ -0,0 +1,60 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * @author Varun Upadhyay (https://github.com/varunu28) + */ +public final class CoinChange { + private CoinChange() { + } + + /** + * This method finds the number of combinations of getting change for a + * given amount and change coins + * + * @param coins The list of coins + * @param amount The amount for which we need to find the change Finds the + * number of combinations of change + */ + public static int change(int[] coins, int amount) { + int[] combinations = new int[amount + 1]; + combinations[0] = 1; + + for (int coin : coins) { + for (int i = coin; i < amount + 1; i++) { + combinations[i] += combinations[i - coin]; + } + } + + return combinations[amount]; + } + + /** + * This method finds the minimum number of coins needed for a given amount. + * + * @param coins The list of coins + * @param amount The amount for which we need to find the minimum number of + * coins. Finds the minimum number of coins that make a given value. + */ + public static int minimumCoins(int[] coins, int amount) { + // minimumCoins[i] will store the minimum coins needed for amount i + int[] minimumCoins = new int[amount + 1]; + + minimumCoins[0] = 0; + + for (int i = 1; i <= amount; i++) { + minimumCoins[i] = Integer.MAX_VALUE; + } + for (int i = 1; i <= amount; i++) { + for (int coin : coins) { + if (coin <= i) { + int subRes = minimumCoins[i - coin]; + if (subRes != Integer.MAX_VALUE && subRes + 1 < minimumCoins[i]) { + minimumCoins[i] = subRes + 1; + } + } + } + } + + return minimumCoins[amount]; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/CountFriendsPairing.java b/src/main/java/com/thealgorithms/dynamicprogramming/CountFriendsPairing.java new file mode 100644 index 000000000000..e731524d4958 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/CountFriendsPairing.java @@ -0,0 +1,34 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * @author <a href="https://github.com/siddhant2002">Siddhant Swarup Mallick</a> + + * In mathematics, the Golomb sequence is a non-decreasing integer sequence where n-th term is equal + * to number of times n appears in the sequence. + + * <a href="https://en.wikipedia.org/wiki/Golomb_sequence">Wikipedia</a> + * Program description - To find the Golomb sequence upto n + */ +public final class CountFriendsPairing { + private CountFriendsPairing() { + } + + public static boolean countFriendsPairing(int n, int[] a) { + int[] dp = new int[n + 1]; + // array of n+1 size is created + dp[0] = 1; + // since 1st index position value is fixed so it's marked as 1 + for (int i = 1; i < n; i++) { + dp[i] = 1 + dp[i - dp[dp[i - 1]]]; + // formula for ith golomb sequence is dp(i) = 1 + dp(i – dp(dp(i - 1))) + } + for (int i = 1; i < n; i++) { + if (a[i - 1] != dp[i]) { + return false; + // checks whether the calculated answer matches with the expected answer + } + } + return true; + // returns true if calculated answer matches with the expected answer + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/DiceThrow.java b/src/main/java/com/thealgorithms/dynamicprogramming/DiceThrow.java new file mode 100644 index 000000000000..c3dc3f32ff7c --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/DiceThrow.java @@ -0,0 +1,47 @@ +package com.thealgorithms.dynamicprogramming; + +// Given N dice each with M faces, numbered from 1 to M, find the number of ways to get sum X. +// X is the summation of values on each face when all the dice are thrown. + +/* The Naive approach is to find all the possible combinations of values from n dice and +keep on counting the results that sum to X. This can be done using recursion. */ +// The above recursion solution exhibits overlapping subproblems. + +/* Hence, storing the results of the solved sub-problems saves time. +And it can be done using Dynamic Programming(DP). +// Time Complexity: O(m * n * x) where m is number of faces, n is number of dice and x is given sum. +Following is implementation of Dynamic Programming approach. */ +// Code ----> +// Java program to find number of ways to get sum 'x' with 'n' +// dice where every dice has 'm' faces +final class DP { + private DP() { + } + + /* The main function that returns the number of ways to get sum 'x' with 'n' dice and 'm' with m + * faces. */ + public static long findWays(int m, int n, int x) { + /* Create a table to store the results of subproblems. + One extra row and column are used for simplicity + (Number of dice is directly used as row index and sum is directly used as column index). + The entries in 0th row and 0th column are never used. */ + long[][] table = new long[n + 1][x + 1]; + + /* Table entries for only one dice */ + for (int j = 1; j <= m && j <= x; j++) { + table[1][j] = 1; + } + + /* Fill rest of the entries in table using recursive relation + i: number of dice, j: sum */ + for (int i = 2; i <= n; i++) { + for (int j = 1; j <= x; j++) { + for (int k = 1; k < j && k <= m; k++) { + table[i][j] += table[i - 1][j - k]; + } + } + } + + return table[n][x]; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/EditDistance.java b/src/main/java/com/thealgorithms/dynamicprogramming/EditDistance.java new file mode 100644 index 000000000000..020d15197b28 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/EditDistance.java @@ -0,0 +1,100 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * A DynamicProgramming based solution for Edit Distance problem In Java + * Description of Edit Distance with an Example: + * + * <p> + * Edit distance is a way of quantifying how dissimilar two strings (e.g., + * words) are to one another, by counting the minimum number of operations + * required to transform one string into the other. The distance operations are + * the removal, insertion, or substitution of a character in the string. + * + * <p> + * + * <p> + * The Distance between "kitten" and "sitting" is 3. A minimal edit script that + * transforms the former into the latter is: + * + * <p> + * kitten → sitten (substitution of "s" for "k") sitten → sittin (substitution + * of "i" for "e") sittin → sitting (insertion of "g" at the end). + * + * @author SUBHAM SANGHAI + */ +public final class EditDistance { + private EditDistance() { + } + + public static int minDistance(String word1, String word2) { + int len1 = word1.length(); + int len2 = word2.length(); + // len1+1, len2+1, because finally return dp[len1][len2] + int[][] dp = new int[len1 + 1][len2 + 1]; + /* If second string is empty, the only option is to + insert all characters of first string into second*/ + for (int i = 0; i <= len1; i++) { + dp[i][0] = i; + } + /* If first string is empty, the only option is to + insert all characters of second string into first*/ + for (int j = 0; j <= len2; j++) { + dp[0][j] = j; + } + // iterate though, and check last char + for (int i = 0; i < len1; i++) { + char c1 = word1.charAt(i); + for (int j = 0; j < len2; j++) { + char c2 = word2.charAt(j); + // if last two chars equal + if (c1 == c2) { + // update dp value for +1 length + dp[i + 1][j + 1] = dp[i][j]; + } else { + /* if two characters are different , + then take the minimum of the various operations(i.e insertion,removal,substitution)*/ + int replace = dp[i][j] + 1; + int insert = dp[i][j + 1] + 1; + int delete = dp[i + 1][j] + 1; + + int min = Math.min(replace, insert); + min = Math.min(delete, min); + dp[i + 1][j + 1] = min; + } + } + } + /* return the final answer , after traversing through both the strings*/ + return dp[len1][len2]; + } + + // edit distance problem + public static int editDistance(String s1, String s2) { + int[][] storage = new int[s1.length() + 1][s2.length() + 1]; + return editDistance(s1, s2, storage); + } + + public static int editDistance(String s1, String s2, int[][] storage) { + int m = s1.length(); + int n = s2.length(); + if (storage[m][n] > 0) { + return storage[m][n]; + } + if (m == 0) { + storage[m][n] = n; + return storage[m][n]; + } + if (n == 0) { + storage[m][n] = m; + return storage[m][n]; + } + if (s1.charAt(0) == s2.charAt(0)) { + storage[m][n] = editDistance(s1.substring(1), s2.substring(1), storage); + } else { + int op1 = editDistance(s1, s2.substring(1), storage); + int op2 = editDistance(s1.substring(1), s2, storage); + int op3 = editDistance(s1.substring(1), s2.substring(1), storage); + storage[m][n] = 1 + Math.min(op1, Math.min(op2, op3)); + } + return storage[m][n]; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/EggDropping.java b/src/main/java/com/thealgorithms/dynamicprogramming/EggDropping.java new file mode 100644 index 000000000000..be52ab166f18 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/EggDropping.java @@ -0,0 +1,51 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * DynamicProgramming solution for the Egg Dropping Puzzle + */ +public final class EggDropping { + private EggDropping() { + } + + // min trials with n eggs and m floors + public static int minTrials(int n, int m) { + int[][] eggFloor = new int[n + 1][m + 1]; + int result; + int x; + + for (int i = 1; i <= n; i++) { + eggFloor[i][0] = 0; // Zero trial for zero floor. + eggFloor[i][1] = 1; // One trial for one floor + } + + // j trials for only 1 egg + for (int j = 1; j <= m; j++) { + eggFloor[1][j] = j; + } + + // Using bottom-up approach in DP + for (int i = 2; i <= n; i++) { + for (int j = 2; j <= m; j++) { + eggFloor[i][j] = Integer.MAX_VALUE; + for (x = 1; x <= j; x++) { + result = 1 + Math.max(eggFloor[i - 1][x - 1], eggFloor[i][j - x]); + + // choose min of all values for particular x + if (result < eggFloor[i][j]) { + eggFloor[i][j] = result; + } + } + } + } + + return eggFloor[n][m]; + } + + public static void main(String[] args) { + int n = 2; + int m = 4; + // result outputs min no. of trials in worst case for n eggs and m floors + int result = minTrials(n, m); + System.out.println(result); + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/Fibonacci.java b/src/main/java/com/thealgorithms/dynamicprogramming/Fibonacci.java new file mode 100644 index 000000000000..0d6aff2bbef3 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/Fibonacci.java @@ -0,0 +1,119 @@ +package com.thealgorithms.dynamicprogramming; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Varun Upadhyay (https://github.com/varunu28) + */ +public final class Fibonacci { + private Fibonacci() { + } + + static final Map<Integer, Integer> CACHE = new HashMap<>(); + + /** + * This method finds the nth fibonacci number using memoization technique + * + * @param n The input n for which we have to determine the fibonacci number + * Outputs the nth fibonacci number + * @throws IllegalArgumentException if n is negative + */ + public static int fibMemo(int n) { + if (n < 0) { + throw new IllegalArgumentException("Input n must be non-negative"); + } + if (CACHE.containsKey(n)) { + return CACHE.get(n); + } + + int f; + + if (n <= 1) { + f = n; + } else { + f = fibMemo(n - 1) + fibMemo(n - 2); + CACHE.put(n, f); + } + return f; + } + + /** + * This method finds the nth fibonacci number using bottom up + * + * @param n The input n for which we have to determine the fibonacci number + * Outputs the nth fibonacci number + * @throws IllegalArgumentException if n is negative + */ + public static int fibBotUp(int n) { + if (n < 0) { + throw new IllegalArgumentException("Input n must be non-negative"); + } + Map<Integer, Integer> fib = new HashMap<>(); + + for (int i = 0; i <= n; i++) { + int f; + if (i <= 1) { + f = i; + } else { + f = fib.get(i - 1) + fib.get(i - 2); + } + fib.put(i, f); + } + + return fib.get(n); + } + + /** + * This method finds the nth fibonacci number using bottom up + * + * @param n The input n for which we have to determine the fibonacci number + * Outputs the nth fibonacci number + * <p> + * This is optimized version of Fibonacci Program. Without using Hashmap and + * recursion. It saves both memory and time. Space Complexity will be O(1) + * Time Complexity will be O(n) + * <p> + * Whereas , the above functions will take O(n) Space. + * @throws IllegalArgumentException if n is negative + * @author Shoaib Rayeen (https://github.com/shoaibrayeen) + */ + public static int fibOptimized(int n) { + if (n < 0) { + throw new IllegalArgumentException("Input n must be non-negative"); + } + if (n == 0) { + return 0; + } + int prev = 0; + int res = 1; + int next; + for (int i = 2; i <= n; i++) { + next = prev + res; + prev = res; + res = next; + } + return res; + } + + /** + * We have only defined the nth Fibonacci number in terms of the two before it. Now, we will + * look at Binet's formula to calculate the nth Fibonacci number in constant time. The Fibonacci + * terms maintain a ratio called golden ratio denoted by Φ, the Greek character pronounced + * ‘phi'. First, let's look at how the golden ratio is calculated: Φ = ( 1 + √5 )/2 + * = 1.6180339887... Now, let's look at Binet's formula: Sn = Φⁿ–(– Φ⁻ⁿ)/√5 We first calculate + * the squareRootof5 and phi and store them in variables. Later, we apply Binet's formula to get + * the required term. Time Complexity will be O(1) + * @param n The input n for which we have to determine the fibonacci number + * Outputs the nth fibonacci number + * @throws IllegalArgumentException if n is negative + */ + public static int fibBinet(int n) { + if (n < 0) { + throw new IllegalArgumentException("Input n must be non-negative"); + } + double squareRootOf5 = Math.sqrt(5); + double phi = (1 + squareRootOf5) / 2; + return (int) ((Math.pow(phi, n) - Math.pow(-phi, -n)) / squareRootOf5); + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/KadaneAlgorithm.java b/src/main/java/com/thealgorithms/dynamicprogramming/KadaneAlgorithm.java new file mode 100644 index 000000000000..7a0a3da94c1e --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/KadaneAlgorithm.java @@ -0,0 +1,52 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * This class implements Kadane's Algorithm to find the maximum subarray sum + * within a given array of integers. The algorithm efficiently computes the maximum + * sum of a contiguous subarray in linear time. + * + * Author: <a href="https://github.com/siddhant2002">Siddhant Swarup Mallick</a> + */ +public final class KadaneAlgorithm { + private KadaneAlgorithm() { + } + + /** + * Computes the maximum subarray sum using Kadane's Algorithm and checks + * if it matches a predicted answer. + * + * @param a The input array of integers for which the maximum + * subarray sum is to be calculated. + * @param predictedAnswer The expected maximum subarray sum to be verified + * against the computed sum. + * @return true if the computed maximum subarray sum equals the predicted + * answer, false otherwise. + * + * <p>Example:</p> + * <pre> + * Input: {89, 56, 98, 123, 26, 75, 12, 40, 39, 68, 91} + * Output: true if the maximum subarray sum is equal to the + * predicted answer. + * </pre> + * + * <p>Algorithmic Complexity:</p> + * <ul> + * <li>Time Complexity: O(n) - the algorithm iterates through the array once.</li> + * <li>Auxiliary Space Complexity: O(1) - only a constant amount of additional space is used.</li> + * </ul> + */ + public static boolean maxSum(int[] a, int predictedAnswer) { + int sum = a[0]; + int runningSum = 0; + + for (int k : a) { + runningSum += k; + sum = Math.max(sum, runningSum); + if (runningSum < 0) { + runningSum = 0; + } + } + + return sum == predictedAnswer; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/Knapsack.java b/src/main/java/com/thealgorithms/dynamicprogramming/Knapsack.java new file mode 100644 index 000000000000..134561766830 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/Knapsack.java @@ -0,0 +1,55 @@ +package com.thealgorithms.dynamicprogramming; + +import java.util.Arrays; + +/** + * A Dynamic Programming based solution for the 0-1 Knapsack problem. + * This class provides a method, `knapSack`, that calculates the maximum value that can be + * obtained from a given set of items with weights and values, while not exceeding a + * given weight capacity. + * + * @see <a href="https://en.wikipedia.org/?title=0-1_Knapsack_problem">0-1 Knapsack Problem </a> + */ +public final class Knapsack { + + private Knapsack() { + } + + private static void throwIfInvalidInput(final int weightCapacity, final int[] weights, final int[] values) { + if (weightCapacity < 0) { + throw new IllegalArgumentException("Weight capacity should not be negative."); + } + if (weights == null || values == null || weights.length != values.length) { + throw new IllegalArgumentException("Input arrays must not be null and must have the same length."); + } + if (Arrays.stream(weights).anyMatch(w -> w <= 0)) { + throw new IllegalArgumentException("Input array should not contain non-positive weight(s)."); + } + } + + /** + * Solves the 0-1 Knapsack problem using Dynamic Programming. + * + * @param weightCapacity The maximum weight capacity of the knapsack. + * @param weights An array of item weights. + * @param values An array of item values. + * @return The maximum value that can be obtained without exceeding the weight capacity. + * @throws IllegalArgumentException If the input arrays are null or have different lengths. + */ + public static int knapSack(final int weightCapacity, final int[] weights, final int[] values) throws IllegalArgumentException { + throwIfInvalidInput(weightCapacity, weights, values); + + // DP table to store the state of the maximum possible return for a given weight capacity. + int[] dp = new int[weightCapacity + 1]; + + for (int i = 0; i < values.length; i++) { + for (int w = weightCapacity; w > 0; w--) { + if (weights[i] <= w) { + dp[w] = Math.max(dp[w], dp[w - weights[i]] + values[i]); + } + } + } + + return dp[weightCapacity]; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/KnapsackMemoization.java b/src/main/java/com/thealgorithms/dynamicprogramming/KnapsackMemoization.java new file mode 100644 index 000000000000..3501e302a6ef --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/KnapsackMemoization.java @@ -0,0 +1,53 @@ +package com.thealgorithms.dynamicprogramming; + +import java.util.Arrays; + +/** + * Recursive Solution for 0-1 knapsack with memoization + * This method is basically an extension to the recursive approach so that we + * can overcome the problem of calculating redundant cases and thus increased + * complexity. We can solve this problem by simply creating a 2-D array that can + * store a particular state (n, w) if we get it the first time. + */ +public class KnapsackMemoization { + + int knapSack(int capacity, int[] weights, int[] profits, int numOfItems) { + + // Declare the table dynamically + int[][] dpTable = new int[numOfItems + 1][capacity + 1]; + + // Loop to initially fill the table with -1 + for (int[] table : dpTable) { + Arrays.fill(table, -1); + } + + return solveKnapsackRecursive(capacity, weights, profits, numOfItems, dpTable); + } + + // Returns the value of maximum profit using recursive approach + int solveKnapsackRecursive(int capacity, int[] weights, int[] profits, int numOfItems, int[][] dpTable) { + // Base condition + if (numOfItems == 0 || capacity == 0) { + return 0; + } + + if (dpTable[numOfItems][capacity] != -1) { + return dpTable[numOfItems][capacity]; + } + + if (weights[numOfItems - 1] > capacity) { + // Store the value of function call stack in table + dpTable[numOfItems][capacity] = solveKnapsackRecursive(capacity, weights, profits, numOfItems - 1, dpTable); + } else { + // case 1. include the item, if it is less than the capacity + final int includeCurrentItem = profits[numOfItems - 1] + solveKnapsackRecursive(capacity - weights[numOfItems - 1], weights, profits, numOfItems - 1, dpTable); + + // case 2. exclude the item if it is more than the capacity + final int excludeCurrentItem = solveKnapsackRecursive(capacity, weights, profits, numOfItems - 1, dpTable); + + // Store the value of function call stack in table and return + dpTable[numOfItems][capacity] = Math.max(includeCurrentItem, excludeCurrentItem); + } + return dpTable[numOfItems][capacity]; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/LevenshteinDistance.java b/src/main/java/com/thealgorithms/dynamicprogramming/LevenshteinDistance.java new file mode 100644 index 000000000000..119d65dfe365 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/LevenshteinDistance.java @@ -0,0 +1,84 @@ +package com.thealgorithms.dynamicprogramming; + +import java.util.stream.IntStream; + +/** + * Provides functions to calculate the Levenshtein distance between two strings. + * + * The Levenshtein distance is a measure of the similarity between two strings by calculating the minimum number of single-character + * edits (insertions, deletions, or substitutions) required to change one string into the other. + */ +public final class LevenshteinDistance { + private LevenshteinDistance() { + } + + /** + * Calculates the Levenshtein distance between two strings using a naive dynamic programming approach. + * + * This function computes the Levenshtein distance by constructing a dynamic programming matrix and iteratively filling it in. + * It follows the standard top-to-bottom, left-to-right approach for filling in the matrix. + * + * @param string1 The first string. + * @param string2 The second string. + * @return The Levenshtein distance between the two input strings. + * + * Time complexity: O(nm), + * Space complexity: O(nm), + * + * where n and m are lengths of `string1` and `string2`. + * + * Note that this implementation uses a straightforward dynamic programming approach without any space optimization. + * It may consume more memory for larger input strings compared to the optimized version. + */ + public static int naiveLevenshteinDistance(final String string1, final String string2) { + int[][] distanceMatrix = IntStream.rangeClosed(0, string1.length()).mapToObj(i -> IntStream.rangeClosed(0, string2.length()).map(j -> (i == 0) ? j : (j == 0) ? i : 0).toArray()).toArray(int[][] ::new); + + IntStream.range(1, string1.length() + 1).forEach(i -> IntStream.range(1, string2.length() + 1).forEach(j -> { + final int cost = (string1.charAt(i - 1) == string2.charAt(j - 1)) ? 0 : 1; + distanceMatrix[i][j] = Math.min(distanceMatrix[i - 1][j - 1] + cost, Math.min(distanceMatrix[i][j - 1] + 1, distanceMatrix[i - 1][j] + 1)); + })); + + return distanceMatrix[string1.length()][string2.length()]; + } + + /** + * Calculates the Levenshtein distance between two strings using an optimized dynamic programming approach. + * + * This edit distance is defined as 1 point per insertion, substitution, or deletion required to make the strings equal. + * + * @param string1 The first string. + * @param string2 The second string. + * @return The Levenshtein distance between the two input strings. + * + * Time complexity: O(nm), + * Space complexity: O(n), + * + * where n and m are lengths of `string1` and `string2`. + * + * Note that this implementation utilizes an optimized dynamic programming approach, significantly reducing the space complexity from O(nm) to O(n), where n and m are the lengths of `string1` and `string2`. + * + * Additionally, it minimizes space usage by leveraging the shortest string horizontally and the longest string vertically in the computation matrix. + */ + public static int optimizedLevenshteinDistance(final String string1, final String string2) { + if (string1.isEmpty()) { + return string2.length(); + } + + int[] previousDistance = IntStream.rangeClosed(0, string1.length()).toArray(); + + for (int j = 1; j <= string2.length(); j++) { + int prevSubstitutionCost = previousDistance[0]; + previousDistance[0] = j; + + for (int i = 1; i <= string1.length(); i++) { + final int deletionCost = previousDistance[i] + 1; + final int insertionCost = previousDistance[i - 1] + 1; + final int substitutionCost = (string1.charAt(i - 1) == string2.charAt(j - 1)) ? prevSubstitutionCost : prevSubstitutionCost + 1; + prevSubstitutionCost = previousDistance[i]; + previousDistance[i] = Math.min(deletionCost, Math.min(insertionCost, substitutionCost)); + } + } + + return previousDistance[string1.length()]; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/LongestAlternatingSubsequence.java b/src/main/java/com/thealgorithms/dynamicprogramming/LongestAlternatingSubsequence.java new file mode 100644 index 000000000000..08039b85ce40 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/LongestAlternatingSubsequence.java @@ -0,0 +1,73 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * Class for finding the length of the longest alternating subsequence in an array. + * + * <p>An alternating sequence is a sequence of numbers where the elements alternate + * between increasing and decreasing. Specifically, a sequence is alternating if its elements + * satisfy one of the following relations: + * + * <ul> + * <li>{@code x1 < x2 > x3 < x4 > x5 < ... < xn}</li> + * <li>{@code x1 > x2 < x3 > x4 < x5 > ... > xn}</li> + * </ul> + * + * <p>This class provides a method to compute the length of the longest such subsequence + * from a given array of integers. + */ +public final class LongestAlternatingSubsequence { + private LongestAlternatingSubsequence() { + } + + /** + * Finds the length of the longest alternating subsequence in the given array. + * + * @param arr an array of integers where the longest alternating subsequence is to be found + * @param n the length of the array {@code arr} + * @return the length of the longest alternating subsequence + * + * <p>The method uses dynamic programming to solve the problem. It maintains a 2D array + * {@code las} where: + * <ul> + * <li>{@code las[i][0]} represents the length of the longest alternating subsequence + * ending at index {@code i} with the last element being greater than the previous element.</li> + * <li>{@code las[i][1]} represents the length of the longest alternating subsequence + * ending at index {@code i} with the last element being smaller than the previous element.</li> + * </ul> + * + * <p>The method iterates through the array and updates the {@code las} array based on + * whether the current element is greater or smaller than the previous elements. + * The result is the maximum value found in the {@code las} array. + */ + static int alternatingLength(int[] arr, int n) { + int[][] las = new int[n][2]; // las = LongestAlternatingSubsequence + + // Initialize the dp array + for (int i = 0; i < n; i++) { + las[i][0] = 1; + las[i][1] = 1; + } + + int result = 1; // Initialize result + + // Compute values in a bottom-up manner + for (int i = 1; i < n; i++) { + for (int j = 0; j < i; j++) { + // If arr[i] is greater than arr[j], update las[i][0] + if (arr[j] < arr[i] && las[i][0] < las[j][1] + 1) { + las[i][0] = las[j][1] + 1; + } + + // If arr[i] is smaller than arr[j], update las[i][1] + if (arr[j] > arr[i] && las[i][1] < las[j][0] + 1) { + las[i][1] = las[j][0] + 1; + } + } + + // Pick the maximum of both values at index i + result = Math.max(result, Math.max(las[i][0], las[i][1])); + } + + return result; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/LongestArithmeticSubsequence.java b/src/main/java/com/thealgorithms/dynamicprogramming/LongestArithmeticSubsequence.java new file mode 100644 index 000000000000..b5ac62b4674b --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/LongestArithmeticSubsequence.java @@ -0,0 +1,42 @@ +package com.thealgorithms.dynamicprogramming; + +import java.util.HashMap; + +final class LongestArithmeticSubsequence { + private LongestArithmeticSubsequence() { + } + + /** + * Returns the length of the longest arithmetic subsequence in the given array. + * + * A sequence seq is arithmetic if seq[i + 1] - seq[i] are all the same value + * (for 0 <= i < seq.length - 1). + * + * @param nums the input array of integers + * @return the length of the longest arithmetic subsequence + */ + public static int getLongestArithmeticSubsequenceLength(int[] nums) { + if (nums == null) { + throw new IllegalArgumentException("Input array cannot be null"); + } + + if (nums.length <= 1) { + return nums.length; + } + + HashMap<Integer, Integer>[] dp = new HashMap[nums.length]; + int maxLength = 2; + + // fill the dp array + for (int i = 0; i < nums.length; i++) { + dp[i] = new HashMap<>(); + for (int j = 0; j < i; j++) { + final int diff = nums[i] - nums[j]; + dp[i].put(diff, dp[j].getOrDefault(diff, 1) + 1); + maxLength = Math.max(maxLength, dp[i].get(diff)); + } + } + + return maxLength; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequence.java b/src/main/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequence.java new file mode 100644 index 000000000000..54837b5f4e71 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequence.java @@ -0,0 +1,98 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * This class implements the Longest Common Subsequence (LCS) problem. + * The LCS of two sequences is the longest sequence that appears in both + * sequences + * in the same order, but not necessarily consecutively. + * + * This implementation uses dynamic programming to find the LCS of two strings. + */ +final class LongestCommonSubsequence { + + private LongestCommonSubsequence() { + } + + /** + * Returns the Longest Common Subsequence (LCS) of two given strings. + * + * @param str1 The first string. + * @param str2 The second string. + * @return The LCS of the two strings, or null if one of the strings is null. + */ + public static String getLCS(String str1, String str2) { + // If either string is null, return null as LCS can't be computed. + if (str1 == null || str2 == null) { + return null; + } + // If either string is empty, return an empty string as LCS. + if (str1.length() == 0 || str2.length() == 0) { + return ""; + } + + // Convert the strings into arrays of characters + String[] arr1 = str1.split(""); + String[] arr2 = str2.split(""); + + // lcsMatrix[i][j] = LCS(first i characters of str1, first j characters of str2) + int[][] lcsMatrix = new int[arr1.length + 1][arr2.length + 1]; + + // Base Case: Fill the LCS matrix 0th row & 0th column with 0s + // as LCS of any string with an empty string is 0. + for (int i = 0; i < arr1.length + 1; i++) { + lcsMatrix[i][0] = 0; + } + for (int j = 1; j < arr2.length + 1; j++) { + lcsMatrix[0][j] = 0; + } + + // Build the LCS matrix by comparing characters of str1 & str2 + for (int i = 1; i < arr1.length + 1; i++) { + for (int j = 1; j < arr2.length + 1; j++) { + // If characters match, the LCS increases by 1 + if (arr1[i - 1].equals(arr2[j - 1])) { + lcsMatrix[i][j] = lcsMatrix[i - 1][j - 1] + 1; + } else { + // Otherwise, take the maximum of the left or above values + lcsMatrix[i][j] = Math.max(lcsMatrix[i - 1][j], lcsMatrix[i][j - 1]); + } + } + } + + // Call helper function to reconstruct the LCS from the matrix + return lcsString(str1, str2, lcsMatrix); + } + + /** + * Reconstructs the LCS string from the LCS matrix. + * + * @param str1 The first string. + * @param str2 The second string. + * @param lcsMatrix The matrix storing the lengths of LCSs + * of substrings of str1 and str2. + * @return The LCS string. + */ + public static String lcsString(String str1, String str2, int[][] lcsMatrix) { + StringBuilder lcs = new StringBuilder(); // Hold the LCS characters. + int i = str1.length(); // Start from the end of str1. + int j = str2.length(); // Start from the end of str2. + + // Trace back through the LCS matrix to reconstruct the LCS + while (i > 0 && j > 0) { + // If characters match, add to the LCS and move diagonally in the matrix + if (str1.charAt(i - 1) == str2.charAt(j - 1)) { + lcs.append(str1.charAt(i - 1)); + i--; + j--; + } else if (lcsMatrix[i - 1][j] > lcsMatrix[i][j - 1]) { + // If the value above is larger, move up + i--; + } else { + // If the value to the left is larger, move left + j--; + } + } + + return lcs.reverse().toString(); // LCS built in reverse, so reverse it back + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/LongestIncreasingSubsequence.java b/src/main/java/com/thealgorithms/dynamicprogramming/LongestIncreasingSubsequence.java new file mode 100644 index 000000000000..470833ce9c97 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/LongestIncreasingSubsequence.java @@ -0,0 +1,98 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * @author Afrizal Fikri (https://github.com/icalF) + */ +public final class LongestIncreasingSubsequence { + private LongestIncreasingSubsequence() { + } + + private static int upperBound(int[] ar, int l, int r, int key) { + while (l < r - 1) { + int m = (l + r) >>> 1; + if (ar[m] >= key) { + r = m; + } else { + l = m; + } + } + + return r; + } + + public static int lis(int[] array) { + int len = array.length; + if (len == 0) { + return 0; + } + + int[] tail = new int[len]; + + // always points empty slot in tail + int length = 1; + + tail[0] = array[0]; + for (int i = 1; i < len; i++) { + // new smallest value + if (array[i] < tail[0]) { + tail[0] = array[i]; + } // array[i] extends largest subsequence + else if (array[i] > tail[length - 1]) { + tail[length++] = array[i]; + } // array[i] will become end candidate of an existing subsequence or + // Throw away larger elements in all LIS, to make room for upcoming grater elements than + // array[i] + // (and also, array[i] would have already appeared in one of LIS, identify the location + // and replace it) + else { + tail[upperBound(tail, -1, length - 1, array[i])] = array[i]; + } + } + + return length; + } + + /** + * @author Alon Firestein (https://github.com/alonfirestein) + */ + // A function for finding the length of the LIS algorithm in O(nlogn) complexity. + public static int findLISLen(int[] a) { + final int size = a.length; + if (size == 0) { + return 0; + } + int[] arr = new int[size]; + arr[0] = a[0]; + int lis = 1; + for (int i = 1; i < size; i++) { + int index = binarySearchBetween(arr, lis - 1, a[i]); + arr[index] = a[i]; + if (index == lis) { + lis++; + } + } + return lis; + } + + // O(logn) + + private static int binarySearchBetween(int[] t, int end, int key) { + int left = 0; + int right = end; + if (key < t[0]) { + return 0; + } + if (key > t[end]) { + return end + 1; + } + while (left < right - 1) { + final int middle = (left + right) >>> 1; + if (t[middle] < key) { + left = middle; + } else { + right = middle; + } + } + return right; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/LongestIncreasingSubsequenceNLogN.java b/src/main/java/com/thealgorithms/dynamicprogramming/LongestIncreasingSubsequenceNLogN.java new file mode 100644 index 000000000000..7bc0855e0566 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/LongestIncreasingSubsequenceNLogN.java @@ -0,0 +1,75 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * Implementation of the Longest Increasing Subsequence (LIS) problem using + * an O(n log n) dynamic programming solution enhanced with binary search. + * + * @author Vusal Huseynov (https://github.com/huseynovvusal) + */ +public final class LongestIncreasingSubsequenceNLogN { + private LongestIncreasingSubsequenceNLogN() { + } + + /** + * Finds the index of the smallest element in the array that is greater than + * or equal to the target using binary search. The search is restricted to + * the first `size` elements of the array. + * + * @param arr The array to search in (assumed to be sorted up to `size`). + * @param size The number of valid elements in the array. + * @param target The target value to find the lower bound for. + * @return The index of the lower bound. + */ + private static int lowerBound(int[] arr, int target, int size) { + int l = 0; + int r = size; + + while (l < r) { + int mid = l + (r - l) / 2; + + if (target > arr[mid]) { + // Move right if target is greater than mid element + l = mid + 1; + } else { + // Move left if target is less than or equal to mid element + r = mid; + } + } + + // Return the index where the target can be inserted + return l; + } + + /** + * Calculates the length of the Longest Increasing Subsequence (LIS) in the given array. + * + * @param arr The input array of integers. + * @return The length of the LIS. + */ + public static int lengthOfLIS(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; // Return 0 for empty or null arrays + } + + // tails[i] - the smallest end element of an increasing subsequence of length i+1 + int[] tails = new int[arr.length]; + // size - the length of the longest increasing subsequence found so far + int size = 0; + + for (int x : arr) { + // Find the position to replace or extend the subsequence + int index = lowerBound(tails, x, size); + + // Update the tails array with the current element + tails[index] = x; + + // If the element extends the subsequence, increase the size + if (index == size) { + size++; + } + } + + // Return the length of the LIS + return size; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/LongestPalindromicSubsequence.java b/src/main/java/com/thealgorithms/dynamicprogramming/LongestPalindromicSubsequence.java new file mode 100644 index 000000000000..0b40d4559341 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/LongestPalindromicSubsequence.java @@ -0,0 +1,58 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * Algorithm explanation + * https://www.educative.io/edpresso/longest-palindromic-subsequence-algorithm + */ +public final class LongestPalindromicSubsequence { + private LongestPalindromicSubsequence() { + } + + public static void main(String[] args) { + String a = "BBABCBCAB"; + String b = "BABCBAB"; + + String aLPS = lps(a); + String bLPS = lps(b); + + System.out.println(a + " => " + aLPS); + System.out.println(b + " => " + bLPS); + } + + public static String lps(String original) throws IllegalArgumentException { + StringBuilder reverse = new StringBuilder(original); + reverse = reverse.reverse(); + return recursiveLPS(original, reverse.toString()); + } + + private static String recursiveLPS(String original, String reverse) { + String bestResult = ""; + + // no more chars, then return empty + if (original.length() == 0 || reverse.length() == 0) { + bestResult = ""; + } else { + // if the last chars match, then remove it from both strings and recur + if (original.charAt(original.length() - 1) == reverse.charAt(reverse.length() - 1)) { + String bestSubResult = recursiveLPS(original.substring(0, original.length() - 1), reverse.substring(0, reverse.length() - 1)); + + bestResult = reverse.charAt(reverse.length() - 1) + bestSubResult; + } else { + // otherwise (1) ignore the last character of reverse, and recur on original and + // updated reverse again (2) ignore the last character of original and recur on the + // updated original and reverse again then select the best result from these two + // subproblems. + + String bestSubResult1 = recursiveLPS(original, reverse.substring(0, reverse.length() - 1)); + String bestSubResult2 = recursiveLPS(original.substring(0, original.length() - 1), reverse); + if (bestSubResult1.length() > bestSubResult2.length()) { + bestResult = bestSubResult1; + } else { + bestResult = bestSubResult2; + } + } + } + + return bestResult; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/LongestPalindromicSubstring.java b/src/main/java/com/thealgorithms/dynamicprogramming/LongestPalindromicSubstring.java new file mode 100644 index 000000000000..8a4ab2f526a9 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/LongestPalindromicSubstring.java @@ -0,0 +1,39 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * Class for finding the longest palindromic substring within a given string. + * <p> + * A palindromic substring is a sequence of characters that reads the same backward as forward. + * This class uses a dynamic programming approach to efficiently find the longest palindromic substring. + * + */ +public final class LongestPalindromicSubstring { + private LongestPalindromicSubstring() { + } + + public static String lps(String input) { + if (input == null || input.isEmpty()) { + return input; + } + boolean[][] arr = new boolean[input.length()][input.length()]; + int start = 0; + int end = 0; + for (int g = 0; g < input.length(); g++) { + for (int i = 0, j = g; j < input.length(); i++, j++) { + if (g == 0) { + arr[i][j] = true; + } else if (g == 1) { + arr[i][j] = input.charAt(i) == input.charAt(j); + } else { + arr[i][j] = input.charAt(i) == input.charAt(j) && arr[i + 1][j - 1]; + } + + if (arr[i][j]) { + start = i; + end = j; + } + } + } + return input.substring(start, end + 1); + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/LongestValidParentheses.java b/src/main/java/com/thealgorithms/dynamicprogramming/LongestValidParentheses.java new file mode 100644 index 000000000000..02696bfca9c2 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/LongestValidParentheses.java @@ -0,0 +1,43 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * Given a string containing just the characters '(' and ')', find the length of + * the longest valid (well-formed) parentheses substring. + * + * @author Libin Yang (https://github.com/yanglbme) + * @since 2018/10/5 + */ +public final class LongestValidParentheses { + private LongestValidParentheses() { + } + + public static int getLongestValidParentheses(String s) { + if (s == null || s.length() < 2) { + return 0; + } + char[] chars = s.toCharArray(); + int n = chars.length; + int[] res = new int[n]; + res[0] = 0; + res[1] = chars[1] == ')' && chars[0] == '(' ? 2 : 0; + + int max = res[1]; + + for (int i = 2; i < n; ++i) { + if (chars[i] == ')') { + if (chars[i - 1] == '(') { + res[i] = res[i - 2] + 2; + } else { + int index = i - res[i - 1] - 1; + if (index >= 0 && chars[index] == '(') { + // ()(()) + res[i] = res[i - 1] + 2 + (index - 1 >= 0 ? res[index - 1] : 0); + } + } + } + max = Math.max(max, res[i]); + } + + return max; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/MatrixChainMultiplication.java b/src/main/java/com/thealgorithms/dynamicprogramming/MatrixChainMultiplication.java new file mode 100644 index 000000000000..1edb7207dee2 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/MatrixChainMultiplication.java @@ -0,0 +1,163 @@ +package com.thealgorithms.dynamicprogramming; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * The MatrixChainMultiplication class provides functionality to compute the + * optimal way to multiply a sequence of matrices. The optimal multiplication + * order is determined using dynamic programming, which minimizes the total + * number of scalar multiplications required. + */ +public final class MatrixChainMultiplication { + private MatrixChainMultiplication() { + } + + // Matrices to store minimum multiplication costs and split points + private static int[][] m; + private static int[][] s; + private static int[] p; + + /** + * Calculates the optimal order for multiplying a given list of matrices. + * + * @param matrices an ArrayList of Matrix objects representing the matrices + * to be multiplied. + * @return a Result object containing the matrices of minimum costs and + * optimal splits. + */ + public static Result calculateMatrixChainOrder(ArrayList<Matrix> matrices) { + int size = matrices.size(); + m = new int[size + 1][size + 1]; + s = new int[size + 1][size + 1]; + p = new int[size + 1]; + + for (int i = 0; i < size + 1; i++) { + Arrays.fill(m[i], -1); + Arrays.fill(s[i], -1); + } + + for (int i = 0; i < p.length; i++) { + p[i] = i == 0 ? matrices.get(i).col() : matrices.get(i - 1).row(); + } + + matrixChainOrder(size); + return new Result(m, s); + } + + /** + * A helper method that computes the minimum cost of multiplying + * the matrices using dynamic programming. + * + * @param size the number of matrices in the multiplication sequence. + */ + private static void matrixChainOrder(int size) { + for (int i = 1; i < size + 1; i++) { + m[i][i] = 0; + } + + for (int l = 2; l < size + 1; l++) { + for (int i = 1; i < size - l + 2; i++) { + int j = i + l - 1; + m[i][j] = Integer.MAX_VALUE; + + for (int k = i; k < j; k++) { + int q = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j]; + if (q < m[i][j]) { + m[i][j] = q; + s[i][j] = k; + } + } + } + } + } + + /** + * The Result class holds the results of the matrix chain multiplication + * calculation, including the matrix of minimum costs and split points. + */ + public static class Result { + private final int[][] m; + private final int[][] s; + + /** + * Constructs a Result object with the specified matrices of minimum + * costs and split points. + * + * @param m the matrix of minimum multiplication costs. + * @param s the matrix of optimal split points. + */ + public Result(int[][] m, int[][] s) { + this.m = m; + this.s = s; + } + + /** + * Returns the matrix of minimum multiplication costs. + * + * @return the matrix of minimum multiplication costs. + */ + public int[][] getM() { + return m; + } + + /** + * Returns the matrix of optimal split points. + * + * @return the matrix of optimal split points. + */ + public int[][] getS() { + return s; + } + } + + /** + * The Matrix class represents a matrix with its dimensions and count. + */ + public static class Matrix { + private final int count; + private final int col; + private final int row; + + /** + * Constructs a Matrix object with the specified count, number of columns, + * and number of rows. + * + * @param count the identifier for the matrix. + * @param col the number of columns in the matrix. + * @param row the number of rows in the matrix. + */ + public Matrix(int count, int col, int row) { + this.count = count; + this.col = col; + this.row = row; + } + + /** + * Returns the identifier of the matrix. + * + * @return the identifier of the matrix. + */ + public int count() { + return count; + } + + /** + * Returns the number of columns in the matrix. + * + * @return the number of columns in the matrix. + */ + public int col() { + return col; + } + + /** + * Returns the number of rows in the matrix. + * + * @return the number of rows in the matrix. + */ + public int row() { + return row; + } + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/MatrixChainRecursiveTopDownMemoisation.java b/src/main/java/com/thealgorithms/dynamicprogramming/MatrixChainRecursiveTopDownMemoisation.java new file mode 100644 index 000000000000..0c1031c6805c --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/MatrixChainRecursiveTopDownMemoisation.java @@ -0,0 +1,68 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * The MatrixChainRecursiveTopDownMemoisation class implements the matrix-chain + * multiplication problem using a top-down recursive approach with memoization. + * + * <p>Given a chain of matrices A1, A2, ..., An, where matrix Ai has dimensions + * pi-1 × pi, this algorithm finds the optimal way to fully parenthesize the + * product A1A2...An in a way that minimizes the total number of scalar + * multiplications required.</p> + * + * <p>This implementation uses a memoization technique to store the results of + * subproblems, which significantly reduces the number of recursive calls and + * improves performance compared to a naive recursive approach.</p> + */ +public final class MatrixChainRecursiveTopDownMemoisation { + private MatrixChainRecursiveTopDownMemoisation() { + } + + /** + * Calculates the minimum number of scalar multiplications needed to multiply + * a chain of matrices. + * + * @param p an array of integers representing the dimensions of the matrices. + * The length of the array is n + 1, where n is the number of matrices. + * @return the minimum number of multiplications required to multiply the chain + * of matrices. + */ + static int memoizedMatrixChain(int[] p) { + int n = p.length; + int[][] m = new int[n][n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + m[i][j] = Integer.MAX_VALUE; + } + } + return lookupChain(m, p, 1, n - 1); + } + + /** + * A recursive helper method to lookup the minimum number of multiplications + * for multiplying matrices from index i to index j. + * + * @param m the memoization table storing the results of subproblems. + * @param p an array of integers representing the dimensions of the matrices. + * @param i the starting index of the matrix chain. + * @param j the ending index of the matrix chain. + * @return the minimum number of multiplications needed to multiply matrices + * from i to j. + */ + static int lookupChain(int[][] m, int[] p, int i, int j) { + if (i == j) { + m[i][j] = 0; + return m[i][j]; + } + if (m[i][j] < Integer.MAX_VALUE) { + return m[i][j]; + } else { + for (int k = i; k < j; k++) { + int q = lookupChain(m, p, i, k) + lookupChain(m, p, k + 1, j) + (p[i - 1] * p[k] * p[j]); + if (q < m[i][j]) { + m[i][j] = q; + } + } + } + return m[i][j]; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/MaximumSumOfNonAdjacentElements.java b/src/main/java/com/thealgorithms/dynamicprogramming/MaximumSumOfNonAdjacentElements.java new file mode 100644 index 000000000000..49af3ae3db88 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/MaximumSumOfNonAdjacentElements.java @@ -0,0 +1,95 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * Class to find the maximum sum of non-adjacent elements in an array. This + * class contains two approaches: one with O(n) space complexity and another + * with O(1) space optimization. For more information, refer to + * https://takeuforward.org/data-structure/maximum-sum-of-non-adjacent-elements-dp-5/ + */ +final class MaximumSumOfNonAdjacentElements { + + private MaximumSumOfNonAdjacentElements() { + } + + /** + * Approach 1: Uses a dynamic programming array to store the maximum sum at + * each index. Time Complexity: O(n) - where n is the length of the input + * array. Space Complexity: O(n) - due to the additional dp array. + * @param arr The input array of integers. + * @return The maximum sum of non-adjacent elements. + */ + public static int getMaxSumApproach1(int[] arr) { + if (arr.length == 0) { + return 0; // Check for empty array + } + + int n = arr.length; + int[] dp = new int[n]; + + // Base case: Maximum sum if only one element is present. + dp[0] = arr[0]; + + for (int ind = 1; ind < n; ind++) { + + // Case 1: Do not take the current element, carry forward the previous max + // sum. + int notTake = dp[ind - 1]; + + // Case 2: Take the current element, add it to the max sum up to two + // indices before. + int take = arr[ind]; + if (ind > 1) { + take += dp[ind - 2]; + } + + // Store the maximum of both choices in the dp array. + dp[ind] = Math.max(take, notTake); + } + + return dp[n - 1]; + } + + /** + * Approach 2: Optimized space complexity approach using two variables instead + * of an array. Time Complexity: O(n) - where n is the length of the input + * array. Space Complexity: O(1) - as it only uses constant space for two + * variables. + * @param arr The input array of integers. + * @return The maximum sum of non-adjacent elements. + */ + public static int getMaxSumApproach2(int[] arr) { + if (arr.length == 0) { + return 0; // Check for empty array + } + + int n = arr.length; + + // Two variables to keep track of previous two results: + // prev1 = max sum up to the last element (n-1) + // prev2 = max sum up to the element before last (n-2) + + int prev1 = arr[0]; // Base case: Maximum sum for the first element. + int prev2 = 0; + + for (int ind = 1; ind < n; ind++) { + // Case 1: Do not take the current element, keep the last max sum. + int notTake = prev1; + + // Case 2: Take the current element and add it to the result from two + // steps back. + int take = arr[ind]; + if (ind > 1) { + take += prev2; + } + + // Calculate the current maximum sum and update previous values. + int current = Math.max(take, notTake); + + // Shift prev1 and prev2 for the next iteration. + prev2 = prev1; + prev1 = current; + } + + return prev1; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/MinimumPathSum.java b/src/main/java/com/thealgorithms/dynamicprogramming/MinimumPathSum.java new file mode 100644 index 000000000000..98773de92f6b --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/MinimumPathSum.java @@ -0,0 +1,64 @@ +package com.thealgorithms.dynamicprogramming; + +/* +Given the following grid with length m and width n: +\---\---\---\ (n) +\ 1 \ 3 \ 1 \ +\---\---\---\ +\ 1 \ 5 \ 1 \ +\---\---\---\ +\ 4 \ 2 \ 1 \ +\---\---\---\ +(m) +Find the path where its sum is the smallest. + +The Time Complexity of your algorithm should be smaller than or equal to O(mn). +The Space Complexity of your algorithm should be smaller than or equal to O(n). +You can only move from the top left corner to the down right corner. +You can only move one step down or right. + +EXAMPLE: +INPUT: grid = [[1,3,1],[1,5,1],[4,2,1]] +OUTPUT: 7 +EXPLANATIONS: 1 + 3 + 1 + 1 + 1 = 7 + +For more information see https://www.geeksforgeeks.org/maximum-path-sum-matrix/ + */ +public final class MinimumPathSum { + + private MinimumPathSum() { + } + + public static int minimumPathSum(final int[][] grid) { + int numRows = grid.length; + int numCols = grid[0].length; + + if (numCols == 0) { + return 0; + } + + int[] dp = new int[numCols]; + + // Initialize the first element of the dp array + dp[0] = grid[0][0]; + + // Calculate the minimum path sums for the first row + for (int col = 1; col < numCols; col++) { + dp[col] = dp[col - 1] + grid[0][col]; + } + + // Calculate the minimum path sums for the remaining rows + for (int row = 1; row < numRows; row++) { + // Update the minimum path sum for the first column + dp[0] += grid[row][0]; + + for (int col = 1; col < numCols; col++) { + // Choose the minimum path sum from the left or above + dp[col] = Math.min(dp[col - 1], dp[col]) + grid[row][col]; + } + } + + // Return the minimum path sum for the last cell in the grid + return dp[numCols - 1]; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/MinimumSumPartition.java b/src/main/java/com/thealgorithms/dynamicprogramming/MinimumSumPartition.java new file mode 100644 index 000000000000..52308c23cf1c --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/MinimumSumPartition.java @@ -0,0 +1,57 @@ +package com.thealgorithms.dynamicprogramming; + +import java.util.Arrays; + +/* +Given an array of non-negative integers , partition the array in two subset that +difference in sum of elements for both subset minimum. +Return the minimum difference in sum of these subsets you can achieve. + +Input: array[] = {1, 6, 11, 4} +Output: 0 +Explanation: +Subset1 = {1, 4, 6}, sum of Subset1 = 11 +Subset2 = {11}, sum of Subset2 = 11 + +Input: array[] = {36, 7, 46, 40} +Output: 23 +Explanation: +Subset1 = {7, 46} ; sum of Subset1 = 53 +Subset2 = {36, 40} ; sum of Subset2 = 76 + */ +public final class MinimumSumPartition { + private MinimumSumPartition() { + } + + private static void throwIfInvalidInput(final int[] array) { + if (Arrays.stream(array).anyMatch(a -> a < 0)) { + throw new IllegalArgumentException("Input array should not contain negative number(s)."); + } + } + + public static int minimumSumPartition(final int[] array) { + throwIfInvalidInput(array); + int sum = Arrays.stream(array).sum(); + boolean[] dp = new boolean[sum / 2 + 1]; + dp[0] = true; // Base case , don't select any element from array + + // Find the closest sum of subset array that we can achieve which is closest to half of sum of full array + int closestPartitionSum = 0; + + for (int i = 0; i < array.length; i++) { + for (int j = sum / 2; j > 0; j--) { + if (array[i] <= j) { + dp[j] = dp[j] || dp[j - array[i]]; + } + if (dp[j]) { + closestPartitionSum = Math.max(closestPartitionSum, j); + } + } + } + /* + Difference in sum = Big partition sum - Small partition sum + = ( Total sum - Small partition sum) - Small partition sum + */ + return sum - (2 * closestPartitionSum); + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/NewManShanksPrime.java b/src/main/java/com/thealgorithms/dynamicprogramming/NewManShanksPrime.java new file mode 100644 index 000000000000..3db40148f1c2 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/NewManShanksPrime.java @@ -0,0 +1,48 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * The NewManShanksPrime class provides a method to determine whether the nth + * New Man Shanks prime matches an expected answer. + * + * <p>This is based on the New Man Shanks prime sequence defined by the recurrence + * relation:</p> + * + * <pre> + * a(n) = 2 * a(n-1) + a(n-2) for n >= 2 + * a(0) = 1 + * a(1) = 1 + * </pre> + * + * <p>For more information on New Man Shanks primes, please refer to the + * <a href="https://en.wikipedia.org/wiki/Newman%E2%80%93Shanks%E2%80%93Williams_prime"> + * Wikipedia article</a>.</p> + * + * <p>Note: The class is designed to be non-instantiable.</p> + * + * @author <a href="https://github.com/siddhant2002">Siddhant Swarup Mallick</a> + */ +public final class NewManShanksPrime { + private NewManShanksPrime() { + } + + /** + * Calculates the nth New Man Shanks prime and checks if it equals the + * expected answer. + * + * @param n the index of the New Man Shanks prime to calculate (0-based). + * @param expectedAnswer the expected value of the nth New Man Shanks prime. + * @return true if the calculated nth New Man Shanks prime matches the + * expected answer; false otherwise. + */ + public static boolean nthManShanksPrime(int n, int expectedAnswer) { + int[] a = new int[n + 1]; + a[0] = 1; + a[1] = 1; + + for (int i = 2; i <= n; i++) { + a[i] = 2 * a[i - 1] + a[i - 2]; + } + + return a[n] == expectedAnswer; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/OptimalJobScheduling.java b/src/main/java/com/thealgorithms/dynamicprogramming/OptimalJobScheduling.java new file mode 100644 index 000000000000..e31bb73096e8 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/OptimalJobScheduling.java @@ -0,0 +1,131 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * This class refers to the Optimal Job Scheduling problem with the following constrains: + * - precedence relation between the processes + * - machine pair dependent transportation delays + * + * https://en.wikipedia.org/wiki/Optimal_job_scheduling + * + * @author georgioct@csd.auth.gr + */ +public class OptimalJobScheduling { + + private final int numberProcesses; + private final int numberMachines; + private final int[][] run; + private final int[][] transfer; + private final int[][] cost; + + /** + * Constructor of the class. + * @param numberProcesses ,refers to the number of precedent processes(N) + * @param numberMachines ,refers to the number of different machines in our disposal(M) + * @param run , N*M matrix refers to the cost of running each process to each machine + * @param transfer ,M*M symmetric matrix refers to the transportation delay for each pair of + * machines + */ + public OptimalJobScheduling(int numberProcesses, int numberMachines, int[][] run, int[][] transfer) { + this.numberProcesses = numberProcesses; + this.numberMachines = numberMachines; + this.run = run; + this.transfer = transfer; + this.cost = new int[numberProcesses][numberMachines]; + } + + /** + * Function which computes the cost of process scheduling to a number of VMs. + */ + public void execute() { + this.calculateCost(); + this.showResults(); + } + + /** + * Function which computes the cost of running each Process to each and every Machine + */ + private void calculateCost() { + + for (int i = 0; i < numberProcesses; i++) { // for each Process + + for (int j = 0; j < numberMachines; j++) { // for each Machine + + cost[i][j] = runningCost(i, j); + } + } + } + + /** + * Function which returns the minimum cost of running a certain Process to a certain Machine.In + * order for the Machine to execute the Process ,he requires the output of the previously + * executed Process, which may have been executed to the same Machine or some other.If the + * previous Process has been executed to another Machine,we have to transfer her result, which + * means extra cost for transferring the data from one Machine to another(if the previous + * Process has been executed to the same Machine, there is no transport cost). + * + * @param process ,refers to the Process + * @param machine ,refers to the Machine + * @return the minimum cost of executing the process to the certain machine. + */ + private int runningCost(int process, int machine) { + + if (process == 0) { // refers to the first process,which does not require for a previous one + // to have been executed + return run[process][machine]; + } else { + + int[] runningCosts = new int[numberMachines]; // stores the costs of executing our Process depending on + // the Machine the previous one was executed + + for (int k = 0; k < numberMachines; k++) { // computes the cost of executing the previous + // process to each and every Machine + runningCosts[k] = cost[process - 1][k] + transfer[k][machine] + run[process][machine]; // transferring the result to our Machine and executing + // the Process to our Machine + } + return findMin(runningCosts); // returns the minimum running cost + } + } + + /** + * Function used in order to return the minimum Cost. + * @param costArr ,an Array of size M which refers to the costs of executing a Process to each + * Machine + * @return the minimum cost + */ + private int findMin(int[] costArr) { + + int min = 0; + + for (int i = 1; i < costArr.length; i++) { + + if (costArr[i] < costArr[min]) { + min = i; + } + } + return costArr[min]; + } + + /** + * Method used in order to present the overall costs. + */ + private void showResults() { + + for (int i = 0; i < numberProcesses; i++) { + + for (int j = 0; j < numberMachines; j++) { + System.out.print(cost[i][j]); + System.out.print(" "); + } + + System.out.println(); + } + System.out.println(); + } + + /** + * Getter for the running Cost of i process on j machine. + */ + public int getCost(int process, int machine) { + return cost[process][machine]; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/PalindromicPartitioning.java b/src/main/java/com/thealgorithms/dynamicprogramming/PalindromicPartitioning.java new file mode 100644 index 000000000000..a323a9a06f7d --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/PalindromicPartitioning.java @@ -0,0 +1,83 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * Provides functionality to solve the Palindrome Partitioning II problem, which involves finding + * the minimum number of partitions needed to divide a given string into palindromic substrings. + * + * <p> + * The problem is solved using dynamic programming. The approach involves checking all possible + * substrings and determining whether they are palindromes. The minimum number of cuts required + * for palindrome partitioning is computed in a bottom-up manner. + * </p> + * + * <p> + * Example: + * <ul> + * <li>Input: "nitik" => Output: 2 (Partitioning: "n | iti | k")</li> + * <li>Input: "ababbbabbababa" => Output: 3 (Partitioning: "aba | b | bbabb | ababa")</li> + * </ul> + * </p> + * + * @see <a href="https://leetcode.com/problems/palindrome-partitioning-ii/">Palindrome Partitioning II</a> + * @see <a href="https://www.geeksforgeeks.org/palindrome-partitioning-dp-17/">Palindrome Partitioning (GeeksforGeeks)</a> + */ +public final class PalindromicPartitioning { + private PalindromicPartitioning() { + } + + public static int minimalPartitions(String word) { + int len = word.length(); + /* We Make two arrays to create a bottom-up solution. + minCuts[i] = Minimum number of cuts needed for palindrome partitioning of substring + word[0..i] isPalindrome[i][j] = true if substring str[i..j] is palindrome Base Condition: + C[i] is 0 if P[0][i]= true + */ + int[] minCuts = new int[len]; + boolean[][] isPalindrome = new boolean[len][len]; + + int i; + int j; + int subLen; // different looping variables + + // Every substring of length 1 is a palindrome + for (i = 0; i < len; i++) { + isPalindrome[i][i] = true; + } + + /* subLen is substring length. Build the solution in bottom up manner by considering all + * substrings of length starting from 2 to n. */ + for (subLen = 2; subLen <= len; subLen++) { + // For substring of length subLen, set different possible starting indexes + for (i = 0; i < len - subLen + 1; i++) { + j = i + subLen - 1; // Ending index + // If subLen is 2, then we just need to + // compare two characters. Else need to + // check two corner characters and value + // of P[i+1][j-1] + if (subLen == 2) { + isPalindrome[i][j] = (word.charAt(i) == word.charAt(j)); + } else { + isPalindrome[i][j] = (word.charAt(i) == word.charAt(j)) && isPalindrome[i + 1][j - 1]; + } + } + } + + // We find the minimum for each index + for (i = 0; i < len; i++) { + if (isPalindrome[0][i]) { + minCuts[i] = 0; + } else { + minCuts[i] = Integer.MAX_VALUE; + for (j = 0; j < i; j++) { + if (isPalindrome[j + 1][i] && 1 + minCuts[j] < minCuts[i]) { + minCuts[i] = 1 + minCuts[j]; + } + } + } + } + + // Return the min cut value for complete + // string. i.e., str[0..n-1] + return minCuts[len - 1]; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/PartitionProblem.java b/src/main/java/com/thealgorithms/dynamicprogramming/PartitionProblem.java new file mode 100644 index 000000000000..49c4a0a3a008 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/PartitionProblem.java @@ -0,0 +1,42 @@ +/** + * @author Md Asif Joardar + * + * Description: The partition problem is a classic problem in computer science + * that asks whether a given set can be partitioned into two subsets such that + * the sum of elements in each subset is the same. + * + * Example: + * Consider nums = {1, 2, 3} + * We can split the array "nums" into two partitions, where each having a sum of 3. + * nums1 = {1, 2} + * nums2 = {3} + * + * The time complexity of the solution is O(n × sum) and requires O(n × sum) space + */ + +package com.thealgorithms.dynamicprogramming; + +import java.util.Arrays; + +public final class PartitionProblem { + private PartitionProblem() { + } + + /** + * Test if a set of integers can be partitioned into two subsets such that the sum of elements + * in each subset is the same. + * + * @param nums the array contains integers. + * @return {@code true} if two subset exists, otherwise {@code false}. + */ + public static boolean partition(int[] nums) { + // calculate the sum of all the elements in the array + int sum = Arrays.stream(nums).sum(); + + // it will return true if the sum is even and the array can be divided into two + // subarrays/subset with equal sum. and here i reuse the SubsetSum class from dynamic + // programming section to check if there is exists a subsetsum into nums[] array same as the + // given sum + return (sum & 1) == 0 && SubsetSum.subsetSum(nums, sum / 2); + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/RegexMatching.java b/src/main/java/com/thealgorithms/dynamicprogramming/RegexMatching.java new file mode 100644 index 000000000000..181ac72a654d --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/RegexMatching.java @@ -0,0 +1,200 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * Given a text and wildcard pattern implement a wildcard pattern matching + * algorithm that finds if wildcard is matched with text. The matching should + * cover the entire text ?-> matches single characters *-> match the sequence of + * characters + * + * For calculation of Time and Space Complexity. Let N be length of src and M be length of pat + * + * Memoization vs Tabulation : https://www.geeksforgeeks.org/tabulation-vs-memoization/ + * Question Link : https://practice.geeksforgeeks.org/problems/wildcard-pattern-matching/1 + */ +public final class RegexMatching { + private RegexMatching() { + } + + /** + * Method 1: Determines if the given source string matches the given pattern using a recursive approach. + * This method directly applies recursion to check if the source string matches the pattern, considering + * the wildcards '?' and '*'. + * + * Time Complexity: O(2^(N+M)), where N is the length of the source string and M is the length of the pattern. + * Space Complexity: O(N + M) due to the recursion stack. + * + * @param src The source string to be matched against the pattern. + * @param pat The pattern containing wildcards ('*' matches a sequence of characters, '?' matches a single character). + * @return {@code true} if the source string matches the pattern, {@code false} otherwise. + */ + public static boolean regexRecursion(String src, String pat) { + if (src.length() == 0 && pat.length() == 0) { + return true; + } + if (src.length() != 0 && pat.length() == 0) { + return false; + } + if (src.length() == 0 && pat.length() != 0) { + for (int i = 0; i < pat.length(); i++) { + if (pat.charAt(i) != '*') { + return false; + } + } + return true; + } + char chs = src.charAt(0); + char chp = pat.charAt(0); + + String ros = src.substring(1); + String rop = pat.substring(1); + + boolean ans; + if (chs == chp || chp == '?') { + ans = regexRecursion(ros, rop); + } else if (chp == '*') { + boolean blank = regexRecursion(src, rop); + boolean multiple = regexRecursion(ros, pat); + ans = blank || multiple; + } else { + ans = false; + } + return ans; + } + + /** + * Method 2: Determines if the given source string matches the given pattern using recursion. + * This method utilizes a virtual index for both the source string and the pattern to manage the recursion. + * + * Time Complexity: O(2^(N+M)) where N is the length of the source string and M is the length of the pattern. + * Space Complexity: O(N + M) due to the recursion stack. + * + * @param src The source string to be matched against the pattern. + * @param pat The pattern containing wildcards ('*' matches a sequence of characters, '?' matches a single character). + * @param svidx The current index in the source string. + * @param pvidx The current index in the pattern. + * @return {@code true} if the source string matches the pattern, {@code false} otherwise. + */ + static boolean regexRecursion(String src, String pat, int svidx, int pvidx) { + if (src.length() == svidx && pat.length() == pvidx) { + return true; + } + if (src.length() != svidx && pat.length() == pvidx) { + return false; + } + if (src.length() == svidx && pat.length() != pvidx) { + for (int i = pvidx; i < pat.length(); i++) { + if (pat.charAt(i) != '*') { + return false; + } + } + return true; + } + char chs = src.charAt(svidx); + char chp = pat.charAt(pvidx); + + boolean ans; + if (chs == chp || chp == '?') { + ans = regexRecursion(src, pat, svidx + 1, pvidx + 1); + } else if (chp == '*') { + boolean blank = regexRecursion(src, pat, svidx, pvidx + 1); + boolean multiple = regexRecursion(src, pat, svidx + 1, pvidx); + ans = blank || multiple; + } else { + ans = false; + } + return ans; + } + + /** + * Method 3: Determines if the given source string matches the given pattern using top-down dynamic programming (memoization). + * This method utilizes memoization to store intermediate results, reducing redundant computations and improving efficiency. + * + * Time Complexity: O(N * M), where N is the length of the source string and M is the length of the pattern. + * Space Complexity: O(N * M) for the memoization table, plus additional space for the recursion stack. + * + * @param src The source string to be matched against the pattern. + * @param pat The pattern containing wildcards ('*' matches a sequence of characters, '?' matches a single character). + * @param svidx The current index in the source string. + * @param pvidx The current index in the pattern. + * @param strg A 2D array used for memoization to store the results of subproblems. + * @return {@code true} if the source string matches the pattern, {@code false} otherwise. + */ + public static boolean regexRecursion(String src, String pat, int svidx, int pvidx, int[][] strg) { + if (src.length() == svidx && pat.length() == pvidx) { + return true; + } + if (src.length() != svidx && pat.length() == pvidx) { + return false; + } + if (src.length() == svidx && pat.length() != pvidx) { + for (int i = pvidx; i < pat.length(); i++) { + if (pat.charAt(i) != '*') { + return false; + } + } + return true; + } + if (strg[svidx][pvidx] != 0) { + return strg[svidx][pvidx] != 1; + } + char chs = src.charAt(svidx); + char chp = pat.charAt(pvidx); + + boolean ans; + if (chs == chp || chp == '?') { + ans = regexRecursion(src, pat, svidx + 1, pvidx + 1, strg); + } else if (chp == '*') { + boolean blank = regexRecursion(src, pat, svidx, pvidx + 1, strg); + boolean multiple = regexRecursion(src, pat, svidx + 1, pvidx, strg); + ans = blank || multiple; + } else { + ans = false; + } + strg[svidx][pvidx] = ans ? 2 : 1; + return ans; + } + + /** + * Method 4: Determines if the given source string matches the given pattern using bottom-up dynamic programming (tabulation). + * This method builds a solution iteratively by filling out a table, where each cell represents whether a substring + * of the source string matches a substring of the pattern. + * + * Time Complexity: O(N * M), where N is the length of the source string and M is the length of the pattern. + * Space Complexity: O(N * M) for the table used in the tabulation process. + * + * @param src The source string to be matched against the pattern. + * @param pat The pattern containing wildcards ('*' matches a sequence of characters, '?' matches a single character). + * @return {@code true} if the source string matches the pattern, {@code false} otherwise. + */ + static boolean regexBU(String src, String pat) { + boolean[][] strg = new boolean[src.length() + 1][pat.length() + 1]; + strg[src.length()][pat.length()] = true; + for (int row = src.length(); row >= 0; row--) { + for (int col = pat.length() - 1; col >= 0; col--) { + if (row == src.length()) { + if (pat.charAt(col) == '*') { + strg[row][col] = strg[row][col + 1]; + } else { + strg[row][col] = false; + } + } else { + char chs = src.charAt(row); + char chp = pat.charAt(col); + + boolean ans; + if (chs == chp || chp == '?') { + ans = strg[row + 1][col + 1]; + } else if (chp == '*') { + boolean blank = strg[row][col + 1]; + boolean multiple = strg[row + 1][col]; + ans = blank || multiple; + } else { + ans = false; + } + strg[row][col] = ans; + } + } + } + return strg[0][0]; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/RodCutting.java b/src/main/java/com/thealgorithms/dynamicprogramming/RodCutting.java new file mode 100644 index 000000000000..6d33826b6b17 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/RodCutting.java @@ -0,0 +1,47 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * A Dynamic Programming solution for the Rod cutting problem. + * Returns the best obtainable price for a rod of length n and price[] as prices of different pieces. + */ +public final class RodCutting { + private RodCutting() { + } + + /** + * This method calculates the maximum obtainable value for cutting a rod of length n + * into different pieces, given the prices for each possible piece length. + * + * @param price An array representing the prices of different pieces, where price[i-1] + * represents the price of a piece of length i. + * @param n The length of the rod to be cut. + * @throws IllegalArgumentException if the price array is null or empty, or if n is less than 0. + * @return The maximum obtainable value. + */ + public static int cutRod(int[] price, int n) { + if (price == null || price.length == 0) { + throw new IllegalArgumentException("Price array cannot be null or empty."); + } + if (n < 0) { + throw new IllegalArgumentException("Rod length cannot be negative."); + } + + // Create an array to store the maximum obtainable values for each rod length. + int[] val = new int[n + 1]; + val[0] = 0; + + // Calculate the maximum value for each rod length from 1 to n. + for (int i = 1; i <= n; i++) { + int maxVal = Integer.MIN_VALUE; + // Try all possible ways to cut the rod and find the maximum value. + for (int j = 1; j <= i; j++) { + maxVal = Math.max(maxVal, price[j - 1] + val[i - j]); + } + // Store the maximum value for the current rod length. + val[i] = maxVal; + } + + // The final element of 'val' contains the maximum obtainable value for a rod of length 'n'. + return val[n]; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/ShortestCommonSupersequenceLength.java b/src/main/java/com/thealgorithms/dynamicprogramming/ShortestCommonSupersequenceLength.java new file mode 100644 index 000000000000..56129dcdbb09 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/ShortestCommonSupersequenceLength.java @@ -0,0 +1,69 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * Class that provides methods to calculate the length of the shortest + * supersequence of two given strings. The shortest supersequence is the smallest string + * that contains both given strings as subsequences. + */ +final class ShortestCommonSupersequenceLength { + private ShortestCommonSupersequenceLength() { + } + + /** + * Finds the length of the shortest supersequence of two given strings. + * The shortest supersequence is defined as the smallest string that contains both + * given strings as subsequences. + * + * @param x The first input string. + * @param y The second input string. + * @return The length of the shortest supersequence of the two strings. + */ + static int shortestSuperSequence(String x, String y) { + int m = x.length(); + int n = y.length(); + + // find lcs + int l = lcs(x, y, m, n); + + // Result is sum of input string + // lengths - length of lcs + return m + n - l; + } + + /** + * Calculates the length of the longest common subsequence (LCS) between two strings. + * The LCS is the longest sequence that can be derived from both strings by deleting some + * (or none) of the characters without changing the order of the remaining characters. + * + * @param x The first input string. + * @param y The second input string. + * @param m The length of the first input string. + * @param n The length of the second input string. + * @return The length of the longest common subsequence of the two strings. + */ + static int lcs(String x, String y, int m, int n) { + int[][] lN = new int[m + 1][n + 1]; + int i; + int j; + + // Following steps build lN[m + 1][n + 1] + // in bottom up fashion. Note that + // lN[i][j] contains length of lNCS + // of x[0..i - 1]and y[0..j - 1] + for (i = 0; i <= m; i++) { + for (j = 0; j <= n; j++) { + if (i == 0 || j == 0) { + lN[i][j] = 0; + } else if (x.charAt(i - 1) == y.charAt(j - 1)) { + lN[i][j] = lN[i - 1][j - 1] + 1; + } else { + lN[i][j] = Math.max(lN[i - 1][j], lN[i][j - 1]); + } + } + } + + // lN[m][n] contains length of LCS + // for x[0..n - 1] and y[0..m - 1] + return lN[m][n]; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/SubsetCount.java b/src/main/java/com/thealgorithms/dynamicprogramming/SubsetCount.java new file mode 100644 index 000000000000..0c5bc2c5884d --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/SubsetCount.java @@ -0,0 +1,77 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * Find the number of subsets present in the given array with a sum equal to target. + * Based on Solution discussed on + * <a href="https://stackoverflow.com/questions/22891076/count-number-of-subsets-with-sum-equal-to-k">StackOverflow</a> + * @author <a href="https://github.com/samratpodder">Samrat Podder</a> + */ +public final class SubsetCount { + private SubsetCount() { + } + + /** + * Dynamic Programming Implementation. + * Method to find out the number of subsets present in the given array with a sum equal to + * target. Time Complexity is O(n*target) and Space Complexity is O(n*target) + * @param arr is the input array on which subsets are to searched + * @param target is the sum of each element of the subset taken together + * + */ + public static int getCount(int[] arr, int target) { + /* + * Base Cases - If target becomes zero, we have reached the required sum for the subset + * If we reach the end of the array arr then, either if target==arr[end], then we add one to + * the final count Otherwise we add 0 to the final count + */ + int n = arr.length; + int[][] dp = new int[n][target + 1]; + for (int i = 0; i < n; i++) { + dp[i][0] = 1; + } + if (arr[0] <= target) { + dp[0][arr[0]] = 1; + } + for (int t = 1; t <= target; t++) { + for (int idx = 1; idx < n; idx++) { + int notpick = dp[idx - 1][t]; + int pick = 0; + if (arr[idx] <= t) { + pick += dp[idx - 1][target - t]; + } + dp[idx][target] = pick + notpick; + } + } + return dp[n - 1][target]; + } + + /** + * This Method is a Space Optimized version of the getCount(int[], int) method and solves the + * same problem This approach is a bit better in terms of Space Used Time Complexity is + * O(n*target) and Space Complexity is O(target) + * @param arr is the input array on which subsets are to searched + * @param target is the sum of each element of the subset taken together + */ + public static int getCountSO(int[] arr, int target) { + int n = arr.length; + int[] prev = new int[target + 1]; + prev[0] = 1; + if (arr[0] <= target) { + prev[arr[0]] = 1; + } + for (int ind = 1; ind < n; ind++) { + int[] cur = new int[target + 1]; + cur[0] = 1; + for (int t = 1; t <= target; t++) { + int notTaken = prev[t]; + int taken = 0; + if (arr[ind] <= t) { + taken = prev[t - arr[ind]]; + } + cur[t] = notTaken + taken; + } + prev = cur; + } + return prev[target]; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/SubsetSum.java b/src/main/java/com/thealgorithms/dynamicprogramming/SubsetSum.java new file mode 100644 index 000000000000..da8667afd0ce --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/SubsetSum.java @@ -0,0 +1,33 @@ +package com.thealgorithms.dynamicprogramming; + +public final class SubsetSum { + private SubsetSum() { + } + + /** + * Test if a set of integers contains a subset that sums to a given integer. + * + * @param arr the array containing integers. + * @param sum the target sum of the subset. + * @return {@code true} if a subset exists that sums to the given value, + * otherwise {@code false}. + */ + public static boolean subsetSum(int[] arr, int sum) { + int n = arr.length; + + // Initialize a single array to store the possible sums + boolean[] isSum = new boolean[sum + 1]; + + // Mark isSum[0] = true since a sum of 0 is always possible with 0 elements + isSum[0] = true; + + // Iterate through each Element in the array + for (int i = 0; i < n; i++) { + // Traverse the isSum array backwards to prevent overwriting values + for (int j = sum; j >= arr[i]; j--) { + isSum[j] = isSum[j] || isSum[j - arr[i]]; + } + } + return isSum[sum]; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/SubsetSumSpaceOptimized.java b/src/main/java/com/thealgorithms/dynamicprogramming/SubsetSumSpaceOptimized.java new file mode 100644 index 000000000000..946da46cb292 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/SubsetSumSpaceOptimized.java @@ -0,0 +1,35 @@ +package com.thealgorithms.dynamicprogramming; +/* +The Sum of Subset problem determines whether a subset of elements from a +given array sums up to a specific target value. +*/ +public final class SubsetSumSpaceOptimized { + private SubsetSumSpaceOptimized() { + } + /** + * This method checks whether the subset of an array + * contains a given sum or not. This is an space + * optimized solution using 1D boolean array + * Time Complexity: O(n * sum), Space complexity: O(sum) + * + * @param arr An array containing integers + * @param sum The target sum of the subset + * @return True or False + */ + public static boolean isSubsetSum(int[] arr, int sum) { + int n = arr.length; + // Declare the boolean array with size sum + 1 + boolean[] dp = new boolean[sum + 1]; + + // Initialize the first element as true + dp[0] = true; + + // Find the subset sum using 1D array + for (int i = 0; i < n; i++) { + for (int j = sum; j >= arr[i]; j--) { + dp[j] = dp[j] || dp[j - arr[i]]; + } + } + return dp[sum]; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/SumOfSubset.java b/src/main/java/com/thealgorithms/dynamicprogramming/SumOfSubset.java new file mode 100644 index 000000000000..e9c15c1b4f24 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/SumOfSubset.java @@ -0,0 +1,45 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * A utility class that contains the Sum of Subset problem solution using + * recursion. + * + * The Sum of Subset problem determines whether a subset of elements from a + * given array sums up to a specific target value. + * + * Wikipedia: https://en.wikipedia.org/wiki/Subset_sum_problem + */ +public final class SumOfSubset { + + private SumOfSubset() { + } + + /** + * Determines if there exists a subset of elements in the array `arr` that + * adds up to the given `key` value using recursion. + * + * @param arr The array of integers. + * @param num The index of the current element being considered. + * @param key The target sum we are trying to achieve. + * @return true if a subset of `arr` adds up to `key`, false otherwise. + * + * This is a recursive solution that checks for two possibilities at + * each step: + * 1. Include the current element in the subset and check if the + * remaining elements can sum up to the remaining target. + * 2. Exclude the current element and check if the remaining elements + * can sum up to the target without this element. + */ + public static boolean subsetSum(int[] arr, int num, int key) { + if (key == 0) { + return true; + } + if (num < 0 || key < 0) { + return false; + } + + boolean include = subsetSum(arr, num - 1, key - arr[num]); + boolean exclude = subsetSum(arr, num - 1, key); + return include || exclude; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/TreeMatching.java b/src/main/java/com/thealgorithms/dynamicprogramming/TreeMatching.java new file mode 100644 index 000000000000..9fd82ccaf078 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/TreeMatching.java @@ -0,0 +1,78 @@ +package com.thealgorithms.dynamicprogramming; + +import com.thealgorithms.datastructures.graphs.UndirectedAdjacencyListGraph; + +/** + * This class implements the algorithm for calculating the maximum weighted matching in a tree. + * The tree is represented as an undirected graph with weighted edges. + * + * Problem Description: + * Given an undirected tree G = (V, E) with edge weights γ: E → N and a root r ∈ V, + * the goal is to find a maximum weight matching M ⊆ E such that no two edges in M + * share a common vertex. The sum of the weights of the edges in M, ∑ e∈M γ(e), should be maximized. + * For more Information: <a href="https://en.wikipedia.org/wiki/Matching_(graph_theory)">Matching (graph theory)</a> + * + * @author <a href="https://github.com/DenizAltunkapan">Deniz Altunkapan</a> + */ +public class TreeMatching { + + private UndirectedAdjacencyListGraph graph; + private int[][] dp; + + /** + * Constructor that initializes the graph and the DP table. + * + * @param graph The graph that represents the tree and is used for the matching algorithm. + */ + public TreeMatching(UndirectedAdjacencyListGraph graph) { + this.graph = graph; + this.dp = new int[graph.size()][2]; + } + + /** + * Calculates the maximum weighted matching for the tree, starting from the given root node. + * + * @param root The index of the root node of the tree. + * @param parent The index of the parent node (used for recursion). + * @return The maximum weighted matching for the tree, starting from the root node. + * + */ + public int getMaxMatching(int root, int parent) { + if (root < 0 || root >= graph.size()) { + throw new IllegalArgumentException("Invalid root: " + root); + } + maxMatching(root, parent); + return Math.max(dp[root][0], dp[root][1]); + } + + /** + * Recursively computes the maximum weighted matching for a node, assuming that the node + * can either be included or excluded from the matching. + * + * @param node The index of the current node for which the matching is calculated. + * @param parent The index of the parent node (to avoid revisiting the parent node during recursion). + */ + private void maxMatching(int node, int parent) { + dp[node][0] = 0; + dp[node][1] = 0; + + int sumWithoutEdge = 0; + for (int adjNode : graph.getNeighbors(node)) { + if (adjNode == parent) { + continue; + } + maxMatching(adjNode, node); + sumWithoutEdge += Math.max(dp[adjNode][0], dp[adjNode][1]); + } + + dp[node][0] = sumWithoutEdge; + + for (int adjNode : graph.getNeighbors(node)) { + if (adjNode == parent) { + continue; + } + int weight = graph.getEdgeWeight(node, adjNode); + dp[node][1] = Math.max(dp[node][1], sumWithoutEdge - Math.max(dp[adjNode][0], dp[adjNode][1]) + dp[adjNode][0] + weight); + } + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/Tribonacci.java b/src/main/java/com/thealgorithms/dynamicprogramming/Tribonacci.java new file mode 100644 index 000000000000..3ff6cc620236 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/Tribonacci.java @@ -0,0 +1,38 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * The {@code Tribonacci} class provides a method to compute the n-th number in the Tribonacci sequence. + * N-th Tribonacci Number - https://leetcode.com/problems/n-th-tribonacci-number/description/ + */ +public final class Tribonacci { + private Tribonacci() { + } + + /** + * Computes the n-th Tribonacci number. + * + * @param n the index of the Tribonacci number to compute + * @return the n-th Tribonacci number + */ + public static int compute(int n) { + if (n == 0) { + return 0; + } + if (n == 1 || n == 2) { + return 1; + } + + int first = 0; + int second = 1; + int third = 1; + + for (int i = 3; i <= n; i++) { + int next = first + second + third; + first = second; + second = third; + third = next; + } + + return third; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/UniquePaths.java b/src/main/java/com/thealgorithms/dynamicprogramming/UniquePaths.java new file mode 100644 index 000000000000..80b553f2744c --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/UniquePaths.java @@ -0,0 +1,72 @@ +/** + * Author: Siddhant Swarup Mallick + * Github: https://github.com/siddhant2002 + * <p> + * Problem Description: + * A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below). + * The robot can only move either down or right at any point in time. + * The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below). + * How many possible unique paths are there? + * <p> + * Program Description: + * This program calculates the number of unique paths possible for a robot to reach the bottom-right corner + * of an m x n grid using dynamic programming. + */ + +package com.thealgorithms.dynamicprogramming; + +import java.util.Arrays; + +public final class UniquePaths { + + private UniquePaths() { + } + + /** + * Calculates the number of unique paths using a 1D dynamic programming array. + * Time complexity O(n*m) + * Space complexity O(min(n,m)) + * + * @param m The number of rows in the grid. + * @param n The number of columns in the grid. + * @return The number of unique paths. + */ + public static int uniquePaths(final int m, final int n) { + if (m > n) { + return uniquePaths(n, m); // Recursive call to handle n > m cases + } + int[] dp = new int[n]; // Create a 1D array to store unique paths for each column + Arrays.fill(dp, 1); // Initialize all values to 1 (one way to reach each cell) + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + dp[j] = Math.addExact(dp[j], dp[j - 1]); // Update the number of unique paths for each cell + } + } + return dp[n - 1]; // The result is stored in the last column of the array + } + + /** + * Calculates the number of unique paths using a 2D dynamic programming array. + * Time complexity O(n*m) + * Space complexity O(n*m) + * + * @param m The number of rows in the grid. + * @param n The number of columns in the grid. + * @return The number of unique paths. + */ + public static int uniquePaths2(final int m, final int n) { + int[][] dp = new int[m][n]; // Create a 2D array to store unique paths for each cell + for (int i = 0; i < m; i++) { + dp[i][0] = 1; // Initialize the first column to 1 (one way to reach each cell) + } + for (int j = 0; j < n; j++) { + dp[0][j] = 1; // Initialize the first row to 1 (one way to reach each cell) + } + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + dp[i][j] = Math.addExact(dp[i - 1][j], dp[i][j - 1]); // Update the number of unique paths for each cell + } + } + return dp[m - 1][n - 1]; // The result is stored in the bottom-right cell of the array + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/UniqueSubsequencesCount.java b/src/main/java/com/thealgorithms/dynamicprogramming/UniqueSubsequencesCount.java new file mode 100755 index 000000000000..8c7ea6179e3f --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/UniqueSubsequencesCount.java @@ -0,0 +1,98 @@ +package com.thealgorithms.dynamicprogramming; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * Utility class to find the number of unique subsequences that can be + * produced from a given string. + * + * <p> This class contains static methods to compute the unique subsequence count + * using dynamic programming and recursion. It ensures that duplicate characters + * are not counted multiple times in the subsequences.</p> + * + * <p> Author: https://github.com/Tuhinm2002 </p> + */ +public final class UniqueSubsequencesCount { + + /** + * Private constructor to prevent instantiation of this utility class. + * This class should only be used in a static context. + * + * @throws UnsupportedOperationException if attempted to instantiate. + */ + private UniqueSubsequencesCount() { + throw new UnsupportedOperationException("Utility class"); + } + + /** + * Finds the number of unique subsequences that can be generated from + * the given string. + * + * <p> This method initializes a dynamic programming (DP) array and invokes + * the recursive helper function to compute the subsequence count.</p> + * + * @param str the input string from which subsequences are generated + * @return the total count of unique subsequences + */ + public static int countSubseq(String str) { + + // DP array initialized to store intermediate results + int[] dp = new int[str.length() + 1]; + Arrays.fill(dp, -1); + + // Calls the recursive function to compute the result + return countSubsequences(str, 0, dp); + } + + /** + * Recursive helper function to count the number of unique subsequences + * starting from the given index. + * + * <p> Uses a HashSet to avoid counting duplicate characters within + * a single subsequence.</p> + * + * @param st the input string + * @param idx the current index from which to calculate subsequences + * @param dp dynamic programming array used to memoize results + * @return the total number of unique subsequences starting from the + * current index + */ + public static int countSubsequences(String st, int idx, int[] dp) { + + // Base case: when index exceeds the string length + if (idx >= st.length()) { + return 0; + } + + // If result is already calculated, return the memoized value + if (dp[idx] != -1) { + return dp[idx]; + } + + // Set to store characters to avoid duplicates + Set<Character> set = new HashSet<>(); + + int res = 0; + + // Iterate over the string starting from current index + for (int j = idx; j < st.length(); j++) { + + // If character is already in the set, skip it + if (set.contains(st.charAt(j))) { + continue; + } + + // Add character to set and recursively calculate subsequences + set.add(st.charAt(j)); + + // 1 for the current subsequence + recursive call for the rest of the string + res = 1 + countSubsequences(st, j + 1, dp) + res; + } + + // Memoize the result + dp[idx] = res; + return dp[idx]; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/WildcardMatching.java b/src/main/java/com/thealgorithms/dynamicprogramming/WildcardMatching.java new file mode 100644 index 000000000000..8e8bf3cc6606 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/WildcardMatching.java @@ -0,0 +1,57 @@ +/** + * + * Author: Janmesh Singh + * Github: https://github.com/janmeshjs + + * Problem Statement: To determine if the pattern matches the text. + * The pattern can include two special wildcard characters: + * ' ? ': Matches any single character. + * ' * ': Matches zero or more of any character sequence. + * + * Use DP to return True if the pattern matches the entire text and False otherwise + * + */ + +package com.thealgorithms.dynamicprogramming; + +public final class WildcardMatching { + private WildcardMatching() { + } + + public static boolean isMatch(String text, String pattern) { + int m = text.length(); + int n = pattern.length(); + + // Create a DP table to store intermediate results + boolean[][] dp = new boolean[m + 1][n + 1]; + + // Base case: an empty pattern matches an empty text + dp[0][0] = true; + + // Handle patterns starting with '*' + for (int j = 1; j <= n; j++) { + if (pattern.charAt(j - 1) == '*') { + dp[0][j] = dp[0][j - 1]; + } + } + + // Fill the DP table + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + char textChar = text.charAt(i - 1); + char patternChar = pattern.charAt(j - 1); + + if (patternChar == textChar || patternChar == '?') { + dp[i][j] = dp[i - 1][j - 1]; + } else if (patternChar == '*') { + // '*' can match zero or more characters + dp[i][j] = dp[i - 1][j] || dp[i][j - 1]; + } else { + dp[i][j] = false; + } + } + } + // The result is in the bottom-right cell of the DP table + return dp[m][n]; + } +} diff --git a/src/main/java/com/thealgorithms/dynamicprogramming/WineProblem.java b/src/main/java/com/thealgorithms/dynamicprogramming/WineProblem.java new file mode 100644 index 000000000000..022b43184a88 --- /dev/null +++ b/src/main/java/com/thealgorithms/dynamicprogramming/WineProblem.java @@ -0,0 +1,109 @@ +package com.thealgorithms.dynamicprogramming; + +/** + * The WineProblem class provides a solution to the wine selling problem. + * Given a collection of N wines with different prices, the objective is to maximize profit by selling + * one wine each year, considering the constraint that only the leftmost or rightmost wine can be sold + * at any given time. + * + * The price of the ith wine is pi, and the selling price increases by a factor of the year in which + * it is sold. This class implements three approaches to solve the problem: + * + * 1. **Recursion**: A straightforward recursive method that computes the maximum profit. + * - Time Complexity: O(2^N) + * - Space Complexity: O(N) due to recursive calls. + * + * 2. **Top-Down Dynamic Programming (Memoization)**: This approach caches the results of subproblems + * to avoid redundant computations. + * - Time Complexity: O(N^2) + * - Space Complexity: O(N^2) for the storage of results and O(N) for recursion stack. + * + * 3. **Bottom-Up Dynamic Programming (Tabulation)**: This method builds a table iteratively to + * compute the maximum profit for all possible subproblems. + * - Time Complexity: O(N^2) + * - Space Complexity: O(N^2) for the table. + */ +public final class WineProblem { + private WineProblem() { + } + + /** + * Calculate maximum profit using recursion. + * + * @param arr Array of wine prices. + * @param si Start index of the wine to consider. + * @param ei End index of the wine to consider. + * @return Maximum profit obtainable by selling the wines. + */ + public static int wpRecursion(int[] arr, int si, int ei) { + int n = arr.length; + int year = (n - (ei - si + 1)) + 1; + if (si == ei) { + return arr[si] * year; + } + + int start = wpRecursion(arr, si + 1, ei) + arr[si] * year; + int end = wpRecursion(arr, si, ei - 1) + arr[ei] * year; + + return Math.max(start, end); + } + + /** + * Calculate maximum profit using top-down dynamic programming with memoization. + * + * @param arr Array of wine prices. + * @param si Start index of the wine to consider. + * @param ei End index of the wine to consider. + * @param strg 2D array to store results of subproblems. + * @return Maximum profit obtainable by selling the wines. + */ + public static int wptd(int[] arr, int si, int ei, int[][] strg) { + int n = arr.length; + int year = (n - (ei - si + 1)) + 1; + if (si == ei) { + return arr[si] * year; + } + + if (strg[si][ei] != 0) { + return strg[si][ei]; + } + int start = wptd(arr, si + 1, ei, strg) + arr[si] * year; + int end = wptd(arr, si, ei - 1, strg) + arr[ei] * year; + + int ans = Math.max(start, end); + strg[si][ei] = ans; + + return ans; + } + + /** + * Calculate maximum profit using bottom-up dynamic programming with tabulation. + * + * @param arr Array of wine prices. + * @throws IllegalArgumentException if the input array is null or empty. + * @return Maximum profit obtainable by selling the wines. + */ + public static int wpbu(int[] arr) { + if (arr == null || arr.length == 0) { + throw new IllegalArgumentException("Input array cannot be null or empty."); + } + int n = arr.length; + int[][] strg = new int[n][n]; + + for (int slide = 0; slide <= n - 1; slide++) { + for (int si = 0; si <= n - slide - 1; si++) { + int ei = si + slide; + int year = (n - (ei - si + 1)) + 1; + if (si == ei) { + strg[si][ei] = arr[si] * year; + } else { + int start = strg[si + 1][ei] + arr[si] * year; + int end = strg[si][ei - 1] + arr[ei] * year; + + strg[si][ei] = Math.max(start, end); + } + } + } + return strg[0][n - 1]; + } +} diff --git a/src/main/java/com/thealgorithms/geometry/BresenhamLine.java b/src/main/java/com/thealgorithms/geometry/BresenhamLine.java new file mode 100644 index 000000000000..51d9930c0250 --- /dev/null +++ b/src/main/java/com/thealgorithms/geometry/BresenhamLine.java @@ -0,0 +1,69 @@ +package com.thealgorithms.geometry; + +import java.awt.Point; +import java.util.ArrayList; +import java.util.List; + +/** + * The {@code BresenhamLine} class implements the Bresenham's line algorithm, + * which is an efficient way to determine the points of a straight line + * between two given points in a 2D space. + * + * <p>This algorithm uses integer arithmetic to calculate the points, + * making it suitable for rasterization in computer graphics.</p> + * + * For more information, please visit {@link https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm} + */ +public final class BresenhamLine { + + private BresenhamLine() { + // Private constructor to prevent instantiation. + } + + /** + * Finds the list of points that form a straight line between two endpoints. + * + * @param x0 the x-coordinate of the starting point + * @param y0 the y-coordinate of the starting point + * @param x1 the x-coordinate of the ending point + * @param y1 the y-coordinate of the ending point + * @return a {@code List<Point>} containing all points on the line + */ + public static List<Point> findLine(int x0, int y0, int x1, int y1) { + List<Point> line = new ArrayList<>(); + + // Calculate differences and steps for each axis + int dx = Math.abs(x1 - x0); // Change in x + int dy = Math.abs(y1 - y0); // Change in y + int sx = (x0 < x1) ? 1 : -1; // Step in x direction + int sy = (y0 < y1) ? 1 : -1; // Step in y direction + int err = dx - dy; // Initial error term + + // Loop until we reach the endpoint + while (true) { + line.add(new Point(x0, y0)); // Add current point to the line + + // Check if we've reached the endpoint + if (x0 == x1 && y0 == y1) { + break; // Exit loop if endpoint is reached + } + + // Calculate error term doubled for decision making + final int e2 = err * 2; + + // Adjust x coordinate if necessary + if (e2 > -dy) { + err -= dy; // Update error term + x0 += sx; // Move to next point in x direction + } + + // Adjust y coordinate if necessary + if (e2 < dx) { + err += dx; // Update error term + y0 += sy; // Move to next point in y direction + } + } + + return line; // Return the list of points forming the line + } +} diff --git a/src/main/java/com/thealgorithms/geometry/ConvexHull.java b/src/main/java/com/thealgorithms/geometry/ConvexHull.java new file mode 100644 index 000000000000..17f400854c64 --- /dev/null +++ b/src/main/java/com/thealgorithms/geometry/ConvexHull.java @@ -0,0 +1,117 @@ +package com.thealgorithms.geometry; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +/** + * A class providing algorithms to compute the convex hull of a set of points + * using brute-force and recursive methods. + * + * Convex hull: The smallest convex polygon that contains all the given points. + * + * Algorithms provided: + * 1. Brute-Force Method + * 2. Recursive (Divide-and-Conquer) Method + * + * @author Hardvan + */ +public final class ConvexHull { + private ConvexHull() { + } + + private static boolean checkPointOrientation(Point i, Point j, Point k) { + int detK = Point.orientation(i, j, k); + if (detK > 0) { + return true; // pointsLeftOfIJ + } else if (detK < 0) { + return false; // pointsRightOfIJ + } else { + return k.compareTo(i) >= 0 && k.compareTo(j) <= 0; + } + } + + public static List<Point> convexHullBruteForce(List<Point> points) { + Set<Point> convexSet = new TreeSet<>(Comparator.naturalOrder()); + + for (int i = 0; i < points.size() - 1; i++) { + for (int j = i + 1; j < points.size(); j++) { + boolean allPointsOnOneSide = true; + boolean leftSide = checkPointOrientation(points.get(i), points.get(j), points.get((i + 1) % points.size())); + + for (int k = 0; k < points.size(); k++) { + if (k != i && k != j && checkPointOrientation(points.get(i), points.get(j), points.get(k)) != leftSide) { + allPointsOnOneSide = false; + break; + } + } + + if (allPointsOnOneSide) { + convexSet.add(points.get(i)); + convexSet.add(points.get(j)); + } + } + } + + return new ArrayList<>(convexSet); + } + + public static List<Point> convexHullRecursive(List<Point> points) { + Collections.sort(points); + Set<Point> convexSet = new HashSet<>(); + Point leftMostPoint = points.get(0); + Point rightMostPoint = points.get(points.size() - 1); + + convexSet.add(leftMostPoint); + convexSet.add(rightMostPoint); + + List<Point> upperHull = new ArrayList<>(); + List<Point> lowerHull = new ArrayList<>(); + + for (int i = 1; i < points.size() - 1; i++) { + int det = Point.orientation(leftMostPoint, rightMostPoint, points.get(i)); + if (det > 0) { + upperHull.add(points.get(i)); + } else if (det < 0) { + lowerHull.add(points.get(i)); + } + } + + constructHull(upperHull, leftMostPoint, rightMostPoint, convexSet); + constructHull(lowerHull, rightMostPoint, leftMostPoint, convexSet); + + List<Point> result = new ArrayList<>(convexSet); + Collections.sort(result); + return result; + } + + private static void constructHull(Collection<Point> points, Point left, Point right, Set<Point> convexSet) { + if (!points.isEmpty()) { + Point extremePoint = null; + int extremePointDistance = Integer.MIN_VALUE; + List<Point> candidatePoints = new ArrayList<>(); + + for (Point p : points) { + int det = Point.orientation(left, right, p); + if (det > 0) { + candidatePoints.add(p); + if (det > extremePointDistance) { + extremePointDistance = det; + extremePoint = p; + } + } + } + + if (extremePoint != null) { + constructHull(candidatePoints, left, extremePoint, convexSet); + convexSet.add(extremePoint); + constructHull(candidatePoints, extremePoint, right, convexSet); + } + } + } +} diff --git a/src/main/java/com/thealgorithms/geometry/GrahamScan.java b/src/main/java/com/thealgorithms/geometry/GrahamScan.java new file mode 100644 index 000000000000..1a373cf315a2 --- /dev/null +++ b/src/main/java/com/thealgorithms/geometry/GrahamScan.java @@ -0,0 +1,68 @@ +package com.thealgorithms.geometry; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Stack; + +/** + * A Java program that computes the convex hull using the Graham Scan algorithm. + * The time complexity is O(n) in the best case and O(n log(n)) in the worst case. + * The space complexity is O(n). + * This algorithm is applicable only to integral coordinates. + * + * References: + * https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/geometry/graham_scan_algorithm.cpp + * https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/geometry/graham_scan_functions.hpp + * https://algs4.cs.princeton.edu/99hull/GrahamScan.java.html + */ +public class GrahamScan { + + private final Stack<Point> hull = new Stack<>(); + + public GrahamScan(Point[] points) { + // Pre-process points: sort by y-coordinate, then by polar order with respect to the first point + Arrays.sort(points); + Arrays.sort(points, 1, points.length, points[0].polarOrder()); + + hull.push(points[0]); + + // Find the first point not equal to points[0] (firstNonEqualIndex) + // and the first point not collinear firstNonCollinearIndex with the previous points + int firstNonEqualIndex; + for (firstNonEqualIndex = 1; firstNonEqualIndex < points.length; firstNonEqualIndex++) { + if (!points[0].equals(points[firstNonEqualIndex])) { + break; + } + } + + if (firstNonEqualIndex == points.length) { + return; + } + + int firstNonCollinearIndex; + for (firstNonCollinearIndex = firstNonEqualIndex + 1; firstNonCollinearIndex < points.length; firstNonCollinearIndex++) { + if (Point.orientation(points[0], points[firstNonEqualIndex], points[firstNonCollinearIndex]) != 0) { + break; + } + } + + hull.push(points[firstNonCollinearIndex - 1]); + + // Process the remaining points and update the hull + for (int i = firstNonCollinearIndex; i < points.length; i++) { + Point top = hull.pop(); + while (Point.orientation(hull.peek(), top, points[i]) <= 0) { + top = hull.pop(); + } + hull.push(top); + hull.push(points[i]); + } + } + + /** + * @return An iterable collection of points representing the convex hull. + */ + public Iterable<Point> hull() { + return new ArrayList<>(hull); + } +} diff --git a/src/main/java/com/thealgorithms/geometry/MidpointCircle.java b/src/main/java/com/thealgorithms/geometry/MidpointCircle.java new file mode 100644 index 000000000000..803e8bb42b53 --- /dev/null +++ b/src/main/java/com/thealgorithms/geometry/MidpointCircle.java @@ -0,0 +1,85 @@ +package com.thealgorithms.geometry; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Class to represent the Midpoint Circle Algorithm. + * This algorithm calculates points on the circumference of a circle + * using integer arithmetic for efficient computation. + */ +public final class MidpointCircle { + + private MidpointCircle() { + // Private Constructor to prevent instantiation. + } + + /** + * Generates points on the circumference of a circle using the midpoint circle algorithm. + * + * @param centerX The x-coordinate of the circle's center. + * @param centerY The y-coordinate of the circle's center. + * @param radius The radius of the circle. + * @return A list of points on the circle, each represented as an int[] with 2 elements [x, y]. + */ + public static List<int[]> generateCirclePoints(int centerX, int centerY, int radius) { + List<int[]> points = new ArrayList<>(); + + // Special case for radius 0, only the center point should be added. + if (radius == 0) { + points.add(new int[] {centerX, centerY}); + return points; + } + + // Start at (radius, 0) + int x = radius; + int y = 0; + + // Decision parameter + int p = 1 - radius; + + // Add the initial points in all octants + addSymmetricPoints(points, centerX, centerY, x, y); + + // Iterate while x > y + while (x > y) { + y++; + + if (p <= 0) { + // Midpoint is inside or on the circle + p = p + 2 * y + 1; + } else { + // Midpoint is outside the circle + x--; + p = p + 2 * y - 2 * x + 1; + } + + // Add points for this (x, y) + addSymmetricPoints(points, centerX, centerY, x, y); + } + + return points; + } + + /** + * Adds the symmetric points in all octants of the circle based on the current x and y values. + * + * @param points The list to which symmetric points will be added. + * @param centerX The x-coordinate of the circle's center. + * @param centerY The y-coordinate of the circle's center. + * @param x The current x-coordinate on the circumference. + * @param y The current y-coordinate on the circumference. + */ + private static void addSymmetricPoints(Collection<int[]> points, int centerX, int centerY, int x, int y) { + // Octant symmetry points + points.add(new int[] {centerX + x, centerY + y}); + points.add(new int[] {centerX - x, centerY + y}); + points.add(new int[] {centerX + x, centerY - y}); + points.add(new int[] {centerX - x, centerY - y}); + points.add(new int[] {centerX + y, centerY + x}); + points.add(new int[] {centerX - y, centerY + x}); + points.add(new int[] {centerX + y, centerY - x}); + points.add(new int[] {centerX - y, centerY - x}); + } +} diff --git a/src/main/java/com/thealgorithms/geometry/MidpointEllipse.java b/src/main/java/com/thealgorithms/geometry/MidpointEllipse.java new file mode 100644 index 000000000000..fbd53c679960 --- /dev/null +++ b/src/main/java/com/thealgorithms/geometry/MidpointEllipse.java @@ -0,0 +1,131 @@ +package com.thealgorithms.geometry; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * The MidpointEllipse class implements the Midpoint Ellipse Drawing Algorithm. + * This algorithm efficiently computes the points on an ellipse by dividing it into two regions + * and using decision parameters to determine the next point to plot. + */ +public final class MidpointEllipse { + + private MidpointEllipse() { + // Private constructor to prevent instantiation + } + + /** + * Draws an ellipse using the Midpoint Ellipse Algorithm. + * + * @param centerX the x-coordinate of the center of the ellipse + * @param centerY the y-coordinate of the center of the ellipse + * @param a the length of the semi-major axis (horizontal radius) + * @param b the length of the semi-minor axis (vertical radius) + * @return a list of points (represented as int arrays) that form the ellipse + */ + public static List<int[]> drawEllipse(int centerX, int centerY, int a, int b) { + List<int[]> points = new ArrayList<>(); + + // Handle degenerate cases with early returns + if (a == 0 && b == 0) { + points.add(new int[] {centerX, centerY}); // Only the center point + return points; + } + + if (a == 0) { + // Semi-major axis is zero, create a vertical line + for (int y = centerY - b; y <= centerY + b; y++) { + points.add(new int[] {centerX, y}); + } + return points; // Early return + } + + if (b == 0) { + // Semi-minor axis is zero, create a horizontal line + for (int x = centerX - a; x <= centerX + a; x++) { + points.add(new int[] {x, centerY}); + } + return points; // Early return + } + + // Normal case: Non-degenerate ellipse + computeEllipsePoints(points, centerX, centerY, a, b); + + return points; // Return all calculated points of the ellipse + } + + /** + * Computes points of a non-degenerate ellipse using the Midpoint Ellipse Algorithm. + * + * @param points the list to which points will be added + * @param centerX the x-coordinate of the center of the ellipse + * @param centerY the y-coordinate of the center of the ellipse + * @param a the length of the semi-major axis (horizontal radius) + * @param b the length of the semi-minor axis (vertical radius) + */ + private static void computeEllipsePoints(Collection<int[]> points, int centerX, int centerY, int a, int b) { + int x = 0; // Initial x-coordinate + int y = b; // Initial y-coordinate + + // Region 1: Initial decision parameter + double d1 = (b * b) - (a * a * b) + (0.25 * a * a); // Decision variable for region 1 + double dx = 2.0 * b * b * x; // Change in x + double dy = 2.0 * a * a * y; // Change in y + + // Region 1: When the slope is less than 1 + while (dx < dy) { + addEllipsePoints(points, centerX, centerY, x, y); + + // Update decision parameter and variables + if (d1 < 0) { + x++; + dx += (2 * b * b); // Update x change + d1 += dx + (b * b); // Update decision parameter + } else { + x++; + y--; + dx += (2 * b * b); // Update x change + dy -= (2 * a * a); // Update y change + d1 += dx - dy + (b * b); // Update decision parameter + } + } + + // Region 2: Initial decision parameter for the second region + double d2 = b * b * (x + 0.5) * (x + 0.5) + a * a * (y - 1) * (y - 1) - a * a * b * b; + + // Region 2: When the slope is greater than or equal to 1 + while (y >= 0) { + addEllipsePoints(points, centerX, centerY, x, y); + + // Update decision parameter and variables + if (d2 > 0) { + y--; + dy -= (2 * a * a); // Update y change + d2 += (a * a) - dy; // Update decision parameter + } else { + y--; + x++; + dx += (2 * b * b); // Update x change + dy -= (2 * a * a); // Update y change + d2 += dx - dy + (a * a); // Update decision parameter + } + } + } + + /** + * Adds points for all four quadrants of the ellipse based on symmetry. + * + * @param points the list to which points will be added + * @param centerX the x-coordinate of the center of the ellipse + * @param centerY the y-coordinate of the center of the ellipse + * @param x the x-coordinate relative to the center + * @param y the y-coordinate relative to the center + */ + private static void addEllipsePoints(Collection<int[]> points, int centerX, int centerY, int x, int y) { + points.add(new int[] {centerX + x, centerY + y}); + points.add(new int[] {centerX - x, centerY + y}); + points.add(new int[] {centerX + x, centerY - y}); + points.add(new int[] {centerX - x, centerY - y}); + } +} diff --git a/src/main/java/com/thealgorithms/geometry/Point.java b/src/main/java/com/thealgorithms/geometry/Point.java new file mode 100644 index 000000000000..564edb4ba7b6 --- /dev/null +++ b/src/main/java/com/thealgorithms/geometry/Point.java @@ -0,0 +1,45 @@ +package com.thealgorithms.geometry; + +import java.util.Comparator; + +public record Point(int x, int y) implements Comparable<Point> { + + @Override + public int compareTo(Point other) { + int cmpY = Integer.compare(this.y, other.y); + return cmpY != 0 ? cmpY : Integer.compare(this.x, other.x); + } + + @Override + public String toString() { + return String.format("(%d, %d)", x, y); + } + + public Comparator<Point> polarOrder() { + return new PolarOrder(); + } + + public static int orientation(Point a, Point b, Point c) { + return Integer.compare((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x), 0); + } + + private final class PolarOrder implements Comparator<Point> { + @Override + public int compare(Point p1, Point p2) { + int dx1 = p1.x - x; + int dy1 = p1.y - y; + int dx2 = p2.x - x; + int dy2 = p2.y - y; + + if (dy1 >= 0 && dy2 < 0) { + return -1; // p1 above p2 + } else if (dy2 >= 0 && dy1 < 0) { + return 1; // p1 below p2 + } else if (dy1 == 0 && dy2 == 0) { // Collinear and horizontal + return Integer.compare(dx2, dx1); + } else { + return -orientation(Point.this, p1, p2); // Compare orientation + } + } + } +} diff --git a/src/main/java/com/thealgorithms/graph/ConstrainedShortestPath.java b/src/main/java/com/thealgorithms/graph/ConstrainedShortestPath.java new file mode 100644 index 000000000000..f397989911d9 --- /dev/null +++ b/src/main/java/com/thealgorithms/graph/ConstrainedShortestPath.java @@ -0,0 +1,123 @@ +package com.thealgorithms.graph; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * This class implements a solution for the Constrained Shortest Path Problem (CSPP). + * also known as Shortest Path Problem with Resource Constraints (SPPRC). + * The goal is to find the shortest path between two nodes while ensuring that + * the resource constraint is not exceeded. + * + * @author <a href="https://github.com/DenizAltunkapan">Deniz Altunkapan</a> + */ +public class ConstrainedShortestPath { + + /** + * Represents a graph using an adjacency list. + * This graph is designed for the Constrained Shortest Path Problem (CSPP). + */ + public static class Graph { + + private List<List<Edge>> adjacencyList; + + public Graph(int numNodes) { + adjacencyList = new ArrayList<>(); + for (int i = 0; i < numNodes; i++) { + adjacencyList.add(new ArrayList<>()); + } + } + + /** + * Adds an edge to the graph. + * @param from the starting node + * @param to the ending node + * @param cost the cost of the edge + * @param resource the resource required to traverse the edge + */ + public void addEdge(int from, int to, int cost, int resource) { + adjacencyList.get(from).add(new Edge(from, to, cost, resource)); + } + + /** + * Gets the edges that are adjacent to a given node. + * @param node the node to get the edges for + * @return the list of edges adjacent to the node + */ + public List<Edge> getEdges(int node) { + return adjacencyList.get(node); + } + + /** + * Gets the number of nodes in the graph. + * @return the number of nodes + */ + public int getNumNodes() { + return adjacencyList.size(); + } + + public record Edge(int from, int to, int cost, int resource) { + } + } + + private Graph graph; + private int maxResource; + + /** + * Constructs a CSPSolver with the given graph and maximum resource constraint. + * + * @param graph the graph representing the problem + * @param maxResource the maximum allowable resource + */ + public ConstrainedShortestPath(Graph graph, int maxResource) { + this.graph = graph; + this.maxResource = maxResource; + } + + /** + * Solves the CSP to find the shortest path from the start node to the target node + * without exceeding the resource constraint. + * + * @param start the starting node + * @param target the target node + * @return the minimum cost to reach the target node within the resource constraint, + * or -1 if no valid path exists + */ + public int solve(int start, int target) { + int numNodes = graph.getNumNodes(); + int[][] dp = new int[maxResource + 1][numNodes]; + + // Initialize dp table with maximum values + for (int i = 0; i <= maxResource; i++) { + Arrays.fill(dp[i], Integer.MAX_VALUE); + } + dp[0][start] = 0; + + // Dynamic Programming: Iterate over resources and nodes + for (int r = 0; r <= maxResource; r++) { + for (int u = 0; u < numNodes; u++) { + if (dp[r][u] == Integer.MAX_VALUE) { + continue; + } + for (Graph.Edge edge : graph.getEdges(u)) { + int v = edge.to(); + int cost = edge.cost(); + int resource = edge.resource(); + + if (r + resource <= maxResource) { + dp[r + resource][v] = Math.min(dp[r + resource][v], dp[r][u] + cost); + } + } + } + } + + // Find the minimum cost to reach the target node + int minCost = Integer.MAX_VALUE; + for (int r = 0; r <= maxResource; r++) { + minCost = Math.min(minCost, dp[r][target]); + } + + return minCost == Integer.MAX_VALUE ? -1 : minCost; + } +} diff --git a/src/main/java/com/thealgorithms/graph/StronglyConnectedComponentOptimized.java b/src/main/java/com/thealgorithms/graph/StronglyConnectedComponentOptimized.java new file mode 100644 index 000000000000..ba75b2f4b1b8 --- /dev/null +++ b/src/main/java/com/thealgorithms/graph/StronglyConnectedComponentOptimized.java @@ -0,0 +1,86 @@ +package com.thealgorithms.graph; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Stack; + +/** + * Finds the strongly connected components in a directed graph. + * + * @param adjList The adjacency list representation of the graph. + * @param n The number of nodes in the graph. + * @return The number of strongly connected components. + */ +public class StronglyConnectedComponentOptimized { + + public void btrack(HashMap<Integer, List<Integer>> adjList, int[] visited, Stack<Integer> dfsCallsNodes, int currentNode) { + visited[currentNode] = 1; + List<Integer> neighbors = adjList.get(currentNode); + // Check for null before iterating + if (neighbors != null) { + for (int neighbor : neighbors) { + if (visited[neighbor] == -1) { + btrack(adjList, visited, dfsCallsNodes, neighbor); + } + } + } + dfsCallsNodes.add(currentNode); + } + + public void btrack2(HashMap<Integer, List<Integer>> adjRevList, int[] visited, int currentNode, List<Integer> newScc) { + visited[currentNode] = 1; + newScc.add(currentNode); + List<Integer> neighbors = adjRevList.get(currentNode); + // Check for null before iterating + if (neighbors != null) { + for (int neighbor : neighbors) { + if (visited[neighbor] == -1) { + btrack2(adjRevList, visited, neighbor, newScc); + } + } + } + } + + public int getOutput(HashMap<Integer, List<Integer>> adjList, int n) { + int[] visited = new int[n]; + Arrays.fill(visited, -1); + Stack<Integer> dfsCallsNodes = new Stack<>(); + + for (int i = 0; i < n; i++) { + if (visited[i] == -1) { + btrack(adjList, visited, dfsCallsNodes, i); + } + } + + HashMap<Integer, List<Integer>> adjRevList = new HashMap<>(); + for (int i = 0; i < n; i++) { + adjRevList.put(i, new ArrayList<>()); + } + + for (int i = 0; i < n; i++) { + List<Integer> neighbors = adjList.get(i); + // Check for null before iterating + if (neighbors != null) { + for (int neighbor : neighbors) { + adjRevList.get(neighbor).add(i); + } + } + } + + Arrays.fill(visited, -1); + int stronglyConnectedComponents = 0; + + while (!dfsCallsNodes.isEmpty()) { + int node = dfsCallsNodes.pop(); + if (visited[node] == -1) { + List<Integer> newScc = new ArrayList<>(); + btrack2(adjRevList, visited, node, newScc); + stronglyConnectedComponents++; + } + } + + return stronglyConnectedComponents; + } +} diff --git a/src/main/java/com/thealgorithms/graph/TravelingSalesman.java b/src/main/java/com/thealgorithms/graph/TravelingSalesman.java new file mode 100644 index 000000000000..14bf89f57cf3 --- /dev/null +++ b/src/main/java/com/thealgorithms/graph/TravelingSalesman.java @@ -0,0 +1,155 @@ +package com.thealgorithms.graph; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * This class provides solutions to the Traveling Salesman Problem (TSP) using both brute-force and dynamic programming approaches. + * For more information, see <a href="https://en.wikipedia.org/wiki/Travelling_salesman_problem">Wikipedia</a>. + * @author <a href="https://github.com/DenizAltunkapan">Deniz Altunkapan</a> + */ + +public final class TravelingSalesman { + + // Private constructor to prevent instantiation + private TravelingSalesman() { + } + + /** + * Solves the Traveling Salesman Problem (TSP) using brute-force approach. + * This method generates all possible permutations of cities, calculates the total distance for each route, and returns the shortest distance found. + * + * @param distanceMatrix A square matrix where element [i][j] represents the distance from city i to city j. + * @return The shortest possible route distance visiting all cities exactly once and returning to the starting city. + */ + public static int bruteForce(int[][] distanceMatrix) { + if (distanceMatrix.length <= 1) { + return 0; + } + + List<Integer> cities = new ArrayList<>(); + for (int i = 1; i < distanceMatrix.length; i++) { + cities.add(i); + } + + List<List<Integer>> permutations = generatePermutations(cities); + int minDistance = Integer.MAX_VALUE; + + for (List<Integer> permutation : permutations) { + List<Integer> route = new ArrayList<>(); + route.add(0); + route.addAll(permutation); + int currentDistance = calculateDistance(distanceMatrix, route); + if (currentDistance < minDistance) { + minDistance = currentDistance; + } + } + + return minDistance; + } + + /** + * Computes the total distance of a given route. + * + * @param distanceMatrix A square matrix where element [i][j] represents the + * distance from city i to city j. + * @param route A list representing the order in which the cities are visited. + * @return The total distance of the route, or Integer.MAX_VALUE if the route is invalid. + */ + public static int calculateDistance(int[][] distanceMatrix, List<Integer> route) { + int distance = 0; + for (int i = 0; i < route.size() - 1; i++) { + int d = distanceMatrix[route.get(i)][route.get(i + 1)]; + if (d == Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + distance += d; + } + int returnDist = distanceMatrix[route.get(route.size() - 1)][route.get(0)]; + return (returnDist == Integer.MAX_VALUE) ? Integer.MAX_VALUE : distance + returnDist; + } + + /** + * Generates all permutations of a given list of cities. + * + * @param cities A list of cities to permute. + * @return A list of all possible permutations. + */ + private static List<List<Integer>> generatePermutations(List<Integer> cities) { + List<List<Integer>> permutations = new ArrayList<>(); + permute(cities, 0, permutations); + return permutations; + } + + /** + * Recursively generates permutations using backtracking. + * + * @param arr The list of cities. + * @param k The current index in the permutation process. + * @param output The list to store generated permutations. + */ + private static void permute(List<Integer> arr, int k, List<List<Integer>> output) { + if (k == arr.size()) { + output.add(new ArrayList<>(arr)); + return; + } + for (int i = k; i < arr.size(); i++) { + Collections.swap(arr, i, k); + permute(arr, k + 1, output); + Collections.swap(arr, i, k); + } + } + + /** + * Solves the Traveling Salesman Problem (TSP) using dynamic programming with the Held-Karp algorithm. + * + * @param distanceMatrix A square matrix where element [i][j] represents the distance from city i to city j. + * @return The shortest possible route distance visiting all cities exactly once and returning to the starting city. + * @throws IllegalArgumentException if the input matrix is not square. + */ + public static int dynamicProgramming(int[][] distanceMatrix) { + if (distanceMatrix.length == 0) { + return 0; + } + int n = distanceMatrix.length; + + for (int[] row : distanceMatrix) { + if (row.length != n) { + throw new IllegalArgumentException("Matrix must be square"); + } + } + + int[][] dp = new int[n][1 << n]; + for (int[] row : dp) { + Arrays.fill(row, Integer.MAX_VALUE); + } + dp[0][1] = 0; + + for (int mask = 1; mask < (1 << n); mask++) { + for (int u = 0; u < n; u++) { + if ((mask & (1 << u)) == 0 || dp[u][mask] == Integer.MAX_VALUE) { + continue; + } + for (int v = 0; v < n; v++) { + if ((mask & (1 << v)) != 0 || distanceMatrix[u][v] == Integer.MAX_VALUE) { + continue; + } + int newMask = mask | (1 << v); + dp[v][newMask] = Math.min(dp[v][newMask], dp[u][mask] + distanceMatrix[u][v]); + } + } + } + + int minDistance = Integer.MAX_VALUE; + int fullMask = (1 << n) - 1; + for (int i = 1; i < n; i++) { + if (dp[i][fullMask] != Integer.MAX_VALUE && distanceMatrix[i][0] != Integer.MAX_VALUE) { + minDistance = Math.min(minDistance, dp[i][fullMask] + distanceMatrix[i][0]); + } + } + + return minDistance == Integer.MAX_VALUE ? 0 : minDistance; + } +} diff --git a/src/main/java/com/thealgorithms/greedyalgorithms/ActivitySelection.java b/src/main/java/com/thealgorithms/greedyalgorithms/ActivitySelection.java new file mode 100644 index 000000000000..54ba4eb650a8 --- /dev/null +++ b/src/main/java/com/thealgorithms/greedyalgorithms/ActivitySelection.java @@ -0,0 +1,68 @@ +package com.thealgorithms.greedyalgorithms; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; + +// Problem Link: https://en.wikipedia.org/wiki/Activity_selection_problem + +public final class ActivitySelection { + + // Private constructor to prevent instantiation of the utility class + private ActivitySelection() { + } + + /** + * Function to perform activity selection using a greedy approach. + * + * The goal is to select the maximum number of activities that don't overlap + * with each other, based on their start and end times. Activities are chosen + * such that no two selected activities overlap. + * + * @param startTimes Array containing the start times of the activities. + * @param endTimes Array containing the end times of the activities. + * @return A list of indices representing the selected activities that can be + * performed without overlap. + */ + public static ArrayList<Integer> activitySelection(int[] startTimes, int[] endTimes) { + int n = startTimes.length; + + // Create a 2D array to store activity indices along with their start and end + // times. + // Each row represents an activity in the format: [activity index, start time, + // end time]. + int[][] activities = new int[n][3]; + + // Populate the 2D array with the activity index, start time, and end time. + for (int i = 0; i < n; i++) { + activities[i][0] = i; // Assign the activity index + activities[i][1] = startTimes[i]; // Assign the start time of the activity + activities[i][2] = endTimes[i]; // Assign the end time of the activity + } + + // Sort activities based on their end times in ascending order. + // This ensures that we always try to finish earlier activities first. + Arrays.sort(activities, Comparator.comparingDouble(activity -> activity[2])); + int lastEndTime; // Variable to store the end time of the last selected activity + // List to store the indices of selected activities + ArrayList<Integer> selectedActivities = new ArrayList<>(); + + // Select the first activity (as it has the earliest end time after sorting) + selectedActivities.add(activities[0][0]); // Add the first activity index to the result + lastEndTime = activities[0][2]; // Keep track of the end time of the last selected activity + + // Iterate over the sorted activities to select the maximum number of compatible + // activities. + for (int i = 1; i < n; i++) { + // If the start time of the current activity is greater than or equal to the + // end time of the last selected activity, it means there's no overlap. + if (activities[i][1] >= lastEndTime) { + selectedActivities.add(activities[i][0]); // Select this activity + lastEndTime = activities[i][2]; // Update the end time of the last selected activity + } + } + + // Return the list of selected activity indices. + return selectedActivities; + } +} diff --git a/src/main/java/com/thealgorithms/greedyalgorithms/BandwidthAllocation.java b/src/main/java/com/thealgorithms/greedyalgorithms/BandwidthAllocation.java new file mode 100644 index 000000000000..602cdcd5ad40 --- /dev/null +++ b/src/main/java/com/thealgorithms/greedyalgorithms/BandwidthAllocation.java @@ -0,0 +1,58 @@ +package com.thealgorithms.greedyalgorithms; + +import java.util.Arrays; + +/** + * Class to solve the Bandwidth Allocation Problem. + * The goal is to maximize the value gained by allocating bandwidth to users. + * Example: + * Bandwidth = 10 + * Users = [3, 5, 7] + * Values = [10, 20, 30] + * The maximum value achievable is 40 by allocating 3 units to user 0 and 7 units to user 2. + * + * @author Hardvan + */ +public final class BandwidthAllocation { + private BandwidthAllocation() { + } + + /** + * Allocates bandwidth to maximize value. + * Steps: + * 1. Calculate the ratio of value/demand for each user. + * 2. Sort the users in descending order of the ratio. + * 3. Allocate bandwidth to users in order of the sorted list. + * 4. If the bandwidth is not enough to allocate the full demand of a user, allocate a fraction of the demand. + * 5. Return the maximum value achievable. + * + * @param bandwidth total available bandwidth to allocate + * @param users array of user demands + * @param values array of values associated with each user's demand + * @return the maximum value achievable + */ + public static int maxValue(int bandwidth, int[] users, int[] values) { + int n = users.length; + double[][] ratio = new double[n][2]; // {index, ratio} + + for (int i = 0; i < n; i++) { + ratio[i][0] = i; + ratio[i][1] = (double) values[i] / users[i]; + } + + Arrays.sort(ratio, (a, b) -> Double.compare(b[1], a[1])); + + int maxValue = 0; + for (int i = 0; i < n; i++) { + int index = (int) ratio[i][0]; + if (bandwidth >= users[index]) { + maxValue += values[index]; + bandwidth -= users[index]; + } else { + maxValue += (int) (ratio[i][1] * bandwidth); + break; + } + } + return maxValue; + } +} diff --git a/src/main/java/com/thealgorithms/greedyalgorithms/BinaryAddition.java b/src/main/java/com/thealgorithms/greedyalgorithms/BinaryAddition.java new file mode 100644 index 000000000000..074c76b9f33f --- /dev/null +++ b/src/main/java/com/thealgorithms/greedyalgorithms/BinaryAddition.java @@ -0,0 +1,75 @@ +package com.thealgorithms.greedyalgorithms; + +import java.util.Collections; + +/** + * BinaryAddition class to perform binary addition of two binary strings. + */ +public class BinaryAddition { + /** + * Computes the sum of two binary characters and a carry. + * @param a First binary character ('0' or '1'). + * @param b Second binary character ('0' or '1'). + * @param carry The carry from the previous operation ('0' or '1'). + * @return The sum as a binary character ('0' or '1'). + */ + public char sum(char a, char b, char carry) { + int count = 0; + if (a == '1') { + count++; + } + if (b == '1') { + count++; + } + if (carry == '1') { + count++; + } + return count % 2 == 0 ? '0' : '1'; + } + /** + * Computes the carry for the next higher bit from two binary characters and a carry. + * @param a First binary character ('0' or '1'). + * @param b Second binary character ('0' or '1'). + * @param carry The carry from the previous operation ('0' or '1'). + * @return The carry for the next bit ('0' or '1'). + */ + public char carry(char a, char b, char carry) { + int count = 0; + if (a == '1') { + count++; + } + if (b == '1') { + count++; + } + if (carry == '1') { + count++; + } + return count >= 2 ? '1' : '0'; + } + /** + * Adds two binary strings and returns their sum as a binary string. + * @param a First binary string. + * @param b Second binary string. + * @return Binary string representing the sum of the two binary inputs. + */ + public String addBinary(String a, String b) { + // Padding the shorter string with leading zeros + int maxLength = Math.max(a.length(), b.length()); + a = String.join("", Collections.nCopies(maxLength - a.length(), "0")) + a; + b = String.join("", Collections.nCopies(maxLength - b.length(), "0")) + b; + StringBuilder result = new StringBuilder(); + char carry = '0'; + // Iterating over the binary strings from the least significant to the most significant bit + for (int i = maxLength - 1; i >= 0; i--) { + char sum = sum(a.charAt(i), b.charAt(i), carry); + carry = carry(a.charAt(i), b.charAt(i), carry); + result.append(sum); + } + // If there's a remaining carry, append it + if (carry == '1') { + result.append('1'); + } + // Reverse the result as we constructed it from the least significant bit + return result.reverse().toString(); + } +} diff --git a/src/main/java/com/thealgorithms/greedyalgorithms/CoinChange.java b/src/main/java/com/thealgorithms/greedyalgorithms/CoinChange.java new file mode 100644 index 000000000000..8054581d21d7 --- /dev/null +++ b/src/main/java/com/thealgorithms/greedyalgorithms/CoinChange.java @@ -0,0 +1,35 @@ +package com.thealgorithms.greedyalgorithms; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; + +// Problem Link : https://en.wikipedia.org/wiki/Change-making_problem + +public final class CoinChange { + private CoinChange() { + } + // Function to solve the coin change problem + public static ArrayList<Integer> coinChangeProblem(int amount) { + // Define an array of coin denominations in descending order + Integer[] coins = {1, 2, 5, 10, 20, 50, 100, 500, 2000}; + + // Sort the coin denominations in descending order + Arrays.sort(coins, Comparator.reverseOrder()); + + ArrayList<Integer> ans = new ArrayList<>(); // List to store selected coins + + // Iterate through the coin denominations + for (int i = 0; i < coins.length; i++) { + // Check if the current coin denomination can be used to reduce the remaining amount + if (coins[i] <= amount) { + // Repeatedly subtract the coin denomination from the remaining amount + while (coins[i] <= amount) { + ans.add(coins[i]); // Add the coin to the list of selected coins + amount -= coins[i]; // Update the remaining amount + } + } + } + return ans; + } +} diff --git a/src/main/java/com/thealgorithms/greedyalgorithms/DigitSeparation.java b/src/main/java/com/thealgorithms/greedyalgorithms/DigitSeparation.java new file mode 100644 index 000000000000..bee5f98cd2ee --- /dev/null +++ b/src/main/java/com/thealgorithms/greedyalgorithms/DigitSeparation.java @@ -0,0 +1,40 @@ +package com.thealgorithms.greedyalgorithms; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * This class provides methods to separate the digits of a large positive number into a list. + */ +public class DigitSeparation { + public DigitSeparation() { + } + /** + * Separates the digits of a large positive number into a list in reverse order. + * @param largeNumber The large number to separate digits from. + * @return A list of digits in reverse order. + */ + public List<Long> digitSeparationReverseOrder(long largeNumber) { + List<Long> result = new ArrayList<>(); + if (largeNumber != 0) { + while (largeNumber != 0) { + result.add(Math.abs(largeNumber % 10)); + largeNumber = largeNumber / 10; + } + } else { + result.add(0L); + } + return result; + } + /** + * Separates the digits of a large positive number into a list in forward order. + * @param largeNumber The large number to separate digits from. + * @return A list of digits in forward order. + */ + public List<Long> digitSeparationForwardOrder(long largeNumber) { + List<Long> result = this.digitSeparationReverseOrder(largeNumber); + Collections.reverse(result); + return result; + } +} diff --git a/src/main/java/com/thealgorithms/greedyalgorithms/EgyptianFraction.java b/src/main/java/com/thealgorithms/greedyalgorithms/EgyptianFraction.java new file mode 100644 index 000000000000..35cbfe876b05 --- /dev/null +++ b/src/main/java/com/thealgorithms/greedyalgorithms/EgyptianFraction.java @@ -0,0 +1,35 @@ +package com.thealgorithms.greedyalgorithms; + +import java.util.ArrayList; +import java.util.List; + +/** + * Class to represent a fraction as a sum of unique unit fractions. + * Example: + * 2/3 = 1/2 + 1/6 + * 3/10 = 1/4 + 1/20 + * + * @author Hardvan + */ +public final class EgyptianFraction { + private EgyptianFraction() { + } + + /** + * Calculates the Egyptian Fraction representation of a given fraction. + * + * @param numerator the numerator of the fraction + * @param denominator the denominator of the fraction + * @return List of unit fractions represented as strings "1/x" + */ + public static List<String> getEgyptianFraction(int numerator, int denominator) { + List<String> result = new ArrayList<>(); + while (numerator != 0) { + int x = (int) Math.ceil((double) denominator / numerator); + result.add("1/" + x); + numerator = numerator * x - denominator; + denominator = denominator * x; + } + return result; + } +} diff --git a/src/main/java/com/thealgorithms/greedyalgorithms/FractionalKnapsack.java b/src/main/java/com/thealgorithms/greedyalgorithms/FractionalKnapsack.java new file mode 100644 index 000000000000..9535a7c6190e --- /dev/null +++ b/src/main/java/com/thealgorithms/greedyalgorithms/FractionalKnapsack.java @@ -0,0 +1,54 @@ +package com.thealgorithms.greedyalgorithms; + +import java.util.Arrays; +import java.util.Comparator; + +/** + * The FractionalKnapsack class provides a method to solve the fractional knapsack problem + * using a greedy algorithm approach. It allows for selecting fractions of items to maximize + * the total value in a knapsack with a given weight capacity. + * + * The problem consists of a set of items, each with a weight and a value, and a knapsack + * that can carry a maximum weight. The goal is to maximize the value of items in the knapsack, + * allowing for the inclusion of fractions of items. + * + * Problem Link: https://en.wikipedia.org/wiki/Continuous_knapsack_problem + */ +public final class FractionalKnapsack { + private FractionalKnapsack() { + } + + /** + * Computes the maximum value that can be accommodated in a knapsack of a given capacity. + * + * @param weight an array of integers representing the weights of the items + * @param value an array of integers representing the values of the items + * @param capacity an integer representing the maximum weight capacity of the knapsack + * @return the maximum value that can be obtained by including the items in the knapsack + */ + public static int fractionalKnapsack(int[] weight, int[] value, int capacity) { + double[][] ratio = new double[weight.length][2]; + + for (int i = 0; i < weight.length; i++) { + ratio[i][0] = i; + ratio[i][1] = value[i] / (double) weight[i]; + } + + Arrays.sort(ratio, Comparator.comparingDouble(o -> o[1])); + + int finalValue = 0; + double current = capacity; + + for (int i = ratio.length - 1; i >= 0; i--) { + int index = (int) ratio[i][0]; + if (current >= weight[index]) { + finalValue += value[index]; + current -= weight[index]; + } else { + finalValue += (int) (ratio[i][1] * current); + break; + } + } + return finalValue; + } +} diff --git a/src/main/java/com/thealgorithms/greedyalgorithms/GaleShapley.java b/src/main/java/com/thealgorithms/greedyalgorithms/GaleShapley.java new file mode 100644 index 000000000000..a4a0366375eb --- /dev/null +++ b/src/main/java/com/thealgorithms/greedyalgorithms/GaleShapley.java @@ -0,0 +1,65 @@ +package com.thealgorithms.greedyalgorithms; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; + +/** + * Implementation of the Gale-Shapley Algorithm for Stable Matching. + * Problem link: https://en.wikipedia.org/wiki/Stable_marriage_problem + */ +public final class GaleShapley { + + private GaleShapley() { + } + + /** + * Function to find stable matches between men and women. + * + * @param womenPrefs A map containing women's preferences where each key is a woman and the value is an array of men in order of preference. + * @param menPrefs A map containing men's preferences where each key is a man and the value is an array of women in order of preference. + * @return A map containing stable matches where the key is a woman and the value is her matched man. + */ + public static Map<String, String> stableMatch(Map<String, LinkedList<String>> womenPrefs, Map<String, LinkedList<String>> menPrefs) { + // Initialize all men as free + Map<String, String> engagements = new HashMap<>(); + LinkedList<String> freeMen = new LinkedList<>(menPrefs.keySet()); + + // While there are free men + while (!freeMen.isEmpty()) { + String man = freeMen.poll(); // Get the first free man + LinkedList<String> manPref = menPrefs.get(man); // Get the preferences of the man + + // Check if manPref is null or empty + if (manPref == null || manPref.isEmpty()) { + continue; // Skip if no preferences + } + + // Propose to the first woman in the man's preference list + String woman = manPref.poll(); + String fiance = engagements.get(woman); + + // If the woman is not engaged, engage her with the current man + if (fiance == null) { + engagements.put(woman, man); + } else { + // If the woman prefers the current man over her current fiance + LinkedList<String> womanPrefList = womenPrefs.get(woman); + + // Check if womanPrefList is null + if (womanPrefList == null) { + continue; // Skip if no preferences for the woman + } + + if (womanPrefList.indexOf(man) < womanPrefList.indexOf(fiance)) { + engagements.put(woman, man); + freeMen.add(fiance); // Previous fiance becomes free + } else { + // Woman rejects the new proposal, the man remains free + freeMen.add(man); + } + } + } + return engagements; // Return the stable matches + } +} diff --git a/src/main/java/com/thealgorithms/greedyalgorithms/JobSequencing.java b/src/main/java/com/thealgorithms/greedyalgorithms/JobSequencing.java new file mode 100644 index 000000000000..ceb664a31e8a --- /dev/null +++ b/src/main/java/com/thealgorithms/greedyalgorithms/JobSequencing.java @@ -0,0 +1,66 @@ +package com.thealgorithms.greedyalgorithms; + +import java.util.ArrayList; +import java.util.Arrays; + +// Problem Link: https://en.wikipedia.org/wiki/Job-shop_scheduling + +public final class JobSequencing { + private JobSequencing() { + } + + // Define a Job class that implements Comparable for sorting by profit in descending order + static class Job implements Comparable<Job> { + char id; + int deadline; + int profit; + + // Compare jobs by profit in descending order + @Override + public int compareTo(Job otherJob) { + return otherJob.profit - this.profit; + } + + Job(char id, int deadline, int profit) { + this.id = id; + this.deadline = deadline; + this.profit = profit; + } + } + + // Function to print the job sequence + public static String findJobSequence(ArrayList<Job> jobs, int size) { + Boolean[] slots = new Boolean[size]; + Arrays.fill(slots, Boolean.FALSE); + + int[] result = new int[size]; + + // Iterate through jobs to find the optimal job sequence + for (int i = 0; i < size; i++) { + for (int j = jobs.get(i).deadline - 1; j >= 0; j--) { + if (!slots[j]) { + result[j] = i; + slots[j] = Boolean.TRUE; + break; + } + } + } + + // Create a StringBuilder to build the job sequence string + StringBuilder jobSequenceBuilder = new StringBuilder(); + jobSequenceBuilder.append("Job Sequence: "); + for (int i = 0; i < jobs.size(); i++) { + if (slots[i]) { + jobSequenceBuilder.append(jobs.get(result[i]).id).append(" -> "); + } + } + + // Remove the trailing " -> " from the job sequence + if (jobSequenceBuilder.length() >= 4) { + jobSequenceBuilder.setLength(jobSequenceBuilder.length() - 4); + } + + // Return the job sequence as a string + return jobSequenceBuilder.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/greedyalgorithms/KCenters.java b/src/main/java/com/thealgorithms/greedyalgorithms/KCenters.java new file mode 100644 index 000000000000..152b36053345 --- /dev/null +++ b/src/main/java/com/thealgorithms/greedyalgorithms/KCenters.java @@ -0,0 +1,62 @@ +package com.thealgorithms.greedyalgorithms; + +import java.util.Arrays; + +/** + * Given a set of points and a number k. + * The goal is to minimize the maximum distance between any point and its nearest center. + * Each point is assigned to the nearest center. + * The distance between two points is the Euclidean distance. + * The problem is NP-hard. + * + * @author Hardvan + */ +public final class KCenters { + private KCenters() { + } + + /** + * Finds the maximum distance to the nearest center given k centers. + * Steps: + * 1. Initialize an array {@code selected} of size n and an array {@code maxDist} of size n. + * 2. Set the first node as selected and update the maxDist array. + * 3. For each center, find the farthest node from the selected centers. + * 4. Update the maxDist array. + * 5. Return the maximum distance to the nearest center. + * + * @param distances matrix representing distances between nodes + * @param k the number of centers + * @return the maximum distance to the nearest center + */ + public static int findKCenters(int[][] distances, int k) { + int n = distances.length; + boolean[] selected = new boolean[n]; + int[] maxDist = new int[n]; + + Arrays.fill(maxDist, Integer.MAX_VALUE); + + selected[0] = true; + for (int i = 1; i < n; i++) { + maxDist[i] = Math.min(maxDist[i], distances[0][i]); + } + + for (int centers = 1; centers < k; centers++) { + int farthest = -1; + for (int i = 0; i < n; i++) { + if (!selected[i] && (farthest == -1 || maxDist[i] > maxDist[farthest])) { + farthest = i; + } + } + selected[farthest] = true; + for (int i = 0; i < n; i++) { + maxDist[i] = Math.min(maxDist[i], distances[farthest][i]); + } + } + + int result = 0; + for (int dist : maxDist) { + result = Math.max(result, dist); + } + return result; + } +} diff --git a/src/main/java/com/thealgorithms/greedyalgorithms/MergeIntervals.java b/src/main/java/com/thealgorithms/greedyalgorithms/MergeIntervals.java new file mode 100644 index 000000000000..07bd0b73326f --- /dev/null +++ b/src/main/java/com/thealgorithms/greedyalgorithms/MergeIntervals.java @@ -0,0 +1,64 @@ +package com.thealgorithms.greedyalgorithms; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Problem Statement: + * Given an array of intervals where intervals[i] = [starti, endi]. + * + * Merge all overlapping intervals and return an array of the non-overlapping + * intervals + * that cover all the intervals in the input. + */ +public final class MergeIntervals { + + /** + * Private constructor to prevent instantiation of this utility class. + */ + private MergeIntervals() { + } + + /** + * Merges overlapping intervals from the given array of intervals. + * + * The method sorts the intervals by their start time, then iterates through the + * sorted intervals + * and merges overlapping intervals. If an interval overlaps with the last + * merged interval, + * it updates the end time of the last merged interval. Otherwise, it adds the + * interval as a new entry. + * + * @param intervals A 2D array representing intervals where each element is an + * interval [starti, endi]. + * @return A 2D array of merged intervals where no intervals overlap. + * + * Example: + * Input: {{1, 3}, {2, 6}, {8, 10}, {15, 18}} + * Output: {{1, 6}, {8, 10}, {15, 18}} + */ + public static int[][] merge(int[][] intervals) { + // Sort the intervals by their start time (ascending order) + Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0])); + + // List to store merged intervals + List<int[]> merged = new ArrayList<>(); + + for (int[] interval : intervals) { // Each interval + // If the merged list is empty or the current interval does not overlap with + // the last merged interval, add it to the merged list. + if (merged.isEmpty() || interval[0] > merged.get(merged.size() - 1)[1]) { + merged.add(interval); + } else { + // If there is an overlap, merge the intervals by updating the end time + // of the last merged interval to the maximum end time between the two + // intervals. + merged.get(merged.size() - 1)[1] = Math.max(merged.get(merged.size() - 1)[1], interval[1]); + } + } + + // Convert the list of merged intervals back to a 2D array and return it + return merged.toArray(new int[merged.size()][]); + } +} diff --git a/src/main/java/com/thealgorithms/greedyalgorithms/MinimizingLateness.java b/src/main/java/com/thealgorithms/greedyalgorithms/MinimizingLateness.java new file mode 100644 index 000000000000..c7f219ef3eab --- /dev/null +++ b/src/main/java/com/thealgorithms/greedyalgorithms/MinimizingLateness.java @@ -0,0 +1,45 @@ +package com.thealgorithms.greedyalgorithms; + +import java.util.Arrays; + +public final class MinimizingLateness { + private MinimizingLateness() { + } + + public static class Job { + String jobName; + int startTime = 0; + int lateness = 0; + int processingTime; + int deadline; + + public Job(String jobName, int processingTime, int deadline) { + this.jobName = jobName; + this.processingTime = processingTime; + this.deadline = deadline; + } + + public static Job of(String jobName, int processingTime, int deadline) { + return new Job(jobName, processingTime, deadline); + } + + @Override + public String toString() { + return String.format("%s, startTime: %d, endTime: %d, lateness: %d", jobName, startTime, processingTime + startTime, lateness); + } + } + + static void calculateLateness(Job... jobs) { + + // sort the jobs based on their deadline + Arrays.sort(jobs, (a, b) -> a.deadline - b.deadline); + + int startTime = 0; + + for (Job job : jobs) { + job.startTime = startTime; + startTime += job.processingTime; + job.lateness = Math.max(0, startTime - job.deadline); // if the job finishes before deadline the lateness is 0 + } + } +} diff --git a/src/main/java/com/thealgorithms/greedyalgorithms/MinimumWaitingTime.java b/src/main/java/com/thealgorithms/greedyalgorithms/MinimumWaitingTime.java new file mode 100644 index 000000000000..2341bcdee9f7 --- /dev/null +++ b/src/main/java/com/thealgorithms/greedyalgorithms/MinimumWaitingTime.java @@ -0,0 +1,37 @@ +package com.thealgorithms.greedyalgorithms; + +import java.util.Arrays; + +/** + * The MinimumWaitingTime class provides a method to calculate the minimum + * waiting time for a list of queries using a greedy algorithm. + * + * @author Hardvan + */ +public final class MinimumWaitingTime { + private MinimumWaitingTime() { + } + + /** + * Calculates the minimum waiting time for a list of queries. + * The function sorts the queries in non-decreasing order and then calculates + * the waiting time for each query based on its position in the sorted list. + * + * @param queries an array of integers representing the query times in picoseconds + * @return the minimum waiting time in picoseconds + */ + public static int minimumWaitingTime(int[] queries) { + int n = queries.length; + if (n <= 1) { + return 0; + } + + Arrays.sort(queries); + + int totalWaitingTime = 0; + for (int i = 0; i < n; i++) { + totalWaitingTime += queries[i] * (n - i - 1); + } + return totalWaitingTime; + } +} diff --git a/src/main/java/com/thealgorithms/greedyalgorithms/OptimalFileMerging.java b/src/main/java/com/thealgorithms/greedyalgorithms/OptimalFileMerging.java new file mode 100644 index 000000000000..7a1f2edf8180 --- /dev/null +++ b/src/main/java/com/thealgorithms/greedyalgorithms/OptimalFileMerging.java @@ -0,0 +1,52 @@ +package com.thealgorithms.greedyalgorithms; + +import java.util.PriorityQueue; + +/** + * Class to solve the Optimal File Merging Problem. + * The goal is to minimize the cost of merging files, where the cost of merging two files is the sum of their sizes. + * The cost of merging all files is the sum of the costs of merging each pair of files. + * Example: + * files = [4, 3, 2, 6] + * The minimum cost to merge all files is 29. + * Steps: + * 1. Merge files 2 and 3 (cost = 2 + 3 = 5). New files = [4, 5, 6] + * 2. Merge files 4 and 5 (cost = 4 + 5 = 9). New files = [6, 9] + * 3. Merge files 6 and 9 (cost = 6 + 9 = 15). New files = [15] + * Total cost = 5 + 9 + 15 = 29 + * + * @author Hardvan + */ +public final class OptimalFileMerging { + private OptimalFileMerging() { + } + + /** + * Calculates the minimum cost to merge all files. + * Steps: + * 1. Add all files to a min heap. + * 2. Remove the two smallest files from the heap, merge them, and add the result back to the heap. + * 3. Repeat step 2 until there is only one file left in the heap. + * 4. The total cost is the sum of all the costs of merging the files. + * + * @param files array of file sizes + * @return the minimum cost to merge the files + */ + public static int minMergeCost(int[] files) { + PriorityQueue<Integer> minHeap = new PriorityQueue<>(); + for (int file : files) { + minHeap.add(file); + } + + int totalCost = 0; + while (minHeap.size() > 1) { + int first = minHeap.poll(); + int second = minHeap.poll(); + int cost = first + second; + totalCost += cost; + + minHeap.add(cost); + } + return totalCost; + } +} diff --git a/src/main/java/com/thealgorithms/greedyalgorithms/StockProfitCalculator.java b/src/main/java/com/thealgorithms/greedyalgorithms/StockProfitCalculator.java new file mode 100644 index 000000000000..01950d902b4f --- /dev/null +++ b/src/main/java/com/thealgorithms/greedyalgorithms/StockProfitCalculator.java @@ -0,0 +1,34 @@ +package com.thealgorithms.greedyalgorithms; + +/** + * The StockProfitCalculator class provides a method to calculate the maximum profit + * that can be made from a single buy and sell of one share of stock. + * The approach uses a greedy algorithm to efficiently determine the maximum profit. + * + * @author Hardvan + */ +public final class StockProfitCalculator { + private StockProfitCalculator() { + } + + /** + * Calculates the maximum profit from a list of stock prices. + * + * @param prices an array of integers representing the stock prices on different days + * @return the maximum profit that can be achieved from a single buy and sell + * transaction, or 0 if no profit can be made + */ + public static int maxProfit(int[] prices) { + if (prices == null || prices.length == 0) { + return 0; + } + + int minPrice = prices[0]; + int maxProfit = 0; + for (int price : prices) { + minPrice = Math.min(price, minPrice); + maxProfit = Math.max(price - minPrice, maxProfit); + } + return maxProfit; + } +} diff --git a/src/main/java/com/thealgorithms/io/BufferedReader.java b/src/main/java/com/thealgorithms/io/BufferedReader.java new file mode 100644 index 000000000000..66673fe281ae --- /dev/null +++ b/src/main/java/com/thealgorithms/io/BufferedReader.java @@ -0,0 +1,197 @@ +package com.thealgorithms.io; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * Mimics the actions of the Original buffered reader + * implements other actions, such as peek(n) to lookahead, + * block() to read a chunk of size {BUFFER SIZE} + * <p> + * Author: Kumaraswamy B.G (Xoma Dev) + */ +public class BufferedReader { + + private static final int DEFAULT_BUFFER_SIZE = 5; + + /** + * The maximum number of bytes the buffer can hold. + * Value is changed when encountered Eof to not + * cause overflow read of 0 bytes + */ + + private int bufferSize; + private final byte[] buffer; + + /** + * posRead -> indicates the next byte to read + */ + private int posRead = 0; + private int bufferPos = 0; + + private boolean foundEof = false; + + private InputStream input; + + public BufferedReader(byte[] input) throws IOException { + this(new ByteArrayInputStream(input)); + } + + public BufferedReader(InputStream input) throws IOException { + this(input, DEFAULT_BUFFER_SIZE); + } + + public BufferedReader(InputStream input, int bufferSize) throws IOException { + this.input = input; + if (input.available() == -1) { + throw new IOException("Empty or already closed stream provided"); + } + + this.bufferSize = bufferSize; + buffer = new byte[bufferSize]; + } + + /** + * Reads a single byte from the stream + */ + public int read() throws IOException { + if (needsRefill()) { + if (foundEof) { + return -1; + } + // the buffer is empty, or the buffer has + // been completely read and needs to be refilled + refill(); + } + return buffer[posRead++] & 0xff; // read and un-sign it + } + + /** + * Number of bytes not yet been read + */ + + public int available() throws IOException { + int available = input.available(); + if (needsRefill()) { + // since the block is already empty, + // we have no responsibility yet + return available; + } + return bufferPos - posRead + available; + } + + /** + * Returns the next character + */ + + public int peek() throws IOException { + return peek(1); + } + + /** + * Peeks and returns a value located at next {n} + */ + + public int peek(int n) throws IOException { + int available = available(); + if (n >= available) { + throw new IOException("Out of range, available %d, but trying with %d".formatted(available, n)); + } + pushRefreshData(); + + if (n >= bufferSize) { + throw new IllegalAccessError("Cannot peek %s, maximum upto %s (Buffer Limit)".formatted(n, bufferSize)); + } + return buffer[n]; + } + + /** + * Removes the already read bytes from the buffer + * in-order to make space for new bytes to be filled up. + * <p> + * This may also do the job to read first time data (the whole buffer is empty) + */ + + private void pushRefreshData() throws IOException { + for (int i = posRead, j = 0; i < bufferSize; i++, j++) { + buffer[j] = buffer[i]; + } + + bufferPos -= posRead; + posRead = 0; + + // fill out the spaces that we've + // emptied + justRefill(); + } + + /** + * Reads one complete block of size {bufferSize} + * if found eof, the total length of an array will + * be that of what's available + * + * @return a completed block + */ + public byte[] readBlock() throws IOException { + pushRefreshData(); + + byte[] cloned = new byte[bufferSize]; + // arraycopy() function is better than clone() + if (bufferPos >= 0) { + System.arraycopy(buffer, 0, cloned, 0, + // important to note that, bufferSize does not stay constant + // once the class is defined. See justRefill() function + bufferSize); + } + // we assume that already a chunk + // has been read + refill(); + return cloned; + } + + private boolean needsRefill() { + return bufferPos == 0 || posRead == bufferSize; + } + + private void refill() throws IOException { + posRead = 0; + bufferPos = 0; + justRefill(); + } + + private void justRefill() throws IOException { + assertStreamOpen(); + + // try to fill in the maximum we can until + // we reach EOF + while (bufferPos < bufferSize) { + int read = input.read(); + if (read == -1) { + // reached end-of-file, no more data left + // to be read + foundEof = true; + // rewrite the BUFFER_SIZE, to know that we've reached + // EOF when requested refill + bufferSize = bufferPos; + } + buffer[bufferPos++] = (byte) read; + } + } + + private void assertStreamOpen() { + if (input == null) { + throw new IllegalStateException("Input Stream already closed!"); + } + } + + public void close() throws IOException { + if (input != null) { + try { + input.close(); + } finally { + input = null; + } + } + } +} diff --git a/src/main/java/com/thealgorithms/lineclipping/CohenSutherland.java b/src/main/java/com/thealgorithms/lineclipping/CohenSutherland.java new file mode 100644 index 000000000000..6e8611b86332 --- /dev/null +++ b/src/main/java/com/thealgorithms/lineclipping/CohenSutherland.java @@ -0,0 +1,133 @@ +package com.thealgorithms.lineclipping; + +import com.thealgorithms.lineclipping.utils.Line; +import com.thealgorithms.lineclipping.utils.Point; + +/** + * @author shikarisohan + * @since 10/4/24 + * Cohen-Sutherland Line Clipping Algorithm + * + * This algorithm is used to clip a line segment to a rectangular window. + * It assigns a region code to each endpoint of the line segment, and + * then efficiently determines whether the line segment is fully inside, + * fully outside, or partially inside the window. + * + * Reference: + * https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm + * + * Clipping window boundaries are defined as (xMin, yMin) and (xMax, yMax). + * The algorithm computes the clipped line segment if it's partially or + * fully inside the clipping window. + */ +public class CohenSutherland { + + // Region codes for the 9 regions + private static final int INSIDE = 0; // 0000 + private static final int LEFT = 1; // 0001 + private static final int RIGHT = 2; // 0010 + private static final int BOTTOM = 4; // 0100 + private static final int TOP = 8; // 1000 + + // Define the clipping window + double xMin; + double yMin; + double xMax; + double yMax; + + public CohenSutherland(double xMin, double yMin, double xMax, double yMax) { + this.xMin = xMin; + this.yMin = yMin; + this.xMax = xMax; + this.yMax = yMax; + } + + // Compute the region code for a point (x, y) + private int computeCode(double x, double y) { + int code = INSIDE; + + if (x < xMin) // to the left of rectangle + { + code |= LEFT; + } else if (x > xMax) // to the right of rectangle + { + code |= RIGHT; + } + if (y < yMin) // below the rectangle + { + code |= BOTTOM; + } else if (y > yMax) // above the rectangle + { + code |= TOP; + } + + return code; + } + + // Cohen-Sutherland algorithm to return the clipped line + public Line cohenSutherlandClip(Line line) { + double x1 = line.start.x; + double y1 = line.start.y; + double x2 = line.end.x; + double y2 = line.end.y; + + int code1 = computeCode(x1, y1); + int code2 = computeCode(x2, y2); + boolean accept = false; + + while (true) { + if ((code1 == 0) && (code2 == 0)) { + // Both points are inside the rectangle + accept = true; + break; + } else if ((code1 & code2) != 0) { + // Both points are outside the rectangle in the same region + break; + } else { + // Some segment of the line is inside the rectangle + double x = 0; + double y = 0; + + // Pick an endpoint that is outside the rectangle + int codeOut = (code1 != 0) ? code1 : code2; + + // Find the intersection point using the line equation + if ((codeOut & TOP) != 0) { + // Point is above the rectangle + x = x1 + (x2 - x1) * (yMax - y1) / (y2 - y1); + y = yMax; + } else if ((codeOut & BOTTOM) != 0) { + // Point is below the rectangle + x = x1 + (x2 - x1) * (yMin - y1) / (y2 - y1); + y = yMin; + } else if ((codeOut & RIGHT) != 0) { + // Point is to the right of the rectangle + y = y1 + (y2 - y1) * (xMax - x1) / (x2 - x1); + x = xMax; + } else if ((codeOut & LEFT) != 0) { + // Point is to the left of the rectangle + y = y1 + (y2 - y1) * (xMin - x1) / (x2 - x1); + x = xMin; + } + + // Replace the point outside the rectangle with the intersection point + if (codeOut == code1) { + x1 = x; + y1 = y; + code1 = computeCode(x1, y1); + } else { + x2 = x; + y2 = y; + code2 = computeCode(x2, y2); + } + } + } + + if (accept) { + return new Line(new Point(x1, y1), new Point(x2, y2)); + } else { + + return null; // The line is fully rejected + } + } +} diff --git a/src/main/java/com/thealgorithms/lineclipping/LiangBarsky.java b/src/main/java/com/thealgorithms/lineclipping/LiangBarsky.java new file mode 100644 index 000000000000..723e2bb2fbf9 --- /dev/null +++ b/src/main/java/com/thealgorithms/lineclipping/LiangBarsky.java @@ -0,0 +1,93 @@ +package com.thealgorithms.lineclipping; + +import com.thealgorithms.lineclipping.utils.Line; +import com.thealgorithms.lineclipping.utils.Point; + +/** + * @author shikarisohan + * @since 10/5/24 + * + * * The Liang-Barsky line clipping algorithm is an efficient algorithm for + * * line clipping against a rectangular window. It is based on the parametric + * * equation of a line and checks the intersections of the line with the + * * window boundaries. This algorithm calculates the intersection points, + * * if any, and returns the clipped line that lies inside the window. + * * + * * Reference: + * * https://en.wikipedia.org/wiki/Liang%E2%80%93Barsky_algorithm + * + * Clipping window boundaries are defined as (xMin, yMin) and (xMax, yMax). + * The algorithm computes the clipped line segment if it's partially or + * fully inside the clipping window. + */ +public class LiangBarsky { + + // Define the clipping window + double xMin; + double xMax; + double yMin; + double yMax; + + public LiangBarsky(double xMin, double yMin, double xMax, double yMax) { + this.xMin = xMin; + this.yMin = yMin; + this.xMax = xMax; + this.yMax = yMax; + } + + // Liang-Barsky algorithm to return the clipped line + public Line liangBarskyClip(Line line) { + double dx = line.end.x - line.start.x; + double dy = line.end.y - line.start.y; + + double[] p = {-dx, dx, -dy, dy}; + double[] q = {line.start.x - xMin, xMax - line.start.x, line.start.y - yMin, yMax - line.start.y}; + + double[] resultT = clipLine(p, q); + + if (resultT == null) { + return null; // Line is outside the clipping window + } + + return calculateClippedLine(line, resultT[0], resultT[1], dx, dy); + } + + // clip the line by adjusting t0 and t1 for each edge + private double[] clipLine(double[] p, double[] q) { + double t0 = 0.0; + double t1 = 1.0; + + for (int i = 0; i < 4; i++) { + double t = q[i] / p[i]; + if (p[i] == 0 && q[i] < 0) { + return null; // Line is outside the boundary + } else if (p[i] < 0) { + if (t > t1) { + return null; + } // Line is outside + if (t > t0) { + t0 = t; + } // Update t0 + } else if (p[i] > 0) { + if (t < t0) { + return null; + } // Line is outside + if (t < t1) { + t1 = t; + } // Update t1 + } + } + + return new double[] {t0, t1}; // Return valid t0 and t1 + } + + // calculate the clipped line based on t0 and t1 + private Line calculateClippedLine(Line line, double t0, double t1, double dx, double dy) { + double clippedX1 = line.start.x + t0 * dx; + double clippedY1 = line.start.y + t0 * dy; + double clippedX2 = line.start.x + t1 * dx; + double clippedY2 = line.start.y + t1 * dy; + + return new Line(new Point(clippedX1, clippedY1), new Point(clippedX2, clippedY2)); + } +} diff --git a/src/main/java/com/thealgorithms/lineclipping/utils/Line.java b/src/main/java/com/thealgorithms/lineclipping/utils/Line.java new file mode 100644 index 000000000000..56cd52e3cdce --- /dev/null +++ b/src/main/java/com/thealgorithms/lineclipping/utils/Line.java @@ -0,0 +1,43 @@ +package com.thealgorithms.lineclipping.utils; + +import java.util.Objects; + +/** + * @author moksedursohan + * @since 10/4/24 + */ +public class Line { + + public Point start; + public Point end; + + public Line() { + } + + public Line(Point start, Point end) { + this.start = start; + this.end = end; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Line line)) { + return false; + } + + return Objects.equals(start, line.start) && Objects.equals(end, line.end); + } + + @Override + public int hashCode() { + return Objects.hash(start, end); + } + + @Override + public String toString() { + return "Line from " + start + " to " + end; + } +} diff --git a/src/main/java/com/thealgorithms/lineclipping/utils/Point.java b/src/main/java/com/thealgorithms/lineclipping/utils/Point.java new file mode 100644 index 000000000000..7ef58c783903 --- /dev/null +++ b/src/main/java/com/thealgorithms/lineclipping/utils/Point.java @@ -0,0 +1,43 @@ +package com.thealgorithms.lineclipping.utils; + +import java.util.Objects; + +/** + * @author moksedursohan + * @since 10/4/24 + */ +public class Point { + + public double x; + public double y; + + public Point() { + } + + public Point(double x, double y) { + this.x = x; + this.y = y; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Point point)) { + return false; + } + + return Double.compare(x, point.x) == 0 && Double.compare(y, point.y) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(x, y); + } + + @Override + public String toString() { + return "(" + x + ", " + y + ")"; + } +} diff --git a/src/main/java/com/thealgorithms/maths/ADTFraction.java b/src/main/java/com/thealgorithms/maths/ADTFraction.java new file mode 100644 index 000000000000..a85ef09079c9 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/ADTFraction.java @@ -0,0 +1,80 @@ +package com.thealgorithms.maths; + +public record ADTFraction(int numerator, int denominator) { + /** + * Initializes a newly created {@code ADTFraction} object so that it represents + * a fraction with the {@code numerator} and {@code denominator} provided as arguments. + * + * @param numerator The fraction numerator + * @param denominator The fraction denominator + */ + public ADTFraction { + if (denominator == 0) { + throw new IllegalArgumentException("Denominator cannot be 0"); + } + } + + /** + * Add two fractions. + * + * @param fraction the {@code ADTFraction} to add + * @return A new {@code ADTFraction} containing the result of the operation + */ + public ADTFraction plus(ADTFraction fraction) { + var numerator = this.denominator * fraction.numerator + this.numerator * fraction.denominator; + var denominator = this.denominator * fraction.denominator; + return new ADTFraction(numerator, denominator); + } + + /** + * Multiply fraction by a number. + * + * @param number the number to multiply + * @return A new {@code ADTFraction} containing the result of the operation + */ + public ADTFraction times(int number) { + return times(new ADTFraction(number, 1)); + } + + /** + * Multiply two fractions. + * + * @param fraction the {@code ADTFraction} to multiply + * @return A new {@code ADTFraction} containing the result of the operation + */ + public ADTFraction times(ADTFraction fraction) { + var numerator = this.numerator * fraction.numerator; + var denominator = this.denominator * fraction.denominator; + return new ADTFraction(numerator, denominator); + } + + /** + * Generates the reciprocal of the fraction. + * + * @return A new {@code ADTFraction} with the {@code numerator} and {@code denominator} switched + */ + public ADTFraction reciprocal() { + return new ADTFraction(this.denominator, this.numerator); + } + + /** + * Calculates the result of the fraction. + * + * @return The numerical result of the division between {@code numerator} and {@code + * denominator} + */ + public float value() { + return (float) this.numerator / this.denominator; + } + + /** + * Returns a string representation of this {@code ADTFraction} in the format + * {@code numerator}/{@code denominator}. + * + * @return A string representation of this {@code ADTFraction} + */ + @Override + public String toString() { + return String.format("%d/%d", this.numerator, this.denominator); + } +} diff --git a/src/main/java/com/thealgorithms/maths/AbsoluteMax.java b/src/main/java/com/thealgorithms/maths/AbsoluteMax.java new file mode 100644 index 000000000000..c32a408b6609 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/AbsoluteMax.java @@ -0,0 +1,26 @@ +package com.thealgorithms.maths; + +public final class AbsoluteMax { + private AbsoluteMax() { + } + + /** + * Finds the absolute maximum value among the given numbers. + * + * @param numbers The numbers to compare. + * @return The absolute maximum value. + * @throws IllegalArgumentException If the input array is empty or null. + */ + public static int getMaxValue(int... numbers) { + if (numbers == null || numbers.length == 0) { + throw new IllegalArgumentException("Numbers array cannot be empty or null"); + } + int absMax = numbers[0]; + for (int i = 1; i < numbers.length; i++) { + if (Math.abs(numbers[i]) > Math.abs(absMax) || (Math.abs(numbers[i]) == Math.abs(absMax) && numbers[i] > absMax)) { + absMax = numbers[i]; + } + } + return absMax; + } +} diff --git a/src/main/java/com/thealgorithms/maths/AbsoluteMin.java b/src/main/java/com/thealgorithms/maths/AbsoluteMin.java new file mode 100644 index 000000000000..1b9575a330dd --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/AbsoluteMin.java @@ -0,0 +1,26 @@ +package com.thealgorithms.maths; + +import java.util.Arrays; + +public final class AbsoluteMin { + private AbsoluteMin() { + } + + /** + * Compares the numbers given as arguments to get the absolute min value. + * + * @param numbers The numbers to compare + * @return The absolute min value + */ + public static int getMinValue(int... numbers) { + if (numbers.length == 0) { + throw new IllegalArgumentException("Numbers array cannot be empty"); + } + + var absMinWrapper = new Object() { int value = numbers[0]; }; + + Arrays.stream(numbers).skip(1).filter(number -> Math.abs(number) <= Math.abs(absMinWrapper.value)).forEach(number -> absMinWrapper.value = Math.min(absMinWrapper.value, number)); + + return absMinWrapper.value; + } +} diff --git a/src/main/java/com/thealgorithms/maths/AbsoluteValue.java b/src/main/java/com/thealgorithms/maths/AbsoluteValue.java new file mode 100644 index 000000000000..b9279d5a244a --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/AbsoluteValue.java @@ -0,0 +1,16 @@ +package com.thealgorithms.maths; + +public final class AbsoluteValue { + private AbsoluteValue() { + } + + /** + * Returns the absolute value of a number. + * + * @param number The number to be transformed + * @return The absolute value of the {@code number} + */ + public static int getAbsValue(int number) { + return number < 0 ? -number : number; + } +} diff --git a/src/main/java/com/thealgorithms/maths/AliquotSum.java b/src/main/java/com/thealgorithms/maths/AliquotSum.java new file mode 100644 index 000000000000..996843b56826 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/AliquotSum.java @@ -0,0 +1,63 @@ +package com.thealgorithms.maths; + +import java.util.stream.IntStream; + +/** + * In number theory, the aliquot sum s(n) of a positive integer n is the sum of + * all proper divisors of n, that is, all divisors of n other than n itself. For + * example, the proper divisors of 15 (that is, the positive divisors of 15 that + * are not equal to 15) are 1, 3 and 5, so the aliquot sum of 15 is 9 i.e. (1 + + * 3 + 5). Wikipedia: https://en.wikipedia.org/wiki/Aliquot_sum + */ +public final class AliquotSum { + private AliquotSum() { + } + + /** + * Finds the aliquot sum of an integer number. + * + * @param number a positive integer + * @return aliquot sum of given {@code number} + */ + public static int getAliquotValue(int number) { + var sumWrapper = new Object() { int value = 0; }; + + IntStream.iterate(1, i -> ++i).limit(number / 2).filter(i -> number % i == 0).forEach(i -> sumWrapper.value += i); + + return sumWrapper.value; + } + + /** + * Function to calculate the aliquot sum of an integer number + * + * @param n a positive integer + * @return aliquot sum of given {@code number} + */ + public static int getAliquotSum(int n) { + if (n <= 0) { + return -1; + } + int sum = 1; + double root = Math.sqrt(n); + /* + * We can get the factors after the root by dividing number by its factors + * before the root. + * Ex- Factors of 100 are 1, 2, 4, 5, 10, 20, 25, 50 and 100. + * Root of 100 is 10. So factors before 10 are 1, 2, 4 and 5. + * Now by dividing 100 by each factor before 10 we get: + * 100/1 = 100, 100/2 = 50, 100/4 = 25 and 100/5 = 20 + * So we get 100, 50, 25 and 20 which are factors of 100 after 10 + */ + for (int i = 2; i <= root; i++) { + if (n % i == 0) { + sum += i + n / i; + } + } + // if n is a perfect square then its root was added twice in above loop, so subtracting root + // from sum + if (root == (int) root) { + sum -= (int) root; + } + return sum; + } +} diff --git a/src/main/java/com/thealgorithms/maths/AmicableNumber.java b/src/main/java/com/thealgorithms/maths/AmicableNumber.java new file mode 100644 index 000000000000..b30831bfdc58 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/AmicableNumber.java @@ -0,0 +1,71 @@ +package com.thealgorithms.maths; + +import java.util.LinkedHashSet; +import java.util.Set; +import org.apache.commons.lang3.tuple.Pair; + +/** + * Amicable numbers are two different natural numbers that the sum of the + * proper divisors of each is equal to the other number. + * (A proper divisor of a number is a positive factor of that number other than the number itself. + * For example, the proper divisors of 6 are 1, 2, and 3.) + * A pair of amicable numbers constitutes an aliquot sequence of period 2. + * It is unknown if there are infinitely many pairs of amicable numbers. + * + * <p> + * link: https://en.wikipedia.org/wiki/Amicable_numbers + * <p> + * Simple Example: (220, 284) + * 220 is divisible by {1,2,4,5,10,11,20,22,44,55,110} <-SUM = 284 + * 284 is divisible by {1,2,4,71,142} <-SUM = 220. + */ +public final class AmicableNumber { + private AmicableNumber() { + } + /** + * Finds all the amicable numbers in a given range. + * + * @param from range start value + * @param to range end value (inclusive) + * @return list with amicable numbers found in given range. + */ + public static Set<Pair<Integer, Integer>> findAllInRange(int from, int to) { + if (from <= 0 || to <= 0 || to < from) { + throw new IllegalArgumentException("Given range of values is invalid!"); + } + + Set<Pair<Integer, Integer>> result = new LinkedHashSet<>(); + + for (int i = from; i < to; i++) { + for (int j = i + 1; j <= to; j++) { + if (isAmicableNumber(i, j)) { + result.add(Pair.of(i, j)); + } + } + } + return result; + } + + /** + * Checks whether 2 numbers are AmicableNumbers or not. + */ + public static boolean isAmicableNumber(int a, int b) { + if (a <= 0 || b <= 0) { + throw new IllegalArgumentException("Input numbers must be natural!"); + } + return sumOfDividers(a, a) == b && sumOfDividers(b, b) == a; + } + + /** + * Recursively calculates the sum of all dividers for a given number excluding the divider itself. + */ + private static int sumOfDividers(int number, int divisor) { + if (divisor == 1) { + return 0; + } else if (number % --divisor == 0) { + return sumOfDividers(number, divisor) + divisor; + } else { + return sumOfDividers(number, divisor); + } + } +} diff --git a/src/main/java/com/thealgorithms/maths/Area.java b/src/main/java/com/thealgorithms/maths/Area.java new file mode 100644 index 000000000000..7a06fd5e5fa0 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/Area.java @@ -0,0 +1,195 @@ +package com.thealgorithms.maths; + +/** + * Find the area of various geometric shapes + */ +public final class Area { + private Area() { + } + + /** + * String of IllegalArgumentException for radius + */ + private static final String POSITIVE_RADIUS = "Must be a positive radius"; + + /** + * String of IllegalArgumentException for height + */ + private static final String POSITIVE_HEIGHT = "Must be a positive height"; + + /** + * String of IllegalArgumentException for base + */ + private static final String POSITIVE_BASE = "Must be a positive base"; + + /** + * Calculate the surface area of a cube. + * + * @param sideLength side length of cube + * @return surface area of given cube + */ + public static double surfaceAreaCube(final double sideLength) { + if (sideLength <= 0) { + throw new IllegalArgumentException("Must be a positive sideLength"); + } + return 6 * sideLength * sideLength; + } + + /** + * Calculate the surface area of a sphere. + * + * @param radius radius of sphere + * @return surface area of given sphere + */ + public static double surfaceAreaSphere(final double radius) { + if (radius <= 0) { + throw new IllegalArgumentException(POSITIVE_RADIUS); + } + return 4 * Math.PI * radius * radius; + } + + /** + * Calculate the area of a rectangle. + * + * @param length length of a rectangle + * @param width width of a rectangle + * @return area of given rectangle + */ + public static double surfaceAreaRectangle(final double length, final double width) { + if (length <= 0) { + throw new IllegalArgumentException("Must be a positive length"); + } + if (width <= 0) { + throw new IllegalArgumentException("Must be a positive width"); + } + return length * width; + } + + /** + * Calculate surface area of a cylinder. + * + * @param radius radius of the floor + * @param height height of the cylinder. + * @return volume of given cylinder + */ + public static double surfaceAreaCylinder(final double radius, final double height) { + if (radius <= 0) { + throw new IllegalArgumentException(POSITIVE_RADIUS); + } + if (height <= 0) { + throw new IllegalArgumentException(POSITIVE_RADIUS); + } + return 2 * (Math.PI * radius * radius + Math.PI * radius * height); + } + + /** + * Calculate the area of a square. + * + * @param sideLength side length of square + * @return area of given square + */ + public static double surfaceAreaSquare(final double sideLength) { + if (sideLength <= 0) { + throw new IllegalArgumentException("Must be a positive sideLength"); + } + return sideLength * sideLength; + } + + /** + * Calculate the area of a triangle. + * + * @param base base of triangle + * @param height height of triangle + * @return area of given triangle + */ + public static double surfaceAreaTriangle(final double base, final double height) { + if (base <= 0) { + throw new IllegalArgumentException(POSITIVE_BASE); + } + if (height <= 0) { + throw new IllegalArgumentException(POSITIVE_HEIGHT); + } + return base * height / 2; + } + + /** + * Calculate the area of a parallelogram. + * + * @param base base of a parallelogram + * @param height height of a parallelogram + * @return area of given parallelogram + */ + public static double surfaceAreaParallelogram(final double base, final double height) { + if (base <= 0) { + throw new IllegalArgumentException(POSITIVE_BASE); + } + if (height <= 0) { + throw new IllegalArgumentException(POSITIVE_HEIGHT); + } + return base * height; + } + + /** + * Calculate the area of a trapezium. + * + * @param base1 upper base of trapezium + * @param base2 bottom base of trapezium + * @param height height of trapezium + * @return area of given trapezium + */ + public static double surfaceAreaTrapezium(final double base1, final double base2, final double height) { + if (base1 <= 0) { + throw new IllegalArgumentException(POSITIVE_BASE + 1); + } + if (base2 <= 0) { + throw new IllegalArgumentException(POSITIVE_BASE + 2); + } + if (height <= 0) { + throw new IllegalArgumentException(POSITIVE_HEIGHT); + } + return (base1 + base2) * height / 2; + } + + /** + * Calculate the area of a circle. + * + * @param radius radius of circle + * @return area of given circle + */ + public static double surfaceAreaCircle(final double radius) { + if (radius <= 0) { + throw new IllegalArgumentException(POSITIVE_RADIUS); + } + return Math.PI * radius * radius; + } + + /** + * Calculate the surface area of a hemisphere. + * + * @param radius radius of hemisphere + * @return surface area of given hemisphere + */ + public static double surfaceAreaHemisphere(final double radius) { + if (radius <= 0) { + throw new IllegalArgumentException(POSITIVE_RADIUS); + } + return 3 * Math.PI * radius * radius; + } + + /** + * Calculate the surface area of a cone. + * + * @param radius radius of cone. + * @param height of cone. + * @return surface area of given cone. + */ + public static double surfaceAreaCone(final double radius, final double height) { + if (radius <= 0) { + throw new IllegalArgumentException(POSITIVE_RADIUS); + } + if (height <= 0) { + throw new IllegalArgumentException(POSITIVE_HEIGHT); + } + return Math.PI * radius * (radius + Math.pow(height * height + radius * radius, 0.5)); + } +} diff --git a/src/main/java/com/thealgorithms/maths/Armstrong.java b/src/main/java/com/thealgorithms/maths/Armstrong.java new file mode 100644 index 000000000000..9a7a014ec99f --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/Armstrong.java @@ -0,0 +1,39 @@ +package com.thealgorithms.maths; + +/** + * This class checks whether a given number is an Armstrong number or not. + * An Armstrong number is a number that is equal to the sum of its own digits, + * each raised to the power of the number of digits. + * + * For example, 370 is an Armstrong number because 3^3 + 7^3 + 0^3 = 370. + * 1634 is an Armstrong number because 1^4 + 6^4 + 3^4 + 4^4 = 1634. + * An Armstrong number is often called a Narcissistic number. + * + * @author satyabarghav + * @modifier rahul katteda - (13/01/2025) - [updated the logic for getting total number of digits] + */ +public class Armstrong { + + /** + * Checks whether a given number is an Armstrong number or not. + * + * @param number the number to check + * @return {@code true} if the given number is an Armstrong number, {@code false} otherwise + */ + public boolean isArmstrong(int number) { + if (number < 0) { + return false; // Negative numbers cannot be Armstrong numbers + } + long sum = 0; + int totalDigits = (int) Math.log10(number) + 1; // get the length of the number (number of digits) + long originalNumber = number; + + while (originalNumber > 0) { + long digit = originalNumber % 10; + sum += (long) Math.pow(digit, totalDigits); // The digit raised to the power of total number of digits and added to the sum. + originalNumber /= 10; + } + + return sum == number; + } +} diff --git a/src/main/java/com/thealgorithms/maths/AutoCorrelation.java b/src/main/java/com/thealgorithms/maths/AutoCorrelation.java new file mode 100644 index 000000000000..344a1271e11c --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/AutoCorrelation.java @@ -0,0 +1,57 @@ +package com.thealgorithms.maths; + +/** + * Class for linear auto-correlation of a discrete signal + * + * @author Athina-Frederiki Swinkels + * @version 2.0 + */ + +public final class AutoCorrelation { + private AutoCorrelation() { + } + + /** + * Discrete linear auto-correlation function. + * Input and output signals have starting index 0. + * + * @param x The discrete signal + * @return The result of the auto-correlation of signals x. The result is also a signal. + */ + public static double[] autoCorrelation(double[] x) { + + /* + To find the auto-correlation of a discrete signal x, we perform cross-correlation between x signal and itself. + Here's an example: + x=[1,2,1,1] + y=[1,2,1,1] + + i=0: [1,2,1,1] + [1,2,1,1] result[0]=1*1=1 + + i=1: [1,2,1,1] + [1,2,1,1] result[1]=1*1+2*1=3 + + i=2: [1,2,1,1] + [1,2,1,1] result[2]=1*2+2*1+1*1=5 + + i=3: [1,2,1,1] + [1,2,1,1] result[3]=1*1+2*2+1*1+1*1=7 + + i=4: [1,2,1,1] + [1,2,1,1] result[4]=2*1+1*2+1*1=5 + + i=5: [1,2,1,1] + [1,2,1,1] result[5]=1*1+1*2=3 + + i=1: [1,2,1,1] + [1,2,1,1] result[6]=1*1=1 + + result=[1,3,5,7,5,3,1] + + + */ + + return CrossCorrelation.crossCorrelation(x, x); + } +} diff --git a/src/main/java/com/thealgorithms/maths/AutomorphicNumber.java b/src/main/java/com/thealgorithms/maths/AutomorphicNumber.java new file mode 100644 index 000000000000..31f81da63f03 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/AutomorphicNumber.java @@ -0,0 +1,67 @@ +package com.thealgorithms.maths; + +import java.math.BigInteger; +/** + * <a href="https://en.wikipedia.org/wiki/Automorphic_number">Automorphic Number</a> + * A number is said to be an Automorphic, if it is present in the last digit(s) + * of its square. Example- Let the number be 25, its square is 625. Since, + * 25(The input number) is present in the last two digits of its square(625), it + * is an Automorphic Number. + */ +public final class AutomorphicNumber { + private AutomorphicNumber() { + } + + /** + * A function to check if a number is Automorphic number or not + * + * @param n The number to be checked + * @return {@code true} if {@code a} is Automorphic number, otherwise + * {@code false} + */ + public static boolean isAutomorphic(long n) { + if (n < 0) { + return false; + } + long square = n * n; // Calculating square of the number + long t = n; + long numberOfdigits = 0; + while (t > 0) { + numberOfdigits++; // Calculating number of digits in n + t /= 10; + } + long lastDigits = square % (long) Math.pow(10, numberOfdigits); // Extracting last Digits of square + return n == lastDigits; + } + + /** + * A function to check if a number is Automorphic number or not by using String functions + * + * @param n The number to be checked + * @return {@code true} if {@code a} is Automorphic number, otherwise + * {@code false} + */ + public static boolean isAutomorphic2(long n) { + if (n < 0) { + return false; + } + long square = n * n; // Calculating square of the number + return String.valueOf(square).endsWith(String.valueOf(n)); + } + + /** + * A function to check if a number is Automorphic number or not by using BigInteger + * + * @param s The number in String to be checked + * @return {@code true} if {@code a} is Automorphic number, otherwise + * {@code false} + */ + public static boolean isAutomorphic3(String s) { + BigInteger n = new BigInteger(s); + if (n.signum() == -1) { + return false; // if number is negative, return false + } + BigInteger square = n.multiply(n); // Calculating square of the number + return String.valueOf(square).endsWith(String.valueOf(n)); + } +} diff --git a/src/main/java/com/thealgorithms/maths/Average.java b/src/main/java/com/thealgorithms/maths/Average.java new file mode 100644 index 000000000000..a550a7f6504d --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/Average.java @@ -0,0 +1,50 @@ +package com.thealgorithms.maths; + +/** + * A utility class for computing the average of numeric arrays. + * This class provides static methods to calculate the average of arrays + * of both {@code double} and {@code int} values. + */ +public final class Average { + + // Prevent instantiation of this utility class + private Average() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated."); + } + + /** + * Computes the average of a {@code double} array. + * + * @param numbers an array of {@code double} values + * @return the average of the given numbers + * @throws IllegalArgumentException if the input array is {@code null} or empty + */ + public static double average(double[] numbers) { + if (numbers == null || numbers.length == 0) { + throw new IllegalArgumentException("Numbers array cannot be empty or null"); + } + double sum = 0; + for (double number : numbers) { + sum += number; + } + return sum / numbers.length; + } + + /** + * Computes the average of an {@code int} array. + * + * @param numbers an array of {@code int} values + * @return the average of the given numbers + * @throws IllegalArgumentException if the input array is {@code null} or empty + */ + public static long average(int[] numbers) { + if (numbers == null || numbers.length == 0) { + throw new IllegalArgumentException("Numbers array cannot be empty or null"); + } + long sum = 0; + for (int number : numbers) { + sum += number; + } + return sum / numbers.length; + } +} diff --git a/src/main/java/com/thealgorithms/maths/BinaryPow.java b/src/main/java/com/thealgorithms/maths/BinaryPow.java new file mode 100644 index 000000000000..1550376782b1 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/BinaryPow.java @@ -0,0 +1,26 @@ +package com.thealgorithms.maths; + +public final class BinaryPow { + private BinaryPow() { + } + + /** + * Calculate a^p using binary exponentiation + * [Binary-Exponentiation](https://cp-algorithms.com/algebra/binary-exp.html) + * + * @param a the base for exponentiation + * @param p the exponent - must be greater than 0 + * @return a^p + */ + public static int binPow(int a, int p) { + int res = 1; + while (p > 0) { + if ((p & 1) == 1) { + res = res * a; + } + a = a * a; + p >>>= 1; + } + return res; + } +} diff --git a/src/main/java/com/thealgorithms/maths/BinomialCoefficient.java b/src/main/java/com/thealgorithms/maths/BinomialCoefficient.java new file mode 100644 index 000000000000..faec049b08a7 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/BinomialCoefficient.java @@ -0,0 +1,40 @@ +package com.thealgorithms.maths; + +/* + * Java program for Binomial Cofficients + * Binomial Cofficients: A binomial cofficient C(n,k) gives number ways + * in which k objects can be chosen from n objects. + * Wikipedia: https://en.wikipedia.org/wiki/Binomial_coefficient + * + * Author: Akshay Dubey (https://github.com/itsAkshayDubey) + * + * */ + +public final class BinomialCoefficient { + private BinomialCoefficient() { + } + + /** + * This method returns the number of ways in which k objects can be chosen from n objects + * + * @param totalObjects Total number of objects + * @param numberOfObjects Number of objects to be chosen from total_objects + * @return number of ways in which no_of_objects objects can be chosen from total_objects + * objects + */ + + public static int binomialCoefficient(int totalObjects, int numberOfObjects) { + // Base Case + if (numberOfObjects > totalObjects) { + return 0; + } + + // Base Case + if (numberOfObjects == 0 || numberOfObjects == totalObjects) { + return 1; + } + + // Recursive Call + return (binomialCoefficient(totalObjects - 1, numberOfObjects - 1) + binomialCoefficient(totalObjects - 1, numberOfObjects)); + } +} diff --git a/src/main/java/com/thealgorithms/maths/CatalanNumbers.java b/src/main/java/com/thealgorithms/maths/CatalanNumbers.java new file mode 100644 index 000000000000..387756bdfa0c --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/CatalanNumbers.java @@ -0,0 +1,39 @@ +package com.thealgorithms.maths; + +/** + * Calculate Catalan Numbers + */ +public final class CatalanNumbers { + private CatalanNumbers() { + } + + /** + * Calculate the nth Catalan number using a recursive formula. + * + * @param n the index of the Catalan number to compute + * @return the nth Catalan number + */ + public static long catalan(final int n) { + if (n < 0) { + throw new IllegalArgumentException("Index must be non-negative"); + } + return factorial(2 * n) / (factorial(n + 1) * factorial(n)); + } + + /** + * Calculate the factorial of a number. + * + * @param n the number to compute the factorial for + * @return the factorial of n + */ + private static long factorial(final int n) { + if (n == 0 || n == 1) { + return 1; + } + long result = 1; + for (int i = 2; i <= n; i++) { + result *= i; + } + return result; + } +} diff --git a/src/main/java/com/thealgorithms/maths/Ceil.java b/src/main/java/com/thealgorithms/maths/Ceil.java new file mode 100644 index 000000000000..aacb9d969950 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/Ceil.java @@ -0,0 +1,23 @@ +package com.thealgorithms.maths; + +public final class Ceil { + private Ceil() { + } + + /** + * Returns the smallest (closest to negative infinity) + * + * @param number the number + * @return the smallest (closest to negative infinity) of given + * {@code number} + */ + public static double ceil(double number) { + if (number - (int) number == 0) { + return number; + } else if (number - (int) number > 0) { + return (int) (number + 1); + } else { + return (int) number; + } + } +} diff --git a/src/main/java/com/thealgorithms/maths/ChineseRemainderTheorem.java b/src/main/java/com/thealgorithms/maths/ChineseRemainderTheorem.java new file mode 100644 index 000000000000..c26e67cffb59 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/ChineseRemainderTheorem.java @@ -0,0 +1,84 @@ +package com.thealgorithms.maths; + +import java.util.List; + +/** + * @brief Implementation of the Chinese Remainder Theorem (CRT) algorithm + * @details + * The Chinese Remainder Theorem (CRT) is used to solve systems of + * simultaneous congruences. Given several pairwise coprime moduli + * and corresponding remainders, the algorithm finds the smallest + * positive solution. + */ +public final class ChineseRemainderTheorem { + private ChineseRemainderTheorem() { + } + + /** + * @brief Solves the Chinese Remainder Theorem problem. + * @param remainders The list of remainders. + * @param moduli The list of pairwise coprime moduli. + * @return The smallest positive solution that satisfies all the given congruences. + */ + public static int solveCRT(List<Integer> remainders, List<Integer> moduli) { + int product = 1; + int result = 0; + + // Calculate the product of all moduli + for (int mod : moduli) { + product *= mod; + } + + // Apply the formula for each congruence + for (int i = 0; i < moduli.size(); i++) { + int partialProduct = product / moduli.get(i); + int inverse = modInverse(partialProduct, moduli.get(i)); + result += remainders.get(i) * partialProduct * inverse; + } + + // Adjust result to be the smallest positive solution + result = result % product; + if (result < 0) { + result += product; + } + + return result; + } + + /** + * @brief Computes the modular inverse of a number with respect to a modulus using + * the Extended Euclidean Algorithm. + * @param a The number for which to find the inverse. + * @param m The modulus. + * @return The modular inverse of a modulo m. + */ + private static int modInverse(int a, int m) { + int m0 = m; + int x0 = 0; + int x1 = 1; + + if (m == 1) { + return 0; + } + + while (a > 1) { + int q = a / m; + int t = m; + + // m is remainder now, process same as Euclid's algorithm + m = a % m; + a = t; + t = x0; + + x0 = x1 - q * x0; + x1 = t; + } + + // Make x1 positive + if (x1 < 0) { + x1 += m0; + } + + return x1; + } +} diff --git a/src/main/java/com/thealgorithms/maths/CircularConvolutionFFT.java b/src/main/java/com/thealgorithms/maths/CircularConvolutionFFT.java new file mode 100644 index 000000000000..87fc5af57b8d --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/CircularConvolutionFFT.java @@ -0,0 +1,62 @@ +package com.thealgorithms.maths; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * Class for circular convolution of two discrete signals using the convolution + * theorem. + * + * @author Ioannis Karavitsis + * @version 1.0 + */ +public final class CircularConvolutionFFT { + private CircularConvolutionFFT() { + } + + /** + * This method pads the signal with zeros until it reaches the new size. + * + * @param x The signal to be padded. + * @param newSize The new size of the signal. + */ + private static void padding(Collection<FFT.Complex> x, int newSize) { + if (x.size() < newSize) { + int diff = newSize - x.size(); + for (int i = 0; i < diff; i++) { + x.add(new FFT.Complex()); + } + } + } + + /** + * Discrete circular convolution function. It uses the convolution theorem + * for discrete signals: convolved = IDFT(DFT(a)*DFT(b)). Then we use the + * FFT algorithm for faster calculations of the two DFTs and the final IDFT. + * + * <p> + * More info: https://en.wikipedia.org/wiki/Convolution_theorem + * + * @param a The first signal. + * @param b The other signal. + * @return The convolved signal. + */ + public static ArrayList<FFT.Complex> fftCircularConvolution(ArrayList<FFT.Complex> a, ArrayList<FFT.Complex> b) { + int convolvedSize = Math.max(a.size(), b.size()); // The two signals must have the same size equal to the bigger one + padding(a, convolvedSize); // Zero padding the smaller signal + padding(b, convolvedSize); + + /* Find the FFTs of both signal. Here we use the Bluestein algorithm because we want the FFT + * to have the same length with the signal and not bigger */ + FFTBluestein.fftBluestein(a, false); + FFTBluestein.fftBluestein(b, false); + ArrayList<FFT.Complex> convolved = new ArrayList<>(); + + for (int i = 0; i < a.size(); i++) { + convolved.add(a.get(i).multiply(b.get(i))); // FFT(a)*FFT(b) + } + FFTBluestein.fftBluestein(convolved, true); // IFFT + + return convolved; + } +} diff --git a/src/main/java/com/thealgorithms/maths/CollatzConjecture.java b/src/main/java/com/thealgorithms/maths/CollatzConjecture.java new file mode 100644 index 000000000000..980dc89b716e --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/CollatzConjecture.java @@ -0,0 +1,42 @@ +package com.thealgorithms.maths; + +import java.util.ArrayList; +import java.util.List; + +/** + * <a href="https://en.wikipedia.org/wiki/Collatz_conjecture">...</a> + */ +public class CollatzConjecture { + + /** + * Calculate the next number of the sequence. + * + * @param n current number of the sequence + * @return next number of the sequence + */ + public int nextNumber(final int n) { + if (n % 2 == 0) { + return n / 2; + } + return 3 * n + 1; + } + + /** + * Calculate the Collatz sequence of any natural number. + * + * @param firstNumber starting number of the sequence + * @return sequence of the Collatz Conjecture + */ + public List<Integer> collatzConjecture(int firstNumber) { + if (firstNumber < 1) { + throw new IllegalArgumentException("Must be a natural number"); + } + ArrayList<Integer> result = new ArrayList<>(); + result.add(firstNumber); + while (firstNumber != 1) { + result.add(nextNumber(firstNumber)); + firstNumber = nextNumber(firstNumber); + } + return result; + } +} diff --git a/src/main/java/com/thealgorithms/maths/Combinations.java b/src/main/java/com/thealgorithms/maths/Combinations.java new file mode 100644 index 000000000000..2b4a78613190 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/Combinations.java @@ -0,0 +1,61 @@ +package com.thealgorithms.maths; + +/** + * @see <a href="https://en.wikipedia.org/wiki/Combination">Combination</a> + */ +public final class Combinations { + private Combinations() { + } + + /** + * Calculate of factorial + * + * @param n the number + * @return factorial of given number + */ + public static long factorial(int n) { + if (n < 0) { + throw new IllegalArgumentException("number is negative"); + } + return n == 0 || n == 1 ? 1 : n * factorial(n - 1); + } + + /** + * Calculate combinations + * + * @param n first number + * @param k second number + * @return combinations of given {@code n} and {@code k} + */ + public static long combinations(int n, int k) { + return factorial(n) / (factorial(k) * factorial(n - k)); + } + + /** + * The above method can exceed limit of long (overflow) when factorial(n) is + * larger than limits of long variable. Thus even if nCk is within range of + * long variable above reason can lead to incorrect result. This is an + * optimized version of computing combinations. Observations: nC(k + 1) = (n + * - k) * nCk / (k + 1) We know the value of nCk when k = 1 which is nCk = n + * Using this base value and above formula we can compute the next term + * nC(k+1) + * + * @param n + * @param k + * @return nCk + */ + public static long combinationsOptimized(int n, int k) { + if (n < 0 || k < 0) { + throw new IllegalArgumentException("n or k can't be negative"); + } + if (n < k) { + throw new IllegalArgumentException("n can't be smaller than k"); + } + // nC0 is always 1 + long solution = 1; + for (int i = 0; i < k; i++) { + solution = (n - i) * solution / (i + 1); + } + return solution; + } +} diff --git a/src/main/java/com/thealgorithms/maths/Convolution.java b/src/main/java/com/thealgorithms/maths/Convolution.java new file mode 100644 index 000000000000..93e103f8c7cf --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/Convolution.java @@ -0,0 +1,48 @@ +package com.thealgorithms.maths; + +/** + * Class for linear convolution of two discrete signals + * + * @author Ioannis Karavitsis + * @version 1.0 + */ +public final class Convolution { + private Convolution() { + } + + /** + * Discrete linear convolution function. Both input signals and the output + * signal must start from 0. If you have a signal that has values before 0 + * then shift it to start from 0. + * + * @param a The first discrete signal + * @param b The second discrete signal + * @return The convolved signal + */ + public static double[] convolution(double[] a, double[] b) { + double[] convolved = new double[a.length + b.length - 1]; + + /* + The discrete convolution of two signals A and B is defined as: + + A.length + C[i] = Σ (A[k]*B[i-k]) + k=0 + + It's obvious that: 0 <= k <= A.length , 0 <= i <= A.length + B.length - 2 and 0 <= i-k <= + B.length - 1 From the last inequality we get that: i - B.length + 1 <= k <= i and thus we get + the conditions below. + */ + for (int i = 0; i < convolved.length; i++) { + convolved[i] = 0; + int k = Math.max(i - b.length + 1, 0); + + while (k < i + 1 && k < a.length) { + convolved[i] += a[k] * b[i - k]; + k++; + } + } + + return convolved; + } +} diff --git a/src/main/java/com/thealgorithms/maths/ConvolutionFFT.java b/src/main/java/com/thealgorithms/maths/ConvolutionFFT.java new file mode 100644 index 000000000000..ed1ba1bbefc3 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/ConvolutionFFT.java @@ -0,0 +1,69 @@ +package com.thealgorithms.maths; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * Class for linear convolution of two discrete signals using the convolution + * theorem. + * + * @author Ioannis Karavitsis + * @version 1.0 + */ +public final class ConvolutionFFT { + private ConvolutionFFT() { + } + + /** + * This method pads the signal with zeros until it reaches the new size. + * + * @param x The signal to be padded. + * @param newSize The new size of the signal. + */ + private static void padding(Collection<FFT.Complex> x, int newSize) { + if (x.size() < newSize) { + int diff = newSize - x.size(); + for (int i = 0; i < diff; i++) { + x.add(new FFT.Complex()); + } + } + } + + /** + * Discrete linear convolution function. It uses the convolution theorem for + * discrete signals convolved: = IDFT(DFT(a)*DFT(b)). This is true for + * circular convolution. In order to get the linear convolution of the two + * signals we first pad the two signals to have the same size equal to the + * convolved signal (a.size() + b.size() - 1). Then we use the FFT algorithm + * for faster calculations of the two DFTs and the final IDFT. + * + * <p> + * More info: https://en.wikipedia.org/wiki/Convolution_theorem + * https://ccrma.stanford.edu/~jos/ReviewFourier/FFT_Convolution.html + * + * @param a The first signal. + * @param b The other signal. + * @return The convolved signal. + */ + public static ArrayList<FFT.Complex> convolutionFFT(ArrayList<FFT.Complex> a, ArrayList<FFT.Complex> b) { + int convolvedSize = a.size() + b.size() - 1; // The size of the convolved signal + padding(a, convolvedSize); // Zero padding both signals + padding(b, convolvedSize); + + /* Find the FFTs of both signals (Note that the size of the FFTs will be bigger than the + * convolvedSize because of the extra zero padding in FFT algorithm) */ + FFT.fft(a, false); + FFT.fft(b, false); + ArrayList<FFT.Complex> convolved = new ArrayList<>(); + + for (int i = 0; i < a.size(); i++) { + convolved.add(a.get(i).multiply(b.get(i))); // FFT(a)*FFT(b) + } + FFT.fft(convolved, true); // IFFT + convolved.subList(convolvedSize, convolved.size()).clear(); // Remove the remaining zeros after the convolvedSize. These extra zeros came + // from + // paddingPowerOfTwo() method inside the fft() method. + + return convolved; + } +} diff --git a/src/main/java/com/thealgorithms/maths/CrossCorrelation.java b/src/main/java/com/thealgorithms/maths/CrossCorrelation.java new file mode 100644 index 000000000000..eeb4d6d1717a --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/CrossCorrelation.java @@ -0,0 +1,89 @@ +package com.thealgorithms.maths; + +/** + * Class for linear cross-correlation of two discrete signals + * + * @author Athina-Frederiki Swinkels + * @version 1.0 + */ + +public final class CrossCorrelation { + private CrossCorrelation() { + } + + /** + * Discrete linear cross-correlation function. + * Input and output signals have starting index 0. + * + * @param x The first discrete signal + * @param y The second discrete signal + * @return The result of the cross-correlation of signals x,y. The result is also a signal. + */ + public static double[] crossCorrelation(double[] x, double[] y) { + // The result signal's length is the sum of the input signals' lengths minus 1 + double[] result = new double[x.length + y.length - 1]; + int n = result.length; + + /* + To find the cross-correlation between 2 discrete signals x & y, we start by "placing" the second signal + y under the first signal x, shifted to the left so that the last value of y meets the first value of x + and for every new position (i++) of the result signal, we shift y signal one position to the right, until + the first y-value meets the last x-value. The result-value for each position is the sum of all x*y meeting + values. + Here's an example: + x=[1,2,1,1] + y=[1,1,2,1] + + i=0: [1,2,1,1] + [1,1,2,1] result[0]=1*1=1 + + i=1: [1,2,1,1] + [1,1,2,1] result[1]=1*2+2*1=4 + + i=2: [1,2,1,1] + [1,1,2,1] result[2]=1*1+2*2+1*1=6 + + i=3: [1,2,1,1] + [1,1,2,1] result[3]=1*1+2*1+1*2+1*1=6 + + i=4: [1,2,1,1] + [1,1,2,1] result[4]=2*1+1*1+1*2=5 + + i=5: [1,2,1,1] + [1,1,2,1] result[5]=1*1+1*1=2 + + i=1: [1,2,1,1] + [1,1,2,1] result[6]=1*1=1 + + result=[1,4,6,6,5,2,1] + + + + + To find the result[i] value for each i:0->n-1, the positions of x-signal in which the 2 signals meet + are calculated: kMin<=k<=kMax. + The variable 'yStart' indicates the starting index of y in each sum calculation. + The variable 'count' increases the index of y-signal by 1, to move to the next value. + */ + int yStart = y.length; + for (int i = 0; i < n; i++) { + result[i] = 0; + + int kMin = Math.max(i - (y.length - 1), 0); + int kMax = Math.min(i, x.length - 1); + + if (i < y.length) { + yStart--; + } + + int count = 0; + for (int k = kMin; k <= kMax; k++) { + result[i] += x[k] * y[yStart + count]; + count++; + } + } + + // The calculated cross-correlation of x & y signals is returned here. + return result; + } +} diff --git a/src/main/java/com/thealgorithms/maths/DeterminantOfMatrix.java b/src/main/java/com/thealgorithms/maths/DeterminantOfMatrix.java new file mode 100644 index 000000000000..b652d4903da8 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/DeterminantOfMatrix.java @@ -0,0 +1,47 @@ +package com.thealgorithms.maths; + +/* + * @author Ojasva Jain + * Determinant of a Matrix Wikipedia link: https://en.wikipedia.org/wiki/Determinant + */ +public final class DeterminantOfMatrix { + private DeterminantOfMatrix() { + } + + /** + * Calculates the determinant of a given matrix. + * + * @param a the input matrix + * @param n the size of the matrix + * @return the determinant of the matrix + */ + static int determinant(int[][] a, int n) { + int det = 0; + int sign = 1; + int p = 0; + int q = 0; + if (n == 1) { + det = a[0][0]; + } else { + int[][] b = new int[n - 1][n - 1]; + for (int x = 0; x < n; x++) { + p = 0; + q = 0; + for (int i = 1; i < n; i++) { + for (int j = 0; j < n; j++) { + if (j != x) { + b[p][q++] = a[i][j]; + if (q % (n - 1) == 0) { + p++; + q = 0; + } + } + } + } + det = det + a[0][x] * determinant(b, n - 1) * sign; + sign = -sign; + } + } + return det; + } +} diff --git a/src/main/java/com/thealgorithms/maths/DigitalRoot.java b/src/main/java/com/thealgorithms/maths/DigitalRoot.java new file mode 100644 index 000000000000..e8f5305c7569 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/DigitalRoot.java @@ -0,0 +1,65 @@ +package com.thealgorithms.maths; + +/** + * @author <a href="https://github.com/skmodi649">Suraj Kumar Modi</a> + * You are given a number n. You need to find the digital root of n. + * DigitalRoot of a number is the recursive sum of its digits until we get a single digit number. + * + * Test Case 1: + * Input: + * n = 1 + * Output: 1 + * Explanation: Digital root of 1 is 1 + * + * Test Case 2: + * Input: + * n = 99999 + * Output: 9 + * Explanation: Sum of digits of 99999 is 45 + * which is not a single digit number, hence + * sum of digit of 45 is 9 which is a single + * digit number. + * Algorithm : + * Step 1 : Define a method digitalRoot(int n) + * Step 2 : Define another method single(int n) + * Step 3 : digitalRoot(int n) method takes output of single(int n) as input + * if(single(int n) <= 9) + * return single(n) + * else + * return digitalRoot(single(n)) + * Step 4 : single(int n) calculates the sum of digits of number n recursively + * if(n<=9) + * return n; + * else + * return (n%10) + (n/10) + * Step 5 : In main method simply take n as input and then call digitalRoot(int n) function and + * print the result + */ +final class DigitalRoot { + private DigitalRoot() { + } + + public static int digitalRoot(int n) { + if (single(n) <= 9) { // If n is already single digit than simply call single method and + // return the value + return single(n); + } else { + return digitalRoot(single(n)); + } + } + + /** + * Time Complexity: O((Number of Digits)^2) Auxiliary Space Complexity: + * O(Number of Digits) Constraints: 1 <= n <= 10^7 + */ + + // This function is used for finding the sum of the digits of number + public static int single(int n) { + if (n <= 9) { // if n becomes less than 10 than return n + return n; + } else { + return (n % 10) + single(n / 10); // n % 10 for extracting digits one by one + } + } // n / 10 is the number obtained after removing the digit one by one + // The Sum of digits is stored in the Stack memory and then finally returned +} diff --git a/src/main/java/com/thealgorithms/maths/DistanceFormula.java b/src/main/java/com/thealgorithms/maths/DistanceFormula.java new file mode 100644 index 000000000000..f7e2c7629551 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/DistanceFormula.java @@ -0,0 +1,47 @@ +package com.thealgorithms.maths; + +public final class DistanceFormula { + private DistanceFormula() { + } + + public static double euclideanDistance(double x1, double y1, double x2, double y2) { + double dX = Math.pow(x2 - x1, 2); + double dY = Math.pow(y2 - x1, 2); + return Math.sqrt(dX + dY); + } + + public static double manhattanDistance(double x1, double y1, double x2, double y2) { + return Math.abs(x1 - x2) + Math.abs(y1 - y2); + } + + public static int hammingDistance(int[] b1, int[] b2) { + int d = 0; + + if (b1.length != b2.length) { + return -1; // error, both arrays must have the same length + } + + for (int i = 0; i < b1.length; i++) { + d += Math.abs(b1[i] - b2[i]); + } + + return d; + } + + public static double minkowskiDistance(double[] p1, double[] p2, int p) { + double d = 0; + double distance = 0.0; + + if (p1.length != p2.length) { + return -1; // error, both arrays must have the same length + } + + for (int i = 0; i < p1.length; i++) { + distance += Math.abs(Math.pow(p1[i] - p2[i], p)); + } + + distance = Math.pow(distance, (double) 1 / p); + d = distance; + return d; + } +} diff --git a/src/main/java/com/thealgorithms/maths/DudeneyNumber.java b/src/main/java/com/thealgorithms/maths/DudeneyNumber.java new file mode 100644 index 000000000000..acf1e55d49c8 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/DudeneyNumber.java @@ -0,0 +1,28 @@ +/** + * A number is said to be Dudeney if the sum of the digits, is the cube root of the entered number. + * Example- Let the number be 512, its sum of digits is 5+1+2=8. The cube root of 512 is also 8. + * Since, the sum of the digits is equal to the cube root of the entered number; + * it is a Dudeney Number. + */ +package com.thealgorithms.maths; + +public final class DudeneyNumber { + private DudeneyNumber() { + } + + // returns True if the number is a Dudeney number and False if it is not a Dudeney number. + public static boolean isDudeney(final int n) { + if (n <= 0) { + throw new IllegalArgumentException("Input must me positive."); + } + // Calculating Cube Root + final int cubeRoot = (int) Math.round(Math.pow(n, 1.0 / 3.0)); + // If the number is not a perfect cube the method returns false. + if (cubeRoot * cubeRoot * cubeRoot != n) { + return false; + } + + // If the cube root of the number is not equal to the sum of its digits, we return false. + return cubeRoot == SumOfDigits.sumOfDigits(n); + } +} diff --git a/src/main/java/com/thealgorithms/maths/EulerMethod.java b/src/main/java/com/thealgorithms/maths/EulerMethod.java new file mode 100644 index 000000000000..3663b6c534aa --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/EulerMethod.java @@ -0,0 +1,101 @@ +package com.thealgorithms.maths; + +import java.util.ArrayList; +import java.util.function.BiFunction; + +/** + * In mathematics and computational science, the Euler method (also called + * forward Euler method) is a first-order numerical procedure for solving + * ordinary differential equations (ODEs) with a given initial value. It is the + * most basic explicit method for numerical integration of ordinary differential + * equations. The method proceeds in a series of steps. At each step the y-value + * is calculated by evaluating the differential equation at the previous step, + * multiplying the result with the step-size and adding it to the last y-value: + * y_n+1 = y_n + stepSize * f(x_n, y_n). (description adapted from + * https://en.wikipedia.org/wiki/Euler_method ) (see also: + * https://www.geeksforgeeks.org/euler-method-solving-differential-equation/ ) + */ +public final class EulerMethod { + private EulerMethod() { + } + + /** + * Illustrates how the algorithm is used in 3 examples and prints the + * results to the console. + */ + public static void main(String[] args) { + System.out.println("example 1:"); + BiFunction<Double, Double, Double> exampleEquation1 = (x, y) -> x; + ArrayList<double[]> points1 = eulerFull(0, 4, 0.1, 0, exampleEquation1); + assert points1.get(points1.size() - 1)[1] == 7.800000000000003; + points1.forEach(point -> System.out.printf("x: %1$f; y: %2$f%n", point[0], point[1])); + + // example from https://en.wikipedia.org/wiki/Euler_method + System.out.println("\n\nexample 2:"); + BiFunction<Double, Double, Double> exampleEquation2 = (x, y) -> y; + ArrayList<double[]> points2 = eulerFull(0, 4, 0.1, 1, exampleEquation2); + assert points2.get(points2.size() - 1)[1] == 45.25925556817596; + points2.forEach(point -> System.out.printf("x: %1$f; y: %2$f%n", point[0], point[1])); + + // example from https://www.geeksforgeeks.org/euler-method-solving-differential-equation/ + System.out.println("\n\nexample 3:"); + BiFunction<Double, Double, Double> exampleEquation3 = (x, y) -> x + y + x * y; + ArrayList<double[]> points3 = eulerFull(0, 0.1, 0.025, 1, exampleEquation3); + assert points3.get(points3.size() - 1)[1] == 1.1116729841674804; + points3.forEach(point -> System.out.printf("x: %1$f; y: %2$f%n", point[0], point[1])); + } + + /** + * calculates the next y-value based on the current value of x, y and the + * stepSize the console. + * + * @param xCurrent Current x-value. + * @param stepSize Step-size on the x-axis. + * @param yCurrent Current y-value. + * @param differentialEquation The differential equation to be solved. + * @return The next y-value. + */ + public static double eulerStep(double xCurrent, double stepSize, double yCurrent, BiFunction<Double, Double, Double> differentialEquation) { + if (stepSize <= 0) { + throw new IllegalArgumentException("stepSize should be greater than zero"); + } + return yCurrent + stepSize * differentialEquation.apply(xCurrent, yCurrent); + } + + /** + * Loops through all the steps until xEnd is reached, adds a point for each + * step and then returns all the points + * + * @param xStart First x-value. + * @param xEnd Last x-value. + * @param stepSize Step-size on the x-axis. + * @param yStart First y-value. + * @param differentialEquation The differential equation to be solved. + * @return The points constituting the solution of the differential + * equation. + */ + public static ArrayList<double[]> eulerFull(double xStart, double xEnd, double stepSize, double yStart, BiFunction<Double, Double, Double> differentialEquation) { + if (xStart >= xEnd) { + throw new IllegalArgumentException("xEnd should be greater than xStart"); + } + if (stepSize <= 0) { + throw new IllegalArgumentException("stepSize should be greater than zero"); + } + + ArrayList<double[]> points = new ArrayList<double[]>(); + double[] firstPoint = {xStart, yStart}; + points.add(firstPoint); + double yCurrent = yStart; + double xCurrent = xStart; + + while (xCurrent < xEnd) { + // Euler's method for next step + yCurrent = eulerStep(xCurrent, stepSize, yCurrent, differentialEquation); + xCurrent += stepSize; + double[] point = {xCurrent, yCurrent}; + points.add(point); + } + + return points; + } +} diff --git a/src/main/java/com/thealgorithms/maths/EulersFunction.java b/src/main/java/com/thealgorithms/maths/EulersFunction.java new file mode 100644 index 000000000000..3a6bc8756681 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/EulersFunction.java @@ -0,0 +1,47 @@ +package com.thealgorithms.maths; + +/** + * Utility class for computing + * <a href="https://en.wikipedia.org/wiki/Euler%27s_totient_function">Euler's totient function</a>. + */ +public final class EulersFunction { + private EulersFunction() { + } + + /** + * Validates that the input is a positive integer. + * + * @param n the input number to validate + * @throws IllegalArgumentException if {@code n} is non-positive + */ + private static void checkInput(int n) { + if (n <= 0) { + throw new IllegalArgumentException("n must be positive."); + } + } + + /** + * Computes the value of Euler's totient function for a given input. + * This function has a time complexity of O(sqrt(n)). + * + * @param n the input number + * @return the value of Euler's totient function for the given input + * @throws IllegalArgumentException if {@code n} is non-positive + */ + public static int getEuler(int n) { + checkInput(n); + int result = n; + for (int i = 2; i * i <= n; i++) { + if (n % i == 0) { + while (n % i == 0) { + n /= i; + } + result -= result / i; + } + } + if (n > 1) { + result -= result / n; + } + return result; + } +} diff --git a/src/main/java/com/thealgorithms/maths/FFT.java b/src/main/java/com/thealgorithms/maths/FFT.java new file mode 100644 index 000000000000..91754bd1a80b --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/FFT.java @@ -0,0 +1,296 @@ +package com.thealgorithms.maths; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +/** + * Class for calculating the Fast Fourier Transform (FFT) of a discrete signal + * using the Cooley-Tukey algorithm. + * + * @author Ioannis Karavitsis + * @version 1.0 + */ +public final class FFT { + private FFT() { + } + + /** + * This class represents a complex number and has methods for basic + * operations. + * + * <p> + * More info: + * https://introcs.cs.princeton.edu/java/32class/Complex.java.html + */ + static class Complex { + + private double real; + private double img; + + /** + * Default Constructor. Creates the complex number 0. + */ + Complex() { + real = 0; + img = 0; + } + + /** + * Constructor. Creates a complex number. + * + * @param r The real part of the number. + * @param i The imaginary part of the number. + */ + Complex(double r, double i) { + real = r; + img = i; + } + + /** + * Returns the real part of the complex number. + * + * @return The real part of the complex number. + */ + public double getReal() { + return real; + } + + /** + * Returns the imaginary part of the complex number. + * + * @return The imaginary part of the complex number. + */ + public double getImaginary() { + return img; + } + + /** + * Adds this complex number to another. + * + * @param z The number to be added. + * @return The sum. + */ + public Complex add(Complex z) { + Complex temp = new Complex(); + temp.real = this.real + z.real; + temp.img = this.img + z.img; + return temp; + } + + /** + * Subtracts a number from this complex number. + * + * @param z The number to be subtracted. + * @return The difference. + */ + public Complex subtract(Complex z) { + Complex temp = new Complex(); + temp.real = this.real - z.real; + temp.img = this.img - z.img; + return temp; + } + + /** + * Multiplies this complex number by another. + * + * @param z The number to be multiplied. + * @return The product. + */ + public Complex multiply(Complex z) { + Complex temp = new Complex(); + temp.real = this.real * z.real - this.img * z.img; + temp.img = this.real * z.img + this.img * z.real; + return temp; + } + + /** + * Multiplies this complex number by a scalar. + * + * @param n The real number to be multiplied. + * @return The product. + */ + public Complex multiply(double n) { + Complex temp = new Complex(); + temp.real = this.real * n; + temp.img = this.img * n; + return temp; + } + + /** + * Finds the conjugate of this complex number. + * + * @return The conjugate. + */ + public Complex conjugate() { + Complex temp = new Complex(); + temp.real = this.real; + temp.img = -this.img; + return temp; + } + + /** + * Finds the magnitude of the complex number. + * + * @return The magnitude. + */ + public double abs() { + return Math.hypot(this.real, this.img); + } + + /** + * Divides this complex number by another. + * + * @param z The divisor. + * @return The quotient. + */ + public Complex divide(Complex z) { + Complex temp = new Complex(); + double d = z.abs() * z.abs(); + d = (double) Math.round(d * 1000000000d) / 1000000000d; + temp.real = (this.real * z.real + this.img * z.img) / (d); + temp.img = (this.img * z.real - this.real * z.img) / (d); + return temp; + } + + /** + * Divides this complex number by a scalar. + * + * @param n The divisor which is a real number. + * @return The quotient. + */ + public Complex divide(double n) { + Complex temp = new Complex(); + temp.real = this.real / n; + temp.img = this.img / n; + return temp; + } + + public double real() { + return real; + } + + public double imaginary() { + return img; + } + } + + /** + * Iterative In-Place Radix-2 Cooley-Tukey Fast Fourier Transform Algorithm + * with Bit-Reversal. The size of the input signal must be a power of 2. If + * it isn't then it is padded with zeros and the output FFT will be bigger + * than the input signal. + * + * <p> + * More info: + * https://www.algorithm-archive.org/contents/cooley_tukey/cooley_tukey.html + * https://www.geeksforgeeks.org/iterative-fast-fourier-transformation-polynomial-multiplication/ + * https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm + * https://cp-algorithms.com/algebra/fft.html + * @param x The discrete signal which is then converted to the FFT or the + * IFFT of signal x. + * @param inverse True if you want to find the inverse FFT. + * @return + */ + public static ArrayList<Complex> fft(ArrayList<Complex> x, boolean inverse) { + /* Pad the signal with zeros if necessary */ + paddingPowerOfTwo(x); + int n = x.size(); + int log2n = findLog2(n); + x = fftBitReversal(n, log2n, x); + int direction = inverse ? -1 : 1; + + /* Main loop of the algorithm */ + for (int len = 2; len <= n; len *= 2) { + double angle = -2 * Math.PI / len * direction; + Complex wlen = new Complex(Math.cos(angle), Math.sin(angle)); + for (int i = 0; i < n; i += len) { + Complex w = new Complex(1, 0); + for (int j = 0; j < len / 2; j++) { + Complex u = x.get(i + j); + Complex v = w.multiply(x.get(i + j + len / 2)); + x.set(i + j, u.add(v)); + x.set(i + j + len / 2, u.subtract(v)); + w = w.multiply(wlen); + } + } + } + x = inverseFFT(n, inverse, x); + return x; + } + + /* Find the log2(n) */ + public static int findLog2(int n) { + int log2n = 0; + while ((1 << log2n) < n) { + log2n++; + } + return log2n; + } + + /* Swap the values of the signal with bit-reversal method */ + public static ArrayList<Complex> fftBitReversal(int n, int log2n, ArrayList<Complex> x) { + int reverse; + for (int i = 0; i < n; i++) { + reverse = reverseBits(i, log2n); + if (i < reverse) { + Collections.swap(x, i, reverse); + } + } + return x; + } + + /* Divide by n if we want the inverse FFT */ + public static ArrayList<Complex> inverseFFT(int n, boolean inverse, ArrayList<Complex> x) { + if (inverse) { + for (int i = 0; i < x.size(); i++) { + Complex z = x.get(i); + x.set(i, z.divide(n)); + } + } + return x; + } + + /** + * This function reverses the bits of a number. It is used in Cooley-Tukey + * FFT algorithm. + * + * <p> + * E.g. num = 13 = 00001101 in binary log2n = 8 Then reversed = 176 = + * 10110000 in binary + * + * <p> + * More info: https://cp-algorithms.com/algebra/fft.html + * https://www.geeksforgeeks.org/write-an-efficient-c-program-to-reverse-bits-of-a-number/ + * + * @param num The integer you want to reverse its bits. + * @param log2n The number of bits you want to reverse. + * @return The reversed number + */ + private static int reverseBits(int num, int log2n) { + int reversed = 0; + for (int i = 0; i < log2n; i++) { + if ((num & (1 << i)) != 0) { + reversed |= 1 << (log2n - 1 - i); + } + } + return reversed; + } + + /** + * This method pads an ArrayList with zeros in order to have a size equal to + * the next power of two of the previous size. + * + * @param x The ArrayList to be padded. + */ + private static void paddingPowerOfTwo(Collection<Complex> x) { + int n = 1; + int oldSize = x.size(); + while (n < oldSize) { + n *= 2; + } + for (int i = 0; i < n - oldSize; i++) { + x.add(new Complex()); + } + } +} diff --git a/src/main/java/com/thealgorithms/maths/FFTBluestein.java b/src/main/java/com/thealgorithms/maths/FFTBluestein.java new file mode 100644 index 000000000000..7a03c20cc642 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/FFTBluestein.java @@ -0,0 +1,71 @@ +package com.thealgorithms.maths; + +import java.util.ArrayList; +import java.util.List; + +/** + * Class for calculating the Fast Fourier Transform (FFT) of a discrete signal + * using the Bluestein's algorithm. + * + * @author Ioannis Karavitsis + * @version 1.0 + */ +public final class FFTBluestein { + private FFTBluestein() { + } + + /** + * Bluestein's FFT Algorithm. + * + * <p> + * More info: + * https://en.wikipedia.org/wiki/Chirp_Z-transform#Bluestein.27s_algorithm + * http://tka4.org/materials/lib/Articles-Books/Numerical%20Algorithms/Hartley_Trasform/Bluestein%27s%20FFT%20algorithm%20-%20Wikipedia,%20the%20free%20encyclopedia.htm + * + * @param x The discrete signal which is then converted to the FFT or the + * IFFT of signal x. + * @param inverse True if you want to find the inverse FFT. + */ + public static void fftBluestein(List<FFT.Complex> x, boolean inverse) { + int n = x.size(); + int bnSize = 2 * n - 1; + int direction = inverse ? -1 : 1; + ArrayList<FFT.Complex> an = new ArrayList<>(); + ArrayList<FFT.Complex> bn = new ArrayList<>(); + + /* Initialization of the b(n) sequence (see Wikipedia's article above for the symbols + * used)*/ + for (int i = 0; i < bnSize; i++) { + bn.add(new FFT.Complex()); + } + + for (int i = 0; i < n; i++) { + double angle = (i - n + 1) * (i - n + 1) * Math.PI / n * direction; + bn.set(i, new FFT.Complex(Math.cos(angle), Math.sin(angle))); + bn.set(bnSize - i - 1, new FFT.Complex(Math.cos(angle), Math.sin(angle))); + } + + /* Initialization of the a(n) sequence */ + for (int i = 0; i < n; i++) { + double angle = -i * i * Math.PI / n * direction; + an.add(x.get(i).multiply(new FFT.Complex(Math.cos(angle), Math.sin(angle)))); + } + + ArrayList<FFT.Complex> convolution = ConvolutionFFT.convolutionFFT(an, bn); + + /* The final multiplication of the convolution with the b*(k) factor */ + for (int i = 0; i < n; i++) { + double angle = -1 * i * i * Math.PI / n * direction; + FFT.Complex bk = new FFT.Complex(Math.cos(angle), Math.sin(angle)); + x.set(i, bk.multiply(convolution.get(i + n - 1))); + } + + /* Divide by n if we want the inverse FFT */ + if (inverse) { + for (int i = 0; i < n; i++) { + FFT.Complex z = x.get(i); + x.set(i, z.divide(n)); + } + } + } +} diff --git a/src/main/java/com/thealgorithms/maths/Factorial.java b/src/main/java/com/thealgorithms/maths/Factorial.java new file mode 100644 index 000000000000..511cc1f84f05 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/Factorial.java @@ -0,0 +1,23 @@ +package com.thealgorithms.maths; + +public final class Factorial { + private Factorial() { + } + + /** + * Calculate factorial N using iteration + * + * @param n the number + * @return the factorial of {@code n} + */ + public static long factorial(int n) { + if (n < 0) { + throw new IllegalArgumentException("Input number cannot be negative"); + } + long factorial = 1; + for (int i = 1; i <= n; ++i) { + factorial *= i; + } + return factorial; + } +} diff --git a/src/main/java/com/thealgorithms/maths/FactorialRecursion.java b/src/main/java/com/thealgorithms/maths/FactorialRecursion.java new file mode 100644 index 000000000000..d9bafd1e39e9 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/FactorialRecursion.java @@ -0,0 +1,18 @@ +package com.thealgorithms.maths; + +public final class FactorialRecursion { + private FactorialRecursion() { + } + /** + * Recursive FactorialRecursion Method + * + * @param n The number to factorial + * @return The factorial of the number + */ + public static long factorial(int n) { + if (n < 0) { + throw new IllegalArgumentException("number is negative"); + } + return n == 0 || n == 1 ? 1 : n * factorial(n - 1); + } +} diff --git a/src/main/java/com/thealgorithms/maths/FastExponentiation.java b/src/main/java/com/thealgorithms/maths/FastExponentiation.java new file mode 100644 index 000000000000..27f49e27ff30 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/FastExponentiation.java @@ -0,0 +1,67 @@ +package com.thealgorithms.maths; + +/** + * This class provides a method to perform fast exponentiation (exponentiation by squaring), + * which calculates (base^exp) % mod efficiently. + * + * <p>The algorithm works by repeatedly squaring the base and reducing the exponent + * by half at each step. It exploits the fact that: + * <ul> + * <li>If exp is even, (base^exp) = (base^(exp/2))^2</li> + * <li>If exp is odd, (base^exp) = base * (base^(exp-1))</li> + * </ul> + * The result is computed modulo `mod` at each step to avoid overflow and keep the result within bounds. + * </p> + * + * <p><strong>Time complexity:</strong> O(log(exp)) — much faster than naive exponentiation (O(exp)).</p> + * + * For more information, please visit {@link https://en.wikipedia.org/wiki/Exponentiation_by_squaring} + */ +public final class FastExponentiation { + + /** + * Private constructor to hide the implicit public one. + */ + private FastExponentiation() { + } + + /** + * Performs fast exponentiation to calculate (base^exp) % mod using the method + * of exponentiation by squaring. + * + * <p>This method efficiently computes the result by squaring the base and halving + * the exponent at each step. It multiplies the base to the result when the exponent is odd. + * + * @param base the base number to be raised to the power of exp + * @param exp the exponent to which the base is raised + * @param mod the modulus to ensure the result does not overflow + * @return (base^exp) % mod + * @throws IllegalArgumentException if the modulus is less than or equal to 0 + * @throws ArithmeticException if the exponent is negative (not supported in this implementation) + */ + public static long fastExponentiation(long base, long exp, long mod) { + if (mod <= 0) { + throw new IllegalArgumentException("Modulus must be positive."); + } + + if (exp < 0) { + throw new ArithmeticException("Negative exponent is not supported."); + } + + long result = 1; + base = base % mod; // Take the modulus of the base to handle large base values + + // Fast exponentiation by squaring algorithm + while (exp > 0) { + // If exp is odd, multiply the base to the result + if ((exp & 1) == 1) { // exp & 1 checks if exp is odd + result = result * base % mod; + } + // Square the base and halve the exponent + base = base * base % mod; // base^2 % mod to avoid overflow + exp >>= 1; // Right shift exp to divide it by 2 + } + + return result; + } +} diff --git a/src/main/java/com/thealgorithms/maths/FastInverseSqrt.java b/src/main/java/com/thealgorithms/maths/FastInverseSqrt.java new file mode 100644 index 000000000000..01a52b913d30 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/FastInverseSqrt.java @@ -0,0 +1,51 @@ +package com.thealgorithms.maths; + +/** + * @author <a href="https://github.com/siddhant2002">Siddhant Swarup Mallick</a> + * Program description - To find out the inverse square root of the given number + * <a href="https://en.wikipedia.org/wiki/Fast_inverse_square_root">Wikipedia</a> + */ +public final class FastInverseSqrt { + private FastInverseSqrt() { + } + /** + * Returns the inverse square root of the given number upto 6 - 8 decimal places. + * calculates the inverse square root of the given number and returns true if calculated answer + * matches with given answer else returns false + * + * OUTPUT : + * Input - number = 4522 + * Output: it calculates the inverse squareroot of a number and returns true with it matches the + * given answer else returns false. 1st approach Time Complexity : O(1) Auxiliary Space Complexity : + * O(1) Input - number = 4522 Output: it calculates the inverse squareroot of a number and returns + * true with it matches the given answer else returns false. 2nd approach Time Complexity : O(1) + * Auxiliary Space Complexity : O(1) + */ + public static boolean inverseSqrt(float number) { + float x = number; + float xhalf = 0.5f * x; + int i = Float.floatToIntBits(x); + i = 0x5f3759df - (i >> 1); + x = Float.intBitsToFloat(i); + x = x * (1.5f - xhalf * x * x); + return x == ((float) 1 / (float) Math.sqrt(number)); + } + + /** + * Returns the inverse square root of the given number upto 14 - 16 decimal places. + * calculates the inverse square root of the given number and returns true if calculated answer + * matches with given answer else returns false + */ + public static boolean inverseSqrt(double number) { + double x = number; + double xhalf = 0.5d * x; + long i = Double.doubleToLongBits(x); + i = 0x5fe6ec85e7de30daL - (i >> 1); + x = Double.longBitsToDouble(i); + for (int it = 0; it < 4; it++) { + x = x * (1.5d - xhalf * x * x); + } + x *= number; + return x == 1 / Math.sqrt(number); + } +} diff --git a/src/main/java/com/thealgorithms/maths/FibonacciJavaStreams.java b/src/main/java/com/thealgorithms/maths/FibonacciJavaStreams.java new file mode 100644 index 000000000000..84390860ccc4 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/FibonacciJavaStreams.java @@ -0,0 +1,34 @@ +package com.thealgorithms.maths; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +/** + * @author: caos321 + * @date: 14 October 2021 (Thursday) + */ +public final class FibonacciJavaStreams { + private FibonacciJavaStreams() { + } + + public static Optional<BigDecimal> calculate(final BigDecimal index) { + if (index == null || index.compareTo(BigDecimal.ZERO) < 0) { + throw new IllegalArgumentException("Input index cannot be null or negative!"); + } + + if (index.compareTo(BigDecimal.ONE) < 0) { + return Optional.of(BigDecimal.ZERO); + } + + if (index.compareTo(BigDecimal.TWO) < 0) { + return Optional.of(BigDecimal.ONE); + } + + final List<BigDecimal> results = Stream.iterate(index, x -> x.compareTo(BigDecimal.ZERO) > 0, x -> x.subtract(BigDecimal.ONE)) + .reduce(List.of(), (list, current) -> list.isEmpty() || list.size() < 2 ? List.of(BigDecimal.ZERO, BigDecimal.ONE) : List.of(list.get(1), list.get(0).add(list.get(1))), (list1, list2) -> list1); + + return results.isEmpty() ? Optional.empty() : Optional.of(results.get(results.size() - 1)); + } +} diff --git a/src/main/java/com/thealgorithms/maths/FibonacciLoop.java b/src/main/java/com/thealgorithms/maths/FibonacciLoop.java new file mode 100644 index 000000000000..de23a4305c3f --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/FibonacciLoop.java @@ -0,0 +1,41 @@ +package com.thealgorithms.maths; + +import java.math.BigInteger; + +/** + * This class provides methods for calculating Fibonacci numbers using BigInteger for large values of 'n'. + */ +public final class FibonacciLoop { + + private FibonacciLoop() { + // Private constructor to prevent instantiation of this utility class. + } + + /** + * Calculates the nth Fibonacci number. + * + * @param n The index of the Fibonacci number to calculate. + * @return The nth Fibonacci number as a BigInteger. + * @throws IllegalArgumentException if the input 'n' is a negative integer. + */ + public static BigInteger compute(final int n) { + if (n < 0) { + throw new IllegalArgumentException("Input 'n' must be a non-negative integer."); + } + + if (n <= 1) { + return BigInteger.valueOf(n); + } + + BigInteger prev = BigInteger.ZERO; + BigInteger current = BigInteger.ONE; + + for (int i = 2; i <= n; i++) { + BigInteger next = prev.add(current); + prev = current; + current = next; + } + + return current; + } +} diff --git a/src/main/java/com/thealgorithms/maths/FibonacciNumberCheck.java b/src/main/java/com/thealgorithms/maths/FibonacciNumberCheck.java new file mode 100644 index 000000000000..781275d3130d --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/FibonacciNumberCheck.java @@ -0,0 +1,37 @@ +package com.thealgorithms.maths; + +/** + * Fibonacci: 0 1 1 2 3 5 8 13 21 ... + * This code checks Fibonacci Numbers up to 45th number. + * Other checks fail because of 'long'-type overflow. + */ +public final class FibonacciNumberCheck { + private FibonacciNumberCheck() { + } + /** + * Check if a number is perfect square number + * + * @param number the number to be checked + * @return <tt>true</tt> if {@code number} is a perfect square, otherwise + * <tt>false</tt> + */ + public static boolean isPerfectSquare(long number) { + long sqrt = (long) Math.sqrt(number); + return sqrt * sqrt == number; + } + + /** + * Check if a number is a Fibonacci number. This is true if and only if at + * least one of 5x^2+4 or 5x^2-4 is a perfect square + * + * @param number the number + * @return <tt>true</tt> if {@code number} is a Fibonacci number, otherwise + * <tt>false</tt> + * @link https://en.wikipedia.org/wiki/Fibonacci_number#Identification + */ + public static boolean isFibonacciNumber(long number) { + long value1 = 5 * number * number + 4; + long value2 = 5 * number * number - 4; + return isPerfectSquare(value1) || isPerfectSquare(value2); + } +} diff --git a/src/main/java/com/thealgorithms/maths/FibonacciNumberGoldenRation.java b/src/main/java/com/thealgorithms/maths/FibonacciNumberGoldenRation.java new file mode 100644 index 000000000000..4df37a40f541 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/FibonacciNumberGoldenRation.java @@ -0,0 +1,50 @@ +package com.thealgorithms.maths; + +/** + * This class provides methods for calculating Fibonacci numbers using Binet's formula. + * Binet's formula is based on the golden ratio and allows computing Fibonacci numbers efficiently. + * + * @see <a href="https://en.wikipedia.org/wiki/Fibonacci_sequence#Binet's_formula">Binet's formula on Wikipedia</a> + */ +public final class FibonacciNumberGoldenRation { + private FibonacciNumberGoldenRation() { + // Private constructor to prevent instantiation of this utility class. + } + + /** + * Compute the limit for 'n' that fits in a long data type. + * Reducing the limit to 70 due to potential floating-point arithmetic errors + * that may result in incorrect results for larger inputs. + */ + public static final int MAX_ARG = 70; + + /** + * Calculates the nth Fibonacci number using Binet's formula. + * + * @param n The index of the Fibonacci number to calculate. + * @return The nth Fibonacci number as a long. + * @throws IllegalArgumentException if the input 'n' is negative or exceeds the range of a long data type. + */ + public static long compute(int n) { + if (n < 0) { + throw new IllegalArgumentException("Input 'n' must be a non-negative integer."); + } + + if (n > MAX_ARG) { + throw new IllegalArgumentException("Input 'n' is too big to give accurate result."); + } + + if (n <= 1) { + return n; + } + + // Calculate the nth Fibonacci number using the golden ratio formula + final double sqrt5 = Math.sqrt(5); + final double phi = (1 + sqrt5) / 2; + final double psi = (1 - sqrt5) / 2; + final double result = (Math.pow(phi, n) - Math.pow(psi, n)) / sqrt5; + + // Round to the nearest integer and return as a long + return Math.round(result); + } +} diff --git a/src/main/java/com/thealgorithms/maths/FindKthNumber.java b/src/main/java/com/thealgorithms/maths/FindKthNumber.java new file mode 100644 index 000000000000..138d4b952be9 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/FindKthNumber.java @@ -0,0 +1,82 @@ +package com.thealgorithms.maths; + +import java.util.Collections; +import java.util.PriorityQueue; +import java.util.Random; + +/** + * Use a quicksort-based approach to identify the k-th largest or k-th max element within the provided array. + */ +public final class FindKthNumber { + private FindKthNumber() { + } + + private static final Random RANDOM = new Random(); + + public static int findKthMax(int[] array, int k) { + if (k <= 0 || k > array.length) { + throw new IllegalArgumentException("k must be between 1 and the size of the array"); + } + + // Convert k-th largest to index for QuickSelect + return quickSelect(array, 0, array.length - 1, array.length - k); + } + + private static int quickSelect(int[] array, int left, int right, int kSmallest) { + if (left == right) { + return array[left]; + } + + // Randomly select a pivot index + int pivotIndex = left + RANDOM.nextInt(right - left + 1); + pivotIndex = partition(array, left, right, pivotIndex); + + if (kSmallest == pivotIndex) { + return array[kSmallest]; + } else if (kSmallest < pivotIndex) { + return quickSelect(array, left, pivotIndex - 1, kSmallest); + } else { + return quickSelect(array, pivotIndex + 1, right, kSmallest); + } + } + + private static int partition(int[] array, int left, int right, int pivotIndex) { + int pivotValue = array[pivotIndex]; + // Move pivot to end + swap(array, pivotIndex, right); + int storeIndex = left; + + // Move all smaller elements to the left + for (int i = left; i < right; i++) { + if (array[i] < pivotValue) { + swap(array, storeIndex, i); + storeIndex++; + } + } + + // Move pivot to its final place + swap(array, storeIndex, right); + return storeIndex; + } + + private static void swap(int[] array, int i, int j) { + int temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } + + public static int findKthMaxUsingHeap(int[] array, int k) { + if (k <= 0 || k > array.length) { + throw new IllegalArgumentException("k must be between 1 and the size of the array"); + } + PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder()); // using max-heap to store numbers. + for (int num : array) { + maxHeap.add(num); + } + while (k > 1) { + maxHeap.poll(); // removing max number from heap + k--; + } + return maxHeap.peek(); + } +} diff --git a/src/main/java/com/thealgorithms/maths/FindMax.java b/src/main/java/com/thealgorithms/maths/FindMax.java new file mode 100644 index 000000000000..0ff2bdd191ac --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/FindMax.java @@ -0,0 +1,27 @@ +package com.thealgorithms.maths; + +public final class FindMax { + private FindMax() { + } + + /** + * @brief finds the maximum value stored in the input array + * + * @param array the input array + * @exception IllegalArgumentException input array is empty + * @return the maximum value stored in the input array + */ + public static int findMax(final int[] array) { + int n = array.length; + if (n == 0) { + throw new IllegalArgumentException("Array must be non-empty."); + } + int max = array[0]; + for (int i = 1; i < n; i++) { + if (array[i] > max) { + max = array[i]; + } + } + return max; + } +} diff --git a/src/main/java/com/thealgorithms/maths/FindMaxRecursion.java b/src/main/java/com/thealgorithms/maths/FindMaxRecursion.java new file mode 100644 index 000000000000..950a0ebe0085 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/FindMaxRecursion.java @@ -0,0 +1,40 @@ +package com.thealgorithms.maths; + +public final class FindMaxRecursion { + + private FindMaxRecursion() { + } + /** + * Get max of an array using divide and conquer algorithm + * + * @param array contains elements + * @param low the index of the first element + * @param high the index of the last element + * @return max of {@code array} + */ + public static int max(final int[] array, final int low, final int high) { + if (array.length == 0) { + throw new IllegalArgumentException("Array must be non-empty."); + } + if (low == high) { + return array[low]; // or array[high] + } + + int mid = (low + high) >>> 1; + + int leftMax = max(array, low, mid); // get max in [low, mid] + int rightMax = max(array, mid + 1, high); // get max in [mid+1, high] + + return Math.max(leftMax, rightMax); + } + + /** + * Get max of an array using recursion algorithm + * + * @param array contains elements + * @return max value of {@code array} + */ + public static int max(final int[] array) { + return max(array, 0, array.length - 1); + } +} diff --git a/src/main/java/com/thealgorithms/maths/FindMin.java b/src/main/java/com/thealgorithms/maths/FindMin.java new file mode 100644 index 000000000000..76fa2e815ee0 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/FindMin.java @@ -0,0 +1,26 @@ +package com.thealgorithms.maths; + +public final class FindMin { + private FindMin() { + } + + /** + * @brief finds the minimum value stored in the input array + * + * @param array the input array + * @exception IllegalArgumentException input array is empty + * @return the mimum value stored in the input array + */ + public static int findMin(final int[] array) { + if (array.length == 0) { + throw new IllegalArgumentException("Array must be non-empty."); + } + int min = array[0]; + for (int i = 1; i < array.length; i++) { + if (array[i] < min) { + min = array[i]; + } + } + return min; + } +} diff --git a/src/main/java/com/thealgorithms/maths/FindMinRecursion.java b/src/main/java/com/thealgorithms/maths/FindMinRecursion.java new file mode 100644 index 000000000000..a2cf1b36d6cb --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/FindMinRecursion.java @@ -0,0 +1,42 @@ +package com.thealgorithms.maths; + +public final class FindMinRecursion { + + private FindMinRecursion() { + } + + /** + * Get min of an array using divide and conquer algorithm + * + * @param array contains elements + * @param low the index of the first element + * @param high the index of the last element + * @return min of {@code array} + */ + + public static int min(final int[] array, final int low, final int high) { + if (array.length == 0) { + throw new IllegalArgumentException("array must be non-empty."); + } + if (low == high) { + return array[low]; // or array[high] + } + + int mid = (low + high) >>> 1; + + int leftMin = min(array, low, mid); // get min in [low, mid] + int rightMin = min(array, mid + 1, high); // get min in [mid+1, high] + + return Math.min(leftMin, rightMin); + } + + /** + * Get min of an array using recursion algorithm + * + * @param array contains elements + * @return min value of {@code array} + */ + public static int min(final int[] array) { + return min(array, 0, array.length - 1); + } +} diff --git a/src/main/java/com/thealgorithms/maths/Floor.java b/src/main/java/com/thealgorithms/maths/Floor.java new file mode 100644 index 000000000000..271fc42d0d17 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/Floor.java @@ -0,0 +1,24 @@ +package com.thealgorithms.maths; + +public final class Floor { + + private Floor() { + } + + /** + * Returns the largest (closest to positive infinity) + * + * @param number the number + * @return the largest (closest to positive infinity) of given + * {@code number} + */ + public static double floor(double number) { + if (number - (int) number == 0) { + return number; + } else if (number - (int) number > 0) { + return (int) number; + } else { + return (int) number - 1; + } + } +} diff --git a/src/main/java/com/thealgorithms/maths/FrizzyNumber.java b/src/main/java/com/thealgorithms/maths/FrizzyNumber.java new file mode 100644 index 000000000000..ff2b00c26ce2 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/FrizzyNumber.java @@ -0,0 +1,31 @@ +package com.thealgorithms.maths; + +/** + * @author <a href="https://github.com/siddhant2002">Siddhant Swarup Mallick</a> + * Program description - To find the FrizzyNumber + */ +public final class FrizzyNumber { + private FrizzyNumber() { + } + + /** + * Returns the n-th number that is a sum of powers + * of the given base. + * Example: base = 3 and n = 4 + * Ascending order of sums of powers of 3 = + * 3^0 = 1, 3^1 = 3, 3^1 + 3^0 = 4, 3^2 + 3^0 = 9 + * Ans = 9 + * + * @param base The base whose n-th sum of powers is required + * @param n Index from ascending order of sum of powers of base + * @return n-th sum of powers of base + */ + public static double getNthFrizzy(int base, int n) { + double final1 = 0.0; + int i = 0; + do { + final1 += Math.pow(base, i++) * (n % 2); + } while ((n /= 2) > 0); + return final1; + } +} diff --git a/src/main/java/com/thealgorithms/maths/GCD.java b/src/main/java/com/thealgorithms/maths/GCD.java new file mode 100644 index 000000000000..df27516367b2 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/GCD.java @@ -0,0 +1,65 @@ +package com.thealgorithms.maths; + +/** + * This class provides methods to compute the Greatest Common Divisor (GCD) of two or more integers. + * + * The Greatest Common Divisor (GCD) of two or more integers is the largest positive integer that divides each of the integers without leaving a remainder. + * + * The GCD can be computed using the Euclidean algorithm, which is based on the principle that the GCD of two numbers also divides their difference. + * + * For more information, refer to the + * <a href="https://en.wikipedia.org/wiki/Greatest_common_divisor">Greatest Common Divisor</a> Wikipedia page. + * + * <b>Example usage:</b> + * <pre> + * int result1 = GCD.gcd(48, 18); + * System.out.println("GCD of 48 and 18: " + result1); // Output: 6 + * + * int result2 = GCD.gcd(48, 18, 30); + * System.out.println("GCD of 48, 18, and 30: " + result2); // Output: 6 + * </pre> + * @author Oskar Enmalm 3/10/17 + */ +public final class GCD { + private GCD() { + } + + /** + * get the greatest common divisor + * + * @param num1 the first number + * @param num2 the second number + * @return gcd + */ + public static int gcd(int num1, int num2) { + if (num1 < 0 || num2 < 0) { + throw new ArithmeticException(); + } + + if (num1 == 0 || num2 == 0) { + return Math.abs(num1 - num2); + } + + while (num1 % num2 != 0) { + int remainder = num1 % num2; + num1 = num2; + num2 = remainder; + } + return num2; + } + + /** + * @brief computes gcd of an array of numbers + * + * @param numbers the input array + * @return gcd of all of the numbers in the input array + */ + public static int gcd(int... numbers) { + int result = 0; + for (final var number : numbers) { + result = gcd(result, number); + } + + return result; + } +} diff --git a/src/main/java/com/thealgorithms/maths/GCDRecursion.java b/src/main/java/com/thealgorithms/maths/GCDRecursion.java new file mode 100644 index 000000000000..e95ce97c8a04 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/GCDRecursion.java @@ -0,0 +1,41 @@ +package com.thealgorithms.maths; + +/** + * @author https://github.com/shellhub/ + */ +public final class GCDRecursion { + private GCDRecursion() { + } + + public static void main(String[] args) { + System.out.println(gcd(20, 15)); + /* output: 5 */ + System.out.println(gcd(10, 8)); + /* output: 2 */ + System.out.println(gcd(gcd(10, 5), gcd(5, 10))); + /* output: 5 */ + } + + /** + * get greatest common divisor + * + * @param a the first number + * @param b the second number + * @return gcd + */ + public static int gcd(int a, int b) { + if (a < 0 || b < 0) { + throw new ArithmeticException(); + } + + if (a == 0 || b == 0) { + return Math.abs(a - b); + } + + if (a % b == 0) { + return b; + } else { + return gcd(b, a % b); + } + } +} diff --git a/src/main/java/com/thealgorithms/maths/Gaussian.java b/src/main/java/com/thealgorithms/maths/Gaussian.java new file mode 100644 index 000000000000..1e02579757cc --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/Gaussian.java @@ -0,0 +1,70 @@ +package com.thealgorithms.maths; + +import java.util.ArrayList; +import java.util.List; + +public final class Gaussian { + private Gaussian() { + } + + public static ArrayList<Double> gaussian(int matSize, List<Double> matrix) { + int i; + int j = 0; + + double[][] mat = new double[matSize + 1][matSize + 1]; + double[][] x = new double[matSize][matSize + 1]; + + // Values from arraylist to matrix + for (i = 0; i < matSize; i++) { + for (j = 0; j <= matSize; j++) { + mat[i][j] = matrix.get(i); + } + } + + mat = gaussianElimination(matSize, i, mat); + return valueOfGaussian(matSize, x, mat); + } + + // Perform Gaussian elimination + public static double[][] gaussianElimination(int matSize, int i, double[][] mat) { + int step = 0; + for (step = 0; step < matSize - 1; step++) { + for (i = step; i < matSize - 1; i++) { + double a = (mat[i + 1][step] / mat[step][step]); + + for (int j = step; j <= matSize; j++) { + mat[i + 1][j] = mat[i + 1][j] - (a * mat[step][j]); + } + } + } + return mat; + } + + // calculate the x_1, x_2, ... values of the gaussian and save it in an arraylist. + public static ArrayList<Double> valueOfGaussian(int matSize, double[][] x, double[][] mat) { + ArrayList<Double> answerArray = new ArrayList<Double>(); + int i; + int j; + + for (i = 0; i < matSize; i++) { + for (j = 0; j <= matSize; j++) { + x[i][j] = mat[i][j]; + } + } + + for (i = matSize - 1; i >= 0; i--) { + double sum = 0; + for (j = matSize - 1; j > i; j--) { + x[i][j] = x[j][j] * x[i][j]; + sum = x[i][j] + sum; + } + if (x[i][i] == 0) { + x[i][i] = 0; + } else { + x[i][i] = (x[i][matSize] - sum) / (x[i][i]); + } + answerArray.add(x[i][j]); + } + return answerArray; + } +} diff --git a/src/main/java/com/thealgorithms/maths/GenericRoot.java b/src/main/java/com/thealgorithms/maths/GenericRoot.java new file mode 100644 index 000000000000..07f4756f93f8 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/GenericRoot.java @@ -0,0 +1,30 @@ +package com.thealgorithms.maths; + +/* + * Algorithm explanation: + * https://technotip.com/6774/c-program-to-find-generic-root-of-a-number/#:~:text=Generic%20Root%3A%20of%20a%20number,get%20a%20single%2Ddigit%20output.&text=For%20Example%3A%20If%20user%20input,%2B%204%20%2B%205%20%3D%2015. + */ +public final class GenericRoot { + private GenericRoot() { + } + + private static int base = 10; + + private static int sumOfDigits(final int n) { + assert n >= 0; + if (n < base) { + return n; + } + return n % base + sumOfDigits(n / base); + } + + public static int genericRoot(final int n) { + if (n < 0) { + return genericRoot(-n); + } + if (n > base) { + return genericRoot(sumOfDigits(n)); + } + return n; + } +} diff --git a/src/main/java/com/thealgorithms/maths/GoldbachConjecture.java b/src/main/java/com/thealgorithms/maths/GoldbachConjecture.java new file mode 100644 index 000000000000..4e962722ba88 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/GoldbachConjecture.java @@ -0,0 +1,30 @@ +package com.thealgorithms.maths; + +import static com.thealgorithms.maths.Prime.PrimeCheck.isPrime; + +/** + * This is a representation of the unsolved problem of Goldbach's Projection, according to which every + * even natural number greater than 2 can be written as the sum of 2 prime numbers + * More info: https://en.wikipedia.org/wiki/Goldbach%27s_conjecture + * @author Vasilis Sarantidis (https://github.com/BILLSARAN) + */ + +public final class GoldbachConjecture { + private GoldbachConjecture() { + } + public record Result(int number1, int number2) { + } + + public static Result getPrimeSum(int number) { + if (number <= 2 || number % 2 != 0) { + throw new IllegalArgumentException("Number must be even and greater than 2."); + } + + for (int i = 0; i <= number / 2; i++) { + if (isPrime(i) && isPrime(number - i)) { + return new Result(i, number - i); + } + } + throw new IllegalStateException("No valid prime sum found."); // Should not occur + } +} diff --git a/src/main/java/com/thealgorithms/maths/HarshadNumber.java b/src/main/java/com/thealgorithms/maths/HarshadNumber.java new file mode 100644 index 000000000000..5792e925a8aa --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/HarshadNumber.java @@ -0,0 +1,51 @@ +package com.thealgorithms.maths; + +// Wikipedia for Harshad Number : https://en.wikipedia.org/wiki/Harshad_number + +public final class HarshadNumber { + private HarshadNumber() { + } + + /** + * A function to check if a number is Harshad number or not + * + * @param n The number to be checked + * @return {@code true} if {@code a} is Harshad number, otherwise + * {@code false} + */ + public static boolean isHarshad(long n) { + if (n <= 0) { + return false; + } + + long t = n; + long sumOfDigits = 0; + while (t > 0) { + sumOfDigits += t % 10; + t /= 10; + } + + return n % sumOfDigits == 0; + } + + /** + * A function to check if a number is Harshad number or not + * + * @param s The number in String to be checked + * @return {@code true} if {@code a} is Harshad number, otherwise + * {@code false} + */ + public static boolean isHarshad(String s) { + final Long n = Long.valueOf(s); + if (n <= 0) { + return false; + } + + int sumOfDigits = 0; + for (char ch : s.toCharArray()) { + sumOfDigits += ch - '0'; + } + + return n % sumOfDigits == 0; + } +} diff --git a/src/main/java/com/thealgorithms/maths/HeronsFormula.java b/src/main/java/com/thealgorithms/maths/HeronsFormula.java new file mode 100644 index 000000000000..5baee715d1ec --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/HeronsFormula.java @@ -0,0 +1,35 @@ +package com.thealgorithms.maths; + +/** + * Wikipedia for HeronsFormula => https://en.wikipedia.org/wiki/Heron%27s_formula + * Find the area of a triangle using only side lengths + */ + +public final class HeronsFormula { + + /* + * A function to get the Area of a Triangle using Heron's Formula + * @param s1,s2,s3 => the three sides of the Triangle + * @return area using the formula (√(s(s – s1)(s – s2)(s – s3))) + * here s is called semi-perimeter and it is the half of the perimeter (i.e; s = (s1+s2+s3)/2) + * @author satyabarghav + */ + private HeronsFormula() { + } + + private static boolean areAllSidesPositive(final double a, final double b, final double c) { + return a > 0 && b > 0 && c > 0; + } + + private static boolean canFormTriangle(final double a, final double b, final double c) { + return a + b > c && b + c > a && c + a > b; + } + + public static double herons(final double a, final double b, final double c) { + if (!areAllSidesPositive(a, b, c) || !canFormTriangle(a, b, c)) { + throw new IllegalArgumentException("Triangle can't be formed with the given side lengths"); + } + final double s = (a + b + c) / 2.0; + return Math.sqrt((s) * (s - a) * (s - b) * (s - c)); + } +} diff --git a/src/main/java/com/thealgorithms/maths/JosephusProblem.java b/src/main/java/com/thealgorithms/maths/JosephusProblem.java new file mode 100644 index 000000000000..98d839011a7f --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/JosephusProblem.java @@ -0,0 +1,41 @@ +package com.thealgorithms.maths; + +/** + * There are n friends that are playing a game. The friends are sitting in a circle and are + * numbered from 1 to n in clockwise order. More formally, moving clockwise from the ith friend + * brings you to the (i+1)th friend for 1 <= i < n, and moving clockwise from the nth friend brings + * you to the 1st friend. + + The rules of the game are as follows: + + 1.Start at the 1st friend. + 2.Count the next k friends in the clockwise direction including the friend you started at. + The counting wraps around the circle and may count some friends more than once. 3.The last friend + you counted leaves the circle and loses the game. 4.If there is still more than one friend in the + circle, go back to step 2 starting from the friend immediately clockwise of the friend who just + lost and repeat. 5.Else, the last friend in the circle wins the game. + + @author Kunal + */ +public final class JosephusProblem { + private JosephusProblem() { + } + + /** + * Find the Winner of the Circular Game. + * + * @param number of friends, n, and an integer k + * @return return the winner of the game + */ + + public static int findTheWinner(int n, int k) { + return winner(n, k) + 1; + } + + public static int winner(int n, int k) { + if (n == 1) { + return 0; + } + return (winner(n - 1, k) + k) % n; + } +} diff --git a/src/main/java/com/thealgorithms/maths/JugglerSequence.java b/src/main/java/com/thealgorithms/maths/JugglerSequence.java new file mode 100644 index 000000000000..702310a1f295 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/JugglerSequence.java @@ -0,0 +1,54 @@ +package com.thealgorithms.maths; + +import java.util.ArrayList; +import java.util.List; + +/* + * Java program for printing juggler sequence + * Wikipedia: https://en.wikipedia.org/wiki/Juggler_sequence + * + * Author: Akshay Dubey (https://github.com/itsAkshayDubey) + * + * */ + +public final class JugglerSequence { + private JugglerSequence() { + } + + /** + * This method prints juggler sequence starting with the number in the parameter + * + * @param inputNumber Number from which juggler sequence is to be started + */ + public static void jugglerSequence(int inputNumber) { + // Copy method argument to a local variable + int n = inputNumber; + List<String> seq = new ArrayList<>(); + seq.add(n + ""); + // Looping till n reaches 1 + while (n != 1) { + int temp; + // if previous term is even then + // next term in the sequence is square root of previous term + // if previous term is odd then + // next term is floor value of 3 time the square root of previous term + + // Check if previous term is even or odd + if (n % 2 == 0) { + temp = (int) Math.floor(Math.sqrt(n)); + } else { + temp = (int) Math.floor(Math.sqrt(n) * Math.sqrt(n) * Math.sqrt(n)); + } + n = temp; + seq.add(n + ""); + } + String res = String.join(",", seq); + System.out.println(res); + } + + // Driver code + public static void main(String[] args) { + jugglerSequence(3); + // Output: 3,5,11,36,6,2,1 + } +} diff --git a/src/main/java/com/thealgorithms/maths/KaprekarNumbers.java b/src/main/java/com/thealgorithms/maths/KaprekarNumbers.java new file mode 100644 index 000000000000..eb9750f9ce08 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/KaprekarNumbers.java @@ -0,0 +1,53 @@ +package com.thealgorithms.maths; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +public final class KaprekarNumbers { + private KaprekarNumbers() { + } + + /* This program demonstrates if a given number is Kaprekar Number or not. + Kaprekar Number: A Kaprekar number is an n-digit number which its square can be split into + two parts where the right part has n digits and sum of these parts is equal to the original + number. */ + + // Provides a list of kaprekarNumber in a range + public static List<Long> kaprekarNumberInRange(long start, long end) throws Exception { + long n = end - start; + if (n < 0) { + throw new Exception("Invalid range"); + } + ArrayList<Long> list = new ArrayList<>(); + + for (long i = start; i <= end; i++) { + if (isKaprekarNumber(i)) { + list.add(i); + } + } + + return list; + } + + // Checks whether a given number is Kaprekar Number or not + public static boolean isKaprekarNumber(long num) { + String number = Long.toString(num); + BigInteger originalNumber = BigInteger.valueOf(num); + BigInteger numberSquared = originalNumber.multiply(originalNumber); + if (number.length() == numberSquared.toString().length()) { + return number.equals(numberSquared.toString()); + } else { + BigInteger leftDigits1 = BigInteger.ZERO; + BigInteger leftDigits2; + if (numberSquared.toString().contains("0")) { + leftDigits1 = new BigInteger(numberSquared.toString().substring(0, numberSquared.toString().indexOf("0"))); + } + leftDigits2 = new BigInteger(numberSquared.toString().substring(0, (numberSquared.toString().length() - number.length()))); + BigInteger rightDigits = new BigInteger(numberSquared.toString().substring(numberSquared.toString().length() - number.length())); + String x = leftDigits1.add(rightDigits).toString(); + String y = leftDigits2.add(rightDigits).toString(); + return (number.equals(x)) || (number.equals(y)); + } + } +} diff --git a/src/main/java/com/thealgorithms/maths/KaratsubaMultiplication.java b/src/main/java/com/thealgorithms/maths/KaratsubaMultiplication.java new file mode 100644 index 000000000000..298fcb7e85f8 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/KaratsubaMultiplication.java @@ -0,0 +1,93 @@ +package com.thealgorithms.maths; + +import java.math.BigInteger; + +/** + * This class provides an implementation of the Karatsuba multiplication algorithm. + * + * <p> + * Karatsuba multiplication is a divide-and-conquer algorithm for multiplying two large + * numbers. It is faster than the classical multiplication algorithm and reduces the + * time complexity to O(n^1.585) by breaking the multiplication of two n-digit numbers + * into three multiplications of n/2-digit numbers. + * </p> + * + * <p> + * The main idea of the Karatsuba algorithm is based on the following observation: + * </p> + * + * <pre> + * Let x and y be two numbers: + * x = a * 10^m + b + * y = c * 10^m + d + * + * Then, the product of x and y can be expressed as: + * x * y = (a * c) * 10^(2*m) + ((a * d) + (b * c)) * 10^m + (b * d) + * </pre> + * + * The Karatsuba algorithm calculates this more efficiently by reducing the number of + * multiplications from four to three by using the identity: + * + * <pre> + * (a + b)(c + d) = ac + ad + bc + bd + * </pre> + * + * <p> + * The recursion continues until the numbers are small enough to multiply directly using + * the traditional method. + * </p> + */ +public final class KaratsubaMultiplication { + + /** + * Private constructor to hide the implicit public constructor + */ + private KaratsubaMultiplication() { + } + + /** + * Multiplies two large numbers using the Karatsuba algorithm. + * + * <p> + * This method recursively splits the numbers into smaller parts until they are + * small enough to be multiplied directly using the traditional method. + * </p> + * + * @param x The first large number to be multiplied (BigInteger). + * @param y The second large number to be multiplied (BigInteger). + * @return The product of the two numbers (BigInteger). + */ + public static BigInteger karatsuba(BigInteger x, BigInteger y) { + // Base case: when numbers are small enough, use direct multiplication + // If the number is 4 bits or smaller, switch to the classical method + if (x.bitLength() <= 4 || y.bitLength() <= 4) { + return x.multiply(y); + } + + // Find the maximum bit length of the two numbers + int n = Math.max(x.bitLength(), y.bitLength()); + + // Split the numbers in the middle + int m = n / 2; + + // High and low parts of the first number x (x = a * 10^m + b) + BigInteger high1 = x.shiftRight(m); // a = x / 2^m (higher part) + BigInteger low1 = x.subtract(high1.shiftLeft(m)); // b = x - a * 2^m (lower part) + + // High and low parts of the second number y (y = c * 10^m + d) + BigInteger high2 = y.shiftRight(m); // c = y / 2^m (higher part) + BigInteger low2 = y.subtract(high2.shiftLeft(m)); // d = y - c * 2^m (lower part) + + // Recursively calculate three products + BigInteger z0 = karatsuba(low1, low2); // z0 = b * d (low1 * low2) + BigInteger z1 = karatsuba(low1.add(high1), low2.add(high2)); // z1 = (a + b) * (c + d) + BigInteger z2 = karatsuba(high1, high2); // z2 = a * c (high1 * high2) + + // Combine the results using Karatsuba's formula + // z0 + ((z1 - z2 - z0) << m) + (z2 << 2m) + return z2 + .shiftLeft(2 * m) // z2 * 10^(2*m) + .add(z1.subtract(z2).subtract(z0).shiftLeft(m)) // (z1 - z2 - z0) * 10^m + .add(z0); // z0 + } +} diff --git a/src/main/java/com/thealgorithms/maths/KeithNumber.java b/src/main/java/com/thealgorithms/maths/KeithNumber.java new file mode 100644 index 000000000000..1756cfbae91b --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/KeithNumber.java @@ -0,0 +1,60 @@ +package com.thealgorithms.maths; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Scanner; + +final class KeithNumber { + private KeithNumber() { + } + + // user-defined function that checks if the given number is Keith or not + static boolean isKeith(int x) { + // List stores all the digits of the X + ArrayList<Integer> terms = new ArrayList<>(); + // n denotes the number of digits + int temp = x; + int n = 0; + // executes until the condition becomes false + while (temp > 0) { + // determines the last digit of the number and add it to the List + terms.add(temp % 10); + // removes the last digit + temp = temp / 10; + // increments the number of digits (n) by 1 + n++; + } + // reverse the List + Collections.reverse(terms); + int nextTerm = 0; + int i = n; + // finds next term for the series + // loop executes until the condition returns true + while (nextTerm < x) { + nextTerm = 0; + // next term is the sum of previous n terms (it depends on number of digits the number + // has) + for (int j = 1; j <= n; j++) { + nextTerm = nextTerm + terms.get(i - j); + } + terms.add(nextTerm); + i++; + } + // when the control comes out of the while loop, there will be two conditions: + // either nextTerm will be equal to x or greater than x + // if equal, the given number is Keith, else not + return (nextTerm == x); + } + + // driver code + public static void main(String[] args) { + Scanner in = new Scanner(System.in); + int n = in.nextInt(); + if (isKeith(n)) { + System.out.println("Yes, the given number is a Keith number."); + } else { + System.out.println("No, the given number is not a Keith number."); + } + in.close(); + } +} diff --git a/src/main/java/com/thealgorithms/maths/KrishnamurthyNumber.java b/src/main/java/com/thealgorithms/maths/KrishnamurthyNumber.java new file mode 100644 index 000000000000..03f18aca786f --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/KrishnamurthyNumber.java @@ -0,0 +1,53 @@ +package com.thealgorithms.maths; + +/** + * Utility class for checking if a number is a Krishnamurthy number. + * + * A Krishnamurthy number (also known as a Strong number) is a number whose sum of the factorials of its digits is equal to the number itself. + * + * For example, 145 is a Krishnamurthy number because 1! + 4! + 5! = 1 + 24 + 120 = 145. + * <b>Example usage:</b> + * <pre> + * boolean isKrishnamurthy = KrishnamurthyNumber.isKrishnamurthy(145); + * System.out.println(isKrishnamurthy); // Output: true + * + * isKrishnamurthy = KrishnamurthyNumber.isKrishnamurthy(123); + * System.out.println(isKrishnamurthy); // Output: false + * </pre> + */ +public final class KrishnamurthyNumber { + + private KrishnamurthyNumber() { + } + + /** + * Checks if a number is a Krishnamurthy number. + * + * @param n The number to check + * @return true if the number is a Krishnamurthy number, false otherwise + */ + public static boolean isKrishnamurthy(int n) { + int tmp = n; + int s = 0; + + if (n <= 0) { + return false; + } else { + while (n != 0) { + // initialising the variable fact that will store the factorials of the digits + int fact = 1; + // computing factorial of each digit + for (int i = 1; i <= n % 10; i++) { + fact = fact * i; + } + // computing the sum of the factorials + s = s + fact; + // discarding the digit for which factorial has been calculated + n = n / 10; + } + + // evaluating if sum of the factorials of the digits equals the number itself + return tmp == s; + } + } +} diff --git a/src/main/java/com/thealgorithms/maths/LeastCommonMultiple.java b/src/main/java/com/thealgorithms/maths/LeastCommonMultiple.java new file mode 100644 index 000000000000..9ec11e9b3220 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/LeastCommonMultiple.java @@ -0,0 +1,42 @@ +package com.thealgorithms.maths; + +/** + * Is a common mathematics concept to find the smallest value number + * that can be divide using either number without having the remainder. + * https://maticschool.blogspot.com/2013/11/find-least-common-multiple-lcm.html + * @author LauKinHoong + */ +public final class LeastCommonMultiple { + private LeastCommonMultiple() { + } + + /** + * Finds the least common multiple of two numbers. + * + * @param num1 The first number. + * @param num2 The second number. + * @return The least common multiple of num1 and num2. + */ + public static int lcm(int num1, int num2) { + int high; + int num3; + int cmv = 0; + + if (num1 > num2) { + high = num1; + num3 = num1; + } else { + high = num2; + num3 = num2; + } + + while (num1 != 0) { + if (high % num1 == 0 && high % num2 == 0) { + cmv = high; + break; + } + high += num3; + } + return cmv; + } +} diff --git a/src/main/java/com/thealgorithms/maths/LeonardoNumber.java b/src/main/java/com/thealgorithms/maths/LeonardoNumber.java new file mode 100644 index 000000000000..bbeec052777f --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/LeonardoNumber.java @@ -0,0 +1,25 @@ +package com.thealgorithms.maths; + +/** + * https://en.wikipedia.org/wiki/Leonardo_number + */ +public final class LeonardoNumber { + private LeonardoNumber() { + } + + /** + * Calculate nth Leonardo Number (1, 1, 3, 5, 9, 15, 25, 41, 67, 109, 177, ...) + * + * @param n the index of Leonardo Number to calculate + * @return nth number of Leonardo sequences + */ + public static int leonardoNumber(int n) { + if (n < 0) { + throw new ArithmeticException(); + } + if (n == 0 || n == 1) { + return 1; + } + return (leonardoNumber(n - 1) + leonardoNumber(n - 2) + 1); + } +} diff --git a/src/main/java/com/thealgorithms/maths/LinearDiophantineEquationsSolver.java b/src/main/java/com/thealgorithms/maths/LinearDiophantineEquationsSolver.java new file mode 100644 index 000000000000..a50cfb218283 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/LinearDiophantineEquationsSolver.java @@ -0,0 +1,151 @@ +package com.thealgorithms.maths; + +import java.util.Objects; + +public final class LinearDiophantineEquationsSolver { + private LinearDiophantineEquationsSolver() { + } + + public static void main(String[] args) { + // 3x + 4y = 7 + final var toSolve = new Equation(3, 4, 7); + System.out.println(findAnySolution(toSolve)); + } + + public static Solution findAnySolution(final Equation equation) { + if (equation.a() == 0 && equation.b() == 0 && equation.c() == 0) { + return Solution.INFINITE_SOLUTIONS; + } + final var stub = new GcdSolutionWrapper(0, new Solution(0, 0)); + final var gcdSolution = gcd(equation.a(), equation.b(), stub); + if (equation.c() % gcdSolution.getGcd() != 0) { + return Solution.NO_SOLUTION; + } + final var toReturn = new Solution(0, 0); + var xToSet = stub.getSolution().getX() * (equation.c() / stub.getGcd()); + var yToSet = stub.getSolution().getY() * (equation.c() / stub.getGcd()); + toReturn.setX(xToSet); + toReturn.setY(yToSet); + return toReturn; + } + + private static GcdSolutionWrapper gcd(final int a, final int b, final GcdSolutionWrapper previous) { + if (b == 0) { + return new GcdSolutionWrapper(a, new Solution(1, 0)); + } + // stub wrapper becomes the `previous` of the next recursive call + final var stubWrapper = new GcdSolutionWrapper(0, new Solution(0, 0)); + final var next = /* recursive call */ gcd(b, a % b, stubWrapper); + previous.getSolution().setX(next.getSolution().getY()); + previous.getSolution().setY(next.getSolution().getX() - (a / b) * (next.getSolution().getY())); + previous.setGcd(next.getGcd()); + return new GcdSolutionWrapper(next.getGcd(), previous.getSolution()); + } + + public static final class Solution { + + public static final Solution NO_SOLUTION = new Solution(Integer.MAX_VALUE, Integer.MAX_VALUE); + public static final Solution INFINITE_SOLUTIONS = new Solution(Integer.MIN_VALUE, Integer.MIN_VALUE); + private int x; + private int y; + + public Solution(int x, int y) { + this.x = x; + this.y = y; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public void setX(int x) { + this.x = x; + } + + public void setY(int y) { + this.y = y; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || obj.getClass() != this.getClass()) { + return false; + } + var that = (Solution) obj; + return this.x == that.x && this.y == that.y; + } + + @Override + public int hashCode() { + return Objects.hash(x, y); + } + + @Override + public String toString() { + return "Solution[" + + "x=" + x + ", " + + "y=" + y + ']'; + } + } + + public record Equation(int a, int b, int c) { + } + + public static final class GcdSolutionWrapper { + + private int gcd; + private Solution solution; + + public GcdSolutionWrapper(int gcd, Solution solution) { + this.gcd = gcd; + this.solution = solution; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || obj.getClass() != this.getClass()) { + return false; + } + var that = (GcdSolutionWrapper) obj; + return (this.gcd == that.gcd && Objects.equals(this.solution, that.solution)); + } + + public int getGcd() { + return gcd; + } + + public void setGcd(int gcd) { + this.gcd = gcd; + } + + public Solution getSolution() { + return solution; + } + + public void setSolution(Solution solution) { + this.solution = solution; + } + + @Override + public int hashCode() { + return Objects.hash(gcd, solution); + } + + @Override + public String toString() { + return ("GcdSolutionWrapper[" + + "gcd=" + gcd + ", " + + "solution=" + solution + ']'); + } + } +} diff --git a/src/main/java/com/thealgorithms/maths/LongDivision.java b/src/main/java/com/thealgorithms/maths/LongDivision.java new file mode 100644 index 000000000000..45e97b1c14c3 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/LongDivision.java @@ -0,0 +1,83 @@ +// Given two integers dividend and divisor, divide two integers without using multiplication, +// division, and mod operator. +// +// The integer division should truncate toward zero, which means losing its fractional part. +// For example, 8.345 would be truncated to 8, and -2.7335 would be truncated to -2. My +// method used Long Division, here is the source +// "https://en.wikipedia.org/wiki/Long_division" + +package com.thealgorithms.maths; + +public final class LongDivision { + private LongDivision() { + } + public static int divide(int dividend, int divisor) { + long newDividend1 = dividend; + long newDivisor1 = divisor; + + if (divisor == 0) { + return 0; + } + if (dividend < 0) { + newDividend1 = newDividend1 * -1; + } + if (divisor < 0) { + newDivisor1 = newDivisor1 * -1; + } + + if (dividend == 0 || newDividend1 < newDivisor1) { + return 0; + } + + StringBuilder answer = new StringBuilder(); + + String dividendString = "" + newDividend1; + int lastIndex = 0; + + String remainder = ""; + + for (int i = 0; i < dividendString.length(); i++) { + String partV1 = remainder + "" + dividendString.substring(lastIndex, i + 1); + long part1 = Long.parseLong(partV1); + if (part1 > newDivisor1) { + int quotient = 0; + while (part1 >= newDivisor1) { + part1 = part1 - newDivisor1; + quotient++; + } + answer.append(quotient); + } else if (part1 == newDivisor1) { + int quotient = 0; + while (part1 >= newDivisor1) { + part1 = part1 - newDivisor1; + quotient++; + } + answer.append(quotient); + } else if (part1 == 0) { + answer.append(0); + } else if (part1 < newDivisor1) { + answer.append(0); + } + if (!(part1 == 0)) { + remainder = String.valueOf(part1); + } else { + remainder = ""; + } + + lastIndex++; + } + + if ((dividend < 0 && divisor > 0) || (dividend > 0 && divisor < 0)) { + try { + return Integer.parseInt(answer.toString()) * (-1); + } catch (NumberFormatException e) { + return -2147483648; + } + } + try { + return Integer.parseInt(answer.toString()); + } catch (NumberFormatException e) { + return 2147483647; + } + } +} diff --git a/src/main/java/com/thealgorithms/maths/LucasSeries.java b/src/main/java/com/thealgorithms/maths/LucasSeries.java new file mode 100644 index 000000000000..e277c511f317 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/LucasSeries.java @@ -0,0 +1,38 @@ +package com.thealgorithms.maths; + +/** + * https://en.wikipedia.org/wiki/Lucas_number + */ +public final class LucasSeries { + private LucasSeries() { + } + + /** + * Calculate nth number of Lucas Series(2, 1, 3, 4, 7, 11, 18, 29, 47, 76, + * 123, ....) using recursion + * + * @param n nth + * @return nth number of Lucas Series + */ + public static int lucasSeries(int n) { + return n == 1 ? 2 : n == 2 ? 1 : lucasSeries(n - 1) + lucasSeries(n - 2); + } + + /** + * Calculate nth number of Lucas Series(2, 1, 3, 4, 7, 11, 18, 29, 47, 76, + * 123, ....) using iteration + * + * @param n nth + * @return nth number of lucas series + */ + public static int lucasSeriesIteration(int n) { + int previous = 2; + int current = 1; + for (int i = 1; i < n; i++) { + int next = previous + current; + previous = current; + current = next; + } + return previous; + } +} diff --git a/src/main/java/com/thealgorithms/maths/MagicSquare.java b/src/main/java/com/thealgorithms/maths/MagicSquare.java new file mode 100644 index 000000000000..de0afc148982 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/MagicSquare.java @@ -0,0 +1,52 @@ +package com.thealgorithms.maths; + +import java.util.Scanner; + +/*A magic square of order n is an arrangement of distinct n^2 integers,in a square, such that the n +numbers in all rows, all columns, and both diagonals sum to the same constant. A magic square +contains the integers from 1 to n^2.*/ +public final class MagicSquare { + private MagicSquare() { + } + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + System.out.print("Input a number: "); + int num = sc.nextInt(); + if ((num % 2 == 0) || (num <= 0)) { + System.out.print("Input number must be odd and >0"); + System.exit(0); + } + + int[][] magicSquare = new int[num][num]; + + int rowNum = num / 2; + int colNum = num - 1; + magicSquare[rowNum][colNum] = 1; + + for (int i = 2; i <= num * num; i++) { + if (magicSquare[(rowNum - 1 + num) % num][(colNum + 1) % num] == 0) { + rowNum = (rowNum - 1 + num) % num; + colNum = (colNum + 1) % num; + } else { + colNum = (colNum - 1 + num) % num; + } + magicSquare[rowNum][colNum] = i; + } + + // print the square + for (int i = 0; i < num; i++) { + for (int j = 0; j < num; j++) { + if (magicSquare[i][j] < 10) { + System.out.print(" "); + } + if (magicSquare[i][j] < 100) { + System.out.print(" "); + } + System.out.print(magicSquare[i][j] + " "); + } + System.out.println(); + } + sc.close(); + } +} diff --git a/src/main/java/com/thealgorithms/maths/MathBuilder.java b/src/main/java/com/thealgorithms/maths/MathBuilder.java new file mode 100644 index 000000000000..1cf3d8b7fc9a --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/MathBuilder.java @@ -0,0 +1,503 @@ +package com.thealgorithms.maths; + +import java.text.DecimalFormat; +import java.util.Random; +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * Author: Sadiul Hakim : https://github.com/sadiul-hakim + * Profession: Backend Engineer + * Date: Oct 20, 2024 + */ +public final class MathBuilder { + private final double result; + + private MathBuilder(Builder builder) { + this.result = builder.number; + } + + // Returns final result + public double get() { + return result; + } + + // Return result in long + public long toLong() { + try { + if (Double.isNaN(result)) { + throw new IllegalArgumentException("Cannot convert NaN to long!"); + } + if (result == Double.POSITIVE_INFINITY) { + return Long.MAX_VALUE; + } + if (result == Double.NEGATIVE_INFINITY) { + return Long.MIN_VALUE; + } + if (result > Long.MAX_VALUE) { + return Long.MAX_VALUE; + } + if (result < Long.MIN_VALUE) { + return Long.MIN_VALUE; + } + return Math.round(result); + } catch (Exception ex) { + return 0; + } + } + + public static class Builder { + private double number; + private double sideNumber; + private boolean inParenthesis; + private double memory = 0; + + public Builder() { + number = 0; + } + + public Builder(double num) { + number = num; + } + + public Builder add(double num) { + if (inParenthesis) { + sideNumber += num; + } else { + number += num; + } + return this; + } + + // Takes a number and a condition, only does the operation if condition is true. + public Builder addIf(double num, BiFunction<Double, Double, Boolean> condition) { + if (!condition.apply(number, num)) { + return this; + } + if (inParenthesis) { + sideNumber += num; + } else { + number += num; + } + return this; + } + + public Builder minus(double num) { + if (inParenthesis) { + sideNumber -= num; + } else { + number -= num; + } + return this; + } + + // Takes a number and a condition, only does the operation if condition is true. + public Builder minusIf(double num, BiFunction<Double, Double, Boolean> condition) { + if (!condition.apply(number, num)) { + return this; + } + if (inParenthesis) { + sideNumber -= num; + } else { + number -= num; + } + return this; + } + + // Generates a random number and sets to NUMBER + public Builder rand(long seed) { + if (number != 0) { + throw new RuntimeException("Number must be zero for random assignment!"); + } + Random random = new Random(); + number = random.nextDouble(seed); + return this; + } + + // Takes PI value and sets to NUMBER + public Builder pi() { + if (number != 0) { + throw new RuntimeException("Number must be zero for PI assignment!"); + } + number = Math.PI; + return this; + } + + // Takes E value and sets to NUMBER + public Builder e() { + if (number != 0) { + throw new RuntimeException("Number must be zero for E assignment!"); + } + number = Math.E; + return this; + } + + public Builder randomInRange(double min, double max) { + if (number != 0) { + throw new RuntimeException("Number must be zero for random assignment!"); + } + Random random = new Random(); + number = min + (max - min) * random.nextDouble(); + return this; + } + + public Builder toDegrees() { + if (inParenthesis) { + sideNumber = Math.toDegrees(sideNumber); + } else { + number = Math.toDegrees(number); + } + return this; + } + + public Builder max(double num) { + if (inParenthesis) { + sideNumber = Math.max(sideNumber, num); + } else { + number = Math.max(number, num); + } + return this; + } + + public Builder min(double num) { + if (inParenthesis) { + sideNumber = Math.min(sideNumber, num); + } else { + number = Math.min(number, num); + } + return this; + } + + public Builder multiply(double num) { + if (inParenthesis) { + sideNumber *= num; + } else { + number *= num; + } + return this; + } + + // Takes a number and a condition, only does the operation if condition is true. + public Builder multiplyIf(double num, BiFunction<Double, Double, Boolean> condition) { + if (!condition.apply(number, num)) { + return this; + } + if (inParenthesis) { + sideNumber *= num; + } else { + number *= num; + } + return this; + } + + public Builder divide(double num) { + if (num == 0) { + return this; + } + if (inParenthesis) { + sideNumber /= num; + } else { + number /= num; + } + return this; + } + + // Takes a number and a condition, only does the operation if condition is true. + public Builder divideIf(double num, BiFunction<Double, Double, Boolean> condition) { + if (num == 0) { + return this; + } + if (!condition.apply(number, num)) { + return this; + } + if (inParenthesis) { + sideNumber /= num; + } else { + number /= num; + } + return this; + } + + public Builder mod(double num) { + if (inParenthesis) { + sideNumber %= num; + } else { + number %= num; + } + return this; + } + + // Takes a number and a condition, only does the operation if condition is true. + public Builder modIf(double num, BiFunction<Double, Double, Boolean> condition) { + if (!condition.apply(number, num)) { + return this; + } + if (inParenthesis) { + sideNumber %= num; + } else { + number %= num; + } + return this; + } + + public Builder pow(double num) { + if (inParenthesis) { + sideNumber = Math.pow(sideNumber, num); + } else { + number = Math.pow(number, num); + } + return this; + } + + public Builder sqrt() { + if (inParenthesis) { + sideNumber = Math.sqrt(sideNumber); + } else { + number = Math.sqrt(number); + } + return this; + } + + public Builder round() { + if (inParenthesis) { + sideNumber = Math.round(sideNumber); + } else { + number = Math.round(number); + } + return this; + } + + public Builder floor() { + if (inParenthesis) { + sideNumber = Math.floor(sideNumber); + } else { + number = Math.floor(number); + } + return this; + } + + public Builder ceil() { + if (inParenthesis) { + sideNumber = Math.ceil(sideNumber); + } else { + number = Math.ceil(number); + } + return this; + } + + public Builder abs() { + if (inParenthesis) { + sideNumber = Math.abs(sideNumber); + } else { + number = Math.abs(number); + } + return this; + } + + public Builder cbrt() { + if (inParenthesis) { + sideNumber = Math.cbrt(sideNumber); + } else { + number = Math.cbrt(number); + } + return this; + } + + public Builder log() { + if (inParenthesis) { + sideNumber = Math.log(sideNumber); + } else { + number = Math.log(number); + } + return this; + } + + public Builder log10() { + if (inParenthesis) { + sideNumber = Math.log10(sideNumber); + } else { + number = Math.log10(number); + } + return this; + } + + public Builder sin() { + if (inParenthesis) { + sideNumber = Math.sin(sideNumber); + } else { + number = Math.sin(number); + } + return this; + } + + public Builder cos() { + if (inParenthesis) { + sideNumber = Math.cos(sideNumber); + } else { + number = Math.cos(number); + } + return this; + } + + public Builder tan() { + if (inParenthesis) { + sideNumber = Math.tan(sideNumber); + } else { + number = Math.tan(number); + } + return this; + } + + public Builder sinh() { + if (inParenthesis) { + sideNumber = Math.sinh(sideNumber); + } else { + number = Math.sinh(number); + } + return this; + } + + public Builder cosh() { + if (inParenthesis) { + sideNumber = Math.cosh(sideNumber); + } else { + number = Math.cosh(number); + } + return this; + } + + public Builder tanh() { + if (inParenthesis) { + sideNumber = Math.tanh(sideNumber); + } else { + number = Math.tanh(number); + } + return this; + } + + public Builder exp() { + if (inParenthesis) { + sideNumber = Math.exp(sideNumber); + } else { + number = Math.exp(number); + } + return this; + } + + public Builder toRadians() { + if (inParenthesis) { + sideNumber = Math.toRadians(sideNumber); + } else { + number = Math.toRadians(number); + } + return this; + } + + // Remembers the NUMBER + public Builder remember() { + memory = number; + return this; + } + + // Recalls the NUMBER + public Builder recall(boolean cleanMemory) { + number = memory; + if (cleanMemory) { + memory = 0; + } + return this; + } + + // Recalls the NUMBER on condition + public Builder recallIf(Function<Double, Boolean> condition, boolean cleanMemory) { + if (!condition.apply(number)) { + return this; + } + number = memory; + if (cleanMemory) { + memory = 0; + } + return this; + } + + // Replaces NUMBER with given number + public Builder set(double num) { + if (number != 0) { + throw new RuntimeException("Number must be zero to set!"); + } + number = num; + return this; + } + + // Replaces NUMBER with given number on condition + public Builder setIf(double num, BiFunction<Double, Double, Boolean> condition) { + if (number != 0) { + throw new RuntimeException("Number must be zero to set!"); + } + if (condition.apply(number, num)) { + number = num; + } + return this; + } + + // Prints current NUMBER + public Builder print() { + System.out.println("MathBuilder Result :: " + number); + return this; + } + + public Builder openParenthesis(double num) { + sideNumber = num; + inParenthesis = true; + return this; + } + + public Builder closeParenthesisAndPlus() { + number += sideNumber; + inParenthesis = false; + sideNumber = 0; + return this; + } + + public Builder closeParenthesisAndMinus() { + number -= sideNumber; + inParenthesis = false; + sideNumber = 0; + return this; + } + + public Builder closeParenthesisAndMultiply() { + number *= sideNumber; + inParenthesis = false; + sideNumber = 0; + return this; + } + + public Builder closeParenthesisAndDivide() { + number /= sideNumber; + inParenthesis = false; + sideNumber = 0; + return this; + } + + public Builder format(String format) { + DecimalFormat formater = new DecimalFormat(format); + String num = formater.format(number); + number = Double.parseDouble(num); + return this; + } + + public Builder format(int decimalPlace) { + String pattern = "." + + "#".repeat(decimalPlace); + DecimalFormat formater = new DecimalFormat(pattern); + String num = formater.format(number); + number = Double.parseDouble(num); + return this; + } + + public MathBuilder build() { + return new MathBuilder(this); + } + } +} diff --git a/src/main/java/com/thealgorithms/maths/MaxValue.java b/src/main/java/com/thealgorithms/maths/MaxValue.java new file mode 100644 index 000000000000..d88291060f51 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/MaxValue.java @@ -0,0 +1,18 @@ +package com.thealgorithms.maths; + +public final class MaxValue { + private MaxValue() { + } + /** + * Returns the greater of two {@code int} values. That is, the result is the + * argument closer to the value of {@link Integer#MAX_VALUE}. If the + * arguments have the same value, the result is that same value. + * + * @param a an argument. + * @param b another argument. + * @return the larger of {@code a} and {@code b}. + */ + public static int max(int a, int b) { + return a >= b ? a : b; + } +} diff --git a/src/main/java/com/thealgorithms/maths/Means.java b/src/main/java/com/thealgorithms/maths/Means.java new file mode 100644 index 000000000000..dccc820b172e --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/Means.java @@ -0,0 +1,54 @@ +package com.thealgorithms.maths; + +import java.util.stream.StreamSupport; +import org.apache.commons.collections4.IterableUtils; + +/** + * https://en.wikipedia.org/wiki/Mean + * <p> + * by: Punit Patel + */ +public final class Means { + + private Means() { + } + + /** + * @brief computes the [Arithmetic Mean](https://en.wikipedia.org/wiki/Arithmetic_mean) of the input + * @param numbers the input numbers + * @throws IllegalArgumentException empty input + * @return the arithmetic mean of the input numbers + */ + public static Double arithmetic(final Iterable<Double> numbers) { + checkIfNotEmpty(numbers); + return StreamSupport.stream(numbers.spliterator(), false).reduce((x, y) -> x + y).get() / IterableUtils.size(numbers); + } + + /** + * @brief computes the [Geometric Mean](https://en.wikipedia.org/wiki/Geometric_mean) of the input + * @param numbers the input numbers + * @throws IllegalArgumentException empty input + * @return the geometric mean of the input numbers + */ + public static Double geometric(final Iterable<Double> numbers) { + checkIfNotEmpty(numbers); + return Math.pow(StreamSupport.stream(numbers.spliterator(), false).reduce((x, y) -> x * y).get(), 1d / IterableUtils.size(numbers)); + } + + /** + * @brief computes the [Harmonic Mean](https://en.wikipedia.org/wiki/Harmonic_mean) of the input + * @param numbers the input numbers + * @throws IllegalArgumentException empty input + * @return the harmonic mean of the input numbers + */ + public static Double harmonic(final Iterable<Double> numbers) { + checkIfNotEmpty(numbers); + return IterableUtils.size(numbers) / StreamSupport.stream(numbers.spliterator(), false).reduce(0d, (x, y) -> x + 1d / y); + } + + private static void checkIfNotEmpty(final Iterable<Double> numbers) { + if (!numbers.iterator().hasNext()) { + throw new IllegalArgumentException("Emtpy list given for Mean computation."); + } + } +} diff --git a/src/main/java/com/thealgorithms/maths/Median.java b/src/main/java/com/thealgorithms/maths/Median.java new file mode 100644 index 000000000000..e4daec8fc11a --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/Median.java @@ -0,0 +1,22 @@ +package com.thealgorithms.maths; + +import java.util.Arrays; + +/** + * Wikipedia: https://en.wikipedia.org/wiki/Median + */ +public final class Median { + private Median() { + } + + /** + * Calculate average median + * @param values sorted numbers to find median of + * @return median of given {@code values} + */ + public static double median(int[] values) { + Arrays.sort(values); + int length = values.length; + return length % 2 == 0 ? (values[length / 2] + values[length / 2 - 1]) / 2.0 : values[length / 2]; + } +} diff --git a/src/main/java/com/thealgorithms/maths/MinValue.java b/src/main/java/com/thealgorithms/maths/MinValue.java new file mode 100644 index 000000000000..88e28e8816c6 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/MinValue.java @@ -0,0 +1,18 @@ +package com.thealgorithms.maths; + +public final class MinValue { + private MinValue() { + } + /** + * Returns the smaller of two {@code int} values. That is, the result the + * argument closer to the value of {@link Integer#MIN_VALUE}. If the + * arguments have the same value, the result is that same value. + * + * @param a an argument. + * @param b another argument. + * @return the smaller of {@code a} and {@code b}. + */ + public static int min(int a, int b) { + return a <= b ? a : b; + } +} diff --git a/src/main/java/com/thealgorithms/maths/Mode.java b/src/main/java/com/thealgorithms/maths/Mode.java new file mode 100644 index 000000000000..f0b747cf02ec --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/Mode.java @@ -0,0 +1,48 @@ +package com.thealgorithms.maths; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; + +/* + * Find the mode of an array of numbers + * + * The mode of an array of numbers is the most frequently occurring number in the array, + * or the most frequently occurring numbers if there are multiple numbers with the same frequency + */ +public final class Mode { + private Mode() { + } + + /* + * Find the mode of an array of integers + * + * @param numbers array of integers + * @return mode of the array + */ + public static int[] mode(final int[] numbers) { + if (numbers.length == 0) { + return null; + } + + HashMap<Integer, Integer> count = new HashMap<>(); + + for (int num : numbers) { + if (count.containsKey(num)) { + count.put(num, count.get(num) + 1); + } else { + count.put(num, 1); + } + } + + int max = Collections.max(count.values()); + ArrayList<Integer> modes = new ArrayList<>(); + + for (final var entry : count.entrySet()) { + if (entry.getValue() == max) { + modes.add(entry.getKey()); + } + } + return modes.stream().mapToInt(n -> n).toArray(); + } +} diff --git a/src/main/java/com/thealgorithms/maths/NonRepeatingElement.java b/src/main/java/com/thealgorithms/maths/NonRepeatingElement.java new file mode 100644 index 000000000000..9c95ebde3740 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/NonRepeatingElement.java @@ -0,0 +1,61 @@ +package com.thealgorithms.maths; + +/** + * Find the 2 elements which are non-repeating in an array + * Reason to use bitwise operator: It makes our program faster as we are operating on bits and not + * on actual numbers. + * + * Explanation of the code: + * Let us assume we have an array [1, 2, 1, 2, 3, 4] + * Property of XOR: num ^ num = 0. + * If we XOR all the elements of the array, we will be left with 3 ^ 4 as 1 ^ 1 + * and 2 ^ 2 would give 0. Our task is to find num1 and num2 from the result of 3 ^ 4 = 7. + * We need to find the two's complement of 7 and find the rightmost set bit, i.e., (num & (-num)). + * Two's complement of 7 is 001, and hence res = 1. There can be 2 options when we Bitwise AND this res + * with all the elements in our array: + * 1. The result will be a non-zero number. + * 2. The result will be 0. + * In the first case, we will XOR our element with the first number (which is initially 0). + * In the second case, we will XOR our element with the second number (which is initially 0). + * This is how we will get non-repeating elements with the help of bitwise operators. + */ +public final class NonRepeatingElement { + private NonRepeatingElement() { + } + + /** + * Finds the two non-repeating elements in the array. + * + * @param arr The input array containing exactly two non-repeating elements and all other elements repeating. + * @return An array containing the two non-repeating elements. + * @throws IllegalArgumentException if the input array length is odd. + */ + public static int[] findNonRepeatingElements(int[] arr) { + if (arr.length % 2 != 0) { + throw new IllegalArgumentException("Array should contain an even number of elements"); + } + + int xorResult = 0; + + // Find XOR of all elements + for (int num : arr) { + xorResult ^= num; + } + + // Find the rightmost set bit + int rightmostSetBit = xorResult & (-xorResult); + int num1 = 0; + int num2 = 0; + + // Divide the elements into two groups and XOR them + for (int num : arr) { + if ((num & rightmostSetBit) != 0) { + num1 ^= num; + } else { + num2 ^= num; + } + } + + return new int[] {num1, num2}; + } +} diff --git a/src/main/java/com/thealgorithms/maths/NthUglyNumber.java b/src/main/java/com/thealgorithms/maths/NthUglyNumber.java new file mode 100644 index 000000000000..2da22c4c8696 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/NthUglyNumber.java @@ -0,0 +1,80 @@ +package com.thealgorithms.maths; + +import static java.util.Collections.singletonList; + +import java.util.ArrayList; +import java.util.Map; +import org.apache.commons.lang3.tuple.MutablePair; + +/** + * @brief class computing the n-th ugly number (when they are sorted) + * @details the ugly numbers with base [2, 3, 5] are all numbers of the form 2^a*3^b^5^c, + * where the exponents a, b, c are non-negative integers. + * Some properties of ugly numbers: + * - base [2, 3, 5] ugly numbers are the 5-smooth numbers, cf. https://oeis.org/A051037 + * - base [2, 3, 5, 7] ugly numbers are 7-smooth numbers, cf. https://oeis.org/A002473 + * - base [2] ugly numbers are the non-negative powers of 2, + * - the base [2, 3, 5] ugly numbers are the same as base [5, 6, 2, 3, 5] ugly numbers + */ +public class NthUglyNumber { + private ArrayList<Long> uglyNumbers = new ArrayList<>(singletonList(1L)); + private ArrayList<MutablePair<Integer, Integer>> positions = new ArrayList<>(); + + /** + * @brief initialized the object allowing to compute ugly numbers with given base + * @param baseNumbers the given base of ugly numbers + * @exception IllegalArgumentException baseNumber is empty + */ + NthUglyNumber(final int[] baseNumbers) { + if (baseNumbers.length == 0) { + throw new IllegalArgumentException("baseNumbers must be non-empty."); + } + + for (final var baseNumber : baseNumbers) { + this.positions.add(MutablePair.of(baseNumber, 0)); + } + } + + /** + * @param n the zero-based-index of the queried ugly number + * @exception IllegalArgumentException n is negative + * @return the n-th ugly number (starting from index 0) + */ + public Long get(final int n) { + if (n < 0) { + throw new IllegalArgumentException("n must be non-negative."); + } + + while (uglyNumbers.size() <= n) { + addUglyNumber(); + } + + return uglyNumbers.get(n); + } + + private void addUglyNumber() { + uglyNumbers.add(computeMinimalCandidate()); + updatePositions(); + } + + private void updatePositions() { + final var lastUglyNumber = uglyNumbers.get(uglyNumbers.size() - 1); + for (var entry : positions) { + if (computeCandidate(entry) == lastUglyNumber) { + entry.setValue(entry.getValue() + 1); + } + } + } + + private long computeCandidate(final Map.Entry<Integer, Integer> entry) { + return entry.getKey() * uglyNumbers.get(entry.getValue()); + } + + private long computeMinimalCandidate() { + long res = Long.MAX_VALUE; + for (final var entry : positions) { + res = Math.min(res, computeCandidate(entry)); + } + return res; + } +} diff --git a/src/main/java/com/thealgorithms/maths/NumberOfDigits.java b/src/main/java/com/thealgorithms/maths/NumberOfDigits.java new file mode 100644 index 000000000000..fc538196c7da --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/NumberOfDigits.java @@ -0,0 +1,53 @@ +package com.thealgorithms.maths; + +/** + * Find the number of digits in a number. + */ +public final class NumberOfDigits { + private NumberOfDigits() { + } + /** + * Find the number of digits in a number. + * + * @param number number to find + * @return number of digits of given number + */ + public static int numberOfDigits(int number) { + int digits = 0; + do { + digits++; + number /= 10; + } while (number != 0); + return digits; + } + + /** + * Find the number of digits in a number fast version. + * + * @param number number to find + * @return number of digits of given number + */ + public static int numberOfDigitsFast(int number) { + return number == 0 ? 1 : (int) Math.floor(Math.log10(Math.abs(number)) + 1); + } + + /** + * Find the number of digits in a number faster version. + * + * @param number number to find + * @return number of digits of given number + */ + public static int numberOfDigitsFaster(int number) { + return number < 0 ? (-number + "").length() : (number + "").length(); + } + + /** + * Find the number of digits in a number using recursion. + * + * @param number number to find + * @return number of digits of given number + */ + public static int numberOfDigitsRecursion(int number) { + return number / 10 == 0 ? 1 : 1 + numberOfDigitsRecursion(number / 10); + } +} diff --git a/src/main/java/com/thealgorithms/maths/PalindromeNumber.java b/src/main/java/com/thealgorithms/maths/PalindromeNumber.java new file mode 100644 index 000000000000..a22d63897b37 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/PalindromeNumber.java @@ -0,0 +1,26 @@ +package com.thealgorithms.maths; + +public final class PalindromeNumber { + private PalindromeNumber() { + } + /** + * Check if {@code n} is palindrome number or not + * + * @param number the number + * @return {@code true} if {@code n} is palindrome number, otherwise + * {@code false} + */ + public static boolean isPalindrome(int number) { + if (number < 0) { + throw new IllegalArgumentException("Input parameter must not be negative!"); + } + int numberCopy = number; + int reverseNumber = 0; + while (numberCopy != 0) { + int remainder = numberCopy % 10; + reverseNumber = reverseNumber * 10 + remainder; + numberCopy /= 10; + } + return number == reverseNumber; + } +} diff --git a/src/main/java/com/thealgorithms/maths/ParseInteger.java b/src/main/java/com/thealgorithms/maths/ParseInteger.java new file mode 100644 index 000000000000..cdca9f815c4d --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/ParseInteger.java @@ -0,0 +1,46 @@ +package com.thealgorithms.maths; + +public final class ParseInteger { + private ParseInteger() { + } + + private static void checkInput(final String s) { + if (s == null) { + throw new NumberFormatException("Input parameter must not be null!"); + } + if (s.isEmpty()) { + throw new NumberFormatException("Input parameter must not be empty!"); + } + } + + private static void checkDigitAt(final String s, final int pos) { + if (!Character.isDigit(s.charAt(pos))) { + throw new NumberFormatException("Input parameter of incorrect format: " + s); + } + } + + private static int digitToInt(final char digit) { + return digit - '0'; + } + + /** + * Parse a string to integer + * + * @param s the string + * @return the integer value represented by the argument in decimal. + * @throws NumberFormatException if the {@code string} does not contain a + * parsable integer. + */ + public static int parseInt(final String s) { + checkInput(s); + + final boolean isNegative = s.charAt(0) == '-'; + final boolean isPositive = s.charAt(0) == '+'; + int number = 0; + for (int i = isNegative || isPositive ? 1 : 0, length = s.length(); i < length; ++i) { + checkDigitAt(s, i); + number = number * 10 + digitToInt(s.charAt(i)); + } + return isNegative ? -number : number; + } +} diff --git a/src/main/java/com/thealgorithms/maths/PascalTriangle.java b/src/main/java/com/thealgorithms/maths/PascalTriangle.java new file mode 100644 index 000000000000..9a9d4450cb98 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/PascalTriangle.java @@ -0,0 +1,65 @@ +package com.thealgorithms.maths; + +public final class PascalTriangle { + private PascalTriangle() { + } + + /** + *In mathematics, Pascal's triangle is a triangular array of the binomial coefficients that + *arises in probability theory, combinatorics, and algebra. In much of the Western world, it is + *named after the French mathematician Blaise Pascal, although other mathematicians studied it + *centuries before him in India, Persia, China, Germany, and Italy. + * + * The rows of Pascal's triangle are conventionally enumerated starting with row n=0 at the top + *(the 0th row). The entries in each row are numbered from the left beginning with k=0 and are + *usually staggered relative to the numbers in the adjacent rows. The triangle may be + *constructed in the following manner: In row 0 (the topmost row), there is a unique nonzero + *entry 1. Each entry of each subsequent row is constructed by adding the number above and to + *the left with the number above and to the right, treating blank entries as 0. For example, the + *initial number in the first (or any other) row is 1 (the sum of 0 and 1), whereas the numbers + *1 and 3 in the third row are added to produce the number 4 in the fourth row. * + * + *<p> + * link:-https://en.wikipedia.org/wiki/Pascal%27s_triangle + * + * <p> + * Example:- + * 1 + * 1 1 + * 1 2 1 + * 1 3 3 1 + * 1 4 6 4 1 + * 1 5 10 10 5 1 + * 1 6 15 20 15 6 1 + * 1 7 21 35 35 21 7 1 + * 1 8 28 56 70 56 28 8 1 + * + */ + + public static int[][] pascal(int n) { + /* + * @param arr An auxiliary array to store generated pascal triangle values + * @return + */ + int[][] arr = new int[n][n]; + /* + * @param line Iterate through every line and print integer(s) in it + * @param i Represents the column number of the element we are currently on + */ + for (int line = 0; line < n; line++) { + /* + * @Every line has number of integers equal to line number + */ + for (int i = 0; i <= line; i++) { + // First and last values in every row are 1 + if (line == i || i == 0) { + arr[line][i] = 1; + } else { + arr[line][i] = arr[line - 1][i - 1] + arr[line - 1][i]; + } + } + } + + return arr; + } +} diff --git a/src/main/java/com/thealgorithms/maths/PerfectCube.java b/src/main/java/com/thealgorithms/maths/PerfectCube.java new file mode 100644 index 000000000000..4104c6238580 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/PerfectCube.java @@ -0,0 +1,34 @@ +package com.thealgorithms.maths; + +/** + * https://en.wikipedia.org/wiki/Cube_(algebra) + */ +public final class PerfectCube { + private PerfectCube() { + } + + /** + * Check if a number is perfect cube or not + * + * @param number number to check + * @return {@code true} if {@code number} is perfect cube, otherwise + * {@code false} + */ + public static boolean isPerfectCube(int number) { + number = Math.abs(number); // converting negative number to positive number + int a = (int) Math.pow(number, 1.0 / 3); + return a * a * a == number; + } + + /** + * Check if a number is perfect cube or not by using Math.cbrt function + * + * @param number number to check + * @return {@code true} if {@code number} is perfect cube, otherwise + * {@code false} + */ + public static boolean isPerfectCubeMathCbrt(int number) { + double cubeRoot = Math.cbrt(number); + return cubeRoot == (int) cubeRoot; + } +} diff --git a/src/main/java/com/thealgorithms/maths/PerfectNumber.java b/src/main/java/com/thealgorithms/maths/PerfectNumber.java new file mode 100644 index 000000000000..f299d08e5d27 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/PerfectNumber.java @@ -0,0 +1,71 @@ +package com.thealgorithms.maths; + +/** + * In number theory, a perfect number is a positive integer that is equal to the + * sum of its positive divisors, excluding the number itself. For instance, 6 + * has divisors 1, 2 and 3 (excluding itself), and 1 + 2 + 3 = 6, so 6 is a + * perfect number. + * + * link:https://en.wikipedia.org/wiki/Perfect_number + */ +public final class PerfectNumber { + private PerfectNumber() { + } + + /** + * Check if {@code number} is perfect number or not + * + * @param number the number + * @return {@code true} if {@code number} is perfect number, otherwise false + */ + public static boolean isPerfectNumber(int number) { + if (number <= 0) { + return false; + } + int sum = 0; + /* sum of its positive divisors */ + for (int i = 1; i < number; ++i) { + if (number % i == 0) { + sum += i; + } + } + return sum == number; + } + + /** + * Check if {@code n} is perfect number or not + * + * @param n the number + * @return {@code true} if {@code number} is perfect number, otherwise false + */ + public static boolean isPerfectNumber2(int n) { + if (n <= 0) { + return false; + } + int sum = 1; + double root = Math.sqrt(n); + + /* + * We can get the factors after the root by dividing number by its factors + * before the root. + * Ex- Factors of 100 are 1, 2, 4, 5, 10, 20, 25, 50 and 100. + * Root of 100 is 10. So factors before 10 are 1, 2, 4 and 5. + * Now by dividing 100 by each factor before 10 we get: + * 100/1 = 100, 100/2 = 50, 100/4 = 25 and 100/5 = 20 + * So we get 100, 50, 25 and 20 which are factors of 100 after 10 + */ + for (int i = 2; i <= root; i++) { + if (n % i == 0) { + sum += i + n / i; + } + } + + // if n is a perfect square then its root was added twice in above loop, so subtracting root + // from sum + if (root == (int) root) { + sum -= (int) root; + } + + return sum == n; + } +} diff --git a/src/main/java/com/thealgorithms/maths/PerfectSquare.java b/src/main/java/com/thealgorithms/maths/PerfectSquare.java new file mode 100644 index 000000000000..e9318bd7d805 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/PerfectSquare.java @@ -0,0 +1,33 @@ +package com.thealgorithms.maths; + +/** + * https://en.wikipedia.org/wiki/Perfect_square + */ +public final class PerfectSquare { + private PerfectSquare() { + } + + /** + * Check if a number is perfect square number + * + * @param number the number to be checked + * @return <tt>true</tt> if {@code number} is perfect square, otherwise + * <tt>false</tt> + */ + public static boolean isPerfectSquare(final int number) { + final int sqrt = (int) Math.sqrt(number); + return sqrt * sqrt == number; + } + + /** + * Check if a number is perfect square or not + * + * @param number number to be checked + * @return {@code true} if {@code number} is perfect square, otherwise + * {@code false} + */ + public static boolean isPerfectSquareUsingPow(long number) { + long a = (long) Math.pow(number, 1.0 / 2); + return a * a == number; + } +} diff --git a/src/main/java/com/thealgorithms/maths/Perimeter.java b/src/main/java/com/thealgorithms/maths/Perimeter.java new file mode 100644 index 000000000000..f8aa1876d388 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/Perimeter.java @@ -0,0 +1,60 @@ +package com.thealgorithms.maths; + +// Perimeter of different 2D geometrical shapes +public final class Perimeter { + private Perimeter() { + } + + /** + * Calculate the Perimeter of regular polygon (equals sides) + * Examples of regular polygon are Equilateral Triangle, Square, Regular Pentagon, Regular + * Hexagon. + * + * @param n for number of sides. + * @param side for length of each side. + * @return Perimeter of given polygon + */ + public static float perimeterRegularPolygon(int n, float side) { + return n * side; + } + + /** + * Calculate the Perimeter of irregular polygon (unequals sides) + * Examples of irregular polygon are scalent triangle, irregular quadrilateral, irregular + * Pentagon, irregular Hexagon. + * + * @param side1 for length of side 1 + * @param side2 for length of side 2 + * @param side3 for length of side 3 + * @param sides for length of remaining sides + * @return Perimeter of given trapezoid. + */ + public static float perimeterIrregularPolygon(float side1, float side2, float side3, float... sides) { + float perimeter = side1 + side2 + side3; + for (float side : sides) { + perimeter += side; + } + return perimeter; + } + + /** + * Calculate the Perimeter of rectangle + * + * @param length for length of rectangle + * @param breadth for breadth of rectangle + * @return Perimeter of given rectangle + */ + public static float perimeterRectangle(float length, float breadth) { + return 2 * (length + breadth); + } + + /** + * Calculate the Perimeter or Circumference of circle. + * + * @param r for radius of circle. + * @return circumference of given circle. + */ + public static double perimeterCircle(float r) { + return 2 * Math.PI * r; + } +} diff --git a/src/main/java/com/thealgorithms/maths/PiNilakantha.java b/src/main/java/com/thealgorithms/maths/PiNilakantha.java new file mode 100644 index 000000000000..6d43f134c94c --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/PiNilakantha.java @@ -0,0 +1,44 @@ +package com.thealgorithms.maths; + +public final class PiNilakantha { + private PiNilakantha() { + } + + // Calculates Pi using Nilakantha's infinite series + // Method 2 in the following link explains the algorithm + // https://en.scratch-wiki.info/wiki/Calculating_Pi + public static void main(String[] args) { + assert calculatePi(0) == 3.0; + assert calculatePi(10) > 3.0; + assert calculatePi(100) < 4.0; + + System.out.println(calculatePi(500)); + } + + /** + * @param iterations number of times the infinite series gets repeated Pi + * get more accurate the higher the value of iterations is Values from 0 up + * to 500 are allowed since double precision is not sufficient for more than + * about 500 repetitions of this algorithm + * @return the pi value of the calculation with a precision of x iteration + */ + public static double calculatePi(int iterations) { + if (iterations < 0 || iterations > 500) { + throw new IllegalArgumentException("Please input Integer Number between 0 and 500"); + } + + double pi = 3; + int divCounter = 2; + + for (int i = 0; i < iterations; i++) { + if (i % 2 == 0) { + pi = pi + 4.0 / (divCounter * (divCounter + 1) * (divCounter + 2)); + } else { + pi = pi - 4.0 / (divCounter * (divCounter + 1) * (divCounter + 2)); + } + + divCounter += 2; + } + return pi; + } +} diff --git a/src/main/java/com/thealgorithms/maths/PollardRho.java b/src/main/java/com/thealgorithms/maths/PollardRho.java new file mode 100644 index 000000000000..7fa913b21b7e --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/PollardRho.java @@ -0,0 +1,80 @@ +package com.thealgorithms.maths; + +/* + * Java program for pollard rho algorithm + * The algorithm is used to factorize a number n = pq, + * where p is a non-trivial factor. + * Pollard's rho algorithm is an algorithm for integer factorization + * and it takes as its inputs n, the integer to be factored; + * and g(x), a polynomial in x computed modulo n. + * In the original algorithm, g(x) = ((x ^ 2) − 1) mod n, + * but nowadays it is more common to use g(x) = ((x ^ 2) + 1 ) mod n. + * The output is either a non-trivial factor of n, or failure. + * It performs the following steps: + * x ← 2 + * y ← 2 + * d ← 1 + + * while d = 1: + * x ← g(x) + * y ← g(g(y)) + * d ← gcd(|x - y|, n) + + * if d = n: + * return failure + * else: + * return d + + * Here x and y corresponds to xi and xj in the previous section. + * Note that this algorithm may fail to find a nontrivial factor even when n is composite. + * In that case, the method can be tried again, using a starting value other than 2 or a different + g(x) + * + * Wikipedia: https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm + * + * Author: Akshay Dubey (https://github.com/itsAkshayDubey) + * + * */ +public final class PollardRho { + private PollardRho() { + } + + /** + * This method returns a polynomial in x computed modulo n + * + * @param base Integer base of the polynomial + * @param modulus Integer is value which is to be used to perform modulo operation over the + * polynomial + * @return Integer (((base * base) - 1) % modulus) + */ + static int g(int base, int modulus) { + return ((base * base) - 1) % modulus; + } + + /** + * This method returns a non-trivial factor of given integer number + * + * @param number Integer is a integer value whose non-trivial factor is to be found + * @return Integer non-trivial factor of number + * @throws RuntimeException object if GCD of given number cannot be found + */ + static int pollardRho(int number) { + int x = 2; + int y = 2; + int d = 1; + while (d == 1) { + // tortoise move + x = g(x, number); + + // hare move + y = g(g(y, number), number); + + // check GCD of |x-y| and number + d = GCD.gcd(Math.abs(x - y), number); + } + if (d == number) { + throw new RuntimeException("GCD cannot be found."); + } + return d; + } +} diff --git a/src/main/java/com/thealgorithms/maths/Pow.java b/src/main/java/com/thealgorithms/maths/Pow.java new file mode 100644 index 000000000000..377b37ac4569 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/Pow.java @@ -0,0 +1,36 @@ +package com.thealgorithms.maths; + +/** + * A utility class for computing exponentiation (power) of integers. + * <p> + * This class provides a method to calculate the value of a base raised to a given exponent using a simple iterative approach. + * For example, given a base {@code a} and an exponent {@code b}, the class computes {@code a}<sup>{@code b}</sup>. + * </p> + */ +public final class Pow { + private Pow() { + } + + /** + * Computes the value of the base raised to the power of the exponent. + * <p> + * The method calculates {@code a}<sup>{@code b}</sup> by iteratively multiplying the base {@code a} with itself {@code b} times. + * If the exponent {@code b} is negative, an {@code IllegalArgumentException} is thrown. + * </p> + * + * @param a the base of the exponentiation. Must be a non-negative integer. + * @param b the exponent to which the base {@code a} is raised. Must be a non-negative integer. + * @return the result of {@code a}<sup>{@code b}</sup> as a {@code long}. + * @throws IllegalArgumentException if {@code b} is negative. + */ + public static long pow(int a, int b) { + if (b < 0) { + throw new IllegalArgumentException("Exponent must be non-negative."); + } + long result = 1; + for (int i = 1; i <= b; i++) { + result *= a; + } + return result; + } +} diff --git a/src/main/java/com/thealgorithms/maths/PowerOfTwoOrNot.java b/src/main/java/com/thealgorithms/maths/PowerOfTwoOrNot.java new file mode 100644 index 000000000000..c1f8beffdb2e --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/PowerOfTwoOrNot.java @@ -0,0 +1,21 @@ +package com.thealgorithms.maths; + +/** + * A utility to check if a given number is power of two or not. For example 8,16 + * etc. + */ +public final class PowerOfTwoOrNot { + private PowerOfTwoOrNot() { + } + + /** + * Checks whether given number is power of two or not. + * + * @param number the number to check + * @return {@code true} if given number is power of two, otherwise + * {@code false} + */ + public static boolean checkIfPowerOfTwoOrNot(final int number) { + return number != 0 && ((number & (number - 1)) == 0); + } +} diff --git a/src/main/java/com/thealgorithms/maths/PowerUsingRecursion.java b/src/main/java/com/thealgorithms/maths/PowerUsingRecursion.java new file mode 100644 index 000000000000..93c8252ab929 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/PowerUsingRecursion.java @@ -0,0 +1,22 @@ +package com.thealgorithms.maths; + +/** + * calculate Power using Recursion + * @author Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi) + */ + +public final class PowerUsingRecursion { + private PowerUsingRecursion() { + } + + public static double power(double base, int exponent) { + // Base case: anything raised to the power of 0 is 1 + if (exponent == 0) { + return 1; + } + + // Recursive case: base ^ exponent = base * base ^ (exponent - 1) + // Recurse with a smaller exponent and multiply with base + return base * power(base, exponent - 1); + } +} diff --git a/src/main/java/com/thealgorithms/maths/Prime/LiouvilleLambdaFunction.java b/src/main/java/com/thealgorithms/maths/Prime/LiouvilleLambdaFunction.java new file mode 100644 index 000000000000..73535b3aedae --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/Prime/LiouvilleLambdaFunction.java @@ -0,0 +1,36 @@ +package com.thealgorithms.maths.Prime; + +/* + * Java program for liouville lambda function + * For any positive integer n, define λ(n) as the sum of the primitive nth roots of unity. + * It has values in {−1, 1} depending on the factorization of n into prime factors: + * λ(n) = +1 if n is a positive integer with an even number of prime factors. + * λ(n) = −1 if n is a positive integer with an odd number of prime factors. + * Wikipedia: https://en.wikipedia.org/wiki/Liouville_function + * + * Author: Akshay Dubey (https://github.com/itsAkshayDubey) + * + * */ + +public final class LiouvilleLambdaFunction { + private LiouvilleLambdaFunction() { + } + + /** + * This method returns λ(n) of given number n + * + * @param number Integer value which λ(n) is to be calculated + * @return 1 when number has even number of prime factors + * -1 when number has odd number of prime factors + * @throws IllegalArgumentException when number is negative + */ + public static int liouvilleLambda(int number) { + if (number <= 0) { + // throw exception when number is less than or is zero + throw new IllegalArgumentException("Number must be greater than zero."); + } + + // return 1 if size of prime factor list is even, -1 otherwise + return PrimeFactorization.pfactors(number).size() % 2 == 0 ? 1 : -1; + } +} diff --git a/src/main/java/com/thealgorithms/maths/Prime/MillerRabinPrimalityCheck.java b/src/main/java/com/thealgorithms/maths/Prime/MillerRabinPrimalityCheck.java new file mode 100644 index 000000000000..debe3a214a32 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/Prime/MillerRabinPrimalityCheck.java @@ -0,0 +1,121 @@ +package com.thealgorithms.maths.Prime; + +import java.util.Random; + +public final class MillerRabinPrimalityCheck { + private MillerRabinPrimalityCheck() { + } + + /** + * Check whether the given number is prime or not + * MillerRabin algorithm is probabilistic. There is also an altered version which is deterministic. + * https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test + * https://cp-algorithms.com/algebra/primality_tests.html + * + * @param n Whole number which is tested on primality + * @param k Number of iterations + * If n is composite then running k iterations of the Miller–Rabin + * test will declare n probably prime with a probability at most 4^(−k) + * @return true or false whether the given number is probably prime or not + */ + + public static boolean millerRabin(long n, int k) { // returns true if n is probably prime, else returns false. + if (n < 4) { + return n == 2 || n == 3; + } + + int s = 0; + long d = n - 1; + while ((d & 1) == 0) { + d >>= 1; + s++; + } + Random rnd = new Random(); + for (int i = 0; i < k; i++) { + long a = 2 + rnd.nextLong(n) % (n - 3); + if (checkComposite(n, a, d, s)) { + return false; + } + } + return true; + } + + public static boolean deterministicMillerRabin(long n) { // returns true if n is prime, else returns false. + if (n < 2) { + return false; + } + + int r = 0; + long d = n - 1; + while ((d & 1) == 0) { + d >>= 1; + r++; + } + + for (int a : new int[] {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}) { + if (n == a) { + return true; + } + if (checkComposite(n, a, d, r)) { + return false; + } + } + return true; + } + + /** + * Check if number n is composite (probabilistic) + * + * @param n Whole number which is tested for compositeness + * @param a Random number (prime base) to check if it holds certain equality + * @param d Number which holds this equation: 'n - 1 = 2^s * d' + * @param s Number of twos in (n - 1) factorization + * + * @return true or false whether the numbers hold the equation or not + * the equations are described on the websites mentioned at the beginning of the class + */ + private static boolean checkComposite(long n, long a, long d, int s) { + long x = powerModP(a, d, n); + if (x == 1 || x == n - 1) { + return false; + } + for (int r = 1; r < s; r++) { + x = powerModP(x, 2, n); + if (x == n - 1) { + return false; + } + } + return true; + } + + private static long powerModP(long x, long y, long p) { + long res = 1; // Initialize result + + x = x % p; // Update x if it is more than or equal to p + + if (x == 0) { + return 0; // In case x is divisible by p; + } + while (y > 0) { + // If y is odd, multiply x with result + if ((y & 1) == 1) { + res = multiplyModP(res, x, p); + } + + // y must be even now + y = y >> 1; // y = y/2 + x = multiplyModP(x, x, p); + } + return res; + } + + private static long multiplyModP(long a, long b, long p) { + long aHi = a >> 24; + long aLo = a & ((1 << 24) - 1); + long bHi = b >> 24; + long bLo = b & ((1 << 24) - 1); + long result = ((((aHi * bHi << 16) % p) << 16) % p) << 16; + result += ((aLo * bHi + aHi * bLo) << 24) + aLo * bLo; + return result % p; + } +} diff --git a/src/main/java/com/thealgorithms/maths/Prime/MobiusFunction.java b/src/main/java/com/thealgorithms/maths/Prime/MobiusFunction.java new file mode 100644 index 000000000000..3d4e4eff0f03 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/Prime/MobiusFunction.java @@ -0,0 +1,57 @@ +package com.thealgorithms.maths.Prime; + +/* + * Java program for mobius function + * For any positive integer n, define μ(n) as the sum of the primitive nth roots of unity. + * It has values in {−1, 0, 1} depending on the factorization of n into prime factors: + * μ(n) = +1 if n is a square-free positive integer with an even number of prime factors. + * μ(n) = −1 if n is a square-free positive integer with an odd number of prime factors. + * μ(n) = 0 if n has a squared prime factor. + * Wikipedia: https://en.wikipedia.org/wiki/M%C3%B6bius_function + * + * Author: Akshay Dubey (https://github.com/itsAkshayDubey) + * + * */ +public final class MobiusFunction { + private MobiusFunction() { + } + + /** + * This method returns μ(n) of given number n + * + * @param number Integer value which μ(n) is to be calculated + * @return 1 when number is less than or equals 1 + * or number has even number of prime factors + * 0 when number has repeated prime factor + * -1 when number has odd number of prime factors + */ + public static int mobius(int number) { + if (number <= 0) { + // throw exception when number is less than or is zero + throw new IllegalArgumentException("Number must be greater than zero."); + } + + if (number == 1) { + // return 1 if number passed is less or is 1 + return 1; + } + + int primeFactorCount = 0; + + for (int i = 1; i <= number; i++) { + // find prime factors of number + if (number % i == 0 && PrimeCheck.isPrime(i)) { + // check if number is divisible by square of prime factor + if (number % (i * i) == 0) { + // if number is divisible by square of prime factor + return 0; + } + /*increment primeFactorCount by 1 + if number is not divisible by square of found prime factor*/ + primeFactorCount++; + } + } + + return (primeFactorCount % 2 == 0) ? 1 : -1; + } +} diff --git a/src/main/java/com/thealgorithms/maths/Prime/PrimeCheck.java b/src/main/java/com/thealgorithms/maths/Prime/PrimeCheck.java new file mode 100644 index 000000000000..91c490f70aef --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/Prime/PrimeCheck.java @@ -0,0 +1,85 @@ +package com.thealgorithms.maths.Prime; + +import java.util.Scanner; + +public final class PrimeCheck { + private PrimeCheck() { + } + + public static void main(String[] args) { + Scanner scanner = new Scanner(System.in); + + System.out.print("Enter a number: "); + int n = scanner.nextInt(); + if (isPrime(n)) { + System.out.println("algo1 verify that " + n + " is a prime number"); + } else { + System.out.println("algo1 verify that " + n + " is not a prime number"); + } + + if (fermatPrimeChecking(n, 20)) { + System.out.println("algo2 verify that " + n + " is a prime number"); + } else { + System.out.println("algo2 verify that " + n + " is not a prime number"); + } + scanner.close(); + } + + /** + * * + * Checks if a number is prime or not + * + * @param n the number + * @return {@code true} if {@code n} is prime + */ + public static boolean isPrime(int n) { + if (n == 2) { + return true; + } + if (n < 2 || n % 2 == 0) { + return false; + } + for (int i = 3, limit = (int) Math.sqrt(n); i <= limit; i += 2) { + if (n % i == 0) { + return false; + } + } + return true; + } + + /** + * * + * Checks if a number is prime or not + * + * @param n the number + * @return {@code true} if {@code n} is prime + */ + public static boolean fermatPrimeChecking(int n, int iteration) { + long a; + int up = n - 2; + int down = 2; + for (int i = 0; i < iteration; i++) { + a = (long) Math.floor(Math.random() * (up - down + 1) + down); + if (modPow(a, n - 1, n) != 1) { + return false; + } + } + return true; + } + + /** + * * + * @param a basis + * @param b exponent + * @param c modulo + * @return (a^b) mod c + */ + private static long modPow(long a, long b, long c) { + long res = 1; + for (int i = 0; i < b; i++) { + res *= a; + res %= c; + } + return res % c; + } +} diff --git a/src/main/java/com/thealgorithms/maths/Prime/PrimeFactorization.java b/src/main/java/com/thealgorithms/maths/Prime/PrimeFactorization.java new file mode 100644 index 000000000000..e12002b3d8c7 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/Prime/PrimeFactorization.java @@ -0,0 +1,40 @@ +package com.thealgorithms.maths.Prime; + +/* + * Authors: + * (1) Aitor Fidalgo Sánchez (https://github.com/aitorfi) + * (2) Akshay Dubey (https://github.com/itsAkshayDubey) + */ + +import java.util.ArrayList; +import java.util.List; + +public final class PrimeFactorization { + private PrimeFactorization() { + } + + public static List<Integer> pfactors(int n) { + List<Integer> primeFactors = new ArrayList<>(); + + if (n == 0) { + return primeFactors; + } + + while (n % 2 == 0) { + primeFactors.add(2); + n /= 2; + } + + for (int i = 3; i <= Math.sqrt(n); i += 2) { + while (n % i == 0) { + primeFactors.add(i); + n /= i; + } + } + + if (n > 2) { + primeFactors.add(n); + } + return primeFactors; + } +} diff --git a/src/main/java/com/thealgorithms/maths/Prime/SquareFreeInteger.java b/src/main/java/com/thealgorithms/maths/Prime/SquareFreeInteger.java new file mode 100644 index 000000000000..15c0a8a691cd --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/Prime/SquareFreeInteger.java @@ -0,0 +1,45 @@ +package com.thealgorithms.maths.Prime; +/* + * Java program for Square free integer + * This class has a function which checks + * if an integer has repeated prime factors + * and will return false if the number has repeated prime factors. + * true otherwise + * Wikipedia: https://en.wikipedia.org/wiki/Square-free_integer + * + * Author: Akshay Dubey (https://github.com/itsAkshayDubey) + * + * */ + +import java.util.HashSet; +import java.util.List; + +public final class SquareFreeInteger { + private SquareFreeInteger() { + } + /** + * This method returns whether an integer is square free + * + * @param number Integer value which is to be checked + * @return false when number has repeated prime factors + * true when number has non repeated prime factors + * @throws IllegalArgumentException when number is negative or zero + */ + public static boolean isSquareFreeInteger(int number) { + + if (number <= 0) { + // throw exception when number is less than or is zero + throw new IllegalArgumentException("Number must be greater than zero."); + } + + // Store prime factors of number which is passed as argument + // in a list + List<Integer> primeFactorsList = PrimeFactorization.pfactors(number); + + // Create set from list of prime factors of integer number + // if size of list and set is equal then the argument passed to this method is square free + // if size of list and set is not equal then the argument passed to this method is not + // square free + return primeFactorsList.size() == new HashSet<>(primeFactorsList).size(); + } +} diff --git a/src/main/java/com/thealgorithms/maths/PronicNumber.java b/src/main/java/com/thealgorithms/maths/PronicNumber.java new file mode 100644 index 000000000000..c2ad2a8139c8 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/PronicNumber.java @@ -0,0 +1,51 @@ +package com.thealgorithms.maths; + +/* + * Java program for Pronic Number + * Pronic Number: A number n is a pronic number if + * it is equal to product of two consecutive numbers m and m+1. + * Wikipedia: https://en.wikipedia.org/wiki/Pronic_number + * + * Author: Akshay Dubey (https://github.com/itsAkshayDubey) + * + * */ + +public final class PronicNumber { + private PronicNumber() { + } + + /** + * This method checks if the given number is pronic number or non-pronic number + * + * @param inputNumber Integer value which is to be checked if is a pronic number or not + * @return true if input number is a pronic number, false otherwise + */ + static boolean isPronic(int inputNumber) { + if (inputNumber == 0) { + return true; + } + // Iterating from 0 to input_number + for (int i = 0; i <= inputNumber; i++) { + // Checking if product of i and (i+1) is equals input_number + if (i * (i + 1) == inputNumber && i != inputNumber) { + // return true if product of i and (i+1) is equals input_number + return true; + } + } + + // return false if product of i and (i+1) for all values from 0 to input_number is not + // equals input_number + return false; + } + + /** + * This method checks if the given number is pronic number or non-pronic number using square root of number for finding divisors + * + * @param number Integer value which is to be checked if is a pronic number or not + * @return true if input number is a pronic number, false otherwise + */ + public static boolean isPronicNumber(int number) { + int squareRoot = (int) Math.sqrt(number); // finding just smaller divisor of the number than its square root. + return squareRoot * (squareRoot + 1) == number; + } +} diff --git a/src/main/java/com/thealgorithms/maths/PythagoreanTriple.java b/src/main/java/com/thealgorithms/maths/PythagoreanTriple.java new file mode 100644 index 000000000000..f535e9e6929b --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/PythagoreanTriple.java @@ -0,0 +1,36 @@ +package com.thealgorithms.maths; + +/** + * https://en.wikipedia.org/wiki/Pythagorean_triple + */ +public final class PythagoreanTriple { + private PythagoreanTriple() { + } + + public static void main(String[] args) { + assert isPythagTriple(3, 4, 5); + assert isPythagTriple(5, 12, 13); + assert isPythagTriple(6, 8, 10); + assert !isPythagTriple(10, 20, 30); + assert !isPythagTriple(6, 8, 100); + assert !isPythagTriple(-1, -1, 1); + } + + /** + * Check if a,b,c are a Pythagorean Triple + * + * @param a x/y component length of a right triangle + * @param b y/x component length of a right triangle + * @param c hypotenuse length of a right triangle + * @return boolean <tt>true</tt> if a, b, c satisfy the Pythagorean theorem, + * otherwise + * <tt>false</tt> + */ + public static boolean isPythagTriple(int a, int b, int c) { + if (a <= 0 || b <= 0 || c <= 0) { + return false; + } else { + return (a * a) + (b * b) == (c * c); + } + } +} diff --git a/src/main/java/com/thealgorithms/maths/QuadraticEquationSolver.java b/src/main/java/com/thealgorithms/maths/QuadraticEquationSolver.java new file mode 100644 index 000000000000..cd654c5dc023 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/QuadraticEquationSolver.java @@ -0,0 +1,60 @@ +package com.thealgorithms.maths; + +/** + * This class represents a complex number which has real and imaginary part + */ +class ComplexNumber { + Double real; + Double imaginary; + + ComplexNumber(double real, double imaginary) { + this.real = real; + this.imaginary = imaginary; + } + + ComplexNumber(double real) { + this.real = real; + this.imaginary = null; + } +} + +/** + * Quadratic Equation Formula is used to find + * the roots of a quadratic equation of the form ax^2 + bx + c = 0 + * + * @see <a href="https://en.wikipedia.org/wiki/Quadratic_equation">Quadratic Equation</a> + */ +public class QuadraticEquationSolver { + /** + * Function takes in the coefficients of the quadratic equation + * + * @param a is the coefficient of x^2 + * @param b is the coefficient of x + * @param c is the constant + * @return roots of the equation which are ComplexNumber type + */ + public ComplexNumber[] solveEquation(double a, double b, double c) { + double discriminant = b * b - 4 * a * c; + + // if discriminant is positive, roots will be different + if (discriminant > 0) { + return new ComplexNumber[] {new ComplexNumber((-b + Math.sqrt(discriminant)) / (2 * a)), new ComplexNumber((-b - Math.sqrt(discriminant)) / (2 * a))}; + } + + // if discriminant is zero, roots will be same + if (discriminant == 0) { + return new ComplexNumber[] {new ComplexNumber((-b) / (2 * a))}; + } + + // if discriminant is negative, roots will have imaginary parts + if (discriminant < 0) { + double realPart = -b / (2 * a); + double imaginaryPart = Math.sqrt(-discriminant) / (2 * a); + + return new ComplexNumber[] {new ComplexNumber(realPart, imaginaryPart), new ComplexNumber(realPart, -imaginaryPart)}; + } + + // return no roots + return new ComplexNumber[] {}; + } +} diff --git a/src/main/java/com/thealgorithms/maths/ReverseNumber.java b/src/main/java/com/thealgorithms/maths/ReverseNumber.java new file mode 100644 index 000000000000..667a0a43fc2c --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/ReverseNumber.java @@ -0,0 +1,29 @@ +package com.thealgorithms.maths; + +/** + * @brief utility class reversing numbers + */ +public final class ReverseNumber { + private ReverseNumber() { + } + + /** + * @brief reverses the input number + * @param number the input number + * @exception IllegalArgumentException number is negative + * @return the number created by reversing the order of digits of the input number + */ + public static int reverseNumber(int number) { + if (number < 0) { + throw new IllegalArgumentException("number must be nonnegative."); + } + + int result = 0; + while (number > 0) { + result *= 10; + result += number % 10; + number /= 10; + } + return result; + } +} diff --git a/src/main/java/com/thealgorithms/maths/RomanNumeralUtil.java b/src/main/java/com/thealgorithms/maths/RomanNumeralUtil.java new file mode 100644 index 000000000000..8f5d44dbe146 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/RomanNumeralUtil.java @@ -0,0 +1,73 @@ +package com.thealgorithms.maths; + +/** + * Translates numbers into the Roman Numeral System. + * + * @see <a href="https://en.wikipedia.org/wiki/Roman_numerals">Roman + * numerals</a> + * @author Sokratis Fotkatzikis + * @version 1.0 + */ +public final class RomanNumeralUtil { + private RomanNumeralUtil() { + } + + private static final int MIN_VALUE = 1; + private static final int MAX_VALUE = 5999; + // 1000-5999 + private static final String[] RN_M = { + "", + "M", + "MM", + "MMM", + "MMMM", + "MMMMM", + }; + // 100-900 + private static final String[] RN_C = { + "", + "C", + "CC", + "CCC", + "CD", + "D", + "DC", + "DCC", + "DCCC", + "CM", + }; + // 10-90 + private static final String[] RN_X = { + "", + "X", + "XX", + "XXX", + "XL", + "L", + "LX", + "LXX", + "LXXX", + "XC", + }; + // 1-9 + private static final String[] RN_I = { + "", + "I", + "II", + "III", + "IV", + "V", + "VI", + "VII", + "VIII", + "IX", + }; + + public static String generate(int number) { + if (number < MIN_VALUE || number > MAX_VALUE) { + throw new IllegalArgumentException(String.format("The number must be in the range [%d, %d]", MIN_VALUE, MAX_VALUE)); + } + + return (RN_M[number / 1000] + RN_C[number % 1000 / 100] + RN_X[number % 100 / 10] + RN_I[number % 10]); + } +} diff --git a/src/main/java/com/thealgorithms/maths/SecondMinMax.java b/src/main/java/com/thealgorithms/maths/SecondMinMax.java new file mode 100644 index 000000000000..e5a2d3b89085 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/SecondMinMax.java @@ -0,0 +1,60 @@ +package com.thealgorithms.maths; + +import java.util.function.BiPredicate; + +public final class SecondMinMax { + + /** + * Utility class for finding second maximum or minimum based on BiPredicate + * @exception IllegalArgumentException => if input array is of length less than 2 also if all elements are same + * @return the second minimum / maximum value from the input array + * @author Bharath Sanjeevi ( https://github.com/BharathSanjeeviT ) + */ + + private SecondMinMax() { + } + + private static int secondBest(final int[] arr, final int initialVal, final BiPredicate<Integer, Integer> isBetter) { + checkInput(arr); + int best = initialVal; + int secBest = initialVal; + for (final int num : arr) { + if (isBetter.test(num, best)) { + secBest = best; + best = num; + } else if ((isBetter.test(num, secBest)) && (num != best)) { + secBest = num; + } + } + checkOutput(secBest, initialVal); + return secBest; + } + + /** + * @brief Finds the Second minimum / maximum value from the array + * @param arr the input array + * @exception IllegalArgumentException => if input array is of length less than 2 also if all elements are same + * @return the second minimum / maximum value from the input array + * @author Bharath Sanjeevi ( https://github.com/BharathSanjeeviT ) + */ + + public static int findSecondMin(final int[] arr) { + return secondBest(arr, Integer.MAX_VALUE, (a, b) -> a < b); + } + + public static int findSecondMax(final int[] arr) { + return secondBest(arr, Integer.MIN_VALUE, (a, b) -> a > b); + } + + private static void checkInput(final int[] arr) { + if (arr.length < 2) { + throw new IllegalArgumentException("Input array must have length of at least two"); + } + } + + private static void checkOutput(final int secNum, final int initialVal) { + if (secNum == initialVal) { + throw new IllegalArgumentException("Input array should have at least 2 distinct elements"); + } + } +} diff --git a/src/main/java/com/thealgorithms/maths/SieveOfEratosthenes.java b/src/main/java/com/thealgorithms/maths/SieveOfEratosthenes.java new file mode 100644 index 000000000000..f22d22e8c6af --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/SieveOfEratosthenes.java @@ -0,0 +1,66 @@ +package com.thealgorithms.maths; + +import java.util.Arrays; + +/** + * @brief utility class implementing <a href="https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes">Sieve of Eratosthenes</a> + */ +public final class SieveOfEratosthenes { + private SieveOfEratosthenes() { + } + + private static void checkInput(int n) { + if (n <= 0) { + throw new IllegalArgumentException("n must be positive."); + } + } + + private static Type[] sievePrimesTill(int n) { + checkInput(n); + Type[] isPrimeArray = new Type[n + 1]; + Arrays.fill(isPrimeArray, Type.PRIME); + isPrimeArray[0] = Type.NOT_PRIME; + isPrimeArray[1] = Type.NOT_PRIME; + + double cap = Math.sqrt(n); + for (int i = 2; i <= cap; i++) { + if (isPrimeArray[i] == Type.PRIME) { + for (int j = 2; i * j <= n; j++) { + isPrimeArray[i * j] = Type.NOT_PRIME; + } + } + } + return isPrimeArray; + } + + private static int countPrimes(Type[] isPrimeArray) { + return (int) Arrays.stream(isPrimeArray).filter(element -> element == Type.PRIME).count(); + } + + private static int[] extractPrimes(Type[] isPrimeArray) { + int numberOfPrimes = countPrimes(isPrimeArray); + int[] primes = new int[numberOfPrimes]; + int primeIndex = 0; + for (int curNumber = 0; curNumber < isPrimeArray.length; ++curNumber) { + if (isPrimeArray[curNumber] == Type.PRIME) { + primes[primeIndex++] = curNumber; + } + } + return primes; + } + + /** + * @brief finds all of the prime numbers up to the given upper (inclusive) limit + * @param n upper (inclusive) limit + * @exception IllegalArgumentException n is non-positive + * @return the array of all primes up to the given number (inclusive) + */ + public static int[] findPrimesTill(int n) { + return extractPrimes(sievePrimesTill(n)); + } + + private enum Type { + PRIME, + NOT_PRIME, + } +} diff --git a/src/main/java/com/thealgorithms/maths/SimpsonIntegration.java b/src/main/java/com/thealgorithms/maths/SimpsonIntegration.java new file mode 100644 index 000000000000..1163208a1f83 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/SimpsonIntegration.java @@ -0,0 +1,88 @@ +package com.thealgorithms.maths; + +import java.util.TreeMap; + +public class SimpsonIntegration { + + /* + * Calculate definite integrals by using Composite Simpson's rule. + * Wiki: https://en.wikipedia.org/wiki/Simpson%27s_rule#Composite_Simpson's_rule + * Given f a function and an even number N of intervals that divide the integration interval + * e.g. [a, b], we calculate the step h = (b-a)/N and create a table that contains all the x + * points of the real axis xi = x0 + i*h and the value f(xi) that corresponds to these xi. + * + * To evaluate the integral i use the formula below: + * I = h/3 * {f(x0) + 4*f(x1) + 2*f(x2) + 4*f(x3) + ... + 2*f(xN-2) + 4*f(xN-1) + f(xN)} + * + */ + public static void main(String[] args) { + SimpsonIntegration integration = new SimpsonIntegration(); + + // Give random data for the example purposes + int n = 16; + double a = 1; + double b = 3; + + // Check so that n is even + if (n % 2 != 0) { + System.out.println("n must be even number for Simpsons method. Aborted"); + System.exit(1); + } + + // Calculate step h and evaluate the integral + double h = (b - a) / (double) n; + double integralEvaluation = integration.simpsonsMethod(n, h, a); + System.out.println("The integral is equal to: " + integralEvaluation); + } + + /* + * @param N: Number of intervals (must be even number N=2*k) + * @param h: Step h = (b-a)/N + * @param a: Starting point of the interval + * @param b: Ending point of the interval + * + * The interpolation points xi = x0 + i*h are stored the treeMap data + * + * @return result of the integral evaluation + */ + public double simpsonsMethod(int n, double h, double a) { + TreeMap<Integer, Double> data = new TreeMap<>(); // Key: i, Value: f(xi) + double temp; + double xi = a; // Initialize the variable xi = x0 + 0*h + + // Create the table of xi and yi points + for (int i = 0; i <= n; i++) { + temp = f(xi); // Get the value of the function at that point + data.put(i, temp); + xi += h; // Increase the xi to the next point + } + + // Apply the formula + double integralEvaluation = 0; + for (int i = 0; i < data.size(); i++) { + if (i == 0 || i == data.size() - 1) { + integralEvaluation += data.get(i); + System.out.println("Multiply f(x" + i + ") by 1"); + } else if (i % 2 != 0) { + integralEvaluation += (double) 4 * data.get(i); + System.out.println("Multiply f(x" + i + ") by 4"); + } else { + integralEvaluation += (double) 2 * data.get(i); + System.out.println("Multiply f(x" + i + ") by 2"); + } + } + + // Multiply by h/3 + integralEvaluation = h / 3 * integralEvaluation; + + // Return the result + return integralEvaluation; + } + + // Sample function f + // Function f(x) = e^(-x) * (4 - x^2) + public double f(double x) { + return Math.exp(-x) * (4 - Math.pow(x, 2)); + // return Math.sqrt(x); + } +} diff --git a/src/main/java/com/thealgorithms/maths/SolovayStrassenPrimalityTest.java b/src/main/java/com/thealgorithms/maths/SolovayStrassenPrimalityTest.java new file mode 100644 index 000000000000..caa1abfc3203 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/SolovayStrassenPrimalityTest.java @@ -0,0 +1,133 @@ +package com.thealgorithms.maths; + +import java.util.Random; + +/** + * This class implements the Solovay-Strassen primality test, + * which is a probabilistic algorithm to determine whether a number is prime. + * The algorithm is based on properties of the Jacobi symbol and modular exponentiation. + * + * For more information, go to {@link https://en.wikipedia.org/wiki/Solovay%E2%80%93Strassen_primality_test} + */ +final class SolovayStrassenPrimalityTest { + + private final Random random; + + /** + * Constructs a SolovayStrassenPrimalityTest instance with a specified seed for randomness. + * + * @param seed the seed for generating random numbers + */ + private SolovayStrassenPrimalityTest(int seed) { + random = new Random(seed); + } + + /** + * Factory method to create an instance of SolovayStrassenPrimalityTest. + * + * @param seed the seed for generating random numbers + * @return a new instance of SolovayStrassenPrimalityTest + */ + public static SolovayStrassenPrimalityTest getSolovayStrassenPrimalityTest(int seed) { + return new SolovayStrassenPrimalityTest(seed); + } + + /** + * Calculates modular exponentiation using the method of exponentiation by squaring. + * + * @param base the base number + * @param exponent the exponent + * @param mod the modulus + * @return (base^exponent) mod mod + */ + private static long calculateModularExponentiation(long base, long exponent, long mod) { + long x = 1; // This will hold the result of (base^exponent) % mod + long y = base; // This holds the current base value being squared + + while (exponent > 0) { + // If exponent is odd, multiply the current base (y) with x + if (exponent % 2 == 1) { + x = x * y % mod; // Update result with current base + } + // Square the base for the next iteration + y = y * y % mod; // Update base to be y^2 + exponent = exponent / 2; // Halve the exponent for next iteration + } + + return x % mod; // Return final result after all iterations + } + + /** + * Computes the Jacobi symbol (a/n), which is a generalization of the Legendre symbol. + * + * @param a the numerator + * @param num the denominator (must be an odd positive integer) + * @return the Jacobi symbol value: 1, -1, or 0 + */ + public int calculateJacobi(long a, long num) { + // Check if num is non-positive or even; Jacobi symbol is not defined in these cases + if (num <= 0 || num % 2 == 0) { + return 0; + } + + a = a % num; // Reduce a modulo num to simplify calculations + int jacobi = 1; // Initialize Jacobi symbol value + + while (a != 0) { + // While a is even, reduce it and adjust jacobi based on properties of num + while (a % 2 == 0) { + a /= 2; // Divide a by 2 until it becomes odd + long nMod8 = num % 8; // Get num modulo 8 to check conditions for jacobi adjustment + if (nMod8 == 3 || nMod8 == 5) { + jacobi = -jacobi; // Flip jacobi sign based on properties of num modulo 8 + } + } + + long temp = a; // Temporarily store value of a + a = num; // Set a to be num for next iteration + num = temp; // Set num to be previous value of a + + // Adjust jacobi based on properties of both numbers when both are odd and congruent to 3 modulo 4 + if (a % 4 == 3 && num % 4 == 3) { + jacobi = -jacobi; // Flip jacobi sign again based on congruences + } + + a = a % num; // Reduce a modulo num for next iteration of Jacobi computation + } + + return (num == 1) ? jacobi : 0; // If num reduces to 1, return jacobi value, otherwise return 0 (not defined) + } + + /** + * Performs the Solovay-Strassen primality test on a given number. + * + * @param num the number to be tested for primality + * @param iterations the number of iterations to run for accuracy + * @return true if num is likely prime, false if it is composite + */ + public boolean solovayStrassen(long num, int iterations) { + if (num <= 1) { + return false; // Numbers <=1 are not prime by definition. + } + if (num <= 3) { + return true; // Numbers <=3 are prime. + } + + for (int i = 0; i < iterations; i++) { + long r = Math.abs(random.nextLong() % (num - 1)) + 2; // Generate a non-negative random number. + long a = r % (num - 1) + 1; // Choose random 'a' in range [1, n-1]. + + long jacobi = (num + calculateJacobi(a, num)) % num; + // Calculate Jacobi symbol and adjust it modulo n. + + long mod = calculateModularExponentiation(a, (num - 1) / 2, num); + // Calculate modular exponentiation: a^((n-1)/2) mod n. + + if (jacobi == 0 || mod != jacobi) { + return false; // If Jacobi symbol is zero or doesn't match modular result, n is composite. + } + } + + return true; // If no contradictions found after all iterations, n is likely prime. + } +} diff --git a/src/main/java/com/thealgorithms/maths/SquareRootWithBabylonianMethod.java b/src/main/java/com/thealgorithms/maths/SquareRootWithBabylonianMethod.java new file mode 100644 index 000000000000..2b071307e2bc --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/SquareRootWithBabylonianMethod.java @@ -0,0 +1,23 @@ +package com.thealgorithms.maths; + +public final class SquareRootWithBabylonianMethod { + private SquareRootWithBabylonianMethod() { + } + + /** + * get the value, return the square root + * + * @param num contains elements + * @return the square root of num + */ + public static float squareRoot(float num) { + float a = num; + float b = 1; + double e = 0.000001; + while (a - b > e) { + a = (a + b) / 2; + b = num / a; + } + return a; + } +} diff --git a/src/main/java/com/thealgorithms/maths/SquareRootWithNewtonRaphsonMethod.java b/src/main/java/com/thealgorithms/maths/SquareRootWithNewtonRaphsonMethod.java new file mode 100644 index 000000000000..80d185c93785 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/SquareRootWithNewtonRaphsonMethod.java @@ -0,0 +1,33 @@ +package com.thealgorithms.maths; + +/* + *To learn about the method, visit the link below : + * https://en.wikipedia.org/wiki/Newton%27s_method + * + * To obtain the square root, no built-in functions should be used + * + * The formula to calculate the root is : root = 0.5(x + n/x), + * here, n is the no. whose square root has to be calculated and + * x has to be guessed such that, the calculation should result into + * the square root of n. + * And the root will be obtained when the error < 0.5 or the precision value can also + * be changed according to the user preference. + */ + +public final class SquareRootWithNewtonRaphsonMethod { + private SquareRootWithNewtonRaphsonMethod() { + } + + public static double squareRoot(int n) { + double x = n; // initially taking a guess that x = n. + double root = 0.5 * (x + n / x); // applying Newton-Raphson Method. + + while (Math.abs(root - x) > 0.0000001) { // root - x = error and error < 0.0000001, 0.0000001 + // is the precision value taken over here. + x = root; // decreasing the value of x to root, i.e. decreasing the guess. + root = 0.5 * (x + n / x); + } + + return root; + } +} diff --git a/src/main/java/com/thealgorithms/maths/StandardDeviation.java b/src/main/java/com/thealgorithms/maths/StandardDeviation.java new file mode 100644 index 000000000000..a8e88d930a9c --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/StandardDeviation.java @@ -0,0 +1,20 @@ +package com.thealgorithms.maths; + +public final class StandardDeviation { + private StandardDeviation() { + } + + public static double stdDev(double[] data) { + double variance = 0; + double avg = 0; + for (int i = 0; i < data.length; i++) { + avg += data[i]; + } + avg /= data.length; + for (int j = 0; j < data.length; j++) { + variance += Math.pow((data[j] - avg), 2); + } + variance /= data.length; + return Math.sqrt(variance); + } +} diff --git a/src/main/java/com/thealgorithms/maths/StandardScore.java b/src/main/java/com/thealgorithms/maths/StandardScore.java new file mode 100644 index 000000000000..22a9f550e114 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/StandardScore.java @@ -0,0 +1,10 @@ +package com.thealgorithms.maths; + +public final class StandardScore { + private StandardScore() { + } + + public static double zScore(double num, double mean, double stdDev) { + return (num - mean) / stdDev; + } +} diff --git a/src/main/java/com/thealgorithms/maths/StrobogrammaticNumber.java b/src/main/java/com/thealgorithms/maths/StrobogrammaticNumber.java new file mode 100644 index 000000000000..bcd9a8467e50 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/StrobogrammaticNumber.java @@ -0,0 +1,43 @@ +package com.thealgorithms.maths; + +import java.util.HashMap; +import java.util.Map; + +/** + * A strobogrammatic number is a number that remains the same when rotated 180 degrees. + * In other words, the number looks the same when rotated upside down. + * Examples of strobogrammatic numbers are "69", "88", "818", and "101". + * Numbers like "609" or "120" are not strobogrammatic because they do not look the same when rotated. + */ +public class StrobogrammaticNumber { + /** + * Check if a number is strobogrammatic + * @param number the number to be checked + * @return true if the number is strobogrammatic, false otherwise + */ + public boolean isStrobogrammatic(String number) { + Map<Character, Character> strobogrammaticMap = new HashMap<>(); + strobogrammaticMap.put('0', '0'); + strobogrammaticMap.put('1', '1'); + strobogrammaticMap.put('6', '9'); + strobogrammaticMap.put('8', '8'); + strobogrammaticMap.put('9', '6'); + + int left = 0; + int right = number.length() - 1; + + while (left <= right) { + char leftChar = number.charAt(left); + char rightChar = number.charAt(right); + + if (!strobogrammaticMap.containsKey(leftChar) || strobogrammaticMap.get(leftChar) != rightChar) { + return false; + } + + left++; + right--; + } + + return true; + } +} diff --git a/src/main/java/com/thealgorithms/maths/SumOfArithmeticSeries.java b/src/main/java/com/thealgorithms/maths/SumOfArithmeticSeries.java new file mode 100644 index 000000000000..315f0d3a7d28 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/SumOfArithmeticSeries.java @@ -0,0 +1,31 @@ +package com.thealgorithms.maths; + +/** + * In mathematics, an arithmetic progression (AP) or arithmetic sequence is a + * sequence of numbers such that the difference between the consecutive terms is + * constant. Difference here means the second minus the first. For instance, the + * sequence 5, 7, 9, 11, 13, 15, . . . is an arithmetic progression with common + * difference of 2. + * + * <p> + * Wikipedia: https://en.wikipedia.org/wiki/Arithmetic_progression + */ +public final class SumOfArithmeticSeries { + private SumOfArithmeticSeries() { + } + + /** + * Calculate sum of arithmetic series + * + * @param firstTerm the initial term of an arithmetic series + * @param commonDiff the common difference of an arithmetic series + * @param numOfTerms the total terms of an arithmetic series + * @return sum of given arithmetic series + */ + public static double sumOfSeries(final double firstTerm, final double commonDiff, final int numOfTerms) { + if (numOfTerms < 0) { + throw new IllegalArgumentException("numOfTerms nonnegative."); + } + return (numOfTerms / 2.0 * (2 * firstTerm + (numOfTerms - 1) * commonDiff)); + } +} diff --git a/src/main/java/com/thealgorithms/maths/SumOfDigits.java b/src/main/java/com/thealgorithms/maths/SumOfDigits.java new file mode 100644 index 000000000000..e5ec8a02025d --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/SumOfDigits.java @@ -0,0 +1,45 @@ +package com.thealgorithms.maths; + +public final class SumOfDigits { + private SumOfDigits() { + } + + /** + * Calculate the sum of digits of a number + * + * @param number the number contains digits + * @return sum of digits of given {@code number} + */ + public static int sumOfDigits(int number) { + final int base = 10; + number = Math.abs(number); + int sum = 0; + while (number != 0) { + sum += number % base; + number /= base; + } + return sum; + } + + /** + * Calculate the sum of digits of a number using recursion + * + * @param number the number contains digits + * @return sum of digits of given {@code number} + */ + public static int sumOfDigitsRecursion(int number) { + final int base = 10; + number = Math.abs(number); + return number < base ? number : number % base + sumOfDigitsRecursion(number / base); + } + + /** + * Calculate the sum of digits of a number using char array + * + * @param number the number contains digits + * @return sum of digits of given {@code number} + */ + public static int sumOfDigitsFast(final int number) { + return String.valueOf(Math.abs(number)).chars().map(c -> c - '0').reduce(0, Integer::sum); + } +} diff --git a/src/main/java/com/thealgorithms/maths/SumOfOddNumbers.java b/src/main/java/com/thealgorithms/maths/SumOfOddNumbers.java new file mode 100644 index 000000000000..c0a1e782659a --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/SumOfOddNumbers.java @@ -0,0 +1,25 @@ +package com.thealgorithms.maths; + +/** + * This program calculates the sum of the first n odd numbers. + * + * https://www.cuemath.com/algebra/sum-of-odd-numbers/ + */ + +public final class SumOfOddNumbers { + private SumOfOddNumbers() { + } + + /** + * Calculate sum of the first n odd numbers + * + * @param n the number of odd numbers to sum + * @return sum of the first n odd numbers + */ + public static int sumOfFirstNOddNumbers(final int n) { + if (n < 0) { + throw new IllegalArgumentException("n must be non-negative."); + } + return n * n; + } +} diff --git a/src/main/java/com/thealgorithms/maths/SumWithoutArithmeticOperators.java b/src/main/java/com/thealgorithms/maths/SumWithoutArithmeticOperators.java new file mode 100644 index 000000000000..5369182a0a94 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/SumWithoutArithmeticOperators.java @@ -0,0 +1,22 @@ +package com.thealgorithms.maths; + +public class SumWithoutArithmeticOperators { + + /** + * Calculate the sum of two numbers a and b without using any arithmetic operators (+, -, *, /). + * All the integers associated are unsigned 32-bit integers + *https://stackoverflow.com/questions/365522/what-is-the-best-way-to-add-two-numbers-without-using-the-operator + *@param a - It is the first number + *@param b - It is the second number + *@return returns an integer which is the sum of the first and second number + */ + + public int getSum(int a, int b) { + if (b == 0) { + return a; + } + int sum = a ^ b; + int carry = (a & b) << 1; + return getSum(sum, carry); + } +} diff --git a/src/main/java/com/thealgorithms/maths/TrinomialTriangle.java b/src/main/java/com/thealgorithms/maths/TrinomialTriangle.java new file mode 100644 index 000000000000..877ef4227afc --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/TrinomialTriangle.java @@ -0,0 +1,44 @@ +package com.thealgorithms.maths; + +/** + * The trinomial triangle is a variation of Pascal’s triangle. The difference + * between the two is that an entry in the trinomial triangle is the sum of the + * three (rather than the two in Pasacal’s triangle) entries above it + * + * Example Input: n = 4 Output 1 1 1 1 1 2 3 2 1 1 3 6 7 6 3 1 + */ +public final class TrinomialTriangle { + private TrinomialTriangle() { + } + + public static int trinomialValue(int n, int k) { + if (n == 0 && k == 0) { + return 1; + } + + if (k < -n || k > n) { + return 0; + } + + return (trinomialValue(n - 1, k - 1) + trinomialValue(n - 1, k) + trinomialValue(n - 1, k + 1)); + } + + public static void printTrinomial(int n) { + for (int i = 0; i < n; i++) { + for (int j = -i; j <= 0; j++) { + System.out.print(trinomialValue(i, j) + " "); + } + + for (int j = 1; j <= i; j++) { + System.out.print(trinomialValue(i, j) + " "); + } + + System.out.println(); + } + } + + public static void main(String[] argc) { + int n = 6; + printTrinomial(n); + } +} diff --git a/src/main/java/com/thealgorithms/maths/TwinPrime.java b/src/main/java/com/thealgorithms/maths/TwinPrime.java new file mode 100644 index 000000000000..f4e546a2d7a4 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/TwinPrime.java @@ -0,0 +1,35 @@ +package com.thealgorithms.maths; +/* + * Java program to find 'twin prime' of a prime number + * Twin Prime: Twin prime of a number n is (n+2) + * if and only if n & (n+2) are prime. + * Wikipedia: https://en.wikipedia.org/wiki/Twin_prime + * + * Author: Akshay Dubey (https://github.com/itsAkshayDubey) + * + * */ + +import com.thealgorithms.maths.Prime.PrimeCheck; + +public final class TwinPrime { + private TwinPrime() { + } + + /** + * This method returns twin prime of the integer value passed as argument + * + * @param inputNumber Integer value of which twin prime is to be found + * @return (number + 2) if number and (number + 2) are prime, -1 otherwise + */ + static int getTwinPrime(int inputNumber) { + + // if inputNumber and (inputNumber + 2) are both prime + // then return (inputNumber + 2) as a result + if (PrimeCheck.isPrime(inputNumber) && PrimeCheck.isPrime(inputNumber + 2)) { + return inputNumber + 2; + } + // if any one from inputNumber and (inputNumber + 2) or if both of them are not prime + // then return -1 as a result + return -1; + } +} diff --git a/src/main/java/com/thealgorithms/maths/UniformNumbers.java b/src/main/java/com/thealgorithms/maths/UniformNumbers.java new file mode 100644 index 000000000000..c83783aab0b3 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/UniformNumbers.java @@ -0,0 +1,50 @@ +package com.thealgorithms.maths; + +/** + * A positive integer is considered uniform if all + * of its digits are equal. For example, 222 is uniform, + * while 223 is not. + * Given two positive integers a and b, determine the + * number of uniform integers between a and b. + */ +public final class UniformNumbers { + // Private constructor to prevent instantiation of the utility class + private UniformNumbers() { + // Prevent instantiation + } + /** + * This function will find the number of uniform numbers + * from 1 to num + * @param num upper limit to find the uniform numbers + * @return the count of uniform numbers between 1 and num + */ + public static int uniformNumbers(int num) { + String numStr = Integer.toString(num); + int uniformCount = (numStr.length() - 1) * 9; + int finalUniform = Integer.parseInt(String.valueOf(numStr.charAt(0)).repeat(numStr.length())); + + if (finalUniform <= num) { + uniformCount += Integer.parseInt(String.valueOf(numStr.charAt(0))); + } else { + uniformCount += Integer.parseInt(String.valueOf(numStr.charAt(0))) - 1; + } + + return uniformCount; + } + /** + * This function will calculate the number of uniform numbers + * between a and b + * @param a lower bound of range + * @param b upper bound of range + * @return the count of uniform numbers between a and b + */ + public static int countUniformIntegers(int a, int b) { + if (b > a && b > 0 && a > 0) { + return uniformNumbers(b) - uniformNumbers(a - 1); + } else if (b == a) { + return 1; + } else { + return 0; + } + } +} diff --git a/src/main/java/com/thealgorithms/maths/VampireNumber.java b/src/main/java/com/thealgorithms/maths/VampireNumber.java new file mode 100644 index 000000000000..45bb9a587778 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/VampireNumber.java @@ -0,0 +1,48 @@ +package com.thealgorithms.maths; + +import java.util.ArrayList; + +/** + * In number theory, a vampire number (or true vampire number) is a composite + * natural number with an even number of digits, that can be factored into two + * natural numbers each with half as many digits as the original number and not + * both with trailing zeroes, where the two factors contain precisely all the + * digits of the original number, in any order, counting multiplicity. The first + * vampire number is 1260 = 21 × 60. + * + * @see <a href='https://en.wikipedia.org/wiki/Vampire_number'>Vampire number on Wikipedia</a> + */ +public final class VampireNumber { + // Forbid instantiation. + private VampireNumber() { + } + + static boolean isVampireNumber(int a, int b, boolean ignorePseudoVampireNumbers) { + // Pseudo vampire numbers don't have to be of n/2 digits. E.g., 126 = 6 x 21 is such a number. + if (ignorePseudoVampireNumbers && String.valueOf(a).length() != String.valueOf(b).length()) { + return false; + } + + String mulDigits = splitIntoSortedDigits(a * b); + String factorDigits = splitIntoSortedDigits(a, b); + + return mulDigits.equals(factorDigits); + } + + // Method to split a pair of numbers to digits and sort them in the ascending order. + static String splitIntoSortedDigits(int... nums) { + // Collect all digits in a list. + ArrayList<Integer> digits = new ArrayList<>(); + for (int num : nums) { + while (num > 0) { + digits.add(num % 10); + num /= 10; + } + } + + // Sort all digits and convert to String. + StringBuilder res = new StringBuilder(); + digits.stream().sorted().forEach(res::append); + return res.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/maths/VectorCrossProduct.java b/src/main/java/com/thealgorithms/maths/VectorCrossProduct.java new file mode 100644 index 000000000000..e2769744bcda --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/VectorCrossProduct.java @@ -0,0 +1,125 @@ +package com.thealgorithms.maths; + +/** + * @file + * + * @brief Calculates the [Cross + * Product](https://en.wikipedia.org/wiki/Cross_product) and the magnitude of + * two mathematical 3D vectors. + * + * + * @details Cross Product of two vectors gives a vector. Direction Ratios of a + * vector are the numeric parts of the given vector. They are the tree parts of + * the vector which determine the magnitude (value) of the vector. The method of + * finding a cross product is the same as finding the determinant of an order 3 + * matrix consisting of the first row with unit vectors of magnitude 1, the + * second row with the direction ratios of the first vector and the third row + * with the direction ratios of the second vector. The magnitude of a vector is + * it's value expressed as a number. Let the direction ratios of the first + * vector, P be: a, b, c Let the direction ratios of the second vector, Q be: x, + * y, z Therefore the calculation for the cross product can be arranged as: + * + * ``` P x Q: 1 1 1 a b c x y z ``` + * + * The direction ratios (DR) are calculated as follows: 1st DR, J: (b * z) - (c + * * y) 2nd DR, A: -((a * z) - (c * x)) 3rd DR, N: (a * y) - (b * x) + * + * Therefore, the direction ratios of the cross product are: J, A, N The + * following Java Program calculates the direction ratios of the cross products + * of two vector. The program uses a function, cross() for doing so. The + * direction ratios for the first and the second vector has to be passed one by + * one separated by a space character. + * + * Magnitude of a vector is the square root of the sum of the squares of the + * direction ratios. + * + * + * For maintaining filename consistency, Vector class has been termed as + * VectorCrossProduct + * + * @author [Syed](https://github.com/roeticvampire) + */ +public class VectorCrossProduct { + + int x; + int y; + int z; + + // Default constructor, initialises all three Direction Ratios to 0 + VectorCrossProduct() { + x = 0; + y = 0; + z = 0; + } + + /** + * constructor, initialises Vector with given Direction Ratios + * + * @param vectorX set to x + * @param vectorY set to y + * @param vectorZ set to z + */ + VectorCrossProduct(int vectorX, int vectorY, int vectorZ) { + x = vectorX; + y = vectorY; + z = vectorZ; + } + + /** + * Returns the magnitude of the vector + * + * @return double + */ + double magnitude() { + return Math.sqrt(x * x + y * y + z * z); + } + + /** + * Returns the dot product of the current vector with a given vector + * + * @param b: the second vector + * @return int: the dot product + */ + int dotProduct(VectorCrossProduct b) { + return x * b.x + y * b.y + z * b.z; + } + + /** + * Returns the cross product of the current vector with a given vector + * + * @param b: the second vector + * @return vectorCrossProduct: the cross product + */ + VectorCrossProduct crossProduct(VectorCrossProduct b) { + VectorCrossProduct product = new VectorCrossProduct(); + product.x = (y * b.z) - (z * b.y); + product.y = -((x * b.z) - (z * b.x)); + product.z = (x * b.y) - (y * b.x); + return product; + } + + /** + * Display the Vector + */ + void displayVector() { + System.out.println("x : " + x + "\ty : " + y + "\tz : " + z); + } + + public static void main(String[] args) { + test(); + } + + static void test() { + // Create two vectors + VectorCrossProduct a = new VectorCrossProduct(1, -2, 3); + VectorCrossProduct b = new VectorCrossProduct(2, 0, 3); + + // Determine cross product + VectorCrossProduct crossProd = a.crossProduct(b); + crossProd.displayVector(); + + // Determine dot product + int dotProd = a.dotProduct(b); + System.out.println("Dot Product of a and b: " + dotProd); + } +} diff --git a/src/main/java/com/thealgorithms/maths/Volume.java b/src/main/java/com/thealgorithms/maths/Volume.java new file mode 100644 index 000000000000..0f282b2abae2 --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/Volume.java @@ -0,0 +1,105 @@ +package com.thealgorithms.maths; + +/* Calculate the volume of various shapes.*/ +public final class Volume { + private Volume() { + } + + /** + * Calculate the volume of a cube. + * + * @param sideLength length of the given cube's sides + * @return volume of the given cube + */ + public static double volumeCube(double sideLength) { + return sideLength * sideLength * sideLength; + } + + /** + * Calculate the volume of a cuboid. + * + * @param width width of given cuboid + * @param height height of given cuboid + * @param length length of given cuboid + * @return volume of given cuboid + */ + public static double volumeCuboid(double width, double height, double length) { + return width * height * length; + } + + /** + * Calculate the volume of a sphere. + * + * @param radius radius of given sphere + * @return volume of given sphere + */ + public static double volumeSphere(double radius) { + return (4 * Math.PI * radius * radius * radius) / 3; + } + + /** + * Calculate volume of a cylinder + * + * @param radius radius of the given cylinder's floor + * @param height height of the given cylinder + * @return volume of given cylinder + */ + public static double volumeCylinder(double radius, double height) { + return Math.PI * radius * radius * height; + } + + /** + * Calculate the volume of a hemisphere. + * + * @param radius radius of given hemisphere + * @return volume of given hemisphere + */ + public static double volumeHemisphere(double radius) { + return (2 * Math.PI * radius * radius * radius) / 3; + } + + /** + * Calculate the volume of a cone. + * + * @param radius radius of given cone + * @param height of given cone + * @return volume of given cone + */ + public static double volumeCone(double radius, double height) { + return (Math.PI * radius * radius * height) / 3; + } + + /** + * Calculate the volume of a prism. + * + * @param baseArea area of the given prism's base + * @param height of given prism + * @return volume of given prism + */ + public static double volumePrism(double baseArea, double height) { + return baseArea * height; + } + + /** + * Calculate the volume of a pyramid. + * + * @param baseArea of the given pyramid's base + * @param height of given pyramid + * @return volume of given pyramid + */ + public static double volumePyramid(double baseArea, double height) { + return (baseArea * height) / 3; + } + + /** + * Calculate the volume of a frustum of a cone. + * + * @param r1 radius of the top of the frustum + * @param r2 radius of the bottom of the frustum + * @param height height of the frustum + * @return volume of the frustum + */ + public static double volumeFrustumOfCone(double r1, double r2, double height) { + return (Math.PI * height / 3) * (r1 * r1 + r2 * r2 + r1 * r2); + } +} diff --git a/src/main/java/com/thealgorithms/matrix/InverseOfMatrix.java b/src/main/java/com/thealgorithms/matrix/InverseOfMatrix.java new file mode 100644 index 000000000000..13e795a91297 --- /dev/null +++ b/src/main/java/com/thealgorithms/matrix/InverseOfMatrix.java @@ -0,0 +1,103 @@ +package com.thealgorithms.matrix; + +/** + * This class provides methods to compute the inverse of a square matrix + * using Gaussian elimination. For more details, refer to: + * https://en.wikipedia.org/wiki/Invertible_matrix + */ +public final class InverseOfMatrix { + private InverseOfMatrix() { + } + + public static double[][] invert(double[][] a) { + int n = a.length; + double[][] x = new double[n][n]; + double[][] b = new double[n][n]; + int[] index = new int[n]; + + // Initialize the identity matrix + for (int i = 0; i < n; ++i) { + b[i][i] = 1; + } + + // Perform Gaussian elimination + gaussian(a, index); + + // Update matrix b with the ratios stored during elimination + for (int i = 0; i < n - 1; ++i) { + for (int j = i + 1; j < n; ++j) { + for (int k = 0; k < n; ++k) { + b[index[j]][k] -= a[index[j]][i] * b[index[i]][k]; + } + } + } + + // Perform backward substitution to find the inverse + for (int i = 0; i < n; ++i) { + x[n - 1][i] = b[index[n - 1]][i] / a[index[n - 1]][n - 1]; + for (int j = n - 2; j >= 0; --j) { + x[j][i] = b[index[j]][i]; + for (int k = j + 1; k < n; ++k) { + x[j][i] -= a[index[j]][k] * x[k][i]; + } + x[j][i] /= a[index[j]][j]; + } + } + return x; + } + /** + * Method to carry out the partial-pivoting Gaussian + * elimination. Here index[] stores pivoting order. + **/ + private static void gaussian(double[][] a, int[] index) { + int n = index.length; + double[] c = new double[n]; + + // Initialize the index array + for (int i = 0; i < n; ++i) { + index[i] = i; + } + + // Find the rescaling factors for each row + for (int i = 0; i < n; ++i) { + double c1 = 0; + for (int j = 0; j < n; ++j) { + double c0 = Math.abs(a[i][j]); + if (c0 > c1) { + c1 = c0; + } + } + c[i] = c1; + } + + // Perform pivoting + for (int j = 0; j < n - 1; ++j) { + double pi1 = 0; + int k = j; + for (int i = j; i < n; ++i) { + double pi0 = Math.abs(a[index[i]][j]) / c[index[i]]; + if (pi0 > pi1) { + pi1 = pi0; + k = i; + } + } + + // Swap rows + int temp = index[j]; + index[j] = index[k]; + index[k] = temp; + + for (int i = j + 1; i < n; ++i) { + double pj = a[index[i]][j] / a[index[j]][j]; + + // Record pivoting ratios below the diagonal + a[index[i]][j] = pj; + + // Modify other elements accordingly + for (int l = j + 1; l < n; ++l) { + a[index[i]][l] -= pj * a[index[j]][l]; + } + } + } + } +} diff --git a/src/main/java/com/thealgorithms/matrix/MatrixRank.java b/src/main/java/com/thealgorithms/matrix/MatrixRank.java new file mode 100644 index 000000000000..6692b6c37c60 --- /dev/null +++ b/src/main/java/com/thealgorithms/matrix/MatrixRank.java @@ -0,0 +1,125 @@ +package com.thealgorithms.matrix; + +import static com.thealgorithms.matrix.utils.MatrixUtil.validateInputMatrix; + +/** + * This class provides a method to compute the rank of a matrix. + * In linear algebra, the rank of a matrix is the maximum number of linearly independent rows or columns in the matrix. + * For example, consider the following 3x3 matrix: + * 1 2 3 + * 2 4 6 + * 3 6 9 + * Despite having 3 rows and 3 columns, this matrix only has a rank of 1 because all rows (and columns) are multiples of each other. + * It's a fundamental concept that gives key insights into the structure of the matrix. + * It's important to note that the rank is not only defined for square matrices but for any m x n matrix. + * + * @author Anup Omkar + */ +public final class MatrixRank { + + private MatrixRank() { + } + + private static final double EPSILON = 1e-10; + + /** + * @brief Computes the rank of the input matrix + * + * @param matrix The input matrix + * @return The rank of the input matrix + */ + public static int computeRank(double[][] matrix) { + validateInputMatrix(matrix); + + int numRows = matrix.length; + int numColumns = matrix[0].length; + int rank = 0; + + boolean[] rowMarked = new boolean[numRows]; + + double[][] matrixCopy = deepCopy(matrix); + + for (int colIndex = 0; colIndex < numColumns; ++colIndex) { + int pivotRow = findPivotRow(matrixCopy, rowMarked, colIndex); + if (pivotRow != numRows) { + ++rank; + rowMarked[pivotRow] = true; + normalizePivotRow(matrixCopy, pivotRow, colIndex); + eliminateRows(matrixCopy, pivotRow, colIndex); + } + } + return rank; + } + + private static boolean isZero(double value) { + return Math.abs(value) < EPSILON; + } + + private static double[][] deepCopy(double[][] matrix) { + int numRows = matrix.length; + int numColumns = matrix[0].length; + double[][] matrixCopy = new double[numRows][numColumns]; + for (int rowIndex = 0; rowIndex < numRows; ++rowIndex) { + System.arraycopy(matrix[rowIndex], 0, matrixCopy[rowIndex], 0, numColumns); + } + return matrixCopy; + } + + /** + * @brief The pivot row is the row in the matrix that is used to eliminate other rows and reduce the matrix to its row echelon form. + * The pivot row is selected as the first row (from top to bottom) where the value in the current column (the pivot column) is not zero. + * This row is then used to "eliminate" other rows, by subtracting multiples of the pivot row from them, so that all other entries in the pivot column become zero. + * This process is repeated for each column, each time selecting a new pivot row, until the matrix is in row echelon form. + * The number of pivot rows (rows with a leading entry, or pivot) then gives the rank of the matrix. + * + * @param matrix The input matrix + * @param rowMarked An array indicating which rows have been marked + * @param colIndex The column index + * @return The pivot row index, or the number of rows if no suitable pivot row was found + */ + private static int findPivotRow(double[][] matrix, boolean[] rowMarked, int colIndex) { + int numRows = matrix.length; + for (int pivotRow = 0; pivotRow < numRows; ++pivotRow) { + if (!rowMarked[pivotRow] && !isZero(matrix[pivotRow][colIndex])) { + return pivotRow; + } + } + return numRows; + } + + /** + * @brief This method divides all values in the pivot row by the value in the given column. + * This ensures that the pivot value itself will be 1, which simplifies further calculations. + * + * @param matrix The input matrix + * @param pivotRow The pivot row index + * @param colIndex The column index + */ + private static void normalizePivotRow(double[][] matrix, int pivotRow, int colIndex) { + int numColumns = matrix[0].length; + for (int nextCol = colIndex + 1; nextCol < numColumns; ++nextCol) { + matrix[pivotRow][nextCol] /= matrix[pivotRow][colIndex]; + } + } + + /** + * @brief This method subtracts multiples of the pivot row from all other rows, + * so that all values in the given column of other rows will be zero. + * This is a key step in reducing the matrix to row echelon form. + * + * @param matrix The input matrix + * @param pivotRow The pivot row index + * @param colIndex The column index + */ + private static void eliminateRows(double[][] matrix, int pivotRow, int colIndex) { + int numRows = matrix.length; + int numColumns = matrix[0].length; + for (int otherRow = 0; otherRow < numRows; ++otherRow) { + if (otherRow != pivotRow && !isZero(matrix[otherRow][colIndex])) { + for (int col2 = colIndex + 1; col2 < numColumns; ++col2) { + matrix[otherRow][col2] -= matrix[pivotRow][col2] * matrix[otherRow][colIndex]; + } + } + } + } +} diff --git a/src/main/java/com/thealgorithms/matrix/MatrixTranspose.java b/src/main/java/com/thealgorithms/matrix/MatrixTranspose.java new file mode 100644 index 000000000000..f91ebc10b8a9 --- /dev/null +++ b/src/main/java/com/thealgorithms/matrix/MatrixTranspose.java @@ -0,0 +1,46 @@ +package com.thealgorithms.matrix; + +/** + * + * + * <h1>Find the Transpose of Matrix!</h1> + * + * Simply take input from the user and print the matrix before the transpose and + * after the transpose. + * + * <p> + * <b>Note:</b> Giving proper comments in your program makes it more user + * friendly and it is assumed as a high quality code. + * + * @author Rajat-Jain29 + * @version 11.0.9 + * @since 2014-03-31 + */ +public final class MatrixTranspose { + private MatrixTranspose() { + } + + /** + * Calculate the transpose of the given matrix. + * + * @param matrix The matrix to be transposed + * @throws IllegalArgumentException if the matrix is empty + * @throws NullPointerException if the matrix is null + * @return The transposed matrix + */ + public static int[][] transpose(int[][] matrix) { + if (matrix == null || matrix.length == 0) { + throw new IllegalArgumentException("Matrix is empty"); + } + + int rows = matrix.length; + int cols = matrix[0].length; + int[][] transposedMatrix = new int[cols][rows]; + for (int i = 0; i < cols; i++) { + for (int j = 0; j < rows; j++) { + transposedMatrix[i][j] = matrix[j][i]; + } + } + return transposedMatrix; + } +} diff --git a/src/main/java/com/thealgorithms/matrix/MedianOfMatrix.java b/src/main/java/com/thealgorithms/matrix/MedianOfMatrix.java new file mode 100644 index 000000000000..c710c60a2d2a --- /dev/null +++ b/src/main/java/com/thealgorithms/matrix/MedianOfMatrix.java @@ -0,0 +1,32 @@ +package com.thealgorithms.matrix; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Median of Matrix (https://medium.com/@vaibhav.yadav8101/median-in-a-row-wise-sorted-matrix-901737f3e116) + * Author: Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi) + */ + +public final class MedianOfMatrix { + private MedianOfMatrix() { + } + + public static int median(Iterable<List<Integer>> matrix) { + // Flatten the matrix into a 1D list + List<Integer> linear = new ArrayList<>(); + for (List<Integer> row : matrix) { + linear.addAll(row); + } + + // Sort the 1D list + Collections.sort(linear); + + // Calculate the middle index + int mid = (0 + linear.size() - 1) / 2; + + // Return the median + return linear.get(mid); + } +} diff --git a/src/main/java/com/thealgorithms/matrix/MirrorOfMatrix.java b/src/main/java/com/thealgorithms/matrix/MirrorOfMatrix.java new file mode 100644 index 000000000000..3a3055f38732 --- /dev/null +++ b/src/main/java/com/thealgorithms/matrix/MirrorOfMatrix.java @@ -0,0 +1,36 @@ +package com.thealgorithms.matrix; + +// Problem Statement + +import com.thealgorithms.matrix.utils.MatrixUtil; + +/* +We have given an array of m x n (where m is the number of rows and n is the number of columns). +Print the new matrix in such a way that the new matrix is the mirror image of the original matrix. + +The Original matrix is: | The Mirror matrix is: +1 2 3 | 3 2 1 +4 5 6 | 6 5 4 +7 8 9 | 9 8 7 + +@author - Aman (https://github.com/Aman28801) +*/ + +public final class MirrorOfMatrix { + private MirrorOfMatrix() { + } + + public static double[][] mirrorMatrix(final double[][] originalMatrix) { + MatrixUtil.validateInputMatrix(originalMatrix); + + int numRows = originalMatrix.length; + int numCols = originalMatrix[0].length; + + double[][] mirroredMatrix = new double[numRows][numCols]; + + for (int i = 0; i < numRows; i++) { + mirroredMatrix[i] = MatrixUtil.reverseRow(originalMatrix[i]); + } + return mirroredMatrix; + } +} diff --git a/src/main/java/com/thealgorithms/matrix/PrintAMatrixInSpiralOrder.java b/src/main/java/com/thealgorithms/matrix/PrintAMatrixInSpiralOrder.java new file mode 100644 index 000000000000..2757da1f9023 --- /dev/null +++ b/src/main/java/com/thealgorithms/matrix/PrintAMatrixInSpiralOrder.java @@ -0,0 +1,53 @@ +package com.thealgorithms.matrix; + +import java.util.ArrayList; +import java.util.List; + +public class PrintAMatrixInSpiralOrder { + /** + * Search a key in row and column wise sorted matrix + * + * @param matrix matrix to be searched + * @param row number of rows matrix has + * @param col number of columns matrix has + * @author Sadiul Hakim : https://github.com/sadiul-hakim + */ + public List<Integer> print(int[][] matrix, int row, int col) { + + // r traverses matrix row wise from first + int r = 0; + // c traverses matrix column wise from first + int c = 0; + int i; + List<Integer> result = new ArrayList<>(); + while (r < row && c < col) { + // print first row of matrix + for (i = c; i < col; i++) { + result.add(matrix[r][i]); + } + // increase r by one because first row printed + r++; + // print last column + for (i = r; i < row; i++) { + result.add(matrix[i][col - 1]); + } + // decrease col by one because last column has been printed + col--; + // print rows from last except printed elements + if (r < row) { + for (i = col - 1; i >= c; i--) { + result.add(matrix[row - 1][i]); + } + row--; + } + // print columns from first except printed elements + if (c < col) { + for (i = row - 1; i >= r; i--) { + result.add(matrix[i][c]); + } + c++; + } + } + return result; + } +} diff --git a/src/main/java/com/thealgorithms/matrix/RotateMatrixBy90Degrees.java b/src/main/java/com/thealgorithms/matrix/RotateMatrixBy90Degrees.java new file mode 100644 index 000000000000..9a7f255282ac --- /dev/null +++ b/src/main/java/com/thealgorithms/matrix/RotateMatrixBy90Degrees.java @@ -0,0 +1,73 @@ +package com.thealgorithms.matrix; + +import java.util.Scanner; +/** + * Given a matrix of size n x n We have to rotate this matrix by 90 Degree Here + * is the algorithm for this problem . + */ +final class RotateMatrixBy90Degrees { + private RotateMatrixBy90Degrees() { + } + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + int t = sc.nextInt(); + + while (t-- > 0) { + int n = sc.nextInt(); + int[][] arr = new int[n][n]; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + arr[i][j] = sc.nextInt(); + } + } + + Rotate.rotate(arr); + printMatrix(arr); + } + sc.close(); + } + + static void printMatrix(int[][] arr) { + for (int i = 0; i < arr.length; i++) { + for (int j = 0; j < arr[0].length; j++) { + System.out.print(arr[i][j] + " "); + } + System.out.println(""); + } + } +} + +/** + * Class containing the algo to roate matrix by 90 degree + */ +final class Rotate { + private Rotate() { + } + + static void rotate(int[][] a) { + int n = a.length; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (i > j) { + int temp = a[i][j]; + a[i][j] = a[j][i]; + a[j][i] = temp; + } + } + } + int i = 0; + int k = n - 1; + while (i < k) { + for (int j = 0; j < n; j++) { + int temp = a[i][j]; + a[i][j] = a[k][j]; + a[k][j] = temp; + } + + i++; + k--; + } + } +} diff --git a/src/main/java/com/thealgorithms/matrix/SolveSystem.java b/src/main/java/com/thealgorithms/matrix/SolveSystem.java new file mode 100644 index 000000000000..9e683bc4dc5c --- /dev/null +++ b/src/main/java/com/thealgorithms/matrix/SolveSystem.java @@ -0,0 +1,71 @@ +package com.thealgorithms.matrix; + +/** + * This class implements an algorithm for solving a system of equations of the form Ax=b using gaussian elimination and back substitution. + * + * @link <a href="https://en.wikipedia.org/wiki/Gaussian_elimination">Gaussian Elimination Wiki</a> + * @see InverseOfMatrix finds the full of inverse of a matrice, but is not required to solve a system. + */ +public final class SolveSystem { + private SolveSystem() { + } + + /** + * Problem: Given a matrix A and vector b, solve the linear system Ax = b for the vector x.\ + * <p> + * <b>This OVERWRITES the input matrix to save on memory</b> + * + * @param matrix - a square matrix of doubles + * @param constants - an array of constant + * @return solutions + */ + public static double[] solveSystem(double[][] matrix, double[] constants) { + final double tol = 0.00000001; // tolerance for round off + for (int k = 0; k < matrix.length - 1; k++) { + // find the largest value in column (to avoid zero pivots) + double maxVal = Math.abs(matrix[k][k]); + int maxIdx = k; + for (int j = k + 1; j < matrix.length; j++) { + if (Math.abs(matrix[j][k]) > maxVal) { + maxVal = matrix[j][k]; + maxIdx = j; + } + } + if (Math.abs(maxVal) < tol) { + // hope the matrix works out + continue; + } + // swap rows + double[] temp = matrix[k]; + matrix[k] = matrix[maxIdx]; + matrix[maxIdx] = temp; + double tempConst = constants[k]; + constants[k] = constants[maxIdx]; + constants[maxIdx] = tempConst; + for (int i = k + 1; i < matrix.length; i++) { + // compute multipliers and save them in the column + matrix[i][k] /= matrix[k][k]; + for (int j = k + 1; j < matrix.length; j++) { + matrix[i][j] -= matrix[i][k] * matrix[k][j]; + } + constants[i] -= matrix[i][k] * constants[k]; + } + } + // back substitution + double[] x = new double[constants.length]; + System.arraycopy(constants, 0, x, 0, constants.length); + for (int i = matrix.length - 1; i >= 0; i--) { + double sum = 0; + for (int j = i + 1; j < matrix.length; j++) { + sum += matrix[i][j] * x[j]; + } + x[i] = constants[i] - sum; + if (Math.abs(matrix[i][i]) > tol) { + x[i] /= matrix[i][i]; + } else { + throw new IllegalArgumentException("Matrix was found to be singular"); + } + } + return x; + } +} diff --git a/src/main/java/com/thealgorithms/matrix/matrixexponentiation/Fibonacci.java b/src/main/java/com/thealgorithms/matrix/matrixexponentiation/Fibonacci.java new file mode 100644 index 000000000000..85852713b9ba --- /dev/null +++ b/src/main/java/com/thealgorithms/matrix/matrixexponentiation/Fibonacci.java @@ -0,0 +1,42 @@ +package com.thealgorithms.matrix.matrixexponentiation; + +import com.thealgorithms.matrix.utils.MatrixUtil; +import java.math.BigDecimal; + +/** + * @author Anirudh Buvanesh (https://github.com/anirudhb11) For more information + * see https://www.geeksforgeeks.org/matrix-exponentiation/ + * + */ +public final class Fibonacci { + private Fibonacci() { + } + + // Exponentiation matrix for Fibonacci sequence + private static final BigDecimal ONE = BigDecimal.valueOf(1); + private static final BigDecimal ZERO = BigDecimal.valueOf(0); + + private static final BigDecimal[][] FIB_MATRIX = {{ONE, ONE}, {ONE, ZERO}}; + private static final BigDecimal[][] IDENTITY_MATRIX = {{ONE, ZERO}, {ZERO, ONE}}; + + /** + * Calculates the fibonacci number using matrix exponentiaition technique + * + * @param n The input n for which we have to determine the fibonacci number + * Outputs the nth * fibonacci number + * @return a 2 X 1 array as { {F_n+1}, {F_n} } + */ + public static BigDecimal[][] fib(int n) { + if (n == 0) { + return IDENTITY_MATRIX; + } else { + BigDecimal[][] cachedResult = fib(n / 2); + BigDecimal[][] matrixExpResult = MatrixUtil.multiply(cachedResult, cachedResult).get(); + if (n % 2 == 0) { + return matrixExpResult; + } else { + return MatrixUtil.multiply(FIB_MATRIX, matrixExpResult).get(); + } + } + } +} diff --git a/src/main/java/com/thealgorithms/matrix/utils/MatrixUtil.java b/src/main/java/com/thealgorithms/matrix/utils/MatrixUtil.java new file mode 100644 index 000000000000..5ff9e37f6b9a --- /dev/null +++ b/src/main/java/com/thealgorithms/matrix/utils/MatrixUtil.java @@ -0,0 +1,133 @@ +package com.thealgorithms.matrix.utils; + +import java.math.BigDecimal; +import java.util.Optional; +import java.util.function.BiFunction; +import java.util.stream.IntStream; + +/** + * @author: caos321 + * @date: 31 October 2021 (Sunday) + */ +public final class MatrixUtil { + + private MatrixUtil() { + } + + private static boolean isValid(final BigDecimal[][] matrix) { + return matrix != null && matrix.length > 0 && matrix[0].length > 0; + } + + private static boolean hasEqualSizes(final BigDecimal[][] matrix1, final BigDecimal[][] matrix2) { + return isValid(matrix1) && isValid(matrix2) && matrix1.length == matrix2.length && matrix1[0].length == matrix2[0].length; + } + + private static boolean canMultiply(final BigDecimal[][] matrix1, final BigDecimal[][] matrix2) { + return isValid(matrix1) && isValid(matrix2) && matrix1[0].length == matrix2.length; + } + + public static void validateInputMatrix(double[][] matrix) { + if (matrix == null) { + throw new IllegalArgumentException("The input matrix cannot be null"); + } + if (matrix.length == 0) { + throw new IllegalArgumentException("The input matrix cannot be empty"); + } + if (!hasValidRows(matrix)) { + throw new IllegalArgumentException("The input matrix cannot have null or empty rows"); + } + if (isJaggedMatrix(matrix)) { + throw new IllegalArgumentException("The input matrix cannot be jagged"); + } + } + + private static boolean hasValidRows(double[][] matrix) { + for (double[] row : matrix) { + if (row == null || row.length == 0) { + return false; + } + } + return true; + } + + /** + * @brief Checks if the input matrix is a jagged matrix. + * Jagged matrix is a matrix where the number of columns in each row is not the same. + * + * @param matrix The input matrix + * @return True if the input matrix is a jagged matrix, false otherwise + */ + private static boolean isJaggedMatrix(double[][] matrix) { + int numColumns = matrix[0].length; + for (double[] row : matrix) { + if (row.length != numColumns) { + return true; + } + } + return false; + } + + private static Optional<BigDecimal[][]> operate(final BigDecimal[][] matrix1, final BigDecimal[][] matrix2, final BiFunction<BigDecimal, BigDecimal, BigDecimal> operation) { + if (!hasEqualSizes(matrix1, matrix2)) { + return Optional.empty(); + } + + final int rowSize = matrix1.length; + final int columnSize = matrix1[0].length; + + final BigDecimal[][] result = new BigDecimal[rowSize][columnSize]; + + IntStream.range(0, rowSize).forEach(rowIndex -> IntStream.range(0, columnSize).forEach(columnIndex -> { + final BigDecimal value1 = matrix1[rowIndex][columnIndex]; + final BigDecimal value2 = matrix2[rowIndex][columnIndex]; + + result[rowIndex][columnIndex] = operation.apply(value1, value2); + })); + + return Optional.of(result); + } + + public static Optional<BigDecimal[][]> add(final BigDecimal[][] matrix1, final BigDecimal[][] matrix2) { + return operate(matrix1, matrix2, BigDecimal::add); + } + + public static Optional<BigDecimal[][]> subtract(final BigDecimal[][] matrix1, final BigDecimal[][] matrix2) { + return operate(matrix1, matrix2, BigDecimal::subtract); + } + + public static Optional<BigDecimal[][]> multiply(final BigDecimal[][] matrix1, final BigDecimal[][] matrix2) { + if (!canMultiply(matrix1, matrix2)) { + return Optional.empty(); + } + + final int size = matrix1[0].length; + + final int matrix1RowSize = matrix1.length; + final int matrix2ColumnSize = matrix2[0].length; + + final BigDecimal[][] result = new BigDecimal[matrix1RowSize][matrix2ColumnSize]; + + IntStream.range(0, matrix1RowSize) + .forEach(rowIndex + -> IntStream.range(0, matrix2ColumnSize) + .forEach(columnIndex + -> result[rowIndex][columnIndex] = IntStream.range(0, size) + .mapToObj(index -> { + final BigDecimal value1 = matrix1[rowIndex][index]; + final BigDecimal value2 = matrix2[index][columnIndex]; + + return value1.multiply(value2); + }) + .reduce(BigDecimal.ZERO, BigDecimal::add))); + + return Optional.of(result); + } + + public static double[] reverseRow(final double[] inRow) { + double[] res = new double[inRow.length]; + for (int i = 0; i < inRow.length; ++i) { + res[i] = inRow[inRow.length - 1 - i]; + } + return res; + } +} diff --git a/src/main/java/com/thealgorithms/misc/ColorContrastRatio.java b/src/main/java/com/thealgorithms/misc/ColorContrastRatio.java new file mode 100644 index 000000000000..a7e3f651cc85 --- /dev/null +++ b/src/main/java/com/thealgorithms/misc/ColorContrastRatio.java @@ -0,0 +1,64 @@ +package com.thealgorithms.misc; + +import java.awt.Color; + +/** + * @brief A Java implementation of the official W3 documented procedure to + * calculate contrast ratio between colors on the web. This is used to calculate + * the readability of a foreground color on top of a background color. + * @since 2020-10-15 + * @see <a href="https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-procedure">Color Contrast Ratio</a> + * @author [Seth Falco](https://github.com/SethFalco) + */ +public class ColorContrastRatio { + + /** + * @brief Calculates the contrast ratio between two given colors. + * @param a Any color, used to get the red, green, and blue values. + * @param b Another color, which will be compared against the first color. + * @return The contrast ratio between the two colors. + */ + public double getContrastRatio(Color a, Color b) { + final double aColorLuminance = getRelativeLuminance(a); + final double bColorLuminance = getRelativeLuminance(b); + + if (aColorLuminance > bColorLuminance) { + return (aColorLuminance + 0.05) / (bColorLuminance + 0.05); + } + + return (bColorLuminance + 0.05) / (aColorLuminance + 0.05); + } + + /** + * @brief Calculates the relative luminance of a given color. + * @param color Any color, used to get the red, green, and blue values. + * @return The relative luminance of the color. + * @see <a href="https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef">More info on relative luminance.</a> + */ + public double getRelativeLuminance(Color color) { + final double red = getColor(color.getRed()); + final double green = getColor(color.getGreen()); + final double blue = getColor(color.getBlue()); + + return 0.2126 * red + 0.7152 * green + 0.0722 * blue; + } + + /** + * @brief Calculates the final value for a color to be used in the relative luminance formula as described in step 1. + * @param color8Bit 8-bit representation of a color component value. + * @return Value for the provided color component to be used in the relative luminance formula. + */ + public double getColor(int color8Bit) { + final double sRgb = getColorSRgb(color8Bit); + return (sRgb <= 0.03928) ? sRgb / 12.92 : Math.pow((sRgb + 0.055) / 1.055, 2.4); + } + + /** + * @brief Calculates the Color sRGB value as denoted in step 1 of the procedure document. + * @param color8Bit 8-bit representation of a color component value. + * @return A percentile value of the color component. + */ + private double getColorSRgb(double color8Bit) { + return color8Bit / 255.0; + } +} diff --git a/src/main/java/com/thealgorithms/misc/MapReduce.java b/src/main/java/com/thealgorithms/misc/MapReduce.java new file mode 100644 index 000000000000..c076957344f9 --- /dev/null +++ b/src/main/java/com/thealgorithms/misc/MapReduce.java @@ -0,0 +1,41 @@ +package com.thealgorithms.misc; + +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/* +* MapReduce is a programming model for processing and generating large data sets with a parallel, +distributed algorithm on a cluster. +* It has two main steps: the Map step, where the data is divided into smaller chunks and processed in parallel, +and the Reduce step, where the results from the Map step are combined to produce the final output. +* Wikipedia link : https://en.wikipedia.org/wiki/MapReduce +*/ + +public final class MapReduce { + private MapReduce() { + } + /* + *Counting all the words frequency within a sentence. + */ + public static String mapreduce(String sentence) { + List<String> wordList = Arrays.stream(sentence.split(" ")).toList(); + + // Map step + Map<String, Long> wordCounts = wordList.stream().collect(Collectors.groupingBy(Function.identity(), LinkedHashMap::new, Collectors.counting())); + + // Reduce step + StringBuilder result = new StringBuilder(); + wordCounts.forEach((word, count) -> result.append(word).append(": ").append(count).append(",")); + + // Removing the last ',' if it exists + if (!result.isEmpty()) { + result.setLength(result.length() - 1); + } + + return result.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/misc/MedianOfRunningArray.java b/src/main/java/com/thealgorithms/misc/MedianOfRunningArray.java new file mode 100644 index 000000000000..62013ee31183 --- /dev/null +++ b/src/main/java/com/thealgorithms/misc/MedianOfRunningArray.java @@ -0,0 +1,53 @@ +package com.thealgorithms.misc; + +import java.util.Collections; +import java.util.PriorityQueue; + +/** + * @author shrutisheoran + */ +public abstract class MedianOfRunningArray<T extends Number & Comparable<T>> { + + private PriorityQueue<T> maxHeap; + private PriorityQueue<T> minHeap; + + // Constructor + public MedianOfRunningArray() { + this.maxHeap = new PriorityQueue<>(Collections.reverseOrder()); // Max Heap + this.minHeap = new PriorityQueue<>(); // Min Heap + } + + /* + Inserting lower half of array to max Heap + and upper half to min heap + */ + public void insert(final T e) { + if (!minHeap.isEmpty() && e.compareTo(minHeap.peek()) < 0) { + maxHeap.offer(e); + if (maxHeap.size() > minHeap.size() + 1) { + minHeap.offer(maxHeap.poll()); + } + } else { + minHeap.offer(e); + if (minHeap.size() > maxHeap.size() + 1) { + maxHeap.offer(minHeap.poll()); + } + } + } + + /* + Returns median at any given point + */ + public T median() { + if (maxHeap.isEmpty() && minHeap.isEmpty()) { + throw new IllegalArgumentException("Enter at least 1 element, Median of empty list is not defined!"); + } else if (maxHeap.size() == minHeap.size()) { + T maxHeapTop = maxHeap.peek(); + T minHeapTop = minHeap.peek(); + return calculateAverage(maxHeapTop, minHeapTop); + } + return maxHeap.size() > minHeap.size() ? maxHeap.peek() : minHeap.peek(); + } + + public abstract T calculateAverage(T a, T b); +} diff --git a/src/main/java/com/thealgorithms/misc/MedianOfRunningArrayByte.java b/src/main/java/com/thealgorithms/misc/MedianOfRunningArrayByte.java new file mode 100644 index 000000000000..668f651e7251 --- /dev/null +++ b/src/main/java/com/thealgorithms/misc/MedianOfRunningArrayByte.java @@ -0,0 +1,8 @@ +package com.thealgorithms.misc; + +public final class MedianOfRunningArrayByte extends MedianOfRunningArray<Byte> { + @Override + public Byte calculateAverage(final Byte a, final Byte b) { + return (byte) ((a + b) / 2); + } +} diff --git a/src/main/java/com/thealgorithms/misc/MedianOfRunningArrayDouble.java b/src/main/java/com/thealgorithms/misc/MedianOfRunningArrayDouble.java new file mode 100644 index 000000000000..9d743de51643 --- /dev/null +++ b/src/main/java/com/thealgorithms/misc/MedianOfRunningArrayDouble.java @@ -0,0 +1,8 @@ +package com.thealgorithms.misc; + +public final class MedianOfRunningArrayDouble extends MedianOfRunningArray<Double> { + @Override + public Double calculateAverage(final Double a, final Double b) { + return (a + b) / 2.0d; + } +} diff --git a/src/main/java/com/thealgorithms/misc/MedianOfRunningArrayFloat.java b/src/main/java/com/thealgorithms/misc/MedianOfRunningArrayFloat.java new file mode 100644 index 000000000000..a667abf6121b --- /dev/null +++ b/src/main/java/com/thealgorithms/misc/MedianOfRunningArrayFloat.java @@ -0,0 +1,8 @@ +package com.thealgorithms.misc; + +public final class MedianOfRunningArrayFloat extends MedianOfRunningArray<Float> { + @Override + public Float calculateAverage(final Float a, final Float b) { + return (a + b) / 2.0f; + } +} diff --git a/src/main/java/com/thealgorithms/misc/MedianOfRunningArrayInteger.java b/src/main/java/com/thealgorithms/misc/MedianOfRunningArrayInteger.java new file mode 100644 index 000000000000..7154ba073136 --- /dev/null +++ b/src/main/java/com/thealgorithms/misc/MedianOfRunningArrayInteger.java @@ -0,0 +1,8 @@ +package com.thealgorithms.misc; + +public final class MedianOfRunningArrayInteger extends MedianOfRunningArray<Integer> { + @Override + public Integer calculateAverage(final Integer a, final Integer b) { + return (a + b) / 2; + } +} diff --git a/src/main/java/com/thealgorithms/misc/MedianOfRunningArrayLong.java b/src/main/java/com/thealgorithms/misc/MedianOfRunningArrayLong.java new file mode 100644 index 000000000000..1f138c6313fb --- /dev/null +++ b/src/main/java/com/thealgorithms/misc/MedianOfRunningArrayLong.java @@ -0,0 +1,8 @@ +package com.thealgorithms.misc; + +public final class MedianOfRunningArrayLong extends MedianOfRunningArray<Long> { + @Override + public Long calculateAverage(final Long a, final Long b) { + return (a + b) / 2L; + } +} diff --git a/src/main/java/com/thealgorithms/misc/PalindromePrime.java b/src/main/java/com/thealgorithms/misc/PalindromePrime.java new file mode 100644 index 000000000000..164e957a9d12 --- /dev/null +++ b/src/main/java/com/thealgorithms/misc/PalindromePrime.java @@ -0,0 +1,57 @@ +package com.thealgorithms.misc; + +import java.util.ArrayList; +import java.util.List; + +public final class PalindromePrime { + private PalindromePrime() { + } + + public static boolean prime(int num) { + if (num < 2) { + return false; // Handle edge case for numbers < 2 + } + if (num == 2) { + return true; // 2 is prime + } + if (num % 2 == 0) { + return false; // Even numbers > 2 are not prime + } + + for (int divisor = 3; divisor <= Math.sqrt(num); divisor += 2) { + if (num % divisor == 0) { + return false; + } + } + return true; + } + + public static int reverse(int n) { + int reverse = 0; + while (n != 0) { + reverse = reverse * 10 + (n % 10); + n /= 10; + } + return reverse; + } + + public static List<Integer> generatePalindromePrimes(int n) { + List<Integer> palindromicPrimes = new ArrayList<>(); + if (n <= 0) { + return palindromicPrimes; // Handle case for 0 or negative input + } + + palindromicPrimes.add(2); // 2 is the first palindromic prime + int count = 1; + int num = 3; + + while (count < n) { + if (num == reverse(num) && prime(num)) { + palindromicPrimes.add(num); + count++; + } + num += 2; // Skip even numbers + } + return palindromicPrimes; + } +} diff --git a/src/main/java/com/thealgorithms/misc/PalindromeSinglyLinkedList.java b/src/main/java/com/thealgorithms/misc/PalindromeSinglyLinkedList.java new file mode 100644 index 000000000000..51dc099ba32e --- /dev/null +++ b/src/main/java/com/thealgorithms/misc/PalindromeSinglyLinkedList.java @@ -0,0 +1,75 @@ +package com.thealgorithms.misc; + +import java.util.Stack; + +/** + * A simple way of knowing if a singly linked list is palindrome is to push all + * the values into a Stack and then compare the list to popped vales from the + * Stack. + * + * See more: + * https://www.geeksforgeeks.org/function-to-check-if-a-singly-linked-list-is-palindrome/ + */ +public final class PalindromeSinglyLinkedList { + private PalindromeSinglyLinkedList() { + } + + public static boolean isPalindrome(final Iterable linkedList) { + var linkedListValues = new Stack<>(); + + for (final var x : linkedList) { + linkedListValues.push(x); + } + + for (final var x : linkedList) { + if (x != linkedListValues.pop()) { + return false; + } + } + + return true; + } + + // Optimised approach with O(n) time complexity and O(1) space complexity + + public static boolean isPalindromeOptimised(Node head) { + if (head == null || head.next == null) { + return true; + } + Node slow = head; + Node fast = head; + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + } + Node midNode = slow; + + Node prevNode = null; + Node currNode = midNode; + Node nextNode; + while (currNode != null) { + nextNode = currNode.next; + currNode.next = prevNode; + prevNode = currNode; + currNode = nextNode; + } + Node left = head; + Node right = prevNode; + while (left != null && right != null) { + if (left.val != right.val) { + return false; + } + right = right.next; + left = left.next; + } + return true; + } + static class Node { + int val; + Node next; + Node(int val) { + this.val = val; + this.next = null; + } + } +} diff --git a/src/main/java/com/thealgorithms/misc/RangeInSortedArray.java b/src/main/java/com/thealgorithms/misc/RangeInSortedArray.java new file mode 100644 index 000000000000..6d3caa1814b6 --- /dev/null +++ b/src/main/java/com/thealgorithms/misc/RangeInSortedArray.java @@ -0,0 +1,90 @@ +package com.thealgorithms.misc; + +public final class RangeInSortedArray { + private RangeInSortedArray() { + } + + // Get the 1st and last occurrence index of a number 'key' in a non-decreasing array 'nums' + // Gives [-1, -1] in case element doesn't exist in array + public static int[] sortedRange(int[] nums, int key) { + int[] range = new int[] {-1, -1}; + alteredBinSearchIter(nums, key, 0, nums.length - 1, range, true); + alteredBinSearchIter(nums, key, 0, nums.length - 1, range, false); + return range; + } + + // Recursive altered binary search which searches for leftmost as well as rightmost occurrence + // of 'key' + public static void alteredBinSearch(int[] nums, int key, int left, int right, int[] range, boolean goLeft) { + if (left > right) { + return; + } + int mid = (left + right) >>> 1; + if (nums[mid] > key) { + alteredBinSearch(nums, key, left, mid - 1, range, goLeft); + } else if (nums[mid] < key) { + alteredBinSearch(nums, key, mid + 1, right, range, goLeft); + } else { + if (goLeft) { + if (mid == 0 || nums[mid - 1] != key) { + range[0] = mid; + } else { + alteredBinSearch(nums, key, left, mid - 1, range, goLeft); + } + } else { + if (mid == nums.length - 1 || nums[mid + 1] != key) { + range[1] = mid; + } else { + alteredBinSearch(nums, key, mid + 1, right, range, goLeft); + } + } + } + } + + // Iterative altered binary search which searches for leftmost as well as rightmost occurrence + // of 'key' + public static void alteredBinSearchIter(int[] nums, int key, int left, int right, int[] range, boolean goLeft) { + while (left <= right) { + final int mid = (left + right) >>> 1; + if (nums[mid] > key) { + right = mid - 1; + } else if (nums[mid] < key) { + left = mid + 1; + } else { + if (goLeft) { + if (mid == 0 || nums[mid - 1] != key) { + range[0] = mid; + return; + } else { + right = mid - 1; + } + } else { + if (mid == nums.length - 1 || nums[mid + 1] != key) { + range[1] = mid; + return; + } else { + left = mid + 1; + } + } + } + } + } + + public static int getCountLessThan(int[] nums, int key) { + return getLessThan(nums, key, 0, nums.length - 1); + } + + public static int getLessThan(int[] nums, int key, int left, int right) { + int count = 0; + while (left <= right) { + final int mid = (left + right) >>> 1; + if (nums[mid] > key) { + right = mid - 1; + } else if (nums[mid] <= key) { + count = mid + 1; // At least mid+1 elements exist which are <= key + left = mid + 1; + } + } + return count; + } +} diff --git a/src/main/java/com/thealgorithms/misc/ShuffleArray.java b/src/main/java/com/thealgorithms/misc/ShuffleArray.java new file mode 100644 index 000000000000..65d38adfc37a --- /dev/null +++ b/src/main/java/com/thealgorithms/misc/ShuffleArray.java @@ -0,0 +1,38 @@ +package com.thealgorithms.misc; + +import java.util.Random; + +/** + * The Fisher-Yates (Knuth) Shuffle algorithm randomly permutes an array's + * elements, ensuring each permutation is equally likely. + * + * <p> + * Worst-case performance O(n) + * Best-case performance O(n) + * Average performance O(n) + * Worst-case space complexity O(1) + * + * This class provides a static method to shuffle an array in place. + * + * @author Rashi Dashore (https://github.com/rashi07dashore) + */ +public final class ShuffleArray { + // Prevent instantiation + private ShuffleArray() { + } + + /** + * This method shuffles an array using the Fisher-Yates algorithm. + * + * @param arr is the input array to be shuffled + */ + public static void shuffle(int[] arr) { + Random random = new Random(); + for (int i = arr.length - 1; i > 0; i--) { + int j = random.nextInt(i + 1); + int temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; + } + } +} diff --git a/src/main/java/com/thealgorithms/misc/Sparsity.java b/src/main/java/com/thealgorithms/misc/Sparsity.java new file mode 100644 index 000000000000..08e50a121da4 --- /dev/null +++ b/src/main/java/com/thealgorithms/misc/Sparsity.java @@ -0,0 +1,40 @@ +package com.thealgorithms.misc; + +/* + *A matrix is sparse if many of its coefficients are zero (In general if 2/3rd of matrix elements + *are 0, it is considered as sparse). The interest in sparsity arises because its exploitation can + *lead to enormous computational savings and because many large matrix problems that occur in + *practice are sparse. + * + * @author Ojasva Jain + */ + +final class Sparsity { + private Sparsity() { + } + + /* + * @param mat the input matrix + * @return Sparsity of matrix + * + * where sparsity = number of zeroes/total elements in matrix + * + */ + static double sparsity(double[][] mat) { + if (mat == null || mat.length == 0) { + throw new IllegalArgumentException("Matrix cannot be null or empty"); + } + + int zero = 0; + // Traversing the matrix to count number of zeroes + for (int i = 0; i < mat.length; i++) { + for (int j = 0; j < mat[i].length; j++) { + if (mat[i][j] == 0) { + zero++; + } + } + } + // return sparsity + return ((double) zero / (mat.length * mat[0].length)); + } +} diff --git a/src/main/java/com/thealgorithms/misc/ThreeSumProblem.java b/src/main/java/com/thealgorithms/misc/ThreeSumProblem.java new file mode 100644 index 000000000000..8ef10758ef80 --- /dev/null +++ b/src/main/java/com/thealgorithms/misc/ThreeSumProblem.java @@ -0,0 +1,88 @@ +package com.thealgorithms.misc; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +public class ThreeSumProblem { + + public List<List<Integer>> bruteForce(int[] nums, int target) { + List<List<Integer>> arr = new ArrayList<List<Integer>>(); + + for (int i = 0; i < nums.length; i++) { + for (int j = i + 1; j < nums.length; j++) { + for (int k = j + 1; k < nums.length; k++) { + if (nums[i] + nums[j] + nums[k] == target) { + List<Integer> temp = new ArrayList<>(); + temp.add(nums[i]); + temp.add(nums[j]); + temp.add(nums[k]); + Collections.sort(temp); + arr.add(temp); + } + } + } + } + arr = new ArrayList<List<Integer>>(new LinkedHashSet<List<Integer>>(arr)); + return arr; + } + + public List<List<Integer>> twoPointer(int[] nums, int target) { + Arrays.sort(nums); + List<List<Integer>> arr = new ArrayList<List<Integer>>(); + int start = 0; + int end = 0; + int i = 0; + while (i < nums.length - 1) { + start = i + 1; + end = nums.length - 1; + while (start < end) { + if (nums[start] + nums[end] + nums[i] == target) { + List<Integer> temp = new ArrayList<>(); + temp.add(nums[i]); + temp.add(nums[start]); + temp.add(nums[end]); + arr.add(temp); + start++; + end--; + } else if (nums[start] + nums[end] + nums[i] < target) { + start += 1; + } else { + end -= 1; + } + } + i++; + } + Set<List<Integer>> set = new LinkedHashSet<List<Integer>>(arr); + return new ArrayList<List<Integer>>(set); + } + + public List<List<Integer>> hashMap(int[] nums, int target) { + Arrays.sort(nums); + Set<List<Integer>> ts = new HashSet<>(); + HashMap<Integer, Integer> hm = new HashMap<>(); + + for (int i = 0; i < nums.length; i++) { + hm.put(nums[i], i); + } + + for (int i = 0; i < nums.length; i++) { + for (int j = i + 1; j < nums.length; j++) { + int t = target - nums[i] - nums[j]; + if (hm.containsKey(t) && hm.get(t) > j) { + List<Integer> temp = new ArrayList<>(); + temp.add(nums[i]); + temp.add(nums[j]); + temp.add(t); + ts.add(temp); + } + } + } + return new ArrayList<>(ts); + } +} diff --git a/src/main/java/com/thealgorithms/misc/TwoSumProblem.java b/src/main/java/com/thealgorithms/misc/TwoSumProblem.java new file mode 100644 index 000000000000..2fc4ed09a792 --- /dev/null +++ b/src/main/java/com/thealgorithms/misc/TwoSumProblem.java @@ -0,0 +1,33 @@ +package com.thealgorithms.misc; + +import java.util.HashMap; +import java.util.Optional; +import org.apache.commons.lang3.tuple.Pair; + +public final class TwoSumProblem { + private TwoSumProblem() { + } + + /** + * The function "twoSum" takes an array of integers and a target integer as input, and returns an + * array of two indices where the corresponding elements in the input array add up to the target. + * @param values An array of integers. + * @param target The target is the sum that we are trying to find using two numbers from the given array. + * @return A pair or indexes such that sum of values at these indexes equals to the target + * @author Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi) + */ + + public static Optional<Pair<Integer, Integer>> twoSum(final int[] values, final int target) { + HashMap<Integer, Integer> valueToIndex = new HashMap<>(); + for (int i = 0; i < values.length; i++) { + final var remainder = target - values[i]; + if (valueToIndex.containsKey(remainder)) { + return Optional.of(Pair.of(valueToIndex.get(remainder), i)); + } + if (!valueToIndex.containsKey(values[i])) { + valueToIndex.put(values[i], i); + } + } + return Optional.empty(); + } +} diff --git a/src/main/java/com/thealgorithms/others/ArrayLeftRotation.java b/src/main/java/com/thealgorithms/others/ArrayLeftRotation.java new file mode 100644 index 000000000000..b54cbec08f74 --- /dev/null +++ b/src/main/java/com/thealgorithms/others/ArrayLeftRotation.java @@ -0,0 +1,44 @@ +package com.thealgorithms.others; + +/** + * Provides a method to perform a left rotation on an array. + * A left rotation operation shifts each element of the array + * by a specified number of positions to the left. + * + * @author sangin-lee + */ +public final class ArrayLeftRotation { + private ArrayLeftRotation() { + } + + /** + * Performs a left rotation on the given array by the specified number of positions. + * + * @param arr the array to be rotated + * @param n the number of positions to rotate the array to the left + * @return a new array containing the elements of the input array rotated to the left + */ + public static int[] rotateLeft(int[] arr, int n) { + int size = arr.length; + + // Handle cases where array is empty or rotation count is zero + if (size == 0 || n <= 0) { + return arr.clone(); + } + + // Normalize the number of rotations + n = n % size; + if (n == 0) { + return arr.clone(); + } + + int[] rotated = new int[size]; + + // Perform rotation + for (int i = 0; i < size; i++) { + rotated[i] = arr[(i + n) % size]; + } + + return rotated; + } +} diff --git a/src/main/java/com/thealgorithms/others/ArrayRightRotation.java b/src/main/java/com/thealgorithms/others/ArrayRightRotation.java new file mode 100644 index 000000000000..125edadb6e73 --- /dev/null +++ b/src/main/java/com/thealgorithms/others/ArrayRightRotation.java @@ -0,0 +1,51 @@ +package com.thealgorithms.others; + +/** + * Provides a method to perform a right rotation on an array. + * A left rotation operation shifts each element of the array + * by a specified number of positions to the right. + * + * https://en.wikipedia.org/wiki/Right_rotation * + */ +public final class ArrayRightRotation { + private ArrayRightRotation() { + } + + /** + * Performs a right rotation on the given array by the specified number of positions. + * + * @param arr the array to be rotated + * @param k the number of positions to rotate the array to the left + * @return a new array containing the elements of the input array rotated to the left + */ + public static int[] rotateRight(int[] arr, int k) { + if (arr == null || arr.length == 0 || k < 0) { + throw new IllegalArgumentException("Invalid input"); + } + + int n = arr.length; + k = k % n; // Handle cases where k is larger than the array length + + reverseArray(arr, 0, n - 1); + reverseArray(arr, 0, k - 1); + reverseArray(arr, k, n - 1); + + return arr; + } + + /** + * Performs reversing of a array + * @param arr the array to be reversed + * @param start starting position + * @param end ending position + */ + private static void reverseArray(int[] arr, int start, int end) { + while (start < end) { + int temp = arr[start]; + arr[start] = arr[end]; + arr[end] = temp; + start++; + end--; + } + } +} diff --git a/src/main/java/com/thealgorithms/others/BFPRT.java b/src/main/java/com/thealgorithms/others/BFPRT.java new file mode 100644 index 000000000000..58c6d4e56830 --- /dev/null +++ b/src/main/java/com/thealgorithms/others/BFPRT.java @@ -0,0 +1,178 @@ +package com.thealgorithms.others; + +/** + * The BFPRT (Median of Medians) algorithm implementation. + * It provides a way to find the k-th smallest element in an unsorted array + * with an optimal worst-case time complexity of O(n). + * This algorithm is used to find the k smallest numbers in an array. + */ +public final class BFPRT { + private BFPRT() { + } + + /** + * Returns the k smallest elements from the array using the BFPRT algorithm. + * + * @param arr the input array + * @param k the number of smallest elements to return + * @return an array containing the k smallest elements, or null if k is invalid + */ + public static int[] getMinKNumsByBFPRT(int[] arr, int k) { + if (k < 1 || k > arr.length) { + return null; + } + int minKth = getMinKthByBFPRT(arr, k); + int[] res = new int[k]; + int index = 0; + for (int value : arr) { + if (value < minKth) { + res[index++] = value; + } + } + for (; index != res.length; index++) { + res[index] = minKth; + } + return res; + } + + /** + * Returns the k-th smallest element from the array using the BFPRT algorithm. + * + * @param arr the input array + * @param k the rank of the smallest element to find + * @return the k-th smallest element + */ + public static int getMinKthByBFPRT(int[] arr, int k) { + int[] copyArr = copyArray(arr); + return bfprt(copyArr, 0, copyArr.length - 1, k - 1); + } + + /** + * Creates a copy of the input array. + * + * @param arr the input array + * @return a copy of the array + */ + public static int[] copyArray(int[] arr) { + int[] copyArr = new int[arr.length]; + System.arraycopy(arr, 0, copyArr, 0, arr.length); + return copyArr; + } + + /** + * BFPRT recursive method to find the k-th smallest element. + * + * @param arr the input array + * @param begin the starting index + * @param end the ending index + * @param i the index of the desired smallest element + * @return the k-th smallest element + */ + public static int bfprt(int[] arr, int begin, int end, int i) { + if (begin == end) { + return arr[begin]; + } + int pivot = medianOfMedians(arr, begin, end); + int[] pivotRange = partition(arr, begin, end, pivot); + if (i >= pivotRange[0] && i <= pivotRange[1]) { + return arr[i]; + } else if (i < pivotRange[0]) { + return bfprt(arr, begin, pivotRange[0] - 1, i); + } else { + return bfprt(arr, pivotRange[1] + 1, end, i); + } + } + + /** + * Finds the median of medians as the pivot element. + * + * @param arr the input array + * @param begin the starting index + * @param end the ending index + * @return the median of medians + */ + public static int medianOfMedians(int[] arr, int begin, int end) { + int num = end - begin + 1; + int offset = num % 5 == 0 ? 0 : 1; + int[] mArr = new int[num / 5 + offset]; + for (int i = 0; i < mArr.length; i++) { + mArr[i] = getMedian(arr, begin + i * 5, Math.min(end, begin + i * 5 + 4)); + } + return bfprt(mArr, 0, mArr.length - 1, mArr.length / 2); + } + + /** + * Partitions the array around a pivot. + * + * @param arr the input array + * @param begin the starting index + * @param end the ending index + * @param num the pivot element + * @return the range where the pivot is located + */ + public static int[] partition(int[] arr, int begin, int end, int num) { + int small = begin - 1; + int cur = begin; + int big = end + 1; + while (cur != big) { + if (arr[cur] < num) { + swap(arr, ++small, cur++); + } else if (arr[cur] > num) { + swap(arr, --big, cur); + } else { + cur++; + } + } + return new int[] {small + 1, big - 1}; + } + + /** + * Finds the median of the elements between the specified range. + * + * @param arr the input array + * @param begin the starting index + * @param end the ending index + * @return the median of the specified range + */ + public static int getMedian(int[] arr, int begin, int end) { + insertionSort(arr, begin, end); + int sum = begin + end; + int mid = sum / 2 + (sum % 2); + return arr[mid]; + } + + /** + * Sorts a portion of the array using insertion sort. + * + * @param arr the input array + * @param begin the starting index + * @param end the ending index + */ + public static void insertionSort(int[] arr, int begin, int end) { + if (arr == null || arr.length < 2) { + return; + } + for (int i = begin + 1; i != end + 1; i++) { + for (int j = i; j != begin; j--) { + if (arr[j - 1] > arr[j]) { + swap(arr, j - 1, j); + } else { + break; + } + } + } + } + + /** + * Swaps two elements in an array. + * + * @param arr the input array + * @param i the index of the first element + * @param j the index of the second element + */ + public static void swap(int[] arr, int i, int j) { + int temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; + } +} diff --git a/src/main/java/com/thealgorithms/others/BankersAlgorithm.java b/src/main/java/com/thealgorithms/others/BankersAlgorithm.java new file mode 100644 index 000000000000..836526529374 --- /dev/null +++ b/src/main/java/com/thealgorithms/others/BankersAlgorithm.java @@ -0,0 +1,179 @@ +package com.thealgorithms.others; + +import java.util.Scanner; + +/** + * This file contains an implementation of BANKER'S ALGORITM Wikipedia: + * https://en.wikipedia.org/wiki/Banker%27s_algorithm + * + * The algorithm for finding out whether or not a system is in a safe state can + * be described as follows: 1. Let Work and Finish be vectors of length ‘m’ and + * ‘n’ respectively. Initialize: Work= Available Finish [i]=false; for + * i=1,2,……,n 2. Find an i such that both a) Finish [i]=false b) Need_i<=work + * + * if no such i exists goto step (4) 3. Work=Work + Allocation_i Finish[i]= true + * goto step(2) 4. If Finish[i]=true for all i, then the system is in safe + * state. + * + * Time Complexity: O(n*n*m) Space Complexity: O(n*m) where n = number of + * processes and m = number of resources. + * + * @author AMRITESH ANAND (https://github.com/amritesh19) + */ +public final class BankersAlgorithm { + private BankersAlgorithm() { + } + + /** + * This method finds the need of each process + */ + static void calculateNeed(int[][] needArray, int[][] maxArray, int[][] allocationArray, int totalProcess, int totalResources) { + for (int i = 0; i < totalProcess; i++) { + for (int j = 0; j < totalResources; j++) { + needArray[i][j] = maxArray[i][j] - allocationArray[i][j]; + } + } + } + + /** + * This method find the system is in safe state or not + * + * @param processes[] int array of processes (0...n-1), size = n + * @param availableArray[] int array of number of instances of each + * resource, size = m + * @param maxArray[][] int matrix(2-D array) of maximum demand of each + * process in a system, size = n*m + * @param allocationArray[][] int matrix(2-D array) of the number of + * resources of each type currently allocated to each process, size = n*m + * @param totalProcess number of total processes, n + * @param totalResources number of total resources, m + * + * @return boolean if the system is in safe state or not + */ + static boolean checkSafeSystem(int[] processes, int[] availableArray, int[][] maxArray, int[][] allocationArray, int totalProcess, int totalResources) { + int[][] needArray = new int[totalProcess][totalResources]; + + calculateNeed(needArray, maxArray, allocationArray, totalProcess, totalResources); + + boolean[] finishProcesses = new boolean[totalProcess]; + + int[] safeSequenceArray = new int[totalProcess]; + + int[] workArray = new int[totalResources]; + System.arraycopy(availableArray, 0, workArray, 0, totalResources); + + int count = 0; + + // While all processes are not finished or system is not in safe state. + while (count < totalProcess) { + boolean foundSafeSystem = false; + for (int m = 0; m < totalProcess; m++) { + if (!finishProcesses[m]) { + int j; + + for (j = 0; j < totalResources; j++) { + if (needArray[m][j] > workArray[j]) { + break; + } + } + + if (j == totalResources) { + for (int k = 0; k < totalResources; k++) { + workArray[k] += allocationArray[m][k]; + } + + safeSequenceArray[count++] = m; + + finishProcesses[m] = true; + + foundSafeSystem = true; + } + } + } + + // If we could not find a next process in safe sequence. + if (!foundSafeSystem) { + System.out.print("The system is not in the safe state because lack of resources"); + return false; + } + } + + System.out.print("The system is in safe sequence and the sequence is as follows: "); + for (int i = 0; i < totalProcess; i++) { + System.out.print("P" + safeSequenceArray[i] + " "); + } + + return true; + } + + /** + * This is main method of Banker's Algorithm + */ + public static void main(String[] args) { + int numberOfProcesses; + int numberOfResources; + + Scanner sc = new Scanner(System.in); + + System.out.println("Enter total number of processes"); + numberOfProcesses = sc.nextInt(); + + System.out.println("Enter total number of resources"); + numberOfResources = sc.nextInt(); + + int[] processes = new int[numberOfProcesses]; + for (int i = 0; i < numberOfProcesses; i++) { + processes[i] = i; + } + + System.out.println("--Enter the availability of--"); + + int[] availableArray = new int[numberOfResources]; + for (int i = 0; i < numberOfResources; i++) { + System.out.println("resource " + i + ": "); + availableArray[i] = sc.nextInt(); + } + + System.out.println("--Enter the maximum matrix--"); + + int[][] maxArray = new int[numberOfProcesses][numberOfResources]; + for (int i = 0; i < numberOfProcesses; i++) { + System.out.println("For process " + i + ": "); + for (int j = 0; j < numberOfResources; j++) { + System.out.println("Enter the maximum instances of resource " + j); + maxArray[i][j] = sc.nextInt(); + } + } + + System.out.println("--Enter the allocation matrix--"); + + int[][] allocationArray = new int[numberOfProcesses][numberOfResources]; + for (int i = 0; i < numberOfProcesses; i++) { + System.out.println("For process " + i + ": "); + for (int j = 0; j < numberOfResources; j++) { + System.out.println("Allocated instances of resource " + j); + allocationArray[i][j] = sc.nextInt(); + } + } + + checkSafeSystem(processes, availableArray, maxArray, allocationArray, numberOfProcesses, numberOfResources); + + sc.close(); + } +} +/* + Example: + n = 5 + m = 3 + + Process Allocation Max Available + 0 1 2 0 1 2 0 1 2 + + 0 0 1 0 7 5 3 3 3 2 + 1 2 0 0 3 2 2 + 2 3 0 2 9 0 2 + 3 2 1 1 2 2 2 + 4 0 0 2 4 3 3 + + Result: The system is in safe sequence and the sequence is as follows: P1, P3, P4, P0, P2 + */ diff --git a/src/main/java/com/thealgorithms/others/BoyerMoore.java b/src/main/java/com/thealgorithms/others/BoyerMoore.java new file mode 100644 index 000000000000..3fb97724b5ac --- /dev/null +++ b/src/main/java/com/thealgorithms/others/BoyerMoore.java @@ -0,0 +1,81 @@ +package com.thealgorithms.others; +import java.util.Optional; + +/** + * Utility class implementing Boyer-Moore's Voting Algorithm to find the majority element + * in an array. The majority element is defined as the element that appears more than n/2 times + * in the array, where n is the length of the array. + * + * For more information on the algorithm, refer to: + * https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_majority_vote_algorithm + */ +public final class BoyerMoore { + private BoyerMoore() { + } + + /** + * Finds the majority element in the given array if it exists. + * + * @param array the input array + * @return an Optional containing the majority element if it exists, otherwise an empty Optional + */ + public static Optional<Integer> findMajorityElement(int[] array) { + if (array == null || array.length == 0) { + return Optional.empty(); + } + + int candidate = findCandidate(array); + int count = countOccurrences(candidate, array); + + if (isMajority(count, array.length)) { + return Optional.of(candidate); + } + return Optional.empty(); + } + + /** + * Identifies the potential majority candidate using Boyer-Moore's Voting Algorithm. + * + * @param array the input array + * @return the candidate for the majority element + */ + private static int findCandidate(final int[] array) { + int count = 0; + int candidate = -1; + for (int value : array) { + if (count == 0) { + candidate = value; + } + count += (value == candidate) ? 1 : -1; + } + return candidate; + } + + /** + * Counts the occurrences of the candidate element in the array. + * + * @param candidate the candidate element + * @param array the input array + * @return the number of times the candidate appears in the array + */ + private static int countOccurrences(final int candidate, final int[] array) { + int count = 0; + for (int value : array) { + if (value == candidate) { + count++; + } + } + return count; + } + + /** + * Determines if the count of the candidate element is more than n/2, where n is the length of the array. + * + * @param count the number of occurrences of the candidate + * @param totalCount the total number of elements in the array + * @return true if the candidate is the majority element, false otherwise + */ + private static boolean isMajority(int count, int totalCount) { + return 2 * count > totalCount; + } +} diff --git a/src/main/java/com/thealgorithms/others/BrianKernighanAlgorithm.java b/src/main/java/com/thealgorithms/others/BrianKernighanAlgorithm.java new file mode 100644 index 000000000000..b70fffe82c5b --- /dev/null +++ b/src/main/java/com/thealgorithms/others/BrianKernighanAlgorithm.java @@ -0,0 +1,50 @@ +package com.thealgorithms.others; + +import java.util.Scanner; + +/** + * @author Nishita Aggarwal + * <p> + * Brian Kernighan’s Algorithm + * <p> + * algorithm to count the number of set bits in a given number + * <p> + * Subtraction of 1 from a number toggles all the bits (from right to left) till + * the rightmost set bit(including the rightmost set bit). So if we subtract a + * number by 1 and do bitwise & with itself i.e. (n & (n-1)), we unset the + * rightmost set bit. + * <p> + * If we do n & (n-1) in a loop and count the no of times loop executes we get + * the set bit count. + * <p> + * <p> + * Time Complexity: O(logn) + */ +public final class BrianKernighanAlgorithm { + private BrianKernighanAlgorithm() { + } + + /** + * @param num: number in which we count the set bits + * @return int: Number of set bits + */ + static int countSetBits(int num) { + int cnt = 0; + while (num != 0) { + num = num & (num - 1); + cnt++; + } + return cnt; + } + + /** + * @param args : command line arguments + */ + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + int num = sc.nextInt(); + int setBitCount = countSetBits(num); + System.out.println(setBitCount); + sc.close(); + } +} diff --git a/src/main/java/com/thealgorithms/others/CRC16.java b/src/main/java/com/thealgorithms/others/CRC16.java new file mode 100644 index 000000000000..847ce8edab0a --- /dev/null +++ b/src/main/java/com/thealgorithms/others/CRC16.java @@ -0,0 +1,32 @@ +package com.thealgorithms.others; + +/** + * Generates a crc16 checksum for a given string + */ +public final class CRC16 { + private CRC16() { + } + + public static void main(String[] args) { + System.out.println(crc16("Hello World!")); + } + + public static String crc16(String message) { + int crc = 0xFFFF; // initial value + int polynomial = 0x1021; // 0001 0000 0010 0001 (0, 5, 12) + byte[] bytes = message.getBytes(); + + for (byte b : bytes) { + for (int i = 0; i < 8; i++) { + boolean bit = ((b >> (7 - i) & 1) == 1); + boolean c15 = ((crc >> 15 & 1) == 1); + crc <<= 1; + if (c15 ^ bit) { + crc ^= polynomial; + } + } + } + crc &= 0xffff; + return Integer.toHexString(crc).toUpperCase(); + } +} diff --git a/Others/CRC32.java b/src/main/java/com/thealgorithms/others/CRC32.java similarity index 86% rename from Others/CRC32.java rename to src/main/java/com/thealgorithms/others/CRC32.java index 80eb3d5c730c..180936ed46c1 100644 --- a/Others/CRC32.java +++ b/src/main/java/com/thealgorithms/others/CRC32.java @@ -1,11 +1,13 @@ -package Others; +package com.thealgorithms.others; import java.util.BitSet; /** * Generates a crc32 checksum for a given string or byte array */ -public class CRC32 { +public final class CRC32 { + private CRC32() { + } public static void main(String[] args) { System.out.println(Integer.toHexString(crc32("Hello World"))); @@ -19,13 +21,13 @@ public static int crc32(byte[] data) { BitSet bitSet = BitSet.valueOf(data); int crc32 = 0xFFFFFFFF; // initial value for (int i = 0; i < data.length * 8; i++) { - if (((crc32 >>> 31) & 1) != (bitSet.get(i) ? 1 : 0)) + if (((crc32 >>> 31) & 1) != (bitSet.get(i) ? 1 : 0)) { crc32 = (crc32 << 1) ^ 0x04C11DB7; // xor with polynomial - else + } else { crc32 = (crc32 << 1); + } } crc32 = Integer.reverse(crc32); // result reflect return crc32 ^ 0xFFFFFFFF; // final xor value } - } diff --git a/src/main/java/com/thealgorithms/others/CRCAlgorithm.java b/src/main/java/com/thealgorithms/others/CRCAlgorithm.java new file mode 100644 index 000000000000..284a290a5af8 --- /dev/null +++ b/src/main/java/com/thealgorithms/others/CRCAlgorithm.java @@ -0,0 +1,197 @@ +package com.thealgorithms.others; + +import java.util.ArrayList; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +/** + * @author dimgrichr + */ +public class CRCAlgorithm { + + private int correctMess; + + private int wrongMess; + + private int wrongMessCaught; + + private int wrongMessNotCaught; + + private int messSize; + + private double ber; + + private boolean messageChanged; + + private ArrayList<Integer> message; + + private ArrayList<Integer> p; + + private Random randomGenerator; + + /** + * The algorithm's main constructor. The most significant variables, used in + * the algorithm, are set in their initial values. + * + * @param str The binary number P, in a string form, which is used by the + * CRC algorithm + * @param size The size of every transmitted message + * @param ber The Bit Error Rate + */ + public CRCAlgorithm(String str, int size, double ber) { + messageChanged = false; + message = new ArrayList<>(); + messSize = size; + p = new ArrayList<>(); + for (int i = 0; i < str.length(); i++) { + p.add(Character.getNumericValue(str.charAt(i))); + } + randomGenerator = new Random(); + correctMess = 0; + wrongMess = 0; + wrongMessCaught = 0; + wrongMessNotCaught = 0; + this.ber = ber; + } + + /** + * Returns the counter wrongMess + * + * @return wrongMess, the number of Wrong Messages + */ + public int getWrongMess() { + return wrongMess; + } + + /** + * Returns the counter wrongMessCaught + * + * @return wrongMessCaught, the number of wrong messages, which are caught + * by the CRC algoriithm + */ + public int getWrongMessCaught() { + return wrongMessCaught; + } + + /** + * Returns the counter wrongMessNotCaught + * + * @return wrongMessNotCaught, the number of wrong messages, which are not + * caught by the CRC algorithm + */ + public int getWrongMessNotCaught() { + return wrongMessNotCaught; + } + + /** + * Returns the counter correctMess + * + * @return correctMess, the number of the Correct Messages + */ + public int getCorrectMess() { + return correctMess; + } + + /** + * Resets some of the object's values, used on the main function, so that it + * can be re-used, in order not to waste too much memory and time, by + * creating new objects. + */ + public void refactor() { + messageChanged = false; + message = new ArrayList<>(); + } + + /** + * Random messages, consisted of 0's and 1's, are generated, so that they + * can later be transmitted + */ + public void generateRandomMess() { + for (int i = 0; i < messSize; i++) { + int x = ThreadLocalRandom.current().nextInt(0, 2); + message.add(x); + } + } + + /** + * The most significant part of the CRC algorithm. The message is divided by + * P, so the dividedMessage ArrayList<Integer> is created. If check == true, + * the dividedMessaage is examined, in order to see if it contains any 1's. + * If it does, the message is considered to be wrong by the receiver,so the + * variable wrongMessCaught changes. If it does not, it is accepted, so one + * of the variables correctMess, wrongMessNotCaught, changes. If check == + * false, the diviided Message is added at the end of the ArrayList<integer> + * message. + * + * @param check the variable used to determine, if the message is going to + * be checked from the receiver if true, it is checked otherwise, it is not + */ + public void divideMessageWithP(boolean check) { + ArrayList<Integer> x = new ArrayList<>(); + ArrayList<Integer> k = (ArrayList<Integer>) message.clone(); + if (!check) { + for (int i = 0; i < p.size() - 1; i++) { + k.add(0); + } + } + while (!k.isEmpty()) { + while (x.size() < p.size() && !k.isEmpty()) { + x.add(k.get(0)); + k.remove(0); + } + if (x.size() == p.size()) { + for (int i = 0; i < p.size(); i++) { + if (x.get(i) == p.get(i)) { + x.set(i, 0); + } else { + x.set(i, 1); + } + } + for (int i = 0; i < x.size() && x.get(i) != 1; i++) { + x.remove(0); + } + } + } + ArrayList<Integer> dividedMessage = (ArrayList<Integer>) x.clone(); + if (!check) { + message.addAll(dividedMessage); + } else { + if (dividedMessage.contains(1) && messageChanged) { + wrongMessCaught++; + } else if (!dividedMessage.contains(1) && messageChanged) { + wrongMessNotCaught++; + } else if (!messageChanged) { + correctMess++; + } + } + } + + /** + * Once the message is transmitted, some of it's elements, is possible to + * change from 1 to 0, or from 0 to 1, because of the Bit Error Rate (ber). + * For every element of the message, a random double number is created. If + * that number is smaller than ber, then the spesific element changes. On + * the other hand, if it's bigger than ber, it does not. Based on these + * changes. the boolean variable messageChanged, gets the value: true, or + * false. + */ + public void changeMess() { + for (int y : message) { + double x = randomGenerator.nextDouble(); + while (x < 0.0000 || x > 1.00000) { + x = randomGenerator.nextDouble(); + } + if (x < ber) { + messageChanged = true; + if (y == 1) { + message.set(message.indexOf(y), 0); + } else { + message.set(message.indexOf(y), 1); + } + } + } + if (messageChanged) { + wrongMess++; + } + } +} diff --git a/src/main/java/com/thealgorithms/others/Conway.java b/src/main/java/com/thealgorithms/others/Conway.java new file mode 100644 index 000000000000..ab39890c9ece --- /dev/null +++ b/src/main/java/com/thealgorithms/others/Conway.java @@ -0,0 +1,37 @@ +package com.thealgorithms.others; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public final class Conway { + private Conway() { + } + + /* + * This class will generate the conway sequence also known as the look and say sequence. + * To generate a member of the sequence from the previous member, read off the digits of the + *previous member, counting the number of digits in groups of the same digit. For example: 1 is + *read off as "one 1" or 11. 11 is read off as "two 1s" or 21. 21 is read off as "one 2, one 1" + *or 1211. 1211 is read off as "one 1, one 2, two 1s" or 111221. 111221 is read off as "three + *1s, two 2s, one 1" or 312211. https://en.wikipedia.org/wiki/Look-and-say_sequence + * */ + + private static final StringBuilder BUILDER = new StringBuilder(); + + protected static List<String> generateList(String originalString, int maxIteration) { + List<String> numbers = new ArrayList<>(); + for (int i = 0; i < maxIteration; i++) { + originalString = generateNextElement(originalString); + numbers.add(originalString); + } + return numbers; + } + + public static String generateNextElement(String originalString) { + BUILDER.setLength(0); + String[] stp = originalString.split("(?<=(.))(?!\\1)"); + Arrays.stream(stp).forEach(s -> BUILDER.append(s.length()).append(s.charAt(0))); + return BUILDER.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/others/Damm.java b/src/main/java/com/thealgorithms/others/Damm.java new file mode 100644 index 000000000000..55a4c5b81a89 --- /dev/null +++ b/src/main/java/com/thealgorithms/others/Damm.java @@ -0,0 +1,116 @@ +package com.thealgorithms.others; + +import java.util.Objects; + +/** + * Damm algorithm is a check digit algorithm that detects all single-digit + * errors and all adjacent transposition errors. It was presented by H. Michael + * Damm in 2004. Essential part of the algorithm is a quasigroup of order 10 + * (i.e. having a 10 × 10 Latin square as the body of its operation table) with + * the special feature of being weakly totally anti-symmetric. Damm revealed + * several methods to create totally anti-symmetric quasigroups of order 10 and + * gave some examples in his doctoral dissertation. + * + * @see <a href="https://en.wikipedia.org/wiki/Damm_algorithm">Wiki. Damm + * algorithm</a> + */ +public final class Damm { + private Damm() { + } + + /** + * Weakly totally anti-symmetric quasigroup of order 10. This table is not + * the only possible realisation of weak totally anti-symmetric quasigroup + * but the most common one (taken from Damm doctoral dissertation). All + * zeros lay on the diagonal because it simplifies the check digit + * calculation. + */ + private static final byte[][] DAMM_TABLE = { + {0, 3, 1, 7, 5, 9, 8, 6, 4, 2}, + {7, 0, 9, 2, 1, 5, 4, 8, 6, 3}, + {4, 2, 0, 6, 8, 7, 1, 3, 5, 9}, + {1, 7, 5, 0, 9, 8, 3, 4, 2, 6}, + {6, 1, 2, 3, 0, 4, 5, 9, 7, 8}, + {3, 6, 7, 4, 2, 0, 9, 5, 8, 1}, + {5, 8, 6, 9, 7, 2, 0, 1, 3, 4}, + {8, 9, 4, 5, 3, 6, 2, 0, 1, 7}, + {9, 4, 3, 8, 6, 1, 7, 2, 0, 5}, + {2, 5, 8, 1, 4, 3, 6, 7, 9, 0}, + }; + + /** + * Check input digits by Damm algorithm. + * + * @param digits input to check + * @return true if check was successful, false otherwise + * @throws IllegalArgumentException if input parameter contains not only + * digits + * @throws NullPointerException if input is null + */ + public static boolean dammCheck(String digits) { + checkInput(digits); + int[] numbers = toIntArray(digits); + + int checksum = 0; + for (int number : numbers) { + checksum = DAMM_TABLE[checksum][number]; + } + + return checksum == 0; + } + + /** + * Calculate check digit for initial digits and add it tho the last + * position. + * + * @param initialDigits initial value + * @return digits with the checksum in the last position + * @throws IllegalArgumentException if input parameter contains not only + * digits + * @throws NullPointerException if input is null + */ + public static String addDammChecksum(String initialDigits) { + checkInput(initialDigits); + int[] numbers = toIntArray(initialDigits); + + int checksum = 0; + for (int number : numbers) { + checksum = DAMM_TABLE[checksum][number]; + } + + return initialDigits + checksum; + } + + public static void main(String[] args) { + System.out.println("Damm algorithm usage examples:"); + var validInput = "5724"; + var invalidInput = "5824"; + checkAndPrint(validInput); + checkAndPrint(invalidInput); + + System.out.println("\nCheck digit generation example:"); + var input = "572"; + generateAndPrint(input); + } + + private static void checkAndPrint(String input) { + String validationResult = Damm.dammCheck(input) ? "valid" : "not valid"; + System.out.println("Input '" + input + "' is " + validationResult); + } + + private static void generateAndPrint(String input) { + String result = addDammChecksum(input); + System.out.println("Generate and add checksum to initial value '" + input + "'. Result: '" + result + "'"); + } + + private static void checkInput(String input) { + Objects.requireNonNull(input); + if (!input.matches("\\d+")) { + throw new IllegalArgumentException("Input '" + input + "' contains not only digits"); + } + } + + private static int[] toIntArray(String string) { + return string.chars().map(i -> Character.digit(i, 10)).toArray(); + } +} diff --git a/src/main/java/com/thealgorithms/others/Dijkstra.java b/src/main/java/com/thealgorithms/others/Dijkstra.java new file mode 100644 index 000000000000..a379100a2f3b --- /dev/null +++ b/src/main/java/com/thealgorithms/others/Dijkstra.java @@ -0,0 +1,248 @@ +package com.thealgorithms.others; + +import java.util.HashMap; +import java.util.Map; +import java.util.NavigableSet; +import java.util.TreeSet; +/** + * Dijkstra's algorithm,is a graph search algorithm that solves the + * single-source shortest path problem for a graph with nonnegative edge path + * costs, producing a shortest path tree. + * + * <p> + * NOTE: The inputs to Dijkstra's algorithm are a directed and weighted graph + * consisting of 2 or more nodes, generally represented by an adjacency matrix + * or list, and a start node. + * + * <p> + * Original source of code: + * https://rosettacode.org/wiki/Dijkstra%27s_algorithm#Java Also most of the + * comments are from RosettaCode. + */ +public final class Dijkstra { + private Dijkstra() { + } + + private static final Graph.Edge[] GRAPH = { + // Distance from node "a" to node "b" is 7. + // In the current Graph there is no way to move the other way (e,g, from "b" to "a"), + // a new edge would be needed for that + new Graph.Edge("a", "b", 7), + new Graph.Edge("a", "c", 9), + new Graph.Edge("a", "f", 14), + new Graph.Edge("b", "c", 10), + new Graph.Edge("b", "d", 15), + new Graph.Edge("c", "d", 11), + new Graph.Edge("c", "f", 2), + new Graph.Edge("d", "e", 6), + new Graph.Edge("e", "f", 9), + }; + private static final String START = "a"; + private static final String END = "e"; + + /** + * main function Will run the code with "GRAPH" that was defined above. + */ + public static void main(String[] args) { + Graph g = new Graph(GRAPH); + g.dijkstra(START); + g.printPath(END); + // g.printAllPaths(); + } +} + +class Graph { + + // mapping of vertex names to Vertex objects, built from a set of Edges + + private final Map<String, Vertex> graph; + + /** + * One edge of the graph (only used by Graph constructor) + */ + public static class Edge { + + public final String v1; + public final String v2; + public final int dist; + + Edge(String v1, String v2, int dist) { + this.v1 = v1; + this.v2 = v2; + this.dist = dist; + } + } + + /** + * One vertex of the graph, complete with mappings to neighbouring vertices + */ + public static class Vertex implements Comparable<Vertex> { + + public final String name; + // MAX_VALUE assumed to be infinity + public int dist = Integer.MAX_VALUE; + public Vertex previous = null; + public final Map<Vertex, Integer> neighbours = new HashMap<>(); + + Vertex(String name) { + this.name = name; + } + + private void printPath() { + if (this == this.previous) { + System.out.printf("%s", this.name); + } else if (this.previous == null) { + System.out.printf("%s(unreached)", this.name); + } else { + this.previous.printPath(); + System.out.printf(" -> %s(%d)", this.name, this.dist); + } + } + + public int compareTo(Vertex other) { + if (dist == other.dist) { + return name.compareTo(other.name); + } + + return Integer.compare(dist, other.dist); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null || getClass() != object.getClass()) { + return false; + } + if (!super.equals(object)) { + return false; + } + + Vertex vertex = (Vertex) object; + + if (dist != vertex.dist) { + return false; + } + if (name != null ? !name.equals(vertex.name) : vertex.name != null) { + return false; + } + if (previous != null ? !previous.equals(vertex.previous) : vertex.previous != null) { + return false; + } + return neighbours != null ? neighbours.equals(vertex.neighbours) : vertex.neighbours == null; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (name != null ? name.hashCode() : 0); + result = 31 * result + dist; + result = 31 * result + (previous != null ? previous.hashCode() : 0); + result = 31 * result + (neighbours != null ? neighbours.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "(" + name + ", " + dist + ")"; + } + } + + /** + * Builds a graph from a set of edges + */ + Graph(Edge[] edges) { + graph = new HashMap<>(edges.length); + + // one pass to find all vertices + for (Edge e : edges) { + if (!graph.containsKey(e.v1)) { + graph.put(e.v1, new Vertex(e.v1)); + } + if (!graph.containsKey(e.v2)) { + graph.put(e.v2, new Vertex(e.v2)); + } + } + + // another pass to set neighbouring vertices + for (Edge e : edges) { + graph.get(e.v1).neighbours.put(graph.get(e.v2), e.dist); + // graph.get(e.v2).neighbours.put(graph.get(e.v1), e.dist); // also do this for an + // undirected graph + } + } + + /** + * Runs dijkstra using a specified source vertex + */ + public void dijkstra(String startName) { + if (!graph.containsKey(startName)) { + System.err.printf("Graph doesn't contain start vertex \"%s\"%n", startName); + return; + } + final Vertex source = graph.get(startName); + NavigableSet<Vertex> q = new TreeSet<>(); + + // set-up vertices + for (Vertex v : graph.values()) { + v.previous = v == source ? source : null; + v.dist = v == source ? 0 : Integer.MAX_VALUE; + q.add(v); + } + + dijkstra(q); + } + + /** + * Implementation of dijkstra's algorithm using a binary heap. + */ + private void dijkstra(final NavigableSet<Vertex> q) { + Vertex u; + Vertex v; + while (!q.isEmpty()) { + // vertex with shortest distance (first iteration will return source) + u = q.pollFirst(); + if (u.dist == Integer.MAX_VALUE) { + break; // we can ignore u (and any other remaining vertices) since they are + // unreachable + } + // look at distances to each neighbour + for (Map.Entry<Vertex, Integer> a : u.neighbours.entrySet()) { + v = a.getKey(); // the neighbour in this iteration + + final int alternateDist = u.dist + a.getValue(); + if (alternateDist < v.dist) { // shorter path to neighbour found + q.remove(v); + v.dist = alternateDist; + v.previous = u; + q.add(v); + } + } + } + } + + /** + * Prints a path from the source to the specified vertex + */ + public void printPath(String endName) { + if (!graph.containsKey(endName)) { + System.err.printf("Graph doesn't contain end vertex \"%s\"%n", endName); + return; + } + + graph.get(endName).printPath(); + System.out.println(); + } + + /** + * Prints the path from the source to every vertex (output order is not + * guaranteed) + */ + public void printAllPaths() { + for (Vertex v : graph.values()) { + v.printPath(); + System.out.println(); + } + } +} diff --git a/src/main/java/com/thealgorithms/others/FloydTriangle.java b/src/main/java/com/thealgorithms/others/FloydTriangle.java new file mode 100644 index 000000000000..dff48443fc25 --- /dev/null +++ b/src/main/java/com/thealgorithms/others/FloydTriangle.java @@ -0,0 +1,30 @@ +package com.thealgorithms.others; + +import java.util.ArrayList; +import java.util.List; + +final class FloydTriangle { + private FloydTriangle() { + } + + /** + * Generates a Floyd Triangle with the specified number of rows. + * + * @param rows The number of rows in the triangle. + * @return A List representing the Floyd Triangle. + */ + public static List<List<Integer>> generateFloydTriangle(int rows) { + List<List<Integer>> triangle = new ArrayList<>(); + int number = 1; + + for (int i = 0; i < rows; i++) { + List<Integer> row = new ArrayList<>(); + for (int j = 0; j <= i; j++) { + row.add(number++); + } + triangle.add(row); + } + + return triangle; + } +} diff --git a/src/main/java/com/thealgorithms/others/GaussLegendre.java b/src/main/java/com/thealgorithms/others/GaussLegendre.java new file mode 100644 index 000000000000..b56b51f158fb --- /dev/null +++ b/src/main/java/com/thealgorithms/others/GaussLegendre.java @@ -0,0 +1,48 @@ +package com.thealgorithms.others; + +/** + * Guass Legendre Algorithm ref + * https://en.wikipedia.org/wiki/Gauss–Legendre_algorithm + * + * @author AKS1996 + */ +public final class GaussLegendre { + private GaussLegendre() { + } + + public static void main(String[] args) { + for (int i = 1; i <= 3; ++i) { + System.out.println(pi(i)); + } + } + + static double pi(int l) { + /* + * l: No of loops to run + */ + + double a = 1; + double b = Math.pow(2, -0.5); + double t = 0.25; + double p = 1; + for (int i = 0; i < l; ++i) { + double[] temp = update(a, b, t, p); + a = temp[0]; + b = temp[1]; + t = temp[2]; + p = temp[3]; + } + + return Math.pow(a + b, 2) / (4 * t); + } + + static double[] update(double a, double b, double t, double p) { + double[] values = new double[4]; + values[0] = (a + b) / 2; + values[1] = Math.sqrt(a * b); + values[2] = t - p * Math.pow(a - values[0], 2); + values[3] = 2 * p; + + return values; + } +} diff --git a/src/main/java/com/thealgorithms/others/HappyNumbersSeq.java b/src/main/java/com/thealgorithms/others/HappyNumbersSeq.java new file mode 100644 index 000000000000..0ae1e451bc6a --- /dev/null +++ b/src/main/java/com/thealgorithms/others/HappyNumbersSeq.java @@ -0,0 +1,39 @@ +package com.thealgorithms.others; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Scanner; +import java.util.Set; + +public final class HappyNumbersSeq { + private HappyNumbersSeq() { + } + + private static final Set<Integer> CYCLE_NUMS = new HashSet<>(Arrays.asList(4, 16, 20, 37, 58, 145)); + + public static void main(String[] args) { + Scanner in = new Scanner(System.in); + System.out.print("Enter number: "); + int n = in.nextInt(); + while (n != 1 && !isSad(n)) { + System.out.print(n + " "); + n = sumSquares(n); + } + String res = n == 1 ? "1 Happy number" : "Sad number"; + System.out.println(res); + in.close(); + } + + private static int sumSquares(int n) { + int s = 0; + for (; n > 0; n /= 10) { + int r = n % 10; + s += r * r; + } + return s; + } + + private static boolean isSad(int n) { + return CYCLE_NUMS.contains(n); + } +} diff --git a/src/main/java/com/thealgorithms/others/Huffman.java b/src/main/java/com/thealgorithms/others/Huffman.java new file mode 100644 index 000000000000..4fdee5d5e70e --- /dev/null +++ b/src/main/java/com/thealgorithms/others/Huffman.java @@ -0,0 +1,125 @@ +package com.thealgorithms.others; + +import java.util.Comparator; +import java.util.PriorityQueue; +import java.util.Scanner; + +// node class is the basic structure +// of each node present in the Huffman - tree. +class HuffmanNode { + + int data; + char c; + + HuffmanNode left; + HuffmanNode right; +} + +// comparator class helps to compare the node +// on the basis of one of its attribute. +// Here we will be compared +// on the basis of data values of the nodes. +class MyComparator implements Comparator<HuffmanNode> { + + public int compare(HuffmanNode x, HuffmanNode y) { + return x.data - y.data; + } +} + +public final class Huffman { + private Huffman() { + } + + // recursive function to print the + // huffman-code through the tree traversal. + // Here s is the huffman - code generated. + public static void printCode(HuffmanNode root, String s) { + // base case; if the left and right are null + // then its a leaf node and we print + // the code s generated by traversing the tree. + if (root.left == null && root.right == null && Character.isLetter(root.c)) { + // c is the character in the node + System.out.println(root.c + ":" + s); + + return; + } + + // if we go to left then add "0" to the code. + // if we go to the right add"1" to the code. + // recursive calls for left and + // right sub-tree of the generated tree. + printCode(root.left, s + "0"); + printCode(root.right, s + "1"); + } + + // main function + public static void main(String[] args) { + Scanner s = new Scanner(System.in); + + // number of characters. + int n = 6; + char[] charArray = {'a', 'b', 'c', 'd', 'e', 'f'}; + int[] charfreq = {5, 9, 12, 13, 16, 45}; + + // creating a priority queue q. + // makes a min-priority queue(min-heap). + PriorityQueue<HuffmanNode> q = new PriorityQueue<HuffmanNode>(n, new MyComparator()); + + for (int i = 0; i < n; i++) { + // creating a Huffman node object + // and add it to the priority queue. + HuffmanNode hn = new HuffmanNode(); + + hn.c = charArray[i]; + hn.data = charfreq[i]; + + hn.left = null; + hn.right = null; + + // add functions adds + // the huffman node to the queue. + q.add(hn); + } + + // create a root node + HuffmanNode root = null; + + // Here we will extract the two minimum value + // from the heap each time until + // its size reduces to 1, extract until + // all the nodes are extracted. + while (q.size() > 1) { + // first min extract. + HuffmanNode x = q.peek(); + q.poll(); + + // second min extarct. + HuffmanNode y = q.peek(); + q.poll(); + + // new node f which is equal + HuffmanNode f = new HuffmanNode(); + + // to the sum of the frequency of the two nodes + // assigning values to the f node. + f.data = x.data + y.data; + f.c = '-'; + + // first extracted node as left child. + f.left = x; + + // second extracted node as the right child. + f.right = y; + + // marking the f node as the root node. + root = f; + + // add this node to the priority-queue. + q.add(f); + } + + // print the codes by traversing the tree + printCode(root, ""); + s.close(); + } +} diff --git a/src/main/java/com/thealgorithms/others/Implementing_auto_completing_features_using_trie.java b/src/main/java/com/thealgorithms/others/Implementing_auto_completing_features_using_trie.java new file mode 100644 index 000000000000..bb88c7e3ae2f --- /dev/null +++ b/src/main/java/com/thealgorithms/others/Implementing_auto_completing_features_using_trie.java @@ -0,0 +1,170 @@ +package com.thealgorithms.others; + +// Java Program to implement Auto-Complete +// Feature using Trie +class Trieac { + + // Alphabet size (# of symbols) + public static final int ALPHABET_SIZE = 26; + + // Trie node + static class TrieNode { + + TrieNode[] children = new TrieNode[ALPHABET_SIZE]; + + // isWordEnd is true if the node represents + // end of a word + boolean isWordEnd; + } + + // Returns new trie node (initialized to NULLs) + static TrieNode getNode() { + TrieNode pNode = new TrieNode(); + pNode.isWordEnd = false; + + for (int i = 0; i < ALPHABET_SIZE; i++) { + pNode.children[i] = null; + } + + return pNode; + } + + // If not present, inserts key into trie. If the + // key is prefix of trie node, just marks leaf node + static void insert(TrieNode root, final String key) { + TrieNode pCrawl = root; + + for (int level = 0; level < key.length(); level++) { + int index = (key.charAt(level) - 'a'); + if (pCrawl.children[index] == null) { + pCrawl.children[index] = getNode(); + } + pCrawl = pCrawl.children[index]; + } + + // mark last node as leaf + pCrawl.isWordEnd = true; + } + + // Returns true if key presents in trie, else false + boolean search(TrieNode root, final String key) { + int length = key.length(); + TrieNode pCrawl = root; + + for (int level = 0; level < length; level++) { + int index = (key.charAt(level) - 'a'); + + if (pCrawl.children[index] == null) { + pCrawl = pCrawl.children[index]; + } + } + + return (pCrawl != null && pCrawl.isWordEnd); + } + + // Returns 0 if current node has a child + // If all children are NULL, return 1. + static boolean isLastNode(TrieNode root) { + for (int i = 0; i < ALPHABET_SIZE; i++) { + if (root.children[i] != null) { + return false; + } + } + return true; + } + + // Recursive function to print auto-suggestions + // for given node. + static void suggestionsRec(TrieNode root, String currPrefix) { + // found a string in Trie with the given prefix + if (root.isWordEnd) { + System.out.println(currPrefix); + } + + // All children struct node pointers are NULL + if (isLastNode(root)) { + return; + } + + for (int i = 0; i < ALPHABET_SIZE; i++) { + if (root.children[i] != null) { + // append current character to currPrefix string + currPrefix += (char) (97 + i); + + // recur over the rest + suggestionsRec(root.children[i], currPrefix); + } + } + } + + // Fucntion to print suggestions for + // given query prefix. + static int printAutoSuggestions(TrieNode root, final String query) { + TrieNode pCrawl = root; + + // Check if prefix is present and find the + // the node (of last level) with last character + // of given string. + int level; + int n = query.length(); + + for (level = 0; level < n; level++) { + int index = (query.charAt(level) - 'a'); + + // no string in the Trie has this prefix + if (pCrawl.children[index] == null) { + return 0; + } + + pCrawl = pCrawl.children[index]; + } + + // If prefix is present as a word. + boolean isWord = (pCrawl.isWordEnd); + + // If prefix is last node of tree (has no + // children) + boolean isLast = isLastNode(pCrawl); + + // If prefix is present as a word, but + // there is no subtree below the last + // matching node. + if (isWord && isLast) { + System.out.println(query); + return -1; + } + + // If there are nodes below the last + // matching character. + if (!isLast) { + String prefix = query; + suggestionsRec(pCrawl, prefix); + return 1; + } + + return 0; + } + + // Driver code + public static void main(String[] args) { + TrieNode root = getNode(); + insert(root, "hello"); + insert(root, "dog"); + insert(root, "hell"); + insert(root, "cat"); + insert(root, "a"); + insert(root, "hel"); + insert(root, "help"); + insert(root, "helps"); + insert(root, "helping"); + int comp = printAutoSuggestions(root, "hel"); + + if (comp == -1) { + System.out.println("No other strings found " + + "with this prefix\n"); + } else if (comp == 0) { + System.out.println("No string found with" + + " this prefix\n"); + } + } +} diff --git a/src/main/java/com/thealgorithms/others/InsertDeleteInArray.java b/src/main/java/com/thealgorithms/others/InsertDeleteInArray.java new file mode 100644 index 000000000000..15093549871b --- /dev/null +++ b/src/main/java/com/thealgorithms/others/InsertDeleteInArray.java @@ -0,0 +1,52 @@ +package com.thealgorithms.others; + +import java.util.Scanner; + +public final class InsertDeleteInArray { + private InsertDeleteInArray() { + } + + public static void main(String[] args) { + try (Scanner s = new Scanner(System.in)) { + System.out.println("Enter the size of the array"); + int size = s.nextInt(); + int[] a = new int[size]; + int i; + + // To enter the initial elements + for (i = 0; i < size; i++) { + System.out.println("Enter the element"); + a[i] = s.nextInt(); + } + + // To insert a new element(we are creating a new array) + System.out.println("Enter the index at which the element should be inserted"); + int insertPos = s.nextInt(); + System.out.println("Enter the element to be inserted"); + int ins = s.nextInt(); + int size2 = size + 1; + int[] b = new int[size2]; + for (i = 0; i < size2; i++) { + if (i <= insertPos) { + b[i] = a[i]; + } else { + b[i] = a[i - 1]; + } + } + b[insertPos] = ins; + for (i = 0; i < size2; i++) { + System.out.println(b[i]); + } + + // To delete an element given the index + System.out.println("Enter the index at which element is to be deleted"); + int delPos = s.nextInt(); + for (i = delPos; i < size2 - 1; i++) { + b[i] = b[i + 1]; + } + for (i = 0; i < size2 - 1; i++) { + System.out.println(b[i]); + } + } + } +} diff --git a/src/main/java/com/thealgorithms/others/KochSnowflake.java b/src/main/java/com/thealgorithms/others/KochSnowflake.java new file mode 100644 index 000000000000..10986aabec4f --- /dev/null +++ b/src/main/java/com/thealgorithms/others/KochSnowflake.java @@ -0,0 +1,246 @@ +package com.thealgorithms.others; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import javax.imageio.ImageIO; + +/** + * The Koch snowflake is a fractal curve and one of the earliest fractals to + * have been described. The Koch snowflake can be built up iteratively, in a + * sequence of stages. The first stage is an equilateral triangle, and each + * successive stage is formed by adding outward bends to each side of the + * previous stage, making smaller equilateral triangles. This can be achieved + * through the following steps for each line: 1. divide the line segment into + * three segments of equal length. 2. draw an equilateral triangle that has the + * middle segment from step 1 as its base and points outward. 3. remove the line + * segment that is the base of the triangle from step 2. (description adapted + * from https://en.wikipedia.org/wiki/Koch_snowflake ) (for a more detailed + * explanation and an implementation in the Processing language, see + * https://natureofcode.com/book/chapter-8-fractals/ + * #84-the-koch-curve-and-the-arraylist-technique ). + */ +public final class KochSnowflake { + private KochSnowflake() { + } + + public static void main(String[] args) { + // Test Iterate-method + ArrayList<Vector2> vectors = new ArrayList<Vector2>(); + vectors.add(new Vector2(0, 0)); + vectors.add(new Vector2(1, 0)); + ArrayList<Vector2> result = iterate(vectors, 1); + + assert result.get(0).x == 0; + assert result.get(0).y == 0; + + assert result.get(1).x == 1. / 3; + assert result.get(1).y == 0; + + assert result.get(2).x == 1. / 2; + assert result.get(2).y == Math.sin(Math.PI / 3) / 3; + + assert result.get(3).x == 2. / 3; + assert result.get(3).y == 0; + + assert result.get(4).x == 1; + assert result.get(4).y == 0; + + // Test GetKochSnowflake-method + int imageWidth = 600; + double offsetX = imageWidth / 10.; + double offsetY = imageWidth / 3.7; + BufferedImage image = getKochSnowflake(imageWidth, 5); + + // The background should be white + assert image.getRGB(0, 0) == new Color(255, 255, 255).getRGB(); + + // The snowflake is drawn in black and this is the position of the first vector + assert image.getRGB((int) offsetX, (int) offsetY) == new Color(0, 0, 0).getRGB(); + + // Save image + try { + ImageIO.write(image, "png", new File("KochSnowflake.png")); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Go through the number of iterations determined by the argument "steps". + * Be careful with high values (above 5) since the time to calculate + * increases exponentially. + * + * @param initialVectors The vectors composing the shape to which the + * algorithm is applied. + * @param steps The number of iterations. + * @return The transformed vectors after the iteration-steps. + */ + public static ArrayList<Vector2> iterate(ArrayList<Vector2> initialVectors, int steps) { + ArrayList<Vector2> vectors = initialVectors; + for (int i = 0; i < steps; i++) { + vectors = iterationStep(vectors); + } + + return vectors; + } + + /** + * Method to render the Koch snowflake to a image. + * + * @param imageWidth The width of the rendered image. + * @param steps The number of iterations. + * @return The image of the rendered Koch snowflake. + */ + public static BufferedImage getKochSnowflake(int imageWidth, int steps) { + if (imageWidth <= 0) { + throw new IllegalArgumentException("imageWidth should be greater than zero"); + } + + double offsetX = imageWidth / 10.; + double offsetY = imageWidth / 3.7; + Vector2 vector1 = new Vector2(offsetX, offsetY); + Vector2 vector2 = new Vector2(imageWidth / 2.0, Math.sin(Math.PI / 3.0) * imageWidth * 0.8 + offsetY); + Vector2 vector3 = new Vector2(imageWidth - offsetX, offsetY); + ArrayList<Vector2> initialVectors = new ArrayList<Vector2>(); + initialVectors.add(vector1); + initialVectors.add(vector2); + initialVectors.add(vector3); + initialVectors.add(vector1); + ArrayList<Vector2> vectors = iterate(initialVectors, steps); + return getImage(vectors, imageWidth, imageWidth); + } + + /** + * Loops through each pair of adjacent vectors. Each line between two + * adjacent vectors is divided into 4 segments by adding 3 additional + * vectors in-between the original two vectors. The vector in the middle is + * constructed through a 60 degree rotation so it is bent outwards. + * + * @param vectors The vectors composing the shape to which the algorithm is + * applied. + * @return The transformed vectors after the iteration-step. + */ + private static ArrayList<Vector2> iterationStep(List<Vector2> vectors) { + ArrayList<Vector2> newVectors = new ArrayList<Vector2>(); + for (int i = 0; i < vectors.size() - 1; i++) { + Vector2 startVector = vectors.get(i); + Vector2 endVector = vectors.get(i + 1); + newVectors.add(startVector); + Vector2 differenceVector = endVector.subtract(startVector).multiply(1. / 3); + newVectors.add(startVector.add(differenceVector)); + newVectors.add(startVector.add(differenceVector).add(differenceVector.rotate(60))); + newVectors.add(startVector.add(differenceVector.multiply(2))); + } + + newVectors.add(vectors.get(vectors.size() - 1)); + return newVectors; + } + + /** + * Utility-method to render the Koch snowflake to an image. + * + * @param vectors The vectors defining the edges to be rendered. + * @param imageWidth The width of the rendered image. + * @param imageHeight The height of the rendered image. + * @return The image of the rendered edges. + */ + private static BufferedImage getImage(ArrayList<Vector2> vectors, int imageWidth, int imageHeight) { + BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB); + Graphics2D g2d = image.createGraphics(); + + // Set the background white + g2d.setBackground(Color.WHITE); + g2d.fillRect(0, 0, imageWidth, imageHeight); + + // Draw the edges + g2d.setColor(Color.BLACK); + BasicStroke bs = new BasicStroke(1); + g2d.setStroke(bs); + for (int i = 0; i < vectors.size() - 1; i++) { + int x1 = (int) vectors.get(i).x; + int y1 = (int) vectors.get(i).y; + int x2 = (int) vectors.get(i + 1).x; + int y2 = (int) vectors.get(i + 1).y; + + g2d.drawLine(x1, y1, x2, y2); + } + + return image; + } + + /** + * Inner class to handle the vector calculations. + */ + private static class Vector2 { + + double x; + double y; + + Vector2(double x, double y) { + this.x = x; + this.y = y; + } + + @Override + public String toString() { + return String.format("[%f, %f]", this.x, this.y); + } + + /** + * Vector addition + * + * @param vector The vector to be added. + * @return The sum-vector. + */ + public Vector2 add(Vector2 vector) { + double x = this.x + vector.x; + double y = this.y + vector.y; + return new Vector2(x, y); + } + + /** + * Vector subtraction + * + * @param vector The vector to be subtracted. + * @return The difference-vector. + */ + public Vector2 subtract(Vector2 vector) { + double x = this.x - vector.x; + double y = this.y - vector.y; + return new Vector2(x, y); + } + + /** + * Vector scalar multiplication + * + * @param scalar The factor by which to multiply the vector. + * @return The scaled vector. + */ + public Vector2 multiply(double scalar) { + double x = this.x * scalar; + double y = this.y * scalar; + return new Vector2(x, y); + } + + /** + * Vector rotation (see https://en.wikipedia.org/wiki/Rotation_matrix) + * + * @param angleInDegrees The angle by which to rotate the vector. + * @return The rotated vector. + */ + public Vector2 rotate(double angleInDegrees) { + double radians = angleInDegrees * Math.PI / 180; + double ca = Math.cos(radians); + double sa = Math.sin(radians); + double x = ca * this.x - sa * this.y; + double y = sa * this.x + ca * this.y; + return new Vector2(x, y); + } + } +} diff --git a/src/main/java/com/thealgorithms/others/Krishnamurthy.java b/src/main/java/com/thealgorithms/others/Krishnamurthy.java new file mode 100644 index 000000000000..8e5ba7c6f1c7 --- /dev/null +++ b/src/main/java/com/thealgorithms/others/Krishnamurthy.java @@ -0,0 +1,38 @@ +package com.thealgorithms.others; + +import java.util.Scanner; + +final class Krishnamurthy { + private Krishnamurthy() { + } + + static int fact(int n) { + int i; + int p = 1; + for (i = n; i >= 1; i--) { + p = p * i; + } + return p; + } + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + int a; + int b; + int s = 0; + System.out.print("Enter the number : "); + a = sc.nextInt(); + int n = a; + while (a > 0) { + b = a % 10; + s = s + fact(b); + a = a / 10; + } + if (s == n) { + System.out.print(n + " is a krishnamurthy number"); + } else { + System.out.print(n + " is not a krishnamurthy number"); + } + sc.close(); + } +} diff --git a/src/main/java/com/thealgorithms/others/LineSweep.java b/src/main/java/com/thealgorithms/others/LineSweep.java new file mode 100644 index 000000000000..b7db964c98d0 --- /dev/null +++ b/src/main/java/com/thealgorithms/others/LineSweep.java @@ -0,0 +1,62 @@ +package com.thealgorithms.others; + +import java.util.Arrays; +import java.util.Comparator; + +/** + * The Line Sweep algorithm is used to solve range problems efficiently. It works by: + * 1. Sorting a list of ranges by their start values in non-decreasing order. + * 2. Sweeping through the number line (x-axis) while updating a count for each point based on the ranges. + * + * An overlapping range is defined as: + * - (StartA <= EndB) AND (EndA >= StartB) + * + * References: + * - https://en.wikipedia.org/wiki/Sweep_line_algorithm + * - https://en.wikipedia.org/wiki/De_Morgan%27s_laws + */ +public final class LineSweep { + private LineSweep() { + } + + /** + * Finds the maximum endpoint from a list of ranges. + * + * @param ranges a 2D array where each element is a range represented by [start, end] + * @return the maximum endpoint among all ranges + */ + public static int findMaximumEndPoint(int[][] ranges) { + Arrays.sort(ranges, Comparator.comparingInt(range -> range[1])); + return ranges[ranges.length - 1][1]; + } + + /** + * Determines if any of the given ranges overlap. + * + * @param ranges a 2D array where each element is a range represented by [start, end] + * @return true if any ranges overlap, false otherwise + */ + public static boolean isOverlap(int[][] ranges) { + if (ranges == null || ranges.length == 0) { + return false; + } + + int maximumEndPoint = findMaximumEndPoint(ranges); + int[] numberLine = new int[maximumEndPoint + 2]; + for (int[] range : ranges) { + int start = range[0]; + int end = range[1]; + + numberLine[start] += 1; + numberLine[end + 1] -= 1; + } + + int currentCount = 0; + int maxOverlaps = 0; + for (int count : numberLine) { + currentCount += count; + maxOverlaps = Math.max(maxOverlaps, currentCount); + } + return maxOverlaps > 1; + } +} diff --git a/Others/LinearCongruentialGenerator.java b/src/main/java/com/thealgorithms/others/LinearCongruentialGenerator.java similarity index 75% rename from Others/LinearCongruentialGenerator.java rename to src/main/java/com/thealgorithms/others/LinearCongruentialGenerator.java index 7ce224e0e9fe..36bcca3edc00 100644 --- a/Others/LinearCongruentialGenerator.java +++ b/src/main/java/com/thealgorithms/others/LinearCongruentialGenerator.java @@ -1,6 +1,7 @@ -package Others; +package com.thealgorithms.others; -/*** +/** + * * * A pseudorandom number generator. * * @author Tobias Carryer @@ -8,27 +9,34 @@ */ public class LinearCongruentialGenerator { - private double a, c, m, previousValue; + private final double a; + private final double c; + private final double m; + private double previousValue; - /*** - * These parameters are saved and used when nextNumber() is called. - * The current timestamp in milliseconds is used as the seed. + /** + * * + * These parameters are saved and used when nextNumber() is called. The + * current timestamp in milliseconds is used as the seed. * * @param multiplier * @param increment - * @param modulo The maximum number that can be generated (exclusive). A common value is 2^32. + * @param modulo The maximum number that can be generated (exclusive). A + * common value is 2^32. */ public LinearCongruentialGenerator(double multiplier, double increment, double modulo) { this(System.currentTimeMillis(), multiplier, increment, modulo); } - /*** + /** + * * * These parameters are saved and used when nextNumber() is called. * * @param seed * @param multiplier * @param increment - * @param modulo The maximum number that can be generated (exclusive). A common value is 2^32. + * @param modulo The maximum number that can be generated (exclusive). A + * common value is 2^32. */ public LinearCongruentialGenerator(double seed, double multiplier, double increment, double modulo) { this.previousValue = seed; @@ -38,8 +46,8 @@ public LinearCongruentialGenerator(double seed, double multiplier, double increm } /** - * The smallest number that can be generated is zero. - * The largest number that can be generated is modulo-1. modulo is set in the constructor. + * The smallest number that can be generated is zero. The largest number + * that can be generated is modulo-1. modulo is set in the constructor. * * @return a pseudorandom number. */ diff --git a/src/main/java/com/thealgorithms/others/LowestBasePalindrome.java b/src/main/java/com/thealgorithms/others/LowestBasePalindrome.java new file mode 100644 index 000000000000..76d1ed4aba1d --- /dev/null +++ b/src/main/java/com/thealgorithms/others/LowestBasePalindrome.java @@ -0,0 +1,115 @@ +package com.thealgorithms.others; + +import java.util.ArrayList; +import java.util.List; + +/** + * @brief Class for finding the lowest base in which a given integer is a palindrome. + cf. https://oeis.org/A016026 + */ +public final class LowestBasePalindrome { + private LowestBasePalindrome() { + } + + /** + * Validates the base, ensuring it is greater than 1. + * + * @param base the base to be checked + * @throws IllegalArgumentException if the base is less than or equal to 1 + */ + private static void checkBase(int base) { + if (base <= 1) { + throw new IllegalArgumentException("Base must be greater than 1."); + } + } + + /** + * Validates the number, ensuring it is non-negative. + * + * @param number the number to be checked + * @throws IllegalArgumentException if the number is negative + */ + private static void checkNumber(int number) { + if (number < 0) { + throw new IllegalArgumentException("Number must be non-negative."); + } + } + + /** + * Computes the digits of a given number in a specified base. + * + * @param number the number to be converted + * @param base the base to be used for the conversion + * @return a list of digits representing the number in the given base, with the most + * significant digit at the end of the list + * @throws IllegalArgumentException if the number is negative or the base is less than 2 + */ + public static List<Integer> computeDigitsInBase(int number, int base) { + checkNumber(number); + checkBase(base); + + List<Integer> digits = new ArrayList<>(); + while (number > 0) { + digits.add(number % base); + number /= base; + } + return digits; + } + + /** + * Checks if a list of integers is palindromic. + * + * @param list the list of integers to be checked + * @return {@code true} if the list is a palindrome, {@code false} otherwise + */ + public static boolean isPalindromic(List<Integer> list) { + int size = list.size(); + for (int i = 0; i < size / 2; i++) { + if (!list.get(i).equals(list.get(size - 1 - i))) { + return false; + } + } + return true; + } + + /** + * Checks if the representation of a given number in a specified base is palindromic. + * + * @param number the number to be checked + * @param base the base in which the number will be represented + * @return {@code true} if the number is palindromic in the specified base, {@code false} otherwise + * @throws IllegalArgumentException if the number is negative or the base is less than 2 + */ + public static boolean isPalindromicInBase(int number, int base) { + checkNumber(number); + checkBase(base); + + if (number <= 1) { + return true; + } + + if (number % base == 0) { + // If the last digit of the number in the given base is 0, it can't be palindromic + return false; + } + + return isPalindromic(computeDigitsInBase(number, base)); + } + + /** + * Finds the smallest base in which the representation of a given number is palindromic. + * + * @param number the number to be checked + * @return the smallest base in which the number is a palindrome + * @throws IllegalArgumentException if the number is negative + */ + public static int lowestBasePalindrome(int number) { + checkNumber(number); + + int base = 2; + while (!isPalindromicInBase(number, base)) { + base++; + } + return base; + } +} diff --git a/src/main/java/com/thealgorithms/others/Luhn.java b/src/main/java/com/thealgorithms/others/Luhn.java new file mode 100644 index 000000000000..600128a7725b --- /dev/null +++ b/src/main/java/com/thealgorithms/others/Luhn.java @@ -0,0 +1,160 @@ +package com.thealgorithms.others; + +import java.util.Arrays; +import java.util.Objects; + +/** + * The Luhn algorithm or Luhn formula, also known as the "modulus 10" or "mod + * 10" algorithm, named after its creator, IBM scientist Hans Peter Luhn, is a + * simple checksum formula used to validate a variety of identification numbers. + * + * <p> + * The algorithm is in the public domain and is in wide use today. It is + * specified in ISO/IEC 7812-1. It is not intended to be a cryptographically + * secure hash function; it was designed to protect against accidental errors, + * not malicious attacks. Most credit cards and many government identification + * numbers use the algorithm as a simple method of distinguishing valid numbers + * from mistyped or otherwise incorrect numbers.</p> + * + * <p> + * The Luhn algorithm will detect any single-digit error, as well as almost all + * transpositions of adjacent digits. It will not, however, detect transposition + * of the two-digit sequence 09 to 90 (or vice versa). It will detect most of + * the possible twin errors (it will not detect 22 ↔ 55, 33 ↔ 66 or 44 ↔ + * 77).</p> + * + * <p> + * The check digit is computed as follows:</p> + * <ol> + * <li>Take the original number and starting from the rightmost digit moving + * left, double the value of every second digit (including the rightmost + * digit).</li> + * <li>Replace the resulting value at each position with the sum of the digits + * of this position's value or just subtract 9 from all numbers more or equal + * then 10.</li> + * <li>Sum up the resulting values from all positions (s).</li> + * <li>The calculated check digit is equal to {@code 10 - s % 10}.</li> + * </ol> + * + * @see <a href="https://en.wikipedia.org/wiki/Luhn_algorithm">Wiki</a> + */ +public final class Luhn { + private Luhn() { + } + + /** + * Check input digits array by Luhn algorithm. Initial array doesn't change + * while processing. + * + * @param digits array of digits from 0 to 9 + * @return true if check was successful, false otherwise + */ + public static boolean luhnCheck(int[] digits) { + int[] numbers = Arrays.copyOf(digits, digits.length); + int sum = 0; + + for (int i = numbers.length - 1; i >= 0; i--) { + if (i % 2 == 0) { + int temp = numbers[i] * 2; + if (temp > 9) { + temp = temp - 9; + } + numbers[i] = temp; + } + sum += numbers[i]; + } + + return sum % 10 == 0; + } + + public static void main(String[] args) { + System.out.println("Luhn algorithm usage examples:"); + int[] validInput = {4, 5, 6, 1, 2, 6, 1, 2, 1, 2, 3, 4, 5, 4, 6, 7}; + int[] invalidInput = {4, 5, 6, 1, 2, 6, 1, 2, 1, 2, 3, 4, 5, 4, 6, 4}; // typo in last + // symbol + checkAndPrint(validInput); + checkAndPrint(invalidInput); + + System.out.println("\nBusiness examples:"); + String validCardNumber = "5265 9251 6151 1412"; + String invalidCardNumber = "4929 3231 3088 1896"; + String illegalCardNumber = "4F15 BC06 3A88 76D5"; + businessExample(validCardNumber); + businessExample(invalidCardNumber); + businessExample(illegalCardNumber); + } + + private static void checkAndPrint(int[] input) { + String validationResult = Luhn.luhnCheck(input) ? "valid" : "not valid"; + System.out.println("Input " + Arrays.toString(input) + " is " + validationResult); + } + + /* + ======================== + Business usage example + ======================== + */ + /** + * Object representation of credit card. + */ + private record CreditCard(int[] digits) { + private static final int DIGITS_COUNT = 16; + + /** + * @param cardNumber string representation of credit card number - 16 + * digits. Can have spaces for digits separation + * @return credit card object + * @throws IllegalArgumentException if input string is not 16 digits or + * if Luhn check was failed + */ + public static CreditCard fromString(String cardNumber) { + Objects.requireNonNull(cardNumber); + String trimmedCardNumber = cardNumber.replaceAll(" ", ""); + if (trimmedCardNumber.length() != DIGITS_COUNT || !trimmedCardNumber.matches("\\d+")) { + throw new IllegalArgumentException("{" + cardNumber + "} - is not a card number"); + } + + int[] cardNumbers = toIntArray(trimmedCardNumber); + boolean isValid = luhnCheck(cardNumbers); + if (!isValid) { + throw new IllegalArgumentException("Credit card number {" + cardNumber + "} - have a typo"); + } + + return new CreditCard(cardNumbers); + } + + /** + * @return string representation separated by space every 4 digits. + * Example: "5265 9251 6151 1412" + */ + public String number() { + StringBuilder result = new StringBuilder(); + for (int i = 0; i < DIGITS_COUNT; i++) { + if (i % 4 == 0 && i != 0) { + result.append(" "); + } + result.append(digits[i]); + } + return result.toString(); + } + + @Override + public String toString() { + return String.format("%s {%s}", CreditCard.class.getSimpleName(), number()); + } + + private static int[] toIntArray(String string) { + return string.chars().map(i -> Character.digit(i, 10)).toArray(); + } + } + + private static void businessExample(String cardNumber) { + try { + System.out.println("Trying to create CreditCard object from valid card number: " + cardNumber); + CreditCard creditCard = CreditCard.fromString(cardNumber); + System.out.println("And business object is successfully created: " + creditCard + "\n"); + } catch (IllegalArgumentException e) { + System.out.println("And fail with exception message: " + e.getMessage() + "\n"); + } + } +} diff --git a/src/main/java/com/thealgorithms/others/Mandelbrot.java b/src/main/java/com/thealgorithms/others/Mandelbrot.java new file mode 100644 index 000000000000..6d7588090ba8 --- /dev/null +++ b/src/main/java/com/thealgorithms/others/Mandelbrot.java @@ -0,0 +1,188 @@ +package com.thealgorithms.others; + +import java.awt.Color; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import javax.imageio.ImageIO; + +/** + * The Mandelbrot set is the set of complex numbers "c" for which the series + * "z_(n+1) = z_n * z_n + c" does not diverge, i.e. remains bounded. Thus, a + * complex number "c" is a member of the Mandelbrot set if, when starting with + * "z_0 = 0" and applying the iteration repeatedly, the absolute value of "z_n" + * remains bounded for all "n > 0". Complex numbers can be written as "a + b*i": + * "a" is the real component, usually drawn on the x-axis, and "b*i" is the + * imaginary component, usually drawn on the y-axis. Most visualizations of the + * Mandelbrot set use a color-coding to indicate after how many steps in the + * series the numbers outside the set cross the divergence threshold. Images of + * the Mandelbrot set exhibit an elaborate and infinitely complicated boundary + * that reveals progressively ever-finer recursive detail at increasing + * magnifications, making the boundary of the Mandelbrot set a fractal curve. + * (description adapted from https://en.wikipedia.org/wiki/Mandelbrot_set ) (see + * also https://en.wikipedia.org/wiki/Plotting_algorithms_for_the_Mandelbrot_set + * ) + */ +public final class Mandelbrot { + private Mandelbrot() { + } + + public static void main(String[] args) { + // Test black and white + BufferedImage blackAndWhiteImage = getImage(800, 600, -0.6, 0, 3.2, 50, false); + + // Pixel outside the Mandelbrot set should be white. + assert blackAndWhiteImage.getRGB(0, 0) == new Color(255, 255, 255).getRGB(); + + // Pixel inside the Mandelbrot set should be black. + assert blackAndWhiteImage.getRGB(400, 300) == new Color(0, 0, 0).getRGB(); + + // Test color-coding + BufferedImage coloredImage = getImage(800, 600, -0.6, 0, 3.2, 50, true); + + // Pixel distant to the Mandelbrot set should be red. + assert coloredImage.getRGB(0, 0) == new Color(255, 0, 0).getRGB(); + + // Pixel inside the Mandelbrot set should be black. + assert coloredImage.getRGB(400, 300) == new Color(0, 0, 0).getRGB(); + + // Save image + try { + ImageIO.write(coloredImage, "png", new File("Mandelbrot.png")); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Method to generate the image of the Mandelbrot set. Two types of + * coordinates are used: image-coordinates that refer to the pixels and + * figure-coordinates that refer to the complex numbers inside and outside + * the Mandelbrot set. The figure-coordinates in the arguments of this + * method determine which section of the Mandelbrot set is viewed. The main + * area of the Mandelbrot set is roughly between "-1.5 < x < 0.5" and "-1 < + * y < 1" in the figure-coordinates. + * + * @param imageWidth The width of the rendered image. + * @param imageHeight The height of the rendered image. + * @param figureCenterX The x-coordinate of the center of the figure. + * @param figureCenterY The y-coordinate of the center of the figure. + * @param figureWidth The width of the figure. + * @param maxStep Maximum number of steps to check for divergent behavior. + * @param useDistanceColorCoding Render in color or black and white. + * @return The image of the rendered Mandelbrot set. + */ + public static BufferedImage getImage(int imageWidth, int imageHeight, double figureCenterX, double figureCenterY, double figureWidth, int maxStep, boolean useDistanceColorCoding) { + if (imageWidth <= 0) { + throw new IllegalArgumentException("imageWidth should be greater than zero"); + } + + if (imageHeight <= 0) { + throw new IllegalArgumentException("imageHeight should be greater than zero"); + } + + if (maxStep <= 0) { + throw new IllegalArgumentException("maxStep should be greater than zero"); + } + + BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB); + double figureHeight = figureWidth / imageWidth * imageHeight; + + // loop through the image-coordinates + for (int imageX = 0; imageX < imageWidth; imageX++) { + for (int imageY = 0; imageY < imageHeight; imageY++) { + // determine the figure-coordinates based on the image-coordinates + double figureX = figureCenterX + ((double) imageX / imageWidth - 0.5) * figureWidth; + double figureY = figureCenterY + ((double) imageY / imageHeight - 0.5) * figureHeight; + + double distance = getDistance(figureX, figureY, maxStep); + + // color the corresponding pixel based on the selected coloring-function + image.setRGB(imageX, imageY, useDistanceColorCoding ? colorCodedColorMap(distance).getRGB() : blackAndWhiteColorMap(distance).getRGB()); + } + } + + return image; + } + + /** + * Black and white color-coding that ignores the relative distance. The + * Mandelbrot set is black, everything else is white. + * + * @param distance Distance until divergence threshold + * @return The color corresponding to the distance. + */ + private static Color blackAndWhiteColorMap(double distance) { + return distance >= 1 ? new Color(0, 0, 0) : new Color(255, 255, 255); + } + + /** + * Color-coding taking the relative distance into account. The Mandelbrot + * set is black. + * + * @param distance Distance until divergence threshold. + * @return The color corresponding to the distance. + */ + private static Color colorCodedColorMap(double distance) { + if (distance >= 1) { + return new Color(0, 0, 0); + } else { + // simplified transformation of HSV to RGB + // distance determines hue + double hue = 360 * distance; + double saturation = 1; + double val = 255; + int hi = (int) (Math.floor(hue / 60)) % 6; + double f = hue / 60 - Math.floor(hue / 60); + + int v = (int) val; + int p = 0; + int q = (int) (val * (1 - f * saturation)); + int t = (int) (val * (1 - (1 - f) * saturation)); + + switch (hi) { + case 0: + return new Color(v, t, p); + case 1: + return new Color(q, v, p); + case 2: + return new Color(p, v, t); + case 3: + return new Color(p, q, v); + case 4: + return new Color(t, p, v); + default: + return new Color(v, p, q); + } + } + } + + /** + * Return the relative distance (ratio of steps taken to maxStep) after + * which the complex number constituted by this x-y-pair diverges. Members + * of the Mandelbrot set do not diverge so their distance is 1. + * + * @param figureX The x-coordinate within the figure. + * @param figureX The y-coordinate within the figure. + * @param maxStep Maximum number of steps to check for divergent behavior. + * @return The relative distance as the ratio of steps taken to maxStep. + */ + private static double getDistance(double figureX, double figureY, int maxStep) { + double a = figureX; + double b = figureY; + int currentStep = 0; + for (int step = 0; step < maxStep; step++) { + currentStep = step; + double aNew = a * a - b * b + figureX; + b = 2 * a * b + figureY; + a = aNew; + + // divergence happens for all complex number with an absolute value + // greater than 4 (= divergence threshold) + if (a * a + b * b > 4) { + break; + } + } + return (double) currentStep / (maxStep - 1); + } +} diff --git a/src/main/java/com/thealgorithms/others/MaximumSlidingWindow.java b/src/main/java/com/thealgorithms/others/MaximumSlidingWindow.java new file mode 100644 index 000000000000..d0b2c2a0e56d --- /dev/null +++ b/src/main/java/com/thealgorithms/others/MaximumSlidingWindow.java @@ -0,0 +1,56 @@ +package com.thealgorithms.others; + +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * Maximum Sliding Window Algorithm + * + * This algorithm finds the maximum element in each sliding window of size k + * in a given array of integers. It uses a deque (double-ended queue) to + * efficiently keep track of potential maximum values in the current window. + * + * Time Complexity: O(n), where n is the number of elements in the input array + * Space Complexity: O(k), where k is the size of the sliding window + */ + +public class MaximumSlidingWindow { + + /** + * Finds the maximum values in each sliding window of size k. + * + * @param nums The input array of integers + * @param windowSize The size of the sliding window + * @return An array of integers representing the maximums in each window + */ + public int[] maxSlidingWindow(int[] nums, int windowSize) { + if (nums == null || nums.length == 0 || windowSize <= 0 || windowSize > nums.length) { + return new int[0]; // Handle edge cases + } + + int[] result = new int[nums.length - windowSize + 1]; + Deque<Integer> deque = new ArrayDeque<>(); + + for (int currentIndex = 0; currentIndex < nums.length; currentIndex++) { + + // Remove the first element if it's outside the current window + if (!deque.isEmpty() && deque.peekFirst() == currentIndex - windowSize) { + deque.pollFirst(); + } + + // Remove all elements smaller than the current element from the end + while (!deque.isEmpty() && nums[deque.peekLast()] < nums[currentIndex]) { + deque.pollLast(); + } + + // Add the current element's index to the deque + deque.offerLast(currentIndex); + + // If we have processed at least k elements, add to result + if (currentIndex >= windowSize - 1) { + result[currentIndex - windowSize + 1] = nums[deque.peekFirst()]; + } + } + return result; + } +} diff --git a/src/main/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthK.java b/src/main/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthK.java new file mode 100644 index 000000000000..c05f1af4e327 --- /dev/null +++ b/src/main/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthK.java @@ -0,0 +1,69 @@ +package com.thealgorithms.others; + +import java.util.HashSet; +import java.util.Set; + +/** + * References: https://en.wikipedia.org/wiki/Streaming_algorithm + * + * This model involves computing the maximum sum of subarrays of a fixed size \( K \) from a stream of integers. + * As the stream progresses, elements from the end of the window are removed, and new elements from the stream are added. + * + * @author Swarga-codes (https://github.com/Swarga-codes) + */ +public final class MaximumSumOfDistinctSubarraysWithLengthK { + private MaximumSumOfDistinctSubarraysWithLengthK() { + } + + /** + * Finds the maximum sum of a subarray of size K consisting of distinct elements. + * + * @param k The size of the subarray. + * @param nums The array from which subarrays will be considered. + * + * @return The maximum sum of any distinct-element subarray of size K. If no such subarray exists, returns 0. + */ + public static long maximumSubarraySum(int k, int... nums) { + if (nums.length < k) { + return 0; + } + long masSum = 0; // Variable to store the maximum sum of distinct subarrays + long currentSum = 0; // Variable to store the sum of the current subarray + Set<Integer> currentSet = new HashSet<>(); // Set to track distinct elements in the current subarray + + // Initialize the first window + for (int i = 0; i < k; i++) { + currentSum += nums[i]; + currentSet.add(nums[i]); + } + // If the first window contains distinct elements, update maxSum + if (currentSet.size() == k) { + masSum = currentSum; + } + // Slide the window across the array + for (int i = 1; i < nums.length - k + 1; i++) { + // Update the sum by removing the element that is sliding out and adding the new element + currentSum = currentSum - nums[i - 1]; + currentSum = currentSum + nums[i + k - 1]; + int j = i; + boolean flag = false; // flag value which says that the subarray contains distinct elements + while (j < i + k && currentSet.size() < k) { + if (nums[i - 1] == nums[j]) { + flag = true; + break; + } else { + j++; + } + } + if (!flag) { + currentSet.remove(nums[i - 1]); + } + currentSet.add(nums[i + k - 1]); + // If the current window has distinct elements, compare and possibly update maxSum + if (currentSet.size() == k && masSum < currentSum) { + masSum = currentSum; + } + } + return masSum; // the final maximum sum + } +} diff --git a/src/main/java/com/thealgorithms/others/MemoryManagementAlgorithms.java b/src/main/java/com/thealgorithms/others/MemoryManagementAlgorithms.java new file mode 100644 index 000000000000..0924b8569942 --- /dev/null +++ b/src/main/java/com/thealgorithms/others/MemoryManagementAlgorithms.java @@ -0,0 +1,282 @@ +package com.thealgorithms.others; + +import java.util.ArrayList; +/** + * @author Alexandros Lemonaris + */ +public abstract class MemoryManagementAlgorithms { + + /** + * Method to allocate memory to blocks according to CPU algorithms. + * Use of inheritance to avoid repeated code. + * Abstract method since it is implemented different for each algorithm. + * It should return an ArrayList of Integers, where the index is the process + * ID (zero-indexed) and the value is the block number (also zero-indexed). + * @param sizeOfBlocks an int array that contains the sizes of the memory + * blocks available. + * @param sizeOfProcesses: an int array that contains the sizes of the + * processes we need memory blocks for. + * @return the ArrayList filled with Integers repressenting the memory + * allocation that took place. + */ + public abstract ArrayList<Integer> fitProcess(int[] sizeOfBlocks, int[] sizeOfProcesses); + + /** + * A constant value used to indicate that an allocation has not been made. + * This value is used as a sentinel value to represent that no allocation has been made + * when allocating space in an array or other data structure. + * The value is -255 and is marked as protected and final to ensure that it cannot be modified + * from outside the class and that its value remains consistent throughout the program + * execution. + * + * @author: Ishan Makadia (github.com/intrepid-ishan) + * @version: April 06, 2023 + */ + protected static final int NO_ALLOCATION = -255; +} + +/** + * @author Dekas Dimitrios + */ +class BestFitCPU extends MemoryManagementAlgorithms { + + /** + * Method to find the maximum valued element of an array filled with + * positive integers. + * + * @param array: an array filled with positive integers. + * @return the maximum valued element of the array. + */ + private static int findMaxElement(int[] array) { + int max = -1; + for (int value : array) { + if (value > max) { + max = value; + } + } + return max; + } + + /** + * Method to find the index of the memory block that is going to fit the + * given process based on the best fit algorithm. + * + * @param blocks: the array with the available memory blocks. + * @param process: the size of the process. + * @return the index of the block that fits, or -255 if no such block + * exists. + */ + private static int findBestFit(int[] blockSizes, int processSize) { + // Initialize minDiff with an unreachable value by a difference between a blockSize and the + // processSize. + int minDiff = findMaxElement(blockSizes); + int index = NO_ALLOCATION; // If there is no block that can fit the process, return + // NO_ALLOCATION as the + // result. + for (int i = 0; i < blockSizes.length; i++) { // Find the most fitting memory block for the given process. + if (blockSizes[i] - processSize < minDiff && blockSizes[i] - processSize >= 0) { + minDiff = blockSizes[i] - processSize; + index = i; + } + } + return index; + } + + /** + * Method to allocate memory to blocks according to the best fit algorithm. + * It should return an ArrayList of Integers, where the index is the process + * ID (zero-indexed) and the value is the block number (also zero-indexed). + * + * @param sizeOfBlocks: an int array that contains the sizes of the memory + * blocks available. + * @param sizeOfProcesses: an int array that contains the sizes of the + * processes we need memory blocks for. + * @return the ArrayList filled with Integers repressenting the memory + * allocation that took place. + */ + public ArrayList<Integer> fitProcess(int[] sizeOfBlocks, int[] sizeOfProcesses) { + // The array list responsible for saving the memory allocations done by the best-fit + // algorithm + ArrayList<Integer> memAlloc = new ArrayList<>(); + // Do this for every process + for (int processSize : sizeOfProcesses) { + int chosenBlockIdx = findBestFit(sizeOfBlocks, processSize); // Find the index of the memory block going to be used + memAlloc.add(chosenBlockIdx); // Store the chosen block index in the memAlloc array list + if (chosenBlockIdx != NO_ALLOCATION) { // Only if a block was chosen to store the process in it, + sizeOfBlocks[chosenBlockIdx] -= processSize; // resize the block based on the process size + } + } + return memAlloc; + } +} + +/** + * @author Dekas Dimitrios + */ +class WorstFitCPU extends MemoryManagementAlgorithms { + + /** + * Method to find the index of the memory block that is going to fit the + * given process based on the worst fit algorithm. + * + * @param blocks: the array with the available memory blocks. + * @param process: the size of the process. + * @return the index of the block that fits, or -255 if no such block + * exists. + */ + private static int findWorstFit(int[] blockSizes, int processSize) { + int max = -1; + int index = -1; + for (int i = 0; i < blockSizes.length; i++) { // Find the index of the biggest memory block available. + if (blockSizes[i] > max) { + max = blockSizes[i]; + index = i; + } + } + // If the biggest memory block cannot fit the process, return -255 as the result + if (processSize > blockSizes[index]) { + return NO_ALLOCATION; + } + return index; + } + + /** + * Method to allocate memory to blocks according to the worst fit algorithm. + * It should return an ArrayList of Integers, where the index is the process + * ID (zero-indexed) and the value is the block number (also zero-indexed). + * + * @param sizeOfBlocks: an int array that contains the sizes of the memory + * blocks available. + * @param sizeOfProcesses: an int array that contains the sizes of the + * processes we need memory blocks for. + * @return the ArrayList filled with Integers repressenting the memory + * allocation that took place. + */ + public ArrayList<Integer> fitProcess(int[] sizeOfBlocks, int[] sizeOfProcesses) { + // The array list responsible for saving the memory allocations done by the worst-fit + // algorithm + ArrayList<Integer> memAlloc = new ArrayList<>(); + // Do this for every process + for (int processSize : sizeOfProcesses) { + int chosenBlockIdx = findWorstFit(sizeOfBlocks, processSize); // Find the index of the memory block going to be used + memAlloc.add(chosenBlockIdx); // Store the chosen block index in the memAlloc array list + if (chosenBlockIdx != NO_ALLOCATION) { // Only if a block was chosen to store the process in it, + sizeOfBlocks[chosenBlockIdx] -= processSize; // resize the block based on the process size + } + } + return memAlloc; + } +} + +/** + * @author Dekas Dimitrios + */ +class FirstFitCPU extends MemoryManagementAlgorithms { + + /** + * Method to find the index of the memory block that is going to fit the + * given process based on the first fit algorithm. + * + * @param blocks: the array with the available memory blocks. + * @param process: the size of the process. + * @return the index of the block that fits, or -255 if no such block + * exists. + */ + private static int findFirstFit(int[] blockSizes, int processSize) { + for (int i = 0; i < blockSizes.length; i++) { + if (blockSizes[i] >= processSize) { + return i; + } + } + // If there is not a block that can fit the process, return -255 as the result + return NO_ALLOCATION; + } + + /** + * Method to allocate memory to blocks according to the first fit algorithm. + * It should return an ArrayList of Integers, where the index is the process + * ID (zero-indexed) and the value is the block number (also zero-indexed). + * + * @param sizeOfBlocks: an int array that contains the sizes of the memory + * blocks available. + * @param sizeOfProcesses: an int array that contains the sizes of the + * processes we need memory blocks for. + * @return the ArrayList filled with Integers repressenting the memory + * allocation that took place. + */ + public ArrayList<Integer> fitProcess(int[] sizeOfBlocks, int[] sizeOfProcesses) { + // The array list responsible for saving the memory allocations done by the first-fit + // algorithm + ArrayList<Integer> memAlloc = new ArrayList<>(); + // Do this for every process + for (int processSize : sizeOfProcesses) { + int chosenBlockIdx = findFirstFit(sizeOfBlocks, processSize); // Find the index of the memory block going to be used + memAlloc.add(chosenBlockIdx); // Store the chosen block index in the memAlloc array list + if (chosenBlockIdx != NO_ALLOCATION) { // Only if a block was chosen to store the process in it, + sizeOfBlocks[chosenBlockIdx] -= processSize; // resize the block based on the process size + } + } + return memAlloc; + } +} + +/** + * @author Alexandros Lemonaris + */ +class NextFit extends MemoryManagementAlgorithms { + + private int counter = 0; // variable that keeps the position of the last registration into the memory + + /** + * Method to find the index of the memory block that is going to fit the + * given process based on the next fit algorithm. In the case of next fit, + * if the search is interrupted in between, the new search is carried out from the last + * location. + * + * @param blocks: the array with the available memory blocks. + * @param process: the size of the process. + * @return the index of the block that fits, or -255 if no such block + * exists. + */ + private int findNextFit(int[] blockSizes, int processSize) { + for (int i = 0; i < blockSizes.length; i++) { + if (counter + i >= blockSizes.length) { + counter = -i; // starts from the start of the array + } + if (blockSizes[i + counter] >= processSize) { + counter += i; + return counter; + } + } + // If there is not a block that can fit the process, return -255 as the result + counter += blockSizes.length; // counter keeps its last value + return NO_ALLOCATION; + } + + /** + * Method to allocate memory to blocks according to the first fit algorithm. + * It should return an ArrayList of Integers, where the index is the process + * ID (zero-indexed) and the value is the block number (also zero-indexed). + * + * @param sizeOfBlocks: an int array that contains the sizes of the memory + * blocks available. + * @param sizeOfProcesses: an int array that contains the sizes of the + * processes we need memory blocks for. + * @return the ArrayList filled with Integers repressenting the memory + * allocation that took place. + */ + public ArrayList<Integer> fitProcess(int[] sizeOfBlocks, int[] sizeOfProcesses) { + // The array list responsible for saving the memory allocations done by the first-fit + // algorithm + ArrayList<Integer> memAlloc = new ArrayList<>(); + // Do this for every process + for (int processSize : sizeOfProcesses) { + int chosenBlockIdx = findNextFit(sizeOfBlocks, processSize); // Find the index of the memory block going to be used + memAlloc.add(chosenBlockIdx); // Store the chosen block index in the memAlloc array list + if (chosenBlockIdx != NO_ALLOCATION) { // Only if a block was chosen to store the process in it, + sizeOfBlocks[chosenBlockIdx] -= processSize; // resize the block based on the process size + } + } + return memAlloc; + } +} diff --git a/src/main/java/com/thealgorithms/others/MiniMaxAlgorithm.java b/src/main/java/com/thealgorithms/others/MiniMaxAlgorithm.java new file mode 100644 index 000000000000..cd2cd02ab908 --- /dev/null +++ b/src/main/java/com/thealgorithms/others/MiniMaxAlgorithm.java @@ -0,0 +1,128 @@ +package com.thealgorithms.others; + +import java.util.Arrays; +import java.util.Random; + +/** + * MiniMax is an algorithm used int artificial intelligence and game theory for + * minimizing the possible loss for the worst case scenario. + * + * See more (https://en.wikipedia.org/wiki/Minimax, + * https://www.geeksforgeeks.org/minimax-algorithm-in-game-theory-set-1-introduction/). + * + * @author aitofi (https://github.com/aitorfi) + */ +public class MiniMaxAlgorithm { + + /** + * Game tree represented as an int array containing scores. Each array + * element is a leaf node. + */ + private int[] scores; + private int height; + + /** + * Initializes the scores with 8 random leaf nodes + */ + public MiniMaxAlgorithm() { + scores = getRandomScores(3, 99); + height = log2(scores.length); + } + + public static void main(String[] args) { + MiniMaxAlgorithm miniMaxAlgorith = new MiniMaxAlgorithm(); + boolean isMaximizer = true; // Specifies the player that goes first. + boolean verbose = true; // True to show each players choices. + int bestScore; + + bestScore = miniMaxAlgorith.miniMax(0, isMaximizer, 0, verbose); + + if (verbose) { + System.out.println(); + } + + System.out.println(Arrays.toString(miniMaxAlgorith.getScores())); + System.out.println("The best score for " + (isMaximizer ? "Maximizer" : "Minimizer") + " is " + bestScore); + } + + /** + * Returns the optimal score assuming that both players play their best. + * + * @param depth Indicates how deep we are into the game tree. + * @param isMaximizer True if it is maximizers turn; otherwise false. + * @param index Index of the leaf node that is being evaluated. + * @param verbose True to show each players choices. + * @return The optimal score for the player that made the first move. + */ + public int miniMax(int depth, boolean isMaximizer, int index, boolean verbose) { + int bestScore; + int score1; + int score2; + + if (depth == height) { // Leaf node reached. + return scores[index]; + } + + score1 = miniMax(depth + 1, !isMaximizer, index * 2, verbose); + score2 = miniMax(depth + 1, !isMaximizer, (index * 2) + 1, verbose); + + if (isMaximizer) { + // Maximizer player wants to get the maximum possible score. + bestScore = Math.max(score1, score2); + } else { + // Minimizer player wants to get the minimum possible score. + bestScore = Math.min(score1, score2); + } + + // Leaf nodes can be sequentially inspected by + // recurssively multiplying (0 * 2) and ((0 * 2) + 1): + // (0 x 2) = 0; ((0 x 2) + 1) = 1 + // (1 x 2) = 2; ((1 x 2) + 1) = 3 + // (2 x 2) = 4; ((2 x 2) + 1) = 5 ... + if (verbose) { + System.out.printf("From %02d and %02d, %s chooses %02d%n", score1, score2, (isMaximizer ? "Maximizer" : "Minimizer"), bestScore); + } + + return bestScore; + } + + /** + * Returns an array of random numbers which lenght is a power of 2. + * + * @param size The power of 2 that will determine the lenght of the array. + * @param maxScore The maximum possible score. + * @return An array of random numbers. + */ + public static int[] getRandomScores(int size, int maxScore) { + int[] randomScores = new int[(int) Math.pow(2, size)]; + Random rand = new Random(); + + for (int i = 0; i < randomScores.length; i++) { + randomScores[i] = rand.nextInt(maxScore) + 1; + } + + return randomScores; + } + + // A utility function to find Log n in base 2 + private int log2(int n) { + return (n == 1) ? 0 : log2(n / 2) + 1; + } + + public void setScores(int[] scores) { + if (scores.length % 1 == 0) { + this.scores = scores; + height = log2(this.scores.length); + } else { + System.out.println("The number of scores must be a power of 2."); + } + } + + public int[] getScores() { + return scores; + } + + public int getHeight() { + return height; + } +} diff --git a/src/main/java/com/thealgorithms/others/PageRank.java b/src/main/java/com/thealgorithms/others/PageRank.java new file mode 100644 index 000000000000..c7be7a9882bc --- /dev/null +++ b/src/main/java/com/thealgorithms/others/PageRank.java @@ -0,0 +1,95 @@ +package com.thealgorithms.others; + +import java.util.Scanner; + +class PageRank { + + public static void main(String[] args) { + int nodes; + int i; + int j; + Scanner in = new Scanner(System.in); + System.out.print("Enter the Number of WebPages: "); + nodes = in.nextInt(); + PageRank p = new PageRank(); + System.out.println("Enter the Adjacency Matrix with 1->PATH & 0->NO PATH Between two WebPages: "); + for (i = 1; i <= nodes; i++) { + for (j = 1; j <= nodes; j++) { + p.path[i][j] = in.nextInt(); + if (j == i) { + p.path[i][j] = 0; + } + } + } + p.calc(nodes); + } + + public int[][] path = new int[10][10]; + public double[] pagerank = new double[10]; + + public void calc(double totalNodes) { + double initialPageRank; + double outgoingLinks = 0; + double dampingFactor = 0.85; + double[] tempPageRank = new double[10]; + int externalNodeNumber; + int internalNodeNumber; + int k = 1; // For Traversing + int iterationStep = 1; + initialPageRank = 1 / totalNodes; + System.out.printf(" Total Number of Nodes :" + totalNodes + "\t Initial PageRank of All Nodes :" + initialPageRank + "\n"); + + // 0th ITERATION _ OR _ INITIALIZATION PHASE // + for (k = 1; k <= totalNodes; k++) { + this.pagerank[k] = initialPageRank; + } + System.out.print("\n Initial PageRank Values , 0th Step \n"); + + for (k = 1; k <= totalNodes; k++) { + System.out.printf(" Page Rank of " + k + " is :\t" + this.pagerank[k] + "\n"); + } + + while (iterationStep <= 2) { // Iterations + // Store the PageRank for All Nodes in Temporary Array + for (k = 1; k <= totalNodes; k++) { + tempPageRank[k] = this.pagerank[k]; + this.pagerank[k] = 0; + } + + for (internalNodeNumber = 1; internalNodeNumber <= totalNodes; internalNodeNumber++) { + for (externalNodeNumber = 1; externalNodeNumber <= totalNodes; externalNodeNumber++) { + if (this.path[externalNodeNumber][internalNodeNumber] == 1) { + k = 1; + outgoingLinks = 0; // Count the Number of Outgoing Links for each externalNodeNumber + while (k <= totalNodes) { + if (this.path[externalNodeNumber][k] == 1) { + outgoingLinks = outgoingLinks + 1; // Counter for Outgoing Links + } + k = k + 1; + } + // Calculate PageRank + this.pagerank[internalNodeNumber] += tempPageRank[externalNodeNumber] * (1 / outgoingLinks); + } + } + System.out.printf("\n After " + iterationStep + "th Step \n"); + + for (k = 1; k <= totalNodes; k++) { + System.out.printf(" Page Rank of " + k + " is :\t" + this.pagerank[k] + "\n"); + } + + iterationStep = iterationStep + 1; + } + + // Add the Damping Factor to PageRank + for (k = 1; k <= totalNodes; k++) { + this.pagerank[k] = (1 - dampingFactor) + dampingFactor * this.pagerank[k]; + } + + // Display PageRank + System.out.print("\n Final Page Rank : \n"); + for (k = 1; k <= totalNodes; k++) { + System.out.printf(" Page Rank of " + k + " is :\t" + this.pagerank[k] + "\n"); + } + } + } +} diff --git a/src/main/java/com/thealgorithms/others/PasswordGen.java b/src/main/java/com/thealgorithms/others/PasswordGen.java new file mode 100644 index 000000000000..da9f21bc918f --- /dev/null +++ b/src/main/java/com/thealgorithms/others/PasswordGen.java @@ -0,0 +1,55 @@ +package com.thealgorithms.others; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +/** + * Creates a random password from ASCII letters Given password length bounds + * + * @author AKS1996 + * @date 2017.10.25 + */ +final class PasswordGen { + private static final String UPPERCASE_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private static final String LOWERCASE_LETTERS = "abcdefghijklmnopqrstuvwxyz"; + private static final String DIGITS = "0123456789"; + private static final String SPECIAL_CHARACTERS = "!@#$%^&*(){}?"; + private static final String ALL_CHARACTERS = UPPERCASE_LETTERS + LOWERCASE_LETTERS + DIGITS + SPECIAL_CHARACTERS; + + private PasswordGen() { + } + + /** + * Generates a random password with a length between minLength and maxLength. + * + * @param minLength The minimum length of the password. + * @param maxLength The maximum length of the password. + * @return A randomly generated password. + * @throws IllegalArgumentException if minLength is greater than maxLength or if either is non-positive. + */ + public static String generatePassword(int minLength, int maxLength) { + if (minLength > maxLength || minLength <= 0 || maxLength <= 0) { + throw new IllegalArgumentException("Incorrect length parameters: minLength must be <= maxLength and both must be > 0"); + } + + Random random = new Random(); + + List<Character> letters = new ArrayList<>(); + for (char c : ALL_CHARACTERS.toCharArray()) { + letters.add(c); + } + + // Inbuilt method to randomly shuffle a elements of a list + Collections.shuffle(letters); + StringBuilder password = new StringBuilder(); + + // Note that size of the password is also random + for (int i = random.nextInt(maxLength - minLength) + minLength; i > 0; --i) { + password.append(ALL_CHARACTERS.charAt(random.nextInt(ALL_CHARACTERS.length()))); + } + + return password.toString(); + } +} diff --git a/Others/PerlinNoise.java b/src/main/java/com/thealgorithms/others/PerlinNoise.java similarity index 75% rename from Others/PerlinNoise.java rename to src/main/java/com/thealgorithms/others/PerlinNoise.java index dbd0a86e4512..e6551ed6b9ee 100644 --- a/Others/PerlinNoise.java +++ b/src/main/java/com/thealgorithms/others/PerlinNoise.java @@ -1,18 +1,22 @@ -package Others; +package com.thealgorithms.others; import java.util.Random; import java.util.Scanner; /** - * For detailed info and implementation see: <a href="http://devmag.org.za/2009/04/25/perlin-noise/">Perlin-Noise</a> + * For detailed info and implementation see: <a + * href="http://devmag.org.za/2009/04/25/perlin-noise/">Perlin-Noise</a> */ -public class PerlinNoise { +public final class PerlinNoise { + private PerlinNoise() { + } + /** - * @param width width of noise array - * @param height height of noise array + * @param width width of noise array + * @param height height of noise array * @param octaveCount numbers of layers used for blending noise * @param persistence value of impact each layer get while blending - * @param seed used for randomizer + * @param seed used for randomizer * @return float array containing calculated "Perlin-Noise" values */ static float[][] generatePerlinNoise(int width, int height, int octaveCount, float persistence, long seed) { @@ -21,14 +25,14 @@ static float[][] generatePerlinNoise(int width, int height, int octaveCount, flo final float[][][] noiseLayers = new float[octaveCount][][]; Random random = new Random(seed); - //fill base array with random values as base for noise + // fill base array with random values as base for noise for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { base[x][y] = random.nextFloat(); } } - //calculate octaves with different roughness + // calculate octaves with different roughness for (int octave = 0; octave < octaveCount; octave++) { noiseLayers[octave] = generatePerlinNoiseLayer(base, width, height, octave); } @@ -36,21 +40,21 @@ static float[][] generatePerlinNoise(int width, int height, int octaveCount, flo float amplitude = 1f; float totalAmplitude = 0f; - //calculate perlin noise by blending each layer together with specific persistence + // calculate perlin noise by blending each layer together with specific persistence for (int octave = octaveCount - 1; octave >= 0; octave--) { amplitude *= persistence; totalAmplitude += amplitude; for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { - //adding each value of the noise layer to the noise - //by increasing amplitude the rougher noises will have more impact + // adding each value of the noise layer to the noise + // by increasing amplitude the rougher noises will have more impact perlinNoise[x][y] += noiseLayers[octave][x][y] * amplitude; } } } - //normalize values so that they stay between 0..1 + // normalize values so that they stay between 0..1 for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { perlinNoise[x][y] /= totalAmplitude; @@ -61,8 +65,8 @@ static float[][] generatePerlinNoise(int width, int height, int octaveCount, flo } /** - * @param base base random float array - * @param width width of noise array + * @param base base random float array + * @param width width of noise array * @param height height of noise array * @param octave current layer * @return float array containing calculated "Perlin-Noise-Layer" values @@ -70,29 +74,29 @@ static float[][] generatePerlinNoise(int width, int height, int octaveCount, flo static float[][] generatePerlinNoiseLayer(float[][] base, int width, int height, int octave) { float[][] perlinNoiseLayer = new float[width][height]; - //calculate period (wavelength) for different shapes - int period = 1 << octave; //2^k + // calculate period (wavelength) for different shapes + int period = 1 << octave; // 2^k float frequency = 1f / period; // 1/2^k for (int x = 0; x < width; x++) { - //calculates the horizontal sampling indices + // calculates the horizontal sampling indices int x0 = (x / period) * period; int x1 = (x0 + period) % width; float horizintalBlend = (x - x0) * frequency; for (int y = 0; y < height; y++) { - //calculates the vertical sampling indices + // calculates the vertical sampling indices int y0 = (y / period) * period; int y1 = (y0 + period) % height; float verticalBlend = (y - y0) * frequency; - //blend top corners + // blend top corners float top = interpolate(base[x0][y0], base[x1][y0], horizintalBlend); - //blend bottom corners + // blend bottom corners float bottom = interpolate(base[x0][y1], base[x1][y1], horizintalBlend); - //blend top and bottom interpolation to get the final blend value for this cell + // blend top and bottom interpolation to get the final blend value for this cell perlinNoiseLayer[x][y] = interpolate(top, bottom, verticalBlend); } } @@ -101,9 +105,10 @@ static float[][] generatePerlinNoiseLayer(float[][] base, int width, int height, } /** - * @param a value of point a - * @param b value of point b - * @param alpha determine which value has more impact (closer to 0 -> a, closer to 1 -> b) + * @param a value of point a + * @param b value of point b + * @param alpha determine which value has more impact (closer to 0 -> a, + * closer to 1 -> b) * @return interpolated value */ static float interpolate(float a, float b, float alpha) { @@ -139,12 +144,11 @@ public static void main(String[] args) { System.out.println("Charset (String): "); charset = in.next(); - perlinNoise = generatePerlinNoise(width, height, octaveCount, persistence, seed); final char[] chars = charset.toCharArray(); final int length = chars.length; final float step = 1f / length; - //output based on charset + // output based on charset for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { float value = step; @@ -162,5 +166,6 @@ public static void main(String[] args) { System.out.println(); } + in.close(); } } diff --git a/src/main/java/com/thealgorithms/others/PrintAMatrixInSpiralOrder.java b/src/main/java/com/thealgorithms/others/PrintAMatrixInSpiralOrder.java new file mode 100644 index 000000000000..abfdd006879e --- /dev/null +++ b/src/main/java/com/thealgorithms/others/PrintAMatrixInSpiralOrder.java @@ -0,0 +1,52 @@ +package com.thealgorithms.others; + +import java.util.ArrayList; +import java.util.List; + +public class PrintAMatrixInSpiralOrder { + /** + * Search a key in row and column wise sorted matrix + * + * @param matrix matrix to be searched + * @param row number of rows matrix has + * @param col number of columns matrix has + * @author Sadiul Hakim : https://github.com/sadiul-hakim + */ + public List<Integer> print(int[][] matrix, int row, int col) { + // r traverses matrix row wise from first + int r = 0; + // c traverses matrix column wise from first + int c = 0; + int i; + List<Integer> result = new ArrayList<>(); + while (r < row && c < col) { + // print first row of matrix + for (i = c; i < col; i++) { + result.add(matrix[r][i]); + } + // increase r by one because first row printed + r++; + // print last column + for (i = r; i < row; i++) { + result.add(matrix[i][col - 1]); + } + // decrease col by one because last column has been printed + col--; + // print rows from last except printed elements + if (r < row) { + for (i = col - 1; i >= c; i--) { + result.add(matrix[row - 1][i]); + } + row--; + } + // print columns from first except printed elements + if (c < col) { + for (i = row - 1; i >= r; i--) { + result.add(matrix[i][c]); + } + c++; + } + } + return result; + } +} diff --git a/src/main/java/com/thealgorithms/others/QueueUsingTwoStacks.java b/src/main/java/com/thealgorithms/others/QueueUsingTwoStacks.java new file mode 100644 index 000000000000..b43110d4d3ff --- /dev/null +++ b/src/main/java/com/thealgorithms/others/QueueUsingTwoStacks.java @@ -0,0 +1,105 @@ +package com.thealgorithms.others; + +import java.util.Stack; + +/** + * This implements Queue using two Stacks. + * + * <p> + * Big O Runtime: insert(): O(1) remove(): O(1) amortized isEmpty(): O(1) + * + * <p> + * A queue data structure functions the same as a real world queue. The elements + * that are added first are the first to be removed. New elements are added to + * the back/rear of the queue. + * + * @author sahilb2 (https://www.github.com/sahilb2) + */ +public class QueueUsingTwoStacks { + private final Stack<Object> inStack; + private final Stack<Object> outStack; + + /** + * Constructor + */ + public QueueUsingTwoStacks() { + this.inStack = new Stack<>(); + this.outStack = new Stack<>(); + } + + /** + * Inserts an element at the rear of the queue + * + * @param x element to be added + */ + public void insert(Object x) { + // Insert element into inStack + this.inStack.push(x); + } + + /** + * Remove an element from the front of the queue + * + * @return the new front of the queue + */ + public Object remove() { + if (this.outStack.isEmpty()) { + // Move all elements from inStack to outStack (preserving the order) + while (!this.inStack.isEmpty()) { + this.outStack.push(this.inStack.pop()); + } + } + return this.outStack.pop(); + } + + /** + * Peek at the element from the front of the queue + * + * @return the front element of the queue + */ + public Object peekFront() { + if (this.outStack.isEmpty()) { + // Move all elements from inStack to outStack (preserving the order) + while (!this.inStack.isEmpty()) { + this.outStack.push(this.inStack.pop()); + } + } + return this.outStack.peek(); + } + + /** + * Peek at the element from the back of the queue + * + * @return the back element of the queue + */ + public Object peekBack() { + return this.inStack.peek(); + } + + /** + * Returns true if the queue is empty + * + * @return true if the queue is empty + */ + public boolean isEmpty() { + return (this.inStack.isEmpty() && this.outStack.isEmpty()); + } + + /** + * Returns true if the inStack is empty. + * + * @return true if the inStack is empty. + */ + public boolean isInStackEmpty() { + return (inStack.isEmpty()); + } + + /** + * Returns true if the outStack is empty. + * + * @return true if the outStack is empty. + */ + public boolean isOutStackEmpty() { + return (outStack.isEmpty()); + } +} diff --git a/src/main/java/com/thealgorithms/others/RemoveDuplicateFromString.java b/src/main/java/com/thealgorithms/others/RemoveDuplicateFromString.java new file mode 100644 index 000000000000..5b6466f6fa07 --- /dev/null +++ b/src/main/java/com/thealgorithms/others/RemoveDuplicateFromString.java @@ -0,0 +1,30 @@ +package com.thealgorithms.others; + +/** + * @author Varun Upadhyay (https://github.com/varunu28) + */ +public final class RemoveDuplicateFromString { + private RemoveDuplicateFromString() { + } + + /** + * Removes duplicate characters from the given string. + * + * @param input The input string from which duplicate characters need to be removed. + * @return A string containing only unique characters from the input, in their original order. + */ + public static String removeDuplicate(String input) { + if (input == null || input.isEmpty()) { + return input; + } + + StringBuilder uniqueChars = new StringBuilder(); + for (char c : input.toCharArray()) { + if (uniqueChars.indexOf(String.valueOf(c)) == -1) { + uniqueChars.append(c); // Append character if it's not already in the StringBuilder + } + } + + return uniqueChars.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/others/ReverseStackUsingRecursion.java b/src/main/java/com/thealgorithms/others/ReverseStackUsingRecursion.java new file mode 100644 index 000000000000..de36673512a0 --- /dev/null +++ b/src/main/java/com/thealgorithms/others/ReverseStackUsingRecursion.java @@ -0,0 +1,44 @@ +package com.thealgorithms.others; + +import java.util.Stack; + +/** + * Class that provides methods to reverse a stack using recursion. + */ +public final class ReverseStackUsingRecursion { + private ReverseStackUsingRecursion() { + } + + /** + * Reverses the elements of the given stack using recursion. + * + * @param stack the stack to be reversed + * @throws IllegalArgumentException if the stack is null + */ + public static void reverse(Stack<Integer> stack) { + if (stack == null) { + throw new IllegalArgumentException("Stack cannot be null"); + } + if (!stack.isEmpty()) { + int topElement = stack.pop(); + reverse(stack); + insertAtBottom(stack, topElement); + } + } + + /** + * Inserts an element at the bottom of the given stack. + * + * @param stack the stack where the element will be inserted + * @param element the element to be inserted at the bottom + */ + private static void insertAtBottom(Stack<Integer> stack, int element) { + if (stack.isEmpty()) { + stack.push(element); + } else { + int topElement = stack.pop(); + insertAtBottom(stack, element); + stack.push(topElement); + } + } +} diff --git a/src/main/java/com/thealgorithms/others/SkylineProblem.java b/src/main/java/com/thealgorithms/others/SkylineProblem.java new file mode 100644 index 000000000000..e84a5c5b585b --- /dev/null +++ b/src/main/java/com/thealgorithms/others/SkylineProblem.java @@ -0,0 +1,153 @@ +package com.thealgorithms.others; + +import java.util.ArrayList; + +/** + * The {@code SkylineProblem} class is used to solve the skyline problem using a + * divide-and-conquer approach. + * It reads input for building data, processes it to find the skyline, and + * prints the skyline. + */ +public class SkylineProblem { + + Building[] building; + int count; + + /** + * Adds a building with the given left, height, and right values to the + * buildings list. + * + * @param left The left x-coordinate of the building. + * @param height The height of the building. + * @param right The right x-coordinate of the building. + */ + public void add(int left, int height, int right) { + building[count++] = new Building(left, height, right); + } + + /** + * Computes the skyline for a range of buildings using the divide-and-conquer + * strategy. + * + * @param start The starting index of the buildings to process. + * @param end The ending index of the buildings to process. + * @return A list of {@link Skyline} objects representing the computed skyline. + */ + public ArrayList<Skyline> findSkyline(int start, int end) { + // Base case: only one building, return its skyline. + if (start == end) { + ArrayList<Skyline> list = new ArrayList<>(); + list.add(new Skyline(building[start].left, building[start].height)); + list.add(new Skyline(building[end].right, 0)); // Add the end of the building + return list; + } + + int mid = (start + end) / 2; + + ArrayList<Skyline> sky1 = this.findSkyline(start, mid); // Find the skyline of the left half + ArrayList<Skyline> sky2 = this.findSkyline(mid + 1, end); // Find the skyline of the right half + return this.mergeSkyline(sky1, sky2); // Merge the two skylines + } + + /** + * Merges two skylines (sky1 and sky2) into one combined skyline. + * + * @param sky1 The first skyline list. + * @param sky2 The second skyline list. + * @return A list of {@link Skyline} objects representing the merged skyline. + */ + public ArrayList<Skyline> mergeSkyline(ArrayList<Skyline> sky1, ArrayList<Skyline> sky2) { + int currentH1 = 0; + int currentH2 = 0; + ArrayList<Skyline> skyline = new ArrayList<>(); + int maxH = 0; + + // Merge the two skylines + while (!sky1.isEmpty() && !sky2.isEmpty()) { + if (sky1.get(0).coordinates < sky2.get(0).coordinates) { + int currentX = sky1.get(0).coordinates; + currentH1 = sky1.get(0).height; + + if (currentH1 < currentH2) { + sky1.remove(0); + if (maxH != currentH2) { + skyline.add(new Skyline(currentX, currentH2)); + } + } else { + maxH = currentH1; + sky1.remove(0); + skyline.add(new Skyline(currentX, currentH1)); + } + } else { + int currentX = sky2.get(0).coordinates; + currentH2 = sky2.get(0).height; + + if (currentH2 < currentH1) { + sky2.remove(0); + if (maxH != currentH1) { + skyline.add(new Skyline(currentX, currentH1)); + } + } else { + maxH = currentH2; + sky2.remove(0); + skyline.add(new Skyline(currentX, currentH2)); + } + } + } + + // Add any remaining points from sky1 or sky2 + while (!sky1.isEmpty()) { + skyline.add(sky1.get(0)); + sky1.remove(0); + } + + while (!sky2.isEmpty()) { + skyline.add(sky2.get(0)); + sky2.remove(0); + } + + return skyline; + } + + /** + * A class representing a point in the skyline with its x-coordinate and height. + */ + public class Skyline { + public int coordinates; + public int height; + + /** + * Constructor for the {@code Skyline} class. + * + * @param coordinates The x-coordinate of the skyline point. + * @param height The height of the skyline at the given coordinate. + */ + public Skyline(int coordinates, int height) { + this.coordinates = coordinates; + this.height = height; + } + } + + /** + * A class representing a building with its left, height, and right + * x-coordinates. + */ + public class Building { + public int left; + public int height; + public int right; + + /** + * Constructor for the {@code Building} class. + * + * @param left The left x-coordinate of the building. + * @param height The height of the building. + * @param right The right x-coordinate of the building. + */ + public Building(int left, int height, int right) { + this.left = left; + this.height = height; + this.right = right; + } + } +} diff --git a/src/main/java/com/thealgorithms/others/TwoPointers.java b/src/main/java/com/thealgorithms/others/TwoPointers.java new file mode 100644 index 000000000000..c551408c38b9 --- /dev/null +++ b/src/main/java/com/thealgorithms/others/TwoPointers.java @@ -0,0 +1,38 @@ +package com.thealgorithms.others; + +/** + * The two-pointer technique is a useful tool to utilize when searching for + * pairs in a sorted array. + * + * <p> + * Link: https://www.geeksforgeeks.org/two-pointers-technique/ + */ +final class TwoPointers { + private TwoPointers() { + } + + /** + * Given a sorted array arr (sorted in ascending order), find if there exists + * any pair of elements such that their sum is equal to the key. + * + * @param arr the array containing elements (must be sorted in ascending order) + * @param key the number to search + * @return {@code true} if there exists a pair of elements, {@code false} otherwise. + */ + public static boolean isPairedSum(int[] arr, int key) { + int i = 0; // index of the first element + int j = arr.length - 1; // index of the last element + + while (i < j) { + int sum = arr[i] + arr[j]; + if (sum == key) { + return true; + } else if (sum < key) { + i++; + } else { + j--; + } + } + return false; + } +} diff --git a/src/main/java/com/thealgorithms/others/Verhoeff.java b/src/main/java/com/thealgorithms/others/Verhoeff.java new file mode 100644 index 000000000000..9088612aaa43 --- /dev/null +++ b/src/main/java/com/thealgorithms/others/Verhoeff.java @@ -0,0 +1,171 @@ +package com.thealgorithms.others; + +import java.util.Objects; + +/** + * The Verhoeff algorithm is a checksum formula for error detection developed by + * the Dutch mathematician Jacobus Verhoeff and was first published in 1969. It + * was the first decimal check digit algorithm which detects all single-digit + * errors, and all transposition errors involving two adjacent digits. + * + * <p> + * The strengths of the algorithm are that it detects all transliteration and + * transposition errors, and additionally most twin, twin jump, jump + * transposition and phonetic errors. The main weakness of the Verhoeff + * algorithm is its complexity. The calculations required cannot easily be + * expressed as a formula. For easy calculation three tables are required:</p> + * <ol> + * <li>multiplication table</li> + * <li>inverse table</li> + * <li>permutation table</li> + * </ol> + * + * @see <a href="https://en.wikipedia.org/wiki/Verhoeff_algorithm">Wiki. + * Verhoeff algorithm</a> + */ +public final class Verhoeff { + private Verhoeff() { + } + + /** + * Table {@code d}. Based on multiplication in the dihedral group D5 and is + * simply the Cayley table of the group. Note that this group is not + * commutative, that is, for some values of {@code j} and {@code k}, + * {@code d(j,k) ≠ d(k, j)}. + * + * @see <a href="https://en.wikipedia.org/wiki/Dihedral_group">Wiki. + * Dihedral group</a> + */ + private static final byte[][] MULTIPLICATION_TABLE = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + {1, 2, 3, 4, 0, 6, 7, 8, 9, 5}, + {2, 3, 4, 0, 1, 7, 8, 9, 5, 6}, + {3, 4, 0, 1, 2, 8, 9, 5, 6, 7}, + {4, 0, 1, 2, 3, 9, 5, 6, 7, 8}, + {5, 9, 8, 7, 6, 0, 4, 3, 2, 1}, + {6, 5, 9, 8, 7, 1, 0, 4, 3, 2}, + {7, 6, 5, 9, 8, 2, 1, 0, 4, 3}, + {8, 7, 6, 5, 9, 3, 2, 1, 0, 4}, + {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}, + }; + + /** + * The inverse table {@code inv}. Represents the multiplicative inverse of a + * digit, that is, the value that satisfies {@code d(j, inv(j)) = 0}. + */ + private static final byte[] MULTIPLICATIVE_INVERSE = { + 0, + 4, + 3, + 2, + 1, + 5, + 6, + 7, + 8, + 9, + }; + + /** + * The permutation table {@code p}. Applies a permutation to each digit + * based on its position in the number. This is actually a single + * permutation {@code (1 5 8 9 4 2 7 0)(3 6)} applied iteratively; i.e. + * {@code p(i+j,n) = p(i, p(j,n))}. + */ + private static final byte[][] PERMUTATION_TABLE = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + {1, 5, 7, 6, 2, 8, 3, 0, 9, 4}, + {5, 8, 0, 3, 7, 9, 6, 1, 4, 2}, + {8, 9, 1, 6, 0, 4, 3, 5, 2, 7}, + {9, 4, 5, 3, 1, 2, 6, 8, 7, 0}, + {4, 2, 8, 6, 5, 7, 3, 9, 0, 1}, + {2, 7, 9, 3, 8, 0, 6, 4, 1, 5}, + {7, 0, 4, 6, 9, 1, 3, 2, 5, 8}, + }; + + /** + * Check input digits by Verhoeff algorithm. + * + * @param digits input to check + * @return true if check was successful, false otherwise + * @throws IllegalArgumentException if input parameter contains not only + * digits + * @throws NullPointerException if input is null + */ + public static boolean verhoeffCheck(String digits) { + checkInput(digits); + int[] numbers = toIntArray(digits); + + // The Verhoeff algorithm + int checksum = 0; + for (int i = 0; i < numbers.length; i++) { + int index = numbers.length - i - 1; + byte b = PERMUTATION_TABLE[i % 8][numbers[index]]; + checksum = MULTIPLICATION_TABLE[checksum][b]; + } + + return checksum == 0; + } + + /** + * Calculate check digit for initial digits and add it tho the last + * position. + * + * @param initialDigits initial value + * @return digits with the checksum in the last position + * @throws IllegalArgumentException if input parameter contains not only + * digits + * @throws NullPointerException if input is null + */ + public static String addVerhoeffChecksum(String initialDigits) { + checkInput(initialDigits); + + // Add zero to end of input value + var modifiedDigits = initialDigits + "0"; + + int[] numbers = toIntArray(modifiedDigits); + + int checksum = 0; + for (int i = 0; i < numbers.length; i++) { + int index = numbers.length - i - 1; + byte b = PERMUTATION_TABLE[i % 8][numbers[index]]; + checksum = MULTIPLICATION_TABLE[checksum][b]; + } + checksum = MULTIPLICATIVE_INVERSE[checksum]; + + return initialDigits + checksum; + } + + public static void main(String[] args) { + System.out.println("Verhoeff algorithm usage examples:"); + var validInput = "2363"; + var invalidInput = "2364"; + checkAndPrint(validInput); + checkAndPrint(invalidInput); + + System.out.println("\nCheck digit generation example:"); + var input = "236"; + generateAndPrint(input); + } + + private static void checkAndPrint(String input) { + String validationResult = Verhoeff.verhoeffCheck(input) ? "valid" : "not valid"; + System.out.println("Input '" + input + "' is " + validationResult); + } + + private static void generateAndPrint(String input) { + String result = addVerhoeffChecksum(input); + System.out.println("Generate and add checksum to initial value '" + input + "'. Result: '" + result + "'"); + } + + private static void checkInput(String input) { + Objects.requireNonNull(input); + if (!input.matches("\\d+")) { + throw new IllegalArgumentException("Input '" + input + "' contains not only digits"); + } + } + + private static int[] toIntArray(String string) { + return string.chars().map(i -> Character.digit(i, 10)).toArray(); + } +} diff --git a/src/main/java/com/thealgorithms/others/cn/HammingDistance.java b/src/main/java/com/thealgorithms/others/cn/HammingDistance.java new file mode 100644 index 000000000000..c8239d53d606 --- /dev/null +++ b/src/main/java/com/thealgorithms/others/cn/HammingDistance.java @@ -0,0 +1,32 @@ +package com.thealgorithms.others.cn; + +public final class HammingDistance { + private HammingDistance() { + } + + private static void checkChar(char inChar) { + if (inChar != '0' && inChar != '1') { + throw new IllegalArgumentException("Input must be a binary string."); + } + } + + public static int compute(char charA, char charB) { + checkChar(charA); + checkChar(charB); + return charA == charB ? 0 : 1; + } + + public static int compute(String bitsStrA, String bitsStrB) { + if (bitsStrA.length() != bitsStrB.length()) { + throw new IllegalArgumentException("Input strings must have the same length."); + } + + int totalErrorBitCount = 0; + + for (int i = 0; i < bitsStrA.length(); i++) { + totalErrorBitCount += compute(bitsStrA.charAt(i), bitsStrB.charAt(i)); + } + + return totalErrorBitCount; + } +} diff --git a/src/main/java/com/thealgorithms/puzzlesandgames/Sudoku.java b/src/main/java/com/thealgorithms/puzzlesandgames/Sudoku.java new file mode 100644 index 000000000000..fce665c4de00 --- /dev/null +++ b/src/main/java/com/thealgorithms/puzzlesandgames/Sudoku.java @@ -0,0 +1,169 @@ +package com.thealgorithms.puzzlesandgames; + +/** + * A class that provides methods to solve Sudoku puzzles of any n x n size + * using a backtracking approach, where n must be a perfect square. + * The algorithm checks for safe number placements in rows, columns, + * and subgrids (which are sqrt(n) x sqrt(n) in size) and recursively solves the puzzle. + * Though commonly used for 9x9 grids, it is adaptable to other valid Sudoku dimensions. + */ +final class Sudoku { + + private Sudoku() { + } + + /** + * Checks if placing a number in a specific position on the Sudoku board is safe. + * The number is considered safe if it does not violate any of the Sudoku rules: + * - It should not be present in the same row. + * - It should not be present in the same column. + * - It should not be present in the corresponding 3x3 subgrid. + * - It should not be present in the corresponding subgrid, which is sqrt(n) x sqrt(n) in size (e.g., for a 9x9 grid, the subgrid will be 3x3). + * + * @param board The current state of the Sudoku board. + * @param row The row index where the number is to be placed. + * @param col The column index where the number is to be placed. + * @param num The number to be placed on the board. + * @return True if the placement is safe, otherwise false. + */ + public static boolean isSafe(int[][] board, int row, int col, int num) { + // Check the row for duplicates + for (int d = 0; d < board.length; d++) { + if (board[row][d] == num) { + return false; + } + } + + // Check the column for duplicates + for (int r = 0; r < board.length; r++) { + if (board[r][col] == num) { + return false; + } + } + + // Check the corresponding 3x3 subgrid for duplicates + int sqrt = (int) Math.sqrt(board.length); + int boxRowStart = row - row % sqrt; + int boxColStart = col - col % sqrt; + + for (int r = boxRowStart; r < boxRowStart + sqrt; r++) { + for (int d = boxColStart; d < boxColStart + sqrt; d++) { + if (board[r][d] == num) { + return false; + } + } + } + + return true; + } + + /** + * Solves the Sudoku puzzle using backtracking. + * The algorithm finds an empty cell and tries placing numbers + * from 1 to n, where n is the size of the board + * (for example, from 1 to 9 in a standard 9x9 Sudoku). + * The algorithm finds an empty cell and tries placing numbers from 1 to 9. + * The standard version of Sudoku uses numbers from 1 to 9, so the algorithm can be + * easily modified for other variations of the game. + * If a number placement is valid (checked via `isSafe`), the number is + * placed and the function recursively attempts to solve the rest of the puzzle. + * If no solution is possible, the number is removed (backtracked), + * and the process is repeated. + * + * @param board The current state of the Sudoku board. + * @param n The size of the Sudoku board (typically 9 for a standard puzzle). + * @return True if the Sudoku puzzle is solvable, false otherwise. + */ + public static boolean solveSudoku(int[][] board, int n) { + int row = -1; + int col = -1; + boolean isEmpty = true; + + // Find the next empty cell + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (board[i][j] == 0) { + row = i; + col = j; + isEmpty = false; + break; + } + } + if (!isEmpty) { + break; + } + } + + // No empty space left + if (isEmpty) { + return true; + } + + // Try placing numbers 1 to n in the empty cell (n should be a perfect square) + // Eg: n=9 for a standard 9x9 Sudoku puzzle, n=16 for a 16x16 puzzle, etc. + for (int num = 1; num <= n; num++) { + if (isSafe(board, row, col, num)) { + board[row][col] = num; + if (solveSudoku(board, n)) { + return true; + } else { + // replace it + board[row][col] = 0; + } + } + } + return false; + } + + /** + * Prints the current state of the Sudoku board in a readable format. + * Each row is printed on a new line, with numbers separated by spaces. + * + * @param board The current state of the Sudoku board. + * @param n The size of the Sudoku board (typically 9 for a standard puzzle). + */ + public static void print(int[][] board, int n) { + // Print the board in a nxn grid format + // if n=9, print the board in a 9x9 grid format + // if n=16, print the board in a 16x16 grid format + for (int r = 0; r < n; r++) { + for (int d = 0; d < n; d++) { + System.out.print(board[r][d]); + System.out.print(" "); + } + System.out.print("\n"); + + if ((r + 1) % (int) Math.sqrt(n) == 0) { + System.out.print(""); + } + } + } + + /** + * The driver method to demonstrate solving a Sudoku puzzle. + * A sample 9x9 Sudoku puzzle is provided, and the program attempts to solve it + * using the `solveSudoku` method. If a solution is found, it is printed to the console. + * + * @param args Command-line arguments (not used in this program). + */ + public static void main(String[] args) { + int[][] board = new int[][] { + {3, 0, 6, 5, 0, 8, 4, 0, 0}, + {5, 2, 0, 0, 0, 0, 0, 0, 0}, + {0, 8, 7, 0, 0, 0, 0, 3, 1}, + {0, 0, 3, 0, 1, 0, 0, 8, 0}, + {9, 0, 0, 8, 6, 3, 0, 0, 5}, + {0, 5, 0, 0, 9, 0, 6, 0, 0}, + {1, 3, 0, 0, 0, 0, 2, 5, 0}, + {0, 0, 0, 0, 0, 0, 0, 7, 4}, + {0, 0, 5, 2, 0, 6, 3, 0, 0}, + }; + int n = board.length; + + if (solveSudoku(board, n)) { + print(board, n); + } else { + System.out.println("No solution"); + } + } +} diff --git a/src/main/java/com/thealgorithms/puzzlesandgames/TowerOfHanoi.java b/src/main/java/com/thealgorithms/puzzlesandgames/TowerOfHanoi.java new file mode 100644 index 000000000000..72e9a14ac070 --- /dev/null +++ b/src/main/java/com/thealgorithms/puzzlesandgames/TowerOfHanoi.java @@ -0,0 +1,65 @@ +package com.thealgorithms.puzzlesandgames; + +import java.util.List; + +/** + * The {@code TowerOfHanoi} class provides a recursive solution to the Tower of Hanoi puzzle. + * This puzzle involves moving a set of discs from one pole to another, following specific rules: + * 1. Only one disc can be moved at a time. + * 2. A disc can only be placed on top of a larger disc. + * 3. All discs must start on one pole and end on another. + * + * This implementation recursively calculates the steps required to solve the puzzle and stores them + * in a provided list. + * + * <p> + * For more information about the Tower of Hanoi, see + * <a href="https://en.wikipedia.org/wiki/Tower_of_Hanoi">Tower of Hanoi on Wikipedia</a>. + * </p> + * + * The {@code shift} method takes the number of discs and the names of the poles, + * and appends the steps required to solve the puzzle to the provided list. + * Time Complexity: O(2^n) - Exponential time complexity due to the recursive nature of the problem. + * Space Complexity: O(n) - Linear space complexity due to the recursion stack. + * Wikipedia: https://en.wikipedia.org/wiki/Tower_of_Hanoi + */ +final class TowerOfHanoi { + + private TowerOfHanoi() { + } + + /** + * Recursively solve the Tower of Hanoi puzzle by moving discs between poles. + * + * @param n The number of discs to move. + * @param startPole The name of the start pole from which discs are moved. + * @param intermediatePole The name of the intermediate pole used as a temporary holding area. + * @param endPole The name of the end pole to which discs are moved. + * @param result A list to store the steps required to solve the puzzle. + * + * <p> + * This method is called recursively to move n-1 discs + * to the intermediate pole, + * then moves the nth disc to the end pole, and finally + * moves the n-1 discs from the + * intermediate pole to the end pole. + * </p> + * + * <p> + * Time Complexity: O(2^n) - Exponential time complexity due to the recursive nature of the problem. + * Space Complexity: O(n) - Linear space complexity due to the recursion stack. + * </p> + */ + public static void shift(int n, String startPole, String intermediatePole, String endPole, List<String> result) { + if (n != 0) { + // Move n-1 discs from startPole to intermediatePole + shift(n - 1, startPole, endPole, intermediatePole, result); + + // Add the move of the nth disc from startPole to endPole + result.add(String.format("Move %d from %s to %s", n, startPole, endPole)); + + // Move the n-1 discs from intermediatePole to endPole + shift(n - 1, intermediatePole, startPole, endPole, result); + } + } +} diff --git a/src/main/java/com/thealgorithms/puzzlesandgames/WordBoggle.java b/src/main/java/com/thealgorithms/puzzlesandgames/WordBoggle.java new file mode 100644 index 000000000000..ca1430f744ab --- /dev/null +++ b/src/main/java/com/thealgorithms/puzzlesandgames/WordBoggle.java @@ -0,0 +1,125 @@ +package com.thealgorithms.puzzlesandgames; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public final class WordBoggle { + + private WordBoggle() { + } + /** + * O(nm * 8^s + ws) time where n = width of boggle board, m = height of + * boggle board, s = length of longest word in string array, w = length of + * string array, 8 is due to 8 explorable neighbours O(nm + ws) space. + */ + public static List<String> boggleBoard(char[][] board, String[] words) { + Trie trie = new Trie(); + for (String word : words) { + trie.add(word); + } + Set<String> finalWords = new HashSet<>(); + boolean[][] visited = new boolean[board.length][board.length]; + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[i].length; j++) { + explore(i, j, board, trie.root, visited, finalWords); + } + } + return new ArrayList<>(finalWords); + } + + public static void explore(int i, int j, char[][] board, TrieNode trieNode, boolean[][] visited, Set<String> finalWords) { + if (visited[i][j]) { + return; + } + + char letter = board[i][j]; + if (!trieNode.children.containsKey(letter)) { + return; + } + visited[i][j] = true; + trieNode = trieNode.children.get(letter); + if (trieNode.children.containsKey('*')) { + finalWords.add(trieNode.word); + } + + List<Integer[]> neighbors = getNeighbors(i, j, board); + for (Integer[] neighbor : neighbors) { + explore(neighbor[0], neighbor[1], board, trieNode, visited, finalWords); + } + + visited[i][j] = false; + } + + public static List<Integer[]> getNeighbors(int i, int j, char[][] board) { + List<Integer[]> neighbors = new ArrayList<>(); + if (i > 0 && j > 0) { + neighbors.add(new Integer[] {i - 1, j - 1}); + } + + if (i > 0 && j < board[0].length - 1) { + neighbors.add(new Integer[] {i - 1, j + 1}); + } + + if (i < board.length - 1 && j < board[0].length - 1) { + neighbors.add(new Integer[] {i + 1, j + 1}); + } + + if (i < board.length - 1 && j > 0) { + neighbors.add(new Integer[] {i + 1, j - 1}); + } + + if (i > 0) { + neighbors.add(new Integer[] {i - 1, j}); + } + + if (i < board.length - 1) { + neighbors.add(new Integer[] {i + 1, j}); + } + + if (j > 0) { + neighbors.add(new Integer[] {i, j - 1}); + } + + if (j < board[0].length - 1) { + neighbors.add(new Integer[] {i, j + 1}); + } + + return neighbors; + } +} + +// Trie used to optimize string search +class TrieNode { + + Map<Character, TrieNode> children = new HashMap<>(); + String word = ""; +} + +class Trie { + + TrieNode root; + char endSymbol; + + Trie() { + this.root = new TrieNode(); + this.endSymbol = '*'; + } + + public void add(String str) { + TrieNode node = this.root; + for (int i = 0; i < str.length(); i++) { + char letter = str.charAt(i); + if (!node.children.containsKey(letter)) { + TrieNode newNode = new TrieNode(); + node.children.put(letter, newNode); + } + node = node.children.get(letter); + } + node.children.put(this.endSymbol, null); + node.word = str; + } +} diff --git a/src/main/java/com/thealgorithms/randomized/ReservoirSampling.java b/src/main/java/com/thealgorithms/randomized/ReservoirSampling.java new file mode 100644 index 000000000000..05e70f635055 --- /dev/null +++ b/src/main/java/com/thealgorithms/randomized/ReservoirSampling.java @@ -0,0 +1,55 @@ +package com.thealgorithms.randomized; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** + * Reservoir Sampling Algorithm + * + * Use Case: + * - Efficient for selecting k random items from a stream of unknown size + * - Used in streaming systems, big data, and memory-limited environments + * + * Time Complexity: O(n) + * Space Complexity: O(k) + * + * @author Michael Alexander Montoya (@cureprotocols) + * @see <a href="https://en.wikipedia.org/wiki/Reservoir_sampling">Reservoir Sampling - Wikipedia</a> + */ +public final class ReservoirSampling { + + // Prevent instantiation of utility class + private ReservoirSampling() { + throw new UnsupportedOperationException("Utility class"); + } + + /** + * Selects k random elements from a stream using reservoir sampling. + * + * @param stream The input stream as an array of integers. + * @param sampleSize The number of elements to sample. + * @return A list containing k randomly selected elements. + */ + public static List<Integer> sample(int[] stream, int sampleSize) { + if (sampleSize > stream.length) { + throw new IllegalArgumentException("Sample size cannot exceed stream size."); + } + + List<Integer> reservoir = new ArrayList<>(sampleSize); + Random rand = new Random(); + + for (int i = 0; i < stream.length; i++) { + if (i < sampleSize) { + reservoir.add(stream[i]); + } else { + int j = rand.nextInt(i + 1); + if (j < sampleSize) { + reservoir.set(j, stream[i]); + } + } + } + + return reservoir; + } +} diff --git a/src/main/java/com/thealgorithms/recursion/FibonacciSeries.java b/src/main/java/com/thealgorithms/recursion/FibonacciSeries.java new file mode 100644 index 000000000000..e5f474085367 --- /dev/null +++ b/src/main/java/com/thealgorithms/recursion/FibonacciSeries.java @@ -0,0 +1,21 @@ +package com.thealgorithms.recursion; + +/* + The Fibonacci series is a sequence of numbers where each number is the sum of the two preceding ones, + starting with 0 and 1. + NUMBER 0 1 2 3 4 5 6 7 8 9 10 ... + FIBONACCI 0 1 1 2 3 5 8 13 21 34 55 ... +*/ + +public final class FibonacciSeries { + private FibonacciSeries() { + throw new UnsupportedOperationException("Utility class"); + } + public static int fibonacci(int n) { + if (n <= 1) { + return n; + } else { + return fibonacci(n - 1) + fibonacci(n - 2); + } + } +} diff --git a/src/main/java/com/thealgorithms/recursion/GenerateSubsets.java b/src/main/java/com/thealgorithms/recursion/GenerateSubsets.java new file mode 100644 index 000000000000..5a3ff2e88040 --- /dev/null +++ b/src/main/java/com/thealgorithms/recursion/GenerateSubsets.java @@ -0,0 +1,36 @@ +package com.thealgorithms.recursion; + +// program to find power set of a string + +import java.util.ArrayList; +import java.util.List; + +public final class GenerateSubsets { + + private GenerateSubsets() { + throw new UnsupportedOperationException("Utility class"); + } + + public static List<String> subsetRecursion(String str) { + return doRecursion("", str); + } + + private static List<String> doRecursion(String p, String up) { + if (up.isEmpty()) { + List<String> list = new ArrayList<>(); + list.add(p); + return list; + } + + // Taking the character + char ch = up.charAt(0); + // Adding the character in the recursion + List<String> left = doRecursion(p + ch, up.substring(1)); + // Not adding the character in the recursion + List<String> right = doRecursion(p, up.substring(1)); + + left.addAll(right); + + return left; + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/AgingScheduling.java b/src/main/java/com/thealgorithms/scheduling/AgingScheduling.java new file mode 100644 index 000000000000..1e5512be9edd --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/AgingScheduling.java @@ -0,0 +1,62 @@ +package com.thealgorithms.scheduling; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * AgingScheduling is an algorithm designed to prevent starvation + * by gradually increasing the priority of waiting tasks. + * The longer a process waits, the higher its priority becomes. + * + * Use Case: Useful in systems with mixed workloads to avoid + * lower-priority tasks being starved by higher-priority tasks. + * + * @author Hardvan + */ +public final class AgingScheduling { + + static class Task { + String name; + int waitTime; + int priority; + + Task(String name, int priority) { + this.name = name; + this.priority = priority; + this.waitTime = 0; + } + } + + private final Queue<Task> taskQueue; + + public AgingScheduling() { + taskQueue = new LinkedList<>(); + } + + /** + * Adds a task to the scheduler with a given priority. + * + * @param name name of the task + * @param priority priority of the task + */ + public void addTask(String name, int priority) { + taskQueue.offer(new Task(name, priority)); + } + + /** + * Schedules the next task based on the priority and wait time. + * The priority of a task increases with the time it spends waiting. + * + * @return name of the next task to be executed + */ + public String scheduleNext() { + if (taskQueue.isEmpty()) { + return null; + } + Task nextTask = taskQueue.poll(); + nextTask.waitTime++; + nextTask.priority += nextTask.waitTime; + taskQueue.offer(nextTask); + return nextTask.name; + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/EDFScheduling.java b/src/main/java/com/thealgorithms/scheduling/EDFScheduling.java new file mode 100644 index 000000000000..5ba79cdbb73a --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/EDFScheduling.java @@ -0,0 +1,99 @@ +package com.thealgorithms.scheduling; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +/** + * The Earliest Deadline First (EDF) Scheduling class implements a dynamic scheduling algorithm. + * It assigns the CPU to processes with the earliest deadlines, ensuring that deadlines are met if possible. + * This scheduling algorithm is ideal for real-time systems where meeting deadlines is critical. + */ +public final class EDFScheduling { + private EDFScheduling() { + } + + private List<Process> processes; + + /** + * Constructs an EDFScheduling object with a list of processes. + * + * @param processes List of processes to be scheduled. + */ + public EDFScheduling(final List<Process> processes) { + this.processes = processes; + } + + /** + * Schedules the processes using Earliest Deadline First (EDF) scheduling. + * Processes are sorted by their deadlines, and the method simulates their execution. + * It calculates the waiting time and turnaround time for each process. + * + * @return List of processes after they have been executed in order of earliest deadline first. + */ + public List<Process> scheduleProcesses() { + processes.sort(Comparator.comparingInt(Process::getDeadline)); + + int currentTime = 0; + List<Process> executedProcesses = new ArrayList<>(); + + for (Process process : processes) { + process.setWaitingTime(currentTime); + currentTime += process.getBurstTime(); + process.setTurnAroundTime(process.getWaitingTime() + process.getBurstTime()); + + if (currentTime > process.getDeadline()) { + System.out.println("Warning: Process " + process.getProcessId() + " missed its deadline."); + } + + executedProcesses.add(process); + } + + return executedProcesses; + } + + /** + * The Process class represents a process with an ID, burst time, deadline, waiting time, and turnaround time. + */ + public static class Process { + private String processId; + private int burstTime; + private int deadline; + private int waitingTime; + private int turnAroundTime; + + public Process(String processId, int burstTime, int deadline) { + this.processId = processId; + this.burstTime = burstTime; + this.deadline = deadline; + } + + public String getProcessId() { + return processId; + } + + public int getBurstTime() { + return burstTime; + } + + public int getDeadline() { + return deadline; + } + + public int getWaitingTime() { + return waitingTime; + } + + public void setWaitingTime(int waitingTime) { + this.waitingTime = waitingTime; + } + + public int getTurnAroundTime() { + return turnAroundTime; + } + + public void setTurnAroundTime(int turnAroundTime) { + this.turnAroundTime = turnAroundTime; + } + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/FCFSScheduling.java b/src/main/java/com/thealgorithms/scheduling/FCFSScheduling.java new file mode 100644 index 000000000000..b22e81fe560e --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/FCFSScheduling.java @@ -0,0 +1,47 @@ +package com.thealgorithms.scheduling; + +import com.thealgorithms.devutils.entities.ProcessDetails; +import java.util.List; + +/** + * Non-pre-emptive First Come First Serve scheduling. This can be understood here - + * https://www.scaler.com/topics/first-come-first-serve/ + */ +public class FCFSScheduling { + + private List<ProcessDetails> processes; + + FCFSScheduling(final List<ProcessDetails> processes) { + this.processes = processes; + } + + public void scheduleProcesses() { + evaluateWaitingTime(); + evaluateTurnAroundTime(); + } + + private void evaluateWaitingTime() { + int processesNumber = processes.size(); + + if (processesNumber == 0) { + return; + } + + int waitingTime = 0; + int burstTime = processes.get(0).getBurstTime(); + + processes.get(0).setWaitingTime(waitingTime); // for the first process, waiting time will be 0. + + for (int i = 1; i < processesNumber; i++) { + processes.get(i).setWaitingTime(waitingTime + burstTime); + waitingTime = processes.get(i).getWaitingTime(); + burstTime = processes.get(i).getBurstTime(); + } + } + + private void evaluateTurnAroundTime() { + for (final var process : processes) { + process.setTurnAroundTimeTime(process.getBurstTime() + process.getWaitingTime()); + } + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/FairShareScheduling.java b/src/main/java/com/thealgorithms/scheduling/FairShareScheduling.java new file mode 100644 index 000000000000..776fc59c0c4d --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/FairShareScheduling.java @@ -0,0 +1,65 @@ +package com.thealgorithms.scheduling; + +import java.util.HashMap; +import java.util.Map; + +/** + * FairShareScheduling allocates CPU resources equally among users or groups + * instead of individual tasks. Each group gets a proportional share, + * preventing resource hogging by a single user's processes. + * + * Use Case: Multi-user systems where users submit multiple tasks simultaneously, + * such as cloud environments. + * + * @author Hardvan + */ +public final class FairShareScheduling { + + static class User { + String name; + int allocatedResources; + int totalWeight; + + User(String name) { + this.name = name; + this.allocatedResources = 0; + this.totalWeight = 0; + } + + void addWeight(int weight) { + this.totalWeight += weight; + } + } + + private final Map<String, User> users; // username -> User + + public FairShareScheduling() { + users = new HashMap<>(); + } + + public void addUser(String userName) { + users.putIfAbsent(userName, new User(userName)); + } + + public void addTask(String userName, int weight) { + User user = users.get(userName); + if (user != null) { + user.addWeight(weight); + } + } + + public void allocateResources(int totalResources) { + int totalWeights = users.values().stream().mapToInt(user -> user.totalWeight).sum(); + for (User user : users.values()) { + user.allocatedResources = (int) ((double) user.totalWeight / totalWeights * totalResources); + } + } + + public Map<String, Integer> getAllocatedResources() { + Map<String, Integer> allocation = new HashMap<>(); + for (User user : users.values()) { + allocation.put(user.name, user.allocatedResources); + } + return allocation; + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/GangScheduling.java b/src/main/java/com/thealgorithms/scheduling/GangScheduling.java new file mode 100644 index 000000000000..ac1ce8ddd6ae --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/GangScheduling.java @@ -0,0 +1,61 @@ +package com.thealgorithms.scheduling; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * GangScheduling groups related tasks (gangs) to run simultaneously on multiple processors. + * All tasks in a gang are executed together or not at all. + * + * Use Case: Parallel computing environments where multiple threads of a program + * need to run concurrently for optimal performance. + * + * @author Hardvan + */ +public final class GangScheduling { + + static class Gang { + String name; + List<String> tasks; + + Gang(String name) { + this.name = name; + this.tasks = new ArrayList<>(); + } + + void addTask(String task) { + tasks.add(task); + } + + List<String> getTasks() { + return tasks; + } + } + + private final Map<String, Gang> gangs; + + public GangScheduling() { + gangs = new HashMap<>(); + } + + public void addGang(String gangName) { + gangs.putIfAbsent(gangName, new Gang(gangName)); + } + + public void addTaskToGang(String gangName, String task) { + Gang gang = gangs.get(gangName); + if (gang != null) { + gang.addTask(task); + } + } + + public Map<String, List<String>> getGangSchedules() { + Map<String, List<String>> schedules = new HashMap<>(); + for (Gang gang : gangs.values()) { + schedules.put(gang.name, gang.getTasks()); + } + return schedules; + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/HighestResponseRatioNextScheduling.java b/src/main/java/com/thealgorithms/scheduling/HighestResponseRatioNextScheduling.java new file mode 100644 index 000000000000..8ed689698557 --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/HighestResponseRatioNextScheduling.java @@ -0,0 +1,158 @@ +package com.thealgorithms.scheduling; + +import java.util.Arrays; +import java.util.Comparator; + +/** + * The {@code HighestResponseRatioNextScheduling} class implements the + * Highest Response Ratio Next (HRRN) scheduling algorithm. + * HRRN is a non-preemptive scheduling algorithm that selects the process with + * the highest response ratio for execution. + * The response ratio is calculated as: + * + * <pre> + * Response Ratio = (waiting time + burst time) / burst time + * </pre> + * + * HRRN is designed to reduce the average waiting time and improve overall + * system performance by balancing between short and long processes, + * minimizing process starvation. + */ +public final class HighestResponseRatioNextScheduling { + + private static final int PROCESS_NOT_FOUND = -1; + private static final double INITIAL_MAX_RESPONSE_RATIO = -1.0; + + private HighestResponseRatioNextScheduling() { + } + + /** + * Represents a process in the scheduling algorithm. + */ + private static class Process { + String name; + int arrivalTime; + int burstTime; + int turnAroundTime; + boolean finished; + + Process(String name, int arrivalTime, int burstTime) { + this.name = name; + this.arrivalTime = arrivalTime; + this.burstTime = burstTime; + this.turnAroundTime = 0; + this.finished = false; + } + + /** + * Calculates the response ratio for this process. + * + * @param currentTime The current time in the scheduling process. + * @return The response ratio for this process. + */ + double calculateResponseRatio(int currentTime) { + return (double) (burstTime + currentTime - arrivalTime) / burstTime; + } + } + + /** + * Calculates the Turn Around Time (TAT) for each process. + * + * <p>Turn Around Time is calculated as the total time a process spends + * in the system from arrival to completion. It is the sum of the burst time + * and the waiting time.</p> + * + * @param processNames Array of process names. + * @param arrivalTimes Array of arrival times corresponding to each process. + * @param burstTimes Array of burst times for each process. + * @param noOfProcesses The number of processes. + * @return An array of Turn Around Times for each process. + */ + public static int[] calculateTurnAroundTime(final String[] processNames, final int[] arrivalTimes, final int[] burstTimes, final int noOfProcesses) { + int currentTime = 0; + int[] turnAroundTime = new int[noOfProcesses]; + Process[] processes = new Process[noOfProcesses]; + + for (int i = 0; i < noOfProcesses; i++) { + processes[i] = new Process(processNames[i], arrivalTimes[i], burstTimes[i]); + } + + Arrays.sort(processes, Comparator.comparingInt(p -> p.arrivalTime)); + + int finishedProcessCount = 0; + while (finishedProcessCount < noOfProcesses) { + int nextProcessIndex = findNextProcess(processes, currentTime); + if (nextProcessIndex == PROCESS_NOT_FOUND) { + currentTime++; + continue; + } + + Process currentProcess = processes[nextProcessIndex]; + currentTime = Math.max(currentTime, currentProcess.arrivalTime); + currentProcess.turnAroundTime = currentTime + currentProcess.burstTime - currentProcess.arrivalTime; + currentTime += currentProcess.burstTime; + currentProcess.finished = true; + finishedProcessCount++; + } + + for (int i = 0; i < noOfProcesses; i++) { + turnAroundTime[i] = processes[i].turnAroundTime; + } + + return turnAroundTime; + } + + /** + * Calculates the Waiting Time (WT) for each process. + * + * @param turnAroundTime The Turn Around Times for each process. + * @param burstTimes The burst times for each process. + * @return An array of Waiting Times for each process. + */ + public static int[] calculateWaitingTime(int[] turnAroundTime, int[] burstTimes) { + int[] waitingTime = new int[turnAroundTime.length]; + for (int i = 0; i < turnAroundTime.length; i++) { + waitingTime[i] = turnAroundTime[i] - burstTimes[i]; + } + return waitingTime; + } + + /** + * Finds the next process to be scheduled based on arrival times and the current time. + * + * @param processes Array of Process objects. + * @param currentTime The current time in the scheduling process. + * @return The index of the next process to be scheduled, or PROCESS_NOT_FOUND if no process is ready. + */ + private static int findNextProcess(Process[] processes, int currentTime) { + return findHighestResponseRatio(processes, currentTime); + } + + /** + * Finds the process with the highest response ratio. + * + * <p>The response ratio is calculated as: + * (waiting time + burst time) / burst time + * where waiting time = current time - arrival time</p> + * + * @param processes Array of Process objects. + * @param currentTime The current time in the scheduling process. + * @return The index of the process with the highest response ratio, or PROCESS_NOT_FOUND if no process is ready. + */ + private static int findHighestResponseRatio(Process[] processes, int currentTime) { + double maxResponseRatio = INITIAL_MAX_RESPONSE_RATIO; + int maxIndex = PROCESS_NOT_FOUND; + + for (int i = 0; i < processes.length; i++) { + Process process = processes[i]; + if (!process.finished && process.arrivalTime <= currentTime) { + double responseRatio = process.calculateResponseRatio(currentTime); + if (responseRatio > maxResponseRatio) { + maxResponseRatio = responseRatio; + maxIndex = i; + } + } + } + return maxIndex; + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/JobSchedulingWithDeadline.java b/src/main/java/com/thealgorithms/scheduling/JobSchedulingWithDeadline.java new file mode 100644 index 000000000000..49638d39fc2a --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/JobSchedulingWithDeadline.java @@ -0,0 +1,88 @@ +package com.thealgorithms.scheduling; + +import java.util.Arrays; +import java.util.Comparator; + +/** + * A class that implements a job scheduling algorithm to maximize profit + * while adhering to job deadlines and arrival times. + * + * This class provides functionality to schedule jobs based on their profit, + * arrival time, and deadlines to ensure that the maximum number of jobs is completed + * within the given timeframe. It sorts the jobs in decreasing order of profit + * and attempts to assign them to the latest possible time slots. + */ +public final class JobSchedulingWithDeadline { + private JobSchedulingWithDeadline() { + } + + /** + * Represents a job with an ID, arrival time, deadline, and profit. + * + * Each job has a unique identifier, an arrival time (when it becomes available for scheduling), + * a deadline by which it must be completed, and a profit associated with completing the job. + */ + static class Job { + int jobId; + int arrivalTime; + int deadline; + int profit; + + /** + * Constructs a Job instance with the specified job ID, arrival time, deadline, and profit. + * + * @param jobId Unique identifier for the job + * @param arrivalTime Time when the job becomes available for scheduling + * @param deadline Deadline for completing the job + * @param profit Profit earned upon completing the job + */ + Job(int jobId, int arrivalTime, int deadline, int profit) { + this.jobId = jobId; + this.arrivalTime = arrivalTime; + this.deadline = deadline; + this.profit = profit; + } + } + + /** + * Schedules jobs to maximize profit while respecting their deadlines and arrival times. + * + * This method sorts the jobs in descending order of profit and attempts + * to allocate them to time slots that are before or on their deadlines, + * provided they have arrived. The function returns an array where the first element + * is the total number of jobs scheduled and the second element is the total profit earned. + * + * @param jobs An array of Job objects, each representing a job with an ID, arrival time, + * deadline, and profit. + * @return An array of two integers: the first element is the count of jobs + * that were successfully scheduled, and the second element is the + * total profit earned from those jobs. + */ + public static int[] jobSequencingWithDeadlines(Job[] jobs) { + Arrays.sort(jobs, Comparator.comparingInt(job -> - job.profit)); + + int maxDeadline = Arrays.stream(jobs).mapToInt(job -> job.deadline).max().orElse(0); + + int[] timeSlots = new int[maxDeadline]; + Arrays.fill(timeSlots, -1); + + int count = 0; + int maxProfit = 0; + + // Schedule the jobs + for (Job job : jobs) { + if (job.arrivalTime <= job.deadline) { + for (int i = Math.min(job.deadline - 1, maxDeadline - 1); i >= job.arrivalTime - 1; i--) { + if (timeSlots[i] == -1) { + timeSlots[i] = job.jobId; + count++; + maxProfit += job.profit; + break; + } + } + } + } + + return new int[] {count, maxProfit}; + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/LotteryScheduling.java b/src/main/java/com/thealgorithms/scheduling/LotteryScheduling.java new file mode 100644 index 000000000000..cea0c793d340 --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/LotteryScheduling.java @@ -0,0 +1,141 @@ +package com.thealgorithms.scheduling; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** + * The LotteryScheduling class implements the Lottery Scheduling algorithm, which is + * a probabilistic CPU scheduling algorithm. Processes are assigned tickets, and + * the CPU is allocated to a randomly selected process based on ticket count. + * Processes with more tickets have a higher chance of being selected. + */ +public final class LotteryScheduling { + private LotteryScheduling() { + } + + private List<Process> processes; + private Random random; + + /** + * Constructs a LotteryScheduling object with the provided list of processes. + * + * @param processes List of processes to be scheduled using Lottery Scheduling. + */ + public LotteryScheduling(final List<Process> processes) { + this.processes = processes; + this.random = new Random(); + } + + /** + * Constructs a LotteryScheduling object with the provided list of processes and a Random object. + * + * @param processes List of processes to be scheduled using Lottery Scheduling. + * @param random Random object used for generating random numbers. + */ + public LotteryScheduling(final List<Process> processes, Random random) { + this.processes = processes; + this.random = random; + } + + /** + * Schedules the processes using the Lottery Scheduling algorithm. + * Each process is assigned a certain number of tickets, and the algorithm randomly + * selects a process to execute based on ticket count. The method calculates the + * waiting time and turnaround time for each process and simulates their execution. + */ + public List<Process> scheduleProcesses() { + int totalTickets = processes.stream().mapToInt(Process::getTickets).sum(); + int currentTime = 0; + List<Process> executedProcesses = new ArrayList<>(); + + while (!processes.isEmpty()) { + int winningTicket = random.nextInt(totalTickets) + 1; + Process selectedProcess = selectProcessByTicket(winningTicket); + + if (selectedProcess == null) { + // This should not happen in normal circumstances, but we'll handle it just in case + System.err.println("Error: No process selected. Recalculating total tickets."); + totalTickets = processes.stream().mapToInt(Process::getTickets).sum(); + continue; + } + + selectedProcess.setWaitingTime(currentTime); + currentTime += selectedProcess.getBurstTime(); + selectedProcess.setTurnAroundTime(selectedProcess.getWaitingTime() + selectedProcess.getBurstTime()); + + executedProcesses.add(selectedProcess); + processes.remove(selectedProcess); + + totalTickets = processes.stream().mapToInt(Process::getTickets).sum(); + } + + return executedProcesses; + } + + /** + * Selects a process based on a winning ticket. The method iterates over the + * list of processes, and as the ticket sum accumulates, it checks if the + * current process holds the winning ticket. + * + * @param winningTicket The randomly generated ticket number that determines the selected process. + * @return The process associated with the winning ticket. + */ + private Process selectProcessByTicket(int winningTicket) { + int ticketSum = 0; + for (Process process : processes) { + ticketSum += process.getTickets(); + if (ticketSum >= winningTicket) { + return process; + } + } + return null; + } + + /** + * The Process class represents a process in the scheduling system. Each process has + * an ID, burst time (CPU time required for execution), number of tickets (used in + * lottery selection), waiting time, and turnaround time. + */ + public static class Process { + private String processId; + private int burstTime; + private int tickets; + private int waitingTime; + private int turnAroundTime; + + public Process(String processId, int burstTime, int tickets) { + this.processId = processId; + this.burstTime = burstTime; + this.tickets = tickets; + } + + public String getProcessId() { + return processId; + } + + public int getBurstTime() { + return burstTime; + } + + public int getTickets() { + return tickets; + } + + public int getWaitingTime() { + return waitingTime; + } + + public void setWaitingTime(int waitingTime) { + this.waitingTime = waitingTime; + } + + public int getTurnAroundTime() { + return turnAroundTime; + } + + public void setTurnAroundTime(int turnAroundTime) { + this.turnAroundTime = turnAroundTime; + } + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/MLFQScheduler.java b/src/main/java/com/thealgorithms/scheduling/MLFQScheduler.java new file mode 100644 index 000000000000..75840a5cbdcf --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/MLFQScheduler.java @@ -0,0 +1,148 @@ +package com.thealgorithms.scheduling; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + * The Multi-Level Feedback Queue (MLFQ) Scheduler class. + * This class simulates scheduling using multiple queues, where processes move + * between queues depending on their CPU burst behavior. + */ +public class MLFQScheduler { + private List<Queue<Process>> queues; // Multi-level feedback queues + private int[] timeQuantum; // Time quantum for each queue level + private int currentTime; // Current time in the system + + /** + * Constructor to initialize the MLFQ scheduler with the specified number of + * levels and their corresponding time quantums. + * + * @param levels Number of queues (priority levels) + * @param timeQuantums Time quantum for each queue level + */ + public MLFQScheduler(int levels, int[] timeQuantums) { + queues = new ArrayList<>(levels); + for (int i = 0; i < levels; i++) { + queues.add(new LinkedList<>()); + } + timeQuantum = timeQuantums; + currentTime = 0; + } + + /** + * Adds a new process to the highest priority queue (queue 0). + * + * @param p The process to be added to the scheduler + */ + public void addProcess(Process p) { + queues.get(0).add(p); + } + + /** + * Executes the scheduling process by running the processes in all queues, + * promoting or demoting them based on their completion status and behavior. + * The process continues until all queues are empty. + */ + public void run() { + while (!allQueuesEmpty()) { + for (int i = 0; i < queues.size(); i++) { + Queue<Process> queue = queues.get(i); + if (!queue.isEmpty()) { + Process p = queue.poll(); + int quantum = timeQuantum[i]; + + // Execute the process for the minimum of the time quantum or the remaining time + int timeSlice = Math.min(quantum, p.remainingTime); + p.execute(timeSlice); + currentTime += timeSlice; // Update the system's current time + + if (p.isFinished()) { + System.out.println("Process " + p.pid + " finished at time " + currentTime); + } else { + if (i < queues.size() - 1) { + p.priority++; // Demote the process to the next lower priority queue + queues.get(i + 1).add(p); // Add to the next queue level + } else { + queue.add(p); // Stay in the same queue if it's the last level + } + } + } + } + } + } + + /** + * Helper function to check if all the queues are empty (i.e., no process is + * left to execute). + * + * @return true if all queues are empty, otherwise false + */ + private boolean allQueuesEmpty() { + for (Queue<Process> queue : queues) { + if (!queue.isEmpty()) { + return false; + } + } + return true; + } + + /** + * Retrieves the current time of the scheduler, which reflects the total time + * elapsed during the execution of all processes. + * + * @return The current time in the system + */ + public int getCurrentTime() { + return currentTime; + } +} + +/** + * Represents a process in the Multi-Level Feedback Queue (MLFQ) scheduling + * algorithm. + */ +class Process { + int pid; + int burstTime; + int remainingTime; + int arrivalTime; + int priority; + + /** + * Constructor to initialize a new process. + * + * @param pid Process ID + * @param burstTime CPU Burst Time (time required for the process) + * @param arrivalTime Arrival time of the process + */ + Process(int pid, int burstTime, int arrivalTime) { + this.pid = pid; + this.burstTime = burstTime; + this.remainingTime = burstTime; + this.arrivalTime = arrivalTime; + this.priority = 0; + } + + /** + * Executes the process for a given time slice. + * + * @param timeSlice The amount of time the process is executed + */ + public void execute(int timeSlice) { + remainingTime -= timeSlice; + if (remainingTime < 0) { + remainingTime = 0; + } + } + + /** + * Checks if the process has finished execution. + * + * @return true if the process is finished, otherwise false + */ + public boolean isFinished() { + return remainingTime == 0; + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/MultiAgentScheduling.java b/src/main/java/com/thealgorithms/scheduling/MultiAgentScheduling.java new file mode 100644 index 000000000000..113b1691dec1 --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/MultiAgentScheduling.java @@ -0,0 +1,72 @@ +package com.thealgorithms.scheduling; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * MultiAgentScheduling assigns tasks to different autonomous agents + * who independently decide the execution order of their assigned tasks. + * The focus is on collaboration between agents to optimize the overall schedule. + * + * Use Case: Distributed scheduling in decentralized systems like IoT networks. + * + * @author Hardvan + */ +public class MultiAgentScheduling { + + static class Agent { + String name; + List<String> tasks; + + Agent(String name) { + this.name = name; + this.tasks = new ArrayList<>(); + } + + void addTask(String task) { + tasks.add(task); + } + + List<String> getTasks() { + return tasks; + } + } + + private final Map<String, Agent> agents; + + public MultiAgentScheduling() { + agents = new HashMap<>(); + } + + public void addAgent(String agentName) { + agents.putIfAbsent(agentName, new Agent(agentName)); + } + + /** + * Assign a task to a specific agent. + * + * @param agentName the name of the agent + * @param task the task to be assigned + */ + public void assignTask(String agentName, String task) { + Agent agent = agents.get(agentName); + if (agent != null) { + agent.addTask(task); + } + } + + /** + * Get the scheduled tasks for each agent. + * + * @return a map of agent names to their scheduled tasks + */ + public Map<String, List<String>> getScheduledTasks() { + Map<String, List<String>> schedule = new HashMap<>(); + for (Agent agent : agents.values()) { + schedule.put(agent.name, agent.getTasks()); + } + return schedule; + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/NonPreemptivePriorityScheduling.java b/src/main/java/com/thealgorithms/scheduling/NonPreemptivePriorityScheduling.java new file mode 100644 index 000000000000..414de4b24e36 --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/NonPreemptivePriorityScheduling.java @@ -0,0 +1,133 @@ +package com.thealgorithms.scheduling; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.PriorityQueue; +import java.util.Queue; + +/** + * This class implements the Non-Preemptive Priority Scheduling algorithm. + * Processes are executed in order of their priority. The process with the + * highest priority (lower priority number) is executed first, + * and once a process starts executing, it cannot be preempted. + */ +public final class NonPreemptivePriorityScheduling { + + private NonPreemptivePriorityScheduling() { + } + + /** + * Represents a process with an ID, burst time, priority, arrival time, and start time. + */ + static class Process implements Comparable<Process> { + int id; + int arrivalTime; + int startTime; + int burstTime; + int priority; + + /** + * Constructs a Process instance with the specified parameters. + * + * @param id Unique identifier for the process + * @param arrivalTime Time when the process arrives in the system + * @param burstTime Time required for the process execution + * @param priority Priority of the process + */ + Process(int id, int arrivalTime, int burstTime, int priority) { + this.id = id; + this.arrivalTime = arrivalTime; + this.startTime = -1; + this.burstTime = burstTime; + this.priority = priority; + } + + /** + * Compare based on priority for scheduling. The process with the lowest + * priority is selected first. + * If two processes have the same priority, the one that arrives earlier is selected. + * + * @param other The other process to compare against + * @return A negative integer, zero, or a positive integer as this process + * is less than, equal to, or greater than the specified process. + */ + @Override + public int compareTo(Process other) { + if (this.priority == other.priority) { + return Integer.compare(this.arrivalTime, other.arrivalTime); + } + return Integer.compare(this.priority, other.priority); + } + } + + /** + * Schedules processes based on their priority in a non-preemptive manner, considering their arrival times. + * + * @param processes Array of processes to be scheduled. + * @return Array of processes in the order they are executed. + */ + public static Process[] scheduleProcesses(Process[] processes) { + PriorityQueue<Process> pq = new PriorityQueue<>(); + Queue<Process> waitingQueue = new LinkedList<>(); + int currentTime = 0; + int index = 0; + Process[] executionOrder = new Process[processes.length]; + + Collections.addAll(waitingQueue, processes); + + while (!waitingQueue.isEmpty() || !pq.isEmpty()) { + // Add processes that have arrived to the priority queue + while (!waitingQueue.isEmpty() && waitingQueue.peek().arrivalTime <= currentTime) { + pq.add(waitingQueue.poll()); + } + + if (!pq.isEmpty()) { + Process currentProcess = pq.poll(); + currentProcess.startTime = currentTime; + executionOrder[index++] = currentProcess; + currentTime += currentProcess.burstTime; + } else { + // If no process is ready, move to the next arrival time + currentTime = waitingQueue.peek().arrivalTime; + } + } + + return executionOrder; + } + + /** + * Calculates the average waiting time of the processes. + * + * @param processes Array of processes. + * @param executionOrder Array of processes in execution order. + * @return Average waiting time. + */ + public static double calculateAverageWaitingTime(Process[] processes, Process[] executionOrder) { + int totalWaitingTime = 0; + + for (Process process : executionOrder) { + int waitingTime = process.startTime - process.arrivalTime; + totalWaitingTime += waitingTime; + } + + return (double) totalWaitingTime / processes.length; + } + + /** + * Calculates the average turn-around time of the processes. + * + * @param processes Array of processes. + * @param executionOrder Array of processes in execution order. + * @return Average turn-around time. + */ + public static double calculateAverageTurnaroundTime(Process[] processes, Process[] executionOrder) { + int totalTurnaroundTime = 0; + + for (Process process : executionOrder) { + int turnaroundTime = process.startTime + process.burstTime - process.arrivalTime; + totalTurnaroundTime += turnaroundTime; + } + + return (double) totalTurnaroundTime / processes.length; + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/PreemptivePriorityScheduling.java b/src/main/java/com/thealgorithms/scheduling/PreemptivePriorityScheduling.java new file mode 100644 index 000000000000..66c99661d13d --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/PreemptivePriorityScheduling.java @@ -0,0 +1,60 @@ +package com.thealgorithms.scheduling; + +import com.thealgorithms.devutils.entities.ProcessDetails; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.PriorityQueue; + +/** + * Preemptive Priority Scheduling Algorithm + * + * @author [Bama Charan Chhandogi](https://www.github.com/BamaCharanChhandogi) + */ +public class PreemptivePriorityScheduling { + protected final List<ProcessDetails> processes; + protected final List<String> ganttChart; + + public PreemptivePriorityScheduling(Collection<ProcessDetails> processes) { + this.processes = new ArrayList<>(processes); + this.ganttChart = new ArrayList<>(); + } + + public void scheduleProcesses() { + PriorityQueue<ProcessDetails> readyQueue = new PriorityQueue<>(Comparator.comparingInt(ProcessDetails::getPriority).reversed().thenComparingInt(ProcessDetails::getArrivalTime)); + + int currentTime = 0; + List<ProcessDetails> arrivedProcesses = new ArrayList<>(); + + while (!processes.isEmpty() || !readyQueue.isEmpty()) { + updateArrivedProcesses(currentTime, arrivedProcesses); + readyQueue.addAll(arrivedProcesses); + arrivedProcesses.clear(); + + if (!readyQueue.isEmpty()) { + ProcessDetails currentProcess = readyQueue.poll(); + ganttChart.add(currentProcess.getProcessId()); + currentProcess.setBurstTime(currentProcess.getBurstTime() - 1); + + if (currentProcess.getBurstTime() > 0) { + readyQueue.add(currentProcess); + } + } else { + ganttChart.add("Idle"); + } + + currentTime++; + } + } + + private void updateArrivedProcesses(int currentTime, List<ProcessDetails> arrivedProcesses) { + processes.removeIf(process -> { + if (process.getArrivalTime() <= currentTime) { + arrivedProcesses.add(process); + return true; + } + return false; + }); + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/ProportionalFairScheduling.java b/src/main/java/com/thealgorithms/scheduling/ProportionalFairScheduling.java new file mode 100644 index 000000000000..fd78dc571819 --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/ProportionalFairScheduling.java @@ -0,0 +1,54 @@ +package com.thealgorithms.scheduling; + +import java.util.ArrayList; +import java.util.List; + +/** + * ProportionalFairScheduling allocates resources to processes based on their + * proportional weight or importance. It aims to balance fairness with + * priority, ensuring that higher-weight processes receive a larger share of resources. + * + * Use Case: Network bandwidth allocation in cellular networks (4G/5G), + * where devices receive a proportional share of bandwidth. + * + * @author Hardvan + */ +public final class ProportionalFairScheduling { + + static class Process { + String name; + int weight; + int allocatedResources; + + Process(String name, int weight) { + this.name = name; + this.weight = weight; + this.allocatedResources = 0; + } + } + + private final List<Process> processes; + + public ProportionalFairScheduling() { + processes = new ArrayList<>(); + } + + public void addProcess(String name, int weight) { + processes.add(new Process(name, weight)); + } + + public void allocateResources(int totalResources) { + int totalWeight = processes.stream().mapToInt(p -> p.weight).sum(); + for (Process process : processes) { + process.allocatedResources = (int) ((double) process.weight / totalWeight * totalResources); + } + } + + public List<String> getAllocatedResources() { + List<String> allocation = new ArrayList<>(); + for (Process process : processes) { + allocation.add(process.name + ": " + process.allocatedResources); + } + return allocation; + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/RRScheduling.java b/src/main/java/com/thealgorithms/scheduling/RRScheduling.java new file mode 100644 index 000000000000..110c97416a42 --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/RRScheduling.java @@ -0,0 +1,104 @@ +/** + * @author Md Asif Joardar + */ + +package com.thealgorithms.scheduling; + +import com.thealgorithms.devutils.entities.ProcessDetails; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + * The Round-robin scheduling algorithm is a kind of preemptive First come, First Serve CPU + * Scheduling algorithm. This can be understood here - + * https://www.scaler.com/topics/round-robin-scheduling-in-os/ + */ + +public class RRScheduling { + private List<ProcessDetails> processes; + private int quantumTime; + + RRScheduling(final List<ProcessDetails> processes, int quantumTime) { + this.processes = processes; + this.quantumTime = quantumTime; + } + + public void scheduleProcesses() { + evaluateTurnAroundTime(); + evaluateWaitingTime(); + } + + private void evaluateTurnAroundTime() { + int processesNumber = processes.size(); + + if (processesNumber == 0) { + return; + } + + Queue<Integer> queue = new LinkedList<>(); + queue.add(0); + int currentTime = 0; // keep track of the time + int completed = 0; + int[] mark = new int[processesNumber]; + Arrays.fill(mark, 0); + mark[0] = 1; + + // a copy of burst time to store the remaining burst time + int[] remainingBurstTime = new int[processesNumber]; + for (int i = 0; i < processesNumber; i++) { + remainingBurstTime[i] = processes.get(i).getBurstTime(); + } + + while (completed != processesNumber) { + int index = queue.poll(); + + if (remainingBurstTime[index] == processes.get(index).getBurstTime()) { + currentTime = Math.max(currentTime, processes.get(index).getArrivalTime()); + } + + if (remainingBurstTime[index] - quantumTime > 0) { + remainingBurstTime[index] -= quantumTime; + currentTime += quantumTime; + } else { + currentTime += remainingBurstTime[index]; + processes.get(index).setTurnAroundTimeTime(currentTime - processes.get(index).getArrivalTime()); + completed++; + remainingBurstTime[index] = 0; + } + + // If some process has arrived when this process was executing, insert them into the + // queue. + for (int i = 1; i < processesNumber; i++) { + if (remainingBurstTime[i] > 0 && processes.get(i).getArrivalTime() <= currentTime && mark[i] == 0) { + mark[i] = 1; + queue.add(i); + } + } + + // If the current process has burst time remaining, push the process into the queue + // again. + if (remainingBurstTime[index] > 0) { + queue.add(index); + } + + // If the queue is empty, pick the first process from the list that is not completed. + if (queue.isEmpty()) { + for (int i = 1; i < processesNumber; i++) { + if (remainingBurstTime[i] > 0) { + mark[i] = 1; + queue.add(i); + break; + } + } + } + } + } + + private void evaluateWaitingTime() { + for (final var process : processes) { + process.setWaitingTime(process.getTurnAroundTimeTime() - process.getBurstTime()); + } + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/RandomScheduling.java b/src/main/java/com/thealgorithms/scheduling/RandomScheduling.java new file mode 100644 index 000000000000..b7e863b5cfd8 --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/RandomScheduling.java @@ -0,0 +1,45 @@ +package com.thealgorithms.scheduling; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +/** + * RandomScheduling is an algorithm that assigns tasks in a random order. + * It doesn't consider priority, deadlines, or burst times, making it + * inefficient but useful in scenarios where fairness or unpredictability + * is required (e.g., load balancing in distributed systems). + * + * Use Case: Distributed systems where randomness helps avoid task starvation. + * + * @author Hardvan + */ +public final class RandomScheduling { + + private final List<String> tasks; + private final Random random; + + /** + * Constructs a new RandomScheduling instance. + * + * @param tasks A collection of task names to be scheduled. + * @param random A Random instance for generating random numbers. + */ + public RandomScheduling(Collection<String> tasks, Random random) { + this.tasks = new ArrayList<>(tasks); + this.random = random; + } + + /** + * Schedules the tasks randomly and returns the randomized order. + * + * @return A list representing the tasks in their randomized execution order. + */ + public List<String> schedule() { + List<String> shuffledTasks = new ArrayList<>(tasks); + Collections.shuffle(shuffledTasks, random); + return shuffledTasks; + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/SJFScheduling.java b/src/main/java/com/thealgorithms/scheduling/SJFScheduling.java new file mode 100644 index 000000000000..cbbc65a3afc5 --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/SJFScheduling.java @@ -0,0 +1,111 @@ +package com.thealgorithms.scheduling; + +import com.thealgorithms.devutils.entities.ProcessDetails; +import java.util.ArrayList; +import java.util.List; + +/** + * Implementation of Shortest Job First Algorithm: The algorithm allows the waiting process with the + * minimal burst time to be executed first. see more here: + * https://www.guru99.com/shortest-job-first-sjf-scheduling.html + */ + +public class SJFScheduling { + protected ArrayList<ProcessDetails> processes; + protected ArrayList<String> schedule; + + private static void sortProcessesByArrivalTime(List<ProcessDetails> processes) { + for (int i = 0; i < processes.size(); i++) { + for (int j = i + 1; j < processes.size() - 1; j++) { + if (processes.get(j).getArrivalTime() > processes.get(j + 1).getArrivalTime()) { + final var temp = processes.get(j); + processes.set(j, processes.get(j + 1)); + processes.set(j + 1, temp); + } + } + } + } + + /** + * a simple constructor + * @param processes a list of processes the user wants to schedule + * it also sorts the processes based on the time of their arrival + */ + SJFScheduling(final ArrayList<ProcessDetails> processes) { + this.processes = processes; + schedule = new ArrayList<>(); + sortProcessesByArrivalTime(this.processes); + } + protected void sortByArrivalTime() { + sortProcessesByArrivalTime(processes); + } + + /** + * this functions returns the order of the executions + */ + + public void scheduleProcesses() { + ArrayList<ProcessDetails> ready = new ArrayList<>(); + + int size = processes.size(); + int runtime; + int time = 0; + int executed = 0; + int j; + int k = 0; + ProcessDetails running; + + if (size == 0) { + return; + } + + while (executed < size) { + while (k < size && processes.get(k).getArrivalTime() <= time) // here we find the processes that have arrived. + { + ready.add(processes.get(k)); + k++; + } + + running = findShortestJob(ready); + if (running == null) { + time++; + } else { + runtime = running.getBurstTime(); + for (j = 0; j < runtime; j++) { + time++; + } + schedule.add(running.getProcessId()); + ready.remove(running); + executed++; + } + } + } + + /** + * this function evaluates the shortest job of all the ready processes (based on a process + * burst time) + * @param readyProcesses an array list of ready processes + * @return returns the process' with the shortest burst time OR NULL if there are no ready + * processes + */ + private ProcessDetails findShortestJob(List<ProcessDetails> readyProcesses) { + if (readyProcesses.isEmpty()) { + return null; + } + int i; + int size = readyProcesses.size(); + int minBurstTime = readyProcesses.get(0).getBurstTime(); + int temp; + int positionOfShortestJob = 0; + + for (i = 1; i < size; i++) { + temp = readyProcesses.get(i).getBurstTime(); + if (minBurstTime > temp) { + minBurstTime = temp; + positionOfShortestJob = i; + } + } + + return readyProcesses.get(positionOfShortestJob); + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/SRTFScheduling.java b/src/main/java/com/thealgorithms/scheduling/SRTFScheduling.java new file mode 100644 index 000000000000..99214fff20c4 --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/SRTFScheduling.java @@ -0,0 +1,70 @@ +package com.thealgorithms.scheduling; + +import com.thealgorithms.devutils.entities.ProcessDetails; +import java.util.ArrayList; +import java.util.List; + +/** + * Implementation of Shortest Remaining Time First Scheduling Algorithm. + * In the SRTF scheduling algorithm, the process with the smallest amount of time remaining until completion is selected to execute. + * Example: + * Consider the processes p1, p2 and the following table with info about their arrival and burst time: + * Process | Burst Time | Arrival Time + * P1 | 6 ms | 0 ms + * P2 | 2 ms | 1 ms + * In this example, P1 will be executed at time = 0 until time = 1 when P2 arrives. At time = 2, P2 will be executed until time = 4. At time 4, P2 is done, and P1 is executed again to be done. + * That's a simple example of how the algorithm works. + * More information you can find here -> https://en.wikipedia.org/wiki/Shortest_remaining_time + */ +public class SRTFScheduling { + protected List<ProcessDetails> processes; + protected List<String> ready; + + /** + * Constructor + * @param processes ArrayList of ProcessDetails given as input + */ + public SRTFScheduling(ArrayList<ProcessDetails> processes) { + this.processes = new ArrayList<>(); + ready = new ArrayList<>(); + this.processes = processes; + } + + public void evaluateScheduling() { + int time = 0; + int cr = 0; // cr=current running process, time= units of time + int n = processes.size(); + int[] remainingTime = new int[n]; + + /* calculating remaining time of every process and total units of time */ + for (int i = 0; i < n; i++) { + remainingTime[i] = processes.get(i).getBurstTime(); + time += processes.get(i).getBurstTime(); + } + + /* if the first process doesn't arrive at 0, we have more units of time */ + if (processes.get(0).getArrivalTime() != 0) { + time += processes.get(0).getArrivalTime(); + } + + /* printing id of the process which is executed at every unit of time */ + // if the first process doesn't arrive at 0, we print only \n until it arrives + if (processes.get(0).getArrivalTime() != 0) { + for (int i = 0; i < processes.get(0).getArrivalTime(); i++) { + ready.add(null); + } + } + + for (int i = processes.get(0).getArrivalTime(); i < time; i++) { + /* checking if there's a process with remaining time less than current running process. + If we find it, then it executes. */ + for (int j = 0; j < n; j++) { + if (processes.get(j).getArrivalTime() <= i && (remainingTime[j] < remainingTime[cr] && remainingTime[j] > 0 || remainingTime[cr] == 0)) { + cr = j; + } + } + ready.add(processes.get(cr).getProcessId()); + remainingTime[cr]--; + } + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/SelfAdjustingScheduling.java b/src/main/java/com/thealgorithms/scheduling/SelfAdjustingScheduling.java new file mode 100644 index 000000000000..15159a07d2d4 --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/SelfAdjustingScheduling.java @@ -0,0 +1,63 @@ +package com.thealgorithms.scheduling; + +import java.util.PriorityQueue; + +/** + * SelfAdjustingScheduling is an algorithm where tasks dynamically adjust + * their priority based on real-time feedback, such as wait time and CPU usage. + * Tasks that wait longer will automatically increase their priority, + * allowing for better responsiveness and fairness in task handling. + * + * Use Case: Real-time systems that require dynamic prioritization + * of tasks to maintain system responsiveness and fairness. + * + * @author Hardvan + */ +public final class SelfAdjustingScheduling { + + private static class Task implements Comparable<Task> { + String name; + int waitTime; + int priority; + + Task(String name, int priority) { + this.name = name; + this.waitTime = 0; + this.priority = priority; + } + + void incrementWaitTime() { + waitTime++; + priority = priority + waitTime; + } + + @Override + public int compareTo(Task other) { + return Integer.compare(this.priority, other.priority); + } + } + + private final PriorityQueue<Task> taskQueue; + + public SelfAdjustingScheduling() { + taskQueue = new PriorityQueue<>(); + } + + public void addTask(String name, int priority) { + taskQueue.offer(new Task(name, priority)); + } + + public String scheduleNext() { + if (taskQueue.isEmpty()) { + return null; + } + Task nextTask = taskQueue.poll(); + nextTask.incrementWaitTime(); + taskQueue.offer(nextTask); + return nextTask.name; + } + + public boolean isEmpty() { + return taskQueue.isEmpty(); + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/SlackTimeScheduling.java b/src/main/java/com/thealgorithms/scheduling/SlackTimeScheduling.java new file mode 100644 index 000000000000..bbfd36f0f660 --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/SlackTimeScheduling.java @@ -0,0 +1,64 @@ +package com.thealgorithms.scheduling; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +/** + * SlackTimeScheduling is an algorithm that prioritizes tasks based on their + * slack time, which is defined as the difference between the task's deadline + * and the time required to execute it. Tasks with less slack time are prioritized. + * + * Use Case: Real-time systems with hard deadlines, such as robotics or embedded systems. + * + * @author Hardvan + */ +public class SlackTimeScheduling { + + static class Task { + String name; + int executionTime; + int deadline; + + Task(String name, int executionTime, int deadline) { + this.name = name; + this.executionTime = executionTime; + this.deadline = deadline; + } + + int getSlackTime() { + return deadline - executionTime; + } + } + + private final List<Task> tasks; + + public SlackTimeScheduling() { + tasks = new ArrayList<>(); + } + + /** + * Adds a task to the scheduler. + * + * @param name the name of the task + * @param executionTime the time required to execute the task + * @param deadline the deadline by which the task must be completed + */ + public void addTask(String name, int executionTime, int deadline) { + tasks.add(new Task(name, executionTime, deadline)); + } + + /** + * Schedules the tasks based on their slack time. + * + * @return the order in which the tasks should be executed + */ + public List<String> scheduleTasks() { + tasks.sort(Comparator.comparingInt(Task::getSlackTime)); + List<String> scheduledOrder = new ArrayList<>(); + for (Task task : tasks) { + scheduledOrder.add(task.name); + } + return scheduledOrder; + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/diskscheduling/CircularLookScheduling.java b/src/main/java/com/thealgorithms/scheduling/diskscheduling/CircularLookScheduling.java new file mode 100644 index 000000000000..2230ecaf35a6 --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/diskscheduling/CircularLookScheduling.java @@ -0,0 +1,80 @@ +package com.thealgorithms.scheduling.diskscheduling; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Circular Look Scheduling (C-LOOK) is a disk scheduling algorithm similar to + * the C-SCAN algorithm but with a key difference. In C-LOOK, the disk arm also + * moves in one direction to service requests, but instead of going all the way + * to the end of the disk, it only goes as far as the furthest request in the + * current direction. After servicing the last request in the current direction, + * the arm immediately jumps back to the closest request on the other side without + * moving to the disk's extreme ends. This reduces the unnecessary movement of the + * disk arm, resulting in better performance compared to C-SCAN, while still + * maintaining fair wait times for requests. + */ +public class CircularLookScheduling { + private int currentPosition; + private boolean movingUp; + private final int maxCylinder; + + public CircularLookScheduling(int startPosition, boolean movingUp, int maxCylinder) { + this.currentPosition = startPosition; + this.movingUp = movingUp; + this.maxCylinder = maxCylinder; + } + + public List<Integer> execute(List<Integer> requests) { + List<Integer> result = new ArrayList<>(); + + // Filter and sort valid requests in both directions + List<Integer> upRequests = new ArrayList<>(); + List<Integer> downRequests = new ArrayList<>(); + + for (int request : requests) { + if (request >= 0 && request < maxCylinder) { + if (request > currentPosition) { + upRequests.add(request); + } else if (request < currentPosition) { + downRequests.add(request); + } + } + } + + Collections.sort(upRequests); + Collections.sort(downRequests); + + if (movingUp) { + // Process all requests in the upward direction + result.addAll(upRequests); + + // Jump to the lowest request and process all requests in the downward direction + result.addAll(downRequests); + } else { + // Process all requests in the downward direction (in reverse order) + Collections.reverse(downRequests); + result.addAll(downRequests); + + // Jump to the highest request and process all requests in the upward direction (in reverse order) + Collections.reverse(upRequests); + result.addAll(upRequests); + } + + // Update current position to the last processed request + if (!result.isEmpty()) { + currentPosition = result.get(result.size() - 1); + } + + return result; + } + + public int getCurrentPosition() { + return currentPosition; + } + + public boolean isMovingUp() { + return movingUp; + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/diskscheduling/CircularScanScheduling.java b/src/main/java/com/thealgorithms/scheduling/diskscheduling/CircularScanScheduling.java new file mode 100644 index 000000000000..a31c3d99a89e --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/diskscheduling/CircularScanScheduling.java @@ -0,0 +1,83 @@ +package com.thealgorithms.scheduling.diskscheduling; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Circular Scan Scheduling (C-SCAN) is a disk scheduling algorithm that + * works by moving the disk arm in one direction to service requests until + * it reaches the end of the disk. Once it reaches the end, instead of reversing + * direction like in the SCAN algorithm, the arm moves back to the starting point + * without servicing any requests. This ensures a more uniform wait time for all + * requests, especially those near the disk edges. The algorithm then continues in + * the same direction, making it effective for balancing service time across all disk sectors. + */ +public class CircularScanScheduling { + private int currentPosition; + private boolean movingUp; + private final int diskSize; + + public CircularScanScheduling(int startPosition, boolean movingUp, int diskSize) { + this.currentPosition = startPosition; + this.movingUp = movingUp; + this.diskSize = diskSize; + } + + public List<Integer> execute(List<Integer> requests) { + if (requests.isEmpty()) { + return new ArrayList<>(); // Return empty list if there are no requests + } + + List<Integer> sortedRequests = new ArrayList<>(requests); + Collections.sort(sortedRequests); + + List<Integer> result = new ArrayList<>(); + + if (movingUp) { + // Moving up: process requests >= current position + for (int request : sortedRequests) { + if (request >= currentPosition && request < diskSize) { + result.add(request); + } + } + + // Jump to the smallest request and continue processing from the start + for (int request : sortedRequests) { + if (request < currentPosition) { + result.add(request); + } + } + } else { + // Moving down: process requests <= current position in reverse order + for (int i = sortedRequests.size() - 1; i >= 0; i--) { + int request = sortedRequests.get(i); + if (request <= currentPosition) { + result.add(request); + } + } + + // Jump to the largest request and continue processing in reverse order + for (int i = sortedRequests.size() - 1; i >= 0; i--) { + int request = sortedRequests.get(i); + if (request > currentPosition) { + result.add(request); + } + } + } + + // Set final position to the last request processed + if (!result.isEmpty()) { + currentPosition = result.get(result.size() - 1); + } + return result; + } + + public int getCurrentPosition() { + return currentPosition; + } + + public boolean isMovingUp() { + return movingUp; + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/diskscheduling/LookScheduling.java b/src/main/java/com/thealgorithms/scheduling/diskscheduling/LookScheduling.java new file mode 100644 index 000000000000..beba69b05eca --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/diskscheduling/LookScheduling.java @@ -0,0 +1,95 @@ +package com.thealgorithms.scheduling.diskscheduling; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * https://en.wikipedia.org/wiki/LOOK_algorithm + * Look Scheduling algorithm implementation. + * The Look algorithm moves the disk arm to the closest request in the current direction, + * and once it processes all requests in that direction, it reverses the direction. + */ +public class LookScheduling { + private final int maxTrack; + private final int currentPosition; + private boolean movingUp; + private int farthestPosition; + public LookScheduling(int startPosition, boolean initialDirection, int maxTrack) { + this.currentPosition = startPosition; + this.movingUp = initialDirection; + this.maxTrack = maxTrack; + } + + /** + * Executes the Look Scheduling algorithm on the given list of requests. + * + * @param requests List of disk requests. + * @return Order in which requests are processed. + */ + public List<Integer> execute(List<Integer> requests) { + List<Integer> result = new ArrayList<>(); + List<Integer> lower = new ArrayList<>(); + List<Integer> upper = new ArrayList<>(); + + // Split requests into two lists based on their position relative to current position + for (int request : requests) { + if (request >= 0 && request < maxTrack) { + if (request < currentPosition) { + lower.add(request); + } else { + upper.add(request); + } + } + } + + // Sort the requests + Collections.sort(lower); + Collections.sort(upper); + + // Process the requests depending on the initial moving direction + if (movingUp) { + // Process requests in the upward direction + result.addAll(upper); + if (!upper.isEmpty()) { + farthestPosition = upper.get(upper.size() - 1); + } + + // Reverse the direction and process downward + movingUp = false; + Collections.reverse(lower); + result.addAll(lower); + if (!lower.isEmpty()) { + farthestPosition = Math.max(farthestPosition, lower.get(0)); + } + } else { + // Process requests in the downward direction + Collections.reverse(lower); + result.addAll(lower); + if (!lower.isEmpty()) { + farthestPosition = lower.get(0); + } + + // Reverse the direction and process upward + movingUp = true; + result.addAll(upper); + if (!upper.isEmpty()) { + farthestPosition = Math.max(farthestPosition, upper.get(upper.size() - 1)); + } + } + + return result; + } + + public int getCurrentPosition() { + return currentPosition; + } + + public boolean isMovingUp() { + return movingUp; + } + + public int getFarthestPosition() { + return farthestPosition; + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/diskscheduling/SSFScheduling.java b/src/main/java/com/thealgorithms/scheduling/diskscheduling/SSFScheduling.java new file mode 100644 index 000000000000..261c1a388393 --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/diskscheduling/SSFScheduling.java @@ -0,0 +1,57 @@ +package com.thealgorithms.scheduling.diskscheduling; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + *https://en.wikipedia.org/wiki/Shortest_seek_first + * Shortest Seek First (SFF) Scheduling algorithm implementation. + * The SFF algorithm selects the next request to be serviced based on the shortest distance + * from the current position of the disk arm. It continuously evaluates all pending requests + * and chooses the one that requires the least amount of movement to service. + * + * This approach minimizes the average seek time, making it efficient in terms of response + * time for individual requests. However, it may lead to starvation for requests located + * further away from the current position of the disk arm. + * + * The SFF algorithm is particularly effective in systems where quick response time + * is crucial, as it ensures that the most accessible requests are prioritized for servicing. + */ +public class SSFScheduling { + private int currentPosition; + + public SSFScheduling(int currentPosition) { + this.currentPosition = currentPosition; + } + + public List<Integer> execute(Collection<Integer> requests) { + List<Integer> result = new ArrayList<>(requests); + List<Integer> orderedRequests = new ArrayList<>(); + + while (!result.isEmpty()) { + int closest = findClosest(result); + orderedRequests.add(closest); + result.remove(Integer.valueOf(closest)); + currentPosition = closest; + } + return orderedRequests; + } + + private int findClosest(List<Integer> requests) { + int minDistance = Integer.MAX_VALUE; + int closest = -1; + for (int request : requests) { + int distance = Math.abs(currentPosition - request); + if (distance < minDistance) { + minDistance = distance; + closest = request; + } + } + return closest; + } + + public int getCurrentPosition() { + return currentPosition; + } +} diff --git a/src/main/java/com/thealgorithms/scheduling/diskscheduling/ScanScheduling.java b/src/main/java/com/thealgorithms/scheduling/diskscheduling/ScanScheduling.java new file mode 100644 index 000000000000..2c4fa7844a12 --- /dev/null +++ b/src/main/java/com/thealgorithms/scheduling/diskscheduling/ScanScheduling.java @@ -0,0 +1,82 @@ +package com.thealgorithms.scheduling.diskscheduling; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * https://en.wikipedia.org/wiki/Elevator_algorithm + * SCAN Scheduling algorithm implementation. + * The SCAN algorithm moves the disk arm towards one end of the disk, servicing all requests + * along the way until it reaches the end. Once it reaches the end, it reverses direction + * and services the requests on its way back. + * + * This algorithm ensures that all requests are serviced in a fair manner, + * while minimizing the seek time for requests located close to the current position + * of the disk arm. + * + * The SCAN algorithm is particularly useful in environments with a large number of + * disk requests, as it reduces the overall movement of the disk arm compared to + */ +public class ScanScheduling { + private int headPosition; + private int diskSize; + private boolean movingUp; + + public ScanScheduling(int headPosition, boolean movingUp, int diskSize) { + this.headPosition = headPosition; + this.movingUp = movingUp; + this.diskSize = diskSize; + } + + public List<Integer> execute(List<Integer> requests) { + // If the request list is empty, return an empty result + if (requests.isEmpty()) { + return new ArrayList<>(); + } + + List<Integer> result = new ArrayList<>(); + List<Integer> left = new ArrayList<>(); + List<Integer> right = new ArrayList<>(); + + // Separate requests into those smaller than the current head position and those larger + for (int request : requests) { + if (request < headPosition) { + left.add(request); + } else { + right.add(request); + } + } + + // Sort the requests + Collections.sort(left); + Collections.sort(right); + + // Simulate the disk head movement + if (movingUp) { + // Head moving upward, process right-side requests first + result.addAll(right); + // After reaching the end of the disk, reverse direction and process left-side requests + result.add(diskSize - 1); // Simulate the head reaching the end of the disk + Collections.reverse(left); + result.addAll(left); + } else { + // Head moving downward, process left-side requests first + Collections.reverse(left); + result.addAll(left); + // After reaching the start of the disk, reverse direction and process right-side requests + result.add(0); // Simulate the head reaching the start of the disk + result.addAll(right); + } + + return result; + } + + public int getHeadPosition() { + return headPosition; + } + + public boolean isMovingUp() { + return movingUp; + } +} diff --git a/src/main/java/com/thealgorithms/searches/BM25InvertedIndex.java b/src/main/java/com/thealgorithms/searches/BM25InvertedIndex.java new file mode 100644 index 000000000000..aeddc591b32b --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/BM25InvertedIndex.java @@ -0,0 +1,220 @@ +package com.thealgorithms.searches; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * Inverted Index implementation with BM25 Scoring for movie search. + * This class supports adding movie documents and searching for terms + * within those documents using the BM25 algorithm. + * @author Prayas Kumar (https://github.com/prayas7102) + */ + +class Movie { + int docId; // Unique identifier for the movie + String name; // Movie name + double imdbRating; // IMDb rating of the movie + int releaseYear; // Year the movie was released + String content; // Full text content (could be the description or script) + + /** + * Constructor for the Movie class. + * @param docId Unique identifier for the movie. + * @param name Name of the movie. + * @param imdbRating IMDb rating of the movie. + * @param releaseYear Release year of the movie. + * @param content Content or description of the movie. + */ + Movie(int docId, String name, double imdbRating, int releaseYear, String content) { + this.docId = docId; + this.name = name; + this.imdbRating = imdbRating; + this.releaseYear = releaseYear; + this.content = content; + } + + /** + * Get all the words from the movie's name and content. + * Converts the name and content to lowercase and splits on non-word characters. + * @return Array of words from the movie name and content. + */ + public String[] getWords() { + return (name + " " + content).toLowerCase().split("\\W+"); + } + + @Override + public String toString() { + return "Movie{" + + "docId=" + docId + ", name='" + name + '\'' + ", imdbRating=" + imdbRating + ", releaseYear=" + releaseYear + '}'; + } +} + +class SearchResult { + int docId; // Unique identifier of the movie document + double relevanceScore; // Relevance score based on the BM25 algorithm + + /** + * Constructor for SearchResult class. + * @param docId Document ID (movie) for this search result. + * @param relevanceScore The relevance score based on BM25 scoring. + */ + SearchResult(int docId, double relevanceScore) { + this.docId = docId; + this.relevanceScore = relevanceScore; + } + + public int getDocId() { + return docId; + } + + @Override + public String toString() { + return "SearchResult{" + + "docId=" + docId + ", relevanceScore=" + relevanceScore + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SearchResult that = (SearchResult) o; + return docId == that.docId && Double.compare(that.relevanceScore, relevanceScore) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(docId, relevanceScore); + } + + public double getRelevanceScore() { + return this.relevanceScore; + } +} + +public final class BM25InvertedIndex { + private Map<String, Map<Integer, Integer>> index; // Inverted index mapping terms to document id and frequency + private Map<Integer, Movie> movies; // Mapping of movie document IDs to Movie objects + private int totalDocuments; // Total number of movies/documents + private double avgDocumentLength; // Average length of documents (number of words) + private static final double K = 1.5; // BM25 tuning parameter, controls term frequency saturation + private static final double B = 0.75; // BM25 tuning parameter, controls length normalization + + /** + * Constructor for BM25InvertedIndex. + * Initializes the inverted index and movie storage. + */ + BM25InvertedIndex() { + index = new HashMap<>(); + movies = new HashMap<>(); + totalDocuments = 0; + avgDocumentLength = 0.0; + } + + /** + * Add a movie to the index. + * @param docId Unique identifier for the movie. + * @param name Name of the movie. + * @param imdbRating IMDb rating of the movie. + * @param releaseYear Release year of the movie. + * @param content Content or description of the movie. + */ + public void addMovie(int docId, String name, double imdbRating, int releaseYear, String content) { + Movie movie = new Movie(docId, name, imdbRating, releaseYear, content); + movies.put(docId, movie); + totalDocuments++; + + // Get words (terms) from the movie's name and content + String[] terms = movie.getWords(); + int docLength = terms.length; + + // Update the average document length + avgDocumentLength = (avgDocumentLength * (totalDocuments - 1) + docLength) / totalDocuments; + + // Update the inverted index + for (String term : terms) { + // Create a new entry if the term is not yet in the index + index.putIfAbsent(term, new HashMap<>()); + + // Get the list of documents containing the term + Map<Integer, Integer> docList = index.get(term); + if (docList == null) { + docList = new HashMap<>(); + index.put(term, docList); // Ensure docList is added to the index + } + // Increment the term frequency in this document + docList.put(docId, docList.getOrDefault(docId, 0) + 1); + } + } + + public int getMoviesLength() { + return movies.size(); + } + + /** + * Search for documents containing a term using BM25 scoring. + * @param term The search term. + * @return A list of search results sorted by relevance score. + */ + public List<SearchResult> search(String term) { + term = term.toLowerCase(); // Normalize search term + if (!index.containsKey(term)) { + return new ArrayList<>(); // Return empty list if term not found + } + + Map<Integer, Integer> termDocs = index.get(term); // Documents containing the term + List<SearchResult> results = new ArrayList<>(); + + // Compute IDF for the search term + double idf = computeIDF(termDocs.size()); + + // Calculate relevance scores for all documents containing the term + for (Map.Entry<Integer, Integer> entry : termDocs.entrySet()) { + int docId = entry.getKey(); + int termFrequency = entry.getValue(); + Movie movie = movies.get(docId); + if (movie == null) { + continue; // Skip this document if movie doesn't exist + } + double docLength = movie.getWords().length; + + // Compute BM25 relevance score + double score = computeBM25Score(termFrequency, docLength, idf); + results.add(new SearchResult(docId, score)); + } + + // Sort the results by relevance score in descending order + results.sort((r1, r2) -> Double.compare(r2.relevanceScore, r1.relevanceScore)); + return results; + } + + /** + * Compute the BM25 score for a given term and document. + * @param termFrequency The frequency of the term in the document. + * @param docLength The length of the document. + * @param idf The inverse document frequency of the term. + * @return The BM25 relevance score for the term in the document. + */ + private double computeBM25Score(int termFrequency, double docLength, double idf) { + double numerator = termFrequency * (K + 1); + double denominator = termFrequency + K * (1 - B + B * (docLength / avgDocumentLength)); + return idf * (numerator / denominator); + } + + /** + * Compute the inverse document frequency (IDF) of a term. + * The IDF measures the importance of a term across the entire document set. + * @param docFrequency The number of documents that contain the term. + * @return The inverse document frequency (IDF) value. + */ + private double computeIDF(int docFrequency) { + // Total number of documents in the index + return Math.log((totalDocuments - docFrequency + 0.5) / (docFrequency + 0.5) + 1); + } +} diff --git a/src/main/java/com/thealgorithms/searches/BinarySearch.java b/src/main/java/com/thealgorithms/searches/BinarySearch.java new file mode 100644 index 000000000000..bedad1667f33 --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/BinarySearch.java @@ -0,0 +1,56 @@ +package com.thealgorithms.searches; + +import com.thealgorithms.devutils.searches.SearchAlgorithm; + +/** + * Binary search is one of the most popular algorithms The algorithm finds the + * position of a target value within a sorted array + * + * <p> + * Worst-case performance O(log n) Best-case performance O(1) Average + * performance O(log n) Worst-case space complexity O(1) + * + * @author Varun Upadhyay (https://github.com/varunu28) + * @author Podshivalov Nikita (https://github.com/nikitap492) + * @see SearchAlgorithm + * @see IterativeBinarySearch + */ +class BinarySearch implements SearchAlgorithm { + + /** + * @param array is an array where the element should be found + * @param key is an element which should be found + * @param <T> is any comparable type + * @return index of the element + */ + @Override + public <T extends Comparable<T>> int find(T[] array, T key) { + return search(array, key, 0, array.length - 1); + } + + /** + * This method implements the Generic Binary Search + * + * @param array The array to make the binary search + * @param key The number you are looking for + * @param left The lower bound + * @param right The upper bound + * @return the location of the key + */ + private <T extends Comparable<T>> int search(T[] array, T key, int left, int right) { + if (right < left) { + return -1; // this means that the key not found + } + // find median + int median = (left + right) >>> 1; + int comp = key.compareTo(array[median]); + + if (comp == 0) { + return median; + } else if (comp < 0) { + return search(array, key, left, median - 1); + } else { + return search(array, key, median + 1, right); + } + } +} diff --git a/src/main/java/com/thealgorithms/searches/BinarySearch2dArray.java b/src/main/java/com/thealgorithms/searches/BinarySearch2dArray.java new file mode 100644 index 000000000000..aa938447b864 --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/BinarySearch2dArray.java @@ -0,0 +1,127 @@ +package com.thealgorithms.searches; + +/** + * This class provides a method to search for a target value in a 2D sorted + * array. + * The search is performed using a combination of binary search on rows and + * columns. + * The 2D array must be strictly sorted in both rows and columns. + * + * The algorithm works by: + * 1. Performing a binary search on the middle column of the 2D array. + * 2. Depending on the value found, it eliminates rows above or below the middle + * element. + * 3. After finding or eliminating rows, it further applies binary search in the + * relevant columns. + */ +public final class BinarySearch2dArray { + + private BinarySearch2dArray() { + } + + /** + * Performs a binary search on a 2D sorted array to find the target value. + * The array must be sorted in ascending order in both rows and columns. + * + * @param arr The 2D array to search in. + * @param target The value to search for. + * @return An array containing the row and column indices of the target, or [-1, + * -1] if the target is not found. + */ + static int[] binarySearch(int[][] arr, int target) { + int rowCount = arr.length; + int colCount = arr[0].length; + + // Edge case: If there's only one row, search that row directly. + if (rowCount == 1) { + return binarySearch(arr, target, 0, 0, colCount); + } + + // Set initial boundaries for binary search on rows. + int startRow = 0; + int endRow = rowCount - 1; + int midCol = colCount / 2; // Middle column index for comparison. + + // Perform binary search on rows based on the middle column. + while (startRow < endRow - 1) { + int midRow = startRow + (endRow - startRow) / 2; + + // If the middle element matches the target, return its position. + if (arr[midRow][midCol] == target) { + return new int[] {midRow, midCol}; + } + // If the middle element is smaller than the target, discard the upper half. + else if (arr[midRow][midCol] < target) { + startRow = midRow; + } + // If the middle element is larger than the target, discard the lower half. + else { + endRow = midRow; + } + } + + // If the target wasn't found during the row search, check the middle column of + // startRow and endRow. + if (arr[startRow][midCol] == target) { + return new int[] {startRow, midCol}; + } + + if (arr[endRow][midCol] == target) { + return new int[] {endRow, midCol}; + } + + // If target is smaller than the element in the left of startRow, perform a + // binary search on the left of startRow. + if (target <= arr[startRow][midCol - 1]) { + return binarySearch(arr, target, startRow, 0, midCol - 1); + } + + // If target is between midCol and the last column of startRow, perform a binary + // search on that part of the row. + if (target >= arr[startRow][midCol + 1] && target <= arr[startRow][colCount - 1]) { + return binarySearch(arr, target, startRow, midCol + 1, colCount - 1); + } + + // If target is smaller than the element in the left of endRow, perform a binary + // search on the left of endRow. + if (target <= arr[endRow][midCol - 1]) { + return binarySearch(arr, target, endRow, 0, midCol - 1); + } else { + // Otherwise, search on the right of endRow. + return binarySearch(arr, target, endRow, midCol + 1, colCount - 1); + } + } + + /** + * Performs a binary search on a specific row of the 2D array. + * + * @param arr The 2D array to search in. + * @param target The value to search for. + * @param row The row index where the target will be searched. + * @param colStart The starting column index for the search. + * @param colEnd The ending column index for the search. + * @return An array containing the row and column indices of the target, or [-1, + * -1] if the target is not found. + */ + static int[] binarySearch(int[][] arr, int target, int row, int colStart, int colEnd) { + // Perform binary search within the specified column range. + while (colStart <= colEnd) { + int midIndex = colStart + (colEnd - colStart) / 2; + + // If the middle element matches the target, return its position. + if (arr[row][midIndex] == target) { + return new int[] {row, midIndex}; + } + // If the middle element is smaller than the target, move to the right half. + else if (arr[row][midIndex] < target) { + colStart = midIndex + 1; + } + // If the middle element is larger than the target, move to the left half. + else { + colEnd = midIndex - 1; + } + } + + return new int[] {-1, -1}; // Target not found + } +} diff --git a/src/main/java/com/thealgorithms/searches/BreadthFirstSearch.java b/src/main/java/com/thealgorithms/searches/BreadthFirstSearch.java new file mode 100644 index 000000000000..7ac9c7b01526 --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/BreadthFirstSearch.java @@ -0,0 +1,71 @@ +package com.thealgorithms.searches; + +import com.thealgorithms.datastructures.Node; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Queue; +import java.util.Set; + +/** + * Breadth-First Search implementation for tree/graph traversal. + * @author caos321 + * @co-author @manishraj27 + * @see <a href="https://en.wikipedia.org/wiki/Breadth-first_search">Breadth-first search</a> + */ +public class BreadthFirstSearch<T> { + private final List<T> visited = new ArrayList<>(); + private final Set<T> visitedSet = new HashSet<>(); + + /** + * Performs a breadth-first search to find a node with the given value. + * + * @param root The root node to start the search from + * @param value The value to search for + * @return Optional containing the found node, or empty if not found + */ + public Optional<Node<T>> search(final Node<T> root, final T value) { + if (root == null) { + return Optional.empty(); + } + + visited.add(root.getValue()); + visitedSet.add(root.getValue()); + + if (root.getValue() == value) { + return Optional.of(root); + } + + Queue<Node<T>> queue = new ArrayDeque<>(root.getChildren()); + while (!queue.isEmpty()) { + final Node<T> current = queue.poll(); + T currentValue = current.getValue(); + + if (visitedSet.contains(currentValue)) { + continue; + } + + visited.add(currentValue); + visitedSet.add(currentValue); + + if (currentValue == value || (value != null && value.equals(currentValue))) { + return Optional.of(current); + } + + queue.addAll(current.getChildren()); + } + + return Optional.empty(); + } + + /** + * Returns the list of nodes in the order they were visited. + * + * @return List containing the visited nodes + */ + public List<T> getVisited() { + return visited; + } +} diff --git a/src/main/java/com/thealgorithms/searches/DepthFirstSearch.java b/src/main/java/com/thealgorithms/searches/DepthFirstSearch.java new file mode 100644 index 000000000000..781768d8049e --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/DepthFirstSearch.java @@ -0,0 +1,32 @@ +package com.thealgorithms.searches; + +import com.thealgorithms.datastructures.Node; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * @author: caos321 + * @date: 31 October 2021 (Sunday) + * @wiki: https://en.wikipedia.org/wiki/Depth-first_search + */ +public class DepthFirstSearch<T> { + + private final List<T> visited = new ArrayList<>(); + + public Optional<Node<T>> recursiveSearch(final Node<T> node, final Integer value) { + if (node == null) { + return Optional.empty(); + } + visited.add(node.getValue()); + if (node.getValue().equals(value)) { + return Optional.of(node); + } + + return node.getChildren().stream().map(v -> recursiveSearch(v, value)).flatMap(Optional::stream).findAny(); + } + + public List<T> getVisited() { + return visited; + } +} diff --git a/src/main/java/com/thealgorithms/searches/ExponentalSearch.java b/src/main/java/com/thealgorithms/searches/ExponentalSearch.java new file mode 100644 index 000000000000..9187dcbc2f4b --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/ExponentalSearch.java @@ -0,0 +1,51 @@ +package com.thealgorithms.searches; + +import com.thealgorithms.devutils.searches.SearchAlgorithm; +import java.util.Arrays; + +/** + * ExponentialSearch is an algorithm that efficiently finds the position of a target + * value within a sorted array. It works by expanding the range to find the bounds + * where the target might exist and then using binary search within that range. + * + * <p> + * Worst-case time complexity: O(log n) + * Best-case time complexity: O(1) when the element is found at the first position. + * Average time complexity: O(log n) + * Worst-case space complexity: O(1) + * </p> + * + * <p> + * Note: This algorithm requires that the input array be sorted. + * </p> + */ +class ExponentialSearch implements SearchAlgorithm { + + /** + * Finds the index of the specified key in a sorted array using exponential search. + * + * @param array The sorted array to search. + * @param key The element to search for. + * @param <T> The type of the elements in the array, which must be comparable. + * @return The index of the key if found, otherwise -1. + */ + @Override + public <T extends Comparable<T>> int find(T[] array, T key) { + if (array.length == 0) { + return -1; + } + if (array[0].equals(key)) { + return 0; + } + if (array[array.length - 1].equals(key)) { + return array.length - 1; + } + + int range = 1; + while (range < array.length && array[range].compareTo(key) < 0) { + range = range * 2; + } + + return Arrays.binarySearch(array, range / 2, Math.min(range, array.length), key); + } +} diff --git a/src/main/java/com/thealgorithms/searches/FibonacciSearch.java b/src/main/java/com/thealgorithms/searches/FibonacciSearch.java new file mode 100644 index 000000000000..2124938bc258 --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/FibonacciSearch.java @@ -0,0 +1,86 @@ +package com.thealgorithms.searches; + +import com.thealgorithms.devutils.searches.SearchAlgorithm; + +/** + * FibonacciSearch is a search algorithm that finds the position of a target value in + * a sorted array using Fibonacci numbers. + * + * <p> + * The time complexity for this search algorithm is O(log n). + * The space complexity for this search algorithm is O(1). + * </p> + * + * <p> + * Note: This algorithm requires that the input array be sorted. + * </p> + */ +public class FibonacciSearch implements SearchAlgorithm { + + /** + * Finds the index of the specified key in a sorted array using Fibonacci search. + * + * @param array The sorted array to search. + * @param key The element to search for. + * @param <T> The type of the elements in the array, which must be comparable. + * @throws IllegalArgumentException if the input array is not sorted or empty, or if the key is null. + * @return The index of the key if found, otherwise -1. + */ + @Override + public <T extends Comparable<T>> int find(T[] array, T key) { + if (array.length == 0) { + throw new IllegalArgumentException("Input array must not be empty."); + } + if (!isSorted(array)) { + throw new IllegalArgumentException("Input array must be sorted."); + } + if (key == null) { + throw new IllegalArgumentException("Key must not be null."); + } + + int fibMinus1 = 1; + int fibMinus2 = 0; + int fibNumber = fibMinus1 + fibMinus2; + int n = array.length; + + while (fibNumber < n) { + fibMinus2 = fibMinus1; + fibMinus1 = fibNumber; + fibNumber = fibMinus2 + fibMinus1; + } + + int offset = -1; + + while (fibNumber > 1) { + int i = Math.min(offset + fibMinus2, n - 1); + + if (array[i].compareTo(key) < 0) { + fibNumber = fibMinus1; + fibMinus1 = fibMinus2; + fibMinus2 = fibNumber - fibMinus1; + offset = i; + } else if (array[i].compareTo(key) > 0) { + fibNumber = fibMinus2; + fibMinus1 = fibMinus1 - fibMinus2; + fibMinus2 = fibNumber - fibMinus1; + } else { + return i; + } + } + + if (fibMinus1 == 1 && array[offset + 1] == key) { + return offset + 1; + } + + return -1; + } + + private boolean isSorted(Comparable[] array) { + for (int i = 1; i < array.length; i++) { + if (array[i - 1].compareTo(array[i]) > 0) { + return false; + } + } + return true; + } +} diff --git a/src/main/java/com/thealgorithms/searches/HowManyTimesRotated.java b/src/main/java/com/thealgorithms/searches/HowManyTimesRotated.java new file mode 100644 index 000000000000..dd01378f4d5f --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/HowManyTimesRotated.java @@ -0,0 +1,63 @@ +package com.thealgorithms.searches; + +import java.util.Scanner; + +/* + Problem Statement: + Given an array, find out how many times it has to been rotated + from its initial sorted position. + Input-Output: + Eg. [11,12,15,18,2,5,6,8] + It has been rotated: 4 times + (One rotation means putting the first element to the end) + Note: The array cannot contain duplicates + + Logic: + The position of the minimum element will give the number of times the array has been rotated + from its initial sorted position. + Eg. For [2,5,6,8,11,12,15,18], 1 rotation gives [5,6,8,11,12,15,18,2], 2 rotations + [6,8,11,12,15,18,2,5] and so on. Finding the minimum element will take O(N) time but, we can use + Binary Search to find the minimum element, we can reduce the complexity to O(log N). If we look + at the rotated array, to identify the minimum element (say a[i]), we observe that + a[i-1]>a[i]<a[i+1]. + + Some other test cases: + 1. [1,2,3,4] Number of rotations: 0 or 4(Both valid) + 2. [15,17,2,3,5] Number of rotations: 3 + */ +final class HowManyTimesRotated { + private HowManyTimesRotated() { + } + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + int n = sc.nextInt(); + int[] a = new int[n]; + for (int i = 0; i < n; i++) { + a[i] = sc.nextInt(); + } + + System.out.println("The array has been rotated " + rotated(a) + " times"); + sc.close(); + } + + public static int rotated(int[] a) { + int low = 0; + int high = a.length - 1; + int mid = 0; // low + (high-low)/2 = (low + high)/2 + + while (low <= high) { + mid = low + (high - low) / 2; + + if (a[mid] < a[mid - 1] && a[mid] < a[mid + 1]) { + break; + } else if (a[mid] > a[mid - 1] && a[mid] < a[mid + 1]) { + high = mid + 1; + } else if (a[mid] > a[mid - 1] && a[mid] > a[mid + 1]) { + low = mid - 1; + } + } + + return mid; + } +} diff --git a/src/main/java/com/thealgorithms/searches/InterpolationSearch.java b/src/main/java/com/thealgorithms/searches/InterpolationSearch.java new file mode 100644 index 000000000000..3ac6be25bf53 --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/InterpolationSearch.java @@ -0,0 +1,57 @@ +package com.thealgorithms.searches; + +/** + * InterpolationSearch is an algorithm that searches for a target value within a sorted array + * by estimating the position based on the values at the corners of the current search range. + * + * <p> + * The performance of this algorithm can vary: + * - Worst-case performance: O(n) + * - Best-case performance: O(1) + * - Average performance: O(log(log(n))) if the elements are uniformly distributed; otherwise O(n) + * - Worst-case space complexity: O(1) + * </p> + * + * <p> + * This search algorithm requires the input array to be sorted. + * </p> + * + * @author Podshivalov Nikita (https://github.com/nikitap492) + */ +class InterpolationSearch { + + /** + * Finds the index of the specified key in a sorted array using interpolation search. + * + * @param array The sorted array to search. + * @param key The value to search for. + * @return The index of the key if found, otherwise -1. + */ + public int find(int[] array, int key) { + // Find indexes of two corners + int start = 0; + int end = (array.length - 1); + + // Since array is sorted, an element present + // in array must be in range defined by corner + while (start <= end && key >= array[start] && key <= array[end]) { + // Probing the position with keeping + // uniform distribution in mind. + int pos = start + (((end - start) / (array[end] - array[start])) * (key - array[start])); + + // Condition of target found + if (array[pos] == key) { + return pos; + } + + // If key is larger, key is in upper part + if (array[pos] < key) { + start = pos + 1; + } // If key is smaller, x is in lower part + else { + end = pos - 1; + } + } + return -1; + } +} diff --git a/src/main/java/com/thealgorithms/searches/IterativeBinarySearch.java b/src/main/java/com/thealgorithms/searches/IterativeBinarySearch.java new file mode 100644 index 000000000000..05fab0534267 --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/IterativeBinarySearch.java @@ -0,0 +1,55 @@ +package com.thealgorithms.searches; + +import com.thealgorithms.devutils.searches.SearchAlgorithm; + +/** + * Binary search is one of the most popular algorithms This class represents + * iterative version {@link BinarySearch} Iterative binary search is likely to + * have lower constant factors because it doesn't involve the overhead of + * manipulating the call stack. But in java the recursive version can be + * optimized by the compiler to this version. + * + * <p> + * Worst-case performance O(log n) Best-case performance O(1) Average + * performance O(log n) Worst-case space complexity O(1) + * + * @author Gabriele La Greca : https://github.com/thegabriele97 + * @author Podshivalov Nikita (https://github.com/nikitap492) + * @see SearchAlgorithm + * @see BinarySearch + */ +public final class IterativeBinarySearch implements SearchAlgorithm { + + /** + * This method implements an iterative version of binary search algorithm + * + * @param array a sorted array + * @param key the key to search in array + * @return the index of key in the array or -1 if not found + */ + @Override + public <T extends Comparable<T>> int find(T[] array, T key) { + int l; + int r; + int k; + int cmp; + + l = 0; + r = array.length - 1; + + while (l <= r) { + k = (l + r) >>> 1; + cmp = key.compareTo(array[k]); + + if (cmp == 0) { + return k; + } else if (cmp < 0) { + r = --k; + } else { + l = ++k; + } + } + + return -1; + } +} diff --git a/src/main/java/com/thealgorithms/searches/IterativeTernarySearch.java b/src/main/java/com/thealgorithms/searches/IterativeTernarySearch.java new file mode 100644 index 000000000000..585d6787d3f8 --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/IterativeTernarySearch.java @@ -0,0 +1,64 @@ +package com.thealgorithms.searches; + +import com.thealgorithms.devutils.searches.SearchAlgorithm; + +/** + * An iterative implementation of the Ternary Search algorithm. + * + * <p> + * Ternary search is a divide-and-conquer algorithm that splits the array into three parts + * instead of two, as in binary search. This implementation is iterative, reducing the overhead + * associated with recursive function calls. However, the recursive version can also be optimized + * by the Java compiler to resemble the iterative version, resulting in similar performance. + * + * <p> + * Worst-case performance: Θ(log3(N))<br> + * Best-case performance: O(1)<br> + * Average performance: Θ(log3(N))<br> + * Worst-case space complexity: O(1) + * + * <p> + * This class implements the {@link SearchAlgorithm} interface, providing a generic search method + * for any comparable type. + * + * @see SearchAlgorithm + * @see TernarySearch + * @since 2018-04-13 + */ +public class IterativeTernarySearch implements SearchAlgorithm { + + @Override + public <T extends Comparable<T>> int find(T[] array, T key) { + if (array == null || array.length == 0 || key == null) { + return -1; + } + if (array.length == 1) { + return array[0].compareTo(key) == 0 ? 0 : -1; + } + + int left = 0; + int right = array.length - 1; + + while (right > left) { + int leftCmp = array[left].compareTo(key); + int rightCmp = array[right].compareTo(key); + if (leftCmp == 0) { + return left; + } + if (rightCmp == 0) { + return right; + } + + int leftThird = left + (right - left) / 3 + 1; + int rightThird = right - (right - left) / 3 - 1; + + if (array[leftThird].compareTo(key) <= 0) { + left = leftThird; + } else { + right = rightThird; + } + } + + return -1; + } +} diff --git a/src/main/java/com/thealgorithms/searches/JumpSearch.java b/src/main/java/com/thealgorithms/searches/JumpSearch.java new file mode 100644 index 000000000000..8dcec3a819a4 --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/JumpSearch.java @@ -0,0 +1,56 @@ +package com.thealgorithms.searches; + +import com.thealgorithms.devutils.searches.SearchAlgorithm; + +/** + * An implementation of the Jump Search algorithm. + * + * <p> + * Jump Search is an algorithm for searching sorted arrays. It works by dividing the array + * into blocks of a fixed size (the block size is typically the square root of the array length) + * and jumping ahead by this block size to find a range where the target element may be located. + * Once the range is found, a linear search is performed within that block. + * + * <p> + * The Jump Search algorithm is particularly effective for large sorted arrays where the cost of + * performing a linear search on the entire array would be prohibitive. + * + * <p> + * Worst-case performance: O(√N)<br> + * Best-case performance: O(1)<br> + * Average performance: O(√N)<br> + * Worst-case space complexity: O(1) + * + * <p> + * This class implements the {@link SearchAlgorithm} interface, providing a generic search method + * for any comparable type. + */ +public class JumpSearch implements SearchAlgorithm { + + /** + * Jump Search algorithm implementation. + * + * @param array the sorted array containing elements + * @param key the element to be searched + * @return the index of {@code key} if found, otherwise -1 + */ + @Override + public <T extends Comparable<T>> int find(T[] array, T key) { + int length = array.length; + int blockSize = (int) Math.sqrt(length); + + int limit = blockSize; + // Jumping ahead to find the block where the key may be located + while (limit < length && key.compareTo(array[limit]) > 0) { + limit = Math.min(limit + blockSize, length - 1); + } + + // Perform linear search within the identified block + for (int i = limit - blockSize; i <= limit && i < length; i++) { + if (array[i].equals(key)) { + return i; + } + } + return -1; + } +} diff --git a/src/main/java/com/thealgorithms/searches/KMPSearch.java b/src/main/java/com/thealgorithms/searches/KMPSearch.java new file mode 100644 index 000000000000..8bf2754ff8f7 --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/KMPSearch.java @@ -0,0 +1,74 @@ +package com.thealgorithms.searches; + +class KMPSearch { + + int kmpSearch(String pat, String txt) { + int m = pat.length(); + int n = txt.length(); + + // create lps[] that will hold the longest + // prefix suffix values for pattern + int[] lps = new int[m]; + int j = 0; // index for pat[] + + // Preprocess the pattern (calculate lps[] + // array) + computeLPSArray(pat, m, lps); + + int i = 0; // index for txt[] + while ((n - i) >= (m - j)) { + if (pat.charAt(j) == txt.charAt(i)) { + j++; + i++; + } + if (j == m) { + System.out.println("Found pattern " + + "at index " + (i - j)); + int index = (i - j); + j = lps[j - 1]; + return index; + } + // mismatch after j matches + else if (i < n && pat.charAt(j) != txt.charAt(i)) { + // Do not match lps[0..lps[j-1]] characters, + // they will match anyway + if (j != 0) { + j = lps[j - 1]; + } else { + i = i + 1; + } + } + } + System.out.println("No pattern found"); + return -1; + } + + void computeLPSArray(String pat, int m, int[] lps) { + // length of the previous longest prefix suffix + int len = 0; + int i = 1; + lps[0] = 0; // lps[0] is always 0 + + // the loop calculates lps[i] for i = 1 to m-1 + while (i < m) { + if (pat.charAt(i) == pat.charAt(len)) { + len++; + lps[i] = len; + i++; + } else { // (pat[i] != pat[len]) + // This is tricky. Consider the example. + // AAACAAAA and i = 7. The idea is similar + // to search step. + if (len != 0) { + len = lps[len - 1]; + // Also, note that we do not increment + // i here + } else { // if (len == 0) + lps[i] = len; + i++; + } + } + } + } +} +// This code has been contributed by Amit Khandelwal. diff --git a/src/main/java/com/thealgorithms/searches/LinearSearch.java b/src/main/java/com/thealgorithms/searches/LinearSearch.java new file mode 100644 index 000000000000..c7b70edb5112 --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/LinearSearch.java @@ -0,0 +1,37 @@ +package com.thealgorithms.searches; + +import com.thealgorithms.devutils.searches.SearchAlgorithm; + +/** + * Linear search is the easiest search algorithm It works with sorted and + * unsorted arrays (an binary search works only with sorted array) This + * algorithm just compares all elements of an array to find a value + * + * <p> + * Worst-case performance O(n) Best-case performance O(1) Average performance + * O(n) Worst-case space complexity + * + * @author Varun Upadhyay (https://github.com/varunu28) + * @author Podshivalov Nikita (https://github.com/nikitap492) + * @see BinarySearch + * @see SearchAlgorithm + */ +public class LinearSearch implements SearchAlgorithm { + + /** + * Generic Linear search method + * + * @param array List to be searched + * @param value Key being searched for + * @return Location of the key + */ + @Override + public <T extends Comparable<T>> int find(T[] array, T value) { + for (int i = 0; i < array.length; i++) { + if (array[i].compareTo(value) == 0) { + return i; + } + } + return -1; + } +} diff --git a/src/main/java/com/thealgorithms/searches/LinearSearchThread.java b/src/main/java/com/thealgorithms/searches/LinearSearchThread.java new file mode 100644 index 000000000000..ba3cff915f5f --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/LinearSearchThread.java @@ -0,0 +1,74 @@ +package com.thealgorithms.searches; + +/** + * LinearSearchThread is a multithreaded implementation of the linear search algorithm. + * It creates multiple threads to search for a specific number in an array. + * + * <p> + * The class generates an array of random integers, prompts the user to enter a number to search for, + * and divides the array into four segments, each handled by a separate thread. + * The threads run concurrently and search for the specified number within their designated segments. + * Finally, it consolidates the results to determine if the number was found. + * </p> + * + * <p> + * Example usage: + * 1. The program will output the generated array. + * 2. The user will be prompted to input a number to search for. + * 3. The program will display whether the number was found in the array. + * </p> + */ +public final class LinearSearchThread { + private LinearSearchThread() { + } +} + +/** + * The Searcher class extends Thread and is responsible for searching for a specific + * number in a segment of an array. + */ +class Searcher extends Thread { + private final int[] arr; // The array to search in + private final int left; // Starting index of the segment + private final int right; // Ending index of the segment + private final int x; // The number to search for + private boolean found; // Result flag + + /** + * Constructor to initialize the Searcher. + * + * @param arr The array to search in + * @param left The starting index of the segment + * @param right The ending index of the segment + * @param x The number to search for + */ + Searcher(int[] arr, int left, int right, int x) { + this.arr = arr; + this.left = left; + this.right = right; + this.x = x; + } + + /** + * The run method for the thread, performing the linear search in its segment. + */ + @Override + public void run() { + int k = left; + found = false; + while (k < right && !found) { + if (arr[k++] == x) { + found = true; + } + } + } + + /** + * Returns whether the number was found in the segment. + * + * @return true if the number was found, false otherwise + */ + boolean getResult() { + return found; + } +} diff --git a/src/main/java/com/thealgorithms/searches/LowerBound.java b/src/main/java/com/thealgorithms/searches/LowerBound.java new file mode 100644 index 000000000000..5a1401edd3c2 --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/LowerBound.java @@ -0,0 +1,64 @@ +package com.thealgorithms.searches; + +import com.thealgorithms.devutils.searches.SearchAlgorithm; + +/** + * The LowerBound method is used to return an index pointing to the first + * element in the range [first, last) which has a value not less than val, i.e. + * the index of the next smallest number just greater than or equal to that + * number. If there are multiple values that are equal to val it returns the + * index of the first such value. + * + * <p> + * This is an extension of BinarySearch. + * + * <p> + * Worst-case performance O(log n) Best-case performance O(1) Average + * performance O(log n) Worst-case space complexity O(1) + * + * @author Pratik Padalia (https://github.com/15pratik) + * @see SearchAlgorithm + * @see BinarySearch + */ +class LowerBound implements SearchAlgorithm { + + /** + * @param array is an array where the LowerBound value is to be found + * @param key is an element for which the LowerBound is to be found + * @param <T> is any comparable type + * @return index of the LowerBound element + */ + @Override + public <T extends Comparable<T>> int find(T[] array, T key) { + return search(array, key, 0, array.length - 1); + } + + /** + * This method implements the Generic Binary Search + * + * @param array The array to make the binary search + * @param key The number you are looking for + * @param left The lower bound + * @param right The upper bound + * @return the location of the key + */ + private <T extends Comparable<T>> int search(T[] array, T key, int left, int right) { + if (right <= left) { + return left; + } + + // find median + int median = (left + right) >>> 1; + int comp = key.compareTo(array[median]); + + if (comp == 0) { + return median; + } else if (comp < 0) { + // median position can be a possible solution + return search(array, key, left, median); + } else { + // key we are looking is greater, so we must look on the right of median position + return search(array, key, median + 1, right); + } + } +} diff --git a/src/main/java/com/thealgorithms/searches/MonteCarloTreeSearch.java b/src/main/java/com/thealgorithms/searches/MonteCarloTreeSearch.java new file mode 100644 index 000000000000..aa74398b708b --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/MonteCarloTreeSearch.java @@ -0,0 +1,173 @@ +package com.thealgorithms.searches; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Random; + +/** + * Monte Carlo Tree Search (MCTS) is a heuristic search algorithm used in + * decition taking problems especially games. + * + * See more: https://en.wikipedia.org/wiki/Monte_Carlo_tree_search, + * https://www.baeldung.com/java-monte-carlo-tree-search + */ +public class MonteCarloTreeSearch { + + public class Node { + + Node parent; + ArrayList<Node> childNodes; + boolean isPlayersTurn; // True if it is the player's turn. + boolean playerWon; // True if the player won; false if the opponent won. + int score; + int visitCount; + + public Node() { + } + + public Node(Node parent, boolean isPlayersTurn) { + this.parent = parent; + childNodes = new ArrayList<>(); + this.isPlayersTurn = isPlayersTurn; + playerWon = false; + score = 0; + visitCount = 0; + } + } + + static final int WIN_SCORE = 10; + static final int TIME_LIMIT = 500; // Time the algorithm will be running for (in milliseconds). + + /** + * Explores a game tree using Monte Carlo Tree Search (MCTS) and returns the + * most promising node. + * + * @param rootNode Root node of the game tree. + * @return The most promising child of the root node. + */ + public Node monteCarloTreeSearch(Node rootNode) { + Node winnerNode; + double timeLimit; + + // Expand the root node. + addChildNodes(rootNode, 10); + + timeLimit = System.currentTimeMillis() + TIME_LIMIT; + + // Explore the tree until the time limit is reached. + while (System.currentTimeMillis() < timeLimit) { + Node promisingNode; + + // Get a promising node using UCT. + promisingNode = getPromisingNode(rootNode); + + // Expand the promising node. + if (promisingNode.childNodes.size() == 0) { + addChildNodes(promisingNode, 10); + } + + simulateRandomPlay(promisingNode); + } + + winnerNode = getWinnerNode(rootNode); + printScores(rootNode); + System.out.format("%nThe optimal node is: %02d%n", rootNode.childNodes.indexOf(winnerNode) + 1); + + return winnerNode; + } + + public void addChildNodes(Node node, int childCount) { + for (int i = 0; i < childCount; i++) { + node.childNodes.add(new Node(node, !node.isPlayersTurn)); + } + } + + /** + * Uses UCT to find a promising child node to be explored. + * + * UCT: Upper Confidence bounds applied to Trees. + * + * @param rootNode Root node of the tree. + * @return The most promising node according to UCT. + */ + public Node getPromisingNode(Node rootNode) { + Node promisingNode = rootNode; + + // Iterate until a node that hasn't been expanded is found. + while (promisingNode.childNodes.size() != 0) { + double uctIndex = Double.MIN_VALUE; + int nodeIndex = 0; + + // Iterate through child nodes and pick the most promising one + // using UCT (Upper Confidence bounds applied to Trees). + for (int i = 0; i < promisingNode.childNodes.size(); i++) { + Node childNode = promisingNode.childNodes.get(i); + double uctTemp; + + // If child node has never been visited + // it will have the highest uct value. + if (childNode.visitCount == 0) { + nodeIndex = i; + break; + } + + uctTemp = ((double) childNode.score / childNode.visitCount) + 1.41 * Math.sqrt(Math.log(promisingNode.visitCount) / (double) childNode.visitCount); + + if (uctTemp > uctIndex) { + uctIndex = uctTemp; + nodeIndex = i; + } + } + + promisingNode = promisingNode.childNodes.get(nodeIndex); + } + + return promisingNode; + } + + /** + * Simulates a random play from a nodes current state and back propagates + * the result. + * + * @param promisingNode Node that will be simulated. + */ + public void simulateRandomPlay(Node promisingNode) { + Random rand = new Random(); + Node tempNode = promisingNode; + boolean isPlayerWinner; + + // The following line randomly determines whether the simulated play is a win or loss. + // To use the MCTS algorithm correctly this should be a simulation of the nodes' current + // state of the game until it finishes (if possible) and use an evaluation function to + // determine how good or bad the play was. + // e.g. Play tic tac toe choosing random squares until the game ends. + promisingNode.playerWon = (rand.nextInt(6) == 0); + + isPlayerWinner = promisingNode.playerWon; + + // Back propagation of the random play. + while (tempNode != null) { + tempNode.visitCount++; + + // Add wining scores to bouth player and opponent depending on the turn. + if ((tempNode.isPlayersTurn && isPlayerWinner) || (!tempNode.isPlayersTurn && !isPlayerWinner)) { + tempNode.score += WIN_SCORE; + } + + tempNode = tempNode.parent; + } + } + + public Node getWinnerNode(Node rootNode) { + return Collections.max(rootNode.childNodes, Comparator.comparing(c -> c.score)); + } + + public void printScores(Node rootNode) { + System.out.println("N.\tScore\t\tVisits"); + + for (int i = 0; i < rootNode.childNodes.size(); i++) { + System.out.printf("%02d\t%d\t\t%d%n", i + 1, rootNode.childNodes.get(i).score, rootNode.childNodes.get(i).visitCount); + } + } +} diff --git a/src/main/java/com/thealgorithms/searches/OrderAgnosticBinarySearch.java b/src/main/java/com/thealgorithms/searches/OrderAgnosticBinarySearch.java new file mode 100644 index 000000000000..d85cb37c4427 --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/OrderAgnosticBinarySearch.java @@ -0,0 +1,49 @@ +package com.thealgorithms.searches; + +// URL: https://www.geeksforgeeks.org/order-agnostic-binary-search/ + +/* Order Agnostic Binary Search is an algorithm where we do not know whether the given + sorted array is ascending or descending order. + We declare a boolean variable to find whether the array is ascending order. + In the while loop, we use the two pointer method (start and end) to get the middle element. + if the middle element is equal to our target element, then that is the answer. + If not, then we check if the array is ascending or descending order. + Depending upon the condition, respective statements will be executed and we will get our answer. + */ + +public final class OrderAgnosticBinarySearch { + private OrderAgnosticBinarySearch() { + } + + static int binSearchAlgo(int[] arr, int start, int end, int target) { + + // Checking whether the given array is ascending order + boolean ascOrd = arr[start] < arr[end]; + + while (start <= end) { + int middle = start + (end - start) / 2; + + // Check if the desired element is present at the middle position + if (arr[middle] == target) { + return middle; // returns the index of the middle element + } + if (ascOrd) { + // Ascending order + if (arr[middle] < target) { + start = middle + 1; + } else { + end = middle - 1; + } + } else { + // Descending order + if (arr[middle] > target) { + start = middle + 1; + } else { + end = middle - 1; + } + } + } + // Element is not present + return -1; + } +} diff --git a/src/main/java/com/thealgorithms/searches/PerfectBinarySearch.java b/src/main/java/com/thealgorithms/searches/PerfectBinarySearch.java new file mode 100644 index 000000000000..495e2e41bc5b --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/PerfectBinarySearch.java @@ -0,0 +1,54 @@ +package com.thealgorithms.searches; + +import com.thealgorithms.devutils.searches.SearchAlgorithm; + +/** + * Binary search is one of the most popular algorithms The algorithm finds the + * position of a target value within a sorted array + * + * <p> + * Worst-case performance O(log n) Best-case performance O(1) Average + * performance O(log n) Worst-case space complexity O(1) + * + * @author D Sunil (https://github.com/sunilnitdgp) + * @see SearchAlgorithm + */ + +public class PerfectBinarySearch<T> implements SearchAlgorithm { + + /** + * @param array is an array where the element should be found + * @param key is an element which should be found + * @param <T> is any comparable type + * @return index of the element + */ + @Override + public <T extends Comparable<T>> int find(T[] array, T key) { + return search(array, key, 0, array.length - 1); + } + + /** + * This method implements the Generic Binary Search iteratively. + * + * @param array The array to make the binary search + * @param key The number you are looking for + * @return the location of the key, or -1 if not found + */ + private static <T extends Comparable<T>> int search(T[] array, T key, int left, int right) { + while (left <= right) { + int median = (left + right) >>> 1; + int comp = key.compareTo(array[median]); + + if (comp == 0) { + return median; // Key found + } + + if (comp < 0) { + right = median - 1; // Adjust the right bound + } else { + left = median + 1; // Adjust the left bound + } + } + return -1; // Key not found + } +} diff --git a/src/main/java/com/thealgorithms/searches/QuickSelect.java b/src/main/java/com/thealgorithms/searches/QuickSelect.java new file mode 100644 index 000000000000..d5f695293a6a --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/QuickSelect.java @@ -0,0 +1,125 @@ +package com.thealgorithms.searches; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; + +/** + * An implementation of the Quickselect algorithm as described + * <a href="https://en.wikipedia.org/wiki/Median_of_medians">here</a>. + */ +public final class QuickSelect { + private QuickSelect() { + } + + /** + * Selects the {@code n}-th largest element of {@code list}, i.e. the element that would + * be at index n if the list was sorted. + * <p> + * Calling this function might change the order of elements in {@code list}. + * + * @param list the list of elements + * @param n the index + * @param <T> the type of list elements + * @return the n-th largest element in the list + * @throws IndexOutOfBoundsException if n is less than 0 or greater or equal to + * the number of elements in the list + * @throws IllegalArgumentException if the list is empty + * @throws NullPointerException if {@code list} is null + */ + public static <T extends Comparable<T>> T select(List<T> list, int n) { + Objects.requireNonNull(list, "The list of elements must not be null."); + + if (list.isEmpty()) { + String msg = "The list of elements must not be empty."; + throw new IllegalArgumentException(msg); + } + + if (n < 0) { + String msg = "The index must not be negative."; + throw new IndexOutOfBoundsException(msg); + } + + if (n >= list.size()) { + String msg = "The index must be less than the number of elements."; + throw new IndexOutOfBoundsException(msg); + } + + int index = selectIndex(list, n); + return list.get(index); + } + + private static <T extends Comparable<T>> int selectIndex(List<T> list, int n) { + return selectIndex(list, 0, list.size() - 1, n); + } + + private static <T extends Comparable<T>> int selectIndex(List<T> list, int left, int right, int n) { + while (true) { + if (left == right) { + return left; + } + int pivotIndex = pivot(list, left, right); + pivotIndex = partition(list, left, right, pivotIndex, n); + if (n == pivotIndex) { + return n; + } else if (n < pivotIndex) { + right = pivotIndex - 1; + } else { + left = pivotIndex + 1; + } + } + } + + private static <T extends Comparable<T>> int partition(List<T> list, int left, int right, int pivotIndex, int n) { + T pivotValue = list.get(pivotIndex); + Collections.swap(list, pivotIndex, right); + int storeIndex = left; + + for (int i = left; i < right; i++) { + if (list.get(i).compareTo(pivotValue) < 0) { + Collections.swap(list, storeIndex, i); + storeIndex++; + } + } + + int storeIndexEq = storeIndex; + + for (int i = storeIndex; i < right; i++) { + if (list.get(i).compareTo(pivotValue) == 0) { + Collections.swap(list, storeIndexEq, i); + storeIndexEq++; + } + } + + Collections.swap(list, right, storeIndexEq); + + return (n < storeIndex) ? storeIndex : Math.min(n, storeIndexEq); + } + + private static <T extends Comparable<T>> int pivot(List<T> list, int left, int right) { + if (right - left < 5) { + return partition5(list, left, right); + } + + for (int i = left; i < right; i += 5) { + int subRight = i + 4; + if (subRight > right) { + subRight = right; + } + int median5 = partition5(list, i, subRight); + int rightIndex = left + (i - left) / 5; + Collections.swap(list, median5, rightIndex); + } + + int mid = (right - left) / 10 + left + 1; + int rightIndex = left + (right - left) / 5; + return selectIndex(list, left, rightIndex, mid); + } + + private static <T extends Comparable<T>> int partition5(List<T> list, int left, int right) { + List<T> ts = list.subList(left, right); + ts.sort(Comparator.naturalOrder()); + return (left + right) >>> 1; + } +} diff --git a/src/main/java/com/thealgorithms/searches/RabinKarpAlgorithm.java b/src/main/java/com/thealgorithms/searches/RabinKarpAlgorithm.java new file mode 100644 index 000000000000..81e16dbbbf07 --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/RabinKarpAlgorithm.java @@ -0,0 +1,68 @@ +package com.thealgorithms.searches; + +// Implementation of Rabin Karp Algorithm + +public final class RabinKarpAlgorithm { + private RabinKarpAlgorithm() { + } + + private static final int ALPHABET_SIZE = 256; + + public static int search(String pattern, String text, int primeNumber) { + + int index = -1; // -1 here represents not found + int patternLength = pattern.length(); + int textLength = text.length(); + int hashForPattern = 0; + int hashForText = 0; + int h = 1; + + // The value of h would be "pow(d, patternLength-1)%primeNumber" + for (int i = 0; i < patternLength - 1; i++) { + h = (h * ALPHABET_SIZE) % primeNumber; + } + + // Calculate the hash value of pattern and first + // window of text + for (int i = 0; i < patternLength; i++) { + hashForPattern = (ALPHABET_SIZE * hashForPattern + pattern.charAt(i)) % primeNumber; + hashForText = (ALPHABET_SIZE * hashForText + text.charAt(i)) % primeNumber; + } + + // Slide the pattern over text one by one + for (int i = 0; i <= textLength - patternLength; i++) { + /* Check the hash values of current window of text + and pattern. If the hash values match then only + check for characters one by one*/ + + int j = 0; + if (hashForPattern == hashForText) { + /* Check for characters one by one */ + for (j = 0; j < patternLength; j++) { + if (text.charAt(i + j) != pattern.charAt(j)) { + break; + } + } + + // if hashForPattern == hashForText and pattern[0...patternLength-1] = text[i, i+1, ...i+patternLength-1] + if (j == patternLength) { + index = i; + return index; + } + } + + // Calculate hash value for next window of text: Remove + // leading digit, add trailing digit + if (i < textLength - patternLength) { + hashForText = (ALPHABET_SIZE * (hashForText - text.charAt(i) * h) + text.charAt(i + patternLength)) % primeNumber; + + // handling negative hashForText + if (hashForText < 0) { + hashForText = (hashForText + primeNumber); + } + } + } + return index; // return -1 if pattern does not found + } +} +// This code is contributed by nuclode diff --git a/src/main/java/com/thealgorithms/searches/RandomSearch.java b/src/main/java/com/thealgorithms/searches/RandomSearch.java new file mode 100644 index 000000000000..3417ff7ddb21 --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/RandomSearch.java @@ -0,0 +1,45 @@ +package com.thealgorithms.searches; + +import com.thealgorithms.devutils.searches.SearchAlgorithm; +import java.util.HashSet; +import java.util.Random; +import java.util.Set; + +/** + * A Random Search algorithm that randomly selects an index and checks if the + * value at that index matches the target. It repeats the process until it + * finds the target or checks all elements. + * + * <p> + * Time Complexity: O(n) in the worst case. + * </p> + * + * @author Hardvan + */ +public class RandomSearch implements SearchAlgorithm { + + private final Random random = new Random(); + + /** + * Finds the index of a given element using random search. + * + * @param array Array to search through + * @param key Element to search for + * @return Index of the element if found, -1 otherwise + */ + @Override + public <T extends Comparable<T>> int find(T[] array, T key) { + Set<Integer> visitedIndices = new HashSet<>(); + int size = array.length; + + while (visitedIndices.size() < size) { + int randomIndex = random.nextInt(size); + if (array[randomIndex].compareTo(key) == 0) { + return randomIndex; + } + visitedIndices.add(randomIndex); + } + + return -1; + } +} diff --git a/src/main/java/com/thealgorithms/searches/RecursiveBinarySearch.java b/src/main/java/com/thealgorithms/searches/RecursiveBinarySearch.java new file mode 100644 index 000000000000..daf0c12c0978 --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/RecursiveBinarySearch.java @@ -0,0 +1,77 @@ +// Code by Pronay Debnath +// Created:- 1/10/2023 +// File Name should be RecursiveBinarySearch.java +// Explanation:- https://www.tutorialspoint.com/java-program-for-binary-search-recursive +package com.thealgorithms.searches; + +import java.util.Scanner; + +// Create a SearchAlgorithm class with a generic type +abstract class SearchAlgorithm<T extends Comparable<T>> { + // Abstract find method to be implemented by subclasses + public abstract int find(T[] arr, T target); +} + +public class RecursiveBinarySearch<T extends Comparable<T>> extends SearchAlgorithm<T> { + + // Override the find method as required + @Override + public int find(T[] arr, T target) { + // Call the recursive binary search function + return binsear(arr, 0, arr.length - 1, target); + } + + // Recursive binary search function + public int binsear(T[] arr, int left, int right, T target) { + if (right >= left) { + int mid = left + (right - left) / 2; + + // Compare the element at the middle with the target + int comparison = arr[mid].compareTo(target); + + // If the element is equal to the target, return its index + if (comparison == 0) { + return mid; + } + + // If the element is greater than the target, search in the left subarray + if (comparison > 0) { + return binsear(arr, left, mid - 1, target); + } + + // Otherwise, search in the right subarray + return binsear(arr, mid + 1, right, target); + } + + // Element is not present in the array + return -1; + } + + public static void main(String[] args) { + try (Scanner sc = new Scanner(System.in)) { + // User inputs + System.out.print("Enter the number of elements in the array: "); + int n = sc.nextInt(); + + Integer[] a = new Integer[n]; // You can change the array type as needed + + System.out.println("Enter the elements in sorted order:"); + + for (int i = 0; i < n; i++) { + a[i] = sc.nextInt(); + } + + System.out.print("Enter the target element to search for: "); + int t = sc.nextInt(); + + RecursiveBinarySearch<Integer> searcher = new RecursiveBinarySearch<>(); + int res = searcher.find(a, t); + + if (res == -1) { + System.out.println("Element not found in the array."); + } else { + System.out.println("Element found at index " + res); + } + } + } +} diff --git a/src/main/java/com/thealgorithms/searches/RowColumnWiseSorted2dArrayBinarySearch.java b/src/main/java/com/thealgorithms/searches/RowColumnWiseSorted2dArrayBinarySearch.java new file mode 100644 index 000000000000..3b4b0b08377f --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/RowColumnWiseSorted2dArrayBinarySearch.java @@ -0,0 +1,47 @@ +package com.thealgorithms.searches; + +import com.thealgorithms.devutils.searches.MatrixSearchAlgorithm; + +/** + * The search is for any array which is sorted row and column-wise too. For ex : + * {{10, 20, 30, 40}, + * {15, 25, 35, 45}, + * {18, 28, 38, 48}, + * {21, 31, 41, 51}} + * + * This array is sorted in both row and column manner. + * In this two pointers are taken, the first points to the 0th row and the second one points to end + * column, and then the element corresponding to the pointers placed in the array is compared with + * the target that either its equal, greater or smaller than the target. If the element is equal to + * the target, the co-ordinates of that element is returned i.e. an array of the two pointers will + * be returned, else if the target is greater than corresponding element then the pointer pointing + * to the 0th row will be incremented by 1, else if the target is lesser than the corresponding + * element then the pointer pointing to the end column will be decremented by 1. And if the element + * doesn't exist in the array, an array + * {-1, -1} will be returned. + */ +public class RowColumnWiseSorted2dArrayBinarySearch implements MatrixSearchAlgorithm { + + @Override + public <T extends Comparable<T>> int[] find(T[][] matrix, T key) { + return search(matrix, key); + } + + public static <T extends Comparable<T>> int[] search(T[][] matrix, T target) { + int rowPointer = 0; // The pointer at 0th row + int colPointer = matrix.length - 1; // The pointer at end column + + while (rowPointer < matrix.length && colPointer >= 0) { + int comp = target.compareTo(matrix[rowPointer][colPointer]); + + if (comp == 0) { + return new int[] {rowPointer, colPointer}; + } else if (comp > 0) { + rowPointer++; // Incrementing the row pointer if the target is greater + } else { + colPointer--; // Decrementing the column pointer if the target is lesser + } + } + return new int[] {-1, -1}; // The not found condition + } +} diff --git a/src/main/java/com/thealgorithms/searches/SaddlebackSearch.java b/src/main/java/com/thealgorithms/searches/SaddlebackSearch.java new file mode 100644 index 000000000000..192c4d26c735 --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/SaddlebackSearch.java @@ -0,0 +1,53 @@ +package com.thealgorithms.searches; + +/** + * Program to perform Saddleback Search Given a sorted 2D array(elements are + * sorted across every row and column, assuming ascending order) of size n*m we + * can search a given element in O(n+m) + * + * <p> + * we start from bottom left corner if the current element is greater than the + * given element then we move up else we move right Sample Input: 5 5 + * ->Dimensions -10 -5 -3 4 9 -6 -2 0 5 10 -4 -1 1 6 12 2 3 7 8 13 100 120 130 + * 140 150 140 ->element to be searched output: 4 3 // first value is row, + * second one is column + * + * @author Nishita Aggarwal + */ +public final class SaddlebackSearch { + private SaddlebackSearch() { + } + + /** + * This method performs Saddleback Search + * + * @param arr The **Sorted** array in which we will search the element. + * @param row the current row. + * @param col the current column. + * @param key the element that we want to search for. + * @throws IllegalArgumentException if the array is empty. + * @return The index(row and column) of the element if found. Else returns + * -1 -1. + */ + static int[] find(int[][] arr, int row, int col, int key) { + if (arr.length == 0) { + throw new IllegalArgumentException("Array is empty"); + } + + // array to store the answer row and column + int[] ans = {-1, -1}; + if (row < 0 || col >= arr[row].length) { + return ans; + } + if (arr[row][col] == key) { + ans[0] = row; + ans[1] = col; + return ans; + } // if the current element is greater than the given element then we move up + else if (arr[row][col] > key) { + return find(arr, row - 1, col, key); + } + // else we move right + return find(arr, row, col + 1, key); + } +} diff --git a/src/main/java/com/thealgorithms/searches/SearchInARowAndColWiseSortedMatrix.java b/src/main/java/com/thealgorithms/searches/SearchInARowAndColWiseSortedMatrix.java new file mode 100644 index 000000000000..b53c7e5256ca --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/SearchInARowAndColWiseSortedMatrix.java @@ -0,0 +1,32 @@ +package com.thealgorithms.searches; + +public class SearchInARowAndColWiseSortedMatrix { + /** + * Search a key in row and column wise sorted matrix + * + * @param matrix matrix to be searched + * @param value Key being searched for + * @author Sadiul Hakim : https://github.com/sadiul-hakim + */ + public int[] search(int[][] matrix, int value) { + int n = matrix.length; + // This variable iterates over rows + int i = 0; + // This variable iterates over columns + int j = n - 1; + int[] result = {-1, -1}; + while (i < n && j >= 0) { + if (matrix[i][j] == value) { + result[0] = i; + result[1] = j; + return result; + } + if (value > matrix[i][j]) { + i++; + } else { + j--; + } + } + return result; + } +} diff --git a/src/main/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearch.java b/src/main/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearch.java new file mode 100644 index 000000000000..6a2a46c2821f --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearch.java @@ -0,0 +1,30 @@ +package com.thealgorithms.searches; +public final class SortOrderAgnosticBinarySearch { + private SortOrderAgnosticBinarySearch() { + } + public static int find(int[] arr, int key) { + int start = 0; + int end = arr.length - 1; + boolean arrDescending = arr[start] > arr[end]; // checking for Array is in ascending order or descending order. + while (start <= end) { + int mid = end - start / 2; + if (arr[mid] == key) { + return mid; + } + if (arrDescending) { // boolean is true then our array is in descending order + if (key < arr[mid]) { + start = mid + 1; + } else { + end = mid - 1; + } + } else { // otherwise our array is in ascending order + if (key > arr[mid]) { + start = mid + 1; + } else { + end = mid - 1; + } + } + } + return -1; + } +} diff --git a/src/main/java/com/thealgorithms/searches/SquareRootBinarySearch.java b/src/main/java/com/thealgorithms/searches/SquareRootBinarySearch.java new file mode 100644 index 000000000000..95e062c274fd --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/SquareRootBinarySearch.java @@ -0,0 +1,47 @@ +package com.thealgorithms.searches; + +/** + * Given an integer x, find the square root of x. If x is not a perfect square, + * then return floor(√x). + * <p> + * For example, if x = 5, The answer should be 2 which is the floor value of √5. + * <p> + * The approach that will be used for solving the above problem is not going to + * be a straight forward Math.sqrt(). Instead we will be using Binary Search to + * find the square root of a number in the most optimised way. + * + * @author sahil + */ +public final class SquareRootBinarySearch { + private SquareRootBinarySearch() { + } + + /** + * This function calculates the floor of square root of a number. We use + * Binary Search algorithm to calculate the square root in a more optimised + * way. + * + * @param num Number + * @return answer + */ + static long squareRoot(long num) { + if (num == 0 || num == 1) { + return num; + } + long l = 1; + long r = num; + long ans = 0; + while (l <= r) { + long mid = l + (r - l) / 2; + if (mid == num / mid) { + return mid; + } else if (mid < num / mid) { + ans = mid; + l = mid + 1; + } else { + r = mid - 1; + } + } + return ans; + } +} diff --git a/src/main/java/com/thealgorithms/searches/TernarySearch.java b/src/main/java/com/thealgorithms/searches/TernarySearch.java new file mode 100644 index 000000000000..4d9f55ea9917 --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/TernarySearch.java @@ -0,0 +1,60 @@ +package com.thealgorithms.searches; + +import com.thealgorithms.devutils.searches.SearchAlgorithm; + +/** + * A ternary search algorithm is a technique in computer science for finding the + * minimum or maximum of a unimodal function The algorithm determines either + * that the minimum or maximum cannot be in the first third of the domain or + * that it cannot be in the last third of the domain, then repeats on the + * remaining third. + * + * <p> + * Worst-case performance Θ(log3(N)) Best-case performance O(1) Average + * performance Θ(log3(N)) Worst-case space complexity O(1) + * + * @author Podshivalov Nikita (https://github.com/nikitap492) + * @see SearchAlgorithm + * @see IterativeBinarySearch + */ +public class TernarySearch implements SearchAlgorithm { + + /** + * @param arr The **Sorted** array in which we will search the element. + * @param value The value that we want to search for. + * @return The index of the element if found. Else returns -1. + */ + @Override + public <T extends Comparable<T>> int find(T[] arr, T value) { + return ternarySearch(arr, value, 0, arr.length - 1); + } + + /** + * @param arr The **Sorted** array in which we will search the element. + * @param key The value that we want to search for. + * @param start The starting index from which we will start Searching. + * @param end The ending index till which we will Search. + * @return Returns the index of the Element if found. Else returns -1. + */ + private <T extends Comparable<T>> int ternarySearch(T[] arr, T key, int start, int end) { + if (start > end) { + return -1; + } + /* First boundary: add 1/3 of length to start */ + int mid1 = start + (end - start) / 3; + /* Second boundary: add 2/3 of length to start */ + int mid2 = start + 2 * (end - start) / 3; + + if (key.compareTo(arr[mid1]) == 0) { + return mid1; + } else if (key.compareTo(arr[mid2]) == 0) { + return mid2; + } /* Search the first (1/3) rd part of the array.*/ else if (key.compareTo(arr[mid1]) < 0) { + return ternarySearch(arr, key, start, --mid1); + } /* Search 3rd (1/3)rd part of the array */ else if (key.compareTo(arr[mid2]) > 0) { + return ternarySearch(arr, key, ++mid2, end); + } /* Search middle (1/3)rd part of the array */ else { + return ternarySearch(arr, key, mid1, mid2); + } + } +} diff --git a/src/main/java/com/thealgorithms/searches/UnionFind.java b/src/main/java/com/thealgorithms/searches/UnionFind.java new file mode 100644 index 000000000000..01202a982266 --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/UnionFind.java @@ -0,0 +1,104 @@ +package com.thealgorithms.searches; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * The Union-Find data structure, also known as Disjoint Set Union (DSU), + * is a data structure that tracks a set of elements partitioned into + * disjoint (non-overlapping) subsets. It supports two main operations: + * + * 1. **Find**: Determine which subset a particular element is in. + * 2. **Union**: Join two subsets into a single subset. + * + * This implementation uses path compression in the `find` operation + * and union by rank in the `union` operation for efficiency. + */ +public class UnionFind { + + private final int[] p; // Parent array + private final int[] r; // Rank array + + /** + * Initializes a Union-Find data structure with n elements. + * Each element is its own parent initially. + * + * @param n the number of elements + */ + public UnionFind(int n) { + p = new int[n]; + r = new int[n]; + + for (int i = 0; i < n; i++) { + p[i] = i; + } + } + + /** + * Finds the root of the set containing the element i. + * Uses path compression to flatten the structure. + * + * @param i the element to find + * @return the root of the set + */ + public int find(int i) { + int parent = p[i]; + + if (i == parent) { + return i; + } + + // Path compression + final int result = find(parent); + p[i] = result; + return result; + } + + /** + * Unites the sets containing elements x and y. + * Uses union by rank to attach the smaller tree under the larger tree. + * + * @param x the first element + * @param y the second element + */ + public void union(int x, int y) { + int r0 = find(x); + int r1 = find(y); + + if (r1 == r0) { + return; + } + + // Union by rank + if (r[r0] > r[r1]) { + p[r1] = r0; + } else if (r[r1] > r[r0]) { + p[r0] = r1; + } else { + p[r1] = r0; + r[r0]++; + } + } + + /** + * Counts the number of disjoint sets. + * + * @return the number of disjoint sets + */ + public int count() { + List<Integer> parents = new ArrayList<>(); + for (int i = 0; i < p.length; i++) { + int root = find(i); + if (!parents.contains(root)) { + parents.add(root); + } + } + return parents.size(); + } + + @Override + public String toString() { + return "p " + Arrays.toString(p) + " r " + Arrays.toString(r) + "\n"; + } +} diff --git a/src/main/java/com/thealgorithms/searches/UpperBound.java b/src/main/java/com/thealgorithms/searches/UpperBound.java new file mode 100644 index 000000000000..ec52c7a0ae5c --- /dev/null +++ b/src/main/java/com/thealgorithms/searches/UpperBound.java @@ -0,0 +1,62 @@ +package com.thealgorithms.searches; + +import com.thealgorithms.devutils.searches.SearchAlgorithm; + +/** + * The UpperBound method is used to return an index pointing to the first + * element in the range [first, last) which has a value greater than val, or the + * last index if no such element exists i.e. the index of the next smallest + * number just greater than that number. If there are multiple values that are + * equal to val it returns the index of the first such value. + * + * <p> + * This is an extension of BinarySearch. + * + * <p> + * Worst-case performance O(log n) Best-case performance O(1) Average + * performance O(log n) Worst-case space complexity O(1) + * + * @author Pratik Padalia (https://github.com/15pratik) + * @see SearchAlgorithm + * @see BinarySearch + */ +class UpperBound implements SearchAlgorithm { + + /** + * @param array is an array where the UpperBound value is to be found + * @param key is an element for which the UpperBound is to be found + * @param <T> is any comparable type + * @return index of the UpperBound element + */ + @Override + public <T extends Comparable<T>> int find(T[] array, T key) { + return search(array, key, 0, array.length - 1); + } + + /** + * This method implements the Generic Binary Search + * + * @param array The array to make the binary search + * @param key The number you are looking for + * @param left The lower bound + * @param right The upper bound + * @return the location of the key + */ + private <T extends Comparable<T>> int search(T[] array, T key, int left, int right) { + if (right <= left) { + return left; + } + + // find median + int median = (left + right) >>> 1; + int comp = key.compareTo(array[median]); + + if (comp < 0) { + // key is smaller, median position can be a possible solution + return search(array, key, left, median); + } else { + // key we are looking is greater, so we must look on the right of median position + return search(array, key, median + 1, right); + } + } +} diff --git a/src/main/java/com/thealgorithms/slidingwindow/LongestSubarrayWithSumLessOrEqualToK.java b/src/main/java/com/thealgorithms/slidingwindow/LongestSubarrayWithSumLessOrEqualToK.java new file mode 100644 index 000000000000..55c3f709b467 --- /dev/null +++ b/src/main/java/com/thealgorithms/slidingwindow/LongestSubarrayWithSumLessOrEqualToK.java @@ -0,0 +1,48 @@ +package com.thealgorithms.slidingwindow; + +/** + * The Longest Subarray with Sum Less Than or Equal to k algorithm finds the length + * of the longest subarray whose sum is less than or equal to a given value k. + * + * <p> + * Worst-case performance O(n) + * Best-case performance O(n) + * Average performance O(n) + * Worst-case space complexity O(1) + * + * @author https://github.com/Chiefpatwal + */ +public final class LongestSubarrayWithSumLessOrEqualToK { + + // Prevent instantiation + private LongestSubarrayWithSumLessOrEqualToK() { + } + + /** + * This method finds the length of the longest subarray with a sum less than or equal to k. + * + * @param arr is the input array + * @param k is the maximum sum allowed + * @return the length of the longest subarray with sum less than or equal to k + */ + public static int longestSubarrayWithSumLEK(int[] arr, int k) { + int maxLength = 0; // To store the maximum length found + int currentSum = 0; // To store the current sum of the window + int left = 0; // Left index of the sliding window + + for (int right = 0; right < arr.length; right++) { + currentSum += arr[right]; // Expand the window to the right + + // Shrink the window from the left if the current sum exceeds k + while (currentSum > k && left <= right) { + currentSum -= arr[left]; // Remove the leftmost element + left++; // Move the left index to the right + } + + // Update maxLength if the current window is valid + maxLength = Math.max(maxLength, right - left + 1); + } + + return maxLength; // Return the maximum length found + } +} diff --git a/src/main/java/com/thealgorithms/slidingwindow/LongestSubstringWithoutRepeatingCharacters.java b/src/main/java/com/thealgorithms/slidingwindow/LongestSubstringWithoutRepeatingCharacters.java new file mode 100644 index 000000000000..0641730d8b09 --- /dev/null +++ b/src/main/java/com/thealgorithms/slidingwindow/LongestSubstringWithoutRepeatingCharacters.java @@ -0,0 +1,46 @@ +package com.thealgorithms.slidingwindow; +import java.util.HashSet; + +/** + * The Longest Substring Without Repeating Characters algorithm finds the length of + * the longest substring without repeating characters in a given string. + * + * <p> + * Worst-case performance O(n) + * Best-case performance O(n) + * Average performance O(n) + * Worst-case space complexity O(min(n, m)), where n is the length of the string + * and m is the size of the character set. + * + * @author (https://github.com/Chiefpatwal) + */ +public final class LongestSubstringWithoutRepeatingCharacters { + + // Prevent instantiation + private LongestSubstringWithoutRepeatingCharacters() { + } + + /** + * This method finds the length of the longest substring without repeating characters. + * + * @param s is the input string + * @return the length of the longest substring without repeating characters + */ + public static int lengthOfLongestSubstring(String s) { + int maxLength = 0; + int left = 0; + HashSet<Character> charSet = new HashSet<>(); + + for (int right = 0; right < s.length(); right++) { + // If the character is already in the set, remove characters from the left + while (charSet.contains(s.charAt(right))) { + charSet.remove(s.charAt(left)); + left++; + } + charSet.add(s.charAt(right)); + maxLength = Math.max(maxLength, right - left + 1); + } + + return maxLength; + } +} diff --git a/src/main/java/com/thealgorithms/slidingwindow/MaxSumKSizeSubarray.java b/src/main/java/com/thealgorithms/slidingwindow/MaxSumKSizeSubarray.java new file mode 100644 index 000000000000..7e8095e08a0b --- /dev/null +++ b/src/main/java/com/thealgorithms/slidingwindow/MaxSumKSizeSubarray.java @@ -0,0 +1,50 @@ +package com.thealgorithms.slidingwindow; + +/** + * The Sliding Window algorithm is used to find the maximum sum of a subarray + * of a fixed size k within a given array. + * + * <p> + * Worst-case performance O(n) + * Best-case performance O(n) + * Average performance O(n) + * Worst-case space complexity O(1) + * + * @author Your Name (https://github.com/Chiefpatwal) + */ +public final class MaxSumKSizeSubarray { + + // Prevent instantiation + private MaxSumKSizeSubarray() { + } + + /** + * This method finds the maximum sum of a subarray of a given size k. + * + * @param arr is the input array where the maximum sum needs to be found + * @param k is the size of the subarray + * @return the maximum sum of the subarray of size k + */ + public static int maxSumKSizeSubarray(int[] arr, int k) { + if (arr.length < k) { + return -1; // Edge case: not enough elements + } + + int maxSum; + int windowSum = 0; + + // Calculate the sum of the first window + for (int i = 0; i < k; i++) { + windowSum += arr[i]; + } + maxSum = windowSum; + + // Slide the window across the array + for (int i = k; i < arr.length; i++) { + windowSum += arr[i] - arr[i - k]; + maxSum = Math.max(maxSum, windowSum); + } + + return maxSum; + } +} diff --git a/src/main/java/com/thealgorithms/slidingwindow/MinSumKSizeSubarray.java b/src/main/java/com/thealgorithms/slidingwindow/MinSumKSizeSubarray.java new file mode 100644 index 000000000000..40a5441fa7a0 --- /dev/null +++ b/src/main/java/com/thealgorithms/slidingwindow/MinSumKSizeSubarray.java @@ -0,0 +1,51 @@ +package com.thealgorithms.slidingwindow; +/** + * The Sliding Window algorithm is used to find the minimum sum of a subarray + * of a fixed size k within a given array. + * + * <p> + * Worst-case performance O(n) + * Best-case performance O(n) + * Average performance O(n) + * Worst-case space complexity O(1) + * + * This class provides a static method to find the minimum sum of a subarray + * with a specified length k. + * + * @author Rashi Dashore (https://github.com/rashi07dashore) + */ +public final class MinSumKSizeSubarray { + + // Prevent instantiation + private MinSumKSizeSubarray() { + } + + /** + * This method finds the minimum sum of a subarray of a given size k. + * + * @param arr is the input array where the minimum sum needs to be found + * @param k is the size of the subarray + * @return the minimum sum of the subarray of size k + */ + public static int minSumKSizeSubarray(int[] arr, int k) { + if (arr.length < k) { + return -1; // Edge case: not enough elements + } + + int minSum; + int windowSum = 0; + + // Calculate the sum of the first window + for (int i = 0; i < k; i++) { + windowSum += arr[i]; + } + minSum = windowSum; + + // Slide the window across the array + for (int i = k; i < arr.length; i++) { + windowSum += arr[i] - arr[i - k]; + minSum = Math.min(minSum, windowSum); + } + return minSum; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/AdaptiveMergeSort.java b/src/main/java/com/thealgorithms/sorts/AdaptiveMergeSort.java new file mode 100644 index 000000000000..2c71bae8b557 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/AdaptiveMergeSort.java @@ -0,0 +1,40 @@ +package com.thealgorithms.sorts; + +public class AdaptiveMergeSort implements SortAlgorithm { + @SuppressWarnings("unchecked") + public <T extends Comparable<T>> T[] sort(T[] array) { + if (array.length <= 1) { + return array; + } + T[] aux = array.clone(); + sort(array, aux, 0, array.length - 1); + return array; + } + + private <T extends Comparable<T>> void sort(T[] array, T[] aux, int low, int high) { + if (low >= high) { + return; + } + int mid = low + (high - low) / 2; + sort(array, aux, low, mid); + sort(array, aux, mid + 1, high); + merge(array, aux, low, mid, high); + } + + private <T extends Comparable<T>> void merge(T[] array, T[] aux, int low, int mid, int high) { + System.arraycopy(array, low, aux, low, high - low + 1); + int i = low; + int j = mid + 1; + for (int k = low; k <= high; k++) { + if (i > mid) { + array[k] = aux[j++]; + } else if (j > high) { + array[k] = aux[i++]; + } else if (aux[j].compareTo(aux[i]) < 0) { + array[k] = aux[j++]; + } else { + array[k] = aux[i++]; + } + } + } +} diff --git a/src/main/java/com/thealgorithms/sorts/BeadSort.java b/src/main/java/com/thealgorithms/sorts/BeadSort.java new file mode 100644 index 000000000000..1e1c64905e46 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/BeadSort.java @@ -0,0 +1,63 @@ +package com.thealgorithms.sorts; + +import java.util.Arrays; + +public class BeadSort { + private enum BeadState { BEAD, EMPTY } + + /** + * Sorts the given array using the BeadSort algorithm. + * + * @param array The array of non-negative integers to be sorted. + * @return The sorted array. + * @throws IllegalArgumentException If the array contains negative numbers. + */ + public int[] sort(int[] array) { + allInputsMustBeNonNegative(array); + return extractSortedFromGrid(fillGrid(array)); + } + + private void allInputsMustBeNonNegative(final int[] array) { + if (Arrays.stream(array).anyMatch(s -> s < 0)) { + throw new IllegalArgumentException("BeadSort cannot sort negative numbers."); + } + } + + private BeadState[][] fillGrid(final int[] array) { + final var maxValue = Arrays.stream(array).max().orElse(0); + var grid = getEmptyGrid(array.length, maxValue); + + int[] count = new int[maxValue]; + for (int i = 0, arrayLength = array.length; i < arrayLength; i++) { + int k = 0; + for (int j = 0; j < array[i]; j++) { + grid[count[maxValue - k - 1]++][k] = BeadState.BEAD; + k++; + } + } + return grid; + } + + private BeadState[][] getEmptyGrid(final int arrayLength, final int maxValue) { + BeadState[][] grid = new BeadState[arrayLength][maxValue]; + for (int i = 0; i < arrayLength; i++) { + for (int j = 0; j < maxValue; j++) { + grid[i][j] = BeadState.EMPTY; + } + } + + return grid; + } + + private int[] extractSortedFromGrid(final BeadState[][] grid) { + int[] sorted = new int[grid.length]; + for (int i = 0; i < grid.length; i++) { + int k = 0; + for (int j = 0; j < grid[grid.length - 1 - i].length && grid[grid.length - 1 - i][j] == BeadState.BEAD; j++) { + k++; + } + sorted[i] = k; + } + return sorted; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/BinaryInsertionSort.java b/src/main/java/com/thealgorithms/sorts/BinaryInsertionSort.java new file mode 100644 index 000000000000..b6f5d92e7928 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/BinaryInsertionSort.java @@ -0,0 +1,40 @@ +package com.thealgorithms.sorts; + +/** + * BinaryInsertionSort class implements the SortAlgorithm interface using the binary insertion sort technique. + * Binary Insertion Sort improves upon the simple insertion sort by using binary search to find the appropriate + * location to insert the new element, reducing the number of comparisons in the insertion step. + */ +public class BinaryInsertionSort implements SortAlgorithm { + + /** + * Sorts the given array using the Binary Insertion Sort algorithm. + * + * @param <T> the type of elements in the array, which must implement the Comparable interface + * @param array the array to be sorted + * @return the sorted array + */ + public <T extends Comparable<T>> T[] sort(T[] array) { + for (int i = 1; i < array.length; i++) { + final T temp = array[i]; + int low = 0; + int high = i - 1; + + while (low <= high) { + final int mid = (low + high) >>> 1; + if (temp.compareTo(array[mid]) < 0) { + high = mid - 1; + } else { + low = mid + 1; + } + } + + for (int j = i; j >= low + 1; j--) { + array[j] = array[j - 1]; + } + + array[low] = temp; + } + return array; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/BitonicSort.java b/src/main/java/com/thealgorithms/sorts/BitonicSort.java new file mode 100644 index 000000000000..90d204818729 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/BitonicSort.java @@ -0,0 +1,112 @@ +package com.thealgorithms.sorts; + +import java.util.Arrays; +import java.util.function.BiPredicate; + +/** + * BitonicSort class implements the SortAlgorithm interface using the bitonic sort technique. + */ +public class BitonicSort implements SortAlgorithm { + private enum Direction { + DESCENDING, + ASCENDING, + } + + /** + * Sorts the given array using the Bitonic Sort algorithm. + * + * @param <T> the type of elements in the array, which must implement the Comparable interface + * @param array the array to be sorted + * @return the sorted array + */ + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + if (array.length == 0) { + return array; + } + + final int paddedSize = nextPowerOfTwo(array.length); + T[] paddedArray = Arrays.copyOf(array, paddedSize); + + // Fill the padded part with a maximum value + final T maxValue = max(array); + Arrays.fill(paddedArray, array.length, paddedSize, maxValue); + + bitonicSort(paddedArray, 0, paddedSize, Direction.ASCENDING); + return Arrays.copyOf(paddedArray, array.length); + } + + private <T extends Comparable<T>> void bitonicSort(final T[] array, final int low, final int cnt, final Direction direction) { + if (cnt > 1) { + final int k = cnt / 2; + + // Sort first half in ascending order + bitonicSort(array, low, k, Direction.ASCENDING); + + // Sort second half in descending order + bitonicSort(array, low + k, cnt - k, Direction.DESCENDING); + + // Merge the whole sequence in ascending order + bitonicMerge(array, low, cnt, direction); + } + } + + /** + * Merges the bitonic sequence in the specified direction. + * + * @param <T> the type of elements in the array, which must be Comparable + * @param array the array containing the bitonic sequence to be merged + * @param low the starting index of the sequence to be merged + * @param cnt the number of elements in the sequence to be merged + * @param direction the direction of sorting + */ + private <T extends Comparable<T>> void bitonicMerge(T[] array, int low, int cnt, Direction direction) { + if (cnt > 1) { + final int k = cnt / 2; + + final BiPredicate<T, T> areSorted = (direction == Direction.ASCENDING) ? (a, b) -> a.compareTo(b) < 0 : (a, b) -> a.compareTo(b) > 0; + for (int i = low; i < low + k; i++) { + if (!areSorted.test(array[i], array[i + k])) { + SortUtils.swap(array, i, i + k); + } + } + + bitonicMerge(array, low, k, direction); + bitonicMerge(array, low + k, cnt - k, direction); + } + } + + /** + * Finds the next power of two greater than or equal to the given number. + * + * @param n the number + * @return the next power of two + */ + private static int nextPowerOfTwo(int n) { + int count = 0; + + // First n in the below condition is for the case where n is 0 + if ((n & (n - 1)) == 0) { + return n; + } + + while (n != 0) { + n >>= 1; + count += 1; + } + + return 1 << count; + } + + /** + * Finds the maximum element in the given array. + * + * @param <T> the type of elements in the array, which must implement the Comparable interface + * @param array the array to be searched + * @return the maximum element in the array + * @throws IllegalArgumentException if the array is null or empty + */ + private static <T extends Comparable<T>> T max(final T[] array) { + return Arrays.stream(array).max(Comparable::compareTo).orElseThrow(); + } +} diff --git a/src/main/java/com/thealgorithms/sorts/BogoSort.java b/src/main/java/com/thealgorithms/sorts/BogoSort.java new file mode 100644 index 000000000000..7a1f7b216437 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/BogoSort.java @@ -0,0 +1,54 @@ +package com.thealgorithms.sorts; + +import java.util.Random; + +/** + * @author Podshivalov Nikita (https://github.com/nikitap492) + * @see SortAlgorithm + */ +public class BogoSort implements SortAlgorithm { + + private static final Random RANDOM = new Random(); + + private static <T extends Comparable<T>> boolean isSorted(T[] array) { + for (int i = 0; i < array.length - 1; i++) { + if (SortUtils.less(array[i + 1], array[i])) { + return false; + } + } + return true; + } + + // Randomly shuffles the array + private static <T> void nextPermutation(T[] array) { + int length = array.length; + + for (int i = 0; i < array.length; i++) { + int randomIndex = i + RANDOM.nextInt(length - i); + SortUtils.swap(array, randomIndex, i); + } + } + + public <T extends Comparable<T>> T[] sort(T[] array) { + while (!isSorted(array)) { + nextPermutation(array); + } + return array; + } + + // Driver Program + public static void main(String[] args) { + // Integer Input + Integer[] integers = {4, 23, 6, 78, 1, 54, 231, 9, 12}; + + BogoSort bogoSort = new BogoSort(); + + // print a sorted array + SortUtils.print(bogoSort.sort(integers)); + + // String Input + String[] strings = {"c", "a", "e", "b", "d"}; + + SortUtils.print(bogoSort.sort(strings)); + } +} diff --git a/src/main/java/com/thealgorithms/sorts/BubbleSort.java b/src/main/java/com/thealgorithms/sorts/BubbleSort.java new file mode 100644 index 000000000000..6823c68d0a74 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/BubbleSort.java @@ -0,0 +1,33 @@ +package com.thealgorithms.sorts; + +/** + * @author Varun Upadhyay (https://github.com/varunu28) + * @author Podshivalov Nikita (https://github.com/nikitap492) + * @see SortAlgorithm + */ +class BubbleSort implements SortAlgorithm { + + /** + * Implements generic bubble sort algorithm. + * + * @param array the array to be sorted. + * @param <T> the type of elements in the array. + * @return the sorted array. + */ + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + for (int i = 1, size = array.length; i < size; ++i) { + boolean swapped = false; + for (int j = 0; j < size - i; ++j) { + if (SortUtils.greater(array[j], array[j + 1])) { + SortUtils.swap(array, j, j + 1); + swapped = true; + } + } + if (!swapped) { + break; + } + } + return array; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/BubbleSortRecursive.java b/src/main/java/com/thealgorithms/sorts/BubbleSortRecursive.java new file mode 100644 index 000000000000..d9cc00f95b69 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/BubbleSortRecursive.java @@ -0,0 +1,35 @@ +package com.thealgorithms.sorts; + +/** + * BubbleSort algorithm implemented using recursion + */ +public class BubbleSortRecursive implements SortAlgorithm { + /** + * @param array - an array should be sorted + * @return sorted array + */ + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + bubbleSort(array, array.length); + return array; + } + + /** + * BubbleSort algorithm implements using recursion + * + * @param array array contains elements + * @param len length of given array + */ + private static <T extends Comparable<T>> void bubbleSort(T[] array, int len) { + boolean swapped = false; + for (int i = 0; i < len - 1; ++i) { + if (SortUtils.greater(array[i], array[i + 1])) { + SortUtils.swap(array, i, i + 1); + swapped = true; + } + } + if (swapped) { + bubbleSort(array, len - 1); + } + } +} diff --git a/src/main/java/com/thealgorithms/sorts/BucketSort.java b/src/main/java/com/thealgorithms/sorts/BucketSort.java new file mode 100644 index 000000000000..62c5e929593b --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/BucketSort.java @@ -0,0 +1,130 @@ +package com.thealgorithms.sorts; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * BucketSort class provides a method to sort an array of elements using the Bucket Sort algorithm + * and implements the SortAlgorithm interface. + */ +public class BucketSort implements SortAlgorithm { + + // Constant that defines the divisor for determining the number of buckets + private static final int BUCKET_DIVISOR = 10; + + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + if (array.length == 0) { + return array; + } + + T min = findMin(array); + T max = findMax(array); + int numberOfBuckets = calculateNumberOfBuckets(array.length); + + List<List<T>> buckets = initializeBuckets(numberOfBuckets); + distributeElementsIntoBuckets(array, buckets, min, max, numberOfBuckets); + + return concatenateBuckets(buckets, array); + } + + /** + * Calculates the number of buckets to use based on the size of the array. + * + * @param arrayLength the length of the array + * @return the number of buckets + */ + private int calculateNumberOfBuckets(final int arrayLength) { + return Math.max(arrayLength / BUCKET_DIVISOR, 1); + } + + /** + * Initializes a list of empty buckets. + * + * @param numberOfBuckets the number of buckets to initialize + * @param <T> the type of elements to be sorted + * @return a list of empty buckets + */ + private <T extends Comparable<T>> List<List<T>> initializeBuckets(int numberOfBuckets) { + List<List<T>> buckets = new ArrayList<>(numberOfBuckets); + for (int i = 0; i < numberOfBuckets; i++) { + buckets.add(new ArrayList<>()); + } + return buckets; + } + + /** + * Distributes elements from the array into the appropriate buckets. + * + * @param array the array of elements to distribute + * @param buckets the list of buckets + * @param min the minimum value in the array + * @param max the maximum value in the array + * @param numberOfBuckets the total number of buckets + * @param <T> the type of elements in the array + */ + private <T extends Comparable<T>> void distributeElementsIntoBuckets(T[] array, List<List<T>> buckets, final T min, final T max, final int numberOfBuckets) { + for (final T element : array) { + int bucketIndex = hash(element, min, max, numberOfBuckets); + buckets.get(bucketIndex).add(element); + } + } + + /** + * Concatenates the sorted buckets back into the original array. + * + * @param buckets the list of sorted buckets + * @param array the original array + * @param <T> the type of elements in the array + * @return the sorted array + */ + private <T extends Comparable<T>> T[] concatenateBuckets(Iterable<List<T>> buckets, T[] array) { + int index = 0; + for (List<T> bucket : buckets) { + Collections.sort(bucket); + for (T element : bucket) { + array[index++] = element; + } + } + return array; + } + + /** + * The method computes the index of the bucket in which a given element should be placed. + * This is done by "normalizing" the element within the range of the array's minimum (min) and maximum (max) values, + * and then mapping this normalized value to a specific bucket index. + * + * @param element the element of the array + * @param min the minimum value in the array + * @param max the maximum value in the array + * @param numberOfBuckets the total number of buckets + * @param <T> the type of elements in the array + * @return the index of the bucket + */ + private <T extends Comparable<T>> int hash(final T element, final T min, final T max, final int numberOfBuckets) { + double range = max.compareTo(min); + double normalizedValue = element.compareTo(min) / range; + return (int) (normalizedValue * (numberOfBuckets - 1)); + } + + private <T extends Comparable<T>> T findMin(T[] array) { + T min = array[0]; + for (T element : array) { + if (element.compareTo(min) < 0) { + min = element; + } + } + return min; + } + + private <T extends Comparable<T>> T findMax(T[] array) { + T max = array[0]; + for (T element : array) { + if (element.compareTo(max) > 0) { + max = element; + } + } + return max; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/CircleSort.java b/src/main/java/com/thealgorithms/sorts/CircleSort.java new file mode 100644 index 000000000000..b9b41be16701 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/CircleSort.java @@ -0,0 +1,58 @@ +package com.thealgorithms.sorts; + +public class CircleSort implements SortAlgorithm { + + /* This method implements the circle sort + * @param array The array to be sorted + */ + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + if (array.length == 0) { + return array; + } + while (doSort(array, 0, array.length - 1)) { + } + return array; + } + + /** + * Recursively sorts the array in a circular manner by comparing elements + * from the start and end of the current segment. + * + * @param <T> The type of elements in the array, which must be comparable + * @param array The array to be sorted + * @param left The left boundary of the current segment being sorted + * @param right The right boundary of the current segment being sorted + * @return true if any elements were swapped during the sort; false otherwise + */ + private <T extends Comparable<T>> boolean doSort(final T[] array, final int left, final int right) { + boolean swapped = false; + + if (left == right) { + return false; + } + + int low = left; + int high = right; + + while (low < high) { + if (array[low].compareTo(array[high]) > 0) { + SortUtils.swap(array, low, high); + swapped = true; + } + low++; + high--; + } + + if (low == high && array[low].compareTo(array[high + 1]) > 0) { + SortUtils.swap(array, low, high + 1); + swapped = true; + } + + final int mid = left + (right - left) / 2; + final boolean leftHalfSwapped = doSort(array, left, mid); + final boolean rightHalfSwapped = doSort(array, mid + 1, right); + + return swapped || leftHalfSwapped || rightHalfSwapped; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/CocktailShakerSort.java b/src/main/java/com/thealgorithms/sorts/CocktailShakerSort.java new file mode 100644 index 000000000000..600ae8d3efc9 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/CocktailShakerSort.java @@ -0,0 +1,81 @@ +package com.thealgorithms.sorts; + +/** + * CocktailShakerSort class implements the Cocktail Shaker Sort algorithm, + * which is a bidirectional bubble sort. It sorts the array by passing + * through it back and forth, progressively moving the largest elements + * to the end and the smallest elements to the beginning. + * + * @author Mateus Bizzo (https://github.com/MattBizzo) + * @author Podshivalov Nikita (https://github.com/nikitap492) + */ +class CocktailShakerSort implements SortAlgorithm { + + /** + * Sorts the given array using the Cocktail Shaker Sort algorithm. + * + * @param <T> The type of elements in the array, which must be comparable + * @param array The array to be sorted + * @return The sorted array + */ + @Override + public <T extends Comparable<T>> T[] sort(final T[] array) { + if (array.length == 0) { + return array; + } + + int left = 0; + int right = array.length - 1; + + while (left < right) { + right = performForwardPass(array, left, right); + left = performBackwardPass(array, left, right); + } + + return array; + } + + /** + * Performs a forward pass through the array, moving larger elements to the end. + * + * @param <T> The type of elements in the array, which must be comparable + * @param array The array being sorted + * @param left The current left boundary of the sorting area + * @param right The current right boundary of the sorting area + * @return The index of the last swapped element during this pass + */ + private <T extends Comparable<T>> int performForwardPass(final T[] array, final int left, final int right) { + int lastSwappedIndex = left; + + for (int i = left; i < right; i++) { + if (SortUtils.less(array[i + 1], array[i])) { + SortUtils.swap(array, i, i + 1); + lastSwappedIndex = i; + } + } + + return lastSwappedIndex; + } + + /** + * Performs a backward pass through the array, moving smaller elements to the beginning. + * + * @param <T> The type of elements in the array, which must be comparable + * @param array The array being sorted + * @param left The current left boundary of the sorting area + * @param right The current right boundary of the sorting area + * @return The index of the last swapped element during this pass + */ + private <T extends Comparable<T>> int performBackwardPass(final T[] array, final int left, final int right) { + int lastSwappedIndex = right; + + for (int i = right; i > left; i--) { + if (SortUtils.less(array[i], array[i - 1])) { + SortUtils.swap(array, i - 1, i); + lastSwappedIndex = i; + } + } + + return lastSwappedIndex; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/CombSort.java b/src/main/java/com/thealgorithms/sorts/CombSort.java new file mode 100644 index 000000000000..cd12a5b2853c --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/CombSort.java @@ -0,0 +1,72 @@ +package com.thealgorithms.sorts; + +/** + * Comb Sort algorithm implementation + * + * <p> + * Best-case performance O(n * log(n)) Worst-case performance O(n ^ 2) + * Worst-case space complexity O(1) + * + * <p> + * Comb sort improves on bubble sort. + * + * @author Sandeep Roy (https://github.com/sandeeproy99) + * @author Podshivalov Nikita (https://github.com/nikitap492) + * @see BubbleSort + * @see SortAlgorithm + */ +class CombSort implements SortAlgorithm { + private static final double SHRINK_FACTOR = 1.3; + + /** + * Method to find the next gap + * + * @param gap the current gap + * @return the next gap value + */ + private int getNextGap(int gap) { + gap = (int) (gap / SHRINK_FACTOR); + return Math.max(gap, 1); + } + + /** + * Method to sort the array using CombSort + * + * @param arr the array to be sorted + * @param <T> the type of elements in the array + * @return the sorted array + */ + @Override + public <T extends Comparable<T>> T[] sort(T[] arr) { + int gap = arr.length; + boolean swapped = true; + + while (gap != 1 || swapped) { + gap = getNextGap(gap); + swapped = performSwaps(arr, gap); + } + + return arr; + } + + /** + * Method to perform the swapping of elements in the array based on the current gap + * + * @param arr the array to be sorted + * @param gap the current gap + * @param <T> the type of elements in the array + * @return true if a swap occurred, false otherwise + */ + private <T extends Comparable<T>> boolean performSwaps(final T[] arr, final int gap) { + boolean swapped = false; + + for (int i = 0; i < arr.length - gap; i++) { + if (SortUtils.less(arr[i + gap], arr[i])) { + SortUtils.swap(arr, i, i + gap); + swapped = true; + } + } + + return swapped; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/CountingSort.java b/src/main/java/com/thealgorithms/sorts/CountingSort.java new file mode 100644 index 000000000000..5d54205032d4 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/CountingSort.java @@ -0,0 +1,60 @@ +package com.thealgorithms.sorts; + +import java.util.Arrays; + +/** + * A standard implementation of the Counting Sort algorithm for integer arrays. + * This implementation has a time complexity of O(n + k), where n is the number + * of elements in the input array and k is the range of the input. + * It works only with integer arrays. + * + * The space complexity is O(k), where k is the range of the input integers. + * + * Note: This implementation handles negative integers as it + * calculates the range based on the minimum and maximum values of the array. + * + */ +public final class CountingSort { + private CountingSort() { + } + + /** + * Sorts an array of integers using the Counting Sort algorithm. + * + * @param array the array to be sorted + * @return the sorted array + */ + public static int[] sort(int[] array) { + if (array.length == 0) { + return array; + } + final var stats = Arrays.stream(array).summaryStatistics(); + final int min = stats.getMin(); + int[] count = computeHistogram(array, min, stats.getMax() - min + 1); + toCumulative(count); + return reconstructSorted(count, min, array); + } + + private static int[] computeHistogram(final int[] array, final int shift, final int spread) { + int[] res = new int[spread]; + for (final var value : array) { + res[value - shift]++; + } + return res; + } + + private static void toCumulative(int[] count) { + for (int i = 1; i < count.length; i++) { + count[i] += count[i - 1]; + } + } + + private static int[] reconstructSorted(final int[] cumulativeCount, final int shift, final int[] array) { + int[] res = new int[array.length]; + for (int i = array.length - 1; i >= 0; i--) { + res[cumulativeCount[array[i] - shift] - 1] = array[i]; + cumulativeCount[array[i] - shift]--; + } + return res; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/CycleSort.java b/src/main/java/com/thealgorithms/sorts/CycleSort.java new file mode 100644 index 000000000000..4ef051419cbb --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/CycleSort.java @@ -0,0 +1,88 @@ +package com.thealgorithms.sorts; + +/** + * This class implements the cycle sort algorithm. + * Cycle sort is an in-place sorting algorithm, unstable, and efficient for scenarios with limited memory usage. + * @author Podshivalov Nikita (https://github.com/nikitap492) + */ +class CycleSort implements SortAlgorithm { + /** + * Sorts an array of comparable elements using the cycle sort algorithm. + * + * @param <T> The type of elements in the array, which must be comparable + * @param array The array to be sorted + * @return The sorted array + */ + @Override + public <T extends Comparable<T>> T[] sort(final T[] array) { + final int length = array.length; + + for (int cycleStart = 0; cycleStart <= length - 2; cycleStart++) { + T item = array[cycleStart]; + int pos = findPosition(array, cycleStart, item); + + if (pos == cycleStart) { + continue; // Item is already in the correct position + } + + item = placeItem(array, item, pos); + + // Rotate the rest of the cycle + while (pos != cycleStart) { + pos = findPosition(array, cycleStart, item); + item = placeItem(array, item, pos); + } + } + return array; + } + + /** + * Finds the correct position for the given item starting from cycleStart. + * + * @param <T> The type of elements in the array, which must be comparable + * @param array The array to be sorted + * @param cycleStart The starting index of the cycle + * @param item The item whose position is to be found + * @return The correct position of the item + */ + private <T extends Comparable<T>> int findPosition(final T[] array, final int cycleStart, final T item) { + int pos = cycleStart; + for (int i = cycleStart + 1; i < array.length; i++) { + if (SortUtils.less(array[i], item)) { + pos++; + } + } + return pos; + } + + /** + * Places the item in its correct position, handling duplicates, and returns the displaced item. + * + * @param <T> The type of elements in the array, which must be comparable + * @param array The array being sorted + * @param item The item to be placed + * @param pos The position where the item is to be placed + * @return The displaced item + */ + private <T extends Comparable<T>> T placeItem(final T[] array, final T item, int pos) { + while (item.compareTo(array[pos]) == 0) { + pos++; + } + return replace(array, pos, item); + } + + /** + * Replaces an element in the array with the given item and returns the replaced item. + * + * @param <T> The type of elements in the array, which must be comparable + * @param array The array in which the replacement will occur + * @param pos The position at which the replacement will occur + * @param item The item to be placed in the array + * @return The replaced item + */ + private <T extends Comparable<T>> T replace(final T[] array, final int pos, final T item) { + final T replacedItem = array[pos]; + array[pos] = item; + return replacedItem; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/DarkSort.java b/src/main/java/com/thealgorithms/sorts/DarkSort.java new file mode 100644 index 000000000000..4887d7d124ba --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/DarkSort.java @@ -0,0 +1,59 @@ +package com.thealgorithms.sorts; + +/** + * Dark Sort algorithm implementation. + * + * Dark Sort uses a temporary array to count occurrences of elements and + * reconstructs the sorted array based on the counts. + */ +class DarkSort { + + /** + * Sorts the array using the Dark Sort algorithm. + * + * @param unsorted the array to be sorted + * @return sorted array + */ + public Integer[] sort(Integer[] unsorted) { + if (unsorted == null || unsorted.length <= 1) { + return unsorted; + } + + int max = findMax(unsorted); // Find the maximum value in the array + + // Create a temporary array for counting occurrences + int[] temp = new int[max + 1]; + + // Count occurrences of each element + for (int value : unsorted) { + temp[value]++; + } + + // Reconstruct the sorted array + int index = 0; + for (int i = 0; i < temp.length; i++) { + while (temp[i] > 0) { + unsorted[index++] = i; + temp[i]--; + } + } + + return unsorted; + } + + /** + * Helper method to find the maximum value in an array. + * + * @param arr the array + * @return the maximum value + */ + private int findMax(Integer[] arr) { + int max = arr[0]; + for (int value : arr) { + if (value > max) { + max = value; + } + } + return max; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/DualPivotQuickSort.java b/src/main/java/com/thealgorithms/sorts/DualPivotQuickSort.java new file mode 100644 index 000000000000..ada745acd25a --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/DualPivotQuickSort.java @@ -0,0 +1,96 @@ +package com.thealgorithms.sorts; + +/** + * Dual Pivot Quick Sort Algorithm + * + * @author Debasish Biswas (https://github.com/debasishbsws) * + * @see SortAlgorithm + */ +public class DualPivotQuickSort implements SortAlgorithm { + + /** + * Sorts an array using the Dual Pivot QuickSort algorithm. + * + * @param array The array to be sorted + * @param <T> The type of elements in the array, which must be comparable + * @return The sorted array + */ + @Override + public <T extends Comparable<T>> T[] sort(final T[] array) { + if (array.length <= 1) { + return array; + } + + dualPivotQuicksort(array, 0, array.length - 1); + return array; + } + + /** + * Recursively applies the Dual Pivot QuickSort algorithm to subarrays. + * + * @param array The array to be sorted + * @param left The starting index of the subarray + * @param right The ending index of the subarray + * @param <T> The type of elements in the array, which must be comparable + */ + private static <T extends Comparable<T>> void dualPivotQuicksort(final T[] array, final int left, final int right) { + if (left < right) { + final int[] pivots = partition(array, left, right); + + dualPivotQuicksort(array, left, pivots[0] - 1); + dualPivotQuicksort(array, pivots[0] + 1, pivots[1] - 1); + dualPivotQuicksort(array, pivots[1] + 1, right); + } + } + + /** + * Partitions the array into three parts using two pivots. + * + * @param array The array to be partitioned + * @param left The starting index for partitioning + * @param right The ending index for partitioning + * @param <T> The type of elements in the array, which must be comparable + * @return An array containing the indices of the two pivots + */ + private static <T extends Comparable<T>> int[] partition(final T[] array, int left, final int right) { + if (SortUtils.greater(array[left], array[right])) { + SortUtils.swap(array, left, right); + } + + final T pivot1 = array[left]; + final T pivot2 = array[right]; + + int pivot1End = left + 1; + int low = left + 1; + int high = right - 1; + + while (low <= high) { + if (SortUtils.less(array[low], pivot1)) { + SortUtils.swap(array, low, pivot1End); + pivot1End++; + } else if (SortUtils.greaterOrEqual(array[low], pivot2)) { + while (low < high && SortUtils.greater(array[high], pivot2)) { + high--; + } + SortUtils.swap(array, low, high); + high--; + + if (SortUtils.less(array[low], pivot1)) { + SortUtils.swap(array, low, pivot1End); + pivot1End++; + } + } + low++; + } + + // Place the pivots in their correct positions + pivot1End--; + high++; + + SortUtils.swap(array, left, pivot1End); + SortUtils.swap(array, right, high); + + // Return the indices of the pivots + return new int[] {low, high}; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/DutchNationalFlagSort.java b/src/main/java/com/thealgorithms/sorts/DutchNationalFlagSort.java new file mode 100644 index 000000000000..abfcb452b29a --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/DutchNationalFlagSort.java @@ -0,0 +1,42 @@ +package com.thealgorithms.sorts; + +/** + * The Dutch National Flag Sort sorts a sequence of values into three permutations which are defined + * by a value given as the indented middle. First permutation: values less than middle. Second + * permutation: values equal middle. Third permutation: values greater than middle. If no indented + * middle is given, this implementation will use a value from the given Array. This value is the one + * positioned in the arrays' middle if the arrays' length is odd. If the arrays' length is even, the + * value left to the middle will be used. More information and Pseudocode: + * https://en.wikipedia.org/wiki/Dutch_national_flag_problem + */ +public class DutchNationalFlagSort implements SortAlgorithm { + + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + return dutchNationalFlagSort(array, array[(int) Math.ceil((array.length) / 2.0) - 1]); + } + + public <T extends Comparable<T>> T[] sort(T[] array, T intendedMiddle) { + return dutchNationalFlagSort(array, intendedMiddle); + } + + private <T extends Comparable<T>> T[] dutchNationalFlagSort(final T[] array, final T intendedMiddle) { + int i = 0; + int j = 0; + int k = array.length - 1; + + while (j <= k) { + if (0 > array[j].compareTo(intendedMiddle)) { + SortUtils.swap(array, i, j); + j++; + i++; + } else if (0 < array[j].compareTo(intendedMiddle)) { + SortUtils.swap(array, j, k); + k--; + } else { + j++; + } + } + return array; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/ExchangeSort.java b/src/main/java/com/thealgorithms/sorts/ExchangeSort.java new file mode 100644 index 000000000000..67e94b889671 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/ExchangeSort.java @@ -0,0 +1,41 @@ +package com.thealgorithms.sorts; + +/** + * ExchangeSort is an implementation of the Exchange Sort algorithm. + * + * <p> + * Exchange sort works by comparing each element with all subsequent elements, + * swapping where needed, to ensure the correct placement of each element + * in the final sorted order. It iteratively performs this process for each + * element in the array. While it lacks the advantage of bubble sort in + * detecting sorted lists in one pass, it can be more efficient than bubble sort + * due to a constant factor (one less pass over the data to be sorted; half as + * many total comparisons) in worst-case scenarios. + * </p> + * + * <p> + * Reference: https://en.wikipedia.org/wiki/Sorting_algorithm#Exchange_sort + * </p> + * + * @author 555vedant (Vedant Kasar) + */ +class ExchangeSort implements SortAlgorithm { + /** + * Implementation of Exchange Sort Algorithm + * + * @param array the array to be sorted. + * @param <T> the type of elements in the array. + * @return the sorted array. + */ + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + for (int i = 0; i < array.length - 1; i++) { + for (int j = i + 1; j < array.length; j++) { + if (array[i].compareTo(array[j]) > 0) { + SortUtils.swap(array, i, j); + } + } + } + return array; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/FlashSort.java b/src/main/java/com/thealgorithms/sorts/FlashSort.java new file mode 100644 index 000000000000..e8dbf8c42742 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/FlashSort.java @@ -0,0 +1,206 @@ +package com.thealgorithms.sorts; + +/** + * Implementation of Flash Sort algorithm that implements the SortAlgorithm interface. + * + * Sorts an array using the Flash Sort algorithm. + * <p> + * Flash Sort is a distribution sorting algorithm that partitions the data into + * different classes based on a classification array. It performs the sorting by + * first distributing the data elements into different buckets (or classes) and + * then permuting these buckets into the sorted order. + * <p> + * The method works as follows: + * <ol> + * <li>Finds the minimum and maximum values in the array.</li> + * <li>Initializes a classification array `L` to keep track of the number of elements in each class.</li> + * <li>Computes a normalization constant `c1` to map elements into classes.</li> + * <li>Classifies each element of the array into the corresponding bucket in the classification array.</li> + * <li>Transforms the classification array to compute the starting indices of each bucket.</li> + * <li>Permutes the elements of the array into sorted order based on the classification.</li> + * <li>Uses insertion sort for the final arrangement to ensure complete sorting.</li> + * </ol> + */ +public class FlashSort implements SortAlgorithm { + private double classificationRatio = 0.45; + + public FlashSort() { + } + + public FlashSort(double classificationRatio) { + if (classificationRatio <= 0 || classificationRatio >= 1) { + throw new IllegalArgumentException("Classification ratio must be between 0 and 1 (exclusive)."); + } + this.classificationRatio = classificationRatio; + } + + public double getClassificationRatio() { + return classificationRatio; + } + + public void setClassificationRatio(double classificationRatio) { + if (classificationRatio <= 0 || classificationRatio >= 1) { + throw new IllegalArgumentException("Classification ratio must be between 0 and 1 (exclusive)."); + } + this.classificationRatio = classificationRatio; + } + + /** + * Sorts an array using the Flash Sort algorithm. + * + * @param array the array to be sorted. + * @param <T> the type of elements to be sorted, must be comparable. + * @return the sorted array. + */ + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + flashSort(array); + return array; + } + + /** + * Sorts an array using the Flash Sort algorithm. + * + * @param arr the array to be sorted. + * @param <T> the type of elements to be sorted, must be comparable. + */ + private <T extends Comparable<? super T>> void flashSort(T[] arr) { + if (arr.length == 0) { + return; + } + + final T min = findMin(arr); + final int maxIndex = findMaxIndex(arr); + + if (arr[maxIndex].compareTo(min) == 0) { + return; // All elements are the same + } + + final int m = (int) (classificationRatio * arr.length); + + final int[] classificationArray = new int[m]; + + final double c1 = (double) (m - 1) / arr[maxIndex].compareTo(min); + + classify(arr, classificationArray, c1, min); + + transform(classificationArray); + + permute(arr, classificationArray, c1, min, arr.length, m); + + insertionSort(arr); + } + + /** + * Finds the minimum value in the array. + * + * @param arr the array to find the minimum value in. + * @param <T> the type of elements in the array, must be comparable. + * @return the minimum value in the array. + */ + private <T extends Comparable<? super T>> T findMin(final T[] arr) { + T min = arr[0]; + for (int i = 1; i < arr.length; i++) { + if (arr[i].compareTo(min) < 0) { + min = arr[i]; + } + } + return min; + } + + /** + * Finds the index of the maximum value in the array. + * + * @param arr the array to find the maximum value index in. + * @param <T> the type of elements in the array, must be comparable. + * @return the index of the maximum value in the array. + */ + private <T extends Comparable<? super T>> int findMaxIndex(final T[] arr) { + int maxIndex = 0; + for (int i = 1; i < arr.length; i++) { + if (arr[i].compareTo(arr[maxIndex]) > 0) { + maxIndex = i; + } + } + return maxIndex; + } + + /** + * Classifies elements of the array into the classification array classificationArray. + * + * @param arr the array to be classified. + * @param classificationArray the classification array holding the count of elements in each class. + * @param c1 the normalization constant used to map the elements to the classification array. + * @param min the minimum value in the array. + * @param <T> the type of elements in the array, must be comparable. + */ + private <T extends Comparable<? super T>> void classify(final T[] arr, final int[] classificationArray, final double c1, final T min) { + for (int i = 0; i < arr.length; i++) { + int k = (int) (c1 * arr[i].compareTo(min)); + classificationArray[k]++; + } + } + + /** + * Transforms the classification array classificationArray into the starting index array. + * + * @param classificationArray the classification array holding the count of elements in each class. + */ + private void transform(final int[] classificationArray) { + for (int i = 1; i < classificationArray.length; i++) { + classificationArray[i] += classificationArray[i - 1]; + } + } + + /** + * Permutes the array into sorted order based on the classification array classificationArray. + * + * @param arr the array to be permuted. + * @param classificationArray the classification array holding the count of elements in each class. + * @param c1 the normalization constant used to map the elements to the classification array. + * @param min the minimum value in the array. + * @param n the length of the array. + * @param m the number of classes in the classification array. + * @param <T> the type of elements in the array, must be comparable. + */ + private <T extends Comparable<? super T>> void permute(final T[] arr, final int[] classificationArray, final double c1, T min, int n, int m) { + int move = 0; + int j = 0; + int k = m - 1; + T flash; + while (move < n - 1) { + while (j > classificationArray[k] - 1) { + j++; + k = (int) (c1 * arr[j].compareTo(min)); + } + flash = arr[j]; + while (j != classificationArray[k]) { + k = (int) (c1 * flash.compareTo(min)); + T temp = arr[classificationArray[k] - 1]; + arr[classificationArray[k] - 1] = flash; + flash = temp; + classificationArray[k]--; + move++; + } + } + } + + /** + * Sorts an array using the insertion sort algorithm. + * + * @param arr the array to be sorted. + * @param <T> the type of elements to be sorted, must be comparable. + */ + private <T extends Comparable<? super T>> void insertionSort(final T[] arr) { + int n = arr.length; + for (int i = 1; i < n; i++) { + T key = arr[i]; + int j = i - 1; + while (j >= 0 && arr[j].compareTo(key) > 0) { + arr[j + 1] = arr[j]; + j--; + } + arr[j + 1] = key; + } + } +} diff --git a/src/main/java/com/thealgorithms/sorts/GnomeSort.java b/src/main/java/com/thealgorithms/sorts/GnomeSort.java new file mode 100644 index 000000000000..b074c271404d --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/GnomeSort.java @@ -0,0 +1,28 @@ +package com.thealgorithms.sorts; + +/** + * Implementation of gnome sort + * + * @author Podshivalov Nikita (https://github.com/nikitap492) + * @since 2018-04-10 + */ +public class GnomeSort implements SortAlgorithm { + + @Override + public <T extends Comparable<T>> T[] sort(final T[] array) { + int i = 1; + int j = 2; + while (i < array.length) { + if (SortUtils.less(array[i - 1], array[i])) { + i = j++; + } else { + SortUtils.swap(array, i - 1, i); + if (--i == 0) { + i = j++; + } + } + } + + return array; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/HeapSort.java b/src/main/java/com/thealgorithms/sorts/HeapSort.java new file mode 100644 index 000000000000..e798fb91b925 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/HeapSort.java @@ -0,0 +1,47 @@ +package com.thealgorithms.sorts; + +/** + * Heap Sort Algorithm Implementation + * + * @see <a href="https://en.wikipedia.org/wiki/Heapsort">Heap Sort Algorithm</a> + */ +public class HeapSort implements SortAlgorithm { + + /** + * For simplicity, we are considering the heap root index as 1 instead of 0. + * This approach simplifies future calculations. As a result, we decrease + * the indexes by 1 when calling {@link SortUtils#less(Comparable, Comparable)} + * and provide adjusted values to the {@link SortUtils#swap(Object[], int, int)} methods. + */ + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + int n = array.length; + heapify(array, n); + while (n > 1) { + SortUtils.swap(array, 0, n - 1); + n--; + siftDown(array, 1, n); + } + return array; + } + + private <T extends Comparable<T>> void heapify(final T[] array, final int n) { + for (int k = n / 2; k >= 1; k--) { + siftDown(array, k, n); + } + } + + private <T extends Comparable<T>> void siftDown(final T[] array, int k, final int n) { + while (2 * k <= n) { + int j = 2 * k; + if (j < n && SortUtils.less(array[j - 1], array[j])) { + j++; + } + if (!SortUtils.less(array[k - 1], array[j - 1])) { + break; + } + SortUtils.swap(array, k - 1, j - 1); + k = j; + } + } +} diff --git a/src/main/java/com/thealgorithms/sorts/InsertionSort.java b/src/main/java/com/thealgorithms/sorts/InsertionSort.java new file mode 100644 index 000000000000..21ebf3827b5f --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/InsertionSort.java @@ -0,0 +1,91 @@ +package com.thealgorithms.sorts; + +class InsertionSort implements SortAlgorithm { + + /** + * Sorts the given array using the standard Insertion Sort algorithm. + * + * @param array The array to be sorted + * @param <T> The type of elements in the array, which must be comparable + * @return The sorted array + */ + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + return sort(array, 0, array.length); + } + + /** + * Sorts a subarray of the given array using the standard Insertion Sort algorithm. + * + * @param array The array to be sorted + * @param lo The starting index of the subarray + * @param hi The ending index of the subarray (exclusive) + * @param <T> The type of elements in the array, which must be comparable + * @return The sorted array + */ + public <T extends Comparable<T>> T[] sort(T[] array, final int lo, final int hi) { + if (array == null || lo >= hi) { + return array; + } + + for (int i = lo + 1; i < hi; i++) { + final T key = array[i]; + int j = i - 1; + while (j >= lo && SortUtils.less(key, array[j])) { + array[j + 1] = array[j]; + j--; + } + array[j + 1] = key; + } + + return array; + } + + /** + * Sentinel sort is a function which on the first step finds the minimal element in the provided + * array and puts it to the zero position, such a trick gives us an ability to avoid redundant + * comparisons like `j > 0` and swaps (we can move elements on position right, until we find + * the right position for the chosen element) on further step. + * + * @param array The array to be sorted + * @param <T> The type of elements in the array, which must be comparable + * @return The sorted array + */ + public <T extends Comparable<T>> T[] sentinelSort(T[] array) { + if (array == null || array.length <= 1) { + return array; + } + + final int minElemIndex = findMinIndex(array); + SortUtils.swap(array, 0, minElemIndex); + + for (int i = 2; i < array.length; i++) { + final T currentValue = array[i]; + int j = i; + while (j > 0 && SortUtils.less(currentValue, array[j - 1])) { + array[j] = array[j - 1]; + j--; + } + array[j] = currentValue; + } + + return array; + } + + /** + * Finds the index of the minimum element in the array. + * + * @param array The array to be searched + * @param <T> The type of elements in the array, which must be comparable + * @return The index of the minimum element + */ + private <T extends Comparable<T>> int findMinIndex(final T[] array) { + int minIndex = 0; + for (int i = 1; i < array.length; i++) { + if (SortUtils.less(array[i], array[minIndex])) { + minIndex = i; + } + } + return minIndex; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/IntrospectiveSort.java b/src/main/java/com/thealgorithms/sorts/IntrospectiveSort.java new file mode 100644 index 000000000000..12ef197b931b --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/IntrospectiveSort.java @@ -0,0 +1,139 @@ +package com.thealgorithms.sorts; + +/** + * Introspective Sort Algorithm Implementation + * + * @see <a href="https://en.wikipedia.org/wiki/Introsort">IntroSort Algorithm</a> + */ +public class IntrospectiveSort implements SortAlgorithm { + + private static final int INSERTION_SORT_THRESHOLD = 16; + + /** + * Sorts the given array using Introspective Sort, which combines quicksort, heapsort, and insertion sort. + * + * @param array The array to be sorted + * @param <T> The type of elements in the array, which must be comparable + * @return The sorted array + */ + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + if (array == null || array.length <= 1) { + return array; + } + final int depth = 2 * (int) (Math.log(array.length) / Math.log(2)); + introspectiveSort(array, 0, array.length - 1, depth); + return array; + } + + /** + * Performs introspective sort on the specified subarray. + * + * @param array The array to be sorted + * @param low The starting index of the subarray + * @param high The ending index of the subarray + * @param depth The current depth of recursion + * @param <T> The type of elements in the array, which must be comparable + */ + private static <T extends Comparable<T>> void introspectiveSort(T[] array, final int low, int high, final int depth) { + while (high - low > INSERTION_SORT_THRESHOLD) { + if (depth == 0) { + heapSort(array, low, high); + return; + } + final int pivotIndex = partition(array, low, high); + introspectiveSort(array, pivotIndex + 1, high, depth - 1); + high = pivotIndex - 1; + } + insertionSort(array, low, high); + } + + /** + * Partitions the array around a pivot. + * + * @param array The array to be partitioned + * @param low The starting index of the subarray + * @param high The ending index of the subarray + * @param <T> The type of elements in the array, which must be comparable + * @return The index of the pivot + */ + private static <T extends Comparable<T>> int partition(T[] array, final int low, final int high) { + final int pivotIndex = low + (int) (Math.random() * (high - low + 1)); + SortUtils.swap(array, pivotIndex, high); + final T pivot = array[high]; + int i = low - 1; + for (int j = low; j < high; j++) { + if (array[j].compareTo(pivot) <= 0) { + i++; + SortUtils.swap(array, i, j); + } + } + SortUtils.swap(array, i + 1, high); + return i + 1; + } + + /** + * Sorts a subarray using insertion sort. + * + * @param array The array to be sorted + * @param low The starting index of the subarray + * @param high The ending index of the subarray + * @param <T> The type of elements in the array, which must be comparable + */ + private static <T extends Comparable<T>> void insertionSort(T[] array, final int low, final int high) { + for (int i = low + 1; i <= high; i++) { + final T key = array[i]; + int j = i - 1; + while (j >= low && array[j].compareTo(key) > 0) { + array[j + 1] = array[j]; + j--; + } + array[j + 1] = key; + } + } + + /** + * Sorts a subarray using heapsort. + * + * @param array The array to be sorted + * @param low The starting index of the subarray + * @param high The ending index of the subarray + * @param <T> The type of elements in the array, which must be comparable + */ + private static <T extends Comparable<T>> void heapSort(T[] array, final int low, final int high) { + final int n = high - low + 1; + for (int i = (n / 2) - 1; i >= 0; i--) { + heapify(array, i, n, low); + } + for (int i = high; i > low; i--) { + SortUtils.swap(array, low, i); + heapify(array, 0, i - low, low); + } + } + + /** + * Maintains the heap property for a subarray. + * + * @param array The array to be heapified + * @param i The index to be heapified + * @param n The size of the heap + * @param low The starting index of the subarray + * @param <T> The type of elements in the array, which must be comparable + */ + private static <T extends Comparable<T>> void heapify(T[] array, final int i, final int n, final int low) { + final int left = 2 * i + 1; + final int right = 2 * i + 2; + int largest = i; + + if (left < n && array[low + left].compareTo(array[low + largest]) > 0) { + largest = left; + } + if (right < n && array[low + right].compareTo(array[low + largest]) > 0) { + largest = right; + } + if (largest != i) { + SortUtils.swap(array, low + i, low + largest); + heapify(array, largest, n, low); + } + } +} diff --git a/src/main/java/com/thealgorithms/sorts/LinkListSort.java b/src/main/java/com/thealgorithms/sorts/LinkListSort.java new file mode 100644 index 000000000000..800d78f36549 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/LinkListSort.java @@ -0,0 +1,326 @@ +package com.thealgorithms.sorts; + +import java.util.Arrays; +/** + * @author <a href="https://github.com/siddhant2002">Siddhant Swarup Mallick</a> + * Program description - To sort the LinkList as per sorting technique + */ +public class LinkListSort { + + public static boolean isSorted(int[] p, int option) { + int[] a = p; + // Array is taken as input from test class + int[] b = p; + // array similar to a + int ch = option; + // Choice is choosed as any number from 1 to 3 (So the linked list will be + // sorted by Merge sort technique/Insertion sort technique/Heap sort technique) + switch (ch) { + case 1: + Task nm = new Task(); + Node start = null; + Node prev = null; + Node fresh; + Node ptr; + for (int i = 0; i < a.length; i++) { + // New nodes are created and values are added + fresh = new Node(); // Node class is called + fresh.val = a[i]; // Node val is stored + if (start == null) { + start = fresh; + } else { + prev.next = fresh; + } + prev = fresh; + } + start = nm.sortByMergeSort(start); + // method is being called + int i = 0; + for (ptr = start; ptr != null; ptr = ptr.next) { + a[i++] = ptr.val; + // storing the sorted values in the array + } + Arrays.sort(b); + // array b is sorted and it will return true when checked with sorted list + LinkListSort uu = new LinkListSort(); + return uu.compare(a, b); + // The given array and the expected array is checked if both are same then true + // is displayed else false is displayed + case 2: + Node start1 = null; + Node prev1 = null; + Node fresh1; + Node ptr1; + for (int i1 = 0; i1 < a.length; i1++) { + // New nodes are created and values are added + fresh1 = new Node(); // New node is created + fresh1.val = a[i1]; // Value is stored in the value part of the node + if (start1 == null) { + start1 = fresh1; + } else { + prev1.next = fresh1; + } + prev1 = fresh1; + } + Task1 kk = new Task1(); + start1 = kk.sortByInsertionSort(start1); + // method is being called + int i1 = 0; + for (ptr1 = start1; ptr1 != null; ptr1 = ptr1.next) { + a[i1++] = ptr1.val; + // storing the sorted values in the array + } + LinkListSort uu1 = new LinkListSort(); + // array b is not sorted and it will return false when checked with sorted list + return uu1.compare(a, b); + // The given array and the expected array is checked if both are same then true + // is displayed else false is displayed + case 3: + Task2 mm = new Task2(); + Node start2 = null; + Node prev2 = null; + Node fresh2; + Node ptr2; + for (int i2 = 0; i2 < a.length; i2++) { + // New nodes are created and values are added + fresh2 = new Node(); // Node class is created + fresh2.val = a[i2]; // Value is stored in the value part of the Node + if (start2 == null) { + start2 = fresh2; + } else { + prev2.next = fresh2; + } + prev2 = fresh2; + } + start2 = mm.sortByHeapSort(start2); + // method is being called + int i3 = 0; + for (ptr2 = start2; ptr2 != null; ptr2 = ptr2.next) { + a[i3++] = ptr2.val; + // storing the sorted values in the array + } + Arrays.sort(b); + // array b is sorted and it will return true when checked with sorted list + LinkListSort uu2 = new LinkListSort(); + return uu2.compare(a, b); + // The given array and the expected array is checked if both are same then true + // is displayed else false is displayed + default: + // default is used incase user puts a unauthorized value + System.out.println("Wrong choice"); + } + // Switch case is used to call the classes as per the user requirement + return false; + } + /** + * OUTPUT : + * Input - {89,56,98,123,26,75,12,40,39,68,91} is same for all the 3 classes + * Output: [12 26 39 40 56 68 75 89 91 98 123] is same for all the 3 classes + * 1st approach Time Complexity : O(n logn) + * Auxiliary Space Complexity : O(n) + * 2nd approach Time Complexity : O(n^2) + * Auxiliary Space Complexity : O(n) + * 3rd approach Time Complexity : O(n logn) + * Auxiliary Space Complexity : O(n) + */ + boolean compare(int[] a, int[] b) { + for (int i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + return false; + } + } + return true; + // Both the arrays are checked for equalness. If both are equal then true is + // returned else false is returned + } +} + +class Node { + + int val; + Node next; + // Node class for creation of linklist nodes +} + +class Task { + + private int[] a; + + public Node sortByMergeSort(Node head) { + if (head == null || head.next == null) { + return head; + } + int c = count(head); + a = new int[c]; + // Array of size c is created + int i = 0; + for (Node ptr = head; ptr != null; ptr = ptr.next) { + a[i++] = ptr.val; + } + // values are stored in the array + i = 0; + task(a, 0, c - 1); + // task method will be executed + for (Node ptr = head; ptr != null; ptr = ptr.next) { + ptr.val = a[i++]; + // Value is stored in the linklist after being sorted + } + return head; + } + + int count(Node head) { + int c = 0; + Node ptr; + for (ptr = head; ptr != null; ptr = ptr.next) { + c++; + } + return c; + // This Method is used to count number of elements/nodes present in the linklist + // It will return a integer type value denoting the number of nodes present + } + + void task(int[] n, int i, int j) { + if (i < j) { + int m = (i + j) / 2; + task(n, i, m); + task(n, m + 1, j); + task1(n, i, m, j); + // Array is halved and sent for sorting + } + } + + void task1(int[] n, int s, int m, int e) { + int i = s; + int k = 0; + int j = m + 1; + int[] b = new int[e - s + 1]; + while (i <= m && j <= e) { + if (n[j] >= n[i]) { + b[k++] = n[i++]; + } else { + b[k++] = n[j++]; + } + } + // Smallest number is stored after checking from both the arrays + while (i <= m) { + b[k++] = n[i++]; + } + while (j <= e) { + b[k++] = n[j++]; + } + for (int p = s; p <= e; p++) { + a[p] = b[p - s]; + } + } + // The method task and task1 is used to sort the linklist using merge sort +} + +class Task1 { + + public Node sortByInsertionSort(Node head) { + if (head == null || head.next == null) { + return head; + } + int c = count(head); + int[] a = new int[c]; + // Array of size c is created + a[0] = head.val; + int i; + Node ptr; + for (ptr = head.next, i = 1; ptr != null; ptr = ptr.next, i++) { + int j = i - 1; + while (j >= 0 && a[j] > ptr.val) { + // values are stored in the array + a[j + 1] = a[j]; + j--; + } + a[j + 1] = ptr.val; + } + i = 0; + for (ptr = head; ptr != null; ptr = ptr.next) { + ptr.val = a[i++]; + // Value is stored in the linklist after being sorted + } + return head; + } + + static int count(Node head) { + Node ptr; + int c = 0; + for (ptr = head; ptr != null; ptr = ptr.next) { + c++; + } + return c; + // This Method is used to count number of elements/nodes present in the linklist + // It will return a integer type value denoting the number of nodes present + } + // The method task and task1 is used to sort the linklist using insertion sort +} + +class Task2 { + + public Node sortByHeapSort(Node head) { + if (head == null || head.next == null) { + return head; + } + int c = count(head); + int[] a = new int[c]; + // Array of size c is created + int i = 0; + for (Node ptr = head; ptr != null; ptr = ptr.next) { + a[i++] = ptr.val; + // values are stored in the array + } + i = 0; + task(a); + for (Node ptr = head; ptr != null; ptr = ptr.next) { + ptr.val = a[i++]; + // Value is stored in the linklist after being sorted + } + return head; + } + + int count(Node head) { + int c = 0; + Node ptr; + for (ptr = head; ptr != null; ptr = ptr.next) { + c++; + } + return c; + // This Method is used to count number of elements/nodes present in the linklist + // It will return a integer type value denoting the number of nodes present + } + + void task(int[] n) { + int k = n.length; + for (int i = k / 2 - 1; i >= 0; i--) { + task1(n, k, i); + } + for (int i = k - 1; i > 0; i--) { + int d = n[0]; + n[0] = n[i]; + n[i] = d; + task1(n, i, 0); + // recursive calling of task1 method + } + } + + void task1(int[] n, int k, int i) { + int p = i; + int l = 2 * i + 1; + int r = 2 * i + 2; + if (l < k && n[l] > n[p]) { + p = l; + } + if (r < k && n[r] > n[p]) { + p = r; + } + if (p != i) { + int d = n[p]; + n[p] = n[i]; + n[i] = d; + task1(n, k, p); + } + } + // The method task and task1 is used to sort the linklist using heap sort +} diff --git a/src/main/java/com/thealgorithms/sorts/MergeSort.java b/src/main/java/com/thealgorithms/sorts/MergeSort.java new file mode 100644 index 000000000000..9949783ca21b --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/MergeSort.java @@ -0,0 +1,69 @@ +package com.thealgorithms.sorts; + +import static com.thealgorithms.sorts.SortUtils.less; + +/** + * Generic merge sort algorithm. + * + * @see SortAlgorithm + */ +class MergeSort implements SortAlgorithm { + + private Comparable[] aux; + + /** + * Generic merge sort algorithm implements. + * + * @param unsorted the array which should be sorted. + * @param <T> Comparable class. + * @return sorted array. + */ + @Override + public <T extends Comparable<T>> T[] sort(T[] unsorted) { + aux = new Comparable[unsorted.length]; + doSort(unsorted, 0, unsorted.length - 1); + return unsorted; + } + + /** + * @param arr the array to be sorted. + * @param left the first index of the array. + * @param right the last index of the array. + */ + private <T extends Comparable<T>> void doSort(T[] arr, int left, int right) { + if (left < right) { + int mid = (left + right) >>> 1; + doSort(arr, left, mid); + doSort(arr, mid + 1, right); + merge(arr, left, mid, right); + } + } + + /** + * Merges two parts of an array. + * + * @param arr the array to be merged. + * @param left the first index of the array. + * @param mid the middle index of the array. + * @param right the last index of the array merges two parts of an array in + * increasing order. + */ + @SuppressWarnings("unchecked") + private <T extends Comparable<T>> void merge(T[] arr, int left, int mid, int right) { + int i = left; + int j = mid + 1; + System.arraycopy(arr, left, aux, left, right + 1 - left); + + for (int k = left; k <= right; k++) { + if (j > right) { + arr[k] = (T) aux[i++]; + } else if (i > mid) { + arr[k] = (T) aux[j++]; + } else if (less(aux[j], aux[i])) { + arr[k] = (T) aux[j++]; + } else { + arr[k] = (T) aux[i++]; + } + } + } +} diff --git a/src/main/java/com/thealgorithms/sorts/MergeSortNoExtraSpace.java b/src/main/java/com/thealgorithms/sorts/MergeSortNoExtraSpace.java new file mode 100644 index 000000000000..7aa3b56e068c --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/MergeSortNoExtraSpace.java @@ -0,0 +1,88 @@ +package com.thealgorithms.sorts; + +import java.util.Arrays; + +/** + * Implementation of Merge Sort without using extra space for merging. + * This implementation performs in-place merging to sort the array of integers. + */ +public final class MergeSortNoExtraSpace { + private MergeSortNoExtraSpace() { + } + + /** + * Sorts the array using in-place merge sort algorithm. + * + * @param array the array to be sorted + * @return the sorted array + * @throws IllegalArgumentException If the array contains negative numbers. + */ + public static int[] sort(int[] array) { + if (array.length == 0) { + return array; + } + if (Arrays.stream(array).anyMatch(s -> s < 0)) { + throw new IllegalArgumentException("Implementation cannot sort negative numbers."); + } + + final int maxElement = Arrays.stream(array).max().getAsInt() + 1; + mergeSort(array, 0, array.length - 1, maxElement); + return array; + } + + /** + * Recursively divides the array into two halves, sorts and merges them. + * + * @param array the array to be sorted + * @param start the starting index of the array + * @param end the ending index of the array + * @param maxElement the value greater than any element in the array, used for encoding + */ + public static void mergeSort(int[] array, int start, int end, int maxElement) { + if (start < end) { + final int middle = (start + end) >>> 1; + mergeSort(array, start, middle, maxElement); + mergeSort(array, middle + 1, end, maxElement); + merge(array, start, middle, end, maxElement); + } + } + + /** + * Merges two sorted subarrays [start...middle] and [middle+1...end] in place. + * + * @param array the array containing the subarrays to be merged + * @param start the starting index of the first subarray + * @param middle the ending index of the first subarray and starting index of the second subarray + * @param end the ending index of the second subarray + * @param maxElement the value greater than any element in the array, used for encoding + */ + private static void merge(int[] array, int start, int middle, int end, int maxElement) { + int i = start; + int j = middle + 1; + int k = start; + while (i <= middle && j <= end) { + if (array[i] % maxElement <= array[j] % maxElement) { + array[k] = array[k] + (array[i] % maxElement) * maxElement; + k++; + i++; + } else { + array[k] = array[k] + (array[j] % maxElement) * maxElement; + k++; + j++; + } + } + while (i <= middle) { + array[k] = array[k] + (array[i] % maxElement) * maxElement; + k++; + i++; + } + while (j <= end) { + array[k] = array[k] + (array[j] % maxElement) * maxElement; + k++; + j++; + } + for (i = start; i <= end; i++) { + array[i] = array[i] / maxElement; + } + } +} diff --git a/src/main/java/com/thealgorithms/sorts/MergeSortRecursive.java b/src/main/java/com/thealgorithms/sorts/MergeSortRecursive.java new file mode 100644 index 000000000000..910157b83231 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/MergeSortRecursive.java @@ -0,0 +1,60 @@ +package com.thealgorithms.sorts; + +import java.util.ArrayList; +import java.util.List; + +public class MergeSortRecursive { + + List<Integer> arr; + + public MergeSortRecursive(List<Integer> arr) { + this.arr = arr; + } + + public List<Integer> mergeSort() { + return merge(arr); + } + + private static List<Integer> merge(List<Integer> arr) { + // base condition + if (arr.size() <= 1) { + return arr; + } + + int arrLength = arr.size(); + int half = arrLength / 2; + List<Integer> arrA = arr.subList(0, half); + List<Integer> arrB = arr.subList(half, arr.size()); + + // recursion + arrA = merge(arrA); + arrB = merge(arrB); + + return sort(arrA, arrB); + } + + private static List<Integer> sort(List<Integer> unsortedA, List<Integer> unsortedB) { + if (unsortedA.isEmpty() && unsortedB.isEmpty()) { + return new ArrayList<>(); + } + if (unsortedA.isEmpty()) { + return unsortedB; + } + if (unsortedB.isEmpty()) { + return unsortedA; + } + if (unsortedA.get(0) <= unsortedB.get(0)) { + List<Integer> newAl = new ArrayList<Integer>() { + { add(unsortedA.get(0)); } + }; + newAl.addAll(sort(unsortedA.subList(1, unsortedA.size()), unsortedB)); + return newAl; + } else { + List<Integer> newAl = new ArrayList<Integer>() { + { add(unsortedB.get(0)); } + }; + newAl.addAll(sort(unsortedA, unsortedB.subList(1, unsortedB.size()))); + return newAl; + } + } +} diff --git a/src/main/java/com/thealgorithms/sorts/OddEvenSort.java b/src/main/java/com/thealgorithms/sorts/OddEvenSort.java new file mode 100644 index 000000000000..ac94982c1474 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/OddEvenSort.java @@ -0,0 +1,51 @@ +package com.thealgorithms.sorts; + +/** + * OddEvenSort class implements the SortAlgorithm interface using the odd-even sort technique. + * Odd-even sort is a comparison sort related to bubble sort. + * It operates by comparing all (odd, even)-indexed pairs of adjacent elements in the list and, if a pair is in the wrong order, swapping them. + * The next step repeats this process for (even, odd)-indexed pairs. This process continues until the list is sorted. + * + */ +public final class OddEvenSort implements SortAlgorithm { + + /** + * Sorts the given array using the Odd-Even Sort algorithm. + * + * @param <T> the type of elements in the array, which must implement the Comparable interface + * @param array the array to be sorted + * @return the sorted array + */ + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + boolean sorted = false; + while (!sorted) { + sorted = performOddSort(array); + sorted = performEvenSort(array) && sorted; + } + + return array; + } + + private <T extends Comparable<T>> boolean performOddSort(T[] array) { + boolean sorted = true; + for (int i = 1; i < array.length - 1; i += 2) { + if (array[i].compareTo(array[i + 1]) > 0) { + SortUtils.swap(array, i, i + 1); + sorted = false; + } + } + return sorted; + } + + private <T extends Comparable<T>> boolean performEvenSort(T[] array) { + boolean sorted = true; + for (int i = 0; i < array.length - 1; i += 2) { + if (array[i].compareTo(array[i + 1]) > 0) { + SortUtils.swap(array, i, i + 1); + sorted = false; + } + } + return sorted; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/PancakeSort.java b/src/main/java/com/thealgorithms/sorts/PancakeSort.java new file mode 100644 index 000000000000..6079672a1d77 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/PancakeSort.java @@ -0,0 +1,44 @@ +package com.thealgorithms.sorts; + +/** + * Implementation of pancake sort + * + * @author Podshivalov Nikita (https://github.com/nikitap492) + * @since 2018-04-10 + */ +public class PancakeSort implements SortAlgorithm { + + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + if (array.length < 2) { + return array; + } + + for (int currentSize = 0; currentSize < array.length; currentSize++) { + int maxIndex = findMaxIndex(array, currentSize); + SortUtils.flip(array, maxIndex, array.length - 1 - currentSize); + } + + return array; + } + + /** + * Finds the index of the maximum element in the array up to the given size. + * + * @param array the array to be searched + * @param currentSize the current size of the unsorted portion of the array + * @param <T> the type of elements in the array + * @return the index of the maximum element + */ + private <T extends Comparable<T>> int findMaxIndex(T[] array, int currentSize) { + T max = array[0]; + int maxIndex = 0; + for (int i = 0; i < array.length - currentSize; i++) { + if (SortUtils.less(max, array[i])) { + max = array[i]; + maxIndex = i; + } + } + return maxIndex; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/PatienceSort.java b/src/main/java/com/thealgorithms/sorts/PatienceSort.java new file mode 100644 index 000000000000..0edce8d9a15d --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/PatienceSort.java @@ -0,0 +1,112 @@ +package com.thealgorithms.sorts; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.PriorityQueue; + +/** + * This class implements the Patience Sort algorithm. Patience Sort is a sorting algorithm that + * is particularly good for sorting sequences that are already partially sorted. + */ +public class PatienceSort implements SortAlgorithm { + + /** + * Sorts an array of comparable elements using the Patience Sort algorithm. + * + * @param array the array to be sorted + * @param <T> the type of elements in the array, must be comparable + * @return the sorted array + */ + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + if (array.length == 0) { + return array; + } + + final List<List<T>> piles = formPiles(array); + final PriorityQueue<PileNode<T>> pq = mergePiles(piles); + extractPiles(array, pq); + + return array; + } + + /** + * Forms piles from the given array. Each pile is a list of elements where + * each element is smaller than the one before it. Binary search is used + * to find the appropriate pile for each element. + * + * @param array the array of elements to be organized into piles + * @param <T> the type of elements in the array, must be comparable + * @return a list of piles + */ + private static <T extends Comparable<T>> List<List<T>> formPiles(final T[] array) { + final List<List<T>> piles = new ArrayList<>(); + final List<T> lastElements = new ArrayList<>(); + + for (T x : array) { + int pos = Collections.binarySearch(lastElements, x); + if (pos < 0) { + pos = -pos - 1; + } + + if (pos < piles.size()) { + piles.get(pos).add(x); + lastElements.set(pos, x); + } else { + List<T> newPile = new ArrayList<>(); + newPile.add(x); + piles.add(newPile); + lastElements.add(x); + } + } + + return piles; + } + + /** + * Merges the piles into a priority queue where the smallest elements are + * prioritized. + * + * @param piles the list of piles to be merged + * @param <T> the type of elements in the piles, must be comparable + * @return a priority queue containing the top element of each pile + */ + private static <T extends Comparable<T>> PriorityQueue<PileNode<T>> mergePiles(final Iterable<List<T>> piles) { + PriorityQueue<PileNode<T>> pq = new PriorityQueue<>(); + for (List<T> pile : piles) { + pq.add(new PileNode<>(pile.removeLast(), pile)); + } + return pq; + } + + /** + * Extracts elements from the priority queue to form the sorted array. + * + * @param array the array to be filled with sorted elements + * @param pq the priority queue containing the elements to be extracted + * @param <T> the type of elements in the array, must be comparable + */ + private static <T extends Comparable<T>> void extractPiles(final T[] array, final PriorityQueue<PileNode<T>> pq) { + int index = 0; + while (!pq.isEmpty()) { + PileNode<T> node = pq.poll(); + array[index++] = node.value; + if (!node.pile.isEmpty()) { + pq.add(new PileNode<>(node.pile.removeLast(), node.pile)); + } + } + } + + /** + * A helper record representing a node in the priority queue. + * + * @param <T> the type of elements in the node, must be comparable + */ + private record PileNode<T extends Comparable<T>>(T value, List<T> pile) implements Comparable<PileNode<T>> { + @Override + public int compareTo(PileNode<T> other) { + return this.value.compareTo(other.value); + } + } +} diff --git a/src/main/java/com/thealgorithms/sorts/PigeonholeSort.java b/src/main/java/com/thealgorithms/sorts/PigeonholeSort.java new file mode 100644 index 000000000000..19f4291d8213 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/PigeonholeSort.java @@ -0,0 +1,89 @@ +package com.thealgorithms.sorts; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public final class PigeonholeSort { + private PigeonholeSort() { + } + + /** + * Sorts the given array using the pigeonhole sort algorithm. + * + * @param array the array to be sorted + * @throws IllegalArgumentException if any negative integers are found + * @return the sorted array + */ + public static int[] sort(int[] array) { + + checkForNegativeInput(array); + + if (array.length == 0) { + return array; + } + + final int maxElement = Arrays.stream(array).max().orElseThrow(); + final List<List<Integer>> pigeonHoles = createPigeonHoles(maxElement); + + populatePigeonHoles(array, pigeonHoles); + collectFromPigeonHoles(array, pigeonHoles); + + return array; + } + + /** + * Checks if the array contains any negative integers. + * + * @param array the array to be checked + * @throws IllegalArgumentException if any negative integers are found + */ + private static void checkForNegativeInput(int[] array) { + for (final int number : array) { + if (number < 0) { + throw new IllegalArgumentException("Array contains negative integers."); + } + } + } + + /** + * Creates pigeonholes for sorting using an ArrayList of ArrayLists. + * + * @param maxElement the maximum element in the array + * @return an ArrayList of ArrayLists + */ + private static List<List<Integer>> createPigeonHoles(int maxElement) { + List<List<Integer>> pigeonHoles = new ArrayList<>(maxElement + 1); + for (int i = 0; i <= maxElement; i++) { + pigeonHoles.add(new ArrayList<>()); + } + return pigeonHoles; + } + + /** + * Populates the pigeonholes with elements from the array. + * + * @param array the array to be sorted + * @param pigeonHoles the pigeonholes to be populated + */ + private static void populatePigeonHoles(int[] array, List<List<Integer>> pigeonHoles) { + for (int element : array) { + pigeonHoles.get(element).add(element); + } + } + + /** + * Collects sorted elements from the pigeonholes back into the array. + * + * @param array the array to be sorted + * @param pigeonHoles the populated pigeonholes + */ + private static void collectFromPigeonHoles(int[] array, Iterable<List<Integer>> pigeonHoles) { + int index = 0; + for (final var pigeonHole : pigeonHoles) { + for (final int element : pigeonHole) { + array[index++] = element; + } + } + } +} diff --git a/src/main/java/com/thealgorithms/sorts/QuickSort.java b/src/main/java/com/thealgorithms/sorts/QuickSort.java new file mode 100644 index 000000000000..3abb1aae2306 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/QuickSort.java @@ -0,0 +1,77 @@ +package com.thealgorithms.sorts; + +/** + * @author Varun Upadhyay (https://github.com/varunu28) + * @author Podshivalov Nikita (https://github.com/nikitap492) + * @see SortAlgorithm + */ +class QuickSort implements SortAlgorithm { + + /** + * This method implements the Generic Quick Sort + * + * @param array The array to be sorted Sorts the array in increasing order + */ + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + doSort(array, 0, array.length - 1); + return array; + } + + /** + * The sorting process + * + * @param left The first index of an array + * @param right The last index of an array + * @param array The array to be sorted + */ + private static <T extends Comparable<T>> void doSort(T[] array, final int left, final int right) { + if (left < right) { + final int pivot = randomPartition(array, left, right); + doSort(array, left, pivot - 1); + doSort(array, pivot, right); + } + } + + /** + * Randomize the array to avoid the basically ordered sequences + * + * @param array The array to be sorted + * @param left The first index of an array + * @param right The last index of an array + * @return the partition index of the array + */ + private static <T extends Comparable<T>> int randomPartition(T[] array, final int left, final int right) { + final int randomIndex = left + (int) (Math.random() * (right - left + 1)); + SortUtils.swap(array, randomIndex, right); + return partition(array, left, right); + } + + /** + * This method finds the partition index for an array + * + * @param array The array to be sorted + * @param left The first index of an array + * @param right The last index of an array Finds the partition index of an + * array + */ + private static <T extends Comparable<T>> int partition(T[] array, int left, int right) { + final int mid = (left + right) >>> 1; + final T pivot = array[mid]; + + while (left <= right) { + while (SortUtils.less(array[left], pivot)) { + ++left; + } + while (SortUtils.less(pivot, array[right])) { + --right; + } + if (left <= right) { + SortUtils.swap(array, left, right); + ++left; + --right; + } + } + return left; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/RadixSort.java b/src/main/java/com/thealgorithms/sorts/RadixSort.java new file mode 100644 index 000000000000..f0201a5a84b8 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/RadixSort.java @@ -0,0 +1,98 @@ +package com.thealgorithms.sorts; + +import com.thealgorithms.maths.NumberOfDigits; +import java.util.Arrays; + +/** + * This class provides an implementation of the radix sort algorithm. + * It sorts an array of nonnegative integers in increasing order. + */ +public final class RadixSort { + private static final int BASE = 10; + + private RadixSort() { + } + + /** + * Sorts an array of nonnegative integers using the radix sort algorithm. + * + * @param array the array to be sorted + * @return the sorted array + * @throws IllegalArgumentException if any negative integers are found + */ + public static int[] sort(int[] array) { + if (array.length == 0) { + return array; + } + + checkForNegativeInput(array); + radixSort(array); + return array; + } + + /** + * Checks if the array contains any negative integers. + * + * @param array the array to be checked + * @throws IllegalArgumentException if any negative integers are found + */ + private static void checkForNegativeInput(int[] array) { + for (int number : array) { + if (number < 0) { + throw new IllegalArgumentException("Array contains non-positive integers."); + } + } + } + + private static void radixSort(int[] array) { + final int max = Arrays.stream(array).max().getAsInt(); + for (int i = 0, exp = 1; i < NumberOfDigits.numberOfDigits(max); i++, exp *= BASE) { + countingSortByDigit(array, exp); + } + } + + /** + * A utility method to perform counting sort of array[] according to the digit represented by exp. + * + * @param array the array to be sorted + * @param exp the exponent representing the current digit position + */ + private static void countingSortByDigit(int[] array, int exp) { + int[] count = countDigits(array, exp); + accumulateCounts(count); + int[] output = buildOutput(array, exp, count); + copyOutput(array, output); + } + + private static int[] countDigits(int[] array, int exp) { + int[] count = new int[BASE]; + for (int i = 0; i < array.length; i++) { + count[getDigit(array[i], exp)]++; + } + return count; + } + + private static int getDigit(int number, int position) { + return (number / position) % BASE; + } + + private static void accumulateCounts(int[] count) { + for (int i = 1; i < BASE; i++) { + count[i] += count[i - 1]; + } + } + + private static int[] buildOutput(int[] array, int exp, int[] count) { + int[] output = new int[array.length]; + for (int i = array.length - 1; i >= 0; i--) { + int digit = getDigit(array[i], exp); + output[count[digit] - 1] = array[i]; + count[digit]--; + } + return output; + } + + private static void copyOutput(int[] array, int[] output) { + System.arraycopy(output, 0, array, 0, array.length); + } +} diff --git a/src/main/java/com/thealgorithms/sorts/SelectionSort.java b/src/main/java/com/thealgorithms/sorts/SelectionSort.java new file mode 100644 index 000000000000..dbb2b88ffcef --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/SelectionSort.java @@ -0,0 +1,30 @@ +package com.thealgorithms.sorts; + +public class SelectionSort implements SortAlgorithm { + /** + * Sorts an array of comparable elements in increasing order using the selection sort algorithm. + * + * @param array the array to be sorted + * @param <T> the class of array elements + * @return the sorted array + */ + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + + for (int i = 0; i < array.length - 1; i++) { + final int minIndex = findIndexOfMin(array, i); + SortUtils.swap(array, i, minIndex); + } + return array; + } + + private static <T extends Comparable<T>> int findIndexOfMin(T[] array, final int startIndex) { + int minIndex = startIndex; + for (int i = startIndex + 1; i < array.length; i++) { + if (array[i].compareTo(array[minIndex]) < 0) { + minIndex = i; + } + } + return minIndex; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/SelectionSortRecursive.java b/src/main/java/com/thealgorithms/sorts/SelectionSortRecursive.java new file mode 100644 index 000000000000..9d24542de592 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/SelectionSortRecursive.java @@ -0,0 +1,61 @@ +package com.thealgorithms.sorts; + +/** + * Class that implements the Selection Sort algorithm using recursion. + */ +public class SelectionSortRecursive implements SortAlgorithm { + + /** + * Sorts an array using recursive selection sort. + * + * @param array the array to be sorted + * @param <T> the type of elements in the array (must be Comparable) + * @return the sorted array + */ + public <T extends Comparable<T>> T[] sort(T[] array) { + if (array.length == 0) { + return array; + } + recursiveSelectionSort(array, 0); + return array; + } + + /** + * Recursively sorts the array using selection sort. + * + * @param array the array to be sorted + * @param index the current index to start sorting from + * @param <T> the type of elements in the array (must be Comparable) + */ + private static <T extends Comparable<T>> void recursiveSelectionSort(T[] array, final int index) { + if (index == array.length - 1) { + return; + } + + SortUtils.swap(array, index, findMinIndex(array, index)); + + // Recursively call selection sort for the remaining array + recursiveSelectionSort(array, index + 1); + } + + /** + * Finds the index of the minimum element in the array starting from the given index. + * + * @param array the array to search + * @param start the starting index for the search + * @param <T> the type of elements in the array + * @return the index of the minimum element + */ + private static <T extends Comparable<T>> int findMinIndex(T[] array, final int start) { + // Base case: if start is the last index, return start + if (start == array.length - 1) { + return start; + } + + // Recursive call to find the minimum index in the rest of the array + final int minIndexInRest = findMinIndex(array, start + 1); + + // Return the index of the smaller element between array[start] and the minimum element in the rest of the array + return array[start].compareTo(array[minIndexInRest]) < 0 ? start : minIndexInRest; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/ShellSort.java b/src/main/java/com/thealgorithms/sorts/ShellSort.java new file mode 100644 index 000000000000..d12b181e65a2 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/ShellSort.java @@ -0,0 +1,69 @@ +package com.thealgorithms.sorts; + +public class ShellSort implements SortAlgorithm { + + /** + * Implements generic shell sort. + * + * @param array the array to be sorted. + * @param <T> the type of elements in the array. + * @return the sorted array. + */ + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + if (array.length == 0) { + return array; + } + + int gap = calculateInitialGap(array.length); + + while (gap > 0) { + performGapInsertionSort(array, gap); + gap = calculateNextGap(gap); + } + + return array; + } + + /** + * Calculates the initial gap value using the Knuth sequence. + * + * @param length the length of the array. + * @return the initial gap value. + */ + private int calculateInitialGap(final int length) { + int gap = 1; + while (gap < length / 3) { + gap = 3 * gap + 1; + } + return gap; + } + + /** + * Calculates the next gap value. + * + * @param currentGap the current gap value. + * @return the next gap value. + */ + private int calculateNextGap(final int currentGap) { + return currentGap / 3; + } + + /** + * Performs an insertion sort for the specified gap value. + * + * @param array the array to be sorted. + * @param gap the current gap value. + * @param <T> the type of elements in the array. + */ + private <T extends Comparable<T>> void performGapInsertionSort(final T[] array, final int gap) { + for (int i = gap; i < array.length; i++) { + T temp = array[i]; + int j; + for (j = i; j >= gap && SortUtils.less(temp, array[j - gap]); j -= gap) { + array[j] = array[j - gap]; + } + array[j] = temp; + } + } +} diff --git a/src/main/java/com/thealgorithms/sorts/SimpleSort.java b/src/main/java/com/thealgorithms/sorts/SimpleSort.java new file mode 100644 index 000000000000..a03223cb01a1 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/SimpleSort.java @@ -0,0 +1,15 @@ +package com.thealgorithms.sorts; + +public class SimpleSort implements SortAlgorithm { + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + for (int i = 0; i < array.length; i++) { + for (int j = i + 1; j < array.length; j++) { + if (SortUtils.less(array[j], array[i])) { + SortUtils.swap(array, i, j); + } + } + } + return array; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/SlowSort.java b/src/main/java/com/thealgorithms/sorts/SlowSort.java new file mode 100644 index 000000000000..38a5fa458778 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/SlowSort.java @@ -0,0 +1,27 @@ +package com.thealgorithms.sorts; + +/** + * @author Amir Hassan (https://github.com/ahsNT) + * @see SortAlgorithm + */ +public class SlowSort implements SortAlgorithm { + + @Override + public <T extends Comparable<T>> T[] sort(T[] unsortedArray) { + sort(unsortedArray, 0, unsortedArray.length - 1); + return unsortedArray; + } + + private <T extends Comparable<T>> void sort(T[] array, int i, int j) { + if (SortUtils.greaterOrEqual(i, j)) { + return; + } + final int m = (i + j) >>> 1; + sort(array, i, m); + sort(array, m + 1, j); + if (SortUtils.less(array[j], array[m])) { + SortUtils.swap(array, j, m); + } + sort(array, i, j - 1); + } +} diff --git a/Sorts/SortAlgorithm.java b/src/main/java/com/thealgorithms/sorts/SortAlgorithm.java similarity index 87% rename from Sorts/SortAlgorithm.java rename to src/main/java/com/thealgorithms/sorts/SortAlgorithm.java index d4d0309da27d..7a3ded37bf3f 100644 --- a/Sorts/SortAlgorithm.java +++ b/src/main/java/com/thealgorithms/sorts/SortAlgorithm.java @@ -1,4 +1,4 @@ -package Sorts; +package com.thealgorithms.sorts; import java.util.Arrays; import java.util.List; @@ -7,9 +7,8 @@ * The common interface of most sorting algorithms * * @author Podshivalov Nikita (https://github.com/nikitap492) - **/ + */ public interface SortAlgorithm { - /** * Main method arrays sorting algorithms * @@ -25,8 +24,7 @@ public interface SortAlgorithm { * @return a sorted list */ @SuppressWarnings("unchecked") - default <T extends Comparable<T>> List<T> sort(List<T> unsorted) { + default<T extends Comparable<T>> List<T> sort(List<T> unsorted) { return Arrays.asList(sort(unsorted.toArray((T[]) new Comparable[unsorted.size()]))); } - } diff --git a/src/main/java/com/thealgorithms/sorts/SortUtils.java b/src/main/java/com/thealgorithms/sorts/SortUtils.java new file mode 100644 index 000000000000..9e51c3d9e09a --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/SortUtils.java @@ -0,0 +1,121 @@ +package com.thealgorithms.sorts; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +final class SortUtils { + private SortUtils() { + } + + /** + * Swaps two elements at the given positions in an array. + * + * @param array the array in which to swap elements + * @param i the index of the first element to swap + * @param j the index of the second element to swap + * @param <T> the type of elements in the array + */ + public static <T> void swap(T[] array, int i, int j) { + if (i != j) { + final T temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } + } + + /** + * Compares two elements to see if the first is less than the second. + * + * @param firstElement the first element to compare + * @param secondElement the second element to compare + * @return true if the first element is less than the second, false otherwise + */ + public static <T extends Comparable<T>> boolean less(T firstElement, T secondElement) { + return firstElement.compareTo(secondElement) < 0; + } + + /** + * Compares two elements to see if the first is greater than the second. + * + * @param firstElement the first element to compare + * @param secondElement the second element to compare + * @return true if the first element is greater than the second, false otherwise + */ + public static <T extends Comparable<T>> boolean greater(T firstElement, T secondElement) { + return firstElement.compareTo(secondElement) > 0; + } + + /** + * Compares two elements to see if the first is greater than or equal to the second. + * + * @param firstElement the first element to compare + * @param secondElement the second element to compare + * @return true if the first element is greater than or equal to the second, false otherwise + */ + static <T extends Comparable<T>> boolean greaterOrEqual(T firstElement, T secondElement) { + return firstElement.compareTo(secondElement) >= 0; + } + + /** + * Prints the elements of a list to standard output. + * + * @param listToPrint the list to print + */ + static void print(List<?> listToPrint) { + String result = listToPrint.stream().map(Object::toString).collect(Collectors.joining(" ")); + System.out.println(result); + } + + /** + * Prints the elements of an array to standard output. + * + * @param array the array to print + */ + static <T> void print(T[] array) { + System.out.println(Arrays.toString(array)); + } + + /** + * Flips the order of elements in the specified range of an array. + * + * @param array the array whose elements are to be flipped + * @param left the left boundary of the range to be flipped (inclusive) + * @param right the right boundary of the range to be flipped (inclusive) + */ + public static <T extends Comparable<T>> void flip(T[] array, int left, int right) { + while (left <= right) { + swap(array, left++, right--); + } + } + + /** + * Checks whether the array is sorted in ascending order. + * + * @param array the array to check + * @return true if the array is sorted in ascending order, false otherwise + */ + public static <T extends Comparable<T>> boolean isSorted(T[] array) { + for (int i = 1; i < array.length; i++) { + if (less(array[i], array[i - 1])) { + return false; + } + } + return true; + } + + /** + * Checks whether the list is sorted in ascending order. + * + * @param list the list to check + * @return true if the list is sorted in ascending order, false otherwise + */ + public static <T extends Comparable<T>> boolean isSorted(List<T> list) { + for (int i = 1; i < list.size(); i++) { + if (less(list.get(i), list.get(i - 1))) { + return false; + } + } + return true; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/SortUtilsRandomGenerator.java b/src/main/java/com/thealgorithms/sorts/SortUtilsRandomGenerator.java new file mode 100644 index 000000000000..ed2f538f4d89 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/SortUtilsRandomGenerator.java @@ -0,0 +1,48 @@ +package com.thealgorithms.sorts; + +import java.util.Random; + +public final class SortUtilsRandomGenerator { + private SortUtilsRandomGenerator() { + } + + private static final Random RANDOM; + private static final long SEED; + + static { + SEED = System.currentTimeMillis(); + RANDOM = new Random(SEED); + } + + /** + * Function to generate array of double values, with predefined size. + * + * @param size result array size + * @return array of Double values, randomly generated, each element is between [0, 1) + */ + public static Double[] generateArray(int size) { + Double[] arr = new Double[size]; + for (int i = 0; i < size; i++) { + arr[i] = generateDouble(); + } + return arr; + } + + /** + * Function to generate Double value. + * + * @return Double value [0, 1) + */ + public static Double generateDouble() { + return RANDOM.nextDouble(); + } + + /** + * Function to generate int value. + * + * @return int value [0, n) + */ + public static int generateInt(int n) { + return RANDOM.nextInt(n); + } +} diff --git a/src/main/java/com/thealgorithms/sorts/SpreadSort.java b/src/main/java/com/thealgorithms/sorts/SpreadSort.java new file mode 100644 index 000000000000..f1fd24f4735d --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/SpreadSort.java @@ -0,0 +1,273 @@ +package com.thealgorithms.sorts; +import java.util.Arrays; + +/** + * SpreadSort is a highly efficient sorting algorithm suitable for large datasets. + * It distributes elements into buckets and recursively sorts these buckets. + * This implementation is generic and can sort any array of elements that extend Comparable. + */ +public class SpreadSort implements SortAlgorithm { + private static final int MAX_INSERTION_SORT_THRESHOLD = 1000; + private static final int MAX_INITIAL_BUCKET_CAPACITY = 1000; + private static final int MAX_MIN_BUCKETS = 100; + + private final int insertionSortThreshold; + private final int initialBucketCapacity; + private final int minBuckets; + + /** + * Constructor to initialize the SpreadSort algorithm with custom parameters. + * + * @param insertionSortThreshold the threshold for using insertion sort for small segments (1-1000) + * @param initialBucketCapacity the initial capacity for each bucket (1-1000) + * @param minBuckets the minimum number of buckets to use (1-100) + */ + public SpreadSort(int insertionSortThreshold, int initialBucketCapacity, int minBuckets) { + if (insertionSortThreshold < 1 || insertionSortThreshold > MAX_INSERTION_SORT_THRESHOLD) { + throw new IllegalArgumentException("Insertion sort threshold must be between 1 and " + MAX_INSERTION_SORT_THRESHOLD); + } + if (initialBucketCapacity < 1 || initialBucketCapacity > MAX_INITIAL_BUCKET_CAPACITY) { + throw new IllegalArgumentException("Initial bucket capacity must be between 1 and " + MAX_INITIAL_BUCKET_CAPACITY); + } + if (minBuckets < 1 || minBuckets > MAX_MIN_BUCKETS) { + throw new IllegalArgumentException("Minimum number of buckets must be between 1 and " + MAX_MIN_BUCKETS); + } + + this.insertionSortThreshold = insertionSortThreshold; + this.initialBucketCapacity = initialBucketCapacity; + this.minBuckets = minBuckets; + } + + /** + * Default constructor with predefined values. + */ + public SpreadSort() { + this(16, 16, 2); + } + + /** + * Sorts an array using the SpreadSort algorithm. + * + * @param array the array to be sorted + * @param <T> the type of elements in the array + * @return the sorted array + */ + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + if (array.length == 0) { + return array; + } + spreadSort(array, 0, array.length - 1); + return array; + } + + /** + * Internal method to sort an array segment using the SpreadSort algorithm. + * + * @param array the array to be sorted + * @param left the left boundary of the segment + * @param right the right boundary of the segment + * @param <T> the type of elements in the array + */ + private <T extends Comparable<T>> void spreadSort(final T[] array, final int left, final int right) { + if (left >= right) { + return; + } + + // Base case for small segments + if (right - left < insertionSortThreshold) { + insertionSort(array, left, right); + return; + } + + T min = findMin(array, left, right); + T max = findMax(array, left, right); + + if (min.equals(max)) { + return; // All elements are the same + } + + int numBuckets = calculateNumBuckets(right - left + 1); + final Bucket<T>[] buckets = createBuckets(numBuckets); + + distributeElements(array, left, right, min, max, numBuckets, buckets); + collectElements(array, left, buckets); + } + + /** + * Finds the minimum element in the specified segment of the array. + * + * @param array the array to search + * @param left the left boundary of the segment + * @param right the right boundary of the segment + * @param <T> the type of elements in the array + * @return the minimum element + */ + private <T extends Comparable<T>> T findMin(final T[] array, final int left, final int right) { + T min = array[left]; + for (int i = left + 1; i <= right; i++) { + if (SortUtils.less(array[i], min)) { + min = array[i]; + } + } + return min; + } + + /** + * Finds the maximum element in the specified segment of the array. + * + * @param array the array to search + * @param left the left boundary of the segment + * @param right the right boundary of the segment + * @param <T> the type of elements in the array + * @return the maximum element + */ + private <T extends Comparable<T>> T findMax(final T[] array, final int left, final int right) { + T max = array[left]; + for (int i = left + 1; i <= right; i++) { + if (SortUtils.greater(array[i], max)) { + max = array[i]; + } + } + return max; + } + + /** + * Calculates the number of buckets needed based on the size of the segment. + * + * @param segmentSize the size of the segment + * @return the number of buckets + */ + private int calculateNumBuckets(final int segmentSize) { + int numBuckets = segmentSize / insertionSortThreshold; + return Math.max(numBuckets, minBuckets); + } + + /** + * Creates an array of buckets. + * + * @param numBuckets the number of buckets to create + * @param <T> the type of elements in the buckets + * @return an array of buckets + */ + @SuppressWarnings("unchecked") + private <T extends Comparable<T>> Bucket<T>[] createBuckets(final int numBuckets) { + final Bucket<T>[] buckets = new Bucket[numBuckets]; + for (int i = 0; i < numBuckets; i++) { + buckets[i] = new Bucket<>(initialBucketCapacity); + } + return buckets; + } + + /** + * Distributes elements of the array segment into buckets. + * + * @param array the array to be sorted + * @param left the left boundary of the segment + * @param right the right boundary of the segment + * @param min the minimum element in the segment + * @param max the maximum element in the segment + * @param numBuckets the number of buckets + * @param buckets the array of buckets + * @param <T> the type of elements in the array + */ + private <T extends Comparable<T>> void distributeElements(final T[] array, final int left, final int right, final T min, final T max, final int numBuckets, final Bucket<T>[] buckets) { + final double range = max.compareTo(min); + for (int i = left; i <= right; i++) { + final int scaleRangeDifference = array[i].compareTo(min) * numBuckets; + int bucketIndex = (int) (scaleRangeDifference / (range + 1)); + buckets[bucketIndex].add(array[i]); + } + } + + /** + * Collects elements from the buckets back into the array. + * + * @param array the array to be sorted + * @param left the left boundary of the segment + * @param buckets the array of buckets + * @param <T> the type of elements in the array + */ + private <T extends Comparable<T>> void collectElements(final T[] array, final int left, final Bucket<T>[] buckets) { + int index = left; + for (Bucket<T> bucket : buckets) { + if (bucket.size() > 0) { + T[] bucketArray = bucket.toArray(); + spreadSort(bucketArray, 0, bucketArray.length - 1); + for (T element : bucketArray) { + array[index++] = element; + } + } + } + } + + /** + * Insertion sort implementation for small segments. + * + * @param array the array to be sorted + * @param left the left boundary of the segment + * @param right the right boundary of the segment + * @param <T> the type of elements in the array + */ + private <T extends Comparable<T>> void insertionSort(final T[] array, final int left, final int right) { + for (int i = left + 1; i <= right; i++) { + T key = array[i]; + int j = i - 1; + while (j >= left && SortUtils.greater(array[j], key)) { + array[j + 1] = array[j]; + j--; + } + array[j + 1] = key; + } + } + + /** + * Bucket class to hold elements during sorting. + * + * @param <T> the type of elements in the bucket + */ + private static class Bucket<T extends Comparable<T>> { + private T[] elements; + private int size; + + /** + * Constructs a new bucket with initial capacity. + */ + @SuppressWarnings("unchecked") + Bucket(int initialBucketCapacity) { + elements = (T[]) new Comparable[initialBucketCapacity]; + size = 0; + } + + /** + * Adds an element to the bucket. + * + * @param element the element to add + */ + void add(T element) { + if (size == elements.length) { + elements = Arrays.copyOf(elements, size * 2); + } + elements[size++] = element; + } + + /** + * Returns the number of elements in the bucket. + * + * @return the size of the bucket + */ + int size() { + return size; + } + + /** + * Returns an array containing all elements in the bucket. + * + * @return an array containing all elements in the bucket + */ + @SuppressWarnings("unchecked") + T[] toArray() { + return Arrays.copyOf(elements, size); + } + } +} diff --git a/src/main/java/com/thealgorithms/sorts/StalinSort.java b/src/main/java/com/thealgorithms/sorts/StalinSort.java new file mode 100644 index 000000000000..5aaf530fd94c --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/StalinSort.java @@ -0,0 +1,21 @@ +package com.thealgorithms.sorts; + +public class StalinSort implements SortAlgorithm { + @SuppressWarnings("unchecked") + public <T extends Comparable<T>> T[] sort(T[] array) { + if (array.length == 0) { + return array; + } + int currentIndex = 0; + for (int i = 1; i < array.length; i++) { + if (array[i].compareTo(array[currentIndex]) >= 0) { + currentIndex++; + array[currentIndex] = array[i]; + } + } + // Create a result array with sorted elements + T[] result = (T[]) java.lang.reflect.Array.newInstance(array.getClass().getComponentType(), currentIndex + 1); + System.arraycopy(array, 0, result, 0, currentIndex + 1); + return result; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/StoogeSort.java b/src/main/java/com/thealgorithms/sorts/StoogeSort.java new file mode 100644 index 000000000000..2a6e04ce29c7 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/StoogeSort.java @@ -0,0 +1,34 @@ +package com.thealgorithms.sorts; + +/** + * @author Amir Hassan (https://github.com/ahsNT) + * @see SortAlgorithm + */ +public class StoogeSort implements SortAlgorithm { + + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + if (array.length == 0) { + return array; + } + sort(array, 0, array.length); + return array; + } + + public <T extends Comparable<T>> T[] sort(final T[] array, final int start, final int end) { + if (SortUtils.less(array[end - 1], array[start])) { + final T temp = array[start]; + array[start] = array[end - 1]; + array[end - 1] = temp; + } + + final int length = end - start; + if (length > 2) { + int third = length / 3; + sort(array, start, end - third); + sort(array, start + third, end); + sort(array, start, end - third); + } + return array; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/StrandSort.java b/src/main/java/com/thealgorithms/sorts/StrandSort.java new file mode 100644 index 000000000000..147d11340d8a --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/StrandSort.java @@ -0,0 +1,79 @@ +package com.thealgorithms.sorts; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * StrandSort class implementing the SortAlgorithm interface using arrays. + */ +public final class StrandSort implements SortAlgorithm { + + /** + * Sorts the given array using the Strand Sort algorithm. + * + * @param <T> The type of elements to be sorted, must be Comparable. + * @param array The array to be sorted. + * @return The sorted array. + */ + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + List<T> unsortedList = new ArrayList<>(Arrays.asList(array)); + List<T> sortedList = strandSort(unsortedList); + return sortedList.toArray(array); + } + + /** + * Strand Sort algorithm that sorts a list. + * + * @param <T> The type of elements to be sorted, must be Comparable. + * @param list The list to be sorted. + * @return The sorted list. + */ + private static <T extends Comparable<? super T>> List<T> strandSort(List<T> list) { + if (list.size() <= 1) { + return list; + } + + List<T> result = new ArrayList<>(); + while (!list.isEmpty()) { + final List<T> sorted = new ArrayList<>(); + sorted.add(list.removeFirst()); + for (int i = 0; i < list.size();) { + if (sorted.getLast().compareTo(list.get(i)) <= 0) { + sorted.add(list.remove(i)); + } else { + i++; + } + } + result = merge(result, sorted); + } + return result; + } + + /** + * Merges two sorted lists into one sorted list. + * + * @param <T> The type of elements to be sorted, must be Comparable. + * @param left The first sorted list. + * @param right The second sorted list. + * @return The merged sorted list. + */ + private static <T extends Comparable<? super T>> List<T> merge(List<T> left, List<T> right) { + List<T> result = new ArrayList<>(); + int i = 0; + int j = 0; + while (i < left.size() && j < right.size()) { + if (left.get(i).compareTo(right.get(j)) <= 0) { + result.add(left.get(i)); + i++; + } else { + result.add(right.get(j)); + j++; + } + } + result.addAll(left.subList(i, left.size())); + result.addAll(right.subList(j, right.size())); + return result; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/SwapSort.java b/src/main/java/com/thealgorithms/sorts/SwapSort.java new file mode 100644 index 000000000000..b5c1c3892546 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/SwapSort.java @@ -0,0 +1,39 @@ +package com.thealgorithms.sorts; + +/** + * The idea of Swap-Sort is to count the number m of smaller values (that are in + * A) from each element of an array A(1...n) and then swap the element with the + * element in A(m+1). This ensures that the exchanged element is already in the + * correct, i.e. final, position. The disadvantage of this algorithm is that + * each element may only occur once, otherwise there is no termination. + */ +public class SwapSort implements SortAlgorithm { + + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + int index = 0; + + while (index < array.length - 1) { + final int amountSmallerElements = this.getSmallerElementCount(array, index); + + if (amountSmallerElements > 0) { + SortUtils.swap(array, index, index + amountSmallerElements); + } else { + index++; + } + } + + return array; + } + + private <T extends Comparable<T>> int getSmallerElementCount(final T[] array, final int index) { + int counter = 0; + for (int i = index + 1; i < array.length; i++) { + if (SortUtils.less(array[i], array[index])) { + counter++; + } + } + + return counter; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/TimSort.java b/src/main/java/com/thealgorithms/sorts/TimSort.java new file mode 100644 index 000000000000..2d5bca2ef6f3 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/TimSort.java @@ -0,0 +1,50 @@ +package com.thealgorithms.sorts; + +import static com.thealgorithms.sorts.SortUtils.less; + +/** + * This is simplified TimSort algorithm implementation. The original one is more complicated. + * <p> + * For more details @see <a href="https://en.wikipedia.org/wiki/Timsort">TimSort Algorithm</a> + */ +class TimSort implements SortAlgorithm { + private static final int SUB_ARRAY_SIZE = 32; + private Comparable[] aux; + + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + final int n = array.length; + + InsertionSort insertionSort = new InsertionSort(); + for (int i = 0; i < n; i += SUB_ARRAY_SIZE) { + insertionSort.sort(array, i, Math.min(i + SUB_ARRAY_SIZE, n)); + } + + aux = new Comparable[n]; + for (int sz = SUB_ARRAY_SIZE; sz < n; sz = sz + sz) { + for (int lo = 0; lo < n - sz; lo += sz + sz) { + merge(array, lo, lo + sz - 1, Math.min(lo + sz + sz - 1, n - 1)); + } + } + + return array; + } + + private <T extends Comparable<T>> void merge(T[] a, final int lo, final int mid, final int hi) { + int i = lo; + int j = mid + 1; + System.arraycopy(a, lo, aux, lo, hi + 1 - lo); + + for (int k = lo; k <= hi; k++) { + if (j > hi) { + a[k] = (T) aux[i++]; + } else if (i > mid) { + a[k] = (T) aux[j++]; + } else if (less(aux[j], aux[i])) { + a[k] = (T) aux[j++]; + } else { + a[k] = (T) aux[i++]; + } + } + } +} diff --git a/src/main/java/com/thealgorithms/sorts/TopologicalSort.java b/src/main/java/com/thealgorithms/sorts/TopologicalSort.java new file mode 100644 index 000000000000..e4ed240a9947 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/TopologicalSort.java @@ -0,0 +1,135 @@ +package com.thealgorithms.sorts; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; + +/** + * The Topological Sorting algorithm linearly orders a DAG or Directed Acyclic Graph into + * a linked list. A Directed Graph is proven to be acyclic when a DFS or Depth First Search is + * performed, yielding no back-edges. + * + * https://en.wikipedia.org/wiki/Topological_sorting + * + * @author Jonathan Taylor (https://github.com/Jtmonument) + * Based on Introduction to Algorithms 3rd Edition + */ +public final class TopologicalSort { + private TopologicalSort() { + } + + /* + * Enum to represent the colors for the depth first search + * */ + private enum Color { + WHITE, + GRAY, + BLACK, + } + + /* + * Class to represent vertices + * */ + private static class Vertex { + + /* + * Name of vertex + * */ + public final String label; + + /* + * Represents the category of visit in DFS + * */ + public Color color = Color.WHITE; + + /* + * The array of names of descendant vertices + * */ + public final ArrayList<String> next = new ArrayList<>(); + + Vertex(String label) { + this.label = label; + } + } + + /* + * Graph class uses the adjacency list representation + * */ + static class Graph { + + /* + * Adjacency list representation + * */ + private final HashMap<String, Vertex> adj = new LinkedHashMap<>(); + + /* + * Function to add an edge to the graph + * */ + public void addEdge(String label, String... next) { + adj.put(label, new Vertex(label)); + if (!next[0].isEmpty()) { + Collections.addAll(adj.get(label).next, next); + } + } + } + + /* + * Depth First Search + * + * DFS(G) + * for each vertex u ∈ G.V + * u.color = WHITE + * u.π = NIL + * time = 0 + * for each vertex u ∈ G.V + * if u.color == WHITE + * DFS-VISIT(G, u) + * + * Performed in Θ(V + E) time + * */ + public static LinkedList<String> sort(Graph graph) { + LinkedList<String> list = new LinkedList<>(); + graph.adj.forEach((name, vertex) -> { + if (vertex.color == Color.WHITE) { + list.addFirst(sort(graph, vertex, list)); + } + }); + return list; + } + + /* + * Depth First Search Visit + * + * DFS-Visit(G, u) + * time = time + 1 + * u.d = time + * u.color = GRAY + * for each v ∈ G.Adj[u] + * if v.color == WHITE + * v.π = u + * DFS-Visit(G, u) + * u.color = BLACK + * time = time + 1 + * u.f = time + * */ + private static String sort(Graph graph, Vertex u, LinkedList<String> list) { + u.color = Color.GRAY; + graph.adj.get(u.label).next.forEach(label -> { + if (graph.adj.get(label).color == Color.WHITE) { + list.addFirst(sort(graph, graph.adj.get(label), list)); + } else if (graph.adj.get(label).color == Color.GRAY) { + /* + * A back edge exists if an edge (u, v) connects a vertex u to its ancestor vertex v + * in a depth first tree. If v.d ≤ u.d < u.f ≤ v.f + * + * In many cases, we will not know u.f, but v.color denotes the type of edge + * */ + throw new RuntimeException("This graph contains a cycle. No linear ordering is possible. Back edge: " + u.label + " -> " + label); + } + }); + u.color = Color.BLACK; + return u.label; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/TreeSort.java b/src/main/java/com/thealgorithms/sorts/TreeSort.java new file mode 100644 index 000000000000..6f4e5509489d --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/TreeSort.java @@ -0,0 +1,118 @@ +package com.thealgorithms.sorts; + +import static com.thealgorithms.sorts.SortUtils.print; + +import com.thealgorithms.datastructures.trees.BSTRecursiveGeneric; +import java.util.List; + +/** + * <h1> Implementation of the Tree Sort algorithm</h1> + * + * <p> + * Tree Sort: A sorting algorithm which constructs a Binary Search Tree using + * the unsorted data and then outputs the data by inorder traversal of the tree. + * + * Reference: https://en.wikipedia.org/wiki/Tree_sort + * </p> + * + * @author Madhur Panwar (https://github.com/mdrpanwar) + */ +public class TreeSort implements SortAlgorithm { + + @Override + public <T extends Comparable<T>> T[] sort(T[] unsortedArray) { + return doTreeSortArray(unsortedArray); + } + + @Override + public <T extends Comparable<T>> List<T> sort(List<T> unsortedList) { + return doTreeSortList(unsortedList); + } + + private <T extends Comparable<T>> T[] doTreeSortArray(T[] unsortedArray) { + // create a generic BST tree + BSTRecursiveGeneric<T> tree = new BSTRecursiveGeneric<T>(); + + // add all elements to the tree + for (T element : unsortedArray) { + tree.add(element); + } + + // get the sorted list by inorder traversal of the tree + List<T> sortedList = tree.inorderSort(); + + // add the elements back to the initial array + int i = 0; + for (T element : sortedList) { + unsortedArray[i++] = element; + } + + // return the array + return unsortedArray; + } + + private <T extends Comparable<T>> List<T> doTreeSortList(Iterable<T> unsortedList) { + // create a generic BST tree + BSTRecursiveGeneric<T> tree = new BSTRecursiveGeneric<T>(); + + // add all elements to the tree + for (T element : unsortedList) { + tree.add(element); + } + + // get the sorted list by inorder traversal of the tree and return it + return tree.inorderSort(); + } + + public static void main(String[] args) { + TreeSort treeSort = new TreeSort(); + + // ==== Integer Array ======= + System.out.println("Testing for Integer Array...."); + Integer[] a = {3, -7, 45, 1, 343, -5, 2, 9}; + System.out.printf("%-10s", "unsorted: "); + print(a); + a = treeSort.sort(a); + System.out.printf("%-10s", "sorted: "); + print(a); + System.out.println(); + + // ==== Integer List ======= + System.out.println("Testing for Integer List...."); + List<Integer> intList = List.of(3, -7, 45, 1, 343, -5, 2, 9); + System.out.printf("%-10s", "unsorted: "); + print(intList); + intList = treeSort.sort(intList); + System.out.printf("%-10s", "sorted: "); + print(intList); + System.out.println(); + + // ==== String Array ======= + System.out.println("Testing for String Array...."); + String[] b = { + "banana", + "berry", + "orange", + "grape", + "peach", + "cherry", + "apple", + "pineapple", + }; + System.out.printf("%-10s", "unsorted: "); + print(b); + b = treeSort.sort(b); + System.out.printf("%-10s", "sorted: "); + print(b); + System.out.println(); + + // ==== String List ======= + System.out.println("Testing for String List...."); + List<String> stringList = List.of("banana", "berry", "orange", "grape", "peach", "cherry", "apple", "pineapple"); + System.out.printf("%-10s", "unsorted: "); + print(stringList); + stringList = treeSort.sort(stringList); + System.out.printf("%-10s", "sorted: "); + print(stringList); + } +} diff --git a/src/main/java/com/thealgorithms/sorts/WaveSort.java b/src/main/java/com/thealgorithms/sorts/WaveSort.java new file mode 100644 index 000000000000..31aad52c6b4a --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/WaveSort.java @@ -0,0 +1,46 @@ +package com.thealgorithms.sorts; + +/** + * The WaveSort algorithm sorts an array so that every alternate element is greater than its adjacent elements. + * This implementation also provides a method to check if an array is wave sorted. + */ +public class WaveSort implements SortAlgorithm { + /** + * Sorts the given array such that every alternate element is greater than its adjacent elements. + * + * @param array The array to be sorted. + * @param <T> The type of elements in the array, which must be Comparable. + * @return The sorted array. + */ + @Override + public <T extends Comparable<T>> T[] sort(T[] array) { + for (int i = 0; i < array.length; i += 2) { + if (i > 0 && SortUtils.less(array[i], array[i - 1])) { + SortUtils.swap(array, i, i - 1); + } + if (i < array.length - 1 && SortUtils.less(array[i], array[i + 1])) { + SortUtils.swap(array, i, i + 1); + } + } + return array; + } + + /** + * Checks if the given array is wave sorted. An array is wave sorted if every alternate element is greater than its adjacent elements. + * + * @param array The array to check. + * @param <T> The type of elements in the array, which must be Comparable. + * @return true if the array is wave sorted, false otherwise. + */ + public <T extends Comparable<T>> boolean isWaveSorted(T[] array) { + for (int i = 0; i < array.length; i += 2) { + if (i > 0 && SortUtils.less(array[i], array[i - 1])) { + return false; + } + if (i < array.length - 1 && SortUtils.less(array[i], array[i + 1])) { + return false; + } + } + return true; + } +} diff --git a/src/main/java/com/thealgorithms/sorts/WiggleSort.java b/src/main/java/com/thealgorithms/sorts/WiggleSort.java new file mode 100644 index 000000000000..c272b820d07a --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/WiggleSort.java @@ -0,0 +1,85 @@ +package com.thealgorithms.sorts; + +import static com.thealgorithms.maths.Ceil.ceil; +import static com.thealgorithms.maths.Floor.floor; +import static com.thealgorithms.searches.QuickSelect.select; + +import java.util.Arrays; + +/** + * A wiggle sort implementation based on John L.s' answer in + * https://cs.stackexchange.com/questions/125372/how-to-wiggle-sort-an-array-in-linear-time-complexity + * Also have a look at: + * https://cs.stackexchange.com/questions/125372/how-to-wiggle-sort-an-array-in-linear-time-complexity?noredirect=1&lq=1 + * Not all arrays are wiggle-sortable. This algorithm will find some obviously not wiggle-sortable + * arrays and throw an error, but there are some exceptions that won't be caught, for example [1, 2, + * 2]. + */ +public class WiggleSort implements SortAlgorithm { + + @Override + public <T extends Comparable<T>> T[] sort(T[] unsorted) { + return wiggleSort(unsorted); + } + + private int mapIndex(int index, int n) { + return ((2 * index + 1) % (n | 1)); + } + + /** + * Modified Dutch National Flag Sort. See also: sorts/DutchNationalFlagSort + * + * @param sortThis array to sort into group "greater", "equal" and "smaller" than median + * @param median defines the groups + * @param <T> extends interface Comparable + */ + private <T extends Comparable<T>> void triColorSort(T[] sortThis, T median) { + int n = sortThis.length; + int i = 0; + int j = 0; + int k = n - 1; + while (j <= k) { + if (0 < sortThis[mapIndex(j, n)].compareTo(median)) { + SortUtils.swap(sortThis, mapIndex(j, n), mapIndex(i, n)); + i++; + j++; + } else if (0 > sortThis[mapIndex(j, n)].compareTo(median)) { + SortUtils.swap(sortThis, mapIndex(j, n), mapIndex(k, n)); + k--; + } else { + j++; + } + } + } + + private <T extends Comparable<T>> T[] wiggleSort(T[] sortThis) { + // find the median using quickSelect (if the result isn't in the array, use the next greater + // value) + T median; + + median = select(Arrays.asList(sortThis), (int) floor(sortThis.length / 2.0)); + + int numMedians = 0; + + for (T sortThi : sortThis) { + if (0 == sortThi.compareTo(median)) { + numMedians++; + } + } + // added condition preventing off-by-one errors for odd arrays. + // https://cs.stackexchange.com/questions/150886/how-to-find-wiggle-sortable-arrays-did-i-misunderstand-john-l-s-answer?noredirect=1&lq=1 + if (sortThis.length % 2 == 1 && numMedians == ceil(sortThis.length / 2.0)) { + T smallestValue = select(Arrays.asList(sortThis), 0); + if (!(0 == smallestValue.compareTo(median))) { + throw new IllegalArgumentException("For odd Arrays if the median appears ceil(n/2) times, " + + "the median has to be the smallest values in the array."); + } + } + if (numMedians > ceil(sortThis.length / 2.0)) { + throw new IllegalArgumentException("No more than half the number of values may be the same."); + } + + triColorSort(sortThis, median); + return sortThis; + } +} diff --git a/src/main/java/com/thealgorithms/stacks/BalancedBrackets.java b/src/main/java/com/thealgorithms/stacks/BalancedBrackets.java new file mode 100644 index 000000000000..d3077ff54135 --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/BalancedBrackets.java @@ -0,0 +1,80 @@ +package com.thealgorithms.stacks; + +import java.util.Stack; + +/** + * The nested brackets problem is a problem that determines if a sequence of + * brackets are properly nested. A sequence of brackets s is considered properly + * nested if any of the following conditions are true: - s is empty - s has the + * form (U) or [U] or {U} where U is a properly nested string - s has the form + * VW where V and W are properly nested strings For example, the string + * "()()[()]" is properly nested but "[(()]" is not. The function called + * is_balanced takes as input a string S which is a sequence of brackets and + * returns true if S is nested and false otherwise. + * + * @author akshay sharma + * @author <a href="https://github.com/khalil2535">khalil2535<a> + * @author shellhub + */ +final class BalancedBrackets { + private BalancedBrackets() { + } + + /** + * Check if {@code leftBracket} and {@code rightBracket} is paired or not + * + * @param leftBracket left bracket + * @param rightBracket right bracket + * @return {@code true} if {@code leftBracket} and {@code rightBracket} is + * paired, otherwise {@code false} + */ + public static boolean isPaired(char leftBracket, char rightBracket) { + char[][] pairedBrackets = { + {'(', ')'}, + {'[', ']'}, + {'{', '}'}, + {'<', '>'}, + }; + for (char[] pairedBracket : pairedBrackets) { + if (pairedBracket[0] == leftBracket && pairedBracket[1] == rightBracket) { + return true; + } + } + return false; + } + + /** + * Check if {@code brackets} is balanced + * + * @param brackets the brackets + * @return {@code true} if {@code brackets} is balanced, otherwise + * {@code false} + */ + public static boolean isBalanced(String brackets) { + if (brackets == null) { + throw new IllegalArgumentException("brackets is null"); + } + Stack<Character> bracketsStack = new Stack<>(); + for (char bracket : brackets.toCharArray()) { + switch (bracket) { + case '(': + case '[': + case '<': + case '{': + bracketsStack.push(bracket); + break; + case ')': + case ']': + case '>': + case '}': + if (bracketsStack.isEmpty() || !isPaired(bracketsStack.pop(), bracket)) { + return false; + } + break; + default: + return false; + } + } + return bracketsStack.isEmpty(); + } +} diff --git a/src/main/java/com/thealgorithms/stacks/CelebrityFinder.java b/src/main/java/com/thealgorithms/stacks/CelebrityFinder.java new file mode 100644 index 000000000000..67ac861ef82b --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/CelebrityFinder.java @@ -0,0 +1,52 @@ +package com.thealgorithms.stacks; + +import java.util.Stack; + +/** + * Solves the celebrity problem using a stack-based algorithm. + * + * <p>Celebrity is someone known by everyone but doesn't know anyone else. + * <p>Applications: Graph theory and social network analysis. + * + * @author Hardvan + */ +public final class CelebrityFinder { + private CelebrityFinder() { + } + + /** + * Finds the celebrity in the given party matrix using a stack-based algorithm. + * + * @param party A 2D matrix where party[i][j] is 1 if i knows j, otherwise 0. + * @return The index of the celebrity, or -1 if there is no celebrity. + */ + public static int findCelebrity(int[][] party) { + + // Push all people onto the stack + Stack<Integer> stack = new Stack<>(); + for (int i = 0; i < party.length; i++) { + stack.push(i); + } + + // Find the potential celebrity by comparing pairs + while (stack.size() > 1) { + int person1 = stack.pop(); + int person2 = stack.pop(); + + if (party[person1][person2] == 1) { + stack.push(person2); // person1 knows person2, so person2 might be the celebrity + } else { + stack.push(person1); // person1 doesn't know person2, so person1 might be the celebrity + } + } + + // Verify the candidate + int candidate = stack.pop(); + for (int i = 0; i < party.length; i++) { + if (i != candidate && (party[candidate][i] == 1 || party[i][candidate] == 0)) { + return -1; + } + } + return candidate; + } +} diff --git a/src/main/java/com/thealgorithms/stacks/DecimalToAnyUsingStack.java b/src/main/java/com/thealgorithms/stacks/DecimalToAnyUsingStack.java new file mode 100644 index 000000000000..ff6402c92695 --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/DecimalToAnyUsingStack.java @@ -0,0 +1,43 @@ +package com.thealgorithms.stacks; + +import java.util.Stack; + +public final class DecimalToAnyUsingStack { + private DecimalToAnyUsingStack() { + } + + /** + * Convert a decimal number to another radix. + * + * @param number the number to be converted + * @param radix the radix + * @return the number represented in the new radix as a String + * @throws IllegalArgumentException if <tt>number</tt> is negative or <tt>radix</tt> is not between 2 and 16 inclusive + */ + public static String convert(int number, int radix) { + if (number < 0) { + throw new IllegalArgumentException("Number must be non-negative."); + } + if (radix < 2 || radix > 16) { + throw new IllegalArgumentException(String.format("Invalid radix: %d. Radix must be between 2 and 16.", radix)); + } + + if (number == 0) { + return "0"; + } + + char[] tables = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + Stack<Character> bits = new Stack<>(); + while (number > 0) { + bits.push(tables[number % radix]); + number = number / radix; + } + + StringBuilder result = new StringBuilder(); + while (!bits.isEmpty()) { + result.append(bits.pop()); + } + return result.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/stacks/DuplicateBrackets.java b/src/main/java/com/thealgorithms/stacks/DuplicateBrackets.java new file mode 100644 index 000000000000..25d265232151 --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/DuplicateBrackets.java @@ -0,0 +1,44 @@ +package com.thealgorithms.stacks; + +import java.util.Stack; + +/** + * Class for detecting unnecessary or redundant brackets in a mathematical expression. + * Assumes the expression is balanced (i.e., all opening brackets have matching closing brackets). + */ +public final class DuplicateBrackets { + private DuplicateBrackets() { + } + + /** + * Checks for extra or redundant brackets in a given expression. + * + * @param expression the string representing the expression to be checked + * @return true if there are extra or redundant brackets, false otherwise + * @throws IllegalArgumentException if the input string is null + */ + public static boolean check(String expression) { + if (expression == null) { + throw new IllegalArgumentException("Input expression cannot be null."); + } + + Stack<Character> stack = new Stack<>(); + for (int i = 0; i < expression.length(); i++) { + char ch = expression.charAt(i); + if (ch == ')') { + if (stack.isEmpty() || stack.peek() == '(') { + return true; + } + while (!stack.isEmpty() && stack.peek() != '(') { + stack.pop(); + } + if (!stack.isEmpty()) { + stack.pop(); + } + } else { + stack.push(ch); + } + } + return false; + } +} diff --git a/src/main/java/com/thealgorithms/stacks/GreatestElementConstantTime.java b/src/main/java/com/thealgorithms/stacks/GreatestElementConstantTime.java new file mode 100644 index 000000000000..be9d0099d38b --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/GreatestElementConstantTime.java @@ -0,0 +1,74 @@ +package com.thealgorithms.stacks; + +import java.util.NoSuchElementException; +import java.util.Stack; + +/** + * A class that implements a stack that gives the maximum element in O(1) time. + * The mainStack is used to store the all the elements of the stack + * While the maxStack stores the maximum elements + * When we want to get a maximum element, we call the top of the maximum stack + * + * Problem: https://www.baeldung.com/cs/stack-constant-time + */ +public class GreatestElementConstantTime { + private Stack<Integer> mainStack; // initialize a mainStack + private Stack<Integer> maxStack; // initialize a maxStack + + /** + * Constructs two empty stacks + */ + public GreatestElementConstantTime() { + mainStack = new Stack<>(); + maxStack = new Stack<>(); + } + + /** + * Pushes an element onto the top of the stack. + * Checks if the element is the maximum or not + * If so, then pushes to the maximum stack + * @param data The element to be pushed onto the stack. + */ + public void push(int data) { + if (mainStack.isEmpty()) { + mainStack.push(data); + maxStack.push(data); + return; + } + + mainStack.push(data); + if (data > maxStack.peek()) { + maxStack.push(data); + } + } + + /** + * Pops an element from the stack. + * Checks if the element to be popped is the maximum or not + * If so, then pop from the minStack + * + * @throws NoSuchElementException if the stack is empty. + */ + public void pop() { + if (mainStack.isEmpty()) { + throw new NoSuchElementException("Stack is empty"); + } + + int ele = mainStack.pop(); + if (ele == maxStack.peek()) { + maxStack.pop(); + } + } + + /** + * Returns the maximum element present in the stack + * + * @return The element at the top of the maxStack, or null if the stack is empty. + */ + public Integer getMaximumElement() { + if (maxStack.isEmpty()) { + return null; + } + return maxStack.peek(); + } +} diff --git a/src/main/java/com/thealgorithms/stacks/InfixToPostfix.java b/src/main/java/com/thealgorithms/stacks/InfixToPostfix.java new file mode 100644 index 000000000000..33611bd73dba --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/InfixToPostfix.java @@ -0,0 +1,60 @@ +package com.thealgorithms.stacks; + +import java.util.Stack; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class InfixToPostfix { + private InfixToPostfix() { + } + + public static String infix2PostFix(String infixExpression) throws Exception { + if (!BalancedBrackets.isBalanced(filterBrackets(infixExpression))) { + throw new Exception("invalid expression"); + } + StringBuilder output = new StringBuilder(); + Stack<Character> stack = new Stack<>(); + for (char element : infixExpression.toCharArray()) { + if (Character.isLetterOrDigit(element)) { + output.append(element); + } else if (element == '(') { + stack.push(element); + } else if (element == ')') { + while (!stack.isEmpty() && stack.peek() != '(') { + output.append(stack.pop()); + } + stack.pop(); + } else { + while (!stack.isEmpty() && precedence(element) <= precedence(stack.peek())) { + output.append(stack.pop()); + } + stack.push(element); + } + } + while (!stack.isEmpty()) { + output.append(stack.pop()); + } + return output.toString(); + } + + private static int precedence(char operator) { + switch (operator) { + case '+': + case '-': + return 0; + case '*': + case '/': + return 1; + case '^': + return 2; + default: + return -1; + } + } + + private static String filterBrackets(String input) { + Pattern pattern = Pattern.compile("[^(){}\\[\\]<>]"); + Matcher matcher = pattern.matcher(input); + return matcher.replaceAll(""); + } +} diff --git a/src/main/java/com/thealgorithms/stacks/InfixToPrefix.java b/src/main/java/com/thealgorithms/stacks/InfixToPrefix.java new file mode 100644 index 000000000000..3d90d14e0d1e --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/InfixToPrefix.java @@ -0,0 +1,92 @@ +package com.thealgorithms.stacks; + +import java.util.Stack; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class InfixToPrefix { + private InfixToPrefix() { + } + + /** + * Convert an infix expression to a prefix expression using stack. + * + * @param infixExpression the infix expression to convert + * @return the prefix expression + * @throws IllegalArgumentException if the infix expression has unbalanced brackets + * @throws NullPointerException if the infix expression is null + */ + public static String infix2Prefix(String infixExpression) throws IllegalArgumentException { + if (infixExpression == null) { + throw new NullPointerException("Input expression cannot be null."); + } + infixExpression = infixExpression.trim(); + if (infixExpression.isEmpty()) { + return ""; + } + if (!BalancedBrackets.isBalanced(filterBrackets(infixExpression))) { + throw new IllegalArgumentException("Invalid expression: unbalanced brackets."); + } + + StringBuilder output = new StringBuilder(); + Stack<Character> stack = new Stack<>(); + // Reverse the infix expression for prefix conversion + String reversedInfix = new StringBuilder(infixExpression).reverse().toString(); + for (char element : reversedInfix.toCharArray()) { + if (Character.isLetterOrDigit(element)) { + output.append(element); + } else if (element == ')') { + stack.push(element); + } else if (element == '(') { + while (!stack.isEmpty() && stack.peek() != ')') { + output.append(stack.pop()); + } + stack.pop(); + } else { + while (!stack.isEmpty() && precedence(element) < precedence(stack.peek())) { + output.append(stack.pop()); + } + stack.push(element); + } + } + while (!stack.isEmpty()) { + output.append(stack.pop()); + } + + // Reverse the result to get the prefix expression + return output.reverse().toString(); + } + + /** + * Determines the precedence of an operator. + * + * @param operator the operator whose precedence is to be determined + * @return the precedence of the operator + */ + private static int precedence(char operator) { + switch (operator) { + case '+': + case '-': + return 0; + case '*': + case '/': + return 1; + case '^': + return 2; + default: + return -1; + } + } + + /** + * Filters out all characters from the input string except brackets. + * + * @param input the input string to filter + * @return a string containing only brackets from the input string + */ + private static String filterBrackets(String input) { + Pattern pattern = Pattern.compile("[^(){}\\[\\]<>]"); + Matcher matcher = pattern.matcher(input); + return matcher.replaceAll(""); + } +} diff --git a/src/main/java/com/thealgorithms/stacks/LargestRectangle.java b/src/main/java/com/thealgorithms/stacks/LargestRectangle.java new file mode 100644 index 000000000000..006e03632e63 --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/LargestRectangle.java @@ -0,0 +1,38 @@ +package com.thealgorithms.stacks; + +import java.util.Stack; + +/** + * + * @author mohd rameez github.com/rameez471 + */ + +public final class LargestRectangle { + private LargestRectangle() { + } + + public static String largestRectangleHistogram(int[] heights) { + int n = heights.length; + int maxArea = 0; + Stack<int[]> st = new Stack<>(); + for (int i = 0; i < n; i++) { + int start = i; + while (!st.isEmpty() && st.peek()[1] > heights[i]) { + int[] tmp = st.pop(); + maxArea = Math.max(maxArea, tmp[1] * (i - tmp[0])); + start = tmp[0]; + } + st.push(new int[] {start, heights[i]}); + } + while (!st.isEmpty()) { + int[] tmp = st.pop(); + maxArea = Math.max(maxArea, tmp[1] * (n - tmp[0])); + } + return Integer.toString(maxArea); + } + + public static void main(String[] args) { + assert largestRectangleHistogram(new int[] {2, 1, 5, 6, 2, 3}).equals("10"); + assert largestRectangleHistogram(new int[] {2, 4}).equals("4"); + } +} diff --git a/src/main/java/com/thealgorithms/stacks/MaximumMinimumWindow.java b/src/main/java/com/thealgorithms/stacks/MaximumMinimumWindow.java new file mode 100644 index 000000000000..d76122b16632 --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/MaximumMinimumWindow.java @@ -0,0 +1,107 @@ +package com.thealgorithms.stacks; + +import java.util.Arrays; +import java.util.Stack; + +/** + * Given an integer array. The task is to find the maximum of the minimum of + * every window size in the array. Note: Window size varies from 1 to the size + * of the Array. + * <p> + * For example, + * <p> + * N = 7 + * arr[] = {10,20,30,50,10,70,30} + * <p> + * So the answer for the above would be : 70 30 20 10 10 10 10 + * <p> + * We need to consider window sizes from 1 to length of array in each iteration. + * So in the iteration 1 the windows would be [10], [20], [30], [50], [10], + * [70], [30]. Now we need to check the minimum value in each window. Since the + * window size is 1 here the minimum element would be the number itself. Now the + * maximum out of these is the result in iteration 1. In the second iteration we + * need to consider window size 2, so there would be [10,20], [20,30], [30,50], + * [50,10], [10,70], [70,30]. Now the minimum of each window size would be + * [10,20,30,10,10] and the maximum out of these is 30. Similarly we solve for + * other window sizes. + * + * @author sahil + */ +public final class MaximumMinimumWindow { + private MaximumMinimumWindow() { + } + + /** + * This function contains the logic of finding maximum of minimum for every + * window size using Stack Data Structure. + * + * @param arr Array containing the numbers + * @param n Length of the array + * @return result array + */ + public static int[] calculateMaxOfMin(int[] arr, int n) { + Stack<Integer> s = new Stack<>(); + int[] left = new int[n + 1]; + int[] right = new int[n + 1]; + for (int i = 0; i < n; i++) { + left[i] = -1; + right[i] = n; + } + + for (int i = 0; i < n; i++) { + while (!s.empty() && arr[s.peek()] >= arr[i]) { + s.pop(); + } + + if (!s.empty()) { + left[i] = s.peek(); + } + + s.push(i); + } + + while (!s.empty()) { + s.pop(); + } + + for (int i = n - 1; i >= 0; i--) { + while (!s.empty() && arr[s.peek()] >= arr[i]) { + s.pop(); + } + + if (!s.empty()) { + right[i] = s.peek(); + } + + s.push(i); + } + + int[] ans = new int[n + 1]; + for (int i = 0; i <= n; i++) { + ans[i] = 0; + } + + for (int i = 0; i < n; i++) { + int len = right[i] - left[i] - 1; + + ans[len] = Math.max(ans[len], arr[i]); + } + + for (int i = n - 1; i >= 1; i--) { + ans[i] = Math.max(ans[i], ans[i + 1]); + } + + // Print the result + for (int i = 1; i <= n; i++) { + System.out.print(ans[i] + " "); + } + return ans; + } + + public static void main(String[] args) { + int[] arr = new int[] {10, 20, 30, 50, 10, 70, 30}; + int[] target = new int[] {70, 30, 20, 10, 10, 10, 10}; + int[] res = calculateMaxOfMin(arr, arr.length); + assert Arrays.equals(target, res); + } +} diff --git a/src/main/java/com/thealgorithms/stacks/MinStackUsingSingleStack.java b/src/main/java/com/thealgorithms/stacks/MinStackUsingSingleStack.java new file mode 100644 index 000000000000..f5e526b102cf --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/MinStackUsingSingleStack.java @@ -0,0 +1,65 @@ +package com.thealgorithms.stacks; + +import java.util.EmptyStackException; +import java.util.Stack; + +/** + * Min-Stack implementation using a single stack. + * + * This stack supports push, pop, and retrieving the minimum element + * in constant time (O(1)) using a modified approach where the stack + * stores both the element and the minimum value so far. + * + * @author Hardvan + */ +public class MinStackUsingSingleStack { + private final Stack<long[]> stack = new Stack<>(); + + /** + * Pushes a new value onto the stack. + * Each entry stores both the value and the minimum value so far. + * + * @param value The value to be pushed onto the stack. + */ + public void push(int value) { + if (stack.isEmpty()) { + stack.push(new long[] {value, value}); + } else { + long minSoFar = Math.min(value, stack.peek()[1]); + stack.push(new long[] {value, minSoFar}); + } + } + + /** + * Removes the top element from the stack. + */ + public void pop() { + if (!stack.isEmpty()) { + stack.pop(); + } + } + + /** + * Retrieves the top element from the stack. + * + * @return The top element of the stack. + */ + public int top() { + if (!stack.isEmpty()) { + return (int) stack.peek()[0]; + } + throw new EmptyStackException(); + } + + /** + * Retrieves the minimum element in the stack. + * + * @return The minimum element so far. + */ + public int getMin() { + if (!stack.isEmpty()) { + return (int) stack.peek()[1]; + } + throw new EmptyStackException(); + } +} diff --git a/src/main/java/com/thealgorithms/stacks/MinStackUsingTwoStacks.java b/src/main/java/com/thealgorithms/stacks/MinStackUsingTwoStacks.java new file mode 100644 index 000000000000..47e337c80b59 --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/MinStackUsingTwoStacks.java @@ -0,0 +1,57 @@ +package com.thealgorithms.stacks; + +import java.util.Stack; + +/** + * Min-Stack implementation that supports push, pop, and retrieving the minimum element in constant time. + * + * @author Hardvan + */ +public final class MinStackUsingTwoStacks { + MinStackUsingTwoStacks() { + } + + private final Stack<Integer> stack = new Stack<>(); + private final Stack<Integer> minStack = new Stack<>(); + + /** + * Pushes a new element onto the {@code stack}. + * If the value is less than or equal to the current minimum, it is also pushed onto the {@code minStack}. + * + * @param value The value to be pushed. + */ + public void push(int value) { + stack.push(value); + if (minStack.isEmpty() || value <= minStack.peek()) { + minStack.push(value); + } + } + + /** + * Removes the top element from the stack. + * If the element is the minimum element, it is also removed from the {@code minStack}. + */ + public void pop() { + if (stack.pop().equals(minStack.peek())) { + minStack.pop(); + } + } + + /** + * Retrieves the top element of the stack. + * + * @return The top element. + */ + public int top() { + return stack.peek(); + } + + /** + * Retrieves the minimum element in the stack. + * + * @return The minimum element. + */ + public int getMin() { + return minStack.peek(); + } +} diff --git a/src/main/java/com/thealgorithms/stacks/NextGreaterElement.java b/src/main/java/com/thealgorithms/stacks/NextGreaterElement.java new file mode 100644 index 000000000000..8b7c9c3ef9cf --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/NextGreaterElement.java @@ -0,0 +1,46 @@ +package com.thealgorithms.stacks; + +import java.util.Stack; + +/** + * Utility class to find the next greater element for each element in a given integer array. + * + * <p>The next greater element for an element x is the first greater element on the right side of x in the array. + * If no such element exists, the result will contain 0 for that position.</p> + * + * <p>Example:</p> + * <pre> + * Input: {2, 7, 3, 5, 4, 6, 8} + * Output: {7, 0, 5, 6, 6, 8, 0} + * </pre> + */ +public final class NextGreaterElement { + private NextGreaterElement() { + } + + /** + * Finds the next greater element for each element in the given array. + * + * @param array the input array of integers + * @return an array where each element is replaced by the next greater element on the right side in the input array, + * or 0 if there is no greater element. + * @throws IllegalArgumentException if the input array is null + */ + public static int[] findNextGreaterElements(int[] array) { + if (array == null) { + throw new IllegalArgumentException("Input array cannot be null"); + } + + int[] result = new int[array.length]; + Stack<Integer> stack = new Stack<>(); + + for (int i = 0; i < array.length; i++) { + while (!stack.isEmpty() && array[stack.peek()] < array[i]) { + result[stack.pop()] = array[i]; + } + stack.push(i); + } + + return result; + } +} diff --git a/src/main/java/com/thealgorithms/stacks/NextSmallerElement.java b/src/main/java/com/thealgorithms/stacks/NextSmallerElement.java new file mode 100644 index 000000000000..254bb3b4b2d7 --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/NextSmallerElement.java @@ -0,0 +1,59 @@ +package com.thealgorithms.stacks; + +import java.util.Arrays; +import java.util.Stack; + +/** + * Utility class to find the next smaller element for each element in a given integer array. + * + * <p>The next smaller element for an element x is the first smaller element on the left side of x in the array. + * If no such element exists, the result will contain -1 for that position.</p> + * + * <p>Example:</p> + * <pre> + * Input: {2, 7, 3, 5, 4, 6, 8} + * Output: [-1, 2, 2, 3, 3, 4, 6] + * </pre> + */ +public final class NextSmallerElement { + private NextSmallerElement() { + } + + /** + * Finds the next smaller element for each element in the given array. + * + * @param array the input array of integers + * @return an array where each element is replaced by the next smaller element on the left side in the input array, + * or -1 if there is no smaller element. + * @throws IllegalArgumentException if the input array is null + */ + public static int[] findNextSmallerElements(int[] array) { + if (array == null) { + throw new IllegalArgumentException("Input array cannot be null"); + } + + int[] result = new int[array.length]; + Stack<Integer> stack = new Stack<>(); + + // Initialize all elements to -1 (in case there is no smaller element) + Arrays.fill(result, -1); + + // Traverse the array from left to right + for (int i = 0; i < array.length; i++) { + // Maintain the stack such that the top of the stack is the next smaller element + while (!stack.isEmpty() && stack.peek() >= array[i]) { + stack.pop(); + } + + // If stack is not empty, then the top is the next smaller element + if (!stack.isEmpty()) { + result[i] = stack.peek(); + } + + // Push the current element onto the stack + stack.push(array[i]); + } + + return result; + } +} diff --git a/src/main/java/com/thealgorithms/stacks/PalindromeWithStack.java b/src/main/java/com/thealgorithms/stacks/PalindromeWithStack.java new file mode 100644 index 000000000000..98c439341a21 --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/PalindromeWithStack.java @@ -0,0 +1,57 @@ +package com.thealgorithms.stacks; + +import java.util.LinkedList; + +/** + * A class that implements a palindrome checker using a stack. + * The stack is used to store the characters of the string, + * which we will pop one-by-one to create the string in reverse. + * + * Reference: https://www.geeksforgeeks.org/check-whether-the-given-string-is-palindrome-using-stack/ + */ +public class PalindromeWithStack { + private LinkedList<Character> stack; + + /** + * Constructs an empty stack that stores characters. + */ + public PalindromeWithStack() { + stack = new LinkedList<Character>(); + } + + /** + * Check if the string is a palindrome or not. + * Convert all characters to lowercase and push them into a stack. + * At the same time, build a string + * Next, pop from the stack and build the reverse string + * Finally, compare these two strings + * + * @param string The string to check if it is palindrome or not. + */ + public boolean checkPalindrome(String string) { + // Create a StringBuilder to build the string from left to right + StringBuilder stringBuilder = new StringBuilder(string.length()); + // Convert all characters to lowercase + String lowercase = string.toLowerCase(); + + // Iterate through the string + for (int i = 0; i < lowercase.length(); ++i) { + char c = lowercase.charAt(i); + // Build the string from L->R + stringBuilder.append(c); + // Push to the stack + stack.push(c); + } + + // The stack contains the reverse order of the string + StringBuilder reverseString = new StringBuilder(stack.size()); + // Until the stack is not empty + while (!stack.isEmpty()) { + // Build the string from R->L + reverseString.append(stack.pop()); + } + + // Finally, compare the L->R string with the R->L string + return reverseString.toString().equals(stringBuilder.toString()); + } +} diff --git a/src/main/java/com/thealgorithms/stacks/PostfixEvaluator.java b/src/main/java/com/thealgorithms/stacks/PostfixEvaluator.java new file mode 100644 index 000000000000..02ef5af8da16 --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/PostfixEvaluator.java @@ -0,0 +1,74 @@ +package com.thealgorithms.stacks; + +import java.util.Set; +import java.util.Stack; + +/** + * Evaluate a postfix (Reverse Polish) expression using a stack. + * + * <p>Example: Expression "5 6 + 2 *" results in 22. + * <p>Applications: Used in calculators and expression evaluation in compilers. + * + * @author Hardvan + */ +public final class PostfixEvaluator { + private PostfixEvaluator() { + } + + private static final Set<String> OPERATORS = Set.of("+", "-", "*", "/"); + + /** + * Evaluates the given postfix expression and returns the result. + * + * @param expression The postfix expression as a string with operands and operators separated by spaces. + * @return The result of evaluating the postfix expression. + * @throws IllegalArgumentException if the expression is invalid. + */ + public static int evaluatePostfix(String expression) { + Stack<Integer> stack = new Stack<>(); + + for (String token : expression.split("\\s+")) { + if (isOperator(token)) { + int operand2 = stack.pop(); + int operand1 = stack.pop(); + stack.push(applyOperator(token, operand1, operand2)); + } else { + stack.push(Integer.valueOf(token)); + } + } + + if (stack.size() != 1) { + throw new IllegalArgumentException("Invalid expression"); + } + + return stack.pop(); + } + + /** + * Checks if the given token is an operator. + * + * @param token The token to check. + * @return true if the token is an operator, false otherwise. + */ + private static boolean isOperator(String token) { + return OPERATORS.contains(token); + } + + /** + * Applies the given operator to the two operands. + * + * @param operator The operator to apply. + * @param a The first operand. + * @param b The second operand. + * @return The result of applying the operator to the operands. + */ + private static int applyOperator(String operator, int a, int b) { + return switch (operator) { + case "+" -> a + b; + case "-" -> a - b; + case "*" -> a * b; + case "/" -> a / b; + default -> throw new IllegalArgumentException("Invalid operator"); + }; + } +} diff --git a/src/main/java/com/thealgorithms/stacks/PostfixToInfix.java b/src/main/java/com/thealgorithms/stacks/PostfixToInfix.java new file mode 100644 index 000000000000..a8f98ac53e5d --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/PostfixToInfix.java @@ -0,0 +1,102 @@ +package com.thealgorithms.stacks; + +import java.util.Stack; + +/** + * Postfix to Infix implementation via Stack + * + * Function: String getPostfixToInfix(String postfix) + * Returns the Infix Expression for the given postfix parameter. + * + * Avoid using parentheses/brackets/braces for the postfix string. + * Postfix Expressions don't require these. + * + * + * @author nikslyon19 (Nikhil Bisht) + * + */ + +public final class PostfixToInfix { + private PostfixToInfix() { + } + + /** + * Determines if a given character is a valid arithmetic operator. + * + * @param token the character to check + * @return true if the character is an operator, false otherwise + */ + public static boolean isOperator(char token) { + return token == '+' || token == '-' || token == '/' || token == '*' || token == '^'; + } + + /** + * Validates whether a given string is a valid postfix expression. + * + * A valid postfix expression must meet these criteria: + * 1. It should have at least one operator and two operands. + * 2. The number of operands should always be greater than the number of operators at any point in the traversal. + * + * @param postfix the postfix expression string to validate + * @return true if the expression is valid, false otherwise + */ + public static boolean isValidPostfixExpression(String postfix) { + if (postfix.length() == 1 && (Character.isAlphabetic(postfix.charAt(0)))) { + return true; + } + + if (postfix.length() < 3) { + return false; // Postfix expression should have at least one operator and two operands + } + + int operandCount = 0; + int operatorCount = 0; + + for (char token : postfix.toCharArray()) { + if (isOperator(token)) { + operatorCount++; + if (operatorCount >= operandCount) { + return false; // Invalid: more operators than operands at any point + } + } else { + operandCount++; + } + } + + return operandCount == operatorCount + 1; + } + + /** + * Converts a valid postfix expression to an infix expression. + * + * @param postfix the postfix expression to convert + * @return the equivalent infix expression + * @throws IllegalArgumentException if the postfix expression is invalid + */ + public static String getPostfixToInfix(String postfix) { + if (postfix.isEmpty()) { + return ""; + } + + if (!isValidPostfixExpression(postfix)) { + throw new IllegalArgumentException("Invalid Postfix Expression"); + } + + Stack<String> stack = new Stack<>(); + StringBuilder valueString = new StringBuilder(); + + for (char token : postfix.toCharArray()) { + if (!isOperator(token)) { + stack.push(Character.toString(token)); + } else { + String operandB = stack.pop(); + String operandA = stack.pop(); + valueString.append('(').append(operandA).append(token).append(operandB).append(')'); + stack.push(valueString.toString()); + valueString.setLength(0); + } + } + + return stack.pop(); + } +} diff --git a/src/main/java/com/thealgorithms/stacks/PrefixEvaluator.java b/src/main/java/com/thealgorithms/stacks/PrefixEvaluator.java new file mode 100644 index 000000000000..eb2ce95e18d8 --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/PrefixEvaluator.java @@ -0,0 +1,76 @@ +package com.thealgorithms.stacks; + +import java.util.Set; +import java.util.Stack; + +/** + * Evaluate a prefix (Polish) expression using a stack. + * + * <p>Example: Expression "+ * 2 3 4" results in 10. + * <p>Applications: Useful for implementing compilers and interpreters. + * + * @author Hardvan + */ +public final class PrefixEvaluator { + private PrefixEvaluator() { + } + + private static final Set<String> OPERATORS = Set.of("+", "-", "*", "/"); + + /** + * Evaluates the given prefix expression and returns the result. + * + * @param expression The prefix expression as a string with operands and operators separated by spaces. + * @return The result of evaluating the prefix expression. + * @throws IllegalArgumentException if the expression is invalid. + */ + public static int evaluatePrefix(String expression) { + Stack<Integer> stack = new Stack<>(); + String[] tokens = expression.split("\\s+"); + + for (int i = tokens.length - 1; i >= 0; i--) { + String token = tokens[i]; + if (isOperator(token)) { + int operand1 = stack.pop(); + int operand2 = stack.pop(); + stack.push(applyOperator(token, operand1, operand2)); + } else { + stack.push(Integer.valueOf(token)); + } + } + + if (stack.size() != 1) { + throw new IllegalArgumentException("Invalid expression"); + } + + return stack.pop(); + } + + /** + * Checks if the given token is an operator. + * + * @param token The token to check. + * @return true if the token is an operator, false otherwise. + */ + private static boolean isOperator(String token) { + return OPERATORS.contains(token); + } + + /** + * Applies the given operator to the two operands. + * + * @param operator The operator to apply. + * @param a The first operand. + * @param b The second operand. + * @return The result of applying the operator to the operands. + */ + private static int applyOperator(String operator, int a, int b) { + return switch (operator) { + case "+" -> a + b; + case "-" -> a - b; + case "*" -> a * b; + case "/" -> a / b; + default -> throw new IllegalArgumentException("Invalid operator"); + }; + } +} diff --git a/src/main/java/com/thealgorithms/stacks/PrefixToInfix.java b/src/main/java/com/thealgorithms/stacks/PrefixToInfix.java new file mode 100644 index 000000000000..41eb974b0e5b --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/PrefixToInfix.java @@ -0,0 +1,69 @@ +package com.thealgorithms.stacks; + +import java.util.Stack; + +/** + * Converts a prefix expression to an infix expression using a stack. + * + * The input prefix expression should consist of + * valid operands (letters or digits) and operators (+, -, *, /, ^). + * Parentheses are not required in the prefix string. + */ +public final class PrefixToInfix { + private PrefixToInfix() { + } + + /** + * Determines if a given character is a valid arithmetic operator. + * + * @param token the character to check + * @return true if the character is an operator, false otherwise + */ + public static boolean isOperator(char token) { + return token == '+' || token == '-' || token == '/' || token == '*' || token == '^'; + } + + /** + * Converts a valid prefix expression to an infix expression. + * + * @param prefix the prefix expression to convert + * @return the equivalent infix expression + * @throws NullPointerException if the prefix expression is null + */ + public static String getPrefixToInfix(String prefix) { + if (prefix == null) { + throw new NullPointerException("Null prefix expression"); + } + if (prefix.isEmpty()) { + return ""; + } + + Stack<String> stack = new Stack<>(); + + // Iterate over the prefix expression from right to left + for (int i = prefix.length() - 1; i >= 0; i--) { + char token = prefix.charAt(i); + + if (isOperator(token)) { + // Pop two operands from stack + String operandA = stack.pop(); + String operandB = stack.pop(); + + // Form the infix expression with parentheses + String infix = "(" + operandA + token + operandB + ")"; + + // Push the resulting infix expression back onto the stack + stack.push(infix); + } else { + // Push operand onto stack + stack.push(Character.toString(token)); + } + } + + if (stack.size() != 1) { + throw new ArithmeticException("Malformed prefix expression"); + } + + return stack.pop(); // final element on the stack is the full infix expression + } +} diff --git a/src/main/java/com/thealgorithms/stacks/SmallestElementConstantTime.java b/src/main/java/com/thealgorithms/stacks/SmallestElementConstantTime.java new file mode 100644 index 000000000000..9864ef9b0f97 --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/SmallestElementConstantTime.java @@ -0,0 +1,74 @@ +package com.thealgorithms.stacks; + +import java.util.NoSuchElementException; +import java.util.Stack; + +/** + * A class that implements a stack that gives the minimum element in O(1) time. + * The mainStack is used to store the all the elements of the stack + * While the minStack stores the minimum elements + * When we want to get a minimum element, we call the top of the minimum stack + * + * Problem: https://www.baeldung.com/cs/stack-constant-time + */ +public class SmallestElementConstantTime { + private Stack<Integer> mainStack; // initialize a mainStack + private Stack<Integer> minStack; // initialize a minStack + + /** + * Constructs two empty stacks + */ + public SmallestElementConstantTime() { + mainStack = new Stack<>(); + minStack = new Stack<>(); + } + + /** + * Pushes an element onto the top of the stack. + * Checks if the element is the minimum or not + * If so, then pushes to the minimum stack + * @param data The element to be pushed onto the stack. + */ + public void push(int data) { + if (mainStack.isEmpty()) { + mainStack.push(data); + minStack.push(data); + return; + } + + mainStack.push(data); + if (data < minStack.peek()) { + minStack.push(data); + } + } + + /** + * Pops an element from the stack. + * Checks if the element to be popped is the minimum or not + * If so, then pop from the minStack + * + * @throws NoSuchElementException if the stack is empty. + */ + public void pop() { + if (mainStack.isEmpty()) { + throw new NoSuchElementException("Stack is empty"); + } + + int ele = mainStack.pop(); + if (ele == minStack.peek()) { + minStack.pop(); + } + } + + /** + * Returns the minimum element present in the stack + * + * @return The element at the top of the minStack, or null if the stack is empty. + */ + public Integer getMinimumElement() { + if (minStack.isEmpty()) { + return null; + } + return minStack.peek(); + } +} diff --git a/src/main/java/com/thealgorithms/stacks/SortStack.java b/src/main/java/com/thealgorithms/stacks/SortStack.java new file mode 100644 index 000000000000..d07d1a5f1dc3 --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/SortStack.java @@ -0,0 +1,60 @@ +package com.thealgorithms.stacks; + +import java.util.Stack; + +/** + * A utility class that provides a method to sort a stack using recursion. + * The elements are sorted in ascending order, with the largest element at the top. + * This algorithm is implemented using only recursion and the original stack, + * without utilizing any additional data structures apart from the stack itself. + */ +public final class SortStack { + private SortStack() { + } + + /** + * Sorts the given stack in ascending order using recursion. + * The sorting is performed such that the largest element ends up on top of the stack. + * This method modifies the original stack and does not return a new stack. + * + * The algorithm works as follows: + * 1. Remove the top element. + * 2. Recursively sort the remaining stack. + * 3. Insert the removed element back into the sorted stack at the correct position. + * + * @param stack The stack to be sorted, containing Integer elements. + * @throws IllegalArgumentException if the stack contains `null` elements. + */ + public static void sortStack(Stack<Integer> stack) { + if (stack.isEmpty()) { + return; + } + + int top = stack.pop(); + sortStack(stack); + insertInSortedOrder(stack, top); + } + + /** + * Helper method to insert an element into the correct position in a sorted stack. + * This method is called recursively to place the given element into the stack + * such that the stack remains sorted in ascending order. + * + * The element is inserted in such a way that all elements below it are smaller + * (if the stack is non-empty), and elements above it are larger, maintaining + * the ascending order. + * + * @param stack The stack in which the element needs to be inserted. + * @param element The element to be inserted into the stack in sorted order. + */ + private static void insertInSortedOrder(Stack<Integer> stack, int element) { + if (stack.isEmpty() || element > stack.peek()) { + stack.push(element); + return; + } + + int top = stack.pop(); + insertInSortedOrder(stack, element); + stack.push(top); + } +} diff --git a/src/main/java/com/thealgorithms/stacks/StackPostfixNotation.java b/src/main/java/com/thealgorithms/stacks/StackPostfixNotation.java new file mode 100644 index 000000000000..690f39d36f5c --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/StackPostfixNotation.java @@ -0,0 +1,72 @@ +package com.thealgorithms.stacks; + +import java.util.Scanner; +import java.util.Stack; +import java.util.function.BiFunction; + +/** + * Utility class for evaluating postfix expressions using integer arithmetic. + * <p> + * Postfix notation, also known as Reverse Polish Notation (RPN), is a mathematical notation in which operators follow their operands. + * This class provides a method to evaluate expressions written in postfix notation. + * </p> + * <p> + * For more information on postfix notation, refer to + * <a href="https://en.wikipedia.org/wiki/Reverse_Polish_notation">Reverse Polish Notation (RPN) on Wikipedia</a>. + * </p> + */ +public final class StackPostfixNotation { + private StackPostfixNotation() { + } + + private static BiFunction<Integer, Integer, Integer> getOperator(final String operationSymbol) { + // note the order of operands + switch (operationSymbol) { + case "+": + return (a, b) -> b + a; + case "-": + return (a, b) -> b - a; + case "*": + return (a, b) -> b * a; + case "/": + return (a, b) -> b / a; + default: + throw new IllegalArgumentException("exp contains an unknown operation."); + } + } + + private static void performOperation(Stack<Integer> s, final String operationSymbol) { + if (s.size() < 2) { + throw new IllegalArgumentException("exp is not a proper postfix expression (too few arguments)."); + } + s.push(getOperator(operationSymbol).apply(s.pop(), s.pop())); + } + + private static void consumeExpression(Stack<Integer> s, final String exp) { + Scanner tokens = new Scanner(exp); + + while (tokens.hasNext()) { + if (tokens.hasNextInt()) { + s.push(tokens.nextInt()); + } else { + performOperation(s, tokens.next()); + } + } + tokens.close(); + } + + /** + * @brief Evaluates the given postfix expression. + * @param exp the expression to evaluate. + * @return the value of the given expression. + * @exception IllegalArgumentException exp is not a valid postix expression. + */ + public static int postfixEvaluate(final String exp) { + Stack<Integer> s = new Stack<>(); + consumeExpression(s, exp); + if (s.size() != 1) { + throw new IllegalArgumentException("exp is not a proper postfix expression."); + } + return s.pop(); + } +} diff --git a/src/main/java/com/thealgorithms/stacks/StackUsingTwoQueues.java b/src/main/java/com/thealgorithms/stacks/StackUsingTwoQueues.java new file mode 100644 index 000000000000..5b1ca5d1d5a5 --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/StackUsingTwoQueues.java @@ -0,0 +1,91 @@ +package com.thealgorithms.stacks; + +import java.util.LinkedList; +import java.util.NoSuchElementException; +import java.util.Queue; + +/** + * A class that implements a stack using two queues. + * This approach ensures that the stack's LIFO (Last In, First Out) behavior + * is maintained by utilizing two queues for storage. + * The mainQueue is used to store the elements of the stack, while the tempQueue + * is used to temporarily store elements during the push operation. + */ +public class StackUsingTwoQueues { + + private Queue<Integer> mainQueue; + private Queue<Integer> tempQueue; + + /** + * Constructs an empty stack using two queues. + */ + public StackUsingTwoQueues() { + mainQueue = new LinkedList<>(); + tempQueue = new LinkedList<>(); + } + + /** + * Pushes an element onto the top of the stack. + * The newly pushed element becomes the top of the stack. + * + * @param item The element to be pushed onto the stack. + */ + public void push(int item) { + tempQueue.add(item); + + // Move all elements from the mainQueue to tempQueue to maintain LIFO order + while (!mainQueue.isEmpty()) { + tempQueue.add(mainQueue.remove()); + } + + // Swap the names of the two queues + Queue<Integer> swap = mainQueue; + mainQueue = tempQueue; + tempQueue = swap; // tempQueue is now empty + } + + /** + * Removes and returns the element at the top of the stack. + * Throws an exception if the stack is empty. + * + * @return The element at the top of the stack. + * @throws NoSuchElementException if the stack is empty. + */ + public int pop() { + if (mainQueue.isEmpty()) { + throw new NoSuchElementException("Stack is empty"); + } + return mainQueue.remove(); + } + + /** + * Returns the element at the top of the stack without removing it. + * Returns null if the stack is empty. + * + * @return The element at the top of the stack, or null if the stack is empty. + */ + public Integer peek() { + if (mainQueue.isEmpty()) { + return null; + } + return mainQueue.peek(); + } + + /** + * Returns true if the stack is empty. + * + * @return true if the stack is empty; false otherwise. + */ + public boolean isEmpty() { + return mainQueue.isEmpty(); + } + + /** + * Returns the number of elements in the stack. + * + * @return The size of the stack. + */ + public int size() { + return mainQueue.size(); + } +} diff --git a/src/main/java/com/thealgorithms/strings/AhoCorasick.java b/src/main/java/com/thealgorithms/strings/AhoCorasick.java new file mode 100644 index 000000000000..a68d0823a00d --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/AhoCorasick.java @@ -0,0 +1,246 @@ +/* + * Aho-Corasick String Matching Algorithm Implementation + * + * This code implements the Aho-Corasick algorithm, which is used for efficient + * string matching in a given text. It can find multiple patterns simultaneously + * and records their positions in the text. + * + * Author: Prabhat-Kumar-42 + * GitHub: https://github.com/Prabhat-Kumar-42 + */ + +package com.thealgorithms.strings; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; + +public final class AhoCorasick { + private AhoCorasick() { + } + + // Trie Node Class + private static class Node { + // Represents a character in the trie + private final Map<Character, Node> child = new HashMap<>(); // Child nodes of the current node + private Node suffixLink; // Suffix link to another node in the trie + private Node outputLink; // Output link to another node in the trie + private int patternInd; // Index of the pattern that ends at this node + + Node() { + this.suffixLink = null; + this.outputLink = null; + this.patternInd = -1; + } + + public Map<Character, Node> getChild() { + return child; + } + + public Node getSuffixLink() { + return suffixLink; + } + + public void setSuffixLink(final Node suffixLink) { + this.suffixLink = suffixLink; + } + + public Node getOutputLink() { + return outputLink; + } + + public void setOutputLink(final Node outputLink) { + this.outputLink = outputLink; + } + + public int getPatternInd() { + return patternInd; + } + + public void setPatternInd(final int patternInd) { + this.patternInd = patternInd; + } + } + + // Trie Class + public static class Trie { + + private Node root = null; // Root node of the trie + private final String[] patterns; // patterns according to which Trie is constructed + + public Trie(final String[] patterns) { + root = new Node(); // Initialize the root of the trie + this.patterns = patterns; + buildTrie(); + buildSuffixAndOutputLinks(); + } + + // builds AhoCorasick Trie + private void buildTrie() { + + // Loop through each input pattern and building Trie + for (int i = 0; i < patterns.length; i++) { + Node curr = root; // Start at the root of the trie for each pattern + + // Loop through each character in the current pattern + for (int j = 0; j < patterns[i].length(); j++) { + char c = patterns[i].charAt(j); // Get the current character + + // Check if the current node has a child for the current character + if (curr.getChild().containsKey(c)) { + curr = curr.getChild().get(c); // Update the current node to the child node + } else { + // If no child node exists, create a new one and add it to the current node's children + Node nn = new Node(); + curr.getChild().put(c, nn); + curr = nn; // Update the current node to the new child node + } + } + curr.setPatternInd(i); // Store the index of the pattern in the current leaf node + } + } + + private void initializeSuffixLinksForChildNodesOfTheRoot(Queue<Node> q) { + for (char rc : root.getChild().keySet()) { + Node childNode = root.getChild().get(rc); + q.add(childNode); // Add child node to the queue + childNode.setSuffixLink(root); // Set suffix link to the root + } + } + + private void buildSuffixAndOutputLinks() { + root.setSuffixLink(root); // Initialize the suffix link of the root to itself + Queue<Node> q = new LinkedList<>(); // Initialize a queue for BFS traversal + + initializeSuffixLinksForChildNodesOfTheRoot(q); + + while (!q.isEmpty()) { + Node currentState = q.poll(); // Get the current node for processing + + // Iterate through child nodes of the current node + for (char cc : currentState.getChild().keySet()) { + Node currentChild = currentState.getChild().get(cc); // Get the child node + Node parentSuffix = currentState.getSuffixLink(); // Get the parent's suffix link + + // Calculate the suffix link for the child based on the parent's suffix link + while (!parentSuffix.getChild().containsKey(cc) && parentSuffix != root) { + parentSuffix = parentSuffix.getSuffixLink(); + } + + // Set the calculated suffix link or default to root + if (parentSuffix.getChild().containsKey(cc)) { + currentChild.setSuffixLink(parentSuffix.getChild().get(cc)); + } else { + currentChild.setSuffixLink(root); + } + + q.add(currentChild); // Add the child node to the queue for further processing + } + + // Establish output links for nodes to efficiently identify patterns within patterns + if (currentState.getSuffixLink().getPatternInd() >= 0) { + currentState.setOutputLink(currentState.getSuffixLink()); + } else { + currentState.setOutputLink(currentState.getSuffixLink().getOutputLink()); + } + } + } + + private List<List<Integer>> initializePositionByStringIndexValue() { + List<List<Integer>> positionByStringIndexValue = new ArrayList<>(patterns.length); // Stores positions where patterns are found in the text + for (int i = 0; i < patterns.length; i++) { + positionByStringIndexValue.add(new ArrayList<>()); + } + return positionByStringIndexValue; + } + + // Searches for patterns in the input text and records their positions + public List<List<Integer>> searchIn(final String text) { + var positionByStringIndexValue = initializePositionByStringIndexValue(); // Initialize a list to store positions of the current pattern + Node parent = root; // Start searching from the root node + + PatternPositionRecorder positionRecorder = new PatternPositionRecorder(positionByStringIndexValue); + + for (int i = 0; i < text.length(); i++) { + char ch = text.charAt(i); // Get the current character in the text + + // Check if the current node has a child for the current character + if (parent.getChild().containsKey(ch)) { + parent = parent.getChild().get(ch); // Update the current node to the child node + positionRecorder.recordPatternPositions(parent, i); // Use the method in PatternPositionRecorder to record positions + } else { + // If no child node exists for the character, backtrack using suffix links + while (parent != root && !parent.getChild().containsKey(ch)) { + parent = parent.getSuffixLink(); + } + if (parent.getChild().containsKey(ch)) { + i--; // Decrement i to reprocess the same character + } + } + } + + setUpStartPoints(positionByStringIndexValue); + return positionByStringIndexValue; + } + + // by default positionByStringIndexValue contains end-points. This function converts those + // endpoints to start points + private void setUpStartPoints(List<List<Integer>> positionByStringIndexValue) { + for (int i = 0; i < patterns.length; i++) { + for (int j = 0; j < positionByStringIndexValue.get(i).size(); j++) { + int endpoint = positionByStringIndexValue.get(i).get(j); + positionByStringIndexValue.get(i).set(j, endpoint - patterns[i].length() + 1); + } + } + } + } + + // Class to handle pattern position recording + private record PatternPositionRecorder(List<List<Integer>> positionByStringIndexValue) { + // Constructor to initialize the recorder with the position list + + /** + * Records positions for a pattern when it's found in the input text and follows + * output links to record positions of other patterns. + * + * @param parent The current node representing a character in the pattern trie. + * @param currentPosition The current position in the input text. + */ + public void recordPatternPositions(final Node parent, final int currentPosition) { + // Check if the current node represents the end of a pattern + if (parent.getPatternInd() > -1) { + // Add the current position to the list of positions for the found pattern + positionByStringIndexValue.get(parent.getPatternInd()).add(currentPosition); + } + + Node outputLink = parent.getOutputLink(); + // Follow output links to find and record positions of other patterns + while (outputLink != null) { + // Add the current position to the list of positions for the pattern linked by outputLink + positionByStringIndexValue.get(outputLink.getPatternInd()).add(currentPosition); + outputLink = outputLink.getOutputLink(); + } + } + } + + // method to search for patterns in text + public static Map<String, List<Integer>> search(final String text, final String[] patterns) { + final var trie = new Trie(patterns); + final var positionByStringIndexValue = trie.searchIn(text); + return convert(positionByStringIndexValue, patterns); + } + + // method for converting results to a map + private static Map<String, List<Integer>> convert(final List<List<Integer>> positionByStringIndexValue, final String[] patterns) { + Map<String, List<Integer>> positionByString = new HashMap<>(); + for (int i = 0; i < patterns.length; i++) { + String pattern = patterns[i]; + List<Integer> positions = positionByStringIndexValue.get(i); + positionByString.put(pattern, new ArrayList<>(positions)); + } + return positionByString; + } +} diff --git a/src/main/java/com/thealgorithms/strings/Alphabetical.java b/src/main/java/com/thealgorithms/strings/Alphabetical.java new file mode 100644 index 000000000000..de07dde2d510 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/Alphabetical.java @@ -0,0 +1,36 @@ +package com.thealgorithms.strings; + +/** + * Alphabetical order is a system whereby character strings are placed in order + * based on the position of the characters in the conventional ordering of an + * alphabet. Wikipedia: https://en.wikipedia.org/wiki/Alphabetical_order + */ +final class Alphabetical { + private Alphabetical() { + } + + public static void main(String[] args) { + assert !isAlphabetical("123abc"); + assert isAlphabetical("aBC"); + assert isAlphabetical("abc"); + assert !isAlphabetical("xyzabc"); + assert isAlphabetical("abcxyz"); + } + + /** + * Check if a string is alphabetical order or not + * + * @param s a string + * @return {@code true} if given string is alphabetical order, otherwise + * {@code false} + */ + public static boolean isAlphabetical(String s) { + s = s.toLowerCase(); + for (int i = 0; i < s.length() - 1; ++i) { + if (!Character.isLetter(s.charAt(i)) || !(s.charAt(i) <= s.charAt(i + 1))) { + return false; + } + } + return true; + } +} diff --git a/src/main/java/com/thealgorithms/strings/Anagrams.java b/src/main/java/com/thealgorithms/strings/Anagrams.java new file mode 100644 index 000000000000..4b24979e2689 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/Anagrams.java @@ -0,0 +1,142 @@ +package com.thealgorithms.strings; + +import java.util.Arrays; +import java.util.HashMap; + +/** + * An anagram is a word or phrase formed by rearranging the letters of a different word or phrase, + * typically using all the original letters exactly once.[1] + * For example, the word anagram itself can be rearranged into nag a ram, + * also the word binary into brainy and the word adobe into abode. + * Reference from https://en.wikipedia.org/wiki/Anagram + */ +public final class Anagrams { + private Anagrams() { + } + + /** + * Checks if two strings are anagrams by sorting the characters and comparing them. + * Time Complexity: O(n log n) + * Space Complexity: O(n) + * + * @param s the first string + * @param t the second string + * @return true if the strings are anagrams, false otherwise + */ + public static boolean approach1(String s, String t) { + if (s.length() != t.length()) { + return false; + } + char[] c = s.toCharArray(); + char[] d = t.toCharArray(); + Arrays.sort(c); + Arrays.sort(d); + return Arrays.equals(c, d); + } + + /** + * Checks if two strings are anagrams by counting the frequency of each character. + * Time Complexity: O(n) + * Space Complexity: O(1) + * + * @param s the first string + * @param t the second string + * @return true if the strings are anagrams, false otherwise + */ + public static boolean approach2(String s, String t) { + if (s.length() != t.length()) { + return false; + } + int[] charCount = new int[26]; + for (int i = 0; i < s.length(); i++) { + charCount[s.charAt(i) - 'a']++; + charCount[t.charAt(i) - 'a']--; + } + for (int count : charCount) { + if (count != 0) { + return false; + } + } + return true; + } + + /** + * Checks if two strings are anagrams by counting the frequency of each character + * using a single array. + * Time Complexity: O(n) + * Space Complexity: O(1) + * + * @param s the first string + * @param t the second string + * @return true if the strings are anagrams, false otherwise + */ + public static boolean approach3(String s, String t) { + if (s.length() != t.length()) { + return false; + } + int[] charCount = new int[26]; + for (int i = 0; i < s.length(); i++) { + charCount[s.charAt(i) - 'a']++; + charCount[t.charAt(i) - 'a']--; + } + for (int count : charCount) { + if (count != 0) { + return false; + } + } + return true; + } + + /** + * Checks if two strings are anagrams using a HashMap to store character frequencies. + * Time Complexity: O(n) + * Space Complexity: O(n) + * + * @param s the first string + * @param t the second string + * @return true if the strings are anagrams, false otherwise + */ + public static boolean approach4(String s, String t) { + if (s.length() != t.length()) { + return false; + } + HashMap<Character, Integer> charCountMap = new HashMap<>(); + for (char c : s.toCharArray()) { + charCountMap.put(c, charCountMap.getOrDefault(c, 0) + 1); + } + for (char c : t.toCharArray()) { + if (!charCountMap.containsKey(c) || charCountMap.get(c) == 0) { + return false; + } + charCountMap.put(c, charCountMap.get(c) - 1); + } + return charCountMap.values().stream().allMatch(count -> count == 0); + } + + /** + * Checks if two strings are anagrams using an array to track character frequencies. + * This approach optimizes space complexity by using only one array. + * Time Complexity: O(n) + * Space Complexity: O(1) + * + * @param s the first string + * @param t the second string + * @return true if the strings are anagrams, false otherwise + */ + public static boolean approach5(String s, String t) { + if (s.length() != t.length()) { + return false; + } + int[] freq = new int[26]; + for (int i = 0; i < s.length(); i++) { + freq[s.charAt(i) - 'a']++; + freq[t.charAt(i) - 'a']--; + } + for (int count : freq) { + if (count != 0) { + return false; + } + } + return true; + } +} diff --git a/src/main/java/com/thealgorithms/strings/CharactersSame.java b/src/main/java/com/thealgorithms/strings/CharactersSame.java new file mode 100644 index 000000000000..68785052e0e1 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/CharactersSame.java @@ -0,0 +1,26 @@ +package com.thealgorithms.strings; + +public final class CharactersSame { + private CharactersSame() { + } + + /** + * Checks if all characters in the string are the same. + * + * @param s the string to check + * @return {@code true} if all characters in the string are the same or if the string is empty, otherwise {@code false} + */ + public static boolean isAllCharactersSame(String s) { + if (s.isEmpty()) { + return true; // Empty strings can be considered as having "all the same characters" + } + + char firstChar = s.charAt(0); + for (int i = 1; i < s.length(); i++) { + if (s.charAt(i) != firstChar) { + return false; + } + } + return true; + } +} diff --git a/src/main/java/com/thealgorithms/strings/CheckAnagrams.java b/src/main/java/com/thealgorithms/strings/CheckAnagrams.java new file mode 100644 index 000000000000..7bf7cd9a7c66 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/CheckAnagrams.java @@ -0,0 +1,110 @@ +package com.thealgorithms.strings; + +import java.util.HashMap; +import java.util.Map; + +/** + * Two strings are anagrams if they are made of the same letters arranged + * differently (ignoring the case). + */ +public final class CheckAnagrams { + private CheckAnagrams() { + } + /** + * Check if two strings are anagrams or not + * + * @param s1 the first string + * @param s2 the second string + * @return {@code true} if two string are anagrams, otherwise {@code false} + */ + public static boolean isAnagrams(String s1, String s2) { + int l1 = s1.length(); + int l2 = s2.length(); + s1 = s1.toLowerCase(); + s2 = s2.toLowerCase(); + Map<Character, Integer> charAppearances = new HashMap<>(); + + for (int i = 0; i < l1; i++) { + char c = s1.charAt(i); + int numOfAppearances = charAppearances.getOrDefault(c, 0); + charAppearances.put(c, numOfAppearances + 1); + } + + for (int i = 0; i < l2; i++) { + char c = s2.charAt(i); + if (!charAppearances.containsKey(c)) { + return false; + } + charAppearances.put(c, charAppearances.get(c) - 1); + } + + for (int cnt : charAppearances.values()) { + if (cnt != 0) { + return false; + } + } + return true; + } + + /** + * If given strings contain Unicode symbols. + * The first 128 ASCII codes are identical to Unicode. + * This algorithm is case-sensitive. + * + * @param s1 the first string + * @param s2 the second string + * @return true if two string are anagrams, otherwise false + */ + public static boolean isAnagramsUnicode(String s1, String s2) { + int[] dict = new int[128]; + for (char ch : s1.toCharArray()) { + dict[ch]++; + } + for (char ch : s2.toCharArray()) { + dict[ch]--; + } + for (int e : dict) { + if (e != 0) { + return false; + } + } + return true; + } + + /** + * If given strings contain only lowercase English letters. + * <p> + * The main "trick": + * To map each character from the first string 's1' we need to subtract an integer value of 'a' character + * as 'dict' array starts with 'a' character. + * + * @param s1 the first string + * @param s2 the second string + * @return true if two string are anagrams, otherwise false + */ + public static boolean isAnagramsOptimised(String s1, String s2) { + // 26 - English alphabet length + int[] dict = new int[26]; + for (char ch : s1.toCharArray()) { + checkLetter(ch); + dict[ch - 'a']++; + } + for (char ch : s2.toCharArray()) { + checkLetter(ch); + dict[ch - 'a']--; + } + for (int e : dict) { + if (e != 0) { + return false; + } + } + return true; + } + + private static void checkLetter(char ch) { + int index = ch - 'a'; + if (index < 0 || index >= 26) { + throw new IllegalArgumentException("Strings must contain only lowercase English letters!"); + } + } +} diff --git a/src/main/java/com/thealgorithms/strings/CheckVowels.java b/src/main/java/com/thealgorithms/strings/CheckVowels.java new file mode 100644 index 000000000000..21b536b5c7d5 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/CheckVowels.java @@ -0,0 +1,34 @@ +package com.thealgorithms.strings; + +import java.util.Set; + +/** + * Vowel Count is a system whereby character strings are placed in order based + * on the position of the characters in the conventional ordering of an + * alphabet. Wikipedia: https://en.wikipedia.org/wiki/Alphabetical_order + */ +public final class CheckVowels { + private static final Set<Character> VOWELS = Set.of('a', 'e', 'i', 'o', 'u'); + + private CheckVowels() { + } + + /** + * Checks if a string contains any vowels. + * + * @param input a string to check + * @return {@code true} if the given string contains at least one vowel, otherwise {@code false} + */ + public static boolean hasVowels(String input) { + if (input == null || input.isEmpty()) { + return false; + } + + for (char c : input.toLowerCase().toCharArray()) { + if (VOWELS.contains(c)) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/com/thealgorithms/strings/CountChar.java b/src/main/java/com/thealgorithms/strings/CountChar.java new file mode 100644 index 000000000000..348905445347 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/CountChar.java @@ -0,0 +1,20 @@ +package com.thealgorithms.strings; + +public final class CountChar { + private CountChar() { + } + + /** + * Counts the number of non-whitespace characters in the given string. + * + * @param str the input string to count the characters in + * @return the number of non-whitespace characters in the specified string; + * returns 0 if the input string is null + */ + public static int countCharacters(String str) { + if (str == null) { + return 0; + } + return str.replaceAll("\\s", "").length(); + } +} diff --git a/src/main/java/com/thealgorithms/strings/CountWords.java b/src/main/java/com/thealgorithms/strings/CountWords.java new file mode 100644 index 000000000000..8ab0700f5586 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/CountWords.java @@ -0,0 +1,55 @@ +package com.thealgorithms.strings; + +/** + * @author Marcus + */ +public final class CountWords { + private CountWords() { + } + + /** + * Counts the number of words in the input string. Words are defined as sequences of + * characters separated by whitespace. + * + * @param s the input string + * @return the number of words in the input string, or 0 if the string is null or empty + */ + public static int wordCount(String s) { + if (s == null || s.isEmpty()) { + return 0; + } + return s.trim().split("\\s+").length; + } + + /** + * Removes all special characters from the input string, leaving only alphanumeric characters + * and whitespace. + * + * @param s the input string + * @return a string containing only alphanumeric characters and whitespace + */ + private static String removeSpecialCharacters(String s) { + StringBuilder sb = new StringBuilder(); + for (char c : s.toCharArray()) { + if (Character.isLetterOrDigit(c) || Character.isWhitespace(c)) { + sb.append(c); + } + } + return sb.toString(); + } + + /** + * Counts the number of words in a sentence, ignoring all non-alphanumeric characters that do + * not contribute to word formation. This method has a time complexity of O(n), where n is the + * length of the input string. + * + * @param s the input string + * @return the number of words in the input string, with special characters removed, or 0 if the string is null + */ + public static int secondaryWordCount(String s) { + if (s == null) { + return 0; + } + return wordCount(removeSpecialCharacters(s)); + } +} diff --git a/src/main/java/com/thealgorithms/strings/HammingDistance.java b/src/main/java/com/thealgorithms/strings/HammingDistance.java new file mode 100644 index 000000000000..235e317d94f1 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/HammingDistance.java @@ -0,0 +1,45 @@ +package com.thealgorithms.strings; + +/** + * Class for calculating the Hamming distance between two strings of equal length. + * <p> + * The Hamming distance is the number of positions at which the corresponding symbols are different. + * It is used in information theory, coding theory, and computer science. + * </p> + * @see <a href="https://en.wikipedia.org/wiki/Hamming_distance">Hamming distance - Wikipedia</a> + */ +public final class HammingDistance { + private HammingDistance() { + } + + /** + * Calculates the Hamming distance between two strings of equal length. + * <p> + * The Hamming distance is defined only for strings of equal length. If the strings are not + * of equal length, this method throws an {@code IllegalArgumentException}. + * </p> + * + * @param s1 the first string + * @param s2 the second string + * @return the Hamming distance between the two strings + * @throws IllegalArgumentException if the lengths of {@code s1} and {@code s2} are not equal + */ + public static int calculateHammingDistance(String s1, String s2) { + if (s1 == null || s2 == null) { + throw new IllegalArgumentException("Strings must not be null"); + } + + if (s1.length() != s2.length()) { + throw new IllegalArgumentException("String lengths must be equal"); + } + + int distance = 0; + + for (int i = 0; i < s1.length(); i++) { + if (s1.charAt(i) != s2.charAt(i)) { + distance++; + } + } + return distance; + } +} diff --git a/src/main/java/com/thealgorithms/strings/HorspoolSearch.java b/src/main/java/com/thealgorithms/strings/HorspoolSearch.java new file mode 100644 index 000000000000..d9187cbf66c4 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/HorspoolSearch.java @@ -0,0 +1,185 @@ +package com.thealgorithms.strings; + +import java.util.HashMap; + +/** + * This class is not thread safe<br> + * <br> + * (From wikipedia) In computer science, the Boyer–Moore–Horspool algorithm or + * Horspool's algorithm is an algorithm for finding substrings in strings. It + * was published by Nigel Horspool in 1980. + * <br> + * <a href=https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm>Wikipedia + * page</a><br> + * <br> + * + * <p> + * An explanation:<br> + * + * <p> + * The Horspool algorithm is a simplification of the Boyer-Moore algorithm in + * that it uses only one of the two heuristic methods for increasing the number + * of characters shifted when finding a bad match in the text. This method is + * usually called the "bad symbol" or "bad character" shift. The bad symbol + * shift method is classified as an input enhancement method in the theory of + * algorithms. Input enhancement is (from wikipedia) the principle that + * processing a given input to a problem and altering it in a specific way will + * increase runtime efficiency or space efficiency, or both. Both algorithms try + * to match the pattern and text comparing the pattern symbols to the text's + * from right to left.<br> + * <br> + * + * <p> + * In the bad symbol shift method, a table is created prior to the search, + * called the "bad symbol table". The bad symbol table contains the shift values + * for any symbol in the text and pattern. For these symbols, the value is the + * length of the pattern, if the symbol is not in the first (length - 1) of the + * pattern. Else it is the distance from its rightmost occurrence in the pattern + * to the last symbol of the pattern. In practice, we only calculate the values + * for the ones that exist in the first (length - 1) of the pattern.<br> + * <br> + * + * <p> + * For more details on the algorithm and the more advanced Boyer-Moore I + * recommend checking out the wikipedia page and professor Anany Levitin's book: + * Introduction To The Design And Analysis Of Algorithms. + */ +public final class HorspoolSearch { + private HorspoolSearch() { + } + + private static HashMap<Character, Integer> shiftValues; // bad symbol table + private static Integer patternLength; + private static int comparisons = 0; // total comparisons in the current/last search + + /** + * Case sensitive version version of the algorithm + * + * @param pattern the pattern to be searched for (needle) + * @param text the text being searched in (haystack) + * @return -1 if not found or first index of the pattern in the text + */ + public static int findFirst(String pattern, String text) { + return firstOccurrence(pattern, text, true); + } + + /** + * Case insensitive version version of the algorithm + * + * @param pattern the pattern to be searched for (needle) + * @param text the text being searched in (haystack) + * @return -1 if not found or first index of the pattern in the text + */ + public static int findFirstInsensitive(String pattern, String text) { + return firstOccurrence(pattern, text, false); + } + + /** + * Utility method that returns comparisons made by last run (mainly for + * tests) + * + * @return number of character comparisons of the last search + */ + public static Integer getLastComparisons() { + return HorspoolSearch.comparisons; + } + + /** + * Fairly standard implementation of the Horspool algorithm. Only the index + * of the last character of the pattern on the text is saved and shifted by + * the appropriate amount when a mismatch is found. The algorithm stops at + * the first match or when the entire text has been exhausted. + * + * @param pattern String to be matched in the text + * @param text text String + * @return index of first occurrence of the pattern in the text + */ + private static int firstOccurrence(String pattern, String text, boolean caseSensitive) { + shiftValues = calcShiftValues(pattern); // build the bad symbol table + comparisons = 0; // reset comparisons + + if (pattern.length() == 0) { // return failure, if pattern empty + return -1; + } + + int textIndex = pattern.length() - 1; // align pattern with text start and get index of the last character + + // while pattern is not out of text bounds + while (textIndex < text.length()) { + // try to match pattern with current part of the text starting from last character + int i = pattern.length() - 1; + while (i >= 0) { + comparisons++; + char patternChar = pattern.charAt(i); + char textChar = text.charAt((textIndex + i) - (pattern.length() - 1)); + if (!charEquals(patternChar, textChar, caseSensitive)) { // bad character, shift pattern + textIndex += getShiftValue(text.charAt(textIndex)); + break; + } + i--; + } + + // check for full match + if (i == -1) { + return textIndex - pattern.length() + 1; + } + } + + // text exhausted, return failure + return -1; + } + + /** + * Compares the argument characters + * + * @param c1 first character + * @param c2 second character + * @param caseSensitive boolean determining case sensitivity of comparison + * @return truth value of the equality comparison + */ + private static boolean charEquals(char c1, char c2, boolean caseSensitive) { + if (caseSensitive) { + return c1 == c2; + } + return Character.toLowerCase(c1) == Character.toLowerCase(c2); + } + + /** + * Builds the bad symbol table required to run the algorithm. The method + * starts from the second to last character of the pattern and moves to the + * left. When it meets a new character, it is by definition its rightmost + * occurrence and therefore puts the distance from the current index to the + * index of the last character into the table. If the character is already + * in the table, then it is not a rightmost occurrence, so it continues. + * + * @param pattern basis for the bad symbol table + * @return the bad symbol table + */ + private static HashMap<Character, Integer> calcShiftValues(String pattern) { + patternLength = pattern.length(); + HashMap<Character, Integer> table = new HashMap<>(); + + for (int i = pattern.length() - 2; i >= 0; i--) { // length - 2 is the index of the second to last character + char c = pattern.charAt(i); + int finalI = i; + table.computeIfAbsent(c, k -> pattern.length() - 1 - finalI); + } + + return table; + } + + /** + * Helper function that uses the bad symbol shift table to return the + * appropriate shift value for a given character + * + * @param c character + * @return shift value that corresponds to the character argument + */ + private static Integer getShiftValue(char c) { + if (shiftValues.get(c) != null) { + return shiftValues.get(c); + } else { + return patternLength; + } + } +} diff --git a/src/main/java/com/thealgorithms/strings/Isomorphic.java b/src/main/java/com/thealgorithms/strings/Isomorphic.java new file mode 100644 index 000000000000..ccd686170715 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/Isomorphic.java @@ -0,0 +1,39 @@ +package com.thealgorithms.strings; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public final class Isomorphic { + private Isomorphic() { + } + + public static boolean checkStrings(String s, String t) { + if (s.length() != t.length()) { + return false; + } + + // To mark the characters of string using MAP + // character of first string as KEY and another as VALUE + // now check occurence by keeping the track with SET data structure + Map<Character, Character> characterMap = new HashMap<>(); + Set<Character> trackUniqueCharacter = new HashSet<>(); + + for (int i = 0; i < s.length(); i++) { + if (characterMap.containsKey(s.charAt(i))) { + if (t.charAt(i) != characterMap.get(s.charAt(i))) { + return false; + } + } else { + if (trackUniqueCharacter.contains(t.charAt(i))) { + return false; + } + + characterMap.put(s.charAt(i), t.charAt(i)); + } + trackUniqueCharacter.add(t.charAt(i)); + } + return true; + } +} diff --git a/src/main/java/com/thealgorithms/strings/KMP.java b/src/main/java/com/thealgorithms/strings/KMP.java new file mode 100644 index 000000000000..07d3b0415006 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/KMP.java @@ -0,0 +1,60 @@ +package com.thealgorithms.strings; + +/** + * Implementation of Knuth–Morris–Pratt algorithm Usage: see the main function + * for an example + */ +public final class KMP { + private KMP() { + } + + // a working example + + public static void main(String[] args) { + final String haystack = "AAAAABAAABA"; // This is the full string + final String needle = "AAAA"; // This is the substring that we want to find + kmpMatcher(haystack, needle); + } + + // find the starting index in string haystack[] that matches the search word P[] + public static void kmpMatcher(final String haystack, final String needle) { + final int m = haystack.length(); + final int n = needle.length(); + final int[] pi = computePrefixFunction(needle); + int q = 0; + for (int i = 0; i < m; i++) { + while (q > 0 && haystack.charAt(i) != needle.charAt(q)) { + q = pi[q - 1]; + } + + if (haystack.charAt(i) == needle.charAt(q)) { + q++; + } + + if (q == n) { + System.out.println("Pattern starts: " + (i + 1 - n)); + q = pi[q - 1]; + } + } + } + + // return the prefix function + private static int[] computePrefixFunction(final String p) { + final int n = p.length(); + final int[] pi = new int[n]; + pi[0] = 0; + int q = 0; + for (int i = 1; i < n; i++) { + while (q > 0 && p.charAt(q) != p.charAt(i)) { + q = pi[q - 1]; + } + + if (p.charAt(q) == p.charAt(i)) { + q++; + } + + pi[i] = q; + } + return pi; + } +} diff --git a/src/main/java/com/thealgorithms/strings/LetterCombinationsOfPhoneNumber.java b/src/main/java/com/thealgorithms/strings/LetterCombinationsOfPhoneNumber.java new file mode 100644 index 000000000000..38c6bc13fa2a --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/LetterCombinationsOfPhoneNumber.java @@ -0,0 +1,65 @@ +package com.thealgorithms.strings; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class LetterCombinationsOfPhoneNumber { + + private static final char EMPTY = '\0'; + + // Mapping of numbers to corresponding letters on a phone keypad + private static final String[] KEYPAD = new String[] {" ", String.valueOf(EMPTY), "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; + + private LetterCombinationsOfPhoneNumber() { + } + + /** + * Generates a list of all possible letter combinations that the provided + * array of numbers could represent on a phone keypad. + * + * @param numbers an array of integers representing the phone numbers + * @return a list of possible letter combinations + */ + public static List<String> getCombinations(int[] numbers) { + if (numbers == null) { + return List.of(""); + } + return generateCombinations(numbers, 0, new StringBuilder()); + } + + /** + * Recursive method to generate combinations of letters from the phone keypad. + * + * @param numbers the input array of phone numbers + * @param index the current index in the numbers array being processed + * @param current a StringBuilder holding the current combination of letters + * @return a list of letter combinations formed from the given numbers + */ + private static List<String> generateCombinations(int[] numbers, int index, StringBuilder current) { + // Base case: if we've processed all numbers, return the current combination + if (index == numbers.length) { + return new ArrayList<>(Collections.singletonList(current.toString())); + } + + final var number = numbers[index]; + if (number < 0 || number > 9) { + throw new IllegalArgumentException("Input numbers must in the range [0, 9]"); + } + + List<String> combinations = new ArrayList<>(); + + // Iterate over each letter and recurse to generate further combinations + for (char letter : KEYPAD[number].toCharArray()) { + if (letter != EMPTY) { + current.append(letter); + } + combinations.addAll(generateCombinations(numbers, index + 1, current)); + if (letter != EMPTY) { + current.deleteCharAt(current.length() - 1); // Backtrack by removing the last appended letter + } + } + + return combinations; + } +} diff --git a/src/main/java/com/thealgorithms/strings/LongestCommonPrefix.java b/src/main/java/com/thealgorithms/strings/LongestCommonPrefix.java new file mode 100644 index 000000000000..0fabdaa2658b --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/LongestCommonPrefix.java @@ -0,0 +1,22 @@ +package com.thealgorithms.strings; + +import java.util.Arrays; + +public final class LongestCommonPrefix { + public String longestCommonPrefix(String[] strs) { + if (strs == null || strs.length == 0) { + return ""; + } + + Arrays.sort(strs); + String shortest = strs[0]; + String longest = strs[strs.length - 1]; + + int index = 0; + while (index < shortest.length() && index < longest.length() && shortest.charAt(index) == longest.charAt(index)) { + index++; + } + + return shortest.substring(0, index); + } +} diff --git a/src/main/java/com/thealgorithms/strings/LongestNonRepetitiveSubstring.java b/src/main/java/com/thealgorithms/strings/LongestNonRepetitiveSubstring.java new file mode 100644 index 000000000000..6808cd50602f --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/LongestNonRepetitiveSubstring.java @@ -0,0 +1,42 @@ +package com.thealgorithms.strings; + +import java.util.HashMap; +import java.util.Map; + +/** + * Class for finding the length of the longest substring without repeating characters. + */ +final class LongestNonRepetitiveSubstring { + private LongestNonRepetitiveSubstring() { + } + + /** + * Finds the length of the longest substring without repeating characters. + * + * @param s the input string + * @return the length of the longest non-repetitive substring + */ + public static int lengthOfLongestSubstring(String s) { + int maxLength = 0; + int start = 0; + Map<Character, Integer> charIndexMap = new HashMap<>(); + + for (int i = 0; i < s.length(); i++) { + char currentChar = s.charAt(i); + + // If the character is already in the map and its index is within the current window + if (charIndexMap.containsKey(currentChar) && charIndexMap.get(currentChar) >= start) { + // Move the start to the position right after the last occurrence of the current character + start = charIndexMap.get(currentChar) + 1; + } + + // Update the last seen index of the current character + charIndexMap.put(currentChar, i); + + // Calculate the maximum length of the substring without repeating characters + maxLength = Math.max(maxLength, i - start + 1); + } + + return maxLength; + } +} diff --git a/src/main/java/com/thealgorithms/strings/LongestPalindromicSubstring.java b/src/main/java/com/thealgorithms/strings/LongestPalindromicSubstring.java new file mode 100644 index 000000000000..ca500357ba77 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/LongestPalindromicSubstring.java @@ -0,0 +1,37 @@ +package com.thealgorithms.strings; + +final class LongestPalindromicSubstring { + private LongestPalindromicSubstring() { + } + + /** + * Finds the longest palindromic substring in the given string. + * + * @param s the input string + * @return the longest palindromic substring + */ + public static String longestPalindrome(String s) { + if (s == null || s.isEmpty()) { + return ""; + } + String maxStr = ""; + for (int i = 0; i < s.length(); ++i) { + for (int j = i; j < s.length(); ++j) { + if (isValid(s, i, j) && (j - i + 1 > maxStr.length())) { + maxStr = s.substring(i, j + 1); + } + } + } + return maxStr; + } + + private static boolean isValid(String s, int lo, int hi) { + int n = hi - lo + 1; + for (int i = 0; i < n / 2; ++i) { + if (s.charAt(lo + i) != s.charAt(hi - i)) { + return false; + } + } + return true; + } +} diff --git a/src/main/java/com/thealgorithms/strings/Lower.java b/src/main/java/com/thealgorithms/strings/Lower.java new file mode 100644 index 000000000000..e20cc5f0f2f7 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/Lower.java @@ -0,0 +1,32 @@ +package com.thealgorithms.strings; + +public final class Lower { + private Lower() { + } + + /** + * Driver Code + */ + public static void main(String[] args) { + String[] strings = {"ABC", "ABC123", "abcABC", "abc123ABC"}; + for (String s : strings) { + assert toLowerCase(s).equals(s.toLowerCase()); + } + } + + /** + * Converts all of the characters in this {@code String} to lower case + * + * @param s the string to convert + * @return the {@code String}, converted to lowercase. + */ + public static String toLowerCase(String s) { + char[] values = s.toCharArray(); + for (int i = 0; i < values.length; ++i) { + if (Character.isLetter(values[i]) && Character.isUpperCase(values[i])) { + values[i] = Character.toLowerCase(values[i]); + } + } + return new String(values); + } +} diff --git a/src/main/java/com/thealgorithms/strings/Manacher.java b/src/main/java/com/thealgorithms/strings/Manacher.java new file mode 100644 index 000000000000..34c303822bee --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/Manacher.java @@ -0,0 +1,69 @@ +package com.thealgorithms.strings; + +/** + * Wikipedia: https://en.wikipedia.org/wiki/Longest_palindromic_substring#Manacher's_algorithm + */ +public final class Manacher { + + private Manacher() { + } + + /** + * Finds the longest palindromic substring using Manacher's Algorithm + * + * @param s The input string + * @return The longest palindromic substring in {@code s} + */ + public static String longestPalindrome(String s) { + final String processedString = preprocess(s); + int[] palindromeLengths = new int[processedString.length()]; + int center = 0; + int rightBoundary = 0; + int maxLen = 0; + int centerIndex = 0; + + for (int i = 1; i < processedString.length() - 1; i++) { + int mirror = 2 * center - i; + + if (i < rightBoundary) { + palindromeLengths[i] = Math.min(rightBoundary - i, palindromeLengths[mirror]); + } + + while (processedString.charAt(i + 1 + palindromeLengths[i]) == processedString.charAt(i - 1 - palindromeLengths[i])) { + palindromeLengths[i]++; + } + + if (i + palindromeLengths[i] > rightBoundary) { + center = i; + rightBoundary = i + palindromeLengths[i]; + } + + if (palindromeLengths[i] > maxLen) { + maxLen = palindromeLengths[i]; + centerIndex = i; + } + } + + final int start = (centerIndex - maxLen) / 2; + return s.substring(start, start + maxLen); + } + + /** + * Preprocesses the input string by inserting a special character ('#') between each character + * and adding '^' at the start and '$' at the end to avoid boundary conditions. + * + * @param s The original string + * @return The preprocessed string with additional characters + */ + private static String preprocess(String s) { + if (s.isEmpty()) { + return "^$"; + } + StringBuilder sb = new StringBuilder("^"); + for (char c : s.toCharArray()) { + sb.append('#').append(c); + } + sb.append("#$"); + return sb.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/strings/MyAtoi.java b/src/main/java/com/thealgorithms/strings/MyAtoi.java new file mode 100644 index 000000000000..5a7c2ce53b1c --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/MyAtoi.java @@ -0,0 +1,65 @@ +package com.thealgorithms.strings; + +/** + * A utility class that provides a method to convert a string to a 32-bit signed integer (similar to C/C++'s atoi function). + */ +public final class MyAtoi { + private MyAtoi() { + } + + /** + * Converts the given string to a 32-bit signed integer. + * The conversion discards any leading whitespace characters until the first non-whitespace character is found. + * Then, it takes an optional initial plus or minus sign followed by as many numerical digits as possible and interprets them as a numerical value. + * The string can contain additional characters after those that form the integral number, which are ignored and have no effect on the behavior of this function. + * + * If the number is out of the range of a 32-bit signed integer: + * - Returns {@code Integer.MAX_VALUE} if the value exceeds {@code Integer.MAX_VALUE}. + * - Returns {@code Integer.MIN_VALUE} if the value is less than {@code Integer.MIN_VALUE}. + * + * If no valid conversion could be performed, a zero is returned. + * + * @param s the string to convert + * @return the converted integer, or 0 if the string cannot be converted to a valid integer + */ + public static int myAtoi(String s) { + if (s == null || s.isEmpty()) { + return 0; + } + + s = s.trim(); + int length = s.length(); + if (length == 0) { + return 0; + } + + int index = 0; + boolean negative = false; + + // Check for the sign + if (s.charAt(index) == '-' || s.charAt(index) == '+') { + negative = s.charAt(index) == '-'; + index++; + } + + int number = 0; + while (index < length) { + char ch = s.charAt(index); + if (!Character.isDigit(ch)) { + break; + } + + int digit = ch - '0'; + + // Check for overflow + if (number > (Integer.MAX_VALUE - digit) / 10) { + return negative ? Integer.MIN_VALUE : Integer.MAX_VALUE; + } + + number = number * 10 + digit; + index++; + } + + return negative ? -number : number; + } +} diff --git a/src/main/java/com/thealgorithms/strings/Palindrome.java b/src/main/java/com/thealgorithms/strings/Palindrome.java new file mode 100644 index 000000000000..3567a371d70e --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/Palindrome.java @@ -0,0 +1,58 @@ +package com.thealgorithms.strings; + +/** + * Wikipedia: https://en.wikipedia.org/wiki/Palindrome + */ +final class Palindrome { + private Palindrome() { + } + + /** + * Check if a string is palindrome string or not using String Builder + * + * @param s a string to check + * @return {@code true} if given string is palindrome, otherwise + * {@code false} + */ + public static boolean isPalindrome(String s) { + return ((s == null || s.length() <= 1) || s.equals(new StringBuilder(s).reverse().toString())); + } + + /** + * Check if a string is palindrome string or not using recursion + * + * @param s a string to check + * @return {@code true} if given string is palindrome, otherwise + * {@code false} + */ + public static boolean isPalindromeRecursion(String s) { + if (s == null || s.length() <= 1) { + return true; + } + + if (s.charAt(0) != s.charAt(s.length() - 1)) { + return false; + } + + return isPalindromeRecursion(s.substring(1, s.length() - 1)); + } + + /** + * Check if a string is palindrome string or not using two pointer technique + * + * @param s a string to check + * @return {@code true} if given string is palindrome, otherwise + * {@code false} + */ + public static boolean isPalindromeTwoPointer(String s) { + if (s == null || s.length() <= 1) { + return true; + } + for (int i = 0, j = s.length() - 1; i < j; ++i, --j) { + if (s.charAt(i) != s.charAt(j)) { + return false; + } + } + return true; + } +} diff --git a/src/main/java/com/thealgorithms/strings/Pangram.java b/src/main/java/com/thealgorithms/strings/Pangram.java new file mode 100644 index 000000000000..01307b28f6c6 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/Pangram.java @@ -0,0 +1,80 @@ +package com.thealgorithms.strings; + +import java.util.HashSet; + +/** + * Wikipedia: https://en.wikipedia.org/wiki/Pangram + */ +public final class Pangram { + private Pangram() { + } + + /** + * Test code + */ + public static void main(String[] args) { + assert isPangram("The quick brown fox jumps over the lazy dog"); + assert !isPangram("The quick brown fox jumps over the azy dog"); // L is missing + assert !isPangram("+-1234 This string is not alphabetical"); + assert !isPangram("\u0000/\\"); + } + + /** + * Checks if a String is considered a Pangram + * + * @param s The String to check + * @return {@code true} if s is a Pangram, otherwise {@code false} + */ + // alternative approach using Java Collection Framework + public static boolean isPangramUsingSet(String s) { + HashSet<Character> alpha = new HashSet<>(); + s = s.trim().toLowerCase(); + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) != ' ') { + alpha.add(s.charAt(i)); + } + } + return alpha.size() == 26; + } + + /** + * Checks if a String is considered a Pangram + * + * @param s The String to check + * @return {@code true} if s is a Pangram, otherwise {@code false} + */ + public static boolean isPangram(String s) { + boolean[] lettersExisting = new boolean[26]; + for (char c : s.toCharArray()) { + int letterIndex = c - (Character.isUpperCase(c) ? 'A' : 'a'); + if (letterIndex >= 0 && letterIndex < lettersExisting.length) { + lettersExisting[letterIndex] = true; + } + } + for (boolean letterFlag : lettersExisting) { + if (!letterFlag) { + return false; + } + } + return true; + } + + /** + * Checks if a String is Pangram or not by checking if each alhpabet is present or not + * + * @param s The String to check + * @return {@code true} if s is a Pangram, otherwise {@code false} + */ + public static boolean isPangram2(String s) { + if (s.length() < 26) { + return false; + } + s = s.toLowerCase(); // Converting s to Lower-Case + for (char i = 'a'; i <= 'z'; i++) { + if (s.indexOf(i) == -1) { + return false; // if any alphabet is not present, return false + } + } + return true; + } +} diff --git a/src/main/java/com/thealgorithms/strings/PermuteString.java b/src/main/java/com/thealgorithms/strings/PermuteString.java new file mode 100644 index 000000000000..124bdb6287b4 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/PermuteString.java @@ -0,0 +1,89 @@ +package com.thealgorithms.strings; + +import java.util.HashSet; +import java.util.Set; + +/** + * This class provides methods for generating all permutations of a given string using a backtracking algorithm. + * <p> + * The algorithm works as follows: + * <ol> + * <li>Fix a character in the current position and swap it with each of the remaining characters. + * For example, for the string "ABC": + * <ul> + * <li>Fix 'A' at the first position: permutations are "ABC", "BAC", "CBA" (obtained by swapping 'A' with 'B' and 'C' respectively).</li> + * </ul> + * </li> + * <li>Repeat the process for the next character. + * For instance, after fixing 'B' in the second position: + * <ul> + * <li>For "BAC", the permutations include "BAC" and "BCA" (after swapping 'A' and 'C').</li> + * </ul> + * </li> + * <li>After generating permutations for the current position, backtrack by swapping the characters back to their original positions to restore the state. + * For example, after generating permutations for "ABC", swap back to restore "BAC" and continue with further permutations.</li> + * <li>Repeat the process for all characters to get all possible permutations.</li> + * </ol> + * </p> + */ +public final class PermuteString { + private PermuteString() { + } + + /** + * Generates all possible permutations of the given string. + * + * <p>This method returns a set containing all unique permutations of the input string. It leverages + * a recursive helper method to generate these permutations. + * + * @param str The input string for which permutations are to be generated. + * If the string is null or empty, the result will be an empty set. + * @return A {@link Set} of strings containing all unique permutations of the input string. + * If the input string has duplicate characters, the set will ensure that only unique permutations + * are returned. + */ + public static Set<String> getPermutations(String str) { + Set<String> permutations = new HashSet<>(); + generatePermutations(str, 0, str.length(), permutations); + return permutations; + } + + /** + * Generates all permutations of the given string and collects them into a set. + * + * @param str the string to permute + * @param start the starting index for the current permutation + * @param end the end index (length of the string) + * @param permutations the set to collect all unique permutations + */ + private static void generatePermutations(String str, int start, int end, Set<String> permutations) { + if (start == end - 1) { + permutations.add(str); + } else { + for (int currentIndex = start; currentIndex < end; currentIndex++) { + // Swap the current character with the character at the start index + str = swapCharacters(str, start, currentIndex); + // Recursively generate permutations for the remaining characters + generatePermutations(str, start + 1, end, permutations); + // Backtrack: swap the characters back to their original positions + str = swapCharacters(str, start, currentIndex); + } + } + } + + /** + * Swaps the characters at the specified positions in the given string. + * + * @param str the string in which characters will be swapped + * @param i the position of the first character to swap + * @param j the position of the second character to swap + * @return a new string with the characters at positions i and j swapped + */ + private static String swapCharacters(String str, int i, int j) { + char[] chars = str.toCharArray(); + char temp = chars[i]; + chars[i] = chars[j]; + chars[j] = temp; + return new String(chars); + } +} diff --git a/src/main/java/com/thealgorithms/strings/RabinKarp.java b/src/main/java/com/thealgorithms/strings/RabinKarp.java new file mode 100644 index 000000000000..bb8df3358453 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/RabinKarp.java @@ -0,0 +1,82 @@ +package com.thealgorithms.strings; + +import java.util.Scanner; + +/** + * @author Prateek Kumar Oraon (https://github.com/prateekKrOraon) + * + An implementation of Rabin-Karp string matching algorithm + Program will simply end if there is no match + */ +public final class RabinKarp { + private RabinKarp() { + } + + public static Scanner scanner = null; + public static final int ALPHABET_SIZE = 256; + + public static void main(String[] args) { + scanner = new Scanner(System.in); + System.out.println("Enter String"); + String text = scanner.nextLine(); + System.out.println("Enter pattern"); + String pattern = scanner.nextLine(); + + int q = 101; + searchPat(text, pattern, q); + } + + private static void searchPat(String text, String pattern, int q) { + int m = pattern.length(); + int n = text.length(); + int t = 0; + int p = 0; + int h = 1; + int j = 0; + int i = 0; + + h = (int) Math.pow(ALPHABET_SIZE, m - 1) % q; + + for (i = 0; i < m; i++) { + // hash value is calculated for each character and then added with the hash value of the + // next character for pattern as well as the text for length equal to the length of + // pattern + p = (ALPHABET_SIZE * p + pattern.charAt(i)) % q; + t = (ALPHABET_SIZE * t + text.charAt(i)) % q; + } + + for (i = 0; i <= n - m; i++) { + // if the calculated hash value of the pattern and text matches then + // all the characters of the pattern is matched with the text of length equal to length + // of the pattern if all matches then pattern exist in string if not then the hash value + // of the first character of the text is subtracted and hash value of the next character + // after the end of the evaluated characters is added + if (p == t) { + // if hash value matches then the individual characters are matched + for (j = 0; j < m; j++) { + // if not matched then break out of the loop + if (text.charAt(i + j) != pattern.charAt(j)) { + break; + } + } + + // if all characters are matched then pattern exist in the string + if (j == m) { + System.out.println("Pattern found at index " + i); + } + } + + // if i<n-m then hash value of the first character of the text is subtracted and hash + // value of the next character after the end of the evaluated characters is added to get + // the hash value of the next window of characters in the text + if (i < n - m) { + t = (ALPHABET_SIZE * (t - text.charAt(i) * h) + text.charAt(i + m)) % q; + + // if hash value becomes less than zero than q is added to make it positive + if (t < 0) { + t = (t + q); + } + } + } + } +} diff --git a/src/main/java/com/thealgorithms/strings/ReturnSubsequence.java b/src/main/java/com/thealgorithms/strings/ReturnSubsequence.java new file mode 100644 index 000000000000..afa8c5f98678 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/ReturnSubsequence.java @@ -0,0 +1,37 @@ +package com.thealgorithms.strings; + +/** + * Class for generating all subsequences of a given string. + */ +public final class ReturnSubsequence { + private ReturnSubsequence() { + } + + /** + * Generates all subsequences of the given string. + * + * @param input The input string. + * @return An array of subsequences. + */ + public static String[] getSubsequences(String input) { + if (input.isEmpty()) { + return new String[] {""}; // Return array with an empty string if input is empty + } + + // Recursively find subsequences of the substring (excluding the first character) + String[] smallerSubsequences = getSubsequences(input.substring(1)); + + // Create an array to hold the final subsequences, double the size of smallerSubsequences + String[] result = new String[2 * smallerSubsequences.length]; + + // Copy the smaller subsequences directly to the result array + System.arraycopy(smallerSubsequences, 0, result, 0, smallerSubsequences.length); + + // Prepend the first character of the input string to each of the smaller subsequences + for (int i = 0; i < smallerSubsequences.length; i++) { + result[i + smallerSubsequences.length] = input.charAt(0) + smallerSubsequences[i]; + } + + return result; + } +} diff --git a/src/main/java/com/thealgorithms/strings/ReverseString.java b/src/main/java/com/thealgorithms/strings/ReverseString.java new file mode 100644 index 000000000000..54a9b779e828 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/ReverseString.java @@ -0,0 +1,60 @@ +package com.thealgorithms.strings; + +/** + * Reverse String using different version + */ +public final class ReverseString { + private ReverseString() { + } + + /** + * easiest way to reverses the string str and returns it + * + * @param str string to be reversed + * @return reversed string + */ + public static String reverse(String str) { + return new StringBuilder(str).reverse().toString(); + } + + /** + * second way to reverses the string str and returns it + * + * @param str string to be reversed + * @return reversed string + */ + public static String reverse2(String str) { + if (str == null || str.isEmpty()) { + return str; + } + + char[] value = str.toCharArray(); + for (int i = 0, j = str.length() - 1; i < j; i++, j--) { + char temp = value[i]; + value[i] = value[j]; + value[j] = temp; + } + return new String(value); + } + + /** + * Reverse version 3 the given string using a StringBuilder. + * This method converts the string to a character array, + * iterates through it in reverse order, and appends each character + * to a StringBuilder. + * + * @param string The input string to be reversed. + * @return The reversed string. + */ + public static String reverse3(String string) { + if (string.isEmpty()) { + return string; + } + char[] chars = string.toCharArray(); + StringBuilder sb = new StringBuilder(); + for (int i = string.length() - 1; i >= 0; i--) { + sb.append(chars[i]); + } + return sb.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/strings/ReverseStringRecursive.java b/src/main/java/com/thealgorithms/strings/ReverseStringRecursive.java new file mode 100644 index 000000000000..817b0a8ccd09 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/ReverseStringRecursive.java @@ -0,0 +1,21 @@ +package com.thealgorithms.strings; + +/** + * Reverse String using Recursion + */ +public final class ReverseStringRecursive { + private ReverseStringRecursive() { + } + + /** + * @param str string to be reversed + * @return reversed string + */ + public static String reverse(String str) { + if (str.isEmpty()) { + return str; + } else { + return reverse(str.substring(1)) + str.charAt(0); + } + } +} diff --git a/src/main/java/com/thealgorithms/strings/ReverseWordsInString.java b/src/main/java/com/thealgorithms/strings/ReverseWordsInString.java new file mode 100644 index 000000000000..7c22cd8a5df2 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/ReverseWordsInString.java @@ -0,0 +1,21 @@ +package com.thealgorithms.strings; + +import java.util.Arrays; +import java.util.Collections; + +public final class ReverseWordsInString { + + private ReverseWordsInString() { + } + + /** + * @brief Reverses words in the input string + * @param s the input string + * @return A string created by reversing the order of the words in {@code s} + */ + public static String reverseWordsInString(final String s) { + var words = s.trim().split("\\s+"); + Collections.reverse(Arrays.asList(words)); + return String.join(" ", words); + } +} diff --git a/src/main/java/com/thealgorithms/strings/Rotation.java b/src/main/java/com/thealgorithms/strings/Rotation.java new file mode 100644 index 000000000000..e1352f1197b1 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/Rotation.java @@ -0,0 +1,62 @@ +package com.thealgorithms.strings; + +/** + * Given a string, moving several characters in front of the string to the end + * of the string. For example, move the two characters'a' and 'b' in front of + * the string "abcdef" to the end of the string, so that the original string + * becomes the string "cdefab" + */ +public final class Rotation { + private Rotation() { + } + + public static void main(String[] args) { + assert rotation("abcdef", 2).equals("cdefab"); + + char[] values = "abcdef".toCharArray(); + rotation(values, 2); + assert new String(values).equals("cdefab"); + } + + /** + * Move {@code n} characters in front of given string to the end of string + * time complexity: O(n) space complexity: O(n) + * + * @param s given string + * @param n the total characters to be moved + * @return string after rotation + */ + public static String rotation(String s, int n) { + return s.substring(n) + s.substring(0, n); + } + + /** + * Move {@code n} characters in front of given character array to the end of + * array time complexity: O(n) space complexity: O(1) + * + * @param values given character array + * @param n the total characters to be moved + */ + public static void rotation(char[] values, int n) { + reverse(values, 0, n - 1); + reverse(values, n, values.length - 1); + reverse(values, 0, values.length - 1); + } + + /** + * Reverse character array + * + * @param values character array + * @param from begin index of given array + * @param to end index of given array + */ + public static void reverse(char[] values, int from, int to) { + while (from < to) { + char temp = values[from]; + values[from] = values[to]; + values[to] = temp; + from++; + to--; + } + } +} diff --git a/src/main/java/com/thealgorithms/strings/StringCompression.java b/src/main/java/com/thealgorithms/strings/StringCompression.java new file mode 100644 index 000000000000..85bc0e4db641 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/StringCompression.java @@ -0,0 +1,60 @@ +package com.thealgorithms.strings; + +/** + * References : https://en.wikipedia.org/wiki/Run-length_encoding + * String compression algorithm deals with encoding the string, that is, shortening the size of the string + * @author Swarga-codes (https://github.com/Swarga-codes) + */ +public final class StringCompression { + private StringCompression() { + } + /** + * Returns the compressed or encoded string + * + * @param input character array that contains the group of characters to be encoded + * @return the compressed character array as string + */ + public static String compress(String input) { + // Keeping the count as 1 since every element present will have at least a count of 1 + int count = 1; + String compressedString = ""; + // Base condition to check whether the array is of size 1, if it is then we return the array + if (input.length() == 1) { + return "" + input.charAt(0); + } + // If the array has a length greater than 1 we move into this loop + for (int i = 0; i < input.length() - 1; i++) { + // here we check for similarity of the adjacent elements and change the count accordingly + if (input.charAt(i) == input.charAt(i + 1)) { + count = count + 1; + } + if ((i + 1) == input.length() - 1 && input.charAt(i + 1) == input.charAt(i)) { + compressedString = appendCount(compressedString, count, input.charAt(i)); + break; + } else if (input.charAt(i) != input.charAt(i + 1)) { + if ((i + 1) == input.length() - 1) { + compressedString = appendCount(compressedString, count, input.charAt(i)) + input.charAt(i + 1); + break; + } else { + compressedString = appendCount(compressedString, count, input.charAt(i)); + count = 1; + } + } + } + return compressedString; + } + /** + * @param res the resulting string + * @param count current count + * @param ch the character at a particular index + * @return the res string appended with the count + */ + public static String appendCount(String res, int count, char ch) { + if (count > 1) { + res += ch + "" + count; + } else { + res += ch + ""; + } + return res; + } +} diff --git a/src/main/java/com/thealgorithms/strings/StringMatchFiniteAutomata.java b/src/main/java/com/thealgorithms/strings/StringMatchFiniteAutomata.java new file mode 100644 index 000000000000..719898a1fd74 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/StringMatchFiniteAutomata.java @@ -0,0 +1,125 @@ +package com.thealgorithms.strings; + +import java.util.Set; +import java.util.TreeSet; + +/** + * A class to perform string matching using <a href="https://en.wikipedia.org/wiki/Finite-state_machine">finite automata</a>. + * + * @author <a href="https://github.com/prateekKrOraon">Prateek Kumar Oraon</a> + */ +public final class StringMatchFiniteAutomata { + + // Constants + private static final int CHARS = Character.MAX_VALUE + 1; // Total number of characters in the input alphabet + + // Private constructor to prevent instantiation + private StringMatchFiniteAutomata() { + } + + /** + * Searches for the pattern in the given text using finite automata. + * + * @param text The text to search within. + * @param pattern The pattern to search for. + */ + public static Set<Integer> searchPattern(final String text, final String pattern) { + final var stateTransitionTable = computeStateTransitionTable(pattern); + FiniteAutomata finiteAutomata = new FiniteAutomata(stateTransitionTable); + + Set<Integer> indexFound = new TreeSet<>(); + for (int i = 0; i < text.length(); i++) { + finiteAutomata.consume(text.charAt(i)); + + if (finiteAutomata.getState() == pattern.length()) { + indexFound.add(i - pattern.length() + 1); + } + } + return indexFound; + } + + /** + * Computes the finite automata table for the given pattern. + * + * @param pattern The pattern to preprocess. + * @return The state transition table. + */ + private static int[][] computeStateTransitionTable(final String pattern) { + final int patternLength = pattern.length(); + int[][] stateTransitionTable = new int[patternLength + 1][CHARS]; + + for (int state = 0; state <= patternLength; ++state) { + for (int x = 0; x < CHARS; ++x) { + stateTransitionTable[state][x] = getNextState(pattern, patternLength, state, x); + } + } + + return stateTransitionTable; + } + + /** + * Gets the next state for the finite automata. + * + * @param pattern The pattern being matched. + * @param patternLength The length of the pattern. + * @param state The current state. + * @param x The current character from the input alphabet. + * @return The next state. + */ + private static int getNextState(final String pattern, final int patternLength, final int state, final int x) { + // If the current state is less than the length of the pattern + // and the character matches the pattern character, go to the next state + if (state < patternLength && x == pattern.charAt(state)) { + return state + 1; + } + + // Check for the highest prefix which is also a suffix + for (int ns = state; ns > 0; ns--) { + if (pattern.charAt(ns - 1) == x) { + boolean match = true; + for (int i = 0; i < ns - 1; i++) { + if (pattern.charAt(i) != pattern.charAt(state - ns + i + 1)) { + match = false; + break; + } + } + if (match) { + return ns; + } + } + } + + // If no prefix which is also a suffix is found, return 0 + return 0; + } + + /** + * A class representing the finite automata for pattern matching. + */ + private static final class FiniteAutomata { + private int state = 0; + private final int[][] stateTransitionTable; + + private FiniteAutomata(int[][] stateTransitionTable) { + this.stateTransitionTable = stateTransitionTable; + } + + /** + * Consumes an input character and transitions to the next state. + * + * @param input The input character. + */ + private void consume(final char input) { + state = stateTransitionTable[state][input]; + } + + /** + * Gets the current state of the finite automata. + * + * @return The current state. + */ + private int getState() { + return state; + } + } +} diff --git a/src/main/java/com/thealgorithms/strings/Upper.java b/src/main/java/com/thealgorithms/strings/Upper.java new file mode 100644 index 000000000000..5e248cb6ee39 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/Upper.java @@ -0,0 +1,39 @@ +package com.thealgorithms.strings; + +public final class Upper { + private Upper() { + } + + /** + * Driver Code + */ + public static void main(String[] args) { + String[] strings = {"ABC", "ABC123", "abcABC", "abc123ABC"}; + for (String s : strings) { + assert toUpperCase(s).equals(s.toUpperCase()); + } + } + + /** + * Converts all the characters in this {@code String} to upper case + * + * @param s the string to convert + * @return the {@code String}, converted to uppercase. + */ + public static String toUpperCase(String s) { + if (s == null) { + throw new IllegalArgumentException("Input string connot be null"); + } + if (s.isEmpty()) { + return s; + } + StringBuilder result = new StringBuilder(s); + for (int i = 0; i < result.length(); ++i) { + char currentChar = result.charAt(i); + if (Character.isLetter(currentChar) && Character.isLowerCase(currentChar)) { + result.setCharAt(i, Character.toUpperCase(currentChar)); + } + } + return result.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/strings/ValidParentheses.java b/src/main/java/com/thealgorithms/strings/ValidParentheses.java new file mode 100644 index 000000000000..629fee495d84 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/ValidParentheses.java @@ -0,0 +1,59 @@ +package com.thealgorithms.strings; +// Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine +// if the input string is valid. An input string is valid if: Open brackets must be closed by +// the same type of brackets. Open brackets must be closed in the correct order. Every close +// bracket has a corresponding open bracket of the same type. + +public final class ValidParentheses { + private ValidParentheses() { + } + public static boolean isValid(String s) { + char[] stack = new char[s.length()]; + int head = 0; + for (char c : s.toCharArray()) { + switch (c) { + case '{': + case '[': + case '(': + stack[head++] = c; + break; + case '}': + if (head == 0 || stack[--head] != '{') { + return false; + } + break; + case ')': + if (head == 0 || stack[--head] != '(') { + return false; + } + break; + case ']': + if (head == 0 || stack[--head] != '[') { + return false; + } + break; + default: + throw new IllegalArgumentException("Unexpected character: " + c); + } + } + return head == 0; + } + public static boolean isValidParentheses(String s) { + int i = -1; + char[] stack = new char[s.length()]; + String openBrackets = "({["; + String closeBrackets = ")}]"; + for (char ch : s.toCharArray()) { + if (openBrackets.indexOf(ch) != -1) { + stack[++i] = ch; + } else { + if (i >= 0 && openBrackets.indexOf(stack[i]) == closeBrackets.indexOf(ch)) { + i--; + } else { + return false; + } + } + } + return i == -1; + } +} diff --git a/src/main/java/com/thealgorithms/strings/WordLadder.java b/src/main/java/com/thealgorithms/strings/WordLadder.java new file mode 100644 index 000000000000..665e5ff3220d --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/WordLadder.java @@ -0,0 +1,64 @@ +package com.thealgorithms.strings; + +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Queue; +import java.util.Set; + +/** + * Class to find the shortest transformation sequence from a beginWord to an endWord using a dictionary of words. + * A transformation sequence is a sequence of words where each adjacent pair differs by exactly one letter. + */ +public final class WordLadder { + private WordLadder() { + } + + /** + * Finds the shortest transformation sequence from beginWord to endWord. + * + * @param beginWord the starting word of the transformation sequence + * @param endWord the target word of the transformation sequence + * @param wordList a list of words that can be used in the transformation sequence + * @return the number of words in the shortest transformation sequence, or 0 if no such sequence exists + */ + public static int ladderLength(String beginWord, String endWord, Collection<String> wordList) { + Set<String> wordSet = new HashSet<>(wordList); + + if (!wordSet.contains(endWord)) { + return 0; + } + + Queue<String> queue = new LinkedList<>(); + queue.offer(beginWord); + int level = 1; + + while (!queue.isEmpty()) { + int size = queue.size(); + for (int i = 0; i < size; i++) { + String currentWord = queue.poll(); + char[] currentWordChars = currentWord.toCharArray(); + for (int j = 0; j < currentWordChars.length; j++) { + char originalChar = currentWordChars[j]; + for (char c = 'a'; c <= 'z'; c++) { + if (currentWordChars[j] == c) { + continue; + } + currentWordChars[j] = c; + String newWord = new String(currentWordChars); + + if (newWord.equals(endWord)) { + return level + 1; + } + if (wordSet.remove(newWord)) { + queue.offer(newWord); + } + } + currentWordChars[j] = originalChar; + } + } + level++; + } + return 0; + } +} diff --git a/src/main/java/com/thealgorithms/strings/zigZagPattern/README.md b/src/main/java/com/thealgorithms/strings/zigZagPattern/README.md new file mode 100644 index 000000000000..17e5ba6e26d8 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/zigZagPattern/README.md @@ -0,0 +1,36 @@ +# About + +convert string into a zig-zagged string + +for example : + + string = "123456789" , numRows = 3 + ans = "159246837" + explanation + 1 5 9 + 2 4 6 8 + 3 7 + + string = "HelloWorldKotlin" , k = 4 + ans = "HoteWrollolKildn" + explanation + H o t + e W r o l + l o l K i + l d n + +# working + + if string size is smaller than numRows or numRows is smaller than 2 + than we can return string because it will make no changes to string. + If not than + we initiate three variable depth which is equalvalent to numRows , + height which starts with 1 and start with starting index of string. + than we generate spaces to skip using formula 2 + (( n - 1 ) * 2 ) + for both height and depth + with each iteration we decrement depth and increate height and start + by one also we keep contantating character on to new string with first + depth spaces and later height spaces that we generated using formula + if not zero + +# beats 80% of submission on leetcode \ No newline at end of file diff --git a/src/main/java/com/thealgorithms/strings/zigZagPattern/ZigZagPattern.java b/src/main/java/com/thealgorithms/strings/zigZagPattern/ZigZagPattern.java new file mode 100644 index 000000000000..ad7835bdbb97 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/zigZagPattern/ZigZagPattern.java @@ -0,0 +1,38 @@ +package com.thealgorithms.strings.zigZagPattern; + +final class ZigZagPattern { + + private ZigZagPattern() { + } + + /** + * Encodes a given string into a zig-zag pattern. + * + * @param s the input string to be encoded + * @param numRows the number of rows in the zigzag pattern + * @return the encoded string in zigzag pattern format + */ + public static String encode(String s, int numRows) { + if (numRows < 2 || s.length() < numRows) { + return s; + } + + StringBuilder result = new StringBuilder(s.length()); + int cycleLength = 2 * numRows - 2; + + for (int row = 0; row < numRows; row++) { + for (int j = row; j < s.length(); j += cycleLength) { + result.append(s.charAt(j)); + + if (row > 0 && row < numRows - 1) { + int diagonal = j + cycleLength - 2 * row; + if (diagonal < s.length()) { + result.append(s.charAt(diagonal)); + } + } + } + } + + return result.toString(); + } +} diff --git a/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java b/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java new file mode 100644 index 000000000000..236a23205180 --- /dev/null +++ b/src/main/java/com/thealgorithms/tree/HeavyLightDecomposition.java @@ -0,0 +1,157 @@ +package com.thealgorithms.tree; + +import java.util.ArrayList; +import java.util.List; + +/** + * Heavy-Light Decomposition (HLD) implementation in Java. + * HLD is used to efficiently handle path queries on trees, such as maximum, + * sum, or updates. It decomposes the tree into heavy and light chains, + * enabling queries in O(log N) time. + * Wikipedia Reference: https://en.wikipedia.org/wiki/Heavy-light_decomposition + * Author: Nithin U. + * Github: https://github.com/NithinU2802 + */ + +public class HeavyLightDecomposition { + private List<List<Integer>> tree; + private int[] parent; + private int[] depth; + private int[] subtreeSize; + private int[] chainHead; + private int[] position; + private int[] nodeValue; + private int[] segmentTree; + private int positionIndex; + + public HeavyLightDecomposition(int n) { + tree = new ArrayList<>(); + for (int i = 0; i <= n; i++) { + tree.add(new ArrayList<>()); + } + parent = new int[n + 1]; + depth = new int[n + 1]; + subtreeSize = new int[n + 1]; + chainHead = new int[n + 1]; + position = new int[n + 1]; + nodeValue = new int[n + 1]; + segmentTree = new int[4 * (n + 1)]; + for (int i = 0; i <= n; i++) { + chainHead[i] = -1; + } + positionIndex = 0; + } + + public int getPosition(int index) { + return position[index]; + } + + public int getPositionIndex() { + return positionIndex; + } + + public void addEdge(int u, int v) { + tree.get(u).add(v); + tree.get(v).add(u); + } + + private void dfsSize(int node, int parentNode) { + parent[node] = parentNode; + subtreeSize[node] = 1; + for (int child : tree.get(node)) { + if (child != parentNode) { + depth[child] = depth[node] + 1; + dfsSize(child, node); + subtreeSize[node] += subtreeSize[child]; + } + } + } + + private void decompose(int node, int head) { + chainHead[node] = head; + position[node] = positionIndex++; + int heavyChild = -1; + int maxSubtreeSize = -1; + for (int child : tree.get(node)) { + if (child != parent[node] && subtreeSize[child] > maxSubtreeSize) { + heavyChild = child; + maxSubtreeSize = subtreeSize[child]; + } + } + if (heavyChild != -1) { + decompose(heavyChild, head); + } + for (int child : tree.get(node)) { + if (child != parent[node] && child != heavyChild) { + decompose(child, child); + } + } + } + + private void buildSegmentTree(int node, int start, int end) { + if (start == end) { + segmentTree[node] = nodeValue[start]; + return; + } + int mid = (start + end) / 2; + buildSegmentTree(2 * node, start, mid); + buildSegmentTree(2 * node + 1, mid + 1, end); + segmentTree[node] = Math.max(segmentTree[2 * node], segmentTree[2 * node + 1]); + } + + public void updateSegmentTree(int node, int start, int end, int index, int value) { + if (start == end) { + segmentTree[node] = value; + return; + } + int mid = (start + end) / 2; + if (index <= mid) { + updateSegmentTree(2 * node, start, mid, index, value); + } else { + updateSegmentTree(2 * node + 1, mid + 1, end, index, value); + } + segmentTree[node] = Math.max(segmentTree[2 * node], segmentTree[2 * node + 1]); + } + + public int querySegmentTree(int node, int start, int end, int left, int right) { + if (left > end || right < start) { + return Integer.MIN_VALUE; + } + if (left <= start && end <= right) { + return segmentTree[node]; + } + int mid = (start + end) / 2; + int leftQuery = querySegmentTree(2 * node, start, mid, left, right); + int rightQuery = querySegmentTree(2 * node + 1, mid + 1, end, left, right); + return Math.max(leftQuery, rightQuery); + } + + public int queryMaxInPath(int u, int v) { + int result = Integer.MIN_VALUE; + while (chainHead[u] != chainHead[v]) { + if (depth[chainHead[u]] < depth[chainHead[v]]) { + int temp = u; + u = v; + v = temp; + } + result = Math.max(result, querySegmentTree(1, 0, positionIndex - 1, position[chainHead[u]], position[u])); + u = parent[chainHead[u]]; + } + if (depth[u] > depth[v]) { + int temp = u; + u = v; + v = temp; + } + result = Math.max(result, querySegmentTree(1, 0, positionIndex - 1, position[u], position[v])); + return result; + } + + public void initialize(int root, int[] values) { + dfsSize(root, -1); + decompose(root, root); + for (int i = 0; i < values.length; i++) { + nodeValue[position[i]] = values[i]; + } + buildSegmentTree(1, 0, positionIndex - 1); + } +} diff --git a/src/test/java/com/thealgorithms/audiofilters/EMAFilterTest.java b/src/test/java/com/thealgorithms/audiofilters/EMAFilterTest.java new file mode 100644 index 000000000000..f2338d3d8296 --- /dev/null +++ b/src/test/java/com/thealgorithms/audiofilters/EMAFilterTest.java @@ -0,0 +1,41 @@ +package com.thealgorithms.audiofilters; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +public class EMAFilterTest { + + @Test + public void testApplyBasicSignal() { + EMAFilter emaFilter = new EMAFilter(0.2); + double[] audioSignal = {0.1, 0.5, 0.8, 0.6, 0.3, 0.9, 0.4}; + double[] expectedOutput = {0.1, 0.18, 0.304, 0.3632, 0.35056, 0.460448, 0.4483584}; + double[] result = emaFilter.apply(audioSignal); + assertArrayEquals(expectedOutput, result, 1e-5); + } + + @Test + public void testApplyEmptySignal() { + EMAFilter emaFilter = new EMAFilter(0.2); + double[] audioSignal = {}; + double[] expectedOutput = {}; + double[] result = emaFilter.apply(audioSignal); + assertArrayEquals(expectedOutput, result); + } + + @Test + public void testAlphaBounds() { + EMAFilter emaFilterMin = new EMAFilter(0.01); + EMAFilter emaFilterMax = new EMAFilter(1.0); + double[] audioSignal = {1.0, 1.0, 1.0, 1.0}; + + // Minimal smoothing (alpha close to 0) + double[] resultMin = emaFilterMin.apply(audioSignal); + assertArrayEquals(audioSignal, resultMin, 1e-5); + + // Maximum smoothing (alpha = 1, output should match input) + double[] resultMax = emaFilterMax.apply(audioSignal); + assertArrayEquals(audioSignal, resultMax, 1e-5); + } +} diff --git a/src/test/java/com/thealgorithms/audiofilters/IIRFilterTest.java b/src/test/java/com/thealgorithms/audiofilters/IIRFilterTest.java new file mode 100644 index 000000000000..66d7d60c501b --- /dev/null +++ b/src/test/java/com/thealgorithms/audiofilters/IIRFilterTest.java @@ -0,0 +1,83 @@ +package com.thealgorithms.audiofilters; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class IIRFilterTest { + + @Test + void testConstructorValidOrder() { + // Test a valid filter creation + IIRFilter filter = new IIRFilter(2); + assertNotNull(filter, "Filter should be instantiated correctly"); + } + + @Test + void testConstructorInvalidOrder() { + // Test an invalid filter creation (order <= 0) + assertThrows(IllegalArgumentException.class, () -> { new IIRFilter(0); }, "Order must be greater than zero"); + } + + @Test + void testSetCoeffsInvalidLengthA() { + IIRFilter filter = new IIRFilter(2); + + // Invalid 'aCoeffs' length + double[] aCoeffs = {1.0}; // too short + double[] bCoeffs = {1.0, 0.5}; + assertThrows(IllegalArgumentException.class, () -> { filter.setCoeffs(aCoeffs, bCoeffs); }, "aCoeffs must be of size 2"); + } + + @Test + void testSetCoeffsInvalidLengthB() { + IIRFilter filter = new IIRFilter(2); + + // Invalid 'bCoeffs' length + double[] aCoeffs = {1.0, 0.5}; + double[] bCoeffs = {1.0}; // too short + assertThrows(IllegalArgumentException.class, () -> { filter.setCoeffs(aCoeffs, bCoeffs); }, "bCoeffs must be of size 2"); + } + + @Test + void testSetCoeffsInvalidACoeffZero() { + IIRFilter filter = new IIRFilter(2); + + // Invalid 'aCoeffs' where aCoeffs[0] == 0.0 + double[] aCoeffs = {0.0, 0.5}; // aCoeffs[0] must not be zero + double[] bCoeffs = {1.0, 0.5}; + assertThrows(IllegalArgumentException.class, () -> { filter.setCoeffs(aCoeffs, bCoeffs); }, "aCoeffs[0] must not be zero"); + } + + @Test + void testProcessWithNoCoeffsSet() { + // Test process method with default coefficients (sane defaults) + IIRFilter filter = new IIRFilter(2); + double inputSample = 0.5; + double result = filter.process(inputSample); + + // Since default coeffsA[0] and coeffsB[0] are 1.0, expect output = input + assertEquals(inputSample, result, 1e-6, "Process should return the same value as input with default coefficients"); + } + + @Test + void testProcessWithCoeffsSet() { + // Test process method with set coefficients + IIRFilter filter = new IIRFilter(2); + + double[] aCoeffs = {1.0, 0.5}; + double[] bCoeffs = {1.0, 0.5}; + filter.setCoeffs(aCoeffs, bCoeffs); + + // Process a sample + double inputSample = 0.5; + double result = filter.process(inputSample); + + // Expected output can be complex to calculate in advance; + // check if the method runs and returns a result within reasonable bounds + assertTrue(result >= -1.0 && result <= 1.0, "Processed result should be in the range [-1, 1]"); + } +} diff --git a/src/test/java/com/thealgorithms/backtracking/AllPathsFromSourceToTargetTest.java b/src/test/java/com/thealgorithms/backtracking/AllPathsFromSourceToTargetTest.java new file mode 100644 index 000000000000..177163b09ca1 --- /dev/null +++ b/src/test/java/com/thealgorithms/backtracking/AllPathsFromSourceToTargetTest.java @@ -0,0 +1,57 @@ +package com.thealgorithms.backtracking; + +import static org.junit.jupiter.api.Assertions.assertIterableEquals; + +import java.util.List; +import org.junit.jupiter.api.Test; + +public class AllPathsFromSourceToTargetTest { + + @Test + void testForFirstCase() { + int vertices = 4; + int[][] a = {{0, 1}, {0, 2}, {0, 3}, {2, 0}, {2, 1}, {1, 3}}; + int source = 2; + int destination = 3; + List<List<Integer>> list2 = List.of(List.of(2, 0, 1, 3), List.of(2, 0, 3), List.of(2, 1, 3)); + List<List<Integer>> list1 = AllPathsFromSourceToTarget.allPathsFromSourceToTarget(vertices, a, source, destination); + list2 = list1; + assertIterableEquals(list1, list2); + } + + @Test + void testForSecondCase() { + int vertices = 5; + int[][] a = {{0, 1}, {0, 2}, {0, 3}, {2, 0}, {2, 1}, {1, 3}, {1, 4}, {3, 4}, {2, 4}}; + int source = 0; + int destination = 4; + List<List<Integer>> list2 = List.of(List.of(0, 1, 3, 4), List.of(0, 1, 4), List.of(0, 2, 1, 3, 4), List.of(0, 2, 1, 4), List.of(0, 2, 4), List.of(0, 3, 4)); + List<List<Integer>> list1 = AllPathsFromSourceToTarget.allPathsFromSourceToTarget(vertices, a, source, destination); + list2 = list1; + assertIterableEquals(list1, list2); + } + + @Test + void testForThirdCase() { + int vertices = 6; + int[][] a = {{1, 0}, {2, 3}, {0, 4}, {1, 5}, {4, 3}, {0, 2}, {0, 3}, {1, 2}, {0, 5}, {3, 4}, {2, 5}, {2, 4}}; + int source = 1; + int destination = 5; + List<List<Integer>> list2 = List.of(List.of(1, 0, 2, 5), List.of(1, 0, 5), List.of(1, 5), List.of(1, 2, 5)); + List<List<Integer>> list1 = AllPathsFromSourceToTarget.allPathsFromSourceToTarget(vertices, a, source, destination); + list2 = list1; + assertIterableEquals(list1, list2); + } + + @Test + void testForFourthcase() { + int vertices = 3; + int[][] a = {{0, 1}, {0, 2}, {1, 2}}; + int source = 0; + int destination = 2; + List<List<Integer>> list2 = List.of(List.of(0, 1, 2), List.of(0, 2)); + List<List<Integer>> list1 = AllPathsFromSourceToTarget.allPathsFromSourceToTarget(vertices, a, source, destination); + list2 = list1; + assertIterableEquals(list1, list2); + } +} diff --git a/src/test/java/com/thealgorithms/backtracking/ArrayCombinationTest.java b/src/test/java/com/thealgorithms/backtracking/ArrayCombinationTest.java new file mode 100644 index 000000000000..a6a3714cb594 --- /dev/null +++ b/src/test/java/com/thealgorithms/backtracking/ArrayCombinationTest.java @@ -0,0 +1,40 @@ +package com.thealgorithms.backtracking; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.thealgorithms.maths.BinomialCoefficient; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class ArrayCombinationTest { + @ParameterizedTest + @MethodSource("regularInputs") + void testCombination(int n, int k, List<List<Integer>> expected) { + assertEquals(expected.size(), BinomialCoefficient.binomialCoefficient(n, k)); + assertEquals(expected, ArrayCombination.combination(n, k)); + } + + @ParameterizedTest + @MethodSource("wrongInputs") + void testCombinationThrows(int n, int k) { + assertThrows(IllegalArgumentException.class, () -> ArrayCombination.combination(n, k)); + } + + private static Stream<Arguments> regularInputs() { + return Stream.of(Arguments.of(0, 0, List.of(new ArrayList<Integer>())), Arguments.of(1, 0, List.of(new ArrayList<Integer>())), Arguments.of(1, 1, List.of(List.of(0))), Arguments.of(3, 0, List.of(new ArrayList<Integer>())), Arguments.of(3, 1, List.of(List.of(0), List.of(1), List.of(2))), + Arguments.of(4, 2, List.of(List.of(0, 1), List.of(0, 2), List.of(0, 3), List.of(1, 2), List.of(1, 3), List.of(2, 3))), + Arguments.of(5, 3, List.of(List.of(0, 1, 2), List.of(0, 1, 3), List.of(0, 1, 4), List.of(0, 2, 3), List.of(0, 2, 4), List.of(0, 3, 4), List.of(1, 2, 3), List.of(1, 2, 4), List.of(1, 3, 4), List.of(2, 3, 4))), + Arguments.of(6, 4, + List.of(List.of(0, 1, 2, 3), List.of(0, 1, 2, 4), List.of(0, 1, 2, 5), List.of(0, 1, 3, 4), List.of(0, 1, 3, 5), List.of(0, 1, 4, 5), List.of(0, 2, 3, 4), List.of(0, 2, 3, 5), List.of(0, 2, 4, 5), List.of(0, 3, 4, 5), List.of(1, 2, 3, 4), List.of(1, 2, 3, 5), List.of(1, 2, 4, 5), + List.of(1, 3, 4, 5), List.of(2, 3, 4, 5)))); + } + + private static Stream<Arguments> wrongInputs() { + return Stream.of(Arguments.of(-1, 0), Arguments.of(0, -1), Arguments.of(2, 100), Arguments.of(3, 4)); + } +} diff --git a/src/test/java/com/thealgorithms/backtracking/CombinationTest.java b/src/test/java/com/thealgorithms/backtracking/CombinationTest.java new file mode 100644 index 000000000000..a9d1163f3ecd --- /dev/null +++ b/src/test/java/com/thealgorithms/backtracking/CombinationTest.java @@ -0,0 +1,51 @@ +package com.thealgorithms.backtracking; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import org.junit.jupiter.api.Test; + +public class CombinationTest { + + @Test + void testNegativeElement() { + Integer[] array = {1, 2}; + assertThrows(IllegalArgumentException.class, () -> { Combination.combination(array, -1); }); + } + + @Test + void testNoElement() { + List<TreeSet<Integer>> result = Combination.combination(new Integer[] {1, 2}, 0); + assertNotNull(result); + assertEquals(0, result.size()); + } + + @Test + void testLengthOne() { + List<TreeSet<Integer>> result = Combination.combination(new Integer[] {1, 2}, 1); + assertTrue(result.get(0).iterator().next() == 1); + assertTrue(result.get(1).iterator().next() == 2); + } + + @Test + void testLengthTwo() { + List<TreeSet<Integer>> result = Combination.combination(new Integer[] {1, 2}, 2); + Integer[] arr = result.get(0).toArray(new Integer[2]); + assertTrue(arr[0] == 1); + assertTrue(arr[1] == 2); + } + + @Test + void testCombinationsWithStrings() { + List<TreeSet<String>> result = Combination.combination(new String[] {"a", "b", "c"}, 2); + assertEquals(3, result.size()); + assertTrue(result.contains(new TreeSet<>(Set.of("a", "b")))); + assertTrue(result.contains(new TreeSet<>(Set.of("a", "c")))); + assertTrue(result.contains(new TreeSet<>(Set.of("b", "c")))); + } +} diff --git a/src/test/java/com/thealgorithms/backtracking/CrosswordSolverTest.java b/src/test/java/com/thealgorithms/backtracking/CrosswordSolverTest.java new file mode 100644 index 000000000000..542fd53fe5ed --- /dev/null +++ b/src/test/java/com/thealgorithms/backtracking/CrosswordSolverTest.java @@ -0,0 +1,66 @@ +package com.thealgorithms.backtracking; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class CrosswordSolverTest { + + @Test + public void testValidPlacement() { + char[][] puzzle = {{' ', ' ', ' '}, {' ', ' ', ' '}, {' ', ' ', ' '}}; + assertTrue(CrosswordSolver.isValid(puzzle, "cat", 0, 0, true)); + assertTrue(CrosswordSolver.isValid(puzzle, "dog", 0, 0, false)); + assertFalse(CrosswordSolver.isValid(puzzle, "cat", 1, 2, false)); + } + + @Test + public void testPlaceAndRemoveWord() { + char[][] puzzle = {{' ', ' ', ' '}, {' ', ' ', ' '}, {' ', ' ', ' '}}; + CrosswordSolver.placeWord(puzzle, "cat", 0, 0, true); + assertEquals('c', puzzle[0][0]); + assertEquals('a', puzzle[1][0]); + assertEquals('t', puzzle[2][0]); + + CrosswordSolver.removeWord(puzzle, "cat", 0, 0, true); + assertEquals(' ', puzzle[0][0]); + assertEquals(' ', puzzle[1][0]); + assertEquals(' ', puzzle[2][0]); + } + + @Test + public void testSolveCrossword() { + char[][] puzzle = {{' ', ' ', ' '}, {' ', ' ', ' '}, {' ', ' ', ' '}}; + List<String> words = Arrays.asList("cat", "dog", "car"); + assertTrue(CrosswordSolver.solveCrossword(puzzle, words)); + + /* Solved crossword: + * c d c + * a o a + * t g r + */ + + assertEquals('c', puzzle[0][0]); + assertEquals('a', puzzle[1][0]); + assertEquals('t', puzzle[2][0]); + + assertEquals('d', puzzle[0][1]); + assertEquals('o', puzzle[1][1]); + assertEquals('g', puzzle[2][1]); + + assertEquals('c', puzzle[0][2]); + assertEquals('a', puzzle[1][2]); + assertEquals('r', puzzle[2][2]); + } + + @Test + public void testNoSolution() { + char[][] puzzle = {{' ', ' ', ' '}, {' ', ' ', ' '}, {' ', ' ', ' '}}; + List<String> words = Arrays.asList("cat", "dog", "elephant"); // 'elephant' is too long for the grid + assertFalse(CrosswordSolver.solveCrossword(puzzle, words)); + } +} diff --git a/src/test/java/com/thealgorithms/backtracking/FloodFillTest.java b/src/test/java/com/thealgorithms/backtracking/FloodFillTest.java new file mode 100644 index 000000000000..8b860b5b46b0 --- /dev/null +++ b/src/test/java/com/thealgorithms/backtracking/FloodFillTest.java @@ -0,0 +1,106 @@ +package com.thealgorithms.backtracking; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +class FloodFillTest { + + @Test + void testForEmptyImage() { + int[][] image = {}; + int[][] expected = {}; + + FloodFill.floodFill(image, 4, 5, 3, 2); + assertArrayEquals(expected, image); + } + + @Test + void testForSingleElementImage() { + int[][] image = {{1}}; + int[][] expected = {{3}}; + + FloodFill.floodFill(image, 0, 0, 3, 1); + assertArrayEquals(expected, image); + } + + @Test + void testForImageOne() { + int[][] image = { + {0, 0, 0, 0, 0, 0, 0}, + {0, 3, 3, 3, 3, 0, 0}, + {0, 3, 1, 1, 5, 0, 0}, + {0, 3, 1, 1, 5, 5, 3}, + {0, 3, 5, 5, 1, 1, 3}, + {0, 0, 0, 5, 1, 1, 3}, + {0, 0, 0, 3, 3, 3, 3}, + }; + + int[][] expected = { + {0, 0, 0, 0, 0, 0, 0}, + {0, 3, 3, 3, 3, 0, 0}, + {0, 3, 2, 2, 5, 0, 0}, + {0, 3, 2, 2, 5, 5, 3}, + {0, 3, 5, 5, 2, 2, 3}, + {0, 0, 0, 5, 2, 2, 3}, + {0, 0, 0, 3, 3, 3, 3}, + }; + + FloodFill.floodFill(image, 2, 2, 2, 1); + assertArrayEquals(expected, image); + } + + @Test + void testForImageTwo() { + int[][] image = { + {0, 0, 1, 1, 0, 0, 0}, + {1, 1, 3, 3, 3, 0, 0}, + {1, 3, 1, 1, 5, 0, 0}, + {0, 3, 1, 1, 5, 5, 3}, + {0, 3, 5, 5, 1, 1, 3}, + {0, 0, 0, 5, 1, 1, 3}, + {0, 0, 0, 1, 3, 1, 3}, + }; + + int[][] expected = { + {0, 0, 2, 2, 0, 0, 0}, + {2, 2, 3, 3, 3, 0, 0}, + {2, 3, 2, 2, 5, 0, 0}, + {0, 3, 2, 2, 5, 5, 3}, + {0, 3, 5, 5, 2, 2, 3}, + {0, 0, 0, 5, 2, 2, 3}, + {0, 0, 0, 2, 3, 2, 3}, + }; + + FloodFill.floodFill(image, 2, 2, 2, 1); + assertArrayEquals(expected, image); + } + + @Test + void testForImageThree() { + int[][] image = { + {1, 1, 2, 3, 1, 1, 1}, + {1, 0, 0, 1, 0, 0, 1}, + {1, 1, 1, 0, 3, 1, 2}, + }; + + int[][] expected = { + {4, 4, 2, 3, 4, 4, 4}, + {4, 0, 0, 4, 0, 0, 4}, + {4, 4, 4, 0, 3, 4, 2}, + }; + + FloodFill.floodFill(image, 0, 1, 4, 1); + assertArrayEquals(expected, image); + } + + @Test + void testForSameNewAndOldColor() { + int[][] image = {{1, 1, 2}, {1, 0, 0}, {1, 1, 1}}; + + int[][] expected = {{1, 1, 2}, {1, 0, 0}, {1, 1, 1}}; + + FloodFill.floodFill(image, 0, 1, 1, 1); + assertArrayEquals(expected, image); + } +} diff --git a/src/test/java/com/thealgorithms/backtracking/KnightsTourTest.java b/src/test/java/com/thealgorithms/backtracking/KnightsTourTest.java new file mode 100644 index 000000000000..306dbf4c2ec7 --- /dev/null +++ b/src/test/java/com/thealgorithms/backtracking/KnightsTourTest.java @@ -0,0 +1,59 @@ +package com.thealgorithms.backtracking; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class KnightsTourTest { + + @BeforeEach + void setUp() { + // Call the reset method in the KnightsTour class + KnightsTour.resetBoard(); + } + + @Test + void testGridInitialization() { + for (int r = 0; r < 12; r++) { + for (int c = 0; c < 12; c++) { + if (r < 2 || r > 12 - 3 || c < 2 || c > 12 - 3) { + assertEquals(-1, KnightsTour.grid[r][c], "Border cells should be -1"); + } else { + assertEquals(0, KnightsTour.grid[r][c], "Internal cells should be 0"); + } + } + } + } + + @Test + void testCountNeighbors() { + // Manually place a knight at (3, 3) and mark nearby cells to test counting + KnightsTour.grid[3][3] = 1; // Knight is here + KnightsTour.grid[5][4] = -1; // Block one potential move + + int neighborCount = KnightsTour.countNeighbors(3, 3); + assertEquals(3, neighborCount, "Knight at (3, 3) should have 3 neighbors (one blocked)"); + + KnightsTour.grid[4][1] = -1; // Block another move + neighborCount = KnightsTour.countNeighbors(3, 3); + assertEquals(3, neighborCount, "Knight at (3, 3) should have 3 neighbors (two blocked)"); + } + + @Test + void testNeighbors() { + // Test the list of valid neighbors for a given cell (3, 3) + List<int[]> neighbors = KnightsTour.neighbors(3, 3); + assertEquals(4, neighbors.size(), "Knight at (3, 3) should have 8 valid neighbors"); + } + + @Test + void testSolveSuccessful() { + // Test if the solve method works for a successful knight's tour + KnightsTour.grid[2][2] = 1; // Start the knight at (2, 2) + boolean result = KnightsTour.solve(2, 2, 2); + assertTrue(result, "solve() should successfully complete a Knight's tour"); + } +} diff --git a/src/test/java/com/thealgorithms/backtracking/MColoringTest.java b/src/test/java/com/thealgorithms/backtracking/MColoringTest.java new file mode 100644 index 000000000000..f3f25933b7a1 --- /dev/null +++ b/src/test/java/com/thealgorithms/backtracking/MColoringTest.java @@ -0,0 +1,58 @@ +package com.thealgorithms.backtracking; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import org.junit.jupiter.api.Test; + +/** + * @author Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi) + */ +class MColoringTest { + + @Test + void testGraphColoring1() { + int n = 4; + int[][] graph = {{0, 1, 1, 1}, {1, 0, 1, 0}, {1, 1, 0, 1}, {1, 0, 1, 0}}; + int m = 3; // Number of colors + + assertTrue(MColoring.isColoringPossible(createGraph(graph), n, m)); + } + + @Test + void testGraphColoring2() { + int n = 5; + int[][] graph = {{0, 1, 1, 1, 0}, {1, 0, 0, 1, 0}, {1, 0, 0, 1, 1}, {1, 1, 1, 0, 1}, {0, 0, 1, 1, 0}}; + int m = 2; // Number of colors + + assertFalse(MColoring.isColoringPossible(createGraph(graph), n, m)); + } + + @Test + void testGraphColoring3() { + int n = 3; + int[][] graph = {{0, 1, 1}, {1, 0, 1}, {1, 1, 0}}; + int m = 2; // Number of colors + + assertFalse(MColoring.isColoringPossible(createGraph(graph), n, m)); + } + + private ArrayList<Node> createGraph(int[][] graph) { + int n = graph.length; + ArrayList<Node> nodes = new ArrayList<>(n + 1); + for (int i = 0; i <= n; i++) { + nodes.add(new Node()); + } + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { // Use j = i + 1 to avoid setting edges twice + if (graph[i][j] > 0) { + nodes.get(i + 1).edges.add(j + 1); + nodes.get(j + 1).edges.add(i + 1); + } + } + } + return nodes; + } +} diff --git a/src/test/java/com/thealgorithms/backtracking/MazeRecursionTest.java b/src/test/java/com/thealgorithms/backtracking/MazeRecursionTest.java new file mode 100644 index 000000000000..b8e77fb38bad --- /dev/null +++ b/src/test/java/com/thealgorithms/backtracking/MazeRecursionTest.java @@ -0,0 +1,67 @@ +package com.thealgorithms.backtracking; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +/** + * @author onglipwei + * @create 2022-08-03 5:17 AM + */ +public class MazeRecursionTest { + + @Test + public void testSolveMazeUsingFirstAndSecondStrategy() { + int[][] map = new int[8][7]; + int[][] map2 = new int[8][7]; + + // We use 1 to indicate walls + // Set the ceiling and floor to 1 + for (int i = 0; i < 7; i++) { + map[0][i] = 1; + map[7][i] = 1; + } + // Set the left and right wall to 1 + for (int i = 0; i < 8; i++) { + map[i][0] = 1; + map[i][6] = 1; + } + // Set obstacles + map[3][1] = 1; + map[3][2] = 1; + + // Clone the original map for the second pathfinding strategy + for (int i = 0; i < map.length; i++) { + System.arraycopy(map[i], 0, map2[i], 0, map[i].length); + } + + // Solve the maze using the first strategy + int[][] solvedMap1 = MazeRecursion.solveMazeUsingFirstStrategy(map); + // Solve the maze using the second strategy + int[][] solvedMap2 = MazeRecursion.solveMazeUsingSecondStrategy(map2); + int[][] expectedMap1 = new int[][] { + {1, 1, 1, 1, 1, 1, 1}, + {1, 2, 0, 0, 0, 0, 1}, + {1, 2, 2, 2, 0, 0, 1}, + {1, 1, 1, 2, 0, 0, 1}, + {1, 0, 0, 2, 0, 0, 1}, + {1, 0, 0, 2, 0, 0, 1}, + {1, 0, 0, 2, 2, 2, 1}, + {1, 1, 1, 1, 1, 1, 1}, + }; + int[][] expectedMap2 = new int[][] { + {1, 1, 1, 1, 1, 1, 1}, + {1, 2, 2, 2, 2, 2, 1}, + {1, 0, 0, 0, 0, 2, 1}, + {1, 1, 1, 0, 0, 2, 1}, + {1, 0, 0, 0, 0, 2, 1}, + {1, 0, 0, 0, 0, 2, 1}, + {1, 0, 0, 0, 0, 2, 1}, + {1, 1, 1, 1, 1, 1, 1}, + }; + + // Assert the results + assertArrayEquals(expectedMap1, solvedMap1); + assertArrayEquals(expectedMap2, solvedMap2); + } +} diff --git a/src/test/java/com/thealgorithms/backtracking/NQueensTest.java b/src/test/java/com/thealgorithms/backtracking/NQueensTest.java new file mode 100644 index 000000000000..243133848ee2 --- /dev/null +++ b/src/test/java/com/thealgorithms/backtracking/NQueensTest.java @@ -0,0 +1,49 @@ +package com.thealgorithms.backtracking; + +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class NQueensTest { + + @Test + public void testNQueens1() { + List<List<String>> expected = singletonList(singletonList("Q")); + assertEquals(expected, NQueens.getNQueensArrangements(1)); + } + + @Test + public void testNQueens2() { + List<List<String>> expected = new ArrayList<>(); // No solution exists + assertEquals(expected, NQueens.getNQueensArrangements(2)); + } + + @Test + public void testNQueens3() { + List<List<String>> expected = new ArrayList<>(); // No solution exists + assertEquals(expected, NQueens.getNQueensArrangements(3)); + } + + @Test + public void testNQueens4() { + List<List<String>> expected = Arrays.asList(Arrays.asList(".Q..", "...Q", "Q...", "..Q."), Arrays.asList("..Q.", "Q...", "...Q", ".Q..")); + assertEquals(expected, NQueens.getNQueensArrangements(4)); + } + + @Test + public void testNQueens5() { + // Only the number of solutions is tested for larger N due to the complexity of checking each board configuration + List<List<String>> result = NQueens.getNQueensArrangements(5); + assertEquals(10, result.size()); // 5x5 board has 10 solutions + } + + @Test + public void testNQueens6() { + List<List<String>> result = NQueens.getNQueensArrangements(6); + assertEquals(4, result.size()); // 6x6 board has 4 solutions + } +} diff --git a/src/test/java/com/thealgorithms/backtracking/ParenthesesGeneratorTest.java b/src/test/java/com/thealgorithms/backtracking/ParenthesesGeneratorTest.java new file mode 100644 index 000000000000..9da16061d8f4 --- /dev/null +++ b/src/test/java/com/thealgorithms/backtracking/ParenthesesGeneratorTest.java @@ -0,0 +1,33 @@ +package com.thealgorithms.backtracking; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class ParenthesesGeneratorTest { + @ParameterizedTest + @MethodSource("regularInputStream") + void regularInputTests(int input, List<String> expected) { + assertEquals(expected, ParenthesesGenerator.generateParentheses(input)); + } + + @ParameterizedTest + @MethodSource("negativeInputStream") + void throwsForNegativeInputTests(int input) { + assertThrows(IllegalArgumentException.class, () -> ParenthesesGenerator.generateParentheses(input)); + } + + private static Stream<Arguments> regularInputStream() { + return Stream.of(Arguments.of(0, List.of("")), Arguments.of(1, List.of("()")), Arguments.of(2, List.of("(())", "()()")), Arguments.of(3, List.of("((()))", "(()())", "(())()", "()(())", "()()()")), + Arguments.of(4, List.of("(((())))", "((()()))", "((())())", "((()))()", "(()(()))", "(()()())", "(()())()", "(())(())", "(())()()", "()((()))", "()(()())", "()(())()", "()()(())", "()()()()"))); + } + + private static Stream<Arguments> negativeInputStream() { + return Stream.of(Arguments.of(-1), Arguments.of(-5), Arguments.of(-10)); + } +} diff --git a/src/test/java/com/thealgorithms/backtracking/PermutationTest.java b/src/test/java/com/thealgorithms/backtracking/PermutationTest.java new file mode 100644 index 000000000000..76a714829109 --- /dev/null +++ b/src/test/java/com/thealgorithms/backtracking/PermutationTest.java @@ -0,0 +1,30 @@ +package com.thealgorithms.backtracking; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class PermutationTest { + + @Test + void testNoElement() { + List<Integer[]> result = Permutation.permutation(new Integer[] {}); + assertEquals(result.get(0).length, 0); + } + + @Test + void testSingleElement() { + List<Integer[]> result = Permutation.permutation(new Integer[] {1}); + assertEquals(result.get(0)[0], 1); + } + + @Test + void testMultipleElements() { + List<Integer[]> result = Permutation.permutation(new Integer[] {1, 2}); + assertTrue(Arrays.equals(result.get(0), new Integer[] {1, 2})); + assertTrue(Arrays.equals(result.get(1), new Integer[] {2, 1})); + } +} diff --git a/src/test/java/com/thealgorithms/backtracking/PowerSumTest.java b/src/test/java/com/thealgorithms/backtracking/PowerSumTest.java new file mode 100644 index 000000000000..b14bce2b1920 --- /dev/null +++ b/src/test/java/com/thealgorithms/backtracking/PowerSumTest.java @@ -0,0 +1,28 @@ +package com.thealgorithms.backtracking; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class PowerSumTest { + + @Test + void testNumberZeroAndPowerZero() { + PowerSum powerSum = new PowerSum(); + int result = powerSum.powSum(0, 0); + assertEquals(1, result); + } + + @Test + void testNumberHundredAndPowerTwo() { + PowerSum powerSum = new PowerSum(); + int result = powerSum.powSum(100, 2); + assertEquals(3, result); + } + + @Test + void testNumberHundredAndPowerThree() { + PowerSum powerSum = new PowerSum(); + int result = powerSum.powSum(100, 3); + assertEquals(1, result); + } +} diff --git a/src/test/java/com/thealgorithms/backtracking/SubsequenceFinderTest.java b/src/test/java/com/thealgorithms/backtracking/SubsequenceFinderTest.java new file mode 100644 index 000000000000..dac2e2675674 --- /dev/null +++ b/src/test/java/com/thealgorithms/backtracking/SubsequenceFinderTest.java @@ -0,0 +1,28 @@ +package com.thealgorithms.backtracking; + +import static org.junit.jupiter.api.Assertions.assertIterableEquals; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class SubsequenceFinderTest { + + @ParameterizedTest + @MethodSource("getTestCases") + void testGenerateAll(TestCase testData) { + final var actual = SubsequenceFinder.generateAll(testData.input()); + assertIterableEquals(testData.expected(), actual); + } + + static Stream<TestCase> getTestCases() { + return Stream.of(new TestCase(new ArrayList<>(), List.of(List.of())), new TestCase(List.of(1, 2), List.of(List.of(), List.of(2), List.of(1), List.of(1, 2))), + new TestCase(List.of("A", "B", "C"), List.of(List.of(), List.of("C"), List.of("B"), List.of("B", "C"), List.of("A"), List.of("A", "C"), List.of("A", "B"), List.of("A", "B", "C"))), + new TestCase(List.of(1, 2, 3), List.of(List.of(), List.of(3), List.of(2), List.of(2, 3), List.of(1), List.of(1, 3), List.of(1, 2), List.of(1, 2, 3))), new TestCase(List.of(2, 2), List.of(List.of(), List.of(2), List.of(2), List.of(2, 2)))); + } + + record TestCase(List<Object> input, List<List<Object>> expected) { + } +} diff --git a/src/test/java/com/thealgorithms/backtracking/WordPatternMatcherTest.java b/src/test/java/com/thealgorithms/backtracking/WordPatternMatcherTest.java new file mode 100644 index 000000000000..4d56be566035 --- /dev/null +++ b/src/test/java/com/thealgorithms/backtracking/WordPatternMatcherTest.java @@ -0,0 +1,40 @@ +package com.thealgorithms.backtracking; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class WordPatternMatcherTest { + + @Test + public void testPatternMatchingSuccess() { + assertTrue(WordPatternMatcher.matchWordPattern("aba", "GraphTreesGraph")); + assertTrue(WordPatternMatcher.matchWordPattern("xyx", "PythonRubyPython")); + } + + @Test + public void testPatternMatchingFailure() { + assertFalse(WordPatternMatcher.matchWordPattern("GG", "PythonJavaPython")); + } + + @Test + public void testEmptyPatternAndString() { + assertTrue(WordPatternMatcher.matchWordPattern("", "")); + } + + @Test + public void testEmptyPattern() { + assertFalse(WordPatternMatcher.matchWordPattern("", "nonempty")); + } + + @Test + public void testEmptyString() { + assertFalse(WordPatternMatcher.matchWordPattern("abc", "")); + } + + @Test + public void testLongerPatternThanString() { + assertFalse(WordPatternMatcher.matchWordPattern("abcd", "abc")); + } +} diff --git a/src/test/java/com/thealgorithms/backtracking/WordSearchTest.java b/src/test/java/com/thealgorithms/backtracking/WordSearchTest.java new file mode 100644 index 000000000000..46f199747322 --- /dev/null +++ b/src/test/java/com/thealgorithms/backtracking/WordSearchTest.java @@ -0,0 +1,32 @@ +package com.thealgorithms.backtracking; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class WordSearchTest { + @Test + void test1() { + WordSearch ws = new WordSearch(); + char[][] board = {{'A', 'B', 'C', 'E'}, {'S', 'F', 'C', 'S'}, {'A', 'D', 'E', 'E'}}; + String word = "ABCCED"; + assertTrue(ws.exist(board, word)); + } + + @Test + void test2() { + WordSearch ws = new WordSearch(); + char[][] board = {{'A', 'B', 'C', 'E'}, {'S', 'F', 'C', 'S'}, {'A', 'D', 'E', 'E'}}; + String word = "SEE"; + assertTrue(ws.exist(board, word)); + } + + @Test + void test3() { + WordSearch ws = new WordSearch(); + char[][] board = {{'A', 'B', 'C', 'E'}, {'S', 'F', 'C', 'S'}, {'A', 'D', 'E', 'E'}}; + String word = "ABCB"; + Assertions.assertFalse(ws.exist(board, word)); + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/BcdConversionTest.java b/src/test/java/com/thealgorithms/bitmanipulation/BcdConversionTest.java new file mode 100644 index 000000000000..727b07eb9065 --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/BcdConversionTest.java @@ -0,0 +1,85 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +public class BcdConversionTest { + + @Test + public void testBcdToDecimal() { + int decimal = BcdConversion.bcdToDecimal(0x1234); + assertEquals(1234, decimal); // BCD 0x1234 should convert to decimal 1234 + } + + @Test + public void testDecimalToBcd() { + int bcd = BcdConversion.decimalToBcd(1234); + assertEquals(0x1234, bcd); // Decimal 1234 should convert to BCD 0x1234 + } + + @Test + public void testBcdToDecimalZero() { + int decimal = BcdConversion.bcdToDecimal(0x0); + assertEquals(0, decimal); // BCD 0x0 should convert to decimal 0 + } + + @Test + public void testDecimalToBcdZero() { + int bcd = BcdConversion.decimalToBcd(0); + assertEquals(0x0, bcd); // Decimal 0 should convert to BCD 0x0 + } + + @Test + public void testBcdToDecimalSingleDigit() { + int decimal = BcdConversion.bcdToDecimal(0x7); + assertEquals(7, decimal); // BCD 0x7 should convert to decimal 7 + } + + @Test + public void testDecimalToBcdSingleDigit() { + int bcd = BcdConversion.decimalToBcd(7); + assertEquals(0x7, bcd); // Decimal 7 should convert to BCD 0x7 + } + + @Test + public void testBcdToDecimalMaxValue() { + int decimal = BcdConversion.bcdToDecimal(0x9999); + assertEquals(9999, decimal); // BCD 0x9999 should convert to decimal 9999 + } + + @Test + public void testDecimalToBcdMaxValue() { + int bcd = BcdConversion.decimalToBcd(9999); + assertEquals(0x9999, bcd); // Decimal 9999 should convert to BCD 0x9999 + } + + @Test + public void testBcdToDecimalInvalidHighDigit() { + // Testing invalid BCD input where one of the digits is > 9 + assertThrows(IllegalArgumentException.class, () -> { + BcdConversion.bcdToDecimal(0x123A); // Invalid BCD, 'A' is not a valid digit + }); + } + + @Test + public void testDecimalToBcdInvalidValue() { + // Testing conversion for numbers greater than 9999, which cannot be represented in BCD + assertThrows(IllegalArgumentException.class, () -> { + BcdConversion.decimalToBcd(10000); // 10000 is too large for BCD representation + }); + } + + @Test + public void testBcdToDecimalLeadingZeroes() { + int decimal = BcdConversion.bcdToDecimal(0x0234); + assertEquals(234, decimal); // BCD 0x0234 should convert to decimal 234, ignoring leading zero + } + + @Test + public void testDecimalToBcdLeadingZeroes() { + int bcd = BcdConversion.decimalToBcd(234); + assertEquals(0x0234, bcd); // Decimal 234 should convert to BCD 0x0234 + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/BinaryPalindromeCheckTest.java b/src/test/java/com/thealgorithms/bitmanipulation/BinaryPalindromeCheckTest.java new file mode 100644 index 000000000000..ff41344266e4 --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/BinaryPalindromeCheckTest.java @@ -0,0 +1,18 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class BinaryPalindromeCheckTest { + + @Test + public void testIsBinaryPalindrome() { + assertTrue(BinaryPalindromeCheck.isBinaryPalindrome(9)); // 1001 is a palindrome + assertFalse(BinaryPalindromeCheck.isBinaryPalindrome(10)); // 1010 is not a palindrome + assertTrue(BinaryPalindromeCheck.isBinaryPalindrome(0)); // 0 is a palindrome + assertTrue(BinaryPalindromeCheck.isBinaryPalindrome(1)); // 1 is a palindrome + assertFalse(BinaryPalindromeCheck.isBinaryPalindrome(12)); // 1100 is not a palindrome + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/BitSwapTest.java b/src/test/java/com/thealgorithms/bitmanipulation/BitSwapTest.java new file mode 100644 index 000000000000..40de770e0c66 --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/BitSwapTest.java @@ -0,0 +1,13 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +public class BitSwapTest { + @Test + void testHighestSetBit() { + assertEquals(3, BitSwap.bitSwap(3, 0, 1)); + assertEquals(5, BitSwap.bitSwap(6, 0, 1)); + assertEquals(7, BitSwap.bitSwap(7, 1, 1)); + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/BooleanAlgebraGatesTest.java b/src/test/java/com/thealgorithms/bitmanipulation/BooleanAlgebraGatesTest.java new file mode 100644 index 000000000000..8737d05ec459 --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/BooleanAlgebraGatesTest.java @@ -0,0 +1,101 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.thealgorithms.bitmanipulation.BooleanAlgebraGates.ANDGate; +import com.thealgorithms.bitmanipulation.BooleanAlgebraGates.BooleanGate; +import com.thealgorithms.bitmanipulation.BooleanAlgebraGates.NANDGate; +import com.thealgorithms.bitmanipulation.BooleanAlgebraGates.NORGate; +import com.thealgorithms.bitmanipulation.BooleanAlgebraGates.NOTGate; +import com.thealgorithms.bitmanipulation.BooleanAlgebraGates.ORGate; +import com.thealgorithms.bitmanipulation.BooleanAlgebraGates.XORGate; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.MethodSource; + +class BooleanAlgebraGatesTest { + + @ParameterizedTest(name = "ANDGate Test Case {index}: inputs={0} -> expected={1}") + @MethodSource("provideAndGateTestCases") + void testANDGate(List<Boolean> inputs, boolean expected) { + BooleanGate gate = new ANDGate(); + assertEquals(expected, gate.evaluate(inputs)); + } + + @ParameterizedTest(name = "ORGate Test Case {index}: inputs={0} -> expected={1}") + @MethodSource("provideOrGateTestCases") + void testORGate(List<Boolean> inputs, boolean expected) { + BooleanGate gate = new ORGate(); + assertEquals(expected, gate.evaluate(inputs)); + } + + @ParameterizedTest(name = "NOTGate Test Case {index}: input={0} -> expected={1}") + @CsvSource({"true, false", "false, true"}) + void testNOTGate(boolean input, boolean expected) { + NOTGate gate = new NOTGate(); + assertEquals(expected, gate.evaluate(input)); + } + + @ParameterizedTest(name = "XORGate Test Case {index}: inputs={0} -> expected={1}") + @MethodSource("provideXorGateTestCases") + void testXORGate(List<Boolean> inputs, boolean expected) { + BooleanGate gate = new XORGate(); + assertEquals(expected, gate.evaluate(inputs)); + } + + @ParameterizedTest(name = "NANDGate Test Case {index}: inputs={0} -> expected={1}") + @MethodSource("provideNandGateTestCases") + void testNANDGate(List<Boolean> inputs, boolean expected) { + BooleanGate gate = new NANDGate(); + assertEquals(expected, gate.evaluate(inputs)); + } + + @ParameterizedTest(name = "NORGate Test Case {index}: inputs={0} -> expected={1}") + @MethodSource("provideNorGateTestCases") + void testNORGate(List<Boolean> inputs, boolean expected) { + BooleanGate gate = new NORGate(); + assertEquals(expected, gate.evaluate(inputs)); + } + + // Helper methods to provide test data for each gate + + static Stream<Object[]> provideAndGateTestCases() { + return Stream.of(new Object[] {Arrays.asList(Boolean.TRUE, Boolean.TRUE, Boolean.TRUE), Boolean.TRUE}, new Object[] {Arrays.asList(Boolean.TRUE, Boolean.FALSE, Boolean.TRUE), Boolean.FALSE}, new Object[] {Arrays.asList(Boolean.FALSE, Boolean.FALSE, Boolean.FALSE), Boolean.FALSE}, + new Object[] {Collections.emptyList(), Boolean.TRUE} // AND over no inputs is true + ); + } + + static Stream<Object[]> provideOrGateTestCases() { + return Stream.of(new Object[] {Arrays.asList(Boolean.TRUE, Boolean.FALSE, Boolean.FALSE), Boolean.TRUE}, new Object[] {Arrays.asList(Boolean.FALSE, Boolean.FALSE, Boolean.FALSE), Boolean.FALSE}, new Object[] {Arrays.asList(Boolean.TRUE, Boolean.TRUE, Boolean.TRUE), Boolean.TRUE}, + new Object[] {Collections.emptyList(), Boolean.FALSE} // OR over no inputs is false + ); + } + + static Stream<Object[]> provideXorGateTestCases() { + return Stream.of(new Object[] {Arrays.asList(Boolean.TRUE, Boolean.FALSE, Boolean.TRUE), Boolean.FALSE}, // XOR over odd true + new Object[] {Arrays.asList(Boolean.TRUE, Boolean.FALSE, Boolean.FALSE), Boolean.TRUE}, // XOR over single true + new Object[] {Arrays.asList(Boolean.FALSE, Boolean.FALSE, Boolean.FALSE), Boolean.FALSE}, // XOR over all false + new Object[] {Arrays.asList(Boolean.TRUE, Boolean.TRUE), Boolean.FALSE} // XOR over even true + ); + } + + static Stream<Object[]> provideNandGateTestCases() { + return Stream.of(new Object[] {Arrays.asList(Boolean.TRUE, Boolean.TRUE, Boolean.TRUE), Boolean.FALSE}, // NAND of all true is false + new Object[] {Arrays.asList(Boolean.TRUE, Boolean.FALSE), Boolean.TRUE}, // NAND with one false is true + new Object[] {Arrays.asList(Boolean.FALSE, Boolean.FALSE), Boolean.TRUE}, // NAND of all false is true + new Object[] {Collections.emptyList(), Boolean.FALSE} // NAND over no inputs is false (negation of AND) + ); + } + + static Stream<Object[]> provideNorGateTestCases() { + return Stream.of(new Object[] {Arrays.asList(Boolean.FALSE, Boolean.FALSE), Boolean.TRUE}, // NOR of all false is true + new Object[] {Arrays.asList(Boolean.FALSE, Boolean.TRUE), Boolean.FALSE}, // NOR with one true is false + new Object[] {Arrays.asList(Boolean.TRUE, Boolean.TRUE), Boolean.FALSE}, // NOR of all true is false + new Object[] {Collections.emptyList(), Boolean.TRUE} // NOR over no inputs is true (negation of OR) + ); + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/ClearLeftmostSetBitTest.java b/src/test/java/com/thealgorithms/bitmanipulation/ClearLeftmostSetBitTest.java new file mode 100644 index 000000000000..e77889fb7b0a --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/ClearLeftmostSetBitTest.java @@ -0,0 +1,16 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class ClearLeftmostSetBitTest { + + @Test + public void testClearLeftmostSetBit() { + assertEquals(10, ClearLeftmostSetBit.clearLeftmostSetBit(26)); // 11010 -> 01010 + assertEquals(0, ClearLeftmostSetBit.clearLeftmostSetBit(1)); // 1 -> 0 + assertEquals(3, ClearLeftmostSetBit.clearLeftmostSetBit(7)); // 111 -> 011 + assertEquals(2, ClearLeftmostSetBit.clearLeftmostSetBit(6)); // 0110 -> 0010 + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/CountLeadingZerosTest.java b/src/test/java/com/thealgorithms/bitmanipulation/CountLeadingZerosTest.java new file mode 100644 index 000000000000..6ab15fd2ab5a --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/CountLeadingZerosTest.java @@ -0,0 +1,16 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class CountLeadingZerosTest { + + @Test + public void testCountLeadingZeros() { + assertEquals(29, CountLeadingZeros.countLeadingZeros(5)); // 000...0101 has 29 leading zeros + assertEquals(32, CountLeadingZeros.countLeadingZeros(0)); // 000...0000 has 32 leading zeros + assertEquals(31, CountLeadingZeros.countLeadingZeros(1)); // 000...0001 has 31 leading zeros + assertEquals(0, CountLeadingZeros.countLeadingZeros(-1)); // No leading zeros in negative number (-1) + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/CountSetBitsTest.java b/src/test/java/com/thealgorithms/bitmanipulation/CountSetBitsTest.java new file mode 100644 index 000000000000..61e0757f9c12 --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/CountSetBitsTest.java @@ -0,0 +1,26 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class CountSetBitsTest { + + @Test + void testSetBits() { + CountSetBits csb = new CountSetBits(); + assertEquals(1L, csb.countSetBits(16)); + assertEquals(4, csb.countSetBits(15)); + assertEquals(5, csb.countSetBits(10000)); + assertEquals(5, csb.countSetBits(31)); + } + + @Test + void testSetBitsLookupApproach() { + CountSetBits csb = new CountSetBits(); + assertEquals(1L, csb.lookupApproach(16)); + assertEquals(4, csb.lookupApproach(15)); + assertEquals(5, csb.lookupApproach(10000)); + assertEquals(5, csb.lookupApproach(31)); + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/FindNthBitTest.java b/src/test/java/com/thealgorithms/bitmanipulation/FindNthBitTest.java new file mode 100644 index 000000000000..978003d21358 --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/FindNthBitTest.java @@ -0,0 +1,39 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public final class FindNthBitTest { + + /** + * A parameterized test that checks the value of the Nth bit for different inputs. + * + * @param num the number whose Nth bit is being tested + * @param n the bit position + * @param expected the expected value of the Nth bit (0 or 1) + */ + @ParameterizedTest + @MethodSource("provideTestCases") + void findNthBitParameterizedTest(int num, int n, int expected) { + assertEquals(expected, FindNthBit.findNthBit(num, n)); + } + + /** + * Provides the test cases as a stream of arguments for the parameterized test. + * + * @return a stream of test cases where each case consists of a number, the bit position, + * and the expected result. + */ + private static Stream<Arguments> provideTestCases() { + return Stream.of(Arguments.of(13, 2, 0), // binary: 1101, 2nd bit is 0 + Arguments.of(13, 3, 1), // binary: 1101, 3rd bit is 1 + Arguments.of(4, 2, 0), // binary: 100, 2nd bit is 0 + Arguments.of(4, 3, 1), // binary: 100, 3rd bit is 1 + Arguments.of(1, 1, 1) // binary: 1, 1st bit is 1 + ); + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/FirstDifferentBitTest.java b/src/test/java/com/thealgorithms/bitmanipulation/FirstDifferentBitTest.java new file mode 100644 index 000000000000..ae7bad9a666a --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/FirstDifferentBitTest.java @@ -0,0 +1,15 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class FirstDifferentBitTest { + + @ParameterizedTest + @CsvSource({"10, 8, 1", "7, 5, 1", "15, 14, 0", "1, 2, 0"}) + void testFirstDifferentBit(int x, int y, int expected) { + assertEquals(expected, FirstDifferentBit.firstDifferentBit(x, y)); + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/GenerateSubsetsTest.java b/src/test/java/com/thealgorithms/bitmanipulation/GenerateSubsetsTest.java new file mode 100644 index 000000000000..912d7e729ade --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/GenerateSubsetsTest.java @@ -0,0 +1,53 @@ +package com.thealgorithms.bitmanipulation; + +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +class GenerateSubsetsTest { + + @Test + void testGenerateSubsetsWithTwoElements() { + int[] set = {1, 2}; + List<List<Integer>> expected = new ArrayList<>(); + expected.add(new ArrayList<>()); + expected.add(singletonList(1)); + expected.add(singletonList(2)); + expected.add(Arrays.asList(1, 2)); + + List<List<Integer>> result = GenerateSubsets.generateSubsets(set); + assertEquals(expected, result); + } + + @Test + void testGenerateSubsetsWithOneElement() { + int[] set = {3}; + List<List<Integer>> expected = new ArrayList<>(); + expected.add(new ArrayList<>()); + expected.add(singletonList(3)); + + List<List<Integer>> result = GenerateSubsets.generateSubsets(set); + assertEquals(expected, result); + } + + @Test + void testGenerateSubsetsWithThreeElements() { + int[] set = {4, 5, 6}; + List<List<Integer>> expected = new ArrayList<>(); + expected.add(new ArrayList<>()); + expected.add(singletonList(4)); + expected.add(singletonList(5)); + expected.add(Arrays.asList(4, 5)); + expected.add(singletonList(6)); + expected.add(Arrays.asList(4, 6)); + expected.add(Arrays.asList(5, 6)); + expected.add(Arrays.asList(4, 5, 6)); + + List<List<Integer>> result = GenerateSubsets.generateSubsets(set); + assertEquals(expected, result); + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/GrayCodeConversionTest.java b/src/test/java/com/thealgorithms/bitmanipulation/GrayCodeConversionTest.java new file mode 100644 index 000000000000..1fe792028dca --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/GrayCodeConversionTest.java @@ -0,0 +1,29 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class GrayCodeConversionTest { + + @Test + public void testBinaryToGray() { + assertEquals(7, GrayCodeConversion.binaryToGray(5)); // 101 -> 111 + assertEquals(4, GrayCodeConversion.binaryToGray(7)); // 111 -> 100 + assertEquals(1, GrayCodeConversion.binaryToGray(1)); // 001 -> 001 + } + + @Test + public void testGrayToBinary() { + assertEquals(5, GrayCodeConversion.grayToBinary(7)); // 111 -> 101 + assertEquals(4, GrayCodeConversion.grayToBinary(6)); // 110 -> 100 + assertEquals(1, GrayCodeConversion.grayToBinary(1)); // 001 -> 001 + } + + @Test + public void testBinaryGrayCycle() { + int binary = 9; // 1001 in binary + int gray = GrayCodeConversion.binaryToGray(binary); + assertEquals(binary, GrayCodeConversion.grayToBinary(gray)); // Should return to original binary + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/HammingDistanceTest.java b/src/test/java/com/thealgorithms/bitmanipulation/HammingDistanceTest.java new file mode 100644 index 000000000000..bde39a69f190 --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/HammingDistanceTest.java @@ -0,0 +1,17 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class HammingDistanceTest { + + @Test + public void testHammingDistance() { + assertEquals(3, HammingDistance.hammingDistance(9, 14)); // 1001 vs 1110, Hamming distance is 3 + assertEquals(0, HammingDistance.hammingDistance(10, 10)); // Same number, Hamming distance is 0 + assertEquals(1, HammingDistance.hammingDistance(1, 0)); // 0001 vs 0000, Hamming distance is 1 + assertEquals(2, HammingDistance.hammingDistance(4, 1)); // 100 vs 001, Hamming distance is 2 + assertEquals(4, HammingDistance.hammingDistance(0, 15)); // 0000 vs 1111, Hamming distance is 4 + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/HigherLowerPowerOfTwoTest.java b/src/test/java/com/thealgorithms/bitmanipulation/HigherLowerPowerOfTwoTest.java new file mode 100644 index 000000000000..34391002941b --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/HigherLowerPowerOfTwoTest.java @@ -0,0 +1,26 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class HigherLowerPowerOfTwoTest { + + @Test + public void testNextHigherPowerOfTwo() { + assertEquals(32, HigherLowerPowerOfTwo.nextHigherPowerOfTwo(19)); // next higher power of two is 32 + assertEquals(1, HigherLowerPowerOfTwo.nextHigherPowerOfTwo(1)); // next higher power of two is 1 + assertEquals(16, HigherLowerPowerOfTwo.nextHigherPowerOfTwo(15)); // next higher power of two is 16 + assertEquals(8, HigherLowerPowerOfTwo.nextHigherPowerOfTwo(8)); // next higher power of two is 8 + assertEquals(16, HigherLowerPowerOfTwo.nextHigherPowerOfTwo(9)); // next higher power of two is 16 + } + + @Test + public void testNextLowerPowerOfTwo() { + assertEquals(16, HigherLowerPowerOfTwo.nextLowerPowerOfTwo(19)); // next lower power of two is 16 + assertEquals(1, HigherLowerPowerOfTwo.nextLowerPowerOfTwo(1)); // next lower power of two is 1 + assertEquals(8, HigherLowerPowerOfTwo.nextLowerPowerOfTwo(9)); // next lower power of two is 8 + assertEquals(8, HigherLowerPowerOfTwo.nextLowerPowerOfTwo(15)); // next lower power of two is 8 + assertEquals(8, HigherLowerPowerOfTwo.nextLowerPowerOfTwo(8)); // next lower power of two is 8 + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/HighestSetBitTest.java b/src/test/java/com/thealgorithms/bitmanipulation/HighestSetBitTest.java new file mode 100644 index 000000000000..532f06f79ab3 --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/HighestSetBitTest.java @@ -0,0 +1,39 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +/** + * Test case for Highest Set Bit + * @author Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi) + */ + +class HighestSetBitTest { + + @Test + void testHighestSetBit() { + assertFalse(HighestSetBit.findHighestSetBit(0).isPresent()); + assertEquals(0, HighestSetBit.findHighestSetBit(1).get()); + assertEquals(1, HighestSetBit.findHighestSetBit(2).get()); + assertEquals(1, HighestSetBit.findHighestSetBit(3).get()); + assertEquals(2, HighestSetBit.findHighestSetBit(4).get()); + assertEquals(2, HighestSetBit.findHighestSetBit(5).get()); + assertEquals(2, HighestSetBit.findHighestSetBit(7).get()); + assertEquals(3, HighestSetBit.findHighestSetBit(8).get()); + assertEquals(3, HighestSetBit.findHighestSetBit(9).get()); + assertEquals(3, HighestSetBit.findHighestSetBit(15).get()); + assertEquals(4, HighestSetBit.findHighestSetBit(16).get()); + assertEquals(4, HighestSetBit.findHighestSetBit(17).get()); + assertEquals(4, HighestSetBit.findHighestSetBit(31).get()); + assertEquals(5, HighestSetBit.findHighestSetBit(32).get()); + assertEquals(5, HighestSetBit.findHighestSetBit(33).get()); + assertEquals(7, HighestSetBit.findHighestSetBit(255).get()); + assertEquals(8, HighestSetBit.findHighestSetBit(256).get()); + assertEquals(8, HighestSetBit.findHighestSetBit(511).get()); + assertEquals(9, HighestSetBit.findHighestSetBit(512).get()); + assertThrows(IllegalArgumentException.class, () -> HighestSetBit.findHighestSetBit(-37)); + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/IndexOfRightMostSetBitTest.java b/src/test/java/com/thealgorithms/bitmanipulation/IndexOfRightMostSetBitTest.java new file mode 100644 index 000000000000..9bf804373438 --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/IndexOfRightMostSetBitTest.java @@ -0,0 +1,20 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +/** + * Test case for Index Of Right Most SetBit + * @author Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi) + */ + +class IndexOfRightMostSetBitTest { + + @Test + void testIndexOfRightMostSetBit() { + assertEquals(3, IndexOfRightMostSetBit.indexOfRightMostSetBit(40)); + assertEquals(-1, IndexOfRightMostSetBit.indexOfRightMostSetBit(0)); + assertEquals(3, IndexOfRightMostSetBit.indexOfRightMostSetBit(-40)); + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/IsEvenTest.java b/src/test/java/com/thealgorithms/bitmanipulation/IsEvenTest.java new file mode 100644 index 000000000000..0b2bfb0bb065 --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/IsEvenTest.java @@ -0,0 +1,17 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class IsEvenTest { + @Test + void testIsEven() { + assertTrue(IsEven.isEven(0)); + assertTrue(IsEven.isEven(2)); + assertTrue(IsEven.isEven(-12)); + assertFalse(IsEven.isEven(21)); + assertFalse(IsEven.isEven(-1)); + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/IsPowerTwoTest.java b/src/test/java/com/thealgorithms/bitmanipulation/IsPowerTwoTest.java new file mode 100644 index 000000000000..ffd9e4ef176d --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/IsPowerTwoTest.java @@ -0,0 +1,51 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * Test case for IsPowerTwo class + * @author Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi) + */ + +public class IsPowerTwoTest { + + @ParameterizedTest + @MethodSource("provideNumbersForPowerTwo") + public void testIsPowerTwo(int number, boolean expected) { + if (expected) { + assertTrue(IsPowerTwo.isPowerTwo(number)); + } else { + assertFalse(IsPowerTwo.isPowerTwo(number)); + } + } + + private static Stream<Arguments> provideNumbersForPowerTwo() { + return Stream.of(Arguments.of(1, Boolean.TRUE), // 2^0 + Arguments.of(2, Boolean.TRUE), // 2^1 + Arguments.of(4, Boolean.TRUE), // 2^2 + Arguments.of(8, Boolean.TRUE), // 2^3 + Arguments.of(16, Boolean.TRUE), // 2^4 + Arguments.of(32, Boolean.TRUE), // 2^5 + Arguments.of(64, Boolean.TRUE), // 2^6 + Arguments.of(128, Boolean.TRUE), // 2^7 + Arguments.of(256, Boolean.TRUE), // 2^8 + Arguments.of(1024, Boolean.TRUE), // 2^10 + Arguments.of(0, Boolean.FALSE), // 0 is not a power of two + Arguments.of(-1, Boolean.FALSE), // Negative number + Arguments.of(-2, Boolean.FALSE), // Negative number + Arguments.of(-4, Boolean.FALSE), // Negative number + Arguments.of(3, Boolean.FALSE), // 3 is not a power of two + Arguments.of(5, Boolean.FALSE), // 5 is not a power of two + Arguments.of(6, Boolean.FALSE), // 6 is not a power of two + Arguments.of(15, Boolean.FALSE), // 15 is not a power of two + Arguments.of(1000, Boolean.FALSE), // 1000 is not a power of two + Arguments.of(1023, Boolean.FALSE) // 1023 is not a power of two + ); + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/LowestSetBitTest.java b/src/test/java/com/thealgorithms/bitmanipulation/LowestSetBitTest.java new file mode 100644 index 000000000000..4c4d33640ad4 --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/LowestSetBitTest.java @@ -0,0 +1,86 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +/** + * Test case for Lowest Set Bit + * @author Prayas Kumar (https://github.com/prayas7102) + */ + +public class LowestSetBitTest { + + @Test + void testLowestSetBitWithPositiveNumber() { + // Test with a general positive number + assertEquals(2, LowestSetBit.isolateLowestSetBit(18)); // 18 in binary: 10010, lowest bit is 2 + } + + @Test + void testLowestSetBitWithZero() { + // Test with zero (edge case, no set bits) + assertEquals(0, LowestSetBit.isolateLowestSetBit(0)); // 0 has no set bits, result should be 0 + } + + @Test + void testLowestSetBitWithOne() { + // Test with number 1 (lowest set bit is 1 itself) + assertEquals(1, LowestSetBit.isolateLowestSetBit(1)); // 1 in binary: 0001, lowest bit is 1 + } + + @Test + void testLowestSetBitWithPowerOfTwo() { + // Test with a power of two (only one set bit) + assertEquals(16, LowestSetBit.isolateLowestSetBit(16)); // 16 in binary: 10000, lowest bit is 16 + } + + @Test + void testLowestSetBitWithAllBitsSet() { + // Test with a number with multiple set bits (like 7) + assertEquals(1, LowestSetBit.isolateLowestSetBit(7)); // 7 in binary: 111, lowest bit is 1 + } + + @Test + void testLowestSetBitWithNegativeNumber() { + // Test with a negative number (-1 in two's complement has all bits set) + assertEquals(1, LowestSetBit.isolateLowestSetBit(-1)); // -1 in two's complement is all 1s, lowest bit is 1 + } + + @Test + void testLowestSetBitWithLargeNumber() { + // Test with a large number + assertEquals(64, LowestSetBit.isolateLowestSetBit(448)); // 448 in binary: 111000000, lowest bit is 64 + } + + @Test + void testClearLowestSetBitFor18() { + // n = 18 (binary: 10010), expected result = 16 (binary: 10000) + assertEquals(16, LowestSetBit.clearLowestSetBit(18)); + } + + @Test + void testClearLowestSetBitFor10() { + // n = 10 (binary: 1010), expected result = 8 (binary: 1000) + assertEquals(8, LowestSetBit.clearLowestSetBit(10)); + } + + @Test + void testClearLowestSetBitFor7() { + // n = 7 (binary: 0111), expected result = 6 (binary: 0110) + assertEquals(6, LowestSetBit.clearLowestSetBit(7)); + } + + @Test + void testClearLowestSetBitFor0() { + // n = 0 (binary: 0000), no set bits to clear, expected result = 0 + assertEquals(0, LowestSetBit.clearLowestSetBit(0)); + } + + @Test + void testClearLowestSetBitForNegativeNumber() { + // Test negative number to see how it behaves with two's complement + // n = -1 (binary: all 1s in two's complement), expected result = -2 (clearing lowest set bit) + assertEquals(-2, LowestSetBit.clearLowestSetBit(-1)); + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/ModuloPowerOfTwoTest.java b/src/test/java/com/thealgorithms/bitmanipulation/ModuloPowerOfTwoTest.java new file mode 100644 index 000000000000..ff7f621a64c0 --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/ModuloPowerOfTwoTest.java @@ -0,0 +1,38 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class ModuloPowerOfTwoTest { + + @ParameterizedTest + @CsvSource({ + "10, 3, 2", + "15, 2, 3", + "20, 4, 4", + "7, 1, 1", + "5, 1, 1", + "36, 5, 4", + }) + void + testModuloPowerOfTwo(int x, int n, int expected) { + assertEquals(expected, ModuloPowerOfTwo.moduloPowerOfTwo(x, n)); + } + + @ParameterizedTest + @CsvSource({ + "10, 0", + "15, -2", + "20, -4", + "7, -1", + "5, -1", + }) + void + testNegativeExponent(int x, int n) { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> ModuloPowerOfTwo.moduloPowerOfTwo(x, n)); + assertEquals("The exponent must be positive", exception.getMessage()); + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/NextHigherSameBitCountTest.java b/src/test/java/com/thealgorithms/bitmanipulation/NextHigherSameBitCountTest.java new file mode 100644 index 000000000000..c8fb9ef21b60 --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/NextHigherSameBitCountTest.java @@ -0,0 +1,22 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class NextHigherSameBitCountTest { + + @ParameterizedTest + @CsvSource({ + "5, 6", // 101 -> 110 + "7, 11", // 0111 -> 1011 + "3, 5", // 011 -> 101 + "12, 17", // 001100 -> 010001 + "15, 23" // 01111 -> 10111 + }) + void + testNextHigherSameBitCount(int input, int expected) { + assertEquals(expected, NextHigherSameBitCount.nextHigherSameBitCount(input)); + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/NonRepeatingNumberFinderTest.java b/src/test/java/com/thealgorithms/bitmanipulation/NonRepeatingNumberFinderTest.java new file mode 100644 index 000000000000..fe2136fd7f04 --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/NonRepeatingNumberFinderTest.java @@ -0,0 +1,36 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * Test case for Non Repeating Number Finder + * This test class validates the functionality of the + * NonRepeatingNumberFinder by checking various scenarios. + * + * @author Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi) + */ +class NonRepeatingNumberFinderTest { + + @ParameterizedTest + @MethodSource("testCases") + void testNonRepeatingNumberFinder(int[] arr, int expected) { + assertEquals(expected, NonRepeatingNumberFinder.findNonRepeatingNumber(arr)); + } + + private static Arguments[] testCases() { + return new Arguments[] { + Arguments.of(new int[] {1, 2, 1, 2, 6}, 6), Arguments.of(new int[] {1, 2, 1, 2}, 0), // All numbers repeat + Arguments.of(new int[] {12}, 12), // Single non-repeating number + Arguments.of(new int[] {3, 5, 3, 4, 4}, 5), // More complex case + Arguments.of(new int[] {7, 8, 7, 9, 8, 10, 10}, 9), // Non-repeating in the middle + Arguments.of(new int[] {0, -1, 0, -1, 2}, 2), // Testing with negative numbers + Arguments.of(new int[] {Integer.MAX_VALUE, 1, 1}, Integer.MAX_VALUE), // Edge case with max int + Arguments.of(new int[] {2, 2, 3, 3, 4, 5, 4}, 5), // Mixed duplicates + Arguments.of(new int[] {}, 0) // Edge case: empty array (should be handled as per design) + }; + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/NumberAppearingOddTimesTest.java b/src/test/java/com/thealgorithms/bitmanipulation/NumberAppearingOddTimesTest.java new file mode 100644 index 000000000000..83d458319f3b --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/NumberAppearingOddTimesTest.java @@ -0,0 +1,49 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class NumberAppearingOddTimesTest { + + /** + * Parameterized test for findOddOccurrence method. Tests multiple + * input arrays and their expected results. + */ + @ParameterizedTest + @MethodSource("provideTestCases") + void testFindOddOccurrence(int[] input, int expected) { + assertEquals(expected, NumberAppearingOddTimes.findOddOccurrence(input)); + } + + /** + * Provides test cases for the parameterized test. + * Each test case consists of an input array and the expected result. + */ + private static Stream<Arguments> provideTestCases() { + return Stream.of( + // Single element appearing odd times (basic case) + Arguments.of(new int[] {5, 6, 7, 8, 6, 7, 5}, 8), + + // More complex case with multiple pairs + Arguments.of(new int[] {2, 3, 5, 4, 5, 2, 4, 3, 5, 2, 4, 4, 2}, 5), + + // Case with only one element appearing once + Arguments.of(new int[] {10, 10, 20, 20, 30}, 30), + + // Negative numbers with an odd occurrence + Arguments.of(new int[] {-5, -5, -3, -3, -7, -7, -7}, -7), + + // All elements cancel out to 0 (even occurrences of all elements) + Arguments.of(new int[] {1, 2, 1, 2}, 0), + + // Array with a single element (trivial case) + Arguments.of(new int[] {42}, 42), + + // Large array with repeated patterns + Arguments.of(new int[] {1, 1, 2, 2, 3, 3, 3, 4, 4}, 3)); + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/NumbersDifferentSignsTest.java b/src/test/java/com/thealgorithms/bitmanipulation/NumbersDifferentSignsTest.java new file mode 100644 index 000000000000..33e5ae048814 --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/NumbersDifferentSignsTest.java @@ -0,0 +1,46 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * Parameterized tests for NumbersDifferentSigns class, which checks + * if two integers have different signs using bitwise XOR. + * + * @author Bama Charan Chhandogi + */ +class NumbersDifferentSignsTest { + + @ParameterizedTest + @MethodSource("provideTestCases") + void testDifferentSigns(int num1, int num2, boolean expected) { + if (expected) { + assertTrue(NumbersDifferentSigns.differentSigns(num1, num2)); + } else { + assertFalse(NumbersDifferentSigns.differentSigns(num1, num2)); + } + } + + private static Stream<Arguments> provideTestCases() { + return Stream.of( + // Different signs (positive and negative) + Arguments.of(2, -1, Boolean.TRUE), Arguments.of(-3, 7, Boolean.TRUE), + + // Same signs (both positive) + Arguments.of(10, 20, Boolean.FALSE), Arguments.of(0, 5, Boolean.FALSE), // 0 is considered non-negative + + // Same signs (both negative) + Arguments.of(-5, -8, Boolean.FALSE), + + // Edge case: Large positive and negative values + Arguments.of(Integer.MAX_VALUE, Integer.MIN_VALUE, Boolean.TRUE), + + // Edge case: Same number (positive and negative) + Arguments.of(-42, -42, Boolean.FALSE), Arguments.of(42, 42, Boolean.FALSE)); + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/OneBitDifferenceTest.java b/src/test/java/com/thealgorithms/bitmanipulation/OneBitDifferenceTest.java new file mode 100644 index 000000000000..06b8d32d3406 --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/OneBitDifferenceTest.java @@ -0,0 +1,15 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class OneBitDifferenceTest { + + @ParameterizedTest + @CsvSource({"7, 5, true", "3, 2, true", "10, 8, true", "15, 15, false", "4, 1, false"}) + void testDifferByOneBit(int x, int y, boolean expected) { + assertEquals(expected, OneBitDifference.differByOneBit(x, y)); + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/OnesComplementTest.java b/src/test/java/com/thealgorithms/bitmanipulation/OnesComplementTest.java new file mode 100644 index 000000000000..6be4eb595f79 --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/OnesComplementTest.java @@ -0,0 +1,47 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +/** + * Test case for Highest Set Bit + * @author Abhinay Verma(https://github.com/Monk-AbhinayVerma) + */ +public class OnesComplementTest { + + @Test + public void testOnesComplementAllZeroes() { + + // Test cases with all-zero binary strings + assertEquals("1111", OnesComplement.onesComplement("0000")); + assertEquals("111", OnesComplement.onesComplement("000")); + assertEquals("11", OnesComplement.onesComplement("00")); + assertEquals("1", OnesComplement.onesComplement("0")); + } + + @Test + public void testOnesComplementAllOnes() { + // Test cases with all-one binary strings + assertEquals("0000", OnesComplement.onesComplement("1111")); + assertEquals("000", OnesComplement.onesComplement("111")); + assertEquals("00", OnesComplement.onesComplement("11")); + assertEquals("0", OnesComplement.onesComplement("1")); + } + + @Test + public void testOnesComplementMixedBits() { + // Test more mixed binary patterns + assertEquals("1010", OnesComplement.onesComplement("0101")); + assertEquals("0101", OnesComplement.onesComplement("1010")); + assertEquals("1100", OnesComplement.onesComplement("0011")); + assertEquals("0011", OnesComplement.onesComplement("1100")); + assertEquals("1001", OnesComplement.onesComplement("0110")); + } + + @Test + public void testOnesComplementEmptyString() { + // Test empty string scenario + assertEquals("", OnesComplement.onesComplement("")); + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/ParityCheckTest.java b/src/test/java/com/thealgorithms/bitmanipulation/ParityCheckTest.java new file mode 100644 index 000000000000..90147a61207b --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/ParityCheckTest.java @@ -0,0 +1,15 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class ParityCheckTest { + @Test + public void testIsOddParity() { + assertTrue(ParityCheck.checkParity(5)); // 101 has 2 ones (even parity) + assertFalse(ParityCheck.checkParity(7)); // 111 has 3 ones (odd parity) + assertFalse(ParityCheck.checkParity(8)); // 1000 has 1 one (odd parity) + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/ReverseBitsTest.java b/src/test/java/com/thealgorithms/bitmanipulation/ReverseBitsTest.java new file mode 100644 index 000000000000..5cfa82355ce7 --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/ReverseBitsTest.java @@ -0,0 +1,41 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class ReverseBitsTest { + + @ParameterizedTest + @MethodSource("provideTestCases") + void testReverseBits(int input, int expected) { + assertEquals(expected, ReverseBits.reverseBits(input)); + } + + private static Stream<Arguments> provideTestCases() { + return Stream.of( + // Edge case: All bits are 0 + Arguments.of(0, 0), + + // Edge case: All bits are 1 (Two’s complement representation of -1) + Arguments.of(-1, -1), + + // Case with random number 43261596 + Arguments.of(43261596, 964176192), + + // Case with maximum positive value for 32-bit integer + Arguments.of(Integer.MAX_VALUE, -2), + + // Case with minimum value (all bits 1 except the sign bit) + Arguments.of(Integer.MIN_VALUE, 1), + + // Case with a single bit set (2^0 = 1) + Arguments.of(1, Integer.MIN_VALUE), + + // Case with alternating bits: 0b101010...10 (in binary) + Arguments.of(0xAAAAAAAA, 0x55555555)); + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/SingleBitOperationsTest.java b/src/test/java/com/thealgorithms/bitmanipulation/SingleBitOperationsTest.java new file mode 100644 index 000000000000..6c447ec9805e --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/SingleBitOperationsTest.java @@ -0,0 +1,61 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class SingleBitOperationsTest { + + @ParameterizedTest + @MethodSource("provideFlipBitTestCases") + void testFlipBit(int input, int bit, int expected) { + assertEquals(expected, SingleBitOperations.flipBit(input, bit)); + } + + private static Stream<Arguments> provideFlipBitTestCases() { + return Stream.of(Arguments.of(3, 1, 1), // Binary: 11 -> 01 + Arguments.of(3, 3, 11) // Binary: 11 -> 1011 + ); + } + + @ParameterizedTest + @MethodSource("provideSetBitTestCases") + void testSetBit(int input, int bit, int expected) { + assertEquals(expected, SingleBitOperations.setBit(input, bit)); + } + + private static Stream<Arguments> provideSetBitTestCases() { + return Stream.of(Arguments.of(4, 0, 5), // 100 -> 101 + Arguments.of(4, 2, 4), // 100 -> 100 (bit already set) + Arguments.of(0, 1, 2), // 00 -> 10 + Arguments.of(10, 2, 14) // 1010 -> 1110 + ); + } + + @ParameterizedTest + @MethodSource("provideClearBitTestCases") + void testClearBit(int input, int bit, int expected) { + assertEquals(expected, SingleBitOperations.clearBit(input, bit)); + } + + private static Stream<Arguments> provideClearBitTestCases() { + return Stream.of(Arguments.of(7, 1, 5), // 111 -> 101 + Arguments.of(5, 1, 5) // 101 -> 101 (bit already cleared) + ); + } + + @ParameterizedTest + @MethodSource("provideGetBitTestCases") + void testGetBit(int input, int bit, int expected) { + assertEquals(expected, SingleBitOperations.getBit(input, bit)); + } + + private static Stream<Arguments> provideGetBitTestCases() { + return Stream.of(Arguments.of(6, 0, 0), // 110 -> Bit 0: 0 + Arguments.of(7, 1, 1) // 111 -> Bit 1: 1 + ); + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/SingleElementTest.java b/src/test/java/com/thealgorithms/bitmanipulation/SingleElementTest.java new file mode 100644 index 000000000000..2e2afe99b0da --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/SingleElementTest.java @@ -0,0 +1,33 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public final class SingleElementTest { + + /** + * Parameterized test to find the single non-duplicate element + * in the given arrays. + * + * @param arr the input array where every element appears twice except one + * @param expected the expected single element result + */ + @ParameterizedTest + @MethodSource("provideTestCases") + void testFindSingleElement(int[] arr, int expected) { + assertEquals(expected, SingleElement.findSingleElement(arr)); + } + + /** + * Provides test cases for the parameterized test. + * + * @return Stream of arguments consisting of arrays and expected results + */ + private static Stream<Arguments> provideTestCases() { + return Stream.of(Arguments.of(new int[] {1, 1, 2, 2, 4, 4, 3}, 3), Arguments.of(new int[] {1, 2, 2, 3, 3}, 1), Arguments.of(new int[] {10}, 10)); + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/SwapAdjacentBitsTest.java b/src/test/java/com/thealgorithms/bitmanipulation/SwapAdjacentBitsTest.java new file mode 100644 index 000000000000..12f0542b92f6 --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/SwapAdjacentBitsTest.java @@ -0,0 +1,29 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class SwapAdjacentBitsTest { + + @ParameterizedTest + @CsvSource({"2, 1", // 2 (binary: 10) -> 1 (binary: 01) + "43, 23", // 43 (binary: 101011) -> 23 (binary: 010111) + "153, 102", // 153 (binary: 10011001) -> 102 (binary: 01100110) + "15, 15", // 15 (binary: 1111) -> 15 (binary: 1111) (no change) + "0, 0", // 0 (binary: 0000) -> 0 (binary: 0000) (no change) + "1, 2", // 1 (binary: 01) -> 2 (binary: 10) + "170, 85", // 170 (binary: 10101010) -> 85 (binary: 01010101) + "85, 170", // 85 (binary: 01010101) -> 170 (binary: 10101010) + "255, 255", // 255 (binary: 11111111) -> 255 (binary: 11111111) (no change) + "128, 64", // 128 (binary: 10000000) -> 64 (binary: 01000000) + "1024, 2048", + "-1, -1", // -1 (all bits 1) remains -1 (no change due to two's complement) + "-2, -3", // -2 (binary: ...1110) -> -3 (binary: ...1101) + "2147483647, -1073741825", "-2147483648, -1073741824"}) + void + testSwapAdjacentBits(int input, int expected) { + assertEquals(expected, SwapAdjacentBits.swapAdjacentBits(input)); + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/TwosComplementTest.java b/src/test/java/com/thealgorithms/bitmanipulation/TwosComplementTest.java new file mode 100644 index 000000000000..578acc7af18c --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/TwosComplementTest.java @@ -0,0 +1,64 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +/** + * Test case for Highest Set Bit + * @author Abhinay Verma(https://github.com/Monk-AbhinayVerma) + */ +public class TwosComplementTest { + + @Test + public void testTwosComplementAllZeroes() { + assertEquals("10000", TwosComplement.twosComplement("0000")); + assertEquals("1000", TwosComplement.twosComplement("000")); + assertEquals("100", TwosComplement.twosComplement("00")); + assertEquals("10", TwosComplement.twosComplement("0")); + } + + @Test + public void testTwosComplementAllOnes() { + assertEquals("00001", TwosComplement.twosComplement("11111")); + assertEquals("0001", TwosComplement.twosComplement("1111")); + assertEquals("001", TwosComplement.twosComplement("111")); + assertEquals("01", TwosComplement.twosComplement("11")); + } + + @Test + public void testTwosComplementMixedBits() { + assertEquals("1111", TwosComplement.twosComplement("0001")); // 1 -> 1111 + assertEquals("1001", TwosComplement.twosComplement("0111")); // 0111 -> 1001 + assertEquals("11001", TwosComplement.twosComplement("00111")); // 00111 -> 11001 + assertEquals("011", TwosComplement.twosComplement("101")); // 101 -> 011 + } + + @Test + public void testTwosComplementSingleBit() { + assertEquals("10", TwosComplement.twosComplement("0")); // 0 -> 10 + assertEquals("1", TwosComplement.twosComplement("1")); // 1 -> 1 + } + + @Test + public void testTwosComplementWithLeadingZeroes() { + assertEquals("1111", TwosComplement.twosComplement("0001")); // 0001 -> 1111 + assertEquals("101", TwosComplement.twosComplement("011")); // 011 -> 101 + assertEquals("110", TwosComplement.twosComplement("010")); // 010 -> 110 + } + + @Test + public void testInvalidBinaryInput() { + // Test for invalid input that contains non-binary characters. + assertThrows(IllegalArgumentException.class, () -> TwosComplement.twosComplement("102")); + assertThrows(IllegalArgumentException.class, () -> TwosComplement.twosComplement("abc")); + assertThrows(IllegalArgumentException.class, () -> TwosComplement.twosComplement("10a01")); + } + + @Test + public void testEmptyInput() { + // Edge case: Empty input should result in an IllegalArgumentException. + assertThrows(IllegalArgumentException.class, () -> TwosComplement.twosComplement("")); + } +} diff --git a/src/test/java/com/thealgorithms/bitmanipulation/Xs3ConversionTest.java b/src/test/java/com/thealgorithms/bitmanipulation/Xs3ConversionTest.java new file mode 100644 index 000000000000..2e3242decba0 --- /dev/null +++ b/src/test/java/com/thealgorithms/bitmanipulation/Xs3ConversionTest.java @@ -0,0 +1,65 @@ +package com.thealgorithms.bitmanipulation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the Xs3Conversion class. + */ +public class Xs3ConversionTest { + + /** + * Test the xs3ToBinary method with an XS-3 number. + */ + @Test + public void testXs3ToBinary() { + int binary = Xs3Conversion.xs3ToBinary(0x4567); + assertEquals(1234, binary); // XS-3 0x4567 should convert to binary 1234 + } + + /** + * Test the binaryToXs3 method with a binary number. + */ + @Test + public void testBinaryToXs3() { + int xs3 = Xs3Conversion.binaryToXs3(1234); + assertEquals(0x4567, xs3); // Binary 1234 should convert to XS-3 0x4567 + } + + /** + * Test the xs3ToBinary method with zero. + */ + @Test + public void testXs3ToBinaryZero() { + int binary = Xs3Conversion.xs3ToBinary(0x0); + assertEquals(0, binary); // XS-3 0x0 should convert to binary 0 + } + + /** + * Test the binaryToXs3 method with zero. + */ + @Test + public void testBinaryToXs3Zero() { + int xs3 = Xs3Conversion.binaryToXs3(0); + assertEquals(0x0, xs3); // Binary 0 should convert to XS-3 0x0 + } + + /** + * Test the xs3ToBinary method with a single digit XS-3 number. + */ + @Test + public void testXs3ToBinarySingleDigit() { + int binary = Xs3Conversion.xs3ToBinary(0x5); + assertEquals(2, binary); // XS-3 0x5 should convert to binary 2 + } + + /** + * Test the binaryToXs3 method with a single digit binary number. + */ + @Test + public void testBinaryToXs3SingleDigit() { + int xs3 = Xs3Conversion.binaryToXs3(2); + assertEquals(0x5, xs3); // Binary 2 should convert to XS-3 0x5 + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/ADFGVXCipherTest.java b/src/test/java/com/thealgorithms/ciphers/ADFGVXCipherTest.java new file mode 100644 index 000000000000..4db856e40b84 --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/ADFGVXCipherTest.java @@ -0,0 +1,46 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class ADFGVXCipherTest { + + private final ADFGVXCipher adfgvxCipher = new ADFGVXCipher(); + + @Test + void testEncrypt() { + String message = "attack at 1200am"; + String key = "PRIVACY"; + + String encrypted = adfgvxCipher.encrypt(message, key); + assertEquals("DGDDDAGDDGAFADDFDADVDVFAADVX", encrypted); + } + + @Test + void testDecrypt() { + String encrypted = "DGDDDAGDDGAFADDFDADVDVFAADVX"; + String key = "PRIVACY"; + + String decrypted = adfgvxCipher.decrypt(encrypted, key); + assertEquals("ATTACKAT1200AM", decrypted); + } + + @Test + void testEmptyInput() { + String encrypted = adfgvxCipher.encrypt("", "PRIVACY"); + String decrypted = adfgvxCipher.decrypt("", "PRIVACY"); + assertEquals("", encrypted); + assertEquals("", decrypted); + } + + @Test + void testShortKey() { + String message = "TESTING"; + String key = "A"; + + String encrypted = adfgvxCipher.encrypt(message, key); + String decrypted = adfgvxCipher.decrypt(encrypted, key); + assertEquals("TESTING", decrypted); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/AESEncryptionTest.java b/src/test/java/com/thealgorithms/ciphers/AESEncryptionTest.java new file mode 100644 index 000000000000..2f0831e35064 --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/AESEncryptionTest.java @@ -0,0 +1,62 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import javax.crypto.SecretKey; +import org.junit.jupiter.api.Test; + +public class AESEncryptionTest { + + @Test + public void testGetSecretEncryptionKey() throws Exception { + SecretKey key = AESEncryption.getSecretEncryptionKey(); + assertNotNull(key, "Secret key should not be null"); + assertEquals(128, key.getEncoded().length * 8, "Key size should be 128 bits"); + } + + @Test + public void testEncryptText() throws Exception { + String plainText = "Hello World"; + SecretKey secKey = AESEncryption.getSecretEncryptionKey(); + byte[] cipherText = AESEncryption.encryptText(plainText, secKey); + + assertNotNull(cipherText, "Ciphertext should not be null"); + assertTrue(cipherText.length > 0, "Ciphertext should not be empty"); + } + + @Test + public void testDecryptText() throws Exception { + String plainText = "Hello World"; + SecretKey secKey = AESEncryption.getSecretEncryptionKey(); + byte[] cipherText = AESEncryption.encryptText(plainText, secKey); + + // Decrypt the ciphertext + String decryptedText = AESEncryption.decryptText(cipherText, secKey); + + assertNotNull(decryptedText, "Decrypted text should not be null"); + assertEquals(plainText, decryptedText, "Decrypted text should match the original plain text"); + } + + @Test + public void testEncryptDecrypt() throws Exception { + String plainText = "Hello AES!"; + SecretKey secKey = AESEncryption.getSecretEncryptionKey(); + + // Encrypt the plaintext + byte[] cipherText = AESEncryption.encryptText(plainText, secKey); + + // Decrypt the ciphertext + String decryptedText = AESEncryption.decryptText(cipherText, secKey); + + assertEquals(plainText, decryptedText, "Decrypted text should match the original plain text"); + } + + @Test + public void testBytesToHex() { + byte[] bytes = new byte[] {0, 1, 15, 16, (byte) 255}; // Test with diverse byte values + String hex = AESEncryption.bytesToHex(bytes); + assertEquals("00010F10FF", hex, "Hex representation should match the expected value"); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/AffineCipherTest.java b/src/test/java/com/thealgorithms/ciphers/AffineCipherTest.java new file mode 100644 index 000000000000..b1a2ce593a8e --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/AffineCipherTest.java @@ -0,0 +1,39 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class AffineCipherTest { + + @Test + public void testEncryptMessage() { + String plaintext = "AFFINE CIPHER"; + char[] msg = plaintext.toCharArray(); + String expectedCiphertext = "UBBAHK CAPJKX"; // Expected ciphertext after encryption + + String actualCiphertext = AffineCipher.encryptMessage(msg); + assertEquals(expectedCiphertext, actualCiphertext, "The encryption result should match the expected ciphertext."); + } + + @Test + public void testEncryptDecrypt() { + String plaintext = "HELLO WORLD"; + char[] msg = plaintext.toCharArray(); + + String ciphertext = AffineCipher.encryptMessage(msg); + String decryptedText = AffineCipher.decryptCipher(ciphertext); + + assertEquals(plaintext, decryptedText, "Decrypted text should match the original plaintext."); + } + + @Test + public void testSpacesHandledInEncryption() { + String plaintext = "HELLO WORLD"; + char[] msg = plaintext.toCharArray(); + String expectedCiphertext = "JKZZY EYXZT"; + + String actualCiphertext = AffineCipher.encryptMessage(msg); + assertEquals(expectedCiphertext, actualCiphertext, "The encryption should handle spaces correctly."); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/AtbashTest.java b/src/test/java/com/thealgorithms/ciphers/AtbashTest.java new file mode 100644 index 000000000000..ec34bc26ad72 --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/AtbashTest.java @@ -0,0 +1,43 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class AtbashTest { + + @ParameterizedTest + @MethodSource("cipherTestProvider") + public void testAtbashCipher(String input, String expected) { + AtbashCipher cipher = new AtbashCipher(input); + assertEquals(expected, cipher.convert()); + } + + private static Stream<Arguments> cipherTestProvider() { + return Stream.of( + // Basic tests with lowercase and uppercase + Arguments.of("Hello", "Svool"), Arguments.of("WORLD", "DLIOW"), + + // Mixed case with spaces and punctuation + Arguments.of("Hello World!", "Svool Dliow!"), Arguments.of("123 ABC xyz", "123 ZYX cba"), + + // Palindromes and mixed cases + Arguments.of("madam", "nzwzn"), Arguments.of("Palindrome", "Kzormwilnv"), + + // Non-alphabetic characters should remain unchanged + Arguments.of("@cipher 123!", "@xrksvi 123!"), Arguments.of("no-change", "ml-xszmtv"), + + // Empty string and single characters + Arguments.of("", ""), Arguments.of("A", "Z"), Arguments.of("z", "a"), + + // Numbers and symbols + Arguments.of("!@#123", "!@#123"), + + // Full sentence with uppercase, lowercase, symbols, and numbers + Arguments.of("Hello World! 123, @cipher abcDEF ZYX 987 madam zzZ Palindrome!", "Svool Dliow! 123, @xrksvi zyxWVU ABC 987 nzwzn aaA Kzormwilnv!"), + Arguments.of("Svool Dliow! 123, @xrksvi zyxWVU ABC 987 nzwzn aaA Kzormwilnv!", "Hello World! 123, @cipher abcDEF ZYX 987 madam zzZ Palindrome!")); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/AutokeyTest.java b/src/test/java/com/thealgorithms/ciphers/AutokeyTest.java new file mode 100644 index 000000000000..52ecff7cdeee --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/AutokeyTest.java @@ -0,0 +1,36 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class AutokeyCipherTest { + + Autokey autokeyCipher = new Autokey(); + + @Test + void autokeyEncryptTest() { + // given + String plaintext = "MEET AT DAWN"; + String keyword = "QUEEN"; + + // when + String cipherText = autokeyCipher.encrypt(plaintext, keyword); + + // then + assertEquals("CYIXNFHEPN", cipherText); + } + + @Test + void autokeyDecryptTest() { + // given + String ciphertext = "CYIX NF HEPN"; + String keyword = "QUEEN"; + + // when + String plainText = autokeyCipher.decrypt(ciphertext, keyword); + + // then + assertEquals("MEETATDAWN", plainText); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/BaconianCipherTest.java b/src/test/java/com/thealgorithms/ciphers/BaconianCipherTest.java new file mode 100644 index 000000000000..bb1ae5a7e4c2 --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/BaconianCipherTest.java @@ -0,0 +1,34 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class BaconianCipherTest { + + BaconianCipher baconianCipher = new BaconianCipher(); + + @Test + void baconianCipherEncryptTest() { + // given + String plaintext = "MEET AT DAWN"; + + // when + String cipherText = baconianCipher.encrypt(plaintext); + + // then + assertEquals("ABBAAAABAAAABAABAABBAAAAABAABBAAABBAAAAABABBAABBAB", cipherText); + } + + @Test + void baconianCipherDecryptTest() { + // given + String ciphertext = "ABBAAAABAAAABAABAABBAAAAABAABBAAABBAAAAABABBAABBAB"; + + // when + String plainText = baconianCipher.decrypt(ciphertext); + + // then + assertEquals("MEETATDAWN", plainText); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/BlowfishTest.java b/src/test/java/com/thealgorithms/ciphers/BlowfishTest.java new file mode 100644 index 000000000000..ef5e634f781b --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/BlowfishTest.java @@ -0,0 +1,38 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class BlowfishTest { + + Blowfish blowfish = new Blowfish(); + + @Test + void testEncrypt() { + // given + String plainText = "123456abcd132536"; + String key = "aabb09182736ccdd"; + String expectedOutput = "d748ec383d3405f7"; + + // when + String cipherText = blowfish.encrypt(plainText, key); + + // then + assertEquals(expectedOutput, cipherText); + } + + @Test + void testDecrypt() { + // given + String cipherText = "d748ec383d3405f7"; + String key = "aabb09182736ccdd"; + String expectedOutput = "123456abcd132536"; + + // when + String plainText = blowfish.decrypt(cipherText, key); + + // then + assertEquals(expectedOutput, plainText); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/CaesarTest.java b/src/test/java/com/thealgorithms/ciphers/CaesarTest.java new file mode 100644 index 000000000000..7aa41c4cf423 --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/CaesarTest.java @@ -0,0 +1,46 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class CaesarTest { + + Caesar caesar = new Caesar(); + + @Test + void caesarEncryptTest() { + // given + String textToEncrypt = "Encrypt this text"; + + // when + String cipherText = caesar.encode(textToEncrypt, 5); + + // then + assertEquals("Jshwduy ymnx yjcy", cipherText); + } + + @Test + void caesarDecryptTest() { + // given + String encryptedText = "Jshwduy ymnx yjcy"; + + // when + String cipherText = caesar.decode(encryptedText, 5); + + // then + assertEquals("Encrypt this text", cipherText); + } + + @Test + void caesarBruteForce() { + // given + String encryptedText = "Jshwduy ymnx yjcy"; + + // when + String[] allPossibleAnswers = caesar.bruteforce(encryptedText); + + assertEquals(27, allPossibleAnswers.length); + assertEquals("Encrypt this text", allPossibleAnswers[5]); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/ColumnarTranspositionCipherTest.java b/src/test/java/com/thealgorithms/ciphers/ColumnarTranspositionCipherTest.java new file mode 100644 index 000000000000..8deae45099d6 --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/ColumnarTranspositionCipherTest.java @@ -0,0 +1,47 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class ColumnarTranspositionCipherTest { + private String keyword; + private String plaintext; + + @BeforeEach + public void setUp() { + keyword = "keyword"; + plaintext = "This is a test message for Columnar Transposition Cipher"; + } + + @Test + public void testEncryption() { + String encryptedText = ColumnarTranspositionCipher.encrypt(plaintext, keyword); + assertNotNull(encryptedText, "The encrypted text should not be null."); + assertFalse(encryptedText.isEmpty(), "The encrypted text should not be empty."); + // Check if the encrypted text is different from the plaintext + assertNotEquals(plaintext, encryptedText, "The encrypted text should be different from the plaintext."); + } + + @Test + public void testDecryption() { + String encryptedText = ColumnarTranspositionCipher.encrypt(plaintext, keyword); + String decryptedText = ColumnarTranspositionCipher.decrypt(); + + assertEquals(plaintext.replaceAll(" ", ""), decryptedText.replaceAll(" ", ""), "The decrypted text should match the original plaintext, ignoring spaces."); + assertEquals(encryptedText, ColumnarTranspositionCipher.encrypt(plaintext, keyword), "The encrypted text should be the same when encrypted again."); + } + + @Test + public void testLongPlainText() { + String longText = "This is a significantly longer piece of text to test the encryption and decryption capabilities of the Columnar Transposition Cipher. It should handle long strings gracefully."; + String encryptedText = ColumnarTranspositionCipher.encrypt(longText, keyword); + String decryptedText = ColumnarTranspositionCipher.decrypt(); + assertEquals(longText.replaceAll(" ", ""), decryptedText.replaceAll(" ", ""), "The decrypted text should match the original long plaintext, ignoring spaces."); + assertEquals(encryptedText, ColumnarTranspositionCipher.encrypt(longText, keyword), "The encrypted text should be the same when encrypted again."); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/DESTest.java b/src/test/java/com/thealgorithms/ciphers/DESTest.java new file mode 100644 index 000000000000..ddc643a6eb35 --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/DESTest.java @@ -0,0 +1,55 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +// Test example taken from https://page.math.tu-berlin.de/~kant/teaching/hess/krypto-ws2006/des.htm +public class DESTest { + + DES des; + + @BeforeEach + public void setUp() { + des = new DES("0000111000110010100100100011001011101010011011010000110101110011"); + } + + @Test + void testEncrypt() { + // given + String plainText = "Your lips are smoother than vaseline\r\n"; + // This is equal to + // c0999fdde378d7ed727da00bca5a84ee47f269a4d6438190d9d52f78f5358499828ac9b453e0e653 in + // hexadecimal + String expectedOutput = "11000000100110011001111111011101111000110111100011010111111" + + "011010111001001111101101000000000101111001010010110101000010011101110010001111111001" + + "001101001101001001101011001000011100000011001000011011001110101010010111101111000111" + + "101010011010110000100100110011000001010001010110010011011010001010011111000001110011001010011"; + + // when + String cipherText = des.encrypt(plainText); + + // then + assertEquals(expectedOutput, cipherText); + } + + @Test + void testDecrypt() { + // given + // This is equal to + // c0999fdde378d7ed727da00bca5a84ee47f269a4d6438190d9d52f78f5358499828ac9b453e0e653 in + // hexadecimal + String cipherText = "11000000100110011001111111011101111000110111100011010111111" + + "011010111001001111101101000000000101111001010010110101000010011101110010001111111001" + + "001101001101001001101011001000011100000011001000011011001110101010010111101111000111" + + "101010011010110000100100110011000001010001010110010011011010001010011111000001110011001010011"; + String expectedOutput = "Your lips are smoother than vaseline\r\n"; + + // when + String plainText = des.decrypt(cipherText); + + // then + assertEquals(expectedOutput, plainText); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/DiffieHellmanTest.java b/src/test/java/com/thealgorithms/ciphers/DiffieHellmanTest.java new file mode 100644 index 000000000000..6255ad22ab56 --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/DiffieHellmanTest.java @@ -0,0 +1,38 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.math.BigInteger; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class DiffieHellmanTest { + + // Test for public value calculation using instance methods + @ParameterizedTest + @MethodSource("provideTestData") + public void testCalculatePublicValue(BigInteger base, BigInteger secret, BigInteger prime, BigInteger publicExpected, BigInteger sharedExpected) { + DiffieHellman dh = new DiffieHellman(base, secret, prime); // Create an instance of DiffieHellman + assertEquals(publicExpected, dh.calculatePublicValue()); // Call instance method + } + + // Test for shared secret calculation using instance methods + @ParameterizedTest + @MethodSource("provideTestData") + public void testCalculateSharedSecret(BigInteger base, BigInteger secret, BigInteger prime, BigInteger publicExpected, BigInteger sharedExpected) { + DiffieHellman dh = new DiffieHellman(base, secret, prime); // Create an instance of DiffieHellman + assertEquals(sharedExpected, dh.calculateSharedSecret(publicExpected)); // Call instance method + } + + // Provide test data for both public key and shared secret calculation + private static Stream<Arguments> provideTestData() { + return Stream.of(createTestArgs(5, 6, 23, 8, 13), createTestArgs(2, 5, 13, 6, 2)); + } + + // Helper method for arguments + private static Arguments createTestArgs(long base, long secret, long prime, long publicExpected, long sharedExpected) { + return Arguments.of(BigInteger.valueOf(base), BigInteger.valueOf(secret), BigInteger.valueOf(prime), BigInteger.valueOf(publicExpected), BigInteger.valueOf(sharedExpected)); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/ECCTest.java b/src/test/java/com/thealgorithms/ciphers/ECCTest.java new file mode 100644 index 000000000000..701f801af1c8 --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/ECCTest.java @@ -0,0 +1,106 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import java.math.BigInteger; +import org.junit.jupiter.api.Test; + +/** + * ECCTest - Unit tests for the ECC (Elliptic Curve Cryptography) implementation. + * This class contains various test cases to validate the encryption and decryption functionalities. + * It ensures the correctness and randomness of ECC operations. + * + * @author xuyang + */ +public class ECCTest { + ECC ecc = new ECC(256); // Generate a 256-bit ECC key pair. Calls generateKeys(bits) to create keys including privateKey and publicKey. + + /** + * Test the encryption functionality: convert plaintext to ciphertext and output relevant encryption data. + */ + @Test + void testEncrypt() { + String textToEncrypt = "Elliptic Curve Cryptography"; + + ECC.ECPoint[] cipherText = ecc.encrypt(textToEncrypt); // Perform encryption + + // Output private key information + System.out.println("Private Key: " + ecc.getPrivateKey()); + + // Output elliptic curve parameters + ECC.EllipticCurve curve = ecc.getCurve(); + System.out.println("Elliptic Curve Parameters:"); + System.out.println("a: " + curve.getA()); + System.out.println("b: " + curve.getB()); + System.out.println("p: " + curve.getP()); + System.out.println("Base Point G: " + curve.getBasePoint()); + + // Verify that the ciphertext is not empty + assertEquals(cipherText.length, 2); // Check if the ciphertext contains two points (R and S) + + // Output the encrypted coordinate points + System.out.println("Encrypted Points:"); + for (ECC.ECPoint point : cipherText) { + System.out.println(point); // Calls ECPoint's toString() method + } + } + + /** + * Test the decryption functionality: convert ciphertext back to plaintext using known private key and elliptic curve parameters. + */ + @Test + void testDecryptWithKnownValues() { + // 1. Define the known private key + BigInteger knownPrivateKey = new BigInteger("28635978664199231399690075483195602260051035216440375973817268759912070302603"); + + // 2. Define the known elliptic curve parameters + BigInteger a = new BigInteger("64505295837372135469230827475895976532873592609649950000895066186842236488761"); // Replace with known a value + BigInteger b = new BigInteger("89111668838830965251111555638616364203833415376750835901427122343021749874324"); // Replace with known b value + BigInteger p = new BigInteger("107276428198310591598877737561885175918069075479103276920057092968372930219921"); // Replace with known p value + ECC.ECPoint basePoint = new ECC.ECPoint(new BigInteger("4"), new BigInteger("8")); // Replace with known base point coordinates + + // 3. Create the elliptic curve object + ECC.EllipticCurve curve = new ECC.EllipticCurve(a, b, p, basePoint); + + // 4. Define the known ciphertext containing two ECPoints (R, S) + ECC.ECPoint rPoint = new ECC.ECPoint(new BigInteger("103077584019003058745849614420912636617007257617156724481937620119667345237687"), new BigInteger("68193862907937248121971710522760893811582068323088661566426323952783362061817")); + ECC.ECPoint sPoint = new ECC.ECPoint(new BigInteger("31932232426664380635434632300383525435115368414929679432313910646436992147798"), new BigInteger("77299754382292904069123203569944908076819220797512755280123348910207308129766")); + ECC.ECPoint[] cipherText = new ECC.ECPoint[] {rPoint, sPoint}; + + // 5. Create an ECC instance and set the private key and curve parameters + ecc.setPrivateKey(knownPrivateKey); // Use setter method to set the private key + ecc.setCurve(curve); // Use setter method to set the elliptic curve + + // 6. Decrypt the known ciphertext + String decryptedMessage = ecc.decrypt(cipherText); + + // 7. Compare the decrypted plaintext with the expected value + String expectedMessage = "Elliptic Curve Cryptography"; // Expected plaintext + assertEquals(expectedMessage, decryptedMessage); + } + + /** + * Test that encrypting the same plaintext with ECC produces different ciphertexts. + */ + @Test + void testCipherTextRandomness() { + String message = "Elliptic Curve Cryptography"; + + ECC.ECPoint[] cipherText1 = ecc.encrypt(message); + ECC.ECPoint[] cipherText2 = ecc.encrypt(message); + + assertNotEquals(cipherText1, cipherText2); // Ensure that the two ciphertexts are different + } + + /** + * Test the entire ECC encryption and decryption process. + */ + @Test + void testECCEncryptionAndDecryption() { + String textToEncrypt = "Elliptic Curve Cryptography"; + ECC.ECPoint[] cipherText = ecc.encrypt(textToEncrypt); + String decryptedText = ecc.decrypt(cipherText); + assertEquals(textToEncrypt, decryptedText); // Verify that the decrypted text matches the original text + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/HillCipherTest.java b/src/test/java/com/thealgorithms/ciphers/HillCipherTest.java new file mode 100644 index 000000000000..8121b6177aa9 --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/HillCipherTest.java @@ -0,0 +1,36 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class HillCipherTest { + + HillCipher hillCipher = new HillCipher(); + + @Test + void hillCipherEncryptTest() { + // given + String message = "ACT"; // Plaintext message + int[][] keyMatrix = {{6, 24, 1}, {13, 16, 10}, {20, 17, 15}}; // Encryption key matrix + + // when + String cipherText = hillCipher.encrypt(message, keyMatrix); + + // then + assertEquals("POH", cipherText); + } + + @Test + void hillCipherDecryptTest() { + // given + String cipherText = "POH"; // Ciphertext message + int[][] inverseKeyMatrix = {{8, 5, 10}, {21, 8, 21}, {21, 12, 8}}; // Decryption (inverse key) matrix + + // when + String plainText = hillCipher.decrypt(cipherText, inverseKeyMatrix); + + // then + assertEquals("ACT", plainText); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/MonoAlphabeticTest.java b/src/test/java/com/thealgorithms/ciphers/MonoAlphabeticTest.java new file mode 100644 index 000000000000..b1a8c78c952e --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/MonoAlphabeticTest.java @@ -0,0 +1,29 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class MonoAlphabeticTest { + + // Test for both encryption and decryption with different keys + @ParameterizedTest + @MethodSource("provideTestData") + public void testEncryptDecrypt(String plainText, String key, String encryptedText) { + // Test encryption + String actualEncrypted = MonoAlphabetic.encrypt(plainText, key); + assertEquals(encryptedText, actualEncrypted, "Encryption failed for input: " + plainText + " with key: " + key); + + // Test decryption + String actualDecrypted = MonoAlphabetic.decrypt(encryptedText, key); + assertEquals(plainText, actualDecrypted, "Decryption failed for input: " + encryptedText + " with key: " + key); + } + + // Provide test data for both encryption and decryption + private static Stream<Arguments> provideTestData() { + return Stream.of(Arguments.of("HELLO", "MNBVCXZLKJHGFDSAPOIUYTREWQ", "LCGGS"), Arguments.of("JAVA", "MNBVCXZLKJHGFDSAPOIUYTREWQ", "JMTM"), Arguments.of("HELLO", "QWERTYUIOPLKJHGFDSAZXCVBNM", "ITKKG"), Arguments.of("JAVA", "QWERTYUIOPLKJHGFDSAZXCVBNM", "PQCQ")); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/PlayfairTest.java b/src/test/java/com/thealgorithms/ciphers/PlayfairTest.java new file mode 100644 index 000000000000..fa497e4682e8 --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/PlayfairTest.java @@ -0,0 +1,37 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class PlayfairTest { + + @Test + public void testEncryption() { + PlayfairCipher playfairCipher = new PlayfairCipher("KEYWORD"); + + String plaintext = "HELLO"; + String encryptedText = playfairCipher.encrypt(plaintext); + assertEquals("GYIZSC", encryptedText); + } + + @Test + public void testDecryption() { + PlayfairCipher playfairCipher = new PlayfairCipher("KEYWORD"); + + String encryptedText = "UDRIYP"; + String decryptedText = playfairCipher.decrypt(encryptedText); + assertEquals("NEBFVH", decryptedText); + } + + @Test + public void testEncryptionAndDecryption() { + PlayfairCipher playfairCipher = new PlayfairCipher("KEYWORD"); + + String plaintext = "PLAYFAIR"; + String encryptedText = playfairCipher.encrypt(plaintext); + String decryptedText = playfairCipher.decrypt(encryptedText); + + assertEquals(plaintext, decryptedText); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/PolybiusTest.java b/src/test/java/com/thealgorithms/ciphers/PolybiusTest.java new file mode 100644 index 000000000000..543a2fe59685 --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/PolybiusTest.java @@ -0,0 +1,45 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class PolybiusTest { + + @Test + void testEncrypt() { + // Given + String plaintext = "HELLOWORLD"; + + // When + String actual = Polybius.encrypt(plaintext); + + // Then + assertEquals("12042121244124322103", actual); + } + + @Test + void testDecrypt() { + // Given + String ciphertext = "12042121244124322103"; + + // When + String actual = Polybius.decrypt(ciphertext); + + // Then + assertEquals("HELLOWORLD", actual); + } + + @Test + void testIsTextTheSameAfterEncryptionAndDecryption() { + // Given + String plaintext = "HELLOWORLD"; + + // When + String encryptedText = Polybius.encrypt(plaintext); + String actual = Polybius.decrypt(encryptedText); + + // Then + assertEquals(plaintext, actual); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/RSATest.java b/src/test/java/com/thealgorithms/ciphers/RSATest.java new file mode 100644 index 000000000000..577f56426be8 --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/RSATest.java @@ -0,0 +1,64 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.math.BigInteger; +import org.junit.jupiter.api.Test; + +class RSATest { + + private final RSA rsa = new RSA(1024); + + @Test + void testEncryptDecryptString() { + String originalMessage = "Such secure"; + String encryptedMessage = rsa.encrypt(originalMessage); + String decryptedMessage = rsa.decrypt(encryptedMessage); + assertEquals(originalMessage, decryptedMessage); + } + + @Test + void testEncryptDecryptBigInteger() { + BigInteger originalMessage = new BigInteger("12345678901234567890"); + BigInteger encryptedMessage = rsa.encrypt(originalMessage); + BigInteger decryptedMessage = rsa.decrypt(encryptedMessage); + assertEquals(originalMessage, decryptedMessage); + } + + @Test + void testEmptyMessage() { + String originalMessage = ""; + assertThrows(IllegalArgumentException.class, () -> rsa.encrypt(originalMessage)); + assertThrows(IllegalArgumentException.class, () -> rsa.decrypt(originalMessage)); + } + + @Test + void testDifferentKeySizes() { + // Testing with 512-bit RSA keys + RSA smallRSA = new RSA(512); + String originalMessage = "Test with smaller key"; + + String encryptedMessage = smallRSA.encrypt(originalMessage); + String decryptedMessage = smallRSA.decrypt(encryptedMessage); + + assertEquals(originalMessage, decryptedMessage); + + // Testing with 2048-bit RSA keys + RSA largeRSA = new RSA(2048); + String largeOriginalMessage = "Test with larger key"; + + String largeEncryptedMessage = largeRSA.encrypt(largeOriginalMessage); + String largeDecryptedMessage = largeRSA.decrypt(largeEncryptedMessage); + + assertEquals(largeOriginalMessage, largeDecryptedMessage); + } + + @Test + void testSpecialCharacters() { + String originalMessage = "Hello, RSA! @2024#"; + String encryptedMessage = rsa.encrypt(originalMessage); + String decryptedMessage = rsa.decrypt(encryptedMessage); + assertEquals(originalMessage, decryptedMessage); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/RailFenceTest.java b/src/test/java/com/thealgorithms/ciphers/RailFenceTest.java new file mode 100644 index 000000000000..2bfa704e3d0b --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/RailFenceTest.java @@ -0,0 +1,62 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class RailFenceTest { + + @Test + void testEncryption() { + RailFenceCipher cipher = new RailFenceCipher(); + + String input = "We are discovered! Flee at once"; + int rails = 3; + String encrypted = cipher.encrypt(input, rails); + assertEquals("Wrivdlaneaedsoee!Fe toc cr e e", encrypted); + + String singleChar = "A"; + int singleRail = 2; + String encryptedSingleChar = cipher.encrypt(singleChar, singleRail); + assertEquals("A", encryptedSingleChar); + + String shortString = "Hello"; + int moreRails = 10; + String encryptedShortString = cipher.encrypt(shortString, moreRails); + assertEquals("Hello", encryptedShortString); + + String inputSingleRail = "Single line"; + int singleRailOnly = 1; + String encryptedSingleRail = cipher.encrypt(inputSingleRail, singleRailOnly); + assertEquals("Single line", encryptedSingleRail); + } + + @Test + void testDecryption() { + RailFenceCipher cipher = new RailFenceCipher(); + + // Scenario 1: Basic decryption with multiple rails + String encryptedInput = "Wrivdlaneaedsoee!Fe toc cr e e"; + int rails = 3; + String decrypted = cipher.decrypt(encryptedInput, rails); + assertEquals("We are discovered! Flee at once", decrypted); + + // Scenario 2: Single character string decryption + String encryptedSingleChar = "A"; + int singleRail = 2; // More than 1 rail + String decryptedSingleChar = cipher.decrypt(encryptedSingleChar, singleRail); + assertEquals("A", decryptedSingleChar); + + // Scenario 3: String length less than the number of rails + String encryptedShortString = "Hello"; + int moreRails = 10; // More rails than characters + String decryptedShortString = cipher.decrypt(encryptedShortString, moreRails); + assertEquals("Hello", decryptedShortString); + + // Scenario 4: Single rail decryption (output should be the same as input) + String encryptedSingleRail = "Single line"; + int singleRailOnly = 1; + String decryptedSingleRail = cipher.decrypt(encryptedSingleRail, singleRailOnly); + assertEquals("Single line", decryptedSingleRail); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/SimpleSubCipherTest.java b/src/test/java/com/thealgorithms/ciphers/SimpleSubCipherTest.java new file mode 100644 index 000000000000..f593a07d89b7 --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/SimpleSubCipherTest.java @@ -0,0 +1,36 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class SimpleSubCipherTest { + + SimpleSubCipher simpleSubCipher = new SimpleSubCipher(); + + @Test + void simpleSubCipherEncryptTest() { + // given + String text = "defend the east wall of the castle"; + String cipherSmall = "phqgiumeaylnofdxjkrcvstzwb"; + + // when + String cipherText = simpleSubCipher.encode(text, cipherSmall); + + // then + assertEquals("giuifg cei iprc tpnn du cei qprcni", cipherText); + } + + @Test + void simpleSubCipherDecryptTest() { + // given + String encryptedText = "giuifg cei iprc tpnn du cei qprcni"; + String cipherSmall = "phqgiumeaylnofdxjkrcvstzwb"; + + // when + String decryptedText = simpleSubCipher.decode(encryptedText, cipherSmall); + + // then + assertEquals("defend the east wall of the castle", decryptedText); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/VigenereTest.java b/src/test/java/com/thealgorithms/ciphers/VigenereTest.java new file mode 100644 index 000000000000..7f94e5731989 --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/VigenereTest.java @@ -0,0 +1,80 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class VigenereTest { + + Vigenere vigenere = new Vigenere(); + + @Test + void testVigenereEncryptDecrypt() { + String text = "Hello World!"; + String key = "suchsecret"; + + String encryptedText = vigenere.encrypt(text, key); + String decryptedText = vigenere.decrypt(encryptedText, key); + + assertEquals("Zynsg Aqipw!", encryptedText); + assertEquals("Hello World!", decryptedText); + } + + @Test + void testWithEmptyMessage() { + String text = ""; + String key = "anykey"; + + String encryptedText = vigenere.encrypt(text, key); + String decryptedText = vigenere.decrypt(encryptedText, key); + + assertEquals("", encryptedText); + assertEquals("", decryptedText); + } + + @Test + void testWithEmptyKey() { + String text = "This should remain the same"; + String key = ""; + + assertThrows(IllegalArgumentException.class, () -> vigenere.encrypt(text, key)); + assertThrows(IllegalArgumentException.class, () -> vigenere.decrypt(text, key)); + } + + @Test + void testWithNumbersInMessage() { + String text = "Vigenere123!"; + String key = "cipher"; + + String encryptedText = vigenere.encrypt(text, key); + String decryptedText = vigenere.decrypt(encryptedText, key); + + assertEquals("Xqvlrvtm123!", encryptedText); + assertEquals(text, decryptedText); + } + + @Test + void testLongerKeyThanMessage() { + String text = "Short"; + String key = "VeryLongSecretKey"; + + String encryptedText = vigenere.encrypt(text, key); + String decryptedText = vigenere.decrypt(encryptedText, key); + + assertEquals("Nlfpe", encryptedText); + assertEquals(text, decryptedText); + } + + @Test + void testUppercaseMessageAndKey() { + String text = "HELLO"; + String key = "SECRET"; + + String encryptedText = vigenere.encrypt(text, key); + String decryptedText = vigenere.decrypt(encryptedText, key); + + assertEquals("ZINCS", encryptedText); + assertEquals(text, decryptedText); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/XORCipherTest.java b/src/test/java/com/thealgorithms/ciphers/XORCipherTest.java new file mode 100644 index 000000000000..fdfe640cc19b --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/XORCipherTest.java @@ -0,0 +1,85 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class XORCipherTest { + + @Test + void xorEncryptDecryptTest() { + String plaintext = "My t&xt th@t will be ençrypted..."; + String key = "My ç&cret key!"; + + String cipherText = XORCipher.encrypt(plaintext, key); + String decryptedText = XORCipher.decrypt(cipherText, key); + + assertEquals("My t&xt th@t will be ençrypted...", decryptedText); + } + + @Test + void testEmptyPlaintext() { + String plaintext = ""; + String key = "anykey"; + + String cipherText = XORCipher.encrypt(plaintext, key); + String decryptedText = XORCipher.decrypt(cipherText, key); + + assertEquals("", cipherText); + assertEquals("", decryptedText); + } + + @Test + void testEmptyKey() { + String plaintext = "Hello World!"; + String key = ""; + + assertThrows(IllegalArgumentException.class, () -> XORCipher.encrypt(plaintext, key)); + assertThrows(IllegalArgumentException.class, () -> XORCipher.decrypt(plaintext, key)); + } + + @Test + void testShortKey() { + String plaintext = "Short message"; + String key = "k"; + + String cipherText = XORCipher.encrypt(plaintext, key); + String decryptedText = XORCipher.decrypt(cipherText, key); + + assertEquals(plaintext, decryptedText); + } + + @Test + void testNonASCIICharacters() { + String plaintext = "こんにちは世界"; // "Hello World" in Japanese (Konichiwa Sekai) + String key = "key"; + + String cipherText = XORCipher.encrypt(plaintext, key); + String decryptedText = XORCipher.decrypt(cipherText, key); + + assertEquals(plaintext, decryptedText); + } + + @Test + void testSameKeyAndPlaintext() { + String plaintext = "samekey"; + String key = "samekey"; + + String cipherText = XORCipher.encrypt(plaintext, key); + String decryptedText = XORCipher.decrypt(cipherText, key); + + assertEquals(plaintext, decryptedText); + } + + @Test + void testLongPlaintextShortKey() { + String plaintext = "This is a long plaintext message."; + String key = "key"; + + String cipherText = XORCipher.encrypt(plaintext, key); + String decryptedText = XORCipher.decrypt(cipherText, key); + + assertEquals(plaintext, decryptedText); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/a5/A5CipherTest.java b/src/test/java/com/thealgorithms/ciphers/a5/A5CipherTest.java new file mode 100644 index 000000000000..011a1b521e31 --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/a5/A5CipherTest.java @@ -0,0 +1,49 @@ +package com.thealgorithms.ciphers.a5; + +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import java.util.BitSet; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class A5CipherTest { + + private A5Cipher a5Cipher; + + @BeforeEach + void setUp() { + // Initialize the session key and frame counter + final var sessionKey = BitSet.valueOf(new long[] {0b1010101010101010L}); + final var frameCounter = BitSet.valueOf(new long[] {0b0000000000000001L}); + a5Cipher = new A5Cipher(sessionKey, frameCounter); + } + + @Test + void testEncryptWithValidInput() { + BitSet plainText = BitSet.valueOf(new long[] {0b1100110011001100L}); // Example plaintext + BitSet encrypted = a5Cipher.encrypt(plainText); + + // The expected result depends on the key stream generated. + // In a real test, you would replace this with the actual expected result. + // For now, we will just assert that the encrypted result is not equal to the plaintext. + assertNotEquals(plainText, encrypted, "Encrypted output should not equal plaintext"); + } + + @Test + void testEncryptAllOnesInput() { + BitSet plainText = BitSet.valueOf(new long[] {0b1111111111111111L}); // All ones + BitSet encrypted = a5Cipher.encrypt(plainText); + + // Similar to testEncryptWithValidInput, ensure that output isn't the same as input + assertNotEquals(plainText, encrypted, "Encrypted output should not equal plaintext of all ones"); + } + + @Test + void testEncryptAllZerosInput() { + BitSet plainText = new BitSet(); // All zeros + BitSet encrypted = a5Cipher.encrypt(plainText); + + // Check that the encrypted output is not the same + assertNotEquals(plainText, encrypted, "Encrypted output should not equal plaintext of all zeros"); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/a5/A5KeyStreamGeneratorTest.java b/src/test/java/com/thealgorithms/ciphers/a5/A5KeyStreamGeneratorTest.java new file mode 100644 index 000000000000..c9b721f90b2d --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/a5/A5KeyStreamGeneratorTest.java @@ -0,0 +1,59 @@ +package com.thealgorithms.ciphers.a5; + +import static com.thealgorithms.ciphers.a5.A5KeyStreamGenerator.FRAME_COUNTER_LENGTH; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.util.BitSet; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class A5KeyStreamGeneratorTest { + + private A5KeyStreamGenerator keyStreamGenerator; + private BitSet frameCounter; + + @BeforeEach + void setUp() { + keyStreamGenerator = new A5KeyStreamGenerator(); + + // Initialize session key and frame counter for testing + final var sessionKey = BitSet.valueOf(new long[] {0b1010101010101010L}); // Example 16-bit key + frameCounter = BitSet.valueOf(new long[] {0b0000000000000001L}); // Example 16-bit frame counter + keyStreamGenerator.initialize(sessionKey, frameCounter); + } + + @Test + void testInitialization() { + // Verify that the internal state is set up correctly + assertNotNull(keyStreamGenerator, "KeyStreamGenerator should be initialized"); + } + + @Test + void testIncrementFrameCounter() { + // Generate key stream to increment the frame counter + keyStreamGenerator.getNextKeyStream(); + + // The frame counter should have been incremented + BitSet incrementedFrameCounter = keyStreamGenerator.getFrameCounter(); + + // Check if the incremented frame counter is expected + BitSet expectedFrameCounter = (BitSet) frameCounter.clone(); + Utils.increment(expectedFrameCounter, FRAME_COUNTER_LENGTH); + + assertEquals(expectedFrameCounter, incrementedFrameCounter, "Frame counter should be incremented after generating key stream"); + } + + @Test + void testGetNextKeyStreamProducesDifferentOutputs() { + // Generate a key stream + BitSet firstKeyStream = keyStreamGenerator.getNextKeyStream(); + + // Generate another key stream + BitSet secondKeyStream = keyStreamGenerator.getNextKeyStream(); + + // Assert that consecutive key streams are different + assertNotEquals(firstKeyStream, secondKeyStream, "Consecutive key streams should be different"); + } +} diff --git a/src/test/java/com/thealgorithms/ciphers/a5/LFSRTest.java b/src/test/java/com/thealgorithms/ciphers/a5/LFSRTest.java new file mode 100644 index 000000000000..6ef478f95358 --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/a5/LFSRTest.java @@ -0,0 +1,96 @@ +package com.thealgorithms.ciphers.a5; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import java.util.BitSet; +import org.junit.jupiter.api.Test; + +// Basic tests for sanity check +class LFSRTest { + + // Represents 0100 1110 0010 1111 0100 1101 0111 1100 0001 1110 1011 1000 1000 1011 0011 1010 + // But we start reverse way because bitset starts from most right (1010) + byte[] sessionKeyBytes = { + 58, + (byte) 139, + (byte) 184, + 30, + 124, + 77, + 47, + 78, + }; + + // Represents 11 1010 1011 0011 1100 1011 + byte[] frameCounterBytes = {(byte) 203, (byte) 179, 58}; + + @Test + void initialize() { + BitSet sessionKey = BitSet.valueOf(sessionKeyBytes); + BitSet frameCounter = BitSet.valueOf(frameCounterBytes); + + BitSet expected = new BitSet(19); + expected.set(0); + expected.set(1); + expected.set(3); + expected.set(4); + expected.set(5); + expected.set(7); + expected.set(9); + expected.set(10); + expected.set(11); + expected.set(12); + expected.set(13); + expected.set(15); + expected.set(16); + expected.set(17); + + LFSR lfsr0 = new LFSR(19, 8, new int[] {13, 16, 17, 18}); + lfsr0.initialize(sessionKey, frameCounter); + assertEquals(expected.toString(), lfsr0.toString()); + } + + @Test + void clock() { + BitSet sessionKey = BitSet.valueOf(sessionKeyBytes); + BitSet frameCounter = BitSet.valueOf(frameCounterBytes); + + LFSR lfsr0 = new LFSR(19, 8, new int[] {13, 16, 17, 18}); + lfsr0.initialize(sessionKey, frameCounter); + + BitSet expected = new BitSet(19); + expected.set(0); + expected.set(1); + expected.set(2); + expected.set(4); + expected.set(5); + expected.set(6); + expected.set(8); + expected.set(10); + expected.set(11); + expected.set(12); + expected.set(13); + expected.set(14); + expected.set(16); + expected.set(17); + expected.set(18); + + lfsr0.clock(); + assertEquals(expected.toString(), lfsr0.toString()); + } + + @Test + void getClockBit() { + BitSet sessionKey = BitSet.valueOf(sessionKeyBytes); + BitSet frameCounter = BitSet.valueOf(frameCounterBytes); + + LFSR lfsr0 = new LFSR(19, 8, new int[] {13, 16, 17, 18}); + + assertFalse(lfsr0.getClockBit()); + + lfsr0.initialize(sessionKey, frameCounter); + + assertFalse(lfsr0.getClockBit()); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/AffineConverterTest.java b/src/test/java/com/thealgorithms/conversions/AffineConverterTest.java new file mode 100644 index 000000000000..47eea139f424 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/AffineConverterTest.java @@ -0,0 +1,87 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class AffineConverterTest { + + private AffineConverter converter; + + @BeforeEach + void setUp() { + converter = new AffineConverter(2.0, 3.0); + } + + @Test + void testConstructorWithValidValues() { + assertEquals(3.0, converter.convert(0.0), "Expected value when input is 0.0"); + assertEquals(5.0, converter.convert(1.0), "Expected value when input is 1.0"); + } + + @Test + void testConstructorWithInvalidValues() { + assertThrows(IllegalArgumentException.class, () -> new AffineConverter(Double.NaN, 3.0), "Constructor should throw IllegalArgumentException for NaN slope"); + } + + @Test + void testConvertWithNegativeValues() { + assertEquals(-1.0, converter.convert(-2.0), "Negative input should convert correctly"); + assertEquals(-3.0, new AffineConverter(-1.0, -1.0).convert(2.0), "Slope and intercept can be negative"); + } + + @Test + void testConvertWithFloatingPointPrecision() { + double result = new AffineConverter(1.3333, 0.6667).convert(3.0); + assertEquals(4.6666, result, 1e-4, "Conversion should maintain floating-point precision"); + } + + @Test + void testInvert() { + AffineConverter inverted = converter.invert(); + assertEquals(0.0, inverted.convert(3.0), "Inverted should return 0.0 for input 3.0"); + assertEquals(1.0, inverted.convert(5.0), "Inverted should return 1.0 for input 5.0"); + } + + @Test + void testInvertWithZeroSlope() { + AffineConverter zeroSlopeConverter = new AffineConverter(0.0, 3.0); + assertThrows(AssertionError.class, zeroSlopeConverter::invert, "Invert should throw AssertionError when slope is zero"); + } + + @Test + void testCompose() { + AffineConverter otherConverter = new AffineConverter(1.0, 2.0); + AffineConverter composed = converter.compose(otherConverter); + + assertEquals(7.0, composed.convert(0.0), "Expected composed conversion at 0.0"); + assertEquals(9.0, composed.convert(1.0), "Expected composed conversion at 1.0"); + } + + @Test + void testMultipleCompositions() { + AffineConverter c1 = new AffineConverter(2.0, 1.0); + AffineConverter c2 = new AffineConverter(3.0, -2.0); + AffineConverter c3 = c1.compose(c2); // (2x + 1) ∘ (3x - 2) => 6x - 1 + + assertEquals(-3.0, c3.convert(0.0), "Composed transformation should return -3.0 at 0.0"); + assertEquals(3.0, c3.convert(1.0), "Composed transformation should return 3.0 at 1.0"); + } + + @Test + void testIdentityComposition() { + AffineConverter identity = new AffineConverter(1.0, 0.0); + AffineConverter composed = converter.compose(identity); + + assertEquals(3.0, composed.convert(0.0), "Identity composition should not change the transformation"); + assertEquals(7.0, composed.convert(2.0), "Identity composition should behave like the original"); + } + + @Test + void testLargeInputs() { + double largeValue = 1e6; + assertEquals(2.0 * largeValue + 3.0, converter.convert(largeValue), "Should handle large input values without overflow"); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/AnyBaseToDecimalTest.java b/src/test/java/com/thealgorithms/conversions/AnyBaseToDecimalTest.java new file mode 100644 index 000000000000..c7f9493ef0e7 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/AnyBaseToDecimalTest.java @@ -0,0 +1,22 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class AnyBaseToDecimalTest { + @ParameterizedTest + @CsvSource({"1010, 2, 10", "777, 8, 511", "999, 10, 999", "ABCDEF, 16, 11259375", "XYZ, 36, 44027", "0, 2, 0", "A, 16, 10", "Z, 36, 35"}) + void testConvertToDecimal(String input, int radix, int expected) { + assertEquals(expected, AnyBaseToDecimal.convertToDecimal(input, radix)); + } + + @Test + void testIncorrectInput() { + assertThrows(NumberFormatException.class, () -> AnyBaseToDecimal.convertToDecimal("G", 16)); + assertThrows(NumberFormatException.class, () -> AnyBaseToDecimal.convertToDecimal("XYZ", 10)); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/AnytoAnyTest.java b/src/test/java/com/thealgorithms/conversions/AnytoAnyTest.java new file mode 100644 index 000000000000..cdc012180bf9 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/AnytoAnyTest.java @@ -0,0 +1,48 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +public class AnytoAnyTest { + + @Test + void testValidConversions() { + assertEquals(101, AnytoAny.convertBase(5, 10, 2), "Decimal 5 should convert to binary 101"); + assertEquals(2, AnytoAny.convertBase(2, 2, 10), "Binary 10 should convert to decimal 2"); + assertEquals(6, AnytoAny.convertBase(110, 2, 8), "Binary 110 should convert to octal 6"); + assertEquals(111, AnytoAny.convertBase(7, 10, 2), "Decimal 7 should convert to binary 111"); + } + + @Test + void testDecimalToBinary() { + assertEquals(1101, AnytoAny.convertBase(13, 10, 2), "Decimal 13 should convert to binary 1101"); + assertEquals(0, AnytoAny.convertBase(0, 10, 2), "Decimal 0 should convert to binary 0"); + } + + @Test + void testBinaryToDecimal() { + assertEquals(13, AnytoAny.convertBase(1101, 2, 10), "Binary 1101 should convert to decimal 13"); + assertEquals(0, AnytoAny.convertBase(0, 2, 10), "Binary 0 should convert to decimal 0"); + } + + @Test + void testOctalToDecimal() { + assertEquals(8, AnytoAny.convertBase(10, 8, 10), "Octal 10 should convert to decimal 8"); + assertEquals(65, AnytoAny.convertBase(101, 8, 10), "Octal 101 should convert to decimal 65"); + } + + @Test + void testInvalidBases() { + assertThrows(IllegalArgumentException.class, () -> AnytoAny.convertBase(5, 1, 10), "Source base less than 2 should throw IllegalArgumentException"); + + assertThrows(IllegalArgumentException.class, () -> AnytoAny.convertBase(5, 10, 11), "Destination base greater than 10 should throw IllegalArgumentException"); + } + + @Test + void testLargeNumberConversion() { + assertEquals(1111101000, AnytoAny.convertBase(1000, 10, 2), "Decimal 1000 should convert to binary 1111101000"); + assertEquals(1750, AnytoAny.convertBase(1000, 10, 8), "Decimal 1000 should convert to octal 1750"); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/BinaryToDecimalTest.java b/src/test/java/com/thealgorithms/conversions/BinaryToDecimalTest.java new file mode 100644 index 000000000000..9045d100285e --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/BinaryToDecimalTest.java @@ -0,0 +1,42 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class BinaryToDecimalTest { + + @Test + // Test converting binary to decimal + public void testBinaryToDecimal() { + // zeros at the starting should be removed + assertEquals(0, BinaryToDecimal.binaryToDecimal(0)); + assertEquals(1, BinaryToDecimal.binaryToDecimal(1)); + assertEquals(5, BinaryToDecimal.binaryToDecimal(101)); + assertEquals(63, BinaryToDecimal.binaryToDecimal(111111)); + assertEquals(512, BinaryToDecimal.binaryToDecimal(1000000000)); + } + + @Test + // Test converting negative binary numbers + public void testNegativeBinaryToDecimal() { + assertEquals(-1, BinaryToDecimal.binaryToDecimal(-1)); + assertEquals(-42, BinaryToDecimal.binaryToDecimal(-101010)); + } + + @Test + // Test converting binary numbers with large values + public void testLargeBinaryToDecimal() { + assertEquals(262144L, BinaryToDecimal.binaryToDecimal(1000000000000000000L)); + assertEquals(524287L, BinaryToDecimal.binaryToDecimal(1111111111111111111L)); + } + + @ParameterizedTest + @CsvSource({"2", "1234", "11112", "101021"}) + void testNotCorrectBinaryInput(long binaryNumber) { + assertThrows(IllegalArgumentException.class, () -> BinaryToDecimal.binaryToDecimal(binaryNumber)); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/BinaryToHexadecimalTest.java b/src/test/java/com/thealgorithms/conversions/BinaryToHexadecimalTest.java new file mode 100644 index 000000000000..0935701e3c32 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/BinaryToHexadecimalTest.java @@ -0,0 +1,22 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class BinaryToHexadecimalTest { + + @ParameterizedTest + @CsvSource({"0, 0", "1, 1", "10, 2", "1111, F", "1101010, 6A", "1100, C"}) + void testBinToHex(int binary, String expectedHex) { + assertEquals(expectedHex, BinaryToHexadecimal.binToHex(binary)); + } + + @ParameterizedTest + @CsvSource({"2", "1234", "11112"}) + void testInvalidBinaryInput(int binary) { + assertThrows(IllegalArgumentException.class, () -> BinaryToHexadecimal.binToHex(binary)); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/BinaryToOctalTest.java b/src/test/java/com/thealgorithms/conversions/BinaryToOctalTest.java new file mode 100644 index 000000000000..98bd55a3d6c9 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/BinaryToOctalTest.java @@ -0,0 +1,24 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class BinaryToOctalTest { + + @ParameterizedTest + @CsvSource({"0, 0", "1, 1", "10, 2", "111, 7", "1000, 10", "1111, 17", "110101, 65", "1010101, 125", "110110011, 663", "111111111, 777", "10010110, 226", "1011101, 135"}) + void testConvertBinaryToOctal(int binary, String expectedOctal) { + assertEquals(expectedOctal, BinaryToOctal.convertBinaryToOctal(binary)); + } + + @Test + void testIncorrectInput() { + assertThrows(IllegalArgumentException.class, () -> BinaryToOctal.convertBinaryToOctal(1234)); + assertThrows(IllegalArgumentException.class, () -> BinaryToOctal.convertBinaryToOctal(102)); + assertThrows(IllegalArgumentException.class, () -> BinaryToOctal.convertBinaryToOctal(-1010)); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/DecimalToAnyBaseTest.java b/src/test/java/com/thealgorithms/conversions/DecimalToAnyBaseTest.java new file mode 100644 index 000000000000..04630b71cce4 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/DecimalToAnyBaseTest.java @@ -0,0 +1,23 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class DecimalToAnyBaseTest { + + @ParameterizedTest + @CsvSource({"0, 2, 0", "0, 16, 0", "0, 36, 0", "10, 2, 1010", "255, 16, FF", "100, 8, 144", "42, 2, 101010", "1234, 16, 4D2", "1234, 36, YA"}) + void testConvertToAnyBase(int decimal, int base, String expected) { + assertEquals(expected, DecimalToAnyBase.convertToAnyBase(decimal, base)); + } + + @Test + void testBaseOutOfRange() { + assertThrows(IllegalArgumentException.class, () -> DecimalToAnyBase.convertToAnyBase(10, 1)); + assertThrows(IllegalArgumentException.class, () -> DecimalToAnyBase.convertToAnyBase(10, 37)); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/DecimalToBinaryTest.java b/src/test/java/com/thealgorithms/conversions/DecimalToBinaryTest.java new file mode 100644 index 000000000000..f194951e9e64 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/DecimalToBinaryTest.java @@ -0,0 +1,21 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class DecimalToBinaryTest { + + @ParameterizedTest + @CsvSource({"0, 0", "1, 1", "2, 10", "5, 101", "10, 1010", "15, 1111", "100, 1100100"}) + void testConvertUsingConventionalAlgorithm(int decimal, int expectedBinary) { + assertEquals(expectedBinary, DecimalToBinary.convertUsingConventionalAlgorithm(decimal)); + } + + @ParameterizedTest + @CsvSource({"0, 0", "1, 1", "2, 10", "5, 101", "10, 1010", "15, 1111", "100, 1100100"}) + void testConvertUsingBitwiseAlgorithm(int decimal, int expectedBinary) { + assertEquals(expectedBinary, DecimalToBinary.convertUsingBitwiseAlgorithm(decimal)); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/DecimalToHexadecimalTest.java b/src/test/java/com/thealgorithms/conversions/DecimalToHexadecimalTest.java new file mode 100644 index 000000000000..c9817b5d6926 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/DecimalToHexadecimalTest.java @@ -0,0 +1,14 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class DecimalToHexadecimalTest { + @ParameterizedTest + @CsvSource({"0, 0", "1, 1", "10, a", "15, f", "16, 10", "255, ff", "190, be", "1800, 708"}) + void testDecToHex(int decimal, String expectedHex) { + assertEquals(expectedHex, DecimalToHexadecimal.decToHex(decimal)); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/DecimalToOctalTest.java b/src/test/java/com/thealgorithms/conversions/DecimalToOctalTest.java new file mode 100644 index 000000000000..7ff274f04800 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/DecimalToOctalTest.java @@ -0,0 +1,21 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class DecimalToOctalTest { + @ParameterizedTest + @CsvSource({"0, 0", "7, 7", "8, 10", "10, 12", "64, 100", "83, 123", "7026, 15562"}) + void testConvertToOctal(int decimal, int expectedOctal) { + assertEquals(expectedOctal, DecimalToOctal.convertToOctal(decimal)); + } + + @Test + void testConvertToOctalNegativeNumber() { + assertThrows(IllegalArgumentException.class, () -> DecimalToOctal.convertToOctal(-10)); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/EndianConverterTest.java b/src/test/java/com/thealgorithms/conversions/EndianConverterTest.java new file mode 100644 index 000000000000..85ffa2190962 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/EndianConverterTest.java @@ -0,0 +1,35 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class EndianConverterTest { + + @ParameterizedTest + @CsvSource({ + "0x78563412, 0x12345678", "0x00000000, 0x00000000", "0x00000001, 0x01000000", + "0xFFFFFFFF, 0xFFFFFFFF", // -1 in two's complement + "0x0000007F, 0x7F000000" // Positive boundary case + }) + public void + testLittleToBigEndian(String inputHex, String expectedHex) { + int input = (int) Long.parseLong(inputHex.substring(2), 16); // Convert hex string to int + int expected = (int) Long.parseLong(expectedHex.substring(2), 16); // Convert hex string to int + assertEquals(expected, EndianConverter.littleToBigEndian(input)); + } + + @ParameterizedTest + @CsvSource({ + "0x12345678, 0x78563412", "0x00000000, 0x00000000", "0x01000000, 0x00000001", + "0xFFFFFFFF, 0xFFFFFFFF", // -1 in two's complement + "0x7F000000, 0x0000007F" // Positive boundary case + }) + public void + testBigToLittleEndian(String inputHex, String expectedHex) { + int input = (int) Long.parseLong(inputHex.substring(2), 16); // Convert hex string to int + int expected = (int) Long.parseLong(expectedHex.substring(2), 16); // Convert hex string to int + assertEquals(expected, EndianConverter.bigToLittleEndian(input)); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/HexToOctTest.java b/src/test/java/com/thealgorithms/conversions/HexToOctTest.java new file mode 100644 index 000000000000..b3f94b19b6e6 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/HexToOctTest.java @@ -0,0 +1,33 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class HexToOctTest { + @Test + public void testHexToDecimal() { + assertEquals(255, HexToOct.hexToDecimal("FF")); + assertEquals(16, HexToOct.hexToDecimal("10")); + assertEquals(0, HexToOct.hexToDecimal("0")); + assertEquals(4095, HexToOct.hexToDecimal("FFF")); + } + + @Test + public void testDecimalToOctal() { + assertEquals(110, HexToOct.decimalToOctal(HexToOct.hexToDecimal("48"))); + assertEquals(255, HexToOct.decimalToOctal(HexToOct.hexToDecimal("AD"))); + assertEquals(377, HexToOct.decimalToOctal(255)); + assertEquals(20, HexToOct.decimalToOctal(16)); + assertEquals(0, HexToOct.decimalToOctal(0)); + assertEquals(7777, HexToOct.decimalToOctal(4095)); + } + + @Test + public void testHexToOctal() { + assertEquals(377, HexToOct.hexToOctal("FF")); + assertEquals(20, HexToOct.hexToOctal("10")); + assertEquals(0, HexToOct.hexToOctal("0")); + assertEquals(7777, HexToOct.hexToOctal("FFF")); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/HexaDecimalToBinaryTest.java b/src/test/java/com/thealgorithms/conversions/HexaDecimalToBinaryTest.java new file mode 100644 index 000000000000..1426eab64d2c --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/HexaDecimalToBinaryTest.java @@ -0,0 +1,45 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +/** + * Unit tests for the {@link EndianConverter} class. + */ +public class HexaDecimalToBinaryTest { + + /** + * Parameterized test to validate the conversion from little-endian to big-endian. + * Hexadecimal values are passed as strings and converted to integers during the test. + */ + @ParameterizedTest + @CsvSource({ + "0x78563412, 0x12345678", "0x00000000, 0x00000000", "0x00000001, 0x01000000", + "0xFFFFFFFF, 0xFFFFFFFF", // -1 in two's complement + "0x0000007F, 0x7F000000" // Positive boundary case + }) + public void + testLittleToBigEndian(String inputHex, String expectedHex) { + int input = (int) Long.parseLong(inputHex.substring(2), 16); // Convert hex string to int + int expected = (int) Long.parseLong(expectedHex.substring(2), 16); // Convert hex string to int + assertEquals(expected, EndianConverter.littleToBigEndian(input)); + } + + /** + * Parameterized test to validate the conversion from big-endian to little-endian. + */ + @ParameterizedTest + @CsvSource({ + "0x12345678, 0x78563412", "0x00000000, 0x00000000", "0x01000000, 0x00000001", + "0xFFFFFFFF, 0xFFFFFFFF", // -1 in two's complement + "0x7F000000, 0x0000007F" // Positive boundary case + }) + public void + testBigToLittleEndian(String inputHex, String expectedHex) { + int input = (int) Long.parseLong(inputHex.substring(2), 16); // Convert hex string to int + int expected = (int) Long.parseLong(expectedHex.substring(2), 16); // Convert hex string to int + assertEquals(expected, EndianConverter.bigToLittleEndian(input)); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/HexaDecimalToDecimalTest.java b/src/test/java/com/thealgorithms/conversions/HexaDecimalToDecimalTest.java new file mode 100644 index 000000000000..d0d6b400e299 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/HexaDecimalToDecimalTest.java @@ -0,0 +1,37 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class HexaDecimalToDecimalTest { + + @ParameterizedTest + @CsvSource({ + "A1, 161", // Simple case with two characters + "1AC, 428", // Mixed-case input + "0, 0", // Single zero + "F, 15", // Single digit + "10, 16", // Power of 16 + "FFFF, 65535", // Max 4-character hex + "7FFFFFFF, 2147483647" // Max positive int value + }) + public void + testValidHexaToDecimal(String hexInput, int expectedDecimal) { + assertEquals(expectedDecimal, HexaDecimalToDecimal.getHexaToDec(hexInput)); + } + + @ParameterizedTest + @CsvSource({ + "G", // Invalid character + "1Z", // Mixed invalid input + "123G", // Valid prefix with invalid character + "#$%" // Non-hexadecimal symbols + }) + public void + testInvalidHexaToDecimal(String invalidHex) { + assertThrows(IllegalArgumentException.class, () -> HexaDecimalToDecimal.getHexaToDec(invalidHex)); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/IPConverterTest.java b/src/test/java/com/thealgorithms/conversions/IPConverterTest.java new file mode 100644 index 000000000000..78c226a9237b --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/IPConverterTest.java @@ -0,0 +1,30 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class IPConverterTest { + + private static String generateTestIP(int a, int b, int c, int d) { + return String.format("%d.%d.%d.%d", a, b, c, d); + } + + private static String generateTestBinary(int a, int b, int c, int d) { + return String.format("%8s.%8s.%8s.%8s", Integer.toBinaryString(a), Integer.toBinaryString(b), Integer.toBinaryString(c), Integer.toBinaryString(d)).replace(' ', '0'); + } + + @Test + public void testIpToBinary() { + assertEquals(generateTestBinary(192, 168, 1, 1), IPConverter.ipToBinary(generateTestIP(192, 168, 1, 1))); + assertEquals(generateTestBinary(127, 3, 4, 5), IPConverter.ipToBinary(generateTestIP(127, 3, 4, 5))); + assertEquals(generateTestBinary(0, 0, 0, 0), IPConverter.ipToBinary(generateTestIP(0, 0, 0, 0))); + } + + @Test + public void testBinaryToIP() { + assertEquals(generateTestIP(192, 168, 1, 1), IPConverter.binaryToIP(generateTestBinary(192, 168, 1, 1))); + assertEquals(generateTestIP(127, 3, 4, 5), IPConverter.binaryToIP(generateTestBinary(127, 3, 4, 5))); + assertEquals(generateTestIP(0, 0, 0, 0), IPConverter.binaryToIP(generateTestBinary(0, 0, 0, 0))); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/IPv6ConverterTest.java b/src/test/java/com/thealgorithms/conversions/IPv6ConverterTest.java new file mode 100644 index 000000000000..443f865ae0dc --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/IPv6ConverterTest.java @@ -0,0 +1,64 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.net.UnknownHostException; +import org.junit.jupiter.api.Test; + +public class IPv6ConverterTest { + + private static final String VALID_IPV4 = "192." + + "0." + + "2." + + "128"; + private static final String EXPECTED_IPV6_MAPPED = ":" + + ":ff" + + "ff" + + ":19" + + "2." + + "0." + + "2.128"; + private static final String INVALID_IPV6_MAPPED = "2001:" + + "db8" + + ":" + + ":1"; + private static final String INVALID_IPV4 = "999." + + "999." + + "999." + + "999"; + private static final String INVALID_IPV6_FORMAT = "invalid:ipv6" + + "::address"; + private static final String EMPTY_STRING = ""; + + @Test + public void testIpv4ToIpv6ValidInput() throws UnknownHostException { + String actualIpv6 = IPv6Converter.ipv4ToIpv6(VALID_IPV4); + assertEquals(EXPECTED_IPV6_MAPPED, actualIpv6); + } + + @Test + public void testIpv6ToIpv4InvalidIPv6MappedAddress() { + assertThrows(IllegalArgumentException.class, () -> IPv6Converter.ipv6ToIpv4(INVALID_IPV6_MAPPED)); + } + + @Test + public void testIpv4ToIpv6InvalidIPv4Address() { + assertThrows(UnknownHostException.class, () -> IPv6Converter.ipv4ToIpv6(INVALID_IPV4)); + } + + @Test + public void testIpv6ToIpv4InvalidFormat() { + assertThrows(UnknownHostException.class, () -> IPv6Converter.ipv6ToIpv4(INVALID_IPV6_FORMAT)); + } + + @Test + public void testIpv4ToIpv6EmptyString() { + assertThrows(UnknownHostException.class, () -> IPv6Converter.ipv4ToIpv6(EMPTY_STRING)); + } + + @Test + public void testIpv6ToIpv4EmptyString() { + assertThrows(IllegalArgumentException.class, () -> IPv6Converter.ipv6ToIpv4(EMPTY_STRING)); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/IntegerToEnglishTest.java b/src/test/java/com/thealgorithms/conversions/IntegerToEnglishTest.java new file mode 100644 index 000000000000..c2a94794b7f8 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/IntegerToEnglishTest.java @@ -0,0 +1,46 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class IntegerToEnglishTest { + + @Test + public void testIntegerToEnglish() { + assertEquals("Two Billion One Hundred Forty Seven Million Four Hundred Eighty Three Thousand Six Hundred Forty Seven", IntegerToEnglish.integerToEnglishWords(2147483647)); + assertEquals("One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven", IntegerToEnglish.integerToEnglishWords(1234567)); + assertEquals("Twelve Thousand Three Hundred Forty Five", IntegerToEnglish.integerToEnglishWords(12345)); + assertEquals("One Hundred", IntegerToEnglish.integerToEnglishWords(100)); + assertEquals("Zero", IntegerToEnglish.integerToEnglishWords(0)); + } + + @Test + public void testSmallNumbers() { + assertEquals("Ten", IntegerToEnglish.integerToEnglishWords(10)); + assertEquals("Nineteen", IntegerToEnglish.integerToEnglishWords(19)); + assertEquals("Twenty One", IntegerToEnglish.integerToEnglishWords(21)); + assertEquals("Ninety Nine", IntegerToEnglish.integerToEnglishWords(99)); + } + + @Test + public void testHundreds() { + assertEquals("One Hundred One", IntegerToEnglish.integerToEnglishWords(101)); + assertEquals("Five Hundred Fifty", IntegerToEnglish.integerToEnglishWords(550)); + assertEquals("Nine Hundred Ninety Nine", IntegerToEnglish.integerToEnglishWords(999)); + } + + @Test + public void testThousands() { + assertEquals("One Thousand", IntegerToEnglish.integerToEnglishWords(1000)); + assertEquals("Ten Thousand One", IntegerToEnglish.integerToEnglishWords(10001)); + assertEquals("Seventy Six Thousand Five Hundred Forty Three", IntegerToEnglish.integerToEnglishWords(76543)); + } + + @Test + public void testEdgeCases() { + assertEquals("One Million", IntegerToEnglish.integerToEnglishWords(1_000_000)); + assertEquals("One Billion", IntegerToEnglish.integerToEnglishWords(1_000_000_000)); + assertEquals("Two Thousand", IntegerToEnglish.integerToEnglishWords(2000)); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/IntegerToRomanTest.java b/src/test/java/com/thealgorithms/conversions/IntegerToRomanTest.java new file mode 100644 index 000000000000..181cad930282 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/IntegerToRomanTest.java @@ -0,0 +1,39 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class IntegerToRomanTest { + + @Test + public void testIntegerToRoman() { + assertEquals("MCMXCIV", IntegerToRoman.integerToRoman(1994)); + assertEquals("LVIII", IntegerToRoman.integerToRoman(58)); + assertEquals("IV", IntegerToRoman.integerToRoman(4)); + assertEquals("IX", IntegerToRoman.integerToRoman(9)); + assertEquals("MMM", IntegerToRoman.integerToRoman(3000)); + } + + @Test + public void testSmallNumbers() { + assertEquals("I", IntegerToRoman.integerToRoman(1)); + assertEquals("II", IntegerToRoman.integerToRoman(2)); + assertEquals("III", IntegerToRoman.integerToRoman(3)); + } + + @Test + public void testRoundNumbers() { + assertEquals("X", IntegerToRoman.integerToRoman(10)); + assertEquals("L", IntegerToRoman.integerToRoman(50)); + assertEquals("C", IntegerToRoman.integerToRoman(100)); + assertEquals("D", IntegerToRoman.integerToRoman(500)); + assertEquals("M", IntegerToRoman.integerToRoman(1000)); + } + + @Test + public void testEdgeCases() { + assertEquals("", IntegerToRoman.integerToRoman(0)); // Non-positive number case + assertEquals("", IntegerToRoman.integerToRoman(-5)); // Negative number case + } +} diff --git a/src/test/java/com/thealgorithms/conversions/MorseCodeConverterTest.java b/src/test/java/com/thealgorithms/conversions/MorseCodeConverterTest.java new file mode 100644 index 000000000000..153b632a0510 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/MorseCodeConverterTest.java @@ -0,0 +1,19 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class MorseCodeConverterTest { + + @Test + public void testTextToMorse() { + assertEquals(".- -...", MorseCodeConverter.textToMorse("AB")); + assertEquals(".... . .-.. .-.. --- | .-- --- .-. .-.. -..", MorseCodeConverter.textToMorse("HELLO WORLD")); + } + + @Test + public void testMorseToText() { + assertEquals("AB", MorseCodeConverter.morseToText(".- -...")); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/NumberToWordsTest.java b/src/test/java/com/thealgorithms/conversions/NumberToWordsTest.java new file mode 100644 index 000000000000..7b264678daa4 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/NumberToWordsTest.java @@ -0,0 +1,60 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.math.BigDecimal; +import org.junit.jupiter.api.Test; + +public class NumberToWordsTest { + + @Test + void testNullInput() { + assertEquals("Invalid Input", NumberToWords.convert(null), "Null input should return 'Invalid Input'"); + } + + @Test + void testZeroInput() { + assertEquals("Zero", NumberToWords.convert(BigDecimal.ZERO), "Zero input should return 'Zero'"); + } + + @Test + void testPositiveWholeNumbers() { + assertEquals("One", NumberToWords.convert(BigDecimal.ONE), "1 should convert to 'One'"); + assertEquals("One Thousand", NumberToWords.convert(new BigDecimal("1000")), "1000 should convert to 'One Thousand'"); + assertEquals("One Million", NumberToWords.convert(new BigDecimal("1000000")), "1000000 should convert to 'One Million'"); + } + + @Test + void testNegativeWholeNumbers() { + assertEquals("Negative One", NumberToWords.convert(new BigDecimal("-1")), "-1 should convert to 'Negative One'"); + assertEquals("Negative One Thousand", NumberToWords.convert(new BigDecimal("-1000")), "-1000 should convert to 'Negative One Thousand'"); + } + + @Test + void testFractionalNumbers() { + assertEquals("Zero Point One Two Three", NumberToWords.convert(new BigDecimal("0.123")), "0.123 should convert to 'Zero Point One Two Three'"); + assertEquals("Negative Zero Point Four Five Six", NumberToWords.convert(new BigDecimal("-0.456")), "-0.456 should convert to 'Negative Zero Point Four Five Six'"); + } + + @Test + void testLargeNumbers() { + assertEquals("Nine Hundred Ninety Nine Million Nine Hundred Ninety Nine Thousand Nine Hundred Ninety Nine", NumberToWords.convert(new BigDecimal("999999999")), "999999999 should convert correctly"); + assertEquals("One Trillion", NumberToWords.convert(new BigDecimal("1000000000000")), "1000000000000 should convert to 'One Trillion'"); + } + + @Test + void testNegativeLargeNumbers() { + assertEquals("Negative Nine Trillion Eight Hundred Seventy Six Billion Five Hundred Forty Three Million Two Hundred Ten Thousand Nine Hundred Eighty Seven", NumberToWords.convert(new BigDecimal("-9876543210987")), "-9876543210987 should convert correctly"); + } + + @Test + void testFloatingPointPrecision() { + assertEquals("One Million Point Zero Zero One", NumberToWords.convert(new BigDecimal("1000000.001")), "1000000.001 should convert to 'One Million Point Zero Zero One'"); + } + + @Test + void testEdgeCases() { + assertEquals("Zero", NumberToWords.convert(new BigDecimal("-0.0")), "-0.0 should convert to 'Zero'"); + assertEquals("Zero Point Zero Zero Zero Zero Zero Zero One", NumberToWords.convert(new BigDecimal("1E-7")), "1E-7 should convert to 'Zero Point Zero Zero Zero Zero Zero Zero One'"); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/OctalToBinaryTest.java b/src/test/java/com/thealgorithms/conversions/OctalToBinaryTest.java new file mode 100644 index 000000000000..8e007151c301 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/OctalToBinaryTest.java @@ -0,0 +1,35 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class OctalToBinaryTest { + @Test + public void testConvertOctalToBinary() { + assertEquals(101, OctalToBinary.convertOctalToBinary(5)); + assertEquals(1001, OctalToBinary.convertOctalToBinary(11)); + assertEquals(101010, OctalToBinary.convertOctalToBinary(52)); + assertEquals(110, OctalToBinary.convertOctalToBinary(6)); + } + + @Test + public void testConvertOctalToBinarySingleDigit() { + assertEquals(0, OctalToBinary.convertOctalToBinary(0)); + assertEquals(1, OctalToBinary.convertOctalToBinary(1)); + assertEquals(111, OctalToBinary.convertOctalToBinary(7)); + } + + @Test + public void testConvertOctalToBinaryMultipleDigits() { + assertEquals(100110111, OctalToBinary.convertOctalToBinary(467)); + assertEquals(111101, OctalToBinary.convertOctalToBinary(75)); + assertEquals(111100101, OctalToBinary.convertOctalToBinary(745)); + } + + @Test + public void testConvertOctalToBinaryWithZeroPadding() { + assertEquals(100001010, OctalToBinary.convertOctalToBinary(412)); + assertEquals(101101110, OctalToBinary.convertOctalToBinary(556)); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/OctalToDecimalTest.java b/src/test/java/com/thealgorithms/conversions/OctalToDecimalTest.java new file mode 100644 index 000000000000..20f0fa40c626 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/OctalToDecimalTest.java @@ -0,0 +1,21 @@ +package com.thealgorithms.conversions; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class OctalToDecimalTest { + + @ParameterizedTest + @CsvSource({"10, 8", "7, 7", "77, 63", "123, 83", "0, 0", "777, 511", "2671, 1465", "275, 189"}) + void testConvertOctalToDecimal(String inputOctal, int expectedDecimal) { + Assertions.assertEquals(expectedDecimal, OctalToDecimal.convertOctalToDecimal(inputOctal)); + } + + @ParameterizedTest + @CsvSource({"'', Input cannot be null or empty", "'8', Incorrect input: Expecting an octal number (digits 0-7)", "'19', Incorrect input: Expecting an octal number (digits 0-7)"}) + void testIncorrectInput(String inputOctal, String expectedMessage) { + IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> OctalToDecimal.convertOctalToDecimal(inputOctal)); + Assertions.assertEquals(expectedMessage, exception.getMessage()); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/OctalToHexadecimalTest.java b/src/test/java/com/thealgorithms/conversions/OctalToHexadecimalTest.java new file mode 100644 index 000000000000..6f2ccc24ab2f --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/OctalToHexadecimalTest.java @@ -0,0 +1,23 @@ +package com.thealgorithms.conversions; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class OctalToHexadecimalTest { + + @ParameterizedTest + @CsvSource({"0, 0", "7, 7", "10, 8", "17, F", "20, 10", "777, 1FF", "1234, 29C", "752, 1EA", "536, 15E"}) + void testCorrectInputs(String inputOctal, String expectedHex) { + int decimal = OctalToHexadecimal.octalToDecimal(inputOctal); + String hex = OctalToHexadecimal.decimalToHexadecimal(decimal); + Assertions.assertEquals(expectedHex, hex); + } + + @ParameterizedTest + @CsvSource({"'', Input cannot be null or empty", "'8', Incorrect octal digit: 8", "'19', Incorrect octal digit: 9"}) + void testIncorrectInputs(String inputOctal, String expectedMessage) { + IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> OctalToHexadecimal.octalToDecimal(inputOctal)); + Assertions.assertEquals(expectedMessage, exception.getMessage()); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/PhoneticAlphabetConverterTest.java b/src/test/java/com/thealgorithms/conversions/PhoneticAlphabetConverterTest.java new file mode 100644 index 000000000000..360af7fa0f51 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/PhoneticAlphabetConverterTest.java @@ -0,0 +1,27 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class PhoneticAlphabetConverterTest { + + @ParameterizedTest + @CsvSource({ + "'AB', 'Alpha Bravo'", "'ABC', 'Alpha Bravo Charlie'", "'A1B2C3', 'Alpha One Bravo Two Charlie Three'", "'Hello', 'Hotel Echo Lima Lima Oscar'", "'123', 'One Two Three'", + "'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', 'Alpha Bravo Charlie Delta Echo Foxtrot Golf Hotel India Juliett Kilo Lima Mike November Oscar Papa Quebec Romeo Sierra Tango Uniform Victor Whiskey X-ray Yankee Zulu Zero One Two Three Four Five Six Seven Eight Nine'", + "'abcdefghijklmnopqrstuvwxyz0123456789', 'Alpha Bravo Charlie Delta Echo Foxtrot Golf Hotel India Juliett Kilo Lima Mike November Oscar Papa Quebec Romeo Sierra Tango Uniform Victor Whiskey X-ray Yankee Zulu Zero One Two Three Four Five Six Seven Eight Nine'", + "'', ''", // Empty string case + "'A B C', 'Alpha Bravo Charlie'", // String with spaces + "'A@B#C', 'Alpha @ Bravo # Charlie'", // Special characters + "'A B C 123', 'Alpha Bravo Charlie One Two Three'", // Mixed letters, digits, and spaces + "'a b c', 'Alpha Bravo Charlie'", // Lowercase letters with spaces + "'123!@#', 'One Two Three ! @ #'", // Numbers with special characters + "'HELLO WORLD', 'Hotel Echo Lima Lima Oscar Whiskey Oscar Romeo Lima Delta'" // Words with space + }) + public void + testTextToPhonetic(String input, String expectedOutput) { + assertEquals(expectedOutput, PhoneticAlphabetConverter.textToPhonetic(input)); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/RomanToIntegerTest.java b/src/test/java/com/thealgorithms/conversions/RomanToIntegerTest.java new file mode 100644 index 000000000000..971eff8c74f5 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/RomanToIntegerTest.java @@ -0,0 +1,38 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +public class RomanToIntegerTest { + + @Test + public void testValidRomanToInteger() { + assertEquals(1994, RomanToInteger.romanToInt("MCMXCIV")); + assertEquals(58, RomanToInteger.romanToInt("LVIII")); + assertEquals(1804, RomanToInteger.romanToInt("MDCCCIV")); + assertEquals(9, RomanToInteger.romanToInt("IX")); + assertEquals(4, RomanToInteger.romanToInt("IV")); + assertEquals(3000, RomanToInteger.romanToInt("MMM")); + } + + @Test + public void testLowercaseInput() { + assertEquals(1994, RomanToInteger.romanToInt("mcmxciv")); + assertEquals(58, RomanToInteger.romanToInt("lviii")); + } + + @Test + public void testInvalidRomanNumerals() { + assertThrows(IllegalArgumentException.class, () -> RomanToInteger.romanToInt("Z")); + assertThrows(IllegalArgumentException.class, () -> RomanToInteger.romanToInt("MZI")); + assertThrows(IllegalArgumentException.class, () -> RomanToInteger.romanToInt("MMMO")); + } + + @Test + public void testEmptyAndNullInput() { + assertEquals(0, RomanToInteger.romanToInt("")); // Empty string case + assertThrows(NullPointerException.class, () -> RomanToInteger.romanToInt(null)); // Null input case + } +} diff --git a/src/test/java/com/thealgorithms/conversions/TurkishToLatinConversionTest.java b/src/test/java/com/thealgorithms/conversions/TurkishToLatinConversionTest.java new file mode 100644 index 000000000000..87e40c78e6a2 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/TurkishToLatinConversionTest.java @@ -0,0 +1,31 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class TurkishToLatinConversionTest { + + @ParameterizedTest + @CsvSource({ + "'çalışma', 'calisma'", // Turkish to Latin conversion for lowercase + "'ÇALIŞMA', 'CALISMA'", // Turkish to Latin conversion for uppercase + "'İSTANBUL', 'ISTANBUL'", // Special case of 'İ' to 'I' + "'istanbul', 'istanbul'", // Special case of 'ı' to 'i' + "'GÜL', 'GUL'", // Special case of 'Ü' to 'U' + "'gül', 'gul'", // Special case of 'ü' to 'u' + "'ÖĞRENME', 'OGRENME'", // Special case of 'Ö' to 'O' and 'Ğ' to 'G' + "'öğrenme', 'ogrenme'", // Special case of 'ö' to 'o' and 'ğ' to 'g' + "'ŞEHIR', 'SEHIR'", // Special case of 'Ş' to 'S' + "'şehir', 'sehir'", // Special case of 'ş' to 's' + "'HELLO', 'HELLO'", // String with no Turkish characters, should remain unchanged + "'Merhaba Dünya!', 'Merhaba Dunya!'", // Mixed Turkish and Latin characters with punctuation + "'Çift kişilik yataklı odalar', 'Cift kisilik yatakli odalar'", // Full sentence conversion + "'', ''" // Empty string case + }) + public void + testConvertTurkishToLatin(String input, String expectedOutput) { + assertEquals(expectedOutput, TurkishToLatinConversion.convertTurkishToLatin(input)); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/UnitConversionsTest.java b/src/test/java/com/thealgorithms/conversions/UnitConversionsTest.java new file mode 100644 index 000000000000..3c4e3d5e4c54 --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/UnitConversionsTest.java @@ -0,0 +1,48 @@ +package com.thealgorithms.conversions; + +import static java.util.Map.entry; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class UnitConversionsTest { + private static void addData(Stream.Builder<Arguments> builder, Map<String, Double> values) { + for (var first : values.entrySet()) { + for (var second : values.entrySet()) { + if (!first.getKey().equals(second.getKey())) { + builder.add(Arguments.of(first.getKey(), second.getKey(), first.getValue(), second.getValue())); + } + } + } + } + + private static Stream<Arguments> temperatureData() { + final Map<String, Double> boilingPointOfWater = Map.ofEntries(entry("Celsius", 99.9839), entry("Fahrenheit", 211.97102), entry("Kelvin", 373.1339), entry("Réaumur", 79.98712), entry("Delisle", 0.02415), entry("Rankine", 671.64102)); + + final Map<String, Double> freezingPointOfWater = Map.ofEntries(entry("Celsius", 0.0), entry("Fahrenheit", 32.0), entry("Kelvin", 273.15), entry("Réaumur", 0.0), entry("Delisle", 150.0), entry("Rankine", 491.67)); + + Stream.Builder<Arguments> builder = Stream.builder(); + addData(builder, boilingPointOfWater); + addData(builder, freezingPointOfWater); + return builder.build(); + } + + @ParameterizedTest + @MethodSource("temperatureData") + void testTemperature(String inputUnit, String outputUnit, double value, double expected) { + final double result = UnitConversions.TEMPERATURE.convert(inputUnit, outputUnit, value); + assertEquals(expected, result, 0.00001); + } + + @Test + void testTemperatureUnits() { + final Set<String> expectedUnits = Set.of("Celsius", "Fahrenheit", "Kelvin", "Réaumur", "Rankine", "Delisle"); + assertEquals(expectedUnits, UnitConversions.TEMPERATURE.availableUnits()); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/UnitsConverterTest.java b/src/test/java/com/thealgorithms/conversions/UnitsConverterTest.java new file mode 100644 index 000000000000..0952129efb4d --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/UnitsConverterTest.java @@ -0,0 +1,44 @@ +package com.thealgorithms.conversions; + +import static java.util.Map.entry; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import org.apache.commons.lang3.tuple.Pair; +import org.junit.jupiter.api.Test; + +public class UnitsConverterTest { + + @Test + void testConvertThrowsForSameUnits() { + final UnitsConverter someConverter = new UnitsConverter(Map.ofEntries(entry(Pair.of("A", "B"), new AffineConverter(10.0, -20.0)))); + assertThrows(IllegalArgumentException.class, () -> someConverter.convert("A", "A", 20.0)); + assertThrows(IllegalArgumentException.class, () -> someConverter.convert("B", "B", 20.0)); + } + + @Test + void testConvertThrowsForUnknownUnits() { + final UnitsConverter someConverter = new UnitsConverter(Map.ofEntries(entry(Pair.of("A", "B"), new AffineConverter(10.0, -20.0)))); + assertThrows(NoSuchElementException.class, () -> someConverter.convert("A", "X", 20.0)); + assertThrows(NoSuchElementException.class, () -> someConverter.convert("X", "A", 20.0)); + assertThrows(NoSuchElementException.class, () -> someConverter.convert("X", "Y", 20.0)); + } + + @Test + void testAvailableUnits() { + final UnitsConverter someConverter = new UnitsConverter(Map.ofEntries(entry(Pair.of("Celsius", "Fahrenheit"), new AffineConverter(9.0 / 5.0, 32.0)), entry(Pair.of("Kelvin", "Celsius"), new AffineConverter(1.0, -273.15)))); + assertEquals(Set.of("Celsius", "Fahrenheit", "Kelvin"), someConverter.availableUnits()); + } + + @Test + void testInvertConversion() { + final UnitsConverter someConverter = new UnitsConverter(Map.ofEntries(entry(Pair.of("A", "B"), new AffineConverter(2.0, 5.0)))); + // Check conversion from A -> B + assertEquals(25.0, someConverter.convert("A", "B", 10.0), 0.0001); + // Check inverse conversion from B -> A + assertEquals(10.0, someConverter.convert("B", "A", 25.0), 0.0001); + } +} diff --git a/src/test/java/com/thealgorithms/conversions/WordsToNumberTest.java b/src/test/java/com/thealgorithms/conversions/WordsToNumberTest.java new file mode 100644 index 000000000000..fbf63e37946b --- /dev/null +++ b/src/test/java/com/thealgorithms/conversions/WordsToNumberTest.java @@ -0,0 +1,114 @@ +package com.thealgorithms.conversions; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.math.BigDecimal; +import org.junit.jupiter.api.Test; + +public class WordsToNumberTest { + + @Test + void testNullInput() { + WordsToNumberException exception = assertThrows(WordsToNumberException.class, () -> WordsToNumber.convert(null)); + assertEquals(WordsToNumberException.ErrorType.NULL_INPUT, exception.getErrorType(), "Exception should be of type NULL_INPUT"); + } + + @Test + void testStandardCases() { + assertEquals("0", WordsToNumber.convert("zero"), "'zero' should convert to '0'"); + assertEquals("5", WordsToNumber.convert("five"), "'five' should convert to '5'"); + assertEquals("21", WordsToNumber.convert("twenty one"), "'twenty one' should convert to '21'"); + assertEquals("101", WordsToNumber.convert("one hundred one"), "'one hundred' should convert to '101'"); + assertEquals("342", WordsToNumber.convert("three hundred and forty two"), "'three hundred and forty two' should convert to '342'"); + } + + @Test + void testLargeNumbers() { + assertEquals("1000000", WordsToNumber.convert("one million"), "'one million' should convert to '1000000'"); + assertEquals("1000000000", WordsToNumber.convert("one billion"), "'one billion' should convert to '1000000000'"); + assertEquals("1000000000000", WordsToNumber.convert("one trillion"), "'one trillion' should convert to '1000000000000'"); + assertEquals("999000000900999", WordsToNumber.convert("nine hundred ninety nine trillion nine hundred thousand nine hundred and ninety nine"), "'nine hundred ninety nine trillion nine hundred thousand nine hundred and ninety nine' should convert to '999000000900999'"); + } + + @Test + void testNegativeNumbers() { + assertEquals("-5", WordsToNumber.convert("negative five"), "'negative five' should convert to '-5'"); + assertEquals("-120", WordsToNumber.convert("negative one hundred and twenty"), "'negative one hundred and twenty' should convert correctly"); + } + + @Test + void testNegativeLargeNumbers() { + assertEquals("-1000000000000", WordsToNumber.convert("negative one trillion"), "'negative one trillion' should convert to '-1000000000000'"); + assertEquals("-9876543210987", WordsToNumber.convert("Negative Nine Trillion Eight Hundred Seventy Six Billion Five Hundred Forty Three Million Two Hundred Ten Thousand Nine Hundred Eighty Seven"), ""); + } + + @Test + void testDecimalNumbers() { + assertEquals("3.1415", WordsToNumber.convert("three point one four one five"), "'three point one four one five' should convert to '3.1415'"); + assertEquals("-2.718", WordsToNumber.convert("negative two point seven one eight"), "'negative two point seven one eight' should convert to '-2.718'"); + assertEquals("-1E-7", WordsToNumber.convert("negative zero point zero zero zero zero zero zero one"), "'negative zero point zero zero zero zero zero zero one' should convert to '-1E-7'"); + } + + @Test + void testLargeDecimalNumbers() { + assertEquals("1000000000.0000000001", WordsToNumber.convert("one billion point zero zero zero zero zero zero zero zero zero one"), "Tests a large whole number with a tiny fractional part"); + assertEquals("999999999999999.9999999999999", + WordsToNumber.convert("nine hundred ninety nine trillion nine hundred ninety nine billion nine hundred ninety nine million nine hundred ninety nine thousand nine hundred ninety nine point nine nine nine nine nine nine nine nine nine nine nine nine nine"), + "Tests maximum scale handling for large decimal numbers"); + assertEquals("0.505", WordsToNumber.convert("zero point five zero five"), "Tests a decimal with an internal zero, ensuring correct parsing"); + assertEquals("42.00000000000001", WordsToNumber.convert("forty two point zero zero zero zero zero zero zero zero zero zero zero zero zero one"), "Tests a decimal with leading zeros before a significant figure"); + assertEquals("7.89E-7", WordsToNumber.convert("zero point zero zero zero zero zero zero seven eight nine"), "Tests scientific notation for a small decimal with multiple digits"); + assertEquals("0.999999", WordsToNumber.convert("zero point nine nine nine nine nine nine"), "Tests a decimal close to one with multiple repeated digits"); + } + + @Test + void testCaseInsensitivity() { + assertEquals("21", WordsToNumber.convert("TWENTY-ONE"), "Uppercase should still convert correctly"); + assertEquals("-100.0001", WordsToNumber.convert("negAtiVe OnE HuNdReD, point ZeRO Zero zERo ONE"), "Mixed case should still convert correctly"); + assertEquals("-225647.00019", WordsToNumber.convert("nEgative twO HundRed, and twenty-Five thOusaNd, six huNdred Forty-Seven, Point zero zero zero One nInE")); + } + + @Test + void testInvalidInputs() { + WordsToNumberException exception; + + exception = assertThrows(WordsToNumberException.class, () -> WordsToNumber.convert("negative one hundred AlPha")); + assertEquals(WordsToNumberException.ErrorType.UNKNOWN_WORD, exception.getErrorType()); + + exception = assertThrows(WordsToNumberException.class, () -> WordsToNumber.convert("twenty thirteen")); + assertEquals(WordsToNumberException.ErrorType.UNEXPECTED_WORD, exception.getErrorType()); + + exception = assertThrows(WordsToNumberException.class, () -> WordsToNumber.convert("negative negative ten")); + assertEquals(WordsToNumberException.ErrorType.MULTIPLE_NEGATIVES, exception.getErrorType()); + + exception = assertThrows(WordsToNumberException.class, () -> WordsToNumber.convert("one hundred hundred")); + assertEquals(WordsToNumberException.ErrorType.UNEXPECTED_WORD, exception.getErrorType()); + + exception = assertThrows(WordsToNumberException.class, () -> WordsToNumber.convert("one thousand and hundred")); + assertEquals(WordsToNumberException.ErrorType.INVALID_CONJUNCTION, exception.getErrorType()); + + exception = assertThrows(WordsToNumberException.class, () -> WordsToNumber.convert("one thousand hundred")); + assertEquals(WordsToNumberException.ErrorType.UNEXPECTED_WORD, exception.getErrorType()); + + exception = assertThrows(WordsToNumberException.class, () -> WordsToNumber.convert("nine hundred and nine hundred")); + assertEquals(WordsToNumberException.ErrorType.INVALID_CONJUNCTION, exception.getErrorType()); + + exception = assertThrows(WordsToNumberException.class, () -> WordsToNumber.convert("forty two point")); + assertEquals(WordsToNumberException.ErrorType.MISSING_DECIMAL_NUMBERS, exception.getErrorType()); + + exception = assertThrows(WordsToNumberException.class, () -> WordsToNumber.convert("sixty seven point hello")); + assertEquals(WordsToNumberException.ErrorType.UNEXPECTED_WORD_AFTER_POINT, exception.getErrorType()); + + exception = assertThrows(WordsToNumberException.class, () -> WordsToNumber.convert("one negative")); + assertEquals(WordsToNumberException.ErrorType.INVALID_NEGATIVE, exception.getErrorType()); + } + + @Test + void testConvertToBigDecimal() { + assertEquals(new BigDecimal("-100000000000000.056"), WordsToNumber.convertToBigDecimal("negative one hundred trillion point zero five six"), "should convert to appropriate BigDecimal value"); + + WordsToNumberException exception = assertThrows(WordsToNumberException.class, () -> WordsToNumber.convertToBigDecimal(null)); + assertEquals(WordsToNumberException.ErrorType.NULL_INPUT, exception.getErrorType(), "Exception should be of type NULL_INPUT"); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/bag/BagTest.java b/src/test/java/com/thealgorithms/datastructures/bag/BagTest.java new file mode 100644 index 000000000000..b7e64851383c --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/bag/BagTest.java @@ -0,0 +1,159 @@ +package com.thealgorithms.datastructures.bag; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.thealgorithms.datastructures.bags.Bag; +import java.util.Iterator; +import org.junit.jupiter.api.Test; + +class BagTest { + + @Test + void testBagOperations() { + Bag<String> bag = new Bag<>(); + assertTrue(bag.isEmpty(), "Bag should be empty initially"); + assertEquals(0, bag.size(), "Bag size should be 0 initially"); + + bag.add("item1"); + bag.add("item2"); + bag.add("item1"); // adding duplicate item + + assertFalse(bag.isEmpty(), "Bag should not be empty after adding elements"); + assertEquals(3, bag.size(), "Bag size should be 3 after adding 3 elements"); + + assertTrue(bag.contains("item1"), "Bag should contain 'item1'"); + assertTrue(bag.contains("item2"), "Bag should contain 'item2'"); + assertFalse(bag.contains("item3"), "Bag should not contain 'item3'"); + assertFalse(bag.contains(null), "Bag should not contain null"); + + // Test iteration + int count = 0; + for (String item : bag) { + assertTrue(item.equals("item1") || item.equals("item2"), "Item should be either 'item1' or 'item2'"); + count++; + } + assertEquals(3, count, "Iterator should traverse all 3 items"); + } + + @Test + void testBagInitialization() { + Bag<String> bag = new Bag<>(); + assertTrue(bag.isEmpty(), "Bag should be empty initially"); + assertEquals(0, bag.size(), "Bag size should be 0 initially"); + } + + @Test + void testAddElements() { + Bag<String> bag = new Bag<>(); + bag.add("item1"); + bag.add("item2"); + bag.add("item1"); // Adding duplicate item + + assertFalse(bag.isEmpty(), "Bag should not be empty after adding elements"); + assertEquals(3, bag.size(), "Bag size should be 3 after adding 3 elements"); + } + + @Test + void testContainsMethod() { + Bag<String> bag = new Bag<>(); + bag.add("item1"); + bag.add("item2"); + + assertTrue(bag.contains("item1"), "Bag should contain 'item1'"); + assertTrue(bag.contains("item2"), "Bag should contain 'item2'"); + assertFalse(bag.contains("item3"), "Bag should not contain 'item3'"); + assertFalse(bag.contains(null), "Bag should not contain null"); + } + + @Test + void testContainsAfterAdditions() { + Bag<String> bag = new Bag<>(); + bag.add("item1"); + bag.add("item2"); + assertTrue(bag.contains("item1"), "Bag should contain 'item1' after addition"); + assertTrue(bag.contains("item2"), "Bag should contain 'item2' after addition"); + } + + @Test + void testIterator() { + Bag<String> bag = new Bag<>(); + bag.add("item1"); + bag.add("item2"); + bag.add("item3"); + + int count = 0; + for (String item : bag) { + assertTrue(item.equals("item1") || item.equals("item2") || item.equals("item3"), "Item should be one of 'item1', 'item2', or 'item3'"); + count++; + } + assertEquals(3, count, "Iterator should traverse all 3 items"); + } + + @Test + void testIteratorEmptyBag() { + Bag<String> bag = new Bag<>(); + int count = 0; + for (String ignored : bag) { + org.junit.jupiter.api.Assertions.fail("Iterator should not return any items for an empty bag"); + } + assertEquals(0, count, "Iterator should not traverse any items in an empty bag"); + } + + @Test + void testRemoveMethodThrowsException() { + Bag<String> bag = new Bag<>(); + bag.add("item1"); + Iterator<String> iterator = bag.iterator(); + assertThrows(UnsupportedOperationException.class, iterator::remove, "Remove operation should throw UnsupportedOperationException"); + } + + @Test + void testMultipleDuplicates() { + Bag<String> bag = new Bag<>(); + bag.add("item1"); + bag.add("item1"); + bag.add("item1"); // Add three duplicates + + assertEquals(3, bag.size(), "Bag size should be 3 after adding three duplicates"); + assertTrue(bag.contains("item1"), "Bag should contain 'item1'"); + } + + @Test + void testLargeNumberOfElements() { + Bag<Integer> bag = new Bag<>(); + for (int i = 0; i < 1000; i++) { + bag.add(i); + } + assertEquals(1000, bag.size(), "Bag should contain 1000 elements"); + } + + @Test + void testMixedTypeElements() { + Bag<Object> bag = new Bag<>(); + bag.add("string"); + bag.add(1); + bag.add(2.0); + + assertTrue(bag.contains("string"), "Bag should contain a string"); + assertTrue(bag.contains(1), "Bag should contain an integer"); + assertTrue(bag.contains(2.0), "Bag should contain a double"); + } + + @Test + void testIteratorWithDuplicates() { + Bag<String> bag = new Bag<>(); + bag.add("item1"); + bag.add("item1"); + bag.add("item2"); + + int count = 0; + for (String item : bag) { + assertTrue(item.equals("item1") || item.equals("item2"), "Item should be either 'item1' or 'item2'"); + count++; + } + assertEquals(3, count, "Iterator should traverse all 3 items including duplicates"); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/bloomfilter/BloomFilterTest.java b/src/test/java/com/thealgorithms/datastructures/bloomfilter/BloomFilterTest.java new file mode 100644 index 000000000000..048eb7e481a7 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/bloomfilter/BloomFilterTest.java @@ -0,0 +1,116 @@ +package com.thealgorithms.datastructures.bloomfilter; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class BloomFilterTest { + private BloomFilter<String> bloomFilter; + + @BeforeEach + void setUp() { + bloomFilter = new BloomFilter<>(3, 100); + } + + @Test + public void testIntegerContains() { + BloomFilter<Integer> bloomFilter = new BloomFilter<>(3, 10); + bloomFilter.insert(3); + bloomFilter.insert(17); + + Assertions.assertTrue(bloomFilter.contains(3)); + Assertions.assertTrue(bloomFilter.contains(17)); + } + + @Test + public void testStringContains() { + bloomFilter.insert("omar"); + bloomFilter.insert("mahamid"); + + Assertions.assertTrue(bloomFilter.contains("omar")); + Assertions.assertTrue(bloomFilter.contains("mahamid")); + } + + @Test + void testInsertAndContains() { + bloomFilter.insert("hello"); + bloomFilter.insert("world"); + + Assertions.assertTrue(bloomFilter.contains("hello")); + Assertions.assertTrue(bloomFilter.contains("world")); + Assertions.assertFalse(bloomFilter.contains("java")); + } + + @Test + void testFalsePositive() { + bloomFilter.insert("apple"); + bloomFilter.insert("banana"); + + Assertions.assertFalse(bloomFilter.contains("grape")); + Assertions.assertFalse(bloomFilter.contains("orange")); + } + + @Test + void testMultipleInsertions() { + for (int i = 0; i < 100; i++) { + bloomFilter.insert("key" + i); + } + + for (int i = 0; i < 100; i++) { + Assertions.assertTrue(bloomFilter.contains("key" + i)); + } + + Assertions.assertFalse(bloomFilter.contains("key" + 200)); + } + + @Test + void testEmptyFilterContains() { + Assertions.assertFalse(bloomFilter.contains("notInserted"), "Filter should not contain any elements when empty"); + Assertions.assertFalse(bloomFilter.contains(null), "Filter should not contain null elements"); + } + + @Test + void testDifferentTypes() { + BloomFilter<Object> filter = new BloomFilter<>(3, 100); + filter.insert("string"); + filter.insert(123); + filter.insert(45.67); + + Assertions.assertTrue(filter.contains("string"), "Filter should contain the string 'string'"); + Assertions.assertTrue(filter.contains(123), "Filter should contain the integer 123"); + Assertions.assertTrue(filter.contains(45.67), "Filter should contain the double 45.67"); + Assertions.assertFalse(filter.contains("missing"), "Filter should not contain elements that were not inserted"); + } + + @Test + void testFalsePositiveAfterInsertions() { + bloomFilter.insert("cat"); + bloomFilter.insert("dog"); + bloomFilter.insert("fish"); + + // Checking for an element that was not added + Assertions.assertFalse(bloomFilter.contains("bird"), "Filter should not contain 'bird' which was never inserted"); + + // To increase chances of false positives, we can add more items + for (int i = 0; i < 100; i++) { + bloomFilter.insert("item" + i); + } + + Assertions.assertFalse(bloomFilter.contains("nonexistent"), "Filter should not contain 'nonexistent' which was never inserted"); + } + + @Test + void testBoundaryConditions() { + BloomFilter<String> filter = new BloomFilter<>(3, 10); + filter.insert("a"); + filter.insert("b"); + filter.insert("c"); + filter.insert("d"); + + Assertions.assertTrue(filter.contains("a"), "Filter should contain 'a'"); + Assertions.assertTrue(filter.contains("b"), "Filter should contain 'b'"); + Assertions.assertTrue(filter.contains("c"), "Filter should contain 'c'"); + Assertions.assertTrue(filter.contains("d"), "Filter should contain 'd'"); + Assertions.assertFalse(filter.contains("e"), "Filter should not contain 'e' which was not inserted"); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/buffers/CircularBufferTest.java b/src/test/java/com/thealgorithms/datastructures/buffers/CircularBufferTest.java new file mode 100644 index 000000000000..b115fc187b1a --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/buffers/CircularBufferTest.java @@ -0,0 +1,88 @@ +package com.thealgorithms.datastructures.buffers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class CircularBufferTest { + + @Test + void testInitialization() { + CircularBuffer<Integer> buffer = new CircularBuffer<>(5); + assertTrue(buffer.isEmpty()); + assertEquals(Boolean.FALSE, buffer.isFull()); + } + + @Test + void testPutAndGet() { + CircularBuffer<String> buffer = new CircularBuffer<>(3); + + assertTrue(buffer.put("A")); + assertEquals(Boolean.FALSE, buffer.isEmpty()); + assertEquals(Boolean.FALSE, buffer.isFull()); + + buffer.put("B"); + buffer.put("C"); + assertTrue(buffer.isFull()); + + assertEquals("A", buffer.get()); + assertEquals("B", buffer.get()); + assertEquals("C", buffer.get()); + assertTrue(buffer.isEmpty()); + } + + @Test + void testOverwrite() { + CircularBuffer<Integer> buffer = new CircularBuffer<>(3); + + buffer.put(1); + buffer.put(2); + buffer.put(3); + assertEquals(Boolean.FALSE, buffer.put(4)); // This should overwrite 1 + + assertEquals(2, buffer.get()); + assertEquals(3, buffer.get()); + assertEquals(4, buffer.get()); + assertNull(buffer.get()); + } + + @Test + void testEmptyBuffer() { + CircularBuffer<Double> buffer = new CircularBuffer<>(2); + assertNull(buffer.get()); + } + + @Test + void testFullBuffer() { + CircularBuffer<Character> buffer = new CircularBuffer<>(2); + buffer.put('A'); + buffer.put('B'); + assertTrue(buffer.isFull()); + assertEquals(Boolean.FALSE, buffer.put('C')); // This should overwrite 'A' + assertEquals('B', buffer.get()); + assertEquals('C', buffer.get()); + } + + @Test + void testIllegalArguments() { + assertThrows(IllegalArgumentException.class, () -> new CircularBuffer<>(0)); + assertThrows(IllegalArgumentException.class, () -> new CircularBuffer<>(-1)); + + CircularBuffer<String> buffer = new CircularBuffer<>(1); + assertThrows(IllegalArgumentException.class, () -> buffer.put(null)); + } + + @Test + void testLargeBuffer() { + CircularBuffer<Integer> buffer = new CircularBuffer<>(1000); + for (int i = 0; i < 1000; i++) { + buffer.put(i); + } + assertTrue(buffer.isFull()); + buffer.put(1000); // This should overwrite 0 + assertEquals(1, buffer.get()); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/caches/LFUCacheTest.java b/src/test/java/com/thealgorithms/datastructures/caches/LFUCacheTest.java new file mode 100644 index 000000000000..8ad927564aa5 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/caches/LFUCacheTest.java @@ -0,0 +1,138 @@ +package com.thealgorithms.datastructures.caches; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class LFUCacheTest { + + @Test + void testLFUCacheWithIntegerValueShouldPass() { + LFUCache<Integer, Integer> lfuCache = new LFUCache<>(5); + lfuCache.put(1, 10); + lfuCache.put(2, 20); + lfuCache.put(3, 30); + lfuCache.put(4, 40); + lfuCache.put(5, 50); + + // get method call will increase frequency of key 1 by 1 + assertEquals(10, lfuCache.get(1)); + + // this operation will remove value with key as 2 + lfuCache.put(6, 60); + + // will return null as value with key 2 is now evicted + assertNull(lfuCache.get(2)); + + // should return 60 + assertEquals(60, lfuCache.get(6)); + + // this operation will remove value with key as 3 + lfuCache.put(7, 70); + + assertNull(lfuCache.get(2)); + assertEquals(70, lfuCache.get(7)); + } + + @Test + void testLFUCacheWithStringValueShouldPass() { + LFUCache<Integer, String> lfuCache = new LFUCache<>(5); + lfuCache.put(1, "Alpha"); + lfuCache.put(2, "Beta"); + lfuCache.put(3, "Gamma"); + lfuCache.put(4, "Delta"); + lfuCache.put(5, "Epsilon"); + + // get method call will increase frequency of key 1 by 1 + assertEquals("Alpha", lfuCache.get(1)); + + // this operation will remove value with key as 2 + lfuCache.put(6, "Digamma"); + + // will return null as value with key 2 is now evicted + assertNull(lfuCache.get(2)); + + // should return string Digamma + assertEquals("Digamma", lfuCache.get(6)); + + // this operation will remove value with key as 3 + lfuCache.put(7, "Zeta"); + + assertNull(lfuCache.get(2)); + assertEquals("Zeta", lfuCache.get(7)); + } + + @Test + void testUpdateValueShouldPreserveFrequency() { + LFUCache<Integer, String> lfuCache = new LFUCache<>(3); + lfuCache.put(1, "A"); + lfuCache.put(2, "B"); + lfuCache.put(3, "C"); + + assertEquals("A", lfuCache.get(1)); // Accessing key 1 + lfuCache.put(4, "D"); // This should evict key 2 + + assertNull(lfuCache.get(2)); // Key 2 should be evicted + assertEquals("C", lfuCache.get(3)); // Key 3 should still exist + assertEquals("A", lfuCache.get(1)); // Key 1 should still exist + + lfuCache.put(1, "Updated A"); // Update the value of key 1 + assertEquals("Updated A", lfuCache.get(1)); // Check if the update was successful + } + + @Test + void testEvictionPolicyWhenFull() { + LFUCache<Integer, String> lfuCache = new LFUCache<>(2); + lfuCache.put(1, "One"); + lfuCache.put(2, "Two"); + + assertEquals("One", lfuCache.get(1)); // Access key 1 + lfuCache.put(3, "Three"); // This should evict key 2 (least frequently used) + + assertNull(lfuCache.get(2)); // Key 2 should be evicted + assertEquals("One", lfuCache.get(1)); // Key 1 should still exist + assertEquals("Three", lfuCache.get(3)); // Check if key 3 exists + } + + @Test + void testGetFromEmptyCacheShouldReturnNull() { + LFUCache<Integer, String> lfuCache = new LFUCache<>(3); + assertNull(lfuCache.get(1)); // Should return null as the cache is empty + } + + @Test + void testPutNullValueShouldStoreNull() { + LFUCache<Integer, String> lfuCache = new LFUCache<>(3); + lfuCache.put(1, null); // Store a null value + + assertNull(lfuCache.get(1)); // Should return null + } + + @Test + void testInvalidCacheCapacityShouldThrowException() { + assertThrows(IllegalArgumentException.class, () -> new LFUCache<>(0)); + assertThrows(IllegalArgumentException.class, () -> new LFUCache<>(-1)); + } + + @Test + void testMultipleAccessPatterns() { + LFUCache<Integer, String> lfuCache = new LFUCache<>(5); + lfuCache.put(1, "A"); + lfuCache.put(2, "B"); + lfuCache.put(3, "C"); + lfuCache.put(4, "D"); + + assertEquals("A", lfuCache.get(1)); // Access 1 + lfuCache.put(5, "E"); // Should not evict anything yet + lfuCache.put(6, "F"); // Evict B + + assertNull(lfuCache.get(2)); // B should be evicted + assertEquals("C", lfuCache.get(3)); // C should still exist + assertEquals("D", lfuCache.get(4)); // D should still exist + assertEquals("A", lfuCache.get(1)); // A should still exist + assertEquals("E", lfuCache.get(5)); // E should exist + assertEquals("F", lfuCache.get(6)); // F should exist + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/caches/LRUCacheTest.java b/src/test/java/com/thealgorithms/datastructures/caches/LRUCacheTest.java new file mode 100644 index 000000000000..99b9952435c4 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/caches/LRUCacheTest.java @@ -0,0 +1,149 @@ +package com.thealgorithms.datastructures.caches; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class LRUCacheTest { + private static final int SIZE = 5; + private LRUCache<Integer, Integer> cache; + + @BeforeEach + void setUp() { + cache = new LRUCache<>(SIZE); + } + + @Test + public void testBasicOperations() { + cache.put(1, 100); + assertEquals(100, cache.get(1)); + assertNull(cache.get(2)); + } + + @Test + public void testEvictionPolicy() { + // Fill cache to capacity + for (int i = 0; i < SIZE; i++) { + cache.put(i, i * 100); + } + + // Verify all elements are present + for (int i = 0; i < SIZE; i++) { + assertEquals(i * 100, cache.get(i)); + } + + // Add one more element, causing eviction of least recently used + cache.put(SIZE, SIZE * 100); + + // First element should be evicted + assertNull(cache.get(0)); + assertEquals(SIZE * 100, cache.get(SIZE)); + } + + @Test + public void testAccessOrder() { + // Fill cache + for (int i = 0; i < SIZE; i++) { + cache.put(i, i); + } + + // Access first element, making it most recently used + cache.get(0); + + // Add new element, should evict second element (1) + cache.put(SIZE, SIZE); + + assertEquals(0, cache.get(0)); // Should still exist + assertNull(cache.get(1)); // Should be evicted + assertEquals(SIZE, cache.get(SIZE)); // Should exist + } + + @Test + public void testUpdateExistingKey() { + cache.put(1, 100); + assertEquals(100, cache.get(1)); + + // Update existing key + cache.put(1, 200); + assertEquals(200, cache.get(1)); + } + + @Test + public void testNullValues() { + cache.put(1, null); + assertNull(cache.get(1)); + + // Update null to non-null + cache.put(1, 100); + assertEquals(100, cache.get(1)); + + // Update non-null to null + cache.put(1, null); + assertNull(cache.get(1)); + } + + @Test + public void testStringKeysAndValues() { + LRUCache<String, String> stringCache = new LRUCache<>(SIZE); + + stringCache.put("key1", "value1"); + stringCache.put("key2", "value2"); + + assertEquals("value1", stringCache.get("key1")); + assertEquals("value2", stringCache.get("key2")); + } + + @Test + public void testLongSequenceOfOperations() { + // Add elements beyond capacity multiple times + for (int i = 0; i < SIZE * 3; i++) { + cache.put(i, i * 100); + + // Verify only the last SIZE elements are present + for (int j = Math.max(0, i - SIZE + 1); j <= i; j++) { + assertEquals(j * 100, cache.get(j)); + } + + // Verify elements before the window are evicted + if (i >= SIZE) { + assertNull(cache.get(i - SIZE)); + } + } + } + + @Test + void testCustomObjects() { + class TestObject { + private final String value; + + TestObject(String value) { + this.value = value; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof TestObject) { + return value.equals(((TestObject) obj).value); + } + return false; + } + + @Override + public int hashCode() { + return value == null ? 0 : value.hashCode(); + } + } + + LRUCache<Integer, TestObject> objectCache = new LRUCache<>(SIZE); + TestObject obj1 = new TestObject("test1"); + TestObject obj2 = new TestObject("test2"); + + objectCache.put(1, obj1); + objectCache.put(2, obj2); + + assertEquals(obj1, objectCache.get(1)); + assertEquals(obj2, objectCache.get(2)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/caches/MRUCacheTest.java b/src/test/java/com/thealgorithms/datastructures/caches/MRUCacheTest.java new file mode 100644 index 000000000000..50303ba239f6 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/caches/MRUCacheTest.java @@ -0,0 +1,125 @@ +package com.thealgorithms.datastructures.caches; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.Test; + +public class MRUCacheTest { + + private static final int SIZE = 5; + + @Test + public void putAndGetIntegerValues() { + MRUCache<Integer, Integer> mruCache = new MRUCache<>(SIZE); + + for (int i = 0; i < SIZE; i++) { + mruCache.put(i, i); + } + + for (int i = 0; i < SIZE; i++) { + assertEquals(i, mruCache.get(i)); + } + } + + @Test + public void putAndGetStringValues() { + MRUCache<String, String> mruCache = new MRUCache<>(SIZE); + + for (int i = 0; i < SIZE; i++) { + mruCache.put("key" + i, "value" + i); + } + + for (int i = 0; i < SIZE; i++) { + assertEquals("value" + i, mruCache.get("key" + i)); + } + } + + @Test + public void nullKeysAndValues() { + MRUCache<Integer, Integer> mruCache = new MRUCache<>(SIZE); + mruCache.put(null, 2); + mruCache.put(6, null); + + assertEquals(2, mruCache.get(null)); + assertNull(mruCache.get(6)); + } + + @Test + public void overCapacity() { + MRUCache<Integer, Integer> mruCache = new MRUCache<>(SIZE); + + for (int i = 0; i < 10; i++) { + mruCache.put(i, i); + } + + // After inserting 10 items, the cache should have evicted the least recently used ones. + assertEquals(9, mruCache.get(9)); // Most recently used + assertEquals(0, mruCache.get(0)); // Least recently used, should be evicted + } + + @Test + public void overwriteExistingKey() { + MRUCache<Integer, String> mruCache = new MRUCache<>(SIZE); + mruCache.put(1, "one"); + mruCache.put(1, "uno"); // Overwriting the value for key 1 + + assertEquals("uno", mruCache.get(1)); + assertNull(mruCache.get(2)); // Ensure other keys are unaffected + } + + @Test + public void evictionOrder() { + MRUCache<Integer, Integer> mruCache = new MRUCache<>(SIZE); + + for (int i = 0; i < SIZE; i++) { + mruCache.put(i, i); + } + + // Access a key to make it most recently used + mruCache.get(2); + + // Add new items to trigger eviction + mruCache.put(5, 5); + mruCache.put(6, 6); + + // Key 3 should be evicted since 2 is the most recently used + assertEquals(3, mruCache.get(3)); + assertEquals(4, mruCache.get(4)); // Key 4 should still be available + assertEquals(6, mruCache.get(6)); // Key 6 should be available + } + + @Test + public void cacheHandlesLargeValues() { + MRUCache<String, String> mruCache = new MRUCache<>(SIZE); + + for (int i = 0; i < SIZE; i++) { + mruCache.put("key" + i, "value" + i); + } + + // Verify values + for (int i = 0; i < SIZE; i++) { + assertEquals("value" + i, mruCache.get("key" + i)); + } + + // Add large value + mruCache.put("largeKey", "largeValue"); + + // Verify eviction of the least recently used (key 0 should be evicted) + assertEquals("value0", mruCache.get("key0")); + assertEquals("largeValue", mruCache.get("largeKey")); + } + + @Test + public void testEmptyCacheBehavior() { + MRUCache<Integer, Integer> mruCache = new MRUCache<>(SIZE); + + // Verify that accessing any key returns null + assertNull(mruCache.get(1)); + assertNull(mruCache.get(100)); + + // Adding to cache and checking again + mruCache.put(1, 10); + assertEquals(10, mruCache.get(1)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/crdt/GCounterTest.java b/src/test/java/com/thealgorithms/datastructures/crdt/GCounterTest.java new file mode 100644 index 000000000000..eb48c7d9e8d6 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/crdt/GCounterTest.java @@ -0,0 +1,56 @@ +package com.thealgorithms.datastructures.crdt; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class GCounterTest { + @Test + void increment() { + GCounter counter = new GCounter(0, 3); + counter.increment(); + counter.increment(); + counter.increment(); + assertEquals(3, counter.value()); + } + + @Test + void merge() { + GCounter counter1 = new GCounter(0, 3); + counter1.increment(); + GCounter counter2 = new GCounter(1, 3); + counter2.increment(); + counter2.increment(); + GCounter counter3 = new GCounter(2, 3); + counter3.increment(); + counter3.increment(); + counter3.increment(); + counter1.merge(counter2); + counter1.merge(counter3); + counter2.merge(counter1); + counter3.merge(counter2); + assertEquals(6, counter1.value()); + assertEquals(6, counter2.value()); + assertEquals(6, counter3.value()); + } + + @Test + void compare() { + GCounter counter1 = new GCounter(0, 5); + GCounter counter2 = new GCounter(3, 5); + counter1.increment(); + counter1.increment(); + counter2.merge(counter1); + counter2.increment(); + counter2.increment(); + assertTrue(counter1.compare(counter2)); + counter1.increment(); + counter2.increment(); + counter2.merge(counter1); + assertTrue(counter1.compare(counter2)); + counter1.increment(); + assertFalse(counter1.compare(counter2)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/crdt/GSetTest.java b/src/test/java/com/thealgorithms/datastructures/crdt/GSetTest.java new file mode 100644 index 000000000000..b4566aa9b1d8 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/crdt/GSetTest.java @@ -0,0 +1,66 @@ +package com.thealgorithms.datastructures.crdt; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class GSetTest { + + @Test + void testAddElement() { + GSet<String> gSet = new GSet<>(); + gSet.addElement("apple"); + gSet.addElement("orange"); + + assertTrue(gSet.lookup("apple")); + assertTrue(gSet.lookup("orange")); + assertFalse(gSet.lookup("banana")); + } + + @Test + void testLookup() { + GSet<Integer> gSet = new GSet<>(); + gSet.addElement(1); + gSet.addElement(2); + + assertTrue(gSet.lookup(1)); + assertTrue(gSet.lookup(2)); + assertFalse(gSet.lookup(3)); + } + + @Test + void testCompare() { + GSet<String> gSet1 = new GSet<>(); + GSet<String> gSet2 = new GSet<>(); + gSet1.addElement("apple"); + gSet1.addElement("orange"); + gSet2.addElement("orange"); + assertFalse(gSet1.compare(gSet2)); + gSet2.addElement("apple"); + assertTrue(gSet1.compare(gSet2)); + gSet2.addElement("banana"); + assertTrue(gSet1.compare(gSet2)); + } + + @Test + void testMerge() { + GSet<String> gSet1 = new GSet<>(); + GSet<String> gSet2 = new GSet<>(); + + gSet1.addElement("apple"); + gSet1.addElement("orange"); + + gSet2.addElement("orange"); + gSet2.addElement("banana"); + + GSet<String> mergedSet = new GSet<>(); + mergedSet.merge(gSet1); + mergedSet.merge(gSet2); + + assertTrue(mergedSet.lookup("apple")); + assertTrue(mergedSet.lookup("orange")); + assertTrue(mergedSet.lookup("banana")); + assertFalse(mergedSet.lookup("grape")); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/crdt/LWWElementSetTest.java b/src/test/java/com/thealgorithms/datastructures/crdt/LWWElementSetTest.java new file mode 100644 index 000000000000..0356949a8f69 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/crdt/LWWElementSetTest.java @@ -0,0 +1,98 @@ +package com.thealgorithms.datastructures.crdt; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.Instant; +import org.junit.jupiter.api.Test; + +class LWWElementSetTest { + + @Test + void testAddElement() { + LWWElementSet<String> set = new LWWElementSet<>(); + set.add("A"); + assertTrue(set.lookup("A")); + } + + @Test + void testRemoveElement() { + LWWElementSet<String> set = new LWWElementSet<>(); + set.add("A"); + set.remove("A"); + assertFalse(set.lookup("A")); + } + + @Test + void testLookupWithoutAdding() { + LWWElementSet<String> set = new LWWElementSet<>(); + assertFalse(set.lookup("A")); + } + + @Test + void testLookupLaterTimestampsFalse() { + LWWElementSet<String> set = new LWWElementSet<>(); + + set.addSet.put("A", new Element<>("A", Instant.now())); + set.removeSet.put("A", new Element<>("A", Instant.now().plusSeconds(10))); + + assertFalse(set.lookup("A")); + } + + @Test + void testLookupEarlierTimestampsTrue() { + LWWElementSet<String> set = new LWWElementSet<>(); + + set.addSet.put("A", new Element<>("A", Instant.now())); + set.removeSet.put("A", new Element<>("A", Instant.now().minusSeconds(10))); + + assertTrue(set.lookup("A")); + } + + @Test + void testLookupWithConcurrentTimestamps() { + LWWElementSet<String> set = new LWWElementSet<>(); + Instant now = Instant.now(); + set.addSet.put("A", new Element<>("A", now)); + set.removeSet.put("A", new Element<>("A", now)); + assertFalse(set.lookup("A")); + } + + @Test + void testMergeTwoSets() { + LWWElementSet<String> set1 = new LWWElementSet<>(); + LWWElementSet<String> set2 = new LWWElementSet<>(); + + set1.add("A"); + set2.add("B"); + set2.remove("A"); + + set1.merge(set2); + + assertFalse(set1.lookup("A")); + assertTrue(set1.lookup("B")); + } + + @Test + void testMergeWithConflictingTimestamps() { + LWWElementSet<String> set1 = new LWWElementSet<>(); + LWWElementSet<String> set2 = new LWWElementSet<>(); + + Instant now = Instant.now(); + set1.addSet.put("A", new Element<>("A", now.minusSeconds(10))); + set2.addSet.put("A", new Element<>("A", now)); + + set1.merge(set2); + + assertTrue(set1.lookup("A")); + } + + @Test + void testRemoveOlderThanAdd() { + LWWElementSet<String> set = new LWWElementSet<>(); + Instant now = Instant.now(); + set.addSet.put("A", new Element<>("A", now)); + set.removeSet.put("A", new Element<>("A", now.minusSeconds(10))); + assertTrue(set.lookup("A")); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/crdt/ORSetTest.java b/src/test/java/com/thealgorithms/datastructures/crdt/ORSetTest.java new file mode 100644 index 000000000000..f6d19a3e7b20 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/crdt/ORSetTest.java @@ -0,0 +1,88 @@ +package com.thealgorithms.datastructures.crdt; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Set; +import org.junit.jupiter.api.Test; + +class ORSetTest { + + @Test + void testContains() { + ORSet<String> orSet = new ORSet<>(); + orSet.add("A"); + assertTrue(orSet.contains("A")); + } + + @Test + void testAdd() { + ORSet<String> orSet = new ORSet<>(); + orSet.add("A"); + assertTrue(orSet.contains("A")); + } + + @Test + void testRemove() { + ORSet<String> orSet = new ORSet<>(); + orSet.add("A"); + orSet.add("A"); + orSet.remove("A"); + assertFalse(orSet.contains("A")); + } + + @Test + void testElements() { + ORSet<String> orSet = new ORSet<>(); + orSet.add("A"); + orSet.add("B"); + assertEquals(Set.of("A", "B"), orSet.elements()); + } + + @Test + void testCompareEqualSets() { + ORSet<String> orSet1 = new ORSet<>(); + ORSet<String> orSet2 = new ORSet<>(); + + orSet1.add("A"); + orSet2.add("A"); + orSet2.add("B"); + orSet2.add("C"); + orSet2.remove("C"); + orSet1.merge(orSet2); + orSet2.merge(orSet1); + orSet2.remove("B"); + + assertTrue(orSet1.compare(orSet2)); + } + + @Test + void testCompareDifferentSets() { + ORSet<String> orSet1 = new ORSet<>(); + ORSet<String> orSet2 = new ORSet<>(); + + orSet1.add("A"); + orSet2.add("B"); + + assertFalse(orSet1.compare(orSet2)); + } + + @Test + void testMerge() { + ORSet<String> orSet1 = new ORSet<>(); + ORSet<String> orSet2 = new ORSet<>(); + + orSet1.add("A"); + orSet1.add("A"); + orSet1.add("B"); + orSet1.remove("B"); + orSet2.add("B"); + orSet2.add("C"); + orSet2.remove("C"); + orSet1.merge(orSet2); + + assertTrue(orSet1.contains("A")); + assertTrue(orSet1.contains("B")); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/crdt/PNCounterTest.java b/src/test/java/com/thealgorithms/datastructures/crdt/PNCounterTest.java new file mode 100644 index 000000000000..4081b8ae01d8 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/crdt/PNCounterTest.java @@ -0,0 +1,56 @@ +package com.thealgorithms.datastructures.crdt; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class PNCounterTest { + + @Test + public void testIncrement() { + PNCounter counter = new PNCounter(0, 3); + counter.increment(); + assertEquals(1, counter.value()); + } + + @Test + public void testDecrement() { + PNCounter counter = new PNCounter(0, 3); + counter.decrement(); + assertEquals(-1, counter.value()); + } + + @Test + public void testIncrementAndDecrement() { + PNCounter counter = new PNCounter(0, 3); + counter.increment(); + counter.increment(); + counter.decrement(); + assertEquals(1, counter.value()); + } + + @Test + public void testCompare() { + PNCounter counter1 = new PNCounter(0, 3); + counter1.increment(); + PNCounter counter2 = new PNCounter(1, 3); + assertTrue(counter1.compare(counter2)); + counter2.increment(); + assertTrue(counter2.compare(counter1)); + counter1.decrement(); + assertFalse(counter1.compare(counter2)); + } + + @Test + public void testMerge() { + PNCounter counter1 = new PNCounter(0, 3); + counter1.increment(); + counter1.increment(); + PNCounter counter2 = new PNCounter(1, 3); + counter2.increment(); + counter1.merge(counter2); + assertEquals(3, counter1.value()); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/crdt/TwoPSetTest.java b/src/test/java/com/thealgorithms/datastructures/crdt/TwoPSetTest.java new file mode 100644 index 000000000000..dfe392a0d616 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/crdt/TwoPSetTest.java @@ -0,0 +1,69 @@ +package com.thealgorithms.datastructures.crdt; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class TwoPSetTest { + + private TwoPSet<String> set; + + @BeforeEach + void setUp() { + set = new TwoPSet<>(); + } + + @Test + void testLookup() { + set.add("A"); + assertTrue(set.lookup("A")); + assertFalse(set.lookup("B")); + set.remove("A"); + assertFalse(set.lookup("A")); + } + + @Test + void testAdd() { + set.add("A"); + assertTrue(set.lookup("A")); + } + + @Test + void testRemove() { + set.add("A"); + set.remove("A"); + assertFalse(set.lookup("A")); + } + + @Test + void testCompare() { + TwoPSet<String> set1 = new TwoPSet<>(); + set1.add("A"); + set1.add("B"); + TwoPSet<String> set2 = new TwoPSet<>(); + set2.add("A"); + assertFalse(set1.compare(set2)); + set2.add("B"); + assertTrue(set1.compare(set2)); + set1.remove("A"); + assertFalse(set1.compare(set2)); + set2.remove("A"); + assertTrue(set1.compare(set2)); + } + + @Test + void testMerge() { + TwoPSet<String> set1 = new TwoPSet<>(); + set1.add("A"); + set1.add("B"); + TwoPSet<String> set2 = new TwoPSet<>(); + set2.add("B"); + set2.add("C"); + TwoPSet<String> mergedSet = set1.merge(set2); + assertTrue(mergedSet.lookup("A")); + assertTrue(mergedSet.lookup("B")); + assertTrue(mergedSet.lookup("C")); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/disjointsetunion/DisjointSetUnionTest.java b/src/test/java/com/thealgorithms/datastructures/disjointsetunion/DisjointSetUnionTest.java new file mode 100644 index 000000000000..a10a99d40496 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/disjointsetunion/DisjointSetUnionTest.java @@ -0,0 +1,51 @@ +package com.thealgorithms.datastructures.disjointsetunion; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class DisjointSetUnionTest { + + @Test + public void testMakeSet() { + DisjointSetUnion<Integer> dsu = new DisjointSetUnion<>(); + Node<Integer> node = dsu.makeSet(1); + assertNotNull(node); + Assertions.assertEquals(node, node.parent); + } + + @Test + public void testUnionFindSet() { + DisjointSetUnion<Integer> dsu = new DisjointSetUnion<>(); + Node<Integer> node1 = dsu.makeSet(1); + Node<Integer> node2 = dsu.makeSet(2); + Node<Integer> node3 = dsu.makeSet(3); + Node<Integer> node4 = dsu.makeSet(4); + + dsu.unionSets(node1, node2); + dsu.unionSets(node3, node2); + dsu.unionSets(node3, node4); + dsu.unionSets(node1, node3); + + Node<Integer> root1 = dsu.findSet(node1); + Node<Integer> root2 = dsu.findSet(node2); + Node<Integer> root3 = dsu.findSet(node3); + Node<Integer> root4 = dsu.findSet(node4); + + Assertions.assertEquals(node1, node1.parent); + Assertions.assertEquals(node1, node2.parent); + Assertions.assertEquals(node1, node3.parent); + Assertions.assertEquals(node1, node4.parent); + + Assertions.assertEquals(node1, root1); + Assertions.assertEquals(node1, root2); + Assertions.assertEquals(node1, root3); + Assertions.assertEquals(node1, root4); + + Assertions.assertEquals(1, node1.rank); + Assertions.assertEquals(0, node2.rank); + Assertions.assertEquals(0, node3.rank); + Assertions.assertEquals(0, node4.rank); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/dynamicarray/DynamicArrayTest.java b/src/test/java/com/thealgorithms/datastructures/dynamicarray/DynamicArrayTest.java new file mode 100644 index 000000000000..8fdc93e1ca22 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/dynamicarray/DynamicArrayTest.java @@ -0,0 +1,258 @@ +package com.thealgorithms.datastructures.dynamicarray; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.stream.Collectors; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class DynamicArrayTest { + + private DynamicArray<String> array; + + @BeforeEach + public void setUp() { + array = new DynamicArray<>(); + } + + @Test + public void testGetElement() { + array.add("Alice"); + array.add("Bob"); + array.add("Charlie"); + array.add("David"); + assertEquals("Bob", array.get(1)); + } + + @Test + public void testGetInvalidIndex() { + assertThrows(IndexOutOfBoundsException.class, () -> array.get(-1)); + assertThrows(IndexOutOfBoundsException.class, () -> array.get(10)); + assertThrows(IndexOutOfBoundsException.class, () -> array.get(100)); + } + + @Test + public void testAddElement() { + array.add("Alice"); + array.add("Bob"); + assertEquals(2, array.getSize()); + assertEquals("Alice", array.get(0)); + assertEquals("Bob", array.get(1)); + } + + @Test + public void testAddAndGet() { + array.add("Alice"); + array.add("Bob"); + + assertEquals("Alice", array.get(0)); + assertEquals("Bob", array.get(1)); + assertThrows(IndexOutOfBoundsException.class, () -> array.get(2)); + } + + @Test + public void testAddBeyondCapacity() { + for (int i = 0; i < 20; i++) { + array.add("Element " + i); + } + assertEquals(20, array.getSize()); + assertEquals("Element 19", array.get(19)); + } + + @Test + public void testPutElement() { + array.put(5, "Placeholder"); + assertEquals(6, array.getSize()); + assertEquals("Placeholder", array.get(5)); + } + + @Test + public void testPutElementBeyondCapacity() { + array.put(20, "FarAway"); + assertEquals(21, array.getSize()); + assertEquals("FarAway", array.get(20)); + } + + @Test + public void testPutAndDynamicCapacity() { + array.put(0, "Alice"); + array.put(2, "Bob"); // Tests capacity expansion + + assertEquals("Alice", array.get(0)); + assertEquals("Bob", array.get(2)); + assertEquals(3, array.getSize()); // Size should be 3 due to index 2 + } + + @Test + public void testRemoveElement() { + array.add("Alice"); + array.add("Bob"); + String removed = array.remove(0); + assertEquals("Alice", removed); + assertEquals(1, array.getSize()); + assertEquals("Bob", array.get(0)); + } + + @Test + public void testRemoveInvalidIndex() { + assertThrows(IndexOutOfBoundsException.class, () -> array.remove(-1)); + assertThrows(IndexOutOfBoundsException.class, () -> array.remove(10)); + } + + @Test + public void testRemoveComplex() { + array.add("Alice"); + array.add("Bob"); + array.add("Charlie"); + + assertEquals("Bob", array.remove(1)); + assertEquals("Alice", array.get(0)); + assertEquals("Charlie", array.get(1)); + assertThrows(IndexOutOfBoundsException.class, () -> array.remove(2)); + } + + @Test + public void testRemoveEdgeCases() { + array.add("Alice"); + array.add("Bob"); + + assertEquals("Alice", array.remove(0)); + assertEquals(1, array.getSize()); + assertEquals("Bob", array.get(0)); + + assertEquals("Bob", array.remove(0)); + assertTrue(array.isEmpty()); + assertThrows(IndexOutOfBoundsException.class, () -> array.get(0)); + } + + @Test + public void testIsEmpty() { + assertTrue(array.isEmpty()); + + array.add("Alice"); + assertFalse(array.isEmpty()); + + array.remove(0); + assertTrue(array.isEmpty()); + } + + @Test + public void testSize() { + DynamicArray<String> array = new DynamicArray<>(); + assertEquals(0, array.getSize()); + + array.add("Alice"); + array.add("Bob"); + assertEquals(2, array.getSize()); + + array.remove(0); + assertEquals(1, array.getSize()); + } + + @Test + public void testToString() { + array.add("Alice"); + array.add("Bob"); + + assertEquals("[Alice, Bob]", array.toString()); + } + + @Test + public void testIterator() { + array.add("Alice"); + array.add("Bob"); + + String result = array.stream().collect(Collectors.joining(", ")); + assertEquals("Alice, Bob", result); + } + + @Test + public void testStreamAsString() { + array.add("Alice"); + array.add("Bob"); + + String result = array.stream().collect(Collectors.joining(", ")); + assertEquals("Alice, Bob", result); + } + + @Test + public void testStream() { + array.add("Alice"); + array.add("Bob"); + long count = array.stream().count(); + assertEquals(2, count); + } + + @Test + public void testAddToFullCapacity() { + DynamicArray<String> array = new DynamicArray<>(2); + array.add("Alice"); + array.add("Bob"); + array.add("Charlie"); // Triggers capacity expansion + + assertEquals(3, array.getSize()); + assertEquals("Charlie", array.get(2)); + } + + @Test + public void testPutWithNegativeIndex() { + assertThrows(IndexOutOfBoundsException.class, () -> array.put(-1, "Alice")); + } + + @Test + public void testGetWithNegativeIndex() { + assertThrows(IndexOutOfBoundsException.class, () -> array.get(-1)); + } + + @Test + public void testIteratorConcurrentModification() { + array.add("Alice"); + array.add("Bob"); + + Iterator<String> iterator = array.iterator(); + array.add("Charlie"); // Modify during iteration + + assertThrows(ConcurrentModificationException.class, iterator::next); + } + + @Test + public void testIteratorRemove() { + array.add("Alice"); + array.add("Bob"); + + Iterator<String> iterator = array.iterator(); + assertEquals("Alice", iterator.next()); + iterator.remove(); + assertEquals(1, array.getSize()); + assertEquals("Bob", array.get(0)); + } + + @Test + public void testRemoveBeyondCapacity() { + DynamicArray<String> array = new DynamicArray<>(2); + array.add("Alice"); + array.add("Bob"); + array.add("Charlie"); + + array.remove(1); + assertEquals(2, array.getSize()); + assertEquals("Alice", array.get(0)); + assertEquals("Charlie", array.get(1)); + } + + @Test + public void testCapacityDoubling() { + DynamicArray<String> array = new DynamicArray<>(1); + array.add("Alice"); + array.add("Bob"); + array.add("Charlie"); // Ensure capacity expansion is working + + assertEquals(3, array.getSize()); + assertEquals("Charlie", array.get(2)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/AStarTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/AStarTest.java new file mode 100644 index 000000000000..810773555a63 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/graphs/AStarTest.java @@ -0,0 +1,47 @@ +package com.thealgorithms.datastructures.graphs; + +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.ArrayList; +import java.util.Arrays; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class AStarTest { + + private AStar.Graph graph; + private int[] heuristic; + + @BeforeEach + public void setUp() { + // Initialize graph and heuristic values for testing + graph = new AStar.Graph(5); + ArrayList<Integer> graphData = new ArrayList<>(Arrays.asList(0, 1, 1, null, 0, 2, 2, null, 1, 3, 1, null, 2, 3, 1, null, 3, 4, 1, null)); + AStar.initializeGraph(graph, graphData); + + heuristic = new int[] {5, 4, 3, 2, 0}; // Heuristic values for each node + } + + @Test + public void testAStarFindsPath() { + AStar.PathAndDistance result = AStar.aStar(0, 4, graph, heuristic); + assertEquals(3, result.getDistance(), "Expected distance from 0 to 4 is 3"); + assertEquals(Arrays.asList(0, 1, 3, 4), result.getPath(), "Expected path from 0 to 4"); + } + + @Test + public void testAStarPathNotFound() { + AStar.PathAndDistance result = AStar.aStar(0, 5, graph, heuristic); // Node 5 does not exist + assertEquals(-1, result.getDistance(), "Expected distance when path not found is -1"); + assertNull(result.getPath(), "Expected path should be null when no path exists"); + } + + @Test + public void testAStarSameNode() { + AStar.PathAndDistance result = AStar.aStar(0, 0, graph, heuristic); + assertEquals(0, result.getDistance(), "Expected distance from 0 to 0 is 0"); + assertEquals(singletonList(0), result.getPath(), "Expected path should only contain the start node"); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/BipartiteGraphDFSTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/BipartiteGraphDFSTest.java new file mode 100644 index 000000000000..75fa6adc3014 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/graphs/BipartiteGraphDFSTest.java @@ -0,0 +1,73 @@ +package com.thealgorithms.datastructures.graphs; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import org.junit.jupiter.api.Test; + +public class BipartiteGraphDFSTest { + + // Helper method to create an adjacency list from edges + private ArrayList<ArrayList<Integer>> createAdjacencyList(int numVertices, int[][] edges) { + ArrayList<ArrayList<Integer>> adj = new ArrayList<>(); + for (int i = 0; i < numVertices; i++) { + adj.add(new ArrayList<>()); + } + for (int[] edge : edges) { + int vertexU = edge[0]; + int vertexV = edge[1]; + adj.get(vertexU).add(vertexV); + adj.get(vertexV).add(vertexU); + } + return adj; + } + + @Test + public void testBipartiteGraphEvenCycle() { + int numVertices = 4; + int[][] edges = {{0, 1}, {1, 2}, {2, 3}, {3, 0}}; // Even cycle + ArrayList<ArrayList<Integer>> adj = createAdjacencyList(numVertices, edges); + assertTrue(BipartiteGraphDFS.isBipartite(numVertices, adj), "Graph should be bipartite (even cycle)"); + } + + @Test + public void testBipartiteGraphOddCycle() { + int numVertices = 5; + int[][] edges = {{0, 1}, {1, 2}, {2, 0}, {1, 3}, {3, 4}}; // Odd cycle + ArrayList<ArrayList<Integer>> adj = createAdjacencyList(numVertices, edges); + assertFalse(BipartiteGraphDFS.isBipartite(numVertices, adj), "Graph should not be bipartite (odd cycle)"); + } + + @Test + public void testBipartiteGraphDisconnected() { + int numVertices = 6; + int[][] edges = {{0, 1}, {2, 3}, {4, 5}}; // Disconnected bipartite graphs + ArrayList<ArrayList<Integer>> adj = createAdjacencyList(numVertices, edges); + assertTrue(BipartiteGraphDFS.isBipartite(numVertices, adj), "Graph should be bipartite (disconnected)"); + } + + @Test + public void testBipartiteGraphSingleVertex() { + int numVertices = 1; + int[][] edges = {}; // Single vertex, no edges + ArrayList<ArrayList<Integer>> adj = createAdjacencyList(numVertices, edges); + assertTrue(BipartiteGraphDFS.isBipartite(numVertices, adj), "Graph should be bipartite (single vertex)"); + } + + @Test + public void testBipartiteGraphCompleteBipartite() { + int numVertices = 4; + int[][] edges = {{0, 2}, {0, 3}, {1, 2}, {1, 3}}; // K2,2 (Complete bipartite graph) + ArrayList<ArrayList<Integer>> adj = createAdjacencyList(numVertices, edges); + assertTrue(BipartiteGraphDFS.isBipartite(numVertices, adj), "Graph should be bipartite (complete bipartite)"); + } + + @Test + public void testBipartiteGraphNonBipartite() { + int numVertices = 3; + int[][] edges = {{0, 1}, {1, 2}, {2, 0}}; // Triangle (odd cycle) + ArrayList<ArrayList<Integer>> adj = createAdjacencyList(numVertices, edges); + assertFalse(BipartiteGraphDFS.isBipartite(numVertices, adj), "Graph should not be bipartite (triangle)"); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/BoruvkaAlgorithmTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/BoruvkaAlgorithmTest.java new file mode 100644 index 000000000000..f089169903d6 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/graphs/BoruvkaAlgorithmTest.java @@ -0,0 +1,193 @@ +package com.thealgorithms.datastructures.graphs; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.thealgorithms.datastructures.graphs.BoruvkaAlgorithm.Graph; +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class BoruvkaAlgorithmTest { + @Test + public void testBoruvkaMSTV9E14() { + List<BoruvkaAlgorithm.Edge> edges = new ArrayList<>(); + + edges.add(new BoruvkaAlgorithm.Edge(0, 1, 10)); + edges.add(new BoruvkaAlgorithm.Edge(0, 2, 12)); + edges.add(new BoruvkaAlgorithm.Edge(1, 2, 9)); + edges.add(new BoruvkaAlgorithm.Edge(1, 3, 8)); + edges.add(new BoruvkaAlgorithm.Edge(2, 4, 3)); + edges.add(new BoruvkaAlgorithm.Edge(2, 5, 1)); + edges.add(new BoruvkaAlgorithm.Edge(4, 5, 3)); + edges.add(new BoruvkaAlgorithm.Edge(4, 3, 7)); + edges.add(new BoruvkaAlgorithm.Edge(3, 6, 8)); + edges.add(new BoruvkaAlgorithm.Edge(3, 7, 5)); + edges.add(new BoruvkaAlgorithm.Edge(5, 7, 6)); + edges.add(new BoruvkaAlgorithm.Edge(6, 7, 9)); + edges.add(new BoruvkaAlgorithm.Edge(6, 8, 2)); + edges.add(new BoruvkaAlgorithm.Edge(7, 8, 11)); + + final var graph = new Graph(9, edges); + /* + * Adjacency matrix + * 0 1 2 3 4 5 6 7 8 + * 0 0 10 12 0 0 0 0 0 0 + * 1 10 0 9 8 0 0 0 0 0 + * 2 12 9 0 0 3 1 0 0 0 + * 3 0 8 0 0 7 0 8 5 0 + * 4 0 0 3 7 0 3 0 0 0 + * 5 0 0 1 0 3 0 0 6 0 + * 6 0 0 0 8 0 0 0 9 2 + * 7 0 0 0 5 0 6 9 0 11 + * 8 0 0 0 0 0 0 2 11 0 + */ + final var result = BoruvkaAlgorithm.boruvkaMST(graph); + assertEquals(8, result.size()); + assertEquals(43, computeTotalWeight(result)); + } + + @Test + void testBoruvkaMSTV2E1() { + List<BoruvkaAlgorithm.Edge> edges = new ArrayList<>(); + + edges.add(new BoruvkaAlgorithm.Edge(0, 1, 10)); + + final var graph = new Graph(2, edges); + + /* + * Adjacency matrix + * 0 1 + * 0 0 10 + * 1 10 0 + */ + final var result = BoruvkaAlgorithm.boruvkaMST(graph); + assertEquals(1, result.size()); + assertEquals(10, computeTotalWeight(result)); + } + + @Test + void testCompleteGraphK4() { + List<BoruvkaAlgorithm.Edge> edges = new ArrayList<>(); + edges.add(new BoruvkaAlgorithm.Edge(0, 1, 7)); + edges.add(new BoruvkaAlgorithm.Edge(0, 2, 2)); + edges.add(new BoruvkaAlgorithm.Edge(0, 3, 5)); + edges.add(new BoruvkaAlgorithm.Edge(1, 2, 3)); + edges.add(new BoruvkaAlgorithm.Edge(1, 3, 4)); + edges.add(new BoruvkaAlgorithm.Edge(2, 3, 1)); + + final var graph = new Graph(4, edges); + + /* + * Adjacency matrix + * 0 1 2 3 + * 0 0 7 2 5 + * 1 7 0 3 4 + * 2 2 3 0 1 + * 3 5 4 1 0 + */ + final var result = BoruvkaAlgorithm.boruvkaMST(graph); + assertEquals(3, result.size()); + assertEquals(6, computeTotalWeight(result)); + } + + @Test + void testNegativeVertices() { + Exception exception1 = assertThrows(IllegalArgumentException.class, () -> new Graph(-1, null)); + String expectedMessage = "Number of vertices must be positive"; + String actualMessage = exception1.getMessage(); + + assertTrue(actualMessage.contains(expectedMessage)); + } + + @Test + void testEdgesNull() { + Exception exception = assertThrows(IllegalArgumentException.class, () -> new Graph(0, null)); + String expectedMessage = "Edges list must not be null or empty"; + String actualMessage = exception.getMessage(); + + assertTrue(actualMessage.contains(expectedMessage)); + } + + @Test + void testEdgesEmpty() { + Exception exception = assertThrows(IllegalArgumentException.class, () -> new Graph(0, new ArrayList<>())); + String expectedMessage = "Edges list must not be null or empty"; + String actualMessage = exception.getMessage(); + + assertTrue(actualMessage.contains(expectedMessage)); + } + + @Test + void testEdgesRange() { + // Valid input + List<BoruvkaAlgorithm.Edge> validEdges = new ArrayList<>(); + validEdges.add(new BoruvkaAlgorithm.Edge(0, 1, 2)); + validEdges.add(new BoruvkaAlgorithm.Edge(1, 2, 3)); + final var validGraph = new BoruvkaAlgorithm.Graph(3, validEdges); + assertEquals(validEdges, validGraph.edges); + + // Edge source out of range + Exception exception1 = assertThrows(IllegalArgumentException.class, () -> { + List<BoruvkaAlgorithm.Edge> invalidEdges = new ArrayList<>(); + invalidEdges.add(new BoruvkaAlgorithm.Edge(-1, 1, 2)); + final var invalidGraph = new BoruvkaAlgorithm.Graph(1, invalidEdges); + assertEquals(invalidEdges, invalidGraph.edges); + }); + String expectedMessage1 = "Edge vertex out of range"; + String actualMessage1 = exception1.getMessage(); + + assertTrue(actualMessage1.contains(expectedMessage1)); + + // Edge source out of range + Exception exception2 = assertThrows(IllegalArgumentException.class, () -> { + List<BoruvkaAlgorithm.Edge> invalidEdges = new ArrayList<>(); + invalidEdges.add(new BoruvkaAlgorithm.Edge(1, 0, 2)); + final var invalidGraph = new BoruvkaAlgorithm.Graph(1, invalidEdges); + assertEquals(invalidEdges, invalidGraph.edges); + }); + String expectedMessage2 = "Edge vertex out of range"; + String actualMessage2 = exception2.getMessage(); + + assertTrue(actualMessage2.contains(expectedMessage2)); + + // Edge destination out of range + Exception exception3 = assertThrows(IllegalArgumentException.class, () -> { + List<BoruvkaAlgorithm.Edge> invalidEdges = new ArrayList<>(); + invalidEdges.add(new BoruvkaAlgorithm.Edge(0, -1, 2)); + final var invalidGraph = new BoruvkaAlgorithm.Graph(1, invalidEdges); + assertEquals(invalidEdges, invalidGraph.edges); + }); + String expectedMessage3 = "Edge vertex out of range"; + String actualMessage3 = exception3.getMessage(); + + assertTrue(actualMessage3.contains(expectedMessage3)); + + // Edge destination out of range + Exception exception4 = assertThrows(IllegalArgumentException.class, () -> { + List<BoruvkaAlgorithm.Edge> invalidEdges = new ArrayList<>(); + invalidEdges.add(new BoruvkaAlgorithm.Edge(0, 1, 2)); + final var invalidGraph = new BoruvkaAlgorithm.Graph(1, invalidEdges); + assertEquals(invalidEdges, invalidGraph.edges); + }); + String expectedMessage4 = "Edge vertex out of range"; + String actualMessage4 = exception4.getMessage(); + + assertTrue(actualMessage4.contains(expectedMessage4)); + } + + /** + * Computes the total weight of the Minimum Spanning Tree + * + * @param result list of edges in the Minimum Spanning Tree + * @return the total weight of the Minimum Spanning Tree + */ + int computeTotalWeight(final Iterable<BoruvkaAlgorithm.Edge> result) { + int totalWeight = 0; + for (final var edge : result) { + totalWeight += edge.weight; + } + return totalWeight; + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/DijkstraAlgorithmTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/DijkstraAlgorithmTest.java new file mode 100644 index 000000000000..c5df9acdf33b --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/graphs/DijkstraAlgorithmTest.java @@ -0,0 +1,64 @@ +package com.thealgorithms.datastructures.graphs; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class DijkstraAlgorithmTest { + + private DijkstraAlgorithm dijkstraAlgorithm; + private int[][] graph; + + @BeforeEach + void setUp() { + graph = new int[][] { + {0, 4, 0, 0, 0, 0, 0, 8, 0}, + {4, 0, 8, 0, 0, 0, 0, 11, 0}, + {0, 8, 0, 7, 0, 4, 0, 0, 2}, + {0, 0, 7, 0, 9, 14, 0, 0, 0}, + {0, 0, 0, 9, 0, 10, 0, 0, 0}, + {0, 0, 4, 14, 10, 0, 2, 0, 0}, + {0, 0, 0, 0, 0, 2, 0, 1, 6}, + {8, 11, 0, 0, 0, 0, 1, 0, 7}, + {0, 0, 2, 0, 0, 0, 6, 7, 0}, + }; + + dijkstraAlgorithm = new DijkstraAlgorithm(graph.length); + } + + @Test + void testRunAlgorithm() { + int[] expectedDistances = {0, 4, 12, 19, 21, 11, 9, 8, 14}; + assertArrayEquals(expectedDistances, dijkstraAlgorithm.run(graph, 0)); + } + + @Test + void testGraphWithDisconnectedNodes() { + int[][] disconnectedGraph = { + {0, 3, 0, 0}, {3, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 0} // Node 3 is disconnected + }; + + DijkstraAlgorithm dijkstraDisconnected = new DijkstraAlgorithm(disconnectedGraph.length); + + // Testing from vertex 0 + int[] expectedDistances = {0, 3, 4, Integer.MAX_VALUE}; // Node 3 is unreachable + assertArrayEquals(expectedDistances, dijkstraDisconnected.run(disconnectedGraph, 0)); + } + + @Test + void testSingleVertexGraph() { + int[][] singleVertexGraph = {{0}}; + DijkstraAlgorithm dijkstraSingleVertex = new DijkstraAlgorithm(1); + + int[] expectedDistances = {0}; // The only vertex's distance to itself is 0 + assertArrayEquals(expectedDistances, dijkstraSingleVertex.run(singleVertexGraph, 0)); + } + + @Test + void testInvalidSourceVertex() { + assertThrows(IllegalArgumentException.class, () -> dijkstraAlgorithm.run(graph, -1)); + assertThrows(IllegalArgumentException.class, () -> dijkstraAlgorithm.run(graph, graph.length)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/DijkstraOptimizedAlgorithmTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/DijkstraOptimizedAlgorithmTest.java new file mode 100644 index 000000000000..bf4e2828e069 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/graphs/DijkstraOptimizedAlgorithmTest.java @@ -0,0 +1,64 @@ +package com.thealgorithms.datastructures.graphs; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class DijkstraOptimizedAlgorithmTest { + + private DijkstraOptimizedAlgorithm dijkstraOptimizedAlgorithm; + private int[][] graph; + + @BeforeEach + void setUp() { + graph = new int[][] { + {0, 4, 0, 0, 0, 0, 0, 8, 0}, + {4, 0, 8, 0, 0, 0, 0, 11, 0}, + {0, 8, 0, 7, 0, 4, 0, 0, 2}, + {0, 0, 7, 0, 9, 14, 0, 0, 0}, + {0, 0, 0, 9, 0, 10, 0, 0, 0}, + {0, 0, 4, 14, 10, 0, 2, 0, 0}, + {0, 0, 0, 0, 0, 2, 0, 1, 6}, + {8, 11, 0, 0, 0, 0, 1, 0, 7}, + {0, 0, 2, 0, 0, 0, 6, 7, 0}, + }; + + dijkstraOptimizedAlgorithm = new DijkstraOptimizedAlgorithm(graph.length); + } + + @Test + void testRunAlgorithm() { + int[] expectedDistances = {0, 4, 12, 19, 21, 11, 9, 8, 14}; + assertArrayEquals(expectedDistances, dijkstraOptimizedAlgorithm.run(graph, 0)); + } + + @Test + void testGraphWithDisconnectedNodes() { + int[][] disconnectedGraph = { + {0, 3, 0, 0}, {3, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 0} // Node 3 is disconnected + }; + + DijkstraOptimizedAlgorithm dijkstraDisconnected = new DijkstraOptimizedAlgorithm(disconnectedGraph.length); + + // Testing from vertex 0 + int[] expectedDistances = {0, 3, 4, Integer.MAX_VALUE}; // Node 3 is unreachable + assertArrayEquals(expectedDistances, dijkstraDisconnected.run(disconnectedGraph, 0)); + } + + @Test + void testSingleVertexGraph() { + int[][] singleVertexGraph = {{0}}; + DijkstraOptimizedAlgorithm dijkstraSingleVertex = new DijkstraOptimizedAlgorithm(1); + + int[] expectedDistances = {0}; // The only vertex's distance to itself is 0 + assertArrayEquals(expectedDistances, dijkstraSingleVertex.run(singleVertexGraph, 0)); + } + + @Test + void testInvalidSourceVertex() { + assertThrows(IllegalArgumentException.class, () -> dijkstraOptimizedAlgorithm.run(graph, -1)); + assertThrows(IllegalArgumentException.class, () -> dijkstraOptimizedAlgorithm.run(graph, graph.length)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/EdmondsBlossomAlgorithmTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/EdmondsBlossomAlgorithmTest.java new file mode 100644 index 000000000000..aa8e6beeb3db --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/graphs/EdmondsBlossomAlgorithmTest.java @@ -0,0 +1,120 @@ +package com.thealgorithms.datastructures.graphs; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the EdmondsBlossomAlgorithm class. + * + * These tests ensure that the Edmonds' Blossom Algorithm implementation + * works as expected for various graph structures, returning the correct + * maximum matching. + */ +public class EdmondsBlossomAlgorithmTest { + + /** + * Helper method to convert a list of matching pairs into a sorted 2D array. + * Sorting ensures consistent ordering of pairs and vertices for easier comparison in tests. + * + * @param matching List of matched pairs returned by the algorithm. + * @return A sorted 2D array of matching pairs. + */ + private int[][] convertMatchingToArray(Collection<int[]> matching) { + // Convert the list of pairs into an array + int[][] result = matching.toArray(new int[0][]); + + // Sort each individual pair for consistency + for (int[] pair : result) { + Arrays.sort(pair); + } + + // Sort the array of pairs to ensure consistent order + Arrays.sort(result, (a, b) -> Integer.compare(a[0], b[0])); + return result; + } + + /** + * Test Case 1: A triangle graph where vertices 0, 1, and 2 form a cycle. + * The expected maximum matching is a single pair (0, 1) or any equivalent pair from the cycle. + */ + @Test + public void testCase1() { + List<int[]> edges = Arrays.asList(new int[] {0, 1}, new int[] {1, 2}, new int[] {2, 0}); + List<int[]> matching = EdmondsBlossomAlgorithm.maximumMatching(edges, 3); + + int[][] expected = new int[][] {{0, 1}}; + assertArrayEquals(expected, convertMatchingToArray(matching)); + } + + /** + * Test Case 2: A disconnected graph where vertices 0, 1, 2 form one component, + * and vertices 3, 4 form another. The expected maximum matching is two pairs: + * (0, 1) and (3, 4). + */ + @Test + public void testCase2() { + List<int[]> edges = Arrays.asList(new int[] {0, 1}, new int[] {1, 2}, new int[] {3, 4}); + List<int[]> matching = EdmondsBlossomAlgorithm.maximumMatching(edges, 5); + + int[][] expected = new int[][] {{0, 1}, {3, 4}}; + assertArrayEquals(expected, convertMatchingToArray(matching)); + } + + /** + * Test Case 3: A cycle graph involving vertices 0, 1, 2, 3 forming a cycle, + * with an additional edge (4, 5) outside the cycle. + * The expected maximum matching is (0, 1) and (4, 5). + */ + @Test + public void testCase3() { + List<int[]> edges = Arrays.asList(new int[] {0, 1}, new int[] {1, 2}, new int[] {2, 3}, new int[] {3, 0}, new int[] {4, 5}); + List<int[]> matching = EdmondsBlossomAlgorithm.maximumMatching(edges, 6); + + // Updated expected output to include the maximum matching pairs + int[][] expected = new int[][] {{0, 1}, {2, 3}, {4, 5}}; + assertArrayEquals(expected, convertMatchingToArray(matching)); + } + + /** + * Test Case 4: A graph with no edges. + * Since there are no edges, the expected matching is an empty set. + */ + @Test + public void testCaseNoMatching() { + List<int[]> edges = Collections.emptyList(); // No edges + List<int[]> matching = EdmondsBlossomAlgorithm.maximumMatching(edges, 3); + + int[][] expected = new int[][] {}; // No pairs expected + assertArrayEquals(expected, convertMatchingToArray(matching)); + } + + /** + * Test Case 5: A more complex graph with multiple cycles and extra edges. + * This tests the algorithm's ability to handle larger, more intricate graphs. + * The expected matching is {{0, 1}, {2, 5}, {3, 4}}. + */ + @Test + public void testCaseLargeGraph() { + List<int[]> edges = Arrays.asList(new int[] {0, 1}, new int[] {1, 2}, new int[] {2, 3}, new int[] {3, 4}, new int[] {4, 5}, new int[] {5, 0}, new int[] {1, 4}, new int[] {2, 5}); + List<int[]> matching = EdmondsBlossomAlgorithm.maximumMatching(edges, 6); + + // Check if the size of the matching is correct (i.e., 3 pairs) + assertEquals(3, matching.size()); + + // Check that the result contains valid pairs (any order is fine) + // Valid maximum matchings could be {{0, 1}, {2, 5}, {3, 4}} or {{0, 1}, {2, 3}, {4, 5}}, etc. + int[][] possibleMatching1 = new int[][] {{0, 1}, {2, 5}, {3, 4}}; + int[][] possibleMatching2 = new int[][] {{0, 1}, {2, 3}, {4, 5}}; + int[][] result = convertMatchingToArray(matching); + + // Assert that the result is one of the valid maximum matchings + assertTrue(Arrays.deepEquals(result, possibleMatching1) || Arrays.deepEquals(result, possibleMatching2)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/FloydWarshallTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/FloydWarshallTest.java new file mode 100644 index 000000000000..7d6a2b239f4b --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/graphs/FloydWarshallTest.java @@ -0,0 +1,33 @@ +package com.thealgorithms.datastructures.graphs; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +class FloydWarshallTest { + + @Test + void testSmallGraph() { + int[][] adjacencyMatrix = {{0, 0, 0, 0}, // Ignored row (0 index) + {0, 0, 3, FloydWarshall.INFINITY}, {0, FloydWarshall.INFINITY, 0, 1}, {0, FloydWarshall.INFINITY, FloydWarshall.INFINITY, 0}}; + + FloydWarshall fw = new FloydWarshall(3); + fw.floydwarshall(adjacencyMatrix); + + int[][] expectedDistanceMatrix = {{0, 0, 0, 0}, {0, 0, 3, 4}, {0, FloydWarshall.INFINITY, 0, 1}, {0, FloydWarshall.INFINITY, FloydWarshall.INFINITY, 0}}; + + assertArrayEquals(expectedDistanceMatrix, fw.getDistanceMatrix()); + } + + @Test + void testLargerGraph() { + int[][] adjacencyMatrix = {{0, 0, 0, 0, 0}, {0, 0, 1, FloydWarshall.INFINITY, 2}, {0, FloydWarshall.INFINITY, 0, 4, FloydWarshall.INFINITY}, {0, FloydWarshall.INFINITY, FloydWarshall.INFINITY, 0, 3}, {0, FloydWarshall.INFINITY, FloydWarshall.INFINITY, FloydWarshall.INFINITY, 0}}; + + FloydWarshall fw = new FloydWarshall(4); + fw.floydwarshall(adjacencyMatrix); + + int[][] expectedDistanceMatrix = {{0, 0, 0, 0, 0}, {0, 0, 1, 5, 2}, {0, FloydWarshall.INFINITY, 0, 4, 7}, {0, FloydWarshall.INFINITY, FloydWarshall.INFINITY, 0, 3}, {0, FloydWarshall.INFINITY, FloydWarshall.INFINITY, FloydWarshall.INFINITY, 0}}; + + assertArrayEquals(expectedDistanceMatrix, fw.getDistanceMatrix()); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/FordFulkersonTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/FordFulkersonTest.java new file mode 100644 index 000000000000..29e373e361ad --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/graphs/FordFulkersonTest.java @@ -0,0 +1,216 @@ +package com.thealgorithms.datastructures.graphs; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class FordFulkersonTest { + @Test + public void testMaxFlow() { + int vertexCount = 6; + int[][] capacity = new int[vertexCount][vertexCount]; + int[][] flow = new int[vertexCount][vertexCount]; + + // Setting up the capacity graph + capacity[0][1] = 12; + capacity[0][3] = 13; + capacity[1][2] = 10; + capacity[2][3] = 13; + capacity[2][4] = 3; + capacity[2][5] = 15; + capacity[3][2] = 7; + capacity[3][4] = 15; + capacity[4][5] = 17; + + int maxFlow = FordFulkerson.networkFlow(vertexCount, capacity, flow, 0, 5); + assertEquals(23, maxFlow); + } + + @Test + public void testNoFlow() { + int vertexCount = 6; + int[][] capacity = new int[vertexCount][vertexCount]; + int[][] flow = new int[vertexCount][vertexCount]; + + // No connections between source and sink + capacity[0][1] = 10; + capacity[2][3] = 10; + + int maxFlow = FordFulkerson.networkFlow(vertexCount, capacity, flow, 1, 4); + assertEquals(0, maxFlow); + } + + @Test + public void testSinglePath() { + int vertexCount = 6; + int[][] capacity = new int[vertexCount][vertexCount]; + int[][] flow = new int[vertexCount][vertexCount]; + + // Setting up a single path from source to sink + capacity[0][1] = 5; + capacity[1][2] = 5; + capacity[2][3] = 5; + capacity[3][4] = 5; + capacity[4][5] = 5; + + int maxFlow = FordFulkerson.networkFlow(vertexCount, capacity, flow, 0, 5); + assertEquals(5, maxFlow); + } + + @Test + public void testParallelPaths() { + int vertexCount = 4; + int[][] capacity = new int[vertexCount][vertexCount]; + int[][] flow = new int[vertexCount][vertexCount]; + + // Setting up parallel paths from source to sink + capacity[0][1] = 10; + capacity[0][2] = 10; + capacity[1][3] = 10; + capacity[2][3] = 10; + + int maxFlow = FordFulkerson.networkFlow(vertexCount, capacity, flow, 0, 3); + assertEquals(20, maxFlow); + } + + @Test + public void testComplexNetwork() { + int vertexCount = 5; + int[][] capacity = new int[vertexCount][vertexCount]; + int[][] flow = new int[vertexCount][vertexCount]; + + // Complex network + capacity[0][1] = 10; + capacity[0][2] = 10; + capacity[1][3] = 4; + capacity[1][4] = 8; + capacity[2][4] = 9; + capacity[3][2] = 6; + capacity[3][4] = 10; + + int maxFlow = FordFulkerson.networkFlow(vertexCount, capacity, flow, 0, 4); + assertEquals(19, maxFlow); + } + + @Test + public void testLargeNetwork() { + int vertexCount = 8; + int[][] capacity = new int[vertexCount][vertexCount]; + int[][] flow = new int[vertexCount][vertexCount]; + + // Setting up a large network + capacity[0][1] = 10; + capacity[0][2] = 5; + capacity[1][3] = 15; + capacity[2][3] = 10; + capacity[1][4] = 10; + capacity[3][5] = 10; + capacity[4][5] = 5; + capacity[4][6] = 10; + capacity[5][7] = 10; + capacity[6][7] = 15; + + int maxFlow = FordFulkerson.networkFlow(vertexCount, capacity, flow, 0, 7); + assertEquals(15, maxFlow); // Maximum flow should be 15 + } + + @Test + public void testMultipleSourcesAndSinks() { + int vertexCount = 7; + int[][] capacity = new int[vertexCount][vertexCount]; + int[][] flow = new int[vertexCount][vertexCount]; + + // Creating multiple sources and sinks scenario + capacity[0][1] = 10; // Source 1 + capacity[0][2] = 5; + capacity[1][3] = 15; + capacity[2][3] = 10; + capacity[3][4] = 10; // Sink 1 + capacity[3][5] = 5; + capacity[3][6] = 10; // Sink 2 + capacity[5][6] = 10; + + int maxFlow = FordFulkerson.networkFlow(vertexCount, capacity, flow, 0, 4); + assertEquals(10, maxFlow); // Maximum flow should be 10 + } + + @Test + public void testDisconnectedGraph() { + int vertexCount = 6; + int[][] capacity = new int[vertexCount][vertexCount]; + int[][] flow = new int[vertexCount][vertexCount]; + + // No connection between source and sink + capacity[0][1] = 10; // Only one edge not connected to the sink + capacity[1][2] = 10; + capacity[3][4] = 10; + + int maxFlow = FordFulkerson.networkFlow(vertexCount, capacity, flow, 0, 5); + assertEquals(0, maxFlow); // No flow should be possible + } + + @Test + public void testZeroCapacityEdge() { + int vertexCount = 4; + int[][] capacity = new int[vertexCount][vertexCount]; + int[][] flow = new int[vertexCount][vertexCount]; + + // Including a zero capacity edge + capacity[0][1] = 10; + capacity[0][2] = 0; // Zero capacity + capacity[1][3] = 5; + capacity[2][3] = 10; + + int maxFlow = FordFulkerson.networkFlow(vertexCount, capacity, flow, 0, 3); + assertEquals(5, maxFlow); // Flow only possible through 0 -> 1 -> 3 + } + + @Test + public void testAllEdgesZeroCapacity() { + int vertexCount = 5; + int[][] capacity = new int[vertexCount][vertexCount]; + int[][] flow = new int[vertexCount][vertexCount]; + + // All edges with zero capacity + capacity[0][1] = 0; + capacity[1][2] = 0; + capacity[2][3] = 0; + capacity[3][4] = 0; + + int maxFlow = FordFulkerson.networkFlow(vertexCount, capacity, flow, 0, 4); + assertEquals(0, maxFlow); // No flow should be possible + } + + @Test + public void testCycleGraph() { + int vertexCount = 4; + int[][] capacity = new int[vertexCount][vertexCount]; + int[][] flow = new int[vertexCount][vertexCount]; + + // Setting up a cycle + capacity[0][1] = 10; + capacity[1][2] = 5; + capacity[2][0] = 5; // This creates a cycle + capacity[1][3] = 15; + capacity[2][3] = 10; + + int maxFlow = FordFulkerson.networkFlow(vertexCount, capacity, flow, 0, 3); + assertEquals(10, maxFlow); // Maximum flow should be 10 + } + + @Test + public void testFlowWithExcessCapacity() { + int vertexCount = 5; + int[][] capacity = new int[vertexCount][vertexCount]; + int[][] flow = new int[vertexCount][vertexCount]; + + // Extra capacity in the flow + capacity[0][1] = 20; + capacity[1][2] = 10; + capacity[2][3] = 15; + capacity[1][3] = 5; + + int maxFlow = FordFulkerson.networkFlow(vertexCount, capacity, flow, 0, 3); + assertEquals(15, maxFlow); // Maximum flow should be 15 (20 from 0->1 and 10->2, limited by 15->3) + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/HamiltonianCycleTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/HamiltonianCycleTest.java new file mode 100644 index 000000000000..4529606d7797 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/graphs/HamiltonianCycleTest.java @@ -0,0 +1,98 @@ +package com.thealgorithms.datastructures.graphs; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +class HamiltonianCycleTest { + + private final HamiltonianCycle hamiltonianCycle = new HamiltonianCycle(); + + @Test + void testFindHamiltonianCycleShouldReturnHamiltonianCycle() { + int[] expectedArray = {0, 1, 2, 4, 3, 0}; + int[][] inputArray = { + {0, 1, 0, 1, 0}, + {1, 0, 1, 1, 1}, + {0, 1, 0, 0, 1}, + {1, 1, 0, 0, 1}, + {0, 1, 1, 1, 0}, + }; + + assertArrayEquals(expectedArray, hamiltonianCycle.findHamiltonianCycle(inputArray)); + } + + @Test + void testFindHamiltonianCycleShouldReturnInfinityArray() { + int[] expectedArray = {-1, -1, -1, -1, -1, -1}; + + int[][] inputArray = { + {0, 1, 0, 1, 0}, + {1, 0, 1, 1, 1}, + {0, 1, 0, 0, 1}, + {1, 1, 0, 0, 0}, + {0, 1, 1, 0, 0}, + }; + + assertArrayEquals(expectedArray, hamiltonianCycle.findHamiltonianCycle(inputArray)); + } + + @Test + void testSingleVertexGraph() { + int[] expectedArray = {0, 0}; + int[][] inputArray = {{0}}; + + assertArrayEquals(expectedArray, hamiltonianCycle.findHamiltonianCycle(inputArray)); + } + + @Test + void testDisconnectedGraphShouldReturnInfinityArray() { + int[] expectedArray = {-1, -1, -1, -1, -1}; + int[][] inputArray = {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}; + + assertArrayEquals(expectedArray, hamiltonianCycle.findHamiltonianCycle(inputArray)); + } + + @Test + void testCompleteGraphShouldReturnHamiltonianCycle() { + int[] expectedArray = {0, 1, 2, 3, 4, 0}; + int[][] inputArray = { + {0, 1, 1, 1, 1}, + {1, 0, 1, 1, 1}, + {1, 1, 0, 1, 1}, + {1, 1, 1, 0, 1}, + {1, 1, 1, 1, 0}, + }; + + assertArrayEquals(expectedArray, hamiltonianCycle.findHamiltonianCycle(inputArray)); + } + + @Test + void testGraphWithNoEdgesShouldReturnInfinityArray() { + int[] expectedArray = {-1, -1, -1, -1, -1, -1}; + + int[][] inputArray = { + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + }; + + assertArrayEquals(expectedArray, hamiltonianCycle.findHamiltonianCycle(inputArray)); + } + + @Test + void testLargeGraphWithHamiltonianCycle() { + int[] expectedArray = {0, 1, 2, 3, 4, 0}; + int[][] inputArray = { + {0, 1, 0, 1, 1}, + {1, 0, 1, 1, 0}, + {0, 1, 0, 1, 1}, + {1, 1, 1, 0, 1}, + {1, 0, 1, 1, 0}, + }; + + assertArrayEquals(expectedArray, hamiltonianCycle.findHamiltonianCycle(inputArray)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/JohnsonsAlgorithmTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/JohnsonsAlgorithmTest.java new file mode 100644 index 000000000000..7ea2449202e0 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/graphs/JohnsonsAlgorithmTest.java @@ -0,0 +1,142 @@ +package com.thealgorithms.datastructures.graphs; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link JohnsonsAlgorithm} class. This class + * contains test cases to verify the correct implementation of + * various methods used in Johnson's Algorithm such as shortest path + * calculations, graph reweighting, and more. + */ +class JohnsonsAlgorithmTest { + + // Constant representing infinity + private static final double INF = Double.POSITIVE_INFINITY; + + /** + * Tests the Johnson's Algorithm with a simple graph without negative edges. + * Verifies that the algorithm returns the correct shortest path distances. + */ + @Test + void testSimpleGraph() { + double[][] graph = {{0, 4, INF, INF}, {INF, 0, 1, INF}, {INF, INF, 0, 2}, {INF, INF, INF, 0}}; + double[][] result = JohnsonsAlgorithm.johnsonAlgorithm(graph); + double[][] expected = {{0, 4, 5, 7}, {INF, 0, 1, 3}, {INF, INF, 0, 2}, {INF, INF, INF, 0}}; + assertArrayEquals(expected, result); + } + + /** + * Tests Johnson's Algorithm on a graph with negative edges but no negative weight cycles. + */ + @Test + void testGraphWithNegativeEdges() { + double[][] graph = {{0, -1, 4}, {INF, 0, 3}, {INF, INF, 0}}; + double[][] result = JohnsonsAlgorithm.johnsonAlgorithm(graph); + double[][] expected = {{0, INF, 4}, {INF, 0, 3}, {INF, INF, 0}}; + assertArrayEquals(expected, result); + } + + /** + * Tests Johnson's Algorithm on a graph with a negative weight cycle. + */ + @Test + void testNegativeWeightCycle() { + double[][] graph = {{0, 1, INF}, {INF, 0, -1}, {-1, INF, 0}}; + assertThrows(IllegalArgumentException.class, () -> JohnsonsAlgorithm.johnsonAlgorithm(graph)); + } + + /** + * Tests Dijkstra's algorithm on a small graph as part of Johnson's Algorithm. + */ + @Test + void testDijkstra() { + double[][] graph = {{0, 1, 2}, {INF, 0, 3}, {INF, INF, 0}}; + double[] modifiedWeights = {0, 0, 0}; + double[] result = JohnsonsAlgorithm.dijkstra(graph, 0, modifiedWeights); + double[] expected = {0, 1, 2}; + assertArrayEquals(expected, result); + } + + /** + * Tests the conversion of an adjacency matrix to an edge list. + */ + @Test + void testEdgeListConversion() { + double[][] graph = {{0, 5, INF}, {INF, 0, 2}, {INF, INF, 0}}; + double[][] edges = JohnsonsAlgorithm.convertToEdgeList(graph); + double[][] expected = {{0, 1, 5}, {1, 2, 2}}; + assertArrayEquals(expected, edges); + } + + /** + * Tests the reweighting of a graph. + */ + @Test + void testReweightGraph() { + double[][] graph = {{0, 2, 9}, {INF, 0, 1}, {INF, INF, 0}}; + double[] modifiedWeights = {1, 2, 3}; + double[][] reweightedGraph = JohnsonsAlgorithm.reweightGraph(graph, modifiedWeights); + double[][] expected = {{0, 1, 7}, {INF, 0, 0}, {INF, INF, 0}}; + assertArrayEquals(expected, reweightedGraph); + } + + /** + * Tests the minDistance method used in Dijkstra's algorithm. + */ + @Test + void testMinDistance() { + double[] dist = {INF, 3, 1, INF}; + boolean[] visited = {false, false, false, false}; + int minIndex = JohnsonsAlgorithm.minDistance(dist, visited); + assertEquals(2, minIndex); + } + + /** + * Tests Johnson's Algorithm on a graph where all vertices are disconnected. + */ + @Test + void testDisconnectedGraph() { + double[][] graph = {{0, INF, INF}, {INF, 0, INF}, {INF, INF, 0}}; + double[][] result = JohnsonsAlgorithm.johnsonAlgorithm(graph); + double[][] expected = {{0, INF, INF}, {INF, 0, INF}, {INF, INF, 0}}; + assertArrayEquals(expected, result); + } + + /** + * Tests Johnson's Algorithm on a fully connected graph. + */ + @Test + void testFullyConnectedGraph() { + double[][] graph = {{0, 1, 2}, {1, 0, 1}, {2, 1, 0}}; + double[][] result = JohnsonsAlgorithm.johnsonAlgorithm(graph); + double[][] expected = {{0, 1, 2}, {1, 0, 1}, {2, 1, 0}}; + assertArrayEquals(expected, result); + } + + /** + * Tests Dijkstra's algorithm on a graph with multiple shortest paths. + */ + @Test + void testDijkstraMultipleShortestPaths() { + double[][] graph = {{0, 1, 2, INF}, {INF, 0, INF, 1}, {INF, INF, 0, 1}, {INF, INF, INF, 0}}; + double[] modifiedWeights = {0, 0, 0, 0}; + double[] result = JohnsonsAlgorithm.dijkstra(graph, 0, modifiedWeights); + double[] expected = {0, 1, 2, 2}; + assertArrayEquals(expected, result); + } + + /** + * Tests Johnson's Algorithm with a graph where all edge weights are zero. + */ + @Test + void testGraphWithZeroWeights() { + double[][] graph = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; + double[][] result = JohnsonsAlgorithm.johnsonAlgorithm(graph); + double[][] expected = {{0, INF, INF}, {INF, 0, INF}, {INF, INF, 0}}; + assertArrayEquals(expected, result); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithmTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithmTest.java new file mode 100644 index 000000000000..8d096a4b4459 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/graphs/KahnsAlgorithmTest.java @@ -0,0 +1,77 @@ +package com.thealgorithms.datastructures.graphs; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.ArrayList; +import org.junit.jupiter.api.Test; + +class KahnsAlgorithmTest { + + @Test + void testBasicGraph() { + // Test case with a basic directed acyclic graph (DAG) + AdjacencyList<String> graph = new AdjacencyList<>(); + graph.addEdge("a", "b"); + graph.addEdge("c", "a"); + graph.addEdge("a", "d"); + graph.addEdge("b", "d"); + + TopologicalSort<String> topSort = new TopologicalSort<>(graph); + ArrayList<String> result = topSort.topSortOrder(); + + String[] expectedOrder = {"c", "a", "b", "d"}; + assertArrayEquals(expectedOrder, result.toArray()); + } + + @Test + void testGraphWithMultipleSources() { + // Test case where graph has multiple independent sources + AdjacencyList<String> graph = new AdjacencyList<>(); + graph.addEdge("a", "c"); + graph.addEdge("b", "c"); + + TopologicalSort<String> topSort = new TopologicalSort<>(graph); + ArrayList<String> result = topSort.topSortOrder(); + + String[] expectedOrder = {"a", "b", "c"}; + assertArrayEquals(expectedOrder, result.toArray()); + } + + @Test + void testDisconnectedGraph() { + // Test case for disconnected graph + AdjacencyList<String> graph = new AdjacencyList<>(); + graph.addEdge("a", "b"); + graph.addEdge("c", "d"); + + TopologicalSort<String> topSort = new TopologicalSort<>(graph); + ArrayList<String> result = topSort.topSortOrder(); + + String[] expectedOrder = {"a", "c", "b", "d"}; + assertArrayEquals(expectedOrder, result.toArray()); + } + + @Test + void testGraphWithCycle() { + // Test case for a graph with a cycle - topological sorting is not possible + AdjacencyList<String> graph = new AdjacencyList<>(); + graph.addEdge("a", "b"); + graph.addEdge("b", "c"); + graph.addEdge("c", "a"); + + TopologicalSort<String> topSort = new TopologicalSort<>(graph); + + assertThrows(IllegalStateException.class, () -> topSort.topSortOrder()); + } + + @Test + void testSingleNodeGraph() { + AdjacencyList<String> graph = new AdjacencyList<>(); + graph.addEdge("a", "a"); // self-loop + + TopologicalSort<String> topSort = new TopologicalSort<>(graph); + + assertThrows(IllegalStateException.class, () -> topSort.topSortOrder()); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/KosarajuTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/KosarajuTest.java new file mode 100644 index 000000000000..53ed26dff26f --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/graphs/KosarajuTest.java @@ -0,0 +1,111 @@ +package com.thealgorithms.datastructures.graphs; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class KosarajuTest { + + private final Kosaraju kosaraju = new Kosaraju(); + + @Test + public void testFindStronglyConnectedComponents() { + // Create a graph using adjacency list + int n = 8; + List<List<Integer>> adjList = new ArrayList<>(n); + for (int i = 0; i < n; i++) { + adjList.add(new ArrayList<>()); + } + + adjList.get(0).add(1); + adjList.get(1).add(2); + adjList.get(2).add(0); + adjList.get(2).add(3); + adjList.get(3).add(4); + adjList.get(4).add(5); + adjList.get(4).add(7); + adjList.get(5).add(6); + adjList.get(6).add(4); + adjList.get(6).add(7); + + List<List<Integer>> actualResult = kosaraju.kosaraju(n, adjList); + List<List<Integer>> expectedResult = new ArrayList<>(); + /* + Expected result: + {0, 1, 2} + {3} + {5, 4, 6} + {7} + */ + expectedResult.add(Arrays.asList(1, 2, 0)); + expectedResult.add(List.of(3)); + expectedResult.add(Arrays.asList(5, 6, 4)); + expectedResult.add(List.of(7)); + + assertEquals(expectedResult, actualResult); + } + + @Test + public void testFindSingleNodeSCC() { + // Create a simple graph using adjacency list + int n = 8; + List<List<Integer>> adjList = new ArrayList<>(n); + for (int i = 0; i < n; i++) { + adjList.add(new ArrayList<>()); + } + + adjList.get(0).add(1); + adjList.get(1).add(2); + adjList.get(2).add(3); + adjList.get(3).add(4); + adjList.get(4).add(5); + adjList.get(5).add(6); + adjList.get(6).add(7); + adjList.get(7).add(0); + + List<List<Integer>> actualResult = kosaraju.kosaraju(n, adjList); + List<List<Integer>> expectedResult = new ArrayList<>(); + /* + Expected result: + {0, 1, 2, 3, 4, 5, 6, 7} + */ + expectedResult.add(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 0)); + + assertEquals(expectedResult, actualResult); + } + + @Test + public void testDisconnectedGraph() { + // Create a disconnected graph (two separate components) + int n = 5; + List<List<Integer>> adjList = new ArrayList<>(n); + for (int i = 0; i < n; i++) { + adjList.add(new ArrayList<>()); + } + + // Add edges for first component + adjList.get(0).add(1); + adjList.get(1).add(2); + adjList.get(2).add(0); + + // Add edges for second component + adjList.get(3).add(4); + adjList.get(4).add(3); + + List<List<Integer>> actualResult = kosaraju.kosaraju(n, adjList); + + List<List<Integer>> expectedResult = new ArrayList<>(); + /* + Expected result: + {0, 1, 2} + {3, 4} + */ + expectedResult.add(Arrays.asList(4, 3)); + expectedResult.add(Arrays.asList(1, 2, 0)); + + assertEquals(expectedResult, actualResult); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/KruskalTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/KruskalTest.java new file mode 100644 index 000000000000..b18f161ef1a6 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/graphs/KruskalTest.java @@ -0,0 +1,112 @@ +package com.thealgorithms.datastructures.graphs; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class KruskalTest { + + private Kruskal kruskal; + private HashSet<Kruskal.Edge>[] graph; + + @BeforeEach + public void setUp() { + kruskal = new Kruskal(); + int n = 7; + graph = new HashSet[n]; + for (int i = 0; i < n; i++) { + graph[i] = new HashSet<>(); + } + + // Add edges to the graph + Kruskal.addEdge(graph, 0, 1, 2); + Kruskal.addEdge(graph, 0, 2, 3); + Kruskal.addEdge(graph, 0, 3, 3); + Kruskal.addEdge(graph, 1, 2, 4); + Kruskal.addEdge(graph, 2, 3, 5); + Kruskal.addEdge(graph, 1, 4, 3); + Kruskal.addEdge(graph, 2, 4, 1); + Kruskal.addEdge(graph, 3, 5, 7); + Kruskal.addEdge(graph, 4, 5, 8); + Kruskal.addEdge(graph, 5, 6, 9); + } + + @Test + public void testKruskal() { + int n = 6; + HashSet<Kruskal.Edge>[] graph = new HashSet[n]; + + for (int i = 0; i < n; i++) { + graph[i] = new HashSet<>(); + } + + Kruskal.addEdge(graph, 0, 1, 4); + Kruskal.addEdge(graph, 0, 2, 2); + Kruskal.addEdge(graph, 1, 2, 1); + Kruskal.addEdge(graph, 1, 3, 5); + Kruskal.addEdge(graph, 2, 3, 8); + Kruskal.addEdge(graph, 2, 4, 10); + Kruskal.addEdge(graph, 3, 4, 2); + Kruskal.addEdge(graph, 3, 5, 6); + Kruskal.addEdge(graph, 4, 5, 3); + + HashSet<Kruskal.Edge>[] result = kruskal.kruskal(graph); + + List<List<Integer>> actualEdges = new ArrayList<>(); + for (HashSet<Kruskal.Edge> edges : result) { + for (Kruskal.Edge edge : edges) { + actualEdges.add(Arrays.asList(edge.from, edge.to, edge.weight)); + } + } + + List<List<Integer>> expectedEdges = Arrays.asList(Arrays.asList(1, 2, 1), Arrays.asList(0, 2, 2), Arrays.asList(3, 4, 2), Arrays.asList(4, 5, 3), Arrays.asList(1, 3, 5)); + + assertTrue(actualEdges.containsAll(expectedEdges) && expectedEdges.containsAll(actualEdges)); + } + + @Test + public void testEmptyGraph() { + HashSet<Kruskal.Edge>[] emptyGraph = new HashSet[0]; + HashSet<Kruskal.Edge>[] result = kruskal.kruskal(emptyGraph); + assertEquals(0, result.length); + } + + @Test + public void testSingleNodeGraph() { + HashSet<Kruskal.Edge>[] singleNodeGraph = new HashSet[1]; + singleNodeGraph[0] = new HashSet<>(); + HashSet<Kruskal.Edge>[] result = kruskal.kruskal(singleNodeGraph); + assertTrue(result[0].isEmpty()); + } + + @Test + public void testGraphWithDisconnectedNodes() { + int n = 5; + HashSet<Kruskal.Edge>[] disconnectedGraph = new HashSet[n]; + for (int i = 0; i < n; i++) { + disconnectedGraph[i] = new HashSet<>(); + } + + Kruskal.addEdge(disconnectedGraph, 0, 1, 2); + Kruskal.addEdge(disconnectedGraph, 2, 3, 4); + + HashSet<Kruskal.Edge>[] result = kruskal.kruskal(disconnectedGraph); + + List<List<Integer>> actualEdges = new ArrayList<>(); + for (HashSet<Kruskal.Edge> edges : result) { + for (Kruskal.Edge edge : edges) { + actualEdges.add(Arrays.asList(edge.from, edge.to, edge.weight)); + } + } + + List<List<Integer>> expectedEdges = Arrays.asList(Arrays.asList(0, 1, 2), Arrays.asList(2, 3, 4)); + + assertTrue(actualEdges.containsAll(expectedEdges) && expectedEdges.containsAll(actualEdges)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/MatrixGraphsTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/MatrixGraphsTest.java new file mode 100644 index 000000000000..cc8a2df872ce --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/graphs/MatrixGraphsTest.java @@ -0,0 +1,140 @@ +package com.thealgorithms.datastructures.graphs; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +class MatrixGraphsTest { + + @Test + void testGraphConstruction() { + AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(5); + assertEquals(5, graph.numberOfVertices()); + assertEquals(0, graph.numberOfEdges()); + } + + @Test + void testAddEdge() { + AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(5); + assertTrue(graph.addEdge(0, 1)); + assertTrue(graph.edgeDoesExist(0, 1)); + assertTrue(graph.edgeDoesExist(1, 0)); + assertEquals(1, graph.numberOfEdges()); + + // Adding the same edge again should return false + assertFalse(graph.addEdge(0, 1)); + assertFalse(graph.addEdge(5, 1)); + assertFalse(graph.addEdge(-1, 1)); + } + + @Test + void testRemoveEdge() { + AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(5); + graph.addEdge(0, 1); + graph.addEdge(1, 2); + + assertTrue(graph.removeEdge(0, 1)); + assertFalse(graph.edgeDoesExist(0, 1)); + assertFalse(graph.edgeDoesExist(1, 0)); + assertEquals(1, graph.numberOfEdges()); + + assertFalse(graph.removeEdge(0, 3)); + assertFalse(graph.removeEdge(5, 1)); + assertFalse(graph.removeEdge(-1, 1)); + } + + @Test + void testVertexDoesExist() { + AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(5); + assertTrue(graph.vertexDoesExist(0)); + assertTrue(graph.vertexDoesExist(4)); + assertFalse(graph.vertexDoesExist(5)); + assertFalse(graph.vertexDoesExist(-1)); + } + + @Test + void testDepthFirstOrder() { + AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(5); + graph.addEdge(0, 1); + graph.addEdge(0, 2); + graph.addEdge(1, 3); + graph.addEdge(2, 4); + + List<Integer> dfs = graph.depthFirstOrder(0); + assertEquals(5, dfs.size()); + assertEquals(0, dfs.getFirst()); + + assertTrue(dfs.containsAll(Arrays.asList(0, 1, 2, 3, 4))); + + List<Integer> emptyDfs = graph.depthFirstOrder(5); + assertTrue(emptyDfs.isEmpty()); + } + + @Test + void testBreadthFirstOrder() { + AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(5); + graph.addEdge(0, 1); + graph.addEdge(0, 2); + graph.addEdge(1, 3); + graph.addEdge(2, 4); + + List<Integer> bfs = graph.breadthFirstOrder(0); + assertEquals(5, bfs.size()); + assertEquals(0, bfs.getFirst()); + + assertTrue(bfs.containsAll(Arrays.asList(0, 1, 2, 3, 4))); + + List<Integer> emptyBfs = graph.breadthFirstOrder(5); + assertTrue(emptyBfs.isEmpty()); + } + + @Test + void testToString() { + AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(3); + graph.addEdge(0, 1); + graph.addEdge(1, 2); + + String expected = " 0 1 2 \n" + + "0 : 0 1 0 \n" + + "1 : 1 0 1 \n" + + "2 : 0 1 0 \n"; + + assertEquals(expected, graph.toString()); + } + + @Test + void testCyclicGraph() { + AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(4); + graph.addEdge(0, 1); + graph.addEdge(1, 2); + graph.addEdge(2, 3); + graph.addEdge(3, 0); + + List<Integer> dfs = graph.depthFirstOrder(0); + List<Integer> bfs = graph.breadthFirstOrder(0); + + assertEquals(4, dfs.size()); + assertEquals(4, bfs.size()); + assertTrue(dfs.containsAll(Arrays.asList(0, 1, 2, 3))); + assertTrue(bfs.containsAll(Arrays.asList(0, 1, 2, 3))); + } + + @Test + void testDisconnectedGraph() { + AdjacencyMatrixGraph graph = new AdjacencyMatrixGraph(5); + graph.addEdge(0, 1); + graph.addEdge(2, 3); + + List<Integer> dfs = graph.depthFirstOrder(0); + List<Integer> bfs = graph.breadthFirstOrder(0); + + assertEquals(2, dfs.size()); + assertEquals(2, bfs.size()); + assertTrue(dfs.containsAll(Arrays.asList(0, 1))); + assertTrue(bfs.containsAll(Arrays.asList(0, 1))); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/PrimMSTTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/PrimMSTTest.java new file mode 100644 index 000000000000..ec59a3880642 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/graphs/PrimMSTTest.java @@ -0,0 +1,54 @@ +package com.thealgorithms.datastructures.graphs; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +public class PrimMSTTest { + + private final PrimMST primMST = new PrimMST(); + + @Test + public void testSimpleGraph() { + // Test graph with 5 nodes and weighted edges + int[][] graph = {{0, 2, 0, 6, 0}, {2, 0, 3, 8, 5}, {0, 3, 0, 0, 7}, {6, 8, 0, 0, 9}, {0, 5, 7, 9, 0}}; + + int[] expectedParent = {-1, 0, 1, 0, 1}; + int[] actualParent = primMST.primMST(graph); + + assertArrayEquals(expectedParent, actualParent); + } + + @Test + public void testDisconnectedGraph() { + // Test case with a disconnected graph (no valid MST) + int[][] graph = {{0, 1, 0, 0, 0}, {1, 0, 2, 0, 0}, {0, 2, 0, 3, 0}, {0, 0, 3, 0, 4}, {0, 0, 0, 4, 0}}; + + int[] expectedParent = {-1, 0, 1, 2, 3}; // Expected MST parent array + int[] actualParent = primMST.primMST(graph); + + assertArrayEquals(expectedParent, actualParent); + } + + @Test + public void testAllEqualWeightsGraph() { + // Test case where all edges have equal weight + int[][] graph = {{0, 1, 1, 1, 1}, {1, 0, 1, 1, 1}, {1, 1, 0, 1, 1}, {1, 1, 1, 0, 1}, {1, 1, 1, 1, 0}}; + + int[] expectedParent = {-1, 0, 0, 0, 0}; // Expected MST parent array (any valid spanning tree) + int[] actualParent = primMST.primMST(graph); + + assertArrayEquals(expectedParent, actualParent); + } + + @Test + public void testSparseGraph() { + // Test case with a sparse graph (few edges) + int[][] graph = {{0, 1, 0, 0, 0}, {1, 0, 1, 0, 0}, {0, 1, 0, 1, 0}, {0, 0, 1, 0, 1}, {0, 0, 0, 1, 0}}; + + int[] expectedParent = {-1, 0, 1, 2, 3}; // Expected MST parent array + int[] actualParent = primMST.primMST(graph); + + assertArrayEquals(expectedParent, actualParent); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/TarjansAlgorithmTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/TarjansAlgorithmTest.java new file mode 100644 index 000000000000..314cc415815d --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/graphs/TarjansAlgorithmTest.java @@ -0,0 +1,132 @@ +package com.thealgorithms.datastructures.graphs; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class TarjansAlgorithmTest { + + private final TarjansAlgorithm tarjansAlgo = new TarjansAlgorithm(); + + @Test + public void testFindStronglyConnectedComponents() { + int v = 5; + var graph = new ArrayList<List<Integer>>(); + for (int i = 0; i < v; i++) { + graph.add(new ArrayList<>()); + } + graph.get(0).add(1); + graph.get(1).add(2); + graph.get(2).add(0); + graph.get(1).add(3); + graph.get(3).add(4); + + var actualResult = tarjansAlgo.stronglyConnectedComponents(v, graph); + /* + Expected result: + 0, 1, 2 + 3 + 4 + */ + List<List<Integer>> expectedResult = new ArrayList<>(); + expectedResult.add(List.of(4)); + expectedResult.add(List.of(3)); + expectedResult.add(Arrays.asList(2, 1, 0)); + assertEquals(expectedResult, actualResult); + } + + @Test + public void testFindStronglyConnectedComponentsWithSingleNodes() { + // Create a graph where each node is its own SCC + int n = 8; + var adjList = new ArrayList<List<Integer>>(n); + for (int i = 0; i < n; i++) { + adjList.add(new ArrayList<>()); + } + adjList.get(0).add(1); + adjList.get(1).add(2); + adjList.get(2).add(3); + adjList.get(3).add(4); + adjList.get(4).add(5); + adjList.get(5).add(6); + adjList.get(6).add(7); + adjList.get(7).add(0); + + List<List<Integer>> actualResult = tarjansAlgo.stronglyConnectedComponents(n, adjList); + List<List<Integer>> expectedResult = new ArrayList<>(); + /* + Expected result: + 7, 6, 5, 4, 3, 2, 1, 0 + */ + expectedResult.add(Arrays.asList(7, 6, 5, 4, 3, 2, 1, 0)); + assertEquals(expectedResult, actualResult); + } + + @Test + public void testGraphWithMultipleSCCs() { + int v = 6; + var graph = new ArrayList<List<Integer>>(); + for (int i = 0; i < v; i++) { + graph.add(new ArrayList<>()); + } + graph.get(0).add(1); + graph.get(1).add(2); + graph.get(2).add(0); + graph.get(3).add(4); + graph.get(4).add(5); + graph.get(5).add(3); + + var actualResult = tarjansAlgo.stronglyConnectedComponents(v, graph); + List<List<Integer>> expectedResult = new ArrayList<>(); + expectedResult.add(Arrays.asList(2, 1, 0)); // SCC containing 0, 1, 2 + expectedResult.add(Arrays.asList(5, 4, 3)); // SCC containing 3, 4, 5 + assertEquals(expectedResult, actualResult); + } + + @Test + public void testDisconnectedGraph() { + int v = 7; + var graph = new ArrayList<List<Integer>>(); + for (int i = 0; i < v; i++) { + graph.add(new ArrayList<>()); + } + graph.get(0).add(1); + graph.get(1).add(0); + graph.get(2).add(3); + graph.get(3).add(4); + graph.get(4).add(2); + + var actualResult = tarjansAlgo.stronglyConnectedComponents(v, graph); + List<List<Integer>> expectedResult = new ArrayList<>(); + expectedResult.add(Arrays.asList(1, 0)); // SCC containing 0, 1 + expectedResult.add(Arrays.asList(4, 3, 2)); // SCC containing 2, 3, 4 + expectedResult.add(List.of(5)); // SCC containing 5 + expectedResult.add(List.of(6)); // SCC containing 6 + assertEquals(expectedResult, actualResult); + } + + @Test + public void testSingleNodeGraph() { + int v = 1; + var graph = new ArrayList<List<Integer>>(); + graph.add(new ArrayList<>()); + + var actualResult = tarjansAlgo.stronglyConnectedComponents(v, graph); + List<List<Integer>> expectedResult = new ArrayList<>(); + expectedResult.add(List.of(0)); // SCC with a single node + assertEquals(expectedResult, actualResult); + } + + @Test + public void testEmptyGraph() { + int v = 0; + var graph = new ArrayList<List<Integer>>(); + + var actualResult = tarjansAlgo.stronglyConnectedComponents(v, graph); + List<List<Integer>> expectedResult = new ArrayList<>(); // No SCCs in an empty graph + assertEquals(expectedResult, actualResult); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/WelshPowellTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/WelshPowellTest.java new file mode 100644 index 000000000000..f45c4e10be56 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/graphs/WelshPowellTest.java @@ -0,0 +1,134 @@ +package com.thealgorithms.datastructures.graphs; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.thealgorithms.datastructures.graphs.WelshPowell.Graph; +import java.util.Arrays; +import org.junit.jupiter.api.Test; + +class WelshPowellTest { + + @Test + void testSimpleGraph() { + final var graph = WelshPowell.makeGraph(4, new int[][] {{0, 1}, {1, 2}, {2, 3}}); + int[] colors = WelshPowell.findColoring(graph); + assertTrue(isColoringValid(graph, colors)); + assertEquals(2, countDistinctColors(colors)); + } + + @Test + void testDisconnectedGraph() { + final var graph = WelshPowell.makeGraph(3, new int[][] {}); // No edges + int[] colors = WelshPowell.findColoring(graph); + assertTrue(isColoringValid(graph, colors)); + assertEquals(1, countDistinctColors(colors)); + } + + @Test + void testCompleteGraph() { + final var graph = WelshPowell.makeGraph(3, new int[][] {{0, 1}, {1, 2}, {2, 0}}); + int[] colors = WelshPowell.findColoring(graph); + assertTrue(isColoringValid(graph, colors)); + assertEquals(3, countDistinctColors(colors)); + } + + @Test + void testComplexGraph() { + int[][] edges = { + {0, 7}, + {0, 1}, + {1, 3}, + {2, 3}, + {3, 8}, + {3, 10}, + {4, 10}, + {4, 5}, + {5, 6}, + {6, 10}, + {6, 7}, + {7, 8}, + {7, 9}, + {7, 10}, + {8, 9}, + {9, 10}, + }; + + final var graph = WelshPowell.makeGraph(11, edges); // 11 vertices from A (0) to K (10) + int[] colors = WelshPowell.findColoring(graph); + + assertTrue(isColoringValid(graph, colors), "The coloring should be valid with no adjacent vertices sharing the same color."); + assertEquals(3, countDistinctColors(colors), "The chromatic number of the graph should be 3."); + } + + @Test + void testNegativeVertices() { + assertThrows(IllegalArgumentException.class, () -> { WelshPowell.makeGraph(-1, new int[][] {}); }, "Number of vertices cannot be negative"); + } + + @Test + void testSelfLoop() { + assertThrows(IllegalArgumentException.class, () -> { WelshPowell.makeGraph(3, new int[][] {{0, 0}}); }, "Self-loops are not allowed"); + } + + @Test + void testInvalidVertex() { + assertThrows(IllegalArgumentException.class, () -> { WelshPowell.makeGraph(3, new int[][] {{0, 3}}); }, "Vertex out of bounds"); + assertThrows(IllegalArgumentException.class, () -> { WelshPowell.makeGraph(3, new int[][] {{0, -1}}); }, "Vertex out of bounds"); + } + + @Test + void testInvalidEdgeArray() { + assertThrows(IllegalArgumentException.class, () -> { WelshPowell.makeGraph(3, new int[][] {{0}}); }, "Edge array must have exactly two elements"); + } + + @Test + void testWithPreColoredVertex() { + final var graph = WelshPowell.makeGraph(4, new int[][] {{0, 1}, {1, 2}, {2, 3}}); + int[] colors = WelshPowell.findColoring(graph); + assertTrue(isColoringValid(graph, colors)); + assertTrue(countDistinctColors(colors) >= 2); + for (int color : colors) { + assertTrue(color >= 0); + } + } + + @Test + void testLargeGraph() { + int[][] edges = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 0}, {6, 7}, {7, 8}, {8, 6}, {9, 10}, {10, 11}, {11, 9}, {12, 13}, {13, 14}, {14, 15}}; + + final var graph = WelshPowell.makeGraph(16, edges); // 16 vertices + int[] colors = WelshPowell.findColoring(graph); + assertTrue(isColoringValid(graph, colors)); + assertEquals(3, countDistinctColors(colors)); // Expecting a maximum of 3 colors + } + + @Test + void testStarGraph() { + int[][] edges = {{0, 1}, {0, 2}, {0, 3}, {0, 4}}; + + final var graph = WelshPowell.makeGraph(5, edges); // 5 vertices in a star formation + int[] colors = WelshPowell.findColoring(graph); + assertTrue(isColoringValid(graph, colors)); + assertEquals(2, countDistinctColors(colors)); // Star graph can be colored with 2 colors + } + + private boolean isColoringValid(Graph graph, int[] colors) { + if (Arrays.stream(colors).anyMatch(n -> n < 0)) { + return false; + } + for (int i = 0; i < graph.getNumVertices(); i++) { + for (int neighbor : graph.getAdjacencyList(i)) { + if (i != neighbor && colors[i] == colors[neighbor]) { + return false; // Adjacent vertices have the same color + } + } + } + return true; // No adjacent vertices share the same color + } + + private int countDistinctColors(int[] colors) { + return (int) Arrays.stream(colors).distinct().count(); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayListTest.java b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayListTest.java new file mode 100644 index 000000000000..629aaae95753 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayListTest.java @@ -0,0 +1,96 @@ +package com.thealgorithms.datastructures.hashmap.hashing; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class GenericHashMapUsingArrayListTest { + + @Test + void testGenericHashmapWhichUsesArrayAndBothKeyAndValueAreStrings() { + GenericHashMapUsingArrayList<String, String> map = new GenericHashMapUsingArrayList<>(); + map.put("USA", "Washington DC"); + map.put("Nepal", "Kathmandu"); + map.put("India", "New Delhi"); + map.put("Australia", "Sydney"); + assertNotNull(map); + assertEquals(4, map.size()); + assertEquals("Kathmandu", map.get("Nepal")); + assertEquals("Sydney", map.get("Australia")); + } + + @Test + void testGenericHashmapWhichUsesArrayAndKeyIsStringValueIsInteger() { + GenericHashMapUsingArrayList<String, Integer> map = new GenericHashMapUsingArrayList<>(); + map.put("USA", 87); + map.put("Nepal", 25); + map.put("India", 101); + map.put("Australia", 99); + assertNotNull(map); + assertEquals(4, map.size()); + assertEquals(25, map.get("Nepal")); + assertEquals(99, map.get("Australia")); + map.remove("Nepal"); + assertFalse(map.containsKey("Nepal")); + } + + @Test + void testGenericHashmapWhichUsesArrayAndKeyIsIntegerValueIsString() { + GenericHashMapUsingArrayList<Integer, String> map = new GenericHashMapUsingArrayList<>(); + map.put(101, "Washington DC"); + map.put(34, "Kathmandu"); + map.put(46, "New Delhi"); + map.put(89, "Sydney"); + assertNotNull(map); + assertEquals(4, map.size()); + assertEquals("Sydney", map.get(89)); + assertEquals("Washington DC", map.get(101)); + assertTrue(map.containsKey(46)); + } + + @Test + void testRemoveNonExistentKey() { + GenericHashMapUsingArrayList<String, String> map = new GenericHashMapUsingArrayList<>(); + map.put("USA", "Washington DC"); + map.remove("Nepal"); // Attempting to remove a non-existent key + assertEquals(1, map.size()); // Size should remain the same + } + + @Test + void testRehashing() { + GenericHashMapUsingArrayList<String, String> map = new GenericHashMapUsingArrayList<>(); + for (int i = 0; i < 20; i++) { + map.put("Key" + i, "Value" + i); + } + assertEquals(20, map.size()); // Ensure all items were added + assertEquals("Value5", map.get("Key5")); // Check retrieval after rehash + } + + @Test + void testUpdateValueForExistingKey() { + GenericHashMapUsingArrayList<String, String> map = new GenericHashMapUsingArrayList<>(); + map.put("USA", "Washington DC"); + map.put("USA", "New Washington DC"); // Updating value for existing key + assertEquals("New Washington DC", map.get("USA")); + } + + @Test + void testToStringMethod() { + GenericHashMapUsingArrayList<String, String> map = new GenericHashMapUsingArrayList<>(); + map.put("USA", "Washington DC"); + map.put("Nepal", "Kathmandu"); + String expected = "{USA : Washington DC, Nepal : Kathmandu}"; + assertEquals(expected, map.toString()); + } + + @Test + void testContainsKey() { + GenericHashMapUsingArrayList<String, String> map = new GenericHashMapUsingArrayList<>(); + map.put("USA", "Washington DC"); + assertTrue(map.containsKey("USA")); + assertFalse(map.containsKey("Nepal")); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayTest.java b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayTest.java new file mode 100644 index 000000000000..5d1733a3e97c --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayTest.java @@ -0,0 +1,96 @@ +package com.thealgorithms.datastructures.hashmap.hashing; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class GenericHashMapUsingArrayTest { + + @Test + void testGenericHashmapWhichUsesArrayAndBothKeyAndValueAreStrings() { + GenericHashMapUsingArray<String, String> map = new GenericHashMapUsingArray<>(); + map.put("USA", "Washington DC"); + map.put("Nepal", "Kathmandu"); + map.put("India", "New Delhi"); + map.put("Australia", "Sydney"); + assertNotNull(map); + assertEquals(4, map.size()); + assertEquals("Kathmandu", map.get("Nepal")); + assertEquals("Sydney", map.get("Australia")); + } + + @Test + void testGenericHashmapWhichUsesArrayAndKeyIsStringValueIsInteger() { + GenericHashMapUsingArray<String, Integer> map = new GenericHashMapUsingArray<>(); + map.put("USA", 87); + map.put("Nepal", 25); + map.put("India", 101); + map.put("Australia", 99); + assertNotNull(map); + assertEquals(4, map.size()); + assertEquals(25, map.get("Nepal")); + assertEquals(99, map.get("Australia")); + map.remove("Nepal"); + assertFalse(map.containsKey("Nepal")); + } + + @Test + void testGenericHashmapWhichUsesArrayAndKeyIsIntegerValueIsString() { + GenericHashMapUsingArray<Integer, String> map = new GenericHashMapUsingArray<>(); + map.put(101, "Washington DC"); + map.put(34, "Kathmandu"); + map.put(46, "New Delhi"); + map.put(89, "Sydney"); + assertNotNull(map); + assertEquals(4, map.size()); + assertEquals("Sydney", map.get(89)); + assertEquals("Washington DC", map.get(101)); + assertTrue(map.containsKey(46)); + } + + @Test + void testRemoveNonExistentKey() { + GenericHashMapUsingArray<String, String> map = new GenericHashMapUsingArray<>(); + map.put("USA", "Washington DC"); + map.remove("Nepal"); // Attempting to remove a non-existent key + assertEquals(1, map.size()); // Size should remain the same + } + + @Test + void testRehashing() { + GenericHashMapUsingArray<String, String> map = new GenericHashMapUsingArray<>(); + for (int i = 0; i < 20; i++) { + map.put("Key" + i, "Value" + i); + } + assertEquals(20, map.size()); // Ensure all items were added + assertEquals("Value5", map.get("Key5")); // Check retrieval after rehash + } + + @Test + void testUpdateValueForExistingKey() { + GenericHashMapUsingArray<String, String> map = new GenericHashMapUsingArray<>(); + map.put("USA", "Washington DC"); + map.put("USA", "New Washington DC"); // Updating value for existing key + assertEquals("New Washington DC", map.get("USA")); + } + + @Test + void testToStringMethod() { + GenericHashMapUsingArray<String, String> map = new GenericHashMapUsingArray<>(); + map.put("USA", "Washington DC"); + map.put("Nepal", "Kathmandu"); + String expected = "{USA : Washington DC, Nepal : Kathmandu}"; + assertEquals(expected, map.toString()); + } + + @Test + void testContainsKey() { + GenericHashMapUsingArray<String, String> map = new GenericHashMapUsingArray<>(); + map.put("USA", "Washington DC"); + assertTrue(map.containsKey("USA")); + assertFalse(map.containsKey("Nepal")); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapCuckooHashingTest.java b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapCuckooHashingTest.java new file mode 100644 index 000000000000..c2f80bfe3210 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapCuckooHashingTest.java @@ -0,0 +1,140 @@ +package com.thealgorithms.datastructures.hashmap.hashing; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class HashMapCuckooHashingTest { + + @Test + void insertKey() { + HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10); + assertEquals(0, hashTable.getNumberOfKeysInTable()); + + hashTable.insertKey2HashTable(3); + assertEquals(1, hashTable.getNumberOfKeysInTable()); + } + + @Test + void getKeyIndex() { + HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10); + hashTable.insertKey2HashTable(8); + hashTable.insertKey2HashTable(4); + + assertNotEquals(-1, hashTable.findKeyInTable(8)); + } + + @Test + void containsKey() { + HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10); + hashTable.insertKey2HashTable(8); + boolean contains = hashTable.checkTableContainsKey(8); + + assertTrue(contains); + } + + @Test + void removeKey() { + HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10); + hashTable.insertKey2HashTable(3); + + int initialSize = hashTable.getNumberOfKeysInTable(); + hashTable.deleteKeyFromHashTable(3); + + assertEquals(initialSize - 1, hashTable.getNumberOfKeysInTable()); + } + + @Test + void removeNone() { + HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10); + try { + hashTable.deleteKeyFromHashTable(3); + } catch (Exception e) { + assertTrue(true); + return; + } + Assertions.fail("Expected exception when trying to delete a non-existing key."); + } + + @Test + void reHashTableIncreasesTableSize() { + HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10); + hashTable.insertKey2HashTable(1); + hashTable.insertKey2HashTable(2); + hashTable.insertKey2HashTable(3); + hashTable.insertKey2HashTable(4); + hashTable.insertKey2HashTable(5); + hashTable.insertKey2HashTable(6); + hashTable.insertKey2HashTable(7); + hashTable.insertKey2HashTable(8); + hashTable.insertKey2HashTable(9); + hashTable.insertKey2HashTable(10); // This should trigger rehashing + + assertEquals(10, hashTable.getNumberOfKeysInTable()); + } + + @Test + void hashFunctionsAreDifferent() { + HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10); + hashTable.insertKey2HashTable(33); + + assertNotEquals(hashTable.hashFunction1(3), hashTable.hashFunction2(3), "Hash functions should produce different indices."); + } + + @Test + void avoidInfiniteLoops() { + HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10); + hashTable.insertKey2HashTable(0); + hashTable.insertKey2HashTable(10); + hashTable.insertKey2HashTable(100); + + assertTrue(hashTable.checkTableContainsKey(0)); + assertTrue(hashTable.checkTableContainsKey(10)); + assertTrue(hashTable.checkTableContainsKey(100)); + } + + @Test + void testLoadFactor() { + HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10); + for (int i = 1; i <= 8; i++) { // Insert 8 keys to test load factor + hashTable.insertKey2HashTable(i); + } + assertEquals(8, hashTable.getNumberOfKeysInTable()); + // Check that rehashing occurs when a 9th key is added + hashTable.insertKey2HashTable(9); + assertTrue(hashTable.getNumberOfKeysInTable() > 8, "Load factor exceeded, table should have been resized."); + } + + @Test + void testDeleteNonExistentKey() { + HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10); + hashTable.insertKey2HashTable(1); + hashTable.insertKey2HashTable(2); + + Exception exception = null; + try { + hashTable.deleteKeyFromHashTable(3); // Try deleting a non-existent key + } catch (IllegalArgumentException e) { + exception = e; // Capture the exception + } + assertNotNull(exception, "Expected an IllegalArgumentException when deleting a non-existent key."); + } + + @Test + void testInsertDuplicateKey() { + HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10); + hashTable.insertKey2HashTable(1); + Exception exception = null; + + try { + hashTable.insertKey2HashTable(1); // Attempt to insert duplicate key + } catch (IllegalArgumentException e) { + exception = e; // Capture the exception + } + assertNotNull(exception, "Expected an IllegalArgumentException for duplicate key insertion."); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapTest.java b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapTest.java new file mode 100644 index 000000000000..ff3ba3ed2571 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapTest.java @@ -0,0 +1,184 @@ +package com.thealgorithms.datastructures.hashmap.hashing; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.Test; + +public class HashMapTest { + + @Test + public void testInsertAndSearch() { + HashMap<Integer, String> hashMap = new HashMap<>(10); + hashMap.insert(15, "Value15"); + hashMap.insert(25, "Value25"); + hashMap.insert(35, "Value35"); + + assertEquals("Value15", hashMap.search(15)); + assertEquals("Value25", hashMap.search(25)); + assertEquals("Value35", hashMap.search(35)); + assertNull(hashMap.search(45)); // Test for non-existent key + } + + @Test + public void testDelete() { + HashMap<Integer, String> hashMap = new HashMap<>(10); + hashMap.insert(15, "Value15"); + hashMap.insert(25, "Value25"); + hashMap.insert(35, "Value35"); + + assertEquals("Value25", hashMap.search(25)); + hashMap.delete(25); + assertNull(hashMap.search(25)); // Confirm deletion + } + + @Test + public void testDisplay() { + HashMap<Integer, String> hashMap = new HashMap<>(5); + hashMap.insert(15, "Value15"); + hashMap.insert(25, "Value25"); + hashMap.insert(35, "Value35"); + // Optionally verify display functionality if it returns a string + hashMap.display(); // Manual check during test execution + } + + @Test + public void testInsertNullKey() { + HashMap<Integer, String> hashMap = new HashMap<>(10); + hashMap.insert(null, "NullValue"); + assertEquals("NullValue", hashMap.search(null)); // Verify null key handling + } + + @Test + public void testInsertNullValue() { + HashMap<Integer, String> hashMap = new HashMap<>(10); + hashMap.insert(15, null); + assertNull(hashMap.search(15)); // Verify null value handling + } + + @Test + public void testUpdateExistingKey() { + HashMap<Integer, String> hashMap = new HashMap<>(10); + hashMap.insert(15, "Value15"); + hashMap.insert(15, "UpdatedValue15"); + + assertEquals("UpdatedValue15", hashMap.search(15)); // Verify update + } + + @Test + public void testHandleCollisions() { + HashMap<Integer, String> hashMap = new HashMap<>(3); // Create a small bucket size to force collisions + // These keys should collide if the hash function is modulo 3 + hashMap.insert(1, "Value1"); + hashMap.insert(4, "Value4"); + hashMap.insert(7, "Value7"); + + assertEquals("Value1", hashMap.search(1)); + assertEquals("Value4", hashMap.search(4)); + assertEquals("Value7", hashMap.search(7)); + } + + @Test + public void testSearchInEmptyHashMap() { + HashMap<Integer, String> hashMap = new HashMap<>(10); + assertNull(hashMap.search(10)); // Confirm search returns null in empty map + } + + @Test + public void testDeleteNonExistentKey() { + HashMap<Integer, String> hashMap = new HashMap<>(10); + hashMap.insert(15, "Value15"); + hashMap.delete(25); // Delete non-existent key + + assertEquals("Value15", hashMap.search(15)); // Ensure existing key remains + assertNull(hashMap.search(25)); // Confirm non-existent key remains null + } + + @Test + public void testInsertLargeNumberOfElements() { + HashMap<Integer, String> hashMap = new HashMap<>(10); + for (int i = 0; i < 100; i++) { + hashMap.insert(i, "Value" + i); + } + + for (int i = 0; i < 100; i++) { + assertEquals("Value" + i, hashMap.search(i)); // Verify all inserted values + } + } + + @Test + public void testDeleteHeadOfBucket() { + HashMap<Integer, String> hashMap = new HashMap<>(3); + hashMap.insert(1, "Value1"); + hashMap.insert(4, "Value4"); + hashMap.insert(7, "Value7"); + + hashMap.delete(1); + assertNull(hashMap.search(1)); // Verify head deletion + assertEquals("Value4", hashMap.search(4)); + assertEquals("Value7", hashMap.search(7)); + } + + @Test + public void testDeleteTailOfBucket() { + HashMap<Integer, String> hashMap = new HashMap<>(3); + hashMap.insert(1, "Value1"); + hashMap.insert(4, "Value4"); + hashMap.insert(7, "Value7"); + + hashMap.delete(7); + assertNull(hashMap.search(7)); // Verify tail deletion + assertEquals("Value1", hashMap.search(1)); + assertEquals("Value4", hashMap.search(4)); + } + + @Test + public void testDeleteMiddleElementOfBucket() { + HashMap<Integer, String> hashMap = new HashMap<>(3); + hashMap.insert(1, "Value1"); + hashMap.insert(4, "Value4"); + hashMap.insert(7, "Value7"); + + hashMap.delete(4); + assertNull(hashMap.search(4)); // Verify middle element deletion + assertEquals("Value1", hashMap.search(1)); + assertEquals("Value7", hashMap.search(7)); + } + + @Test + public void testResizeHashMap() { + HashMap<Integer, String> hashMap = new HashMap<>(2); // Small initial size to force rehashing + for (int i = 0; i < 10; i++) { + hashMap.insert(i, "Value" + i); + } + + // Verify all values after resizing + for (int i = 0; i < 10; i++) { + assertEquals("Value" + i, hashMap.search(i)); + } + } + + @Test + public void testCollisionResolution() { + HashMap<String, String> hashMap = new HashMap<>(3); + hashMap.insert("abc", "Value1"); // Hash index 0 + hashMap.insert("cab", "Value2"); // Hash index 0 (collision) + hashMap.insert("bac", "Value3"); // Hash index 0 (collision) + + assertEquals("Value1", hashMap.search("abc")); + assertEquals("Value2", hashMap.search("cab")); + assertEquals("Value3", hashMap.search("bac")); + } + + @Test + public void testClearHashMap() { + HashMap<Integer, String> hashMap = new HashMap<>(10); + hashMap.insert(1, "Value1"); + hashMap.insert(2, "Value2"); + + hashMap.clear(); // Assuming clear method resets the hash map + assertNull(hashMap.search(1)); + assertNull(hashMap.search(2)); + assertEquals(0, hashMap.size()); // Verify size is reset + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/IntersectionTest.java b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/IntersectionTest.java new file mode 100644 index 000000000000..df6d15fd9ba4 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/IntersectionTest.java @@ -0,0 +1,76 @@ +package com.thealgorithms.datastructures.hashmap.hashing; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.junit.jupiter.api.Test; + +public class IntersectionTest { + + @Test + void testBasicIntersection() { + int[] arr1 = {1, 2, 2, 1}; + int[] arr2 = {2, 2}; + List<Integer> result = Intersection.intersection(arr1, arr2); + assertEquals(List.of(2, 2), result, "Intersection should return [2, 2]"); + } + + @Test + void testNoIntersection() { + int[] arr1 = {1, 2, 3}; + int[] arr2 = {4, 5, 6}; + List<Integer> result = Intersection.intersection(arr1, arr2); + assertTrue(result.isEmpty(), "Intersection should be empty for disjoint sets"); + } + + @Test + void testEmptyArray() { + int[] arr1 = {}; + int[] arr2 = {1, 2, 3}; + List<Integer> result = Intersection.intersection(arr1, arr2); + assertTrue(result.isEmpty(), "Intersection should be empty when first array is empty"); + + result = Intersection.intersection(arr2, arr1); + assertTrue(result.isEmpty(), "Intersection should be empty when second array is empty"); + } + + @Test + void testNullArray() { + int[] arr1 = null; + int[] arr2 = {1, 2, 3}; + List<Integer> result = Intersection.intersection(arr1, arr2); + assertTrue(result.isEmpty(), "Intersection should be empty when first array is null"); + + result = Intersection.intersection(arr2, arr1); + assertTrue(result.isEmpty(), "Intersection should be empty when second array is null"); + } + + @Test + void testMultipleOccurrences() { + int[] arr1 = {5, 5, 5, 6}; + int[] arr2 = {5, 5, 6, 6, 6}; + List<Integer> result = Intersection.intersection(arr1, arr2); + assertEquals(List.of(5, 5, 6), result, "Intersection should return [5, 5, 6]"); + } + + @Test + void testSameElements() { + int[] arr1 = {1, 1, 1}; + int[] arr2 = {1, 1, 1}; + List<Integer> result = Intersection.intersection(arr1, arr2); + assertEquals(List.of(1, 1, 1), result, "Intersection should return [1, 1, 1] for same elements"); + } + + @Test + void testLargeArrays() { + int[] arr1 = new int[1000]; + int[] arr2 = new int[1000]; + for (int i = 0; i < 1000; i++) { + arr1[i] = i; + arr2[i] = i; + } + List<Integer> result = Intersection.intersection(arr1, arr2); + assertEquals(1000, result.size(), "Intersection should return all elements for identical large arrays"); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/LinearProbingHashMapTest.java b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/LinearProbingHashMapTest.java new file mode 100644 index 000000000000..34b165d4bbcf --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/LinearProbingHashMapTest.java @@ -0,0 +1,91 @@ +package com.thealgorithms.datastructures.hashmap.hashing; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class LinearProbingHashMapTest extends MapTest { + + @Override + <Key extends Comparable<Key>, Value> Map<Key, Value> getMap() { + return new LinearProbingHashMap<>(); + } + + @Test + void putNullKey() { + Map<Integer, String> map = getMap(); + assertFalse(map.put(null, "value"), "Putting a null key should return false"); + } + + @Test + void putDuplicateKeys() { + Map<Integer, String> map = getMap(); + map.put(1, "one"); + map.put(1, "uno"); + assertEquals("uno", map.get(1), "Value should be updated to 'uno'"); + } + + @Test + void putResizeTest() { + Map<Integer, String> map = getMap(); + for (int i = 0; i < 20; i++) { + map.put(i, String.valueOf(i)); + } + assertEquals(20, map.size(), "Map size should be 20 after inserting 20 elements"); + } + + @Test + void deleteNonExistentKey() { + Map<Integer, String> map = getMap(); + assertFalse(map.delete(999), "Deleting a non-existent key should return false"); + } + + @Test + void deleteAndReinsert() { + Map<Integer, String> map = getMap(); + map.put(1, "one"); + map.delete(1); + assertFalse(map.contains(1), "Map should not contain the deleted key"); + map.put(1, "one again"); + assertTrue(map.contains(1), "Map should contain the key after reinsertion"); + } + + @Test + void resizeDown() { + Map<Integer, String> map = getMap(); + for (int i = 0; i < 16; i++) { + map.put(i, String.valueOf(i)); + } + for (int i = 0; i < 12; i++) { + map.delete(i); + } + assertEquals(4, map.size(), "Map size should be 4 after deleting 12 elements"); + } + + @Test + void keysOrderTest() { + Map<Integer, String> map = getMap(); + for (int i = 10; i > 0; i--) { + map.put(i, String.valueOf(i)); + } + int expectedKey = 1; + for (Integer key : map.keys()) { + assertEquals(expectedKey++, key, "Keys should be in sorted order"); + } + } + + @Test + void stressTest() { + Map<Integer, String> map = getMap(); + for (int i = 0; i < 1000; i++) { + map.put(i, String.valueOf(i)); + assertEquals(i + 1, map.size(), "Size should match number of inserted elements"); + } + for (int i = 0; i < 500; i++) { + map.delete(i); + assertEquals(1000 - (i + 1), map.size(), "Size should decrease correctly"); + } + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/MajorityElementTest.java b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/MajorityElementTest.java new file mode 100644 index 000000000000..7dcd5eb7a8f4 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/MajorityElementTest.java @@ -0,0 +1,80 @@ +package com.thealgorithms.datastructures.hashmap.hashing; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class MajorityElementTest { + @Test + void testMajorityWithSingleMajorityElement() { + int[] nums = {1, 2, 3, 9, 9, 6, 7, 8, 9, 9, 9, 9}; + List<Integer> expected = new ArrayList<>(); + expected.add(9); + List<Integer> actual = MajorityElement.majority(nums); + assertEquals(expected, actual); + } + + @Test + void testMajorityWithMultipleMajorityElements() { + int[] nums = {1, 2, 3, 3, 4, 4, 4, 4}; + List<Integer> expected = new ArrayList<>(); + expected.add(4); + List<Integer> actual = MajorityElement.majority(nums); + assertEquals(expected, actual); + } + + @Test + void testMajorityWithNoMajorityElement() { + int[] nums = {1, 2, 4, 4, 5, 4}; + List<Integer> expected = new ArrayList<>(); + expected.add(4); + List<Integer> actual = MajorityElement.majority(nums); + assertEquals(expected, actual); + } + + @Test + void testMajorityWithEmptyArray() { + int[] nums = {}; + List<Integer> expected = Collections.emptyList(); + List<Integer> actual = MajorityElement.majority(nums); + assertEquals(expected, actual); + } + + @Test + void testMajorityWithAllElementsSame() { + int[] nums = {5, 5, 5, 5, 5}; + List<Integer> expected = new ArrayList<>(); + expected.add(5); + List<Integer> actual = MajorityElement.majority(nums); + assertEquals(expected, actual); + } + + @Test + void testMajorityWithEvenCountAndOneMajorityElement() { + int[] nums = {1, 2, 2, 3, 3, 2}; + List<Integer> expected = new ArrayList<>(); + expected.add(2); + List<Integer> actual = MajorityElement.majority(nums); + assertEquals(expected, actual); + } + + @Test + void testMajorityWithNoElementsEqualToHalf() { + int[] nums = {1, 1, 2, 2, 3, 3, 4}; + List<Integer> expected = Collections.emptyList(); + List<Integer> actual = MajorityElement.majority(nums); + assertEquals(expected, actual); + } + + @Test + void testMajorityWithLargeArray() { + int[] nums = {1, 2, 3, 1, 1, 1, 2, 1, 1}; + List<Integer> expected = new ArrayList<>(); + expected.add(1); + List<Integer> actual = MajorityElement.majority(nums); + assertEquals(expected, actual); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/MapTest.java b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/MapTest.java new file mode 100644 index 000000000000..44551a8adac6 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/MapTest.java @@ -0,0 +1,131 @@ +package com.thealgorithms.datastructures.hashmap.hashing; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Random; +import org.junit.jupiter.api.Test; + +abstract class MapTest { + abstract <Key extends Comparable<Key>, Value> Map<Key, Value> getMap(); + + @Test + void putTest() { + Map<Integer, String> map = getMap(); + + assertFalse(map.put(null, "-25")); + assertFalse(map.put(null, null)); + assertTrue(map.put(-25, "-25")); + assertTrue(map.put(33, "33")); + assertTrue(map.put(100, "100")); + assertTrue(map.put(100, "+100")); + assertTrue(map.put(100, null)); + } + + @Test + void getTest() { + Map<Integer, String> map = getMap(); + for (int i = -100; i < 100; i++) { + map.put(i, String.valueOf(i)); + } + + for (int i = -100; i < 100; i++) { + assertEquals(map.get(i), String.valueOf(i)); + } + + for (int i = 100; i < 200; i++) { + assertNull(map.get(i)); + } + + assertNull(map.get(null)); + } + + @Test + void deleteTest() { + Map<Integer, String> map = getMap(); + for (int i = -100; i < 100; i++) { + map.put(i, String.valueOf(i)); + } + + for (int i = 0; i < 100; i++) { + assertTrue(map.delete(i)); + } + + for (int i = 100; i < 200; i++) { + assertFalse(map.delete(i)); + } + + assertFalse(map.delete(null)); + } + + @Test + void containsTest() { + Map<Integer, String> map = getMap(); + for (int i = -100; i < 100; i++) { + map.put(i, String.valueOf(i)); + } + + for (int i = -50; i < 50; i++) { + assertTrue(map.contains(i)); + } + + for (int i = 100; i < 200; i++) { + assertFalse(map.contains(i)); + } + + assertFalse(map.contains(null)); + } + + @Test + void sizeTest() { + Map<Integer, String> map = getMap(); + assertEquals(map.size(), 0); + + for (int i = -100; i < 100; i++) { + map.put(i, String.valueOf(i)); + } + + assertEquals(map.size(), 200); + + for (int i = -50; i < 50; i++) { + map.delete(i); + } + + assertEquals(map.size(), 100); + } + + @Test + void keysTest() { + Map<Integer, String> map = getMap(); + Iterable<Integer> keys = map.keys(); + assertFalse(keys.iterator().hasNext()); + + for (int i = 100; i > -100; i--) { + map.put(i, String.valueOf(i)); + } + + keys = map.keys(); + int i = -100; + for (Integer key : keys) { + assertEquals(key, ++i); + } + } + + @Test + void hashTest() { + Map<Integer, String> map = getMap(); + int testSize = 100; + Random random = new Random(); + for (int i = 0; i < 1000; i++) { + int randomInt = random.nextInt(); + int hashIndex = map.hash(randomInt, testSize); + int negateHashIndex = map.hash(-randomInt, testSize); + assertTrue(hashIndex >= 0); + assertTrue(hashIndex < testSize); + assertTrue(negateHashIndex >= 0); + assertTrue(negateHashIndex < testSize); + } + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/heaps/FibonacciHeapTest.java b/src/test/java/com/thealgorithms/datastructures/heaps/FibonacciHeapTest.java new file mode 100644 index 000000000000..d911f3ac30d8 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/heaps/FibonacciHeapTest.java @@ -0,0 +1,108 @@ +package com.thealgorithms.datastructures.heaps; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class FibonacciHeapTest { + + @Test + void testHeapInsertionAndMinimum() { + FibonacciHeap fibonacciHeap = new FibonacciHeap(); + fibonacciHeap.insert(5); + fibonacciHeap.insert(3); + fibonacciHeap.insert(1); + fibonacciHeap.insert(18); + fibonacciHeap.insert(33); + + Assertions.assertEquals(1, fibonacciHeap.findMin().getKey()); + fibonacciHeap.deleteMin(); + Assertions.assertEquals(3, fibonacciHeap.findMin().getKey()); + } + + @Test + void testDeleteMinOnSingleElementHeap() { + FibonacciHeap fibonacciHeap = new FibonacciHeap(10); + Assertions.assertEquals(10, fibonacciHeap.findMin().getKey()); + fibonacciHeap.deleteMin(); + Assertions.assertTrue(fibonacciHeap.empty()); + } + + @Test + void testHeapMeld() { + FibonacciHeap heap1 = new FibonacciHeap(); + FibonacciHeap heap2 = new FibonacciHeap(); + heap1.insert(1); + heap1.insert(2); + heap2.insert(3); + heap2.insert(4); + + heap1.meld(heap2); + Assertions.assertEquals(1, heap1.findMin().getKey()); + } + + @Test + void testHeapSize() { + FibonacciHeap fibonacciHeap = new FibonacciHeap(); + Assertions.assertEquals(0, fibonacciHeap.size()); + fibonacciHeap.insert(5); + Assertions.assertEquals(1, fibonacciHeap.size()); + fibonacciHeap.insert(3); + Assertions.assertEquals(2, fibonacciHeap.size()); + fibonacciHeap.deleteMin(); + Assertions.assertEquals(1, fibonacciHeap.size()); + } + + @Test + void testCountersRep() { + FibonacciHeap fibonacciHeap = new FibonacciHeap(); + fibonacciHeap.insert(5); + fibonacciHeap.insert(3); + fibonacciHeap.insert(8); + fibonacciHeap.insert(1); + + int[] counters = fibonacciHeap.countersRep(); + Assertions.assertEquals(4, counters[0]); + Assertions.assertEquals(0, counters[1]); + } + + @Test + void testDeleteMinMultipleElements() { + FibonacciHeap fibonacciHeap = new FibonacciHeap(); + fibonacciHeap.insert(5); + fibonacciHeap.insert(2); + fibonacciHeap.insert(8); + fibonacciHeap.insert(1); + + Assertions.assertEquals(1, fibonacciHeap.findMin().getKey()); + fibonacciHeap.deleteMin(); + Assertions.assertEquals(2, fibonacciHeap.findMin().getKey()); + } + + @Test + void testInsertNegativeKeys() { + FibonacciHeap fibonacciHeap = new FibonacciHeap(); + fibonacciHeap.insert(-10); + fibonacciHeap.insert(-5); + fibonacciHeap.insert(-20); + + Assertions.assertEquals(-20, fibonacciHeap.findMin().getKey()); + } + + @Test + void testDeleteOnEmptyHeap() { + FibonacciHeap fibonacciHeap = new FibonacciHeap(); + Assertions.assertThrows(NullPointerException.class, () -> { fibonacciHeap.delete(fibonacciHeap.findMin()); }); + } + + @Test + void testPotentialCalculation() { + FibonacciHeap fibonacciHeap = new FibonacciHeap(); + fibonacciHeap.insert(10); + fibonacciHeap.insert(20); + + Assertions.assertEquals(2, fibonacciHeap.potential()); // 2 trees, no marked nodes + var node = fibonacciHeap.findMin(); + fibonacciHeap.delete(node); + Assertions.assertEquals(1, fibonacciHeap.potential()); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/heaps/GenericHeapTest.java b/src/test/java/com/thealgorithms/datastructures/heaps/GenericHeapTest.java new file mode 100644 index 000000000000..a3642996b769 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/heaps/GenericHeapTest.java @@ -0,0 +1,85 @@ +package com.thealgorithms.datastructures.heaps; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class GenericHeapTest { + + private GenericHeap<Integer> heap; + + @BeforeEach + void setUp() { + heap = new GenericHeap<>(); + } + + @Test + void testAddAndGet() { + heap.add(10); + heap.add(20); + heap.add(5); + + assertEquals(20, heap.get()); + } + + @Test + void testRemove() { + heap.add(10); + heap.add(20); + heap.add(5); + + assertEquals(20, heap.remove()); + assertEquals(10, heap.get()); + } + + @Test + void testIsEmpty() { + assertTrue(heap.isEmpty()); + heap.add(1); + assertFalse(heap.isEmpty()); + } + + @Test + void testSize() { + assertEquals(0, heap.size()); + heap.add(1); + heap.add(2); + assertEquals(2, heap.size()); + } + + @Test + void testUpdatePriority() { + heap.add(10); + heap.add(20); + heap.add(5); + + heap.updatePriority(10); + assertEquals(20, heap.get()); + + heap.add(30); + heap.updatePriority(20); // 20 will be moved up + assertEquals(30, heap.get()); + } + + @Test + void testRemoveFromEmptyHeap() { + Exception exception = assertThrows(IllegalStateException.class, () -> heap.remove()); + assertEquals("Heap is empty", exception.getMessage()); + } + + @Test + void testGetFromEmptyHeap() { + Exception exception = assertThrows(IllegalStateException.class, () -> heap.get()); + assertEquals("Heap is empty", exception.getMessage()); + } + + @Test + void testUpdatePriorityForNonExistentItem() { + Exception exception = assertThrows(IllegalArgumentException.class, () -> heap.updatePriority(100)); + assertEquals("Item not found in the heap", exception.getMessage()); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/heaps/HeapElementTest.java b/src/test/java/com/thealgorithms/datastructures/heaps/HeapElementTest.java new file mode 100644 index 000000000000..d04a9de8a94b --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/heaps/HeapElementTest.java @@ -0,0 +1,55 @@ +package com.thealgorithms.datastructures.heaps; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.Test; + +class HeapElementTest { + + @Test + void testConstructorAndGetters() { + HeapElement element = new HeapElement(5.0, "Info"); + assertEquals(5.0, element.getKey()); + assertEquals("Info", element.getInfo()); + } + + @Test + void testConstructorWithNullInfo() { + HeapElement element = new HeapElement(10); + assertEquals(10, element.getKey()); + assertNull(element.getInfo()); + } + + @Test + void testToString() { + HeapElement element = new HeapElement(7.5, "TestInfo"); + assertEquals("Key: 7.5 - TestInfo", element.toString()); + + HeapElement elementWithoutInfo = new HeapElement(3); + assertEquals("Key: 3.0 - No additional info", elementWithoutInfo.toString()); + } + + @Test + void testEquals() { + HeapElement element1 = new HeapElement(2.5, "Data"); + HeapElement element2 = new HeapElement(2.5, "Data"); + HeapElement element3 = new HeapElement(3.0, "DifferentData"); + + assertEquals(element1, element2); // Same key and info + assertNotEquals(element1, element3); // Different key + assertNotEquals(null, element1); // Check for null + assertNotEquals("String", element1); // Check for different type + } + + @Test + void testHashCode() { + HeapElement element1 = new HeapElement(4, "HashMe"); + HeapElement element2 = new HeapElement(4, "HashMe"); + HeapElement element3 = new HeapElement(4, "DifferentHash"); + + assertEquals(element1.hashCode(), element2.hashCode()); // Same key and info + assertNotEquals(element1.hashCode(), element3.hashCode()); // Different info + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/heaps/KthElementFinderTest.java b/src/test/java/com/thealgorithms/datastructures/heaps/KthElementFinderTest.java new file mode 100644 index 000000000000..7b92a57eaa77 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/heaps/KthElementFinderTest.java @@ -0,0 +1,19 @@ +package com.thealgorithms.datastructures.heaps; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class KthElementFinderTest { + @Test + public void testFindKthLargest() { + int[] nums = {3, 2, 1, 5, 6, 4}; + assertEquals(5, KthElementFinder.findKthLargest(nums, 2)); + } + + @Test + public void testFindKthSmallest() { + int[] nums = {7, 10, 4, 3, 20, 15}; + assertEquals(7, KthElementFinder.findKthSmallest(nums, 3)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/heaps/LeftistHeapTest.java b/src/test/java/com/thealgorithms/datastructures/heaps/LeftistHeapTest.java new file mode 100644 index 000000000000..8c313237f88f --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/heaps/LeftistHeapTest.java @@ -0,0 +1,85 @@ +package com.thealgorithms.datastructures.heaps; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class LeftistHeapTest { + + @Test + void testIsEmpty() { + LeftistHeap heap = new LeftistHeap(); + Assertions.assertTrue(heap.isEmpty(), "Heap should be empty initially."); + + heap.insert(10); + Assertions.assertFalse(heap.isEmpty(), "Heap should not be empty after insertion."); + + heap.clear(); + Assertions.assertTrue(heap.isEmpty(), "Heap should be empty after clearing."); + } + + @Test + void testInsertAndExtractMin() { + LeftistHeap heap = new LeftistHeap(); + heap.insert(6); + heap.insert(2); + heap.insert(3); + heap.insert(1); + + Assertions.assertEquals(1, heap.extractMin(), "Minimum should be 1."); + Assertions.assertEquals(2, heap.extractMin(), "Next minimum should be 2."); + Assertions.assertEquals(3, heap.extractMin(), "Next minimum should be 3."); + Assertions.assertEquals(6, heap.extractMin(), "Next minimum should be 6."); + Assertions.assertEquals(-1, heap.extractMin(), "Extracting from an empty heap should return -1."); + } + + @Test + void testMerge() { + LeftistHeap heap1 = new LeftistHeap(); + heap1.insert(1); + heap1.insert(3); + heap1.insert(5); + + LeftistHeap heap2 = new LeftistHeap(); + heap2.insert(2); + heap2.insert(4); + heap2.insert(6); + + heap1.merge(heap2); + + Assertions.assertEquals(1, heap1.extractMin(), "After merging, minimum should be 1."); + Assertions.assertEquals(2, heap1.extractMin(), "Next minimum should be 2."); + Assertions.assertEquals(3, heap1.extractMin(), "Next minimum should be 3."); + Assertions.assertEquals(4, heap1.extractMin(), "Next minimum should be 4."); + Assertions.assertEquals(5, heap1.extractMin(), "Next minimum should be 5."); + Assertions.assertEquals(6, heap1.extractMin(), "Next minimum should be 6."); + Assertions.assertEquals(-1, heap1.extractMin(), "Extracting from an empty heap should return -1."); + } + + @Test + void testInOrderTraversal() { + LeftistHeap heap = new LeftistHeap(); + heap.insert(10); + heap.insert(5); + heap.insert(20); + heap.insert(15); + heap.insert(30); + + Assertions.assertEquals("[20, 15, 30, 5, 10]", heap.inOrder().toString(), "In-order traversal should match the expected output."); + } + + @Test + void testMultipleExtractions() { + LeftistHeap heap = new LeftistHeap(); + heap.insert(10); + heap.insert(5); + heap.insert(3); + heap.insert(8); + + // Extract multiple elements + Assertions.assertEquals(3, heap.extractMin()); + Assertions.assertEquals(5, heap.extractMin()); + Assertions.assertEquals(8, heap.extractMin()); + Assertions.assertEquals(10, heap.extractMin()); + Assertions.assertEquals(-1, heap.extractMin(), "Extracting from an empty heap should return -1."); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/heaps/MaxHeapTest.java b/src/test/java/com/thealgorithms/datastructures/heaps/MaxHeapTest.java new file mode 100644 index 000000000000..c1b7eb3fd4ae --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/heaps/MaxHeapTest.java @@ -0,0 +1,144 @@ +package com.thealgorithms.datastructures.heaps; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for MaxHeap implementation + */ +class MaxHeapTest { + + private MaxHeap heap; + + @BeforeEach + void setUp() { + // Create a fresh heap for each test + List<HeapElement> elements = Arrays.asList(new HeapElement(5.0, "Five"), new HeapElement(2.0, "Two"), new HeapElement(8.0, "Eight"), new HeapElement(1.0, "One"), new HeapElement(9.0, "Nine")); + heap = new MaxHeap(elements); + } + + @Test + void testConstructorWithNullList() { + assertThrows(IllegalArgumentException.class, () -> new MaxHeap(null)); + } + + @Test + void testConstructorWithEmptyList() { + MaxHeap emptyHeap = new MaxHeap(new ArrayList<>()); + assertTrue(emptyHeap.isEmpty()); + } + + @Test + void testConstructorWithNullElements() { + List<HeapElement> elements = Arrays.asList(new HeapElement(1.0, "One"), null, new HeapElement(2.0, "Two")); + MaxHeap heap = new MaxHeap(elements); + assertEquals(2, heap.size()); + } + + @Test + void testInsertElement() { + heap.insertElement(new HeapElement(10.0, "Ten")); + assertEquals(10.0, heap.getElement(1).getKey()); + assertEquals(6, heap.size()); + } + + @Test + void testInsertNullElement() { + assertThrows(IllegalArgumentException.class, () -> heap.insertElement(null)); + } + + @Test + void testGetElementAtIndex() { + HeapElement element = heap.getElement(1); + assertEquals(9.0, element.getKey()); + assertEquals("Nine", element.getValue()); + } + + @Test + void testGetElementAtInvalidIndex() { + assertThrows(IndexOutOfBoundsException.class, () -> heap.getElement(0)); + assertThrows(IndexOutOfBoundsException.class, () -> heap.getElement(10)); + } + + @Test + void testDeleteElement() throws EmptyHeapException { + heap.deleteElement(1); + assertEquals(8.0, heap.getElement(1).getKey()); + assertEquals(4, heap.size()); + } + + @Test + void testDeleteElementAtInvalidIndex() { + assertThrows(IndexOutOfBoundsException.class, () -> heap.deleteElement(0)); + assertThrows(IndexOutOfBoundsException.class, () -> heap.deleteElement(10)); + } + + @Test + void testDeleteFromEmptyHeap() { + MaxHeap emptyHeap = new MaxHeap(new ArrayList<>()); + assertThrows(EmptyHeapException.class, () -> emptyHeap.deleteElement(1)); + } + + @Test + void testExtractMax() throws EmptyHeapException { + HeapElement max = heap.getElement(); + assertEquals(9.0, max.getKey()); + assertEquals("Nine", max.getValue()); + assertEquals(4, heap.size()); + + max = heap.getElement(); + assertEquals(8.0, max.getKey()); + assertEquals(3, heap.size()); + } + + @Test + void testExtractMaxFromEmptyHeap() { + MaxHeap emptyHeap = new MaxHeap(new ArrayList<>()); + assertThrows(EmptyHeapException.class, () -> emptyHeap.getElement()); + } + + @Test + void testHeapOrder() { + // Test that parent is always greater than or equal to children + for (int i = 1; i <= heap.size() / 2; i++) { + double parentKey = heap.getElement(i).getKey(); + + // Check left child + if (2 * i <= heap.size()) { + assertTrue(parentKey >= heap.getElement(2 * i).getKey()); + } + + // Check right child + if (2 * i + 1 <= heap.size()) { + assertTrue(parentKey >= heap.getElement(2 * i + 1).getKey()); + } + } + } + + @Test + void testSizeAndEmpty() { + assertEquals(5, heap.size()); + assertFalse(heap.isEmpty()); + + // Remove all elements + while (!heap.isEmpty()) { + try { + heap.getElement(); + } catch (EmptyHeapException e) { + Assertions.fail("Should not throw EmptyHeapException while heap is not empty"); + } + } + + assertEquals(0, heap.size()); + assertTrue(heap.isEmpty()); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/heaps/MedianFinderTest.java b/src/test/java/com/thealgorithms/datastructures/heaps/MedianFinderTest.java new file mode 100644 index 000000000000..fcacb21fcfc3 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/heaps/MedianFinderTest.java @@ -0,0 +1,17 @@ +package com.thealgorithms.datastructures.heaps; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class MedianFinderTest { + @Test + public void testMedianMaintenance() { + MedianFinder mf = new MedianFinder(); + mf.addNum(1); + mf.addNum(2); + assertEquals(1.5, mf.findMedian()); + mf.addNum(3); + assertEquals(2.0, mf.findMedian()); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/heaps/MergeKSortedArraysTest.java b/src/test/java/com/thealgorithms/datastructures/heaps/MergeKSortedArraysTest.java new file mode 100644 index 000000000000..c9b754619286 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/heaps/MergeKSortedArraysTest.java @@ -0,0 +1,41 @@ +package com.thealgorithms.datastructures.heaps; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class MergeKSortedArraysTest { + + @ParameterizedTest + @MethodSource("provideTestCases") + public void testMergeKArrays(int[][] arrays, int[] expected) { + assertArrayEquals(expected, MergeKSortedArrays.mergeKArrays(arrays)); + } + + private static Stream<Arguments> provideTestCases() { + return Stream.of( + // Basic test case with multiple arrays + Arguments.of(new int[][] {{1, 4, 5}, {1, 3, 4}, {2, 6}}, new int[] {1, 1, 2, 3, 4, 4, 5, 6}), + + // Edge case: All arrays are empty + Arguments.of(new int[][] {{}, {}, {}}, new int[] {}), + + // Edge case: One array is empty + Arguments.of(new int[][] {{1, 3, 5}, {}, {2, 4, 6}}, new int[] {1, 2, 3, 4, 5, 6}), + + // Single array + Arguments.of(new int[][] {{1, 2, 3}}, new int[] {1, 2, 3}), + + // Arrays with negative numbers + Arguments.of(new int[][] {{-5, 1, 3}, {-10, 0, 2}}, new int[] {-10, -5, 0, 1, 2, 3}), + + // Arrays with duplicate elements + Arguments.of(new int[][] {{1, 1, 2}, {1, 3, 3}, {2, 2, 4}}, new int[] {1, 1, 1, 2, 2, 2, 3, 3, 4}), + + // Edge case: Arrays of varying lengths + Arguments.of(new int[][] {{1, 2}, {3}, {4, 5, 6, 7}}, new int[] {1, 2, 3, 4, 5, 6, 7})); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/heaps/MinHeapTest.java b/src/test/java/com/thealgorithms/datastructures/heaps/MinHeapTest.java new file mode 100644 index 000000000000..1c2caf54cdb1 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/heaps/MinHeapTest.java @@ -0,0 +1,141 @@ +package com.thealgorithms.datastructures.heaps; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class MinHeapTest { + + private MinHeap heap; + + @BeforeEach + void setUp() { + // Create a fresh heap for each test + List<HeapElement> elements = Arrays.asList(new HeapElement(5.0, "Five"), new HeapElement(2.0, "Two"), new HeapElement(8.0, "Eight"), new HeapElement(1.0, "One"), new HeapElement(9.0, "Nine")); + heap = new MinHeap(elements); + } + + @Test + void testConstructorWithNullList() { + assertThrows(IllegalArgumentException.class, () -> new MinHeap(null)); + } + + @Test + void testConstructorWithEmptyList() { + MinHeap emptyHeap = new MinHeap(new ArrayList<>()); + assertTrue(emptyHeap.isEmpty()); + } + + @Test + void testConstructorWithNullElements() { + List<HeapElement> elements = Arrays.asList(new HeapElement(1.0, "One"), null, new HeapElement(2.0, "Two")); + MinHeap heap = new MinHeap(elements); + assertEquals(2, heap.size()); + } + + @Test + void testInsertElement() { + heap.insertElement(new HeapElement(0.5, "Half")); + assertEquals(0.5, heap.getElement(1).getKey()); + assertEquals(6, heap.size()); + } + + @Test + void testInsertNullElement() { + assertThrows(IllegalArgumentException.class, () -> heap.insertElement(null)); + } + + @Test + void testGetElementAtIndex() { + HeapElement element = heap.getElement(1); + assertEquals(1.0, element.getKey()); + assertEquals("One", element.getValue()); + } + + @Test + void testGetElementAtInvalidIndex() { + assertThrows(IndexOutOfBoundsException.class, () -> heap.getElement(0)); + assertThrows(IndexOutOfBoundsException.class, () -> heap.getElement(10)); + } + + @Test + void testDeleteElement() throws EmptyHeapException { + heap.deleteElement(1); + assertEquals(2.0, heap.getElement(1).getKey()); + assertEquals(4, heap.size()); + } + + @Test + void testDeleteElementAtInvalidIndex() { + assertThrows(IndexOutOfBoundsException.class, () -> heap.deleteElement(0)); + assertThrows(IndexOutOfBoundsException.class, () -> heap.deleteElement(10)); + } + + @Test + void testDeleteFromEmptyHeap() { + MinHeap emptyHeap = new MinHeap(new ArrayList<>()); + assertThrows(EmptyHeapException.class, () -> emptyHeap.deleteElement(1)); + } + + @Test + void testExtractMin() throws EmptyHeapException { + HeapElement min = heap.getElement(); + assertEquals(1.0, min.getKey()); + assertEquals("One", min.getValue()); + assertEquals(4, heap.size()); + + min = heap.getElement(); + assertEquals(2.0, min.getKey()); + assertEquals(3, heap.size()); + } + + @Test + void testExtractMinFromEmptyHeap() { + MinHeap emptyHeap = new MinHeap(new ArrayList<>()); + assertThrows(EmptyHeapException.class, () -> emptyHeap.getElement()); + } + + @Test + void testHeapOrder() { + // Test that parent is always smaller than or equal to children + for (int i = 1; i <= heap.size() / 2; i++) { + double parentKey = heap.getElement(i).getKey(); + + // Check left child + if (2 * i <= heap.size()) { + assertTrue(parentKey <= heap.getElement(2 * i).getKey()); + } + + // Check right child + if (2 * i + 1 <= heap.size()) { + assertTrue(parentKey <= heap.getElement(2 * i + 1).getKey()); + } + } + } + + @Test + void testSizeAndEmpty() { + assertEquals(5, heap.size()); + assertFalse(heap.isEmpty()); + + // Remove all elements + while (!heap.isEmpty()) { + try { + heap.getElement(); + } catch (EmptyHeapException e) { + Assertions.fail("Should not throw EmptyHeapException while heap is not empty"); + } + } + + assertEquals(0, heap.size()); + assertTrue(heap.isEmpty()); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/heaps/MinPriorityQueueTest.java b/src/test/java/com/thealgorithms/datastructures/heaps/MinPriorityQueueTest.java new file mode 100644 index 000000000000..8f93bd630aa7 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/heaps/MinPriorityQueueTest.java @@ -0,0 +1,87 @@ +package com.thealgorithms.datastructures.heaps; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class MinPriorityQueueTest { + + @Test + void testInsertAndPeek() { + MinPriorityQueue queue = new MinPriorityQueue(5); + queue.insert(10); + queue.insert(5); + queue.insert(15); + + Assertions.assertEquals(5, queue.peek(), "The minimum element should be 5."); + } + + @Test + void testDelete() { + MinPriorityQueue queue = new MinPriorityQueue(5); + queue.insert(10); + queue.insert(5); + queue.insert(15); + + Assertions.assertEquals(5, queue.delete(), "The deleted minimum element should be 5."); + Assertions.assertEquals(10, queue.peek(), "After deletion, the new minimum should be 10."); + } + + @Test + void testIsEmpty() { + MinPriorityQueue queue = new MinPriorityQueue(5); + Assertions.assertTrue(queue.isEmpty(), "The queue should be empty initially."); + + queue.insert(10); + Assertions.assertFalse(queue.isEmpty(), "The queue should not be empty after insertion."); + } + + @Test + void testIsFull() { + MinPriorityQueue queue = new MinPriorityQueue(2); + queue.insert(10); + queue.insert(5); + + Assertions.assertTrue(queue.isFull(), "The queue should be full after inserting two elements."); + queue.delete(); + Assertions.assertFalse(queue.isFull(), "The queue should not be full after deletion."); + } + + @Test + void testHeapSort() { + MinPriorityQueue queue = new MinPriorityQueue(5); + queue.insert(10); + queue.insert(5); + queue.insert(15); + queue.insert(1); + queue.insert(3); + + // Delete all elements to sort the queue + int[] sortedArray = new int[5]; + for (int i = 0; i < 5; i++) { + sortedArray[i] = queue.delete(); + } + + Assertions.assertArrayEquals(new int[] {1, 3, 5, 10, 15}, sortedArray, "The array should be sorted in ascending order."); + } + + @Test + void testPeekEmptyQueue() { + MinPriorityQueue queue = new MinPriorityQueue(5); + Assertions.assertThrows(IllegalStateException.class, queue::peek, "Should throw an exception when peeking into an empty queue."); + } + + @Test + void testDeleteEmptyQueue() { + MinPriorityQueue queue = new MinPriorityQueue(5); + Assertions.assertThrows(IllegalStateException.class, queue::delete, "Should throw an exception when deleting from an empty queue."); + } + + @Test + void testInsertWhenFull() { + MinPriorityQueue queue = new MinPriorityQueue(2); + queue.insert(10); + queue.insert(5); + + Assertions.assertThrows(IllegalStateException.class, () -> queue.insert(15), "Should throw an exception when inserting into a full queue."); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/lists/CircleLinkedListTest.java b/src/test/java/com/thealgorithms/datastructures/lists/CircleLinkedListTest.java new file mode 100644 index 000000000000..883d2b02ba7c --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/lists/CircleLinkedListTest.java @@ -0,0 +1,115 @@ +package com.thealgorithms.datastructures.lists; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class CircleLinkedListTest { + + private CircleLinkedList<Integer> list; + + @BeforeEach + public void setUp() { + list = new CircleLinkedList<>(); + } + + @Test + public void testInitialSize() { + assertEquals(0, list.getSize(), "Initial size should be 0."); + } + + @Test + public void testAppendAndSize() { + list.append(1); + list.append(2); + list.append(3); + + assertEquals(3, list.getSize(), "Size after three appends should be 3."); + assertEquals("[ 1, 2, 3 ]", list.toString(), "List content should match appended values."); + } + + @Test + public void testRemove() { + list.append(1); + list.append(2); + list.append(3); + list.append(4); + + assertEquals(2, list.remove(1), "Removed element at index 1 should be 2."); + assertEquals(3, list.remove(1), "Removed element at index 1 after update should be 3."); + assertEquals("[ 1, 4 ]", list.toString(), "List content should reflect removals."); + assertEquals(2, list.getSize(), "Size after two removals should be 2."); + } + + @Test + public void testRemoveInvalidIndex() { + list.append(1); + list.append(2); + + assertThrows(IndexOutOfBoundsException.class, () -> list.remove(2), "Should throw on out-of-bounds index."); + assertThrows(IndexOutOfBoundsException.class, () -> list.remove(-1), "Should throw on negative index."); + } + + @Test + public void testToStringEmpty() { + assertEquals("[]", list.toString(), "Empty list should be represented by '[]'."); + } + + @Test + public void testToStringAfterRemoval() { + list.append(1); + list.append(2); + list.append(3); + list.remove(1); + + assertEquals("[ 1, 3 ]", list.toString(), "List content should match remaining elements after removal."); + } + + @Test + public void testSingleElement() { + list.append(1); + + assertEquals(1, list.getSize(), "Size after single append should be 1."); + assertEquals("[ 1 ]", list.toString(), "Single element list should display properly."); + assertEquals(1, list.remove(0), "Single element removed should match appended value."); + assertEquals("[]", list.toString(), "List should be empty after removing the single element."); + } + + @Test + public void testNullElement() { + assertThrows(NullPointerException.class, () -> list.append(null), "Appending null should throw exception."); + } + + @Test + public void testCircularReference() { + list.append(1); + list.append(2); + list.append(3); + CircleLinkedList.Node<Integer> current = list.head; + + // Traverse one full cycle and verify the circular reference + for (int i = 0; i <= list.getSize(); i++) { + current = current.next; + } + assertEquals(list.head, current, "End of list should point back to the head (circular structure)."); + } + + @Test + public void testClear() { + list.append(1); + list.append(2); + list.append(3); + + // Remove all elements to simulate clearing the list + for (int i = list.getSize() - 1; i >= 0; i--) { + list.remove(i); + } + + assertEquals(0, list.getSize(), "Size after clearing should be 0."); + assertEquals("[]", list.toString(), "Empty list should be represented by '[]' after clear."); + assertSame(list.head.next, list.head, "Head's next should point to itself after clearing."); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/lists/CountSinglyLinkedListRecursionTest.java b/src/test/java/com/thealgorithms/datastructures/lists/CountSinglyLinkedListRecursionTest.java new file mode 100644 index 000000000000..1a3efe8a5572 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/lists/CountSinglyLinkedListRecursionTest.java @@ -0,0 +1,49 @@ +package com.thealgorithms.datastructures.lists; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class CountSinglyLinkedListRecursionTest { + + private CountSinglyLinkedListRecursion list; + + @BeforeEach + public void setUp() { + list = new CountSinglyLinkedListRecursion(); + } + + @Test + public void testCountEmptyList() { + // An empty list should have a count of 0 + assertEquals(0, list.count(), "Count of an empty list should be 0."); + } + + @Test + public void testCountSingleElementList() { + // Insert a single element and check the count + list.insert(1); + assertEquals(1, list.count(), "Count of a single-element list should be 1."); + } + + @Test + public void testCountMultipleElements() { + // Insert multiple elements and check the count + for (int i = 1; i <= 5; i++) { + list.insert(i); + } + assertEquals(5, list.count(), "Count of a list with 5 elements should be 5."); + } + + @Test + public void testCountWithDuplicateElements() { + // Insert duplicate elements and verify the count is correct + list.insert(1); + list.insert(2); + list.insert(2); + list.insert(3); + list.insert(3); + assertEquals(5, list.count(), "Count of a list with duplicate elements should match total node count."); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/lists/CreateAndDetectLoopTest.java b/src/test/java/com/thealgorithms/datastructures/lists/CreateAndDetectLoopTest.java new file mode 100644 index 000000000000..2e1012f2b79b --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/lists/CreateAndDetectLoopTest.java @@ -0,0 +1,86 @@ +package com.thealgorithms.datastructures.lists; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class CreateAndDetectLoopTest { + + private CreateAndDetectLoop.Node head; + + @BeforeEach + void setUp() { + // Set up a linked list: 1 -> 2 -> 3 -> 4 -> 5 -> 6 + head = new CreateAndDetectLoop.Node(1); + CreateAndDetectLoop.Node second = new CreateAndDetectLoop.Node(2); + CreateAndDetectLoop.Node third = new CreateAndDetectLoop.Node(3); + CreateAndDetectLoop.Node fourth = new CreateAndDetectLoop.Node(4); + CreateAndDetectLoop.Node fifth = new CreateAndDetectLoop.Node(5); + CreateAndDetectLoop.Node sixth = new CreateAndDetectLoop.Node(6); + + head.next = second; + second.next = third; + third.next = fourth; + fourth.next = fifth; + fifth.next = sixth; + } + + @Test + void testDetectLoopNoLoop() { + // Test when no loop exists + assertFalse(CreateAndDetectLoop.detectLoop(head), "There should be no loop."); + } + + @Test + void testCreateAndDetectLoopLoopExists() { + // Create a loop between position 2 (node with value 2) and position 5 (node with value 5) + CreateAndDetectLoop.createLoop(head, 2, 5); + + // Now test if the loop is detected + assertTrue(CreateAndDetectLoop.detectLoop(head), "A loop should be detected."); + } + + @Test + void testCreateLoopInvalidPosition() { + // Create loop with invalid positions (0) + CreateAndDetectLoop.createLoop(head, 0, 0); + + // Ensure no loop was created + assertFalse(CreateAndDetectLoop.detectLoop(head), "There should be no loop with invalid positions."); + } + + @Test + void testCreateLoopSelfLoop() { + // Create a self-loop at position 3 (node with value 3) + CreateAndDetectLoop.createLoop(head, 3, 3); + + // Test if the self-loop is detected + assertTrue(CreateAndDetectLoop.detectLoop(head), "A self-loop should be detected."); + } + + @Test + void testCreateLoopNoChangeForNonExistentPositions() { + // Create a loop with non-existent positions + CreateAndDetectLoop.createLoop(head, 10, 20); + + // Ensure no loop was created + assertFalse(CreateAndDetectLoop.detectLoop(head), "No loop should be created if positions are out of bounds."); + } + + @Test + void testMultipleNodesWithNoLoop() { + // Multiple nodes without creating any loop + assertFalse(CreateAndDetectLoop.detectLoop(head), "No loop should be detected for a standard linear list."); + } + + @Test + void testHeadToTailLoop() { + // Create a loop from the tail back to the head + CreateAndDetectLoop.createLoop(head, 1, 6); + + // Detect the head-to-tail loop + assertTrue(CreateAndDetectLoop.detectLoop(head), "A head-to-tail loop should be detected."); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/lists/CursorLinkedListTest.java b/src/test/java/com/thealgorithms/datastructures/lists/CursorLinkedListTest.java new file mode 100644 index 000000000000..bf5501826994 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/lists/CursorLinkedListTest.java @@ -0,0 +1,112 @@ +package com.thealgorithms.datastructures.lists; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class CursorLinkedListTest { + private CursorLinkedList<String> list; + + @BeforeEach + void setUp() { + list = new CursorLinkedList<>(); + } + + @Test + void testAppendAndGet() { + list.append("First"); + list.append("Second"); + list.append("Third"); + + assertEquals("First", list.get(0)); + assertEquals("Second", list.get(1)); + assertEquals("Third", list.get(2)); + assertNull(list.get(3)); + assertNull(list.get(-1)); + } + + @Test + void testIndexOf() { + list.append("First"); + list.append("Second"); + list.append("Third"); + + assertEquals(0, list.indexOf("First")); + assertEquals(1, list.indexOf("Second")); + assertEquals(2, list.indexOf("Third")); + assertEquals(-1, list.indexOf("NonExistent")); + } + + @Test + void testRemove() { + list.append("First"); + list.append("Second"); + list.append("Third"); + + list.remove("Second"); + assertEquals("First", list.get(0)); + assertEquals("Third", list.get(1)); + assertNull(list.get(2)); + assertEquals(-1, list.indexOf("Second")); + } + + @Test + void testRemoveByIndex() { + list.append("First"); + list.append("Second"); + list.append("Third"); + + list.removeByIndex(1); + assertEquals("First", list.get(0)); + assertEquals("Third", list.get(1)); + assertNull(list.get(2)); + } + + @Test + void testRemoveFirstElement() { + list.append("First"); + list.append("Second"); + + list.remove("First"); + assertEquals("Second", list.get(0)); + assertNull(list.get(1)); + assertEquals(-1, list.indexOf("First")); + } + + @Test + void testRemoveLastElement() { + list.append("First"); + list.append("Second"); + + list.remove("Second"); + assertEquals("First", list.get(0)); + assertNull(list.get(1)); + assertEquals(-1, list.indexOf("Second")); + } + + @Test + void testNullHandling() { + assertThrows(NullPointerException.class, () -> list.append(null)); + assertThrows(NullPointerException.class, () -> list.remove(null)); + assertThrows(NullPointerException.class, () -> list.indexOf(null)); + } + + @Test + void testEmptyList() { + assertNull(list.get(0)); + assertEquals(-1, list.indexOf("Any")); + } + + @Test + void testMemoryLimitExceeded() { + // Test adding more elements than CURSOR_SPACE_SIZE + assertThrows(OutOfMemoryError.class, () -> { + for (int i = 0; i < 101; i++) { // CURSOR_SPACE_SIZE is 100 + list.append("Element" + i); + } + }); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/lists/MergeKSortedLinkedListTest.java b/src/test/java/com/thealgorithms/datastructures/lists/MergeKSortedLinkedListTest.java new file mode 100644 index 000000000000..99a890112d31 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/lists/MergeKSortedLinkedListTest.java @@ -0,0 +1,93 @@ +package com.thealgorithms.datastructures.lists; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import com.thealgorithms.datastructures.lists.MergeKSortedLinkedList.Node; +import java.util.Arrays; +import org.junit.jupiter.api.Test; + +class MergeKSortedLinkedListTest { + + @Test + void testMergeKLists() { + Node list1 = new Node(1, new Node(4, new Node(5))); + Node list2 = new Node(1, new Node(3, new Node(4))); + Node list3 = new Node(2, new Node(6)); + Node[] lists = {list1, list2, list3}; + + MergeKSortedLinkedList merger = new MergeKSortedLinkedList(); + Node mergedHead = merger.mergeKList(lists, lists.length); + + int[] expectedValues = {1, 1, 2, 3, 4, 4, 5, 6}; + int[] actualValues = getListValues(mergedHead); + assertArrayEquals(expectedValues, actualValues, "Merged list values do not match the expected sorted order."); + } + + @Test + void testMergeEmptyLists() { + Node[] lists = {null, null, null}; + + MergeKSortedLinkedList merger = new MergeKSortedLinkedList(); + Node mergedHead = merger.mergeKList(lists, lists.length); + + assertNull(mergedHead, "Merged list should be null when all input lists are empty."); + } + + @Test + void testMergeSingleList() { + Node list1 = new Node(1, new Node(3, new Node(5))); + Node[] lists = {list1}; + + MergeKSortedLinkedList merger = new MergeKSortedLinkedList(); + Node mergedHead = merger.mergeKList(lists, lists.length); + + int[] expectedValues = {1, 3, 5}; + int[] actualValues = getListValues(mergedHead); + assertArrayEquals(expectedValues, actualValues, "Merged list should match the single input list when only one list is provided."); + } + + @Test + void testMergeListsOfDifferentLengths() { + Node list1 = new Node(1, new Node(3, new Node(5))); + Node list2 = new Node(2, new Node(4)); + Node list3 = new Node(6); + Node[] lists = {list1, list2, list3}; + + MergeKSortedLinkedList merger = new MergeKSortedLinkedList(); + Node mergedHead = merger.mergeKList(lists, lists.length); + + int[] expectedValues = {1, 2, 3, 4, 5, 6}; + int[] actualValues = getListValues(mergedHead); + assertArrayEquals(expectedValues, actualValues, "Merged list values do not match expected sorted order for lists of different lengths."); + } + + @Test + void testMergeSingleElementLists() { + Node list1 = new Node(1); + Node list2 = new Node(3); + Node list3 = new Node(2); + Node[] lists = {list1, list2, list3}; + + MergeKSortedLinkedList merger = new MergeKSortedLinkedList(); + Node mergedHead = merger.mergeKList(lists, lists.length); + + int[] expectedValues = {1, 2, 3}; + int[] actualValues = getListValues(mergedHead); + assertArrayEquals(expectedValues, actualValues, "Merged list values do not match expected sorted order for single-element lists."); + } + + /** + * Helper method to extract values from the linked list into an array for assertion. + */ + private int[] getListValues(Node head) { + int[] values = new int[100]; // assuming max length for simplicity + int i = 0; + Node curr = head; + while (curr != null) { + values[i++] = curr.data; + curr = curr.next; + } + return Arrays.copyOf(values, i); // return only filled part + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/lists/MergeSortedArrayListTest.java b/src/test/java/com/thealgorithms/datastructures/lists/MergeSortedArrayListTest.java new file mode 100644 index 000000000000..5483bbcd0394 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/lists/MergeSortedArrayListTest.java @@ -0,0 +1,99 @@ +package com.thealgorithms.datastructures.lists; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +class MergeSortedArrayListTest { + + @Test + void testMergeTwoSortedLists() { + List<Integer> listA = Arrays.asList(1, 3, 5, 7, 9); + List<Integer> listB = Arrays.asList(2, 4, 6, 8, 10); + List<Integer> result = new ArrayList<>(); + + MergeSortedArrayList.merge(listA, listB, result); + + List<Integer> expected = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + assertEquals(expected, result, "Merged list should be sorted and contain all elements from both input lists."); + } + + @Test + void testMergeWithEmptyList() { + List<Integer> listA = Arrays.asList(1, 2, 3); + List<Integer> listB = new ArrayList<>(); // Empty list + List<Integer> result = new ArrayList<>(); + + MergeSortedArrayList.merge(listA, listB, result); + + List<Integer> expected = Arrays.asList(1, 2, 3); + assertEquals(expected, result, "Merged list should match listA when listB is empty."); + } + + @Test + void testMergeWithBothEmptyLists() { + List<Integer> listA = new ArrayList<>(); // Empty list + List<Integer> listB = new ArrayList<>(); // Empty list + List<Integer> result = new ArrayList<>(); + + MergeSortedArrayList.merge(listA, listB, result); + + assertTrue(result.isEmpty(), "Merged list should be empty when both input lists are empty."); + } + + @Test + void testMergeWithDuplicateElements() { + List<Integer> listA = Arrays.asList(1, 2, 2, 3); + List<Integer> listB = Arrays.asList(2, 3, 4); + List<Integer> result = new ArrayList<>(); + + MergeSortedArrayList.merge(listA, listB, result); + + List<Integer> expected = Arrays.asList(1, 2, 2, 2, 3, 3, 4); + assertEquals(expected, result, "Merged list should correctly handle and include duplicate elements."); + } + + @Test + void testMergeWithNegativeAndPositiveNumbers() { + List<Integer> listA = Arrays.asList(-3, -1, 2); + List<Integer> listB = Arrays.asList(-2, 0, 3); + List<Integer> result = new ArrayList<>(); + + MergeSortedArrayList.merge(listA, listB, result); + + List<Integer> expected = Arrays.asList(-3, -2, -1, 0, 2, 3); + assertEquals(expected, result, "Merged list should correctly handle negative and positive numbers."); + } + + @Test + void testMergeThrowsExceptionOnNullInput() { + List<Integer> listA = null; + List<Integer> listB = Arrays.asList(1, 2, 3); + List<Integer> result = new ArrayList<>(); + + List<Integer> finalListB = listB; + List<Integer> finalListA = listA; + List<Integer> finalResult = result; + assertThrows(NullPointerException.class, () -> MergeSortedArrayList.merge(finalListA, finalListB, finalResult), "Should throw NullPointerException if any input list is null."); + + listA = Arrays.asList(1, 2, 3); + listB = null; + List<Integer> finalListA1 = listA; + List<Integer> finalListB1 = listB; + List<Integer> finalResult1 = result; + assertThrows(NullPointerException.class, () -> MergeSortedArrayList.merge(finalListA1, finalListB1, finalResult1), "Should throw NullPointerException if any input list is null."); + + listA = Arrays.asList(1, 2, 3); + listB = Arrays.asList(4, 5, 6); + result = null; + List<Integer> finalListA2 = listA; + List<Integer> finalListB2 = listB; + List<Integer> finalResult2 = result; + assertThrows(NullPointerException.class, () -> MergeSortedArrayList.merge(finalListA2, finalListB2, finalResult2), "Should throw NullPointerException if the result collection is null."); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/lists/MergeSortedSinglyLinkedListTest.java b/src/test/java/com/thealgorithms/datastructures/lists/MergeSortedSinglyLinkedListTest.java new file mode 100644 index 000000000000..b6b785f43711 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/lists/MergeSortedSinglyLinkedListTest.java @@ -0,0 +1,90 @@ +package com.thealgorithms.datastructures.lists; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class MergeSortedSinglyLinkedListTest { + + @Test + void testMergeTwoSortedLists() { + SinglyLinkedList listA = new SinglyLinkedList(); + SinglyLinkedList listB = new SinglyLinkedList(); + + for (int i = 2; i <= 10; i += 2) { + listA.insert(i); + listB.insert(i - 1); + } + + SinglyLinkedList mergedList = MergeSortedSinglyLinkedList.merge(listA, listB); + assertEquals("1->2->3->4->5->6->7->8->9->10", mergedList.toString(), "Merged list should contain all elements in sorted order."); + } + + @Test + void testMergeWithEmptyListA() { + SinglyLinkedList listA = new SinglyLinkedList(); // Empty listA + SinglyLinkedList listB = new SinglyLinkedList(); + listB.insert(1); + listB.insert(3); + listB.insert(5); + + SinglyLinkedList mergedList = MergeSortedSinglyLinkedList.merge(listA, listB); + assertEquals("1->3->5", mergedList.toString(), "Merged list should match listB when listA is empty."); + } + + @Test + void testMergeWithEmptyListB() { + SinglyLinkedList listA = new SinglyLinkedList(); + SinglyLinkedList listB = new SinglyLinkedList(); // Empty listB + listA.insert(2); + listA.insert(4); + listA.insert(6); + + SinglyLinkedList mergedList = MergeSortedSinglyLinkedList.merge(listA, listB); + assertEquals("2->4->6", mergedList.toString(), "Merged list should match listA when listB is empty."); + } + + @Test + void testMergeWithBothEmptyLists() { + SinglyLinkedList listA = new SinglyLinkedList(); // Empty listA + SinglyLinkedList listB = new SinglyLinkedList(); // Empty listB + + SinglyLinkedList mergedList = MergeSortedSinglyLinkedList.merge(listA, listB); + assertEquals("", mergedList.toString(), "Merged list should be empty when both input lists are empty."); + } + + @Test + void testMergeWithDuplicateValues() { + SinglyLinkedList listA = new SinglyLinkedList(); + SinglyLinkedList listB = new SinglyLinkedList(); + + listA.insert(1); + listA.insert(3); + listA.insert(5); + listB.insert(1); + listB.insert(4); + listB.insert(5); + + SinglyLinkedList mergedList = MergeSortedSinglyLinkedList.merge(listA, listB); + assertEquals("1->1->3->4->5->5", mergedList.toString(), "Merged list should include duplicate values in sorted order."); + } + + @Test + void testMergeThrowsExceptionOnNullInput() { + SinglyLinkedList listA = null; + SinglyLinkedList listB = new SinglyLinkedList(); + listB.insert(1); + listB.insert(2); + + SinglyLinkedList finalListA = listA; + SinglyLinkedList finalListB = listB; + assertThrows(NullPointerException.class, () -> MergeSortedSinglyLinkedList.merge(finalListA, finalListB), "Should throw NullPointerException if listA is null."); + + listA = new SinglyLinkedList(); + listB = null; + SinglyLinkedList finalListA1 = listA; + SinglyLinkedList finalListB1 = listB; + assertThrows(NullPointerException.class, () -> MergeSortedSinglyLinkedList.merge(finalListA1, finalListB1), "Should throw NullPointerException if listB is null."); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/lists/QuickSortLinkedListTest.java b/src/test/java/com/thealgorithms/datastructures/lists/QuickSortLinkedListTest.java new file mode 100644 index 000000000000..4e86f317c2e8 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/lists/QuickSortLinkedListTest.java @@ -0,0 +1,95 @@ +package com.thealgorithms.datastructures.lists; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.Test; + +/** + * Test cases for QuickSortLinkedList. + * Author: Prabhat-Kumar-42 + * GitHub: https://github.com/Prabhat-Kumar-42 + */ +public class QuickSortLinkedListTest { + + @Test + public void testSortEmptyList() { + SinglyLinkedList emptyList = new SinglyLinkedList(); + QuickSortLinkedList sorter = new QuickSortLinkedList(emptyList); + + sorter.sortList(); + assertNull(emptyList.getHead(), "Sorted empty list should have no elements."); + } + + @Test + public void testSortSingleNodeList() { + SinglyLinkedList singleNodeList = new SinglyLinkedList(); + singleNodeList.insert(5); + QuickSortLinkedList sorter = new QuickSortLinkedList(singleNodeList); + + sorter.sortList(); + assertEquals(5, singleNodeList.getHead().value, "Single node list should remain unchanged after sorting."); + assertNull(singleNodeList.getHead().next, "Single node should not have a next node."); + } + + @Test + public void testSortAlreadySorted() { + SinglyLinkedList sortedList = new SinglyLinkedList(); + sortedList.insert(1); + sortedList.insert(2); + sortedList.insert(3); + sortedList.insert(4); + sortedList.insert(5); + QuickSortLinkedList sorter = new QuickSortLinkedList(sortedList); + + sorter.sortList(); + assertEquals("1->2->3->4->5", sortedList.toString(), "Already sorted list should remain unchanged."); + } + + @Test + public void testSortReverseOrderedList() { + SinglyLinkedList reverseList = new SinglyLinkedList(); + reverseList.insert(5); + reverseList.insert(4); + reverseList.insert(3); + reverseList.insert(2); + reverseList.insert(1); + QuickSortLinkedList sorter = new QuickSortLinkedList(reverseList); + + sorter.sortList(); + assertEquals("1->2->3->4->5", reverseList.toString(), "Reverse ordered list should be sorted in ascending order."); + } + + @Test + public void testSortWithDuplicates() { + SinglyLinkedList listWithDuplicates = new SinglyLinkedList(); + listWithDuplicates.insert(3); + listWithDuplicates.insert(1); + listWithDuplicates.insert(3); + listWithDuplicates.insert(2); + listWithDuplicates.insert(2); + QuickSortLinkedList sorter = new QuickSortLinkedList(listWithDuplicates); + + sorter.sortList(); + assertEquals("1->2->2->3->3", listWithDuplicates.toString(), "List with duplicates should be sorted correctly."); + } + + @Test + public void testSortMultipleElementsList() { + SinglyLinkedList list = new SinglyLinkedList(); + list.insert(5); + list.insert(3); + list.insert(8); + list.insert(1); + list.insert(10); + list.insert(2); + list.insert(7); + list.insert(4); + list.insert(9); + list.insert(6); + QuickSortLinkedList sorter = new QuickSortLinkedList(list); + + sorter.sortList(); + assertEquals("1->2->3->4->5->6->7->8->9->10", list.toString(), "List should be sorted in ascending order."); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/lists/ReverseKGroupTest.java b/src/test/java/com/thealgorithms/datastructures/lists/ReverseKGroupTest.java new file mode 100644 index 000000000000..76b7ab063de4 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/lists/ReverseKGroupTest.java @@ -0,0 +1,72 @@ +package com.thealgorithms.datastructures.lists; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.Test; + +/** + * Test cases for Reverse K Group LinkedList + * Author: Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi) + */ +public class ReverseKGroupTest { + + @Test + public void testReverseKGroupWithEmptyList() { + ReverseKGroup reverser = new ReverseKGroup(); + assertNull(reverser.reverseKGroup(null, 3)); + } + + @Test + public void testReverseKGroupWithSingleNodeList() { + ReverseKGroup reverser = new ReverseKGroup(); + SinglyLinkedListNode singleNode = new SinglyLinkedListNode(5); + SinglyLinkedListNode result = reverser.reverseKGroup(singleNode, 2); + assertEquals(5, result.value); + assertNull(result.next); + } + + @Test + public void testReverseKGroupWithKEqualTo2() { + ReverseKGroup reverser = new ReverseKGroup(); + + // Create a list with multiple elements (1 -> 2 -> 3 -> 4 -> 5) + SinglyLinkedListNode head; + head = new SinglyLinkedListNode(1); + head.next = new SinglyLinkedListNode(2); + head.next.next = new SinglyLinkedListNode(3); + head.next.next.next = new SinglyLinkedListNode(4); + head.next.next.next.next = new SinglyLinkedListNode(5); + + // Test reverse with k=2 + SinglyLinkedListNode result1 = reverser.reverseKGroup(head, 2); + assertEquals(2, result1.value); + assertEquals(1, result1.next.value); + assertEquals(4, result1.next.next.value); + assertEquals(3, result1.next.next.next.value); + assertEquals(5, result1.next.next.next.next.value); + assertNull(result1.next.next.next.next.next); + } + + @Test + public void testReverseKGroupWithKEqualTo3() { + ReverseKGroup reverser = new ReverseKGroup(); + + // Create a list with multiple elements (1 -> 2 -> 3 -> 4 -> 5) + SinglyLinkedListNode head; + head = new SinglyLinkedListNode(1); + head.next = new SinglyLinkedListNode(2); + head.next.next = new SinglyLinkedListNode(3); + head.next.next.next = new SinglyLinkedListNode(4); + head.next.next.next.next = new SinglyLinkedListNode(5); + + // Test reverse with k=3 + SinglyLinkedListNode result = reverser.reverseKGroup(head, 3); + assertEquals(3, result.value); + assertEquals(2, result.next.value); + assertEquals(1, result.next.next.value); + assertEquals(4, result.next.next.next.value); + assertEquals(5, result.next.next.next.next.value); + assertNull(result.next.next.next.next.next); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/lists/RotateSinglyLinkedListsTest.java b/src/test/java/com/thealgorithms/datastructures/lists/RotateSinglyLinkedListsTest.java new file mode 100644 index 000000000000..c476ad1b0203 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/lists/RotateSinglyLinkedListsTest.java @@ -0,0 +1,105 @@ +package com.thealgorithms.datastructures.lists; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.Test; + +/** + * Test cases for RotateSinglyLinkedLists. + * Author: Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi) + */ +public class RotateSinglyLinkedListsTest { + + private final RotateSinglyLinkedLists rotator = new RotateSinglyLinkedLists(); + + // Helper method to create a linked list from an array of values + private SinglyLinkedListNode createLinkedList(int[] values) { + if (values.length == 0) { + return null; + } + + SinglyLinkedListNode head = new SinglyLinkedListNode(values[0]); + SinglyLinkedListNode current = head; + for (int i = 1; i < values.length; i++) { + current.next = new SinglyLinkedListNode(values[i]); + current = current.next; + } + return head; + } + + // Helper method to convert a linked list to a string for easy comparison + private String linkedListToString(SinglyLinkedListNode head) { + StringBuilder sb = new StringBuilder(); + SinglyLinkedListNode current = head; + while (current != null) { + sb.append(current.value); + if (current.next != null) { + sb.append(" -> "); + } + current = current.next; + } + return sb.toString(); + } + + @Test + public void testRotateRightEmptyList() { + // Rotate an empty list + assertNull(rotator.rotateRight(null, 2)); + } + + @Test + public void testRotateRightSingleNodeList() { + // Rotate a list with a single element + SinglyLinkedListNode singleNode = new SinglyLinkedListNode(5); + SinglyLinkedListNode rotatedSingleNode = rotator.rotateRight(singleNode, 3); + assertEquals("5", linkedListToString(rotatedSingleNode)); + } + + @Test + public void testRotateRightMultipleElementsList() { + // Rotate a list with multiple elements (rotate by 2) + SinglyLinkedListNode head = createLinkedList(new int[] {1, 2, 3, 4, 5}); + SinglyLinkedListNode rotated = rotator.rotateRight(head, 2); + assertEquals("4 -> 5 -> 1 -> 2 -> 3", linkedListToString(rotated)); + } + + @Test + public void testRotateRightFullRotation() { + // Rotate by more than the length of the list + SinglyLinkedListNode head = createLinkedList(new int[] {1, 2, 3, 4, 5}); + SinglyLinkedListNode rotated = rotator.rotateRight(head, 7); + assertEquals("4 -> 5 -> 1 -> 2 -> 3", linkedListToString(rotated)); + } + + @Test + public void testRotateRightZeroRotation() { + // Rotate a list by k = 0 (no rotation) + SinglyLinkedListNode head = createLinkedList(new int[] {1, 2, 3, 4, 5}); + SinglyLinkedListNode rotated = rotator.rotateRight(head, 0); + assertEquals("1 -> 2 -> 3 -> 4 -> 5", linkedListToString(rotated)); + } + + @Test + public void testRotateRightByListLength() { + // Rotate a list by k equal to list length (no change) + SinglyLinkedListNode head = createLinkedList(new int[] {1, 2, 3, 4, 5}); + SinglyLinkedListNode rotated = rotator.rotateRight(head, 5); + assertEquals("1 -> 2 -> 3 -> 4 -> 5", linkedListToString(rotated)); + } + + @Test + public void testRotateRightByMultipleOfListLength() { + SinglyLinkedListNode head = createLinkedList(new int[] {1, 2, 3, 4, 5}); + SinglyLinkedListNode rotated = rotator.rotateRight(head, 10); // k = 2 * list length + assertEquals("1 -> 2 -> 3 -> 4 -> 5", linkedListToString(rotated)); + } + + @Test + public void testRotateRightLongerList() { + // Rotate a longer list by a smaller k + SinglyLinkedListNode head = createLinkedList(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9}); + SinglyLinkedListNode rotated = rotator.rotateRight(head, 4); + assertEquals("6 -> 7 -> 8 -> 9 -> 1 -> 2 -> 3 -> 4 -> 5", linkedListToString(rotated)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/lists/SearchSinglyLinkedListRecursionTest.java b/src/test/java/com/thealgorithms/datastructures/lists/SearchSinglyLinkedListRecursionTest.java new file mode 100644 index 000000000000..76b905841c18 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/lists/SearchSinglyLinkedListRecursionTest.java @@ -0,0 +1,89 @@ +package com.thealgorithms.datastructures.lists; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class SearchSinglyLinkedListRecursionTest { + + private SearchSinglyLinkedListRecursion list; + + @BeforeEach + public void setUp() { + list = new SearchSinglyLinkedListRecursion(); + } + + @Test + public void testSearchInEmptyList() { + // Test searching for a value in an empty list (should return false) + assertFalse(list.search(1)); + } + + @Test + public void testSearchSingleElementListFound() { + // Insert a single element and search for it + list.insert(5); + assertTrue(list.search(5)); + } + + @Test + public void testSearchSingleElementListNotFound() { + // Insert a single element and search for a non-existent value + list.insert(5); + assertFalse(list.search(10)); + } + + @Test + public void testSearchMultipleElementsListFound() { + // Insert multiple elements and search for a middle value + for (int i = 1; i <= 10; i++) { + list.insert(i); + } + assertTrue(list.search(5)); + } + + @Test + public void testSearchMultipleElementsListFirstElement() { + // Insert multiple elements and search for the first element + for (int i = 1; i <= 10; i++) { + list.insert(i); + } + assertTrue(list.search(1)); + } + + @Test + public void testSearchMultipleElementsListLastElement() { + // Insert multiple elements and search for the last element + for (int i = 1; i <= 10; i++) { + list.insert(i); + } + assertTrue(list.search(10)); + } + + @Test + public void testSearchMultipleElementsListNotFound() { + // Insert multiple elements and search for a non-existent element + for (int i = 1; i <= 10; i++) { + list.insert(i); + } + assertFalse(list.search(15)); + } + + @Test + public void testSearchNegativeValues() { + // Insert positive and negative values and search for a negative value + list.insert(-5); + list.insert(-10); + list.insert(5); + assertTrue(list.search(-10)); + assertFalse(list.search(-3)); + } + + @Test + public void testSearchZeroValue() { + list.insert(0); + assertTrue(list.search(0)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/lists/SinglyLinkedListTest.java b/src/test/java/com/thealgorithms/datastructures/lists/SinglyLinkedListTest.java new file mode 100644 index 000000000000..f80c6b5055f0 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/lists/SinglyLinkedListTest.java @@ -0,0 +1,260 @@ +package com.thealgorithms.datastructures.lists; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class SinglyLinkedListTest { + + /** + * Initialize a list with natural order values with pre-defined length + * @param length + * @return linked list with pre-defined number of nodes + */ + private SinglyLinkedList createSampleList(int length) { + List<SinglyLinkedListNode> nodeList = new ArrayList<>(); + for (int i = 1; i <= length; i++) { + SinglyLinkedListNode node = new SinglyLinkedListNode(i); + nodeList.add(node); + } + + for (int i = 0; i < length - 1; i++) { + nodeList.get(i).next = nodeList.get(i + 1); + } + + return new SinglyLinkedList(nodeList.get(0), length); + } + + @Test + void detectLoop() { + // List has cycle + SinglyLinkedListNode firstNode = new SinglyLinkedListNode(1); + SinglyLinkedListNode secondNode = new SinglyLinkedListNode(2); + SinglyLinkedListNode thirdNode = new SinglyLinkedListNode(3); + SinglyLinkedListNode fourthNode = new SinglyLinkedListNode(4); + + firstNode.next = secondNode; + secondNode.next = thirdNode; + thirdNode.next = fourthNode; + fourthNode.next = firstNode; + + SinglyLinkedList listHasLoop = new SinglyLinkedList(firstNode, 4); + assertTrue(listHasLoop.detectLoop()); + + SinglyLinkedList listHasNoLoop = createSampleList(5); + assertFalse(listHasNoLoop.detectLoop()); + } + + @Test + void middle() { + int oddNumberOfNode = 7; + SinglyLinkedList list = createSampleList(oddNumberOfNode); + assertEquals(oddNumberOfNode / 2 + 1, list.middle().value); + int evenNumberOfNode = 8; + list = createSampleList(evenNumberOfNode); + assertEquals(evenNumberOfNode / 2, list.middle().value); + + // return null if empty + list = new SinglyLinkedList(); + assertNull(list.middle()); + + // return head if there is only one node + list = createSampleList(1); + assertEquals(list.getHead(), list.middle()); + } + + @Test + void swap() { + SinglyLinkedList list = createSampleList(5); + assertEquals(1, list.getHead().value); + assertEquals(5, list.getNth(4)); + list.swapNodes(1, 5); + assertEquals(5, list.getHead().value); + assertEquals(1, list.getNth(4)); + } + + @Test + void clear() { + SinglyLinkedList list = createSampleList(5); + assertEquals(5, list.size()); + list.clear(); + assertEquals(0, list.size()); + assertTrue(list.isEmpty()); + } + + @Test + void search() { + SinglyLinkedList list = createSampleList(10); + assertTrue(list.search(5)); + assertFalse(list.search(20)); + } + + @Test + void deleteNth() { + SinglyLinkedList list = createSampleList(10); + assertTrue(list.search(7)); + list.deleteNth(6); // Index 6 has value 7 + assertFalse(list.search(7)); + } + // Test to check whether the method reverseList() works fine + @Test + void reverseList() { + + // Creating a new LinkedList of size:4 + // The linkedlist will be 1->2->3->4->null + SinglyLinkedList list = createSampleList(4); + + // Reversing the LinkedList using reverseList() method and storing the head of the reversed + // linkedlist in a head node The reversed linkedlist will be 4->3->2->1->null + SinglyLinkedListNode head = list.reverseListIter(list.getHead()); + + // Recording the Nodes after reversing the LinkedList + SinglyLinkedListNode firstNode = head; // 4 + SinglyLinkedListNode secondNode = firstNode.next; // 3 + SinglyLinkedListNode thirdNode = secondNode.next; // 2 + SinglyLinkedListNode fourthNode = thirdNode.next; // 1 + + // Checking whether the LinkedList is reversed or not by comparing the original list and + // reversed list nodes + assertEquals(1, fourthNode.value); + assertEquals(2, thirdNode.value); + assertEquals(3, secondNode.value); + assertEquals(4, firstNode.value); + } + + // Test to check whether implemented reverseList() method handles NullPointer Exception for + // TestCase where head==null + @Test + void reverseListNullPointer() { + // Creating a linkedlist with first node assigned to null + SinglyLinkedList list = new SinglyLinkedList(); + SinglyLinkedListNode first = list.getHead(); + + // Reversing the linkedlist + SinglyLinkedListNode head = list.reverseListIter(first); + + // checking whether the method works fine if the input is null + assertEquals(head, first); + } + + // Testing reverseList() method for a linkedlist of size: 20 + @Test + void reverseListTest() { + // Creating a new linkedlist + SinglyLinkedList list = createSampleList(20); + + // Reversing the LinkedList using reverseList() method and storing the head of the reversed + // linkedlist in a head node + SinglyLinkedListNode head = list.reverseListIter(list.getHead()); + + // Storing the head in a temp variable, so that we cannot loose the track of head + SinglyLinkedListNode temp = head; + + int i = 20; // This is for the comparison of values of nodes of the reversed linkedlist + // Checking whether the reverseList() method performed its task + while (temp != null && i > 0) { + assertEquals(i, temp.value); + temp = temp.next; + i--; + } + } + // This is Recursive Reverse List Test + // Test to check whether the method reverseListRec() works fine + void recursiveReverseList() { + // Create a linked list: 1 -> 2 -> 3 -> 4 -> 5 + SinglyLinkedList list = createSampleList(5); + + // Reversing the linked list using reverseList() method + SinglyLinkedListNode head = list.reverseListRec(list.getHead()); + + // Check if the reversed list is: 5 -> 4 -> 3 -> 2 -> 1 + assertEquals(5, head.value); + assertEquals(4, head.next.value); + assertEquals(3, head.next.next.value); + assertEquals(2, head.next.next.next.value); + assertEquals(1, head.next.next.next.next.value); + } + + @Test + void recursiveReverseListNullPointer() { + // Create an empty linked list + SinglyLinkedList list = new SinglyLinkedList(); + SinglyLinkedListNode first = list.getHead(); + + // Reversing the empty linked list + SinglyLinkedListNode head = list.reverseListRec(first); + + // Check if the head remains the same (null) + assertNull(head); + } + + @Test + void recursiveReverseListTest() { + // Create a linked list with values from 1 to 20 + SinglyLinkedList list = createSampleList(20); + + // Reversing the linked list using reverseList() method + SinglyLinkedListNode head = list.reverseListRec(list.getHead()); + + // Check if the reversed list has the correct values + int i = 20; + SinglyLinkedListNode temp = head; + while (temp != null && i > 0) { + assertEquals(i, temp.value); + temp = temp.next; + i--; + } + } + + @Test + void readWithEnhancedForLoopTest() { + final var expeced = new ArrayList<Integer>(Arrays.asList(10, 20, 30)); + + SinglyLinkedList list = new SinglyLinkedList(); + for (final var x : expeced) { + list.insert(x); + } + + var readElements = new ArrayList<Integer>(); + for (final var x : list) { + readElements.add(x); + } + + assertEquals(readElements, expeced); + } + + @Test + void toStringTest() { + SinglyLinkedList list = new SinglyLinkedList(); + list.insert(1); + list.insert(2); + list.insert(3); + assertEquals("1->2->3", list.toString()); + } + + @Test + void toStringForEmptyListTest() { + SinglyLinkedList list = new SinglyLinkedList(); + assertEquals("", list.toString()); + } + + @Test + void countTest() { + SinglyLinkedList list = new SinglyLinkedList(); + list.insert(10); + list.insert(20); + assertEquals(2, list.count()); + } + + @Test + void countForEmptyListTest() { + SinglyLinkedList list = new SinglyLinkedList(); + assertEquals(0, list.count()); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/lists/SkipListTest.java b/src/test/java/com/thealgorithms/datastructures/lists/SkipListTest.java new file mode 100644 index 000000000000..c572739ffbbf --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/lists/SkipListTest.java @@ -0,0 +1,110 @@ +package com.thealgorithms.datastructures.lists; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.stream.IntStream; +import org.junit.jupiter.api.Test; + +class SkipListTest { + + @Test + void add() { + SkipList<String> skipList = new SkipList<>(); + assertEquals(0, skipList.size()); + + skipList.add("value"); + + print(skipList); + assertEquals(1, skipList.size()); + } + + @Test + void get() { + SkipList<String> skipList = new SkipList<>(); + skipList.add("value"); + + String actualValue = skipList.get(0); + + print(skipList); + assertEquals("value", actualValue); + } + + @Test + void contains() { + SkipList<String> skipList = createSkipList(); + print(skipList); + + boolean contains = skipList.contains("b"); + + assertTrue(contains); + } + + @Test + void removeFromHead() { + SkipList<String> skipList = createSkipList(); + String mostLeftElement = skipList.get(0); + int initialSize = skipList.size(); + print(skipList); + + skipList.remove(mostLeftElement); + + print(skipList); + assertEquals(initialSize - 1, skipList.size()); + } + + @Test + void removeFromTail() { + SkipList<String> skipList = createSkipList(); + String mostRightValue = skipList.get(skipList.size() - 1); + int initialSize = skipList.size(); + print(skipList); + + skipList.remove(mostRightValue); + + print(skipList); + assertEquals(initialSize - 1, skipList.size()); + } + + @Test + void checkSortedOnLowestLayer() { + SkipList<String> skipList = new SkipList<>(); + String[] values = {"d", "b", "a", "c"}; + Arrays.stream(values).forEach(skipList::add); + print(skipList); + + String[] actualOrder = IntStream.range(0, values.length).mapToObj(skipList::get).toArray(String[] ::new); + + assertArrayEquals(new String[] {"a", "b", "c", "d"}, actualOrder); + } + + private SkipList<String> createSkipList() { + SkipList<String> skipList = new SkipList<>(); + String[] values = { + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + }; + Arrays.stream(values).forEach(skipList::add); + return skipList; + } + + /** + * Print Skip List representation to console. + * Optional method not involved in testing process. Used only for visualisation purposes. + * @param skipList to print + */ + private void print(SkipList<?> skipList) { + System.out.println(skipList); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/lists/SortedLinkedListTest.java b/src/test/java/com/thealgorithms/datastructures/lists/SortedLinkedListTest.java new file mode 100644 index 000000000000..82e0853da374 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/lists/SortedLinkedListTest.java @@ -0,0 +1,131 @@ +package com.thealgorithms.datastructures.lists; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class SortedLinkedListTest { + + private SortedLinkedList list; + + @BeforeEach + public void setUp() { + list = new SortedLinkedList(); + } + + @Test + public void testInsertIntoEmptyList() { + list.insert(5); + assertEquals("[5]", list.toString()); + } + + @Test + public void testInsertInSortedOrder() { + list.insert(5); + list.insert(3); + list.insert(7); + assertEquals("[3, 5, 7]", list.toString()); + } + + @Test + public void testInsertDuplicateValues() { + list.insert(5); + list.insert(5); + list.insert(5); + assertEquals("[5, 5, 5]", list.toString()); + } + + @Test + public void testDeleteHeadElement() { + list.insert(1); + list.insert(2); + list.insert(3); + assertTrue(list.delete(1)); + assertEquals("[2, 3]", list.toString()); + } + + @Test + public void testDeleteTailElement() { + list.insert(1); + list.insert(2); + list.insert(3); + assertTrue(list.delete(3)); + assertEquals("[1, 2]", list.toString()); + } + + @Test + public void testDeleteMiddleElement() { + list.insert(1); + list.insert(2); + list.insert(3); + assertTrue(list.delete(2)); + assertEquals("[1, 3]", list.toString()); + } + + @Test + public void testDeleteNonexistentElement() { + list.insert(1); + list.insert(2); + assertFalse(list.delete(3)); + } + + @Test + public void testDeleteFromSingleElementList() { + list.insert(5); + assertTrue(list.delete(5)); + assertEquals("[]", list.toString()); + } + + @Test + public void testDeleteFromEmptyList() { + assertFalse(list.delete(5)); + } + + @Test + public void testSearchInEmptyList() { + assertFalse(list.search(5)); + } + + @Test + public void testSearchForExistingElement() { + list.insert(3); + list.insert(1); + list.insert(5); + assertTrue(list.search(3)); + } + + @Test + public void testSearchForNonexistentElement() { + list.insert(3); + list.insert(1); + list.insert(5); + assertFalse(list.search(10)); + } + + @Test + public void testIsEmptyOnEmptyList() { + assertTrue(list.isEmpty()); + } + + @Test + public void testIsEmptyOnNonEmptyList() { + list.insert(10); + assertFalse(list.isEmpty()); + } + + @Test + public void testIsEmptyAfterInsertion() { + list.insert(10); + assertFalse(list.isEmpty()); + } + + @Test + public void testIsEmptyAfterDeletion() { + list.insert(10); + list.delete(10); + assertTrue(list.isEmpty()); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/queues/CircularQueueTest.java b/src/test/java/com/thealgorithms/datastructures/queues/CircularQueueTest.java new file mode 100644 index 000000000000..55ce9995a32d --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/queues/CircularQueueTest.java @@ -0,0 +1,172 @@ +package com.thealgorithms.datastructures.queues; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class CircularQueueTest { + + @Test + void testEnQueue() { + CircularQueue<Integer> cq = new CircularQueue<>(3); + cq.enQueue(1); + cq.enQueue(2); + cq.enQueue(3); + + assertEquals(1, cq.peek()); + assertTrue(cq.isFull()); + } + + @Test + void testDeQueue() { + CircularQueue<Integer> cq = new CircularQueue<>(3); + cq.enQueue(1); + cq.enQueue(2); + cq.enQueue(3); + + assertEquals(1, cq.deQueue()); + assertEquals(2, cq.peek()); + assertFalse(cq.isFull()); + } + + @Test + void testIsEmpty() { + CircularQueue<Integer> cq = new CircularQueue<>(3); + assertTrue(cq.isEmpty()); + + cq.enQueue(1); + assertFalse(cq.isEmpty()); + } + + @Test + void testIsFull() { + CircularQueue<Integer> cq = new CircularQueue<>(2); + cq.enQueue(1); + cq.enQueue(2); + assertTrue(cq.isFull()); + + cq.deQueue(); + assertFalse(cq.isFull()); + } + + @Test + void testPeek() { + CircularQueue<Integer> cq = new CircularQueue<>(3); + cq.enQueue(1); + cq.enQueue(2); + + assertEquals(1, cq.peek()); + assertEquals(1, cq.peek()); // Ensure peek doesn't remove the element + } + + @Test + void testDeleteQueue() { + CircularQueue<Integer> cq = new CircularQueue<>(3); + cq.enQueue(1); + cq.enQueue(2); + cq.deleteQueue(); + + org.junit.jupiter.api.Assertions.assertThrows(IllegalStateException.class, cq::peek); + } + + @Test + void testEnQueueOnFull() { + CircularQueue<Integer> cq = new CircularQueue<>(2); + cq.enQueue(1); + cq.enQueue(2); + + org.junit.jupiter.api.Assertions.assertThrows(IllegalStateException.class, () -> cq.enQueue(3)); + } + + @Test + void testDeQueueOnEmpty() { + CircularQueue<Integer> cq = new CircularQueue<>(2); + org.junit.jupiter.api.Assertions.assertThrows(IllegalStateException.class, cq::deQueue); + } + + @Test + void testPeekOnEmpty() { + CircularQueue<Integer> cq = new CircularQueue<>(2); + org.junit.jupiter.api.Assertions.assertThrows(IllegalStateException.class, cq::peek); + } + + @Test + void testSize() { + CircularQueue<Integer> cq = new CircularQueue<>(3); + cq.enQueue(1); + cq.enQueue(2); + + assertEquals(2, cq.size()); + + cq.deQueue(); + assertEquals(1, cq.size()); + } + + @Test + void testCircularWrapAround() { + CircularQueue<Integer> cq = new CircularQueue<>(3); + cq.enQueue(1); + cq.enQueue(2); + cq.enQueue(3); + + cq.deQueue(); + cq.enQueue(4); + + assertEquals(2, cq.deQueue()); + assertEquals(3, cq.deQueue()); + assertEquals(4, cq.deQueue()); + assertTrue(cq.isEmpty()); + } + + @Test + void testEnQueueDeQueueMultipleTimes() { + CircularQueue<Integer> cq = new CircularQueue<>(3); + cq.enQueue(1); + cq.enQueue(2); + cq.deQueue(); + cq.enQueue(3); + cq.enQueue(4); + + assertTrue(cq.isFull()); + assertEquals(2, cq.deQueue()); + assertEquals(3, cq.deQueue()); + assertEquals(4, cq.deQueue()); + assertTrue(cq.isEmpty()); + } + + @Test + void testMultipleWrapArounds() { + CircularQueue<Integer> cq = new CircularQueue<>(3); + cq.enQueue(1); + cq.deQueue(); + cq.enQueue(2); + cq.deQueue(); + cq.enQueue(3); + cq.deQueue(); + cq.enQueue(4); + + assertEquals(4, cq.peek()); + } + + @Test + void testSizeDuringOperations() { + CircularQueue<Integer> cq = new CircularQueue<>(3); + assertEquals(0, cq.size()); + + cq.enQueue(1); + cq.enQueue(2); + assertEquals(2, cq.size()); + + cq.deQueue(); + assertEquals(1, cq.size()); + + cq.enQueue(3); + cq.enQueue(4); + assertEquals(3, cq.size()); + cq.deQueue(); + cq.deQueue(); + assertEquals(1, cq.size()); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/queues/DequeTest.java b/src/test/java/com/thealgorithms/datastructures/queues/DequeTest.java new file mode 100644 index 000000000000..1244a2e260d2 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/queues/DequeTest.java @@ -0,0 +1,90 @@ +package com.thealgorithms.datastructures.queues; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.NoSuchElementException; +import org.junit.jupiter.api.Test; + +class DequeTest { + + @Test + void testAddFirst() { + Deque<Integer> deque = new Deque<>(); + deque.addFirst(10); + assertEquals(10, deque.peekFirst()); + assertEquals(10, deque.peekLast()); + assertEquals(1, deque.size()); + } + + @Test + void testAddLast() { + Deque<Integer> deque = new Deque<>(); + deque.addLast(20); + assertEquals(20, deque.peekFirst()); + assertEquals(20, deque.peekLast()); + assertEquals(1, deque.size()); + } + + @Test + void testPollFirst() { + Deque<Integer> deque = new Deque<>(); + deque.addFirst(10); + deque.addLast(20); + assertEquals(10, deque.pollFirst()); + assertEquals(20, deque.peekFirst()); + assertEquals(1, deque.size()); + } + + @Test + void testPollLast() { + Deque<Integer> deque = new Deque<>(); + deque.addFirst(10); + deque.addLast(20); + assertEquals(20, deque.pollLast()); + assertEquals(10, deque.peekLast()); + assertEquals(1, deque.size()); + } + + @Test + void testIsEmpty() { + Deque<Integer> deque = new Deque<>(); + org.junit.jupiter.api.Assertions.assertTrue(deque.isEmpty()); + deque.addFirst(10); + assertFalse(deque.isEmpty()); + } + + @Test + void testPeekFirstEmpty() { + Deque<Integer> deque = new Deque<>(); + assertNull(deque.peekFirst()); + } + + @Test + void testPeekLastEmpty() { + Deque<Integer> deque = new Deque<>(); + assertNull(deque.peekLast()); + } + + @Test + void testPollFirstEmpty() { + Deque<Integer> deque = new Deque<>(); + org.junit.jupiter.api.Assertions.assertThrows(NoSuchElementException.class, deque::pollFirst); + } + + @Test + void testPollLastEmpty() { + Deque<Integer> deque = new Deque<>(); + org.junit.jupiter.api.Assertions.assertThrows(NoSuchElementException.class, deque::pollLast); + } + + @Test + void testToString() { + Deque<Integer> deque = new Deque<>(); + deque.addFirst(10); + deque.addLast(20); + deque.addFirst(5); + assertEquals("Head -> 5 <-> 10 <-> 20 <- Tail", deque.toString()); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/queues/GenericArrayListQueueTest.java b/src/test/java/com/thealgorithms/datastructures/queues/GenericArrayListQueueTest.java new file mode 100644 index 000000000000..b19513e19ad9 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/queues/GenericArrayListQueueTest.java @@ -0,0 +1,100 @@ +package com.thealgorithms.datastructures.queues; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class GenericArrayListQueueTest { + + @Test + void testAdd() { + GenericArrayListQueue<Integer> queue = new GenericArrayListQueue<>(); + assertTrue(queue.add(10)); + assertTrue(queue.add(20)); + assertEquals(10, queue.peek()); // Ensure the first added element is at the front + } + + @Test + void testPeek() { + GenericArrayListQueue<Integer> queue = new GenericArrayListQueue<>(); + assertNull(queue.peek(), "Peek should return null for an empty queue"); + + queue.add(10); + queue.add(20); + + assertEquals(10, queue.peek(), "Peek should return the first element (10)"); + queue.poll(); + assertEquals(20, queue.peek(), "Peek should return the next element (20) after poll"); + } + + @Test + void testPoll() { + GenericArrayListQueue<Integer> queue = new GenericArrayListQueue<>(); + assertNull(queue.poll(), "Poll should return null for an empty queue"); + + queue.add(10); + queue.add(20); + + assertEquals(10, queue.poll(), "Poll should return and remove the first element (10)"); + assertEquals(20, queue.poll(), "Poll should return and remove the next element (20)"); + assertNull(queue.poll(), "Poll should return null when queue is empty after removals"); + } + + @Test + void testIsEmpty() { + GenericArrayListQueue<Integer> queue = new GenericArrayListQueue<>(); + assertTrue(queue.isEmpty(), "Queue should initially be empty"); + + queue.add(30); + assertFalse(queue.isEmpty(), "Queue should not be empty after adding an element"); + queue.poll(); + assertTrue(queue.isEmpty(), "Queue should be empty after removing the only element"); + } + + @Test + void testClearQueueAndReuse() { + GenericArrayListQueue<Integer> queue = new GenericArrayListQueue<>(); + queue.add(5); + queue.add(10); + queue.poll(); + queue.poll(); // Remove all elements + + assertTrue(queue.isEmpty(), "Queue should be empty after all elements are removed"); + assertNull(queue.peek(), "Peek should return null on an empty queue after clear"); + assertTrue(queue.add(15), "Queue should be reusable after being emptied"); + assertEquals(15, queue.peek(), "Newly added element should be accessible in the empty queue"); + } + + @Test + void testOrderMaintained() { + GenericArrayListQueue<String> queue = new GenericArrayListQueue<>(); + queue.add("First"); + queue.add("Second"); + queue.add("Third"); + + assertEquals("First", queue.poll(), "Order should be maintained; expected 'First'"); + assertEquals("Second", queue.poll(), "Order should be maintained; expected 'Second'"); + assertEquals("Third", queue.poll(), "Order should be maintained; expected 'Third'"); + } + + @Test + void testVariousDataTypes() { + GenericArrayListQueue<Double> queue = new GenericArrayListQueue<>(); + queue.add(1.1); + queue.add(2.2); + + assertEquals(1.1, queue.peek(), "Queue should handle Double data type correctly"); + assertEquals(1.1, queue.poll(), "Poll should return correct Double value"); + assertEquals(2.2, queue.peek(), "Peek should show next Double value in the queue"); + } + + @Test + void testEmptyPollAndPeekBehavior() { + GenericArrayListQueue<Integer> queue = new GenericArrayListQueue<>(); + assertNull(queue.peek(), "Peek on an empty queue should return null"); + assertNull(queue.poll(), "Poll on an empty queue should return null"); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/queues/LinkedQueueTest.java b/src/test/java/com/thealgorithms/datastructures/queues/LinkedQueueTest.java new file mode 100644 index 000000000000..157d771b5a80 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/queues/LinkedQueueTest.java @@ -0,0 +1,209 @@ +package com.thealgorithms.datastructures.queues; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class LinkedQueueTest { + + private LinkedQueue<Integer> queue; + + @BeforeEach + void setUp() { + queue = new LinkedQueue<>(); + } + + @Test + void testIsEmptyOnNewQueue() { + assertTrue(queue.isEmpty(), "Queue should be empty on initialization."); + } + + @Test + void testEnqueueAndSize() { + queue.enqueue(10); + assertFalse(queue.isEmpty(), "Queue should not be empty after enqueue."); + assertEquals(1, queue.size(), "Queue size should be 1 after one enqueue."); + + queue.enqueue(20); + queue.enqueue(30); + assertEquals(3, queue.size(), "Queue size should be 3 after three enqueues."); + } + + @Test + void testDequeueOnSingleElementQueue() { + queue.enqueue(10); + assertEquals(10, queue.dequeue(), "Dequeued element should be the same as the enqueued one."); + assertTrue(queue.isEmpty(), "Queue should be empty after dequeueing the only element."); + } + + @Test + void testDequeueMultipleElements() { + queue.enqueue(10); + queue.enqueue(20); + queue.enqueue(30); + + assertEquals(10, queue.dequeue(), "First dequeued element should be the first enqueued one."); + assertEquals(20, queue.dequeue(), "Second dequeued element should be the second enqueued one."); + assertEquals(30, queue.dequeue(), "Third dequeued element should be the third enqueued one."); + assertTrue(queue.isEmpty(), "Queue should be empty after dequeueing all elements."); + } + + @Test + void testDequeueOnEmptyQueue() { + org.junit.jupiter.api.Assertions.assertThrows(NoSuchElementException.class, () -> queue.dequeue(), "Dequeueing from an empty queue should throw NoSuchElementException."); + } + + @Test + void testPeekFrontOnEmptyQueue() { + org.junit.jupiter.api.Assertions.assertThrows(NoSuchElementException.class, () -> queue.peekFront(), "Peeking front on an empty queue should throw NoSuchElementException."); + } + + @Test + void testPeekRearOnEmptyQueue() { + org.junit.jupiter.api.Assertions.assertThrows(NoSuchElementException.class, () -> queue.peekRear(), "Peeking rear on an empty queue should throw NoSuchElementException."); + } + + @Test + void testPeekFront() { + queue.enqueue(10); + queue.enqueue(20); + queue.enqueue(30); + + assertEquals(10, queue.peekFront(), "Peek front should return the first enqueued element."); + assertEquals(10, queue.peekFront(), "Peek front should not remove the element."); + assertEquals(3, queue.size(), "Queue size should remain unchanged after peek."); + } + + @Test + void testPeekRear() { + queue.enqueue(10); + queue.enqueue(20); + queue.enqueue(30); + + assertEquals(30, queue.peekRear(), "Peek rear should return the last enqueued element."); + assertEquals(30, queue.peekRear(), "Peek rear should not remove the element."); + assertEquals(3, queue.size(), "Queue size should remain unchanged after peek."); + } + + @Test + void testPeekAtPosition() { + queue.enqueue(10); + queue.enqueue(20); + queue.enqueue(30); + + assertEquals(10, queue.peek(1), "Peek at position 1 should return the first enqueued element."); + assertEquals(20, queue.peek(2), "Peek at position 2 should return the second enqueued element."); + assertEquals(30, queue.peek(3), "Peek at position 3 should return the third enqueued element."); + } + + @Test + void testPeekAtInvalidPosition() { + queue.enqueue(10); + queue.enqueue(20); + queue.enqueue(30); + + org.junit.jupiter.api.Assertions.assertThrows(IndexOutOfBoundsException.class, () -> queue.peek(4), "Peeking at a position greater than size should throw IndexOutOfBoundsException."); + org.junit.jupiter.api.Assertions.assertThrows(IndexOutOfBoundsException.class, () -> queue.peek(0), "Peeking at position 0 should throw IndexOutOfBoundsException."); + } + + @Test + void testClearQueue() { + queue.enqueue(10); + queue.enqueue(20); + queue.enqueue(30); + + queue.clear(); + assertTrue(queue.isEmpty(), "Queue should be empty after clear."); + assertEquals(0, queue.size(), "Queue size should be 0 after clear."); + } + + @Test + void testIterator() { + queue.enqueue(10); + queue.enqueue(20); + queue.enqueue(30); + + Iterator<Integer> it = queue.iterator(); + assertTrue(it.hasNext(), "Iterator should have next element."); + assertEquals(10, it.next(), "First iterator value should be the first enqueued element."); + assertEquals(20, it.next(), "Second iterator value should be the second enqueued element."); + assertEquals(30, it.next(), "Third iterator value should be the third enqueued element."); + assertFalse(it.hasNext(), "Iterator should not have next element after last element."); + org.junit.jupiter.api.Assertions.assertThrows(NoSuchElementException.class, it::next, "Calling next() on exhausted iterator should throw NoSuchElementException."); + } + + @Test + void testToString() { + queue.enqueue(10); + queue.enqueue(20); + queue.enqueue(30); + + assertEquals("[10, 20, 30]", queue.toString(), "toString should return a properly formatted string representation of the queue."); + } + + @Test + void testEnqueueAfterDequeue() { + queue.enqueue(10); + queue.enqueue(20); + queue.enqueue(30); + + assertEquals(10, queue.dequeue(), "Dequeued element should be 10."); + assertEquals(20, queue.dequeue(), "Dequeued element should be 20."); + + queue.enqueue(40); + assertEquals(30, queue.peekFront(), "Peek front should return 30 after dequeuing and enqueuing new elements."); + assertEquals(40, queue.peekRear(), "Peek rear should return 40 after enqueuing new elements."); + } + + @Test + void testQueueMaintainsOrder() { + for (int i = 1; i <= 100; i++) { + queue.enqueue(i); + } + + for (int i = 1; i <= 100; i++) { + assertEquals(i, queue.dequeue(), "Queue should maintain the correct order of elements."); + } + + assertTrue(queue.isEmpty(), "Queue should be empty after dequeuing all elements."); + } + + @Test + void testSizeAfterOperations() { + assertEquals(0, queue.size(), "Initial queue size should be 0."); + + queue.enqueue(10); + assertEquals(1, queue.size(), "Queue size should be 1 after one enqueue."); + + queue.enqueue(20); + assertEquals(2, queue.size(), "Queue size should be 2 after two enqueues."); + + queue.dequeue(); + assertEquals(1, queue.size(), "Queue size should be 1 after one dequeue."); + + queue.clear(); + assertEquals(0, queue.size(), "Queue size should be 0 after clear."); + } + + @Test + void testQueueToStringOnEmptyQueue() { + assertEquals("[]", queue.toString(), "toString on empty queue should return '[]'."); + } + + @Test + void testEnqueueNull() { + org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class, () -> queue.enqueue(null), "Cannot enqueue null data."); + } + + @Test + void testIteratorOnEmptyQueue() { + Iterator<Integer> it = queue.iterator(); + assertFalse(it.hasNext(), "Iterator on empty queue should not have next element."); + org.junit.jupiter.api.Assertions.assertThrows(NoSuchElementException.class, it::next, "Calling next() on empty queue iterator should throw NoSuchElementException."); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/queues/PriorityQueuesTest.java b/src/test/java/com/thealgorithms/datastructures/queues/PriorityQueuesTest.java new file mode 100644 index 000000000000..1a3b5aadebb2 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/queues/PriorityQueuesTest.java @@ -0,0 +1,58 @@ +package com.thealgorithms.datastructures.queues; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class PriorityQueuesTest { + + @Test + void testPQInsertion() { + PriorityQueue myQueue = new PriorityQueue(4); + myQueue.insert(2); + Assertions.assertEquals(myQueue.peek(), 2); + + myQueue.insert(5); + myQueue.insert(3); + Assertions.assertEquals(myQueue.peek(), 5); + + myQueue.insert(10); + Assertions.assertEquals(myQueue.peek(), 10); + } + + @Test + void testPQDeletion() { + PriorityQueue myQueue = new PriorityQueue(4); + myQueue.insert(2); + myQueue.insert(5); + myQueue.insert(3); + myQueue.insert(10); + + myQueue.remove(); + Assertions.assertEquals(myQueue.peek(), 5); + myQueue.remove(); + myQueue.remove(); + Assertions.assertEquals(myQueue.peek(), 2); + } + + @Test + void testPQExtra() { + PriorityQueue myQueue = new PriorityQueue(4); + Assertions.assertEquals(myQueue.isEmpty(), true); + Assertions.assertEquals(myQueue.isFull(), false); + myQueue.insert(2); + myQueue.insert(5); + Assertions.assertEquals(myQueue.isFull(), false); + myQueue.insert(3); + myQueue.insert(10); + Assertions.assertEquals(myQueue.isEmpty(), false); + Assertions.assertEquals(myQueue.isFull(), true); + + myQueue.remove(); + Assertions.assertEquals(myQueue.getSize(), 3); + Assertions.assertEquals(myQueue.peek(), 5); + myQueue.remove(); + myQueue.remove(); + Assertions.assertEquals(myQueue.peek(), 2); + Assertions.assertEquals(myQueue.getSize(), 1); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/queues/QueueByTwoStacksTest.java b/src/test/java/com/thealgorithms/datastructures/queues/QueueByTwoStacksTest.java new file mode 100644 index 000000000000..87f136a84631 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/queues/QueueByTwoStacksTest.java @@ -0,0 +1,69 @@ +package com.thealgorithms.datastructures.queues; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.NoSuchElementException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class QueueByTwoStacksTest { + + private QueueByTwoStacks<Integer> queue; + + @BeforeEach + public void setUp() { + queue = new QueueByTwoStacks<>(); + } + + @Test + public void testEmptyQueue() { + assertEquals(0, queue.size()); + } + + @Test + public void testEnqueue() { + queue.put(10); + queue.put(20); + assertEquals(2, queue.size()); + } + + @Test + public void testDequeue() { + queue.put(10); + queue.put(20); + queue.put(30); + assertEquals(10, queue.get()); // First item out + assertEquals(20, queue.get()); // Second item out + assertEquals(30, queue.get()); // Third item out + } + + @Test + public void testInterleavedOperations() { + queue.put(10); + queue.put(20); + assertEquals(10, queue.get()); // Dequeue first item + queue.put(30); + assertEquals(20, queue.get()); // Dequeue second item + assertEquals(30, queue.get()); // Dequeue third item + } + + @Test + public void testQueueSize() { + assertEquals(0, queue.size()); + queue.put(1); + assertEquals(1, queue.size()); + queue.put(2); + queue.put(3); + assertEquals(3, queue.size()); + queue.get(); + assertEquals(2, queue.size()); + } + + @Test + public void testEmptyQueueException() { + assertThrows(NoSuchElementException.class, () -> { + queue.get(); // Attempting to dequeue from empty queue + }); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/queues/QueueTest.java b/src/test/java/com/thealgorithms/datastructures/queues/QueueTest.java new file mode 100644 index 000000000000..9a4f50d5e216 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/queues/QueueTest.java @@ -0,0 +1,127 @@ +package com.thealgorithms.datastructures.queues; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class QueueTest { + + private static final int INITIAL_CAPACITY = 3; + private Queue<Integer> queue; + + @BeforeEach + void setUp() { + queue = new Queue<>(INITIAL_CAPACITY); + } + + @Test + void testQueueInsertion() { + Assertions.assertTrue(queue.insert(1)); + Assertions.assertTrue(queue.insert(2)); + Assertions.assertTrue(queue.insert(3)); + Assertions.assertFalse(queue.insert(4)); // Queue is full + + Assertions.assertEquals(1, queue.peekFront()); + Assertions.assertEquals(3, queue.peekRear()); + Assertions.assertEquals(3, queue.getSize()); + } + + @Test + void testQueueRemoval() { + queue.insert(1); + queue.insert(2); + queue.insert(3); + + Assertions.assertEquals(1, queue.remove()); + Assertions.assertEquals(2, queue.peekFront()); + Assertions.assertEquals(2, queue.getSize()); + + Assertions.assertEquals(2, queue.remove()); + Assertions.assertEquals(3, queue.peekFront()); + Assertions.assertEquals(1, queue.getSize()); + + Assertions.assertEquals(3, queue.remove()); + Assertions.assertTrue(queue.isEmpty()); + + Assertions.assertThrows(IllegalStateException.class, queue::remove); // Queue is empty + } + + @Test + void testPeekFrontAndRear() { + queue.insert(1); + queue.insert(2); + + Assertions.assertEquals(1, queue.peekFront()); + Assertions.assertEquals(2, queue.peekRear()); + + queue.insert(3); + Assertions.assertEquals(1, queue.peekFront()); + Assertions.assertEquals(3, queue.peekRear()); + } + + @Test + void testQueueIsEmptyAndIsFull() { + Assertions.assertTrue(queue.isEmpty()); + Assertions.assertFalse(queue.isFull()); + + queue.insert(1); + queue.insert(2); + queue.insert(3); + + Assertions.assertFalse(queue.isEmpty()); + Assertions.assertTrue(queue.isFull()); + + queue.remove(); + Assertions.assertFalse(queue.isFull()); + Assertions.assertFalse(queue.isEmpty()); + } + + @Test + void testQueueSize() { + Assertions.assertEquals(0, queue.getSize()); + queue.insert(1); + Assertions.assertEquals(1, queue.getSize()); + queue.insert(2); + Assertions.assertEquals(2, queue.getSize()); + queue.insert(3); + Assertions.assertEquals(3, queue.getSize()); + queue.remove(); + Assertions.assertEquals(2, queue.getSize()); + } + + @Test + void testQueueToString() { + Assertions.assertEquals("[]", queue.toString()); + + queue.insert(1); + queue.insert(2); + Assertions.assertEquals("[1, 2]", queue.toString()); + + queue.insert(3); + Assertions.assertEquals("[1, 2, 3]", queue.toString()); + + queue.remove(); + Assertions.assertEquals("[2, 3]", queue.toString()); + + queue.remove(); + queue.remove(); + Assertions.assertEquals("[]", queue.toString()); + } + + @Test + void testQueueThrowsExceptionOnEmptyPeek() { + Assertions.assertThrows(IllegalStateException.class, queue::peekFront); + Assertions.assertThrows(IllegalStateException.class, queue::peekRear); + } + + @Test + void testQueueThrowsExceptionOnRemoveFromEmptyQueue() { + Assertions.assertThrows(IllegalStateException.class, queue::remove); + } + + @Test + void testQueueCapacityException() { + Assertions.assertThrows(IllegalArgumentException.class, () -> new Queue<>(0)); + Assertions.assertThrows(IllegalArgumentException.class, () -> new Queue<>(-5)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/queues/SlidingWindowMaximumTest.java b/src/test/java/com/thealgorithms/datastructures/queues/SlidingWindowMaximumTest.java new file mode 100644 index 000000000000..e435f9192a88 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/queues/SlidingWindowMaximumTest.java @@ -0,0 +1,50 @@ +package com.thealgorithms.datastructures.queues; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class SlidingWindowMaximumTest { + + @ParameterizedTest + @MethodSource("provideTestCases") + public void testMaxSlidingWindow(int[] nums, int k, int[] expected) { + assertArrayEquals(expected, SlidingWindowMaximum.maxSlidingWindow(nums, k)); + } + + private static Stream<Arguments> provideTestCases() { + return Stream.of( + // Test case 1: Example from the problem statement + Arguments.of(new int[] {1, 3, -1, -3, 5, 3, 6, 7}, 3, new int[] {3, 3, 5, 5, 6, 7}), + + // Test case 2: All elements are the same + Arguments.of(new int[] {4, 4, 4, 4, 4}, 2, new int[] {4, 4, 4, 4}), + + // Test case 3: Window size equals the array length + Arguments.of(new int[] {2, 1, 5, 3, 6}, 5, new int[] {6}), + + // Test case 4: Single element array with window size 1 + Arguments.of(new int[] {7}, 1, new int[] {7}), + + // Test case 5: Window size larger than the array length + Arguments.of(new int[] {1, 2, 3}, 4, new int[] {}), + + // Test case 6: Decreasing sequence + Arguments.of(new int[] {9, 8, 7, 6, 5, 4}, 3, new int[] {9, 8, 7, 6}), + + // Test case 7: Increasing sequence + Arguments.of(new int[] {1, 2, 3, 4, 5}, 2, new int[] {2, 3, 4, 5}), + + // Test case 8: k is zero + Arguments.of(new int[] {1, 3, -1, -3, 5, 3, 6, 7}, 0, new int[] {}), + + // Test case 9: Array with negative numbers + Arguments.of(new int[] {-4, -2, -5, -1, -3}, 3, new int[] {-2, -1, -1}), + + // Test case 10: Empty array + Arguments.of(new int[] {}, 3, new int[] {})); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/queues/TokenBucketTest.java b/src/test/java/com/thealgorithms/datastructures/queues/TokenBucketTest.java new file mode 100644 index 000000000000..959ea8724f8b --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/queues/TokenBucketTest.java @@ -0,0 +1,90 @@ +package com.thealgorithms.datastructures.queues; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class TokenBucketTest { + + @Test + public void testRateLimiter() throws InterruptedException { + TokenBucket bucket = new TokenBucket(5, 1); + for (int i = 0; i < 5; i++) { + assertTrue(bucket.allowRequest()); + } + assertFalse(bucket.allowRequest()); + Thread.sleep(1000); + assertTrue(bucket.allowRequest()); + } + + @Test + public void testRateLimiterWithExceedingRequests() throws InterruptedException { + TokenBucket bucket = new TokenBucket(3, 1); + + for (int i = 0; i < 3; i++) { + assertTrue(bucket.allowRequest()); + } + assertFalse(bucket.allowRequest()); + + Thread.sleep(1000); + assertTrue(bucket.allowRequest()); + assertFalse(bucket.allowRequest()); + } + + @Test + public void testRateLimiterMultipleRefills() throws InterruptedException { + TokenBucket bucket = new TokenBucket(2, 1); + + assertTrue(bucket.allowRequest()); + assertTrue(bucket.allowRequest()); + assertFalse(bucket.allowRequest()); + + Thread.sleep(1000); + assertTrue(bucket.allowRequest()); + + Thread.sleep(1000); + assertTrue(bucket.allowRequest()); + assertFalse(bucket.allowRequest()); + } + + @Test + public void testRateLimiterEmptyBucket() { + TokenBucket bucket = new TokenBucket(0, 1); + + assertFalse(bucket.allowRequest()); + } + + @Test + public void testRateLimiterWithHighRefillRate() throws InterruptedException { + TokenBucket bucket = new TokenBucket(5, 10); + + for (int i = 0; i < 5; i++) { + assertTrue(bucket.allowRequest()); + } + + assertFalse(bucket.allowRequest()); + + Thread.sleep(1000); + + for (int i = 0; i < 5; i++) { + assertTrue(bucket.allowRequest()); + } + } + + @Test + public void testRateLimiterWithSlowRequests() throws InterruptedException { + TokenBucket bucket = new TokenBucket(5, 1); + + for (int i = 0; i < 5; i++) { + assertTrue(bucket.allowRequest()); + } + + Thread.sleep(1000); + assertTrue(bucket.allowRequest()); + + Thread.sleep(2000); + assertTrue(bucket.allowRequest()); + assertTrue(bucket.allowRequest()); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/stacks/NodeStackTest.java b/src/test/java/com/thealgorithms/datastructures/stacks/NodeStackTest.java new file mode 100644 index 000000000000..7ac0d8bc324b --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/stacks/NodeStackTest.java @@ -0,0 +1,73 @@ +package com.thealgorithms.datastructures.stacks; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class NodeStackTest { + + @Test + void testPush() { + NodeStack<Integer> stack = new NodeStack<>(); + stack.push(10); + stack.push(20); + assertEquals(20, stack.peek(), "Top element should be 20 after pushing 10 and 20."); + } + + @Test + void testPop() { + NodeStack<String> stack = new NodeStack<>(); + stack.push("First"); + stack.push("Second"); + assertEquals("Second", stack.pop(), "Pop should return 'Second', the last pushed element."); + assertEquals("First", stack.pop(), "Pop should return 'First' after 'Second' is removed."); + } + + @Test + void testPopOnEmptyStack() { + NodeStack<Double> stack = new NodeStack<>(); + assertThrows(IllegalStateException.class, stack::pop, "Popping an empty stack should throw IllegalStateException."); + } + + @Test + void testPeek() { + NodeStack<Integer> stack = new NodeStack<>(); + stack.push(5); + stack.push(15); + assertEquals(15, stack.peek(), "Peek should return 15, the top element."); + stack.pop(); + assertEquals(5, stack.peek(), "Peek should return 5 after 15 is popped."); + } + + @Test + void testPeekOnEmptyStack() { + NodeStack<String> stack = new NodeStack<>(); + assertThrows(IllegalStateException.class, stack::peek, "Peeking an empty stack should throw IllegalStateException."); + } + + @Test + void testIsEmpty() { + NodeStack<Character> stack = new NodeStack<>(); + assertTrue(stack.isEmpty(), "Newly initialized stack should be empty."); + stack.push('A'); + assertFalse(stack.isEmpty(), "Stack should not be empty after a push operation."); + stack.pop(); + assertTrue(stack.isEmpty(), "Stack should be empty after popping the only element."); + } + + @Test + void testSize() { + NodeStack<Integer> stack = new NodeStack<>(); + assertEquals(0, stack.size(), "Size of empty stack should be 0."); + stack.push(3); + stack.push(6); + assertEquals(2, stack.size(), "Size should be 2 after pushing two elements."); + stack.pop(); + assertEquals(1, stack.size(), "Size should be 1 after popping one element."); + stack.pop(); + assertEquals(0, stack.size(), "Size should be 0 after popping all elements."); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/stacks/ReverseStackTest.java b/src/test/java/com/thealgorithms/datastructures/stacks/ReverseStackTest.java new file mode 100644 index 000000000000..2e2bc5adae3a --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/stacks/ReverseStackTest.java @@ -0,0 +1,70 @@ +package com.thealgorithms.datastructures.stacks; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Stack; +import org.junit.jupiter.api.Test; + +class ReverseStackTest { + + @Test + void testReverseEmptyStack() { + Stack<Integer> stack = new Stack<>(); + ReverseStack.reverseStack(stack); + assertTrue(stack.isEmpty(), "Reversing an empty stack should result in an empty stack."); + } + + @Test + void testReverseSingleElementStack() { + Stack<Integer> stack = new Stack<>(); + stack.push(1); + ReverseStack.reverseStack(stack); + assertEquals(1, stack.peek(), "Reversing a single-element stack should have the same element on top."); + } + + @Test + void testReverseTwoElementStack() { + Stack<Integer> stack = new Stack<>(); + stack.push(1); + stack.push(2); + ReverseStack.reverseStack(stack); + + assertEquals(1, stack.pop(), "After reversal, the stack's top element should be the first inserted element."); + assertEquals(2, stack.pop(), "After reversal, the stack's next element should be the second inserted element."); + } + + @Test + void testReverseMultipleElementsStack() { + Stack<Integer> stack = new Stack<>(); + stack.push(1); + stack.push(2); + stack.push(3); + stack.push(4); + + ReverseStack.reverseStack(stack); + + assertEquals(1, stack.pop(), "Stack order after reversal should match the initial push order."); + assertEquals(2, stack.pop()); + assertEquals(3, stack.pop()); + assertEquals(4, stack.pop()); + } + + @Test + void testReverseStackAndVerifySize() { + Stack<Integer> stack = new Stack<>(); + stack.push(10); + stack.push(20); + stack.push(30); + stack.push(40); + int originalSize = stack.size(); + + ReverseStack.reverseStack(stack); + + assertEquals(originalSize, stack.size(), "Stack size should remain unchanged after reversal."); + assertEquals(10, stack.pop(), "Reversal should place the first inserted element on top."); + assertEquals(20, stack.pop()); + assertEquals(30, stack.pop()); + assertEquals(40, stack.pop()); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/stacks/StackArrayListTest.java b/src/test/java/com/thealgorithms/datastructures/stacks/StackArrayListTest.java new file mode 100644 index 000000000000..c8811bb3ccc2 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/stacks/StackArrayListTest.java @@ -0,0 +1,107 @@ +package com.thealgorithms.datastructures.stacks; + +import java.util.EmptyStackException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class StackArrayListTest { + + private StackArrayList<Integer> stack; + + @BeforeEach + void setUp() { + stack = new StackArrayList<>(); + } + + @Test + void testPushAndPop() { + stack.push(1); + stack.push(2); + stack.push(3); + + Assertions.assertEquals(3, stack.pop()); + Assertions.assertEquals(2, stack.pop()); + Assertions.assertEquals(1, stack.pop()); + } + + @Test + void testPeek() { + stack.push(10); + stack.push(20); + + Assertions.assertEquals(20, stack.peek()); // Peek should return the top element + stack.pop(); // Remove top element + Assertions.assertEquals(10, stack.peek()); // Peek should now return the new top element + } + + @Test + void testIsEmpty() { + Assertions.assertTrue(stack.isEmpty()); // Stack should initially be empty + stack.push(1); + Assertions.assertFalse(stack.isEmpty()); // After pushing, stack should not be empty + stack.pop(); + Assertions.assertTrue(stack.isEmpty()); // After popping, stack should be empty again + } + + @Test + void testMakeEmpty() { + stack.push(1); + stack.push(2); + stack.push(3); + stack.makeEmpty(); + Assertions.assertTrue(stack.isEmpty()); // Stack should be empty after makeEmpty is called + Assertions.assertEquals(0, stack.size()); // Size should be 0 after makeEmpty + } + + @Test + void testSize() { + Assertions.assertEquals(0, stack.size()); // Initial size should be 0 + stack.push(1); + stack.push(2); + Assertions.assertEquals(2, stack.size()); // Size should reflect number of elements added + stack.pop(); + Assertions.assertEquals(1, stack.size()); // Size should decrease with elements removed + } + + @Test + void testPopEmptyStackThrowsException() { + Assertions.assertThrows(EmptyStackException.class, stack::pop); // Popping from an empty stack should throw an exception + } + + @Test + void testPeekEmptyStackThrowsException() { + Assertions.assertThrows(EmptyStackException.class, stack::peek); // Peeking into an empty stack should throw an exception + } + + @Test + void testMixedOperations() { + // Testing a mix of push, pop, peek, and size operations in sequence + stack.push(5); + stack.push(10); + stack.push(15); + + Assertions.assertEquals(3, stack.size()); // Size should reflect number of elements + Assertions.assertEquals(15, stack.peek()); // Peek should show last element added + + stack.pop(); // Remove top element + Assertions.assertEquals(10, stack.peek()); // New top should be 10 + Assertions.assertEquals(2, stack.size()); // Size should reflect removal + + stack.push(20); // Add a new element + Assertions.assertEquals(20, stack.peek()); // Top should be the last added element + } + + @Test + void testMultipleMakeEmptyCalls() { + // Ensures calling makeEmpty multiple times does not throw errors or misbehave + stack.push(1); + stack.push(2); + stack.makeEmpty(); + Assertions.assertTrue(stack.isEmpty()); + + stack.makeEmpty(); + Assertions.assertTrue(stack.isEmpty()); + Assertions.assertEquals(0, stack.size()); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/stacks/StackArrayTest.java b/src/test/java/com/thealgorithms/datastructures/stacks/StackArrayTest.java new file mode 100644 index 000000000000..74de7ad6435a --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/stacks/StackArrayTest.java @@ -0,0 +1,129 @@ +package com.thealgorithms.datastructures.stacks; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class StackArrayTest { + + private Stack<Integer> stack; + + @BeforeEach + void setUp() { + stack = new StackArray<>(5); // Initialize a stack with capacity of 5 + } + + @Test + void testPushAndPop() { + stack.push(1); + stack.push(2); + stack.push(3); + stack.push(4); + stack.push(5); + + Assertions.assertEquals(5, stack.pop()); + Assertions.assertEquals(4, stack.pop()); + Assertions.assertEquals(3, stack.pop()); + Assertions.assertEquals(2, stack.pop()); + Assertions.assertEquals(1, stack.pop()); + } + + @Test + void testPeek() { + stack.push(10); + stack.push(20); + stack.push(30); + + Assertions.assertEquals(30, stack.peek()); + Assertions.assertEquals(3, stack.size()); + + stack.pop(); + Assertions.assertEquals(20, stack.peek()); + } + + @Test + void testIsEmpty() { + Assertions.assertTrue(stack.isEmpty()); + stack.push(42); + Assertions.assertFalse(stack.isEmpty()); + stack.pop(); + Assertions.assertTrue(stack.isEmpty()); + } + + @Test + void testResizeOnPush() { + StackArray<Integer> smallStack = new StackArray<>(2); + smallStack.push(1); + smallStack.push(2); + Assertions.assertTrue(smallStack.isFull()); + + smallStack.push(3); + Assertions.assertFalse(smallStack.isFull()); + Assertions.assertEquals(3, smallStack.size()); + + Assertions.assertEquals(3, smallStack.pop()); + Assertions.assertEquals(2, smallStack.pop()); + Assertions.assertEquals(1, smallStack.pop()); + } + + @Test + void testResizeOnPop() { + StackArray<Integer> stack = new StackArray<>(4); + stack.push(1); + stack.push(2); + stack.push(3); + stack.push(4); + + stack.pop(); + stack.pop(); + stack.pop(); + Assertions.assertEquals(1, stack.size()); + + stack.pop(); + Assertions.assertTrue(stack.isEmpty()); + } + + @Test + void testMakeEmpty() { + stack.push(1); + stack.push(2); + stack.push(3); + stack.makeEmpty(); + + Assertions.assertTrue(stack.isEmpty()); + Assertions.assertThrows(IllegalStateException.class, stack::pop); + } + + @Test + void testPopEmptyStackThrowsException() { + Assertions.assertThrows(IllegalStateException.class, stack::pop); + } + + @Test + void testPeekEmptyStackThrowsException() { + Assertions.assertThrows(IllegalStateException.class, stack::peek); + } + + @Test + void testConstructorWithInvalidSizeThrowsException() { + Assertions.assertThrows(IllegalArgumentException.class, () -> new StackArray<>(0)); + Assertions.assertThrows(IllegalArgumentException.class, () -> new StackArray<>(-5)); + } + + @Test + void testDefaultConstructor() { + StackArray<Integer> defaultStack = new StackArray<>(); + Assertions.assertEquals(0, defaultStack.size()); + + defaultStack.push(1); + Assertions.assertEquals(1, defaultStack.size()); + } + + @Test + void testToString() { + stack.push(1); + stack.push(2); + stack.push(3); + Assertions.assertEquals("StackArray [1, 2, 3]", stack.toString()); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/stacks/StackOfLinkedListTest.java b/src/test/java/com/thealgorithms/datastructures/stacks/StackOfLinkedListTest.java new file mode 100644 index 000000000000..58af66bc38f4 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/stacks/StackOfLinkedListTest.java @@ -0,0 +1,121 @@ +package com.thealgorithms.datastructures.stacks; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.NoSuchElementException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class StackOfLinkedListTest { + + private LinkedListStack stack; + + @BeforeEach + public void setUp() { + stack = new LinkedListStack(); + } + + @Test + public void testPushAndPeek() { + stack.push(1); + stack.push(2); + stack.push(3); + + assertEquals(3, stack.peek(), "Peek should return the last pushed value"); + assertEquals(3, stack.getSize(), "Size should reflect the number of elements"); + } + + @Test + public void testPop() { + stack.push(1); + stack.push(2); + stack.push(3); + + assertEquals(3, stack.pop(), "Pop should return the last pushed value"); + assertEquals(2, stack.pop(), "Pop should return the next last pushed value"); + assertEquals(1, stack.pop(), "Pop should return the first pushed value"); + assertTrue(stack.isEmpty(), "Stack should be empty after popping all elements"); + } + + @Test + public void testPopEmptyStack() { + org.junit.jupiter.api.Assertions.assertThrows(NoSuchElementException.class, () -> stack.pop(), "Popping from an empty stack should throw NoSuchElementException"); + } + + @Test + public void testPeekEmptyStack() { + org.junit.jupiter.api.Assertions.assertThrows(NoSuchElementException.class, () -> stack.peek(), "Peeking into an empty stack should throw NoSuchElementException"); + } + + @Test + public void testIsEmpty() { + assertTrue(stack.isEmpty(), "Newly created stack should be empty"); + + stack.push(1); + assertFalse(stack.isEmpty(), "Stack should not be empty after pushing an element"); + + stack.pop(); + assertTrue(stack.isEmpty(), "Stack should be empty after popping the only element"); + } + + @Test + public void testToString() { + stack.push(1); + stack.push(2); + stack.push(3); + + assertEquals("3->2->1", stack.toString(), "String representation of stack should match the expected format"); + } + + @Test + public void testMultiplePushesAndPops() { + stack.push(5); + stack.push(10); + stack.push(15); + + assertEquals(15, stack.pop(), "Pop should return the last pushed value"); + assertEquals(10, stack.peek(), "Peek should return the new top value after popping"); + assertEquals(10, stack.pop(), "Pop should return the next last pushed value"); + assertEquals(5, stack.pop(), "Pop should return the first pushed value"); + assertTrue(stack.isEmpty(), "Stack should be empty after popping all elements"); + } + + @Test + public void testGetSize() { + assertEquals(0, stack.getSize(), "Size of an empty stack should be zero"); + stack.push(1); + stack.push(2); + assertEquals(2, stack.getSize(), "Size should reflect the number of elements"); + stack.pop(); + assertEquals(1, stack.getSize(), "Size should decrease with each pop"); + } + + @Test + public void testSizeAfterClearingStack() { + stack.push(1); + stack.push(2); + stack.push(3); + + // Manually clear the stack + while (!stack.isEmpty()) { + stack.pop(); + } + assertTrue(stack.isEmpty(), "Stack should be empty after clearing"); + assertEquals(0, stack.getSize(), "Size should be zero after clearing the stack"); + } + + @Test + public void testSequentialPushAndPop() { + for (int i = 1; i <= 100; i++) { + stack.push(i); + } + assertEquals(100, stack.getSize(), "Size should be 100 after pushing 100 elements"); + + for (int i = 100; i >= 1; i--) { + assertEquals(i, stack.pop(), "Popping should return values in LIFO order"); + } + assertTrue(stack.isEmpty(), "Stack should be empty after popping all elements"); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/AVLTreeTest.java b/src/test/java/com/thealgorithms/datastructures/trees/AVLTreeTest.java new file mode 100644 index 000000000000..6aa5dc9e22ed --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/AVLTreeTest.java @@ -0,0 +1,101 @@ +package com.thealgorithms.datastructures.trees; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class AVLTreeTest { + private AVLTree avlTree; + + @BeforeEach + public void setUp() { + avlTree = new AVLTree(); + } + + @Test + public void testInsert() { + assertTrue(avlTree.insert(10)); + assertTrue(avlTree.insert(20)); + assertTrue(avlTree.insert(5)); + assertFalse(avlTree.insert(10)); // Duplicate + } + + @Test + public void testSearch() { + avlTree.insert(15); + avlTree.insert(25); + assertTrue(avlTree.search(15)); + assertFalse(avlTree.search(30)); // Not in the tree + } + + @Test + public void testDeleteLeafNode() { + avlTree.insert(10); + avlTree.insert(20); + avlTree.insert(30); + avlTree.delete(30); + assertFalse(avlTree.search(30)); + } + + @Test + public void testDeleteNodeWithOneChild() { + avlTree.insert(20); + avlTree.insert(10); + avlTree.insert(30); + avlTree.delete(10); + assertFalse(avlTree.search(10)); + } + + @Test + public void testDeleteNodeWithTwoChildren() { + avlTree.insert(20); + avlTree.insert(10); + avlTree.insert(30); + avlTree.insert(25); + avlTree.delete(20); + assertFalse(avlTree.search(20)); + assertTrue(avlTree.search(30)); + assertTrue(avlTree.search(25)); + } + + @Test + public void testReturnBalance() { + avlTree.insert(10); + avlTree.insert(20); + avlTree.insert(5); + List<Integer> balances = avlTree.returnBalance(); + assertEquals(3, balances.size()); // There should be 3 nodes + assertEquals(0, balances.get(0)); // Balance for node 5 + assertEquals(0, balances.get(1)); // Balance for node 10 + assertEquals(0, balances.get(2)); // Balance for node 20 + } + + @Test + public void testInsertAndRebalance() { + avlTree.insert(30); + avlTree.insert(20); + avlTree.insert(10); // This should cause a right rotation + assertTrue(avlTree.search(20)); + assertTrue(avlTree.search(10)); + assertTrue(avlTree.search(30)); + } + + @Test + public void testComplexInsertionAndDeletion() { + avlTree.insert(30); + avlTree.insert(20); + avlTree.insert(10); + avlTree.insert(25); + avlTree.insert(5); + avlTree.insert(15); + + avlTree.delete(20); // Test deletion + assertFalse(avlTree.search(20)); + assertTrue(avlTree.search(30)); + assertTrue(avlTree.search(25)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/BSTFromSortedArrayTest.java b/src/test/java/com/thealgorithms/datastructures/trees/BSTFromSortedArrayTest.java new file mode 100644 index 000000000000..aab3b82c45eb --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/BSTFromSortedArrayTest.java @@ -0,0 +1,47 @@ +package com.thealgorithms.datastructures.trees; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * @author Albina Gimaletdinova on 20/04/2023 + */ +public class BSTFromSortedArrayTest { + @Test + public void testNullArray() { + BinaryTree.Node actualBST = BSTFromSortedArray.createBST(null); + Assertions.assertNull(actualBST); + } + + @Test + public void testEmptyArray() { + BinaryTree.Node actualBST = BSTFromSortedArray.createBST(new int[] {}); + Assertions.assertNull(actualBST); + } + + @Test + public void testSingleElementArray() { + BinaryTree.Node actualBST = BSTFromSortedArray.createBST(new int[] {Integer.MIN_VALUE}); + Assertions.assertTrue(CheckBinaryTreeIsValidBST.isBST(actualBST)); + } + + @Test + public void testCreateBSTFromSmallArray() { + BinaryTree.Node actualBST = BSTFromSortedArray.createBST(new int[] {1, 2, 3}); + Assertions.assertTrue(CheckBinaryTreeIsValidBST.isBST(actualBST)); + } + + @Test + public void testCreateBSTFromLongerArray() { + int[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + BinaryTree.Node actualBST = BSTFromSortedArray.createBST(array); + Assertions.assertTrue(CheckBinaryTreeIsValidBST.isBST(actualBST)); + } + + @Test + public void testShouldNotCreateBSTFromNonSortedArray() { + int[] array = {10, 2, 3, 4, 5, 6, 7, 8, 9, 1}; + BinaryTree.Node actualBST = BSTFromSortedArray.createBST(array); + Assertions.assertFalse(CheckBinaryTreeIsValidBST.isBST(actualBST)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/BSTIterativeTest.java b/src/test/java/com/thealgorithms/datastructures/trees/BSTIterativeTest.java new file mode 100644 index 000000000000..f47e2ad5285d --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/BSTIterativeTest.java @@ -0,0 +1,61 @@ +package com.thealgorithms.datastructures.trees; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * @author Albina Gimaletdinova on 23/04/2023 + */ +public class BSTIterativeTest { + @Test + public void testBSTIsCorrectlyConstructedFromOneNode() { + BSTIterative tree = new BSTIterative(); + tree.add(6); + + Assertions.assertTrue(CheckBinaryTreeIsValidBST.isBST(tree.getRoot())); + } + + @Test + public void testBSTIsCorrectlyCleanedAndEmpty() { + BSTIterative tree = new BSTIterative(); + + tree.add(6); + tree.remove(6); + + tree.add(12); + tree.add(1); + tree.add(2); + + tree.remove(1); + tree.remove(2); + tree.remove(12); + + Assertions.assertNull(tree.getRoot()); + } + + @Test + public void testBSTIsCorrectlyCleanedAndNonEmpty() { + BSTIterative tree = new BSTIterative(); + + tree.add(6); + tree.remove(6); + + tree.add(12); + tree.add(1); + tree.add(2); + + Assertions.assertTrue(CheckBinaryTreeIsValidBST.isBST(tree.getRoot())); + } + + @Test + public void testBSTIsCorrectlyConstructedFromMultipleNodes() { + BSTIterative tree = new BSTIterative(); + tree.add(7); + tree.add(1); + tree.add(5); + tree.add(100); + tree.add(50); + + Assertions.assertTrue(CheckBinaryTreeIsValidBST.isBST(tree.getRoot())); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/BSTRecursiveTest.java b/src/test/java/com/thealgorithms/datastructures/trees/BSTRecursiveTest.java new file mode 100644 index 000000000000..07bcd2aa15b6 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/BSTRecursiveTest.java @@ -0,0 +1,61 @@ +package com.thealgorithms.datastructures.trees; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * @author Albina Gimaletdinova on 06/05/2023 + */ +public class BSTRecursiveTest { + @Test + public void testBSTIsCorrectlyConstructedFromOneNode() { + BSTRecursive tree = new BSTRecursive(); + tree.add(6); + + Assertions.assertTrue(CheckBinaryTreeIsValidBST.isBST(tree.getRoot())); + } + + @Test + public void testBSTIsCorrectlyCleanedAndEmpty() { + BSTRecursive tree = new BSTRecursive(); + + tree.add(6); + tree.remove(6); + + tree.add(12); + tree.add(1); + tree.add(2); + + tree.remove(1); + tree.remove(2); + tree.remove(12); + + Assertions.assertNull(tree.getRoot()); + } + + @Test + public void testBSTIsCorrectlyCleanedAndNonEmpty() { + BSTRecursive tree = new BSTRecursive(); + + tree.add(6); + tree.remove(6); + + tree.add(12); + tree.add(1); + tree.add(2); + + Assertions.assertTrue(CheckBinaryTreeIsValidBST.isBST(tree.getRoot())); + } + + @Test + public void testBSTIsCorrectlyConstructedFromMultipleNodes() { + BSTRecursive tree = new BSTRecursive(); + tree.add(7); + tree.add(1); + tree.add(5); + tree.add(100); + tree.add(50); + + Assertions.assertTrue(CheckBinaryTreeIsValidBST.isBST(tree.getRoot())); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/BinaryTreeTest.java b/src/test/java/com/thealgorithms/datastructures/trees/BinaryTreeTest.java new file mode 100644 index 000000000000..d6581fb8c4e8 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/BinaryTreeTest.java @@ -0,0 +1,78 @@ +package com.thealgorithms.datastructures.trees; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the BinaryTree class. + */ +public class BinaryTreeTest { + + @Test + public void testInsertAndFind() { + BinaryTree tree = new BinaryTree(); + tree.put(3); + tree.put(5); + tree.put(7); + tree.put(9); + tree.put(12); + + Assertions.assertNotNull(tree.find(5), "Node with value 5 should exist"); + Assertions.assertEquals(5, tree.find(5).data, "Value of the found node should be 5"); + Assertions.assertEquals(7, tree.find(7).data, "Value of the found node should be 7"); + } + + @Test + public void testRemove() { + BinaryTree tree = new BinaryTree(); + tree.put(3); + tree.put(5); + tree.put(7); + tree.put(9); + tree.put(12); + tree.remove(3); + tree.remove(5); + tree.remove(7); + + Assertions.assertNotNull(tree.getRoot(), "Root should not be null after removals"); + if (tree.getRoot() != null) { + Assertions.assertEquals(9, tree.getRoot().data, "Root value should be 9 after removals"); + } else { + Assertions.fail("Root should not be null after removals, but it is."); + } + } + + @Test + public void testRemoveReturnValue() { + BinaryTree tree = new BinaryTree(); + tree.put(3); + tree.put(5); + tree.put(7); + tree.put(9); + tree.put(12); + + Assertions.assertTrue(tree.remove(9), "Removing existing node 9 should return true"); + Assertions.assertFalse(tree.remove(398745987), "Removing non-existing node should return false"); + } + + @Test + public void testTraversalMethods() { + BinaryTree tree = new BinaryTree(); + tree.put(3); + tree.put(5); + tree.put(7); + tree.put(9); + tree.put(12); + + // Testing traversal methods + tree.bfs(tree.getRoot()); + tree.inOrder(tree.getRoot()); + tree.preOrder(tree.getRoot()); + tree.postOrder(tree.getRoot()); + + Assertions.assertTrue(tree.remove(9), "Removing existing node 9 should return true"); + Assertions.assertFalse(tree.remove(398745987), "Removing non-existing node should return false"); + + Assertions.assertNotNull(tree.getRoot(), "Root should not be null after operations"); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/BoundaryTraversalTest.java b/src/test/java/com/thealgorithms/datastructures/trees/BoundaryTraversalTest.java new file mode 100644 index 000000000000..515dac88ce09 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/BoundaryTraversalTest.java @@ -0,0 +1,108 @@ +package com.thealgorithms.datastructures.trees; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.Test; + +/** + * + */ +public class BoundaryTraversalTest { + + @Test + public void testNullRoot() { + assertEquals(Collections.emptyList(), BoundaryTraversal.boundaryTraversal(null)); + assertEquals(Collections.emptyList(), BoundaryTraversal.iterativeBoundaryTraversal(null)); + } + + @Test + public void testSingleNodeTree() { + final BinaryTree.Node root = new BinaryTree.Node(1); + + List<Integer> expected = List.of(1); + + assertEquals(expected, BoundaryTraversal.boundaryTraversal(root)); + assertEquals(expected, BoundaryTraversal.iterativeBoundaryTraversal(root)); + } + + /* + 1 + / \ + 2 3 + / \ / \ + 4 5 6 7 + + */ + @Test + public void testCompleteBinaryTree() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {1, 2, 3, 4, 5, 6, 7}); + + List<Integer> expected = List.of(1, 2, 4, 5, 6, 7, 3); + + assertEquals(expected, BoundaryTraversal.boundaryTraversal(root)); + assertEquals(expected, BoundaryTraversal.iterativeBoundaryTraversal(root)); + } + + /* + 1 + / \ + 2 7 + / \ + 3 8 + \ / + 4 9 + / \ + 5 6 + / \ + 10 11 + */ + @Test + public void testBoundaryTraversal() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {1, 2, 7, 3, null, null, 8, null, 4, 9, null, 5, 6, 10, 11}); + + List<Integer> expected = List.of(1, 2, 3, 4, 5, 6, 10, 11, 9, 8, 7); + + assertEquals(expected, BoundaryTraversal.boundaryTraversal(root)); + assertEquals(expected, BoundaryTraversal.iterativeBoundaryTraversal(root)); + } + + /* + 1 + / + 2 + / + 3 + / + 4 + */ + @Test + public void testLeftSkewedTree() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {1, 2, null, 3, null, 4, null}); + + List<Integer> expected = List.of(1, 2, 3, 4); + + assertEquals(expected, BoundaryTraversal.boundaryTraversal(root)); + assertEquals(expected, BoundaryTraversal.iterativeBoundaryTraversal(root)); + } + + /* + 5 + \ + 6 + \ + 7 + \ + 8 + */ + @Test + public void testRightSkewedTree() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {5, null, 6, null, 7, null, 8}); + + List<Integer> expected = List.of(5, 6, 7, 8); + + assertEquals(expected, BoundaryTraversal.boundaryTraversal(root)); + assertEquals(expected, BoundaryTraversal.iterativeBoundaryTraversal(root)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/CeilInBinarySearchTreeTest.java b/src/test/java/com/thealgorithms/datastructures/trees/CeilInBinarySearchTreeTest.java new file mode 100644 index 000000000000..779c68137e42 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/CeilInBinarySearchTreeTest.java @@ -0,0 +1,51 @@ +package com.thealgorithms.datastructures.trees; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import com.thealgorithms.datastructures.trees.BinaryTree.Node; +import org.junit.jupiter.api.Test; + +public class CeilInBinarySearchTreeTest { + + @Test + public void testRootNull() { + assertNull(CeilInBinarySearchTree.getCeil(null, 9)); + } + + @Test + public void testKeyPresentRootIsCeil() { + final Node root = TreeTestUtils.createTree(new Integer[] {100, 10, 200}); + assertEquals(100, CeilInBinarySearchTree.getCeil(root, 100).data); + } + + @Test + public void testKeyPresentLeafIsCeil() { + final Node root = TreeTestUtils.createTree(new Integer[] {100, 10, 200}); + assertEquals(10, CeilInBinarySearchTree.getCeil(root, 10).data); + } + + @Test + public void testKeyAbsentRootIsCeil() { + final Node root = TreeTestUtils.createTree(new Integer[] {100, 10, 200, 5, 50, 150, 300}); + assertEquals(100, CeilInBinarySearchTree.getCeil(root, 75).data); + } + + @Test + public void testKeyAbsentLeafIsCeil() { + final Node root = TreeTestUtils.createTree(new Integer[] {100, 10, 200, 5, 50, 150, 300}); + assertEquals(50, CeilInBinarySearchTree.getCeil(root, 40).data); + } + + @Test + public void testKeyAbsentLeftMostNodeIsCeil() { + final Node root = TreeTestUtils.createTree(new Integer[] {100, 10, 200, 5, 50, 150, 300}); + assertEquals(5, CeilInBinarySearchTree.getCeil(root, 1).data); + } + + @Test + public void testKeyAbsentCeilIsNull() { + final Node root = TreeTestUtils.createTree(new Integer[] {100, 10, 200, 5, 50, 150, 300}); + assertNull(CeilInBinarySearchTree.getCeil(root, 400)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/CheckBinaryTreeIsValidBSTTest.java b/src/test/java/com/thealgorithms/datastructures/trees/CheckBinaryTreeIsValidBSTTest.java new file mode 100644 index 000000000000..bf7b946143f9 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/CheckBinaryTreeIsValidBSTTest.java @@ -0,0 +1,61 @@ +package com.thealgorithms.datastructures.trees; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +/** + * @author Albina Gimaletdinova on 17/02/2023 + */ +public class CheckBinaryTreeIsValidBSTTest { + @Test + public void testRootNull() { + assertTrue(CheckBinaryTreeIsValidBST.isBST(null)); + } + + @Test + public void testOneNode() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {Integer.MIN_VALUE}); + assertTrue(CheckBinaryTreeIsValidBST.isBST(root)); + } + + /* + 9 + / \ + 7 13 + /\ / \ + 3 8 10 20 + */ + @Test + public void testBinaryTreeIsBST() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {9, 7, 13, 3, 8, 10, 20}); + assertTrue(CheckBinaryTreeIsValidBST.isBST(root)); + } + + /* + 9 + / \ + 7 13 + /\ / \ + 3 8 10 13 <--- duplicated node + */ + @Test + public void testBinaryTreeWithDuplicatedNodesIsNotBST() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {9, 7, 13, 3, 8, 10, 13}); + assertFalse(CheckBinaryTreeIsValidBST.isBST(root)); + } + + /* + 9 + / \ + 7 13 + /\ / \ + 3 8 10 12 <---- violates BST rule, needs to be more than 13 (parent node) + */ + @Test + public void testBinaryTreeIsNotBST() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {9, 7, 13, 3, 8, 10, 12}); + assertFalse(CheckBinaryTreeIsValidBST.isBST(root)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/CheckIfBinaryTreeBalancedTest.java b/src/test/java/com/thealgorithms/datastructures/trees/CheckIfBinaryTreeBalancedTest.java new file mode 100644 index 000000000000..0eb234d618ed --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/CheckIfBinaryTreeBalancedTest.java @@ -0,0 +1,84 @@ +package com.thealgorithms.datastructures.trees; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +/** + * Test check both implemented ways, iterative and recursive algorithms. + * + * @author Albina Gimaletdinova on 26/06/2023 + */ +public class CheckIfBinaryTreeBalancedTest { + @Test + public void testRootNull() { + assertTrue(CheckIfBinaryTreeBalanced.isBalancedRecursive(null)); + assertTrue(CheckIfBinaryTreeBalanced.isBalancedIterative(null)); + } + + @Test + public void testOneNode() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {Integer.MIN_VALUE}); + assertTrue(CheckIfBinaryTreeBalanced.isBalancedRecursive(root)); + assertTrue(CheckIfBinaryTreeBalanced.isBalancedIterative(root)); + } + + /* + 9 <-- Math.abs(height(left) - height(right)) == 0 + / \ + 7 13 + /\ / \ + 3 8 10 20 +*/ + @Test + public void testBinaryTreeIsBalancedEqualSubtreeHeights() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {9, 7, 13, 3, 8, 10, 20}); + assertTrue(CheckIfBinaryTreeBalanced.isBalancedRecursive(root)); + assertTrue(CheckIfBinaryTreeBalanced.isBalancedIterative(root)); + } + + /* + 9 <-- Math.abs(height(left) - height(right)) == 1 + / \ + 7 13 + /\ + 3 8 +*/ + @Test + public void testBinaryTreeIsBalancedWithDifferentHeights() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {9, 7, 13, 3, 8}); + assertTrue(CheckIfBinaryTreeBalanced.isBalancedRecursive(root)); + assertTrue(CheckIfBinaryTreeBalanced.isBalancedIterative(root)); + } + + /* + 9 <-- only left tree exists, Math.abs(height(left) - height(right)) > 1 + / + 7 + /\ + 3 8 + */ + @Test + public void testBinaryTreeNotBalanced() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {9, 7, null, 3, 8}); + assertFalse(CheckIfBinaryTreeBalanced.isBalancedRecursive(root)); + assertFalse(CheckIfBinaryTreeBalanced.isBalancedIterative(root)); + } + + /* + 9 <-- Math.abs(height(left) - height(right)) > 1 + / \ + 7 13 + /\ + 3 8 + / + 11 +*/ + @Test + public void testBinaryTreeNotBalancedBecauseLeftTreeNotBalanced() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {9, 7, 13, 3, 8, null, null, 11}); + assertFalse(CheckIfBinaryTreeBalanced.isBalancedRecursive(root)); + assertFalse(CheckIfBinaryTreeBalanced.isBalancedIterative(root)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/CheckTreeIsSymmetricTest.java b/src/test/java/com/thealgorithms/datastructures/trees/CheckTreeIsSymmetricTest.java new file mode 100644 index 000000000000..a610a32aa91f --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/CheckTreeIsSymmetricTest.java @@ -0,0 +1,35 @@ +package com.thealgorithms.datastructures.trees; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +/** + * @author kumanoit on 10/10/22 IST 1:02 AM + */ +public class CheckTreeIsSymmetricTest { + + @Test + public void testRootNull() { + assertTrue(CheckTreeIsSymmetric.isSymmetric(null)); + } + + @Test + public void testSingleNodeTree() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {100}); + assertTrue(CheckTreeIsSymmetric.isSymmetric(root)); + } + + @Test + public void testSymmetricTree() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {1, 2, 2, 3, 4, 4, 3}); + assertTrue(CheckTreeIsSymmetric.isSymmetric(root)); + } + + @Test + public void testNonSymmetricTree() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {1, 2, 2, 3, 5, 4, 3}); + assertFalse(CheckTreeIsSymmetric.isSymmetric(root)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/CreateBinaryTreeFromInorderPreorderTest.java b/src/test/java/com/thealgorithms/datastructures/trees/CreateBinaryTreeFromInorderPreorderTest.java new file mode 100644 index 000000000000..10f7aa667230 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/CreateBinaryTreeFromInorderPreorderTest.java @@ -0,0 +1,102 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.Arrays; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * @author Albina Gimaletdinova on 14/05/2023 + */ +public class CreateBinaryTreeFromInorderPreorderTest { + @Test + public void testOnNullArraysShouldReturnNullTree() { + // when + BinaryTree.Node root = CreateBinaryTreeFromInorderPreorder.createTree(null, null); + BinaryTree.Node rootOpt = CreateBinaryTreeFromInorderPreorder.createTreeOptimized(null, null); + + // then + Assertions.assertNull(root); + Assertions.assertNull(rootOpt); + } + + @Test + public void testOnEmptyArraysShouldCreateNullTree() { + // given + Integer[] preorder = {}; + Integer[] inorder = {}; + + // when + BinaryTree.Node root = CreateBinaryTreeFromInorderPreorder.createTree(preorder, inorder); + BinaryTree.Node rootOpt = CreateBinaryTreeFromInorderPreorder.createTreeOptimized(preorder, inorder); + + // then + Assertions.assertNull(root); + Assertions.assertNull(rootOpt); + } + + @Test + public void testOnSingleNodeTreeShouldCreateCorrectTree() { + // given + Integer[] preorder = {1}; + Integer[] inorder = {1}; + + // when + BinaryTree.Node root = CreateBinaryTreeFromInorderPreorder.createTree(preorder, inorder); + BinaryTree.Node rootOpt = CreateBinaryTreeFromInorderPreorder.createTreeOptimized(preorder, inorder); + + // then + checkTree(preorder, inorder, root); + checkTree(preorder, inorder, rootOpt); + } + + @Test + public void testOnRightSkewedTreeShouldCreateCorrectTree() { + // given + Integer[] preorder = {1, 2, 3, 4}; + Integer[] inorder = {1, 2, 3, 4}; + + // when + BinaryTree.Node root = CreateBinaryTreeFromInorderPreorder.createTree(preorder, inorder); + BinaryTree.Node rootOpt = CreateBinaryTreeFromInorderPreorder.createTreeOptimized(preorder, inorder); + + // then + checkTree(preorder, inorder, root); + checkTree(preorder, inorder, rootOpt); + } + + @Test + public void testOnLeftSkewedTreeShouldCreateCorrectTree() { + // given + Integer[] preorder = {1, 2, 3, 4}; + Integer[] inorder = {4, 3, 2, 1}; + + // when + BinaryTree.Node root = CreateBinaryTreeFromInorderPreorder.createTree(preorder, inorder); + BinaryTree.Node rootOpt = CreateBinaryTreeFromInorderPreorder.createTreeOptimized(preorder, inorder); + + // then + checkTree(preorder, inorder, root); + checkTree(preorder, inorder, rootOpt); + } + + @Test + public void testOnNormalTreeShouldCreateCorrectTree() { + // given + Integer[] preorder = {3, 9, 20, 15, 7}; + Integer[] inorder = {9, 3, 15, 20, 7}; + + // when + BinaryTree.Node root = CreateBinaryTreeFromInorderPreorder.createTree(preorder, inorder); + BinaryTree.Node rootOpt = CreateBinaryTreeFromInorderPreorder.createTreeOptimized(preorder, inorder); + + // then + checkTree(preorder, inorder, root); + checkTree(preorder, inorder, rootOpt); + } + + private static void checkTree(Integer[] preorder, Integer[] inorder, BinaryTree.Node root) { + Assertions.assertNotNull(root); + Assertions.assertEquals(PreOrderTraversal.iterativePreOrder(root), Arrays.asList(preorder)); + Assertions.assertEquals(InorderTraversal.iterativeInorder(root), Arrays.asList(inorder)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/InorderTraversalTest.java b/src/test/java/com/thealgorithms/datastructures/trees/InorderTraversalTest.java new file mode 100644 index 000000000000..e65beb891606 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/InorderTraversalTest.java @@ -0,0 +1,52 @@ +package com.thealgorithms.datastructures.trees; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.Test; + +/** + * @author Albina Gimaletdinova on 21/02/2023 + */ +public class InorderTraversalTest { + @Test + public void testNullRoot() { + assertEquals(Collections.emptyList(), InorderTraversal.recursiveInorder(null)); + assertEquals(Collections.emptyList(), InorderTraversal.iterativeInorder(null)); + } + + /* + 1 + / \ + 2 3 + /\ /\ + 4 5 6 7 + */ + @Test + public void testRecursiveInorder() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {1, 2, 3, 4, 5, 6, 7}); + List<Integer> expected = List.of(4, 2, 5, 1, 6, 3, 7); + + assertEquals(expected, InorderTraversal.recursiveInorder(root)); + assertEquals(expected, InorderTraversal.iterativeInorder(root)); + } + + /* + 5 + \ + 6 + \ + 7 + \ + 8 + */ + @Test + public void testRecursiveInorderNonBalanced() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {5, null, 6, null, 7, null, 8}); + List<Integer> expected = List.of(5, 6, 7, 8); + + assertEquals(expected, InorderTraversal.recursiveInorder(root)); + assertEquals(expected, InorderTraversal.iterativeInorder(root)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/KDTreeTest.java b/src/test/java/com/thealgorithms/datastructures/trees/KDTreeTest.java new file mode 100644 index 000000000000..49babb033e81 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/KDTreeTest.java @@ -0,0 +1,63 @@ +package com.thealgorithms.datastructures.trees; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class KDTreeTest { + + KDTree.Point pointOf(int x, int y) { + return new KDTree.Point(new int[] {x, y}); + } + + @Test + void findMin() { + int[][] coordinates = { + {30, 40}, + {5, 25}, + {70, 70}, + {10, 12}, + {50, 30}, + {35, 45}, + }; + KDTree kdTree = new KDTree(coordinates); + + assertEquals(5, kdTree.findMin(0).getCoordinate(0)); + assertEquals(12, kdTree.findMin(1).getCoordinate(1)); + } + + @Test + void delete() { + int[][] coordinates = { + {30, 40}, + {5, 25}, + {70, 70}, + {10, 12}, + {50, 30}, + {35, 45}, + }; + KDTree kdTree = new KDTree(coordinates); + + kdTree.delete(pointOf(30, 40)); + assertEquals(35, kdTree.getRoot().getPoint().getCoordinate(0)); + assertEquals(45, kdTree.getRoot().getPoint().getCoordinate(1)); + } + + @Test + void findNearest() { + int[][] coordinates = { + {2, 3}, + {5, 4}, + {9, 6}, + {4, 7}, + {8, 1}, + {7, 2}, + }; + KDTree kdTree = new KDTree(coordinates); + + assertEquals(pointOf(7, 2), kdTree.findNearest(pointOf(7, 2))); + assertEquals(pointOf(8, 1), kdTree.findNearest(pointOf(8, 1))); + assertEquals(pointOf(2, 3), kdTree.findNearest(pointOf(1, 1))); + assertEquals(pointOf(5, 4), kdTree.findNearest(pointOf(5, 5))); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/LazySegmentTreeTest.java b/src/test/java/com/thealgorithms/datastructures/trees/LazySegmentTreeTest.java new file mode 100644 index 000000000000..7daf8c6313cd --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/LazySegmentTreeTest.java @@ -0,0 +1,61 @@ +package com.thealgorithms.datastructures.trees; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class LazySegmentTreeTest { + + @Test + void build() { + int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + LazySegmentTree lazySegmentTree = new LazySegmentTree(arr); + assertEquals(55, lazySegmentTree.getRoot().getValue()); + assertEquals(15, lazySegmentTree.getRoot().getLeft().getValue()); + assertEquals(40, lazySegmentTree.getRoot().getRight().getValue()); + } + + @Test + void update() { + int[] arr = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + LazySegmentTree lazySegmentTree = new LazySegmentTree(arr); + assertEquals(10, lazySegmentTree.getRoot().getValue()); + + lazySegmentTree.updateRange(0, 2, 1); + assertEquals(12, lazySegmentTree.getRoot().getValue()); + + lazySegmentTree.updateRange(1, 3, 1); + assertEquals(14, lazySegmentTree.getRoot().getValue()); + + lazySegmentTree.updateRange(6, 8, 1); + assertEquals(16, lazySegmentTree.getRoot().getValue()); + + lazySegmentTree.updateRange(3, 9, 1); + assertEquals(22, lazySegmentTree.getRoot().getValue()); + } + + @Test + void get() { + int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + LazySegmentTree lazySegmentTree = new LazySegmentTree(arr); + assertEquals(55, lazySegmentTree.getRange(0, 10)); + assertEquals(3, lazySegmentTree.getRange(0, 2)); + assertEquals(19, lazySegmentTree.getRange(8, 10)); + assertEquals(44, lazySegmentTree.getRange(1, 9)); + } + + @Test + void updateAndGet() { + int[] arr = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + LazySegmentTree lazySegmentTree = new LazySegmentTree(arr); + + for (int i = 0; i < 10; i++) { + for (int j = i + 1; j < 10; j++) { + lazySegmentTree.updateRange(i, j, 1); + assertEquals(j - i, lazySegmentTree.getRange(i, j)); + lazySegmentTree.updateRange(i, j, -1); + assertEquals(0, lazySegmentTree.getRange(i, j)); + } + } + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/LevelOrderTraversalTest.java b/src/test/java/com/thealgorithms/datastructures/trees/LevelOrderTraversalTest.java new file mode 100644 index 000000000000..ee807bccc5f1 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/LevelOrderTraversalTest.java @@ -0,0 +1,51 @@ +package com.thealgorithms.datastructures.trees; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.Test; + +/** + * @author Albina Gimaletdinova on 08/02/2023 + */ +public class LevelOrderTraversalTest { + @Test + public void testRootNull() { + assertEquals(Collections.emptyList(), LevelOrderTraversal.traverse(null)); + } + + @Test + public void testSingleNodeTree() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {50}); + assertEquals(List.of(List.of(50)), LevelOrderTraversal.traverse(root)); + } + + /* + 1 + / \ + 2 3 + /\ /\ + 4 5 6 7 + */ + @Test + public void testLevelOrderTraversalCompleteTree() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {1, 2, 3, 4, 5, 6, 7}); + assertEquals(List.of(List.of(1), List.of(2, 3), List.of(4, 5, 6, 7)), LevelOrderTraversal.traverse(root)); + } + + /* + 1 + / \ + 2 3 + /\ /\ + 4 5 6 7 + / \ + 8 9 + */ + @Test + public void testLevelOrderTraversalDifferentHeight() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {1, 2, 3, 4, 5, 6, 7, null, null, 8, null, null, 9}); + assertEquals(List.of(List.of(1), List.of(2, 3), List.of(4, 5, 6, 7), List.of(8, 9)), LevelOrderTraversal.traverse(root)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/PostOrderTraversalTest.java b/src/test/java/com/thealgorithms/datastructures/trees/PostOrderTraversalTest.java new file mode 100644 index 000000000000..3acc7f07c087 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/PostOrderTraversalTest.java @@ -0,0 +1,54 @@ +package com.thealgorithms.datastructures.trees; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.Test; + +/** + * Given tree is traversed in a 'post-order' way: LEFT -> RIGHT -> ROOT. + * + * @author Albina Gimaletdinova on 21/02/2023 + */ +public class PostOrderTraversalTest { + @Test + public void testNullRoot() { + assertEquals(Collections.emptyList(), PostOrderTraversal.recursivePostOrder(null)); + assertEquals(Collections.emptyList(), PostOrderTraversal.iterativePostOrder(null)); + } + + /* + 1 + / \ + 2 3 + /\ /\ + 4 5 6 7 + */ + @Test + public void testPostOrder() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {1, 2, 3, 4, 5, 6, 7}); + List<Integer> expected = List.of(4, 5, 2, 6, 7, 3, 1); + + assertEquals(expected, PostOrderTraversal.recursivePostOrder(root)); + assertEquals(expected, PostOrderTraversal.iterativePostOrder(root)); + } + + /* + 5 + \ + 6 + \ + 7 + \ + 8 + */ + @Test + public void testPostOrderNonBalanced() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {5, null, 6, null, 7, null, 8}); + List<Integer> expected = List.of(8, 7, 6, 5); + + assertEquals(expected, PostOrderTraversal.recursivePostOrder(root)); + assertEquals(expected, PostOrderTraversal.iterativePostOrder(root)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/PreOrderTraversalTest.java b/src/test/java/com/thealgorithms/datastructures/trees/PreOrderTraversalTest.java new file mode 100644 index 000000000000..b194da01485b --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/PreOrderTraversalTest.java @@ -0,0 +1,52 @@ +package com.thealgorithms.datastructures.trees; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.Test; + +/** + * @author Albina Gimaletdinova on 17/02/2023 + */ +public class PreOrderTraversalTest { + @Test + public void testNullRoot() { + assertEquals(Collections.emptyList(), PreOrderTraversal.recursivePreOrder(null)); + assertEquals(Collections.emptyList(), PreOrderTraversal.iterativePreOrder(null)); + } + + /* + 1 + / \ + 2 3 + /\ /\ + 4 5 6 7 + */ + @Test + public void testRecursivePreOrder() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {1, 2, 3, 4, 5, 6, 7}); + List<Integer> expected = List.of(1, 2, 4, 5, 3, 6, 7); + + assertEquals(expected, PreOrderTraversal.recursivePreOrder(root)); + assertEquals(expected, PreOrderTraversal.iterativePreOrder(root)); + } + + /* + 5 + \ + 6 + \ + 7 + \ + 8 + */ + @Test + public void testRecursivePreOrderNonBalanced() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {5, null, 6, null, 7, null, 8}); + List<Integer> expected = List.of(5, 6, 7, 8); + + assertEquals(expected, PreOrderTraversal.recursivePreOrder(root)); + assertEquals(expected, PreOrderTraversal.iterativePreOrder(root)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/QuadTreeTest.java b/src/test/java/com/thealgorithms/datastructures/trees/QuadTreeTest.java new file mode 100644 index 000000000000..62b86da214db --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/QuadTreeTest.java @@ -0,0 +1,58 @@ +package com.thealgorithms.datastructures.trees; + +import java.util.List; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class QuadTreeTest { + int quadTreeCapacity = 4; + BoundingBox boundingBox = new BoundingBox(new Point(0, 0), 500); + QuadTree quadTree = new QuadTree(boundingBox, quadTreeCapacity); + + @Test + public void testNullPointInsertIntoQuadTree() { + Assertions.assertFalse(quadTree.insert(null)); + } + + @Test + public void testInsertIntoQuadTree() { + Assertions.assertTrue(quadTree.insert(new Point(10, -10))); + Assertions.assertTrue(quadTree.insert(new Point(-10, 10))); + Assertions.assertTrue(quadTree.insert(new Point(-10, -10))); + Assertions.assertTrue(quadTree.insert(new Point(10, 10))); + Assertions.assertFalse(quadTree.insert(new Point(1050, 1050))); + } + + @Test + public void testInsertIntoQuadTreeAndSubDivide() { + Assertions.assertTrue(quadTree.insert(new Point(10, -10))); + Assertions.assertTrue(quadTree.insert(new Point(-10, 10))); + Assertions.assertTrue(quadTree.insert(new Point(-10, -10))); + Assertions.assertTrue(quadTree.insert(new Point(10, 10))); + Assertions.assertTrue(quadTree.insert(new Point(-100, 100))); + Assertions.assertTrue(quadTree.insert(new Point(100, -101))); + Assertions.assertTrue(quadTree.insert(new Point(-100, -100))); + Assertions.assertTrue(quadTree.insert(new Point(100, 100))); + } + + @Test + public void testQueryInQuadTree() { + quadTree.insert(new Point(10, -10)); + quadTree.insert(new Point(-10, 10)); + quadTree.insert(new Point(-10, -10)); + quadTree.insert(new Point(10, 10)); + quadTree.insert(new Point(-100, 100)); + quadTree.insert(new Point(100, -100)); + quadTree.insert(new Point(-100, -100)); + quadTree.insert(new Point(100, 100)); + + List<Point> points = quadTree.query(new BoundingBox(new Point(0, 0), 100)); + Assertions.assertEquals(8, points.size()); + + points = quadTree.query(new BoundingBox(new Point(5, 5), 5)); + Assertions.assertEquals(1, points.size()); + + points = quadTree.query(new BoundingBox(new Point(-200, -200), 5)); + Assertions.assertEquals(0, points.size()); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/SameTreesCheckTest.java b/src/test/java/com/thealgorithms/datastructures/trees/SameTreesCheckTest.java new file mode 100644 index 000000000000..440222880517 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/SameTreesCheckTest.java @@ -0,0 +1,69 @@ +package com.thealgorithms.datastructures.trees; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +/** + * @author Albina Gimaletdinova on 12/01/2023 + */ +public class SameTreesCheckTest { + @Test + public void testBothRootsAreNull() { + assertTrue(SameTreesCheck.check(null, null)); + } + + @Test + public void testOneRootIsNull() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {100}); + assertFalse(SameTreesCheck.check(root, null)); + } + + @Test + public void testSingleNodeTreesAreSame() { + final BinaryTree.Node p = TreeTestUtils.createTree(new Integer[] {100}); + final BinaryTree.Node q = TreeTestUtils.createTree(new Integer[] {100}); + assertTrue(SameTreesCheck.check(p, q)); + } + + /* + 1 1 + / \ / \ + 2 3 2 3 + /\ /\ /\ /\ + 4 5 6 7 4 5 6 7 + */ + @Test + public void testSameTreesIsSuccessful() { + final BinaryTree.Node p = TreeTestUtils.createTree(new Integer[] {1, 2, 3, 4, 5, 6, 7}); + final BinaryTree.Node q = TreeTestUtils.createTree(new Integer[] {1, 2, 3, 4, 5, 6, 7}); + assertTrue(SameTreesCheck.check(p, q)); + } + + /* + 1 1 + / \ / \ + 2 3 2 3 + /\ /\ /\ / + 4 5 6 7 4 5 6 + */ + @Test + public void testSameTreesFails() { + final BinaryTree.Node p = TreeTestUtils.createTree(new Integer[] {1, 2, 3, 4, 5, 6, 7}); + final BinaryTree.Node q = TreeTestUtils.createTree(new Integer[] {1, 2, 3, 4, 5, 6}); + assertFalse(SameTreesCheck.check(p, q)); + } + + /* + 1 1 + / \ + 2 2 + */ + @Test + public void testTreesWithDifferentStructure() { + final BinaryTree.Node p = TreeTestUtils.createTree(new Integer[] {1, 2}); + final BinaryTree.Node q = TreeTestUtils.createTree(new Integer[] {1, null, 2}); + assertFalse(SameTreesCheck.check(p, q)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/SplayTreeTest.java b/src/test/java/com/thealgorithms/datastructures/trees/SplayTreeTest.java new file mode 100644 index 000000000000..d520be94e7f3 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/SplayTreeTest.java @@ -0,0 +1,113 @@ +package com.thealgorithms.datastructures.trees; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class SplayTreeTest { + + @ParameterizedTest + @MethodSource("traversalStrategies") + public void testTraversal(SplayTree.TreeTraversal traversal, List<Integer> expected) { + SplayTree tree = createComplexTree(); + List<Integer> result = tree.traverse(traversal); + assertEquals(expected, result); + } + + @ParameterizedTest + @MethodSource("valuesToTest") + public void testSearch(int value) { + SplayTree tree = createComplexTree(); + assertTrue(tree.search(value)); + } + + @ParameterizedTest + @MethodSource("valuesToTest") + public void testDelete(int value) { + SplayTree tree = createComplexTree(); + assertTrue(tree.search(value)); + tree.delete(value); + assertFalse(tree.search(value)); + } + + @ParameterizedTest + @MethodSource("nonExistentValues") + public void testSearchNonExistent(int value) { + SplayTree tree = createComplexTree(); + assertFalse(tree.search(value)); + } + + @ParameterizedTest + @MethodSource("nonExistentValues") + public void testDeleteNonExistent(int value) { + SplayTree tree = createComplexTree(); + tree.delete(value); + assertFalse(tree.search(value)); + } + + @ParameterizedTest + @MethodSource("valuesToTest") + public void testDeleteThrowsExceptionForEmptyTree(int value) { + SplayTree tree = new SplayTree(); + assertThrows(SplayTree.EmptyTreeException.class, () -> tree.delete(value)); + } + + @ParameterizedTest + @MethodSource("valuesToTest") + public void testInsertThrowsExceptionForDuplicateKeys(int value) { + SplayTree tree = createComplexTree(); + assertThrows(SplayTree.DuplicateKeyException.class, () -> tree.insert(value)); + } + + @ParameterizedTest + @MethodSource("valuesToTest") + public void testSearchInEmptyTree(int value) { + SplayTree tree = new SplayTree(); + assertFalse(tree.search(value)); + } + + private static Stream<Object[]> traversalStrategies() { + return Stream.of(new Object[] {SplayTree.IN_ORDER, Arrays.asList(5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90)}, new Object[] {SplayTree.PRE_ORDER, Arrays.asList(15, 5, 10, 80, 70, 45, 25, 20, 35, 30, 40, 55, 50, 65, 60, 75, 90, 85)}, + new Object[] {SplayTree.POST_ORDER, Arrays.asList(10, 5, 20, 30, 40, 35, 25, 50, 60, 65, 55, 45, 75, 70, 85, 90, 80, 15)}); + } + + private static Stream<Integer> valuesToTest() { + return Stream.of(5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90); + } + + private static Stream<Integer> nonExistentValues() { + return Stream.of(0, 100, 42, 58); + } + + private SplayTree createComplexTree() { + SplayTree tree = new SplayTree(); + + tree.insert(50); + tree.insert(30); + tree.insert(40); + tree.insert(70); + tree.insert(60); + tree.insert(20); + tree.insert(80); + tree.insert(10); + tree.insert(25); + tree.insert(35); + tree.insert(45); + tree.insert(55); + tree.insert(65); + tree.insert(75); + tree.insert(85); + tree.insert(5); + tree.insert(90); + tree.insert(15); + + return tree; + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/TreapTest.java b/src/test/java/com/thealgorithms/datastructures/trees/TreapTest.java new file mode 100644 index 000000000000..09ada594faca --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/TreapTest.java @@ -0,0 +1,62 @@ +package com.thealgorithms.datastructures.trees; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import org.junit.jupiter.api.Test; + +public class TreapTest { + + @Test + public void searchAndFound() { + Treap treap = new Treap(); + treap.insert(5); + treap.insert(9); + treap.insert(6); + treap.insert(2); + treap.insert(3); + treap.insert(8); + treap.insert(1); + assertEquals(5, treap.search(5).value); + } + + @Test + public void searchAndNotFound() { + Treap treap = new Treap(); + treap.insert(5); + treap.insert(9); + treap.insert(6); + treap.insert(2); + treap.insert(3); + treap.insert(8); + treap.insert(1); + assertEquals(null, treap.search(4)); + } + + @Test + public void lowerBound() { + Treap treap = new Treap(); + treap.insert(5); + treap.insert(9); + treap.insert(6); + treap.insert(2); + treap.insert(3); + treap.insert(8); + treap.insert(1); + assertEquals(5, treap.lowerBound(4).value); + } + + @Test + public void size() { + Treap treap = new Treap(); + treap.insert(5); + treap.insert(9); + treap.insert(6); + treap.insert(2); + treap.insert(3); + treap.insert(8); + treap.insert(1); + assertEquals(7, treap.size()); + assertFalse(treap.isEmpty()); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/TreeTestUtils.java b/src/test/java/com/thealgorithms/datastructures/trees/TreeTestUtils.java new file mode 100644 index 000000000000..9628e86b9bff --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/TreeTestUtils.java @@ -0,0 +1,46 @@ +package com.thealgorithms.datastructures.trees; + +import com.thealgorithms.datastructures.trees.BinaryTree.Node; +import java.util.LinkedList; +import java.util.Queue; + +public final class TreeTestUtils { + private TreeTestUtils() { + } + + /** + * Creates a binary tree with given values + * + * @param values: Level order representation of tree + * @return Root of a binary tree + */ + public static Node createTree(final Integer[] values) { + if (values == null || values.length == 0 || values[0] == null) { + throw new IllegalArgumentException("Values array should not be empty or null."); + } + final Node root = new Node(values[0]); + final Queue<Node> queue = new LinkedList<>(); + queue.add(root); + int end = 1; + while (end < values.length) { + final Node node = queue.remove(); + if (values[end] == null) { + node.left = null; + } else { + node.left = new Node(values[end]); + queue.add(node.left); + } + end++; + if (end < values.length) { + if (values[end] == null) { + node.right = null; + } else { + node.right = new Node(values[end]); + queue.add(node.right); + } + } + end++; + } + return root; + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/TrieTest.java b/src/test/java/com/thealgorithms/datastructures/trees/TrieTest.java new file mode 100644 index 000000000000..9348118bb343 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/TrieTest.java @@ -0,0 +1,95 @@ +package com.thealgorithms.datastructures.trees; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class TrieTest { + private static final List<String> WORDS = List.of("Apple", "App", "app", "APPLE"); + + private Trie trie; + + @BeforeEach + public void setUp() { + trie = new Trie(); + } + + @Test + public void testInsertAndSearchBasic() { + String word = "hello"; + trie.insert(word); + assertTrue(trie.search(word), "Search should return true for an inserted word."); + } + + @Test + public void testSearchNonExistentWord() { + String word = "world"; + assertFalse(trie.search(word), "Search should return false for a non-existent word."); + } + + @Test + public void testInsertAndSearchMultipleWords() { + String word1 = "cat"; + String word2 = "car"; + trie.insert(word1); + trie.insert(word2); + + assertTrue(trie.search(word1), "Search should return true for an inserted word."); + assertTrue(trie.search(word2), "Search should return true for another inserted word."); + assertFalse(trie.search("dog"), "Search should return false for a word not in the Trie."); + } + + @Test + public void testDeleteExistingWord() { + String word = "remove"; + trie.insert(word); + assertTrue(trie.delete(word), "Delete should return true for an existing word."); + assertFalse(trie.search(word), "Search should return false after deletion."); + } + + @Test + public void testDeleteNonExistentWord() { + String word = "nonexistent"; + assertFalse(trie.delete(word), "Delete should return false for a non-existent word."); + } + + @Test + public void testInsertAndSearchPrefix() { + String prefix = "pre"; + String word = "prefix"; + trie.insert(prefix); + trie.insert(word); + + assertTrue(trie.search(prefix), "Search should return true for an inserted prefix."); + assertTrue(trie.search(word), "Search should return true for a word with the prefix."); + assertFalse(trie.search("pref"), "Search should return false for a prefix that is not a full word."); + } + + @Test + public void testCountWords() { + Trie trie = createTrie(); + assertEquals(WORDS.size(), trie.countWords(), "Count words should return the correct number of words."); + } + + @Test + public void testStartsWithPrefix() { + Trie trie = createTrie(); + assertTrue(trie.startsWithPrefix("App"), "Starts with prefix should return true."); + } + + @Test + public void testCountWordsWithPrefix() { + Trie trie = createTrie(); + assertEquals(2, trie.countWordsWithPrefix("App"), "Count words with prefix should return 2."); + } + + private Trie createTrie() { + Trie trie = new Trie(); + WORDS.forEach(trie::insert); + return trie; + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/VerticalOrderTraversalTest.java b/src/test/java/com/thealgorithms/datastructures/trees/VerticalOrderTraversalTest.java new file mode 100644 index 000000000000..cc89c9813fa8 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/VerticalOrderTraversalTest.java @@ -0,0 +1,51 @@ +package com.thealgorithms.datastructures.trees; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.Test; + +/** + * @author Albina Gimaletdinova on 13/01/2023 + */ +public class VerticalOrderTraversalTest { + @Test + public void testRootNull() { + assertEquals(Collections.emptyList(), VerticalOrderTraversal.verticalTraversal(null)); + } + + @Test + public void testSingleNodeTree() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {50}); + assertEquals(List.of(50), VerticalOrderTraversal.verticalTraversal(root)); + } + + /* + 1 + / \ + 2 3 + /\ /\ + 4 5 6 7 + */ + @Test + public void testVerticalTraversalCompleteTree() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {1, 2, 3, 4, 5, 6, 7}); + assertEquals(List.of(4, 2, 1, 5, 6, 3, 7), VerticalOrderTraversal.verticalTraversal(root)); + } + + /* + 1 + / \ + 2 3 + /\ /\ + 4 5 6 7 + / \ + 8 9 + */ + @Test + public void testVerticalTraversalDifferentHeight() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {1, 2, 3, 4, 5, 6, 7, null, null, 8, null, null, 9}); + assertEquals(List.of(4, 2, 8, 1, 5, 6, 3, 9, 7), VerticalOrderTraversal.verticalTraversal(root)); + } +} diff --git a/src/test/java/com/thealgorithms/datastructures/trees/ZigzagTraversalTest.java b/src/test/java/com/thealgorithms/datastructures/trees/ZigzagTraversalTest.java new file mode 100644 index 000000000000..eb5c0e3ce4df --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/trees/ZigzagTraversalTest.java @@ -0,0 +1,51 @@ +package com.thealgorithms.datastructures.trees; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.Test; + +/** + * @author Albina Gimaletdinova on 11/01/2023 + */ +public class ZigzagTraversalTest { + @Test + public void testRootNull() { + assertEquals(Collections.emptyList(), ZigzagTraversal.traverse(null)); + } + + @Test + public void testSingleNodeTree() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {50}); + assertEquals(List.of(List.of(50)), ZigzagTraversal.traverse(root)); + } + + /* + 1 + / \ + 2 3 + /\ /\ + 4 5 6 7 + */ + @Test + public void testZigzagTraversalCompleteTree() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {1, 2, 3, 4, 5, 6, 7}); + assertEquals(List.of(List.of(1), List.of(3, 2), List.of(4, 5, 6, 7)), ZigzagTraversal.traverse(root)); + } + + /* + 1 + / \ + 2 3 + /\ /\ + 4 5 6 7 + / \ + 8 9 + */ + @Test + public void testZigzagTraversalDifferentHeight() { + final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {1, 2, 3, 4, 5, 6, 7, null, null, 8, null, null, 9}); + assertEquals(List.of(List.of(1), List.of(3, 2), List.of(4, 5, 6, 7), List.of(9, 8)), ZigzagTraversal.traverse(root)); + } +} diff --git a/src/test/java/com/thealgorithms/divideandconquer/BinaryExponentiationTest.java b/src/test/java/com/thealgorithms/divideandconquer/BinaryExponentiationTest.java new file mode 100644 index 000000000000..ccada6e061e1 --- /dev/null +++ b/src/test/java/com/thealgorithms/divideandconquer/BinaryExponentiationTest.java @@ -0,0 +1,38 @@ +package com.thealgorithms.divideandconquer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class BinaryExponentiationTest { + + @Test + public void testCalculatePower() { + assertEquals(1, BinaryExponentiation.calculatePower(1, 10000000)); + assertEquals(1, BinaryExponentiation.calculatePower(1, 100000000)); + assertEquals(1, BinaryExponentiation.calculatePower(1, 1000000000)); + assertEquals(1, BinaryExponentiation.calculatePower(1, 10000000000L)); + assertEquals(1, BinaryExponentiation.calculatePower(1, 100000000000L)); + assertEquals(1, BinaryExponentiation.calculatePower(1, 1000000000000L)); + assertEquals(1, BinaryExponentiation.calculatePower(1, 10000000000000L)); + assertEquals(1, BinaryExponentiation.calculatePower(1, 100000000000000L)); + assertEquals(1, BinaryExponentiation.calculatePower(1, 1000000000000000L)); + assertEquals(1, BinaryExponentiation.calculatePower(1, 10000000000000000L)); + assertEquals(1, BinaryExponentiation.calculatePower(1, 100000000000000000L)); + } + + @Test + public void testPower() { + assertEquals(1, new BinaryExponentiation().power(1, 10000000)); + assertEquals(1, new BinaryExponentiation().power(1, 100000000)); + assertEquals(1, new BinaryExponentiation().power(1, 1000000000)); + assertEquals(1, new BinaryExponentiation().power(1, 10000000000L)); + assertEquals(1, new BinaryExponentiation().power(1, 100000000000L)); + assertEquals(1, new BinaryExponentiation().power(1, 1000000000000L)); + assertEquals(1, new BinaryExponentiation().power(1, 10000000000000L)); + assertEquals(1, new BinaryExponentiation().power(1, 100000000000000L)); + assertEquals(1, new BinaryExponentiation().power(1, 1000000000000000L)); + assertEquals(1, new BinaryExponentiation().power(1, 10000000000000000L)); + assertEquals(1, new BinaryExponentiation().power(1, 100000000000000000L)); + } +} diff --git a/src/test/java/com/thealgorithms/divideandconquer/ClosestPairTest.java b/src/test/java/com/thealgorithms/divideandconquer/ClosestPairTest.java new file mode 100644 index 000000000000..38784228d68e --- /dev/null +++ b/src/test/java/com/thealgorithms/divideandconquer/ClosestPairTest.java @@ -0,0 +1,75 @@ +package com.thealgorithms.divideandconquer; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; + +public class ClosestPairTest { + + @Test + public void testBuildLocation() { + ClosestPair cp = new ClosestPair(1); + ClosestPair.Location point = cp.buildLocation(3.0, 4.0); + assertNotNull(point); + assertEquals(3.0, point.x); + assertEquals(4.0, point.y); + } + + @Test + public void testCreateLocation() { + ClosestPair cp = new ClosestPair(5); + ClosestPair.Location[] locations = cp.createLocation(5); + assertNotNull(locations); + assertEquals(5, locations.length); + } + + @Test + public void testXPartition() { + ClosestPair cp = new ClosestPair(5); + ClosestPair.Location[] points = new ClosestPair.Location[5]; + points[0] = cp.buildLocation(2.0, 3.0); + points[1] = cp.buildLocation(5.0, 1.0); + points[2] = cp.buildLocation(1.0, 6.0); + points[3] = cp.buildLocation(4.0, 7.0); + points[4] = cp.buildLocation(3.0, 2.0); + + int pivotIndex = cp.xPartition(points, 0, 4); + assertEquals(2, pivotIndex); + assertEquals(2.0, points[0].x); + assertEquals(1.0, points[1].x); + assertEquals(3.0, points[2].x); + assertEquals(4.0, points[3].x); + assertEquals(5.0, points[4].x); + } + + @Test + public void testYPartition() { + ClosestPair cp = new ClosestPair(5); + ClosestPair.Location[] points = new ClosestPair.Location[5]; + points[0] = cp.buildLocation(2.0, 3.0); + points[1] = cp.buildLocation(5.0, 1.0); + points[2] = cp.buildLocation(1.0, 6.0); + points[3] = cp.buildLocation(4.0, 7.0); + points[4] = cp.buildLocation(3.0, 2.0); + + int pivotIndex = cp.yPartition(points, 0, 4); + assertEquals(1, pivotIndex); + assertEquals(2.0, points[1].y); + assertEquals(3.0, points[4].y); + assertEquals(1.0, points[0].y); + assertEquals(6.0, points[2].y); + assertEquals(7.0, points[3].y); + } + + @Test + public void testBruteForce() { + ClosestPair cp = new ClosestPair(2); + ClosestPair.Location loc1 = cp.buildLocation(1.0, 2.0); + ClosestPair.Location loc2 = cp.buildLocation(4.0, 6.0); + + ClosestPair.Location[] locations = new ClosestPair.Location[] {loc1, loc2}; + double result = cp.bruteForce(locations); + assertEquals(5.0, result, 0.01); + } +} diff --git a/src/test/java/com/thealgorithms/divideandconquer/CountingInversionsTest.java b/src/test/java/com/thealgorithms/divideandconquer/CountingInversionsTest.java new file mode 100644 index 000000000000..d12614d6fd06 --- /dev/null +++ b/src/test/java/com/thealgorithms/divideandconquer/CountingInversionsTest.java @@ -0,0 +1,32 @@ +package com.thealgorithms.divideandconquer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class CountingInversionsTest { + + @Test + public void testCountInversions() { + int[] arr = {2, 3, 8, 6, 1}; + assertEquals(5, CountingInversions.countInversions(arr)); + } + + @Test + public void testNoInversions() { + int[] arr = {1, 2, 3, 4, 5}; + assertEquals(0, CountingInversions.countInversions(arr)); + } + + @Test + public void testSingleElement() { + int[] arr = {1}; + assertEquals(0, CountingInversions.countInversions(arr)); + } + + @Test + public void testAllInversions() { + int[] arr = {5, 4, 3, 2, 1}; + assertEquals(10, CountingInversions.countInversions(arr)); + } +} diff --git a/src/test/java/com/thealgorithms/divideandconquer/MedianOfTwoSortedArraysTest.java b/src/test/java/com/thealgorithms/divideandconquer/MedianOfTwoSortedArraysTest.java new file mode 100644 index 000000000000..6cfbc2379600 --- /dev/null +++ b/src/test/java/com/thealgorithms/divideandconquer/MedianOfTwoSortedArraysTest.java @@ -0,0 +1,41 @@ +package com.thealgorithms.divideandconquer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class MedianOfTwoSortedArraysTest { + + @ParameterizedTest + @MethodSource("provideTestCases") + void testFindMedianSortedArrays(int[] nums1, int[] nums2, double expectedMedian) { + assertEquals(expectedMedian, MedianOfTwoSortedArrays.findMedianSortedArrays(nums1, nums2)); + } + + private static Stream<Arguments> provideTestCases() { + return Stream.of( + // Test case 1: Arrays of equal length + Arguments.of(new int[] {1, 3}, new int[] {2, 4}, 2.5), + + // Test case 2: Arrays of different lengths + Arguments.of(new int[] {1, 3}, new int[] {2}, 2.0), + + // Test case 3: Arrays with even total length + Arguments.of(new int[] {1, 2, 8}, new int[] {3, 4, 5, 6, 7}, 4.5), + + // Test case 4: Arrays with odd total length + Arguments.of(new int[] {1, 2, 8}, new int[] {3, 4, 5}, 3.5), + + // Test case 5: Single element arrays + Arguments.of(new int[] {1}, new int[] {3}, 2.0), + + // Test case 6: Empty arrays + Arguments.of(new int[] {}, new int[] {0}, 0.0), + + // Test case 7: Same element arrays + Arguments.of(new int[] {2, 2, 2}, new int[] {2, 2, 2}, 2.0)); + } +} diff --git a/src/test/java/com/thealgorithms/divideandconquer/SkylineAlgorithmTest.java b/src/test/java/com/thealgorithms/divideandconquer/SkylineAlgorithmTest.java new file mode 100644 index 000000000000..f85515110b70 --- /dev/null +++ b/src/test/java/com/thealgorithms/divideandconquer/SkylineAlgorithmTest.java @@ -0,0 +1,102 @@ +package com.thealgorithms.divideandconquer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class SkylineAlgorithmTest { + + private SkylineAlgorithm skylineAlgorithm; + + @BeforeEach + public void setUp() { + skylineAlgorithm = new SkylineAlgorithm(); + } + + @Test + public void testProduceSubSkyLinesSinglePoint() { + // Test with a single point + ArrayList<SkylineAlgorithm.Point> points = new ArrayList<>(); + points.add(new SkylineAlgorithm.Point(1, 10)); + + ArrayList<SkylineAlgorithm.Point> result = skylineAlgorithm.produceSubSkyLines(points); + + assertEquals(1, result.size()); + assertEquals(1, result.get(0).getX()); + assertEquals(10, result.get(0).getY()); + } + + @Test + public void testProduceSubSkyLinesTwoPoints() { + // Test with two points, one dominated by the other + ArrayList<SkylineAlgorithm.Point> points = new ArrayList<>(); + points.add(new SkylineAlgorithm.Point(1, 10)); + points.add(new SkylineAlgorithm.Point(1, 5)); + + ArrayList<SkylineAlgorithm.Point> result = skylineAlgorithm.produceSubSkyLines(points); + + assertEquals(1, result.size()); + assertEquals(1, result.get(0).getX()); + assertEquals(5, result.get(0).getY()); + } + + @Test + public void testProduceSubSkyLinesMultiplePoints() { + // Test with more than two points + ArrayList<SkylineAlgorithm.Point> points = new ArrayList<>(); + points.add(new SkylineAlgorithm.Point(1, 10)); + points.add(new SkylineAlgorithm.Point(2, 15)); + points.add(new SkylineAlgorithm.Point(3, 5)); + points.add(new SkylineAlgorithm.Point(4, 20)); + + ArrayList<SkylineAlgorithm.Point> result = skylineAlgorithm.produceSubSkyLines(points); + + assertEquals(2, result.size()); + + // Assert the correct points in skyline + assertEquals(1, result.get(0).getX()); + assertEquals(10, result.get(0).getY()); + assertEquals(3, result.get(1).getX()); + assertEquals(5, result.get(1).getY()); + } + + @Test + public void testProduceFinalSkyLine() { + // Test merging two skylines + ArrayList<SkylineAlgorithm.Point> left = new ArrayList<>(); + left.add(new SkylineAlgorithm.Point(1, 10)); + left.add(new SkylineAlgorithm.Point(2, 5)); + + ArrayList<SkylineAlgorithm.Point> right = new ArrayList<>(); + right.add(new SkylineAlgorithm.Point(3, 8)); + right.add(new SkylineAlgorithm.Point(4, 3)); + + ArrayList<SkylineAlgorithm.Point> result = skylineAlgorithm.produceFinalSkyLine(left, right); + + assertEquals(3, result.size()); + + // Assert the correct points in the final skyline + assertEquals(1, result.get(0).getX()); + assertEquals(10, result.get(0).getY()); + assertEquals(2, result.get(1).getX()); + assertEquals(5, result.get(1).getY()); + assertEquals(4, result.get(2).getX()); + assertEquals(3, result.get(2).getY()); + } + + @Test + public void testXComparator() { + // Test the XComparator used for sorting the points + SkylineAlgorithm.XComparator comparator = new SkylineAlgorithm().new XComparator(); + + SkylineAlgorithm.Point p1 = new SkylineAlgorithm.Point(1, 10); + SkylineAlgorithm.Point p2 = new SkylineAlgorithm.Point(2, 5); + + // Check if the XComparator sorts points by their x-value + assertEquals(-1, comparator.compare(p1, p2)); // p1.x < p2.x + assertEquals(1, comparator.compare(p2, p1)); // p2.x > p1.x + assertEquals(0, comparator.compare(p1, new SkylineAlgorithm.Point(1, 15))); // p1.x == p2.x + } +} diff --git a/src/test/java/com/thealgorithms/divideandconquer/StrassenMatrixMultiplicationTest.java b/src/test/java/com/thealgorithms/divideandconquer/StrassenMatrixMultiplicationTest.java new file mode 100644 index 000000000000..1ec45a863e1a --- /dev/null +++ b/src/test/java/com/thealgorithms/divideandconquer/StrassenMatrixMultiplicationTest.java @@ -0,0 +1,41 @@ +package com.thealgorithms.divideandconquer; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +class StrassenMatrixMultiplicationTest { + + StrassenMatrixMultiplication smm = new StrassenMatrixMultiplication(); + + // Strassen Matrix Multiplication can only be allplied to matrices of size 2^n + // and has to be a Square Matrix + + @Test + public void strassenMatrixMultiplicationTest2x2() { + int[][] a = {{1, 2}, {3, 4}}; + int[][] b = {{5, 6}, {7, 8}}; + int[][] expResult = {{19, 22}, {43, 50}}; + int[][] actResult = smm.multiply(a, b); + assertArrayEquals(expResult, actResult); + } + + @Test + void strassenMatrixMultiplicationTest4x4() { + int[][] a = {{1, 2, 5, 4}, {9, 3, 0, 6}, {4, 6, 3, 1}, {0, 2, 0, 6}}; + int[][] b = {{1, 0, 4, 1}, {1, 2, 0, 2}, {0, 3, 1, 3}, {1, 8, 1, 2}}; + int[][] expResult = {{7, 51, 13, 28}, {18, 54, 42, 27}, {11, 29, 20, 27}, {8, 52, 6, 16}}; + int[][] actResult = smm.multiply(a, b); + assertArrayEquals(expResult, actResult); + } + + @Test + + void strassenMatrixMultiplicationTestNegetiveNumber4x4() { + int[][] a = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; + int[][] b = {{1, -2, -3, 4}, {4, -3, -2, 1}, {5, -6, -7, 8}, {8, -7, -6, -5}}; + int[][] expResult = {{56, -54, -52, 10}, {128, -126, -124, 42}, {200, -198, -196, 74}, {272, -270, -268, 106}}; + int[][] actResult = smm.multiply(a, b); + assertArrayEquals(expResult, actResult); + } +} diff --git a/src/test/java/com/thealgorithms/divideandconquer/TilingProblemTest.java b/src/test/java/com/thealgorithms/divideandconquer/TilingProblemTest.java new file mode 100644 index 000000000000..720e425f5ea3 --- /dev/null +++ b/src/test/java/com/thealgorithms/divideandconquer/TilingProblemTest.java @@ -0,0 +1,15 @@ +package com.thealgorithms.divideandconquer; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +public class TilingProblemTest { + + @Test + public void testTilingSize2() { + int[][] expected = {{1, 1}, {1, 0}}; + int[][] result = TilingProblem.solveTiling(2, 1, 1); + assertArrayEquals(expected, result); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/AbbreviationTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/AbbreviationTest.java new file mode 100644 index 000000000000..4e36edbd7774 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/AbbreviationTest.java @@ -0,0 +1,44 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class AbbreviationTest { + + @ParameterizedTest + @MethodSource("provideTestCases") + public void testAbbreviation(String a, String b, boolean expected) { + assertEquals(expected, Abbreviation.abbr(a, b)); + } + + private static Stream<Arguments> provideTestCases() { + return Stream.of( + // Example test case from problem description + Arguments.of("daBcd", "ABC", Boolean.TRUE), + + // Test case where transformation is impossible + Arguments.of("dBcd", "ABC", Boolean.FALSE), + + // Test case with exact match (all uppercase) + Arguments.of("ABC", "ABC", Boolean.TRUE), + + // Test case where input string contains all required letters plus extra lowercase letters + Arguments.of("aAbBcC", "ABC", Boolean.TRUE), + + // Test case with only lowercase letters in input + Arguments.of("abcd", "ABCD", Boolean.TRUE), + + // Test case with an empty second string (b) + Arguments.of("abc", "", Boolean.TRUE), + + // Test case with an empty first string (a) but non-empty second string (b) + Arguments.of("", "A", Boolean.FALSE), + + // Complex case with interleaved letters + Arguments.of("daBcAbCd", "ABCD", Boolean.FALSE)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/AllConstructTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/AllConstructTest.java new file mode 100644 index 000000000000..012876921c15 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/AllConstructTest.java @@ -0,0 +1,40 @@ +package com.thealgorithms.dynamicprogramming; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class AllConstructTest { + + @Test + public void testAllConstructBasic() { + List<List<String>> expected = singletonList(Arrays.asList("he", "l", "l", "o")); + List<List<String>> result = AllConstruct.allConstruct("hello", Arrays.asList("he", "l", "o")); + assertEquals(expected, result); + } + + @Test + public void testAllConstructMultipleWays() { + List<List<String>> expected = Arrays.asList(Arrays.asList("purp", "le"), Arrays.asList("p", "ur", "p", "le")); + List<List<String>> result = AllConstruct.allConstruct("purple", Arrays.asList("purp", "p", "ur", "le", "purpl")); + assertEquals(expected, result); + } + + @Test + public void testAllConstructNoWays() { + List<List<String>> expected = emptyList(); + List<List<String>> result = AllConstruct.allConstruct("abcdef", Arrays.asList("gh", "ijk")); + assertEquals(expected, result); + } + + @Test + public void testAllConstructEmptyTarget() { + List<List<String>> expected = singletonList(emptyList()); + List<List<String>> result = AllConstruct.allConstruct("", Arrays.asList("a", "b", "c")); + assertEquals(expected, result); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/AssignmentUsingBitmaskTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/AssignmentUsingBitmaskTest.java new file mode 100644 index 000000000000..eadc43ce59c5 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/AssignmentUsingBitmaskTest.java @@ -0,0 +1,55 @@ +package com.thealgorithms.dynamicprogramming; + +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public final class AssignmentUsingBitmaskTest { + + @Test + public void testCountNoOfWays() { + int totalTasks = 5; + + List<List<Integer>> taskPerformed = Arrays.asList(Arrays.asList(1, 3, 4), Arrays.asList(1, 2, 5), Arrays.asList(3, 4)); + + AssignmentUsingBitmask assignment = new AssignmentUsingBitmask(taskPerformed, totalTasks); + int ways = assignment.countNoOfWays(); + assertEquals(10, ways); + } + + @Test + public void testNoPossibleAssignments() { + int totalTasks = 3; + + List<List<Integer>> taskPerformed = Arrays.asList(singletonList(2), singletonList(3)); + + AssignmentUsingBitmask assignment = new AssignmentUsingBitmask(taskPerformed, totalTasks); + int ways = assignment.countNoOfWays(); + assertEquals(1, ways); + } + + @Test + public void testSinglePersonMultipleTasks() { + int totalTasks = 3; + + List<List<Integer>> taskPerformed = singletonList(Arrays.asList(1, 2, 3)); + + AssignmentUsingBitmask assignment = new AssignmentUsingBitmask(taskPerformed, totalTasks); + int ways = assignment.countNoOfWays(); + assertEquals(3, ways); + } + + @Test + public void testMultiplePeopleSingleTask() { + int totalTasks = 1; + + List<List<Integer>> taskPerformed = Arrays.asList(singletonList(1), singletonList(1)); + + AssignmentUsingBitmask assignment = new AssignmentUsingBitmask(taskPerformed, totalTasks); + int ways = assignment.countNoOfWays(); + assertEquals(0, ways); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/BoardPathTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/BoardPathTest.java new file mode 100644 index 000000000000..a704620e92da --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/BoardPathTest.java @@ -0,0 +1,33 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class BoardPathTest { + + @ParameterizedTest + @MethodSource("provideTestCases") + void testBpR(int start, int end, int expected) { + assertEquals(expected, BoardPath.bpR(start, end)); + } + + @ParameterizedTest + @MethodSource("provideTestCases") + void testBpRS(int start, int end, int expected) { + assertEquals(expected, BoardPath.bpRS(start, end, new int[end + 1])); + } + + @ParameterizedTest + @MethodSource("provideTestCases") + void testBpIS(int start, int end, int expected) { + assertEquals(expected, BoardPath.bpIS(start, end, new int[end + 1])); + } + + private static Stream<Arguments> provideTestCases() { + return Stream.of(Arguments.of(0, 10, 492), Arguments.of(0, 5, 16), Arguments.of(0, 6, 32), Arguments.of(0, 3, 4), Arguments.of(0, 1, 1)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/BoundaryFillTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/BoundaryFillTest.java new file mode 100644 index 000000000000..4aa412731a10 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/BoundaryFillTest.java @@ -0,0 +1,66 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class BoundaryFillTest { + + private int[][] image; + + @BeforeEach + void setUp() { + image = new int[][] {{0, 0, 0, 0, 0, 0, 0}, {0, 3, 3, 3, 3, 0, 0}, {0, 3, 0, 0, 3, 0, 0}, {0, 3, 0, 0, 3, 3, 3}, {0, 3, 3, 3, 0, 0, 3}, {0, 0, 0, 3, 0, 0, 3}, {0, 0, 0, 3, 3, 3, 3}}; + } + + @Test + void testGetPixel() { + assertEquals(3, BoundaryFill.getPixel(image, 1, 1)); + assertEquals(0, BoundaryFill.getPixel(image, 2, 2)); + assertEquals(3, BoundaryFill.getPixel(image, 4, 3)); + } + + @Test + void testPutPixel() { + BoundaryFill.putPixel(image, 2, 2, 5); + assertEquals(5, BoundaryFill.getPixel(image, 2, 2)); + + BoundaryFill.putPixel(image, 0, 0, 7); + assertEquals(7, BoundaryFill.getPixel(image, 0, 0)); + } + + @Test + void testBoundaryFill() { + BoundaryFill.boundaryFill(image, 2, 2, 5, 3); + + int[][] expectedImage = {{0, 0, 0, 0, 0, 0, 0}, {0, 3, 3, 3, 3, 0, 0}, {0, 3, 5, 5, 3, 0, 0}, {0, 3, 5, 5, 3, 3, 3}, {0, 3, 3, 3, 5, 5, 3}, {0, 0, 0, 3, 5, 5, 3}, {0, 0, 0, 3, 3, 3, 3}}; + + for (int i = 0; i < image.length; i++) { + assertArrayEquals(expectedImage[i], image[i]); + } + } + + @Test + void testBoundaryFillEdgeCase() { + BoundaryFill.boundaryFill(image, 1, 1, 3, 3); + + int[][] expectedImage = {{0, 0, 0, 0, 0, 0, 0}, {0, 3, 3, 3, 3, 0, 0}, {0, 3, 0, 0, 3, 0, 0}, {0, 3, 0, 0, 3, 3, 3}, {0, 3, 3, 3, 0, 0, 3}, {0, 0, 0, 3, 0, 0, 3}, {0, 0, 0, 3, 3, 3, 3}}; + + for (int i = 0; i < image.length; i++) { + assertArrayEquals(expectedImage[i], image[i]); + } + } + + @Test + void testBoundaryFillInvalidCoordinates() { + BoundaryFill.boundaryFill(image, -1, -1, 5, 3); + + int[][] expectedImage = {{0, 0, 0, 0, 0, 0, 0}, {0, 3, 3, 3, 3, 0, 0}, {0, 3, 0, 0, 3, 0, 0}, {0, 3, 0, 0, 3, 3, 3}, {0, 3, 3, 3, 0, 0, 3}, {0, 0, 0, 3, 0, 0, 3}, {0, 0, 0, 3, 3, 3, 3}}; + + for (int i = 0; i < image.length; i++) { + assertArrayEquals(expectedImage[i], image[i]); + } + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/BruteForceKnapsackTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/BruteForceKnapsackTest.java new file mode 100644 index 000000000000..ef96f16e04f7 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/BruteForceKnapsackTest.java @@ -0,0 +1,96 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class BruteForceKnapsackTest { + + @Test + void testKnapSackBasicCase() { + int[] val = {60, 100, 120}; + int[] wt = {10, 20, 30}; + int w = 50; + int n = val.length; + + // The expected result for this case is 220 (items 2 and 3 are included) + assertEquals(220, BruteForceKnapsack.knapSack(w, wt, val, n)); + } + + @Test + void testKnapSackNoItems() { + int[] val = {}; + int[] wt = {}; + int w = 50; + int n = val.length; + + // With no items, the maximum value should be 0 + assertEquals(0, BruteForceKnapsack.knapSack(w, wt, val, n)); + } + + @Test + void testKnapSackZeroCapacity() { + int[] val = {60, 100, 120}; + int[] wt = {10, 20, 30}; + int w = 0; + int n = val.length; + + // With a knapsack of 0 capacity, no items can be included, so the value is 0 + assertEquals(0, BruteForceKnapsack.knapSack(w, wt, val, n)); + } + + @Test + void testKnapSackSingleItemFits() { + int[] val = {100}; + int[] wt = {20}; + int w = 30; + int n = val.length; + + // Only one item, and it fits in the knapsack, so the result is 100 + assertEquals(100, BruteForceKnapsack.knapSack(w, wt, val, n)); + } + + @Test + void testKnapSackSingleItemDoesNotFit() { + int[] val = {100}; + int[] wt = {20}; + int w = 10; + int n = val.length; + + // Single item does not fit in the knapsack, so the result is 0 + assertEquals(0, BruteForceKnapsack.knapSack(w, wt, val, n)); + } + + @Test + void testKnapSackAllItemsFit() { + int[] val = {20, 30, 40}; + int[] wt = {1, 2, 3}; + int w = 6; + int n = val.length; + + // All items fit into the knapsack, so the result is the sum of all values (20 + 30 + 40 = 90) + assertEquals(90, BruteForceKnapsack.knapSack(w, wt, val, n)); + } + + @Test + void testKnapSackNoneFit() { + int[] val = {100, 200, 300}; + int[] wt = {100, 200, 300}; + int w = 50; + int n = val.length; + + // None of the items fit into the knapsack, so the result is 0 + assertEquals(0, BruteForceKnapsack.knapSack(w, wt, val, n)); + } + + @Test + void testKnapSackSomeItemsFit() { + int[] val = {60, 100, 120}; + int[] wt = {10, 20, 30}; + int w = 40; + int n = val.length; + + // Here, only the 2nd and 1st items should be included for a total value of 160 + assertEquals(180, BruteForceKnapsack.knapSack(w, wt, val, n)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/CatalanNumberTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/CatalanNumberTest.java new file mode 100644 index 000000000000..a065bb8c2a10 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/CatalanNumberTest.java @@ -0,0 +1,14 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class CatalanNumberTest { + + @Test + public void testCatalanNumber() { + assertEquals(42, CatalanNumber.findNthCatalan(5)); + assertEquals(16796, CatalanNumber.findNthCatalan(10)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/ClimbStairsTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/ClimbStairsTest.java new file mode 100644 index 000000000000..1f2de4a11b62 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/ClimbStairsTest.java @@ -0,0 +1,33 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class ClimbStairsTest { + + @Test + void climbStairsTestForTwo() { + assertEquals(2, ClimbingStairs.numberOfWays(2)); + } + + @Test + void climbStairsTestForZero() { + assertEquals(0, ClimbingStairs.numberOfWays(0)); + } + + @Test + void climbStairsTestForOne() { + assertEquals(1, ClimbingStairs.numberOfWays(1)); + } + + @Test + void climbStairsTestForFive() { + assertEquals(8, ClimbingStairs.numberOfWays(5)); + } + + @Test + void climbStairsTestForThree() { + assertEquals(3, ClimbingStairs.numberOfWays(3)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/CoinChangeTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/CoinChangeTest.java new file mode 100644 index 000000000000..10bc6600ae1e --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/CoinChangeTest.java @@ -0,0 +1,80 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class CoinChangeTest { + + @Test + void testChangeBasic() { + int amount = 12; + int[] coins = {2, 4, 5}; + + assertEquals(5, CoinChange.change(coins, amount)); + } + + @Test + void testChangeNoCoins() { + int amount = 12; + int[] coins = {}; + + assertEquals(0, CoinChange.change(coins, amount)); + } + + @Test + void testChangeNoAmount() { + int amount = 0; + int[] coins = {2, 4, 5}; + + assertEquals(1, CoinChange.change(coins, amount)); + } + + @Test + void testChangeImpossibleAmount() { + int amount = 3; + int[] coins = {2, 4, 5}; + + assertEquals(0, CoinChange.change(coins, amount)); + } + + @Test + void testMinimumCoinsBasic() { + int amount = 12; + int[] coins = {2, 4, 5}; + + assertEquals(3, CoinChange.minimumCoins(coins, amount)); + } + + @Test + void testMinimumCoinsNoCoins() { + int amount = 12; + int[] coins = {}; + + assertEquals(Integer.MAX_VALUE, CoinChange.minimumCoins(coins, amount)); + } + + @Test + void testMinimumCoinsNoAmount() { + int amount = 0; + int[] coins = {2, 4, 5}; + + assertEquals(0, CoinChange.minimumCoins(coins, amount)); + } + + @Test + void testMinimumCoinsImpossibleAmount() { + int amount = 3; + int[] coins = {2, 4, 5}; + + assertEquals(Integer.MAX_VALUE, CoinChange.minimumCoins(coins, amount)); + } + + @Test + void testMinimumCoinsExactAmount() { + int amount = 10; + int[] coins = {1, 5, 10}; + + assertEquals(1, CoinChange.minimumCoins(coins, amount)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/CountFriendsPairingTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/CountFriendsPairingTest.java new file mode 100644 index 000000000000..765daba5f69f --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/CountFriendsPairingTest.java @@ -0,0 +1,50 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class CountFriendsPairingTest { + + @Test + void testSmallCase() { + int n = 5; + int[] expectedGolombSequence = {1, 2, 2, 3, 3}; + + assertTrue(CountFriendsPairing.countFriendsPairing(n, expectedGolombSequence)); + } + + @Test + void testMismatchSequence() { + int n = 5; + int[] wrongSequence = {1, 2, 2, 2, 3}; // An incorrect sequence + + assertFalse(CountFriendsPairing.countFriendsPairing(n, wrongSequence)); + } + + @Test + void testLargerCase() { + int n = 10; + int[] expectedGolombSequence = {1, 2, 2, 3, 3, 4, 4, 4, 5, 5}; + + assertTrue(CountFriendsPairing.countFriendsPairing(n, expectedGolombSequence)); + } + + @Test + void testEdgeCaseSingleElement() { + int n = 1; + int[] expectedGolombSequence = {1}; + + assertTrue(CountFriendsPairing.countFriendsPairing(n, expectedGolombSequence)); + } + + @Test + void testEmptySequence() { + int n = 0; + int[] emptySequence = {}; + + // Test the case where n is 0 (should handle this gracefully) + assertTrue(CountFriendsPairing.countFriendsPairing(n, emptySequence)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/DPTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/DPTest.java new file mode 100644 index 000000000000..e3bea67fe269 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/DPTest.java @@ -0,0 +1,56 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class DPTest { + + @Test + void testSumLessThanMinimumFaceValue() { + // When the sum is less than the minimum possible face value + // There are 0 ways to achieve the sum + assertEquals(0, DP.findWays(4, 2, 1)); // 4 faces, 2 dice, sum = 1 + } + + @Test + void testTwoDiceWithSumEqualToTwo() { + // When there are 2 dice and the sum is equal to the number of dice + // The only way is to have both dice showing 1 + assertEquals(1, DP.findWays(2, 2, 2)); // 2 faces, 2 dice, sum = 2 + } + + @Test + void testTwoDiceWithSumThree() { + // When there are 2 dice and the sum is equal to 3 + // Possible combinations are (1,2) and (2,1) + assertEquals(2, DP.findWays(2, 2, 3)); // 2 faces, 2 dice, sum = 3 + } + + @Test + void testThreeDiceWithSumEight() { + // Test for 3 dice, each having 6 faces + // Possible combinations to make sum of 8 + assertEquals(21, DP.findWays(6, 3, 8)); // 6 faces, 3 dice, sum = 8 + } + + @Test + void testTwoDiceWithSumFive() { + // Test for 2 dice, with 4 faces to make sum of 5 + // Possible combinations: (1,4), (2,3), (3,2), (4,1) + assertEquals(4, DP.findWays(4, 2, 5)); // 4 faces, 2 dice, sum = 5 + } + + @Test + void testThreeDiceWithSumFive() { + // Test for 3 dice, with 4 faces to make sum of 5 + // Possible combinations: (1,1,3), (1,2,2), (1,3,1), (2,1,2), (2,2,1), (3,1,1) + assertEquals(6, DP.findWays(4, 3, 5)); // 4 faces, 3 dice, sum = 5 + } + + @Test + void testEdgeCaseZeroSum() { + // Test for 0 sum with 0 dice + assertEquals(0, DP.findWays(4, 0, 0)); // 4 faces, 0 dice, sum = 0 + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/EditDistanceTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/EditDistanceTest.java new file mode 100644 index 000000000000..737e8d1d0918 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/EditDistanceTest.java @@ -0,0 +1,104 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class EditDistanceTest { + + @ParameterizedTest + @CsvSource({"'', '', 0", "'abc', '', 3", "'', 'abcd', 4", "'same', 'same', 0", "'a', 'b', 1", "'abc', 'abd', 1"}) + void testMinDistance(String str1, String str2, int expected) { + assertEquals(expected, EditDistance.minDistance(str1, str2)); + } + + @Test + public void testEditDistanceBothEmptyStrings() { + assertEquals(0, EditDistance.editDistance("", "")); + } + + @Test + public void testEditDistanceOneEmptyString() { + assertEquals(5, EditDistance.editDistance("", "hello")); + assertEquals(7, EditDistance.editDistance("worldly", "")); + } + + @Test + public void testEditDistanceOneEmptyStringMemoization() { + int[][] storage = new int[1][6]; + assertAll("String assertions", + () + -> assertEquals(5, EditDistance.editDistance("", "hello", storage)), + () -> assertEquals(0, storage[0][0]), () -> assertEquals(0, storage[0][1]), () -> assertEquals(0, storage[0][2]), () -> assertEquals(0, storage[0][3]), () -> assertEquals(0, storage[0][4]), () -> assertEquals(5, storage[0][5])); + } + + @Test + public void testEditDistanceEqualStrings() { + assertEquals(0, EditDistance.editDistance("test", "test")); + assertEquals(0, EditDistance.editDistance("abc", "abc")); + } + + @Test + public void testEditDistanceEqualStringsMemoization() { + int[][] storage = new int[4][4]; + assertAll("String assertions", + () + -> assertEquals(0, EditDistance.editDistance("abc", "abc", storage)), + () + -> assertEquals(0, storage[0][0]), + () + -> assertEquals(0, storage[0][1]), + () + -> assertEquals(0, storage[0][2]), + () + -> assertEquals(0, storage[0][3]), + () + -> assertEquals(0, storage[1][0]), + () + -> assertEquals(0, storage[1][1]), + () + -> assertEquals(0, storage[1][2]), + () + -> assertEquals(0, storage[1][3]), + () + -> assertEquals(0, storage[2][0]), + () -> assertEquals(0, storage[2][1]), () -> assertEquals(0, storage[2][2]), () -> assertEquals(0, storage[2][3]), () -> assertEquals(0, storage[3][0]), () -> assertEquals(0, storage[3][1]), () -> assertEquals(0, storage[3][2]), () -> assertEquals(0, storage[3][3])); + } + + @Test + public void testEditDistanceOneCharacterDifference() { + assertEquals(1, EditDistance.editDistance("cat", "bat")); + assertEquals(1, EditDistance.editDistance("cat", "cats")); + assertEquals(1, EditDistance.editDistance("cats", "cat")); + } + + @Test + public void testEditDistanceOneCharacterDifferenceMemoization() { + int[][] storage = new int[3][3]; + assertAll("All assertions", + () + -> assertEquals(1, EditDistance.editDistance("at", "it", storage)), + () + -> assertEquals(0, storage[0][0]), + () + -> assertEquals(1, storage[0][1]), + () -> assertEquals(2, storage[0][2]), () -> assertEquals(1, storage[1][0]), () -> assertEquals(0, storage[1][1]), () -> assertEquals(1, storage[1][2]), () -> assertEquals(2, storage[2][0]), () -> assertEquals(1, storage[2][1]), () -> assertEquals(1, storage[2][2])); + } + + @Test + public void testEditDistanceGeneralCases() { + assertEquals(3, EditDistance.editDistance("kitten", "sitting")); + assertEquals(2, EditDistance.editDistance("flaw", "lawn")); + assertEquals(5, EditDistance.editDistance("intention", "execution")); + } + + @Test + public void testEditDistanceGeneralCasesMemoization() { + int[][] storage = new int[7][8]; + assertEquals(3, EditDistance.editDistance("kitten", "sitting", storage)); + assertAll("All assertions", () -> assertEquals(0, storage[0][0]), () -> assertEquals(3, storage[6][7])); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/EggDroppingTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/EggDroppingTest.java new file mode 100644 index 000000000000..bc569cc294b0 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/EggDroppingTest.java @@ -0,0 +1,28 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class EggDroppingTest { + + @Test + void hasMultipleEggSingleFloor() { + assertEquals(1, EggDropping.minTrials(3, 1)); + } + + @Test + void hasSingleEggSingleFloor() { + assertEquals(1, EggDropping.minTrials(1, 1)); + } + + @Test + void hasSingleEggMultipleFloor() { + assertEquals(3, EggDropping.minTrials(1, 3)); + } + + @Test + void hasMultipleEggMultipleFloor() { + assertEquals(7, EggDropping.minTrials(100, 101)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/FibonacciTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/FibonacciTest.java new file mode 100644 index 000000000000..166e20c3083f --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/FibonacciTest.java @@ -0,0 +1,89 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class FibonacciTest { + + @BeforeEach + void setUp() { + // Clear the cache before each test to avoid interference + Fibonacci.CACHE.clear(); + } + + @Test + void testFibMemo() { + // Test memoization method + assertEquals(0, Fibonacci.fibMemo(0)); + assertEquals(1, Fibonacci.fibMemo(1)); + assertEquals(1, Fibonacci.fibMemo(2)); + assertEquals(2, Fibonacci.fibMemo(3)); + assertEquals(3, Fibonacci.fibMemo(4)); + assertEquals(5, Fibonacci.fibMemo(5)); + assertEquals(8, Fibonacci.fibMemo(6)); + assertEquals(13, Fibonacci.fibMemo(7)); + assertEquals(21, Fibonacci.fibMemo(8)); + assertEquals(34, Fibonacci.fibMemo(9)); + assertEquals(55, Fibonacci.fibMemo(10)); + } + + @Test + void testFibBotUp() { + // Test bottom-up method + assertEquals(0, Fibonacci.fibBotUp(0)); + assertEquals(1, Fibonacci.fibBotUp(1)); + assertEquals(1, Fibonacci.fibBotUp(2)); + assertEquals(2, Fibonacci.fibBotUp(3)); + assertEquals(3, Fibonacci.fibBotUp(4)); + assertEquals(5, Fibonacci.fibBotUp(5)); + assertEquals(8, Fibonacci.fibBotUp(6)); + assertEquals(13, Fibonacci.fibBotUp(7)); + assertEquals(21, Fibonacci.fibBotUp(8)); + assertEquals(34, Fibonacci.fibBotUp(9)); + assertEquals(55, Fibonacci.fibBotUp(10)); + } + + @Test + void testFibOptimized() { + // Test optimized Fibonacci method + assertEquals(0, Fibonacci.fibOptimized(0)); + assertEquals(1, Fibonacci.fibOptimized(1)); + assertEquals(1, Fibonacci.fibOptimized(2)); + assertEquals(2, Fibonacci.fibOptimized(3)); + assertEquals(3, Fibonacci.fibOptimized(4)); + assertEquals(5, Fibonacci.fibOptimized(5)); + assertEquals(8, Fibonacci.fibOptimized(6)); + assertEquals(13, Fibonacci.fibOptimized(7)); + assertEquals(21, Fibonacci.fibOptimized(8)); + assertEquals(34, Fibonacci.fibOptimized(9)); + assertEquals(55, Fibonacci.fibOptimized(10)); + } + + @Test + void testFibBinet() { + // Test Binet's formula method + assertEquals(0, Fibonacci.fibBinet(0)); + assertEquals(1, Fibonacci.fibBinet(1)); + assertEquals(1, Fibonacci.fibBinet(2)); + assertEquals(2, Fibonacci.fibBinet(3)); + assertEquals(3, Fibonacci.fibBinet(4)); + assertEquals(5, Fibonacci.fibBinet(5)); + assertEquals(8, Fibonacci.fibBinet(6)); + assertEquals(13, Fibonacci.fibBinet(7)); + assertEquals(21, Fibonacci.fibBinet(8)); + assertEquals(34, Fibonacci.fibBinet(9)); + assertEquals(55, Fibonacci.fibBinet(10)); + } + + @Test + void testNegativeInput() { + // Test negative input; Fibonacci is not defined for negative numbers + assertThrows(IllegalArgumentException.class, () -> { Fibonacci.fibMemo(-1); }); + assertThrows(IllegalArgumentException.class, () -> { Fibonacci.fibBotUp(-1); }); + assertThrows(IllegalArgumentException.class, () -> { Fibonacci.fibOptimized(-1); }); + assertThrows(IllegalArgumentException.class, () -> { Fibonacci.fibBinet(-1); }); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/KadaneAlgorithmTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/KadaneAlgorithmTest.java new file mode 100644 index 000000000000..e26606ef98a9 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/KadaneAlgorithmTest.java @@ -0,0 +1,61 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class KadaneAlgorithmTest { + + @Test + void testMaxSumWithPositiveValues() { + // Test with all positive numbers + int[] input = {89, 56, 98, 123, 26, 75, 12, 40, 39, 68, 91}; + int expectedMaxSum = 89 + 56 + 98 + 123 + 26 + 75 + 12 + 40 + 39 + 68 + 91; // sum of all elements + assertTrue(KadaneAlgorithm.maxSum(input, expectedMaxSum)); + } + + @Test + void testMaxSumWithMixedValues() { + // Test with mixed positive and negative numbers + int[] input = {1, -2, 3, 4, -1, 2, 1, -5, 4}; + int expectedMaxSum = 3 + 4 + -1 + 2 + 1; // max subarray is [3, 4, -1, 2, 1] + assertTrue(KadaneAlgorithm.maxSum(input, expectedMaxSum)); + } + + @Test + void testMaxSumWithAllNegativeValues() { + // Test with all negative numbers + int[] input = {-2, -3, -1, -4}; + int expectedMaxSum = -1; // max subarray is the least negative number + assertTrue(KadaneAlgorithm.maxSum(input, expectedMaxSum)); + } + + @Test + void testMaxSumWithSingleElement() { + // Test with a single positive element + int[] input = {10}; + int expectedMaxSum = 10; // max subarray is the single element + assertTrue(KadaneAlgorithm.maxSum(input, expectedMaxSum)); + + // Test with a single negative element + input = new int[] {-10}; + expectedMaxSum = -10; // max subarray is the single element + assertTrue(KadaneAlgorithm.maxSum(input, expectedMaxSum)); + } + + @Test + void testMaxSumWithZero() { + // Test with zeros in the array + int[] input = {0, -1, 2, -2, 0, 3}; + int expectedMaxSum = 3; // max subarray is [2, -2, 0, 3] + assertTrue(KadaneAlgorithm.maxSum(input, expectedMaxSum)); + } + + @Test + void testMaxSumWithEmptyArray() { + // Test with an empty array; should ideally throw an exception or return false + int[] input = {}; + assertThrows(ArrayIndexOutOfBoundsException.class, () -> { KadaneAlgorithm.maxSum(input, 0); }); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/KnapsackMemoizationTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/KnapsackMemoizationTest.java new file mode 100644 index 000000000000..3545eb2667ed --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/KnapsackMemoizationTest.java @@ -0,0 +1,49 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class KnapsackMemoizationTest { + + KnapsackMemoization knapsackMemoization = new KnapsackMemoization(); + + @Test + void test1() { + int[] weight = {1, 3, 4, 5}; + int[] value = {1, 4, 5, 7}; + int capacity = 10; + assertEquals(13, knapsackMemoization.knapSack(capacity, weight, value, weight.length)); + } + + @Test + void test2() { + int[] weight = {95, 4, 60, 32, 23, 72, 80, 62, 65, 46}; + int[] value = {55, 10, 47, 5, 4, 50, 8, 61, 85, 87}; + int capacity = 269; + assertEquals(295, knapsackMemoization.knapSack(capacity, weight, value, weight.length)); + } + + @Test + void test3() { + int[] weight = {10, 20, 30}; + int[] value = {60, 100, 120}; + int capacity = 50; + assertEquals(220, knapsackMemoization.knapSack(capacity, weight, value, weight.length)); + } + + @Test + void test4() { + int[] weight = {1, 2, 3}; + int[] value = {10, 20, 30}; + int capacity = 0; + assertEquals(0, knapsackMemoization.knapSack(capacity, weight, value, weight.length)); + } + @Test + void test5() { + int[] weight = {1, 2, 3, 8}; + int[] value = {10, 20, 30, 40}; + int capacity = 50; + assertEquals(100, knapsackMemoization.knapSack(capacity, weight, value, weight.length)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/KnapsackTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/KnapsackTest.java new file mode 100644 index 000000000000..3ff733db7e15 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/KnapsackTest.java @@ -0,0 +1,81 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +public class KnapsackTest { + @Test + public void testKnapSackBasic() { + int[] weights = {2, 3, 4, 5}; + int[] values = {3, 4, 5, 6}; + int weightCapacity = 5; + int expected = 7; // Maximum value should be 7 (items 1 and 4). + int result = Knapsack.knapSack(weightCapacity, weights, values); + assertEquals(expected, result); + } + + @Test + public void testKnapSackEmpty() { + int[] weights = {}; + int[] values = {}; + int weightCapacity = 10; + int expected = 0; // With no items, the result should be 0. + int result = Knapsack.knapSack(weightCapacity, weights, values); + assertEquals(expected, result); + } + + @Test + public void testKnapSackNoCapacity() { + int[] weights = {2, 3, 4}; + int[] values = {3, 4, 5}; + int weightCapacity = 0; + int expected = 0; // With no capacity, the result should be 0. + int result = Knapsack.knapSack(weightCapacity, weights, values); + assertEquals(expected, result); + } + + @Test + public void testKnapSackMaxCapacity() { + int[] weights = {2, 3, 4, 5}; + int[] values = {3, 4, 5, 6}; + int weightCapacity = 10; + int expected = 13; // Maximum value should be 13 (items 1, 3, and 4). + int result = Knapsack.knapSack(weightCapacity, weights, values); + assertEquals(expected, result); + } + + @Test + public void testKnapSackThrowsForInputsOfDifferentLength() { + int[] weights = {2, 3, 4}; + int[] values = {3, 4, 5, 6}; // Different length values array. + int weightCapacity = 5; + assertThrows(IllegalArgumentException.class, () -> { Knapsack.knapSack(weightCapacity, weights, values); }); + } + + @Test + public void testKnapSackThrowsForNullInputs() { + int[] weights = {2, 3, 4}; + int[] values = {3, 4, 6}; + int weightCapacity = 5; + assertThrows(IllegalArgumentException.class, () -> { Knapsack.knapSack(weightCapacity, null, values); }); + assertThrows(IllegalArgumentException.class, () -> { Knapsack.knapSack(weightCapacity, weights, null); }); + } + + @Test + public void testKnapSackThrowsForNegativeCapacity() { + int[] weights = {2, 3, 4, 5}; + int[] values = {3, 4, 5, 6}; + int weightCapacity = -5; + assertThrows(IllegalArgumentException.class, () -> { Knapsack.knapSack(weightCapacity, weights, values); }); + } + + @Test + public void testKnapSackThrowsForNegativeWeight() { + int[] weights = {2, 0, 4}; + int[] values = {3, 4, 6}; + int weightCapacity = 5; + assertThrows(IllegalArgumentException.class, () -> { Knapsack.knapSack(weightCapacity, weights, values); }); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/LevenshteinDistanceTests.java b/src/test/java/com/thealgorithms/dynamicprogramming/LevenshteinDistanceTests.java new file mode 100644 index 000000000000..ad4c4c7c53e0 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/LevenshteinDistanceTests.java @@ -0,0 +1,45 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.List; +import java.util.function.ToIntBiFunction; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class LevenshteinDistanceTests { + + @ParameterizedTest + @MethodSource("testCases") + public void testLevenshteinDistance(final int expected, final String str1, final String str2, final ToIntBiFunction<String, String> dist) { + assertEquals(expected, dist.applyAsInt(str1, str2)); + assertEquals(expected, dist.applyAsInt(str2, str1)); + assertEquals(0, dist.applyAsInt(str1, str1)); + assertEquals(0, dist.applyAsInt(str2, str2)); + } + + private static Stream<Arguments> testCases() { + final Object[][] testData = { + {0, "", ""}, + {0, "Hello, World!", "Hello, World!"}, + {4, "", "Rust"}, + {3, "horse", "ros"}, + {6, "tan", "elephant"}, + {8, "execute", "intention"}, + {1, "a", "b"}, + {1, "a", "aa"}, + {1, "a", ""}, + {1, "a", "ab"}, + {1, "a", "ba"}, + {2, "a", "bc"}, + {2, "a", "cb"}, + }; + + final List<ToIntBiFunction<String, String>> methods = Arrays.asList(LevenshteinDistance::naiveLevenshteinDistance, LevenshteinDistance::optimizedLevenshteinDistance); + + return Stream.of(testData).flatMap(input -> methods.stream().map(method -> Arguments.of(input[0], input[1], input[2], method))); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/LongestAlternatingSubsequenceTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/LongestAlternatingSubsequenceTest.java new file mode 100644 index 000000000000..3de1114a0987 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/LongestAlternatingSubsequenceTest.java @@ -0,0 +1,22 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class LongestAlternatingSubsequenceTest { + + @ParameterizedTest + @MethodSource("provideTestCases") + void testAlternatingLength(int[] arr, int expected) { + assertEquals(expected, LongestAlternatingSubsequence.alternatingLength(arr, arr.length)); + } + + private static Stream<Arguments> provideTestCases() { + return Stream.of(Arguments.of(new int[] {1}, 1), Arguments.of(new int[] {1, 2}, 2), Arguments.of(new int[] {2, 1}, 2), Arguments.of(new int[] {1, 3, 2, 4, 3, 5}, 6), Arguments.of(new int[] {1, 2, 3, 4, 5}, 2), Arguments.of(new int[] {5, 4, 3, 2, 1}, 2), + Arguments.of(new int[] {10, 22, 9, 33, 49, 50, 31, 60}, 6), Arguments.of(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 2)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/LongestArithmeticSubsequenceTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/LongestArithmeticSubsequenceTest.java new file mode 100644 index 000000000000..6384fe2afebe --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/LongestArithmeticSubsequenceTest.java @@ -0,0 +1,35 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.apache.commons.lang3.ArrayUtils; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class LongestArithmeticSubsequenceTest { + @ParameterizedTest + @MethodSource("provideTestCases") + void testGetLongestArithmeticSubsequenceLength(int[] nums, int expected) { + assertEquals(expected, LongestArithmeticSubsequence.getLongestArithmeticSubsequenceLength(nums)); + } + @ParameterizedTest + @MethodSource("provideTestCases") + void testGetLongestArithmeticSubsequenceLengthReversedInput(int[] nums, int expected) { + ArrayUtils.reverse(nums); + assertEquals(expected, LongestArithmeticSubsequence.getLongestArithmeticSubsequenceLength(nums)); + } + + @Test + void testGetLongestArithmeticSubsequenceLengthThrowsForNullInput() { + assertThrows(IllegalArgumentException.class, () -> LongestArithmeticSubsequence.getLongestArithmeticSubsequenceLength(null)); + } + + private static Stream<Arguments> provideTestCases() { + return Stream.of(Arguments.of(new int[] {3, 6, 9, 12, 15}, 5), Arguments.of(new int[] {1, 7, 10, 13, 14, 19}, 4), Arguments.of(new int[] {1, 2, 3, 4}, 4), Arguments.of(new int[] {}, 0), Arguments.of(new int[] {10}, 1), Arguments.of(new int[] {9, 4, 7, 2, 10}, 3), + Arguments.of(new int[] {1, 2, 2, 2, 2, 5}, 4)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequenceTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequenceTest.java new file mode 100644 index 000000000000..40bbdff15ca6 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/LongestCommonSubsequenceTest.java @@ -0,0 +1,89 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class LongestCommonSubsequenceTest { + + @Test + public void testLCSBasic() { + String str1 = "ABCBDAB"; + String str2 = "BDCAB"; + String expected = "BDAB"; // The longest common subsequence + String result = LongestCommonSubsequence.getLCS(str1, str2); + assertEquals(expected, result); + } + + @Test + public void testLCSIdenticalStrings() { + String str1 = "AGGTAB"; + String str2 = "AGGTAB"; + String expected = "AGGTAB"; // LCS is the same as the strings + String result = LongestCommonSubsequence.getLCS(str1, str2); + assertEquals(expected, result); + } + + @Test + public void testLCSNoCommonCharacters() { + String str1 = "ABC"; + String str2 = "XYZ"; + String expected = ""; // No common subsequence + String result = LongestCommonSubsequence.getLCS(str1, str2); + assertEquals(expected, result); + } + + @Test + public void testLCSWithEmptyString() { + String str1 = ""; + String str2 = "XYZ"; + String expected = ""; // LCS with an empty string should be empty + String result = LongestCommonSubsequence.getLCS(str1, str2); + assertEquals(expected, result); + } + + @Test + public void testLCSWithBothEmptyStrings() { + String str1 = ""; + String str2 = ""; + String expected = ""; // LCS with both strings empty should be empty + String result = LongestCommonSubsequence.getLCS(str1, str2); + assertEquals(expected, result); + } + + @Test + public void testLCSWithNullFirstString() { + String str1 = null; + String str2 = "XYZ"; + String expected = null; // Should return null if first string is null + String result = LongestCommonSubsequence.getLCS(str1, str2); + assertEquals(expected, result); + } + + @Test + public void testLCSWithNullSecondString() { + String str1 = "ABC"; + String str2 = null; + String expected = null; // Should return null if second string is null + String result = LongestCommonSubsequence.getLCS(str1, str2); + assertEquals(expected, result); + } + + @Test + public void testLCSWithNullBothStrings() { + String str1 = null; + String str2 = null; + String expected = null; // Should return null if both strings are null + String result = LongestCommonSubsequence.getLCS(str1, str2); + assertEquals(expected, result); + } + + @Test + public void testLCSWithLongerStringContainingCommonSubsequence() { + String str1 = "ABCDEF"; + String str2 = "AEBDF"; + String expected = "ABDF"; // Common subsequence is "ABDF" + String result = LongestCommonSubsequence.getLCS(str1, str2); + assertEquals(expected, result); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/LongestIncreasingSubsequenceNLogNTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/LongestIncreasingSubsequenceNLogNTest.java new file mode 100644 index 000000000000..dc87d6751460 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/LongestIncreasingSubsequenceNLogNTest.java @@ -0,0 +1,22 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class LongestIncreasingSubsequenceNLogNTest { + + private static Stream<Arguments> provideTestCases() { + return Stream.of(Arguments.of(new int[] {10, 9, 2, 5, 3, 7, 101, 18}, 4), Arguments.of(new int[] {0, 1, 0, 3, 2, 3}, 4), Arguments.of(new int[] {7, 7, 7, 7, 7}, 1), Arguments.of(new int[] {1, 3, 5, 4, 7}, 4), Arguments.of(new int[] {}, 0), Arguments.of(new int[] {10}, 1), + Arguments.of(new int[] {3, 10, 2, 1, 20}, 3), Arguments.of(new int[] {50, 3, 10, 7, 40, 80}, 4)); + } + + @ParameterizedTest + @MethodSource("provideTestCases") + public void testLengthOfLIS(int[] input, int expected) { + assertEquals(expected, LongestIncreasingSubsequenceNLogN.lengthOfLIS(input)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/LongestIncreasingSubsequenceTests.java b/src/test/java/com/thealgorithms/dynamicprogramming/LongestIncreasingSubsequenceTests.java new file mode 100644 index 000000000000..5135105592a5 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/LongestIncreasingSubsequenceTests.java @@ -0,0 +1,49 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class LongestIncreasingSubsequenceTests { + @FunctionalInterface + public interface IntArrayToInt { + int apply(int[] array); + } + + @ParameterizedTest + @MethodSource("testCases") + public void testLongestIncreasingSubsequence(final int expected, final int[] input, final IntArrayToInt method) { + assertEquals(expected, method.apply(input)); + } + + private static Stream<Arguments> testCases() { + final Object[][] testData = { + {0, new int[] {}}, + {1, new int[] {1}}, + {1, new int[] {2, 2}}, + {1, new int[] {3, 3, 3}}, + {1, new int[] {4, 4, 4, 4}}, + {1, new int[] {5, 5, 5, 5, 5}}, + {2, new int[] {1, 2}}, + {2, new int[] {1, 2, 2, 2, 2}}, + {2, new int[] {1, 0, 2}}, + {3, new int[] {1, 10, 2, 30}}, + {3, new int[] {5, 8, 3, 7, 9, 1}}, + {6, new int[] {0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15}}, + {4, new int[] {10, 9, 2, 5, 3, 7, 101, 18}}, + {4, new int[] {10, 10, 9, 9, 2, 2, 5, 5, 3, 3, 7, 7, 101, 101, 18, 18}}, + {4, new int[] {0, 1, 0, 3, 2, 3}}, + {2, new int[] {1, 1, 2, 2, 2}}, + {3, new int[] {1, 1, 2, 2, 2, 3, 3, 3, 3}}, + }; + + final List<IntArrayToInt> methods = Arrays.asList(LongestIncreasingSubsequence::lis, LongestIncreasingSubsequence::findLISLen); + + return Stream.of(testData).flatMap(input -> methods.stream().map(method -> Arguments.of(input[0], input[1], method))); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/LongestPalindromicSubstringTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/LongestPalindromicSubstringTest.java new file mode 100644 index 000000000000..278de7e10e0a --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/LongestPalindromicSubstringTest.java @@ -0,0 +1,22 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class LongestPalindromicSubstringTest { + + private static Stream<Arguments> provideTestCases() { + return Stream.of( + Arguments.of("babad", "aba"), Arguments.of("cbbd", "bb"), Arguments.of("a", "a"), Arguments.of("x", "x"), Arguments.of("", ""), Arguments.of("aaaa", "aaaa"), Arguments.of("mm", "mm"), Arguments.of("level", "level"), Arguments.of("bananas", "anana"), Arguments.of("abacabad", "abacaba")); + } + + @ParameterizedTest + @MethodSource("provideTestCases") + public void testLps(String input, String expected) { + assertEquals(expected, LongestPalindromicSubstring.lps(input)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/LongestValidParenthesesTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/LongestValidParenthesesTest.java new file mode 100644 index 000000000000..77b7dda6a972 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/LongestValidParenthesesTest.java @@ -0,0 +1,22 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class LongestValidParenthesesTest { + + private static Stream<Arguments> provideTestCases() { + return Stream.of(Arguments.of("", 0), Arguments.of("(", 0), Arguments.of(")", 0), Arguments.of("()", 2), Arguments.of("(())", 4), Arguments.of("()()", 4), Arguments.of(")(", 0), Arguments.of("(()", 2), Arguments.of("())(", 2), Arguments.of("(()())", 6), Arguments.of("(((())))", 8), + Arguments.of("(()))(()", 4), Arguments.of("()()()(", 6), Arguments.of("(()())()(", 8), Arguments.of("((((((", 0), Arguments.of("))))))", 0), Arguments.of("(()())(", 6), Arguments.of("))()(", 2), Arguments.of("()((()))", 8), Arguments.of("((()((())))", 10)); + } + + @ParameterizedTest + @MethodSource("provideTestCases") + public void testLongestValidParentheses(String input, int expected) { + assertEquals(expected, LongestValidParentheses.getLongestValidParentheses(input)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/MatrixChainMultiplicationTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/MatrixChainMultiplicationTest.java new file mode 100644 index 000000000000..2bee0ca52918 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/MatrixChainMultiplicationTest.java @@ -0,0 +1,54 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import org.junit.jupiter.api.Test; + +class MatrixChainMultiplicationTest { + + @Test + void testMatrixCreation() { + MatrixChainMultiplication.Matrix matrix1 = new MatrixChainMultiplication.Matrix(1, 10, 20); + MatrixChainMultiplication.Matrix matrix2 = new MatrixChainMultiplication.Matrix(2, 20, 30); + + assertEquals(1, matrix1.count()); + assertEquals(10, matrix1.col()); + assertEquals(20, matrix1.row()); + + assertEquals(2, matrix2.count()); + assertEquals(20, matrix2.col()); + assertEquals(30, matrix2.row()); + } + + @Test + void testMatrixChainOrder() { + // Create a list of matrices to be multiplied + ArrayList<MatrixChainMultiplication.Matrix> matrices = new ArrayList<>(); + matrices.add(new MatrixChainMultiplication.Matrix(1, 10, 20)); // A(1) = 10 x 20 + matrices.add(new MatrixChainMultiplication.Matrix(2, 20, 30)); // A(2) = 20 x 30 + + // Calculate matrix chain order + MatrixChainMultiplication.Result result = MatrixChainMultiplication.calculateMatrixChainOrder(matrices); + + // Expected cost of multiplying A(1) and A(2) + int expectedCost = 6000; // The expected optimal cost of multiplying A(1)(10x20) and A(2)(20x30) + int actualCost = result.getM()[1][2]; + + assertEquals(expectedCost, actualCost); + } + + @Test + void testOptimalParentheses() { + // Create a list of matrices to be multiplied + ArrayList<MatrixChainMultiplication.Matrix> matrices = new ArrayList<>(); + matrices.add(new MatrixChainMultiplication.Matrix(1, 10, 20)); // A(1) = 10 x 20 + matrices.add(new MatrixChainMultiplication.Matrix(2, 20, 30)); // A(2) = 20 x 30 + + // Calculate matrix chain order + MatrixChainMultiplication.Result result = MatrixChainMultiplication.calculateMatrixChainOrder(matrices); + + // Check the optimal split for parentheses + assertEquals(1, result.getS()[1][2]); // s[1][2] should point to the optimal split + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/MatrixChainRecursiveTopDownMemoisationTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/MatrixChainRecursiveTopDownMemoisationTest.java new file mode 100644 index 000000000000..f8270f6d50b5 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/MatrixChainRecursiveTopDownMemoisationTest.java @@ -0,0 +1,68 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class MatrixChainRecursiveTopDownMemoisationTest { + + /** + * Test case for four matrices with dimensions 1x2, 2x3, 3x4, and 4x5. + * The expected minimum number of multiplications is 38. + */ + @Test + void testFourMatrices() { + int[] dimensions = {1, 2, 3, 4, 5}; + int expected = 38; + int actual = MatrixChainRecursiveTopDownMemoisation.memoizedMatrixChain(dimensions); + assertEquals(expected, actual, "The minimum number of multiplications should be 38."); + } + + /** + * Test case for three matrices with dimensions 10x20, 20x30, and 30x40. + * The expected minimum number of multiplications is 6000. + */ + @Test + void testThreeMatrices() { + int[] dimensions = {10, 20, 30, 40}; + int expected = 18000; + int actual = MatrixChainRecursiveTopDownMemoisation.memoizedMatrixChain(dimensions); + assertEquals(expected, actual, "The minimum number of multiplications should be 18000."); + } + + /** + * Test case for two matrices with dimensions 5x10 and 10x20. + * The expected minimum number of multiplications is 1000. + */ + @Test + void testTwoMatrices() { + int[] dimensions = {5, 10, 20}; + int expected = 1000; + int actual = MatrixChainRecursiveTopDownMemoisation.memoizedMatrixChain(dimensions); + assertEquals(expected, actual, "The minimum number of multiplications should be 1000."); + } + + /** + * Test case for a single matrix. + * The expected minimum number of multiplications is 0, as there are no multiplications needed. + */ + @Test + void testSingleMatrix() { + int[] dimensions = {10, 20}; // Single matrix dimensions + int expected = 0; + int actual = MatrixChainRecursiveTopDownMemoisation.memoizedMatrixChain(dimensions); + assertEquals(expected, actual, "The minimum number of multiplications should be 0."); + } + + /** + * Test case for matrices with varying dimensions. + * The expected minimum number of multiplications is calculated based on the dimensions provided. + */ + @Test + void testVaryingDimensions() { + int[] dimensions = {2, 3, 4, 5, 6}; // Dimensions for 4 matrices + int expected = 124; // Expected value needs to be calculated based on the problem + int actual = MatrixChainRecursiveTopDownMemoisation.memoizedMatrixChain(dimensions); + assertEquals(expected, actual, "The minimum number of multiplications should be 124."); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/MaximumSumOfNonAdjacentElementsTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/MaximumSumOfNonAdjacentElementsTest.java new file mode 100644 index 000000000000..3f312d86462e --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/MaximumSumOfNonAdjacentElementsTest.java @@ -0,0 +1,52 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class MaximumSumOfNonAdjacentElementsTest { + + // Tests for Approach1 + @Test + public void testGetMaxSumApproach1WithEmptyArray() { + assertEquals(0, MaximumSumOfNonAdjacentElements.getMaxSumApproach1(new int[] {})); // Empty array + } + + @Test + public void testGetMaxSumApproach1WithSingleElement() { + assertEquals(1, MaximumSumOfNonAdjacentElements.getMaxSumApproach1(new int[] {1})); // Single element + } + + @Test + public void testGetMaxSumApproach1WithTwoElementsTakeMax() { + assertEquals(2, MaximumSumOfNonAdjacentElements.getMaxSumApproach1(new int[] {1, 2})); // Take max of both + } + + @Test + public void testGetMaxSumApproach1WithMultipleElements() { + assertEquals(15, MaximumSumOfNonAdjacentElements.getMaxSumApproach1(new int[] {3, 2, 5, 10, 7})); // 3 + 7 + 5 + assertEquals(10, MaximumSumOfNonAdjacentElements.getMaxSumApproach1(new int[] {5, 1, 1, 5})); // 5 + 5 + } + + // Tests for Approach2 + @Test + public void testGetMaxSumApproach2WithEmptyArray() { + assertEquals(0, MaximumSumOfNonAdjacentElements.getMaxSumApproach2(new int[] {})); // Empty array + } + + @Test + public void testGetMaxSumApproach2WithSingleElement() { + assertEquals(1, MaximumSumOfNonAdjacentElements.getMaxSumApproach2(new int[] {1})); // Single element + } + + @Test + public void testGetMaxSumApproach2WithTwoElementsTakeMax() { + assertEquals(2, MaximumSumOfNonAdjacentElements.getMaxSumApproach2(new int[] {1, 2})); // Take max of both + } + + @Test + public void testGetMaxSumApproach2WithMultipleElements() { + assertEquals(15, MaximumSumOfNonAdjacentElements.getMaxSumApproach2(new int[] {3, 2, 5, 10, 7})); // 3 + 7 + 5 + assertEquals(10, MaximumSumOfNonAdjacentElements.getMaxSumApproach2(new int[] {5, 1, 1, 5})); // 5 + 5 + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/MinimumPathSumTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/MinimumPathSumTest.java new file mode 100644 index 000000000000..5c722c3c0036 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/MinimumPathSumTest.java @@ -0,0 +1,50 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class MinimumPathSumTest { + + @Test + public void testMinimumPathSumWithRegularGrid() { + int[][] grid = {{1, 3, 1}, {1, 5, 1}, {4, 2, 1}}; + assertEquals(7, MinimumPathSum.minimumPathSum(grid)); + } + + @Test + public void testMinimumPathSumWithOneRowOneColumnGrid() { + int[][] grid = {{2}}; + assertEquals(2, MinimumPathSum.minimumPathSum(grid)); + } + + @Test + public void testMinimumPathSumWithEmptyGrid() { + int[][] grid = {{}}; + assertEquals(0, MinimumPathSum.minimumPathSum(grid)); + } + + @Test + public void testMinimumPathSumWithOneColumnGrid() { + int[][] grid = {{1}, {2}, {3}}; + assertEquals(6, MinimumPathSum.minimumPathSum(grid)); + } + + @Test + public void testMinimumPathSumGridOneRowGrid() { + int[][] grid = {{1, 2, 3}}; + assertEquals(6, MinimumPathSum.minimumPathSum(grid)); + } + + @Test + public void testMinimumPathSumWithDiffRowAndColumnGrid() { + int[][] grid = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; + assertEquals(30, MinimumPathSum.minimumPathSum(grid)); + } + + @Test + public void testMinimumPathSumWithNegativeNumberGrid() { + int[][] grid = {{1, 3, 1}, {3, 4, 1}, {4, -3, 1}}; + assertEquals(6, MinimumPathSum.minimumPathSum(grid)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/MinimumSumPartitionTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/MinimumSumPartitionTest.java new file mode 100644 index 000000000000..1f14320244ea --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/MinimumSumPartitionTest.java @@ -0,0 +1,44 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class MinimumSumPartitionTest { + @Test + public void testMinimumSumPartitionWithEvenSum() { + int[] array = {1, 6, 11, 4}; + assertEquals(0, MinimumSumPartition.minimumSumPartition(array)); + } + + @Test + public void testMinimumSumPartitionWithOddSum() { + int[] array = {36, 7, 46, 40}; + assertEquals(23, MinimumSumPartition.minimumSumPartition(array)); + } + + @Test + public void testMinimumSumPartitionWithSingleElement() { + int[] array = {7}; + assertEquals(7, MinimumSumPartition.minimumSumPartition(array)); + } + + @Test + public void testMinimumSumPartitionWithLargeNumbers() { + int[] array = {100, 200, 300, 400, 500}; + assertEquals(100, MinimumSumPartition.minimumSumPartition(array)); + } + + @Test + public void testMinimumSumPartitionWithEmptyArray() { + int[] array = {}; + assertEquals(0, MinimumSumPartition.minimumSumPartition(array)); + } + + @Test + public void testMinimumSumPartitionThrowsForNegativeArray() { + int[] array = {4, 1, -6, 7}; + assertThrows(IllegalArgumentException.class, () -> { MinimumSumPartition.minimumSumPartition(array); }); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/NewManShanksPrimeTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/NewManShanksPrimeTest.java new file mode 100644 index 000000000000..a16ad67d6470 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/NewManShanksPrimeTest.java @@ -0,0 +1,80 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the NewManShanksPrime class. + * This test class verifies the correctness of the nthManShanksPrime method + * for various input cases. + */ +class NewManShanksPrimeTest { + + /** + * Test case for the 1st New Man Shanks prime. + * The expected answer is 1. + */ + @Test + void testNthManShanksPrime1() { + int n = 1; + int expectedAnswer = 1; + assertTrue(NewManShanksPrime.nthManShanksPrime(n, expectedAnswer), "The 1st New Man Shanks prime should be 1."); + } + + /** + * Test case for the 2nd New Man Shanks prime. + * The expected answer is 3. + */ + @Test + void testNthManShanksPrime2() { + int n = 2; + int expectedAnswer = 3; + assertTrue(NewManShanksPrime.nthManShanksPrime(n, expectedAnswer), "The 2nd New Man Shanks prime should be 3."); + } + + /** + * Test case for the 3rd New Man Shanks prime. + * The expected answer is 7. + */ + @Test + void testNthManShanksPrime3() { + int n = 3; + int expectedAnswer = 7; + assertTrue(NewManShanksPrime.nthManShanksPrime(n, expectedAnswer), "The 3rd New Man Shanks prime should be 7."); + } + + /** + * Test case for the 4th New Man Shanks prime. + * The expected answer is 17. + */ + @Test + void testNthManShanksPrime4() { + int n = 4; + int expectedAnswer = 17; + assertTrue(NewManShanksPrime.nthManShanksPrime(n, expectedAnswer), "The 4th New Man Shanks prime should be 17."); + } + + /** + * Test case for the 5th New Man Shanks prime. + * The expected answer is 41. + */ + @Test + void testNthManShanksPrime5() { + int n = 5; + int expectedAnswer = 41; + assertTrue(NewManShanksPrime.nthManShanksPrime(n, expectedAnswer), "The 5th New Man Shanks prime should be 41."); + } + + /** + * Test case with an incorrect expected answer. + * For n = 2, the expected answer is 3. + */ + @Test + void testNthManShanksPrimeIncorrectAnswer() { + int n = 2; + int expectedAnswer = 4; // Incorrect expected value + assertFalse(NewManShanksPrime.nthManShanksPrime(n, expectedAnswer), "The 2nd New Man Shanks prime should not be 4."); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/OptimalJobSchedulingTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/OptimalJobSchedulingTest.java new file mode 100644 index 000000000000..173ed00488d0 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/OptimalJobSchedulingTest.java @@ -0,0 +1,98 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +/** + * @author georgioct@csd.auth.gr + */ +public class OptimalJobSchedulingTest { + + @Test + public void testOptimalJobScheduling1() { + + int numberProcesses = 5; + int numberMachines = 4; + + int[][] run = {{5, 1, 3, 2}, {4, 2, 1, 3}, {1, 5, 2, 1}, {2, 3, 4, 2}, {1, 1, 3, 1}}; + + int[][] transfer = {{0, 1, 2, 4}, {1, 0, 2, 3}, {2, 2, 0, 1}, {4, 3, 1, 0}}; + + OptimalJobScheduling opt = new OptimalJobScheduling(numberProcesses, numberMachines, run, transfer); + + opt.execute(); + + int[][] costs = {{5, 1, 3, 2}, {6, 3, 4, 5}, {5, 8, 6, 6}, {7, 9, 10, 8}, {8, 9, 12, 9}}; + + for (int i = 0; i < numberProcesses; i++) { + + for (int j = 0; j < numberMachines; j++) { + + assertEquals(costs[i][j], opt.getCost(i, j)); + } + } + } + + @Test + public void testOptimalJobScheduling2() { + + int numberProcesses = 3; + int numberMachines = 3; + + int[][] run = {{5, 1, 3}, {4, 2, 1}, {1, 5, 2}}; + + int[][] transfer = {{0, 1, 2}, {1, 0, 2}, {2, 2, 0}}; + + OptimalJobScheduling opt = new OptimalJobScheduling(numberProcesses, numberMachines, run, transfer); + + opt.execute(); + + int[][] costs = {{5, 1, 3}, {6, 3, 4}, {5, 8, 6}}; + + for (int i = 0; i < numberProcesses; i++) { + + for (int j = 0; j < numberMachines; j++) { + + assertEquals(costs[i][j], opt.getCost(i, j)); + } + } + } + + @Test + public void testOptimalJobScheduling3() { + + int numberProcesses = 6; + int numberMachines = 4; + + int[][] run = { + {5, 1, 3, 2}, + {4, 2, 1, 1}, + {1, 5, 2, 6}, + {1, 1, 2, 3}, + {2, 1, 4, 6}, + {3, 2, 2, 3}, + }; + + int[][] transfer = { + {0, 1, 2, 1}, + {1, 0, 2, 3}, + {2, 2, 0, 2}, + {1, 3, 2, 0}, + }; + + OptimalJobScheduling opt = new OptimalJobScheduling(numberProcesses, numberMachines, run, transfer); + + opt.execute(); + + int[][] costs = {{5, 1, 3, 2}, {6, 3, 4, 3}, {5, 8, 6, 9}, {6, 7, 8, 9}, {8, 8, 12, 13}, {11, 10, 12, 12}}; + + for (int i = 0; i < numberProcesses; i++) { + + for (int j = 0; j < numberMachines; j++) { + + assertEquals(costs[i][j], opt.getCost(i, j)); + } + } + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/PalindromicPartitioningTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/PalindromicPartitioningTest.java new file mode 100644 index 000000000000..be503359e770 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/PalindromicPartitioningTest.java @@ -0,0 +1,21 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class PalindromicPartitioningTest { + + private static Stream<Arguments> provideTestCases() { + return Stream.of(Arguments.of("a", 0), Arguments.of("aa", 0), Arguments.of("ab", 1), Arguments.of("ababbbabbababa", 3), Arguments.of("abcde", 4), Arguments.of("abacdcaba", 0)); + } + + @ParameterizedTest + @MethodSource("provideTestCases") + public void testMinimalPartitions(String input, int expected) { + assertEquals(expected, PalindromicPartitioning.minimalPartitions(input)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/PartitionProblemTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/PartitionProblemTest.java new file mode 100644 index 000000000000..098893f41771 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/PartitionProblemTest.java @@ -0,0 +1,25 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class PartitionProblemTest { + @Test + public void testIfSumOfTheArrayIsOdd() { + assertFalse(PartitionProblem.partition(new int[] {1, 2, 2})); + } + @Test + public void testIfSizeOfTheArrayIsOne() { + assertFalse(PartitionProblem.partition(new int[] {2})); + } + @Test + public void testIfSumOfTheArrayIsEven1() { + assertTrue(PartitionProblem.partition(new int[] {1, 2, 3, 6})); + } + @Test + public void testIfSumOfTheArrayIsEven2() { + assertFalse(PartitionProblem.partition(new int[] {1, 2, 3, 8})); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/RegexMatchingTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/RegexMatchingTest.java new file mode 100644 index 000000000000..e75482a68d8b --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/RegexMatchingTest.java @@ -0,0 +1,43 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class RegexMatchingTest { + + private record RegexTestCase(String s, String p, boolean expected) { + } + + private static Stream<Arguments> provideTestCases() { + return Stream.of(Arguments.of(new RegexTestCase("aa", "*", true)), Arguments.of(new RegexTestCase("aa", "a*", true)), Arguments.of(new RegexTestCase("aa", "a", false)), Arguments.of(new RegexTestCase("cb", "?b", true)), Arguments.of(new RegexTestCase("cb", "?a", false)), + Arguments.of(new RegexTestCase("adceb", "*a*b", true)), Arguments.of(new RegexTestCase("acdcb", "a*c?b", false)), Arguments.of(new RegexTestCase("", "*", true)), Arguments.of(new RegexTestCase("", "", true))); + } + + @ParameterizedTest + @MethodSource("provideTestCases") + void testRegexRecursionMethod1(RegexTestCase testCase) { + assertEquals(testCase.expected(), RegexMatching.regexRecursion(testCase.s(), testCase.p())); + } + + @ParameterizedTest + @MethodSource("provideTestCases") + void testRegexRecursionMethod2(RegexTestCase testCase) { + assertEquals(testCase.expected(), RegexMatching.regexRecursion(testCase.s(), testCase.p(), 0, 0)); + } + + @ParameterizedTest + @MethodSource("provideTestCases") + void testRegexRecursionMethod3(RegexTestCase testCase) { + assertEquals(testCase.expected(), RegexMatching.regexRecursion(testCase.s(), testCase.p(), 0, 0, new int[testCase.s().length()][testCase.p().length()])); + } + + @ParameterizedTest + @MethodSource("provideTestCases") + void testRegexBottomUp(RegexTestCase testCase) { + assertEquals(testCase.expected(), RegexMatching.regexBU(testCase.s(), testCase.p())); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/RodCuttingTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/RodCuttingTest.java new file mode 100644 index 000000000000..9cf21fd836db --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/RodCuttingTest.java @@ -0,0 +1,102 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the RodCutting class. + * This test class verifies the correctness of the cutRod method for various input cases. + */ +class RodCuttingTest { + + /** + * Test case for cutting a rod of length 1. + * The expected maximum obtainable value is the price of the piece of length 1. + */ + @Test + void testCutRodLength1() { + int[] prices = {1}; // Price for piece of length 1 + int length = 1; + int expectedValue = 1; + assertEquals(expectedValue, RodCutting.cutRod(prices, length), "The maximum obtainable value for a rod of length 1 should be 1."); + } + + /** + * Test case for cutting a rod of length 2. + * The expected maximum obtainable value is the best price combination for length 2. + */ + @Test + void testCutRodLength2() { + int[] prices = {1, 5}; // Prices for lengths 1 and 2 + int length = 2; + int expectedValue = 5; // Best value is to cut it into a single piece of length 2 + assertEquals(expectedValue, RodCutting.cutRod(prices, length), "The maximum obtainable value for a rod of length 2 should be 5."); + } + + /** + * Test case for cutting a rod of length 3. + * The expected maximum obtainable value is the best price combination for length 3. + */ + @Test + void testCutRodLength3() { + int[] prices = {1, 5, 8}; // Prices for lengths 1, 2, and 3 + int length = 3; + int expectedValue = 8; // Best value is to cut it into a single piece of length 3 + assertEquals(expectedValue, RodCutting.cutRod(prices, length), "The maximum obtainable value for a rod of length 3 should be 8."); + } + + /** + * Test case for cutting a rod of length 4. + * The expected maximum obtainable value is the best price combination for length 4. + */ + @Test + void testCutRodLength4() { + int[] prices = {1, 5, 8, 9}; // Prices for lengths 1, 2, 3, and 4 + int length = 4; + int expectedValue = 10; // Best value is to cut it into two pieces of length 2 + assertEquals(expectedValue, RodCutting.cutRod(prices, length), "The maximum obtainable value for a rod of length 4 should be 10."); + } + + /** + * Test case for cutting a rod of length 5. + * The expected maximum obtainable value is the best price combination for length 5. + */ + @Test + void testCutRodLength5() { + int[] prices = {1, 5, 8, 9, 10}; // Prices for lengths 1, 2, 3, 4, and 5 + int length = 5; + int expectedValue = 13; // Best value is to cut it into pieces of lengths 2 and 3 + assertEquals(expectedValue, RodCutting.cutRod(prices, length), "The maximum obtainable value for a rod of length 5 should be 13."); + } + + /** + * Test case for cutting a rod of length 0. + * The expected maximum obtainable value should be 0 since the rod has no length. + */ + @Test + void testCutRodLength0() { + int[] prices = {1, 5, 8, 9, 10}; // Prices are irrelevant for length 0 + int length = 0; + int expectedValue = 0; // No value obtainable from a rod of length 0 + assertEquals(expectedValue, RodCutting.cutRod(prices, length), "The maximum obtainable value for a rod of length 0 should be 0."); + } + + /** + * Test case for an empty prices array. + * The expected maximum obtainable value should still be 0 for any length. + */ + @Test + void testCutRodEmptyPrices() { + int[] prices = {}; + int length = 5; + assertThrows(IllegalArgumentException.class, () -> RodCutting.cutRod(prices, length), "An empty prices array should throw an IllegalArgumentException."); + } + @Test + void testCutRodNegativeLength() { + int[] prices = {1, 5, 8, 9, 10}; // Prices are irrelevant for negative length + int length = -1; + assertThrows(IllegalArgumentException.class, () -> RodCutting.cutRod(prices, length), "A negative rod length should throw an IllegalArgumentException."); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/ShortestCommonSupersequenceLengthTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/ShortestCommonSupersequenceLengthTest.java new file mode 100644 index 000000000000..8f0fd79f18a4 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/ShortestCommonSupersequenceLengthTest.java @@ -0,0 +1,14 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class ShortestCommonSupersequenceLengthTest { + @ParameterizedTest + @CsvSource({"AGGTAB, GXTXAYB, 9", "ABC, ABC, 3", "ABC, DEF, 6", "'', ABC, 3", "ABCD, AB, 4", "ABC, BCD, 4", "A, B, 2"}) + void testShortestSupersequence(String input1, String input2, int expected) { + assertEquals(expected, ShortestCommonSupersequenceLength.shortestSuperSequence(input1, input2)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/SubsetCountTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/SubsetCountTest.java new file mode 100644 index 000000000000..c76f89deb600 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/SubsetCountTest.java @@ -0,0 +1,29 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class SubsetCountTest { + @Test + void hasMultipleSubset() { + int[] arr = new int[] {1, 2, 3, 3}; + assertEquals(3, SubsetCount.getCount(arr, 6)); + } + @Test + void singleElementSubset() { + int[] arr = new int[] {1, 1, 1, 1}; + assertEquals(4, SubsetCount.getCount(arr, 1)); + } + + @Test + void hasMultipleSubsetSO() { + int[] arr = new int[] {1, 2, 3, 3}; + assertEquals(3, SubsetCount.getCountSO(arr, 6)); + } + @Test + void singleSubsetSO() { + int[] arr = new int[] {1, 1, 1, 1}; + assertEquals(1, SubsetCount.getCountSO(arr, 4)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/SubsetSumSpaceOptimizedTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/SubsetSumSpaceOptimizedTest.java new file mode 100644 index 000000000000..3a965f4e68b8 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/SubsetSumSpaceOptimizedTest.java @@ -0,0 +1,18 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class SubsetSumSpaceOptimizedTest { + + @Test + void basicCheck() { + assertTrue(SubsetSumSpaceOptimized.isSubsetSum(new int[] {7, 3, 2, 5, 8}, 14)); + assertTrue(SubsetSumSpaceOptimized.isSubsetSum(new int[] {4, 3, 2, 1}, 5)); + assertTrue(SubsetSumSpaceOptimized.isSubsetSum(new int[] {1, 7, 2, 9, 10}, 13)); + assertFalse(SubsetSumSpaceOptimized.isSubsetSum(new int[] {1, 2, 7, 10, 9}, 14)); + assertFalse(SubsetSumSpaceOptimized.isSubsetSum(new int[] {2, 15, 1, 6, 7}, 4)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/SubsetSumTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/SubsetSumTest.java new file mode 100644 index 000000000000..1ae4e71232f0 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/SubsetSumTest.java @@ -0,0 +1,24 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +class SubsetSumTest { + + record TestCase(int[] arr, int sum, boolean expected) { + } + + @ParameterizedTest + @MethodSource("provideTestCases") + void testSubsetSum(TestCase testCase) { + assertEquals(testCase.expected(), SubsetSum.subsetSum(testCase.arr(), testCase.sum())); + } + + private static Stream<TestCase> provideTestCases() { + return Stream.of(new TestCase(new int[] {50, 4, 10, 15, 34}, 64, true), new TestCase(new int[] {50, 4, 10, 15, 34}, 99, true), new TestCase(new int[] {50, 4, 10, 15, 34}, 5, false), new TestCase(new int[] {50, 4, 10, 15, 34}, 66, false), new TestCase(new int[] {}, 0, true), + new TestCase(new int[] {1, 2, 3}, 6, true), new TestCase(new int[] {1, 2, 3}, 7, false), new TestCase(new int[] {3, 34, 4, 12, 5, 2}, 9, true)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/SumOfSubsetTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/SumOfSubsetTest.java new file mode 100644 index 000000000000..9df35447eefa --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/SumOfSubsetTest.java @@ -0,0 +1,18 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class SumOfSubsetTest { + + @Test + void basicCheck() { + assertFalse(SumOfSubset.subsetSum(new int[] {1, 2, 7, 10, 9}, 4, 14)); + assertFalse(SumOfSubset.subsetSum(new int[] {2, 15, 1, 6, 7}, 4, 4)); + assertTrue(SumOfSubset.subsetSum(new int[] {7, 3, 2, 5, 8}, 4, 14)); + assertTrue(SumOfSubset.subsetSum(new int[] {4, 3, 2, 1}, 3, 5)); + assertTrue(SumOfSubset.subsetSum(new int[] {1, 7, 2, 9, 10}, 4, 13)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/TreeMatchingTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/TreeMatchingTest.java new file mode 100644 index 000000000000..d5418770a5d1 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/TreeMatchingTest.java @@ -0,0 +1,120 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.thealgorithms.datastructures.graphs.UndirectedAdjacencyListGraph; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class TreeMatchingTest { + UndirectedAdjacencyListGraph graph; + + @BeforeEach + void setUp() { + graph = new UndirectedAdjacencyListGraph(); + for (int i = 0; i < 14; i++) { + graph.addNode(); + } + } + + @Test + void testMaxMatchingForGeneralTree() { + graph.addEdge(0, 1, 20); + graph.addEdge(0, 2, 30); + graph.addEdge(1, 3, 40); + graph.addEdge(1, 4, 10); + graph.addEdge(2, 5, 20); + graph.addEdge(3, 6, 30); + graph.addEdge(3, 7, 30); + graph.addEdge(5, 8, 40); + graph.addEdge(5, 9, 10); + + TreeMatching treeMatching = new TreeMatching(graph); + assertEquals(110, treeMatching.getMaxMatching(0, -1)); + } + + @Test + void testMaxMatchingForBalancedTree() { + graph.addEdge(0, 1, 20); + graph.addEdge(0, 2, 30); + graph.addEdge(0, 3, 40); + graph.addEdge(1, 4, 10); + graph.addEdge(1, 5, 20); + graph.addEdge(2, 6, 20); + graph.addEdge(3, 7, 30); + graph.addEdge(5, 8, 10); + graph.addEdge(5, 9, 20); + graph.addEdge(7, 10, 10); + graph.addEdge(7, 11, 10); + graph.addEdge(7, 12, 5); + TreeMatching treeMatching = new TreeMatching(graph); + assertEquals(100, treeMatching.getMaxMatching(0, -1)); + } + + @Test + void testMaxMatchingForTreeWithVariedEdgeWeights() { + graph.addEdge(0, 1, 20); + graph.addEdge(0, 2, 30); + graph.addEdge(0, 3, 40); + graph.addEdge(0, 4, 50); + graph.addEdge(1, 5, 20); + graph.addEdge(2, 6, 20); + graph.addEdge(3, 7, 30); + graph.addEdge(5, 8, 10); + graph.addEdge(5, 9, 20); + graph.addEdge(7, 10, 10); + graph.addEdge(4, 11, 50); + graph.addEdge(4, 12, 20); + TreeMatching treeMatching = new TreeMatching(graph); + assertEquals(140, treeMatching.getMaxMatching(0, -1)); + } + + @Test + void emptyTree() { + TreeMatching treeMatching = new TreeMatching(graph); + assertEquals(0, treeMatching.getMaxMatching(0, -1)); + } + + @Test + void testSingleNodeTree() { + UndirectedAdjacencyListGraph singleNodeGraph = new UndirectedAdjacencyListGraph(); + singleNodeGraph.addNode(); + + TreeMatching treeMatching = new TreeMatching(singleNodeGraph); + assertEquals(0, treeMatching.getMaxMatching(0, -1)); + } + + @Test + void testLinearTree() { + graph.addEdge(0, 1, 10); + graph.addEdge(1, 2, 20); + graph.addEdge(2, 3, 30); + graph.addEdge(3, 4, 40); + + TreeMatching treeMatching = new TreeMatching(graph); + assertEquals(60, treeMatching.getMaxMatching(0, -1)); + } + + @Test + void testStarShapedTree() { + graph.addEdge(0, 1, 15); + graph.addEdge(0, 2, 25); + graph.addEdge(0, 3, 35); + graph.addEdge(0, 4, 45); + + TreeMatching treeMatching = new TreeMatching(graph); + assertEquals(45, treeMatching.getMaxMatching(0, -1)); + } + + @Test + void testUnbalancedTree() { + graph.addEdge(0, 1, 10); + graph.addEdge(0, 2, 20); + graph.addEdge(1, 3, 30); + graph.addEdge(2, 4, 40); + graph.addEdge(4, 5, 50); + + TreeMatching treeMatching = new TreeMatching(graph); + assertEquals(100, treeMatching.getMaxMatching(0, -1)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/TribonacciTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/TribonacciTest.java new file mode 100644 index 000000000000..434a1825dfec --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/TribonacciTest.java @@ -0,0 +1,24 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +/** + * Test class for {@code Tribonacci}. + */ +public class TribonacciTest { + + /** + * Tests the Tribonacci computation for a set of known values. + */ + @Test + public void testKnownValues() { + assertEquals(0, Tribonacci.compute(0), "The 0th Tribonacci should be 0."); + assertEquals(1, Tribonacci.compute(1), "The 1st Tribonacci should be 1."); + assertEquals(1, Tribonacci.compute(2), "The 2nd Tribonacci should be 1."); + assertEquals(2, Tribonacci.compute(3), "The 3rd Tribonacci should be 2."); + assertEquals(4, Tribonacci.compute(4), "The 4th Tribonacci should be 4."); + assertEquals(7, Tribonacci.compute(5), "The 5th Tribonacci should be 7."); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/UniquePathsTests.java b/src/test/java/com/thealgorithms/dynamicprogramming/UniquePathsTests.java new file mode 100644 index 000000000000..386938d28ec9 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/UniquePathsTests.java @@ -0,0 +1,59 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +public class UniquePathsTests { + + @Test + public void testUniquePaths3x3() { + assertEquals(6, UniquePaths.uniquePaths(3, 3)); + } + + @Test + public void testUniquePaths1x1() { + assertEquals(1, UniquePaths.uniquePaths(1, 1)); + } + + @Test + public void testUniquePaths3x7() { + assertEquals(28, UniquePaths.uniquePaths(3, 7)); + } + + @Test + public void testUniquePaths7x3() { + assertEquals(28, UniquePaths.uniquePaths(7, 3)); + } + + @Test + public void testUniquePaths100x100() { + assertThrows(ArithmeticException.class, () -> UniquePaths.uniquePaths(100, 100)); + } + + @Test + public void testUniquePathsII3x3() { + assertEquals(6, UniquePaths.uniquePaths2(3, 3)); + } + + @Test + public void testUniquePathsII1x1() { + assertEquals(1, UniquePaths.uniquePaths2(1, 1)); + } + + @Test + public void testUniquePathsII3x7() { + assertEquals(28, UniquePaths.uniquePaths2(3, 7)); + } + + @Test + public void testUniquePathsII7x3() { + assertEquals(28, UniquePaths.uniquePaths2(7, 3)); + } + + @Test + public void testUniquePathsII100x100() { + assertThrows(ArithmeticException.class, () -> UniquePaths.uniquePaths2(100, 100)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/UniqueSubsequencesCountTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/UniqueSubsequencesCountTest.java new file mode 100755 index 000000000000..049804f58b5a --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/UniqueSubsequencesCountTest.java @@ -0,0 +1,15 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class UniqueSubsequencesCountTest { + + @ParameterizedTest + @CsvSource({"abc, 7", "abcdashgdhas, 3592", "a, 1", "'a b', 7", "a1b2, 15", "AaBb, 15", "abab, 11"}) + void subseqCountParameterizedTest(String input, int expected) { + assertEquals(expected, UniqueSubsequencesCount.countSubseq(input)); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/WildcardMatchingTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/WildcardMatchingTest.java new file mode 100644 index 000000000000..56cd4ffe7d44 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/WildcardMatchingTest.java @@ -0,0 +1,27 @@ +package com.thealgorithms.dynamicprogramming; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class WildcardMatchingTest { + + @Test + public void testMatchingPattern() { + assertTrue(WildcardMatching.isMatch("aa", "a*")); + assertTrue(WildcardMatching.isMatch("adceb", "*a*b")); + } + + @Test + public void testNonMatchingPattern() { + assertFalse(WildcardMatching.isMatch("cb", "?a")); + assertFalse(WildcardMatching.isMatch("acdcb", "a*c?b")); + assertFalse(WildcardMatching.isMatch("mississippi", "m*issi*iss?*i")); + } + + @Test + public void testEmptyPattern() { + assertTrue(WildcardMatching.isMatch("", "")); + assertFalse(WildcardMatching.isMatch("abc", "")); + } +} diff --git a/src/test/java/com/thealgorithms/dynamicprogramming/WineProblemTest.java b/src/test/java/com/thealgorithms/dynamicprogramming/WineProblemTest.java new file mode 100644 index 000000000000..fbcc2c6f3a83 --- /dev/null +++ b/src/test/java/com/thealgorithms/dynamicprogramming/WineProblemTest.java @@ -0,0 +1,72 @@ +package com.thealgorithms.dynamicprogramming; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the WineProblem class. + * This test class verifies the correctness of the wine selling problem solutions. + */ +class WineProblemTest { + + /** + * Test for wpRecursion method. + */ + @Test + void testWpRecursion() { + int[] wines = {2, 3, 5, 1, 4}; // Prices of wines + int expectedProfit = 50; // The expected maximum profit + assertEquals(expectedProfit, WineProblem.wpRecursion(wines, 0, wines.length - 1), "The maximum profit using recursion should be 50."); + } + + /** + * Test for wptd method (Top-Down DP with Memoization). + */ + @Test + void testWptd() { + int[] wines = {2, 3, 5, 1, 4}; // Prices of wines + int expectedProfit = 50; // The expected maximum profit + assertEquals(expectedProfit, WineProblem.wptd(wines, 0, wines.length - 1, new int[wines.length][wines.length]), "The maximum profit using top-down DP should be 50."); + } + + /** + * Test for wpbu method (Bottom-Up DP with Tabulation). + */ + @Test + void testWpbu() { + int[] wines = {2, 3, 5, 1, 4}; // Prices of wines + int expectedProfit = 50; // The expected maximum profit + assertEquals(expectedProfit, WineProblem.wpbu(wines), "The maximum profit using bottom-up DP should be 50."); + } + + /** + * Test with a single wine. + */ + @Test + void testSingleWine() { + int[] wines = {10}; // Only one wine + int expectedProfit = 10; // Selling the only wine at year 1 + assertEquals(expectedProfit, WineProblem.wpbu(wines), "The maximum profit for a single wine should be 10."); + } + + /** + * Test with multiple wines of the same price. + */ + @Test + void testSamePriceWines() { + int[] wines = {5, 5, 5}; // All wines have the same price + int expectedProfit = 30; // Profit is 5 * (1 + 2 + 3) + assertEquals(expectedProfit, WineProblem.wpbu(wines), "The maximum profit with same price wines should be 30."); + } + + /** + * Test with no wines. + */ + @Test + void testNoWines() { + int[] wines = {}; + assertThrows(IllegalArgumentException.class, () -> WineProblem.wpbu(wines), "The maximum profit for no wines should throw an IllegalArgumentException."); + } +} diff --git a/src/test/java/com/thealgorithms/geometry/BresenhamLineTest.java b/src/test/java/com/thealgorithms/geometry/BresenhamLineTest.java new file mode 100644 index 000000000000..9df308497ddf --- /dev/null +++ b/src/test/java/com/thealgorithms/geometry/BresenhamLineTest.java @@ -0,0 +1,57 @@ +package com.thealgorithms.geometry; + +import java.awt.Point; +import java.util.Collection; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * The {@code BresenhamLineTest} class contains unit tests for the + * {@code BresenhamLine} class, specifically testing the + * {@code findLine} method. + * + * <p>This class uses parameterized tests to validate the output of + * Bresenham's line algorithm for various input points.</p> + */ +class BresenhamLineTest { + + /** + * Provides test cases for the parameterized test. + * + * <p>Each test case includes starting coordinates, ending coordinates, + * and the expected collection of points that should be generated by the + * {@code findLine} method.</p> + * + * @return a stream of arguments containing test cases + */ + static Stream<Arguments> linePointsProvider() { + return Stream.of(Arguments.of(0, 0, 5, 5, List.of(new Point(0, 0), new Point(1, 1), new Point(2, 2), new Point(3, 3), new Point(4, 4), new Point(5, 5))), Arguments.of(0, 0, 5, 0, List.of(new Point(0, 0), new Point(1, 0), new Point(2, 0), new Point(3, 0), new Point(4, 0), new Point(5, 0))), + Arguments.of(0, 0, 0, 5, List.of(new Point(0, 0), new Point(0, 1), new Point(0, 2), new Point(0, 3), new Point(0, 4), new Point(0, 5))), Arguments.of(-2, -2, -5, -5, List.of(new Point(-2, -2), new Point(-3, -3), new Point(-4, -4), new Point(-5, -5))), + Arguments.of(-1, -1, 2, 2, List.of(new Point(-1, -1), new Point(0, 0), new Point(1, 1), new Point(2, 2))), Arguments.of(2, -1, -1, -4, List.of(new Point(2, -1), new Point(1, -2), new Point(0, -3), new Point(-1, -4)))); + } + + /** + * Tests the {@code findLine} method of the {@code BresenhamLine} class. + * + * <p>This parameterized test runs multiple times with different sets of + * starting and ending coordinates to validate that the generated points + * match the expected output.</p> + * + * @param x0 the x-coordinate of the starting point + * @param y0 the y-coordinate of the starting point + * @param x1 the x-coordinate of the ending point + * @param y1 the y-coordinate of the ending point + * @param expected a collection of expected points that should form a line + */ + @ParameterizedTest + @MethodSource("linePointsProvider") + void testFindLine(int x0, int y0, int x1, int y1, Collection<Point> expected) { + List<Point> actual = BresenhamLine.findLine(x0, y0, x1, y1); + Assertions.assertEquals(expected.size(), actual.size(), "The size of the points list should match."); + Assertions.assertTrue(expected.containsAll(actual) && actual.containsAll(expected), "The points generated should match the expected points."); + } +} diff --git a/src/test/java/com/thealgorithms/geometry/ConvexHullTest.java b/src/test/java/com/thealgorithms/geometry/ConvexHullTest.java new file mode 100644 index 000000000000..e3e32e43c6de --- /dev/null +++ b/src/test/java/com/thealgorithms/geometry/ConvexHullTest.java @@ -0,0 +1,40 @@ +package com.thealgorithms.geometry; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class ConvexHullTest { + + @Test + void testConvexHullBruteForce() { + List<Point> points = Arrays.asList(new Point(0, 0), new Point(1, 0), new Point(10, 1)); + List<Point> expected = Arrays.asList(new Point(0, 0), new Point(1, 0), new Point(10, 1)); + assertEquals(expected, ConvexHull.convexHullBruteForce(points)); + + points = Arrays.asList(new Point(0, 0), new Point(1, 0), new Point(10, 0)); + expected = Arrays.asList(new Point(0, 0), new Point(10, 0)); + assertEquals(expected, ConvexHull.convexHullBruteForce(points)); + + points = Arrays.asList(new Point(0, 3), new Point(2, 2), new Point(1, 1), new Point(2, 1), new Point(3, 0), new Point(0, 0), new Point(3, 3), new Point(2, -1), new Point(2, -4), new Point(1, -3)); + expected = Arrays.asList(new Point(2, -4), new Point(1, -3), new Point(0, 0), new Point(3, 0), new Point(0, 3), new Point(3, 3)); + assertEquals(expected, ConvexHull.convexHullBruteForce(points)); + } + + @Test + void testConvexHullRecursive() { + List<Point> points = Arrays.asList(new Point(0, 0), new Point(1, 0), new Point(10, 1)); + List<Point> expected = Arrays.asList(new Point(0, 0), new Point(1, 0), new Point(10, 1)); + assertEquals(expected, ConvexHull.convexHullRecursive(points)); + + points = Arrays.asList(new Point(0, 0), new Point(1, 0), new Point(10, 0)); + expected = Arrays.asList(new Point(0, 0), new Point(10, 0)); + assertEquals(expected, ConvexHull.convexHullRecursive(points)); + + points = Arrays.asList(new Point(0, 3), new Point(2, 2), new Point(1, 1), new Point(2, 1), new Point(3, 0), new Point(0, 0), new Point(3, 3), new Point(2, -1), new Point(2, -4), new Point(1, -3)); + expected = Arrays.asList(new Point(2, -4), new Point(1, -3), new Point(0, 0), new Point(3, 0), new Point(0, 3), new Point(3, 3)); + assertEquals(expected, ConvexHull.convexHullRecursive(points)); + } +} diff --git a/src/test/java/com/thealgorithms/geometry/GrahamScanTest.java b/src/test/java/com/thealgorithms/geometry/GrahamScanTest.java new file mode 100644 index 000000000000..622273881a27 --- /dev/null +++ b/src/test/java/com/thealgorithms/geometry/GrahamScanTest.java @@ -0,0 +1,16 @@ +package com.thealgorithms.geometry; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class GrahamScanTest { + @Test + void testGrahamScan() { + Point[] points = {new Point(0, 3), new Point(1, 1), new Point(2, 2), new Point(4, 4), new Point(0, 0), new Point(1, 2), new Point(3, 1), new Point(3, 3)}; + String expectedResult = "[(0, 0), (3, 1), (4, 4), (0, 3)]"; + + GrahamScan graham = new GrahamScan(points); + assertEquals(expectedResult, graham.hull().toString()); + } +} diff --git a/src/test/java/com/thealgorithms/geometry/MidpointCircleTest.java b/src/test/java/com/thealgorithms/geometry/MidpointCircleTest.java new file mode 100644 index 000000000000..24370cd43b25 --- /dev/null +++ b/src/test/java/com/thealgorithms/geometry/MidpointCircleTest.java @@ -0,0 +1,55 @@ +package com.thealgorithms.geometry; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +/** + * Test class for the {@code MidpointCircle} class + */ +class MidpointCircleTest { + + /** + * Parameterized test to check the generated points for different circles. + * The points are checked based on the expected center and radius. + * + * @param centerX The x-coordinate of the circle's center. + * @param centerY The y-coordinate of the circle's center. + * @param radius The radius of the circle. + */ + @ParameterizedTest + @CsvSource({ + "0, 0, 3", // Circle centered at (0, 0) with radius 3 + "10, 10, 2" // Circle centered at (10, 10) with radius 2 + }) + void + testGenerateCirclePoints(int centerX, int centerY, int radius) { + List<int[]> points = MidpointCircle.generateCirclePoints(centerX, centerY, radius); + + // Ensure that all points satisfy the circle equation (x - centerX)^2 + (y - centerY)^2 = radius^2 + for (int[] point : points) { + int x = point[0]; + int y = point[1]; + + int dx = x - centerX; + int dy = y - centerY; + int distanceSquared = dx * dx + dy * dy; + + assertTrue(Math.abs(distanceSquared - radius * radius) <= 1, "Point (" + x + ", " + y + ") does not satisfy the circle equation."); + } + } + + /** + * Test to ensure the algorithm generates points for a zero-radius circle. + */ + @Test + void testZeroRadiusCircle() { + List<int[]> points = MidpointCircle.generateCirclePoints(0, 0, 0); + + // A zero-radius circle should only have one point: (0, 0) + assertTrue(points.size() == 1 && points.get(0)[0] == 0 && points.get(0)[1] == 0, "Zero-radius circle did not generate the correct point."); + } +} diff --git a/src/test/java/com/thealgorithms/geometry/MidpointEllipseTest.java b/src/test/java/com/thealgorithms/geometry/MidpointEllipseTest.java new file mode 100644 index 000000000000..9d03909c60ca --- /dev/null +++ b/src/test/java/com/thealgorithms/geometry/MidpointEllipseTest.java @@ -0,0 +1,99 @@ +package com.thealgorithms.geometry; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * The {@code MidpointEllipseTest} class contains unit tests for the + * {@code MidpointEllipse} class, specifically testing the + * {@code drawEllipse} method. + * + * <p>This class uses parameterized tests to validate the output of + * Midpoint Ellipse algorithm for various input points.</p> + */ +class MidpointEllipseTest { + + /** + * Provides test cases for the drawEllipse method. + * Each argument contains: centerX, centerY, a, b, and expected points. + * + * @return a stream of arguments for parameterized testing + */ + static Stream<Arguments> ellipseTestProvider() { + return Stream.of( + Arguments.of(0, 0, 5, 3, new int[][] {{0, 3}, {0, 3}, {0, -3}, {0, -3}, {1, 3}, {-1, 3}, {1, -3}, {-1, -3}, {2, 3}, {-2, 3}, {2, -3}, {-2, -3}, {3, 2}, {-3, 2}, {3, -2}, {-3, -2}, {4, 2}, {-4, 2}, {4, -2}, {-4, -2}, {5, 1}, {-5, 1}, {5, -1}, {-5, -1}, {5, 0}, {-5, 0}, {5, 0}, {-5, 0}}), + Arguments.of(0, 0, 0, 5, + new int[][] { + {0, -5}, {0, -4}, {0, -3}, {0, -2}, {0, -1}, {0, 0}, {0, 1}, {0, 2}, {0, 3}, {0, 4}, {0, 5} // Only vertical line points and center + }), + Arguments.of(0, 0, 5, 0, + new int[][] { + {-5, 0}, {-4, 0}, {-3, 0}, {-2, 0}, {-1, 0}, {0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0} // Only horizontal line points and center + }), + Arguments.of(0, 0, 0, 0, + new int[][] { + {0, 0} // Only center point + }), + Arguments.of(0, 0, 4, 4, + new int[][] { + {0, 4}, + {0, 4}, + {0, -4}, + {0, -4}, + {1, 4}, + {-1, 4}, + {1, -4}, + {-1, -4}, + {2, 3}, + {-2, 3}, + {2, -3}, + {-2, -3}, + {3, 3}, + {-3, 3}, + {3, -3}, + {-3, -3}, + {3, 2}, + {-3, 2}, + {3, -2}, + {-3, -2}, + {4, 1}, + {-4, 1}, + {4, -1}, + {-4, -1}, + {4, 0}, + {-4, 0}, + {4, 0}, + {-4, 0}, + })); + } + + /** + * Tests the drawEllipse method with various parameters. + * + * @param centerX the x-coordinate of the center of the ellipse + * @param centerY the y-coordinate of the center of the ellipse + * @param a the length of the semi-major axis + * @param b the length of the semi-minor axis + * @param expectedPoints the expected points forming the ellipse + */ + @ParameterizedTest + @MethodSource("ellipseTestProvider") + @DisplayName("Test drawing ellipses with various parameters") + void testDrawEllipse(int centerX, int centerY, int a, int b, int[][] expectedPoints) { + List<int[]> points = MidpointEllipse.drawEllipse(centerX, centerY, a, b); + + // Validate the number of points and the specific points + assertEquals(expectedPoints.length, points.size(), "Number of points should match expected."); + + for (int i = 0; i < expectedPoints.length; i++) { + assertArrayEquals(expectedPoints[i], points.get(i), "Point mismatch at index " + i); + } + } +} diff --git a/src/test/java/com/thealgorithms/graph/ConstrainedShortestPathTest.java b/src/test/java/com/thealgorithms/graph/ConstrainedShortestPathTest.java new file mode 100644 index 000000000000..eccd359f2634 --- /dev/null +++ b/src/test/java/com/thealgorithms/graph/ConstrainedShortestPathTest.java @@ -0,0 +1,218 @@ +package com.thealgorithms.graph; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.thealgorithms.graph.ConstrainedShortestPath.Graph; +import org.junit.jupiter.api.Test; + +public class ConstrainedShortestPathTest { + + /** + * Tests a simple linear graph to verify if the solver calculates the shortest path correctly. + * Expected: The minimal path cost from node 0 to node 2 should be 5 while not exceeding the resource limit. + */ + @Test + public void testSimpleGraph() { + Graph graph = new Graph(3); + graph.addEdge(0, 1, 2, 3); + graph.addEdge(1, 2, 3, 2); + + int maxResource = 5; + ConstrainedShortestPath solver = new ConstrainedShortestPath(graph, maxResource); + + assertEquals(5, solver.solve(0, 2)); + } + + /** + * Tests a graph where no valid path exists due to resource constraints. + * Expected: The solver should return -1, indicating no path is feasible. + */ + @Test + public void testNoPath() { + Graph graph = new Graph(3); + graph.addEdge(0, 1, 2, 6); + graph.addEdge(1, 2, 3, 6); + + int maxResource = 5; + ConstrainedShortestPath solver = new ConstrainedShortestPath(graph, maxResource); + + assertEquals(-1, solver.solve(0, 2)); + } + + /** + * Tests a graph with multiple paths between source and destination. + * Expected: The solver should choose the path with the minimal cost of 5, considering the resource limit. + */ + @Test + public void testMultiplePaths() { + Graph graph = new Graph(4); + graph.addEdge(0, 1, 1, 1); + graph.addEdge(1, 3, 5, 2); + graph.addEdge(0, 2, 2, 1); + graph.addEdge(2, 3, 3, 2); + + int maxResource = 3; + ConstrainedShortestPath solver = new ConstrainedShortestPath(graph, maxResource); + + assertEquals(5, solver.solve(0, 3)); + } + + /** + * Verifies that the solver allows a path exactly matching the resource limit. + * Expected: The path is valid with a total cost of 5. + */ + @Test + public void testExactResourceLimit() { + Graph graph = new Graph(3); + graph.addEdge(0, 1, 2, 3); + graph.addEdge(1, 2, 3, 2); + + int maxResource = 5; + ConstrainedShortestPath solver = new ConstrainedShortestPath(graph, maxResource); + + assertEquals(5, solver.solve(0, 2)); + } + + /** + * Tests a disconnected graph where the destination node cannot be reached. + * Expected: The solver should return -1, as the destination is unreachable. + */ + @Test + public void testDisconnectedGraph() { + Graph graph = new Graph(4); + graph.addEdge(0, 1, 2, 2); + graph.addEdge(2, 3, 3, 2); + + int maxResource = 5; + ConstrainedShortestPath solver = new ConstrainedShortestPath(graph, maxResource); + + assertEquals(-1, solver.solve(0, 3)); + } + + /** + * Tests a graph with cycles to ensure the solver does not fall into infinite loops and correctly calculates costs. + * Expected: The solver should compute the minimal path cost of 6. + */ + @Test + public void testGraphWithCycles() { + Graph graph = new Graph(4); + graph.addEdge(0, 1, 2, 1); + graph.addEdge(1, 2, 3, 1); + graph.addEdge(2, 0, 1, 1); + graph.addEdge(1, 3, 4, 2); + + int maxResource = 3; + ConstrainedShortestPath solver = new ConstrainedShortestPath(graph, maxResource); + + assertEquals(6, solver.solve(0, 3)); + } + + /** + * Tests the solver's performance and correctness on a large linear graph with 1000 nodes. + * Expected: The solver should efficiently calculate the shortest path with a cost of 999. + */ + @Test + public void testLargeGraphPerformance() { + int nodeCount = 1000; + Graph graph = new Graph(nodeCount); + for (int i = 0; i < nodeCount - 1; i++) { + graph.addEdge(i, i + 1, 1, 1); + } + + int maxResource = 1000; + ConstrainedShortestPath solver = new ConstrainedShortestPath(graph, maxResource); + + assertEquals(999, solver.solve(0, nodeCount - 1)); + } + + /** + * Tests a graph with isolated nodes to ensure the solver recognizes unreachable destinations. + * Expected: The solver should return -1 for unreachable nodes. + */ + @Test + public void testIsolatedNodes() { + Graph graph = new Graph(5); + graph.addEdge(0, 1, 2, 1); + graph.addEdge(1, 2, 3, 1); + + int maxResource = 5; + ConstrainedShortestPath solver = new ConstrainedShortestPath(graph, maxResource); + + assertEquals(-1, solver.solve(0, 3)); + } + + /** + * Tests a cyclic large graph with multiple overlapping paths. + * Expected: The solver should calculate the shortest path cost of 5. + */ + @Test + public void testCyclicLargeGraph() { + Graph graph = new Graph(10); + for (int i = 0; i < 9; i++) { + graph.addEdge(i, (i + 1) % 10, 1, 1); + } + graph.addEdge(0, 5, 5, 3); + + int maxResource = 10; + ConstrainedShortestPath solver = new ConstrainedShortestPath(graph, maxResource); + + assertEquals(5, solver.solve(0, 5)); + } + + /** + * Tests a large complex graph with multiple paths and varying resource constraints. + * Expected: The solver should identify the optimal path with a cost of 19 within the resource limit. + */ + @Test + public void testLargeComplexGraph() { + Graph graph = new Graph(10); + graph.addEdge(0, 1, 4, 2); + graph.addEdge(0, 2, 3, 3); + graph.addEdge(1, 3, 2, 1); + graph.addEdge(2, 3, 5, 2); + graph.addEdge(2, 4, 8, 4); + graph.addEdge(3, 5, 7, 3); + graph.addEdge(3, 6, 6, 2); + graph.addEdge(4, 6, 3, 2); + graph.addEdge(5, 7, 1, 1); + graph.addEdge(6, 7, 2, 2); + graph.addEdge(7, 8, 3, 1); + graph.addEdge(8, 9, 2, 1); + + int maxResource = 10; + ConstrainedShortestPath solver = new ConstrainedShortestPath(graph, maxResource); + + assertEquals(19, solver.solve(0, 9)); + } + + /** + * Edge case test where the graph has only one node and no edges. + * Expected: The minimal path cost is 0, as the start and destination are the same. + */ + @Test + public void testSingleNodeGraph() { + Graph graph = new Graph(1); + + int maxResource = 0; + ConstrainedShortestPath solver = new ConstrainedShortestPath(graph, maxResource); + + assertEquals(0, solver.solve(0, 0)); + } + + /** + * Tests a graph with multiple paths but a tight resource constraint. + * Expected: The solver should return -1 if no path can be found within the resource limit. + */ + @Test + public void testTightResourceConstraint() { + Graph graph = new Graph(4); + graph.addEdge(0, 1, 3, 4); + graph.addEdge(1, 2, 1, 2); + graph.addEdge(0, 2, 2, 2); + + int maxResource = 3; + ConstrainedShortestPath solver = new ConstrainedShortestPath(graph, maxResource); + + assertEquals(2, solver.solve(0, 2)); + } +} diff --git a/src/test/java/com/thealgorithms/graph/StronglyConnectedComponentOptimizedTest.java b/src/test/java/com/thealgorithms/graph/StronglyConnectedComponentOptimizedTest.java new file mode 100644 index 000000000000..9473a328c982 --- /dev/null +++ b/src/test/java/com/thealgorithms/graph/StronglyConnectedComponentOptimizedTest.java @@ -0,0 +1,80 @@ +package com.thealgorithms.graph; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class StronglyConnectedComponentOptimizedTest { + + private StronglyConnectedComponentOptimized sccOptimized; + + @BeforeEach + public void setUp() { + sccOptimized = new StronglyConnectedComponentOptimized(); + } + + @Test + public void testSingleComponent() { + // Create a simple graph with 3 nodes, all forming one SCC + HashMap<Integer, List<Integer>> adjList = new HashMap<>(); + adjList.put(0, new ArrayList<>(List.of(1))); + adjList.put(1, new ArrayList<>(List.of(2))); + adjList.put(2, new ArrayList<>(List.of(0))); + + int result = sccOptimized.getOutput(adjList, 3); + + // The entire graph is one strongly connected component + assertEquals(1, result, "There should be 1 strongly connected component."); + } + + @Test + public void testTwoComponents() { + // Create a graph with 4 nodes and two SCCs: {0, 1, 2} and {3} + HashMap<Integer, List<Integer>> adjList = new HashMap<>(); + adjList.put(0, new ArrayList<>(List.of(1))); + adjList.put(1, new ArrayList<>(List.of(2))); + adjList.put(2, new ArrayList<>(List.of(0))); + adjList.put(3, new ArrayList<>()); + + int result = sccOptimized.getOutput(adjList, 4); + + // There are 2 SCCs: {0, 1, 2} and {3} + assertEquals(2, result, "There should be 2 strongly connected components."); + } + + @Test + public void testDisconnectedGraph() { + // Create a graph with 4 nodes that are all disconnected + HashMap<Integer, List<Integer>> adjList = new HashMap<>(); + adjList.put(0, new ArrayList<>()); + adjList.put(1, new ArrayList<>()); + adjList.put(2, new ArrayList<>()); + adjList.put(3, new ArrayList<>()); + + int result = sccOptimized.getOutput(adjList, 4); + + // Each node is its own strongly connected component + assertEquals(4, result, "There should be 4 strongly connected components."); + } + + @Test + public void testComplexGraph() { + // Create a more complex graph with multiple SCCs + HashMap<Integer, List<Integer>> adjList = new HashMap<>(); + adjList.put(0, new ArrayList<>(List.of(1))); + adjList.put(1, new ArrayList<>(List.of(2))); + adjList.put(2, new ArrayList<>(List.of(0, 3))); + adjList.put(3, new ArrayList<>(List.of(4))); + adjList.put(4, new ArrayList<>(List.of(5))); + adjList.put(5, new ArrayList<>(List.of(3))); + + int result = sccOptimized.getOutput(adjList, 6); + + // There are 2 SCCs: {0, 1, 2} and {3, 4, 5} + assertEquals(2, result, "There should be 2 strongly connected components."); + } +} diff --git a/src/test/java/com/thealgorithms/graph/TravelingSalesmanTest.java b/src/test/java/com/thealgorithms/graph/TravelingSalesmanTest.java new file mode 100644 index 000000000000..b93c9f89c944 --- /dev/null +++ b/src/test/java/com/thealgorithms/graph/TravelingSalesmanTest.java @@ -0,0 +1,127 @@ +package com.thealgorithms.graph; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class TravelingSalesmanTest { + + // Test Case 1: A simple distance matrix with 4 cities + @Test + public void testBruteForceSimple() { + int[][] distanceMatrix = {{0, 10, 15, 20}, {10, 0, 35, 25}, {15, 35, 0, 30}, {20, 25, 30, 0}}; + int expectedMinDistance = 80; + int result = TravelingSalesman.bruteForce(distanceMatrix); + assertEquals(expectedMinDistance, result); + } + + @Test + public void testDynamicProgrammingSimple() { + int[][] distanceMatrix = {{0, 10, 15, 20}, {10, 0, 35, 25}, {15, 35, 0, 30}, {20, 25, 30, 0}}; + int expectedMinDistance = 80; + int result = TravelingSalesman.dynamicProgramming(distanceMatrix); + assertEquals(expectedMinDistance, result); + } + + // Test Case 2: A distance matrix with 3 cities + @Test + public void testBruteForceThreeCities() { + int[][] distanceMatrix = {{0, 10, 15}, {10, 0, 35}, {15, 35, 0}}; + int expectedMinDistance = 60; + int result = TravelingSalesman.bruteForce(distanceMatrix); + assertEquals(expectedMinDistance, result); + } + + @Test + public void testDynamicProgrammingThreeCities() { + int[][] distanceMatrix = {{0, 10, 15}, {10, 0, 35}, {15, 35, 0}}; + int expectedMinDistance = 60; + int result = TravelingSalesman.dynamicProgramming(distanceMatrix); + assertEquals(expectedMinDistance, result); + } + + // Test Case 3: A distance matrix with 5 cities (larger input) + @Test + public void testBruteForceFiveCities() { + int[][] distanceMatrix = {{0, 2, 9, 10, 1}, {2, 0, 6, 5, 8}, {9, 6, 0, 4, 3}, {10, 5, 4, 0, 7}, {1, 8, 3, 7, 0}}; + int expectedMinDistance = 15; + int result = TravelingSalesman.bruteForce(distanceMatrix); + assertEquals(expectedMinDistance, result); + } + + @Test + public void testDynamicProgrammingFiveCities() { + int[][] distanceMatrix = {{0, 2, 9, 10, 1}, {2, 0, 6, 5, 8}, {9, 6, 0, 4, 3}, {10, 5, 4, 0, 7}, {1, 8, 3, 7, 0}}; + int expectedMinDistance = 15; + int result = TravelingSalesman.dynamicProgramming(distanceMatrix); + assertEquals(expectedMinDistance, result); + } + + // Test Case 4: A distance matrix with 2 cities (simple case) + @Test + public void testBruteForceTwoCities() { + int[][] distanceMatrix = {{0, 1}, {1, 0}}; + int expectedMinDistance = 2; + int result = TravelingSalesman.bruteForce(distanceMatrix); + assertEquals(expectedMinDistance, result); + } + + @Test + public void testDynamicProgrammingTwoCities() { + int[][] distanceMatrix = {{0, 1}, {1, 0}}; + int expectedMinDistance = 2; + int result = TravelingSalesman.dynamicProgramming(distanceMatrix); + assertEquals(expectedMinDistance, result); + } + + // Test Case 5: A distance matrix with identical distances + @Test + public void testBruteForceEqualDistances() { + int[][] distanceMatrix = {{0, 10, 10, 10}, {10, 0, 10, 10}, {10, 10, 0, 10}, {10, 10, 10, 0}}; + int expectedMinDistance = 40; + int result = TravelingSalesman.bruteForce(distanceMatrix); + assertEquals(expectedMinDistance, result); + } + + @Test + public void testDynamicProgrammingEqualDistances() { + int[][] distanceMatrix = {{0, 10, 10, 10}, {10, 0, 10, 10}, {10, 10, 0, 10}, {10, 10, 10, 0}}; + int expectedMinDistance = 40; + int result = TravelingSalesman.dynamicProgramming(distanceMatrix); + assertEquals(expectedMinDistance, result); + } + + // Test Case 6: A distance matrix with only one city + @Test + public void testBruteForceOneCity() { + int[][] distanceMatrix = {{0}}; + int expectedMinDistance = 0; + int result = TravelingSalesman.bruteForce(distanceMatrix); + assertEquals(expectedMinDistance, result); + } + + @Test + public void testDynamicProgrammingOneCity() { + int[][] distanceMatrix = {{0}}; + int expectedMinDistance = 0; + int result = TravelingSalesman.dynamicProgramming(distanceMatrix); + assertEquals(expectedMinDistance, result); + } + + // Test Case 7: Distance matrix with large numbers + @Test + public void testBruteForceLargeNumbers() { + int[][] distanceMatrix = {{0, 1000000, 2000000}, {1000000, 0, 1500000}, {2000000, 1500000, 0}}; + int expectedMinDistance = 4500000; + int result = TravelingSalesman.bruteForce(distanceMatrix); + assertEquals(expectedMinDistance, result); + } + + @Test + public void testDynamicProgrammingLargeNumbers() { + int[][] distanceMatrix = {{0, 1000000, 2000000}, {1000000, 0, 1500000}, {2000000, 1500000, 0}}; + int expectedMinDistance = 4500000; + int result = TravelingSalesman.dynamicProgramming(distanceMatrix); + assertEquals(expectedMinDistance, result); + } +} diff --git a/src/test/java/com/thealgorithms/greedyalgorithms/ActivitySelectionTest.java b/src/test/java/com/thealgorithms/greedyalgorithms/ActivitySelectionTest.java new file mode 100644 index 000000000000..d24264a321bc --- /dev/null +++ b/src/test/java/com/thealgorithms/greedyalgorithms/ActivitySelectionTest.java @@ -0,0 +1,44 @@ +package com.thealgorithms.greedyalgorithms; + +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class ActivitySelectionTest { + @Test + public void testActivitySelection() { + int[] start = {1, 3, 0, 5, 8, 5}; + int[] end = {2, 4, 6, 7, 9, 9}; + + ArrayList<Integer> result = ActivitySelection.activitySelection(start, end); + ArrayList<Integer> expected = new ArrayList<>(Arrays.asList(0, 1, 3, 4)); + + assertEquals(expected, result); + } + + @Test + public void testSingleActivity() { + int[] start = {1}; + int[] end = {2}; + + ArrayList<Integer> result = ActivitySelection.activitySelection(start, end); + List<Integer> expected = singletonList(0); + + assertEquals(expected, result); + } + + @Test + public void testNoOverlap() { + int[] start = {1, 2, 3}; + int[] end = {2, 3, 4}; + + ArrayList<Integer> result = ActivitySelection.activitySelection(start, end); + ArrayList<Integer> expected = new ArrayList<>(Arrays.asList(0, 1, 2)); + + assertEquals(expected, result); + } +} diff --git a/src/test/java/com/thealgorithms/greedyalgorithms/BandwidthAllocationTest.java b/src/test/java/com/thealgorithms/greedyalgorithms/BandwidthAllocationTest.java new file mode 100644 index 000000000000..452f7858c162 --- /dev/null +++ b/src/test/java/com/thealgorithms/greedyalgorithms/BandwidthAllocationTest.java @@ -0,0 +1,22 @@ +package com.thealgorithms.greedyalgorithms; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class BandwidthAllocationTest { + + @ParameterizedTest + @MethodSource("bandwidthProvider") + public void testMaxValue(int capacity, int[] bandwidths, int[] values, int expected) { + assertEquals(expected, BandwidthAllocation.maxValue(capacity, bandwidths, values)); + } + + private static Stream<Arguments> bandwidthProvider() { + return Stream.of(Arguments.of(50, new int[] {20, 10, 30}, new int[] {40, 20, 30}, 80), Arguments.of(0, new int[] {5, 10}, new int[] {10, 20}, 0), Arguments.of(5, new int[] {5, 10}, new int[] {10, 20}, 10), Arguments.of(15, new int[] {10, 20}, new int[] {10, 25}, 18), + Arguments.of(25, new int[] {10, 15, 20}, new int[] {10, 30, 50}, 60)); + } +} diff --git a/src/test/java/com/thealgorithms/greedyalgorithms/BinaryAdditionTest.java b/src/test/java/com/thealgorithms/greedyalgorithms/BinaryAdditionTest.java new file mode 100644 index 000000000000..893ca02ed8a3 --- /dev/null +++ b/src/test/java/com/thealgorithms/greedyalgorithms/BinaryAdditionTest.java @@ -0,0 +1,96 @@ +package com.thealgorithms.greedyalgorithms; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class BinaryAdditionTest { + + BinaryAddition binaryAddition = new BinaryAddition(); + + @Test + public void testEqualLengthNoCarry() { + String a = "1010"; + String b = "1101"; + String expected = "10111"; + assertEquals(expected, binaryAddition.addBinary(a, b)); + } + + @Test + public void testEqualLengthWithCarry() { + String a = "1111"; + String b = "1111"; + String expected = "11110"; + assertEquals(expected, binaryAddition.addBinary(a, b)); + } + + @Test + public void testDifferentLengths() { + String a = "101"; + String b = "11"; + String expected = "1000"; + assertEquals(expected, binaryAddition.addBinary(a, b)); + } + + @Test + public void testAllZeros() { + String a = "0"; + String b = "0"; + String expected = "0"; + assertEquals(expected, binaryAddition.addBinary(a, b)); + } + + @Test + public void testAllOnes() { + String a = "1111"; + String b = "1111"; + String expected = "11110"; + assertEquals(expected, binaryAddition.addBinary(a, b)); + } + + @Test + public void testOneZeroString() { + String a = "0"; + String b = "10101"; + String expected = "10101"; + assertEquals(expected, binaryAddition.addBinary(a, b)); + + // Test the other way around + a = "10101"; + b = "0"; + expected = "10101"; + assertEquals(expected, binaryAddition.addBinary(a, b)); + } + + @Test + public void testLargeBinaryNumbers() { + String a = "101010101010101010101010101010"; + String b = "110110110110110110110110110110"; + String expected = "1100001100001100001100001100000"; + assertEquals(expected, binaryAddition.addBinary(a, b)); + } + + @Test + public void testOneMuchLonger() { + String a = "1"; + String b = "11111111"; + String expected = "100000000"; + assertEquals(expected, binaryAddition.addBinary(a, b)); + } + + @Test + public void testEmptyStrings() { + String a = ""; + String b = ""; + String expected = ""; // Adding two empty strings should return 0 + assertEquals(expected, binaryAddition.addBinary(a, b)); + } + + @Test + public void testAlternatingBits() { + String a = "10101010"; + String b = "01010101"; + String expected = "11111111"; + assertEquals(expected, binaryAddition.addBinary(a, b)); + } +} diff --git a/src/test/java/com/thealgorithms/greedyalgorithms/CoinChangeTest.java b/src/test/java/com/thealgorithms/greedyalgorithms/CoinChangeTest.java new file mode 100644 index 000000000000..b9745be63088 --- /dev/null +++ b/src/test/java/com/thealgorithms/greedyalgorithms/CoinChangeTest.java @@ -0,0 +1,60 @@ +package com.thealgorithms.greedyalgorithms; + +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class CoinChangeTest { + @Test + public void testCoinChangeProblemWithValidAmount() { + ArrayList<Integer> expected = new ArrayList<>(Arrays.asList(500, 50, 20, 20, 1)); + ArrayList<Integer> coins = CoinChange.coinChangeProblem(591); + assertEquals(expected, coins); + } + + @Test + public void testCoinChangeProblemWithLargeAmount() { + List<Integer> expected = singletonList(2000); + ArrayList<Integer> coins = CoinChange.coinChangeProblem(2000); + assertEquals(expected, coins); + } + + @Test + public void testCoinChangeProblemWithPartialCoins2() { + ArrayList<Integer> expected = new ArrayList<>(Arrays.asList(500, 50, 20)); + ArrayList<Integer> coins = CoinChange.coinChangeProblem(570); + assertEquals(expected, coins); + } + + @Test + public void testCoinChangeProblemWithSmallAmount() { + ArrayList<Integer> expected = new ArrayList<>(Arrays.asList(2, 1)); + ArrayList<Integer> coins = CoinChange.coinChangeProblem(3); + assertEquals(expected, coins); + } + + @Test + public void testCoinChangeProblemWithLargeAmountAndMultipleDenominations() { + ArrayList<Integer> expected = new ArrayList<>(Arrays.asList(2000, 2000, 2000, 2000, 500, 500, 500, 100, 100, 100, 100, 50, 20, 20, 5, 2, 2)); + ArrayList<Integer> coins = CoinChange.coinChangeProblem(9999); + assertEquals(expected, coins); + } + + @Test + public void testCoinChangeProblemWithAllDenominations() { + ArrayList<Integer> expected = new ArrayList<>(Arrays.asList(2000, 500, 100, 100, 100, 50, 20, 10, 5, 2, 1)); + ArrayList<Integer> coins = CoinChange.coinChangeProblem(2888); + assertEquals(expected, coins); + } + + @Test + public void testCoinChangeProblemWithZeroAmount() { + ArrayList<Integer> expected = new ArrayList<>(); + ArrayList<Integer> coins = CoinChange.coinChangeProblem(0); + assertEquals(expected, coins); + } +} diff --git a/src/test/java/com/thealgorithms/greedyalgorithms/DigitSeparationTest.java b/src/test/java/com/thealgorithms/greedyalgorithms/DigitSeparationTest.java new file mode 100644 index 000000000000..1fe018ecce18 --- /dev/null +++ b/src/test/java/com/thealgorithms/greedyalgorithms/DigitSeparationTest.java @@ -0,0 +1,78 @@ +package com.thealgorithms.greedyalgorithms; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.junit.jupiter.api.Test; +public class DigitSeparationTest { + + @Test + public void testDigitSeparationReverseOrderSingleDigit() { + DigitSeparation digitSeparation = new DigitSeparation(); + List<Long> result = digitSeparation.digitSeparationReverseOrder(5); + assertEquals(List.of(5L), result); + } + + @Test + public void testDigitSeparationReverseOrderMultipleDigits() { + DigitSeparation digitSeparation = new DigitSeparation(); + List<Long> result = digitSeparation.digitSeparationReverseOrder(123); + assertEquals(List.of(3L, 2L, 1L), result); + } + + @Test + public void testDigitSeparationReverseOrderLargeNumber() { + DigitSeparation digitSeparation = new DigitSeparation(); + List<Long> result = digitSeparation.digitSeparationReverseOrder(123456789); + assertEquals(List.of(9L, 8L, 7L, 6L, 5L, 4L, 3L, 2L, 1L), result); + } + + @Test + public void testDigitSeparationReverseOrderZero() { + DigitSeparation digitSeparation = new DigitSeparation(); + List<Long> result = digitSeparation.digitSeparationReverseOrder(0); + assertEquals(List.of(0L), result); + } + + @Test + public void testDigitSeparationReverseOrderNegativeNumbers() { + DigitSeparation digitSeparation = new DigitSeparation(); + List<Long> result = digitSeparation.digitSeparationReverseOrder(-123); + assertEquals(List.of(3L, 2L, 1L), result); + } + + @Test + public void testDigitSeparationForwardOrderSingleDigit() { + DigitSeparation digitSeparation = new DigitSeparation(); + List<Long> result = digitSeparation.digitSeparationForwardOrder(5); + assertEquals(List.of(5L), result); + } + + @Test + public void testDigitSeparationForwardOrderMultipleDigits() { + DigitSeparation digitSeparation = new DigitSeparation(); + List<Long> result = digitSeparation.digitSeparationForwardOrder(123); + assertEquals(List.of(1L, 2L, 3L), result); + } + + @Test + public void testDigitSeparationForwardOrderLargeNumber() { + DigitSeparation digitSeparation = new DigitSeparation(); + List<Long> result = digitSeparation.digitSeparationForwardOrder(123456789); + assertEquals(List.of(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L), result); + } + + @Test + public void testDigitSeparationForwardOrderZero() { + DigitSeparation digitSeparation = new DigitSeparation(); + List<Long> result = digitSeparation.digitSeparationForwardOrder(0); + assertEquals(List.of(0L), result); + } + + @Test + public void testDigitSeparationForwardOrderNegativeNumber() { + DigitSeparation digitSeparation = new DigitSeparation(); + List<Long> result = digitSeparation.digitSeparationForwardOrder(-123); + assertEquals(List.of(1L, 2L, 3L), result); + } +} diff --git a/src/test/java/com/thealgorithms/greedyalgorithms/EgyptianFractionTest.java b/src/test/java/com/thealgorithms/greedyalgorithms/EgyptianFractionTest.java new file mode 100644 index 000000000000..1d34876de864 --- /dev/null +++ b/src/test/java/com/thealgorithms/greedyalgorithms/EgyptianFractionTest.java @@ -0,0 +1,22 @@ +package com.thealgorithms.greedyalgorithms; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class EgyptianFractionTest { + + @ParameterizedTest + @MethodSource("fractionProvider") + public void testGetEgyptianFraction(int numerator, int denominator, List<String> expected) { + assertEquals(expected, EgyptianFraction.getEgyptianFraction(numerator, denominator)); + } + + private static Stream<Arguments> fractionProvider() { + return Stream.of(Arguments.of(2, 3, List.of("1/2", "1/6")), Arguments.of(3, 10, List.of("1/4", "1/20")), Arguments.of(1, 3, List.of("1/3")), Arguments.of(1, 2, List.of("1/2")), Arguments.of(4, 13, List.of("1/4", "1/18", "1/468"))); + } +} diff --git a/src/test/java/com/thealgorithms/greedyalgorithms/FractionalKnapsackTest.java b/src/test/java/com/thealgorithms/greedyalgorithms/FractionalKnapsackTest.java new file mode 100644 index 000000000000..34ec354ba6f0 --- /dev/null +++ b/src/test/java/com/thealgorithms/greedyalgorithms/FractionalKnapsackTest.java @@ -0,0 +1,32 @@ +package com.thealgorithms.greedyalgorithms; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class FractionalKnapsackTest { + + @Test + public void testFractionalKnapsackWithExampleCase() { + int[] weight = {10, 20, 30}; + int[] value = {60, 100, 120}; + int capacity = 50; + assertEquals(240, FractionalKnapsack.fractionalKnapsack(weight, value, capacity)); + } + + @Test + public void testFractionalKnapsackWithZeroCapacity() { + int[] weight = {10, 20, 30}; + int[] value = {60, 100, 120}; + int capacity = 0; + assertEquals(0, FractionalKnapsack.fractionalKnapsack(weight, value, capacity)); + } + + @Test + public void testFractionalKnapsackWithEmptyItems() { + int[] weight = {}; + int[] value = {}; + int capacity = 50; + assertEquals(0, FractionalKnapsack.fractionalKnapsack(weight, value, capacity)); + } +} diff --git a/src/test/java/com/thealgorithms/greedyalgorithms/GaleShapleyTest.java b/src/test/java/com/thealgorithms/greedyalgorithms/GaleShapleyTest.java new file mode 100644 index 000000000000..fcd173202469 --- /dev/null +++ b/src/test/java/com/thealgorithms/greedyalgorithms/GaleShapleyTest.java @@ -0,0 +1,72 @@ +package com.thealgorithms.greedyalgorithms; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; + +public class GaleShapleyTest { + + @Test + public void testStableMatch() { + Map<String, LinkedList<String>> womenPrefs = new HashMap<>(); + womenPrefs.put("A", new LinkedList<>(List.of("X", "Y", "Z"))); + womenPrefs.put("B", new LinkedList<>(List.of("Y", "X", "Z"))); + womenPrefs.put("C", new LinkedList<>(List.of("X", "Y", "Z"))); + + Map<String, LinkedList<String>> menPrefs = new HashMap<>(); + menPrefs.put("X", new LinkedList<>(List.of("A", "B", "C"))); + menPrefs.put("Y", new LinkedList<>(List.of("B", "A", "C"))); + menPrefs.put("Z", new LinkedList<>(List.of("A", "B", "C"))); + + Map<String, String> result = GaleShapley.stableMatch(womenPrefs, menPrefs); + + Map<String, String> expected = new HashMap<>(); + expected.put("A", "X"); + expected.put("B", "Y"); + expected.put("C", "Z"); + + assertEquals(expected, result); + } + + @Test + public void testSinglePair() { + Map<String, LinkedList<String>> womenPrefs = new HashMap<>(); + womenPrefs.put("A", new LinkedList<>(List.of("X"))); + + Map<String, LinkedList<String>> menPrefs = new HashMap<>(); + menPrefs.put("X", new LinkedList<>(List.of("A"))); + + Map<String, String> result = GaleShapley.stableMatch(womenPrefs, menPrefs); + + Map<String, String> expected = new HashMap<>(); + expected.put("A", "X"); + + assertEquals(expected, result); + } + + @Test + public void testEqualPreferences() { + Map<String, LinkedList<String>> womenPrefs = new HashMap<>(); + womenPrefs.put("A", new LinkedList<>(List.of("X", "Y", "Z"))); + womenPrefs.put("B", new LinkedList<>(List.of("X", "Y", "Z"))); + womenPrefs.put("C", new LinkedList<>(List.of("X", "Y", "Z"))); + + Map<String, LinkedList<String>> menPrefs = new HashMap<>(); + menPrefs.put("X", new LinkedList<>(List.of("A", "B", "C"))); + menPrefs.put("Y", new LinkedList<>(List.of("A", "B", "C"))); + menPrefs.put("Z", new LinkedList<>(List.of("A", "B", "C"))); + + Map<String, String> result = GaleShapley.stableMatch(womenPrefs, menPrefs); + + Map<String, String> expected = new HashMap<>(); + expected.put("A", "X"); + expected.put("B", "Y"); + expected.put("C", "Z"); + + assertEquals(expected, result); + } +} diff --git a/src/test/java/com/thealgorithms/greedyalgorithms/JobSequencingTest.java b/src/test/java/com/thealgorithms/greedyalgorithms/JobSequencingTest.java new file mode 100644 index 000000000000..b679121689a1 --- /dev/null +++ b/src/test/java/com/thealgorithms/greedyalgorithms/JobSequencingTest.java @@ -0,0 +1,23 @@ +package com.thealgorithms.greedyalgorithms; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.Collections; +import org.junit.jupiter.api.Test; + +public class JobSequencingTest { + @Test + public void testJobSequencingWithExampleCase() { + ArrayList<JobSequencing.Job> jobs = new ArrayList<>(); + jobs.add(new JobSequencing.Job('a', 2, 100)); + jobs.add(new JobSequencing.Job('b', 1, 19)); + jobs.add(new JobSequencing.Job('c', 2, 27)); + jobs.add(new JobSequencing.Job('d', 1, 25)); + jobs.add(new JobSequencing.Job('e', 3, 15)); + Collections.sort(jobs); + String jobSequence = JobSequencing.findJobSequence(jobs, jobs.size()); + + assertEquals("Job Sequence: c -> a -> e", jobSequence); + } +} diff --git a/src/test/java/com/thealgorithms/greedyalgorithms/KCentersTest.java b/src/test/java/com/thealgorithms/greedyalgorithms/KCentersTest.java new file mode 100644 index 000000000000..9202d3467f0c --- /dev/null +++ b/src/test/java/com/thealgorithms/greedyalgorithms/KCentersTest.java @@ -0,0 +1,15 @@ +package com.thealgorithms.greedyalgorithms; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class KCentersTest { + + @Test + public void testFindKCenters() { + int[][] distances = {{0, 2, 3, 4}, {2, 0, 5, 1}, {3, 5, 0, 7}, {4, 1, 7, 0}}; + assertEquals(4, KCenters.findKCenters(distances, 2)); + assertEquals(2, KCenters.findKCenters(distances, 4)); + } +} diff --git a/src/test/java/com/thealgorithms/greedyalgorithms/MergeIntervalsTest.java b/src/test/java/com/thealgorithms/greedyalgorithms/MergeIntervalsTest.java new file mode 100644 index 000000000000..0135f9d73260 --- /dev/null +++ b/src/test/java/com/thealgorithms/greedyalgorithms/MergeIntervalsTest.java @@ -0,0 +1,71 @@ +package com.thealgorithms.greedyalgorithms; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +public class MergeIntervalsTest { + + @Test + public void testMergeIntervalsWithOverlappingIntervals() { + // Test case where some intervals overlap and should be merged + int[][] intervals = {{1, 3}, {2, 6}, {8, 10}, {15, 18}}; + int[][] expected = {{1, 6}, {8, 10}, {15, 18}}; + int[][] result = MergeIntervals.merge(intervals); + assertArrayEquals(expected, result); + } + + @Test + public void testMergeIntervalsWithNoOverlap() { + // Test case where intervals do not overlap + int[][] intervals = {{1, 2}, {3, 4}, {5, 6}}; + int[][] expected = {{1, 2}, {3, 4}, {5, 6}}; + int[][] result = MergeIntervals.merge(intervals); + assertArrayEquals(expected, result); + } + + @Test + public void testMergeIntervalsWithCompleteOverlap() { + // Test case where intervals completely overlap + int[][] intervals = {{1, 5}, {2, 4}, {3, 6}}; + int[][] expected = {{1, 6}}; + int[][] result = MergeIntervals.merge(intervals); + assertArrayEquals(expected, result); + } + + @Test + public void testMergeIntervalsWithSingleInterval() { + // Test case where only one interval is given + int[][] intervals = {{1, 2}}; + int[][] expected = {{1, 2}}; + int[][] result = MergeIntervals.merge(intervals); + assertArrayEquals(expected, result); + } + + @Test + public void testMergeIntervalsWithEmptyArray() { + // Test case where the input array is empty + int[][] intervals = {}; + int[][] expected = {}; + int[][] result = MergeIntervals.merge(intervals); + assertArrayEquals(expected, result); + } + + @Test + public void testMergeIntervalsWithIdenticalIntervals() { + // Test case where multiple identical intervals are given + int[][] intervals = {{1, 3}, {1, 3}, {1, 3}}; + int[][] expected = {{1, 3}}; + int[][] result = MergeIntervals.merge(intervals); + assertArrayEquals(expected, result); + } + + @Test + public void testMergeIntervalsWithRandomIntervals() { + // Test case with a mix of overlapping and non-overlapping intervals + int[][] intervals = {{1, 4}, {5, 7}, {2, 6}, {8, 10}}; + int[][] expected = {{1, 7}, {8, 10}}; + int[][] result = MergeIntervals.merge(intervals); + assertArrayEquals(expected, result); + } +} diff --git a/src/test/java/com/thealgorithms/greedyalgorithms/MinimizingLatenessTest.java b/src/test/java/com/thealgorithms/greedyalgorithms/MinimizingLatenessTest.java new file mode 100644 index 000000000000..04f6900d14db --- /dev/null +++ b/src/test/java/com/thealgorithms/greedyalgorithms/MinimizingLatenessTest.java @@ -0,0 +1,43 @@ +package com.thealgorithms.greedyalgorithms; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.thealgorithms.greedyalgorithms.MinimizingLateness.Job; +import org.junit.jupiter.api.Test; + +public class MinimizingLatenessTest { + + @Test + void testCalculateLateness() { + // Test case with three jobs + Job job1 = new Job("Job1", 4, 6); + Job job2 = new Job("Job2", 2, 8); + Job job3 = new Job("Job3", 1, 9); + Job job4 = new Job("Job4", 5, 9); + Job job5 = new Job("Job5", 4, 10); + Job job6 = new Job("Job6", 3, 5); + + MinimizingLateness.calculateLateness(job1, job2, job3, job4, job5, job6); + + // Check lateness for each job + assertEquals(6, job4.lateness); + assertEquals(0, job6.lateness); + assertEquals(1, job2.lateness); + } + + @Test + void testCheckStartTime() { + + Job job1 = new Job("Job1", 2, 5); + Job job2 = new Job("Job2", 1, 7); + Job job3 = new Job("Job3", 3, 8); + Job job4 = new Job("Job4", 2, 4); + Job job5 = new Job("Job5", 4, 10); + + MinimizingLateness.calculateLateness(job1, job2, job3, job4, job5); + + assertEquals(2, job1.startTime); + assertEquals(5, job3.startTime); + assertEquals(8, job5.startTime); + } +} diff --git a/src/test/java/com/thealgorithms/greedyalgorithms/MinimumWaitingTimeTest.java b/src/test/java/com/thealgorithms/greedyalgorithms/MinimumWaitingTimeTest.java new file mode 100644 index 000000000000..64cb4b80f182 --- /dev/null +++ b/src/test/java/com/thealgorithms/greedyalgorithms/MinimumWaitingTimeTest.java @@ -0,0 +1,21 @@ +package com.thealgorithms.greedyalgorithms; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class MinimumWaitingTimeTest { + + @ParameterizedTest + @MethodSource("provideTestCases") + public void testMinimumWaitingTime(int[] queries, int expected) { + assertEquals(expected, MinimumWaitingTime.minimumWaitingTime(queries)); + } + + private static Stream<Arguments> provideTestCases() { + return Stream.of(Arguments.of(new int[] {3, 2, 1, 2, 6}, 17), Arguments.of(new int[] {3, 2, 1}, 4), Arguments.of(new int[] {1, 2, 3, 4}, 10), Arguments.of(new int[] {5, 5, 5, 5}, 30), Arguments.of(new int[] {}, 0)); + } +} diff --git a/src/test/java/com/thealgorithms/greedyalgorithms/OptimalFileMergingTest.java b/src/test/java/com/thealgorithms/greedyalgorithms/OptimalFileMergingTest.java new file mode 100644 index 000000000000..9ff2b95ff2e5 --- /dev/null +++ b/src/test/java/com/thealgorithms/greedyalgorithms/OptimalFileMergingTest.java @@ -0,0 +1,22 @@ +package com.thealgorithms.greedyalgorithms; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class OptimalFileMergingTest { + + @ParameterizedTest + @MethodSource("fileMergingProvider") + public void testMinMergeCost(int[] files, int expected) { + assertEquals(expected, OptimalFileMerging.minMergeCost(files)); + } + + private static Stream<Arguments> fileMergingProvider() { + return Stream.of(Arguments.of(new int[] {4, 3, 2, 6}, 29), Arguments.of(new int[] {5}, 0), Arguments.of(new int[] {2, 2, 2}, 10), Arguments.of(new int[] {10, 5, 3, 2}, 35), Arguments.of(new int[] {1, 1, 1, 1}, 8), Arguments.of(new int[] {1, 2, 3, 4, 5}, 33), + Arguments.of(new int[] {1, 2, 3, 4, 5, 6}, 51), Arguments.of(new int[] {1, 2, 3, 4, 5, 6, 7}, 74), Arguments.of(new int[] {1, 2, 3, 4, 5, 6, 7, 8}, 102), Arguments.of(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9}, 135)); + } +} diff --git a/src/test/java/com/thealgorithms/greedyalgorithms/StockProfitCalculatorTest.java b/src/test/java/com/thealgorithms/greedyalgorithms/StockProfitCalculatorTest.java new file mode 100644 index 000000000000..afad76a34521 --- /dev/null +++ b/src/test/java/com/thealgorithms/greedyalgorithms/StockProfitCalculatorTest.java @@ -0,0 +1,21 @@ +package com.thealgorithms.greedyalgorithms; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class StockProfitCalculatorTest { + + @ParameterizedTest + @MethodSource("provideTestCases") + public void testMaxProfit(int[] prices, int expected) { + assertEquals(expected, StockProfitCalculator.maxProfit(prices)); + } + + private static Stream<Arguments> provideTestCases() { + return Stream.of(Arguments.of(new int[] {7, 1, 5, 3, 6, 4}, 5), Arguments.of(new int[] {7, 6, 4, 3, 1}, 0), Arguments.of(new int[] {5, 5, 5, 5, 5}, 0), Arguments.of(new int[] {10}, 0), Arguments.of(new int[] {1, 5}, 4), Arguments.of(new int[] {2, 4, 1, 3, 7, 5}, 6)); + } +} diff --git a/src/test/java/com/thealgorithms/io/BufferedReaderTest.java b/src/test/java/com/thealgorithms/io/BufferedReaderTest.java new file mode 100644 index 000000000000..891c3066058e --- /dev/null +++ b/src/test/java/com/thealgorithms/io/BufferedReaderTest.java @@ -0,0 +1,99 @@ +package com.thealgorithms.io; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import org.junit.jupiter.api.Test; + +class BufferedReaderTest { + @Test + public void testPeeks() throws IOException { + String text = "Hello!\nWorld!"; + int len = text.length(); + byte[] bytes = text.getBytes(); + + ByteArrayInputStream input = new ByteArrayInputStream(bytes); + BufferedReader reader = new BufferedReader(input); + + // read the first letter + assertEquals(reader.read(), 'H'); + len--; + assertEquals(reader.available(), len); + + // position: H[e]llo!\nWorld! + // reader.read() will be == 'e' + assertEquals(reader.peek(1), 'l'); + assertEquals(reader.peek(2), 'l'); // second l + assertEquals(reader.peek(3), 'o'); + } + + @Test + public void testMixes() throws IOException { + String text = "Hello!\nWorld!"; + int len = text.length(); + byte[] bytes = text.getBytes(); + + ByteArrayInputStream input = new ByteArrayInputStream(bytes); + BufferedReader reader = new BufferedReader(input); + + // read the first letter + assertEquals(reader.read(), 'H'); // first letter + len--; + + assertEquals(reader.peek(1), 'l'); // third later (second letter after 'H') + assertEquals(reader.read(), 'e'); // second letter + len--; + assertEquals(reader.available(), len); + + // position: H[e]llo!\nWorld! + assertEquals(reader.peek(2), 'o'); // second l + assertEquals(reader.peek(3), '!'); + assertEquals(reader.peek(4), '\n'); + + assertEquals(reader.read(), 'l'); // third letter + assertEquals(reader.peek(1), 'o'); // fourth letter + + for (int i = 0; i < 6; i++) { + reader.read(); + } + try { + System.out.println((char) reader.peek(4)); + } catch (Exception ignored) { + System.out.println("[cached intentional error]"); + // intentional, for testing purpose + } + } + + @Test + public void testBlockPractical() throws IOException { + String text = "!Hello\nWorld!"; + byte[] bytes = text.getBytes(); + int len = bytes.length; + + ByteArrayInputStream input = new ByteArrayInputStream(bytes); + BufferedReader reader = new BufferedReader(input); + + assertEquals(reader.peek(), 'H'); + assertEquals(reader.read(), '!'); // read the first letter + len--; + + // this only reads the next 5 bytes (Hello) because + // the default buffer size = 5 + assertEquals(new String(reader.readBlock()), "Hello"); + len -= 5; + assertEquals(reader.available(), len); + + // maybe kind of a practical demonstration / use case + if (reader.read() == '\n') { + assertEquals(reader.read(), 'W'); + assertEquals(reader.read(), 'o'); + + // the rest of the blocks + assertEquals(new String(reader.readBlock()), "rld!"); + } else { + // should not reach + throw new IOException("Something not right"); + } + } +} diff --git a/src/test/java/com/thealgorithms/lineclipping/CohenSutherlandTest.java b/src/test/java/com/thealgorithms/lineclipping/CohenSutherlandTest.java new file mode 100644 index 000000000000..064f71a17c12 --- /dev/null +++ b/src/test/java/com/thealgorithms/lineclipping/CohenSutherlandTest.java @@ -0,0 +1,81 @@ +package com.thealgorithms.lineclipping; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import com.thealgorithms.lineclipping.utils.Line; +import com.thealgorithms.lineclipping.utils.Point; +import org.junit.jupiter.api.Test; + +/** + * @author shikarisohan + * @since 10/4/24 + */ +class CohenSutherlandTest { + + // Define the clipping window (1.0, 1.0) to (10.0, 10.0) + CohenSutherland cs = new CohenSutherland(1.0, 1.0, 10.0, 10.0); + + @Test + void testLineCompletelyInside() { + // Line fully inside the clipping window + Line line = new Line(new Point(2.0, 2.0), new Point(8.0, 8.0)); + Line clippedLine = cs.cohenSutherlandClip(line); + + assertNotNull(clippedLine, "Line should not be null."); + assertEquals(line, clippedLine, "Line inside the window should remain unchanged."); + } + + @Test + void testLineCompletelyOutside() { + // Line completely outside and above the clipping window + Line line = new Line(new Point(11.0, 12.0), new Point(15.0, 18.0)); + Line clippedLine = cs.cohenSutherlandClip(line); + + assertNull(clippedLine, "Line should be null because it's completely outside."); + } + + @Test + void testLinePartiallyInside() { + // Line partially inside the clipping window + Line line = new Line(new Point(5.0, 5.0), new Point(12.0, 12.0)); + Line expectedClippedLine = new Line(new Point(5.0, 5.0), new Point(10.0, 10.0)); // Clipped at (10, 10) + Line clippedLine = cs.cohenSutherlandClip(line); + + assertNotNull(clippedLine, "Line should not be null."); + assertEquals(expectedClippedLine, clippedLine, "Line should be clipped correctly."); + } + + @Test + void testLineOnBoundary() { + // Line exactly on the boundary of the clipping window + Line line = new Line(new Point(1.0, 5.0), new Point(10.0, 5.0)); + Line clippedLine = cs.cohenSutherlandClip(line); + + assertNotNull(clippedLine, "Line should not be null."); + assertEquals(line, clippedLine, "Line on the boundary should remain unchanged."); + } + + @Test + void testDiagonalLineThroughClippingWindow() { + // Diagonal line crossing from outside to outside through the window + Line line = new Line(new Point(0.0, 0.0), new Point(12.0, 12.0)); + Line expectedClippedLine = new Line(new Point(1.0, 1.0), new Point(10.0, 10.0)); // Clipped at both boundaries + Line clippedLine = cs.cohenSutherlandClip(line); + + assertNotNull(clippedLine, "Line should not be null."); + assertEquals(expectedClippedLine, clippedLine, "Diagonal line should be clipped correctly."); + } + + @Test + void testVerticalLineClipping() { + // Vertical line crossing the top and bottom of the clipping window + Line line = new Line(new Point(5.0, 0.0), new Point(5.0, 12.0)); + Line expectedClippedLine = new Line(new Point(5.0, 1.0), new Point(5.0, 10.0)); // Clipped at yMin and yMax + Line clippedLine = cs.cohenSutherlandClip(line); + + assertNotNull(clippedLine, "Line should not be null."); + assertEquals(expectedClippedLine, clippedLine, "Vertical line should be clipped correctly."); + } +} diff --git a/src/test/java/com/thealgorithms/lineclipping/LiangBarskyTest.java b/src/test/java/com/thealgorithms/lineclipping/LiangBarskyTest.java new file mode 100644 index 000000000000..1c48cd106572 --- /dev/null +++ b/src/test/java/com/thealgorithms/lineclipping/LiangBarskyTest.java @@ -0,0 +1,65 @@ +package com.thealgorithms.lineclipping; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import com.thealgorithms.lineclipping.utils.Line; +import com.thealgorithms.lineclipping.utils.Point; +import org.junit.jupiter.api.Test; + +/** + * @author shikarisohan + * @since 10/5/24 + */ +class LiangBarskyTest { + + LiangBarsky lb = new LiangBarsky(1.0, 1.0, 10.0, 10.0); + + @Test + void testLineCompletelyInside() { + Line line = new Line(new Point(2.0, 2.0), new Point(8.0, 8.0)); + Line clippedLine = lb.liangBarskyClip(line); + + assertNotNull(clippedLine, "Line should not be null."); + assertEquals(line, clippedLine, "Line inside the window should remain unchanged."); + } + + @Test + void testLineCompletelyOutside() { + Line line = new Line(new Point(12.0, 12.0), new Point(15.0, 18.0)); + Line clippedLine = lb.liangBarskyClip(line); + + assertNull(clippedLine, "Line should be null because it's completely outside."); + } + + @Test + void testLinePartiallyInside() { + Line line = new Line(new Point(5.0, 5.0), new Point(12.0, 12.0)); + Line expectedClippedLine = new Line(new Point(5.0, 5.0), new Point(10.0, 10.0)); // Clipped at (10, 10) + Line clippedLine = lb.liangBarskyClip(line); + + assertNotNull(clippedLine, "Line should not be null."); + assertEquals(expectedClippedLine, clippedLine, "Line should be clipped correctly."); + } + + @Test + void testDiagonalLineThroughClippingWindow() { + Line line = new Line(new Point(0.0, 0.0), new Point(12.0, 12.0)); + Line expectedClippedLine = new Line(new Point(1.0, 1.0), new Point(10.0, 10.0)); // Clipped at both boundaries + Line clippedLine = lb.liangBarskyClip(line); + + assertNotNull(clippedLine, "Line should not be null."); + assertEquals(expectedClippedLine, clippedLine, "Diagonal line should be clipped correctly."); + } + + @Test + void testVerticalLineClipping() { + Line line = new Line(new Point(5.0, 0.0), new Point(5.0, 12.0)); + Line expectedClippedLine = new Line(new Point(5.0, 1.0), new Point(5.0, 10.0)); // Clipped at yMin and yMax + Line clippedLine = lb.liangBarskyClip(line); + + assertNotNull(clippedLine, "Line should not be null."); + assertEquals(expectedClippedLine, clippedLine, "Vertical line should be clipped correctly."); + } +} diff --git a/src/test/java/com/thealgorithms/maths/ADTFractionTest.java b/src/test/java/com/thealgorithms/maths/ADTFractionTest.java new file mode 100644 index 000000000000..938c7d558841 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/ADTFractionTest.java @@ -0,0 +1,52 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class ADTFractionTest { + + private final ADTFraction fraction1 = new ADTFraction(3, 5); + private final ADTFraction fraction2 = new ADTFraction(7, 8); + + @Test + void testConstructorWithDenominatorEqualToZero() { + Exception exception = assertThrows(IllegalArgumentException.class, () -> new ADTFraction(1, 0)); + assertEquals("Denominator cannot be 0", exception.getMessage()); + } + + @Test + public void testPlus() { + assertEquals(new ADTFraction(59, 40), fraction1.plus(fraction2)); + } + + @Test + public void testTimes() { + assertEquals(new ADTFraction(12, 5), fraction1.times(4)); + assertEquals(new ADTFraction(21, 40), fraction1.times(fraction2)); + } + + @Test + public void testReciprocal() { + assertEquals(new ADTFraction(5, 3), fraction1.reciprocal()); + } + + @Test + public void testValue() { + assertEquals(0.6F, fraction1.value()); + } + + @Test + public void testEqualsAndHashCode() { + ADTFraction fraction3 = new ADTFraction(3, 5); + assertTrue(fraction1.equals(fraction3) && fraction3.equals(fraction1)); + assertEquals(fraction1.hashCode(), fraction3.hashCode()); + } + + @Test + public void testToString() { + assertEquals("3/5", fraction1.toString()); + } +} diff --git a/src/test/java/com/thealgorithms/maths/AbsoluteMaxTest.java b/src/test/java/com/thealgorithms/maths/AbsoluteMaxTest.java new file mode 100644 index 000000000000..33461fbbc088 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/AbsoluteMaxTest.java @@ -0,0 +1,30 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +public class AbsoluteMaxTest { + + @Test + void testGetMaxValue() { + assertEquals(16, AbsoluteMax.getMaxValue(-2, 0, 16)); + assertEquals(-22, AbsoluteMax.getMaxValue(-3, -10, -22)); + assertEquals(-888, AbsoluteMax.getMaxValue(-888)); + assertEquals(-1, AbsoluteMax.getMaxValue(-1, -1, -1, -1, -1)); + } + + @Test + void testGetMaxValueWithNoArguments() { + assertThrows(IllegalArgumentException.class, AbsoluteMax::getMaxValue); + } + + @Test + void testGetMaxValueWithSameAbsoluteValues() { + assertEquals(5, AbsoluteMax.getMaxValue(-5, 5)); + assertEquals(5, AbsoluteMax.getMaxValue(5, -5)); + assertEquals(12, AbsoluteMax.getMaxValue(-12, 9, 3, 12, 1)); + assertEquals(12, AbsoluteMax.getMaxValue(12, 9, 3, -12, 1)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/AbsoluteMinTest.java b/src/test/java/com/thealgorithms/maths/AbsoluteMinTest.java new file mode 100644 index 000000000000..dfca757fd877 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/AbsoluteMinTest.java @@ -0,0 +1,27 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +public class AbsoluteMinTest { + + @Test + void testGetMinValue() { + assertEquals(0, AbsoluteMin.getMinValue(4, 0, 16)); + assertEquals(-2, AbsoluteMin.getMinValue(3, -10, -2)); + } + + @Test + void testGetMinValueWithNoArguments() { + Exception exception = assertThrows(IllegalArgumentException.class, AbsoluteMin::getMinValue); + assertEquals("Numbers array cannot be empty", exception.getMessage()); + } + + @Test + void testGetMinValueWithSameAbsoluteValues() { + assertEquals(-5, AbsoluteMin.getMinValue(-5, 5)); + assertEquals(-5, AbsoluteMin.getMinValue(5, -5)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/AbsoluteValueTest.java b/src/test/java/com/thealgorithms/maths/AbsoluteValueTest.java new file mode 100644 index 000000000000..f87652253641 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/AbsoluteValueTest.java @@ -0,0 +1,15 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; + +public class AbsoluteValueTest { + + @Test + void testGetAbsValue() { + Stream.generate(() -> ThreadLocalRandom.current().nextInt()).limit(1000).forEach(number -> assertEquals(Math.abs(number), AbsoluteValue.getAbsValue(number))); + } +} diff --git a/src/test/java/com/thealgorithms/maths/AliquotSumTest.java b/src/test/java/com/thealgorithms/maths/AliquotSumTest.java new file mode 100644 index 000000000000..94631637fc4f --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/AliquotSumTest.java @@ -0,0 +1,20 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class AliquotSumTest { + + @Test + void testGetMaxValue() { + assertEquals(0, AliquotSum.getAliquotValue(1)); + assertEquals(6, AliquotSum.getAliquotValue(6)); + assertEquals(9, AliquotSum.getAliquotValue(15)); + assertEquals(1, AliquotSum.getAliquotValue(19)); + assertEquals(0, AliquotSum.getAliquotSum(1)); + assertEquals(6, AliquotSum.getAliquotSum(6)); + assertEquals(9, AliquotSum.getAliquotSum(15)); + assertEquals(1, AliquotSum.getAliquotSum(19)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/AmicableNumberTest.java b/src/test/java/com/thealgorithms/maths/AmicableNumberTest.java new file mode 100644 index 000000000000..14679f22636a --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/AmicableNumberTest.java @@ -0,0 +1,58 @@ +package com.thealgorithms.maths; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Set; +import org.apache.commons.lang3.tuple.Pair; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class AmicableNumberTest { + private static final String INVALID_RANGE_EXCEPTION_MESSAGE = "Given range of values is invalid!"; + private static final String INVALID_NUMBERS_EXCEPTION_MESSAGE = "Input numbers must be natural!"; + + @Test + public void testShouldThrowExceptionWhenInvalidRangeProvided() { + checkInvalidRange(0, 0); + checkInvalidRange(0, 1); + checkInvalidRange(1, 0); + checkInvalidRange(10, -1); + checkInvalidRange(-1, 10); + } + + @Test + public void testShouldThrowExceptionWhenInvalidNumbersProvided() { + checkInvalidNumbers(0, 0); + checkInvalidNumbers(0, 1); + checkInvalidNumbers(1, 0); + } + + @Test + public void testAmicableNumbers() { + assertThat(AmicableNumber.isAmicableNumber(220, 284)).isTrue(); + assertThat(AmicableNumber.isAmicableNumber(1184, 1210)).isTrue(); + assertThat(AmicableNumber.isAmicableNumber(2620, 2924)).isTrue(); + } + + @Test + public void testShouldFindAllAmicableNumbersInRange() { + // given + var expectedResult = Set.of(Pair.of(220, 284), Pair.of(1184, 1210), Pair.of(2620, 2924)); + + // when + Set<Pair<Integer, Integer>> result = AmicableNumber.findAllInRange(1, 3000); + + // then + Assertions.assertTrue(result.containsAll(expectedResult)); + } + + private static void checkInvalidRange(int from, int to) { + IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> AmicableNumber.findAllInRange(from, to)); + Assertions.assertEquals(exception.getMessage(), INVALID_RANGE_EXCEPTION_MESSAGE); + } + + private static void checkInvalidNumbers(int a, int b) { + IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> AmicableNumber.isAmicableNumber(a, b)); + Assertions.assertEquals(exception.getMessage(), INVALID_NUMBERS_EXCEPTION_MESSAGE); + } +} diff --git a/src/test/java/com/thealgorithms/maths/AreaTest.java b/src/test/java/com/thealgorithms/maths/AreaTest.java new file mode 100644 index 000000000000..b28afb85fbc3 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/AreaTest.java @@ -0,0 +1,103 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +/** + * @author Amarildo Aliaj + */ +class AreaTest { + + @Test + void testSurfaceAreaCube() { + assertEquals(6.0, Area.surfaceAreaCube(1)); + } + + @Test + void testSurfaceAreaSphere() { + assertEquals(12.566370614359172, Area.surfaceAreaSphere(1)); + } + + @Test + void testSurfaceAreaRectangle() { + assertEquals(200.0, Area.surfaceAreaRectangle(10, 20)); + } + + @Test + void testSurfaceAreaCylinder() { + assertEquals(18.84955592153876, Area.surfaceAreaCylinder(1, 2)); + } + + @Test + void testSurfaceAreaSquare() { + assertEquals(100.0, Area.surfaceAreaSquare(10)); + } + + @Test + void testSurfaceAreaTriangle() { + assertEquals(50.0, Area.surfaceAreaTriangle(10, 10)); + } + + @Test + void testSurfaceAreaParallelogram() { + assertEquals(200.0, Area.surfaceAreaParallelogram(10, 20)); + } + + @Test + void testSurfaceAreaTrapezium() { + assertEquals(450.0, Area.surfaceAreaTrapezium(10, 20, 30)); + } + + @Test + void testSurfaceAreaCircle() { + assertEquals(1256.6370614359173, Area.surfaceAreaCircle(20)); + } + + @Test + void surfaceAreaHemisphere() { + assertEquals(235.61944901923448, Area.surfaceAreaHemisphere(5)); + } + + @Test + void surfaceAreaCone() { + assertEquals(301.59289474462014, Area.surfaceAreaCone(6, 8)); + } + + @Test + void testAllIllegalInput() { + assertAll(() + -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaCube(0)), + () + -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaSphere(0)), + () + -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaRectangle(0, 10)), + () + -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaRectangle(10, 0)), + () + -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaCylinder(0, 1)), + () + -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaCylinder(1, 0)), + () + -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaSquare(0)), + () + -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaTriangle(0, 1)), + () + -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaTriangle(1, 0)), + () + -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaParallelogram(0, 1)), + () + -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaParallelogram(1, 0)), + () + -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaTrapezium(0, 1, 1)), + () + -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaTrapezium(1, 0, 1)), + () + -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaTrapezium(1, 1, 0)), + () + -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaCircle(0)), + () -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaHemisphere(0)), () -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaCone(1, 0)), () -> assertThrows(IllegalArgumentException.class, () -> Area.surfaceAreaCone(0, 1))); + } +} diff --git a/src/test/java/com/thealgorithms/maths/ArmstrongTest.java b/src/test/java/com/thealgorithms/maths/ArmstrongTest.java new file mode 100644 index 000000000000..e5d0d9eb3c43 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/ArmstrongTest.java @@ -0,0 +1,25 @@ +package com.thealgorithms.maths; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +/** + * @author satyabarghav + * @since 4/10/2023 + */ +class ArmstrongTest { + + @Test + void testIsArmstrong() { + Armstrong armstrong = new Armstrong(); + assertThat(armstrong.isArmstrong(0)).isTrue(); + assertThat(armstrong.isArmstrong(1)).isTrue(); + assertThat(armstrong.isArmstrong(153)).isTrue(); + assertThat(armstrong.isArmstrong(371)).isTrue(); + assertThat(armstrong.isArmstrong(1634)).isTrue(); + assertThat(armstrong.isArmstrong(200)).isFalse(); + assertThat(armstrong.isArmstrong(548834)).isTrue(); + assertThat(armstrong.isArmstrong(9474)).isTrue(); + } +} diff --git a/src/test/java/com/thealgorithms/maths/AutoCorrelationTest.java b/src/test/java/com/thealgorithms/maths/AutoCorrelationTest.java new file mode 100644 index 000000000000..cacfe5904faa --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/AutoCorrelationTest.java @@ -0,0 +1,37 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +/** + * Test class for AutoCorrelation class + * + * @author Athina-Frederiki Swinkels + * @version 2.0 + */ + +public class AutoCorrelationTest { + + @ParameterizedTest + @CsvSource({"1;2;1;1, 1;3;5;7;5;3;1", "1;2;3, 3;8;14;8;3", "1.5;2.3;3.1;4.2, 6.3;14.31;23.6;34.79;23.6;14.31;6.3"}) + + public void testAutoCorrelationParameterized(String input, String expected) { + double[] array = convertStringToArray(input); + double[] expectedResult = convertStringToArray(expected); + + double[] result = AutoCorrelation.autoCorrelation(array); + + assertArrayEquals(expectedResult, result, 0.0001); + } + + private double[] convertStringToArray(String input) { + String[] elements = input.split(";"); + double[] result = new double[elements.length]; + for (int i = 0; i < elements.length; i++) { + result[i] = Double.parseDouble(elements[i]); + } + return result; + } +} diff --git a/src/test/java/com/thealgorithms/maths/AutomorphicNumberTest.java b/src/test/java/com/thealgorithms/maths/AutomorphicNumberTest.java new file mode 100644 index 000000000000..0a366cd1adbe --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/AutomorphicNumberTest.java @@ -0,0 +1,27 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class AutomorphicNumberTest { + + @Test + void testAutomorphicNumber() { + int[] trueTestCases = {0, 1, 25, 625, 12890625}; + int[] falseTestCases = {-5, 2, 26, 1234}; + for (Integer n : trueTestCases) { + assertTrue(AutomorphicNumber.isAutomorphic(n)); + assertTrue(AutomorphicNumber.isAutomorphic2(n)); + assertTrue(AutomorphicNumber.isAutomorphic3(String.valueOf(n))); + } + for (Integer n : falseTestCases) { + assertFalse(AutomorphicNumber.isAutomorphic(n)); + assertFalse(AutomorphicNumber.isAutomorphic2(n)); + assertFalse(AutomorphicNumber.isAutomorphic3(String.valueOf(n))); + } + assertTrue(AutomorphicNumber.isAutomorphic3("59918212890625")); // Special case for BigInteger + assertFalse(AutomorphicNumber.isAutomorphic3("12345678912345")); // Special case for BigInteger + } +} diff --git a/src/test/java/com/thealgorithms/maths/AverageTest.java b/src/test/java/com/thealgorithms/maths/AverageTest.java new file mode 100644 index 000000000000..c5c751938f5d --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/AverageTest.java @@ -0,0 +1,33 @@ +package com.thealgorithms.maths; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class AverageTest { + + private static final double SMALL_VALUE = 0.00001d; + + @Test + public void testAverageDouble12() { + double[] numbers = {3d, 6d, 9d, 12d, 15d, 18d, 21d}; + Assertions.assertEquals(12d, Average.average(numbers), SMALL_VALUE); + } + + @Test + public void testAverageDouble20() { + double[] numbers = {5d, 10d, 15d, 20d, 25d, 30d, 35d}; + Assertions.assertEquals(20d, Average.average(numbers), SMALL_VALUE); + } + + @Test + public void testAverageDouble() { + double[] numbers = {1d, 2d, 3d, 4d, 5d, 6d, 7d, 8d}; + Assertions.assertEquals(4.5d, Average.average(numbers), SMALL_VALUE); + } + + @Test + public void testAverageInt() { + int[] numbers = {2, 4, 10}; + Assertions.assertEquals(5, Average.average(numbers)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/BinaryPowTest.java b/src/test/java/com/thealgorithms/maths/BinaryPowTest.java new file mode 100644 index 000000000000..f9b019e8fad4 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/BinaryPowTest.java @@ -0,0 +1,16 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class BinaryPowTest { + + @Test + void testBinPow() { + assertEquals(4, BinaryPow.binPow(2, 2)); + assertEquals(256, BinaryPow.binPow(4, 4)); + assertEquals(729, BinaryPow.binPow(9, 3)); + assertEquals(262144, BinaryPow.binPow(8, 6)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/BinomialCoefficientTest.java b/src/test/java/com/thealgorithms/maths/BinomialCoefficientTest.java new file mode 100644 index 000000000000..58cfcbad57fb --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/BinomialCoefficientTest.java @@ -0,0 +1,16 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class BinomialCoefficientTest { + + @Test + void testBinomialCoefficient() { + assertEquals(190, BinomialCoefficient.binomialCoefficient(20, 2)); + assertEquals(792, BinomialCoefficient.binomialCoefficient(12, 5)); + assertEquals(84, BinomialCoefficient.binomialCoefficient(9, 3)); + assertEquals(1, BinomialCoefficient.binomialCoefficient(17, 17)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/CatalanNumbersTest.java b/src/test/java/com/thealgorithms/maths/CatalanNumbersTest.java new file mode 100644 index 000000000000..2248ffd93732 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/CatalanNumbersTest.java @@ -0,0 +1,43 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * Test class for CatalanNumbers + */ +class CatalanNumbersTest { + + /** + * Provides test data for the parameterized Catalan number test. + * Each array contains two elements: + * [input number, expected Catalan number for that input] + */ + static Stream<Object[]> catalanNumbersProvider() { + return Stream.of(new Object[] {0, 1}, new Object[] {1, 1}, new Object[] {2, 2}, new Object[] {3, 5}, new Object[] {4, 14}, new Object[] {5, 42}, new Object[] {6, 132}, new Object[] {7, 429}, new Object[] {8, 1430}, new Object[] {9, 4862}, new Object[] {10, 16796}); + } + + /** + * Parameterized test for checking the correctness of Catalan numbers. + * Uses the data from the provider method 'catalanNumbersProvider'. + */ + @ParameterizedTest + @MethodSource("catalanNumbersProvider") + void testCatalanNumbers(int input, int expected) { + assertEquals(expected, CatalanNumbers.catalan(input), () -> String.format("Catalan number for input %d should be %d", input, expected)); + } + + /** + * Test for invalid inputs which should throw an IllegalArgumentException. + */ + @Test + void testIllegalInput() { + assertThrows(IllegalArgumentException.class, () -> CatalanNumbers.catalan(-1)); + assertThrows(IllegalArgumentException.class, () -> CatalanNumbers.catalan(-5)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/CeilTest.java b/src/test/java/com/thealgorithms/maths/CeilTest.java new file mode 100644 index 000000000000..5596760a8c40 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/CeilTest.java @@ -0,0 +1,17 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class CeilTest { + + @Test + void testCeil() { + assertEquals(8, Ceil.ceil(7.057)); + assertEquals(8, Ceil.ceil(7.004)); + assertEquals(-13, Ceil.ceil(-13.004)); + assertEquals(1, Ceil.ceil(.98)); + assertEquals(-11, Ceil.ceil(-11.357)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/ChineseRemainderTheoremTest.java b/src/test/java/com/thealgorithms/maths/ChineseRemainderTheoremTest.java new file mode 100644 index 000000000000..7c153ae4cdda --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/ChineseRemainderTheoremTest.java @@ -0,0 +1,55 @@ +package com.thealgorithms.maths; + +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class ChineseRemainderTheoremTest { + @Test + public void testCRTSimpleCase() { + List<Integer> remainders = Arrays.asList(2, 3, 2); + List<Integer> moduli = Arrays.asList(3, 5, 7); + int expected = 23; + int result = ChineseRemainderTheorem.solveCRT(remainders, moduli); + assertEquals(expected, result); + } + + @Test + public void testCRTLargeModuli() { + List<Integer> remainders = Arrays.asList(1, 2, 3); + List<Integer> moduli = Arrays.asList(5, 7, 9); + int expected = 156; + int result = ChineseRemainderTheorem.solveCRT(remainders, moduli); + assertEquals(expected, result); + } + + @Test + public void testCRTWithSingleCongruence() { + List<Integer> remainders = singletonList(4); + List<Integer> moduli = singletonList(7); + int expected = 4; + int result = ChineseRemainderTheorem.solveCRT(remainders, moduli); + assertEquals(expected, result); + } + + @Test + public void testCRTWithMultipleSolutions() { + List<Integer> remainders = Arrays.asList(0, 3); + List<Integer> moduli = Arrays.asList(4, 5); + int expected = 8; + int result = ChineseRemainderTheorem.solveCRT(remainders, moduli); + assertEquals(expected, result); + } + + @Test + public void testCRTLargeNumbers() { + List<Integer> remainders = Arrays.asList(0, 4, 6); + List<Integer> moduli = Arrays.asList(11, 13, 17); + int expected = 550; + int result = ChineseRemainderTheorem.solveCRT(remainders, moduli); + assertEquals(expected, result); + } +} diff --git a/src/test/java/com/thealgorithms/maths/CollatzConjectureTest.java b/src/test/java/com/thealgorithms/maths/CollatzConjectureTest.java new file mode 100644 index 000000000000..4e88d1e91bac --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/CollatzConjectureTest.java @@ -0,0 +1,41 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertIterableEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.List; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +class CollatzConjectureTest { + + static CollatzConjecture cConjecture; + + @BeforeAll + static void setUp() { + cConjecture = new CollatzConjecture(); + } + + @Test + void nextNumberFromEvenNumber() { + assertEquals(25, cConjecture.nextNumber(50)); + } + + @Test + void nextNumberFromOddNumber() { + assertEquals(154, cConjecture.nextNumber(51)); + } + + @Test + void collatzConjecture() { + final List<Integer> expected = List.of(35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1); + assertIterableEquals(expected, cConjecture.collatzConjecture(35)); + } + + @Test + void sequenceOfNotNaturalFirstNumber() { + assertThrows(IllegalArgumentException.class, () -> cConjecture.collatzConjecture(0)); + assertThrows(IllegalArgumentException.class, () -> cConjecture.collatzConjecture(-1)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/CombinationsTest.java b/src/test/java/com/thealgorithms/maths/CombinationsTest.java new file mode 100644 index 000000000000..f260b1e11494 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/CombinationsTest.java @@ -0,0 +1,26 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class CombinationsTest { + + @Test + void testCombination() { + assertEquals(1, Combinations.combinations(1, 1)); + assertEquals(252, Combinations.combinations(10, 5)); + assertEquals(20, Combinations.combinations(6, 3)); + assertEquals(15504, Combinations.combinations(20, 5)); + } + + @Test + void testCombinationOptimised() { + assertEquals(100, Combinations.combinationsOptimized(100, 1)); + assertEquals(1, Combinations.combinationsOptimized(1, 1)); + assertEquals(252, Combinations.combinationsOptimized(10, 5)); + assertEquals(20, Combinations.combinationsOptimized(6, 3)); + assertEquals(15504, Combinations.combinationsOptimized(20, 5)); + assertEquals(2535650040L, Combinations.combinationsOptimized(200, 5)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/ConvolutionFFTTest.java b/src/test/java/com/thealgorithms/maths/ConvolutionFFTTest.java new file mode 100644 index 000000000000..4d627f939d42 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/ConvolutionFFTTest.java @@ -0,0 +1,55 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class ConvolutionFFTTest { + + /** + * Helper method to create a complex signal from an array of doubles. + */ + private ArrayList<FFT.Complex> createComplexSignal(double[] values) { + ArrayList<FFT.Complex> signal = new ArrayList<>(); + for (double value : values) { + signal.add(new FFT.Complex(value, 0)); + } + return signal; + } + + /** + * Helper method to compare two complex signals for equality within a small margin of error. + */ + private void assertComplexArrayEquals(List<FFT.Complex> expected, List<FFT.Complex> result, double delta) { + assertEquals(expected.size(), result.size(), "Signal lengths are not equal."); + for (int i = 0; i < expected.size(); i++) { + FFT.Complex expectedValue = expected.get(i); + FFT.Complex resultValue = result.get(i); + assertEquals(expectedValue.real(), resultValue.real(), delta, "Real part mismatch at index " + i); + assertEquals(expectedValue.imaginary(), resultValue.imaginary(), delta, "Imaginary part mismatch at index " + i); + } + } + + @ParameterizedTest(name = "Test case {index}: {3}") + @MethodSource("provideTestCases") + public void testConvolutionFFT(double[] a, double[] b, double[] expectedOutput, String testDescription) { + ArrayList<FFT.Complex> signalA = createComplexSignal(a); + ArrayList<FFT.Complex> signalB = createComplexSignal(b); + + ArrayList<FFT.Complex> expected = createComplexSignal(expectedOutput); + ArrayList<FFT.Complex> result = ConvolutionFFT.convolutionFFT(signalA, signalB); + + assertComplexArrayEquals(expected, result, 1e-9); // Allow small margin of error + } + + private static Stream<Arguments> provideTestCases() { + return Stream.of(Arguments.of(new double[] {1, 2, 3}, new double[] {4, 5, 6}, new double[] {4, 13, 28, 27, 18}, "Basic test"), Arguments.of(new double[] {0, 0, 0}, new double[] {1, 2, 3}, new double[] {0, 0, 0, 0, 0}, "Test with zero elements"), + Arguments.of(new double[] {1, 2}, new double[] {3, 4, 5}, new double[] {3, 10, 13, 10}, "Test with different sizes"), Arguments.of(new double[] {5}, new double[] {2}, new double[] {10}, "Test with single element"), + Arguments.of(new double[] {1, -2, 3}, new double[] {-1, 2, -3}, new double[] {-1, 4, -10, 12, -9}, "Test with negative values")); + } +} diff --git a/src/test/java/com/thealgorithms/maths/ConvolutionTest.java b/src/test/java/com/thealgorithms/maths/ConvolutionTest.java new file mode 100644 index 000000000000..d57b3b3ca4e5 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/ConvolutionTest.java @@ -0,0 +1,27 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class ConvolutionTest { + + record ConvolutionTestCase(String description, double[] signalA, double[] signalB, double[] expected) { + } + + @ParameterizedTest(name = "{0}") + @MethodSource("provideTestCases") + void testConvolution(ConvolutionTestCase testCase) { + double[] result = Convolution.convolution(testCase.signalA, testCase.signalB); + assertArrayEquals(testCase.expected, result, 1e-9, testCase.description); + } + + private static Stream<ConvolutionTestCase> provideTestCases() { + return Stream.of(new ConvolutionTestCase("Basic convolution", new double[] {1, 2, 3}, new double[] {4, 5, 6}, new double[] {4, 13, 28, 27, 18}), new ConvolutionTestCase("Convolution with zero elements", new double[] {0, 0, 0}, new double[] {1, 2, 3}, new double[] {0, 0, 0, 0, 0}), + new ConvolutionTestCase("Convolution with single element", new double[] {2}, new double[] {3}, new double[] {6}), new ConvolutionTestCase("Convolution with different sizes", new double[] {1, 2}, new double[] {3, 4, 5}, new double[] {3, 10, 13, 10}), + new ConvolutionTestCase("Convolution with negative values", new double[] {1, -2, 3}, new double[] {-1, 2, -3}, new double[] {-1, 4, -10, 12, -9}), + new ConvolutionTestCase("Convolution with large numbers", new double[] {1e6, 2e6}, new double[] {3e6, 4e6}, new double[] {3e12, 1e13, 8e12})); + } +} diff --git a/src/test/java/com/thealgorithms/maths/CrossCorrelationTest.java b/src/test/java/com/thealgorithms/maths/CrossCorrelationTest.java new file mode 100644 index 000000000000..a7e4f14fb3af --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/CrossCorrelationTest.java @@ -0,0 +1,37 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +/** + * Test class for CrossCorrelation class + * + * @author Athina-Frederiki Swinkels + * @version 2.0 + */ +public class CrossCorrelationTest { + + @ParameterizedTest + @CsvSource({"1;2;1;1, 1;1;2;1, 1;4;6;6;5;2;1", "1;2;3, 1;2;3;4;5, 5;14;26;20;14;8;3", "1;2;3;4;5, 1;2;3, 3;8;14;20;26;14;5", "1.5;2.3;3.1;4.2, 1.1;2.2;3.3, 4.95;10.89;16.94;23.21;12.65;4.62"}) + + public void testCrossCorrelationParameterized(String input1, String input2, String expected) { + double[] array1 = convertStringToArray(input1); + double[] array2 = convertStringToArray(input2); + double[] expectedResult = convertStringToArray(expected); + + double[] result = CrossCorrelation.crossCorrelation(array1, array2); + + assertArrayEquals(expectedResult, result, 0.0001); + } + + private double[] convertStringToArray(String input) { + String[] elements = input.split(";"); + double[] result = new double[elements.length]; + for (int i = 0; i < elements.length; i++) { + result[i] = Double.parseDouble(elements[i]); + } + return result; + } +} diff --git a/src/test/java/com/thealgorithms/maths/DeterminantOfMatrixTest.java b/src/test/java/com/thealgorithms/maths/DeterminantOfMatrixTest.java new file mode 100644 index 000000000000..dd1c3ac4e605 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/DeterminantOfMatrixTest.java @@ -0,0 +1,50 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class DeterminantOfMatrixTest { + + @Test + public void testDeterminant2x2Matrix() { + int[][] matrix = {{1, 2}, {3, 4}}; + int expected = -2; + assertEquals(expected, DeterminantOfMatrix.determinant(matrix, 2)); + } + + @Test + public void testDeterminant3x3Matrix() { + int[][] matrix = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; + int expected = 0; + assertEquals(expected, DeterminantOfMatrix.determinant(matrix, 3)); + } + + @Test + public void testDeterminant3x3MatrixNonZero() { + int[][] matrix = {{1, 2, 3}, {0, 1, 4}, {5, 6, 0}}; + int expected = 1; + assertEquals(expected, DeterminantOfMatrix.determinant(matrix, 3)); + } + + @Test + public void testDeterminant1x1Matrix() { + int[][] matrix = {{7}}; + int expected = 7; + assertEquals(expected, DeterminantOfMatrix.determinant(matrix, 1)); + } + + @Test + public void testDeterminant4x4Matrix() { + int[][] matrix = {{1, 0, 0, 1}, {0, 1, 0, 0}, {0, 0, 1, 0}, {1, 0, 0, 1}}; + int expected = 0; + assertEquals(expected, DeterminantOfMatrix.determinant(matrix, 4)); + } + + @Test + public void testDeterminant4x4MatrixZero() { + int[][] matrix = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; + int expected = 0; + assertEquals(expected, DeterminantOfMatrix.determinant(matrix, 4)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/DigitalRootTest.java b/src/test/java/com/thealgorithms/maths/DigitalRootTest.java new file mode 100644 index 000000000000..eeeeb5431507 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/DigitalRootTest.java @@ -0,0 +1,18 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class DigitalRootTest { + + @Test + void testDigitalroot() { + assertEquals(4, DigitalRoot.digitalRoot(4)); + assertEquals(9, DigitalRoot.digitalRoot(9)); + assertEquals(4, DigitalRoot.digitalRoot(49)); + assertEquals(6, DigitalRoot.digitalRoot(78)); + assertEquals(4, DigitalRoot.digitalRoot(1228)); + assertEquals(5, DigitalRoot.digitalRoot(71348)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/DistanceFormulaTest.java b/src/test/java/com/thealgorithms/maths/DistanceFormulaTest.java new file mode 100644 index 000000000000..3a14b80dd4f9 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/DistanceFormulaTest.java @@ -0,0 +1,86 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class DistanceFormulaTest { + + @Test + void euclideanTest1() { + Assertions.assertEquals(DistanceFormula.euclideanDistance(1, 1, 2, 2), 1.4142135623730951); + } + + @Test + void euclideanTest2() { + Assertions.assertEquals(DistanceFormula.euclideanDistance(1, 3, 8, 0), 7.0710678118654755); + } + + @Test + void euclideanTest3() { + Assertions.assertEquals(DistanceFormula.euclideanDistance(2.4, 9.1, 55.1, 100), 110.91911467371168); + } + + @Test + void euclideanTest4() { + Assertions.assertEquals(DistanceFormula.euclideanDistance(1000, 13, 20000, 84), 19022.067605809836); + } + + @Test + public void manhattantest1() { + assertEquals(DistanceFormula.manhattanDistance(1, 2, 3, 4), 4); + } + + @Test + public void manhattantest2() { + assertEquals(DistanceFormula.manhattanDistance(6.5, 8.4, 20.1, 13.6), 18.8); + } + + @Test + public void manhattanTest3() { + assertEquals(DistanceFormula.manhattanDistance(10.112, 50, 8, 25.67), 26.442); + } + + @Test + public void hammingTest1() { + int[] array1 = {1, 1, 1, 1}; + int[] array2 = {0, 0, 0, 0}; + assertEquals(DistanceFormula.hammingDistance(array1, array2), 4); + } + + @Test + public void hammingTest2() { + int[] array1 = {1, 1, 1, 1}; + int[] array2 = {1, 1, 1, 1}; + assertEquals(DistanceFormula.hammingDistance(array1, array2), 0); + } + + @Test + public void hammingTest3() { + int[] array1 = {1, 0, 0, 1, 1, 0, 1, 1, 0}; + int[] array2 = {0, 1, 0, 0, 1, 1, 1, 0, 0}; + assertEquals(DistanceFormula.hammingDistance(array1, array2), 5); + } + + @Test + public void minkowskiTest1() { + double[] array1 = {1, 3, 8, 5}; + double[] array2 = {4, 2, 6, 9}; + assertEquals(DistanceFormula.minkowskiDistance(array1, array2, 1), 10); + } + + @Test + public void minkowskiTest2() { + double[] array1 = {1, 3, 8, 5}; + double[] array2 = {4, 2, 6, 9}; + assertEquals(DistanceFormula.minkowskiDistance(array1, array2, 2), 5.477225575051661); + } + + @Test + public void minkowskiTest3() { + double[] array1 = {1, 3, 8, 5}; + double[] array2 = {4, 2, 6, 9}; + assertEquals(DistanceFormula.minkowskiDistance(array1, array2, 3), 4.641588833612778); + } +} diff --git a/src/test/java/com/thealgorithms/maths/DudeneyNumberTest.java b/src/test/java/com/thealgorithms/maths/DudeneyNumberTest.java new file mode 100644 index 000000000000..cd93edfae61d --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/DudeneyNumberTest.java @@ -0,0 +1,28 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class DudeneyNumberTest { + @ParameterizedTest + @CsvSource({"1", "512", "4913", "5832", "17576", "19683"}) + void positiveDudeneyBase10Power3(final int n) { + assertTrue(DudeneyNumber.isDudeney(n)); + } + + @ParameterizedTest + @CsvSource({"2", "19", "21", "125", "27", "343", "729", "19682", "19684"}) + void negativeDudeneyBase10Power3(final int n) { + assertFalse(DudeneyNumber.isDudeney(n)); + } + + @ParameterizedTest + @CsvSource({"0", "-1"}) + void throwsInputLessThanOne(final int n) { + assertThrows(IllegalArgumentException.class, () -> DudeneyNumber.isDudeney(n)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/EulerMethodTest.java b/src/test/java/com/thealgorithms/maths/EulerMethodTest.java new file mode 100644 index 000000000000..5ae5ac70b1df --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/EulerMethodTest.java @@ -0,0 +1,79 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.ArrayList; +import java.util.function.BiFunction; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class EulerMethodTest { + + private static class EulerFullTestCase { + double[] params; + BiFunction<Double, Double, Double> equation; + int expectedSize; + double[] expectedFirstPoint; + double[] expectedLastPoint; + + EulerFullTestCase(double[] params, BiFunction<Double, Double, Double> equation, int expectedSize, double[] expectedFirstPoint, double[] expectedLastPoint) { + this.params = params; + this.equation = equation; + this.expectedSize = expectedSize; + this.expectedFirstPoint = expectedFirstPoint; + this.expectedLastPoint = expectedLastPoint; + } + } + + @ParameterizedTest + @MethodSource("eulerStepTestCases") + void testEulerStep(double x, double h, double y, BiFunction<Double, Double, Double> equation, double expected) { + double result = EulerMethod.eulerStep(x, h, y, equation); + assertEquals(expected, result, 1e-9, "Euler step failed for the given equation."); + } + + static Stream<Arguments> eulerStepTestCases() { + return Stream.of(Arguments.of(0.0, 0.1, 1.0, (BiFunction<Double, Double, Double>) ((x, y) -> x + y), 1.1)); + } + + @ParameterizedTest + @MethodSource("eulerStepInvalidCases") + void testEulerStepInvalidInput(double x, double h, double y, BiFunction<Double, Double, Double> equation, Class<? extends Exception> expectedExceptionClass) { + assertThrows(expectedExceptionClass, () -> EulerMethod.eulerStep(x, h, y, equation)); + } + + static Stream<Arguments> eulerStepInvalidCases() { + BiFunction<Double, Double, Double> dummyEquation = (x, y) -> x + y; + return Stream.of(Arguments.of(0.0, -0.1, 1.0, dummyEquation, IllegalArgumentException.class), Arguments.of(0.0, 0.0, 1.0, dummyEquation, IllegalArgumentException.class)); + } + + @ParameterizedTest + @MethodSource("eulerFullTestCases") + void testEulerFull(EulerFullTestCase testCase) { + ArrayList<double[]> result = EulerMethod.eulerFull(testCase.params[0], testCase.params[1], testCase.params[2], testCase.params[3], testCase.equation); + assertEquals(testCase.expectedSize, result.size(), "Incorrect number of points in the result."); + assertArrayEquals(testCase.expectedFirstPoint, result.get(0), 1e-9, "Incorrect first point."); + assertArrayEquals(testCase.expectedLastPoint, result.get(result.size() - 1), 1e-9, "Incorrect last point."); + } + + static Stream<Arguments> eulerFullTestCases() { + return Stream.of(Arguments.of(new EulerFullTestCase(new double[] {0.0, 1.0, 0.5, 0.0}, (x, y) -> x, 3, new double[] {0.0, 0.0}, new double[] {1.0, 0.25})), + Arguments.of(new EulerFullTestCase(new double[] {0.0, 1.0, 0.1, 1.0}, (x, y) -> y, 12, new double[] {0.0, 1.0}, new double[] {1.0999999999999999, 2.8531167061100002})), + Arguments.of(new EulerFullTestCase(new double[] {0.0, 0.1, 0.1, 1.0}, (x, y) -> x + y, 2, new double[] {0.0, 1.0}, new double[] {0.1, 1.1}))); + } + + @ParameterizedTest + @MethodSource("eulerFullInvalidCases") + void testEulerFullInvalidInput(double xStart, double xEnd, double stepSize, double yInitial, BiFunction<Double, Double, Double> equation, Class<? extends Exception> expectedExceptionClass) { + assertThrows(expectedExceptionClass, () -> EulerMethod.eulerFull(xStart, xEnd, stepSize, yInitial, equation)); + } + + static Stream<Arguments> eulerFullInvalidCases() { + BiFunction<Double, Double, Double> dummyEquation = (x, y) -> x + y; + return Stream.of(Arguments.of(1.0, 0.0, 0.1, 1.0, dummyEquation, IllegalArgumentException.class), Arguments.of(0.0, 1.0, 0.0, 1.0, dummyEquation, IllegalArgumentException.class), Arguments.of(0.0, 1.0, -0.1, 1.0, dummyEquation, IllegalArgumentException.class)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/EulersFunctionTest.java b/src/test/java/com/thealgorithms/maths/EulersFunctionTest.java new file mode 100644 index 000000000000..9048a711d0d6 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/EulersFunctionTest.java @@ -0,0 +1,33 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class EulersFunctionTest { + + @ParameterizedTest + @MethodSource("provideNumbersForGetEuler") + void testGetEuler(int input, int expected) { + assertEquals(expected, EulersFunction.getEuler(input)); + } + + @ParameterizedTest + @MethodSource("provideInvalidNumbersForGetEuler") + void testGetEulerThrowsExceptionForNonPositiveInput(int input) { + assertThrows(IllegalArgumentException.class, () -> EulersFunction.getEuler(input)); + } + + private static Stream<Arguments> provideNumbersForGetEuler() { + return Stream.of(Arguments.of(1, 1), Arguments.of(2, 1), Arguments.of(3, 2), Arguments.of(4, 2), Arguments.of(5, 4), Arguments.of(6, 2), Arguments.of(10, 4), Arguments.of(21, 12), Arguments.of(69, 44), Arguments.of(47, 46), Arguments.of(46, 22), Arguments.of(55, 40), Arguments.of(34, 16), + Arguments.of(20, 8), Arguments.of(1024, 512)); + } + + private static Stream<Arguments> provideInvalidNumbersForGetEuler() { + return Stream.of(Arguments.of(0), Arguments.of(-1), Arguments.of(-10)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/FFTTest.java b/src/test/java/com/thealgorithms/maths/FFTTest.java new file mode 100644 index 000000000000..8d78fb0f2a16 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/FFTTest.java @@ -0,0 +1,138 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import java.util.ArrayList; +import org.junit.jupiter.api.Test; + +class FFTTest { + + // Testing the simple function getReal + @Test + void getRealtest() { + FFT.Complex complex = new FFT.Complex(1.0, 1.0); + assertEquals(1.0, complex.getReal()); + } + + // Testing the simple function getImaginary + @Test + void getImaginaryTest() { + FFT.Complex complex = new FFT.Complex(); + assertEquals(0.0, complex.getImaginary()); + } + + // Testing the function add, assertEqual test + @Test + void addTest() { + FFT.Complex complex1 = new FFT.Complex(1.0, 1.0); + FFT.Complex complex2 = new FFT.Complex(2.0, 2.0); + double add = complex1.add(complex2).getReal(); + assertEquals(3.0, add); + } + + // Testing the function add, assertNotEqual test + @Test + void addFalseTest() { + FFT.Complex complex1 = new FFT.Complex(1.0, 1.0); + FFT.Complex complex2 = new FFT.Complex(2.0, 2.0); + double add = complex1.add(complex2).getReal(); + assertNotEquals(2.0, add); + } + + // Testing the function subtract, assertEqual test + @Test + void subtractTest() { + FFT.Complex complex1 = new FFT.Complex(2.0, 2.0); + FFT.Complex complex2 = new FFT.Complex(1.0, 1.0); + double sub = complex1.subtract(complex2).getReal(); + assertEquals(1.0, sub); + } + + // Testing the function multiply complex, assertEqual test + @Test + void multiplyWithComplexTest() { + FFT.Complex complex1 = new FFT.Complex(2.0, 2.0); + FFT.Complex complex2 = new FFT.Complex(1.0, 1.0); + double multiReal = complex1.multiply(complex2).getReal(); + double multiImg = complex1.multiply(complex2).getImaginary(); + assertEquals(0.0, multiReal); + assertEquals(4.0, multiImg); + } + + // Testing the function multiply scalar, assertEqual test + @Test + void multiplyWithScalarTest() { + FFT.Complex complex1 = new FFT.Complex(2.0, 2.0); + + double multiReal = complex1.multiply(2).getReal(); + double multiImg = complex1.multiply(3).getImaginary(); + assertEquals(4.0, multiReal); + assertEquals(6.0, multiImg); + } + + // Testing the function conjugate, assertEqual test + @Test + void conjugateTest() { + FFT.Complex complex1 = new FFT.Complex(2.0, 2.0); + double conReal = complex1.conjugate().getReal(); + double conImg = complex1.conjugate().getImaginary(); + assertEquals(2.0, conReal); + assertEquals(-2.0, conImg); + } + + // Testing the function abs, assertEqual test + @Test + void abs() { + FFT.Complex complex1 = new FFT.Complex(2.0, 3.0); + double abs = complex1.abs(); + assertEquals(Math.sqrt(13), abs); + } + + // Testing the function divide complex, assertEqual test. + @Test + void divideWithComplexTest() { + FFT.Complex complex1 = new FFT.Complex(2.0, 2.0); + FFT.Complex complex2 = new FFT.Complex(1.0, 2.0); + double divReal = complex1.divide(complex2).getReal(); + double divImg = complex1.divide(complex2).getImaginary(); + assertEquals(1.2, divReal); + assertEquals(-0.4, divImg); + } + + // Testing the function divide scalar, assertEqual test. + @Test + void divideWithScalarTest() { + FFT.Complex complex1 = new FFT.Complex(2.0, 2.0); + double divReal = complex1.divide(2).getReal(); + double divImg = complex1.divide(2).getImaginary(); + assertEquals(1, divReal); + assertEquals(1, divImg); + } + + // Testing the function fft, assertEqual test. + // https://scistatcalc.blogspot.com/2013/12/fft-calculator.html used this link to + // ensure the result + @Test + void fft() { + ArrayList<FFT.Complex> arr = new ArrayList<FFT.Complex>(); + FFT.Complex complex1 = new FFT.Complex(2.0, 2.0); + FFT.Complex complex2 = new FFT.Complex(1.0, 3.0); + FFT.Complex complex3 = new FFT.Complex(3.0, 1.0); + FFT.Complex complex4 = new FFT.Complex(2.0, 2.0); + + arr.add(complex1); + arr.add(complex2); + arr.add(complex3); + arr.add(complex4); + arr = FFT.fft(arr, false); + double realV1 = arr.get(0).getReal(); + double realV2 = arr.get(2).getReal(); + double imgV1 = arr.get(0).getImaginary(); + double imgV2 = arr.get(2).getImaginary(); + assertEquals(8.0, realV1); + assertEquals(2.0, realV2); + assertEquals(8.0, imgV1); + assertEquals(-2.0, imgV2); + } +} diff --git a/src/test/java/com/thealgorithms/maths/FactorialRecursionTest.java b/src/test/java/com/thealgorithms/maths/FactorialRecursionTest.java new file mode 100644 index 000000000000..db18b46356b4 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/FactorialRecursionTest.java @@ -0,0 +1,27 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class FactorialRecursionTest { + @ParameterizedTest + @MethodSource("inputStream") + void testFactorialRecursion(long expected, int number) { + assertEquals(expected, FactorialRecursion.factorial(number)); + } + + private static Stream<Arguments> inputStream() { + return Stream.of(Arguments.of(1, 0), Arguments.of(1, 1), Arguments.of(2, 2), Arguments.of(6, 3), Arguments.of(120, 5)); + } + + @Test + void testThrowsForNegativeInput() { + assertThrows(IllegalArgumentException.class, () -> FactorialRecursion.factorial(-1)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/FactorialTest.java b/src/test/java/com/thealgorithms/maths/FactorialTest.java new file mode 100644 index 000000000000..b38dc45589ee --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/FactorialTest.java @@ -0,0 +1,24 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +public class FactorialTest { + private static final String EXCEPTION_MESSAGE = "Input number cannot be negative"; + + @Test + public void testWhenInvalidInoutProvidedShouldThrowException() { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> Factorial.factorial(-1)); + assertEquals(exception.getMessage(), EXCEPTION_MESSAGE); + } + + @Test + public void testCorrectFactorialCalculation() { + assertEquals(1, Factorial.factorial(0)); + assertEquals(1, Factorial.factorial(1)); + assertEquals(120, Factorial.factorial(5)); + assertEquals(3628800, Factorial.factorial(10)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/FastExponentiationTest.java b/src/test/java/com/thealgorithms/maths/FastExponentiationTest.java new file mode 100644 index 000000000000..f117f90233e3 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/FastExponentiationTest.java @@ -0,0 +1,67 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the {@link FastExponentiation} class. + * + * <p>This class contains various test cases to verify the correctness of the fastExponentiation method. + * It covers basic functionality, edge cases, and exceptional cases. + */ +class FastExponentiationTest { + + /** + * Tests fast exponentiation with small numbers. + */ + @Test + void testSmallNumbers() { + assertEquals(1024, FastExponentiation.fastExponentiation(2, 10, 10000), "2^10 mod 10000 should be 1024"); + assertEquals(81, FastExponentiation.fastExponentiation(3, 4, 1000), "3^4 mod 1000 should be 81"); + } + + /** + * Tests the behavior of the fast exponentiation method when using a modulus. + */ + @Test + void testWithModulo() { + assertEquals(24, FastExponentiation.fastExponentiation(2, 10, 1000), "2^10 mod 1000 should be 24"); + assertEquals(0, FastExponentiation.fastExponentiation(10, 5, 10), "10^5 mod 10 should be 0"); + } + + /** + * Tests the edge cases where base or exponent is 0. + */ + @Test + void testBaseCases() { + assertEquals(1, FastExponentiation.fastExponentiation(2, 0, 1000), "Any number raised to the power 0 mod anything should be 1"); + assertEquals(0, FastExponentiation.fastExponentiation(0, 10, 1000), "0 raised to any power should be 0"); + assertEquals(1, FastExponentiation.fastExponentiation(0, 0, 1000), "0^0 is considered 0 in modular arithmetic."); + } + + /** + * Tests fast exponentiation with a negative base to ensure correctness under modular arithmetic. + */ + @Test + void testNegativeBase() { + assertEquals(9765625, FastExponentiation.fastExponentiation(-5, 10, 1000000007), "-5^10 mod 1000000007 should be 9765625"); + } + + /** + * Tests that a negative exponent throws an ArithmeticException. + */ + @Test + void testNegativeExponent() { + assertThrows(ArithmeticException.class, () -> { FastExponentiation.fastExponentiation(2, -5, 1000); }); + } + + /** + * Tests that the method throws an IllegalArgumentException for invalid modulus values. + */ + @Test + void testInvalidModulus() { + assertThrows(IllegalArgumentException.class, () -> { FastExponentiation.fastExponentiation(2, 5, 0); }); + } +} diff --git a/src/test/java/com/thealgorithms/maths/FastInverseSqrtTests.java b/src/test/java/com/thealgorithms/maths/FastInverseSqrtTests.java new file mode 100644 index 000000000000..a3416a6bc871 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/FastInverseSqrtTests.java @@ -0,0 +1,50 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertFalse; + +import org.junit.jupiter.api.Test; + +public class FastInverseSqrtTests { + + @Test + void testForOneElement() { + assertFalse(FastInverseSqrt.inverseSqrt(1332)); + // calls for the 2nd inverse method + } + + @Test + void testForsecond() { + assertFalse(FastInverseSqrt.inverseSqrt(1332f)); + // calls for the 1st inverse method + } + + @Test + void testForThird() { + assertFalse(FastInverseSqrt.inverseSqrt(1)); + } + + @Test + void testForFourth() { + assertFalse(FastInverseSqrt.inverseSqrt(1f)); + } + + @Test + void testForFifth() { + assertFalse(FastInverseSqrt.inverseSqrt(4522)); + } + + @Test + void testForSixth() { + assertFalse(FastInverseSqrt.inverseSqrt(4522f)); + } + + @Test + void testForSeventh() { + assertFalse(FastInverseSqrt.inverseSqrt(21)); + } + + @Test + void testForEighth() { + assertFalse(FastInverseSqrt.inverseSqrt(21f)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/FibonacciJavaStreamsTest.java b/src/test/java/com/thealgorithms/maths/FibonacciJavaStreamsTest.java new file mode 100644 index 000000000000..5cfb304ae471 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/FibonacciJavaStreamsTest.java @@ -0,0 +1,71 @@ +package com.thealgorithms.maths; + +import java.math.BigDecimal; +import java.util.Optional; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * @author Albina Gimaletdinova on 25/07/2023 + */ +public class FibonacciJavaStreamsTest { + private static final String EXCEPTION_MESSAGE = "Input index cannot be null or negative!"; + + @Test + public void testWithNegativeIndexShouldThrowException() { + Exception exception = Assertions.assertThrows(IllegalArgumentException.class, () -> FibonacciJavaStreams.calculate(new BigDecimal(-1))); + Assertions.assertEquals(EXCEPTION_MESSAGE, exception.getMessage()); + } + + @Test + public void testCheckTheFirst4SequenceElements() { + checkElement(BigDecimal.ZERO, BigDecimal.ZERO); + checkElement(BigDecimal.ONE, BigDecimal.ONE); + checkElement(BigDecimal.TWO, BigDecimal.ONE); + checkElement(new BigDecimal(3), BigDecimal.TWO); + } + + @Test + public void testCheck10thSequenceElement() { + checkElement(BigDecimal.TEN, new BigDecimal(55)); + } + + @Test + public void testCheck20thSequenceElement() { + checkElement(new BigDecimal(20), new BigDecimal(6765)); + } + + @Test + public void testCheck30thSequenceElement() { + checkElement(new BigDecimal(30), new BigDecimal(832040)); + } + + @Test + public void testCheck40thSequenceElement() { + checkElement(new BigDecimal(40), new BigDecimal(102334155)); + } + + @Test + public void testCheck50thSequenceElement() { + checkElement(new BigDecimal(50), new BigDecimal(12586269025L)); + } + + @Test + public void testCheck100thSequenceElement() { + checkElement(new BigDecimal(100), new BigDecimal("354224848179261915075")); + } + + @Test + public void testCheck200thSequenceElement() { + checkElement(new BigDecimal(200), new BigDecimal("280571172992510140037611932413038677189525")); + } + + private static void checkElement(BigDecimal index, BigDecimal expected) { + // when + Optional<BigDecimal> result = FibonacciJavaStreams.calculate(index); + + // then + Assertions.assertTrue(result.isPresent()); + Assertions.assertEquals(result.get(), expected); + } +} diff --git a/src/test/java/com/thealgorithms/maths/FibonacciLoopTest.java b/src/test/java/com/thealgorithms/maths/FibonacciLoopTest.java new file mode 100644 index 000000000000..93aec39765d4 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/FibonacciLoopTest.java @@ -0,0 +1,36 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.math.BigInteger; +import org.junit.jupiter.api.Test; + +public class FibonacciLoopTest { + @Test + public void checkValueAtZero() { + assertEquals(BigInteger.ZERO, FibonacciLoop.compute(0)); + } + + @Test + public void checkValueAtOne() { + assertEquals(BigInteger.ONE, FibonacciLoop.compute(1)); + } + + @Test + public void checkValueAtTwo() { + assertEquals(BigInteger.ONE, FibonacciLoop.compute(2)); + } + + @Test + public void checkRecurrenceRelation() { + for (int i = 0; i < 100; ++i) { + assertEquals(FibonacciLoop.compute(i + 2), FibonacciLoop.compute(i + 1).add(FibonacciLoop.compute(i))); + } + } + + @Test + public void checkNegativeInput() { + assertThrows(IllegalArgumentException.class, () -> { FibonacciLoop.compute(-1); }); + } +} diff --git a/src/test/java/com/thealgorithms/maths/FibonacciNumberCheckTest.java b/src/test/java/com/thealgorithms/maths/FibonacciNumberCheckTest.java new file mode 100644 index 000000000000..6ba81639a11a --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/FibonacciNumberCheckTest.java @@ -0,0 +1,30 @@ +package com.thealgorithms.maths; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * Fibonacci Sequence: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144... + * + * @author Albina Gimaletdinova on 01/07/2023 + */ +public class FibonacciNumberCheckTest { + @Test + public void testNumberIsFibonacciNumber() { + Assertions.assertTrue(FibonacciNumberCheck.isFibonacciNumber(1)); + Assertions.assertTrue(FibonacciNumberCheck.isFibonacciNumber(2)); + Assertions.assertTrue(FibonacciNumberCheck.isFibonacciNumber(21)); + Assertions.assertTrue(FibonacciNumberCheck.isFibonacciNumber(6765)); // 20th number + Assertions.assertTrue(FibonacciNumberCheck.isFibonacciNumber(832040)); // 30th number + Assertions.assertTrue(FibonacciNumberCheck.isFibonacciNumber(102334155)); // 40th number + Assertions.assertTrue(FibonacciNumberCheck.isFibonacciNumber(701408733)); // 45th number + } + + @Test + public void testNumberIsNotFibonacciNumber() { + Assertions.assertFalse(FibonacciNumberCheck.isFibonacciNumber(9)); + Assertions.assertFalse(FibonacciNumberCheck.isFibonacciNumber(10)); + Assertions.assertFalse(FibonacciNumberCheck.isFibonacciNumber(145)); + Assertions.assertFalse(FibonacciNumberCheck.isFibonacciNumber(701408734)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/FibonacciNumberGoldenRationTest.java b/src/test/java/com/thealgorithms/maths/FibonacciNumberGoldenRationTest.java new file mode 100644 index 000000000000..e3f7bf3e0fed --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/FibonacciNumberGoldenRationTest.java @@ -0,0 +1,29 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.math.BigInteger; +import org.junit.jupiter.api.Test; + +public class FibonacciNumberGoldenRationTest { + + @Test + public void returnsCorrectValues() { + for (int n = 0; n <= FibonacciNumberGoldenRation.MAX_ARG; ++n) { + final var actual = FibonacciNumberGoldenRation.compute(n); + final var expected = FibonacciLoop.compute(n); + assertEquals(expected, BigInteger.valueOf(actual)); + } + } + + @Test + public void throwsIllegalArgumentExceptionForNegativeInput() { + assertThrows(IllegalArgumentException.class, () -> { FibonacciNumberGoldenRation.compute(-1); }); + } + + @Test + public void throwsIllegalArgumentExceptionForLargeInput() { + assertThrows(IllegalArgumentException.class, () -> { FibonacciNumberGoldenRation.compute(FibonacciNumberGoldenRation.MAX_ARG + 1); }); + } +} diff --git a/src/test/java/com/thealgorithms/maths/FindKthNumberTest.java b/src/test/java/com/thealgorithms/maths/FindKthNumberTest.java new file mode 100644 index 000000000000..ca69e66c9f6a --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/FindKthNumberTest.java @@ -0,0 +1,72 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.Arrays; +import java.util.Random; +import org.junit.jupiter.api.Test; + +public class FindKthNumberTest { + @Test + public void testFindKthMaxTypicalCases() { + int[] array1 = {3, 2, 1, 4, 5}; + assertEquals(3, FindKthNumber.findKthMax(array1, 3)); + assertEquals(4, FindKthNumber.findKthMax(array1, 2)); + assertEquals(5, FindKthNumber.findKthMax(array1, 1)); + assertEquals(3, FindKthNumber.findKthMaxUsingHeap(array1, 3)); + assertEquals(4, FindKthNumber.findKthMaxUsingHeap(array1, 2)); + assertEquals(5, FindKthNumber.findKthMaxUsingHeap(array1, 1)); + + int[] array2 = {7, 5, 8, 2, 1, 6}; + assertEquals(5, FindKthNumber.findKthMax(array2, 4)); + assertEquals(6, FindKthNumber.findKthMax(array2, 3)); + assertEquals(8, FindKthNumber.findKthMax(array2, 1)); + assertEquals(5, FindKthNumber.findKthMaxUsingHeap(array2, 4)); + assertEquals(6, FindKthNumber.findKthMaxUsingHeap(array2, 3)); + assertEquals(8, FindKthNumber.findKthMaxUsingHeap(array2, 1)); + } + + @Test + public void testFindKthMaxEdgeCases() { + int[] array1 = {1}; + assertEquals(1, FindKthNumber.findKthMax(array1, 1)); + assertEquals(1, FindKthNumber.findKthMaxUsingHeap(array1, 1)); + + int[] array2 = {5, 3}; + assertEquals(5, FindKthNumber.findKthMax(array2, 1)); + assertEquals(3, FindKthNumber.findKthMax(array2, 2)); + assertEquals(5, FindKthNumber.findKthMaxUsingHeap(array2, 1)); + assertEquals(3, FindKthNumber.findKthMaxUsingHeap(array2, 2)); + } + + @Test + public void testFindKthMaxInvalidK() { + int[] array = {1, 2, 3, 4, 5}; + assertThrows(IllegalArgumentException.class, () -> FindKthNumber.findKthMax(array, 0)); + assertThrows(IllegalArgumentException.class, () -> FindKthNumber.findKthMax(array, 6)); + assertThrows(IllegalArgumentException.class, () -> FindKthNumber.findKthMaxUsingHeap(array, 0)); + assertThrows(IllegalArgumentException.class, () -> FindKthNumber.findKthMaxUsingHeap(array, 6)); + } + + @Test + public void testFindKthMaxLargeArray() { + int[] array = generateArray(1000); + int k = new Random().nextInt(1, array.length); + int result = FindKthNumber.findKthMax(array, k); + int maxK = FindKthNumber.findKthMaxUsingHeap(array, k); + Arrays.sort(array); + assertEquals(array[array.length - k], result); + assertEquals(array[array.length - k], maxK); + } + + public static int[] generateArray(int capacity) { + int size = new Random().nextInt(2, capacity); + int[] array = new int[size]; + + for (int i = 0; i < size; i++) { + array[i] = new Random().nextInt(100); // Ensure positive values for testing + } + return array; + } +} diff --git a/src/test/java/com/thealgorithms/maths/FindMaxRecursionTest.java b/src/test/java/com/thealgorithms/maths/FindMaxRecursionTest.java new file mode 100644 index 000000000000..d54cae67209f --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/FindMaxRecursionTest.java @@ -0,0 +1,28 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class FindMaxRecursionTest { + + @ParameterizedTest + @MethodSource("inputStream") + void numberTests(int expected, int[] input) { + Assertions.assertEquals(expected, FindMaxRecursion.max(input)); + } + + private static Stream<Arguments> inputStream() { + return Stream.of(Arguments.of(5, new int[] {5, 5, 5, 5, 5}), Arguments.of(0, new int[] {-1, 0}), Arguments.of(-1, new int[] {-10, -9, -8, -7, -6, -5, -4, -3, -2, -1}), Arguments.of(9, new int[] {3, -2, 3, 9, -4, -4, 8}), Arguments.of(3, new int[] {3})); + } + + @Test + public void testFindMaxThrowsExceptionForEmptyInput() { + assertThrows(IllegalArgumentException.class, () -> FindMaxRecursion.max(new int[] {})); + } +} diff --git a/src/test/java/com/thealgorithms/maths/FindMaxTest.java b/src/test/java/com/thealgorithms/maths/FindMaxTest.java new file mode 100644 index 000000000000..a863a2c8586b --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/FindMaxTest.java @@ -0,0 +1,28 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class FindMaxTest { + + @ParameterizedTest + @MethodSource("inputStream") + void numberTests(int expected, int[] input) { + Assertions.assertEquals(expected, FindMax.findMax(input)); + } + + private static Stream<Arguments> inputStream() { + return Stream.of(Arguments.of(10, new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}), Arguments.of(5, new int[] {5, 5, 5, 5, 5}), Arguments.of(0, new int[] {-1, 0}), Arguments.of(-1, new int[] {-10, -9, -8, -7, -6, -5, -4, -3, -2, -1}), Arguments.of(9, new int[] {3, -2, 3, 9, -4, -4, 8})); + } + + @Test + public void testFindMaxThrowsExceptionForEmptyInput() { + assertThrows(IllegalArgumentException.class, () -> FindMax.findMax(new int[] {})); + } +} diff --git a/src/test/java/com/thealgorithms/maths/FindMinRecursionTest.java b/src/test/java/com/thealgorithms/maths/FindMinRecursionTest.java new file mode 100644 index 000000000000..3c36702b881d --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/FindMinRecursionTest.java @@ -0,0 +1,28 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class FindMinRecursionTest { + + @ParameterizedTest + @MethodSource("inputStream") + void numberTests(int expected, int[] input) { + Assertions.assertEquals(expected, FindMinRecursion.min(input)); + } + + private static Stream<Arguments> inputStream() { + return Stream.of(Arguments.of(5, new int[] {5, 5, 5, 5, 5}), Arguments.of(-1, new int[] {-1, 0}), Arguments.of(-10, new int[] {-10, -9, -8, -7, -6, -5, -4, -3, -2, -1}), Arguments.of(-4, new int[] {3, -2, 3, 9, -4, -4, 8}), Arguments.of(3, new int[] {3})); + } + + @Test + public void testFindMaxThrowsExceptionForEmptyInput() { + assertThrows(IllegalArgumentException.class, () -> FindMinRecursion.min(new int[] {})); + } +} diff --git a/src/test/java/com/thealgorithms/maths/FindMinTest.java b/src/test/java/com/thealgorithms/maths/FindMinTest.java new file mode 100644 index 000000000000..e713c0191ea1 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/FindMinTest.java @@ -0,0 +1,29 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class FindMinTest { + + @ParameterizedTest + @MethodSource("inputStream") + void numberTests(int expected, int[] input) { + Assertions.assertEquals(expected, FindMin.findMin(input)); + } + + private static Stream<Arguments> inputStream() { + return Stream.of(Arguments.of(1, new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}), Arguments.of(5, new int[] {5, 5, 5, 5, 5}), Arguments.of(0, new int[] {0, 192, 384, 576}), Arguments.of(-1, new int[] {-1, 2, 5, 10}), Arguments.of(-10, new int[] {-10, -9, -8, -7, -6, -5, -4, -3, -2, -1}), + Arguments.of(-4, new int[] {4, -3, 8, 9, -4, -4, 10})); + } + + @Test + public void testFindMinThrowsExceptionForEmptyInput() { + assertThrows(IllegalArgumentException.class, () -> FindMin.findMin(new int[] {})); + } +} diff --git a/src/test/java/com/thealgorithms/maths/FloorTest.java b/src/test/java/com/thealgorithms/maths/FloorTest.java new file mode 100644 index 000000000000..19aed70ccb71 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/FloorTest.java @@ -0,0 +1,28 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +public class FloorTest { + @Test + public void testFloorWholeNumber() { + assertEquals(0, Floor.floor(0)); + assertEquals(1, Floor.floor(1)); + assertEquals(-1, Floor.floor(-1)); + assertEquals(42, Floor.floor(42)); + assertEquals(-42, Floor.floor(-42)); + } + + @Test + public void testFloorDoubleNumber() { + assertEquals(0, Floor.floor(0.1)); + assertEquals(1, Floor.floor(1.9)); + assertEquals(-2, Floor.floor(-1.1)); + assertEquals(-43, Floor.floor(-42.7)); + } + + @Test + public void testFloorNegativeZero() { + assertEquals(-0.0, Floor.floor(-0.0)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/FrizzyNumberTest.java b/src/test/java/com/thealgorithms/maths/FrizzyNumberTest.java new file mode 100644 index 000000000000..a5fd867e900e --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/FrizzyNumberTest.java @@ -0,0 +1,28 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +public class FrizzyNumberTest { + @Test + public void testFrizziesForBase2() { + assertEquals(1, FrizzyNumber.getNthFrizzy(2, 1)); + assertEquals(3, FrizzyNumber.getNthFrizzy(2, 3)); + assertEquals(1000, FrizzyNumber.getNthFrizzy(2, 1000)); + } + + @Test + public void testFrizziesForBase3() { + assertEquals(1, FrizzyNumber.getNthFrizzy(3, 1)); + assertEquals(3, FrizzyNumber.getNthFrizzy(3, 2)); + assertEquals(29430, FrizzyNumber.getNthFrizzy(3, 1000)); + } + + @Test + public void testFrizziesForBase69() { + assertEquals(1, FrizzyNumber.getNthFrizzy(69, 1)); + assertEquals(69, FrizzyNumber.getNthFrizzy(69, 2)); + assertEquals(328510, FrizzyNumber.getNthFrizzy(69, 9)); + assertEquals(333340, FrizzyNumber.getNthFrizzy(69, 15)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/GCDRecursionTest.java b/src/test/java/com/thealgorithms/maths/GCDRecursionTest.java new file mode 100644 index 000000000000..6b6ea2f85164 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/GCDRecursionTest.java @@ -0,0 +1,48 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; + +public class GCDRecursionTest { + + @ParameterizedTest + @CsvSource({"7, 5, 1", "9, 12, 3", "18, 24, 6", "36, 60, 12"}) + void testGcdPositiveNumbers(int a, int b, int expectedGcd) { + assertEquals(expectedGcd, GCDRecursion.gcd(a, b)); + } + + @ParameterizedTest + @CsvSource({"0, 5, 5", "8, 0, 8"}) + void testGcdOneZero(int a, int b, int expectedGcd) { + assertEquals(expectedGcd, GCDRecursion.gcd(a, b)); + } + + @Test + void testGcdBothZero() { + assertEquals(0, GCDRecursion.gcd(0, 0)); + } + + @ParameterizedTest + @ValueSource(ints = {-5, -15}) + void testGcdNegativeNumbers(int negativeValue) { + assertThrows(ArithmeticException.class, () -> GCDRecursion.gcd(negativeValue, 15)); + assertThrows(ArithmeticException.class, () -> GCDRecursion.gcd(15, negativeValue)); + } + + @ParameterizedTest + @CsvSource({"5, 5, 5", "8, 8, 8"}) + void testGcdWithSameNumbers(int a, int b, int expectedGcd) { + assertEquals(expectedGcd, GCDRecursion.gcd(a, b)); + } + + @ParameterizedTest + @CsvSource({"7, 13, 1", "11, 17, 1"}) + void testGcdWithPrimeNumbers(int a, int b, int expectedGcd) { + assertEquals(expectedGcd, GCDRecursion.gcd(a, b)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/GCDTest.java b/src/test/java/com/thealgorithms/maths/GCDTest.java new file mode 100644 index 000000000000..bac3f8f7596c --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/GCDTest.java @@ -0,0 +1,62 @@ +package com.thealgorithms.maths; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class GCDTest { + + @Test + void test1() { + Assertions.assertThrows(ArithmeticException.class, () -> GCD.gcd(-1, 0)); + } + + @Test + void test2() { + Assertions.assertThrows(ArithmeticException.class, () -> GCD.gcd(10, -2)); + } + + @Test + void test3() { + Assertions.assertThrows(ArithmeticException.class, () -> GCD.gcd(-5, -3)); + } + + @Test + void test4() { + Assertions.assertEquals(GCD.gcd(0, 2), 2); + } + + @Test + void test5() { + Assertions.assertEquals(GCD.gcd(10, 0), 10); + } + + @Test + void test6() { + Assertions.assertEquals(GCD.gcd(1, 0), 1); + } + + @Test + void test7() { + Assertions.assertEquals(GCD.gcd(9, 6), 3); + } + + @Test + void test8() { + Assertions.assertEquals(GCD.gcd(48, 18, 30, 12), 6); + } + + @Test + void testArrayGcd1() { + Assertions.assertEquals(GCD.gcd(new int[] {9, 6}), 3); + } + + @Test + void testArrayGcd2() { + Assertions.assertEquals(GCD.gcd(new int[] {2 * 3 * 5 * 7, 2 * 5 * 5 * 5, 2 * 5 * 11, 5 * 5 * 5 * 13}), 5); + } + + @Test + void testArrayGcdForEmptyInput() { + Assertions.assertEquals(GCD.gcd(new int[] {}), 0); + } +} diff --git a/src/test/java/com/thealgorithms/maths/GaussianTest.java b/src/test/java/com/thealgorithms/maths/GaussianTest.java new file mode 100644 index 000000000000..fe900fa22d26 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/GaussianTest.java @@ -0,0 +1,29 @@ +package com.thealgorithms.maths; + +import static com.thealgorithms.maths.Gaussian.gaussian; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import org.junit.jupiter.api.Test; + +public class GaussianTest { + + // easy pass test for the whole class. Matrix of 2*3. + @Test + void passTest1() { + ArrayList<Double> list = new ArrayList<Double>(); + ArrayList<Double> answer = new ArrayList<Double>(); + answer.add(0.0); + answer.add(1.0); + + int matrixSize = 2; + list.add(1.0); + list.add(1.0); + list.add(1.0); + list.add(2.0); + list.add(1.0); + list.add(1.0); + + assertEquals(answer, gaussian(matrixSize, list)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/GenericRootTest.java b/src/test/java/com/thealgorithms/maths/GenericRootTest.java new file mode 100644 index 000000000000..2578cfe82305 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/GenericRootTest.java @@ -0,0 +1,26 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class GenericRootTest { + @ParameterizedTest + @MethodSource("tcStream") + public void testGenericRoot(final int input, final int expected) { + assertEquals(expected, GenericRoot.genericRoot(input)); + } + + @ParameterizedTest + @MethodSource("tcStream") + public void testGenericRootWithNegativeInputs(final int input, final int expected) { + assertEquals(expected, GenericRoot.genericRoot(-input)); + } + + private static Stream<Arguments> tcStream() { + return Stream.of(Arguments.of(0, 0), Arguments.of(1, 1), Arguments.of(12345, 6), Arguments.of(123, 6), Arguments.of(15937, 7), Arguments.of(222222, 3), Arguments.of(99999, 9)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/GoldbachConjectureTest.java b/src/test/java/com/thealgorithms/maths/GoldbachConjectureTest.java new file mode 100644 index 000000000000..84c5824d26ae --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/GoldbachConjectureTest.java @@ -0,0 +1,29 @@ +package com.thealgorithms.maths; + +import static com.thealgorithms.maths.GoldbachConjecture.getPrimeSum; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +public class GoldbachConjectureTest { + @Test + void testValidEvenNumbers() { + assertEquals(new GoldbachConjecture.Result(3, 7), getPrimeSum(10)); // 10 = 3 + 7 + assertEquals(new GoldbachConjecture.Result(5, 7), getPrimeSum(12)); // 12 = 5 + 7 + assertEquals(new GoldbachConjecture.Result(3, 11), getPrimeSum(14)); // 14 = 3 + 11 + assertEquals(new GoldbachConjecture.Result(5, 13), getPrimeSum(18)); // 18 = 5 + 13 + } + @Test + void testInvalidOddNumbers() { + assertThrows(IllegalArgumentException.class, () -> getPrimeSum(7)); + assertThrows(IllegalArgumentException.class, () -> getPrimeSum(15)); + } + @Test + void testLesserThanTwo() { + assertThrows(IllegalArgumentException.class, () -> getPrimeSum(1)); + assertThrows(IllegalArgumentException.class, () -> getPrimeSum(2)); + assertThrows(IllegalArgumentException.class, () -> getPrimeSum(-5)); + assertThrows(IllegalArgumentException.class, () -> getPrimeSum(-26)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/HarshadNumberTest.java b/src/test/java/com/thealgorithms/maths/HarshadNumberTest.java new file mode 100644 index 000000000000..af1c459f3d7f --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/HarshadNumberTest.java @@ -0,0 +1,25 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class HarshadNumberTest { + + @Test + public void harshadNumber() { + + assertTrue(HarshadNumber.isHarshad(18)); + assertFalse(HarshadNumber.isHarshad(-18)); + assertFalse(HarshadNumber.isHarshad(19)); + assertTrue(HarshadNumber.isHarshad(999999999)); + assertFalse(HarshadNumber.isHarshad(0)); + + assertTrue(HarshadNumber.isHarshad("18")); + assertFalse(HarshadNumber.isHarshad("-18")); + assertFalse(HarshadNumber.isHarshad("19")); + assertTrue(HarshadNumber.isHarshad("999999999")); + assertTrue(HarshadNumber.isHarshad("99999999999100")); + } +} diff --git a/src/test/java/com/thealgorithms/maths/HeronsFormulaTest.java b/src/test/java/com/thealgorithms/maths/HeronsFormulaTest.java new file mode 100644 index 000000000000..22cecf4dc960 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/HeronsFormulaTest.java @@ -0,0 +1,39 @@ +package com.thealgorithms.maths; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class HeronsFormulaTest { + + @Test + void test1() { + Assertions.assertEquals(HeronsFormula.herons(3, 4, 5), 6.0); + } + + @Test + void test2() { + Assertions.assertEquals(HeronsFormula.herons(24, 30, 18), 216.0); + } + + @Test + void test3() { + Assertions.assertEquals(HeronsFormula.herons(1, 1, 1), 0.4330127018922193); + } + + @Test + void test4() { + Assertions.assertEquals(HeronsFormula.herons(4, 5, 8), 8.181534085976786); + } + + @Test + public void testCalculateAreaWithInvalidInput() { + Assertions.assertThrows(IllegalArgumentException.class, () -> { HeronsFormula.herons(1, 2, 3); }); + Assertions.assertThrows(IllegalArgumentException.class, () -> { HeronsFormula.herons(2, 1, 3); }); + Assertions.assertThrows(IllegalArgumentException.class, () -> { HeronsFormula.herons(3, 2, 1); }); + Assertions.assertThrows(IllegalArgumentException.class, () -> { HeronsFormula.herons(1, 3, 2); }); + + Assertions.assertThrows(IllegalArgumentException.class, () -> { HeronsFormula.herons(1, 1, 0); }); + Assertions.assertThrows(IllegalArgumentException.class, () -> { HeronsFormula.herons(1, 0, 1); }); + Assertions.assertThrows(IllegalArgumentException.class, () -> { HeronsFormula.herons(0, 1, 1); }); + } +} diff --git a/src/test/java/com/thealgorithms/maths/JosephusProblemTest.java b/src/test/java/com/thealgorithms/maths/JosephusProblemTest.java new file mode 100644 index 000000000000..650b8dd578f7 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/JosephusProblemTest.java @@ -0,0 +1,14 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class JosephusProblemTest { + + @Test + void testJosephusProblem() { + assertEquals(3, JosephusProblem.findTheWinner(5, 2)); + assertEquals(5, JosephusProblem.findTheWinner(6, 4)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/KaprekarNumbersTest.java b/src/test/java/com/thealgorithms/maths/KaprekarNumbersTest.java new file mode 100644 index 000000000000..05e58cf88e22 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/KaprekarNumbersTest.java @@ -0,0 +1,85 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.junit.jupiter.api.Test; + +public class KaprekarNumbersTest { + + @Test + void testFor1() { + assertTrue(KaprekarNumbers.isKaprekarNumber(1)); + } + + @Test + void testFor45() { + assertTrue(KaprekarNumbers.isKaprekarNumber(45)); + } + + @Test + void testFor297() { + assertTrue(KaprekarNumbers.isKaprekarNumber(297)); + } + + @Test + void testFor2223() { + assertTrue(KaprekarNumbers.isKaprekarNumber(2223)); + } + + @Test + void testFor857143() { + assertTrue(KaprekarNumbers.isKaprekarNumber(857143)); + } + + @Test + void testFor3() { + assertFalse(KaprekarNumbers.isKaprekarNumber(3)); + } + + @Test + void testFor26() { + assertFalse(KaprekarNumbers.isKaprekarNumber(26)); + } + + @Test + void testFor98() { + assertFalse(KaprekarNumbers.isKaprekarNumber(98)); + } + + @Test + void testForRangeOfNumber() { + try { + List<Long> rangedNumbers = KaprekarNumbers.kaprekarNumberInRange(1, 100000); + long[] allTheNumbers = { + 1, + 9, + 45, + 55, + 99, + 297, + 703, + 999, + 2223, + 2728, + 4950, + 5050, + 7272, + 7777, + 9999, + 17344, + 22222, + 77778, + 82656, + 95121, + 99999, + }; + for (long i : allTheNumbers) { + assert rangedNumbers.contains(i); + } + } catch (Exception e) { + assert false; + } + } +} diff --git a/src/test/java/com/thealgorithms/maths/KaratsubaMultiplicationTest.java b/src/test/java/com/thealgorithms/maths/KaratsubaMultiplicationTest.java new file mode 100644 index 000000000000..e184d998724a --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/KaratsubaMultiplicationTest.java @@ -0,0 +1,58 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.math.BigInteger; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * Unit test class for {@link KaratsubaMultiplication} class. + * + * <p> + * This class tests various edge cases and normal cases for the + * Karatsuba multiplication algorithm implemented in the KaratsubaMultiplication class. + * It uses parameterized tests to handle multiple test cases. + * </p> + */ +class KaratsubaMultiplicationTest { + + /** + * Provides test data for the parameterized test. + * Each entry in the stream contains three elements: x, y, and the expected result. + * + * @return a stream of arguments for the parameterized test + */ + static Stream<Arguments> provideTestCases() { + return Stream.of( + // Test case 1: Two small numbers + Arguments.of(new BigInteger("1234"), new BigInteger("5678"), new BigInteger("7006652")), + // Test case 2: Two large numbers + Arguments.of(new BigInteger("342364"), new BigInteger("393958"), new BigInteger("134877036712")), + // Test case 3: One number is zero + Arguments.of(BigInteger.ZERO, new BigInteger("5678"), BigInteger.ZERO), + // Test case 4: Both numbers are zero + Arguments.of(BigInteger.ZERO, BigInteger.ZERO, BigInteger.ZERO), + // Test case 5: Single-digit numbers + Arguments.of(new BigInteger("9"), new BigInteger("8"), new BigInteger("72"))); + } + + /** + * Parameterized test for Karatsuba multiplication. + * + * <p> + * This method runs the Karatsuba multiplication algorithm for multiple test cases. + * </p> + * + * @param x the first number to multiply + * @param y the second number to multiply + * @param expected the expected result of x * y + */ + @ParameterizedTest + @MethodSource("provideTestCases") + void testKaratsubaMultiplication(BigInteger x, BigInteger y, BigInteger expected) { + assertEquals(expected, KaratsubaMultiplication.karatsuba(x, y)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/KrishnamurthyNumberTest.java b/src/test/java/com/thealgorithms/maths/KrishnamurthyNumberTest.java new file mode 100644 index 000000000000..595acde2b5d8 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/KrishnamurthyNumberTest.java @@ -0,0 +1,62 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the KrishnamurthyNumber class. + */ +public class KrishnamurthyNumberTest { + + /** + * Test the isKrishnamurthy method with a known Krishnamurthy number. + */ + @Test + public void testIsKrishnamurthyTrue() { + assertTrue(KrishnamurthyNumber.isKrishnamurthy(145)); + } + + /** + * Test the isKrishnamurthy method with a number that is not a Krishnamurthy number. + */ + @Test + public void testIsKrishnamurthyFalse() { + assertFalse(KrishnamurthyNumber.isKrishnamurthy(123)); + } + + /** + * Test the isKrishnamurthy method with zero. + */ + @Test + public void testIsKrishnamurthyZero() { + assertFalse(KrishnamurthyNumber.isKrishnamurthy(0)); + } + + /** + * Test the isKrishnamurthy method with a negative number. + */ + @Test + public void testIsKrishnamurthyNegative() { + assertFalse(KrishnamurthyNumber.isKrishnamurthy(-145)); + } + + /** + * Test the isKrishnamurthy method with a single-digit Krishnamurthy number. + */ + @Test + public void testIsKrishnamurthySingleDigitTrue() { + assertTrue(KrishnamurthyNumber.isKrishnamurthy(1)); + assertTrue(KrishnamurthyNumber.isKrishnamurthy(2)); + } + + /** + * Test the isKrishnamurthy method with a single-digit number that is not a Krishnamurthy number. + */ + @Test + public void testIsKrishnamurthySingleDigitFalse() { + assertFalse(KrishnamurthyNumber.isKrishnamurthy(3)); + assertFalse(KrishnamurthyNumber.isKrishnamurthy(4)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/LeastCommonMultipleTest.java b/src/test/java/com/thealgorithms/maths/LeastCommonMultipleTest.java new file mode 100644 index 000000000000..73da509723c5 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/LeastCommonMultipleTest.java @@ -0,0 +1,21 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class LeastCommonMultipleTest { + + @ParameterizedTest + @MethodSource("provideTestCases") + void testLcm(int num1, int num2, int expected) { + assertEquals(expected, LeastCommonMultiple.lcm(num1, num2)); + } + + private static Stream<Arguments> provideTestCases() { + return Stream.of(Arguments.of(12, 18, 36), Arguments.of(5, 10, 10), Arguments.of(7, 3, 21), Arguments.of(21, 6, 42), Arguments.of(1, 1, 1), Arguments.of(8, 12, 24), Arguments.of(14, 35, 70), Arguments.of(15, 25, 75), Arguments.of(100, 25, 100), Arguments.of(0, 10, 0)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/LeonardoNumberTest.java b/src/test/java/com/thealgorithms/maths/LeonardoNumberTest.java new file mode 100644 index 000000000000..705f1a1006fa --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/LeonardoNumberTest.java @@ -0,0 +1,29 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +public class LeonardoNumberTest { + @Test + void leonardoNumberNegative() { + assertThrows(ArithmeticException.class, () -> LeonardoNumber.leonardoNumber(-1)); + } + @Test + void leonardoNumberZero() { + assertEquals(1, LeonardoNumber.leonardoNumber(0)); + } + @Test + void leonardoNumberOne() { + assertEquals(1, LeonardoNumber.leonardoNumber(1)); + } + @Test + void leonardoNumberFive() { + assertEquals(15, LeonardoNumber.leonardoNumber(5)); + } + @Test + void leonardoNumberTwenty() { + assertEquals(21891, LeonardoNumber.leonardoNumber(20)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/LongDivisionTest.java b/src/test/java/com/thealgorithms/maths/LongDivisionTest.java new file mode 100644 index 000000000000..24f757f8f3ad --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/LongDivisionTest.java @@ -0,0 +1,58 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class LongDivisionTest { + + // Requirement: Dividend (positive) is greater than divisor (positive), returns correct integer + // after division + @Test + void testOne() { + assertEquals(3, LongDivision.divide(10, 3)); + } + + // Requirement: Dividend (positive) is greater than divisor (negative), returns correct integer + // after division + @Test + void testTwo() { + assertEquals(-2, LongDivision.divide(7, -3)); + } + + // Requirement: Dividend (positive) is greater than divisor (negative), returns correct integer + // after division Basically the same as in the first test + @Test + void testThree() { + assertEquals(10, LongDivision.divide(105, 10)); + } + + // Requirement: Dividend (negative), divisor (positive), returns correct integer after division + // Tests the case where the dividend is less than 0. + @Test + void testNegativeDividend() { + assertEquals(-1, LongDivision.divide(-5, 3)); + } + + // Requirement: Dividend (positive), divisor (positive), returns correct integer after division + // Tests the case where the dividend is less than the divisor. The test should return 0 in this + // case. + @Test + void testDividendLessThanDivisor() { + assertEquals(0, LongDivision.divide(3, 5)); + } + + // Requirement: Dividend (neither), divisor (positive), returns correct integer after division + // Tests the case where the dividend is 0. This should return a 0. + @Test + void testDividendIsZero() { + assertEquals(0, LongDivision.divide(0, 5)); + } + + // Requirement: Dividend (positive), divisor (neither), returns correct integer after division + // Tests the case where the divisor is 0. This should return a 0. + @Test + void testDivisionByZero() { + assertEquals(0, LongDivision.divide(5, 0)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/LucasSeriesTest.java b/src/test/java/com/thealgorithms/maths/LucasSeriesTest.java new file mode 100644 index 000000000000..3576268c5d0c --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/LucasSeriesTest.java @@ -0,0 +1,28 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class LucasSeriesTest { + @Test + void lucasSeriesTwo() { + assertEquals(2, LucasSeries.lucasSeries(1)); + assertEquals(2, LucasSeries.lucasSeriesIteration(1)); + } + @Test + void lucasSeriesOne() { + assertEquals(1, LucasSeries.lucasSeries(2)); + assertEquals(1, LucasSeries.lucasSeriesIteration(2)); + } + @Test + void lucasSeriesSeven() { + assertEquals(7, LucasSeries.lucasSeries(5)); + assertEquals(7, LucasSeries.lucasSeriesIteration(5)); + } + @Test + void lucasSeriesEleven() { + assertEquals(123, LucasSeries.lucasSeries(11)); + assertEquals(123, LucasSeries.lucasSeriesIteration(11)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/MathBuilderTest.java b/src/test/java/com/thealgorithms/maths/MathBuilderTest.java new file mode 100644 index 000000000000..dc381bfca5d3 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/MathBuilderTest.java @@ -0,0 +1,52 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class MathBuilderTest { + + @Test + void simpleMath() { + double result = new MathBuilder.Builder(100).add(200).multiply(10).print().divideIf(20, (a, b) -> a % 2 == 0).sqrt().print().ceil().build().get(); + assertEquals(13, result); + } + + @Test + void memoryTest() { + long result = new MathBuilder.Builder().set(100).sqrt().remember().add(21).recallIf(a -> a < 50, true).mod(2).build().toLong(); + assertEquals(0, result); + } + + @Test + void freeFallDistance() { + long distance = new MathBuilder.Builder(9.81).multiply(0.5).multiply(5 * 5).round().build().toLong(); + assertEquals(123, distance); // Expected result: 0.5 * 9.81 * 25 = 122.625 ≈ 123 + } + + @Test + void batchSalaryProcessing() { + double[] salaries = {2000, 3000, 4000, 5000}; + long[] processedSalaries = new long[salaries.length]; + for (int i = 0; i < salaries.length; i++) { + processedSalaries[i] = new MathBuilder.Builder(salaries[i]).addIf(salaries[i] * 0.1, (sal, bonus) -> sal > 2500).multiply(0.92).round().build().toLong(); + } + long[] expectedSalaries = {1840, 3036, 4048, 5060}; + assertArrayEquals(expectedSalaries, processedSalaries); + } + + @Test + void parenthesis() { + // 10 + (20*5) - 40 + (100 / 10) = 80 + double result = new MathBuilder.Builder(10).openParenthesis(20).multiply(5).closeParenthesisAndPlus().minus(40).openParenthesis(100).divide(10).closeParenthesisAndPlus().build().get(); + assertEquals(80, result); + } + + @Test + void areaOfCircle() { + // Radius is 4 + double area = new MathBuilder.Builder().pi().openParenthesis(4).multiply(4).closeParenthesisAndMultiply().build().get(); + assertEquals(Math.PI * 4 * 4, area); + } +} diff --git a/src/test/java/com/thealgorithms/maths/MaxValueTest.java b/src/test/java/com/thealgorithms/maths/MaxValueTest.java new file mode 100644 index 000000000000..3c0fe8447c12 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/MaxValueTest.java @@ -0,0 +1,14 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class MaxValueTest { + @Test + public void maxTest() { + assertEquals(-1, MaxValue.max(-1, -3)); + assertEquals(3, MaxValue.max(3, 2)); + assertEquals(5, MaxValue.max(5, 5)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/MeansTest.java b/src/test/java/com/thealgorithms/maths/MeansTest.java new file mode 100644 index 000000000000..4b3a5df44b34 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/MeansTest.java @@ -0,0 +1,71 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.Vector; +import org.assertj.core.util.Lists; +import org.assertj.core.util.Sets; +import org.junit.jupiter.api.Test; + +class MeansTest { + + @Test + void arithmeticMeanZeroNumbers() throws IllegalArgumentException { + List<Double> numbers = new ArrayList<>(); + assertThrows(IllegalArgumentException.class, () -> Means.arithmetic(numbers)); + } + + @Test + void geometricMeanZeroNumbers() throws IllegalArgumentException { + List<Double> numbers = new ArrayList<>(); + assertThrows(IllegalArgumentException.class, () -> Means.geometric(numbers)); + } + + @Test + void harmonicMeanZeroNumbers() throws IllegalArgumentException { + List<Double> numbers = new ArrayList<>(); + assertThrows(IllegalArgumentException.class, () -> Means.harmonic(numbers)); + } + + @Test + void arithmeticMeanSingleNumber() { + List<Double> numbers = Lists.newArrayList(2.5); + assertEquals(2.5, Means.arithmetic(numbers)); + } + + @Test + void geometricMeanSingleNumber() { + Set<Double> numbers = Sets.newHashSet(Lists.newArrayList(2.5)); + assertEquals(2.5, Means.geometric(numbers)); + } + + @Test + void harmonicMeanSingleNumber() { + LinkedHashSet<Double> numbers = Sets.newLinkedHashSet(2.5); + assertEquals(2.5, Means.harmonic(numbers)); + } + + @Test + void arithmeticMeanMultipleNumbers() { + Set<Double> numbers = Sets.newTreeSet(1d, 2.5, 83.3, 25.9999, 46.0001, 74.7, 74.5); + assertEquals(44, Means.arithmetic(numbers)); + } + + @Test + void geometricMeanMultipleNumbers() { + LinkedList<Double> numbers = new LinkedList<>(Lists.newArrayList(1d, 2d, 3d, 4d, 5d, 6d, 1.25)); + assertEquals(2.6426195539300585, Means.geometric(numbers)); + } + + @Test + void harmonicMeanMultipleNumbers() { + Vector<Double> numbers = new Vector<>(Lists.newArrayList(1d, 2.5, 83.3, 25.9999, 46.0001, 74.7, 74.5)); + assertEquals(4.6697322801074135, Means.harmonic(numbers)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/MedianTest.java b/src/test/java/com/thealgorithms/maths/MedianTest.java new file mode 100644 index 000000000000..d2b637abd3cb --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/MedianTest.java @@ -0,0 +1,37 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class MedianTest { + @Test + void medianSingleValue() { + int[] arr = {0}; + assertEquals(0, Median.median(arr)); + } + + @Test + void medianTwoValues() { + int[] arr = {1, 2}; + assertEquals(1.5, Median.median(arr)); + } + + @Test + void medianThreeValues() { + int[] arr = {1, 2, 3}; + assertEquals(2, Median.median(arr)); + } + + @Test + void medianDecimalValueReturn() { + int[] arr = {1, 2, 3, 4, 5, 6, 8, 9}; + assertEquals(4.5, Median.median(arr)); + } + + @Test + void medianNegativeValues() { + int[] arr = {-27, -16, -7, -4, -2, -1}; + assertEquals(-5.5, Median.median(arr)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/MinValueTest.java b/src/test/java/com/thealgorithms/maths/MinValueTest.java new file mode 100644 index 000000000000..beb0ccb1a571 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/MinValueTest.java @@ -0,0 +1,14 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class MinValueTest { + @Test + public void minTest() { + assertEquals(-1, MinValue.min(-1, 3)); + assertEquals(2, MinValue.min(3, 2)); + assertEquals(5, MinValue.min(5, 5)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/ModeTest.java b/src/test/java/com/thealgorithms/maths/ModeTest.java new file mode 100644 index 000000000000..629fd8bd580a --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/ModeTest.java @@ -0,0 +1,21 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class ModeTest { + @ParameterizedTest + @MethodSource("tcStream") + void basicTest(final int[] expected, final int[] numbers) { + assertArrayEquals(expected, Mode.mode(numbers)); + } + + private static Stream<Arguments> tcStream() { + return Stream.of(Arguments.of(null, new int[] {}), Arguments.of(new int[] {5}, new int[] {5}), Arguments.of(new int[] {1, 2, 3, 4, 5}, new int[] {1, 2, 3, 4, 5}), Arguments.of(new int[] {1, 2, 3, 4, 5}, new int[] {5, 4, 3, 2, 1}), + Arguments.of(new int[] {7}, new int[] {7, 9, 9, 4, 5, 6, 7, 7, 8}), Arguments.of(new int[] {7, 9}, new int[] {7, 9, 9, 4, 5, 6, 7, 7, 9})); + } +} diff --git a/src/test/java/com/thealgorithms/maths/NonRepeatingElementTest.java b/src/test/java/com/thealgorithms/maths/NonRepeatingElementTest.java new file mode 100644 index 000000000000..5f35cfe68a7a --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/NonRepeatingElementTest.java @@ -0,0 +1,30 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class NonRepeatingElementTest { + + private record TestData(int[] input, int[] expected) { + } + + private static Stream<TestData> provideTestCases() { + return Stream.of(new TestData(new int[] {1, 2, 1, 3, 2, 4}, new int[] {3, 4}), new TestData(new int[] {-1, -2, -1, -3, -2, -4}, new int[] {-3, -4}), new TestData(new int[] {-1, 2, 2, -3, -1, 4}, new int[] {-3, 4})); + } + + @ParameterizedTest + @MethodSource("provideTestCases") + void testFindNonRepeatingElements(TestData testData) { + int[] result = NonRepeatingElement.findNonRepeatingElements(testData.input); + assertArrayEquals(testData.expected, result); + } + + @Test + public void testFindNonRepeatingElementsWithLargeNumbers() { + assertArrayEquals(new int[] {200000, 400000}, NonRepeatingElement.findNonRepeatingElements(new int[] {100000, 200000, 100000, 300000, 400000, 300000})); + } +} diff --git a/src/test/java/com/thealgorithms/maths/NthUglyNumberTest.java b/src/test/java/com/thealgorithms/maths/NthUglyNumberTest.java new file mode 100644 index 000000000000..3fe58dadf8a5 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/NthUglyNumberTest.java @@ -0,0 +1,79 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.HashMap; +import org.junit.jupiter.api.Test; + +public class NthUglyNumberTest { + @Test + public void testGetWithNewObject() { + HashMap<Integer, Long> testCases = new HashMap<>(); + testCases.put(0, 1L); + testCases.put(1, 2L); + testCases.put(2, 3L); + testCases.put(3, 4L); + testCases.put(4, 5L); + testCases.put(5, 6L); + testCases.put(9, 12L); + testCases.put(19, 36L); + testCases.put(52, 270L); + testCases.put(1078, 84934656L); + testCases.put(1963, 6973568802L); + + for (final var tc : testCases.entrySet()) { + var uglyNumbers = new NthUglyNumber(new int[] {2, 3, 5}); + assertEquals(uglyNumbers.get(tc.getKey()), tc.getValue()); + + var otherUglyNumbers = new NthUglyNumber(new int[] {5, 25, 6, 2, 3, 5}); + assertEquals(otherUglyNumbers.get(tc.getKey()), tc.getValue()); + } + } + + @Test + public void testGetWithSameObject() { + HashMap<Integer, Long> testCases = new HashMap<>(); + testCases.put(0, 1L); + testCases.put(1, 2L); + testCases.put(2, 3L); + testCases.put(3, 4L); + testCases.put(4, 5L); + testCases.put(5, 6L); + testCases.put(6, 7L); + testCases.put(1499, 1984500L); + testCases.put(1572, 2449440L); + testCases.put(1658, 3072000L); + testCases.put(6625, 4300800000L); + + var uglyNumbers = new NthUglyNumber(new int[] {7, 2, 5, 3}); + for (final var tc : testCases.entrySet()) { + assertEquals(uglyNumbers.get(tc.getKey()), tc.getValue()); + } + + assertEquals(uglyNumbers.get(999), 385875); + } + + @Test + public void testGetWithBase1() { + var uglyNumbers = new NthUglyNumber(new int[] {1}); + assertEquals(uglyNumbers.get(10), 1); + } + + @Test + public void testGetWithBase2() { + var uglyNumbers = new NthUglyNumber(new int[] {2}); + assertEquals(uglyNumbers.get(5), 32); + } + + @Test + public void testGetThrowsAnErrorForNegativeInput() { + var uglyNumbers = new NthUglyNumber(new int[] {1, 2}); + assertThrows(IllegalArgumentException.class, () -> uglyNumbers.get(-1)); + } + + @Test + public void testConstructorThrowsAnErrorForEmptyInput() { + assertThrows(IllegalArgumentException.class, () -> new NthUglyNumber(new int[] {})); + } +} diff --git a/src/test/java/com/thealgorithms/maths/NumberOfDigitsTest.java b/src/test/java/com/thealgorithms/maths/NumberOfDigitsTest.java new file mode 100644 index 000000000000..799052b22d83 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/NumberOfDigitsTest.java @@ -0,0 +1,39 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.function.IntFunction; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class NumberOfDigitsTest { + + @ParameterizedTest + @MethodSource("testCases") + void testNumberOfDigits(final int expected, final int number, final IntFunction<Integer> methodUnderTest) { + assertEquals(expected, methodUnderTest.apply(number)); + assertEquals(expected, methodUnderTest.apply(-number)); + } + + private static Stream<Arguments> testCases() { + final Integer[][] inputs = new Integer[][] { + {3, 100}, + {1, 0}, + {2, 12}, + {3, 123}, + {4, 1234}, + {5, 12345}, + {6, 123456}, + {7, 1234567}, + {8, 12345678}, + {9, 123456789}, + {9, 987654321}, + }; + + final IntFunction<Integer>[] methods = new IntFunction[] {NumberOfDigits::numberOfDigits, NumberOfDigits::numberOfDigitsFast, NumberOfDigits::numberOfDigitsFaster, NumberOfDigits::numberOfDigitsRecursion}; + + return Stream.of(inputs).flatMap(input -> Stream.of(methods).map(method -> Arguments.of(input[0], input[1], method))); + } +} diff --git a/src/test/java/com/thealgorithms/maths/PalindromeNumberTest.java b/src/test/java/com/thealgorithms/maths/PalindromeNumberTest.java new file mode 100644 index 000000000000..a70100c0b913 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/PalindromeNumberTest.java @@ -0,0 +1,30 @@ +package com.thealgorithms.maths; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * @author Albina Gimaletdinova on 01/07/2023 + */ +public class PalindromeNumberTest { + @Test + public void testNumbersArePalindromes() { + Assertions.assertTrue(PalindromeNumber.isPalindrome(0)); + Assertions.assertTrue(PalindromeNumber.isPalindrome(1)); + Assertions.assertTrue(PalindromeNumber.isPalindrome(2332)); + Assertions.assertTrue(PalindromeNumber.isPalindrome(12321)); + } + + @Test + public void testNumbersAreNotPalindromes() { + Assertions.assertFalse(PalindromeNumber.isPalindrome(12)); + Assertions.assertFalse(PalindromeNumber.isPalindrome(990)); + Assertions.assertFalse(PalindromeNumber.isPalindrome(1234)); + } + + @Test + public void testIfNegativeInputThenExceptionExpected() { + IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> PalindromeNumber.isPalindrome(-1)); + Assertions.assertEquals(exception.getMessage(), "Input parameter must not be negative!"); + } +} diff --git a/src/test/java/com/thealgorithms/maths/ParseIntegerTest.java b/src/test/java/com/thealgorithms/maths/ParseIntegerTest.java new file mode 100644 index 000000000000..7649e21eb231 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/ParseIntegerTest.java @@ -0,0 +1,51 @@ +package com.thealgorithms.maths; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * @author Albina Gimaletdinova on 01/07/2023 + */ +public class ParseIntegerTest { + private static final String NULL_PARAMETER_MESSAGE = "Input parameter must not be null!"; + private static final String EMPTY_PARAMETER_MESSAGE = "Input parameter must not be empty!"; + private static final String INCORRECT_FORMAT_MESSAGE = "Input parameter of incorrect format"; + + @Test + public void testNullInput() { + IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> ParseInteger.parseInt(null)); + Assertions.assertEquals(exception.getMessage(), NULL_PARAMETER_MESSAGE); + } + + @Test + public void testEmptyInput() { + IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> ParseInteger.parseInt("")); + Assertions.assertEquals(exception.getMessage(), EMPTY_PARAMETER_MESSAGE); + } + + @Test + public void testInputOfIncorrectFormat() { + IllegalArgumentException exception = Assertions.assertThrows(NumberFormatException.class, () -> ParseInteger.parseInt("+0a123")); + Assertions.assertTrue(exception.getMessage().contains(INCORRECT_FORMAT_MESSAGE)); + + exception = Assertions.assertThrows(NumberFormatException.class, () -> ParseInteger.parseInt("b")); + Assertions.assertTrue(exception.getMessage().contains(INCORRECT_FORMAT_MESSAGE)); + } + + @Test + public void testPositiveValueIsSuccessfullyConverted() { + Assertions.assertEquals(ParseInteger.parseInt("0"), Integer.parseInt("0")); + Assertions.assertEquals(ParseInteger.parseInt("123"), Integer.parseInt("123")); + Assertions.assertEquals(ParseInteger.parseInt("0123"), Integer.parseInt("0123")); + Assertions.assertEquals(ParseInteger.parseInt("+0123"), Integer.parseInt("+0123")); + Assertions.assertEquals(ParseInteger.parseInt("+123"), Integer.parseInt("+123")); + } + + @Test + public void testNegativeValueIsSuccessfullyConverted() { + Assertions.assertEquals(ParseInteger.parseInt("-1"), Integer.parseInt("-1")); + Assertions.assertEquals(ParseInteger.parseInt("-123"), Integer.parseInt("-123")); + Assertions.assertEquals(ParseInteger.parseInt("-0123"), Integer.parseInt("-0123")); + Assertions.assertEquals(ParseInteger.parseInt("-00123"), Integer.parseInt("-00123")); + } +} diff --git a/src/test/java/com/thealgorithms/maths/PascalTriangleTest.java b/src/test/java/com/thealgorithms/maths/PascalTriangleTest.java new file mode 100644 index 000000000000..a1512aacb30d --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/PascalTriangleTest.java @@ -0,0 +1,51 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +class PascalTriangleTest { + + @Test + void testForOne() { + int[][] result = PascalTriangle.pascal(1); + int[][] expected = {{1}}; + assertArrayEquals(result, expected); + } + + @Test + void testForTwo() { + int[][] result = PascalTriangle.pascal(2); + int[][] expected = {{1, 0}, {1, 1}}; + assertArrayEquals(result, expected); + } + + @Test + void testForFive() { + int[][] result = PascalTriangle.pascal(5); + int[][] expected = { + {1, 0, 0, 0, 0}, + {1, 1, 0, 0, 0}, + {1, 2, 1, 0, 0}, + {1, 3, 3, 1, 0}, + {1, 4, 6, 4, 1}, + }; + assertArrayEquals(result, expected); + } + + @Test + void testForEight() { + int[][] result = PascalTriangle.pascal(8); + int[][] expected = { + {1, 0, 0, 0, 0, 0, 0, 0}, + {1, 1, 0, 0, 0, 0, 0, 0}, + {1, 2, 1, 0, 0, 0, 0, 0}, + {1, 3, 3, 1, 0, 0, 0, 0}, + {1, 4, 6, 4, 1, 0, 0, 0}, + {1, 5, 10, 10, 5, 1, 0, 0}, + {1, 6, 15, 20, 15, 6, 1, 0}, + {1, 7, 21, 35, 35, 21, 7, 1}, + }; + assertArrayEquals(expected, result); + } +} diff --git a/src/test/java/com/thealgorithms/maths/PerfectCubeTest.java b/src/test/java/com/thealgorithms/maths/PerfectCubeTest.java new file mode 100644 index 000000000000..806f5493918c --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/PerfectCubeTest.java @@ -0,0 +1,33 @@ +package com.thealgorithms.maths; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class PerfectCubeTest { + + @Test + public void perfectCube() { + + Assertions.assertTrue(PerfectCube.isPerfectCube(-27)); + Assertions.assertTrue(PerfectCube.isPerfectCubeMathCbrt(-27)); + Assertions.assertTrue(PerfectCube.isPerfectCube(-1)); + Assertions.assertTrue(PerfectCube.isPerfectCubeMathCbrt(-1)); + Assertions.assertTrue(PerfectCube.isPerfectCube(0)); + Assertions.assertTrue(PerfectCube.isPerfectCubeMathCbrt(0)); + Assertions.assertTrue(PerfectCube.isPerfectCube(1)); + Assertions.assertTrue(PerfectCube.isPerfectCubeMathCbrt(1)); + Assertions.assertTrue(PerfectCube.isPerfectCube(8)); + Assertions.assertTrue(PerfectCube.isPerfectCubeMathCbrt(8)); + Assertions.assertTrue(PerfectCube.isPerfectCube(27)); + Assertions.assertTrue(PerfectCube.isPerfectCubeMathCbrt(27)); + + Assertions.assertFalse(PerfectCube.isPerfectCube(-9)); + Assertions.assertFalse(PerfectCube.isPerfectCubeMathCbrt(-9)); + Assertions.assertFalse(PerfectCube.isPerfectCube(2)); + Assertions.assertFalse(PerfectCube.isPerfectCubeMathCbrt(2)); + Assertions.assertFalse(PerfectCube.isPerfectCube(4)); + Assertions.assertFalse(PerfectCube.isPerfectCubeMathCbrt(4)); + Assertions.assertFalse(PerfectCube.isPerfectCube(30)); + Assertions.assertFalse(PerfectCube.isPerfectCubeMathCbrt(30)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/PerfectNumberTest.java b/src/test/java/com/thealgorithms/maths/PerfectNumberTest.java new file mode 100644 index 000000000000..0433ba80cfa4 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/PerfectNumberTest.java @@ -0,0 +1,23 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class PerfectNumberTest { + + @Test + public void perfectNumber() { + int[] trueTestCases = {6, 28, 496, 8128, 33550336}; + int[] falseTestCases = {-6, 0, 1, 9, 123}; + for (Integer n : trueTestCases) { + assertTrue(PerfectNumber.isPerfectNumber(n)); + assertTrue(PerfectNumber.isPerfectNumber2(n)); + } + for (Integer n : falseTestCases) { + assertFalse(PerfectNumber.isPerfectNumber(n)); + assertFalse(PerfectNumber.isPerfectNumber2(n)); + } + } +} diff --git a/src/test/java/com/thealgorithms/maths/PerfectSquareTest.java b/src/test/java/com/thealgorithms/maths/PerfectSquareTest.java new file mode 100644 index 000000000000..2bda5bfd6cf8 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/PerfectSquareTest.java @@ -0,0 +1,21 @@ +package com.thealgorithms.maths; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +public class PerfectSquareTest { + @ParameterizedTest + @ValueSource(ints = {0, 1, 2 * 2, 3 * 3, 4 * 4, 5 * 5, 6 * 6, 7 * 7, 8 * 8, 9 * 9, 10 * 10, 11 * 11, 123 * 123}) + void positiveTest(final int number) { + Assertions.assertTrue(PerfectSquare.isPerfectSquare(number)); + Assertions.assertTrue(PerfectSquare.isPerfectSquareUsingPow(number)); + } + + @ParameterizedTest + @ValueSource(ints = {-1, -2, -3, -4, -5, -100, 2, 3, 5, 6, 7, 8, 10, 11, 12, 13, 15, 17, 99, 101, 257, 999, 1001}) + void negativeTest(final int number) { + Assertions.assertFalse(PerfectSquare.isPerfectSquare(number)); + Assertions.assertFalse(PerfectSquare.isPerfectSquareUsingPow(number)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/PerimeterTest.java b/src/test/java/com/thealgorithms/maths/PerimeterTest.java new file mode 100644 index 000000000000..94d3d3ae0577 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/PerimeterTest.java @@ -0,0 +1,51 @@ +package com.thealgorithms.maths; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class PerimeterTest { + + // Perimeter of Regular polygon + @Test + void testcase1() { + Assertions.assertEquals(20.0, Perimeter.perimeterRegularPolygon(4, 5)); + } + + @Test + void testcase2() { + Assertions.assertEquals(30.0, Perimeter.perimeterRegularPolygon(5, 6)); + } + + // Perimeter of Rectangle + @Test + void testcase3() { + Assertions.assertEquals(18.0, Perimeter.perimeterRectangle(4, 5)); + } + + @Test + void testcase4() { + Assertions.assertEquals(14.0, Perimeter.perimeterRectangle(4, 3)); + } + + // Circumference/Perimeter of a circle + @Test + void testcase5() { + Assertions.assertEquals(31.41592653589793, Perimeter.perimeterCircle(5)); + } + + @Test + void testcase6() { + Assertions.assertEquals(43.982297150257104, Perimeter.perimeterCircle(7)); + } + + // Perimeter of Irregular polygon + @Test + void testcase7() { + Assertions.assertEquals(12.0, Perimeter.perimeterIrregularPolygon(4, 5, 3)); + } + + @Test + void testcase8() { + Assertions.assertEquals(21.0, Perimeter.perimeterIrregularPolygon(3, 4, 5, 3, 6)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/PollardRhoTest.java b/src/test/java/com/thealgorithms/maths/PollardRhoTest.java new file mode 100644 index 000000000000..3e2270a66a98 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/PollardRhoTest.java @@ -0,0 +1,49 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class PollardRhoTest { + + @Test + void testPollardRhoForNumber315MustReturn5() { + // given + int number = 315; + int expectedResult = 5; + + // when + int actualResult = PollardRho.pollardRho(number); + + // then + assertEquals(expectedResult, actualResult); + } + + @Test + void testPollardRhoForNumber187MustReturn11() { + // given + int number = 187; + int expectedResult = 11; + + // when + int actualResult = PollardRho.pollardRho(number); + + // then + assertEquals(expectedResult, actualResult); + } + + @Test + void testPollardRhoForNumber239MustThrowException() { + // given + int number = 239; + String expectedMessage = "GCD cannot be found."; + + // when + Exception exception = assertThrows(RuntimeException.class, () -> { PollardRho.pollardRho(number); }); + String actualMessage = exception.getMessage(); + + // then + assertEquals(expectedMessage, actualMessage); + } +} diff --git a/src/test/java/com/thealgorithms/maths/PowTest.java b/src/test/java/com/thealgorithms/maths/PowTest.java new file mode 100644 index 000000000000..4309b0f10ed5 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/PowTest.java @@ -0,0 +1,36 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class PowTest { + @ParameterizedTest + @CsvSource({"2, 0, 1", "0, 2, 0", "2, 10, 1024", "10, 2, 100", "5, 3, 125", "3, 4, 81"}) + void testPow(int base, int exponent, long expected) { + assertEquals(expected, Pow.pow(base, exponent), "Failed for base: " + base + " and exponent: " + exponent); + } + + @Test + void testPowThrowsExceptionForNegativeExponent() { + assertThrows(IllegalArgumentException.class, () -> Pow.pow(2, -1)); + } + + @Test + void testPowHandlesLargeNumbers() { + assertEquals(1048576, Pow.pow(2, 20)); + } + + @Test + void testPowHandlesZeroBase() { + assertEquals(0, Pow.pow(0, 5)); + } + + @Test + void testPowHandlesOneBase() { + assertEquals(1, Pow.pow(1, 100)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/PowerOfTwoOrNotTest.java b/src/test/java/com/thealgorithms/maths/PowerOfTwoOrNotTest.java new file mode 100644 index 000000000000..ac8d2be17d7c --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/PowerOfTwoOrNotTest.java @@ -0,0 +1,24 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class PowerOfTwoOrNotTest { + @Test + public void testPowerOfTwoOrNotForPowersOfTwo() { + final var powersOfTwo = new int[] {1, 2, 4, 8, 16, 32, 64}; + for (final var n : powersOfTwo) { + assertTrue(PowerOfTwoOrNot.checkIfPowerOfTwoOrNot(n)); + } + } + + @Test + public void testPowerOfTwoOrNotForNotPowersOfTwo() { + final var notPowersOfTwo = new int[] {-16, -8, -6, -5, -4, -3, -2, -1, 0, 3, 5, 6, 7, 9, 10, 11, 33, 63, 65, 1000, 9999}; + for (final var n : notPowersOfTwo) { + assertFalse(PowerOfTwoOrNot.checkIfPowerOfTwoOrNot(n)); + } + } +} diff --git a/src/test/java/com/thealgorithms/maths/PowerUsingRecursionTest.java b/src/test/java/com/thealgorithms/maths/PowerUsingRecursionTest.java new file mode 100644 index 000000000000..705cc6672818 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/PowerUsingRecursionTest.java @@ -0,0 +1,20 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +/** + * Test case for Power using Recursion + * @author Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi) + */ + +class PowerUsingRecursionTest { + + @Test + void testPowerUsingRecursion() { + assertEquals(32.0, PowerUsingRecursion.power(2.0, 5)); + assertEquals(97.65625, PowerUsingRecursion.power(2.5, 5)); + assertEquals(81, PowerUsingRecursion.power(3, 4)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/PronicNumberTest.java b/src/test/java/com/thealgorithms/maths/PronicNumberTest.java new file mode 100644 index 000000000000..8bf2b1852652 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/PronicNumberTest.java @@ -0,0 +1,22 @@ +package com.thealgorithms.maths; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +public class PronicNumberTest { + + @ParameterizedTest + @ValueSource(ints = {0, 2, 6, 12, 20, 30, 42, 110, 272, 380, 420, 1260, 2550}) + void testForPronicNumber(final int number) { + Assertions.assertTrue(PronicNumber.isPronic(number)); + Assertions.assertTrue(PronicNumber.isPronicNumber(number)); + } + + @ParameterizedTest + @ValueSource(ints = {1, 4, 21, 36, 150, 2500}) + void testForNonPronicNumber(final int number) { + Assertions.assertFalse(PronicNumber.isPronic(number)); + Assertions.assertFalse(PronicNumber.isPronicNumber(number)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/PythagoreanTripleTest.java b/src/test/java/com/thealgorithms/maths/PythagoreanTripleTest.java new file mode 100644 index 000000000000..13ea58155dec --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/PythagoreanTripleTest.java @@ -0,0 +1,22 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class PythagoreanTripleTest { + + @Test + public void testPythagoreanTriple() { + assertTrue(PythagoreanTriple.isPythagTriple(3, 4, 5)); + assertTrue(PythagoreanTriple.isPythagTriple(6, 8, 10)); + assertTrue(PythagoreanTriple.isPythagTriple(9, 12, 15)); + assertTrue(PythagoreanTriple.isPythagTriple(12, 16, 20)); + assertTrue(PythagoreanTriple.isPythagTriple(15, 20, 25)); + assertTrue(PythagoreanTriple.isPythagTriple(18, 24, 30)); + assertFalse(PythagoreanTriple.isPythagTriple(5, 20, 30)); + assertFalse(PythagoreanTriple.isPythagTriple(6, 8, 100)); + assertFalse(PythagoreanTriple.isPythagTriple(-2, -2, 2)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/QuadraticEquationSolverTest.java b/src/test/java/com/thealgorithms/maths/QuadraticEquationSolverTest.java new file mode 100644 index 000000000000..a2046511ddf5 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/QuadraticEquationSolverTest.java @@ -0,0 +1,50 @@ +package com.thealgorithms.maths; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class QuadraticEquationSolverTest { + private final QuadraticEquationSolver quadraticEquationSolver = new QuadraticEquationSolver(); + + @Test + public void testSolveEquationRealRoots() { + // 4.2x^2 + 8x + 1.9 = 0 + double a = 4.2; + double b = 8; + double c = 1.9; + + ComplexNumber[] roots = quadraticEquationSolver.solveEquation(a, b, c); + Assertions.assertEquals(roots.length, 2); + Assertions.assertEquals(roots[0].real, -0.27810465435684306); + Assertions.assertNull(roots[0].imaginary); + Assertions.assertEquals(roots[1].real, -1.6266572504050616); + Assertions.assertNull(roots[1].imaginary); + } + + @Test + public void testSolveEquationEqualRoots() { + // x^2 + 2x + 1 = 0 + double a = 1; + double b = 2; + double c = 1; + + ComplexNumber[] roots = quadraticEquationSolver.solveEquation(a, b, c); + Assertions.assertEquals(roots.length, 1); + Assertions.assertEquals(roots[0].real, -1); + } + + @Test + public void testSolveEquationComplexRoots() { + // 2.3x^2 + 4x + 5.6 = 0 + double a = 2.3; + double b = 4; + double c = 5.6; + + ComplexNumber[] roots = quadraticEquationSolver.solveEquation(a, b, c); + Assertions.assertEquals(roots.length, 2); + Assertions.assertEquals(roots[0].real, -0.8695652173913044); + Assertions.assertEquals(roots[0].imaginary, 1.2956229935435948); + Assertions.assertEquals(roots[1].real, -0.8695652173913044); + Assertions.assertEquals(roots[1].imaginary, -1.2956229935435948); + } +} diff --git a/src/test/java/com/thealgorithms/maths/ReverseNumberTest.java b/src/test/java/com/thealgorithms/maths/ReverseNumberTest.java new file mode 100644 index 000000000000..f9538b9fd829 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/ReverseNumberTest.java @@ -0,0 +1,23 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; + +public class ReverseNumberTest { + + @ParameterizedTest + @CsvSource({"0, 0", "1, 1", "10, 1", "123, 321", "7890, 987"}) + public void testReverseNumber(int input, int expected) { + assertEquals(expected, ReverseNumber.reverseNumber(input)); + } + + @ParameterizedTest + @ValueSource(ints = {-1, -123, -7890}) + public void testReverseNumberThrowsExceptionForNegativeInput(int input) { + assertThrows(IllegalArgumentException.class, () -> ReverseNumber.reverseNumber(input)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/SecondMinMaxTest.java b/src/test/java/com/thealgorithms/maths/SecondMinMaxTest.java new file mode 100644 index 000000000000..c744614e5cfa --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/SecondMinMaxTest.java @@ -0,0 +1,58 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class SecondMinMaxTest { + + private static final String EXP_MSG_ARR_LEN_LESS_2 = "Input array must have length of at least two"; + private static final String EXP_MSG_ARR_SAME_ELE = "Input array should have at least 2 distinct elements"; + + public static class TestCase { + public TestCase(final int[] inInputArray, final int inSecondMin, final int inSecondMax) { + inputArray = inInputArray; + secondMin = inSecondMin; + secondMax = inSecondMax; + } + final int[] inputArray; + final int secondMin; + final int secondMax; + } + + @Test + public void testForEmptyInputArray() { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> SecondMinMax.findSecondMin(new int[] {})); + assertEquals(exception.getMessage(), EXP_MSG_ARR_LEN_LESS_2); + } + + @Test + public void testForArrayWithSingleElement() { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> SecondMinMax.findSecondMax(new int[] {1})); + assertEquals(exception.getMessage(), EXP_MSG_ARR_LEN_LESS_2); + } + + @Test + public void testForArrayWithSameElements() { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> SecondMinMax.findSecondMin(new int[] {1, 1, 1, 1})); + assertEquals(exception.getMessage(), EXP_MSG_ARR_SAME_ELE); + } + + @ParameterizedTest + @MethodSource("inputStream") + void numberTests(final TestCase tc) { + Assertions.assertEquals(tc.secondMax, SecondMinMax.findSecondMax(tc.inputArray)); + Assertions.assertEquals(tc.secondMin, SecondMinMax.findSecondMin(tc.inputArray)); + } + + private static Stream<Arguments> inputStream() { + return Stream.of(Arguments.of(new TestCase(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 2, 9)), Arguments.of(new TestCase(new int[] {5, 4, 5, 5, 5}, 5, 4)), Arguments.of(new TestCase(new int[] {-1, 0}, 0, -1)), + Arguments.of(new TestCase(new int[] {-10, -9, -8, -7, -6, -5, -4, -3, -2, -1}, -9, -2)), Arguments.of(new TestCase(new int[] {3, -2, 3, 9, -4, -4, 8}, -2, 8))); + } +} diff --git a/src/test/java/com/thealgorithms/maths/SieveOfEratosthenesTest.java b/src/test/java/com/thealgorithms/maths/SieveOfEratosthenesTest.java new file mode 100644 index 000000000000..ebbd5df712fc --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/SieveOfEratosthenesTest.java @@ -0,0 +1,46 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class SieveOfEratosthenesTest { + @Test + public void testfFindPrimesTill1() { + assertArrayEquals(new int[] {}, SieveOfEratosthenes.findPrimesTill(1)); + } + + @Test + public void testfFindPrimesTill2() { + assertArrayEquals(new int[] {2}, SieveOfEratosthenes.findPrimesTill(2)); + } + + @Test + public void testfFindPrimesTill4() { + var primesTill4 = new int[] {2, 3}; + assertArrayEquals(primesTill4, SieveOfEratosthenes.findPrimesTill(3)); + assertArrayEquals(primesTill4, SieveOfEratosthenes.findPrimesTill(4)); + } + + @Test + public void testfFindPrimesTill40() { + var primesTill40 = new int[] {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}; + assertArrayEquals(primesTill40, SieveOfEratosthenes.findPrimesTill(37)); + assertArrayEquals(primesTill40, SieveOfEratosthenes.findPrimesTill(38)); + assertArrayEquals(primesTill40, SieveOfEratosthenes.findPrimesTill(39)); + assertArrayEquals(primesTill40, SieveOfEratosthenes.findPrimesTill(40)); + } + + @Test + public void testfFindPrimesTill240() { + var primesTill240 = new int[] {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239}; + assertArrayEquals(primesTill240, SieveOfEratosthenes.findPrimesTill(239)); + assertArrayEquals(primesTill240, SieveOfEratosthenes.findPrimesTill(240)); + } + + @Test + public void testFindPrimesTillThrowsExceptionForNonPositiveInput() { + assertThrows(IllegalArgumentException.class, () -> SieveOfEratosthenes.findPrimesTill(0)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/SolovayStrassenPrimalityTestTest.java b/src/test/java/com/thealgorithms/maths/SolovayStrassenPrimalityTestTest.java new file mode 100644 index 000000000000..18cc35266c8c --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/SolovayStrassenPrimalityTestTest.java @@ -0,0 +1,122 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * Unit tests for the {@link SolovayStrassenPrimalityTest} class. + * This class tests the functionality of the Solovay-Strassen primality test implementation. + */ +class SolovayStrassenPrimalityTestTest { + + private static final int RANDOM_SEED = 123; // Seed for reproducibility + private SolovayStrassenPrimalityTest testInstance; + + /** + * Sets up a new instance of {@link SolovayStrassenPrimalityTest} + * before each test case, using a fixed random seed for consistency. + */ + @BeforeEach + void setUp() { + testInstance = SolovayStrassenPrimalityTest.getSolovayStrassenPrimalityTest(RANDOM_SEED); + } + + /** + * Provides test cases for prime numbers with various values of n and k (iterations). + * + * @return an array of objects containing pairs of n and k values + */ + static Object[][] primeNumbers() { + return new Object[][] {{2, 1}, {3, 1}, {5, 5}, {7, 10}, {11, 20}, {13, 10}, {17, 5}, {19, 1}}; + } + + /** + * Tests known prime numbers with various values of n and k (iterations). + * + * @param n the number to be tested for primality + * @param k the number of iterations to use in the primality test + */ + @ParameterizedTest + @MethodSource("primeNumbers") + void testPrimeNumbersWithDifferentNAndK(int n, int k) { + assertTrue(testInstance.solovayStrassen(n, k), n + " should be prime"); + } + + /** + * Provides test cases for composite numbers with various values of n and k (iterations). + * + * @return an array of objects containing pairs of n and k values + */ + static Object[][] compositeNumbers() { + return new Object[][] {{4, 1}, {6, 5}, {8, 10}, {9, 20}, {10, 1}, {12, 5}, {15, 10}}; + } + + /** + * Tests known composite numbers with various values of n and k (iterations). + * + * @param n the number to be tested for primality + * @param k the number of iterations to use in the primality test + */ + @ParameterizedTest + @MethodSource("compositeNumbers") + void testCompositeNumbersWithDifferentNAndK(int n, int k) { + assertFalse(testInstance.solovayStrassen(n, k), n + " should be composite"); + } + + /** + * Tests edge cases for the primality test. + * This includes negative numbers and small integers (0 and 1). + */ + @Test + void testEdgeCases() { + assertFalse(testInstance.solovayStrassen(-1, 10), "-1 should not be prime"); + assertFalse(testInstance.solovayStrassen(0, 10), "0 should not be prime"); + assertFalse(testInstance.solovayStrassen(1, 10), "1 should not be prime"); + + // Test small primes and composites + assertTrue(testInstance.solovayStrassen(2, 1), "2 is a prime number (single iteration)"); + assertFalse(testInstance.solovayStrassen(9, 1), "9 is a composite number (single iteration)"); + + // Test larger primes and composites + long largePrime = 104729; // Known large prime number + long largeComposite = 104730; // Composite number (even) + + assertTrue(testInstance.solovayStrassen(largePrime, 20), "104729 is a prime number"); + assertFalse(testInstance.solovayStrassen(largeComposite, 20), "104730 is a composite number"); + + // Test very large numbers (may take longer) + long veryLargePrime = 512927357; // Known very large prime number + long veryLargeComposite = 512927358; // Composite number (even) + + assertTrue(testInstance.solovayStrassen(veryLargePrime, 20), Long.MAX_VALUE - 1 + " is likely a prime number."); + + assertFalse(testInstance.solovayStrassen(veryLargeComposite, 20), Long.MAX_VALUE + " is a composite number."); + } + + /** + * Tests the Jacobi symbol calculation directly for known values. + * This verifies that the Jacobi symbol method behaves as expected. + */ + @Test + void testJacobiSymbolCalculation() { + // Jacobi symbol (a/n) where n is odd and positive + int jacobi1 = testInstance.calculateJacobi(6, 11); // Should return -1 + int jacobi2 = testInstance.calculateJacobi(5, 11); // Should return +1 + + assertEquals(-1, jacobi1); + assertEquals(+1, jacobi2); + + // Edge case: Jacobi symbol with even n or non-positive n + int jacobi4 = testInstance.calculateJacobi(5, -11); // Should return 0 (invalid) + int jacobi5 = testInstance.calculateJacobi(5, 0); // Should return 0 (invalid) + + assertEquals(0, jacobi4); + assertEquals(0, jacobi5); + } +} diff --git a/src/test/java/com/thealgorithms/maths/SquareFreeIntegerTest.java b/src/test/java/com/thealgorithms/maths/SquareFreeIntegerTest.java new file mode 100644 index 000000000000..5b35ee7bd9d0 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/SquareFreeIntegerTest.java @@ -0,0 +1,84 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.thealgorithms.maths.Prime.SquareFreeInteger; +import java.util.List; +import org.junit.jupiter.api.Test; + +class SquareFreeIntegerTest { + + @Test + void testIsSquareFreeInteger() { + + // given + List<Integer> listOfSquareFreeIntegers = List.of(1, 2, 3, 5, 6, 7, 10, 11, 13, 14, 15, 17, 19, 21, 22, 23, 26, 29, 30, 31, 33, 34, 35, 37, 38, 39, 41, 42, 43, 46, 47, 51, 53, 55, 57, 58, 59, 61, 62, 65, 66, 67, 69, 70, 71, 73, 74, 77, 78, 79, 82, 83, 85, 86, 87, 89, 91, 93, 94, 95, 97, 101, + 102, 103, 105, 106, 107, 109, 110, 111, 113, 114, 115, 118, 119, 122, 123, 127, 129, 130, 131, 133, 134, 137, 138, 139, 141, 142, 143, 145, 146, 149, 151, 154, 155, 157, 158, 159, 161, 163, 165, 166, 167, 170, 173, 174, 177, 178, 179, 181, 182, 183, 185, 186, 187, 190, 191, 193, 194, + 195, 197, 199, 201, 202, 203, 205, 206, 209, 210, 211, 213, 214, 215, 217, 218, 219, 221, 222, 223, 226, 227, 229, 230, 231, 233, 235, 237, 238, 239, 241, 246, 247, 249, 251, 253, 254, 255, 257, 258, 259, 262, 263, 265, 266, 267, 269, 271, 273, 274, 277, 278, 281, 282, 283, 285, 286, + 287, 290, 291, 293, 295, 298, 299, 301, 302, 303, 305, 307, 309, 310, 311, 313, 314, 317, 318, 319, 321, 322, 323, 326, 327, 329, 330, 331, 334, 335, 337, 339, 341, 345, 346, 347, 349, 353, 354, 355, 357, 358, 359, 362, 365, 366, 367, 370, 371, 373, 374, 377, 379, 381, 382, 383, 385, + 386, 389, 390, 391, 393, 394, 395, 397, 398, 399, 401, 402, 403, 406, 407, 409, 410, 411, 413, 415, 417, 418, 419, 421, 422, 426, 427, 429, 430, 431, 433, 434, 435, 437, 438, 439, 442, 443, 445, 446, 447, 449, 451, 453, 454, 455, 457, 458, 461, 462, 463, 465, 466, 467, 469, 470, 471, + 473, 474, 478, 479, 481, 482, 483, 485, 487, 489, 491, 493, 494, 497, 498, 499, 501, 502, 503, 505, 506, 509, 510, 511, 514, 515, 517, 518, 519, 521, 523, 526, 527, 530, 533, 534, 535, 537, 538, 541, 542, 543, 545, 546, 547, 551, 553, 554, 555, 557, 559, 561, 562, 563, 565, 566, 569, + 570, 571, 573, 574, 577, 579, 581, 582, 583, 586, 587, 589, 590, 591, 593, 595, 597, 598, 599, 601, 602, 606, 607, 609, 610, 611, 613, 614, 615, 617, 618, 619, 622, 623, 626, 627, 629, 631, 633, 634, 635, 638, 641, 642, 643, 645, 646, 647, 649, 651, 653, 654, 655, 658, 659, 661, 662, + 663, 665, 667, 669, 670, 671, 673, 674, 677, 678, 679, 681, 682, 683, 685, 687, 689, 690, 691, 694, 695, 697, 698, 699, 701, 703, 705, 706, 707, 709, 710, 713, 714, 715, 717, 718, 719, 721, 723, 727, 730, 731, 733, 734, 737, 739, 741, 742, 743, 745, 746, 749, 751, 753, 754, 755, 757, + 758, 759, 761, 762, 763, 766, 767, 769, 770, 771, 773, 777, 778, 779, 781, 782, 785, 786, 787, 789, 790, 791, 793, 794, 795, 797, 798, 799, 802, 803, 805, 806, 807, 809, 811, 813, 814, 815, 817, 818, 821, 822, 823, 826, 827, 829, 830, 831, 834, 835, 838, 839, 842, 843, 849, 851, 853, + 854, 857, 858, 859, 861, 862, 863, 865, 866, 869, 870, 871, 874, 877, 878, 879, 881, 883, 885, 886, 887, 889, 890, 893, 894, 895, 897, 898, 899, 901, 902, 903, 905, 906, 907, 910, 911, 913, 914, 915, 917, 919, 921, 922, 923, 926, 929, 930, 933, 934, 935, 937, 938, 939, 941, 942, 943, + 946, 947, 949, 951, 953, 955, 957, 958, 959, 962, 965, 966, 967, 969, 970, 971, 973, 974, 977, 978, 979, 982, 983, 985, 986, 987, 989, 991, 993, 994, 995, 997, 998, 1001, 1002, 1003, 1005, 1006, 1007, 1009, 1010, 1011, 1013, 1015, 1018, 1019, 1021, 1022, 1023, 1027, 1030, 1031, 1033, + 1034, 1037, 1038, 1039, 1041, 1042, 1043, 1045, 1046, 1047, 1049, 1051, 1054, 1055, 1057, 1059, 1061, 1063, 1065, 1066, 1067, 1069, 1070, 1073, 1074, 1077, 1079, 1081, 1082, 1085, 1086, 1087, 1090, 1091, 1093, 1094, 1095, 1097, 1099, 1101, 1102, 1103, 1105, 1106, 1109, 1110, 1111, 1113, + 1114, 1115, 1117, 1118, 1119, 1121, 1122, 1123, 1126, 1129, 1130, 1131, 1133, 1135, 1137, 1138, 1139, 1141, 1142, 1145, 1146, 1147, 1149, 1151, 1153, 1154, 1155, 1157, 1158, 1159, 1162, 1163, 1165, 1166, 1167, 1169, 1171, 1173, 1174, 1177, 1178, 1181, 1182, 1185, 1186, 1187, 1189, 1190, + 1191, 1193, 1194, 1195, 1198, 1199, 1201, 1202, 1203, 1205, 1207, 1209, 1211, 1213, 1214, 1217, 1218, 1219, 1221, 1222, 1223, 1226, 1227, 1229, 1230, 1231, 1234, 1235, 1237, 1238, 1239, 1241, 1243, 1245, 1246, 1247, 1249, 1253, 1254, 1255, 1257, 1258, 1259, 1261, 1262, 1263, 1265, 1266, + 1267, 1270, 1271, 1273, 1277, 1279, 1281, 1282, 1283, 1285, 1286, 1289, 1290, 1291, 1293, 1294, 1295, 1297, 1298, 1299, 1301, 1302, 1303, 1306, 1307, 1309, 1310, 1311, 1313, 1315, 1317, 1318, 1319, 1321, 1322, 1326, 1327, 1329, 1330, 1333, 1334, 1335, 1337, 1338, 1339, 1342, 1343, 1345, + 1346, 1347, 1349, 1351, 1353, 1354, 1355, 1357, 1358, 1361, 1362, 1363, 1365, 1366, 1367, 1370, 1371, 1373, 1374, 1378, 1379, 1381, 1382, 1383, 1385, 1387, 1389, 1390, 1391, 1393, 1394, 1397, 1398, 1399, 1401, 1402, 1403, 1405, 1406, 1407, 1409, 1410, 1411, 1414, 1415, 1417, 1418, 1419, + 1423, 1426, 1427, 1429, 1430, 1433, 1434, 1435, 1437, 1438, 1439, 1441, 1442, 1443, 1446, 1447, 1451, 1453, 1454, 1455, 1457, 1459, 1461, 1462, 1463, 1465, 1466, 1469, 1471, 1473, 1474, 1477, 1478, 1479, 1481, 1482, 1483, 1486, 1487, 1489, 1490, 1491, 1493, 1495, 1497, 1498, 1499, 1501, + 1502, 1505, 1506, 1507, 1509, 1510, 1511, 1513, 1514, 1515, 1517, 1518, 1522, 1523, 1526, 1527, 1529, 1531, 1533, 1534, 1535, 1537, 1538, 1541, 1542, 1543, 1545, 1546, 1547, 1549, 1551, 1553, 1554, 1555, 1558, 1559, 1561, 1562, 1563, 1565, 1567, 1569, 1570, 1571, 1574, 1577, 1578, 1579, + 1581, 1582, 1583, 1585, 1586, 1589, 1590, 1591, 1594, 1595, 1597, 1598, 1599, 1601, 1603, 1605, 1606, 1607, 1609, 1610, 1613, 1614, 1615, 1618, 1619, 1621, 1622, 1623, 1626, 1627, 1630, 1631, 1633, 1634, 1635, 1637, 1639, 1641, 1642, 1643, 1645, 1646, 1649, 1651, 1653, 1654, 1655, 1657, + 1658, 1659, 1661, 1662, 1663, 1667, 1669, 1670, 1671, 1673, 1677, 1678, 1679, 1685, 1686, 1687, 1689, 1691, 1693, 1695, 1697, 1698, 1699, 1702, 1703, 1705, 1706, 1707, 1709, 1711, 1713, 1714, 1717, 1718, 1721, 1722, 1723, 1726, 1727, 1729, 1730, 1731, 1733, 1735, 1738, 1739, 1741, 1742, + 1743, 1745, 1747, 1749, 1751, 1753, 1754, 1757, 1758, 1759, 1761, 1762, 1763, 1765, 1766, 1767, 1769, 1770, 1771, 1774, 1777, 1778, 1779, 1781, 1783, 1785, 1786, 1787, 1789, 1790, 1793, 1794, 1795, 1797, 1798, 1799, 1801, 1802, 1803, 1806, 1807, 1810, 1811, 1814, 1817, 1819, 1821, 1822, + 1823, 1826, 1829, 1830, 1831, 1833, 1834, 1835, 1837, 1838, 1839, 1841, 1842, 1843, 1846, 1847, 1851, 1853, 1855, 1857, 1858, 1861, 1865, 1866, 1867, 1869, 1870, 1871, 1873, 1874, 1877, 1878, 1879, 1882, 1883, 1885, 1886, 1887, 1889, 1891, 1893, 1894, 1895, 1897, 1898, 1901, 1902, 1903, + 1905, 1906, 1907, 1909, 1910, 1913, 1914, 1915, 1918, 1919, 1921, 1923, 1927, 1929, 1930, 1931, 1933, 1934, 1937, 1938, 1939, 1941, 1942, 1943, 1945, 1946, 1947, 1949, 1951, 1954, 1955, 1957, 1958, 1959, 1961, 1963, 1965, 1966, 1967, 1969, 1970, 1973, 1974, 1977, 1978, 1979, 1981, 1982, + 1983, 1985, 1986, 1987, 1990, 1991, 1993, 1994, 1995, 1997, 1999, 2001, 2002, 2003, 2005, 2006, 2010, 2011, 2013, 2014, 2015, 2017, 2018, 2019, 2021, 2022, 2026, 2027, 2029, 2030, 2031, 2033, 2035, 2037, 2038, 2039, 2041, 2042, 2045, 2046, 2047, 2049, 2051, 2053, 2054, 2055, 2059, 2062, + 2063, 2065, 2066, 2067, 2069, 2071, 2073, 2074, 2077, 2078, 2081, 2082, 2083, 2085, 2086, 2087, 2089, 2090, 2091, 2093, 2094, 2095, 2098, 2099, 2101, 2102, 2103, 2105, 2109, 2110, 2111, 2113, 2114, 2117, 2118, 2119, 2121, 2122, 2123, 2126, 2127, 2129, 2130, 2131, 2134, 2135, 2137, 2138, + 2139, 2141, 2143, 2145, 2146, 2147, 2149, 2153, 2154, 2155, 2157, 2158, 2159, 2161, 2162, 2163, 2165, 2167, 2170, 2171, 2173, 2174, 2177, 2179, 2181, 2182, 2183, 2185, 2186, 2189, 2190, 2191, 2193, 2194, 2195, 2198, 2199, 2201, 2202, 2203, 2206, 2207, 2210, 2211, 2213, 2215, 2217, 2218, + 2219, 2221, 2222, 2226, 2227, 2229, 2230, 2231, 2233, 2234, 2235, 2237, 2238, 2239, 2242, 2243, 2245, 2246, 2247, 2249, 2251, 2253, 2255, 2257, 2258, 2261, 2262, 2263, 2265, 2266, 2267, 2269, 2270, 2271, 2273, 2274, 2278, 2279, 2281, 2282, 2283, 2285, 2287, 2289, 2290, 2291, 2293, 2294, + 2297, 2298, 2301, 2302, 2305, 2306, 2307, 2309, 2310, 2311, 2314, 2315, 2317, 2318, 2319, 2321, 2323, 2326, 2327, 2329, 2330, 2333, 2334, 2335, 2337, 2338, 2339, 2341, 2342, 2343, 2345, 2346, 2347, 2351, 2353, 2354, 2355, 2357, 2359, 2361, 2362, 2363, 2365, 2369, 2370, 2371, 2373, 2374, + 2377, 2378, 2379, 2381, 2382, 2383, 2386, 2387, 2389, 2390, 2391, 2393, 2395, 2397, 2398, 2399, 2402, 2405, 2406, 2407, 2409, 2410, 2411, 2413, 2414, 2415, 2417, 2418, 2419, 2422, 2423, 2426, 2427, 2429, 2431, 2433, 2434, 2435, 2437, 2438, 2441, 2442, 2443, 2445, 2446, 2447, 2449, 2451, + 2453, 2454, 2455, 2458, 2459, 2461, 2462, 2463, 2465, 2467, 2469, 2470, 2471, 2473, 2474, 2477, 2478, 2479, 2481, 2482, 2483, 2485, 2486, 2487, 2489, 2490, 2491, 2494, 2495, 2497, 2498); + + for (int i = 1; i <= 2500; i++) { + // when + boolean isNumberSquareFree = SquareFreeInteger.isSquareFreeInteger(i); + boolean isNumberPresentInList = listOfSquareFreeIntegers.contains(i); + + // then + assertEquals(isNumberSquareFree, isNumberPresentInList); + } + } + + @Test + void testIsSquareFreeIntegerThrowExceptionIfNumberIsZero() { + // given + int number = 0; + String expectedMessage = "Number must be greater than zero."; + + // when + Exception exception = assertThrows(IllegalArgumentException.class, () -> { SquareFreeInteger.isSquareFreeInteger(number); }); + String actualMessage = exception.getMessage(); + + // then + assertEquals(expectedMessage, actualMessage); + } + + @Test + void testIsSquareFreeIntegerMustThrowExceptionIfNumberIsNegative() { + // given + int number = -1; + String expectedMessage = "Number must be greater than zero."; + + // when + Exception exception = assertThrows(IllegalArgumentException.class, () -> { SquareFreeInteger.isSquareFreeInteger(number); }); + String actualMessage = exception.getMessage(); + + // then + assertEquals(expectedMessage, actualMessage); + } +} diff --git a/src/test/java/com/thealgorithms/maths/SquareRootWithNewtonRaphsonTestMethod.java b/src/test/java/com/thealgorithms/maths/SquareRootWithNewtonRaphsonTestMethod.java new file mode 100644 index 000000000000..067b68962bdb --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/SquareRootWithNewtonRaphsonTestMethod.java @@ -0,0 +1,22 @@ +package com.thealgorithms.maths; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class SquareRootWithNewtonRaphsonTestMethod { + + @Test + void testfor1() { + Assertions.assertEquals(1, SquareRootWithNewtonRaphsonMethod.squareRoot(1)); + } + + @Test + void testfor2() { + Assertions.assertEquals(1.414213562373095, SquareRootWithNewtonRaphsonMethod.squareRoot(2)); + } + + @Test + void testfor625() { + Assertions.assertEquals(25.0, SquareRootWithNewtonRaphsonMethod.squareRoot(625)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/SquareRootwithBabylonianMethodTest.java b/src/test/java/com/thealgorithms/maths/SquareRootwithBabylonianMethodTest.java new file mode 100644 index 000000000000..77446d30df32 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/SquareRootwithBabylonianMethodTest.java @@ -0,0 +1,27 @@ +package com.thealgorithms.maths; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class SquareRootwithBabylonianMethodTest { + + @Test + void testfor4() { + Assertions.assertEquals(2, SquareRootWithBabylonianMethod.squareRoot(4)); + } + + @Test + void testfor1() { + Assertions.assertEquals(1, SquareRootWithBabylonianMethod.squareRoot(1)); + } + + @Test + void testfor2() { + Assertions.assertEquals(1.4142135381698608, SquareRootWithBabylonianMethod.squareRoot(2)); + } + + @Test + void testfor625() { + Assertions.assertEquals(25, SquareRootWithBabylonianMethod.squareRoot(625)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/StandardDeviationTest.java b/src/test/java/com/thealgorithms/maths/StandardDeviationTest.java new file mode 100644 index 000000000000..2c10d2d14f3e --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/StandardDeviationTest.java @@ -0,0 +1,37 @@ +package com.thealgorithms.maths; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class StandardDeviationTest { + + @Test + void test1() { + double[] t1 = new double[] {1, 1, 1, 1, 1}; + Assertions.assertEquals(StandardDeviation.stdDev(t1), 0.0); + } + + @Test + void test2() { + double[] t2 = new double[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + Assertions.assertEquals(StandardDeviation.stdDev(t2), 2.8722813232690143); + } + + @Test + void test3() { + double[] t3 = new double[] {1.1, 8.5, 20.3, 2.4, 6.2}; + Assertions.assertEquals(StandardDeviation.stdDev(t3), 6.8308125431752265); + } + + @Test + void test4() { + double[] t4 = new double[] { + 3.14, + 2.22222, + 9.89898989, + 100.00045, + 56.7, + }; + Assertions.assertEquals(StandardDeviation.stdDev(t4), 38.506117353865775); + } +} diff --git a/src/test/java/com/thealgorithms/maths/StandardScoreTest.java b/src/test/java/com/thealgorithms/maths/StandardScoreTest.java new file mode 100644 index 000000000000..436b1fd011c6 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/StandardScoreTest.java @@ -0,0 +1,27 @@ +package com.thealgorithms.maths; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class StandardScoreTest { + + @Test + void test1() { + Assertions.assertEquals(StandardScore.zScore(2, 0, 5), 0.4); + } + + @Test + void test2() { + Assertions.assertEquals(StandardScore.zScore(1, 1, 1), 0.0); + } + + @Test + void test3() { + Assertions.assertEquals(StandardScore.zScore(2.5, 1.8, 0.7), 1.0); + } + + @Test + void test4() { + Assertions.assertEquals(StandardScore.zScore(8.9, 3, 4.2), 1.4047619047619049); + } +} diff --git a/src/test/java/com/thealgorithms/maths/StrobogrammaticNumberTest.java b/src/test/java/com/thealgorithms/maths/StrobogrammaticNumberTest.java new file mode 100644 index 000000000000..2269291e06c1 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/StrobogrammaticNumberTest.java @@ -0,0 +1,19 @@ +package com.thealgorithms.maths; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class StrobogrammaticNumberTest { + + @Test + void testIsStrobogrammatic() { + StrobogrammaticNumber strobogrammaticNumber = new StrobogrammaticNumber(); + assertThat(strobogrammaticNumber.isStrobogrammatic("69")).isTrue(); + assertThat(strobogrammaticNumber.isStrobogrammatic("88")).isTrue(); + assertThat(strobogrammaticNumber.isStrobogrammatic("818")).isTrue(); + assertThat(strobogrammaticNumber.isStrobogrammatic("101")).isTrue(); + assertThat(strobogrammaticNumber.isStrobogrammatic("609")).isTrue(); + assertThat(strobogrammaticNumber.isStrobogrammatic("120")).isFalse(); + } +} diff --git a/src/test/java/com/thealgorithms/maths/SumOfArithmeticSeriesTest.java b/src/test/java/com/thealgorithms/maths/SumOfArithmeticSeriesTest.java new file mode 100644 index 000000000000..527cef2c187d --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/SumOfArithmeticSeriesTest.java @@ -0,0 +1,53 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +public class SumOfArithmeticSeriesTest { + @Test + public void testSumFrom1To10() { + assertEquals(55.0, SumOfArithmeticSeries.sumOfSeries(1.0, 1.0, 10)); + } + + @Test + public void testSumOfOddNumbers1To19() { + assertEquals(100.0, SumOfArithmeticSeries.sumOfSeries(1.0, 2.0, 10)); + } + + @Test + public void testA() { + assertEquals(460.0, SumOfArithmeticSeries.sumOfSeries(1.0, 10.0, 10)); + } + + @Test + public void testB() { + assertEquals(5.5, SumOfArithmeticSeries.sumOfSeries(0.1, 0.1, 10)); + } + + @Test + public void testC() { + assertEquals(49600.0, SumOfArithmeticSeries.sumOfSeries(1, 10, 100)); + } + + @Test + public void testForZeroTerms() { + assertEquals(0.0, SumOfArithmeticSeries.sumOfSeries(1.0, 100.0, 0), 0.00001); + } + + @Test + public void testIfThrowsExceptionForNegativeNumberOfTerms() { + assertThrows(IllegalArgumentException.class, () -> SumOfArithmeticSeries.sumOfSeries(1.0, 1.0, -1)); + } + + @Test + public void testWithSingleTerm() { + assertEquals(123.0, SumOfArithmeticSeries.sumOfSeries(123.0, 5.0, 1)); + } + + @Test + public void testWithZeroCommonDiff() { + assertEquals(100.0, SumOfArithmeticSeries.sumOfSeries(1.0, 0.0, 100)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/SumOfDigitsTest.java b/src/test/java/com/thealgorithms/maths/SumOfDigitsTest.java new file mode 100644 index 000000000000..76aca44a2220 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/SumOfDigitsTest.java @@ -0,0 +1,31 @@ +package com.thealgorithms.maths; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class SumOfDigitsTest { + @ParameterizedTest + @MethodSource("testCases") + void sumOfDigitsTest(final int expected, final int input) { + Assertions.assertEquals(expected, SumOfDigits.sumOfDigits(input)); + } + + @ParameterizedTest + @MethodSource("testCases") + void sumOfDigitsRecursionTest(final int expected, final int input) { + Assertions.assertEquals(expected, SumOfDigits.sumOfDigitsRecursion(input)); + } + + @ParameterizedTest + @MethodSource("testCases") + void sumOfDigitsFastTest(final int expected, final int input) { + Assertions.assertEquals(expected, SumOfDigits.sumOfDigitsFast(input)); + } + + private static Stream<Arguments> testCases() { + return Stream.of(Arguments.of(0, 0), Arguments.of(1, 1), Arguments.of(15, 12345), Arguments.of(6, -123), Arguments.of(1, -100000), Arguments.of(8, 512)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/SumOfOddNumbersTest.java b/src/test/java/com/thealgorithms/maths/SumOfOddNumbersTest.java new file mode 100644 index 000000000000..6470d7983888 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/SumOfOddNumbersTest.java @@ -0,0 +1,28 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class SumOfOddNumbersTest { + + @ParameterizedTest + @MethodSource("inputStream") + void sumOfFirstNOddNumbersTests(int expected, int input) { + Assertions.assertEquals(expected, SumOfOddNumbers.sumOfFirstNOddNumbers(input)); + } + + private static Stream<Arguments> inputStream() { + return Stream.of(Arguments.of(1, 1), Arguments.of(4, 2), Arguments.of(9, 3), Arguments.of(16, 4), Arguments.of(25, 5), Arguments.of(100, 10)); + } + + @Test + public void testSumOfFirstNOddNumbersThrowsExceptionForNegativeInput() { + assertThrows(IllegalArgumentException.class, () -> SumOfOddNumbers.sumOfFirstNOddNumbers(-1)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/SumWithoutArithmeticOperatorsTest.java b/src/test/java/com/thealgorithms/maths/SumWithoutArithmeticOperatorsTest.java new file mode 100644 index 000000000000..1eab73c67642 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/SumWithoutArithmeticOperatorsTest.java @@ -0,0 +1,40 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class SumWithoutArithmeticOperatorsTest { + SumWithoutArithmeticOperators obj = new SumWithoutArithmeticOperators(); + + @Test + void addZerotoZero() { + assertEquals(0, obj.getSum(0, 0)); + } + + @Test + void addZerotoNumber() { + assertEquals(5, obj.getSum(0, 5)); + assertEquals(28, obj.getSum(28, 0)); + } + + @Test + void addOddtoEven() { + assertEquals(13, obj.getSum(3, 10)); + assertEquals(55, obj.getSum(49, 6)); + } + + @Test + void addEventoOdd() { + assertEquals(13, obj.getSum(10, 3)); + assertEquals(41, obj.getSum(40, 1)); + } + + @Test + void addRandoms() { + assertEquals(88, obj.getSum(44, 44)); + assertEquals(370, obj.getSum(100, 270)); + assertEquals(3, obj.getSum(1, 2)); + assertEquals(5, obj.getSum(2, 3)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/TestArmstrong.java b/src/test/java/com/thealgorithms/maths/TestArmstrong.java new file mode 100644 index 000000000000..60f4f2431189 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/TestArmstrong.java @@ -0,0 +1,15 @@ +package com.thealgorithms.maths; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +public class TestArmstrong { + + @Test + public void testArmstrong() { + Armstrong armstrong = new Armstrong(); + assertThat(armstrong.isArmstrong(371)).isTrue(); + assertThat(armstrong.isArmstrong(200)).isFalse(); + } +} diff --git a/src/test/java/com/thealgorithms/maths/TwinPrimeTest.java b/src/test/java/com/thealgorithms/maths/TwinPrimeTest.java new file mode 100644 index 000000000000..e214b0ada347 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/TwinPrimeTest.java @@ -0,0 +1,60 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class TwinPrimeTest { + + @Test + void shouldReturn7() { + // given + int number = 5; + int expectedResult = 7; + + // when + int actualResult = TwinPrime.getTwinPrime(number); + + // then + assertEquals(expectedResult, actualResult); + } + + @Test + void shouldReturn5() { + // given + int number = 3; + int expectedResult = 5; + + // when + int actualResult = TwinPrime.getTwinPrime(number); + + // then + assertEquals(expectedResult, actualResult); + } + + @Test + void shouldReturnNegative1() { + // given + int number = 4; + int expectedResult = -1; + + // when + int actualResult = TwinPrime.getTwinPrime(number); + + // then + assertEquals(expectedResult, actualResult); + } + + @Test + void shouldReturn19() { + // given + int number = 17; + int expectedResult = 19; + + // when + int actualResult = TwinPrime.getTwinPrime(number); + + // then + assertEquals(expectedResult, actualResult); + } +} diff --git a/src/test/java/com/thealgorithms/maths/UniformNumbersTest.java b/src/test/java/com/thealgorithms/maths/UniformNumbersTest.java new file mode 100644 index 000000000000..ac46c00014ad --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/UniformNumbersTest.java @@ -0,0 +1,56 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class UniformNumbersTest { + + @Test + void testSingleUniformDigitRange() { + assertEquals(1, UniformNumbers.countUniformIntegers(1, 1)); + assertEquals(9, UniformNumbers.countUniformIntegers(1, 9)); + } + + @Test + void testSmallRange() { + assertEquals(1, UniformNumbers.countUniformIntegers(10, 11)); + assertEquals(2, UniformNumbers.countUniformIntegers(22, 33)); + } + + @Test + void testRangeWithNoUniformNumbers() { + assertEquals(0, UniformNumbers.countUniformIntegers(12, 21)); + assertEquals(0, UniformNumbers.countUniformIntegers(123, 128)); + } + + @Test + void testRangeWithAllUniformNumbers() { + assertEquals(9, UniformNumbers.countUniformIntegers(1, 9)); + assertEquals(18, UniformNumbers.countUniformIntegers(1, 99)); + } + + @Test + void testMultiDigitRangeWithUniformNumbers() { + assertEquals(1, UniformNumbers.countUniformIntegers(100, 111)); + assertEquals(2, UniformNumbers.countUniformIntegers(111, 222)); + } + + @Test + void testExactUniformBoundary() { + assertEquals(1, UniformNumbers.countUniformIntegers(111, 111)); + assertEquals(2, UniformNumbers.countUniformIntegers(111, 222)); + } + + @Test + void testLargeRange() { + assertEquals(27, UniformNumbers.countUniformIntegers(1, 999)); + assertEquals(36, UniformNumbers.countUniformIntegers(1, 9999)); + } + + @Test + void testInvalidRange() { + assertEquals(0, UniformNumbers.countUniformIntegers(500, 100)); + assertEquals(0, UniformNumbers.countUniformIntegers(-100, -1)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/VampireNumberTest.java b/src/test/java/com/thealgorithms/maths/VampireNumberTest.java new file mode 100644 index 000000000000..6f331f1252cd --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/VampireNumberTest.java @@ -0,0 +1,32 @@ +package com.thealgorithms.maths; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class VampireNumberTest { + @Test + void areVampireNumbers() { + Assertions.assertTrue(VampireNumber.isVampireNumber(15, 93, true)); + Assertions.assertTrue(VampireNumber.isVampireNumber(135, 801, true)); + Assertions.assertTrue(VampireNumber.isVampireNumber(201, 600, true)); + } + + @Test + void arePseudoVampireNumbers() { + Assertions.assertTrue(VampireNumber.isVampireNumber(150, 93, false)); + Assertions.assertTrue(VampireNumber.isVampireNumber(546, 84, false)); + Assertions.assertTrue(VampireNumber.isVampireNumber(641, 65, false)); + } + + @Test + void areNotVampireNumbers() { + Assertions.assertFalse(VampireNumber.isVampireNumber(51, 39, false)); + Assertions.assertFalse(VampireNumber.isVampireNumber(51, 39, true)); + } + + @Test + void testSplitIntoSortedDigits() { + Assertions.assertEquals("123", VampireNumber.splitIntoSortedDigits(321)); + Assertions.assertEquals("02234", VampireNumber.splitIntoSortedDigits(20, 324)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/VolumeTest.java b/src/test/java/com/thealgorithms/maths/VolumeTest.java new file mode 100644 index 000000000000..7cd0c6716147 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/VolumeTest.java @@ -0,0 +1,39 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class VolumeTest { + + @Test + public void volume() { + + /* test cube */ + assertTrue(Volume.volumeCube(7) == 343.0); + + /* test cuboid */ + assertTrue(Volume.volumeCuboid(2, 5, 7) == 70.0); + + /* test sphere */ + assertTrue(Volume.volumeSphere(7) == 1436.7550402417319); + + /* test cylinder */ + assertTrue(Volume.volumeCylinder(3, 7) == 197.92033717615698); + + /* test hemisphere */ + assertTrue(Volume.volumeHemisphere(7) == 718.3775201208659); + + /* test cone */ + assertTrue(Volume.volumeCone(3, 7) == 65.97344572538566); + + /* test prism */ + assertTrue(Volume.volumePrism(10, 2) == 20.0); + + /* test pyramid */ + assertTrue(Volume.volumePyramid(10, 3) == 10.0); + + /* test frustum */ + assertTrue(Volume.volumeFrustumOfCone(3, 5, 7) == 359.188760060433); + } +} diff --git a/src/test/java/com/thealgorithms/maths/prime/LiouvilleLambdaFunctionTest.java b/src/test/java/com/thealgorithms/maths/prime/LiouvilleLambdaFunctionTest.java new file mode 100644 index 000000000000..d32815c0b8a9 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/prime/LiouvilleLambdaFunctionTest.java @@ -0,0 +1,64 @@ +package com.thealgorithms.maths.prime; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.thealgorithms.maths.Prime.LiouvilleLambdaFunction; +import org.junit.jupiter.api.Test; + +class LiouvilleLambdaFunctionTest { + + @Test + void testLiouvilleLambdaMustThrowExceptionIfNumberIsZero() { + // given + int number = 0; + String expectedMessage = "Number must be greater than zero."; + + // when + Exception exception = assertThrows(IllegalArgumentException.class, () -> { LiouvilleLambdaFunction.liouvilleLambda(number); }); + String actualMessage = exception.getMessage(); + + // then + assertEquals(expectedMessage, actualMessage); + } + + @Test + void testLiouvilleLambdaMustThrowExceptionIfNumberIsNegative() { + // given + int number = -1; + String expectedMessage = "Number must be greater than zero."; + + // when + Exception exception = assertThrows(IllegalArgumentException.class, () -> { LiouvilleLambdaFunction.liouvilleLambda(number); }); + String actualMessage = exception.getMessage(); + + // then + assertEquals(expectedMessage, actualMessage); + } + + @Test + void testLiouvilleLambdaMustReturnNegativeOne() { + // given + int number = 11; + int expectedOutput = -1; + + // when + int actualOutput = LiouvilleLambdaFunction.liouvilleLambda(number); + + // then + assertEquals(expectedOutput, actualOutput); + } + + @Test + void testLiouvilleLambdaMustReturnPositiveOne() { + // given + int number = 10; + int expectedOutput = 1; + + // when + int actualOutput = LiouvilleLambdaFunction.liouvilleLambda(number); + + // then + assertEquals(expectedOutput, actualOutput); + } +} diff --git a/src/test/java/com/thealgorithms/maths/prime/MillerRabinPrimalityCheckTest.java b/src/test/java/com/thealgorithms/maths/prime/MillerRabinPrimalityCheckTest.java new file mode 100644 index 000000000000..4defcd587758 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/prime/MillerRabinPrimalityCheckTest.java @@ -0,0 +1,31 @@ +package com.thealgorithms.maths.prime; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.thealgorithms.maths.Prime.MillerRabinPrimalityCheck; +import org.junit.jupiter.api.Test; + +class MillerRabinPrimalityCheckTest { + @Test + void testDeterministicMillerRabinForPrimes() { + assertTrue(MillerRabinPrimalityCheck.deterministicMillerRabin(2)); + assertTrue(MillerRabinPrimalityCheck.deterministicMillerRabin(37)); + assertTrue(MillerRabinPrimalityCheck.deterministicMillerRabin(123457)); + assertTrue(MillerRabinPrimalityCheck.deterministicMillerRabin(6472601713L)); + } + @Test + void testDeterministicMillerRabinForNotPrimes() { + assertFalse(MillerRabinPrimalityCheck.deterministicMillerRabin(1)); + assertFalse(MillerRabinPrimalityCheck.deterministicMillerRabin(35)); + assertFalse(MillerRabinPrimalityCheck.deterministicMillerRabin(123453)); + assertFalse(MillerRabinPrimalityCheck.deterministicMillerRabin(647260175)); + } + @Test + void testMillerRabinForPrimes() { + assertTrue(MillerRabinPrimalityCheck.millerRabin(11, 5)); + assertTrue(MillerRabinPrimalityCheck.millerRabin(97, 5)); + assertTrue(MillerRabinPrimalityCheck.millerRabin(6720589, 5)); + assertTrue(MillerRabinPrimalityCheck.millerRabin(9549401549L, 5)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/prime/MobiusFunctionTest.java b/src/test/java/com/thealgorithms/maths/prime/MobiusFunctionTest.java new file mode 100644 index 000000000000..734d02477ba2 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/prime/MobiusFunctionTest.java @@ -0,0 +1,155 @@ +package com.thealgorithms.maths.prime; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.thealgorithms.maths.Prime.MobiusFunction; +import org.junit.jupiter.api.Test; + +class MobiusFunctionTest { + + @Test + void testMobiusForZero() { + // given + int number = 0; + String expectedMessage = "Number must be greater than zero."; + + // when + Exception exception = assertThrows(IllegalArgumentException.class, () -> { MobiusFunction.mobius(number); }); + String actualMessage = exception.getMessage(); + + // then + assertEquals(expectedMessage, actualMessage); + } + + @Test + void testMobiusForNegativeNumber() { + // given + int number = -1; + String expectedMessage = "Number must be greater than zero."; + + // when + Exception exception = assertThrows(IllegalArgumentException.class, () -> { MobiusFunction.mobius(number); }); + String actualMessage = exception.getMessage(); + + // then + assertEquals(expectedMessage, actualMessage); + } + + @Test + void testMobiusFunction() { + int[] expectedResultArray = { + 1, + -1, + -1, + 0, + -1, + 1, + -1, + 0, + 0, + 1, + -1, + 0, + -1, + 1, + 1, + 0, + -1, + 0, + -1, + 0, + 1, + 1, + -1, + 0, + 0, + 1, + 0, + 0, + -1, + -1, + -1, + 0, + 1, + 1, + 1, + 0, + -1, + 1, + 1, + 0, + -1, + -1, + -1, + 0, + 0, + 1, + -1, + 0, + 0, + 0, + 1, + 0, + -1, + 0, + 1, + 0, + 1, + 1, + -1, + 0, + -1, + 1, + 0, + 0, + 1, + -1, + -1, + 0, + 1, + -1, + -1, + 0, + -1, + 1, + 0, + 0, + 1, + -1, + -1, + 0, + 0, + 1, + -1, + 0, + 1, + 1, + 1, + 0, + -1, + 0, + 1, + 0, + 1, + 1, + 1, + 0, + -1, + 0, + 0, + 0, + }; + + for (int i = 1; i <= 100; i++) { + // given + int expectedValue = expectedResultArray[i - 1]; + + // when + int actualValue = MobiusFunction.mobius(i); + + // then + assertEquals(expectedValue, actualValue); + } + } +} diff --git a/src/test/java/com/thealgorithms/maths/prime/PrimeCheckTest.java b/src/test/java/com/thealgorithms/maths/prime/PrimeCheckTest.java new file mode 100644 index 000000000000..2182bcd9cb16 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/prime/PrimeCheckTest.java @@ -0,0 +1,43 @@ +package com.thealgorithms.maths.prime; + +import com.thealgorithms.maths.Prime.PrimeCheck; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class PrimeCheckTest { + + @Test + void test1() { + Assertions.assertTrue(PrimeCheck.isPrime(2)); + } + + @Test + void test2() { + Assertions.assertFalse(PrimeCheck.isPrime(-1)); + } + + @Test + void test3() { + Assertions.assertFalse(PrimeCheck.isPrime(4)); + } + + @Test + void test4() { + Assertions.assertTrue(PrimeCheck.isPrime(5)); + } + + @Test + void test5() { + Assertions.assertFalse(PrimeCheck.isPrime(15)); + } + + @Test + void test6() { + Assertions.assertTrue(PrimeCheck.isPrime(11)); + } + + @Test + void test7() { + Assertions.assertFalse(PrimeCheck.isPrime(49)); + } +} diff --git a/src/test/java/com/thealgorithms/maths/prime/PrimeFactorizationTest.java b/src/test/java/com/thealgorithms/maths/prime/PrimeFactorizationTest.java new file mode 100644 index 000000000000..79d685726261 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/prime/PrimeFactorizationTest.java @@ -0,0 +1,33 @@ +package com.thealgorithms.maths.prime; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.thealgorithms.maths.Prime.PrimeFactorization; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class PrimeFactorizationTest { + + @ParameterizedTest + @MethodSource("provideNumbersAndFactors") + void testPrimeFactorization(int number, List<Integer> expectedFactors) { + assertEquals(expectedFactors, PrimeFactorization.pfactors(number), "Prime factors for number: " + number); + } + + @ParameterizedTest + @MethodSource("provideNumbersAndSizes") + void testPrimeFactorsSize(int number, int expectedSize) { + assertEquals(expectedSize, PrimeFactorization.pfactors(number).size(), "Size of prime factors list for number: " + number); + } + + private static Stream<Arguments> provideNumbersAndFactors() { + return Stream.of(Arguments.of(0, List.of()), Arguments.of(1, List.of()), Arguments.of(2, List.of(2)), Arguments.of(3, List.of(3)), Arguments.of(4, List.of(2, 2)), Arguments.of(18, List.of(2, 3, 3)), Arguments.of(100, List.of(2, 2, 5, 5)), Arguments.of(198, List.of(2, 3, 3, 11))); + } + + private static Stream<Arguments> provideNumbersAndSizes() { + return Stream.of(Arguments.of(2, 1), Arguments.of(3, 1), Arguments.of(4, 2), Arguments.of(18, 3), Arguments.of(100, 4), Arguments.of(198, 4)); + } +} diff --git a/src/test/java/com/thealgorithms/matrix/InverseOfMatrixTest.java b/src/test/java/com/thealgorithms/matrix/InverseOfMatrixTest.java new file mode 100644 index 000000000000..930fb377cd32 --- /dev/null +++ b/src/test/java/com/thealgorithms/matrix/InverseOfMatrixTest.java @@ -0,0 +1,27 @@ +package com.thealgorithms.matrix; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class InverseOfMatrixTest { + + @ParameterizedTest + @MethodSource("provideTestCases") + void testInvert(double[][] matrix, double[][] expectedInverse) { + double[][] result = InverseOfMatrix.invert(matrix); + assertMatrixEquals(expectedInverse, result); + } + + private static Stream<Arguments> provideTestCases() { + return Stream.of(Arguments.of(new double[][] {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}, new double[][] {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}), Arguments.of(new double[][] {{4, 7}, {2, 6}}, new double[][] {{0.6, -0.7}, {-0.2, 0.4}})); + } + + private void assertMatrixEquals(double[][] expected, double[][] actual) { + for (int i = 0; i < expected.length; i++) { + assertArrayEquals(expected[i], actual[i], 1.0E-10, "Row " + i + " is not equal"); + } + } +} diff --git a/src/test/java/com/thealgorithms/matrix/MatrixRankTest.java b/src/test/java/com/thealgorithms/matrix/MatrixRankTest.java new file mode 100644 index 000000000000..33a0196b7bf7 --- /dev/null +++ b/src/test/java/com/thealgorithms/matrix/MatrixRankTest.java @@ -0,0 +1,45 @@ +package com.thealgorithms.matrix; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.Arrays; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class MatrixRankTest { + + private static Stream<Arguments> validInputStream() { + return Stream.of(Arguments.of(3, new double[][] {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}), Arguments.of(0, new double[][] {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}), Arguments.of(1, new double[][] {{1}}), Arguments.of(2, new double[][] {{1, 2}, {3, 4}}), + Arguments.of(2, new double[][] {{3, -1, 2}, {-3, 1, 2}, {-6, 2, 4}}), Arguments.of(3, new double[][] {{2, 3, 0, 1}, {1, 0, 1, 2}, {-1, 1, 1, -2}, {1, 5, 3, -1}}), Arguments.of(1, new double[][] {{1, 2, 3}, {3, 6, 9}}), + Arguments.of(2, new double[][] {{0.25, 0.5, 0.75, 2}, {1.5, 3, 4.5, 6}, {1, 2, 3, 4}})); + } + + private static Stream<Arguments> invalidInputStream() { + return Stream.of(Arguments.of((Object) new double[][] {{1, 2}, {10}, {100, 200, 300}}), // jagged array + Arguments.of((Object) new double[][] {}), // empty matrix + Arguments.of((Object) new double[][] {{}, {}}), // empty row + Arguments.of((Object) null), // null matrix + Arguments.of((Object) new double[][] {{1, 2}, null}) // null row + ); + } + + @ParameterizedTest + @MethodSource("validInputStream") + void computeRankTests(int expectedRank, double[][] matrix) { + int originalHashCode = Arrays.deepHashCode(matrix); + int rank = MatrixRank.computeRank(matrix); + int newHashCode = Arrays.deepHashCode(matrix); + + assertEquals(expectedRank, rank); + assertEquals(originalHashCode, newHashCode); + } + + @ParameterizedTest + @MethodSource("invalidInputStream") + void computeRankWithInvalidMatrix(double[][] matrix) { + assertThrows(IllegalArgumentException.class, () -> MatrixRank.computeRank(matrix)); + } +} diff --git a/src/test/java/com/thealgorithms/matrix/MatrixTransposeTest.java b/src/test/java/com/thealgorithms/matrix/MatrixTransposeTest.java new file mode 100644 index 000000000000..0457f31418cf --- /dev/null +++ b/src/test/java/com/thealgorithms/matrix/MatrixTransposeTest.java @@ -0,0 +1,33 @@ +package com.thealgorithms.matrix; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class MatrixTransposeTest { + + private static Stream<Arguments> provideValidMatrixTestCases() { + return Stream.of(Arguments.of(new int[][] {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}, new int[][] {{1, 4, 7}, {2, 5, 8}, {3, 6, 9}}, "Transpose of square matrix"), Arguments.of(new int[][] {{1, 2}, {3, 4}, {5, 6}}, new int[][] {{1, 3, 5}, {2, 4, 6}}, "Transpose of rectangular matrix"), + Arguments.of(new int[][] {{1, 2, 3}}, new int[][] {{1}, {2}, {3}}, "Transpose of single-row matrix"), Arguments.of(new int[][] {{1}, {2}, {3}}, new int[][] {{1, 2, 3}}, "Transpose of single-column matrix")); + } + + private static Stream<Arguments> provideInvalidMatrixTestCases() { + return Stream.of(Arguments.of(new int[0][0], "Empty matrix should throw IllegalArgumentException"), Arguments.of(null, "Null matrix should throw IllegalArgumentException")); + } + + @ParameterizedTest(name = "Test case {index}: {2}") + @MethodSource("provideValidMatrixTestCases") + void testValidMatrixTranspose(int[][] input, int[][] expected, String description) { + assertArrayEquals(expected, MatrixTranspose.transpose(input), description); + } + + @ParameterizedTest(name = "Test case {index}: {1}") + @MethodSource("provideInvalidMatrixTestCases") + void testInvalidMatrixTranspose(int[][] input, String description) { + assertThrows(IllegalArgumentException.class, () -> MatrixTranspose.transpose(input), description); + } +} diff --git a/src/test/java/com/thealgorithms/matrix/MatrixUtilTest.java b/src/test/java/com/thealgorithms/matrix/MatrixUtilTest.java new file mode 100644 index 000000000000..78947b1e70cb --- /dev/null +++ b/src/test/java/com/thealgorithms/matrix/MatrixUtilTest.java @@ -0,0 +1,80 @@ +package com.thealgorithms.matrix; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.thealgorithms.matrix.utils.MatrixUtil; +import java.math.BigDecimal; +import java.util.Objects; +import org.junit.jupiter.api.Test; + +class MatrixUtilTest { + + @Test + void add() { + final BigDecimal[][] matrix1 = { + {new BigDecimal(3), BigDecimal.TWO}, + {BigDecimal.ZERO, BigDecimal.ONE}, + }; + + final BigDecimal[][] matrix2 = { + {BigDecimal.ONE, new BigDecimal(3)}, + {BigDecimal.TWO, BigDecimal.ZERO}, + }; + + final BigDecimal[][] actual = MatrixUtil.add(matrix1, matrix2).orElseThrow(() -> new AssertionError("Could not compute matrix!")); + + final BigDecimal[][] expected = { + {new BigDecimal(4), new BigDecimal(5)}, + {BigDecimal.TWO, BigDecimal.ONE}, + }; + + assertTrue(Objects.deepEquals(actual, expected)); + } + @Test + void subtract() { + final BigDecimal[][] matrix1 = { + {BigDecimal.ONE, new BigDecimal(4)}, + {new BigDecimal(5), new BigDecimal(6)}, + }; + + final BigDecimal[][] matrix2 = { + {BigDecimal.TWO, BigDecimal.ZERO}, + {new BigDecimal(-2), new BigDecimal(-3)}, + }; + + final BigDecimal[][] actual = MatrixUtil.subtract(matrix1, matrix2).orElseThrow(() -> new AssertionError("Could not compute matrix!")); + + final BigDecimal[][] expected = { + {new BigDecimal(-1), new BigDecimal(4)}, + {new BigDecimal(7), new BigDecimal(9)}, + }; + + assertTrue(Objects.deepEquals(actual, expected)); + } + + @Test + void multiply() { + + final BigDecimal[][] matrix1 = { + {BigDecimal.ONE, BigDecimal.TWO, new BigDecimal(3)}, + {new BigDecimal(4), new BigDecimal(5), new BigDecimal(6)}, + {new BigDecimal(7), new BigDecimal(8), new BigDecimal(9)}, + }; + + final BigDecimal[][] matrix2 = { + {BigDecimal.ONE, BigDecimal.TWO}, + {new BigDecimal(3), new BigDecimal(4)}, + {new BigDecimal(5), new BigDecimal(6)}, + }; + + final BigDecimal[][] actual = MatrixUtil.multiply(matrix1, matrix2).orElseThrow(() -> new AssertionError("Could not compute matrix!")); + + final BigDecimal[][] expected = { + {new BigDecimal(22), new BigDecimal(28)}, + {new BigDecimal(49), new BigDecimal(64)}, + {new BigDecimal(76), new BigDecimal(100)}, + }; + + assertTrue(Objects.deepEquals(actual, expected)); + } +} diff --git a/src/test/java/com/thealgorithms/matrix/MedianOfMatrixTest.java b/src/test/java/com/thealgorithms/matrix/MedianOfMatrixTest.java new file mode 100644 index 000000000000..db66bb2d187b --- /dev/null +++ b/src/test/java/com/thealgorithms/matrix/MedianOfMatrixTest.java @@ -0,0 +1,34 @@ +package com.thealgorithms.matrix; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class MedianOfMatrixTest { + + @Test + public void testMedianWithOddNumberOfElements() { + List<List<Integer>> matrix = new ArrayList<>(); + matrix.add(Arrays.asList(1, 3, 5)); + matrix.add(Arrays.asList(2, 4, 6)); + matrix.add(Arrays.asList(7, 8, 9)); + + int result = MedianOfMatrix.median(matrix); + + assertEquals(5, result); + } + + @Test + public void testMedianWithEvenNumberOfElements() { + List<List<Integer>> matrix = new ArrayList<>(); + matrix.add(Arrays.asList(2, 4)); + matrix.add(Arrays.asList(1, 3)); + + int result = MedianOfMatrix.median(matrix); + + assertEquals(2, result); + } +} diff --git a/src/test/java/com/thealgorithms/matrix/MirrorOfMatrixTest.java b/src/test/java/com/thealgorithms/matrix/MirrorOfMatrixTest.java new file mode 100644 index 000000000000..2e4370922370 --- /dev/null +++ b/src/test/java/com/thealgorithms/matrix/MirrorOfMatrixTest.java @@ -0,0 +1,53 @@ +package com.thealgorithms.matrix; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class MirrorOfMatrixTest { + + @Test + void testMirrorMatrixRegularMatrix() { + double[][] originalMatrix = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; + double[][] expectedMirrorMatrix = {{3, 2, 1}, {6, 5, 4}, {9, 8, 7}}; + double[][] mirroredMatrix = MirrorOfMatrix.mirrorMatrix(originalMatrix); + assertArrayEquals(expectedMirrorMatrix, mirroredMatrix); + } + + @Test + void testMirrorMatrixEmptyMatrix() { + double[][] originalMatrix = {}; + Exception e = assertThrows(IllegalArgumentException.class, () -> MirrorOfMatrix.mirrorMatrix(originalMatrix)); + assertEquals("The input matrix cannot be empty", e.getMessage()); + } + + @Test + void testMirrorMatrixSingleElementMatrix() { + double[][] originalMatrix = {{42}}; + double[][] expectedMirrorMatrix = {{42}}; + double[][] mirroredMatrix = MirrorOfMatrix.mirrorMatrix(originalMatrix); + assertArrayEquals(expectedMirrorMatrix, mirroredMatrix); + } + + @Test + void testMirrorMatrixMultipleRowsOneColumnMatrix() { + double[][] originalMatrix = {{1}, {2}, {3}, {4}}; + double[][] expectedMirrorMatrix = {{1}, {2}, {3}, {4}}; + double[][] mirroredMatrix = MirrorOfMatrix.mirrorMatrix(originalMatrix); + assertArrayEquals(expectedMirrorMatrix, mirroredMatrix); + } + + @Test + void testMirrorMatrixNullInput() { + double[][] originalMatrix = null; + Exception e = assertThrows(IllegalArgumentException.class, () -> MirrorOfMatrix.mirrorMatrix(originalMatrix)); + assertEquals("The input matrix cannot be null", e.getMessage()); + } + + @Test + void testMirrorMatrixThrows() { + assertThrows(IllegalArgumentException.class, () -> MirrorOfMatrix.mirrorMatrix(new double[][] {{1}, {2, 3}})); + } +} diff --git a/src/test/java/com/thealgorithms/matrix/SolveSystemTest.java b/src/test/java/com/thealgorithms/matrix/SolveSystemTest.java new file mode 100644 index 000000000000..c8d289bd8339 --- /dev/null +++ b/src/test/java/com/thealgorithms/matrix/SolveSystemTest.java @@ -0,0 +1,21 @@ +package com.thealgorithms.matrix; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class SolveSystemTest { + + @ParameterizedTest + @MethodSource({"matrixGenerator"}) + void solveSystem(double[][] matrix, double[] constants, double[] solution) { + double[] expected = SolveSystem.solveSystem(matrix, constants); + assertArrayEquals(expected, solution, 1.0E-10, "Solution does not match expected"); + } + private static Stream<Arguments> matrixGenerator() { + return Stream.of(Arguments.of(new double[][] {{-5, 8, -4}, {0, 6, 3}, {0, 0, -4}}, new double[] {38, -9, 20}, new double[] {-2, 1, -5}), Arguments.of(new double[][] {{-2, -1, -1}, {3, 4, 1}, {3, 6, 5}}, new double[] {-11, 19, 43}, new double[] {2, 2, 5})); + } +} diff --git a/src/test/java/com/thealgorithms/matrix/TestPrintMatrixInSpiralOrder.java b/src/test/java/com/thealgorithms/matrix/TestPrintMatrixInSpiralOrder.java new file mode 100644 index 000000000000..bb415a5861a8 --- /dev/null +++ b/src/test/java/com/thealgorithms/matrix/TestPrintMatrixInSpiralOrder.java @@ -0,0 +1,26 @@ +package com.thealgorithms.matrix; + +import static org.junit.jupiter.api.Assertions.assertIterableEquals; + +import java.util.List; +import org.junit.jupiter.api.Test; + +public class TestPrintMatrixInSpiralOrder { + @Test + public void testOne() { + int[][] matrix = {{3, 4, 5, 6, 7}, {8, 9, 10, 11, 12}, {14, 15, 16, 17, 18}, {23, 24, 25, 26, 27}, {30, 31, 32, 33, 34}}; + var printClass = new PrintAMatrixInSpiralOrder(); + List<Integer> res = printClass.print(matrix, matrix.length, matrix[0].length); + List<Integer> list = List.of(3, 4, 5, 6, 7, 12, 18, 27, 34, 33, 32, 31, 30, 23, 14, 8, 9, 10, 11, 17, 26, 25, 24, 15, 16); + assertIterableEquals(res, list); + } + + @Test + public void testTwo() { + int[][] matrix = {{2, 2}}; + var printClass = new PrintAMatrixInSpiralOrder(); + List<Integer> res = printClass.print(matrix, matrix.length, matrix[0].length); + List<Integer> list = List.of(2, 2); + assertIterableEquals(res, list); + } +} diff --git a/src/test/java/com/thealgorithms/misc/ColorContrastRatioTest.java b/src/test/java/com/thealgorithms/misc/ColorContrastRatioTest.java new file mode 100644 index 000000000000..6b80edf4b14c --- /dev/null +++ b/src/test/java/com/thealgorithms/misc/ColorContrastRatioTest.java @@ -0,0 +1,33 @@ +package com.thealgorithms.misc; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.awt.Color; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class ColorContrastRatioTest { + private final ColorContrastRatio colorContrastRationCalculator = new ColorContrastRatio(); + + static Stream<Arguments> relativeLuminanceProvider() { + return Stream.of(Arguments.of(Color.BLACK, 0.0), Arguments.of(Color.WHITE, 1.0), Arguments.of(new Color(23, 103, 154), 0.12215748057375966), Arguments.of(new Color(226, 229, 248), 0.7898468477881603)); + } + + static Stream<Arguments> contrastRatioProvider() { + return Stream.of(Arguments.of(Color.BLACK, Color.WHITE, 21.0), Arguments.of(new Color(23, 103, 154), new Color(226, 229, 248), 4.878363954846178)); + } + + @ParameterizedTest + @MethodSource("relativeLuminanceProvider") + void testGetRelativeLuminance(Color color, double expectedLuminance) { + assertEquals(expectedLuminance, colorContrastRationCalculator.getRelativeLuminance(color), 1e-10); + } + + @ParameterizedTest + @MethodSource("contrastRatioProvider") + void testGetContrastRatio(Color a, Color b, double expectedRatio) { + assertEquals(expectedRatio, colorContrastRationCalculator.getContrastRatio(a, b), 1e-10); + } +} diff --git a/src/test/java/com/thealgorithms/misc/MapReduceTest.java b/src/test/java/com/thealgorithms/misc/MapReduceTest.java new file mode 100644 index 000000000000..c79c40701cc1 --- /dev/null +++ b/src/test/java/com/thealgorithms/misc/MapReduceTest.java @@ -0,0 +1,23 @@ +package com.thealgorithms.misc; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class MapReduceTest { + @Test + public void testMapReduceWithSingleWordSentence() { + String oneWordSentence = "Hactober"; + String result = MapReduce.mapreduce(oneWordSentence); + + assertEquals("Hactober: 1", result); + } + + @Test + public void testMapReduceWithMultipleWordSentence() { + String multipleWordSentence = "I Love Love HactoberFest"; + String result = MapReduce.mapreduce(multipleWordSentence); + + assertEquals("I: 1,Love: 2,HactoberFest: 1", result); + } +} diff --git a/src/test/java/com/thealgorithms/misc/MedianOfRunningArrayTest.java b/src/test/java/com/thealgorithms/misc/MedianOfRunningArrayTest.java new file mode 100644 index 000000000000..e64ae1b741b6 --- /dev/null +++ b/src/test/java/com/thealgorithms/misc/MedianOfRunningArrayTest.java @@ -0,0 +1,201 @@ +package com.thealgorithms.misc; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +/** + * Test case for Median Of Running Array Problem. + * @author Ansh Shah (https://github.com/govardhanshah456) + */ + +public class MedianOfRunningArrayTest { + private static final String EXCEPTION_MESSAGE = "Enter at least 1 element, Median of empty list is not defined!"; + + @Test + public void testWhenInvalidInoutProvidedShouldThrowException() { + var stream = new MedianOfRunningArrayInteger(); + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> stream.median()); + assertEquals(exception.getMessage(), EXCEPTION_MESSAGE); + } + + @Test + public void testWithNegativeValues() { + var stream = new MedianOfRunningArrayInteger(); + stream.insert(-1); + assertEquals(-1, stream.median()); + stream.insert(-2); + assertEquals(-1, stream.median()); + stream.insert(-3); + assertEquals(-2, stream.median()); + } + + @Test + public void testWithSingleValues() { + var stream = new MedianOfRunningArrayInteger(); + stream.insert(-1); + assertEquals(-1, stream.median()); + } + + @Test + public void testWithRandomValues() { + var stream = new MedianOfRunningArrayInteger(); + stream.insert(10); + assertEquals(10, stream.median()); + + stream.insert(5); + assertEquals(7, stream.median()); + + stream.insert(20); + assertEquals(10, stream.median()); + + stream.insert(15); + assertEquals(12, stream.median()); + + stream.insert(25); + assertEquals(15, stream.median()); + + stream.insert(30); + assertEquals(17, stream.median()); + + stream.insert(35); + assertEquals(20, stream.median()); + + stream.insert(1); + assertEquals(17, stream.median()); + } + + @Test + public void testWithNegativeAndPositiveValues() { + var stream = new MedianOfRunningArrayInteger(); + stream.insert(-1); + assertEquals(-1, stream.median()); + stream.insert(2); + assertEquals(0, stream.median()); + stream.insert(-3); + assertEquals(-1, stream.median()); + } + + @Test + public void testWithDuplicateValues() { + var stream = new MedianOfRunningArrayInteger(); + stream.insert(-1); + assertEquals(-1, stream.median()); + stream.insert(-1); + assertEquals(-1, stream.median()); + stream.insert(-1); + assertEquals(-1, stream.median()); + } + + @Test + public void testWithDuplicateValuesB() { + var stream = new MedianOfRunningArrayInteger(); + stream.insert(10); + stream.insert(20); + stream.insert(10); + stream.insert(10); + stream.insert(20); + stream.insert(0); + stream.insert(50); + assertEquals(10, stream.median()); + } + + @Test + public void testWithLargeValues() { + var stream = new MedianOfRunningArrayInteger(); + stream.insert(1000000); + assertEquals(1000000, stream.median()); + stream.insert(12000); + assertEquals(506000, stream.median()); + stream.insert(15000000); + assertEquals(1000000, stream.median()); + stream.insert(2300000); + assertEquals(1650000, stream.median()); + } + + @Test + public void testWithLargeCountOfValues() { + var stream = new MedianOfRunningArrayInteger(); + for (int i = 1; i <= 1000; i++) { + stream.insert(i); + } + assertEquals(500, stream.median()); + } + + @Test + public void testWithThreeValuesInDescendingOrder() { + var stream = new MedianOfRunningArrayInteger(); + stream.insert(30); + stream.insert(20); + stream.insert(10); + assertEquals(20, stream.median()); + } + + @Test + public void testWithThreeValuesInOrder() { + var stream = new MedianOfRunningArrayInteger(); + stream.insert(10); + stream.insert(20); + stream.insert(30); + assertEquals(20, stream.median()); + } + + @Test + public void testWithThreeValuesNotInOrderA() { + var stream = new MedianOfRunningArrayInteger(); + stream.insert(30); + stream.insert(10); + stream.insert(20); + assertEquals(20, stream.median()); + } + + @Test + public void testWithThreeValuesNotInOrderB() { + var stream = new MedianOfRunningArrayInteger(); + stream.insert(20); + stream.insert(10); + stream.insert(30); + assertEquals(20, stream.median()); + } + + @Test + public void testWithFloatValues() { + var stream = new MedianOfRunningArrayFloat(); + stream.insert(20.0f); + assertEquals(20.0f, stream.median()); + stream.insert(10.5f); + assertEquals(15.25f, stream.median()); + stream.insert(30.0f); + assertEquals(20.0f, stream.median()); + } + + @Test + public void testWithByteValues() { + var stream = new MedianOfRunningArrayByte(); + stream.insert((byte) 120); + assertEquals((byte) 120, stream.median()); + stream.insert((byte) -120); + assertEquals((byte) 0, stream.median()); + stream.insert((byte) 127); + assertEquals((byte) 120, stream.median()); + } + + @Test + public void testWithLongValues() { + var stream = new MedianOfRunningArrayLong(); + stream.insert(120000000L); + assertEquals(120000000L, stream.median()); + stream.insert(92233720368547757L); + assertEquals(46116860244273878L, stream.median()); + } + + @Test + public void testWithDoubleValues() { + var stream = new MedianOfRunningArrayDouble(); + stream.insert(12345.67891); + assertEquals(12345.67891, stream.median()); + stream.insert(23456789.98); + assertEquals(11734567.83, stream.median(), .01); + } +} diff --git a/src/test/java/com/thealgorithms/misc/PalindromePrimeTest.java b/src/test/java/com/thealgorithms/misc/PalindromePrimeTest.java new file mode 100644 index 000000000000..130cd19b47b1 --- /dev/null +++ b/src/test/java/com/thealgorithms/misc/PalindromePrimeTest.java @@ -0,0 +1,53 @@ +package com.thealgorithms.misc; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.junit.jupiter.api.Test; + +public class PalindromePrimeTest { + + @Test + public void testPrimeWithPrimeNumbers() { + assertTrue(PalindromePrime.prime(2), "2 should be prime"); + assertTrue(PalindromePrime.prime(3), "3 should be prime"); + assertTrue(PalindromePrime.prime(5), "5 should be prime"); + assertTrue(PalindromePrime.prime(11), "11 should be prime"); + } + + @Test + public void testPrimeWithNonPrimeNumbers() { + assertFalse(PalindromePrime.prime(1), "1 is not prime"); + assertFalse(PalindromePrime.prime(4), "4 is not prime"); + assertFalse(PalindromePrime.prime(9), "9 is not prime"); + assertFalse(PalindromePrime.prime(15), "15 is not prime"); + } + + @Test + public void testReverse() { + assertEquals(123, PalindromePrime.reverse(321), "Reverse of 321 should be 123"); + assertEquals(7, PalindromePrime.reverse(7), "Reverse of 7 should be 7"); + assertEquals(1221, PalindromePrime.reverse(1221), "Reverse of 1221 should be 1221"); + } + + @Test + public void testGeneratePalindromePrimes() { + List<Integer> result = PalindromePrime.generatePalindromePrimes(5); + List<Integer> expected = List.of(2, 3, 5, 7, 11); + assertEquals(expected, result, "The first 5 palindromic primes should be [2, 3, 5, 7, 11]"); + } + + @Test + public void testGeneratePalindromePrimesWithZero() { + List<Integer> result = PalindromePrime.generatePalindromePrimes(0); + assertTrue(result.isEmpty(), "Generating 0 palindromic primes should return an empty list"); + } + + @Test + public void testGeneratePalindromePrimesWithNegativeInput() { + List<Integer> result = PalindromePrime.generatePalindromePrimes(-5); + assertTrue(result.isEmpty(), "Generating a negative number of palindromic primes should return an empty list"); + } +} diff --git a/src/test/java/com/thealgorithms/misc/PalindromeSinglyLinkedListTest.java b/src/test/java/com/thealgorithms/misc/PalindromeSinglyLinkedListTest.java new file mode 100644 index 000000000000..0f0577d39094 --- /dev/null +++ b/src/test/java/com/thealgorithms/misc/PalindromeSinglyLinkedListTest.java @@ -0,0 +1,142 @@ +package com.thealgorithms.misc; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.thealgorithms.datastructures.lists.SinglyLinkedList; +import org.junit.jupiter.api.Test; + +public class PalindromeSinglyLinkedListTest { + + // Stack-based tests + @Test + public void testWithEmptyList() { + assertTrue(PalindromeSinglyLinkedList.isPalindrome(new SinglyLinkedList())); + } + + @Test + public void testWithSingleElement() { + var exampleList = new SinglyLinkedList(); + exampleList.insert(100); + assertTrue(PalindromeSinglyLinkedList.isPalindrome(exampleList)); + } + + @Test + public void testWithListWithOddLengthPositive() { + var exampleList = new SinglyLinkedList(); + exampleList.insert(1); + exampleList.insert(2); + exampleList.insert(1); + assertTrue(PalindromeSinglyLinkedList.isPalindrome(exampleList)); + } + + @Test + public void testWithListWithOddLengthPositive2() { + var exampleList = new SinglyLinkedList(); + exampleList.insert(3); + exampleList.insert(2); + exampleList.insert(1); + exampleList.insert(2); + exampleList.insert(3); + assertTrue(PalindromeSinglyLinkedList.isPalindrome(exampleList)); + } + + @Test + public void testWithListWithEvenLengthPositive() { + var exampleList = new SinglyLinkedList(); + exampleList.insert(10); + exampleList.insert(20); + exampleList.insert(20); + exampleList.insert(10); + assertTrue(PalindromeSinglyLinkedList.isPalindrome(exampleList)); + } + + @Test + public void testWithListWithOddLengthNegative() { + var exampleList = new SinglyLinkedList(); + exampleList.insert(1); + exampleList.insert(2); + exampleList.insert(2); + assertFalse(PalindromeSinglyLinkedList.isPalindrome(exampleList)); + } + + @Test + public void testWithListWithEvenLengthNegative() { + var exampleList = new SinglyLinkedList(); + exampleList.insert(10); + exampleList.insert(20); + exampleList.insert(20); + exampleList.insert(20); + assertFalse(PalindromeSinglyLinkedList.isPalindrome(exampleList)); + } + + // Optimized approach tests + @Test + public void testOptimisedWithEmptyList() { + assertTrue(PalindromeSinglyLinkedList.isPalindromeOptimised(null)); + } + + @Test + public void testOptimisedWithSingleElement() { + PalindromeSinglyLinkedList.Node node = new PalindromeSinglyLinkedList.Node(100); + assertTrue(PalindromeSinglyLinkedList.isPalindromeOptimised(node)); + } + + @Test + public void testOptimisedWithOddLengthPositive() { + PalindromeSinglyLinkedList.Node node1 = new PalindromeSinglyLinkedList.Node(1); + PalindromeSinglyLinkedList.Node node2 = new PalindromeSinglyLinkedList.Node(2); + PalindromeSinglyLinkedList.Node node3 = new PalindromeSinglyLinkedList.Node(1); + node1.next = node2; + node2.next = node3; + assertTrue(PalindromeSinglyLinkedList.isPalindromeOptimised(node1)); + } + + @Test + public void testOptimisedWithOddLengthPositive2() { + PalindromeSinglyLinkedList.Node node1 = new PalindromeSinglyLinkedList.Node(3); + PalindromeSinglyLinkedList.Node node2 = new PalindromeSinglyLinkedList.Node(2); + PalindromeSinglyLinkedList.Node node3 = new PalindromeSinglyLinkedList.Node(1); + PalindromeSinglyLinkedList.Node node4 = new PalindromeSinglyLinkedList.Node(2); + PalindromeSinglyLinkedList.Node node5 = new PalindromeSinglyLinkedList.Node(3); + node1.next = node2; + node2.next = node3; + node3.next = node4; + node4.next = node5; + assertTrue(PalindromeSinglyLinkedList.isPalindromeOptimised(node1)); + } + + @Test + public void testOptimisedWithEvenLengthPositive() { + PalindromeSinglyLinkedList.Node node1 = new PalindromeSinglyLinkedList.Node(10); + PalindromeSinglyLinkedList.Node node2 = new PalindromeSinglyLinkedList.Node(20); + PalindromeSinglyLinkedList.Node node3 = new PalindromeSinglyLinkedList.Node(20); + PalindromeSinglyLinkedList.Node node4 = new PalindromeSinglyLinkedList.Node(10); + node1.next = node2; + node2.next = node3; + node3.next = node4; + assertTrue(PalindromeSinglyLinkedList.isPalindromeOptimised(node1)); + } + + @Test + public void testOptimisedWithOddLengthNegative() { + PalindromeSinglyLinkedList.Node node1 = new PalindromeSinglyLinkedList.Node(1); + PalindromeSinglyLinkedList.Node node2 = new PalindromeSinglyLinkedList.Node(2); + PalindromeSinglyLinkedList.Node node3 = new PalindromeSinglyLinkedList.Node(2); + node1.next = node2; + node2.next = node3; + assertFalse(PalindromeSinglyLinkedList.isPalindromeOptimised(node1)); + } + + @Test + public void testOptimisedWithEvenLengthNegative() { + PalindromeSinglyLinkedList.Node node1 = new PalindromeSinglyLinkedList.Node(10); + PalindromeSinglyLinkedList.Node node2 = new PalindromeSinglyLinkedList.Node(20); + PalindromeSinglyLinkedList.Node node3 = new PalindromeSinglyLinkedList.Node(20); + PalindromeSinglyLinkedList.Node node4 = new PalindromeSinglyLinkedList.Node(20); + node1.next = node2; + node2.next = node3; + node3.next = node4; + assertFalse(PalindromeSinglyLinkedList.isPalindromeOptimised(node1)); + } +} diff --git a/src/test/java/com/thealgorithms/misc/RangeInSortedArrayTest.java b/src/test/java/com/thealgorithms/misc/RangeInSortedArrayTest.java new file mode 100644 index 000000000000..7630d3e78dc7 --- /dev/null +++ b/src/test/java/com/thealgorithms/misc/RangeInSortedArrayTest.java @@ -0,0 +1,35 @@ +package com.thealgorithms.misc; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class RangeInSortedArrayTest { + + @ParameterizedTest(name = "Test case {index}: {3}") + @MethodSource("provideSortedRangeTestCases") + void testSortedRange(int[] nums, int key, int[] expectedRange, String description) { + assertArrayEquals(expectedRange, RangeInSortedArray.sortedRange(nums, key), description); + } + + private static Stream<Arguments> provideSortedRangeTestCases() { + return Stream.of(Arguments.of(new int[] {1, 2, 3, 3, 3, 4, 5}, 3, new int[] {2, 4}, "Range for key 3 with multiple occurrences"), Arguments.of(new int[] {1, 2, 3, 3, 3, 4, 5}, 4, new int[] {5, 5}, "Range for key 4 with single occurrence"), + Arguments.of(new int[] {0, 1, 2}, 3, new int[] {-1, -1}, "Range for non-existent key"), Arguments.of(new int[] {}, 1, new int[] {-1, -1}, "Range in empty array"), Arguments.of(new int[] {1, 1, 1, 2, 3, 4, 5, 5, 5}, 1, new int[] {0, 2}, "Range for key at start"), + Arguments.of(new int[] {1, 1, 1, 2, 3, 4, 5, 5, 5}, 5, new int[] {6, 8}, "Range for key at end")); + } + + @ParameterizedTest(name = "Test case {index}: {3}") + @MethodSource("provideGetCountLessThanTestCases") + void testGetCountLessThan(int[] nums, int key, int expectedCount, String description) { + assertEquals(expectedCount, RangeInSortedArray.getCountLessThan(nums, key), description); + } + + private static Stream<Arguments> provideGetCountLessThanTestCases() { + return Stream.of(Arguments.of(new int[] {1, 2, 3, 3, 4, 5}, 3, 4, "Count of elements less than existing key"), Arguments.of(new int[] {1, 2, 3, 3, 4, 5}, 4, 5, "Count of elements less than non-existing key"), Arguments.of(new int[] {1, 2, 2, 3}, 5, 4, "Count with all smaller elements"), + Arguments.of(new int[] {2, 3, 4, 5}, 1, 0, "Count with no smaller elements"), Arguments.of(new int[] {}, 1, 0, "Count in empty array")); + } +} diff --git a/src/test/java/com/thealgorithms/misc/ShuffleArrayTest.java b/src/test/java/com/thealgorithms/misc/ShuffleArrayTest.java new file mode 100644 index 000000000000..915b83e376b6 --- /dev/null +++ b/src/test/java/com/thealgorithms/misc/ShuffleArrayTest.java @@ -0,0 +1,84 @@ +package com.thealgorithms.misc; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class ShuffleArrayTest { + + @Test + void testShuffleBasic() { + int[] arr = {1, 2, 3, 4, 5}; + int[] originalArr = arr.clone(); // Clone original array for comparison + ShuffleArray.shuffle(arr); + + // Check that the shuffled array is not the same as the original + assertNotEquals(originalArr, arr); + } + + @Test + void testShuffleSingleElement() { + int[] arr = {1}; + int[] originalArr = arr.clone(); + ShuffleArray.shuffle(arr); + + // Check that the shuffled array is the same as the original + assertArrayEquals(originalArr, arr); + } + + @Test + void testShuffleTwoElements() { + int[] arr = {1, 2}; + int[] originalArr = arr.clone(); + ShuffleArray.shuffle(arr); + + // Check that the shuffled array is not the same as the original + assertNotEquals(originalArr, arr); + // Check that the shuffled array still contains the same elements + assertTrue(arr[0] == 1 || arr[0] == 2); + assertTrue(arr[1] == 1 || arr[1] == 2); + } + + @Test + void testShuffleEmptyArray() { + int[] arr = {}; + int[] originalArr = arr.clone(); + ShuffleArray.shuffle(arr); + + // Check that the shuffled array is the same as the original (still empty) + assertArrayEquals(originalArr, arr); + } + + @Test + void testShuffleLargeArray() { + int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + int[] originalArr = arr.clone(); + ShuffleArray.shuffle(arr); + + // Check that the shuffled array is not the same as the original + assertNotEquals(originalArr, arr); + } + + @Test + void testShuffleRetainsElements() { + int[] arr = {1, 2, 3, 4, 5}; + ShuffleArray.shuffle(arr); + + // Check that the shuffled array contains the same elements + assertTrue(arr.length == 5); + for (int i = 1; i <= 5; i++) { + assertTrue(contains(arr, i)); + } + } + + private boolean contains(int[] arr, int value) { + for (int num : arr) { + if (num == value) { + return true; + } + } + return false; + } +} diff --git a/src/test/java/com/thealgorithms/misc/SparsityTest.java b/src/test/java/com/thealgorithms/misc/SparsityTest.java new file mode 100644 index 000000000000..b93e4f44937d --- /dev/null +++ b/src/test/java/com/thealgorithms/misc/SparsityTest.java @@ -0,0 +1,36 @@ +package com.thealgorithms.misc; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class SparsityTest { + + private static final double DELTA = 1e-9; + + @ParameterizedTest(name = "Test case {index}: {2}") + @MethodSource("provideTestCases") + public void testSparsity(double[][] matrix, double expectedSparsity, String description) { + assertEquals(expectedSparsity, Sparsity.sparsity(matrix), DELTA, description); + } + + private static Stream<Arguments> provideTestCases() { + return Stream.of(Arguments.of(new double[][] {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, 1.0, "Matrix with all zero elements"), Arguments.of(new double[][] {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}, 0.0, "Matrix with no zero elements"), + Arguments.of(new double[][] {{0, 2, 0}, {4, 0, 6}, {0, 8, 0}}, 5.0 / 9.0, "Matrix with mixed elements"), Arguments.of(new double[][] {{0, 1, 0, 2, 0}}, 3.0 / 5.0, "Single-row matrix"), Arguments.of(new double[][] {{1}, {0}, {0}, {2}}, 2.0 / 4.0, "Single-column matrix"), + Arguments.of(new double[][] {{0}}, 1.0, "Matrix with a single zero element"), Arguments.of(new double[][] {{5}}, 0.0, "Matrix with a single non-zero element")); + } + + @ParameterizedTest(name = "Test case {index}: {1}") + @MethodSource("provideExceptionTestCases") + public void testSparsityExceptions(double[][] matrix, String description) { + assertThrows(IllegalArgumentException.class, () -> Sparsity.sparsity(matrix), description); + } + + private static Stream<Arguments> provideExceptionTestCases() { + return Stream.of(Arguments.of(new double[][] {}, "Empty matrix should throw IllegalArgumentException")); + } +} diff --git a/src/test/java/com/thealgorithms/misc/ThreeSumProblemTest.java b/src/test/java/com/thealgorithms/misc/ThreeSumProblemTest.java new file mode 100644 index 000000000000..5353168216ec --- /dev/null +++ b/src/test/java/com/thealgorithms/misc/ThreeSumProblemTest.java @@ -0,0 +1,52 @@ +package com.thealgorithms.misc; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class ThreeSumProblemTest { + + private ThreeSumProblem tsp; + + @BeforeEach + public void setup() { + tsp = new ThreeSumProblem(); + } + + @ParameterizedTest + @MethodSource("bruteForceTestProvider") + public void testBruteForce(int[] nums, int target, List<List<Integer>> expected) { + assertEquals(expected, tsp.bruteForce(nums, target)); + } + + @ParameterizedTest + @MethodSource("twoPointerTestProvider") + public void testTwoPointer(int[] nums, int target, List<List<Integer>> expected) { + assertEquals(expected, tsp.twoPointer(nums, target)); + } + + @ParameterizedTest + @MethodSource("hashMapTestProvider") + public void testHashMap(int[] nums, int target, List<List<Integer>> expected) { + assertEquals(expected, tsp.hashMap(nums, target)); + } + + private static Stream<Arguments> bruteForceTestProvider() { + return Stream.of(Arguments.of(new int[] {1, 2, -3, 4, -2, -1}, 0, Arrays.asList(Arrays.asList(-3, 1, 2), Arrays.asList(-3, -1, 4))), Arguments.of(new int[] {1, 2, 3, 4, 5}, 50, new ArrayList<>())); + } + + private static Stream<Arguments> twoPointerTestProvider() { + return Stream.of(Arguments.of(new int[] {0, -1, 2, -3, 1}, 0, Arrays.asList(Arrays.asList(-3, 1, 2), Arrays.asList(-1, 0, 1))), Arguments.of(new int[] {-5, -4, -3, -2, -1}, -10, Arrays.asList(Arrays.asList(-5, -4, -1), Arrays.asList(-5, -3, -2)))); + } + + private static Stream<Arguments> hashMapTestProvider() { + return Stream.of(Arguments.of(new int[] {1, 2, -1, -4, 3, 0}, 2, Arrays.asList(Arrays.asList(-1, 0, 3), Arrays.asList(-1, 1, 2))), Arguments.of(new int[] {5, 7, 9, 11}, 10, new ArrayList<>()), Arguments.of(new int[] {}, 0, new ArrayList<>())); + } +} diff --git a/src/test/java/com/thealgorithms/misc/TwoSumProblemTest.java b/src/test/java/com/thealgorithms/misc/TwoSumProblemTest.java new file mode 100644 index 000000000000..86e73ac0547c --- /dev/null +++ b/src/test/java/com/thealgorithms/misc/TwoSumProblemTest.java @@ -0,0 +1,61 @@ +package com.thealgorithms.misc; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import org.apache.commons.lang3.tuple.Pair; +import org.junit.jupiter.api.Test; + +/** + * Test case for Two sum Problem. + * @author Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi) + */ + +public class TwoSumProblemTest { + + @Test + void testTwoSumExists() { + final int[] values = new int[] {2, 7, 11, 15}; + final int target = 9; + final var expected = Pair.of(0, 1); // values[0] + values[1] = 2 + 7 = 9 + assertEquals(expected, TwoSumProblem.twoSum(values, target).get()); + } + + @Test + void testTwoSumNoSolution() { + final int[] values = new int[] {2, 7, 11, 15}; + final int target = 3; + assertFalse(TwoSumProblem.twoSum(values, target).isPresent()); + } + + @Test + void testTwoSumMultipleSolutions() { + final int[] values = {3, 3}; + final int target = 6; + final var expected = Pair.of(0, 1); // values[0] + values[1] = 3 + 3 = 6 + assertEquals(expected, TwoSumProblem.twoSum(values, target).get()); + } + + @Test + void testTwoSumMultipleSolution() { + final int[] values = {3, 4, 3, 3}; + final int target = 6; + final var expected = Pair.of(0, 2); // values[0] + values[2] = 3 + 3 = 6 + assertEquals(expected, TwoSumProblem.twoSum(values, target).get()); + } + + @Test + void testTwoSumNegativeNumbers() { + final int[] values = {-1, -2, -3, -4, -5}; + final int target = -8; + final var expected = Pair.of(2, 4); // values[2] + values[4] = -3 + (-5) = -8 + assertEquals(expected, TwoSumProblem.twoSum(values, target).get()); + } + + @Test + void testTwoSumNoSolutionDuplicatedInputs() { + final int[] values = {0, 0, 0}; + final int target = 100; + assertFalse(TwoSumProblem.twoSum(values, target).isPresent()); + } +} diff --git a/src/test/java/com/thealgorithms/others/ArrayLeftRotationTest.java b/src/test/java/com/thealgorithms/others/ArrayLeftRotationTest.java new file mode 100644 index 000000000000..b31b7d825ed5 --- /dev/null +++ b/src/test/java/com/thealgorithms/others/ArrayLeftRotationTest.java @@ -0,0 +1,54 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +class ArrayLeftRotationTest { + + @Test + void testForOneElement() { + int[] arr = {3}; + int[] result = ArrayLeftRotation.rotateLeft(arr, 3); + assertArrayEquals(arr, result); + } + + @Test + void testForZeroStep() { + int[] arr = {3, 1, 5, 8, 6}; + int[] result = ArrayLeftRotation.rotateLeft(arr, 0); + assertArrayEquals(arr, result); + } + + @Test + void testForEqualSizeStep() { + int[] arr = {3, 1, 5, 8, 6}; + int[] result = ArrayLeftRotation.rotateLeft(arr, 5); + assertArrayEquals(arr, result); + } + + @Test + void testForLowerSizeStep() { + int[] arr = {3, 1, 5, 8, 6}; + int n = 2; + int[] expected = {5, 8, 6, 3, 1}; + int[] result = ArrayLeftRotation.rotateLeft(arr, n); + assertArrayEquals(expected, result); + } + + @Test + void testForHigherSizeStep() { + int[] arr = {3, 1, 5, 8, 6}; + int n = 7; + int[] expected = {5, 8, 6, 3, 1}; + int[] result = ArrayLeftRotation.rotateLeft(arr, n); + assertArrayEquals(expected, result); + } + + @Test + void testForEmptyArray() { + int[] arr = {}; + int[] result = ArrayLeftRotation.rotateLeft(arr, 3); + assertArrayEquals(arr, result); + } +} diff --git a/src/test/java/com/thealgorithms/others/ArrayRightRotationTest.java b/src/test/java/com/thealgorithms/others/ArrayRightRotationTest.java new file mode 100644 index 000000000000..f132d56dd9cd --- /dev/null +++ b/src/test/java/com/thealgorithms/others/ArrayRightRotationTest.java @@ -0,0 +1,53 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +class ArrayRightRotationTest { + + @Test + void testArrayRightRotation() { + int[] arr = {1, 2, 3, 4, 5, 6, 7}; + int k = 3; + int[] expected = {5, 6, 7, 1, 2, 3, 4}; + int[] result = ArrayRightRotation.rotateRight(arr, k); + assertArrayEquals(expected, result); + } + + @Test + void testArrayRightRotationWithZeroSteps() { + int[] arr = {1, 2, 3, 4, 5, 6, 7}; + int k = 0; + int[] expected = {1, 2, 3, 4, 5, 6, 7}; + int[] result = ArrayRightRotation.rotateRight(arr, k); + assertArrayEquals(expected, result); + } + + @Test + void testArrayRightRotationWithEqualSizeSteps() { + int[] arr = {1, 2, 3, 4, 5, 6, 7}; + int k = arr.length; + int[] expected = {1, 2, 3, 4, 5, 6, 7}; + int[] result = ArrayRightRotation.rotateRight(arr, k); + assertArrayEquals(expected, result); + } + + @Test + void testArrayRightRotationWithLowerSizeSteps() { + int[] arr = {1, 2, 3, 4, 5, 6, 7}; + int k = 2; + int[] expected = {6, 7, 1, 2, 3, 4, 5}; + int[] result = ArrayRightRotation.rotateRight(arr, k); + assertArrayEquals(expected, result); + } + + @Test + void testArrayRightRotationWithHigherSizeSteps() { + int[] arr = {1, 2, 3, 4, 5, 6, 7}; + int k = 10; + int[] expected = {5, 6, 7, 1, 2, 3, 4}; + int[] result = ArrayRightRotation.rotateRight(arr, k); + assertArrayEquals(expected, result); + } +} diff --git a/src/test/java/com/thealgorithms/others/BFPRTTest.java b/src/test/java/com/thealgorithms/others/BFPRTTest.java new file mode 100644 index 000000000000..bb7c8f074864 --- /dev/null +++ b/src/test/java/com/thealgorithms/others/BFPRTTest.java @@ -0,0 +1,34 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class BFPRTTest { + + @ParameterizedTest + @MethodSource("minKNumsTestData") + void testGetMinKNumsByBFPRT(int[] arr, int k, int[] expected) { + int[] result = BFPRT.getMinKNumsByBFPRT(arr, k); + assertArrayEquals(expected, result); + } + + private static Stream<Arguments> minKNumsTestData() { + return Stream.of(Arguments.of(new int[] {11, 9, 1, 3, 9, 2, 2, 5, 6, 5, 3, 5, 9, 7, 2, 5, 5, 1, 9}, 5, new int[] {1, 1, 2, 2, 2}), Arguments.of(new int[] {3, 2, 1}, 2, new int[] {1, 2}), Arguments.of(new int[] {7, 5, 9, 1, 3, 8, 2, 4, 6}, 3, new int[] {1, 2, 3})); + } + + @ParameterizedTest + @MethodSource("minKthTestData") + void testGetMinKthByBFPRT(int[] arr, int k, int expected) { + int result = BFPRT.getMinKthByBFPRT(arr, k); + assertEquals(expected, result); + } + + private static Stream<Arguments> minKthTestData() { + return Stream.of(Arguments.of(new int[] {3, 2, 1}, 2, 2), Arguments.of(new int[] {7, 5, 9, 1, 3, 8, 2, 4, 6}, 3, 3), Arguments.of(new int[] {5, 8, 6, 3, 2, 7, 1, 4}, 4, 4)); + } +} diff --git a/src/test/java/com/thealgorithms/others/BestFitCPUTest.java b/src/test/java/com/thealgorithms/others/BestFitCPUTest.java new file mode 100644 index 000000000000..296cf1ca1c04 --- /dev/null +++ b/src/test/java/com/thealgorithms/others/BestFitCPUTest.java @@ -0,0 +1,69 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import org.junit.jupiter.api.Test; + +/** + * author Alexandros Lemonaris + */ +class BestFitCPUTest { + + int[] sizeOfBlocks; + int[] sizeOfProcesses; + ArrayList<Integer> memAllocation = new ArrayList<>(); + ArrayList<Integer> testMemAllocation; + MemoryManagementAlgorithms bestFit = new BestFitCPU(); + + @Test + void testFitForUseOfOneBlock() { + // test1 - 2 processes shall fit to one block instead of using a different block each + sizeOfBlocks = new int[] {5, 12, 17, 10}; + sizeOfProcesses = new int[] {10, 5, 15, 2}; + memAllocation = bestFit.fitProcess(sizeOfBlocks, sizeOfProcesses); + testMemAllocation = new ArrayList<>(Arrays.asList(3, 0, 2, 2)); + assertEquals(testMemAllocation, memAllocation); + } + + @Test + void testFitForEqualProcecesses() { + // test2 + sizeOfBlocks = new int[] {5, 12, 17, 10}; + sizeOfProcesses = new int[] {10, 10, 10, 10}; + memAllocation = bestFit.fitProcess(sizeOfBlocks, sizeOfProcesses); + testMemAllocation = new ArrayList<>(Arrays.asList(3, 1, 2, -255)); + assertEquals(testMemAllocation, memAllocation); + } + + @Test + void testFitForNoEmptyBlockCell() { + // test3 for more processes than blocks - no empty space left to none of the blocks + sizeOfBlocks = new int[] {5, 12, 17}; + sizeOfProcesses = new int[] {5, 12, 10, 7}; + memAllocation = bestFit.fitProcess(sizeOfBlocks, sizeOfProcesses); + testMemAllocation = new ArrayList<>(Arrays.asList(0, 1, 2, 2)); + assertEquals(testMemAllocation, memAllocation); + } + + @Test + void testFitForSameInputDifferentQuery() { + // test4 for more processes than blocks - one element does not fit due to input series + sizeOfBlocks = new int[] {5, 12, 17}; + sizeOfProcesses = new int[] {5, 7, 10, 12}; + memAllocation = bestFit.fitProcess(sizeOfBlocks, sizeOfProcesses); + testMemAllocation = new ArrayList<>(Arrays.asList(0, 1, 2, -255)); + assertEquals(testMemAllocation, memAllocation); + } + + @Test + void testFitForMoreBlocksNoFit() { + // test5 for more blocks than processes + sizeOfBlocks = new int[] {5, 4, -1, 3, 6}; + sizeOfProcesses = new int[] {10, 11}; + memAllocation = bestFit.fitProcess(sizeOfBlocks, sizeOfProcesses); + testMemAllocation = new ArrayList<>(Arrays.asList(-255, -255)); + assertEquals(testMemAllocation, memAllocation); + } +} diff --git a/src/test/java/com/thealgorithms/others/BoyerMooreTest.java b/src/test/java/com/thealgorithms/others/BoyerMooreTest.java new file mode 100644 index 000000000000..8416535b2111 --- /dev/null +++ b/src/test/java/com/thealgorithms/others/BoyerMooreTest.java @@ -0,0 +1,30 @@ +package com.thealgorithms.others; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class BoyerMooreTest { + + @ParameterizedTest + @MethodSource("inputStreamWithExistingMajority") + void checkWhenMajorityExists(int expected, int[] input) { + Assertions.assertEquals(expected, BoyerMoore.findMajorityElement(input).get()); + } + + private static Stream<Arguments> inputStreamWithExistingMajority() { + return Stream.of(Arguments.of(5, new int[] {5, 5, 5, 2}), Arguments.of(10, new int[] {10, 10, 20}), Arguments.of(10, new int[] {10, 20, 10}), Arguments.of(10, new int[] {20, 10, 10}), Arguments.of(4, new int[] {1, 4, 2, 4, 4, 5, 4}), Arguments.of(-1, new int[] {-1})); + } + + @ParameterizedTest + @MethodSource("inputStreamWithoutMajority") + void checkWhenMajorityExists(int[] input) { + Assertions.assertFalse(BoyerMoore.findMajorityElement(input).isPresent()); + } + + private static Stream<Arguments> inputStreamWithoutMajority() { + return Stream.of(Arguments.of(new int[] {10, 10, 20, 20, 30, 30}), Arguments.of(new int[] {10, 20, 30, 40, 50}), Arguments.of(new int[] {1, 2}), Arguments.of(new int[] {})); + } +} diff --git a/src/test/java/com/thealgorithms/others/CRC16Test.java b/src/test/java/com/thealgorithms/others/CRC16Test.java new file mode 100644 index 000000000000..bf309928bbf4 --- /dev/null +++ b/src/test/java/com/thealgorithms/others/CRC16Test.java @@ -0,0 +1,19 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class CRC16Test { + @Test + void testCRC16() { + // given + String textToCRC16 = "hacktoberfest!"; + + // when + String resultCRC16 = CRC16.crc16(textToCRC16); // Algorithm CRC16-CCITT-FALSE + + // then + assertEquals("10FC", resultCRC16); + } +} diff --git a/src/test/java/com/thealgorithms/others/CRCAlgorithmTest.java b/src/test/java/com/thealgorithms/others/CRCAlgorithmTest.java new file mode 100644 index 000000000000..a581a35bf963 --- /dev/null +++ b/src/test/java/com/thealgorithms/others/CRCAlgorithmTest.java @@ -0,0 +1,29 @@ + +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class CRCAlgorithmTest { + + @Test + void test1() { + CRCAlgorithm c = new CRCAlgorithm("10010101010100101010010000001010010101010", 10, 0.0); + + // A bit-error rate of 0.0 should not provide any wrong messages + c.changeMess(); + c.divideMessageWithP(false); + assertEquals(c.getWrongMess(), 0); + } + + @Test + void test2() { + CRCAlgorithm c = new CRCAlgorithm("10010101010100101010010000001010010101010", 10, 1.0); + + // A bit error rate of 1.0 should not provide any correct messages + c.changeMess(); + c.divideMessageWithP(false); + assertEquals(c.getCorrectMess(), 0); + } +} diff --git a/src/test/java/com/thealgorithms/others/ConwayTest.java b/src/test/java/com/thealgorithms/others/ConwayTest.java new file mode 100644 index 000000000000..f4c3051a1fe2 --- /dev/null +++ b/src/test/java/com/thealgorithms/others/ConwayTest.java @@ -0,0 +1,41 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class ConwayTest { + @Test + public void testGenerateWith1() { + assertEquals("31131211131221", Conway.generateList("1", 8).get(7)); + } + + @Test + public void testGenerateWith123456() { + assertEquals( + "13211321322113311213212312311211131122211213211331121321122112133221123113112221131112212211131221121321131211132221123113112221131112311332211211131221131211132211121312211231131112311211232221143113112221131112311332111213122112311311123112112322211531131122211311123113321112131221123113111231121123222116", + Conway.generateList("123456", 20).get(11)); + } + + @Test + public void testGenerateWith1A1Z3E1R1T3G1F1D2E1S1C() { + assertEquals( + "311311222113111231133211121312211231131112311211133112111312211213211312111322211231131122111213122112311311222112111331121113112221121113122113121113222112132113213221232112111312111213322112311311222113111221221113122112132113121113222112311311222113111231133221121113311211131122211211131221131112311332211211131221131211132221232112111312111213322112132113213221133112132113221321123113213221121113122123211211131221222112112322211231131122211311123113321112132132112211131221131211132221121321132132212321121113121112133221123113112221131112311332111213211322111213111213211231131211132211121311222113321132211221121332211A311311222113111231133211121312211231131112311211133112111312211213211312111322211231131122111213122112311311222112111331121113112221121113122113121113222112132113213221232112111312111213322112311311222113111221221113122112132113121113222112311311222113111231133221121113311211131122211211131221131112311332211211131221131211132221232112111312111213322112132113213221133112132113221321123113213221121113122123211211131221222112112322211231131122211311123113321112132132112211131221131211132221121321132132212321121113121112133221123113112221131112311332111213211322111213111213211231131211132211121311222113321132211221121332211Z111312211312111322212321121113121112131112132112311321322112111312212321121113122112131112131221121321132132211231131122111213122112311311222113111221131221221321132132211331121321231231121113112221121321133112132112211213322112311311222113111231133211121312211231131122211322311311222112111312211311123113322112132113212231121113112221121321132122211322212221121123222112111312211312111322212321121113121112131112132112311321322112111312212321121113122112131112131221121321132132211231131122211331121321232221121113122113121122132112311321322112111312211312113221133211322112211213322112132113213221133112132123123112111311222112132113311213211231232112311311222112111312211311123113322112132113213221133112132123222113221321132132211331121321232221123113112221131112311322311211131122211213211331121321122112133221121113122113121113222123112221221321132132211231131122211331121321232221121113122113121113222123113221231231121113213221231221132221222112112322211E311311222113111231133211121312211231131112311211133112111312211213211312111322211231131122111213122112311311222112111331121113112221121113122113121113222112132113213221232112111312111213322112311311222113111221221113122112132113121113222112311311222113111231133221121113311211131122211211131221131112311332211211131221131211132221232112111312111213322112132113213221133112132113221321123113213221121113122123211211131221222112112322211231131122211311123113321112132132112211131221131211132221121321132132212321121113121112133221123113112221131112311332111213211322111213111213211231131211132211121311222113321132211221121332211R311311222113111231133211121312211231131112311211133112111312211213211312111322211231131122111213122112311311222112111331121113112221121113122113121113222112132113213221232112111312111213322112311311222113111221221113122112132113121113222112311311222113111231133221121113311211131122211211131221131112311332211211131221131211132221232112111312111213322112132113213221133112132113221321123113213221121113122123211211131221222112112322211231131122211311123113321112132132112211131221131211132221121321132132212321121113121112133221123113112221131112311332111213211322111213111213211231131211132211121311222113321132211221121332211T111312211312111322212321121113121112131112132112311321322112111312212321121113122112131112131221121321132132211231131122111213122112311311222113111221131221221321132132211331121321231231121113112221121321133112132112211213322112311311222113111231133211121312211231131122211322311311222112111312211311123113322112132113212231121113112221121321132122211322212221121123222112111312211312111322212321121113121112131112132112311321322112111312212321121113122112131112131221121321132132211231131122211331121321232221121113122113121122132112311321322112111312211312113221133211322112211213322112132113213221133112132123123112111311222112132113311213211231232112311311222112111312211311123113322112132113213221133112132123222113221321132132211331121321232221123113112221131112311322311211131122211213211331121321122112133221121113122113121113222123112221221321132132211231131122211331121321232221121113122113121113222123113221231231121113213221231221132221222112112322211G311311222113111231133211121312211231131112311211133112111312211213211312111322211231131122111213122112311311222112111331121113112221121113122113121113222112132113213221232112111312111213322112311311222113111221221113122112132113121113222112311311222113111231133221121113311211131122211211131221131112311332211211131221131211132221232112111312111213322112132113213221133112132113221321123113213221121113122123211211131221222112112322211231131122211311123113321112132132112211131221131211132221121321132132212321121113121112133221123113112221131112311332111213211322111213111213211231131211132211121311222113321132211221121332211F311311222113111231133211121312211231131112311211133112111312211213211312111322211231131122111213122112311311222112111331121113112221121113122113121113222112132113213221232112111312111213322112311311222113111221221113122112132113121113222112311311222113111231133221121113311211131122211211131221131112311332211211131221131211132221232112111312111213322112132113213221133112132113221321123113213221121113122123211211131221222112112322211231131122211311123113321112132132112211131221131211132221121321132132212321121113121112133221123113112221131112311332111213211322111213111213211231131211132211121311222113321132211221121332211D111312211312111322212321121113121112131112132112311321322112111312212321121113122112131112131221121321132132211231131122211331121321232221121113122113121122132112311321322112111312211312111322211213111213122112132113121113222112132113213221133112132123222112311311222113111231132231121113112221121321133112132112211213322112111312211312111322212311222122132113213221123113112221133112132123222112111312211312111322212311322123123112111321322123122113222122211211232221121113122113121113222123211211131211121311121321123113213221121113122123211211131221121311121312211213211321322112311311222113311213212322211211131221131211221321123113213221121113122113121113222112131112131221121321131211132221121321132132211331121321232221123113112221131112311322311211131122211213211331121321122112133221121113122113121113222123112221221321132132211231131122211331121321232221121113122113121113222123113221231231121113213221231221132221222112112322211E311311222113111231133211121312211231131112311211133112111312211213211312111322211231131122111213122112311311222112111331121113112221121113122113121113222112132113213221232112111312111213322112311311222113111221221113122112132113121113222112311311222113111231133221121113311211131122211211131221131112311332211211131221131211132221232112111312111213322112132113213221133112132113221321123113213221121113122123211211131221222112112322211231131122211311123113321112132132112211131221131211132221121321132132212321121113121112133221123113112221131112311332111213211322111213111213211231131211132211121311222113321132211221121332211S311311222113111231133211121312211231131112311211133112111312211213211312111322211231131122111213122112311311222112111331121113112221121113122113121113222112132113213221232112111312111213322112311311222113111221221113122112132113121113222112311311222113111231133221121113311211131122211211131221131112311332211211131221131211132221232112111312111213322112132113213221133112132113221321123113213221121113122123211211131221222112112322211231131122211311123113321112132132112211131221131211132221121321132132212321121113121112133221123113112221131112311332111213211322111213111213211231131211132211121311222113321132211221121332211C", + Conway.generateList("1A1Z3E1R1T3G1F1D2E1S1C", 20).get(19)); + } + + @Test + public void testGenerateNextElementWith1() { + assertEquals("11", Conway.generateNextElement("1")); + } + + @Test + public void testGenerateNextElementWith123456() { + assertEquals("111213141516", Conway.generateNextElement("123456")); + } + + @Test + public void testGenerateNextElementWith1A1Z3E1R1T3G1F1D2E1S1C() { + assertEquals("111A111Z131E111R111T131G111F111D121E111S111C", Conway.generateNextElement("1A1Z3E1R1T3G1F1D2E1S1C")); + } +} diff --git a/src/test/java/com/thealgorithms/others/CountFriendsPairingTest.java b/src/test/java/com/thealgorithms/others/CountFriendsPairingTest.java new file mode 100644 index 000000000000..f2e6944c06d2 --- /dev/null +++ b/src/test/java/com/thealgorithms/others/CountFriendsPairingTest.java @@ -0,0 +1,57 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.thealgorithms.dynamicprogramming.CountFriendsPairing; +import org.junit.jupiter.api.Test; + +public class CountFriendsPairingTest { + + @Test + void testForOneElement() { + int[] a = {1, 2, 2}; + assertTrue(CountFriendsPairing.countFriendsPairing(3, a)); + } + + @Test + void testForTwoElements() { + int[] a = {1, 2, 2, 3}; + assertTrue(CountFriendsPairing.countFriendsPairing(4, a)); + } + + @Test + void testForThreeElements() { + int[] a = {1, 2, 2, 3, 3}; + assertTrue(CountFriendsPairing.countFriendsPairing(5, a)); + } + + @Test + void testForFourElements() { + int[] a = {1, 2, 2, 3, 3, 4}; + assertTrue(CountFriendsPairing.countFriendsPairing(6, a)); + } + + @Test + void testForFiveElements() { + int[] a = {1, 2, 2, 3, 3, 4, 4}; + assertTrue(CountFriendsPairing.countFriendsPairing(7, a)); + } + + @Test + void testForSixElements() { + int[] a = {1, 2, 2, 3, 3, 4, 4, 4}; + assertTrue(CountFriendsPairing.countFriendsPairing(8, a)); + } + + @Test + void testForSevenElements() { + int[] a = {1, 2, 2, 3, 3, 4, 4, 4, 5}; + assertTrue(CountFriendsPairing.countFriendsPairing(9, a)); + } + + @Test + void testForEightElements() { + int[] a = {1, 2, 2, 3, 3, 4, 4, 4, 5, 5}; + assertTrue(CountFriendsPairing.countFriendsPairing(10, a)); + } +} diff --git a/src/test/java/com/thealgorithms/others/FirstFitCPUTest.java b/src/test/java/com/thealgorithms/others/FirstFitCPUTest.java new file mode 100644 index 000000000000..57b6e189b116 --- /dev/null +++ b/src/test/java/com/thealgorithms/others/FirstFitCPUTest.java @@ -0,0 +1,69 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import org.junit.jupiter.api.Test; + +/** + * author Alexandros Lemonaris + */ +class FirstFitCPUTest { + + int[] sizeOfBlocks; + int[] sizeOfProcesses; + ArrayList<Integer> memAllocation = new ArrayList<>(); + ArrayList<Integer> testMemAllocation; + MemoryManagementAlgorithms firstFit = new FirstFitCPU(); + + @Test + void testFitForUseOfOneBlock() { + // test1 - no use of one block for two processes + sizeOfBlocks = new int[] {5, 12, 17, 10}; + sizeOfProcesses = new int[] {10, 5, 15, 2}; + memAllocation = firstFit.fitProcess(sizeOfBlocks, sizeOfProcesses); + testMemAllocation = new ArrayList<>(Arrays.asList(1, 0, 2, 1)); + assertEquals(testMemAllocation, memAllocation); + } + + @Test + void testFitForEqualProcecesses() { + // test2 + sizeOfBlocks = new int[] {5, 12, 17, 10}; + sizeOfProcesses = new int[] {10, 10, 10, 10}; + memAllocation = firstFit.fitProcess(sizeOfBlocks, sizeOfProcesses); + testMemAllocation = new ArrayList<>(Arrays.asList(1, 2, 3, -255)); + assertEquals(testMemAllocation, memAllocation); + } + + @Test + void testFitForNoEmptyBlockCell() { + // test3 for more processes than blocks - no empty space left to none of the blocks + sizeOfBlocks = new int[] {5, 12, 17}; + sizeOfProcesses = new int[] {5, 12, 10, 7}; + memAllocation = firstFit.fitProcess(sizeOfBlocks, sizeOfProcesses); + testMemAllocation = new ArrayList<>(Arrays.asList(0, 1, 2, 2)); + assertEquals(testMemAllocation, memAllocation); + } + + @Test + void testFitForSameInputDifferentQuery() { + // test4 for more processes than blocks - one element does not fit due to input series + sizeOfBlocks = new int[] {5, 12, 17}; + sizeOfProcesses = new int[] {5, 7, 10, 12}; + memAllocation = firstFit.fitProcess(sizeOfBlocks, sizeOfProcesses); + testMemAllocation = new ArrayList<>(Arrays.asList(0, 1, 2, -255)); + assertEquals(testMemAllocation, memAllocation); + } + + @Test + void testFitForMoreBlocksNoFit() { + // test5 for more blocks than processes + sizeOfBlocks = new int[] {5, 4, -1, 3, 6}; + sizeOfProcesses = new int[] {10, 11}; + memAllocation = firstFit.fitProcess(sizeOfBlocks, sizeOfProcesses); + testMemAllocation = new ArrayList<>(Arrays.asList(-255, -255)); + assertEquals(testMemAllocation, memAllocation); + } +} diff --git a/src/test/java/com/thealgorithms/others/FloydTriangleTest.java b/src/test/java/com/thealgorithms/others/FloydTriangleTest.java new file mode 100644 index 000000000000..b336ac4be51f --- /dev/null +++ b/src/test/java/com/thealgorithms/others/FloydTriangleTest.java @@ -0,0 +1,48 @@ +package com.thealgorithms.others; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class FloydTriangleTest { + + @Test + public void testGenerateFloydTriangleWithValidInput() { + List<List<Integer>> expectedOutput = Arrays.asList(singletonList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6)); + assertEquals(expectedOutput, FloydTriangle.generateFloydTriangle(3)); + } + + @Test + public void testGenerateFloydTriangleWithOneRow() { + List<List<Integer>> expectedOutput = singletonList(singletonList(1)); + assertEquals(expectedOutput, FloydTriangle.generateFloydTriangle(1)); + } + + @Test + public void testGenerateFloydTriangleWithZeroRows() { + List<List<Integer>> expectedOutput = emptyList(); + assertEquals(expectedOutput, FloydTriangle.generateFloydTriangle(0)); + } + + @Test + public void testGenerateFloydTriangleWithNegativeRows() { + List<List<Integer>> expectedOutput = emptyList(); + assertEquals(expectedOutput, FloydTriangle.generateFloydTriangle(-3)); + } + + @Test + public void testGenerateFloydTriangleWithMultipleRows() { + List<List<Integer>> expectedOutput = Arrays.asList(singletonList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6), Arrays.asList(7, 8, 9, 10), Arrays.asList(11, 12, 13, 14, 15)); + assertEquals(expectedOutput, FloydTriangle.generateFloydTriangle(5)); + } + + @Test + public void testGenerateFloydTriangleWithMoreMultipleRows() { + List<List<Integer>> expectedOutput = Arrays.asList(singletonList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6), Arrays.asList(7, 8, 9, 10), Arrays.asList(11, 12, 13, 14, 15), Arrays.asList(16, 17, 18, 19, 20, 21), Arrays.asList(22, 23, 24, 25, 26, 27, 28)); + assertEquals(expectedOutput, FloydTriangle.generateFloydTriangle(7)); + } +} diff --git a/src/test/java/com/thealgorithms/others/KadaneAlogrithmTest.java b/src/test/java/com/thealgorithms/others/KadaneAlogrithmTest.java new file mode 100644 index 000000000000..25b211657c5d --- /dev/null +++ b/src/test/java/com/thealgorithms/others/KadaneAlogrithmTest.java @@ -0,0 +1,57 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.thealgorithms.dynamicprogramming.KadaneAlgorithm; +import org.junit.jupiter.api.Test; + +public class KadaneAlogrithmTest { + + @Test + void testForOneElement() { + int[] a = {-1}; + assertTrue(KadaneAlgorithm.maxSum(a, -1)); + } + + @Test + void testForTwoElements() { + int[] a = {-2, 1}; + assertTrue(KadaneAlgorithm.maxSum(a, 1)); + } + + @Test + void testForThreeElements() { + int[] a = {5, 3, 12}; + assertTrue(KadaneAlgorithm.maxSum(a, 20)); + } + + @Test + void testForFourElements() { + int[] a = {-1, -3, -7, -4}; + assertTrue(KadaneAlgorithm.maxSum(a, -1)); + } + + @Test + void testForFiveElements() { + int[] a = {4, 5, 3, 0, 2}; + assertTrue(KadaneAlgorithm.maxSum(a, 14)); + } + + @Test + void testForSixElements() { + int[] a = {-43, -45, 47, 12, 87, -13}; + assertTrue(KadaneAlgorithm.maxSum(a, 146)); + } + + @Test + void testForSevenElements() { + int[] a = {9, 8, 2, 23, 13, 6, 7}; + assertTrue(KadaneAlgorithm.maxSum(a, 68)); + } + + @Test + void testForEightElements() { + int[] a = {9, -5, -5, -2, 4, 5, 0, 1}; + assertTrue(KadaneAlgorithm.maxSum(a, 10)); + } +} diff --git a/src/test/java/com/thealgorithms/others/LineSweepTest.java b/src/test/java/com/thealgorithms/others/LineSweepTest.java new file mode 100644 index 000000000000..59fd0fafb068 --- /dev/null +++ b/src/test/java/com/thealgorithms/others/LineSweepTest.java @@ -0,0 +1,37 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class LineSweepTest { + private record OverlapTestCase(int[][] ranges, boolean expected) { + } + + private record MaximumEndPointTestCase(int[][] ranges, int expected) { + } + + @ParameterizedTest + @MethodSource("provideOverlapTestData") + void testIsOverlap(OverlapTestCase testCase) { + assertEquals(testCase.expected(), LineSweep.isOverlap(testCase.ranges())); + } + + private static Stream<Arguments> provideOverlapTestData() { + return Stream.of(Arguments.of(new OverlapTestCase(new int[][] {{0, 10}, {7, 20}, {15, 24}}, true)), Arguments.of(new OverlapTestCase(new int[][] {{0, 10}, {11, 20}, {21, 24}}, false)), Arguments.of(new OverlapTestCase(new int[][] {{0, 10}, {10, 20}, {21, 24}}, true)), + Arguments.of(new OverlapTestCase(new int[][] {{5, 10}}, false)), Arguments.of(new OverlapTestCase(new int[][] {{1, 5}, {1, 5}, {1, 5}}, true)), Arguments.of(new OverlapTestCase(new int[][] {{1, 1}, {2, 2}, {3, 3}}, false)), Arguments.of(new OverlapTestCase(new int[][] {}, false))); + } + + @ParameterizedTest + @MethodSource("provideMaximumEndPointTestData") + void testFindMaximumEndPoint(MaximumEndPointTestCase testCase) { + assertEquals(testCase.expected(), LineSweep.findMaximumEndPoint(testCase.ranges())); + } + + private static Stream<Arguments> provideMaximumEndPointTestData() { + return Stream.of(Arguments.of(new MaximumEndPointTestCase(new int[][] {{10, 20}, {1, 100}, {14, 16}, {1, 8}}, 100))); + } +} diff --git a/src/test/java/com/thealgorithms/others/LinkListSortTest.java b/src/test/java/com/thealgorithms/others/LinkListSortTest.java new file mode 100644 index 000000000000..100593b1f756 --- /dev/null +++ b/src/test/java/com/thealgorithms/others/LinkListSortTest.java @@ -0,0 +1,57 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.thealgorithms.sorts.LinkListSort; +import org.junit.jupiter.api.Test; + +public class LinkListSortTest { + + @Test + void testForOneElement() { + int[] a = {56}; + assertTrue(LinkListSort.isSorted(a, 2)); + } + + @Test + void testForTwoElements() { + int[] a = {6, 4}; + assertTrue(LinkListSort.isSorted(a, 1)); + } + + @Test + void testForThreeElements() { + int[] a = {875, 253, 12}; + assertTrue(LinkListSort.isSorted(a, 3)); + } + + @Test + void testForFourElements() { + int[] a = {86, 32, 87, 13}; + assertTrue(LinkListSort.isSorted(a, 1)); + } + + @Test + void testForFiveElements() { + int[] a = {6, 5, 3, 0, 9}; + assertTrue(LinkListSort.isSorted(a, 1)); + } + + @Test + void testForSixElements() { + int[] a = {9, 65, 432, 32, 47, 327}; + assertTrue(LinkListSort.isSorted(a, 3)); + } + + @Test + void testForSevenElements() { + int[] a = {6, 4, 2, 1, 3, 6, 7}; + assertTrue(LinkListSort.isSorted(a, 1)); + } + + @Test + void testForEightElements() { + int[] a = {123, 234, 145, 764, 322, 367, 768, 34}; + assertTrue(LinkListSort.isSorted(a, 2)); + } +} diff --git a/src/test/java/com/thealgorithms/others/LowestBasePalindromeTest.java b/src/test/java/com/thealgorithms/others/LowestBasePalindromeTest.java new file mode 100644 index 000000000000..1014f39a26bc --- /dev/null +++ b/src/test/java/com/thealgorithms/others/LowestBasePalindromeTest.java @@ -0,0 +1,77 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class LowestBasePalindromeTest { + + @ParameterizedTest + @MethodSource("provideListsForIsPalindromicPositive") + public void testIsPalindromicPositive(List<Integer> list) { + assertTrue(LowestBasePalindrome.isPalindromic(list)); + } + + @ParameterizedTest + @MethodSource("provideListsForIsPalindromicNegative") + public void testIsPalindromicNegative(List<Integer> list) { + assertFalse(LowestBasePalindrome.isPalindromic(list)); + } + + @ParameterizedTest + @MethodSource("provideNumbersAndBasesForIsPalindromicInBasePositive") + public void testIsPalindromicInBasePositive(int number, int base) { + assertTrue(LowestBasePalindrome.isPalindromicInBase(number, base)); + } + + @ParameterizedTest + @MethodSource("provideNumbersAndBasesForIsPalindromicInBaseNegative") + public void testIsPalindromicInBaseNegative(int number, int base) { + assertFalse(LowestBasePalindrome.isPalindromicInBase(number, base)); + } + + @ParameterizedTest + @MethodSource("provideNumbersAndBasesForExceptions") + public void testIsPalindromicInBaseThrowsException(int number, int base) { + org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class, () -> LowestBasePalindrome.isPalindromicInBase(number, base)); + } + + @ParameterizedTest + @MethodSource("provideNumbersForLowestBasePalindrome") + public void testLowestBasePalindrome(int number, int expectedBase) { + assertEquals(expectedBase, LowestBasePalindrome.lowestBasePalindrome(number)); + } + + private static Stream<Arguments> provideListsForIsPalindromicPositive() { + return Stream.of(Arguments.of(new ArrayList<>()), Arguments.of(new ArrayList<>(List.of(1))), Arguments.of(new ArrayList<>(Arrays.asList(1, 1))), Arguments.of(new ArrayList<>(Arrays.asList(1, 2, 1))), Arguments.of(new ArrayList<>(Arrays.asList(1, 2, 2, 1)))); + } + + private static Stream<Arguments> provideListsForIsPalindromicNegative() { + return Stream.of(Arguments.of(new ArrayList<>(Arrays.asList(1, 2))), Arguments.of(new ArrayList<>(Arrays.asList(1, 2, 1, 1)))); + } + + private static Stream<Arguments> provideNumbersAndBasesForIsPalindromicInBasePositive() { + return Stream.of(Arguments.of(101, 10), Arguments.of(1, 190), Arguments.of(0, 11), Arguments.of(10101, 10), Arguments.of(23, 22)); + } + + private static Stream<Arguments> provideNumbersAndBasesForIsPalindromicInBaseNegative() { + return Stream.of(Arguments.of(1010, 10), Arguments.of(123, 10)); + } + + private static Stream<Arguments> provideNumbersAndBasesForExceptions() { + return Stream.of(Arguments.of(-1, 5), Arguments.of(10, 1)); + } + + private static Stream<Arguments> provideNumbersForLowestBasePalindrome() { + return Stream.of(Arguments.of(0, 2), Arguments.of(1, 2), Arguments.of(2, 3), Arguments.of(3, 2), Arguments.of(10, 3), Arguments.of(11, 10), Arguments.of(15, 2), Arguments.of(39, 12), Arguments.of(44, 10), Arguments.of(58, 28), Arguments.of(69, 22), Arguments.of(79, 78), Arguments.of(87, 28), + Arguments.of(90, 14), Arguments.of(5591, 37), Arguments.of(5895, 130), Arguments.of(9950, 198), Arguments.of(9974, 4986)); + } +} diff --git a/src/test/java/com/thealgorithms/others/MaximumSlidingWindowTest.java b/src/test/java/com/thealgorithms/others/MaximumSlidingWindowTest.java new file mode 100644 index 000000000000..9209136a5af3 --- /dev/null +++ b/src/test/java/com/thealgorithms/others/MaximumSlidingWindowTest.java @@ -0,0 +1,63 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class MaximumSlidingWindowTest { + + MaximumSlidingWindow msw; + int[] nums; + int k; + + @BeforeEach + void setUp() { + msw = new MaximumSlidingWindow(); // Initialize the MaximumSlidingWindow object + } + + // Test for a simple sliding window case + @Test + void testMaxSlidingWindowSimpleCase() { + nums = new int[] {1, 3, -1, -3, 5, 3, 6, 7}; + k = 3; + int[] expected = {3, 3, 5, 5, 6, 7}; + assertArrayEquals(expected, msw.maxSlidingWindow(nums, k)); + } + + // Test when window size is 1 (output should be the array itself) + @Test + void testMaxSlidingWindowWindowSizeOne() { + nums = new int[] {4, 2, 12, 11, -5}; + k = 1; + int[] expected = {4, 2, 12, 11, -5}; + assertArrayEquals(expected, msw.maxSlidingWindow(nums, k)); + } + + // Test when the window size is equal to the array length (output should be a single max element) + @Test + void testMaxSlidingWindowWindowSizeEqualsArrayLength() { + nums = new int[] {4, 2, 12, 11, -5}; + k = nums.length; + int[] expected = {12}; // Maximum of the entire array + assertArrayEquals(expected, msw.maxSlidingWindow(nums, k)); + } + + // Test when the input array is empty + @Test + void testMaxSlidingWindowEmptyArray() { + nums = new int[] {}; + k = 3; + int[] expected = {}; + assertArrayEquals(expected, msw.maxSlidingWindow(nums, k)); + } + + // Test when the window size is larger than the array (should return empty) + @Test + void testMaxSlidingWindowWindowSizeLargerThanArray() { + nums = new int[] {1, 2, 3}; + k = 5; + int[] expected = {}; // Window size is too large, so no result + assertArrayEquals(expected, msw.maxSlidingWindow(nums, k)); + } +} diff --git a/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java b/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java new file mode 100644 index 000000000000..f360e3f53546 --- /dev/null +++ b/src/test/java/com/thealgorithms/others/MaximumSumOfDistinctSubarraysWithLengthKTest.java @@ -0,0 +1,22 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class MaximumSumOfDistinctSubarraysWithLengthKTest { + + @ParameterizedTest + @MethodSource("inputStream") + void testMaximumSubarraySum(int expected, int k, int[] arr) { + assertEquals(expected, MaximumSumOfDistinctSubarraysWithLengthK.maximumSubarraySum(k, arr)); + } + + private static Stream<Arguments> inputStream() { + return Stream.of(Arguments.of(15, 3, new int[] {1, 5, 4, 2, 9, 9, 9}), Arguments.of(0, 3, new int[] {4, 4, 4}), Arguments.of(12, 3, new int[] {9, 9, 9, 1, 2, 3}), Arguments.of(0, 0, new int[] {9, 9, 9}), Arguments.of(0, 5, new int[] {9, 9, 9}), Arguments.of(9, 1, new int[] {9, 2, 3, 7}), + Arguments.of(15, 5, new int[] {1, 2, 3, 4, 5}), Arguments.of(6, 3, new int[] {-1, 2, 3, 1, -2, 4}), Arguments.of(10, 1, new int[] {10}), Arguments.of(0, 2, new int[] {7, 7, 7, 7}), Arguments.of(0, 3, new int[] {}), Arguments.of(0, 10, new int[] {1, 2, 3})); + } +} diff --git a/src/test/java/com/thealgorithms/others/NewManShanksPrimeTest.java b/src/test/java/com/thealgorithms/others/NewManShanksPrimeTest.java new file mode 100644 index 000000000000..3b657e441b1c --- /dev/null +++ b/src/test/java/com/thealgorithms/others/NewManShanksPrimeTest.java @@ -0,0 +1,49 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.thealgorithms.dynamicprogramming.NewManShanksPrime; +import org.junit.jupiter.api.Test; + +public class NewManShanksPrimeTest { + + @Test + void testOne() { + assertTrue(NewManShanksPrime.nthManShanksPrime(1, 1)); + } + + @Test + void testTwo() { + assertTrue(NewManShanksPrime.nthManShanksPrime(2, 3)); + } + + @Test + void testThree() { + assertTrue(NewManShanksPrime.nthManShanksPrime(3, 7)); + } + + @Test + void testFour() { + assertTrue(NewManShanksPrime.nthManShanksPrime(4, 17)); + } + + @Test + void testFive() { + assertTrue(NewManShanksPrime.nthManShanksPrime(5, 41)); + } + + @Test + void testSix() { + assertTrue(NewManShanksPrime.nthManShanksPrime(6, 99)); + } + + @Test + void testSeven() { + assertTrue(NewManShanksPrime.nthManShanksPrime(7, 239)); + } + + @Test + void testEight() { + assertTrue(NewManShanksPrime.nthManShanksPrime(8, 577)); + } +} diff --git a/src/test/java/com/thealgorithms/others/NextFitTest.java b/src/test/java/com/thealgorithms/others/NextFitTest.java new file mode 100644 index 000000000000..75fb3ab7c261 --- /dev/null +++ b/src/test/java/com/thealgorithms/others/NextFitTest.java @@ -0,0 +1,69 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import org.junit.jupiter.api.Test; + +/** + * author Alexandros Lemonaris + */ +class NextFitCPUTest { + + int[] sizeOfBlocks; + int[] sizeOfProcesses; + ArrayList<Integer> memAllocation = new ArrayList<>(); + ArrayList<Integer> testMemAllocation; + MemoryManagementAlgorithms nextFit = new NextFit(); + + @Test + void testFitForUseOfOneBlock() { + // test1 - third process does not fit because of algorithms procedure + sizeOfBlocks = new int[] {5, 12, 17, 10}; + sizeOfProcesses = new int[] {10, 5, 15, 2}; + memAllocation = nextFit.fitProcess(sizeOfBlocks, sizeOfProcesses); + testMemAllocation = new ArrayList<>(Arrays.asList(1, 2, -255, 2)); + assertEquals(testMemAllocation, memAllocation); + } + + @Test + void testFitForEqualProcecesses() { + // test2 + sizeOfBlocks = new int[] {5, 12, 17, 10}; + sizeOfProcesses = new int[] {10, 10, 10, 10}; + memAllocation = nextFit.fitProcess(sizeOfBlocks, sizeOfProcesses); + testMemAllocation = new ArrayList<>(Arrays.asList(1, 2, 3, -255)); + assertEquals(testMemAllocation, memAllocation); + } + + @Test + void testFitForNoEmptyBlockCell() { + // test3 for more processes than blocks - no empty space left to none of the blocks + sizeOfBlocks = new int[] {5, 12, 17}; + sizeOfProcesses = new int[] {5, 12, 10, 7}; + memAllocation = nextFit.fitProcess(sizeOfBlocks, sizeOfProcesses); + testMemAllocation = new ArrayList<>(Arrays.asList(0, 1, 2, 2)); + assertEquals(testMemAllocation, memAllocation); + } + + @Test + void testFitForSameInputDifferentQuery() { + // test4 for more processes than blocks - one element does not fit due to input series + sizeOfBlocks = new int[] {5, 12, 17}; + sizeOfProcesses = new int[] {5, 7, 10, 12}; + memAllocation = nextFit.fitProcess(sizeOfBlocks, sizeOfProcesses); + testMemAllocation = new ArrayList<>(Arrays.asList(0, 1, 2, -255)); + assertEquals(testMemAllocation, memAllocation); + } + + @Test + void testFitForMoreBlocksNoFit() { + // test5 for more blocks than processes + sizeOfBlocks = new int[] {5, 4, -1, 3, 6}; + sizeOfProcesses = new int[] {10, 11}; + memAllocation = nextFit.fitProcess(sizeOfBlocks, sizeOfProcesses); + testMemAllocation = new ArrayList<>(Arrays.asList(-255, -255)); + assertEquals(testMemAllocation, memAllocation); + } +} diff --git a/src/test/java/com/thealgorithms/others/PasswordGenTest.java b/src/test/java/com/thealgorithms/others/PasswordGenTest.java new file mode 100644 index 000000000000..76492556e75f --- /dev/null +++ b/src/test/java/com/thealgorithms/others/PasswordGenTest.java @@ -0,0 +1,53 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class PasswordGenTest { + + @Test + public void failGenerationWithSameMinMaxLengthTest() { + int length = 10; + assertThrows(IllegalArgumentException.class, () -> PasswordGen.generatePassword(length, length)); + } + + @Test + public void generateOneCharacterPassword() { + String tempPassword = PasswordGen.generatePassword(1, 2); + assertTrue(tempPassword.length() == 1); + } + + @Test + public void failGenerationWithMinLengthSmallerThanMaxLengthTest() { + int minLength = 10; + int maxLength = 5; + assertThrows(IllegalArgumentException.class, () -> PasswordGen.generatePassword(minLength, maxLength)); + } + + @Test + public void generatePasswordNonEmptyTest() { + String tempPassword = PasswordGen.generatePassword(8, 16); + assertTrue(tempPassword.length() != 0); + } + + @Test + public void testGeneratePasswordWithMinGreaterThanMax() { + Exception exception = assertThrows(IllegalArgumentException.class, () -> PasswordGen.generatePassword(12, 8)); + assertEquals("Incorrect length parameters: minLength must be <= maxLength and both must be > 0", exception.getMessage()); + } + + @Test + public void testGeneratePasswordWithNegativeLength() { + Exception exception = assertThrows(IllegalArgumentException.class, () -> PasswordGen.generatePassword(-5, 10)); + assertEquals("Incorrect length parameters: minLength must be <= maxLength and both must be > 0", exception.getMessage()); + } + + @Test + public void testGeneratePasswordWithZeroLength() { + Exception exception = assertThrows(IllegalArgumentException.class, () -> PasswordGen.generatePassword(0, 0)); + assertEquals("Incorrect length parameters: minLength must be <= maxLength and both must be > 0", exception.getMessage()); + } +} diff --git a/src/test/java/com/thealgorithms/others/QueueUsingTwoStacksTest.java b/src/test/java/com/thealgorithms/others/QueueUsingTwoStacksTest.java new file mode 100644 index 000000000000..4901b9c0d3a1 --- /dev/null +++ b/src/test/java/com/thealgorithms/others/QueueUsingTwoStacksTest.java @@ -0,0 +1,145 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.EmptyStackException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class QueueUsingTwoStacksTest { + + private QueueUsingTwoStacks queue; + + @BeforeEach + void setUp() { + queue = new QueueUsingTwoStacks(); + } + + @Test + void testIsEmptyInitially() { + assertTrue(queue.isEmpty(), "Queue should be empty initially"); + } + + @Test + void testInsertSingleElement() { + queue.insert(1); + assertFalse(queue.isEmpty(), "Queue should not be empty after inserting an element"); + assertEquals(1, queue.peekFront(), "The front element should be the inserted element"); + } + + @Test + void testRemoveSingleElement() { + queue.insert(1); + assertEquals(1, queue.remove(), "Removing should return the first inserted element"); + assertTrue(queue.isEmpty(), "Queue should be empty after removing the only element"); + } + + @Test + void testRemoveMultipleElements() { + queue.insert(1); + queue.insert(2); + queue.insert(3); + assertEquals(1, queue.remove(), "First removed element should be the first inserted element"); + assertEquals(2, queue.remove(), "Second removed element should be the second inserted element"); + assertEquals(3, queue.remove(), "Third removed element should be the third inserted element"); + assertTrue(queue.isEmpty(), "Queue should be empty after removing all elements"); + } + + @Test + void testPeekFrontWithMultipleElements() { + queue.insert(1); + queue.insert(2); + queue.insert(3); + assertEquals(1, queue.peekFront(), "The front element should be the first inserted element"); + } + + @Test + void testPeekBackWithMultipleElements() { + queue.insert(1); + queue.insert(2); + queue.insert(3); + assertEquals(3, queue.peekBack(), "The back element should be the last inserted element"); + } + + @Test + void testPeekFrontAfterRemovals() { + queue.insert(1); + queue.insert(2); + queue.insert(3); + queue.remove(); + assertEquals(2, queue.peekFront(), "After removing one element, the front should be the second element"); + } + + @Test + void testIsEmptyAfterRemovals() { + queue.insert(1); + queue.insert(2); + queue.remove(); + queue.remove(); + assertTrue(queue.isEmpty(), "Queue should be empty after removing all elements"); + } + + @Test + void testRemoveFromEmptyQueue() { + org.junit.jupiter.api.Assertions.assertThrows(EmptyStackException.class, queue::remove, "Removing from an empty queue should throw an exception"); + } + + @Test + void testPeekFrontFromEmptyQueue() { + org.junit.jupiter.api.Assertions.assertThrows(EmptyStackException.class, queue::peekFront, "Peeking front from an empty queue should throw an exception"); + } + + @Test + void testPeekBackFromEmptyQueue() { + org.junit.jupiter.api.Assertions.assertThrows(EmptyStackException.class, queue::peekBack, "Peeking back from an empty queue should throw an exception"); + } + + @Test + void testIsInStackEmptyInitially() { + assertTrue(queue.isInStackEmpty(), "inStack should be empty initially"); + } + + @Test + void testIsOutStackEmptyInitially() { + assertTrue(queue.isOutStackEmpty(), "outStack should be empty initially"); + } + + @Test + void testIsInStackEmptyAfterInsertion() { + queue.insert(1); + assertFalse(queue.isInStackEmpty(), "inStack should not be empty after an insertion"); + } + + @Test + void testIsOutStackEmptyAfterInsertion() { + queue.insert(1); + assertTrue(queue.isOutStackEmpty(), "outStack should still be empty after an insertion"); + } + + @Test + void testIsOutStackEmptyAfterRemoval() { + queue.insert(1); + queue.remove(); + assertTrue(queue.isOutStackEmpty(), "outStack should be empty after removing the only element"); + } + + @Test + void testIsInStackEmptyAfterMultipleRemovals() { + queue.insert(1); + queue.insert(2); + queue.remove(); + queue.remove(); + assertTrue(queue.isInStackEmpty(), "inStack should be empty after removing all elements"); + } + + @Test + void testIsOutStackEmptyAfterMultipleRemovals() { + queue.insert(1); + queue.insert(2); + queue.remove(); + queue.remove(); + assertTrue(queue.isOutStackEmpty(), "outStack should be empty after removing all elements"); + } +} diff --git a/src/test/java/com/thealgorithms/others/RemoveDuplicateFromStringTest.java b/src/test/java/com/thealgorithms/others/RemoveDuplicateFromStringTest.java new file mode 100644 index 000000000000..3401a51c56c9 --- /dev/null +++ b/src/test/java/com/thealgorithms/others/RemoveDuplicateFromStringTest.java @@ -0,0 +1,44 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.Test; + +class RemoveDuplicateFromStringTest { + + @Test + void testEmptyString() { + assertEquals("", RemoveDuplicateFromString.removeDuplicate("")); + } + + @Test + void testNullString() { + assertNull(RemoveDuplicateFromString.removeDuplicate(null)); + } + + @Test + void testSingleCharacterString() { + assertEquals("a", RemoveDuplicateFromString.removeDuplicate("a")); + } + + @Test + void testStringWithNoDuplicates() { + assertEquals("abc", RemoveDuplicateFromString.removeDuplicate("abc")); + } + + @Test + void testStringWithDuplicates() { + assertEquals("abcd", RemoveDuplicateFromString.removeDuplicate("aabbbccccddddd")); + } + + @Test + void testStringWithAllSameCharacters() { + assertEquals("a", RemoveDuplicateFromString.removeDuplicate("aaaaa")); + } + + @Test + void testStringWithMixedCase() { + assertEquals("abAB", RemoveDuplicateFromString.removeDuplicate("aabABAB")); + } +} diff --git a/src/test/java/com/thealgorithms/others/ReverseStackUsingRecursionTest.java b/src/test/java/com/thealgorithms/others/ReverseStackUsingRecursionTest.java new file mode 100644 index 000000000000..23b99ae87d35 --- /dev/null +++ b/src/test/java/com/thealgorithms/others/ReverseStackUsingRecursionTest.java @@ -0,0 +1,58 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Stack; +import org.junit.jupiter.api.Test; + +public class ReverseStackUsingRecursionTest { + + @Test + void testReverseWithMultipleElements() { + Stack<Integer> stack = new Stack<>(); + for (int i = 0; i < 5; i++) { + stack.push(i); + } + + ReverseStackUsingRecursion.reverse(stack); + + for (int i = 0; i < 5; i++) { + assertEquals(i, stack.pop()); + } + assertTrue(stack.isEmpty()); + } + + @Test + void testReverseWithSingleElement() { + Stack<Integer> stack = new Stack<>(); + stack.push(1); + + ReverseStackUsingRecursion.reverse(stack); + + assertEquals(1, stack.pop()); + assertTrue(stack.isEmpty()); + } + + @Test + void testReverseWithEmptyStack() { + Stack<Integer> stack = new Stack<>(); + + ReverseStackUsingRecursion.reverse(stack); + + assertTrue(stack.isEmpty()); + } + + @Test + void testReverseWithNullStack() { + Stack<Integer> stack = null; + + Exception exception = assertThrows(IllegalArgumentException.class, () -> ReverseStackUsingRecursion.reverse(stack)); + + String expectedMessage = "Stack cannot be null"; + String actualMessage = exception.getMessage(); + + assertTrue(actualMessage.contains(expectedMessage)); + } +} diff --git a/src/test/java/com/thealgorithms/others/SkylineProblemTest.java b/src/test/java/com/thealgorithms/others/SkylineProblemTest.java new file mode 100644 index 000000000000..1ed5ced709c1 --- /dev/null +++ b/src/test/java/com/thealgorithms/others/SkylineProblemTest.java @@ -0,0 +1,86 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import org.junit.jupiter.api.Test; + +public class SkylineProblemTest { + + @Test + public void testSingleBuildingSkyline() { + SkylineProblem skylineProblem = new SkylineProblem(); + skylineProblem.building = new SkylineProblem.Building[1]; + skylineProblem.add(2, 10, 9); + + ArrayList<SkylineProblem.Skyline> result = skylineProblem.findSkyline(0, 0); + + assertEquals(2, result.get(0).coordinates); + assertEquals(10, result.get(0).height); + assertEquals(9, result.get(1).coordinates); + assertEquals(0, result.get(1).height); + } + + @Test + public void testTwoBuildingsSkyline() { + SkylineProblem skylineProblem = new SkylineProblem(); + skylineProblem.building = new SkylineProblem.Building[2]; + skylineProblem.add(1, 11, 5); + skylineProblem.add(2, 6, 7); + + ArrayList<SkylineProblem.Skyline> result = skylineProblem.findSkyline(0, 1); + + // Expected skyline points: (1, 11), (5, 6), (7, 0) + assertEquals(1, result.get(0).coordinates); + assertEquals(11, result.get(0).height); + assertEquals(5, result.get(1).coordinates); + assertEquals(6, result.get(1).height); + assertEquals(7, result.get(2).coordinates); + assertEquals(0, result.get(2).height); + } + + @Test + public void testMergeSkyline() { + SkylineProblem skylineProblem = new SkylineProblem(); + ArrayList<SkylineProblem.Skyline> sky1 = new ArrayList<>(); + ArrayList<SkylineProblem.Skyline> sky2 = new ArrayList<>(); + + sky1.add(skylineProblem.new Skyline(2, 10)); + sky1.add(skylineProblem.new Skyline(9, 0)); + + sky2.add(skylineProblem.new Skyline(3, 15)); + sky2.add(skylineProblem.new Skyline(7, 0)); + + ArrayList<SkylineProblem.Skyline> result = skylineProblem.mergeSkyline(sky1, sky2); + + // Expected merged skyline: (2, 10), (3, 15), (7, 10), (9, 0) + assertEquals(2, result.get(0).coordinates); + assertEquals(10, result.get(0).height); + assertEquals(3, result.get(1).coordinates); + assertEquals(15, result.get(1).height); + assertEquals(7, result.get(2).coordinates); + assertEquals(10, result.get(2).height); + assertEquals(9, result.get(3).coordinates); + assertEquals(0, result.get(3).height); + } + + @Test + public void testMultipleBuildingsSkyline() { + SkylineProblem skylineProblem = new SkylineProblem(); + skylineProblem.building = new SkylineProblem.Building[3]; + skylineProblem.add(1, 10, 5); + skylineProblem.add(2, 15, 7); + skylineProblem.add(3, 12, 9); + + ArrayList<SkylineProblem.Skyline> result = skylineProblem.findSkyline(0, 2); + + assertEquals(1, result.get(0).coordinates); + assertEquals(10, result.get(0).height); + assertEquals(2, result.get(1).coordinates); + assertEquals(15, result.get(1).height); + assertEquals(7, result.get(2).coordinates); + assertEquals(12, result.get(2).height); + assertEquals(9, result.get(3).coordinates); + assertEquals(0, result.get(3).height); + } +} diff --git a/src/test/java/com/thealgorithms/others/TestPrintMatrixInSpiralOrder.java b/src/test/java/com/thealgorithms/others/TestPrintMatrixInSpiralOrder.java new file mode 100644 index 000000000000..d35d4bb60c73 --- /dev/null +++ b/src/test/java/com/thealgorithms/others/TestPrintMatrixInSpiralOrder.java @@ -0,0 +1,26 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertIterableEquals; + +import java.util.List; +import org.junit.jupiter.api.Test; + +public class TestPrintMatrixInSpiralOrder { + @Test + public void testOne() { + int[][] matrix = {{3, 4, 5, 6, 7}, {8, 9, 10, 11, 12}, {14, 15, 16, 17, 18}, {23, 24, 25, 26, 27}, {30, 31, 32, 33, 34}}; + var printClass = new PrintAMatrixInSpiralOrder(); + List<Integer> res = printClass.print(matrix, matrix.length, matrix[0].length); + List<Integer> list = List.of(3, 4, 5, 6, 7, 12, 18, 27, 34, 33, 32, 31, 30, 23, 14, 8, 9, 10, 11, 17, 26, 25, 24, 15, 16); + assertIterableEquals(res, list); + } + + @Test + public void testTwo() { + int[][] matrix = {{2, 2}}; + var printClass = new PrintAMatrixInSpiralOrder(); + List<Integer> res = printClass.print(matrix, matrix.length, matrix[0].length); + List<Integer> list = List.of(2, 2); + assertIterableEquals(res, list); + } +} diff --git a/src/test/java/com/thealgorithms/others/TwoPointersTest.java b/src/test/java/com/thealgorithms/others/TwoPointersTest.java new file mode 100644 index 000000000000..3a174e0cd19e --- /dev/null +++ b/src/test/java/com/thealgorithms/others/TwoPointersTest.java @@ -0,0 +1,72 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class TwoPointersTest { + + @Test + void testPositivePairExists() { + int[] arr = {2, 6, 9, 22, 121}; + int key = 28; + assertTrue(TwoPointers.isPairedSum(arr, key)); + } + + @Test + void testNegativePairExists() { + int[] arr = {-12, -1, 0, 8, 12}; + int key = 0; + assertTrue(TwoPointers.isPairedSum(arr, key)); + } + + @Test + void testPairDoesNotExist() { + int[] arr = {0, 12, 12, 35, 152}; + int key = 13; + assertFalse(TwoPointers.isPairedSum(arr, key)); + } + + @Test + void testNegativeSumPair() { + int[] arr = {-10, -3, 1, 2, 5, 9}; + int key = -8; + assertTrue(TwoPointers.isPairedSum(arr, key)); + } + + @Test + void testPairDoesNotExistWithPositiveSum() { + int[] arr = {1, 2, 3, 4, 5}; + int key = 10; + assertFalse(TwoPointers.isPairedSum(arr, key)); + } + + @Test + void testEmptyArray() { + int[] arr = {}; + int key = 5; + assertFalse(TwoPointers.isPairedSum(arr, key)); + } + + @Test + void testSingleElementArray() { + int[] arr = {5}; + int key = 5; + assertFalse(TwoPointers.isPairedSum(arr, key)); + } + + @Test + void testArrayWithDuplicateElements() { + int[] arr = {1, 1, 3, 5, 5}; + int key = 6; + assertTrue(TwoPointers.isPairedSum(arr, key)); + } + + @Test + void testPairExistsAtEdges() { + int[] arr = {1, 3, 4, 7, 8}; + int key = 9; + assertTrue(TwoPointers.isPairedSum(arr, key)); + } +} diff --git a/src/test/java/com/thealgorithms/others/WorstFitCPUTest.java b/src/test/java/com/thealgorithms/others/WorstFitCPUTest.java new file mode 100644 index 000000000000..eb69f6056132 --- /dev/null +++ b/src/test/java/com/thealgorithms/others/WorstFitCPUTest.java @@ -0,0 +1,79 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import org.junit.jupiter.api.Test; + +/** + * author Alexandros Lemonaris + */ +class WorstFitCPUTest { + + int[] sizeOfBlocks; + int[] sizeOfProcesses; + ArrayList<Integer> memAllocation = new ArrayList<>(); + ArrayList<Integer> testMemAllocation; + MemoryManagementAlgorithms worstFit = new WorstFitCPU(); + + @Test + void testFitForUseOfOneBlock() { + // test1 + sizeOfBlocks = new int[] {5, 12, 17, 10}; + sizeOfProcesses = new int[] {10, 5, 15, 2}; + memAllocation = worstFit.fitProcess(sizeOfBlocks, sizeOfProcesses); + testMemAllocation = new ArrayList<>(Arrays.asList(2, 1, -255, 3)); + assertEquals(testMemAllocation, memAllocation); + } + + @Test + void testFitForEqualProcecesses() { + // test2 + sizeOfBlocks = new int[] {5, 12, 17, 10}; + sizeOfProcesses = new int[] {10, 10, 10, 10}; + memAllocation = worstFit.fitProcess(sizeOfBlocks, sizeOfProcesses); + testMemAllocation = new ArrayList<>(Arrays.asList(2, 1, 3, -255)); + assertEquals(testMemAllocation, memAllocation); + } + + @Test + void testFitForNoEmptyBlockCell() { + // test3 - could suits best, bad use of memory allocation due to worstFit algorithm + sizeOfBlocks = new int[] {5, 12, 17}; + sizeOfProcesses = new int[] {5, 12, 10, 7}; + memAllocation = worstFit.fitProcess(sizeOfBlocks, sizeOfProcesses); + testMemAllocation = new ArrayList<>(Arrays.asList(2, 1, 2, -255)); + assertEquals(testMemAllocation, memAllocation); + } + + @Test + void testFitForSameInputDifferentQuery() { + // test4 same example different series - same results + sizeOfBlocks = new int[] {5, 12, 17}; + sizeOfProcesses = new int[] {5, 7, 10, 12}; + memAllocation = worstFit.fitProcess(sizeOfBlocks, sizeOfProcesses); + testMemAllocation = new ArrayList<>(Arrays.asList(2, 1, 2, -255)); + assertEquals(testMemAllocation, memAllocation); + } + + @Test + void testFitForMoreBlocksNoFit() { + // test5 for more blocks than processes + sizeOfBlocks = new int[] {5, 4, -1, 3, 6}; + sizeOfProcesses = new int[] {10, 11}; + memAllocation = worstFit.fitProcess(sizeOfBlocks, sizeOfProcesses); + testMemAllocation = new ArrayList<>(Arrays.asList(-255, -255)); + assertEquals(testMemAllocation, memAllocation); + } + + @Test + void testFitBadCase() { + // test6 for only two process fit + sizeOfBlocks = new int[] {7, 17, 7, 5, 6}; + sizeOfProcesses = new int[] {8, 10, 10, 8, 8, 8}; + memAllocation = worstFit.fitProcess(sizeOfBlocks, sizeOfProcesses); + testMemAllocation = new ArrayList<>(Arrays.asList(1, -255, -255, 1, -255, -255)); + assertEquals(testMemAllocation, memAllocation); + } +} diff --git a/src/test/java/com/thealgorithms/others/cn/HammingDistanceTest.java b/src/test/java/com/thealgorithms/others/cn/HammingDistanceTest.java new file mode 100644 index 000000000000..669f928cd247 --- /dev/null +++ b/src/test/java/com/thealgorithms/others/cn/HammingDistanceTest.java @@ -0,0 +1,82 @@ +package com.thealgorithms.others.cn; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +public class HammingDistanceTest { + @Test + public void checkForDifferentBits() { + int answer = HammingDistance.compute("000", "011"); + Assertions.assertThat(answer).isEqualTo(2); + } + + /* + + 1 0 1 0 1 + 1 1 1 1 0 + ---------- + 0 1 0 1 1 + + + */ + @Test + public void checkForDifferentBitsLength() { + int answer = HammingDistance.compute("10101", "11110"); + Assertions.assertThat(answer).isEqualTo(3); + } + + @Test + public void checkForSameBits() { + String someBits = "111"; + int answer = HammingDistance.compute(someBits, someBits); + Assertions.assertThat(answer).isEqualTo(0); + } + + @Test + public void checkForLongDataBits() { + int answer = HammingDistance.compute("10010101101010000100110100", "00110100001011001100110101"); + Assertions.assertThat(answer).isEqualTo(7); + } + + @Test + public void mismatchDataBits() { + Exception ex = org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class, () -> { HammingDistance.compute("100010", "00011"); }); + + Assertions.assertThat(ex.getMessage()).contains("must have the same length"); + } + + @Test + public void mismatchDataBits2() { + Exception ex = org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class, () -> { HammingDistance.compute("1", "11"); }); + + Assertions.assertThat(ex.getMessage()).contains("must have the same length"); + } + + @Test + public void checkForLongDataBitsSame() { + String someBits = "10010101101010000100110100"; + int answer = HammingDistance.compute(someBits, someBits); + Assertions.assertThat(answer).isEqualTo(0); + } + + @Test + public void checkForEmptyInput() { + String someBits = ""; + int answer = HammingDistance.compute(someBits, someBits); + Assertions.assertThat(answer).isEqualTo(0); + } + + @Test + public void checkForInputOfLength1() { + String someBits = "0"; + int answer = HammingDistance.compute(someBits, someBits); + Assertions.assertThat(answer).isEqualTo(0); + } + + @Test + public void computeThrowsExceptionWhenInputsAreNotBitStrs() { + Exception ex = org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class, () -> { HammingDistance.compute("1A", "11"); }); + + Assertions.assertThat(ex.getMessage()).contains("must be a binary string"); + } +} diff --git a/src/test/java/com/thealgorithms/puzzlesandgames/SudokuTest.java b/src/test/java/com/thealgorithms/puzzlesandgames/SudokuTest.java new file mode 100644 index 000000000000..7fb96dcf805f --- /dev/null +++ b/src/test/java/com/thealgorithms/puzzlesandgames/SudokuTest.java @@ -0,0 +1,38 @@ +package com.thealgorithms.puzzlesandgames; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class SudokuTest { + + @Test + void testIsSafe2() { + int[][] board = {{3, 0, 6, 5, 0, 8, 4, 0, 0}, {5, 2, 0, 0, 0, 0, 0, 0, 0}, {0, 8, 7, 0, 0, 0, 0, 3, 1}, {0, 0, 3, 0, 1, 0, 0, 8, 0}, {9, 0, 0, 8, 6, 3, 0, 0, 5}, {0, 5, 0, 0, 9, 0, 6, 0, 0}, {1, 3, 0, 0, 0, 0, 2, 5, 0}, {0, 0, 0, 0, 0, 0, 0, 7, 4}, {0, 0, 5, 2, 0, 6, 3, 0, 0}}; + + assertFalse(Sudoku.isSafe(board, 0, 1, 3)); + assertTrue(Sudoku.isSafe(board, 1, 2, 1)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> { Sudoku.isSafe(board, 10, 10, 5); }); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> { Sudoku.isSafe(board, -1, 0, 5); }); + } + + @Test + void testSolveSudoku() { + int[][] board = {{3, 0, 6, 5, 0, 8, 4, 0, 0}, {5, 2, 0, 0, 0, 0, 0, 0, 0}, {0, 8, 7, 0, 0, 0, 0, 3, 1}, {0, 0, 3, 0, 1, 0, 0, 8, 0}, {9, 0, 0, 8, 6, 3, 0, 0, 5}, {0, 5, 0, 0, 9, 0, 6, 0, 0}, {1, 3, 0, 0, 0, 0, 2, 5, 0}, {0, 0, 0, 0, 0, 0, 0, 7, 4}, {0, 0, 5, 2, 0, 6, 3, 0, 0}}; + + assertTrue(Sudoku.solveSudoku(board, board.length)); + assertEquals(1, board[0][1]); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> { Sudoku.solveSudoku(board, 10); }); + assertTrue(Sudoku.solveSudoku(board, -1)); + } + + @Test + void testUnsolvableSudoku() { + int[][] unsolvableBoard = {{5, 1, 6, 8, 4, 9, 7, 3, 2}, {3, 0, 7, 6, 0, 5, 0, 0, 0}, {8, 0, 9, 7, 0, 0, 0, 6, 5}, {1, 3, 5, 0, 6, 0, 9, 0, 7}, {4, 7, 2, 5, 9, 1, 0, 0, 6}, {9, 6, 8, 3, 7, 0, 0, 5, 0}, {2, 5, 3, 1, 8, 6, 0, 7, 4}, {6, 8, 4, 2, 5, 7, 3, 9, 0}, {7, 9, 1, 4, 3, 0, 5, 0, 0}}; + + assertFalse(Sudoku.solveSudoku(unsolvableBoard, unsolvableBoard.length)); + } +} diff --git a/src/test/java/com/thealgorithms/puzzlesandgames/TowerOfHanoiTest.java b/src/test/java/com/thealgorithms/puzzlesandgames/TowerOfHanoiTest.java new file mode 100644 index 000000000000..42669eb03bb4 --- /dev/null +++ b/src/test/java/com/thealgorithms/puzzlesandgames/TowerOfHanoiTest.java @@ -0,0 +1,50 @@ +package com.thealgorithms.puzzlesandgames; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class TowerOfHanoiTest { + + @Test + public void testHanoiWithOneDisc() { + List<String> result = new ArrayList<>(); + TowerOfHanoi.shift(1, "Pole1", "Pole2", "Pole3", result); + + // Expected output for 1 disc + List<String> expected = List.of("Move 1 from Pole1 to Pole3"); + assertEquals(expected, result); + } + + @Test + public void testHanoiWithTwoDiscs() { + List<String> result = new ArrayList<>(); + TowerOfHanoi.shift(2, "Pole1", "Pole2", "Pole3", result); + + // Expected output for 2 discs + List<String> expected = List.of("Move 1 from Pole1 to Pole2", "Move 2 from Pole1 to Pole3", "Move 1 from Pole2 to Pole3"); + assertEquals(expected, result); + } + + @Test + public void testHanoiWithThreeDiscs() { + List<String> result = new ArrayList<>(); + TowerOfHanoi.shift(3, "Pole1", "Pole2", "Pole3", result); + + // Expected output for 3 discs + List<String> expected = List.of("Move 1 from Pole1 to Pole3", "Move 2 from Pole1 to Pole2", "Move 1 from Pole3 to Pole2", "Move 3 from Pole1 to Pole3", "Move 1 from Pole2 to Pole1", "Move 2 from Pole2 to Pole3", "Move 1 from Pole1 to Pole3"); + assertEquals(expected, result); + } + + @Test + public void testHanoiWithZeroDiscs() { + List<String> result = new ArrayList<>(); + TowerOfHanoi.shift(0, "Pole1", "Pole2", "Pole3", result); + + // There should be no moves if there are 0 discs + assertTrue(result.isEmpty()); + } +} diff --git a/src/test/java/com/thealgorithms/puzzlesandgames/WordBoggleTest.java b/src/test/java/com/thealgorithms/puzzlesandgames/WordBoggleTest.java new file mode 100644 index 000000000000..ef5d3c92eb5e --- /dev/null +++ b/src/test/java/com/thealgorithms/puzzlesandgames/WordBoggleTest.java @@ -0,0 +1,57 @@ +package com.thealgorithms.puzzlesandgames; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class WordBoggleTest { + private char[][] board; + + @BeforeEach + void setup() { + board = new char[][] { + {'t', 'h', 'i', 's', 'i', 's', 'a'}, + {'s', 'i', 'm', 'p', 'l', 'e', 'x'}, + {'b', 'x', 'x', 'x', 'x', 'e', 'b'}, + {'x', 'o', 'g', 'g', 'l', 'x', 'o'}, + {'x', 'x', 'x', 'D', 'T', 'r', 'a'}, + {'R', 'E', 'P', 'E', 'A', 'd', 'x'}, + {'x', 'x', 'x', 'x', 'x', 'x', 'x'}, + {'N', 'O', 'T', 'R', 'E', '_', 'P'}, + {'x', 'x', 'D', 'E', 'T', 'A', 'E'}, + }; + } + + @ParameterizedTest + @MethodSource("provideTestCases") + void testBoggleBoard(String[] words, List<String> expectedWords, String testDescription) { + List<String> result = WordBoggle.boggleBoard(board, words); + assertEquals(expectedWords.size(), result.size(), "Test failed for: " + testDescription); + assertTrue(expectedWords.containsAll(result), "Test failed for: " + testDescription); + } + + private static Stream<Arguments> provideTestCases() { + return Stream.of(Arguments.of(new String[] {"this", "is", "not", "a", "simple", "test", "boggle", "board", "REPEATED", "NOTRE_PEATED"}, Arrays.asList("this", "is", "a", "simple", "board", "boggle", "NOTRE_PEATED"), "All words"), + Arguments.of(new String[] {"xyz", "hello", "world"}, List.of(), "No matching words"), Arguments.of(new String[] {}, List.of(), "Empty words array"), Arguments.of(new String[] {"this", "this", "board", "board"}, Arrays.asList("this", "board"), "Duplicate words in input")); + } + + @ParameterizedTest + @MethodSource("provideSpecialCases") + void testBoggleBoardSpecialCases(char[][] specialBoard, String[] words, Collection<String> expectedWords, String testDescription) { + List<String> result = WordBoggle.boggleBoard(specialBoard, words); + assertEquals(expectedWords.size(), result.size(), "Test failed for: " + testDescription); + assertTrue(expectedWords.containsAll(result), "Test failed for: " + testDescription); + } + + private static Stream<Arguments> provideSpecialCases() { + return Stream.of(Arguments.of(new char[0][0], new String[] {"this", "is", "a", "test"}, List.of(), "Empty board")); + } +} diff --git a/src/test/java/com/thealgorithms/randomized/ReservoirSamplingTest.java b/src/test/java/com/thealgorithms/randomized/ReservoirSamplingTest.java new file mode 100644 index 000000000000..0c6061fcde2a --- /dev/null +++ b/src/test/java/com/thealgorithms/randomized/ReservoirSamplingTest.java @@ -0,0 +1,45 @@ +package com.thealgorithms.randomized; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class ReservoirSamplingTest { + + @Test + public void testSampleSizeEqualsStreamLength() { + int[] stream = {1, 2, 3, 4, 5}; + int sampleSize = 5; + + List<Integer> result = ReservoirSampling.sample(stream, sampleSize); + + assertEquals(sampleSize, result.size()); + assertTrue(Arrays.stream(stream).allMatch(result::contains)); + } + + @Test + public void testSampleSizeLessThanStreamLength() { + int[] stream = {10, 20, 30, 40, 50, 60}; + int sampleSize = 3; + + List<Integer> result = ReservoirSampling.sample(stream, sampleSize); + + assertEquals(sampleSize, result.size()); + for (int value : result) { + assertTrue(Arrays.stream(stream).anyMatch(x -> x == value)); + } + } + + @Test + public void testSampleSizeGreaterThanStreamLengthThrowsException() { + int[] stream = {1, 2, 3}; + + Exception exception = assertThrows(IllegalArgumentException.class, () -> ReservoirSampling.sample(stream, 5)); + + assertEquals("Sample size cannot exceed stream size.", exception.getMessage()); + } +} diff --git a/src/test/java/com/thealgorithms/recursion/FibonacciSeriesTest.java b/src/test/java/com/thealgorithms/recursion/FibonacciSeriesTest.java new file mode 100644 index 000000000000..f8b59f7e9ac6 --- /dev/null +++ b/src/test/java/com/thealgorithms/recursion/FibonacciSeriesTest.java @@ -0,0 +1,27 @@ +package com.thealgorithms.recursion; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class FibonacciSeriesTest { + + @Test + public void testFibonacci() { + assertEquals(0, FibonacciSeries.fibonacci(0)); + assertEquals(1, FibonacciSeries.fibonacci(1)); + assertEquals(1, FibonacciSeries.fibonacci(2)); + assertEquals(2, FibonacciSeries.fibonacci(3)); + assertEquals(3, FibonacciSeries.fibonacci(4)); + assertEquals(5, FibonacciSeries.fibonacci(5)); + assertEquals(8, FibonacciSeries.fibonacci(6)); + assertEquals(13, FibonacciSeries.fibonacci(7)); + assertEquals(21, FibonacciSeries.fibonacci(8)); + assertEquals(34, FibonacciSeries.fibonacci(9)); + assertEquals(55, FibonacciSeries.fibonacci(10)); + assertEquals(89, FibonacciSeries.fibonacci(11)); + assertEquals(144, FibonacciSeries.fibonacci(12)); + assertEquals(233, FibonacciSeries.fibonacci(13)); + assertEquals(377, FibonacciSeries.fibonacci(14)); + } +} diff --git a/src/test/java/com/thealgorithms/recursion/GenerateSubsetsTest.java b/src/test/java/com/thealgorithms/recursion/GenerateSubsetsTest.java new file mode 100644 index 000000000000..b92d1406b0a7 --- /dev/null +++ b/src/test/java/com/thealgorithms/recursion/GenerateSubsetsTest.java @@ -0,0 +1,36 @@ +package com.thealgorithms.recursion; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.util.List; +import org.junit.jupiter.api.Test; + +public final class GenerateSubsetsTest { + + @Test + void subsetRecursionTestOne() { + String str = "abc"; + String[] expected = new String[] {"abc", "ab", "ac", "a", "bc", "b", "c", ""}; + + List<String> ans = GenerateSubsets.subsetRecursion(str); + assertArrayEquals(ans.toArray(), expected); + } + + @Test + void subsetRecursionTestTwo() { + String str = "cbf"; + String[] expected = new String[] {"cbf", "cb", "cf", "c", "bf", "b", "f", ""}; + + List<String> ans = GenerateSubsets.subsetRecursion(str); + assertArrayEquals(ans.toArray(), expected); + } + + @Test + void subsetRecursionTestThree() { + String str = "aba"; + String[] expected = new String[] {"aba", "ab", "aa", "a", "ba", "b", "a", ""}; + + List<String> ans = GenerateSubsets.subsetRecursion(str); + assertArrayEquals(ans.toArray(), expected); + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/AgingSchedulingTest.java b/src/test/java/com/thealgorithms/scheduling/AgingSchedulingTest.java new file mode 100644 index 000000000000..cab78e4d1c58 --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/AgingSchedulingTest.java @@ -0,0 +1,54 @@ +package com.thealgorithms.scheduling; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class AgingSchedulingTest { + + private AgingScheduling scheduler; + + @BeforeEach + public void setup() { + scheduler = new AgingScheduling(); + } + + @Test + public void testAddAndScheduleSingleTask() { + scheduler.addTask("Task1", 5); + assertEquals("Task1", scheduler.scheduleNext()); + } + + @Test + public void testAddMultipleTasks() { + scheduler.addTask("Task1", 1); + scheduler.addTask("Task2", 1); + assertEquals("Task1", scheduler.scheduleNext()); + assertEquals("Task2", scheduler.scheduleNext()); + } + + @Test + public void testPriorityAdjustmentWithWait() { + scheduler.addTask("Task1", 1); + scheduler.addTask("Task2", 1); + scheduler.scheduleNext(); + scheduler.scheduleNext(); + assertEquals("Task1", scheduler.scheduleNext()); + } + + @Test + public void testEmptyScheduler() { + assertNull(scheduler.scheduleNext()); + } + + @Test + public void testMultipleRounds() { + scheduler.addTask("Task1", 1); + scheduler.addTask("Task2", 2); + scheduler.scheduleNext(); + scheduler.scheduleNext(); + assertEquals("Task1", scheduler.scheduleNext()); + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/EDFSchedulingTest.java b/src/test/java/com/thealgorithms/scheduling/EDFSchedulingTest.java new file mode 100644 index 000000000000..8ce290cb246f --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/EDFSchedulingTest.java @@ -0,0 +1,69 @@ +package com.thealgorithms.scheduling; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class EDFSchedulingTest { + + private List<EDFScheduling.Process> processes; + + @BeforeEach + public void setup() { + processes = createProcesses(); + } + + @Test + public void testEDFScheduling() { + EDFScheduling edfScheduling = new EDFScheduling(processes); + List<EDFScheduling.Process> executedProcesses = edfScheduling.scheduleProcesses(); + + // Assert the correct number of processes + assertEquals(3, executedProcesses.size()); + + // Assert that processes are executed in order of earliest deadline first + EDFScheduling.Process process1 = executedProcesses.get(0); + assertEquals("P2", process1.getProcessId()); + assertEquals(0, process1.getWaitingTime()); + assertEquals(3, process1.getTurnAroundTime()); + + EDFScheduling.Process process2 = executedProcesses.get(1); + assertEquals("P1", process2.getProcessId()); + assertEquals(3, process2.getWaitingTime()); + assertEquals(10, process2.getTurnAroundTime()); + + EDFScheduling.Process process3 = executedProcesses.get(2); + assertEquals("P3", process3.getProcessId()); + assertEquals(10, process3.getWaitingTime()); + assertEquals(18, process3.getTurnAroundTime()); + } + + @Test + public void testProcessMissedDeadline() { + // Modify the deadline of a process to ensure it will miss its deadline + processes.get(1).setTurnAroundTime(5); // Set P1's deadline to 5 (which it will miss) + + EDFScheduling edfScheduling = new EDFScheduling(processes); + edfScheduling.scheduleProcesses(); + + // Check if the process with ID "P1" missed its deadline + assertEquals("P1", processes.get(1).getProcessId()); + } + + private List<EDFScheduling.Process> createProcesses() { + // Process ID, Burst Time, Deadline + EDFScheduling.Process process1 = new EDFScheduling.Process("P1", 7, 10); // 7 burst time, 10 deadline + EDFScheduling.Process process2 = new EDFScheduling.Process("P2", 3, 5); // 3 burst time, 5 deadline + EDFScheduling.Process process3 = new EDFScheduling.Process("P3", 8, 18); // 8 burst time, 18 deadline + + List<EDFScheduling.Process> processes = new ArrayList<>(); + processes.add(process1); + processes.add(process2); + processes.add(process3); + + return processes; + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/FCFSSchedulingTest.java b/src/test/java/com/thealgorithms/scheduling/FCFSSchedulingTest.java new file mode 100644 index 000000000000..87b3d12c8dcf --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/FCFSSchedulingTest.java @@ -0,0 +1,46 @@ +package com.thealgorithms.scheduling; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.thealgorithms.devutils.entities.ProcessDetails; +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class FCFSSchedulingTest { + + @Test + public void testingProcesses() { + List<ProcessDetails> processes = addProcessesForFCFS(); + final FCFSScheduling fcfsScheduling = new FCFSScheduling(processes); // for sending to FCFS + + fcfsScheduling.scheduleProcesses(); + + assertEquals(3, processes.size()); + + assertEquals("P1", processes.get(0).getProcessId()); + assertEquals(0, processes.get(0).getWaitingTime()); + assertEquals(10, processes.get(0).getTurnAroundTimeTime()); + + assertEquals("P2", processes.get(1).getProcessId()); + assertEquals(10, processes.get(1).getWaitingTime()); + assertEquals(15, processes.get(1).getTurnAroundTimeTime()); + + assertEquals("P3", processes.get(2).getProcessId()); + assertEquals(15, processes.get(2).getWaitingTime()); + assertEquals(23, processes.get(2).getTurnAroundTimeTime()); + } + + private List<ProcessDetails> addProcessesForFCFS() { + final ProcessDetails process1 = new ProcessDetails("P1", 0, 10); + final ProcessDetails process2 = new ProcessDetails("P2", 1, 5); + final ProcessDetails process3 = new ProcessDetails("P3", 2, 8); + + final List<ProcessDetails> processDetails = new ArrayList<>(); + processDetails.add(process1); + processDetails.add(process2); + processDetails.add(process3); + + return processDetails; + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/FairShareSchedulingTest.java b/src/test/java/com/thealgorithms/scheduling/FairShareSchedulingTest.java new file mode 100644 index 000000000000..7aa6e061c497 --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/FairShareSchedulingTest.java @@ -0,0 +1,60 @@ +package com.thealgorithms.scheduling; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class FairShareSchedulingTest { + + private FairShareScheduling scheduler; + + @BeforeEach + public void setup() { + scheduler = new FairShareScheduling(); + } + + @Test + public void testAllocateResourcesSingleUser() { + scheduler.addUser("User1"); + scheduler.addTask("User1", 5); + scheduler.allocateResources(100); + Map<String, Integer> expected = Map.of("User1", 100); + assertEquals(expected, scheduler.getAllocatedResources()); + } + + @Test + public void testAllocateResourcesMultipleUsers() { + scheduler.addUser("User1"); + scheduler.addUser("User2"); + scheduler.addUser("User3"); + scheduler.addTask("User1", 2); + scheduler.addTask("User2", 3); + scheduler.addTask("User3", 5); + scheduler.allocateResources(100); + Map<String, Integer> expected = Map.of("User1", 20, "User2", 30, "User3", 50); + assertEquals(expected, scheduler.getAllocatedResources()); + } + + @Test + public void testAllocateResourcesZeroWeightUser() { + scheduler.addUser("User1"); + scheduler.addUser("User2"); + scheduler.addTask("User2", 5); + scheduler.allocateResources(100); + Map<String, Integer> expected = Map.of("User1", 0, "User2", 100); + assertEquals(expected, scheduler.getAllocatedResources()); + } + + @Test + public void testAllocateResourcesEqualWeights() { + scheduler.addUser("User1"); + scheduler.addUser("User2"); + scheduler.addTask("User1", 1); + scheduler.addTask("User2", 1); + scheduler.allocateResources(100); + Map<String, Integer> expected = Map.of("User1", 50, "User2", 50); + assertEquals(expected, scheduler.getAllocatedResources()); + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/GangSchedulingTest.java b/src/test/java/com/thealgorithms/scheduling/GangSchedulingTest.java new file mode 100644 index 000000000000..2e1bb4cd0e20 --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/GangSchedulingTest.java @@ -0,0 +1,52 @@ +package com.thealgorithms.scheduling; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class GangSchedulingTest { + + private GangScheduling scheduler; + + @BeforeEach + public void setup() { + scheduler = new GangScheduling(); + } + + @Test + public void testAddGangAndTask() { + scheduler.addGang("Gang1"); + scheduler.addTaskToGang("Gang1", "Task1"); + Map<String, List<String>> expected = Map.of("Gang1", List.of("Task1")); + assertEquals(expected, scheduler.getGangSchedules()); + } + + @Test + public void testMultipleGangs() { + scheduler.addGang("Gang1"); + scheduler.addGang("Gang2"); + scheduler.addTaskToGang("Gang1", "Task1"); + scheduler.addTaskToGang("Gang2", "Task2"); + Map<String, List<String>> expected = Map.of("Gang1", List.of("Task1"), "Gang2", List.of("Task2")); + assertEquals(expected, scheduler.getGangSchedules()); + } + + @Test + public void testGangWithMultipleTasks() { + scheduler.addGang("Gang1"); + scheduler.addTaskToGang("Gang1", "Task1"); + scheduler.addTaskToGang("Gang1", "Task2"); + Map<String, List<String>> expected = Map.of("Gang1", List.of("Task1", "Task2")); + assertEquals(expected, scheduler.getGangSchedules()); + } + + @Test + public void testEmptyGangSchedule() { + scheduler.addGang("Gang1"); + Map<String, List<String>> expected = Map.of("Gang1", List.of()); + assertEquals(expected, scheduler.getGangSchedules()); + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/HighestResponseRatioNextSchedulingTest.java b/src/test/java/com/thealgorithms/scheduling/HighestResponseRatioNextSchedulingTest.java new file mode 100644 index 000000000000..d225d76b82c9 --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/HighestResponseRatioNextSchedulingTest.java @@ -0,0 +1,112 @@ +package com.thealgorithms.scheduling; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +public class HighestResponseRatioNextSchedulingTest { + + @Test + public void testCalculateTurnAroundTime() { + String[] processNames = {"A", "B", "C"}; + int[] arrivalTimes = {0, 2, 4}; + int[] burstTimes = {3, 1, 2}; + int noOfProcesses = 3; + + int[] expectedTurnAroundTimes = {3, 2, 2}; + int[] actualTurnAroundTimes = HighestResponseRatioNextScheduling.calculateTurnAroundTime(processNames, arrivalTimes, burstTimes, noOfProcesses); + + assertArrayEquals(expectedTurnAroundTimes, actualTurnAroundTimes, "Turn Around Times do not match"); + } + + @Test + public void testCalculateWaitingTime() { + int[] turnAroundTimes = {3, 1, 5}; + int[] burstTimes = {3, 1, 2}; + + int[] expectedWaitingTimes = {0, 0, 3}; + int[] actualWaitingTimes = HighestResponseRatioNextScheduling.calculateWaitingTime(turnAroundTimes, burstTimes); + + assertArrayEquals(expectedWaitingTimes, actualWaitingTimes, "Waiting Times do not match"); + } + + @Test + public void testCompleteSchedulingScenario() { + String[] processNames = {"A", "B", "C"}; + int[] arrivalTimes = {0, 1, 2}; + int[] burstTimes = {5, 2, 1}; + + int[] expectedTurnAroundTimes = {5, 7, 4}; + int[] turnAroundTimes = HighestResponseRatioNextScheduling.calculateTurnAroundTime(processNames, arrivalTimes, burstTimes, processNames.length); + assertArrayEquals(expectedTurnAroundTimes, turnAroundTimes, "Turn Around Times do not match"); + + int[] expectedWaitingTimes = {0, 5, 3}; + int[] waitingTimes = HighestResponseRatioNextScheduling.calculateWaitingTime(turnAroundTimes, burstTimes); + assertArrayEquals(expectedWaitingTimes, waitingTimes, "Waiting Times do not match"); + } + + @Test + public void testZeroProcesses() { + String[] processNames = {}; + int[] arrivalTimes = {}; + int[] burstTimes = {}; + int noOfProcesses = 0; + + int[] expectedTurnAroundTimes = {}; + int[] actualTurnAroundTimes = HighestResponseRatioNextScheduling.calculateTurnAroundTime(processNames, arrivalTimes, burstTimes, noOfProcesses); + + assertArrayEquals(expectedTurnAroundTimes, actualTurnAroundTimes, "Turn Around Times for zero processes should be an empty array"); + } + + @Test + public void testAllProcessesArriveAtSameTime() { + String[] processNames = {"A", "B", "C", "D"}; + int[] arrivalTimes = {0, 0, 0, 0}; + int[] burstTimes = {4, 3, 1, 2}; + int noOfProcesses = 4; + + int[] expectedTurnAroundTimes = {4, 10, 5, 7}; + int[] actualTurnAroundTimes = HighestResponseRatioNextScheduling.calculateTurnAroundTime(processNames, arrivalTimes, burstTimes, noOfProcesses); + + assertArrayEquals(expectedTurnAroundTimes, actualTurnAroundTimes, "Turn Around Times for processes arriving at the same time do not match"); + } + + @Test + public void testProcessesWithZeroBurstTime() { + String[] processNames = {"A", "B", "C"}; + int[] arrivalTimes = {0, 1, 2}; + int[] burstTimes = {3, 0, 2}; + int noOfProcesses = 3; + + int[] expectedTurnAroundTimes = {3, 2, 3}; + int[] actualTurnAroundTimes = HighestResponseRatioNextScheduling.calculateTurnAroundTime(processNames, arrivalTimes, burstTimes, noOfProcesses); + + assertArrayEquals(expectedTurnAroundTimes, actualTurnAroundTimes, "Turn Around Times for processes with zero burst time do not match"); + } + + @Test + public void testProcessesWithLargeGapsBetweenArrivals() { + String[] processNames = {"A", "B", "C"}; + int[] arrivalTimes = {0, 100, 200}; + int[] burstTimes = {10, 10, 10}; + int noOfProcesses = 3; + + int[] expectedTurnAroundTimes = {10, 10, 10}; + int[] actualTurnAroundTimes = HighestResponseRatioNextScheduling.calculateTurnAroundTime(processNames, arrivalTimes, burstTimes, noOfProcesses); + + assertArrayEquals(expectedTurnAroundTimes, actualTurnAroundTimes, "Turn Around Times for processes with large gaps between arrivals do not match"); + } + + @Test + public void testProcessesWithVeryLargeBurstTimes() { + String[] processNames = {"A", "B"}; + int[] arrivalTimes = {0, 1}; + int[] burstTimes = {Integer.MAX_VALUE / 2, Integer.MAX_VALUE / 2}; + int noOfProcesses = 2; + + int[] expectedTurnAroundTimes = {Integer.MAX_VALUE / 2, Integer.MAX_VALUE - 2}; + int[] actualTurnAroundTimes = HighestResponseRatioNextScheduling.calculateTurnAroundTime(processNames, arrivalTimes, burstTimes, noOfProcesses); + + assertArrayEquals(expectedTurnAroundTimes, actualTurnAroundTimes, "Turn Around Times for processes with very large burst times do not match"); + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/JobSchedulingWithDeadlineTest.java b/src/test/java/com/thealgorithms/scheduling/JobSchedulingWithDeadlineTest.java new file mode 100644 index 000000000000..538db92a1f26 --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/JobSchedulingWithDeadlineTest.java @@ -0,0 +1,43 @@ +package com.thealgorithms.scheduling; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +class JobSchedulingWithDeadlineTest { + + @Test + void testJobSequencingWithDeadlines1() { + JobSchedulingWithDeadline.Job[] jobs = {new JobSchedulingWithDeadline.Job(1, 1, 4, 20), new JobSchedulingWithDeadline.Job(2, 1, 1, 10), new JobSchedulingWithDeadline.Job(3, 1, 1, 40), new JobSchedulingWithDeadline.Job(4, 1, 1, 30)}; + int[] result = JobSchedulingWithDeadline.jobSequencingWithDeadlines(jobs); + assertArrayEquals(new int[] {2, 60}, result); // Expected output: 2 jobs, 60 profit + } + + @Test + void testJobSequencingWithDeadlines2() { + JobSchedulingWithDeadline.Job[] jobs = {new JobSchedulingWithDeadline.Job(1, 1, 2, 100), new JobSchedulingWithDeadline.Job(2, 1, 1, 19), new JobSchedulingWithDeadline.Job(3, 1, 2, 27), new JobSchedulingWithDeadline.Job(4, 1, 1, 25), new JobSchedulingWithDeadline.Job(5, 1, 1, 15)}; + int[] result = JobSchedulingWithDeadline.jobSequencingWithDeadlines(jobs); + assertArrayEquals(new int[] {2, 127}, result); // Expected output: 2 jobs, 127 profit + } + + @Test + void testJobSequencingWithDeadlinesWithArrivalTimes() { + JobSchedulingWithDeadline.Job[] jobs = {new JobSchedulingWithDeadline.Job(1, 2, 5, 50), new JobSchedulingWithDeadline.Job(2, 3, 4, 60), new JobSchedulingWithDeadline.Job(3, 1, 3, 20)}; + int[] result = JobSchedulingWithDeadline.jobSequencingWithDeadlines(jobs); + assertArrayEquals(new int[] {3, 130}, result); // All 3 jobs fit within their deadlines + } + + @Test + void testJobSequencingWithDeadlinesNoJobs() { + JobSchedulingWithDeadline.Job[] jobs = {}; + int[] result = JobSchedulingWithDeadline.jobSequencingWithDeadlines(jobs); + assertArrayEquals(new int[] {0, 0}, result); // No jobs, 0 profit + } + + @Test + void testJobSequencingWithDeadlinesSingleJob() { + JobSchedulingWithDeadline.Job[] jobs = {new JobSchedulingWithDeadline.Job(1, 1, 1, 50)}; + int[] result = JobSchedulingWithDeadline.jobSequencingWithDeadlines(jobs); + assertArrayEquals(new int[] {1, 50}, result); // 1 job scheduled, 50 profit + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/LotterySchedulingTest.java b/src/test/java/com/thealgorithms/scheduling/LotterySchedulingTest.java new file mode 100644 index 000000000000..00fd8adcde27 --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/LotterySchedulingTest.java @@ -0,0 +1,64 @@ +package com.thealgorithms.scheduling; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class LotterySchedulingTest { + + private Random mockRandom; + + @BeforeEach + public void setup() { + mockRandom = mock(Random.class); + } + + @Test + public void testLotterySchedulingWithMockedRandom() { + List<LotteryScheduling.Process> processes = createProcesses(); + LotteryScheduling lotteryScheduling = new LotteryScheduling(processes, mockRandom); + + // Mock the sequence of random numbers (winning tickets) + // This sequence ensures that P1 (10 tickets), P3 (8 tickets), and P2 (5 tickets) are selected. + when(mockRandom.nextInt(23)).thenReturn(5, 18, 11); // winning tickets for P1, P3, and P2 + + List<LotteryScheduling.Process> executedProcesses = lotteryScheduling.scheduleProcesses(); + + assertEquals(3, executedProcesses.size()); + + // Assert the process execution order and properties + LotteryScheduling.Process process1 = executedProcesses.get(0); + assertEquals("P1", process1.getProcessId()); + assertEquals(0, process1.getWaitingTime()); + assertEquals(10, process1.getTurnAroundTime()); + + LotteryScheduling.Process process2 = executedProcesses.get(1); + assertEquals("P2", process2.getProcessId()); + assertEquals(10, process2.getWaitingTime()); + assertEquals(15, process2.getTurnAroundTime()); + + LotteryScheduling.Process process3 = executedProcesses.get(2); + assertEquals("P3", process3.getProcessId()); + assertEquals(15, process3.getWaitingTime()); + assertEquals(23, process3.getTurnAroundTime()); + } + + private List<LotteryScheduling.Process> createProcesses() { + LotteryScheduling.Process process1 = new LotteryScheduling.Process("P1", 10, 10); // 10 tickets + LotteryScheduling.Process process2 = new LotteryScheduling.Process("P2", 5, 5); // 5 tickets + LotteryScheduling.Process process3 = new LotteryScheduling.Process("P3", 8, 8); // 8 tickets + + List<LotteryScheduling.Process> processes = new ArrayList<>(); + processes.add(process1); + processes.add(process2); + processes.add(process3); + + return processes; + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/MLFQSchedulerTest.java b/src/test/java/com/thealgorithms/scheduling/MLFQSchedulerTest.java new file mode 100644 index 000000000000..d7d27e9b32b6 --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/MLFQSchedulerTest.java @@ -0,0 +1,46 @@ +package com.thealgorithms.scheduling; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class MLFQSchedulerTest { + + @Test + void testMLFQScheduling() { + // Create MLFQ Scheduler with 3 levels and time quantum for each level + int[] timeQuantums = {4, 8, 12}; // Example of different quantum for each queue + MLFQScheduler scheduler = new MLFQScheduler(3, timeQuantums); + + // Add processes to the scheduler + scheduler.addProcess(new Process(1, 10, 0)); // pid=1, burstTime=10, arrivalTime=0 + scheduler.addProcess(new Process(2, 15, 0)); // pid=2, burstTime=15, arrivalTime=0 + scheduler.addProcess(new Process(3, 25, 0)); // pid=3, burstTime=25, arrivalTime=0 + + // Run the scheduler + scheduler.run(); + + // Check current time after all processes are finished + assertEquals(50, scheduler.getCurrentTime()); + } + + @Test + void testProcessCompletionOrder() { + int[] timeQuantums = {3, 6, 9}; + MLFQScheduler scheduler = new MLFQScheduler(3, timeQuantums); + + Process p1 = new Process(1, 10, 0); + Process p2 = new Process(2, 5, 0); + Process p3 = new Process(3, 20, 0); + + scheduler.addProcess(p1); + scheduler.addProcess(p2); + scheduler.addProcess(p3); + + scheduler.run(); + + // After running, current time should match the total burst time for all + // processes + assertEquals(35, scheduler.getCurrentTime()); + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/MultiAgentSchedulingTest.java b/src/test/java/com/thealgorithms/scheduling/MultiAgentSchedulingTest.java new file mode 100644 index 000000000000..16067fa8c22a --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/MultiAgentSchedulingTest.java @@ -0,0 +1,52 @@ +package com.thealgorithms.scheduling; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class MultiAgentSchedulingTest { + + private MultiAgentScheduling scheduler; + + @BeforeEach + public void setup() { + scheduler = new MultiAgentScheduling(); + } + + @Test + public void testAddAgentAndAssignTask() { + scheduler.addAgent("Agent1"); + scheduler.assignTask("Agent1", "Task1"); + Map<String, List<String>> expected = Map.of("Agent1", List.of("Task1")); + assertEquals(expected, scheduler.getScheduledTasks()); + } + + @Test + public void testMultipleAgentsWithTasks() { + scheduler.addAgent("Agent1"); + scheduler.addAgent("Agent2"); + scheduler.assignTask("Agent1", "Task1"); + scheduler.assignTask("Agent2", "Task2"); + Map<String, List<String>> expected = Map.of("Agent1", List.of("Task1"), "Agent2", List.of("Task2")); + assertEquals(expected, scheduler.getScheduledTasks()); + } + + @Test + public void testAgentWithMultipleTasks() { + scheduler.addAgent("Agent1"); + scheduler.assignTask("Agent1", "Task1"); + scheduler.assignTask("Agent1", "Task2"); + Map<String, List<String>> expected = Map.of("Agent1", List.of("Task1", "Task2")); + assertEquals(expected, scheduler.getScheduledTasks()); + } + + @Test + public void testEmptyAgentSchedule() { + scheduler.addAgent("Agent1"); + Map<String, List<String>> expected = Map.of("Agent1", List.of()); + assertEquals(expected, scheduler.getScheduledTasks()); + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/NonPreemptivePrioritySchedulingTest.java b/src/test/java/com/thealgorithms/scheduling/NonPreemptivePrioritySchedulingTest.java new file mode 100644 index 000000000000..d28dcfeaaea3 --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/NonPreemptivePrioritySchedulingTest.java @@ -0,0 +1,70 @@ +package com.thealgorithms.scheduling; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class NonPreemptivePrioritySchedulingTest { + + @Test + public void testCalculateAverageWaitingTime() { + NonPreemptivePriorityScheduling.Process[] processes = {new NonPreemptivePriorityScheduling.Process(1, 0, 10, 2), // id, arrivalTime, burstTime, priority + new NonPreemptivePriorityScheduling.Process(2, 0, 5, 1), new NonPreemptivePriorityScheduling.Process(3, 0, 8, 3)}; + NonPreemptivePriorityScheduling.Process[] executionOrder = NonPreemptivePriorityScheduling.scheduleProcesses(processes); + + double expectedAvgWaitingTime = (0 + 5 + 15) / 3.0; // Waiting times: 0 for P2, 5 for P1, 15 for P3 + double actualAvgWaitingTime = NonPreemptivePriorityScheduling.calculateAverageWaitingTime(processes, executionOrder); + + assertEquals(expectedAvgWaitingTime, actualAvgWaitingTime, 0.01, "Average waiting time should be calculated correctly."); + } + + @Test + public void testCalculateAverageTurnaroundTime() { + NonPreemptivePriorityScheduling.Process[] processes = {new NonPreemptivePriorityScheduling.Process(1, 0, 10, 2), // id, arrivalTime, burstTime, priority + new NonPreemptivePriorityScheduling.Process(2, 0, 5, 1), new NonPreemptivePriorityScheduling.Process(3, 0, 8, 3)}; + + NonPreemptivePriorityScheduling.Process[] executionOrder = NonPreemptivePriorityScheduling.scheduleProcesses(processes); + + double expectedAvgTurnaroundTime = (5 + 15 + 23) / 3.0; // Turnaround times: 5 for P2, 15 for P1, 23 for P3 + double actualAvgTurnaroundTime = NonPreemptivePriorityScheduling.calculateAverageTurnaroundTime(processes, executionOrder); + + assertEquals(expectedAvgTurnaroundTime, actualAvgTurnaroundTime, 0.01, "Average turnaround time should be calculated correctly."); + } + + @Test + public void testStartTimeIsCorrect() { + NonPreemptivePriorityScheduling.Process[] processes = {new NonPreemptivePriorityScheduling.Process(1, 0, 10, 2), // id, arrivalTime, burstTime, priority + new NonPreemptivePriorityScheduling.Process(2, 0, 5, 1), new NonPreemptivePriorityScheduling.Process(3, 0, 8, 3)}; + NonPreemptivePriorityScheduling.Process[] executionOrder = NonPreemptivePriorityScheduling.scheduleProcesses(processes); + + // Check that the start time for each process is correctly set + assertEquals(0, executionOrder[0].startTime, "First process (P2) should start at time 0."); // Process 2 has the highest priority + assertEquals(5, executionOrder[1].startTime, "Second process (P1) should start at time 5."); + assertEquals(15, executionOrder[2].startTime, "Third process (P3) should start at time 15."); + } + + @Test + public void testWithDelayedArrivalTimes() { + NonPreemptivePriorityScheduling.Process[] processes = {new NonPreemptivePriorityScheduling.Process(1, 0, 4, 1), // id, arrivalTime, burstTime, priority + new NonPreemptivePriorityScheduling.Process(2, 2, 3, 2), new NonPreemptivePriorityScheduling.Process(3, 4, 2, 3)}; + NonPreemptivePriorityScheduling.Process[] executionOrder = NonPreemptivePriorityScheduling.scheduleProcesses(processes); + + // Test the start times considering delayed arrivals + assertEquals(0, executionOrder[0].startTime, "First process (P1) should start at time 0."); + assertEquals(4, executionOrder[1].startTime, "Second process (P2) should start at time 4."); // After P1 finishes + assertEquals(7, executionOrder[2].startTime, "Third process (P3) should start at time 7."); // After P2 finishes + } + + @Test + public void testWithGapsInArrivals() { + NonPreemptivePriorityScheduling.Process[] processes = {new NonPreemptivePriorityScheduling.Process(1, 0, 6, 2), // id, arrivalTime, burstTime, priority + new NonPreemptivePriorityScheduling.Process(2, 8, 4, 1), new NonPreemptivePriorityScheduling.Process(3, 12, 5, 3)}; + + NonPreemptivePriorityScheduling.Process[] executionOrder = NonPreemptivePriorityScheduling.scheduleProcesses(processes); + + // Test the start times for processes with gaps in arrival times + assertEquals(0, executionOrder[0].startTime, "First process (P1) should start at time 0."); + assertEquals(8, executionOrder[1].startTime, "Second process (P2) should start at time 8."); // After P1 finishes, arrives at 8 + assertEquals(12, executionOrder[2].startTime, "Third process (P3) should start at time 12."); // After P2 finishes, arrives at 12 + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/PreemptivePrioritySchedulingTest.java b/src/test/java/com/thealgorithms/scheduling/PreemptivePrioritySchedulingTest.java new file mode 100644 index 000000000000..ea692686afd2 --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/PreemptivePrioritySchedulingTest.java @@ -0,0 +1,31 @@ +package com.thealgorithms.scheduling; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.thealgorithms.devutils.entities.ProcessDetails; +import java.util.Collection; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * Test Cases of Preemptive Priority Scheduling Algorithm + * + * @author [Bama Charan Chhandogi](https://www.github.com/BamaCharanChhandogi) + */ +class PreemptivePrioritySchedulingTest { + @ParameterizedTest + @MethodSource("provideProcessesAndExpectedSchedules") + void testPreemptivePriorityScheduling(Collection<ProcessDetails> processes, List<String> expectedSchedule) { + PreemptivePriorityScheduling scheduler = new PreemptivePriorityScheduling(processes); + scheduler.scheduleProcesses(); + assertEquals(expectedSchedule, scheduler.ganttChart); + } + + static Stream<Arguments> provideProcessesAndExpectedSchedules() { + return Stream.of(Arguments.of(List.of(new ProcessDetails("P1", 0, 5, 2), new ProcessDetails("P2", 1, 4, 4), new ProcessDetails("P3", 2, 2, 6), new ProcessDetails("P4", 4, 1, 8)), List.of("P1", "P2", "P3", "P3", "P4", "P2", "P2", "P2", "P1", "P1", "P1", "P1")), + Arguments.of(List.of(new ProcessDetails("P1", 2, 5, 3), new ProcessDetails("P2", 5, 3, 5), new ProcessDetails("P3", 7, 1, 9)), List.of("Idle", "Idle", "P1", "P1", "P1", "P2", "P2", "P3", "P2", "P1", "P1"))); + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/ProportionalFairSchedulingTest.java b/src/test/java/com/thealgorithms/scheduling/ProportionalFairSchedulingTest.java new file mode 100644 index 000000000000..0d379ee90b0e --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/ProportionalFairSchedulingTest.java @@ -0,0 +1,53 @@ +package com.thealgorithms.scheduling; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class ProportionalFairSchedulingTest { + + private ProportionalFairScheduling scheduler; + + @BeforeEach + public void setup() { + scheduler = new ProportionalFairScheduling(); + } + + @Test + public void testAllocateResourcesSingleProcess() { + scheduler.addProcess("Process1", 5); + scheduler.allocateResources(100); + List<String> expected = List.of("Process1: 100"); + assertEquals(expected, scheduler.getAllocatedResources()); + } + + @Test + public void testAllocateResourcesMultipleProcesses() { + scheduler.addProcess("Process1", 2); + scheduler.addProcess("Process2", 3); + scheduler.addProcess("Process3", 5); + scheduler.allocateResources(100); + List<String> expected = List.of("Process1: 20", "Process2: 30", "Process3: 50"); + assertEquals(expected, scheduler.getAllocatedResources()); + } + + @Test + public void testAllocateResourcesZeroWeightProcess() { + scheduler.addProcess("Process1", 0); + scheduler.addProcess("Process2", 5); + scheduler.allocateResources(100); + List<String> expected = List.of("Process1: 0", "Process2: 100"); + assertEquals(expected, scheduler.getAllocatedResources()); + } + + @Test + public void testAllocateResourcesEqualWeights() { + scheduler.addProcess("Process1", 1); + scheduler.addProcess("Process2", 1); + scheduler.allocateResources(100); + List<String> expected = List.of("Process1: 50", "Process2: 50"); + assertEquals(expected, scheduler.getAllocatedResources()); + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/RRSchedulingTest.java b/src/test/java/com/thealgorithms/scheduling/RRSchedulingTest.java new file mode 100644 index 000000000000..3da526601f5f --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/RRSchedulingTest.java @@ -0,0 +1,63 @@ +package com.thealgorithms.scheduling; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.thealgorithms.devutils.entities.ProcessDetails; +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Test; + +class RRSchedulingTest { + @Test + public void testingProcesses() { + List<ProcessDetails> processes = addProcessesForRR(); + final RRScheduling rrScheduling = new RRScheduling(processes, 4); // for sending to RR with quantum value 4 + + rrScheduling.scheduleProcesses(); + + assertEquals(6, processes.size()); + + assertEquals("P1", processes.get(0).getProcessId()); + assertEquals(12, processes.get(0).getWaitingTime()); + assertEquals(17, processes.get(0).getTurnAroundTimeTime()); + + assertEquals("P2", processes.get(1).getProcessId()); + assertEquals(16, processes.get(1).getWaitingTime()); + assertEquals(22, processes.get(1).getTurnAroundTimeTime()); + + assertEquals("P3", processes.get(2).getProcessId()); + assertEquals(6, processes.get(2).getWaitingTime()); + assertEquals(9, processes.get(2).getTurnAroundTimeTime()); + + assertEquals("P4", processes.get(3).getProcessId()); + assertEquals(8, processes.get(3).getWaitingTime()); + assertEquals(9, processes.get(3).getTurnAroundTimeTime()); + + assertEquals("P5", processes.get(4).getProcessId()); + assertEquals(15, processes.get(4).getWaitingTime()); + assertEquals(20, processes.get(4).getTurnAroundTimeTime()); + + assertEquals("P6", processes.get(5).getProcessId()); + assertEquals(11, processes.get(5).getWaitingTime()); + assertEquals(15, processes.get(5).getTurnAroundTimeTime()); + } + + private List<ProcessDetails> addProcessesForRR() { + final ProcessDetails process1 = new ProcessDetails("P1", 0, 5); + final ProcessDetails process2 = new ProcessDetails("P2", 1, 6); + final ProcessDetails process3 = new ProcessDetails("P3", 2, 3); + final ProcessDetails process4 = new ProcessDetails("P4", 3, 1); + final ProcessDetails process5 = new ProcessDetails("P5", 4, 5); + final ProcessDetails process6 = new ProcessDetails("P6", 6, 4); + + final List<ProcessDetails> processDetails = new ArrayList<>(); + processDetails.add(process1); + processDetails.add(process2); + processDetails.add(process3); + processDetails.add(process4); + processDetails.add(process5); + processDetails.add(process6); + + return processDetails; + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/RandomSchedulingTest.java b/src/test/java/com/thealgorithms/scheduling/RandomSchedulingTest.java new file mode 100644 index 000000000000..e2c8777d892f --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/RandomSchedulingTest.java @@ -0,0 +1,93 @@ +package com.thealgorithms.scheduling; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Random; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class RandomSchedulingTest { + + private RandomScheduling randomScheduling; + private Random mockRandom; + + @BeforeEach + public void setup() { + mockRandom = mock(Random.class); // Mocking Random for predictable behavior + } + + @Test + public void testRandomOrder1() { + // Arrange + List<String> tasks = List.of("Task1", "Task2", "Task3"); + // Mock the random sequence to control shuffling: swap 0 <-> 1, and 1 <-> 2. + when(mockRandom.nextInt(anyInt())).thenReturn(1, 2, 0); + randomScheduling = new RandomScheduling(tasks, mockRandom); + + // Act + List<String> result = randomScheduling.schedule(); + + // Assert + assertEquals(List.of("Task1", "Task2", "Task3"), result); + } + + @Test + public void testRandomOrder2() { + // Arrange + List<String> tasks = List.of("A", "B", "C", "D"); + // Mocking predictable swaps for the sequence: [C, B, D, A] + when(mockRandom.nextInt(anyInt())).thenReturn(2, 1, 3, 0); + randomScheduling = new RandomScheduling(tasks, mockRandom); + + // Act + List<String> result = randomScheduling.schedule(); + + // Assert + assertEquals(List.of("A", "C", "B", "D"), result); + } + + @Test + public void testSingleTask() { + // Arrange + List<String> tasks = List.of("SingleTask"); + when(mockRandom.nextInt(anyInt())).thenReturn(0); // No real shuffle + randomScheduling = new RandomScheduling(tasks, mockRandom); + + // Act + List<String> result = randomScheduling.schedule(); + + // Assert + assertEquals(List.of("SingleTask"), result); + } + + @Test + public void testEmptyTaskList() { + // Arrange + List<String> tasks = List.of(); + randomScheduling = new RandomScheduling(tasks, mockRandom); + + // Act + List<String> result = randomScheduling.schedule(); + + // Assert + assertEquals(List.of(), result); // Should return an empty list + } + + @Test + public void testSameTasksMultipleTimes() { + // Arrange + List<String> tasks = List.of("X", "X", "Y", "Z"); + when(mockRandom.nextInt(anyInt())).thenReturn(3, 0, 1, 2); + randomScheduling = new RandomScheduling(tasks, mockRandom); + + // Act + List<String> result = randomScheduling.schedule(); + + // Assert + assertEquals(List.of("Y", "X", "X", "Z"), result); + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/SJFSchedulingTest.java b/src/test/java/com/thealgorithms/scheduling/SJFSchedulingTest.java new file mode 100644 index 000000000000..aab5c64c847f --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/SJFSchedulingTest.java @@ -0,0 +1,110 @@ +package com.thealgorithms.scheduling; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.thealgorithms.devutils.entities.ProcessDetails; +import java.util.ArrayList; +import org.junit.jupiter.api.Test; + +class SJFSchedulingTest { + private ArrayList<ProcessDetails> process; + void initialisation0() { + + process = new ArrayList<>(); + process.add(new ProcessDetails("1", 0, 6)); + process.add(new ProcessDetails("2", 1, 2)); + } + void initialisation1() { + + process = new ArrayList<>(); + process.add(new ProcessDetails("1", 0, 6)); + process.add(new ProcessDetails("2", 1, 2)); + process.add(new ProcessDetails("3", 4, 3)); + process.add(new ProcessDetails("4", 3, 1)); + process.add(new ProcessDetails("5", 6, 4)); + process.add(new ProcessDetails("6", 5, 5)); + } + + void initialisation2() { + + process = new ArrayList<>(); + process.add(new ProcessDetails("1", 0, 3)); + process.add(new ProcessDetails("2", 1, 2)); + process.add(new ProcessDetails("3", 2, 1)); + } + void initialisation3() { + process = new ArrayList<>(); + process.add(new ProcessDetails("1", 0, 3)); + process.add(new ProcessDetails("2", 5, 2)); + process.add(new ProcessDetails("3", 9, 1)); + } + @Test + void constructor() { + initialisation0(); + SJFScheduling a = new SJFScheduling(process); + assertEquals(6, a.processes.get(0).getBurstTime()); + assertEquals(2, a.processes.get(1).getBurstTime()); + } + + @Test + void sort() { + initialisation1(); + SJFScheduling a = new SJFScheduling(process); + a.sortByArrivalTime(); + assertEquals("1", a.processes.get(0).getProcessId()); + assertEquals("2", a.processes.get(1).getProcessId()); + assertEquals("3", a.processes.get(3).getProcessId()); + assertEquals("4", a.processes.get(2).getProcessId()); + assertEquals("5", a.processes.get(5).getProcessId()); + assertEquals("6", a.processes.get(4).getProcessId()); + } + + @Test + void scheduling() { + initialisation1(); + SJFScheduling a = new SJFScheduling(process); + a.scheduleProcesses(); + assertEquals("1", a.schedule.get(0)); + assertEquals("4", a.schedule.get(1)); + assertEquals("2", a.schedule.get(2)); + assertEquals("3", a.schedule.get(3)); + assertEquals("5", a.schedule.get(4)); + assertEquals("6", a.schedule.get(5)); + } + + @Test + void schedulingOfTwoProcesses() { + initialisation0(); + SJFScheduling a = new SJFScheduling(process); + a.scheduleProcesses(); + assertEquals("1", a.schedule.get(0)); + assertEquals("2", a.schedule.get(1)); + } + + @Test + void schedulingOfAShortestJobArrivingLast() { + initialisation2(); + SJFScheduling a = new SJFScheduling(process); + a.scheduleProcesses(); + assertEquals("1", a.schedule.get(0)); + assertEquals("3", a.schedule.get(1)); + assertEquals("2", a.schedule.get(2)); + } + @Test + void schedulingWithProcessesNotComingBackToBack() { + initialisation3(); + SJFScheduling a = new SJFScheduling(process); + a.scheduleProcesses(); + assertEquals("1", a.schedule.get(0)); + assertEquals("2", a.schedule.get(1)); + assertEquals("3", a.schedule.get(2)); + } + @Test + void schedulingOfNothing() { + process = new ArrayList<>(); + SJFScheduling a = new SJFScheduling(process); + a.scheduleProcesses(); + assertTrue(a.schedule.isEmpty()); + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/SRTFSchedulingTest.java b/src/test/java/com/thealgorithms/scheduling/SRTFSchedulingTest.java new file mode 100644 index 000000000000..9cec31130164 --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/SRTFSchedulingTest.java @@ -0,0 +1,61 @@ +package com.thealgorithms.scheduling; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.thealgorithms.devutils.entities.ProcessDetails; +import java.util.ArrayList; +import org.junit.jupiter.api.Test; + +class SRTFSchedulingTest { + ArrayList<ProcessDetails> processes; + + public void initialization() { + processes = new ArrayList<>(); + processes.add(new ProcessDetails("4", 0, 3)); + processes.add(new ProcessDetails("3", 1, 8)); + processes.add(new ProcessDetails("1", 2, 6)); + processes.add(new ProcessDetails("5", 4, 4)); + processes.add(new ProcessDetails("2", 5, 2)); + } + + @Test + public void constructor() { + initialization(); + SRTFScheduling s = new SRTFScheduling(processes); + assertEquals(3, s.processes.get(0).getBurstTime()); + assertEquals(8, s.processes.get(1).getBurstTime()); + assertEquals(6, s.processes.get(2).getBurstTime()); + assertEquals(4, s.processes.get(3).getBurstTime()); + assertEquals(2, s.processes.get(4).getBurstTime()); + } + + @Test + void evaluateScheduling() { + initialization(); + SRTFScheduling s = new SRTFScheduling(processes); + s.evaluateScheduling(); + assertEquals("4", s.ready.get(0)); + assertEquals("4", s.ready.get(1)); + assertEquals("4", s.ready.get(2)); + assertEquals("1", s.ready.get(3)); + assertEquals("5", s.ready.get(4)); + assertEquals("2", s.ready.get(5)); + assertEquals("2", s.ready.get(6)); + assertEquals("5", s.ready.get(7)); + assertEquals("5", s.ready.get(8)); + assertEquals("5", s.ready.get(9)); + assertEquals("1", s.ready.get(10)); + assertEquals("1", s.ready.get(11)); + assertEquals("1", s.ready.get(12)); + assertEquals("1", s.ready.get(13)); + assertEquals("1", s.ready.get(14)); + assertEquals("3", s.ready.get(15)); + assertEquals("3", s.ready.get(16)); + assertEquals("3", s.ready.get(17)); + assertEquals("3", s.ready.get(18)); + assertEquals("3", s.ready.get(19)); + assertEquals("3", s.ready.get(20)); + assertEquals("3", s.ready.get(21)); + assertEquals("3", s.ready.get(22)); + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/SelfAdjustingSchedulingTest.java b/src/test/java/com/thealgorithms/scheduling/SelfAdjustingSchedulingTest.java new file mode 100644 index 000000000000..8675a1ec397d --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/SelfAdjustingSchedulingTest.java @@ -0,0 +1,57 @@ +package com.thealgorithms.scheduling; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class SelfAdjustingSchedulingTest { + + private SelfAdjustingScheduling scheduler; + + @BeforeEach + public void setup() { + scheduler = new SelfAdjustingScheduling(); + } + + @Test + public void testAddAndScheduleSingleTask() { + scheduler.addTask("Task1", 5); + assertEquals("Task1", scheduler.scheduleNext()); + } + + @Test + public void testAddMultipleTasks() { + scheduler.addTask("Task1", 5); + scheduler.addTask("Task2", 1); + scheduler.addTask("Task3", 3); + assertEquals("Task2", scheduler.scheduleNext()); + assertEquals("Task2", scheduler.scheduleNext()); + assertEquals("Task3", scheduler.scheduleNext()); + } + + @Test + public void testPriorityAdjustment() { + scheduler.addTask("Task1", 1); + scheduler.addTask("Task2", 1); + scheduler.scheduleNext(); + scheduler.scheduleNext(); + scheduler.scheduleNext(); + assertEquals("Task2", scheduler.scheduleNext()); + } + + @Test + public void testEmptyScheduler() { + assertNull(scheduler.scheduleNext()); + } + + @Test + public void testTaskReschedulingAfterWait() { + scheduler.addTask("Task1", 1); + scheduler.addTask("Task2", 2); + scheduler.scheduleNext(); + scheduler.scheduleNext(); + assertEquals("Task1", scheduler.scheduleNext()); + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/SlackTimeSchedulingTest.java b/src/test/java/com/thealgorithms/scheduling/SlackTimeSchedulingTest.java new file mode 100644 index 000000000000..555a0941e7f5 --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/SlackTimeSchedulingTest.java @@ -0,0 +1,48 @@ +package com.thealgorithms.scheduling; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class SlackTimeSchedulingTest { + + private SlackTimeScheduling scheduler; + + @BeforeEach + public void setup() { + scheduler = new SlackTimeScheduling(); + } + + @Test + public void testAddAndScheduleSingleTask() { + scheduler.addTask("Task1", 2, 5); + List<String> expected = List.of("Task1"); + assertEquals(expected, scheduler.scheduleTasks()); + } + + @Test + public void testScheduleMultipleTasks() { + scheduler.addTask("Task1", 2, 5); + scheduler.addTask("Task2", 1, 4); + scheduler.addTask("Task3", 3, 7); + List<String> expected = List.of("Task1", "Task2", "Task3"); + assertEquals(expected, scheduler.scheduleTasks()); + } + + @Test + public void testScheduleTasksWithSameSlackTime() { + scheduler.addTask("Task1", 2, 5); + scheduler.addTask("Task2", 3, 6); + scheduler.addTask("Task3", 1, 4); + List<String> expected = List.of("Task1", "Task2", "Task3"); + assertEquals(expected, scheduler.scheduleTasks()); + } + + @Test + public void testEmptyScheduler() { + List<String> expected = List.of(); + assertEquals(expected, scheduler.scheduleTasks()); + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/diskscheduling/CircularLookSchedulingTest.java b/src/test/java/com/thealgorithms/scheduling/diskscheduling/CircularLookSchedulingTest.java new file mode 100644 index 000000000000..55429e41b84d --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/diskscheduling/CircularLookSchedulingTest.java @@ -0,0 +1,55 @@ +package com.thealgorithms.scheduling.diskscheduling; + +import static java.util.Collections.emptyList; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class CircularLookSchedulingTest { + + @Test + public void testCircularLookSchedulingMovingUp() { + CircularLookScheduling scheduling = new CircularLookScheduling(50, true, 200); + List<Integer> requests = Arrays.asList(55, 58, 39, 18, 90, 160, 150); + List<Integer> expected = Arrays.asList(55, 58, 90, 150, 160, 18, 39); + + List<Integer> result = scheduling.execute(requests); + assertEquals(expected, result); + } + + @Test + public void testCircularLookSchedulingMovingDown() { + CircularLookScheduling scheduling = new CircularLookScheduling(50, false, 200); + List<Integer> requests = Arrays.asList(55, 58, 39, 18, 90, 160, 150); + List<Integer> expected = Arrays.asList(39, 18, 160, 150, 90, 58, 55); + + List<Integer> result = scheduling.execute(requests); + assertEquals(expected, result); + } + + @Test + public void testCircularLookSchedulingEmptyRequests() { + CircularLookScheduling scheduling = new CircularLookScheduling(50, true, 200); + List<Integer> requests = emptyList(); + List<Integer> expected = emptyList(); + + List<Integer> result = scheduling.execute(requests); + assertEquals(expected, result); + } + + @Test + public void testCircularLookSchedulingPrintStatus() { + CircularLookScheduling scheduling = new CircularLookScheduling(50, true, 200); + List<Integer> requests = Arrays.asList(55, 58, 39, 18, 90, 160, 150); + List<Integer> result = scheduling.execute(requests); + + // Print the final status + System.out.println("Final CircularLookScheduling Position: " + scheduling.getCurrentPosition()); + System.out.println("CircularLookScheduling Moving Up: " + scheduling.isMovingUp()); + + // Print the order of request processing + System.out.println("Request Order: " + result); + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/diskscheduling/CircularScanSchedulingTest.java b/src/test/java/com/thealgorithms/scheduling/diskscheduling/CircularScanSchedulingTest.java new file mode 100644 index 000000000000..38daf5104b82 --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/diskscheduling/CircularScanSchedulingTest.java @@ -0,0 +1,49 @@ +package com.thealgorithms.scheduling.diskscheduling; + +import static java.util.Collections.emptyList; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class CircularScanSchedulingTest { + + @Test + public void testCircularScanSchedulingMovingUp() { + CircularScanScheduling circularScan = new CircularScanScheduling(50, true, 200); + List<Integer> requests = Arrays.asList(55, 58, 39, 18, 90, 160, 150); + List<Integer> expectedOrder = Arrays.asList(55, 58, 90, 150, 160, 18, 39); + + List<Integer> result = circularScan.execute(requests); + assertEquals(expectedOrder, result); + + System.out.println("Final CircularScan Position: " + circularScan.getCurrentPosition()); + System.out.println("CircularScan Moving Up: " + circularScan.isMovingUp()); + System.out.println("Request Order: " + result); + } + + @Test + public void testCircularScanSchedulingMovingDown() { + CircularScanScheduling circularScan = new CircularScanScheduling(50, false, 200); + List<Integer> requests = Arrays.asList(55, 58, 39, 18, 90, 160, 150); + List<Integer> expectedOrder = Arrays.asList(39, 18, 160, 150, 90, 58, 55); + + List<Integer> result = circularScan.execute(requests); + assertEquals(expectedOrder, result); + + System.out.println("Final CircularScan Position: " + circularScan.getCurrentPosition()); + System.out.println("CircularScan Moving Down: " + circularScan.isMovingUp()); + System.out.println("Request Order: " + result); + } + + @Test + public void testCircularScanSchedulingEmptyRequests() { + CircularScanScheduling circularScan = new CircularScanScheduling(50, true, 200); + List<Integer> requests = emptyList(); + List<Integer> expectedOrder = emptyList(); + + List<Integer> result = circularScan.execute(requests); + assertEquals(expectedOrder, result); + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/diskscheduling/LookSchedulingTest.java b/src/test/java/com/thealgorithms/scheduling/diskscheduling/LookSchedulingTest.java new file mode 100644 index 000000000000..43ef1a698b55 --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/diskscheduling/LookSchedulingTest.java @@ -0,0 +1,68 @@ +package com.thealgorithms.scheduling.diskscheduling; + +import static java.util.Collections.emptyList; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class LookSchedulingTest { + + @Test + public void testLookSchedulingMovingUp() { + LookScheduling lookScheduling = new LookScheduling(50, true, 200); + List<Integer> requests = Arrays.asList(55, 58, 39, 18, 90, 160, 150); + List<Integer> expected = Arrays.asList(55, 58, 90, 150, 160, 39, 18); + + List<Integer> result = lookScheduling.execute(requests); + assertEquals(expected, result); + } + + @Test + public void testLookSchedulingMovingDown() { + LookScheduling lookScheduling = new LookScheduling(50, false, 200); + List<Integer> requests = Arrays.asList(55, 58, 39, 18, 90, 160, 150); + List<Integer> expected = Arrays.asList(39, 18, 55, 58, 90, 150, 160); + + List<Integer> result = lookScheduling.execute(requests); + assertEquals(expected, result); + } + + @Test + public void testLookSchedulingEmptyRequests() { + LookScheduling lookScheduling = new LookScheduling(50, true, 200); + List<Integer> requests = emptyList(); + List<Integer> expected = emptyList(); + + List<Integer> result = lookScheduling.execute(requests); + assertEquals(expected, result); + } + + @Test + public void testLookSchedulingCurrentPosition() { + LookScheduling lookScheduling = new LookScheduling(50, true, 200); + + // Testing current position remains unchanged after scheduling. + assertEquals(50, lookScheduling.getCurrentPosition()); + } + + @Test + public void testLookSchedulingPrintStatus() { + LookScheduling lookScheduling = new LookScheduling(50, true, 200); + + List<Integer> requests = Arrays.asList(55, 58, 39, 18, 90, 160, 150); + + List<Integer> result = lookScheduling.execute(requests); + + List<Integer> expectedOrder = Arrays.asList(55, 58, 90, 150, 160, 39, 18); + assertEquals(expectedOrder, result); + + System.out.println("Final LookScheduling Position: " + lookScheduling.getCurrentPosition()); + System.out.println("LookScheduling Moving Up: " + lookScheduling.isMovingUp()); + + System.out.println("Farthest Position Reached: " + lookScheduling.getFarthestPosition()); + + System.out.println("Request Order: " + result); + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/diskscheduling/SSFSchedulingTest.java b/src/test/java/com/thealgorithms/scheduling/diskscheduling/SSFSchedulingTest.java new file mode 100644 index 000000000000..0239b0117f1b --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/diskscheduling/SSFSchedulingTest.java @@ -0,0 +1,55 @@ +package com.thealgorithms.scheduling.diskscheduling; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class SSFSchedulingTest { + + private SSFScheduling scheduler; + + @BeforeEach + public void setUp() { + scheduler = new SSFScheduling(50); + } + + @Test + public void testExecuteWithEmptyList() { + List<Integer> requests = new ArrayList<>(); + List<Integer> result = scheduler.execute(requests); + assertTrue(result.isEmpty(), "Result should be empty for an empty request list."); + } + + @Test + public void testExecuteWithSingleRequest() { + List<Integer> requests = new ArrayList<>(List.of(100)); + List<Integer> result = scheduler.execute(requests); + assertEquals(List.of(100), result, "The only request should be served first."); + } + + @Test + public void testExecuteWithMultipleRequests() { + List<Integer> requests = new ArrayList<>(List.of(10, 90, 60, 40, 30, 70)); + List<Integer> result = scheduler.execute(requests); + assertEquals(List.of(60, 70, 90, 40, 30, 10), result, "Requests should be served in the shortest seek first order."); + } + + @Test + public void testExecuteWithSameDistanceRequests() { + List<Integer> requests = new ArrayList<>(List.of(45, 55)); + List<Integer> result = scheduler.execute(requests); + assertEquals(List.of(45, 55), result, "When distances are equal, requests should be served in the order they appear in the list."); + } + + @Test + public void testGetCurrentPositionAfterExecution() { + List<Integer> requests = new ArrayList<>(List.of(10, 90, 60, 40, 30, 70)); + scheduler.execute(requests); + int currentPosition = scheduler.getCurrentPosition(); + assertEquals(10, currentPosition, "Current position should be the last request after execution."); + } +} diff --git a/src/test/java/com/thealgorithms/scheduling/diskscheduling/ScanSchedulingTest.java b/src/test/java/com/thealgorithms/scheduling/diskscheduling/ScanSchedulingTest.java new file mode 100644 index 000000000000..d1525d9a9c0d --- /dev/null +++ b/src/test/java/com/thealgorithms/scheduling/diskscheduling/ScanSchedulingTest.java @@ -0,0 +1,55 @@ +package com.thealgorithms.scheduling.diskscheduling; + +import static java.util.Collections.emptyList; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class ScanSchedulingTest { + + @Test + public void testScanSchedulingMovingUp() { + ScanScheduling scanScheduling = new ScanScheduling(50, true, 200); + List<Integer> requests = Arrays.asList(55, 58, 39, 18, 90, 160, 150); + List<Integer> expected = Arrays.asList(55, 58, 90, 150, 160, 199, 39, 18); + + List<Integer> result = scanScheduling.execute(requests); + assertEquals(expected, result); + } + + @Test + public void testScanSchedulingMovingDown() { + ScanScheduling scanScheduling = new ScanScheduling(50, false, 200); + List<Integer> requests = Arrays.asList(55, 58, 39, 18, 90, 160, 150); + List<Integer> expected = Arrays.asList(39, 18, 0, 55, 58, 90, 150, 160); + + List<Integer> result = scanScheduling.execute(requests); + assertEquals(expected, result); + } + + @Test + public void testScanSchedulingEmptyRequests() { + ScanScheduling scanScheduling = new ScanScheduling(50, true, 200); + List<Integer> requests = emptyList(); + List<Integer> expected = emptyList(); + + List<Integer> result = scanScheduling.execute(requests); + assertEquals(expected, result); + } + + @Test + public void testScanScheduling() { + ScanScheduling scanScheduling = new ScanScheduling(50, true, 200); + List<Integer> requests = Arrays.asList(55, 58, 39, 18, 90, 160, 150); + + List<Integer> result = scanScheduling.execute(requests); + List<Integer> expectedOrder = Arrays.asList(55, 58, 90, 150, 160, 199, 39, 18); + assertEquals(expectedOrder, result); + + System.out.println("Final Head Position: " + scanScheduling.getHeadPosition()); + System.out.println("Head Moving Up: " + scanScheduling.isMovingUp()); + System.out.println("Request Order: " + result); + } +} diff --git a/src/test/java/com/thealgorithms/searches/BM25InvertedIndexTest.java b/src/test/java/com/thealgorithms/searches/BM25InvertedIndexTest.java new file mode 100644 index 000000000000..2017c11dfb3c --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/BM25InvertedIndexTest.java @@ -0,0 +1,95 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +/** + * Test Cases for Inverted Index with BM25 + * @author Prayas Kumar (https://github.com/prayas7102) + */ + +class BM25InvertedIndexTest { + + private static BM25InvertedIndex index; + + @BeforeAll + static void setUp() { + index = new BM25InvertedIndex(); + index.addMovie(1, "The Shawshank Redemption", 9.3, 1994, "Hope is a good thing. Maybe the best of things. And no good thing ever dies."); + index.addMovie(2, "The Godfather", 9.2, 1972, "I'm gonna make him an offer he can't refuse."); + index.addMovie(3, "The Dark Knight", 9.0, 2008, "You either die a hero or live long enough to see yourself become the villain."); + index.addMovie(4, "Pulp Fiction", 8.9, 1994, "You know what they call a Quarter Pounder with Cheese in Paris? They call it a Royale with Cheese."); + index.addMovie(5, "Good Will Hunting", 8.3, 1997, "Will Hunting is a genius and he has a good heart. The best of his abilities is yet to be explored."); + index.addMovie(6, "It's a Wonderful Life", 8.6, 1946, "Each man's life touches so many other lives. If he wasn't around, it would leave an awfully good hole."); + index.addMovie(7, "The Pursuit of Happyness", 8.0, 2006, "It was the pursuit of a better life, and a good opportunity to change things for the better."); + index.addMovie(8, "A Few Good Men", 7.7, 1992, "You can't handle the truth! This movie has a lot of good moments and intense drama."); + } + + @Test + void testAddMovie() { + // Check that the index contains the correct number of movies + int moviesLength = index.getMoviesLength(); + assertEquals(8, moviesLength); + } + + @Test + void testSearchForTermFound() { + int expected = 1; + List<SearchResult> result = index.search("hope"); + int actual = result.getFirst().getDocId(); + assertEquals(expected, actual); + } + + @Test + void testSearchRanking() { + // Perform search for the term "good" + List<SearchResult> results = index.search("good"); + assertFalse(results.isEmpty()); + for (SearchResult result : results) { + System.out.println(result); + } + // Validate the ranking based on the provided relevance scores + assertEquals(1, results.get(0).getDocId()); // The Shawshank Redemption should be ranked 1st + assertEquals(8, results.get(1).getDocId()); // A Few Good Men should be ranked 2nd + assertEquals(5, results.get(2).getDocId()); // Good Will Hunting should be ranked 3rd + assertEquals(7, results.get(3).getDocId()); // The Pursuit of Happyness should be ranked 4th + assertEquals(6, results.get(4).getDocId()); // It's a Wonderful Life should be ranked 5th + + // Ensure the relevance scores are in descending order + for (int i = 0; i < results.size() - 1; i++) { + assertTrue(results.get(i).getRelevanceScore() > results.get(i + 1).getRelevanceScore()); + } + } + + @Test + void testSearchForTermNotFound() { + List<SearchResult> results = index.search("nonexistent"); + assertTrue(results.isEmpty()); + } + + @Test + void testSearchForCommonTerm() { + List<SearchResult> results = index.search("the"); + assertFalse(results.isEmpty()); + assertTrue(results.size() > 1); + } + + @Test + void testBM25ScoreCalculation() { + List<SearchResult> results = index.search("cheese"); + assertEquals(1, results.size()); + assertEquals(4, results.getFirst().docId); // Pulp Fiction should have the highest score + } + + @Test + void testCaseInsensitivity() { + List<SearchResult> resultsLowerCase = index.search("hope"); + List<SearchResult> resultsUpperCase = index.search("HOPE"); + assertEquals(resultsLowerCase, resultsUpperCase); + } +} diff --git a/src/test/java/com/thealgorithms/searches/BinarySearch2dArrayTest.java b/src/test/java/com/thealgorithms/searches/BinarySearch2dArrayTest.java new file mode 100644 index 000000000000..18f0afc6a0a6 --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/BinarySearch2dArrayTest.java @@ -0,0 +1,157 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.Arrays; +import org.junit.jupiter.api.Test; + +public class BinarySearch2dArrayTest { + + @Test + // valid test case + public void binarySearch2dArrayTestMiddle() { + int[][] arr = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; + int target = 6; + + int[] ans = BinarySearch2dArray.binarySearch(arr, target); + System.out.println(Arrays.toString(ans)); + assertEquals(1, ans[0]); + assertEquals(1, ans[1]); + } + + @Test + // valid test case + public void binarySearch2dArrayTestMiddleSide() { + int[][] arr = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; + int target = 8; + + int[] ans = BinarySearch2dArray.binarySearch(arr, target); + System.out.println(Arrays.toString(ans)); + assertEquals(1, ans[0]); + assertEquals(3, ans[1]); + } + + @Test + // valid test case + public void binarySearch2dArrayTestUpper() { + int[][] arr = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; + int target = 2; + + int[] ans = BinarySearch2dArray.binarySearch(arr, target); + System.out.println(Arrays.toString(ans)); + assertEquals(0, ans[0]); + assertEquals(1, ans[1]); + } + + @Test + // valid test case + public void binarySearch2dArrayTestUpperSide() { + int[][] arr = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; + int target = 1; + + int[] ans = BinarySearch2dArray.binarySearch(arr, target); + System.out.println(Arrays.toString(ans)); + assertEquals(0, ans[0]); + assertEquals(0, ans[1]); + } + + @Test + // valid test case + public void binarySearch2dArrayTestLower() { + int[][] arr = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; + int target = 10; + + int[] ans = BinarySearch2dArray.binarySearch(arr, target); + System.out.println(Arrays.toString(ans)); + assertEquals(2, ans[0]); + assertEquals(1, ans[1]); + } + + @Test + // valid test case + public void binarySearch2dArrayTestLowerSide() { + int[][] arr = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; + int target = 11; + + int[] ans = BinarySearch2dArray.binarySearch(arr, target); + System.out.println(Arrays.toString(ans)); + assertEquals(2, ans[0]); + assertEquals(2, ans[1]); + } + + @Test + // valid test case + public void binarySearch2dArrayTestNotFound() { + int[][] arr = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; + int target = 101; + + int[] ans = BinarySearch2dArray.binarySearch(arr, target); + System.out.println(Arrays.toString(ans)); + assertEquals(-1, ans[0]); + assertEquals(-1, ans[1]); + } + + /** + * Test if the method works with input arrays consisting only of one row. + */ + @Test + public void binarySearch2dArrayTestOneRow() { + int[][] arr = {{1, 2, 3, 4}}; + int target = 2; + + // Assert that the requirement, that the array only has one row, is fulfilled. + assertEquals(arr.length, 1); + int[] ans = BinarySearch2dArray.binarySearch(arr, target); + System.out.println(Arrays.toString(ans)); + assertEquals(0, ans[0]); + assertEquals(1, ans[1]); + } + + /** + * Test if the method works with the target in the middle of the input. + */ + @Test + public void binarySearch2dArrayTestTargetInMiddle() { + int[][] arr = {{1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}, {11, 12, 13, 14, 15}}; + int target = 8; + // Assert that the requirement, that the target is in the middle row and middle column, is + // fulfilled. + assertEquals(arr[arr.length / 2][arr[0].length / 2], target); + int[] ans = BinarySearch2dArray.binarySearch(arr, target); + System.out.println(Arrays.toString(ans)); + assertEquals(1, ans[0]); + assertEquals(2, ans[1]); + } + + /** + * Test if the method works with the target in the middle column, + * in the row above the middle row. + */ + @Test + public void binarySearch2dArrayTestTargetAboveMiddleRowInMiddleColumn() { + int[][] arr = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; + int target = 3; + + // Assert that the requirement, that he target is in the middle column, + // in an array with an even number of columns, and on the row "above" the middle row. + assertEquals(arr[0].length % 2, 0); + assertEquals(arr[arr.length / 2 - 1][arr[0].length / 2], target); + int[] ans = BinarySearch2dArray.binarySearch(arr, target); + System.out.println(Arrays.toString(ans)); + assertEquals(0, ans[0]); + assertEquals(2, ans[1]); + } + + /** + * Test if the method works with an empty array. + */ + @Test + public void binarySearch2dArrayTestEmptyArray() { + int[][] arr = {}; + int target = 5; + + // Assert that an empty array is not valid input for the method. + assertThrows(ArrayIndexOutOfBoundsException.class, () -> BinarySearch2dArray.binarySearch(arr, target)); + } +} diff --git a/src/test/java/com/thealgorithms/searches/BinarySearchTest.java b/src/test/java/com/thealgorithms/searches/BinarySearchTest.java new file mode 100644 index 000000000000..bd4620a7fa7d --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/BinarySearchTest.java @@ -0,0 +1,108 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.IntStream; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the BinarySearch class. + */ +class BinarySearchTest { + + /** + * Test for basic binary search functionality. + */ + @Test + void testBinarySearchFound() { + BinarySearch binarySearch = new BinarySearch(); + Integer[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + int key = 7; + int expectedIndex = 6; // Index of the key in the array + assertEquals(expectedIndex, binarySearch.find(array, key), "The index of the found element should be 6."); + } + + /** + * Test for binary search when the element is not present. + */ + @Test + void testBinarySearchNotFound() { + BinarySearch binarySearch = new BinarySearch(); + Integer[] array = {1, 2, 3, 4, 5}; + int key = 6; // Element not present in the array + int expectedIndex = -1; // Key not found + assertEquals(expectedIndex, binarySearch.find(array, key), "The element should not be found in the array."); + } + + /** + * Test for binary search with first element as the key. + */ + @Test + void testBinarySearchFirstElement() { + BinarySearch binarySearch = new BinarySearch(); + Integer[] array = {1, 2, 3, 4, 5}; + int key = 1; // First element + int expectedIndex = 0; // Index of the key in the array + assertEquals(expectedIndex, binarySearch.find(array, key), "The index of the first element should be 0."); + } + + /** + * Test for binary search with last element as the key. + */ + @Test + void testBinarySearchLastElement() { + BinarySearch binarySearch = new BinarySearch(); + Integer[] array = {1, 2, 3, 4, 5}; + int key = 5; // Last element + int expectedIndex = 4; // Index of the key in the array + assertEquals(expectedIndex, binarySearch.find(array, key), "The index of the last element should be 4."); + } + + /** + * Test for binary search with a single element present. + */ + @Test + void testBinarySearchSingleElementFound() { + BinarySearch binarySearch = new BinarySearch(); + Integer[] array = {1}; + int key = 1; // Only element present + int expectedIndex = 0; // Index of the key in the array + assertEquals(expectedIndex, binarySearch.find(array, key), "The index of the single element should be 0."); + } + + /** + * Test for binary search with a single element not present. + */ + @Test + void testBinarySearchSingleElementNotFound() { + BinarySearch binarySearch = new BinarySearch(); + Integer[] array = {1}; + int key = 2; // Key not present + int expectedIndex = -1; // Key not found + assertEquals(expectedIndex, binarySearch.find(array, key), "The element should not be found in the array."); + } + + /** + * Test for binary search with an empty array. + */ + @Test + void testBinarySearchEmptyArray() { + BinarySearch binarySearch = new BinarySearch(); + Integer[] array = {}; // Empty array + int key = 1; // Key not present + int expectedIndex = -1; // Key not found + assertEquals(expectedIndex, binarySearch.find(array, key), "The element should not be found in an empty array."); + } + + /** + * Test for binary search on large array. + */ + @Test + void testBinarySearchLargeArray() { + BinarySearch binarySearch = new BinarySearch(); + Integer[] array = IntStream.range(0, 10000).boxed().toArray(Integer[] ::new); // Array from 0 to 9999 + int key = 9999; // Last element + int expectedIndex = 9999; // Index of the last element + assertEquals(expectedIndex, binarySearch.find(array, key), "The index of the last element should be 9999."); + } +} diff --git a/src/test/java/com/thealgorithms/searches/BreadthFirstSearchTest.java b/src/test/java/com/thealgorithms/searches/BreadthFirstSearchTest.java new file mode 100644 index 000000000000..d05856dd29ad --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/BreadthFirstSearchTest.java @@ -0,0 +1,104 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.thealgorithms.datastructures.Node; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class BreadthFirstSearchTest { + private Node<String> root; + private BreadthFirstSearch<String> bfs; + + // Tree structure: + // + // A + // / | \ + // B C D + // / \ + // E F + + @BeforeEach + public void setUp() { + // nodes declaration + root = new Node<>("A"); + + var nodeB = new Node<>("B"); + var nodeC = new Node<>("C"); + var nodeD = new Node<>("D"); + + var nodeE = new Node<>("E"); + var nodeF = new Node<>("F"); + + // tree initialization + root.addChild(nodeB); + root.addChild(nodeC); + root.addChild(nodeD); + + nodeB.addChild(nodeE); + nodeB.addChild(nodeF); + + // create an instance to monitor the visited nodes + bfs = new BreadthFirstSearch<>(); + } + + @Test + public void testSearchRoot() { + String expectedValue = "A"; + List<String> expectedPath = List.of("A"); + + // check value + Optional<Node<String>> value = bfs.search(root, expectedValue); + assertEquals(expectedValue, value.orElseGet(() -> new Node<>("")).getValue()); + + // check path + assertArrayEquals(expectedPath.toArray(), bfs.getVisited().toArray()); + } + + @Test + public void testSearchF() { + String expectedValue = "F"; + List<String> expectedPath = List.of("A", "B", "C", "D", "E", "F"); + + // check value + Optional<Node<String>> value = Optional.of(bfs.search(root, expectedValue).orElseGet(() -> new Node<>(null))); + assertEquals(expectedValue, value.get().getValue()); + + // check path + assertArrayEquals(expectedPath.toArray(), bfs.getVisited().toArray()); + } + + @Test + void testSearchNull() { + List<String> expectedPath = List.of("A", "B", "C", "D", "E", "F"); + Optional<Node<String>> node = bfs.search(root, null); + + // check value + assertTrue(node.isEmpty()); + + // check path + assertArrayEquals(expectedPath.toArray(), bfs.getVisited().toArray()); + } + + @Test + void testNullRoot() { + var value = bfs.search(null, "B"); + assertTrue(value.isEmpty()); + } + + @Test + void testSearchValueThatNotExists() { + List<String> expectedPath = List.of("A", "B", "C", "D", "E", "F"); + var value = bfs.search(root, "Z"); + + // check that the value is empty because it's not exists in the tree + assertTrue(value.isEmpty()); + + // check path is the whole list + assertArrayEquals(expectedPath.toArray(), bfs.getVisited().toArray()); + } +} diff --git a/src/test/java/com/thealgorithms/searches/DepthFirstSearchTest.java b/src/test/java/com/thealgorithms/searches/DepthFirstSearchTest.java new file mode 100644 index 000000000000..2785d48b70cf --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/DepthFirstSearchTest.java @@ -0,0 +1,93 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.thealgorithms.datastructures.Node; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class DepthFirstSearchTest { + + private Node<Integer> root; + private DepthFirstSearch<Integer> dfs; + + // + // Tree structure: + // 1 + // / | \ + // 2 3 4 + // / \ + // 5 6 + + @BeforeEach + public void setUp() { + // nodes declaration + root = new Node<>(1); + + var nodeB = new Node<>(2); + var nodeC = new Node<>(3); + var nodeD = new Node<>(4); + + var nodeE = new Node<>(5); + var nodeF = new Node<>(6); + + // tree initialization + root.addChild(nodeB); + root.addChild(nodeC); + root.addChild(nodeD); + + nodeB.addChild(nodeE); + nodeB.addChild(nodeF); + + // create an instance to monitor the visited nodes + dfs = new DepthFirstSearch<>(); + } + + @Test + public void testSearchRoot() { + Integer expectedValue = 1; + List<Integer> expectedPath = List.of(1); + + // check value + Optional<Node<Integer>> value = dfs.recursiveSearch(root, expectedValue); + assertEquals(expectedValue, value.orElseGet(() -> new Node<>(null)).getValue()); + + // check path + assertArrayEquals(expectedPath.toArray(), dfs.getVisited().toArray()); + } + + @Test + public void testSearch4() { + Integer expectedValue = 4; + List<Integer> expectedPath = List.of(1, 2, 5, 6, 3, 4); + + // check value + Optional<Node<Integer>> value = dfs.recursiveSearch(root, expectedValue); + assertEquals(expectedValue, value.orElseGet(() -> new Node<>(null)).getValue()); + + // check path + assertArrayEquals(expectedPath.toArray(), dfs.getVisited().toArray()); + } + + @Test + void testNullRoot() { + var value = dfs.recursiveSearch(null, 4); + assertTrue(value.isEmpty()); + } + + @Test + void testSearchValueThatNotExists() { + List<Integer> expectedPath = List.of(1, 2, 5, 6, 3, 4); + var value = dfs.recursiveSearch(root, 10); + + // check that the value is empty because it's not exists in the tree + assertTrue(value.isEmpty()); + + // check path is the whole list + assertArrayEquals(expectedPath.toArray(), dfs.getVisited().toArray()); + } +} diff --git a/src/test/java/com/thealgorithms/searches/ExponentialSearchTest.java b/src/test/java/com/thealgorithms/searches/ExponentialSearchTest.java new file mode 100644 index 000000000000..c84da531e8a4 --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/ExponentialSearchTest.java @@ -0,0 +1,84 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.IntStream; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the ExponentialSearch class. + */ +class ExponentialSearchTest { + + /** + * Test for basic exponential search functionality. + */ + @Test + void testExponentialSearchFound() { + ExponentialSearch exponentialSearch = new ExponentialSearch(); + Integer[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + int key = 7; + int expectedIndex = 6; // Index of the key in the array + assertEquals(expectedIndex, exponentialSearch.find(array, key), "The index of the found element should be 6."); + } + + /** + * Test for exponential search with the first element as the key. + */ + @Test + void testExponentialSearchFirstElement() { + ExponentialSearch exponentialSearch = new ExponentialSearch(); + Integer[] array = {1, 2, 3, 4, 5}; + int key = 1; // First element + int expectedIndex = 0; // Index of the key in the array + assertEquals(expectedIndex, exponentialSearch.find(array, key), "The index of the first element should be 0."); + } + + /** + * Test for exponential search with the last element as the key. + */ + @Test + void testExponentialSearchLastElement() { + ExponentialSearch exponentialSearch = new ExponentialSearch(); + Integer[] array = {1, 2, 3, 4, 5}; + int key = 5; // Last element + int expectedIndex = 4; // Index of the key in the array + assertEquals(expectedIndex, exponentialSearch.find(array, key), "The index of the last element should be 4."); + } + + /** + * Test for exponential search with a single element present. + */ + @Test + void testExponentialSearchSingleElementFound() { + ExponentialSearch exponentialSearch = new ExponentialSearch(); + Integer[] array = {1}; + int key = 1; // Only element present + int expectedIndex = 0; // Index of the key in the array + assertEquals(expectedIndex, exponentialSearch.find(array, key), "The index of the single element should be 0."); + } + + /** + * Test for exponential search with an empty array. + */ + @Test + void testExponentialSearchEmptyArray() { + ExponentialSearch exponentialSearch = new ExponentialSearch(); + Integer[] array = {}; // Empty array + int key = 1; // Key not present + int expectedIndex = -1; // Key not found + assertEquals(expectedIndex, exponentialSearch.find(array, key), "The element should not be found in an empty array."); + } + + /** + * Test for exponential search on large array. + */ + @Test + void testExponentialSearchLargeArray() { + ExponentialSearch exponentialSearch = new ExponentialSearch(); + Integer[] array = IntStream.range(0, 10000).boxed().toArray(Integer[] ::new); // Array from 0 to 9999 + int key = 9999; + int expectedIndex = 9999; + assertEquals(expectedIndex, exponentialSearch.find(array, key), "The index of the last element should be 9999."); + } +} diff --git a/src/test/java/com/thealgorithms/searches/FibonacciSearchTest.java b/src/test/java/com/thealgorithms/searches/FibonacciSearchTest.java new file mode 100644 index 000000000000..801c33b1d09a --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/FibonacciSearchTest.java @@ -0,0 +1,124 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.IntStream; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the FibonacciSearch class. + */ +class FibonacciSearchTest { + + /** + * Test for basic Fibonacci search functionality. + */ + @Test + void testFibonacciSearchFound() { + FibonacciSearch fibonacciSearch = new FibonacciSearch(); + Integer[] array = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512}; + int key = 128; + int expectedIndex = 7; // Index of the key in the array + assertEquals(expectedIndex, fibonacciSearch.find(array, key), "The index of the found element should be 7."); + } + + /** + * Test for Fibonacci search when the element is not present. + */ + @Test + void testFibonacciSearchNotFound() { + FibonacciSearch fibonacciSearch = new FibonacciSearch(); + Integer[] array = {1, 2, 4, 8, 16}; + int key = 6; // Element not present in the array + int expectedIndex = -1; // Key not found + assertEquals(expectedIndex, fibonacciSearch.find(array, key), "The element should not be found in the array."); + } + + /** + * Test for Fibonacci search with the first element as the key. + */ + @Test + void testFibonacciSearchFirstElement() { + FibonacciSearch fibonacciSearch = new FibonacciSearch(); + Integer[] array = {1, 2, 4, 8, 16}; + int key = 1; // First element + int expectedIndex = 0; // Index of the key in the array + assertEquals(expectedIndex, fibonacciSearch.find(array, key), "The index of the first element should be 0."); + } + + /** + * Test for Fibonacci search with the last element as the key. + */ + @Test + void testFibonacciSearchLastElement() { + FibonacciSearch fibonacciSearch = new FibonacciSearch(); + Integer[] array = {1, 2, 4, 8, 16}; + int key = 16; // Last element + int expectedIndex = 4; // Index of the key in the array + assertEquals(expectedIndex, fibonacciSearch.find(array, key), "The index of the last element should be 4."); + } + + /** + * Test for Fibonacci search with a single element present. + */ + @Test + void testFibonacciSearchSingleElementFound() { + FibonacciSearch fibonacciSearch = new FibonacciSearch(); + Integer[] array = {1}; + int key = 1; // Only element present + int expectedIndex = 0; // Index of the key in the array + assertEquals(expectedIndex, fibonacciSearch.find(array, key), "The index of the single element should be 0."); + } + + /** + * Test for Fibonacci search with a single element not present. + */ + @Test + void testFibonacciSearchSingleElementNotFound() { + FibonacciSearch fibonacciSearch = new FibonacciSearch(); + Integer[] array = {1}; + int key = 2; // Key not present + int expectedIndex = -1; // Key not found + assertEquals(expectedIndex, fibonacciSearch.find(array, key), "The element should not be found in the array."); + } + + /** + * Test for Fibonacci search with an empty array. + */ + @Test + void testFibonacciSearchEmptyArray() { + FibonacciSearch fibonacciSearch = new FibonacciSearch(); + Integer[] array = {}; // Empty array + int key = 1; // Key not present + assertThrows(IllegalArgumentException.class, () -> fibonacciSearch.find(array, key), "An empty array should throw an IllegalArgumentException."); + } + + @Test + void testFibonacciSearchUnsortedArray() { + FibonacciSearch fibonacciSearch = new FibonacciSearch(); + Integer[] array = {2, 1, 4, 3, 6, 5}; + int key = 3; // Key not present + assertThrows(IllegalArgumentException.class, () -> fibonacciSearch.find(array, key), "An unsorted array should throw an IllegalArgumentException."); + } + + @Test + void testFibonacciSearchNullKey() { + FibonacciSearch fibonacciSearch = new FibonacciSearch(); + Integer[] array = {1, 2, 4, 8, 16}; + Integer key = null; // Null key + assertThrows(IllegalArgumentException.class, () -> fibonacciSearch.find(array, key), "A null key should throw an IllegalArgumentException."); + } + + /** + * Test for Fibonacci search on large array. + */ + @Test + void testFibonacciSearchLargeArray() { + FibonacciSearch fibonacciSearch = new FibonacciSearch(); + Integer[] array = IntStream.range(0, 10000).boxed().toArray(Integer[] ::new); // Array from 0 to 9999 + int key = 9999; + int expectedIndex = 9999; + assertEquals(expectedIndex, fibonacciSearch.find(array, key), "The index of the last element should be 9999."); + } +} diff --git a/src/test/java/com/thealgorithms/searches/HowManyTimesRotatedTest.java b/src/test/java/com/thealgorithms/searches/HowManyTimesRotatedTest.java new file mode 100644 index 000000000000..7d52e9fb4eca --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/HowManyTimesRotatedTest.java @@ -0,0 +1,16 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class HowManyTimesRotatedTest { + + @Test + public void testHowManyTimesRotated() { + int[] arr1 = {5, 1, 2, 3, 4}; + assertEquals(1, HowManyTimesRotated.rotated(arr1)); + int[] arr2 = {15, 17, 2, 3, 5}; + assertEquals(2, HowManyTimesRotated.rotated(arr2)); + } +} diff --git a/src/test/java/com/thealgorithms/searches/InterpolationSearchTest.java b/src/test/java/com/thealgorithms/searches/InterpolationSearchTest.java new file mode 100644 index 000000000000..b3b7e7ef129c --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/InterpolationSearchTest.java @@ -0,0 +1,90 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.IntStream; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the InterpolationSearch class. + */ +class InterpolationSearchTest { + + /** + * Test for basic interpolation search functionality when the element is found. + */ + @Test + void testInterpolationSearchFound() { + InterpolationSearch interpolationSearch = new InterpolationSearch(); + int[] array = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512}; + int key = 128; + int expectedIndex = 7; // Index of the key in the array + assertEquals(expectedIndex, interpolationSearch.find(array, key), "The index of the found element should be 7."); + } + + /** + * Test for interpolation search when the element is not present in the array. + */ + @Test + void testInterpolationSearchNotFound() { + InterpolationSearch interpolationSearch = new InterpolationSearch(); + int[] array = {1, 2, 4, 8, 16}; + int key = 6; // Element not present in the array + assertEquals(-1, interpolationSearch.find(array, key), "The element should not be found in the array."); + } + + /** + * Test for interpolation search with the first element as the key. + */ + @Test + void testInterpolationSearchFirstElement() { + InterpolationSearch interpolationSearch = new InterpolationSearch(); + int[] array = {1, 2, 4, 8, 16}; + int key = 1; // First element + assertEquals(0, interpolationSearch.find(array, key), "The index of the first element should be 0."); + } + + /** + * Test for interpolation search with a single element not present. + */ + @Test + void testInterpolationSearchSingleElementNotFound() { + InterpolationSearch interpolationSearch = new InterpolationSearch(); + int[] array = {1}; + int key = 2; // Key not present + assertEquals(-1, interpolationSearch.find(array, key), "The element should not be found in the array."); + } + + /** + * Test for interpolation search with an empty array. + */ + @Test + void testInterpolationSearchEmptyArray() { + InterpolationSearch interpolationSearch = new InterpolationSearch(); + int[] array = {}; // Empty array + int key = 1; // Key not present + assertEquals(-1, interpolationSearch.find(array, key), "The element should not be found in an empty array."); + } + + /** + * Test for interpolation search on large uniformly distributed array. + */ + @Test + void testInterpolationSearchLargeUniformArray() { + InterpolationSearch interpolationSearch = new InterpolationSearch(); + int[] array = IntStream.range(0, 10000).map(i -> i * 2).toArray(); // Array from 0 to 19998, step 2 + int key = 9998; // Last even number in the array + assertEquals(4999, interpolationSearch.find(array, key), "The index of the last element should be 4999."); + } + + /** + * Test for interpolation search on large non-uniformly distributed array. + */ + @Test + void testInterpolationSearchLargeNonUniformArray() { + InterpolationSearch interpolationSearch = new InterpolationSearch(); + int[] array = {1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144}; // Fibonacci numbers + int key = 21; // Present in the array + assertEquals(6, interpolationSearch.find(array, key), "The index of the found element should be 6."); + } +} diff --git a/src/test/java/com/thealgorithms/searches/IterativeBinarySearchTest.java b/src/test/java/com/thealgorithms/searches/IterativeBinarySearchTest.java new file mode 100644 index 000000000000..b2e121ac1ba0 --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/IterativeBinarySearchTest.java @@ -0,0 +1,117 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the IterativeBinarySearch class. + */ +class IterativeBinarySearchTest { + + /** + * Test for basic binary search functionality when the element is found. + */ + @Test + void testBinarySearchFound() { + IterativeBinarySearch binarySearch = new IterativeBinarySearch(); + Integer[] array = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512}; + Integer key = 128; + int expectedIndex = 7; // Index of the key in the array + assertEquals(expectedIndex, binarySearch.find(array, key), "The index of the found element should be 7."); + } + + /** + * Test for binary search when the element is not present in the array. + */ + @Test + void testBinarySearchNotFound() { + IterativeBinarySearch binarySearch = new IterativeBinarySearch(); + Integer[] array = {1, 2, 4, 8, 16}; + Integer key = 6; // Element not present in the array + assertEquals(-1, binarySearch.find(array, key), "The element should not be found in the array."); + } + + /** + * Test for binary search with the first element as the key. + */ + @Test + void testBinarySearchFirstElement() { + IterativeBinarySearch binarySearch = new IterativeBinarySearch(); + Integer[] array = {1, 2, 4, 8, 16}; + Integer key = 1; // First element + assertEquals(0, binarySearch.find(array, key), "The index of the first element should be 0."); + } + + /** + * Test for binary search with the last element as the key. + */ + @Test + void testBinarySearchLastElement() { + IterativeBinarySearch binarySearch = new IterativeBinarySearch(); + Integer[] array = {1, 2, 4, 8, 16}; + Integer key = 16; // Last element + assertEquals(4, binarySearch.find(array, key), "The index of the last element should be 4."); + } + + /** + * Test for binary search with a single element present. + */ + @Test + void testBinarySearchSingleElementFound() { + IterativeBinarySearch binarySearch = new IterativeBinarySearch(); + Integer[] array = {1}; + Integer key = 1; // Only element present + assertEquals(0, binarySearch.find(array, key), "The index of the single element should be 0."); + } + + /** + * Test for binary search with a single element not present. + */ + @Test + void testBinarySearchSingleElementNotFound() { + IterativeBinarySearch binarySearch = new IterativeBinarySearch(); + Integer[] array = {1}; + Integer key = 2; // Key not present + assertEquals(-1, binarySearch.find(array, key), "The element should not be found in the array."); + } + + /** + * Test for binary search with an empty array. + */ + @Test + void testBinarySearchEmptyArray() { + IterativeBinarySearch binarySearch = new IterativeBinarySearch(); + Integer[] array = {}; // Empty array + Integer key = 1; // Key not present + assertEquals(-1, binarySearch.find(array, key), "The element should not be found in an empty array."); + } + + /** + * Test for binary search on a large array. + */ + @Test + void testBinarySearchLargeArray() { + IterativeBinarySearch binarySearch = new IterativeBinarySearch(); + Integer[] array = new Integer[10000]; + for (int i = 0; i < array.length; i++) { + array[i] = i * 2; + } // Array from 0 to 19998, step 2 + Integer key = 9998; // Present in the array + assertEquals(4999, binarySearch.find(array, key), "The index of the found element should be 4999."); + } + + /** + * Test for binary search on large array with a non-existent key. + */ + @Test + void testBinarySearchLargeArrayNotFound() { + IterativeBinarySearch binarySearch = new IterativeBinarySearch(); + Integer[] array = new Integer[10000]; + for (int i = 0; i < array.length; i++) { + array[i] = i * 2; + } // Array from 0 to 19998, step 2 + Integer key = 9999; // Key not present + assertEquals(-1, binarySearch.find(array, key), "The element should not be found in the array."); + } +} diff --git a/src/test/java/com/thealgorithms/searches/IterativeTernarySearchTest.java b/src/test/java/com/thealgorithms/searches/IterativeTernarySearchTest.java new file mode 100644 index 000000000000..c7640e6d0672 --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/IterativeTernarySearchTest.java @@ -0,0 +1,117 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the IterativeTernarySearch class. + */ +class IterativeTernarySearchTest { + + /** + * Test for basic ternary search functionality when the element is found. + */ + @Test + void testTernarySearchFound() { + IterativeTernarySearch ternarySearch = new IterativeTernarySearch(); + Integer[] array = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512}; + Integer key = 128; + int expectedIndex = 7; // Index of the key in the array + assertEquals(expectedIndex, ternarySearch.find(array, key), "The index of the found element should be 7."); + } + + /** + * Test for ternary search when the element is not present in the array. + */ + @Test + void testTernarySearchNotFound() { + IterativeTernarySearch ternarySearch = new IterativeTernarySearch(); + Integer[] array = {1, 2, 4, 8, 16}; + Integer key = 6; // Element not present in the array + assertEquals(-1, ternarySearch.find(array, key), "The element should not be found in the array."); + } + + /** + * Test for ternary search with the first element as the key. + */ + @Test + void testTernarySearchFirstElement() { + IterativeTernarySearch ternarySearch = new IterativeTernarySearch(); + Integer[] array = {1, 2, 4, 8, 16}; + Integer key = 1; // First element + assertEquals(0, ternarySearch.find(array, key), "The index of the first element should be 0."); + } + + /** + * Test for ternary search with the last element as the key. + */ + @Test + void testTernarySearchLastElement() { + IterativeTernarySearch ternarySearch = new IterativeTernarySearch(); + Integer[] array = {1, 2, 4, 8, 16}; + Integer key = 16; // Last element + assertEquals(4, ternarySearch.find(array, key), "The index of the last element should be 4."); + } + + /** + * Test for ternary search with a single element present. + */ + @Test + void testTernarySearchSingleElementFound() { + IterativeTernarySearch ternarySearch = new IterativeTernarySearch(); + Integer[] array = {1}; + Integer key = 1; // Only element present + assertEquals(0, ternarySearch.find(array, key), "The index of the single element should be 0."); + } + + /** + * Test for ternary search with a single element not present. + */ + @Test + void testTernarySearchSingleElementNotFound() { + IterativeTernarySearch ternarySearch = new IterativeTernarySearch(); + Integer[] array = {1}; + Integer key = 2; // Key not present + assertEquals(-1, ternarySearch.find(array, key), "The element should not be found in the array."); + } + + /** + * Test for ternary search with an empty array. + */ + @Test + void testTernarySearchEmptyArray() { + IterativeTernarySearch ternarySearch = new IterativeTernarySearch(); + Integer[] array = {}; // Empty array + Integer key = 1; // Key not present + assertEquals(-1, ternarySearch.find(array, key), "The element should not be found in an empty array."); + } + + /** + * Test for ternary search on a large array. + */ + @Test + void testTernarySearchLargeArray() { + IterativeTernarySearch ternarySearch = new IterativeTernarySearch(); + Integer[] array = new Integer[10000]; + for (int i = 0; i < array.length; i++) { + array[i] = i * 2; + } // Array from 0 to 19998, step 2 + Integer key = 9998; // Present in the array + assertEquals(4999, ternarySearch.find(array, key), "The index of the found element should be 4999."); + } + + /** + * Test for ternary search on large array with a non-existent key. + */ + @Test + void testTernarySearchLargeArrayNotFound() { + IterativeTernarySearch ternarySearch = new IterativeTernarySearch(); + Integer[] array = new Integer[10000]; + for (int i = 0; i < array.length; i++) { + array[i] = i * 2; + } // Array from 0 to 19998, step 2 + Integer key = 9999; // Key not present + assertEquals(-1, ternarySearch.find(array, key), "The element should not be found in the array."); + } +} diff --git a/src/test/java/com/thealgorithms/searches/JumpSearchTest.java b/src/test/java/com/thealgorithms/searches/JumpSearchTest.java new file mode 100644 index 000000000000..3fa319b66a41 --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/JumpSearchTest.java @@ -0,0 +1,94 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the JumpSearch class. + */ +class JumpSearchTest { + + /** + * Test for finding an element present in the array. + */ + @Test + void testJumpSearchFound() { + JumpSearch jumpSearch = new JumpSearch(); + Integer[] array = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + Integer key = 5; // Element to find + assertEquals(5, jumpSearch.find(array, key), "The index of the found element should be 5."); + } + + /** + * Test for finding the first element in the array. + */ + @Test + void testJumpSearchFirstElement() { + JumpSearch jumpSearch = new JumpSearch(); + Integer[] array = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + Integer key = 0; // First element + assertEquals(0, jumpSearch.find(array, key), "The index of the first element should be 0."); + } + + /** + * Test for finding the last element in the array. + */ + @Test + void testJumpSearchLastElement() { + JumpSearch jumpSearch = new JumpSearch(); + Integer[] array = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + Integer key = 10; // Last element + assertEquals(10, jumpSearch.find(array, key), "The index of the last element should be 10."); + } + + /** + * Test for finding an element not present in the array. + */ + @Test + void testJumpSearchNotFound() { + JumpSearch jumpSearch = new JumpSearch(); + Integer[] array = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + Integer key = -1; // Element not in the array + assertEquals(-1, jumpSearch.find(array, key), "The element should not be found in the array."); + } + + /** + * Test for finding an element in an empty array. + */ + @Test + void testJumpSearchEmptyArray() { + JumpSearch jumpSearch = new JumpSearch(); + Integer[] array = {}; // Empty array + Integer key = 1; // Key not present + assertEquals(-1, jumpSearch.find(array, key), "The element should not be found in an empty array."); + } + + /** + * Test for finding an element in a large array. + */ + @Test + void testJumpSearchLargeArray() { + JumpSearch jumpSearch = new JumpSearch(); + Integer[] array = new Integer[1000]; + for (int i = 0; i < array.length; i++) { + array[i] = i * 2; // Fill the array with even numbers + } + Integer key = 256; // Present in the array + assertEquals(128, jumpSearch.find(array, key), "The index of the found element should be 128."); + } + + /** + * Test for finding an element in a large array when it is not present. + */ + @Test + void testJumpSearchLargeArrayNotFound() { + JumpSearch jumpSearch = new JumpSearch(); + Integer[] array = new Integer[1000]; + for (int i = 0; i < array.length; i++) { + array[i] = i * 2; // Fill the array with even numbers + } + Integer key = 999; // Key not present + assertEquals(-1, jumpSearch.find(array, key), "The element should not be found in the array."); + } +} diff --git a/src/test/java/com/thealgorithms/searches/KMPSearchTest.java b/src/test/java/com/thealgorithms/searches/KMPSearchTest.java new file mode 100644 index 000000000000..cb804ac6a6a3 --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/KMPSearchTest.java @@ -0,0 +1,63 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class KMPSearchTest { + + @Test + // valid test case + public void kmpSearchTestLast() { + String txt = "ABABDABACDABABCABAB"; + String pat = "ABABCABAB"; + KMPSearch kmpSearch = new KMPSearch(); + int value = kmpSearch.kmpSearch(pat, txt); + System.out.println(value); + assertEquals(value, 10); + } + + @Test + // valid test case + public void kmpSearchTestFront() { + String txt = "AAAAABAAABA"; + String pat = "AAAA"; + KMPSearch kmpSearch = new KMPSearch(); + int value = kmpSearch.kmpSearch(pat, txt); + System.out.println(value); + assertEquals(value, 0); + } + + @Test + // valid test case + public void kmpSearchTestMiddle() { + String txt = "AAACAAAAAC"; + String pat = "AAAA"; + KMPSearch kmpSearch = new KMPSearch(); + int value = kmpSearch.kmpSearch(pat, txt); + System.out.println(value); + assertEquals(value, 4); + } + + @Test + // valid test case + public void kmpSearchTestNotFound() { + String txt = "AAABAAAA"; + String pat = "AAAA"; + KMPSearch kmpSearch = new KMPSearch(); + int value = kmpSearch.kmpSearch(pat, txt); + System.out.println(value); + assertEquals(value, 4); + } + + @Test + // not valid test case + public void kmpSearchTest4() { + String txt = "AABAAA"; + String pat = "AAAA"; + KMPSearch kmpSearch = new KMPSearch(); + int value = kmpSearch.kmpSearch(pat, txt); + System.out.println(value); + assertEquals(value, -1); + } +} diff --git a/src/test/java/com/thealgorithms/searches/LinearSearchTest.java b/src/test/java/com/thealgorithms/searches/LinearSearchTest.java new file mode 100644 index 000000000000..5c09dec6d578 --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/LinearSearchTest.java @@ -0,0 +1,118 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Random; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the LinearSearch class. + */ +class LinearSearchTest { + + /** + * Test for finding an element present in the array. + */ + @Test + void testLinearSearchFound() { + LinearSearch linearSearch = new LinearSearch(); + Integer[] array = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + Integer key = 5; // Element to find + assertEquals(5, linearSearch.find(array, key), "The index of the found element should be 5."); + } + + /** + * Test for finding the first element in the array. + */ + @Test + void testLinearSearchFirstElement() { + LinearSearch linearSearch = new LinearSearch(); + Integer[] array = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + Integer key = 0; // First element + assertEquals(0, linearSearch.find(array, key), "The index of the first element should be 0."); + } + + /** + * Test for finding the last element in the array. + */ + @Test + void testLinearSearchLastElement() { + LinearSearch linearSearch = new LinearSearch(); + Integer[] array = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + Integer key = 10; // Last element + assertEquals(10, linearSearch.find(array, key), "The index of the last element should be 10."); + } + + /** + * Test for finding an element not present in the array. + */ + @Test + void testLinearSearchNotFound() { + LinearSearch linearSearch = new LinearSearch(); + Integer[] array = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + Integer key = -1; // Element not in the array + assertEquals(-1, linearSearch.find(array, key), "The element should not be found in the array."); + } + + /** + * Test for finding an element in an empty array. + */ + @Test + void testLinearSearchEmptyArray() { + LinearSearch linearSearch = new LinearSearch(); + Integer[] array = {}; // Empty array + Integer key = 1; // Key not present + assertEquals(-1, linearSearch.find(array, key), "The element should not be found in an empty array."); + } + + /** + * Test for finding an element in a large array. + */ + @Test + void testLinearSearchLargeArray() { + LinearSearch linearSearch = new LinearSearch(); + Integer[] array = new Integer[1000]; + for (int i = 0; i < array.length; i++) { + array[i] = i; // Fill the array with integers 0 to 999 + } + Integer key = 256; // Present in the array + assertEquals(256, linearSearch.find(array, key), "The index of the found element should be 256."); + } + + /** + * Test for finding an element in a large array when it is not present. + */ + @Test + void testLinearSearchLargeArrayNotFound() { + LinearSearch linearSearch = new LinearSearch(); + Integer[] array = new Integer[1000]; + for (int i = 0; i < array.length; i++) { + array[i] = i; // Fill the array with integers 0 to 999 + } + Integer key = 1001; // Key not present + assertEquals(-1, linearSearch.find(array, key), "The element should not be found in the array."); + } + + /** + * Test for finding multiple occurrences of the same element in the array. + */ + @Test + void testLinearSearchMultipleOccurrences() { + LinearSearch linearSearch = new LinearSearch(); + Integer[] array = {1, 2, 3, 4, 5, 3, 6, 7, 3}; // 3 occurs multiple times + Integer key = 3; // Key to find + assertEquals(2, linearSearch.find(array, key), "The index of the first occurrence of the element should be 2."); + } + + /** + * Test for performance with random large array. + */ + @Test + void testLinearSearchRandomArray() { + LinearSearch linearSearch = new LinearSearch(); + Random random = new Random(); + Integer[] array = random.ints(0, 1000).distinct().limit(1000).boxed().toArray(Integer[] ::new); + Integer key = array[random.nextInt(array.length)]; // Key should be in the array + assertEquals(java.util.Arrays.asList(array).indexOf(key), linearSearch.find(array, key), "The index of the found element should match."); + } +} diff --git a/src/test/java/com/thealgorithms/searches/LinearSearchThreadTest.java b/src/test/java/com/thealgorithms/searches/LinearSearchThreadTest.java new file mode 100644 index 000000000000..534c2a4487b2 --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/LinearSearchThreadTest.java @@ -0,0 +1,74 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class LinearSearchThreadTest { + + /** + * Test for finding an element that is present in the array. + */ + @Test + void testSearcherFound() throws InterruptedException { + int[] array = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + Searcher searcher = new Searcher(array, 0, array.length, 5); + searcher.start(); + searcher.join(); + assertTrue(searcher.getResult(), "The element 5 should be found in the array."); + } + + /** + * Test for finding an element that is not present in the array. + */ + @Test + void testSearcherNotFound() throws InterruptedException { + int[] array = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + Searcher searcher = new Searcher(array, 0, array.length, 11); + searcher.start(); + searcher.join(); + assertFalse(searcher.getResult(), "The element 11 should not be found in the array."); + } + + /** + * Test for searching a segment of the array. + */ + @Test + void testSearcherSegmentFound() throws InterruptedException { + int[] array = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + Searcher searcher = new Searcher(array, 0, 5, 3); + searcher.start(); + searcher.join(); + assertTrue(searcher.getResult(), "The element 3 should be found in the segment."); + } + + /** + * Test for searching an empty array segment. + */ + @Test + void testSearcherEmptySegment() throws InterruptedException { + int[] array = {}; + Searcher searcher = new Searcher(array, 0, 0, 1); // Empty array + searcher.start(); + searcher.join(); + assertFalse(searcher.getResult(), "The element should not be found in an empty segment."); + } + + /** + * Test for searching with random numbers. + */ + @Test + void testSearcherRandomNumbers() throws InterruptedException { + int size = 200; + int[] array = new int[size]; + for (int i = 0; i < size; i++) { + array[i] = (int) (Math.random() * 100); + } + int target = array[(int) (Math.random() * size)]; // Randomly select a target that is present + Searcher searcher = new Searcher(array, 0, size, target); + searcher.start(); + searcher.join(); + assertTrue(searcher.getResult(), "The randomly selected target should be found in the array."); + } +} diff --git a/src/test/java/com/thealgorithms/searches/LowerBoundTest.java b/src/test/java/com/thealgorithms/searches/LowerBoundTest.java new file mode 100644 index 000000000000..30f4a5cb257c --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/LowerBoundTest.java @@ -0,0 +1,59 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class LowerBoundTest { + + /** + * Test finding the lower bound for an element present in the array. + */ + @Test + void testLowerBoundElementPresent() { + Integer[] array = {1, 2, 3, 4, 5}; + LowerBound lowerBound = new LowerBound(); + + // Test for a value that is present + assertEquals(2, lowerBound.find(array, 3), "Lower bound for 3 should be at index 2"); + assertEquals(0, lowerBound.find(array, 1), "Lower bound for 1 should be at index 0"); + assertEquals(4, lowerBound.find(array, 5), "Lower bound for 5 should be at index 4"); + } + + /** + * Test finding the lower bound for a value greater than the maximum element in the array. + */ + @Test + void testLowerBoundElementGreaterThanMax() { + Integer[] array = {1, 2, 3, 4, 5}; + LowerBound lowerBound = new LowerBound(); + + // Test for a value greater than the maximum + assertEquals(4, lowerBound.find(array, 6), "Lower bound for 6 should be at index 4"); + } + + /** + * Test finding the lower bound for a value less than the minimum element in the array. + */ + @Test + void testLowerBoundElementLessThanMin() { + Integer[] array = {1, 2, 3, 4, 5}; + LowerBound lowerBound = new LowerBound(); + + // Test for a value less than the minimum + assertEquals(0, lowerBound.find(array, 0), "Lower bound for 0 should be at index 0"); + } + + /** + * Test finding the lower bound for a non-existent value that falls between two elements. + */ + @Test + void testLowerBoundNonExistentValue() { + Integer[] array = {1, 2, 3, 4, 5}; + LowerBound lowerBound = new LowerBound(); + + // Test for a value that is not present + assertEquals(4, lowerBound.find(array, 7), "Lower bound for 7 should be at index 4"); + assertEquals(0, lowerBound.find(array, 0), "Lower bound for 0 should be at index 0"); + } +} diff --git a/src/test/java/com/thealgorithms/searches/MonteCarloTreeSearchTest.java b/src/test/java/com/thealgorithms/searches/MonteCarloTreeSearchTest.java new file mode 100644 index 000000000000..69c958f67a40 --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/MonteCarloTreeSearchTest.java @@ -0,0 +1,126 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class MonteCarloTreeSearchTest { + + /** + * Test the creation of a node and its initial state. + */ + @Test + void testNodeCreation() { + MonteCarloTreeSearch.Node node = new MonteCarloTreeSearch().new Node(null, true); + assertNotNull(node, "Node should be created"); + assertTrue(node.childNodes.isEmpty(), "Child nodes should be empty upon creation"); + assertTrue(node.isPlayersTurn, "Initial turn should be player's turn"); + assertEquals(0, node.score, "Initial score should be zero"); + assertEquals(0, node.visitCount, "Initial visit count should be zero"); + } + + /** + * Test adding child nodes to a parent node. + */ + @Test + void testAddChildNodes() { + MonteCarloTreeSearch mcts = new MonteCarloTreeSearch(); + MonteCarloTreeSearch.Node parentNode = mcts.new Node(null, true); + + mcts.addChildNodes(parentNode, 5); + + assertEquals(5, parentNode.childNodes.size(), "Parent should have 5 child nodes"); + for (MonteCarloTreeSearch.Node child : parentNode.childNodes) { + assertFalse(child.isPlayersTurn, "Child node should not be player's turn"); + assertEquals(0, child.visitCount, "Child node visit count should be zero"); + } + } + + /** + * Test the UCT selection of a promising node. + */ + @Test + void testGetPromisingNode() { + MonteCarloTreeSearch mcts = new MonteCarloTreeSearch(); + MonteCarloTreeSearch.Node parentNode = mcts.new Node(null, true); + + // Create child nodes with different visit counts and scores + for (int i = 0; i < 3; i++) { + MonteCarloTreeSearch.Node child = mcts.new Node(parentNode, false); + child.visitCount = i + 1; + child.score = i * 2; + parentNode.childNodes.add(child); + } + + // Get promising node + MonteCarloTreeSearch.Node promisingNode = mcts.getPromisingNode(parentNode); + + // The child with the highest UCT value should be chosen. + assertNotNull(promisingNode, "Promising node should not be null"); + assertEquals(0, parentNode.childNodes.indexOf(promisingNode), "The first child should be the most promising"); + } + + /** + * Test simulation of random play and backpropagation. + */ + @Test + void testSimulateRandomPlay() { + MonteCarloTreeSearch mcts = new MonteCarloTreeSearch(); + MonteCarloTreeSearch.Node node = mcts.new Node(null, true); + node.visitCount = 10; // Simulating existing visits + + // Simulate random play + mcts.simulateRandomPlay(node); + + // Check visit count after simulation + assertEquals(11, node.visitCount, "Visit count should increase after simulation"); + + // Check if score is updated correctly + assertTrue(node.score >= 0 && node.score <= MonteCarloTreeSearch.WIN_SCORE, "Score should be between 0 and WIN_SCORE"); + } + + /** + * Test retrieving the winning node based on scores. + */ + @Test + void testGetWinnerNode() { + MonteCarloTreeSearch mcts = new MonteCarloTreeSearch(); + MonteCarloTreeSearch.Node parentNode = mcts.new Node(null, true); + + // Create child nodes with varying scores + MonteCarloTreeSearch.Node winningNode = mcts.new Node(parentNode, false); + winningNode.score = 10; // Highest score + parentNode.childNodes.add(winningNode); + + MonteCarloTreeSearch.Node losingNode = mcts.new Node(parentNode, false); + losingNode.score = 5; + parentNode.childNodes.add(losingNode); + + MonteCarloTreeSearch.Node anotherLosingNode = mcts.new Node(parentNode, false); + anotherLosingNode.score = 3; + parentNode.childNodes.add(anotherLosingNode); + + // Get the winning node + MonteCarloTreeSearch.Node winnerNode = mcts.getWinnerNode(parentNode); + + assertEquals(winningNode, winnerNode, "Winning node should have the highest score"); + } + + /** + * Test the full Monte Carlo Tree Search process. + */ + @Test + void testMonteCarloTreeSearch() { + MonteCarloTreeSearch mcts = new MonteCarloTreeSearch(); + MonteCarloTreeSearch.Node rootNode = mcts.new Node(null, true); + + // Execute MCTS and check the resulting node + MonteCarloTreeSearch.Node optimalNode = mcts.monteCarloTreeSearch(rootNode); + + assertNotNull(optimalNode, "MCTS should return a non-null optimal node"); + assertTrue(rootNode.childNodes.contains(optimalNode), "Optimal node should be a child of the root"); + } +} diff --git a/src/test/java/com/thealgorithms/searches/OrderAgnosticBinarySearchTest.java b/src/test/java/com/thealgorithms/searches/OrderAgnosticBinarySearchTest.java new file mode 100644 index 000000000000..03502eec00d1 --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/OrderAgnosticBinarySearchTest.java @@ -0,0 +1,67 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class OrderAgnosticBinarySearchTest { + @Test + // valid Test Case + public void elementInMiddle() { + int[] arr = {10, 20, 30, 40, 50}; + int answer = OrderAgnosticBinarySearch.binSearchAlgo(arr, 0, arr.length - 1, 30); + System.out.println(answer); + int expected = 2; + assertEquals(expected, answer); + } + + @Test + // valid Test Case + public void rightHalfDescOrder() { + int[] arr = {50, 40, 30, 20, 10}; + int answer = OrderAgnosticBinarySearch.binSearchAlgo(arr, 0, arr.length - 1, 10); + System.out.println(answer); + int expected = 4; + assertEquals(expected, answer); + } + + @Test + // valid test case + public void leftHalfDescOrder() { + int[] arr = {50, 40, 30, 20, 10}; + int answer = OrderAgnosticBinarySearch.binSearchAlgo(arr, 0, arr.length - 1, 50); + System.out.println(answer); + int expected = 0; + assertEquals(expected, answer); + } + + @Test + // valid test case + public void rightHalfAscOrder() { + int[] arr = {10, 20, 30, 40, 50}; + int answer = OrderAgnosticBinarySearch.binSearchAlgo(arr, 0, arr.length - 1, 50); + System.out.println(answer); + int expected = 4; + assertEquals(expected, answer); + } + + @Test + // valid test case + public void leftHalfAscOrder() { + int[] arr = {10, 20, 30, 40, 50}; + int answer = OrderAgnosticBinarySearch.binSearchAlgo(arr, 0, arr.length - 1, 10); + System.out.println(answer); + int expected = 0; + assertEquals(expected, answer); + } + + @Test + // valid test case + public void elementNotFound() { + int[] arr = {10, 20, 30, 40, 50}; + int answer = OrderAgnosticBinarySearch.binSearchAlgo(arr, 0, arr.length - 1, 100); + System.out.println(answer); + int expected = -1; + assertEquals(expected, answer); + } +} diff --git a/src/test/java/com/thealgorithms/searches/PerfectBinarySearchTest.java b/src/test/java/com/thealgorithms/searches/PerfectBinarySearchTest.java new file mode 100644 index 000000000000..6eab20f45467 --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/PerfectBinarySearchTest.java @@ -0,0 +1,44 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +/** + * @author D Sunil (https://github.com/sunilnitdgp) + * @see PerfectBinarySearch + */ +public class PerfectBinarySearchTest { + + @Test + public void testIntegerBinarySearch() { + Integer[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + PerfectBinarySearch<Integer> binarySearch = new PerfectBinarySearch<>(); + + // Test cases for elements present in the array + assertEquals(0, binarySearch.find(array, 1)); // First element + assertEquals(4, binarySearch.find(array, 5)); // Middle element + assertEquals(9, binarySearch.find(array, 10)); // Last element + assertEquals(6, binarySearch.find(array, 7)); // Element in the middle + + // Test cases for elements not in the array + assertEquals(-1, binarySearch.find(array, 0)); // Element before the array + assertEquals(-1, binarySearch.find(array, 11)); // Element after the array + assertEquals(-1, binarySearch.find(array, 100)); // Element not in the array + } + + @Test + public void testStringBinarySearch() { + String[] array = {"apple", "banana", "cherry", "date", "fig"}; + PerfectBinarySearch<String> binarySearch = new PerfectBinarySearch<>(); + + // Test cases for elements not in the array + assertEquals(-1, binarySearch.find(array, "apricot")); // Element not in the array + assertEquals(-1, binarySearch.find(array, "bananaa")); // Element not in the array + + // Test cases for elements present in the array + assertEquals(0, binarySearch.find(array, "apple")); // First element + assertEquals(2, binarySearch.find(array, "cherry")); // Middle element + assertEquals(4, binarySearch.find(array, "fig")); // Last element + } +} diff --git a/src/test/java/com/thealgorithms/searches/QuickSelectTest.java b/src/test/java/com/thealgorithms/searches/QuickSelectTest.java new file mode 100644 index 000000000000..cf160b0ff4b5 --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/QuickSelectTest.java @@ -0,0 +1,232 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; +import org.junit.jupiter.api.Test; + +class QuickSelectTest { + + @Test + void quickSelectMinimumOfOneElement() { + List<Integer> elements = Collections.singletonList(42); + int minimum = QuickSelect.select(elements, 0); + assertEquals(42, minimum); + } + + @Test + void quickSelectMinimumOfTwoElements() { + List<Integer> elements1 = Arrays.asList(42, 90); + List<Integer> elements2 = Arrays.asList(90, 42); + + int minimum1 = QuickSelect.select(elements1, 0); + int minimum2 = QuickSelect.select(elements2, 0); + + assertEquals(42, minimum1); + assertEquals(42, minimum2); + } + + @Test + void quickSelectMinimumOfThreeElements() { + List<Integer> elements1 = Arrays.asList(1, 2, 3); + List<Integer> elements2 = Arrays.asList(2, 1, 3); + List<Integer> elements3 = Arrays.asList(2, 3, 1); + + int minimum1 = QuickSelect.select(elements1, 0); + int minimum2 = QuickSelect.select(elements2, 0); + int minimum3 = QuickSelect.select(elements3, 0); + + assertEquals(1, minimum1); + assertEquals(1, minimum2); + assertEquals(1, minimum3); + } + + @Test + void quickSelectMinimumOfManyElements() { + List<Integer> elements = generateRandomIntegers(NUM_RND_ELEMENTS); + int actual = QuickSelect.select(elements, 0); + int expected = elements.stream().min(Comparator.naturalOrder()).get(); + assertEquals(expected, actual); + } + + @Test + void quickSelectMaximumOfOneElement() { + List<Integer> elements = Collections.singletonList(42); + int maximum = QuickSelect.select(elements, 0); + assertEquals(42, maximum); + } + + @Test + void quickSelectMaximumOfTwoElements() { + List<Integer> elements1 = Arrays.asList(42, 90); + List<Integer> elements2 = Arrays.asList(90, 42); + + int maximum1 = QuickSelect.select(elements1, 1); + int maximum2 = QuickSelect.select(elements2, 1); + + assertEquals(90, maximum1); + assertEquals(90, maximum2); + } + + @Test + void quickSelectMaximumOfThreeElements() { + List<Integer> elements1 = Arrays.asList(1, 2, 3); + List<Integer> elements2 = Arrays.asList(2, 1, 3); + List<Integer> elements3 = Arrays.asList(2, 3, 1); + + int maximum1 = QuickSelect.select(elements1, 2); + int maximum2 = QuickSelect.select(elements2, 2); + int maximum3 = QuickSelect.select(elements3, 2); + + assertEquals(3, maximum1); + assertEquals(3, maximum2); + assertEquals(3, maximum3); + } + + @Test + void quickSelectMaximumOfManyElements() { + List<Integer> elements = generateRandomIntegers(NUM_RND_ELEMENTS); + int actual = QuickSelect.select(elements, NUM_RND_ELEMENTS - 1); + int expected = elements.stream().max(Comparator.naturalOrder()).get(); + assertEquals(expected, actual); + } + + @Test + void quickSelectMedianOfOneElement() { + List<Integer> elements = Collections.singletonList(42); + int median = QuickSelect.select(elements, 0); + assertEquals(42, median); + } + + @Test + void quickSelectMedianOfThreeElements() { + List<Integer> elements1 = Arrays.asList(1, 2, 3); + List<Integer> elements2 = Arrays.asList(2, 1, 3); + List<Integer> elements3 = Arrays.asList(2, 3, 1); + + int median1 = QuickSelect.select(elements1, 1); + int median2 = QuickSelect.select(elements2, 1); + int median3 = QuickSelect.select(elements3, 1); + + assertEquals(2, median1); + assertEquals(2, median2); + assertEquals(2, median3); + } + + @Test + void quickSelectMedianOfManyElements() { + int medianIndex = NUM_RND_ELEMENTS / 2; + List<Integer> elements = generateRandomIntegers(NUM_RND_ELEMENTS); + int actual = QuickSelect.select(elements, medianIndex); + + List<Integer> elementsSorted = getSortedCopyOfList(elements); + assertEquals(elementsSorted.get(medianIndex), actual); + } + + @Test + void quickSelect30thPercentileOf10Elements() { + List<Integer> elements = generateRandomIntegers(10); + int actual = QuickSelect.select(elements, 2); + + List<Integer> elementsSorted = getSortedCopyOfList(elements); + assertEquals(elementsSorted.get(2), actual); + } + + @Test + void quickSelect30thPercentileOfManyElements() { + int percentile30th = NUM_RND_ELEMENTS / 10 * 3; + List<Integer> elements = generateRandomIntegers(NUM_RND_ELEMENTS); + int actual = QuickSelect.select(elements, percentile30th); + + List<Integer> elementsSorted = getSortedCopyOfList(elements); + assertEquals(elementsSorted.get(percentile30th), actual); + } + + @Test + void quickSelect70thPercentileOf10Elements() { + List<Integer> elements = generateRandomIntegers(10); + int actual = QuickSelect.select(elements, 6); + + List<Integer> elementsSorted = getSortedCopyOfList(elements); + assertEquals(elementsSorted.get(6), actual); + } + + @Test + void quickSelect70thPercentileOfManyElements() { + int percentile70th = NUM_RND_ELEMENTS / 10 * 7; + List<Integer> elements = generateRandomIntegers(NUM_RND_ELEMENTS); + int actual = QuickSelect.select(elements, percentile70th); + + List<Integer> elementsSorted = getSortedCopyOfList(elements); + assertEquals(elementsSorted.get(percentile70th), actual); + } + + @Test + void quickSelectMedianOfThreeCharacters() { + List<Character> elements = Arrays.asList('X', 'Z', 'Y'); + char actual = QuickSelect.select(elements, 1); + assertEquals(actual, 'Y'); + } + + @Test + void quickSelectMedianOfManyCharacters() { + List<Character> elements = generateRandomCharacters(NUM_RND_ELEMENTS); + char actual = QuickSelect.select(elements, NUM_RND_ELEMENTS / 30); + + List<Character> elementsSorted = getSortedCopyOfList(elements); + assertEquals(elementsSorted.get(NUM_RND_ELEMENTS / 30), actual); + } + + @Test + void quickSelectNullList() { + NullPointerException exception = assertThrows(NullPointerException.class, () -> QuickSelect.select(null, 0)); + String expectedMsg = "The list of elements must not be null."; + assertEquals(expectedMsg, exception.getMessage()); + } + + @Test + void quickSelectEmptyList() { + List<String> objects = Collections.emptyList(); + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> QuickSelect.select(objects, 0)); + String expectedMsg = "The list of elements must not be empty."; + assertEquals(expectedMsg, exception.getMessage()); + } + + @Test + void quickSelectIndexOutOfLeftBound() { + IndexOutOfBoundsException exception = assertThrows(IndexOutOfBoundsException.class, () -> QuickSelect.select(Collections.singletonList(1), -1)); + String expectedMsg = "The index must not be negative."; + assertEquals(expectedMsg, exception.getMessage()); + } + + @Test + void quickSelectIndexOutOfRightBound() { + IndexOutOfBoundsException exception = assertThrows(IndexOutOfBoundsException.class, () -> QuickSelect.select(Collections.singletonList(1), 1)); + String expectedMsg = "The index must be less than the number of elements."; + assertEquals(expectedMsg, exception.getMessage()); + } + + private static final int NUM_RND_ELEMENTS = 99; + private static final Random RANDOM = new Random(42); + private static final int ASCII_A = 0x41; + private static final int ASCII_Z = 0x5A; + + private static List<Integer> generateRandomIntegers(int n) { + return RANDOM.ints(n).boxed().collect(Collectors.toList()); + } + + private static List<Character> generateRandomCharacters(int n) { + return RANDOM.ints(n, ASCII_A, ASCII_Z).mapToObj(i -> (char) i).collect(Collectors.toList()); + } + + private static <T extends Comparable<T>> List<T> getSortedCopyOfList(Collection<T> list) { + return list.stream().sorted().collect(Collectors.toList()); + } +} diff --git a/src/test/java/com/thealgorithms/searches/RabinKarpAlgorithmTest.java b/src/test/java/com/thealgorithms/searches/RabinKarpAlgorithmTest.java new file mode 100644 index 000000000000..40e1acce9c3a --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/RabinKarpAlgorithmTest.java @@ -0,0 +1,17 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class RabinKarpAlgorithmTest { + + @ParameterizedTest + @CsvSource({"This is an example for rabin karp algorithmn, algorithmn, 101", "AAABBDDG, AAA, 137", "AAABBCCBB, BBCC, 101", "AAABBCCBB, BBCC, 131", "AAAABBBBCCC, CCC, 41", "ABCBCBCAAB, AADB, 293", "Algorithm The Algorithm, Algorithm, 101"}) + void rabinKarpAlgorithmTestExample(String txt, String pat, int q) { + int indexFromOurAlgorithm = RabinKarpAlgorithm.search(pat, txt, q); + int indexFromLinearSearch = txt.indexOf(pat); + assertEquals(indexFromOurAlgorithm, indexFromLinearSearch); + } +} diff --git a/src/test/java/com/thealgorithms/searches/RandomSearchTest.java b/src/test/java/com/thealgorithms/searches/RandomSearchTest.java new file mode 100644 index 000000000000..0a1dbeafd888 --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/RandomSearchTest.java @@ -0,0 +1,87 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class RandomSearchTest { + + private RandomSearch randomSearch; + + @BeforeEach + void setUp() { + randomSearch = new RandomSearch(); + } + + @Test + void testElementFound() { + Integer[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + Integer key = 5; + int index = randomSearch.find(array, key); + + assertNotEquals(-1, index, "Element should be found in the array."); + assertEquals(key, array[index], "Element found should match the key."); + } + + @Test + void testElementNotFound() { + Integer[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + Integer key = 11; + int index = randomSearch.find(array, key); + + assertEquals(-1, index, "Element not present in the array should return -1."); + } + + @Test + void testEmptyArray() { + Integer[] emptyArray = {}; + Integer key = 5; + int index = randomSearch.find(emptyArray, key); + + assertEquals(-1, index, "Searching in an empty array should return -1."); + } + + @Test + void testSingleElementArrayFound() { + Integer[] array = {5}; + Integer key = 5; + int index = randomSearch.find(array, key); + + assertEquals(0, index, "The key should be found at index 0 in a single-element array."); + } + + @Test + void testSingleElementArrayNotFound() { + Integer[] array = {1}; + Integer key = 5; + int index = randomSearch.find(array, key); + + assertEquals(-1, index, "The key should not be found in a single-element array if it does not match."); + } + + @Test + void testDuplicateElementsFound() { + Integer[] array = {1, 2, 3, 4, 5, 5, 5, 7, 8, 9, 10}; + Integer key = 5; + int index = randomSearch.find(array, key); + + assertNotEquals(-1, index, "The key should be found in the array with duplicates."); + assertEquals(key, array[index], "The key found should be 5."); + } + + @Test + void testLargeArray() { + Integer[] largeArray = new Integer[1000]; + for (int i = 0; i < largeArray.length; i++) { + largeArray[i] = i + 1; // Fill with values 1 to 1000 + } + + Integer key = 500; + int index = randomSearch.find(largeArray, key); + + assertNotEquals(-1, index, "The key should be found in the large array."); + assertEquals(key, largeArray[index], "The key found should match 500."); + } +} diff --git a/src/test/java/com/thealgorithms/searches/RecursiveBinarySearchTest.java b/src/test/java/com/thealgorithms/searches/RecursiveBinarySearchTest.java new file mode 100644 index 000000000000..0a2794f9e8d7 --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/RecursiveBinarySearchTest.java @@ -0,0 +1,41 @@ +// Created by Pronay Debnath +// Date:- 1/10/2023 +// Test file updated with JUnit tests +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; // Import the JUnit 5 Test annotation + +public class RecursiveBinarySearchTest { + + @Test + public void testBinarySearch() { + // Create an instance of GenericBinarySearch + RecursiveBinarySearch<Integer> searcher = new RecursiveBinarySearch<>(); + + // Test case 1: Element found in the array + Integer[] arr1 = {1, 2, 3, 4, 5}; + int target1 = 3; + int result1 = searcher.binsear(arr1, 0, arr1.length - 1, target1); + assertEquals(2, result1); + + // Test case 2: Element not found in the array + Integer[] arr2 = {1, 2, 3, 4, 5}; + int target2 = 6; + int result2 = searcher.binsear(arr2, 0, arr2.length - 1, target2); + assertEquals(-1, result2); + + // Test case 3: Element found at the beginning of the array + Integer[] arr3 = {10, 20, 30, 40, 50}; + int target3 = 10; + int result3 = searcher.binsear(arr3, 0, arr3.length - 1, target3); + assertEquals(0, result3); + + // Test case 4: Element found at the end of the array + Integer[] arr4 = {10, 20, 30, 40, 50}; + int target4 = 50; + int result4 = searcher.binsear(arr4, 0, arr4.length - 1, target4); + assertEquals(4, result4); + } +} diff --git a/src/test/java/com/thealgorithms/searches/RowColumnWiseSorted2dArrayBinarySearchTest.java b/src/test/java/com/thealgorithms/searches/RowColumnWiseSorted2dArrayBinarySearchTest.java new file mode 100644 index 000000000000..39ac5bf037ea --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/RowColumnWiseSorted2dArrayBinarySearchTest.java @@ -0,0 +1,113 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class RowColumnWiseSorted2dArrayBinarySearchTest { + + @Test + public void rowColumnSorted2dArrayBinarySearchTestMiddle() { + Integer[][] arr = { + {10, 20, 30, 40}, + {15, 25, 35, 45}, + {18, 28, 38, 48}, + {21, 31, 41, 51}, + }; + Integer target = 35; + int[] ans = RowColumnWiseSorted2dArrayBinarySearch.search(arr, target); + int[] expected = {1, 2}; + assertEquals(expected[0], ans[0]); + assertEquals(expected[1], ans[1]); + } + + @Test + public void rowColumnSorted2dArrayBinarySearchTestSide() { + Integer[][] arr = { + {10, 20, 30, 40}, + {15, 25, 35, 45}, + {18, 28, 38, 48}, + {21, 31, 41, 51}, + }; + Integer target = 48; + int[] ans = RowColumnWiseSorted2dArrayBinarySearch.search(arr, target); + int[] expected = {2, 3}; + assertEquals(expected[0], ans[0]); + assertEquals(expected[1], ans[1]); + } + + @Test + public void rowColumnSorted2dArrayBinarySearchTestUpper() { + Integer[][] arr = { + {10, 20, 30, 40}, + {15, 25, 35, 45}, + {18, 28, 38, 48}, + {21, 31, 41, 51}, + }; + Integer target = 20; + int[] ans = RowColumnWiseSorted2dArrayBinarySearch.search(arr, target); + int[] expected = {0, 1}; + assertEquals(expected[0], ans[0]); + assertEquals(expected[1], ans[1]); + } + + @Test + public void rowColumnSorted2dArrayBinarySearchTestUpperSide() { + Integer[][] arr = { + {10, 20, 30, 40}, + {15, 25, 35, 45}, + {18, 28, 38, 48}, + {21, 31, 41, 51}, + }; + Integer target = 40; + int[] ans = RowColumnWiseSorted2dArrayBinarySearch.search(arr, target); + int[] expected = {0, 3}; + assertEquals(expected[0], ans[0]); + assertEquals(expected[1], ans[1]); + } + + @Test + public void rowColumnSorted2dArrayBinarySearchTestLower() { + Integer[][] arr = { + {10, 20, 30, 40}, + {15, 25, 35, 45}, + {18, 28, 38, 48}, + {21, 31, 41, 51}, + }; + Integer target = 31; + int[] ans = RowColumnWiseSorted2dArrayBinarySearch.search(arr, target); + int[] expected = {3, 1}; + assertEquals(expected[0], ans[0]); + assertEquals(expected[1], ans[1]); + } + + @Test + public void rowColumnSorted2dArrayBinarySearchTestLowerSide() { + Integer[][] arr = { + {10, 20, 30, 40}, + {15, 25, 35, 45}, + {18, 28, 38, 48}, + {21, 31, 41, 51}, + }; + Integer target = 51; + int[] ans = RowColumnWiseSorted2dArrayBinarySearch.search(arr, target); + int[] expected = {3, 3}; + assertEquals(expected[0], ans[0]); + assertEquals(expected[1], ans[1]); + } + + @Test + public void rowColumnSorted2dArrayBinarySearchTestNotFound() { + Integer[][] arr = { + {10, 20, 30, 40}, + {15, 25, 35, 45}, + {18, 28, 38, 48}, + {21, 31, 41, 51}, + }; + Integer target = 101; + int[] ans = RowColumnWiseSorted2dArrayBinarySearch.search(arr, target); + int[] expected = {-1, -1}; + assertEquals(expected[0], ans[0]); + assertEquals(expected[1], ans[1]); + } +} diff --git a/src/test/java/com/thealgorithms/searches/SaddlebackSearchTest.java b/src/test/java/com/thealgorithms/searches/SaddlebackSearchTest.java new file mode 100644 index 000000000000..ec22cbf38152 --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/SaddlebackSearchTest.java @@ -0,0 +1,85 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class SaddlebackSearchTest { + + /** + * Test searching for an element that exists in the array. + */ + @Test + void testFindElementExists() { + int[][] arr = {{-10, -5, -3, 4, 9}, {-6, -2, 0, 5, 10}, {-4, -1, 1, 6, 12}, {2, 3, 7, 8, 13}, {100, 120, 130, 140, 150}}; + + int[] result = SaddlebackSearch.find(arr, arr.length - 1, 0, 4); + assertArrayEquals(new int[] {0, 3}, result, "Element 4 should be found at (0, 3)"); + } + + /** + * Test searching for an element that does not exist in the array. + */ + @Test + void testFindElementNotExists() { + int[][] arr = {{-10, -5, -3, 4, 9}, {-6, -2, 0, 5, 10}, {-4, -1, 1, 6, 12}, {2, 3, 7, 8, 13}, {100, 120, 130, 140, 150}}; + + int[] result = SaddlebackSearch.find(arr, arr.length - 1, 0, 1000); + assertArrayEquals(new int[] {-1, -1}, result, "Element 1000 should not be found"); + } + + /** + * Test searching for the smallest element in the array. + */ + @Test + void testFindSmallestElement() { + int[][] arr = {{-10, -5, -3, 4, 9}, {-6, -2, 0, 5, 10}, {-4, -1, 1, 6, 12}, {2, 3, 7, 8, 13}, {100, 120, 130, 140, 150}}; + + int[] result = SaddlebackSearch.find(arr, arr.length - 1, 0, -10); + assertArrayEquals(new int[] {0, 0}, result, "Element -10 should be found at (0, 0)"); + } + + /** + * Test searching for the largest element in the array. + */ + @Test + void testFindLargestElement() { + int[][] arr = {{-10, -5, -3, 4, 9}, {-6, -2, 0, 5, 10}, {-4, -1, 1, 6, 12}, {2, 3, 7, 8, 13}, {100, 120, 130, 140, 150}}; + + int[] result = SaddlebackSearch.find(arr, arr.length - 1, 0, 150); + assertArrayEquals(new int[] {4, 4}, result, "Element 150 should be found at (4, 4)"); + } + + /** + * Test searching in an empty array. + */ + @Test + void testFindInEmptyArray() { + int[][] arr = {}; + + assertThrows(IllegalArgumentException.class, () -> { SaddlebackSearch.find(arr, 0, 0, 4); }); + } + + /** + * Test searching in a single element array that matches the search key. + */ + @Test + void testFindSingleElementExists() { + int[][] arr = {{5}}; + + int[] result = SaddlebackSearch.find(arr, 0, 0, 5); + assertArrayEquals(new int[] {0, 0}, result, "Element 5 should be found at (0, 0)"); + } + + /** + * Test searching in a single element array that does not match the search key. + */ + @Test + void testFindSingleElementNotExists() { + int[][] arr = {{5}}; + + int[] result = SaddlebackSearch.find(arr, 0, 0, 10); + assertArrayEquals(new int[] {-1, -1}, result, "Element 10 should not be found in single element array"); + } +} diff --git a/src/test/java/com/thealgorithms/searches/SearchInARowAndColWiseSortedMatrixTest.java b/src/test/java/com/thealgorithms/searches/SearchInARowAndColWiseSortedMatrixTest.java new file mode 100644 index 000000000000..88227804b775 --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/SearchInARowAndColWiseSortedMatrixTest.java @@ -0,0 +1,66 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +class SearchInARowAndColWiseSortedMatrixTest { + + private final SearchInARowAndColWiseSortedMatrix searcher = new SearchInARowAndColWiseSortedMatrix(); + + @Test + void testSearchValueExistsInMatrix() { + int[][] matrix = {{10, 20, 30, 40}, {15, 25, 35, 45}, {27, 29, 37, 48}, {32, 33, 39, 50}}; + int value = 29; + int[] expected = {2, 1}; // Row 2, Column 1 + assertArrayEquals(expected, searcher.search(matrix, value), "Value should be found in the matrix"); + } + + @Test + void testSearchValueNotExistsInMatrix() { + int[][] matrix = {{10, 20, 30, 40}, {15, 25, 35, 45}, {27, 29, 37, 48}, {32, 33, 39, 50}}; + int value = 100; + int[] expected = {-1, -1}; // Not found + assertArrayEquals(expected, searcher.search(matrix, value), "Value should not be found in the matrix"); + } + + @Test + void testSearchInEmptyMatrix() { + int[][] matrix = {}; + int value = 5; + int[] expected = {-1, -1}; // Not found + assertArrayEquals(expected, searcher.search(matrix, value), "Should return {-1, -1} for empty matrix"); + } + + @Test + void testSearchInSingleElementMatrixFound() { + int[][] matrix = {{5}}; + int value = 5; + int[] expected = {0, 0}; // Found at (0,0) + assertArrayEquals(expected, searcher.search(matrix, value), "Value should be found in single element matrix"); + } + + @Test + void testSearchInSingleElementMatrixNotFound() { + int[][] matrix = {{10}}; + int value = 5; + int[] expected = {-1, -1}; // Not found + assertArrayEquals(expected, searcher.search(matrix, value), "Should return {-1, -1} for value not found in single element matrix"); + } + + @Test + void testSearchInRowWiseSortedMatrix() { + int[][] matrix = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; + int value = 6; + int[] expected = {1, 2}; // Found at (1, 2) + assertArrayEquals(expected, searcher.search(matrix, value), "Value should be found in the row-wise sorted matrix"); + } + + @Test + void testSearchInColWiseSortedMatrix() { + int[][] matrix = {{1, 4, 7}, {2, 5, 8}, {3, 6, 9}}; + int value = 5; + int[] expected = {1, 1}; // Found at (1, 1) + assertArrayEquals(expected, searcher.search(matrix, value), "Value should be found in the column-wise sorted matrix"); + } +} diff --git a/src/test/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearchTest.java b/src/test/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearchTest.java new file mode 100644 index 000000000000..e2917733d1d9 --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/SortOrderAgnosticBinarySearchTest.java @@ -0,0 +1,26 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class SortOrderAgnosticBinarySearchTest { + + @Test + public void testAscending() { + int[] arr = {1, 2, 3, 4, 5}; // for ascending order. + int target = 2; + int ans = SortOrderAgnosticBinarySearch.find(arr, target); + int excepted = 1; + assertEquals(excepted, ans); + } + + @Test + public void testDescending() { + int[] arr = {5, 4, 3, 2, 1}; // for descending order. + int target = 2; + int ans = SortOrderAgnosticBinarySearch.find(arr, target); + int excepted = 3; + assertEquals(excepted, ans); + } +} diff --git a/src/test/java/com/thealgorithms/searches/SquareRootBinarySearchTest.java b/src/test/java/com/thealgorithms/searches/SquareRootBinarySearchTest.java new file mode 100644 index 000000000000..f23f4ee83fc3 --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/SquareRootBinarySearchTest.java @@ -0,0 +1,57 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class SquareRootBinarySearchTest { + + @Test + void testPerfectSquare() { + long input = 16; + long expected = 4; + assertEquals(expected, SquareRootBinarySearch.squareRoot(input), "Square root of 16 should be 4"); + } + + @Test + void testNonPerfectSquare() { + long input = 15; + long expected = 3; + assertEquals(expected, SquareRootBinarySearch.squareRoot(input), "Square root of 15 should be 3"); + } + + @Test + void testZero() { + long input = 0; + long expected = 0; + assertEquals(expected, SquareRootBinarySearch.squareRoot(input), "Square root of 0 should be 0"); + } + + @Test + void testOne() { + long input = 1; + long expected = 1; + assertEquals(expected, SquareRootBinarySearch.squareRoot(input), "Square root of 1 should be 1"); + } + + @Test + void testLargeNumberPerfectSquare() { + long input = 1000000; + long expected = 1000; + assertEquals(expected, SquareRootBinarySearch.squareRoot(input), "Square root of 1000000 should be 1000"); + } + + @Test + void testLargeNumberNonPerfectSquare() { + long input = 999999; + long expected = 999; + assertEquals(expected, SquareRootBinarySearch.squareRoot(input), "Square root of 999999 should be 999"); + } + + @Test + void testNegativeInput() { + long input = -4; + long expected = 0; // Assuming the implementation should return 0 for negative input + assertEquals(expected, SquareRootBinarySearch.squareRoot(input), "Square root of negative number should return 0"); + } +} diff --git a/src/test/java/com/thealgorithms/searches/TernarySearchTest.java b/src/test/java/com/thealgorithms/searches/TernarySearchTest.java new file mode 100644 index 000000000000..367b595e55d9 --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/TernarySearchTest.java @@ -0,0 +1,81 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class TernarySearchTest { + + @Test + void testFindElementInSortedArray() { + Integer[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + TernarySearch search = new TernarySearch(); + + int index = search.find(arr, 5); + + assertEquals(4, index, "Should find the element 5 at index 4"); + } + + @Test + void testElementNotFound() { + Integer[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + TernarySearch search = new TernarySearch(); + + int index = search.find(arr, 11); + + assertEquals(-1, index, "Should return -1 for element 11 which is not present"); + } + + @Test + void testFindFirstElement() { + Integer[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + TernarySearch search = new TernarySearch(); + + int index = search.find(arr, 1); + + assertEquals(0, index, "Should find the first element 1 at index 0"); + } + + @Test + void testFindLastElement() { + Integer[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + TernarySearch search = new TernarySearch(); + + int index = search.find(arr, 10); + + assertEquals(9, index, "Should find the last element 10 at index 9"); + } + + @Test + void testFindInLargeArray() { + Integer[] arr = new Integer[1000]; + for (int i = 0; i < 1000; i++) { + arr[i] = i + 1; // Array from 1 to 1000 + } + TernarySearch search = new TernarySearch(); + + int index = search.find(arr, 500); + + assertEquals(499, index, "Should find element 500 at index 499"); + } + + @Test + void testNegativeNumbers() { + Integer[] arr = {-10, -5, -3, -1, 0, 1, 3, 5, 7, 10}; + TernarySearch search = new TernarySearch(); + + int index = search.find(arr, -3); + + assertEquals(2, index, "Should find the element -3 at index 2"); + } + + @Test + void testEdgeCaseEmptyArray() { + Integer[] arr = {}; + TernarySearch search = new TernarySearch(); + + int index = search.find(arr, 5); + + assertEquals(-1, index, "Should return -1 for an empty array"); + } +} diff --git a/src/test/java/com/thealgorithms/searches/TestSearchInARowAndColWiseSortedMatrix.java b/src/test/java/com/thealgorithms/searches/TestSearchInARowAndColWiseSortedMatrix.java new file mode 100644 index 000000000000..a56f79670cf3 --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/TestSearchInARowAndColWiseSortedMatrix.java @@ -0,0 +1,25 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +public class TestSearchInARowAndColWiseSortedMatrix { + @Test + public void searchItem() { + int[][] matrix = {{3, 4, 5, 6, 7}, {8, 9, 10, 11, 12}, {14, 15, 16, 17, 18}, {23, 24, 25, 26, 27}, {30, 31, 32, 33, 34}}; + var test = new SearchInARowAndColWiseSortedMatrix(); + int[] res = test.search(matrix, 16); + int[] expectedResult = {2, 2}; + assertArrayEquals(expectedResult, res); + } + + @Test + public void notFound() { + int[][] matrix = {{3, 4, 5, 6, 7}, {8, 9, 10, 11, 12}, {14, 15, 16, 17, 18}, {23, 24, 25, 26, 27}, {30, 31, 32, 33, 34}}; + var test = new SearchInARowAndColWiseSortedMatrix(); + int[] res = test.search(matrix, 96); + int[] expectedResult = {-1, -1}; + assertArrayEquals(expectedResult, res); + } +} diff --git a/src/test/java/com/thealgorithms/searches/UnionFindTest.java b/src/test/java/com/thealgorithms/searches/UnionFindTest.java new file mode 100644 index 000000000000..3cc025ff595c --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/UnionFindTest.java @@ -0,0 +1,81 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class UnionFindTest { + private UnionFind uf; + + @BeforeEach + void setUp() { + uf = new UnionFind(10); // Initialize with 10 elements + } + + @Test + void testInitialState() { + // Verify that each element is its own parent and rank is 0 + assertEquals("p [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] r [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n", uf.toString()); + assertEquals(10, uf.count(), "Initial count of disjoint sets should be 10."); + } + + @Test + void testUnionOperation() { + uf.union(0, 1); + uf.union(1, 2); + assertEquals(8, uf.count(), "Count should decrease after unions."); + assertEquals(0, uf.find(2), "Element 2 should point to root 0 after unions."); + } + + @Test + void testUnionWithRank() { + uf.union(0, 1); + uf.union(1, 2); // Make 0 the root of 2 + uf.union(3, 4); + uf.union(4, 5); // Make 3 the root of 5 + uf.union(0, 3); // Union two trees + + assertEquals(5, uf.count(), "Count should decrease after unions."); + assertEquals(0, uf.find(5), "Element 5 should point to root 0 after unions."); + } + + @Test + void testFindOperation() { + uf.union(2, 3); + uf.union(4, 5); + uf.union(3, 5); // Connect 2-3 and 4-5 + + assertEquals(2, uf.find(3), "Find operation should return the root of the set."); + assertEquals(2, uf.find(5), "Find operation should return the root of the set."); + } + + @Test + void testCountAfterMultipleUnions() { + uf.union(0, 1); + uf.union(2, 3); + uf.union(4, 5); + uf.union(1, 3); // Connect 0-1-2-3 + uf.union(5, 6); + + assertEquals(5, uf.count(), "Count should reflect the number of disjoint sets after multiple unions."); + } + + @Test + void testNoUnion() { + assertEquals(10, uf.count(), "Count should remain 10 if no unions are made."); + } + + @Test + void testUnionSameSet() { + uf.union(1, 2); + uf.union(1, 2); // Union same elements again + + assertEquals(9, uf.count(), "Count should not decrease if union is called on the same set."); + } + + @Test + void testFindOnSingleElement() { + assertEquals(7, uf.find(7), "Find on a single element should return itself."); + } +} diff --git a/src/test/java/com/thealgorithms/searches/UpperBoundTest.java b/src/test/java/com/thealgorithms/searches/UpperBoundTest.java new file mode 100644 index 000000000000..dc0cbdd97e19 --- /dev/null +++ b/src/test/java/com/thealgorithms/searches/UpperBoundTest.java @@ -0,0 +1,99 @@ +package com.thealgorithms.searches; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Random; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class UpperBoundTest { + + private UpperBound upperBound; + private Integer[] sortedArray; + + @BeforeEach + void setUp() { + upperBound = new UpperBound(); + + // Generate a sorted array of random integers for testing + Random random = new Random(); + int size = 100; + int maxElement = 100; + sortedArray = random.ints(size, 1, maxElement) + .distinct() // Ensure all elements are unique + .sorted() + .boxed() + .toArray(Integer[] ::new); + } + + @Test + void testUpperBoundFound() { + int key = sortedArray[sortedArray.length - 1] + 1; // Test with a key larger than max element + int index = upperBound.find(sortedArray, key); + + // The upper bound should be equal to the length of the array + assertEquals(sortedArray.length - 1, index, "Upper bound for a larger key should be the size of the array."); + } + + @Test + void testUpperBoundExactMatch() { + int key = sortedArray[sortedArray.length / 2]; // Choose a key from the middle of the array + int index = upperBound.find(sortedArray, key); + + // The index should point to the first element greater than the key + assertTrue(index < sortedArray.length, "Upper bound should not exceed array length."); + assertTrue(sortedArray[index] > key, "The element at the index should be greater than the key."); + } + + @Test + void testUpperBoundMultipleValues() { + Integer[] arrayWithDuplicates = new Integer[] {1, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9}; // Test array with duplicates + int key = 4; + int index = upperBound.find(arrayWithDuplicates, key); + + assertTrue(index < arrayWithDuplicates.length, "Upper bound index should be valid."); + assertEquals(6, index, "The upper bound for 4 should be the index of the first 5."); + assertTrue(arrayWithDuplicates[index] > key, "Element at the upper bound index should be greater than the key."); + } + + @Test + void testUpperBoundLowerThanMin() { + int key = 0; // Test with a key lower than the minimum element + int index = upperBound.find(sortedArray, key); + + assertEquals(0, index, "Upper bound for a key lower than minimum should be 0."); + assertTrue(sortedArray[index] > key, "The element at index 0 should be greater than the key."); + } + + @Test + void testUpperBoundHigherThanMax() { + int key = sortedArray[sortedArray.length - 1] + 1; // Test with a key higher than maximum element + int index = upperBound.find(sortedArray, key); + + assertEquals(sortedArray.length - 1, index, "Upper bound for a key higher than maximum should be the size of the array."); + } + + @Test + void testUpperBoundEdgeCase() { + // Edge case: empty array + Integer[] emptyArray = {}; + int index = upperBound.find(emptyArray, 5); + + assertEquals(0, index, "Upper bound for an empty array should be 0."); + } + + @Test + void testUpperBoundSingleElementArray() { + Integer[] singleElementArray = {10}; + int index = upperBound.find(singleElementArray, 5); + + assertEquals(0, index, "Upper bound for 5 in a single element array should be 0."); + + index = upperBound.find(singleElementArray, 10); + assertEquals(0, index, "Upper bound for 10 in a single element array should be 0."); + + index = upperBound.find(singleElementArray, 15); + assertEquals(0, index, "Upper bound for 15 in a single element array should be 0."); + } +} diff --git a/src/test/java/com/thealgorithms/slidingwindow/LongestSubarrayWithSumLessOrEqualToKTest.java b/src/test/java/com/thealgorithms/slidingwindow/LongestSubarrayWithSumLessOrEqualToKTest.java new file mode 100644 index 000000000000..da282ab35ef3 --- /dev/null +++ b/src/test/java/com/thealgorithms/slidingwindow/LongestSubarrayWithSumLessOrEqualToKTest.java @@ -0,0 +1,22 @@ +package com.thealgorithms.slidingwindow; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the LongestSubarrayWithSumLessOrEqualToK algorithm. + */ +public class LongestSubarrayWithSumLessOrEqualToKTest { + + /** + * Tests for the longest subarray with a sum less than or equal to k. + */ + @Test + public void testLongestSubarrayWithSumLEK() { + assertEquals(3, LongestSubarrayWithSumLessOrEqualToK.longestSubarrayWithSumLEK(new int[] {1, 2, 3, 4}, 6)); // {1, 2, 3} + assertEquals(4, LongestSubarrayWithSumLessOrEqualToK.longestSubarrayWithSumLEK(new int[] {1, 2, 3, 4}, 10)); // {1, 2, 3, 4} + assertEquals(2, LongestSubarrayWithSumLessOrEqualToK.longestSubarrayWithSumLEK(new int[] {5, 1, 2, 3}, 5)); // {5} + assertEquals(0, LongestSubarrayWithSumLessOrEqualToK.longestSubarrayWithSumLEK(new int[] {1, 2, 3}, 0)); // No valid subarray + } +} diff --git a/src/test/java/com/thealgorithms/slidingwindow/LongestSubstringWithoutRepeatingCharactersTest.java b/src/test/java/com/thealgorithms/slidingwindow/LongestSubstringWithoutRepeatingCharactersTest.java new file mode 100644 index 000000000000..8638a707a3e2 --- /dev/null +++ b/src/test/java/com/thealgorithms/slidingwindow/LongestSubstringWithoutRepeatingCharactersTest.java @@ -0,0 +1,23 @@ +package com.thealgorithms.slidingwindow; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the LongestSubstringWithoutRepeatingCharacters class. + * + * @author (https://github.com/Chiefpatwal) + */ +public class LongestSubstringWithoutRepeatingCharactersTest { + + @Test + public void testLengthOfLongestSubstring() { + // Test cases for the lengthOfLongestSubstring method + assertEquals(3, LongestSubstringWithoutRepeatingCharacters.lengthOfLongestSubstring("abcabcbb")); + assertEquals(1, LongestSubstringWithoutRepeatingCharacters.lengthOfLongestSubstring("bbbbb")); + assertEquals(3, LongestSubstringWithoutRepeatingCharacters.lengthOfLongestSubstring("pwwkew")); + assertEquals(0, LongestSubstringWithoutRepeatingCharacters.lengthOfLongestSubstring("")); + assertEquals(5, LongestSubstringWithoutRepeatingCharacters.lengthOfLongestSubstring("abcde")); + } +} diff --git a/src/test/java/com/thealgorithms/slidingwindow/MaxSumKSizeSubarrayTest.java b/src/test/java/com/thealgorithms/slidingwindow/MaxSumKSizeSubarrayTest.java new file mode 100644 index 000000000000..aa3c2eae3294 --- /dev/null +++ b/src/test/java/com/thealgorithms/slidingwindow/MaxSumKSizeSubarrayTest.java @@ -0,0 +1,79 @@ +package com.thealgorithms.slidingwindow; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the MaxSumKSizeSubarray class. + * + * @author Your Name (https://github.com/Chiefpatwal) + */ +class MaxSumKSizeSubarrayTest { + + /** + * Test for the basic case of finding the maximum sum. + */ + @Test + void testMaxSumKSizeSubarray() { + int[] arr = {1, 2, 3, 4, 5}; + int k = 2; + int expectedMaxSum = 9; // 4 + 5 + assertEquals(expectedMaxSum, MaxSumKSizeSubarray.maxSumKSizeSubarray(arr, k)); + } + + /** + * Test for a different array and subarray size. + */ + @Test + void testMaxSumKSizeSubarrayWithDifferentValues() { + int[] arr = {2, 1, 5, 1, 3, 2}; + int k = 3; + int expectedMaxSum = 9; // 5 + 1 + 3 + assertEquals(expectedMaxSum, MaxSumKSizeSubarray.maxSumKSizeSubarray(arr, k)); + } + + /** + * Test for edge case with insufficient elements. + */ + @Test + void testMaxSumKSizeSubarrayWithInsufficientElements() { + int[] arr = {1, 2}; + int k = 3; // Not enough elements + int expectedMaxSum = -1; // Edge case + assertEquals(expectedMaxSum, MaxSumKSizeSubarray.maxSumKSizeSubarray(arr, k)); + } + + /** + * Test for large array. + */ + @Test + void testMaxSumKSizeSubarrayWithLargeArray() { + int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + int k = 5; + int expectedMaxSum = 40; // 6 + 7 + 8 + 9 + 10 + assertEquals(expectedMaxSum, MaxSumKSizeSubarray.maxSumKSizeSubarray(arr, k)); + } + + /** + * Test for array with negative numbers. + */ + @Test + void testMaxSumKSizeSubarrayWithNegativeNumbers() { + int[] arr = {-1, -2, -3, -4, -5}; + int k = 2; + int expectedMaxSum = -3; // -1 + -2 + assertEquals(expectedMaxSum, MaxSumKSizeSubarray.maxSumKSizeSubarray(arr, k)); + } + + /** + * Test for the case where k equals the array length. + */ + @Test + void testMaxSumKSizeSubarrayWithKEqualToArrayLength() { + int[] arr = {1, 2, 3, 4, 5}; + int k = 5; + int expectedMaxSum = 15; // 1 + 2 + 3 + 4 + 5 + assertEquals(expectedMaxSum, MaxSumKSizeSubarray.maxSumKSizeSubarray(arr, k)); + } +} diff --git a/src/test/java/com/thealgorithms/slidingwindow/MinSumKSizeSubarrayTest.java b/src/test/java/com/thealgorithms/slidingwindow/MinSumKSizeSubarrayTest.java new file mode 100644 index 000000000000..4656c8baf327 --- /dev/null +++ b/src/test/java/com/thealgorithms/slidingwindow/MinSumKSizeSubarrayTest.java @@ -0,0 +1,79 @@ +package com.thealgorithms.slidingwindow; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the MinSumKSizeSubarray class. + * + * @author Rashi Dashore (https://github.com/rashi07dashore) + */ +class MinSumKSizeSubarrayTest { + + /** + * Test for the basic case of finding the minimum sum. + */ + @Test + void testMinSumKSizeSubarray() { + int[] arr = {2, 1, 5, 1, 3, 2}; + int k = 3; + int expectedMinSum = 6; // Corrected: Minimum sum of a subarray of size 3 + assertEquals(expectedMinSum, MinSumKSizeSubarray.minSumKSizeSubarray(arr, k)); + } + + /** + * Test for a different array and subarray size. + */ + @Test + void testMinSumKSizeSubarrayWithDifferentValues() { + int[] arr = {1, 2, 3, 4, 5}; + int k = 2; + int expectedMinSum = 3; // 1 + 2 + assertEquals(expectedMinSum, MinSumKSizeSubarray.minSumKSizeSubarray(arr, k)); + } + + /** + * Test for edge case with insufficient elements. + */ + @Test + void testMinSumKSizeSubarrayWithInsufficientElements() { + int[] arr = {1, 2}; + int k = 3; // Not enough elements + int expectedMinSum = -1; // Edge case + assertEquals(expectedMinSum, MinSumKSizeSubarray.minSumKSizeSubarray(arr, k)); + } + + /** + * Test for large array. + */ + @Test + void testMinSumKSizeSubarrayWithLargeArray() { + int[] arr = {5, 4, 3, 2, 1, 0, -1, -2, -3, -4}; + int k = 5; + int expectedMinSum = -10; // -1 + -2 + -3 + -4 + 0 + assertEquals(expectedMinSum, MinSumKSizeSubarray.minSumKSizeSubarray(arr, k)); + } + + /** + * Test for array with negative numbers. + */ + @Test + void testMinSumKSizeSubarrayWithNegativeNumbers() { + int[] arr = {-1, -2, -3, -4, -5}; + int k = 2; + int expectedMinSum = -9; // -4 + -5 + assertEquals(expectedMinSum, MinSumKSizeSubarray.minSumKSizeSubarray(arr, k)); + } + + /** + * Test for the case where k equals the array length. + */ + @Test + void testMinSumKSizeSubarrayWithKEqualToArrayLength() { + int[] arr = {1, 2, 3, 4, 5}; + int k = 5; + int expectedMinSum = 15; // 1 + 2 + 3 + 4 + 5 + assertEquals(expectedMinSum, MinSumKSizeSubarray.minSumKSizeSubarray(arr, k)); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/AdaptiveMergeSortTest.java b/src/test/java/com/thealgorithms/sorts/AdaptiveMergeSortTest.java new file mode 100644 index 000000000000..9d94b165d81b --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/AdaptiveMergeSortTest.java @@ -0,0 +1,53 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +public class AdaptiveMergeSortTest { + + @Test + public void testSortIntegers() { + AdaptiveMergeSort adaptiveMergeSort = new AdaptiveMergeSort(); + Integer[] input = {4, 23, 6, 78, 1, 54, 231, 9, 12}; + Integer[] expected = {1, 4, 6, 9, 12, 23, 54, 78, 231}; + Integer[] result = adaptiveMergeSort.sort(input); + assertArrayEquals(expected, result); + } + + @Test + public void testSortStrings() { + AdaptiveMergeSort adaptiveMergeSort = new AdaptiveMergeSort(); + String[] input = {"c", "a", "e", "b", "d"}; + String[] expected = {"a", "b", "c", "d", "e"}; + String[] result = adaptiveMergeSort.sort(input); + assertArrayEquals(expected, result); + } + + @Test + public void testSortWithDuplicates() { + AdaptiveMergeSort adaptiveMergeSort = new AdaptiveMergeSort(); + Integer[] input = {1, 3, 2, 2, 5, 4}; + Integer[] expected = {1, 2, 2, 3, 4, 5}; + Integer[] result = adaptiveMergeSort.sort(input); + assertArrayEquals(expected, result); + } + + @Test + public void testSortEmptyArray() { + AdaptiveMergeSort adaptiveMergeSort = new AdaptiveMergeSort(); + Integer[] input = {}; + Integer[] expected = {}; + Integer[] result = adaptiveMergeSort.sort(input); + assertArrayEquals(expected, result); + } + + @Test + public void testSortSingleElement() { + AdaptiveMergeSort adaptiveMergeSort = new AdaptiveMergeSort(); + Integer[] input = {42}; + Integer[] expected = {42}; + Integer[] result = adaptiveMergeSort.sort(input); + assertArrayEquals(expected, result); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/BeadSortTest.java b/src/test/java/com/thealgorithms/sorts/BeadSortTest.java new file mode 100644 index 000000000000..d8519147bc2e --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/BeadSortTest.java @@ -0,0 +1,29 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class BeadSortTest { + @ParameterizedTest + @MethodSource("provideArraysForBeadSort") + public void testBeadSort(int[] inputArray, int[] expectedArray) { + BeadSort beadSort = new BeadSort(); + assertArrayEquals(expectedArray, beadSort.sort(inputArray)); + } + + private static Stream<Arguments> provideArraysForBeadSort() { + return Stream.of(Arguments.of(new int[] {}, new int[] {}), Arguments.of(new int[] {4}, new int[] {4}), Arguments.of(new int[] {6, 1, 99, 27, 15, 23, 36}, new int[] {1, 6, 15, 23, 27, 36, 99}), Arguments.of(new int[] {6, 1, 27, 15, 23, 27, 36, 23}, new int[] {1, 6, 15, 23, 23, 27, 27, 36}), + Arguments.of(new int[] {5, 5, 5, 5, 5}, new int[] {5, 5, 5, 5, 5}), Arguments.of(new int[] {1, 2, 3, 4, 5}, new int[] {1, 2, 3, 4, 5}), Arguments.of(new int[] {5, 4, 3, 2, 1}, new int[] {1, 2, 3, 4, 5})); + } + + @Test + public void testWithNegativeNumbers() { + assertThrows(IllegalArgumentException.class, () -> new BeadSort().sort(new int[] {3, 1, 4, 1, 5, -9})); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/BinaryInsertionSortTest.java b/src/test/java/com/thealgorithms/sorts/BinaryInsertionSortTest.java new file mode 100644 index 000000000000..82cb3ba60987 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/BinaryInsertionSortTest.java @@ -0,0 +1,10 @@ +package com.thealgorithms.sorts; + +class BinaryInsertionSortTest extends SortingAlgorithmTest { + private final BinaryInsertionSort binaryInsertionSort = new BinaryInsertionSort(); + + @Override + SortAlgorithm getSortAlgorithm() { + return binaryInsertionSort; + } +} diff --git a/src/test/java/com/thealgorithms/sorts/BitonicSortTest.java b/src/test/java/com/thealgorithms/sorts/BitonicSortTest.java new file mode 100644 index 000000000000..60c4bbe9d342 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/BitonicSortTest.java @@ -0,0 +1,8 @@ +package com.thealgorithms.sorts; + +public class BitonicSortTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new BitonicSort(); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/BogoSortTest.java b/src/test/java/com/thealgorithms/sorts/BogoSortTest.java new file mode 100644 index 000000000000..3ebfb7a305b0 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/BogoSortTest.java @@ -0,0 +1,66 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +public class BogoSortTest { + + private BogoSort bogoSort = new BogoSort(); + + @Test + public void bogoSortEmptyArray() { + Integer[] inputArray = {}; + Integer[] outputArray = bogoSort.sort(inputArray); + Integer[] expectedOutput = {}; + assertArrayEquals(outputArray, expectedOutput); + } + + @Test + public void bogoSortSingleIntegerArray() { + Integer[] inputArray = {4}; + Integer[] outputArray = bogoSort.sort(inputArray); + Integer[] expectedOutput = {4}; + assertArrayEquals(outputArray, expectedOutput); + } + + @Test + public void bogoSortSingleStringArray() { + String[] inputArray = {"s"}; + String[] outputArray = bogoSort.sort(inputArray); + String[] expectedOutput = {"s"}; + assertArrayEquals(outputArray, expectedOutput); + } + + @Test + public void bogoSortNonDuplicateIntegerArray() { + Integer[] inputArray = {6, -1, 99, 27, -15, 23, -36}; + Integer[] outputArray = bogoSort.sort(inputArray); + Integer[] expectedOutput = {-36, -15, -1, 6, 23, 27, 99}; + assertArrayEquals(outputArray, expectedOutput); + } + + @Test + public void bogoSortDuplicateIntegerArray() { + Integer[] inputArray = {6, -1, 27, -15, 23, 27, -36, 23}; + Integer[] outputArray = bogoSort.sort(inputArray); + Integer[] expectedOutput = {-36, -15, -1, 6, 23, 23, 27, 27}; + assertArrayEquals(outputArray, expectedOutput); + } + + @Test + public void bogoSortNonDuplicateStringArray() { + String[] inputArray = {"s", "b", "k", "a", "d", "c", "h"}; + String[] outputArray = bogoSort.sort(inputArray); + String[] expectedOutput = {"a", "b", "c", "d", "h", "k", "s"}; + assertArrayEquals(outputArray, expectedOutput); + } + + @Test + public void bogoSortDuplicateStringArray() { + String[] inputArray = {"s", "b", "d", "a", "d", "c", "h", "b"}; + String[] outputArray = bogoSort.sort(inputArray); + String[] expectedOutput = {"a", "b", "b", "c", "d", "d", "h", "s"}; + assertArrayEquals(outputArray, expectedOutput); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/BubbleSortRecursiveTest.java b/src/test/java/com/thealgorithms/sorts/BubbleSortRecursiveTest.java new file mode 100644 index 000000000000..e05842f46be8 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/BubbleSortRecursiveTest.java @@ -0,0 +1,8 @@ +package com.thealgorithms.sorts; + +public class BubbleSortRecursiveTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new BubbleSortRecursive(); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/BubbleSortTest.java b/src/test/java/com/thealgorithms/sorts/BubbleSortTest.java new file mode 100644 index 000000000000..8690a3f5435c --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/BubbleSortTest.java @@ -0,0 +1,94 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +/** + * @author Aitor Fidalgo (https://github.com/aitorfi) + * @see BubbleSort + */ +public class BubbleSortTest { + + private BubbleSort bubbleSort = new BubbleSort(); + + @Test + public void bubbleSortEmptyArray() { + Integer[] inputArray = {}; + Integer[] outputArray = bubbleSort.sort(inputArray); + Integer[] expectedOutput = {}; + assertArrayEquals(outputArray, expectedOutput); + } + + @Test + public void bubbleSortSingleIntegerElementArray() { + Integer[] inputArray = {4}; + Integer[] outputArray = bubbleSort.sort(inputArray); + Integer[] expectedOutput = {4}; + assertArrayEquals(outputArray, expectedOutput); + } + + @Test + public void bubbleSortSingleStringElementArray() { + String[] inputArray = {"s"}; + String[] outputArray = bubbleSort.sort(inputArray); + String[] expectedOutput = {"s"}; + assertArrayEquals(outputArray, expectedOutput); + } + + @Test + public void bubbleSortIntegerArray() { + Integer[] inputArray = {4, 23, -6, 78, 1, 54, 23, -6, -231, 9, 12}; + Integer[] outputArray = bubbleSort.sort(inputArray); + Integer[] expectedOutput = { + -231, + -6, + -6, + 1, + 4, + 9, + 12, + 23, + 23, + 54, + 78, + }; + assertArrayEquals(outputArray, expectedOutput); + } + + @Test + public void bubbleSortStringArray() { + String[] inputArray = { + "cbf", + "auk", + "ó", + "(b", + "a", + ")", + "au", + "á", + "cba", + "auk", + "(a", + "bhy", + "cba", + }; + String[] outputArray = bubbleSort.sort(inputArray); + String[] expectedOutput = { + "(a", + "(b", + ")", + "a", + "au", + "auk", + "auk", + "bhy", + "cba", + "cba", + "cbf", + "á", + "ó", + }; + assertArrayEquals(outputArray, expectedOutput); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/BucketSortTest.java b/src/test/java/com/thealgorithms/sorts/BucketSortTest.java new file mode 100644 index 000000000000..a2dcb8cadfd9 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/BucketSortTest.java @@ -0,0 +1,8 @@ +package com.thealgorithms.sorts; + +public class BucketSortTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new BucketSort(); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/CircleSortTest.java b/src/test/java/com/thealgorithms/sorts/CircleSortTest.java new file mode 100644 index 000000000000..d113473272b7 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/CircleSortTest.java @@ -0,0 +1,8 @@ +package com.thealgorithms.sorts; + +class CircleSortTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new CircleSort(); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/CocktailShakerSortTest.java b/src/test/java/com/thealgorithms/sorts/CocktailShakerSortTest.java new file mode 100644 index 000000000000..45c1220275df --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/CocktailShakerSortTest.java @@ -0,0 +1,8 @@ +package com.thealgorithms.sorts; + +public class CocktailShakerSortTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new CocktailShakerSort(); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/CombSortTest.java b/src/test/java/com/thealgorithms/sorts/CombSortTest.java new file mode 100644 index 000000000000..6b70ffacda47 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/CombSortTest.java @@ -0,0 +1,8 @@ +package com.thealgorithms.sorts; + +public class CombSortTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new CombSort(); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/CountingSortTest.java b/src/test/java/com/thealgorithms/sorts/CountingSortTest.java new file mode 100644 index 000000000000..2426de6f2807 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/CountingSortTest.java @@ -0,0 +1,27 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class CountingSortTest { + + record TestCase(int[] inputArray, int[] expectedArray) { + } + + static Stream<TestCase> provideTestCases() { + return Stream.of(new TestCase(new int[] {}, new int[] {}), new TestCase(new int[] {4}, new int[] {4}), new TestCase(new int[] {6, 1, 99, 27, 15, 23, 36}, new int[] {1, 6, 15, 23, 27, 36, 99}), new TestCase(new int[] {6, 1, 27, 15, 23, 27, 36, 23}, new int[] {1, 6, 15, 23, 23, 27, 27, 36}), + new TestCase(new int[] {5, 5, 5, 5, 5}, new int[] {5, 5, 5, 5, 5}), new TestCase(new int[] {1, 2, 3, 4, 5}, new int[] {1, 2, 3, 4, 5}), new TestCase(new int[] {5, 4, 3, 2, 1}, new int[] {1, 2, 3, 4, 5}), new TestCase(new int[] {3, -1, 4, 1, 5, -9}, new int[] {-9, -1, 1, 3, 4, 5}), + new TestCase(new int[] {0, 0, 0, 0}, new int[] {0, 0, 0, 0}), new TestCase(new int[] {3, 3, -1, -1, 2, 2, 0, 0}, new int[] {-1, -1, 0, 0, 2, 2, 3, 3}), new TestCase(new int[] {-3, -2, -1, -5, -4}, new int[] {-5, -4, -3, -2, -1}), + new TestCase(new int[] {1000, 500, 100, 50, 10, 5, 1}, new int[] {1, 5, 10, 50, 100, 500, 1000}), new TestCase(new int[] {4, -5, 10, 0}, new int[] {-5, 0, 4, 10})); + } + + @ParameterizedTest + @MethodSource("provideTestCases") + public void testCountingSortException(TestCase testCase) { + int[] outputArray = CountingSort.sort(testCase.inputArray); + assertArrayEquals(testCase.expectedArray, outputArray); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/CycleSortTest.java b/src/test/java/com/thealgorithms/sorts/CycleSortTest.java new file mode 100644 index 000000000000..b8c3d1653713 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/CycleSortTest.java @@ -0,0 +1,8 @@ +package com.thealgorithms.sorts; + +public class CycleSortTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new CycleSort(); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/DarkSortTest.java b/src/test/java/com/thealgorithms/sorts/DarkSortTest.java new file mode 100644 index 000000000000..1df077e2ad74 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/DarkSortTest.java @@ -0,0 +1,74 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.Test; + +class DarkSortTest { + + @Test + void testSortWithIntegers() { + Integer[] unsorted = {5, 3, 8, 6, 2, 7, 4, 1}; + Integer[] expected = {1, 2, 3, 4, 5, 6, 7, 8}; + + DarkSort darkSort = new DarkSort(); + Integer[] sorted = darkSort.sort(unsorted); + + assertArrayEquals(expected, sorted); + } + + @Test + void testEmptyArray() { + Integer[] unsorted = {}; + Integer[] expected = {}; + + DarkSort darkSort = new DarkSort(); + Integer[] sorted = darkSort.sort(unsorted); + + assertArrayEquals(expected, sorted); + } + + @Test + void testSingleElementArray() { + Integer[] unsorted = {42}; + Integer[] expected = {42}; + + DarkSort darkSort = new DarkSort(); + Integer[] sorted = darkSort.sort(unsorted); + + assertArrayEquals(expected, sorted); + } + + @Test + void testAlreadySortedArray() { + Integer[] unsorted = {1, 2, 3, 4, 5}; + Integer[] expected = {1, 2, 3, 4, 5}; + + DarkSort darkSort = new DarkSort(); + Integer[] sorted = darkSort.sort(unsorted); + + assertArrayEquals(expected, sorted); + } + + @Test + void testDuplicateElementsArray() { + Integer[] unsorted = {4, 2, 7, 2, 1, 4}; + Integer[] expected = {1, 2, 2, 4, 4, 7}; + + DarkSort darkSort = new DarkSort(); + Integer[] sorted = darkSort.sort(unsorted); + + assertArrayEquals(expected, sorted); + } + + @Test + void testNullArray() { + Integer[] unsorted = null; + + DarkSort darkSort = new DarkSort(); + Integer[] sorted = darkSort.sort(unsorted); + + assertNull(sorted, "Sorting a null array should return null"); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/DualPivotQuickSortTest.java b/src/test/java/com/thealgorithms/sorts/DualPivotQuickSortTest.java new file mode 100644 index 000000000000..0b9cc1de5886 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/DualPivotQuickSortTest.java @@ -0,0 +1,8 @@ +package com.thealgorithms.sorts; + +class DualPivotQuickSortTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new DualPivotQuickSort(); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/DutchNationalFlagSortTest.java b/src/test/java/com/thealgorithms/sorts/DutchNationalFlagSortTest.java new file mode 100644 index 000000000000..c7581e7c8f7b --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/DutchNationalFlagSortTest.java @@ -0,0 +1,112 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +public class DutchNationalFlagSortTest { + + @Test + /* + 1 will be used as intended middle. + Partitions on the result array: [ smaller than 1 , equal 1, greater than 1] + */ + void testOddDnfs() { + Integer[] integers = {1, 3, 1, 4, 0}; + Integer[] integersResult = {0, 1, 1, 4, 3}; + DutchNationalFlagSort dutchNationalFlagSort = new DutchNationalFlagSort(); + dutchNationalFlagSort.sort(integers); + assertArrayEquals(integers, integersResult); + } + + @Test + /* + 3 will be used as intended middle. + Partitions on the result array: [ smaller than 3 , equal 3, greater than 3] + */ + void testEvenDnfs() { + Integer[] integers = {8, 1, 3, 1, 4, 0}; + Integer[] integersResult = {0, 1, 1, 3, 4, 8}; + DutchNationalFlagSort dutchNationalFlagSort = new DutchNationalFlagSort(); + dutchNationalFlagSort.sort(integers); + assertArrayEquals(integers, integersResult); + } + + @Test + /* + "b" will be used as intended middle. + Partitions on the result array: [ smaller than b , equal b, greater than b] + */ + void testEvenStringsDnfs() { + String[] strings = {"a", "d", "b", "s", "e", "e"}; + String[] stringsResult = {"a", "b", "s", "e", "e", "d"}; + DutchNationalFlagSort dutchNationalFlagSort = new DutchNationalFlagSort(); + dutchNationalFlagSort.sort(strings); + assertArrayEquals(strings, stringsResult); + } + + @Test + /* + "b" will be used as intended middle. + Partitions on the result array: [ smaller than b , equal b, greater than b] + */ + void testOddStringsDnfs() { + String[] strings = {"a", "d", "b", "s", "e"}; + String[] stringsResult = {"a", "b", "s", "e", "d"}; + DutchNationalFlagSort dutchNationalFlagSort = new DutchNationalFlagSort(); + dutchNationalFlagSort.sort(strings); + assertArrayEquals(strings, stringsResult); + } + + @Test + /* + 0 will be used as intended middle. + Partitions on the result array: [ smaller than 0 , equal 0, greater than 0] + */ + void testOddMidGivenDnfs() { + Integer[] integers = {1, 3, 1, 4, 0}; + Integer[] integersResult = {0, 1, 4, 3, 1}; + DutchNationalFlagSort dutchNationalFlagSort = new DutchNationalFlagSort(); + dutchNationalFlagSort.sort(integers, 0); + assertArrayEquals(integers, integersResult); + } + + @Test + /* + 4 will be used as intended middle. + Partitions on the result array: [ smaller than 4 , equal 4, greater than 4] + */ + void testEvenMidGivenDnfs() { + Integer[] integers = {8, 1, 3, 1, 4, 0}; + Integer[] integersResult = {0, 1, 3, 1, 4, 8}; + DutchNationalFlagSort dutchNationalFlagSort = new DutchNationalFlagSort(); + dutchNationalFlagSort.sort(integers, 4); + assertArrayEquals(integers, integersResult); + } + + @Test + /* + "s" will be used as intended middle. + Partitions on the result array: [ smaller than s , equal s, greater than s] + */ + void testEvenStringsMidGivenDnfs() { + String[] strings = {"a", "d", "b", "s", "e", "e"}; + String[] stringsResult = {"a", "d", "b", "e", "e", "s"}; + DutchNationalFlagSort dutchNationalFlagSort = new DutchNationalFlagSort(); + dutchNationalFlagSort.sort(strings, "s"); + assertArrayEquals(strings, stringsResult); + } + + @Test + /* + "e" will be used as intended middle. + Partitions on the result array: [ smaller than e , equal e, greater than e] + */ + void testOddStringsMidGivenDnfs() { + String[] strings = {"a", "d", "b", "s", "e"}; + String[] stringsResult = {"a", "d", "b", "e", "s"}; + DutchNationalFlagSort dutchNationalFlagSort = new DutchNationalFlagSort(); + dutchNationalFlagSort.sort(strings, "e"); + assertArrayEquals(strings, stringsResult); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/ExchangeSortTest.java b/src/test/java/com/thealgorithms/sorts/ExchangeSortTest.java new file mode 100644 index 000000000000..6c4271fa9e19 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/ExchangeSortTest.java @@ -0,0 +1,8 @@ +package com.thealgorithms.sorts; + +public class ExchangeSortTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new ExchangeSort(); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/FlashSortTest.java b/src/test/java/com/thealgorithms/sorts/FlashSortTest.java new file mode 100644 index 000000000000..6b1a74403a59 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/FlashSortTest.java @@ -0,0 +1,90 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestFactory; +import org.junit.jupiter.api.function.Executable; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +public class FlashSortTest extends SortingAlgorithmTest { + private final FlashSort flashSort = new FlashSort(); + + public FlashSort getFlashSort() { + return flashSort; + } + + @Override + SortAlgorithm getSortAlgorithm() { + return getFlashSort(); + } + + @Test + public void testDefaultConstructor() { + double defaultRation = 0.45; + FlashSort sorter = new FlashSort(); + assertEquals(defaultRation, sorter.getClassificationRatio()); + } + + @ParameterizedTest + @ValueSource(doubles = {0.1, 0.2, 0.5, 0.9}) + public void testCustomConstructorValidRatio(double ratio) { + FlashSort sorter = new FlashSort(ratio); + assertEquals(ratio, sorter.getClassificationRatio()); + } + + @ParameterizedTest + @ValueSource(doubles = {0, 1, -0.1, 1.1}) + public void testCustomConstructorInvalidRatio(double ratio) { + assertThrows(IllegalArgumentException.class, () -> new FlashSort(ratio)); + } + + @TestFactory + public Collection<DynamicTest> dynamicTestsForSorting() { + List<DynamicTest> dynamicTests = new ArrayList<>(); + double[] ratios = {0.1, 0.2, 0.5, 0.9}; + + for (double ratio : ratios) { + FlashSort sorter = (FlashSort) getSortAlgorithm(); + sorter.setClassificationRatio(ratio); + dynamicTests.addAll(createDynamicTestsForRatio(ratio)); + } + + return dynamicTests; + } + + private Collection<DynamicTest> createDynamicTestsForRatio(double ratio) { + List<DynamicTest> dynamicTests = new ArrayList<>(); + for (TestMethod testMethod : getTestMethodsFromSuperClass()) { + dynamicTests.add(DynamicTest.dynamicTest("Ratio: " + ratio + " - Test: " + testMethod.name(), testMethod.executable())); + } + return dynamicTests; + } + + private List<TestMethod> getTestMethodsFromSuperClass() { + List<TestMethod> testMethods = new ArrayList<>(); + Method[] methods = SortingAlgorithmTest.class.getDeclaredMethods(); + for (Method method : methods) { + if (method.isAnnotationPresent(Test.class)) { + testMethods.add(new TestMethod(() -> { + try { + method.invoke(this); + } catch (Exception e) { + throw new RuntimeException(e); + } + }, method.getName())); + } + } + return testMethods; + } + + record TestMethod(Executable executable, String name) { + } +} diff --git a/src/test/java/com/thealgorithms/sorts/GnomeSortTest.java b/src/test/java/com/thealgorithms/sorts/GnomeSortTest.java new file mode 100644 index 000000000000..d86546472580 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/GnomeSortTest.java @@ -0,0 +1,82 @@ +package com.thealgorithms.sorts; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class GnomeSortTest { + + private GnomeSort gnomeSort = new GnomeSort(); + + @Test + @DisplayName("GnomeSort empty Array") + public void gnomeSortEmptyArray() { + Integer[] inputArray = {}; + gnomeSort.sort(inputArray); + assertThat(inputArray).isEmpty(); + } + + @Test + @DisplayName("GnomeSort single Integer Array") + public void singleIntegerArray() { + Integer[] inputArray = {4}; + Integer[] expectedOutput = {4}; + gnomeSort.sort(inputArray); + assertThat(inputArray).isEqualTo(expectedOutput); + } + + @Test + @DisplayName("GnomeSort non duplicate Integer Array") + public void gnomeSortNonDuplicateIntegerArray() { + Integer[] inputArray = {6, 3, 87, 99, 27, 4}; + Integer[] expectedOutput = {3, 4, 6, 27, 87, 99}; + gnomeSort.sort(inputArray); + assertThat(inputArray).isEqualTo(expectedOutput); + } + + @Test + @DisplayName("GnomeSort Integer Array with duplicates") + public void gnomeSortDuplicateIntegerArray() { + Integer[] inputArray = {6, 3, 87, 3, 99, 27, 4, 27}; + Integer[] expectedOutput = {3, 3, 4, 6, 27, 27, 87, 99}; + gnomeSort.sort(inputArray); + assertThat(inputArray).isEqualTo(expectedOutput); + } + + @Test + @DisplayName("GnomeSort negative Integer Array with duplicates") + public void gnomeSortNegativeDuplicateIntegerArray() { + Integer[] inputArray = {6, 3, -87, 3, 99, -27, 4, -27}; + Integer[] expectedOutput = {-87, -27, -27, 3, 3, 4, 6, 99}; + gnomeSort.sort(inputArray); + assertThat(inputArray).isEqualTo(expectedOutput); + } + + @Test + @DisplayName("GnomeSort single String Array") + public void singleStringArray() { + String[] inputArray = {"b"}; + String[] expectedOutput = {"b"}; + gnomeSort.sort(inputArray); + assertThat(inputArray).isEqualTo(expectedOutput); + } + + @Test + @DisplayName("GnomeSort non duplicate String Array") + public void gnomeSortNonDuplicateStringArray() { + String[] inputArray = {"He", "A", "bc", "lo", "n", "bcp", "mhp", "d"}; + String[] expectedOutput = {"A", "He", "bc", "bcp", "d", "lo", "mhp", "n"}; + gnomeSort.sort(inputArray); + assertThat(inputArray).isEqualTo(expectedOutput); + } + + @Test + @DisplayName("GnomeSort String Array with duplicates") + public void gnomeSortDuplicateStringArray() { + String[] inputArray = {"He", "A", "bc", "lo", "n", "bcp", "mhp", "bcp"}; + String[] expectedOutput = {"A", "He", "bc", "bcp", "bcp", "lo", "mhp", "n"}; + gnomeSort.sort(inputArray); + assertThat(inputArray).isEqualTo(expectedOutput); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/HeapSortTest.java b/src/test/java/com/thealgorithms/sorts/HeapSortTest.java new file mode 100644 index 000000000000..90b2ebd68bc6 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/HeapSortTest.java @@ -0,0 +1,8 @@ +package com.thealgorithms.sorts; + +public class HeapSortTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new HeapSort(); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/InsertionSortTest.java b/src/test/java/com/thealgorithms/sorts/InsertionSortTest.java new file mode 100644 index 000000000000..78744973355d --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/InsertionSortTest.java @@ -0,0 +1,114 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.function.Function; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class InsertionSortTest { + private InsertionSort insertionSort; + + @BeforeEach + void setUp() { + insertionSort = new InsertionSort(); + } + + @Test + void insertionSortSortEmptyArrayShouldPass() { + testEmptyArray(insertionSort::sort); + testEmptyArray(insertionSort::sentinelSort); + } + + private void testEmptyArray(Function<Integer[], Integer[]> sortAlgorithm) { + Integer[] array = {}; + Integer[] sorted = sortAlgorithm.apply(array); + Integer[] expected = {}; + assertArrayEquals(expected, sorted); + assertTrue(SortUtils.isSorted(sorted)); + } + + @Test + void insertionSortClassicalSortSingleValueArrayShouldPass() { + testSingleValue(insertionSort::sort); + testSingleValue(insertionSort::sentinelSort); + } + + private void testSingleValue(Function<Integer[], Integer[]> sortAlgorithm) { + Integer[] array = {7}; + Integer[] sorted = sortAlgorithm.apply(array); + Integer[] expected = {7}; + assertArrayEquals(expected, sorted); + assertTrue(SortUtils.isSorted(sorted)); + } + + @Test + void insertionSortClassicalWithIntegerArrayShouldPass() { + testIntegerArray(insertionSort::sort); + testIntegerArray(insertionSort::sentinelSort); + } + + private void testIntegerArray(Function<Integer[], Integer[]> sortAlgorithm) { + Integer[] array = {49, 4, 36, 9, 144, 1}; + Integer[] sorted = sortAlgorithm.apply(array); + Integer[] expected = {1, 4, 9, 36, 49, 144}; + assertArrayEquals(expected, sorted); + assertTrue(SortUtils.isSorted(sorted)); + } + + @Test + void insertionSortClassicalForArrayWithNegativeValuesShouldPass() { + testWithNegativeValues(insertionSort::sort); + testWithNegativeValues(insertionSort::sentinelSort); + } + + private void testWithNegativeValues(Function<Integer[], Integer[]> sortAlgorithm) { + Integer[] array = {49, -36, -144, -49, 1, 9}; + Integer[] sorted = sortAlgorithm.apply(array); + Integer[] expected = {-144, -49, -36, 1, 9, 49}; + assertArrayEquals(expected, sorted); + assertTrue(SortUtils.isSorted(sorted)); + } + + @Test + void insertionSortClassicalForArrayWithDuplicateValuesShouldPass() { + testWithDuplicates(insertionSort::sort); + testWithDuplicates(insertionSort::sentinelSort); + } + + private void testWithDuplicates(Function<Integer[], Integer[]> sortAlgorithm) { + Integer[] array = {36, 1, 49, 1, 4, 9}; + Integer[] sorted = sortAlgorithm.apply(array); + Integer[] expected = {1, 1, 4, 9, 36, 49}; + assertArrayEquals(expected, sorted); + assertTrue(SortUtils.isSorted(sorted)); + } + + @Test + void insertionSortClassicalWithStringArrayShouldPass() { + testWithStringArray(insertionSort::sort); + testWithStringArray(insertionSort::sentinelSort); + } + + private void testWithStringArray(Function<String[], String[]> sortAlgorithm) { + String[] array = {"c", "a", "e", "b", "d"}; + String[] sorted = sortAlgorithm.apply(array); + String[] expected = {"a", "b", "c", "d", "e"}; + assertArrayEquals(expected, sorted); + assertTrue(SortUtils.isSorted(sorted)); + } + + @Test + void insertionSortClassicalWithRandomArrayPass() { + testWithRandomArray(insertionSort::sort); + testWithRandomArray(insertionSort::sentinelSort); + } + + private void testWithRandomArray(Function<Double[], Double[]> sortAlgorithm) { + int randomSize = SortUtilsRandomGenerator.generateInt(10_000); + Double[] array = SortUtilsRandomGenerator.generateArray(randomSize); + Double[] sorted = sortAlgorithm.apply(array); + assertTrue(SortUtils.isSorted(sorted)); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/IntrospectiveSortTest.java b/src/test/java/com/thealgorithms/sorts/IntrospectiveSortTest.java new file mode 100644 index 000000000000..7760ee3849e2 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/IntrospectiveSortTest.java @@ -0,0 +1,8 @@ +package com.thealgorithms.sorts; + +public class IntrospectiveSortTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new IntrospectiveSort(); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/MergeSortNoExtraSpaceTest.java b/src/test/java/com/thealgorithms/sorts/MergeSortNoExtraSpaceTest.java new file mode 100644 index 000000000000..5677a7c95e09 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/MergeSortNoExtraSpaceTest.java @@ -0,0 +1,34 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class MergeSortNoExtraSpaceTest { + record TestCase(int[] inputArray, int[] expectedArray) { + } + + static Stream<TestCase> provideTestCases() { + return Stream.of(new TestCase(new int[] {}, new int[] {}), new TestCase(new int[] {1}, new int[] {1}), new TestCase(new int[] {1, 2, 3, 4, 5}, new int[] {1, 2, 3, 4, 5}), new TestCase(new int[] {5, 4, 3, 2, 1}, new int[] {1, 2, 3, 4, 5}), + new TestCase(new int[] {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5}, new int[] {1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9}), new TestCase(new int[] {4, 2, 4, 3, 2, 1, 5}, new int[] {1, 2, 2, 3, 4, 4, 5}), new TestCase(new int[] {0, 0, 0, 0}, new int[] {0, 0, 0, 0}), + new TestCase(new int[] {1000, 500, 100, 50, 10, 5, 1}, new int[] {1, 5, 10, 50, 100, 500, 1000}), new TestCase(new int[] {1, 2, 3, 1, 2, 3, 1, 2, 3}, new int[] {1, 1, 1, 2, 2, 2, 3, 3, 3}), + new TestCase(new int[] {10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}, new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}), new TestCase(new int[] {2, 1}, new int[] {1, 2}), new TestCase(new int[] {1, 3, 2}, new int[] {1, 2, 3})); + } + + @ParameterizedTest + @MethodSource("provideTestCases") + public void testCountingSort(TestCase testCase) { + int[] outputArray = MergeSortNoExtraSpace.sort(testCase.inputArray); + assertArrayEquals(testCase.expectedArray, outputArray); + } + + @Test + public void testNegativeNumbers() { + int[] arrayWithNegatives = {1, -2, 3, -4}; + assertThrows(IllegalArgumentException.class, () -> MergeSortNoExtraSpace.sort(arrayWithNegatives)); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/MergeSortRecursiveTest.java b/src/test/java/com/thealgorithms/sorts/MergeSortRecursiveTest.java new file mode 100644 index 000000000000..0e7874584c3f --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/MergeSortRecursiveTest.java @@ -0,0 +1,32 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class MergeSortRecursiveTest { + + // private MergeSortRecursive mergeSortRecursive = new MergeSortRecursive(); + + @Test + void testMergeSortRecursiveCase1() { + MergeSortRecursive mergeSortRecursive = new MergeSortRecursive(Arrays.asList(5, 12, 9, 3, 15, 88)); + + List<Integer> expected = Arrays.asList(3, 5, 9, 12, 15, 88); + List<Integer> sorted = mergeSortRecursive.mergeSort(); + + assertEquals(expected, sorted); + } + + @Test + void testMergeSortRecursiveCase2() { + MergeSortRecursive mergeSortRecursive = new MergeSortRecursive(Arrays.asList(-3, 5, 3, 4, 3, 7, 40, -20, 30, 0)); + + List<Integer> expected = Arrays.asList(-20, -3, 0, 3, 3, 4, 5, 7, 30, 40); + List<Integer> sorted = mergeSortRecursive.mergeSort(); + + assertEquals(expected, sorted); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/MergeSortTest.java b/src/test/java/com/thealgorithms/sorts/MergeSortTest.java new file mode 100644 index 000000000000..af06e360a038 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/MergeSortTest.java @@ -0,0 +1,8 @@ +package com.thealgorithms.sorts; + +public class MergeSortTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new MergeSort(); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/OddEvenSortTest.java b/src/test/java/com/thealgorithms/sorts/OddEvenSortTest.java new file mode 100644 index 000000000000..09ef8014cec1 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/OddEvenSortTest.java @@ -0,0 +1,15 @@ +package com.thealgorithms.sorts; + +/** + * @author Tabbygray (https://github.com/Tabbygray) + * @see OddEvenSort + */ + +public class OddEvenSortTest extends SortingAlgorithmTest { + private final OddEvenSort oddEvenSort = new OddEvenSort(); + + @Override + SortAlgorithm getSortAlgorithm() { + return oddEvenSort; + } +} diff --git a/src/test/java/com/thealgorithms/sorts/PancakeSortTest.java b/src/test/java/com/thealgorithms/sorts/PancakeSortTest.java new file mode 100644 index 000000000000..0039bdfb03e7 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/PancakeSortTest.java @@ -0,0 +1,8 @@ +package com.thealgorithms.sorts; + +public class PancakeSortTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new PancakeSort(); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/PatienceSortTest.java b/src/test/java/com/thealgorithms/sorts/PatienceSortTest.java new file mode 100644 index 000000000000..f3cf7874f3b1 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/PatienceSortTest.java @@ -0,0 +1,8 @@ +package com.thealgorithms.sorts; + +public class PatienceSortTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new PatienceSort(); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/PigeonholeSortTest.java b/src/test/java/com/thealgorithms/sorts/PigeonholeSortTest.java new file mode 100644 index 000000000000..d1772de83701 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/PigeonholeSortTest.java @@ -0,0 +1,31 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class PigeonholeSortTest { + + @ParameterizedTest + @MethodSource("provideArraysForPigeonholeSort") + public void testPigeonholeSort(int[] inputArray, int[] expectedArray) { + PigeonholeSort.sort(inputArray); + assertArrayEquals(expectedArray, inputArray); + } + + private static Stream<Arguments> provideArraysForPigeonholeSort() { + return Stream.of(Arguments.of(new int[] {}, new int[] {}), Arguments.of(new int[] {4}, new int[] {4}), Arguments.of(new int[] {6, 1, 99, 27, 15, 23, 36}, new int[] {1, 6, 15, 23, 27, 36, 99}), Arguments.of(new int[] {6, 1, 27, 15, 23, 27, 36, 23}, new int[] {1, 6, 15, 23, 23, 27, 27, 36}), + Arguments.of(new int[] {5, 5, 5, 5, 5}, new int[] {5, 5, 5, 5, 5}), Arguments.of(new int[] {1, 2, 3, 4, 5}, new int[] {1, 2, 3, 4, 5}), Arguments.of(new int[] {5, 4, 3, 2, 1}, new int[] {1, 2, 3, 4, 5})); + } + + @Test + public void testWithNegativeNumbers() { + assertThrows(IllegalArgumentException.class, () -> PigeonholeSort.sort(new int[] {3, 1, 4, 1, 5, -9})); + assertThrows(IllegalArgumentException.class, () -> PigeonholeSort.sort(new int[] {-1})); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/QuickSortTest.java b/src/test/java/com/thealgorithms/sorts/QuickSortTest.java new file mode 100644 index 000000000000..fca57626b75f --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/QuickSortTest.java @@ -0,0 +1,12 @@ +package com.thealgorithms.sorts; + +/** + * @author Akshay Dubey (https://github.com/itsAkshayDubey) + * @see QuickSort + */ +class QuickSortTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new QuickSort(); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/RadixSortTest.java b/src/test/java/com/thealgorithms/sorts/RadixSortTest.java new file mode 100644 index 000000000000..24ab52b199aa --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/RadixSortTest.java @@ -0,0 +1,30 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class RadixSortTest { + @ParameterizedTest + @MethodSource("provideTestCases") + public void test(int[] inputArray, int[] expectedArray) { + assertArrayEquals(RadixSort.sort(inputArray), expectedArray); + } + + private static Stream<Arguments> provideTestCases() { + return Stream.of(Arguments.of(new int[] {170, 45, 75, 90, 802, 24, 2, 66}, new int[] {2, 24, 45, 66, 75, 90, 170, 802}), Arguments.of(new int[] {3, 3, 3, 3}, new int[] {3, 3, 3, 3}), Arguments.of(new int[] {9, 4, 6, 8, 14, 3}, new int[] {3, 4, 6, 8, 9, 14}), + Arguments.of(new int[] {10, 90, 49, 2, 1, 5, 23}, new int[] {1, 2, 5, 10, 23, 49, 90}), Arguments.of(new int[] {1, 3, 4, 2, 7, 8}, new int[] {1, 2, 3, 4, 7, 8}), Arguments.of(new int[] {}, new int[] {}), Arguments.of(new int[] {1}, new int[] {1}), + Arguments.of(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9}, new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9}), Arguments.of(new int[] {9, 8, 7, 6, 5, 4, 3, 2, 1}, new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9}), + Arguments.of(new int[] {1000000000, 999999999, 888888888, 777777777}, new int[] {777777777, 888888888, 999999999, 1000000000}), Arguments.of(new int[] {123, 9, 54321, 123456789, 0}, new int[] {0, 9, 123, 54321, 123456789})); + } + + @Test + public void testWithNegativeNumbers() { + assertThrows(IllegalArgumentException.class, () -> RadixSort.sort(new int[] {3, 1, 4, 1, 5, -9})); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/SelectionSortRecursiveTest.java b/src/test/java/com/thealgorithms/sorts/SelectionSortRecursiveTest.java new file mode 100644 index 000000000000..20dcf249b31a --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/SelectionSortRecursiveTest.java @@ -0,0 +1,12 @@ +package com.thealgorithms.sorts; + +public class SelectionSortRecursiveTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new SelectionSortRecursive(); + } + + protected int getGeneratedArraySize() { + return 5000; + } +} diff --git a/src/test/java/com/thealgorithms/sorts/SelectionSortTest.java b/src/test/java/com/thealgorithms/sorts/SelectionSortTest.java new file mode 100644 index 000000000000..fab4be1ad01b --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/SelectionSortTest.java @@ -0,0 +1,8 @@ +package com.thealgorithms.sorts; + +class SelectionSortTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new SelectionSort(); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/ShellSortTest.java b/src/test/java/com/thealgorithms/sorts/ShellSortTest.java new file mode 100644 index 000000000000..b41f2c2e863b --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/ShellSortTest.java @@ -0,0 +1,8 @@ +package com.thealgorithms.sorts; + +public class ShellSortTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new ShellSort(); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/SimpleSortTest.java b/src/test/java/com/thealgorithms/sorts/SimpleSortTest.java new file mode 100644 index 000000000000..887b314e46ff --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/SimpleSortTest.java @@ -0,0 +1,8 @@ +package com.thealgorithms.sorts; + +public class SimpleSortTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new SimpleSort(); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/SlowSortTest.java b/src/test/java/com/thealgorithms/sorts/SlowSortTest.java new file mode 100644 index 000000000000..d4d9eaa1c275 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/SlowSortTest.java @@ -0,0 +1,79 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +/** + * @author Rebecca Velez (https://github.com/rebeccavelez) + * @see SlowSort + */ + +public class SlowSortTest { + + private SlowSort slowSort = new SlowSort(); + + @Test + public void slowSortEmptyArray() { + Integer[] inputArray = {}; + Integer[] outputArray = slowSort.sort(inputArray); + Integer[] expectedOutput = {}; + assertArrayEquals(outputArray, expectedOutput); + } + + @Test + public void slowSortSingleIntegerElementArray() { + Integer[] inputArray = {5}; + Integer[] outputArray = slowSort.sort(inputArray); + Integer[] expectedOutput = {5}; + assertArrayEquals(outputArray, expectedOutput); + } + + @Test + public void slowSortSingleStringElementArray() { + String[] inputArray = {"k"}; + String[] outputArray = slowSort.sort(inputArray); + String[] expectedOutput = {"k"}; + assertArrayEquals(outputArray, expectedOutput); + } + + @Test + public void slowSortIntegerArray() { + Integer[] inputArray = {8, 84, 53, -683, 953, 64, 2, 202, 98, -10}; + Integer[] outputArray = slowSort.sort(inputArray); + Integer[] expectedOutput = {-683, -10, 2, 8, 53, 64, 84, 98, 202, 953}; + assertArrayEquals(outputArray, expectedOutput); + } + + @Test + public void slowSortDuplicateIntegerArray() { + Integer[] inputArray = {8, 84, 8, -2, 953, 64, 2, 953, 98}; + Integer[] outputArray = slowSort.sort(inputArray); + Integer[] expectedOutput = {-2, 2, 8, 8, 64, 84, 98, 953, 953}; + assertArrayEquals(outputArray, expectedOutput); + } + + @Test + public void slowSortStringArray() { + String[] inputArray = {"g", "d", "a", "b", "f", "c", "e"}; + String[] outputArray = slowSort.sort(inputArray); + String[] expectedOutput = {"a", "b", "c", "d", "e", "f", "g"}; + assertArrayEquals(outputArray, expectedOutput); + } + + @Test + public void slowSortDuplicateStringArray() { + String[] inputArray = {"g", "d", "a", "g", "b", "f", "d", "c", "e"}; + String[] outputArray = slowSort.sort(inputArray); + String[] expectedOutput = {"a", "b", "c", "d", "d", "e", "f", "g", "g"}; + assertArrayEquals(outputArray, expectedOutput); + } + + @Test + public void slowSortStringSymbolArray() { + String[] inputArray = {"cbf", "auk", "ó", "(b", "a", ")", "au", "á", "cba", "auk", "(a", "bhy", "cba"}; + String[] outputArray = slowSort.sort(inputArray); + String[] expectedOutput = {"(a", "(b", ")", "a", "au", "auk", "auk", "bhy", "cba", "cba", "cbf", "á", "ó"}; + assertArrayEquals(outputArray, expectedOutput); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/SortUtilsRandomGeneratorTest.java b/src/test/java/com/thealgorithms/sorts/SortUtilsRandomGeneratorTest.java new file mode 100644 index 000000000000..16571b3b9d24 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/SortUtilsRandomGeneratorTest.java @@ -0,0 +1,31 @@ +package com.thealgorithms.sorts; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.RepeatedTest; +import org.junit.jupiter.api.Test; + +class SortUtilsRandomGeneratorTest { + + @RepeatedTest(1000) + void generateArray() { + int size = 1_000; + Double[] doubles = SortUtilsRandomGenerator.generateArray(size); + assertThat(doubles).hasSize(size); + assertThat(doubles).doesNotContainNull(); + } + + @Test + void generateArrayEmpty() { + int size = 0; + Double[] doubles = SortUtilsRandomGenerator.generateArray(size); + assertThat(doubles).hasSize(size); + } + + @RepeatedTest(1000) + void generateDouble() { + Double randomDouble = SortUtilsRandomGenerator.generateDouble(); + assertThat(randomDouble).isBetween(0.0, 1.0); + assertThat(randomDouble).isNotEqualTo(1.0); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/SortUtilsTest.java b/src/test/java/com/thealgorithms/sorts/SortUtilsTest.java new file mode 100644 index 000000000000..ac654148c967 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/SortUtilsTest.java @@ -0,0 +1,94 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class SortUtilsTest { + + @Test + void isSortedEmptyArray() { + Double[] emptyArray = {}; + assertTrue(SortUtils.isSorted(emptyArray)); + } + + @Test + void isSortedWithSingleElement() { + Double[] singleElementArray = {1.0}; + assertTrue(SortUtils.isSorted(singleElementArray)); + } + + @Test + void isSortedArrayTrue() { + Integer[] array = {1, 1, 2, 3, 5, 8, 11}; + assertTrue(SortUtils.isSorted(array)); + + Integer[] identicalArray = {1, 1, 1, 1, 1}; + assertTrue(SortUtils.isSorted(identicalArray)); + + Double[] doubles = {-15.123, -15.111, 0.0, 0.12, 0.15}; + assertTrue(SortUtils.isSorted(doubles)); + } + + @Test + void isSortedArrayFalse() { + Double[] array = {1.0, 3.0, -0.15}; + assertFalse(SortUtils.isSorted(array)); + + Integer[] array2 = {14, 15, 16, 1}; + assertFalse(SortUtils.isSorted(array2)); + + Integer[] array3 = {5, 4, 3, 2, 1}; + assertFalse(SortUtils.isSorted(array3)); + } + + @Test + void isSortedListTrue() { + List<Integer> list = List.of(1, 1, 2, 3, 5, 8, 11); + assertTrue(SortUtils.isSorted(list)); + + List<Integer> identicalList = List.of(1, 1, 1, 1, 1); + assertTrue(SortUtils.isSorted(identicalList)); + + List<Double> doubles = List.of(-15.123, -15.111, 0.0, 0.12, 0.15); + assertTrue(SortUtils.isSorted(doubles)); + } + + @Test + void isSortedListFalse() { + List<Double> list = List.of(1.0, 3.0, -0.15); + assertFalse(SortUtils.isSorted(list)); + + List<Integer> array2 = List.of(14, 15, 16, 1); + assertFalse(SortUtils.isSorted(array2)); + + List<Integer> array3 = List.of(5, 4, 3, 2, 1); + assertFalse(SortUtils.isSorted(array3)); + } + + @ParameterizedTest + @MethodSource("provideArraysForSwap") + public <T> void testSwap(T[] array, int i, int j, T[] expected) { + SortUtils.swap(array, i, j); + assertArrayEquals(expected, array); + } + + @ParameterizedTest + @MethodSource("provideArraysForSwap") + public <T> void testSwapFlippedIndices(T[] array, int i, int j, T[] expected) { + SortUtils.swap(array, j, i); + assertArrayEquals(expected, array); + } + + private static Stream<Arguments> provideArraysForSwap() { + return Stream.of(Arguments.of(new Integer[] {1, 2, 3, 4}, 1, 2, new Integer[] {1, 3, 2, 4}), Arguments.of(new Integer[] {1, 2, 3, 4}, 0, 3, new Integer[] {4, 2, 3, 1}), Arguments.of(new Integer[] {1, 2, 3, 4}, 2, 2, new Integer[] {1, 2, 3, 4}), + Arguments.of(new String[] {"a", "b", "c", "d"}, 0, 3, new String[] {"d", "b", "c", "a"}), Arguments.of(new String[] {null, "b", "c", null}, 0, 3, new String[] {null, "b", "c", null})); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/SortingAlgorithmTest.java b/src/test/java/com/thealgorithms/sorts/SortingAlgorithmTest.java new file mode 100644 index 000000000000..43de55018071 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/SortingAlgorithmTest.java @@ -0,0 +1,365 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertIterableEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import org.junit.jupiter.api.Test; + +public abstract class SortingAlgorithmTest { + abstract SortAlgorithm getSortAlgorithm(); + + protected int getGeneratedArraySize() { + return 10_000; + } + + @Test + void shouldAcceptWhenEmptyArrayIsPassed() { + Integer[] array = new Integer[] {}; + Integer[] expected = new Integer[] {}; + + Integer[] sorted = getSortAlgorithm().sort(array); + + assertArrayEquals(expected, sorted); + } + + @Test + void shouldAcceptWhenEmptyListIsPassed() { + List<Integer> list = new ArrayList<>(); + List<Integer> expected = new ArrayList<>(); + + List<Integer> sorted = getSortAlgorithm().sort(list); + + assertIterableEquals(expected, sorted); + } + + @Test + void shouldAcceptWhenSingleValuedArrayIsPassed() { + Integer[] array = new Integer[] {2}; + Integer[] expected = new Integer[] {2}; + + Integer[] sorted = getSortAlgorithm().sort(array); + + assertArrayEquals(expected, sorted); + } + + @Test + void shouldAcceptWhenSingleValuedListIsPassed() { + List<Integer> list = List.of(2); + List<Integer> expected = List.of(2); + + List<Integer> sorted = getSortAlgorithm().sort(list); + + assertIterableEquals(expected, sorted); + } + + @Test + void shouldAcceptWhenListWithAllPositiveValuesIsPassed() { + Integer[] array = new Integer[] {60, 7, 55, 9, 999, 3}; + Integer[] expected = new Integer[] {3, 7, 9, 55, 60, 999}; + + Integer[] sorted = getSortAlgorithm().sort(array); + + assertArrayEquals(expected, sorted); + } + + @Test + void shouldAcceptWhenArrayWithAllPositiveValuesIsPassed() { + List<Integer> list = List.of(60, 7, 55, 9, 999, 3); + List<Integer> expected = List.of(3, 7, 9, 55, 60, 999); + + List<Integer> sorted = getSortAlgorithm().sort(list); + + assertIterableEquals(expected, sorted); + } + + @Test + void shouldAcceptWhenArrayWithAllNegativeValuesIsPassed() { + Integer[] array = new Integer[] {-60, -7, -55, -9, -999, -3}; + Integer[] expected = new Integer[] {-999, -60, -55, -9, -7, -3}; + + Integer[] sorted = getSortAlgorithm().sort(array); + + assertArrayEquals(expected, sorted); + } + + @Test + void shouldAcceptWhenListWithAllNegativeValuesIsPassed() { + List<Integer> list = List.of(-60, -7, -55, -9, -999, -3); + List<Integer> expected = List.of(-999, -60, -55, -9, -7, -3); + + List<Integer> sorted = getSortAlgorithm().sort(list); + + assertIterableEquals(expected, sorted); + } + + @Test + void shouldAcceptWhenArrayWithRealNumberValuesIsPassed() { + Integer[] array = new Integer[] {60, -7, 55, 9, -999, -3}; + Integer[] expected = new Integer[] {-999, -7, -3, 9, 55, 60}; + + Integer[] sorted = getSortAlgorithm().sort(array); + + assertArrayEquals(expected, sorted); + } + + @Test + void shouldAcceptWhenListWithRealNumberValuesIsPassed() { + List<Integer> list = List.of(60, -7, 55, 9, -999, -3); + List<Integer> expected = List.of(-999, -7, -3, 9, 55, 60); + + List<Integer> sorted = getSortAlgorithm().sort(list); + + assertIterableEquals(expected, sorted); + } + + @Test + void shouldAcceptWhenArrayWithDuplicateValueIsPassed() { + Integer[] array = new Integer[] {60, 7, 55, 55, 999, 3}; + Integer[] expected = new Integer[] {3, 7, 55, 55, 60, 999}; + + Integer[] sorted = getSortAlgorithm().sort(array); + + assertArrayEquals(expected, sorted); + } + + @Test + void shouldAcceptWhenListWithDuplicateValueIsPassed() { + List<Integer> list = List.of(60, 7, 55, 55, 999, 3); + List<Integer> expected = List.of(3, 7, 55, 55, 60, 999); + + List<Integer> sorted = getSortAlgorithm().sort(list); + + assertIterableEquals(expected, sorted); + } + + @Test + void shouldAcceptWhenStringValueArrayIsPassed() { + String[] array = {"z", "a", "x", "b", "y"}; + String[] expected = {"a", "b", "x", "y", "z"}; + + String[] sorted = getSortAlgorithm().sort(array); + + assertArrayEquals(expected, sorted); + } + + @Test + void shouldAcceptWhenStringValueListIsPassed() { + List<String> list = List.of("z", "a", "x", "b", "y"); + List<String> expected = List.of("a", "b", "x", "y", "z"); + + List<String> sorted = getSortAlgorithm().sort(list); + + assertIterableEquals(expected, sorted); + } + + @Test + void shouldAcceptWhenRandomArrayIsPassed() { + int randomSize = SortUtilsRandomGenerator.generateInt(getGeneratedArraySize()); + Double[] array = SortUtilsRandomGenerator.generateArray(randomSize); + Double[] sorted = getSortAlgorithm().sort(array); + assertTrue(SortUtils.isSorted(sorted)); + } + + @Test + void shouldAcceptWhenRandomListIsPassed() { + int randomSize = SortUtilsRandomGenerator.generateInt(getGeneratedArraySize()); + Double[] array = SortUtilsRandomGenerator.generateArray(randomSize); + List<Double> list = List.of(array); + List<Double> sorted = getSortAlgorithm().sort(list); + assertTrue(SortUtils.isSorted(sorted)); + } + + @Test + public void shouldAcceptWhenArrayWithAllIdenticalValuesIsPassed() { + Integer[] array = {1, 1, 1, 1}; + Integer[] sortedArray = getSortAlgorithm().sort(array); + assertArrayEquals(new Integer[] {1, 1, 1, 1}, sortedArray); + } + + @Test + public void shouldAcceptWhenListWithAllIdenticalValuesIsPassed() { + List<Integer> list = Arrays.asList(1, 1, 1, 1); + List<Integer> sortedList = getSortAlgorithm().sort(list); + assertEquals(Arrays.asList(1, 1, 1, 1), sortedList); + } + + @Test + public void shouldAcceptWhenArrayWithMixedPositiveAndNegativeValuesIsPassed() { + Integer[] array = {-1, 3, -2, 5, 0}; + Integer[] sortedArray = getSortAlgorithm().sort(array); + assertArrayEquals(new Integer[] {-2, -1, 0, 3, 5}, sortedArray); + } + + @Test + public void shouldAcceptWhenListWithMixedPositiveAndNegativeValuesIsPassed() { + List<Integer> list = Arrays.asList(-1, 3, -2, 5, 0); + List<Integer> sortedList = getSortAlgorithm().sort(list); + assertEquals(Arrays.asList(-2, -1, 0, 3, 5), sortedList); + } + + @Test + public void shouldAcceptWhenArrayWithLargeNumbersIsPassed() { + Long[] array = {10000000000L, 9999999999L, 10000000001L}; + Long[] sortedArray = getSortAlgorithm().sort(array); + assertArrayEquals(new Long[] {9999999999L, 10000000000L, 10000000001L}, sortedArray); + } + + @Test + public void shouldAcceptWhenListWithLargeNumbersIsPassed() { + List<Long> list = Arrays.asList(10000000000L, 9999999999L, 10000000001L); + List<Long> sortedList = getSortAlgorithm().sort(list); + assertEquals(Arrays.asList(9999999999L, 10000000000L, 10000000001L), sortedList); + } + + @Test + public void shouldAcceptWhenArrayWithMaxIntegerValuesIsPassed() { + Integer[] array = {Integer.MAX_VALUE, Integer.MIN_VALUE, 0}; + Integer[] sortedArray = getSortAlgorithm().sort(array); + assertArrayEquals(new Integer[] {Integer.MIN_VALUE, 0, Integer.MAX_VALUE}, sortedArray); + } + + @Test + public void shouldAcceptWhenListWithMaxIntegerValuesIsPassed() { + List<Integer> list = Arrays.asList(Integer.MAX_VALUE, Integer.MIN_VALUE, 0); + List<Integer> sortedList = getSortAlgorithm().sort(list); + assertEquals(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE), sortedList); + } + + @Test + public void shouldAcceptWhenArrayWithMinIntegerValuesIsPassed() { + Integer[] array = {Integer.MIN_VALUE, Integer.MAX_VALUE, 0}; + Integer[] sortedArray = getSortAlgorithm().sort(array); + assertArrayEquals(new Integer[] {Integer.MIN_VALUE, 0, Integer.MAX_VALUE}, sortedArray); + } + + @Test + public void shouldAcceptWhenListWithMinIntegerValuesIsPassed() { + List<Integer> list = Arrays.asList(Integer.MIN_VALUE, Integer.MAX_VALUE, 0); + List<Integer> sortedList = getSortAlgorithm().sort(list); + assertEquals(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE), sortedList); + } + + @Test + public void shouldAcceptWhenArrayWithSpecialCharactersIsPassed() { + String[] array = {"!", "@", "#", "$"}; + String[] sortedArray = getSortAlgorithm().sort(array); + assertArrayEquals(new String[] {"!", "#", "$", "@"}, sortedArray); + } + + @Test + public void shouldAcceptWhenListWithSpecialCharactersIsPassed() { + List<String> list = Arrays.asList("!", "@", "#", "$"); + List<String> sortedList = getSortAlgorithm().sort(list); + assertEquals(Arrays.asList("!", "#", "$", "@"), sortedList); + } + + @Test + public void shouldAcceptWhenArrayWithMixedCaseStringsIsPassed() { + String[] array = {"apple", "Banana", "cherry", "Date"}; + String[] sortedArray = getSortAlgorithm().sort(array); + assertArrayEquals(new String[] {"Banana", "Date", "apple", "cherry"}, sortedArray); + } + + @Test + public void shouldAcceptWhenListWithMixedCaseStringsIsPassed() { + List<String> list = Arrays.asList("apple", "Banana", "cherry", "Date"); + List<String> sortedList = getSortAlgorithm().sort(list); + assertEquals(Arrays.asList("Banana", "Date", "apple", "cherry"), sortedList); + } + + @Test + public void shouldHandleArrayWithNullValues() { + Integer[] array = {3, null, 2, null, 1}; + org.junit.jupiter.api.Assertions.assertThrows(NullPointerException.class, () -> getSortAlgorithm().sort(array)); + } + + @Test + public void shouldHandleListWithNullValues() { + List<Integer> list = Arrays.asList(3, null, 2, null, 1); + org.junit.jupiter.api.Assertions.assertThrows(NullPointerException.class, () -> getSortAlgorithm().sort(list)); + } + + static class CustomObject implements Comparable<CustomObject> { + int value; + + CustomObject(int value) { + this.value = value; + } + + @Override + public int compareTo(CustomObject o) { + return Integer.compare(this.value, o.value); + } + + @Override + public String toString() { + return "CustomObject{" + + "value=" + value + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CustomObject that = (CustomObject) o; + return value == that.value; + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + } + + @Test + public void shouldHandleArrayOfCustomObjects() { + CustomObject[] array = {new CustomObject(3), new CustomObject(1), new CustomObject(2)}; + CustomObject[] sortedArray = getSortAlgorithm().sort(array); + assertArrayEquals(new CustomObject[] {new CustomObject(1), new CustomObject(2), new CustomObject(3)}, sortedArray); + } + + @Test + public void shouldHandleListOfCustomObjects() { + List<CustomObject> list = Arrays.asList(new CustomObject(3), new CustomObject(1), new CustomObject(2)); + List<CustomObject> sortedList = getSortAlgorithm().sort(list); + assertEquals(Arrays.asList(new CustomObject(1), new CustomObject(2), new CustomObject(3)), sortedList); + } + + @Test + public void shouldHandleArrayOfFloatingPointNumbers() { + Double[] array = {3.3, 2.2, 1.1, Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}; + Double[] sortedArray = getSortAlgorithm().sort(array); + assertArrayEquals(new Double[] {Double.NEGATIVE_INFINITY, 1.1, 2.2, 3.3, Double.POSITIVE_INFINITY, Double.NaN}, sortedArray); + } + + @Test + public void shouldHandleListOfFloatingPointNumbers() { + List<Double> list = Arrays.asList(3.3, 2.2, 1.1, Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY); + List<Double> sortedList = getSortAlgorithm().sort(list); + assertEquals(Arrays.asList(Double.NEGATIVE_INFINITY, 1.1, 2.2, 3.3, Double.POSITIVE_INFINITY, Double.NaN), sortedList); + } + + @Test + public void shouldHandleArrayWithEmptyStrings() { + String[] array = {"apple", "", "banana", ""}; + String[] sortedArray = getSortAlgorithm().sort(array); + assertArrayEquals(new String[] {"", "", "apple", "banana"}, sortedArray); + } + + @Test + public void shouldHandleListWithEmptyStrings() { + List<String> list = Arrays.asList("apple", "", "banana", ""); + List<String> sortedList = getSortAlgorithm().sort(list); + assertEquals(Arrays.asList("", "", "apple", "banana"), sortedList); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/SpreadSortTest.java b/src/test/java/com/thealgorithms/sorts/SpreadSortTest.java new file mode 100644 index 000000000000..a4992a02abfa --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/SpreadSortTest.java @@ -0,0 +1,37 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.api.function.Executable; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; + +public class SpreadSortTest extends SortingAlgorithmTest { + + protected int getGeneratedArraySize() { + return 1000; + } + + @Override + SortAlgorithm getSortAlgorithm() { + return new SpreadSort(); + } + + static class ConstructorArgumentsProvider implements ArgumentsProvider { + @Override + public Stream<? extends Arguments> provideArguments(org.junit.jupiter.api.extension.ExtensionContext context) { + return Stream.of(Arguments.of(0, 16, 2, IllegalArgumentException.class), Arguments.of(16, 0, 2, IllegalArgumentException.class), Arguments.of(16, 16, 0, IllegalArgumentException.class), Arguments.of(1001, 16, 2, IllegalArgumentException.class), + Arguments.of(16, 1001, 2, IllegalArgumentException.class), Arguments.of(16, 16, 101, IllegalArgumentException.class)); + } + } + + @ParameterizedTest + @ArgumentsSource(ConstructorArgumentsProvider.class) + void testConstructor(int insertionSortThreshold, int initialBucketCapacity, int minBuckets, Class<Exception> expectedException) { + Executable executable = () -> new SpreadSort(insertionSortThreshold, initialBucketCapacity, minBuckets); + assertThrows(expectedException, executable); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/StalinSortTest.java b/src/test/java/com/thealgorithms/sorts/StalinSortTest.java new file mode 100644 index 000000000000..ee9c61379527 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/StalinSortTest.java @@ -0,0 +1,53 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +public class StalinSortTest { + + @Test + public void testSortIntegers() { + StalinSort stalinSort = new StalinSort(); + Integer[] input = {4, 23, 6, 78, 1, 54, 231, 9, 12}; + Integer[] expected = {4, 23, 78, 231}; + Integer[] result = stalinSort.sort(input); + assertArrayEquals(expected, result); + } + + @Test + public void testSortStrings() { + StalinSort stalinSort = new StalinSort(); + String[] input = {"c", "a", "e", "b", "d"}; + String[] expected = {"c", "e"}; + String[] result = stalinSort.sort(input); + assertArrayEquals(expected, result); + } + + @Test + public void testSortWithDuplicates() { + StalinSort stalinSort = new StalinSort(); + Integer[] input = {1, 3, 2, 2, 5, 4}; + Integer[] expected = {1, 3, 5}; + Integer[] result = stalinSort.sort(input); + assertArrayEquals(expected, result); + } + + @Test + public void testSortEmptyArray() { + StalinSort stalinSort = new StalinSort(); + Integer[] input = {}; + Integer[] expected = {}; + Integer[] result = stalinSort.sort(input); + assertArrayEquals(expected, result); + } + + @Test + public void testSortSingleElement() { + StalinSort stalinSort = new StalinSort(); + Integer[] input = {42}; + Integer[] expected = {42}; + Integer[] result = stalinSort.sort(input); + assertArrayEquals(expected, result); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/StoogeSortTest.java b/src/test/java/com/thealgorithms/sorts/StoogeSortTest.java new file mode 100644 index 000000000000..e230ac2ac590 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/StoogeSortTest.java @@ -0,0 +1,12 @@ +package com.thealgorithms.sorts; + +public class StoogeSortTest extends SortingAlgorithmTest { + protected int getGeneratedArraySize() { + return 1000; + } + + @Override + SortAlgorithm getSortAlgorithm() { + return new StoogeSort(); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/StrandSortTest.java b/src/test/java/com/thealgorithms/sorts/StrandSortTest.java new file mode 100644 index 000000000000..a7250acf9bec --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/StrandSortTest.java @@ -0,0 +1,8 @@ +package com.thealgorithms.sorts; + +class StrandSortTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new StrandSort(); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/SwapSortTest.java b/src/test/java/com/thealgorithms/sorts/SwapSortTest.java new file mode 100644 index 000000000000..c1638a385940 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/SwapSortTest.java @@ -0,0 +1,8 @@ +package com.thealgorithms.sorts; + +public class SwapSortTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new SwapSort(); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/TimSortTest.java b/src/test/java/com/thealgorithms/sorts/TimSortTest.java new file mode 100644 index 000000000000..38e29d6eadd3 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/TimSortTest.java @@ -0,0 +1,8 @@ +package com.thealgorithms.sorts; + +class TimSortTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new TimSort(); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/TopologicalSortTest.java b/src/test/java/com/thealgorithms/sorts/TopologicalSortTest.java new file mode 100644 index 000000000000..de115b458fe7 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/TopologicalSortTest.java @@ -0,0 +1,62 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertIterableEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.thealgorithms.sorts.TopologicalSort.Graph; +import java.util.LinkedList; +import org.junit.jupiter.api.Test; + +class TopologicalSortTest { + + @Test + void successTest() { + /* + * Professor Bumstead example DAG. Each directed edge means that garment u must be put on + * before garment v. + * */ + Graph graph = new Graph(); + graph.addEdge("shirt", "tie", "belt"); + graph.addEdge("tie", "jacket"); + graph.addEdge("belt", "jacket"); + graph.addEdge("watch", ""); + graph.addEdge("undershorts", "pants", "shoes"); + graph.addEdge("shoes", ""); + graph.addEdge("socks", "shoes"); + graph.addEdge("jacket", ""); + graph.addEdge("pants", "belt", "shoes"); + LinkedList<String> expected = new LinkedList<>(); + expected.add("socks"); + expected.add("undershorts"); + expected.add("pants"); + expected.add("shoes"); + expected.add("watch"); + expected.add("shirt"); + expected.add("belt"); + expected.add("tie"); + expected.add("jacket"); + assertIterableEquals(expected, TopologicalSort.sort(graph)); + } + + @Test + public void failureTest() { + /* + * Graph example from Geeks For Geeks + * https://www.geeksforgeeks.org/tree-back-edge-and-cross-edges-in-dfs-of-graph/ + * */ + Graph graph = new Graph(); + graph.addEdge("1", "2", "3", "8"); + graph.addEdge("2", "4"); + graph.addEdge("3", "5"); + graph.addEdge("4", "6"); + graph.addEdge("5", "4", "7", "8"); + graph.addEdge("6", "2"); + graph.addEdge("7", ""); + graph.addEdge("8", ""); + Exception exception = assertThrows(RuntimeException.class, () -> TopologicalSort.sort(graph)); + String expected = "This graph contains a cycle. No linear ordering is possible. " + + "Back edge: 6 -> 2"; + assertEquals(exception.getMessage(), expected); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/TreeSortTest.java b/src/test/java/com/thealgorithms/sorts/TreeSortTest.java new file mode 100644 index 000000000000..5fb4c18e953e --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/TreeSortTest.java @@ -0,0 +1,54 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import org.junit.jupiter.api.Test; + +/** + * @author Tabbygray (https://github.com/Tabbygray) + * @see TreeSort + */ + +public class TreeSortTest { + private TreeSort treeSort = new TreeSort(); + + @Test + public void treeSortEmptyArray() { + Integer[] inputArray = {}; + Integer[] outputArray = treeSort.sort(inputArray); + Integer[] expectedOutput = {}; + assertArrayEquals(outputArray, expectedOutput); + } + + @Test + public void treeSortSingleStringElement() { + String[] inputArray = {"Test"}; + String[] outputArray = treeSort.sort(inputArray); + String[] expectedArray = {"Test"}; + assertArrayEquals(outputArray, expectedArray); + } + + @Test + public void treeSortStringArray() { + String[] inputArray = {"F6w9", "l1qz", "dIxH", "larj", "kRzy", "vnNH", "3ftM", "hc4n", "C5Qi", "btGF"}; + String[] outputArray = treeSort.sort(inputArray); + String[] expectedArray = {"3ftM", "C5Qi", "F6w9", "btGF", "dIxH", "hc4n", "kRzy", "l1qz", "larj", "vnNH"}; + assertArrayEquals(outputArray, expectedArray); + } + + @Test + public void treeSortIntegerArray() { + Integer[] inputArray = {-97, -44, -4, -85, -92, 74, 79, -26, 76, -5}; + Integer[] outputArray = treeSort.sort(inputArray); + Integer[] expectedArray = {-97, -92, -85, -44, -26, -5, -4, 74, 76, 79}; + assertArrayEquals(outputArray, expectedArray); + } + + @Test + public void treeSortDoubleArray() { + Double[] inputArray = {0.8047485045, 0.4493112337, 0.8298433723, 0.2691406748, 0.2482782839, 0.5976243420, 0.6746235284, 0.0552623569, 0.3515624123, 0.0536747336}; + Double[] outputArray = treeSort.sort(inputArray); + Double[] expectedArray = {0.0536747336, 0.0552623569, 0.2482782839, 0.2691406748, 0.3515624123, 0.4493112337, 0.5976243420, 0.6746235284, 0.8047485045, 0.8298433723}; + assertArrayEquals(outputArray, expectedArray); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/WaveSortTest.java b/src/test/java/com/thealgorithms/sorts/WaveSortTest.java new file mode 100644 index 000000000000..3bc6fa63c01d --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/WaveSortTest.java @@ -0,0 +1,48 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class WaveSortTest { + @ParameterizedTest + @MethodSource("arraysToWaveSort") + public void waveSortTest(Integer[] array) { + WaveSort waveSort = new WaveSort(); + final var inputHistogram = getHistogram(array); + final var sortedArray = waveSort.sort(array); + assertTrue(waveSort.isWaveSorted(sortedArray)); + final var sortedHistogram = getHistogram(sortedArray); + assertEquals(inputHistogram, sortedHistogram, "Element counts do not match"); + } + + private Map<Integer, Integer> getHistogram(Integer[] array) { + Map<Integer, Integer> histogram = new HashMap<>(); + for (final var element : array) { + histogram.put(element, histogram.getOrDefault(element, 0) + 1); + } + return histogram; + } + + private static Stream<Object[]> arraysToWaveSort() { + return Stream.of(new Object[] {new Integer[] {7, 7, 11, 3, 4, 5, 15}}, new Object[] {new Integer[] {1, 2, 3, 4, 5, 6, 7, 8}}, new Object[] {new Integer[] {8, 7, 6, 5, 4, 3, 2, 1}}, new Object[] {new Integer[] {3, 3, 3, 3}}, new Object[] {new Integer[] {-1, -3, -2, -4, -6, -5}}, + new Object[] {new Integer[] {5, 3, 1, 2, 9, 7, 6, 8, 4, 0}}, new Object[] {new Integer[] {1}}, new Object[] {new Integer[] {2, 1}}, new Object[] {new Integer[] {1, 2}}, new Object[] {new Integer[] {}}, new Object[] {new Integer[] {0, 5, -3, 2, -1, 4, -2, 1, 3}}); + } + + @ParameterizedTest + @MethodSource("waveSortedArrays") + public <T extends Comparable<T>> void testIsWaveSorted(T[] array, boolean expected) { + final WaveSort waveSort = new WaveSort(); + assertEquals(expected, waveSort.isWaveSorted(array)); + } + public static Stream<Object[]> waveSortedArrays() { + return Stream.of(new Object[] {new Integer[] {3, 1, 4, 2, 5}, Boolean.TRUE}, new Object[] {new Integer[] {3, 1, 4, 2}, Boolean.TRUE}, new Object[] {new Integer[] {1, 3, 2, 4, 5}, Boolean.FALSE}, new Object[] {new Integer[] {4, 3, 5, 2, 3, 1, 2}, Boolean.TRUE}, + new Object[] {new Integer[] {10, 90, 49, 2, 1, 5, 23}, Boolean.FALSE}, new Object[] {new Integer[] {}, Boolean.TRUE}, new Object[] {new Integer[] {1}, Boolean.TRUE}, new Object[] {new Integer[] {2, 1}, Boolean.TRUE}, new Object[] {new Integer[] {4, 3, 2, 5}, Boolean.FALSE}, + new Object[] {new Double[] {4.0, 3.0, 5.1, 2.1, 3.3, 1.1, 2.2}, Boolean.TRUE}, new Object[] {new Double[] {10.1, 2.0, 2.0}, Boolean.TRUE}, new Object[] {new String[] {"a", "b", "c", "d"}, Boolean.FALSE}, new Object[] {new String[] {"b", "a", "b", "a", "b"}, Boolean.TRUE}); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/WiggleSortTest.java b/src/test/java/com/thealgorithms/sorts/WiggleSortTest.java new file mode 100644 index 000000000000..c5d57d63cf38 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/WiggleSortTest.java @@ -0,0 +1,73 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.util.Arrays; +import org.junit.jupiter.api.Test; + +public class WiggleSortTest { + + @Test + void wiggleTestNumbersEven() { + WiggleSort wiggleSort = new WiggleSort(); + Integer[] values = {1, 2, 3, 4}; + Integer[] result = {1, 4, 2, 3}; + wiggleSort.sort(values); + assertArrayEquals(values, result); + } + + @Test + void wiggleTestNumbersOdd() { + WiggleSort wiggleSort = new WiggleSort(); + Integer[] values = {1, 2, 3, 4, 5}; + Integer[] result = {3, 5, 1, 4, 2}; + wiggleSort.sort(values); + assertArrayEquals(values, result); + } + + @Test + void wiggleTestNumbersOddDuplicates() { + WiggleSort wiggleSort = new WiggleSort(); + Integer[] values = {7, 2, 2, 2, 5}; + Integer[] result = {2, 7, 2, 5, 2}; + wiggleSort.sort(values); + assertArrayEquals(values, result); + } + + @Test + void wiggleTestNumbersOddMultipleDuplicates() { + WiggleSort wiggleSort = new WiggleSort(); + Integer[] values = {1, 1, 2, 2, 5}; + Integer[] result = {2, 5, 1, 2, 1}; + wiggleSort.sort(values); + assertArrayEquals(values, result); + } + + @Test + void wiggleTestNumbersEvenMultipleDuplicates() { + WiggleSort wiggleSort = new WiggleSort(); + Integer[] values = {1, 1, 2, 2, 2, 5}; + Integer[] result = {2, 5, 1, 2, 1, 2}; + wiggleSort.sort(values); + System.out.println(Arrays.toString(values)); + assertArrayEquals(values, result); + } + + @Test + void wiggleTestNumbersEvenDuplicates() { + WiggleSort wiggleSort = new WiggleSort(); + Integer[] values = {1, 2, 4, 4}; + Integer[] result = {1, 4, 2, 4}; + wiggleSort.sort(values); + assertArrayEquals(values, result); + } + + @Test + void wiggleTestStrings() { + WiggleSort wiggleSort = new WiggleSort(); + String[] values = {"a", "b", "d", "c"}; + String[] result = {"a", "d", "b", "c"}; + wiggleSort.sort(values); + assertArrayEquals(values, result); + } +} diff --git a/src/test/java/com/thealgorithms/stacks/BalancedBracketsTest.java b/src/test/java/com/thealgorithms/stacks/BalancedBracketsTest.java new file mode 100644 index 000000000000..33ef18af9c5f --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/BalancedBracketsTest.java @@ -0,0 +1,36 @@ +package com.thealgorithms.stacks; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class BalancedBracketsTest { + + @ParameterizedTest + @CsvSource({"(, )", "[, ]", "{, }", "<, >"}) + void testIsPairedTrue(char opening, char closing) { + assertTrue(BalancedBrackets.isPaired(opening, closing)); + } + + @ParameterizedTest + @CsvSource({"(, ]", "[, )", "{, >", "<, )", "a, b", "!, @"}) + void testIsPairedFalse(char opening, char closing) { + assertFalse(BalancedBrackets.isPaired(opening, closing)); + } + + @ParameterizedTest + @CsvSource({"'[()]{}{[()()]()}', true", "'()', true", "'[]', true", "'{}', true", "'<>', true", "'[{<>}]', true", "'', true", "'[(])', false", "'([)]', false", "'{[<]>}', false", "'[', false", "')', false", "'[{', false", "']', false", "'[a+b]', false", "'a+b', false"}) + void testIsBalanced(String input, boolean expected) { + assertEquals(expected, BalancedBrackets.isBalanced(input)); + } + + @Test + void testIsBalancedNull() { + assertThrows(IllegalArgumentException.class, () -> BalancedBrackets.isBalanced(null)); + } +} diff --git a/src/test/java/com/thealgorithms/stacks/CelebrityFinderTest.java b/src/test/java/com/thealgorithms/stacks/CelebrityFinderTest.java new file mode 100644 index 000000000000..da0217940c2c --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/CelebrityFinderTest.java @@ -0,0 +1,41 @@ +package com.thealgorithms.stacks; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class CelebrityFinderTest { + + @ParameterizedTest + @MethodSource("providePartyMatrices") + public void testCelebrityFinder(int[][] party, int expected) { + assertEquals(expected, CelebrityFinder.findCelebrity(party)); + } + + private static Stream<Arguments> providePartyMatrices() { + return Stream.of( + // Test case 1: Celebrity exists + Arguments.of(new int[][] {{0, 1, 1}, {0, 0, 1}, {0, 0, 0}}, 2), + + // Test case 2: No celebrity + Arguments.of(new int[][] {{0, 1, 0}, {1, 0, 1}, {1, 1, 0}}, -1), + + // Test case 3: Everyone knows each other, no celebrity + Arguments.of(new int[][] {{0, 1, 1}, {1, 0, 1}, {1, 1, 0}}, -1), + + // Test case 4: Single person, they are trivially a celebrity + Arguments.of(new int[][] {{0}}, 0), + + // Test case 5: All know the last person, and they know no one + Arguments.of(new int[][] {{0, 1, 1, 1}, {0, 0, 1, 1}, {0, 0, 0, 1}, {0, 0, 0, 0}}, 3), + + // Test case 6: Larger party with no celebrity + Arguments.of(new int[][] {{0, 1, 1, 0}, {1, 0, 0, 1}, {0, 1, 0, 1}, {1, 1, 1, 0}}, -1), + + // Test case 7: Celebrity at the start of the matrix + Arguments.of(new int[][] {{0, 0, 0}, {1, 0, 1}, {1, 1, 0}}, 0)); + } +} diff --git a/src/test/java/com/thealgorithms/stacks/DecimalToAnyUsingStackTest.java b/src/test/java/com/thealgorithms/stacks/DecimalToAnyUsingStackTest.java new file mode 100644 index 000000000000..4bd9f2af0376 --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/DecimalToAnyUsingStackTest.java @@ -0,0 +1,45 @@ +package com.thealgorithms.stacks; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class DecimalToAnyUsingStackTest { + + @Test + void testConvertToBinary() { + assertEquals("0", DecimalToAnyUsingStack.convert(0, 2)); + assertEquals("11110", DecimalToAnyUsingStack.convert(30, 2)); + } + + @Test + void testConvertToOctal() { + assertEquals("36", DecimalToAnyUsingStack.convert(30, 8)); + } + + @Test + void testConvertToDecimal() { + assertEquals("30", DecimalToAnyUsingStack.convert(30, 10)); + } + + @Test + void testConvertToHexadecimal() { + assertEquals("1E", DecimalToAnyUsingStack.convert(30, 16)); + } + + @Test + void testInvalidRadix() { + IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> DecimalToAnyUsingStack.convert(30, 1)); + assertEquals("Invalid radix: 1. Radix must be between 2 and 16.", thrown.getMessage()); + + thrown = assertThrows(IllegalArgumentException.class, () -> DecimalToAnyUsingStack.convert(30, 17)); + assertEquals("Invalid radix: 17. Radix must be between 2 and 16.", thrown.getMessage()); + } + + @Test + void testNegativeNumber() { + IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> DecimalToAnyUsingStack.convert(-30, 2)); + assertEquals("Number must be non-negative.", thrown.getMessage()); + } +} diff --git a/src/test/java/com/thealgorithms/stacks/DuplicateBracketsTest.java b/src/test/java/com/thealgorithms/stacks/DuplicateBracketsTest.java new file mode 100644 index 000000000000..e2cc6acb8112 --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/DuplicateBracketsTest.java @@ -0,0 +1,29 @@ +package com.thealgorithms.stacks; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class DuplicateBracketsTest { + + @ParameterizedTest + @CsvSource({"'((a + b) + (c + d))'", "'(a + b)'", "'a + b'", "'('", "''"}) + void testInputReturnsFalse(String input) { + assertFalse(DuplicateBrackets.check(input)); + } + + @ParameterizedTest + @CsvSource({"'(a + b) + ((c + d))'", "'((a + b))'", "'((((a + b)))))'"}) + void testInputReturnsTrue(String input) { + assertTrue(DuplicateBrackets.check(input)); + } + + @Test + void testInvalidInput() { + assertThrows(IllegalArgumentException.class, () -> DuplicateBrackets.check(null)); + } +} diff --git a/src/test/java/com/thealgorithms/stacks/GreatestElementConstantTimeTest.java b/src/test/java/com/thealgorithms/stacks/GreatestElementConstantTimeTest.java new file mode 100644 index 000000000000..080592dc68e8 --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/GreatestElementConstantTimeTest.java @@ -0,0 +1,70 @@ +package com.thealgorithms.stacks; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.NoSuchElementException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class GreatestElementConstantTimeTest { + + private GreatestElementConstantTime constantTime; + + @BeforeEach + public void setConstantTime() { + constantTime = new GreatestElementConstantTime(); + } + + @Test + public void testMaxAtFirst() { + constantTime.push(1); + constantTime.push(10); + constantTime.push(20); + constantTime.push(5); + assertEquals(20, constantTime.getMaximumElement()); + } + + @Test + public void testMinTwo() { + constantTime.push(5); + constantTime.push(10); + constantTime.push(20); + constantTime.push(1); + assertEquals(20, constantTime.getMaximumElement()); + constantTime.pop(); + constantTime.pop(); + assertEquals(10, constantTime.getMaximumElement()); + } + + @Test + public void testNullMax() { + constantTime.push(10); + constantTime.push(20); + constantTime.pop(); + constantTime.pop(); + assertNull(constantTime.getMaximumElement()); + } + + @Test + public void testBlankHandle() { + constantTime.push(10); + constantTime.push(1); + constantTime.pop(); + constantTime.pop(); + assertThrows(NoSuchElementException.class, () -> constantTime.pop()); + } + + @Test + public void testPushPopAfterEmpty() { + constantTime.push(10); + constantTime.push(1); + constantTime.pop(); + constantTime.pop(); + constantTime.push(5); + assertEquals(5, constantTime.getMaximumElement()); + constantTime.push(1); + assertEquals(5, constantTime.getMaximumElement()); + } +} diff --git a/src/test/java/com/thealgorithms/stacks/InfixToPostfixTest.java b/src/test/java/com/thealgorithms/stacks/InfixToPostfixTest.java new file mode 100644 index 000000000000..02a08e393a00 --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/InfixToPostfixTest.java @@ -0,0 +1,33 @@ +package com.thealgorithms.stacks; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class InfixToPostfixTest { + + @ParameterizedTest + @MethodSource("provideValidExpressions") + void testValidExpressions(String infix, String expectedPostfix) throws Exception { + assertEquals(expectedPostfix, InfixToPostfix.infix2PostFix(infix)); + } + + private static Stream<Arguments> provideValidExpressions() { + return Stream.of(Arguments.of("3+2", "32+"), Arguments.of("1+(2+3)", "123++"), Arguments.of("(3+4)*5-6", "34+5*6-")); + } + + @ParameterizedTest + @MethodSource("provideInvalidExpressions") + void testInvalidExpressions(String infix, String expectedMessage) { + Exception exception = assertThrows(Exception.class, () -> InfixToPostfix.infix2PostFix(infix)); + assertEquals(expectedMessage, exception.getMessage()); + } + + private static Stream<Arguments> provideInvalidExpressions() { + return Stream.of(Arguments.of("((a+b)*c-d", "invalid expression")); + } +} diff --git a/src/test/java/com/thealgorithms/stacks/InfixToPrefixTest.java b/src/test/java/com/thealgorithms/stacks/InfixToPrefixTest.java new file mode 100644 index 000000000000..91be8a63da62 --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/InfixToPrefixTest.java @@ -0,0 +1,44 @@ +package com.thealgorithms.stacks; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class InfixToPrefixTest { + + @ParameterizedTest + @MethodSource("provideValidExpressions") + void testValidExpressions(String infix, String expectedPrefix) throws Exception { + assertEquals(expectedPrefix, InfixToPrefix.infix2Prefix(infix)); + } + + @Test + void testEmptyString() { + // Assuming that an empty string returns an empty prefix or throws an exception + assertEquals("", InfixToPrefix.infix2Prefix("")); + } + + @Test + void testNullValue() { + // Assuming that a null input throws a NullPointerException + assertThrows(NullPointerException.class, () -> InfixToPrefix.infix2Prefix(null)); + } + + private static Stream<Arguments> provideValidExpressions() { + return Stream.of(Arguments.of("3+2", "+32"), // Simple addition + Arguments.of("1+(2+3)", "+1+23"), // Parentheses + Arguments.of("(3+4)*5-6", "-*+3456"), // Nested operations + Arguments.of("a+b*c", "+a*bc"), // Multiplication precedence + Arguments.of("a+b*c/d", "+a/*bcd"), // Division precedence + Arguments.of("a+b*c-d", "-+a*bcd"), // Subtraction precedence + Arguments.of("a+b*c/d-e", "-+a/*bcde"), // Mixed precedence + Arguments.of("a+b*(c-d)", "+a*b-cd"), // Parentheses precedence + Arguments.of("a+b*(c-d)/e", "+a/*b-cde") // Mixed precedence with parentheses + ); + } +} diff --git a/src/test/java/com/thealgorithms/stacks/LargestRectangleTest.java b/src/test/java/com/thealgorithms/stacks/LargestRectangleTest.java new file mode 100644 index 000000000000..a54372adda0e --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/LargestRectangleTest.java @@ -0,0 +1,77 @@ +package com.thealgorithms.stacks; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class LargestRectangleTest { + + @Test + void testLargestRectangleHistogramWithTypicalCases() { + // Typical case with mixed heights + int[] heights = {2, 1, 5, 6, 2, 3}; + String expected = "10"; + String result = LargestRectangle.largestRectangleHistogram(heights); + assertEquals(expected, result); + + // Another typical case with increasing heights + heights = new int[] {2, 4}; + expected = "4"; + result = LargestRectangle.largestRectangleHistogram(heights); + assertEquals(expected, result); + + // Case with multiple bars of the same height + heights = new int[] {4, 4, 4, 4}; + expected = "16"; + result = LargestRectangle.largestRectangleHistogram(heights); + assertEquals(expected, result); + } + + @Test + void testLargestRectangleHistogramWithEdgeCases() { + // Edge case with an empty array + int[] heights = {}; + String expected = "0"; + String result = LargestRectangle.largestRectangleHistogram(heights); + assertEquals(expected, result); + + // Edge case with a single bar + heights = new int[] {5}; + expected = "5"; + result = LargestRectangle.largestRectangleHistogram(heights); + assertEquals(expected, result); + + // Edge case with all bars of height 0 + heights = new int[] {0, 0, 0}; + expected = "0"; + result = LargestRectangle.largestRectangleHistogram(heights); + assertEquals(expected, result); + } + + @Test + void testLargestRectangleHistogramWithLargeInput() { + // Large input case + int[] heights = new int[10000]; + for (int i = 0; i < heights.length; i++) { + heights[i] = 1; + } + String expected = "10000"; + String result = LargestRectangle.largestRectangleHistogram(heights); + assertEquals(expected, result); + } + + @Test + void testLargestRectangleHistogramWithComplexCases() { + // Complex case with a mix of heights + int[] heights = {6, 2, 5, 4, 5, 1, 6}; + String expected = "12"; + String result = LargestRectangle.largestRectangleHistogram(heights); + assertEquals(expected, result); + + // Case with a peak in the middle + heights = new int[] {2, 1, 5, 6, 2, 3, 1}; + expected = "10"; + result = LargestRectangle.largestRectangleHistogram(heights); + assertEquals(expected, result); + } +} diff --git a/src/test/java/com/thealgorithms/stacks/MinStackUsingSingleStackTest.java b/src/test/java/com/thealgorithms/stacks/MinStackUsingSingleStackTest.java new file mode 100644 index 000000000000..90887294638f --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/MinStackUsingSingleStackTest.java @@ -0,0 +1,66 @@ +package com.thealgorithms.stacks; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.EmptyStackException; +import org.junit.jupiter.api.Test; + +public class MinStackUsingSingleStackTest { + + @Test + public void testBasicOperations() { + MinStackUsingSingleStack minStack = new MinStackUsingSingleStack(); + + minStack.push(3); + minStack.push(5); + assertEquals(3, minStack.getMin(), "Minimum should be 3"); + + minStack.push(2); + minStack.push(1); + assertEquals(1, minStack.getMin(), "Minimum should be 1"); + + minStack.pop(); + assertEquals(2, minStack.getMin(), "Minimum should be 2"); + + minStack.pop(); + assertEquals(3, minStack.getMin(), "Minimum should be 3"); + } + + @Test + public void testTopElement() { + MinStackUsingSingleStack minStack = new MinStackUsingSingleStack(); + + minStack.push(8); + minStack.push(10); + assertEquals(10, minStack.top(), "Top element should be 10"); + + minStack.pop(); + assertEquals(8, minStack.top(), "Top element should be 8"); + } + + @Test + public void testGetMinAfterPops() { + MinStackUsingSingleStack minStack = new MinStackUsingSingleStack(); + + minStack.push(5); + minStack.push(3); + minStack.push(7); + + assertEquals(3, minStack.getMin(), "Minimum should be 3"); + + minStack.pop(); // Popping 7 + assertEquals(3, minStack.getMin(), "Minimum should still be 3"); + + minStack.pop(); // Popping 3 + assertEquals(5, minStack.getMin(), "Minimum should now be 5"); + } + + @Test + public void testEmptyStack() { + MinStackUsingSingleStack minStack = new MinStackUsingSingleStack(); + + assertThrows(EmptyStackException.class, minStack::top, "Should throw exception on top()"); + assertThrows(EmptyStackException.class, minStack::getMin, "Should throw exception on getMin()"); + } +} diff --git a/src/test/java/com/thealgorithms/stacks/MinStackUsingTwoStacksTest.java b/src/test/java/com/thealgorithms/stacks/MinStackUsingTwoStacksTest.java new file mode 100644 index 000000000000..e5deb17e9a8f --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/MinStackUsingTwoStacksTest.java @@ -0,0 +1,38 @@ +package com.thealgorithms.stacks; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class MinStackUsingTwoStacksTest { + + @Test + public void testMinStackOperations() { + MinStackUsingTwoStacks minStack = new MinStackUsingTwoStacks(); + minStack.push(3); + minStack.push(5); + assertEquals(3, minStack.getMin()); + + minStack.push(2); + minStack.push(1); + assertEquals(1, minStack.getMin()); + + minStack.pop(); + assertEquals(2, minStack.getMin()); + } + + @Test + public void testMinStackOperations2() { + MinStackUsingTwoStacks minStack = new MinStackUsingTwoStacks(); + minStack.push(3); + minStack.push(5); + assertEquals(3, minStack.getMin()); + + minStack.push(2); + minStack.push(1); + assertEquals(1, minStack.getMin()); + + minStack.pop(); + assertEquals(2, minStack.getMin()); + } +} diff --git a/src/test/java/com/thealgorithms/stacks/NextGreaterElementTest.java b/src/test/java/com/thealgorithms/stacks/NextGreaterElementTest.java new file mode 100644 index 000000000000..bf015ddc9996 --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/NextGreaterElementTest.java @@ -0,0 +1,29 @@ +package com.thealgorithms.stacks; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class NextGreaterElementTest { + + @ParameterizedTest + @MethodSource("provideTestCases") + void testFindNextGreaterElements(int[] input, int[] expected) { + assertArrayEquals(expected, NextGreaterElement.findNextGreaterElements(input)); + } + + static Stream<Arguments> provideTestCases() { + return Stream.of(Arguments.of(new int[] {2, 7, 3, 5, 4, 6, 8}, new int[] {7, 8, 5, 6, 6, 8, 0}), Arguments.of(new int[] {5}, new int[] {0}), Arguments.of(new int[] {1, 2, 3, 4, 5}, new int[] {2, 3, 4, 5, 0}), Arguments.of(new int[] {5, 4, 3, 2, 1}, new int[] {0, 0, 0, 0, 0}), + Arguments.of(new int[] {4, 5, 2, 25}, new int[] {5, 25, 25, 0}), Arguments.of(new int[] {}, new int[] {})); + } + + @Test + void testNullInput() { + assertThrows(IllegalArgumentException.class, () -> NextGreaterElement.findNextGreaterElements(null)); + } +} diff --git a/src/test/java/com/thealgorithms/stacks/NextSmallerElementTest.java b/src/test/java/com/thealgorithms/stacks/NextSmallerElementTest.java new file mode 100644 index 000000000000..62578baa6632 --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/NextSmallerElementTest.java @@ -0,0 +1,29 @@ +package com.thealgorithms.stacks; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class NextSmallerElementTest { + + @ParameterizedTest + @MethodSource("provideTestCases") + void testFindNextSmallerElements(int[] input, int[] expected) { + assertArrayEquals(expected, NextSmallerElement.findNextSmallerElements(input)); + } + + static Stream<Arguments> provideTestCases() { + return Stream.of(Arguments.of(new int[] {2, 7, 3, 5, 4, 6, 8}, new int[] {-1, 2, 2, 3, 3, 4, 6}), Arguments.of(new int[] {5}, new int[] {-1}), Arguments.of(new int[] {1, 2, 3, 4, 5}, new int[] {-1, 1, 2, 3, 4}), Arguments.of(new int[] {5, 4, 3, 2, 1}, new int[] {-1, -1, -1, -1, -1}), + Arguments.of(new int[] {4, 5, 2, 25}, new int[] {-1, 4, -1, 2}), Arguments.of(new int[] {}, new int[] {})); + } + + @Test + void testFindNextSmallerElementsExceptions() { + assertThrows(IllegalArgumentException.class, () -> NextSmallerElement.findNextSmallerElements(null)); + } +} diff --git a/src/test/java/com/thealgorithms/stacks/PalindromeWithStackTest.java b/src/test/java/com/thealgorithms/stacks/PalindromeWithStackTest.java new file mode 100644 index 000000000000..47b21d5e9e9c --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/PalindromeWithStackTest.java @@ -0,0 +1,77 @@ +package com.thealgorithms.stacks; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class PalindromeWithStackTest { + + private PalindromeWithStack palindromeChecker; + + @BeforeEach + public void setUp() { + palindromeChecker = new PalindromeWithStack(); + } + + @Test + public void testValidOne() { + String testString = "Racecar"; + assertTrue(palindromeChecker.checkPalindrome(testString)); + } + + @Test + public void testInvalidOne() { + String testString = "James"; + assertFalse(palindromeChecker.checkPalindrome(testString)); + } + + @Test + public void testValidTwo() { + String testString = "madam"; + assertTrue(palindromeChecker.checkPalindrome(testString)); + } + + @Test + public void testInvalidTwo() { + String testString = "pantry"; + assertFalse(palindromeChecker.checkPalindrome(testString)); + } + + @Test + public void testValidThree() { + String testString = "RaDar"; + assertTrue(palindromeChecker.checkPalindrome(testString)); + } + + @Test + public void testInvalidThree() { + String testString = "Win"; + assertFalse(palindromeChecker.checkPalindrome(testString)); + } + + @Test + public void testBlankString() { + String testString = ""; + assertTrue(palindromeChecker.checkPalindrome(testString)); + } + + @Test + public void testStringWithNumbers() { + String testString = "12321"; + assertTrue(palindromeChecker.checkPalindrome(testString)); + } + + @Test + public void testStringWithNumbersTwo() { + String testString = "12325"; + assertFalse(palindromeChecker.checkPalindrome(testString)); + } + + @Test + public void testStringWithNumbersAndLetters() { + String testString = "po454op"; + assertTrue(palindromeChecker.checkPalindrome(testString)); + } +} diff --git a/src/test/java/com/thealgorithms/stacks/PostfixEvaluatorTest.java b/src/test/java/com/thealgorithms/stacks/PostfixEvaluatorTest.java new file mode 100644 index 000000000000..882fe644ccd5 --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/PostfixEvaluatorTest.java @@ -0,0 +1,27 @@ +package com.thealgorithms.stacks; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.EmptyStackException; +import org.junit.jupiter.api.Test; + +public class PostfixEvaluatorTest { + + @Test + public void testValidExpressions() { + assertEquals(22, PostfixEvaluator.evaluatePostfix("5 6 + 2 *")); + assertEquals(27, PostfixEvaluator.evaluatePostfix("7 2 + 3 *")); + assertEquals(3, PostfixEvaluator.evaluatePostfix("10 5 / 1 +")); + } + + @Test + public void testInvalidExpression() { + assertThrows(EmptyStackException.class, () -> PostfixEvaluator.evaluatePostfix("5 +")); + } + + @Test + public void testMoreThanOneStackSizeAfterEvaluation() { + assertThrows(IllegalArgumentException.class, () -> PostfixEvaluator.evaluatePostfix("5 6 + 2 * 3")); + } +} diff --git a/src/test/java/com/thealgorithms/stacks/PostfixToInfixTest.java b/src/test/java/com/thealgorithms/stacks/PostfixToInfixTest.java new file mode 100644 index 000000000000..a018865e69ec --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/PostfixToInfixTest.java @@ -0,0 +1,33 @@ +package com.thealgorithms.stacks; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class PostfixToInfixTest { + + @ParameterizedTest + @MethodSource("provideValidPostfixToInfixTestCases") + void testValidPostfixToInfixConversion(String postfix, String expectedInfix) { + assertEquals(expectedInfix, PostfixToInfix.getPostfixToInfix(postfix)); + } + + static Stream<Arguments> provideValidPostfixToInfixTestCases() { + return Stream.of(Arguments.of("A", "A"), Arguments.of("ABC+/", "(A/(B+C))"), Arguments.of("AB+CD+*", "((A+B)*(C+D))"), Arguments.of("AB+C+D+", "(((A+B)+C)+D)"), Arguments.of("ABCDE^*/-", "(A-(B/(C*(D^E))))"), Arguments.of("AB+CD^/E*FGH+-^", "((((A+B)/(C^D))*E)^(F-(G+H)))")); + } + + @Test + void testEmptyPostfixExpression() { + assertEquals("", PostfixToInfix.getPostfixToInfix("")); + } + + @Test + void testNullPostfixExpression() { + assertThrows(NullPointerException.class, () -> PostfixToInfix.getPostfixToInfix(null)); + } +} diff --git a/src/test/java/com/thealgorithms/stacks/PrefixEvaluatorTest.java b/src/test/java/com/thealgorithms/stacks/PrefixEvaluatorTest.java new file mode 100644 index 000000000000..e2faa61955b3 --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/PrefixEvaluatorTest.java @@ -0,0 +1,27 @@ +package com.thealgorithms.stacks; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.EmptyStackException; +import org.junit.jupiter.api.Test; + +public class PrefixEvaluatorTest { + + @Test + public void testValidExpressions() { + assertEquals(10, PrefixEvaluator.evaluatePrefix("+ * 2 3 4")); + assertEquals(5, PrefixEvaluator.evaluatePrefix("- + 7 3 5")); + assertEquals(6, PrefixEvaluator.evaluatePrefix("/ * 3 2 1")); + } + + @Test + public void testInvalidExpression() { + assertThrows(EmptyStackException.class, () -> PrefixEvaluator.evaluatePrefix("+ 3")); + } + + @Test + public void testMoreThanOneStackSizeAfterEvaluation() { + assertThrows(IllegalArgumentException.class, () -> PrefixEvaluator.evaluatePrefix("+ 3 4 5")); + } +} diff --git a/src/test/java/com/thealgorithms/stacks/PrefixToInfixTest.java b/src/test/java/com/thealgorithms/stacks/PrefixToInfixTest.java new file mode 100644 index 000000000000..83fd09e1bbf6 --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/PrefixToInfixTest.java @@ -0,0 +1,44 @@ +package com.thealgorithms.stacks; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class PrefixToInfixTest { + + @ParameterizedTest + @MethodSource("provideValidPrefixToInfixTestCases") + void testValidPrefixToInfixConversion(String prefix, String expectedInfix) { + assertEquals(expectedInfix, PrefixToInfix.getPrefixToInfix(prefix)); + } + + static Stream<Arguments> provideValidPrefixToInfixTestCases() { + return Stream.of(Arguments.of("A", "A"), // Single operand + Arguments.of("+AB", "(A+B)"), // Addition + Arguments.of("*+ABC", "((A+B)*C)"), // Addition and multiplication + Arguments.of("-+A*BCD", "((A+(B*C))-D)"), // Mixed operators + Arguments.of("/-A*BC+DE", "((A-(B*C))/(D+E))"), // Mixed operators + Arguments.of("^+AB*CD", "((A+B)^(C*D))") // Mixed operators + ); + } + + @Test + void testEmptyPrefixExpression() { + assertEquals("", PrefixToInfix.getPrefixToInfix("")); + } + + @Test + void testNullPrefixExpression() { + assertThrows(NullPointerException.class, () -> PrefixToInfix.getPrefixToInfix(null)); + } + + @Test + void testMalformedPrefixExpression() { + assertThrows(ArithmeticException.class, () -> PrefixToInfix.getPrefixToInfix("+ABC")); + } +} diff --git a/src/test/java/com/thealgorithms/stacks/SmallestElementConstantTimeTest.java b/src/test/java/com/thealgorithms/stacks/SmallestElementConstantTimeTest.java new file mode 100644 index 000000000000..b5eda9e8cb46 --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/SmallestElementConstantTimeTest.java @@ -0,0 +1,69 @@ +package com.thealgorithms.stacks; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.NoSuchElementException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class SmallestElementConstantTimeTest { + + private SmallestElementConstantTime sect; + + @BeforeEach + public void setSect() { + sect = new SmallestElementConstantTime(); + } + + @Test + public void testMinAtFirst() { + sect.push(1); + sect.push(10); + sect.push(20); + sect.push(5); + assertEquals(1, sect.getMinimumElement()); + } + + @Test + public void testMinTwo() { + sect.push(5); + sect.push(10); + sect.push(20); + sect.push(1); + assertEquals(1, sect.getMinimumElement()); + sect.pop(); + assertEquals(5, sect.getMinimumElement()); + } + + @Test + public void testNullMin() { + sect.push(10); + sect.push(20); + sect.pop(); + sect.pop(); + assertNull(sect.getMinimumElement()); + } + + @Test + public void testBlankHandle() { + sect.push(10); + sect.push(1); + sect.pop(); + sect.pop(); + assertThrows(NoSuchElementException.class, () -> sect.pop()); + } + + @Test + public void testPushPopAfterEmpty() { + sect.push(10); + sect.push(1); + sect.pop(); + sect.pop(); + sect.push(5); + assertEquals(5, sect.getMinimumElement()); + sect.push(1); + assertEquals(1, sect.getMinimumElement()); + } +} diff --git a/src/test/java/com/thealgorithms/stacks/SortStackTest.java b/src/test/java/com/thealgorithms/stacks/SortStackTest.java new file mode 100644 index 000000000000..b9f2f1b6f106 --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/SortStackTest.java @@ -0,0 +1,77 @@ +package com.thealgorithms.stacks; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Stack; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class SortStackTest { + + private Stack<Integer> stack; + + @BeforeEach + public void setUp() { + stack = new Stack<>(); + } + + @Test + public void testSortEmptyStack() { + SortStack.sortStack(stack); + assertTrue(stack.isEmpty()); // An empty stack should remain empty + } + + @Test + public void testSortSingleElementStack() { + stack.push(10); + SortStack.sortStack(stack); + assertEquals(1, stack.size()); + assertEquals(10, (int) stack.peek()); // Single element should remain unchanged + } + + @Test + public void testSortAlreadySortedStack() { + stack.push(1); + stack.push(2); + stack.push(3); + stack.push(4); + SortStack.sortStack(stack); + + assertEquals(4, stack.size()); + assertEquals(4, (int) stack.pop()); + assertEquals(3, (int) stack.pop()); + assertEquals(2, (int) stack.pop()); + assertEquals(1, (int) stack.pop()); + } + + @Test + public void testSortUnsortedStack() { + stack.push(3); + stack.push(1); + stack.push(4); + stack.push(2); + SortStack.sortStack(stack); + + assertEquals(4, stack.size()); + assertEquals(4, (int) stack.pop()); + assertEquals(3, (int) stack.pop()); + assertEquals(2, (int) stack.pop()); + assertEquals(1, (int) stack.pop()); + } + + @Test + public void testSortWithDuplicateElements() { + stack.push(3); + stack.push(1); + stack.push(3); + stack.push(2); + SortStack.sortStack(stack); + + assertEquals(4, stack.size()); + assertEquals(3, (int) stack.pop()); + assertEquals(3, (int) stack.pop()); + assertEquals(2, (int) stack.pop()); + assertEquals(1, (int) stack.pop()); + } +} diff --git a/src/test/java/com/thealgorithms/stacks/StackPostfixNotationTest.java b/src/test/java/com/thealgorithms/stacks/StackPostfixNotationTest.java new file mode 100644 index 000000000000..97424a8f291d --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/StackPostfixNotationTest.java @@ -0,0 +1,32 @@ +package com.thealgorithms.stacks; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class StackPostfixNotationTest { + + @ParameterizedTest + @MethodSource("provideValidTestCases") + void testEvaluate(String expression, int expected) { + assertEquals(expected, StackPostfixNotation.postfixEvaluate(expression)); + } + + static Stream<Arguments> provideValidTestCases() { + return Stream.of(Arguments.of("1 1 +", 2), Arguments.of("2 3 *", 6), Arguments.of("6 2 /", 3), Arguments.of("-5 -2 -", -3), Arguments.of("5 2 + 3 *", 21), Arguments.of("-5", -5)); + } + + @ParameterizedTest + @MethodSource("provideInvalidTestCases") + void testEvaluateThrowsException(String expression) { + assertThrows(IllegalArgumentException.class, () -> StackPostfixNotation.postfixEvaluate(expression)); + } + + static Stream<Arguments> provideInvalidTestCases() { + return Stream.of(Arguments.of(""), Arguments.of("3 3 3"), Arguments.of("3 3 !"), Arguments.of("+"), Arguments.of("2 +")); + } +} diff --git a/src/test/java/com/thealgorithms/stacks/StackUsingTwoQueuesTest.java b/src/test/java/com/thealgorithms/stacks/StackUsingTwoQueuesTest.java new file mode 100644 index 000000000000..a7e24421e682 --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/StackUsingTwoQueuesTest.java @@ -0,0 +1,70 @@ +package com.thealgorithms.stacks; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class StackUsingTwoQueuesTest { + + private StackUsingTwoQueues stack; + + @BeforeEach + public void setUp() { + stack = new StackUsingTwoQueues(); + } + + @Test + public void testPushAndPeek() { + stack.push(1); + stack.push(2); + stack.push(3); + assertEquals(3, stack.peek()); + } + + @Test + public void testPop() { + stack.push(1); + stack.push(2); + stack.push(3); + assertEquals(3, stack.pop()); + assertEquals(2, stack.pop()); + assertEquals(1, stack.pop()); + } + + @Test + public void testPeek() { + stack.push(10); + stack.push(20); + assertEquals(20, stack.peek()); + stack.pop(); + assertEquals(10, stack.peek()); + } + + @Test + public void testIsEmpty() { + assertTrue(stack.isEmpty()); + stack.push(1); + assertFalse(stack.isEmpty()); + stack.pop(); + assertTrue(stack.isEmpty()); + } + + @Test + public void testSize() { + assertEquals(0, stack.size()); + stack.push(1); + stack.push(2); + assertEquals(2, stack.size()); + stack.pop(); + assertEquals(1, stack.size()); + } + + @Test + public void testPeekEmptyStack() { + assertNull(stack.peek()); + } +} diff --git a/src/test/java/com/thealgorithms/strings/AhoCorasickTest.java b/src/test/java/com/thealgorithms/strings/AhoCorasickTest.java new file mode 100644 index 000000000000..43137b358cf9 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/AhoCorasickTest.java @@ -0,0 +1,121 @@ +/* + * Tests For Aho-Corasick String Matching Algorithm + * + * Author: Prabhat-Kumar-42 + * GitHub: https://github.com/Prabhat-Kumar-42 + */ + +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * This class contains test cases for the Aho-Corasick String Matching Algorithm. + * The Aho-Corasick algorithm is used to efficiently find all occurrences of multiple + * patterns in a given text. + */ +class AhoCorasickTest { + private String[] patterns; // The array of patterns to search for + private String text; // The input text to search within + + /** + * This method sets up the test environment before each test case. + * It initializes the patterns and text to be used for testing. + */ + @BeforeEach + void setUp() { + patterns = new String[] {"ACC", "ATC", "CAT", "GCG", "C", "T"}; + text = "GCATCG"; + } + + /** + * Test searching for multiple patterns in the input text. + * The expected results are defined for each pattern. + */ + @Test + void testSearch() { + // Define the expected results for each pattern + final var expected = Map.of("ACC", new ArrayList<>(List.of()), "ATC", new ArrayList<>(List.of(2)), "CAT", new ArrayList<>(List.of(1)), "GCG", new ArrayList<>(List.of()), "C", new ArrayList<>(List.of(1, 4)), "T", new ArrayList<>(List.of(3))); + assertEquals(expected, AhoCorasick.search(text, patterns)); + } + + /** + * Test searching with an empty pattern array. + * The result should be an empty map. + */ + @Test + void testEmptyPatterns() { + // Define an empty pattern array + final var emptyPatterns = new String[] {}; + assertTrue(AhoCorasick.search(text, emptyPatterns).isEmpty()); + } + + /** + * Test searching for patterns that are not present in the input text. + * The result should be an empty list for each pattern. + */ + @Test + void testPatternNotFound() { + // Define patterns that are not present in the text + final var searchPatterns = new String[] {"XYZ", "123"}; + final var expected = Map.of("XYZ", new ArrayList<Integer>(), "123", new ArrayList<Integer>()); + assertEquals(expected, AhoCorasick.search(text, searchPatterns)); + } + + /** + * Test searching for patterns that start at the beginning of the input text. + * The expected position for each pattern is 0. + */ + @Test + void testPatternAtBeginning() { + // Define patterns that start at the beginning of the text + final var searchPatterns = new String[] {"GC", "GCA", "GCAT"}; + final var expected = Map.of("GC", new ArrayList<>(List.of(0)), "GCA", new ArrayList<>(List.of(0)), "GCAT", new ArrayList<>(List.of(0))); + assertEquals(expected, AhoCorasick.search(text, searchPatterns)); + } + + /** + * Test searching for patterns that end at the end of the input text. + * The expected positions are 4, 3, and 2 for the patterns. + */ + @Test + void testPatternAtEnd() { + // Define patterns that end at the end of the text + final var searchPatterns = new String[] {"CG", "TCG", "ATCG"}; + final var expected = Map.of("CG", new ArrayList<>(List.of(4)), "TCG", new ArrayList<>(List.of(3)), "ATCG", new ArrayList<>(List.of(2))); + assertEquals(expected, AhoCorasick.search(text, searchPatterns)); + } + + /** + * Test searching for patterns with multiple occurrences in the input text. + * The expected sizes are 1 and 1, and the expected positions are 2 and 3 + * for the patterns "AT" and "T" respectively. + */ + @Test + void testMultipleOccurrencesOfPattern() { + // Define patterns with multiple occurrences in the text + final var searchPatterns = new String[] {"AT", "T"}; + final var expected = Map.of("AT", new ArrayList<>(List.of(2)), "T", new ArrayList<>(List.of(3))); + assertEquals(expected, AhoCorasick.search(text, searchPatterns)); + } + + /** + * Test searching for patterns in a case-insensitive manner. + * The search should consider patterns regardless of their case. + */ + @Test + void testCaseInsensitiveSearch() { + // Define patterns with different cases + final var searchPatterns = new String[] {"gca", "aTc", "C"}; + final var expected = Map.of("gca", new ArrayList<Integer>(), "aTc", new ArrayList<Integer>(), "C", new ArrayList<>(Arrays.asList(1, 4))); + assertEquals(expected, AhoCorasick.search(text, searchPatterns)); + } +} diff --git a/src/test/java/com/thealgorithms/strings/AlphabeticalTest.java b/src/test/java/com/thealgorithms/strings/AlphabeticalTest.java new file mode 100644 index 000000000000..083239152ec2 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/AlphabeticalTest.java @@ -0,0 +1,30 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class AlphabeticalTest { + + @Test + public void isAlphabetical() { + // expected to be true + String input1 = "abcdefghijklmno"; + String input2 = "abcdxxxyzzzz"; + String input3 = "fpw"; + + // expected to be false + String input4 = "123a"; + String input5 = "abcABC"; + String input6 = "abcdefghikjlmno"; + + assertTrue(Alphabetical.isAlphabetical(input1)); + assertTrue(Alphabetical.isAlphabetical(input2)); + assertTrue(Alphabetical.isAlphabetical(input3)); + + assertFalse(Alphabetical.isAlphabetical(input4)); + assertFalse(Alphabetical.isAlphabetical(input5)); + assertFalse(Alphabetical.isAlphabetical(input6)); + } +} diff --git a/src/test/java/com/thealgorithms/strings/AnagramsTest.java b/src/test/java/com/thealgorithms/strings/AnagramsTest.java new file mode 100644 index 000000000000..88f6e0bb72ec --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/AnagramsTest.java @@ -0,0 +1,48 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class AnagramsTest { + + record AnagramTestCase(String input1, String input2, boolean expected) { + } + + private static Stream<AnagramTestCase> anagramTestData() { + return Stream.of(new AnagramTestCase("late", "tale", true), new AnagramTestCase("late", "teal", true), new AnagramTestCase("listen", "silent", true), new AnagramTestCase("hello", "olelh", true), new AnagramTestCase("hello", "world", false), new AnagramTestCase("deal", "lead", true), + new AnagramTestCase("binary", "brainy", true), new AnagramTestCase("adobe", "abode", true), new AnagramTestCase("cat", "act", true), new AnagramTestCase("cat", "cut", false)); + } + + @ParameterizedTest + @MethodSource("anagramTestData") + void testApproach1(AnagramTestCase testCase) { + assertEquals(testCase.expected(), Anagrams.approach1(testCase.input1(), testCase.input2())); + } + + @ParameterizedTest + @MethodSource("anagramTestData") + void testApproach2(AnagramTestCase testCase) { + assertEquals(testCase.expected(), Anagrams.approach2(testCase.input1(), testCase.input2())); + } + + @ParameterizedTest + @MethodSource("anagramTestData") + void testApproach3(AnagramTestCase testCase) { + assertEquals(testCase.expected(), Anagrams.approach3(testCase.input1(), testCase.input2())); + } + + @ParameterizedTest + @MethodSource("anagramTestData") + void testApproach4(AnagramTestCase testCase) { + assertEquals(testCase.expected(), Anagrams.approach4(testCase.input1(), testCase.input2())); + } + + @ParameterizedTest + @MethodSource("anagramTestData") + void testApproach5(AnagramTestCase testCase) { + assertEquals(testCase.expected(), Anagrams.approach5(testCase.input1(), testCase.input2())); + } +} diff --git a/src/test/java/com/thealgorithms/strings/CharacterSameTest.java b/src/test/java/com/thealgorithms/strings/CharacterSameTest.java new file mode 100644 index 000000000000..98f822c4d345 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/CharacterSameTest.java @@ -0,0 +1,15 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class CharactersSameTest { + + @ParameterizedTest + @CsvSource({"aaa, true", "abc, false", "'1 1 1 1', false", "111, true", "'', true", "' ', true", "'. ', false", "'a', true", "' ', true", "'ab', false", "'11111', true", "'ababab', false", "' ', true", "'+++', true"}) + void testIsAllCharactersSame(String input, boolean expected) { + assertEquals(CharactersSame.isAllCharactersSame(input), expected); + } +} diff --git a/src/test/java/com/thealgorithms/strings/CheckAnagramsTest.java b/src/test/java/com/thealgorithms/strings/CheckAnagramsTest.java new file mode 100644 index 000000000000..82a75a130ef0 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/CheckAnagramsTest.java @@ -0,0 +1,69 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class CheckAnagramsTest { + private static final String MESSAGE = "Strings must contain only lowercase English letters!"; + + // CHECK METHOD isAnagrams() + @Test + public void testCheckAnagrams() { + String testString1 = "STUDY"; + String testString2 = "DUSTY"; + Assertions.assertTrue(CheckAnagrams.isAnagrams(testString1, testString2)); + } + + @Test + public void testCheckFalseAnagrams() { + String testString1 = "STUDY"; + String testString2 = "random"; + Assertions.assertFalse(CheckAnagrams.isAnagrams(testString1, testString2)); + } + + @Test + public void testCheckSameWordAnagrams() { + String testString1 = "STUDY"; + Assertions.assertTrue(CheckAnagrams.isAnagrams(testString1, testString1)); + } + + @Test + public void testCheckDifferentCasesAnagram() { + String testString1 = "STUDY"; + String testString2 = "dusty"; + Assertions.assertTrue(CheckAnagrams.isAnagrams(testString1, testString2)); + } + + // CHECK METHOD isAnagramsUnicode() + // Below tests work with strings which consist of Unicode symbols & the algorithm is case-sensitive. + @Test + public void testStringAreValidAnagramsCaseSensitive() { + Assertions.assertTrue(CheckAnagrams.isAnagramsUnicode("Silent", "liSten")); + Assertions.assertTrue(CheckAnagrams.isAnagramsUnicode("This is a string", "is This a string")); + } + + @Test + public void testStringAreNotAnagramsCaseSensitive() { + Assertions.assertFalse(CheckAnagrams.isAnagramsUnicode("Silent", "Listen")); + Assertions.assertFalse(CheckAnagrams.isAnagramsUnicode("This is a string", "Is this a string")); + } + + // CHECK METHOD isAnagramsOptimised() + // Below tests work with strings which consist of only lowercase English letters + @Test + public void testOptimisedAlgorithmStringsAreValidAnagrams() { + Assertions.assertTrue(CheckAnagrams.isAnagramsOptimised("silent", "listen")); + Assertions.assertTrue(CheckAnagrams.isAnagramsOptimised("mam", "amm")); + } + + @Test + public void testOptimisedAlgorithmShouldThrowExceptionWhenStringsContainUppercaseLetters() { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> CheckAnagrams.isAnagramsOptimised("Silent", "Listen")); + Assertions.assertEquals(exception.getMessage(), MESSAGE); + + exception = assertThrows(IllegalArgumentException.class, () -> Assertions.assertFalse(CheckAnagrams.isAnagramsOptimised("This is a string", "Is this a string"))); + Assertions.assertEquals(exception.getMessage(), MESSAGE); + } +} diff --git a/src/test/java/com/thealgorithms/strings/CheckVowelsTest.java b/src/test/java/com/thealgorithms/strings/CheckVowelsTest.java new file mode 100644 index 000000000000..ba510aa8e995 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/CheckVowelsTest.java @@ -0,0 +1,15 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class CheckVowelsTest { + + @ParameterizedTest + @CsvSource({"'foo', true", "'bar', true", "'why', false", "'myths', false", "'', false", "'AEIOU', true", "'bcdfghjklmnpqrstvwxyz', false", "'AeIoU', true"}) + void testHasVowels(String input, boolean expected) { + assertEquals(CheckVowels.hasVowels(input), expected); + } +} diff --git a/src/test/java/com/thealgorithms/strings/CountCharTest.java b/src/test/java/com/thealgorithms/strings/CountCharTest.java new file mode 100644 index 000000000000..c84f2d01c2c5 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/CountCharTest.java @@ -0,0 +1,24 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class CountCharTest { + + @ParameterizedTest(name = "\"{0}\" should have {1} non-whitespace characters") + @CsvSource({"'', 0", "' ', 0", "'a', 1", "'abc', 3", "'a b c', 3", "' a b c ', 3", "'\tabc\n', 3", "' a b\tc ', 3", "' 12345 ', 5", "'Hello, World!', 12"}) + @DisplayName("Test countCharacters with various inputs") + void testCountCharacters(String input, int expected) { + assertEquals(expected, CountChar.countCharacters(input)); + } + + @Test + @DisplayName("Test countCharacters with null input") + void testCountCharactersNullInput() { + assertEquals(0, CountChar.countCharacters(null)); + } +} diff --git a/src/test/java/com/thealgorithms/strings/CountWordsTest.java b/src/test/java/com/thealgorithms/strings/CountWordsTest.java new file mode 100644 index 000000000000..a8aca1ae6092 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/CountWordsTest.java @@ -0,0 +1,31 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class CountWordsTest { + + @ParameterizedTest + @MethodSource("wordCountTestCases") + void testWordCount(String input, int expectedCount) { + assertEquals(expectedCount, CountWords.wordCount(input)); + } + + @ParameterizedTest + @MethodSource("secondaryWordCountTestCases") + void testSecondaryWordCount(String input, int expectedCount) { + assertEquals(expectedCount, CountWords.secondaryWordCount(input)); + } + + private static Stream<Arguments> wordCountTestCases() { + return Stream.of(Arguments.of("", 0), Arguments.of(null, 0), Arguments.of("aaaa bbb cccc", 3), Arguments.of("note extra spaces here", 4), Arguments.of(" a b c d e ", 5)); + } + + private static Stream<Arguments> secondaryWordCountTestCases() { + return Stream.of(Arguments.of("", 0), Arguments.of(null, 0), Arguments.of("aaaa bbb cccc", 3), Arguments.of("this-is-one-word!", 1), Arguments.of("What, about, this? Hmmm----strange", 4), Arguments.of("word1 word-2 word-3- w?o,r.d.@!@#$&*()<>4", 4)); + } +} diff --git a/src/test/java/com/thealgorithms/strings/HammingDistanceTest.java b/src/test/java/com/thealgorithms/strings/HammingDistanceTest.java new file mode 100644 index 000000000000..5c0640348404 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/HammingDistanceTest.java @@ -0,0 +1,36 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.MethodSource; + +class HammingDistanceTest { + + @ParameterizedTest + @CsvSource({"'', '', 0", "'java', 'java', 0", "'karolin', 'kathrin', 3", "'kathrin', 'kerstin', 4", "'00000', '11111', 5", "'10101', '10100', 1"}) + void testHammingDistance(String s1, String s2, int expected) { + assertEquals(expected, HammingDistance.calculateHammingDistance(s1, s2)); + } + + @ParameterizedTest + @MethodSource("provideNullInputs") + void testHammingDistanceWithNullInputs(String input1, String input2) { + assertThrows(IllegalArgumentException.class, () -> HammingDistance.calculateHammingDistance(input1, input2)); + } + + private static Stream<Arguments> provideNullInputs() { + return Stream.of(Arguments.of(null, "abc"), Arguments.of("abc", null), Arguments.of(null, null)); + } + + @Test + void testNotEqualStringLengths() { + Exception exception = assertThrows(IllegalArgumentException.class, () -> HammingDistance.calculateHammingDistance("ab", "abc")); + assertEquals("String lengths must be equal", exception.getMessage()); + } +} diff --git a/src/test/java/com/thealgorithms/strings/HorspoolSearchTest.java b/src/test/java/com/thealgorithms/strings/HorspoolSearchTest.java new file mode 100644 index 000000000000..9649a89a3325 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/HorspoolSearchTest.java @@ -0,0 +1,87 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class HorspoolSearchTest { + + @Test + void testFindFirstMatch() { + int index = HorspoolSearch.findFirst("World", "Hello World"); + assertEquals(6, index); + } + + @Test + void testFindFirstNotMatch() { + int index = HorspoolSearch.findFirst("hell", "Hello World"); + assertEquals(-1, index); + } + + @Test + void testFindFirstPatternLongerText() { + int index = HorspoolSearch.findFirst("Hello World!!!", "Hello World"); + assertEquals(-1, index); + } + + @Test + void testFindFirstPatternEmpty() { + int index = HorspoolSearch.findFirst("", "Hello World"); + assertEquals(-1, index); + } + + @Test + void testFindFirstTextEmpty() { + int index = HorspoolSearch.findFirst("Hello", ""); + assertEquals(-1, index); + } + + @Test + void testFindFirstPatternAndTextEmpty() { + int index = HorspoolSearch.findFirst("", ""); + assertEquals(-1, index); + } + + @Test + void testFindFirstSpecialCharacter() { + int index = HorspoolSearch.findFirst("$3**", "Hello $3**$ World"); + assertEquals(6, index); + } + + @Test + void testFindFirstInsensitiveMatch() { + int index = HorspoolSearch.findFirstInsensitive("hello", "Hello World"); + assertEquals(0, index); + } + + @Test + void testFindFirstInsensitiveNotMatch() { + int index = HorspoolSearch.findFirstInsensitive("helo", "Hello World"); + assertEquals(-1, index); + } + + @Test + void testGetLastComparisons() { + HorspoolSearch.findFirst("World", "Hello World"); + int lastSearchNumber = HorspoolSearch.getLastComparisons(); + assertEquals(7, lastSearchNumber); + } + + @Test + void testGetLastComparisonsNotMatch() { + HorspoolSearch.findFirst("Word", "Hello World"); + int lastSearchNumber = HorspoolSearch.getLastComparisons(); + assertEquals(3, lastSearchNumber); + } + + @Test + void testFindFirstPatternNull() { + assertThrows(NullPointerException.class, () -> HorspoolSearch.findFirst(null, "Hello World")); + } + + @Test + void testFindFirstTextNull() { + assertThrows(NullPointerException.class, () -> HorspoolSearch.findFirst("Hello", null)); + } +} diff --git a/src/test/java/com/thealgorithms/strings/IsomorphicTest.java b/src/test/java/com/thealgorithms/strings/IsomorphicTest.java new file mode 100644 index 000000000000..0dac47551868 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/IsomorphicTest.java @@ -0,0 +1,31 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public final class IsomorphicTest { + private IsomorphicTest() { + } + + @Test + public static void main(String[] args) { + String str1 = "abbbbaac"; + String str2 = "kffffkkd"; + + String str3 = "xyxyxy"; + String str4 = "bnbnbn"; + + String str5 = "ghjknnmm"; + String str6 = "wertpopo"; + + String str7 = "aaammmnnn"; + String str8 = "ggghhhbbj"; + + assertTrue(Isomorphic.checkStrings(str1, str2)); + assertTrue(Isomorphic.checkStrings(str3, str4)); + assertFalse(Isomorphic.checkStrings(str5, str6)); + assertFalse(Isomorphic.checkStrings(str7, str8)); + } +} diff --git a/src/test/java/com/thealgorithms/strings/LetterCombinationsOfPhoneNumberTest.java b/src/test/java/com/thealgorithms/strings/LetterCombinationsOfPhoneNumberTest.java new file mode 100644 index 000000000000..dcdb7d5b3392 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/LetterCombinationsOfPhoneNumberTest.java @@ -0,0 +1,37 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class LetterCombinationsOfPhoneNumberTest { + + @ParameterizedTest + @MethodSource("provideTestCases") + public void testLetterCombinationsOfPhoneNumber(int[] numbers, List<String> expectedOutput) { + assertEquals(expectedOutput, LetterCombinationsOfPhoneNumber.getCombinations(numbers)); + } + + @ParameterizedTest + @MethodSource("wrongInputs") + void throwsForWrongInput(int[] numbers) { + assertThrows(IllegalArgumentException.class, () -> LetterCombinationsOfPhoneNumber.getCombinations(numbers)); + } + + private static Stream<Arguments> provideTestCases() { + return Stream.of(Arguments.of(null, List.of("")), Arguments.of(new int[] {}, List.of("")), Arguments.of(new int[] {2}, List.of("a", "b", "c")), Arguments.of(new int[] {2, 3}, List.of("ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf")), + Arguments.of(new int[] {2, 3, 4}, List.of("adg", "adh", "adi", "aeg", "aeh", "aei", "afg", "afh", "afi", "bdg", "bdh", "bdi", "beg", "beh", "bei", "bfg", "bfh", "bfi", "cdg", "cdh", "cdi", "ceg", "ceh", "cei", "cfg", "cfh", "cfi")), + Arguments.of(new int[] {3, 3}, List.of("dd", "de", "df", "ed", "ee", "ef", "fd", "fe", "ff")), Arguments.of(new int[] {8, 4}, List.of("tg", "th", "ti", "ug", "uh", "ui", "vg", "vh", "vi")), Arguments.of(new int[] {2, 0}, List.of("a ", "b ", "c ")), + Arguments.of(new int[] {9, 2}, List.of("wa", "wb", "wc", "xa", "xb", "xc", "ya", "yb", "yc", "za", "zb", "zc")), Arguments.of(new int[] {0}, List.of(" ")), Arguments.of(new int[] {1}, List.of("")), Arguments.of(new int[] {2}, List.of("a", "b", "c")), + Arguments.of(new int[] {1, 2, 0, 4}, List.of("a g", "a h", "a i", "b g", "b h", "b i", "c g", "c h", "c i"))); + } + + private static Stream<Arguments> wrongInputs() { + return Stream.of(Arguments.of(new int[] {-1}), Arguments.of(new int[] {10}), Arguments.of(new int[] {2, 2, -1, 0}), Arguments.of(new int[] {0, 0, 0, 10})); + } +} diff --git a/src/test/java/com/thealgorithms/strings/LongestCommonPrefixTest.java b/src/test/java/com/thealgorithms/strings/LongestCommonPrefixTest.java new file mode 100644 index 000000000000..580a2726d285 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/LongestCommonPrefixTest.java @@ -0,0 +1,73 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class LongestCommonPrefixTest { + + private final LongestCommonPrefix longestCommonPrefix = new LongestCommonPrefix(); + + @Test + public void testCommonPrefix() { + String[] input = {"flower", "flow", "flight"}; + String expected = "fl"; + assertEquals(expected, longestCommonPrefix.longestCommonPrefix(input)); + } + + @Test + public void testNoCommonPrefix() { + String[] input = {"dog", "racecar", "car"}; + String expected = ""; + assertEquals(expected, longestCommonPrefix.longestCommonPrefix(input)); + } + + @Test + public void testEmptyArray() { + String[] input = {}; + String expected = ""; + assertEquals(expected, longestCommonPrefix.longestCommonPrefix(input)); + } + + @Test + public void testNullArray() { + String[] input = null; + String expected = ""; + assertEquals(expected, longestCommonPrefix.longestCommonPrefix(input)); + } + + @Test + public void testSingleString() { + String[] input = {"single"}; + String expected = "single"; + assertEquals(expected, longestCommonPrefix.longestCommonPrefix(input)); + } + + @Test + public void testCommonPrefixWithDifferentLengths() { + String[] input = {"ab", "a"}; + String expected = "a"; + assertEquals(expected, longestCommonPrefix.longestCommonPrefix(input)); + } + + @Test + public void testAllSameStrings() { + String[] input = {"test", "test", "test"}; + String expected = "test"; + assertEquals(expected, longestCommonPrefix.longestCommonPrefix(input)); + } + + @Test + public void testPrefixAtEnd() { + String[] input = {"abcde", "abcfgh", "abcmnop"}; + String expected = "abc"; + assertEquals(expected, longestCommonPrefix.longestCommonPrefix(input)); + } + + @Test + public void testMixedCase() { + String[] input = {"Flower", "flow", "flight"}; + String expected = ""; + assertEquals(expected, longestCommonPrefix.longestCommonPrefix(input)); + } +} diff --git a/src/test/java/com/thealgorithms/strings/LongestNonRepetitiveSubstringTest.java b/src/test/java/com/thealgorithms/strings/LongestNonRepetitiveSubstringTest.java new file mode 100644 index 000000000000..791c4ba3ae32 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/LongestNonRepetitiveSubstringTest.java @@ -0,0 +1,22 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class LongestNonRepetitiveSubstringTest { + + private static Stream<Arguments> provideTestCases() { + return Stream.of(Arguments.of("", 0), Arguments.of("a", 1), Arguments.of("abcde", 5), Arguments.of("aaaaa", 1), Arguments.of("abca", 3), Arguments.of("abcdeabc", 5), Arguments.of("a1b2c3", 6), Arguments.of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890", 62), + Arguments.of("aabb", 2), Arguments.of("abcdefghijabc", 10)); + } + + @ParameterizedTest + @MethodSource("provideTestCases") + void testLengthOfLongestSubstring(String input, int expectedLength) { + assertEquals(expectedLength, LongestNonRepetitiveSubstring.lengthOfLongestSubstring(input)); + } +} diff --git a/src/test/java/com/thealgorithms/strings/LongestPalindromicSubstringTest.java b/src/test/java/com/thealgorithms/strings/LongestPalindromicSubstringTest.java new file mode 100644 index 000000000000..aa13c0f4a474 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/LongestPalindromicSubstringTest.java @@ -0,0 +1,21 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class LongestPalindromicSubstringTest { + + @ParameterizedTest + @MethodSource("provideTestCasesForLongestPalindrome") + void testLongestPalindrome(String input, String expected) { + assertEquals(expected, LongestPalindromicSubstring.longestPalindrome(input)); + } + + private static Stream<Arguments> provideTestCasesForLongestPalindrome() { + return Stream.of(Arguments.of("babad", "bab"), Arguments.of("cbbd", "bb"), Arguments.of("a", "a"), Arguments.of("", ""), Arguments.of("abc", "a"), Arguments.of(null, ""), Arguments.of("aaaaa", "aaaaa")); + } +} diff --git a/src/test/java/com/thealgorithms/strings/LowerTest.java b/src/test/java/com/thealgorithms/strings/LowerTest.java new file mode 100644 index 000000000000..e0abb921a593 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/LowerTest.java @@ -0,0 +1,18 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class LowerTest { + @Test + public void toLowerCase() { + String input1 = "hello world"; + String input2 = "HelLO WoRld"; + String input3 = "HELLO WORLD"; + + assertEquals("hello world", Lower.toLowerCase(input1)); + assertEquals("hello world", Lower.toLowerCase(input2)); + assertEquals("hello world", Lower.toLowerCase(input3)); + } +} diff --git a/src/test/java/com/thealgorithms/strings/ManacherTest.java b/src/test/java/com/thealgorithms/strings/ManacherTest.java new file mode 100644 index 000000000000..dc74df31b866 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/ManacherTest.java @@ -0,0 +1,53 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class ManacherTest { + + @ParameterizedTest + @MethodSource("provideTestCasesForLongestPalindrome") + public void testLongestPalindrome(String input, String expected) { + assertEquals(expected, Manacher.longestPalindrome(input)); + } + + private static Stream<Arguments> provideTestCasesForLongestPalindrome() { + return Stream.of(Arguments.of("abracadabraabcdefggfedcbaabracadabra", "aabcdefggfedcbaa"), Arguments.of("somelongtextwithracecarmiddletext", "racecar"), Arguments.of("bananananananana", "ananananananana"), Arguments.of("qwertydefgfedzxcvbnm", "defgfed"), + Arguments.of("abcdefghijklmnopqrstuvwxyzzyxwvutsrqponmlkjihgfedcba", "abcdefghijklmnopqrstuvwxyzzyxwvutsrqponmlkjihgfedcba")); + } + + @ParameterizedTest + @MethodSource("provideTestCasesForEmptyAndSingle") + public void testEmptyAndSingle(String input, String expected) { + assertEquals(expected, Manacher.longestPalindrome(input)); + } + + private static Stream<Arguments> provideTestCasesForEmptyAndSingle() { + return Stream.of(Arguments.of("", ""), Arguments.of("a", "a")); + } + + @ParameterizedTest + @MethodSource("provideTestCasesForComplexCases") + public void testComplexCases(String input, String expected) { + assertEquals(expected, Manacher.longestPalindrome(input)); + } + + private static Stream<Arguments> provideTestCasesForComplexCases() { + return Stream.of(Arguments.of("abcdefghijklmnopqrstuvwxyzttattarrattatabcdefghijklmnopqrstuvwxyz", "tattarrattat"), Arguments.of("aaaaabaaaaacbaaaaa", "aaaaabaaaaa"), Arguments.of("sometextrandomabcdefgabcdefghhgfedcbahijklmnopqrstuvwxyz", "abcdefghhgfedcba"), + Arguments.of("therewasasignthatsaidmadaminedenimadamitwasthereallalong", "madaminedenimadam")); + } + + @ParameterizedTest + @MethodSource("provideTestCasesForSentencePalindromes") + public void testSentencePalindromes(String input, String expected) { + assertEquals(expected, Manacher.longestPalindrome(input)); + } + + private static Stream<Arguments> provideTestCasesForSentencePalindromes() { + return Stream.of(Arguments.of("XThisisalongtextbuthiddeninsideisAmanaplanacanalPanamaWhichweknowisfamous", "lanacanal"), Arguments.of("AverylongstringthatcontainsNeveroddoreveninahiddenmanner", "everoddoreve")); + } +} diff --git a/src/test/java/com/thealgorithms/strings/MyAtoiTest.java b/src/test/java/com/thealgorithms/strings/MyAtoiTest.java new file mode 100644 index 000000000000..8d2354910e2a --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/MyAtoiTest.java @@ -0,0 +1,43 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class MyAtoiTest { + + @ParameterizedTest + @CsvSource({"'42', 42", "' -42', -42", "'4193 with words', 4193", "'words and 987', 0", "'-91283472332', -2147483648", "'21474836460', 2147483647", "' +123', 123", "'', 0", "' ', 0", "'-2147483648', -2147483648", "'+2147483647', 2147483647", "' -0012a42', -12", + "'9223372036854775808', 2147483647", "'-9223372036854775809', -2147483648", "'3.14159', 3", "' -0012', -12", "' 0000000000012345678', 12345678", "' -0000000000012345678', -12345678", "' +0000000000012345678', 12345678", "'0', 0", "'+0', 0", "'-0', 0"}) + void + testMyAtoi(String input, int expected) { + assertEquals(expected, MyAtoi.myAtoi(input)); + } + + @Test + void testNullInput() { + assertEquals(0, MyAtoi.myAtoi(null)); + } + + @Test + void testSinglePlus() { + assertEquals(0, MyAtoi.myAtoi("+")); + } + + @Test + void testSingleMinus() { + assertEquals(0, MyAtoi.myAtoi("-")); + } + + @Test + void testIntegerMinBoundary() { + assertEquals(Integer.MIN_VALUE, MyAtoi.myAtoi("-2147483648")); + } + + @Test + void testIntegerMaxBoundary() { + assertEquals(Integer.MAX_VALUE, MyAtoi.myAtoi("2147483647")); + } +} diff --git a/src/test/java/com/thealgorithms/strings/PalindromeTest.java b/src/test/java/com/thealgorithms/strings/PalindromeTest.java new file mode 100644 index 000000000000..d839e381c6dd --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/PalindromeTest.java @@ -0,0 +1,21 @@ +package com.thealgorithms.strings; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class PalindromeTest { + private static Stream<TestData> provideTestCases() { + return Stream.of(new TestData(null, true), new TestData("", true), new TestData("aba", true), new TestData("123321", true), new TestData("kayak", true), new TestData("abb", false), new TestData("abc", false), new TestData("abc123", false), new TestData("kayaks", false)); + } + + @ParameterizedTest + @MethodSource("provideTestCases") + void testPalindrome(TestData testData) { + Assertions.assertEquals(testData.expected, Palindrome.isPalindrome(testData.input) && Palindrome.isPalindromeRecursion(testData.input) && Palindrome.isPalindromeTwoPointer(testData.input)); + } + + private record TestData(String input, boolean expected) { + } +} diff --git a/src/test/java/com/thealgorithms/strings/PangramTest.java b/src/test/java/com/thealgorithms/strings/PangramTest.java new file mode 100644 index 000000000000..00ecb909b2de --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/PangramTest.java @@ -0,0 +1,27 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class PangramTest { + + @Test + public void testPangram() { + assertTrue(Pangram.isPangram("The quick brown fox jumps over the lazy dog")); + assertFalse(Pangram.isPangram("The quick brown fox jumps over the azy dog")); // L is missing + assertFalse(Pangram.isPangram("+-1234 This string is not alphabetical")); + assertFalse(Pangram.isPangram("\u0000/\\ Invalid characters are alright too")); + + assertTrue(Pangram.isPangram2("The quick brown fox jumps over the lazy dog")); + assertFalse(Pangram.isPangram2("The quick brown fox jumps over the azy dog")); // L is missing + assertFalse(Pangram.isPangram2("+-1234 This string is not alphabetical")); + assertFalse(Pangram.isPangram2("\u0000/\\ Invalid characters are alright too")); + + assertTrue(Pangram.isPangramUsingSet("The quick brown fox jumps over the lazy dog")); + assertFalse(Pangram.isPangramUsingSet("The quick brown fox jumps over the azy dog")); // L is missing + assertFalse(Pangram.isPangramUsingSet("+-1234 This string is not alphabetical")); + assertFalse(Pangram.isPangramUsingSet("\u0000/\\ Invalid characters are alright too")); + } +} diff --git a/src/test/java/com/thealgorithms/strings/PermuteStringTest.java b/src/test/java/com/thealgorithms/strings/PermuteStringTest.java new file mode 100644 index 000000000000..c726874f4cec --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/PermuteStringTest.java @@ -0,0 +1,29 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Set; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class PermuteStringTest { + + private static Stream<TestData> provideTestCases() { + return Stream.of(new TestData("ABC", Set.of("ABC", "ACB", "BAC", "BCA", "CAB", "CBA")), new TestData("AB", Set.of("AB", "BA")), new TestData("A", Set.of("A")), new TestData("AA", Set.of("AA")), new TestData("123", Set.of("123", "132", "213", "231", "312", "321")), + new TestData("aA", Set.of("aA", "Aa")), new TestData("AaB", Set.of("AaB", "ABa", "aAB", "aBA", "BAa", "BaA")), new TestData("!@", Set.of("!@", "@!")), new TestData("!a@", Set.of("!a@", "!@a", "a!@", "a@!", "@!a", "@a!")), + new TestData("ABCD", Set.of("ABCD", "ABDC", "ACBD", "ACDB", "ADBC", "ADCB", "BACD", "BADC", "BCAD", "BCDA", "BDAC", "BDCA", "CABD", "CADB", "CBAD", "CBDA", "CDAB", "CDBA", "DABC", "DACB", "DBAC", "DBCA", "DCAB", "DCBA")), + new TestData("A B", Set.of("A B", "AB ", " AB", " BA", "BA ", "B A")), + new TestData("abcd", Set.of("abcd", "abdc", "acbd", "acdb", "adbc", "adcb", "bacd", "badc", "bcad", "bcda", "bdac", "bdca", "cabd", "cadb", "cbad", "cbda", "cdab", "cdba", "dabc", "dacb", "dbac", "dbca", "dcab", "dcba"))); + } + + @ParameterizedTest + @MethodSource("provideTestCases") + void testPermutations(TestData testData) { + Set<String> actualPermutations = PermuteString.getPermutations(testData.input); + assertEquals(testData.expected, actualPermutations, "The permutations of '" + testData.input + "' are not correct."); + } + + record TestData(String input, Set<String> expected) { + } +} diff --git a/src/test/java/com/thealgorithms/strings/ReturnSubsequenceTest.java b/src/test/java/com/thealgorithms/strings/ReturnSubsequenceTest.java new file mode 100644 index 000000000000..d4e1248d05ad --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/ReturnSubsequenceTest.java @@ -0,0 +1,23 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class ReturnSubsequenceTest { + + @ParameterizedTest + @MethodSource("provideTestCases") + void testSubsequences(String input, String[] expected) { + String[] actual = ReturnSubsequence.getSubsequences(input); + assertArrayEquals(expected, actual); + } + + static Stream<Arguments> provideTestCases() { + return Stream.of(Arguments.of("", new String[] {""}), Arguments.of("a", new String[] {"", "a"}), Arguments.of("ab", new String[] {"", "b", "a", "ab"}), Arguments.of("abc", new String[] {"", "c", "b", "bc", "a", "ac", "ab", "abc"}), + Arguments.of("aab", new String[] {"", "b", "a", "ab", "a", "ab", "aa", "aab"})); + } +} diff --git a/src/test/java/com/thealgorithms/strings/ReverseStringRecursiveTest.java b/src/test/java/com/thealgorithms/strings/ReverseStringRecursiveTest.java new file mode 100644 index 000000000000..19daa61f48ca --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/ReverseStringRecursiveTest.java @@ -0,0 +1,16 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class ReverseStringRecursiveTest { + @ParameterizedTest + @CsvSource({"'Hello World', 'dlroW olleH'", "'helloworld', 'dlrowolleh'", "'123456789', '987654321'", "'', ''", "'A', 'A'", "'!123 ABC xyz!', '!zyx CBA 321!'", "'Abc 123 Xyz', 'zyX 321 cbA'", "'12.34,56;78:90', '09:87;65,43.21'", "'abcdEFGHiJKL', 'LKJiHGFEdcba'", + "'MixOf123AndText!', '!txeTdnA321fOxiM'"}) + public void + testReverseString(String input, String expectedOutput) { + assertEquals(expectedOutput, ReverseStringRecursive.reverse(input)); + } +} diff --git a/src/test/java/com/thealgorithms/strings/ReverseStringTest.java b/src/test/java/com/thealgorithms/strings/ReverseStringTest.java new file mode 100644 index 000000000000..08f5fb586d82 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/ReverseStringTest.java @@ -0,0 +1,34 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class ReverseStringTest { + + private static Stream<Arguments> testCases() { + return Stream.of(Arguments.of("Hello World", "dlroW olleH"), Arguments.of("helloworld", "dlrowolleh"), Arguments.of("123456789", "987654321"), Arguments.of("", ""), Arguments.of("A", "A"), Arguments.of("ab", "ba"), + Arguments.of(" leading and trailing spaces ", " secaps gniliart dna gnidael "), Arguments.of("!@#$%^&*()", ")(*&^%$#@!"), Arguments.of("MixOf123AndText!", "!txeTdnA321fOxiM")); + } + + @ParameterizedTest + @MethodSource("testCases") + public void testReverseString(String input, String expectedOutput) { + assertEquals(expectedOutput, ReverseString.reverse(input)); + } + + @ParameterizedTest + @MethodSource("testCases") + public void testReverseString2(String input, String expectedOutput) { + assertEquals(expectedOutput, ReverseString.reverse2(input)); + } + + @ParameterizedTest + @MethodSource("testCases") + public void testReverseString3(String input, String expectedOutput) { + assertEquals(expectedOutput, ReverseString.reverse3(input)); + } +} diff --git a/src/test/java/com/thealgorithms/strings/ReverseWordsInStringTest.java b/src/test/java/com/thealgorithms/strings/ReverseWordsInStringTest.java new file mode 100644 index 000000000000..7cab6aa7c698 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/ReverseWordsInStringTest.java @@ -0,0 +1,19 @@ +package com.thealgorithms.strings; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class ReverseWordsInStringTest { + @ParameterizedTest + @MethodSource("inputStream") + void numberTests(String expected, String input) { + Assertions.assertEquals(expected, ReverseWordsInString.reverseWordsInString(input)); + } + + private static Stream<Arguments> inputStream() { + return Stream.of(Arguments.of("blue is Sky", "Sky is blue"), Arguments.of("blue is Sky", "Sky \n is \t \n blue "), Arguments.of("", ""), Arguments.of("", " "), Arguments.of("", "\t")); + } +} diff --git a/src/test/java/com/thealgorithms/strings/RotationTest.java b/src/test/java/com/thealgorithms/strings/RotationTest.java new file mode 100644 index 000000000000..911ff87d29ad --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/RotationTest.java @@ -0,0 +1,15 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class RotationTest { + + @Test + public void testRotation() { + assertEquals("eksge", Rotation.rotation("geeks", 2)); + assertEquals("anasban", Rotation.rotation("bananas", 3)); + assertEquals("abracadabra", Rotation.rotation("abracadabra", 0)); + } +} diff --git a/src/test/java/com/thealgorithms/strings/StringCompressionTest.java b/src/test/java/com/thealgorithms/strings/StringCompressionTest.java new file mode 100644 index 000000000000..fdec311f8f0a --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/StringCompressionTest.java @@ -0,0 +1,15 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class StringCompressionTest { + @ParameterizedTest + @CsvSource({"'a', 'a'", "'aabbb', 'a2b3'", "'abbbc', 'ab3c'", "'aabccd', 'a2bc2d'", "'aaaabbbcccc', 'a4b3c4'", "'abcd', 'abcd'", "'aabbccdd', 'a2b2c2d2'", "'aaabbaa', 'a3b2a2'", "'', ''", "'a', 'a'", "'aaaaa', 'a5'", "'aabb', 'a2b2'", "'aabbbaaa', 'a2b3a3'", "'qwerty', 'qwerty'"}) + void stringCompressionTest(String input, String expectedOutput) { + String output = StringCompression.compress(input); + assertEquals(expectedOutput, output); + } +} diff --git a/src/test/java/com/thealgorithms/strings/StringMatchFiniteAutomataTest.java b/src/test/java/com/thealgorithms/strings/StringMatchFiniteAutomataTest.java new file mode 100644 index 000000000000..be460c7c4d91 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/StringMatchFiniteAutomataTest.java @@ -0,0 +1,23 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Set; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class StringMatchFiniteAutomataTest { + + @ParameterizedTest + @MethodSource("provideTestCases") + void searchPattern(String text, String pattern, Set<Integer> expectedOutput) { + assertEquals(expectedOutput, StringMatchFiniteAutomata.searchPattern(text, pattern)); + } + + private static Stream<Arguments> provideTestCases() { + return Stream.of(Arguments.of("abcbcabc", "abc", Set.of(0, 5)), Arguments.of("", "abc", Set.of()), Arguments.of("", "", Set.of()), Arguments.of("a", "b", Set.of()), Arguments.of("a", "a", Set.of(0)), Arguments.of("abcdabcabcabcd", "abcd", Set.of(0, 10)), Arguments.of("abc", "bcd", Set.of()), + Arguments.of("abcdefg", "xyz", Set.of()), Arguments.of("abcde", "", Set.of(1, 2, 3, 4, 5)), Arguments.of("abcabcabc", "abc", Set.of(0, 3, 6)), Arguments.of("abcabcabc", "abcabcabc", Set.of(0)), Arguments.of("aaabbbaaa", "aaa", Set.of(0, 6)), Arguments.of("abcdefg", "efg", Set.of(4))); + } +} diff --git a/src/test/java/com/thealgorithms/strings/UpperTest.java b/src/test/java/com/thealgorithms/strings/UpperTest.java new file mode 100644 index 000000000000..5413c77b913a --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/UpperTest.java @@ -0,0 +1,18 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class UpperTest { + + @Test + public void toUpperCase() { + String input1 = "hello world"; + String input2 = "hElLo WoRlD"; + String input3 = "HELLO WORLD"; + assertEquals("HELLO WORLD", Upper.toUpperCase(input1)); + assertEquals("HELLO WORLD", Upper.toUpperCase(input2)); + assertEquals("HELLO WORLD", Upper.toUpperCase(input3)); + } +} diff --git a/src/test/java/com/thealgorithms/strings/ValidParenthesesTest.java b/src/test/java/com/thealgorithms/strings/ValidParenthesesTest.java new file mode 100644 index 000000000000..2b6884c91c8f --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/ValidParenthesesTest.java @@ -0,0 +1,27 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class ValidParenthesesTest { + + @Test + void testOne() { + assertTrue(ValidParentheses.isValid("()")); + assertTrue(ValidParentheses.isValidParentheses("()")); + } + + @Test + void testTwo() { + assertTrue(ValidParentheses.isValid("()[]{}")); + assertTrue(ValidParentheses.isValidParentheses("()[]{}")); + } + + @Test + void testThree() { + assertFalse(ValidParentheses.isValid("(]")); + assertFalse(ValidParentheses.isValidParentheses("(]")); + } +} diff --git a/src/test/java/com/thealgorithms/strings/WordLadderTest.java b/src/test/java/com/thealgorithms/strings/WordLadderTest.java new file mode 100644 index 000000000000..221953411da7 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/WordLadderTest.java @@ -0,0 +1,67 @@ +package com.thealgorithms.strings; + +import static java.util.Collections.emptyList; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class WordLadderTest { + + /** + * Test 1: + * Input: beginWord = "hit", endWord = "cog", wordList = + * ["hot","dot","dog","lot","log","cog"] + * Output: 5 + * Explanation: One shortest transformation sequence is + * "hit" -> "hot" -> "dot" -> "dog" -> cog" + * which is 5 words long. + */ + @Test + public void testWordLadder() { + + List<String> wordList1 = Arrays.asList("hot", "dot", "dog", "lot", "log", "cog"); + assertEquals(WordLadder.ladderLength("hit", "cog", wordList1), 5); + } + + /** + * Test 2: + * Input: beginWord = "hit", endWord = "cog", wordList = + * ["hot","dot","dog","lot","log"] + * Output: 0 + * Explanation: The endWord "cog" is not in wordList, + * therefore there is no valid transformation sequence. + */ + @Test + public void testWordLadder2() { + + List<String> wordList2 = Arrays.asList("hot", "dot", "dog", "lot", "log"); + assertEquals(WordLadder.ladderLength("hit", "cog", wordList2), 0); + } + + /** + * Test 3: + * Input: beginWord = "hit", endWord = "cog", wordList = + * [] + * Output: 0 + * Explanation: The wordList is empty (corner case), + * therefore there is no valid transformation sequence. + */ + @Test + public void testWordLadder3() { + + List<String> wordList3 = emptyList(); + assertEquals(WordLadder.ladderLength("hit", "cog", wordList3), 0); + } + + @ParameterizedTest + @CsvSource({"'a', 'c', 'b,c', 2", "'a', 'c', 'a', 0", "'a', 'a', 'a', 0", "'ab', 'cd', 'ad,bd,cd', 3", "'a', 'd', 'b,c,d', 2", "'a', 'd', 'b,c,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,d', 2"}) + void testLadderLength(String beginWord, String endWord, String wordListStr, int expectedLength) { + List<String> wordList = List.of(wordListStr.split(",")); + int result = WordLadder.ladderLength(beginWord, endWord, wordList); + assertEquals(expectedLength, result); + } +} diff --git a/src/test/java/com/thealgorithms/strings/zigZagPattern/ZigZagPatternTest.java b/src/test/java/com/thealgorithms/strings/zigZagPattern/ZigZagPatternTest.java new file mode 100644 index 000000000000..2cbbfe3d2dd8 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/zigZagPattern/ZigZagPatternTest.java @@ -0,0 +1,19 @@ +package com.thealgorithms.strings.zigZagPattern; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ZigZagPatternTest { + + @Test + public void testZigZagPattern() { + String input1 = "HelloWorldFromJava"; + String input2 = "javaIsAProgrammingLanguage"; + Assertions.assertEquals(ZigZagPattern.encode(input1, 4), "HooeWrrmalolFJvlda"); + Assertions.assertEquals(ZigZagPattern.encode(input2, 4), "jAaLgasPrmgaaevIrgmnnuaoig"); + // Edge cases + Assertions.assertEquals("ABC", ZigZagPattern.encode("ABC", 1)); // Single row + Assertions.assertEquals("A", ZigZagPattern.encode("A", 2)); // numRows > length of string + Assertions.assertEquals("", ZigZagPattern.encode("", 3)); // Empty string + } +} diff --git a/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java b/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java new file mode 100644 index 000000000000..29189290e1d4 --- /dev/null +++ b/src/test/java/com/thealgorithms/tree/HeavyLightDecompositionTest.java @@ -0,0 +1,69 @@ +package com.thealgorithms.tree; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class HeavyLightDecompositionTest { + + private HeavyLightDecomposition hld; + private final int[] values = {0, 10, 20, 30, 40, 50}; + + /** + * Initializes the test environment with a predefined tree structure and values. + */ + @BeforeEach + void setUp() { + hld = new HeavyLightDecomposition(5); + hld.addEdge(1, 2); + hld.addEdge(1, 3); + hld.addEdge(2, 4); + hld.addEdge(2, 5); + hld.initialize(1, values); + } + + /** + * Verifies that the tree initializes successfully without errors. + */ + @Test + void testBasicTreeInitialization() { + assertTrue(true, "Basic tree structure initialized successfully"); + } + + /** + * Tests the maximum value query in the path between nodes. + */ + @Test + void testQueryMaxInPath() { + assertEquals(50, hld.queryMaxInPath(4, 5), "Max value in path (4,5) should be 50"); + assertEquals(30, hld.queryMaxInPath(3, 2), "Max value in path (3,2) should be 30"); + } + + /** + * Tests updating a node's value and ensuring it is reflected in queries. + */ + @Test + void testUpdateNodeValue() { + hld.updateSegmentTree(1, 0, hld.getPositionIndex() - 1, hld.getPosition(4), 100); + assertEquals(100, hld.queryMaxInPath(4, 5), "Updated value should be reflected in query"); + } + + /** + * Tests the maximum value query in a skewed tree structure. + */ + @Test + void testSkewedTreeMaxQuery() { + assertEquals(40, hld.queryMaxInPath(1, 4), "Max value in skewed tree (1,4) should be 40"); + } + + /** + * Ensures query handles cases where u is a deeper node correctly. + */ + @Test + void testDepthSwapInPathQuery() { + assertEquals(50, hld.queryMaxInPath(5, 2), "Query should handle depth swap correctly"); + assertEquals(40, hld.queryMaxInPath(4, 1), "Query should handle swapped nodes correctly and return max value"); + } +}