{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "source": [
        "# Lecture 12 - Stacks, simulating recursion using stacks\n",
        "\n",
        "In this lecture,we will discuss -\n",
        "\n",
        "* Stacks - a fundamental data structure\n",
        "* Operations on stacks - push, pop, isEmpty\n",
        "* Applications of stacks - backtracking, reversing\n",
        "* Summation, factorial - simulating recursive calls using stacks"
      ],
      "metadata": {
        "id": "lhfVFsDo41Gn"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Backtracking\n",
        "\n",
        "Ever wondered how the browser stores data to support the back operation?\n",
        "\n",
        "Or text editors that provide an undo mechanism to revert to a former state of the document?\n",
        "\n",
        "Turns out they operate on the same principle as a PEZ dispenser - the last element to go in is the first element that gets out!"
      ],
      "metadata": {
        "id": "Goxt5kkEIO1M"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Stacks\n",
        "\n",
        "A stack is a collection of objects that operates on the Last In First Out principle (LIFO) - the last object in is the first object out.\n",
        "\n",
        "Web browsers store the list of visited websites using a stack that helps them trackback to the most recent website quickly.\n",
        "\n",
        "Text editors store the state of a document by storing text changes made to a stack - which helps them undo most recently made changes to revert to an earlier state of the document.\n",
        "\n",
        "Stack operations visualization - https://visualgo.net/en/list\n"
      ],
      "metadata": {
        "id": "vAioUMM0F_53"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Stack operations\n",
        "\n",
        "\n",
        "\n",
        "1.   Push - Pushes an element to the top of the stack.\n",
        "2.   Pop - Pops the element at the top of the stack - decreasing its size by 1.\n",
        "3. is_Empty - Boolean return value indicating whether the stack is empty.\n",
        "4. Peek / Top - returns the top element of the stack without removing it from the stack\n",
        "5. len() - returns the length of the stack\n",
        "\n",
        "Let's write some code!\n"
      ],
      "metadata": {
        "id": "ZtFf9qWjL1ht"
      }
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "Dzyken444yM6",
        "outputId": "5dd92572-c529-460c-bd3a-a144fa2edb5f"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Stack:  ['A', 'B', 'C']\n",
            "Peek:  C\n",
            "Pop:  C\n",
            "Pop:  B\n",
            "Stack after Pop:  ['A']\n",
            "isEmpty:  False\n",
            "Size:  1\n"
          ]
        }
      ],
      "source": [
        "#Implementing a stack uisng python lists\n",
        "\n",
        "stack = []\n",
        "\n",
        "# Push - since we use lists, we can use the append function to simulate pushing to a stack\n",
        "stack.append('A')\n",
        "stack.append('B')\n",
        "stack.append('C')\n",
        "print(\"Stack: \", stack)\n",
        "\n",
        "# Peek - since we can access the last element using index -1 for lists, we can simulate peek using stack[-1]\n",
        "topElement = stack[-1]\n",
        "print(\"Peek: \", topElement)\n",
        "\n",
        "# Pop - python lists support pop function - this removes the last element of the list -  (top) of the stack\n",
        "poppedElement = stack.pop()\n",
        "print(\"Pop: \", poppedElement)\n",
        "\n",
        "poppedElement = stack.pop()\n",
        "print(\"Pop: \", poppedElement)\n",
        "\n",
        "# Stack after Pop\n",
        "print(\"Stack after Pop: \", stack)\n",
        "\n",
        "# isEmpty - since python lists support len, we can find the size of a stack using len(Stack)\n",
        "isEmpty = (len(stack)==0)\n",
        "print(\"isEmpty: \", isEmpty)\n",
        "\n",
        "# Size\n",
        "print(\"Size: \",len(stack))"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Applications of Stacks\n",
        "\n",
        "Stacks are used in many real-world scenarios:\n",
        "\n",
        "1. Backtracking\n",
        "2. Reversing data\n",
        "3. Matching delimiters\n",
        "\n",
        "---\n",
        "\n",
        "#Backtracking\n",
        "\n",
        "\n",
        "* Undo/Redo operations in text editors\n",
        "* Browser history (back/forward)\n",
        "* Function call stack in programming\n",
        "---\n",
        "#Reversing data\n",
        "\n",
        "* Stacks can be used to reverse data\n",
        "\n",
        "\n",
        "---\n",
        "# Matching delimiters\n",
        "\n",
        "* Parentheses testing\n",
        "* Expression evaluation\n",
        "\n"
      ],
      "metadata": {
        "id": "zDdGAsT_Nw9q"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "#Write a code that uses stacks to reverse the input string s\n",
        "\n",
        "s = \"reverse_this\"\n",
        "\n",
        "stack = []\n",
        "\n",
        "for i in range(len(s)):\n",
        "  stack.append(s[i])\n",
        "\n",
        "rev = \"\"\n",
        "\n",
        "for i in range(len(s)):\n",
        "  rev += str(stack.pop())\n",
        "\n",
        "print(rev)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "0IGgi3fabd50",
        "outputId": "1642a913-3314-436d-9536-da18d99782c9"
      },
      "execution_count": 3,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "siht_esrever\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Simulating recursive computation using stacks\n",
        "\n",
        "The following code snippets show two different ways of computing summation/factorial - one using recursion and another non-recursive way that uses a stack to simulate function calls"
      ],
      "metadata": {
        "id": "vsK5efZNGar0"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "def summation(n):\n",
        "  if(n == 1):\n",
        "    return 1\n",
        "  return n + summation(n-1)\n",
        "\n",
        "#print(summation(4))\n",
        "\n",
        "def summation_nonrecursive(n):\n",
        "  stack = []\n",
        "  i = 0\n",
        "  while(i < n):\n",
        "    stack.append(n-i)\n",
        "    i += 1\n",
        "    #print(stack)\n",
        "  ans = 0\n",
        "  while(len(stack)!= 0):\n",
        "    ans = ans + stack.pop()\n",
        "    #print(ans)\n",
        "  return ans\n",
        "\n",
        "print(summation_nonrecursive(4))\n"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "r0R4d4JOnZWP",
        "outputId": "16d06623-52b4-4cad-9ac8-beb712f9c651"
      },
      "execution_count": 4,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "10\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "def factorial(n):\n",
        "  if(n == 1):\n",
        "    return 1\n",
        "  return n*factorial(n-1)\n",
        "\n",
        "print(factorial(7))\n",
        "\n",
        "\n",
        "def factorial_nonrecursive(n):\n",
        "  if(n == 1):\n",
        "    return 1\n",
        "\n",
        "  stack = []\n",
        "\n",
        "  for i in range(n):\n",
        "    stack.append(n-i)\n",
        "\n",
        "  #print(stack)\n",
        "  product = 1\n",
        "\n",
        "  while(len(stack)!= 0):\n",
        "    product = product * stack.pop()\n",
        "\n",
        "  return product\n",
        "\n",
        "\n",
        "print(factorial_nonrecursive(7))\n"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "JnvWhYpGrLLV",
        "outputId": "1050be36-b906-4478-a20d-6bc69a5b76cd"
      },
      "execution_count": 6,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "5040\n",
            "5040\n"
          ]
        }
      ]
    }
  ]
}