diff --git a/Colab Codes/Colab Notebooks/chapter_14_sorting.ipynb b/Colab Codes/Colab Notebooks/chapter_14_sorting.ipynb index 67e1cbc..9edc7aa 100644 --- a/Colab Codes/Colab Notebooks/chapter_14_sorting.ipynb +++ b/Colab Codes/Colab Notebooks/chapter_14_sorting.ipynb @@ -1 +1 @@ -{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"chapter_14_sorting.ipynb","version":"0.3.2","provenance":[],"collapsed_sections":[]},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"metadata":{"id":"XOkspIkgKots","colab_type":"text"},"cell_type":"markdown","source":["## Counting Sorting in O(n+k)"]},{"metadata":{"id":"ptAUeFu8Kvrs","colab_type":"code","colab":{}},"cell_type":"code","source":["def countSort(a):\n"," minK, maxK = min(a), max(a)\n"," k = maxK - minK + 1\n"," count = [0] * (maxK - minK + 1)\n"," n = len(a)\n"," order = [0] * n\n"," # get occurrence\n"," for key in a:\n"," count[key - minK] += 1\n"," \n"," # get prefix sum\n"," for i in range(1, k):\n"," count[i] += count[i-1]\n"," \n"," # put it back in the input\n"," for i in range(n-1, -1, -1):\n"," key = a[i] - minK\n"," count[key] -= 1 # to get the index as position\n"," order[count[key]] = a[i] # put the key back to the sorted position\n"," return order"],"execution_count":0,"outputs":[]},{"metadata":{"id":"IUEe34hHMqBx","colab_type":"code","outputId":"b5074a60-b9b8-4313-ca63-1ba44addd461","executionInfo":{"status":"ok","timestamp":1550259045549,"user_tz":480,"elapsed":271,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":34}},"cell_type":"code","source":["a = [9, 10, 2, 8, 9, 3, 7]\n","print(countSort(a))"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[2, 3, 7, 8, 9, 9, 10]\n"],"name":"stdout"}]},{"metadata":{"id":"lHFCwzZNGrMu","colab_type":"text"},"cell_type":"markdown","source":["## Bubble Sort in O(n^2)"]},{"metadata":{"id":"uqzSlyDXGzhO","colab_type":"code","colab":{}},"cell_type":"code","source":["def bubbleSort(a):\n"," if not a or len(a) == 1:\n"," return a\n"," n = len(a)\n"," for i in range(n - 1): #n-1 passes, \n"," for j in range(n - i -1): #each pass will have valid window [0, n-i], and j is the starting index of each pair\n"," if a[j] > a[j + 1]:\n"," a[j], a[j + 1] = a[j + 1], a[j] #swap\n"," return a"],"execution_count":0,"outputs":[]},{"metadata":{"id":"B-WK_VNi_unM","colab_type":"code","colab":{}},"cell_type":"code","source":["def bubbleSortOptimized(a):\n"," if not a or len(a) == 1:\n"," return a\n"," n = len(a)\n"," for i in range(n - 1): #n-1 passes, \n"," bSwap = False\n"," for j in range(n - i -1): #each pass will have valid window [0, n-i], and j is the starting index of each pair\n"," if a[j] > a[j + 1]:\n"," a[j], a[j + 1] = a[j + 1], a[j] #swap\n"," bSwap = True\n"," if not bSwap:\n"," break\n"," return a"],"execution_count":0,"outputs":[]},{"metadata":{"id":"npb5NRJdI4sV","colab_type":"code","outputId":"57c48506-47f9-4d27-cad6-b93c3729e4c5","executionInfo":{"status":"ok","timestamp":1550350492596,"user_tz":480,"elapsed":323,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["a = [9, 10, 2, 8, 9, 3, 7]\n","print(bubbleSortOptimized(a))"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[2, 3, 7, 8, 9, 9, 10]\n"],"name":"stdout"}]},{"metadata":{"id":"RZwzr_7xBjae","colab_type":"text"},"cell_type":"markdown","source":["## Selection Sort in O(n^2)"]},{"metadata":{"id":"e0U-HGHcBpL4","colab_type":"code","colab":{}},"cell_type":"code","source":["def selectSort(a):\n"," n = len(a)\n"," for i in range(n - 1): #n-1 passes, \n"," ti = n - 1 -i # the position to fill in the largest item of valid window [0, n-i]\n"," li = 0\n"," for j in range(n - i):\n"," if a[j] > a[li]:\n"," li = j\n"," # swap li and ti\n"," print('swap', a[li], a[ti])\n"," a[ti], a[li] = a[li], a[ti]\n"," print(a)\n"," return a\n"," \n"," \n"," #"],"execution_count":0,"outputs":[]},{"metadata":{"id":"r3v62LNjEZVY","colab_type":"code","outputId":"7d4a8ad0-466f-4ff4-dd2f-e214c7979e3e","executionInfo":{"status":"ok","timestamp":1550350715349,"user_tz":480,"elapsed":388,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":256}},"cell_type":"code","source":["a = [9, 10, 2, 8, 9, 3, 9]\n","print(selectSort(a))"],"execution_count":0,"outputs":[{"output_type":"stream","text":["swap 10 9\n","[9, 9, 2, 8, 9, 3, 10]\n","swap 9 3\n","[3, 9, 2, 8, 9, 9, 10]\n","swap 9 9\n","[3, 9, 2, 8, 9, 9, 10]\n","swap 9 8\n","[3, 8, 2, 9, 9, 9, 10]\n","swap 8 2\n","[3, 2, 8, 9, 9, 9, 10]\n","swap 3 2\n","[2, 3, 8, 9, 9, 9, 10]\n","[2, 3, 8, 9, 9, 9, 10]\n"],"name":"stdout"}]},{"metadata":{"id":"IQcElo1mM_Sf","colab_type":"text"},"cell_type":"markdown","source":["## Insertion Sort in O(n^2)"]},{"metadata":{"id":"yJL27nHcNEJl","colab_type":"code","colab":{}},"cell_type":"code","source":["def insertionSort(a):\n"," if not a or len(a) == 1:\n"," return a\n"," n = len(a)\n"," sl = [a[0]] # sorted list\n"," for i in range(1, n): # items to be inserted into the sorted\n"," j = 0 \n"," while j < len(sl):\n"," if a[i] > sl[j]:\n"," j += 1\n"," else:\n"," sl.insert(j, a[i])\n"," break\n"," if j == len(sl): # not inserted yet\n"," sl.insert(j, a[i])\n"," return sl\n"," \n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"Zb5jMt-iScSz","colab_type":"code","outputId":"766ddcce-178d-4832-d154-e853a03debaa","executionInfo":{"status":"ok","timestamp":1550259083716,"user_tz":480,"elapsed":283,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":221}},"cell_type":"code","source":["a = [9, 10, 2, 8, 9, 3, 7]\n","print(insertionSort(a))"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[9, 10]\n","[2, 9, 10]\n","[2, 9, 10]\n","[2, 8, 9, 10]\n","[2, 8, 9, 10]\n","[2, 8, 9, 9, 10]\n","[2, 8, 9, 9, 10]\n","[2, 3, 8, 9, 9, 10]\n","[2, 3, 8, 9, 9, 10]\n","[2, 3, 7, 8, 9, 9, 10]\n","[2, 3, 7, 8, 9, 9, 10]\n","[2, 3, 7, 8, 9, 9, 10]\n"],"name":"stdout"}]},{"metadata":{"id":"EI1bDD3106fV","colab_type":"code","colab":{}},"cell_type":"code","source":["def shift(a, start, end):\n"," for i in range(end, start, -1): # [i, j)\n"," a[i] = a[i-1]\n"," \n","def insertionSortForward(a):\n"," if not a or len(a) == 1:\n"," return a\n"," n = len(a)\n"," sl = [a[0]] # sorted list\n"," for i in range(1, n): # items to be inserted into the sorted\n"," for j in range(i):\n"," if a[i] < a[j]:\n"," # shift all other elements [j, i-1]\n"," tmp = a[i]\n"," shift(a, j, i)\n"," a[j] = tmp \n"," return a\n","\n","def insertionSortInPlace(a):\n"," if not a or len(a) == 1:\n"," return a\n"," n = len(a)\n"," for i in range(1, n): # items to be inserted into the sorted\n"," t = a[i]\n"," j = i - 1\n"," while j >= 0 and t < a[j]: # keep comparing if target is still smaller\n"," a[j+1] = a[j] # shift current item backward\n"," j -= 1\n"," a[j+1] = t # a[j] <= t , insert t at the location j+1 \n"," return a"],"execution_count":0,"outputs":[]},{"metadata":{"id":"NgeLGKn51mxY","colab_type":"code","outputId":"6051e043-4c10-48c3-d477-10bb3b13a05b","executionInfo":{"status":"ok","timestamp":1550348226402,"user_tz":480,"elapsed":325,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["a = [9, 10, 2, 8, 9, 3, 7]\n","print(insertionSortInPlace(a))"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[2, 3, 7, 8, 9, 9, 10]\n"],"name":"stdout"}]},{"metadata":{"id":"GN1yseSKImNy","colab_type":"text"},"cell_type":"markdown","source":["## Merge Sort O(nlgn)"]},{"metadata":{"id":"rILA4lBhdPxD","colab_type":"code","colab":{}},"cell_type":"code","source":["def merge(l, r): \n"," '''combine the left and right sorted list'''\n"," ans = []\n"," i = j = 0 # two pointers each points at l and r\n"," n, m = len(l), len(r)\n"," \n"," # first while loop to merge\n"," while i < n and j < m: \n"," if l[i] <= r[j]:\n"," ans.append(l[i])\n"," i += 1\n"," else:\n"," ans.append(r[j])\n"," j += 1\n"," \n"," # now one list of l and r might have items left\n"," ans += l[i:]\n"," ans += r[j:]\n"," return ans\n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"DK003Ic1Isb3","colab_type":"code","colab":{}},"cell_type":"code","source":["def mergeSort(a, s, e):\n"," # base case , can not be divided further\n"," if s == e:\n"," return [a[s]]\n"," # divide into two halves from the middle point\n"," m = (s + e) // 2\n"," \n"," # conquer\n"," l = mergeSort(a, s , m)\n"," r = mergeSort(a, m+1, e)\n"," \n"," # combine\n"," return merge(l, r)"],"execution_count":0,"outputs":[]},{"metadata":{"id":"ys6tUAd8i7ao","colab_type":"code","outputId":"afc20a0d-012e-413f-baf1-f098bb8e23ce","executionInfo":{"status":"ok","timestamp":1550374945810,"user_tz":480,"elapsed":336,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["a = [9, 10, 2, 8, 9, 3, 7, 9]\n","mergeSort(a, 0, len(a)-1)"],"execution_count":0,"outputs":[{"output_type":"execute_result","data":{"text/plain":["[2, 3, 7, 8, 9, 9, 9, 10]"]},"metadata":{"tags":[]},"execution_count":34}]},{"metadata":{"id":"IL6hPk6IjGSf","colab_type":"text"},"cell_type":"markdown","source":["### prove merge sort is stable by sorting tuple and printing id"]},{"metadata":{"id":"UwgdFdaRipEN","colab_type":"code","colab":{}},"cell_type":"code","source":["def mergeTuple(l, r): \n"," '''combine the left and right sorted list'''\n"," ans = []\n"," i = j = 0 # two pointers each points at l and r\n"," n, m = len(l), len(r)\n"," \n"," # first while loop to merge\n"," while i < n and j < m: \n"," if l[i][0] <= r[j][0]: # chaning it to l[i][0] < r[j][0] will not be stable anymore. \n"," ans.append(l[i])\n"," i += 1\n"," else:\n"," ans.append(r[j])\n"," j += 1\n"," \n"," # now one list of l and r might have items left\n"," ans += l[i:]\n"," ans += r[j:]\n"," return ans\n","\n","def mergeSortTuple(a, s, e):\n"," # base case , can not be divided further\n"," if s == e:\n"," return [a[s]]\n"," # divide into two halves from the middle point\n"," m = (s + e) // 2\n"," \n"," # conquer\n"," l = mergeSort(a, s , m)\n"," r = mergeSort(a, m+1, e)\n"," \n"," # combine\n"," return mergeTuple(l, r)"],"execution_count":0,"outputs":[]},{"metadata":{"id":"mm07Ac0-dO3A","colab_type":"code","outputId":"dd9eca57-98b7-4e04-acc2-7a31e7efd61d","executionInfo":{"status":"ok","timestamp":1550378993589,"user_tz":480,"elapsed":914,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":312}},"cell_type":"code","source":["a = [(9, 1), (10, 1), (2, 1), (8, 1), (9, 2), (3, 1), (7, 1), (9, 3)] # the second item represents the index of duplcates\n","ids = [id(x) if x[0] == 9 else None for x in a]\n","sorted_a = mergeSortTuple(a, 0, len(a)-1)\n","ids2 = [id(x) if x[0] == 9 else None for x in sorted_a]\n","print(sorted_a)\n","ids, ids2"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[(2, 1), (3, 1), (7, 1), (8, 1), (9, 2), (9, 3), (9, 1), (10, 1)]\n"],"name":"stdout"},{"output_type":"execute_result","data":{"text/plain":["([140381548618120,\n"," None,\n"," None,\n"," None,\n"," 140381548653128,\n"," None,\n"," None,\n"," 140381548653320],\n"," [None,\n"," None,\n"," None,\n"," None,\n"," 140381548653128,\n"," 140381548653320,\n"," 140381548618120,\n"," None])"]},"metadata":{"tags":[]},"execution_count":47}]},{"metadata":{"id":"h0QYWQaDxt9D","colab_type":"text"},"cell_type":"markdown","source":["## QuickSort in O(nlogn)"]},{"metadata":{"id":"j4mW9xNrO6hm","colab_type":"code","colab":{}},"cell_type":"code","source":["def partition(a, s, e):\n"," '''Lumutos partition'''\n"," p = a[e]\n"," i = s - 1\n"," for j in range(s, e): #a[s, e-1]\n"," \n"," if a[j] <= p:\n"," i += 1\n"," a[i], a[j] = a[j], a[i] # swap a[i] and a[j]\n"," # print out the range of each region\n","# print('p<->i', [a[x] for x in range(s, i+1)])\n","# print('i+1<->j', [a[x] for x in range(i+1, j+1)])\n"," # place p at position i+1 through swapping with a[i+1]\n"," a[i+1], a[e] = a[e], a[i+1]\n"," return i+1"],"execution_count":0,"outputs":[]},{"metadata":{"id":"YPUErvQ1pT1v","colab_type":"text"},"cell_type":"markdown","source":["### experiment the correctness of lumutos partition"]},{"metadata":{"id":"oTmEOjk2QQZV","colab_type":"code","outputId":"cc8a3971-7ef6-45b4-da03-d2c46460ffcf","executionInfo":{"status":"ok","timestamp":1550560957592,"user_tz":480,"elapsed":474,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":54}},"cell_type":"code","source":["lst = [9, 10, 2, 8, 9, 3, 7]\n","print(partition(lst, 0, len(lst)-1))\n","print(lst)"],"execution_count":37,"outputs":[{"output_type":"stream","text":["2\n","[2, 3, 7, 8, 9, 10, 9]\n"],"name":"stdout"}]},{"metadata":{"id":"qi_gd9TQpbn_","colab_type":"text"},"cell_type":"markdown","source":["### main algorithm of quick sort"]},{"metadata":{"id":"v2_nP14pObAn","colab_type":"code","colab":{}},"cell_type":"code","source":["def quickSort(a, s, e, partition=partition):\n"," # base case , can not be divided further\n"," if s >= e:\n"," return \n"," p = partition(a, s, e)\n"," \n"," # conquer smaller problem\n"," quickSort(a, s , p-1, partition)\n"," quickSort(a, p+1, e, partition)\n"," return"],"execution_count":0,"outputs":[]},{"metadata":{"id":"vPtdpJnApa4n","colab_type":"text"},"cell_type":"markdown","source":[""]},{"metadata":{"id":"8N4QPVcqouFf","colab_type":"text"},"cell_type":"markdown","source":["### experiment to see the stability of quick sort"]},{"metadata":{"id":"oOHBweKDgiMw","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":35},"outputId":"1634d771-17cd-4bec-de8d-2da7e3632066","executionInfo":{"status":"ok","timestamp":1550561308432,"user_tz":480,"elapsed":588,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}}},"cell_type":"code","source":["a = [(5, 1), (7, 1),(3, 1), (2, 1), (5, 2), (6,1), (7, 2), (8, 1), (9, 1), (5, 3), (5, 4)] # the second item represents the index of duplcates\n","def partition_tuple(a, s, e):\n"," '''Lumutos partition'''\n"," p = a[e][0]\n"," i = s - 1\n"," for j in range(s, e): #a[s, e-1]\n"," \n"," if a[j][0] <= p:\n"," i += 1\n"," a[i], a[j] = a[j], a[i] # swap a[i] and a[j]\n"," # print out the range of each region\n","# print('p<->i', [a[x] for x in range(s, i+1)])\n","# print('i+1<->j', [a[x] for x in range(i+1, j+1)])\n"," # place p at position i+1 through swapping with a[i+1]\n"," a[i+1], a[e] = a[e], a[i+1]\n"," return i+1\n","quickSort(a, 0, len(a) - 1, partition_tuple)\n","print(a)"],"execution_count":47,"outputs":[{"output_type":"stream","text":["[(2, 1), (3, 1), (5, 1), (5, 2), (5, 3), (5, 4), (6, 1), (7, 1), (7, 2), (8, 1), (9, 1)]\n"],"name":"stdout"}]},{"metadata":{"id":"QqcdUXaepGSa","colab_type":"text"},"cell_type":"markdown","source":["### experiment to see the performance of worst time"]},{"metadata":{"id":"5n7nsw13pLWr","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":54},"outputId":"736cf200-711d-4f42-b2b1-f5c369f70ce8","executionInfo":{"status":"ok","timestamp":1550562301025,"user_tz":480,"elapsed":637,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}}},"cell_type":"code","source":["import random, time\n","lst1 = [random.randint(1, 25) for i in range(400)]\n","lst2 = [i for i in range(400)[::-1]]\n","t1 = time.time()\n","quickSort(lst1, 0, len(lst1)-1, partition)\n","print('time for random values:', time.time()-t1)\n","\n","t1 = time.time()\n","quickSort(lst2, 0, len(lst2)-1, partition)\n","print('time for sorted values:', time.time()-t1)"],"execution_count":56,"outputs":[{"output_type":"stream","text":["time for random values: 0.0017516613006591797\n","time for sorted values: 0.0171658992767334\n"],"name":"stdout"}]},{"metadata":{"id":"0y5x07wwo4Um","colab_type":"text"},"cell_type":"markdown","source":["### Hoare Partition"]},{"metadata":{"id":"3vNUigFmo7ei","colab_type":"code","colab":{}},"cell_type":"code","source":["# def partition_hoare(a, s, e):\n","# '''Hoare Parition'''\n","# p = a[e]\n","# i = s\n","# j = e-1\n","# while True:\n","# while a[i] <= p and i < j:\n","# i += 1\n","# while a[j] > p and i < j:\n","# j -= 1\n","# if i < j:\n","# a[i], a[j] = a[j], a[i]\n","# else:\n","# return j\n","# return j"],"execution_count":0,"outputs":[]},{"metadata":{"id":"GKbXRk4Czwjt","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":54},"outputId":"be048173-9125-4716-89f0-17ad7fb0b345","executionInfo":{"status":"ok","timestamp":1550564268216,"user_tz":480,"elapsed":325,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}}},"cell_type":"code","source":["# lst = [9, 10, 2, 8, 9, 3, 7]\n","# print(partition_hoare(lst, 0, len(lst)-1))\n","# print(lst)"],"execution_count":72,"outputs":[{"output_type":"stream","text":["2\n","[3, 2, 10, 8, 9, 9, 7]\n"],"name":"stdout"}]},{"metadata":{"id":"4EDqug7Yg2yl","colab_type":"text"},"cell_type":"markdown","source":["## HeapSort in O(nlogn)"]},{"metadata":{"id":"0PE9BxQBg7lu","colab_type":"code","colab":{}},"cell_type":"code","source":["from heapq import heapify, heappop\n","def heapsort(a):\n"," heapify(a)\n"," return [heappop(a) for i in range(len(a))]"],"execution_count":0,"outputs":[]},{"metadata":{"id":"PcUurYvkg_Px","colab_type":"code","outputId":"a96f214c-6fdb-4ed5-9c94-7e8772e65fb5","executionInfo":{"status":"ok","timestamp":1550525399708,"user_tz":480,"elapsed":286,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["lst = [21, 1, 45, 78, 3, 5]\n","heapsort(lst)"],"execution_count":0,"outputs":[{"output_type":"execute_result","data":{"text/plain":["[1, 3, 5, 21, 45, 78]"]},"metadata":{"tags":[]},"execution_count":2}]},{"metadata":{"id":"K1EUj-De16tk","colab_type":"text"},"cell_type":"markdown","source":["## Bucket Sort"]},{"metadata":{"id":"GOF5OoRGioTg","colab_type":"code","colab":{}},"cell_type":"code","source":[""],"execution_count":0,"outputs":[]}]} \ No newline at end of file +{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"chapter_14_sorting.ipynb","version":"0.3.2","provenance":[],"collapsed_sections":[]},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"metadata":{"id":"XOkspIkgKots","colab_type":"text"},"cell_type":"markdown","source":["## Counting Sort in O(n+k)"]},{"metadata":{"id":"ptAUeFu8Kvrs","colab_type":"code","colab":{}},"cell_type":"code","source":["def countSort(a):\n"," minK, maxK = min(a), max(a)\n"," k = maxK - minK + 1\n"," count = [0] * (maxK - minK + 1)\n"," n = len(a)\n"," order = [0] * n\n"," # get occurrence\n"," for key in a:\n"," count[key - minK] += 1\n"," \n"," # get prefix sum\n"," for i in range(1, k):\n"," count[i] += count[i-1]\n"," \n"," # put it back in the input\n"," for i in range(n-1, -1, -1):\n"," key = a[i] - minK\n"," count[key] -= 1 # to get the index as position\n"," order[count[key]] = a[i] # put the key back to the sorted position\n"," return order"],"execution_count":0,"outputs":[]},{"metadata":{"id":"IUEe34hHMqBx","colab_type":"code","outputId":"b5074a60-b9b8-4313-ca63-1ba44addd461","executionInfo":{"status":"ok","timestamp":1550259045549,"user_tz":480,"elapsed":271,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":34}},"cell_type":"code","source":["a = [9, 10, 2, 8, 9, 3, 7]\n","print(countSort(a))"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[2, 3, 7, 8, 9, 9, 10]\n"],"name":"stdout"}]},{"metadata":{"id":"lHFCwzZNGrMu","colab_type":"text"},"cell_type":"markdown","source":["## Bubble Sort in O(n2)"]},{"metadata":{"id":"uqzSlyDXGzhO","colab_type":"code","colab":{}},"cell_type":"code","source":["def bubbleSort(a):\n"," if not a or len(a) == 1:\n"," return a\n"," n = len(a)\n"," for i in range(n - 1): #n-1 passes, \n"," for j in range(n - i -1): #each pass will have valid window [0, n-i], and j is the starting index of each pair\n"," if a[j] > a[j + 1]:\n"," a[j], a[j + 1] = a[j + 1], a[j] #swap\n"," return a"],"execution_count":0,"outputs":[]},{"metadata":{"id":"B-WK_VNi_unM","colab_type":"code","colab":{}},"cell_type":"code","source":["def bubbleSortOptimized(a):\n"," if not a or len(a) == 1:\n"," return a\n"," n = len(a)\n"," for i in range(n - 1): #n-1 passes, \n"," bSwap = False\n"," for j in range(n - i -1): #each pass will have valid window [0, n-i], and j is the starting index of each pair\n"," if a[j] > a[j + 1]:\n"," a[j], a[j + 1] = a[j + 1], a[j] #swap\n"," bSwap = True\n"," if not bSwap:\n"," break\n"," return a"],"execution_count":0,"outputs":[]},{"metadata":{"id":"npb5NRJdI4sV","colab_type":"code","outputId":"57c48506-47f9-4d27-cad6-b93c3729e4c5","executionInfo":{"status":"ok","timestamp":1550350492596,"user_tz":480,"elapsed":323,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["a = [9, 10, 2, 8, 9, 3, 7]\n","print(bubbleSortOptimized(a))"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[2, 3, 7, 8, 9, 9, 10]\n"],"name":"stdout"}]},{"metadata":{"id":"RZwzr_7xBjae","colab_type":"text"},"cell_type":"markdown","source":["## Selection Sort in O(n2)"]},{"metadata":{"id":"e0U-HGHcBpL4","colab_type":"code","colab":{}},"cell_type":"code","source":["def selectSort(a):\n"," n = len(a)\n"," for i in range(n - 1): #n-1 passes, \n"," ti = n - 1 -i # the position to fill in the largest item of valid window [0, n-i]\n"," li = 0\n"," for j in range(n - i):\n"," if a[j] > a[li]:\n"," li = j\n"," # swap li and ti\n"," print('swap', a[li], a[ti])\n"," a[ti], a[li] = a[li], a[ti]\n"," print(a)\n"," return a\n"," \n"," \n"," #"],"execution_count":0,"outputs":[]},{"metadata":{"id":"r3v62LNjEZVY","colab_type":"code","outputId":"7d4a8ad0-466f-4ff4-dd2f-e214c7979e3e","executionInfo":{"status":"ok","timestamp":1550350715349,"user_tz":480,"elapsed":388,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":256}},"cell_type":"code","source":["a = [9, 10, 2, 8, 9, 3, 9]\n","print(selectSort(a))"],"execution_count":0,"outputs":[{"output_type":"stream","text":["swap 10 9\n","[9, 9, 2, 8, 9, 3, 10]\n","swap 9 3\n","[3, 9, 2, 8, 9, 9, 10]\n","swap 9 9\n","[3, 9, 2, 8, 9, 9, 10]\n","swap 9 8\n","[3, 8, 2, 9, 9, 9, 10]\n","swap 8 2\n","[3, 2, 8, 9, 9, 9, 10]\n","swap 3 2\n","[2, 3, 8, 9, 9, 9, 10]\n","[2, 3, 8, 9, 9, 9, 10]\n"],"name":"stdout"}]},{"metadata":{"id":"IQcElo1mM_Sf","colab_type":"text"},"cell_type":"markdown","source":["## Insertion Sort in O(n2)"]},{"metadata":{"id":"yJL27nHcNEJl","colab_type":"code","colab":{}},"cell_type":"code","source":["def insertionSort(a):\n"," if not a or len(a) == 1:\n"," return a\n"," n = len(a)\n"," sl = [a[0]] # sorted list\n"," for i in range(1, n): # items to be inserted into the sorted\n"," j = 0 \n"," while j < len(sl):\n"," if a[i] > sl[j]:\n"," j += 1\n"," else:\n"," sl.insert(j, a[i])\n"," break\n"," if j == len(sl): # not inserted yet\n"," sl.insert(j, a[i])\n"," return sl\n"," \n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"Zb5jMt-iScSz","colab_type":"code","outputId":"766ddcce-178d-4832-d154-e853a03debaa","executionInfo":{"status":"ok","timestamp":1550259083716,"user_tz":480,"elapsed":283,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":221}},"cell_type":"code","source":["a = [9, 10, 2, 8, 9, 3, 7]\n","print(insertionSort(a))"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[9, 10]\n","[2, 9, 10]\n","[2, 9, 10]\n","[2, 8, 9, 10]\n","[2, 8, 9, 10]\n","[2, 8, 9, 9, 10]\n","[2, 8, 9, 9, 10]\n","[2, 3, 8, 9, 9, 10]\n","[2, 3, 8, 9, 9, 10]\n","[2, 3, 7, 8, 9, 9, 10]\n","[2, 3, 7, 8, 9, 9, 10]\n","[2, 3, 7, 8, 9, 9, 10]\n"],"name":"stdout"}]},{"metadata":{"id":"EI1bDD3106fV","colab_type":"code","colab":{}},"cell_type":"code","source":["def shift(a, start, end):\n"," for i in range(end, start, -1): # [i, j)\n"," a[i] = a[i-1]\n"," \n","def insertionSortForward(a):\n"," if not a or len(a) == 1:\n"," return a\n"," n = len(a)\n"," sl = [a[0]] # sorted list\n"," for i in range(1, n): # items to be inserted into the sorted\n"," for j in range(i):\n"," if a[i] < a[j]:\n"," # shift all other elements [j, i-1]\n"," tmp = a[i]\n"," shift(a, j, i)\n"," a[j] = tmp \n"," return a\n","\n","def insertionSortInPlace(a):\n"," if not a or len(a) == 1:\n"," return a\n"," n = len(a)\n"," for i in range(1, n): # items to be inserted into the sorted\n"," t = a[i]\n"," j = i - 1\n"," while j >= 0 and t < a[j]: # keep comparing if target is still smaller\n"," a[j+1] = a[j] # shift current item backward\n"," j -= 1\n"," a[j+1] = t # a[j] <= t , insert t at the location j+1 \n"," return a"],"execution_count":0,"outputs":[]},{"metadata":{"id":"NgeLGKn51mxY","colab_type":"code","outputId":"6051e043-4c10-48c3-d477-10bb3b13a05b","executionInfo":{"status":"ok","timestamp":1550348226402,"user_tz":480,"elapsed":325,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["a = [9, 10, 2, 8, 9, 3, 7]\n","print(insertionSortInPlace(a))"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[2, 3, 7, 8, 9, 9, 10]\n"],"name":"stdout"}]},{"metadata":{"id":"GN1yseSKImNy","colab_type":"text"},"cell_type":"markdown","source":["## Merge Sort in O(nlogn)"]},{"metadata":{"id":"rILA4lBhdPxD","colab_type":"code","colab":{}},"cell_type":"code","source":["def merge(l, r): \n"," '''combine the left and right sorted list'''\n"," ans = []\n"," i = j = 0 # two pointers each points at l and r\n"," n, m = len(l), len(r)\n"," \n"," # first while loop to merge\n"," while i < n and j < m: \n"," if l[i] <= r[j]:\n"," ans.append(l[i])\n"," i += 1\n"," else:\n"," ans.append(r[j])\n"," j += 1\n"," \n"," # now one list of l and r might have items left\n"," ans += l[i:]\n"," ans += r[j:]\n"," return ans\n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"DK003Ic1Isb3","colab_type":"code","colab":{}},"cell_type":"code","source":["def mergeSort(a, s, e):\n"," # base case , can not be divided further\n"," if s == e:\n"," return [a[s]]\n"," # divide into two halves from the middle point\n"," m = (s + e) // 2\n"," \n"," # conquer\n"," l = mergeSort(a, s , m)\n"," r = mergeSort(a, m+1, e)\n"," \n"," # combine\n"," return merge(l, r)"],"execution_count":0,"outputs":[]},{"metadata":{"id":"ys6tUAd8i7ao","colab_type":"code","outputId":"afc20a0d-012e-413f-baf1-f098bb8e23ce","executionInfo":{"status":"ok","timestamp":1550374945810,"user_tz":480,"elapsed":336,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["a = [9, 10, 2, 8, 9, 3, 7, 9]\n","mergeSort(a, 0, len(a)-1)"],"execution_count":0,"outputs":[{"output_type":"execute_result","data":{"text/plain":["[2, 3, 7, 8, 9, 9, 9, 10]"]},"metadata":{"tags":[]},"execution_count":34}]},{"metadata":{"id":"IL6hPk6IjGSf","colab_type":"text"},"cell_type":"markdown","source":["### prove Merge Sort is stable by sorting tuple and printing id"]},{"metadata":{"id":"UwgdFdaRipEN","colab_type":"code","colab":{}},"cell_type":"code","source":["def mergeTuple(l, r): \n"," '''combine the left and right sorted list'''\n"," ans = []\n"," i = j = 0 # two pointers each points at l and r\n"," n, m = len(l), len(r)\n"," \n"," # first while loop to merge\n"," while i < n and j < m: \n"," if l[i][0] <= r[j][0]: # chaning it to l[i][0] < r[j][0] will not be stable anymore. \n"," ans.append(l[i])\n"," i += 1\n"," else:\n"," ans.append(r[j])\n"," j += 1\n"," \n"," # now one list of l and r might have items left\n"," ans += l[i:]\n"," ans += r[j:]\n"," return ans\n","\n","def mergeSortTuple(a, s, e):\n"," # base case , can not be divided further\n"," if s == e:\n"," return [a[s]]\n"," # divide into two halves from the middle point\n"," m = (s + e) // 2\n"," \n"," # conquer\n"," l = mergeSort(a, s , m)\n"," r = mergeSort(a, m+1, e)\n"," \n"," # combine\n"," return mergeTuple(l, r)"],"execution_count":0,"outputs":[]},{"metadata":{"id":"mm07Ac0-dO3A","colab_type":"code","outputId":"dd9eca57-98b7-4e04-acc2-7a31e7efd61d","executionInfo":{"status":"ok","timestamp":1550378993589,"user_tz":480,"elapsed":914,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":312}},"cell_type":"code","source":["a = [(9, 1), (10, 1), (2, 1), (8, 1), (9, 2), (3, 1), (7, 1), (9, 3)] # the second item represents the index of duplcates\n","ids = [id(x) if x[0] == 9 else None for x in a]\n","sorted_a = mergeSortTuple(a, 0, len(a)-1)\n","ids2 = [id(x) if x[0] == 9 else None for x in sorted_a]\n","print(sorted_a)\n","ids, ids2"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[(2, 1), (3, 1), (7, 1), (8, 1), (9, 2), (9, 3), (9, 1), (10, 1)]\n"],"name":"stdout"},{"output_type":"execute_result","data":{"text/plain":["([140381548618120,\n"," None,\n"," None,\n"," None,\n"," 140381548653128,\n"," None,\n"," None,\n"," 140381548653320],\n"," [None,\n"," None,\n"," None,\n"," None,\n"," 140381548653128,\n"," 140381548653320,\n"," 140381548618120,\n"," None])"]},"metadata":{"tags":[]},"execution_count":47}]},{"metadata":{"id":"h0QYWQaDxt9D","colab_type":"text"},"cell_type":"markdown","source":["## Quick Sort in O(nlogn)"]},{"metadata":{"id":"j4mW9xNrO6hm","colab_type":"code","colab":{}},"cell_type":"code","source":["def partition(a, s, e):\n"," '''Lomuto partition'''\n"," p = a[e]\n"," i = s - 1\n"," for j in range(s, e): #a[s, e-1]\n"," \n"," if a[j] <= p:\n"," i += 1\n"," a[i], a[j] = a[j], a[i] # swap a[i] and a[j]\n"," # print out the range of each region\n","# print('p<->i', [a[x] for x in range(s, i+1)])\n","# print('i+1<->j', [a[x] for x in range(i+1, j+1)])\n"," # place p at position i+1 through swapping with a[i+1]\n"," a[i+1], a[e] = a[e], a[i+1]\n"," return i+1"],"execution_count":0,"outputs":[]},{"metadata":{"id":"YPUErvQ1pT1v","colab_type":"text"},"cell_type":"markdown","source":["### experiment to see the correctness of the Lomuto partition"]},{"metadata":{"id":"oTmEOjk2QQZV","colab_type":"code","outputId":"cc8a3971-7ef6-45b4-da03-d2c46460ffcf","executionInfo":{"status":"ok","timestamp":1550560957592,"user_tz":480,"elapsed":474,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":54}},"cell_type":"code","source":["lst = [9, 10, 2, 8, 9, 3, 7]\n","print(partition(lst, 0, len(lst)-1))\n","print(lst)"],"execution_count":37,"outputs":[{"output_type":"stream","text":["2\n","[2, 3, 7, 8, 9, 10, 9]\n"],"name":"stdout"}]},{"metadata":{"id":"qi_gd9TQpbn_","colab_type":"text"},"cell_type":"markdown","source":["### main algorithm of Quick Sort"]},{"metadata":{"id":"v2_nP14pObAn","colab_type":"code","colab":{}},"cell_type":"code","source":["def quickSort(a, s, e, partition=partition):\n"," # base case , can not be divided further\n"," if s >= e:\n"," return \n"," p = partition(a, s, e)\n"," \n"," # conquer smaller problem\n"," quickSort(a, s , p-1, partition)\n"," quickSort(a, p+1, e, partition)\n"," return"],"execution_count":0,"outputs":[]},{"metadata":{"id":"vPtdpJnApa4n","colab_type":"text"},"cell_type":"markdown","source":[""]},{"metadata":{"id":"8N4QPVcqouFf","colab_type":"text"},"cell_type":"markdown","source":["### experiment to see the stability of Quick Sort"]},{"metadata":{"id":"oOHBweKDgiMw","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":35},"outputId":"1634d771-17cd-4bec-de8d-2da7e3632066","executionInfo":{"status":"ok","timestamp":1550561308432,"user_tz":480,"elapsed":588,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}}},"cell_type":"code","source":["a = [(5, 1), (7, 1),(3, 1), (2, 1), (5, 2), (6,1), (7, 2), (8, 1), (9, 1), (5, 3), (5, 4)] # the second item represents the index of duplcates\n","def partition_tuple(a, s, e):\n"," '''Lomuto partition'''\n"," p = a[e][0]\n"," i = s - 1\n"," for j in range(s, e): #a[s, e-1]\n"," \n"," if a[j][0] <= p:\n"," i += 1\n"," a[i], a[j] = a[j], a[i] # swap a[i] and a[j]\n"," # print out the range of each region\n","# print('p<->i', [a[x] for x in range(s, i+1)])\n","# print('i+1<->j', [a[x] for x in range(i+1, j+1)])\n"," # place p at position i+1 through swapping with a[i+1]\n"," a[i+1], a[e] = a[e], a[i+1]\n"," return i+1\n","quickSort(a, 0, len(a) - 1, partition_tuple)\n","print(a)"],"execution_count":47,"outputs":[{"output_type":"stream","text":["[(2, 1), (3, 1), (5, 1), (5, 2), (5, 3), (5, 4), (6, 1), (7, 1), (7, 2), (8, 1), (9, 1)]\n"],"name":"stdout"}]},{"metadata":{"id":"QqcdUXaepGSa","colab_type":"text"},"cell_type":"markdown","source":["### experiment to see the performance of worst time"]},{"metadata":{"id":"5n7nsw13pLWr","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":54},"outputId":"736cf200-711d-4f42-b2b1-f5c369f70ce8","executionInfo":{"status":"ok","timestamp":1550562301025,"user_tz":480,"elapsed":637,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}}},"cell_type":"code","source":["import random, time\n","lst1 = [random.randint(1, 25) for i in range(400)]\n","lst2 = [i for i in range(400)[::-1]]\n","t1 = time.time()\n","quickSort(lst1, 0, len(lst1)-1, partition)\n","print('time for random values:', time.time()-t1)\n","\n","t1 = time.time()\n","quickSort(lst2, 0, len(lst2)-1, partition)\n","print('time for sorted values:', time.time()-t1)"],"execution_count":56,"outputs":[{"output_type":"stream","text":["time for random values: 0.0017516613006591797\n","time for sorted values: 0.0171658992767334\n"],"name":"stdout"}]},{"metadata":{"id":"0y5x07wwo4Um","colab_type":"text"},"cell_type":"markdown","source":["### Hoare Partition"]},{"metadata":{"id":"3vNUigFmo7ei","colab_type":"code","colab":{}},"cell_type":"code","source":["# def partition_hoare(a, s, e):\n","# '''Hoare Parition'''\n","# p = a[e]\n","# i = s\n","# j = e-1\n","# while True:\n","# while a[i] <= p and i < j:\n","# i += 1\n","# while a[j] > p and i < j:\n","# j -= 1\n","# if i < j:\n","# a[i], a[j] = a[j], a[i]\n","# else:\n","# return j\n","# return j"],"execution_count":0,"outputs":[]},{"metadata":{"id":"GKbXRk4Czwjt","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":54},"outputId":"be048173-9125-4716-89f0-17ad7fb0b345","executionInfo":{"status":"ok","timestamp":1550564268216,"user_tz":480,"elapsed":325,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}}},"cell_type":"code","source":["# lst = [9, 10, 2, 8, 9, 3, 7]\n","# print(partition_hoare(lst, 0, len(lst)-1))\n","# print(lst)"],"execution_count":72,"outputs":[{"output_type":"stream","text":["2\n","[3, 2, 10, 8, 9, 9, 7]\n"],"name":"stdout"}]},{"metadata":{"id":"4EDqug7Yg2yl","colab_type":"text"},"cell_type":"markdown","source":["## Heap Sort in O(nlogn)"]},{"metadata":{"id":"0PE9BxQBg7lu","colab_type":"code","colab":{}},"cell_type":"code","source":["from heapq import heapify, heappop\n","def heapsort(a):\n"," heapify(a)\n"," return [heappop(a) for i in range(len(a))]"],"execution_count":0,"outputs":[]},{"metadata":{"id":"PcUurYvkg_Px","colab_type":"code","outputId":"a96f214c-6fdb-4ed5-9c94-7e8772e65fb5","executionInfo":{"status":"ok","timestamp":1550525399708,"user_tz":480,"elapsed":286,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["lst = [21, 1, 45, 78, 3, 5]\n","heapsort(lst)"],"execution_count":0,"outputs":[{"output_type":"execute_result","data":{"text/plain":["[1, 3, 5, 21, 45, 78]"]},"metadata":{"tags":[]},"execution_count":2}]},{"metadata":{"id":"K1EUj-De16tk","colab_type":"text"},"cell_type":"markdown","source":["## Bucket Sort"]},{"metadata":{"id":"GOF5OoRGioTg","colab_type":"code","colab":{}},"cell_type":"code","source":[""],"execution_count":0,"outputs":[]}]} diff --git a/Colab Codes/Colab Notebooks/graph_data_structure.ipynb b/Colab Codes/Colab Notebooks/graph_data_structure.ipynb index ddc2a1c..79a4745 100644 --- a/Colab Codes/Colab Notebooks/graph_data_structure.ipynb +++ b/Colab Codes/Colab Notebooks/graph_data_structure.ipynb @@ -1 +1 @@ -{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"graph_data_structure.ipynb","version":"0.3.2","provenance":[],"collapsed_sections":[],"toc_visible":true},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"metadata":{"id":"QqvQdfYqnyIq","colab_type":"text"},"cell_type":"markdown","source":["### Python 2-d array"]},{"metadata":{"id":"GaOxthzYn1vA","colab_type":"code","colab":{}},"cell_type":"code","source":["ta = [[11, 3, 9, 1], [25, 6,10], [10, 8, 12, 5]]"],"execution_count":0,"outputs":[]},{"metadata":{"id":"I7HYxgqMoJUP","colab_type":"code","outputId":"b109f9c0-cf54-47fb-ffdf-a176b0f04b89","executionInfo":{"status":"ok","timestamp":1552498711913,"user_tz":420,"elapsed":4896,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":54}},"cell_type":"code","source":["print(ta[0])\n","print(ta[2][1])"],"execution_count":2,"outputs":[{"output_type":"stream","text":["[11, 3, 9, 1]\n","8\n"],"name":"stdout"}]},{"metadata":{"id":"r8TlWsjzqZ4R","colab_type":"text"},"cell_type":"markdown","source":["#### Empty 2-d array"]},{"metadata":{"id":"MAM2UOQ7qiAN","colab_type":"code","outputId":"e63256a8-7834-4f8c-a07d-2c84eeaf3d5d","executionInfo":{"status":"ok","timestamp":1552498711918,"user_tz":420,"elapsed":4869,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["empty_2d = [[]]\n","print(empty_2d)"],"execution_count":3,"outputs":[{"output_type":"stream","text":["[[]]\n"],"name":"stdout"}]},{"metadata":{"id":"3BoPkppMqo3V","colab_type":"text"},"cell_type":"markdown","source":["#### fix the outer dimension"]},{"metadata":{"id":"LBMKQA59qv8E","colab_type":"code","outputId":"99575bde-cfaa-4f8d-edf7-10d2f71d374e","executionInfo":{"status":"ok","timestamp":1552498711924,"user_tz":420,"elapsed":4852,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["fix_out_d = [[] for _ in range(5)]\n","print(fix_out_d)"],"execution_count":4,"outputs":[{"output_type":"stream","text":["[[], [], [], [], []]\n"],"name":"stdout"}]},{"metadata":{"id":"_GtKfbeRtnIo","colab_type":"text"},"cell_type":"markdown","source":["#### matrices"]},{"metadata":{"id":"PCR24OxUto-Z","colab_type":"code","outputId":"495a315c-e171-4068-f183-310a64a25ec6","executionInfo":{"status":"ok","timestamp":1552498711929,"user_tz":420,"elapsed":4839,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["rows, cols = 3, 4\n","m1 = [[0 for _ in range(cols)] for _ in range(rows)] # rows * cols\n","m2 = [[0]*cols for _ in range(rows)] # rows * cols\n","print(m1, m2)"],"execution_count":5,"outputs":[{"output_type":"stream","text":["[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]\n"],"name":"stdout"}]},{"metadata":{"id":"2bzn_EbNuWrt","colab_type":"code","outputId":"f190b0f1-d8e8-4967-ef48-b4bc5b3f9716","executionInfo":{"status":"ok","timestamp":1552498711932,"user_tz":420,"elapsed":4825,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["m1[1][2] = 1\n","m2[1][2] = 1\n","print(m1, m2)"],"execution_count":6,"outputs":[{"output_type":"stream","text":["[[0, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]] [[0, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]]\n"],"name":"stdout"}]},{"metadata":{"id":"bILW8JA1vRG0","colab_type":"code","outputId":"3308eee8-202a-405c-fa4b-9aef3f70f9f3","executionInfo":{"status":"ok","timestamp":1552498711935,"user_tz":420,"elapsed":4813,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["m4 = [[0]*cols]*rows\n","m4[1][2] = 1\n","print(m4)"],"execution_count":7,"outputs":[{"output_type":"stream","text":["[[0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 1, 0]]\n"],"name":"stdout"}]},{"metadata":{"id":"l6jRaJGuwKiK","colab_type":"text"},"cell_type":"markdown","source":["#### access rows"]},{"metadata":{"id":"o1MWJZtOwMUr","colab_type":"code","outputId":"5637c2c6-776d-4c31-a697-532e65b5a8cb","executionInfo":{"status":"ok","timestamp":1552498711937,"user_tz":420,"elapsed":4798,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":72}},"cell_type":"code","source":["for row in m1:\n"," print(row)"],"execution_count":8,"outputs":[{"output_type":"stream","text":["[0, 0, 0, 0]\n","[0, 0, 1, 0]\n","[0, 0, 0, 0]\n"],"name":"stdout"}]},{"metadata":{"id":"nuwXftOcw4G0","colab_type":"text"},"cell_type":"markdown","source":["#### access cols"]},{"metadata":{"id":"2JZVSYv_w5yn","colab_type":"code","outputId":"e3b76e39-3260-458f-8906-c3acc9aee004","executionInfo":{"status":"ok","timestamp":1552498711939,"user_tz":420,"elapsed":4785,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":90}},"cell_type":"code","source":["for i in range(cols):\n"," col = [row[i] for row in m1]\n"," print(col)"],"execution_count":9,"outputs":[{"output_type":"stream","text":["[0, 0, 0]\n","[0, 0, 0]\n","[0, 1, 0]\n","[0, 0, 0]\n"],"name":"stdout"}]},{"metadata":{"id":"OUtscU8nxZMY","colab_type":"code","outputId":"8438deaa-00f7-4c85-f39d-af4fb9b91ca8","executionInfo":{"status":"ok","timestamp":1552498711946,"user_tz":420,"elapsed":4778,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["transposedM1 = list(zip(*m1))\n","print(transposedM1)"],"execution_count":10,"outputs":[{"output_type":"stream","text":["[(0, 0, 0), (0, 0, 0), (0, 1, 0), (0, 0, 0)]\n"],"name":"stdout"}]},{"metadata":{"id":"fmVFH68bbFi6","colab_type":"text"},"cell_type":"markdown","source":["### adjacency matrix"]},{"metadata":{"id":"60craYL7bIC_","colab_type":"code","outputId":"b96ceee6-7b8b-4bee-8473-babc442774e2","executionInfo":{"status":"ok","timestamp":1552498711949,"user_tz":420,"elapsed":4762,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["am = [[0]*7 for _ in range(7)]\n","\n","# set 8 edges\n","am[0][1] = am[1][0] = 1\n","am[0][2] = am[2][0] = 1\n","am[1][2] = am[2][1] = 1\n","am[1][3] = am[3][1] = 1\n","am[2][4] = am[4][2] = 1\n","am[3][4] = am[4][3] = 1\n","am[4][5] = am[5][4] = 1\n","am[5][6] = am[6][5] = 1\n","\n","print(am)"],"execution_count":11,"outputs":[{"output_type":"stream","text":["[[0, 1, 1, 0, 0, 0, 0], [1, 0, 1, 1, 0, 0, 0], [1, 1, 0, 0, 1, 0, 0], [0, 1, 0, 0, 1, 0, 0], [0, 0, 1, 1, 0, 1, 0], [0, 0, 0, 0, 1, 0, 1], [0, 0, 0, 0, 0, 1, 0]]\n"],"name":"stdout"}]},{"metadata":{"id":"Bbh2mPsUdhzm","colab_type":"text"},"cell_type":"markdown","source":["### adjacency list"]},{"metadata":{"id":"nfeVyXxadkJM","colab_type":"code","outputId":"da19e829-0800-4bec-f563-5a48459cd27e","executionInfo":{"status":"ok","timestamp":1552498711954,"user_tz":420,"elapsed":4749,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["al = [[] for _ in range(7)]\n","\n","# set 8 edges\n","al[0] = [1, 2]\n","al[1] = [2, 3]\n","al[2] = [0, 4]\n","al[3] = [1, 4]\n","al[4] = [2, 3, 5]\n","al[5] = [4, 6]\n","al[6] = [5]\n","\n","print(al)"],"execution_count":12,"outputs":[{"output_type":"stream","text":["[[1, 2], [2, 3], [0, 4], [1, 4], [2, 3, 5], [4, 6], [5]]\n"],"name":"stdout"}]},{"metadata":{"id":"pc6flSew8FXi","colab_type":"text"},"cell_type":"markdown","source":["### edge list"]},{"metadata":{"id":"LKJ3ch4s8G3x","colab_type":"code","outputId":"8b38471b-d9eb-4b8a-c37a-43bb61474da6","executionInfo":{"status":"ok","timestamp":1552498711957,"user_tz":420,"elapsed":4735,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["el = []\n","el.extend([[0, 1], [1, 0]])\n","el.extend([[0, 2], [2, 0]])\n","el.extend([[1, 2], [2, 1]])\n","el.extend([[1, 3], [3, 1]])\n","el.extend([[3, 4], [4, 3]])\n","el.extend([[2, 4], [4, 2]])\n","el.extend([[4, 5], [5, 4]])\n","el.extend([[5, 6], [6, 5]])\n","\n","print(el)"],"execution_count":13,"outputs":[{"output_type":"stream","text":["[[0, 1], [1, 0], [0, 2], [2, 0], [1, 2], [2, 1], [1, 3], [3, 1], [3, 4], [4, 3], [2, 4], [4, 2], [4, 5], [5, 4], [5, 6], [6, 5]]\n"],"name":"stdout"}]},{"metadata":{"id":"YFtL16Ko2OaU","colab_type":"text"},"cell_type":"markdown","source":["### Use dictionary data structure"]},{"metadata":{"id":"lLL_GR0L2R7U","colab_type":"code","outputId":"d9f7163c-3927-49b5-86c3-73c814ae1880","executionInfo":{"status":"ok","timestamp":1552498711958,"user_tz":420,"elapsed":4722,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["from collections import defaultdict\n","\n","d = defaultdict(set)\n","for v1, v2 in el:\n"," d[chr(v1 + ord('a'))].add(chr(v2 + ord('a')))\n","\n","print(d)"],"execution_count":14,"outputs":[{"output_type":"stream","text":["defaultdict(, {'a': {'c', 'b'}, 'b': {'a', 'd', 'c'}, 'c': {'a', 'e', 'b'}, 'd': {'e', 'b'}, 'e': {'d', 'f', 'c'}, 'f': {'e', 'g'}, 'g': {'f'}})\n"],"name":"stdout"}]},{"metadata":{"id":"R8_0w0qR3ilj","colab_type":"code","outputId":"1e1bf46f-94ee-4110-d63c-95ce6767e292","executionInfo":{"status":"ok","timestamp":1552498711963,"user_tz":420,"elapsed":4712,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["dw = defaultdict(dict)\n","for v1, v2 in el:\n"," dw[v1][v2] = v1 + v2\n","print(dw)"],"execution_count":15,"outputs":[{"output_type":"stream","text":["defaultdict(, {0: {1: 1, 2: 2}, 1: {0: 1, 2: 3, 3: 4}, 2: {0: 2, 1: 3, 4: 6}, 3: {1: 4, 4: 7}, 4: {3: 7, 2: 6, 5: 9}, 5: {4: 9, 6: 11}, 6: {5: 11}})\n"],"name":"stdout"}]},{"metadata":{"id":"b_55IC6F8iP_","colab_type":"text"},"cell_type":"markdown","source":["# Breath-first Search"]},{"metadata":{"id":"b_Bo_1as8kX1","colab_type":"code","colab":{}},"cell_type":"code","source":["class STATE:\n"," white = 0\n"," gray = 1\n"," black = 2\n"," \n","def bfs(g, s):\n"," '''node by node bfs using queue''' \n"," v = len(g)\n"," state = [False] * v\n"," \n"," # allocate space for the predecessor list and colors \n"," pi = [None] * v\n"," state[s] = True # make the state of the visiting node\n"," dist = [0] * v\n"," \n"," q, orders = [s], [s]\n"," while q:\n"," u = q.pop(0)\n"," \n"," print(u, ' out, ', end = ' ')\n"," for v in g[u]:\n"," if not state[v]:\n"," state[v] = True\n"," pi[v] = u # set the predecessor\n"," dist[v] = dist[u] + 1\n"," q.append(v)\n"," orders.append(v)\n"," print(v, ' in', end = ' ')\n"," return orders, pi, dist\n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"meQvv4lE_X27","colab_type":"code","outputId":"0a4875f0-c2ad-4d1a-c869-6a9026221ba9","executionInfo":{"status":"ok","timestamp":1552498711969,"user_tz":420,"elapsed":4697,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["orders, pi, dist = bfs(al, 0)"],"execution_count":17,"outputs":[{"output_type":"stream","text":["0 out, 1 in 2 in 1 out, 3 in 2 out, 4 in 3 out, 4 out, 5 in 5 out, 6 in 6 out, "],"name":"stdout"}]},{"metadata":{"id":"TEZR7eayuZuI","colab_type":"code","outputId":"801c3c94-75f5-4b89-d578-69d262d29c68","executionInfo":{"status":"ok","timestamp":1552498711971,"user_tz":420,"elapsed":4685,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["print(pi)"],"execution_count":18,"outputs":[{"output_type":"stream","text":["[None, 0, 0, 1, 2, 4, 5]\n"],"name":"stdout"}]},{"metadata":{"id":"CoGfQZk1BmqD","colab_type":"code","colab":{}},"cell_type":"code","source":["def get_path(s, t, pi):\n"," '''iterative'''\n"," p = t\n"," path = []\n"," while p != s:\n"," path.append(p)\n"," p = pi[p]\n"," path.append(s)\n"," return path[::-1]\n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"XqYMGN4nCBJA","colab_type":"code","outputId":"8a7e1a63-7409-4c37-f80c-a3ba5a7d58b2","executionInfo":{"status":"ok","timestamp":1552498711982,"user_tz":420,"elapsed":4676,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["get_path(0, 5, pi)"],"execution_count":20,"outputs":[{"output_type":"execute_result","data":{"text/plain":["[0, 2, 4, 5]"]},"metadata":{"tags":[]},"execution_count":20}]},{"metadata":{"id":"wmejAVv8s-ZV","colab_type":"code","colab":{}},"cell_type":"code","source":["def get_path(s, t, pi, path):\n"," '''recursive'''\n"," if s == t:\n"," path.append(t)\n"," return\n"," elif pi[t] is None:\n"," print('no path from ', s, ' to ', v)\n"," else:\n"," get_path(s, pi[t], pi, path)\n"," path.append(t)\n"," return"],"execution_count":0,"outputs":[]},{"metadata":{"id":"4aUNsrKPtkG6","colab_type":"code","outputId":"940a2f48-7d81-4dcd-e24a-0128271c5263","executionInfo":{"status":"ok","timestamp":1552498711988,"user_tz":420,"elapsed":4663,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["path = []\n","get_path(0, 5, pi, path)\n","print(path)"],"execution_count":22,"outputs":[{"output_type":"stream","text":["[0, 2, 4, 5]\n"],"name":"stdout"}]},{"metadata":{"id":"aR60hTwOQOo0","colab_type":"code","colab":{}},"cell_type":"code","source":["def bfs(g, s):\n"," '''simplified bfs'''\n"," v = len(g)\n"," colors = [STATE.white] * v\n"," \n"," q, orders = [s], [s]\n"," complete_orders = []\n"," colors[s] = STATE.gray # make the state of the visiting node\n"," while q:\n"," u = q.pop(0)\n"," \n"," for v in g[u]:\n"," if colors[v] == STATE.white:\n"," colors[v] = STATE.gray\n"," q.append(v)\n"," orders.append(v)\n"," # complete \n"," colors[u] = STATE.black\n"," complete_orders.append(u)\n"," return orders, complete_orders"],"execution_count":0,"outputs":[]},{"metadata":{"id":"CN9MaYWwQ5vb","colab_type":"code","outputId":"4ddf5a46-d062-40ef-8ebe-804bb6467cb2","executionInfo":{"status":"ok","timestamp":1552498711998,"user_tz":420,"elapsed":4658,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["print(bfs(al, 0))"],"execution_count":24,"outputs":[{"output_type":"stream","text":["([0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6])\n"],"name":"stdout"}]},{"metadata":{"id":"I-LT_HgDrrtI","colab_type":"text"},"cell_type":"markdown","source":["#### level by level bfs"]},{"metadata":{"id":"sQyW-n5qrt6k","colab_type":"code","colab":{}},"cell_type":"code","source":["def bfs_level(g, s):\n"," '''level by level bfs'''\n"," v = len(g)\n"," state = [False] * v\n"," \n"," orders = []\n"," lst = [s]\n"," state[s] = True\n"," d = 0 # track distance\n"," while lst:\n"," print('distance ', d, ': ', lst)\n"," tmp_lst = []\n"," for u in lst:\n"," orders.append(u)\n"," for v in g[u]:\n"," if not state[v]:\n"," state[v] = True\n"," tmp_lst.append(v) \n"," lst = tmp_lst\n"," d += 1\n"," return orders\n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"KwUfwlRN2qbe","colab_type":"code","outputId":"79c23a32-db70-483b-db81-3ee958fea037","executionInfo":{"status":"ok","timestamp":1552498712006,"user_tz":420,"elapsed":4646,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":127}},"cell_type":"code","source":["print(bfs_level(al, 0))"],"execution_count":26,"outputs":[{"output_type":"stream","text":["distance 0 : [0]\n","distance 1 : [1, 2]\n","distance 2 : [3, 4]\n","distance 3 : [5]\n","distance 4 : [6]\n","[0, 1, 2, 3, 4, 5, 6]\n"],"name":"stdout"}]},{"metadata":{"id":"KDFDgG-BOBml","colab_type":"text"},"cell_type":"markdown","source":["# Depth-first Search"]},{"metadata":{"id":"TAPb-16WOHMI","colab_type":"code","colab":{}},"cell_type":"code","source":["def dfs(g, s, colors, orders, complete_orders):\n"," colors[s] = STATE.gray\n"," orders.append(s)\n"," for v in g[s]:\n"," if colors[v] == STATE.white:\n"," dfs(g, v, colors, orders, complete_orders)\n"," # complete\n"," colors[s] = STATE.black # this is not necessary in the code, just to help track the state\n"," complete_orders.append(s)\n"," return"],"execution_count":0,"outputs":[]},{"metadata":{"id":"IdRqJxJ34qkw","colab_type":"code","outputId":"45757452-267b-41e1-dfa0-c45cc2c95f01","executionInfo":{"status":"ok","timestamp":1552498712011,"user_tz":420,"elapsed":4634,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["# initialization\n","'''start from 0'''\n","v = len(al)\n","orders, complete_orders = [], []\n","colors = [STATE.white] * v\n","dfs(al,0, colors, orders, complete_orders)\n","\n","print(orders, complete_orders)"],"execution_count":28,"outputs":[{"output_type":"stream","text":["[0, 1, 2, 4, 3, 5, 6] [3, 6, 5, 4, 2, 1, 0]\n"],"name":"stdout"}]},{"metadata":{"id":"FNElkf4jmOre","colab_type":"code","outputId":"8dda9d1d-d4da-429a-ac9d-92b56ec3a8f1","executionInfo":{"status":"ok","timestamp":1552498712013,"user_tz":420,"elapsed":4624,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["# initialization\n","'''start from 1'''\n","v = len(al)\n","orders, complete_orders = [], []\n","colors = [STATE.white] * v\n","dfs(al,1, colors, orders, complete_orders)\n","\n","print(orders, complete_orders)"],"execution_count":29,"outputs":[{"output_type":"stream","text":["[1, 2, 0, 4, 3, 5, 6] [0, 3, 6, 5, 4, 2, 1]\n"],"name":"stdout"}]},{"metadata":{"id":"H_5VwJiLqca6","colab_type":"code","colab":{}},"cell_type":"code","source":["def dftIter(g, s):\n"," '''not preserving the same discovery ordering'''\n"," n = len(g)\n"," orders = []\n"," colors = [STATE.white] * n\n"," stack = [s]\n","\n"," orders.append(s) # track gray order\n"," colors[s] = STATE.gray\n"," \n"," while stack:\n"," u = stack.pop()\n"," \n"," for v in g[u]:\n"," if colors[v] == STATE.white:\n"," colors[v] = STATE.gray\n"," stack.append(v)\n"," orders.append(v) # track gray order\n"," \n"," return orders"],"execution_count":0,"outputs":[]},{"metadata":{"id":"q3hq9ARKqqnw","colab_type":"code","outputId":"6644b5fb-752c-48da-cee3-d617a576f501","executionInfo":{"status":"ok","timestamp":1552498712018,"user_tz":420,"elapsed":4608,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["# initialization\n","'''start from 0'''\n","print(dftIter(al,0))"],"execution_count":31,"outputs":[{"output_type":"stream","text":["[0, 1, 2, 4, 3, 5, 6]\n"],"name":"stdout"}]},{"metadata":{"id":"RIVOD221rHii","colab_type":"code","outputId":"cca27f41-bbc6-4f99-8584-c82ef53df5cc","executionInfo":{"status":"ok","timestamp":1552498712021,"user_tz":420,"elapsed":4596,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["print(dftIter(al, 1))"],"execution_count":32,"outputs":[{"output_type":"stream","text":["[1, 2, 3, 4, 5, 6, 0]\n"],"name":"stdout"}]},{"metadata":{"id":"I4eVWYzGj01U","colab_type":"code","colab":{}},"cell_type":"code","source":["def dftIter(g, s):\n"," '''preserving only discovery ordering'''\n"," n = len(g)\n"," orders = []\n"," colors = [STATE.white] * n\n"," stack = [s]\n","\n"," #orders.append(s) # track gray order\n"," #colors[s] = STATE.gray\n"," \n"," while stack:\n"," u = stack.pop()\n"," if colors[u] == STATE.white:\n"," orders.append(u) # track gray order\n"," colors[u] = STATE.gray\n"," for v in g[u][::-1]:\n"," if colors[v] == STATE.white:\n"," \n"," stack.append(v)\n"," #orders.append(v) # track gray order\n"," \n"," return orders"],"execution_count":0,"outputs":[]},{"metadata":{"id":"5JWNwi9rlAER","colab_type":"code","outputId":"b1131559-e0a3-49e6-837b-440d34c01ba3","executionInfo":{"status":"ok","timestamp":1552498712036,"user_tz":420,"elapsed":4596,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["print(dftIter(al, 0))"],"execution_count":34,"outputs":[{"output_type":"stream","text":["[0, 1, 2, 4, 3, 5, 6]\n"],"name":"stdout"}]},{"metadata":{"id":"mBqP-iy9ma3d","colab_type":"code","outputId":"2dab6dff-4fdc-4a44-ec2b-1dee60d0bb06","executionInfo":{"status":"ok","timestamp":1552498712043,"user_tz":420,"elapsed":4588,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["print(dftIter(al, 1))"],"execution_count":35,"outputs":[{"output_type":"stream","text":["[1, 2, 0, 4, 3, 5, 6]\n"],"name":"stdout"}]},{"metadata":{"id":"rrFl2gwokZON","colab_type":"code","colab":{}},"cell_type":"code","source":["def dfsIter(g, s):\n"," '''iterative dfs'''\n"," v = len(g)\n"," orders, complete_orders = [], []\n"," colors = [STATE.white] * v\n"," stack = [s]\n","\n"," orders.append(s) # track gray order\n"," colors[s] = STATE.gray\n"," \n"," while stack:\n"," u = stack[-1]\n"," bAdj = False\n"," for v in g[u]:\n"," if colors[v] == STATE.white:\n"," colors[v] = STATE.gray\n"," stack.append(v)\n"," orders.append(v) # track gray order\n"," bAdj = True\n"," break\n"," \n"," if not bAdj: # if no adjacent is found, pop out\n"," # complete\n"," colors[u] = STATE.black # this is not necessary in the code, just to help track the state\n"," complete_orders.append(u)\n"," stack.pop()\n"," \n"," return orders, complete_orders\n"," \n","\n"," \n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"vGaO1vCbly-a","colab_type":"code","outputId":"c32c9589-3d7c-46d7-dd2c-546cfcc04393","executionInfo":{"status":"ok","timestamp":1552498712054,"user_tz":420,"elapsed":4587,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["print(dfsIter(al, 0))"],"execution_count":37,"outputs":[{"output_type":"stream","text":["([0, 1, 2, 4, 3, 5, 6], [3, 6, 5, 4, 2, 1, 0])\n"],"name":"stdout"}]},{"metadata":{"id":"wo9AyzN4tRSw","colab_type":"text"},"cell_type":"markdown","source":["### To do\n","Implement the simple iterative version that track the discover and finishing time. So that we can use the iterative version in the topological sort and strongly connected component."]},{"metadata":{"id":"RlYFKGBrfxLo","colab_type":"text"},"cell_type":"markdown","source":["### add finish time"]},{"metadata":{"id":"12yqIv0pf38y","colab_type":"code","colab":{}},"cell_type":"code","source":["# def static_var(varname, value):\n","# def decorate(func):\n","# setattr(func, varname, value)\n","# return func\n","# return decorate\n","# @static_var(\"t\", -1)\n","def dfs(g, s, colors, dt, ft):\n"," dfs.t += 1 # static variable\n"," colors[s] = STATE.gray\n"," dt[s] = dfs.t\n"," for v in g[s]:\n"," if colors[v] == STATE.white:\n"," dfs(g, v, colors, dt, ft)\n"," # complete\n"," dfs.t += 1\n"," ft[s] = dfs.t\n"," return"],"execution_count":0,"outputs":[]},{"metadata":{"id":"WKM_eo7ngKsG","colab_type":"code","outputId":"b2955e44-33bf-4a8c-8e7c-d35198fbfdb0","executionInfo":{"status":"ok","timestamp":1552498712061,"user_tz":420,"elapsed":4578,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":54}},"cell_type":"code","source":["# initialization\n","v = len(al)\n","dt, ft = [-1] * v, [-1] * v\n","colors = [STATE.white] * v\n","dfs.t = -1\n","dfs(al,0, colors, dt, ft)\n","\n","merge_orders = [-1] * 2 * v\n","\n","for i, t in enumerate(dt):\n"," merge_orders[t] = i\n"," \n","for i, t in enumerate(ft):\n"," merge_orders[t] = i\n","\n","print(merge_orders)\n","nodes = set()\n","for i in merge_orders:\n"," if i not in nodes:\n"," print('(', i, end = ', ')\n"," nodes.add(i)\n"," else:\n"," print(i, ') ', end = ' ')\n"],"execution_count":39,"outputs":[{"output_type":"stream","text":["[0, 1, 2, 4, 3, 3, 5, 6, 6, 5, 4, 2, 1, 0]\n","( 0, ( 1, ( 2, ( 4, ( 3, 3 ) ( 5, ( 6, 6 ) 5 ) 4 ) 2 ) 1 ) 0 ) "],"name":"stdout"}]}]} \ No newline at end of file +{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"graph_data_structure.ipynb","version":"0.3.2","provenance":[],"collapsed_sections":[],"toc_visible":true},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"metadata":{"id":"QqvQdfYqnyIq","colab_type":"text"},"cell_type":"markdown","source":["### Python 2-d array"]},{"metadata":{"id":"GaOxthzYn1vA","colab_type":"code","colab":{}},"cell_type":"code","source":["ta = [[11, 3, 9, 1], [25, 6,10], [10, 8, 12, 5]]"],"execution_count":0,"outputs":[]},{"metadata":{"id":"I7HYxgqMoJUP","colab_type":"code","outputId":"b109f9c0-cf54-47fb-ffdf-a176b0f04b89","executionInfo":{"status":"ok","timestamp":1552498711913,"user_tz":420,"elapsed":4896,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":54}},"cell_type":"code","source":["print(ta[0])\n","print(ta[2][1])"],"execution_count":2,"outputs":[{"output_type":"stream","text":["[11, 3, 9, 1]\n","8\n"],"name":"stdout"}]},{"metadata":{"id":"r8TlWsjzqZ4R","colab_type":"text"},"cell_type":"markdown","source":["#### Empty 2-d array"]},{"metadata":{"id":"MAM2UOQ7qiAN","colab_type":"code","outputId":"e63256a8-7834-4f8c-a07d-2c84eeaf3d5d","executionInfo":{"status":"ok","timestamp":1552498711918,"user_tz":420,"elapsed":4869,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["empty_2d = [[]]\n","print(empty_2d)"],"execution_count":3,"outputs":[{"output_type":"stream","text":["[[]]\n"],"name":"stdout"}]},{"metadata":{"id":"3BoPkppMqo3V","colab_type":"text"},"cell_type":"markdown","source":["#### fix the outer dimension"]},{"metadata":{"id":"LBMKQA59qv8E","colab_type":"code","outputId":"99575bde-cfaa-4f8d-edf7-10d2f71d374e","executionInfo":{"status":"ok","timestamp":1552498711924,"user_tz":420,"elapsed":4852,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["fix_out_d = [[] for _ in range(5)]\n","print(fix_out_d)"],"execution_count":4,"outputs":[{"output_type":"stream","text":["[[], [], [], [], []]\n"],"name":"stdout"}]},{"metadata":{"id":"_GtKfbeRtnIo","colab_type":"text"},"cell_type":"markdown","source":["#### matrices"]},{"metadata":{"id":"PCR24OxUto-Z","colab_type":"code","outputId":"495a315c-e171-4068-f183-310a64a25ec6","executionInfo":{"status":"ok","timestamp":1552498711929,"user_tz":420,"elapsed":4839,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["rows, cols = 3, 4\n","m1 = [[0 for _ in range(cols)] for _ in range(rows)] # rows * cols\n","m2 = [[0]*cols for _ in range(rows)] # rows * cols\n","print(m1, m2)"],"execution_count":5,"outputs":[{"output_type":"stream","text":["[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]\n"],"name":"stdout"}]},{"metadata":{"id":"2bzn_EbNuWrt","colab_type":"code","outputId":"f190b0f1-d8e8-4967-ef48-b4bc5b3f9716","executionInfo":{"status":"ok","timestamp":1552498711932,"user_tz":420,"elapsed":4825,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["m1[1][2] = 1\n","m2[1][2] = 1\n","print(m1, m2)"],"execution_count":6,"outputs":[{"output_type":"stream","text":["[[0, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]] [[0, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]]\n"],"name":"stdout"}]},{"metadata":{"id":"bILW8JA1vRG0","colab_type":"code","outputId":"3308eee8-202a-405c-fa4b-9aef3f70f9f3","executionInfo":{"status":"ok","timestamp":1552498711935,"user_tz":420,"elapsed":4813,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["m4 = [[0]*cols]*rows\n","m4[1][2] = 1\n","print(m4)"],"execution_count":7,"outputs":[{"output_type":"stream","text":["[[0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 1, 0]]\n"],"name":"stdout"}]},{"metadata":{"id":"l6jRaJGuwKiK","colab_type":"text"},"cell_type":"markdown","source":["#### access rows"]},{"metadata":{"id":"o1MWJZtOwMUr","colab_type":"code","outputId":"5637c2c6-776d-4c31-a697-532e65b5a8cb","executionInfo":{"status":"ok","timestamp":1552498711937,"user_tz":420,"elapsed":4798,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":72}},"cell_type":"code","source":["for row in m1:\n"," print(row)"],"execution_count":8,"outputs":[{"output_type":"stream","text":["[0, 0, 0, 0]\n","[0, 0, 1, 0]\n","[0, 0, 0, 0]\n"],"name":"stdout"}]},{"metadata":{"id":"nuwXftOcw4G0","colab_type":"text"},"cell_type":"markdown","source":["#### access cols"]},{"metadata":{"id":"2JZVSYv_w5yn","colab_type":"code","outputId":"e3b76e39-3260-458f-8906-c3acc9aee004","executionInfo":{"status":"ok","timestamp":1552498711939,"user_tz":420,"elapsed":4785,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":90}},"cell_type":"code","source":["for i in range(cols):\n"," col = [row[i] for row in m1]\n"," print(col)"],"execution_count":9,"outputs":[{"output_type":"stream","text":["[0, 0, 0]\n","[0, 0, 0]\n","[0, 1, 0]\n","[0, 0, 0]\n"],"name":"stdout"}]},{"metadata":{"id":"OUtscU8nxZMY","colab_type":"code","outputId":"8438deaa-00f7-4c85-f39d-af4fb9b91ca8","executionInfo":{"status":"ok","timestamp":1552498711946,"user_tz":420,"elapsed":4778,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["transposedM1 = list(zip(*m1))\n","print(transposedM1)"],"execution_count":10,"outputs":[{"output_type":"stream","text":["[(0, 0, 0), (0, 0, 0), (0, 1, 0), (0, 0, 0)]\n"],"name":"stdout"}]},{"metadata":{"id":"fmVFH68bbFi6","colab_type":"text"},"cell_type":"markdown","source":["### adjacency matrix"]},{"metadata":{"id":"60craYL7bIC_","colab_type":"code","outputId":"b96ceee6-7b8b-4bee-8473-babc442774e2","executionInfo":{"status":"ok","timestamp":1552498711949,"user_tz":420,"elapsed":4762,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["am = [[0]*7 for _ in range(7)]\n","\n","# set 8 edges\n","am[0][1] = am[1][0] = 1\n","am[0][2] = am[2][0] = 1\n","am[1][2] = am[2][1] = 1\n","am[1][3] = am[3][1] = 1\n","am[2][4] = am[4][2] = 1\n","am[3][4] = am[4][3] = 1\n","am[4][5] = am[5][4] = 1\n","am[5][6] = am[6][5] = 1\n","\n","print(am)"],"execution_count":11,"outputs":[{"output_type":"stream","text":["[[0, 1, 1, 0, 0, 0, 0], [1, 0, 1, 1, 0, 0, 0], [1, 1, 0, 0, 1, 0, 0], [0, 1, 0, 0, 1, 0, 0], [0, 0, 1, 1, 0, 1, 0], [0, 0, 0, 0, 1, 0, 1], [0, 0, 0, 0, 0, 1, 0]]\n"],"name":"stdout"}]},{"metadata":{"id":"Bbh2mPsUdhzm","colab_type":"text"},"cell_type":"markdown","source":["### adjacency list"]},{"metadata":{"id":"nfeVyXxadkJM","colab_type":"code","outputId":"da19e829-0800-4bec-f563-5a48459cd27e","executionInfo":{"status":"ok","timestamp":1552498711954,"user_tz":420,"elapsed":4749,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["al = [[] for _ in range(7)]\n","\n","# set 8 edges\n","al[0] = [1, 2]\n","al[1] = [2, 3]\n","al[2] = [0, 4]\n","al[3] = [1, 4]\n","al[4] = [2, 3, 5]\n","al[5] = [4, 6]\n","al[6] = [5]\n","\n","print(al)"],"execution_count":12,"outputs":[{"output_type":"stream","text":["[[1, 2], [2, 3], [0, 4], [1, 4], [2, 3, 5], [4, 6], [5]]\n"],"name":"stdout"}]},{"metadata":{"id":"pc6flSew8FXi","colab_type":"text"},"cell_type":"markdown","source":["### edge list"]},{"metadata":{"id":"LKJ3ch4s8G3x","colab_type":"code","outputId":"8b38471b-d9eb-4b8a-c37a-43bb61474da6","executionInfo":{"status":"ok","timestamp":1552498711957,"user_tz":420,"elapsed":4735,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["el = []\n","el.extend([[0, 1], [1, 0]])\n","el.extend([[0, 2], [2, 0]])\n","el.extend([[1, 2], [2, 1]])\n","el.extend([[1, 3], [3, 1]])\n","el.extend([[3, 4], [4, 3]])\n","el.extend([[2, 4], [4, 2]])\n","el.extend([[4, 5], [5, 4]])\n","el.extend([[5, 6], [6, 5]])\n","\n","print(el)"],"execution_count":13,"outputs":[{"output_type":"stream","text":["[[0, 1], [1, 0], [0, 2], [2, 0], [1, 2], [2, 1], [1, 3], [3, 1], [3, 4], [4, 3], [2, 4], [4, 2], [4, 5], [5, 4], [5, 6], [6, 5]]\n"],"name":"stdout"}]},{"metadata":{"id":"YFtL16Ko2OaU","colab_type":"text"},"cell_type":"markdown","source":["### Use dictionary data structure"]},{"metadata":{"id":"lLL_GR0L2R7U","colab_type":"code","outputId":"d9f7163c-3927-49b5-86c3-73c814ae1880","executionInfo":{"status":"ok","timestamp":1552498711958,"user_tz":420,"elapsed":4722,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["from collections import defaultdict\n","\n","d = defaultdict(set)\n","for v1, v2 in el:\n"," d[chr(v1 + ord('a'))].add(chr(v2 + ord('a')))\n","\n","print(d)"],"execution_count":14,"outputs":[{"output_type":"stream","text":["defaultdict(, {'a': {'c', 'b'}, 'b': {'a', 'd', 'c'}, 'c': {'a', 'e', 'b'}, 'd': {'e', 'b'}, 'e': {'d', 'f', 'c'}, 'f': {'e', 'g'}, 'g': {'f'}})\n"],"name":"stdout"}]},{"metadata":{"id":"R8_0w0qR3ilj","colab_type":"code","outputId":"1e1bf46f-94ee-4110-d63c-95ce6767e292","executionInfo":{"status":"ok","timestamp":1552498711963,"user_tz":420,"elapsed":4712,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["dw = defaultdict(dict)\n","for v1, v2 in el:\n"," dw[v1][v2] = v1 + v2\n","print(dw)"],"execution_count":15,"outputs":[{"output_type":"stream","text":["defaultdict(, {0: {1: 1, 2: 2}, 1: {0: 1, 2: 3, 3: 4}, 2: {0: 2, 1: 3, 4: 6}, 3: {1: 4, 4: 7}, 4: {3: 7, 2: 6, 5: 9}, 5: {4: 9, 6: 11}, 6: {5: 11}})\n"],"name":"stdout"}]},{"metadata":{"id":"b_55IC6F8iP_","colab_type":"text"},"cell_type":"markdown","source":["# Breadth-first Search"]},{"metadata":{"id":"b_Bo_1as8kX1","colab_type":"code","colab":{}},"cell_type":"code","source":["class STATE:\n"," white = 0\n"," gray = 1\n"," black = 2\n"," \n","def bfs(g, s):\n"," '''node by node bfs using queue''' \n"," v = len(g)\n"," state = [False] * v\n"," \n"," # allocate space for the predecessor list and colors \n"," pi = [None] * v\n"," state[s] = True # make the state of the visiting node\n"," dist = [0] * v\n"," \n"," q, orders = [s], [s]\n"," while q:\n"," u = q.pop(0)\n"," \n"," print(u, ' out, ', end = ' ')\n"," for v in g[u]:\n"," if not state[v]:\n"," state[v] = True\n"," pi[v] = u # set the predecessor\n"," dist[v] = dist[u] + 1\n"," q.append(v)\n"," orders.append(v)\n"," print(v, ' in', end = ' ')\n"," return orders, pi, dist\n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"meQvv4lE_X27","colab_type":"code","outputId":"0a4875f0-c2ad-4d1a-c869-6a9026221ba9","executionInfo":{"status":"ok","timestamp":1552498711969,"user_tz":420,"elapsed":4697,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["orders, pi, dist = bfs(al, 0)"],"execution_count":17,"outputs":[{"output_type":"stream","text":["0 out, 1 in 2 in 1 out, 3 in 2 out, 4 in 3 out, 4 out, 5 in 5 out, 6 in 6 out, "],"name":"stdout"}]},{"metadata":{"id":"TEZR7eayuZuI","colab_type":"code","outputId":"801c3c94-75f5-4b89-d578-69d262d29c68","executionInfo":{"status":"ok","timestamp":1552498711971,"user_tz":420,"elapsed":4685,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["print(pi)"],"execution_count":18,"outputs":[{"output_type":"stream","text":["[None, 0, 0, 1, 2, 4, 5]\n"],"name":"stdout"}]},{"metadata":{"id":"CoGfQZk1BmqD","colab_type":"code","colab":{}},"cell_type":"code","source":["def get_path(s, t, pi):\n"," '''iterative'''\n"," p = t\n"," path = []\n"," while p != s:\n"," path.append(p)\n"," p = pi[p]\n"," path.append(s)\n"," return path[::-1]\n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"XqYMGN4nCBJA","colab_type":"code","outputId":"8a7e1a63-7409-4c37-f80c-a3ba5a7d58b2","executionInfo":{"status":"ok","timestamp":1552498711982,"user_tz":420,"elapsed":4676,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["get_path(0, 5, pi)"],"execution_count":20,"outputs":[{"output_type":"execute_result","data":{"text/plain":["[0, 2, 4, 5]"]},"metadata":{"tags":[]},"execution_count":20}]},{"metadata":{"id":"wmejAVv8s-ZV","colab_type":"code","colab":{}},"cell_type":"code","source":["def get_path(s, t, pi, path):\n"," '''recursive'''\n"," if s == t:\n"," path.append(t)\n"," return\n"," elif pi[t] is None:\n"," print('no path from ', s, ' to ', v)\n"," else:\n"," get_path(s, pi[t], pi, path)\n"," path.append(t)\n"," return"],"execution_count":0,"outputs":[]},{"metadata":{"id":"4aUNsrKPtkG6","colab_type":"code","outputId":"940a2f48-7d81-4dcd-e24a-0128271c5263","executionInfo":{"status":"ok","timestamp":1552498711988,"user_tz":420,"elapsed":4663,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["path = []\n","get_path(0, 5, pi, path)\n","print(path)"],"execution_count":22,"outputs":[{"output_type":"stream","text":["[0, 2, 4, 5]\n"],"name":"stdout"}]},{"metadata":{"id":"aR60hTwOQOo0","colab_type":"code","colab":{}},"cell_type":"code","source":["def bfs(g, s):\n"," '''simplified bfs'''\n"," v = len(g)\n"," colors = [STATE.white] * v\n"," \n"," q, orders = [s], [s]\n"," complete_orders = []\n"," colors[s] = STATE.gray # make the state of the visiting node\n"," while q:\n"," u = q.pop(0)\n"," \n"," for v in g[u]:\n"," if colors[v] == STATE.white:\n"," colors[v] = STATE.gray\n"," q.append(v)\n"," orders.append(v)\n"," # complete \n"," colors[u] = STATE.black\n"," complete_orders.append(u)\n"," return orders, complete_orders"],"execution_count":0,"outputs":[]},{"metadata":{"id":"CN9MaYWwQ5vb","colab_type":"code","outputId":"4ddf5a46-d062-40ef-8ebe-804bb6467cb2","executionInfo":{"status":"ok","timestamp":1552498711998,"user_tz":420,"elapsed":4658,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["print(bfs(al, 0))"],"execution_count":24,"outputs":[{"output_type":"stream","text":["([0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6])\n"],"name":"stdout"}]},{"metadata":{"id":"I-LT_HgDrrtI","colab_type":"text"},"cell_type":"markdown","source":["#### level by level bfs"]},{"metadata":{"id":"sQyW-n5qrt6k","colab_type":"code","colab":{}},"cell_type":"code","source":["def bfs_level(g, s):\n"," '''level by level bfs'''\n"," v = len(g)\n"," state = [False] * v\n"," \n"," orders = []\n"," lst = [s]\n"," state[s] = True\n"," d = 0 # track distance\n"," while lst:\n"," print('distance ', d, ': ', lst)\n"," tmp_lst = []\n"," for u in lst:\n"," orders.append(u)\n"," for v in g[u]:\n"," if not state[v]:\n"," state[v] = True\n"," tmp_lst.append(v) \n"," lst = tmp_lst\n"," d += 1\n"," return orders\n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"KwUfwlRN2qbe","colab_type":"code","outputId":"79c23a32-db70-483b-db81-3ee958fea037","executionInfo":{"status":"ok","timestamp":1552498712006,"user_tz":420,"elapsed":4646,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":127}},"cell_type":"code","source":["print(bfs_level(al, 0))"],"execution_count":26,"outputs":[{"output_type":"stream","text":["distance 0 : [0]\n","distance 1 : [1, 2]\n","distance 2 : [3, 4]\n","distance 3 : [5]\n","distance 4 : [6]\n","[0, 1, 2, 3, 4, 5, 6]\n"],"name":"stdout"}]},{"metadata":{"id":"KDFDgG-BOBml","colab_type":"text"},"cell_type":"markdown","source":["# Depth-first Search"]},{"metadata":{"id":"TAPb-16WOHMI","colab_type":"code","colab":{}},"cell_type":"code","source":["def dfs(g, s, colors, orders, complete_orders):\n"," colors[s] = STATE.gray\n"," orders.append(s)\n"," for v in g[s]:\n"," if colors[v] == STATE.white:\n"," dfs(g, v, colors, orders, complete_orders)\n"," # complete\n"," colors[s] = STATE.black # this is not necessary in the code, just to help track the state\n"," complete_orders.append(s)\n"," return"],"execution_count":0,"outputs":[]},{"metadata":{"id":"IdRqJxJ34qkw","colab_type":"code","outputId":"45757452-267b-41e1-dfa0-c45cc2c95f01","executionInfo":{"status":"ok","timestamp":1552498712011,"user_tz":420,"elapsed":4634,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["# initialization\n","'''start from 0'''\n","v = len(al)\n","orders, complete_orders = [], []\n","colors = [STATE.white] * v\n","dfs(al,0, colors, orders, complete_orders)\n","\n","print(orders, complete_orders)"],"execution_count":28,"outputs":[{"output_type":"stream","text":["[0, 1, 2, 4, 3, 5, 6] [3, 6, 5, 4, 2, 1, 0]\n"],"name":"stdout"}]},{"metadata":{"id":"FNElkf4jmOre","colab_type":"code","outputId":"8dda9d1d-d4da-429a-ac9d-92b56ec3a8f1","executionInfo":{"status":"ok","timestamp":1552498712013,"user_tz":420,"elapsed":4624,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["# initialization\n","'''start from 1'''\n","v = len(al)\n","orders, complete_orders = [], []\n","colors = [STATE.white] * v\n","dfs(al,1, colors, orders, complete_orders)\n","\n","print(orders, complete_orders)"],"execution_count":29,"outputs":[{"output_type":"stream","text":["[1, 2, 0, 4, 3, 5, 6] [0, 3, 6, 5, 4, 2, 1]\n"],"name":"stdout"}]},{"metadata":{"id":"H_5VwJiLqca6","colab_type":"code","colab":{}},"cell_type":"code","source":["def dftIter(g, s):\n"," '''not preserving the same discovery ordering'''\n"," n = len(g)\n"," orders = []\n"," colors = [STATE.white] * n\n"," stack = [s]\n","\n"," orders.append(s) # track gray order\n"," colors[s] = STATE.gray\n"," \n"," while stack:\n"," u = stack.pop()\n"," \n"," for v in g[u]:\n"," if colors[v] == STATE.white:\n"," colors[v] = STATE.gray\n"," stack.append(v)\n"," orders.append(v) # track gray order\n"," \n"," return orders"],"execution_count":0,"outputs":[]},{"metadata":{"id":"q3hq9ARKqqnw","colab_type":"code","outputId":"6644b5fb-752c-48da-cee3-d617a576f501","executionInfo":{"status":"ok","timestamp":1552498712018,"user_tz":420,"elapsed":4608,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["# initialization\n","'''start from 0'''\n","print(dftIter(al,0))"],"execution_count":31,"outputs":[{"output_type":"stream","text":["[0, 1, 2, 4, 3, 5, 6]\n"],"name":"stdout"}]},{"metadata":{"id":"RIVOD221rHii","colab_type":"code","outputId":"cca27f41-bbc6-4f99-8584-c82ef53df5cc","executionInfo":{"status":"ok","timestamp":1552498712021,"user_tz":420,"elapsed":4596,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["print(dftIter(al, 1))"],"execution_count":32,"outputs":[{"output_type":"stream","text":["[1, 2, 3, 4, 5, 6, 0]\n"],"name":"stdout"}]},{"metadata":{"id":"I4eVWYzGj01U","colab_type":"code","colab":{}},"cell_type":"code","source":["def dftIter(g, s):\n"," '''preserving only discovery ordering'''\n"," n = len(g)\n"," orders = []\n"," colors = [STATE.white] * n\n"," stack = [s]\n","\n"," #orders.append(s) # track gray order\n"," #colors[s] = STATE.gray\n"," \n"," while stack:\n"," u = stack.pop()\n"," if colors[u] == STATE.white:\n"," orders.append(u) # track gray order\n"," colors[u] = STATE.gray\n"," for v in g[u][::-1]:\n"," if colors[v] == STATE.white:\n"," \n"," stack.append(v)\n"," #orders.append(v) # track gray order\n"," \n"," return orders"],"execution_count":0,"outputs":[]},{"metadata":{"id":"5JWNwi9rlAER","colab_type":"code","outputId":"b1131559-e0a3-49e6-837b-440d34c01ba3","executionInfo":{"status":"ok","timestamp":1552498712036,"user_tz":420,"elapsed":4596,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["print(dftIter(al, 0))"],"execution_count":34,"outputs":[{"output_type":"stream","text":["[0, 1, 2, 4, 3, 5, 6]\n"],"name":"stdout"}]},{"metadata":{"id":"mBqP-iy9ma3d","colab_type":"code","outputId":"2dab6dff-4fdc-4a44-ec2b-1dee60d0bb06","executionInfo":{"status":"ok","timestamp":1552498712043,"user_tz":420,"elapsed":4588,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["print(dftIter(al, 1))"],"execution_count":35,"outputs":[{"output_type":"stream","text":["[1, 2, 0, 4, 3, 5, 6]\n"],"name":"stdout"}]},{"metadata":{"id":"rrFl2gwokZON","colab_type":"code","colab":{}},"cell_type":"code","source":["def dfsIter(g, s):\n"," '''iterative dfs'''\n"," v = len(g)\n"," orders, complete_orders = [], []\n"," colors = [STATE.white] * v\n"," stack = [s]\n","\n"," orders.append(s) # track gray order\n"," colors[s] = STATE.gray\n"," \n"," while stack:\n"," u = stack[-1]\n"," bAdj = False\n"," for v in g[u]:\n"," if colors[v] == STATE.white:\n"," colors[v] = STATE.gray\n"," stack.append(v)\n"," orders.append(v) # track gray order\n"," bAdj = True\n"," break\n"," \n"," if not bAdj: # if no adjacent is found, pop out\n"," # complete\n"," colors[u] = STATE.black # this is not necessary in the code, just to help track the state\n"," complete_orders.append(u)\n"," stack.pop()\n"," \n"," return orders, complete_orders\n"," \n","\n"," \n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"vGaO1vCbly-a","colab_type":"code","outputId":"c32c9589-3d7c-46d7-dd2c-546cfcc04393","executionInfo":{"status":"ok","timestamp":1552498712054,"user_tz":420,"elapsed":4587,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["print(dfsIter(al, 0))"],"execution_count":37,"outputs":[{"output_type":"stream","text":["([0, 1, 2, 4, 3, 5, 6], [3, 6, 5, 4, 2, 1, 0])\n"],"name":"stdout"}]},{"metadata":{"id":"wo9AyzN4tRSw","colab_type":"text"},"cell_type":"markdown","source":["### To do\n","Implement the simple iterative version that tracks the discovery and finishing time so we can use the iterative version in the topological sort and strongly connected components."]},{"metadata":{"id":"RlYFKGBrfxLo","colab_type":"text"},"cell_type":"markdown","source":["### add finish time"]},{"metadata":{"id":"12yqIv0pf38y","colab_type":"code","colab":{}},"cell_type":"code","source":["# def static_var(varname, value):\n","# def decorate(func):\n","# setattr(func, varname, value)\n","# return func\n","# return decorate\n","# @static_var(\"t\", -1)\n","def dfs(g, s, colors, dt, ft):\n"," dfs.t += 1 # static variable\n"," colors[s] = STATE.gray\n"," dt[s] = dfs.t\n"," for v in g[s]:\n"," if colors[v] == STATE.white:\n"," dfs(g, v, colors, dt, ft)\n"," # complete\n"," dfs.t += 1\n"," ft[s] = dfs.t\n"," return"],"execution_count":0,"outputs":[]},{"metadata":{"id":"WKM_eo7ngKsG","colab_type":"code","outputId":"b2955e44-33bf-4a8c-8e7c-d35198fbfdb0","executionInfo":{"status":"ok","timestamp":1552498712061,"user_tz":420,"elapsed":4578,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":54}},"cell_type":"code","source":["# initialization\n","v = len(al)\n","dt, ft = [-1] * v, [-1] * v\n","colors = [STATE.white] * v\n","dfs.t = -1\n","dfs(al,0, colors, dt, ft)\n","\n","merge_orders = [-1] * 2 * v\n","\n","for i, t in enumerate(dt):\n"," merge_orders[t] = i\n"," \n","for i, t in enumerate(ft):\n"," merge_orders[t] = i\n","\n","print(merge_orders)\n","nodes = set()\n","for i in merge_orders:\n"," if i not in nodes:\n"," print('(', i, end = ', ')\n"," nodes.add(i)\n"," else:\n"," print(i, ') ', end = ' ')\n"],"execution_count":39,"outputs":[{"output_type":"stream","text":["[0, 1, 2, 4, 3, 3, 5, 6, 6, 5, 4, 2, 1, 0]\n","( 0, ( 1, ( 2, ( 4, ( 3, 3 ) ( 5, ( 6, 6 ) 5 ) 4 ) 2 ) 1 ) 0 ) "],"name":"stdout"}]}]} diff --git a/Colab Codes/Colab Notebooks/graph_search.ipynb b/Colab Codes/Colab Notebooks/graph_search.ipynb index 990acca..861ca07 100644 --- a/Colab Codes/Colab Notebooks/graph_search.ipynb +++ b/Colab Codes/Colab Notebooks/graph_search.ipynb @@ -1 +1 @@ -{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"graph_search.ipynb","version":"0.3.2","provenance":[],"collapsed_sections":[],"toc_visible":true},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"metadata":{"id":"ZhL7pFlp8gTD","colab_type":"text"},"cell_type":"markdown","source":["## Backtracking\n","\n","1. Permutation\n","2. Combination\n","3. All Paths\n","\n","\n","\n"]},{"metadata":{"id":"a76_VshDTaE4","colab_type":"text"},"cell_type":"markdown","source":["### Permutation"]},{"metadata":{"id":"XhTLVA0sZw61","colab_type":"code","colab":{}},"cell_type":"code","source":["def A_n_k(a, n, k, depth, used, curr, ans):\n"," '''\n"," Implement permutation of k items out of n items\n"," depth: start from 0, and represent the depth of the search\n"," used: track what items are in the partial solution from the set of n\n"," curr: the current partial solution\n"," ans: collect all the valide solutions\n"," '''\n"," if depth == k: #end condition\n"," ans.append(curr[::]) # use deepcopy because curr is tracking all partial solution, it eventually become []\n"," return\n"," \n"," for i in range(n):\n"," if not used[i]:\n"," # generate the next solution from curr\n"," curr.append(a[i])\n"," used[i] = True\n"," print(curr)\n"," # move to the next solution\n"," A_n_k(a, n, k, depth+1, used, curr, ans)\n"," \n"," #backtrack to previous partial state\n"," curr.pop()\n"," print('backtrack: ', curr)\n"," used[i] = False\n"," return"],"execution_count":0,"outputs":[]},{"metadata":{"id":"RVZsfQb9_Xga","colab_type":"code","outputId":"bb9e8b92-0d8e-4cb7-8dd9-06f2c3384151","executionInfo":{"status":"ok","timestamp":1553585906154,"user_tz":420,"elapsed":440,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":555}},"cell_type":"code","source":["a = [1, 2, 3]\n","n = len(a)\n","ans = [[None]]\n","used = [False] * len(a)\n","ans = []\n","A_n_k(a, n, n, 0, used, [], ans)\n","print(ans)\n"],"execution_count":4,"outputs":[{"output_type":"stream","text":["[1]\n","[1, 2]\n","[1, 2, 3]\n","backtrack: [1, 2]\n","backtrack: [1]\n","[1, 3]\n","[1, 3, 2]\n","backtrack: [1, 3]\n","backtrack: [1]\n","backtrack: []\n","[2]\n","[2, 1]\n","[2, 1, 3]\n","backtrack: [2, 1]\n","backtrack: [2]\n","[2, 3]\n","[2, 3, 1]\n","backtrack: [2, 3]\n","backtrack: [2]\n","backtrack: []\n","[3]\n","[3, 1]\n","[3, 1, 2]\n","backtrack: [3, 1]\n","backtrack: [3]\n","[3, 2]\n","[3, 2, 1]\n","backtrack: [3, 2]\n","backtrack: [3]\n","backtrack: []\n","[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]\n"],"name":"stdout"}]},{"metadata":{"id":"HikETMOQcM7H","colab_type":"text"},"cell_type":"markdown","source":["### Combination"]},{"metadata":{"id":"fcIaL7i7-9na","colab_type":"code","colab":{}},"cell_type":"code","source":["def C_n_k(a, n, k, start, depth, curr, ans):\n"," '''\n"," Implement combination of k items out of n items\n"," start: the start of candinate\n"," depth: start from 0, and represent the depth of the search\n"," curr: the current partial solution\n"," ans: collect all the valide solutions\n"," '''\n"," if depth == k: #end condition\n"," ans.append(curr[::]) \n"," return\n"," \n"," for i in range(start, n): \n"," # generate the next solution from curr\n"," curr.append(a[i])\n"," # move to the next solution\n"," C_n_k(a, n, k, i+1, depth+1, curr, ans)\n","\n"," #backtrack to previous partial state\n"," curr.pop()\n"," return"],"execution_count":0,"outputs":[]},{"metadata":{"id":"FQlAWNcp8-5y","colab_type":"code","outputId":"0dcbc55c-b228-4d99-f37f-5d44111f3183","executionInfo":{"status":"ok","timestamp":1553038549590,"user_tz":420,"elapsed":793,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["a = [1, 2, 3]\n","n = len(a)\n","ans = [[None]]\n","ans = []\n","C_n_k(a, n, 2, 0, 0, [], ans)\n","print(ans)\n"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[[1, 2], [1, 3], [2, 3]]\n"],"name":"stdout"}]},{"metadata":{"id":"ze7L-ttOgCUd","colab_type":"text"},"cell_type":"markdown","source":["### All paths"]},{"metadata":{"id":"jYkhLKk9QZqi","colab_type":"code","colab":{}},"cell_type":"code","source":["def all_paths(g, s, path, ans):\n"," '''generate all pahts with backtrack'''\n"," ans.append(path[::])\n"," for v in g[s]:\n"," path.append(v)\n"," print(path)\n"," all_paths(g, v, path, ans)\n"," path.pop()\n"," print(path, 'backtrack')"],"execution_count":0,"outputs":[]},{"metadata":{"id":"84vSh1JIQyLH","colab_type":"code","outputId":"809fc79d-57cb-4f5a-f444-7f8b41476e79","executionInfo":{"status":"ok","timestamp":1553038549595,"user_tz":420,"elapsed":757,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["al = [[1], [2], [4], [], [3, 5], [6], []]\n","print(al)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[[1], [2], [4], [], [3, 5], [6], []]\n"],"name":"stdout"}]},{"metadata":{"id":"HhBVClwEVeUJ","colab_type":"code","outputId":"1e88bace-af15-4632-b2c4-0cb9cedbe323","executionInfo":{"status":"ok","timestamp":1553038549597,"user_tz":420,"elapsed":740,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":256}},"cell_type":"code","source":["ans = []\n","path = [0]\n","all_paths(al, 0, path, ans)\n","print(ans)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[0, 1]\n","[0, 1, 2]\n","[0, 1, 2, 4]\n","[0, 1, 2, 4, 3]\n","[0, 1, 2, 4] backtrack\n","[0, 1, 2, 4, 5]\n","[0, 1, 2, 4, 5, 6]\n","[0, 1, 2, 4, 5] backtrack\n","[0, 1, 2, 4] backtrack\n","[0, 1, 2] backtrack\n","[0, 1] backtrack\n","[0] backtrack\n","[[0], [0, 1], [0, 1, 2], [0, 1, 2, 4], [0, 1, 2, 4, 3], [0, 1, 2, 4, 5], [0, 1, 2, 4, 5, 6]]\n"],"name":"stdout"}]},{"metadata":{"id":"7kLQciZ1Zt2i","colab_type":"text"},"cell_type":"markdown","source":["## Constraint Satisfaction Problems with Backtracking and Pruning"]},{"metadata":{"id":"oHsib-ORB9Fh","colab_type":"text"},"cell_type":"markdown","source":["First, we build up the board"]},{"metadata":{"id":"wU0IzGC8_EiP","colab_type":"code","colab":{}},"cell_type":"code","source":["board = [[5, 3, None, None, 7, None, None, None, None],\n"," [6, None, None, 1, 9, 5, None, None, None],\n"," [None, 9, 8, None, None, None, None, 6, None],\n"," [8, None, None, None, 6, None, None, None, 3], \n"," [4, None, None, 8, None, 3, None, None, 1], \n"," [7, None, None, None, 2, None, None, None, 6], \n"," [None, 6, None, None, None, None, 2, 8, None], \n"," [None, None, None, 4, 1, 9, None, None, 5],\n"," [None, None, None, None, 8, None, None, 7, 9]]"],"execution_count":0,"outputs":[]},{"metadata":{"id":"Psq2cedrMTGJ","colab_type":"text"},"cell_type":"markdown","source":["Define how to change the state"]},{"metadata":{"id":"t2oeCmmvCotc","colab_type":"code","colab":{}},"cell_type":"code","source":["def setState(i, j, v, row_state, col_state, grid_state):\n"," row_state[i] |= 1 << v\n"," col_state[j] |= 1 << v\n"," grid_index = (i//3)*3 + (j//3)\n"," grid_state[grid_index] |= 1 << v\n"," \n","def resetState(i, j, v, row_state, col_state, grid_state):\n"," row_state[i] &= ~(1 << v)\n"," col_state[j] &= ~(1 << v)\n"," grid_index = (i//3)*3 + (j//3)\n"," grid_state[grid_index] &= ~(1 << v)\n"," \n","def checkState(i, j, v, row_state, col_state, grid_state):\n"," row_bit = (1 << v) & row_state[i] != 0\n"," col_bit = (1 << v) & col_state[j] != 0\n"," grid_index = (i//3)*3 + (j//3)\n"," grid_bit = (1 << v) & grid_state[grid_index] != 0\n"," return not row_bit and not col_bit and not grid_bit"],"execution_count":0,"outputs":[]},{"metadata":{"id":"SgghNi99MWXw","colab_type":"text"},"cell_type":"markdown","source":["Get the empty spots and its values"]},{"metadata":{"id":"v0IZMZHU5FR-","colab_type":"code","colab":{}},"cell_type":"code","source":[" def getEmptySpots(board, rows, cols, row_state, col_state, grid_state): \n"," ''' get empty spots and find its corresponding values in O(n*n)'''\n"," empty_spots = {}\n"," # initialize the state, and get empty spots\n"," for i in range(rows):\n"," for j in range(cols):\n"," if board[i][j]:\n"," # set that bit to 1\n"," setState(i, j, board[i][j]-1, row_state, col_state, grid_state) \n"," else:\n"," empty_spots[(i,j)] = []\n"," \n"," # get possible values for each spot\n"," for i, j in empty_spots.keys():\n"," for v in range(9):\n"," if checkState(i, j, v, row_state, col_state, grid_state):\n"," empty_spots[(i, j)].append(v+1)\n"," \n"," return empty_spots"],"execution_count":0,"outputs":[]},{"metadata":{"id":"39DSr_mfCBrQ","colab_type":"text"},"cell_type":"markdown","source":["Second, we intialize the state and find empty spots. "]},{"metadata":{"id":"G76l_z6DAk4n","colab_type":"code","outputId":"1664fb47-2e89-46f7-c15c-47bff27bc873","executionInfo":{"status":"ok","timestamp":1553038549608,"user_tz":420,"elapsed":682,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":74}},"cell_type":"code","source":["# initialize state\n","row_state = [0]*9\n","col_state = [0]*9\n","grid_state = [0]*9\n","\n","empty_spots = getEmptySpots(board, 9, 9, row_state, col_state, grid_state)\n","print(row_state, col_state, grid_state) \n","sorted_empty_spots = sorted(empty_spots.items(), key=lambda x: len(x[1]))\n","print(sorted_empty_spots)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[84, 305, 416, 164, 141, 98, 162, 281, 448] [248, 292, 128, 137, 483, 276, 2, 224, 309] [436, 337, 32, 200, 166, 37, 32, 393, 466]\n","[((4, 4), [5]), ((6, 5), [7]), ((6, 8), [4]), ((7, 7), [3]), ((0, 3), [2, 6]), ((2, 0), [1, 2]), ((2, 3), [2, 3]), ((2, 4), [3, 4]), ((2, 5), [2, 4]), ((4, 1), [2, 5]), ((5, 1), [1, 5]), ((5, 3), [5, 9]), ((5, 5), [1, 4]), ((6, 4), [3, 5]), ((7, 0), [2, 3]), ((7, 6), [3, 6]), ((8, 5), [2, 6]), ((0, 2), [1, 2, 4]), ((0, 8), [2, 4, 8]), ((1, 1), [2, 4, 7]), ((1, 2), [2, 4, 7]), ((1, 7), [2, 3, 4]), ((2, 8), [2, 4, 7]), ((3, 1), [1, 2, 5]), ((3, 3), [5, 7, 9]), ((3, 5), [1, 4, 7]), ((4, 6), [5, 7, 9]), ((4, 7), [2, 5, 9]), ((5, 7), [4, 5, 9]), ((6, 0), [1, 3, 9]), ((6, 3), [3, 5, 7]), ((7, 1), [2, 7, 8]), ((7, 2), [2, 3, 7]), ((8, 0), [1, 2, 3]), ((0, 5), [2, 4, 6, 8]), ((0, 6), [1, 4, 8, 9]), ((0, 7), [1, 2, 4, 9]), ((1, 6), [3, 4, 7, 8]), ((1, 8), [2, 4, 7, 8]), ((3, 2), [1, 2, 5, 9]), ((3, 6), [4, 5, 7, 9]), ((3, 7), [2, 4, 5, 9]), ((4, 2), [2, 5, 6, 9]), ((5, 2), [1, 3, 5, 9]), ((5, 6), [4, 5, 8, 9]), ((8, 1), [1, 2, 4, 5]), ((8, 3), [2, 3, 5, 6]), ((8, 6), [1, 3, 4, 6]), ((2, 6), [1, 3, 4, 5, 7]), ((8, 2), [1, 2, 3, 4, 5]), ((6, 2), [1, 3, 4, 5, 7, 9])]\n"],"name":"stdout"}]},{"metadata":{"id":"jUPMX4-jF7N_","colab_type":"text"},"cell_type":"markdown","source":["Traverse the empty_spots, and fill in. "]},{"metadata":{"id":"ved6mk_0F6F-","colab_type":"code","colab":{}},"cell_type":"code","source":["def dfs_backtrack(empty_spots, index):\n"," if index == len(empty_spots):\n"," return True\n"," (i, j), vl = empty_spots[index]\n"," \n"," for v in vl: #try each value\n"," # check the state\n"," if checkState(i, j, v-1, row_state, col_state, grid_state):\n"," # set the state\n"," setState(i, j, v-1, row_state, col_state, grid_state)\n"," # mark the board\n"," board[i][j] = v\n"," if dfs_backtrack(empty_spots, index+1):\n"," return True\n"," else:\n"," #backtack to previouse state\n"," resetState(i, j, v-1, row_state, col_state, grid_state)\n"," #unmark the board\n"," board[i][j] = None\n"," return False\n"," \n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"QUr5dZQxIpdn","colab_type":"code","outputId":"0d08b439-e745-478d-98e8-84498f033412","executionInfo":{"status":"ok","timestamp":1553038549612,"user_tz":420,"elapsed":656,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":74}},"cell_type":"code","source":["ans = dfs_backtrack(sorted_empty_spots, 0)\n","print(ans)\n","print(board)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["True\n","[[5, 3, 4, 6, 7, 8, 9, 1, 2], [6, 7, 2, 1, 9, 5, 3, 4, 8], [1, 9, 8, 3, 4, 2, 5, 6, 7], [8, 5, 9, 7, 6, 1, 4, 2, 3], [4, 2, 6, 8, 5, 3, 7, 9, 1], [7, 1, 3, 9, 2, 4, 8, 5, 6], [9, 6, 1, 5, 3, 7, 2, 8, 4], [2, 8, 7, 4, 1, 9, 6, 3, 5], [3, 4, 5, 2, 8, 6, 1, 7, 9]]\n"],"name":"stdout"}]},{"metadata":{"id":"4vOhZxglCBKD","colab_type":"text"},"cell_type":"markdown","source":["#### Sudoku Solver"]},{"metadata":{"id":"j7d_45x3MiY9","colab_type":"code","colab":{}},"cell_type":"code","source":["from copy import deepcopy\n","import time\n","class SudokoSolver():\n"," def __init__(self, board):\n"," self.original_board = deepcopy(board)\n"," self.board = deepcopy(board)\n"," self.n = len(board)\n"," assert (self.n == len(board[0]))\n"," # initialize state\n"," self.row_state = [0]*self.n\n"," self.col_state = [0]*self.n\n"," self.grid_state = [0]*self.n\n"," \n"," def _setState(self, i, j, v):\n"," self.row_state[i] |= 1 << v\n"," self.col_state[j] |= 1 << v\n"," grid_index = (i//3)*3 + (j//3)\n"," self.grid_state[grid_index] |= 1 << v\n"," \n"," def _resetState(self, i, j, v):\n"," self.row_state[i] &= ~(1 << v)\n"," self.col_state[j] &= ~(1 << v)\n"," grid_index = (i//3)*3 + (j//3)\n"," self.grid_state[grid_index] &= ~(1 << v)\n"," \n"," def _checkState(self, i, j, v):\n"," row_bit = (1 << v) & self.row_state[i] != 0\n"," col_bit = (1 << v) & self.col_state[j] != 0\n"," grid_index = (i//3)*3 + (j//3)\n"," grid_bit = (1 << v) & self.grid_state[grid_index] != 0\n"," return not row_bit and not col_bit and not grid_bit\n"," \n"," def reset(self):\n"," # initialize state\n"," self.row_state = [0]*self.n\n"," self.col_state = [0]*self.n\n"," self.grid_state = [0]*self.n\n"," self.board = deepcopy(self.original_board)\n"," \n"," def _getEmptySpots(self): \n"," ''' get empty spots and find its corresponding values in O(n*n)'''\n"," empty_spots = {}\n"," # initialize the state, and get empty spots\n"," for i in range(self.n):\n"," for j in range(self.n):\n"," if self.board[i][j]:\n"," # set that bit to 1\n"," self._setState(i, j, self.board[i][j]-1) \n"," else:\n"," empty_spots[(i,j)] = []\n"," \n"," # get possible values for each spot\n"," for i, j in empty_spots.keys():\n"," for v in range(self.n):\n"," if self._checkState(i, j, v):\n"," empty_spots[(i, j)].append(v+1)\n"," \n"," return empty_spots\n"," \n"," def helper(self, empty_spots, index):\n"," if index == len(empty_spots):\n"," return True\n"," (i, j), vl = empty_spots[index]\n"," \n"," for v in vl: #try each value\n"," # check the state\n"," if self._checkState(i, j, v-1):\n"," # set the state\n"," self._setState(i, j, v-1)\n"," # mark the board\n"," self.board[i][j] = v\n"," if self.helper(empty_spots, index+1):\n"," return True\n"," else:\n"," #backtack to previouse state\n"," self._resetState(i, j, v-1)\n"," #unmark the board\n"," self.board[i][j] = None\n"," return False\n"," \n"," def backtrackSolver(self):\n"," self.reset()\n"," empty_spots = self._getEmptySpots()\n"," empty_spots = [(k, v) for k, v in empty_spots.items() ]\n"," t0 = time.time()\n"," ans = self.helper(empty_spots, 0)\n"," print('total time: ', time.time() - t0)\n"," return ans\n"," \n"," def backtrackSolverSorted(self):\n"," self.reset()\n"," empty_spots = self._getEmptySpots()\n"," empty_spots = sorted(empty_spots.items(), key=lambda x: len(x[1]))\n"," t0 = time.time()\n"," ans = self.helper(empty_spots, 0)\n"," print('sorted total time: ', time.time() - t0)\n"," return ans\n","\n"," \n"," \n"," \n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"zIti82qiQ7Je","colab_type":"code","outputId":"c8220407-50a1-426d-a2df-cc6d005b497d","executionInfo":{"status":"ok","timestamp":1553038549788,"user_tz":420,"elapsed":786,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":72}},"cell_type":"code","source":["board = [[5, 3, None, None, 7, None, None, None, None],\n"," [6, None, None, 1, 9, 5, None, None, None],\n"," [None, 9, 8, None, None, None, None, 6, None],\n"," [8, None, None, None, 6, None, None, None, 3], \n"," [4, None, None, 8, None, 3, None, None, 1], \n"," [7, None, None, None, 2, None, None, None, 6], \n"," [None, 6, None, None, None, None, 2, 8, None], \n"," [None, None, None, 4, 1, 9, None, None, 5],\n"," [None, None, None, None, 8, None, None, 7, 9]]\n","solver = SudokoSolver(board)\n","solver.backtrackSolver()\n","solver.backtrackSolverSorted()"],"execution_count":0,"outputs":[{"output_type":"stream","text":["total time: 0.027954578399658203\n","sorted total time: 0.0004558563232421875\n"],"name":"stdout"},{"output_type":"execute_result","data":{"text/plain":["True"]},"metadata":{"tags":[]},"execution_count":239}]}]} \ No newline at end of file +{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"graph_search.ipynb","version":"0.3.2","provenance":[],"collapsed_sections":[],"toc_visible":true},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"metadata":{"id":"ZhL7pFlp8gTD","colab_type":"text"},"cell_type":"markdown","source":["## Backtracking\n","\n","1. Permutation\n","2. Combination\n","3. All Paths\n","\n","\n","\n"]},{"metadata":{"id":"a76_VshDTaE4","colab_type":"text"},"cell_type":"markdown","source":["### Permutation"]},{"metadata":{"id":"XhTLVA0sZw61","colab_type":"code","colab":{}},"cell_type":"code","source":["def A_n_k(a, n, k, depth, used, curr, ans):\n"," '''\n"," Implement permutation of k items out of n items\n"," depth: start from 0, and represent the depth of the search\n"," used: track what items are in the partial solution from the set of n\n"," curr: the current partial solution\n"," ans: collect all the valid solutions\n"," '''\n"," if depth == k: #end condition\n"," ans.append(curr[::]) # use deepcopy because curr is tracking all partial solution, it eventually become []\n"," return\n"," \n"," for i in range(n):\n"," if not used[i]:\n"," # generate the next solution from curr\n"," curr.append(a[i])\n"," used[i] = True\n"," print(curr)\n"," # move to the next solution\n"," A_n_k(a, n, k, depth+1, used, curr, ans)\n"," \n"," #backtrack to previous partial state\n"," curr.pop()\n"," print('backtrack: ', curr)\n"," used[i] = False\n"," return"],"execution_count":0,"outputs":[]},{"metadata":{"id":"RVZsfQb9_Xga","colab_type":"code","outputId":"bb9e8b92-0d8e-4cb7-8dd9-06f2c3384151","executionInfo":{"status":"ok","timestamp":1553585906154,"user_tz":420,"elapsed":440,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":555}},"cell_type":"code","source":["a = [1, 2, 3]\n","n = len(a)\n","ans = [[None]]\n","used = [False] * len(a)\n","ans = []\n","A_n_k(a, n, n, 0, used, [], ans)\n","print(ans)\n"],"execution_count":4,"outputs":[{"output_type":"stream","text":["[1]\n","[1, 2]\n","[1, 2, 3]\n","backtrack: [1, 2]\n","backtrack: [1]\n","[1, 3]\n","[1, 3, 2]\n","backtrack: [1, 3]\n","backtrack: [1]\n","backtrack: []\n","[2]\n","[2, 1]\n","[2, 1, 3]\n","backtrack: [2, 1]\n","backtrack: [2]\n","[2, 3]\n","[2, 3, 1]\n","backtrack: [2, 3]\n","backtrack: [2]\n","backtrack: []\n","[3]\n","[3, 1]\n","[3, 1, 2]\n","backtrack: [3, 1]\n","backtrack: [3]\n","[3, 2]\n","[3, 2, 1]\n","backtrack: [3, 2]\n","backtrack: [3]\n","backtrack: []\n","[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]\n"],"name":"stdout"}]},{"metadata":{"id":"HikETMOQcM7H","colab_type":"text"},"cell_type":"markdown","source":["### Combination"]},{"metadata":{"id":"fcIaL7i7-9na","colab_type":"code","colab":{}},"cell_type":"code","source":["def C_n_k(a, n, k, start, depth, curr, ans):\n"," '''\n"," Implement combination of k items out of n items\n"," start: the start of candinate\n"," depth: start from 0, and represent the depth of the search\n"," curr: the current partial solution\n"," ans: collect all the valid solutions\n"," '''\n"," if depth == k: #end condition\n"," ans.append(curr[::]) \n"," return\n"," \n"," for i in range(start, n): \n"," # generate the next solution from curr\n"," curr.append(a[i])\n"," # move to the next solution\n"," C_n_k(a, n, k, i+1, depth+1, curr, ans)\n","\n"," #backtrack to previous partial state\n"," curr.pop()\n"," return"],"execution_count":0,"outputs":[]},{"metadata":{"id":"FQlAWNcp8-5y","colab_type":"code","outputId":"0dcbc55c-b228-4d99-f37f-5d44111f3183","executionInfo":{"status":"ok","timestamp":1553038549590,"user_tz":420,"elapsed":793,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["a = [1, 2, 3]\n","n = len(a)\n","ans = [[None]]\n","ans = []\n","C_n_k(a, n, 2, 0, 0, [], ans)\n","print(ans)\n"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[[1, 2], [1, 3], [2, 3]]\n"],"name":"stdout"}]},{"metadata":{"id":"ze7L-ttOgCUd","colab_type":"text"},"cell_type":"markdown","source":["### All paths"]},{"metadata":{"id":"jYkhLKk9QZqi","colab_type":"code","colab":{}},"cell_type":"code","source":["def all_paths(g, s, path, ans):\n"," '''generate all paths with backtrack'''\n"," ans.append(path[::])\n"," for v in g[s]:\n"," path.append(v)\n"," print(path)\n"," all_paths(g, v, path, ans)\n"," path.pop()\n"," print(path, 'backtrack')"],"execution_count":0,"outputs":[]},{"metadata":{"id":"84vSh1JIQyLH","colab_type":"code","outputId":"809fc79d-57cb-4f5a-f444-7f8b41476e79","executionInfo":{"status":"ok","timestamp":1553038549595,"user_tz":420,"elapsed":757,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["al = [[1], [2], [4], [], [3, 5], [6], []]\n","print(al)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[[1], [2], [4], [], [3, 5], [6], []]\n"],"name":"stdout"}]},{"metadata":{"id":"HhBVClwEVeUJ","colab_type":"code","outputId":"1e88bace-af15-4632-b2c4-0cb9cedbe323","executionInfo":{"status":"ok","timestamp":1553038549597,"user_tz":420,"elapsed":740,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":256}},"cell_type":"code","source":["ans = []\n","path = [0]\n","all_paths(al, 0, path, ans)\n","print(ans)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[0, 1]\n","[0, 1, 2]\n","[0, 1, 2, 4]\n","[0, 1, 2, 4, 3]\n","[0, 1, 2, 4] backtrack\n","[0, 1, 2, 4, 5]\n","[0, 1, 2, 4, 5, 6]\n","[0, 1, 2, 4, 5] backtrack\n","[0, 1, 2, 4] backtrack\n","[0, 1, 2] backtrack\n","[0, 1] backtrack\n","[0] backtrack\n","[[0], [0, 1], [0, 1, 2], [0, 1, 2, 4], [0, 1, 2, 4, 3], [0, 1, 2, 4, 5], [0, 1, 2, 4, 5, 6]]\n"],"name":"stdout"}]},{"metadata":{"id":"7kLQciZ1Zt2i","colab_type":"text"},"cell_type":"markdown","source":["## Constraint Satisfaction Problems with Backtracking and Pruning"]},{"metadata":{"id":"oHsib-ORB9Fh","colab_type":"text"},"cell_type":"markdown","source":["First, we build up the board"]},{"metadata":{"id":"wU0IzGC8_EiP","colab_type":"code","colab":{}},"cell_type":"code","source":["board = [[5, 3, None, None, 7, None, None, None, None],\n"," [6, None, None, 1, 9, 5, None, None, None],\n"," [None, 9, 8, None, None, None, None, 6, None],\n"," [8, None, None, None, 6, None, None, None, 3], \n"," [4, None, None, 8, None, 3, None, None, 1], \n"," [7, None, None, None, 2, None, None, None, 6], \n"," [None, 6, None, None, None, None, 2, 8, None], \n"," [None, None, None, 4, 1, 9, None, None, 5],\n"," [None, None, None, None, 8, None, None, 7, 9]]"],"execution_count":0,"outputs":[]},{"metadata":{"id":"Psq2cedrMTGJ","colab_type":"text"},"cell_type":"markdown","source":["Define how to change the state"]},{"metadata":{"id":"t2oeCmmvCotc","colab_type":"code","colab":{}},"cell_type":"code","source":["def setState(i, j, v, row_state, col_state, grid_state):\n"," row_state[i] |= 1 << v\n"," col_state[j] |= 1 << v\n"," grid_index = (i//3)*3 + (j//3)\n"," grid_state[grid_index] |= 1 << v\n"," \n","def resetState(i, j, v, row_state, col_state, grid_state):\n"," row_state[i] &= ~(1 << v)\n"," col_state[j] &= ~(1 << v)\n"," grid_index = (i//3)*3 + (j//3)\n"," grid_state[grid_index] &= ~(1 << v)\n"," \n","def checkState(i, j, v, row_state, col_state, grid_state):\n"," row_bit = (1 << v) & row_state[i] != 0\n"," col_bit = (1 << v) & col_state[j] != 0\n"," grid_index = (i//3)*3 + (j//3)\n"," grid_bit = (1 << v) & grid_state[grid_index] != 0\n"," return not row_bit and not col_bit and not grid_bit"],"execution_count":0,"outputs":[]},{"metadata":{"id":"SgghNi99MWXw","colab_type":"text"},"cell_type":"markdown","source":["Get the empty spots and its values"]},{"metadata":{"id":"v0IZMZHU5FR-","colab_type":"code","colab":{}},"cell_type":"code","source":[" def getEmptySpots(board, rows, cols, row_state, col_state, grid_state): \n"," ''' get empty spots and find the corresponding values in O(n*n)'''\n"," empty_spots = {}\n"," # initialize the state, and get empty spots\n"," for i in range(rows):\n"," for j in range(cols):\n"," if board[i][j]:\n"," # set that bit to 1\n"," setState(i, j, board[i][j]-1, row_state, col_state, grid_state) \n"," else:\n"," empty_spots[(i,j)] = []\n"," \n"," # get possible values for each spot\n"," for i, j in empty_spots.keys():\n"," for v in range(9):\n"," if checkState(i, j, v, row_state, col_state, grid_state):\n"," empty_spots[(i, j)].append(v+1)\n"," \n"," return empty_spots"],"execution_count":0,"outputs":[]},{"metadata":{"id":"39DSr_mfCBrQ","colab_type":"text"},"cell_type":"markdown","source":["Second, we intialize the state and find empty spots. "]},{"metadata":{"id":"G76l_z6DAk4n","colab_type":"code","outputId":"1664fb47-2e89-46f7-c15c-47bff27bc873","executionInfo":{"status":"ok","timestamp":1553038549608,"user_tz":420,"elapsed":682,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":74}},"cell_type":"code","source":["# initialize state\n","row_state = [0]*9\n","col_state = [0]*9\n","grid_state = [0]*9\n","\n","empty_spots = getEmptySpots(board, 9, 9, row_state, col_state, grid_state)\n","print(row_state, col_state, grid_state) \n","sorted_empty_spots = sorted(empty_spots.items(), key=lambda x: len(x[1]))\n","print(sorted_empty_spots)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[84, 305, 416, 164, 141, 98, 162, 281, 448] [248, 292, 128, 137, 483, 276, 2, 224, 309] [436, 337, 32, 200, 166, 37, 32, 393, 466]\n","[((4, 4), [5]), ((6, 5), [7]), ((6, 8), [4]), ((7, 7), [3]), ((0, 3), [2, 6]), ((2, 0), [1, 2]), ((2, 3), [2, 3]), ((2, 4), [3, 4]), ((2, 5), [2, 4]), ((4, 1), [2, 5]), ((5, 1), [1, 5]), ((5, 3), [5, 9]), ((5, 5), [1, 4]), ((6, 4), [3, 5]), ((7, 0), [2, 3]), ((7, 6), [3, 6]), ((8, 5), [2, 6]), ((0, 2), [1, 2, 4]), ((0, 8), [2, 4, 8]), ((1, 1), [2, 4, 7]), ((1, 2), [2, 4, 7]), ((1, 7), [2, 3, 4]), ((2, 8), [2, 4, 7]), ((3, 1), [1, 2, 5]), ((3, 3), [5, 7, 9]), ((3, 5), [1, 4, 7]), ((4, 6), [5, 7, 9]), ((4, 7), [2, 5, 9]), ((5, 7), [4, 5, 9]), ((6, 0), [1, 3, 9]), ((6, 3), [3, 5, 7]), ((7, 1), [2, 7, 8]), ((7, 2), [2, 3, 7]), ((8, 0), [1, 2, 3]), ((0, 5), [2, 4, 6, 8]), ((0, 6), [1, 4, 8, 9]), ((0, 7), [1, 2, 4, 9]), ((1, 6), [3, 4, 7, 8]), ((1, 8), [2, 4, 7, 8]), ((3, 2), [1, 2, 5, 9]), ((3, 6), [4, 5, 7, 9]), ((3, 7), [2, 4, 5, 9]), ((4, 2), [2, 5, 6, 9]), ((5, 2), [1, 3, 5, 9]), ((5, 6), [4, 5, 8, 9]), ((8, 1), [1, 2, 4, 5]), ((8, 3), [2, 3, 5, 6]), ((8, 6), [1, 3, 4, 6]), ((2, 6), [1, 3, 4, 5, 7]), ((8, 2), [1, 2, 3, 4, 5]), ((6, 2), [1, 3, 4, 5, 7, 9])]\n"],"name":"stdout"}]},{"metadata":{"id":"jUPMX4-jF7N_","colab_type":"text"},"cell_type":"markdown","source":["Traverse the empty_spots, and fill in. "]},{"metadata":{"id":"ved6mk_0F6F-","colab_type":"code","colab":{}},"cell_type":"code","source":["def dfs_backtrack(empty_spots, index):\n"," if index == len(empty_spots):\n"," return True\n"," (i, j), vl = empty_spots[index]\n"," \n"," for v in vl: #try each value\n"," # check the state\n"," if checkState(i, j, v-1, row_state, col_state, grid_state):\n"," # set the state\n"," setState(i, j, v-1, row_state, col_state, grid_state)\n"," # mark the board\n"," board[i][j] = v\n"," if dfs_backtrack(empty_spots, index+1):\n"," return True\n"," else:\n"," #backtack to previouse state\n"," resetState(i, j, v-1, row_state, col_state, grid_state)\n"," #unmark the board\n"," board[i][j] = None\n"," return False\n"," \n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"QUr5dZQxIpdn","colab_type":"code","outputId":"0d08b439-e745-478d-98e8-84498f033412","executionInfo":{"status":"ok","timestamp":1553038549612,"user_tz":420,"elapsed":656,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":74}},"cell_type":"code","source":["ans = dfs_backtrack(sorted_empty_spots, 0)\n","print(ans)\n","print(board)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["True\n","[[5, 3, 4, 6, 7, 8, 9, 1, 2], [6, 7, 2, 1, 9, 5, 3, 4, 8], [1, 9, 8, 3, 4, 2, 5, 6, 7], [8, 5, 9, 7, 6, 1, 4, 2, 3], [4, 2, 6, 8, 5, 3, 7, 9, 1], [7, 1, 3, 9, 2, 4, 8, 5, 6], [9, 6, 1, 5, 3, 7, 2, 8, 4], [2, 8, 7, 4, 1, 9, 6, 3, 5], [3, 4, 5, 2, 8, 6, 1, 7, 9]]\n"],"name":"stdout"}]},{"metadata":{"id":"4vOhZxglCBKD","colab_type":"text"},"cell_type":"markdown","source":["#### Sudoku Solver"]},{"metadata":{"id":"j7d_45x3MiY9","colab_type":"code","colab":{}},"cell_type":"code","source":["from copy import deepcopy\n","import time\n","class SudokoSolver():\n"," def __init__(self, board):\n"," self.original_board = deepcopy(board)\n"," self.board = deepcopy(board)\n"," self.n = len(board)\n"," assert (self.n == len(board[0]))\n"," # initialize state\n"," self.row_state = [0]*self.n\n"," self.col_state = [0]*self.n\n"," self.grid_state = [0]*self.n\n"," \n"," def _setState(self, i, j, v):\n"," self.row_state[i] |= 1 << v\n"," self.col_state[j] |= 1 << v\n"," grid_index = (i//3)*3 + (j//3)\n"," self.grid_state[grid_index] |= 1 << v\n"," \n"," def _resetState(self, i, j, v):\n"," self.row_state[i] &= ~(1 << v)\n"," self.col_state[j] &= ~(1 << v)\n"," grid_index = (i//3)*3 + (j//3)\n"," self.grid_state[grid_index] &= ~(1 << v)\n"," \n"," def _checkState(self, i, j, v):\n"," row_bit = (1 << v) & self.row_state[i] != 0\n"," col_bit = (1 << v) & self.col_state[j] != 0\n"," grid_index = (i//3)*3 + (j//3)\n"," grid_bit = (1 << v) & self.grid_state[grid_index] != 0\n"," return not row_bit and not col_bit and not grid_bit\n"," \n"," def reset(self):\n"," # initialize state\n"," self.row_state = [0]*self.n\n"," self.col_state = [0]*self.n\n"," self.grid_state = [0]*self.n\n"," self.board = deepcopy(self.original_board)\n"," \n"," def _getEmptySpots(self): \n"," ''' get empty spots and find the corresponding values in O(n*n)'''\n"," empty_spots = {}\n"," # initialize the state, and get empty spots\n"," for i in range(self.n):\n"," for j in range(self.n):\n"," if self.board[i][j]:\n"," # set that bit to 1\n"," self._setState(i, j, self.board[i][j]-1) \n"," else:\n"," empty_spots[(i,j)] = []\n"," \n"," # get possible values for each spot\n"," for i, j in empty_spots.keys():\n"," for v in range(self.n):\n"," if self._checkState(i, j, v):\n"," empty_spots[(i, j)].append(v+1)\n"," \n"," return empty_spots\n"," \n"," def helper(self, empty_spots, index):\n"," if index == len(empty_spots):\n"," return True\n"," (i, j), vl = empty_spots[index]\n"," \n"," for v in vl: #try each value\n"," # check the state\n"," if self._checkState(i, j, v-1):\n"," # set the state\n"," self._setState(i, j, v-1)\n"," # mark the board\n"," self.board[i][j] = v\n"," if self.helper(empty_spots, index+1):\n"," return True\n"," else:\n"," #backtack to previouse state\n"," self._resetState(i, j, v-1)\n"," #unmark the board\n"," self.board[i][j] = None\n"," return False\n"," \n"," def backtrackSolver(self):\n"," self.reset()\n"," empty_spots = self._getEmptySpots()\n"," empty_spots = [(k, v) for k, v in empty_spots.items() ]\n"," t0 = time.time()\n"," ans = self.helper(empty_spots, 0)\n"," print('total time: ', time.time() - t0)\n"," return ans\n"," \n"," def backtrackSolverSorted(self):\n"," self.reset()\n"," empty_spots = self._getEmptySpots()\n"," empty_spots = sorted(empty_spots.items(), key=lambda x: len(x[1]))\n"," t0 = time.time()\n"," ans = self.helper(empty_spots, 0)\n"," print('sorted total time: ', time.time() - t0)\n"," return ans\n","\n"," \n"," \n"," \n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"zIti82qiQ7Je","colab_type":"code","outputId":"c8220407-50a1-426d-a2df-cc6d005b497d","executionInfo":{"status":"ok","timestamp":1553038549788,"user_tz":420,"elapsed":786,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":72}},"cell_type":"code","source":["board = [[5, 3, None, None, 7, None, None, None, None],\n"," [6, None, None, 1, 9, 5, None, None, None],\n"," [None, 9, 8, None, None, None, None, 6, None],\n"," [8, None, None, None, 6, None, None, None, 3], \n"," [4, None, None, 8, None, 3, None, None, 1], \n"," [7, None, None, None, 2, None, None, None, 6], \n"," [None, 6, None, None, None, None, 2, 8, None], \n"," [None, None, None, 4, 1, 9, None, None, 5],\n"," [None, None, None, None, 8, None, None, 7, 9]]\n","solver = SudokoSolver(board)\n","solver.backtrackSolver()\n","solver.backtrackSolverSorted()"],"execution_count":0,"outputs":[{"output_type":"stream","text":["total time: 0.027954578399658203\n","sorted total time: 0.0004558563232421875\n"],"name":"stdout"},{"output_type":"execute_result","data":{"text/plain":["True"]},"metadata":{"tags":[]},"execution_count":239}]}]} diff --git a/Colab Codes/Colab Notebooks/graph_search_application.ipynb b/Colab Codes/Colab Notebooks/graph_search_application.ipynb index 27edfcc..348832b 100644 --- a/Colab Codes/Colab Notebooks/graph_search_application.ipynb +++ b/Colab Codes/Colab Notebooks/graph_search_application.ipynb @@ -1 +1 @@ -{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"graph_search_application.ipynb","version":"0.3.2","provenance":[],"toc_visible":true},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"metadata":{"id":"auwghE4hWHSK","colab_type":"text"},"cell_type":"markdown","source":["### Cycle Check"]},{"metadata":{"id":"6nYEnOCo3FOC","colab_type":"code","colab":{}},"cell_type":"code","source":["# initialization\n","class STATE:\n"," white = 0\n"," gray = 1\n"," black = 2"],"execution_count":0,"outputs":[]},{"metadata":{"id":"dLTp6RWw2mYX","colab_type":"text"},"cell_type":"markdown","source":["### For directed graph"]},{"metadata":{"id":"xPzr88zz2pw_","colab_type":"code","outputId":"3bd3c586-7da2-420e-c60c-d24569711308","executionInfo":{"status":"ok","timestamp":1552244194011,"user_tz":420,"elapsed":539,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["al = [[] for _ in range(7)]\n","\n","# set 8 edges\n","al[0] = [1]\n","al[1] = [2]\n","al[2] = [0, 4]\n","al[4] = [3]\n","al[5] = [6]\n","\n","print(al)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[[1], [2], [0, 4], [], [3], [6], []]\n"],"name":"stdout"}]},{"metadata":{"id":"iq07bZAp29mi","colab_type":"code","colab":{}},"cell_type":"code","source":["def hasCycle(g, s, state):\n"," '''convert dfs to check cycle'''\n"," state[s] = STATE.gray # first be visited\n"," for v in g[s]:\n"," if state[v] == STATE.white:\n"," if hasCycle(g, v, state):\n"," return True\n"," elif state[v] == STATE.gray: # aback edge\n"," return True\n"," else:\n"," pass\n"," state[s] = STATE.black # mark it as complete\n","\n"," return False\n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"zbtKB8Rg3bWg","colab_type":"code","colab":{}},"cell_type":"code","source":["def cycleDetect(g):\n"," '''cycle detect in directed graph'''\n"," n = len(g)\n"," state = [STATE.white] * n\n"," for i in range(n):\n"," if state[i] == STATE.white:\n"," if hasCycle(g, i, state):\n"," print('cycle starts at vertex ', i)\n"," return True\n"," return False"],"execution_count":0,"outputs":[]},{"metadata":{"id":"q-0e10OJ4M5i","colab_type":"code","outputId":"54d8c30c-f873-4ed4-84c4-ff1e3e2fdc7a","executionInfo":{"status":"ok","timestamp":1552244246348,"user_tz":420,"elapsed":338,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":54}},"cell_type":"code","source":["cycleDetect(al)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["cycle starts at vertex 5\n"],"name":"stdout"},{"output_type":"execute_result","data":{"text/plain":["True"]},"metadata":{"tags":[]},"execution_count":6}]},{"metadata":{"id":"GTx9ANEY562r","colab_type":"text"},"cell_type":"markdown","source":["#### For undirected Graph"]},{"metadata":{"id":"wE01rAq55-oF","colab_type":"code","colab":{}},"cell_type":"code","source":["def hasCycle(g, s, p, state):\n"," '''convert dfs to check cycle'''\n"," state[s] = STATE.gray # first be visited\n"," for v in g[s]:\n"," if state[v] == STATE.white:\n"," if hasCycle(g, v, s, state):\n"," return True\n"," elif state[v] == STATE.gray and v != p: # aback edge\n"," return True\n"," else:\n"," pass\n"," state[s] = STATE.black # mark it as complete\n","\n"," return False"],"execution_count":0,"outputs":[]},{"metadata":{"id":"sA6dMqrE7MOC","colab_type":"code","colab":{}},"cell_type":"code","source":["def cycleDetect(g):\n"," '''cycle detect in directed graph'''\n"," n = len(g)\n"," state = [STATE.white] * n\n"," for i in range(n):\n"," if state[i] == STATE.white:\n"," if hasCycle(g, i, -1, state):\n"," print('cycle starts at vertex ', i)\n"," return True\n"," return False"],"execution_count":0,"outputs":[]},{"metadata":{"id":"aD-FizTB6WbB","colab_type":"code","outputId":"6bc3e3f4-bcfa-443d-e037-49ca81fca9d7","executionInfo":{"status":"ok","timestamp":1552244252680,"user_tz":420,"elapsed":592,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["al = [[] for _ in range(7)]\n","\n","# set 8 edges\n","al[0] = [1, 2]\n","al[1] = [0, 2]\n","al[2] = [0, 4]\n","al[4] = [2, 3]\n","al[5] = [6]\n","al[6] = [5]\n","\n","print(al)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[[1, 2], [0, 2], [0, 4], [], [2, 3], [6], [5]]\n"],"name":"stdout"}]},{"metadata":{"id":"30NtiTxy7TIq","colab_type":"code","outputId":"d1c19bb1-7043-469c-a8f3-a40a800abaa3","executionInfo":{"status":"ok","timestamp":1552244255134,"user_tz":420,"elapsed":563,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":54}},"cell_type":"code","source":["cycleDetect(al)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["cycle starts at vertex 0\n"],"name":"stdout"},{"output_type":"execute_result","data":{"text/plain":["True"]},"metadata":{"tags":[]},"execution_count":10}]},{"metadata":{"id":"WkpyVhW8WMN1","colab_type":"text"},"cell_type":"markdown","source":["### Topolgical Sort"]},{"metadata":{"id":"G1MfbU4eWPrA","colab_type":"code","outputId":"f615d164-63d1-4533-95db-4413433655d4","executionInfo":{"status":"ok","timestamp":1552244256915,"user_tz":420,"elapsed":510,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["al = [[] for _ in range(7)]\n","\n","# set 8 edges\n","al[0] = [1]\n","al[1] = [2]\n","al[2] = [4]\n","al[3] = []\n","al[4] = [3, 5]\n","al[5] = [6]\n","al[6] = []\n","\n","print(al)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[[1], [2], [4], [], [3, 5], [6], []]\n"],"name":"stdout"}]},{"metadata":{"id":"Pf7Jaj8CXWSM","colab_type":"code","colab":{}},"cell_type":"code","source":["def dfs(g, s, colors, orders, complete_orders):\n"," colors[s] = STATE.gray\n"," orders.append(s)\n"," for v in g[s]:\n"," if colors[v] == STATE.white:\n"," dfs(g, v, colors, orders, complete_orders)\n"," # complete\n"," colors[s] = STATE.black # this is not necessary in the code, just to help track the state\n"," complete_orders.append(s)\n"," return"],"execution_count":0,"outputs":[]},{"metadata":{"id":"FvKHA5plXaoW","colab_type":"code","colab":{}},"cell_type":"code","source":[""],"execution_count":0,"outputs":[]},{"metadata":{"id":"hbTw8Pf3YzjJ","colab_type":"code","colab":{}},"cell_type":"code","source":["def topo_sort(g):\n"," n = len(g)\n"," orders, complete_orders = [], []\n"," colors = [STATE.white] * n\n"," for i in range(n): # run dfs on all the node\n"," if colors[i] == STATE.white:\n"," dfs(g,i, colors, orders, complete_orders)\n","\n"," #print(orders, complete_orders[::-1])\n"," return orders, complete_orders[::-1]"],"execution_count":0,"outputs":[]},{"metadata":{"id":"WeoxmZkcZUKA","colab_type":"code","outputId":"961ef42a-ee79-4d5c-d8a5-bcc71ecf495c","executionInfo":{"status":"ok","timestamp":1552244278240,"user_tz":420,"elapsed":360,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["orders, complete_orders = topo_sort(al)\n","print(orders, complete_orders)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[0, 1, 2, 4, 3, 5, 6] [0, 1, 2, 4, 5, 6, 3]\n"],"name":"stdout"}]},{"metadata":{"id":"nHK_jknJXrhq","colab_type":"text"},"cell_type":"markdown","source":["Now, change the edge (2->4) to (4->2) and run the code again."]},{"metadata":{"id":"TX9opC9CXqbZ","colab_type":"code","outputId":"caa8397f-c251-4f8e-c469-7399412eec16","executionInfo":{"status":"ok","timestamp":1552244281902,"user_tz":420,"elapsed":392,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["al[2].remove(4)\n","al[4].append(2)\n","print(al)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[[1], [2], [], [], [3, 5, 2], [6], []]\n"],"name":"stdout"}]},{"metadata":{"id":"AXgDs24MYJHA","colab_type":"code","outputId":"881cb099-d2d1-4f6e-a8d7-e0c08c5d9108","executionInfo":{"status":"ok","timestamp":1552244283447,"user_tz":420,"elapsed":405,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["orders, complete_orders = topo_sort(al)\n","print(orders, complete_orders)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[0, 1, 2, 3, 4, 5, 6] [4, 5, 6, 3, 0, 1, 2]\n"],"name":"stdout"}]},{"metadata":{"id":"GTwh1v4l7G2B","colab_type":"text"},"cell_type":"markdown","source":["### Connected Components\n","\n","In the example, we only experiment with BFS. "]},{"metadata":{"id":"kIPfcNX9ARU7","colab_type":"code","colab":{}},"cell_type":"code","source":["def bfs(g, s, state):\n"," state[s] = True\n"," \n"," q, orders = [s], [s]\n"," while q:\n"," u = q.pop(0)\n"," \n"," for v in g[u]:\n"," if not state[v]:\n"," state[v] = True\n"," q.append(v)\n"," orders.append(v)\n"," return orders"],"execution_count":0,"outputs":[]},{"metadata":{"id":"eH7-zUmhAoDp","colab_type":"code","colab":{}},"cell_type":"code","source":["def connectedComponent(g):\n"," n = len(g)\n"," ccs = []\n"," state = [False] * n\n"," for i in range(n):\n"," if not state[i]:\n"," ccs.append(bfs(g, i, state))\n"," return ccs \n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"6VWvKNSGBLeA","colab_type":"code","outputId":"8df73569-85f6-4b11-a59a-66ea47411c6d","executionInfo":{"status":"ok","timestamp":1552244289362,"user_tz":420,"elapsed":371,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":54}},"cell_type":"code","source":["al = [[] for _ in range(7)]\n","\n","# set 8 edges\n","al[0] = [1, 2]\n","al[1] = [0, 2]\n","al[2] = [0, 4]\n","al[4] = [2, 3]\n","al[5] = [6]\n","al[6] = [5]\n","\n","print(al)\n","print(connectedComponent(al))"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[[1, 2], [0, 2], [0, 4], [], [2, 3], [6], [5]]\n","[[0, 1, 2, 4, 3], [5, 6]]\n"],"name":"stdout"}]},{"metadata":{"id":"arzllBW_GsIX","colab_type":"text"},"cell_type":"markdown","source":["####Strongly connected components"]},{"metadata":{"id":"WkIxpFk7GvkW","colab_type":"code","outputId":"589d3781-35f5-45e4-cbe1-65446ddb50ba","executionInfo":{"status":"ok","timestamp":1552244293701,"user_tz":420,"elapsed":360,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":54}},"cell_type":"code","source":["'''in the second undirected graph'''\n","al2 = [[] for _ in range(7)]\n","\n","# set 8 edges\n","al2[0] = [1]\n","al2[1] = [2]\n","al2[2] = [0, 4]\n","al2[4] = [3]\n","al2[5] = [6]\n","\n","print(al)\n","\n","'''in the first undirected graph'''\n","al1 = al2[::]\n","\n","al1[3].append(1)\n","\n","print(al1)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[[1, 2], [0, 2], [0, 4], [], [2, 3], [6], [5]]\n","[[1], [2], [0, 4], [1], [3], [6], []]\n"],"name":"stdout"}]},{"metadata":{"id":"0c7CQ8aUIw1c","colab_type":"code","colab":{}},"cell_type":"code","source":["def topo_sort(g):\n"," v = len(al)\n"," orders, complete_orders = [], []\n"," colors = [STATE.white] * v\n"," for i in range(v): # run dfs on all the node\n"," if colors[i] == STATE.white:\n"," dfs(al,i, colors, orders, complete_orders)\n"," return complete_orders[::-1]"],"execution_count":0,"outputs":[]},{"metadata":{"id":"H3ra08cvIz5B","colab_type":"code","outputId":"41e31576-c092-4f7a-919b-1cb22d1eda04","executionInfo":{"status":"ok","timestamp":1552244300038,"user_tz":420,"elapsed":326,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":54}},"cell_type":"code","source":["print(topo_sort(al2))\n","print(topo_sort(al1))"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[5, 6, 0, 1, 2, 4, 3]\n","[5, 6, 0, 1, 2, 4, 3]\n"],"name":"stdout"}]},{"metadata":{"id":"YVz-BG3YFJFX","colab_type":"text"},"cell_type":"markdown","source":["### Minimum Spanning Tree"]},{"metadata":{"id":"tI9aTeoy7Z2p","colab_type":"text"},"cell_type":"markdown","source":["#### Prim's Algorithm"]},{"metadata":{"id":"ZUW60VFQGwiD","colab_type":"code","colab":{}},"cell_type":"code","source":["a= {1:[(2, 2), (3, 12), (4, 10)], 2:[(1, 2), (3, 8), (5, 9)], 3:[(1, 12), (2, 8), (4, 6), (5, 3)], 4:[(1, 10),(3, 6), (5, 7)], 5:[(2, 9), (3, 3), (4, 7)]}\n","\n","class edge():\n"," def __init__(self, pid, id, w ):\n"," self.pid = pid\n"," self.id = id\n"," self.w = w\n"," def __lt__(self, other):\n"," return self.w < other.w\n"," \n"," def __eq__(self, other):\n"," return self.w == other.w\n"," \n"," def __str__(self):\n"," return str(self.pid) + '->' + str(self.id) + ':' + str(self.w)\n"," \n"],"execution_count":0,"outputs":[]},{"metadata":{"id":"AdLTUkgxAI1I","colab_type":"code","colab":{}},"cell_type":"code","source":["import queue\n","def prim(g, n):\n"," # step 1:\n"," start = 1\n"," V = {start} #spanning tree set\n"," E = queue.PriorityQueue() # the set of all edges, \n"," ans = []\n"," \n"," while len(V) < n:\n"," # add edges of start, and the other endpoint is in nv\n"," idlst = g[start]\n"," for id, w in idlst:\n"," if id not in V:\n"," E.put(edge(start, id, w))\n"," \n"," while E:\n"," # pick the smallest edge\n"," minEdge = E.get()\n","\n"," if minEdge.id not in V:\n"," # set the new id as start\n"," start = minEdge.id\n"," # add this id to the set of tree nodes\n"," V.add(minEdge.id)\n"," ans.append(minEdge)\n"," break\n"," return ans\n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"40vRdighTR8f","colab_type":"code","outputId":"d3c8197f-536b-4868-8f21-6dab330cdf69","executionInfo":{"status":"ok","timestamp":1553243555679,"user_tz":420,"elapsed":802,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":90}},"cell_type":"code","source":["ans = prim(a, 5)\n","for e in ans:\n"," print(e)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["1->2:2\n","2->3:8\n","3->5:3\n","3->4:6\n"],"name":"stdout"}]},{"metadata":{"id":"0r0DIbiU7d_v","colab_type":"code","colab":{}},"cell_type":"code","source":["class node:\n"," def __init__(self, p, w):\n"," self.p = p\n"," self.w = w\n"," def __lt__(self, other):\n"," return self.w < other.w\n"," def __eq__(self, other):\n"," return self.w == other.w\n"," def __str__(self):\n"," return str(self.p) + '->' +str(self.id)+':'+str(self.w)\n","\n"],"execution_count":0,"outputs":[]},{"metadata":{"id":"iE1wCIcbtj7t","colab_type":"code","colab":{}},"cell_type":"code","source":["def extractMin(q):\n"," minNode = None\n"," minW = float('inf')\n"," minIndex = -1\n"," for idx, node in enumerate(q):\n"," if node.w < minW:\n"," minNode = node\n"," minW = node.w\n"," minIdx = idx\n"," #q.remove(minNode)\n"," return minNode, minIdx\n","\n","def primMst(g, n):\n"," q = [None]*n\n"," S = {}\n"," ans = []\n"," for i in range(n):\n"," q[i] = node(None, float('inf'))\n"," q[0] = node(None, 0)\n"," S = {1}\n"," # main process\n"," while len(S) < n:\n"," minNode, minIdx = extractMin(q)\n"," S.add(minIdx+1)\n"," if minNode.p is not None:\n"," ans.append((minNode.p+1, minIdx+1))\n"," q[minIdx] = node(None, float('inf'))\n"," for v, w in g[minIdx+1]:\n"," if v not in S and w < q[v-1].w:\n"," q[v-1].p = minIdx\n"," q[v-1].w = w\n"," return ans\n"," \n"," \n"," \n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"JzWJqXcYRuyh","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":35},"outputId":"c71f7b89-d6f5-40d5-cc45-f8391f4511b9","executionInfo":{"status":"ok","timestamp":1553382526262,"user_tz":420,"elapsed":401,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}}},"cell_type":"code","source":["print(primMst(a, 5))"],"execution_count":42,"outputs":[{"output_type":"stream","text":["[(1, 2), (2, 3), (3, 5), (3, 4)]\n"],"name":"stdout"}]},{"metadata":{"id":"vwa3SlexCQAn","colab_type":"text"},"cell_type":"markdown","source":["#### Kruskal's Algorithm"]},{"metadata":{"id":"QC19-o-XCUF1","colab_type":"code","colab":{}},"cell_type":"code","source":["def kruskalMst(g, n):"],"execution_count":0,"outputs":[]}]} \ No newline at end of file +{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"graph_search_application.ipynb","version":"0.3.2","provenance":[],"toc_visible":true},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"metadata":{"id":"auwghE4hWHSK","colab_type":"text"},"cell_type":"markdown","source":["### Cycle Check"]},{"metadata":{"id":"6nYEnOCo3FOC","colab_type":"code","colab":{}},"cell_type":"code","source":["# initialization\n","class STATE:\n"," white = 0\n"," gray = 1\n"," black = 2"],"execution_count":0,"outputs":[]},{"metadata":{"id":"dLTp6RWw2mYX","colab_type":"text"},"cell_type":"markdown","source":["### For directed graph"]},{"metadata":{"id":"xPzr88zz2pw_","colab_type":"code","outputId":"3bd3c586-7da2-420e-c60c-d24569711308","executionInfo":{"status":"ok","timestamp":1552244194011,"user_tz":420,"elapsed":539,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["al = [[] for _ in range(7)]\n","\n","# set 8 edges\n","al[0] = [1]\n","al[1] = [2]\n","al[2] = [0, 4]\n","al[4] = [3]\n","al[5] = [6]\n","\n","print(al)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[[1], [2], [0, 4], [], [3], [6], []]\n"],"name":"stdout"}]},{"metadata":{"id":"iq07bZAp29mi","colab_type":"code","colab":{}},"cell_type":"code","source":["def hasCycle(g, s, state):\n"," '''convert dfs to check cycle'''\n"," state[s] = STATE.gray # first be visited\n"," for v in g[s]:\n"," if state[v] == STATE.white:\n"," if hasCycle(g, v, state):\n"," return True\n"," elif state[v] == STATE.gray: # aback edge\n"," return True\n"," else:\n"," pass\n"," state[s] = STATE.black # mark it as complete\n","\n"," return False\n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"zbtKB8Rg3bWg","colab_type":"code","colab":{}},"cell_type":"code","source":["def cycleDetect(g):\n"," '''cycle detect in directed graph'''\n"," n = len(g)\n"," state = [STATE.white] * n\n"," for i in range(n):\n"," if state[i] == STATE.white:\n"," if hasCycle(g, i, state):\n"," print('cycle starts at vertex ', i)\n"," return True\n"," return False"],"execution_count":0,"outputs":[]},{"metadata":{"id":"q-0e10OJ4M5i","colab_type":"code","outputId":"54d8c30c-f873-4ed4-84c4-ff1e3e2fdc7a","executionInfo":{"status":"ok","timestamp":1552244246348,"user_tz":420,"elapsed":338,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":54}},"cell_type":"code","source":["cycleDetect(al)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["cycle starts at vertex 5\n"],"name":"stdout"},{"output_type":"execute_result","data":{"text/plain":["True"]},"metadata":{"tags":[]},"execution_count":6}]},{"metadata":{"id":"GTx9ANEY562r","colab_type":"text"},"cell_type":"markdown","source":["#### For undirected Graph"]},{"metadata":{"id":"wE01rAq55-oF","colab_type":"code","colab":{}},"cell_type":"code","source":["def hasCycle(g, s, p, state):\n"," '''convert dfs to check cycle'''\n"," state[s] = STATE.gray # first be visited\n"," for v in g[s]:\n"," if state[v] == STATE.white:\n"," if hasCycle(g, v, s, state):\n"," return True\n"," elif state[v] == STATE.gray and v != p: # aback edge\n"," return True\n"," else:\n"," pass\n"," state[s] = STATE.black # mark it as complete\n","\n"," return False"],"execution_count":0,"outputs":[]},{"metadata":{"id":"sA6dMqrE7MOC","colab_type":"code","colab":{}},"cell_type":"code","source":["def cycleDetect(g):\n"," '''cycle detect in directed graph'''\n"," n = len(g)\n"," state = [STATE.white] * n\n"," for i in range(n):\n"," if state[i] == STATE.white:\n"," if hasCycle(g, i, -1, state):\n"," print('cycle starts at vertex ', i)\n"," return True\n"," return False"],"execution_count":0,"outputs":[]},{"metadata":{"id":"aD-FizTB6WbB","colab_type":"code","outputId":"6bc3e3f4-bcfa-443d-e037-49ca81fca9d7","executionInfo":{"status":"ok","timestamp":1552244252680,"user_tz":420,"elapsed":592,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["al = [[] for _ in range(7)]\n","\n","# set 8 edges\n","al[0] = [1, 2]\n","al[1] = [0, 2]\n","al[2] = [0, 4]\n","al[4] = [2, 3]\n","al[5] = [6]\n","al[6] = [5]\n","\n","print(al)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[[1, 2], [0, 2], [0, 4], [], [2, 3], [6], [5]]\n"],"name":"stdout"}]},{"metadata":{"id":"30NtiTxy7TIq","colab_type":"code","outputId":"d1c19bb1-7043-469c-a8f3-a40a800abaa3","executionInfo":{"status":"ok","timestamp":1552244255134,"user_tz":420,"elapsed":563,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":54}},"cell_type":"code","source":["cycleDetect(al)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["cycle starts at vertex 0\n"],"name":"stdout"},{"output_type":"execute_result","data":{"text/plain":["True"]},"metadata":{"tags":[]},"execution_count":10}]},{"metadata":{"id":"WkpyVhW8WMN1","colab_type":"text"},"cell_type":"markdown","source":["### Topolgical Sort"]},{"metadata":{"id":"G1MfbU4eWPrA","colab_type":"code","outputId":"f615d164-63d1-4533-95db-4413433655d4","executionInfo":{"status":"ok","timestamp":1552244256915,"user_tz":420,"elapsed":510,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["al = [[] for _ in range(7)]\n","\n","# set 8 edges\n","al[0] = [1]\n","al[1] = [2]\n","al[2] = [4]\n","al[3] = []\n","al[4] = [3, 5]\n","al[5] = [6]\n","al[6] = []\n","\n","print(al)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[[1], [2], [4], [], [3, 5], [6], []]\n"],"name":"stdout"}]},{"metadata":{"id":"Pf7Jaj8CXWSM","colab_type":"code","colab":{}},"cell_type":"code","source":["def dfs(g, s, colors, orders, complete_orders):\n"," colors[s] = STATE.gray\n"," orders.append(s)\n"," for v in g[s]:\n"," if colors[v] == STATE.white:\n"," dfs(g, v, colors, orders, complete_orders)\n"," # complete\n"," colors[s] = STATE.black # this is not necessary in the code, just to help track the state\n"," complete_orders.append(s)\n"," return"],"execution_count":0,"outputs":[]},{"metadata":{"id":"FvKHA5plXaoW","colab_type":"code","colab":{}},"cell_type":"code","source":[""],"execution_count":0,"outputs":[]},{"metadata":{"id":"hbTw8Pf3YzjJ","colab_type":"code","colab":{}},"cell_type":"code","source":["def topo_sort(g):\n"," n = len(g)\n"," orders, complete_orders = [], []\n"," colors = [STATE.white] * n\n"," for i in range(n): # run dfs on all the node\n"," if colors[i] == STATE.white:\n"," dfs(g,i, colors, orders, complete_orders)\n","\n"," #print(orders, complete_orders[::-1])\n"," return orders, complete_orders[::-1]"],"execution_count":0,"outputs":[]},{"metadata":{"id":"WeoxmZkcZUKA","colab_type":"code","outputId":"961ef42a-ee79-4d5c-d8a5-bcc71ecf495c","executionInfo":{"status":"ok","timestamp":1552244278240,"user_tz":420,"elapsed":360,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["orders, complete_orders = topo_sort(al)\n","print(orders, complete_orders)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[0, 1, 2, 4, 3, 5, 6] [0, 1, 2, 4, 5, 6, 3]\n"],"name":"stdout"}]},{"metadata":{"id":"nHK_jknJXrhq","colab_type":"text"},"cell_type":"markdown","source":["Now, change the edge (2→4) to (4→2) and run the code again."]},{"metadata":{"id":"TX9opC9CXqbZ","colab_type":"code","outputId":"caa8397f-c251-4f8e-c469-7399412eec16","executionInfo":{"status":"ok","timestamp":1552244281902,"user_tz":420,"elapsed":392,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["al[2].remove(4)\n","al[4].append(2)\n","print(al)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[[1], [2], [], [], [3, 5, 2], [6], []]\n"],"name":"stdout"}]},{"metadata":{"id":"AXgDs24MYJHA","colab_type":"code","outputId":"881cb099-d2d1-4f6e-a8d7-e0c08c5d9108","executionInfo":{"status":"ok","timestamp":1552244283447,"user_tz":420,"elapsed":405,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":35}},"cell_type":"code","source":["orders, complete_orders = topo_sort(al)\n","print(orders, complete_orders)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[0, 1, 2, 3, 4, 5, 6] [4, 5, 6, 3, 0, 1, 2]\n"],"name":"stdout"}]},{"metadata":{"id":"GTwh1v4l7G2B","colab_type":"text"},"cell_type":"markdown","source":["### Connected Components\n","\n","In the example, we only experiment with BFS. "]},{"metadata":{"id":"kIPfcNX9ARU7","colab_type":"code","colab":{}},"cell_type":"code","source":["def bfs(g, s, state):\n"," state[s] = True\n"," \n"," q, orders = [s], [s]\n"," while q:\n"," u = q.pop(0)\n"," \n"," for v in g[u]:\n"," if not state[v]:\n"," state[v] = True\n"," q.append(v)\n"," orders.append(v)\n"," return orders"],"execution_count":0,"outputs":[]},{"metadata":{"id":"eH7-zUmhAoDp","colab_type":"code","colab":{}},"cell_type":"code","source":["def connectedComponent(g):\n"," n = len(g)\n"," ccs = []\n"," state = [False] * n\n"," for i in range(n):\n"," if not state[i]:\n"," ccs.append(bfs(g, i, state))\n"," return ccs \n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"6VWvKNSGBLeA","colab_type":"code","outputId":"8df73569-85f6-4b11-a59a-66ea47411c6d","executionInfo":{"status":"ok","timestamp":1552244289362,"user_tz":420,"elapsed":371,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":54}},"cell_type":"code","source":["al = [[] for _ in range(7)]\n","\n","# set 8 edges\n","al[0] = [1, 2]\n","al[1] = [0, 2]\n","al[2] = [0, 4]\n","al[4] = [2, 3]\n","al[5] = [6]\n","al[6] = [5]\n","\n","print(al)\n","print(connectedComponent(al))"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[[1, 2], [0, 2], [0, 4], [], [2, 3], [6], [5]]\n","[[0, 1, 2, 4, 3], [5, 6]]\n"],"name":"stdout"}]},{"metadata":{"id":"arzllBW_GsIX","colab_type":"text"},"cell_type":"markdown","source":["####Strongly connected components"]},{"metadata":{"id":"WkIxpFk7GvkW","colab_type":"code","outputId":"589d3781-35f5-45e4-cbe1-65446ddb50ba","executionInfo":{"status":"ok","timestamp":1552244293701,"user_tz":420,"elapsed":360,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":54}},"cell_type":"code","source":["'''in the second undirected graph'''\n","al2 = [[] for _ in range(7)]\n","\n","# set 8 edges\n","al2[0] = [1]\n","al2[1] = [2]\n","al2[2] = [0, 4]\n","al2[4] = [3]\n","al2[5] = [6]\n","\n","print(al)\n","\n","'''in the first undirected graph'''\n","al1 = al2[::]\n","\n","al1[3].append(1)\n","\n","print(al1)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[[1, 2], [0, 2], [0, 4], [], [2, 3], [6], [5]]\n","[[1], [2], [0, 4], [1], [3], [6], []]\n"],"name":"stdout"}]},{"metadata":{"id":"0c7CQ8aUIw1c","colab_type":"code","colab":{}},"cell_type":"code","source":["def topo_sort(g):\n"," v = len(al)\n"," orders, complete_orders = [], []\n"," colors = [STATE.white] * v\n"," for i in range(v): # run dfs on all the node\n"," if colors[i] == STATE.white:\n"," dfs(al,i, colors, orders, complete_orders)\n"," return complete_orders[::-1]"],"execution_count":0,"outputs":[]},{"metadata":{"id":"H3ra08cvIz5B","colab_type":"code","outputId":"41e31576-c092-4f7a-919b-1cb22d1eda04","executionInfo":{"status":"ok","timestamp":1552244300038,"user_tz":420,"elapsed":326,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":54}},"cell_type":"code","source":["print(topo_sort(al2))\n","print(topo_sort(al1))"],"execution_count":0,"outputs":[{"output_type":"stream","text":["[5, 6, 0, 1, 2, 4, 3]\n","[5, 6, 0, 1, 2, 4, 3]\n"],"name":"stdout"}]},{"metadata":{"id":"YVz-BG3YFJFX","colab_type":"text"},"cell_type":"markdown","source":["### Minimum Spanning Tree"]},{"metadata":{"id":"tI9aTeoy7Z2p","colab_type":"text"},"cell_type":"markdown","source":["#### Prim's Algorithm"]},{"metadata":{"id":"ZUW60VFQGwiD","colab_type":"code","colab":{}},"cell_type":"code","source":["a= {1:[(2, 2), (3, 12), (4, 10)], 2:[(1, 2), (3, 8), (5, 9)], 3:[(1, 12), (2, 8), (4, 6), (5, 3)], 4:[(1, 10),(3, 6), (5, 7)], 5:[(2, 9), (3, 3), (4, 7)]}\n","\n","class edge():\n"," def __init__(self, pid, id, w ):\n"," self.pid = pid\n"," self.id = id\n"," self.w = w\n"," def __lt__(self, other):\n"," return self.w < other.w\n"," \n"," def __eq__(self, other):\n"," return self.w == other.w\n"," \n"," def __str__(self):\n"," return str(self.pid) + '->' + str(self.id) + ':' + str(self.w)\n"," \n"],"execution_count":0,"outputs":[]},{"metadata":{"id":"AdLTUkgxAI1I","colab_type":"code","colab":{}},"cell_type":"code","source":["import queue\n","def prim(g, n):\n"," # step 1:\n"," start = 1\n"," V = {start} #spanning tree set\n"," E = queue.PriorityQueue() # the set of all edges, \n"," ans = []\n"," \n"," while len(V) < n:\n"," # add edges of start, and the other endpoint is in nv\n"," idlst = g[start]\n"," for id, w in idlst:\n"," if id not in V:\n"," E.put(edge(start, id, w))\n"," \n"," while E:\n"," # pick the smallest edge\n"," minEdge = E.get()\n","\n"," if minEdge.id not in V:\n"," # set the new id as start\n"," start = minEdge.id\n"," # add this id to the set of tree nodes\n"," V.add(minEdge.id)\n"," ans.append(minEdge)\n"," break\n"," return ans\n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"40vRdighTR8f","colab_type":"code","outputId":"d3c8197f-536b-4868-8f21-6dab330cdf69","executionInfo":{"status":"ok","timestamp":1553243555679,"user_tz":420,"elapsed":802,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":90}},"cell_type":"code","source":["ans = prim(a, 5)\n","for e in ans:\n"," print(e)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["1->2:2\n","2->3:8\n","3->5:3\n","3->4:6\n"],"name":"stdout"}]},{"metadata":{"id":"0r0DIbiU7d_v","colab_type":"code","colab":{}},"cell_type":"code","source":["class node:\n"," def __init__(self, p, w):\n"," self.p = p\n"," self.w = w\n"," def __lt__(self, other):\n"," return self.w < other.w\n"," def __eq__(self, other):\n"," return self.w == other.w\n"," def __str__(self):\n"," return str(self.p) + '->' +str(self.id)+':'+str(self.w)\n","\n"],"execution_count":0,"outputs":[]},{"metadata":{"id":"iE1wCIcbtj7t","colab_type":"code","colab":{}},"cell_type":"code","source":["def extractMin(q):\n"," minNode = None\n"," minW = float('inf')\n"," minIndex = -1\n"," for idx, node in enumerate(q):\n"," if node.w < minW:\n"," minNode = node\n"," minW = node.w\n"," minIdx = idx\n"," #q.remove(minNode)\n"," return minNode, minIdx\n","\n","def primMst(g, n):\n"," q = [None]*n\n"," S = {}\n"," ans = []\n"," for i in range(n):\n"," q[i] = node(None, float('inf'))\n"," q[0] = node(None, 0)\n"," S = {1}\n"," # main process\n"," while len(S) < n:\n"," minNode, minIdx = extractMin(q)\n"," S.add(minIdx+1)\n"," if minNode.p is not None:\n"," ans.append((minNode.p+1, minIdx+1))\n"," q[minIdx] = node(None, float('inf'))\n"," for v, w in g[minIdx+1]:\n"," if v not in S and w < q[v-1].w:\n"," q[v-1].p = minIdx\n"," q[v-1].w = w\n"," return ans\n"," \n"," \n"," \n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"JzWJqXcYRuyh","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":35},"outputId":"c71f7b89-d6f5-40d5-cc45-f8391f4511b9","executionInfo":{"status":"ok","timestamp":1553382526262,"user_tz":420,"elapsed":401,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}}},"cell_type":"code","source":["print(primMst(a, 5))"],"execution_count":42,"outputs":[{"output_type":"stream","text":["[(1, 2), (2, 3), (3, 5), (3, 4)]\n"],"name":"stdout"}]},{"metadata":{"id":"vwa3SlexCQAn","colab_type":"text"},"cell_type":"markdown","source":["#### Kruskal's Algorithm"]},{"metadata":{"id":"QC19-o-XCUF1","colab_type":"code","colab":{}},"cell_type":"code","source":["def kruskalMst(g, n):"],"execution_count":0,"outputs":[]}]} diff --git a/Colab Codes/Colab Notebooks/linear_search_two_pointer.ipynb b/Colab Codes/Colab Notebooks/linear_search_two_pointer.ipynb index cfbfe12..7a6c8aa 100644 --- a/Colab Codes/Colab Notebooks/linear_search_two_pointer.ipynb +++ b/Colab Codes/Colab Notebooks/linear_search_two_pointer.ipynb @@ -1 +1 @@ -{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"linear_search_two_pointer.ipynb","version":"0.3.2","provenance":[]},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"metadata":{"id":"Td9spABdUsmw","colab_type":"text"},"cell_type":"markdown","source":["## Fixed sliding window"]},{"metadata":{"id":"ZtQn4pmaUw9p","colab_type":"code","colab":{}},"cell_type":"code","source":["def fixedSlideWindow(A, k):\n"," n = len(A)\n"," if k >= n:\n"," return sum(A)\n"," # compute the first window\n"," acc = sum(A[:k])\n"," ans = acc\n"," # slide the window\n"," for i in range(n-k): # i is the start point of the window\n"," j = i + k # j is the end point of the window\n"," acc = acc - A[i] + A[j]\n"," ans = max(ans, acc)\n"," return ans"],"execution_count":0,"outputs":[]},{"metadata":{"id":"qadccsiTUyCl","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":35},"outputId":"deb89a38-04eb-4da8-f75e-49efaccf633c","executionInfo":{"status":"ok","timestamp":1550371239838,"user_tz":480,"elapsed":421,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}}},"cell_type":"code","source":["A =[8,5,10,7,9,4,15,12,90,13]\n","fixedSlideWindow(A,3)"],"execution_count":2,"outputs":[{"output_type":"execute_result","data":{"text/plain":["117"]},"metadata":{"tags":[]},"execution_count":2}]},{"metadata":{"id":"BGv4PowyVBul","colab_type":"text"},"cell_type":"markdown","source":["## Plot the prefix sum"]},{"metadata":{"id":"HY1vpO-fVKJb","colab_type":"code","colab":{}},"cell_type":"code","source":["nums = [2,3,1,2,4,3]\n","s = 7\n","\n","prefixSum = [0]\n","for n in nums:\n"," prefixSum.append(prefixSum[-1]+n)"],"execution_count":0,"outputs":[]},{"metadata":{"id":"sce50K_-VEGF","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":388},"outputId":"01d62b6a-ef95-434b-8561-deddac3717dd","executionInfo":{"status":"ok","timestamp":1550371305221,"user_tz":480,"elapsed":975,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}}},"cell_type":"code","source":["import matplotlib.pyplot as plt\n","import numpy as np\n","from mpl_toolkits.mplot3d import Axes3D\n","#nums = [3, 4, 5, 6, 7, 0, 1, 2]\n","x = np.arange(0, len(nums)-1, 1)\n","# fig = plt.figure(1)\n","f=plt.figure(figsize=(10,6))\n","\n","#f.gca().set_aspect('equal', adjustable='box')\n","\n","# f.suptitle('The process of monotone decreasing stack')\n","# f.xlabel('index')\n","# f.ylabel('value')\n","ax1 = f.add_subplot(121)\n","ax1.plot(np.arange(0, len(nums), 1), nums, marker='o', markersize=4, label = 'array')\n","ax1.plot(np.arange(-1, len(prefixSum)-1, 1), prefixSum, marker='o', markersize=4, label = 'prefix sum')\n","ax1.set_xlabel('index')\n","ax1.set_ylabel('value')\n","ax1.legend()\n","\n","x, y = np.meshgrid(np.arange(0, len(nums), 1), np.arange(0, len(nums), 1))\n","\n","Z = [[-1 for c in range(len(nums))] for r in range(len(nums))]\n","for r in range(len(nums)):\n"," for c in range(r, len(nums)):\n"," Z[r][c] = prefixSum[c+1]-prefixSum[r]\n","ax2 = f.add_subplot(122,projection='3d')\n","ax2.scatter(x,y, Z, marker='*',s=50, alpha=0.6)\n","\n","# for i in range(1, 3):\n","# print(deStacks[i-1])\n","# axarrs[0, i].plot(np.arange(0, len(deStacks[i-1]), 1), deStacks[i-1], markersize = 10, color = 'r', marker='*', linestyle=':') # x, A, 'o')\n","# axarrs[0, i].plot(x, A, 'o',markersize=4)\n","# axarrs[0, i].set_title('step: '+ str(i))\n","# for i in range(3):\n","# axarrs[1, i].plot(np.arange(0, len(deStacks[2+i]), 1), deStacks[2+i], markersize = 10, color = 'r', marker='*', linestyle=':')\n","# axarrs[1, i].plot(x, A, 'o',markersize=4)\n","# axarrs[1, i].set_title('step: '+str(i+3))\n","plt.show()"],"execution_count":5,"outputs":[{"output_type":"display_data","data":{"image/png":"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\n","text/plain":["
"]},"metadata":{"tags":[]}}]}]} \ No newline at end of file +{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"linear_search_two_pointer.ipynb","version":"0.3.2","provenance":[]},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"metadata":{"id":"Td9spABdUsmw","colab_type":"text"},"cell_type":"markdown","source":["## Fixed sliding window"]},{"metadata":{"id":"ZtQn4pmaUw9p","colab_type":"code","colab":{}},"cell_type":"code","source":["def fixedSlideWindow(A, k):\n"," n = len(A)\n"," if k >= n:\n"," return sum(A)\n"," # compute the first window\n"," acc = sum(A[:k])\n"," ans = acc\n"," # slide the window\n"," for i in range(n-k): # i is the start point of the window\n"," j = i + k # j is the end point of the window\n"," acc = acc - A[i] + A[j]\n"," ans = max(ans, acc)\n"," return ans"],"execution_count":0,"outputs":[]},{"metadata":{"id":"qadccsiTUyCl","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":35},"outputId":"deb89a38-04eb-4da8-f75e-49efaccf633c","executionInfo":{"status":"ok","timestamp":1550371239838,"user_tz":480,"elapsed":421,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}}},"cell_type":"code","source":["A =[8,5,10,7,9,4,15,12,90,13]\n","fixedSlideWindow(A,3)"],"execution_count":2,"outputs":[{"output_type":"execute_result","data":{"text/plain":["117"]},"metadata":{"tags":[]},"execution_count":2}]},{"metadata":{"id":"BGv4PowyVBul","colab_type":"text"},"cell_type":"markdown","source":["## Plot the prefix sum"]},{"metadata":{"id":"HY1vpO-fVKJb","colab_type":"code","colab":{}},"cell_type":"code","source":["nums = [2,3,1,2,4,3]\n","s = 7\n","\n","prefixSum = [0]\n","for n in nums:\n"," prefixSum.append(prefixSum[-1]+n)"],"execution_count":0,"outputs":[]},{"metadata":{"id":"sce50K_-VEGF","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":388},"outputId":"01d62b6a-ef95-434b-8561-deddac3717dd","executionInfo":{"status":"ok","timestamp":1550371305221,"user_tz":480,"elapsed":975,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}}},"cell_type":"code","source":["import matplotlib.pyplot as plt\n","import numpy as np\n","from mpl_toolkits.mplot3d import Axes3D\n","#nums = [3, 4, 5, 6, 7, 0, 1, 2]\n","x = np.arange(0, len(nums)-1, 1)\n","# fig = plt.figure(1)\n","f=plt.figure(figsize=(10,6))\n","\n","#f.gca().set_aspect('equal', adjustable='box')\n","\n","# f.suptitle('The process of monotonically decreasing stack')\n","# f.xlabel('index')\n","# f.ylabel('value')\n","ax1 = f.add_subplot(121)\n","ax1.plot(np.arange(0, len(nums), 1), nums, marker='o', markersize=4, label = 'array')\n","ax1.plot(np.arange(-1, len(prefixSum)-1, 1), prefixSum, marker='o', markersize=4, label = 'prefix sum')\n","ax1.set_xlabel('index')\n","ax1.set_ylabel('value')\n","ax1.legend()\n","\n","x, y = np.meshgrid(np.arange(0, len(nums), 1), np.arange(0, len(nums), 1))\n","\n","Z = [[-1 for c in range(len(nums))] for r in range(len(nums))]\n","for r in range(len(nums)):\n"," for c in range(r, len(nums)):\n"," Z[r][c] = prefixSum[c+1]-prefixSum[r]\n","ax2 = f.add_subplot(122,projection='3d')\n","ax2.scatter(x,y, Z, marker='*',s=50, alpha=0.6)\n","\n","# for i in range(1, 3):\n","# print(deStacks[i-1])\n","# axarrs[0, i].plot(np.arange(0, len(deStacks[i-1]), 1), deStacks[i-1], markersize = 10, color = 'r', marker='*', linestyle=':') # x, A, 'o')\n","# axarrs[0, i].plot(x, A, 'o',markersize=4)\n","# axarrs[0, i].set_title('step: '+ str(i))\n","# for i in range(3):\n","# axarrs[1, i].plot(np.arange(0, len(deStacks[2+i]), 1), deStacks[2+i], markersize = 10, color = 'r', marker='*', linestyle=':')\n","# axarrs[1, i].plot(x, A, 'o',markersize=4)\n","# axarrs[1, i].set_title('step: '+str(i+3))\n","plt.show()"],"execution_count":5,"outputs":[{"output_type":"display_data","data":{"image/png":"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\n","text/plain":["
"]},"metadata":{"tags":[]}}]}]} diff --git a/Colab Codes/Colab Notebooks/suffix_array.ipynb b/Colab Codes/Colab Notebooks/suffix_array.ipynb index db31069..3f053d5 100644 --- a/Colab Codes/Colab Notebooks/suffix_array.ipynb +++ b/Colab Codes/Colab Notebooks/suffix_array.ipynb @@ -1 +1 @@ -{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"suffix_array.ipynb","version":"0.3.2","provenance":[],"toc_visible":true},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"metadata":{"id":"3s7lvSxaj9Fi","colab_type":"text"},"cell_type":"markdown","source":["## sort cyclic shifts of string s + '$' to construct the suffix array"]},{"metadata":{"id":"JjYArMUrkDIu","colab_type":"code","colab":{}},"cell_type":"code","source":["from collections import OrderedDict\n","\n","def getCharOrder(s):\n"," n = len(s)\n"," numChars = 256\n"," count = [0]*numChars # totally 256 chars, if you want, can print it out to see these chars\n"," \n"," order = [0]*(n)\n"," \n"," #count the occurrence of each char\n"," for c in s:\n"," count[ord(c)] += 1\n"," \n"," # prefix sum of each char\n"," for i in range(1, numChars):\n"," count[i] += count[i-1]\n"," \n"," # assign from count down to be stable\n"," for i in range(n-1,-1,-1):\n"," count[ord(s[i])] -=1\n"," order[count[ord(s[i])]] = i # put the index into the order instead the suffix string\n"," \n"," return order\n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"CXthy2Rqt6nm","colab_type":"code","colab":{}},"cell_type":"code","source":["def getCharClass(order, cls):\n"," n = len(order)\n"," cls = [0]*n\n"," # if it all differs, then cls[i] = order[i]\n"," cls[order[0]] = 0 #the 6th will be 0\n"," for i in range(1, n):\n"," # use order[i] as index, so the last index\n"," if s[order[i]] != s[order[i-1]]:\n"," print('diff',s[order[i]],s[order[i-1]])\n"," cls[order[i]] = cls[order[i-1]] + 1\n"," else:\n"," cls[order[i]] = cls[order[i-1]]\n"," return cls\n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"yfCeF3b77m4M","colab_type":"code","colab":{}},"cell_type":"code","source":["'''It is a counting sort using the first part as class'''\n","def sortDoubled(s, L, order, cls):\n"," n = len(s)\n"," count = [0] * n\n"," new_order = [0] * n\n"," # their key is the class\n"," for i in range(n):\n"," count[cls[i]] += 1\n"," \n"," # prefix sum\n"," for i in range(1, n):\n"," count[i] += count[i-1]\n"," \n"," # assign from count down to be stable\n"," # sort the first half\n"," for i in range(n-1, -1, -1):\n"," start = (order[i] - L + n) % n #get the start index of the first half, \n"," count[cls[start]] -= 1\n"," new_order[count[cls[start]]] = start\n"," \n"," return new_order"],"execution_count":0,"outputs":[]},{"metadata":{"id":"2I1nIvImB3O8","colab_type":"code","colab":{}},"cell_type":"code","source":["def updateClass(order, cls, L):\n"," n = len(order)\n"," new_cls = [0]*n\n"," # if it all differs, then cls[i] = order[i]\n"," new_cls[order[0]] = 0 #the 6th will be 0\n"," for i in range(1, n):\n"," cur_order, prev_order = order[i], order[i-1]\n"," # use order[i] as index, so the last index\n"," if cls[cur_order] != cls[prev_order] or cls[(cur_order+L) % n] != cls[(prev_order+L) % n]:\n"," new_cls[cur_order] = new_cls[prev_order] + 1\n"," else:\n"," new_cls[cur_order] = new_cls[prev_order]\n"," return new_cls"],"execution_count":0,"outputs":[]},{"metadata":{"id":"2Wv3muUHkNl_","colab_type":"code","colab":{}},"cell_type":"code","source":["\n","def cyclic_shifts_sort(s):\n"," s = s + '$'\n"," n = len(s)\n"," order = getCharOrder(s)\n"," cls = getCharClass(s, order)\n"," print(order, cls)\n"," L = 1\n"," while L < n:\n"," order = sortDoubled(s, 1, order, cls)\n"," cls = updateClass(order, cls, L)\n"," print(order, cls)\n"," L *= 2\n"," \n"," return order"],"execution_count":0,"outputs":[]},{"metadata":{"id":"OpqYr3Z6rsM_","colab_type":"code","outputId":"eb382cee-6f8d-459c-911f-10c955b45dad","executionInfo":{"status":"ok","timestamp":1549232253713,"user_tz":480,"elapsed":336,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":146}},"cell_type":"code","source":["s = 'ababaa'\n","cyclic_shifts_sort(s)"],"execution_count":32,"outputs":[{"output_type":"stream","text":["diff a $\n","diff b a\n","[6, 0, 2, 4, 5, 1, 3] [1, 2, 1, 2, 1, 1, 0]\n","[6, 5, 4, 0, 2, 1, 3] [3, 4, 3, 4, 2, 1, 0]\n","[6, 5, 4, 0, 2, 3, 1] [3, 6, 4, 5, 2, 1, 0]\n","[6, 5, 4, 0, 2, 3, 1] [3, 6, 4, 5, 2, 1, 0]\n"],"name":"stdout"},{"output_type":"execute_result","data":{"text/plain":["[6, 5, 4, 0, 2, 3, 1]"]},"metadata":{"tags":[]},"execution_count":32}]},{"metadata":{"id":"5nNbGVRTjvvL","colab_type":"text"},"cell_type":"markdown","source":[""]}]} \ No newline at end of file +{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"suffix_array.ipynb","version":"0.3.2","provenance":[],"toc_visible":true},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"metadata":{"id":"3s7lvSxaj9Fi","colab_type":"text"},"cell_type":"markdown","source":["## sort cyclic shifts of string s + '$' to construct the suffix array"]},{"metadata":{"id":"JjYArMUrkDIu","colab_type":"code","colab":{}},"cell_type":"code","source":["from collections import OrderedDict\n","\n","def getCharOrder(s):\n"," n = len(s)\n"," numChars = 256\n"," count = [0]*numChars # total of 256 chars, if you want, can print it out to see these chars\n"," \n"," order = [0]*(n)\n"," \n"," #count the occurrence of each char\n"," for c in s:\n"," count[ord(c)] += 1\n"," \n"," # prefix sum of each char\n"," for i in range(1, numChars):\n"," count[i] += count[i-1]\n"," \n"," # assign from count down to be stable\n"," for i in range(n-1,-1,-1):\n"," count[ord(s[i])] -=1\n"," order[count[ord(s[i])]] = i # put the index into the order instead the suffix string\n"," \n"," return order\n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"CXthy2Rqt6nm","colab_type":"code","colab":{}},"cell_type":"code","source":["def getCharClass(order, cls):\n"," n = len(order)\n"," cls = [0]*n\n"," # if it all differs, then cls[i] = order[i]\n"," cls[order[0]] = 0 #the 6th will be 0\n"," for i in range(1, n):\n"," # use order[i] as index, so the last index\n"," if s[order[i]] != s[order[i-1]]:\n"," print('diff',s[order[i]],s[order[i-1]])\n"," cls[order[i]] = cls[order[i-1]] + 1\n"," else:\n"," cls[order[i]] = cls[order[i-1]]\n"," return cls\n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"yfCeF3b77m4M","colab_type":"code","colab":{}},"cell_type":"code","source":["'''It is a counting sort using the first part as class'''\n","def sortDoubled(s, L, order, cls):\n"," n = len(s)\n"," count = [0] * n\n"," new_order = [0] * n\n"," # their key is the class\n"," for i in range(n):\n"," count[cls[i]] += 1\n"," \n"," # prefix sum\n"," for i in range(1, n):\n"," count[i] += count[i-1]\n"," \n"," # assign from count down to be stable\n"," # sort the first half\n"," for i in range(n-1, -1, -1):\n"," start = (order[i] - L + n) % n #get the start index of the first half, \n"," count[cls[start]] -= 1\n"," new_order[count[cls[start]]] = start\n"," \n"," return new_order"],"execution_count":0,"outputs":[]},{"metadata":{"id":"2I1nIvImB3O8","colab_type":"code","colab":{}},"cell_type":"code","source":["def updateClass(order, cls, L):\n"," n = len(order)\n"," new_cls = [0]*n\n"," # if it all differs, then cls[i] = order[i]\n"," new_cls[order[0]] = 0 #the 6th will be 0\n"," for i in range(1, n):\n"," cur_order, prev_order = order[i], order[i-1]\n"," # use order[i] as index, so the last index\n"," if cls[cur_order] != cls[prev_order] or cls[(cur_order+L) % n] != cls[(prev_order+L) % n]:\n"," new_cls[cur_order] = new_cls[prev_order] + 1\n"," else:\n"," new_cls[cur_order] = new_cls[prev_order]\n"," return new_cls"],"execution_count":0,"outputs":[]},{"metadata":{"id":"2Wv3muUHkNl_","colab_type":"code","colab":{}},"cell_type":"code","source":["\n","def cyclic_shifts_sort(s):\n"," s = s + '$'\n"," n = len(s)\n"," order = getCharOrder(s)\n"," cls = getCharClass(s, order)\n"," print(order, cls)\n"," L = 1\n"," while L < n:\n"," order = sortDoubled(s, 1, order, cls)\n"," cls = updateClass(order, cls, L)\n"," print(order, cls)\n"," L *= 2\n"," \n"," return order"],"execution_count":0,"outputs":[]},{"metadata":{"id":"OpqYr3Z6rsM_","colab_type":"code","outputId":"eb382cee-6f8d-459c-911f-10c955b45dad","executionInfo":{"status":"ok","timestamp":1549232253713,"user_tz":480,"elapsed":336,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":146}},"cell_type":"code","source":["s = 'ababaa'\n","cyclic_shifts_sort(s)"],"execution_count":32,"outputs":[{"output_type":"stream","text":["diff a $\n","diff b a\n","[6, 0, 2, 4, 5, 1, 3] [1, 2, 1, 2, 1, 1, 0]\n","[6, 5, 4, 0, 2, 1, 3] [3, 4, 3, 4, 2, 1, 0]\n","[6, 5, 4, 0, 2, 3, 1] [3, 6, 4, 5, 2, 1, 0]\n","[6, 5, 4, 0, 2, 3, 1] [3, 6, 4, 5, 2, 1, 0]\n"],"name":"stdout"},{"output_type":"execute_result","data":{"text/plain":["[6, 5, 4, 0, 2, 3, 1]"]},"metadata":{"tags":[]},"execution_count":32}]},{"metadata":{"id":"5nNbGVRTjvvL","colab_type":"text"},"cell_type":"markdown","source":[""]}]} diff --git a/Colab Codes/Colab Notebooks/tree_data_structure.ipynb b/Colab Codes/Colab Notebooks/tree_data_structure.ipynb index 7575aef..e5e7dc8 100644 --- a/Colab Codes/Colab Notebooks/tree_data_structure.ipynb +++ b/Colab Codes/Colab Notebooks/tree_data_structure.ipynb @@ -1 +1 @@ -{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"tree_data_structure.ipynb","version":"0.3.2","provenance":[],"toc_visible":true},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"metadata":{"id":"3A2mprldYvXe","colab_type":"text"},"cell_type":"markdown","source":["### N-aray Tree"]},{"metadata":{"id":"h5LNx9p_ZxtH","colab_type":"text"},"cell_type":"markdown","source":["#### Define Tree Node"]},{"metadata":{"id":"9R_XYWC2Yz-G","colab_type":"code","colab":{}},"cell_type":"code","source":["class NaryNode:\n"," '''Define a n-ary node'''\n"," def __init__(self, n, val):\n"," self.children = [None] * n\n"," self.val = val\n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"Xx5WEdJDZ4-q","colab_type":"text"},"cell_type":"markdown","source":["#### Define a Tree and implement operations"]},{"metadata":{"id":"qtAos_DOZwkw","colab_type":"code","colab":{}},"cell_type":"code","source":[""],"execution_count":0,"outputs":[]},{"metadata":{"id":"wLPjQrfnZbQY","colab_type":"text"},"cell_type":"markdown","source":["### Binary Tree"]},{"metadata":{"id":"oUZYimKbZ1ZL","colab_type":"text"},"cell_type":"markdown","source":["#### Define Tree Node"]},{"metadata":{"id":"ZKknhNURZdA0","colab_type":"code","colab":{}},"cell_type":"code","source":["class BinaryNode:\n"," '''Define a classical binary tree node'''\n"," def __init__(self, val):\n"," self.left = None\n"," self.right = None\n"," self.val = val"],"execution_count":0,"outputs":[]},{"metadata":{"id":"yPukXXkXZ_zn","colab_type":"text"},"cell_type":"markdown","source":["#### Define a Tree and implement operations\n"," 1\n"," / \\ \n"," 2 3\n"," / \\ \\\n","4 5 6 "]},{"metadata":{"id":"CyXwZ9sf8dyG","colab_type":"code","colab":{}},"cell_type":"code","source":["root = BinaryNode(1)\n","left = BinaryNode(2)\n","right = BinaryNode(3)\n","root.left = left\n","root.right = right\n","left.left = BinaryNode(4)\n","left.right = BinaryNode(5)\n","right.right = BinaryNode(6)"],"execution_count":0,"outputs":[]},{"metadata":{"id":"gOHqKKfY_Ug0","colab_type":"code","colab":{}},"cell_type":"code","source":["def constructTree(a, idx):\n"," '''construct a binary tree recursively from input array a'''\n"," if idx >= len(a):\n"," return None\n"," node = BinaryNode(a[idx])\n"," node.left = constructTree(a, 2*idx + 1)\n"," node.right = constructTree(a, 2*idx + 2)\n"," return node"],"execution_count":0,"outputs":[]},{"metadata":{"id":"STvuN-5p_5Di","colab_type":"code","colab":{}},"cell_type":"code","source":["nums = [1, 2, 3, 4, 5, None, 6]\n","root = constructTree(nums, 0)"],"execution_count":0,"outputs":[]},{"metadata":{"id":"hrJC3v5ZAvD-","colab_type":"text"},"cell_type":"markdown","source":["#### To show the nodes at each level, we use LevelOrder function to print out the tree:"]},{"metadata":{"id":"uPxVdjRrA0UB","colab_type":"code","colab":{}},"cell_type":"code","source":["def LevelOrder(root):\n"," q = [root]\n"," while q:\n"," new_q = []\n"," for n in q:\n"," if n is not None:\n"," print(n.val, end=',')\n"," if n.left:\n"," new_q.append(n.left)\n"," if n.right:\n"," new_q.append(n.right)\n"," q = new_q\n"," print('\\n')\n"],"execution_count":0,"outputs":[]},{"metadata":{"id":"Pl2HGcHkA4rT","colab_type":"code","outputId":"2282a8ed-e1c1-4dae-b83c-0c2a8c19b470","executionInfo":{"status":"ok","timestamp":1550969967703,"user_tz":480,"elapsed":336,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":127}},"cell_type":"code","source":["LevelOrder(root)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["1,\n","\n","2,3,\n","\n","4,5,None,6,\n","\n"],"name":"stdout"}]},{"metadata":{"id":"2edkOHbCCV7n","colab_type":"text"},"cell_type":"markdown","source":[""]}]} \ No newline at end of file +{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"tree_data_structure.ipynb","version":"0.3.2","provenance":[],"toc_visible":true},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"metadata":{"id":"3A2mprldYvXe","colab_type":"text"},"cell_type":"markdown","source":["### N-ary Tree"]},{"metadata":{"id":"h5LNx9p_ZxtH","colab_type":"text"},"cell_type":"markdown","source":["#### Define Tree Node"]},{"metadata":{"id":"9R_XYWC2Yz-G","colab_type":"code","colab":{}},"cell_type":"code","source":["class NaryNode:\n"," '''Define a n-ary node'''\n"," def __init__(self, n, val):\n"," self.children = [None] * n\n"," self.val = val\n"," "],"execution_count":0,"outputs":[]},{"metadata":{"id":"Xx5WEdJDZ4-q","colab_type":"text"},"cell_type":"markdown","source":["#### Define a Tree and implement operations"]},{"metadata":{"id":"qtAos_DOZwkw","colab_type":"code","colab":{}},"cell_type":"code","source":[""],"execution_count":0,"outputs":[]},{"metadata":{"id":"wLPjQrfnZbQY","colab_type":"text"},"cell_type":"markdown","source":["### Binary Tree"]},{"metadata":{"id":"oUZYimKbZ1ZL","colab_type":"text"},"cell_type":"markdown","source":["#### Define Tree Node"]},{"metadata":{"id":"ZKknhNURZdA0","colab_type":"code","colab":{}},"cell_type":"code","source":["class BinaryNode:\n"," '''Define a classical binary tree node'''\n"," def __init__(self, val):\n"," self.left = None\n"," self.right = None\n"," self.val = val"],"execution_count":0,"outputs":[]},{"metadata":{"id":"yPukXXkXZ_zn","colab_type":"text"},"cell_type":"markdown","source":["#### Define a Tree and implement operations\n"," 1\n"," / \\ \n"," 2 3\n"," / \\ \\\n","4 5 6 "]},{"metadata":{"id":"CyXwZ9sf8dyG","colab_type":"code","colab":{}},"cell_type":"code","source":["root = BinaryNode(1)\n","left = BinaryNode(2)\n","right = BinaryNode(3)\n","root.left = left\n","root.right = right\n","left.left = BinaryNode(4)\n","left.right = BinaryNode(5)\n","right.right = BinaryNode(6)"],"execution_count":0,"outputs":[]},{"metadata":{"id":"gOHqKKfY_Ug0","colab_type":"code","colab":{}},"cell_type":"code","source":["def constructTree(a, idx):\n"," '''construct a binary tree recursively from input array a'''\n"," if idx >= len(a):\n"," return None\n"," node = BinaryNode(a[idx])\n"," node.left = constructTree(a, 2*idx + 1)\n"," node.right = constructTree(a, 2*idx + 2)\n"," return node"],"execution_count":0,"outputs":[]},{"metadata":{"id":"STvuN-5p_5Di","colab_type":"code","colab":{}},"cell_type":"code","source":["nums = [1, 2, 3, 4, 5, None, 6]\n","root = constructTree(nums, 0)"],"execution_count":0,"outputs":[]},{"metadata":{"id":"hrJC3v5ZAvD-","colab_type":"text"},"cell_type":"markdown","source":["#### To show the nodes at each level, we use the LevelOrder function to print out the tree:"]},{"metadata":{"id":"uPxVdjRrA0UB","colab_type":"code","colab":{}},"cell_type":"code","source":["def LevelOrder(root):\n"," q = [root]\n"," while q:\n"," new_q = []\n"," for n in q:\n"," if n is not None:\n"," print(n.val, end=',')\n"," if n.left:\n"," new_q.append(n.left)\n"," if n.right:\n"," new_q.append(n.right)\n"," q = new_q\n"," print('\\n')\n"],"execution_count":0,"outputs":[]},{"metadata":{"id":"Pl2HGcHkA4rT","colab_type":"code","outputId":"2282a8ed-e1c1-4dae-b83c-0c2a8c19b470","executionInfo":{"status":"ok","timestamp":1550969967703,"user_tz":480,"elapsed":336,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}},"colab":{"base_uri":"https://localhost:8080/","height":127}},"cell_type":"code","source":["LevelOrder(root)"],"execution_count":0,"outputs":[{"output_type":"stream","text":["1,\n","\n","2,3,\n","\n","4,5,None,6,\n","\n"],"name":"stdout"}]},{"metadata":{"id":"2edkOHbCCV7n","colab_type":"text"},"cell_type":"markdown","source":[""]}]} diff --git a/Colab Codes/Colab Notebooks/tree_search.ipynb b/Colab Codes/Colab Notebooks/tree_search.ipynb index f3fada9..38e6f2a 100644 --- a/Colab Codes/Colab Notebooks/tree_search.ipynb +++ b/Colab Codes/Colab Notebooks/tree_search.ipynb @@ -1 +1 @@ -{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"tree_search.ipynb","version":"0.3.2","provenance":[]},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"metadata":{"id":"-N44hhD6CqRT","colab_type":"code","colab":{}},"cell_type":"code","source":[""],"execution_count":0,"outputs":[]},{"metadata":{"id":"8xckq2iGCtG8","colab_type":"text"},"cell_type":"markdown","source":["## DFS tree search"]},{"metadata":{"id":"dw0xDCnDCyLo","colab_type":"text"},"cell_type":"markdown","source":["### A general purpose search"]},{"metadata":{"id":"c9xcMvZxC0zK","colab_type":"code","colab":{}},"cell_type":"code","source":["def dfs(t, s):\n"," '''implement the dfs recursive of tree'''\n"," print(s)\n"," for neighbor in t[s]:\n"," dfs(t, neighbor)\n"," return"],"execution_count":0,"outputs":[]},{"metadata":{"id":"pOnCVL5eDIh-","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":121},"outputId":"7c437d60-4881-4eea-b4ba-c9675519a7e2","executionInfo":{"status":"ok","timestamp":1551054538760,"user_tz":480,"elapsed":403,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}}},"cell_type":"code","source":["t = {1: [2, 3], 2: [4, 5],\n"," 3: [], 4: [6], 5: [], 6: []}\n","dfs(t, 1)"],"execution_count":4,"outputs":[{"output_type":"stream","text":["1\n","2\n","4\n","6\n","5\n","3\n"],"name":"stdout"}]}]} \ No newline at end of file +{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"tree_search.ipynb","version":"0.3.2","provenance":[]},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"metadata":{"id":"-N44hhD6CqRT","colab_type":"code","colab":{}},"cell_type":"code","source":[""],"execution_count":0,"outputs":[]},{"metadata":{"id":"8xckq2iGCtG8","colab_type":"text"},"cell_type":"markdown","source":["## DFS tree search"]},{"metadata":{"id":"dw0xDCnDCyLo","colab_type":"text"},"cell_type":"markdown","source":["### A general purpose search"]},{"metadata":{"id":"c9xcMvZxC0zK","colab_type":"code","colab":{}},"cell_type":"code","source":["def dfs(t, s):\n"," '''implement the recursive dfs of tree'''\n"," print(s)\n"," for neighbor in t[s]:\n"," dfs(t, neighbor)\n"," return"],"execution_count":0,"outputs":[]},{"metadata":{"id":"pOnCVL5eDIh-","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":121},"outputId":"7c437d60-4881-4eea-b4ba-c9675519a7e2","executionInfo":{"status":"ok","timestamp":1551054538760,"user_tz":480,"elapsed":403,"user":{"displayName":"Li Yin","photoUrl":"https://lh5.googleusercontent.com/-KgiTKdqPRUg/AAAAAAAAAAI/AAAAAAAAAIE/aHQ6xO5vQpY/s64/photo.jpg","userId":"13365523799853678553"}}},"cell_type":"code","source":["t = {1: [2, 3], 2: [4, 5],\n"," 3: [], 4: [6], 5: [], 6: []}\n","dfs(t, 1)"],"execution_count":4,"outputs":[{"output_type":"stream","text":["1\n","2\n","4\n","6\n","5\n","3\n"],"name":"stdout"}]}]}