이진 트리의 다양한 순회
재귀 순회
중순 순회 : 중순 순회 순서는 왼쪽 루트 오른쪽이며 재귀 쓰기 방법은 비교적 간단합니다. 템플릿을 직접 붙여 넣기
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;
}