A tree data structure can be defined recursively (locally) as a collection of nodes (starting at a root node), where each node is a data structure consisting of a value, together with a list of references to nodes (the "children"), with the constraints that no reference is duplicated, and none points to the root.
A tree data structure can be defined recursively (locally) as a collection of nodes (starting at a root node), where each node is a data structure consisting of a value, together with a list of references to nodes (the "children"), with the constraints that no reference is duplicated, and none points to the root.
A data structure made up of nodes or vertices and directed edges without having any cycle.
Tree
B Tree
Trie Tree
Binary Tree
BST
RB Tree
AVL
A
B
C
D
E
F
G
J
I
H
A
B
C
D
E
F
G
J
I
H
The depth of a node is the number of edges from the node to the tree's root node.
A root node will have a depth of 0.
The height of a node is the number of edges on the longest path from the node to a leaf.
A leaf node will have a height of 0.
The height of a tree would be the height of its root node,
or equivalently, the depth of its deepest node.
Note that depth doesn't make sense for a tree.
what is the max number of nodes a tree can have if the height of the tree is h?
class TreeNode {
int val;
TreeNode left;
TreeNode right;
public TreeNode(int val_) {
val = val_;
left = null;
right = null;
}
}class Solution {
// Please implement this function.
public void function(TreeNode root);
}Traversal is the basic operation for almost all binary tree problems.
Traversal is the basic operation for almost all binary tree problems.
Parent Node
A
B
D
E
H
I
J
G
F
C
LevelOrder: A B C D E F G H I J
A
B
D
E
H
I
J
G
F
C
PreOrder: A B D E H I C F G J
InOrder: D B H E I A F C G J
PostOrder: D H I E B F J G C A
Tree is a recursive data structure.
public void traverse(ListNode head) {
visit(head);
traverse(head.next);
}public void traverse(TreeNode root) {
visit(root);
traverse(root.left);
traverse(root.right);
}public void preorder(TreeNode root) {
if(root != null) {
// Visit the node by printing the node data
System.out.printf("%d ",root.val);
preorder(root.left);
preorder(root.right);
}
} public void inorder(TreeNode root) {
if(root != null) {
inorder(root.left);
System.out.printf("%d ",root.val);
inorder(root.right);
}
}public void postorder(TreeNode root) {
if(root != null) {
postorder(root.left);
postorder(root.right);
System.out.printf("%d ",root.val);
}
}We can't visit each node once we meet them. Need to store some node for future visit.
public void levelorder(TreeNode root) { // BFS
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode top = queue.pop();
System.out.printf("%d ", top.val);
if (top.left != null) {
queue.offer(top.left);
}
if (top.right != null) {
queue.offer(top.right);
}
}
return;
} public void levelorder(TreeNode root) { // BFS
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int level = 0;
while (!queue.isEmpty()) {
System.out.printf("Level %d\n", level++);
Queue<TreeNode> queue2 = new LinkedList<>();
while (!queue.isEmpty()) {
TreeNode top = queue.poll();
System.out.printf("%d ", top.val);
if (top.left != null) queue2.offer(top.left);
if (top.right != null) queue2.offer(top.right);
}
queue = queue2;
}
} public void levelorder(TreeNode root) { // BFS
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
queue.offer(null);
int level = 0;
while (!queue.isEmpty()) {
TreeNode top = queue.poll();
if (top == null) {
System.out.printf("----Level %d\n", level++);
queue.offer(null);
} else {
System.out.printf("%d ", top.val);
if (top.left != null) queue.offer(top.left);
if (top.right != null) queue.offer(top.right);
}
}
} public void levelorder(TreeNode root) { // BFS
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int level = 0;
int curLevelNum = 1;
while (!queue.isEmpty()) {
System.out.printf("Level %d\n", level++);
int nextLevelNum = 0;
while (curLevelNum-- != 0) {
TreeNode top = queue.poll();
System.out.printf("%d ", top.val);
if (top.left != null) {
queue.offer(top.left);
nextLevelNum++;
}
if (top.right != null) {
queue.offer(top.right);
nextLevelNum++;
}
}
curLevelNum = nextLevelNum;
}
} Given a binary tree, find its maximum depth.
The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.
Given a binary tree, find its maximum depth.
The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.
public int maxDepth(TreeNode root) {
if (root == null) return 0;
int leftDepth = maxDepth(root.left);
int rightDepth = maxDepth(root.right);
return Math.max(leftDepth, rightDepth) + 1;
}Given a binary tree, find its maximum depth.
The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.
public int maxDepth(TreeNode root) {
if (root == null) return 0;
return Math.max(maxDepth(root.left),
maxDepth(root.right)) + 1;
}Given a binary tree, find its minimum depth.
The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node.
Given a binary tree, find its minimum depth.
The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node.
public int minDepth(TreeNode root) {
if (root == null) return 0;
int leftDepth = minDepth(root.left);
int rightDepth = minDepth(root.right);
return Math.min(leftDepth, rightDepth) + 1;
}Wrong Answer
Given a binary tree, find its minimum depth.
The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node.
public int minDepth(TreeNode root) {
if (root == null) return 0;
int leftDepth = minDepth(root.left);
int rightDepth = minDepth(root.right);
return Math.min(leftDepth, rightDepth) + 1;
}Wrong Answer
A
B
C
Given a binary tree, find its minimum depth.
The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node.
public int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
if (root.left == null && root.right == null) {
return 1;
}
if (root.left == null && root.right != null) {
return minDepth(root.right) + 1;
}
if (root.left != null && root.right == null) {
return minDepth(root.left) + 1;
}
return Math.min(minDepth(root.left),
minDepth(root.right)) + 1;
}A
B
C
Given a binary tree, find its minimum depth.
The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node.
public int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
int leftDepth = minDepth(root.left);
int rightDepth = minDepth(root.right);
if (leftDepth == 0) {
return rightDepth + 1;
} else if (rightDepth == 0) {
return leftDepth + 1;
}
return Math.min(leftDepth, rightDepth) + 1;
}A
B
C
public int minDepth(TreeNode root) {
if (root == null) return 0;
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.offer(root);
int minDepth = 1;
while (!queue.isEmpty()) {
Queue<TreeNode> queue2 = new LinkedList<TreeNode>();
while (!queue.isEmpty()) {
TreeNode top = queue.poll();
if (top.left == null && top.right == null) {
return minDepth;
}
if (top.left != null) {
queue2.offer(top.left);
}
if (top.right != null) {
queue2.offer(top.right);
}
}
minDepth++;
queue = queue2;
}
return minDepth;
}Given two binary trees, write a function to check if they are equal or not.
Two binary trees are considered equal if they are structurally identical and the nodes have the same value.
Given two binary trees, write a function to check if they are equal or not.
Two binary trees are considered equal if they are structurally identical and the nodes have the same value.
Given two binary trees, write a function to check if they are equal or not.
Two binary trees are considered equal if they are structurally identical and the nodes have the same value.
public boolean isSameTree(TreeNode p, TreeNode q) {
if (p == null && q == null) {
return true;
} else if (p == null || q == null) {
return false;
}
return p.val == q.val
&& isSameTree(p.left, q.left)
&& isSameTree(p.right, q.right);
}Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center).
1 / \ 2 2 / \ / \ 3 4 4 3
1 / \ 2 2 / \ / \ 4 3 4 3
1 / \ 2 2 \ \ 3 3
1 / \ 2 2 / \ 3 3
True
False
False
True
Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center).
1 / \ 2 2 \ \ 3 3
1 / \ 2 2 / \ 3 3
False
True
Root is Symmetric
A is symmetric to B
public boolean isSymmetric(TreeNode root) {
if (root == null) {
return true;
}
return isSymmetric(root.left, root.right);
}
public boolean isSymmetric(TreeNode left, TreeNode right) {
if (left == null && right == null) {
return true;
}
if (left == null || right == null) {
return false;
}
return left.val == right.val
&& isSymmetric(left.left, right.right)
&& isSymmetric(left.right, right.left);
}Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum.
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
Sum: 22 --> True
(5 - 4 - 11 - 2)
Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum.
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum.
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
public boolean hasPathSum(TreeNode root, int sum) {
if (root == null) {
return false;
}
if (root.left == null && root.right == null) {
if (sum == root.val) {
return true;
}
return false;
}
return hasPathSum(root.left, sum - root.val) ||
hasPathSum(root.right, sum - root.val);
}Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the given sum.
public List<List<Integer>> pathSum(TreeNode root, int sum) {
List<List<Integer>> results = new ArrayList<>();
pathSum(results, new ArrayList<Integer>(), root, sum);
return results;
}
private void pathSum(List<List<Integer>> results,
List<Integer> path, TreeNode root, int sum){
if (root == null) {
return;
}
sum -= root.val;
if (root.left == null && root.right == null) {
if (sum == 0){
path.add(root.val);
results.add(new ArrayList<Integer>(path));
path.remove(path.size() - 1);
}
return;
}
path.add(root.val);
pathSum(results, path, root.left, sum);
pathSum(results, path, root.right, sum);
path.remove(path.size() - 1);
}30
18
13
24
22
27
47
40
31
34
30
18
13
24
22
27
47
40
31
34
public boolean find(int value, TreeNode root) {
TreeNode cur = root;
while(cur != null) {
if (val == cur.value) {
return true;
}
if (value < cur.val) {
cur = cur.leftNode;
} else {
cur = cur.rightNode;
}
}
return false;
}O(logN)
30
18
13
24
22
27
47
40
31
34
30
18
13
24
22
27
47
40
31
34
42
public boolean add(int value, TreeNode root) {
if (root == null) {
root = new TreeNode(value);
return true;
}
TreeNode cur = root;
while (cur != null) {
if (cur.val == value) {
return false;
}
if (value < cur.val) {
if (cur.leftNode != null) {
cur = cur.leftNode;
} else {
cur.leftNode = new TreeNode(value);
return true;
}
} else {
if (cur.rightNode != null) {
cur = node.rightNode;
} else {
cur.rightNode = new TreeNode(value);
return true;
}
}
}
return false;
}O(logN)
30
18
13
24
22
27
47
40
31
34
42
P
Q
R
P
Q
R
P
Q
R
P
Q
R
P<R<Q
P<Q<R
R<Q<P
Q<R<P
Just use R to replace Q
P
Q
R1
R2
If we remove Q, which is the best substitute?
P
Q
R1
R2
If we remove Q, which is the best substitute?
public static boolean remove(int value, Node root) {
if(root == null) return false;
if(root.data == value) {
root = removeNode(root);
return true;
}
Node node = root;
while(node != null) {
if(node.data > value) {
if(node.leftNode != null && node.leftNode.data != value) {
node = node.leftNode;
}
else if(node.leftNode == null) return false;
else {
node.leftNode = removeNode(node.leftNode);
return true;
}
}
else if(node.data < value) {
if(node.rightNode != null && node.rightNode.data != value) {
node = node.rightNode;
}
else if(node.rightNode == null) return false;
else {
node.rightNode = removeNode(node.rightNode);
return true;
}
}
else return false;
}
return false;
}public static Node removeNode(Node node) {
if(node.leftNode == null && node.rightNode == null) {
return null;
}
else if(node.leftNode == null) {
return node.rightNode;
}
else if(node.rightNode == null) {
return node.leftNode;
}
else {
node.data = findAndRemove(node);
return node;
}
}
public static int findAndRemove(Node node) {
int result;
if(node.leftNode.rightNode == null) {
result = node.leftNode.data;
node.leftNode = node.leftNode.leftNode;
return result;
}
node = node.leftNode;
while(node.rightNode.rightNode != null) {
node = node.rightNode;
}
result = node.rightNode.data;
node.rightNode = node.rightNode.leftNode;
return result;
}30
18
13
24
22
27
47
40
31
34
42
43
30
18
13
24
22
27
47
43
34
40
42
31
We know the search time is highly related to the height of the tree. If we keep add and remove elements in the tree, the tree will become unbalanced.
So we have Red-black tree and AVL tree, they could use rotation and reconstruct to make the tree balance.
Tree
B Tree
Trie Tree
Binary Tree
BST
RB Tree
AVL
A graph data structure consists of a finite (and possibly mutable) set of vertices or nodes or points, together with a set of unordered pairs of these vertices for an undirected graph or a set of ordered pairs for a directed graph.
These pairs are known as edges, arcs, or lines for an undirected graph and as arrows, directed edges, directed arcs, or directed lines for a directed graph.
class GraphNode {
int val;
List<GraphNode> neighbors;
}
| 1 | 1 | 1 | ||
|---|---|---|---|---|
| 1 | 1 | |||
| 1 | 1 | |||
| 1 | ||||
| 1 | 1 |
| 1 | 2 | 3 | 5 |
|---|
| 2 | 4 |
|---|
| 3 | 5 | 6 | 7 | 8 |
|---|
0:
1:
2: