Graph is everywhere





5.00/5 (8 votes)
The traditional way to solve the ZigZag Conversion problem is to either build a 2d table or find a pattern among the index of the letters on the same row. There is also a beautiful solution which builds on top of Breadth First Search. Continue reading..
The traditional way to solve the ZigZag Conversion problem is to either build a 2d table or find a pattern among the index of the letters on the same row. There is also a beautiful solution which builds on top of the BFS(Breadth First Search).
Problem
The string "PAYPALISHIRING"
is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility)
P A H N A P L S I I G Y I R
And then read line by line: "PAHNAPLSIIGYIR"
Write the code that will take a string and make this conversion given a number of rows:
string convert(string text, int nRows);
convert("PAYPALISHIRING", 3)
should return "PAHNAPLSIIGYIR"
.
Breadth First Search
Make each letter in the string a node in the graph. If we link the nodes following the order in the original string, we actually build a graph. Each node can have at most two neighbors. The order of the letters in the original string is actually result of DFS(Depth First Search) on this graph.
E.g., for string “ABCDE”,
A E B D C
the resultant graph is as follows. We create some virtual paths between the nodes on the first row and the Root.
The BFS walk produces “AEBDC”, which is exactly what we want.
Just one thing left. What if the node “E” doesn’t exist? For the last branch (e.g., it’s C->D here), we may need to create a virtual path to connect to the Root as show below.
Here comes the code.
from collections import deque class Node: def __init__(self, value): self.visited = False self.value = value self.neighbors = [] class Solution: # @param {string} s # @param {integer} numRows # @return {string} def convert(self, s, numRows): self.__s = s self.__numRows = numRows return self.__BFS(self.__buildGraph()) def __connect(self, prev, this): prev.neighbors.append(this) this.neighbors.append(prev) def __buildGraph(self): '''Build the graph from DFS traversal of the string''' root = Node('') prev = None row = 0 up = True for i in range(len(self.__s)): this = Node(self.__s[i]) if prev is not None: self.__connect(prev, this) prev = this # Connect nearest nodes(those on row #0) to the root if row == 0: self.__connect(root, this) if up: if row < self.__numRows - 1: row += 1 elif row > 0: row -= 1 up = False else: if row > 0: row -= 1 elif row < self.__numRows - 1: row += 1 up = True # The triky part, for BFS later # Build a virtual path to connect to the root for the last branch, if not already if not up and row < self.__numRows - 2: for i in range(row, -1, -1): this = Node('') self.__connect(prev, this) prev = this self.__connect(prev, root) return root def __BFS(self, root): '''Breadth First Search gives the desired string''' work_queue = deque() root.visited = True work_queue.append(root) s = '' while work_queue: node = work_queue.popleft() # No special action for the root as it is an empty string;) s += node.value for i in node.neighbors: if not i.visited: i.visited = True work_queue.append(i) return s