알고리즘 검토-이진 트리에 대한 기본 지식

이진 트리의 다양한 순회

재귀 순회

중순 순회 : 중순 순회 순서는 왼쪽 루트 오른쪽이며 재귀 쓰기 방법은 비교적 간단합니다. 템플릿을 직접 붙여 넣기

public void inorder(TreeNode root){
    
    
    if(root == null)	return;
    if(root.left != null) inorder(root.left);
    //当前位置即为操作,可以根据题意进行添加
    if(root.right != null)	inorder(root.right);
}

예를 들어, lettcode94는 순회 순회 방법을 사용하여 이진 트리를 순회하지만 목록을 다시 넣어야하는 경우 위의 코드 주석에 논리의이 부분을 추가 할 수 있습니다.

public List<Integer> inorderTraversal(TreeNode root) {
    
    
    List<Integer> res = new ArrayList<Integer>();
    inorder(root, res);
    return res;
}
public void inorder(TreeNode root, List<Integer> res){
    
    
    if(root == null)    return;
    if(root.left != null)   inorder(root.left, res);
    res.add(root.val);
    if(root.right != null)  inorder(root.right, res);
}

그런 다음 다른 두 가지를 직접 인용하고, 사전 주문 순회 및 주문 후 순회 작성을 작성하고, 작업의 논리 코드를 다른 위치에 배치하기 만하면됩니다.

//先序遍历
public List<Integer> preorderTraversal(TreeNode root) {
    
    
    List<Integer> res = new ArrayList<Integer>();
    inorder(root, res);
    return res;
}
public void preorder(TreeNode root, List<Integer> res){
    
    
    if(root == null)    return;
    res.add(root.val);
    if(root.left != null)   inorder(root.left, res);
    if(root.right != null)  inorder(root.right, res);
}
//后序遍历
public List<Integer> postorderTraversal(TreeNode root) {
    
    
    List<Integer> res = new ArrayList<Integer>();
    inorder(root, res);
    return res;
}
public void postorder(TreeNode root, List<Integer> res){
    
    
    if(root == null)    return;
    if(root.left != null)   inorder(root.left, res);
    if(root.right != null)  inorder(root.right, res);
    res.add(root.val);
}

비 재귀 순회

재귀 순회의 장점은 코드가 간결하다는 것입니다.하지만 이진 트리에 노드가 더 많으면 StackOverFlow를 버스트하기가 쉽기 때문에 일반적으로 이진 트리의 비재 귀적 쓰기도 작성합니다. 일반적으로 재귀 적 쓰기는 비재 귀적 쓰기로 변경됩니다., 데이터 구조 스택을 사용해야합니다. 스택은 선입 선출이 특징입니다. 순차 순회의 경우 왼쪽 노드가 먼저 순회되므로 모든 왼쪽 노드 먼저 스택에 추가 한 다음 마지막 왼쪽 노드에 추가 할 수 있습니다. 값을 얻기 위해 꺼내고 오른쪽 노드를 꺼내고 반복합니다.

public List<Integer> inorder(TreeNode root){
    
    
    List<Integer> res = new ArrayList<>();
    Deque<TreeNode> stack = new LinkedList<>();
    while(root != null && !stack.isEmpty()){
    
    
		while(root != null){
    
    
            stack.push(root);
            root = root.left;
        }
        root = stack.pop();
        res.add(root.val);
        root = root.right;
    }
    return res;
}

중차 순회를 기반으로 1 차 순회를 작성하여 직접 작성할 수 있습니다.

public List<Integer> perorder(TreeNode root){
    
    
    List<Integer> res = new ArrayList<>();
    Deque<TreeNode> stack = new LinkedList<>();
    while(root != null || !stack.isEmpty()){
    
    
        while(root != null){
    
    
            res.add(root.val);
            stack.push(root);
            root = root.left;
        }
        root = stack.pop();
        root = root.right;
    }
    return res;
}

주문 후 순회는 어떻습니까? post-order traversal을 수행 할 때 현재 노드에 왼쪽과 오른쪽 노드가 있는지 확인해야하는데, 이는 더 번거 롭습니다. 1 차 순회는 왼쪽과 오른쪽 루트이고 포스트이기 때문에 여기서 더 유연한 방법이 사용됩니다. -order traversal은 왼쪽과 오른쪽 루트이고 원래 이진 트리의 왼쪽과 오른쪽 자식입니다. 트리가 반전 된 다음 사전 주문 탐색의 결과를 반전하여 주문 후 탐색의 결과를 얻을 수 있습니다. 예를 들면

원래 나무 :

      1
    /    \
   2      3
  /  \    /  \
 7   6   4	  5

주문 후 순회 : 7 6 2 4 5 3 1

왼쪽 및 오른쪽 노드 반전

      1
    /    \
   3      2
  /  \   /  \ 
 5    4 6	 7

선주문 순회 : 1 3 5 4 2 6 7

사전 주문 순회를 되 돌리면 다음을 얻을 수 있습니다. 7 6 2 4 5 3 1은 원래 트리의 주문 후 순회이며 왼쪽 및 오른쪽 하위 트리를 뒤집는 것은 비교적 간단하며 수행 할 수 있습니다. 오른쪽 하위 트리 만 통과하면됩니다. 먼저 사전 주문 순회 중, 다음 왼쪽 하위 트리를 순회합니다.

public List<Integer> postorder(TreeNode root){
    
    
    List<Integer> res = new ArrayList<>();
    Deque<TreeNode> stack = new LinkedList<>();
    while(root != null || !stack.isEmpty()){
    
    
        while(root != null){
    
    
            res.add(root.val);
            stack.push(root);
            root = root.right;
        }
        root = stack.pop();
        root = root.left;
    }
    Collections.reverse(res);
    return res;
}

레벨 순회

계층 적 순회는 위에서 아래로, 왼쪽에서 오른쪽으로, BFD, 너비 우선 알고리즘이 일반적으로 사용될 수 있으며이 알고리즘을 구현하려면 데이터 구조 대기열의 도움이 필요합니다. 대기열의 장점은 첫 번째입니다. -인, 선출.

대기열을 사용하는 이유는 무엇입니까?

    3
   / \
  9  20
    /  \
   15   7

반환:

[3,9,20,15,7]

위의 예에 따르면 노드의 가능한 왼쪽 및 오른쪽 하위 트리 노드를 얻기 위해 각 노드를 저장해야 함을 알 수 있습니다. 인쇄가 왼쪽에서 오른쪽으로 진행되는 경우 선입 선출 대기열을 사용하면이를 충족 할 수 있습니다. 특색

public int[] levelOrder(TreeNode root) {
    
    
    List<Integer> res = new ArrayList<>();
    Deque<TreeNode> q = new LinkedList<>();
    if(root == null)    return new int[]{
    
    };
    q.add(root);
    while(!q.isEmpty()){
    
    
        TreeNode temp = q.poll();                
        res.add(temp.val);
        if(temp.left != null) q.add(temp.left);
        if(temp.right != null) q.add(temp.right);
    }
    int[] r = new int[res.size()];
    for(int i = 0;i < res.size();i++){
    
    
        r[i] = res.get(i);
    }
    return r;
}

다음은 스택 및 대기열로서 Deque의 몇 가지 일반적인 방법에 대한 간략한 검토입니다.

作为栈

방법 효과
푸시 (e) 불쑥 나가다
팝() 팝업
몰래 엿보다() 스택의 맨 위 요소 가져 오기

作为队列

방법 효과
제안 (e) 팀장에 합류
투표() 줄 끝에서 제외
몰래 엿보다() 줄 끝에서

운동

Sword는 offer32 질문 1을 나타냅니다.

이진 트리는 위에서 아래로 레이어로 인쇄되고 동일한 레이어의 노드는 왼쪽에서 오른쪽으로 인쇄되며 각 레이어는 한 줄로 인쇄됩니다.

例如:
给定二叉树: [3,9,20,null,null,15,7],
	3	
   / \
  9  20
    /  \
   15   7
返回其层次遍历结果:

[
  [3],
  [9,20],
  [15,7]
]

이 문제는 레벨 순회와 달리 각 레이어의 데이터를 라인으로 인쇄해야하는데, 이는 각 라인의 데이터가 대기열에 저장되어 각주기 동안 현재 대기열에 대한 작업이 필요하기 때문입니다. 먼저 대기열의 크기를 기억 한 다음 자식 노드를 추가합니다.

public List<List<Integer>> levelOrder(TreeNode root) {
    
    
    List<List<Integer>> res = new ArrayList<>();
    Deque<TreeNode> q = new LinkedList<>();

    if(root == null)    return new ArrayList<List<Integer>>();
    q.offer(root);
    while(!q.isEmpty()){
    
    
        List<Integer> tempA = new ArrayList<>();
        //这行代码很重要,需要记住当前层的队列数量
        // for(int i = queue.size(); i > 0; i--)
        int size = q.size();
        for(int i = 0;i < size;i++){
    
    
            TreeNode temp = q.poll();
            if(temp.left != null) q.offer(temp.left);
            if(temp.right != null) q.offer(temp.right);
            tempA.add(temp.val);
        }
        res.add(tempA);
    }
    return res;
}

Sword는 offer32 질문 2를 나타냅니다.

이진 트리를 지그재그 순서로 인쇄하는 기능을 구현하십시오. 즉, 첫 번째 줄은 왼쪽에서 오른쪽으로, 두 번째 레이어는 오른쪽에서 왼쪽으로, 세 번째 줄은 왼쪽에서 오른쪽으로 인쇄합니다. 기타 등등 의 위에.

    3
   / \
  9  20
    /  \
   15   7
返回其层次遍历结果:

[
  [3],
  [20,9],
  [15,7]
]

이것은 위의 문제의 변형입니다. 짝수 행이있을 때만 데이터를 반전하면됩니다.

public List<List<Integer>> levelOrder(TreeNode root) {
    
    
        List<List<Integer>> res = new ArrayList<>();
        Deque<TreeNode> q = new LinkedList<>();
        if(root == null) return new ArrayList<>();
        int flag = 1;
        q.offer(root);
        while(!q.isEmpty()){
    
    
            List<Integer> temp = new ArrayList<>();
            for(int i = q.size();i >0 ;i--){
    
    
                TreeNode node = q.poll();
                temp.add(node.val);
                if(node.left != null)   q.offer(node.left);
                if(node.right != null)  q.offer(node.right);
            }
            if(flag % 2 == 0){
    
    
                Collections.reverse(temp);
            }
            res.add(temp);
            flag++;
        } 
        return res;
    }

추천

출처blog.csdn.net/weixin_44706647/article/details/115190793