Flip (reverse order) algorithm summary

overview

There are quite a lot of topic types for flipping algorithms, such as array flipping, string flipping, linked list flipping, binary tree flipping, etc. Although some topics are not called flipping, they are of similar types, such as outputting a set of numbers in reverse order.

analyze

What are the implementations of the flipping algorithm?

Method 1: stack

In the data structure, there is a natural auxiliary tool for realizing reverse order- stack . The first-in-last-out feature of the stack can handle the vast majority of flipping questions. Here's an example:
print a linked list from end to end

private static void printReverseSingleNode(SingleNode head){
    
    
        Stack<Integer> stack = new Stack<>();
        while (head != null){
    
    
            stack.push(head.data);
            head = head.next;
        }
        while (!stack.empty()){
    
    
            System.out.print(stack.pop());
        }
    }

It should be noted here that I only store the values ​​of the linked list nodes in the stack, and do not store the linked list nodes, which is also the easiest to implement. If you use the stack to reverse the linked list, the loss outweighs the gain, because a new linked list will be created at the end, the space complexity is high, and it will be more troublesome to implement.

Method 2: Recursion

Regarding the learning of recursion, I have always had a misunderstanding before, that is, I want to understand every detail of recursive execution, and then I finally found that when you delve into the details, you will be confused (somewhat similar to the uncertainty principle of quantum mechanics. ..) Therefore, as long as we encounter recursion, we abstract it into a recursive formula, without thinking about the layer-by-layer call relationship, and don't try to use the human brain to decompose each step of the recursion. The use of recursive algorithms on binary trees is very common. Here is an example of binary trees:
flipping binary trees

/**
     * 递归版本,其实就是左右交换
     * 由于树中每一个节点都需要被访问,因此时间复杂度就是O(n),其中n 是节点个数
     * 本方法使用了递归,在最坏情况下栈内存需要存放O(h)个方法调用,其中h是树的高度,由于h属于O(n),可得空间复杂度是O(n)
     */
    private static TreeNode reverseTree1(TreeNode root){
    
    
        if (root == null){
    
    
            return null;
        }
        TreeNode temp = root.leftChild;
        root.leftChild = reverseTree1(root.rightChild);
        root.rightChild = reverseTree1(temp);
        return root;
    }

Method 3: Iteration

The method of double pointer is used here. Sometimes the interviewer may not let you use recursion to implement an algorithm (for example, the traversal of the front, middle and back of the binary tree, it is too simple to write using recursion), or you do not have a deep understanding of the recursion idea (it is best Still have to go deep, recursion is very important), then at this time, using iterative methods to deal with problems is not so convoluted compared to the previous two methods. For example:
reversing a string

 /**
     * 反转方法,使用双指针法
     * @param s
     */
    private static void reverseString(char[] s) {
    
    
        int left = 0;
        int right = s.length -1;
        char temp;
        int len = s.length;
        for (int i = 0; i < len; i++) {
    
    
            if (left < right){
    
    
                temp = s[left];
                s[left] = s[right];
                s[right] = temp;
                left++;
                right--;
            }
        }
    }

Use double pointers, one pointing to the head of the array and one pointing to the tail of the array (this idea will also be used in binary search), and then both parties start calling. Called N/2. The time complexity is O(n), and the space complexity is O(1).
Of course, this double pointer looks a bit simple, let's take a look at a slightly more complicated one.
Flip the singly linked list , first look at the code:

 */
    public static SingleNode reverseSingleNode2(SingleNode head){
    
    
        SingleNode current = head;
        SingleNode pre = null;
        SingleNode next;
        while (current != null) {
    
    
            // 取出 next
            next = current.next;
            // 将上一个赋值给 next
            current.next = pre;
            // 更改 上一个到当前位置
            pre = current;
            // 当前位置往下移动
            current = next;
        }
        return pre;
    }

Flipping the single-linked list is more complicated than flipping the one that looks bigger than the string. I also debugged step by step, and then I figured it out after careful thinking (I am a bit stupid, so I used a stupid way). To understand, you can put the third pointer next Understand it as a temp, which is convenient for the linked list to move backward. Take one node at a time, and then add it to the front of the previous record node. The key is the understanding of current.next = pre; and pre = current;.

In fact, the way of using the stack is also a kind of iteration. Here is an example of using a queue as an auxiliary tool to realize flipping a binary tree

/**
     * 非递归版本,即使用迭代法
     * 创建一个队列来存储所有的左孩子和右孩子还没有被交换过的节点,开始的时候仅根节点在队列中,只要队列不为空,
     * 就一直从队列中取出节点,然后交换这个节点的左右孩子节点,接着再把孩子节点放入队列中,对于其中的空节点不用加到队列中,
     * 因为最后队列一定为空,这个时候所有的孩子节点已经被交换过了,所以最后再返回根节点即可。
     */

    private static TreeNode reverseTree2(TreeNode root){
    
    
        if (root == null){
    
    
            return null;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()){
    
    
            TreeNode current = queue.poll();
            TreeNode tmp = current.leftChild;
            current.leftChild = current.rightChild;
            current.rightChild = tmp;
            if (current.leftChild != null) queue.add(current.leftChild);
            if (current.rightChild != null) queue.add(current.rightChild);
        }

        return root;
    }

The iterative algorithm implements flipping, roughly divided into two steps. For non-linear data structure data (such as binary tree), you need an auxiliary tool to complete the data flipping, and then use the exchange algorithm. For linear structure data, just use two pointers directly.

Guess you like

Origin blog.csdn.net/qq_26439323/article/details/107189525