이진 트리 요약의 비재 귀적 1 차 순회 (3 가지 방법)

이진 트리 요약의 알고리즘 비 재귀 1 차 순회 (3 가지 방법)

@ 저자 : Jingdai
@date : 2020.12.03

이진 트리의 재귀 적 1 차 순회는 매우 간단하지만 인터뷰 중에 면접관은 종종 비재 귀적 방법을 작성하도록 요청합니다. 여기에 요약이 있습니다.

방법 1

가장 간단한 방법을 먼저 작성하십시오. 먼저 큐를 사용하여 트리 순회를 완료하여 이진 트리를 계층 적으로 순회하는 코드를 기억하십시오. 다음 코드.

public static void levelOrderTraverse(TreeNode root) {
    
    
    if (root == null)
        return;

    LinkedList<TreeNode> queue = new LinkedList<>();
    queue.offer(root);
    while (queue.size() != 0) {
    
    
        TreeNode cur = queue.poll();
        System.out.println(cur.val);
        if (cur.left != null)
            queue.offer(cur.left);
        if (cur.right != null)
            queue.offer(cur.right);
    }
}

위 코드의 대기열이 스택으로 바뀌면 어떻게됩니까? 트리의 순회는 여전히 완료되지만 순회 순서는 약간 이상합니다. 노드의 경우 먼저 자신을 순회 한 다음 오른쪽 하위 트리를 순회 한 다음 왼쪽 하위 트리를 순회합니다. 선주문 순회 순서는 무엇입니까? 먼저 자신을 탐색 한 다음 왼쪽 하위 트리를 탐색 한 다음 오른쪽 하위 트리를 탐색하여 왼쪽 및 오른쪽 하위 트리의 스택 순서를 변경할 수 있으며 결과는 다음 코드가됩니다. 본질적으로 큐를 스택으로 바꾸는 것은 bfs에서 dfs로 변경됩니다.

public static void preOrderTraverse(TreeNode root) {
    
    
    if (root == null)
        return;

    LinkedList<TreeNode> stack = new LinkedList<>();
    stack.push(root);
    while (stack.size() != 0) {
    
    
        TreeNode cur = stack.pop();
        System.out.println(cur.val);
        if (cur.right != null)
            stack.push(cur.right);
        if (cur.left != null)
            stack.push(cur.left);
    }
}

또한 왼쪽 및 오른쪽 하위 트리의 경우 왼쪽 하위 트리를 먼저 통과 한 다음 오른쪽 하위 트리를 통과해야합니다. 스택은 후입 선출입니다. 스택에서 벗어날 때 통과합니다. , 그래서 우리는 먼저 횡단 한 다음 스택에 들어갑니다. 왼쪽 하위 트리.

방법 2

다음은 일반 데이터 구조 책에서 소개 한 방법 인 약간 더 복잡한 방법을 소개합니다.

첫 번째 순서는 첫 번째 만남의 순회이고, 중간 순서는 두 번째 만남의 순회이며, 사후 순서는 세 번째 만남의 순회입니다. 코드를 살펴보십시오.

public static void preOrderTraverse(TreeNode root) {
    
    

    TreeNode p = root;
    LinkedList<TreeNode> stack = new LinkedList<>();
    while (p != null || stack.size() != 0) {
    
    
        while (p != null) {
    
    
            System.out.println(p.val);
            stack.push(p);
            p = p.left;
        }
        p = stack.pop();
        p = p.right;
    }
}

각 노드에 대해 만날 때 순회 된 다음 왼쪽 하위 트리가 순회됩니다. 노드에 왼쪽 하위 트리가 없으면 p가 오른쪽 하위 트리를 가리 키도록 요소를 팝한 다음 동일한 방식으로 오른쪽 하위 트리를 탐색합니다.

3 가지 방법 중 3 : Morris 방법

위 방법의 시간 복잡도는 모두 O(n)이고 공간 복잡도는 O(h)재귀 방법은 동일하지만 시스템의 스택 공간을 사용한다는 것입니다. Morris는 복잡한 O(1)접근을 위한 공간 인이 문제에 대응합니다 .

먼저 Morris 순회 과정을 살펴 보겠습니다. (주오 쉔의 생각 참조) 이것은 선주문이 아니고,이 순회를 기반으로 선주문이 조금 변경된 후 선주문이됩니다.

현재 순회중인 노드의 cur경우 다음 두 가지 상황이 있습니다.

  1. 경우 cur에는 왼쪽 하위 트리가없는, 바로 cur오른쪽 서브 트리로 이동 : cur = cur.right.

  2. cur왼쪽 하위 트리가 왼쪽 하위 트리의 오른쪽 노드를 찾을 때 rightmost:

    • 경우 rightmost.right == null, 여기에 대한 설명입니다 rightmost.rightcur, 다음 cur의 왼쪽 서브 트리로 이동은 :

      rightmost.right = cur; cur = cur.left;

    • 경우 rightmost.right == cur본원에 기재된 두 번째 rightmost.right포인트 복구는 널 (NULL)이다 cur오른쪽 서브 트리로 이동 :

      rightmost.right = null; cur = cur.right;

cur가리키면 null루프가 종료됩니다.

최종 코드는 다음과 같습니다.

public static void morrisOrderTraverse(TreeNode root) {
    
    

    TreeNode cur = root;
    TreeNode rightmost = null;
    while (cur != null) {
    
    
        if (cur.left != null) {
    
    
            rightmost = cur.left;
            while (rightmost.right != null && rightmost.right != cur) {
    
    
                rightmost = rightmost.right;
            }
            // the first time to come here
            if (rightmost.right == null) {
    
    
                rightmost.right = cur;
                System.out.println(cur.val);
                cur = cur.left;
            } else {
    
     // seconde time to come here
                rightmost.right = null;
                System.out.println(cur.val);
                cur = cur.right;
            }
        } else {
    
    
            System.out.println(cur.val);
            cur = cur.right;
        }
    }
}

모리스 주문의 순서인데, 선주문으로 변경하는 방법은? 선주문은 처음 만날 때 순회하는 것입니다. Morris 순회에서 노드에 왼쪽 자식이 있으면 두 번 순회합니다 (처음에는 왼쪽 자식의 맨 오른쪽 노드가 null을 가리키고 두 번째는 맨 오른쪽 노드를 가리 킵니다). 왼쪽 자식의이 노드를 가리 킵니다. 자식에 왼쪽 자식이 없으면 한 번만 순회됩니다 (null은 순회하지 않기 때문에 null을 앞뒤로 계산하는 것도 두 번입니다). 그래서 우리가 처음으로 노드를 만나는 한 순회 할 수 있습니다. 즉, 노드에 왼쪽 자식이 있으면 첫 번째 만남에서 순회되고, 노드에 왼쪽 자식이 없으면 직접 순회됩니다. 마지막으로 Morris의 선주문 순회 코드는 다음과 같습니다.

public static void preOrderTraverse(TreeNode root) {
    
    

    TreeNode cur = root;
    TreeNode rightmost = null;
    while (cur != null) {
    
    
        if (cur.left != null) {
    
    
            rightmost = cur.left;
            while (rightmost.right != null && rightmost.right != cur) {
    
    
                rightmost = rightmost.right;
            }
            // the first time to come here
            if (rightmost.right == null) {
    
    
                rightmost.right = cur;
                System.out.println(cur.val);
                cur = cur.left;
            } else {
    
     // the second time
                rightmost.right = null;
                cur = cur.right;
            }
        } else {
    
    
            System.out.println(cur.val);
            cur = cur.right;
        }
    }
}

같은 방식으로 Morris 메서드를 기반으로 한 순차 순회도 작성하기 쉽습니다. 즉, 노드가 두 번째로 만날 때 순회됩니다. 노드에 왼쪽 하위 트리가 없으면 직접 순회되고, 노드에 왼쪽 하위 트리가 있으면 두 번째로 발견 될 때 다시 순회됩니다.

테스트 코드

테스트를 용이하게하기 위해 여기에서 코드를 사용하여 트리를 만들고 테스트 할 수 있습니다.

public static void main(String[] args) {
    
    

    // create tree
    TreeNode root = new TreeNode(1);
    TreeNode node2 = new TreeNode(2);
    TreeNode node3 = new TreeNode(3);
    TreeNode node4 = new TreeNode(4);
    TreeNode node5 = new TreeNode(5);
    TreeNode node6 = new TreeNode(6);
    TreeNode node7 = new TreeNode(7);
    root.left = node2;
    root.right = node3;
    node2.left = node4;
    node2.right = node5;
    node3.left = node6;
    node3.right = node7;

    preOrderTraverse(root);
}
class TreeNode {
    
    
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) {
    
     val = x; }
}

추천

출처blog.csdn.net/qq_41512783/article/details/110524693