├── Hashmap and Heap Implementation.pdf ├── Python Data Structure- Linked List.ipynb ├── Python Data Structures - Binary Trees.ipynb ├── Python Data Structures- Binary Search Trees.ipynb ├── Python Data Strutures- Hash Table.pdf ├── README.md └── String Processing.ipynb /Hashmap and Heap Implementation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abg3/Python-Data-Structures/42e61fa68907af0d2fe03ac1818ec010a4978de2/Hashmap and Heap Implementation.pdf -------------------------------------------------------------------------------- /Python Data Structure- Linked List.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Node class" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "class Node:\n", 17 | " def __init__(self,data):\n", 18 | " self.data=data\n", 19 | " self.next=None\n", 20 | " \n", 21 | "#LinkedList class\n", 22 | "\n", 23 | "class LinkedList:\n", 24 | " def __init__(self):\n", 25 | " self.head=None" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "3 use cases of Insertion:\n", 33 | "- Append\n", 34 | "- Prepend\n", 35 | "- Insert_after_node" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": 7, 41 | "metadata": {}, 42 | "outputs": [], 43 | "source": [ 44 | "class LinkedList:\n", 45 | " def __init__(self):\n", 46 | " self.head=None\n", 47 | " def append(self,data):\n", 48 | " new_node=Node(data)\n", 49 | " if self.head is None:\n", 50 | " self.head=new_node\n", 51 | " return\n", 52 | " last_node=self.head\n", 53 | " while last_node.next:\n", 54 | " last_node=last_node.next\n", 55 | " last_node.next=new_node\n", 56 | " \n", 57 | " #printlist function\n", 58 | " def print_list(self):\n", 59 | " curr=self.head\n", 60 | " while curr:\n", 61 | " print(curr.data)\n", 62 | " curr=curr.next" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": 15, 68 | "metadata": {}, 69 | "outputs": [ 70 | { 71 | "name": "stdout", 72 | "output_type": "stream", 73 | "text": [ 74 | "A\n", 75 | "B\n", 76 | "C\n", 77 | "D\n" 78 | ] 79 | } 80 | ], 81 | "source": [ 82 | "llist = LinkedList()\n", 83 | "llist.append(\"A\")\n", 84 | "llist.append(\"B\")\n", 85 | "llist.append(\"C\")\n", 86 | "llist.append(\"D\")\n", 87 | "llist.print_list()" 88 | ] 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "metadata": {}, 93 | "source": [ 94 | "**Prepend*" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": 19, 100 | "metadata": {}, 101 | "outputs": [], 102 | "source": [ 103 | "class LinkedList:\n", 104 | " def __init__(self):\n", 105 | " self.head=None\n", 106 | " def append(self,data):\n", 107 | " new_node=Node(data)\n", 108 | " if self.head is None:\n", 109 | " self.head=new_node\n", 110 | " return\n", 111 | " last_node=self.head\n", 112 | " while last_node.next:\n", 113 | " last_node=last_node.next\n", 114 | " last_node.next=new_node\n", 115 | " \n", 116 | " #printlist function\n", 117 | " def print_list(self):\n", 118 | " curr=self.head\n", 119 | " while curr:\n", 120 | " print(curr.data)\n", 121 | " curr=curr.next\n", 122 | " def prepend(self,data):\n", 123 | " new_node=Node(data)\n", 124 | " \n", 125 | " new_node.next=self.head\n", 126 | " self.head=new_node" 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": 21, 132 | "metadata": {}, 133 | "outputs": [ 134 | { 135 | "name": "stdout", 136 | "output_type": "stream", 137 | "text": [ 138 | "E\n", 139 | "A\n", 140 | "B\n", 141 | "C\n", 142 | "D\n" 143 | ] 144 | } 145 | ], 146 | "source": [ 147 | "llist = LinkedList()\n", 148 | "llist.append(\"A\")\n", 149 | "llist.append(\"B\")\n", 150 | "llist.append(\"C\")\n", 151 | "llist.append(\"D\")\n", 152 | "llist.prepend(\"E\")\n", 153 | "llist.print_list()" 154 | ] 155 | }, 156 | { 157 | "cell_type": "code", 158 | "execution_count": 23, 159 | "metadata": {}, 160 | "outputs": [], 161 | "source": [ 162 | "class LinkedList:\n", 163 | " def __init__(self):\n", 164 | " self.head=None\n", 165 | " def append(self,data):\n", 166 | " new_node=Node(data)\n", 167 | " if self.head is None:\n", 168 | " self.head=new_node\n", 169 | " return\n", 170 | " last_node=self.head\n", 171 | " while last_node.next:\n", 172 | " last_node=last_node.next\n", 173 | " last_node.next=new_node\n", 174 | " \n", 175 | " #printlist function\n", 176 | " def print_list(self):\n", 177 | " curr=self.head\n", 178 | " while curr:\n", 179 | " print(curr.data)\n", 180 | " curr=curr.next\n", 181 | " \n", 182 | " def prepend(self,data):\n", 183 | " new_node=Node(data)\n", 184 | " \n", 185 | " new_node.next=self.head\n", 186 | " self.head=new_node\n", 187 | " def insert_after_node(self,prev_node,data):\n", 188 | " if not prev_node:\n", 189 | " print('previous node does not exist')\n", 190 | " return\n", 191 | " new_node=Node(data)\n", 192 | " new_node.next=prev_node.next\n", 193 | " prev_node.next=new_node" 194 | ] 195 | }, 196 | { 197 | "cell_type": "code", 198 | "execution_count": 26, 199 | "metadata": {}, 200 | "outputs": [ 201 | { 202 | "name": "stdout", 203 | "output_type": "stream", 204 | "text": [ 205 | "E\n", 206 | "F\n", 207 | "A\n", 208 | "B\n", 209 | "C\n", 210 | "D\n" 211 | ] 212 | } 213 | ], 214 | "source": [ 215 | "llist=LinkedList()\n", 216 | "llist.append(\"A\")\n", 217 | "llist.append(\"B\")\n", 218 | "llist.append(\"C\")\n", 219 | "llist.append(\"D\")\n", 220 | "llist.prepend(\"E\")\n", 221 | "llist.insert_after_node(llist.head,\"F\")\n", 222 | "llist.print_list()" 223 | ] 224 | }, 225 | { 226 | "cell_type": "markdown", 227 | "metadata": {}, 228 | "source": [ 229 | "**A question what if I want to insert after node C what should i write in argument of insert_after_node?*" 230 | ] 231 | }, 232 | { 233 | "cell_type": "code", 234 | "execution_count": 30, 235 | "metadata": {}, 236 | "outputs": [], 237 | "source": [ 238 | "#practice again and again\n", 239 | "def append(self,data):\n", 240 | " new_node=Node(data)\n", 241 | " if self.head is None:\n", 242 | " self.head=new_node\n", 243 | " return\n", 244 | " last_node= self.head\n", 245 | " while last_node.next:\n", 246 | " last_node=last_node.next\n", 247 | " last_node.next=new_node\n", 248 | " \n", 249 | "def prepend(self,data):\n", 250 | " new_node=Node(data)\n", 251 | " new_node.next=self.head\n", 252 | " self.head=new_node\n", 253 | " \n", 254 | "def insert_after_node(self,prev_node,data):\n", 255 | " if not prev_node:\n", 256 | " print(\"previous node does not exist\")\n", 257 | " return\n", 258 | " curr=Node(data)\n", 259 | " curr.next=prev_node.next\n", 260 | " prev_node.next=curr " 261 | ] 262 | }, 263 | { 264 | "cell_type": "markdown", 265 | "metadata": {}, 266 | "source": [ 267 | "**Deletion*" 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": 32, 273 | "metadata": {}, 274 | "outputs": [], 275 | "source": [ 276 | "def delete_node(self,key):\n", 277 | " curr=self.head\n", 278 | " if curr and curr.data==key:\n", 279 | " self.head=curr.next\n", 280 | " curr=None\n", 281 | " return\n", 282 | "\n", 283 | " prev=None\n", 284 | " while curr and curr.data!=key:\n", 285 | " prev=curr\n", 286 | " curr=curr.next\n", 287 | " if curr is None:\n", 288 | " return\n", 289 | " prev.next=curr.next\n", 290 | " curr=None" 291 | ] 292 | }, 293 | { 294 | "cell_type": "code", 295 | "execution_count": 34, 296 | "metadata": {}, 297 | "outputs": [], 298 | "source": [ 299 | "class LinkedList:\n", 300 | " def __init__(self):\n", 301 | " self.head=None\n", 302 | " def append(self,data):\n", 303 | " new_node=Node(data)\n", 304 | " if self.head is None:\n", 305 | " self.head=new_node\n", 306 | " return\n", 307 | " last_node=self.head\n", 308 | " while last_node.next:\n", 309 | " last_node=last_node.next\n", 310 | " last_node.next=new_node\n", 311 | " \n", 312 | " #printlist function\n", 313 | " def print_list(self):\n", 314 | " curr=self.head\n", 315 | " while curr:\n", 316 | " print(curr.data)\n", 317 | " curr=curr.next\n", 318 | " \n", 319 | " def prepend(self,data):\n", 320 | " new_node=Node(data)\n", 321 | " \n", 322 | " new_node.next=self.head\n", 323 | " self.head=new_node\n", 324 | " \n", 325 | " def insert_after_node(self,prev_node,data):\n", 326 | " if not prev_node:\n", 327 | " print('previous node does not exist')\n", 328 | " return\n", 329 | " new_node=Node(data)\n", 330 | " new_node.next=prev_node.next\n", 331 | " prev_node.next=new_node\n", 332 | " \n", 333 | " def delete_node(self,key):\n", 334 | " curr=self.head\n", 335 | " if curr and curr.data==key:\n", 336 | " self.head=curr.next\n", 337 | " curr=None\n", 338 | " return\n", 339 | "\n", 340 | " prev=None\n", 341 | " while curr and curr.data!=key:\n", 342 | " prev=curr\n", 343 | " curr=curr.next\n", 344 | " if curr is None:\n", 345 | " return\n", 346 | " prev.next=curr.next\n", 347 | " curr=None" 348 | ] 349 | }, 350 | { 351 | "cell_type": "code", 352 | "execution_count": 43, 353 | "metadata": {}, 354 | "outputs": [ 355 | { 356 | "name": "stdout", 357 | "output_type": "stream", 358 | "text": [ 359 | "E\n", 360 | "F\n", 361 | "A\n", 362 | "B\n", 363 | "C\n", 364 | "D\n", 365 | "After deletion\n", 366 | "A\n", 367 | "B\n", 368 | "C\n", 369 | "D\n" 370 | ] 371 | } 372 | ], 373 | "source": [ 374 | "llist=LinkedList()\n", 375 | "llist.append(\"A\")\n", 376 | "llist.append(\"B\")\n", 377 | "llist.append(\"C\")\n", 378 | "llist.append(\"D\")\n", 379 | "llist.prepend(\"E\")\n", 380 | "llist.insert_after_node(llist.head,\"F\")\n", 381 | "llist.print_list()\n", 382 | "\n", 383 | "print(\"After deletion\")\n", 384 | "llist.delete_node(\"F\")\n", 385 | "llist.delete_node(\"E\")\n", 386 | "\n", 387 | "llist.print_list()" 388 | ] 389 | }, 390 | { 391 | "cell_type": "markdown", 392 | "metadata": {}, 393 | "source": [ 394 | "**Deletion by position*" 395 | ] 396 | }, 397 | { 398 | "cell_type": "code", 399 | "execution_count": 46, 400 | "metadata": {}, 401 | "outputs": [], 402 | "source": [ 403 | "def delete_node_by_pos(self,pos):\n", 404 | " if self.head:\n", 405 | " curr=self.head\n", 406 | " if pos==0:\n", 407 | " self.head=curr.next\n", 408 | " curr=None\n", 409 | " return\n", 410 | "\n", 411 | " prev=0\n", 412 | " count=0\n", 413 | " while curr and curr.count!=pos:\n", 414 | " prev=curr\n", 415 | " curr=curr.next\n", 416 | " count+=1\n", 417 | "\n", 418 | " if curr is None:\n", 419 | " return\n", 420 | " prev.next=curr.next\n", 421 | " curr=None " 422 | ] 423 | }, 424 | { 425 | "cell_type": "code", 426 | "execution_count": 49, 427 | "metadata": {}, 428 | "outputs": [], 429 | "source": [ 430 | "class LinkedList:\n", 431 | " def __init__(self):\n", 432 | " self.head=None\n", 433 | " def append(self,data):\n", 434 | " new_node=Node(data)\n", 435 | " if self.head is None:\n", 436 | " self.head=new_node\n", 437 | " return\n", 438 | " last_node=self.head\n", 439 | " while last_node.next:\n", 440 | " last_node=last_node.next\n", 441 | " last_node.next=new_node\n", 442 | " \n", 443 | " #printlist function\n", 444 | " def print_list(self):\n", 445 | " curr=self.head\n", 446 | " while curr:\n", 447 | " print(curr.data)\n", 448 | " curr=curr.next\n", 449 | " \n", 450 | " def prepend(self,data):\n", 451 | " new_node=Node(data)\n", 452 | " \n", 453 | " new_node.next=self.head\n", 454 | " self.head=new_node\n", 455 | " def insert_after_node(self,prev_node,data):\n", 456 | " if not prev_node:\n", 457 | " print('previous node does not exist')\n", 458 | " return\n", 459 | " new_node=Node(data)\n", 460 | " new_node.next=prev_node.next\n", 461 | " prev_node.next=new_node\n", 462 | " \n", 463 | " def delete_node(self,key):\n", 464 | " curr=self.head\n", 465 | " if curr and curr.data==key:\n", 466 | " self.head=curr.next\n", 467 | " curr=None\n", 468 | " return\n", 469 | "\n", 470 | " prev=None\n", 471 | " while curr and curr.data!=key:\n", 472 | " prev=curr\n", 473 | " curr=curr.next\n", 474 | " if curr is None:\n", 475 | " return\n", 476 | " prev.next=curr.next\n", 477 | " curr=None\n", 478 | " \n", 479 | " def delete_node_by_pos(self,pos):\n", 480 | " if self.head:\n", 481 | " curr=self.head\n", 482 | " if pos==0:\n", 483 | " self.head=curr.next\n", 484 | " curr=None\n", 485 | " return\n", 486 | "\n", 487 | " prev=0\n", 488 | " count=0\n", 489 | " while curr and count!=pos:\n", 490 | " prev=curr\n", 491 | " curr=curr.next\n", 492 | " count+=1\n", 493 | "\n", 494 | " if curr is None:\n", 495 | " return\n", 496 | " prev.next=curr.next\n", 497 | " curr=None" 498 | ] 499 | }, 500 | { 501 | "cell_type": "code", 502 | "execution_count": 51, 503 | "metadata": {}, 504 | "outputs": [ 505 | { 506 | "name": "stdout", 507 | "output_type": "stream", 508 | "text": [ 509 | "E\n", 510 | "F\n", 511 | "A\n", 512 | "B\n", 513 | "C\n", 514 | "D\n", 515 | "After deletion by value\n", 516 | "A\n", 517 | "B\n", 518 | "C\n", 519 | "D\n", 520 | "After deletion by postion\n", 521 | "A\n", 522 | "C\n", 523 | "D\n" 524 | ] 525 | } 526 | ], 527 | "source": [ 528 | "llist=LinkedList()\n", 529 | "llist.append(\"A\")\n", 530 | "llist.append(\"B\")\n", 531 | "llist.append(\"C\")\n", 532 | "llist.append(\"D\")\n", 533 | "llist.prepend(\"E\")\n", 534 | "llist.insert_after_node(llist.head,\"F\")\n", 535 | "llist.print_list()\n", 536 | "\n", 537 | "print(\"After deletion by value\")\n", 538 | "llist.delete_node(\"F\")\n", 539 | "llist.delete_node(\"E\")\n", 540 | "llist.print_list()\n", 541 | "print(\"After deletion by postion\")\n", 542 | "llist.delete_node_by_pos(1)\n", 543 | "llist.print_list()" 544 | ] 545 | }, 546 | { 547 | "cell_type": "markdown", 548 | "metadata": {}, 549 | "source": [ 550 | "**Length of Linked List*" 551 | ] 552 | }, 553 | { 554 | "cell_type": "markdown", 555 | "metadata": {}, 556 | "source": [ 557 | "2 ways: Iterative and Recursive implementation" 558 | ] 559 | }, 560 | { 561 | "cell_type": "code", 562 | "execution_count": 52, 563 | "metadata": {}, 564 | "outputs": [], 565 | "source": [ 566 | "def len_iterative(self):\n", 567 | " count=0\n", 568 | " curr=self.head\n", 569 | " while curr:\n", 570 | " count+=1\n", 571 | " curr=curr.next\n", 572 | " \n", 573 | " return count" 574 | ] 575 | }, 576 | { 577 | "cell_type": "code", 578 | "execution_count": 53, 579 | "metadata": {}, 580 | "outputs": [], 581 | "source": [ 582 | "def len_recursive(self,node):\n", 583 | " if self.head is None:\n", 584 | " return 0\n", 585 | " return 1+ self.len_recursive(node.next)" 586 | ] 587 | }, 588 | { 589 | "cell_type": "code", 590 | "execution_count": 57, 591 | "metadata": {}, 592 | "outputs": [], 593 | "source": [ 594 | "class LinkedList:\n", 595 | " def __init__(self):\n", 596 | " self.head=None\n", 597 | " def append(self,data):\n", 598 | " new_node=Node(data)\n", 599 | " if self.head is None:\n", 600 | " self.head=new_node\n", 601 | " return\n", 602 | " last_node=self.head\n", 603 | " while last_node.next:\n", 604 | " last_node=last_node.next\n", 605 | " last_node.next=new_node\n", 606 | " \n", 607 | " #printlist function\n", 608 | " def print_list(self):\n", 609 | " curr=self.head\n", 610 | " while curr:\n", 611 | " print(curr.data)\n", 612 | " curr=curr.next\n", 613 | " \n", 614 | " def prepend(self,data):\n", 615 | " new_node=Node(data)\n", 616 | " \n", 617 | " new_node.next=self.head\n", 618 | " self.head=new_node\n", 619 | " def insert_after_node(self,prev_node,data):\n", 620 | " if not prev_node:\n", 621 | " print('previous node does not exist')\n", 622 | " return\n", 623 | " new_node=Node(data)\n", 624 | " new_node.next=prev_node.next\n", 625 | " prev_node.next=new_node\n", 626 | " \n", 627 | " def delete_node(self,key):\n", 628 | " curr=self.head\n", 629 | " if curr and curr.data==key:\n", 630 | " self.head=curr.next\n", 631 | " curr=None\n", 632 | " return\n", 633 | "\n", 634 | " prev=None\n", 635 | " while curr and curr.data!=key:\n", 636 | " prev=curr\n", 637 | " curr=curr.next\n", 638 | " if curr is None:\n", 639 | " return\n", 640 | " prev.next=curr.next\n", 641 | " curr=None\n", 642 | " \n", 643 | " def delete_node_by_pos(self,pos):\n", 644 | " if self.head:\n", 645 | " curr=self.head\n", 646 | " if pos==0:\n", 647 | " self.head=curr.next\n", 648 | " curr=None\n", 649 | " return\n", 650 | "\n", 651 | " prev=0\n", 652 | " count=0\n", 653 | " while curr and count!=pos:\n", 654 | " prev=curr\n", 655 | " curr=curr.next\n", 656 | " count+=1\n", 657 | "\n", 658 | " if curr is None:\n", 659 | " return\n", 660 | " prev.next=curr.next\n", 661 | " curr=None\n", 662 | " def len_iterative(self):\n", 663 | " count=0\n", 664 | " curr=self.head\n", 665 | " while curr:\n", 666 | " count+=1\n", 667 | " curr=curr.next\n", 668 | "\n", 669 | " return count\n", 670 | " \n", 671 | " def len_recursive(self,node):\n", 672 | " if node is None:\n", 673 | " return 0\n", 674 | " return 1+ self.len_recursive(node.next)" 675 | ] 676 | }, 677 | { 678 | "cell_type": "code", 679 | "execution_count": 58, 680 | "metadata": {}, 681 | "outputs": [ 682 | { 683 | "name": "stdout", 684 | "output_type": "stream", 685 | "text": [ 686 | "E\n", 687 | "F\n", 688 | "A\n", 689 | "B\n", 690 | "C\n", 691 | "D\n", 692 | "After deletion by value\n", 693 | "A\n", 694 | "B\n", 695 | "C\n", 696 | "D\n", 697 | "After deletion by postion\n", 698 | "A\n", 699 | "C\n", 700 | "D\n", 701 | "The length of the linked list calculated recursively should be 3:\n", 702 | "3\n", 703 | "The length of the linked list calculated iteratively should also be 3:\n", 704 | "3\n" 705 | ] 706 | } 707 | ], 708 | "source": [ 709 | "llist=LinkedList()\n", 710 | "llist.append(\"A\")\n", 711 | "llist.append(\"B\")\n", 712 | "llist.append(\"C\")\n", 713 | "llist.append(\"D\")\n", 714 | "llist.prepend(\"E\")\n", 715 | "llist.insert_after_node(llist.head,\"F\")\n", 716 | "llist.print_list()\n", 717 | "\n", 718 | "print(\"After deletion by value\")\n", 719 | "llist.delete_node(\"F\")\n", 720 | "llist.delete_node(\"E\")\n", 721 | "llist.print_list()\n", 722 | "print(\"After deletion by postion\")\n", 723 | "llist.delete_node_by_pos(1)\n", 724 | "llist.print_list()\n", 725 | "\n", 726 | "print(\"The length of the linked list calculated recursively should be 3:\")\n", 727 | "print(llist.len_recursive(llist.head))\n", 728 | "print(\"The length of the linked list calculated iteratively should also be 3:\")\n", 729 | "print(llist.len_iterative())" 730 | ] 731 | }, 732 | { 733 | "cell_type": "markdown", 734 | "metadata": {}, 735 | "source": [ 736 | "**Node Swap*" 737 | ] 738 | }, 739 | { 740 | "cell_type": "code", 741 | "execution_count": 59, 742 | "metadata": {}, 743 | "outputs": [], 744 | "source": [ 745 | "def swap_nodes(self,key1,key2):\n", 746 | " if key1==key2:\n", 747 | " return\n", 748 | " prev1=None\n", 749 | " curr1=self.head\n", 750 | " while curr1 and curr1.data!=key1:\n", 751 | " prev1=curr1\n", 752 | " curr1=curr1.next\n", 753 | " \n", 754 | " prev2=None\n", 755 | " curr2=self.head\n", 756 | " while curr2 and curr2.data!=key2:\n", 757 | " prev2=curr2\n", 758 | " curr2=curr2.next\n", 759 | " \n", 760 | " if not curr1 or not curr2:\n", 761 | " return\n", 762 | " \n", 763 | " #doubt from here\n", 764 | " if prev1:\n", 765 | " prev1.next=curr2\n", 766 | " else:\n", 767 | " self.head=curr2\n", 768 | " \n", 769 | " if prev2:\n", 770 | " prev2.next=curr1\n", 771 | " else:\n", 772 | " self.head=curr1\n", 773 | " \n", 774 | " curr1.next,curr2.next=curr2.next,curr1.next\n", 775 | " " 776 | ] 777 | }, 778 | { 779 | "cell_type": "code", 780 | "execution_count": 60, 781 | "metadata": {}, 782 | "outputs": [], 783 | "source": [ 784 | "class LinkedList:\n", 785 | " def __init__(self):\n", 786 | " self.head=None\n", 787 | " def append(self,data):\n", 788 | " new_node=Node(data)\n", 789 | " if self.head is None:\n", 790 | " self.head=new_node\n", 791 | " return\n", 792 | " last_node=self.head\n", 793 | " while last_node.next:\n", 794 | " last_node=last_node.next\n", 795 | " last_node.next=new_node\n", 796 | " \n", 797 | " #printlist function\n", 798 | " def print_list(self):\n", 799 | " curr=self.head\n", 800 | " while curr:\n", 801 | " print(curr.data)\n", 802 | " curr=curr.next\n", 803 | " \n", 804 | " def prepend(self,data):\n", 805 | " new_node=Node(data)\n", 806 | " \n", 807 | " new_node.next=self.head\n", 808 | " self.head=new_node\n", 809 | " def insert_after_node(self,prev_node,data):\n", 810 | " if not prev_node:\n", 811 | " print('previous node does not exist')\n", 812 | " return\n", 813 | " new_node=Node(data)\n", 814 | " new_node.next=prev_node.next\n", 815 | " prev_node.next=new_node\n", 816 | " \n", 817 | " def delete_node(self,key):\n", 818 | " curr=self.head\n", 819 | " if curr and curr.data==key:\n", 820 | " self.head=curr.next\n", 821 | " curr=None\n", 822 | " return\n", 823 | "\n", 824 | " prev=None\n", 825 | " while curr and curr.data!=key:\n", 826 | " prev=curr\n", 827 | " curr=curr.next\n", 828 | " if curr is None:\n", 829 | " return\n", 830 | " prev.next=curr.next\n", 831 | " curr=None\n", 832 | " \n", 833 | " def delete_node_by_pos(self,pos):\n", 834 | " if self.head:\n", 835 | " curr=self.head\n", 836 | " if pos==0:\n", 837 | " self.head=curr.next\n", 838 | " curr=None\n", 839 | " return\n", 840 | "\n", 841 | " prev=0\n", 842 | " count=0\n", 843 | " while curr and count!=pos:\n", 844 | " prev=curr\n", 845 | " curr=curr.next\n", 846 | " count+=1\n", 847 | "\n", 848 | " if curr is None:\n", 849 | " return\n", 850 | " prev.next=curr.next\n", 851 | " curr=None\n", 852 | " def len_iterative(self):\n", 853 | " count=0\n", 854 | " curr=self.head\n", 855 | " while curr:\n", 856 | " count+=1\n", 857 | " curr=curr.next\n", 858 | "\n", 859 | " return count\n", 860 | " \n", 861 | " def len_recursive(self,node):\n", 862 | " if node is None:\n", 863 | " return 0\n", 864 | " return 1+ self.len_recursive(node.next)\n", 865 | " \n", 866 | " def swap_nodes(self,key1,key2):\n", 867 | " if key1==key2:\n", 868 | " return\n", 869 | " \n", 870 | " prev1=None\n", 871 | " curr1=self.head\n", 872 | " while curr1 and curr1.data!=key1:\n", 873 | " prev1=curr1\n", 874 | " curr1=curr1.next\n", 875 | "\n", 876 | " prev2=None\n", 877 | " curr2=self.head\n", 878 | " while curr2 and curr2.data!=key2:\n", 879 | " prev2=curr2\n", 880 | " curr2=curr2.next\n", 881 | "\n", 882 | " if not curr1 or not curr2:\n", 883 | " return\n", 884 | "\n", 885 | " if prev1:\n", 886 | " prev1.next=curr2\n", 887 | " else:\n", 888 | " self.head=curr2\n", 889 | "\n", 890 | " if prev2:\n", 891 | " prev2.next=curr1\n", 892 | " else:\n", 893 | " self.head=curr1\n", 894 | "\n", 895 | " curr1.next,curr2.next=curr2.next,curr1.next\n" 896 | ] 897 | }, 898 | { 899 | "cell_type": "code", 900 | "execution_count": 64, 901 | "metadata": {}, 902 | "outputs": [ 903 | { 904 | "name": "stdout", 905 | "output_type": "stream", 906 | "text": [ 907 | "Original List\n", 908 | "A\n", 909 | "B\n", 910 | "C\n", 911 | "D\n", 912 | "Swapping nodes B and C that are not head nodes\n", 913 | "A\n", 914 | "C\n", 915 | "B\n", 916 | "D\n", 917 | "Swapping nodes A and B where key_1 is head node\n", 918 | "B\n", 919 | "C\n", 920 | "A\n", 921 | "D\n", 922 | "Swapping nodes D and B where key_2 is head node\n", 923 | "D\n", 924 | "C\n", 925 | "A\n", 926 | "B\n", 927 | "Swapping nodes C and C where both keys are same\n", 928 | "D\n", 929 | "C\n", 930 | "A\n", 931 | "B\n" 932 | ] 933 | } 934 | ], 935 | "source": [ 936 | "llist = LinkedList()\n", 937 | "llist.append(\"A\")\n", 938 | "llist.append(\"B\")\n", 939 | "llist.append(\"C\")\n", 940 | "llist.append(\"D\")\n", 941 | "\n", 942 | "print(\"Original List\")\n", 943 | "llist.print_list()\n", 944 | "\n", 945 | "# llist.prepend(\"E\")\n", 946 | "# llist.insert_after_node(llist.head,\"F\")\n", 947 | "\n", 948 | "# print(\"After inserting E and F\")\n", 949 | "# llist.print_list()\n", 950 | "\n", 951 | "# print(\"After deletion by value\")\n", 952 | "# llist.delete_node(\"F\")\n", 953 | "# llist.delete_node(\"E\")\n", 954 | "# llist.print_list()\n", 955 | "# print(\"After deletion by postion\")\n", 956 | "# llist.delete_node_by_pos(1)\n", 957 | "# llist.print_list()\n", 958 | "\n", 959 | "llist.swap_nodes(\"B\", \"C\")\n", 960 | "print(\"Swapping nodes B and C that are not head nodes\")\n", 961 | "llist.print_list()\n", 962 | "\n", 963 | "llist.swap_nodes(\"A\", \"B\")\n", 964 | "print(\"Swapping nodes A and B where key_1 is head node\")\n", 965 | "llist.print_list()\n", 966 | "\n", 967 | "llist.swap_nodes(\"D\", \"B\")\n", 968 | "print(\"Swapping nodes D and B where key_2 is head node\")\n", 969 | "llist.print_list()\n", 970 | "\n", 971 | "llist.swap_nodes(\"C\", \"C\")\n", 972 | "print(\"Swapping nodes C and C where both keys are same\")\n", 973 | "llist.print_list()" 974 | ] 975 | }, 976 | { 977 | "cell_type": "markdown", 978 | "metadata": {}, 979 | "source": [ 980 | "**Reverse linkedlist*" 981 | ] 982 | }, 983 | { 984 | "cell_type": "markdown", 985 | "metadata": {}, 986 | "source": [ 987 | "2 ways: using Iterative and recursive implementation" 988 | ] 989 | }, 990 | { 991 | "cell_type": "code", 992 | "execution_count": 67, 993 | "metadata": {}, 994 | "outputs": [], 995 | "source": [ 996 | "def reverse_iterative(self):\n", 997 | " prev=None\n", 998 | " curr=self.head\n", 999 | " while curr:\n", 1000 | " nxt=curr.next\n", 1001 | " curr.next=prev\n", 1002 | " prev=curr\n", 1003 | " curr=nxt\n", 1004 | " self.head=prev\n", 1005 | "\n", 1006 | "def _reverse_recursive(self):\n", 1007 | " def reverse_recursive(curr,prev):\n", 1008 | "\n", 1009 | " if not curr:\n", 1010 | " return prev\n", 1011 | "\n", 1012 | " nxt=curr.next\n", 1013 | " curr.next=prev\n", 1014 | " prev=curr\n", 1015 | " curr=nxt\n", 1016 | " return reverse_recursive(curr,prev)\n", 1017 | "\n", 1018 | " self.head=reverse_recursive(curr=self.head,prev=None)" 1019 | ] 1020 | }, 1021 | { 1022 | "cell_type": "code", 1023 | "execution_count": 68, 1024 | "metadata": {}, 1025 | "outputs": [], 1026 | "source": [ 1027 | "class LinkedList:\n", 1028 | " def __init__(self):\n", 1029 | " self.head=None\n", 1030 | " def append(self,data):\n", 1031 | " new_node=Node(data)\n", 1032 | " if self.head is None:\n", 1033 | " self.head=new_node\n", 1034 | " return\n", 1035 | " last_node=self.head\n", 1036 | " while last_node.next:\n", 1037 | " last_node=last_node.next\n", 1038 | " last_node.next=new_node\n", 1039 | " \n", 1040 | " #printlist function\n", 1041 | " def print_list(self):\n", 1042 | " curr=self.head\n", 1043 | " while curr:\n", 1044 | " print(curr.data)\n", 1045 | " curr=curr.next\n", 1046 | " \n", 1047 | " def prepend(self,data):\n", 1048 | " new_node=Node(data)\n", 1049 | " \n", 1050 | " new_node.next=self.head\n", 1051 | " self.head=new_node\n", 1052 | " def insert_after_node(self,prev_node,data):\n", 1053 | " if not prev_node:\n", 1054 | " print('previous node does not exist')\n", 1055 | " return\n", 1056 | " new_node=Node(data)\n", 1057 | " new_node.next=prev_node.next\n", 1058 | " prev_node.next=new_node\n", 1059 | " \n", 1060 | " def delete_node(self,key):\n", 1061 | " curr=self.head\n", 1062 | " if curr and curr.data==key:\n", 1063 | " self.head=curr.next\n", 1064 | " curr=None\n", 1065 | " return\n", 1066 | "\n", 1067 | " prev=None\n", 1068 | " while curr and curr.data!=key:\n", 1069 | " prev=curr\n", 1070 | " curr=curr.next\n", 1071 | " if curr is None:\n", 1072 | " return\n", 1073 | " prev.next=curr.next\n", 1074 | " curr=None\n", 1075 | " \n", 1076 | " def delete_node_by_pos(self,pos):\n", 1077 | " if self.head:\n", 1078 | " curr=self.head\n", 1079 | " if pos==0:\n", 1080 | " self.head=curr.next\n", 1081 | " curr=None\n", 1082 | " return\n", 1083 | " \n", 1084 | "\n", 1085 | " prev=0\n", 1086 | " count=0\n", 1087 | " while curr and count!=pos:\n", 1088 | " prev=curr\n", 1089 | " curr=curr.next\n", 1090 | " count+=1\n", 1091 | "\n", 1092 | " if curr is None:\n", 1093 | " return\n", 1094 | " prev.next=curr.next\n", 1095 | " curr=None\n", 1096 | " def len_iterative(self):\n", 1097 | " count=0\n", 1098 | " curr=self.head\n", 1099 | " while curr:\n", 1100 | " count+=1\n", 1101 | " curr=curr.next\n", 1102 | "\n", 1103 | " return count\n", 1104 | " \n", 1105 | " def len_recursive(self,node):\n", 1106 | " if node is None:\n", 1107 | " return 0\n", 1108 | " return 1+ self.len_recursive(node.next)\n", 1109 | " \n", 1110 | " def swap_nodes(self,key1,key2):\n", 1111 | " if key1==key2:\n", 1112 | " return\n", 1113 | " \n", 1114 | " prev1=None\n", 1115 | " curr1=self.head\n", 1116 | " while curr1 and curr1.data!=key1:\n", 1117 | " prev1=curr1\n", 1118 | " curr1=curr1.next\n", 1119 | "\n", 1120 | " prev2=None\n", 1121 | " curr2=self.head\n", 1122 | " while curr2 and curr2.data!=key2:\n", 1123 | " prev2=curr2\n", 1124 | " curr2=curr2.next\n", 1125 | "\n", 1126 | " if not curr1 or not curr2:\n", 1127 | " return\n", 1128 | "\n", 1129 | " if prev1:\n", 1130 | " prev1.next=curr2\n", 1131 | " else:\n", 1132 | " self.head=curr2\n", 1133 | "\n", 1134 | " if prev2:\n", 1135 | " prev2.next=curr1\n", 1136 | " else:\n", 1137 | " self.head=curr1\n", 1138 | "\n", 1139 | " curr1.next,curr2.next=curr2.next,curr1.next\n", 1140 | " \n", 1141 | " def reverse_iterative(self):\n", 1142 | " prev=None\n", 1143 | " curr=self.head\n", 1144 | " while curr:\n", 1145 | " nxt=curr.next\n", 1146 | " curr.next=prev\n", 1147 | " prev=curr\n", 1148 | " curr=nxt\n", 1149 | " self.head=prev\n", 1150 | "\n", 1151 | " def _reverse_recursive(self):\n", 1152 | " def reverse_recursive(curr,prev):\n", 1153 | "\n", 1154 | " if not curr:\n", 1155 | " return prev\n", 1156 | "\n", 1157 | " nxt=curr.next\n", 1158 | " curr.next=prev\n", 1159 | " prev=curr\n", 1160 | " curr=nxt\n", 1161 | " return reverse_recursive(curr,prev)\n", 1162 | "\n", 1163 | " self.head=reverse_recursive(curr=self.head,prev=None)" 1164 | ] 1165 | }, 1166 | { 1167 | "cell_type": "code", 1168 | "execution_count": 73, 1169 | "metadata": {}, 1170 | "outputs": [ 1171 | { 1172 | "name": "stdout", 1173 | "output_type": "stream", 1174 | "text": [ 1175 | "Iterative method\n", 1176 | "D\n", 1177 | "C\n", 1178 | "B\n", 1179 | "A\n", 1180 | "Recursive method\n", 1181 | "A\n", 1182 | "B\n", 1183 | "C\n", 1184 | "D\n" 1185 | ] 1186 | } 1187 | ], 1188 | "source": [ 1189 | "llist = LinkedList()\n", 1190 | "llist.append(\"A\")\n", 1191 | "llist.append(\"B\")\n", 1192 | "llist.append(\"C\")\n", 1193 | "llist.append(\"D\")\n", 1194 | "print(\"Iterative method\")\n", 1195 | "llist.reverse_iterative()\n", 1196 | "llist.print_list()\n", 1197 | "\n", 1198 | "print(\"Recursive method\")\n", 1199 | "llist._reverse_recursive()\n", 1200 | "llist.print_list()" 1201 | ] 1202 | }, 1203 | { 1204 | "cell_type": "markdown", 1205 | "metadata": {}, 1206 | "source": [ 1207 | "**Remove Duplicates*" 1208 | ] 1209 | }, 1210 | { 1211 | "cell_type": "code", 1212 | "execution_count": 74, 1213 | "metadata": {}, 1214 | "outputs": [], 1215 | "source": [ 1216 | "def remove_duplicates(self):\n", 1217 | " prev=None\n", 1218 | " curr=self.head\n", 1219 | " dup_dict={}\n", 1220 | " \n", 1221 | " while curr:\n", 1222 | " if curr.data in dup_dict:\n", 1223 | " prev.next=curr.next\n", 1224 | " curr=None\n", 1225 | " else:\n", 1226 | " dup_dict[curr.data]= 1\n", 1227 | " prev=curr\n", 1228 | " curr=prev.next\n", 1229 | " " 1230 | ] 1231 | }, 1232 | { 1233 | "cell_type": "code", 1234 | "execution_count": 75, 1235 | "metadata": {}, 1236 | "outputs": [], 1237 | "source": [ 1238 | "class LinkedList:\n", 1239 | " def __init__(self):\n", 1240 | " self.head=None\n", 1241 | " def append(self,data):\n", 1242 | " new_node=Node(data)\n", 1243 | " if self.head is None:\n", 1244 | " self.head=new_node\n", 1245 | " return\n", 1246 | " last_node=self.head\n", 1247 | " while last_node.next:\n", 1248 | " last_node=last_node.next\n", 1249 | " last_node.next=new_node\n", 1250 | " \n", 1251 | " #printlist function\n", 1252 | " def print_list(self):\n", 1253 | " curr=self.head\n", 1254 | " while curr:\n", 1255 | " print(curr.data)\n", 1256 | " curr=curr.next\n", 1257 | " \n", 1258 | " def prepend(self,data):\n", 1259 | " new_node=Node(data)\n", 1260 | " \n", 1261 | " new_node.next=self.head\n", 1262 | " self.head=new_node\n", 1263 | " def insert_after_node(self,prev_node,data):\n", 1264 | " if not prev_node:\n", 1265 | " print('previous node does not exist')\n", 1266 | " return\n", 1267 | " new_node=Node(data)\n", 1268 | " new_node.next=prev_node.next\n", 1269 | " prev_node.next=new_node\n", 1270 | " \n", 1271 | " def remove_duplicates(self):\n", 1272 | " prev=None\n", 1273 | " curr=self.head\n", 1274 | " dup_dict={}\n", 1275 | "\n", 1276 | " while curr:\n", 1277 | " if curr.data in dup_dict:\n", 1278 | " prev.next=curr.next\n", 1279 | " curr=None\n", 1280 | " else:\n", 1281 | " dup_dict[curr.data]= 1\n", 1282 | " prev=curr\n", 1283 | " curr=prev.next" 1284 | ] 1285 | }, 1286 | { 1287 | "cell_type": "code", 1288 | "execution_count": 77, 1289 | "metadata": {}, 1290 | "outputs": [ 1291 | { 1292 | "name": "stdout", 1293 | "output_type": "stream", 1294 | "text": [ 1295 | "With duplicates\n", 1296 | "A\n", 1297 | "B\n", 1298 | "C\n", 1299 | "D\n", 1300 | "D\n", 1301 | "A\n", 1302 | "B\n", 1303 | "B\n", 1304 | "Without duplicates\n", 1305 | "A\n", 1306 | "B\n", 1307 | "C\n", 1308 | "D\n" 1309 | ] 1310 | } 1311 | ], 1312 | "source": [ 1313 | "llist = LinkedList()\n", 1314 | "llist.append(\"A\")\n", 1315 | "llist.append(\"B\")\n", 1316 | "llist.append(\"C\")\n", 1317 | "llist.append(\"D\")\n", 1318 | "llist.append(\"D\")\n", 1319 | "llist.append(\"A\")\n", 1320 | "llist.append(\"B\")\n", 1321 | "llist.append(\"B\")\n", 1322 | "print(\"With duplicates\")\n", 1323 | "llist.print_list()\n", 1324 | "print(\"Without duplicates\")\n", 1325 | "llist.remove_duplicates()\n", 1326 | "llist.print_list()" 1327 | ] 1328 | }, 1329 | { 1330 | "cell_type": "code", 1331 | "execution_count": 78, 1332 | "metadata": {}, 1333 | "outputs": [], 1334 | "source": [ 1335 | "def print_nth_from_lastnode(self,n):\n", 1336 | " total_len=self.len_iterative()\n", 1337 | " \n", 1338 | " curr=self.head\n", 1339 | " while curr:\n", 1340 | " if total_len==n:\n", 1341 | " print(curr.data)\n", 1342 | " return curr.data\n", 1343 | " total_len-=1\n", 1344 | " curr=curr.next\n", 1345 | " " 1346 | ] 1347 | }, 1348 | { 1349 | "cell_type": "code", 1350 | "execution_count": 81, 1351 | "metadata": {}, 1352 | "outputs": [], 1353 | "source": [ 1354 | "class LinkedList:\n", 1355 | " def __init__(self):\n", 1356 | " self.head=None\n", 1357 | " def append(self,data):\n", 1358 | " new_node=Node(data)\n", 1359 | " if self.head is None:\n", 1360 | " self.head=new_node\n", 1361 | " return\n", 1362 | " last_node=self.head\n", 1363 | " while last_node.next:\n", 1364 | " last_node=last_node.next\n", 1365 | " last_node.next=new_node\n", 1366 | " \n", 1367 | " #printlist function\n", 1368 | " def print_list(self):\n", 1369 | " curr=self.head\n", 1370 | " while curr:\n", 1371 | " print(curr.data)\n", 1372 | " curr=curr.next\n", 1373 | " \n", 1374 | " def prepend(self,data):\n", 1375 | " new_node=Node(data)\n", 1376 | " \n", 1377 | " new_node.next=self.head\n", 1378 | " self.head=new_node\n", 1379 | " def insert_after_node(self,prev_node,data):\n", 1380 | " if not prev_node:\n", 1381 | " print('previous node does not exist')\n", 1382 | " return\n", 1383 | " new_node=Node(data)\n", 1384 | " new_node.next=prev_node.next\n", 1385 | " prev_node.next=new_node\n", 1386 | " \n", 1387 | "\n", 1388 | " def len_iterative(self):\n", 1389 | " count=0\n", 1390 | " curr=self.head\n", 1391 | " while curr:\n", 1392 | " count+=1\n", 1393 | " curr=curr.next\n", 1394 | "\n", 1395 | " return count\n", 1396 | " \n", 1397 | " def len_recursive(self,node):\n", 1398 | " if node is None:\n", 1399 | " return 0\n", 1400 | " return 1+ self.len_recursive(node.next)\n", 1401 | " \n", 1402 | " def print_nth_from_lastnode(self,n):\n", 1403 | " total_len=self.len_iterative()\n", 1404 | "\n", 1405 | " curr=self.head\n", 1406 | " while curr:\n", 1407 | " if total_len==n:\n", 1408 | " print(curr.data)\n", 1409 | " return curr.data\n", 1410 | " total_len-=1\n", 1411 | " curr=curr.next\n", 1412 | " if curr is None:\n", 1413 | " return" 1414 | ] 1415 | }, 1416 | { 1417 | "cell_type": "code", 1418 | "execution_count": 84, 1419 | "metadata": {}, 1420 | "outputs": [ 1421 | { 1422 | "name": "stdout", 1423 | "output_type": "stream", 1424 | "text": [ 1425 | "A\n", 1426 | "B\n", 1427 | "C\n", 1428 | "D\n", 1429 | "D\n", 1430 | "A\n", 1431 | "B\n", 1432 | "B\n", 1433 | "4th node from last is:\n", 1434 | "D\n", 1435 | "8th node from last is the first node, so it is the head which is:\n", 1436 | "A\n" 1437 | ] 1438 | }, 1439 | { 1440 | "data": { 1441 | "text/plain": [ 1442 | "'A'" 1443 | ] 1444 | }, 1445 | "execution_count": 84, 1446 | "metadata": {}, 1447 | "output_type": "execute_result" 1448 | } 1449 | ], 1450 | "source": [ 1451 | "llist = LinkedList()\n", 1452 | "llist.append(\"A\")\n", 1453 | "llist.append(\"B\")\n", 1454 | "llist.append(\"C\")\n", 1455 | "llist.append(\"D\")\n", 1456 | "llist.append(\"D\")\n", 1457 | "llist.append(\"A\")\n", 1458 | "llist.append(\"B\")\n", 1459 | "llist.append(\"B\")\n", 1460 | "llist.print_list()\n", 1461 | "print(\"4th node from last is:\")\n", 1462 | "llist.print_nth_from_lastnode(4)\n", 1463 | "print(\"8th node from last is the first node, so it is the head which is:\")\n", 1464 | "llist.print_nth_from_lastnode(8)" 1465 | ] 1466 | }, 1467 | { 1468 | "cell_type": "code", 1469 | "execution_count": 86, 1470 | "metadata": {}, 1471 | "outputs": [], 1472 | "source": [ 1473 | "def count_occurences_iterative(self, data):\n", 1474 | " count = 0\n", 1475 | " cur = self.head\n", 1476 | " while cur:\n", 1477 | " \n", 1478 | " if cur.data == data:\n", 1479 | " count += 1\n", 1480 | " cur = cur.next\n", 1481 | " return count \n", 1482 | " " 1483 | ] 1484 | }, 1485 | { 1486 | "cell_type": "code", 1487 | "execution_count": 95, 1488 | "metadata": {}, 1489 | "outputs": [], 1490 | "source": [ 1491 | "class LinkedList:\n", 1492 | " def __init__(self):\n", 1493 | " self.head=None\n", 1494 | " def append(self,data):\n", 1495 | " new_node=Node(data)\n", 1496 | " if self.head is None:\n", 1497 | " self.head=new_node\n", 1498 | " return\n", 1499 | " last_node=self.head\n", 1500 | " while last_node.next:\n", 1501 | " last_node=last_node.next\n", 1502 | " last_node.next=new_node\n", 1503 | " \n", 1504 | " #printlist function\n", 1505 | " def print_list(self):\n", 1506 | " curr=self.head\n", 1507 | " while curr:\n", 1508 | " print(curr.data)\n", 1509 | " curr=curr.next\n", 1510 | " \n", 1511 | " def prepend(self,data):\n", 1512 | " new_node=Node(data)\n", 1513 | " \n", 1514 | " new_node.next=self.head\n", 1515 | " self.head=new_node\n", 1516 | " def insert_after_node(self,prev_node,data):\n", 1517 | " if not prev_node:\n", 1518 | " print('previous node does not exist')\n", 1519 | " return\n", 1520 | " new_node=Node(data)\n", 1521 | " new_node.next=prev_node.next\n", 1522 | " prev_node.next=new_node\n", 1523 | " \n", 1524 | "\n", 1525 | " def len_iterative(self):\n", 1526 | " count=0\n", 1527 | " curr=self.head\n", 1528 | " while curr:\n", 1529 | " count+=1\n", 1530 | " curr=curr.next\n", 1531 | "\n", 1532 | " return count\n", 1533 | " \n", 1534 | " def len_recursive(self,node):\n", 1535 | " if node is None:\n", 1536 | " return 0\n", 1537 | " return 1+ self.len_recursive(node.next)\n", 1538 | " \n", 1539 | " def print_nth_from_lastnode(self,n):\n", 1540 | " total_len=self.len_iterative()\n", 1541 | "\n", 1542 | " curr=self.head\n", 1543 | " while curr:\n", 1544 | " if total_len==n:\n", 1545 | " print(curr.data)\n", 1546 | " return curr.data\n", 1547 | " total_len-=1\n", 1548 | " curr=curr.next\n", 1549 | " if curr is None:\n", 1550 | " return\n", 1551 | " \n", 1552 | " def count_occurences_iterative(self, data):\n", 1553 | " count = 0\n", 1554 | " cur = self.head\n", 1555 | " while cur:\n", 1556 | " if cur.data == data:\n", 1557 | " count += 1\n", 1558 | " cur = cur.next\n", 1559 | " return count \n", 1560 | " " 1561 | ] 1562 | }, 1563 | { 1564 | "cell_type": "code", 1565 | "execution_count": 96, 1566 | "metadata": {}, 1567 | "outputs": [ 1568 | { 1569 | "name": "stdout", 1570 | "output_type": "stream", 1571 | "text": [ 1572 | "A\n", 1573 | "B\n", 1574 | "C\n", 1575 | "D\n", 1576 | "D\n", 1577 | "A\n", 1578 | "B\n", 1579 | "B\n", 1580 | "The number of Bs are:\n" 1581 | ] 1582 | }, 1583 | { 1584 | "data": { 1585 | "text/plain": [ 1586 | "3" 1587 | ] 1588 | }, 1589 | "execution_count": 96, 1590 | "metadata": {}, 1591 | "output_type": "execute_result" 1592 | } 1593 | ], 1594 | "source": [ 1595 | "llist = LinkedList()\n", 1596 | "llist.append(\"A\")\n", 1597 | "llist.append(\"B\")\n", 1598 | "llist.append(\"C\")\n", 1599 | "llist.append(\"D\")\n", 1600 | "llist.append(\"D\")\n", 1601 | "llist.append(\"A\")\n", 1602 | "llist.append(\"B\")\n", 1603 | "llist.append(\"B\")\n", 1604 | "llist.print_list()\n", 1605 | "print(\"The number of Bs are:\")\n", 1606 | "llist.count_occurences_iterative(\"B\")" 1607 | ] 1608 | }, 1609 | { 1610 | "cell_type": "code", 1611 | "execution_count": null, 1612 | "metadata": {}, 1613 | "outputs": [], 1614 | "source": [ 1615 | "#append\n", 1616 | "new_node=Node()\n", 1617 | "if self.head is None:\n", 1618 | " self.head=new_node\n", 1619 | " return\n", 1620 | "curr=self.head\n", 1621 | "while curr.next:\n", 1622 | " curr=curr.next\n", 1623 | "curr.next=new_node\n", 1624 | "\n", 1625 | "#prepend\n", 1626 | "new_node=Node(data)\n", 1627 | "new_node.next=self.head\n", 1628 | "self.head=new_node\n", 1629 | "\n", 1630 | "#insertafternode\n", 1631 | "new_node=Node(data)\n", 1632 | "new_node.next=prev.next\n", 1633 | "prev.next=new_node\n", 1634 | "\n", 1635 | "#" 1636 | ] 1637 | } 1638 | ], 1639 | "metadata": { 1640 | "kernelspec": { 1641 | "display_name": "Python 3", 1642 | "language": "python", 1643 | "name": "python3" 1644 | }, 1645 | "language_info": { 1646 | "codemirror_mode": { 1647 | "name": "ipython", 1648 | "version": 3 1649 | }, 1650 | "file_extension": ".py", 1651 | "mimetype": "text/x-python", 1652 | "name": "python", 1653 | "nbconvert_exporter": "python", 1654 | "pygments_lexer": "ipython3", 1655 | "version": "3.7.4" 1656 | } 1657 | }, 1658 | "nbformat": 4, 1659 | "nbformat_minor": 4 1660 | } 1661 | -------------------------------------------------------------------------------- /Python Data Structures - Binary Trees.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Binary Trees" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "A binary tree is a tree data structure in which each node has at most two children, which are referred to as the left child and the right child. " 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "**Depth of a Node**\n", 22 | "\n", 23 | "The length of the path from a node, n, to the root node. The depth of the root node is 0." 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "**Height of a Tree**\n", 31 | "\n", 32 | "The length of the path from n to its deepest descendant. The height of the tree itself is the height of the root node, and the height of leaf nodes is always 0." 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "**Memory Tip!**- **Depth calculation starts from root to leaf node and Height calculation from leaf node to the root! Easy?*" 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "metadata": {}, 45 | "source": [ 46 | "# Types of Binary Trees" 47 | ] 48 | }, 49 | { 50 | "cell_type": "markdown", 51 | "metadata": {}, 52 | "source": [ 53 | "**Complete Binary Tree**- In a complete binary tree, every level except possibly the last, is completely filled and all nodes in the last level are as far left as possible.\n", 54 | "\n", 55 | "**Full Binary Tree**- A full binary tree (sometimes referred to as a proper or plane binary tree) is a tree in which every node has either 0 or 2 children." 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [ 62 | "# **Implementation**" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": 1, 68 | "metadata": {}, 69 | "outputs": [], 70 | "source": [ 71 | "class Node(object):\n", 72 | " def __init__(self,value):\n", 73 | " self.value=value\n", 74 | " self.left=None\n", 75 | " self.right=None" 76 | ] 77 | }, 78 | { 79 | "cell_type": "markdown", 80 | "metadata": {}, 81 | "source": [ 82 | "The Node class has three attributes:\n", 83 | "\n", 84 | "1. self.value\n", 85 | "2. self.left\n", 86 | "3. self.right" 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "metadata": {}, 92 | "source": [ 93 | "Let’s go ahead and implement BinaryTree class:" 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": 2, 99 | "metadata": {}, 100 | "outputs": [], 101 | "source": [ 102 | "class BinaryTree(object):\n", 103 | " def __init__(self,root):\n", 104 | " self.root=Node(root)" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 3, 110 | "metadata": {}, 111 | "outputs": [], 112 | "source": [ 113 | "#Creating object of BinaryTree class\n", 114 | "tree = BinaryTree(1)\n", 115 | "tree.root.left = Node(2)\n", 116 | "tree.root.right = Node(3)\n", 117 | "tree.root.left.left = Node(4)\n", 118 | "tree.root.left.right = Node(5)\n", 119 | "tree.root.right.left = Node(6)\n", 120 | "tree.root.right.right = Node(7)" 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "metadata": {}, 126 | "source": [ 127 | "#### Traversal Algorithms" 128 | ] 129 | }, 130 | { 131 | "cell_type": "markdown", 132 | "metadata": {}, 133 | "source": [ 134 | "**Tree Traversal** - Tree Traversal is the process of visiting (checking or updating) each node in a tree data structure, exactly once. Unlike linked lists or one-dimensional arrays that are canonically traversed in linear order, trees may be traversed in multiple ways. They may be traversed in depth-first or breadth-first order." 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "metadata": {}, 140 | "source": [ 141 | "There are three common ways to traverse a tree in depth-first order:\n", 142 | "\n", 143 | "1. In-order\n", 144 | "2. Pre-order\n", 145 | "3. Post-order" 146 | ] 147 | }, 148 | { 149 | "cell_type": "markdown", 150 | "metadata": {}, 151 | "source": [ 152 | "#### Pre-order Traversal" 153 | ] 154 | }, 155 | { 156 | "cell_type": "markdown", 157 | "metadata": {}, 158 | "source": [ 159 | "Here is the algorithm for a pre-order traversal:\n", 160 | "\n", 161 | "- Check if the current node is empty/null.\n", 162 | "- Display the data part of the root (or current node).\n", 163 | "- Traverse the left subtree by recursively calling the pre-order method.\n", 164 | "- Traverse the right subtree by recursively calling the pre-order method." 165 | ] 166 | }, 167 | { 168 | "cell_type": "markdown", 169 | "metadata": {}, 170 | "source": [ 171 | "Let's implement:\n" 172 | ] 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": 9, 177 | "metadata": {}, 178 | "outputs": [], 179 | "source": [ 180 | "def preorder_print(self, start, traversal):\n", 181 | " \"\"\"Root->Left->Right\"\"\"\n", 182 | " if start:\n", 183 | " traversal += (str(start.value) + \"-\")\n", 184 | " traversal = self.preorder_print(start.left, traversal)\n", 185 | " traversal = self.preorder_print(start.right, traversal)\n", 186 | " return traversal" 187 | ] 188 | }, 189 | { 190 | "cell_type": "markdown", 191 | "metadata": {}, 192 | "source": [ 193 | "We check if start (i.e., the current node) is empty or not. If not, then we append start.value to the traversal string and recursively call preorder_print on start.left and start.right which are the right and left child of the current node. Finally, we return traversal from the method after we have returned from all the recursive calls in case start is not None. traversal is just a string that will concatenate the value of nodes in an order that we visited them." 194 | ] 195 | }, 196 | { 197 | "cell_type": "markdown", 198 | "metadata": {}, 199 | "source": [ 200 | "**Memory Tip!**- **Always start with root, go left, then right!*" 201 | ] 202 | }, 203 | { 204 | "cell_type": "markdown", 205 | "metadata": {}, 206 | "source": [ 207 | "#### In-order Traversal" 208 | ] 209 | }, 210 | { 211 | "cell_type": "markdown", 212 | "metadata": {}, 213 | "source": [ 214 | "Here is the algorithm for an in-order traversal:\n", 215 | "\n", 216 | "- Check if the current node is empty/null.\n", 217 | "- Traverse the left subtree by recursively calling the in-order method.\n", 218 | "- Display the data part of the root (or current node).\n", 219 | "- Traverse the right subtree by recursively calling the in-order method." 220 | ] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "execution_count": 10, 225 | "metadata": {}, 226 | "outputs": [], 227 | "source": [ 228 | "def inorder_print(self, start, traversal):\n", 229 | " \"\"\"Left->Root->Right\"\"\"\n", 230 | " if start:\n", 231 | " traversal = self.inorder_print(start.left, traversal)\n", 232 | " traversal += (str(start.value) + \"-\")\n", 233 | " traversal = self.inorder_print(start.right, traversal)\n", 234 | " return traversal" 235 | ] 236 | }, 237 | { 238 | "cell_type": "markdown", 239 | "metadata": {}, 240 | "source": [ 241 | "The inorder_print is pretty much the same as the preorder_print except that the order Root->Left->Right from pre-order changes to Left->Root->Right in in-order traversal. In order to achieve this order, we just change the order of statements in the if-condition, i.e., we first make a recursive call on the left child and after we are done will all the subsequent calls from line 4, we concatenate the value of the current node with traversal on line 5. Then, we can make a recursive call to right subtree on line 6. This will help us keep the order required for the in-order traversal." 242 | ] 243 | }, 244 | { 245 | "cell_type": "markdown", 246 | "metadata": {}, 247 | "source": [ 248 | "**Memory Tip!**- **Always start with left, when done display root, then cover right!*" 249 | ] 250 | }, 251 | { 252 | "cell_type": "markdown", 253 | "metadata": {}, 254 | "source": [ 255 | "#### Post-order Traversal " 256 | ] 257 | }, 258 | { 259 | "cell_type": "markdown", 260 | "metadata": {}, 261 | "source": [ 262 | "There you go:\n", 263 | "\n", 264 | "- Check if the current node is empty/null.\n", 265 | "- Traverse the left subtree by recursively calling the post-order method.\n", 266 | "- Traverse the right subtree by recursively calling the post-order method.\n", 267 | "- Display the data part of the root (or current node)." 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": 12, 273 | "metadata": {}, 274 | "outputs": [], 275 | "source": [ 276 | "def postorder_print(self, start, traversal):\n", 277 | " \"\"\"Left->Right->Root\"\"\"\n", 278 | " if start:\n", 279 | " traversal = self.postorder_print(start.left, traversal)\n", 280 | " traversal = self.postorder_print(start.right, traversal)\n", 281 | " traversal += (str(start.value) + \"-\")\n", 282 | " return traversal " 283 | ] 284 | }, 285 | { 286 | "cell_type": "markdown", 287 | "metadata": {}, 288 | "source": [ 289 | "The recursive calls to the left and the right subtree have been placed before concatenating the value of the current node to traversal." 290 | ] 291 | }, 292 | { 293 | "cell_type": "markdown", 294 | "metadata": {}, 295 | "source": [ 296 | "**Memory Tip!**- **Start with right,then cover left, finally display root!*" 297 | ] 298 | }, 299 | { 300 | "cell_type": "markdown", 301 | "metadata": {}, 302 | "source": [ 303 | "**Helper Method**\n", 304 | "\n", 305 | "Below is the implementation of all the tree traversal methods within the Binary Tree class. Additionally, there is a helper method print_tree(self, traversal_type) which will invoke the specified method according to traversal_type. All the pieces of code above have been combined below!" 306 | ] 307 | }, 308 | { 309 | "cell_type": "code", 310 | "execution_count": 13, 311 | "metadata": {}, 312 | "outputs": [ 313 | { 314 | "name": "stdout", 315 | "output_type": "stream", 316 | "text": [ 317 | "1-2-4-5-3-6-7-\n", 318 | "4-2-5-1-6-3-7-\n", 319 | "4-5-2-6-7-3-1-\n", 320 | "Traversal type laworder is not supported.\n", 321 | "False\n" 322 | ] 323 | } 324 | ], 325 | "source": [ 326 | "class Node(object):\n", 327 | " def __init__(self, value):\n", 328 | " self.value = value\n", 329 | " self.left = None\n", 330 | " self.right = None\n", 331 | "\n", 332 | "\n", 333 | "class BinaryTree(object):\n", 334 | " def __init__(self, root):\n", 335 | " self.root = Node(root)\n", 336 | "\n", 337 | " def print_tree(self, traversal_type):\n", 338 | " if traversal_type == \"preorder\":\n", 339 | " return self.preorder_print(tree.root, \"\")\n", 340 | " elif traversal_type == \"inorder\":\n", 341 | " return self.inorder_print(tree.root, \"\")\n", 342 | " elif traversal_type == \"postorder\":\n", 343 | " return self.postorder_print(tree.root, \"\")\n", 344 | "\n", 345 | " else:\n", 346 | " print(\"Traversal type \" + str(traversal_type) + \" is not supported.\")\n", 347 | " return False\n", 348 | "\n", 349 | " def preorder_print(self, start, traversal):\n", 350 | " \"\"\"Root->Left->Right\"\"\"\n", 351 | " if start:\n", 352 | " traversal += (str(start.value) + \"-\")\n", 353 | " traversal = self.preorder_print(start.left, traversal)\n", 354 | " traversal = self.preorder_print(start.right, traversal)\n", 355 | " return traversal\n", 356 | "\n", 357 | " def inorder_print(self, start, traversal):\n", 358 | " \"\"\"Left->Root->Right\"\"\"\n", 359 | " if start:\n", 360 | " traversal = self.inorder_print(start.left, traversal)\n", 361 | " traversal += (str(start.value) + \"-\")\n", 362 | " traversal = self.inorder_print(start.right, traversal)\n", 363 | " return traversal\n", 364 | "\n", 365 | " def postorder_print(self, start, traversal):\n", 366 | " \"\"\"Left->Right->Root\"\"\"\n", 367 | " if start:\n", 368 | " traversal = self.postorder_print(start.left, traversal)\n", 369 | " traversal = self.postorder_print(start.right, traversal)\n", 370 | " traversal += (str(start.value) + \"-\")\n", 371 | " return traversal\n", 372 | "\n", 373 | "# 1-2-4-5-3-6-7-\n", 374 | "# 4-2-5-1-6-3-7\n", 375 | "# 4-5-2-6-7-3-1\n", 376 | "# 1\n", 377 | "# / \\ \n", 378 | "# 2 3 \n", 379 | "# / \\ / \\\n", 380 | "# 4 5 6 7 \n", 381 | "\n", 382 | "# Set up tree:\n", 383 | "tree = BinaryTree(1)\n", 384 | "tree.root.left = Node(2)\n", 385 | "tree.root.right = Node(3)\n", 386 | "tree.root.left.left = Node(4)\n", 387 | "tree.root.left.right = Node(5)\n", 388 | "tree.root.right.left = Node(6)\n", 389 | "tree.root.right.right = Node(7)\n", 390 | "\n", 391 | "print(tree.print_tree(\"preorder\"))\n", 392 | "print(tree.print_tree(\"inorder\"))\n", 393 | "print(tree.print_tree(\"postorder\"))\n", 394 | "print(tree.print_tree(\"laworder\"))" 395 | ] 396 | }, 397 | { 398 | "cell_type": "markdown", 399 | "metadata": {}, 400 | "source": [ 401 | "**Keep in mind the memory tips!**\n", 402 | "\n", 403 | "**Hope you find these depth-first tree traversals useful! Let's do level-order traversal which is a kind of breadth-first tree traversal.*" 404 | ] 405 | }, 406 | { 407 | "cell_type": "markdown", 408 | "metadata": {}, 409 | "source": [ 410 | "#### Level-Order Traversal" 411 | ] 412 | }, 413 | { 414 | "cell_type": "markdown", 415 | "metadata": {}, 416 | "source": [ 417 | "Algorithm #\n", 418 | "\n", 419 | "To do a level-order traversal of a binary tree, we require a queue. Have a look at the slides below for the algorithm:" 420 | ] 421 | }, 422 | { 423 | "cell_type": "markdown", 424 | "metadata": {}, 425 | "source": [ 426 | "Let’s jump to the implementation in Python. First, we’ll need to implement Queue so that we can use its object in our solution of level-order traversal." 427 | ] 428 | }, 429 | { 430 | "cell_type": "code", 431 | "execution_count": 23, 432 | "metadata": {}, 433 | "outputs": [], 434 | "source": [ 435 | "class Queue(object):\n", 436 | " def __init__(self):\n", 437 | " self.items = []\n", 438 | "\n", 439 | " def enqueue(self, item):\n", 440 | " self.items.insert(0, item)\n", 441 | "\n", 442 | " def dequeue(self):\n", 443 | " if not self.is_empty():\n", 444 | " return self.items.pop()\n", 445 | "\n", 446 | " def is_empty(self):\n", 447 | " return len(self.items) == 0\n", 448 | "\n", 449 | " def peek(self):\n", 450 | " if not self.is_empty():\n", 451 | " return self.items[-1].value\n", 452 | "\n", 453 | " def __len__(self):\n", 454 | " return self.size()\n", 455 | "\n", 456 | " def size(self):\n", 457 | " return len(self.items)" 458 | ] 459 | }, 460 | { 461 | "cell_type": "markdown", 462 | "metadata": {}, 463 | "source": [ 464 | "The constructor of the Queue class initializes self.items to an empty list on line 3. This list will store all the elements in the queue. We assume the last element to be the front of the queue and the first element to be the back of the queue.\n", 465 | "\n", 466 | "To perform the enqueue operation, in the enqueue method, we make use of the insert method of Python list which will insert item on the 0th index in self.items as specified on line 6. On the other hand, in the dequeue method, we use the pop method of Python list to pop out the last element as the queue follows the First-In, First-Out property. The method also ensures that the pop method is only called if the queue is not empty. To see if a queue is empty or not, the is_empty method comes in handy which checks for the length of self.items and compares it with 0. If the length of self.items is 0, True is returned, otherwise, False is returned.\n", 467 | "\n", 468 | "The peek method will return the value of the last element in self.items which we assume to be the front of our queue. We have also overridden the len method on line 19 which calls the size method on line 22. The size method returns the length of self.items." 469 | ] 470 | }, 471 | { 472 | "cell_type": "markdown", 473 | "metadata": {}, 474 | "source": [ 475 | "Let’s go ahead and implement level-order traversal:\n", 476 | "\n", 477 | "\n" 478 | ] 479 | }, 480 | { 481 | "cell_type": "code", 482 | "execution_count": 38, 483 | "metadata": {}, 484 | "outputs": [], 485 | "source": [ 486 | "def levelorder_print(self, start):\n", 487 | " if start is None:\n", 488 | " return \n", 489 | "\n", 490 | " queue = Queue()\n", 491 | " queue.enqueue(start)\n", 492 | "\n", 493 | " traversal = \"\"\n", 494 | " while len(queue) > 0:\n", 495 | " traversal += str(queue.peek()) + \"-\"\n", 496 | " node = queue.dequeue()\n", 497 | "\n", 498 | " if node.left:\n", 499 | " queue.enqueue(node.left)\n", 500 | " if node.right:\n", 501 | " queue.enqueue(node.right)\n", 502 | " \n", 503 | " return traversal" 504 | ] 505 | }, 506 | { 507 | "cell_type": "markdown", 508 | "metadata": {}, 509 | "source": [ 510 | "In the code above, first of all, we handle an edge case on line 2, i.e., start (root node) is None or we have an empty tree. In such a case, we return from the levelorder_print method.\n", 511 | "\n", 512 | "On line 5, we initialize a Queue object from the class we just implemented and name it as queue to which we enqueue start on line 6 as described in the algorithm. traversal is initialized to an empty string on line 8. Next, we set up a while loop on line 9 which runs until the length of the queue is greater than 0. Just as depicted in the algorithm, we append an element using the peek method to traversal and also concatenate a - so that the traversal appears in a format where the visited nodes will be divided by -. Once traversal is updated to register the node we visit, we dequeue that node and save it in the variable node on line 11. From lines 13-16, we check for the left and the right children of node and enqueue them to queue if they exist.\n", 513 | "\n", 514 | "Finally, we return traversal on line 18 which will have all the nodes we visited according to level-order." 515 | ] 516 | }, 517 | { 518 | "cell_type": "markdown", 519 | "metadata": {}, 520 | "source": [ 521 | "**The pieces of code are combined and I have added levelorder_print to BinaryTree class and have also added \"levelorder\" as a traversal_type to print_tree method.*" 522 | ] 523 | }, 524 | { 525 | "cell_type": "code", 526 | "execution_count": 40, 527 | "metadata": {}, 528 | "outputs": [ 529 | { 530 | "name": "stdout", 531 | "output_type": "stream", 532 | "text": [ 533 | "1-2-3-4-5-\n" 534 | ] 535 | } 536 | ], 537 | "source": [ 538 | "class Queue(object):\n", 539 | " def __init__(self):\n", 540 | " self.items = []\n", 541 | "\n", 542 | " def enqueue(self, item):\n", 543 | " self.items.insert(0, item)\n", 544 | "\n", 545 | " def dequeue(self):\n", 546 | " if not self.is_empty():\n", 547 | " return self.items.pop()\n", 548 | "\n", 549 | " def is_empty(self):\n", 550 | " return len(self.items) == 0\n", 551 | "\n", 552 | " def peek(self):\n", 553 | " if not self.is_empty():\n", 554 | " return self.items[-1].value\n", 555 | "\n", 556 | " def __len__(self):\n", 557 | " return self.size()\n", 558 | "\n", 559 | " def size(self):\n", 560 | " return len(self.items)\n", 561 | "\n", 562 | "\n", 563 | "class Node(object):\n", 564 | " def __init__(self, value):\n", 565 | " self.value = value\n", 566 | " self.left = None\n", 567 | " self.right = None\n", 568 | "\n", 569 | "\n", 570 | "class BinaryTree(object):\n", 571 | " def __init__(self, root):\n", 572 | " self.root = Node(root)\n", 573 | "\n", 574 | " def print_tree(self, traversal_type):\n", 575 | " if traversal_type == \"preorder\":\n", 576 | " return self.preorder_print(tree.root, \"\")\n", 577 | " elif traversal_type == \"inorder\":\n", 578 | " return self.inorder_print(tree.root, \"\")\n", 579 | " elif traversal_type == \"postorder\":\n", 580 | " return self.postorder_print(tree.root, \"\")\n", 581 | " elif traversal_type == \"levelorder\":\n", 582 | " return self.levelorder_print(tree.root)\n", 583 | "\n", 584 | " else:\n", 585 | " print(\"Traversal type \" + str(traversal_type) + \" is not supported.\")\n", 586 | " return False\n", 587 | "\n", 588 | " def preorder_print(self, start, traversal):\n", 589 | " \"\"\"Root->Left->Right\"\"\"\n", 590 | " if start:\n", 591 | " traversal += (str(start.value) + \"-\")\n", 592 | " traversal = self.preorder_print(start.left, traversal)\n", 593 | " traversal = self.preorder_print(start.right, traversal)\n", 594 | " return traversal\n", 595 | "\n", 596 | " def inorder_print(self, start, traversal):\n", 597 | " \"\"\"Left->Root->Right\"\"\"\n", 598 | " if start:\n", 599 | " traversal = self.inorder_print(start.left, traversal)\n", 600 | " traversal += (str(start.value) + \"-\")\n", 601 | " traversal = self.inorder_print(start.right, traversal)\n", 602 | " return traversal\n", 603 | "\n", 604 | " def postorder_print(self, start, traversal):\n", 605 | " \"\"\"Left->Right->Root\"\"\"\n", 606 | " if start:\n", 607 | " traversal = self.postorder_print(start.left, traversal)\n", 608 | " traversal = self.postorder_print(start.right, traversal)\n", 609 | " traversal += (str(start.value) + \"-\")\n", 610 | " return traversal\n", 611 | "\n", 612 | " def levelorder_print(self, start):\n", 613 | " if start is None:\n", 614 | " return \n", 615 | "\n", 616 | " queue = Queue()\n", 617 | " queue.enqueue(start)\n", 618 | "\n", 619 | " traversal = \"\"\n", 620 | " while len(queue) > 0:\n", 621 | " traversal += str(queue.peek()) + \"-\"\n", 622 | " node = queue.dequeue()\n", 623 | "\n", 624 | " if node.left:\n", 625 | " queue.enqueue(node.left)\n", 626 | " if node.right:\n", 627 | " queue.enqueue(node.right)\n", 628 | "\n", 629 | " return traversal\n", 630 | "\n", 631 | "\n", 632 | "tree = BinaryTree(1)\n", 633 | "tree.root.left = Node(2)\n", 634 | "tree.root.right = Node(3)\n", 635 | "tree.root.left.left = Node(4)\n", 636 | "tree.root.left.right = Node(5)\n", 637 | "\n", 638 | "print(tree.print_tree(\"levelorder\"))" 639 | ] 640 | }, 641 | { 642 | "cell_type": "markdown", 643 | "metadata": {}, 644 | "source": [ 645 | "#### Reverse Level-Order Traversal" 646 | ] 647 | }, 648 | { 649 | "cell_type": "markdown", 650 | "metadata": {}, 651 | "source": [ 652 | "**Algorithm**\n", 653 | "\n", 654 | "To solve this problem, we’ll use a queue again just like we did with level-order traversal, but with a slight tweak; we’ll enqueue the right child before the left child. Additionally, we will use a stack. The algorithm starts with enqueuing the root node. As we traverse the tree, we dequeue the nodes from the queue and push them to the stack. After we push a node on to the stack, we check for its children, and if they are present, we enqueue them. This process is repeated until the queue becomes empty. In the end, popping the element from the stack will give us the reverse-order traversal. Let’s step through the algorithm using the illustrations below:" 655 | ] 656 | }, 657 | { 658 | "cell_type": "markdown", 659 | "metadata": {}, 660 | "source": [ 661 | "First, we’ll implement Stack class:" 662 | ] 663 | }, 664 | { 665 | "cell_type": "code", 666 | "execution_count": 41, 667 | "metadata": {}, 668 | "outputs": [], 669 | "source": [ 670 | "class Stack(object):\n", 671 | " def __init__(self):\n", 672 | " self.items = []\n", 673 | "\n", 674 | " def __len__(self):\n", 675 | " return self.size()\n", 676 | " \n", 677 | " def size(self):\n", 678 | " return len(self.items)\n", 679 | "\n", 680 | " def push(self, item):\n", 681 | " self.items.append(item)\n", 682 | "\n", 683 | " def pop(self): \n", 684 | " if not self.is_empty():\n", 685 | " return self.items.pop()\n", 686 | "\n", 687 | " def peek(self):\n", 688 | " if not self.is_empty():\n", 689 | " return self.items[-1]\n", 690 | "\n", 691 | " def is_empty(self):\n", 692 | " return len(self.items) == 0\n", 693 | "\n", 694 | " def __str__(self):\n", 695 | " s = \"\"\n", 696 | " for i in range(len(self.items)):\n", 697 | " s += str(self.items[i].value) + \"-\"\n", 698 | " return s" 699 | ] 700 | }, 701 | { 702 | "cell_type": "markdown", 703 | "metadata": {}, 704 | "source": [ 705 | "The above code is explanatory itself, except in method __str__, we are using a for loop to create a string which iterates through self.items and concatenates them into a string which is returned from the method." 706 | ] 707 | }, 708 | { 709 | "cell_type": "code", 710 | "execution_count": 42, 711 | "metadata": {}, 712 | "outputs": [], 713 | "source": [ 714 | "def reverse_levelorder_print(self, start):\n", 715 | " \n", 716 | " if start is None:\n", 717 | " return \n", 718 | "\n", 719 | " queue = Queue()\n", 720 | " stack = Stack()\n", 721 | " queue.enqueue(start)\n", 722 | "\n", 723 | "\n", 724 | " traversal = \"\"\n", 725 | " while len(queue) > 0:\n", 726 | " node = queue.dequeue()\n", 727 | "\n", 728 | " stack.push(node)\n", 729 | "\n", 730 | " if node.right:\n", 731 | " queue.enqueue(node.right)\n", 732 | " if node.left:\n", 733 | " queue.enqueue(node.left)\n", 734 | " \n", 735 | " while len(stack) > 0:\n", 736 | " node = stack.pop()\n", 737 | " traversal += str(node.value) + \"-\"\n" 738 | ] 739 | }, 740 | { 741 | "cell_type": "markdown", 742 | "metadata": {}, 743 | "source": [ 744 | "Let's add reverse_levelorder_print to the BinaryTree class and also \"reverse_levelorder\" as a traversal_type to print_tree method. Thus, combining the above codes." 745 | ] 746 | }, 747 | { 748 | "cell_type": "code", 749 | "execution_count": null, 750 | "metadata": {}, 751 | "outputs": [], 752 | "source": [ 753 | "class Stack(object):\n", 754 | " def __init__(self):\n", 755 | " self.items = []\n", 756 | "\n", 757 | " def __len__(self):\n", 758 | " return self.size()\n", 759 | " \n", 760 | " def size(self):\n", 761 | " return len(self.items)\n", 762 | "\n", 763 | " def push(self, item):\n", 764 | " self.items.append(item)\n", 765 | "\n", 766 | " def pop(self): \n", 767 | " if not self.is_empty():\n", 768 | " return self.items.pop()\n", 769 | "\n", 770 | " def peek(self):\n", 771 | " if not self.is_empty():\n", 772 | " return self.items[-1]\n", 773 | "\n", 774 | " def is_empty(self):\n", 775 | " return len(self.items) == 0\n", 776 | "\n", 777 | " def __str__(self):\n", 778 | " s = \"\"\n", 779 | " for i in range(len(self.items)):\n", 780 | " s += str(self.items[i].value) + \"-\"\n", 781 | " return s\n", 782 | " \n", 783 | "class Queue(object):\n", 784 | " def __init__(self):\n", 785 | " self.items = []\n", 786 | "\n", 787 | " def enqueue(self, item):\n", 788 | " self.items.insert(0, item)\n", 789 | "\n", 790 | " def dequeue(self):\n", 791 | " if not self.is_empty():\n", 792 | " return self.items.pop()\n", 793 | "\n", 794 | " def is_empty(self):\n", 795 | " return len(self.items) == 0\n", 796 | "\n", 797 | " def peek(self):\n", 798 | " if not self.is_empty():\n", 799 | " return self.items[-1].value\n", 800 | "\n", 801 | " def __len__(self):\n", 802 | " return self.size()\n", 803 | "\n", 804 | " def size(self):\n", 805 | " return len(self.items)\n", 806 | "\n", 807 | "\n", 808 | "class Node(object):\n", 809 | " def __init__(self, value):\n", 810 | " self.value = value\n", 811 | " self.left = None\n", 812 | " self.right = None\n", 813 | "\n", 814 | "\n", 815 | "class BinaryTree(object):\n", 816 | " def __init__(self, root):\n", 817 | " self.root = Node(root)\n", 818 | "\n", 819 | " def print_tree(self, traversal_type):\n", 820 | " if traversal_type == \"preorder\":\n", 821 | " return self.preorder_print(tree.root, \"\")\n", 822 | " elif traversal_type == \"inorder\":\n", 823 | " return self.inorder_print(tree.root, \"\")\n", 824 | " elif traversal_type == \"postorder\":\n", 825 | " return self.postorder_print(tree.root, \"\")\n", 826 | " elif traversal_type == \"levelorder\":\n", 827 | " return self.levelorder_print(tree.root)\n", 828 | " elif traversal_type == \"reverse_levelorder\":\n", 829 | " return self.reverse_levelorder_print(tree.root)\n", 830 | "\n", 831 | " else:\n", 832 | " print(\"Traversal type \" + str(traversal_type) + \" is not supported.\")\n", 833 | " return False\n", 834 | "\n", 835 | " def preorder_print(self, start, traversal):\n", 836 | " \"\"\"Root->Left->Right\"\"\"\n", 837 | " if start:\n", 838 | " traversal += (str(start.value) + \"-\")\n", 839 | " traversal = self.preorder_print(start.left, traversal)\n", 840 | " traversal = self.preorder_print(start.right, traversal)\n", 841 | " return traversal\n", 842 | "\n", 843 | " def inorder_print(self, start, traversal):\n", 844 | " \"\"\"Left->Root->Right\"\"\"\n", 845 | " if start:\n", 846 | " traversal = self.inorder_print(start.left, traversal)\n", 847 | " traversal += (str(start.value) + \"-\")\n", 848 | " traversal = self.inorder_print(start.right, traversal)\n", 849 | " return traversal\n", 850 | "\n", 851 | " def postorder_print(self, start, traversal):\n", 852 | " \"\"\"Left->Right->Root\"\"\"\n", 853 | " if start:\n", 854 | " traversal = self.inorder_print(start.left, traversal)\n", 855 | " traversal = self.inorder_print(start.right, traversal)\n", 856 | " traversal += (str(start.value) + \"-\")\n", 857 | " return traversal\n", 858 | "\n", 859 | " def levelorder_print(self, start):\n", 860 | " if start is None:\n", 861 | " return \n", 862 | "\n", 863 | " queue = Queue()\n", 864 | " queue.enqueue(start)\n", 865 | "\n", 866 | " traversal = \"\"\n", 867 | " while len(queue) > 0:\n", 868 | " traversal += str(queue.peek()) + \"-\"\n", 869 | " node = queue.dequeue()\n", 870 | "\n", 871 | " if node.left:\n", 872 | " queue.enqueue(node.left)\n", 873 | " if node.right:\n", 874 | " queue.enqueue(node.right)\n", 875 | "\n", 876 | " return traversal\n", 877 | "\n", 878 | " def reverse_levelorder_print(self, start):\n", 879 | " if start is None:\n", 880 | " return \n", 881 | "\n", 882 | " queue = Queue()\n", 883 | " stack = Stack()\n", 884 | " queue.enqueue(start)\n", 885 | "\n", 886 | "\n", 887 | " traversal = \"\"\n", 888 | " while len(queue) > 0:\n", 889 | " node = queue.dequeue()\n", 890 | "\n", 891 | " stack.push(node)\n", 892 | "\n", 893 | " if node.right:\n", 894 | " queue.enqueue(node.right)\n", 895 | " if node.left:\n", 896 | " queue.enqueue(node.left)\n", 897 | " \n", 898 | " while len(stack) > 0:\n", 899 | " node = stack.pop()\n", 900 | " traversal += str(node.value) + \"-\"\n", 901 | "\n", 902 | " return traversal\n", 903 | "\n", 904 | "\n", 905 | "\n", 906 | "tree = BinaryTree(1)\n", 907 | "tree.root.left = Node(2)\n", 908 | "tree.root.right = Node(3)\n", 909 | "tree.root.left.left = Node(4)\n", 910 | "tree.root.left.right = Node(5)\n", 911 | "\n", 912 | "print(tree.print_tree(\"reverse_levelorder\"))" 913 | ] 914 | }, 915 | { 916 | "cell_type": "markdown", 917 | "metadata": {}, 918 | "source": [ 919 | "Review the above code carefully, it would make sense. The length is big but the pieces individually are easy to comprehend! Good?" 920 | ] 921 | }, 922 | { 923 | "cell_type": "markdown", 924 | "metadata": {}, 925 | "source": [ 926 | "#### Calculating the Height of a Binary Tree" 927 | ] 928 | }, 929 | { 930 | "cell_type": "markdown", 931 | "metadata": {}, 932 | "source": [ 933 | "We studied before right? let's recall!" 934 | ] 935 | }, 936 | { 937 | "cell_type": "markdown", 938 | "metadata": {}, 939 | "source": [ 940 | "The height of a node is the number of edges on the longest path between that node and a leaf. The height of a leaf node is 0.\n", 941 | "\n", 942 | "Recursively defined, the height of a node is one greater than the max of its right and left children’s height." 943 | ] 944 | }, 945 | { 946 | "cell_type": "markdown", 947 | "metadata": {}, 948 | "source": [ 949 | "#### Algorithm" 950 | ] 951 | }, 952 | { 953 | "cell_type": "markdown", 954 | "metadata": {}, 955 | "source": [ 956 | "In this lesson, we will consider the recursive approach to calculate the height of a tree. The idea is to break down the problem using recursion and traverse through the left and right subtree of a node to calculate the height of that node. Once we get the height of the left and right subtree, we will consider the maximum of the two heights plus one to be the height of the tree." 957 | ] 958 | }, 959 | { 960 | "cell_type": "code", 961 | "execution_count": 47, 962 | "metadata": {}, 963 | "outputs": [], 964 | "source": [ 965 | "def height(self, node):\n", 966 | " if node is None:\n", 967 | " return -1\n", 968 | "\n", 969 | " left_height = self.height(node.left)\n", 970 | " right_height = self.height(node.right)\n", 971 | "\n", 972 | " return 1 + max(left_height, right_height)\n", 973 | " " 974 | ] 975 | }, 976 | { 977 | "cell_type": "markdown", 978 | "metadata": {}, 979 | "source": [ 980 | "Below, the height method is made part of the BinaryTree Class. Write your test cases to verify the height method. A sample test case has been given to you." 981 | ] 982 | }, 983 | { 984 | "cell_type": "code", 985 | "execution_count": 49, 986 | "metadata": {}, 987 | "outputs": [ 988 | { 989 | "name": "stdout", 990 | "output_type": "stream", 991 | "text": [ 992 | "2\n" 993 | ] 994 | } 995 | ], 996 | "source": [ 997 | "class Stack(object):\n", 998 | " def __init__(self):\n", 999 | " self.items = []\n", 1000 | "\n", 1001 | " def __len__(self):\n", 1002 | " return self.size()\n", 1003 | " \n", 1004 | " def size(self):\n", 1005 | " return len(self.items)\n", 1006 | "\n", 1007 | " def push(self, item):\n", 1008 | " self.items.append(item)\n", 1009 | "\n", 1010 | " def pop(self): \n", 1011 | " if not self.is_empty():\n", 1012 | " return self.items.pop()\n", 1013 | "\n", 1014 | " def peek(self):\n", 1015 | " if not self.is_empty():\n", 1016 | " return self.items[-1]\n", 1017 | "\n", 1018 | " def is_empty(self):\n", 1019 | " return len(self.items) == 0\n", 1020 | "\n", 1021 | " def __str__(self):\n", 1022 | " s = \"\"\n", 1023 | " for i in range(len(self.items)):\n", 1024 | " s += str(self.items[i].value) + \"-\"\n", 1025 | " return s\n", 1026 | "\n", 1027 | "\n", 1028 | "class Queue(object):\n", 1029 | " def __init__(self):\n", 1030 | " self.items = []\n", 1031 | "\n", 1032 | " def __len__(self):\n", 1033 | " return self.size()\n", 1034 | "\n", 1035 | " def enqueue(self, item):\n", 1036 | " self.items.insert(0, item)\n", 1037 | "\n", 1038 | " def dequeue(self):\n", 1039 | " if not self.is_empty():\n", 1040 | " return self.items.pop()\n", 1041 | "\n", 1042 | " def size(self):\n", 1043 | " return len(self.items)\n", 1044 | "\n", 1045 | " def is_empty(self):\n", 1046 | " return len(self.items) == 0\n", 1047 | "\n", 1048 | " def peek(self):\n", 1049 | " if not self.is_empty():\n", 1050 | " return self.items[-1].value\n", 1051 | "\n", 1052 | "\n", 1053 | "class Node(object):\n", 1054 | " def __init__(self, value):\n", 1055 | " self.value = value\n", 1056 | " self.left = None\n", 1057 | " self.right = None\n", 1058 | "\n", 1059 | "\n", 1060 | "class BinaryTree(object):\n", 1061 | " def __init__(self, root):\n", 1062 | " self.root = Node(root)\n", 1063 | "\n", 1064 | " def search(self, find_val, traversal_type):\n", 1065 | " if traversal_type == \"preorder\":\n", 1066 | " return self.preorder_search(tree.root, find_val)\n", 1067 | " elif traversal_type == \"inorder\":\n", 1068 | " return self.inorder_search(tree.root, find_val)\n", 1069 | " elif traversal_type == \"postorder\":\n", 1070 | " return self.postorder_search(tree.root, find_val)\n", 1071 | " else:\n", 1072 | " print(\"Traversal type \" + str(traversal_type) + \" not recognized.\")\n", 1073 | " return False\n", 1074 | "\n", 1075 | " def print_tree(self, traversal_type):\n", 1076 | " # Recursive traversals\n", 1077 | " if traversal_type == \"preorder\":\n", 1078 | " return self.preorder_print(tree.root, \"\")\n", 1079 | " elif traversal_type == \"inorder\":\n", 1080 | " return self.inorder_print(tree.root, \"\")\n", 1081 | " elif traversal_type == \"postorder\":\n", 1082 | " return self.postorder_print(tree.root, \"\")\n", 1083 | "\n", 1084 | " # Iterative traversals\n", 1085 | " elif traversal_type == \"levelorder\":\n", 1086 | " return self.levelorder_print(tree.root)\n", 1087 | " elif traversal_type == \"inorder_iterative\":\n", 1088 | " return self.inorder_iterative(tree.root)\n", 1089 | " elif traversal_type == \"preorder_iterative\":\n", 1090 | " return self.preorder_iterative(tree.root)\n", 1091 | " elif traversal_type == \"postorder_iterative\":\n", 1092 | " return self.postorder_iterative(tree.root)\n", 1093 | " else:\n", 1094 | " print(\"Traversal type \" + str(traversal_type) + \" not recognized.\")\n", 1095 | " return False\n", 1096 | "\n", 1097 | " def levelorder_print(self, start):\n", 1098 | " if start is None:\n", 1099 | " return\n", 1100 | " queue = Queue()\n", 1101 | " queue.enqueue(start)\n", 1102 | "\n", 1103 | " traversal = \"\"\n", 1104 | " while len(queue) > 0:\n", 1105 | " traversal += str(queue.peek()) + \"-\"\n", 1106 | " node = queue.dequeue()\n", 1107 | "\n", 1108 | " if node.left:\n", 1109 | " queue.enqueue(node.left)\n", 1110 | " if node.right:\n", 1111 | " queue.enqueue(node.right)\n", 1112 | "\n", 1113 | " return traversal\n", 1114 | "\n", 1115 | " def preorder_search(self, start, find_val):\n", 1116 | " if start:\n", 1117 | " if start.value == find_val:\n", 1118 | " return True\n", 1119 | " else:\n", 1120 | " return self.preorder_search(start.left, find_val) or \\\n", 1121 | " self.preorder_search(start.right, find_val)\n", 1122 | " return False\n", 1123 | "\n", 1124 | " def preorder_print(self, start, traversal):\n", 1125 | " \"\"\"Root->Left-Right\"\"\"\n", 1126 | " if start:\n", 1127 | " traversal += (str(start.value) + \"-\")\n", 1128 | " traversal = self.preorder_print(start.left, traversal)\n", 1129 | " traversal = self.preorder_print(start.right, traversal)\n", 1130 | " return traversal\n", 1131 | "\n", 1132 | " def inorder_print(self, start, traversal):\n", 1133 | " \"\"\"Left->Root->Right\"\"\"\n", 1134 | " if start:\n", 1135 | " traversal = self.inorder_print(start.left, traversal)\n", 1136 | " traversal += (str(start.value) + \"-\")\n", 1137 | " traversal = self.inorder_print(start.right, traversal)\n", 1138 | " return traversal\n", 1139 | "\n", 1140 | " def postorder_print(self, start, traversal):\n", 1141 | " \"\"\"Left->Right->Root\"\"\"\n", 1142 | " if start:\n", 1143 | " traversal = self.postorder_print(start.left, traversal)\n", 1144 | " traversal = self.postorder_print(start.right, traversal)\n", 1145 | " traversal += (str(start.value) + \"-\")\n", 1146 | " return traversal\n", 1147 | "\n", 1148 | " def height(self, node):\n", 1149 | " if node is None:\n", 1150 | " return -1\n", 1151 | "\n", 1152 | " left_height = self.height(node.left)\n", 1153 | " right_height = self.height(node.right)\n", 1154 | "\n", 1155 | " return 1 + max(left_height, right_height)\n", 1156 | "\n", 1157 | "\n", 1158 | "# Calculate height of binary tree:\n", 1159 | "# 1\n", 1160 | "# / \\\n", 1161 | "# 2 3\n", 1162 | "# / \\\n", 1163 | "# 4 5\n", 1164 | "# \n", 1165 | "tree = BinaryTree(1)\n", 1166 | "tree.root.left = Node(2)\n", 1167 | "tree.root.right = Node(3)\n", 1168 | "tree.root.left.left = Node(4)\n", 1169 | "tree.root.left.right = Node(5)\n", 1170 | "\n", 1171 | "print(tree.height(tree.root))" 1172 | ] 1173 | }, 1174 | { 1175 | "cell_type": "markdown", 1176 | "metadata": {}, 1177 | "source": [ 1178 | "**Hurray! This sums up our content on Binary Trees.*" 1179 | ] 1180 | }, 1181 | { 1182 | "cell_type": "markdown", 1183 | "metadata": {}, 1184 | "source": [ 1185 | "**Time for a challenge!!**" 1186 | ] 1187 | }, 1188 | { 1189 | "cell_type": "markdown", 1190 | "metadata": {}, 1191 | "source": [ 1192 | "#### Calculating the Size of a Tree" 1193 | ] 1194 | }, 1195 | { 1196 | "cell_type": "markdown", 1197 | "metadata": {}, 1198 | "source": [ 1199 | "The size of the tree is the total number of nodes in a tree." 1200 | ] 1201 | }, 1202 | { 1203 | "cell_type": "markdown", 1204 | "metadata": {}, 1205 | "source": [ 1206 | "Iterative Approach" 1207 | ] 1208 | }, 1209 | { 1210 | "cell_type": "code", 1211 | "execution_count": 53, 1212 | "metadata": {}, 1213 | "outputs": [], 1214 | "source": [ 1215 | "def size(self):\n", 1216 | " if self.root is None:\n", 1217 | " return 0\n", 1218 | " size=1\n", 1219 | " stack=Stack()\n", 1220 | " stack.push(self.root)\n", 1221 | " while stack:\n", 1222 | " node=stack.pop()\n", 1223 | " if node.left:\n", 1224 | " stack.push(node.left)\n", 1225 | " size+=1\n", 1226 | " if node.right:\n", 1227 | " stack.push(node.right)\n", 1228 | " size+=1\n", 1229 | " return size\n", 1230 | " \n", 1231 | " " 1232 | ] 1233 | }, 1234 | { 1235 | "cell_type": "markdown", 1236 | "metadata": {}, 1237 | "source": [ 1238 | "Recursive Approach" 1239 | ] 1240 | }, 1241 | { 1242 | "cell_type": "code", 1243 | "execution_count": 52, 1244 | "metadata": {}, 1245 | "outputs": [], 1246 | "source": [ 1247 | "def size_(self,node):\n", 1248 | " if node is None:\n", 1249 | " return 0\n", 1250 | " return 1 + self.size_(node.left)+ self.size_(node.right)" 1251 | ] 1252 | }, 1253 | { 1254 | "cell_type": "markdown", 1255 | "metadata": {}, 1256 | "source": [ 1257 | "Let's combine the code and test!" 1258 | ] 1259 | }, 1260 | { 1261 | "cell_type": "code", 1262 | "execution_count": 54, 1263 | "metadata": {}, 1264 | "outputs": [ 1265 | { 1266 | "name": "stdout", 1267 | "output_type": "stream", 1268 | "text": [ 1269 | "5\n", 1270 | "5\n" 1271 | ] 1272 | } 1273 | ], 1274 | "source": [ 1275 | "class Stack(object):\n", 1276 | " def __init__(self):\n", 1277 | " self.items = []\n", 1278 | "\n", 1279 | " def __len__(self):\n", 1280 | " return self.size()\n", 1281 | " \n", 1282 | " def size(self):\n", 1283 | " return len(self.items)\n", 1284 | "\n", 1285 | " def push(self, item):\n", 1286 | " self.items.append(item)\n", 1287 | "\n", 1288 | " def pop(self): \n", 1289 | " if not self.is_empty():\n", 1290 | " return self.items.pop()\n", 1291 | "\n", 1292 | " def peek(self):\n", 1293 | " if not self.is_empty():\n", 1294 | " return self.items[-1]\n", 1295 | "\n", 1296 | " def is_empty(self):\n", 1297 | " return len(self.items) == 0\n", 1298 | "\n", 1299 | " def __str__(self):\n", 1300 | " s = \"\"\n", 1301 | " for i in range(len(self.items)):\n", 1302 | " s += str(self.items[i].value) + \"-\"\n", 1303 | " return s\n", 1304 | "\n", 1305 | "\n", 1306 | "class Queue(object):\n", 1307 | " def __init__(self):\n", 1308 | " self.items = []\n", 1309 | "\n", 1310 | " def __len__(self):\n", 1311 | " return self.size()\n", 1312 | "\n", 1313 | " def enqueue(self, item):\n", 1314 | " self.items.insert(0, item)\n", 1315 | "\n", 1316 | " def dequeue(self):\n", 1317 | " if not self.is_empty():\n", 1318 | " return self.items.pop()\n", 1319 | "\n", 1320 | " def size(self):\n", 1321 | " return len(self.items)\n", 1322 | "\n", 1323 | " def is_empty(self):\n", 1324 | " return len(self.items) == 0\n", 1325 | "\n", 1326 | " def peek(self):\n", 1327 | " if not self.is_empty():\n", 1328 | " return self.items[-1].value\n", 1329 | "\n", 1330 | "\n", 1331 | "class Node(object):\n", 1332 | " def __init__(self, value):\n", 1333 | " self.value = value\n", 1334 | " self.left = None\n", 1335 | " self.right = None\n", 1336 | "\n", 1337 | "\n", 1338 | "class BinaryTree(object):\n", 1339 | " def __init__(self, root):\n", 1340 | " self.root = Node(root)\n", 1341 | "\n", 1342 | " def search(self, find_val, traversal_type):\n", 1343 | " if traversal_type == \"preorder\":\n", 1344 | " return self.preorder_search(tree.root, find_val)\n", 1345 | " elif traversal_type == \"inorder\":\n", 1346 | " return self.inorder_search(tree.root, find_val)\n", 1347 | " elif traversal_type == \"postorder\":\n", 1348 | " return self.postorder_search(tree.root, find_val)\n", 1349 | " else:\n", 1350 | " print(\"Traversal type \" + str(traversal_type) + \" not recognized.\")\n", 1351 | " return False\n", 1352 | "\n", 1353 | " def print_tree(self, traversal_type):\n", 1354 | " # Recursive traversals\n", 1355 | " if traversal_type == \"preorder\":\n", 1356 | " return self.preorder_print(tree.root, \"\")\n", 1357 | " elif traversal_type == \"inorder\":\n", 1358 | " return self.inorder_print(tree.root, \"\")\n", 1359 | " elif traversal_type == \"postorder\":\n", 1360 | " return self.postorder_print(tree.root, \"\")\n", 1361 | "\n", 1362 | " # Iterative traversals\n", 1363 | " elif traversal_type == \"levelorder\":\n", 1364 | " return self.levelorder_print(tree.root)\n", 1365 | " elif traversal_type == \"inorder_iterative\":\n", 1366 | " return self.inorder_iterative(tree.root)\n", 1367 | " elif traversal_type == \"preorder_iterative\":\n", 1368 | " return self.preorder_iterative(tree.root)\n", 1369 | " elif traversal_type == \"postorder_iterative\":\n", 1370 | " return self.postorder_iterative(tree.root)\n", 1371 | " else:\n", 1372 | " print(\"Traversal type \" + str(traversal_type) + \" not recognized.\")\n", 1373 | " return False\n", 1374 | "\n", 1375 | " def levelorder_print(self, start):\n", 1376 | " if start is None:\n", 1377 | " return\n", 1378 | " queue = Queue()\n", 1379 | " queue.enqueue(start)\n", 1380 | "\n", 1381 | " traversal = \"\"\n", 1382 | " while len(queue) > 0:\n", 1383 | " traversal += str(queue.peek()) + \"-\"\n", 1384 | " node = queue.dequeue()\n", 1385 | "\n", 1386 | " if node.left:\n", 1387 | " queue.enqueue(node.left)\n", 1388 | " if node.right:\n", 1389 | " queue.enqueue(node.right)\n", 1390 | "\n", 1391 | " return traversal\n", 1392 | "\n", 1393 | " def preorder_search(self, start, find_val):\n", 1394 | " if start:\n", 1395 | " if start.value == find_val:\n", 1396 | " return True\n", 1397 | " else:\n", 1398 | " return self.preorder_search(start.left, find_val) or \\\n", 1399 | " self.preorder_search(start.right, find_val)\n", 1400 | " return False\n", 1401 | "\n", 1402 | " def preorder_print(self, start, traversal):\n", 1403 | " \"\"\"Root->Left-Right\"\"\"\n", 1404 | " if start:\n", 1405 | " traversal += (str(start.value) + \"-\")\n", 1406 | " traversal = self.preorder_print(start.left, traversal)\n", 1407 | " traversal = self.preorder_print(start.right, traversal)\n", 1408 | " return traversal\n", 1409 | "\n", 1410 | " def inorder_print(self, start, traversal):\n", 1411 | " \"\"\"Left->Root->Right\"\"\"\n", 1412 | " if start:\n", 1413 | " traversal = self.inorder_print(start.left, traversal)\n", 1414 | " traversal += (str(start.value) + \"-\")\n", 1415 | " traversal = self.inorder_print(start.right, traversal)\n", 1416 | " return traversal\n", 1417 | "\n", 1418 | " def postorder_print(self, start, traversal):\n", 1419 | " \"\"\"Left->Right->Root\"\"\"\n", 1420 | " if start:\n", 1421 | " traversal = self.postorder_print(start.left, traversal)\n", 1422 | " traversal = self.postorder_print(start.right, traversal)\n", 1423 | " traversal += (str(start.value) + \"-\")\n", 1424 | " return traversal\n", 1425 | "\n", 1426 | " def preorder_iterative(self, start):\n", 1427 | " stack = Stack()\n", 1428 | "\n", 1429 | " cur = start\n", 1430 | " is_done = False\n", 1431 | "\n", 1432 | " traversal = \"\"\n", 1433 | " while not is_done:\n", 1434 | " if cur is not None:\n", 1435 | " traversal += str(cur.value) + \"-\"\n", 1436 | " stack.push(cur)\n", 1437 | " cur = cur.left\n", 1438 | " else:\n", 1439 | " if len(stack) > 0:\n", 1440 | " cur = stack.pop()\n", 1441 | " cur = cur.right\n", 1442 | " else:\n", 1443 | " is_done = True\n", 1444 | " return traversal\n", 1445 | "\n", 1446 | " def inorder_iterative(self, start):\n", 1447 | " s = Stack()\n", 1448 | "\n", 1449 | " cur = start\n", 1450 | " is_done = False\n", 1451 | "\n", 1452 | " traversal = \"\"\n", 1453 | " while not is_done:\n", 1454 | " if cur is not None:\n", 1455 | " s.push(cur)\n", 1456 | " cur = cur.left\n", 1457 | " else:\n", 1458 | " if len(s) > 0:\n", 1459 | " cur = s.pop()\n", 1460 | " traversal += str(cur.value) + \"-\"\n", 1461 | " cur = cur.right\n", 1462 | " else:\n", 1463 | " is_done = True\n", 1464 | " return traversal\n", 1465 | "\n", 1466 | " def postorder_iterative(self, start):\n", 1467 | " s = Stack()\n", 1468 | "\n", 1469 | " cur = start\n", 1470 | " is_done = False\n", 1471 | "\n", 1472 | " traversal = \"\"\n", 1473 | " while not is_done:\n", 1474 | " \n", 1475 | " if cur is not None:\n", 1476 | " s.push(cur)\n", 1477 | " cur = cur.left\n", 1478 | " else:\n", 1479 | " if len(s) > 0:\n", 1480 | " cur = s.pop()\n", 1481 | " traversal += str(cur.value) + \"-\"\n", 1482 | " cur = cur.right\n", 1483 | " else:\n", 1484 | " is_done = True\n", 1485 | "\n", 1486 | " return traversal\n", 1487 | "\n", 1488 | " def height(self, node):\n", 1489 | " if node is None:\n", 1490 | " return -1\n", 1491 | " left_height = self.height(node.left)\n", 1492 | " right_height = self.height(node.right)\n", 1493 | "\n", 1494 | " return 1 + max(left_height, right_height)\n", 1495 | "\n", 1496 | " def size_(self, node):\n", 1497 | " if node is None:\n", 1498 | " return 0\n", 1499 | " return 1 + self.size_(node.left) + self.size_(node.right)\n", 1500 | "\n", 1501 | " def size(self):\n", 1502 | " if self.root is None:\n", 1503 | " return 0\n", 1504 | "\n", 1505 | " stack = Stack()\n", 1506 | " stack.push(self.root)\n", 1507 | " size = 1\n", 1508 | " while stack:\n", 1509 | " node = stack.pop()\n", 1510 | " if node.left:\n", 1511 | " size += 1\n", 1512 | " stack.push(node.left)\n", 1513 | " if node.right:\n", 1514 | " size += 1\n", 1515 | " stack.push(node.right)\n", 1516 | " return size\n", 1517 | "\n", 1518 | "# Calculate size of binary tree:\n", 1519 | "# 1\n", 1520 | "# / \\\n", 1521 | "# 2 3\n", 1522 | "# / \\\n", 1523 | "# 4 5\n", 1524 | "# \n", 1525 | "tree = BinaryTree(1)\n", 1526 | "tree.root.left = Node(2)\n", 1527 | "tree.root.right = Node(3)\n", 1528 | "tree.root.left.left = Node(4)\n", 1529 | "tree.root.left.right = Node(5)\n", 1530 | "\n", 1531 | "print(tree.size())\n", 1532 | "print(tree.size_(tree.root))" 1533 | ] 1534 | }, 1535 | { 1536 | "cell_type": "markdown", 1537 | "metadata": {}, 1538 | "source": [ 1539 | "Done and dusted!!" 1540 | ] 1541 | } 1542 | ], 1543 | "metadata": { 1544 | "kernelspec": { 1545 | "display_name": "Python 3", 1546 | "language": "python", 1547 | "name": "python3" 1548 | }, 1549 | "language_info": { 1550 | "codemirror_mode": { 1551 | "name": "ipython", 1552 | "version": 3 1553 | }, 1554 | "file_extension": ".py", 1555 | "mimetype": "text/x-python", 1556 | "name": "python", 1557 | "nbconvert_exporter": "python", 1558 | "pygments_lexer": "ipython3", 1559 | "version": "3.7.4" 1560 | } 1561 | }, 1562 | "nbformat": 4, 1563 | "nbformat_minor": 4 1564 | } 1565 | -------------------------------------------------------------------------------- /Python Data Structures- Binary Search Trees.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Insertion and Search" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "A binary search tree is a tree data structure in which nodes are arranged according to the BST property which is as follows:\n", 15 | "\n", 16 | "The value of the left child of any node in a binary search tree will be less than whatever value we have in that node, and the value of the right child of a node will be greater than the value in that node." 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "# Important CHALLENGE: IS BST OR NOT?\n" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 1, 29 | "metadata": {}, 30 | "outputs": [], 31 | "source": [ 32 | "def is_bst_satisfied(self):\n", 33 | " def helper(node, lower=float('-inf'), upper=float('inf')):\n", 34 | " if not node:\n", 35 | " return True\n", 36 | " \n", 37 | " val = node.data\n", 38 | " if val <= lower or val >= upper:\n", 39 | " return False\n", 40 | "\n", 41 | " if not helper(node.right, val, upper):\n", 42 | " return False\n", 43 | " if not helper(node.left, lower, val):\n", 44 | " return False\n", 45 | " return True\n", 46 | "\n", 47 | " return helper(self.root)" 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "metadata": {}, 53 | "source": [ 54 | "In the is_bst_satisfied method, we define an inner method on line 2,helper, which takes node, lower and upper as input parameters. On line 3, we have the base case which caters to an empty tree or a None node. If node is None, True is returned from the method on line 4. Otherwise, the execution proceeds to line 6 where val is made equal to node.data.\n", 55 | "\n", 56 | "Next, we check if val is less or equal to lower or if val is greater or equal to upper on line 7. If any of the two conditions is True, False is returned from the method on line 8. This is because the value of the current node should be greater than all the values of the children in the left subtree, and it should be less than all the values of the children in the right subtree.\n", 57 | "\n", 58 | "Now that we have checked the BST property for the current node, it’s time to check it for the subtrees. On line 10, we make a recursive call to the right subtree of the current node.node.right is passed as node, val is passed as lower while upper stays the same.lower is now the lower bound for the right subtree as all the children in the right subtree have to be greater than the value of the current node. If the recursive call returns False, the condition on line 10 will evaluate to True and False will be returned from the method.\n", 59 | "\n", 60 | "Similarly, the left subtree is evaluated through a recursive call on line 12. Now val is passed as upper for the recursive call as all the children in the left subtree have to be less than the value of the current node.\n", 61 | "\n", 62 | "If none of the conditions before line 14 evaluate to True, True is returned on line 14 declaring that the BST property is satisfied." 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "metadata": {}, 68 | "source": [ 69 | "Complete code below!" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": 3, 75 | "metadata": {}, 76 | "outputs": [ 77 | { 78 | "name": "stdout", 79 | "output_type": "stream", 80 | "text": [ 81 | "True\n", 82 | "False\n" 83 | ] 84 | } 85 | ], 86 | "source": [ 87 | "class Node(object):\n", 88 | " def __init__(self, data):\n", 89 | " self.data = data\n", 90 | " self.left = None\n", 91 | " self.right = None\n", 92 | "\n", 93 | "\n", 94 | "class BST(object):\n", 95 | " def __init__(self, root):\n", 96 | " self.root = Node(root)\n", 97 | "\n", 98 | " def insert(self, data):\n", 99 | " if self.root is None:\n", 100 | " self.root = Node(data)\n", 101 | " else:\n", 102 | " self._insert(data, self.root)\n", 103 | "\n", 104 | " def _insert(self, data, cur_node):\n", 105 | " if data < cur_node.data:\n", 106 | " if cur_node.left is None:\n", 107 | " cur_node.left = Node(data)\n", 108 | " else:\n", 109 | " self._insert(data, cur_node.left)\n", 110 | "\n", 111 | " elif data > cur_node.data:\n", 112 | " if cur_node.right is None:\n", 113 | " cur_node.right = Node(data)\n", 114 | " else:\n", 115 | " self._insert(data, cur_node.right)\n", 116 | " else:\n", 117 | " print(\"Value already in tree!\")\n", 118 | "\n", 119 | " def inorder_print_tree(self):\n", 120 | " if self.root:\n", 121 | " self._inorder_print_tree(self.root)\n", 122 | "\n", 123 | " def _inorder_print_tree(self, cur_node):\n", 124 | " if cur_node:\n", 125 | " self._inorder_print_tree(cur_node.left)\n", 126 | " print(str(cur_node.data))\n", 127 | " self._inorder_print_tree(cur_node.right)\n", 128 | "\n", 129 | " def find(self, data):\n", 130 | " if self.root:\n", 131 | " is_found = self._find(data, self.root)\n", 132 | " if is_found:\n", 133 | " return True\n", 134 | " return False\n", 135 | " else:\n", 136 | " return None\n", 137 | "\n", 138 | " def _find(self, data, cur_node):\n", 139 | " if data > cur_node.data and cur_node.right:\n", 140 | " return self._find(data, cur_node.right)\n", 141 | " elif data < cur_node.data and cur_node.left:\n", 142 | " return self._find(data, cur_node.left)\n", 143 | " if data == cur_node.data:\n", 144 | " return True\n", 145 | "\n", 146 | " def is_bst_satisfied(self):\n", 147 | " def helper(node, lower=float('-inf'), upper=float('inf')):\n", 148 | " if not node:\n", 149 | " return True\n", 150 | " \n", 151 | " val = node.data\n", 152 | " if val <= lower or val >= upper:\n", 153 | " return False\n", 154 | "\n", 155 | " if not helper(node.right, val, upper):\n", 156 | " return False\n", 157 | " if not helper(node.left, lower, val):\n", 158 | " return False\n", 159 | " return True\n", 160 | "\n", 161 | " return helper(self.root) \n", 162 | "\n", 163 | "bst = BST(4)\n", 164 | "bst.insert(2)\n", 165 | "bst.insert(8)\n", 166 | "bst.insert(5)\n", 167 | "bst.insert(10)\n", 168 | "\n", 169 | "tree = BST(1)\n", 170 | "tree.root.left = Node(2)\n", 171 | "tree.root.right = Node(3)\n", 172 | "tree.root.left.left = Node(4)\n", 173 | "tree.root.left.right = Node(5)\n", 174 | "tree.root.right.left = Node(6)\n", 175 | "tree.root.right.right = Node(7)\n", 176 | "tree.root.right.right.right = Node(8)\n", 177 | "\n", 178 | "print(bst.is_bst_satisfied())\n", 179 | "print(tree.is_bst_satisfied())" 180 | ] 181 | }, 182 | { 183 | "cell_type": "markdown", 184 | "metadata": {}, 185 | "source": [ 186 | "### That was it for data structure! Let's go into:\n", 187 | "\n", 188 | "# ALGORITHMS" 189 | ] 190 | }, 191 | { 192 | "cell_type": "markdown", 193 | "metadata": {}, 194 | "source": [ 195 | "**Solution 1- Binary Search (Iterative)**\n", 196 | "\n", 197 | "Binary search assumes that the array on which the search will take place is sorted in ascending order." 198 | ] 199 | }, 200 | { 201 | "cell_type": "markdown", 202 | "metadata": {}, 203 | "source": [ 204 | "In binary search, the target element is compared with the middle element of the array following which the next chunk of the array to be searched is decided. If the target matches the middle element, we are successful. Otherwise, since the array is sorted, if the target is smaller than the middle element, it could only be in the left half of the array. Alternatively, if the target is greater than the middle element, it could be in the right half of the array. So, we exclude one half of the array from the further search and repeat the same strategy to the remaining half.\n", 205 | "\n", 206 | "Let’s jump to the code below so you get a clearer idea of binary search." 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "execution_count": 5, 212 | "metadata": {}, 213 | "outputs": [], 214 | "source": [ 215 | "def binary_search_iterative(data,target):\n", 216 | " low= 0\n", 217 | " high=len(data)-1\n", 218 | " \n", 219 | " while low<=high:\n", 220 | " mid=(low+high)//2\n", 221 | " if target==data[mid]:\n", 222 | " return True\n", 223 | " elif target< data[mid]:\n", 224 | " high=mid-1\n", 225 | " else:\n", 226 | " low=mid+1\n", 227 | " return False" 228 | ] 229 | }, 230 | { 231 | "cell_type": "markdown", 232 | "metadata": {}, 233 | "source": [ 234 | "We keep dividing the array into halves in the binary search instead of iterating through all the elements to search for the target element. This implies that it takes O(log n) steps to divide into halves until we reach a single element. As a result, the worst-case time complexity of a binary search is O(log n)." 235 | ] 236 | }, 237 | { 238 | "cell_type": "markdown", 239 | "metadata": {}, 240 | "source": [ 241 | "**Solution 2 - Binary Search (Recursive)**" 242 | ] 243 | }, 244 | { 245 | "cell_type": "code", 246 | "execution_count": 7, 247 | "metadata": {}, 248 | "outputs": [], 249 | "source": [ 250 | "def binary_search_recursive(data, target, low, high):\n", 251 | " if low>high:\n", 252 | " return False\n", 253 | " else:\n", 254 | " mid=(low+high)//2\n", 255 | " if target==data[mid]:\n", 256 | " return True\n", 257 | " if target< data[mid]:\n", 258 | " return binary_search_recursive(data, target, low, mid-1)\n", 259 | " else:\n", 260 | " return binary_search_recursive(data, target, mid+1, high)" 261 | ] 262 | }, 263 | { 264 | "cell_type": "markdown", 265 | "metadata": {}, 266 | "source": [ 267 | "- In the recursive approach, in addition to data and target, low and high are also passed as input parameters to binary_search_recursive. This is to help us code our base case. The base case for this recursive function will be when low becomes greater than high. If the base case turns out to be True, False is returned from the function to end the recursive calls (lines 2-3). \n", 268 | "\n", 269 | "\n", 270 | "- On the other hand, if low is less than or equal to high, execution jumps to line 5 where mid is calculated in the same way as in the iterative function. If target is equal to data[mid], True is returned (line 7). If not, then the condition on line 8 is evaluated. If target is less than data[mid], we make a recursive call to binary_search_recursive and pass mid - 1 which is the high in the scope of the next recursive call. This will reduce the search span as it will be halved with each recursive call. Similarly, if target is greater than data[mid], low needs to be adjusted and so we pass mid + 1 to the recursive call on line 11 which is low in the next recursive call.\n", 271 | "\n", 272 | "\n", 273 | "- We keep dividing the array into halves with recursive calls until the base case is reached. As every recursive call takes constant time, the worst-case time complexity of the recursive approach is also O(log n)O(logn)." 274 | ] 275 | }, 276 | { 277 | "cell_type": "markdown", 278 | "metadata": {}, 279 | "source": [ 280 | "**Complete Implementation**" 281 | ] 282 | }, 283 | { 284 | "cell_type": "code", 285 | "execution_count": 8, 286 | "metadata": {}, 287 | "outputs": [ 288 | { 289 | "name": "stdout", 290 | "output_type": "stream", 291 | "text": [ 292 | "True\n", 293 | "True\n" 294 | ] 295 | } 296 | ], 297 | "source": [ 298 | "# Linear Search\n", 299 | "def linear_search(data, target):\t\n", 300 | "\tfor i in range(len(data)):\n", 301 | "\t\tif data[i] == target:\n", 302 | "\t\t\treturn True\n", 303 | "\treturn False\n", 304 | "\n", 305 | "# Iterative Binary Search \n", 306 | "def binary_search_iterative(data, target):\n", 307 | "\tlow = 0\n", 308 | "\thigh = len(data) - 1\n", 309 | "\n", 310 | "\twhile low <= high:\n", 311 | "\t\tmid = (low + high) // 2\n", 312 | "\t\tif target == data[mid]:\n", 313 | "\t\t\treturn True\n", 314 | "\t\telif target < data[mid]:\n", 315 | "\t\t\thigh = mid - 1\n", 316 | "\t\telse:\n", 317 | "\t\t\tlow = mid + 1\n", 318 | "\treturn False \n", 319 | "\n", 320 | "# Recursive Binary Search \n", 321 | "def binary_search_recursive(data, target, low, high):\n", 322 | "\tif low > high:\n", 323 | "\t\treturn False\n", 324 | "\telse:\n", 325 | "\t\tmid = (low + high) // 2\n", 326 | "\t\tif target == data[mid]:\n", 327 | "\t\t\treturn True\n", 328 | "\t\telif target < data[mid]:\n", 329 | "\t\t\treturn binary_search_recursive(data, target, low, mid-1)\n", 330 | "\t\telse:\n", 331 | "\t\t\treturn binary_search_recursive(data, target, mid+1, high)\n", 332 | "\n", 333 | "\n", 334 | "data = [2,4,5,7,8,9,12,14,17,19,22,25,27,28,33,37]\n", 335 | "target = 37\n", 336 | "\n", 337 | "print(binary_search_recursive(data, target, 0, len(data)-1))\n", 338 | "print(binary_search_iterative(data, target))" 339 | ] 340 | }, 341 | { 342 | "cell_type": "markdown", 343 | "metadata": {}, 344 | "source": [ 345 | "# Find the Closest Number" 346 | ] 347 | }, 348 | { 349 | "cell_type": "markdown", 350 | "metadata": {}, 351 | "source": [ 352 | "We will be given a sorted array and a target number. Our goal is to find a number in the array that is closest to the target number. We will be making use of a binary search to solve this problem. The array may contain duplicate values and negative numbers." 353 | ] 354 | }, 355 | { 356 | "cell_type": "code", 357 | "execution_count": 11, 358 | "metadata": {}, 359 | "outputs": [], 360 | "source": [ 361 | "def find_closest_num(A,target):\n", 362 | " min_diff=float(\"inf\")\n", 363 | " low=0\n", 364 | " high=len(A)-1\n", 365 | " closest_num=None\n", 366 | " \n", 367 | " #Let's handle edge cases\n", 368 | " if len(A)==0:\n", 369 | " return None\n", 370 | " if len(A)==1:\n", 371 | " return A[0]\n", 372 | " \n", 373 | " while low<=high:\n", 374 | " mid=(low+high)//2\n", 375 | " \n", 376 | " #make sure to not go out of bounds\n", 377 | " if mid+10:\n", 381 | " min_diff_left=abs(A[mid-1]-target)\n", 382 | " \n", 383 | " #check if current difference was less than previous encountered data\n", 384 | " \n", 385 | " if min_diff_righttarget:\n", 396 | " high=mid-1\n", 397 | " elif A[mid] mid:\n", 467 | " high = mid - 1\n", 468 | " else:\n", 469 | " return A[mid]\n", 470 | " return None" 471 | ] 472 | }, 473 | { 474 | "cell_type": "markdown", 475 | "metadata": {}, 476 | "source": [ 477 | "As we have employed a binary search to write the above code, the time complexity for the code above is O(log n) while the space complexity is O(1)." 478 | ] 479 | }, 480 | { 481 | "cell_type": "markdown", 482 | "metadata": {}, 483 | "source": [ 484 | "# Find Bitonic Peak" 485 | ] 486 | }, 487 | { 488 | "cell_type": "markdown", 489 | "metadata": {}, 490 | "source": [ 491 | "We will be given an array that is bitonically sorted, an array that starts off with increasing terms and then concludes with decreasing terms. In any such sequence, there is a “peak” element which is the largest element in the sequence. We will be writing a solution to help us find the peak element of a bitonic sequence." 492 | ] 493 | }, 494 | { 495 | "cell_type": "code", 496 | "execution_count": 29, 497 | "metadata": {}, 498 | "outputs": [], 499 | "source": [ 500 | "def find_highest_number(A):\n", 501 | " low = 0\n", 502 | " high = len(A) - 1\n", 503 | "\n", 504 | " # Require at least 3 elements for a bitonic sequence.\n", 505 | " if len(A) < 3:\n", 506 | " return None\n", 507 | "\n", 508 | " while low <= high:\n", 509 | " mid = (low + high)//2\n", 510 | " \n", 511 | " mid_left=A[mid-1] if mid-1>0 else float(\"-inf\")\n", 512 | " mid_right=A[mid+1] if mid+1A[mid]:\n", 515 | " low=mid+1\n", 516 | " elif mid_left>A[mid] and mid_right target:\n", 597 | " high = mid - 1\n", 598 | " else:\n", 599 | " if mid - 1 < 0:\n", 600 | " return mid\n", 601 | " if A[mid - 1] != target:\n", 602 | " return mid\n", 603 | " high = mid - 1\n", 604 | "\n", 605 | "A = [-14, -10, 2, 108, 108, 243, 285, 285, 285, 401]\n", 606 | "target = 108\n", 607 | "x = find(A, target)\n", 608 | "print(x)" 609 | ] 610 | }, 611 | { 612 | "cell_type": "markdown", 613 | "metadata": {}, 614 | "source": [ 615 | "The code above updates the lower and upper bounds for our search space according to the result of the comparison between the value of target and the value of the midpoint (A[mid]) (lines 8 -11) just as in the original Binary Search. The execution jumps to line 13 when target is equal to A[mid]. Since the array is sorted in ascending order, our target is either the middle element or an element on its left. Now we have to make sure that we return the first occurrence from the function.\n", 616 | "\n", 617 | "On line 13, we check if mid - 1 is less than 0, this is an edge case we would deal with if the first occurrence is on the first index. Next, we check if the element to the left of A[mid], i.e., A[mid - 1] is the target element or not (line 15). If it isn’t, the condition on line 15 is True, and this implies that A[mid] is the first occurrence, so we return mid on line 16. However, if the element to the left is also the same as the target element, we update high to mid - 1. In this way, we condense our search space to find the first occurrence of the target which will be to the left of the midpoint." 618 | ] 619 | }, 620 | { 621 | "cell_type": "markdown", 622 | "metadata": {}, 623 | "source": [ 624 | "# Challenge 1~ Integer Square Root" 625 | ] 626 | }, 627 | { 628 | "cell_type": "markdown", 629 | "metadata": {}, 630 | "source": [ 631 | "You are required to write a function that takes a non-negative integer, k, and returns the largest integer whose square is less than or equal to the specified integer k." 632 | ] 633 | }, 634 | { 635 | "cell_type": "code", 636 | "execution_count": 1, 637 | "metadata": {}, 638 | "outputs": [], 639 | "source": [ 640 | "def integer_square_root(k):\n", 641 | " low=0\n", 642 | " high=k\n", 643 | " \n", 644 | " while low<=high:\n", 645 | " mid=(low+high)//2\n", 646 | " mid_sqr=mid**2\n", 647 | " if mid_sqr<=k:\n", 648 | " low=mid+1\n", 649 | " else:\n", 650 | " high=mid-1\n", 651 | " \n", 652 | " return low-1 " 653 | ] 654 | }, 655 | { 656 | "cell_type": "code", 657 | "execution_count": 2, 658 | "metadata": {}, 659 | "outputs": [ 660 | { 661 | "name": "stdout", 662 | "output_type": "stream", 663 | "text": [ 664 | "17\n" 665 | ] 666 | } 667 | ], 668 | "source": [ 669 | "k = 300\n", 670 | "print(integer_square_root(k))" 671 | ] 672 | }, 673 | { 674 | "cell_type": "markdown", 675 | "metadata": {}, 676 | "source": [ 677 | "# Challenge 2~ Cyclically Shifted Array" 678 | ] 679 | }, 680 | { 681 | "cell_type": "markdown", 682 | "metadata": {}, 683 | "source": [ 684 | "You are required to write a function that determines the index of the smallest element of the cyclically shifted array." 685 | ] 686 | }, 687 | { 688 | "cell_type": "code", 689 | "execution_count": 11, 690 | "metadata": {}, 691 | "outputs": [ 692 | { 693 | "name": "stdout", 694 | "output_type": "stream", 695 | "text": [ 696 | "0\n" 697 | ] 698 | } 699 | ], 700 | "source": [ 701 | "def find(A):\n", 702 | " low=0\n", 703 | " high=len(A)-1\n", 704 | " \n", 705 | " while lowA[high]:\n", 709 | " low=mid+1\n", 710 | " else:\n", 711 | " #important here \n", 712 | " high=mid\n", 713 | " return low\n", 714 | "\n", 715 | "A = [4, 5, 6, 0, 1, 2, 3]\n", 716 | "idx = find(A)\n", 717 | "print(A[idx]) " 718 | ] 719 | }, 720 | { 721 | "cell_type": "markdown", 722 | "metadata": {}, 723 | "source": [ 724 | "# Recursion" 725 | ] 726 | }, 727 | { 728 | "cell_type": "markdown", 729 | "metadata": {}, 730 | "source": [ 731 | "### Find Uppercase Letter in String" 732 | ] 733 | }, 734 | { 735 | "cell_type": "code", 736 | "execution_count": 14, 737 | "metadata": {}, 738 | "outputs": [], 739 | "source": [ 740 | "def find_upper_iterative(str):\n", 741 | " for i in range(len(str)):\n", 742 | " if str[i].isupper():\n", 743 | " return str[i]\n", 744 | " return \"no uppercase found\"\n", 745 | " " 746 | ] 747 | }, 748 | { 749 | "cell_type": "code", 750 | "execution_count": 15, 751 | "metadata": {}, 752 | "outputs": [ 753 | { 754 | "name": "stdout", 755 | "output_type": "stream", 756 | "text": [ 757 | "P\n", 758 | "L\n", 759 | "no uppercase found\n" 760 | ] 761 | } 762 | ], 763 | "source": [ 764 | "input_str_1 = \"lucidProgramming\"\n", 765 | "input_str_2 = \"LucidProgramming\"\n", 766 | "input_str_3 = \"lucidprogramming\"\n", 767 | "print(find_upper_iterative(input_str_1))\n", 768 | "print(find_upper_iterative(input_str_2))\n", 769 | "print(find_upper_iterative(input_str_3))" 770 | ] 771 | }, 772 | { 773 | "cell_type": "markdown", 774 | "metadata": {}, 775 | "source": [ 776 | "### Recursive Approach" 777 | ] 778 | }, 779 | { 780 | "cell_type": "code", 781 | "execution_count": 16, 782 | "metadata": {}, 783 | "outputs": [], 784 | "source": [ 785 | "def find_upper_recursive(str,idx=0):\n", 786 | " if str[idx].isupper():\n", 787 | " return str[idx]\n", 788 | " if idx==len(str)-1:\n", 789 | " return \"No uppercase\"\n", 790 | " \n", 791 | " return find_upper_recursive(str,idx+1)" 792 | ] 793 | }, 794 | { 795 | "cell_type": "code", 796 | "execution_count": 17, 797 | "metadata": {}, 798 | "outputs": [ 799 | { 800 | "name": "stdout", 801 | "output_type": "stream", 802 | "text": [ 803 | "P\n", 804 | "L\n", 805 | "No uppercase\n" 806 | ] 807 | } 808 | ], 809 | "source": [ 810 | "print(find_upper_recursive(input_str_1))\n", 811 | "print(find_upper_recursive(input_str_2))\n", 812 | "print(find_upper_recursive(input_str_3))" 813 | ] 814 | }, 815 | { 816 | "cell_type": "markdown", 817 | "metadata": {}, 818 | "source": [ 819 | "### Calculate String Length" 820 | ] 821 | }, 822 | { 823 | "cell_type": "code", 824 | "execution_count": 18, 825 | "metadata": {}, 826 | "outputs": [ 827 | { 828 | "name": "stdout", 829 | "output_type": "stream", 830 | "text": [ 831 | "16\n" 832 | ] 833 | } 834 | ], 835 | "source": [ 836 | "def str_len(str):\n", 837 | " count=0\n", 838 | " for i in range(len(str)):\n", 839 | " count+=1\n", 840 | " return count\n", 841 | "\n", 842 | "input_str = \"LucidProgramming\"\n", 843 | "\n", 844 | "print(str_len(input_str))" 845 | ] 846 | }, 847 | { 848 | "cell_type": "markdown", 849 | "metadata": {}, 850 | "source": [ 851 | "### Recursive Approach" 852 | ] 853 | }, 854 | { 855 | "cell_type": "code", 856 | "execution_count": 19, 857 | "metadata": {}, 858 | "outputs": [ 859 | { 860 | "name": "stdout", 861 | "output_type": "stream", 862 | "text": [ 863 | "16\n" 864 | ] 865 | } 866 | ], 867 | "source": [ 868 | "def str_len_recursive(str):\n", 869 | " if str=='':\n", 870 | " return 0\n", 871 | " \n", 872 | " return 1+ str_len_recursive(str[1:])\n", 873 | "\n", 874 | "input_str = \"LucidProgramming\"\n", 875 | "\n", 876 | "print(str_len_recursive(input_str))" 877 | ] 878 | }, 879 | { 880 | "cell_type": "markdown", 881 | "metadata": {}, 882 | "source": [ 883 | "The base case for this function is when an empty string is encountered. If input_str is empty, 0 is returned to make a count of 0 for the empty string. Otherwise, 1 is added to whatever is returned from the recursive call on line 5 which takes in input_str[1:]. The slicing notation in input_str[1:] indicates that all the characters except at the 0th index are passed into the recursive call. Therefore, every recursive call keeps shortening input_str by one character, which is being counted in that recursive call. As there will be nn recursive calls, each expending a constant amount of computational effort, the time complexity for this solution is O(n)O(n) where nn is the length of the string." 884 | ] 885 | }, 886 | { 887 | "cell_type": "markdown", 888 | "metadata": {}, 889 | "source": [ 890 | "### Count Consonants in String" 891 | ] 892 | }, 893 | { 894 | "cell_type": "code", 895 | "execution_count": 24, 896 | "metadata": {}, 897 | "outputs": [], 898 | "source": [ 899 | "vowels='aeiou'\n", 900 | "def count_cons(str):\n", 901 | " count=0\n", 902 | " for i in range(len(str)):\n", 903 | " if str[i].lower() not in vowels and str[i].isalpha():\n", 904 | " count+=1\n", 905 | " return count\n", 906 | " " 907 | ] 908 | }, 909 | { 910 | "cell_type": "code", 911 | "execution_count": 25, 912 | "metadata": {}, 913 | "outputs": [ 914 | { 915 | "name": "stdout", 916 | "output_type": "stream", 917 | "text": [ 918 | "abc de\n", 919 | "3\n" 920 | ] 921 | } 922 | ], 923 | "source": [ 924 | "input_str = \"abc de\"\n", 925 | "print(input_str)\n", 926 | "print(count_cons(input_str))" 927 | ] 928 | }, 929 | { 930 | "cell_type": "markdown", 931 | "metadata": {}, 932 | "source": [ 933 | "#### Recursive Approach:\n", 934 | " " 935 | ] 936 | }, 937 | { 938 | "cell_type": "code", 939 | "execution_count": 38, 940 | "metadata": {}, 941 | "outputs": [], 942 | "source": [ 943 | "def count_cons_recursive(str):\n", 944 | " if str =='':\n", 945 | " return 0\n", 946 | " if str[0].lower() not in vowels and str[0].isalpha():\n", 947 | " return 1+ count_cons_recursive(str[1:])\n", 948 | " \n", 949 | " else:\n", 950 | " return count_cons_recursive(str[1:])" 951 | ] 952 | }, 953 | { 954 | "cell_type": "code", 955 | "execution_count": 39, 956 | "metadata": {}, 957 | "outputs": [ 958 | { 959 | "name": "stdout", 960 | "output_type": "stream", 961 | "text": [ 962 | "abc de\n", 963 | "3\n" 964 | ] 965 | } 966 | ], 967 | "source": [ 968 | "print(input_str)\n", 969 | "print(count_cons_recursive(input_str))" 970 | ] 971 | }, 972 | { 973 | "cell_type": "markdown", 974 | "metadata": {}, 975 | "source": [ 976 | "#### Product of two integers\n" 977 | ] 978 | }, 979 | { 980 | "cell_type": "code", 981 | "execution_count": 44, 982 | "metadata": {}, 983 | "outputs": [ 984 | { 985 | "name": "stdout", 986 | "output_type": "stream", 987 | "text": [ 988 | "140\n" 989 | ] 990 | } 991 | ], 992 | "source": [ 993 | "def recursive_product(x,y):\n", 994 | " if x 0:\n", 342 | " str.append(chr(ord('0')+ integer%10))\n", 343 | " integer//=10\n", 344 | " str=str[::-1]\n", 345 | " \n", 346 | " str=''.join(str)\n", 347 | " \n", 348 | " if is_neg:\n", 349 | " return '-' + str\n", 350 | " else:\n", 351 | " return str" 352 | ] 353 | }, 354 | { 355 | "cell_type": "code", 356 | "execution_count": 60, 357 | "metadata": {}, 358 | "outputs": [ 359 | { 360 | "name": "stdout", 361 | "output_type": "stream", 362 | "text": [ 363 | "-123\n", 364 | "\n", 365 | "-123\n", 366 | "\n" 367 | ] 368 | } 369 | ], 370 | "source": [ 371 | "input_int = -123\n", 372 | "print(input_int)\n", 373 | "print(type(input_int))\n", 374 | "output_str = int_to_str(input_int)\n", 375 | "print(output_str)\n", 376 | "print(type(output_str))" 377 | ] 378 | }, 379 | { 380 | "cell_type": "markdown", 381 | "metadata": {}, 382 | "source": [ 383 | "# String to Integer" 384 | ] 385 | }, 386 | { 387 | "cell_type": "code", 388 | "execution_count": 61, 389 | "metadata": {}, 390 | "outputs": [], 391 | "source": [ 392 | "def str_to_int(input_str):\n", 393 | " \n", 394 | " output_int = 0\n", 395 | " \n", 396 | " if input_str[0] == '-':\n", 397 | " start_idx = 1\n", 398 | " is_negative = True\n", 399 | " else:\n", 400 | " start_idx = 0\n", 401 | " is_negative = False\n", 402 | "\n", 403 | " for i in range(start_idx, len(input_str)):\n", 404 | " place = 10**(len(input_str) - (i+1))\n", 405 | " digit = ord(input_str[i]) - ord('0')\n", 406 | " output_int += place * digit\n", 407 | "\n", 408 | " if is_negative:\n", 409 | " return -1 * output_int\n", 410 | " else:\n", 411 | " return output_int" 412 | ] 413 | }, 414 | { 415 | "cell_type": "code", 416 | "execution_count": 63, 417 | "metadata": {}, 418 | "outputs": [ 419 | { 420 | "name": "stdout", 421 | "output_type": "stream", 422 | "text": [ 423 | "123\n", 424 | "\n" 425 | ] 426 | } 427 | ], 428 | "source": [ 429 | "s = \"123\"\n", 430 | "print(str_to_int(s))\n", 431 | "print(type(str_to_int(s)))" 432 | ] 433 | } 434 | ], 435 | "metadata": { 436 | "kernelspec": { 437 | "display_name": "Python 3", 438 | "language": "python", 439 | "name": "python3" 440 | }, 441 | "language_info": { 442 | "codemirror_mode": { 443 | "name": "ipython", 444 | "version": 3 445 | }, 446 | "file_extension": ".py", 447 | "mimetype": "text/x-python", 448 | "name": "python", 449 | "nbconvert_exporter": "python", 450 | "pygments_lexer": "ipython3", 451 | "version": "3.7.4" 452 | } 453 | }, 454 | "nbformat": 4, 455 | "nbformat_minor": 4 456 | } 457 | --------------------------------------------------------------------------------