The definition of binary tree

public class TreeNode {
    public var val: Int
    public var left: TreeNode?
    public var right: TreeNode?
    public init(_ val: Int) {
        self.val = val
        self.left = nil
        self.right = nil}}Copy the code

Comparison of binary trees

Binary trees are equal

Comparing whether two binary trees are equal or not is also a common function in binary tree operations. The main idea is to use backtracking to compare nodes of binary trees one by one. The code is as follows:

func isSameTree(_ p: TreeNode? ._ q: TreeNode?). -> Bool {
    if p = = nil && q = = nil {
        return true
    }
    if p ! = nil && q ! = nil && p?.val = = q?.val {
        return isSameTree(p?.left, q?.left) && isSameTree(p?.right, q?.right)
    } else {
        return false}}Copy the code

Symmetric binary tree decision

func isSymmetric(_ root: TreeNode?). -> Bool {
    
    return isMirror(root, t2: root)
}

func isMirror(_ t1: TreeNode? .t2: TreeNode?). -> Bool {
    if t1 = = nil && t2 = = nil {
        return true
    }
    
    if t1 = = nil || t2 = = nil {
        return false
    }
    
    return t1?.val = = t2?.val && isMirror(t1?.right, t2: t2?.left) && isMirror(t1?.left, t2: t2?.right)
}
Copy the code

The maximum depth of a binary tree

func maxDepth(_ root: TreeNode?). -> Int {
    if root = = nil {
        return 0
    }
    let leftHeight = maxDepth(root?.left)
    let rightHeigh = maxDepth(root?.right)
    return max(leftHeight, rightHeigh) + 1
}
Copy the code

Minimum depth of binary tree

// Minimum depth of binary tree
func minDepth(_ root: TreeNode?). -> Int {
    if root = = nil {
        return 0
    }
    let leftH = minDepth(root?.left)
    let rightH = minDepth(root?.right)
    if leftH ! = 0 && rightH ! = 0 {
        return min(leftH, rightH) + 1
    } else {
        return leftH + rightH + 1}}Copy the code

Binary tree path synthesis

// Total of paths
func hasPathSum(_ root: TreeNode? ._ sum: Int) -> Bool {
    if root = = nil {
        return false
    }
    
    if root?.left = = nil && root?.right = = nil && root?.val = = sum {
        return true
    }
    return hasPathSum(root?.left, sum - root!.val) || hasPathSum(root?.right, sum - root!.val)
}
Copy the code

Balanced binary tree judgment

// Balance the binary tree
func isBalanced(_ root: TreeNode?). -> Bool {
    return dfsHeight(root) ! = -1
}

func dfsHeight(_ root: TreeNode?). -> Int {
    if root = = nil {
        return 0
    }
    let leftHeight = dfsHeight(root?.left)
    if leftHeight = = -1 {
        return -1
    }
    let rightHeight = dfsHeight(root?.right)
    if rightHeight = = -1 {
        return -1
    }
    if abs(leftHeight - rightHeight) > 1 {
        return -1
    }
    return max(leftHeight, rightHeight) + 1
}
Copy the code

Binary Search tree (BST)

Binary sort tree (BST) is called binary search tree or binary sort tree. BST satisfies the following three conditions:

  • The left subtree of a node contains a node whose value is less than that node’s value
  • The right subtree of a node contains a node whose value is greater than or equal to that node’s value
  • The left and right subtrees of the node are both BST
  • Middle order traversal can produce an increasing sequence

Check whether the binary tree is BST

func isValidBST(root: TreeNode?). -> Bool {
    return _helper(node: root, nil.nil)}func _helper(node: TreeNode? ._ min: Int? ._ max: Int?). -> Bool {
    guard let node = node else {
        return true
    }
    if let min = min, node.val < = min {
        return false
    }
    if let max = max, node.val > = max {
        return false
    }
    return _helper(node: node.left, min, node.val) && _helper(node: node.right, node.val, max)
}
Copy the code

Define a binary search tree

public class BinarySearchTree<T: Comparable> {
    private(set) public var value: T
    private(set) public var parent: BinarySearchTree?
    private(set) public var left: BinarySearchTree?
    private(set) public var right: BinarySearchTree?
    
    public init(value: T) {
        self.value = value
    }
    /// Is the root node
    public var isRoot: Bool {
        return parent = = nil
    }
    /// is a leaf node
    public var isLeaf: Bool {
        return left = = nil && right = = nil
    }
    /// is the left child node
    public var isLeftChild: Bool {
        return parent?.left = = = self
    }
    /// is the right child node
    public var isRightChild: Bool {
        return parent?.right = = = self
    }
    /// Whether there is a left child node
    public var hasLeftChild: Bool {
        return left ! = nil
    }
    /// Whether there is a right child node
    public var hasRightChild: Bool {
        return right ! = nil
    }
    /// Whether there are child nodes
    public var hasAnyChild: Bool {
        return hasLeftChild || hasRightChild
    }
    // whether there are two child nodes
    public var hasBothChildren: Bool {
        return hasLeftChild && hasRightChild
    }
    /// The current node includes the total number of nodes in the subtree
    public var count: Int {
        return (left?.count ?? 0) + 1 + (right?.count ?? 0)}}Copy the code

Basic operation

Insert a new node

When we perform the insert, we first compare the new value to the root node. If the new value is small, we take the left branch; If it’s bigger, we take the right branch. We go down this path until we find a space where we can insert a new value.

public func insert(value: T) {
      if value < self.value {
          if let left = left {
              left.insert(value: value)
          } else {
              left = BinarySearchTree(value: value)
              left?.parent = self}}else {
          if let right = right {
              right.insert(value: value)
          } else {
              right = BinarySearchTree(value: value)
              right?.parent = self}}}Copy the code

The creation of BST

Create binary lookup numbers as arrays:

public convenience init(array: [T]) {
  	precondition(array.count > 0)
  	self.init(value: array.first!)
  	for v in array.dropFirst() {
    		insert(value: v)
  	}
}
Copy the code

Search binary search tree

  • If the value is less than the current node, the left branch is selected.
  • If the value is greater than the current node, the right branch is selected.
  • If the value is equal to the current node, we have found it!
public func search(value: T) -> BinarySearchTree? {
  	if value < self.value {
    	return left?.search(value)
  	} else if value > self.value {
    	return right?.search(value)
  	} else {
    	return self  // found it!}}Copy the code

Traverse the binary search tree

There are three ways to traverse a binary tree:

  • Middle-order (or depth-first, in-order /depth-first) : First look at the left child of the node, then look at the node itself, and finally look at its right child.
  • Pre-order: First look at the node itself, then look at its left and right children.
  • Post-order: First look at the left and right child nodes and last process the node itself.

The process of traversing the tree is also recursive; if you traverse the binary search tree in middle order, it looks at all the nodes and the results are ordered.

// middle order traversal
public func traverseInOrder(process: (T) - >Void) {
  	left?.traverseInOrder(process: process)
  	process(value)
  	right?.traverseInOrder(process: process)
}
// preorder traversal
public func traversePreOrder(process: (T) - >Void) {
  	process(value)
  	left?.traversePreOrder(process: process)
  	right?.traversePreOrder(process: process)
}
// after the sequence traversal
public func traversePostOrder(process: (T) - >Void) {
  	left?.traversePostOrder(process: process)
  	right?.traversePostOrder(process: process)
  	process(value)
}
Copy the code

Remove nodes

After deleting a binary search tree node, replace it with the largest child node on the left or the smallest node on the right.

// Remove a node
@discardableResult public func remove(a) -> BinarySearchTree? {
  	let replacement: BinarySearchTree?

  	// The replacement of the current node can be the largest replacement on the left or
  	// The smallest on the right
  	if let right = right {
    	replacement = right.minimum()
  	} else if let left = left {
    	replacement = left.maximum()
  	} else {
    	replacement = nil
  	}

  	replacement?.remove()

  	// Place the replacement at the current node's location
    replacement?.right = right
    replacement?.left = left
    right?.parent = replacement
    left?.parent = replacement
    reconnectParentTo(node:replacement)

    // The current node is no longer part of the tree, so clean it up.
    parent = nil
    left = nil
    right = nil

    return replacement
}
// Reset a node
private func reconnectParentTo(node: BinarySearchTree?). {
  	if let parent = parent {
    	if isLeftChild {
      	parent.left = node
    	} else {
      	parent.right = node
    	}
  	}
  	node?.parent = parent
}
Copy the code

Converts an ordered array to a binary lookup tree

public func sortedArrayToBST(_ nums: [Int]) -> BinarySearchTree<Int>? {
      if nums.isEmpty {
          return nil
      }

      return sortedArrayToBSTFunc(nums, l: 0, r: nums.count-1)}// Convert an ordered array to a binary search tree
  private func sortedArrayToBSTFunc(_ nums: [Int].l: Int.r: Int) -> BinarySearchTree<Int>? {
      if r < l {
          return nil
      }
      let mid = l + (r - l) / 2
      let root = BinarySearchTree<Int>(value: nums[mid])
      root.left = sortedArrayToBSTFunc(nums, l: l, r: mid-1)
      root.right = sortedArrayToBSTFunc(nums, l: mid+1, r: r)
      return root
  }
Copy the code