Swift-二叉树常用操作

1,375 阅读4分钟

二叉树的定义

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
    }
}

二叉树的比较

二叉树相等

比较两个二叉树是否相等在二叉树的操作中也是很常用的函数,主要思路也是使用回溯的思路,对二叉树的节点逐个比较,代码如下:

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
    }
}

对称二叉树判定

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)
}

二叉树的最大深度

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
}

二叉树最小深度

// 二叉树最小深度
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
    }
}

二叉树路径综合

// 路径总和
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)
}

平衡二叉树判断

// 平衡二叉树
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
}

二叉查找树(BST)

BST(binary sort tree)叫做二叉查找树,或者二叉排序树。BST满足一下三个条件:

  • 节点的左子树包含的节点的值小于该节点的值
  • 节点的右子树包含的节点的值大于等于该节点的值
  • 节点的左子树和右子树都是BST
  • 中序遍历可以得到一个递增序列

判断二叉树是否是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)
}

定义二叉查找树

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
    }
    /// 是否是根节点
    public var isRoot: Bool {
        return parent == nil
    }
    /// 是否是叶节点
    public var isLeaf: Bool {
        return left == nil && right == nil
    }
    /// 是否是左子节点
    public var isLeftChild: Bool {
        return parent?.left === self
    }
    /// 是否是右子节点
    public var isRightChild: Bool {
        return parent?.right === self
    }
    /// 是否有左子节点
    public var hasLeftChild: Bool {
        return left != nil
    }
    /// 是否有右子节点
    public var hasRightChild: Bool {
        return right != nil
    }
    /// 是否有子节点
    public var hasAnyChild: Bool {
        return hasLeftChild || hasRightChild
    }
    /// 是否左右两个子节点都有
    public var hasBothChildren: Bool {
        return hasLeftChild && hasRightChild
    }
    /// 当前节点包括子树中的所有节点总数
    public var count: Int {
        return (left?.count ?? 0) + 1 + (right?.count ?? 0)
    }
}

基本操作

插入新节点

执行插入时,我们首先将新值与根节点进行比较。 如果新值较小,我们采取分支; 如果更大,我们采取分支。我们沿着这条路向下走,直到找到一个我们可以插入新值的空位。

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
          }
      }
  }

BST的创建

以数组的方式创建二叉查找数:

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

搜索二叉查找树

  • 如果该值小于当前节点,则选择左分支。
  • 如果该值大于当前节点,则选择右分支。
  • 如果该值等于当前节点,我们就找到了它!
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!
  	}
}

遍历二叉查找树

遍历二叉树有三种方法:

  • 中序(或 深度优先,In-order/depth-first):首先查看节点的左子节点,然后查看节点本身,最后查看其右子节点。
  • 前序(Pre-order):首先查看节点本身,然后查看其左右子节点。
  • 后序(Post-order):首先查看左右子节点并最后处理节点本身。

遍历树的过程也是递归的,如果按 中序遍历 二叉搜索树,它会查看所有节点,并且结果是 有序 的。

//中序遍历
public func traverseInOrder(process: (T) -> Void) {
  	left?.traverseInOrder(process: process)
  	process(value)
  	right?.traverseInOrder(process: process)
}
//前序遍历
public func traversePreOrder(process: (T) -> Void) {
  	process(value)
  	left?.traversePreOrder(process: process)
  	right?.traversePreOrder(process: process)
}
//后序遍历
public func traversePostOrder(process: (T) -> Void) {
  	left?.traversePostOrder(process: process)
  	right?.traversePostOrder(process: process)
  	process(value)
}

删除节点

删除二叉查找树节点后,需要将节点替换为左侧最大的子节点或右侧的最小子节点。

//移除一个节点
@discardableResult public func remove() -> BinarySearchTree? {
  	let replacement: BinarySearchTree?

  	// 当前节点的替换可以是左侧最大的替换  或者
  	// 右边最小的那个
  	if let right = right {
    	replacement = right.minimum()
  	} else if let left = left {
    	replacement = left.maximum()
  	} else {
    	replacement = nil
  	}

  	replacement?.remove()

  	// 将替换放置在当前节点的位置
    replacement?.right = right
    replacement?.left = left
    right?.parent = replacement
    left?.parent = replacement
    reconnectParentTo(node:replacement)

    // 当前节点不再是树的一部分,因此请清理它。
    parent = nil
    left = nil
    right = nil

    return replacement
}
//重新设置一个节点
private func reconnectParentTo(node: BinarySearchTree?) {
  	if let parent = parent {
    	if isLeftChild {
      	parent.left = node
    	} else {
      	parent.right = node
    	}
  	}
  	node?.parent = parent
}

将有序数组转换为二叉查找树

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

      return sortedArrayToBSTFunc(nums, l: 0, r: nums.count-1)
  }

  // 将有序数组转换为二叉查找树
  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
  }