Skip to content

Commit cefa79b

Browse files
committed
leetcode
1 parent f8a21e9 commit cefa79b

File tree

4 files changed

+353
-0
lines changed

4 files changed

+353
-0
lines changed
+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
3+
4+
-* Binary Tree Paths *-
5+
6+
7+
8+
Given the root of a binary tree, return all root-to-leaf paths in any order.
9+
10+
A leaf is a node with no children.
11+
12+
13+
14+
Example 1:
15+
16+
17+
Input: root = [1,2,3,null,5]
18+
Output: ["1->2->5","1->3"]
19+
Example 2:
20+
21+
Input: root = [1]
22+
Output: ["1"]
23+
24+
25+
Constraints:
26+
27+
The number of nodes in the tree is in the range [1, 100].
28+
-100 <= Node.val <= 100
29+
30+
*/
31+
32+
// Definition for a binary tree node.
33+
34+
class TreeNode {
35+
int val;
36+
TreeNode? left;
37+
TreeNode? right;
38+
TreeNode([this.val = 0, this.left, this.right]);
39+
}
40+
41+
/*
42+
43+
44+
*/
45+
46+
class A {
47+
// Runtime: 466 ms, faster than 50.00% of Dart online submissions for Binary Tree Paths.
48+
// Memory Usage: 142.6 MB, less than 50.00% of Dart online submissions for Binary Tree Paths.
49+
List<String> binaryTreePaths(TreeNode? root) {
50+
List<String> res = [];
51+
if (root == null) return res;
52+
dfs(root, res, "");
53+
return res;
54+
}
55+
56+
void dfs(TreeNode? root, List ls, String str) {
57+
if (root == null) return;
58+
str += (str.length == 0 ? "" : "->") + root.val.toString();
59+
if (root.left == null && root.right == null) {
60+
ls.add(str);
61+
return;
62+
}
63+
dfs(root.left, ls, str);
64+
dfs(root.right, ls, str);
65+
}
66+
}
67+
68+
class B {
69+
// wrong
70+
List binaryTreePaths(TreeNode? root) {
71+
List<String> res = [];
72+
StringBuffer sb = StringBuffer();
73+
if (root == null) return res;
74+
dfs(root, res, sb);
75+
return res;
76+
}
77+
78+
void dfs(TreeNode? root, List ls, StringBuffer str) {
79+
if (root == null) return;
80+
str.write((str.length == 0 ? "" : "->") + root.val.toString());
81+
if (root.left == null && root.right == null) {
82+
ls.add(str.toString());
83+
return;
84+
}
85+
dfs(root.left, ls, str);
86+
str.length;
87+
dfs(root.right, ls, str);
88+
str.length;
89+
}
90+
}
91+
92+
class C {
93+
List<String> binaryTreePaths(TreeNode? root) {
94+
List<String> res = [];
95+
if (root == null) {
96+
return res;
97+
}
98+
StringBuffer sb = StringBuffer();
99+
dfs(root, res, sb);
100+
return res;
101+
}
102+
103+
void dfs(TreeNode root, List<String> res, StringBuffer sb) {
104+
if (root.left == null && root.right == null) {
105+
sb.write("" + root.val.toString());
106+
res.add(sb.toString());
107+
return;
108+
}
109+
if (root.left != null) {
110+
String prev = sb.toString();
111+
sb.write(root.val.toString() + "->");
112+
dfs(root.left!, res, sb);
113+
sb = StringBuffer([prev]);
114+
}
115+
if (root.right != null) {
116+
sb.write(root.val.toString() + "->");
117+
dfs(root.right!, res, sb);
118+
}
119+
}
120+
}

BinaryTreePaths/binary_tree_paths.go

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package main
2+
3+
import (
4+
"strconv"
5+
"strings"
6+
)
7+
8+
// Definition for a binary tree node.
9+
type TreeNode struct {
10+
Val int
11+
Left *TreeNode
12+
Right *TreeNode
13+
}
14+
15+
type nodePath struct {
16+
node *TreeNode
17+
prevPath []int
18+
}
19+
20+
func binaryTreePaths(root *TreeNode) []string {
21+
res := make([]string, 0)
22+
23+
stack := make([]nodePath, 0)
24+
stack = append(stack, nodePath{root, []int{}})
25+
26+
var sb strings.Builder
27+
for len(stack) > 0 {
28+
cur := stack[len(stack)-1]
29+
stack = stack[:len(stack)-1]
30+
31+
if cur.node.Left == nil && cur.node.Right == nil {
32+
for _, n := range cur.prevPath {
33+
sb.WriteString(strconv.Itoa(n))
34+
sb.WriteString("->")
35+
}
36+
sb.WriteString(strconv.Itoa(cur.node.Val))
37+
res = append(res, sb.String())
38+
sb.Reset()
39+
}
40+
41+
if cur.node.Left != nil {
42+
stack = append(stack, nodePath{cur.node.Left, append(cur.prevPath, cur.node.Val)})
43+
}
44+
if cur.node.Right != nil {
45+
stack = append(stack, nodePath{cur.node.Right, append(cur.prevPath, cur.node.Val)})
46+
}
47+
}
48+
49+
return res
50+
}
51+
52+
// func binaryTreePaths(root *TreeNode) []string {
53+
// return binaryTreePathsWithString(root, "")
54+
// }
55+
56+
// func binaryTreePathsWithString(node *TreeNode, str string) []string {
57+
// if node == nil {
58+
// return []string{}
59+
// }
60+
61+
// if node.Left == nil && node.Right == nil {
62+
// str += fmt.Sprint(node.Val)
63+
// return []string{str}
64+
// }
65+
66+
// str += fmt.Sprintf("%v->", node.Val)
67+
68+
// return append(
69+
// binaryTreePathsWithString(node.Left, str),
70+
// binaryTreePathsWithString(node.Right, str)...,
71+
// )
72+
// }

BinaryTreePaths/binary_tree_paths.md

+160
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
# 🔥 Binary Tree Paths 🔥 || 3 Solutions || Simple Fast and Easy || with Explanation
2+
3+
## Time Complexity Analysis
4+
5+
### Case Study
6+
7+
First let's work on the balanced tree situation:
8+
9+
- It should be obvious to see now that each node will contribute to the total time cost an amount of length of the path from the root to this node. The problem is to see how to sum up these paths' lengths for N nodes altogether.
10+
11+
- Denote the time complexity for N nodes as T(N).
12+
13+
Suppose we do have that balanced tree now (and also N is 2^N-1 for simplicity of discussion). Amd we know that N/2 nodes lie at the leaf/deepest level of the BST since it's balanced binary tree.
14+
15+
We easily have this recurrence formula:
16+
T(N) = T(N/2) + (N/2) * lgN
17+
18+
- Which means, we have N nodes, with half lying on the deepest (the lgNth) level. The sum of path lengths for N nodes equals to sum of path lengths for all nodes except those on the lgN-th level plus the sum of path lengths for those nodes on the lgN-th level.
19+
20+
- This recurrence is not hard to solve. I did not try to work out the exact solution since the discussion above in itself are in essence a little blurry on corner cases, but it is easy to discover that T(N) = O(NlgN).
21+
22+
### To Generalize: Let's Start with Worst-Case
23+
24+
- The problem left here now, is a balanced tree the best-case or the worst-case? I was convinced it was the worst case before I doodled some tree up and found otherwise.
25+
26+
- The worst case is actually when all nodes lie up to a single line like a linked list, and the complexity in this case is easily calculable as O(N^2). But how do we prove that?
27+
28+
the proof is easier than you think. Just use induction. Suppose we have N - 1 nodes in a line, and by inductive hypothesis we claim that this tree is the max-path-sum tree for N-1 nodes. We just have to prove that the max-path-sum tree for N nodes is also a single line. How do you prove it? Well suppose that you see the N-1-node line here, and you want to add the N-th node, where would you put it? Of course the deepest level so that the new node gets maximum depth.
29+
30+
### Best-Case
31+
32+
Proving that the best case is the balanced tree can be a little trickier. By some definition, A tree where no leaf is much farther away from the root than any other leaf. Suppose we define much farther as like 2 steps farther.
33+
Then for the purpose of contradiction, suppose the min-path-sum tree for N nodes is not balanced, then there is a leaf A that is at least 2 steps further to the root than another leaf B. Then we can always move A to be the direct descendant of B (since B is leaf, there is an opening) resulting in a tree with smaller sum of paths. Thus the contradiction.
34+
This proof is a little informal, but I hope the idea is clear.
35+
36+
### Conclusion: Upper and Lower Bounds
37+
38+
In conclusion, the complexity of this program is Ω(NlgN) ~ O(N^2).
39+
40+
Optimization: How About Mutability
41+
I do think that the main cost incurred in this algorithm is a result of the immutability of String and the consequent allocation and copying.
42+
Intuitively, it seems like that StringBuilder could help. Using StringBuilder, we could make sure that String allocation and copying only happens when we are at a leaf. That means, the cost would be now sum of length of all root-to-leaf paths rather than sum of length of all root-to-node paths. This is assuming that StringBuilder.append and StringBuilder.setLength can work in O(1) or at least less than O(N) time.
43+
44+
## Definition for a binary tree node
45+
46+
```dart
47+
class TreeNode {
48+
int val;
49+
TreeNode? left;
50+
TreeNode? right;
51+
TreeNode([this.val = 0, this.left, this.right]);
52+
}
53+
```
54+
55+
## Solution - 1
56+
57+
```dart
58+
59+
class Solution {
60+
// Runtime: 466 ms, faster than 50.00% of Dart online submissions for Binary Tree Paths.
61+
// Memory Usage: 142.6 MB, less than 50.00% of Dart online submissions for Binary Tree Paths.
62+
List<String> binaryTreePaths(TreeNode? root) {
63+
List<String> res = [];
64+
if (root == null) return res;
65+
dfs(root, res, "");
66+
return res;
67+
}
68+
69+
void dfs(TreeNode? root, List ls, String str) {
70+
if (root == null) return;
71+
str += (str.length == 0 ? "" : "->") + root.val.toString();
72+
if (root.left == null && root.right == null) {
73+
ls.add(str);
74+
return;
75+
}
76+
dfs(root.left, ls, str);
77+
dfs(root.right, ls, str);
78+
}
79+
}
80+
```
81+
82+
## Solution - 2
83+
84+
```dart
85+
class Solution {
86+
// wrong
87+
List binaryTreePaths(TreeNode? root) {
88+
List<String> res = [];
89+
StringBuffer sb = StringBuffer();
90+
if (root == null) return res;
91+
dfs(root, res, sb);
92+
return res;
93+
}
94+
95+
void dfs(TreeNode? root, List ls, StringBuffer str) {
96+
if (root == null) return;
97+
str.write((str.length == 0 ? "" : "->") + root.val.toString());
98+
if (root.left == null && root.right == null) {
99+
ls.add(str.toString());
100+
return;
101+
}
102+
dfs(root.left, ls, str);
103+
str.length;
104+
dfs(root.right, ls, str);
105+
str.length;
106+
}
107+
}
108+
```
109+
110+
## Solution - 3 - Golang
111+
112+
```go
113+
import (
114+
"strconv"
115+
"strings"
116+
)
117+
// Definition for a binary tree node.
118+
type TreeNode struct {
119+
Val int
120+
Left *TreeNode
121+
Right *TreeNode
122+
}
123+
124+
type nodePath struct {
125+
node *TreeNode
126+
prevPath []int
127+
}
128+
129+
func binaryTreePaths(root *TreeNode) []string {
130+
res := make([]string, 0)
131+
132+
stack := make([]nodePath, 0)
133+
stack = append(stack, nodePath{root, []int{}})
134+
135+
var sb strings.Builder
136+
for len(stack) > 0 {
137+
cur := stack[len(stack)-1]
138+
stack = stack[:len(stack)-1]
139+
140+
if cur.node.Left == nil && cur.node.Right == nil {
141+
for _, n := range cur.prevPath {
142+
sb.WriteString(strconv.Itoa(n))
143+
sb.WriteString("->")
144+
}
145+
sb.WriteString(strconv.Itoa(cur.node.Val))
146+
res = append(res, sb.String())
147+
sb.Reset()
148+
}
149+
150+
if cur.node.Left != nil {
151+
stack = append(stack, nodePath{cur.node.Left, append(cur.prevPath, cur.node.Val)})
152+
}
153+
if cur.node.Right != nil {
154+
stack = append(stack, nodePath{cur.node.Right, append(cur.prevPath, cur.node.Val)})
155+
}
156+
}
157+
158+
return res
159+
}
160+
```

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ This repo contain leetcode solution using DART and GO programming language. Most
9191
- [Increasing Triplet Subsequence](IncreasingTripletSubsequence/increasing_triplet_subsequence.dart)
9292
- [Shortest Word Distance](ShortestWordDistance/shortest_word_distance.dart)
9393
- [Largest Perimeter Triangle](LargestPerimeterTriangle/largest_perimeter_triangle.dart)
94+
- [Binary Tree Paths](BinaryTreePaths/binary_tree_paths.dart)
9495

9596
## Reach me via
9697

0 commit comments

Comments
 (0)