Talking about recursive algorithms from "Stack Reverse Order"

Source: Internet
Author: User

I recently saw a question: it is required to use recursion to reverse a stack.

 

Let's first look at what the most common solution should be. Obviously, the data structure of stack will pop into our minds when we describe the problem of "reverse order.

 

The implementation code is as follows:

public static LinkedStack<Integer> reverseStackDirectly(LinkedStack<Integer> stack) {if(null != stack && !stack.isEmpty()) {LinkedStack<Integer> auxiliary = new LinkedStack<Integer>();while(!stack.isEmpty()) {auxiliary.push(stack.pop());}return auxiliary;}return stack;}

The idea of the code is very clear. First, a new stack is opened as the auxiliary stack, elements in the original Stack are popped up and pushed into the auxiliary stack in turn, and finally the auxiliary stack is returned.

 

Of course, our practice does not conform to the meaning of the question, and the subject requirements need to be solved using recursion. As a matter of fact, everyone understands that recursion also uses the stack data structure, but recursion only uses the method call stack during program running, it is not explicitly created and managed by us.

Next we will consider the recursive solution. when we consider the recursive solution, we should remember that we should find Sub-Problems in the original problem.

In this case, the subproblem can be considered in the following two forms:

  1. After the top elements of the stack are extracted, the stack is reversed, and the top elements of the stack are inserted to the bottom of the stack.
  2. After the elements at the bottom of the stack are extracted, the stack is reversed, and the elements at the bottom of the stack are pushed to the top of the stack.

We can find that no matter which seed problem is used, the stack is displayed in reverse order, and this step does not consider the specific object to be operated, it is the same as the big problem we want to solve.

 

Convert the description of the first question into code:

Public static void reversestack (Consumer stack <integer> stack) {If (null! = Stack &&! Stack. isempty () {// extract the top element of the stack integer Top = stack. pop (); // recursively reversestack (stack) the stack in reverse order; // Insert the elements retrieved earlier into the inserttostackbottom (stack, top) at the bottom of the stack );}}

Let's skip the last step: insert the retrieved elements to the bottom of the stack.

Just compare the differences in the program structure when recursive and non-recursive methods are used:

  • In Recursive Implementation, we first declare a variable to save the top element of the stack, and then call this method recursively.
  • In non-Recursive Implementation, we get the top element of the stack and press it into the auxiliary stack.

When declaring a variable in a method, a variable is actually added to the top stack frame of the method call stack. This is not obvious in the Code. For Java programs, the javap tool allows you to see what happened. For the above programs:

public static void reverseStack(LinkedStack stack);     0  aload_0 [stack]     1  ifnull 28     4  aload_0 [stack]     5  invokevirtual LinkedStack.isEmpty() : boolean [18]     8  ifne 28    11  aload_0 [stack]    12  invokevirtual LinkedStack.pop() : java.lang.Object [24]    15  checkcast java.lang.Integer [28]    18  astore_1 [top]    19  aload_0 [stack]    20  invokestatic misc.ReverseStack.reverseStack(LinkedStack) : void [30]    23  aload_0 [stack]    24  aload_1 [top]    25  invokestatic misc.ReverseStack.insertToStackBottom(LinkedStack, java.lang.Integer) : void [32]    28  return      Local variable table:        [pc: 0, pc: 29] local: stack index: 0 type: LinkedStack        [pc: 19, pc: 28] local: top index: 1 type: java.lang.Integer

Important:

  • Aload_x indicates that the value of the local variable X is pushed into the current stack frame.
  • Astore_x stores the elements at the top of the stack in the current stack frame to the local variable X.

 

When the PC (program counter) value is 18, the astore_1 [Top] occurs to store the top value to the local variable with the index as 1, for local variables, refer to the following local
Variable table.

 

Then, when the PC is equal to 20, the method is called recursively. Here, a new stack frame is created, the new stack frame contains input parameters (also in the form of local variables), and the newly created stack frame is pushed into the method call stack. Then execute the same operation, and return the result recursively, that is, the method call stack is a stack frame after a pop-up.

 

Repeat, the local variable table belongs to a stack frame, while a stack frame is an element of the method call stack. Therefore, when the top element of the stack is saved to a local variable, the local variable is pushed to the stack in essence (in the form of stack frame being pushed to the method call stack ), but this process is not so obvious.

// Non-Recursive Implementation of auxiliary. Push (stack. Pop (); // Recursive Implementation of integer Top = stack. Pop (); reversestack (stack );

So far, the first two steps have been completed. The last step is to insert the elements at the top of the stack to the bottom of the stack.

 

First of all, starting with the most intuitive idea, the stack features determine that it is impossible to directly Insert elements to the bottom of the stack. So we can pop up all the elements in the current stack, and then press the elements to be inserted into the stack. The pushed position is of course the bottom of the stack, and then press the elements that have popped up before into the stack. Obviously, the element pop-up and press-in are involved here. It is not difficult to conclude that this order meets the operation characteristics of the stack. More specifically, element pop-up and re-push meet the "Last-out-first-in" law.


Therefore, the most intuitive implementation is as follows:

private static void insertToStackBottom(LinkedStack<Integer> stack, Integer bottom) {assert (null != stack);LinkedStack<Integer> auxiliary = new LinkedStack<Integer>();while(!stack.isEmpty()) {auxiliary.push(stack.pop());}stack.push(bottom);while(!auxiliary.isEmpty()) {stack.push(auxiliary.pop());}}

The code above uses a secondary stack as a temporary storage space for elements in the original stack. After the incoming elements are pushed to the bottom of the stack, the elements in the temporary Stack are pushed to the original stack. However, obviously, the explicit declaration stack is used here.

 

If you want to implement the above method recursively, you must find out the sub-problems that can be used. It sounds like you are using dynamic planning to solve the problem. In fact, there is also a subtle relationship between Recursive Algorithms and Dynamic Planning Algorithms. Generally, there are top-down and bottom-up dynamic planning methods. The top-down method usually uses recursion and memorandum.

Use recursive methods to describe the above issues as follows:

  • Extract the top elements of the stack, insert the elements to be inserted to the bottom of the stack, and press the top elements of the stack

The above sub-Problem description is not natural, but this is the only way to use the stack structure without explicit use.

Obviously, when the stack does not contain any elements, you can push the elements to be inserted into the stack. Therefore, you can write the following Recursive Implementation:

Private Static void inserttostackbottom (batch stack <integer> stack, integer bottom) {// when the stack is empty, place the element to be inserted to the bottom of the stack if (stack. isempty () {stack. push (bottom); return;} // retrieve the element integer Top = stack at the top of the stack. pop (); // place the passed elements at the bottom of the stack to inserttostackbottom (stack, bottom); // restore the stack. push (top );}

Compare the non-recursive version and recursive version of this method:

  • In Recursive Implementation, local variables are declared and then stored using the method call stack.
  • In non-recursive implementations, a stack is explicitly created to store relevant variables.

 

After careful comparison, we can find that the following two implementations are essentially the same:

// Non-recursive while (! Stack. isempty () {auxiliary. Push (stack. Pop ();} stack. Push (bottom); While (! Auxiliary. isempty () {stack. push (Auxiliary. pop ();} // recursively implement integer Top = stack. pop (); inserttostackbottom (stack, bottom); stack. push (top );

Specifically, the elements in the stack are explicitly stored in another stack created in non-recursive implementations, recursive Implementation implicitly saves all the elements in the stack to the method call stack (through the local variable table in the stack frame ).

 

The following describes another sub-problem:

  • After the elements at the bottom of the stack are extracted, the stack is reversed, and the elements at the bottom of the stack are pushed to the top of the stack.

It is essentially the same, and it is not implemented here.

 

I think the reason why recursive algorithms are sometimes hard to understand is probably because the running rules of method call stacks are not well understood. In general, Recursive Implementation of the same problem is always more concise than non-Recursive Implementation. Here it refers to the concise code volume, the simplicity of the Code volume comes from the implicit use of the method call stack-data structures such as explicit declarations and operation Stacks will certainly reduce the amount of code. But there is no doubt that Recursive Implementation requires a higher level of thinking. First, you need to have a global understanding of solving the problem and know how to break it down into subproblems. Second, we also need to have good programming capabilities to know how to deal with various boundary judgment and termination conditions in the recursive process.

Bytes -----------------------------------------------------------------------------------------------------------

Please leave a message if you have any questions or suggestions. Thank you very much!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.