diff --git a/SPM/.gitignore b/SPM/.gitignore new file mode 100644 index 0000000..6564d5e --- /dev/null +++ b/SPM/.gitignore @@ -0,0 +1,92 @@ +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +*.DS_Store + +## User settings +xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +build/ +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 + +## Obj-C/Swift specific +*.hmap + +## App packaging +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +# Package.pins +# Package.resolved +# *.xcodeproj +# +# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata +# hence it is not needed unless you have added a package configuration file to your project +# .swiftpm + +.build/ + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# Pods/ +# +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build/ + +# Accio dependency management +Dependencies/ +.accio/ + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output + +# Code Injection +# +# After new code Injection tools there's a generated folder /iOSInjectionProject +# https://github.com/johnno1962/injectionforxcode + +iOSInjectionProject/ diff --git a/SPM/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/SPM/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/SPM/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/SPM/LICENSE b/SPM/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/SPM/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/SPM/Package.swift b/SPM/Package.swift new file mode 100644 index 0000000..c48c764 --- /dev/null +++ b/SPM/Package.swift @@ -0,0 +1,28 @@ +// swift-tools-version:5.2 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "DataStructuresAlgorithms", + products: [ + // Products define the executables and libraries produced by a package, and make them visible to other packages. + .library( + name: "DataStructuresAlgorithms", + targets: ["DataStructuresAlgorithms"]), + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + // .package(url: /* package url */, from: "1.0.0"), + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages which this package depends on. + .target( + name: "DataStructuresAlgorithms", + dependencies: []), + .testTarget( + name: "DataStructuresAlgorithmsTests", + dependencies: ["DataStructuresAlgorithms"]), + ] +) diff --git a/SPM/README.md b/SPM/README.md new file mode 100644 index 0000000..d85aa88 --- /dev/null +++ b/SPM/README.md @@ -0,0 +1,16 @@ +# DataStructuresAlgs +This package offers a collection of data structures and algorithms written in Swift. +This includes well-known data structures and algorithms such as Djikstra's shortest path algorithm, as well as algorithms for specific problems such as checking whether a string contains all unique characters. + +Each data structures and algorithm class also has a corresponding unit test with, if applicable, a link to test data/further decription. + +## Licence + +This swift package can be used freely, see https://github.com/sjaindl/DataStructuresAlgs/blob/master/DataStructuresAlgorithms/LICENSE for license notes. + +## Installation + +No special prerequisites required. You just need an IDE (preferably Xcode) and clone the source code: +`git clone https://github.com/sjaindl/DataStructuresAlgs.git` + +You can execute all unit tests in the DataStructuresAlgorithmsTest class. diff --git a/SPM/Sources/DataStructuresAlgorithms/Algorithms/BellmanFord.swift b/SPM/Sources/DataStructuresAlgorithms/Algorithms/BellmanFord.swift new file mode 100644 index 0000000..7cc769c --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/Algorithms/BellmanFord.swift @@ -0,0 +1,42 @@ +// +// BellmanFord.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 09.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class BellmanFord { + public let graph: WeightedDirectedGraph + + public init(graph: WeightedDirectedGraph) { + self.graph = graph + } + + open func findShortestPath(source: Vertice, target: Vertice) throws -> Int { + var distTo = [Int] (repeating: Int.max, count: graph.vertices.count) + distTo[source.id] = 0 + + var pass = 1 + + while pass < graph.vertices.count { + for edge in graph.edges() { + if distTo[edge.from.id] != Int.max && distTo[edge.to.id] > distTo[edge.from.id] + edge.weight { + distTo[edge.to.id] = distTo[edge.from.id] + edge.weight + } + } + pass += 1 + } + + //Check for negative cycle: There is one, if edges can be forever relaxed. + for edge in graph.edges() { + if distTo[edge.from.id] != Int.max && distTo[edge.to.id] > distTo[edge.from.id] + edge.weight { + throw NSError(domain: "Bellman Ford: Negative Cycle", code: 0, userInfo: nil) + } + } + + return distTo[target.id] + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/Algorithms/BipartiteGraphChecker.swift b/SPM/Sources/DataStructuresAlgorithms/Algorithms/BipartiteGraphChecker.swift new file mode 100644 index 0000000..41f3195 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/Algorithms/BipartiteGraphChecker.swift @@ -0,0 +1,80 @@ +// +// BipartiteGraph.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 23.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class BipartiteGraphChecker { + + public enum Color { + case red + case black + case none + } + + public var graph: UndirectedGraph + + public init(graph: UndirectedGraph) { + self.graph = graph + } + + open func isBipartite() throws -> [Color]? { + if graph.vertices.count < 1 { + return [] + //a graph with no edges is also Bipiartite. Note that the Bipartite condition says all edges should be from one set to another. + } + + var colors: [Color]? = [Color] (repeating: .none, count: graph.vertices.count) + + for vertice in graph.vertices { //important if graph is not connected + if colors?[vertice.id] == BipartiteGraphChecker.Color.none { + colors = try isBipartite(vertice: vertice, colors: colors) + } + } + + return colors + } + + open func isBipartite(vertice: Vertice, colors: [Color]?) throws -> [Color]? { + guard var colors = colors else { + return nil + } + + let bfsQueue = Queue() + let rootNode = graph.vertices[vertice.id] + + colors[rootNode.id] = .red + bfsQueue.enqueue(val: rootNode) + + while !bfsQueue.isEmpty() { + let curNode = try bfsQueue.dequeue() + + let neighbours = graph.neighbours(v: curNode) + var currentHead = neighbours.head + let colorForNeighbors: Color = colors[curNode.id] == .black ? .red : .black + while let curHead = currentHead { + let curVertice = curHead.val + if colors[curVertice.id] == colors[curNode.id] { + return nil //same color between neighbors (not possible cycle)! + } + + if curVertice.id == curNode.id { + return nil //graph is not bipartite, if there's a self-loop + } + + if colors[curVertice.id] == .none { + colors[curVertice.id] = colorForNeighbors + bfsQueue.enqueue(val: curVertice) + } + + currentHead = currentHead?.next + } + } + + return colors + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/Algorithms/Djikstra.swift b/SPM/Sources/DataStructuresAlgorithms/Algorithms/Djikstra.swift new file mode 100644 index 0000000..3d30615 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/Algorithms/Djikstra.swift @@ -0,0 +1,65 @@ +// +// Djikstra.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 02.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class Djikstra { + public var graph: WeightedUndirectedGraph + public var previous = Dictionary() + public var distanceTo = Dictionary() + + public init(graph: WeightedUndirectedGraph) { + self.graph = graph + for vertice in graph.vertices { + vertice.distanceTo = Int.max + distanceTo[vertice] = Int.max + } + } + + open func djikstra(from: Vertice, to: Vertice) throws -> Int? { + distanceTo[from] = 0 + let pq = IndexedMinPriorityQueue(maxElements: graph.vertices.count) + + for vertice in graph.vertices { + try? pq.insert(index: vertice.id, key: vertice) + } + + while !pq.isEmpty(), let vertice = try? pq.extractMin() { + let neighbours = graph.neighbours(v: vertice) + var neighbour = neighbours.head + + while let n = neighbour, let curDistToV = distanceTo[vertice], let curDistToNeighbour = distanceTo[n.val.to] { + let distInPath = curDistToV == Int.max ? Int.max : curDistToV + n.val.weight + + if distInPath < curDistToNeighbour { + distanceTo[n.val.to] = distInPath + previous[n.val.to] = vertice + + n.val.to.distanceTo = distInPath + try? pq.changeKey(index: n.val.to.id, key: n.val.to) + } + + neighbour = neighbour?.next + } + } + + return distanceTo[to] + } + + open func path(to: Vertice) -> [Vertice] { + var path: [Vertice] = [] + + var prev: Vertice? = to + while let prevVertice = prev { + path.append(prevVertice) + prev = previous[prevVertice] + } + + return path.reversed() + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/Algorithms/FloydWarshall.swift b/SPM/Sources/DataStructuresAlgorithms/Algorithms/FloydWarshall.swift new file mode 100644 index 0000000..f5504f3 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/Algorithms/FloydWarshall.swift @@ -0,0 +1,37 @@ +// +// FloydWarshall.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 11.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class FloydWarshall { + public let graph: WeightedDirectedGraphWithAdjMatrix + + public init(graph: WeightedDirectedGraphWithAdjMatrix) { + self.graph = graph + } + + open func findShortestPaths() throws { + for intermediateIndex in 0 ... graph.vertices.count - 1 { + for startIndex in 0 ... graph.vertices.count - 1 { + for endIndex in 0 ... graph.vertices.count - 1 { + if graph.adjMatrix[startIndex][intermediateIndex] < Int.max && graph.adjMatrix[intermediateIndex][endIndex] < Int.max + && graph.adjMatrix[startIndex][intermediateIndex] + graph.adjMatrix[intermediateIndex][endIndex] < graph.adjMatrix[startIndex][endIndex] { + graph.adjMatrix[startIndex][endIndex] = graph.adjMatrix[startIndex][intermediateIndex] + graph.adjMatrix[intermediateIndex][endIndex] + } + } + } + } + + //Check for negative cycle: + for index in 0 ... graph.vertices.count - 1 { + if graph.adjMatrix[index][index] < 0 { + throw NSError(domain: "FloydWarshall: Negative Cycle", code: 0, userInfo: nil) + } + } + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/Algorithms/GraphSearch.swift b/SPM/Sources/DataStructuresAlgorithms/Algorithms/GraphSearch.swift new file mode 100644 index 0000000..f55c6fe --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/Algorithms/GraphSearch.swift @@ -0,0 +1,89 @@ +// +// GraphSearch.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 27.03.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class GraphSearch { + private let graph: UndirectedGraph + private let start: Vertice + private var visited: [Bool] + + public init(graph: UndirectedGraph, start: Vertice) { + self.graph = graph + self.start = start + + visited = [Bool] (repeating: false, count: graph.vertices.count) + } + + open func bfs() { + let q = Queue() + + q.enqueue(val: start) + visited[start.id] = true + while !q.isEmpty() { + let v = try! q.dequeue() + + let n = graph.neighbours(v: v) + var curNode = n.head + while curNode != nil { + if !visited[curNode!.val.id] { + q.enqueue(val: curNode!.val) + visited[curNode!.val.id] = true + } + curNode = curNode!.next + } + } + } + + open func dfs() { + visited[start.id] = true + dfs(v: start) + } + + open func dfs(v: Vertice) { + let n = graph.neighbours(v: v) + var curVertice = n.head + + while curVertice != nil { + if !visited[curVertice!.val.id] { + visited[curVertice!.val.id] = true + dfs(v: curVertice!.val) + } + + curVertice = curVertice?.next + } + } + + open func dfsIterative() { + let s = Stack() + s.push(val: start) + visited[start.id] = true + + while !s.isEmpty() { + do { + let v = try s.pop() + + let n = graph.neighbours(v: v) + var curVertice = n.head + + while curVertice != nil { + if !visited[curVertice!.val.id] { + visited[curVertice!.val.id] = true + s.push(val: curVertice!.val) + } + + curVertice = curVertice?.next + } + + } + catch { + fatalError() + } + } + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/Algorithms/Knapsack.swift b/SPM/Sources/DataStructuresAlgorithms/Algorithms/Knapsack.swift new file mode 100644 index 0000000..9351cc9 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/Algorithms/Knapsack.swift @@ -0,0 +1,71 @@ +// +// Knapsack.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 26.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +//https://www.geeksforgeeks.org/0-1-knapsack-problem-dp-10/ +open class Knapsack { + + private var knapsack: [[Int]] = [] + + public init() { } + + open func knapsack(weights: [Int], values: [Int], maxWeight: Int) throws -> Int { + if values.count != weights.count { + throw NSError(domain: "Knapsack: values and weights are not of same size", code: 0, userInfo: nil) + } + + //base case: no more weight left OR no values left + if maxWeight == 0 || values.isEmpty { + return 0 + } + + //compute knapsack without last element + let knapsackWithoutLastElement = try knapsack(weights: Array(weights[0 ..< weights.count - 1]), values: Array(values[0 ..< values.count - 1]), maxWeight: maxWeight) + + //if weight of last > maxWeight, the last element definitely can't be included in knapsack + if weights[values.count - 1] > maxWeight { + return knapsackWithoutLastElement + } + + //compute knapsack with last element + let knapsackWithLastElement = try values[values.count - 1] + knapsack(weights: Array(weights[0 ..< weights.count - 1]), values: Array(values[0 ..< values.count - 1]), maxWeight: maxWeight - weights[weights.count - 1]) + + //case 1: value at last position in values array may be included in knapsack + //case 2: value at last position in values array is NOT included in knapsack + return max(knapsackWithLastElement, knapsackWithoutLastElement) + } + + open func knapsackDynamicProgramming(weights: [Int], values: [Int], maxWeight: Int) throws -> Int { + if values.count != weights.count { + throw NSError(domain: "Knapsack: values and weights are not of same size", code: 0, userInfo: nil) + } + + var knapsack = Array(repeating: Array(repeating: -1, count: maxWeight + 1), count: values.count + 1) + + for valueIndex in 0 ... values.count { + for weight in 0 ... maxWeight { + if valueIndex == 0 || weight == 0 { + knapsack[valueIndex][weight] = 0 + } else if weights[valueIndex - 1] > weight { + //value doesn't fit it knapsack (too much weight) + knapsack[valueIndex][weight] = knapsack[valueIndex - 1][weight] + } else { + //value would fit, take max of both cases: + //case 1: value at last position in values array may be included in knapsack + //case 2: value at last position in values array is NOT included in knapsack + let knapsackWithLastElement = values[valueIndex - 1] + knapsack[valueIndex - 1][weight - weights[valueIndex - 1]] + let knapsackWithoutLastElement = knapsack[valueIndex - 1][weight] + knapsack[valueIndex][weight] = max(knapsackWithLastElement, knapsackWithoutLastElement) + } + } + } + + return knapsack[values.count][maxWeight] + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/Algorithms/MapReduce.swift b/SPM/Sources/DataStructuresAlgorithms/Algorithms/MapReduce.swift new file mode 100644 index 0000000..d43b4db --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/Algorithms/MapReduce.swift @@ -0,0 +1,69 @@ +// +// MapReduce.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 08.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class Document { + let words: [String] + + public init(words: [String]) { + self.words = words + } +} + +open class Result: Comparable { + public let key: String + public let value: Int + + public init(key: String, value: Int) { + self.key = key + self.value = value + } + + public static func < (lhs: Result, rhs: Result) -> Bool { + return lhs.key < rhs.key + } + + public static func == (lhs: Result, rhs: Result) -> Bool { + return lhs.key == rhs.key + } +} + +open class MapReduceWordCount { + var partialResults: [Result] = [] + var finalResults: [Result] = [] + + public init() { } + + open func map(document: Document) { + for word in document.words { + emit(partialResult: Result(key: word, value: 1)) + } + } + + open func emit(partialResult: Result) { + partialResults.append(partialResult) + } + + open func shuffleAndSort() { + partialResults.sort() + } + + open func reduce(partialResults: [Result]) { + if partialResults.count > 0 { + let key = partialResults[0].key + var sum = 0 + + for partialResult in partialResults { + sum += partialResult.value + } + + finalResults.append(Result(key: key, value: sum)) + } + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/Algorithms/MergeSort.swift b/SPM/Sources/DataStructuresAlgorithms/Algorithms/MergeSort.swift new file mode 100644 index 0000000..162393a --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/Algorithms/MergeSort.swift @@ -0,0 +1,50 @@ +// +// MergeSort.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 28.03.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class MergeSort { + + public static func mergeSort(array: inout [T]) { + mergeSort(array: &array, min: 0, max: array.count - 1) + } + + public static func mergeSort(array: inout [T], min: Int, max: Int) { + if max <= min { + return + } + + let middle = min + (max - min) / 2 + mergeSort(array: &array, min: min, max: middle) + mergeSort(array: &array, min: middle + 1, max: max) + merge(array: &array, min: min, middle: middle, max: max) + } + + private static func merge(array: inout [T], min: Int, middle: Int, max: Int) { + let aux = array + var cur = min + var left = min + var right = middle + 1 + while cur <= max { + if left > middle { + array[cur] = aux[right] + right += 1 + } else if right > max { + array[cur] = aux[left] + left += 1 + } else if aux[left] <= aux[right] { + array[cur] = aux[left] + left += 1 + } else { + array[cur] = aux[right] + right += 1 + } + cur += 1 + } + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/Algorithms/MinimumSpanningTreeKruskal.swift b/SPM/Sources/DataStructuresAlgorithms/Algorithms/MinimumSpanningTreeKruskal.swift new file mode 100644 index 0000000..494ca34 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/Algorithms/MinimumSpanningTreeKruskal.swift @@ -0,0 +1,97 @@ +// +// MinimumSpanningTreeKruskal.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 12.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class MinimumSpanningTreeKruskal { + //1. Sort edges by weight --> O(E log E) + //alternative use priority queue --> O(E log E) + //2. Pop min edge + //3. If there is a cycle, discard (detection with UnionFind) + //4. Else include in MST + + public var graph: WeightedUndirectedGraph + + public init(graph: WeightedUndirectedGraph) { + self.graph = graph + } + + open func minimumSpanningTreeQuickSort() -> [Edge] { + var mst: [Edge] = [] + var edges = graph.edges() + + //test + let pq1 = MinHeap() + for edge in edges { + pq1.insert(val: edge.weight) + } + + QuickSort.quickSort(array: &edges) // O(E log E) + + let components = UnionFind(graph: graph) + + var index = 0 + while mst.count < graph.vertices.count - 1 { + let edge = edges[index] + let firstVertice = edges[index].from + let secondVertice = edges[index].to + + let parentOfFirstVertice = components.find(index: firstVertice.id) + let parentOfSecondVertice = components.find(index: secondVertice.id) + + if parentOfFirstVertice != parentOfSecondVertice { + //no cylce, if we take this edge + mst.append(edge) + components.union(firstIndex: parentOfFirstVertice, secondIndex: parentOfSecondVertice) + } // else we have a cycle: ignore edge + + index += 1 + } + + return mst + } + + open func minimumSpanningTreeHeap() -> [Edge] { + var mst: [Edge] = [] + let edges = graph.edges() + + //test + let pq1 = MinHeap() + for edge in edges { + pq1.insert(val: edge.weight) + } + + let pq = MinHeap() + for edge in edges { //O(E log E) + pq.insert(val: edge) + } + + let components = UnionFind(graph: graph) + + var index = 0 + while mst.count < graph.vertices.count - 1 { + + let edge = try! pq.extractMin() + let firstVertice = edge.from + let secondVertice = edge.to + + let parentOfFirstVertice = components.find(index: firstVertice.id) + let parentOfSecondVertice = components.find(index: secondVertice.id) + + if parentOfFirstVertice != parentOfSecondVertice { + //no cylce, if we take this edge + mst.append(edge) + components.union(firstIndex: parentOfFirstVertice, secondIndex: parentOfSecondVertice) + } // else we have a cycle: ignore edge + + index += 1 + } + + return mst + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/Algorithms/MinimumSpanningTreePrim.swift b/SPM/Sources/DataStructuresAlgorithms/Algorithms/MinimumSpanningTreePrim.swift new file mode 100644 index 0000000..21ddeed --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/Algorithms/MinimumSpanningTreePrim.swift @@ -0,0 +1,58 @@ +// +// MinimumSpanningTreePrim.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 13.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class MinimumSpanningTreePrim { + + public var graph: WeightedUndirectedGraph + + public init(graph: WeightedUndirectedGraph) { + self.graph = graph + } + + open func minimumSpanningTree() -> [Edge] { + var mstEdges: [Edge] = [] + var mstVertices: Set = Set() + let outsideVertices: IndexedMinPriorityQueue = IndexedMinPriorityQueue(maxElements: graph.vertices.count) + + for vertice in graph.vertices { // O(V) + let weight = vertice.id == 0 ? 0 : Int.max + vertice.weight = weight + try! outsideVertices.insert(index: vertice.id, key: vertice) + } + + while mstVertices.count < graph.vertices.count { // O(V) + let minVertice = try! outsideVertices.extractMin()! //O(log V) + mstVertices.insert(minVertice) + + if let edge = minVertice.minIngoingEdge { + mstEdges.append(edge) + } + + let neighbours = graph.neighbours(v: minVertice) + var neighbour = neighbours.head + + while let curNeighbour = neighbour { //O(E/V) + let edge = curNeighbour.val + let otherVertice = minVertice == edge.from ? edge.to : edge.from + if !mstVertices.contains(otherVertice) { //O(1) + if otherVertice.weight > edge.weight { + otherVertice.weight = edge.weight + otherVertice.minIngoingEdge = edge + try! outsideVertices.changeKey(index: otherVertice.id, key: otherVertice) //O(log V) with IndexPQ. + } + } + + neighbour = neighbour?.next + } + } + + return mstEdges + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/Algorithms/Permutation.swift b/SPM/Sources/DataStructuresAlgorithms/Algorithms/Permutation.swift new file mode 100644 index 0000000..a80d68a --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/Algorithms/Permutation.swift @@ -0,0 +1,72 @@ +// +// Permutation.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 27.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +//https://www.topcoder.com/generating-permutations/ +open class Permuation { + + public init() { } + + open func permutations(array: inout [T]) -> [[T]] { + if array.isEmpty { + return [] + } + + var generatedPermutations: [[T]] = [] + permutations(array: &array, currentArrayCount: array.count, generatedPermutations: &generatedPermutations) + return generatedPermutations + } + + private func permutations(array: inout [T], currentArrayCount: Int, generatedPermutations: inout [[T]]) { + if currentArrayCount == 1 { + generatedPermutations.append(array) + return + } else { + for count in 0 ..< currentArrayCount { + swap(array: &array, from: count, to: currentArrayCount - 1) + permutations(array: &array, currentArrayCount: currentArrayCount - 1, generatedPermutations: &generatedPermutations) + swap(array: &array, from: count, to: currentArrayCount - 1) + } + } + } + + open func heapsPermutations(array: inout [T]) -> [[T]] { + if array.isEmpty { + return [] + } + + var permutations: [[T]] = [] + + heapsPermutations(array: &array, currentArrayCount: array.count, permutations: &permutations) + return permutations + } + + private func heapsPermutations(array: inout [T], currentArrayCount: Int, permutations: inout [[T]]) { + if currentArrayCount == 1 { + permutations.append(array) + return + } else { + for count in 0 ..< currentArrayCount - 1 { + heapsPermutations(array: &array, currentArrayCount: currentArrayCount - 1, permutations: &permutations) + if currentArrayCount % 2 == 0 { + swap(array: &array, from: count, to: currentArrayCount - 1) + } else { + swap(array: &array, from: 0, to: currentArrayCount - 1) + } + } + } + heapsPermutations(array: &array, currentArrayCount: currentArrayCount - 1, permutations: &permutations) + } + + private func swap(array: inout [T], from: Int, to: Int) { + let temp = array[from] + array[from] = array[to] + array[to] = temp + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/Algorithms/QuickSort.swift b/SPM/Sources/DataStructuresAlgorithms/Algorithms/QuickSort.swift new file mode 100644 index 0000000..59d128e --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/Algorithms/QuickSort.swift @@ -0,0 +1,88 @@ +// +// QuickSort.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 29.03.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class QuickSort { + public static func quickSort(array: inout [T]) { + if array.count <= 1 { + return + } + + quickSort(array: &array, min: 0, max: array.count - 1) + } + + private static func quickSort(array: inout [T], min: Int, max: Int) { + if min < max { + let pivot = partition(array: &array, min: min, max: max) + quickSort(array: &array, min: min, max: pivot - 1) + quickSort(array: &array, min: pivot + 1, max: max) + } + } + + private static func partition(array: inout [T], min: Int, max: Int) -> Int { + var left = min + 1 + var pivot = medianOfThree(array: array, min: min, max: max) + exchange(array: &array, index1: pivot, index2: min) + pivot = min + var right = max + + while true { + while array[left] < array[pivot] { + left += 1 + if left == max { + break + } + } + + while array[right] > array[pivot] { + right -= 1 + if right == min { + break + } + } + + if left >= right { + break + } + + if !(array[left] < array[right]), !(array[right] < array[left]) { + right -= 1 + } else { + exchange(array: &array, index1: left, index2: right) + } + } + + exchange(array: &array, index1: pivot, index2: right) + pivot = right + + return pivot + } + + private static func medianOfThree(array: [T], min: Int, max: Int) -> Int { + let middle = min + (max - min) / 2 + + if array[min] < array[middle] && array[min] > array[max] + || array[min] > array[middle] && array[min] < array[max] { + return min + } + + if array[max] < array[middle] && array[max] > array[min] + || array[max] > array[middle] && array[max] < array[min] { + return max + } + + return middle + } + + private static func exchange(array: inout [T], index1: Int, index2: Int) { + let temp = array[index1] + array[index1] = array[index2] + array[index2] = temp + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/Algorithms/RabinKarp.swift b/SPM/Sources/DataStructuresAlgorithms/Algorithms/RabinKarp.swift new file mode 100644 index 0000000..7bb61f3 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/Algorithms/RabinKarp.swift @@ -0,0 +1,88 @@ +// +// RabinKarp.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 04.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class RabinKarpSubstringSearch { + private var base: Int + public let hashSize: Int = 2147483647 + //A good rule of thumb is to pick both values as prime numbers with M (hashSize) as large as possible + //so that no integer overflow occurs and "base" being at least the size of the alphabet. + + public init(base: Int) { + self.base = base + } + + open func substr(of sub: String, in str: String) -> Int? { + if sub.count < 1 { + return nil + } + + if sub.count > str.count { + return nil + } + + var hashes = [Int] (repeating: -1, count: str.count - sub.count + 1) + let stringChars = Array(str) + + hashes[0] = rabinFinterprint(Array(stringChars[0 ... sub.count - 1])) + + for index in 1 ... str.count - sub.count { + //let compare = rabinFinterprint(Array(stringChars[index ... index + sub.count - 1])) + hashes[index] = shiftedRabin(prevHash: hashes[index - 1], prevChar: stringChars[index - 1], newChar: stringChars[index + sub.count - 1], exp: sub.count - 1) + } + + let subStrHash = rabinFinterprint(Array(sub)) + + Hash: + for (index, hash) in hashes.enumerated() { + if hash == subStrHash { + for (subIndex, char) in sub.enumerated() { + if stringChars[index + subIndex] != char { + continue Hash + } + } + return index + } + } + + return nil + } + + private func rabinFinterprint(_ c: [Character]) -> Int { + var hash = 0 + var index = 0 + + for exp in stride(from: c.count - 1, to: -1, by: -1) { + hash += Int(c[index].asciiValue!) * Int(pow(Double(base), Double(exp))) + index += 1 + } + + hash = hash % hashSize + + if hash < 0 { // in case overflows occurs + hash += hashSize; + } + + return hash + } + + private func shiftedRabin(prevHash: Int, prevChar: Character, newChar: Character, exp: Int) -> Int { + let hashOfPrevChar = Int(prevChar.asciiValue!) * Int(pow(Double(base), Double(exp))) + let leftShiftedValue = (prevHash - hashOfPrevChar) * base + var hash = leftShiftedValue + Int(newChar.asciiValue!) + + hash = hash % hashSize + + if hash < 0 { // in case overflows occurs + hash += hashSize; + } + + return hash + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/Algorithms/TopologicalSort.swift b/SPM/Sources/DataStructuresAlgorithms/Algorithms/TopologicalSort.swift new file mode 100644 index 0000000..3f1664f --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/Algorithms/TopologicalSort.swift @@ -0,0 +1,57 @@ +// +// TopologicalSort.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 01.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class TopologicalSort { + private var graph: DirectedGraph + + public init(graph: DirectedGraph) { + self.graph = graph + } + + open func topologicalSort() throws -> Queue { + let sort = Queue() + let processNext = Queue() + + for vertice in graph.vertices { + let neighbours = graph.neighbours(v: vertice) + var currentNeighbour = neighbours.head + while let current = currentNeighbour { + current.val.inboundCount += 1 + currentNeighbour = current.next + } + } + + for vertice in graph.vertices { + if vertice.inboundCount == 0 { + processNext.enqueue(val: vertice) + } + } + + while !processNext.isEmpty(), let vertice = try? processNext.dequeue() { + sort.enqueue(val: vertice) + let neighbours = graph.neighbours(v: vertice) + var currentNeighbour = neighbours.head + while let current = currentNeighbour { + current.val.inboundCount -= 1 + if current.val.inboundCount == 0 { + processNext.enqueue(val: current.val) + } + + currentNeighbour = current.next + } + } + + if sort.size != graph.vertices.count { + throw NSError(domain: "topologicalSort: There is a cycle in the graph", code: 0, userInfo: nil) + } + + return sort + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/Algorithms/TravelingSalesman.swift b/SPM/Sources/DataStructuresAlgorithms/Algorithms/TravelingSalesman.swift new file mode 100644 index 0000000..e7baf56 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/Algorithms/TravelingSalesman.swift @@ -0,0 +1,53 @@ +// +// TravellingSalesman.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 27.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class TravelingSalesman { + + private var graph: WeightedUndirectedGraphWithAdjMatrix + public var minimumDistance = Int.max + + public init(graph: WeightedUndirectedGraphWithAdjMatrix) { + self.graph = graph //this should be a complete graph + } + + open func travelingSalesman(startCity: Vertice) -> [Int] { + var cities: [Int] = [] + + for city in graph.vertices { + if city != startCity { + cities.append(city.id) + } + } + + var permutations = Permuation().heapsPermutations(array: &cities) + + var minimumRoute: [Int] = [] + + while var currentPermutation = permutations.popLast() { + + currentPermutation.append(startCity.id) + + var currentDistance = 0 + var currentCity = startCity.id + for city in currentPermutation { + currentDistance += graph.adjMatrix[currentCity][city] + currentCity = city + } + + if currentDistance < minimumDistance { + minimumDistance = currentDistance + currentPermutation.insert(startCity.id, at: 0) + minimumRoute = currentPermutation + } + } + + return minimumRoute + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/AVLTree.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/AVLTree.swift new file mode 100644 index 0000000..e013153 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/AVLTree.swift @@ -0,0 +1,332 @@ +// +// AVLTree.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 05.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class AVLTreeNode { + + public var left: AVLTreeNode? + public var right: AVLTreeNode? + public var parent: AVLTreeNode? + public var value: T + public var height: Int + + public init(parent: AVLTreeNode?, value: T, height: Int) { + self.parent = parent + self.value = value + self.height = height + } + + open func isRoot() -> Bool { + return parent == nil + } + + open func other() -> AVLTreeNode? { + if let left = parent?.left, left.value == value { + return parent?.right + } + + return parent?.left + } +} + +open class AVLTree { + public var root: AVLTreeNode? + public var count = 0 + + public init() { } + + open func insert(val: T) { + count += 1 + + if root == nil { + root = AVLTreeNode(parent: nil, value: val, height: 1) + return + } + + var current = root + while current != nil, let cur = current { + if cur.value > val { + if cur.left == nil { + cur.left = AVLTreeNode(parent: cur, value: val, height: 1) + increaseHeight(cur) + return + } else { + current = cur.left + } + } else { + if cur.right == nil { + cur.right = AVLTreeNode(parent: cur, value: val, height: 1) + increaseHeight(cur) + return + } else { + current = cur.right + } + } + } + } + + @discardableResult + open func deleteTop() -> AVLTreeNode? { + guard let value = root?.value else { + return nil + } + + count -= 1 + + return delete(value: value, root: &root) + } + + private func delete(value: T, root: inout AVLTreeNode?) -> AVLTreeNode? { + // step 1: Standard BST deletion + guard let currentRoot = root else { + return nil + } + + if value < currentRoot.value { + // If the key to be deleted is smaller than the root's key, then it lies in left subtree + currentRoot.left = delete(value: value, root: ¤tRoot.left) + } else if value > currentRoot.value { + // If the key to be deleted is greater than the root's key, then it lies in right subtree + currentRoot.right = delete(value: value, root: ¤tRoot.right) + } else { + // if key is same as root's key, then this is the node to be deleted + + // node with only one child or no child + if currentRoot.left == nil || currentRoot.right == nil { + root = currentRoot.right == nil ? currentRoot.left : currentRoot.right //could be no or one child + } else { + // node with two children: Get the inorder successor (smallest in the right subtree) + let temp = min(from: currentRoot.right)! + + // Copy the inorder successor's data to this node + currentRoot.value = temp.value + + // Delete the inorder successor + currentRoot.right = delete(value: temp.value, root: ¤tRoot.right) + } + } + + // If the tree had only one node then return + if root == nil { + return nil + } + + // step 2: Update height of the current node + currentRoot.height = max(currentRoot.left?.height ?? 0, currentRoot.right?.height ?? 0) + 1 + + // step 3: Get the balance factor of this node (to check whether this node became unbalanced) + let rootBalance = balance(of: currentRoot) + + // If this node becomes unbalanced, then there are 4 cases Left Left Case + if rootBalance > 1 && balance(of: currentRoot.left) >= 0 { + return rightRotateForDelete(y: currentRoot) + } + + // Left Right Case + if rootBalance > 1 && balance(of: currentRoot.left) < 0 { + currentRoot.left = leftRotateForDelete(x: currentRoot.left) + return rightRotateForDelete(y: currentRoot) + } + + // Right Right Case + if (rootBalance < -1 && balance(of: currentRoot.right) <= 0) { + return leftRotateForDelete(x: currentRoot) + } + + // Right Left Case + if (rootBalance < -1 && balance(of: currentRoot.right) > 0) + { + currentRoot.right = rightRotateForDelete(y: currentRoot.right); + return leftRotateForDelete(x: currentRoot) + } + + return root + } + + open func minNode() -> AVLTreeNode? { + return min(from: root) + } + + open func min(from node: AVLTreeNode?) -> AVLTreeNode? { + var curNode = node + while curNode?.left != nil { + curNode = curNode?.left + } + + return curNode + } + + // Get Balance factor of node N + private func balance(of node: AVLTreeNode?) -> Int { + guard let left = node?.left, let right = node?.right else { + return 0 + } + + return left.height - right.height + } + + private func increaseHeight(_ node: AVLTreeNode) { + if node.left != nil, node.right != nil { + return //must be balanced + } + + var current: AVLTreeNode? = node + while current != nil, let cur = current { + let leftHeight = cur.left?.height ?? 0 + let rightHeight = cur.right?.height ?? 0 + + cur.height = max(leftHeight, rightHeight) + 1 + + let heightDiff = leftHeight - rightHeight + + if heightDiff == 0 { + return + } else if heightDiff == -2 { + let rightLeftHeight = cur.right?.left?.height ?? 0 + let rightRightHeight = cur.right?.right?.height ?? 0 + + if rightLeftHeight > rightRightHeight { + //right left shape -> left + right rotation + let nodeToRotate = cur.right?.left + rightRotate(n: nodeToRotate) + leftRotate(n: nodeToRotate) + } else { + //right right shape -> right rotation + leftRotate(n: cur.right) + } + current = cur.parent + } else if heightDiff == 2 { + let leftLeftHeight = cur.left?.left?.height ?? 0 + let leftRightHeight = cur.left?.right?.height ?? 0 + + if leftRightHeight > leftLeftHeight { + //left right shape -> left + right rotation + let nodeToRotate = cur.left?.right + leftRotate(n: nodeToRotate) + rightRotate(n: nodeToRotate) + } else { + //left left shape -> right rotation + rightRotate(n: cur.left) + } + current = cur.parent + } else { + current = cur.parent + } + } + } + + open func description() -> Stack { + var stack = Stack() + description(node: root, stack: &stack) + + return stack + } + + private func description(node: AVLTreeNode?, stack: inout Stack) { + if let node = node { + stack.push(val: "\(node.value): \(node.height)") + description(node: node.left, stack: &stack) + description(node: node.right, stack: &stack) + } + } + + private func rightRotate(n: AVLTreeNode?) { + let p = n?.parent + let gp = p?.parent + let gpLeft = p?.value == gp?.left?.value + + p?.left = n?.right + p?.left?.parent = p + + n?.right = p + p?.parent = n // p? = n?.right + + n?.parent = gp + if gpLeft { + gp?.left = n + } else { + gp?.right = n + } + + if gp == nil { + root = n + } + + let pLeftHeight = p?.left?.height ?? 0 + let pRightHeight = p?.right?.height ?? 0 + p?.height = max(pLeftHeight, pRightHeight) + 1 + + let nLeftHeight = n?.left?.height ?? 0 + let nRightHeight = n?.right?.height ?? 0 + n?.height = max(nLeftHeight, nRightHeight) + 1 + } + + private func leftRotate(n: AVLTreeNode?) { + let p = n?.parent + let gp = p?.parent + let gpLeft = p?.value == gp?.left?.value + + p?.right = n?.left + p?.right?.parent = p + + n?.left = p + p?.parent = n + + n?.parent = gp + if gpLeft { + gp?.left = n + } else { + gp?.right = n + } + + if gp == nil { + root = n + } + + let pLeftHeight = p?.left?.height ?? 0 + let pRightHeight = p?.right?.height ?? 0 + p?.height = max(pLeftHeight, pRightHeight) + 1 + + let nLeftHeight = n?.left?.height ?? 0 + let nRightHeight = n?.right?.height ?? 0 + n?.height = max(nLeftHeight, nRightHeight) + 1 + } + + private func rightRotateForDelete(y: AVLTreeNode?) -> AVLTreeNode? { + let x = y?.left + let T2 = x?.right + + // Perform rotation + x?.right = y + y?.left = T2 + + // Update heights + y?.height = max(y?.left?.height ?? 0, y?.right?.height ?? 0) + 1 + x?.height = max(x?.left?.height ?? 0, x?.right?.height ?? 0) + 1 + + // Return new root + return x + } + + private func leftRotateForDelete(x: AVLTreeNode?) -> AVLTreeNode? { + let y = x?.left + let T2 = y?.right + + // Perform rotation + y?.left = x + x?.right = T2 + + // Update heights + x?.height = max(x?.left?.height ?? 0, x?.right?.height ?? 0) + 1 + y?.height = max(y?.left?.height ?? 0, y?.right?.height ?? 0) + 1 + + // Return new root + return y + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/BTree.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/BTree.swift new file mode 100644 index 0000000..b4008a1 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/BTree.swift @@ -0,0 +1,233 @@ +// +// BTree.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 18.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class BTreeNode { + + public var degree: Int + + public var numberOfKeys: Int + public var numberOfChilds: Int + + public var keys: [T?] + public var childNodes: [BTreeNode?] + + private var maxKeys: Int { + return 2 * degree - 1 + } + + private var maxChilds: Int { + return 2 * degree + } + + public init(degree: Int) { + self.degree = degree + numberOfKeys = 0 + numberOfChilds = 0 + keys = [T?] (repeatElement(nil, count: 2 * degree - 1)) + childNodes = [BTreeNode?] (repeatElement(nil, count: 2 * degree)) + } + + open func traverse(stack: inout Stack) { + //inorder traversal + for keyIndex in 0 ... numberOfKeys { // numberOfChilds can be one greater than numberOfKeys + childNodes[keyIndex]?.traverse(stack: &stack) + if let key = keys[keyIndex] { + stack.push(val: key) + } + } + } + + open func search(key: T) -> Bool { + for nodeIndex in 0 ... numberOfKeys { // numberOfChilds can be one greater than numberOfKeys + if let child = childNodes[nodeIndex], child.search(key: key) { + return true + } + + if let keyAtIndex = keys[nodeIndex] { + if keyAtIndex == key { + return true + } + } + } + + return false + } + + open func isLeafNode() -> Bool { + return numberOfChilds == 0 + } + + open func isFull() -> Bool { + return numberOfKeys == maxKeys + } + + open func insert(key: T) throws { + if numberOfKeys == 0 { + try insert(key: key, at: 0) + } else { + for index in 0 ... numberOfKeys { + if let keyAtIndex = keys[index], keyAtIndex > key { + try insert(key: key, at: index - 1) + return + } + } + try insert(key: key, at: numberOfKeys) + } + } + + open func insert(key: T?, at index: Int) throws { + guard let key = key else { + return + } + + if numberOfKeys == maxKeys { + throw NSError(domain: "BTreeNode: BTreeNode is full", code: 0, userInfo: nil) + } + + for keyIndex in stride(from: numberOfKeys - 2, to: index + 1, by: -1) { + keys.insert(keys[keyIndex], at: keyIndex + 1) + } + + keys[index] = key + numberOfKeys += 1 + } + + open func addChild(child: BTreeNode?, at index: Int) throws { + guard let child = child else { + return + } + + if numberOfChilds == maxChilds { + throw NSError(domain: "BTreeNode: BTreeNode is full", code: 0, userInfo: nil) + } + + for childIndex in stride(from: numberOfChilds - 2, to: index + 1, by: -1) { + childNodes.insert(childNodes[childIndex], at: childIndex + 1) + } + + childNodes[index] = child + numberOfChilds += 1 + } + + open func replaceChild(child: BTreeNode?, at index: Int) { + if child != nil, childNodes[index] == nil { + numberOfChilds += 1 + } + + childNodes[index] = child + } +} + +open class BTree { + public var root: BTreeNode? + + public init(root: BTreeNode) { + self.root = root + } + + open func traverse() -> Stack { + var stack = Stack() + + guard let root = root else { + return stack + } + + root.traverse(stack: &stack) + + return stack + } + + open func search(key: T) -> Bool { + guard let root = root else { + return false + } + + return root.search(key: key) + } + + open func insert(key: T) throws { + guard var root = root else { + return + } + + if root.isFull() { + root = try splitChild(root, of: nil, childIndex: 0) + self.root = root + } + + var currentNode = root + + while !currentNode.isLeafNode() { + var childIndex = currentNode.numberOfChilds - 1 + for keysIndex in 0 ... currentNode.numberOfKeys { + if let keysValue = currentNode.keys[keysIndex], keysValue > key { + childIndex = keysIndex + break + } + } + + let childNode = currentNode.childNodes[childIndex]! + if !childNode.isFull() { + currentNode = currentNode.childNodes[childIndex]! + } + else { + currentNode = try splitChild(childNode, of: currentNode, childIndex: childIndex) + + if key < currentNode.keys[childIndex]! { + currentNode = currentNode.childNodes[childIndex]! + } else { + currentNode = currentNode.childNodes[childIndex + 1]! + } + } + } + + try currentNode.insert(key: key) + } + + private func splitChild(_ child: BTreeNode, of root: BTreeNode?, childIndex: Int) throws -> BTreeNode { + let leftChild: BTreeNode = BTreeNode(degree: child.degree) + let rightChild: BTreeNode = BTreeNode(degree: child.degree) + + let parentNodeIndex = child.numberOfKeys / 2 + + for leftIndex in 0 ..< parentNodeIndex { + try leftChild.insert(key: child.keys[leftIndex], at: leftIndex) + } + + for leftIndex in 0 ... parentNodeIndex { + try leftChild.addChild(child: child.childNodes[leftIndex], at: leftIndex) + } + + for rightIndex in parentNodeIndex + 1 ..< child.numberOfKeys { + try rightChild.insert(key: child.keys[rightIndex], at: rightIndex - parentNodeIndex - 1) + } + + if child.numberOfChilds >= parentNodeIndex + 1 { + for rightIndex in parentNodeIndex + 1 ... child.numberOfChilds { + try rightChild.addChild(child: child.childNodes[rightIndex], at: rightIndex - parentNodeIndex - 1) + } + } + + if let root = root { + try root.insert(key: child.keys[parentNodeIndex], at: childIndex) + root.replaceChild(child: leftChild, at: childIndex) + try root.addChild(child: rightChild, at: childIndex + 1) + + return root + } else { + let rootNode = BTreeNode(degree: child.degree) + try rootNode.insert(key: child.keys[parentNodeIndex], at: childIndex) + try rootNode.addChild(child: leftChild, at: 0) + try rootNode.addChild(child: rightChild, at: 1) + + return rootNode + } + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/BitVector.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/BitVector.swift new file mode 100644 index 0000000..09b35f4 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/BitVector.swift @@ -0,0 +1,47 @@ +// +// BitVector.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 03.05.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class BitVector { + private let numberOfBits: Int + private var bitStorage: [UInt64] + private let base = 64 + + public init(numberOfBits: Int) { + self.numberOfBits = numberOfBits + + //storage has place for 64 bits per UInt64 array entry. Round up. + let arraySize = (numberOfBits + base - 1) / base + bitStorage = [UInt64] (repeating: 0, count: arraySize) + } + + open func setBit(index: Int) { + let arrayIndex = index / base + let bitIndex = index % base + let bit: UInt64 = 1 << bitIndex + + bitStorage[arrayIndex] |= bit + } + + open func unsetBit(index: Int) { + let arrayIndex = index / base + let bitIndex = index % base + let bit: UInt64 = ~(1 << bitIndex) + + bitStorage[arrayIndex] &= bit + } + + open func isBitSet(index: Int) -> Bool { + let arrayIndex = index / base + let bitIndex = index % base + let bit: UInt64 = 1 << bitIndex + + return bitStorage[arrayIndex] & bit > 0 + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/CompressedAVLChildCountTree.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/CompressedAVLChildCountTree.swift new file mode 100644 index 0000000..2605b07 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/CompressedAVLChildCountTree.swift @@ -0,0 +1,279 @@ +// +// CompressedAVLChildCountTree.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 27.09.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class CompressedAVLChildCountTreeNode { + public var left: CompressedAVLChildCountTreeNode? + public var right: CompressedAVLChildCountTreeNode? + public var parent: CompressedAVLChildCountTreeNode? + public var value: T + public var height: Int + + //Count of value duplicates in tree node + public var valueCount: Int + + public var numElementsInSubtree: Int + + public var totalRank: Int { + return valueCount - 1 + leftChildRank + } + + public var rightChildRank: Int { + return right?.numElementsInSubtree ?? 0 + } + + public var leftChildRank: Int { + return left?.numElementsInSubtree ?? 0 + } + + public init(parent: CompressedAVLChildCountTreeNode?, value: T, valueCount: Int, numElementsInSubtree: Int, height: Int) { + self.parent = parent + self.value = value + self.valueCount = valueCount + self.numElementsInSubtree = numElementsInSubtree + self.height = height + } + + open func isRoot() -> Bool { + return parent == nil + } + + open func other() -> CompressedAVLChildCountTreeNode? { + if let left = parent?.left, left.value == value { + return parent?.right + } + + return parent?.left + } +} + +open class CompressedAVLChildCountTree { + public var root: CompressedAVLChildCountTreeNode? + + public init() { } + + open func insert(val: T) { + if root == nil { + root = CompressedAVLChildCountTreeNode(parent: nil, value: val, valueCount: 1, numElementsInSubtree: 1, height: 1) + return + } + + var current = root + while current != nil, let cur = current { + if cur.value == val { + cur.valueCount += 1 + cur.numElementsInSubtree += 1 + increaseHeightAndNumElementsInSubtree(cur, duplicate: true) + break + } else if cur.value > val { + if cur.left == nil { + cur.left = CompressedAVLChildCountTreeNode(parent: cur, value: val, valueCount: 1, numElementsInSubtree: 1, height: 1) + increaseHeightAndNumElementsInSubtree(cur, duplicate: false) + return + } else { + current = cur.left + } + } else { + if cur.right == nil { + cur.right = CompressedAVLChildCountTreeNode(parent: cur, value: val, valueCount: 1, numElementsInSubtree: 1, height: 1) + increaseHeightAndNumElementsInSubtree(cur, duplicate: false) + return + } else { + current = cur.right + } + } + } + } + + open func rank(of value: T) -> Int { + guard root != nil else { + return 0 + } + + if let root = root, root.value == value { + return root.totalRank + } + + var currentNode = root + var pathCount = 0 + + while let curNode = currentNode, curNode.value != value { + if curNode.value > value { + //go left + if curNode.left == nil { + break + } else { + currentNode = curNode.left + } + } else { + //go right + if curNode.right == nil { + break + } else { + pathCount += curNode.totalRank + 1 + currentNode = curNode.right + } + } + } + + guard let curNode = currentNode else { + return 0 + } + + if curNode.value == value { + //exact match + return pathCount + curNode.totalRank + } else { + //element is bigger or smaller + return pathCount + (curNode.value < value ? curNode.totalRank + 1 : 0) + } + } + + private func increaseHeightAndNumElementsInSubtree(_ node: CompressedAVLChildCountTreeNode, duplicate: Bool) { + if node.left != nil, node.right != nil { + return //must be balanced + } + + var current: CompressedAVLChildCountTreeNode? = node + while current != nil, let cur = current { + let leftNumElementsInSubtree = cur.left?.numElementsInSubtree ?? 0 + let rightNumElementsInSubtree = cur.right?.numElementsInSubtree ?? 0 + let leftHeight = cur.left?.height ?? 0 + let rightHeight = cur.right?.height ?? 0 + + cur.numElementsInSubtree = leftNumElementsInSubtree + rightNumElementsInSubtree + cur.valueCount + cur.height = max(leftHeight, rightHeight) + 1 + + let heightDiff = leftHeight - rightHeight + + if heightDiff == 0, !duplicate { + return + } else if heightDiff == -2 { + let rightLeftHeight = cur.right?.left?.height ?? 0 + let rightRightHeight = cur.right?.right?.height ?? 0 + + if rightLeftHeight > rightRightHeight { + //right left shape -> left + right rotation + let nodeToRotate = cur.right?.left + rightRotate(n: nodeToRotate) + leftRotate(n: nodeToRotate) + } else { + //right right shape -> right rotation + leftRotate(n: cur.right) + } + current = cur.parent + } else if heightDiff == 2 { + let leftLeftHeight = cur.left?.left?.height ?? 0 + let leftRightHeight = cur.left?.right?.height ?? 0 + + if leftRightHeight > leftLeftHeight { + //left right shape -> left + right rotation + let nodeToRotate = cur.left?.right + leftRotate(n: nodeToRotate) + rightRotate(n: nodeToRotate) + } else { + //left left shape -> right rotation + rightRotate(n: cur.left) + } + current = cur.parent + } else { + current = cur.parent + } + } + } + + open func description() -> Stack { + var stack = Stack() + description(node: root, stack: &stack) + + return stack + } + + private func description(node: CompressedAVLChildCountTreeNode?, stack: inout Stack) { + if let node = node { + stack.push(val: "\(node.value): \(node.numElementsInSubtree) - (duplicates \(node.valueCount))") + description(node: node.left, stack: &stack) + description(node: node.right, stack: &stack) + } + } + + private func rightRotate(n: CompressedAVLChildCountTreeNode?) { + let p = n?.parent + let gp = p?.parent + let gpLeft = p?.value == gp?.left?.value + + p?.left = n?.right + p?.left?.parent = p + + n?.right = p + p?.parent = n // p? = n?.right + + n?.parent = gp + if gpLeft { + gp?.left = n + } else { + gp?.right = n + } + + if gp == nil { + root = n + } + + let pLeftNumElementsInSubtree = p?.left?.numElementsInSubtree ?? 0 + let pRightNumElementsInSubtree = p?.right?.numElementsInSubtree ?? 0 + p?.numElementsInSubtree = pLeftNumElementsInSubtree + pRightNumElementsInSubtree + (p?.valueCount ?? 0) + let pLeftHeight = p?.left?.height ?? 0 + let pRightHeight = p?.right?.height ?? 0 + p?.height = max(pLeftHeight, pRightHeight) + 1 + + let nLeftNumElementsInSubtree = n?.left?.numElementsInSubtree ?? 0 + let nRightNumElementsInSubtree = n?.right?.numElementsInSubtree ?? 0 + n?.numElementsInSubtree = nLeftNumElementsInSubtree + nRightNumElementsInSubtree + (n?.valueCount ?? 0) + let nLeftHeight = n?.left?.height ?? 0 + let nRightHeight = n?.right?.height ?? 0 + n?.height = max(nLeftHeight, nRightHeight) + 1 + } + + private func leftRotate(n: CompressedAVLChildCountTreeNode?) { + let p = n?.parent + let gp = p?.parent + let gpLeft = p?.value == gp?.left?.value + + p?.right = n?.left + p?.right?.parent = p + + n?.left = p + p?.parent = n + + n?.parent = gp + if gpLeft { + gp?.left = n + } else { + gp?.right = n + } + + if gp == nil { + root = n + } + + let pLeftNumElementsInSubtree = p?.left?.numElementsInSubtree ?? 0 + let pRightNumElementsInSubtree = p?.right?.numElementsInSubtree ?? 0 + p?.numElementsInSubtree = pLeftNumElementsInSubtree + pRightNumElementsInSubtree + (p?.valueCount ?? 0) + let pLeftHeight = p?.left?.height ?? 0 + let pRightHeight = p?.right?.height ?? 0 + p?.height = max(pLeftHeight, pRightHeight) + 1 + + let nLeftNumElementsInSubtree = n?.left?.numElementsInSubtree ?? 0 + let nRightNumElementsInSubtree = n?.right?.numElementsInSubtree ?? 0 + n?.numElementsInSubtree = nLeftNumElementsInSubtree + nRightNumElementsInSubtree + (n?.valueCount ?? 0) + let nLeftHeight = n?.left?.height ?? 0 + let nRightHeight = n?.right?.height ?? 0 + n?.height = max(nLeftHeight, nRightHeight) + 1 + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/DirectedGraph.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/DirectedGraph.swift new file mode 100644 index 0000000..17a7a37 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/DirectedGraph.swift @@ -0,0 +1,36 @@ +// +// DirectedGraph.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 01.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class DirectedGraph { + public let vertices: [Vertice] + private var adjList: [DoubleLinkedList] = [] + + public init(vertices: [Vertice]) { + self.vertices = vertices + + for _ in vertices { + adjList.append(DoubleLinkedList()) + } + } + + open func addEdge(v1: Vertice, v2: Vertice) { + adjList[v1.id].add(node: Node(val: v2)) + } + + open func removeEdge(v1: Vertice, v2: Vertice) { + if adjList.count > v1.id { + adjList[v1.id].removeByKey(val: v2.id) + } + } + + open func neighbours(v: Vertice) -> DoubleLinkedList { + return adjList[v.id] + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/DoubleLinkedList.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/DoubleLinkedList.swift new file mode 100644 index 0000000..96e3fdd --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/DoubleLinkedList.swift @@ -0,0 +1,122 @@ +import Foundation + +public enum MyError: Error { + case runtimeError(String) +} + +open class Node: Hashable { + var next: Node? + var val: T + var prev: Node? + + init(val: T, next: Node? = nil, prev: Node? = nil) { + self.val = val + self.next = next + self.prev = prev + } + + public static func == (lhs: Node, rhs: Node) -> Bool { + return lhs.val == rhs.val && lhs.next == rhs.next && lhs.prev == rhs.prev + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(val) + hasher.combine(next) + hasher.combine(prev) + } +} + +open class DoubleLinkedList { + public var head: Node? + public var tail: Node? + public var count: Int = 0 + + public init() { } + + open func add(node: Node) { + if count == 0 { + head = node + tail = node + } else { + let temp = tail + tail = node + temp?.next = tail + tail?.prev = temp + } + count += 1 + } + + open func removeFirst() throws -> Node? { + if count == 0 { + throw MyError.runtimeError("Invalid call") + } + + let node = head + + if count == 1 { + head = nil; tail = nil + } else { + head = node?.next + head?.prev = nil + node?.next = nil + } + + count -= 1 + + return node + } + + open func removeLast() throws -> Node? { + if count == 0 { + throw MyError.runtimeError("Invalid call") + } + + let node = tail + + if count == 1 { + head = nil; tail = nil + } else { + tail?.prev?.next = nil + tail = tail?.prev + } + + count -= 1 + + return node + } + + open func removeByKey(val: Int) { + var current = head + + while current != nil { + if let cur = current, let curVal = current?.val as? Int, curVal == val, let headVal = head?.val as? Int, let tailVal = tail?.val as? Int { + if count == 1 { + head = nil; tail = nil; + } else if curVal == headVal { + head?.next?.prev = nil + head = head?.next + } else if curVal == tailVal { + tail?.prev?.next = nil + tail = tail?.prev + } else { + if let prev = cur.prev { + prev.next = cur.next + } + if let next = cur.next { + next.prev = cur.prev + } + } + + if headVal == tailVal { + head = tail + } + + count -= 1 + + return + } + + current = current?.next + } + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/Edge.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/Edge.swift new file mode 100644 index 0000000..779366c --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/Edge.swift @@ -0,0 +1,35 @@ +// +// Edge.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 02.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class Edge: Comparable, Hashable { + public let from: Vertice + public let to: Vertice + public let weight: Int + + public init(from: Vertice, to: Vertice, weight: Int) { + self.from = from + self.to = to + self.weight = weight + } + + public static func < (lhs: Edge, rhs: Edge) -> Bool { + return lhs.weight < rhs.weight + } + + public static func == (lhs: Edge, rhs: Edge) -> Bool { + return lhs.from == rhs.from && lhs.to == rhs.to + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(from) + hasher.combine(to) + hasher.combine(weight) + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/HashTable.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/HashTable.swift new file mode 100644 index 0000000..98fc3af --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/HashTable.swift @@ -0,0 +1,53 @@ +// +// HashTable.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 26.03.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class HashTable { + private var table: [DoubleLinkedList] = [] + + public init(size: Int) { + table = [DoubleLinkedList] (repeating: DoubleLinkedList(), count: size) + for i in 0 ... size - 1 { + table[i] = DoubleLinkedList() + } + } + + open func hash(val: String) -> Int { + var hash: Int = 0 + var index: Int = 0 + + for char in Array(val) { + index += 1 + hash += Int(char.asciiValue!) * index + } + + hash %= table.count + + return Int(hash) + } + + open func insert(val: String) { + let h = hash(val: val) + table[h].add(node: Node(val: val)) + } + + open func contains(val: String) -> Bool { + let h = hash(val: val) + let list = table[h] + + var curNode = list.head + while curNode != nil { + if curNode?.val == val { + return true + } + curNode = curNode?.next + } + return false + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/IndexedMinPriorityQueue.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/IndexedMinPriorityQueue.swift new file mode 100644 index 0000000..d1b65d3 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/IndexedMinPriorityQueue.swift @@ -0,0 +1,179 @@ +// +// IndexedMinPriorityQueue.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 15.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class IndexedMinPriorityQueue { + private var keys = [T?]() + private var keyIndeces: [Int] //qp + private var heapIndeces = [Int]() //pq + private var maxElements: Int = 0 + + public var size: Int = 0 + + public init(maxElements: Int) { + keyIndeces = [Int] (repeating: -1, count: maxElements) + heapIndeces = [Int] (repeating: Int.max, count: maxElements) + keys = [T?] (repeating: nil, count: maxElements) + self.maxElements = maxElements + } + + open func isEmpty() -> Bool { + return size == 0 + } + + open func contains(index: Int) -> Bool { + return keyIndeces[index] != -1 + } + + + open func insert(index: Int, key: T) throws { + if size >= maxElements { + throw NSError(domain: "IndexedMinPriorityQueue: PQ is full", code: 0, userInfo: nil) + } + + keys[index] = key + keyIndeces[index] = size + heapIndeces[size] = index + + swim(index: size) + size += 1 + } + + open func minIndex() throws -> Int { + if size < 1 { + throw NSError(domain: "IndexedMinPriorityQueue: no elements in PQ", code: 0, userInfo: nil) + } + + return heapIndeces[0] + } + + open func minKey() throws -> T? { + if size < 1 { + throw NSError(domain: "IndexedMinPriorityQueue: no elements in PQ", code: 0, userInfo: nil) + } + + return keys[heapIndeces[0]] + } + + open func extractMin() throws -> T? { + let key = try minKey() + let index = try minIndex() + + exchange(index1: 0, index2: size - 1) + size -= 1 + sink(index: 0) + + keyIndeces[index] = -1 + keys[index] = nil + heapIndeces[size] = Int.max + + return key + } + + open func delete(index: Int) throws { + if !contains(index: index) { + throw NSError(domain: "IndexedMinPriorityQueue: Element not in queue", code: 0, userInfo: nil) + } + + let heapIndex = keyIndeces[index] + exchange(index1: heapIndex, index2: size - 1) + size -= 1 + swim(index: heapIndex) + sink(index: heapIndex) + + keyIndeces[index] = -1 + keys[index] = nil + heapIndeces[size] = Int.max + } + + open func keyOf(index: Int) -> T? { + return keys[index] + } + + open func changeKey(index: Int, key: T) throws { + if !contains(index: index) { + throw NSError(domain: "IndexedMinPriorityQueue: Element not in queue", code: 0, userInfo: nil) + } + + keys[index] = key + sink(index: keyIndeces[index]) + swim(index: keyIndeces[index]) + } + + open func decreaseKey(index: Int, key: T) throws { + if !contains(index: index) { + throw NSError(domain: "IndexedMinPriorityQueue: Element not in queue", code: 0, userInfo: nil) + } + + if let currentKey = keys[index], currentKey < key { + throw NSError(domain: "IndexedMinPriorityQueue: Called decreaseKey with increased or equal value", code: 0, userInfo: nil) + } + + keys[index] = key + swim(index: keyIndeces[index]) + } + + open func increaseKey(index: Int, key: T) throws { + if !contains(index: index) { + throw NSError(domain: "IndexedMinPriorityQueue: Element not in queue", code: 0, userInfo: nil) + } + + if let currentKey = keys[index], currentKey > key { + throw NSError(domain: "IndexedMinPriorityQueue: Called increaseKey with decreased or equal value", code: 0, userInfo: nil) + } + + keys[index] = key + sink(index: keyIndeces[index]) + } + + private func greater(_ i: Int?, _ j: Int?) -> Bool { + guard let i = i, let j = j, i >= 0, i < size, j >= 0, j < size, + heapIndeces[i] < keys.count, heapIndeces[j] < keys.count, + heapIndeces[i] >= 0, heapIndeces[j] >= 0, + let key1 = keys[heapIndeces[i]], let key2 = keys[heapIndeces[j]] else { + return false + } + + return key1 > key2 + } + + private func swim(index: Int) { + if greater((index - 1) / 2, index) { + exchange(index1: index, index2: ((index - 1) / 2)) + swim(index: (index - 1) / 2) + } + } + + private func sink(index: Int) { + let key1Index = 2 * index + 1 + let key2Index = 2 * index + 2 + + if greater(index, key1Index) || greater(index, key2Index) { + if greater(key1Index, key2Index) { + exchange(index1: index, index2: key2Index) + sink(index: key2Index) + } else { + exchange(index1: index, index2: key1Index) + sink(index: key1Index) + } + } else if greater(index, key1Index) { + exchange(index1: index, index2: key1Index) + sink(index: key1Index) + } + } + + private func exchange(index1: Int, index2: Int) { + let temp = heapIndeces[index1] + heapIndeces[index1] = heapIndeces[index2] + heapIndeces[index2] = temp + + keyIndeces[heapIndeces[index1]] = index1 + keyIndeces[heapIndeces[index2]] = index2 + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/IntervalTree.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/IntervalTree.swift new file mode 100644 index 0000000..8489248 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/IntervalTree.swift @@ -0,0 +1,315 @@ +// +// IntervalTree.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 19.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +public struct Interval { + var low: T + var high: T + + static func == (lhs: Interval, rhs: Interval) -> Bool { + return lhs.low == rhs.low && lhs.high == rhs.high + } + + static func < (lhs: Interval, rhs: Interval) -> Bool { + return lhs.low < rhs.low + } + + static func > (lhs: Interval, rhs: Interval) -> Bool { + return lhs.low > rhs.low + } +} + +open class IntervalNode { + public var interval: Interval + public var minMaxInterval: Interval + public var left: IntervalNode? + public var right: IntervalNode? + public var parent: IntervalNode? + public var height: Int + + init(parent: IntervalNode? = nil, interval: Interval, height: Int) { + self.parent = parent + self.interval = interval + minMaxInterval = interval + self.height = height + } +} + +open class IntervalTree { + public var root: IntervalNode? + + public init(root: IntervalNode) { + self.root = root + } + + open func overlapsInterval(interval: Interval) -> Interval? { + guard let root = root else { + return nil + } + + return overlapsInterval(interval: interval, node: root) + } + + private func overlapsInterval(interval: Interval, node: IntervalNode) -> Interval? { + if interval.low < node.interval.high && interval.high >= node.interval.high //interval overlaps right + || interval.low <= node.interval.low && interval.high > node.interval.low //interval overlaps left + || interval.low >= node.interval.low && interval.high <= node.interval.high //interval inside + || interval.low <= node.interval.low && interval.high >= node.interval.high //interval completely overlaps + { + return node.interval //overlaps + } + + if let leftChild = node.left, leftChild.minMaxInterval.high > interval.low, leftChild.minMaxInterval.low < node.minMaxInterval.high { + return overlapsInterval(interval: interval, node: leftChild) + } + + if let rightChild = node.right, rightChild.minMaxInterval.high > interval.low, rightChild.minMaxInterval.low < interval.high { + return overlapsInterval(interval: interval, node: rightChild) + } + + return nil + } + + open func insert(interval: Interval) { + if root == nil { + root = IntervalNode(parent: nil, interval: interval, height: 1) + return + } + + var current = root + while current != nil, let cur = current { + if cur.interval > interval { + if cur.left == nil { + cur.left = IntervalNode(parent: cur, interval: interval, height: 1) + increaseHeightAndSetIntervals(cur) + return + } else { + current = cur.left + } + } else { + if cur.right == nil { + cur.right = IntervalNode(parent: cur, interval: interval, height: 1) + increaseHeightAndSetIntervals(cur) + return + } else { + current = cur.right + } + } + } + } + + private func increaseHeightAndSetIntervals(_ node: IntervalNode) { + if node.left != nil, node.right != nil { + return //must be balanced + } + + var current: IntervalNode? = node + while current != nil, let cur = current { + let leftHeight = cur.left?.height ?? 0 + let rightHeight = cur.right?.height ?? 0 + + cur.height = max(leftHeight, rightHeight) + 1 + + let leftInterval = cur.left?.minMaxInterval ?? nil + let rightInterval = cur.right?.minMaxInterval ?? nil + + if let interval = leftInterval { + cur.minMaxInterval.low = min(cur.minMaxInterval.low, interval.low) + cur.minMaxInterval.high = max(cur.minMaxInterval.high, interval.high) + } + + if let interval = rightInterval { + cur.minMaxInterval.low = min(cur.minMaxInterval.low, interval.low) + cur.minMaxInterval.high = max(cur.minMaxInterval.high, interval.high) + } + + let heightDiff = leftHeight - rightHeight + + if heightDiff == 0 { + return + } else if heightDiff == -2 { + let rightLeftHeight = cur.right?.left?.height ?? 0 + let rightRightHeight = cur.right?.right?.height ?? 0 + + if rightLeftHeight > rightRightHeight { + //right left shape -> left + right rotation + let nodeToRotate = cur.right?.left + rightRotate(n: nodeToRotate) + leftRotate(n: nodeToRotate) + } else { + //right right shape -> right rotation + leftRotate(n: cur.right) + } + current = cur.parent + } else if heightDiff == 2 { + let leftLeftHeight = cur.left?.left?.height ?? 0 + let leftRightHeight = cur.left?.right?.height ?? 0 + + if leftRightHeight > leftLeftHeight { + //left right shape -> left + right rotation + let nodeToRotate = cur.left?.right + leftRotate(n: nodeToRotate) + rightRotate(n: nodeToRotate) + } else { + //left left shape -> right rotation + rightRotate(n: cur.left) + } + current = cur.parent + } else { + current = cur.parent + } + } + } + + open func description() -> Stack { + var stack = Stack() + description(node: root, stack: &stack) + + return stack + } + + private func description(node: IntervalNode?, stack: inout Stack) { + if let node = node { + stack.push(val: "Low/High: \(node.interval.low)-\(node.interval.high), Min/Max: \(node.minMaxInterval.low)-\(node.minMaxInterval.high), Height: \(node.height)") + + description(node: node.left, stack: &stack) + description(node: node.right, stack: &stack) + } + } + + private func rightRotate(n: IntervalNode?) { + let p = n?.parent + let gp = p?.parent + let gpLeft = p?.interval.low == gp?.left?.interval.low && p?.interval.high == gp?.left?.interval.high + + p?.left = n?.right + p?.left?.parent = p + + n?.right = p; + p?.parent = n // p? = n?.right + + n?.parent = gp + if gpLeft { + gp?.left = n + } else { + gp?.right = n + } + + if gp == nil { + root = n + } + + let pLeftHeight = p?.left?.height ?? 0 + let pRightHeight = p?.right?.height ?? 0 + p?.height = max(pLeftHeight, pRightHeight) + 1 + + let pLeftInterval = p?.left?.minMaxInterval ?? nil + let pRightInterval = p?.right?.minMaxInterval ?? nil + + if let p = p { + p.minMaxInterval = p.interval + + if let interval = pLeftInterval { + p.minMaxInterval.low = min(p.minMaxInterval.low, interval.low) + p.minMaxInterval.high = max(p.minMaxInterval.high, interval.high) + } + + if let interval = pRightInterval { + p.minMaxInterval.low = min(p.minMaxInterval.low, interval.low) + p.minMaxInterval.high = max(p.minMaxInterval.high, interval.high) + } + } + + let nLeftHeight = n?.left?.height ?? 0 + let nRightHeight = n?.right?.height ?? 0 + n?.height = max(nLeftHeight, nRightHeight) + 1 + + let nLeftInterval = n?.left?.minMaxInterval ?? nil + let nRightInterval = n?.right?.minMaxInterval ?? nil + + if let n = n { + n.minMaxInterval = n.interval + + if let interval = nLeftInterval { + n.minMaxInterval.low = min(n.minMaxInterval.low, interval.low) + n.minMaxInterval.high = max(n.minMaxInterval.high, interval.high) + } + + if let interval = nRightInterval { + n.minMaxInterval.low = min(n.minMaxInterval.low, interval.low) + n.minMaxInterval.high = max(n.minMaxInterval.high, interval.high) + } + } + } + + private func leftRotate(n: IntervalNode?) { + let p = n?.parent + let gp = p?.parent + let gpLeft = p?.interval.low == gp?.left?.interval.low && p?.interval.high == gp?.left?.interval.high + + p?.right = n?.left + p?.right?.parent = p + + n?.left = p + p?.parent = n + + n?.parent = gp + if gpLeft { + gp?.left = n + } else { + gp?.right = n + } + + if gp == nil { + root = n + } + + let pLeftHeight = p?.left?.height ?? 0 + let pRightHeight = p?.right?.height ?? 0 + p?.height = max(pLeftHeight, pRightHeight) + 1 + + let pLeftInterval = p?.left?.minMaxInterval ?? nil + let pRightInterval = p?.right?.minMaxInterval ?? nil + + if let p = p { + p.minMaxInterval = p.interval + + if let interval = pLeftInterval { + p.minMaxInterval.low = min(p.minMaxInterval.low, interval.low) + p.minMaxInterval.high = max(p.minMaxInterval.high, interval.high) + } + + if let interval = pRightInterval { + p.minMaxInterval.low = min(p.minMaxInterval.low, interval.low) + p.minMaxInterval.high = max(p.minMaxInterval.high, interval.high) + } + } + + let nLeftHeight = n?.left?.height ?? 0 + let nRightHeight = n?.right?.height ?? 0 + n?.height = max(nLeftHeight, nRightHeight) + 1 + + let nLeftInterval = n?.left?.minMaxInterval ?? nil + let nRightInterval = n?.right?.minMaxInterval ?? nil + + if let n = n { + n.minMaxInterval = n.interval + + if let interval = nLeftInterval { + n.minMaxInterval.low = min(n.minMaxInterval.low, interval.low) + n.minMaxInterval.high = max(n.minMaxInterval.high, interval.high) + } + + if let interval = nRightInterval { + n.minMaxInterval.low = min(n.minMaxInterval.low, interval.low) + n.minMaxInterval.high = max(n.minMaxInterval.high, interval.high) + } + } + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/LRUCache.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/LRUCache.swift new file mode 100644 index 0000000..3dec361 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/LRUCache.swift @@ -0,0 +1,100 @@ +// +// LRUCache.swift +// +// +// Created by Stefan Jaindl on 01.12.20. +// + +import Foundation + +open class LRUCache { + + private let cacheSize: Int + private var currentCount = 0 + private var cache = DoubleLinkedList() + private var lookup = [T: Node]() + + public init(cacheSize: Int) { + self.cacheSize = cacheSize + } + + open func insert(_ value: T) { + if let node = get(value) { + node.val = value + + //Promote node to top of cache + guard node != cache.tail else { + return //already at top of cache + } + + promote(node: node) + + return + } + + if isFull() { + removeHead() + } + + updateCache(with: Node(val: value)) + currentCount += 1 + } + + open func get(_ value: T) -> Node? { + guard let node = lookup[value] else { + return nil + } + + promote(node: node) + + return node + } + + private func promote(node: Node) { + let nextNode = node.next + node.prev?.next = nextNode + nextNode?.prev = node.prev + + node.next = nil + node.prev = cache.tail + cache.tail?.next = node + + cache.tail = node + } + + private func removeHead() { + guard let node = cache.head else { + return + } + + lookup.removeValue(forKey: node.val) + + let newHead = cache.head?.next + newHead?.prev = nil + cache.head?.next = nil + cache.head = newHead + + currentCount -= 1 + } + + private func updateCache(with node: Node) { + if currentCount == 0 { + cache.head = node + cache.tail = node + } else if currentCount == 1 { + cache.tail = node + node.prev = cache.head + cache.head?.next = node + } else { + cache.tail?.next = node + node.prev = cache.tail + cache.tail = node + } + + lookup[node.val] = node + } + + private func isFull() -> Bool { + return currentCount == cacheSize + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/MaxHeap.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/MaxHeap.swift new file mode 100644 index 0000000..4ec944d --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/MaxHeap.swift @@ -0,0 +1,78 @@ +// +// MaxHeap.swift +// +// +// Created by Stefan Jaindl on 11.12.20. +// + +import Foundation + +open class MaxHeap { + private var array = [T] () + + public init() { } + + open func insert(val: T) { + array.append(val) + swim(key: array.count - 1) + } + + open func peekMax() throws -> T { + if array.count < 1 { + throw NSError(domain: "Heap: no elements in heap", code: 0, userInfo: nil) + } + + return array[0] + } + + @discardableResult + open func extractMax() throws -> T { + let max = try peekMax() + array[0] = array[array.count - 1] + array.removeLast() + sink(key: 0) + + return max + } + + open func isEmpty() -> Bool { + return array.isEmpty + } + + open func numberOfElements() -> Int { + return array.count + } + + private func swim(key: Int) { + if array[key] > array[(key-1) / 2] { + exchange(index1: key, index2: ((key - 1) / 2)) + swim(key: (key - 1) / 2) + } + } + + private func sink(key: Int) { + let key1Index = 2 * key + 1 + let key2Index = 2 * key + 2 + + if array.count - 1 >= key2Index && + array[key] < max(array[key1Index], array[key2Index]) { + if array[key1Index] < array[key2Index] { + exchange(index1: key, index2: key2Index) + sink(key: key2Index) + } else { + exchange(index1: key, index2: key1Index) + sink(key: key1Index) + } + } else if array.count - 1 >= key1Index && + array[key] < array[key1Index] { + exchange(index1: key, index2: key1Index) + sink(key: key1Index) + } + } + + private func exchange(index1: Int, index2: Int) { + let temp = array[index1] + array[index1] = array[index2] + array[index2] = temp + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/MinHeap.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/MinHeap.swift new file mode 100644 index 0000000..d84e7c5 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/MinHeap.swift @@ -0,0 +1,79 @@ +// +// MinHeap.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 25.03.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class MinHeap { + private var array = [T] () + + public init() { } + + open func insert(val: T) { + array.append(val) + swim(key: array.count - 1) + } + + open func peekMin() throws -> T { + if array.count < 1 { + throw NSError(domain: "Heap: no elements in heap", code: 0, userInfo: nil) + } + + return array[0] + } + + @discardableResult + open func extractMin() throws -> T { + let min = try peekMin() + array[0] = array[array.count - 1] + array.removeLast() + sink(key: 0) + + return min + } + + open func isEmpty() -> Bool { + return array.isEmpty + } + + open func numberOfElements() -> Int { + return array.count + } + + private func swim(key: Int) { + if array[key] < array[(key-1) / 2] { + exchange(index1: key, index2: ((key - 1) / 2)) + swim(key: (key - 1) / 2) + } + } + + private func sink(key: Int) { + let key1Index = 2 * key + 1 + let key2Index = 2 * key + 2 + + if array.count - 1 >= key2Index && + array[key] > min(array[key1Index], array[key2Index]) { + if array[key1Index] > array[key2Index] { + exchange(index1: key, index2: key2Index) + sink(key: key2Index) + } else { + exchange(index1: key, index2: key1Index) + sink(key: key1Index) + } + } else if array.count - 1 >= key1Index && + array[key] > array[key1Index] { + exchange(index1: key, index2: key1Index) + sink(key: key1Index) + } + } + + private func exchange(index1: Int, index2: Int) { + let temp = array[index1] + array[index1] = array[index2] + array[index2] = temp + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/Queue.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/Queue.swift new file mode 100644 index 0000000..4e50b87 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/Queue.swift @@ -0,0 +1,77 @@ +// +// Queue.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 23.03.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class Queue { + private var items: [Node?] + private var capacity: Int = 10 + private var nextPos: Int = 0 + private var frontPos: Int = 0 + public var size: Int = 0 + + public init() { + items = [Node?](repeating: nil, count: capacity) + } + + open func enqueue(val: T) { + if size == items.count { + resizeArray(newSize: size * 2) + } + + if nextPos == items.count { + rebuildArray() + } + + let node = Node(val: val) + items[nextPos] = node + size += 1 + nextPos += 1 + } + + open func dequeue() throws -> T { + if size == 0 { + throw NSError(domain: "No element in queue", code: 0, userInfo: nil) + } + + if items.count < size / 4 { + resizeArray(newSize: size / 4) + } + + let item = items[frontPos] + frontPos += 1 + size -= 1 + + return item!.val + } + + open func isEmpty() -> Bool { + return size == 0 + } + + private func rebuildArray() { + var newArr = [Node?] (repeating: nil, count: items.count) + for n in frontPos ... nextPos { + newArr.append(items[n]) + } + items = newArr + nextPos -= frontPos + frontPos = 0 + } + + private func resizeArray(newSize: Int) { + var newArr = [Node?] (repeating: nil, count: newSize) + for n in frontPos ... nextPos { + newArr.append(items[n]) + } + items = newArr + nextPos -= frontPos + frontPos = 0 + size = newSize + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/RedBlackTree.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/RedBlackTree.swift new file mode 100644 index 0000000..8dff5fd --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/RedBlackTree.swift @@ -0,0 +1,159 @@ +import Foundation + +open class RedBlackTreeNode { + + public enum TreeColor { + case red + case black + } + + public var left: RedBlackTreeNode? + public var right: RedBlackTreeNode? + public var parent: RedBlackTreeNode? + public var value: T + public var color = TreeColor.red + + public init(parent: RedBlackTreeNode?, value: T) { + self.parent = parent + self.value = value + } + + open func isRoot() -> Bool { + return parent == nil + } + + open func uncle() -> RedBlackTreeNode? { + if let left = parent?.parent?.left, left.value == parent?.value { + return parent?.parent?.right + } + return parent?.parent?.left + } +} + +extension RedBlackTreeNode : Equatable { + public static func == (lhs: RedBlackTreeNode, rhs: RedBlackTreeNode) -> Bool { + return lhs.value == rhs.value + } +} + +open class RedBlackTree { + public var root: RedBlackTreeNode? + public var count = 0 + + public init() { } + + open func insert(value: T) { + if root == nil { + root = RedBlackTreeNode(parent: nil, value: value) + root?.color = .black + return + } + + count += 1 + + let node = bstInsert(value: value) + + if node.parent?.color == .black { + return //everything ok, no double red violation + } + + //else: Double red violation! + + if let uncle = node.uncle(), uncle.color == .red { + //try to recolor: parent and uncle are both red! + recolor(node: node) + } else { + let nIsLeft = node.parent?.left == node + let pIsLeft = node.parent?.parent?.left == node.parent + + if nIsLeft && pIsLeft { + rightRotate(n: node.parent) + } else if !nIsLeft && pIsLeft { + leftRotate(n: node) + rightRotate(n: node) + } else if nIsLeft && !pIsLeft { + leftRotate(n: node) + } else { //both right + leftRotate(n: node.parent) + } + } + } + + private func bstInsert(value: T) -> RedBlackTreeNode { //assumption: no equal value nodes, root exists + var curNode = root + + while true { + if curNode!.value > value { + if curNode?.left != nil { + curNode = curNode?.left + } else { + curNode?.left = RedBlackTreeNode(parent: curNode, value: value) + curNode = curNode?.left + break + } + } else { + if curNode?.right != nil { + curNode = curNode?.right + } else { + curNode?.right = RedBlackTreeNode(parent: curNode, value: value) + curNode = curNode?.right + break + } + } + } + + return curNode! + } + + private func recolor(node: RedBlackTreeNode?) { + guard node != nil else { + return + } + + let p = node?.parent + let u = node?.uncle() + let g = p?.parent + + p?.color = .black + u?.color = .black + g?.color = .red + + if g?.parent == nil { + g?.color = .black //paint root always black + } + + recolor(node: g) + } + + private func rightRotate(n: RedBlackTreeNode?) { + let p = n?.parent + p?.left = n?.right + p?.left?.parent = p + + n?.right = p + n?.parent = p?.parent + p?.parent = n + if n?.parent == nil { + root = n + } + + p?.color = .red + n?.color = .black + } + + private func leftRotate(n: RedBlackTreeNode?) { + let p = n?.parent + p?.right = n?.left + p?.right?.parent = p + + n?.left = p + n?.parent = p?.parent + p?.parent = n + if n?.parent == nil { + root = n + } + + n?.color = .black + p?.color = .red + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/SingleLinkedList.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/SingleLinkedList.swift new file mode 100644 index 0000000..26906b3 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/SingleLinkedList.swift @@ -0,0 +1,67 @@ +import Foundation + +open class SingleNode { + var next: SingleNode? + var val: T + + init(val: T, next: SingleNode? = nil) { + self.val = val + self.next = next + } +} + +open class SingleLinkedList { + public var head: SingleNode? + public var count: Int = 0 + + public init() { } + + open func add(node: SingleNode) { + if count == 0 { + head = node + } else { + var current = head + while current?.next != nil { + current = current?.next + } + current?.next = node + } + + count += 1 + } + + open func prepend(node: SingleNode) { + node.next = head + head = node + + count += 1 + } + + open func removeLast() throws -> SingleNode? { + if count == 0 { + throw MyError.runtimeError("Invalid call") + } + + var node: SingleNode? + if count == 1 { + node = head + head = nil + } else { + var current = head + while current?.next != nil { + node = current + current = current?.next + } + node?.next = nil + node = current + } + + count -= 1 + + return node + } + + open func isEmpty() -> Bool { + return count == 0 + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/Stack.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/Stack.swift new file mode 100644 index 0000000..0299996 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/Stack.swift @@ -0,0 +1,42 @@ +// +// Stack.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 23.03.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class Stack { + public var head: Node? + public var size: Int = 0 + + open func push(val: T) { + let newHead = Node(val: val, next: head) + head = newHead + size += 1 + } + + open func pop() throws -> T { + guard let node = head else { + throw NSError(domain: "Stack: Invalid call", code: 0, userInfo: nil) + } + + head = node.next + size -= 1 + return node.val + } + + open func peek() throws -> T { + guard let node = head else { + throw NSError(domain: "Stack: Invalid call", code: 0, userInfo: nil) + } + + return node.val + } + + open func isEmpty() -> Bool { + return size == 0 + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/TreeNodes.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/TreeNodes.swift new file mode 100644 index 0000000..3e4b661 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/TreeNodes.swift @@ -0,0 +1,55 @@ +// +// SimpleTreeNode.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 23.06.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class SimpleTreeNode { + + public var left: SimpleTreeNode? + public var right: SimpleTreeNode? + public var value: T + public var numElements = 0 + + public init(value: T) { + self.value = value + } + + public init(value: T, numElements: Int) { + self.value = value + self.numElements = numElements + } + + public func isLeaveNode() -> Bool { + return left == nil && right == nil + } +} + +open class TreeNode: Equatable { + + public var left: TreeNode? + public var right: TreeNode? + public var parent: TreeNode? + public var value: T + + public init(value: T) { + self.value = value + } + + public init(value: T, parent: TreeNode?) { + self.value = value + self.parent = parent + } + + public static func == (lhs: TreeNode, rhs: TreeNode) -> Bool { + return lhs.value == rhs.value + } + + public func isLeaveNode() -> Bool { + return left == nil && right == nil + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/Trie.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/Trie.swift new file mode 100644 index 0000000..a46cc16 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/Trie.swift @@ -0,0 +1,64 @@ +import Foundation + +open class TrieNode { + public let value: Character + public var terminates: Bool + public var childNodes: Dictionary = Dictionary() + + public init(value: Character, terminates: Bool = false) { + self.value = value + self.terminates = terminates + } +} + +open class Trie { + public let root: TrieNode = TrieNode(value: " ") + + public init() { } + + open func insert(word: String) { + insert(word: Array(word), node: root) + } + + private func insert(word: [Character], node: TrieNode) { + if word.isEmpty { + return + } + + var nextNode: TrieNode + if let nextChildNode = node.childNodes[word[0]] { + nextNode = nextChildNode + } else { + nextNode = TrieNode(value: word[0]) + node.childNodes[word[0]] = nextNode + } + + if word.count == 1 { + nextNode.terminates = true + } else { + let slice = word[1...] + insert(word: Array(slice), node: nextNode) + } + } + + open func search(word: String) -> Bool { + var curNode: TrieNode? = root + + var chars = Array(word) + + if chars.count < 1 { + return false + } + + while curNode != nil && chars.count > 0 { + let char = chars[0] + curNode = curNode?.childNodes[char] + chars = Array(chars[1...]) + if chars.isEmpty, let curNode = curNode, curNode.terminates { + return true + } + } + + return false + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/UndirectedGraph.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/UndirectedGraph.swift new file mode 100644 index 0000000..b9a703b --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/UndirectedGraph.swift @@ -0,0 +1,31 @@ +// +// UndirectedGraph.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 27.03.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class UndirectedGraph { + public let vertices: [Vertice] + private var adjList: [DoubleLinkedList] = [] + + public init(vertices: [Vertice]) { + self.vertices = vertices + + for _ in vertices { + adjList.append(DoubleLinkedList()) + } + } + + open func addEdge(v1: Vertice, v2: Vertice) { + adjList[v1.id].add(node: Node(val: v2)) + adjList[v2.id].add(node: Node(val: v1)) + } + + open func neighbours(v: Vertice) -> DoubleLinkedList { + return adjList[v.id] + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/UndirectedGraphWithAdjMatrix.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/UndirectedGraphWithAdjMatrix.swift new file mode 100644 index 0000000..5c6c9ea --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/UndirectedGraphWithAdjMatrix.swift @@ -0,0 +1,28 @@ +// +// UndirectedGraphWithAdjMatrix.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 27.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class WeightedUndirectedGraphWithAdjMatrix { + public let vertices: [Vertice] + public var adjMatrix: [[Int]] + + public init(vertices: [Vertice]) { + self.vertices = vertices + adjMatrix = [[Int]] (repeating: [Int] (repeating: Int.max, count: vertices.count), count: vertices.count) + } + + open func addEdge(v1: Vertice, v2: Vertice, weight: Int) { + adjMatrix[v1.id][v2.id] = weight + adjMatrix[v2.id][v1.id] = weight + } + + open func neighbours(v: Vertice) -> [Int] { + return adjMatrix[v.id] + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/UnionFind.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/UnionFind.swift new file mode 100644 index 0000000..5d0314b --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/UnionFind.swift @@ -0,0 +1,65 @@ +// +// UnionFind.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 12.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +public struct UnionFindElement { + public var parent: Int + public var size: Int +} + +open class UnionFind { + //union find with path compression and rank by size + private var components: [UnionFindElement] + private(set) var numberOfComponents: Int + + public init(numberOfElements: Int) { + components = [UnionFindElement] (repeating: UnionFindElement(parent: -1, size: 0), count: numberOfElements) + numberOfComponents = numberOfElements + } + + public convenience init(graph: WeightedUndirectedGraph) { + self.init(numberOfElements: graph.vertices.count) + + for vertice in graph.vertices { + components[vertice.id] = UnionFindElement(parent: vertice.id, size: 1) + } + } + + open func find(index: Int) -> Int { + if components[index].parent != index { + //path compression + components[index].parent = find(index: components[index].parent) + } + + return components[index].parent + } + + open func union(firstIndex: Int, secondIndex: Int) { + let firstElement = find(index: firstIndex) + let secondElement = find(index: secondIndex) + + if firstElement == secondElement { + //cycle detected! + //throw NSError(domain: "UnionFind - Cycle detected!", code: 0, userInfo: nil) + } else { + //weight by rank (size) + if components[firstElement].size > components[secondElement].size { + components[secondElement].parent = firstElement + } else if components[firstElement].size < components[secondElement].size { + components[firstElement].parent = secondElement + } else { + components[secondElement].parent = firstElement + components[firstElement].size += 1 + } + + numberOfComponents -= 1 + } + } + +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/Vertice.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/Vertice.swift new file mode 100644 index 0000000..e4e53d5 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/Vertice.swift @@ -0,0 +1,53 @@ +// +// Vertice.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 01.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class Vertice: NSObject, Comparable { + + public static func < (lhs: Vertice, rhs: Vertice) -> Bool { + return lhs.distanceTo < rhs.distanceTo + } + + static func == (lhs: Vertice, rhs: Vertice) -> Bool { + return lhs.id == rhs.id + } + + public var id: Int + + public var name: String + + //Needed for topological sort: + public var inboundCount: Int = 0 + + //Needed for Djikstra + Prim's MST (weight): + public var distanceTo: Int = 0 + + //Needed for Prim's MST: + public var minIngoingEdge: Edge? + + public var weight: Int { + get { + return distanceTo + } + set (weight) { + distanceTo = weight + } + } + + public var visited: Bool = false + + public init(id: Int, name: String = "") { + self.id = id + self.name = name + } + +// public override static func == (lhs: Vertice, rhs: Vertice) -> Bool { +// return lhs.id == rhs.id +// } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/WeightedDirectedGraph.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/WeightedDirectedGraph.swift new file mode 100644 index 0000000..f23551e --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/WeightedDirectedGraph.swift @@ -0,0 +1,44 @@ +// +// WeightedDirectedGraph.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 02.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class WeightedDirectedGraph { + public let vertices: [Vertice] + private var adjList: [DoubleLinkedList] = [] + + public init(vertices: [Vertice]) { + self.vertices = vertices + + for _ in vertices { + adjList.append(DoubleLinkedList()) + } + } + + open func addEdge(v1: Vertice, v2: Vertice, weight: Int) { + let edge = Edge(from: v1, to: v2, weight: weight) + adjList[v1.id].add(node: Node(val: edge)) + } + + open func neighbours(v: Vertice) -> DoubleLinkedList { + return adjList[v.id] + } + + open func edges() -> [Edge] { + var edges: [Edge] = [] + for edgeList in adjList { + var current = edgeList.head + while let cur = current { + edges.append(cur.val) + current = cur.next + } + } + + return edges + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/WeightedDirectedGraphWithAjdMatrix.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/WeightedDirectedGraphWithAjdMatrix.swift new file mode 100644 index 0000000..d151425 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/WeightedDirectedGraphWithAjdMatrix.swift @@ -0,0 +1,28 @@ +// +// WeightedDirectedGraphWithAjdMatrix.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 11.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class WeightedDirectedGraphWithAdjMatrix { + public let vertices: [Vertice] + public var adjMatrix: [[Int]] + + public init(vertices: [Vertice]) { + self.vertices = vertices + + adjMatrix = Array(repeating: Array(repeating: Int.max, count: vertices.count), count: vertices.count) + + for index in 0 ... vertices.count - 1 { + adjMatrix[index][index] = 0 + } + } + + open func addEdge(v1: Vertice, v2: Vertice, weight: Int) { + adjMatrix[v1.id][v2.id] = weight + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/DataStructures/WeightedUndirectedGraph.swift b/SPM/Sources/DataStructuresAlgorithms/DataStructures/WeightedUndirectedGraph.swift new file mode 100644 index 0000000..1a1c00e --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/DataStructures/WeightedUndirectedGraph.swift @@ -0,0 +1,41 @@ +// +// WeightedUndirectedGraph.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 12.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class WeightedUndirectedGraph { + public let vertices: [Vertice] + private var adjList: [DoubleLinkedList] = [] + private var edgesOfGraph: [Edge] = [] + + public init(vertices: [Vertice]) { + self.vertices = vertices + + for _ in vertices { + adjList.append(DoubleLinkedList()) + } + } + + open func addEdge(v1: Vertice, v2: Vertice, weight: Int) { + let edge = Edge(from: v1, to: v2, weight: weight) + adjList[v1.id].add(node: Node(val: edge)) + edgesOfGraph.append(edge) + + let edge2 = Edge(from: v2, to: v1, weight: weight) + adjList[v2.id].add(node: Node(val: edge2)) + edgesOfGraph.append(edge2) + } + + open func neighbours(v: Vertice) -> DoubleLinkedList { + return adjList[v.id] + } + + open func edges() -> [Edge] { + return edgesOfGraph + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/ArrayPairsWithSum.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/ArrayPairsWithSum.swift new file mode 100644 index 0000000..bf5f73a --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/ArrayPairsWithSum.swift @@ -0,0 +1,38 @@ +// +// ArrayPairsWithSum.swift +// +// +// Created by Stefan Jaindl on 01.12.20. +// + +import Foundation + +open class ArrayPairsWithSum { + public init() { } + + open func pairsWithSum(array: [Int], targetSum: Int) -> Set { + guard array.count > 1 else { + return [] + } + + var lookupTable = Set() + + var pairs = Set() + + array.forEach { element in + let diffToTarget = targetSum - element + if lookupTable.contains(where: { $0 == diffToTarget }) { + pairs.insert(TargetSumPair(first: diffToTarget, second: element)) + } + + lookupTable.insert(element) + } + + return pairs + } + + public struct TargetSumPair: Hashable { + let first: Int + let second: Int + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/CircularArray.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/CircularArray.swift new file mode 100644 index 0000000..3935abc --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/CircularArray.swift @@ -0,0 +1,71 @@ +// +// CircularArray.swift +// +// +// Created by Stefan Jaindl on 10.08.20. +// + +import Foundation + +open class CircularArray { + public init (array: [T]) { + self.array = array + } + + let array: [T] + var startIndex = 0 + var endIndex: Int { + return startIndex == 0 ? array.count : startIndex - 1 + } + + open func isEmpty() -> Bool { + return array.count == 0 + } + + open func rightRotate(by: Int) { + startIndex = (startIndex - by) % array.count + if startIndex < 0 { + startIndex += array.count + } + } + + open func leftRotate(by: Int) { + startIndex = (startIndex + by) % array.count + } +} + +extension CircularArray: Sequence { + public func makeIterator() -> CircularArrayIterator { + return CircularArrayIterator(array: array, startIndex: startIndex) + } +} + +open class CircularArrayIterator: IteratorProtocol { + let array: [T] + var startIndex: Int + var currentIndex: Int + var previousIndex: Int + + init(array: [T], startIndex: Int) { + self.array = array + self.startIndex = startIndex + currentIndex = startIndex + previousIndex = -1 + } + + open func next() -> T? { + guard hasNext() else { + return nil + } + + let element = array[currentIndex] + previousIndex = currentIndex + currentIndex = (currentIndex + 1) % array.count + + return element + } + + private func hasNext() -> Bool { + return previousIndex == -1 || currentIndex != startIndex + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/IntToEnglishString.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/IntToEnglishString.swift new file mode 100644 index 0000000..713d9a2 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/IntToEnglishString.swift @@ -0,0 +1,170 @@ +// +// IntToEnglishString.swift +// +// +// Created by Stefan Jaindl on 19.11.20. +// + +import Foundation + +open class IntToEnglishString { + + public init() { } + + private enum Config: CaseIterable { + case ones + case tens + case hundred + case thousand + case tenthousand + case hundredthousand + case million + case tenmillion + case hundredmillion + case billion + } + + private let ones: [Int: String] = [ + 0: "", + 1: "One", + 2: "Two", + 3: "Three", + 4: "Four", + 5: "Five", + 6: "Six", + 7: "Seven", + 8: "Eight", + 9: "Nine" + ] + + private let tens: [Int: String] = [ + 0: "", + 1: "Ten", + 2: "Twenty", + 3: "Thirty", + 4: "Fourty", + 5: "Fifty", + 6: "Sixty", + 7: "Seventy", + 8: "Eighty", + 9: "Ninety" + ] + + private let specialTens: [Int: String] = [ + 0: "Ten", + 1: "Eleven", + 2: "Twelve", + 3: "Thirteen", + 4: "Fourteen", + 5: "Fifteen", + 6: "Sixteen", + 7: "Seventeen", + 8: "Eighteen", + 9: "Nineteen" + ] + + private let zero = "Zero" + private let hundred = "Hundred" + private let thousand = "Thousand" + private let million = "Million" + private let billion = "Billion" + + open func convertToEnglishString(number: Int) throws -> String { + let stack = Stack() + + var convertedString = "" + var curNumber = number + let order = Config.allCases + var orderIndex = 0 + var thousandAppended = false + var millionAppended = false + + while curNumber > 0 { + let currentDigit = curNumber % 10 + let place = order[orderIndex] + var valueOfPlace: String + + switch place { + case .ones: + valueOfPlace = ones[currentDigit] ?? "" + case .tens: + valueOfPlace = try tens(with: "", stack: stack, currentDigit: currentDigit) + case .hundred: + valueOfPlace = digit(with: hundred, stack: stack, currentDigit: currentDigit) + case .thousand: + valueOfPlace = digit(with: thousand, stack: stack, currentDigit: currentDigit) + if !valueOfPlace.isEmpty { + thousandAppended = true + } + case .tenthousand: + valueOfPlace = try tens(with: thousandAppended ? "" : thousand, stack: stack, currentDigit: currentDigit) + if !valueOfPlace.isEmpty { + thousandAppended = true + } + case .hundredthousand: + valueOfPlace = digit(with: hundred, stack: stack, currentDigit: currentDigit) + if !valueOfPlace.isEmpty, !thousandAppended { + valueOfPlace.append(" \(thousand)") + } + case .million: + valueOfPlace = digit(with: million, stack: stack, currentDigit: currentDigit) + if !valueOfPlace.isEmpty { + millionAppended = true + } + case .tenmillion: + valueOfPlace = try tens(with: millionAppended ? "" : million, stack: stack, currentDigit: currentDigit) + if !valueOfPlace.isEmpty { + millionAppended = true + } + case .hundredmillion: + valueOfPlace = digit(with: hundred, stack: stack, currentDigit: currentDigit) + if !valueOfPlace.isEmpty, !millionAppended { + valueOfPlace.append(" \(million)") + } + case .billion: + valueOfPlace = digit(with: billion, stack: stack, currentDigit: currentDigit) + } + + stack.push(val: valueOfPlace) + + orderIndex += 1 + curNumber /= 10 + } + + while !stack.isEmpty() { + convertedString = "\(convertedString) \(try stack.pop())".trimmingCharacters(in: CharacterSet(arrayLiteral: " ")) + } + + return convertedString.isEmpty ? zero : convertedString + } + + private func tens(with appended: String, stack: Stack, currentDigit: Int) throws -> String { + guard var valueOfPlace = tens[currentDigit] else { + return "" + } + + if valueOfPlace == tens[1] { + let onesPlace = try stack.pop() + if !onesPlace.isEmpty, let value = ones.filter({ $0.value == onesPlace }).first { + valueOfPlace = specialTens[value.key] ?? "" + } else { + valueOfPlace = valueOfPlace.trimmingCharacters(in: CharacterSet(arrayLiteral: " ")) + valueOfPlace.append(" \(appended)") + } + } + + return valueOfPlace + } + + private func digit(with appended: String, stack: Stack, currentDigit: Int) -> String { + guard var valueOfPlace = ones[currentDigit] else { + return "" + } + + if !valueOfPlace.isEmpty, !appended.isEmpty { + valueOfPlace.append(" \(appended)") + } + + return valueOfPlace + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/LettersAndNumbersLargestSubsequence.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/LettersAndNumbersLargestSubsequence.swift new file mode 100644 index 0000000..34b218e --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/LettersAndNumbersLargestSubsequence.swift @@ -0,0 +1,84 @@ +// +// LettersAndNumbersLargestSubsequence.swift +// +// +// Created by Stefan Jaindl on 05.12.20. +// + +import Foundation + +//Computes largest subsequence of equal numbers of letters and numbers in O(N) time +open class LettersAndNumbersLargestSubsequence { + public init() { } + + open func findLargestSubSequence(of array: [String]) -> [String] { + guard !array.isEmpty else { + return [] + } + + let difference = computeDifferences(of: array) + if let submatch = findLargestSubmatch(of: difference) { + return Array(array[submatch.start ... submatch.end]) + } + + return [] + + } + + private func computeDifferences(of array: [String]) -> Difference { + var differenceArray: [Int] = [] + var zeroDifference: (start: Int, end: Int)? + var diff = 0 + for (index, element) in array.enumerated() { + if isNumber(element: element) { + diff += 1 + } else { + diff -= 1 + } + differenceArray.append(diff) + + if diff == 0 { + zeroDifference = (start: 0, end: index) + } + } + + return Difference(differences: differenceArray, largestZeroDifference: zeroDifference) + } + + private func findLargestSubmatch(of difference: Difference) -> (start: Int, end: Int)? { + var checkedDiffs: [Int: Int] = [:] //maps difference to index + + var biggestDiffIndices: (start: Int, end: Int)? + var biggestDiff = 0 + + if let zeroDiff = difference.largestZeroDifference { + biggestDiffIndices = zeroDiff + biggestDiff = zeroDiff.end - zeroDiff.start + 1 + } + + let differences = difference.differences + + for (index, diff) in differences.enumerated() { + if let previousIndexOfDiff = checkedDiffs[diff] { + let indexDifference = index - previousIndexOfDiff + 1 + if indexDifference > biggestDiff { + biggestDiff = indexDifference + biggestDiffIndices = (start: previousIndexOfDiff + 1, end: index) + } + } else { + checkedDiffs[diff] = index + } + } + + return biggestDiffIndices + } + + private func isNumber(element: String) -> Bool { + return Int(element) != nil + } +} + +struct Difference { + let differences: [Int] + let largestZeroDifference: (start: Int, end: Int)? +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/LongestWordFinder.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/LongestWordFinder.swift new file mode 100644 index 0000000..9de0768 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/LongestWordFinder.swift @@ -0,0 +1,55 @@ +// +// LongestWordFinder.swift +// +// +// Created by Stefan Jaindl on 11.12.20. +// + +import Foundation + +open class LongestWordFinder { + + public init() { } + + //Finds longest word made up of another words + open func findLongestCombinedWord(of words: [String]) -> String? { + guard words.count > 1 else { + return nil + } + + let trie = buildTrie(of: words) + let longest = longestWord(trieNode: trie.root, shorterWordFound: false, chars: []) + + return longest.isEmpty ? nil : String(longest) + } + + private func longestWord(trieNode: TrieNode, shorterWordFound: Bool, chars: [Character]) -> [Character] { + let terminates = trieNode.terminates + var longest: [Character] = [] + + if terminates && shorterWordFound { + longest = chars + } + + for child in trieNode.childNodes { + var newChars = chars + newChars.append(child.key) + + let longestChild = longestWord(trieNode: child.value, shorterWordFound: shorterWordFound || terminates, chars: newChars) + if longestChild.count > longest.count { + longest = longestChild + } + } + + return longest + } + + private func buildTrie(of words: [String]) -> Trie { + let trie = Trie() + for word in words { + trie.insert(word: word) + } + + return trie + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/MajorityElement.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/MajorityElement.swift new file mode 100644 index 0000000..cb1347e --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/MajorityElement.swift @@ -0,0 +1,52 @@ +// +// MajorityElement.swift +// +// +// Created by Stefan Jaindl on 08.12.20. +// + +import Foundation + +open class MajorityElement { + public init() { } + + private let minSubarrayElementsToCheck = 2 + private let matchPercentageTreshold = 0.5 + + open func find(array: [Int]) -> Int? { + var index = 0 + while index < array.count { + if let matched = checkSubarray(array: array, startIndex: index) { + return matched + } + + index += 2 + } + + return nil + } + + private func checkSubarray(array: [Int], startIndex: Int) -> Int? { + var matches = 0 + var checked = 0 + var index = startIndex + + let elementToMatch = array[index] + + while checked < minSubarrayElementsToCheck || Double(Double(matches) / Double(checked)) > matchPercentageTreshold { + if index == startIndex && matches > 0 { + //found majority element! + return elementToMatch + } + + if array[index] == elementToMatch { + matches += 1 + } + + index = (index + 1) % array.count + checked += 1 + } + + return nil + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/MatrixRotation.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/MatrixRotation.swift new file mode 100644 index 0000000..7e64199 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/MatrixRotation.swift @@ -0,0 +1,32 @@ +// MatrixRotation.swift +// DataStructuresAlgorithms +// +// Created by Stefan Jaindl on 16.05.20. +// + +import Foundation + +open class MatrixRotation { + + public init() { } + + open func rotateRight(array: inout [[Int]]) { + for offset in 0 ..< array.count / 2 { + rotateRight(array: &array, offset: offset) + } + } + + open func rotateRight(array: inout [[Int]], offset: Int) { + for pos in 0 ..< array.count - 1 - offset * 2 { + let top = array[offset][offset + pos] + let right = array[offset + pos][array.count - 1 - offset] + let bottom = array[array.count - 1 - offset][array.count - 1 - offset - pos] + let left = array[array.count - 1 - offset - pos][offset] + + array[offset][offset + pos] = left //left -> top + array[offset + pos][array.count - 1 - offset] = top //top -> right + array[array.count - 1 - offset][array.count - 1 - offset - pos] = right //right -> bottom + array[array.count - 1 - offset - pos][offset] = bottom //bottom -> left + } + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/MaxBlack.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/MaxBlack.swift new file mode 100644 index 0000000..a9d5e09 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/MaxBlack.swift @@ -0,0 +1,200 @@ +// +// MaxBlack.swift +// +// +// Created by Stefan Jaindl on 19.12.20. +// + +import Foundation + +open class MaxBlack { + public init() { } + + open func maxBorder(of square: BlackWhiteSquare) -> SubSquare? { + var len = square.cells.count + let count = square.cells.count + let blackRowCounts = blackRightRowCounts(of: square) + let blackColCounts = blackBottomColCounts(of: square) + + while len > 0 { + for row in 0 ... count - len { + for col in 0 ... count - len { + let cell = SubSquare(topLeft: CellIndex(row: row, col: col), bottomRight: CellIndex(row: row + len - 1, col: col + len - 1)) + if isAllBlackBorder(square: square, cell: cell, blackRowCounts: blackRowCounts, blackColCounts: blackColCounts, len: len) { + return cell + } + } + } + + len -= 1 + } + + return nil + } + + private func blackRightRowCounts(of square: BlackWhiteSquare) -> [CellIndex: Int] { + var rowIndex = square.cells.count - 1 + var rowRightCounts: [CellIndex: Int] = [:] + + while rowIndex >= 0 { + var blackCount = 0 + var colIndex = square.cells.count - 1 + + while colIndex >= 0 { + rowRightCounts[CellIndex(row: rowIndex, col: colIndex)] = blackCount + if square.cells[rowIndex][colIndex].color == .black { + blackCount += 1 + } + colIndex -= 1 + } + + rowIndex -= 1 + } + + return rowRightCounts + } + + private func blackBottomColCounts(of square: BlackWhiteSquare) -> [CellIndex: Int] { + var colIndex = square.cells.count - 1 + var colBottomCounts: [CellIndex: Int] = [:] + + while colIndex >= 0 { + var blackCount = 0 + var rowIndex = square.cells.count - 1 + + while rowIndex >= 0 { + colBottomCounts[CellIndex(row: rowIndex, col: colIndex)] = blackCount + if square.cells[rowIndex][colIndex].color == .black { + blackCount += 1 + } + rowIndex -= 1 + } + + colIndex -= 1 + } + + return colBottomCounts + } + + private func isAllBlackBorder(square: BlackWhiteSquare, cell: SubSquare, blackRowCounts: [CellIndex: Int], blackColCounts: [CellIndex: Int], len: Int) -> Bool { + let minRow = cell.topLeft.row + let maxRow = cell.bottomRight.row + let minCol = cell.topLeft.col + let maxCol = cell.bottomRight.col + + let topLeftCell = CellIndex(row: minRow, col: minCol) + let topRightCell = CellIndex(row: minRow, col: maxCol) + let bottomLeftCell = CellIndex(row: maxRow, col: minCol) + let bottomRightCell = CellIndex(row: maxRow, col: maxCol) + + guard let topLeftRowCount = blackRowCounts[topLeftCell], + let topRightRowCount = blackRowCounts[topRightCell], + let bottomLeftRowCount = blackRowCounts[bottomLeftCell], + let bottomRightRowCount = blackRowCounts[bottomRightCell], + + let topLeftColCount = blackColCounts[topLeftCell], + let topRightColCount = blackColCounts[topRightCell], + let bottomLeftColCount = blackColCounts[bottomLeftCell], + let bottomRightColCount = blackColCounts[bottomRightCell] else { + return false + } + + let blackTopRowCount = topLeftRowCount - topRightRowCount + (square.cells[minRow][minCol].color == .black ? 1 : 0) + let blackBottomRowCount = bottomLeftRowCount - bottomRightRowCount + (square.cells[maxRow][minCol].color == .black ? 1 : 0) + + let blackLeftColCount = topLeftColCount - bottomLeftColCount + (square.cells[minRow][minCol].color == .black ? 1 : 0) + let blackRightColCount = topRightColCount - bottomRightColCount + (square.cells[minRow][maxCol].color == .black ? 1 : 0) + + return blackLeftColCount == len && blackRightColCount == len && blackTopRowCount == len && blackBottomRowCount == len + } + + //Given an NxN matrix of black & white cells, return the max sized black-only square (in O(n^4)) + open func maxBlackFilled(of square: BlackWhiteSquare) -> SubSquare? { + let count = square.cells.count + for size in sizes(count: count) { + let rowSize = size.rowSize + let colSize = size.colSize + for row in 0 ... count - rowSize { + for col in 0 ... count - colSize { + let cell = SubSquare(topLeft: CellIndex(row: row, col: col), bottomRight: CellIndex(row: row + rowSize - 1, col: col + colSize - 1)) + if isAllBlack(square: square, cell: cell) { + return cell + } + } + } + } + + return nil + } + + private func isAllBlack(square: BlackWhiteSquare, cell: SubSquare) -> Bool { + for row in cell.rowRange { + for col in cell.colRange { + if square.cells[row][col].color == .white { + return false + } + } + } + + return true + } + + private func sizes(count: Int) -> [(rowSize: Int, colSize: Int)] { + guard count > 0 else { + return [] + } + + var sizes: [(rowSize: Int, colSize: Int)] = [] + for row in 1 ... count { + for col in 1 ... count { + sizes.append((rowSize: row, colSize: col)) + } + } + + return sizes.sorted(by: { + $0.rowSize * $0.colSize > $1.rowSize * $1.colSize + }) + } +} + +public struct BlackWhiteSquare { + let cells: [[SquareCell]] +} + + +public struct SquareCell { + let color: SquareCellColor +} + +public struct SubRectangle: Equatable, Hashable { + let topLeft: CellIndex + let bottomRight: CellIndex + + var width: Int { + return bottomRight.row - topLeft.row + } + + var height: Int { + return topLeft.col - bottomRight.col + } + + var rowRange: ClosedRange { + return topLeft.row ... bottomRight.row + } + + var colRange: ClosedRange { + return topLeft.col ... bottomRight.col + } +} + +public typealias SubSquare = SubRectangle + +public enum SquareCellColor { + case black + case white +} + +public struct CellIndex: Hashable, Equatable { + let row: Int + let col: Int +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/MaxMatrixSum.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/MaxMatrixSum.swift new file mode 100644 index 0000000..627f1eb --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/MaxMatrixSum.swift @@ -0,0 +1,86 @@ +// +// MaxMatrixSum.swift +// +// +// Created by Stefan Jaindl on 20.12.20. +// + +import Foundation + +open class MaxMatrixSum { + + public init() { } + + //Finds submatrix of given matrix with positive and negative int's in O(n^4) time & O(n^2) space + open func maxSum(of matrix: [[Int]]) -> SubRectangle? { + guard !matrix.isEmpty, matrix.count == matrix[0].count else { + return nil + } + + let subsumsFromOrigin: [[Int]] = computeSumsFromTopLeft(of: matrix) + var maxSum = Int.min + var maxRectangle: SubRectangle? = nil + + for rowSize in 1 ... matrix.count { + let maxValidStartRow = matrix.count - rowSize + for colSize in 1 ... matrix[0].count { + let maxValidStartColumn = matrix[0].count - colSize + for topRow in 0 ... maxValidStartRow { + for leftCol in 0 ... maxValidStartColumn { + let bottomRow = topRow + rowSize - 1 + let rightCol = leftCol + colSize - 1 + + let fromIndex = CellIndex(row: topRow, col: leftCol) + let toIndex = CellIndex(row: bottomRow, col: rightCol) + let sum = subsum(from: fromIndex, to: toIndex, subsumsFromOrigin: subsumsFromOrigin) + + if sum > maxSum { + maxSum = sum + maxRectangle = SubRectangle(topLeft: fromIndex, bottomRight: toIndex) + } + } + } + } + } + + return maxRectangle + } + + private func subsum(from: CellIndex, to: CellIndex, subsumsFromOrigin: [[Int]]) -> Int { + var sum = subsumsFromOrigin[to.row][to.col] + + if from.row > 0 { + sum -= subsumsFromOrigin[from.row - 1][to.col] + } + if from.col > 0 { + sum -= subsumsFromOrigin[to.row][from.col - 1] + } + if from.row > 0, from.col > 0 { + sum += subsumsFromOrigin[from.row - 1][from.col - 1] + } + + return sum + } + + private func computeSumsFromTopLeft(of matrix: [[Int]]) -> [[Int]] { + var subsums: [[Int]] = [[Int]](repeating: [Int](repeating: 0, count: matrix[0].count), count: matrix.count) + + for row in 0 ..< matrix.count { + for col in 0 ..< matrix[0].count { + var sum = matrix[row][col] + if row > 0, col > 0 { + sum -= subsums[row - 1][col - 1] + } + if row > 0 { + sum += subsums[row - 1][col] + } + if col > 0 { + sum += subsums[row][col - 1] + } + subsums[row][col] = sum + } + } + + return subsums + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/MinElementsFinder.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/MinElementsFinder.swift new file mode 100644 index 0000000..e911785 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/MinElementsFinder.swift @@ -0,0 +1,117 @@ +// +// MinElementsFinder.swift +// +// +// Created by Stefan Jaindl on 11.12.20. +// + +import Foundation + +open class MinElementsFinder { + public init() { } + + //Finds smallest numberOfElements elements and returns them sorted in O(N log N) time using a heap. + open func findMinElementsSorted(numberOfElements: Int, array: [Int]) throws -> [Int] { + guard !array.isEmpty else { + return [] + } + + if numberOfElements >= array.count { + return array + } + + let priorityQueue = MaxHeap() + + for element in array { + if priorityQueue.numberOfElements() < numberOfElements { + priorityQueue.insert(val: element) + } else { + if element < (try priorityQueue.peekMax()) { + try priorityQueue.extractMax() + priorityQueue.insert(val: element) + } + } + } + + var minElements: [Int] = [] + while !priorityQueue.isEmpty() { + try minElements.append(priorityQueue.extractMax()) + } + + return minElements.reversed() + } + + //Finds smallest numberOfElements elements and returns them unsorted in O(N log N) time using an adapted quicksort partition method + open func findMinElementsUnique(numberOfElements: Int, array: [Int]) -> [Int] { + guard !array.isEmpty else { + return [] + } + + if numberOfElements >= array.count { + return array + } + + var finalIndex = -1 + var minIndex = 0 + var maxIndex = array.count - 1 + var searchedArray = array + + while finalIndex != numberOfElements - 1 { + finalIndex = partition(array: &searchedArray, min: minIndex, max: maxIndex) + if finalIndex == numberOfElements - 1 { + break + } else if finalIndex < numberOfElements - 1 { + //go right + minIndex = finalIndex + 1 + } else { + //go left + maxIndex = finalIndex - 1 + } + } + + var minElements: [Int] = [] + var index = 0 + while index < numberOfElements { + minElements.append(searchedArray[index]) + index += 1 + } + + return minElements + } + + private func partition(array: inout [Int], min: Int, max: Int) -> Int { + let pivot = array[medianOfThree(array: array, min: min, max: max)] + var left = min + var right = max + while left <= right { + if array[left] > pivot { + array.swapAt(left, right) + right -= 1 + } else if array[right] <= pivot { + array.swapAt(left, right) + left += 1 + } else { + left += 1 + right -= 1 + } + } + + return left - 1 + } + + private func medianOfThree(array: [Int], min: Int, max: Int) -> Int { + let middle = min + (max - min) / 2 + + if array[min] < array[middle] && array[min] > array[max] + || array[min] > array[middle] && array[min] < array[max] { + return min + } + + if array[max] < array[middle] && array[max] > array[min] + || array[max] > array[middle] && array[max] < array[min] { + return max + } + + return middle + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/MissingNumber.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/MissingNumber.swift new file mode 100644 index 0000000..0973ea0 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/MissingNumber.swift @@ -0,0 +1,67 @@ +// +// MissingNumber.swift +// +// +// Created by Stefan Jaindl on 04.12.20. +// + +import Foundation + +open class MissingNumber { + public init() { } + + //Array has non-negative numbers without duplicates in the range 0 ... n, where one number is missing. + //Runtime: ~ O(2N) = ~ O(N): checking O(N) -> O(N/2) -> O(N/4) ... + open func findMissingNumber(of array: [Int], lenght: Int) -> Int? { + guard !array.isEmpty, lenght == array.count else { + return nil + } + + var numbersInArray = array + var currentBit = 0 //start with LSB + var missingNumber = 0 + + while currentBit < 64 { + let counts = bitCounts(bitIndex: currentBit, zero: true, of: numbersInArray) + + let isZeroBit = counts.zeroes.count <= counts.ones.count + let min = isZeroBit ? counts.zeroes : counts.ones + + numbersInArray = min + if !isZeroBit { + missingNumber |= (1 << currentBit) + } + + if numbersInArray.count == 1 { + if numbersInArray[0] == missingNumber { + missingNumber |= 1 << (currentBit + 1) + } + + return missingNumber + } + + currentBit += 1 + } + + return nil + } + + private func bitCounts(bitIndex: Int, zero: Bool, of numbers: [Int]) -> (zeroes: [Int], ones: [Int]) { + var zeroes: [Int] = [] + var ones: [Int] = [] + for number in numbers { + if isBitSet(bitIndex: bitIndex, of: number) { + ones.append(number) + } else { + zeroes.append(number) + } + } + + return (zeroes: zeroes, ones: ones) + } + + private func isBitSet(bitIndex: Int, of number: Int) -> Bool { + let mask = 1 << bitIndex + return (number & mask) != 0 + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/MissingTwo.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/MissingTwo.swift new file mode 100644 index 0000000..ab9deb5 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/MissingTwo.swift @@ -0,0 +1,125 @@ +// +// MissingTwo.swift +// +// +// Created by Stefan Jaindl on 13.12.20. +// + +import Foundation + +open class MissingTwo { + public init() { } + + //Finds missing number in array from 1 ... N + open func findMissingNumber(in array: [Int]) -> Int? { + guard !array.isEmpty else { + return nil + } + + let arraySum = sum(of: array) + let expectedSum = expected(totalCount: array.count + 1) //includes missing int + + return expectedSum - arraySum + } + + open func findMissingTwoNumbers(in array: [Int]) -> [Int] { + guard !array.isEmpty else { + return [] + } + + let totalCount = array.count + 2 + let arraySum = sum(of: array) + let expectedSum = expected(totalCount: totalCount) //includes 2 missing ints + let lookup = buildLookupTable(of: array) + + let diff = expectedSum - arraySum + + //Search only possible diffs + let max = totalCount % 2 == 0 ? diff / 2 - 1 : diff / 2 + for current in 1 ... max { + if !lookup.contains(current) { + //found! + let other = diff - current + return [current, other] + } + } + + return [] + } + + open func findMissingTwoNumbersWithLessSpace(in array: [Int]) -> [Int] { + guard !array.isEmpty else { + return [] + } + + let totalCount = array.count + 2 + let arraySum = sum(of: array) + let arrayQuadraticSum = quadraticSum(of: array) + let expectedArraySum = expected(totalCount: totalCount) //includes 2 missing ints + let expectedArrayQuadraticSum = expectedQuadraticSum(totalCount: totalCount) + + let missingQuadraticSum = expectedArrayQuadraticSum - arrayQuadraticSum //the missing 2 ints are the 2 quadratics sums of this sum! + let missingSum = expectedArraySum - arraySum //this is the sum of the 2 missing ints + + for number in 1 ... Int(missingSum / 2) { + let other = missingSum - number + let numberQuadr = Int(pow(Double(number), 2)) + let otherQuadr = Int(pow(Double(other), 2)) + + if numberQuadr + otherQuadr == missingQuadraticSum { + return [number, other] + } + } + + return [] + } + + private func sum(of array: [Int]) -> Int { + var arraySum = 0 + for number in array { + arraySum += number + } + + return arraySum + } + + private func quadraticSum(of array: [Int]) -> Int { + var arrayQuadraticSum = 0 + for number in array { + arrayQuadraticSum += Int(pow(Double(number), 2)) + } + + return arrayQuadraticSum + } + + private func expected(totalCount: Int) -> Int { + let expected: Int + + if totalCount % 2 == 1 { + expected = (totalCount / 2 + 1) * totalCount //odd + } else { + expected = (totalCount / 2) * totalCount + totalCount / 2 + } + + return expected + } + + private func expectedQuadraticSum(totalCount: Int) -> Int { + var expected = 0 + + for number in 1 ... totalCount { + expected += Int(pow(Double(number), 2)) + } + + return expected + } + + private func buildLookupTable(of array: [Int]) -> Set { + var set = Set() + for element in array { + set.insert(element) + } + + return set + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/NamesMerger.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/NamesMerger.swift new file mode 100644 index 0000000..2483ad5 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/NamesMerger.swift @@ -0,0 +1,146 @@ +// +// NamesMerger.swift +// +// +// Created by Stefan Jaindl on 06.12.20. +// + +import Foundation + +open class NamesMerger { + public init() { } + + private var vertices: [Vertice] = [] + private var nameToMergedIndexDict: [String:Int] = [:] + private var nameToVerticeIndexDict: [String:Int] = [:] + + open func mergeLists(nameFrequencies: [NameFrequency], nameMappings: [(String, String)]) throws -> [NameFrequency] { + var mergedFrequency: [NameFrequency] = [] + + // 1: Convert nameMappings array to directed graph + /* + E.g. nameMappings = [(Jon, John), (John, Johnny), (Chris, Kris), (Chris, Christopher)] + --> mappingsGraph components = (Jon, John, Johnny), (Chris, Kris, Christopher) + */ + + let mappingGraph = buildNameMappingGraph(nameFrequencies: nameFrequencies, nameMappings: nameMappings) + + // 2: Associate already found indices for names + /* + E.g. nameFrequencies = [John: 15, Jon: 12, Chris: 13, Kris: 4, Christopher: 19] + Check entries in connected graph components: + --> no entry in index dict: Append to mergedFrequency and store index in nameToIndexDict + --> entry in index dict: Sum up in mergedFrequency at index, store index in nameToIndexDict + */ + + try nameFrequencies.forEach { nameFrequency in + let connectedComponents = try connectedNames(graph: mappingGraph, for: nameFrequency.name) + + if let index = findIndex(mapping: connectedComponents, nameToIndexDict: nameToMergedIndexDict) { + let frequency = mergedFrequency[index] + frequency.frequency += nameFrequency.frequency + nameToMergedIndexDict[nameFrequency.name] = index + } else { + mergedFrequency.append(nameFrequency) + nameToMergedIndexDict[nameFrequency.name] = mergedFrequency.count - 1 + } + } + + return mergedFrequency + } + + private func findIndex(mapping: Set, nameToIndexDict: [String: Int]) -> Int? { + var index: Int? + + mapping.forEach { name in + if let nameIndex = nameToIndexDict[name] { + index = nameIndex + return + } + } + + return index + } + + private func buildNameMappingGraph(nameFrequencies: [NameFrequency], nameMappings: [(String, String)]) -> UndirectedGraph { + for (index, nameFrequency) in nameFrequencies.enumerated() { + nameToVerticeIndexDict[nameFrequency.name] = index + vertices.append(Vertice(id: index, name: nameFrequency.name)) + } + + nameMappings.forEach { mapping in + if vertice(for: mapping.0) == nil { + let index = nameToVerticeIndexDict.count + nameToVerticeIndexDict[mapping.0] = index + vertices.append(Vertice(id: index, name: mapping.0)) + } + + if vertice(for: mapping.1) == nil { + let index = nameToVerticeIndexDict.count + nameToVerticeIndexDict[mapping.1] = index + vertices.append(Vertice(id: index, name: mapping.1)) + } + } + + let graph = UndirectedGraph(vertices: vertices) + + nameMappings.forEach { mapping in + if let firstVertice = vertice(for: mapping.0), let secondVertice = vertice(for: mapping.1) { + graph.addEdge(v1: firstVertice, v2: secondVertice) + } + } + + return graph + } + + private func connectedNames(graph: UndirectedGraph, for name: String) throws -> Set { + var connected = Set() + + guard let startVertice = vertice(for: name) else { + return connected + } + + let nameQueue = Queue() + nameQueue.enqueue(val: startVertice) + while !nameQueue.isEmpty() { + let currentVertice = try nameQueue.dequeue() + connected.insert(currentVertice.name) + + let neighbours = graph.neighbours(v: currentVertice) + var currentHead = neighbours.head + while let curHead = currentHead { + let curNeighbour = curHead.val + + if !connected.contains(curNeighbour.name) { + nameQueue.enqueue(val: curNeighbour) + } + + currentHead = currentHead?.next + } + } + + return connected + } + + private func vertice(for name: String) -> Vertice? { + guard let index = nameToVerticeIndexDict[name] else { + return nil + } + + return vertices[index] + } +} + +open class NameFrequency: Equatable { + let name: String + var frequency: Int + + init(name: String, frequency: Int) { + self.name = name + self.frequency = frequency + } + + public static func == (lhs: NameFrequency, rhs: NameFrequency) -> Bool { + lhs.name == rhs.name && lhs.frequency == rhs.frequency + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/OneAwayChecker.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/OneAwayChecker.swift new file mode 100644 index 0000000..17bfc19 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/OneAwayChecker.swift @@ -0,0 +1,75 @@ +// +// OneAwayChecker.swift +// DataStructuresAlgorithms +// +// Created by Stefan Jaindl on 15.05.20. +// + +import Foundation + +open class OneAwayChecker { + + public init() { } + + //Checks whether 2 strings are at most one replace, insert or remove operation away from each other (considering char order) + open func isOneAway(string1: String, string2: String) -> Bool { + if string1.count <= 1, string2.count <= 1 { //can't be more than one away + return true + } + + if string1.count == string2.count { + return isOneReplaceAway(string1: string1, string2: string2) + } + + if abs(string1.count - string2.count) == 1 { + return isOneInsertAway(string1: string1, string2: string2) + } + + return false + } + + private func isOneReplaceAway(string1: String, string2: String) -> Bool { + var diffFound = false + + for index in 0 ..< string1.count { + let firstIndex = string1.index(string1.startIndex, offsetBy: index) + let secondIndex = string2.index(string2.startIndex, offsetBy: index) + if string1[firstIndex] != string2[secondIndex] { + if diffFound { + return false + } + diffFound = true + } + } + + return true + } + + private func isOneInsertAway(string1: String, string2: String) -> Bool { + var diffFound = false + var index1 = 0 + var index2 = 0 + + while index1 < string1.count, index2 < string2.count { + let firstIndex = string1.index(string1.startIndex, offsetBy: index1) + let secondIndex = string2.index(string2.startIndex, offsetBy: index2) + if string1[firstIndex] != string2[secondIndex] { + if diffFound { + return false + } + diffFound = true + + if string1.count > string2.count { + index1 += 1 + } else { + index2 += 1 + } + } else { + index1 += 1 + index2 += 1 + } + } + + return true + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/PalindromePermutation.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/PalindromePermutation.swift new file mode 100644 index 0000000..81084ad --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/PalindromePermutation.swift @@ -0,0 +1,50 @@ +// +// PalindromePermutation.swift +// DataStructuresAlgorithms +// +// Created by Stefan Jaindl on 14.05.20. +// + +import Foundation + +open class PalindromePermutation { + private let alphabetSize: Int + + public init(alphabetSize: Int) { + self.alphabetSize = alphabetSize + } + + open func isPalindromePermutation(input: String) -> Bool { + if input.count < 2 { + return true + } + + var oddCounter = 0 + let bitVector = BitVector(numberOfBits: alphabetSize) + + for char in input { + guard let asciiValue = char.asciiValue else { + continue //disregard non ASCII chars.. + } + + if bitVector.isBitSet(index: Int(asciiValue)) { + bitVector.unsetBit(index: Int(asciiValue)) + oddCounter -= 1 + } else { + bitVector.setBit(index: Int(asciiValue)) + oddCounter += 1 + } + } + + if oddCounter <= 1 { + //For odd strings there must be exactly 1 odd char (center char) and for even strings no odd char + return true + } + + //Alternative with bit manipulation - It's a palindrome permuation, if: + //1. no bit is set (= no unique chars) --> bitvector Int == 0 + //2. or not more than 1 bit is set (middle one for odd strings) --> bitVector Int at pos - 1. pos - 1 & pos + + return false + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/PatternMatcher.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/PatternMatcher.swift new file mode 100644 index 0000000..aca3952 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/PatternMatcher.swift @@ -0,0 +1,110 @@ +// +// PatternMatcher.swift +// +// +// Created by Stefan Jaindl on 28.11.20. +// + +import Foundation + +//Checks whether string matches patterns in O(n^2) time. n for iterating through possible lenghts and n for building strings each +//Notice that in most cases early returns are done .. e.g. if one substring doesn't match or we there are impossible counts for a length. +open class PatternMatcher { + public init() { } + + open func matches(value: String, pattern: [PatternValue]) -> Bool { + guard !pattern.isEmpty, !value.isEmpty else { + return false + } + + //Count a's & b's in pattern + let aCount = pattern.filter { $0 == .a }.count + let bCount = pattern.filter { $0 == .b }.count + + if !basicChecks(value: value, aCount: aCount, bCount: bCount) { + return false + } + + //check increasing lengths (min len = 1) + let minLen = 1 + let maxLen = value.count - bCount + + PatternCheck: + for aLenght in minLen ..< maxLen { + let bLenght = (value.count - (aLenght * aCount)) / bCount + + if bLenght * bCount + aLenght * aCount != value.count { + //impossible pattern match + continue PatternCheck + } + + if checkPattern(aLenght: aLenght, bLength: bLenght, value: value, pattern: pattern) { + return true + } + } + + return false + } + + private func basicChecks(value: String, aCount: Int, bCount: Int) -> Bool { + if value.count < aCount + bCount { + return false + } + + if aCount == 0 { + return value.count % bCount == 0 + } + + if bCount == 0 { + return value.count % aCount == 0 + } + + return true + } + + private func checkPattern(aLenght: Int, bLength: Int, value: String, pattern: [PatternValue]) -> Bool { + var aPatternCount = 0 + var bPatternCount = 0 + var expectedA: String? + var expectedB: String? + for patternValue in pattern { + let offset = aPatternCount * aLenght + bPatternCount * bLength + + switch patternValue { + case .a: + if !patternValueIsValid(value: value, offset: offset, lenght: aLenght, expected: &expectedA) { + return false + } + aPatternCount += 1 + case .b: + if !patternValueIsValid(value: value, offset: offset, lenght: bLength, expected: &expectedB) { + return false + } + bPatternCount += 1 + } + } + + return true + } + + private func patternValueIsValid(value: String, offset: Int, lenght: Int, expected: inout String?) -> Bool { + let substring = String(value[value.index(pos: offset) ..< value.index(pos: offset + lenght)]) + if let expectedValue = expected, expectedValue != substring { + return false + } + expected = substring + + return true + } +} + +public extension String { + func index(pos: Int) -> String.Index { + return index(startIndex, offsetBy: pos) + } +} + +public enum PatternValue { + case a + case b +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/PondSizes.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/PondSizes.swift new file mode 100644 index 0000000..132bf84 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/PondSizes.swift @@ -0,0 +1,80 @@ +// +// PondSizes.swift +// +// +// Created by Stefan Jaindl on 29.11.20. +// + +import Foundation + +open class PondSizes { + public init() { } + + //Input is a RxC matrix, where 0 indicates water and everything > 0 is above sea level. + //Water can be horizontally, vertically and diagonally connected. + //Returns sizes of connected ponds (water) + open func sizes(matrix: [[Int]]) -> [Int] { + guard !matrix.isEmpty, !matrix[0].isEmpty else { + return [] + } + + var marked = [[Bool]](repeating: [Bool](repeating: false, count: matrix[0].count), count: matrix.count) + var ponds: [Int] = [] + + for row in 0 ..< matrix.count { + for column in 0 ..< matrix[0].count { + if marked[row][column] { + continue + } + + if matrix[row][column] == 0 { //Water! + ponds.append(pondCountDfs(matrix: matrix, marked: &marked, row: row, column: column)) + } + + marked[row][column] = true + } + } + + return ponds + } + + private func pondCountDfs(matrix: [[Int]], marked: inout [[Bool]], row: Int, column: Int) -> Int { + guard !marked[row][column] else { + return 0 + } + + marked[row][column] = true + + if matrix[row][column] != 0 { //no water here + return 0 + } + + var pondCount = 1 + for neighbour in neighbours(matrix: matrix, row: row, column: column) { + if !marked[neighbour.row][neighbour.column] { + pondCount += pondCountDfs(matrix: matrix, marked: &marked, row: neighbour.row, column: neighbour.column) + } + } + + return pondCount + } + + private func neighbours(matrix: [[Int]], row: Int, column: Int) -> [(row: Int, column: Int)] { + let rowIndices = [-1, 0, 1] + let columnIndices = [-1, 0, 1] + + var neighbourCells: [(row: Int, column: Int)] = [] + for rowIndex in rowIndices { + for columnIndex in columnIndices { + let neighbourColumn = column + columnIndex + let neighbourRow = row + rowIndex + if row + rowIndex >= 0, neighbourRow < matrix.count, neighbourColumn >= 0, neighbourColumn < matrix[0].count, + !(rowIndex == 0 && columnIndex == 0) { + neighbourCells.append((row: neighbourRow, column: neighbourColumn)) + } + } + } + + return neighbourCells + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/ShortestSuperSequence.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/ShortestSuperSequence.swift new file mode 100644 index 0000000..772b73a --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/ShortestSuperSequence.swift @@ -0,0 +1,115 @@ +// +// ShortestSuperSequence.swift +// +// +// Created by Stefan Jaindl on 12.12.20. +// + +import Foundation + +open class ShortestSuperSequence { + public init() { } + + open func findShortestSuperSequence(of smaller: [T], in bigger: [T]) -> SuperSequenceIndexPair? { + guard !smaller.isEmpty, !bigger.isEmpty, smaller.count < bigger.count else { + return nil + } + + let searchedCounts = buildSearchedTable(of: smaller) + + //Search in increasing slider ranges: + for arraySearchRange in smaller.count ... bigger.count { + var runningCounts = buildRunningCounts(of: bigger, with: searchedCounts, range: arraySearchRange, elementCount: smaller.count) + if runningCounts.matches { + return SuperSequenceIndexPair(start: 0, end: arraySearchRange - 1) + } + + //Iterate through array in current range: + for index in 0 ..< bigger.count - arraySearchRange { + let nextToBeRemoved = bigger[index] + let nextToBeAdded = bigger[index + arraySearchRange] + + updateRunningCounts(add: nextToBeAdded, remove: nextToBeRemoved, runningCounts: &runningCounts) + if runningCounts.matches { + return SuperSequenceIndexPair(start: index + 1, end: index + arraySearchRange) + } + } + } + + return nil + } + + private func updateRunningCounts(add: T, remove: T, runningCounts: inout SequenceResult) { + let removedMatching = addOrChangeExisting(table: &runningCounts.counts, element: remove, changeValue: 1) >= 0 + let addedMatching = addOrChangeExisting(table: &runningCounts.counts, element: add, changeValue: -1) >= 0 + + if removedMatching { + runningCounts.diffToTarget += 1 + } + + if addedMatching { + runningCounts.diffToTarget -= 1 + } + } + + private func buildSearchedTable(of array: [T]) -> [T: Int] { + //building table: value -> count + var table: [T: Int] = [:] + for element in array { + addOrIncrease(table: &table, element: element, increaseValue: 1) + } + + return table + } + + private func addOrChangeExisting(table: inout [T: Int], element: T, changeValue: Int) -> Int { + if let count = table[element] { + table[element] = count + changeValue + return count + changeValue + } + + return -1 + } + + private func addOrIncrease(table: inout [T: Int], element: T, increaseValue: Int) { + if let count = table[element] { + table[element] = count + increaseValue + } else { + table[element] = increaseValue + } + } + + private func buildRunningCounts(of array: [T], with searched: [T: Int], range: Int, elementCount: Int) -> SequenceResult { + var diffToTarget = elementCount + var counts = searched + + for curIndex in 0 ..< range { + let curElement = array[curIndex] + if let matched = searched[curElement] { + counts[curElement] = matched - 1 + diffToTarget = max(0, diffToTarget - 1) + } + } + + return SequenceResult(counts: counts, diffToTarget: diffToTarget) + } +} + +public struct SuperSequenceIndexPair: Equatable { + let start: Int + let end: Int +} + +open class SequenceResult { + var counts: [T: Int] + var diffToTarget: Int + + init(counts: [T: Int], diffToTarget: Int) { + self.counts = counts + self.diffToTarget = diffToTarget + } + + var matches: Bool { + return diffToTarget == 0 + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/SmallestDifference.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/SmallestDifference.swift new file mode 100644 index 0000000..68a6e03 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/SmallestDifference.swift @@ -0,0 +1,41 @@ +// +// SmallestDifference.swift +// +// +// Created by Stefan Jaindl on 17.11.20. +// + +import Foundation + +open class SmallestDifference { + public init() { } + + open func smallestDifference(first: [Int], second: [Int]) -> Int? { + guard !first.isEmpty, !second.isEmpty else { + return nil + } + + let firstSorted = first.sorted(by: { $0 < $1 } ) + let secondSorted = second.sorted(by: { $0 < $1 } ) + + var firstIndex = 0 + var secondIndex = 0 + var smallestDifference = Int.max + + while firstIndex < firstSorted.count, secondIndex < secondSorted.count { + let difference = firstSorted[firstIndex] - secondSorted[secondIndex] + + if difference >= 0, difference < smallestDifference { + smallestDifference = difference + } + + if firstSorted[firstIndex] < secondSorted[secondIndex] { + firstIndex += 1 + } else { + secondIndex += 1 + } + } + + return smallestDifference + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/SpaceInserter.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/SpaceInserter.swift new file mode 100644 index 0000000..b0491f6 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/SpaceInserter.swift @@ -0,0 +1,72 @@ +// +// SpaceInserter.swift +// +// +// Created by Stefan Jaindl on 10.12.20. +// + +import Foundation + +open class SpaceInserter { + public init() { } + + open func insertSpaces(into string: String, wordsDict: [String]) -> String { + guard !wordsDict.isEmpty, !string.isEmpty else { + return string + } + + let characters = Array(string) + var spacedString: [Character] = [] + let trie = buildTrie(wordsDict: wordsDict) + + var index = 0 + while index < characters.count { + let remainder = Array(characters[index ..< characters.count]) + let word = findWord(string: remainder, trie: trie) + if !word.isEmpty { + if !spacedString.isEmpty, spacedString[spacedString.count - 1] != " " { + spacedString += " " + } + spacedString.append(contentsOf: "\(String(word))") + if index != characters.count - 1 { + spacedString.append(" ") + } + index += word.count + } else { + spacedString.append(characters[index]) + index += 1 + } + } + + return String(spacedString).trimmingCharacters(in: CharacterSet(arrayLiteral: " ")) + } + + private func findWord(string: [Character], trie: Trie) -> [Character] { + var trieNode: TrieNode? = trie.root + var found: [Character] = [] + var curIndex = 0 + + while curIndex < string.count, let node = trieNode { + let char = string[curIndex] + trieNode = node.childNodes[char] + + if trieNode?.terminates == true { + found = Array(string[0 ... curIndex]) + } + + curIndex += 1 + } + + return found + } + + private func buildTrie(wordsDict: [String]) -> Trie { + let trie = Trie() + + for word in wordsDict { + trie.insert(word: word) + } + + return trie + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/SparseMatrix.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/SparseMatrix.swift new file mode 100644 index 0000000..65868ac --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/SparseMatrix.swift @@ -0,0 +1,123 @@ +// +// SparseMatrix.swift +// +// +// Created by Stefan Jaindl on 23.12.20. +// + +import Foundation + +open class SparseMatrix { + + public init() { } + + open func similarity(of documents: [SparseDocument]) -> [Similarity] { + guard !documents.isEmpty else { + return [] + } + + //map from words to documents: + let mapping = buildIntToDocumentMapping(documents: documents) + + //intersection count for each document pair: + let intersections = intersectionCounts(of: mapping) + + //calculate union and intersection counts of documents where an intersection exists + let documentDict = buildDocumentIdDict(documents: documents) + + return calculateSimilarities(documentDict: documentDict, intersectionCounts: intersections) + } + + private func intersectionCounts(of mapping: [Int: [DocumentId]]) -> [DocumentPair: Int] { + var counts = [DocumentPair: Int]() + + for documents in mapping.values { + for (index, firstDoc) in documents.enumerated() { + var secondIndex = index + 1 + while secondIndex < documents.count { + let pair = DocumentPair(firstId: firstDoc, secondId: documents[secondIndex]) + + if let count = counts[pair] { + counts[pair] = count + 1 + } else { + counts[pair] = 1 + } + + secondIndex += 1 + } + } + } + + return counts + } + + private func buildIntToDocumentMapping(documents: [SparseDocument]) -> [Int: [DocumentId]] { + var mapping = [Int: [Int]]() //dict from word to docId's + + for document in documents { + for word in document.words { + if var docs = mapping[word] { + docs.append(document.id) + mapping[word] = docs + } else { + let docs = [document.id] + mapping[word] = docs + } + } + } + + return mapping + } + + private func buildDocumentIdDict(documents: [SparseDocument]) -> [DocumentId: SparseDocument] { + var documentDict = [DocumentId: SparseDocument]() + + for document in documents { + documentDict[document.id] = document + } + + return documentDict + } + + private func calculateSimilarities(documentDict: [DocumentId: SparseDocument], intersectionCounts: [DocumentPair: Int]) -> [Similarity] { + var similarities = [Similarity]() + + for (documentPair, intersectionCount) in intersectionCounts { + guard let firstTotal = documentDict[documentPair.firstId]?.words.count, + let secondTotal = documentDict[documentPair.secondId]?.words.count else { + continue + } + + let totalCount = firstTotal + secondTotal + let unionCount = totalCount - intersectionCount //intersection counted twice + let similarity = Similarity(documents: documentPair, similarity: Double(Double(intersectionCount) / Double(unionCount))) + + similarities.append(similarity) + } + + return similarities + } +} + +public struct SparseDocument { + let id: DocumentId + let words: [Int] +} + +public struct Similarity: Equatable { + let documents: DocumentPair + let similarity: Double + + public static func == (lhs: Similarity, rhs: Similarity) -> Bool { + return lhs.documents.firstId == rhs.documents.firstId && + lhs.documents.secondId == rhs.documents.secondId && + lhs.similarity == rhs.similarity + } +} + +public struct DocumentPair: Hashable { + let firstId: DocumentId + let secondId: DocumentId +} + +typealias DocumentId = Int diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/StringCompresser.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/StringCompresser.swift new file mode 100644 index 0000000..ff2221f --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/StringCompresser.swift @@ -0,0 +1,75 @@ +// +// OneAwayChecker.swift +// DataStructuresAlgorithms +// +// Created by Stefan Jaindl on 15.05.20. +// + +import Foundation + +open class StringCompresser { + + public init() { } + + //Performs simple string compression + open func compress(string: String) -> String { + if string.count <= 2 { + return string + } + + let compressedStringCount = compressedCount(string: string) + + if compressedStringCount >= string.count { + return string + } + + var compressed: [Character] = [] + compressed.reserveCapacity(compressedStringCount) + + var currentCharCount = 0 + var currentChar: Character? + + for char in string { + guard let current = currentChar else { + currentChar = char + currentCharCount += 1 + continue + } + + if current == char { + currentCharCount += 1 + } else { + compressed.append(current) + compressed.append(contentsOf: String(currentCharCount)) + currentChar = char + currentCharCount = 1 + } + } + + if currentCharCount > 0, let currentChar = currentChar { + compressed.append(currentChar) + compressed.append(contentsOf: String(currentCharCount)) + } + + return String(compressed) + } + + func compressedCount(string: String) -> Int { + var count = 0 + var currentChar: Character? + for char in string { + guard let current = currentChar else { + count += 1 + currentChar = char + continue + } + + if char != current { + count += 1 + currentChar = char + } + } + + return count * 2 + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/StringPermutationChecker.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/StringPermutationChecker.swift new file mode 100644 index 0000000..e286cc6 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/StringPermutationChecker.swift @@ -0,0 +1,59 @@ +// +// StringPermuationChecker.swift +// +// +// Created by Stefan Jaindl on 09.05.20. +// + +import Foundation + +open class StringPermuationChecker { + private let alphabetSize: Int + + init(alphabetSize: Int) { + self.alphabetSize = alphabetSize + } + + open func isPermutationOfByCharArray(first: String, second: String) -> Bool { + if first.count != second.count { + //Permutations must have same size + return false + } + + //Check for same character count: + var array = [Int] (repeating: 0, count: alphabetSize) + for char in Array(first) { + guard let ascii = char.asciiValue else { + return false + } + + array[Int(ascii)] += 1 + } + + for char in Array(second) { + guard let ascii = char.asciiValue else { + return false + } + + array[Int(ascii)] -= 1 + if array[Int(ascii)] < 0 { + return false + } + } + + return true + } + + open func isPermutationOfBySorting(first: String, second: String) -> Bool { + if first.count != second.count { + //Permutations must have same size + return false + } + + //Sort strings + let firstSorted = first.sorted() + let secondSorted = second.sorted() + + return firstSorted == secondSorted + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/StringRotation.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/StringRotation.swift new file mode 100644 index 0000000..7f2540d --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/StringRotation.swift @@ -0,0 +1,29 @@ +// StringRotation.swift +// DataStructuresAlgorithms +// +// Created by Stefan Jaindl on 20.05.20. +// + +import Foundation + +open class StringRotation { + + public init() { } + + open func isRotation(firstString: String, secondString: String) -> Bool { + if firstString.count != secondString.count { + return false + } + + if firstString == secondString { + return false + } + + if firstString.count <= 1 { + return true + } + + let appendedString = secondString + secondString + return appendedString.contains(firstString) + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/SumSwapper.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/SumSwapper.swift new file mode 100644 index 0000000..028a905 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/SumSwapper.swift @@ -0,0 +1,54 @@ +// +// SumSwapper.swift +// +// +// Created by Stefan Jaindl on 30.11.20. +// + +import Foundation + +open class SumSwapper { + public init() { } + + open func swap(first: [Int], second: [Int]) -> Swappable? { + let firstSum = sum(of: first) + let secondSum = sum(of: second) + let diff = secondSum - firstSum + + guard diff % 2 == 0 else { + return nil //not possible to have same sum with odd and even counts + } + + let searchedSwapDiff = diff / 2 + + let firstSet = buildSet(from: first) + let secondSet = buildSet(from: second) + + for number in firstSet { + let searched = number + searchedSwapDiff + if secondSet.contains(where: { $0 == searched }) { + return Swappable(first: number, second: searched) + } + } + + return nil + } + + private func sum(of array: [Int]) -> Int { + return array.reduce(0) { $0 + $1 } + } + + private func buildSet(from array: [Int]) -> Set { + var set = Set() + array.forEach { + set.insert($0) + } + + return set + } +} + +public struct Swappable: Equatable { + let first: Int + let second: Int +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/T9Keyboard.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/T9Keyboard.swift new file mode 100644 index 0000000..f6929e6 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/T9Keyboard.swift @@ -0,0 +1,92 @@ +// +// T9Keyboard.swift +// +// +// Created by Stefan Jaindl on 30.11.20. +// + +import Foundation + +public struct KeyMapping { + let mapping: [Int: [Character]] +} + +open class T9Keyboard { + let keyMapping: KeyMapping? + let validWords: Trie //trie with root node + + let defaultKeyMapping = KeyMapping(mapping: [1: [], + 2: ["a", "b", "c"], + 3: ["d", "e", "f"], + 4: ["g", "h", "i"], + 5: ["j", "k", "l"], + 6: ["m", "n", "o"], + 7: ["p", "q", "r", "s"], + 8: ["t", "u", "v"], + 9: ["w", "x", "y", "z"]]) + + public init(validWords: Trie, keyMapping: KeyMapping? = nil) { + self.validWords = validWords + self.keyMapping = keyMapping + } + + private func getMapping() -> [Int: [Character]] { + let mapping = keyMapping ?? defaultKeyMapping + return mapping.mapping + } + + //given: Mapping, digits, valid words + //returns: Possible valid words (autocomplete) + open func autocomplete(for digits: [Int]) throws -> [String] { + guard !digits.isEmpty else { + return [] + } + + return try autocomplete(for: digits, mapping: getMapping(), wordSoFar: [], currentTrieNode: validWords.root) + } + + private func autocomplete(for digits: [Int], mapping: [Int: [Character]], wordSoFar: [Character], currentTrieNode: TrieNode?) throws -> [String] { + guard let currentTrieNode = currentTrieNode else { + return [] + } + + if digits.isEmpty { + return completeWords(wordSoFar: wordSoFar, currentTrieNode: currentTrieNode) + } + + var validWords: [String] = [] + + let firstDigit = digits[0] + let remainingDigits = Array(digits[1 ..< digits.count]) + + guard firstDigit >= 0, firstDigit <= 9, let chars = mapping[firstDigit] else { + throw NSError(domain: "T9Keyboard: Invalid mapping", code: 0, userInfo: nil) + } + + for char in chars { + let node = currentTrieNode.childNodes[char] + var newWord = wordSoFar + newWord.append(char) + validWords.append(contentsOf: try autocomplete(for: remainingDigits, mapping: mapping, wordSoFar: newWord, currentTrieNode: node)) + } + + return validWords + } + + private func completeWords(wordSoFar: [Character], currentTrieNode: TrieNode) -> [String] { + var validWords: [String] = [] + + if currentTrieNode.terminates { + validWords.append(String(wordSoFar)) + } + + currentTrieNode.childNodes.forEach { child in + let node = child.value + var newWord = wordSoFar + newWord.append(child.key) + validWords.append(contentsOf: completeWords(wordSoFar: newWord, currentTrieNode: node)) + } + + return validWords + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/UniqueStringSearch.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/UniqueStringSearch.swift new file mode 100644 index 0000000..5702e7a --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/UniqueStringSearch.swift @@ -0,0 +1,79 @@ +// +// UniqueStringSearch.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 03.05.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +open class UniqueStringSearch { + + private let alphabetSize: Int + + public init(alphabetSize: Int) { + self.alphabetSize = alphabetSize + } + + //V1: HashTable/Set + open func isUniqueStringWithHash(input: String) -> Bool { + if input.count > alphabetSize { + return false + } + + var charSet = Set() + + for char in Array(input) { + if charSet.contains(char) { + return false + } + + charSet.insert(char) + } + + return true + } + + //V2: BitVector + open func isUniqueStringWithBitVector(input: String) -> Bool { + if input.count > alphabetSize { + return false + } + + let bitVector = BitVector(numberOfBits: alphabetSize) + + for char in Array(input) { + guard let ascii = char.asciiValue else { + continue + } + + if bitVector.isBitSet(index: Int(ascii)) { + return false + } + + bitVector.setBit(index: Int(ascii)) + } + + return true + } + + //V3: Without additional datastructures + open func isUniqueStringWithSorting(input: String) -> Bool { + if input.count > alphabetSize { + return false + } + + let sorted = input.sorted() + var char: Character? + + for curChar in Array(sorted) { + if let char = char, curChar == char { + return false + } + char = curChar + } + + return true + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/Urlify.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/Urlify.swift new file mode 100644 index 0000000..4af361d --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/Urlify.swift @@ -0,0 +1,52 @@ +// +// Urlify.swift +// DataStructuresAlgorithms +// +// Created by Stefan Jaindl on 10.05.20. +// + +import Foundation + +open class Urlifier { + + public init() { } + + open func urlify(_ input: inout [Character]) { + if input.isEmpty { + return + } + + var charCount = input.count + + //count nr. of spaces to replace: + var spaces = 0 + for charCount in 0 ..< input.count { + if input[charCount] == " " { + spaces += 1 + } + } + + //count incl. replaced spaces (%20) + var finalCount = input.count + spaces * (3 - 1) + + //resize array, if necessary: + while input.count < finalCount { + input.append(" ") + } + + //We can optimize here: if charCount == finalCount, we already have replaced all blanks + while charCount != finalCount { + let char = input[charCount - 1] + if char == " " { + input[finalCount - 3] = "%" + input[finalCount - 2] = "2" + input[finalCount - 1] = "0" + finalCount -= 3 + } else { + input[finalCount - 1] = char + finalCount -= 1 + } + charCount -= 1 + } + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/WordFrequencyCounter.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/WordFrequencyCounter.swift new file mode 100644 index 0000000..678cbe4 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/WordFrequencyCounter.swift @@ -0,0 +1,30 @@ +// +// WordFrequencyCounter.swift +// +// +// Created by Stefan Jaindl on 15.11.20. +// + +import Foundation + +open class WordFrequencyCounter { + private var wordCounts: [String: Int] = [:] + + public init(words: [String]) { + for word in words { + let adjusted = word.lowercased().trimmingCharacters(in: CharacterSet(charactersIn: " ")) + let count: Int + if let currentCount = wordCounts[adjusted] { + count = currentCount + 1 + } else { + count = 1 + } + + wordCounts[adjusted] = count + } + } + + open func count(of word: String) -> Int { + return wordCounts[word.lowercased().trimmingCharacters(in: CharacterSet(charactersIn: " "))] ?? 0 + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/WordRectangle.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/WordRectangle.swift new file mode 100644 index 0000000..2ffa8a6 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/WordRectangle.swift @@ -0,0 +1,157 @@ +// +// WordRectangle.swift +// +// +// Created by Stefan Jaindl on 21.12.20. +// + +import Foundation + +open class WordRectangle { + public init() { } + + open func maxRectangle(of words: [String]) -> [String] { + guard !words.isEmpty else { + return [] + } + + let wordsByLength = words.sorted(by: { $0.count > $1.count }) + let wordsDict = wordsDictByLen(wordsByLength: wordsByLength) + let tries = buildTriesByLen(wordsByLength: wordsByLength) + let sizes = decreasingSizes(to: wordsByLength[0].count) + + for size in sizes { + guard let columnTrie = tries[size.col], + var wordsSet = wordsDict[size.row] else { + continue + } + + let maxRectangle = buildRows(rowLen: size.row, colLen: size.col, rootTrie: columnTrie, availableRowWords: &wordsSet) + if !maxRectangle.isEmpty { + return maxRectangle + } + } + + return [] + } + + private func buildRows(rowLen: Int, colLen: Int, rootTrie: Trie, availableRowWords: inout Set<[Character]>) -> [String] { + var tries = [TrieNode]() + for _ in 0 ..< colLen { + tries.append(rootTrie.root) + } + + var rows = [String]() + rows = buildRow(rowLen: rowLen, colLen: colLen, rows: &rows, tries: tries, availableRowWords: &availableRowWords) + + return rows + } + + private func buildRow(rowLen: Int, colLen: Int, rows: inout [String], tries: [TrieNode], availableRowWords: inout Set<[Character]>) -> [String] { + if rows.count == rowLen { + for colIndex in 0 ..< colLen { + var colWord = [Character]() + for rowIndex in 0 ..< rowLen { + let row = Array(rows[rowIndex]) + let char = row[colIndex] + colWord.append(char) + } + if !availableRowWords.contains(colWord) { + return [] + } + } + + return rows + } + + var wordsArray = [String]() + for word in availableRowWords { + wordsArray.append(String(word)) + } + + let permutations = Permuation().permutations(array: &wordsArray) + + for permutation in permutations { + + var newTrieNodes = [TrieNode]() + + Word: + for rowWord in permutation { + let rowChars = Array(rowWord) + for column in 0 ..< colLen { + let trie = tries[column] + let charOfRowWord = rowChars[column] + if let child = trie.childNodes[charOfRowWord] { + newTrieNodes.append(child) + } else { + continue Word + } + } + + availableRowWords.remove(rowChars) + rows.append(String(rowChars)) + let buildRows = buildRow(rowLen: rowLen, colLen: colLen, rows: &rows, tries: newTrieNodes, availableRowWords: &availableRowWords) + rows.removeLast() + availableRowWords.insert(rowChars) + + if !buildRows.isEmpty { + return buildRows + } + } + } + + return [] + } + + private func decreasingSizes(to: Int) -> [(row: Int, col: Int)] { + var sizes = [(row: Int, col: Int)]() + for row in 1 ... to { + for col in 1 ... to { + sizes.append((row: row, col: col)) + } + } + + return sizes.sorted(by: { $0.row * $0.col > $1.row * $1.col }) + } + + private func wordsDictByLen(wordsByLength: [String]) -> [Int: Set<[Character]>] { + var dict = [Int: Set<[Character]>]() + + for word in wordsByLength { + if word.isEmpty { + continue + } + + if var curEntry = dict[word.count] { + curEntry.insert(Array(word)) + dict[word.count] = curEntry + } else { + var set = Set<[Character]>() + set.insert(Array(word)) + dict[word.count] = set + } + } + + return dict + } + + private func buildTriesByLen(wordsByLength: [String]) -> [Int: Trie] { + var tries = [Int: Trie]() + + for word in wordsByLength { + if word.isEmpty { + continue + } + + if let curTrie = tries[word.count] { + curTrie.insert(word: word) + } else { + let curTrie = Trie() + curTrie.insert(word: word) + tries[word.count] = curTrie + } + } + + return tries + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/WordTransformer.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/WordTransformer.swift new file mode 100644 index 0000000..76cc3df --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/WordTransformer.swift @@ -0,0 +1,96 @@ +// +// WordTransformer.swift +// +// +// Created by Stefan Jaindl on 18.12.20. +// + +import Foundation + +private let wildcardChar: Character = "*" + +open class WordTransformer { + + let words: Set + var oneEditAway: [WildcardMapping: Set<[Character]>] = [:] + + public init(words: Set) { + self.words = words + buildValidWords(words: words) + } + + open func transform(_ first: String, into second: String) -> [String] { + guard !first.isEmpty, !second.isEmpty, first.count == second.count else { + return [] + } + + var checked = Set<[Character]>() + checked.insert(Array(first)) + return transform(searched: Array(second), path: [Array(first)], checked: &checked) + } + + private func transform(searched: [Character], path: [[Character]], checked: inout Set<[Character]>) -> [String] { + let current = Array(path[path.count - 1]) + + if current == searched { + var stringPath: [String] = [] + for chars in path { + stringPath.append(String(chars)) + } + + return stringPath + } + + for index in 0 ..< searched.count { + let mapping = WildcardMapping(chars: current, wildcardIndex: index) + + for validChar in oneEditAway[mapping] ?? [] { + let changedWord = validChar + + if !checked.contains(changedWord) { + checked.insert(changedWord) + var newPath = path + newPath.append(changedWord) + + let subPath = transform(searched: searched, path: newPath, checked: &checked) + if !subPath.isEmpty { + return subPath + } + } + } + } + + return [] + } + + private func buildValidWords(words: Set) { + for word in words { + for (index, _) in word.enumerated() { + let mapping = WildcardMapping(chars: Array(word), wildcardIndex: index) + if var oneAway = oneEditAway[mapping] { + oneAway.insert(Array(word)) + oneEditAway[mapping] = oneAway + } else { + oneEditAway[mapping] = [Array(word)] + } + } + } + } +} + +public struct WildcardMapping: Hashable, Equatable { + let chars: [Character] + let wildcardIndex: Int + + init(chars: [Character], wildcardIndex: Int) { + var adaptedChars = chars + adaptedChars[wildcardIndex] = wildcardChar + + self.chars = adaptedChars + self.wildcardIndex = wildcardIndex + } + + public static func == (lhs: WildcardMapping, rhs: WildcardMapping) -> Bool { + return lhs.chars == rhs.chars && lhs.wildcardIndex == rhs.wildcardIndex + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/XMLEncoder.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/XMLEncoder.swift new file mode 100644 index 0000000..edf65f8 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/XMLEncoder.swift @@ -0,0 +1,106 @@ +// +// XMLEncoder.swift +// +// +// Created by Stefan Jaindl on 23.11.20. +// + +import Foundation + +public enum XMLElement { + case tag(encoded: Int) + case end + case value(value: String) +} + +open class XMLEncoder { + public init() { } + + private let endEncoding = "0" + + open func encode(xml: String, attributesMapping: [String: Int]) throws -> String { + guard !xml.isEmpty else { + return "" + } + + let elements = try buildElements(xml: xml, attributesMapping: attributesMapping) + let encoded = encode(elements: elements) + + return encoded + } + + private func buildElements(xml: String, attributesMapping: [String: Int]) throws -> [XMLElement] { + var remainingXml = xml + var elements: [XMLElement] = [] + + while !remainingXml.isEmpty { + //First, search for string enclosed in < .. > + guard let elementOpenIndex = remainingXml.firstIndex(of: "<"), let closeIndex = remainingXml.firstIndex(of: ">") else { + throw NSError(domain: "XMLEncoder: Invalid XML", code: 0, userInfo: nil) + } + + let elementCloseIndex = remainingXml.index(after: closeIndex) + let element = String(remainingXml[elementOpenIndex ..< elementCloseIndex]) + remainingXml = String(remainingXml[elementCloseIndex ..< remainingXml.endIndex]) + + let subElements = element.split(separator: " ") + try subElements.forEach { subElement in + let endsWithClosing = subElement[subElement.index(before: subElement.endIndex)] == ">" + + if subElement.starts(with: "")).trimmingCharacters(in: CharacterSet(arrayLiteral: "\""))) + + guard let encoding = attributesMapping[tag] else { + throw NSError(domain: "XMLEncoder: Missing mapping for \(tag)", code: 0, userInfo: nil) + } + + elements.append(.tag(encoded: encoding)) + elements.append(.value(value: tagValue)) + } + + if endsWithClosing, let possibleValueIndex = remainingXml.firstIndex(of: "<") { + let value = remainingXml[remainingXml.startIndex ..< possibleValueIndex].trimmingCharacters(in: CharacterSet(arrayLiteral: " ")) + if !value.isEmpty { + elements.append(.value(value: value)) + } + } + } + } + + return elements + } + + private func encode(elements: [XMLElement]) -> String { + var encodedString = "" + + elements.forEach { element in + switch element { + case .tag(let encoded): + encodedString.append(" \(encoded)") + case .end: + encodedString.append(" \(endEncoding)") + case .value(value: let value): + encodedString.append(" \(value)") + } + } + + return encodedString.trimmingCharacters(in: CharacterSet(arrayLiteral: " ")) + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/ZeroMatrix.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/ZeroMatrix.swift new file mode 100644 index 0000000..efc011f --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/ArraysAndStrings/ZeroMatrix.swift @@ -0,0 +1,84 @@ +// ZeroMatrix.swift +// DataStructuresAlgorithms +// +// Created by Stefan Jaindl on 19.05.20. +// + +import Foundation + +open class ZeroMatrix { + + public init() { } + + open func zeroMatrix(matrix: inout [[Int]]) { + guard !matrix.isEmpty, !matrix[0].isEmpty else { + return + } + + //We use the first row and column to zero the matrix in place + + //So, first remember whether we have to nullify first row and column + let nullifyFirstRow = checkNullifyFirstRow(matrix: matrix) + let nullifyFirstColumn = checkNullifyFirstColumn(matrix: matrix) + + //Check remaining cols/rows + for row in 1 ..< matrix.count { + for column in 1 ..< matrix[0].count { + if matrix[row][column] == 0 { + matrix[0][column] = 0 + matrix[row][0] = 0 + } + } + } + + //Nullify remaining columns + for column in 1 ..< matrix[0].count { + if matrix[0][column] == 0 { + for row in 1 ..< matrix.count { + matrix[row][column] = 0 + } + } + } + + //Nullify remaining rows + for row in 1 ..< matrix.count { + if matrix[row][0] == 0 { + for column in 1 ..< matrix[0].count { + matrix[row][column] = 0 + } + } + } + + if nullifyFirstRow { + for column in 0 ..< matrix[0].count { + matrix[0][column] = 0 + } + } + + if nullifyFirstColumn { + for row in 0 ..< matrix.count { + matrix[row][0] = 0 + } + } + } + + private func checkNullifyFirstRow(matrix: [[Int]]) -> Bool { + for col in 0 ..< matrix[0].count { + if matrix[0][col] == 0 { + return true + } + } + + return false + } + + private func checkNullifyFirstColumn(matrix: [[Int]]) -> Bool { + for row in 0 ..< matrix.count { + if matrix[row][0] == 0 { + return true + } + } + + return false + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/Adder.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/Adder.swift new file mode 100644 index 0000000..56ec286 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/Adder.swift @@ -0,0 +1,73 @@ +// +// Adder.swift +// +// +// Created by Stefan Jaindl on 03.12.20. +// + +import Foundation + +open class Adder { + public init() { } + + open func addWithoutPlus(first: Int, second: Int) -> Int { + var currentFirst = first + var currentSecond = second + var shiftIndex = 0 + var result = 0 + var carry = 0 + + while currentFirst != 0 || currentSecond != 0 || carry != 0 { + let firstDigit = currentFirst & 1 + let secondDigit = currentSecond & 1 + var bit = firstDigit ^ secondDigit + if carry == 1 { + bit = bit == 1 ? 0 : 1 + } + + carry = firstDigit & secondDigit == 1 || carry > 0 && firstDigit | secondDigit > 0 ? 1 : 0 + result |= (bit << shiftIndex) + currentFirst >>= 1 + currentSecond >>= 1 + shiftIndex = incrementByOne(bit: shiftIndex) + } + + return result + } + + open func incrementByOne(bit: Int) -> Int { + //1. Find rightmost 0 bit & Toggle all trailing 1's to 0 (right of index) + //2. toggle index place to 1 + let zeroIndex = rightmostZeroIndex(of: bit) + let temp = setRangeToZero(bit: bit, at: zeroIndex) + return setBitToOne(of: temp, until: zeroIndex) + } + + private func rightmostZeroIndex(of bit: Int) -> Int { + var index = 0 + var shiftedBit = bit + + while shiftedBit & 1 != 0 { + shiftedBit >>= 1 + index += 1 + } + + return index + } + + private func setRangeToZero(bit: Int, at index: Int) -> Int { + //mask format: e.g.: 111111111111111111111111111111111111111111111111111111111111100 (for index 2) + let mask = Int.max & ((Int.max >> index) << index) + + return bit & mask + + //alternative: let mask = (1 << index) - 1; return bit ^ mask + } + + private func setBitToOne(of bit: Int, until index: Int) -> Int { + //mask format: e.g.: 100 (for index 2) + let mask = 1 << index + + return bit | mask + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/BinaryNearbySearcher.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/BinaryNearbySearcher.swift new file mode 100644 index 0000000..77b8086 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/BinaryNearbySearcher.swift @@ -0,0 +1,103 @@ +// +// BinaryNearbySearcher.swift +// +// +// Created by Stefan Jaindl on 29.07.20. +// + +import Foundation + +open class BinaryNearbySearcher { + public init() { } + + private enum SearchType { + case smaller, bigger + } + + //Returns next smaller (if existing) and next bigger number with the same amount of 1 bits in its binary representation + public func nearestBinaryNumbers(number: Int) -> (Int?, Int?) { + let smaller = nearest(number: number, type: .smaller) + let bigger = nearest(number: number, type: .bigger) + + return (bigger, smaller) + } + + private func nearest(number: Int, type: SearchType) -> Int? { + var runningMask = 0b1 + var lastRight = 0b0 + var right = 0b0 + var shiftedCount = 0 + var nonTrailingFound = false + var nonTrailingCount = 0 + var found = false + + if number == 0 { + //nearest not existing + return nil + } + + while !found { + if runningMask > (number << 1) { + //nearest not existing + return nil + } + + let result = number & runningMask + + switch type { + case .smaller: + if result == 0 { + if nonTrailingFound { + nonTrailingCount += 1 + } + nonTrailingFound = true + } else if nonTrailingFound { + found = true + } + case .bigger: + if result > 0 { + if nonTrailingFound { + nonTrailingCount += 1 + } + nonTrailingFound = true + } else if nonTrailingFound { + found = true + } + } + + shiftedCount += 1 + runningMask <<= 1 + + if !found { + lastRight = right + right |= result + } else { + right = lastRight + } + } + + var result = number >> shiftedCount + + result <<= 1 + if type == .bigger { + result |= 0b1 + } + + result <<= 1 + if type == .smaller { + result |= 0b1 + } + + result <<= shiftedCount - 2 + + if type == .bigger { + while right > 0, right & 0b1 == 0 { + right >>= 1 + } + } else if type == .smaller { + right <<= nonTrailingCount + } + + return result | right + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/BinaryNumberConverter.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/BinaryNumberConverter.swift new file mode 100644 index 0000000..34a227c --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/BinaryNumberConverter.swift @@ -0,0 +1,34 @@ +// +// BinaryNumberConverter.swift +// +// +// Created by Stefan Jaindl on 31.07.20. +// + +import Foundation + +open class BinaryNumberConverter { + + public init() { } + + open func binaryConversionCount(from first: Int, to second: Int) -> Int { + var result = first ^ second + var bitsDifferentCount = 0 + + while result > 0 { + if result & 0b1 == 1 { + bitsDifferentCount += 1 + } + result >>= 1 + } + + /* Alternative: + while result > 0 { + bitsDifferentCount += 1 + result = result & (result - 1) + } + */ + + return bitsDifferentCount + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/BinaryToStringConverter.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/BinaryToStringConverter.swift new file mode 100644 index 0000000..2bce7b1 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/BinaryToStringConverter.swift @@ -0,0 +1,40 @@ +// +// BinaryToStringConverter.swift +// +// +// Created by Stefan Jaindl on 27.07.20. +// + +import Foundation + + +open class BinaryToStringConverter { + + private let errorString = "ERROR" + + public init() { } + + //Converts a real number between 0 and 1 to its binary representation + //x1 * 1 / 2^1 + x2 * 1 / 2^2 + x3 * 1 / 2^3 + .. + + func binaryRepresentation(of number: Double) -> String { + var binary = "." + var currentNumber = number + + if currentNumber <= 0 || currentNumber >= 1 { + return errorString + } + + while currentNumber > 0 { + if binary.count >= 32 { + return errorString + } + + let digit = currentNumber * 2 //Same as shift left by 1 + binary.append(digit >= 1 ? "1" : "0") + currentNumber = digit >= 1 ? digit - 1 : digit + } + + return binary + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/BitFlipper.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/BitFlipper.swift new file mode 100644 index 0000000..49d6bf5 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/BitFlipper.swift @@ -0,0 +1,35 @@ +// +// BitFlipper.swift +// +// +// Created by Stefan Jaindl on 27.07.20. +// + +import Foundation + +open class BitFlipper { + public init() { } + + open func findLongestOneBitSequenceByFlippingMaxOneBit(input: Int) -> Int { + var maxi = 0 + var countWithZero = 0 + var countWithoutZero = 0 + var current = input + + while current != 0 { + let bit = current & 1 + if bit == 1 { + countWithZero += 1 + countWithoutZero += 1 + } else { + countWithZero = countWithoutZero + 1 + countWithoutZero = 0 + } + + maxi = max(maxi, countWithZero) + current >>= 1 + } + + return maxi + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/BitInserter.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/BitInserter.swift new file mode 100644 index 0000000..4be2d6b --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/BitInserter.swift @@ -0,0 +1,31 @@ +// +// BitInserter.swift +// +// +// Created by Stefan Jaindl on 23.07.20. +// + +import Foundation + +open class BitInserter { + //Inserts bits of numberToInsert into baseNumber at given positions + public init() { } + + open func insert(_ numberToInsert: Int, into baseNumber: Int, fromBitIndex: Int, toBitIndex: Int) -> Int { + let cleared = clearBits(number: baseNumber, fromBitIndex: fromBitIndex, toBitIndex: toBitIndex) + return insert(numberToInsert: numberToInsert, cleared: cleared, fromBitIndex: fromBitIndex) + } + + private func clearBits(number: Int, fromBitIndex: Int, toBitIndex: Int) -> Int { + var bitMask = -1 << (fromBitIndex + toBitIndex - 1) + for index in 0 ..< fromBitIndex { + bitMask |= 1 << index + } + + return number & bitMask + } + + private func insert(numberToInsert: Int, cleared: Int, fromBitIndex: Int) -> Int { + return (numberToInsert << fromBitIndex) | cleared + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/NumberMax.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/NumberMax.swift new file mode 100644 index 0000000..5547380 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/NumberMax.swift @@ -0,0 +1,36 @@ +// +// NumberMax.swift +// +// +// Created by Stefan Jaindl on 18.11.20. +// + +import Foundation + +open class NumberMax { + public init() { } + + //Finding max element without if-else and comparison operators + open func max(first: Int, second: Int) -> Int { + var firstShifted = first + var secondShifted = second + var diff = first ^ second + + if diff == 0 { + //first == second, return any + return first + } + + while diff != 1 { + diff >>= 1 + firstShifted >>= 1 + secondShifted >>= 1 + } + + let firstBigger = (firstShifted & 0b1) == 1 + let firstValue = Int(truncating: NSNumber(value: firstBigger)) + let secondValue = Int(truncating: NSNumber(value: !firstBigger)) + + return firstValue * first + secondValue * second + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/Operations.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/Operations.swift new file mode 100644 index 0000000..77d739e --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/Operations.swift @@ -0,0 +1,87 @@ +// +// Operations.swift +// +// +// Created by Stefan Jaindl on 20.11.20. +// + +import Foundation + +//Implements basic operations only using + operator and bit manipulation +open class Operations { + public init() { } + + open func add(first: Int, second: Int) -> Int { + return first + second + } + + open func subtract(first: Int, second: Int) -> Int { + // first - second = -second + first (-> flipping sign of second) + // 10 - 4 = -4 + 10 = 6 + let secondFlipped = negate(number: second) + return first + secondFlipped + } + + open func multiply(first: Int, second: Int) -> Int { + //Find out smaller value, will be faster in loop + let smaller = first < second ? first : second + let bigger = first < second ? second : first + + let smallerMSB = signBit(of: smaller) + let biggerMSB = signBit(of: bigger) + + let smallerToMultiply = smallerMSB == 1 ? negate(number: smaller) : smaller + let biggerToMultiply = biggerMSB == 1 ? negate(number: bigger) : bigger + + var result = 0 + var count = 0 + while count < smallerToMultiply { + result += biggerToMultiply + count += 1 + } + + if smallerMSB != biggerMSB { + //If one of both numbers are negative, result will be negative + result = negate(number: result) + } + + return result + } + + open func divide(first: Int, second: Int) -> Int { + let firstMSB = signBit(of: first) + let secondMSB = signBit(of: second) + + let firstToDivide = firstMSB == 1 ? negate(number: first) : first + let secondToDivide = secondMSB == 1 ? negate(number: second) : second + + var result = 0 + var temp = 0 + while temp <= firstToDivide { + result += 1 + temp += secondToDivide + } + + if temp > first { + //Truncate result, if division is with a remainder + result -= 1 + } + + if firstMSB != secondMSB { + //If one of both numbers are negative, result will be negative + result = negate(number: result) + } + + return result + } + + private func signBit(of number: Int) -> Int { + let mask = 1 << (Int.bitWidth - 1) //bitWidth = 64 + return number & mask == 0 ? 0 : 1 + } + + + private func negate(number: Int) -> Int { + return ~number + 1 + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/PairwiseSwap.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/PairwiseSwap.swift new file mode 100644 index 0000000..52c37e9 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/PairwiseSwap.swift @@ -0,0 +1,19 @@ +// +// PairwiseSwap.swift +// +// +// Created by Stefan Jaindl on 03.08.20. +// + +import Foundation + +open class PairwiseSwap { + public init() { } + + private let oddMask = 0x5555 + private let evenMask = 0xAAAA + + open func swapOddEven(number: Int) -> Int { + return (number >> 1 & oddMask) | (number << 1 & evenMask) + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/ScreenManipulator.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/ScreenManipulator.swift new file mode 100644 index 0000000..a752e52 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/ScreenManipulator.swift @@ -0,0 +1,51 @@ +// +// ScreenManipulator.swift +// +// +// Created by Stefan Jaindl on 03.08.20. +// + +import Foundation + +open class ScreenManipulator { + public init() { } + + //width + coordinates in bits. Screen is stored in whole bytes. + //screen width is divisible by 8. + open func drawLine(screen: inout [UInt8], width: Int, x1: Int, x2: Int, y: Int) { + let height = screen.count / (width / 8) + if y >= height { + return + } + + let startByte = y * width / 8 + x1 / 8 + let endByte = startByte + (x2 - x1) / 8 + + if startByte == endByte { + //horizontal line within same byte + var ones = x2 - x1 + var mask: UInt8 = 0b1 + while ones > 0 { + mask = mask << 1 | 1 + ones -= 1 + } + mask <<= 8 - 1 - x2 + screen[startByte] = mask + } else { + var start: UInt8 = 0xFF + let startRemainder = x1 % 8 + start >>= startRemainder + + var end: UInt8 = 0xFF + let endRemainder = x2 % 8 + end >>= 8 - endRemainder + end <<= 8 - endRemainder + + screen[startByte] = start + for index in startByte + 1 ..< endByte { + screen[index] = 0xFF + } + screen[endByte] = end + } + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/Swapper.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/Swapper.swift new file mode 100644 index 0000000..dadd332 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/BitManipulation/Swapper.swift @@ -0,0 +1,18 @@ +// +// Swapper.swift +// +// +// Created by Stefan Jaindl on 15.11.20. +// + +import Foundation + +open class Swapper { + public init() { } + + open func swapInline(_ first: inout T, _ second: inout T) { + first = first ^ second //bit difference + second = first ^ second //= original first + first = first ^ second //= original second + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/BSTSequence.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/BSTSequence.swift new file mode 100644 index 0000000..4889c03 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/BSTSequence.swift @@ -0,0 +1,64 @@ +// +// BSTSequence.swift +// +// +// Created by Stefan Jaindl on 06.07.20. +// + +import Foundation + +open class BSTSequence { + + public init() { } + + //Returns all possible arrays from which the given BST could result (no self-balancing tree) + open func BSTSequences(root: SimpleTreeNode?) -> [[SimpleTreeNode]] { + guard let root = root else { + return [] + } + + let leftSequences = BSTSequences(root: root.left) + let rightSequences = BSTSequences(root: root.right) + + if leftSequences.isEmpty, rightSequences.isEmpty { + return [[root]] + } else if leftSequences.isEmpty { + return rightSequences + } else if rightSequences.isEmpty { + return leftSequences + } + + var sequences: [[SimpleTreeNode]] = [] + + leftSequences.forEach { left in + rightSequences.forEach { right in + sequences.append(contentsOf: weave(left: left, right: right, prefix: [root])) + } + } + + return sequences + } + + private func weave(left: [SimpleTreeNode], right: [SimpleTreeNode], prefix: [SimpleTreeNode]) -> [[SimpleTreeNode]] { + var newPrefix = prefix + + if left.isEmpty { + newPrefix.append(contentsOf: right) + return [newPrefix] + } else if right.isEmpty { + newPrefix.append(contentsOf: left) + return [newPrefix] + } + + newPrefix.append(left[0]) + var result = weave(left: Array(left[1.. { + + public init() { } + + //Given: Checks whether a Binary Tree is balanced (= height of left and right child of any node doesn't differ more than one) + open func isBalanced(root: SimpleTreeNode) -> Bool { + return isBalancedRecursive(root: root).valid + } + + private func isBalancedRecursive(root: SimpleTreeNode?) -> ResultWrapper { + guard let root = root else { + return ResultWrapper(height: 0, valid: true) + } + + var leftResult = isBalancedRecursive(root: root.left) + //If balance check for left or right child already failed, just pass invalid result up + if !leftResult.valid { + return leftResult + } + + let rightResult = isBalancedRecursive(root: root.right) + if !rightResult.valid { + return rightResult + } + + if abs(leftResult.height - rightResult.height) > 1 { + //height differs more than one = not balanced! + //Return any result as invalid + leftResult.valid = false + return leftResult + } + + var maxHeightResult = max(leftResult, rightResult) + maxHeightResult.height += 1 + + return maxHeightResult + } +} + +private struct ResultWrapper: Comparable { + var height: Int + var valid: Bool + + static func < (lhs: ResultWrapper, rhs: ResultWrapper) -> Bool { + return lhs.height < rhs.height + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/FirstCommonAncestor.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/FirstCommonAncestor.swift new file mode 100644 index 0000000..b26d576 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/FirstCommonAncestor.swift @@ -0,0 +1,65 @@ +// +// FirstCommonAncestor.swift +// +// +// Created by Stefan Jaindl on 30.06.20. +// + +import Foundation + +open class FirstCommonAncestor { + + public init() { } + + //Return the first common ancestor of two nodes (without parent links) + open func firstCommonAncestor(root: SimpleTreeNode, first: SimpleTreeNode, second: SimpleTreeNode) -> SimpleTreeNode? { + var parent: SimpleTreeNode? = root + var isValidRoot = true + + if first === second { + return first + } + + while isValidRoot { + let firstLeftFound = isDescendant(root: parent?.left, node: first) + let secondLeftFound = isDescendant(root: parent?.left, node: second) + + //If both nodes are on the left side, move to left child + if firstLeftFound && secondLeftFound { + parent = parent?.left + continue + } + + let firstRightFound = isDescendant(root: parent?.right, node: first) + let secondRightFound = isDescendant(root: parent?.right, node: second) + + //If both nodes are on the right side, move to right child + if firstRightFound && secondRightFound { + parent = parent?.right + continue + } + + //If both nodes are found on different sides, parent is the FCA + if (firstLeftFound || firstRightFound || first === parent) && (secondLeftFound || secondRightFound || second === parent) { + return parent + } + + //If no node was found, input is an invalid node + isValidRoot = false + } + + return nil + } + + private func isDescendant(root: SimpleTreeNode?, node: SimpleTreeNode) -> Bool { + guard let root = root else { + return false + } + + if root === node { + return true + } + + return isDescendant(root: root.left, node: node) || isDescendant(root: root.right, node: node) + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/ListOfDepths.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/ListOfDepths.swift new file mode 100644 index 0000000..bc3db7a --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/ListOfDepths.swift @@ -0,0 +1,51 @@ +// +// ListOfDepths.swift +// +// +// Created by Stefan Jaindl on 24.06.20. +// + +import Foundation + +open class ListOfDepths { + + public init () { } + + open func listOfDepths(root: SimpleTreeNode) -> Array> { + var resultList = Array>() + let rootList = SingleLinkedList() + + rootList.add(node: SingleNode(val: root.value)) + resultList.append(rootList) + + if let left = root.left { + listOfDepths(resultList: &resultList, depth: 2, treeNode: left) + } + + if let right = root.right { + listOfDepths(resultList: &resultList, depth: 2, treeNode: right) + } + + return resultList + } + + private func listOfDepths(resultList: inout Array>, depth: Int, treeNode: SimpleTreeNode) { + var levelList: SingleLinkedList + if resultList.count >= depth { + levelList = resultList[depth - 1] + } else { + levelList = SingleLinkedList() + resultList.insert(levelList, at: depth - 1) + } + + levelList.add(node: SingleNode(val: treeNode.value)) + + if let left = treeNode.left { + listOfDepths(resultList: &resultList, depth: depth + 1, treeNode: left) + } + + if let right = treeNode.right { + listOfDepths(resultList: &resultList, depth: depth + 1, treeNode: right) + } + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/MinTree.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/MinTree.swift new file mode 100644 index 0000000..3df1796 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/MinTree.swift @@ -0,0 +1,43 @@ +// +// MinTree.swift +// +// +// Created by Stefan Jaindl on 23.06.20. +// + +import Foundation + +open class MinTree { + + //Given: Ascendingly sorted array without duplicates + //Return: Root of build binary search tree + open func minTree(array: [T]) throws -> SimpleTreeNode { + guard array.count > 0 else { + throw NSError(domain: "MinTree: Empty input", code: 0, userInfo: nil) + } + + let minIndex = 0 + let maxIndex = array.count - 1 + let middleIndex = minIndex + (maxIndex - minIndex) / 2 + let root = SimpleTreeNode(value: array[middleIndex]) + + root.left = insert(array: array, minIndex: 0, maxIndex: middleIndex - 1, root: root) + root.right = insert(array: array, minIndex: middleIndex + 1, maxIndex: maxIndex, root: root) + + return root + } + + private func insert(array: [T], minIndex: Int, maxIndex: Int, root: SimpleTreeNode) -> SimpleTreeNode? { + if minIndex > maxIndex { + return nil + } + + let middleIndex = minIndex + (maxIndex - minIndex) / 2 + let node = SimpleTreeNode(value: array[middleIndex]) + + node.left = insert(array: array, minIndex: minIndex, maxIndex: middleIndex - 1, root: node) + node.right = insert(array: array, minIndex: middleIndex + 1, maxIndex: maxIndex, root: node) + + return node + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/NodeSuccessor.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/NodeSuccessor.swift new file mode 100644 index 0000000..072f616 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/NodeSuccessor.swift @@ -0,0 +1,50 @@ +// +// BalancedTreeChecker.swift +// +// +// Created by Stefan Jaindl on 28.06.20. +// + +import Foundation + +open class NodeSuccessor { + + public init() { } + + //return successor node with in-order-traversal of binary search tree + open func successor(root: TreeNode) -> TreeNode? { + if let right = root.right { + //Case 1: If right child exists, go right, and then until leave node found + return traverseLeft(node: right) + } else if root == root.parent?.left { + //Case 2: If node is left child, parent node is successor + return root.parent + } else { + //Case 3: Node is right child -> traverse up until node which is a left child is found + return traverseParent(node: root.parent) + } + } + + private func traverseLeft(node: TreeNode) -> TreeNode? { + var current = node.left + while let left = current?.left { + current = left + } + + return current + } + + private func traverseParent(node: TreeNode?) -> TreeNode? { + var current = node + + while let parent = current?.parent { + if current == parent.left { + return parent + } + + current = parent + } + + return nil + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/PathChecker.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/PathChecker.swift new file mode 100644 index 0000000..84fed4c --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/PathChecker.swift @@ -0,0 +1,33 @@ +// +// PathChecker.swift +// +// +// Created by Stefan Jaindl on 22.06.20. +// + +import Foundation + +extension DirectedGraph { + + open func pathExists(from: Vertice, to: Vertice) -> Bool { + let queue = Queue() + + queue.enqueue(val: from) + while !queue.isEmpty(), let vertice = try? queue.dequeue() { + vertice.visited = true + if vertice == to { + return true + } + + var currentNode = neighbours(v: vertice).head + while let neighbour = currentNode { + if !neighbour.val.visited { + queue.enqueue(val: neighbour.val) + } + currentNode = neighbour.next + } + } + + return false + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/PathSum.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/PathSum.swift new file mode 100644 index 0000000..441c818 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/PathSum.swift @@ -0,0 +1,47 @@ +// +// PathSum.swift +// +// +// Created by Stefan Jaindl on 15.07.20. +// + +import Foundation + +open class PathSum { + + public init() { } + + open func pathSumCount(root: SimpleTreeNode, valueToMatch: Int) -> Int { + var sums = [Int: Int]() + return sumUp(root: root, sums: &sums, offset: 0, valueToMatch: valueToMatch) + } + + //Traverse all nodes and call traverse to check paths from each node + private func sumUp(root: SimpleTreeNode?, sums: inout [Int: Int], offset: Int, valueToMatch: Int) -> Int { + guard let root = root else { + return 0 + } + + return traverse(root: root, sums: &sums, offset: offset, runningSum: 0, valueToMatch: valueToMatch) + sumUp(root: root.left, sums: &sums, offset: offset + root.value, valueToMatch: valueToMatch) + sumUp(root: root.right, sums: &sums, offset: offset + root.value, valueToMatch: valueToMatch) + } + + //Traverse down from a given root + private func traverse(root: SimpleTreeNode?, sums: inout [Int: Int], offset: Int, runningSum: Int, valueToMatch: Int) -> Int { + guard let root = root else { + return 0 + } + + let currentSum = runningSum + root.value + + //Sums maps from running sum to node count (that sum to that value) + if let sumForCount = sums[currentSum] { + sums[currentSum] = sumForCount + 1 + } else { + sums[currentSum] = 1 + } + + let matching = currentSum == valueToMatch ? 1 : 0 + + return matching + traverse(root: root.left, sums: &sums, offset: offset, runningSum: currentSum, valueToMatch: valueToMatch) + traverse(root: root.right, sums: &sums, offset: offset, runningSum: currentSum, valueToMatch: valueToMatch) + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/RandomBinarySearchTree.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/RandomBinarySearchTree.swift new file mode 100644 index 0000000..dfa51e9 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/RandomBinarySearchTree.swift @@ -0,0 +1,213 @@ +// +// RandomBinarySearchTree.swift +// +// +// Created by Stefan Jaindl on 12.07.20. +// + +import Foundation + +open class RandomBinarySearchTree { + + var root: SimpleTreeNode? + + public init() { } + + open func insert(value: T) { + if root == nil { + root = SimpleTreeNode(value: value, numElements: 1) + } else { + var currentNode = root + while let current = currentNode { + current.numElements += 1 + + if value <= current.value { + if current.left == nil { + current.left = SimpleTreeNode(value: value, numElements: 1) + return + } else { + currentNode = current.left + } + } else { + if current.right == nil { + current.right = SimpleTreeNode(value: value, numElements: 1) + return + } else { + currentNode = current.right + } + } + } + } + } + + open func find(value: T) -> SimpleTreeNode? { + var current = root + + while let currentNode = current { + if currentNode.value == value { + return currentNode + } + + current = currentNode.value > value ? currentNode.left : currentNode.right + } + + return nil + } + + //TODO: Check delete functionality for root + open func delete(value: T) { + var current = root + var previous: SimpleTreeNode? = nil + + while let currentNode = current { + if currentNode.value == value { + break + } + + previous = current + previous?.numElements -= 1 + current = currentNode.value > value ? currentNode.left : currentNode.right + } + + //case 1: node has no childs - simply delete + if current?.left == nil, current?.right == nil { + if previous?.left === current { + previous?.left = nil + } else { + previous?.right = nil + } + } + + //case 2: node has 1 child: upgrade + else if current?.left == nil { + if previous?.left === current { + previous?.left = current?.right + } else { + previous?.right = current?.right + } + previous?.numElements -= 1 + } else if current?.right == nil { + if previous?.left === current { + previous?.left = current?.left + } else { + previous?.right = current?.left + } + } + + //case 3: node has 2 childs - exchange with inorder successor (or predecessor) + else { + if let left = current?.left, let right = current?.right, left.numElements > right.numElements { + let (previousPredecessor, predecessorNode) = inorderPredecessor(root: current) + //Remove last node + if previousPredecessor?.left === predecessorNode { + previousPredecessor?.left = nil + } else { + previousPredecessor?.right = nil + } + + //replace node with in-order predecessor + if previous?.left === current { + previous?.left = predecessorNode + } else { + previous?.right = predecessorNode + } + + if previousPredecessor === root { + root = predecessorNode + } + } else { + let (previousSuccessor, successorNode) = inorderSuccessor(root: current) + //Remove last node + if previousSuccessor?.left === successorNode { + previousSuccessor?.left = nil + } else { + previousSuccessor?.right = nil + } + + //replace node with in-order predecessor + if previous?.left === current { + previous?.left = successorNode + } else { + previous?.right = successorNode + } + + if previousSuccessor === root { + root = successorNode + } + } + } + + if current === root, current?.numElements == 0 { + root = nil + return + } + } + + open func getRandomNode() -> SimpleTreeNode? { + guard let root = root else { + return nil + } + + var random = Int.random(in: 0..? = root + + while let current = currentNode { + + var rootId = 0 + if let left = current.left { + rootId += left.numElements + } + + if random == rootId { + return currentNode + } else if random < rootId { + currentNode = current.left + } else { + random -= rootId + 1 + currentNode = current.right + } + } + + return currentNode + } + + private func inorderSuccessor(root: SimpleTreeNode?) -> (SimpleTreeNode?, SimpleTreeNode?) { + var node = root?.right + var previous: SimpleTreeNode? = root + + if let left = node?.left, let right = node?.right, left.numElements > right.numElements { + node?.numElements -= 1 + } + + while let leftNode = node?.left { + if let left = leftNode.left, let right = leftNode.right, left.numElements < right.numElements { + leftNode.numElements -= 1 + } + + previous = node + node = leftNode + } + + return (previous, node) + } + + private func inorderPredecessor(root: SimpleTreeNode?) -> (SimpleTreeNode?, SimpleTreeNode?) { + var node = root?.left + var previous: SimpleTreeNode? = root + + if let left = node?.left, let right = node?.right, left.numElements < right.numElements { + node?.numElements -= 1 + } + + while let rightNode = node?.right { + if let left = rightNode.left, let right = rightNode.right, left.numElements > right.numElements { + rightNode.numElements -= 1 + } + + previous = node + node = rightNode + } + + return (previous, node) + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/SubtreeChecker.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/SubtreeChecker.swift new file mode 100644 index 0000000..a250ed9 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/SubtreeChecker.swift @@ -0,0 +1,62 @@ +// +// SubTreeChecker.swift +// +// +// Created by Stefan Jaindl on 12.07.20. +// + +import Foundation + +open class SubTreeChecker { + + private let nilNodeValue: T + + public init(nilNodeValue: T) { + self.nilNodeValue = nilNodeValue + } + + //Given: Checks whether subtree is really a subtree of tree + //This can be checked by whether it is an array subsequence with preorder traversal and nil node values at leave nodes + open func isSubtree(tree: SimpleTreeNode, subtree: SimpleTreeNode) -> Bool { + let subPreorder = preorderTravelsal(root: subtree) + let treePreorder = preorderTravelsal(root: tree) + + let subTreeValues = subPreorder.compactMap { return $0.value } + let treeValues = treePreorder.compactMap { return $0.value } + + return treeValues.contains(subarray: subTreeValues) + } + + private func preorderTravelsal(root: SimpleTreeNode) -> [SimpleTreeNode] { + var preorder = [root] + + if root.left == nil, root.right == nil { + preorder.append(SimpleTreeNode(value: nilNodeValue)) + } + + if let left = root.left { + preorder.append(contentsOf: preorderTravelsal(root: left)) + } + + if let right = root.right { + preorder.append(contentsOf: preorderTravelsal(root: right)) + } + + return preorder + } +} + +extension Array where Element: Comparable { + func contains(subarray: [Element]) -> Bool { + var found = 0 + for element in self where found < subarray.count { + if element == subarray[found] { + found += 1 + } else { + found = element == subarray[0] ? 1 : 0 + } + } + + return found == subarray.count + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/ValidateBST.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/ValidateBST.swift new file mode 100644 index 0000000..44edaa6 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/GraphsAndTrees/ValidateBST.swift @@ -0,0 +1,61 @@ +// +// ValidateBST.swift +// +// +// Created by Stefan Jaindl on 28.06.20. +// + +import Foundation + +open class BSTValidator { + + public init() { } + + //Given: Binary tree. Output: Bool indicating whether BT is a BST. + //BST defintion: for each node - max value left subtree <= root node < min value right subtree + open func isValidBST(root: SimpleTreeNode) -> Bool { + return isValidBST(root: root).valid + } + + private func isValidBST(root: SimpleTreeNode) -> ResultWrapper { + var leftResult: ResultWrapper + var rightResult: ResultWrapper + + if let left = root.left { + leftResult = isValidBST(root: left) + } else { + leftResult = ResultWrapper(min: root.value, max: root.value, valid: true) + } + + //If BST check for left child already failed, just pass invalid result up + if !leftResult.valid { + return leftResult + } + + if let right = root.right { + rightResult = isValidBST(root: right) + } else { + rightResult = ResultWrapper(min: root.value, max: root.value, valid: true) + } + + //If BST check for right child already failed, just pass invalid result up + if !rightResult.valid { + return rightResult + } + + if !root.isLeaveNode() && !(root.value >= leftResult.max && root.value < rightResult.min) { + leftResult.valid = false + return leftResult + } + + leftResult.max = rightResult.max + + return leftResult + } +} + +private struct ResultWrapper { + var min: T + var max: T + var valid: Bool +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/BinaryNodeConverter.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/BinaryNodeConverter.swift new file mode 100644 index 0000000..0cf7055 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/BinaryNodeConverter.swift @@ -0,0 +1,54 @@ +// +// BinaryNodeConverter.swift +// +// +// Created by Stefan Jaindl on 09.12.20. +// + +import Foundation + +open class BinaryNodeConverter { + public init() { } + + //Converts a BST into a doubly linked list in O(N) time and O(1) space + open func convertBinarySearchTreeToDoubleLinkedList(root: BiNode?) -> BiNodes? { + guard let root = root else { + return nil + } + + let leftNodes = convertBinarySearchTreeToDoubleLinkedList(root: root.left) + let rightNodes = convertBinarySearchTreeToDoubleLinkedList(root: root.right) + + let lastOnLeft = leftNodes?.right + let firstRight = rightNodes?.left + + lastOnLeft?.right = root + root.left = lastOnLeft + + root.right = firstRight + firstRight?.left = root + + return BiNodes(left: leftNodes?.left ?? root, right: rightNodes?.right ?? root) + } +} + +open class BiNode: Equatable { + var left: BiNode? + var right: BiNode? + var data: T + + init(data: T, left: BiNode? = nil, right: BiNode? = nil) { + self.left = left + self.right = right + self.data = data + } + + public static func == (lhs: BiNode, rhs: BiNode) -> Bool { + return lhs.data == rhs.data && lhs.left == rhs.left && lhs.right == rhs.right + } +} + +public struct BiNodes { + let left: BiNode? + let right: BiNode? +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/DeleteMiddleNode.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/DeleteMiddleNode.swift new file mode 100644 index 0000000..fc7dc9b --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/DeleteMiddleNode.swift @@ -0,0 +1,24 @@ +// +// DeleteMiddleNode.swift +// DataStructuresAlgorithms +// +// Created by Stefan Jaindl on 03.06.20. +// + +import Foundation + +open class DeleteMiddleNode { + + public init() { } + + open func deleteNode(node: SingleNode) { + guard let nextNode = node.next else { + //In case the node is the last node, deletion isn't possible. + //It could be marked as dummy, though. + return + } + + node.val = nextNode.val + node.next = nextNode.next + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/DuplicateRemover.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/DuplicateRemover.swift new file mode 100644 index 0000000..08c69e3 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/DuplicateRemover.swift @@ -0,0 +1,37 @@ +// +// DuplicateRemover.swift +// DataStructuresAlgorithms +// +// Created by Stefan Jaindl on 01.06.20. +// + +import Foundation + +open class DuplicateRemover { + + public init() { } + + open func removeDuplicates(linkedList: SingleLinkedList) { + let root = linkedList.head + var current: SingleNode? = root + + while let outerNode = current { + let currentValue = outerNode.val + var previous: SingleNode? = outerNode + var currentInnerNode = current?.next + + while currentInnerNode != nil { + if let innerNodeValue = currentInnerNode?.val, innerNodeValue == currentValue { + previous?.next = currentInnerNode?.next + currentInnerNode = currentInnerNode?.next + linkedList.count -= 1 + } else { + previous = previous?.next + currentInnerNode = currentInnerNode?.next + } + } + + current = current?.next + } + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/KToLast.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/KToLast.swift new file mode 100644 index 0000000..095ab7e --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/KToLast.swift @@ -0,0 +1,36 @@ +// +// KToLast.swift +// DataStructuresAlgorithms +// +// Created by Stefan Jaindl on 03.06.20. +// + +import Foundation + +open class KToLast { + + public init() { } + + open func kToLast(linkedList: SingleLinkedList, k: Int) -> SingleNode? { + guard k >= 0 else { + return nil + } + + var front = linkedList.head + for _ in 0 ..< k { + if front?.next == nil { + return nil + } else { + front = front?.next + } + } + + var back = linkedList.head + while front?.next != nil { + front = front?.next + back = back?.next + } + + return back + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/LinkedListPalindrome.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/LinkedListPalindrome.swift new file mode 100644 index 0000000..7b5d700 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/LinkedListPalindrome.swift @@ -0,0 +1,40 @@ +// +// DeleteMiddleNode.swift +// DataStructuresAlgorithms +// +// Created by Stefan Jaindl on 03.06.20. +// + +import Foundation + +open class LinkedListPalindrome { + + public init() { } + + open func isPalindrome(linkedList: SingleLinkedList) -> Bool { + guard let head = linkedList.head, linkedList.count > 1 else { + return true + } + + return isPalindrome(node: head, index: 0, count: linkedList.count).isPalindrome + } + + private func isPalindrome(node: SingleNode?, index: Int, count: Int) -> ResultWrapper { + if index >= Int(trunc(Double(count) / 2)) { + let node = count % 2 == 0 /* even */ ? node : /* odd */ node?.next + return ResultWrapper(node: node, isPalindrome: true) + } + + var resultSoFar = isPalindrome(node: node?.next, index: index + 1, count: count) + let equal = resultSoFar.node?.val == node?.val + resultSoFar.isPalindrome = resultSoFar.isPalindrome && equal + resultSoFar.node = resultSoFar.node?.next + + return resultSoFar + } + + private struct ResultWrapper { + var node: SingleNode? + var isPalindrome: Bool + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/LoopDetector.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/LoopDetector.swift new file mode 100644 index 0000000..da0aa1a --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/LoopDetector.swift @@ -0,0 +1,57 @@ +// +// LoopDetector.swift +// DataStructuresAlgorithms +// +// Created by Stefan Jaindl on 10.06.20. +// + +import Foundation + +open class LoopDetector { + + public init() { } + + open func detectLoop(linkedList: SingleLinkedList) -> SingleNode? { + if linkedList.isEmpty() { + return nil + } + + guard let loopIndex = loop(linkedList: linkedList) else { + return nil + } + + let loopStartIndex = linkedList.count - loopIndex + 1 + //alternative: Reset slow runner to head and continue until slowRunner === fastRunner + + return node(linkedList: linkedList, offset: loopStartIndex) + } + + func loop(linkedList: SingleLinkedList) -> Int? { + var slowRunner = linkedList.head + var fastRunner = linkedList.head + var count = 1 + + while slowRunner?.val != fastRunner?.val { + count += 1 + slowRunner = slowRunner?.next + fastRunner = fastRunner?.next?.next + + if fastRunner == nil { + return nil + } + } + + return count + } + + func node(linkedList: SingleLinkedList, offset: Int) -> SingleNode? { + var node = linkedList.head + var index = 0 + while index < offset, node != nil { + node = node?.next + index += 1 + } + + return node + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/NodeIntersection.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/NodeIntersection.swift new file mode 100644 index 0000000..624a729 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/NodeIntersection.swift @@ -0,0 +1,73 @@ +// +// NodeIntersection.swift +// DataStructuresAlgorithms +// +// Created by Stefan Jaindl on 10.06.20. +// + +import Foundation + +open class NodeIntersection { + + public init() { } + + open func intersect(first: SingleLinkedList, second: SingleLinkedList) -> SingleNode? { + if first.isEmpty() || second.isEmpty() { + return nil + } + + guard let firstIntersection = getIntersectionWrapper(linkedList: first), let secondIntersection = getIntersectionWrapper(linkedList: second) else { + return nil + } + + //compare by reference + if firstIntersection.lastNode !== secondIntersection.lastNode { + return nil + } + + var firstNode = node(linkedList: first, offset: max(0, firstIntersection.count - secondIntersection.count)) + var secondNode = node(linkedList: second, offset: max(0, secondIntersection.count - firstIntersection.count)) + + while firstNode != nil, secondNode != nil { + if firstNode === secondNode { + return firstNode + } + + firstNode = firstNode?.next + secondNode = secondNode?.next + } + + return nil + } + + func getIntersectionWrapper(linkedList: SingleLinkedList) -> IntersectionWrapper? { + var count = 0 + var curNode = linkedList.head + + while curNode?.next != nil { + count += 1 + curNode = curNode?.next + } + + if let curNode = curNode { + return IntersectionWrapper(count: count, lastNode: curNode) + } + return nil + } + + func node(linkedList: SingleLinkedList, offset: Int) -> SingleNode? { + var node = linkedList.head + var index = 0 + while index < offset, node != nil { + node = node?.next + index += 1 + } + + return node + } +} + +public struct IntersectionWrapper { + var count: Int + var lastNode: SingleNode +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/PartitionLinkedList.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/PartitionLinkedList.swift new file mode 100644 index 0000000..7fe865a --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/PartitionLinkedList.swift @@ -0,0 +1,32 @@ +// +// DuplicateRemover.swift +// DataStructuresAlgorithms +// +// Created by Stefan Jaindl on 01.06.20. +// + +import Foundation + +open class PartitionLinkedList { + + public init() { } + + open func partition(linkedList: SingleLinkedList, around pivot: T) { + var prev: SingleNode? + var cur = linkedList.head + var isHead = true + while let current = cur { + if current.val < pivot, !isHead { + prev?.next = current.next + current.next = linkedList.head + linkedList.head = current + cur = prev?.next + } else { + prev = prev != nil ? prev?.next : linkedList.head + cur = cur?.next + } + + isHead = false + } + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/SumLists.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/SumLists.swift new file mode 100644 index 0000000..fabebbc --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/LinkedList/SumLists.swift @@ -0,0 +1,94 @@ +// +// DuplicateRemover.swift +// DataStructuresAlgorithms +// +// Created by Stefan Jaindl on 01.06.20. +// + +import Foundation + +open class SumLists { + + public init() { } + + open func reverseSum(first: SingleNode?, second: SingleNode?, carry: Int = 0) -> SingleNode? { + if first == nil, second == nil { + if carry == 1 { + return SingleNode(val: carry) + } + return nil + } + + var sum = carry + if let firstVal = first?.val { + sum += firstVal + } + + if let secondVal = second?.val { + sum += secondVal + } + + let node = SingleNode(val: sum % 10) + node.next = reverseSum(first: first?.next, second: second?.next, carry: sum > 9 ? 1 : 0) + + return node + } + + open func sumNodes(first: SingleNode, second: SingleNode, carry: Int = 0) -> SingleLinkedList { + let firstNodeCount = len(first) + let secondNodeCount = len(second) + + if firstNodeCount < secondNodeCount { + pad(first, with: 0, forCount: secondNodeCount - firstNodeCount) + } else if secondNodeCount < firstNodeCount { + pad(second, with: 0, forCount: firstNodeCount - secondNodeCount) + } + + let result = sum(first: first, second: second) + if result.carry == 1 { + result.resultList.prepend(node: SingleNode(val: result.carry)) + } + + return result.resultList + } + + private func len(_ node: SingleNode) -> Int { + var count = 0 + var current: SingleNode? = node + while current != nil { + count += 1 + current = current?.next + } + + return count + } + + private func pad(_ node: SingleNode, with: Int, forCount: Int) { + var currentNode = node + for _ in 0 ..< forCount { + let newNode = SingleNode(val: with) + newNode.next = currentNode + currentNode = newNode + } + } + + private func sum(first: SingleNode?, second: SingleNode?) -> ResultWrapper { + guard let first = first, let second = second else { + return ResultWrapper(carry: 0, resultList: SingleLinkedList()) + } + + var wrapper = sum(first: first.next, second: second.next) + let sumAtNode = first.val + second.val + wrapper.carry + let sumNode = SingleNode(val: sumAtNode % 10) + + wrapper.resultList.prepend(node: sumNode) + wrapper.carry = sumAtNode > 9 ? 1 : 0 + + return wrapper + } +} + +private struct ResultWrapper { + var carry: Int + var resultList: SingleLinkedList +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/AppointmentTimeOptimizer.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/AppointmentTimeOptimizer.swift new file mode 100644 index 0000000..c37fcef --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/AppointmentTimeOptimizer.swift @@ -0,0 +1,65 @@ +// +// AppointmentTimeOptimizer.swift +// +// +// Created by Stefan Jaindl on 11.12.20. +// + +import Foundation + +//Given an array of minutes, find the maximum nr. of minutes where no 2 adjacent indexes can be choosen. +open class AppointmentTimeOptimizer { + public init() { } + + //O(N) time + O(N) space + open func calculateMaxPossibleAppointmentTime(from minutes: [Int]) -> Int { + var lookup: [Int: Int] = [:] //lookup table from array index to minutes + + return calculateMaxMinutesRecursive(from: minutes, index: 0, lookup: &lookup) + } + + private func calculateMaxMinutesRecursive(from minutes: [Int], index: Int, lookup: inout [Int: Int]) -> Int { + guard index < minutes.count else { + return 0 + } + + let minutesNextSpot: Int + if let lookedupMinutes = lookup[index + 2] { + minutesNextSpot = lookedupMinutes + } else { + minutesNextSpot = calculateMaxMinutesRecursive(from: minutes, index: index + 2, lookup: &lookup) + lookup[index + 2] = minutesNextSpot + } + + let minutesAfterNextSpot: Int + if let lookedupMinutes = lookup[index + 3] { + minutesAfterNextSpot = lookedupMinutes + } else { + minutesAfterNextSpot = calculateMaxMinutesRecursive(from: minutes, index: index + 3, lookup: &lookup) + lookup[index + 3] = minutesAfterNextSpot + } + + return minutes[index] + max(minutesNextSpot, minutesAfterNextSpot) + } + + //O(N) time + O(1) space + open func calculateMaxMinutesIterative(from minutes: [Int]) -> Int { + guard !minutes.isEmpty else { + return 0 + } + + var index = minutes.count - 1 + + var lastBest = 0 //best index + 1 + var lastLastBest = 0 //best index + 2 + while index >= 0 { + let currentBest = max(minutes[index] + lastLastBest, lastBest) + lastLastBest = lastBest + lastBest = currentBest + + index -= 1 + } + + return max(lastBest, lastLastBest) + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/BoolEvaluation.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/BoolEvaluation.swift new file mode 100644 index 0000000..df0dac0 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/BoolEvaluation.swift @@ -0,0 +1,87 @@ +// +// BoolEvaluation.swift +// +// +// Created by Stefan Jaindl on 27.08.20. +// + +import Foundation + +open class BoolEvaluation { + + public init() { } + + open func boolEvaluation(expression: String, expectedResult: Bool) throws -> Int { + if expression.isEmpty { + return 0 + } + + var lookup: [String: (trueCount: Int, falseCount: Int)] = [:] + + let result = try countValidParenthesesPositions(expression: expression, lookup: &lookup) + + return expectedResult ? result.trueCount : result.falseCount + } + + private func countValidParenthesesPositions(expression: String, lookup: inout [String: (trueCount: Int, falseCount: Int)]) throws -> (trueCount: Int, falseCount: Int) { + if expression.count == 1 { + //base case: 1 or 0 + var result: (trueCount: Int, falseCount: Int) = (0, 0) + let value = expression.toBool() + if value { + result.trueCount = 1 + } else { + result.falseCount = 1 + } + + return result + } + + if let result = lookup[expression] { + return result + } + + var cumulatedResult: (trueCount: Int, falseCount: Int) = (0, 0) + for index in stride(from: 1, to: expression.count - 1, by: 2) { + let operatorIndex = expression.index(expression.startIndex, offsetBy: index) + let leftRange = expression.startIndex ... expression.index(before: operatorIndex) + let rightRange = expression.index(after: operatorIndex) ..< expression.endIndex + + let operatorBetweenParentheses = String(expression[operatorIndex]) + let leftParenthesizedExpression = String(expression[leftRange]) + let rightParenthesizedExpression = String(expression[rightRange]) + + let leftResult = try countValidParenthesesPositions(expression: leftParenthesizedExpression, lookup: &lookup) + let rightResult = try countValidParenthesesPositions(expression: rightParenthesizedExpression, lookup: &lookup) + + var result: (trueCount: Int, falseCount: Int) = (0, 0) + let totalCount = (leftResult.trueCount + leftResult.falseCount) * (rightResult.trueCount + rightResult.falseCount) + + switch operatorBetweenParentheses { + case "|": + result.trueCount = leftResult.trueCount * rightResult.trueCount + leftResult.trueCount * rightResult.falseCount + leftResult.falseCount * rightResult.trueCount + case "&": + result.trueCount = leftResult.trueCount * rightResult.trueCount + case "^": + result.trueCount = leftResult.trueCount * rightResult.falseCount + leftResult.falseCount * rightResult.trueCount + default: + throw NSError(domain: "Unsupported operator!", code: 0, userInfo: nil) + } + + result.falseCount = totalCount - result.trueCount + + cumulatedResult.trueCount += result.trueCount + cumulatedResult.falseCount += result.falseCount + } + + lookup[expression] = cumulatedResult + + return cumulatedResult + } +} + +extension String { + func toBool() -> Bool { + return self == "1" ? true : false + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/CircusTower.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/CircusTower.swift new file mode 100644 index 0000000..c29c68b --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/CircusTower.swift @@ -0,0 +1,49 @@ +// +// CircusTower.swift +// +// +// Created by Stefan Jaindl on 06.12.20. +// + +import Foundation + +open class CircusTower { + public init() { } + + open func highestTowerSize(of people: [CircusPeople]) -> Int { + guard !people.isEmpty else { + return 0 + } + + let sorted = people.sorted(by: { $0.height < $1.height }) + var lookup: [CircusPeople: Int] = [:] + + return towerSize(of: sorted, minWidth: -1, minHeight: -1, countSoFar: 0, lookup: &lookup) + } + + private func towerSize(of people: [CircusPeople], minWidth: Double, minHeight: Double, countSoFar: Int, lookup: inout [CircusPeople: Int]) -> Int { + if people.isEmpty { + return countSoFar + } + + var maxCount = countSoFar + for (index, guy) in people.enumerated() { + if guy.height > minHeight, guy.width > minWidth { + if let count = lookup[guy] { + maxCount = max(maxCount, countSoFar + count) + } else { + let subCount = towerSize(of: Array(people[index + 1 ..< people.count]), minWidth: guy.width, minHeight: guy.height, countSoFar: countSoFar + 1, lookup: &lookup) + maxCount = max(maxCount, subCount) + lookup[guy] = subCount - countSoFar + } + } + } + + return maxCount + } +} + +public struct CircusPeople: Hashable { + let width: Double + let height: Double +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/CoinCounter.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/CoinCounter.swift new file mode 100644 index 0000000..45b6dca --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/CoinCounter.swift @@ -0,0 +1,51 @@ +// +// CoinCounter.swift +// +// +// Created by Stefan Jaindl on 24.08.20. +// + +import Foundation + +open class CoinCounter { + let coins: [Coin] + var subcounts: [[Int]] = [] //lookup table: amount->coinIndex + + public init(coins: [Coin]) { + self.coins = coins + } + + open func coins(amount: Int) -> Int { + subcounts = [[Int]](repeating: [Int](repeating: 0, count: coins.count), count: amount + 1) + return coins(amount: amount, coinIndex: 0) + } + + open func coins(amount: Int, coinIndex: Int) -> Int { + if subcounts[amount][coinIndex] > 0 { + return subcounts[amount][coinIndex] + } + + if coinIndex >= coins.count - 1 { + return 1 //last coin + } + + var ways = 0 + var amountOfCurrentCoin = 0 + let coinValue = coins[coinIndex].value + //try coin values in desc. order, as long as they <= the remaining amount + while amountOfCurrentCoin * coinValue <= amount { + let remainingAmount = amount - amountOfCurrentCoin * coinValue + ways += coins(amount: remainingAmount, coinIndex: coinIndex + 1) + amountOfCurrentCoin += 1 + } + + subcounts[amount][coinIndex] = ways + + return ways + } +} + +public struct Coin: Hashable { + let id: String + let value: Int +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/DivingBoard.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/DivingBoard.swift new file mode 100644 index 0000000..17691f2 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/DivingBoard.swift @@ -0,0 +1,89 @@ +// +// DivingBoard.swift +// +// +// Created by Stefan Jaindl on 22.11.20. +// + +import Foundation + +open class DivingBoard { + let shortPlank: Plank + let longPlank: Plank + + public init(shortPlank: Plank, longPlank: Plank) { + self.shortPlank = shortPlank + self.longPlank = longPlank + } + + open func divingBoardFast(numberOfPlanks: Int) -> [Int] { + //O(P) = linear solution + guard numberOfPlanks > 0 else { + return [] + } + + var plankLenghts: [Int] = [] + for longPlanks in 0 ... numberOfPlanks { + let shortPlanks = numberOfPlanks - longPlanks + plankLenghts.append(shortPlanks * shortPlank.length + longPlanks * longPlank.length) + } + + return plankLenghts + } + + open func divingBoard(numberOfPlanks: Int) -> [Int] { + // optimized solution, memoization brings O(2 ^ P) runtime down O(K^2), as we are filling K levels with max K sums each (HT) + guard numberOfPlanks > 0 else { + return [] + } + + var lengths: [PlankLength: [Int]] = [:] //lookup table for already calculated lenghts + var results: Set = [] //Final results array + + _ = divingBoard(maxPlanks: numberOfPlanks, currentPlanks: 0, lenghts: &lengths, currentLenght: 0, results: &results) + + return results.sorted(by: { $0 < $1 }) + } + + private func divingBoard(maxPlanks: Int, currentPlanks: Int, lenghts: inout [PlankLength: [Int]], currentLenght: Int, results: inout Set) -> [Int] { + if currentPlanks == maxPlanks { + results.insert(currentLenght) + return [currentLenght] + } + + let plankLength = PlankLength(currentLength: currentLenght, currentPlanks: currentPlanks) + if lenghts.contains(where: { $0.key == plankLength }) { + return [] //skip element + } + + var totals = divingBoard(maxPlanks: maxPlanks, currentPlanks: currentPlanks + 1, lenghts: &lenghts, currentLenght: currentLenght + shortPlank.length, results: &results) + totals.append(contentsOf: divingBoard(maxPlanks: maxPlanks, currentPlanks: currentPlanks + 1, lenghts: &lenghts, currentLenght: currentLenght + longPlank.length, results: &results)) + + lenghts[plankLength] = totals + + return totals + } +} + +public struct PlankLength: Equatable, Hashable { + let currentLength: Int + let currentPlanks: Int +} + +public enum PlankSize { + case short(Int) + case long(Int) +} + +public struct Plank { + let size: PlankSize + + var length: Int { + switch size { + case .short(let len): + return len + case .long(let len): + return len + } + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/FactorialZeroes.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/FactorialZeroes.swift new file mode 100644 index 0000000..f676d48 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/FactorialZeroes.swift @@ -0,0 +1,32 @@ +// +// FactorialZeroes.swift +// +// +// Created by Stefan Jaindl on 16.11.20. +// + +import Foundation + +open class FactorialZeroes { + public init() { } + + open func factorialTrailingZeroesCount(number: Int) -> Int { + return factorialCount(number: number - number % 5, count: 0) + } + + private func factorialCount(number: Int, count: Int) -> Int { + if number < 5 { + return count + } + + var currentNumberToCheck = number + var newCount = count + + while currentNumberToCheck % 5 == 0 { + newCount += 1 + currentNumberToCheck /= 5 + } + + return factorialCount(number: number - 5, count: newCount) + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/GridPathFinder.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/GridPathFinder.swift new file mode 100644 index 0000000..01f6a7c --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/GridPathFinder.swift @@ -0,0 +1,83 @@ +// +// GridPathFinder.swift +// +// +// Created by Stefan Jaindl on 13.08.20. +// + +import Foundation + +open class GridPathFinder { + + var cellGrid: [[GridCell]] + + public init(grid: [[GridCell]]) { + cellGrid = grid + } + + //Finds a path from the top-left cell to the bottom-right cell, if one exists + //Path can only go downwards and right + open func findPath() -> [T]? { + if cellGrid.isEmpty || cellGrid[0].isEmpty { + return nil + } + + let rows = cellGrid.count + let columns = cellGrid[0].count + let current = cellGrid[0][0] + let target = cellGrid[rows - 1][columns - 1] + + let result = findPath(target: target, current: current) + + return result.valid ? result.path.reversed() : nil + } + + private func findPath(target: GridCell, current: GridCell) -> (valid: Bool, path: [T]) { + //base case + if current == target { + return (valid: true, path: [current.value]) + } + + //Check right path + if isInBoundsAndAccessible(row: current.row, column: current.column + 1), !cellGrid[current.row][current.column + 1].checked { + cellGrid[current.row][current.column + 1].checked = true + var result = findPath(target: target, current: cellGrid[current.row][current.column + 1]) + if result.valid { + result.path.append(current.value) + return result + } + } + + //Check down path + if isInBoundsAndAccessible(row: current.row + 1, column: current.column), !cellGrid[current.row + 1][current.column].checked { + cellGrid[current.row + 1][current.column].checked = true + var result = findPath(target: target, current: cellGrid[current.row + 1][current.column]) + if result.valid { + result.path.append(current.value) + return result + } + } + + return (false, []) + } + + private func isInBoundsAndAccessible(row: Int, column: Int) -> Bool { + if row < cellGrid.count && column < cellGrid[0].count { + return cellGrid[row][column].isAccessible + } + + return false + } +} + +public struct GridCell: Equatable { + var checked = false + let value: T + let isAccessible: Bool + let row: Int + let column: Int + + public static func == (lhs: GridCell, rhs: GridCell) -> Bool { + return lhs.row == rhs.row && lhs.column == rhs.column + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/Multiply.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/Multiply.swift new file mode 100644 index 0000000..405a4f3 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/Multiply.swift @@ -0,0 +1,39 @@ +// +// Multiplyer.swift +// +// +// Created by Stefan Jaindl on 16.08.20. +// + +import Foundation + +open class Multiplyer { + + public init() { } + + open func multiply(_ first: UInt, with second: UInt) -> UInt { + if first == 0 || second == 0 { + return 0 + } + + let bigger = first > second ? first : second + let smaller = first == bigger ? second : first + + if smaller == 1 { + return bigger + } + + return multiply(number: bigger, times: smaller) + } + + private func multiply(number: UInt, times: UInt) -> UInt { + if times == 1 { + return number + } + + let timesEven = times % 2 == 0 ? times : times - 1 + let remainder = times % 2 == 0 ? 0 : number + + return remainder + multiply(number: number << 1, times: timesEven >> 1) + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/PaintFill.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/PaintFill.swift new file mode 100644 index 0000000..790eb15 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/PaintFill.swift @@ -0,0 +1,54 @@ +// +// PaintFill.swift +// +// +// Created by Stefan Jaindl on 24.08.20. +// + +import Foundation + +//Paints surrounding area using BFS with a queue +open class PaintFill { + public init() { } + + let neighbourCoords = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)] + + open func paintFill(screen: inout [[CGColor]], point: Point, newColor: CGColor) throws { + if screen.count == 0 || !isInBounds(point: point, xLen: screen.count, yLen: screen[0].count) || screen[Int(point.x)][Int(point.y)] == newColor { + return + } + + let queue = Queue() + queue.enqueue(val: point) + let originalColor = screen[Int(point.x)][Int(point.y)] + + while !queue.isEmpty() { + let current = try queue.dequeue() + screen[Int(current.x)][Int(current.y)] = newColor + + for neighbour in neighboursToFill(color: originalColor, point: current, screen: &screen) { + queue.enqueue(val: neighbour) + } + } + } + + private func neighboursToFill(color: CGColor, point: Point, screen: inout [[CGColor]]) -> [Point] { + var neighbours: [Point] = [] + + for coord in neighbourCoords { + let neighbourCoord = Point(x: Double(coord.0) + point.x, y: Double(coord.1) + point.y) + if isInBounds(point: neighbourCoord, xLen: screen.count, yLen: screen[0].count), screen[Int(neighbourCoord.x)][Int(neighbourCoord.y)] == color { + neighbours.append(neighbourCoord) + } + } + + return neighbours + } + + private func isInBounds(point: Point, xLen: Int, yLen: Int) -> Bool { + let x = Int(point.x) + let y = Int(point.y) + + return x >= 0 && y >= 0 && x < xLen && y < yLen + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/ParensBuilder.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/ParensBuilder.swift new file mode 100644 index 0000000..beee7e7 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/ParensBuilder.swift @@ -0,0 +1,59 @@ +// +// ParensBuilder.swift +// +// +// Created by Stefan Jaindl on 24.08.20. +// + +import Foundation + +open class ParensBuilder { + public init() { } + + open func parens(numberOfBrackets: Int) -> [String] { + if numberOfBrackets <= 1 { + return [""] + } + + let bracketCount = BracketCount(opening: numberOfBrackets - 1, closing: numberOfBrackets) + + return parens(input: "(", bracketCount: bracketCount) + } + + private func parens(input: String, bracketCount: BracketCount) -> [String] { + if bracketCount.opening == 0 { + //base case + var complete = input + for _ in 0 ..< bracketCount.closing { + complete.append(")") + } + + return [complete] + } + + var combisClosing: [String] = [] + if bracketCount.closing > bracketCount.opening { + let next = "\(input))" + let newBracketCount = BracketCount(opening: bracketCount.opening, closing: bracketCount.closing - 1) + + combisClosing = parens(input: next, bracketCount: newBracketCount) + } + + var combisOpening: [String] = [] + let next = "\(input)(" + let newBracketCount = BracketCount(opening: bracketCount.opening - 1, closing: bracketCount.closing) + + combisOpening = parens(input: next, bracketCount: newBracketCount) + + return combisClosing + combisOpening + } +} + +private struct BracketCount: Hashable, Equatable { + var opening: Int + var closing: Int + + static func == (lhs: Self, rhs: Self) -> Bool { + return lhs.opening == rhs.opening && lhs.closing == rhs.closing + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/Permutations.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/Permutations.swift new file mode 100644 index 0000000..c0221e3 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/Permutations.swift @@ -0,0 +1,78 @@ +// +// Permutations.swift +// +// +// Created by Stefan Jaindl on 19.08.20. +// + +import Foundation + +open class Permutations { + public init() { } + + open func permutationsWithoutDuplicates(of string: String) -> [String] { + if string.isEmpty { + return [""] + } + + var permutations: [String] = [] + for char in string { + if permutations.isEmpty { + permutations.append(String(char)) + continue + } + + var newPermutations: [String] = [] + for lastLevelPermutation in permutations { + for index in 0 ... lastLevelPermutation.count { + var newPermutation = lastLevelPermutation + newPermutation.insert(char, at: lastLevelPermutation.index(lastLevelPermutation.startIndex, offsetBy: index)) + newPermutations.append(newPermutation) + } + } + permutations = newPermutations + } + + return permutations + } + + open func permutationsWithDuplicates(of string: String) -> [String] { + return permutations(of: String(string.sorted())) + } + + private func permutations(of string: String) -> [String] { + if string.count <= 1 { + return [string] + } + + var perms: [String] = [] + + for (index, char) in string.enumerated() { + if index > 0, string[string.index(string.startIndex, offsetBy: index - 1)] == char { + continue //char is a duplicate of previous char - so, skip duplicate + } + + var prefix = "" + var postfix = "" + + if index > 0 { + let prefixIndex = string.index(string.startIndex, offsetBy: index - 1) + prefix = String(string[string.startIndex ... prefixIndex]) + } + + if index < string.count - 1 { + let postfixIndex = string.index(after: string.index(string.startIndex, offsetBy: index)) + postfix = String(string[postfixIndex ..< string.endIndex]) + } + + let remainder = "\(prefix)\(postfix)" + + let subPermuations = permutations(of: remainder) + for subPermutation in subPermuations { + perms.append("\(char)\(subPermutation)") + } + } + + return perms + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/PowerSet.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/PowerSet.swift new file mode 100644 index 0000000..17ef7f5 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/PowerSet.swift @@ -0,0 +1,95 @@ +// +// PowerSet.swift +// +// +// Created by Stefan Jaindl on 15.08.20. +// + +import Foundation + +open class PowerSet { + + public init () { } + + //O(n*n!) = nr. of elements (n) * nr. of subsets (n!) + open func powerSetPermutations(set: [T]) -> [[T]] { + var result: [[T]] = [] + var lastResultSet: [[T]] = [] + + result.append([]) //empty set + + for value in set { + lastResultSet.append([value]) //level 1 + } + + result.append(contentsOf: lastResultSet) + + var level = 2 + + while level <= set.count { + var currentLevelSet: [[T]] = [] + for subset in lastResultSet { + for value in set { + if !subset.contains(value) { + var element = subset + element.append(value) + currentLevelSet.append(element) + } + } + } + + level += 1 + result.append(contentsOf: currentLevelSet) + lastResultSet = currentLevelSet + } + + return result + } + + //O(n*2^n) = nr. of elements (n) * nr. of subsets (2^n) + open func powerSetCombinations(set: [T]) -> [[T]] { + var result: [[T]] = [] + result.insert([], at: 0) + + for value in set { + var newSet: [[T]] = [] + for subset in result { + var newSubset = subset + newSubset.append(value) + newSet.append(newSubset) + } + result.append(contentsOf: newSet) + } + + return result + } + + //O(n*2^n) = nr. of elements (n) * nr. of subsets (2^n) + open func powerSetCombinationsByBitMasking(set: [T]) -> [[T]] { + var result: [[T]] = [] + + let maxValue = 1 << set.count + for value in 0 ..< maxValue { + result.append(convertIntToSubset(value: value, set: set)) + } + + return result + } + + private func convertIntToSubset(value: Int, set: [T]) -> [T] { + var index = 0 + var valueToConvert = value + var subset: [T] = [] + + while valueToConvert > 0 { + if valueToConvert & 1 == 1 { + subset.append(set[index]) + } + + valueToConvert >>= 1 + index += 1 + } + + return subset + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/QueensOnChessboard.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/QueensOnChessboard.swift new file mode 100644 index 0000000..38032e4 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/QueensOnChessboard.swift @@ -0,0 +1,64 @@ +// +// QueensOnChessboard.swift +// +// +// Created by Stefan Jaindl on 25.08.20. +// + +import Foundation + +open class QueensOnChessboard { + + private let boardLength: Int + + public init(boardLength: Int = 8) { + self.boardLength = boardLength + } + + //return all possible arrangements of 8 queens on 8x8 chessboard with the following limitations: + /* + only 1 queen in a row + only 1 queen in a column + only 1 queen in each diagonal + */ + + open func queenArrangements(partial: [(row: Int, column: Int)] = [], row: Int = 0) -> [[(row: Int, column: Int)]] { + if row >= boardLength { + return [partial] + } + + var arrangements: [[(Int, Int)]] = [] + for column in 0 ..< boardLength { + if isValid(row: row, column: column, partial: partial) { + var newArrangement = partial + newArrangement.append((row, column)) + let arrangementForColumn = queenArrangements(partial: newArrangement, row: row + 1) + arrangements.append(contentsOf: arrangementForColumn) + } + } + + return arrangements + } + + private func isValid(row: Int, column: Int, partial: [(row: Int, column: Int)]) -> Bool { + if row >= boardLength || column >= boardLength { + return false + } + + //no queens placed for row 0, that index is always valid, and is skipped in loop + for currentRow in 0 ..< row { + if partial[currentRow].column == column { + return false //column already used + } + + let rowDistance = row - currentRow + let leftDiagonalIndex = column - rowDistance + let rightDiagonalIndex = column + rowDistance + if partial[currentRow].column == leftDiagonalIndex || partial[currentRow].column == rightDiagonalIndex { + return false //diagonal occupied + } + } + + return true + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/StackOfBoxes.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/StackOfBoxes.swift new file mode 100644 index 0000000..6af3231 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/StackOfBoxes.swift @@ -0,0 +1,66 @@ +// +// StackOfBoxes.swift +// +// +// Created by Stefan Jaindl on 26.08.20. +// + +import Foundation + +open class StackOfBoxes { + + public init() { } + + open func maxHeight(of stack: [Box]) -> Int { + if stack.isEmpty { + return 0 + } + + let sorted = stack.sorted(by: >) + var maxHeight = 0 + + for (index, box) in sorted.enumerated() { + if box.height > maxHeight { + maxHeight = box.height + } + + for previous in 0 ..< index { + if isValid(first: box, second: sorted[previous]) && sorted[previous].maxHeight + box.height > box.maxHeight { + box.maxHeight = box.height + sorted[previous].maxHeight + + maxHeight = max(maxHeight, box.maxHeight) + } + } + } + + return maxHeight + } + + private func isValid(first: Box, second: Box) -> Bool { + return first.width < second.width && first.height < second.height && first.depth < second.depth + } +} + +open class Box: Equatable, Comparable { + public let width: Int + public let height: Int + public let depth: Int + + public var maxHeight: Int + + public init(width: Int, height: Int, depth: Int) { + self.width = width + self.height = height + self.depth = depth + + maxHeight = height //max height together with other boxes is at least == height of the box itself + } + + public static func < (lhs: Box, rhs: Box) -> Bool { + return lhs.height < rhs.height + } + + public static func == (lhs: Box, rhs: Box) -> Bool { + return lhs.width == rhs.width && lhs.height == rhs.height && lhs.depth == rhs.depth + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/TowersOfHanoi.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/TowersOfHanoi.swift new file mode 100644 index 0000000..66f4eb1 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/TowersOfHanoi.swift @@ -0,0 +1,45 @@ +// +// TowersOfHanoi.swift +// +// +// Created by Stefan Jaindl on 18.08.20. +// + +import Foundation + +open class TowerOfHanoi { + public let disks = Stack() + public let index: Int + + init(index: Int) { + self.index = index + } + + public func add(disk: T) throws { + if !disks.isEmpty(), try disks.peek() <= disk { + throw NSError(domain: "TowersOfHanoi: Cannot put larger disk on smaller one", code: 0, userInfo: nil) + } + + disks.push(val: disk) + } + + public func moveTopTo(otherTower: TowerOfHanoi) throws { + let top = try disks.pop() + try otherTower.add(disk: top) + } + + public func moveDisks(numberOfDisks: Int, destination: TowerOfHanoi, buffer: TowerOfHanoi) throws { + guard numberOfDisks > 0 else { + return + } + + //First move all disks except last from origin to buffer: + try moveDisks(numberOfDisks: numberOfDisks - 1, destination: buffer, buffer: destination) + + //Move last (biggest) disk from origin to destination: + try moveTopTo(otherTower: destination) + + //Move remaining n-1 disks from buffer to destination: + try buffer.moveDisks(numberOfDisks: numberOfDisks - 1, destination: destination, buffer: self) + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/TripleStep.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/TripleStep.swift new file mode 100644 index 0000000..62627c0 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/RecursionDynamicProgramming/TripleStep.swift @@ -0,0 +1,44 @@ +// +// TripleStep.swift +// +// +// Created by Stefan Jaindl on 13.08.20. +// + +import Foundation + +open class TripleStep { + + public init() { } + + open func tripleStep(for steps: Int) -> Int { + var currentStep = steps - 1 + var previous = 0 + var previousPrevious = 0 + var previousPreviousPrevious = 0 + + while currentStep > 0 { + //3 base cases + if currentStep == steps - 1 { + previous = 1 + } else if currentStep == steps - 2 { + previous = 2 + previousPrevious = 1 + } else if currentStep == steps - 3 { + previous = 4 + previousPrevious = 2 + previousPreviousPrevious = 1 + } else { + //normal case + let resultAtStep = previous + previousPrevious + previousPreviousPrevious + previousPreviousPrevious = previousPrevious + previousPrevious = previous + previous = resultAtStep + } + + currentStep -= 1 + } + + return previous + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/AnagramGrouper.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/AnagramGrouper.swift new file mode 100644 index 0000000..43a3378 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/AnagramGrouper.swift @@ -0,0 +1,80 @@ +// +// AnagramGrouper.swift +// +// +// Created by Stefan Jaindl on 20.09.20. +// + +import Foundation + +open class AnagramGrouper { + public init() { } + + open func sortAnagram(array: [String]) -> [String] { + //Modified bucket sort: Put anagrams in a bucket each! + //Map: sortedString:Anagrams + var anagramsMap: [String: [String]] = [:] + + //1. Build anagrams map + for word in array { + let sorted = String(word.sorted()) + if var anagrams = anagramsMap[sorted] { + anagrams.append(word) + anagramsMap[sorted] = anagrams + } else { + anagramsMap[sorted] = [word] + } + } + + //2. Convert map to array + var groupedAnagrams: [String] = [] + for element in anagramsMap { + groupedAnagrams.append(contentsOf: element.value) + } + + return groupedAnagrams + } +} + +//extension AnagramGrouper { +// static let anagramSorter: (Any, Any) -> ComparisonResult = { lhs, rhs in +// guard let lhs = lhs as? String, let rhs = rhs as? String else { +// return .orderedAscending +// } +// +// if lhs.count != rhs.count { +// return .orderedAscending +// } +// +// //Calculate count of each char of first string +// var charCounts: [Character: Int] = [:] +// for char in lhs { +// if let count = charCounts[char] { +// charCounts[char] = count + 1 +// } else { +// charCounts[char] = 1 +// } +// } +// +// //Check if second string has same counts - then it is an anagram: +// for char in rhs { +// if let count = charCounts[char] { +// if count <= 0 { +// return .orderedAscending +// } +// charCounts[char] = count - 1 +// } else { +// return .orderedAscending +// } +// } +// +// //Check if each char count is zeroed out: +// for element in charCounts { +// if element.value != 0 { +// return .orderedAscending +// } +// } +// +// return .orderedSame +// } +//} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/BestLineSearcher.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/BestLineSearcher.swift new file mode 100644 index 0000000..60dbb8e --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/BestLineSearcher.swift @@ -0,0 +1,99 @@ +// +// BestLineSearcher.swift +// +// +// Created by Stefan Jaindl on 26.11.20. +// + +import Foundation + +//Searches for a straight line in a 2D-graph that connects most points of it +open class BestLineSearcher { + public init() { } + + open func bestLine(of graph: TwoDGraph) -> MultiPrecisionLine? { + guard !graph.points.isEmpty else { + return nil + } + + var bestPoints: Set = Set() + var slopeIntercepts: [SlopeWithIntercept: Set] = [:] + + for firstIndex in 0 ..< graph.points.count { + let firstPoint = graph.points[firstIndex] + for secondIndex in firstIndex + 1 ..< graph.points.count { + let secondPoint = graph.points[secondIndex] + + let slope = secondPoint.x == firstPoint.x + ? Double.greatestFiniteMagnitude //prevent division by 0 - use greatestFiniteMagnitude as indicator + : (secondPoint.y - firstPoint.y) / (secondPoint.x - firstPoint.x) + + //One issue with using floating point numbers is inaccuracy of numbers. We could come around this by defining an epsilon value or ceil/round + let yIntercept: Double + if slope == Double.greatestFiniteMagnitude { + yIntercept = Double.greatestFiniteMagnitude + } else { + let xDiffToYIntercept = firstPoint.x < secondPoint.x ? firstPoint.x : secondPoint.x + let minY = firstPoint.y < secondPoint.y ? firstPoint.y : secondPoint.y + let direction: Double = firstPoint.y < secondPoint.y ? -1.0 : 1.0 + yIntercept = minY + direction * slope * xDiffToYIntercept + } + + let slopeWithIntercept = SlopeWithIntercept(slope: slope, yIntercept: yIntercept) + if var points = slopeIntercepts[slopeWithIntercept] { + points.insert(firstPoint) + points.insert(secondPoint) + slopeIntercepts[slopeWithIntercept] = points + } else { + slopeIntercepts[slopeWithIntercept] = [firstPoint, secondPoint] + } + + if let curPoints = slopeIntercepts[slopeWithIntercept], curPoints.count > bestPoints.count { + bestPoints = curPoints + } + } + } + + return convertToLine(points: bestPoints) + } + + private func convertToLine(points: Set) -> MultiPrecisionLine { + var topPoint = Point(x: Double.leastNonzeroMagnitude, y: Double.leastNonzeroMagnitude) + var bottomPoint = Point(x: Double.greatestFiniteMagnitude, y: Double.greatestFiniteMagnitude) + + points.forEach { point in + if point.y > topPoint.y { + topPoint = Point(x: point.x, y: point.y) + } else if point.y == topPoint.y, point.x > topPoint.x { + topPoint = Point(x: point.x, y: point.y) + } + + if point.y < bottomPoint.y { + bottomPoint = Point(x: point.x, y: point.y) + } else if point.y == bottomPoint.y, point.x < bottomPoint.x { + bottomPoint = Point(x: point.x, y: point.y) + } + } + + return MultiPrecisionLine(startPoint: bottomPoint, endPoint: topPoint) + } +} + +public struct TwoDGraph { + let points: [Point] +} + +public struct Point: Hashable { + let x: Double + let y: Double +} + +public struct SlopeWithIntercept: Hashable { + let slope: Double + let yIntercept: Double +} + +public struct MultiPrecisionLine: Equatable { + let startPoint: Point + let endPoint: Point +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/ContiguousSum.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/ContiguousSum.swift new file mode 100644 index 0000000..d6302ec --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/ContiguousSum.swift @@ -0,0 +1,30 @@ +// +// ContiguousSum.swift +// +// +// Created by Stefan Jaindl on 28.11.20. +// + +import Foundation + +open class ContiguousSum { + public init() { } + + open func largestContiguousSum(of array: [Int]) -> Int? { + if array.isEmpty { + return nil + } + + var maximum = Int.min + var trackingSum = 0 + + array.forEach { element in + maximum = max(maximum, element) + trackingSum += element + maximum = max(maximum, trackingSum) + trackingSum = max(trackingSum, 0) + } + + return maximum + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/DuplicateFinder.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/DuplicateFinder.swift new file mode 100644 index 0000000..f941afe --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/DuplicateFinder.swift @@ -0,0 +1,34 @@ +// +// DuplicateFinder.swift +// +// +// Created by Stefan Jaindl on 25.09.20. +// + +import Foundation + +open class DuplicateFinder { + public init() { } + + static let minAllowedValue = 1 + static let maxAllowedValue = 32000 + + open func findDuplicates(array: [Int], maxValue: Int) -> Set { + guard !array.isEmpty, maxValue >= Self.minAllowedValue, maxValue <= Self.maxAllowedValue else { + return [] + } + + var duplicates = Set() + let bitVector = BitVector(numberOfBits: maxValue + 1) //0 bit of index 0 is unused, we need to expand bits to not risk index out of bounds + + for value in array { + if bitVector.isBitSet(index: value) { + duplicates.insert(value) + } else { + bitVector.setBit(index: value) + } + } + + return duplicates + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/IntersectionFinder.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/IntersectionFinder.swift new file mode 100644 index 0000000..3b0a936 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/IntersectionFinder.swift @@ -0,0 +1,134 @@ +// +// IntersectionFinder.swift +// +// +// Created by Stefan Jaindl on 15.11.20. +// + +import Foundation + +open class IntersectionFinder { + public init() { } + + open func intersection(firstLine: LineSegment, secondLine: LineSegment) -> CGPoint? { + var changed = true + + while changed { + changed = false + + let minXLine = firstLine.from.x <= secondLine.from.x ? firstLine : secondLine + let maxXLine = firstLine == minXLine ? secondLine : firstLine + let minYLine = firstLine.from.y <= secondLine.from.y ? firstLine : secondLine + let maxYLine = firstLine == minXLine ? secondLine : firstLine + + //Sanity checks + if minXLine.to.x < maxXLine.from.x || minYLine.to.y < maxYLine.from.y { + return nil + } + + //adapt min x + let minValidX = max(firstLine.from.x, secondLine.from.x) + let xDiffToMinValidX = minValidX - minXLine.from.x + if xDiffToMinValidX != 0 { + if minXLine.from.x > minValidX { + return nil //invalid + } + + let yDiffToMinValidX = xDiffToMinValidX * minXLine.slope + minXLine.from.x = minValidX + minXLine.from.y += yDiffToMinValidX + changed = true + } + + //adapt max x + let maxValidX = min(firstLine.to.x, secondLine.to.x) + let biggerX = firstLine.to.x > secondLine.to.x ? firstLine : secondLine + let xDiffToMaxValidX = biggerX.to.x - maxValidX + if xDiffToMaxValidX != 0 { + if biggerX.from.x > maxValidX { + return nil //invalid + } + + let yDiffToMaxValidX = xDiffToMaxValidX * biggerX.slope + biggerX.to.x = maxValidX + biggerX.to.y -= yDiffToMaxValidX + changed = true + } + + //adapt min y + let minValidY = max(min(firstLine.from.y, firstLine.to.y), min(secondLine.from.y, secondLine.to.y)) + let smallerY = firstLine.from.y == minValidY || firstLine.to.y == minValidY ? secondLine : firstLine + let yDiffToMinValidY = minValidY - min(smallerY.from.y, smallerY.to.y) + if yDiffToMinValidY != 0 { + let xDiffToMinValidY = yDiffToMinValidY * smallerY.slope + if smallerY.from.y < smallerY.to.y { + if smallerY.to.y < minValidY { + return nil //invalid + } + + smallerY.from.y = minValidY + smallerY.from.x += xDiffToMinValidY + } else { + if smallerY.from.y < minValidY { + return nil //invalid + } + + smallerY.to.y = minValidY + smallerY.to.x += xDiffToMinValidY + } + changed = true + } + + + //adapt max y + let maxValidY = min(max(firstLine.from.y, firstLine.to.y), max(secondLine.from.y, secondLine.to.y)) + let biggerY = firstLine.from.y == maxValidY || firstLine.to.y == maxValidY ? secondLine : firstLine + let yDiffToMaxValidY = max(biggerY.from.y, biggerY.to.y) - maxValidY + if yDiffToMaxValidY != 0 { + let xDiffToMaxValidY = yDiffToMaxValidY * biggerY.slope + if biggerY.from.y > biggerY.to.y { + if biggerY.to.y < maxValidY { + return nil //invalid + } + + biggerY.from.y = maxValidY + biggerY.from.x += xDiffToMaxValidY + } else { + if biggerY.from.y < maxValidY { + return nil //invalid + } + + biggerY.to.y = maxValidY + biggerY.to.x -= xDiffToMaxValidY + } + changed = true + } + } + + let minXLine = firstLine.from.x <= secondLine.from.x ? firstLine : secondLine + let minYLine = firstLine.from.y <= secondLine.from.y ? firstLine : secondLine + + let xCenter = (minXLine.from.x + minXLine.to.x) / 2 + let yCenter = (minYLine.from.x + minYLine.to.x) / 2 + + return CGPoint(x: xCenter, y: yCenter) + } +} + +open class LineSegment: Equatable { + var from: CGPoint + var to: CGPoint + + init(from: CGPoint, to: CGPoint) { + self.from = from + self.to = to + } + + var slope: CGFloat { + return (to.y - from.y) / (to.x - from.x) + } + + public static func == (lhs: LineSegment, rhs: LineSegment) -> Bool { + return lhs.from == rhs.from && lhs.to == rhs.to + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/Listy.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/Listy.swift new file mode 100644 index 0000000..69a2b42 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/Listy.swift @@ -0,0 +1,59 @@ +// +// Listy.swift +// +// +// Created by Stefan Jaindl on 21.09.20. +// + +import Foundation + +open class Listy { + private let array: [T] + + public init(array: [T]) { + self.array = array.sorted() + } + + open func element(at index: Int) -> T? { + if index >= 0 && index < array.count { + return array[index] + } + + return nil + } +} + +open class ListySearcher { + public init() { } + + open func listySearch(listy: Listy, searched: T) -> Int? { + guard listy.element(at: 0) != nil else { + return nil + } + + var currentIndex = 0 + var minValidIndex = 0 + var maxValidIndex = Int.max + var valid = true + + while valid { + let currentElement = listy.element(at: currentIndex) + + if currentElement == searched { + return currentIndex + } else if let currentElement = currentElement, currentElement < searched { + minValidIndex = currentIndex //go right + currentIndex = max(1, maxValidIndex == Int.max ? currentIndex * 2 : (currentIndex + maxValidIndex) / 2) //for index 0 increase to 1! + } else { //currentElement > searched OR currentElement == nil + maxValidIndex = currentIndex + currentIndex = minValidIndex == 0 ? currentIndex / 2 : (currentIndex + minValidIndex) / 2 + } + + if abs(maxValidIndex - minValidIndex) <= 1 { + valid = false + } + } + + return nil + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/LivingPeople.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/LivingPeople.swift new file mode 100644 index 0000000..8dc211a --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/LivingPeople.swift @@ -0,0 +1,64 @@ +// +// LivingPeople.swift +// +// +// Created by Stefan Jaindl on 23.11.20. +// + +import Foundation + +open class LivingPeople { + public init() { } + + //Returns year where most people are alive (any, if years have same alive count) + open func yearsWithMostPeopleAlive(peoples: [People], minYear: Int, maxYear: Int = Calendar.current.component(.year, from: Date())) -> Int? { + guard !peoples.isEmpty else { + return nil + } + + //Sort by date of birth + let sortedPeople = peoples.sorted(by: { $0.yearOfBirth < $1.yearOfBirth }) + var currentAlive = 0 + var maxAlive = 0 + var maxAliveYear: Int? + var toYearCount: [Int: Int] = [:] + var currentYear = minYear + + for people in sortedPeople { + if people.yearOfBirth > maxYear { + return maxAliveYear + } + + currentAlive += 1 + if let to = people.yearOfDeath { + addOrIncrement(hashTable: &toYearCount, key: to, by: 1) + } + + if currentYear != people.yearOfBirth { + currentAlive -= toYearCount[currentYear - 1] ?? 0 + currentYear = people.yearOfBirth + } + + if currentAlive > maxAlive { + maxAlive = currentAlive + maxAliveYear = currentYear + } + } + + return maxAliveYear + } + + private func addOrIncrement(hashTable: inout [Int: Int], key: Int, by: Int) { + if let currentValue = hashTable[key] { + hashTable[key] = currentValue + by + } else { + hashTable[key] = by + } + } +} + +public struct People { + let name: String + let yearOfBirth: Int + let yearOfDeath: Int? +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/MedianKeeper.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/MedianKeeper.swift new file mode 100644 index 0000000..b1fc2c4 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/MedianKeeper.swift @@ -0,0 +1,72 @@ +// +// MedianKeeper.swift +// +// +// Created by Stefan Jaindl on 12.12.20. +// + +import Foundation + +open class MedianKeeper { + public init() { } + + open func median(of values: [T]) throws -> T? { + guard !values.isEmpty else { + return nil + } + + if values.count == 1 { + return values[0] + } + + let upperRangeHeap = MinHeap() //stores larger half of elements + let lowerRangeHeap = MaxHeap() //stores smaller half of elements + + for value in values { + if upperRangeHeap.isEmpty(), lowerRangeHeap.isEmpty() { + upperRangeHeap.insert(val: value) + continue + } else if lowerRangeHeap.isEmpty() { + //exactly 1 element in min heap + if try value > upperRangeHeap.peekMin() { + let min = try upperRangeHeap.extractMin() + upperRangeHeap.insert(val: value) + lowerRangeHeap.insert(val: min) + } else { + lowerRangeHeap.insert(val: value) + } + } else { //both heaps contain elements + if try value > upperRangeHeap.peekMin() { + //store in upper range + if upperRangeHeap.numberOfElements() > lowerRangeHeap.numberOfElements() { + let min = try upperRangeHeap.extractMin() + upperRangeHeap.insert(val: value) + lowerRangeHeap.insert(val: min) + } else { + upperRangeHeap.insert(val: value) + } + } else { + //store in lower range + if lowerRangeHeap.numberOfElements() > upperRangeHeap.numberOfElements() { + let max = try lowerRangeHeap.extractMax() + lowerRangeHeap.insert(val: value) + upperRangeHeap.insert(val: max) + } else { + lowerRangeHeap.insert(val: value) + } + } + } + } + + if lowerRangeHeap.numberOfElements() == upperRangeHeap.numberOfElements() { + let lower = try lowerRangeHeap.peekMax() + let upper = try upperRangeHeap.peekMin() + + return (lower + upper) / 2 + } else if lowerRangeHeap.numberOfElements() < upperRangeHeap.numberOfElements() { + return try upperRangeHeap.peekMin() + } else { + return try lowerRangeHeap.peekMax() + } + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/MultiSearch.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/MultiSearch.swift new file mode 100644 index 0000000..eda13ac --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/MultiSearch.swift @@ -0,0 +1,69 @@ +// +// MultiSearch.swift +// +// +// Created by Stefan Jaindl on 11.12.20. +// + +import Foundation + +open class MultiSearch { + public init() { } + + //returns matched sub arrays + open func multiSearch(in string: String, for subArrays: [String]) -> [String] { + guard !string.isEmpty, !subArrays.isEmpty else { + return [] + } + + var matched = Set() + let trie = buildTrie(for: subArrays) + let chars = Array(string) + + for index in 0 ..< string.count { + let subStrings = isSubArray(string: chars, index: index, trie: trie) + subStrings.forEach { substring in + matched.insert(substring) + } + } + + var matchedArray: [String] = [] + matchedArray.append(contentsOf: matched) + + return matchedArray + } + + private func isSubArray(string: [Character], index: Int, trie: Trie) -> Set { + var trieNode: TrieNode? = trie.root + var subStrings = Set() + var charsSoFar: [Character] = [] + + var currentIndex = index + while currentIndex < string.count, trieNode != nil { + let char = string[currentIndex] + trieNode = trieNode?.childNodes[char] + if trieNode == nil { + break + } + + charsSoFar.append(char) + if trieNode?.terminates == true { + subStrings.insert(String(charsSoFar)) + } + + currentIndex += 1 + } + + return subStrings + } + + private func buildTrie(for array: [String]) -> Trie { + let trie = Trie() + + for string in array { + trie.insert(word: string) + } + + return trie + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/PeakSort.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/PeakSort.swift new file mode 100644 index 0000000..a1f0d8f --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/PeakSort.swift @@ -0,0 +1,33 @@ +// +// PeakSort.swift +// +// +// Created by Stefan Jaindl on 29.09.20. +// + +import Foundation + +open class PeakSort { + public init() { } + + open func sort(array: inout [T]) { + var index = 0 + + while index < array.count { + if array.count - 1 > index { + //If there's only 1 element left, we don't need to sort it. + //We want the peak (higher or equal element to appear first) + if array[index + 1] > array[index] { + array.swapAt(index, index + 1) + } + } + + //Check and fix previous misplacement + if index > 0 && array[index - 1] > array[index] { + array.swapAt(index - 1, index) + } + + index += 2 + } + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/RotatedArraySearch.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/RotatedArraySearch.swift new file mode 100644 index 0000000..f13d820 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/RotatedArraySearch.swift @@ -0,0 +1,62 @@ +// +// RotatedArraySearch.swift +// +// +// Created by Stefan Jaindl on 21.09.20. +// + +import Foundation + +open class RotatedArraySearch { + public init() { } + + open func search(array: [T], searched: T) -> Int? { + guard !array.isEmpty else { + return nil + } + + return search(array: array, searched: searched, low: 0, high: array.count - 1) + } + + private func search(array: [T], searched: T, low: Int, high: Int) -> Int? { + let center = low + (high - low) / 2 + if array[center] == searched { + return center + } + + if high <= low { + return nil + } + + //search for ordered half + if array[low] < array[center] { + //left half is ordered + if array[low] <= searched && searched <= array[center] { + //within left range + return search(array: array, searched: searched, low: low, high: center - 1) + } else { + //not within left range - search right + return search(array: array, searched: searched, low: center + 1, high: high) + } + } else if array[high] > array[center] { + //right half is ordered + if array[center] <= searched && searched <= array[high] { + //within right range + return search(array: array, searched: searched, low: center + 1, high: high) + } else { + //not within right range - search left + return search(array: array, searched: searched, low: low, high: center - 1) + } + } else { + //could be on both sides + if array[high] != array[center] { + return search(array: array, searched: searched, low: center + 1, high: high) + } else { + if let result = search(array: array, searched: searched, low: center + 1, high: high) { + return result + } + return search(array: array, searched: searched, low: low, high: center - 1) + } + } + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/SortedMatrixSearch.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/SortedMatrixSearch.swift new file mode 100644 index 0000000..2f232b6 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/SortedMatrixSearch.swift @@ -0,0 +1,201 @@ +// +// SortedMatrixSearch.swift +// +// +// Created by Stefan Jaindl on 26.09.20. +// + +import Foundation + +open class SortedMatrixSearch { + public init() { } + + //Searches a path through the matrix: O(M * N) average case. O(M * N) additional space. + open func sortedMatrixSearch(matrix: [[T]], searched: T) -> Bool { + guard !matrix.isEmpty, !matrix[0].isEmpty else { + return false + } + + let columnCount = matrix[0].count + for row in 0 ..< matrix.count { + if matrix[row].count != columnCount { + //Matrix has different amount of columns! + return false + } + } + + var lookupMatrix: [[Bool]] = [[Bool]](repeating: [Bool](repeating: false, count: columnCount), count: matrix.count) + + return containsRecursive(matrix: matrix, searched: searched, row: 0, column: 0, lookupMatrix: &lookupMatrix) + } + + private func containsRecursive(matrix: [[T]], searched: T, row: Int, column: Int, lookupMatrix: inout [[Bool]]) -> Bool { + lookupMatrix[row][column] = true + let matrixValue = matrix[row][column] + + if matrixValue > searched { + return false + } + + if matrixValue == searched { + return true + } + + var submatch = false + if isInBounds(matrix: matrix, row: row + 1, column: column), !lookupMatrix[row + 1][column] { + submatch = containsRecursive(matrix: matrix, searched: searched, row: row + 1, column: column, lookupMatrix: &lookupMatrix) + } + if !submatch, isInBounds(matrix: matrix, row: row, column: column + 1), !lookupMatrix[row][column + 1] { + submatch = containsRecursive(matrix: matrix, searched: searched, row: row, column: column + 1, lookupMatrix: &lookupMatrix) + } + + return submatch + } + + private func isInBounds(matrix: [[T]], row: Int, column: Int) -> Bool { + return row >= 0 && column >= 0 && row < matrix.count && column < matrix[0].count + } + + // Faster Implementation and without additional space. + //Searches from center of matrix onwards to 4 sides (at least eliminates one quarter at each rec. call). No additional space. + open func sortedMatrixSearchFaster(matrix: [[T]], searched: T) -> Bool { + guard !matrix.isEmpty, !matrix[0].isEmpty else { + return false + } + + let columnCount = matrix[0].count + for row in 0 ..< matrix.count { + if matrix[row].count != columnCount { + //Matrix has different amount of columns! + return false + } + } + + let from = Coordinate(row: 0, column: 0) + let to = Coordinate(row: matrix.count - 1, column: columnCount - 1) + return containsRecursiveFaster(matrix: matrix, searched: searched, from: from, to: to) + } + + private func containsRecursiveFaster(matrix: [[T]], searched: T, from: Coordinate, to: Coordinate) -> Bool { + let currentValue = value(of: matrix, at: from) + if currentValue == searched { //found + return true + } + + if currentValue > searched { //not in matrix + return false + } + + if (from == to) { //only 1 field left + return false + } + + if from.column == to.column { //only 1 column left, move on to next row + let newFrom = Coordinate(row: from.row + 1, column: from.column) + return containsRecursiveFaster(matrix: matrix, searched: searched, from: newFrom, to: to) + } + + if from.row == to.row { //only 1 row left, move on to next column + let newFrom = Coordinate(row: from.row, column: from.column + 1) + return containsRecursiveFaster(matrix: matrix, searched: searched, from: newFrom, to: to) + } + + guard let partition = partitionDiagonally(matrix: matrix, searched: searched, from: from, to: to) else { + return false + } + + if partition.found { + return true + } + + var found = false + //search top right + let topRightFrom = Coordinate(row: from.row, column: partition.bottomRightCoordinate.column) + let topRightTo = Coordinate(row: partition.topLeftCoordinate.row, column: to.column) + if isInBounds(matrix: matrix, coordinate: topRightFrom) { + found = containsRecursiveFaster(matrix: matrix, searched: searched, from: topRightFrom, to: topRightTo) + } + + //search bottom left + let bottomLeftFrom = Coordinate(row: partition.bottomRightCoordinate.row, column: from.column) + let bottomLeftTo = Coordinate(row: to.row, column: partition.topLeftCoordinate.column) + if !found && isInBounds(matrix: matrix, coordinate: bottomLeftFrom) { + found = containsRecursiveFaster(matrix: matrix, searched: searched, from: bottomLeftFrom, to: bottomLeftTo) + } + + return found + } + + private func partitionDiagonally(matrix: [[T]], searched: T, from: Coordinate, to: Coordinate) -> (topLeftCoordinate: Coordinate, bottomRightCoordinate: Coordinate, found: Bool)? { + if value(of: matrix, at: from) > searched { + //searched value lower than any in matrix + return nil + } + + if value(of: matrix, at: to) < searched { + //searched value higher than any in matrix + return nil + } + + //find partitioning element, where first coordinate < searched and last coordinate > searched. + //Or return true if element found + + let maxAllowedDiff = min(to.column - from.column, to.row - from.row) + var topLeftCoordinate = from + var bottomRightCoordinate = to + let bottomRightCoordinateToCompare = Coordinate(row: min(to.row, from.row + maxAllowedDiff), column: min(to.column, from.column + maxAllowedDiff)) + while topLeftCoordinate < bottomRightCoordinate { + let center = coordinatesCenter(from: topLeftCoordinate, to: bottomRightCoordinateToCompare) + let diff = center.column - topLeftCoordinate.column + let valueAtCenter = value(of: matrix, at: center) + let valueAtTopLeft = value(of: matrix, at: topLeftCoordinate) + let valueAtBottomRight = value(of: matrix, at: bottomRightCoordinate) + let valueAtBottomRightToCompare = value(of: matrix, at: bottomRightCoordinateToCompare) + + if valueAtCenter == searched || valueAtTopLeft == searched || valueAtBottomRight == searched || valueAtBottomRightToCompare == searched { + //value found + return (center, center, true) + } else if bottomRightCoordinate.column - topLeftCoordinate.column == 1 + && valueAtTopLeft < searched && valueAtBottomRight > searched, valueAtTopLeft < searched && valueAtBottomRight > searched { + //coordinates are next to each other and searched value is in between + break + } else if valueAtCenter < searched { + //go right/down + topLeftCoordinate = Coordinate(row: topLeftCoordinate.row + diff, column: topLeftCoordinate.column + diff) + } else { //valueAtCenter < searched + //go left/up + bottomRightCoordinate = Coordinate(row: center.row, column: center.column) + } + } + + return (topLeftCoordinate, bottomRightCoordinate, false) + } + + private func isInBounds(matrix: [[T]], coordinate: Coordinate) -> Bool { + return coordinate.row >= 0 && coordinate.column >= 0 && coordinate.row < matrix.count && coordinate.column < matrix[0].count + } + + private func value(of matrix: [[T]], at coordinate: Coordinate) -> T { + return matrix[coordinate.row][coordinate.column] + } + + private func coordinatesCenter(from: Coordinate, to: Coordinate) -> Coordinate { + let centerRow = (from.row + to.row) / 2 + let centerColumn = (from.column + to.column) / 2 + + return Coordinate(row: centerRow, column: centerColumn) + } +} + +private struct Coordinate: Equatable, Comparable { + var row: Int + var column: Int + + static func == (lhs: Coordinate, rhs: Coordinate) -> Bool { + return lhs.row == rhs.row && lhs.column == rhs.column + } + + static func < (lhs: Coordinate, rhs: Coordinate) -> Bool { + return lhs.row < rhs.row && lhs.column < rhs.column + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/SortedMerge.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/SortedMerge.swift new file mode 100644 index 0000000..31d99a6 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/SortedMerge.swift @@ -0,0 +1,76 @@ +// +// SortedMerge.swift +// +// +// Created by Stefan Jaindl on 18.09.20. +// + +import Foundation + +open class SortedMerge { + + public init() { } + + //Merges second array into first + //Prerequisites: Both arrays are already sorted AND first array has enough space (nil elements) at the end to hold second array + func sortAndMerge(first: inout [T?], second: inout [T?]) { + if first.isEmpty || second.isEmpty { + return + } + + var firstPos = 0 + + //Check as long as there is no elements left in first array + while first.element(at: firstPos) != nil { + while let firstElement = first.element(at: firstPos), let secondElement = second.element(at: 0), firstElement <= secondElement { + //move along as long as current element of first array <= current element of second array + firstPos += 1 + } + + if first.element(at: firstPos) == nil { + //We have sorted elements for first array length + break + } + + exchange(first: &first, second: &second, firstPos: firstPos, secondPos: 0) + + var secondPos = 0 + while secondPos + 1 < second.count, let secondElement = second.element(at: secondPos), let nextSecondElement = second.element(at: secondPos + 1), secondElement > nextSecondElement { + //Correct order of second array after new element was added in front, as long as necessary + exchange(array: &second, firstPos: secondPos, secondPos: secondPos + 1) + + secondPos += 1 + } + } + + var secondPos = 0 + //Append remaining elements from second array to first (they are sorted and >= current last element of first array) + while secondPos < second.count { + first[first.index(at: firstPos)] = second.element(at: secondPos) + firstPos += 1 + secondPos += 1 + } + } + + private func exchange(array: inout [T?], firstPos: Int, secondPos: Int) { + let firstValue = array.element(at: firstPos) + array[array.index(at: firstPos)] = array.element(at: secondPos) + array[array.index(at: secondPos)] = firstValue + } + + private func exchange(first: inout [T?], second: inout [T?], firstPos: Int, secondPos: Int) { + let firstValue = first.element(at: firstPos) + first[first.index(at: firstPos)] = second.element(at: secondPos) + second[second.index(at: secondPos)] = firstValue + } +} + +extension Array { + public func index(at offset: Int) -> Index { + return index(startIndex, offsetBy: offset) + } + + public func element(at offset: Int) -> Element { + return self[index(at: offset)] + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/SparseSearch.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/SparseSearch.swift new file mode 100644 index 0000000..1dc5f12 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/SparseSearch.swift @@ -0,0 +1,61 @@ +// +// SparseSearch.swift +// +// +// Created by Stefan Jaindl on 23.09.20. +// + +import Foundation + +open class SparseSearch { + public init() { } + + open func sparseSearch(array: [String], searched: String) -> Int? { + guard !array.isEmpty, !searched.isEmpty else { + return nil + } + + return sparseSearch(array: array, searched: searched, low: 0, high: array.count - 1) + } + + private func sparseSearch(array: [String], searched: String, low: Int, high: Int) -> Int? { + let center = low + (high - low) / 2 + if array[center] == searched { + return center + } + + if high <= low { + return nil + } + + var newIndex = center + + //First, move right to skip blank strings + while newIndex < high - 1, array[newIndex] == "" { + newIndex += 1 + } + + //If there are no valid strings on the right side, go to left side + if array[newIndex] == "" { + newIndex = center + while newIndex > low + 1, array[newIndex] == "" { + newIndex -= 1 + } + } + + //.. Alternatively, we could go 1 index left & right simultaneously until we find a non-empty string + + //If we haven't found a valid string, string isn't in array + if array[newIndex] == "" { + return nil + } + + if array[newIndex] == searched { + return newIndex + } else if array[newIndex] < searched { + return sparseSearch(array: array, searched: searched, low: max(center, newIndex) + 1, high: high) + } else { //array[newIndex] > searched + return sparseSearch(array: array, searched: searched, low: low, high: min(center, newIndex) - 1) + } + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/StreamRank.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/StreamRank.swift new file mode 100644 index 0000000..0e179b8 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/StreamRank.swift @@ -0,0 +1,22 @@ +// +// StreamRank.swift +// +// +// Created by Stefan Jaindl on 27.09.20. +// + +import Foundation + +open class StreamRank { + var rankTree = CompressedAVLChildCountTree() + + public init() { } + + open func track(value: T) { + rankTree.insert(val: value) + } + + open func rank(of value: T) -> Int { + return rankTree.rank(of: value) + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/SubSorter.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/SubSorter.swift new file mode 100644 index 0000000..b2a891a --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/SortingAndSearching/SubSorter.swift @@ -0,0 +1,99 @@ +// +// SubSorter.swift +// +// +// Created by Stefan Jaindl on 28.11.20. +// + +import Foundation + +open class SubSorter { + public init() { } + + open func shortestSubsortRange(of array: [Int]) -> SortRange? { + if array.count <= 1 { + return nil + } + + var leftSequenceIndex = 0 + var rightSequenceIndex = array.count - 1 + var leftValue = Int.min + var rightValue = Int.max + + //find longest sorted sequence from left + while leftSequenceIndex < array.count, array[leftSequenceIndex] > leftValue { + leftValue = array[leftSequenceIndex] + leftSequenceIndex += 1 + } + if leftSequenceIndex > 0 { + leftSequenceIndex -= 1 //reset to last valid pos + } + + //find longest sorted sequence from right + while rightSequenceIndex >= 0, array[rightSequenceIndex] < rightValue { + rightValue = array[rightSequenceIndex] + rightSequenceIndex -= 1 + } + if rightSequenceIndex < array.count - 1 { + rightSequenceIndex += 1 //reset to last valid pos + } + + if rightSequenceIndex <= leftSequenceIndex { + return nil //already sorted + } + + //center elements are definitely included in subsort + if rightSequenceIndex - leftSequenceIndex > 0 { + //Find min and max center elements and move sequence indices back to some point, if necessary + let (min, max) = minMax(of: array, from: leftSequenceIndex, to: rightSequenceIndex) + + leftSequenceIndex = shiftLeft(array: array, index: leftSequenceIndex, to: min) + rightSequenceIndex = shiftRight(array: array, index: rightSequenceIndex, to: max) + } + + return SortRange(from: leftSequenceIndex, to: rightSequenceIndex) + } + + private func minMax(of array: [Int], from: Int, to: Int) -> (min: Int, max: Int) { + var minimum = Int.max + var maximum = Int.min + + for index in from ... to { + minimum = min(array[index], minimum) + maximum = max(array[index], maximum) + } + + return (min: minimum, max: maximum) + } + + private func shiftLeft(array: [Int], index: Int, to value: Int) -> Int { + var newIndex = index + while newIndex >= 0, array[newIndex] > value { + newIndex -= 1 + } + + if newIndex != index { + newIndex += 1 //set to last valid pos + } + + return newIndex + } + + private func shiftRight(array: [Int], index: Int, to value: Int) -> Int { + var newIndex = index + while newIndex < array.count, array[newIndex] < value { + newIndex += 1 + } + + if newIndex != index { + newIndex -= 1 //set to last valid pos + } + + return newIndex + } +} + +public struct SortRange: Equatable { + let from: Int + let to: Int +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Stack/AnimalShelter.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Stack/AnimalShelter.swift new file mode 100644 index 0000000..fe83907 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Stack/AnimalShelter.swift @@ -0,0 +1,102 @@ +// +// AnimalShelter.swift +// +// +// Created by Stefan Jaindl on 18.06.20. +// + +import Foundation + +open class AnimalShelter { + private var head: Node? + private var dogTail: Node? + private var catTail: Node? + + public init() { } + + open func enqueue(animal: Animal) { + let node = Node(val: animal) + + if head == nil { + head = node + } + + if animal is Dog { + if dogTail == nil { + dogTail = node + } else { + dogTail?.next = node + node.prev = dogTail + dogTail = node + } + } else if animal is Cat { + if catTail == nil { + catTail = node + } else { + catTail?.next = node + node.prev = catTail + catTail = node + } + } + } + + open func dequeueDog() throws -> Dog { + guard let dog = dogTail?.val as? Dog else { + throw NSError(domain: "Stack: Invalid call", code: 0, userInfo: nil) + } + + dogTail = dogTail?.prev + dogTail?.next = nil + + if dogTail == nil, head?.val is Dog { + head = nil + } + + return dog + } + + open func dequeueCat() throws -> Cat { + guard let cat = catTail?.val as? Cat else { + throw NSError(domain: "Stack: Invalid call", code: 0, userInfo: nil) + } + + catTail = catTail?.prev + catTail?.next = nil + + if catTail == nil, head?.val is Cat { + head = nil + } + + return cat + } + + open func dequeueAny() throws -> Animal { + //Is the last element a dog? + if catTail?.next != nil { + return try dequeueDog() + } + + return try dequeueCat() + } + +} + +open class Animal: Hashable, Equatable { + var name: String + + init(name: String) { + self.name = name + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(name) + } + + public static func == (lhs: Animal, rhs: Animal) -> Bool { + return lhs.name == rhs.name + } +} + +open class Dog: Animal { } + +open class Cat: Animal { } diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Stack/Calculator.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Stack/Calculator.swift new file mode 100644 index 0000000..9ce0bc3 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Stack/Calculator.swift @@ -0,0 +1,103 @@ +// +// Calculator.swift +// +// +// Created by Stefan Jaindl on 01.12.20. +// + +import Foundation + +open class Calculator { + let tempStack = Stack() + let resultStack = Stack() + + public init() { } + + open func calculate(expressions: [Expression]) throws -> Double { + guard !expressions.isEmpty else { + return 0.0 + } + + var lastOperator: Expression? + + try expressions.forEach { expression in + switch expression { + case .operand(let number): + try handleOperand(number: number, lastOperator: lastOperator) + case .plus: + lastOperator = .plus + try handlePlus() + case .minus: + lastOperator = .minus + try handleMinus() + case .divide: + lastOperator = .divide + case .multiply: + lastOperator = .multiply + } + } + + if !tempStack.isEmpty() { + let lastElement = try tempStack.pop() + let lastResult = try resultStack.pop() + resultStack.push(val: lastElement + lastResult) + } + + return try resultStack.pop() + } + + private func handleOperand(number: Double, lastOperator: Expression?) throws { + if tempStack.isEmpty() { + tempStack.push(val: number) + } else if let lastOperator = lastOperator { + switch lastOperator { + case .multiply: + let firstOperand = try tempStack.pop() + tempStack.push(val: number * firstOperand) + case .divide: + let firstOperand = try tempStack.pop() + tempStack.push(val: firstOperand / number) + case .plus: + tempStack.push(val: number) + case .minus: + tempStack.push(val: -number) + case .operand(_): + throw NSError(domain: "Invalid Input", code: 0, userInfo: nil) + } + } + } + + private func handlePlus() throws { + if !tempStack.isEmpty() { + let lastResult = try tempStack.pop() + if !resultStack.isEmpty() { + let resultSoFar = try resultStack.pop() + resultStack.push(val: resultSoFar + lastResult) + } else { + resultStack.push(val: lastResult) + } + } + } + + private func handleMinus() throws { + if !tempStack.isEmpty() { + let lastResult = try tempStack.pop() + if !resultStack.isEmpty() { + let resultSoFar = try resultStack.pop() + resultStack.push(val: resultSoFar - lastResult) + } else { + resultStack.push(val: lastResult) + } + } + } +} + +extension Calculator { + public enum Expression { + case operand(Double) + case plus + case minus + case divide + case multiply + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Stack/MinStack.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Stack/MinStack.swift new file mode 100644 index 0000000..0339c85 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Stack/MinStack.swift @@ -0,0 +1,54 @@ +// +// MinStack.swift +// +// +// Created by Stefan Jaindl on 18.06.20. +// + +import Foundation + +open class MinStack { + private var head: MinStackNode? + private var size: Int = 0 + + open func push(value: T) { + let minValueSubstack = Swift.min(value, head?.minValueSubstack ?? value) + let newHead = MinStackNode(value: value, next: head, minValueSubstack: minValueSubstack) + head = newHead + size += 1 + } + + open func pop() throws -> T { + guard let node = head else { + throw NSError(domain: "Stack: Invalid call", code: 0, userInfo: nil) + } + + head = node.next + size -= 1 + return node.value + } + + open func isEmpty() -> Bool { + return size == 0 + } + + open func min() throws -> T { + guard let head = head else { + throw NSError(domain: "Stack: Invalid call", code: 0, userInfo: nil) + } + + return head.minValueSubstack + } +} + +private class MinStackNode { + var value: T + var next: MinStackNode? + var minValueSubstack: T + + init(value: T, next: MinStackNode?, minValueSubstack: T) { + self.value = value + self.next = next + self.minValueSubstack = minValueSubstack + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Stack/MultiStack.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Stack/MultiStack.swift new file mode 100644 index 0000000..06c63d8 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Stack/MultiStack.swift @@ -0,0 +1,136 @@ +// +// MultiStack.swift +// +// +// Created by Stefan Jaindl on 14.06.20. +// + +import Foundation + +open class MultiStack { + + private var numberOfStacks: Int + private var stackInfo: [StackInfo] = [] + private var array: [T?] + private var totalElements: Int + + public init(numberOfStacks: Int, len: Int) { + self.numberOfStacks = numberOfStacks + stackInfo.reserveCapacity(numberOfStacks) + + let baseCapacity = len / numberOfStacks + let remainder = len % numberOfStacks + for stackNumber in 0 ..< numberOfStacks { + let index = stackNumber * len / numberOfStacks + let stackCapacity = stackNumber == numberOfStacks - 1 ? baseCapacity + remainder : baseCapacity + stackInfo.append(StackInfo(numberOfElements: 0, capacity: stackCapacity, startIndex: index)) + } + + array = [T?](repeating: nil, count: len) + totalElements = 0 + } + + open func isEmpty(stackNumber: Int) -> Bool { + return stackInfo[stackNumber].numberOfElements == 0 + } + + open func peek(stackNumber: Int) throws -> T { + if isEmpty(stackNumber: stackNumber) { + throw NSError(domain: "Stack: Invalid call", code: 0, userInfo: nil) + } + + guard let element = array[lastIndex(stackNumber: stackNumber)] else { + throw NSError(domain: "Stack: Invalid call", code: 0, userInfo: nil) + } + + return element + } + + open func pop(stackNumber: Int) throws -> T { + let element = try peek(stackNumber: stackNumber) + + stackInfo[stackNumber].numberOfElements -= 1 + + totalElements -= 1 + + return element + } + + open func push(stackNumber: Int, element: T) throws { + if totalElements == array.count { + throw NSError(domain: "Stack: Invalid call", code: 0, userInfo: nil) + } + + let info = stackInfo[stackNumber] + + if info.isFull() { + decreaseCapacity(stackNumber: (stackNumber + 1) % numberOfStacks) + info.capacity += 1 + } + + let index = currentIndex(stackNumber: stackNumber) + array[index] = element + info.numberOfElements += 1 + + totalElements += 1 + } + + private func decreaseCapacity(stackNumber: Int) { + if stackInfo[stackNumber].isFull() { + decreaseCapacity(stackNumber: (stackNumber + 1) % numberOfStacks) + stackInfo[stackNumber].capacity += 1 + shiftRight(stackNumber: stackNumber) + stackInfo[stackNumber].startIndex += 1 + stackInfo[stackNumber].capacity -= 1 + } else { + shiftRight(stackNumber: stackNumber) + stackInfo[stackNumber].startIndex += 1 + stackInfo[stackNumber].capacity -= 1 + } + } + + private func shiftRight(stackNumber: Int) { + if isEmpty(stackNumber: stackNumber) { + return + } + + var index = lastIndex(stackNumber: stackNumber) + while index >= stackInfo[stackNumber].startIndex { + let nextIndex = index + 1 + array[nextIndex] = array[index] + index -= 1 + } + } + + private func lastIndex(stackNumber: Int) -> Int { + return stackInfo[stackNumber].lastIndex % array.count + } + + private func currentIndex(stackNumber: Int) -> Int { + return stackInfo[stackNumber].currentIndex % array.count + } +} + +private class StackInfo { + var numberOfElements: Int + var capacity: Int + var startIndex: Int + + init(numberOfElements: Int, capacity: Int, startIndex: Int) { + self.numberOfElements = numberOfElements + self.capacity = capacity + self.startIndex = startIndex + } + + var currentIndex: Int { + return startIndex + numberOfElements + } + + var lastIndex: Int { + return startIndex + numberOfElements - 1 + } + + func isFull() -> Bool { + return numberOfElements == capacity + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Stack/QueueWithStacks.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Stack/QueueWithStacks.swift new file mode 100644 index 0000000..649d01c --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Stack/QueueWithStacks.swift @@ -0,0 +1,39 @@ +// +// MinStack.swift +// +// +// Created by Stefan Jaindl on 18.06.20. +// + +import Foundation + +open class QueueWithStacks { + private var firstStack = Stack() + private var secondStack = Stack() + + public init() { } + + open func enqueue(value: T) throws { + firstStack.push(val: value) + } + + open func dequeue() throws -> T { + if firstStack.isEmpty() && secondStack.isEmpty() { + throw NSError(domain: "Stack: Invalid call", code: 0, userInfo: nil) + } + + moveElements(from: firstStack, to: secondStack) + + return try secondStack.pop() + } + + open func isEmpty() -> Bool { + return firstStack.isEmpty() && secondStack.isEmpty() + } + + private func moveElements(from: Stack, to: Stack) { + while !from.isEmpty(), let node = try? from.pop() { + to.push(val: node) + } + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Stack/StackSet.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Stack/StackSet.swift new file mode 100644 index 0000000..aa60b37 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Stack/StackSet.swift @@ -0,0 +1,160 @@ +// +// StackSet.swift +// +// +// Created by Stefan Jaindl on 21.06.20. +// + +import Foundation + +open class StackSet { + private var totalElements = 0 + private let treshold: Int + private var stacks: [SingleStack] = [] + + public init(treshold: Int) { + self.treshold = treshold + + stacks.append(SingleStack(capacity: treshold)) + } + + open func isEmpty() -> Bool { + return totalElements == 0 + } + + open func push(element: T) throws { + var stack = lastStack() + + if stack.isFull() { + stack = addStack() + } + + try stack.push(element: element) + + totalElements += 1 + } + + open func pop() throws -> T { + if isEmpty() { + throw NSError(domain: "Stack: Invalid call", code: 0, userInfo: nil) + } + + let value = try lastStack().pop() + resizeStacks() + totalElements -= 1 + + return value + } + + open func pop(at index: Int) throws -> T { + if index < 0 || index >= stacks.count { + throw NSError(domain: "Stack: Invalid call", code: 0, userInfo: nil) + } + + let value = try stacks[index].pop() + try moveLeft(from: index) + totalElements -= 1 + + return value + } + + private func lastStack() -> SingleStack { + stacks.count > 1 && stacks[stacks.count - 1].isEmpty() ? stacks[stacks.count - 2] : stacks[stacks.count - 1] + } + + private func addStack() -> SingleStack { + stacks.append(SingleStack(capacity: treshold)) + return stacks[stacks.count - 1] + } + + private func resizeStacks() { + guard stacks.count > 1 else { + return + } + + if stacks[stacks.count - 1].isEmpty() && stacks[stacks.count - 2].isEmpty() { + stacks.removeLast() + } + } + + private func moveLeft(from index: Int) throws { + var currentIndex = index + + while currentIndex + 1 < stacks.count { + let firstStack = stacks[currentIndex] + let secondStack = stacks[currentIndex + 1] + + var secondStackIndex = 0 + + while !firstStack.isFull(), secondStackIndex < secondStack.capacity, let element = secondStack.array[secondStackIndex] { + try firstStack.push(element: element) + secondStack.array[secondStackIndex] = nil + secondStack.numElements -= 1 + secondStackIndex += 1 + } + + moveNonNullValuesToBeginning(of: secondStack) + currentIndex += 1 + } + } + + private func moveNonNullValuesToBeginning(of stack: SingleStack) { + //move non-null values of array to beginning + if stack.array[0] == nil { + var backIndex = 0 + while backIndex < stack.array.count, stack.array[backIndex] == nil { + backIndex += 1 + } + + var frontIndex = 0 + while backIndex < stack.capacity - 1 { + stack.array[frontIndex] = stack.array[backIndex] + stack.array[backIndex] = nil + frontIndex += 1 + backIndex += 1 + } + + while stack.array[stack.array.count - 1] == nil { + stack.array.removeLast() + } + } + } +} + +private class SingleStack { + var numElements: Int = 0 + let capacity: Int + var array: [T?] = [] + + init(capacity: Int) { + self.capacity = capacity + } + + func isFull() -> Bool { + return numElements == capacity + } + + func isEmpty() -> Bool { + return numElements == 0 + } + + func push(element: T) throws { + if isFull() { + throw NSError(domain: "Stack: Invalid call", code: 0, userInfo: nil) + } + + array.append(element) + numElements += 1 + } + + func pop() throws -> T { + guard !isEmpty(), let value = array[numElements - 1] else { + throw NSError(domain: "Stack: Invalid call", code: 0, userInfo: nil) + } + + array.removeLast() + numElements -= 1 + + return value + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Stack/StackSort.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Stack/StackSort.swift new file mode 100644 index 0000000..6abdbe3 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Stack/StackSort.swift @@ -0,0 +1,35 @@ +// +// MultiStack.swift +// +// +// Created by Stefan Jaindl on 21.06.20. +// + +import Foundation + +open class StackSort { + + public init() { } + + public static func sort(stack: Stack) { + let tempStack = Stack() + + while !stack.isEmpty(), let element = try? stack.pop() { + if tempStack.isEmpty() { + tempStack.push(val: element) + } else { + while !tempStack.isEmpty(), let tempElement = try? tempStack.peek(), tempElement >= element { + try? stack.push(val: tempStack.pop()) + } + + tempStack.push(val: element) + } + } + + //elements are now sorted in reverse order in temp stack. + //So, copy elements back from temp to original stack to have in correct order + while !tempStack.isEmpty() { + try? stack.push(val: tempStack.pop()) + } + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Threading/DeadlockPreventingLock.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Threading/DeadlockPreventingLock.swift new file mode 100644 index 0000000..4ff6b51 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Threading/DeadlockPreventingLock.swift @@ -0,0 +1,94 @@ +// +// DeadlockPreventingLock.swift +// +// +// Created by Stefan Jaindl on 07.10.20. +// + +import Foundation + +open class DeadlockPreventingLock { + public init(vertices: [Vertice]) { + graph = DirectedGraph(vertices: vertices) + } + + var graph: DirectedGraph + + open func canAddThread(with usage: RessourceUsage) -> Bool { + buildGraph(with: usage) + + if containsCycle() { + resetGraph(with: usage) + return false + } + + return true + } + + private func containsCycle() -> Bool { + var checked = Set() + + for vertice in graph.vertices { + let visited = Set() + if !checked.contains(where: { $0 == vertice.id }) { + checked.insert(vertice.id) + + if containsCycle(vertice: vertice, visited: visited, checked: &checked) { + return true + } + } + } + + return false + } + + private func containsCycle(vertice: Vertice, visited: Set, checked: inout Set) -> Bool { + var current = graph.neighbours(v: vertice).head + checked.insert(vertice.id) + + var curVisited = visited + + while let cur = current { + if curVisited.contains(where: { $0 == cur.val.id }) { + return true //already visited this node, cycle detected! + } + + curVisited.insert(cur.val.id) + if containsCycle(vertice: cur.val, visited: curVisited, checked: &checked) { + return true + } + + current = cur.next + } + + return false + } + + private func buildGraph(with usage: RessourceUsage) { + var previous: Vertice? = nil + for vertice in usage.lockOrder { + if let previous = previous { + graph.addEdge(v1: previous, v2: vertice) + } + previous = vertice + } + } + + private func resetGraph(with usage: RessourceUsage) { + var previous: Vertice? = nil + for vertice in usage.lockOrder { + if let previous = previous { + graph.removeEdge(v1: previous, v2: vertice) + } + previous = vertice + } + } +} + +open class RessourceUsage { + let lockOrder: [Vertice] + + public init(lockOrder: [Vertice]) { + self.lockOrder = lockOrder + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Threading/DiningPhilosophers.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Threading/DiningPhilosophers.swift new file mode 100644 index 0000000..1310da3 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Threading/DiningPhilosophers.swift @@ -0,0 +1,56 @@ +// +// DiningPhilosophers.swift +// +// +// Created by Stefan Jaindl on 07.10.20. +// + +import Foundation + +open class Chopstick { + let index: Int + let nslock = NSLock() + var lockedCount = 0 + + public init(index: Int) { + self.index = index + } + + open func lock() { + nslock.lock() + } + + open func unlock() { + nslock.unlock() + lockedCount += 1 + } +} + +open class Philosopher: Thread { + let philosopherName: String + let left: Chopstick + let right: Chopstick + + let queue = DispatchQueue(label: "philosophersQueue") + + public init(name: String, left: Chopstick, right: Chopstick) { + philosopherName = name + self.left = left + self.right = right + } + + open func eat() { + queue.async { + let lower = self.left.index < self.right.index ? self.left : self.right + let higher = self.left.index >= self.right.index ? self.left : self.right + lower.lock() + higher.lock() + + Philosopher.sleep(forTimeInterval: 0.1) + + higher.unlock() + lower.unlock() + } + } +} + diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Threading/FizzBuzz.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Threading/FizzBuzz.swift new file mode 100644 index 0000000..b251852 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Threading/FizzBuzz.swift @@ -0,0 +1,68 @@ +// +// FizzBuzz.swift +// +// +// Created by Stefan Jaindl on 09.10.20. +// + +import Foundation + +open class FizzBuzz { + + let targetNumber: Int + var fizzBuzzData = FizzBuzzData() + + public init(targetNumber: Int) { + self.targetNumber = targetNumber + } + + open func play() { + let threads: [FizzBuzzThread] = [ + FizzBuzzThread(id: 1, targetNumber: targetNumber, validator: { $0 % 3 == 0 && $0 % 5 == 0 }, fizzBuzzData: fizzBuzzData, output: { _ in "FizzBuzz" }), + FizzBuzzThread(id: 2, targetNumber: targetNumber, validator: { $0 % 3 != 0 && $0 % 5 == 0 }, fizzBuzzData: fizzBuzzData, output: { _ in "Buzz" }), + FizzBuzzThread(id: 3, targetNumber: targetNumber, validator: { $0 % 3 == 0 && $0 % 5 != 0 }, fizzBuzzData: fizzBuzzData, output: { _ in "Fizz"}), + FizzBuzzThread(id: 4, targetNumber: targetNumber, validator: { $0 % 3 != 0 && $0 % 5 != 0 }, fizzBuzzData: fizzBuzzData, output: { number in "\(number)"}) + ] + + threads.forEach { thread in + DispatchQueue.global().async { + thread.play() + } + } + } +} + +open class FizzBuzzThread: Thread { + let id: Int + let targetNumber: Int + let validator: (Int) -> Bool + let fizzBuzzData: FizzBuzzData + let output: (Int) -> String + + public init(id: Int, targetNumber: Int, validator: @escaping (Int) -> Bool, fizzBuzzData: FizzBuzzData, output: @escaping (Int) -> String) { + self.id = id + self.targetNumber = targetNumber + self.validator = validator + self.fizzBuzzData = fizzBuzzData + self.output = output + } + + open func play() { + while fizzBuzzData.currentNumber <= targetNumber { + if validator(fizzBuzzData.currentNumber) { + //Sychronized access to shared data class + fizzBuzzData.semaphore.wait() + fizzBuzzData.result.append(output(fizzBuzzData.currentNumber)) + fizzBuzzData.currentNumber += 1 + fizzBuzzData.semaphore.signal() + } + } + } +} + +open class FizzBuzzData { + var result: [String] = [] + var currentNumber = 1 + + let semaphore = DispatchSemaphore(value: 1) +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/CountOfTwo.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/CountOfTwo.swift new file mode 100644 index 0000000..c8da8dd --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/CountOfTwo.swift @@ -0,0 +1,33 @@ +// +// CountOfTwo.swift +// +// +// Created by Stefan Jaindl on 05.12.20. +// + +import Foundation + +//Calculates all 2's in the range of 0 ... number (counting e.g. 22 2 times) +open class CountOfTwos { + public init() { } + + open func countOfTwos(number: Int) -> Int { + return countOfTwos(number: number, divisor: 10, multiplicator: 1) + } + + private func countOfTwos(number: Int, divisor: Int, multiplicator: Int) -> Int { + if number < multiplicator { + return 0 //Base Case + } + + var twosCountAtLevel = (number / divisor) * multiplicator + let remainder = number % divisor + if remainder >= 3 * multiplicator { + twosCountAtLevel += multiplicator + } else if remainder >= 2 * multiplicator { + twosCountAtLevel += (remainder % multiplicator) + 1 + } + + return twosCountAtLevel + countOfTwos(number: number, divisor: divisor * 10, multiplicator: multiplicator * 10) + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/DeckShuffler.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/DeckShuffler.swift new file mode 100644 index 0000000..ede2a6d --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/DeckShuffler.swift @@ -0,0 +1,118 @@ +// +// DeckShuffler.swift +// +// +// Created by Stefan Jaindl on 04.12.20. +// + +import Foundation + +open class DeckShuffler { + public init() { } + + open func shuffle(deck: Deck) -> Deck { + var cards = deck.cards + for index in 0 ..< cards.count { + let newIndex = Int.random(in: 0 ..< index + 1) + let temp = cards[index] + cards[index] = cards[newIndex] + cards[newIndex] = temp + } + + return Deck(cards: cards) + } +} + +public struct Deck { + let cards: [Card] + + static func allCards() -> [Card] { + return [ + Card(color: .cross, value: .two), + Card(color: .cross, value: .three), + Card(color: .cross, value: .four), + Card(color: .cross, value: .five), + Card(color: .cross, value: .six), + Card(color: .cross, value: .seven), + Card(color: .cross, value: .eight), + Card(color: .cross, value: .nine), + Card(color: .cross, value: .ten), + Card(color: .cross, value: .jack), + Card(color: .cross, value: .queen), + Card(color: .cross, value: .king), + Card(color: .cross, value: .ace), + Card(color: .hearts, value: .two), + Card(color: .hearts, value: .three), + Card(color: .hearts, value: .four), + Card(color: .hearts, value: .five), + Card(color: .hearts, value: .six), + Card(color: .hearts, value: .seven), + Card(color: .hearts, value: .eight), + Card(color: .hearts, value: .nine), + Card(color: .hearts, value: .ten), + Card(color: .hearts, value: .jack), + Card(color: .hearts, value: .queen), + Card(color: .hearts, value: .king), + Card(color: .hearts, value: .ace), + Card(color: .diamond, value: .two), + Card(color: .diamond, value: .three), + Card(color: .diamond, value: .four), + Card(color: .diamond, value: .five), + Card(color: .diamond, value: .six), + Card(color: .diamond, value: .seven), + Card(color: .diamond, value: .eight), + Card(color: .diamond, value: .nine), + Card(color: .diamond, value: .ten), + Card(color: .diamond, value: .jack), + Card(color: .diamond, value: .queen), + Card(color: .diamond, value: .king), + Card(color: .diamond, value: .ace), + Card(color: .squades, value: .two), + Card(color: .squades, value: .three), + Card(color: .squades, value: .four), + Card(color: .squades, value: .five), + Card(color: .squades, value: .six), + Card(color: .squades, value: .seven), + Card(color: .squades, value: .eight), + Card(color: .squades, value: .nine), + Card(color: .squades, value: .ten), + Card(color: .squades, value: .jack), + Card(color: .squades, value: .queen), + Card(color: .squades, value: .king), + Card(color: .squades, value: .ace) + ] + } +} + +public struct Card: Hashable { + let color: CardColor + let value: CardValue + + public func hash(into hasher: inout Hasher) { + hasher.combine(color) + hasher.combine(value) + } +} + +public enum CardColor { + case hearts + case squades + case diamond + case cross +} + +public enum CardValue: Int { + case two = 2 + case three + case four + case five + case six + case seven + case eight + case nine + case ten + case jack + case queen + case king + case ace +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/HistogramVolume.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/HistogramVolume.swift new file mode 100644 index 0000000..13df7a4 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/HistogramVolume.swift @@ -0,0 +1,114 @@ +// +// HistogramVolume.swift +// +// +// Created by Stefan Jaindl on 18.12.20. +// + +import Foundation + +open class HistogramVolume { + public init() { } + + open func volume(of histogram: [Int]) -> Int { + guard !histogram.isEmpty, let highestBarIndex = highest(of: histogram, from: 0, to: histogram.count - 1) else { + return 0 + } + + let lookup = createHistogramData(histogram: histogram) + return volume(of: histogram, from: 0, to: histogram.count - 1, lookup: lookup, highestBarIndex: highestBarIndex) + } + + private func volume(of histogram: [Int], from: Int, to: Int, lookup: [HistogramData], highestBarIndex: Int) -> Int { + guard !histogram.isEmpty, to > from else { + return 0 + } + + var leftVolume = 0 + if highestBarIndex > 0 { + let highestBarLeftIndex = lookup[highestBarIndex - 1].nextHighestLeftIndex + if highestBarLeftIndex != highestBarIndex, highestBarLeftIndex >= from { + let maxHeight = histogram[highestBarLeftIndex] + for index in highestBarLeftIndex + 1 ..< highestBarIndex { + leftVolume += maxHeight - histogram[index] + } + + leftVolume += volume(of: histogram, from: from, to: highestBarLeftIndex, lookup: lookup, highestBarIndex: highestBarLeftIndex) + } + } + + var rightVolume = 0 + if highestBarIndex < histogram.count - 1 { + let highestBarRightIndex = lookup[highestBarIndex + 1].nextHighestRightIndex + if highestBarRightIndex != highestBarIndex, highestBarRightIndex <= to { + let maxHeight = histogram[highestBarRightIndex] + for index in highestBarIndex + 1 ..< highestBarRightIndex { + rightVolume += maxHeight - histogram[index] + } + + rightVolume += volume(of: histogram, from: highestBarRightIndex, to: to, lookup: lookup, highestBarIndex: highestBarRightIndex) + } + } + + return leftVolume + rightVolume + } + + private func highest(of histogram: [Int], from: Int, to: Int) -> Int? { + guard to > from else { + return nil + } + + var max = 0 + var maxIndex = from + for index in from ... to { + let level = histogram[index] + if level > max { + max = level + maxIndex = index + } + } + + return maxIndex + } + + //Lookup table bringing runtime down from O(n^2) to O(n), needing O(n) space + private func createHistogramData(histogram: [Int]) -> [HistogramData] { + guard !histogram.isEmpty else { + return [] + } + + var histogramDataArray: [HistogramData] = [] + + //Save tallest left indices on right sweep (array lookup) + var currentHighestIndexOnLeft = 0 + for (index, value) in histogram.enumerated() { + currentHighestIndexOnLeft = value > histogram[currentHighestIndexOnLeft] ? index : currentHighestIndexOnLeft + histogramDataArray.append(HistogramData(index: index, nextHighestLeftIndex: currentHighestIndexOnLeft)) + } + + //Save tallest right indices on left sweep (array lookup) + var index = histogram.count - 1 + var currentHighestIndexOnRight = index + while index >= 0 { + currentHighestIndexOnRight = histogram[index] > histogram[currentHighestIndexOnRight] ? index : currentHighestIndexOnRight + let histogramData = histogramDataArray[index] + histogramData.nextHighestRightIndex = currentHighestIndexOnRight + + index -= 1 + } + + return histogramDataArray + } +} + +private class HistogramData { + let index: Int + var nextHighestLeftIndex: Int + var nextHighestRightIndex: Int + + init(index: Int, nextHighestLeftIndex: Int? = nil, nextHighestRightIndex: Int? = nil) { + self.index = index + self.nextHighestLeftIndex = nextHighestLeftIndex ?? index + self.nextHighestRightIndex = nextHighestRightIndex ?? index + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/KthMultiple.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/KthMultiple.swift new file mode 100644 index 0000000..06701cb --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/KthMultiple.swift @@ -0,0 +1,162 @@ +// +// KthMultiple.swift +// +// +// Created by Stefan Jaindl on 07.12.20. +// + +import Foundation + +open class KthMultiple { + let multiples: [Int] + + public init(multiples: [Int]) { + self.multiples = multiples + } + + //Returns kth multiple that can only be divided by 3, 5, 7 or itself + //Starting with: 1, 3, 5, 7, 9, 15, 21,... + open func allToKthMultipleComposedOfOnlyLowestOddPrimes(k: Int) -> [Int] { + guard k > 0 else { + return [] + } + + var multiples: [Int] = [] + + for currentNumber in 0 ... k { + if isValidPrimeNumber(k: currentNumber) { + multiples.append(currentNumber) + } + } + + return multiples + } + + private func isValidPrimeNumber(k: Int) -> Bool { + guard k % 2 != 0 else { + //Even numbers are always divisible by 2, we can rule those numbers out + return false + } + + var remainder = k + + for primeFactor in multiples { + while remainder % primeFactor == 0 { + remainder /= primeFactor + } + } + + if multiples.contains(remainder) || remainder == 1 { + return true + } + + return false + } + + open func allToKthMultipleComposedOfOnlyLowestOddPrimesFast(k: Int) -> [Int] { + guard k > 0 else { + return [] + } + + var computedPrimes = Set() + + addNumberRecursive(product: 1, computed: &computedPrimes, target: k) + + return computedPrimes.sorted() + } + + private func addNumberRecursive(product: Int, computed: inout Set, target: Int) { + if product == target { + computed.insert(target) + } + + if product > target { + return + } + + if computed.contains(product) { + return + } else { + computed.insert(product) + } + + for multiple in multiples { + if product <= target / multiple { //Avoid arithmetic overflow + addNumberRecursive(product: product * multiple, computed: &computed, target: target) + } + } + } + + open func kthMultipleComposedOfOnlyLowestOddPrimes(k: Int) throws -> Int? { + guard k > 0 else { + return nil + } + + let multiples = [3, 5, 7] + + if k <= 4 { + let muliplesWithOne = [1] + multiples + return muliplesWithOne[k - 1] + } + + let deque = DoubleLinkedList() + var count = 1 + + var computed = Set() + enqueue(val: 1, deque: deque) + + while let nextMultiple = try deque.removeFirst() { + for multiple in multiples { + if count == k { + return try deque.removeLast()?.val + } + + let value = nextMultiple.val * multiple + if !computed.contains(value) { + insert(value: value, computed: &computed, deque: deque) + count += 1 + } + } + } + + return nil + } + + private func insert(value: Int, computed: inout Set, deque: DoubleLinkedList) { + if !computed.contains(value) { + enqueue(val: value, deque: deque) + } + + computed.insert(value) + } + + private func enqueue(val: Int, deque: DoubleLinkedList) { + var current = deque.tail + let node = Node(val: val) + if current == nil { + deque.add(node: node) + return + } else if let current = current, current.val < val { + deque.add(node: node) + return + } + + while let cur = current { + if cur.val > val { + if cur == deque.head { + node.next = cur + cur.prev = node + deque.head = node + return + } else { + current = cur.prev + } + } else { + cur.next?.prev = node + node.prev = cur + cur.next = node + return + } + } + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/LangthonsCell.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/LangthonsCell.swift new file mode 100644 index 0000000..62df91d --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/LangthonsCell.swift @@ -0,0 +1,145 @@ +// +// LangthonsCell.swift +// +// +// Created by Stefan Jaindl on 01.12.20. +// + +import Foundation + +open class Langthons { + public init() { + grid[antPosition] = Cell(color: randomColor(), coordinate: GridCoordinate(x: 0, y: 0)) + } + + static let initialDirection = Direction.right + var ant = Ant(direction: initialDirection) + var antPosition = GridCoordinate(x: 0, y: 0) + var grid: [GridCoordinate: Cell] = [:] + + func performMoves(kMoves: Int) -> [Cell] { + var movesLeft = kMoves + + while movesLeft > 0 { + performSingleMove() + movesLeft -= 1 + } + + return buildGrid() + } + + private func performSingleMove() { + var currentCell = grid[antPosition]! + + currentCell.flipColor() + + switch currentCell.color { + case .white: + ant.go(to: .right) + case .black: + ant.go(to: .left) + } + + let nextCoordinate = getNextCoordinate() + let nextCell = grid[nextCoordinate] ?? Cell(color: randomColor(), coordinate: nextCoordinate) + + antPosition = nextCoordinate + grid[nextCoordinate] = nextCell + } + + private func getNextCoordinate() -> GridCoordinate { + let nextCoordinate: GridCoordinate + + switch ant.direction { + case .bottom: + nextCoordinate = GridCoordinate(x: antPosition.x - 1, y: antPosition.y) + case .top: + nextCoordinate = GridCoordinate(x: antPosition.x + 1, y: antPosition.y) + case .left: + nextCoordinate = GridCoordinate(x: antPosition.x, y: antPosition.y - 1) + case .right: + nextCoordinate = GridCoordinate(x: antPosition.x, y: antPosition.y + 1) + } + + return nextCoordinate + } + + private func randomColor() -> Color { + let random = Int.random(in: 0 ..< 2) + return Color(rawValue: random)! + } + + private func buildGrid() -> [Cell] { + let cellsToSort = grid.map { $0.value } + + let sorted = cellsToSort.sorted(by: { (lhs, rhs) in + lhs.coordinate > rhs.coordinate + }) + + return sorted + } +} + +extension Langthons { + public enum Direction: Int { + case right + case bottom + case left + case top + } + + public struct GridCoordinate: Hashable, Comparable { + let x: Int + let y: Int + + public static func < (lhs: GridCoordinate, rhs: GridCoordinate) -> Bool { + if lhs.y != rhs.y { + return lhs.y < rhs.y + } + return lhs.x < rhs.x + } + + public static func == (lhs: GridCoordinate, rhs: GridCoordinate) -> Bool { + return lhs.x == rhs.x && lhs.y == rhs.y + } + } + + public struct Ant { + var direction: Direction + + public mutating func go(to direction: Direction) { + switch direction { + case .left: + switch self.direction { + case .right: + self.direction = .top + default: + self.direction = Direction(rawValue: self.direction.rawValue - 1)! + } + case .right: + switch self.direction { + case .top: + self.direction = .right + default: + self.direction = Direction(rawValue: self.direction.rawValue + 1)! + } + default: + debugPrint("Unsupported ant move") + } + } + } + + public enum Color: Int { + case black + case white + } + + public struct Cell { + var color: Color + let coordinate: GridCoordinate + + public mutating func flipColor() { + color = color == Color.black ? Color.white : Color.black + } + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/MasterMindChecker.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/MasterMindChecker.swift new file mode 100644 index 0000000..aeecf84 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/MasterMindChecker.swift @@ -0,0 +1,75 @@ +// +// MasterMindChecker.swift +// +// +// Created by Stefan Jaindl on 27.11.20. +// + +import Foundation + +public enum MasterMindColor { + case red + case yellow + case blue + case green +} + +open class MasterMindChecker { + public init() { } + + open func check(guess: [MasterMindColor], solution: [MasterMindColor]) throws -> MasterMindCheckResult { + guard guess.count == solution.count, !guess.isEmpty else { + throw NSError(domain: "MasterMind: Invalid guess or solution", code: 0, userInfo: nil) + } + + var guessMisses: [MasterMindColor: Int] = [:] + var solutionMisses: [MasterMindColor: Int] = [:] + + //1st pass: Check hits + let hits = checkHits(guess: guess, solution: solution, guessMisses: &guessMisses, solutionMisses: &solutionMisses) + + //2nd pass: Check pseudohits + let pseudoHits = checkPseudoHits(guessMisses: guessMisses, solutionMisses: solutionMisses) + + return MasterMindCheckResult(hits: hits, pseudohits: pseudoHits) + } + + private func checkHits(guess: [MasterMindColor], solution: [MasterMindColor], guessMisses: inout [MasterMindColor: Int], solutionMisses: inout [MasterMindColor: Int]) -> Int { + var hits = 0 + for index in 0 ..< guess.count { + if guess[index] == solution[index] { + hits += 1 + } else { + createOrIncrement(hashTable: &guessMisses, for: guess[index]) + createOrIncrement(hashTable: &solutionMisses, for: solution[index]) + } + } + + return hits + } + + private func checkPseudoHits(guessMisses: [MasterMindColor: Int], solutionMisses: [MasterMindColor: Int]) -> Int { + var pseudoHits = 0 + + guessMisses.forEach { miss in + if let pseudoSolution = solutionMisses[miss.key] { + pseudoHits += min(miss.value, pseudoSolution) + } + } + + return pseudoHits + } + + private func createOrIncrement(hashTable: inout [MasterMindColor: Int], for key: MasterMindColor) { + if let value = hashTable[key] { + hashTable[key] = value + 1 + } else { + hashTable[key] = 1 + } + } +} + +public struct MasterMindCheckResult: Equatable { + var hits: Int + var pseudohits: Int +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/Rand7.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/Rand7.swift new file mode 100644 index 0000000..0af9c7d --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/Rand7.swift @@ -0,0 +1,26 @@ +// +// Rand7.swift +// +// +// Created by Stefan Jaindl on 01.12.20. +// + +import Foundation + +open class Rand7 { + open func random() -> Int { + var random = Int.max + + while random >= 21 { + random = rand5() * 5 + rand5() + } + + return random % 7 + } + + private func rand5() -> Int { + return Int.random(in: 0 ..< 5) + } +} + + diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/RandomSet.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/RandomSet.swift new file mode 100644 index 0000000..fdd91ca --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/RandomSet.swift @@ -0,0 +1,35 @@ +// +// RandomSet.swift +// +// +// Created by Stefan Jaindl on 04.12.20. +// + +import Foundation + +open class RandomSet { + public init() { } + + open func randomSet(of size: Int, from array: [T]) -> [T] { + guard size > 0, !array.isEmpty, size < array.count else { + return [] + } + + var randomSet: [T] = [] + + for (index, element) in array.enumerated() { + if index < size { + //fill up array + randomSet.append(element) + } else { + let random = Int.random(in: 0 ..< index) + if random < size { + //replace element + randomSet[random] = element + } + } + } + + return randomSet + } +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/SquareConnector.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/SquareConnector.swift new file mode 100644 index 0000000..04b209b --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/SquareConnector.swift @@ -0,0 +1,100 @@ +// +// SquareConnector.swift +// +// +// Created by Stefan Jaindl on 24.11.20. +// + +import Foundation + +//Draws a line connecting 2 square centers starting the bottom edge and ending at top edge of second square +open class SquareConnector { + public init() { } + + open func connect(firstSquare: Square, secondSquare: Square) -> Line { + let centerOfFirstSquare = center(of: firstSquare) + let centerOfSecondSquare = center(of: secondSquare) + + var relevantEdges: [CGPoint] = [] + relevantEdges.append(expand(centerOfFirstSquare: centerOfSecondSquare, centerOfSecondSquare: centerOfFirstSquare, radiusToEdge: firstSquare.radius)) + relevantEdges.append(expand(centerOfFirstSquare: centerOfSecondSquare, centerOfSecondSquare: centerOfFirstSquare, radiusToEdge: -firstSquare.radius)) + relevantEdges.append(expand(centerOfFirstSquare: centerOfFirstSquare, centerOfSecondSquare: centerOfSecondSquare, radiusToEdge: secondSquare.radius)) + relevantEdges.append(expand(centerOfFirstSquare: centerOfFirstSquare, centerOfSecondSquare: centerOfSecondSquare, radiusToEdge: -secondSquare.radius)) + + let edges = outerEdges(edges: relevantEdges) + + return Line(startPoint: edges.bottomEdge, endPoint: edges.topEdge) + } + + private func center(of square: Square) -> CGPoint { + return CGPoint(x: square.topLeftCorner.x + (square.bottomRightCorner.x - square.topLeftCorner.x) / 2, y: square.bottomRightCorner.y + (square.topLeftCorner.y - square.bottomRightCorner.y) / 2) + } + + private func expand(centerOfFirstSquare: CGPoint, centerOfSecondSquare: CGPoint, radiusToEdge: CGFloat) -> CGPoint { + let xDirection: CGFloat = centerOfFirstSquare.x < centerOfSecondSquare.x ? -1.0: 1.0 + let yDirection: CGFloat = centerOfFirstSquare.y < centerOfSecondSquare.y ? -1.0 : 1.0 + + if centerOfFirstSquare.x == centerOfSecondSquare.x { + //Slope calculation would cause division by zero error. + //In this special case x axis won't change and y axis will change by half of radius to edge + return CGPoint(x: centerOfSecondSquare.x, y: centerOfSecondSquare.y + yDirection * radiusToEdge) + } + + let slope = (centerOfFirstSquare.y - centerOfSecondSquare.y) / (centerOfFirstSquare.x - centerOfSecondSquare.x) + + if slope == 1 { + //diagonal slope: x and y edges diagonally reached by line + return CGPoint(x: centerOfFirstSquare.x + xDirection * radiusToEdge, y: centerOfFirstSquare.y + yDirection * radiusToEdge) + } else if slope < 1 { + //flat slope: x axis reached, y partially depending on slope + let x = centerOfFirstSquare.x + xDirection * radiusToEdge + let y = centerOfFirstSquare.y + (x - centerOfFirstSquare.x) * slope + return CGPoint(x: x, y: y) + } else { + //big slope: y axis reached, x partially depending on slope + let y = centerOfFirstSquare.y + yDirection * radiusToEdge + let x = centerOfFirstSquare.x + (y - centerOfFirstSquare.y) / slope + return CGPoint(x: x, y: y) + } + } + + private func outerEdges(edges: [CGPoint]) -> (topEdge: CGPoint, bottomEdge: CGPoint) { + var topEdge = CGPoint(x: CGFloat.leastNonzeroMagnitude, y: CGFloat.leastNonzeroMagnitude) + var bottomEdge = CGPoint(x: CGFloat.greatestFiniteMagnitude, y: CGFloat.greatestFiniteMagnitude) + + edges.forEach { edge in + if edge.y > topEdge.y { + topEdge = edge + } else if edge.y == topEdge.y, edge.x > topEdge.x { + topEdge = edge + } + + if edge.y < bottomEdge.y { + bottomEdge = edge + } else if edge.y == bottomEdge.y, edge.x < bottomEdge.x { + bottomEdge = edge + } + } + + return (topEdge: topEdge, bottomEdge: bottomEdge) + } +} + +public struct Square { + let topLeftCorner: CGPoint + let bottomRightCorner: CGPoint + + init(topLeftCorner: CGPoint, length: CGFloat) { + self.topLeftCorner = topLeftCorner + self.bottomRightCorner = CGPoint(x: topLeftCorner.x + length, y: topLeftCorner.y - length) + } + + var radius: CGFloat { + return (topLeftCorner.y - bottomRightCorner.y) / 2 + } +} + +public struct Line: Equatable { + let startPoint: CGPoint + let endPoint: CGPoint +} diff --git a/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/WordDistance.swift b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/WordDistance.swift new file mode 100644 index 0000000..2e4e9d7 --- /dev/null +++ b/SPM/Sources/DataStructuresAlgorithms/SpecificAlgorithms/Various/WordDistance.swift @@ -0,0 +1,48 @@ +// +// WordDistance.swift +// +// +// Created by Stefan Jaindl on 09.12.20. +// + +import Foundation + +open class WordDistance { + + var indexDict: [String: [Int]] = [:] + + public init(words: [String]) { + preprocess(words: words) + } + + private func preprocess(words: [String]) { + for (index, word) in words.enumerated() { + if var indices = indexDict[word] { + indices.append(index) + indexDict[word] = indices + } else { + indexDict[word] = [index] + } + } + } + + open func distance(first: String, second: String) -> Int? { + guard let firstIndices = indexDict[first], let secondIndices = indexDict[second] else { + return nil + } + + var firstIndex = 0 + var secondIndex = 0 + var minDistance = Int.max + while firstIndex < firstIndices.count, secondIndex < secondIndices.count { + minDistance = min(minDistance, abs(firstIndices[firstIndex] - secondIndices[secondIndex])) + if firstIndices[firstIndex] < secondIndices[secondIndex] { + firstIndex += 1 + } else { + secondIndex += 1 + } + } + + return minDistance + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/BellmanFordTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/BellmanFordTests.swift new file mode 100644 index 0000000..ebc5bac --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/BellmanFordTests.swift @@ -0,0 +1,42 @@ +// +// BellmanFord.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 09.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class BellmanFordTests: XCTestCase { + + open func testBellmanFord() { + let graph = setupTestGraph() + let bellmanFord = BellmanFord(graph: graph) + + XCTAssertEqual(try? bellmanFord.findShortestPath(source: graph.vertices[0], target: graph.vertices[0]), 0) + XCTAssertEqual(try? bellmanFord.findShortestPath(source: graph.vertices[0], target: graph.vertices[1]), -1) + XCTAssertEqual(try? bellmanFord.findShortestPath(source: graph.vertices[0], target: graph.vertices[2]), 2) + XCTAssertEqual(try? bellmanFord.findShortestPath(source: graph.vertices[0], target: graph.vertices[3]), -2) + XCTAssertEqual(try? bellmanFord.findShortestPath(source: graph.vertices[0], target: graph.vertices[4]), 1) + } + + //https://www.geeksforgeeks.org/bellman-ford-algorithm-dp-23/?ref=lbp + private func setupTestGraph() -> WeightedDirectedGraph { + let vertices: [Vertice] = [Vertice(id: 0), Vertice(id: 1), Vertice(id: 2), + Vertice(id: 3), Vertice(id: 4)] + let graph = WeightedDirectedGraph(vertices: vertices) + + graph.addEdge(v1: vertices[0], v2: vertices[1], weight: -1) + graph.addEdge(v1: vertices[0], v2: vertices[2], weight: 4) + graph.addEdge(v1: vertices[1], v2: vertices[2], weight: 3) + graph.addEdge(v1: vertices[1], v2: vertices[3], weight: 2) + graph.addEdge(v1: vertices[1], v2: vertices[4], weight: 2) + graph.addEdge(v1: vertices[3], v2: vertices[1], weight: 1) + graph.addEdge(v1: vertices[3], v2: vertices[2], weight: 5) + graph.addEdge(v1: vertices[4], v2: vertices[3], weight: -3) + + return graph + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/BipartiteGraphCheckerTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/BipartiteGraphCheckerTests.swift new file mode 100644 index 0000000..c0f0f54 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/BipartiteGraphCheckerTests.swift @@ -0,0 +1,53 @@ +// +// BipartiteGraph.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 23.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class BipartiteGraphCheckerTests: XCTestCase { + + open func testBipartiteGraph() { + let graph = setupTestGraph() + let bipartite = BipartiteGraphChecker(graph: graph) + let colors = try? bipartite.isBipartite() + + XCTAssertNotNil(colors) + for (index, color) in colors!.enumerated() { + if index % 2 == 0 { + XCTAssertEqual(color, .red) + } else { + XCTAssertEqual(color, .black) + } + } + } + + //https://www.geeksforgeeks.org/bipartite-graph + private func setupTestGraph() -> UndirectedGraph { + let vertices: [Vertice] = [Vertice(id: 0), Vertice(id: 1), Vertice(id: 2), Vertice(id: 3), + Vertice(id: 4), Vertice(id: 5), Vertice(id: 6), Vertice(id: 7), + Vertice(id: 8), Vertice(id: 9), Vertice(id: 10), Vertice(id: 11)] + let graph = UndirectedGraph(vertices: vertices) + + graph.addEdge(v1: vertices[0], v2: vertices[1]) + graph.addEdge(v1: vertices[1], v2: vertices[2]) + graph.addEdge(v1: vertices[2], v2: vertices[3]) + graph.addEdge(v1: vertices[3], v2: vertices[4]) + graph.addEdge(v1: vertices[4], v2: vertices[5]) + graph.addEdge(v1: vertices[5], v2: vertices[0]) + + //2 connected components + graph.addEdge(v1: vertices[6], v2: vertices[7]) + graph.addEdge(v1: vertices[7], v2: vertices[8]) + graph.addEdge(v1: vertices[8], v2: vertices[9]) + graph.addEdge(v1: vertices[9], v2: vertices[10]) + graph.addEdge(v1: vertices[10], v2: vertices[11]) + graph.addEdge(v1: vertices[11], v2: vertices[6]) + + return graph + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/DjikstraTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/DjikstraTests.swift new file mode 100644 index 0000000..1cd1f45 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/DjikstraTests.swift @@ -0,0 +1,54 @@ +// +// Djikstra.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 02.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class DjikstraTests: XCTestCase { + + open func testDjikstra() throws { + let graph = setupTestGraph() + let djikstra = Djikstra(graph: graph) + let distance = try djikstra.djikstra(from: graph.vertices[0], to: graph.vertices[4]) + + XCTAssertNotNil(distance) + XCTAssertEqual(distance!, 21) + + let path = djikstra.path(to: graph.vertices[4]) + let pathIds = path.compactMap { + return $0.id + } + + XCTAssertEqual(pathIds, [0, 7, 6, 5, 4]) + } + + //https://www.geeksforgeeks.org/dijkstras-shortest-path-algorithm-greedy-algo-7/ + private func setupTestGraph() -> WeightedUndirectedGraph { + let vertices: [Vertice] = [Vertice(id: 0), Vertice(id: 1), Vertice(id: 2), + Vertice(id: 3), Vertice(id: 4), Vertice(id: 5), + Vertice(id: 6), Vertice(id: 7), Vertice(id: 8)] + let graph = WeightedUndirectedGraph(vertices: vertices) + + graph.addEdge(v1: vertices[0], v2: vertices[1], weight: 4) + graph.addEdge(v1: vertices[0], v2: vertices[7], weight: 8) + graph.addEdge(v1: vertices[1], v2: vertices[2], weight: 8) + graph.addEdge(v1: vertices[1], v2: vertices[7], weight: 11) + graph.addEdge(v1: vertices[2], v2: vertices[3], weight: 7) + graph.addEdge(v1: vertices[2], v2: vertices[5], weight: 4) + graph.addEdge(v1: vertices[2], v2: vertices[8], weight: 2) + graph.addEdge(v1: vertices[3], v2: vertices[4], weight: 9) + graph.addEdge(v1: vertices[3], v2: vertices[5], weight: 14) + graph.addEdge(v1: vertices[4], v2: vertices[5], weight: 10) + graph.addEdge(v1: vertices[5], v2: vertices[6], weight: 2) + graph.addEdge(v1: vertices[6], v2: vertices[7], weight: 1) + graph.addEdge(v1: vertices[6], v2: vertices[8], weight: 6) + graph.addEdge(v1: vertices[7], v2: vertices[8], weight: 7) + + return graph + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/FloydWarshallTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/FloydWarshallTests.swift new file mode 100644 index 0000000..2896eee --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/FloydWarshallTests.swift @@ -0,0 +1,38 @@ +// +// FloydWarshall.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 11.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class FloydWarshallTests: XCTestCase { + + open func testFloydWarshall() throws { + let graph = setupTestGraph() + let floydWarshall = FloydWarshall(graph: graph) + try floydWarshall.findShortestPaths() + + XCTAssertEqual(floydWarshall.graph.adjMatrix[0][0], 0) + XCTAssertEqual(floydWarshall.graph.adjMatrix[0][3], 0) + XCTAssertEqual(floydWarshall.graph.adjMatrix[1][3], 4) + XCTAssertEqual(floydWarshall.graph.adjMatrix[2][3], 2) + } + + //https://www.techiedelight.com/pairs-shortest-paths-floyd-warshall-algorithm/ + private func setupTestGraph() -> WeightedDirectedGraphWithAdjMatrix { + let vertices: [Vertice] = [Vertice(id: 0), Vertice(id: 1), Vertice(id: 2), Vertice(id: 3)] + let graph = WeightedDirectedGraphWithAdjMatrix(vertices: vertices) + + graph.addEdge(v1: vertices[0], v2: vertices[2], weight: -2) + graph.addEdge(v1: vertices[1], v2: vertices[0], weight: 4) + graph.addEdge(v1: vertices[1], v2: vertices[2], weight: 3) + graph.addEdge(v1: vertices[2], v2: vertices[3], weight: 2) + graph.addEdge(v1: vertices[3], v2: vertices[1], weight: -1) + + return graph + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/GraphSearchTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/GraphSearchTests.swift new file mode 100644 index 0000000..b9d2172 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/GraphSearchTests.swift @@ -0,0 +1,57 @@ +// +// GraphSearch.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 27.03.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class GraphSearchTests: XCTestCase { + + open func testBFS() { + let graph = setupTestGraph() + + let gsBfs = GraphSearch(graph: graph, start: graph.vertices[0]) + gsBfs.bfs() + } + + open func testDFS() { + let graph = setupTestGraph() + + let gsDfs = GraphSearch(graph: graph, start: graph.vertices[0]) + gsDfs.dfs() + } + + open func testDFSIterative() { + let graph = setupTestGraph() + + let gsDfsIt = GraphSearch(graph: graph, start: graph.vertices[0]) + gsDfsIt.dfs() + } + + private func setupTestGraph() -> UndirectedGraph { + let vertices: [Vertice] = [Vertice(id: 0), Vertice(id: 1), Vertice(id: 2), + Vertice(id: 3), Vertice(id: 4), Vertice(id: 5), + Vertice(id: 6), Vertice(id: 7), Vertice(id: 8)] + let graph = UndirectedGraph(vertices: vertices) + + graph.addEdge(v1: vertices[0], v2: vertices[1]) + graph.addEdge(v1: vertices[0], v2: vertices[2]) + graph.addEdge(v1: vertices[0], v2: vertices[3]) + + graph.addEdge(v1: vertices[1], v2: vertices[2]) + graph.addEdge(v1: vertices[1], v2: vertices[4]) + graph.addEdge(v1: vertices[2], v2: vertices[5]) + graph.addEdge(v1: vertices[3], v2: vertices[8]) + graph.addEdge(v1: vertices[4], v2: vertices[5]) + graph.addEdge(v1: vertices[5], v2: vertices[6]) + graph.addEdge(v1: vertices[6], v2: vertices[7]) + graph.addEdge(v1: vertices[7], v2: vertices[8]) + graph.addEdge(v1: vertices[8], v2: vertices[6]) + + return graph + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/KnapsackTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/KnapsackTests.swift new file mode 100644 index 0000000..96f1391 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/KnapsackTests.swift @@ -0,0 +1,34 @@ +// +// Knapsack.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 26.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +//https://www.geeksforgeeks.org/0-1-knapsack-problem-dp-10/ +open class KnapsackTests: XCTestCase { + + open func testKnapsack() { + let knapsack = Knapsack() + let weights = [1, 1, 1] + let values = [20, 10, 30] + let knapsackMaxValue = try! knapsack.knapsack(weights: weights, values: values, maxWeight: 2) + let knapsackDynamicMaxValue = try! knapsack.knapsackDynamicProgramming(weights: weights, values: values, maxWeight: 2) + + XCTAssertEqual(knapsackMaxValue, 50) + XCTAssertEqual(knapsackDynamicMaxValue, 50) + + let knapsack2 = Knapsack() + let weights2 = [10, 20, 30] + let values2 = [60, 100, 120] + let knapsack2MaxValue = try! knapsack2.knapsack(weights: weights2, values: values2, maxWeight: 50) + let knapsack2DynamicMaxValue = try! knapsack2.knapsackDynamicProgramming(weights: weights2, values: values2, maxWeight: 50) + + XCTAssertEqual(knapsack2MaxValue, 220) + XCTAssertEqual(knapsack2DynamicMaxValue, 220) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/LRUCacheTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/LRUCacheTests.swift new file mode 100644 index 0000000..0fe155e --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/LRUCacheTests.swift @@ -0,0 +1,44 @@ +// +// LRUCacheTests.swift +// +// +// Created by Stefan Jaindl on 01.12.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class LRUCacheTests: XCTestCase { + + open func testCache() { + + let cache = LRUCache(cacheSize: 4) + + cache.insert(1) + cache.insert(2) + cache.insert(3) + cache.insert(4) + + XCTAssertNotNil(cache.get(1)) + XCTAssertNotNil(cache.get(2)) + XCTAssertNotNil(cache.get(3)) + XCTAssertNotNil(cache.get(4)) + XCTAssertNil(cache.get(5)) + + cache.insert(5) + XCTAssertNil(cache.get(1)) + XCTAssertNotNil(cache.get(2)) + XCTAssertNotNil(cache.get(3)) + XCTAssertNotNil(cache.get(4)) + XCTAssertNotNil(cache.get(5)) + + cache.insert(1) + XCTAssertNotNil(cache.get(1)) + XCTAssertNil(cache.get(2)) + XCTAssertNotNil(cache.get(3)) + XCTAssertNotNil(cache.get(4)) + XCTAssertNotNil(cache.get(5)) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/MapReduceTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/MapReduceTests.swift new file mode 100644 index 0000000..ff25b10 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/MapReduceTests.swift @@ -0,0 +1,50 @@ +// +// MapReduce.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 08.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class MapReduceTests: XCTestCase { + + open func testMapReduceMock() { + let words = ["Test", "document", "for", "map", "reduce", "Lorem", "Ipsum", "Dolores", "map", "map", "reduce"] + let document = Document(words: words) + let mapReduce = MapReduceWordCount() + + mapReduce.map(document: document) + mapReduce.shuffleAndSort() + + if mapReduce.partialResults.count > 0 { + var currentKey = mapReduce.partialResults[0].key + var currentResults: [Result] = [] + + for result in mapReduce.partialResults { + if result.key == currentKey { + currentResults.append(result) + } else { + mapReduce.reduce(partialResults: currentResults) + currentKey = result.key + currentResults = [] + currentResults.append(result) + } + } + } + + let reducedWords = mapReduce.finalResults.compactMap { reduced in + return "\(reduced.key): \(reduced.value)" + } + + XCTAssertEqual(reducedWords[0], "Dolores: 1") + XCTAssertEqual(reducedWords[1], "Ipsum: 1") + XCTAssertEqual(reducedWords[2], "Lorem: 1") + XCTAssertEqual(reducedWords[3], "Test: 1") + XCTAssertEqual(reducedWords[4], "document: 1") + XCTAssertEqual(reducedWords[5], "for: 1") + XCTAssertEqual(reducedWords[6], "map: 3") + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/MergeSortTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/MergeSortTests.swift new file mode 100644 index 0000000..edff644 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/MergeSortTests.swift @@ -0,0 +1,44 @@ +// +// MergeSort.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 28.03.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class MergeSortTests: XCTestCase { + + open func testMergeSort() { + var array1 = [4, 3, 2, 1] + MergeSort.mergeSort(array: &array1) + XCTAssertEqual([1, 2, 3, 4], array1) + + var array2 = [1, 3, 5, 7, 12, 14, 17, 19, 20, 22] + MergeSort.mergeSort(array: &array2) + XCTAssertEqual([1, 3, 5, 7, 12, 14, 17, 19, 20, 22], array2) + + var array3 = [22, 20, 19, 17, 14, 12, 7, 5, 3, 1] + MergeSort.mergeSort(array: &array3) + XCTAssertEqual( + [1, 3, 5, 7, 12, 14, 17, 19, 20, 22], array3) + } + + open func testLargeArrayWithUniqueItems() { + var array = [4, 4, 8, 8, 8, 8, 11, 11, 7, 7, 4, 4, 2, 2, 9, 9, 14, 14, 10, 10, 2, 2, 1, 1, 6, 6, 7, 7] + + MergeSort.mergeSort(array: &array) + XCTAssertEqual(array, + [1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 10, 10, 11, 11, 14, 14]) + } + + open func testSmallArrayWithUniqueItems() { + var array = [4, 4, 7, 8, 8, 9, 9] + + MergeSort.mergeSort(array: &array) + XCTAssertEqual(array, + [4, 4, 7, 8, 8, 9, 9]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/MinimumSpanningTreeKruskalTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/MinimumSpanningTreeKruskalTests.swift new file mode 100644 index 0000000..de8fce7 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/MinimumSpanningTreeKruskalTests.swift @@ -0,0 +1,76 @@ +// +// MinimumSpanningTreeKruskal.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 12.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class MinimumSpanningTreeKruskalTests: XCTestCase { + + open func testMSTKruskalQuickSort() { + let graph = setupTestGraph() + let mstKruskal = MinimumSpanningTreeKruskal(graph: graph) + let mst = mstKruskal.minimumSpanningTreeQuickSort() + + let array: [String] = mst.compactMap { + return "\($0.from.id) -> \($0.to.id): \($0.weight)" + } + + XCTAssertEqual(array, ["6 -> 7: 1", + "2 -> 8: 2", + "6 -> 5: 2", + "1 -> 0: 4", + "2 -> 5: 4", + "3 -> 2: 7", + "0 -> 7: 8", + "3 -> 4: 9"]) + } + + open func testMSTKruskalHeap() { + let graph = setupTestGraph() + let mstKruskal = MinimumSpanningTreeKruskal(graph: graph) + let mst = mstKruskal.minimumSpanningTreeQuickSort() + + let array: [String] = mst.compactMap { + return "\($0.from.id) -> \($0.to.id): \($0.weight)" + } + + XCTAssertEqual(array, ["6 -> 7: 1", + "2 -> 8: 2", + "6 -> 5: 2", + "1 -> 0: 4", + "2 -> 5: 4", + "3 -> 2: 7", + "0 -> 7: 8", + "3 -> 4: 9"]) + } + + //https://www.geeksforgeeks.org/kruskals-minimum-spanning-tree-algorithm-greedy-algo-2/?ref=lbp + private func setupTestGraph() -> WeightedUndirectedGraph { + let vertices: [Vertice] = + [Vertice(id: 0), Vertice(id: 1), Vertice(id: 2), Vertice(id: 3), Vertice(id: 4), + Vertice(id: 5), Vertice(id: 6), Vertice(id: 7), Vertice(id: 8)] + let graph = WeightedUndirectedGraph(vertices: vertices) + + graph.addEdge(v1: vertices[0], v2: vertices[1], weight: 4) + graph.addEdge(v1: vertices[0], v2: vertices[7], weight: 8) + graph.addEdge(v1: vertices[1], v2: vertices[2], weight: 8) + graph.addEdge(v1: vertices[1], v2: vertices[7], weight: 11) + graph.addEdge(v1: vertices[2], v2: vertices[3], weight: 7) + graph.addEdge(v1: vertices[2], v2: vertices[5], weight: 4) + graph.addEdge(v1: vertices[2], v2: vertices[8], weight: 2) + graph.addEdge(v1: vertices[3], v2: vertices[4], weight: 9) + graph.addEdge(v1: vertices[3], v2: vertices[5], weight: 14) + graph.addEdge(v1: vertices[4], v2: vertices[5], weight: 10) + graph.addEdge(v1: vertices[5], v2: vertices[6], weight: 2) + graph.addEdge(v1: vertices[6], v2: vertices[7], weight: 1) + graph.addEdge(v1: vertices[6], v2: vertices[8], weight: 6) + graph.addEdge(v1: vertices[7], v2: vertices[8], weight: 7) + + return graph + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/MinimumSpanningTreePrimTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/MinimumSpanningTreePrimTests.swift new file mode 100644 index 0000000..c9ceb09 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/MinimumSpanningTreePrimTests.swift @@ -0,0 +1,56 @@ +// +// MinimumSpanningTreePrim.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 13.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class MinimumSpanningTreePrimTests: XCTestCase { + + open func testMSTPrim() { + let graph = setupTestGraph() + let mstPrim = MinimumSpanningTreePrim(graph: graph) + let mst = mstPrim.minimumSpanningTree() + + let tree = mst.compactMap { + return "\($0.from.id) -> \($0.to.id): \($0.weight)" + } + + XCTAssertEqual(tree, ["0 -> 1: 4", + "0 -> 7: 8", + "7 -> 6: 1", + "6 -> 5: 2", + "5 -> 2: 4", + "2 -> 8: 2", + "2 -> 3: 7", + "3 -> 4: 9"]) + } + + private func setupTestGraph() -> WeightedUndirectedGraph { + let vertices: [Vertice] = [Vertice(id: 0), Vertice(id: 1), Vertice(id: 2), Vertice(id: 3), + Vertice(id: 4), Vertice(id: 5), Vertice(id: 6), Vertice(id: 7), Vertice(id: 8)] + let graph = WeightedUndirectedGraph(vertices: vertices) + + //https://www.geeksforgeeks.org/prims-minimum-spanning-tree-mst-greedy-algo-5/?ref=lbp + graph.addEdge(v1: vertices[0], v2: vertices[1], weight: 4) + graph.addEdge(v1: vertices[0], v2: vertices[7], weight: 8) + graph.addEdge(v1: vertices[1], v2: vertices[2], weight: 9) //changed to 9, as example algo doesn't pick this edge + graph.addEdge(v1: vertices[1], v2: vertices[7], weight: 11) + graph.addEdge(v1: vertices[2], v2: vertices[3], weight: 7) + graph.addEdge(v1: vertices[2], v2: vertices[5], weight: 4) + graph.addEdge(v1: vertices[2], v2: vertices[8], weight: 2) + graph.addEdge(v1: vertices[3], v2: vertices[4], weight: 9) + graph.addEdge(v1: vertices[3], v2: vertices[5], weight: 14) + graph.addEdge(v1: vertices[4], v2: vertices[5], weight: 10) + graph.addEdge(v1: vertices[5], v2: vertices[6], weight: 2) + graph.addEdge(v1: vertices[6], v2: vertices[7], weight: 1) + graph.addEdge(v1: vertices[6], v2: vertices[8], weight: 6) + graph.addEdge(v1: vertices[7], v2: vertices[8], weight: 7) + + return graph + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/PermutationTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/PermutationTests.swift new file mode 100644 index 0000000..709916c --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/PermutationTests.swift @@ -0,0 +1,25 @@ +// +// Permutation.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 27.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +//https://www.topcoder.com/generating-permutations/ +open class PermuationTests: XCTestCase { + + open func testPermutations() { + let permutation = Permuation() + var array = [1,2,3,4] + + var permuations = permutation.permutations(array: &array) + XCTAssertEqual(permuations, [[2, 3, 4, 1], [3, 2, 4, 1], [3, 4, 2, 1], [4, 3, 2, 1], [2, 4, 3, 1], [4, 2, 3, 1], [4, 3, 1, 2], [3, 4, 1, 2], [3, 1, 4, 2], [1, 3, 4, 2], [4, 1, 3, 2], [1, 4, 3, 2], [2, 4, 1, 3], [4, 2, 1, 3], [4, 1, 2, 3], [1, 4, 2, 3], [2, 1, 4, 3], [1, 2, 4, 3], [2, 3, 1, 4], [3, 2, 1, 4], [3, 1, 2, 4], [1, 3, 2, 4], [2, 1, 3, 4], [1, 2, 3, 4]]) + + permuations = permutation.heapsPermutations(array: &array) + XCTAssertEqual(permuations, [[1, 2, 3, 4], [2, 1, 3, 4], [3, 1, 2, 4], [1, 3, 2, 4], [2, 3, 1, 4], [3, 2, 1, 4], [4, 2, 1, 3], [2, 4, 1, 3], [1, 4, 2, 3], [4, 1, 2, 3], [2, 1, 4, 3], [1, 2, 4, 3], [1, 3, 4, 2], [3, 1, 4, 2], [4, 1, 3, 2], [1, 4, 3, 2], [3, 4, 1, 2], [4, 3, 1, 2], [4, 3, 2, 1], [3, 4, 2, 1], [2, 4, 3, 1], [4, 2, 3, 1], [3, 2, 4, 1], [2, 3, 4, 1]]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/QuickSortTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/QuickSortTests.swift new file mode 100644 index 0000000..771437d --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/QuickSortTests.swift @@ -0,0 +1,47 @@ +// +// QuickSort.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 29.03.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class QuickSortTests: XCTestCase { + + open func testQuickSort() { + var array1 = [2, 6, 5, 3, 8, 7, 1, 0] + QuickSort.quickSort(array: &array1) + XCTAssertEqual(array1, [0, 1, 2, 3, 5, 6, 7, 8]) + + var array2 = [0, 1, 2, 4, 5] + QuickSort.quickSort(array: &array2) + XCTAssertEqual(array2, [0, 1, 2, 4, 5]) + + var array3 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + QuickSort.quickSort(array: &array3) + XCTAssertEqual(array3, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + + var array4: [Int] = [] + QuickSort.quickSort(array: &array4) + XCTAssertEqual(array4, []) + } + + open func testLargeArrayWithUniqueItems() { + var array = [4, 4, 8, 8, 8, 8, 11, 11, 7, 7, 4, 4, 2, 2, 9, 9, 14, 14, 10, 10, 2, 2, 1, 1, 6, 6, 7, 7] + + QuickSort.quickSort(array: &array) + XCTAssertEqual(array, + [1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 10, 10, 11, 11, 14, 14]) + } + + open func testSmallArrayWithUniqueItems() { + var array = [4, 4, 7, 8, 8, 9, 9] + + QuickSort.quickSort(array: &array) + XCTAssertEqual(array, + [4, 4, 7, 8, 8, 9, 9]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/RabinKarpTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/RabinKarpTests.swift new file mode 100644 index 0000000..5bda852 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/RabinKarpTests.swift @@ -0,0 +1,25 @@ +// +// RabinKarp.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 04.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class RabinKarpSubstringSearchTests: XCTestCase { + + open func testRabinKarp() { + let rabinKarp = RabinKarpSubstringSearch(base: 128) + + XCTAssertNil(rabinKarp.substr(of: "xxx", in: "Substring")) + XCTAssertNil(rabinKarp.substr(of: "", in: "Substring")) + XCTAssertNil(rabinKarp.substr(of: "string", in: "str")) + + XCTAssertEqual(rabinKarp.substr(of: "Sub", in: "Substring"), 0) + XCTAssertEqual(rabinKarp.substr(of: "str", in: "Substring"), 3) + XCTAssertEqual(rabinKarp.substr(of: "a", in: "Travel Companion"), 2) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/TopologicalSortTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/TopologicalSortTests.swift new file mode 100644 index 0000000..13d1a17 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/TopologicalSortTests.swift @@ -0,0 +1,44 @@ +// +// TopologicalSort.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 01.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class TopologicalSortTests: XCTestCase { + + open func testTopologicalSort() throws { + let graph = setupTestGraph() + let topological = TopologicalSort(graph: graph) + + let sort = try topological.topologicalSort() + var vertices: [Int] = [] + while !sort.isEmpty() { + let vertice = try sort.dequeue() + vertices.append(vertice.id) + } + + //This is one possible topological sort. There can be more than one. + XCTAssertEqual(vertices, [4, 5, 2, 0, 3, 1]) + } + + //https://www.geeksforgeeks.org/topological-sorting/ + private func setupTestGraph() -> DirectedGraph { + let vertices: [Vertice] = [Vertice(id: 0), Vertice(id: 1), Vertice(id: 2), + Vertice(id: 3), Vertice(id: 4), Vertice(id: 5)] + let graph = DirectedGraph(vertices: vertices) + + graph.addEdge(v1: vertices[2], v2: vertices[3]) + graph.addEdge(v1: vertices[3], v2: vertices[1]) + graph.addEdge(v1: vertices[4], v2: vertices[1]) + graph.addEdge(v1: vertices[4], v2: vertices[0]) + graph.addEdge(v1: vertices[5], v2: vertices[2]) + graph.addEdge(v1: vertices[5], v2: vertices[0]) + + return graph + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/TravelingSalesmanTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/TravelingSalesmanTests.swift new file mode 100644 index 0000000..d686a07 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/Algorithms/TravelingSalesmanTests.swift @@ -0,0 +1,37 @@ +// +// TravellingSalesman.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 27.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class TravelingSalesmanTests: XCTestCase { + + open func testTravelingSalesman() { + let graph = setupTestGraph() + let travelingSalesman = TravelingSalesman(graph: graph) + let minRoute = travelingSalesman.travelingSalesman(startCity: graph.vertices[0]) + + XCTAssertEqual(travelingSalesman.minimumDistance, 80) + XCTAssertEqual(minRoute, [0, 2, 3, 1, 0]) + } + + //https://www.geeksforgeeks.org/traveling-salesman-problem-tsp-implementation + private func setupTestGraph() -> WeightedUndirectedGraphWithAdjMatrix { + let vertices: [Vertice] = [Vertice(id: 0), Vertice(id: 1), Vertice(id: 2), Vertice(id: 3)] + let graph = WeightedUndirectedGraphWithAdjMatrix(vertices: vertices) + + graph.addEdge(v1: vertices[0], v2: vertices[1], weight: 10) + graph.addEdge(v1: vertices[0], v2: vertices[2], weight: 15) + graph.addEdge(v1: vertices[0], v2: vertices[3], weight: 20) + graph.addEdge(v1: vertices[1], v2: vertices[2], weight: 35) + graph.addEdge(v1: vertices[1], v2: vertices[3], weight: 25) + graph.addEdge(v1: vertices[2], v2: vertices[3], weight: 30) + + return graph + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/AVLTreeTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/AVLTreeTests.swift new file mode 100644 index 0000000..396024d --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/AVLTreeTests.swift @@ -0,0 +1,148 @@ +// +// AVLTree.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 05.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class AVLTreeTests: XCTestCase { + + //Sample Trees from https://www.geeksforgeeks.org/avl-tree-set-1-insertion + open func testAVLTree() throws { + let rootValue = 13 + let treeLeftRotate = AVLTree() + + treeLeftRotate.insert(val: rootValue) + + treeLeftRotate.insert(val: 10) + treeLeftRotate.insert(val: 15) + + treeLeftRotate.insert(val: 5) + treeLeftRotate.insert(val: 11) + treeLeftRotate.insert(val: 16) + + treeLeftRotate.insert(val: 4) + treeLeftRotate.insert(val: 8) + + treeLeftRotate.insert(val: 3) + + //right rotation + var stack = treeLeftRotate.description() + var array: [String] = [] + while !stack.isEmpty() { + let element = try stack.pop() + array.insert(element, at: 0) + } + XCTAssertEqual(array, + ["13: 4", + "5: 3", + "4: 2", + "3: 1", + "10: 2", + "8: 1", + "11: 1", + "15: 2", + "16: 1"]) + + let treeRightRotate = AVLTree() + + treeRightRotate.insert(val: 30) + + treeRightRotate.insert(val: 5) + treeRightRotate.insert(val: 35) + + treeRightRotate.insert(val: 32) + treeRightRotate.insert(val: 40) + + treeRightRotate.insert(val: 45) + + //left rotation + stack = treeRightRotate.description() + array = [] + while !stack.isEmpty() { + let element = try stack.pop() + array.insert(element, at: 0) + } + XCTAssertEqual(array, + ["35: 3", + "30: 2", + "5: 1", + "32: 1", + "40: 2", + "45: 1"]) + + let treeLeftRightRotate = AVLTree() + + treeLeftRightRotate.insert(val: 13) + + treeLeftRightRotate.insert(val: 10) + treeLeftRightRotate.insert(val: 15) + + treeLeftRightRotate.insert(val: 5) + treeLeftRightRotate.insert(val: 11) + treeLeftRightRotate.insert(val: 16) + + treeLeftRightRotate.insert(val: 4) + treeLeftRightRotate.insert(val: 6) + + treeLeftRightRotate.insert(val: 7) + + //left right rotation + stack = treeLeftRightRotate.description() + array = [] + while !stack.isEmpty() { + let element = try stack.pop() + array.insert(element, at: 0) + } + XCTAssertEqual(array, + ["13: 4", + "6: 3", + "5: 2", + "4: 1", + "10: 2", + "7: 1", + "11: 1", + "15: 2", + "16: 1"]) + + let treeRightLeftRotate = AVLTree() + + treeRightLeftRotate.insert(val: 5) + + treeRightLeftRotate.insert(val: 2) + treeRightLeftRotate.insert(val: 7) + + treeRightLeftRotate.insert(val: 1) + treeRightLeftRotate.insert(val: 4) + treeRightLeftRotate.insert(val: 6) + treeRightLeftRotate.insert(val: 9) + + treeRightLeftRotate.insert(val: 3) + treeRightLeftRotate.insert(val: 16) + + treeRightLeftRotate.insert(val: 15) + + //right left rotation + stack = treeRightLeftRotate.description() + array = [] + while !stack.isEmpty() { + let element = try stack.pop() + array.insert(element, at: 0) + } + XCTAssertEqual(array, + ["5: 4", + "2: 3", + "1: 1", + "4: 2", + "3: 1", + "7: 3", + "6: 1", + "15: 2", + "9: 1", + "16: 1"]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/BTreeTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/BTreeTests.swift new file mode 100644 index 0000000..06a34ca --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/BTreeTests.swift @@ -0,0 +1,53 @@ +// +// BTree.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 18.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class BTreeTests: XCTestCase { + + //https://www.geeksforgeeks.org/insert-operation-in-b-tree + open func testBTree() throws { + let degree: Int = 3 + let rootNode = BTreeNode(degree: degree) + let bTree = BTree(root: rootNode) + + try bTree.insert(key: 10) + try bTree.insert(key: 20) + try bTree.insert(key: 30) + try bTree.insert(key: 40) + try bTree.insert(key: 50) + try bTree.insert(key: 60) + try bTree.insert(key: 70) + try bTree.insert(key: 80) + try bTree.insert(key: 90) + + let stack = bTree.traverse() + var array: [Int] = [] + while !stack.isEmpty() { + let element = try stack.pop() + array.insert(element, at: 0) + } + XCTAssertEqual(array, + [10, + 20, + 30, + 40, + 50, + 60, + 70, + 80, + 90]) + + XCTAssertTrue(bTree.search(key: 10)) + XCTAssertTrue(bTree.search(key: 30)) + XCTAssertTrue(bTree.search(key: 50)) + XCTAssertTrue(bTree.search(key: 90)) + XCTAssertFalse(bTree.search(key: 100)) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/BitVectorTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/BitVectorTests.swift new file mode 100644 index 0000000..ab6bf7c --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/BitVectorTests.swift @@ -0,0 +1,24 @@ +// +// BitVector.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 03.05.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class BitVectorTests: XCTestCase { + + open func testBitVector() { + let bitVector = BitVector(numberOfBits: 256) + + XCTAssertFalse(bitVector.isBitSet(index: 101)) + bitVector.setBit(index: 101) + XCTAssertTrue(bitVector.isBitSet(index: 101)) + + bitVector.unsetBit(index: 101) + XCTAssertFalse(bitVector.isBitSet(index: 101)) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/HashTableTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/HashTableTests.swift new file mode 100644 index 0000000..4c930a4 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/HashTableTests.swift @@ -0,0 +1,24 @@ +// +// HashTable.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 26.03.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class HashTableTests: XCTestCase { + + open func testHashTable() { + let hashTable = HashTable(size: 2069) //prime number + + hashTable.insert(val: "abcdef") + hashTable.insert(val: "bcdefa") + + XCTAssertTrue(hashTable.contains(val: "abcdef")) + XCTAssertTrue(hashTable.contains(val: "bcdefa")) + XCTAssertFalse(hashTable.contains(val: "bcdeaf")) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/IndexedMinPriorityQueueTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/IndexedMinPriorityQueueTests.swift new file mode 100644 index 0000000..39c1de3 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/IndexedMinPriorityQueueTests.swift @@ -0,0 +1,40 @@ +// +// IndexedMinPriorityQueue.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 15.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class IndexedMinPriorityQueueTests: XCTestCase { + + open func testIndexedMinPriorityQueue() throws { + let indexPq = IndexedMinPriorityQueue(maxElements: 10) + + try indexPq.insert(index: 4, key: 8) + try indexPq.insert(index: 5, key: 10) + try indexPq.insert(index: 3, key: 6) + try indexPq.insert(index: 1, key: 2) + try indexPq.insert(index: 2, key: 4) + + try indexPq.changeKey(index: 1, key: 10) + try indexPq.decreaseKey(index: 5, key: 2) + + XCTAssertNotNil(try indexPq.minKey()) + XCTAssertEqual(try indexPq.minKey(), 2) + XCTAssertEqual(try indexPq.minIndex(), 5) + XCTAssertEqual(try indexPq.extractMin(), 2) + + try indexPq.increaseKey(index: 2, key: 7) + + XCTAssertEqual(try indexPq.extractMin(), 6) + + try indexPq.delete(index: 4) + XCTAssertEqual(try indexPq.extractMin(), 7) + XCTAssertEqual(try indexPq.extractMin(), 10) + XCTAssertTrue(indexPq.isEmpty()) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/IntervalTreeTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/IntervalTreeTests.swift new file mode 100644 index 0000000..bafc6a9 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/IntervalTreeTests.swift @@ -0,0 +1,58 @@ +// +// IntervalTree.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 19.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class IntervalTreeTests: XCTestCase { + + //https://www.geeksforgeeks.org/interval-tree + open func testIntervalTree() throws { + let rootValue = Interval(low: 15, high: 20) + let rootNode = IntervalNode(interval: rootValue, height: 1) + let tree: IntervalTree = IntervalTree(root: rootNode) + + tree.insert(interval: Interval(low: 10, high: 30)) + tree.insert(interval: Interval(low: 5, high: 20)) + tree.insert(interval: Interval(low: 17, high: 19)) + tree.insert(interval: Interval(low: 12, high: 15)) + tree.insert(interval: Interval(low: 30, high: 40)) + + let stack = tree.description() + var array: [String] = [] + while !stack.isEmpty() { + let element = try stack.pop() + array.insert(element, at: 0) + } + XCTAssertEqual(array, + ["Low/High: 15-20, Min/Max: 5-40, Height: 3", + "Low/High: 10-30, Min/Max: 5-30, Height: 2", + "Low/High: 5-20, Min/Max: 5-20, Height: 1", + "Low/High: 12-15, Min/Max: 12-15, Height: 1", + "Low/High: 17-19, Min/Max: 17-40, Height: 2", + "Low/High: 30-40, Min/Max: 30-40, Height: 1"]) + + var interval = tree.overlapsInterval(interval: Interval(low: 10, high: 30)) + XCTAssertNotNil(interval) + XCTAssertEqual(interval!.low, 15) + XCTAssertEqual(interval!.high, 20) + + interval = tree.overlapsInterval(interval: Interval(low: 0, high: 5)) + XCTAssertNil(interval) + + interval = tree.overlapsInterval(interval: Interval(low: 4, high: 6)) + XCTAssertNotNil(interval) + XCTAssertEqual(interval!.low, 5) + XCTAssertEqual(interval!.high, 20) + + interval = tree.overlapsInterval(interval: Interval(low: 25, high: 27)) + XCTAssertNotNil(interval) + XCTAssertEqual(interval!.low, 10) + XCTAssertEqual(interval!.high, 30) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/LinkedListTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/LinkedListTests.swift new file mode 100644 index 0000000..cc3f17d --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/LinkedListTests.swift @@ -0,0 +1,28 @@ +import XCTest +@testable import DataStructuresAlgorithms + +open class LinkedListTests: XCTestCase { + + open func testLinkedList() { + let list = DoubleLinkedList() + + let node1 = Node(val: 1) + list.add(node: node1) + + let node2 = Node(val: 2) + list.add(node: node2) + + XCTAssertEqual(list.head?.val, 1) + XCTAssertEqual(list.head?.next?.val, 2) + XCTAssertEqual(list.tail?.val, 2) + XCTAssertNil(list.tail?.prev?.prev?.val) + XCTAssertEqual(list.count, 2) + + list.removeByKey(val: 1) + XCTAssertEqual(list.head?.val, 2) + XCTAssertNil(list.head?.next?.val) + XCTAssertEqual(list.tail?.val, 2) + XCTAssertNil(list.tail?.prev?.prev?.val) + XCTAssertEqual(list.count, 1) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/MaxHeapTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/MaxHeapTests.swift new file mode 100644 index 0000000..75869cf --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/MaxHeapTests.swift @@ -0,0 +1,52 @@ +// +// MaxHeapTests.swift +// +// +// Created by Stefan Jaindl on 11.12.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class MaxHeapTests: XCTestCase { + + open func testMaxHeap() throws { + let heap = MaxHeap() + + heap.insert(val: 1) + heap.insert(val: 5) + heap.insert(val: 2) + heap.insert(val: 10) + heap.insert(val: -5) + + XCTAssertEqual(try heap.peekMax(), 10) + XCTAssertEqual(try heap.extractMax(), 10) + XCTAssertEqual(try heap.extractMax(), 5) + XCTAssertEqual(try heap.extractMax(), 2) + XCTAssertEqual(try heap.extractMax(), 1) + + heap.insert(val: 11) + heap.insert(val: 7) + + XCTAssertEqual(try heap.extractMax(), 11) + XCTAssertEqual(try heap.extractMax(), 7) + XCTAssertEqual(try heap.extractMax(), -5) + } + + open func testMaxHeapEqualValues() throws { + let heap = MaxHeap() + let test = [1, 1, 2, 7, 6, 8, 4, 7, 8, 8, 10, 10, 11, 11, + 9, 7, 6, 4, 2, 4, 8, 9, 4, 2, 2, 7, 14, 14] + + for value in test { + heap.insert(val: value) + } + + var lastElement = Int.max + while !heap.isEmpty() { + let element = try heap.extractMax() + XCTAssertGreaterThanOrEqual(lastElement, element) + lastElement = element + } + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/MinHeapTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/MinHeapTests.swift new file mode 100644 index 0000000..e9b47dd --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/MinHeapTests.swift @@ -0,0 +1,53 @@ +// +// MinHeap.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 25.03.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class MinHeapTests: XCTestCase { + + open func testMinHeap() throws { + let heap = MinHeap() + + heap.insert(val: 1) + heap.insert(val: 5) + heap.insert(val: 2) + heap.insert(val: 10) + heap.insert(val: -5) + + XCTAssertEqual(try heap.peekMin(), -5) + XCTAssertEqual(try heap.extractMin(), -5) + XCTAssertEqual(try heap.extractMin(), 1) + XCTAssertEqual(try heap.extractMin(), 2) + XCTAssertEqual(try heap.extractMin(), 5) + + heap.insert(val: 11) + heap.insert(val: 7) + + XCTAssertEqual(try heap.extractMin(), 7) + XCTAssertEqual(try heap.extractMin(), 10) + XCTAssertEqual(try heap.extractMin(), 11) + } + + open func testMinHeapEqualValues() throws { + let heap = MinHeap() + let test = [1, 1, 2, 7, 6, 8, 4, 7, 8, 8, 10, 10, 11, 11, + 9, 7, 6, 4, 2, 4, 8, 9, 4, 2, 2, 7, 14, 14] + + for value in test { + heap.insert(val: value) + } + + var lastElement = Int.min + while !heap.isEmpty() { + let element = try heap.extractMin() + XCTAssertLessThanOrEqual(lastElement, element) + lastElement = element + } + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/QueueTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/QueueTests.swift new file mode 100644 index 0000000..57dc3ad --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/QueueTests.swift @@ -0,0 +1,26 @@ +// +// Queue.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 23.03.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class QueueTests: XCTestCase { + + open func testQueue() throws { + let queue = Queue() + + queue.enqueue(val: 1) + queue.enqueue(val: 2) + + var element = try queue.dequeue() + XCTAssertEqual(element, 1) + + element = try queue.dequeue() + XCTAssertEqual(element, 2) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/RedBlackTreeTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/RedBlackTreeTests.swift new file mode 100644 index 0000000..3ce4ef9 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/RedBlackTreeTests.swift @@ -0,0 +1,28 @@ +import XCTest +@testable import DataStructuresAlgorithms + +open class RedBlackTreeTests: XCTestCase { + + open func testRedBlackTree() { + let rootValue = 10 + let tree = RedBlackTree() + + tree.insert(value: rootValue) + tree.insert(value: 5) + tree.insert(value: 20) + + tree.insert(value: 2) + + XCTAssertEqual(tree.root?.value, 10) + XCTAssertEqual(tree.root?.color, .black) + + XCTAssertEqual(tree.root?.left?.value, 5) + XCTAssertEqual(tree.root?.left?.color, .black) + + XCTAssertEqual(tree.root?.right?.value, 20) + XCTAssertEqual(tree.root?.right?.color, .black) + + XCTAssertEqual(tree.root?.left?.left?.value, 2) + XCTAssertEqual(tree.root?.left?.left?.color, .red) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/StackTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/StackTests.swift new file mode 100644 index 0000000..907a9a3 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/StackTests.swift @@ -0,0 +1,25 @@ +// +// Stack.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 23.03.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class StackTests: XCTestCase { + + open func testStack() throws { + let stack = Stack() + + stack.push(val: 5) + stack.push(val: 10) + + XCTAssertFalse(stack.isEmpty()) + XCTAssertEqual(try stack.pop(), 10) + XCTAssertEqual(try stack.pop(), 5) + XCTAssertTrue(stack.isEmpty()) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/TrieTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/TrieTests.swift new file mode 100644 index 0000000..420f513 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/TrieTests.swift @@ -0,0 +1,16 @@ +import XCTest +@testable import DataStructuresAlgorithms + +open class TrieTests: XCTestCase { + + open func testTrie() { + let trieValue = "Trie" + let trie = Trie() + + trie.insert(word: trieValue) + + XCTAssertFalse(trie.search(word: "T")) + XCTAssertTrue(trie.search(word: trieValue)) + XCTAssertFalse(trie.search(word: "Triex")) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/UnionFindTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/UnionFindTests.swift new file mode 100644 index 0000000..de43eed --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructures/UnionFindTests.swift @@ -0,0 +1,39 @@ +// +// UnionFind.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 12.04.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class UnionFindTests: XCTestCase { + + open func testUnionFind() { + let graph = setupTestGraph() + let unionFind = UnionFind(graph: graph) + + XCTAssertEqual(unionFind.numberOfComponents, 4) + + unionFind.union(firstIndex: 0, secondIndex: 1) + XCTAssertEqual(unionFind.find(index: 0), 0) + XCTAssertEqual(unionFind.find(index: 1), 0) + XCTAssertEqual(unionFind.numberOfComponents, 3) + } + + //https://www.geeksforgeeks.org/kruskals-minimum-spanning-tree-algorithm-greedy-algo-2/?ref=lbp + private func setupTestGraph() -> WeightedUndirectedGraph { + let vertices: [Vertice] = [Vertice(id: 0), Vertice(id: 1), Vertice(id: 2), Vertice(id: 3)] + let graph = WeightedUndirectedGraph(vertices: vertices) + + graph.addEdge(v1: vertices[0], v2: vertices[1], weight: 10) + graph.addEdge(v1: vertices[0], v2: vertices[2], weight: 6) + graph.addEdge(v1: vertices[0], v2: vertices[3], weight: 5) + graph.addEdge(v1: vertices[1], v2: vertices[3], weight: 15) + graph.addEdge(v1: vertices[2], v2: vertices[3], weight: 4) + + return graph + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/DataStructuresAlgorithmsTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructuresAlgorithmsTests.swift new file mode 100644 index 0000000..9d6574f --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/DataStructuresAlgorithmsTests.swift @@ -0,0 +1,212 @@ +import XCTest +@testable import DataStructuresAlgorithms + +open class DataStructuresAlgorithmsTests: XCTestCase { + + open func testAllSynchronous() throws { + try testMajorDatastructuresAndAlgorithms() + try testStringAndArray() + try testLinkedList() + try testStackAndQueue() + try testTreeAndGraph() + testBitManipulation() + try testRecursionAndDynamicProgramming() + testSortingAndSearching() + try testOtherAlgorithms() + } + + open func testThreading() { + DiningPhilosophersTests().testDiningPhilosophers() + DeadlockPreventingLockTests().testDeadlockPreventingLock() + FizzBuzzTests().testFizzBuzz() + } + + open func testMajorDatastructuresAndAlgorithms() throws { + BellmanFordTests().testBellmanFord() + BipartiteGraphCheckerTests().testBipartiteGraph() + try DjikstraTests().testDjikstra() + try FloydWarshallTests().testFloydWarshall() + GraphSearchTests().testBFS() + GraphSearchTests().testDFS() + GraphSearchTests().testDFSIterative() + KnapsackTests().testKnapsack() + MapReduceTests().testMapReduceMock() + MergeSortTests().testMergeSort() + MergeSortTests().testLargeArrayWithUniqueItems() + MergeSortTests().testSmallArrayWithUniqueItems() + MinimumSpanningTreeKruskalTests().testMSTKruskalQuickSort() + MinimumSpanningTreeKruskalTests().testMSTKruskalHeap() + MinimumSpanningTreePrimTests().testMSTPrim() + PermuationTests().testPermutations() + QuickSortTests().testQuickSort() + QuickSortTests().testLargeArrayWithUniqueItems() + QuickSortTests().testSmallArrayWithUniqueItems() + RabinKarpSubstringSearchTests().testRabinKarp() + try TopologicalSortTests().testTopologicalSort() + TravelingSalesmanTests().testTravelingSalesman() + UniqueStringSearchTests().testUniqueStringSearch() + try AVLTreeTests().testAVLTree() + BitVectorTests().testBitVector() + try BTreeTests().testBTree() + HashTableTests().testHashTable() + try IndexedMinPriorityQueueTests().testIndexedMinPriorityQueue() + try IntervalTreeTests().testIntervalTree() + LinkedListTests().testLinkedList() + try MinHeapTests().testMinHeap() + try MinHeapTests().testMinHeapEqualValues() + try QueueTests().testQueue() + RedBlackTreeTests().testRedBlackTree() + try StackTests().testStack() + TrieTests().testTrie() + UnionFindTests().testUnionFind() + StringPermutationCheckerTests().testStringPermutations() + LRUCacheTests().testCache() + try MaxHeapTests().testMaxHeap() + } + + open func testStringAndArray() throws { + UniqueStringSearchTests().testUniqueStringSearch() + UrlifierTests().testUrlify() + PalindromePermutationTests().testPalindromePermutations() + OneAwayCheckerTests().testOneAwayChecker() + StringCompresserTests().testStringCompresser() + MatrixRotationTests().testMatrixRightRotation() + ZeroMatrixTests().testZeroMatrix() + StringRotationTests().testStringRotation() + CircularArrayTests().testCircularArray() + WordFrequencyCounterTests().testCounts() + SmallestDifferenceTests().testSmallestDifference() + try IntToEnglishStringTests().testEnglishString() + try XMLEncoderTests().testXMLEncoding() + PatternMatcherTests().testPatterns() + try T9KeyboardTests().testT9Keyboard() + SumSwapperTests().testSumSwapper() + ArrayPairsWithSumTests().testPairs() + MissingNumberTests().testMissingNumber() + LettersAndNumbersLargestSubsequenceTests().testLargestSubsequence() + try NamesMergerTests().testMerging1() + try NamesMergerTests().testMerging2() + MajorityElementTests().testMajorityElement() + SpaceInserterTests().testSpaceInserter() + MinElementsFinderTests().testFindMinElements() + try MinElementsFinderTests().testFindMinElementsSorted() + LongestWordFinderTests().testLongest() + WordTransformerTests().testTransform() + MaxBlackTests().testMaxBlack() + MaxMatrixSumTests().testMaxSum() + WordRectangleTests().testWordRectangle() + SparseMatrixTests().testSparseMatrix() + } + + open func testLinkedList() throws { + try DuplicateRemoverTests().testDuplicateRemoval() + try KToLastTest().testKToLast() + try DeleteMiddleNodeTest().testDeleteMiddleNode() + try PartitionLinkedListTests().testPartition() + try SumListsTests().testReverseSum() + try SumListsTests().testSumWithPadding() + try SumListsTests().testSumWithExcessCarry() + try LoopDetectorTests().testLoopDetector() + BinaryNodeConverterTests().testConvertion() + } + + open func testStackAndQueue() throws { + try MultiStackTests().testMultiStack() + try MinStackTests().testMinStack() + try AnimalShelterTests().testAnimalShelter() + try QueueWithStackTests().testQueueWithStacks() + try StackSetTests().testStackSet() + try StackSetTests().testStackSet() + try CalculatorTests().testCalculator() + } + + open func testTreeAndGraph() throws { + PathCheckerTests().testPathChecker() + try MinTreeTests().testMinTree() + ListOfDepthsTests().testListOfDepths() + BalancedTreeCheckerTests().testBalancedTree() + BalancedTreeCheckerTests().testUnbalancedTree() + ValidateBSTTests().testValidTree() + ValidateBSTTests().testInvalidTree() + NodeSuccessorTests().testNodeSuccessorCaseOne() + NodeSuccessorTests().testNodeSuccessorCaseTwo() + NodeSuccessorTests().testNodeSuccessorCaseThree() + FirstCommonAncestorTests().testFirstCommonAncestor() + BSTSequenceTests().testBSTSequence() + SubtreeCheckerTests().testSubtreeChecker() + RandomBinarySearchTreeTests().testRandomBinarySearchTree() + PathSumTests().testPathSum() + } + + open func testBitManipulation() { + BitInserterTests().testBitsInsertion() + BinaryToStringConverterTests().testDoubleConversion() + BitFlipperTests().testLongestOneBitSequence() + BinaryNearbySearcherTests().testNearestBinaryNumbers() + BinaryNumberConverterTests().testBinaryNumberConverter() + PairwiseSwapTests().testSwap() + ScreenManipulatorTests().testDrawLine() + NumberSwapperTests().testSwap() + NumberMaxTests().testMaxNumber() + OperationsTests().testOperations() + AdderTests().testAddWithoutPlus() + } + + open func testRecursionAndDynamicProgramming() throws { + TripleStepTests().testTripleSteps() + GridPathFinderTests().testGridPathFinder() + PowerSetTests().testPowerSetPermutations() + PowerSetTests().testPowerSetCombinations() + PowerSetTests().testPowerSetCombinationsByBitmasking() + MultiplyTests().testMultiply() + try TowerOfHanoiTests().testTowersOfHanoi() + PermutationsTests().testPermutationsWithoutDuplicates() + ParensBuilderTests().testParens() + try PaintFillTests().testPaintFill() + CoinCounterTests().testCoinCounter() + QueensOnChessboardTests().testQueensArrangements() + StackOfBoxesTests().testStackOfBoxes() + try BoolEvaluationTests().testEvaluation() + FactorialZeroesTests().testTrailingZeroCount() + DivingBoardTests().testDivingBoard() + DivingBoardTests().testDivingBoardFast() + PondSizesTests().testPatterns() + CircusTowerTests().testMaxTower() + AppointmentTimeOptimizerTests().testAppointments() + } + + open func testSortingAndSearching() { + SortedMergeTests().testSortedMerge() + AnagramGrouperTests().testAnagramGrouper() + RotatedArraySearchTests().testRotatedArraySearch() + ListyTests().testListy() + SparseSearchTests().testSparseSearch() + DuplicateFinderTests().testFindDuplicates() + SortedMatrixSearchTests().testSortedMatrixSearch() + SortedMatrixSearchTests().testSortedMatrixSearchFaster() + StreamRankTests().testStreamRank() + PeakSortTests().testPeakSort() + IntersectionFinderTests().testIntersection() + LivingPeopleTests().testLivingPeople() + BestLineSearcherTests().testBestLineSearcher() + SubSorterTests().testShortestSubsortSequence() + ContiguousSumTests().testLargestContiguousSum() + MultiSearchTests().testMultiSearch() + ShortestSuperSequenceTests().testShortestSequence() + MedianKeeperTests().testMedian() + } + + open func testOtherAlgorithms() throws { + SquareConnectorTests().testSquareConnector() + try MasterMindCheckerTests().testMasterMindChecker() + LangthonsCellTests().testLangthons() + Rand7Tests().testRand7() + DeckShufflerTests().testShuffle() + RandomSetTests().testRandomSet() + CountOfTwosTests().testTwosCount() + try KthMultipleTests().testMultiples() + WordDistanceTests().testMinDistance() + MissingTwoTests().testMissing() + HistogramVolumeTests().testVolume() + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/ArrayPairsWithSumTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/ArrayPairsWithSumTests.swift new file mode 100644 index 0000000..af243ae --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/ArrayPairsWithSumTests.swift @@ -0,0 +1,24 @@ +// +// ArrayPairsWithSumTests.swift +// +// +// Created by Stefan Jaindl on 01.12.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class ArrayPairsWithSumTests: XCTestCase { + + open func testPairs() { + let arrayWithSumPairs = ArrayPairsWithSum() + let array = [1, 7, 5, 2, 3, 3, 1, 6, 4] + + let pairs = arrayWithSumPairs.pairsWithSum(array: array, targetSum: 7) + + XCTAssertEqual(pairs.count, 3) + XCTAssertTrue(pairs.contains(where: { $0 == ArrayPairsWithSum.TargetSumPair(first: 1, second: 6) })) + XCTAssertTrue(pairs.contains(where: { $0 == ArrayPairsWithSum.TargetSumPair(first: 5, second: 2) })) + XCTAssertTrue(pairs.contains(where: { $0 == ArrayPairsWithSum.TargetSumPair(first: 3, second: 4) })) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/CircularArrayTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/CircularArrayTests.swift new file mode 100644 index 0000000..c8b6c9c --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/CircularArrayTests.swift @@ -0,0 +1,37 @@ +// +// CircularArrayTests.swift +// +// +// Created by Stefan Jaindl on 10.08.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class CircularArrayTests: XCTestCase { + open func testCircularArray() { + let array = [1, 2, 3, 4, 5] + let circularArray = CircularArray(array: array) + + var iterator = circularArray.makeIterator() + XCTAssertEqual(iterator.next(), 1) + + circularArray.leftRotate(by: 2) + iterator = circularArray.makeIterator() + XCTAssertEqual(iterator.next(), 3) + XCTAssertEqual(iterator.next(), 4) + XCTAssertEqual(iterator.next(), 5) + XCTAssertEqual(iterator.next(), 1) + XCTAssertEqual(iterator.next(), 2) + XCTAssertEqual(iterator.next(), nil) + + circularArray.rightRotate(by: 4) + iterator = circularArray.makeIterator() + XCTAssertEqual(iterator.next(), 4) + XCTAssertEqual(iterator.next(), 5) + XCTAssertEqual(iterator.next(), 1) + XCTAssertEqual(iterator.next(), 2) + XCTAssertEqual(iterator.next(), 3) + XCTAssertEqual(iterator.next(), nil) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/IntToEnglishStringTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/IntToEnglishStringTests.swift new file mode 100644 index 0000000..3ecfb39 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/IntToEnglishStringTests.swift @@ -0,0 +1,36 @@ +// +// IntToEnglishStringTests.swift +// +// +// Created by Stefan Jaindl on 20.11.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class IntToEnglishStringTests: XCTestCase { + open func testEnglishString() throws { + let converter = IntToEnglishString() + + XCTAssertEqual(try converter.convertToEnglishString(number: 0), "Zero") + XCTAssertEqual(try converter.convertToEnglishString(number: 1), "One") + XCTAssertEqual(try converter.convertToEnglishString(number: 10), "Ten") + XCTAssertEqual(try converter.convertToEnglishString(number: 12), "Twelve") + XCTAssertEqual(try converter.convertToEnglishString(number: 20), "Twenty") + XCTAssertEqual(try converter.convertToEnglishString(number: 25), "Twenty Five") + XCTAssertEqual(try converter.convertToEnglishString(number: 100), "One Hundred") + XCTAssertEqual(try converter.convertToEnglishString(number: 102), "One Hundred Two") + XCTAssertEqual(try converter.convertToEnglishString(number: 112), "One Hundred Twelve") + XCTAssertEqual(try converter.convertToEnglishString(number: 1000), "One Thousand") + XCTAssertEqual(try converter.convertToEnglishString(number: 1002), "One Thousand Two") + XCTAssertEqual(try converter.convertToEnglishString(number: 10000), "Ten Thousand") + XCTAssertEqual(try converter.convertToEnglishString(number: 100000), "One Hundred Thousand") + XCTAssertEqual(try converter.convertToEnglishString(number: 1000000), "One Million") + XCTAssertEqual(try converter.convertToEnglishString(number: 10000000), "Ten Million") + XCTAssertEqual(try converter.convertToEnglishString(number: 100000000), "One Hundred Million") + XCTAssertEqual(try converter.convertToEnglishString(number: 1000000000), "One Billion") + XCTAssertEqual(try converter.convertToEnglishString(number: 1234567890), "One Billion Two Hundred Thirty Four Million Five Hundred Sixty Seven Thousand Eight Hundred Ninety") + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/LettersAndNumbersLargestSubsequenceTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/LettersAndNumbersLargestSubsequenceTests.swift new file mode 100644 index 0000000..f34e276 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/LettersAndNumbersLargestSubsequenceTests.swift @@ -0,0 +1,25 @@ +// +// LettersAndNumbersLargestSubsequenceTests.swift +// +// +// Created by Stefan Jaindl on 05.12.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class LettersAndNumbersLargestSubsequenceTests: XCTestCase { + open func testLargestSubsequence() { + let subSequenceFinder = LettersAndNumbersLargestSubsequence() + + XCTAssertEqual(subSequenceFinder.findLargestSubSequence(of: ["1"]), []) + XCTAssertEqual(subSequenceFinder.findLargestSubSequence(of: []), []) + XCTAssertEqual(subSequenceFinder.findLargestSubSequence(of: ["1", "a"]), ["1", "a"]) + XCTAssertEqual(subSequenceFinder.findLargestSubSequence(of: ["1", "1", "a"]), ["1", "a"]) + XCTAssertEqual(subSequenceFinder.findLargestSubSequence(of: ["1", "a", "a", "1", "a"]), ["1", "a", "a", "1"]) + XCTAssertEqual(subSequenceFinder.findLargestSubSequence(of: ["a", "1", "a", "a", "1"]), ["1", "a", "a", "1"]) + XCTAssertEqual(subSequenceFinder.findLargestSubSequence(of: ["1", "1", "1", "1", "a", "a", "a", "a", "1", "a", "1", "1", "a", "1", "1"]), ["1", "1", "1", "a", "a", "a", "a", "1", "a", "1", "1", "a"]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/LongestWordFinderTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/LongestWordFinderTests.swift new file mode 100644 index 0000000..3549d6f --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/LongestWordFinderTests.swift @@ -0,0 +1,19 @@ +// +// LongestWordFinderTests.swift +// +// +// Created by Stefan Jaindl on 11.12.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class LongestWordFinderTests: XCTestCase { + open func testLongest() { + let finder = LongestWordFinder() + + let longest = finder.findLongestCombinedWord(of: ["cat", "banana", "dog", "nana", "walk", "walker", "dogwalker"]) + XCTAssertEqual(longest, "dogwalker") + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/MajorityElementTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/MajorityElementTests.swift new file mode 100644 index 0000000..e269219 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/MajorityElementTests.swift @@ -0,0 +1,25 @@ +// +// MajorityElementTests.swift +// +// +// Created by Stefan Jaindl on 08.12.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class MajorityElementTests: XCTestCase { + open func testMajorityElement() { + let array = [1, 2, 5, 9, 5, 9, 5, 5, 5] + let majorityFinder = MajorityElement() + + XCTAssertEqual(majorityFinder.find(array: array), 5) + XCTAssertEqual(majorityFinder.find(array: [1, 2, 5, 9, 5, 9, 5, 5]), nil) + XCTAssertEqual(majorityFinder.find(array: [1, 2, 1]), 1) + XCTAssertEqual(majorityFinder.find(array: [1]), 1) + XCTAssertEqual(majorityFinder.find(array: [1, 1]), 1) + XCTAssertEqual(majorityFinder.find(array: [1, 2]), nil) + XCTAssertEqual(majorityFinder.find(array: [1, 2, 3]), nil) + XCTAssertEqual(majorityFinder.find(array: []), nil) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/MatrixRotationTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/MatrixRotationTests.swift new file mode 100644 index 0000000..8b6bb50 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/MatrixRotationTests.swift @@ -0,0 +1,41 @@ +// +// PalindromePermutationTests.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 14.05.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class MatrixRotationTests: XCTestCase { + + open func testMatrixRightRotation() { + let rotator = MatrixRotation() + + var oddArray = [[1, 2, 3], + [4, 5, 6], + [7, 8, 9]] + + let rotatedOddArray = [[7, 4, 1], + [8, 5, 2], + [9, 6, 3]] + + var evenArray = [[1, 2, 3, 4], + [5, 6, 7, 8], + [9, 10, 11, 12], + [13, 14, 15, 16]] + + let rotatedEvenArray = [[13, 9, 5, 1], + [14, 10, 6, 2], + [15, 11, 7, 3], + [16, 12, 8, 4]] + + rotator.rotateRight(array: &oddArray) + rotator.rotateRight(array: &evenArray) + + XCTAssertEqual(oddArray, rotatedOddArray) + XCTAssertEqual(evenArray, rotatedEvenArray) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/MaxBlackTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/MaxBlackTests.swift new file mode 100644 index 0000000..d417679 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/MaxBlackTests.swift @@ -0,0 +1,159 @@ +// +// MaxBlackTests.swift +// +// +// Created by Stefan Jaindl on 19.12.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class MaxBlackTests: XCTestCase { + + + open func testMaxBlackBorder() { + let maxBlacker = MaxBlack() + + var cells = [ + [white(), black(), black(), black(), black()], + [white(), black(), black(), black(), black()], + [white(), black(), black(), black(), black()], + [white(), black(), black(), black(), black()], + [white(), black(), black(), black(), black()] + ] + + var square = BlackWhiteSquare(cells: cells) + var max = maxBlacker.maxBorder(of: square) + + XCTAssertEqual(max, SubSquare(topLeft: CellIndex(row: 0, col: 1), bottomRight: CellIndex(row: 3, col: 4))) + + cells = [ + [white(), black(), black(), black(), black()], + [white(), black(), black(), black(), black()], + [white(), black(), black(), black(), white()], + [white(), black(), black(), black(), black()], + [white(), black(), black(), black(), black()] + ] + + square = BlackWhiteSquare(cells: cells) + max = maxBlacker.maxBorder(of: square) + + XCTAssertEqual(max, SubSquare(topLeft: CellIndex(row: 0, col: 1), bottomRight: CellIndex(row: 2, col: 3))) + + cells = [ + [white(), white(), white(), white(), white()], + [white(), white(), white(), black(), white()], + [white(), white(), black(), black(), white()], + [white(), black(), black(), black(), white()], + [white(), white(), white(), white(), black()] + ] + + square = BlackWhiteSquare(cells: cells) + max = maxBlacker.maxBorder(of: square) + + XCTAssertEqual(max, SubSquare(topLeft: CellIndex(row: 2, col: 2), bottomRight: CellIndex(row: 3, col: 3))) + + cells = [ + [white(), white(), white(), white(), white()], + [white(), white(), white(), white(), white()], + [white(), white(), white(), white(), white()], + [white(), white(), white(), white(), white()], + [white(), white(), white(), white(), black()] + ] + + square = BlackWhiteSquare(cells: cells) + max = maxBlacker.maxBorder(of: square) + + XCTAssertEqual(max, SubSquare(topLeft: CellIndex(row: 4, col: 4), bottomRight: CellIndex(row: 4, col: 4))) + + cells = [ + [white(), white(), white(), white(), white()], + [white(), white(), white(), white(), white()], + [white(), white(), white(), white(), white()], + [white(), white(), white(), white(), white()], + [white(), white(), white(), white(), white()] + ] + + square = BlackWhiteSquare(cells: cells) + max = maxBlacker.maxBorder(of: square) + + XCTAssertEqual(max, nil) + } + + open func testMaxBlack() { + let maxBlacker = MaxBlack() + + var cells = [ + [white(), black(), black(), black(), black()], + [white(), black(), black(), black(), black()], + [white(), black(), black(), black(), black()], + [white(), black(), black(), black(), black()], + [white(), black(), black(), black(), black()] + ] + + var square = BlackWhiteSquare(cells: cells) + var max = maxBlacker.maxBlackFilled(of: square) + + XCTAssertEqual(max, SubSquare(topLeft: CellIndex(row: 0, col: 1), bottomRight: CellIndex(row: 4, col: 4))) + + cells = [ + [white(), black(), black(), black(), black()], + [white(), black(), black(), black(), black()], + [white(), black(), black(), black(), white()], + [white(), black(), black(), black(), black()], + [white(), black(), black(), black(), black()] + ] + + square = BlackWhiteSquare(cells: cells) + max = maxBlacker.maxBlackFilled(of: square) + + XCTAssertEqual(max, SubSquare(topLeft: CellIndex(row: 0, col: 1), bottomRight: CellIndex(row: 4, col: 3))) + + cells = [ + [white(), white(), white(), white(), white()], + [white(), white(), white(), black(), white()], + [white(), white(), black(), black(), white()], + [white(), black(), black(), black(), white()], + [white(), white(), white(), white(), black()] + ] + + square = BlackWhiteSquare(cells: cells) + max = maxBlacker.maxBlackFilled(of: square) + + XCTAssertEqual(max, SubSquare(topLeft: CellIndex(row: 2, col: 2), bottomRight: CellIndex(row: 3, col: 3))) + + cells = [ + [white(), white(), white(), white(), white()], + [white(), white(), white(), white(), white()], + [white(), white(), white(), white(), white()], + [white(), white(), white(), white(), white()], + [white(), white(), white(), white(), black()] + ] + + square = BlackWhiteSquare(cells: cells) + max = maxBlacker.maxBlackFilled(of: square) + + XCTAssertEqual(max, SubSquare(topLeft: CellIndex(row: 4, col: 4), bottomRight: CellIndex(row: 4, col: 4))) + + cells = [ + [white(), white(), white(), white(), white()], + [white(), white(), white(), white(), white()], + [white(), white(), white(), white(), white()], + [white(), white(), white(), white(), white()], + [white(), white(), white(), white(), white()] + ] + + square = BlackWhiteSquare(cells: cells) + max = maxBlacker.maxBlackFilled(of: square) + + XCTAssertEqual(max, nil) + } + + private func black() -> SquareCell { + return SquareCell(color: .black) + } + + private func white() -> SquareCell { + return SquareCell(color: .white) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/MaxMatrixSumTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/MaxMatrixSumTests.swift new file mode 100644 index 0000000..cffe459 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/MaxMatrixSumTests.swift @@ -0,0 +1,26 @@ +// +// MaxMatrixSumTests.swift +// +// +// Created by Stefan Jaindl on 20.12.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class MaxMatrixSumTests: XCTestCase { + + open func testMaxSum() { + let summer = MaxMatrixSum() + + let matrix = [ + [1, 7, -4, 2], + [2, -1, -4, 2], + [3, -3, -4, 2], + [4, 7, -4, 0] + ] + + let maxRectangle = summer.maxSum(of: matrix) + XCTAssertEqual(maxRectangle, SubRectangle(topLeft: CellIndex(row: 0, col: 0), bottomRight: CellIndex(row: 3, col: 1))) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/MinElementsFinderTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/MinElementsFinderTests.swift new file mode 100644 index 0000000..72f86a9 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/MinElementsFinderTests.swift @@ -0,0 +1,29 @@ +// +// MinElementsFinderTests.swift +// +// +// Created by Stefan Jaindl on 11.12.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class MinElementsFinderTests: XCTestCase { + + open func testFindMinElementsSorted() throws { + let finder = MinElementsFinder() + + XCTAssertEqual(try finder.findMinElementsSorted(numberOfElements: 4, array: [1, 2, 3, 4, 5, 6]), [1, 2, 3, 4]) + XCTAssertEqual(try finder.findMinElementsSorted(numberOfElements: 4, array: [6, 5, 4, 3, 2, 1]), [1, 2, 3, 4]) + XCTAssertEqual(try finder.findMinElementsSorted(numberOfElements: 2, array: [6, 5, 4, 3, 2, 2]), [2, 2]) + } + + open func testFindMinElements() { + let finder = MinElementsFinder() + + XCTAssertEqual(finder.findMinElementsUnique(numberOfElements: 4, array: [3, 6, 1, 2, 5, 4]).sorted(), [1, 2, 3, 4]) + XCTAssertEqual(finder.findMinElementsUnique(numberOfElements: 4, array: [1, 2, 3, 4, 5, 6]).sorted(), [1, 2, 3, 4]) + XCTAssertEqual(finder.findMinElementsUnique(numberOfElements: 4, array: [6, 5, 4, 3, 2, 1]).sorted(), [1, 2, 3, 4]) + XCTAssertEqual(finder.findMinElementsUnique(numberOfElements: 2, array: [6, 5, 4, 3, 2, 2]), [2, 2]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/MissingNumberTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/MissingNumberTests.swift new file mode 100644 index 0000000..fa8edc1 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/MissingNumberTests.swift @@ -0,0 +1,24 @@ +// +// MissingNumberTests.swift +// +// +// Created by Stefan Jaindl on 04.12.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class MissingNumberTests: XCTestCase { + + open func testMissingNumber() { + let missingNumber = MissingNumber() + + XCTAssertEqual(missingNumber.findMissingNumber(of: [0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], lenght: 13), 3) + XCTAssertEqual(missingNumber.findMissingNumber(of: [1, 2, 3], lenght: 3), 0) + XCTAssertEqual(missingNumber.findMissingNumber(of: [1, 2, 3], lenght: 3), 0) + XCTAssertEqual(missingNumber.findMissingNumber(of: [0, 1, 4, 3, 5, 6, 7, 8, 9, 10], lenght: 10), 2) + XCTAssertEqual(missingNumber.findMissingNumber(of: [0, 1, 4, 3, 5, 6, 7, 8, 9], lenght: 9), 2) + XCTAssertEqual(missingNumber.findMissingNumber(of: [0, 1, 4, 3, 5, 6, 7, 8, 2], lenght: 9), 9) + XCTAssertEqual(missingNumber.findMissingNumber(of: [0, 1, 4, 3, 5, 6, 7, 8], lenght: 9), nil) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/MissingTwoTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/MissingTwoTests.swift new file mode 100644 index 0000000..9e965ec --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/MissingTwoTests.swift @@ -0,0 +1,27 @@ +// +// MissingTwoTests.swift +// +// +// Created by Stefan Jaindl on 13.12.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class MissingTwoTests: XCTestCase { + + open func testMissing() { + let misser = MissingTwo() + + XCTAssertEqual(misser.findMissingNumber(in: [1, 5, 2, 3]), 4) + XCTAssertEqual(misser.findMissingTwoNumbers(in: [1, 5, 3]), [2, 4]) + XCTAssertEqual(misser.findMissingTwoNumbers(in: [1, 5, 3, 7, 8, 9, 2]), [4, 6]) + XCTAssertEqual(misser.findMissingNumber(in: [2]), 1) + XCTAssertEqual(misser.findMissingNumber(in: []), nil) + XCTAssertEqual(misser.findMissingTwoNumbers(in: []), []) + + XCTAssertEqual(misser.findMissingTwoNumbersWithLessSpace(in: [1, 5, 3]), [2, 4]) + XCTAssertEqual(misser.findMissingTwoNumbersWithLessSpace(in: [1, 5, 3, 7, 8, 9, 2]), [4, 6]) + XCTAssertEqual(misser.findMissingTwoNumbersWithLessSpace(in: []), []) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/NamesMergerTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/NamesMergerTests.swift new file mode 100644 index 0000000..a61ac08 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/NamesMergerTests.swift @@ -0,0 +1,65 @@ +// +// NamesMergerTests.swift +// +// +// Created by Stefan Jaindl on 06.12.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class NamesMergerTests: XCTestCase { + + open func testMerging1() throws { + let merger = NamesMerger() + + let frequencies = [ + NameFrequency(name: "John", frequency: 15), + NameFrequency(name: "Jon", frequency: 12), + NameFrequency(name: "Chris", frequency: 13), + NameFrequency(name: "Kris", frequency: 4), + NameFrequency(name: "Christopher", frequency: 19), + NameFrequency(name: "Christof", frequency: 10), + ] + + let mappings = [("Jon", "John"), ("John", "Johnny"), ("Chris", "Kris"), ("Chris", "Christopher"), ("Christopher", "Christof")] + + let mergedFrequencies = try merger.mergeLists(nameFrequencies: frequencies, nameMappings: mappings) + + let expectedMergedFrequencies = [ + NameFrequency(name: "John", frequency: 27), + NameFrequency(name: "Chris", frequency: 46) + ] + + XCTAssertEqual(mergedFrequencies, expectedMergedFrequencies) + } + + open func testMerging2() throws { + let merger = NamesMerger() + + let frequencies = [ + NameFrequency(name: "John", frequency: 10), + NameFrequency(name: "Jon", frequency: 3), + NameFrequency(name: "Davis", frequency: 2), + NameFrequency(name: "Kari", frequency: 3), + NameFrequency(name: "Johnny", frequency: 11), + NameFrequency(name: "Carlton", frequency: 8), + NameFrequency(name: "Carleton", frequency: 2), + NameFrequency(name: "Jonathan", frequency: 9), + NameFrequency(name: "Carrie", frequency: 5), + ] + + let mappings = [("Jonathan", "John"), ("Jon", "Johnny"), ("Johnny", "John"), ("Kari", "Carrie"), ("Carleton", "Carlton")] + + let mergedFrequencies = try merger.mergeLists(nameFrequencies: frequencies, nameMappings: mappings) + + let expectedMergedFrequencies = [ + NameFrequency(name: "John", frequency: 33), + NameFrequency(name: "Davis", frequency: 2), + NameFrequency(name: "Kari", frequency: 8), + NameFrequency(name: "Carlton", frequency: 10) + ] + + XCTAssertEqual(mergedFrequencies, expectedMergedFrequencies) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/OneAwayCheckerTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/OneAwayCheckerTests.swift new file mode 100644 index 0000000..84a4ffd --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/OneAwayCheckerTests.swift @@ -0,0 +1,22 @@ +// +// PalindromePermutationTests.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 14.05.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class OneAwayCheckerTests: XCTestCase { + + open func testOneAwayChecker() { + let oneAwayChecker = OneAwayChecker() + + XCTAssertTrue(oneAwayChecker.isOneAway(string1: "pale", string2: "ple")) + XCTAssertTrue(oneAwayChecker.isOneAway(string1: "pales", string2: "pale")) + XCTAssertFalse(oneAwayChecker.isOneAway(string1: "pale", string2: "bake")) + XCTAssertFalse(oneAwayChecker.isOneAway(string1: "test", string2: "test123")) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/PalindromePermutationTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/PalindromePermutationTests.swift new file mode 100644 index 0000000..4ae3e70 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/PalindromePermutationTests.swift @@ -0,0 +1,22 @@ +// +// PalindromePermutationTests.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 14.05.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class PalindromePermutationTests: XCTestCase { + + open func testPalindromePermutations() { + let palindromePermuation = PalindromePermutation(alphabetSize: 256) //Extended ASCII alphabet + + let testString1 = "TACTCOA" + let testString2 = "BEER" + XCTAssertTrue(palindromePermuation.isPalindromePermutation(input: testString1)) + XCTAssertFalse(palindromePermuation.isPalindromePermutation(input: testString2)) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/PatternMatcherTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/PatternMatcherTests.swift new file mode 100644 index 0000000..5c9e978 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/PatternMatcherTests.swift @@ -0,0 +1,24 @@ +// +// PatternMatcherTests.swift +// +// +// Created by Stefan Jaindl on 28.11.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class PatternMatcherTests: XCTestCase { + + open func testPatterns() { + let matcher = PatternMatcher() + + XCTAssertTrue(matcher.matches(value: "catcatgogo", pattern: [.a, .a, .b, .b])) + XCTAssertTrue(matcher.matches(value: "catcatgogo", pattern: [.b, .b, .a, .a])) + XCTAssertTrue(matcher.matches(value: "catcatgogo", pattern: [.b, .a])) + XCTAssertTrue(matcher.matches(value: "catcatgogo", pattern: [.b])) + XCTAssertFalse(matcher.matches(value: "catcatgogo", pattern: [])) + XCTAssertFalse(matcher.matches(value: "catcatgogoo", pattern: [.a, .a, .b, .b])) + XCTAssertFalse(matcher.matches(value: "", pattern: [.a])) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/PondSizesTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/PondSizesTests.swift new file mode 100644 index 0000000..71cecbb --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/PondSizesTests.swift @@ -0,0 +1,22 @@ +// +// PondSizesTests.swift +// +// +// Created by Stefan Jaindl on 29.11.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class PondSizesTests: XCTestCase { + + open func testPatterns() { + let pondSizes = PondSizes() + let matrix = [[0, 2, 1, 0], + [0, 1, 0, 1], + [1, 1, 0, 1], + [0, 1, 0, 1]] + + XCTAssertEqual(pondSizes.sizes(matrix: matrix), [2, 4, 1]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/ShortestSuperSequenceTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/ShortestSuperSequenceTests.swift new file mode 100644 index 0000000..3a8c555 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/ShortestSuperSequenceTests.swift @@ -0,0 +1,27 @@ +// +// ShortestSuperSequenceTests.swift +// +// +// Created by Stefan Jaindl on 12.12.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class ShortestSuperSequenceTests: XCTestCase { + + open func testShortestSequence() { + let sequencer = ShortestSuperSequence() + let biggerArray = [1, 7, 5, 2, 10, 1, 2, 8, 11, 3, 2] + + XCTAssertEqual(sequencer.findShortestSuperSequence(of: [1, 7, 5], in: biggerArray), SuperSequenceIndexPair(start: 0, end: 2)) + XCTAssertEqual(sequencer.findShortestSuperSequence(of: [1, 5, 7], in: biggerArray), SuperSequenceIndexPair(start: 0, end: 2)) + XCTAssertEqual(sequencer.findShortestSuperSequence(of: [7, 5, 2], in: biggerArray), SuperSequenceIndexPair(start: 1, end: 3)) + XCTAssertEqual(sequencer.findShortestSuperSequence(of: [2, 7, 1], in: biggerArray), SuperSequenceIndexPair(start: 0, end: 3)) + XCTAssertEqual(sequencer.findShortestSuperSequence(of: [8, 11, 3], in: biggerArray), SuperSequenceIndexPair(start: 7, end: 9)) + XCTAssertEqual(sequencer.findShortestSuperSequence(of: [3, 11, 8], in: biggerArray), SuperSequenceIndexPair(start: 7, end: 9)) + XCTAssertEqual(sequencer.findShortestSuperSequence(of: [8, 3, 1], in: biggerArray), SuperSequenceIndexPair(start: 5, end: 9)) + XCTAssertEqual(sequencer.findShortestSuperSequence(of: [8, 2, 2], in: biggerArray), SuperSequenceIndexPair(start: 3, end: 7)) + XCTAssertEqual(sequencer.findShortestSuperSequence(of: [1, 2, 2, 2, 1], in: biggerArray), SuperSequenceIndexPair(start: 0, end: 10)) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/SmallestDifferenceTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/SmallestDifferenceTests.swift new file mode 100644 index 0000000..7f91ba7 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/SmallestDifferenceTests.swift @@ -0,0 +1,19 @@ +// +// SmallestDifferenceTests.swift +// +// +// Created by Stefan Jaindl on 17.11.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class SmallestDifferenceTests: XCTestCase { + + open func testSmallestDifference() { + let smallestDifferenceFinder = SmallestDifference() + + XCTAssertEqual(smallestDifferenceFinder.smallestDifference(first: [], second: []), nil) + XCTAssertEqual(smallestDifferenceFinder.smallestDifference(first: [1, 3, 15, 11, 2], second: [23, 127, 235, 19, 8]), 3) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/SpaceInserterTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/SpaceInserterTests.swift new file mode 100644 index 0000000..e199ef6 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/SpaceInserterTests.swift @@ -0,0 +1,21 @@ +// +// SpaceInserterTests.swift +// +// +// Created by Stefan Jaindl on 10.12.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class SpaceInserterTests: XCTestCase { + + open func testSpaceInserter() { + let spacer = SpaceInserter() + + let spaced = spacer.insertSpaces(into: "ihaveaccidentallydeletedallthespacesintheallmightystring", + wordsDict: ["i", "have", "accidentally", "deleted", "all", "spaces", "allmighty", "string", "in"]) + + XCTAssertEqual(spaced, "i have accidentally deleted all the spaces in the allmighty string") + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/SparseMatrixTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/SparseMatrixTests.swift new file mode 100644 index 0000000..e980df6 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/SparseMatrixTests.swift @@ -0,0 +1,29 @@ +// +// SparseMatrixTests.swift +// +// +// Created by Stefan Jaindl on 23.12.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class SparseMatrixTests: XCTestCase { + + open func testSparseMatrix() { + let sparser = SparseMatrix() + + let documents = [ + SparseDocument(id: 13, words: [14, 15, 100, 9, 3]), + SparseDocument(id: 16, words: [12, 1, 9, 3, 5]), + SparseDocument(id: 19, words: [15, 29, 2, 6, 8, 7]), + SparseDocument(id: 24, words: [7, 10]) + ] + + let similarities = sparser.similarity(of: documents) + + XCTAssertTrue(similarities.contains(Similarity(documents: DocumentPair(firstId: 13, secondId: 16), similarity: 0.25))) + XCTAssertTrue(similarities.contains(Similarity(documents: DocumentPair(firstId: 13, secondId: 19), similarity: 0.1))) + XCTAssertTrue(similarities.contains(Similarity(documents: DocumentPair(firstId: 19, secondId: 24), similarity: 0.14285714285714285))) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/StringCompresserTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/StringCompresserTests.swift new file mode 100644 index 0000000..645d247 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/StringCompresserTests.swift @@ -0,0 +1,22 @@ +// +// PalindromePermutationTests.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 14.05.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class StringCompresserTests: XCTestCase { + + open func testStringCompresser() { + let stringCompresser = StringCompresser() + + XCTAssertEqual(stringCompresser.compress(string: "abc"), "abc") + XCTAssertEqual(stringCompresser.compress(string: "aabcccccaaa"), "a2b1c5a3") + XCTAssertEqual(stringCompresser.compress(string: "aabbcc"), "aabbcc") + XCTAssertEqual(stringCompresser.compress(string: "aabbccc"), "a2b2c3") + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/StringPermuationCheckerTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/StringPermuationCheckerTests.swift new file mode 100644 index 0000000..46a5aa9 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/StringPermuationCheckerTests.swift @@ -0,0 +1,32 @@ +// +// StringPermutationCheckerTests.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 09.05.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class StringPermutationCheckerTests: XCTestCase { + + open func testStringPermutations() { + let permuationChecker = StringPermuationChecker(alphabetSize: 256) //Extended ASCII alphabet + + var testString1 = "MAOAM" + var testString2 = "AMAMO" + XCTAssertTrue(permuationChecker.isPermutationOfByCharArray(first: testString1, second: testString2)) + XCTAssertTrue(permuationChecker.isPermutationOfBySorting(first: testString1, second: testString2)) + + testString1 = "MAO" + testString2 = "OMO" + XCTAssertFalse(permuationChecker.isPermutationOfByCharArray(first: testString1, second: testString2)) + XCTAssertFalse(permuationChecker.isPermutationOfBySorting(first: testString1, second: testString2)) + + testString1 = "MAO" + testString2 = "MA" + XCTAssertFalse(permuationChecker.isPermutationOfByCharArray(first: testString1, second: testString2)) + XCTAssertFalse(permuationChecker.isPermutationOfBySorting(first: testString1, second: testString2)) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/StringRotationTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/StringRotationTests.swift new file mode 100644 index 0000000..b1d18fa --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/StringRotationTests.swift @@ -0,0 +1,22 @@ +// +// StringRotationTests.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 20.05.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class StringRotationTests: XCTestCase { + + open func testStringRotation() { + let rotationTester = StringRotation() + + XCTAssertTrue(rotationTester.isRotation(firstString: "waterbottle", secondString: "bottlewater")) + XCTAssertTrue(rotationTester.isRotation(firstString: "waterbottle", secondString: "terbottlewa")) + XCTAssertFalse(rotationTester.isRotation(firstString: "waterbottle", secondString: "terbottlew")) + XCTAssertFalse(rotationTester.isRotation(firstString: "waterbottle", secondString: "waterbottle")) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/SumSwapperTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/SumSwapperTests.swift new file mode 100644 index 0000000..eca894c --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/SumSwapperTests.swift @@ -0,0 +1,20 @@ +// +// SumSwapperTests.swift +// +// +// Created by Stefan Jaindl on 30.11.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class SumSwapperTests: XCTestCase { + + open func testSumSwapper() { + let swapper = SumSwapper() + + let first = [4, 2, 2, 1, 1, 2] //sum: 12 + let second = [3, 7, 3, 3] //sum 16 + XCTAssertEqual(swapper.swap(first: first, second: second), Swappable(first: 1, second: 3)) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/T9KeyboardTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/T9KeyboardTests.swift new file mode 100644 index 0000000..1f677b2 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/T9KeyboardTests.swift @@ -0,0 +1,29 @@ +// +// T9KeyboardTests.swift +// +// +// Created by Stefan Jaindl on 30.11.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class T9KeyboardTests: XCTestCase { + + open func testT9Keyboard() throws { + let validWords = Trie() + + validWords.insert(word: "adgj") + validWords.insert(word: "agdjux") + validWords.insert(word: "agd") + validWords.insert(word: "adgjaaa") + + let keyboard = T9Keyboard(validWords: validWords) + + let digits = [2, 3, 4, 5] + + let autocompleted = try keyboard.autocomplete(for: digits) + + XCTAssertEqual(autocompleted, ["adgj", "adgjaaa"]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/UniqueStringSearchTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/UniqueStringSearchTests.swift new file mode 100644 index 0000000..73b6f1e --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/UniqueStringSearchTests.swift @@ -0,0 +1,27 @@ +// +// UniqueStringSearch.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 03.05.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class UniqueStringSearchTests: XCTestCase { + + open func testUniqueStringSearch() { + let stringSearch = UniqueStringSearch(alphabetSize: 256) //Extended ASCII alphabet + + let testString1 = "test" + XCTAssertFalse(stringSearch.isUniqueStringWithHash(input: testString1)) + XCTAssertFalse(stringSearch.isUniqueStringWithBitVector(input: testString1)) + XCTAssertFalse(stringSearch.isUniqueStringWithSorting(input: testString1)) + + let testString2 = "string2" + XCTAssertTrue(stringSearch.isUniqueStringWithHash(input: testString2)) + XCTAssertTrue(stringSearch.isUniqueStringWithBitVector(input: testString2)) + XCTAssertTrue(stringSearch.isUniqueStringWithSorting(input: testString2)) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/UrlifierTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/UrlifierTests.swift new file mode 100644 index 0000000..2122ff7 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/UrlifierTests.swift @@ -0,0 +1,28 @@ +// +// UrlifierTests.swift +// DataStructuresAlgorithms +// +// Created by Stefan Jaindl on 10.05.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class UrlifierTests: XCTestCase { + + open func testUrlify() { + let urlifier = Urlifier() + + var string: [Character] = Array("Mr DJ") + urlifier.urlify(&string) + XCTAssertEqual(Array("Mr%20DJ"), string) + + var string2: [Character] = Array("Mr John Smith") + urlifier.urlify(&string2) + XCTAssertEqual(Array("Mr%20John%20Smith"), string2) + + var emptyString: [Character] = [] + urlifier.urlify(&emptyString) + XCTAssertEqual(Array(""), emptyString) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/WordFrequencyCounterTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/WordFrequencyCounterTests.swift new file mode 100644 index 0000000..4ab960a --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/WordFrequencyCounterTests.swift @@ -0,0 +1,23 @@ +// +// WordFrequencyCounterTests.swift +// +// +// Created by Stefan Jaindl on 15.11.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class WordFrequencyCounterTests: XCTestCase { + + open func testCounts() { + let counter = WordFrequencyCounter(words: ["test", " test ", "xxx", "Test ", "unique", "special", "special"]) + + XCTAssertEqual(counter.count(of: "test"), 3) + XCTAssertEqual(counter.count(of: "xxx"), 1) + XCTAssertEqual(counter.count(of: "test_"), 0) + XCTAssertEqual(counter.count(of: "special"), 2) + XCTAssertEqual(counter.count(of: "unique"), 1) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/WordRectangleTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/WordRectangleTests.swift new file mode 100644 index 0000000..b24c5c8 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/WordRectangleTests.swift @@ -0,0 +1,37 @@ +// +// WordRectangleTests.swift +// +// +// Created by Stefan Jaindl on 22.12.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class WordRectangleTests: XCTestCase { + + open func testWordRectangle() { + let maxRectangler = WordRectangle() + + /* + Rectangle: + ab + cd + */ + var words = ["ab", "ac", "cd", "bd"] + var max = maxRectangler.maxRectangle(of: words) + XCTAssertTrue(max == ["ab", "cd"] || max == ["ac", "bd"]) + + /* + Rectangle: + gaus + reto + arte + zooy + */ + words = ["reto", "gaus", "graz", "soey", "maoam", "valid", "mamam", "", "arte", "zooy", "utto", "aero"] + max = maxRectangler.maxRectangle(of: words) + XCTAssertTrue(max == ["gaus", "reto", "arte", "zooy"] || max == ["graz", "aero", "utto", "soey"]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/WordTransformerTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/WordTransformerTests.swift new file mode 100644 index 0000000..1b3583d --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/WordTransformerTests.swift @@ -0,0 +1,20 @@ +// +// WordTransformerTests.swift +// +// +// Created by Stefan Jaindl on 18.12.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class WordTransformerTests: XCTestCase { + + open func testTransform() { + let words: Set = ["damp", "lamp", "limp", "lime", "like"] + let transformer = WordTransformer(words: words) + + XCTAssertEqual(transformer.transform("damp", into: "like"), ["damp", "lamp", "limp", "lime", "like"]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/XMLEncoderTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/XMLEncoderTests.swift new file mode 100644 index 0000000..6417bbe --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/XMLEncoderTests.swift @@ -0,0 +1,28 @@ +// +// XMLEncoderTests.swift +// +// +// Created by Stefan Jaindl on 24.11.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class XMLEncoderTests: XCTestCase { + + open func testXMLEncoding() throws { + let encoder = XMLEncoder() + + let xml = "Some Message" + let attributesMapping: [String: Int] = [ + "family": 1, + "firstName": 2, + "lastname": 3, + "state": 4, + "person": 5 + ] + + XCTAssertEqual(try encoder.encode(xml: xml, attributesMapping: attributesMapping), "1 3 McDowell 4 CA 5 2 Gayle Some Message 0 0") + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/ZeroMatrixTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/ZeroMatrixTests.swift new file mode 100644 index 0000000..3e17436 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/ArraysAndStrings/ZeroMatrixTests.swift @@ -0,0 +1,31 @@ +// +// ZeroMatrixTests.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 19.05.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class ZeroMatrixTests: XCTestCase { + + open func testZeroMatrix() { + let zeroer = ZeroMatrix() + + var matrix = [[1, 2, 0], + [4, 5, 6], + [7, 8, 0], + [9, 10, 11]] + + let zeroedMatrix = [[0, 0, 0], + [4, 5, 0], + [0, 0, 0], + [9, 10, 0]] + + zeroer.zeroMatrix(matrix: &matrix) + + XCTAssertEqual(matrix, zeroedMatrix) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/AdderTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/AdderTests.swift new file mode 100644 index 0000000..e448e44 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/AdderTests.swift @@ -0,0 +1,26 @@ +// +// AdderTests.swift +// +// +// Created by Stefan Jaindl on 03.12.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class AdderTests: XCTestCase { + + open func testAddWithoutPlus() { + let adder = Adder() + XCTAssertEqual(adder.addWithoutPlus(first: 0, second: 0), 0) + XCTAssertEqual(adder.addWithoutPlus(first: 0, second: 1), 1) + XCTAssertEqual(adder.addWithoutPlus(first: 1, second: 0), 1) + XCTAssertEqual(adder.addWithoutPlus(first: 1, second: 2), 3) + XCTAssertEqual(adder.addWithoutPlus(first: 2, second: 2), 4) + XCTAssertEqual(adder.addWithoutPlus(first: 3, second: 3), 6) + XCTAssertEqual(adder.addWithoutPlus(first: 4, second: 7), 11) + XCTAssertEqual(adder.addWithoutPlus(first: 25, second: 13), 38) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/BinaryNearbySearcherTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/BinaryNearbySearcherTests.swift new file mode 100644 index 0000000..930c8ac --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/BinaryNearbySearcherTests.swift @@ -0,0 +1,34 @@ +// +// BinaryNearbySearcherTests.swift +// +// +// Created by Stefan Jaindl on 29.07.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class BinaryNearbySearcherTests: XCTestCase { + + open func testNearestBinaryNumbers() { + let searcher = BinaryNearbySearcher() + + var result = searcher.nearestBinaryNumbers(number: 0b1011) + XCTAssertEqual(result.0, 0b1101) + XCTAssertEqual(result.1, 0b0111) + + result = searcher.nearestBinaryNumbers(number: 0b0111) + XCTAssertEqual(result.0, 0b1011) + XCTAssertEqual(result.1, nil) + + result = searcher.nearestBinaryNumbers(number: 0b11011) + XCTAssertEqual(result.0, 0b11101) + XCTAssertEqual(result.1, 0b10111) + + result = searcher.nearestBinaryNumbers(number: 0b1100) + XCTAssertEqual(result.0, 0b10001) + XCTAssertEqual(result.1, 0b1010) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/BinaryNumberConverterTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/BinaryNumberConverterTests.swift new file mode 100644 index 0000000..38da9aa --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/BinaryNumberConverterTests.swift @@ -0,0 +1,23 @@ +// +// BinaryNumberConverterTests.swift +// +// +// Created by Stefan Jaindl on 31.07.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class BinaryNumberConverterTests: XCTestCase { + + open func testBinaryNumberConverter() { + let from = 0b1001101 + let to = 0b0101011 + + let converter = BinaryNumberConverter() + let bitDifferentCount = converter.binaryConversionCount(from: from, to: to) + XCTAssertEqual(bitDifferentCount, 4) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/BinaryToStringConverterTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/BinaryToStringConverterTests.swift new file mode 100644 index 0000000..c347913 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/BinaryToStringConverterTests.swift @@ -0,0 +1,20 @@ +// +// BinaryToStringConverterTests.swift +// +// +// Created by Stefan Jaindl on 27.07.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class BinaryToStringConverterTests: XCTestCase { + + open func testDoubleConversion() { + let converter = BinaryToStringConverter() + + let number = 0.625 + let binary = converter.binaryRepresentation(of: number) + XCTAssertEqual(binary, ".101") + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/BitFlipperTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/BitFlipperTests.swift new file mode 100644 index 0000000..688ddf1 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/BitFlipperTests.swift @@ -0,0 +1,21 @@ +// +// BitFlipperTests.swift +// +// +// Created by Stefan Jaindl on 27.07.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class BitFlipperTests: XCTestCase { + + open func testLongestOneBitSequence() { + let bitFlipper = BitFlipper() + + let input = 0b11011101111 //1775 + let longestSequence = bitFlipper.findLongestOneBitSequenceByFlippingMaxOneBit(input: input) + + XCTAssertEqual(longestSequence, 8) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/BitInserterTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/BitInserterTests.swift new file mode 100644 index 0000000..e438dc3 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/BitInserterTests.swift @@ -0,0 +1,23 @@ +// +// BitInserterTests.swift +// +// +// Created by Stefan Jaindl on 23.07.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class BitInserterTests: XCTestCase { + + open func testBitsInsertion() { + let bitInserter = BitInserter() + + let baseNumber = 0b10000010000 + let numberToInsert = 0b10011 + + let newNumber = bitInserter.insert(numberToInsert, into: baseNumber, fromBitIndex: 2, toBitIndex: 6) + + XCTAssertEqual(newNumber, 0b10001001100) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/NumberMaxTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/NumberMaxTests.swift new file mode 100644 index 0000000..19b1d12 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/NumberMaxTests.swift @@ -0,0 +1,23 @@ +// +// NumberMaxTests.swift +// +// +// Created by Stefan Jaindl on 18.11.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class NumberMaxTests: XCTestCase { + + open func testMaxNumber() { + let maxNumberTester = NumberMax() + + XCTAssertEqual(maxNumberTester.max(first: 8, second: 12), 12) + XCTAssertEqual(maxNumberTester.max(first: 8, second: 0), 8) + XCTAssertEqual(maxNumberTester.max(first: 8, second: 8), 8) + XCTAssertEqual(maxNumberTester.max(first: -10, second: -8), -8) + XCTAssertEqual(maxNumberTester.max(first: 0, second: 0), 0) + XCTAssertEqual(maxNumberTester.max(first: Int.max, second: 12), Int.max) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/NumberSwapperTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/NumberSwapperTests.swift new file mode 100644 index 0000000..0ae472e --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/NumberSwapperTests.swift @@ -0,0 +1,28 @@ +// +// NumberSwapperTests.swift +// +// +// Created by Stefan Jaindl on 15.11.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class NumberSwapperTests: XCTestCase { + + open func testSwap() { + let swapper = Swapper() + + var first = 7 + var second = 3 + swapper.swapInline(&first, &second) + XCTAssertEqual(first, 3) + XCTAssertEqual(second, 7) + + first = -4 + second = 1777 + swapper.swapInline(&first, &second) + XCTAssertEqual(first, 1777) + XCTAssertEqual(second, -4) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/OperationsTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/OperationsTests.swift new file mode 100644 index 0000000..274cede --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/OperationsTests.swift @@ -0,0 +1,30 @@ +// +// OperationsTests.swift +// +// +// Created by Stefan Jaindl on 21.11.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class OperationsTests: XCTestCase { + + open func testOperations() { + let operations = Operations() + + XCTAssertEqual(operations.add(first: 4, second: 5), 9) + + XCTAssertEqual(operations.multiply(first: 4, second: 5), 20) + XCTAssertEqual(operations.multiply(first: -2, second: 50), -100) + XCTAssertEqual(operations.multiply(first: -4, second: -10), 40) + + XCTAssertEqual(operations.subtract(first: 5, second: 10), -5) + XCTAssertEqual(operations.subtract(first: 10, second: 5), 5) + XCTAssertEqual(operations.divide(first: 10, second: 5), 2) + XCTAssertEqual(operations.divide(first: 10, second: 4), 2) + XCTAssertEqual(operations.divide(first: 100, second: -5), -20) + XCTAssertEqual(operations.divide(first: -100, second: -5), 20) + } +} + diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/PairwiseSwapTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/PairwiseSwapTests.swift new file mode 100644 index 0000000..5bbd12f --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/PairwiseSwapTests.swift @@ -0,0 +1,20 @@ +// +// PairwiseSwapTests.swift +// +// +// Created by Stefan Jaindl on 03.08.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class PairwiseSwapTests: XCTestCase { + public func testSwap() { + let number = 0b0100111001 + + let swapper = PairwiseSwap() + let swapped = swapper.swapOddEven(number: number) + + XCTAssertEqual(swapped, 0b1000110110) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/ScreenManipulatorTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/ScreenManipulatorTests.swift new file mode 100644 index 0000000..65d03d3 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/BitManipulation/ScreenManipulatorTests.swift @@ -0,0 +1,21 @@ +// +// ScreenManipulatorTests.swift +// +// +// Created by Stefan Jaindl on 03.08.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class ScreenManipulatorTests: XCTestCase { + public func testDrawLine() { + var screen = [UInt8](repeating: 0, count: 2 * 5) + + let screenManipulator = ScreenManipulator() + screenManipulator.drawLine(screen: &screen, width: 16, x1: 2, x2: 4, y: 1) + + let expected: [UInt8] = [0, 0, 0b00111000, 0, 0, 0, 0, 0, 0, 0] + XCTAssertEqual(expected, screen) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/BSTSequenceTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/BSTSequenceTests.swift new file mode 100644 index 0000000..f929a64 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/BSTSequenceTests.swift @@ -0,0 +1,37 @@ +// +// BSTSequenceTests.swift +// +// +// Created by Stefan Jaindl on 06.07.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class BSTSequenceTests: XCTestCase { + + open func testBSTSequence() { + //setup input Tree + let root = SimpleTreeNode(value: 10) + root.left = SimpleTreeNode(value: 8) + root.left?.left = SimpleTreeNode(value: 4) + root.left?.right = SimpleTreeNode(value: 9) + + root.right = SimpleTreeNode(value: 12) + root.right?.right = SimpleTreeNode(value: 14) + root.right?.left = SimpleTreeNode(value: 11) + + let sequencer = BSTSequence() + let possibleSequences = sequencer.BSTSequences(root: root) + + var values: [[Int]] = [] + possibleSequences.forEach { sequence in + let singleValue = sequence.map { $0.value } + values.append(singleValue) + } + + XCTAssertEqual(values, [[10, 8, 4, 9, 12, 11, 14], [10, 8, 4, 12, 9, 11, 14], [10, 8, 4, 12, 11, 9, 14], [10, 8, 4, 12, 11, 14, 9], [10, 8, 12, 4, 9, 11, 14], [10, 8, 12, 4, 11, 9, 14], [10, 8, 12, 4, 11, 14, 9], [10, 8, 12, 11, 4, 9, 14], [10, 8, 12, 11, 4, 14, 9], [10, 8, 12, 11, 14, 4, 9], [10, 12, 8, 4, 9, 11, 14], [10, 12, 8, 4, 11, 9, 14], [10, 12, 8, 4, 11, 14, 9], [10, 12, 8, 11, 4, 9, 14], [10, 12, 8, 11, 4, 14, 9], [10, 12, 8, 11, 14, 4, 9], [10, 12, 11, 8, 4, 9, 14], [10, 12, 11, 8, 4, 14, 9], [10, 12, 11, 8, 14, 4, 9], [10, 12, 11, 14, 8, 4, 9], [10, 8, 4, 9, 12, 14, 11], [10, 8, 4, 12, 9, 14, 11], [10, 8, 4, 12, 14, 9, 11], [10, 8, 4, 12, 14, 11, 9], [10, 8, 12, 4, 9, 14, 11], [10, 8, 12, 4, 14, 9, 11], [10, 8, 12, 4, 14, 11, 9], [10, 8, 12, 14, 4, 9, 11], [10, 8, 12, 14, 4, 11, 9], [10, 8, 12, 14, 11, 4, 9], [10, 12, 8, 4, 9, 14, 11], [10, 12, 8, 4, 14, 9, 11], [10, 12, 8, 4, 14, 11, 9], [10, 12, 8, 14, 4, 9, 11], [10, 12, 8, 14, 4, 11, 9], [10, 12, 8, 14, 11, 4, 9], [10, 12, 14, 8, 4, 9, 11], [10, 12, 14, 8, 4, 11, 9], [10, 12, 14, 8, 11, 4, 9], [10, 12, 14, 11, 8, 4, 9], [10, 8, 9, 4, 12, 11, 14], [10, 8, 9, 12, 4, 11, 14], [10, 8, 9, 12, 11, 4, 14], [10, 8, 9, 12, 11, 14, 4], [10, 8, 12, 9, 4, 11, 14], [10, 8, 12, 9, 11, 4, 14], [10, 8, 12, 9, 11, 14, 4], [10, 8, 12, 11, 9, 4, 14], [10, 8, 12, 11, 9, 14, 4], [10, 8, 12, 11, 14, 9, 4], [10, 12, 8, 9, 4, 11, 14], [10, 12, 8, 9, 11, 4, 14], [10, 12, 8, 9, 11, 14, 4], [10, 12, 8, 11, 9, 4, 14], [10, 12, 8, 11, 9, 14, 4], [10, 12, 8, 11, 14, 9, 4], [10, 12, 11, 8, 9, 4, 14], [10, 12, 11, 8, 9, 14, 4], [10, 12, 11, 8, 14, 9, 4], [10, 12, 11, 14, 8, 9, 4], [10, 8, 9, 4, 12, 14, 11], [10, 8, 9, 12, 4, 14, 11], [10, 8, 9, 12, 14, 4, 11], [10, 8, 9, 12, 14, 11, 4], [10, 8, 12, 9, 4, 14, 11], [10, 8, 12, 9, 14, 4, 11], [10, 8, 12, 9, 14, 11, 4], [10, 8, 12, 14, 9, 4, 11], [10, 8, 12, 14, 9, 11, 4], [10, 8, 12, 14, 11, 9, 4], [10, 12, 8, 9, 4, 14, 11], [10, 12, 8, 9, 14, 4, 11], [10, 12, 8, 9, 14, 11, 4], [10, 12, 8, 14, 9, 4, 11], [10, 12, 8, 14, 9, 11, 4], [10, 12, 8, 14, 11, 9, 4], [10, 12, 14, 8, 9, 4, 11], [10, 12, 14, 8, 9, 11, 4], [10, 12, 14, 8, 11, 9, 4], [10, 12, 14, 11, 8, 9, 4]]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/BalancedTreeCheckerTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/BalancedTreeCheckerTests.swift new file mode 100644 index 0000000..c8e87d3 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/BalancedTreeCheckerTests.swift @@ -0,0 +1,53 @@ +// +// BalancedTreeCheckerTests.swift +// +// +// Created by Stefan Jaindl on 24.06.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class BalancedTreeCheckerTests: XCTestCase { + + open func testUnbalancedTree() { + //setup input Tree + let root = SimpleTreeNode(value: 1) + root.left = SimpleTreeNode(value: 2) + root.right = SimpleTreeNode(value: 2) + root.left?.left = SimpleTreeNode(value: 3) + root.left?.left?.left = SimpleTreeNode(value: 4) + root.left?.right = SimpleTreeNode(value: 3) + root.left?.right?.left = SimpleTreeNode(value: 4) + root.left?.right?.right = SimpleTreeNode(value: 4) + root.left?.right?.right?.left = SimpleTreeNode(value: 5) + + root.right?.right = SimpleTreeNode(value: 3) + root.right?.left = SimpleTreeNode(value: 3) + + let balancedChecker = BalancedTreeChecker() + XCTAssertFalse(balancedChecker.isBalanced(root: root)) + } + + open func testBalancedTree() { + //setup input Tree + let root = SimpleTreeNode(value: 1) + root.left = SimpleTreeNode(value: 2) + root.right = SimpleTreeNode(value: 2) + root.left?.left = SimpleTreeNode(value: 3) + root.left?.left?.left = SimpleTreeNode(value: 4) + root.left?.right = SimpleTreeNode(value: 3) + root.left?.right?.left = SimpleTreeNode(value: 4) + root.left?.right?.right = SimpleTreeNode(value: 4) + root.left?.right?.right?.left = SimpleTreeNode(value: 5) + + root.right?.right = SimpleTreeNode(value: 3) + root.right?.left = SimpleTreeNode(value: 3) + root.right?.right?.right = SimpleTreeNode(value: 4) //add node, so that height diff at root is only 1 + + let balancedChecker = BalancedTreeChecker() + XCTAssertTrue(balancedChecker.isBalanced(root: root)) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/FirstCommonAncestorTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/FirstCommonAncestorTests.swift new file mode 100644 index 0000000..6eeec54 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/FirstCommonAncestorTests.swift @@ -0,0 +1,45 @@ +// +// FirstCommonAncestorTests.swift +// +// +// Created by Stefan Jaindl on 30.06.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class FirstCommonAncestorTests: XCTestCase { + + open func testFirstCommonAncestor() { + //setup input Tree + let root = SimpleTreeNode(value: 1) + root.left = SimpleTreeNode(value: 4) + root.right = SimpleTreeNode(value: 9) + root.left?.left = SimpleTreeNode(value: 5) + root.left?.left?.left = SimpleTreeNode(value: 6) + root.left?.right = SimpleTreeNode(value: 7) + root.left?.right?.left = SimpleTreeNode(value: 2) + + root.right?.right = SimpleTreeNode(value: 3) + root.right?.right?.left = SimpleTreeNode(value: 8) + + let firstCommonAncestorChecker = FirstCommonAncestor() + + var firstCommonAncestor = firstCommonAncestorChecker.firstCommonAncestor(root: root, first: (root.left?.right?.left)!, second: (root.left?.left?.left)!)! + XCTAssertEqual(firstCommonAncestor.value, root.left?.value) //2, 6 -> 4 + + firstCommonAncestor = firstCommonAncestorChecker.firstCommonAncestor(root: root, first: (root.left?.right?.left)!, second: (root.left?.right?.left)!)! + XCTAssertEqual(firstCommonAncestor.value, root.left?.right?.left?.value) //2, 2 -> 2 + + firstCommonAncestor = firstCommonAncestorChecker.firstCommonAncestor(root: root, first: (root.left?.left?.left)!, second: (root.right?.right?.left)!)! + XCTAssertEqual(firstCommonAncestor.value, root.value) //6, 8 -> 1 + + firstCommonAncestor = firstCommonAncestorChecker.firstCommonAncestor(root: root, first: (root.left?.left?.left)!, second: (root.left?.right)!)! + XCTAssertEqual(firstCommonAncestor.value, root.left?.value) //6, 7 -> 4 + + firstCommonAncestor = firstCommonAncestorChecker.firstCommonAncestor(root: root, first: (root.left)!, second: (root.left?.left)!)! + XCTAssertEqual(firstCommonAncestor.value, root.left?.value) //4, 5 -> 4 + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/ListOfDepthsTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/ListOfDepthsTests.swift new file mode 100644 index 0000000..aa00897 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/ListOfDepthsTests.swift @@ -0,0 +1,63 @@ +// +// ListOfDepthsTests.swift +// +// +// Created by Stefan Jaindl on 24.06.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class ListOfDepthsTests: XCTestCase { + + open func testListOfDepths() { + //setup input Tree + let root = SimpleTreeNode(value: 3) + root.left = SimpleTreeNode(value: 4) + root.right = SimpleTreeNode(value: 2) + root.left?.left = SimpleTreeNode(value: 1) + root.left?.right = SimpleTreeNode(value: 3) + root.left?.right?.left = SimpleTreeNode(value: 2) + root.right?.right = SimpleTreeNode(value: 7) + root.right?.right?.right = SimpleTreeNode(value: 0) + + //setup expected result: + var resultList = Array>() + let rootList = SingleLinkedList() + let levelTwoList = SingleLinkedList() + let levelThreeList = SingleLinkedList() + let levelFourList = SingleLinkedList() + + rootList.add(node: SingleNode(val: 3)) + levelTwoList.add(node: SingleNode(val: 4)) + levelTwoList.add(node: SingleNode(val: 2)) + levelThreeList.add(node: SingleNode(val: 1)) + levelThreeList.add(node: SingleNode(val: 3)) + levelThreeList.add(node: SingleNode(val: 7)) + levelFourList.add(node: SingleNode(val: 2)) + levelFourList.add(node: SingleNode(val: 0)) + resultList.append(rootList) + resultList.append(levelTwoList) + resultList.append(levelThreeList) + resultList.append(levelFourList) + + let listOfDepth = ListOfDepths() + let result = listOfDepth.listOfDepths(root: root) + + XCTAssertEqual(result.count, 4) + XCTAssertEqual(result[0].count, rootList.count) + XCTAssertEqual(result[0].head?.val, rootList.head?.val) + XCTAssertEqual(result[1].count, levelTwoList.count) + XCTAssertEqual(result[1].head?.val, levelTwoList.head?.val) + XCTAssertEqual(result[1].head?.next?.val, levelTwoList.head?.next?.val) + XCTAssertEqual(result[2].count, levelThreeList.count) + XCTAssertEqual(result[2].head?.val, levelThreeList.head?.val) + XCTAssertEqual(result[2].head?.next?.val, levelThreeList.head?.next?.val) + XCTAssertEqual(result[2].head?.next?.next?.val, levelThreeList.head?.next?.next?.val) + XCTAssertEqual(result[3].count, levelFourList.count) + XCTAssertEqual(result[3].head?.val, levelFourList.head?.val) + XCTAssertEqual(result[3].head?.next?.val, levelFourList.head?.next?.val) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/MinTreeTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/MinTreeTests.swift new file mode 100644 index 0000000..7f5044e --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/MinTreeTests.swift @@ -0,0 +1,53 @@ +// +// MinTreeTests.swift +// +// +// Created by Stefan Jaindl on 23.06.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class MinTreeTests: XCTestCase { + + open func testMinTree() throws { + + let array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] + let minTreeBuilder = MinTree() + + let root = try minTreeBuilder.minTree(array: array) + + //Check in pre-order: + XCTAssertEqual(root.value, 10) + XCTAssertEqual(root.left?.value, 5) + XCTAssertEqual(root.left?.left?.value, 2) + XCTAssertEqual(root.left?.left?.left?.value, 1) + XCTAssertEqual(root.left?.left?.right?.value, 3) + XCTAssertEqual(root.left?.left?.right?.right?.value, 4) + XCTAssertEqual(root.left?.right?.value, 7) + XCTAssertEqual(root.left?.right?.left?.value, 6) + XCTAssertEqual(root.left?.right?.right?.value, 8) + XCTAssertEqual(root.left?.right?.right?.right?.value, 9) + + XCTAssertEqual(root.right?.value, 15) + XCTAssertEqual(root.right?.left?.value, 12) + XCTAssertEqual(root.right?.left?.left?.value, 11) + XCTAssertEqual(root.right?.left?.right?.value, 13) + XCTAssertEqual(root.right?.left?.right?.right?.value, 14) + XCTAssertEqual(root.right?.right?.value, 18) + XCTAssertEqual(root.right?.right?.left?.value, 16) + XCTAssertEqual(root.right?.right?.left?.right?.value, 17) + XCTAssertEqual(root.right?.right?.right?.value, 19) + XCTAssertEqual(root.right?.right?.right?.right?.value, 20) + + /* Result BST: + 10 (root) + 5 15 + 2 7 12 18 + 1 3 6 8 11 13 16 19 + 4 9 14 17 + */ + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/NodeSuccessorTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/NodeSuccessorTests.swift new file mode 100644 index 0000000..b86d099 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/NodeSuccessorTests.swift @@ -0,0 +1,51 @@ +// +// NodeSuccessorTests.swift +// +// +// Created by Stefan Jaindl on 28.06.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class NodeSuccessorTests: XCTestCase { + + open func testNodeSuccessorCaseOne() { + let root = setupTree() + + let nodeSuccessor = NodeSuccessor() + + XCTAssertEqual(nodeSuccessor.successor(root: root)?.value, 7) + } + + open func testNodeSuccessorCaseTwo() { + let root = setupTree() + + let nodeSuccessor = NodeSuccessor() + + XCTAssertEqual(nodeSuccessor.successor(root: root.left!.left!)?.value, 2) + } + + open func testNodeSuccessorCaseThree() { + let root = setupTree() + + let nodeSuccessor = NodeSuccessor() + + XCTAssertEqual(nodeSuccessor.successor(root: root.left!.right!)?.value, 5) + } + + private func setupTree() -> TreeNode { + let root = TreeNode(value: 5) + root.left = TreeNode(value: 2, parent: root) + root.left?.left = TreeNode(value: 1, parent: root.left) + root.left?.right = TreeNode(value: 3, parent: root.left) + + root.right = TreeNode(value: 8, parent: root) + root.right?.right = TreeNode(value: 10, parent: root.right) + root.right?.left = TreeNode(value: 7, parent: root.right) + + return root + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/PathCheckerTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/PathCheckerTests.swift new file mode 100644 index 0000000..b71bbf6 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/PathCheckerTests.swift @@ -0,0 +1,41 @@ +// +// PathCheckerTests.swift +// +// +// Created by Stefan Jaindl on 22.06.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class PathCheckerTests: XCTestCase { + + open func testPathChecker() { + let from = Vertice(id: 0) + let to = Vertice(id: 3) + let unconnected = Vertice(id: 6) + let graph = setupTestGraph(from: from, to: to, unconnected: unconnected) + + XCTAssertTrue(graph.pathExists(from: from, to: to)) + XCTAssertFalse(graph.pathExists(from: from, to: unconnected)) + } + + private func setupTestGraph(from: Vertice, to: Vertice, unconnected: Vertice) -> DirectedGraph { + let vertices: [Vertice] = [from, Vertice(id: 1), Vertice(id: 2), + to, Vertice(id: 4), Vertice(id: 5), unconnected] + let graph = DirectedGraph(vertices: vertices) + + graph.addEdge(v1: vertices[0], v2: vertices[1]) + graph.addEdge(v1: vertices[0], v2: vertices[2]) + graph.addEdge(v1: vertices[1], v2: vertices[2]) + graph.addEdge(v1: vertices[1], v2: vertices[3]) + graph.addEdge(v1: vertices[2], v2: vertices[1]) + graph.addEdge(v1: vertices[2], v2: vertices[4]) + graph.addEdge(v1: vertices[4], v2: vertices[5]) + graph.addEdge(v1: vertices[5], v2: vertices[3]) + + return graph + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/PathSumTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/PathSumTests.swift new file mode 100644 index 0000000..d04d1aa --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/PathSumTests.swift @@ -0,0 +1,41 @@ +// +// PathSumTests.swift +// +// +// Created by Stefan Jaindl on 15.07.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class PathSumTests: XCTestCase { + + open func testPathSum() { + let root = SimpleTreeNode(value: 3) + root.left = SimpleTreeNode(value: 1) + root.left?.left = SimpleTreeNode(value: 1) + root.left?.left?.left = SimpleTreeNode(value: 4) + root.left?.left?.right = SimpleTreeNode(value: 6) + root.left?.left?.left?.left = SimpleTreeNode(value: 1) + + root.right = SimpleTreeNode(value: 2) + root.right?.right = SimpleTreeNode(value: -1) + root.right?.right?.right = SimpleTreeNode(value: 1) + + let pathSum = PathSum() + + let count = pathSum.pathSumCount(root: root, valueToMatch: 5) + + XCTAssertEqual(count, 5) + + /* Result BST: + 3 (root) + 1 2 + 1 -1 + 4 6 1 + 1 + */ + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/RandomBinarySearchTreeTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/RandomBinarySearchTreeTests.swift new file mode 100644 index 0000000..15ead0f --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/RandomBinarySearchTreeTests.swift @@ -0,0 +1,39 @@ +// +// RandomBinarySearchTreeTests.swift +// +// +// Created by Stefan Jaindl on 13.07.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class RandomBinarySearchTreeTests: XCTestCase { + + open func testRandomBinarySearchTree() { + //setup input Tree + let searchTree = RandomBinarySearchTree() + searchTree.insert(value: 5) + searchTree.insert(value: 2) + searchTree.insert(value: 7) + searchTree.insert(value: 10) + searchTree.insert(value: 11) + searchTree.insert(value: 12) + + XCTAssertNotNil(searchTree.find(value: 5)) + XCTAssertNotNil(searchTree.find(value: 12)) + XCTAssertNil(searchTree.find(value: 8)) + + searchTree.delete(value: 12) + XCTAssertNil(searchTree.find(value: 12)) + + let random1 = searchTree.getRandomNode() + let random2 = searchTree.getRandomNode() + let random3 = searchTree.getRandomNode() + let random4 = searchTree.getRandomNode() + + XCTAssertFalse(random1?.value == random2?.value && random2?.value == random3?.value && random3?.value == random4?.value) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/SubtreeCheckerTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/SubtreeCheckerTests.swift new file mode 100644 index 0000000..c2ade08 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/SubtreeCheckerTests.swift @@ -0,0 +1,36 @@ +// +// SubtreeCheckerTests.swift +// +// +// Created by Stefan Jaindl on 13.07.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class SubtreeCheckerTests: XCTestCase { + + open func testSubtreeChecker() { + //setup input Tree + let tree = SimpleTreeNode(value: 10) + tree.left = SimpleTreeNode(value: 8) + tree.left?.left = SimpleTreeNode(value: 4) + tree.left?.right = SimpleTreeNode(value: 9) + tree.right = SimpleTreeNode(value: 12) + tree.right?.right = SimpleTreeNode(value: 14) + tree.right?.left = SimpleTreeNode(value: 11) + + let subtree = SimpleTreeNode(value: 8) + subtree.left = SimpleTreeNode(value: 4) + subtree.right = SimpleTreeNode(value: 9) + + let subtreeChecker = SubTreeChecker(nilNodeValue: Int.min) + + XCTAssertTrue(subtreeChecker.isSubtree(tree: tree, subtree: subtree)) + + subtree.left?.right = SimpleTreeNode(value: 99) + XCTAssertFalse(subtreeChecker.isSubtree(tree: tree, subtree: subtree)) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/ValidateBSTTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/ValidateBSTTests.swift new file mode 100644 index 0000000..e75a249 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/GraphsAndTrees/ValidateBSTTests.swift @@ -0,0 +1,43 @@ +// +// ValidateBSTTests.swift +// +// +// Created by Stefan Jaindl on 28.06.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class ValidateBSTTests: XCTestCase { + + open func testValidTree() { + //setup input Tree + let root = SimpleTreeNode(value: 5) + root.left = SimpleTreeNode(value: 3) + root.left?.left = SimpleTreeNode(value: 3) + root.left?.right = SimpleTreeNode(value: 4) + + root.right = SimpleTreeNode(value: 7) + root.right?.right = SimpleTreeNode(value: 10) + root.right?.left = SimpleTreeNode(value: 6) + + let validator = BSTValidator() + XCTAssertTrue(validator.isValidBST(root: root)) + } + + open func testInvalidTree() { + //setup input Tree + let root = SimpleTreeNode(value: 5) + root.left = SimpleTreeNode(value: 4) + root.left?.left = SimpleTreeNode(value: 3) + root.left?.right = SimpleTreeNode(value: 6) + + root.right = SimpleTreeNode(value: 7) + root.right?.right = SimpleTreeNode(value: 8) + + let validator = BSTValidator() + XCTAssertFalse(validator.isValidBST(root: root)) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/BinaryNodeConverterTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/BinaryNodeConverterTests.swift new file mode 100644 index 0000000..1ac45e5 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/BinaryNodeConverterTests.swift @@ -0,0 +1,43 @@ +// +// BinaryNodeConverterTests.swift +// +// +// Created by Stefan Jaindl on 09.12.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class BinaryNodeConverterTests: XCTestCase { + + open func testConvertion() { + let converter = BinaryNodeConverter() + + let bstRoot = BiNode(data: 10) + bstRoot.left = BiNode(data: 6) + bstRoot.left?.left = BiNode(data: 5) + bstRoot.left?.right = BiNode(data: 8) + bstRoot.left?.right?.left = BiNode(data: 7) + bstRoot.left?.right?.right = BiNode(data: 9) + + bstRoot.right = BiNode(data: 12) + bstRoot.right?.left = BiNode(data: 11) + + let newRoot = converter.convertBinarySearchTreeToDoubleLinkedList(root: bstRoot)?.left + + XCTAssertEqual(newRoot?.data, 5) + XCTAssertEqual(newRoot?.left, nil) + XCTAssertEqual(newRoot?.right?.data, 6) + XCTAssertEqual(newRoot?.right?.left?.data, 5) + XCTAssertEqual(newRoot?.right?.right?.data, 7) + XCTAssertEqual(newRoot?.right?.right?.right?.data, 8) + XCTAssertEqual(newRoot?.right?.right?.right?.right?.data, 9) + XCTAssertEqual(newRoot?.right?.right?.right?.right?.right?.data, 10) + XCTAssertEqual(newRoot?.right?.right?.right?.right?.right?.right?.data, 11) + XCTAssertEqual(newRoot?.right?.right?.right?.right?.right?.right?.right?.data, 12) + XCTAssertEqual(newRoot?.right?.right?.right?.right?.right?.right?.right?.right, nil) + XCTAssertEqual(newRoot?.right?.right?.right?.right?.right?.right?.right?.left?.data, 11) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/DeleteMiddleNodeTest.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/DeleteMiddleNodeTest.swift new file mode 100644 index 0000000..901f006 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/DeleteMiddleNodeTest.swift @@ -0,0 +1,37 @@ +// +// DeleteMiddleNodeTest.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 03.06.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class DeleteMiddleNodeTest: XCTestCase { + + open func testDeleteMiddleNode() throws { + let linkedList = SingleLinkedList() + + let root = SingleNode(val: 1) + let secondNode = SingleNode(val: 2) + linkedList.add(node: root) + linkedList.add(node: secondNode) + linkedList.add(node: SingleNode(val: 3)) + linkedList.add(node: SingleNode(val: 4)) + linkedList.add(node: SingleNode(val: 5)) + + let nodeDeleter = DeleteMiddleNode() + nodeDeleter.deleteNode(node: secondNode) + linkedList.count -= 1 + + var result: [Int] = [] + while !linkedList.isEmpty() { + result.insert(try linkedList.removeLast()!.val, at: 0) + } + + XCTAssertEqual(result, [1, 3, 4, 5]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/DuplicateRemoverTest.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/DuplicateRemoverTest.swift new file mode 100644 index 0000000..0e1e25a --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/DuplicateRemoverTest.swift @@ -0,0 +1,38 @@ +// +// DuplicateRemoverTest.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 01.06.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + + +import XCTest +@testable import DataStructuresAlgorithms + +open class DuplicateRemoverTests: XCTestCase { + + open func testDuplicateRemoval() throws { + let linkedList = SingleLinkedList() + + let root = SingleNode(val: 1) + linkedList.add(node: root) + linkedList.add(node: SingleNode(val: 3)) + linkedList.add(node: SingleNode(val: 2)) + linkedList.add(node: SingleNode(val: 3)) + linkedList.add(node: SingleNode(val: 5)) + linkedList.add(node: SingleNode(val: 1)) + + let duplicateRemover = DuplicateRemover() + duplicateRemover.removeDuplicates(linkedList: linkedList) + + var result: [Int] = [] + while !linkedList.isEmpty() { + result.insert(try linkedList.removeLast()!.val, at: 0) + } + + XCTAssertEqual(result, [1, 3, 2, 5]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/KToLastTest.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/KToLastTest.swift new file mode 100644 index 0000000..0625e45 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/KToLastTest.swift @@ -0,0 +1,32 @@ +// +// KToLastTest.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 03.06.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + + +import XCTest +@testable import DataStructuresAlgorithms + +open class KToLastTest: XCTestCase { + + open func testKToLast() throws { + let linkedList = SingleLinkedList() + + let root = SingleNode(val: 1) + linkedList.add(node: root) + linkedList.add(node: SingleNode(val: 5)) + linkedList.add(node: SingleNode(val: 3)) + linkedList.add(node: SingleNode(val: 4)) + linkedList.add(node: SingleNode(val: 10)) + + let kToLast = KToLast() + let kToLastNode = kToLast.kToLast(linkedList: linkedList, k: 2) + + XCTAssertEqual(kToLastNode?.val, 3) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/LinkedListPalindromeTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/LinkedListPalindromeTests.swift new file mode 100644 index 0000000..a8534ec --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/LinkedListPalindromeTests.swift @@ -0,0 +1,45 @@ +// +// DeleteMiddleNodeTest.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 03.06.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + + +import XCTest +@testable import DataStructuresAlgorithms + +open class LinkedListPalindromeTests: XCTestCase { + + open func testPalindromeEven() throws { + let linkedList = SingleLinkedList() + + linkedList.add(node: SingleNode(val: "1")) + linkedList.add(node: SingleNode(val: "2")) + linkedList.add(node: SingleNode(val: "3")) + linkedList.add(node: SingleNode(val: "3")) + linkedList.add(node: SingleNode(val: "2")) + linkedList.add(node: SingleNode(val: "1")) + + let palindromeChecker = LinkedListPalindrome() + XCTAssertTrue(palindromeChecker.isPalindrome(linkedList: linkedList)) + } + + open func testPalindromeOdd() throws { + let linkedList = SingleLinkedList() + + linkedList.add(node: SingleNode(val: "M")) + linkedList.add(node: SingleNode(val: "A")) + linkedList.add(node: SingleNode(val: "H")) + linkedList.add(node: SingleNode(val: "A")) + linkedList.add(node: SingleNode(val: "H")) + linkedList.add(node: SingleNode(val: "A")) + linkedList.add(node: SingleNode(val: "M")) + + let palindromeChecker = LinkedListPalindrome() + XCTAssertTrue(palindromeChecker.isPalindrome(linkedList: linkedList)) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/LoopDetectorTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/LoopDetectorTests.swift new file mode 100644 index 0000000..62af71c --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/LoopDetectorTests.swift @@ -0,0 +1,35 @@ +// +// DuplicateRemoverTest.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 10.06.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + + +import XCTest +@testable import DataStructuresAlgorithms + +open class LoopDetectorTests: XCTestCase { + + open func testLoopDetector() throws { + let linkedList = SingleLinkedList() + + let loopStartNode = SingleNode(val: 3) + let loopBackLinkNode = SingleNode(val: 5) + loopBackLinkNode.next = loopStartNode + + linkedList.add(node: SingleNode(val: 1)) + linkedList.add(node: SingleNode(val: 2)) + linkedList.add(node: loopStartNode) + linkedList.add(node: SingleNode(val: 4)) + linkedList.add(node: loopBackLinkNode) + + let loopDetector = LoopDetector() + let foundLoopStartNode = loopDetector.detectLoop(linkedList: linkedList) + + XCTAssertTrue(loopStartNode === foundLoopStartNode) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/NodeIntersectionTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/NodeIntersectionTests.swift new file mode 100644 index 0000000..977a2d4 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/NodeIntersectionTests.swift @@ -0,0 +1,39 @@ +// +// NodeIntersectionTests.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 10.06.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class NodeIntersectionTests: XCTestCase { + + open func testNodeIntersection() throws { + let firstLinkedList = SingleLinkedList() + + let intersectingNode = SingleNode(val: 4) + + firstLinkedList.add(node: SingleNode(val: 1)) + firstLinkedList.add(node: SingleNode(val: 2)) + firstLinkedList.add(node: intersectingNode) + firstLinkedList.add(node: SingleNode(val: 3)) + + let secondLinkedList = SingleLinkedList() + + secondLinkedList.add(node: SingleNode(val: 1)) + secondLinkedList.add(node: SingleNode(val: 4)) + secondLinkedList.add(node: SingleNode(val: 3)) + secondLinkedList.add(node: SingleNode(val: 2)) + secondLinkedList.add(node: intersectingNode) + + let intersectionChecker = NodeIntersection() + let foundIntersectionNode = intersectionChecker.intersect(first: firstLinkedList, second: secondLinkedList) + + XCTAssertTrue(foundIntersectionNode === intersectingNode) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/PartitionLinkedListTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/PartitionLinkedListTests.swift new file mode 100644 index 0000000..57352e5 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/PartitionLinkedListTests.swift @@ -0,0 +1,37 @@ +// +// PartitionLinkedListTests.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 07.06.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class PartitionLinkedListTests: XCTestCase { + + open func testPartition() throws { + let linkedList = SingleLinkedList() + + linkedList.add(node: SingleNode(val: 3)) + linkedList.add(node: SingleNode(val: 5)) + linkedList.add(node: SingleNode(val: 8)) + linkedList.add(node: SingleNode(val: 5)) + linkedList.add(node: SingleNode(val: 10)) + linkedList.add(node: SingleNode(val: 2)) + linkedList.add(node: SingleNode(val: 1)) + + let partitioner = PartitionLinkedList() + partitioner.partition(linkedList: linkedList, around: 5) + + var result: [Int] = [] + while !linkedList.isEmpty() { + result.insert(try linkedList.removeLast()!.val, at: 0) + } + + XCTAssertEqual(result, [1, 2, 3, 5, 8, 5, 10]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/SumListsTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/SumListsTests.swift new file mode 100644 index 0000000..3c4fe31 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/LinkedList/SumListsTests.swift @@ -0,0 +1,76 @@ +// +// DeleteMiddleNodeTest.swift +// DataStructuresAlgs +// +// Created by Stefan Jaindl on 03.06.20. +// Copyright © 2020 Stefan Jaindl. All rights reserved. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class SumListsTests: XCTestCase { + + open func testSumWithPadding() throws { + let first = SingleNode(val: 5) + first.next = SingleNode(val: 1) + first.next?.next = SingleNode(val: 2) + + let second = SingleNode(val: 0) + second.next = SingleNode(val: 9) + second.next?.next = SingleNode(val: 2) + + let summarizer = SumLists() + let sum = summarizer.sumNodes(first: first, second: second) + + var result: [Int] = [] + while !sum.isEmpty() { + result.insert(try sum.removeLast()!.val, at: 0) + } + + XCTAssertEqual(result, [6, 0, 4]) + } + + open func testSumWithExcessCarry() throws { + let first = SingleNode(val: 7) + first.next = SingleNode(val: 1) + first.next?.next = SingleNode(val: 7) + + let second = SingleNode(val: 2) + second.next = SingleNode(val: 9) + second.next?.next = SingleNode(val: 5) + + let summarizer = SumLists() + let sum = summarizer.sumNodes(first: first, second: second) + + var result: [Int] = [] + while !sum.isEmpty() { + result.insert(try sum.removeLast()!.val, at: 0) + } + + XCTAssertEqual(result, [1, 0, 1, 2]) + } + + open func testReverseSum() throws { + let first = SingleNode(val: 9) + first.next = SingleNode(val: 7) + first.next?.next = SingleNode(val: 8) + + let second = SingleNode(val: 6) + second.next = SingleNode(val: 8) + second.next?.next = SingleNode(val: 5) + + let summarizer = SumLists() + var sumNode = summarizer.reverseSum(first: first, second: second) + + var result: [Int] = [] + while let sum = sumNode { + result.insert(sum.val, at: 0) + sumNode = sumNode?.next + } + + XCTAssertEqual(result, [1, 4, 6, 5]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/AppointmentTimeOptimizerTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/AppointmentTimeOptimizerTests.swift new file mode 100644 index 0000000..e6749ae --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/AppointmentTimeOptimizerTests.swift @@ -0,0 +1,23 @@ +// +// AppointmentTimeOptimizerTests.swift +// +// +// Created by Stefan Jaindl on 11.12.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class AppointmentTimeOptimizerTests: XCTestCase { + + open func testAppointments() { + let optimizer = AppointmentTimeOptimizer() + + let minutes = [30, 15, 60, 75, 45, 15, 15, 45] + let maxMinutes = optimizer.calculateMaxPossibleAppointmentTime(from: minutes) + + XCTAssertEqual(maxMinutes, 180) //30 + 60 + 45 + 45 + + XCTAssertEqual(optimizer.calculateMaxMinutesIterative(from: minutes), 180) //30 + 60 + 45 + 45 + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/BoolEvaluationTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/BoolEvaluationTests.swift new file mode 100644 index 0000000..b502a8b --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/BoolEvaluationTests.swift @@ -0,0 +1,24 @@ +// +// BoolEvaluationTests.swift +// +// +// Created by Stefan Jaindl on 27.08.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class BoolEvaluationTests: XCTestCase { + + open func testEvaluation() throws { + let boolEvaluator = BoolEvaluation() + let result = try boolEvaluator.boolEvaluation(expression: "1|1|1", expectedResult: true) + XCTAssertEqual(result, 2) + + let result2 = try boolEvaluator.boolEvaluation(expression: "1^0|0|1", expectedResult: false) + XCTAssertEqual(result2, 2) + + let result3 = try boolEvaluator.boolEvaluation(expression: "0&0&0&1^1|0", expectedResult: true) + XCTAssertEqual(result3, 10) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/CircusTowerTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/CircusTowerTests.swift new file mode 100644 index 0000000..326d2d8 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/CircusTowerTests.swift @@ -0,0 +1,28 @@ +// +// CircusTowerTests.swift +// +// +// Created by Stefan Jaindl on 06.12.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class CircusTowerTests: XCTestCase { + + open func testMaxTower() { + let tower = CircusTower() + + let people = [ + CircusPeople(width: 65, height: 100), + CircusPeople(width: 70, height: 150), + CircusPeople(width: 56, height: 90), + CircusPeople(width: 75, height: 190), + CircusPeople(width: 60, height: 85), + CircusPeople(width: 68, height: 110) + ] + + let size = tower.highestTowerSize(of: people) + XCTAssertEqual(size, 5) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/CoinCounterTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/CoinCounterTests.swift new file mode 100644 index 0000000..853e034 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/CoinCounterTests.swift @@ -0,0 +1,24 @@ +// +// CoinCounterTests.swift +// +// +// Created by Stefan Jaindl on 24.08.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class CoinCounterTests: XCTestCase { + + open func testCoinCounter() { + let coins = [Coin(id: "Quarter", value: 25), + Coin(id: "Dime", value: 10), + Coin(id: "Nickel", value: 5), + Coin(id: "Penny", value: 1)] + + let coinCounter = CoinCounter(coins: coins) + let ways = coinCounter.coins(amount: 15) + + XCTAssertEqual(ways, 6) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/DivingBoardTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/DivingBoardTests.swift new file mode 100644 index 0000000..033c469 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/DivingBoardTests.swift @@ -0,0 +1,34 @@ +// +// DivingBoardTests.swift +// +// +// Created by Stefan Jaindl on 22.11.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class DivingBoardTests: XCTestCase { + + open func testDivingBoard() { + let board = DivingBoard(shortPlank: Plank(size: .short(1)), longPlank: Plank(size: .long(2))) + + XCTAssertEqual(board.divingBoard(numberOfPlanks: -1), []) + XCTAssertEqual(board.divingBoard(numberOfPlanks: 0), []) + XCTAssertEqual(board.divingBoard(numberOfPlanks: 1), [1, 2]) + XCTAssertEqual(board.divingBoard(numberOfPlanks: 2), [2, 3, 4]) + XCTAssertEqual(board.divingBoard(numberOfPlanks: 3), [3, 4, 5, 6]) + XCTAssertEqual(board.divingBoard(numberOfPlanks: 10),[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) + } + + open func testDivingBoardFast() { + let board = DivingBoard(shortPlank: Plank(size: .short(1)), longPlank: Plank(size: .long(2))) + + XCTAssertEqual(board.divingBoardFast(numberOfPlanks: -1), []) + XCTAssertEqual(board.divingBoardFast(numberOfPlanks: 0), []) + XCTAssertEqual(board.divingBoardFast(numberOfPlanks: 1), [1, 2]) + XCTAssertEqual(board.divingBoardFast(numberOfPlanks: 2), [2, 3, 4]) + XCTAssertEqual(board.divingBoardFast(numberOfPlanks: 3), [3, 4, 5, 6]) + XCTAssertEqual(board.divingBoardFast(numberOfPlanks: 10),[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/FactorialZeroesTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/FactorialZeroesTests.swift new file mode 100644 index 0000000..cd9b640 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/FactorialZeroesTests.swift @@ -0,0 +1,25 @@ +// +// FactorialZeroesTests.swift +// +// +// Created by Stefan Jaindl on 16.11.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class FactorialZeroesTests: XCTestCase { + + open func testTrailingZeroCount() { + let counter = FactorialZeroes() + + XCTAssertEqual(counter.factorialTrailingZeroesCount(number: 1), 0) + XCTAssertEqual(counter.factorialTrailingZeroesCount(number: 5), 1) + XCTAssertEqual(counter.factorialTrailingZeroesCount(number: 10), 2) + XCTAssertEqual(counter.factorialTrailingZeroesCount(number: 15), 3) + XCTAssertEqual(counter.factorialTrailingZeroesCount(number: 20), 4) + XCTAssertEqual(counter.factorialTrailingZeroesCount(number: 25), 6) + XCTAssertEqual(counter.factorialTrailingZeroesCount(number: 30), 7) + XCTAssertEqual(counter.factorialTrailingZeroesCount(number: 32), 7) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/GridPathFinderTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/GridPathFinderTests.swift new file mode 100644 index 0000000..fc0c8be --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/GridPathFinderTests.swift @@ -0,0 +1,64 @@ +// +// GridPathFinderTests.swift +// +// +// Created by Stefan Jaindl on 13.08.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class GridPathFinderTests: XCTestCase { + + open func testGridPathFinder() { + let grid: [[GridCell]] = [ + [ GridCell(value: 0, isAccessible: true, row: 0, column: 0), + GridCell(value: 1, isAccessible: true, row: 0, column: 1), + GridCell(value: 2, isAccessible: true, row: 0, column: 2), + GridCell(value: 3, isAccessible: true, row: 0, column: 3), + GridCell(value: 4, isAccessible: true, row: 0, column: 4), + ], + [ GridCell(value: 10, isAccessible: true, row: 1, column: 0), + GridCell(value: 11, isAccessible: true, row: 1, column: 1), + GridCell(value: 12, isAccessible: true, row: 1, column: 2), + GridCell(value: 13, isAccessible: true, row: 1, column: 3), + GridCell(value: 14, isAccessible: true, row: 1, column: 4), + ], + [ GridCell(value: 20, isAccessible: false, row: 2, column: 0), + GridCell(value: 21, isAccessible: true, row: 2, column: 1), + GridCell(value: 22, isAccessible: false, row: 2, column: 2), + GridCell(value: 23, isAccessible: true, row: 2, column: 3), + GridCell(value: 24, isAccessible: true, row: 2, column: 4), + ], + [ GridCell(value: 30, isAccessible: true, row: 3, column: 0), + GridCell(value: 31, isAccessible: true, row: 3, column: 1), + GridCell(value: 32, isAccessible: true, row: 3, column: 2), + GridCell(value: 33, isAccessible: true, row: 3, column: 3), + GridCell(value: 34, isAccessible: true, row: 3, column: 4), + ], + [ GridCell(value: 40, isAccessible: true, row: 4, column: 0), + GridCell(value: 41, isAccessible: true, row: 4, column: 1), + GridCell(value: 42, isAccessible: false, row: 4, column: 2), + GridCell(value: 43, isAccessible: true, row: 4, column: 3), + GridCell(value: 44, isAccessible: true, row: 4, column: 4), + ], + [ GridCell(value: 50, isAccessible: true, row: 5, column: 0), + GridCell(value: 51, isAccessible: true, row: 5, column: 1), + GridCell(value: 52, isAccessible: false, row: 5, column: 2), + GridCell(value: 53, isAccessible: false, row: 5, column: 3), + GridCell(value: 54, isAccessible: false, row: 5, column: 4), + ], + [ GridCell(value: 60, isAccessible: true, row: 6, column: 0), + GridCell(value: 61, isAccessible: true, row: 6, column: 1), + GridCell(value: 62, isAccessible: true, row: 6, column: 2), + GridCell(value: 63, isAccessible: true, row: 6, column: 3), + GridCell(value: 64, isAccessible: true, row: 6, column: 4), + ] + ] + + let roboterPathFinder = GridPathFinder(grid: grid) + let path = roboterPathFinder.findPath() + XCTAssertNotNil(path) + XCTAssertEqual(path, [0, 1, 11, 21, 31, 41, 51, 61, 62, 63, 64]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/MultiplyTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/MultiplyTests.swift new file mode 100644 index 0000000..03391bc --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/MultiplyTests.swift @@ -0,0 +1,26 @@ +// +// MultiplyTests.swift +// +// +// Created by Stefan Jaindl on 16.08.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class MultiplyTests: XCTestCase { + + open func testMultiply() { + let multiplier = Multiplyer() + + XCTAssertEqual(multiplier.multiply(3, with: 4), 12) + XCTAssertEqual(multiplier.multiply(4, with: 3), 12) + XCTAssertEqual(multiplier.multiply(0, with: 3), 0) + XCTAssertEqual(multiplier.multiply(4, with: 0), 0) + XCTAssertEqual(multiplier.multiply(4, with: 1), 4) + XCTAssertEqual(multiplier.multiply(1, with: 3), 3) + XCTAssertEqual(multiplier.multiply(8, with: 2), 16) + XCTAssertEqual(multiplier.multiply(12, with: 9), 108) + XCTAssertEqual(multiplier.multiply(7, with: 7), 49) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/PaintFillTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/PaintFillTests.swift new file mode 100644 index 0000000..9c3584d --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/PaintFillTests.swift @@ -0,0 +1,35 @@ +// +// PaintFillTests.swift +// +// +// Created by Stefan Jaindl on 24.08.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class PaintFillTests: XCTestCase { + + open func testPaintFill() throws { + let paintfill = PaintFill() + let black = CGColor.black + let white = CGColor.white + let red = CGColor.init(red: 255, green: 0, blue: 0, alpha: 1) + + var screen = [ [black, white, white, white], + [black, black, black, white], + [black, white, white, black], + [white, white, white, white], + [white, white, white, white] ] + + let expected = [ [red, white, white, white], + [red, red, red, white], + [red, white, white, red], + [white, white, white, white], + [white, white, white, white] ] + + try paintfill.paintFill(screen: &screen, point: Point(x: 2, y: 3), newColor: red) + + XCTAssertEqual(screen, expected) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/ParensBuilderTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/ParensBuilderTests.swift new file mode 100644 index 0000000..5b11efb --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/ParensBuilderTests.swift @@ -0,0 +1,19 @@ +// +// ParensBuilderTests.swift +// +// +// Created by Stefan Jaindl on 24.08.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class ParensBuilderTests: XCTestCase { + + open func testParens() { + let parensBuilder = ParensBuilder() + let combis = parensBuilder.parens(numberOfBrackets: 3) + + XCTAssertEqual(combis, ["()()()", "()(())", "(())()", "(()())", "((()))"]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/PermutationsTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/PermutationsTests.swift new file mode 100644 index 0000000..a97e558 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/PermutationsTests.swift @@ -0,0 +1,29 @@ +// +// PermutationsTests.swift +// +// +// Created by Stefan Jaindl on 19.08.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class PermutationsTests: XCTestCase { + + open func testPermutationsWithoutDuplicates() { + let permutationBuilder = Permutations() + let permutations = permutationBuilder.permutationsWithoutDuplicates(of: "abc") + + XCTAssertEqual(permutations, ["cba", "bca", "bac", "cab", "acb", "abc"]) + } + + open func testPermutationsWithDuplicates() { + let permutationBuilder = Permutations() + + var permutations = permutationBuilder.permutationsWithDuplicates(of: "cbab") + XCTAssertEqual(permutations, ["abbc", "abcb", "acbb", "babc", "bacb", "bbac", "bbca", "bcab", "bcba", "cabb", "cbab", "cbba"]) + + permutations = permutationBuilder.permutationsWithDuplicates(of: "aabaa") + XCTAssertEqual(permutations, ["aaaab", "aaaba", "aabaa", "abaaa", "baaaa"]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/PowerSetTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/PowerSetTests.swift new file mode 100644 index 0000000..b7b2a09 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/PowerSetTests.swift @@ -0,0 +1,33 @@ +// +// PowerSetTests.swift +// +// +// Created by Stefan Jaindl on 15.08.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class PowerSetTests: XCTestCase { + + open func testPowerSetPermutations() { + let powerSet = PowerSet() + let sets = powerSet.powerSetPermutations(set: [1, 2, 3]) + + XCTAssertEqual(sets, [[], [1], [2], [3], [1, 2], [1, 3], [2, 1], [2, 3], [3, 1], [3, 2], [1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]) + } + + open func testPowerSetCombinations() { + let powerSet = PowerSet() + let sets = powerSet.powerSetCombinations(set: [1, 2, 3]) + + XCTAssertEqual(sets, [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]) + } + + open func testPowerSetCombinationsByBitmasking() { + let powerSet = PowerSet() + let sets = powerSet.powerSetCombinationsByBitMasking(set: [1, 2, 3]) + + XCTAssertEqual(sets, [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/QueensOnChessboardTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/QueensOnChessboardTests.swift new file mode 100644 index 0000000..bcb6836 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/QueensOnChessboardTests.swift @@ -0,0 +1,20 @@ +// +// QueensOnChessboardTests.swift +// +// +// Created by Stefan Jaindl on 25.08.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class QueensOnChessboardTests: XCTestCase { + + open func testQueensArrangements() { + let queensArranger = QueensOnChessboard() + let arrangements = queensArranger.queenArrangements() + + XCTAssertEqual(arrangements.count, 92) + } + +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/StackOfBoxesTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/StackOfBoxesTests.swift new file mode 100644 index 0000000..0a1b3f3 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/StackOfBoxesTests.swift @@ -0,0 +1,23 @@ +// +// StackOfBoxesTests.swift +// +// +// Created by Stefan Jaindl on 26.08.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class StackOfBoxesTests: XCTestCase { + + open func testStackOfBoxes() { + let stackOfBoxes = StackOfBoxes() + let boxes = [ Box(width: 10, height: 20, depth: 15), + Box(width: 5, height: 15, depth: 20), + Box(width: 1, height: 10, depth: 1), + Box(width: 30, height: 5, depth: 25)] + let maxHeight = stackOfBoxes.maxHeight(of: boxes) + + XCTAssertEqual(maxHeight, 30) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/TowerOfHanoiTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/TowerOfHanoiTests.swift new file mode 100644 index 0000000..401de43 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/TowerOfHanoiTests.swift @@ -0,0 +1,35 @@ +// +// TowerOfHanoiTests.swift +// +// +// Created by Stefan Jaindl on 19.08.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class TowerOfHanoiTests: XCTestCase { + + open func testTowersOfHanoi() throws { + let numberOfTowers = 3 + var towers: [TowerOfHanoi] = [] + + for tower in 0 ..< numberOfTowers { + towers.append(TowerOfHanoi(index: tower)) + } + + for diskNr in stride(from: 10, to: 0, by: -1) { + try towers[0].add(disk: diskNr) + } + + try towers[0].moveDisks(numberOfDisks: 10, destination: towers[2], buffer: towers[1]) + + var array: [Int] = [] + while !towers[2].disks.isEmpty() { + let element = try towers[2].disks.pop() + array.append(element) + } + + XCTAssertEqual(array, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/TripleStepTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/TripleStepTests.swift new file mode 100644 index 0000000..90d3cca --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/RecursionDynamicProgramming/TripleStepTests.swift @@ -0,0 +1,22 @@ +// +// TripleStepTests.swift +// +// +// Created by Stefan Jaindl on 13.08.20. +// + +import XCTest +@testable import DataStructuresAlgorithms + +open class TripleStepTests: XCTestCase { + + open func testTripleSteps() { + let stepper = TripleStep() + XCTAssertEqual(stepper.tripleStep(for: 0), 0) + XCTAssertEqual(stepper.tripleStep(for: 1), 0) + XCTAssertEqual(stepper.tripleStep(for: 2), 1) + XCTAssertEqual(stepper.tripleStep(for: 3), 2) + XCTAssertEqual(stepper.tripleStep(for: 4), 4) + XCTAssertEqual(stepper.tripleStep(for: 5), 7) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/AnagramGrouperTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/AnagramGrouperTests.swift new file mode 100644 index 0000000..1506f98 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/AnagramGrouperTests.swift @@ -0,0 +1,39 @@ +// +// AnagramGrouper.swift +// +// +// Created by Stefan Jaindl on 20.09.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class AnagramGrouperTests: XCTestCase { + + open func testAnagramGrouper() { + let grouper = AnagramGrouper() + let array = ["test", "ets", "xx", "sett", "ste"] + + let grouped = grouper.sortAnagram(array: array) + var lastElement = "" + for (index, element) in grouped.enumerated() { + if element == "xx" || index == 0 || index == 4 { + lastElement = element + continue + } + + if element == "test" { + XCTAssertTrue(lastElement == "sett" || grouped[index + 1] == "sett") + } else if element == "sett" { + XCTAssertTrue(lastElement == "test" || grouped[index + 1] == "test") + } else if element == "ets" { + XCTAssertTrue(lastElement == "ste" || grouped[index + 1] == "ste") + } else if element == "ste" { + XCTAssertTrue(lastElement == "ets" || grouped[index + 1] == "ets") + } + + lastElement = element + } + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/BestLineSearcherTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/BestLineSearcherTests.swift new file mode 100644 index 0000000..8b94936 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/BestLineSearcherTests.swift @@ -0,0 +1,31 @@ +// +// BestLineSearcherTests.swift +// +// +// Created by Stefan Jaindl on 26.11.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class BestLineSearcherTests: XCTestCase { + + open func testBestLineSearcher() { + + let points: [Point] = [ + Point(x: 2, y: 2), + Point(x: 4, y: 2), + Point(x: 5, y: 3), + Point(x: 6, y: 4), + Point(x: 0, y: 17) + ] + + let graph = TwoDGraph(points: points) + let bestLineSearcher = BestLineSearcher() + + let line = bestLineSearcher.bestLine(of: graph) + + XCTAssertEqual(line, MultiPrecisionLine(startPoint: Point(x: 4, y: 2), endPoint: Point(x: 6, y: 4))) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/ContiguousSumTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/ContiguousSumTests.swift new file mode 100644 index 0000000..5f24292 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/ContiguousSumTests.swift @@ -0,0 +1,21 @@ +// +// ContiguousSumTests.swift +// +// +// Created by Stefan Jaindl on 28.11.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class ContiguousSumTests: XCTestCase { + + open func testLargestContiguousSum() { + + let summer = ContiguousSum() + let array = [2, -8, 3, -2, 4, -10] + + XCTAssertEqual(summer.largestContiguousSum(of: array), 5) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/DuplicateFinderTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/DuplicateFinderTests.swift new file mode 100644 index 0000000..c8987b5 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/DuplicateFinderTests.swift @@ -0,0 +1,24 @@ +// +// DuplicateFinderTests.swift +// +// +// Created by Stefan Jaindl on 25.09.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class DuplicateFinderTests: XCTestCase { + + open func testFindDuplicates() { + let array = [1, 1, 3, 5, 7, 10, 3, 32000, 0, 3000, 32000] + let finder = DuplicateFinder() + let duplicates = finder.findDuplicates(array: array, maxValue: 32000) + + XCTAssertTrue(duplicates.contains(1)) + XCTAssertTrue(duplicates.contains(3)) + XCTAssertTrue(duplicates.contains(32000)) + XCTAssertEqual(duplicates.count, 3) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/IntersectionFinderTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/IntersectionFinderTests.swift new file mode 100644 index 0000000..2ce5ad4 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/IntersectionFinderTests.swift @@ -0,0 +1,27 @@ +// +// IntersectionFinder.swift +// +// +// Created by Stefan Jaindl on 15.11.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class IntersectionFinderTests: XCTestCase { + + open func testIntersection() { + let intersectionFinder = IntersectionFinder() + + var first = LineSegment(from: CGPoint(x: 0, y: 0), to: CGPoint(x: 4, y: 4)) + var second = LineSegment(from: CGPoint(x: 1, y: 2), to: CGPoint(x: 3, y: 4)) + + XCTAssertEqual(intersectionFinder.intersection(firstLine: first, secondLine: second), nil) + + first = LineSegment(from: CGPoint(x: 0, y: 0), to: CGPoint(x: 4, y: 4)) + second = LineSegment(from: CGPoint(x: 1, y: 3), to: CGPoint(x: 3, y: 1)) + + XCTAssertEqual(intersectionFinder.intersection(firstLine: first, secondLine: second), CGPoint(x: 2, y: 2)) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/ListyTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/ListyTests.swift new file mode 100644 index 0000000..8f10883 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/ListyTests.swift @@ -0,0 +1,29 @@ +// +// ListyTests.swift +// +// +// Created by Stefan Jaindl on 21.09.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class ListyTests: XCTestCase { + + open func testListy() { + let array = [1, 1, 3, 5, 7, 10] + let listy = Listy(array: array) + let listySearcher = ListySearcher() + + XCTAssertEqual(listySearcher.listySearch(listy: listy, searched: 1), 0) + XCTAssertEqual(listySearcher.listySearch(listy: listy, searched: 3), 2) + XCTAssertEqual(listySearcher.listySearch(listy: listy, searched: 5), 3) + XCTAssertEqual(listySearcher.listySearch(listy: listy, searched: 7), 4) + XCTAssertEqual(listySearcher.listySearch(listy: listy, searched: 10), 5) + XCTAssertEqual(listySearcher.listySearch(listy: listy, searched: 6), nil) + XCTAssertEqual(listySearcher.listySearch(listy: listy, searched: 0), nil) + XCTAssertEqual(listySearcher.listySearch(listy: listy, searched: 12), nil) + + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/LivingPeopleTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/LivingPeopleTests.swift new file mode 100644 index 0000000..d69f8cf --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/LivingPeopleTests.swift @@ -0,0 +1,26 @@ +// +// LivingPeopleTests.swift +// +// +// Created by Stefan Jaindl on 23.11.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class LivingPeopleTests: XCTestCase { + + open func testLivingPeople() { + let livingPeople = LivingPeople() + + let peoples: [People] = [ + People(name: "", yearOfBirth: 1909, yearOfDeath: 1950), + People(name: "", yearOfBirth: 1930, yearOfDeath: 1970), + People(name: "", yearOfBirth: 1920, yearOfDeath: 1990), + People(name: "", yearOfBirth: 1940, yearOfDeath: nil) + ] + + XCTAssertEqual(livingPeople.yearsWithMostPeopleAlive(peoples: peoples, minYear: 1900), 1940) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/MedianKeeperTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/MedianKeeperTests.swift new file mode 100644 index 0000000..564ebf8 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/MedianKeeperTests.swift @@ -0,0 +1,23 @@ +// +// MedianKeeperTests.swift +// +// +// Created by Stefan Jaindl on 12.12.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class MedianKeeperTests: XCTestCase { + + open func testMedian() { + let medianer = MedianKeeper() + + XCTAssertEqual(try medianer.median(of: []), nil) + XCTAssertEqual(try medianer.median(of: [1]), 1) + XCTAssertEqual(try medianer.median(of: [1, 3]), 2) + XCTAssertEqual(try medianer.median(of: [1, 2, 3, 4, 5]), 3) + XCTAssertEqual(try medianer.median(of: [1, 2, 4, 5]), 3) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/MultiSearchTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/MultiSearchTests.swift new file mode 100644 index 0000000..8d9e422 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/MultiSearchTests.swift @@ -0,0 +1,23 @@ +// +// MultiSearchTests.swift +// +// +// Created by Stefan Jaindl on 11.12.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class MultiSearchTests: XCTestCase { + + open func testMultiSearch() { + let searcher = MultiSearch() + + var matches = searcher.multiSearch(in: "large teststring with multiple entries", for: ["large", "test", "nothing", "entries", ""]) + XCTAssertEqual(matches.sorted(), ["large", "test", "entries"].sorted()) + + matches = searcher.multiSearch(in: "mississsippi", for: ["is", "ppi", "hi", "sis", "i", "ssippi"]) + XCTAssertEqual(matches.sorted(), ["is", "ppi", "sis", "i", "ssippi"].sorted()) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/PeakSortTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/PeakSortTests.swift new file mode 100644 index 0000000..9af7f60 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/PeakSortTests.swift @@ -0,0 +1,26 @@ +// +// PeakSortTests.swift +// +// +// Created by Stefan Jaindl on 29.09.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class PeakSortTests: XCTestCase { + + open func testPeakSort() { + let peaksort = PeakSort() + var array = [1, 5, 2, 4, 6, 3] + + peaksort.sort(array: &array) + XCTAssertEqual(array, [5, 1, 4, 2, 6, 3]) + + array = [1, 2, 3, 4, 5, 6, 7, 8, 9] + peaksort.sort(array: &array) + + XCTAssertEqual(array, [2, 1, 4, 3, 6, 5, 8, 7, 9]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/RotatedArraySearchTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/RotatedArraySearchTests.swift new file mode 100644 index 0000000..fb537a8 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/RotatedArraySearchTests.swift @@ -0,0 +1,37 @@ +// +// RotatedArraySearchTests.swift +// +// +// Created by Stefan Jaindl on 21.09.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class RotatedArraySearchTests: XCTestCase { + + open func testRotatedArraySearch() { + let searcher = RotatedArraySearch() + + var array = [15, 16, 19, 20, 25, 1, 3, 4, 5, 7, 10, 14] + XCTAssertEqual(searcher.search(array: array, searched: 5) , 8) + XCTAssertEqual(searcher.search(array: array, searched: 17) , nil) + XCTAssertEqual(searcher.search(array: array, searched: 20) , 3) + + array = [2, 2, 2, 3, 4, 2] + XCTAssertEqual(searcher.search(array: array, searched: 2) , 2) + XCTAssertEqual(searcher.search(array: array, searched: 3) , 3) + XCTAssertEqual(searcher.search(array: array, searched: 4) , 4) + + array = [2, 3, 4, 2, 2, 2] + XCTAssertEqual(searcher.search(array: array, searched: 2) , 0) + XCTAssertEqual(searcher.search(array: array, searched: 3) , 1) + XCTAssertEqual(searcher.search(array: array, searched: 4) , 2) + + array = [1, 2, 3, 4, 5, 6, 7, 8] + XCTAssertEqual(searcher.search(array: array, searched: 1) , 0) + XCTAssertEqual(searcher.search(array: array, searched: 3) , 2) + XCTAssertEqual(searcher.search(array: array, searched: 8) , 7) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/SortedMatrixSearchTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/SortedMatrixSearchTests.swift new file mode 100644 index 0000000..6e61d6f --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/SortedMatrixSearchTests.swift @@ -0,0 +1,97 @@ +// +// SortedMatrixSearchTests.swift +// +// +// Created by Stefan Jaindl on 26.09.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class SortedMatrixSearchTests: XCTestCase { + + open func testSortedMatrixSearch() { + var matrix = [[1, 2, 3], + [3, 3, 5], + [3, 5, 6], + [7, 8, 10]] + + let matrixSearcher = SortedMatrixSearch() + + XCTAssertFalse(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 0)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 1)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 2)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 3)) + XCTAssertFalse(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 4)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 5)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 6)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 7)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 8)) + XCTAssertFalse(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 9)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 10)) + XCTAssertFalse(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 11)) + + matrix = [[1, 2, 4, 4], + [2, 3, 5, 8], + [3, 5, 8, 8], + [5, 8, 9, 10], + [7, 9, 10, 12]] + + XCTAssertFalse(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 0)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 1)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 2)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 3)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 4)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 5)) + XCTAssertFalse(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 6)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 7)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 8)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 9)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 10)) + XCTAssertFalse(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 11)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearch(matrix: matrix, searched: 12)) + } + + open func testSortedMatrixSearchFaster() { + var matrix = [[1, 2, 4, 4], + [2, 3, 5, 8], + [3, 5, 8, 8], + [5, 8, 9, 10], + [7, 9, 10, 12]] + + let matrixSearcher = SortedMatrixSearch() + + XCTAssertFalse(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 0)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 1)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 2)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 3)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 4)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 5)) + XCTAssertFalse(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 6)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 7)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 8)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 9)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 10)) + XCTAssertFalse(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 11)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 12)) + + matrix = [[1, 2, 3], + [3, 3, 5], + [3, 5, 6], + [7, 8, 10]] + + XCTAssertFalse(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 0)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 1)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 2)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 3)) + XCTAssertFalse(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 4)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 5)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 6)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 7)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 8)) + XCTAssertFalse(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 9)) + XCTAssertTrue(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 10)) + XCTAssertFalse(matrixSearcher.sortedMatrixSearchFaster(matrix: matrix, searched: 11)) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/SortedMergeTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/SortedMergeTests.swift new file mode 100644 index 0000000..5a6c8da --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/SortedMergeTests.swift @@ -0,0 +1,23 @@ +// +// SortedMergeTests.swift +// +// +// Created by Stefan Jaindl on 19.09.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class SortedMergeTests: XCTestCase { + + open func testSortedMerge() { + let sorter = SortedMerge() + var first = [1, 3, 3, 5, 7, 10, nil, nil, nil] + var second: [Int?] = [2, 3, 12] + let expected = [1, 2, 3, 3, 3, 5, 7, 10, 12] + + sorter.sortAndMerge(first: &first, second: &second) + XCTAssertEqual(first, expected) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/SparseSearchTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/SparseSearchTests.swift new file mode 100644 index 0000000..d06d4c0 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/SparseSearchTests.swift @@ -0,0 +1,25 @@ +// +// SparseSearchTests.swift +// +// +// Created by Stefan Jaindl on 23.09.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class SparseSearchTests: XCTestCase { + + open func testSparseSearch() { + let array = ["at", "", "", "", "ball", "", "", "car", "", "", "dad", "", ""] + let searcher = SparseSearch() + + XCTAssertEqual(searcher.sparseSearch(array: array, searched: "at"), 0) + XCTAssertEqual(searcher.sparseSearch(array: array, searched: "ball"), 4) + XCTAssertEqual(searcher.sparseSearch(array: array, searched: "car"), 7) + XCTAssertEqual(searcher.sparseSearch(array: array, searched: "dad"), 10) + XCTAssertEqual(searcher.sparseSearch(array: array, searched: "not in array"), nil) + XCTAssertEqual(searcher.sparseSearch(array: array, searched: ""), nil) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/StreamRankTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/StreamRankTests.swift new file mode 100644 index 0000000..bc87ce0 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/StreamRankTests.swift @@ -0,0 +1,41 @@ +// +// StreamRankTests.swift +// +// +// Created by Stefan Jaindl on 27.09.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class StreamRankTests: XCTestCase { + + open func testStreamRank() { + let ranker = StreamRank() + + ranker.track(value: 5) + ranker.track(value: 1) + ranker.track(value: 4) + ranker.track(value: 4) + ranker.track(value: 5) + ranker.track(value: 9) + ranker.track(value: 7) + ranker.track(value: 13) + ranker.track(value: 3) + + XCTAssertEqual(ranker.rank(of: 0), 0) + XCTAssertEqual(ranker.rank(of: 1), 0) + XCTAssertEqual(ranker.rank(of: 2), 1) + XCTAssertEqual(ranker.rank(of: 3), 1) + XCTAssertEqual(ranker.rank(of: 4), 3) + XCTAssertEqual(ranker.rank(of: 5), 5) + XCTAssertEqual(ranker.rank(of: 6), 6) + XCTAssertEqual(ranker.rank(of: 7), 6) + XCTAssertEqual(ranker.rank(of: 8), 7) + XCTAssertEqual(ranker.rank(of: 9), 7) + XCTAssertEqual(ranker.rank(of: 10), 8) + XCTAssertEqual(ranker.rank(of: 13), 8) + XCTAssertEqual(ranker.rank(of: 14), 9) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/SubSorterTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/SubSorterTests.swift new file mode 100644 index 0000000..f1b7c35 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/SortingAndSearching/SubSorterTests.swift @@ -0,0 +1,20 @@ +// +// SubSorterTests.swift +// +// +// Created by Stefan Jaindl on 28.11.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class SubSorterTests: XCTestCase { + + open func testShortestSubsortSequence() { + let sorter = SubSorter() + let array = [1, 2, 4, 7, 10, 11, 7, 12, 6, 7, 16, 18, 19] //shortest sequence to sort is from index 3 (=7) to 9 (=7) + + XCTAssertEqual(sorter.shortestSubsortRange(of: array), SortRange(from: 3, to: 9)) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Stack/AnimalShelterTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Stack/AnimalShelterTests.swift new file mode 100644 index 0000000..f301a05 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Stack/AnimalShelterTests.swift @@ -0,0 +1,29 @@ +// +// SortStackTests.swift +// +// +// Created by Stefan Jaindl on 14.06.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class AnimalShelterTests: XCTestCase { + + open func testAnimalShelter() throws { + let animalShelter = AnimalShelter() + + animalShelter.enqueue(animal: Dog(name: "Waldi")) + animalShelter.enqueue(animal: Dog(name: "Lumpi")) + animalShelter.enqueue(animal: Cat(name: "Gina")) + animalShelter.enqueue(animal: Cat(name: "KitCat")) + animalShelter.enqueue(animal: Cat(name: "Tom")) + animalShelter.enqueue(animal: Dog(name: "Wuff")) + + XCTAssertEqual(try animalShelter.dequeueCat().name, "Tom") + XCTAssertEqual(try animalShelter.dequeueDog().name, "Wuff") + XCTAssertEqual(try animalShelter.dequeueAny().name, "KitCat") + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Stack/CalculatorTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Stack/CalculatorTests.swift new file mode 100644 index 0000000..b7f3dfb --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Stack/CalculatorTests.swift @@ -0,0 +1,26 @@ +// +// CalculatorTests.swift +// +// +// Created by Stefan Jaindl on 01.12.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class CalculatorTests: XCTestCase { + + open func testCalculator() throws { + let calculator = Calculator() + + // 2 * 3 + 5 / 6 * 3 + 15 + let expression: [Calculator.Expression] = [ + .operand(2), .multiply, .operand(3), .plus, .operand(5), .divide, . operand(6), .multiply, .operand(3), .plus, .operand(15) + ] + + let result = try calculator.calculate(expressions: expression) + XCTAssertEqual(result, 23.5) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Stack/MinStackTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Stack/MinStackTests.swift new file mode 100644 index 0000000..0a5270b --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Stack/MinStackTests.swift @@ -0,0 +1,32 @@ +// +// MinStackTests.swift +// +// +// Created by Stefan Jaindl on 14.06.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class MinStackTests: XCTestCase { + + open func testMinStack() throws { + //inits stack with 3x3 capacity: + let minStack = MinStack() + + minStack.push(value: 1) + minStack.push(value: 2) + XCTAssertEqual(try minStack.min(), 1) + + minStack.push(value: 3) + XCTAssertEqual(try minStack.min(), 1) + + minStack.push(value: 0) + XCTAssertEqual(try minStack.min(), 0) + + minStack.push(value: 50) + XCTAssertEqual(try minStack.min(), 0) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Stack/MultiStackTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Stack/MultiStackTests.swift new file mode 100644 index 0000000..1a90d18 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Stack/MultiStackTests.swift @@ -0,0 +1,36 @@ +// +// MultiStackTests.swift +// +// +// Created by Stefan Jaindl on 14.06.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class MultiStackTests: XCTestCase { + + open func testMultiStack() throws { + //inits stack with 3x3 capacity: + let multiStack = MultiStack(numberOfStacks: 3, len: 9) + + try multiStack.push(stackNumber: 0, element: 1) + try multiStack.push(stackNumber: 0, element: 2) + XCTAssertFalse(multiStack.isEmpty(stackNumber: 0)) + XCTAssertTrue(multiStack.isEmpty(stackNumber: 1)) + + try multiStack.push(stackNumber: 0, element: 3) + + //Cause array to shift elements for stack 1: + try multiStack.push(stackNumber: 0, element: 4) + + XCTAssertEqual(try multiStack.peek(stackNumber: 0), 4) + XCTAssertEqual(try multiStack.pop(stackNumber: 0), 4) + XCTAssertEqual(try multiStack.pop(stackNumber: 0), 3) + XCTAssertEqual(try multiStack.pop(stackNumber: 0), 2) + XCTAssertEqual(try multiStack.pop(stackNumber: 0), 1) + XCTAssertTrue(multiStack.isEmpty(stackNumber: 0)) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Stack/QueueWithStackTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Stack/QueueWithStackTests.swift new file mode 100644 index 0000000..1854fed --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Stack/QueueWithStackTests.swift @@ -0,0 +1,28 @@ +// +// QueueWithStackTests.swift +// +// +// Created by Stefan Jaindl on 21.06.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class QueueWithStackTests: XCTestCase { + + open func testQueueWithStacks() throws { + let queue = QueueWithStacks() + + try queue.enqueue(value: 1) + try queue.enqueue(value: 2) + try queue.enqueue(value: 3) + + XCTAssertFalse(queue.isEmpty()) + XCTAssertEqual(try queue.dequeue(), 1) + XCTAssertEqual(try queue.dequeue(), 2) + XCTAssertEqual(try queue.dequeue(), 3) + XCTAssertTrue(queue.isEmpty()) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Stack/StackSetTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Stack/StackSetTests.swift new file mode 100644 index 0000000..9a23e06 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Stack/StackSetTests.swift @@ -0,0 +1,30 @@ +// +// StackSetTests.swift +// +// +// Created by Stefan Jaindl on 21.06.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class StackSetTests: XCTestCase { + + open func testStackSet() throws { + let stackSet = StackSet(treshold: 3) + + try stackSet.push(element: 1) + try stackSet.push(element: 2) + try stackSet.push(element: 3) + + //push on 2nd stack: + try stackSet.push(element: 4) + try stackSet.push(element: 5) + + XCTAssertEqual(try stackSet.pop(at: 0), 3) + XCTAssertEqual(try stackSet.pop(at: 1), 5) + XCTAssertEqual(try stackSet.pop(), 4) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Stack/StackSortTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Stack/StackSortTests.swift new file mode 100644 index 0000000..f872442 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Stack/StackSortTests.swift @@ -0,0 +1,31 @@ +// +// SortStackTests.swift +// +// +// Created by Stefan Jaindl on 14.06.20. +// + +import Foundation + +import XCTest +@testable import DataStructuresAlgorithms + +open class StackSortTests: XCTestCase { + + open func testSortStack() throws { + let stack = Stack() + stack.push(val: 3) + stack.push(val: 1) + stack.push(val: 5) + stack.push(val: 2) + + StackSort.sort(stack: stack) + + var result: [Int] = [] + while !stack.isEmpty() { + result.append(try stack.pop()) + } + + XCTAssertEqual(result, [1, 2, 3, 5]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Threading/DeadlockPreventingLockTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Threading/DeadlockPreventingLockTests.swift new file mode 100644 index 0000000..68ead6c --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Threading/DeadlockPreventingLockTests.swift @@ -0,0 +1,33 @@ +// +// DeadlockPreventingLockTests.swift +// +// +// Created by Stefan Jaindl on 07.10.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class DeadlockPreventingLockTests: XCTestCase { + + open func testDeadlockPreventingLock() { + let v1 = Vertice(id: 0) + let v2 = Vertice(id: 1) + let v3 = Vertice(id: 2) + let v4 = Vertice(id: 3) + let v5 = Vertice(id: 4) + let v7 = Vertice(id: 5) + let v9 = Vertice(id: 6) + + let threadOneUsage = [v1, v2, v3, v4] + let threadTwoUsage = [v1, v3, v5] + let threadThreeUsage = [v7, v5, v9, v2] + + let deadlockChecker = DeadlockPreventingLock(vertices: [v1, v2, v3, v4, v5, v7, v9]) + + XCTAssertTrue(deadlockChecker.canAddThread(with: RessourceUsage(lockOrder: threadOneUsage))) + XCTAssertTrue(deadlockChecker.canAddThread(with: RessourceUsage(lockOrder: threadTwoUsage))) + XCTAssertFalse(deadlockChecker.canAddThread(with: RessourceUsage(lockOrder: threadThreeUsage))) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Threading/DiningPhilosophersTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Threading/DiningPhilosophersTests.swift new file mode 100644 index 0000000..14a2f9a --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Threading/DiningPhilosophersTests.swift @@ -0,0 +1,38 @@ +// +// DiningPhilosophersTests.swift +// +// +// Created by Stefan Jaindl on 07.10.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class DiningPhilosophersTests: XCTestCase { + + open func testDiningPhilosophers() { + let chopstickOne = Chopstick(index: 1) + let chopstickTwo = Chopstick(index: 2) + let chopstickThree = Chopstick(index: 3) + let chopstickFour = Chopstick(index: 4) + let chopstickFive = Chopstick(index: 5) + + var philosophers: [Philosopher] = [] + philosophers.append(Philosopher(name: "Plato", left: chopstickOne, right: chopstickFive)) + philosophers.append(Philosopher(name: "Aristoteles", left: chopstickTwo, right: chopstickOne)) + philosophers.append(Philosopher(name: "Ronaldo", left: chopstickThree, right: chopstickTwo)) + philosophers.append(Philosopher(name: "Messi", left: chopstickFour, right: chopstickThree)) + philosophers.append(Philosopher(name: "Einstein", left: chopstickFive, right: chopstickFour)) + + for _ in 0 ..< 3 { + for philosopher in philosophers { + philosopher.eat() + } + } + + sleep(4) + + XCTAssertEqual(chopstickOne.lockedCount, 6) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Threading/FizzBuzzTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Threading/FizzBuzzTests.swift new file mode 100644 index 0000000..4292c26 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Threading/FizzBuzzTests.swift @@ -0,0 +1,22 @@ +// +// FizzBuzzTests.swift +// +// +// Created by Stefan Jaindl on 09.10.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class FizzBuzzTests: XCTestCase { + + open func testFizzBuzz() { + let fizzBuzz = FizzBuzz(targetNumber: 21) + fizzBuzz.play() + + sleep(2) + + XCTAssertEqual(fizzBuzz.fizzBuzzData.result, ["1", "2", "Fizz", "4", "Buzz", "Fizz", "7", "8", "Fizz", "Buzz", "11", "Fizz", "13", "14", "FizzBuzz", "16", "17", "Fizz", "19", "Buzz", "Fizz"]) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/CountOfTwosTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/CountOfTwosTests.swift new file mode 100644 index 0000000..604b450 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/CountOfTwosTests.swift @@ -0,0 +1,51 @@ +// +// CountOfTwosTests.swift +// +// +// Created by Stefan Jaindl on 05.12.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class CountOfTwosTests: XCTestCase { + + open func testTwosCount() { + let counter = CountOfTwos() + + XCTAssertEqual(counter.countOfTwos(number: 0), 0) + XCTAssertEqual(counter.countOfTwos(number: 1), 0) + XCTAssertEqual(counter.countOfTwos(number: 2), 1) + XCTAssertEqual(counter.countOfTwos(number: 3), 1) + XCTAssertEqual(counter.countOfTwos(number: 10), 1) + XCTAssertEqual(counter.countOfTwos(number: 11), 1) + XCTAssertEqual(counter.countOfTwos(number: 12), 2) + XCTAssertEqual(counter.countOfTwos(number: 19), 2) + XCTAssertEqual(counter.countOfTwos(number: 20), 3) + XCTAssertEqual(counter.countOfTwos(number: 22), 6) + XCTAssertEqual(counter.countOfTwos(number: 25), 9) + XCTAssertEqual(counter.countOfTwos(number: 30), 13) + XCTAssertEqual(counter.countOfTwos(number: 99), 20) + XCTAssertEqual(counter.countOfTwos(number: 100), 20) + XCTAssertEqual(counter.countOfTwos(number: 122), 26) + XCTAssertEqual(counter.countOfTwos(number: 130), 33) + XCTAssertEqual(counter.countOfTwos(number: 199), 40) + XCTAssertEqual(counter.countOfTwos(number: 200), 41) + XCTAssertEqual(counter.countOfTwos(number: 250), 106) + XCTAssertEqual(counter.countOfTwos(number: 300), 160) + XCTAssertEqual(counter.countOfTwos(number: 400), 180) + XCTAssertEqual(counter.countOfTwos(number: 900), 280) + XCTAssertEqual(counter.countOfTwos(number: 999), 300) + XCTAssertEqual(counter.countOfTwos(number: 1000), 300) + XCTAssertEqual(counter.countOfTwos(number: 2000), 601) + XCTAssertEqual(counter.countOfTwos(number: 3000), 1900) + XCTAssertEqual(counter.countOfTwos(number: 5000), 2500) + XCTAssertEqual(counter.countOfTwos(number: 9000), 3700) + XCTAssertEqual(counter.countOfTwos(number: 10000), 4000) + XCTAssertEqual(counter.countOfTwos(number: 20000), 8001) + XCTAssertEqual(counter.countOfTwos(number: 30000), 22000) + XCTAssertEqual(counter.countOfTwos(number: 100000), 50000) + XCTAssertEqual(counter.countOfTwos(number: 100002), 50001) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/DeckShufflerTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/DeckShufflerTests.swift new file mode 100644 index 0000000..6bade31 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/DeckShufflerTests.swift @@ -0,0 +1,25 @@ +// +// DeckShufflerTests.swift +// +// +// Created by Stefan Jaindl on 04.12.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class DeckShufflerTests: XCTestCase { + + open func testShuffle() { + let shuffler = DeckShuffler() + + var randomSet = Set() + for _ in 0 ..< 10 { + let shuffledDeck = shuffler.shuffle(deck: Deck(cards: Deck.allCards())) + randomSet.insert(shuffledDeck.cards[0]) + } + + XCTAssertTrue(randomSet.count >= 8) //statistical assumption + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/HistogramVolumeTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/HistogramVolumeTests.swift new file mode 100644 index 0000000..3c4b474 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/HistogramVolumeTests.swift @@ -0,0 +1,20 @@ +// +// HistogramVolumeTests.swift +// +// +// Created by Stefan Jaindl on 18.12.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class HistogramVolumeTests: XCTestCase { + + open func testVolume() { + let volumer = HistogramVolume() + + XCTAssertEqual(volumer.volume(of: [3, 1, 4, 0, 0, 6, 0, 3, 0, 2]), 15) + XCTAssertEqual(volumer.volume(of: [0, 0, 4, 0, 0, 6, 0, 0, 3, 0, 5, 0, 1, 0, 0, 0]), 26) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/KthMultipleTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/KthMultipleTests.swift new file mode 100644 index 0000000..e66fbb3 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/KthMultipleTests.swift @@ -0,0 +1,30 @@ +// +// KthMultipleTests.swift +// +// +// Created by Stefan Jaindl on 07.12.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class KthMultipleTests: XCTestCase { + + open func testMultiples() throws { + let multipler = KthMultiple(multiples: [3, 5, 7]) + + XCTAssertEqual(multipler.allToKthMultipleComposedOfOnlyLowestOddPrimes(k: 25), [1, 3, 5, 7, 9, 15, 21, 25]) + XCTAssertEqual(multipler.allToKthMultipleComposedOfOnlyLowestOddPrimesFast(k: 25), [1, 3, 5, 7, 9, 15, 21, 25]) + + XCTAssertEqual(try multipler.kthMultipleComposedOfOnlyLowestOddPrimes(k: 0), nil) + XCTAssertEqual(try multipler.kthMultipleComposedOfOnlyLowestOddPrimes(k: 1), 1) + XCTAssertEqual(try multipler.kthMultipleComposedOfOnlyLowestOddPrimes(k: 2), 3) + XCTAssertEqual(try multipler.kthMultipleComposedOfOnlyLowestOddPrimes(k: 3), 5) + XCTAssertEqual(try multipler.kthMultipleComposedOfOnlyLowestOddPrimes(k: 4), 7) + XCTAssertEqual(try multipler.kthMultipleComposedOfOnlyLowestOddPrimes(k: 5), 9) + XCTAssertEqual(try multipler.kthMultipleComposedOfOnlyLowestOddPrimes(k: 6), 15) + XCTAssertEqual(try multipler.kthMultipleComposedOfOnlyLowestOddPrimes(k: 7), 21) + XCTAssertEqual(try multipler.kthMultipleComposedOfOnlyLowestOddPrimes(k: 8), 25) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/LangthonsCellTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/LangthonsCellTests.swift new file mode 100644 index 0000000..4177615 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/LangthonsCellTests.swift @@ -0,0 +1,21 @@ +// +// LangthonsCellTests.swift +// +// +// Created by Stefan Jaindl on 01.12.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class LangthonsCellTests: XCTestCase { + + open func testLangthons() { + let langthons = Langthons() + + let grid = langthons.performMoves(kMoves: 10) + + XCTAssertTrue(!grid.isEmpty) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/MasterMindCheckerTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/MasterMindCheckerTests.swift new file mode 100644 index 0000000..35c20c0 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/MasterMindCheckerTests.swift @@ -0,0 +1,27 @@ +// +// MasterMindCheckerTests.swift +// +// +// Created by Stefan Jaindl on 27.11.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class MasterMindCheckerTests: XCTestCase { + + open func testMasterMindChecker() throws { + let checker = MasterMindChecker() + + let guess: [MasterMindColor] = [ + .blue, .blue, .yellow, .red + ] + + let solution: [MasterMindColor] = [ + .yellow, .blue, .green, .red + ] + + XCTAssertEqual(try checker.check(guess: guess, solution: solution), MasterMindCheckResult(hits: 2, pseudohits: 1)) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/Rand7Tests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/Rand7Tests.swift new file mode 100644 index 0000000..92e4617 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/Rand7Tests.swift @@ -0,0 +1,38 @@ +// +// Rand7Tests.swift +// +// +// Created by Stefan Jaindl on 01.12.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class Rand7Tests: XCTestCase { + + open func testRand7() { + let rand7 = Rand7() + + var array: [Int] = [Int](repeating: 0, count: 7) + + for _ in 0 ..< 10000 { + let random = rand7.random() + array[random] += 1 + } + + var average = 0 + array.forEach { entry in + average += entry + } + + average /= 7 + + let lowerBound = Int(Double(average) * 0.75) + let upperBound = Int(Double(average) * 1.25) + + array.forEach { entry in + XCTAssertTrue(entry > lowerBound && entry < upperBound) + } + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/RandomSetTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/RandomSetTests.swift new file mode 100644 index 0000000..461db82 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/RandomSetTests.swift @@ -0,0 +1,26 @@ +// +// RandomSetTests.swift +// +// +// Created by Stefan Jaindl on 04.12.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class RandomSetTests: XCTestCase { + + open func testRandomSet() { + let randomizer = RandomSet() + + var randomSet = Set() + + for _ in 0 ..< 20 { + let random = randomizer.randomSet(of: 5, from: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) + randomSet.insert(random[0]) + } + + XCTAssertTrue(randomSet.count >= 8) //statistical assumption + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/SquareConnectorTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/SquareConnectorTests.swift new file mode 100644 index 0000000..94e4ac2 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/SquareConnectorTests.swift @@ -0,0 +1,42 @@ +// +// SquareConnectorTests.swift +// +// +// Created by Stefan Jaindl on 26.11.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class SquareConnectorTests: XCTestCase { + + open func testSquareConnector() { + let connector = SquareConnector() + + //Slope 1 squares + var firstSquare = Square(topLeftCorner: CGPoint(x: 2, y: 4), length: 2) + var secondSquare = Square(topLeftCorner: CGPoint(x: 6, y: 8), length: 2) + XCTAssertEqual(connector.connect(firstSquare: firstSquare, secondSquare: secondSquare), Line(startPoint: CGPoint(x: 2, y: 2), endPoint: CGPoint(x: 8, y: 8))) + + //Same squares - straight line to top through x center + firstSquare = Square(topLeftCorner: CGPoint(x: 2, y: 4), length: 2) + secondSquare = Square(topLeftCorner: CGPoint(x: 2, y: 4), length: 2) + XCTAssertEqual(connector.connect(firstSquare: firstSquare, secondSquare: secondSquare), Line(startPoint: CGPoint(x: 3, y: 2), endPoint: CGPoint(x: 3, y: 4))) + + //Stapled squares - straight line to top through x center + firstSquare = Square(topLeftCorner: CGPoint(x: 2, y: 4), length: 2) + secondSquare = Square(topLeftCorner: CGPoint(x: 2, y: 6), length: 2) + XCTAssertEqual(connector.connect(firstSquare: firstSquare, secondSquare: secondSquare), Line(startPoint: CGPoint(x: 3, y: 2), endPoint: CGPoint(x: 3, y: 6))) + + //slope > 1 squares + firstSquare = Square(topLeftCorner: CGPoint(x: 2, y: 4), length: 2) + secondSquare = Square(topLeftCorner: CGPoint(x: 3, y: 7), length: 2) + XCTAssertEqual(connector.connect(firstSquare: firstSquare, secondSquare: secondSquare), Line(startPoint: CGPoint(x: 2.6666666666666665, y: 2), endPoint: CGPoint(x: 4.333333333333333, y: 7))) + + //slope < 1 squares + firstSquare = Square(topLeftCorner: CGPoint(x: 2, y: 4), length: 2) + secondSquare = Square(topLeftCorner: CGPoint(x: 6, y: 5), length: 2) + XCTAssertEqual(connector.connect(firstSquare: firstSquare, secondSquare: secondSquare), Line(startPoint: CGPoint(x: 2, y: 2.75), endPoint: CGPoint(x: 8, y: 4.25))) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/WordDistanceTests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/WordDistanceTests.swift new file mode 100644 index 0000000..0a27983 --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/SpecificAlgorithms/Various/WordDistanceTests.swift @@ -0,0 +1,48 @@ +// +// WordDistanceTests.swift +// +// +// Created by Stefan Jaindl on 09.12.20. +// + +import Foundation +import XCTest +@testable import DataStructuresAlgorithms + +open class WordDistanceTests: XCTestCase { + + open func testMinDistance() { + let words = [ + "test", + "index", + "lauser", + "alaba", + "whatever", + "test", + "test2", + "google", + "", + "empty", + "xxx", + "juventus", + "sturm", + "gak", + "index" + ] + + let wordDistance = WordDistance(words: words) + + XCTAssertEqual(wordDistance.distance(first: "test", second: "index"), 1) + XCTAssertEqual(wordDistance.distance(first: "test", second: "test2"), 1) + XCTAssertEqual(wordDistance.distance(first: "test", second: "google"), 2) + XCTAssertEqual(wordDistance.distance(first: "test", second: "lauser"), 2) + XCTAssertEqual(wordDistance.distance(first: "sturm", second: "index"), 2) + XCTAssertEqual(wordDistance.distance(first: "juventus", second: "gak"), 2) + XCTAssertEqual(wordDistance.distance(first: "lauser", second: "gak"), 11) + XCTAssertEqual(wordDistance.distance(first: "index", second: "index"), 0) + XCTAssertEqual(wordDistance.distance(first: "", second: "index"), 6) + XCTAssertEqual(wordDistance.distance(first: "test", second: "test3"), nil) + XCTAssertEqual(wordDistance.distance(first: "test3", second: "index"), nil) + XCTAssertEqual(wordDistance.distance(first: "wrong", second: "right"), nil) + } +} diff --git a/SPM/Tests/DataStructuresAlgorithmsTests/XCTestManifests.swift b/SPM/Tests/DataStructuresAlgorithmsTests/XCTestManifests.swift new file mode 100644 index 0000000..166f1fd --- /dev/null +++ b/SPM/Tests/DataStructuresAlgorithmsTests/XCTestManifests.swift @@ -0,0 +1,9 @@ +import XCTest + +#if !canImport(ObjectiveC) +open func allTests() -> [XCTestCaseEntry] { + return [ + testCase(DataStructuresAlgorithmsTests.allTests) + ] +} +#endif diff --git a/SPM/Tests/LinuxMain.swift b/SPM/Tests/LinuxMain.swift new file mode 100644 index 0000000..53237e9 --- /dev/null +++ b/SPM/Tests/LinuxMain.swift @@ -0,0 +1,7 @@ +import XCTest + +import DataStructuresAlgorithmsTests + +var tests = [XCTestCaseEntry]() +tests += DataStructuresAlgorithmsTests.allTests() +XCTMain(tests)