이진 트리의 순회

재귀 순회

재귀 알고리즘의 세 가지 요소를 결정하는 데 도움이 됩니다. 재귀를 작성할 때마다 이 세 가지 요소에 따라 작성하십시오. 그러면 모든 사람이 올바른 재귀 알고리즘을 작성할 수 있습니다!

  1. 재귀 함수의 매개 변수 및 반환 값 결정: 재귀 프로세스 중에 처리해야 할 매개 변수를 결정한 다음 이 매개 변수를 재귀 함수에 추가하고 각 재귀의 반환 값이 무엇인지 지정하여 반환 유형을 결정합니다. 재귀 함수의 .
  2. 종료 조건 결정: 재귀 알고리즘을 작성한 후 실행할 때 종종 스택 오버플로 오류가 발생합니다.즉, 종료 조건이 작성되지 않았거나 종료 조건이 잘못 작성되었습니다.운영 체제는 스택 구조를 사용하여 재귀의 각 계층의 정보를 저장하십시오. 재귀가 종료되지 않으면 운영 체제의 메모리 스택이 필연적으로 오버플로됩니다.
  3. 단일 수준 재귀의 논리 결정: 각 수준의 재귀에 대해 처리해야 하는 정보를 결정합니다. 여기서 재귀를 이루기 위해 자신을 반복적으로 호출하는 과정도 반복될 것이다.

선주문 순회

class Solution {
    
    
public:
    void traversal(TreeNode* cur, vector<int>& vec) {
    
    
        if (cur == NULL) return;
        vec.push_back(cur->val);    // 中
        traversal(cur->left, vec);  // 左
        traversal(cur->right, vec); // 右
    }
    vector<int> preorderTraversal(TreeNode* root) {
    
    
        vector<int> result;
        traversal(root, result);
        return result;
    }
};

중위 순회

class Solution {
    
    
public:
    void traversal(TreeNode* cur, vector<int>& vec) {
    
    
         if (cur == NULL) return;
	     traversal(cur->left, vec);  // 左
	     vec.push_back(cur->val);    // 中
	     traversal(cur->right, vec); // 右
    }
    vector<int> postorderTraversal(TreeNode* root) {
    
    
        vector<int> result;
        traversal(root, result);
        return result;
    }
};

사후 순회

class Solution {
    
    
public:
    void traversal(TreeNode* cur, vector<int>& vec) {
    
    
         if (cur == NULL) return;
   		 traversal(cur->left, vec);  // 左
  	     traversal(cur->right, vec); // 右
         vec.push_back(cur->val);    // 中
    }
    vector<int> postorderTraversal(TreeNode* root) {
    
    
        vector<int> result;
        traversal(root, result);
        return result;
    }
};

시퀀스 순회

class Solution {
    
    
public:
    void order(TreeNode* cur, vector<vector<int>>& result, int depth)
    {
    
    
        if (cur == nullptr) return;
        if (result.size() == depth) result.push_back(vector<int>());
        result[depth].push_back(cur->val);
        order(cur->left, result, depth + 1);
        order(cur->right, result, depth + 1);
    }
    vector<vector<int>> levelOrder(TreeNode* root) {
    
    
        vector<vector<int>> result;
        int depth = 0;
        order(root, result, depth);
        return result;
    }
};

반복하다

재귀의 구현은 다음과 같습니다. 각 재귀 호출은 로컬 변수, 매개 변수 값 및 함수의 반환 주소를 호출 스택으로 푸시한 다음 재귀가 반환될 때 이전 재귀의 매개 변수가 맨 위에서 팝됩니다. 스택이므로 이것이 재귀입니다. 이전 위치로 돌아갈 수 있는 이유입니다.

이때 스택을 사용하여 이진 트리의 앞뒤 순회를 실현할 수도 있다는 것을 모두가 알아야 합니다.

선주문 순회(반복)

선순 순회는 중간 및 왼쪽이며 중간 노드가 먼저 처리될 때마다 루트 노드가 먼저 스택에 들어간 다음 오른쪽 자식이 스택에 추가된 다음 왼쪽 자식이 추가됩니다.

lass Solution {
    
    
public:
    vector<int> preorderTraversal(TreeNode* root) {
    
    
        stack<TreeNode*> st;
        vector<int> result;
        if (root == NULL) return result;
        st.push(root);
        while (!st.empty()) {
    
    
            TreeNode* node = st.top();                     // 中
            st.pop();
            result.push_back(node->val);
            if (node->right) st.push(node->right);         // 右(空节点不入栈)
            if (node->left) st.push(node->left);           // 左(空节点不入栈)
        }
        return result;
    }

중위 순회

in-order traversal은 왼쪽, 중간, 오른쪽으로 먼저 이진 트리의 상단에 있는 노드에 접근한 다음 트리의 왼쪽 하단에 도달할 때까지 계층별로 접근한 다음 시작한다. 노드를 처리하기 위해(즉, 노드의 값을 결과 배열에 넣음) 이로 인해 처리 순서와 액세스 순서가 일치하지 않게 됩니다.

그런 다음 반복적 방법을 사용하여 순서대로 순회를 작성할 때 노드 액세스를 돕기 위해 포인터 순회를 빌릴 필요가 있으며 스택은 노드의 요소를 처리하는 데 사용됩니다 .

class Solution {
    
    
public:
    vector<int> inorderTraversal(TreeNode* root) {
    
    
        vector<int> result;
        stack<TreeNode*> st;
        TreeNode* cur = root;
        while (cur != NULL || !st.empty()) {
    
    
            if (cur != NULL) {
    
     // 指针来访问节点,访问到最底层
                st.push(cur); // 将访问的节点放进栈
                cur = cur->left;                // 左
            } else {
    
    
                cur = st.top();
                 // 从栈里弹出的数据,就是要处理的数据(放进result数组里的数据)
                st.pop();
                result.push_back(cur->val);     // 中
                cur = cur->right;               // 右
            }
        }
        return result;
    }
};

사후 순회

다시 post-order traversal을 보자. pre-order traversal은 왼쪽과 오른쪽에 있고, 후속 traversal은 왼쪽과 오른쪽에 있다. 그러면 pre-order traversal의 코드 순서만 조정하면 된다. 중간, 오른쪽 및 왼쪽의 순회 순서, 그리고 결과 배열 및 출력을 뒤집습니다. 결과 시퀀스는 아래 그림과 같이 오른쪽 및 왼쪽입니다.
여기에 이미지 설명 삽입

따라서 post-order traversal은 pre-order traversal의 코드를 약간만 수정하면 됩니다. 코드는 다음과 같습니다.

class Solution {
    
    
public:
    vector<int> postorderTraversal(TreeNode* root) {
    
    
        stack<TreeNode*> st;
        vector<int> result;
        if (root == NULL) return result;
        st.push(root);
        while (!st.empty()) {
    
    
            TreeNode* node = st.top();
            st.pop();
            result.push_back(node->val);
            if (node->left) st.push(node->left); 
            // 相对于前序遍历,这更改一下入栈顺序 (空节点不入栈)
            if (node->right) st.push(node->right); // 空节点不入栈
        }
        reverse(result.begin(), result.end()); // 将结果反转之后就是左右中的顺序了
        return result;
    }
};

시퀀스 순회

보조 데이터 구조, 즉 큐를 빌려야 하는데 큐는 선입선출(First-In-First-Out) 방식으로 층별 순회 논리를 따른다.대신 스택 선입선출 방식을 사용한다. -out은 재귀인 깊이 우선 순회 논리를 시뮬레이션하는 데 적합합니다.

그리고 이 계층 순서 순회 방법은 그래프 이론에서 너비 우선 순회이지만 이진 트리에 적용합니다.

class Solution {
    
    
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
    
    
        queue<TreeNode*> que;
        if (root != NULL) que.push(root);
        vector<vector<int>> result;
        while (!que.empty()) {
    
    
            int size = que.size();
            vector<int> vec;
        // 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的
            for (int i = 0; i < size; i++) {
    
    
                TreeNode* node = que.front();
                que.pop();
                vec.push_back(node->val);
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
            result.push_back(vec);
        }
        return result;
    }
};

추천

출처blog.csdn.net/weixin_54792212/article/details/125356542