I am learning tree on Leetcode. Need to prepare the testing data.
It is easy to convert the array to an organized node, where its elements are integers.
such as [3, 9, 20, 15, 7]
Here is my code:
extension Array where Element == Int{
func arrayToTree() -> TreeNode{
var nodes = [TreeNode]()
for num in 0..<self.count{
nodes.append(TreeNode(self[num]))
}
var i = 0
repeat{
nodes[i].left = nodes[2 * i + 1]
if self.count > 2 * i + 2 {
nodes[i].right = nodes[2 * i + 2]
}
i+=1
}while i < (self.count)/2
return nodes.first!
}
}
When the last level contains some nils , such as [3, 9, 20, nil, nil, 15, 7]
Here is the code:
extension Array where Element == Int?{
func arrayToTree() -> TreeNode?{
guard self.count > 0 else{
return nil
}
var nodes = [TreeNode?]()
for num in 0..<self.count{
if let num = self[num]{
nodes.append(TreeNode(num))
}
else{
nodes.append(nil)
}
}
var i = 0
repeat {
nodes[i]?.left = nodes[2 * i + 1]
if self.count > 2 * i + 2 {
nodes[i]?.right = nodes[2 * i + 2]
}
i += 1
} while i < (self.count) / 2
return nodes.first!
}
}
How can I refactor this?
Such as combine them together using Swift Generic with some Protocol.
-
\$\begingroup\$ Could you add a link to the leetcode question or the number? \$\endgroup\$muescha– muescha2019年02月16日 15:32:24 +00:00Commented Feb 16, 2019 at 15:32
-
\$\begingroup\$ This is not a question. It's on improving auxiliary code. \$\endgroup\$dengApro– dengApro2019年02月18日 00:26:31 +00:00Commented Feb 18, 2019 at 0:26
1 Answer 1
Simplifying func arrayToTree()
This
var nodes = [TreeNode?]() for num in 0..<self.count{ if let num = self[num]{ nodes.append(TreeNode(num)) } else{ nodes.append(nil) } }
creates a new array by mapping each element in self
(an optional Int
) to a new element (an optional TreeNode
). That can be simplified to
let nodes = self.map { 0ドル.map { TreeNode(0ドル) } }
where the outer Array.map
maps the given array to a new array, and the inner Optional.map
maps an optional Int
to an optional TreeNode
.
The
var i = 0 repeat { // ... i += 1 } while i < (self.count) / 2
loop can be simplified to
for i in 0..<self.count/2 {
// ...
}
The forced unwrapping
return nodes.first!
cannot crash – the nodes
array cannot be empty at this point. I would still suggest to avoid it since later code changes might break the logic. It also makes it easier for future maintainers of the code to verify its correctness.
Actually the preceding code just results in an empty nodes
array if the given list is empty. Therefore we can remove the initial guard
and replace it by
guard let first = nodes.first else {
return nil
}
return first
at the end of the method. This can be further shortened to
return nodes.first.flatMap { 0ドル }
Putting it together, the function would look like this:
func arrayToTree() -> TreeNode? {
let nodes = self.map { 0ドル.map { TreeNode(0ドル) } }
for i in 0..<self.count/2 {
nodes[i]?.left = nodes[2 * i + 1]
if self.count > 2 * i + 2 {
nodes[i]?.right = nodes[2 * i + 2]
}
}
return nodes.first.flatMap { 0ドル }
}
An alternative implementation
What you have is a way to create a TreeNode
, and that is what initializers methods are for. Therefore I would put the code in a
public convenience init?(values: [Int?])
of the TreeNode
class instead of an Array
extension method. The usage would be
let list = [3, 9, 20, nil, nil, 15, 7]
if let tree = TreeNode(values: list) {
// ...
}
And the task calls for a recursive implementation:
public convenience init?(values: [Int?], offset: Int = 0) {
guard offset < values.count, let value = values[offset] else {
return nil
}
self.init(value)
self.left = TreeNode(values: values, offset: 2 * offset + 1)
self.right = TreeNode(values: values, offset: 2 * offset + 2)
}
Making it generic
With the above changes it is now easy to replace Int
by an arbitrary value type:
public class TreeNode<T> {
public var val: T
public var left: TreeNode?
public var right: TreeNode?
public init(_ val: T) {
self.val = val
self.left = nil
self.right = nil
}
public convenience init?(values: [T?], offset: Int = 0) {
guard offset < values.count, let value = values[offset] else {
return nil
}
self.init(value)
self.left = TreeNode(values: values, offset: 2 * offset + 1)
self.right = TreeNode(values: values, offset: 2 * offset + 2)
}
}