数据结构与算法--非线性数据结构

目录

二叉查找树


树的高度,深度,层数的定义下面用图来说明这三个概念的区别

下面用图来说明这三个概念的区别

高度,这个概念跟生活中的楼层一样,从下往上数如第10层,12层起点都是地面
深度,是从上往下度量的,比如水中鱼的深度,是从水平面开始度量读
层数,跟深度类似,但计数起点是1

二叉树
满二叉树,叶子节点全都在最底层,出了叶子节点之外,每个节点都有左右两个子节点
完全二叉树,叶子节点都在最底下两层,最后一层的叶子节点都靠左排列,除了最后一层其他层的节点个数都要达到最大

完全二叉树这种特殊的定义,在用数组存储二叉树时候可以避免浪费,数组的节点都用上了没有空间浪费

二叉树的遍历
前序遍历,对于树种的任意节点,先打印这个节点,再打印它的左子树,最后打印它的右子树
中序遍历,对于树种的任意节点来说,先打印它的左子树,然后打印它本身,最后打印它的右子树
后序遍历,对于树种任意节点来说,先打印它的左子树,然后再打印它的右子树,最后打印这个节点本身

三种遍历的代码实现

void preOrder(Node* root) {
  if (root == null) return;
  print root // 此处为伪代码,表示打印 root 节点
  preOrder(root->left);
  preOrder(root->right);
}

void inOrder(Node* root) {
  if (root == null) return;
  inOrder(root->left);
  print root // 此处为伪代码,表示打印 root 节点
  inOrder(root->right);
}

void postOrder(Node* root) {
  if (root == null) return;
  postOrder(root->left);
  postOrder(root->right);
  print root // 此处为伪代码,表示打印 root 节点
}

遍历二叉树,每个节点最多会被访问两次
所以二叉树遍历的时间复杂度是O(n)
一组数据,1,3,5,6,9,10  可以构建出多少种不同的二叉树
这是卡特兰数,是C[n,2n]/(n+1)种形式,c是组合数,节点的不同又是一种全排列
一共是n! * C[n,2n]/(n+1) 个二叉树

二叉查找树

在树种任意一个节点,其左字数中的每个节点的值,都要小于这个节点的值,而右字数节点的值都大于这个节点的值
查找,更新,插入都很容,删除比较复杂有三种情况
1.如果要删除的节点没有子节点,更新父节点指向null即可
2.要删除的节点只有一个子节点(只有左子节点或右子节点),只要更新父节点指向要删除的节点的子节点即可
3.如果有两个子节点,需要找到这个节点右子树中的最小节点,把它替换到要删除的节点上,再删除这个最小节点,也可以加个删除标记

支持重复数据的二叉查找树
插入时,如果碰到一个节点的值与要插入的值相同,就将要插入的数据放到这个节点的右子树
查找时,遇到相同节点,继续在右子树种查找,直到遇到叶子节点

删除时,先查找每个要删除的节点,再按之前的删除方式,以此删除

完全二叉树的的高度小于等于logn

有了散列表为什么还要用二叉树

  • 散列表中断数据是无序存储的,如果要输出有序数据,需要先排序,二叉树的中序遍历O(n)时间就可以输出了
  • 散列表扩容时很耗时,遇到散列冲突性能不稳定,平衡二叉树的性能非常稳定
  • 尽管散列表的查找操作时间复杂度是常量级,但因哈希冲突这个常量不一定比logn小,实际操作不一定比O(logn)块,加上函数的耗时,也不一定就比平衡二叉树效率高
  • 散列表的构造比二叉树要复杂,需要考虑散列函数的设计,冲突解决,扩容缩容,平衡二叉树只要考虑平衡这一个问题
  • 为避免过多的散列冲突,散列表的装载因子不能太大,特别是基于开放寻址法解决冲突时
     

猜你喜欢

转载自blog.csdn.net/hixiaoxiaoniao/article/details/88831173