--30 data structures and code line simulation recursion stack

This article originating in individual public number: TechFlow , originality is not easy, seeking followers


Defined stack


Today we originally wanted to talk about quick selection algorithm, but found written several consecutive sorting related, so temporarily change the subject, talking points today, data structures, take a look at classic and simple data structures - stack.

The stack structure I think we should all be familiar with, especially in many parts of the heap and tied together, called "stack" is even more widely publicized. But in fact are two different data structures on the heap and stack essence, we can not simply be confused. Let's start with a relatively simple stack start.

Stacks and queues are in fact the essence of the array (strictly speaking, linear table). But we add some restrictions on the array so that it meets certain conditions only, so a lot of the data structure timid students can relax a bit, nothing special stack tricks, is a special kind of array.

Linear and generalized table data structure other than up, only two stack specificity, is a last-out, the other is read from only one side of the array. But these two are essentially the same, because we can only read and write elements from one side, so the sooner out into the night, of course, last out. From the image below it should be easy Nengkanmingbai.

The predetermined stack we can read and write from one side, we will be conventionally referred to as a side top of the stack can read and write. Not read or write the other side is referred to as a bottom of the stack. We can see from the above chart, after only top of the stack elements of the stack, and to access to the bottom of the stack of elements.

We use Python array to achieve this stack data structure, uncommented really only less than 30 lines, can be said to be very simple, let's look at the code.

class Stack(object):

    def __init__(self, size_limit=None):
        self._size_limit = size_limit
        self.elements = []
        self._size = 0

    # 进栈,判断是否越界
    def push(self, value):
        if self._size_limit is not None and len(self.elements) > self._size_limit:
            raise IndexError("Stack is full")
        else:
            self.elements.append(value)
            self._size += 1

    # 判断栈是否为空
    def is_empty(self):
        return self._size == 0

    # 栈清空
    def clear(self):
        self.elements = []
        self._size = 0

    # 访问元素数量
    def size(self):
        return self._size

    # 查询栈顶元素
    def top(self):
        return self.elements[-1]
    
    # 弹出栈顶元素
    def pop(self):
        val = self.elements.pop()
        self._size -= 1
        return val

In essence, the general stack implementation so only a few methods above, may be less. Because some of the language among the stacks, top, and pop are merged. Means access must pop, pop does not support non-access. So stack implementation logic is very simple, and even can be said that there is no technical content, ideal for entry data structure.

Of course, from the other side it can also be said to achieve the principle of stack and less important, compared to more important is the stack usually used in any place.


Stack of applications


Stack is the most widely used operating system in which, for example, when the program calls the method of execution, the internal compiler, in fact, is a method of recording the current call stack. For example, such a method is the current call to A, if A process went called method B, then the computer will store them in a system of stack-pointer method B, if B in turn calls the method to a C then will add a pointer to the C. When the end of the execution method C, then C will pop up, the computer will result into C, B, before continuing execution B, and so on, until the stack is empty.

So, the question is, if a method A calls itself what will happen?

The answer is to create a new computer will fill the stack pointer A, if A continues recursively, the system then create a new stack pointer ......

From the above process, we can determine two things. First, we write a recursive procedure time, in fact, it is the internal compiler stack form of execution. Second, if we use a recursive infinite loop to keep, due to size limitations stack, so when the depth of the stack exceeds the limit, it will SystemStackExceed error. That is not infinite recursion, because in addition to the operating system memory limit for running outside, the compiler will have maximum recursion depth limit, prevent recursion infinite loop resulting in a system crash. Although each language mechanism is not exactly the same, but one thing is for sure, recursion depth is limited, we can not be unlimited recursion.

That question is, if our system is that there will be a large-scale recursive how to do? Is it still possible to manually add memory to the machine do?

This is one of the issues ACM player on the field often encountered, experienced players in the first day warm-up match will be done in addition to the configuration of vim or other IDE, that will test the computer's maximum recursion depth . In C ++, which is supported forced open recursion depth limit by assembly language, but even so is limited, and as far as I know only C ++ can do it, for other languages, and was opened a case of recursion depth is not enough, there is only one way is to manually build stack simulation recursion.


Manual recursive


Many students may feel pain recursion, but if they try to manually build a stack to simulate recursion, you will find to be more painful. Not only to additional variables to store intermediate state, and the program is also a huge challenge for.

Let's look at an example:

class Node:
    def __init__(self, val):
        self.val = val
        # 左孩子
        self.lchild = None
        # 右孩子
        self.rchild = None


if __name__ == "__main__":
    # 建树
    root = Node(0)
    node1 = Node(1)
    root.lchild = node1
    node2 = Node(2)
    root.rchild = node2
    node3 = Node(3)
    node1.lchild = node3
    node4 = Node(4)
    node1.rchild = node4
    node5 = Node(5)
    node2.rchild = node5

This is a simple binary tree, drawn like this:

          0
         / \
        1   2
       / \   \
      3   4   5

Here we want to pass the stack without using recursive preorder it down, preorder we all know, is the first to traverse the left subtree, then the current output node, and then traverse the right subtree. Write recursive very convenient, only a few lines:

def dfs(node):
    if node is None:
        return
    dfs(node.lchild)
    print(node.val)
    dfs(node.rchild)

Think about it, if you do not use recursion how should I do? If you really try to write, you will find seemingly simple problem seems to be very complicated. We can easily imagine, we have among the nodes stored on the stack, but only the appearance of the stored data. Nature of the problem is when we get a node from the stack of them, how do we determine what it should do? It should be left node traversal, output should do, or should traverse the right node?

Careful analysis and thinking on these issues, we can see that they are all related and recursive backtracking.

Recursive them, when we iterate over a subtree of the current node, with the stack of pop, will return this node. For example, the tree above them, in which a recursive process, we will encounter two 1 this node. The first time it will not output 1, but go to traverse its left subtree, which is 3, then back again to 1, because of its left subtree has been traversed, it will output 1. The left back is called backtracking. If you think of the tree falls, then this process is a bit like down the river, but also upstream, translated into the back is still pretty reasonable.

Before we return to the problem, all the confused nature are derived from the node we can not judge what is currently experiencing the first meeting or reunion after backtracking. And this is related to what we want it to do. Originally recursive them, because the program will record the location and status code when a recursive run, will return to the position after the last call of the recursive backtracking, so we can ignore this issue. Because now we no longer use recursion, so we need to judge for yourself the state of the node.

Figured out is actually very simple, we only need to add a state in which node field indicating whether the node backtracking occurs. Obviously in the beginning, all the node status is True.

class Node:
    def __init__(self, val):
        self.val = val
        self.lchild = None
        self.rchild = None
        self.flag = True

We Node class add a flag as a record, when we initialize it defaults to True. Then very simple, we traverse the nodes in the order of left, right, as long as there is to go out into the left side of the left sub-tree traversal, flag these nodes are encountered in the process all the way to the left of all set to False, since their back has already begun, the future will not happen again back up. Because right traversal issue backtracking does not exist, so can be ignored, want to see, the code will come out ahead.

# 使用我们自己刚刚创建的数据结构
stack = Stack()
# 插入根节点
stack.push(root)

while not stack.is_empty():
    # 获取栈顶元素,也就是当前遍历的节点
    tmp = stack.top()
    # 如果不曾回溯过,并且左子树存在
    while tmp.flag and tmp.lchild is not None:
        # 回溯标记置为False
        tmp.flag = False
        # 栈顶push左孩子
        stack.push(tmp.lchild)
        # 往左遍历
        tmp = tmp.lchild
    # 弹出栈顶
    tmp = stack.pop()
    # 此时说明左节点已经遍历完了,输出
    print(tmp.val)
    # 往右遍历
    if tmp.rchild is not None:
        stack.push(tmp.rchild)

This code is short, but in fact not simple, you want to fully understand the need for in-depth understanding of recursive and circulation. Is a typical simple look at the real problem is not easy, I personally prefer this kind of problem, in addition to the exercise of thinking is also very suitable for the interview, the candidate of thinking, the ability to control the code basically a clear picture. The students do not understand, do not worry, because not encountered such a scene in which the actual scene, the future will introduce other articles on recursive search algorithm and, as long as you insist on reading, I believe we will read it.

Today's article is that, if that be harvested, please easily scan code point of a concern now, you little things are important to me.

Guess you like

Origin www.cnblogs.com/techflow/p/12334261.html