From 8b4a0685df475c087c2ca440065b19c2d439a103 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 27 Oct 2020 14:25:01 +0530 Subject: [PATCH 001/117] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 13793e33..c076e8a4 100644 --- a/README.md +++ b/README.md @@ -67,4 +67,4 @@ To follow the guidelines, refer to [Contributing.md](CONTRIBUTING.md) ## :page_facing_up: License -[MIT @ Prabhu Pant](LICENSE) +[MIT](LICENSE) From fbe77052450c65a4031893e82fefac572b888131 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 27 Oct 2020 14:27:25 +0530 Subject: [PATCH 002/117] Update square_of_sorted_array.py --- data_structures/array/square_of_sorted_array.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/data_structures/array/square_of_sorted_array.py b/data_structures/array/square_of_sorted_array.py index 88ef6c87..1bebb758 100644 --- a/data_structures/array/square_of_sorted_array.py +++ b/data_structures/array/square_of_sorted_array.py @@ -26,8 +26,3 @@ def square(arr): j += 1 return ans - - - - - From cda8d2016aaf93d72518333dd58fd8edb3cfac85 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 27 Oct 2020 14:31:29 +0530 Subject: [PATCH 003/117] Added question and answer --- data_structures/array/max_product_three_elements.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data_structures/array/max_product_three_elements.py b/data_structures/array/max_product_three_elements.py index 0d31fd53..5883172e 100644 --- a/data_structures/array/max_product_three_elements.py +++ b/data_structures/array/max_product_three_elements.py @@ -1,3 +1,6 @@ +# Q - Find the max product of three elements of an array +# A - Find the max 3 numbers and 2 min numbers. Then find the max of (min1*min2*max1, max1*max2*max3) + import sys def product(arr): From 652caac783fac0a933a49c728e0dd10a930b6b5f Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 27 Oct 2020 14:33:16 +0530 Subject: [PATCH 004/117] Fix the name of the file Change the extension of the file from pyt (not a Python file) to py --- ...eck_divide_in_two_halves.pyt => check_divide_in_two_halves.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename data_structures/binary_trees/{check_divide_in_two_halves.pyt => check_divide_in_two_halves.py} (100%) diff --git a/data_structures/binary_trees/check_divide_in_two_halves.pyt b/data_structures/binary_trees/check_divide_in_two_halves.py similarity index 100% rename from data_structures/binary_trees/check_divide_in_two_halves.pyt rename to data_structures/binary_trees/check_divide_in_two_halves.py From c91dda1874083b414e0c718fdd12aad411146a34 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Mon, 4 Jan 2021 12:14:41 +0530 Subject: [PATCH 005/117] Update misc.md --- bookmarks/misc.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bookmarks/misc.md b/bookmarks/misc.md index dbb88192..2afa94e6 100644 --- a/bookmarks/misc.md +++ b/bookmarks/misc.md @@ -17,3 +17,5 @@ This is a list of misc links that may be useful when learning or researching dat - https://stackoverflow.com/questions/10631326/difference-between-select-into-and-insert-into-from-old-table - Why SSL are not issued for IP address - https://stackoverflow.com/a/33419662/6111200 + +- PATCH vs PUT https://stackoverflow.com/questions/28459418/use-of-put-vs-patch-methods-in-rest-api-real-life-scenarios/39338329#39338329 From 6e8b81eb52a6340e5375798d0a55f796fde94bfc Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Wed, 13 Jan 2021 09:30:34 +0530 Subject: [PATCH 006/117] Update articles.md --- bookmarks/articles.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bookmarks/articles.md b/bookmarks/articles.md index a2b8e1ca..81c00863 100644 --- a/bookmarks/articles.md +++ b/bookmarks/articles.md @@ -55,3 +55,5 @@ This is a list of articles that may be useful for algorithms and data structures - https://www.ultravioletsoftware.com/single-post/2017/03/23/An-introduction-into-the-WSGI-ecosystem - https://jwt.io/introduction/ + +- https://khashtamov.com/en/how-to-become-a-data-engineer/ From 27ce2622793f4951ae166414a2a2cfbf011ddece Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Wed, 20 Jan 2021 19:22:47 +0530 Subject: [PATCH 007/117] Delete videos.md --- bookmarks/videos.md | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 bookmarks/videos.md diff --git a/bookmarks/videos.md b/bookmarks/videos.md deleted file mode 100644 index 0ac8813e..00000000 --- a/bookmarks/videos.md +++ /dev/null @@ -1,9 +0,0 @@ -# Videos - -This is a list of video links that may be useful for algorithms and data structures learning. - -## Links: - -- https://www.youtube.com/watch?v=mSzUb7f47qk - -- https://www.youtube.com/user/mycodeschool \ No newline at end of file From 79206a4bca47c265e23d4e4baee24e7c5fe63c3c Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Mon, 22 Feb 2021 18:53:39 +0530 Subject: [PATCH 008/117] Remove index updater --- index_updater.py | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100755 index_updater.py diff --git a/index_updater.py b/index_updater.py deleted file mode 100755 index 6f4e1bb3..00000000 --- a/index_updater.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import os - -# Terminal parameters. -if len(sys.argv[1:]) == 0: - print('Please, give a path') - sys.exit(1) -else: - mypath = sys.argv[1] - -# Going through folders and subfolders. -for root, dirs, files in os.walk(mypath): - curr_files = list() - curr_folder = os.path.basename(root) - - for name in files: - if name.endswith('.py') and not name == '__init__.py' and not dirs: - curr_files.append(name) - - # If we have files, write "index.md" - if curr_files: - with open(os.path.join(root, 'index.md'), mode='w') as md_file: - md_file.write('# Index of {}\n\n'.format(curr_folder)) - for line in curr_files: - file_name = line.split('.')[0].replace('_',' ').title() - md_file.write('* [' + file_name + '](' +line + ')\n') From 4130ae411b963bbbf3655645927d845012864dcf Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Mon, 22 Feb 2021 18:55:14 +0530 Subject: [PATCH 009/117] Remove logo and emoticons --- README.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c076e8a4..cdbc6a72 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,12 @@ -![logo](logo/logo.png) - # Python Data Structures and Algorithms This repository contains questions requiring implementation of data structures and algorithms concepts. It is useful for interviews in Python. -## :dart: Objective +## Objective The open source community has helped me a lot during my interview preparations and studies while I was in my undergrad. I always wanted to give something back to the community. In my endeavour to contribute something back, I will be uploading data structures and algorithms questions in Python in this repo. Feel free to contribute and get in touch! :smiley: -## :file_folder: Structure of the repository +## Structure of the repository As of now, the repository contains 3 main directories: [**Bookmarks**](bookmarks), [**Data Structures**](data_structures) and [**Algorithms**](algorithms). @@ -50,7 +48,7 @@ You can find useful links in this repository in the different markdown files. Be | Videos | [Click Here](bookmarks/videos.md) | | Misc. | [Click Here](bookmarks/misc.md) | -## :clipboard: Things need to be done +## Things need to be done As you can see, the repo is still in its infancy. Here are some key things in the to-do. @@ -58,13 +56,13 @@ As you can see, the repo is still in its infancy. Here are some key things in th 2. Algorithms 3. More questions in data structures, especially for graph, circular linked list, trees, heaps and hash. -## :raised_hand: Contributing +## Contributing Contributions are always welcomed. :smiley: Feel free to raise new issues, file new PRs. Consider giving it a star and fork this repo! :wink: To follow the guidelines, refer to [Contributing.md](CONTRIBUTING.md) -## :page_facing_up: License +## License [MIT](LICENSE) From a3c864c4807b709c64e5688055a762d99e3288b3 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Mon, 22 Feb 2021 19:01:08 +0530 Subject: [PATCH 010/117] Remove emoticons from contributing.md --- CONTRIBUTING.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index be0d596c..7fb881f7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ This is a project that is for the community and its true essence is only possible when it is community driven. -Please feel free to contribute to this project and be a part of this. Anything from raising issues to adding new features or even a typo in documentations, all are welcome. Please report issues here [https://github.com/prabhupant/python-ds/issues](https://github.com/prabhupant/python-ds/issues). It is also recommended to go through the Contributions Best Practices below to help organize contributions :smiley: +Please feel free to contribute to this project and be a part of this. Anything from raising issues to adding new features or even a typo in documentations, all are welcome. Please report issues here [https://github.com/prabhupant/python-ds/issues](https://github.com/prabhupant/python-ds/issues). It is also recommended to go through the Contributions Best Practices below to help organize contributions ## Contributions Best Practices @@ -18,7 +18,7 @@ Please feel free to contribute to this project and be a part of this. Anything f ### Issues -Feel free to open up any issue in the repository, whether it is about code improvements or bug fixes or documentation. You can also use any label you want to associate with the issue. Please provide a clear description of the issue while filing it :smiley: +Feel free to open up any issue in the repository, whether it is about code improvements or bug fixes or documentation. You can also use any label you want to associate with the issue. Please provide a clear description of the issue while filing it. ### Code Styling Guide @@ -26,9 +26,9 @@ Python follows a Pep8 styling. Styling the code according to it makes it univers ### Questions -While there are infinite number of data structure and algorithm questions, it is impossible to collect all of them here. So please add only those questions that are either unique or tricky or have some mind blowing approach to solve them. Also, try not to file a PR for a question which is already present in the repo. Interesting questions and implementations are always welcomed :wink: +While there are infinite number of data structure and algorithm questions, it is impossible to collect all of them here. So please add only those questions that are either unique or tricky or have some mind blowing approach to solve them. Also, try not to file a PR for a question which is already present in the repo. Interesting questions and implementations are always welcomed. -For filing a PR for a new question, please open a issue first for the same and then reference it in the PR. For example, let's say you want to add a new question called "find max element in array". So first head over to the issues tab and create a new issue called "New question: find max element in array". Then if you want to work on it, please mention it in the description. After you are done writing the code and ready to file a PR, refer to the issue number in the PR description. Let's say the issue number was #23. So in the PR description, it should be "Fixes #23". That's it! :smiley: +For filing a PR for a new question, please open a issue first for the same and then reference it in the PR. For example, let's say you want to add a new question called "find max element in array". So first head over to the issues tab and create a new issue called "New question: find max element in array". Then if you want to work on it, please mention it in the description. After you are done writing the code and ready to file a PR, refer to the issue number in the PR description. Let's say the issue number was #23. So in the PR description, it should be "Fixes #23". That's it! ### Bookmarks @@ -36,13 +36,13 @@ The Bookmarks directory is for storing awesome links to articles, videos, slides * Don't add a link to a MOOC or course (like DS Algo MIT lectures) -* Ask yourself "Is the link interesting enough that someone will really love it?" or "Is it something that an interviewer might ask in the interview to test you?" or "Is it some common or amazing thing people don't have enough knowledge about?". If yes to any of them, then sure, quickly file the PR :wink: +* Ask yourself "Is the link interesting enough that someone will really love it?" or "Is it something that an interviewer might ask in the interview to test you?" or "Is it some common or amazing thing people don't have enough knowledge about?". If yes to any of them, then sure, quickly file the PR. * If an article has more than one part, be sure to refer to the first only in the PR. ## How To Contribute? -You can add anything to the repo as long as it is related to programming and increasing knowledge :smiley: +You can add anything to the repo as long as it is related to programming and increasing knowledge. 0. Firstly, fork the repo to your GitHub account. Just to get familiar with some terms, this will be the `origin`. To state `origin` and `upstream` explicitly, @@ -80,13 +80,13 @@ $ git push origin max-number-in-array 6. Write a good brief description and refer to the issue (if any) and submit the PR. -7. Now wait for the approval :smiley: +7. Now wait for the approval. ## Join The Development Before you join the development, please fork and clone the repository to your local machine and explore it. Don't worry nothing will happen, atmost some code might not work :wink: -Feel free to contribute and be a part of this endeavour :beers: +Feel free to contribute and be a part of this endeavour. From 0894eea3d945b400c2b454a779a3698347dd5b3d Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 23 Feb 2021 11:50:04 +0530 Subject: [PATCH 011/117] remove logo --- logo/logo.png | Bin 9910 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 logo/logo.png diff --git a/logo/logo.png b/logo/logo.png deleted file mode 100644 index 28749cb76889825a2f93f55013728fad2adaabec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9910 zcma)i1yEeg*6m=y-67cEt|7P%!C`QBcZb2v_> zdVkfMsxxz1YV|&QpXpw`dPjqmq|s4GPyhe`x~vRH6##%?f!>3W-a)?$k7azJKQL~p z(h`8GiT6j)0aE4&ToV=e1wVKfCh1sO(r0|)Q30QN%at>c~7xo*Nt5*1@ zcrAM0Tf^yNaI{sN`Ovl__19u4Q2wEk=($J`AnP85bI2V{jGnr0$I%Q98{z=#8xjI* z!{<`sMhS)arfz9>P!>$Wg4|&E7s>8g(KrIIvjB-^h${)|b7G{M+uPeotX0M>&CO^e z`aP~LF2p>|A{B;`=^ig@X2M1MvUbAArd19VEmx<(jUxklGK2UP?->ns-;BWR!6ms2 z936nXWZ2Sb_)V15*iou}XluUnPry(csy7?kOFXkSTZh#;(@q#8>q9uU6P3N7QTj04 z(R3b;1$eb)U=DlrQ{=A)dzR{=v)J60gDJ`Kpig0q1@386O=DG^Sy&fvGM% z!2vdGk)lvQ^H2at<$=r$Z680{#*YB#2JnqwK!d9<=>H%sNkjg?1V+FjrcEdfdu5?q z;fcef8f4yy|6s&d^qg~0dw;8y;b>$p{#&+DezG}aZ-$y)-$QGw#-xE;Dk6(rE?0UR z1P~De*=6=F9Y#rD(;>$vOyxx=Vo@54afs*9i#dN8vgIc8bC5*B3G0ve1O#Hq2xW`p>%s;}(D_^GQ39|vDEkSM zxsdX8tbeEDs$0a+ev*5`u&m&|!5HI#SYW(Du6TRsSyls#Pphlf z7h>;@Yqpa~Po#aRZx2AFvw$xMoOoiSAYRz3nEY(glDh0zd~)dXZ6h()+6XQTfm9rEGF#-Z!R%p)r3UKDum z?nRb}Cb=tKc^O)?N2~XflMQUxrb|i4&!agHR%>O+Q+Q}P-_)hnAk~ADRPFkd!WJH3GAxVvU(P?Sxj67F-r40b8fxAlFEyuc3SnQG zx5kT+cI$%fziz&()t)fcTTW1u>6f0C<36B<70>VlMjkx6pyK* zDV{*54bS#&ioOR$eQp9UI}o$^6vr&LIKfsfSjc`_T!6>>w*o`;bw+1cCvBSh5-p6Z&&Z7y|hR^SakYy(fe4b-DN?@gIieJY|Y6VKc}f4ZK9+R zap%jR?F3tQK5=uZGPi?cggA22g>u{mGKZvEY&-*-e_b5lPwKdyr!7`!*N85@BF2d6 zoTAq};70ArVpeMe!HQKC5~9Ldcq}5?$(;!|H_rLXR~)ip;qBjkS0LDYB1t^QOPimM z0PcGkb)Cj^lzQ#)(9&=aM7>D#ZGMGK&&pH@irR&1Zi3jnBt-0?2ClAk$dQ&e{Oon9 zm3K5d&Z50jGO9D}dDMS8x0f6ZR}$5Od0krN<(63Tv>O`i#{YB~EdRDky0D725>cPm zs+Ic`R6F(9RIF8TMV~t$IBH^^v$Q#HXv;*>bDj$ zLNQHxT{O@Lla60t$B#&qoSm(_8txtdPd$@?lf~mZ`TZeIY>=xWy(k=TkmYLja=7Sd z&`iCUW^6Nwu)SG9cUj!Os5mO0^X|g^SQt9l=AtHTg!cEjYW0cVBGO z(N=qGN?rv5au6?`|6H{W!QY2Us_ke5pECng-;jnYXuT*b7$yl3gt8kIqx#pj#O!U@ z$I~7OP$Wj~*&oV@E{61hDZf;3<@^~0ejGFDrx)bN9AhFqUg>!FD z_uU=9YXnQd76Ko03ssUR%9o|TlIog8$oJeSR(gOlma>vHQ!u%_le|(+$9G4~ofw=0 z%vpe{`)rvB4hE}N>nAQ2^*!`a`K;fpb}$vJx_-VXiRVuklqq~fiLi13e;_$aa%I<{ zqlBG_uC%au_}!lXZ}1m^u=3{*JpG2L+U`IXr<&FicdSE6VHO-a;;o~&Bh&fth>Y;~ z<(Ra@i7tD&-KRC-TpL{mti6UqzRFS9p@U=*_}QLNq-fTaeH-+78)*N*<0R3L=8dBa z>j(XnUn6pyDFTU&!W1B?QmH7&1ez2`3`Za!A#4HEIoY4i{bc|lc*prKSd^vGWSPYL zj*VXSK^M6Pq77pCj0&q-zkn47_Wu_|cOwlbPMmlwMReGlVR3WL>)+yxW7woq|JoncHftc4tV#g*@oLb$)|ICDd4w?Z z!;iE3?8L@3pk(_5b9G%t0 zo{$y(Sm!+h-s7Ca8bO|GBuJbvR?ZA_uz@Hb%!0vkQ#diEHyK{F``O>Bc`ub#GKjYC zwzp@COnOx$$*f#q#i)79fKeq5Vqy^VwNe>Q|2ZID{MEf>(iS(sofIeOiYag!Y>?q0 z69XuR$@&@~t~Aj(`ML#VS*ZOpi~N;-I28^RY%TPwjU;weex{zw!bSv#($n&+SSH=e z1nsyLx_|YnPCu}+dy{t~q*vYb$~0ON8TmgE{@_Q*WgORQ=>3?zZBnv_10Y6Vo~=*V zC8SHM8goM-7z2&wuv(GGMHDYn-p_7jkVu5+ZjmaE5_V*sa;IvGSI+`6h`#!akZKg> za}Y+{=aq_=eCv{vH_0L7jwg-NQH$@v>T0?s{(-0Nw~NqU6q6CQVOP+7a733BbU$-> zZgM=VsjXWh`e)#pLZQ28f9rz8)83d6(*wXr6K`oRmz83S?MHQQ@_P5PqBYP15}yM z9LcatW%v0A*1Qvf&sFT83qgf>X4$D@Cinw=2ShO3$tqZOx9ZBxN7?Y|^>5Lz?`Y)m>|VjW+rt`&BIOfdAzK)^OkQ zr`TeN#M7Bk1qP}4<7cm&Nb{lW-_qL2>xktBubtMHr$t=m?H^|5P#7O@Lr63>SKBXXu<v}HYe6u$#Dax4+ zz8?eEAJ8oqa)U#o6&*RVv~-K`wJL<_@(E{q&ki>f89=Xle&pf77R+rL=6#xhROzpM zc>3R{;#uSP<+YSThwJ~+?dJD+2rLoO?6VycIQ1ZX1#%mcAm8EC(|y9*&%ehtgQMvHhxrl1m;CL21Ee0V z&u%47Pfs{|m$ryV5{%z+#&cs34U2mi+(?2cSR|-l&?$Av*lj%mRR%^vj}iZ9^{A$f z9C9qE(|g|8@6StoMo*rfm?#QlO36t>3X;P(neUPOmE+G!*S#Y)sCj^u852ixJTzBJ zou{sqFBbtiC^I?E|B5mCQR?F$*JhE&SL- z^g?ltE;-P`ooPO|7O%rrtbOCFK#-$gxTcSK0V=(3#T4N-44`wtg0+n=c<=f8{q+;W zkVW%fr7n`B5DPs~gyx~6j3@}aYqsi@V_y61!}8wEo?B{_Lgt>iyy#*dj;gH}%FKXl zEXmC5Y((Yj_BTBwL$thANL4|mCb}eFGJ%$4kEHOx@%vw(TWO5|@(9T~?j9XR@qipe zZ$n(fK`jGp7+nbyzxrbQCrJYPLD~q!qA*~P!|KvcH__M25k!dpEJiZoxU!sk})RD&18k2Z9Sjh7kbiA2(p!$cKMw*Y+59#QS=?W@PTpp}hihu6kpCgK-%ZBXVlQ>3}Z7vQXI{!k;^yVgph6kal# z7VhTpH+$X}^3oCv(R8a-164B_kOQfv9G9Ev{Mk*M;6>ZedUxlVlgaewJC~6|GKLG9 z15f(cI59?38ABw=tKN%2{WF>VX?jtUA+k%Z;eIA|D zv=&uNtt{AQ3YeVxgEay?jn=925d3n;w=YHJ70Ssm(BMWv z>VJ}bQVGH-KJ+{z=CYqrTfX)FZW`{N8nuG-Ljbid79e=nu#;U`8%_b6jZaX(Yb*zE zhOz=SUIIsHIYqslI5vQjzRVrvS&%|pP9P-^4n|x<7HrlLd&HIoijCAh{2|d}8ce|+ zxuVj?5iO{BOr*AQR(g#d74NDMA6zlxv#srvbzuWVQ3+_(&ai0^d#?5>JyC%8e2c@b zSKcsJ&h7ofLm5-e*aW}4u-wd$0Yn8$GvuH81XpXkeg6!Z+N=?~g-=AAGS!|mzGGos zBj+`X{DH3!h-kRMOTca~Ob`%{pBoecK*o--X^eUwhBF&VE`X9MHY4|utg7PWW`32V zTt09XtmY$-ZziMh5dt&$_!*Qyk5|YeS!EHx9!ysHwZ ztM7#>hj)3b6}2WO7z$ZoY%%!PYS^ioklQtzr8%JrZsB@vHq;=-u&A>!!r$#q3^~8~ z5T1HP?$HFv6A{2N!39L0h*BGF8}Ly@Zq+1Ut1Yxwz6mv5*|^nA5J5eBj+8u~jchD_ z`~hYH${hj`iO^JVY8`@063yruuBxTrdq=k)!rTe?Cl?zUj`yN z`brwjcptdf#mez^I!o*fp!{H*cm4^Sbq1ZNyLEl zrQ7=|elvCmPQ=3p1~=FSDcLzy%FSz{3*R{H-Btp~xXfS-8u^dQ6ymopZDY|K_$$4J z_Z`@~#JmAlbIbQN7D!hL<=jr-p>W0R08bR6r;PeSvU4AavQ?CF3PQtD-5M&e;UbGn z^#NVRfhiWM<{Sqv#Qp#VSSn4`xjE$|QX%#*k`Bv1f0VH>({{x;Q68>#z|!I+0KkI? zPAJObQ{J9WON<zHSTi#gD0aQjx2Ved~!DdZJ750ipHD;k}QG{!}33T2W9^Y1Zi+ z>Ee2zRj;swjcH5@jee>2ZPWE-+M@C&>7~dh2eBb(&9MIK%AM8UUc7){G3ME(ue;|) z&!@d)!xw31JKu*{n1?O zX1!tCP^6U(HEhtJlQ_5LR3~b-UXV($bQ3DYQPnryUsz#a9seyM6jV;761MQE2XXsj+w^xp{U%KDH7q|r*hP85_dQgoTZ11?d$QF!Hr1+%vM?`e#*teG zf3WJoNmRDA*huqkD{+VFqz3cjnX0RLRwlEo*LT4f<>4rVLs^tZ4fzQE=mhvf4)s#s zG@>*6(lVFne8}=05vPIRONU)d24rH?S2QU_`z68mZG9rvCPIVUE_z8DF(K7C_XMfg zuUu6k9%S_de;n_Ic(aYuu<;YXG6`4~v0NXQH!Dxd2tQc0v2P6p7gavI(Hym3mM&;W zeXJa5Et7K5Ih}`pbuw~bVl#T!YwXbD9W&I;ttIen8q>27yU#|`YHN=5`9O=;6V4=h z{s3K5eSa%_02>-H5#R1n*S1mHTe8Guq2m?%qw>>(T_(e^_=`?#>d&kPMIUE!iHM)! z`=RXF%ZeqD+v-fZw`0hCIpcnah#h^R3c96tSq&wG{1rY}{6#!T5XwzY5Def%+8_7;zxt6N_LNy`a^ zaZeV;=Z|(VuN`0lHr-`GMI>9d8p0DnOw1a26}ORCE!-Y`%lgR4m1qgBxkz+O16}xO z^OXV~HkEE`Y=|*XL5qTDF@PPUH0ms$hHX(D=>m)ATWXkVVCWTz<4qa36y_0~IK%jH zfpAynmJq2yTWsji4L}J_7y5mI{&%m^k`GVzRs$K7*eWo8Dt=MjVz0dg>Z_JpM^0W0 zNN7)I0Yu4&IP64k|9t$pfbFeZU7bb2za{8}PCT>4ug1tm`Nuo>>aiNJoKR#g?G=-? z*BF0YwDQl{>egi(_AED{=A8Us6A)w%Z-G=(Crr^)ogMA58sB5HBiesSeG8>VL=h%K zf1wnGnJ4lYI@zL4I;2OXiM)?2Q7XHefO`S*qSvQv*-URuNqR~*+lsA+h>P__DKf*2 zf2}4Q9{XGF%9b)u#8Cbeu!{7SyQnpB?w*dv+9_=GaLty~aZjFfOGsP$<6~yL9C zREk2}6<@taNX^{P#rxDPNT|X=XpPTyr@Zvn*udhVa&Uf^g1FSN)lrbaZ>-yrP-<csjC7ccEYGB*B+nYK>$*kLoq-pQYM%tXRQ&(H_j!$*Q3I1qc0+J9S zg>e5NNN;8WRS9q(AF}UB#!!Bm5Vb2wi;hte2oTGZ#9(#$lLOi zvySZ&DW%dIRV`y11<8YWy9iMQT~5aMRCP*qRJ>TD4&I+AM=RiN_Jr(%^`QS6OO)FX zBHCK6S;ky%$C>1EH1P==Tk_FIofVe-AZ#mGJ~d*o%XLQHkrU6*-w553J=Q^eGaZG$ zEPED`XP{!?(hnS|2{j3vF8bMza9G{Uz@4mA{aJ^t%$17?LG2Wrtm1ISyM$A;aJQvZ zHg2+_NA&2=f{4f<{`qx+#8QOuQ+dn?^hm6NE6aD%j*#VrX$tZC@89+VRFamIYU%<~ zsDd`?VW@6BcTILWO|-jC7}^`ncoxnDQ1oum`_{qs#SebUJg=h!X)G`dQ&{<`?^3qb z9Hsm2()Xjzt8{9OyY%qNeM!pYNI*5sZRVyhD3n|VlvYv0^oc-C8ZTQCqtBpmkB-_N z@T(I}$;suQ_R;G!RysNE>{&!SGt7RK=`ZuHvRBzbGm9zRu`^)yJ2s zD}xU2_1bZ?@^C1vEL8N;_XTsk8nt zX@VS7B4VPVzB~=@Fo)G9z^h@4NK#MML)DC7ZU=}WX;I7&19(7!XggmMzFxJ6AhCof zN2^liY4U1OQUA7=oy~?gS*rG3P()dhKrjnnDl@y{weo|K(?3Vji%xB(?B5I;lFbdv zGAK|q(~@mjjerlD?DjIDVsLq)5?)|t005r*pIm^iHeT0;{A-zI1RYiWueiu_fMMJ4 z_-;0okx+_9(Js#c4ME>or%%3Il>~HmlcAY{OU-^;q>Z0~Ho0|##XJ-44dxaGLchNw zgfSkEK{&A?Zdz)*c>~SW6^xMqnV3uuwIf{ZzdnSu_K*B3SpqP2HZKmiu*)&`)gze3 z+Bp9FB`V#!MH)K)qe=LBY*~{y~s-ljwc(uKeXOxLid`sIlux-ng$?4YZ%k`7if zslMfb4;tJ;&2^Bn4mLg!Iu-}5L6f@yY#2Ipz!}IFW?B_vgC4Y z^C~HqzLTA)%c=rw#L^uO{DyKt)X$4%OF>hV( z2{BhV_Pfu!(NPIZwKhcAti$_^Ys}ZfOXZ1mg%UQIVfym=qs&Qw;8=7@>D14}r=W3Z zFx$>2QC>^munA3UB}PTkLzYbt9#w3t(r?guqr)TOTRSM3Q!6>6?DWtYYki4}9P^LU z20}{R)lTEv5#M;UIH1{Rl!h!#W+j}1Ex z@PdJ$1I4kQJGQiFbsWA=TlENJF?4nYpeVlNd{-x01Om-F2y32Uln;bCE0^lTqP69&>)R=Em{wxNc8J_XV}R4H1JPOM!2>jiI@6b z5)gS%YZymKao8-gmN#nVY9QKodLFrfv)r61CnmQ_-Y~8ZNpOAW zqZNMPLHE-lMUg?bRHNq#4Wp(?PrcuZI^Va?GYQ`Ulj&L%Q+S=J)An zF&e#1fi51Xzs4RS`$=M6^;!1%@tFzJs8hr_dtm`EyZ}CdOW|PNgf}c12T8?WMu<~9 zP}G0|y&hn*GD9Y^eXw&KKo9B0I*@pAfFO}R3}OW56!LSzY*+Y2_2zj!dEVK@g@3!G z;yvE1)c=@@^FKAD1#i8<_4-s&cF)Hi0sydBHtO2$+KLJSW=;-lCgx727Hkj)XK3OP z03aj^aW*ltvv8+0wXm{r6sA9K>!PQ$F&C!S`k=_6=qzbrZ6o93YN6(%q;BS8XU1<% zFDil}1QCD&I9RxwP(mE+9o+;V!u0>b6@cFVQ_W6K`L8bScEa?s4klI>0s>MH8%uR3 zCwodx4mLgxdMJpixut+A=+nO;pe12?Yj<~N0d{sTFE2JPZZ;=ZD|SwPetvciE_N<1 zR%j1aH*ZIG69}uL8#NRGh4Nn*APYA$R~u({8z)D~e=toFH4@{|)W@-=H1c z{_O#%FW8|C?3`@e>`*L9N+D=TK-|OK+R0T!&BVji#MGKn)x<{VpI-mF9y%iXKSTVt zA^-o)fV%8IPxybB0Woogn!)jJGoalBBwZ~`+?`zi@t26CiKC~95c~gK{r@!Oe_020 nD%8E~{|OSb@SiYSI6`CV3Jqd21>Dm=Z-A_n5~xbTIOM+oPo8q4 From 0114e599616382e0ba770fede9c332984c5f8f06 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Wed, 24 Feb 2021 17:16:57 +0530 Subject: [PATCH 012/117] Added peak element --- data_structures/array/peak_element.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/data_structures/array/peak_element.py b/data_structures/array/peak_element.py index e69de29b..29d8b1f2 100644 --- a/data_structures/array/peak_element.py +++ b/data_structures/array/peak_element.py @@ -0,0 +1,22 @@ +# A peak element is an element such that both of its neighbours are smaller than it +# In case of corner elements, consider only one neighbour + + +def peak(arr, low, high): + n = len(arr) + + while low <= high: + mid = low + (high - low) / 2 + mid = int(mid) + + if (mid == 0 or arr[mid-1] <= arr[mid]) and (mid == n-1 or arr[mid+1] <= arr[mid]): + return(arr[mid]) + + elif mid > 0 and arr[mid-1] > arr[mid]: + high = mid - 1 + + else: + low = mid + 1 + +arr = [1, 3, 20, 4, 1, 0] +print(peak(arr, 0, len(arr) - 1)) \ No newline at end of file From a6375c3e60a46aaa2e710f819196f2df7b88d521 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 25 Feb 2021 08:17:26 +0530 Subject: [PATCH 013/117] Modified duplicates --- data_structures/array/duplicate.py | 16 +++++++++++++--- data_structures/array/duplicates.py | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/data_structures/array/duplicate.py b/data_structures/array/duplicate.py index ad4a3fa5..1e4f6291 100644 --- a/data_structures/array/duplicate.py +++ b/data_structures/array/duplicate.py @@ -1,6 +1,12 @@ # Find duplicate in an array of integers given that the integers are in random order and # not necessarily each integer i is 0 <= i <= N where N = length of array +# Solution - use tortoise and hare algorithm. The tortoise pointer moves slower while the hare pointer +# moves faster + +# Note: this array will always contain a duplicate number due to the pigeonhole principle. You are trying to fit +# N different numbers in an array of size N - 1 so one number will be repeated + def duplicate(arr): tortoise = arr[0] hare = arr[0] @@ -11,10 +17,14 @@ def duplicate(arr): if tortoise == hare: break - ptr1 = nums[0] + ptr1 = arr[0] ptr2 = tortoise while ptr1 != ptr2: - ptr1 = nums[ptr1] - ptr2 = nums[ptr2] + ptr1 = arr[ptr1] + ptr2 = arr[ptr2] return ptr1 + + +arr = [3,5,1,2,4,5] +print(duplicate(arr)) \ No newline at end of file diff --git a/data_structures/array/duplicates.py b/data_structures/array/duplicates.py index 04940c0e..a146efba 100644 --- a/data_structures/array/duplicates.py +++ b/data_structures/array/duplicates.py @@ -4,5 +4,5 @@ def duplicate(arr): if arr[abs(x) - 1] < 0: res.append(abs(x)) else: - nums[abs(x) - 1] *= -1 + arr[abs(x) - 1] *= -1 return res From 53a1e37f81fde93665d1d8aa861053343e810d80 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 25 Feb 2021 08:17:48 +0530 Subject: [PATCH 014/117] Add rotation --- data_structures/array/rotation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data_structures/array/rotation.py b/data_structures/array/rotation.py index 36b9db0b..c89ab715 100644 --- a/data_structures/array/rotation.py +++ b/data_structures/array/rotation.py @@ -14,14 +14,14 @@ def rotate(arr, d): while 1: k = j + d if k >= n: - k = k -n + k -= n if k == i: break arr[j] = arr[k] j = k arr[j] = temp - + arr = [1,2,3,4,5] -rotate(arr, 1) +rotate(arr, 3) print(arr) From 1911e7069e8425fa8d4cbdf5c0044f7b8bb48fec Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Fri, 26 Feb 2021 17:40:37 +0530 Subject: [PATCH 015/117] Add program description --- data_structures/array/majority_element.py | 9 +++++++++ data_structures/array/moves_zeros_to_end.py | 3 +++ 2 files changed, 12 insertions(+) diff --git a/data_structures/array/majority_element.py b/data_structures/array/majority_element.py index 511902dd..457686ad 100644 --- a/data_structures/array/majority_element.py +++ b/data_structures/array/majority_element.py @@ -1,3 +1,7 @@ +# The only prerequisite condition of this algorithm is that the array +# definitely contains a majority element, otherwise it will just return +# the last element + def majority(arr): maj_index = 0 count = 1 @@ -11,3 +15,8 @@ def majority(arr): count = 1 return arr[maj_index] + + +arr = [3, 3, 1,5,6,8,3,0,7] + +print(majority(arr)) \ No newline at end of file diff --git a/data_structures/array/moves_zeros_to_end.py b/data_structures/array/moves_zeros_to_end.py index 036c5cd8..c5589961 100644 --- a/data_structures/array/moves_zeros_to_end.py +++ b/data_structures/array/moves_zeros_to_end.py @@ -1,3 +1,6 @@ +# Move all zeros in an array to the end + + def move(arr): count = 0 for a in arr: From b3a66193cddf0bcb0e30493e505ec291647d49c8 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Sun, 28 Feb 2021 12:31:13 +0530 Subject: [PATCH 016/117] New array questions --- data_structures/array/equilibrium_index.py | 23 +++++++++++ data_structures/array/even_more_than_odd.py | 16 ++++++++ data_structures/array/min_swaps.py | 39 +++++++++++++++++++ .../array/rearrange_positive_negative.py | 23 +++++++++++ 4 files changed, 101 insertions(+) create mode 100644 data_structures/array/equilibrium_index.py create mode 100644 data_structures/array/even_more_than_odd.py create mode 100644 data_structures/array/min_swaps.py create mode 100644 data_structures/array/rearrange_positive_negative.py diff --git a/data_structures/array/equilibrium_index.py b/data_structures/array/equilibrium_index.py new file mode 100644 index 00000000..2b1d0601 --- /dev/null +++ b/data_structures/array/equilibrium_index.py @@ -0,0 +1,23 @@ +# Find the equilibrium index of an array. An equilibrium index is such that +# the sum of elements to the left of it is equal to sum of elements to the right +# of it + +def find_equi(arr): + total_sum = sum(arr) + + left_sum = 0 + + for i, num in enumerate(arr): + + total_sum -= num + + if left_sum == total_sum: + return i + + left_sum += num + + return -1 + + +arr = [-7, 1, 5, 2, -4, 3, 0] +print(find_equi(arr)) diff --git a/data_structures/array/even_more_than_odd.py b/data_structures/array/even_more_than_odd.py new file mode 100644 index 00000000..62735d9c --- /dev/null +++ b/data_structures/array/even_more_than_odd.py @@ -0,0 +1,16 @@ +# Rearrange an array such that numbers at even indexes are greater than numbers +# at odd indexes + +def rearrange(arr): + for i in range(1, len(arr)): + if i % 2 == 0: + if arr[i] > arr[i-1]: + arr[i-1], arr[i] = arr[i], arr[i-1] + else: + if arr[i] < arr[i-1]: + arr[i-1], arr[i] = arr[i], arr[i-1] + print(arr) + + +arr = [ 1, 3, 2, 2, 5 ] +rearrange(arr) \ No newline at end of file diff --git a/data_structures/array/min_swaps.py b/data_structures/array/min_swaps.py new file mode 100644 index 00000000..3fdd13a7 --- /dev/null +++ b/data_structures/array/min_swaps.py @@ -0,0 +1,39 @@ +# Minimum swaps required to bring all elements less than or equal to k together + +def min_swaps(arr, k): + # First find out how many elements are there which are less than or + # equal to k + count = 0 + for i in arr: + if i <= k: + count += 1 + + # This count defines a window - inside this window all our elements should + # be placed + # Find the count of bad elements - elements which are more than k and that will be + # our starting answer as we will have to swap them out + bad = 0 + for i in range(0, count): + if arr[i] > k: + bad += 1 + + ans = bad + j = count + + for i in range(0, len(arr)): + if j == len(arr): + break + + if arr[i] > k: + bad -= 1 # because we have moved the bad element out of the window + + if arr[j] > k: + bad += 1 + + ans = min(bad, ans) + j += 1 + + print('answer - ', ans) + +arr = [2,7,9,5,8,7,4] +min_swaps(arr, 5) \ No newline at end of file diff --git a/data_structures/array/rearrange_positive_negative.py b/data_structures/array/rearrange_positive_negative.py new file mode 100644 index 00000000..ab1b971c --- /dev/null +++ b/data_structures/array/rearrange_positive_negative.py @@ -0,0 +1,23 @@ +# Rearragne positive and negative numbers in an array such that they appear +# alternately. If there are more numbers of any one kind, put them at the end + +def rearrange(arr): + i = -1 + + for j in range(len(arr)): + if arr[j] < 0: + i += 1 # maintaining index of the last negative number + arr[j], arr[i] = arr[i], arr[j] + + pos = i + 1 # index of first positive number + neg = 0 # index of first negative number + + while pos < len(arr) and neg < pos and arr[neg] < 0: + arr[pos], arr[neg] = arr[neg], arr[pos] + pos += 1 + neg += 2 + + print(arr) + +arr = [-1, 2, -3, 4, 5, 6, -7, 8, 9] +rearrange(arr) \ No newline at end of file From 90c562fd475bddbd1b2a3d0a051c0361c6cffd45 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Mon, 1 Mar 2021 18:55:00 +0530 Subject: [PATCH 017/117] Add bipartite graph --- data_structures/graphs/bipartite_graph.py | 56 +++++++++++++++++++---- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/data_structures/graphs/bipartite_graph.py b/data_structures/graphs/bipartite_graph.py index 0c851248..a1ebb149 100644 --- a/data_structures/graphs/bipartite_graph.py +++ b/data_structures/graphs/bipartite_graph.py @@ -7,6 +7,12 @@ # Reference - https://www.geeksforgeeks.org/bipartite-graph/ +# A bipartite graph is a graph whose vertices can be divided into two disjoint sets U and V +# such that every edge connects a vertex in U to one in V. Also, a bipartite graph does not +# contain any odd length cycles + +# Also, a graph with no edge is a bipartite graph - https://math.stackexchange.com/a/53947 + from collections import defaultdict class Graph: @@ -16,31 +22,61 @@ def __init__(self, vertices): self.graph = defaultdict(list) + def add_edge(self, u, v): + self.graph[u].append(v) + self.graph[v].append(u) + + + def bfs(self, s): + visited = [False] * self.V + queue = [] + queue.append(s) + visited[s] = True + + while queue: + s = queue.pop(0) + print(s, end=' ') + + for i in self.graph[s]: + if not visited[i]: + visited[i] = True + queue.append(i) + + def is_bipartite(self, s): - # 0 -> color 0 - # 1 -> color 1 + # 0 -> color 0 (blue) + # 1 -> color 1 (red) # -1 -> no color assigned colors = [-1] * self.V - colors[s] = 1 + colors[s] = 1 # Color soure vertex red queue = [] queue.append(s) while queue: - u = queue.pop() + s = queue.pop(0) - if self.graph[u][v] == 1: # Check for self loop + if s in self.graph[s]: # Check for self loop return False # An edge u to v exists and destination is not # colored - for v in range(self.V): - if self.graph[u][v] == 1 and colors[v] == -1: - colors[v] = 1 - colors[u] - queue.append(v) + for i in self.graph[s]: + if colors[i] == -1: + colors[i] = 1 - colors[s] + queue.append(i) - elif self.graph[u][v] == 1 and colors[v] == colors[u]: + elif colors[i] == colors[s]: return False return True + + +g = Graph(5) +g.add_edge(0, 1) +g.add_edge(1, 2) +g.add_edge(2, 3) +g.add_edge(3, 4) +g.add_edge(4, 0) +print(g.is_bipartite(0)) \ No newline at end of file From 58232f3f95037d080131636cd74ba9b0cce54d21 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 4 Mar 2021 08:40:29 +0530 Subject: [PATCH 018/117] Modified iterative dfs to work for disconnected vertices --- data_structures/graphs/iterative_dfs.py | 51 ++++++++++++++----------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/data_structures/graphs/iterative_dfs.py b/data_structures/graphs/iterative_dfs.py index 65c092dc..d150c9cb 100644 --- a/data_structures/graphs/iterative_dfs.py +++ b/data_structures/graphs/iterative_dfs.py @@ -5,7 +5,8 @@ class Graph: - def __init__(self): + def __init__(self, vertices): + self.vertices = vertices self.graph = defaultdict(list) @@ -13,30 +14,36 @@ def add_edge(self, u, v): self.graph[u].append(v) - def dfs(self, s): - visited = [False] * len(self.graph) + def dfs(self): + visited = [False] * self.vertices - stack = [] + for s in range(self.vertices): + if not visited[s]: + visited[s] = True + + stack = [] + stack.append(s) - stack.append(s) - visited[s] = True + while stack: + s = stack.pop() + print(s, end=' ') - while stack: - s = stack.pop() - print(s, end=' ') + for i in self.graph[s]: + if visited[i] == False: + stack.append(i) + visited[i] = True - for i in self.graph[s]: - if visited[i] == False: - stack.append(i) - visited[i] = True +# g = Graph() +# g.add_edge(0, 1) +# g.add_edge(0, 2) +# g.add_edge(1, 2) +# g.add_edge(2, 0) +# g.add_edge(2, 3) +# g.add_edge(3, 3) +g = Graph(5) +g.add_edge(0, 1) +g.add_edge(0, 2) +g.add_edge(3, 4) -g = Graph() -g.add_edge(0, 1) -g.add_edge(0, 2) -g.add_edge(1, 2) -g.add_edge(2, 0) -g.add_edge(2, 3) -g.add_edge(3, 3) - -g.dfs(0) +g.dfs() From 4979d39c02179c6deb6125058971bbb00c4ea43c Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 4 Mar 2021 08:40:44 +0530 Subject: [PATCH 019/117] Add count trees --- data_structures/graphs/count_trees.py | 46 +++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 data_structures/graphs/count_trees.py diff --git a/data_structures/graphs/count_trees.py b/data_structures/graphs/count_trees.py new file mode 100644 index 00000000..84841062 --- /dev/null +++ b/data_structures/graphs/count_trees.py @@ -0,0 +1,46 @@ +# Count the number of tress in a graph (forest) + +from collections import defaultdict + +class Graph: + + def __init__(self, vertices): + self.vertices = vertices + self.graph = defaultdict(list) + + + def add_edge(self, u, v): + self.graph[v].append(u) + self.graph[u].append(v) + + + def count_trees(self): + visited = [False] * self.vertices + count = 0 + + for s in range(self.vertices): + if not visited[s]: + visited[s] = True + stack = [] + stack.append(s) + count += 1 + + while stack: + print(stack) + s = stack.pop() + + for i in self.graph[s]: + if not visited[i]: + visited[i] = True + stack.append(i) + + return count + + +g = Graph(5) +g.add_edge(0, 1) +g.add_edge(0, 2) +g.add_edge(3, 4) + +print('Count of trees - ', g.count_trees()) + From e5a98ba51a959892dced8a525ad9c5b800f263f8 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 4 Mar 2021 09:12:16 +0530 Subject: [PATCH 020/117] Add levels of nodes --- data_structures/graphs/level_of_nodes.py | 45 ++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 data_structures/graphs/level_of_nodes.py diff --git a/data_structures/graphs/level_of_nodes.py b/data_structures/graphs/level_of_nodes.py new file mode 100644 index 00000000..31d01618 --- /dev/null +++ b/data_structures/graphs/level_of_nodes.py @@ -0,0 +1,45 @@ +from collections import defaultdict + +class Graph: + + def __init__(self, vertices): + self.vertices = vertices + self.graph = defaultdict(list) + + + def add_edge(self, u, v): + self.graph[u].append(v) + self.graph[v].append(u) + + + def print_levels(self, s): + levels = [None] * self.vertices + levels[s] = 0 + queue = [] + queue.append(s) + + while queue: + s = queue.pop(0) + + for i in self.graph[s]: + if not levels[i]: + levels[i] = levels[s] + 1 + queue.append(i) + + levels[0] = 0 + + print('Node \t Level') + for node, level in enumerate(levels): + print(f'{node} \t {level}') + +g = Graph(8) +g.add_edge(0, 1) +g.add_edge(0, 2) +g.add_edge(1, 3) +g.add_edge(1, 4) +g.add_edge(1, 5) +g.add_edge(2, 5) +g.add_edge(2, 6) +g.add_edge(6, 7) +g.print_levels(0) + \ No newline at end of file From 16f7b5439f1c7501a3aa17df7c0b7bae7bdf00b1 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Sat, 6 Mar 2021 09:22:29 +0530 Subject: [PATCH 021/117] Remove check and update statement for source node --- data_structures/graphs/level_of_nodes.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/data_structures/graphs/level_of_nodes.py b/data_structures/graphs/level_of_nodes.py index 31d01618..7190341c 100644 --- a/data_structures/graphs/level_of_nodes.py +++ b/data_structures/graphs/level_of_nodes.py @@ -22,12 +22,10 @@ def print_levels(self, s): s = queue.pop(0) for i in self.graph[s]: - if not levels[i]: + if levels[i] == None: levels[i] = levels[s] + 1 queue.append(i) - - levels[0] = 0 - + print('Node \t Level') for node, level in enumerate(levels): print(f'{node} \t {level}') @@ -42,4 +40,4 @@ def print_levels(self, s): g.add_edge(2, 6) g.add_edge(6, 7) g.print_levels(0) - \ No newline at end of file + From 9eb88425822b6da4d7bd673a124c13fbe6f17523 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 9 Mar 2021 15:20:13 +0530 Subject: [PATCH 022/117] Add print in dfs --- data_structures/graphs/dfs.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/data_structures/graphs/dfs.py b/data_structures/graphs/dfs.py index b6608f2b..6af4a6cc 100644 --- a/data_structures/graphs/dfs.py +++ b/data_structures/graphs/dfs.py @@ -7,7 +7,8 @@ def __init__(self): def add_edge(self, u, v): - self.graph[u].append[v] + self.graph[u].append(v) + self.graph[v].append(u) def dfs_util(self, v, visited): @@ -22,3 +23,13 @@ def dfs_util(self, v, visited): def dfs(self, v): visited = [False] * (len(self.graph)) self.dfs_util(v, visited) + + +g = Graph() +g.add_edge(0, 1) +g.add_edge(0, 2) +g.add_edge(0, 3) +g.add_edge(1, 4) +g.add_edge(2, 5) +g.add_edge(3, 6) +print(g.dfs(0)) From b2ca87d6eb0a3828f0beb49bf1eabbc801b85f35 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 6 Apr 2021 17:01:51 +0530 Subject: [PATCH 023/117] New graph questions --- .../graphs/min_edges_between_two_vertices.py | 52 ++++++++++++ .../graphs/print_all_paths_between_nodes.py | 50 ++++++++++++ data_structures/graphs/same_path.py | 80 +++++++++++++++++++ 3 files changed, 182 insertions(+) create mode 100644 data_structures/graphs/min_edges_between_two_vertices.py create mode 100644 data_structures/graphs/print_all_paths_between_nodes.py create mode 100644 data_structures/graphs/same_path.py diff --git a/data_structures/graphs/min_edges_between_two_vertices.py b/data_structures/graphs/min_edges_between_two_vertices.py new file mode 100644 index 00000000..13456d5f --- /dev/null +++ b/data_structures/graphs/min_edges_between_two_vertices.py @@ -0,0 +1,52 @@ +from collections import defaultdict + + +class Graph: + + def __init__(self, vertices): + self.vertices = vertices + self.graph = defaultdict(list) + + + def add_edge(self, u, v): + self.graph[u].append(v) + self.graph[v].append(u) + + + def find_min_edges(self, u, v): + visited = [False] * self.vertices + distance = [0] * self.vertices + queue = [] + + queue.append(u) + visited[u] = True + + while queue: + s = queue.pop(0) + + for i in self.graph[s]: + if visited[i] == False: + distance[i] = distance[s] + 1 + queue.append(i) + visited[i] = True + + return distance[v] + + +g = Graph(9) + +g.add_edge(0, 1) +g.add_edge(0, 7) +g.add_edge(1, 7) +g.add_edge(1, 2) +g.add_edge(2, 3) +g.add_edge(2, 5) +g.add_edge(2, 8) +g.add_edge(3, 4) +g.add_edge(3, 5) +g.add_edge(4, 5) +g.add_edge(5, 6) +g.add_edge(6, 7) +g.add_edge(7, 8) + +print(g.find_min_edges(0, 5)) diff --git a/data_structures/graphs/print_all_paths_between_nodes.py b/data_structures/graphs/print_all_paths_between_nodes.py new file mode 100644 index 00000000..cbd04abf --- /dev/null +++ b/data_structures/graphs/print_all_paths_between_nodes.py @@ -0,0 +1,50 @@ +from collections import defaultdict + +class Graph: + + + def __init__(self, vertices): + self.graph = defaultdict(list) + self.vertices = vertices + + + def add_edge(self, u, v): + self.graph[u].append(v) + + + def print_path(self, s, d, visited, path): + visited[s] = True + path.append(s) + + if s == d: + print(path) + + else: + for i in self.graph[s]: + if visited[i] == False: + self.print_path(i, d, visited, path) + + path.pop() + visited[s] = False + + + def print_all_paths(self, s, d): + visited = [False] * self.vertices + path = [] + self.print_path(s, d, visited, path) + + +g = Graph(4) + +g.add_edge(0, 3) +g.add_edge(0, 1) +g.add_edge(0, 2) +g.add_edge(1, 3) +g.add_edge(2, 0) +g.add_edge(2, 1) + +s = 2 +d = 3 + +print(f'Paths from {s} to {d} are - ') +g.print_all_paths(s, d) \ No newline at end of file diff --git a/data_structures/graphs/same_path.py b/data_structures/graphs/same_path.py new file mode 100644 index 00000000..127c1b86 --- /dev/null +++ b/data_structures/graphs/same_path.py @@ -0,0 +1,80 @@ +# Check if two nodes are on the same path in a tree. Use DFS and the concept of intime and outtime. +# Intime - time when a node is visited for the first time +# Outtime - time when a node is visited for the second time after all its children have been visited +# For any pair of node if they are on the same path - +# intime[u] < intime[v] and outtime[u] > outtime[v] + +from collections import defaultdict + +class Graph: + + + def __init__(self, vertices): + self.graph = defaultdict(list) + self.vertices = vertices + self.intime = None + self.outtime = None + + + def add_edge(self, u, v): + self.graph[u].append(v) + self.graph[v].append(u) + + + def dfs(self): + visited = [False] * self.vertices + intime = [0] * self.vertices + outtime = [0] * self.vertices + timer = 0 + num_children_visited = [0] * self.vertices + + for s in range(self.vertices): + if not visited[s]: + visited[s] = True + + stack = [] + stack.append(s) + + while stack: + s = stack.pop() + timer += 1 + intime[s] = timer + + print(s) + + for i in self.graph[s]: + if visited[i] == False: + stack.append(i) + visited[i] = True + num_children_visited[s] += 1 + + if num_children_visited[i] == len(self.graph[i]): + timer += 1 + outtime[i] = timer + + print('intime - ', intime) + print('outtime - ', outtime) + print('num_children_visited - ', num_children_visited) + self.intime = intime + self.outtime = outtime + + + def check_same_path(self, u, v): + if (self.intime[u] < self.intime[v] and self.outtime[u] > self.outtime[v]) or \ + (self.intime[v] < self.intime[u] and self.outtime[v] > self.outtime[u]): + return True + return False + + +g = Graph(7) +g.add_edge(0, 1) +g.add_edge(0, 2) +g.add_edge(0, 3) +g.add_edge(1, 4) +g.add_edge(2, 5) +g.add_edge(3, 6) + +g.dfs() + +print('Same path - ', g.check_same_path(0, 6)) +print(g.graph[6]) \ No newline at end of file From 38906557a2f63c7342d240c974b349797fbe4851 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 6 Apr 2021 17:39:11 +0530 Subject: [PATCH 024/117] Add min number of operations --- .../graphs/min_number_of_operations.py | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 data_structures/graphs/min_number_of_operations.py diff --git a/data_structures/graphs/min_number_of_operations.py b/data_structures/graphs/min_number_of_operations.py new file mode 100644 index 00000000..7d783e30 --- /dev/null +++ b/data_structures/graphs/min_number_of_operations.py @@ -0,0 +1,54 @@ +""" +Minimum number of operations required to transform a number x to y +Valid operations - multiplication by 2, subtraction by 1 +Ex - x = 4, y = 7 +1. 4 * 2 = 8 +2. 8 - 1 = 7 +Answer = 2 +""" + + +from collections import defaultdict + + +class Node: + + + def __init__(self, value, level): + self.value = value + self.level = level + + +def min_steps(x, y): + node_x = Node(x, 0) + + visited = [] + queue = [] + queue.append(node_x) + + while queue: + s = queue.pop(0) + + if s.value == y: + return s.level + + visited.append(s.value) + + if s.value * 2 == y or s.value - 1 == y: + return s.level + 1 + + # If not visited already, add its children + + if s.value * 2 not in visited: + new_node = Node(s.value * 2, s.level + 1) + queue.append(new_node) + + if s.value - 1 not in visited: + new_node = Node(s.value - 1, s.level + 1) + queue.append(new_node) + + +x = 2 +y = 5 + +print(min_steps(x, y)) \ No newline at end of file From 9ad3c9aee7d5c8a3db77d17f7802878388b090b4 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Wed, 7 Apr 2021 08:45:01 +0530 Subject: [PATCH 025/117] Refactored comments --- data_structures/graphs/bipartite_graph.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/data_structures/graphs/bipartite_graph.py b/data_structures/graphs/bipartite_graph.py index a1ebb149..0296de6e 100644 --- a/data_structures/graphs/bipartite_graph.py +++ b/data_structures/graphs/bipartite_graph.py @@ -1,17 +1,19 @@ -# Check for bipartite graph +""" +Check for bipartite graph -# Do a BFS and make source of red color and its neighbour blue -# Keep on doing this. -# If self loop, return false -# If current color == neighbour color, false, else true +Do a BFS and make source of red color and its neighbour blue +Keep on doing this. +If self loop, return false +If current color == neighbour color, false, else true -# Reference - https://www.geeksforgeeks.org/bipartite-graph/ +Reference - https://www.geeksforgeeks.org/bipartite-graph/ -# A bipartite graph is a graph whose vertices can be divided into two disjoint sets U and V -# such that every edge connects a vertex in U to one in V. Also, a bipartite graph does not -# contain any odd length cycles +A bipartite graph is a graph whose vertices can be divided into two disjoint sets U and V +such that every edge connects a vertex in U to one in V. Also, a bipartite graph does not +contain any odd length cycles -# Also, a graph with no edge is a bipartite graph - https://math.stackexchange.com/a/53947 +Also, a graph with no edge is a bipartite graph - https://math.stackexchange.com/a/53947 +""" from collections import defaultdict From f0b8d79eca5a1583933431357e43257fbab61bee Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Wed, 7 Apr 2021 08:58:22 +0530 Subject: [PATCH 026/117] max_edges_to_make_bipartite.py --- .../graphs/max_edges_to_make_bipartite.py | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 data_structures/graphs/max_edges_to_make_bipartite.py diff --git a/data_structures/graphs/max_edges_to_make_bipartite.py b/data_structures/graphs/max_edges_to_make_bipartite.py new file mode 100644 index 00000000..9b42fb82 --- /dev/null +++ b/data_structures/graphs/max_edges_to_make_bipartite.py @@ -0,0 +1,60 @@ +""" +Find the max edges that can be added to a tree so that it stays a bipartite graph + +keep count of nodes of each color - say count_color1, count_color2 +These nodes can be connected in count_color1 x count_color2 ways = max no of edges of a bipartite graph +Also, a tree has n-1 edges +So answer = (count_color1 x count_color2) - (n - 1) + +If the answer comes negative, then its impossible to add any edge because of odd cycles +""" + +from collections import defaultdict + +class Graph: + + + def __init__(self, vertices): + self.graph = defaultdict(list) + self.vertices = vertices + + + def add_edge(self, u, v): + self.graph[u].append(v) + self.graph[v].append(u) + + + def bfs(self, s): + count_color0 = 0 + count_color1 = 0 + + colors = [-1] * self.vertices + colors[s] = 1 + count_color1 += 1 + + queue = [] + queue.append(s) + + while queue: + s = queue.pop(0) + + for i in self.graph[s]: + if colors[i] == -1: + colors[i] = 1 - colors[s] + if colors[i] == 0: + count_color0 += 1 + else: + count_color1 += 1 + queue.append(i) + + ans = (count_color0 * count_color1) - (self.vertices - 1) + return ans if ans > 0 else 0 + + +g = Graph(5) +g.add_edge(0, 1) +g.add_edge(0, 2) +g.add_edge(1, 3) +g.add_edge(2, 4) + +print(g.bfs(0)) \ No newline at end of file From f844c64b75834bc67669a3eb069c00b572ffa3f3 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 8 Apr 2021 17:43:56 +0530 Subject: [PATCH 027/117] Add directed cycle cycle check iterative --- .../cycle_in_directed_graph_iterative.py | 53 +++++++++++++++++++ data_structures/graphs/iterative_dfs.py | 6 +-- 2 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 data_structures/graphs/cycle_in_directed_graph_iterative.py diff --git a/data_structures/graphs/cycle_in_directed_graph_iterative.py b/data_structures/graphs/cycle_in_directed_graph_iterative.py new file mode 100644 index 00000000..b20b8002 --- /dev/null +++ b/data_structures/graphs/cycle_in_directed_graph_iterative.py @@ -0,0 +1,53 @@ +from collections import defaultdict + + +class Graph: + + + def __init__(self, vertices): + self.graph = defaultdict(list) + self.vertices = vertices + + + def add_edge(self, u, v): + self.graph[u].append(v) + + + def dfs(self): + visited = [False] * self.vertices + recursion_stack = [False] * self.vertices + stack = [] + + for s in range(self.vertices): + if visited[s] == False: + visited[s] = True + recursion_stack[s] = True + + stack.append(s) + + while stack: + s = stack.pop() + + recursion_stack[s] = True + + for i in self.graph[s]: + if visited[i] == False: + stack.append(i) + visited[i] = True + recursion_stack[i] = True + elif recursion_stack[i] == True: + return "Contains Cycle" + + recursion_stack[s] = False + + return "No cycle" + + +g = Graph(4) +g.add_edge(0, 1) +g.add_edge(0, 2) +g.add_edge(1, 2) +g.add_edge(2, 0) +g.add_edge(2, 3) +g.add_edge(3, 3) +print(g.dfs()) \ No newline at end of file diff --git a/data_structures/graphs/iterative_dfs.py b/data_structures/graphs/iterative_dfs.py index d150c9cb..c2eb7169 100644 --- a/data_structures/graphs/iterative_dfs.py +++ b/data_structures/graphs/iterative_dfs.py @@ -16,12 +16,12 @@ def add_edge(self, u, v): def dfs(self): visited = [False] * self.vertices + stack = [] for s in range(self.vertices): - if not visited[s]: + if visited[s] == False: visited[s] = True - stack = [] stack.append(s) while stack: @@ -34,7 +34,7 @@ def dfs(self): visited[i] = True -# g = Graph() +# g = Graph(4) # g.add_edge(0, 1) # g.add_edge(0, 2) # g.add_edge(1, 2) From 1702373f324b4a56430725407614a20969c14f33 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 8 Apr 2021 18:13:50 +0530 Subject: [PATCH 028/117] Refactored code and add cycle in directed graph using colors --- .../cycle_in_directed_graph_iterative.py | 12 ++-- .../cycle_in_directed_graph_using_colors.py | 60 +++++++++++++++++++ ..._graph.py => cycle_in_undirected_graph.py} | 0 .../cycle_in_undirected_graph_iterative.py | 0 .../cycle_in_undirected_graph_union_find.py | 6 ++ 5 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 data_structures/graphs/cycle_in_directed_graph_using_colors.py rename data_structures/graphs/{cyclic_in_undirected_graph.py => cycle_in_undirected_graph.py} (100%) create mode 100644 data_structures/graphs/cycle_in_undirected_graph_iterative.py create mode 100644 data_structures/graphs/cycle_in_undirected_graph_union_find.py diff --git a/data_structures/graphs/cycle_in_directed_graph_iterative.py b/data_structures/graphs/cycle_in_directed_graph_iterative.py index b20b8002..cc0b3cd2 100644 --- a/data_structures/graphs/cycle_in_directed_graph_iterative.py +++ b/data_structures/graphs/cycle_in_directed_graph_iterative.py @@ -18,12 +18,12 @@ def dfs(self): recursion_stack = [False] * self.vertices stack = [] - for s in range(self.vertices): - if visited[s] == False: - visited[s] = True - recursion_stack[s] = True + for v in range(self.vertices): + if visited[v] == False: + visited[v] = True + recursion_stack[v] = True - stack.append(s) + stack.append(v) while stack: s = stack.pop() @@ -38,7 +38,7 @@ def dfs(self): elif recursion_stack[i] == True: return "Contains Cycle" - recursion_stack[s] = False + recursion_stack[v] = False return "No cycle" diff --git a/data_structures/graphs/cycle_in_directed_graph_using_colors.py b/data_structures/graphs/cycle_in_directed_graph_using_colors.py new file mode 100644 index 00000000..44dfb5ac --- /dev/null +++ b/data_structures/graphs/cycle_in_directed_graph_using_colors.py @@ -0,0 +1,60 @@ +""" +Using three colors - white, gray and black +White - vertices that are not processed (inital state of all vertices) +Gray - vertices that are in DFS +Black - fully traversed vertices (i.e its progenies are also done) + +If while traversing any adjacent node is colored Gray, that means cycle exists +""" + +from collections import defaultdict + +class Graph: + + def __init__(self, vertices): + self.vertices = vertices + self.graph = defaultdict(list) + + + def add_edge(self, u, v): + self.graph[u].append(v) + + + def detect_cycle(self): + color = ['white'] * self.vertices + visited = [False] * self.vertices + + stack = [] + + for v in range(self.vertices): + if color[v] == 'white': + color[v] = 'gray' + + stack.append(v) + + while stack: + s = stack.pop() + + for i in self.graph[s]: + if color[i] == 'white': + stack.append(i) + color[i] = 'gray' + elif color[i] == 'gray': + return "Cycle detected" + + color[v] = 'black' + + return "Cycle not present" + + +g = Graph(4) +g.add_edge(0, 1) +g.add_edge(0, 2) +g.add_edge(1, 2) +g.add_edge(2, 0) +g.add_edge(2, 3) +g.add_edge(3, 3) +# g.add_edge(0, 1) +# g.add_edge(0, 2) +# g.add_edge(1, 3) +print(g.detect_cycle()) \ No newline at end of file diff --git a/data_structures/graphs/cyclic_in_undirected_graph.py b/data_structures/graphs/cycle_in_undirected_graph.py similarity index 100% rename from data_structures/graphs/cyclic_in_undirected_graph.py rename to data_structures/graphs/cycle_in_undirected_graph.py diff --git a/data_structures/graphs/cycle_in_undirected_graph_iterative.py b/data_structures/graphs/cycle_in_undirected_graph_iterative.py new file mode 100644 index 00000000..e69de29b diff --git a/data_structures/graphs/cycle_in_undirected_graph_union_find.py b/data_structures/graphs/cycle_in_undirected_graph_union_find.py new file mode 100644 index 00000000..b1904365 --- /dev/null +++ b/data_structures/graphs/cycle_in_undirected_graph_union_find.py @@ -0,0 +1,6 @@ +""" +This method cannot be used in Directed graph because of the direction of the edge. +In a union of set containing element A and B, you cannot specify whether A is going to B +or vice versa +""" + From 66b0a6428dfeaab23f6eeff3b28bada68eb3f427 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 8 Apr 2021 18:43:21 +0530 Subject: [PATCH 029/117] cyclic graphs --- .../cycle_in_undirected_graph_iterative.py | 8 +++ .../cycle_in_undirected_graph_union_find.py | 53 +++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/data_structures/graphs/cycle_in_undirected_graph_iterative.py b/data_structures/graphs/cycle_in_undirected_graph_iterative.py index e69de29b..3a1124ec 100644 --- a/data_structures/graphs/cycle_in_undirected_graph_iterative.py +++ b/data_structures/graphs/cycle_in_undirected_graph_iterative.py @@ -0,0 +1,8 @@ +""" +This is almost same as the iterative one for directed graph but we cannot use the +concept of recursion stack here because in directed graphs, there is defined path to +traverse but in undirected its possible that an edge (or a path) can be traversed +infite number of times. + +Instead check for parents. If a parent of a node is visited and +""" \ No newline at end of file diff --git a/data_structures/graphs/cycle_in_undirected_graph_union_find.py b/data_structures/graphs/cycle_in_undirected_graph_union_find.py index b1904365..e8e0af42 100644 --- a/data_structures/graphs/cycle_in_undirected_graph_union_find.py +++ b/data_structures/graphs/cycle_in_undirected_graph_union_find.py @@ -4,3 +4,56 @@ or vice versa """ +from collections import defaultdict + + +class Graph: + + + def __init__(self, vertices): + self.vertices = vertices + self.graph = defaultdict(list) + + + def add_edge(self, u, v): + self.graph[u].append(v) + + + def union(self, parent, x, y): + parent[x] = y + + + def find_parent(self, parent, i): + if parent[i] == -1: + return i + else: + return self.find_parent(parent, parent[i]) + + + def check_cyclic(self): + parent = [-1] * self.vertices + + for i in self.graph.keys(): + for j in self.graph[i]: + x = self.find_parent(parent, i) + y = self.find_parent(parent, j) + + if x == y: + return "Cyclic" + + self.union(parent, x, y) + + return "Not cyclic" + + +g = Graph(4) +g.add_edge(0, 1) +g.add_edge(0, 2) +g.add_edge(1, 2) +g.add_edge(2, 0) +g.add_edge(2, 3) +g.add_edge(3, 3) +# g.add_edge(0, 1) +# g.add_edge(0, 2) +# g.add_edge(1, 3) +print(g.check_cyclic()) \ No newline at end of file From bb279df2d55dcddbfad04d10edbf96c275e5674d Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Fri, 9 Apr 2021 17:31:59 +0530 Subject: [PATCH 030/117] Add bellman ford --- data_structures/graphs/bellman_ford.py | 101 +++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 data_structures/graphs/bellman_ford.py diff --git a/data_structures/graphs/bellman_ford.py b/data_structures/graphs/bellman_ford.py new file mode 100644 index 00000000..35c6522d --- /dev/null +++ b/data_structures/graphs/bellman_ford.py @@ -0,0 +1,101 @@ +""" +Bellman Ford algorithm is used to find the single source shortest path in a weighted directed graph. +Example - find the shortest path from node A to F + +Advantages of this algorithm over Djikstra's - +1. Bellman Ford also works for negative weight edges +2. Also finds negative weight cycles in a graph + +Disvantage - +1. Slower than Dijsktra's + +** Idea (https://www.youtube.com/watch?v=-mOEd_3gTK0&t) + +0. Shortest path can be found as - +if distance[v] > distance[u] + weight(u, v), then the right side is the shorter path +1. Mark parent and the new weight each time this is run +2. Do this V-1 times. In the first iteration, the algorithm will find the shortest path between the source +and source's neighbours +3. In the second, between source and source's neighbours' neighbours and so on (V - 1) times +4. Do this again. If in this Vth iteration, the weights of path decrease, then there is a negative cycle in the +graph + +Time complexity - O(V * E) +Space complexity - O(V) +""" + +from collections import defaultdict + +class Edge: + + def __init__(self, source, dest, weight): + self.source = source + self.dest = dest + self.weight = weight + + +class Graph: + + + def __init__(self, vertices): + self.graph = defaultdict(list) + self.vertices = vertices + self.edges = {} + + + def add_edge(self, u, v, weight): + self.graph[u].append(v) + self.edges[(u, v)] = Edge(u, v, weight) + + + def bellman_ford(self, source, destination): + parent = [-1] * self.vertices + distance = [float("inf")] * self.vertices + + parent[source] = source + distance[source] = source + + for (u, v) in self.edges: + wt = self.edges[(u, v)].weight + + if distance[v] > distance[u] + wt: + distance[v] = distance[u] + wt + parent[v] = u + + # Now for the Vth iteration, check for the negative cycle + + negative_cycle_present = False + + for (u, v) in self.edges: + wt = self.edges[(u, v)].weight + if distance[v] > distance[u] + wt: + print('h - ', u, v) + negative_cycle_present = True + break + + if negative_cycle_present: + print('Contains negative cycle') + else: + print('No negative cycle') + + # Printing the shortest path from source to destination + + while True: + print(f'{destination}', end=' ') + destination = parent[destination] + + if destination == source: + break + + +g = Graph(5) + +g.add_edge(0, 1, 4) +g.add_edge(0, 2, 5) +g.add_edge(0, 3, 8) +g.add_edge(1, 2, -3) +g.add_edge(2, 4, 4) +g.add_edge(3, 4, 2) +g.add_edge(4, 3, 1) + +g.bellman_ford(0, 3) \ No newline at end of file From f98921fab34afdd36383ffadf4e015b577659b0b Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Mon, 12 Apr 2021 10:22:45 +0530 Subject: [PATCH 031/117] Add max edges to add to DAG --- .../max_edges_that_can_be_added_to_dag.py | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 data_structures/graphs/max_edges_that_can_be_added_to_dag.py diff --git a/data_structures/graphs/max_edges_that_can_be_added_to_dag.py b/data_structures/graphs/max_edges_that_can_be_added_to_dag.py new file mode 100644 index 00000000..acad5380 --- /dev/null +++ b/data_structures/graphs/max_edges_that_can_be_added_to_dag.py @@ -0,0 +1,81 @@ +""" +Find the max number of edges that can be added to a Directed Acyclic Graph such that +it remains a DAG. + +Solution - +The trick is to add all edges from left to right. Now adding any edge from right to left +will make the graph cyclic because that edge's counterpart will exist from left-to-right. +Hence a cycle will be formed. + +1. Topologically sort all the edges +2. If edge is not there left to right, create the edge +3. Count the number of edges added + +""" + +from collections import defaultdict + +class Graph: + + + def __init__(self, vertices): + self.graph = defaultdict(list) + self.vertices = vertices + + + def add_edge(self, u, v): + self.graph[u].append(v) + + + def topological_sort_util(self, v, visited, stack): + visited[v] = True + + for i in self.graph[v]: + if visited[i] == False: + self.topological_sort_util(i, visited, stack) + + stack.insert(0, v) + + + def topological_sort(self): + visited = [False] * self.vertices + stack = [] + + for i in range(self.vertices): + if visited[i] == False: + self.topological_sort_util(i, visited, stack) + + return stack + + + def max_edges(self): + topo = self.topological_sort() + visited = [False] * self.vertices + count = 0 + + for i in topo: + vertex = topo[i] + # Mark the connected vertices visited + for j in self.graph[vertex]: + visited[j] = True + + # Print the unmarked nodes from topo + for j in range(i+1, len(topo)): + if visited[topo[j]] == False: + print(f"{vertex} -> {topo[j]}") + count += 1 + + visited[topo[j]] = False + + print('Maximum edges that can be added - ', count) + + +g = Graph(6) +g.add_edge(5, 2) +g.add_edge(5, 0) +g.add_edge(4, 0) +g.add_edge(4, 1) +g.add_edge(2, 3) +g.add_edge(3, 1) + +g.max_edges() \ No newline at end of file From 600c8457bfedd2ef46c774342a7c30d6d5806dee Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Mon, 12 Apr 2021 12:52:39 +0530 Subject: [PATCH 032/117] Add DAG shortest and longest path --- data_structures/graphs/dag_longest_path.py | 71 ++++++++++++++++++ data_structures/graphs/dag_shortest_path.py | 79 +++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 data_structures/graphs/dag_longest_path.py create mode 100644 data_structures/graphs/dag_shortest_path.py diff --git a/data_structures/graphs/dag_longest_path.py b/data_structures/graphs/dag_longest_path.py new file mode 100644 index 00000000..796c43da --- /dev/null +++ b/data_structures/graphs/dag_longest_path.py @@ -0,0 +1,71 @@ +""" +The idea is similar to DAG Shortest Path. Only the comparision part changes +""" + +from collections import defaultdict + +class Graph: + + + def __init__(self, vertices): + self.graph = defaultdict(list) + self.vertices = vertices + + + def add_edge(self, u, v, w): + self.graph[u].append((v, w)) + + + def topological_sort_util(self, vertex, visited, stack): + visited[vertex] = True + + for v, weight in self.graph[vertex]: + if visited[v] == False: + self.topological_sort_util(v, visited, stack) + + stack.insert(0, vertex) + + + def topological_sort(self): + visited = [False] * self.vertices + stack = [] + + for v in range(self.vertices): + if visited[v] == False: + self.topological_sort_util(v, visited, stack) + + return stack + + + def longest_path(self, s): + stack = self.topological_sort() + + distance = [-float("inf")] * self.vertices + distance[s] = 0 + + while stack: + i = stack.pop(0) + + for vertex, weight in self.graph[i]: + if distance[vertex] < distance[i] + weight: + distance[vertex] = distance[i] + weight + + for i in range(self.vertices): + print(f"{s} -> {i} = {distance[i]}") + + +g = Graph(6) +g.add_edge(0, 1, 5) +g.add_edge(0, 2, 3) +g.add_edge(1, 3, 6) +g.add_edge(1, 2, 2) +g.add_edge(2, 4, 4) +g.add_edge(2, 5, 2) +g.add_edge(2, 3, 7) +g.add_edge(3, 5, 1) +g.add_edge(3, 4, -1) +g.add_edge(4, 5, -2) + +source = 0 + +g.longest_path(source) \ No newline at end of file diff --git a/data_structures/graphs/dag_shortest_path.py b/data_structures/graphs/dag_shortest_path.py new file mode 100644 index 00000000..36bebda4 --- /dev/null +++ b/data_structures/graphs/dag_shortest_path.py @@ -0,0 +1,79 @@ +""" +Find shortest path from source vertex to all vertices + +In general, Bellaman-Ford and Dijkstra can be used to find shortest path. But for +DAG, we can improvise + +Bellman-Ford: O(V*E), Dijsktra: O(E+VlogV) + +For DAG, using Topological sort will solve this problem in O(V+E). We are using Topo +sort because it lays out a graph in its linear representation according to the paths. +Hence we can proceed in this order to find shortest path +""" + +from collections import defaultdict + +class Graph: + + + def __init__(self, vertices): + self.graph = defaultdict(list) + self.vertices = vertices + + + def add_edge(self, u, v, w): + self.graph[u].append((v, w)) + + + def topological_sort_util(self, vertex, visited, stack): + visited[vertex] = True + + for v, weight in self.graph[vertex]: + if visited[v] == False: + self.topological_sort_util(v, visited, stack) + + stack.insert(0, vertex) + + + def topological_sort(self): + visited = [False] * self.vertices + stack = [] + + for v in range(self.vertices): + if visited[v] == False: + self.topological_sort_util(v, visited, stack) + + return stack + + + def shortest_path(self, s): + stack = self.topological_sort() + + distance = [float("inf")] * self.vertices + distance[s] = 0 + + while stack: + i = stack.pop(0) + + for vertex, weight in self.graph[i]: + if distance[vertex] > distance[i] + weight: + distance[vertex] = distance[i] + weight + + for i in range(self.vertices): + print(f"{s} -> {i} = {distance[i]}") + + +g = Graph(6) +g.add_edge(0, 1, 5) +g.add_edge(0, 2, 3) +g.add_edge(1, 3, 6) +g.add_edge(1, 2, 2) +g.add_edge(2, 4, 4) +g.add_edge(2, 5, 2) +g.add_edge(2, 3, 7) +g.add_edge(3, 4, -1) +g.add_edge(4, 5, -2) + +source = 0 + +g.shortest_path(source) \ No newline at end of file From 0635e65a1112563552a6136e5afbd6e1840fb7e4 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 13 Apr 2021 17:57:31 +0530 Subject: [PATCH 033/117] Fix BFS --- data_structures/graphs/bfs.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/data_structures/graphs/bfs.py b/data_structures/graphs/bfs.py index 82bda7d3..67722def 100644 --- a/data_structures/graphs/bfs.py +++ b/data_structures/graphs/bfs.py @@ -2,8 +2,9 @@ class Graph: - def __init__(self): + def __init__(self, vertices): self.graph = defaultdict(list) + self.vertices = vertices def add_edge(self, u, v): @@ -11,29 +12,31 @@ def add_edge(self, u, v): def bfs(self, s): - visited = [False] * len(self.graph) + visited = [False] * self.vertices queue = [] queue.append(s) visited[s] = True + bfs = [] + while queue: s = queue.pop(0) print(s, end=' ') - for i in self.graph[s]: if visited[i] == False: queue.append(i) visited[i] = True +g = Graph(6) -g = Graph() -g.add_edge(0, 1) -g.add_edge(0, 2) -g.add_edge(1, 2) -g.add_edge(2, 0) -g.add_edge(2, 3) -g.add_edge(3, 3) +g.add_edge(0, 1) +g.add_edge(0, 2) +g.add_edge(1, 3) +g.add_edge(0, 3) +g.add_edge(2, 4) +g.add_edge(3, 4) +g.add_edge(3, 5) g.bfs(0) From 3e6f90d07a97468f8bc63980ee505fb621b4ede6 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 13 Apr 2021 18:15:45 +0530 Subject: [PATCH 034/117] Add description --- data_structures/graphs/level_of_nodes.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/data_structures/graphs/level_of_nodes.py b/data_structures/graphs/level_of_nodes.py index 7190341c..c453fa95 100644 --- a/data_structures/graphs/level_of_nodes.py +++ b/data_structures/graphs/level_of_nodes.py @@ -1,3 +1,10 @@ +""" +Level is the distance of a node from a source node. This concept can be used to find +the distance between 2 nodes in an unweighted graph as well. A simple BFS traversal +between these 2 nodes will give the level and level will always be the shortest distance +between nodes. +""" + from collections import defaultdict class Graph: @@ -39,5 +46,4 @@ def print_levels(self, s): g.add_edge(2, 5) g.add_edge(2, 6) g.add_edge(6, 7) -g.print_levels(0) - +g.print_levels(0) \ No newline at end of file From d6051d2d6457643d580b8423892e2f70ac0b23fc Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 13 Apr 2021 18:16:03 +0530 Subject: [PATCH 035/117] Add shortest path --- .../graphs/shortest_path_unweighted_graph.py | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 data_structures/graphs/shortest_path_unweighted_graph.py diff --git a/data_structures/graphs/shortest_path_unweighted_graph.py b/data_structures/graphs/shortest_path_unweighted_graph.py new file mode 100644 index 00000000..ca5cd60b --- /dev/null +++ b/data_structures/graphs/shortest_path_unweighted_graph.py @@ -0,0 +1,68 @@ +""" +Find the shortest path between two nodes in an unweighted undirected graph. Remember this +is about finding the shortest path, not the shortest distance. For shortest +distance you can simply calculate the level of nodes from the source vertex +and that will give the answer. For shortest path, use the concept of parents of +Bellman-Ford algorithm. + +Simply do a BFS and keep track of parents of each node. Then recursively print the +parents of destination node until the source node. +""" + +from collections import defaultdict + +class Graph: + + def __init__(self, vertices): + self.vertices = vertices + self.graph = defaultdict(list) + + + def add_edge(self, u, v): + self.graph[u].append(v) + self.graph[v].append(u) + + + def bfs(self, s): + parent = [-1] * self.vertices + visited = [False] * self.vertices + visited[s] = True + queue = [] + queue.append(s) + + while queue: + s = queue.pop(0) + + for i in self.graph[s]: + if visited[i] == False: + queue.append(i) + parent[i] = s + visited[i] = True + + return parent + + + def shortest_path(self, source, dest): + parent = self.bfs(source) + + while True: + print(dest, end=' ') + dest = parent[dest] + + if dest == source: + break + + +g = Graph(8) +g.add_edge(0, 1) +g.add_edge(0, 3) +g.add_edge(1, 2) +g.add_edge(3, 4) +g.add_edge(3, 7) +g.add_edge(4, 5) +g.add_edge(4, 6) +g.add_edge(4, 7) +g.add_edge(5, 6) +g.add_edge(6, 7) + +g.shortest_path(0, 7) \ No newline at end of file From a0e470a7acbf9d8e1de961498ecbb226a050e3f9 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Wed, 14 Apr 2021 12:45:45 +0530 Subject: [PATCH 036/117] Add comments --- data_structures/graphs/count_edges.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/data_structures/graphs/count_edges.py b/data_structures/graphs/count_edges.py index f1601542..6b9d1f87 100644 --- a/data_structures/graphs/count_edges.py +++ b/data_structures/graphs/count_edges.py @@ -1,6 +1,12 @@ -# Use handshaking lemma -# deg(v) = 2|E| -# Time - O(V) +""" +Count the number of edges in an undirected graph + +Use Handshaking Lemma (Note: Handshaking Lemma is only for undirected graph) + +For all v in V, deg(v) = 2|E| +""" + +Time - O(V) class Graph: @@ -39,4 +45,4 @@ def count_edges(self): g.add_edge(6, 8 ) g.add_edge(7, 8 ) -print(g.count_edges()) +print(g.count_edges()) \ No newline at end of file From 2f87807aef8a8a65b7777db7d3c7c7c885566d9d Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Wed, 14 Apr 2021 12:50:29 +0530 Subject: [PATCH 037/117] Add comments --- data_structures/graphs/count_trees.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/data_structures/graphs/count_trees.py b/data_structures/graphs/count_trees.py index 84841062..28033ee9 100644 --- a/data_structures/graphs/count_trees.py +++ b/data_structures/graphs/count_trees.py @@ -1,4 +1,10 @@ -# Count the number of tress in a graph (forest) +""" +Count the number of trees in a forest + +A forest is a collection of trees. Do a DFS. If any vertex if not reachable +from any other, then it means it is not a part of that subgraph (tree). Hence +it is a different tree, so increment the count +""" from collections import defaultdict @@ -42,5 +48,4 @@ def count_trees(self): g.add_edge(0, 2) g.add_edge(3, 4) -print('Count of trees - ', g.count_trees()) - +print('Count of trees - ', g.count_trees()) \ No newline at end of file From 42a6240bfaad46e6b23319d3338be9a588f7d54e Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Wed, 14 Apr 2021 18:14:37 +0530 Subject: [PATCH 038/117] Add question --- .../cycle_in_undirected_graph_iterative.py | 56 ++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/data_structures/graphs/cycle_in_undirected_graph_iterative.py b/data_structures/graphs/cycle_in_undirected_graph_iterative.py index 3a1124ec..53bca95d 100644 --- a/data_structures/graphs/cycle_in_undirected_graph_iterative.py +++ b/data_structures/graphs/cycle_in_undirected_graph_iterative.py @@ -4,5 +4,57 @@ traverse but in undirected its possible that an edge (or a path) can be traversed infite number of times. -Instead check for parents. If a parent of a node is visited and -""" \ No newline at end of file +Instead check for parents (which means vertex fro which you reached the current vertex). +If a vertex is visited and you are not coming to this vertex from the current "source" vertex +(source - vertex from which DFS has started), then it means that in the same DFS chain, there is +another path to reach this vertex - hence a cycle +""" + +from collections import defaultdict + +class Graph: + + + def __init__(self, vertices): + self.graph = defaultdict(list) + self.vertices = vertices + + + def add_edge(self, u, v): + self.graph[u].append(v) + self.graph[v].append(u) + + + def dfs(self): + visited = [False] * self.vertices + parent = [-1] * self.vertices + + stack = [] + + for v in range(self.vertices): + if visited[v] == False: + visited[v] = True + + stack.append(v) + + while stack: + s = stack.pop() + + for i in self.graph[s]: + if visited[i] == False: + parent[i] = s + stack.append(i) + visited[i] = True + elif parent[s] != i: + return "Contains Cycle" + + return "No cycle" + + +g = Graph(5) +g.add_edge(1, 0) +g.add_edge(1, 2) +g.add_edge(2, 0) +g.add_edge(0, 3) +g.add_edge(3, 4) +print(g.dfs()) \ No newline at end of file From 5fe1373e4e20e8b08845b7dbae85b619ab66e04a Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Wed, 14 Apr 2021 18:42:51 +0530 Subject: [PATCH 039/117] Add check if graph is tree --- .../graphs/check_if_graph_is_tree.py | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 data_structures/graphs/check_if_graph_is_tree.py diff --git a/data_structures/graphs/check_if_graph_is_tree.py b/data_structures/graphs/check_if_graph_is_tree.py new file mode 100644 index 00000000..90ed3425 --- /dev/null +++ b/data_structures/graphs/check_if_graph_is_tree.py @@ -0,0 +1,59 @@ +""" +A graph is a tree if - +1. It does not contain cycles +2. The graph is connected + +Do DFS and see if every vertex can be visited from a source vertex and check for cycle +""" + +from collections import defaultdict + +class Graph: + + + def __init__(self, vertices): + self.vertices = vertices + self.graph = defaultdict(list) + + + def add_edge(self, u, v): + self.graph[u].append(v) + self.graph[v].append(u) + + + def is_tree(self, s): + visited = [False] * self.vertices + parent = [-1] * self.vertices + stack = [] + + visited[s] = True + no_of_visited = 1 + stack.append(s) + + while stack: + s = stack.pop() + + for i in self.graph[s]: + if visited[i] == False: + parent[i] = s + visited[i] = True + stack.append(i) + no_of_visited += 1 + elif parent[s] != i: + return "Not a tree" + + if no_of_visited == self.vertices: + return "Graph is Tree" + else: + return "Not a tree" + + +g = Graph(7) +g.add_edge(0, 1) +g.add_edge(0, 2) +g.add_edge(1, 3) +g.add_edge(2, 4) +g.add_edge(4, 5) +g.add_edge(1, 6) + +print(g.is_tree(0)) \ No newline at end of file From 469a9bff4af81789c05a5a5add569062fa171e09 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 15 Apr 2021 11:55:26 +0530 Subject: [PATCH 040/117] Add sink nodes --- data_structures/graphs/count_sink_nodes.py | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 data_structures/graphs/count_sink_nodes.py diff --git a/data_structures/graphs/count_sink_nodes.py b/data_structures/graphs/count_sink_nodes.py new file mode 100644 index 00000000..7cce157f --- /dev/null +++ b/data_structures/graphs/count_sink_nodes.py @@ -0,0 +1,31 @@ +""" +Count the number of sink nodes + +Sink nodes are the nodes without any outgoing edge + +Solution - +vertices contains the total number of edges and graph is an adjacency list. +Simply subtract the vertices and len(graph) +""" + +from collections import defaultdict + +class Graph: + + def __init__(self, vertices): + self.graph = defaultdict(list) + self.vertices = vertices + + + def add_edge(self, u, v): + self.graph[u].append(v) + + + def count_sink_nodes(self): + return self.vertices - len(self.graph) + + +g = Graph(3) +g.add_edge(0, 1) +g.add_edge(0, 2) +print(g.count_sink_nodes()) \ No newline at end of file From 941bb7992d8a2159760008058500b7babf729092 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 15 Apr 2021 11:55:35 +0530 Subject: [PATCH 041/117] Update readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cdbc6a72..ec31aa8c 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # Python Data Structures and Algorithms -This repository contains questions requiring implementation of data structures and algorithms concepts. It is useful for interviews in Python. +No non-sense solutions to common Data Structure and Algorithm interview questions in Python. ## Objective -The open source community has helped me a lot during my interview preparations and studies while I was in my undergrad. I always wanted to give something back to the community. In my endeavour to contribute something back, I will be uploading data structures and algorithms questions in Python in this repo. Feel free to contribute and get in touch! :smiley: +The open source community has helped me a lot during my interview preparations and studies while I was in my undergrad. I always wanted to give something back to the community. In my endeavour to contribute something back, I will be uploading data structures and algorithms questions in Python in this repo. Feel free to contribute and get in touch! ## Structure of the repository @@ -58,8 +58,8 @@ As you can see, the repo is still in its infancy. Here are some key things in th ## Contributing -Contributions are always welcomed. :smiley: -Feel free to raise new issues, file new PRs. Consider giving it a star and fork this repo! :wink: +Contributions are always welcomed. +Feel free to raise new issues, file new PRs. Consider giving it a star and fork this repo! To follow the guidelines, refer to [Contributing.md](CONTRIBUTING.md) From a6c75eaac88b0b53ae74cd24ca600428b4cf5263 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 15 Apr 2021 17:19:04 +0530 Subject: [PATCH 042/117] Two cliques --- data_structures/graphs/two_cliques.py | 75 +++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 data_structures/graphs/two_cliques.py diff --git a/data_structures/graphs/two_cliques.py b/data_structures/graphs/two_cliques.py new file mode 100644 index 00000000..4eefba9c --- /dev/null +++ b/data_structures/graphs/two_cliques.py @@ -0,0 +1,75 @@ +""" +Find if a graph can be divided into two cliques + +A clique is a subgraph such that all vertices in it are completely +connected with each other + +Solution - +A bipartite graph is a graph which can be divided into two sets U and V such +that every edge from u or v has a destination in the other. +So take the complement of the graph (create edge where there is none, and destroy +edges where present) and if the complement is a bipartite, it means that there are edges +in U and V that connect each other. So complement's complement (i.e the original graph) will +not have these edges and hence it can be divided into two clique +""" + +from collections import defaultdict + +class Graph: + + + def __init__(self, vertices): + self.graph = defaultdict(list) + self.cgraph = defaultdict(list) + self.vertices = vertices + + + def add_edge(self, u, v): + self.graph[u].append(v) + self.graph[v].append(u) + + + def is_bipartite(self): + colors = [-1] * self.vertices + queue = [] + + for v in range(self.vertices): + if colors[v] == -1: + colors[v] = 1 + + queue.append(v) + + while queue: + s = queue.pop(0) + + for i in self.cgraph[s]: + if colors[i] == -1: + colors[i] = 1 - colors[s] + queue.append(i) + + elif colors[i] == colors[s]: + return False + + return True + + + def make_complement(self): + for src, dest in self.graph.items(): + for v in range(self.vertices): + if v not in dest and src != v: + self.cgraph[src].append(v) + self.cgraph[v].append(src) + + + def two_cliques(self): + self.make_complement() + return self.is_bipartite() + + +g = Graph(5) +g.add_edge(0, 3) +g.add_edge(3, 4) +g.add_edge(0, 1) +g.add_edge(1, 2) +g.add_edge(2, 0) +print(g.two_cliques()) \ No newline at end of file From 3e7c26038805dfca32a263b7ef2613fd0da72599 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Sun, 18 Apr 2021 14:33:28 +0530 Subject: [PATCH 043/117] Added kosaraju's algorithm --- data_structures/graphs/kosaraju_algorithm.py | 80 +++++++++++++------- 1 file changed, 52 insertions(+), 28 deletions(-) diff --git a/data_structures/graphs/kosaraju_algorithm.py b/data_structures/graphs/kosaraju_algorithm.py index cfc2a2b0..cd446c1a 100644 --- a/data_structures/graphs/kosaraju_algorithm.py +++ b/data_structures/graphs/kosaraju_algorithm.py @@ -1,7 +1,13 @@ -# Reference - https://www.geeksforgeeks.org/strongly-connected-components/ +""" +Reference - https://www.geeksforgeeks.org/strongly-connected-components/ -# This is used to find all the strongly connected components and does DFS -# 2 times. +Algorithm - https://youtu.be/RpgcYiky7uw + +This is used to find all the strongly connected components and does DFS +2 times. + +Note: A single vertex is also strongly connected +""" from collections import defaultdict @@ -9,7 +15,7 @@ class Graph: def __init__(self, vertices): - self.V = vertices + self.vertices = vertices self.graph = defaultdict(list) @@ -17,49 +23,67 @@ def add_edge(self, u, v): self.graph[u].append(v) - def dfs_util(self, v, visited): + def fill_time(self, v, visited, stack): visited[v] = True - print(v, end=' ') for i in self.graph[v]: if visited[i] == False: - self.dfs_util(i, visited) + self.fill_time(i, visited, stack) + stack = stack.append(v) - def fill_order(self, v, visited, stack): + + def dfs(self, v, visited): visited[v] = True + print(v, end=' ') + for i in self.graph[v]: if visited[i] == False: - self.fill_order(i, visited, stack) - stack.append(v) + self.dfs(i, visited) - - def get_transpose(self): - g = Graph(self.V) + + def create_tranpose(self): + tgraph = Graph(self.vertices) for i in self.graph: for j in self.graph[i]: - g.add_edge(j, i) - - return g + tgraph.add_edge(j, i) + return tgraph + def kosaraju(self): + visited = [False] * self.vertices stack = [] - visited = [False] * self.V - for i in range(self.V): - if visited[i] == False: - self.fill_order(i, visited, stack) + for v in range(self.vertices): + if visited[v] == False: + self.fill_time(v, visited, stack) - gr = self.get_transpose() + tgraph = self.create_tranpose() + visited = [False] * self.vertices - visited = [False] * self.V + print('stack - ', stack) while stack: - i = stack.pop() - if visited[i] == False: - gr.dfs_util(i, visited) - print() - - + s = stack.pop() + + if visited[s] == False: + tgraph.dfs(s, visited) + print() + + +g = Graph(11) +g.add_edge(0, 1) +g.add_edge(2, 0) +g.add_edge(1, 2) +g.add_edge(1, 3) +g.add_edge(3, 4) +g.add_edge(4, 5) +g.add_edge(5, 3) +g.add_edge(6, 5) +g.add_edge(6, 7) +g.add_edge(7, 8) +g.add_edge(8, 9) +g.add_edge(9, 10) +g.kosaraju() \ No newline at end of file From 8834704dbdf69e3ac322218e6df2ac1fa4008bac Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Sun, 18 Apr 2021 14:39:32 +0530 Subject: [PATCH 044/117] Add connected components undirected graph --- .../connected_components_undirected_graphs.py | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 data_structures/graphs/connected_components_undirected_graphs.py diff --git a/data_structures/graphs/connected_components_undirected_graphs.py b/data_structures/graphs/connected_components_undirected_graphs.py new file mode 100644 index 00000000..feb3dce4 --- /dev/null +++ b/data_structures/graphs/connected_components_undirected_graphs.py @@ -0,0 +1,46 @@ +from collections import defaultdict + + +class Graph: + + def __init__(self, vertices): + self.vertices = vertices + self.graph = defaultdict(list) + + + def add_edge(self, u, v): + self.graph[u].append(v) + self.graph[v].append(u) + + + def dfs(self, v, temp, visited): + visited[v] = True + temp.append(v) + + for i in self.graph[v]: + if visited[i] == False: + temp = self.dfs(i, temp, visited) + + return temp + + + def connected_components(self): + visited = [False] * self.vertices + cc = [] + + for i in range(self.vertices): + if visited[i] == False: + temp = [] + cc.append(self.dfs(i, temp, visited)) + + return cc + + +g = Graph(5) +g.add_edge(0, 1) +g.add_edge(1, 2) +g.add_edge(2, 0) +g.add_edge(0, 3) +g.add_edge(3, 4) + +print(g.connected_components()) \ No newline at end of file From 54cdebaf032197fb41396a841395f054a62c9f97 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Mon, 19 Apr 2021 17:23:23 +0530 Subject: [PATCH 045/117] Remove TODO from binary tree --- data_structures/binary_trees/TO_DO | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 data_structures/binary_trees/TO_DO diff --git a/data_structures/binary_trees/TO_DO b/data_structures/binary_trees/TO_DO deleted file mode 100644 index cb734cb7..00000000 --- a/data_structures/binary_trees/TO_DO +++ /dev/null @@ -1,2 +0,0 @@ -1. Max width of a binary tree -2. Total width of a binary tree From ec052e90f9a08da46935662ebac40fed91fef709 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 20 Apr 2021 14:09:51 +0530 Subject: [PATCH 046/117] Delete index --- data_structures/binary_trees/index.md | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 data_structures/binary_trees/index.md diff --git a/data_structures/binary_trees/index.md b/data_structures/binary_trees/index.md deleted file mode 100644 index 4f749eae..00000000 --- a/data_structures/binary_trees/index.md +++ /dev/null @@ -1,3 +0,0 @@ -# Index of binary_trees - -* [Symmetric Binary Tree](symmetric_binary_tree.py) From 505c3b41101d4149e9752c5a618a41f6cf703070 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 20 Apr 2021 16:07:39 +0530 Subject: [PATCH 047/117] boundary traversal --- .../binary_trees/boundary_traversal.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/data_structures/binary_trees/boundary_traversal.py b/data_structures/binary_trees/boundary_traversal.py index 760a6342..a4f08c00 100644 --- a/data_structures/binary_trees/boundary_traversal.py +++ b/data_structures/binary_trees/boundary_traversal.py @@ -15,8 +15,6 @@ def print_leaves(root): print_leaves(root.right) -# To ensure top down, print before calling for other -# To ensure bottom up, print after calling def print_left_boundary(root): if root: @@ -30,6 +28,11 @@ def print_left_boundary(root): def print_right_boundary(root): + """ + The traversal here will be from top to bottom because + this will be called after finishing the left side which is + top-to-bottom traversal + """ if root: if root.right: print_right_boundary(root.right) @@ -47,3 +50,14 @@ def print_boundary(root): print_leaves(root.left) print_leaves(root.right) print_right_boundary(root.right) + + +root = Node(20) +root.left = Node(8) +root.left.left = Node(4) +root.left.right = Node(12) +root.left.right.left = Node(10) +root.left.right.right = Node(14) +root.right = Node(22) +root.right.right = Node(25) +print_boundary(root) \ No newline at end of file From eaf5678fecdd14962bb0657c1c6d56323614abcd Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Wed, 21 Apr 2021 08:41:49 +0530 Subject: [PATCH 048/117] Add function call in check cousins --- data_structures/binary_trees/check_cousin.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/data_structures/binary_trees/check_cousin.py b/data_structures/binary_trees/check_cousin.py index 6453eed4..a65b95c8 100644 --- a/data_structures/binary_trees/check_cousin.py +++ b/data_structures/binary_trees/check_cousin.py @@ -34,3 +34,19 @@ def is_cousin(root, a, b): return True else: return False + + +root = Node(1) +root.left = Node(2) +root.right = Node(3) +root.left.left = Node(4) +root.left.right = Node(5) +root.left.right.right = Node(15) +root.right.left = Node(6) +root.right.right = Node(7) +root.right.left.right = Node(8) + +node1 = root.left.right +node2 = root.right.right + +print(is_cousin(root, node1, node2)) \ No newline at end of file From 6a0a08e919a77b7d7e50cbaa9a8df4c9b5434e2e Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Wed, 21 Apr 2021 09:49:23 +0530 Subject: [PATCH 049/117] Add implementation --- .../check_divide_in_two_halves.py | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/data_structures/binary_trees/check_divide_in_two_halves.py b/data_structures/binary_trees/check_divide_in_two_halves.py index df4f71b3..828138d2 100644 --- a/data_structures/binary_trees/check_divide_in_two_halves.py +++ b/data_structures/binary_trees/check_divide_in_two_halves.py @@ -1,8 +1,10 @@ -# Check if removing an edge of a binary tree can divide -# the tree in two equal halves +""" +Check if removing an edge of a binary tree can divide +the tree in two equal halves -# Count the number of nodes, say n. Then traverse the tree -# in bottom up manner and check if n - s = s +Solution - Count the number of nodes, say n. Then traverse the tree +in bottom up manner and find the size of every subtree (s). Check if n - s = s +""" class Node: @@ -31,5 +33,15 @@ def check_util(root, n): def check(root): - n = count(rot) + n = count(root) return check_util(root, n) + + +root = Node(5) +root.left = Node(1) +root.right = Node(6) +root.left.left = Node(3) +root.right.left = Node(7) +root.right.right = Node(4) + +print(check(root)) \ No newline at end of file From adddf51b3a076a3ebbecb5dd988406356a5196cb Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 22 Apr 2021 17:13:44 +0530 Subject: [PATCH 050/117] Add question --- data_structures/binary_trees/boundary_traversal.py | 8 +++++++- data_structures/binary_trees/check_if_graph_is_tree.py | 1 - 2 files changed, 7 insertions(+), 2 deletions(-) delete mode 100644 data_structures/binary_trees/check_if_graph_is_tree.py diff --git a/data_structures/binary_trees/boundary_traversal.py b/data_structures/binary_trees/boundary_traversal.py index a4f08c00..706970f1 100644 --- a/data_structures/binary_trees/boundary_traversal.py +++ b/data_structures/binary_trees/boundary_traversal.py @@ -1,3 +1,9 @@ +""" +Print the boundary traversal of a binary tree + +Boundary traversal - root, left (top-down), bottom, right (bottom-up) +""" + class Node: def __init__(self, val): @@ -21,7 +27,7 @@ def print_left_boundary(root): if root.left: print(root.val, end=' ') print_left_boundary(root.left) - + elif root.right: print(root.val, end=' ') print_left_boundary(root.right) diff --git a/data_structures/binary_trees/check_if_graph_is_tree.py b/data_structures/binary_trees/check_if_graph_is_tree.py deleted file mode 100644 index 8b137891..00000000 --- a/data_structures/binary_trees/check_if_graph_is_tree.py +++ /dev/null @@ -1 +0,0 @@ - From d59296d4ec3ce63969cfea2148ea2e3243d2fd29 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 22 Apr 2021 18:44:37 +0530 Subject: [PATCH 051/117] Modifications --- data_structures/binary_trees/check_cousin.py | 25 +++++++++++++------ .../binary_trees/check_full_binary_tree.py | 19 ++++++++++---- .../binary_trees/check_if_path_exists.py | 7 +++--- data_structures/binary_trees/traversals.py | 15 +++++++++++ 4 files changed, 50 insertions(+), 16 deletions(-) create mode 100644 data_structures/binary_trees/traversals.py diff --git a/data_structures/binary_trees/check_cousin.py b/data_structures/binary_trees/check_cousin.py index a65b95c8..c1f83d16 100644 --- a/data_structures/binary_trees/check_cousin.py +++ b/data_structures/binary_trees/check_cousin.py @@ -1,4 +1,11 @@ -# Check if two nodes are cousin or not +""" +Check if two nodes are cousin or not + +Condition for being cousin - +1. Level is same +2. They are not siblings + +""" class Node: @@ -11,22 +18,26 @@ def __init__(self, val): def level(root, node, lev): if not root: return 0 + if root == node: return lev - l = level(root.left, node, lev + 1) - + l = level(root.left, node, lev+1) + if not l == 0: return l - l = level(root.right, node, lev + 1) + l = level(root.right, node, lev+1) def is_sibling(root, a, b): - if root is None: - return 0 + if not root: + return False - return ( (root.left == a and root.right == b) or (root.left == b and root.right == a) or is_sibling(root.left, a, b) or is_sibling(root.right, a, b) ) + return (root.left == a and root.right == b) or \ + (root.right == b and root.left == a) or \ + is_sibling(root.left, a, b) or \ + is_sibling(root.right, a, b) def is_cousin(root, a, b): diff --git a/data_structures/binary_trees/check_full_binary_tree.py b/data_structures/binary_trees/check_full_binary_tree.py index 23c03f16..2498a4e6 100644 --- a/data_structures/binary_trees/check_full_binary_tree.py +++ b/data_structures/binary_trees/check_full_binary_tree.py @@ -1,4 +1,6 @@ -# A full binary tree is one which has 0 or 2 children only +""" +A full binary tree is a tree which has either 0 children or 2 children +""" class Node: @@ -9,14 +11,21 @@ def __init__(self, val): def check(root): - if root is None: + if not root: return True - if root.left is None and root.right is None: + if not root.left and not root.right: return True - if root.left is not None and root.right is not None: + if root.left and root.right: return check(root.left) and check(root.right) - return False +root = Node(0) +root.left = Node(1) +root.right = Node(2) + +if check(root): + print('True') +else: + print("False") \ No newline at end of file diff --git a/data_structures/binary_trees/check_if_path_exists.py b/data_structures/binary_trees/check_if_path_exists.py index be05f876..c6e33e5b 100644 --- a/data_structures/binary_trees/check_if_path_exists.py +++ b/data_structures/binary_trees/check_if_path_exists.py @@ -1,8 +1,7 @@ -# Check if a given path exists in a tree - -# Traverse the tree in preorder fashion and keep matching the node -# value to the index of the given path +""" +Given a path in an array form, check if this path leads to a leaf node +""" class Node: def __init__(self, val): diff --git a/data_structures/binary_trees/traversals.py b/data_structures/binary_trees/traversals.py new file mode 100644 index 00000000..5c26eb39 --- /dev/null +++ b/data_structures/binary_trees/traversals.py @@ -0,0 +1,15 @@ +class Node: + + def __init__(self, val): + self.val = val + self.left = None + self.right = None + + +class Tree: + + def __init__(self, root): + self.root = root + + + def inorder() From 015e49899c890710191ed61188fc4460203e190d Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Mon, 26 Apr 2021 10:52:47 +0530 Subject: [PATCH 052/117] Add height of tree --- .../binary_trees/height_of_tree.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 data_structures/binary_trees/height_of_tree.py diff --git a/data_structures/binary_trees/height_of_tree.py b/data_structures/binary_trees/height_of_tree.py new file mode 100644 index 00000000..cd082f1f --- /dev/null +++ b/data_structures/binary_trees/height_of_tree.py @@ -0,0 +1,35 @@ +""" +Find the height of a binary tree + +Height - (max of left height and right height) + 1 +""" + +class Node: + + def __init__(self, val): + self.val = val + self.left = None + self.right = None + + +def height(root): + if not root: + return 0 + + else: + lheight = height(root.left) + rheight = height(root.right) + + return 1 + max(lheight, rheight) + + +root = Node(1) +root.left = Node(2) +root.right = Node(3) +root.left.left = Node(7) +root.left.right = Node(6) +root.right.left = Node(5) +root.right.right = Node(4) +root.right.right.right = Node(40) + +print(height(root)) \ No newline at end of file From 397a53a5c0ddc84815007366b2cc3e8b96ad9aa1 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Mon, 26 Apr 2021 11:13:49 +0530 Subject: [PATCH 053/117] Add comments --- data_structures/binary_trees/diameter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data_structures/binary_trees/diameter.py b/data_structures/binary_trees/diameter.py index da1726b7..7c84ccd6 100644 --- a/data_structures/binary_trees/diameter.py +++ b/data_structures/binary_trees/diameter.py @@ -16,9 +16,9 @@ def height(root, ans): lheight = height(root.left, ans) rheight = height(root.right, ans) - ans[0] = max(ans[0], 1 + lheight + rheight) + ans[0] = max(ans[0], 1 + lheight + rheight) # This is for diameter - return 1 + max(lheight, rheight) + return 1 + max(lheight, rheight) # This is for height def diameter(root): From 253002b86bb2dfde0ab34be2158afb76a2b22162 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Mon, 26 Apr 2021 11:14:23 +0530 Subject: [PATCH 054/117] Add comments --- data_structures/binary_trees/longest_path_with_same_value.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data_structures/binary_trees/longest_path_with_same_value.py b/data_structures/binary_trees/longest_path_with_same_value.py index 288bbf95..6c285fd7 100644 --- a/data_structures/binary_trees/longest_path_with_same_value.py +++ b/data_structures/binary_trees/longest_path_with_same_value.py @@ -1,3 +1,7 @@ +""" +Find the longest path in the tree with the same value +""" + class Node: def __init__(self, val): From 213e225e0235004ea8c41a8813b193ad2c5bf15b Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Mon, 26 Apr 2021 12:00:03 +0530 Subject: [PATCH 055/117] Add new questions --- .../binary_trees/print_path_to_a_node.py | 10 ++-- ...ree.py => print_spiral_tree_two_stacks.py} | 0 data_structures/binary_trees/spiral_tree.py | 56 +++++++++++++++++++ 3 files changed, 60 insertions(+), 6 deletions(-) rename data_structures/binary_trees/{print_spiral_tree.py => print_spiral_tree_two_stacks.py} (100%) create mode 100644 data_structures/binary_trees/spiral_tree.py diff --git a/data_structures/binary_trees/print_path_to_a_node.py b/data_structures/binary_trees/print_path_to_a_node.py index 3e1ece97..7202dbc6 100644 --- a/data_structures/binary_trees/print_path_to_a_node.py +++ b/data_structures/binary_trees/print_path_to_a_node.py @@ -6,21 +6,19 @@ def __init__(self, val): self.right = None -def has_path(root, arr, x): +def has_path(root, stack, x): if not root: return False - arr.append(root.val) + stack.append(root.val) if root.val == x: return True - if has_path(root.left, arr, x) or has_path(root.right, arr, x): + if has_path(root.left, stack, x) or has_path(root.right, stack, x): return True - # If the required node is not in the left or right subtree, remove - # the parent node from where the fork starts - arr.pop() + stack.pop() return False diff --git a/data_structures/binary_trees/print_spiral_tree.py b/data_structures/binary_trees/print_spiral_tree_two_stacks.py similarity index 100% rename from data_structures/binary_trees/print_spiral_tree.py rename to data_structures/binary_trees/print_spiral_tree_two_stacks.py diff --git a/data_structures/binary_trees/spiral_tree.py b/data_structures/binary_trees/spiral_tree.py new file mode 100644 index 00000000..e8505384 --- /dev/null +++ b/data_structures/binary_trees/spiral_tree.py @@ -0,0 +1,56 @@ +""" +Do level order traversal of a tree in spiral form +""" + +class Node: + + def __init__(self, val): + self.val = val + self.left = None + self.right = None + + +def height(root): + if not root: + return 0 + + lheight = height(root.left) + rheight = height(root.right) + + return 1 + max(lheight, rheight) + + +def print_spiral(root): + h = height(root) + + left_to_right = False + + for i in range(1, h+1): + print_level(root, i, left_to_right) + left_to_right = not left_to_right + + +def print_level(root, level, left_to_right): + if not root: + return + + if level == 1: + print(root.val, end=' ') + elif level > 1: + if left_to_right: + print_level(root.left, level-1, left_to_right) + print_level(root.right, level-1, left_to_right) + else: + print_level(root.right, level-1, left_to_right) + print_level(root.left, level-1, left_to_right) + + +root = Node(1) +root.left = Node(2) +root.right = Node(3) +root.left.left = Node(7) +root.left.right = Node(6) +root.right.left = Node(5) +root.right.right = Node(4) + +print_spiral(root) \ No newline at end of file From 8764dc8f5c3ded1b5f3eef583268b474ab529bfd Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Mon, 26 Apr 2021 12:39:00 +0530 Subject: [PATCH 056/117] Add perfect tree --- data_structures/binary_trees/perfect_tree.py | 47 ++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 data_structures/binary_trees/perfect_tree.py diff --git a/data_structures/binary_trees/perfect_tree.py b/data_structures/binary_trees/perfect_tree.py new file mode 100644 index 00000000..68c1dfe0 --- /dev/null +++ b/data_structures/binary_trees/perfect_tree.py @@ -0,0 +1,47 @@ +""" +Check if a given binary tree is perfect or not +""" + +class Node: + + def __init__(self, val): + self.val = val + self.left = None + self.right = None + + +def find_depth(node): + d = 0 + while node: + d += 1 + node = node.left + return d + + +def is_perfect_util(root, d, level=0): + if not root: + return True + + if not root.left and not root.right: + return d == level + 1 + + if not root.left or not root.right: + return False + + return is_perfect_util(root.left, d, level+1) and is_perfect_util(root.right, d, level+1) + + +def is_perfect(root): + depth = find_depth(root) + return is_perfect_util(root, depth) + + +root = Node(10) +root.left = Node(20) +root.right = Node(30) +root.left.left = Node(40) +root.left.right = Node(50) +root.right.left = Node(60) +root.right.right = Node(70) + +print(is_perfect(root)) \ No newline at end of file From 547a2a06d21ce26871f4a2003a4f0beb087a8d77 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 27 Apr 2021 13:00:28 +0530 Subject: [PATCH 057/117] Add views --- data_structures/binary_trees/left_view.py | 38 +++++++ data_structures/binary_trees/right_view.py | 42 ++++++++ data_structures/binary_trees/top_view.py | 52 +++++++++ .../binary_trees/vertical_traversal.py | 102 ++++++++++++++++++ 4 files changed, 234 insertions(+) create mode 100644 data_structures/binary_trees/left_view.py create mode 100644 data_structures/binary_trees/right_view.py create mode 100644 data_structures/binary_trees/top_view.py create mode 100644 data_structures/binary_trees/vertical_traversal.py diff --git a/data_structures/binary_trees/left_view.py b/data_structures/binary_trees/left_view.py new file mode 100644 index 00000000..63cdbf9d --- /dev/null +++ b/data_structures/binary_trees/left_view.py @@ -0,0 +1,38 @@ +""" +Print left view of a binary tree +""" + +class Node: + + def __init__(self, val): + self.val = val + self.left = None + self.right = None + + +def left_view_util(root, max_level, level): + if not root: + return + + if max_level[0] < level: + print(root.val) + max_level[0] = level + + left_view_util(root.left, max_level, level+1) + left_view_util(root.right, max_level, level+1) + + +def left_view(root): + max_level = [0] + left_view_util(root, max_level, 1) + + +root = Node(1) +root.left = Node(2) +root.right = Node(3) +root.left.left = Node(4) +root.left.left.left = Node(5) +root.left.left.left.left = Node(6) +root.right.right = Node(7) +root.right.right.right = Node(8) +left_view(root) \ No newline at end of file diff --git a/data_structures/binary_trees/right_view.py b/data_structures/binary_trees/right_view.py new file mode 100644 index 00000000..cc6aa796 --- /dev/null +++ b/data_structures/binary_trees/right_view.py @@ -0,0 +1,42 @@ +""" +Print right view of a binary tree + +The idea behind using max_level[0] is that - +1. Its value when changes will be reflected in every recursion call +2. We are visiting right side first. So this acts as a check that that level was done +""" + +class Node: + + def __init__(self, val): + self.val = val + self.left = None + self.right = None + + +def right_view_util(root, max_level, level): + if not root: + return + + if max_level[0] < level: + print(root.val) + max_level[0] = level + + right_view_util(root.right, max_level, level+1) + right_view_util(root.left, max_level, level+1) + + +def right_view(root): + max_level = [0] + right_view_util(root, max_level, 1) + + +root = Node(1) +root.left = Node(2) +root.right = Node(3) +root.left.left = Node(4) +root.left.right = Node(5) +root.right.left = Node(6) +root.right.right = Node(7) +root.right.left.right = Node(8) +right_view(root) \ No newline at end of file diff --git a/data_structures/binary_trees/top_view.py b/data_structures/binary_trees/top_view.py new file mode 100644 index 00000000..565e9e00 --- /dev/null +++ b/data_structures/binary_trees/top_view.py @@ -0,0 +1,52 @@ +""" +Print the top view of a binary tree + +Almost like vertical traversal +""" + +class Node: + + def __init__(self, val): + self.val = val + self.left = None + self.right = None + self.col = None + + +def top_view(root): + if not root: + return + + queue = [] + col = 0 + d = {} + + queue.append(root) + root.col = col + + while queue: + root = queue.pop(0) + col = root.col + + if col not in d: + d[col] = root.val + + if root.left: + queue.append(root.left) + root.left.col = col - 1 + if root.right: + queue.append(root.right) + root.right.col = col + 1 + + for i in sorted(d): + print(d[i], end=" ") + + +root = Node(1) +root.left = Node(2) +root.right = Node(3) +root.left.right = Node(4) +root.left.right.right = Node(5) +root.left.right.right.right = Node(6) + +top_view(root) \ No newline at end of file diff --git a/data_structures/binary_trees/vertical_traversal.py b/data_structures/binary_trees/vertical_traversal.py new file mode 100644 index 00000000..344a546f --- /dev/null +++ b/data_structures/binary_trees/vertical_traversal.py @@ -0,0 +1,102 @@ +""" +Print vertical view of a binary tree + + 1 + / \ + 2 3 + / \ / \ + 4 5 6 7 + / \ + 8 9 + + +The output of print this tree vertically will be: +4 +2 +1 5 6 +3 8 +7 +9 + +source - https://www.geeksforgeeks.org/print-binary-tree-vertical-order-set-2/ +""" + +class Node: + + def __init__(self, val): + self.val = val + self.left = None + self.right = None + self.col = None + + +def print_vertical_util(root, col, d): + if not root: + return + + if col in d: + d[col].append(root.val) + else: + d[col] = [root.val] + + print_vertical_util(root.left, col-1, d) + print_vertical_util(root.right, col+1, d) + + +def print_vertical(root): + d = {} + col = 0 + + print_vertical_util(root, col, d) + + for k, v in sorted(d.items()): + for i in v: + print(i, end=' ') + print() + + +def print_vertical_iterative(root): + queue = [] + col = 0 + d = {} + + queue.append(root) + root.col = col + + while queue: + root = queue.pop(0) + col = root.col + + if col not in d: + d[col] = [root.val] + else: + d[col].append(root.val) + + if root.left: + queue.append(root.left) + root.left.col = col - 1 + if root.right: + queue.append(root.right) + root.right.col = col + 1 + + for k, v in sorted(d.items()): + for i in v: + print(i, end=' ') + print() + + + +root = Node(1) +root.left = Node(2) +root.right = Node(3) +root.left.left = Node(4) +root.left.right = Node(5) +root.right.left = Node(6) +root.right.right = Node(7) +root.right.left.right = Node(8) +root.right.right.right = Node(9) + +print_vertical(root) + +print("Iterative solution - ") +print_vertical_iterative(root) \ No newline at end of file From b6d135f510e2cc772cdf34f8d8564744bb93aca7 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Wed, 28 Apr 2021 13:30:09 +0530 Subject: [PATCH 058/117] add comments --- data_structures/bst/second_largest_in_bst.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/data_structures/bst/second_largest_in_bst.py b/data_structures/bst/second_largest_in_bst.py index 598b36bb..334a3dd6 100644 --- a/data_structures/bst/second_largest_in_bst.py +++ b/data_structures/bst/second_largest_in_bst.py @@ -1,3 +1,14 @@ +""" +There can be two condition for finding the second largest element in a BST + +1. If right subtree does not exist, find the largest on the left side +Otherwise, +2. If right exists but right's left and right's right do not, that means +you are currently at the second largest element + +Move to right +""" + class Node: def __init__(self, val): From 0bfea10c8a919be75eca512345b2a89985cb486d Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Wed, 28 Apr 2021 20:14:38 +0530 Subject: [PATCH 059/117] Remove index --- data_structures/bst/index.md | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 data_structures/bst/index.md diff --git a/data_structures/bst/index.md b/data_structures/bst/index.md deleted file mode 100644 index 97a6320f..00000000 --- a/data_structures/bst/index.md +++ /dev/null @@ -1,31 +0,0 @@ -# Index of BST - -* [Deletion](deletion.py) -* [Sorted Array To BST](sorted_array_to_bst.py) -* [Print Left Node](print_left_node.py) -* [Diameter](diameter.py) -* [BFS](bfs.py) -* [Check if BT is BST](check_if_bt_if_bst.py) -* [Trim BST](trim_bst.py) -* [BT to BST](bt_to_bst.py) -* [Binary Search Tree](binary_search_tree.py) -* [Convert BST to Right Node Tree](convert_bst_to_right_node_tree.py) -* [kth largest in BST](kth_largest_in_bst.py) -* [Range Sum](range_sum.py) -* [DFS Iterative](dfs_iterative.py) -* [Print Ancestor](print_ancestor.py) -* [Min Max Value in BST](min_max_value_in_bst.py) -* [Check BT is Subtree of another BT](check_bt_is_subtree_of_another_bt.py) -* [Ceil](ceil.py) -* [Closest Element](closest_element.py) -* [kth smallest in BST](kth_smallest_in_bst.py) -* [Insertion Iterative](insertion_iterative.py) -* [Lowest Common Ancestor](lowest_common_ancestor.py) -* [DFS Recursion](dfs_recursion.py) -* [Search](search.py) -* [Insertion Recursive](insertion_recursive.py) -* [Duplicate Keys](duplicate_keys.py) -* [Merge Sum](merge_sum.py) -* [Linked List to BST](linked_list_to_bst.py) -* [Reverse Inorder Traversal](reverse_inorder_traversal.py) -* [Average of Levels](average_of_levels.py) From bf82d5cb426318a075654910fba9d81c18a570f4 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 29 Apr 2021 18:21:28 +0530 Subject: [PATCH 060/117] Add comments --- data_structures/bst/duplicate_keys.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/data_structures/bst/duplicate_keys.py b/data_structures/bst/duplicate_keys.py index 4e0e5cbf..ba8b6123 100644 --- a/data_structures/bst/duplicate_keys.py +++ b/data_structures/bst/duplicate_keys.py @@ -1,3 +1,11 @@ +""" +Create a BST such that it can have duplicate nodes. +Technically BST cannot have duplicate nodes. The work around is to +have a counter associated with every node. Increment its count whenever +there is a duplicate. If the count goes to zero, delete that node +""" + + class Node(): def __init__(self, val): From 09edf53c5f4676eb66c448b27f885e0600456350 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 29 Apr 2021 18:34:26 +0530 Subject: [PATCH 061/117] Complete duplicate keys by adding deletion --- data_structures/bst/duplicate_keys.py | 37 +++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/data_structures/bst/duplicate_keys.py b/data_structures/bst/duplicate_keys.py index ba8b6123..a09dc2c1 100644 --- a/data_structures/bst/duplicate_keys.py +++ b/data_structures/bst/duplicate_keys.py @@ -45,12 +45,41 @@ def insert(root, val): return root +def min_value_node(root): + curr = root + while curr: + curr = curr.left + return curr.val + + def delete(root, val): if not root: - return - if root.val == val: + return None + + if val < root.val: + root.left = delete(root.left, val) + elif val > root.val: + root.right = delete(root.right, val) + else: if root.count > 1: root.count -= 1 + else: + # check if left node is None + if not root.left: + temp = root.right + root = None + return temp + # chec if right node is None + if not root.right: + temp = root.left + root = None + return temp + + temp = min_value_node(root.right) + root.val = temp.val + root.right = delete(root.right, temp.val) + + return root root = Node(5) @@ -61,3 +90,7 @@ def delete(root, val): insert(root, 10) inorder(root) + +print("After deletion") +root = delete(root, 8) +inorder(root) \ No newline at end of file From 2fc7e45577c53d103e90e1fb5de631126e157445 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Fri, 30 Apr 2021 12:26:58 +0530 Subject: [PATCH 062/117] Add comments --- data_structures/bst/kth_largest_in_bst.py | 8 ++++++++ data_structures/bst/kth_smallest_in_bst.py | 8 ++++++++ data_structures/bst/print_ancestor.py | 3 --- data_structures/bst/trim_bst.py | 6 +++++- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/data_structures/bst/kth_largest_in_bst.py b/data_structures/bst/kth_largest_in_bst.py index 1a013baf..e96eb84c 100644 --- a/data_structures/bst/kth_largest_in_bst.py +++ b/data_structures/bst/kth_largest_in_bst.py @@ -1,3 +1,11 @@ +""" +Print the kth largest element in a BST + +The inorder traversal gives elements of BST in ascending order. Do reverse inorder +(Right-Node-Left) and print the kth element +""" + + class Node(): def __init__(self, val): diff --git a/data_structures/bst/kth_smallest_in_bst.py b/data_structures/bst/kth_smallest_in_bst.py index aefa1345..26131ee1 100644 --- a/data_structures/bst/kth_smallest_in_bst.py +++ b/data_structures/bst/kth_smallest_in_bst.py @@ -1,3 +1,11 @@ +""" +Print the kth smallest number in BST + +The inorder traversal of BST gives elements in ascending order. So do inorder, +keep count and return the kth value +""" + + class Node(): def __init__(self, val): diff --git a/data_structures/bst/print_ancestor.py b/data_structures/bst/print_ancestor.py index b443c559..b065efa0 100644 --- a/data_structures/bst/print_ancestor.py +++ b/data_structures/bst/print_ancestor.py @@ -14,6 +14,3 @@ def print_ancestor_recursive(root, key): if print_ancestor_recursive(root.left, key) or print_ancestor_recursive(root.right, key): return root.data return False - - - diff --git a/data_structures/bst/trim_bst.py b/data_structures/bst/trim_bst.py index 31a17422..1ba86545 100644 --- a/data_structures/bst/trim_bst.py +++ b/data_structures/bst/trim_bst.py @@ -1,3 +1,7 @@ +""" +Trim a BST so that all elements lie within a given high and low range +""" + class Node(): def __init__(self, val): @@ -12,7 +16,7 @@ def trim(root, L, R): if root.val > R: return trim(root.left, L, R) if root.val < L: - return trim(root. right, L, R) + return trim(root.right, L, R) root.left = trim(root.left, L, R) root.right = trim(root.right, L, R) return root From f454f57eeee84b7efefa973f35fb7a24d9cf6be5 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 6 May 2021 09:43:56 +0530 Subject: [PATCH 063/117] Add comments --- data_structures/graphs/print_all_paths_between_nodes.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data_structures/graphs/print_all_paths_between_nodes.py b/data_structures/graphs/print_all_paths_between_nodes.py index cbd04abf..0b370237 100644 --- a/data_structures/graphs/print_all_paths_between_nodes.py +++ b/data_structures/graphs/print_all_paths_between_nodes.py @@ -24,6 +24,8 @@ def print_path(self, s, d, visited, path): if visited[i] == False: self.print_path(i, d, visited, path) + # If path from this node does not lead to the destination, remove it + # from the path stack and mark it as not visited path.pop() visited[s] = False From 25e8966ea638866c13ca937c3e858fd28d067aab Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 6 May 2021 16:59:56 +0530 Subject: [PATCH 064/117] Add min degree --- .../graphs/min_nodes_to_reach_all_nodes.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 data_structures/graphs/min_nodes_to_reach_all_nodes.py diff --git a/data_structures/graphs/min_nodes_to_reach_all_nodes.py b/data_structures/graphs/min_nodes_to_reach_all_nodes.py new file mode 100644 index 00000000..d60f6f9b --- /dev/null +++ b/data_structures/graphs/min_nodes_to_reach_all_nodes.py @@ -0,0 +1,11 @@ +""" +We are given a DAG. Find the smallest set of vertices from which all +nodes in the graph are reachable. It's guaranteed that a unique solution exists. + +Solution - +The crux of the question is that - +1. If any node has an indegree > 1 (i.e it is reachable from any other node), then it means +in a connected graph it will be possible to reach here if its parent node also has an indegree +2. So the min will be the set of nodes with indegree = 0 +""" + From fafcfab9a6b528834fbfb74f7f3e4e4fa5cb0662 Mon Sep 17 00:00:00 2001 From: RK Date: Mon, 10 May 2021 10:36:45 +0530 Subject: [PATCH 065/117] fixed bug in line 20 and simplified code for creating graph --- data_structures/graphs/Adjacency_matrix.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/data_structures/graphs/Adjacency_matrix.py b/data_structures/graphs/Adjacency_matrix.py index 7895a279..f173bc37 100644 --- a/data_structures/graphs/Adjacency_matrix.py +++ b/data_structures/graphs/Adjacency_matrix.py @@ -4,10 +4,7 @@ def __init__(self, vertices, directed: bool): self.V = vertices self.e = 0 self.d = directed - self.graph = [] - for i in range(self.V): - lst = [0] * self.V - self.graph.append(lst) + self.graph = [[0 for i in range(vertices)] for j in range(vertices)] def add_edge(self, ver1, ver2): if self.d: @@ -17,7 +14,7 @@ def add_edge(self, ver1, ver2): self.graph[ver2][ver1] = 1 def remove_edge(self, ver1, ver2): - if self.d[ver1][ver2] == 0: + if self.graph[ver1][ver2] == 0: print("No edge between %d and %d" % (ver1, ver2)) return if self.d: @@ -30,6 +27,11 @@ def print_graph(self): for i in self.graph: print(i) - - +if __name__=="__main__": + g1 = Graph(3,0) + g1.add_edge(0,0) + g1.add_edge(1,1) + g1.add_edge(2,2) + g1.remove_edge(2,1) + g1.print_graph() From eb8ea1471c3f9473776ec6ba9bf95a52f3a36864 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Mon, 10 May 2021 18:02:12 +0530 Subject: [PATCH 066/117] New changes --- README.md | 4 ++++ data_structures/graphs/dijsktra_algorithm.py | 0 2 files changed, 4 insertions(+) create mode 100644 data_structures/graphs/dijsktra_algorithm.py diff --git a/README.md b/README.md index ec31aa8c..b5a68823 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ No non-sense solutions to common Data Structure and Algorithm interview question ## Objective +There are a plenty of resources when it comes to interview preparations on the internet. What prompted me to create this project was the dissimilarity across different approaches and the infused complexity of the code. + +Feel free to contribute but please follow the Contributing Guidelines as I want to maintain the uniformity of the implementation of data structures and algorithms. Last time around, people bombarded with me with Pull Requests, Issues and Emails insisting me to merge their changes + The open source community has helped me a lot during my interview preparations and studies while I was in my undergrad. I always wanted to give something back to the community. In my endeavour to contribute something back, I will be uploading data structures and algorithms questions in Python in this repo. Feel free to contribute and get in touch! ## Structure of the repository diff --git a/data_structures/graphs/dijsktra_algorithm.py b/data_structures/graphs/dijsktra_algorithm.py new file mode 100644 index 00000000..e69de29b From cbe2097711a3fc34aad779a263d8ab8c26b33a53 Mon Sep 17 00:00:00 2001 From: Rohan Krishna Ullas <71774184+Kakarot-2000@users.noreply.github.com> Date: Tue, 18 May 2021 15:39:41 +0530 Subject: [PATCH 067/117] Updated balanced_expression.py to fix bugs --- data_structures/stack/balanced_expression.py | 26 ++++++++++++-------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/data_structures/stack/balanced_expression.py b/data_structures/stack/balanced_expression.py index 784666c0..60e3c506 100644 --- a/data_structures/stack/balanced_expression.py +++ b/data_structures/stack/balanced_expression.py @@ -1,24 +1,30 @@ -# simple program to check if an expression is balanced using stack stack = [] def checkBalanced(expr): for i in expr: if i == "{" or i == "[" or i == "(": stack.append(i) elif i == "}" or i == "]" or i == ")": - temp = stack.pop() - if i == "}" and temp != "{": + if not stack: return False - elif i == "]" and temp != "[": + top = stack.pop() + if i == "}" and top != "{": return False - elif i == ")" and temp != "(": + elif i == "]" and top != "[": return False + elif i == ")" and top != "(": + return False + else: + print("Invalid Expression") + return False - return True + if not len(stack): + return True + else: + return False # main function expr = input() -result = checkBalanced(expr) -if result: - print("Expression is balanced") +if not checkBalanced(expr): + print("Not Balanced") else: - print("Expression is not balanced") + print('Balanced') From a8954614d2128f40a50fb9e932be37a671b68d4e Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Wed, 2 Jun 2021 09:34:14 +0530 Subject: [PATCH 068/117] Add array to bt --- .../binary_trees/array_to_binary_tree.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 data_structures/binary_trees/array_to_binary_tree.py diff --git a/data_structures/binary_trees/array_to_binary_tree.py b/data_structures/binary_trees/array_to_binary_tree.py new file mode 100644 index 00000000..9057b163 --- /dev/null +++ b/data_structures/binary_trees/array_to_binary_tree.py @@ -0,0 +1,37 @@ +""" +Convert an array to a binary tree + +Sample input - +[1,2,3,4,5,null,6,7,null,null,null,null,8] + +Note - +if a tree has N nodes and is complete, then the no of internal +nodes can be (N-1) / 2 +""" + +class Node: + + def __init__(self, val): + self.val = val + self.left = None + self.right = None + + +def create_tree(arr): + curr_ptr = 0 + child_ptr = 0 + + root = Node(arr[0]) + curr_node = root + + while i < (len(arr) - 1)/2: + curr_ptr = arr[i] + child_ptr = i + 1 + + left_child = arr[child_ptr] + right_child = arr[child_ptr + 1] + + curr_node.left = Node(left_child) + curr_node.right = Node(right_child) + + From 9e8e9ca35ef876cf7543ef068f3dc9cca000a758 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Fri, 4 Jun 2021 18:52:03 +0530 Subject: [PATCH 069/117] Update first_repeating_char.py --- data_structures/array/first_repeating_char.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data_structures/array/first_repeating_char.py b/data_structures/array/first_repeating_char.py index 2ba272eb..89477411 100644 --- a/data_structures/array/first_repeating_char.py +++ b/data_structures/array/first_repeating_char.py @@ -1,4 +1,4 @@ -# Find the first character in a string without using extra space +# Find the first repeated character in a string without using extra space # With extra space its simple. Just check for the element in a hash map # If present, then it is the recurrent char From e522e65b070759ba0bb0f5672e6d3566dea77b63 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Fri, 4 Jun 2021 20:21:41 +0530 Subject: [PATCH 070/117] Update cycle_in_undirected_graph_iterative.py --- data_structures/graphs/cycle_in_undirected_graph_iterative.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data_structures/graphs/cycle_in_undirected_graph_iterative.py b/data_structures/graphs/cycle_in_undirected_graph_iterative.py index 53bca95d..565cedde 100644 --- a/data_structures/graphs/cycle_in_undirected_graph_iterative.py +++ b/data_structures/graphs/cycle_in_undirected_graph_iterative.py @@ -4,7 +4,7 @@ traverse but in undirected its possible that an edge (or a path) can be traversed infite number of times. -Instead check for parents (which means vertex fro which you reached the current vertex). +Instead check for parents (which means vertex from which you reached the current vertex). If a vertex is visited and you are not coming to this vertex from the current "source" vertex (source - vertex from which DFS has started), then it means that in the same DFS chain, there is another path to reach this vertex - hence a cycle @@ -57,4 +57,4 @@ def dfs(self): g.add_edge(2, 0) g.add_edge(0, 3) g.add_edge(3, 4) -print(g.dfs()) \ No newline at end of file +print(g.dfs()) From bca7a225fa8bb9f2fba312a4c27ac4e6b98eff65 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Mon, 7 Jun 2021 08:38:36 +0530 Subject: [PATCH 071/117] Update topics.md --- bookmarks/topics.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bookmarks/topics.md b/bookmarks/topics.md index bf8c7be2..d1d54010 100644 --- a/bookmarks/topics.md +++ b/bookmarks/topics.md @@ -14,4 +14,6 @@ This is a list of links to topics that may be helpful in learning or researching - https://stackoverflow.com/questions/40200413/sessions-vs-token-based-authentication -- https://stackoverflow.com/questions/15678406/when-to-use-myisam-and-innodb \ No newline at end of file +- https://stackoverflow.com/questions/15678406/when-to-use-myisam-and-innodb + +- https://www.bigocheatsheet.com/ From 40a29e79ce1b1fa78cb0a4b9995d4342bca642c4 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Mon, 7 Jun 2021 10:31:07 +0530 Subject: [PATCH 072/117] add heap --- data_structures/heap/max_heap.py | 152 +++++++++++++++++++++++++++++++ data_structures/heap/min_heap.py | 0 2 files changed, 152 insertions(+) create mode 100644 data_structures/heap/max_heap.py create mode 100644 data_structures/heap/min_heap.py diff --git a/data_structures/heap/max_heap.py b/data_structures/heap/max_heap.py new file mode 100644 index 00000000..3061e2d3 --- /dev/null +++ b/data_structures/heap/max_heap.py @@ -0,0 +1,152 @@ +""" +Thing to remember - +* index of parent = i / 2 +* index of left child = 2i + 1 +* index of right child = 2i + 2 +""" + +import sys + +class MaxHeap: + + def __init__(self, maxsize): + self.maxsize = maxsize + self.size = 0 # current number of elements in the heap + self.heap = [0] * self.maxsize + self.front = 0 + + + def parent(self, pos): + return pos // 2 + + + def left_child(self, pos): + return 2*pos + 1 + + + def right_child(self, pos): + return 2*pos + 2 + + + def mid_index(self): + return self.size // 2 + + + def last_index(self): + return self.size - 1 + + + def is_leaf(self, pos): + """ + Every node that is after the middle index of the heap + is a leaf node because their children cannot exist as the + index of children are twice their index as those indexes + do not exist in the heap + """ + if self.mid_index() <= pos <= self.last_index(): + return True + return False + + + def is_empty(self): + if self.size == 0 and not self.heap[0]: + return True + return False + + + def insert(self, value): + if self.is_empty(): # if the heap is empty + self.heap[self.front] = value + self.size += 1 + return + + if self.size >= self.maxsize: # if max size has been reached + return + + self.size += 1 + self.heap[self.last_index()] = value + + curr = self.last_index() + + # While inserting the element in the heap we have to + # make sure that the inserted element is always smaller + # than its parent. So basically here we are adjusting the + # position of the parent + while self.heap[curr] > self.heap[self.parent(curr)]: + self.swap(curr, self.parent(curr)) + curr = self.parent(curr) + + + def max_heapify(self, pos): + """ + This function will run whenever a node is non-leaf + node and smaller than its childen + """ + if not self.is_leaf(pos): + left = self.heap[self.left_child(pos)] + right = self.heap[self.right_child(pos)] + curr = self.heap[pos] + + if curr < left or curr < right: + + if left > right: + self.swap(pos, self.left_child(pos)) + self.max_heapify(self.left_child(pos)) + else: + self.swap(pos, self.right_child(pos)) + self.max_heapify(self.right_child(pos)) + + + def swap(self, x, y): + self.heap[x], self.heap[y] = self.heap[y], self.heap[x] + + + def pop_max(self): + max_element = self.heap[self.front] # max element is always at the front + self.heap[self.front] = self.heap[self.last_index()] # placing last element at the front + self.heap[self.last_index()] = 0 + self.size -= 1 # decrease size as one element has been popped + self.max_heapify(self.front) # heapify the heap again + return max_element + + + def print(self): + """ + Priting in inorder + """ + for i in range(0, self.mid_index() + 1): + parent = self.heap[i] + left = self.heap[self.left_child(i)] + right = self.heap[self.right_child(i)] + + print(f"Parent: {self.heap[i]}") + + if left: + print(f"Left child: {left}") + if right: + print(f"Right child: {right}") + + +if __name__ == '__main__': + max_heap = MaxHeap(15) + max_heap.insert(5) + max_heap.insert(3) + max_heap.insert(17) + max_heap.insert(10) + max_heap.insert(84) + max_heap.insert(19) + max_heap.insert(6) + max_heap.insert(22) + max_heap.insert(9) + + max_heap.print() + + print('Max element is - ', max_heap.pop_max()) + + + max_heap.print() + + print('Max element is - ', max_heap.pop_max()) + + + diff --git a/data_structures/heap/min_heap.py b/data_structures/heap/min_heap.py new file mode 100644 index 00000000..e69de29b From b76544e65806cb80ab6c3263651464d5a840e432 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Mon, 7 Jun 2021 17:42:26 +0530 Subject: [PATCH 073/117] Add code for min and max heap --- data_structures/heap/max_heap.py | 17 +---- data_structures/heap/min_heap.py | 122 +++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 13 deletions(-) diff --git a/data_structures/heap/max_heap.py b/data_structures/heap/max_heap.py index 3061e2d3..29fcef70 100644 --- a/data_structures/heap/max_heap.py +++ b/data_structures/heap/max_heap.py @@ -5,8 +5,6 @@ * index of right child = 2i + 2 """ -import sys - class MaxHeap: def __init__(self, maxsize): @@ -17,7 +15,7 @@ def __init__(self, maxsize): def parent(self, pos): - return pos // 2 + return (pos) // 2 def left_child(self, pos): @@ -49,7 +47,7 @@ def is_leaf(self, pos): def is_empty(self): - if self.size == 0 and not self.heap[0]: + if self.size == 0: return True return False @@ -88,7 +86,8 @@ def max_heapify(self, pos): curr = self.heap[pos] if curr < left or curr < right: - + + # This check is only to prevent out-of-index error if left > right: self.swap(pos, self.left_child(pos)) self.max_heapify(self.left_child(pos)) @@ -140,13 +139,5 @@ def print(self): max_heap.insert(9) max_heap.print() - print('Max element is - ', max_heap.pop_max()) - - max_heap.print() - - print('Max element is - ', max_heap.pop_max()) - - - diff --git a/data_structures/heap/min_heap.py b/data_structures/heap/min_heap.py index e69de29b..069524d4 100644 --- a/data_structures/heap/min_heap.py +++ b/data_structures/heap/min_heap.py @@ -0,0 +1,122 @@ +""" +See max_heap.py for more detailed comments +""" + +class MinHeap: + + + def __init__(self, maxsize): + self.maxsize = maxsize + self.size = 0 + self.first = 0 + self.heap = [0] * self.maxsize + + + def is_empty(self): + return self.size == 0 + + + def is_leaf(self, pos): + if self.mid_index() <= pos <= self.last_index(): + return True + return False + + + def parent(self, pos): + return pos // 2 + + + def left_child(self, pos): + return 2*pos + 1 + + + def right_child(self, pos): + return 2*pos + 2 + + + def last_index(self): + return self.size - 1 + + + def mid_index(self): + return self.size // 2 + + + def swap(self, x, y): + self.heap[x], self.heap[y] = self.heap[y], self.heap[x] + + + def pop_min(self): + min_element = self.heap[self.first] + self.heap[self.first] = self.heap[self.last_index()] + self.heap[self.last_index()] = 0 + self.size -= 1 + self.min_heapify(self.first) + return min_element + + + def min_heapify(self, pos): + if not self.is_leaf(pos): + left = self.heap[self.left_child(pos)] + right = self.heap[self.right_child(pos)] + curr = self.heap[pos] + + if curr > left or curr > right: + + if left > right: + self.swap(pos, self.left_child(pos)) + self.min_heapify(self.left_child(pos)) + else: + self.swap(pos, self.right_child(pos)) + self.min_heapify(self.right_child(pos)) + + + def insert(self, element): + if self.is_empty(): + self.heap[self.first] = element + self.size += 1 + return + + if self.size >= self.maxsize: + return + + self.size += 1 + self.heap[self.last_index()] = element + + curr = self.last_index() + + while self.heap[curr] < self.heap[self.parent(curr)]: + self.swap(curr, self.parent(curr)) + curr = self.parent(curr) + + + def print(self): + for i in range(0, self.mid_index() + 1): + parent = self.heap[i] + left = self.heap[self.left_child(i)] + right = self.heap[self.right_child(i)] + + print(f"Parent: {self.heap[i]}") + + if left: + print(f"Left child: {left}") + if right: + print(f"Right child: {right}") + + +if __name__ == '__main__': + min_heap = MinHeap(15) + min_heap.insert(5) + min_heap.insert(3) + min_heap.insert(17) + min_heap.insert(10) + min_heap.insert(84) + min_heap.insert(19) + min_heap.insert(6) + min_heap.insert(22) + min_heap.insert(9) + + min_heap.print() + print(min_heap.heap) + print('Min element is - ', min_heap.pop_min()) + min_heap.print() From 1dd751bc3e99148a82c9ca034f80985e6531a0ca Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Mon, 7 Jun 2021 17:47:21 +0530 Subject: [PATCH 074/117] add heap using library functions --- data_structures/heap/heap_using_heapq.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 data_structures/heap/heap_using_heapq.py diff --git a/data_structures/heap/heap_using_heapq.py b/data_structures/heap/heap_using_heapq.py new file mode 100644 index 00000000..0ee5410a --- /dev/null +++ b/data_structures/heap/heap_using_heapq.py @@ -0,0 +1,23 @@ +""" +Heap in python using heapq library function + +Note: by default, heapq creates a min-heap. To make it a +max-heap, add items after multiplying them by -1 +""" + +from heapq import heappop, heappush, heapify + +heap = [] +heapify(heap) + +heappush(heap, 10) +heappush(heap, 11) +heappush(heap, 2) +heappush(heap, 4) +heappush(heap, 14) +heappush(heap, 1) + +print('first element - ', heap[0]) +print('popping min element - ', heappop(heap)) +print('first element - ', heap[0]) + From 936d1734791ef509f4b807693e67f10c13e369d4 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 8 Jun 2021 06:33:10 +0530 Subject: [PATCH 075/117] Add array indexes --- data_structures/heap/heap_using_heapq.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data_structures/heap/heap_using_heapq.py b/data_structures/heap/heap_using_heapq.py index 0ee5410a..7e28d109 100644 --- a/data_structures/heap/heap_using_heapq.py +++ b/data_structures/heap/heap_using_heapq.py @@ -21,3 +21,6 @@ print('popping min element - ', heappop(heap)) print('first element - ', heap[0]) +# Heap prints as an array and can be access using indexes +print(heap) +print(heap[2]) From 440cec21054a5a5e77c265882fd32fdfecaa12b4 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 8 Jun 2021 06:33:28 +0530 Subject: [PATCH 076/117] Add median of infinite stream --- .../heap/median_of_infinite_stream.py | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 data_structures/heap/median_of_infinite_stream.py diff --git a/data_structures/heap/median_of_infinite_stream.py b/data_structures/heap/median_of_infinite_stream.py new file mode 100644 index 00000000..9ce834a8 --- /dev/null +++ b/data_structures/heap/median_of_infinite_stream.py @@ -0,0 +1,95 @@ +""" +Awesome explanation - https://youtu.be/1LkOrc-Le-Y + +Median is the middle element in a sorted array. The stream of input +integers can be in any order and we will have to store the integers in +such a way that the stream is maintained in an increasing order. + +So the main idea is that take an array and divide it into two parts of +equal length and the median will be as follows - + +* if the total number of integers in the stream is even, then both part will +have the same length. Hence the median will be the average of last element of the first +part (i.e max element of the first part) and the first element of the second part (i.e +min element of the second part) + +* if total number of integers in the stream is odd, add the extra element in the first part. +In this case, the median will be the last element of the first part + +Now we just have to maintain the order of both the parts of the array. Since we want the max +element from the first part, we can use a max heap there. And we can use min heap for the second +part as we need the min element from the second part + +Time complexity: + if the size of stream is N, we will have to iterate N times. LogN because insertion in heap + takes this much time. O(1) for getting the max or min element + + N * LogN +""" + +from heapq import heappush, heappop, heapify + +class MedianStream: + + def __init__(self): + self.stream = [] + self.min_heap = [] + self.max_heap = [] + heapify(self.max_heap) + heapify(self.min_heap) + self.curr_median = None + + + def add_number(self, num): + """ + min heap length <= maxheap length <= min heap length + 1 + """ + self.stream.append(num) + + if len(self.max_heap) == len(self.min_heap) == 0: + self.curr_median = num + + if len(self.max_heap) > len(self.min_heap): + if num < self.curr_median: + max_popped = -1 * heappop(self.max_heap) + heappush(self.min_heap, max_popped) + heappush(self.max_heap, -1 * num) + self.find_median('avg') + else: + heappush(self.min_heap, num) + self.find_median('avg') + else: + if num > self.curr_median: + # num will go to the min heap and the min element + # of min heap will go the max heap + min_popped = heappop(self.min_heap) + heappush(self.max_heap, -1 * min_popped) + heappush(self.min_heap, num) + self.find_median('max') + + else: + # num will go to the max heap + heappush(self.max_heap, -1 * num) + self.find_median('max') + + + def find_median(self, how): + if how == 'max': + self.curr_median = -1 * self.max_heap[0] + elif how == 'avg': + self.curr_median = (self.min_heap[0] + (-1 * self.max_heap[0])) / 2 + + +x = MedianStream() + +num = input() + +while num != 'q': + if num == 's': + print('Stream of integers - ', x.stream) + else: + num = int(num) + x.add_number(num) + print('Median - ', x.curr_median) + + num = input() From b6bf29a82abbdda4c4cd1a62ec80cf147780f25c Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 8 Jun 2021 09:13:03 +0530 Subject: [PATCH 077/117] add heap questions --- .../heap/kth_largest_element_in_stream.py | 61 +++++++++++++++++++ data_structures/heap/sum_elements_range.py | 24 ++++++++ 2 files changed, 85 insertions(+) create mode 100644 data_structures/heap/kth_largest_element_in_stream.py create mode 100644 data_structures/heap/sum_elements_range.py diff --git a/data_structures/heap/kth_largest_element_in_stream.py b/data_structures/heap/kth_largest_element_in_stream.py new file mode 100644 index 00000000..dfc278e4 --- /dev/null +++ b/data_structures/heap/kth_largest_element_in_stream.py @@ -0,0 +1,61 @@ +""" +Use a priority queue - min heap + +as soon as the stream reaches a length of K, start finding the number + +Since we are using a priority queue (min heap), the minimum number will be +at the first index. O(1) time to extract it + +We have to make sure that the length of the stream does not go above K because +we to find the kth largest element which in terms of this heap means the smallest +element. For example lets say we have K = 4. So as soon as the stream reaches a +length of 4, we start to find the 4th largest number. Now as we are maintaining the +length of the array at 4, 4th largest number will mean the smallest number. Using this +we are designing the program. + +If the number entered in the stream is less than the current min, we dont take it as it +wont affect the result. +""" + +from heapq import heapify, heappop, heappush + +class Stream: + + def __init__(self, k): + self.heap = [] + self.stream = [] + self.k = k + self.curr_min = None + heapify(self.heap) + + + def insert(self, x): + self.stream.append(x) + + if len(self.heap) < self.k: # when the heap is empty or size is less than K + heappush(self.heap, x) + self.curr_min = self.heap[0] + else: + if x > self.curr_min: + heappop(self.heap) # remove the curr min element + heappush(self.heap, x) # insert x + self.curr_min = self.heap[0] + + + def find_kth_max(self): + if len(self.heap) == self.k: + print(f'{self.k}th max number - {self.heap[0]}') + +k = 3 +x = Stream(k) + +num = input() + +while num != 'q': + if num == 's': + print(f'Stream - {x.stream} | K - {k}') + else: + num = int(num) + x.insert(num) + x.find_kth_max() + num = input() diff --git a/data_structures/heap/sum_elements_range.py b/data_structures/heap/sum_elements_range.py new file mode 100644 index 00000000..4a3b6645 --- /dev/null +++ b/data_structures/heap/sum_elements_range.py @@ -0,0 +1,24 @@ +""" +Find the sum of elements between k1th and k2th smallest elements +""" + +from heapq import heappush, heapify, heappop + +heap = [20, 8, 22, 4, 12, 10, 14] +k1 = 3 +k2 = 6 + +heapify(heap) + +# extracting min k1 times + +for i in range(k1): + heappop(heap) + +# now do extract min k2 - (k1 + 1) times +s = 0 + +for i in range(k2 - k1 - 1): + s += heappop(heap) + +print(s) From 65e797eb93624988b9d58e74c5af0f11530d4389 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 8 Jun 2021 10:06:42 +0530 Subject: [PATCH 078/117] new question --- ...s_that_can_searched_using_binary_search.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 data_structures/array/number_of_elements_that_can_searched_using_binary_search.py diff --git a/data_structures/array/number_of_elements_that_can_searched_using_binary_search.py b/data_structures/array/number_of_elements_that_can_searched_using_binary_search.py new file mode 100644 index 00000000..b1889522 --- /dev/null +++ b/data_structures/array/number_of_elements_that_can_searched_using_binary_search.py @@ -0,0 +1,52 @@ +""" +In an input of unsorted integer array, find the number of elements +that can be searched using binary search + +The idea is the an element is binary searchable if the elements to the +left of it are smaller than it and the elements to the right of it +are bigger than it + +So maintain two arrays - left_max and right_min such that in i'th index - + +* left_max[i] contains the max element between 0 and i-1 (left to right movement) +* right_min[i] contains the min element between n-1 and i+1 (right to left movement) + +Now for every element in the array, if its index its i, then it is binary searchable +if left_max[i] < arr[i] < right_min[i] +""" +import sys + +def get_searchable_numbers(arr, n): + left_max = [None] * n + right_min = [None] * n + + left_max[0] = float('-inf') + right_min[n-1] = float('inf') + + for i in range(1, n): + left_max[i] = max(left_max[i-1], arr[i-1]) + + for i in range(len(arr) - 2, -1, -1): + right_min[i] = min(right_min[i+1], arr[i+1]) + + res = [] + count = 0 + + for i in range(0, n): + num = arr[i] + left = left_max[i] + right = right_min[i] + + if left < num < right: + res.append(num) + count += 1 + + return count, res + + +if __name__ == '__main__': + #arr = [5,1,4,3,6,8,10,7,9] + arr = [4,1,3,9,8,10,11] + count, res = get_searchable_numbers(arr, len(arr)) + + print(count, res) From 179ef490f8c233445a78b1d2de8a5c9d471b0055 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Wed, 9 Jun 2021 20:53:59 +0530 Subject: [PATCH 079/117] add hash --- .../longest_subarray_sum_divisible_by_k.py | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 data_structures/hash/longest_subarray_sum_divisible_by_k.py diff --git a/data_structures/hash/longest_subarray_sum_divisible_by_k.py b/data_structures/hash/longest_subarray_sum_divisible_by_k.py new file mode 100644 index 00000000..4d691f59 --- /dev/null +++ b/data_structures/hash/longest_subarray_sum_divisible_by_k.py @@ -0,0 +1,55 @@ +""" +Find the longest subarray in an array whose sum is ` +divisible by k + +source - https://www.geeksforgeeks.org/longest-subarray-sum-divisible-k/ + +The idea is that we create a new array mod_arr where we mod_arr[i] = +sum(arr[0]...arr[i]) % k. So basically this array tells us that upto this +point in the input array, if we take sum of numbers till index i, that sum will +be divisible by k + +We will be creating a hash table for this to store the mod results + +Now, lets say x = sum(arr[0]...arr[i]) % k = mod_arr[i]. If + +1. if we find x == 0, increment length by 1 +2. if x not in hash, create it and store (x, index of x) +3. if x in hash: + this tells us that upto this point, where the remainder of sum of numbers + till this point divided by k is x, that remainder we already saw before as it + exists in the hash. So if we ignore the first dont consider the first occurence of x + and remove that from the sum, then this sum will be divisible by k (because subtracting remainder + from a number makes it divisible). + Now find the max length of such case as + if length = max(length, (i - index(x)) +""" + +def find_length(arr, k): + hash_table = {} + mod_arr = [] + s = 0 + length = 0 + start, end = 0, 0 + + for i in range(0, len(arr)): + s += arr[i] + mod_arr.append(s % k) + + for i in range(0, len(mod_arr)): + if mod_arr[i] == 0: + length += 1 + else: + if mod_arr[i] not in hash_table: + hash_table[mod_arr[i]] = i + else: + if length < (i - mod_arr[i]): + length = i - mod_arr[i] + start = mod_arr[i] + end = i - 1 # i-1 because the current number is not to considered as it makes the sum not divisible by k + + return length, arr[start:end+1] + + +arr = [ 2, 7, 6, 1, 4, 5 ] +print(find_length(arr, 3)) From 45a32a8822c421cf58bb75fb6324da0fae69174c Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 10 Jun 2021 08:01:04 +0530 Subject: [PATCH 080/117] Add longest consecutuve subsequence --- .../hash/longest_consecutive_subsequence.py | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 data_structures/hash/longest_consecutive_subsequence.py diff --git a/data_structures/hash/longest_consecutive_subsequence.py b/data_structures/hash/longest_consecutive_subsequence.py new file mode 100644 index 00000000..742ef4b7 --- /dev/null +++ b/data_structures/hash/longest_consecutive_subsequence.py @@ -0,0 +1,45 @@ +""" +Given an array of integers, find the length of the longest sub-sequence +such that elements in the subsequence are consecutive integers, the +consecutive numbers can be in any order. + +The idea is to store all the elements in a set first. Then as we are iterating +over the array, we check two things - +1. a number x can be a starting number in a sequence if x-1 is not present in the +set. If this is the case, create a loop and check how many elements from x to x+j are +in the set +2. if x -1 is there in the set, do nothing as this number is not a starting element +and must have been considered in a different sequence +""" + +def find_seq(arr, n): + s = set() + + for num in arr: + s.add(num) + + ans = 0 + elements = [] + + for i in range(n): + temp = [] + + if arr[i] - 1 not in s: + j = arr[i] + + while j in s: + temp.append(j) + j += 1 + + if j - arr[i] > ans: + ans = j - arr[i] + elements = temp.copy() + + return ans, elements + + +arr = [36, 41, 56, 35, 44, 33, 34, 92, 43, 32, 42] + +ans, elements = find_seq(arr, len(arr)) +print('Length - ', ans) +print('Elements - ', elements) From e0cffcfdefa4af9ad4689d3bcd6972842f75ea7d Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 10 Jun 2021 09:18:16 +0530 Subject: [PATCH 081/117] moved code around --- .../longest_consecutive_subsequence.py | 0 ...gest_increasing_consecutive_subsequence.py | 26 +++++++++++++++++++ .../longest_subarray_sum_divisible_by_k.py | 0 data_structures/hash/hash_table.py | 0 4 files changed, 26 insertions(+) rename {data_structures/hash => algorithms/dynamic_programming}/longest_consecutive_subsequence.py (100%) create mode 100644 algorithms/dynamic_programming/longest_increasing_consecutive_subsequence.py rename {data_structures/hash => algorithms/dynamic_programming}/longest_subarray_sum_divisible_by_k.py (100%) create mode 100644 data_structures/hash/hash_table.py diff --git a/data_structures/hash/longest_consecutive_subsequence.py b/algorithms/dynamic_programming/longest_consecutive_subsequence.py similarity index 100% rename from data_structures/hash/longest_consecutive_subsequence.py rename to algorithms/dynamic_programming/longest_consecutive_subsequence.py diff --git a/algorithms/dynamic_programming/longest_increasing_consecutive_subsequence.py b/algorithms/dynamic_programming/longest_increasing_consecutive_subsequence.py new file mode 100644 index 00000000..c272820c --- /dev/null +++ b/algorithms/dynamic_programming/longest_increasing_consecutive_subsequence.py @@ -0,0 +1,26 @@ +""" +Find the longest increasing consecutive subsequence in an array + +Idea - + +create a dictionary 'seq' and start iterating over the array + +1. if arr[i] - 1 exists in the array, length = length + seq[arr[i] - 1] +2. else, seq[i] = 1 +""" + +def find_seq(arr): + seq = {} + count = 0 + + for num in arr: + if num - 1 in seq: + seq[num] = seq[num - 1] + 1 + count = max(count, seq[num]) + else: + seq[num] = 1 + + return count + +arr = [6, 7, 8, 3, 4, 5, 9, 10] +print(find_seq(arr)) diff --git a/data_structures/hash/longest_subarray_sum_divisible_by_k.py b/algorithms/dynamic_programming/longest_subarray_sum_divisible_by_k.py similarity index 100% rename from data_structures/hash/longest_subarray_sum_divisible_by_k.py rename to algorithms/dynamic_programming/longest_subarray_sum_divisible_by_k.py diff --git a/data_structures/hash/hash_table.py b/data_structures/hash/hash_table.py new file mode 100644 index 00000000..e69de29b From 246f5272eddfe72317dcbfe4fb031654b59ca2c0 Mon Sep 17 00:00:00 2001 From: pratikscfr Date: Thu, 10 Jun 2021 11:50:34 +0530 Subject: [PATCH 082/117] Added Partition Sum Problem --- .../dynamic_programming/partition_sum.py | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 algorithms/dynamic_programming/partition_sum.py diff --git a/algorithms/dynamic_programming/partition_sum.py b/algorithms/dynamic_programming/partition_sum.py new file mode 100644 index 00000000..ff8cddcf --- /dev/null +++ b/algorithms/dynamic_programming/partition_sum.py @@ -0,0 +1,46 @@ +# A Dynamic Programming based +# Python3 program to partition problem + +# Returns true if arr[] can be partitioned +# in two subsets of equal sum, otherwise false +def findPartiion(arr, n) : + Sum = 0 + + # Calculate sum of all elements + for i in range(n) : + Sum += arr[i] + if (Sum % 2 != 0) : + return 0 + part = [0] * ((Sum // 2) + 1) + + # Initialize the part array as 0 + for i in range((Sum // 2) + 1) : + part[i] = 0 + + # Fill the partition table in bottom up manner + for i in range(n) : + + # the element to be included + # in the sum cannot be + # greater than the sum + for j in range(Sum // 2, arr[i] - 1, -1) : + + # check if sum - arr[i] + # could be formed + # from a subset + # using elements + # before index i + if (part[j - arr[i]] == 1 or j == arr[i]) : + part[j] = 1 + + return part[Sum // 2] + +# Drive code +arr = [ 1, 3, 3, 2, 3, 2 ] +n = len(arr) + +# Function call +if (findPartiion(arr, n) == 1) : + print("Can be divided into two subsets of equal sum") +else : + print("Can not be divided into two subsets of equal sum") From 2c7103f5170e7139f1ae59023d190d04ec09fa72 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 10 Jun 2021 18:00:57 +0530 Subject: [PATCH 083/117] Add file for hash table --- data_structures/hash/hash_table.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/data_structures/hash/hash_table.py b/data_structures/hash/hash_table.py index e69de29b..c1c05ced 100644 --- a/data_structures/hash/hash_table.py +++ b/data_structures/hash/hash_table.py @@ -0,0 +1,29 @@ +""" +Create a hash table from scratch. Use chaining for hash collision +""" + +class HashTable: + + + def __init__(self): + self.hash_table = + + + def check_collision(self): + pass + + + def add_to_linked_list(self): + pass + + + def insert(self): + pass + + + def delete(self): + pass + + + def get(self): + pass From 800eb1d1c48c9d71164bcbb0875d36ff6d81e516 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Fri, 11 Jun 2021 09:28:28 +0530 Subject: [PATCH 084/117] Fix issues and bugs and add comments --- .../graphs/all_paths_between_two_vertices.py | 2 +- data_structures/graphs/bellman_ford.py | 12 ++-- ...n_directed_graph_using_colors_recursive.py | 58 +++++++++++++++++ data_structures/graphs/kosaraju_algorithm.py | 3 +- .../max_edges_that_can_be_added_to_dag.py | 4 +- data_structures/graphs/mother_vertex.py | 10 +-- .../graphs/root_which_gives_min_height.py | 12 ++-- data_structures/graphs/same_path.py | 14 ++-- data_structures/graphs/same_path_recursive.py | 65 +++++++++++++++++++ 9 files changed, 155 insertions(+), 25 deletions(-) create mode 100644 data_structures/graphs/cycle_in_directed_graph_using_colors_recursive.py create mode 100644 data_structures/graphs/same_path_recursive.py diff --git a/data_structures/graphs/all_paths_between_two_vertices.py b/data_structures/graphs/all_paths_between_two_vertices.py index 958a2b82..3632d36e 100644 --- a/data_structures/graphs/all_paths_between_two_vertices.py +++ b/data_structures/graphs/all_paths_between_two_vertices.py @@ -1,5 +1,5 @@ # Use backtracking -# The only with this approach is that if there is a cycle, then +# The only poroblem with this approach is that if there is a cycle, then # it can show infinitely many paths # Reference - https://www.geeksforgeeks.org/count-possible-paths-two-vertices/ diff --git a/data_structures/graphs/bellman_ford.py b/data_structures/graphs/bellman_ford.py index 35c6522d..2901613e 100644 --- a/data_structures/graphs/bellman_ford.py +++ b/data_structures/graphs/bellman_ford.py @@ -55,12 +55,14 @@ def bellman_ford(self, source, destination): parent[source] = source distance[source] = source - for (u, v) in self.edges: - wt = self.edges[(u, v)].weight + for i in range(self.vertices - 1): # Doing V - 1 times to find shortest distance - if distance[v] > distance[u] + wt: - distance[v] = distance[u] + wt - parent[v] = u + for (u, v) in self.edges: + wt = self.edges[(u, v)].weight + + if distance[v] > distance[u] + wt: + distance[v] = distance[u] + wt + parent[v] = u # Now for the Vth iteration, check for the negative cycle diff --git a/data_structures/graphs/cycle_in_directed_graph_using_colors_recursive.py b/data_structures/graphs/cycle_in_directed_graph_using_colors_recursive.py new file mode 100644 index 00000000..c4a687ed --- /dev/null +++ b/data_structures/graphs/cycle_in_directed_graph_using_colors_recursive.py @@ -0,0 +1,58 @@ +""" +Using three colors - white, gray and black +White - vertices that are not processed (inital state of all vertices) +Gray - vertices that are in DFS +Black - fully traversed vertices (i.e its progenies are also done) + +If while traversing any adjacent node is colored Gray, that means cycle exists +""" + +from collections import defaultdict + +class Graph: + + + def __init__(self, vertices): + self.graph = defaultdict(list) + self.vertices = vertices + + + def add_edge(self, u, v): + self.graph[u].append(v) + + + def dfs(self, vertex, colors): + colors[vertex] = 'Gray' + + for v in self.graph[vertex]: + + if colors[v] == 'Gray': + return True + + elif colors[v] == 'White' and self.dfs(v, colors) == True: + return True + + colors[vertex] = 'Black' + return False + + + def is_cyclic(self): + colors = ['White'] * self.vertices + + for vertex in self.graph.keys(): + if colors[vertex] == 'White': + if self.dfs(vertex, colors) == True: + return True + + return False + + +g = Graph(4) +g.add_edge(0, 1) +g.add_edge(0, 2) +g.add_edge(1, 2) +g.add_edge(2, 0) +g.add_edge(2, 3) +g.add_edge(3, 3) + +print(g.is_cyclic()) \ No newline at end of file diff --git a/data_structures/graphs/kosaraju_algorithm.py b/data_structures/graphs/kosaraju_algorithm.py index cd446c1a..99277672 100644 --- a/data_structures/graphs/kosaraju_algorithm.py +++ b/data_structures/graphs/kosaraju_algorithm.py @@ -14,6 +14,7 @@ class Graph: + def __init__(self, vertices): self.vertices = vertices self.graph = defaultdict(list) @@ -63,8 +64,6 @@ def kosaraju(self): tgraph = self.create_tranpose() visited = [False] * self.vertices - print('stack - ', stack) - while stack: s = stack.pop() diff --git a/data_structures/graphs/max_edges_that_can_be_added_to_dag.py b/data_structures/graphs/max_edges_that_can_be_added_to_dag.py index acad5380..f4ba0900 100644 --- a/data_structures/graphs/max_edges_that_can_be_added_to_dag.py +++ b/data_structures/graphs/max_edges_that_can_be_added_to_dag.py @@ -11,6 +11,8 @@ 2. If edge is not there left to right, create the edge 3. Count the number of edges added +source - https://www.geeksforgeeks.org/maximum-edges-can-added-dag-remains-dag/ + """ from collections import defaultdict @@ -53,7 +55,7 @@ def max_edges(self): visited = [False] * self.vertices count = 0 - for i in topo: + for i in range(len(topo)): vertex = topo[i] # Mark the connected vertices visited for j in self.graph[vertex]: diff --git a/data_structures/graphs/mother_vertex.py b/data_structures/graphs/mother_vertex.py index 40f8ac10..794c88ee 100644 --- a/data_structures/graphs/mother_vertex.py +++ b/data_structures/graphs/mother_vertex.py @@ -1,9 +1,11 @@ -# A mother vertex is a vertex such that all other vertices -# can be reached by a path from this vertex +""" +A mother vertex is a vertex such that all other vertices +can be reached by a path from this vertex -# Reference - https://www.geeksforgeeks.org/find-a-mother-vertex-in-a-graph/ +Reference - https://www.geeksforgeeks.org/find-a-mother-vertex-in-a-graph/ -# Time complexity - 2 * O(V + E) = O(V + E) +Time complexity - 2 * O(V + E) = O(V + E) +""" from collections import defaultdict diff --git a/data_structures/graphs/root_which_gives_min_height.py b/data_structures/graphs/root_which_gives_min_height.py index 77a738a9..7a079196 100644 --- a/data_structures/graphs/root_which_gives_min_height.py +++ b/data_structures/graphs/root_which_gives_min_height.py @@ -1,4 +1,7 @@ -# Reference - https://www.geeksforgeeks.org/roots-tree-gives-minimum-height/ +""" +Find the node in an undirected graph which gives the minimum height +Reference - https://www.geeksforgeeks.org/roots-tree-gives-minimum-height/ +""" from collections import defaultdict from queue import Queue @@ -22,10 +25,10 @@ def root_min_height(self): q = Queue() for i in range(self.V): - if self.degree[i] == 1: + if self.degree[i] == 1: # To identify leaf nodes q.put(i) - - + + # now move inwards from the leaf node while self.V > 2: for i in range(q.qsize()): t = q.get() @@ -38,7 +41,6 @@ def root_min_height(self): if self.degree[j] == 1: q.put(j) - res = list() while q.qsize() > 0: res.append(q.get()) diff --git a/data_structures/graphs/same_path.py b/data_structures/graphs/same_path.py index 127c1b86..82219691 100644 --- a/data_structures/graphs/same_path.py +++ b/data_structures/graphs/same_path.py @@ -1,8 +1,10 @@ -# Check if two nodes are on the same path in a tree. Use DFS and the concept of intime and outtime. -# Intime - time when a node is visited for the first time -# Outtime - time when a node is visited for the second time after all its children have been visited -# For any pair of node if they are on the same path - -# intime[u] < intime[v] and outtime[u] > outtime[v] +""" +Check if two nodes are on the same path in a tree. Use DFS and the concept of intime and outtime. +Intime - time when a node is visited for the first time +Outtime - time when a node is visited for the second time after all its children have been visited +For any pair of node if they are on the same path - +intime[u] < intime[v] and outtime[u] > outtime[v] +""" from collections import defaultdict @@ -40,8 +42,6 @@ def dfs(self): timer += 1 intime[s] = timer - print(s) - for i in self.graph[s]: if visited[i] == False: stack.append(i) diff --git a/data_structures/graphs/same_path_recursive.py b/data_structures/graphs/same_path_recursive.py new file mode 100644 index 00000000..3b2087aa --- /dev/null +++ b/data_structures/graphs/same_path_recursive.py @@ -0,0 +1,65 @@ +""" +Check if two nodes are on the same path in a undirected graph. Use DFS and the concept of intime and outtime. +Intime - time when a node is visited for the first time +Outtime - time when a node is visited for the second time after all its children have been visited +For any pair of node if they are on the same path - +intime[u] < intime[v] and outtime[u] > outtime[v] +""" + +from collections import defaultdict + +class Graph: + + def __init__(self, vertices): + self.graph = defaultdict(list) + self.vertices = vertices + + + def add_edge(self, u, v): + self.graph[u].append(v) + self.graph[v].append(u) + + + def dfs(self, vertex, intime, outtime, timer, visited): + visited[vertex] = True + timer += 1 + intime[vertex] = timer + + for v in self.graph[vertex]: + if visited[v] == False: + self.dfs(v, intime, outtime, timer, visited) + + timer += 1 + outtime[vertex] = timer + + + def on_same_path(self, u, v): + intime = [-1] * self.vertices + outtime = [-1] * self.vertices + timer = 0 + visited = [False] * self.vertices + + for vertex in self.graph: + if visited[vertex] == False: + self.dfs(vertex, intime, outtime, timer, visited) + + + if (intime[u] < intime[v] and outtime[u] > outtime[v]) \ + or (intime[v] < intime[u] and outtime[v] > outtime[u]): + return True + return False + + +g = Graph(9) +g.add_edge(0, 1) +g.add_edge(0, 2) +g.add_edge(2, 5) +g.add_edge(1, 3) +g.add_edge(1, 4) +g.add_edge(4, 6) +g.add_edge(4, 7) +g.add_edge(4, 8) + +print(g.on_same_path(0, 4)) +print(g.on_same_path(1, 8)) +print(g.on_same_path(1, 5)) \ No newline at end of file From e05e57e703bd0df915f72a6d8d51842950d7a5ae Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Sat, 12 Jun 2021 15:59:12 +0530 Subject: [PATCH 085/117] added new question --- ...subarray_with_no_pairsum_divisible_by_k.py | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 algorithms/dynamic_programming/longest_subarray_with_no_pairsum_divisible_by_k.py diff --git a/algorithms/dynamic_programming/longest_subarray_with_no_pairsum_divisible_by_k.py b/algorithms/dynamic_programming/longest_subarray_with_no_pairsum_divisible_by_k.py new file mode 100644 index 00000000..844b5611 --- /dev/null +++ b/algorithms/dynamic_programming/longest_subarray_with_no_pairsum_divisible_by_k.py @@ -0,0 +1,54 @@ +""" +Find the longest subarray in the input array such that the pairwise sum of +the elements of this subarray is not divisible by K + +The idea is - +How can we tell that two numbers x and y will make a pairsum that will be +divisible by K just by looking at their remainders? There can be two conditions + +1. It will be only possible if the sum of the remainders when x and y are +divided by K is equal to K. As the sum of the remainders cannot exceed K +so if it reaches K then it means that the sum of those numbers will also be +divisible by K +0 < (X%K) + (Y%K) <= K + +2. If arr[i] % k == 0 and there is also an element j such that arr[j] % k == 0 +and 0 exists in the hash (i.e hash[j] = True) +""" + +def find_subarray(arr, k): + """ + True means divisible by k + """ + start, end = 0, 0 + max_start, max_end = 0, 0 + + n = len(arr) + mod_arr = [0] * n + + mod_arr[arr[0] % k] = mod_arr[arr[0] % k] + 1 + + for i in range(1, n): + mod = arr[i] % k + + while (mod_arr[k - mod] != 0) or (mod == 0 and mod_arr[mod] != 0): + mod_arr[arr[start] % k] = mod_arr[arr[start] % k] - 1 + start += 1 + + mod_arr[mod] = mod_arr[mod] + 1 + end += 1 + + if (end - start) > (max_end - max_start): + max_end = end + max_start = start + + print(f'Max size is {max_end - max_start}') + + for i in (max_start, max_end + 1): + print(arr[i], end=" ") + + +arr = [3, 7, 1, 9, 2] +k = 3 +find_subarray(arr, k) + From a5b56bbf76075c6b4fd19becaefe4c978d84e5d9 Mon Sep 17 00:00:00 2001 From: pratikscfr Date: Sun, 13 Jun 2021 21:04:08 +0530 Subject: [PATCH 086/117] changed casing --- .../dynamic_programming/partition_sum.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/algorithms/dynamic_programming/partition_sum.py b/algorithms/dynamic_programming/partition_sum.py index ff8cddcf..c07b79f6 100644 --- a/algorithms/dynamic_programming/partition_sum.py +++ b/algorithms/dynamic_programming/partition_sum.py @@ -3,18 +3,18 @@ # Returns true if arr[] can be partitioned # in two subsets of equal sum, otherwise false -def findPartiion(arr, n) : - Sum = 0 +def find_partiion(arr, n) : + sum = 0 # Calculate sum of all elements for i in range(n) : - Sum += arr[i] - if (Sum % 2 != 0) : + sum += arr[i] + if (sum % 2 != 0) : return 0 - part = [0] * ((Sum // 2) + 1) + part = [0] * ((sum // 2) + 1) # Initialize the part array as 0 - for i in range((Sum // 2) + 1) : + for i in range((sum // 2) + 1) : part[i] = 0 # Fill the partition table in bottom up manner @@ -23,7 +23,7 @@ def findPartiion(arr, n) : # the element to be included # in the sum cannot be # greater than the sum - for j in range(Sum // 2, arr[i] - 1, -1) : + for j in range(sum // 2, arr[i] - 1, -1) : # check if sum - arr[i] # could be formed @@ -33,14 +33,14 @@ def findPartiion(arr, n) : if (part[j - arr[i]] == 1 or j == arr[i]) : part[j] = 1 - return part[Sum // 2] + return part[sum // 2] # Drive code arr = [ 1, 3, 3, 2, 3, 2 ] n = len(arr) # Function call -if (findPartiion(arr, n) == 1) : +if (find_partiion(arr, n) == 1) : print("Can be divided into two subsets of equal sum") else : print("Can not be divided into two subsets of equal sum") From 13621d82012fcbdea4420eea1945ce35e65984fb Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 15 Jun 2021 08:48:22 +0530 Subject: [PATCH 087/117] Cleaned code --- algorithms/greedy/__init__.py | 1 - algorithms/greedy/activity_selection.py | 31 ++++++++++++++++++------- algorithms/greedy/index.md | 1 - 3 files changed, 23 insertions(+), 10 deletions(-) delete mode 100644 algorithms/greedy/__init__.py delete mode 100644 algorithms/greedy/index.md diff --git a/algorithms/greedy/__init__.py b/algorithms/greedy/__init__.py deleted file mode 100644 index 8b137891..00000000 --- a/algorithms/greedy/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/algorithms/greedy/activity_selection.py b/algorithms/greedy/activity_selection.py index 8dbd822f..374ad563 100644 --- a/algorithms/greedy/activity_selection.py +++ b/algorithms/greedy/activity_selection.py @@ -4,15 +4,30 @@ #s[]--> An array that contains start time of all activities #f[] --> An array that contains finish time of all activities -def print_max_activities(s, f): - n = len(f) - # the first activity is always selected +def find_activities(arr): + n = len(arr) + selected = [] + + arr.sort(key = lambda x: x[1]) + i = 0 - print(i, end=' ') - # for the rest - for j in range(n): - if s[j] >= f[i]: - print(j, end=' ') + # since it is a greedy algorithm, the first acitivity is always + # selected because it is the most optimal choice at that point + selected.append(arr[i]) + + for j in range(1, n): + start_time_next_activity = arr[j][0] + end_time_prev_activity = arr[i][1] + + if start_time_next_activity >= end_time_prev_activity: + selected.append(arr[j]) i = j + + return selected + + +arr = [[5, 9], [1, 2], [3, 4], [0, 6],[5, 7], [8, 9]] +print(find_activities(arr)) + diff --git a/algorithms/greedy/index.md b/algorithms/greedy/index.md deleted file mode 100644 index d9b79484..00000000 --- a/algorithms/greedy/index.md +++ /dev/null @@ -1 +0,0 @@ -# Index of Greedy \ No newline at end of file From ede7c4dd1babe732e8dac3c5121e2c60b28d28a8 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 15 Jun 2021 10:20:48 +0530 Subject: [PATCH 088/117] Add questions --- algorithms/dynamic_programming/coin_change.py | 39 +++++++++++++------ algorithms/greedy/min_platforms.py | 37 ++++++++++++++++++ 2 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 algorithms/greedy/min_platforms.py diff --git a/algorithms/dynamic_programming/coin_change.py b/algorithms/dynamic_programming/coin_change.py index 96b57648..7b9cbe26 100644 --- a/algorithms/dynamic_programming/coin_change.py +++ b/algorithms/dynamic_programming/coin_change.py @@ -1,16 +1,33 @@ -# Concept is almost same as 01 Knapsack Problem -def min_coin(coins, total): +def min_coins(coins, total): cols = total + 1 - rows = len(coins) - t = [[[0] if col == 0 else float('inf') for col in range(cols)] for i in range(rows)] + min_coins = [float('inf')] * (total + 1) + coins_used = [-1] * (total + 1) - for i in range(rows): - for j in range(1, cols): - if j < coins[i]: - t[i][j] = t[i-1][j] - else: - t[i][j] = min(t[i-1][j], 1 + t[i][j-coins[i]]) + min_coins[0] = 0 # to form 0, we need 0 coins - return t[rows-1][cols-1] + for i in range(0, len(coins)): + for j in range(1, len(min_coins)): + if coins[i] > j: # if the coin value is more than j (curr total), ignore it + continue + + if (1 + min_coins[j - coins[i]]) < min_coins[j]: + min_coins[j] = 1 + min_coins[j - coins[i]] + coins_used[j] = i + + # finding which coins were used + picked_coins = [] + while total > 0: + index_of_coin_used = coins_used[total] + coin = coins[index_of_coin_used] + picked_coins.append(coin) + total -= coin + + print('Min coins needed - ', min_coins[-1]) + print('Coins used - ', picked_coins) + +total = 11 +coins = [9, 6, 5, 1] + +min_coins(coins, total) diff --git a/algorithms/greedy/min_platforms.py b/algorithms/greedy/min_platforms.py new file mode 100644 index 00000000..fd372f0a --- /dev/null +++ b/algorithms/greedy/min_platforms.py @@ -0,0 +1,37 @@ +""" +Given the arrival and departure times of buses at a station +find the min number of platforms that must be there +""" + + +def find_platforms(arrival, departure): + n = len(arrival) + + arrival.sort() + departure.sort() + + i = 1 + j = 0 + + ans = 1 # atleast one platform is required + plat = 1 + + while i < n and j < n: + if arrival[i] <= departure[j]: + plat += 1 + i += 1 + + elif arrival[i] > departure[j]: + plat -= 1 + j += 1 + + ans = max(ans, plat) + + + return ans + + +arr = [900, 940, 950, 1100, 1500, 1800] +dep = [910, 1200, 1120, 1130, 1900, 2000] + +print(find_platforms(arr, dep)) From 3c2597ffa0113acb5877d77baab21b8f0a2c3d3a Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Wed, 16 Jun 2021 08:40:40 +0530 Subject: [PATCH 089/117] Added cost of tiling --- algorithms/greedy/cost_of_tiles.py | 45 ++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 algorithms/greedy/cost_of_tiles.py diff --git a/algorithms/greedy/cost_of_tiles.py b/algorithms/greedy/cost_of_tiles.py new file mode 100644 index 00000000..05fde8da --- /dev/null +++ b/algorithms/greedy/cost_of_tiles.py @@ -0,0 +1,45 @@ +""" +Find the min cost of tiles to cover a floor. +Floor is represented by 2D array where - +* = tile already placed +. = no tile + +tiles available are 1*1 and 1*2 and their costs +are A and B + +Source - https://www.geeksforgeeks.org/minimize-cost-to-cover-floor-using-tiles-of-dimensions-11-and-12/ +""" + +def cost(arr, A, B): + n = len(arr) + m = len(arr[0]) + + ans = 0 + + for i in range(n): + j = 0 + + while j < m: + if arr[i][j] == '*': # tile is already there + j += 1 + continue + + if j == m - 1: # if j is pointing to last tile, you can use only 1*1 tile + ans += A + else: + if arr[i][j+1] == '.': + ans += min(2 * A, B) + j += 1 + else: + ans += A + + j += 1 + + print('Cost of tiling is - ', ans) + +arr = [ [ '.', '.', '*' ], + [ '.', '*', '*' ] ] + +A, B = 2, 10 + +cost(arr, A, B) From 1342be014357df0816f9e3b105ebadeff5d94323 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Sun, 20 Jun 2021 15:48:33 +0530 Subject: [PATCH 090/117] Remove graph --- algorithms/graph/bfs.py | 49 ---------------- algorithms/graph/dfs.py | 46 --------------- algorithms/graph/dijkstra.py | 55 ------------------ algorithms/graph/find_all_paths.py | 32 ----------- algorithms/graph/index.md | 4 -- algorithms/graph/mst.py | 83 ---------------------------- algorithms/graph/topological_sort.py | 33 ----------- 7 files changed, 302 deletions(-) delete mode 100644 algorithms/graph/bfs.py delete mode 100644 algorithms/graph/dfs.py delete mode 100644 algorithms/graph/dijkstra.py delete mode 100644 algorithms/graph/find_all_paths.py delete mode 100644 algorithms/graph/index.md delete mode 100644 algorithms/graph/mst.py delete mode 100644 algorithms/graph/topological_sort.py diff --git a/algorithms/graph/bfs.py b/algorithms/graph/bfs.py deleted file mode 100644 index 6c64f75f..00000000 --- a/algorithms/graph/bfs.py +++ /dev/null @@ -1,49 +0,0 @@ -# Python3 Program to print BFS traversal -# from a given source vertex. BFS(int s) -# traverses vertices reachable from s. -from collections import defaultdict - -# This class represents a directed graph -# using adjacency list representation -class Graph: - - # Constructor - def __init__(self): - - # default dictionary to store graph - self.graph = defaultdict(list) - - # function to add an edge to graph - def addEdge(self,u,v): - self.graph[u].append(v) - - # Function to print a BFS of graph - def BFS(self, s): - - # Mark all the vertices as not visited - visited = [False] * (len(self.graph)) - - # Create a queue for BFS - queue = [] - - # Mark the source node as - # visited and enqueue it - queue.append(s) - visited[s] = True - - while queue: - - # Dequeue a vertex from - # queue and print it - s = queue.pop(0) - print (s, end = " ") - - # Get all adjacent vertices of the - # dequeued vertex s. If a adjacent - # has not been visited, then mark it - # visited and enqueue it - for i in self.graph[s]: - if visited[i] == False: - queue.append(i) - visited[i] = True - \ No newline at end of file diff --git a/algorithms/graph/dfs.py b/algorithms/graph/dfs.py deleted file mode 100644 index 4a452536..00000000 --- a/algorithms/graph/dfs.py +++ /dev/null @@ -1,46 +0,0 @@ -# Python program to print DFS traversal for complete graph -from __future__ import print_function -from collections import defaultdict - -# This class represents a directed graph using adjacency -# list representation -class Graph: - - # Constructor - def __init__(self): - - # default dictionary to store graph - self.graph = defaultdict(list) - - # function to add an edge to graph - def addEdge(self,u,v): - self.graph[u].append(v) - - # A function used by DFS - def DFSUtil(self, v, visited): - - # Mark the current node as visited and print it - visited[v]= True - print(v, end=" ") - - # Recur for all the vertices adjacent to - # this vertex - for i in self.graph[v]: - if visited[i] == False: - self.DFSUtil(i, visited) - - - # The function to do DFS traversal. It uses - # recursive DFSUtil() - def DFS(self): - V = len(self.graph) #total vertices - - # Mark all the vertices as not visited - visited =[False]*(V) - - # Call the recursive helper function to print - # DFS traversal starting from all vertices one - # by one - for i in range(V): - if visited[i] == False: - self.DFSUtil(i, visited) diff --git a/algorithms/graph/dijkstra.py b/algorithms/graph/dijkstra.py deleted file mode 100644 index 91dbe362..00000000 --- a/algorithms/graph/dijkstra.py +++ /dev/null @@ -1,55 +0,0 @@ -# i checked your other py files and decided that i would go for default dict -# i used same structure as needed for this repository. -from collections import defaultdict -import sys -r = range -# min weight goes for 0 in this case -max_weight = sys.maxsize - - -class Graph: - # Class initializer - def __init__(self, vertices): - # num of vertices and our starting graph - self.vertices = vertices - - # Values will be [[]] two deminsial array with - # columns for start going to rows. - self.graph = [[0] * self.vertices for _ in r(self.vertices)] - self.visited = [0] * self.vertices # to control visited vertices - # for our distances to minimaze them. - self.distances = [max_weight] * self.vertices - - # Add edge to graph start --> end point with specific weight! - def add_edge(self, start, end, weight): - self.graph[start][end] = weight - - def print_dist(self, dist): - for _ in r(self.vertices): - print("vert ", _, "\tdist ", self.distances[_]) - - def dijkstra(self, end_point): - self.distances[end_point] = 0 - - for vert in r(self.vertices): - - # we need to check for minimum but not visited!! - my_min, min_index = max_weight, 0 - for _ in r(self.vertices): - if not self.visited[_]: - if my_min > self.distances[_]: - my_min = self.distances[_] - min_index = _ - - # check the flag for visited - self.visited[min_index] = 1 - - # # iterate and update if needed - for adj in r(self.vertices): - # check for edge and visited - val = self.graph[min_index][adj] - if not self.visited[adj] and val != 0: - # check if needed update - if self.distances[adj] > self.distances[min_index] + val: - self.distances[adj] = self.distances[min_index] + val - self.print_dist(self.distances) diff --git a/algorithms/graph/find_all_paths.py b/algorithms/graph/find_all_paths.py deleted file mode 100644 index 9152bf2a..00000000 --- a/algorithms/graph/find_all_paths.py +++ /dev/null @@ -1,32 +0,0 @@ -''' -find all the possible paths in a directed cyclic graph from -a start point to a end point. -''' - - - - -def find_all_paths(graph, start, end, path=[]): - path = path + [start] - if start == end: - return [path] - if start not in graph.keys(): - return [] - paths = [] - for node in graph[start]: - if node not in path: #to prevent cyclic rotations - newpaths = find_all_paths(graph, node, end, path) - #print(newpaths) - for newpath in newpaths: - paths.append(newpath) - return paths - - -graph={1:[2,4], - 2:[3], - 4:[5], - 3:[5] - } - -for i in graph.keys(): - print(i,'to 5',find_all_paths(graph,i,5)) diff --git a/algorithms/graph/index.md b/algorithms/graph/index.md deleted file mode 100644 index e5197adc..00000000 --- a/algorithms/graph/index.md +++ /dev/null @@ -1,4 +0,0 @@ -# Index of graph - -* dfs.py -* bfs.py diff --git a/algorithms/graph/mst.py b/algorithms/graph/mst.py deleted file mode 100644 index 930a2211..00000000 --- a/algorithms/graph/mst.py +++ /dev/null @@ -1,83 +0,0 @@ -# i checked your other py files and decided that i would go for default dict -# i used same structure as needed for this repository. -from collections import defaultdict -r = range - - -class Graph: - # Class initializer - def __init__(self, vertices): - # num of vertices and our starting graph - - self.vertices = vertices - # Values will be [start_point, end_point, weight] - self.graph = [] - - # Add edge to graph - def add_edge(self, start, end, weight): - value = [start, end, weight] - self.graph.append(value) - - # Simple search alghoritm - def search(self, parent_ranks, index): - if parent_ranks[index] != index: - return self.search(parent_ranks, parent_ranks[index]) - return index - - def union(self, ranks, parent_ranks, fir, sec): - fir, sec = self.search(parent_ranks, fir), self.search( - parent_ranks, sec) - - # 3 steps. ranks lower, higher, same - - if (ranks[fir] > ranks[sec]): - parent_ranks[sec] = fir - - elif (ranks[fir] < ranks[sec]): - parent_ranks[fir] = sec - - elif (ranks[fir] == ranks[sec]): - parent_ranks[sec] = fir - ranks[fir] += 1 - - # run mst alghoritm main part. - - def run_mst(self, ranks, parent_ranks, answer): - edge, index = 0, 0 - - while True: - if ((self.vertices - 1) <= edge): - break - - # Take value - value = self.graph[index] - - # check cycle - fir, sec = self.search(parent_ranks, value[0]), self.search( - parent_ranks, value[1]) - - if fir != sec: - edge += 1 # increase edge - - # append and union - answer.append(value) - self.union(ranks, parent_ranks, fir, sec) - - index += 1 - - def print_graph(self, answer): - for start, end, weight in answer: - print(f"{start} - {end} --> {weight}") - - # Main function for mst alghoritm - def MST(self): - # sort the graph - self.graph = sorted(self.graph, key=lambda item: item[2]) - - # For this alghoritm we need two array. - ranks = [0] * self.vertices - parent_ranks = [_ for _ in r(self.vertices)] - answer = [] - - self.run_mst(ranks, parent_ranks, answer) - self.print_graph(answer) diff --git a/algorithms/graph/topological_sort.py b/algorithms/graph/topological_sort.py deleted file mode 100644 index ea214d0d..00000000 --- a/algorithms/graph/topological_sort.py +++ /dev/null @@ -1,33 +0,0 @@ -from collections import defaultdict - - -def topological_sort(graph: dict) -> list: - """Provides the topologically sorted nodes of a graph in a list. Takes input as a dictionary, - where the key is a node and the value is a list of the nodes that the key is a source node for.""" - - # Keeps track of the "degree" of a node; once this reaches 0, we push it onto the output. - leading_in = defaultdict(lambda: 0) - - for key, values in graph.items(): - if key not in leading_in.keys(): - leading_in[key] = 0 - for node in values: - leading_in[node] += 1 - - queue = [] - output = [] - - for node, degree in leading_in.items(): - if degree == 0: - queue.append(node) - output.append(node) - - while queue: - node = queue.pop(0) - for destination in graph.get(node, []): - leading_in[destination] -= 1 - if leading_in[destination] == 0: - queue.append(destination) - output.append(destination) - - return output From c68b97f6e3622ecffa2806a4fde99c82f9cd079a Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Wed, 23 Jun 2021 08:14:30 +0530 Subject: [PATCH 091/117] Remove index files --- data_structures/circular_linked_list/index.md | 5 ----- data_structures/queue/index.md | 3 --- 2 files changed, 8 deletions(-) delete mode 100644 data_structures/circular_linked_list/index.md delete mode 100644 data_structures/queue/index.md diff --git a/data_structures/circular_linked_list/index.md b/data_structures/circular_linked_list/index.md deleted file mode 100644 index a14e9993..00000000 --- a/data_structures/circular_linked_list/index.md +++ /dev/null @@ -1,5 +0,0 @@ -# Index of circular linked list - -* [Check Circular Linked List](check_circular_linked_list.py) -* [Delete](delete.py) -* [Traversal](traversal.py) diff --git a/data_structures/queue/index.md b/data_structures/queue/index.md deleted file mode 100644 index 8506ea63..00000000 --- a/data_structures/queue/index.md +++ /dev/null @@ -1,3 +0,0 @@ -# Index of Queue - -[Queue](queue.py) \ No newline at end of file From 6e222610c8537c476c61566d79ce945c3cda639e Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 24 Jun 2021 09:29:58 +0530 Subject: [PATCH 092/117] Remove index from graphs --- data_structures/graphs/index.md | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 data_structures/graphs/index.md diff --git a/data_structures/graphs/index.md b/data_structures/graphs/index.md deleted file mode 100644 index 7baba1cf..00000000 --- a/data_structures/graphs/index.md +++ /dev/null @@ -1,3 +0,0 @@ -# Index of graphs - -* [Adjacency List](adjacency_list.py) From 4dc760fd8d07df815c17b96a89372ef1909e7eea Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 1 Jul 2021 18:47:50 +0530 Subject: [PATCH 093/117] Add docstring --- data_structures/binary_trees/check_perfect_binary_tree.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/data_structures/binary_trees/check_perfect_binary_tree.py b/data_structures/binary_trees/check_perfect_binary_tree.py index c3f76281..5768717b 100644 --- a/data_structures/binary_trees/check_perfect_binary_tree.py +++ b/data_structures/binary_trees/check_perfect_binary_tree.py @@ -1,5 +1,7 @@ -# A binary tree is perfect if all the internal nodes have 2 children and -# all the leaves are at the same level +""" +A binary tree is perfect if all the internal nodes have 2 children and +all the leaves are at the same level +""" class Node: From 3322d66c59220c17ecf9f456fc49ae09e30f8133 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 1 Jul 2021 19:11:16 +0530 Subject: [PATCH 094/117] Bug fix --- data_structures/binary_trees/identical_trees.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data_structures/binary_trees/identical_trees.py b/data_structures/binary_trees/identical_trees.py index 1d81cef3..bacc0614 100644 --- a/data_structures/binary_trees/identical_trees.py +++ b/data_structures/binary_trees/identical_trees.py @@ -13,6 +13,6 @@ def identical(root1, root2): return True if root1 is not None and root2 is not None: - return root1.val == root2. val and identical(root1.left, root2.left) and identical(root1.right, root2.right) + return root1.val == root2.val and identical(root1.left, root2.left) and identical(root1.right, root2.right) return False From d9da92f29bd23163c069a58f4126d90e5bb2d0d7 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Fri, 2 Jul 2021 08:31:44 +0530 Subject: [PATCH 095/117] Remove print statement --- data_structures/graphs/bellman_ford.py | 1 - 1 file changed, 1 deletion(-) diff --git a/data_structures/graphs/bellman_ford.py b/data_structures/graphs/bellman_ford.py index 2901613e..49aabe8a 100644 --- a/data_structures/graphs/bellman_ford.py +++ b/data_structures/graphs/bellman_ford.py @@ -71,7 +71,6 @@ def bellman_ford(self, source, destination): for (u, v) in self.edges: wt = self.edges[(u, v)].weight if distance[v] > distance[u] + wt: - print('h - ', u, v) negative_cycle_present = True break From d414f3498e93cdbb51ab685a0f98b8d4ea9767fc Mon Sep 17 00:00:00 2001 From: "A.H" <73077907+ahcodes@users.noreply.github.com> Date: Fri, 2 Jul 2021 21:15:34 +0530 Subject: [PATCH 096/117] Minor alteration of a sentence. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b5a68823..1286c6c4 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Contains all data structure questions categorised into sub-directories like stac ### Algorithms -This directory contains various types of algorithm questions like Dynamic Programming, Sorting, Greedy, etc. The current structure of this directory is like - +This directory contains various types of algorithm questions like Dynamic Programming, Sorting, Greedy, etc. The current structure of this directory is as follows: 1. [Dynamic Programming](algorithms/dynamic_programming) 2. [Graphs](algorithms/graph) From 27b802be136a035acc9ec4f3663d52cebe407e3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tadej=20Glin=C5=A1ek?= Date: Mon, 19 Jul 2021 16:51:25 +0200 Subject: [PATCH 097/117] Fixed a bug in permutations_of_word.py --- data_structures/array/permutations_of_word.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data_structures/array/permutations_of_word.py b/data_structures/array/permutations_of_word.py index c0085b54..e5eb8998 100644 --- a/data_structures/array/permutations_of_word.py +++ b/data_structures/array/permutations_of_word.py @@ -6,7 +6,7 @@ def permutation(lst): l = [] for i in range(len(lst)): m = lst[i] - rem_lst = lst[:i] + lst[i+i:] + rem_lst = lst[:i] + lst[i+1:] for p in permutation(rem_lst): l.append([m] + p) return l From 70d2852bd4a2f752b2b9f2b7475fb8b7206450a7 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Wed, 25 Aug 2021 10:10:00 +0530 Subject: [PATCH 098/117] Add comments for Kadane's algorithm --- data_structures/array/kadane_algorithm.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/data_structures/array/kadane_algorithm.py b/data_structures/array/kadane_algorithm.py index f2fee2d3..19824af5 100644 --- a/data_structures/array/kadane_algorithm.py +++ b/data_structures/array/kadane_algorithm.py @@ -1,3 +1,12 @@ +""" +Kadane's algorithm is used to find the maximum contiguous sum in an array. +The logic is simple. Take the first element in the sum and then find current max num. +Curr max = max(arr[i], curr_max + arr[i]) - we add this number if it increases the sum, +otherwise we take the number if it is more than the sum + +Then keep track of max of this value +""" + def max_sum(arr): max_so_far = arr[0] curr_max = arr[0] From 9220f3ec5739e70ca3ae38cfa2e5d1d366338a9d Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 26 Aug 2021 16:34:01 +0530 Subject: [PATCH 099/117] add comments --- data_structures/array/number_of_1_in_sorted_array.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/data_structures/array/number_of_1_in_sorted_array.py b/data_structures/array/number_of_1_in_sorted_array.py index 79689872..bfcfda50 100644 --- a/data_structures/array/number_of_1_in_sorted_array.py +++ b/data_structures/array/number_of_1_in_sorted_array.py @@ -1,4 +1,8 @@ -# The array is sorted in decreasing order +""" +Count the number of 1s in a sorted array +Instead of linearly searching the array to find the first occurence, +do a binary search to find the first 0 +""" def count(arr): start = 0 From 7bb1eb02fa933d8508594df1a20a0e1794317768 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 26 Aug 2021 16:35:49 +0530 Subject: [PATCH 100/117] Correct the logic --- .../number_of_elements_that_can_searched_using_binary_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data_structures/array/number_of_elements_that_can_searched_using_binary_search.py b/data_structures/array/number_of_elements_that_can_searched_using_binary_search.py index b1889522..9137db3c 100644 --- a/data_structures/array/number_of_elements_that_can_searched_using_binary_search.py +++ b/data_structures/array/number_of_elements_that_can_searched_using_binary_search.py @@ -9,7 +9,7 @@ So maintain two arrays - left_max and right_min such that in i'th index - * left_max[i] contains the max element between 0 and i-1 (left to right movement) -* right_min[i] contains the min element between n-1 and i+1 (right to left movement) +* right_min[i] contains the min element between i+1 and n-1 (right to left movement) Now for every element in the array, if its index its i, then it is binary searchable if left_max[i] < arr[i] < right_min[i] From f6beedd2f3cbc116b7b07a05a261faf6846d9e5f Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Fri, 27 Aug 2021 12:04:29 +0530 Subject: [PATCH 101/117] adjust formatting --- .../largest_rectangle_area_in_histogram.py | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/data_structures/stack/largest_rectangle_area_in_histogram.py b/data_structures/stack/largest_rectangle_area_in_histogram.py index bee42b5b..86ac073d 100644 --- a/data_structures/stack/largest_rectangle_area_in_histogram.py +++ b/data_structures/stack/largest_rectangle_area_in_histogram.py @@ -1,8 +1,7 @@ ''' Largest rectangle area in a histogram:: -Find the largest rectangular area possible in a given histogram where the largest rectangle can be made of a number of contiguous bars. For simplicity, assume that all bars have same width and the width is 1 unit. - - +Find the largest rectangular area possible in a given histogram where the largest rectangle can be made of a number of contiguous bars. +For simplicity, assume that all bars have same width and the width is 1 unit. ''' def max_area_histogram(histogram): @@ -12,36 +11,30 @@ def max_area_histogram(histogram): max_area = 0 # Initialize max area index = 0 - while index < len(histogram): - + + while index < len(histogram): if (not stack) or (histogram[stack[-1]] <= histogram[index]): stack.append(index) index += 1 - - else: - top_of_stack = stack.pop() - area = (histogram[top_of_stack] * ((index - stack[-1] - 1) if stack else index)) - max_area = max(max_area, area) while stack: - top_of_stack = stack.pop() - area = (histogram[top_of_stack] * ((index - stack[-1] - 1) if stack else index)) max_area = max(max_area, area) - return max_area + + hist = [4, 7, 1, 8, 4, 9, 5] print("Maximum area is", - max_area_histogram(hist)) +max_area_histogram(hist)) From f81517515b5f6911c2fecd7abd7f062141d982a2 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Sat, 4 Sep 2021 18:40:47 +0530 Subject: [PATCH 102/117] refactored --- .../stack/largest_rectangle_area_in_histogram.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/data_structures/stack/largest_rectangle_area_in_histogram.py b/data_structures/stack/largest_rectangle_area_in_histogram.py index 86ac073d..6aa55947 100644 --- a/data_structures/stack/largest_rectangle_area_in_histogram.py +++ b/data_structures/stack/largest_rectangle_area_in_histogram.py @@ -19,16 +19,12 @@ def max_area_histogram(histogram): index += 1 else: top_of_stack = stack.pop() - area = (histogram[top_of_stack] * - ((index - stack[-1] - 1) - if stack else index)) + area = (histogram[top_of_stack] * ((index - stack[-1] - 1) if stack else index)) max_area = max(max_area, area) while stack: top_of_stack = stack.pop() - area = (histogram[top_of_stack] * - ((index - stack[-1] - 1) - if stack else index)) + area = (histogram[top_of_stack] * ((index - stack[-1] - 1) if stack else index)) max_area = max(max_area, area) From 2cfccd1a7e8476f8f1eb30ca17d9f83036929756 Mon Sep 17 00:00:00 2001 From: sterben04 Date: Mon, 4 Oct 2021 21:02:55 +0530 Subject: [PATCH 103/117] Added unique-char-check --- data_structures/strings/unique_char_check.py | 24 ++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 data_structures/strings/unique_char_check.py diff --git a/data_structures/strings/unique_char_check.py b/data_structures/strings/unique_char_check.py new file mode 100644 index 00000000..3af3083c --- /dev/null +++ b/data_structures/strings/unique_char_check.py @@ -0,0 +1,24 @@ +""" +Question +You are given a string S, check if all characters are unique. + +SAMPLE INPUT 1 +abcd +SAMPLE OUTPUT 1 +True + +SAMPLE INPUT 2 +aabc +SAMPLE OUTPUT 2 +False +""" +from collections import Counter +def unique_char_check(S): + character_count = Counter(S) + + if len(character_count) == len(S): + return True + return False + +S = input() +print(unique_char_check(S)) \ No newline at end of file From 565278fc4a63213684c73bef2b99feeccd69f09e Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Fri, 15 Oct 2021 12:10:50 +0530 Subject: [PATCH 104/117] Remove init from data structures --- data_structures/__init__.py | 1 - 1 file changed, 1 deletion(-) delete mode 100644 data_structures/__init__.py diff --git a/data_structures/__init__.py b/data_structures/__init__.py deleted file mode 100644 index 8b137891..00000000 --- a/data_structures/__init__.py +++ /dev/null @@ -1 +0,0 @@ - From dde44bc8d0e740a83a3b08f754d3ae86dff9ea3b Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Fri, 15 Oct 2021 12:13:44 +0530 Subject: [PATCH 105/117] remove init from algorithms dir --- algorithms/__init__.py | 1 - 1 file changed, 1 deletion(-) delete mode 100644 algorithms/__init__.py diff --git a/algorithms/__init__.py b/algorithms/__init__.py deleted file mode 100644 index 8b137891..00000000 --- a/algorithms/__init__.py +++ /dev/null @@ -1 +0,0 @@ - From f4b55227e97e4a8dacd43e4694099d57995d0424 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Sun, 24 Oct 2021 08:48:28 +0530 Subject: [PATCH 106/117] Update License full name --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1286c6c4..fc10b02b 100644 --- a/README.md +++ b/README.md @@ -69,4 +69,4 @@ To follow the guidelines, refer to [Contributing.md](CONTRIBUTING.md) ## License -[MIT](LICENSE) +[MIT License](LICENSE) From 99dfc16d82aea6bdfdc1aff4d77cc52664912d56 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Sun, 24 Oct 2021 08:50:24 +0530 Subject: [PATCH 107/117] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fc10b02b..7c05260f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Python Data Structures and Algorithms -No non-sense solutions to common Data Structure and Algorithm interview questions in Python. +No non-sense solutions to common Data Structure and Algorithm interview questions in Python. Follows a consistent approach throughout problems. ## Objective From 6b7ad5611109697873e5b6000b907f9cd8c01c5c Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Thu, 7 Apr 2022 09:52:48 +0530 Subject: [PATCH 108/117] Add WebSockets vs XMPP --- bookmarks/articles.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bookmarks/articles.md b/bookmarks/articles.md index 81c00863..ccdd64e2 100644 --- a/bookmarks/articles.md +++ b/bookmarks/articles.md @@ -57,3 +57,5 @@ This is a list of articles that may be useful for algorithms and data structures - https://jwt.io/introduction/ - https://khashtamov.com/en/how-to-become-a-data-engineer/ + +- https://blog.mirrorfly.com/xmpp-vs-websockets-instant-messaging-protocol-comparison/ From 6d7079c5d922d905ca1c0f8bc9a4ca366e7dd1d2 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 4 Oct 2022 06:57:20 +0530 Subject: [PATCH 109/117] Added comments --- data_structures/bst/average_of_levels.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data_structures/bst/average_of_levels.py b/data_structures/bst/average_of_levels.py index 5028cf58..273c18d6 100644 --- a/data_structures/bst/average_of_levels.py +++ b/data_structures/bst/average_of_levels.py @@ -1,3 +1,7 @@ +""" +Find the mathematical average of all levels of a BST +""" + import collections class Node(): From b971f2a931ed4850727c1441353fe1294cecd1ac Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 4 Oct 2022 06:58:19 +0530 Subject: [PATCH 110/117] Update check_circular_linked_list.py --- .../circular_linked_list/check_circular_linked_list.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data_structures/circular_linked_list/check_circular_linked_list.py b/data_structures/circular_linked_list/check_circular_linked_list.py index 6a72e6e7..4da76507 100644 --- a/data_structures/circular_linked_list/check_circular_linked_list.py +++ b/data_structures/circular_linked_list/check_circular_linked_list.py @@ -1,3 +1,7 @@ +""" +Check if a linked list is a circular linked list +""" + class Node(): def __init__(self, val): From f835c9a71361d5cbba995df926b08ae980cb52c5 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 4 Oct 2022 06:58:41 +0530 Subject: [PATCH 111/117] Delete index.md --- data_structures/doubly_linked_list/index.md | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 data_structures/doubly_linked_list/index.md diff --git a/data_structures/doubly_linked_list/index.md b/data_structures/doubly_linked_list/index.md deleted file mode 100644 index 0a7d6f6d..00000000 --- a/data_structures/doubly_linked_list/index.md +++ /dev/null @@ -1,3 +0,0 @@ -# Index of doubly_linked_list - -* [Doubly Linked List](doubly_linked_list.py) From a21f390fc06b0432bda509b5199335075a593723 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 4 Oct 2022 06:59:30 +0530 Subject: [PATCH 112/117] Rename Adjacency_matrix.py to adjacency_matrix.py --- .../graphs/{Adjacency_matrix.py => adjacency_matrix.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename data_structures/graphs/{Adjacency_matrix.py => adjacency_matrix.py} (100%) diff --git a/data_structures/graphs/Adjacency_matrix.py b/data_structures/graphs/adjacency_matrix.py similarity index 100% rename from data_structures/graphs/Adjacency_matrix.py rename to data_structures/graphs/adjacency_matrix.py From f3305bc4c55f02c69e1d03f50be0472bb30f6d89 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Mon, 1 Apr 2024 19:33:02 +0530 Subject: [PATCH 113/117] Fix infinite array search --- data_structures/array/binary_search_infinite_array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data_structures/array/binary_search_infinite_array.py b/data_structures/array/binary_search_infinite_array.py index 6eff9207..1db83e07 100644 --- a/data_structures/array/binary_search_infinite_array.py +++ b/data_structures/array/binary_search_infinite_array.py @@ -23,7 +23,7 @@ def search(arr, val): high = 1 while temp < val: - low = 0 + low = high high = 2 * high temp = arr[high] From f17ce1b59f23feb58778491151b095c4c52e6013 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 2 Apr 2024 11:06:54 +0530 Subject: [PATCH 114/117] Some fixes --- data_structures/array/duplicate.py | 12 ++++++------ data_structures/array/find_given_sum_in_array.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/data_structures/array/duplicate.py b/data_structures/array/duplicate.py index 1e4f6291..4838b1aa 100644 --- a/data_structures/array/duplicate.py +++ b/data_structures/array/duplicate.py @@ -17,13 +17,13 @@ def duplicate(arr): if tortoise == hare: break - ptr1 = arr[0] - ptr2 = tortoise - while ptr1 != ptr2: - ptr1 = arr[ptr1] - ptr2 = arr[ptr2] + tortoise = arr[0] + + while tortoise != hare: + tortoise = arr[tortoise] + hare = arr[hare] - return ptr1 + return hare arr = [3,5,1,2,4,5] diff --git a/data_structures/array/find_given_sum_in_array.py b/data_structures/array/find_given_sum_in_array.py index 0aacc5b4..e3adcfcb 100644 --- a/data_structures/array/find_given_sum_in_array.py +++ b/data_structures/array/find_given_sum_in_array.py @@ -24,4 +24,4 @@ def find_sum(arr, s): arr = [15, 2, 4, 8, 9, 5, 10, 23] -print(find_sum(arr, 6)) +print(find_sum(arr, 6)) \ No newline at end of file From ef23195057a1721eea41d39e0bdbd4c52b5640ea Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Tue, 2 Apr 2024 15:49:12 +0530 Subject: [PATCH 115/117] Small fixes --- data_structures/array/peak_element.py | 3 +-- data_structures/array/square_of_sorted_array.py | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/data_structures/array/peak_element.py b/data_structures/array/peak_element.py index 29d8b1f2..ded661b0 100644 --- a/data_structures/array/peak_element.py +++ b/data_structures/array/peak_element.py @@ -6,8 +6,7 @@ def peak(arr, low, high): n = len(arr) while low <= high: - mid = low + (high - low) / 2 - mid = int(mid) + mid = (high - low) // 2 if (mid == 0 or arr[mid-1] <= arr[mid]) and (mid == n-1 or arr[mid+1] <= arr[mid]): return(arr[mid]) diff --git a/data_structures/array/square_of_sorted_array.py b/data_structures/array/square_of_sorted_array.py index 1bebb758..68f196b9 100644 --- a/data_structures/array/square_of_sorted_array.py +++ b/data_structures/array/square_of_sorted_array.py @@ -1,3 +1,8 @@ +""" +Find the square of all the numbers of a sorted array such that after finding the square of the sorted array, the +resultant array containing the squared numbers remains sorted +""" + def square(arr): n = len(arr) j = 0 From 27124b9b77a18a2d3e89c262311286a1d9c2910c Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Fri, 5 Apr 2024 15:51:52 +0530 Subject: [PATCH 116/117] Convert camelCase to snake_case --- data_structures/deque/deque.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data_structures/deque/deque.py b/data_structures/deque/deque.py index d578c8e2..85c7dd2c 100644 --- a/data_structures/deque/deque.py +++ b/data_structures/deque/deque.py @@ -47,7 +47,7 @@ def get_last(self): def size(self): return len(self.data) - def isEmpty(self): + def is_empty(self): if len(self.data) == 0: return True return False @@ -59,7 +59,7 @@ def contains(self, elem): return False - def printElems(self): + def print_elements(self): result = "" for i in self.data: From 35d3556a992ceccc5b925afa892fae3ba01a0e81 Mon Sep 17 00:00:00 2001 From: Prabhu Pant Date: Sat, 6 Apr 2024 13:13:16 +0530 Subject: [PATCH 117/117] Add comments --- data_structures/linked_list/delete_last_occurrence.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data_structures/linked_list/delete_last_occurrence.py b/data_structures/linked_list/delete_last_occurrence.py index e76f1d9a..80951ab0 100644 --- a/data_structures/linked_list/delete_last_occurrence.py +++ b/data_structures/linked_list/delete_last_occurrence.py @@ -1,3 +1,7 @@ +""" +Delete last occurence of a number in linked list. +""" + class Node(): def __init__(self, val):