编程语言
首页 > 编程语言> > 树、二叉树、查找算法

树、二叉树、查找算法

作者:互联网

一、思维导图

二、概念笔记

1)

结点的度:结点拥有的子树数。

树的度:树中所有结点的度的最大值。

2)

二叉树的五种基本形态:空树、只有根结点、右子树为空树、左子树为空树、左右子树均不为空树

3)

二叉树的性质

性质1:在二叉树的第 i 层上至多有2^(i-1)个结点(i≥1)。

归纳法证明:

归纳基:i = 1 层时,只有一个根结点: 2i-1 = 20 = 1,命题成立。

归纳假设:假设对所有的 j,1≤ j < i,命题成立。即第j层上至多有2j-1个结点。

归纳证明:j=i时,命题成立。

​ ∵二叉树上每个结点至多有两棵子树,且由归纳

假设有:第i-1层上至多有2i-2个结点

​ ∴第 i=j 层的结点数至多 = 2i-2 2 = 2i-1 。

命题成立(证毕)

性质2:深度为 k 的二叉树上至多含 2^(k)-1个结点(k≥1)。

性质3:对任何一棵二叉树,若它含有n0 个叶子结点、n2 个度为2的结点,则必存在关系式:n0 = n2+1。

证明:

设二叉树上结点总数 n = n0 + n1 + n2,

∵二叉树上分支总数 b = n1+2n2,①

而b = n-1 = n0 + n1 + n2 – 1 ②

由①②, n0 = n2 + 1

除根结点外,其余结点都有一个分 支进入,设b为分支总数,则n=b+1。

3)⭐重要概念辨析:特殊的二叉树:

满二叉树:指的是深度为k且含有2^(k)-1个结点的二 叉树。

​ 特点:是每一层上的结点数都是最大结点数。(结点全满)

完全二叉树:树中所含的 n 个结点和满二叉树中 编号为 1 至 n 的结点一一对应。

​ 特点:⑴叶子结点只可能在层次最大 的两层出现;⑵对任一结点,若其右 分支下的子孙的最大层次为l,则其左 分支下的子孙的最大层次为l或l+1。(最后一层可不满的满二叉树)

完全二叉树的性质:

性质4:具有n个结点的完全二叉树的深度为  log(2)n +1(向下取整)

性质5:若对含 n 个结点的完全二叉树从上到下且 从左至右进行 1 至 n 的编号,则对完全二叉树中 任意一个编号为 i 的结点: (1)若 i=1,则该结点是二叉树的根,无双亲,否 则,编号为i/2(向下)的结点为其双亲结点;

(2)若 2i>n,则该结点无左孩子,否则,编号为 2i 的结点为其左孩子结点;

(3)若 2i+1>n,则该结点无右孩子结点,否则,编 号为2i+1 的结点为其右孩子结点。

4)二叉树的存储结构:

顺序:特点

◼ 一组地址连续的存储单元存储各结点(如一维数组)

◼ 自根而下、自左而右存储结点;

◼ 按完全二叉树上的结点位置进行编号和存储

※在最坏的情况下,一个深度为 k 且只有 k 个结点的单支树(树中不存在度为2的 结点)却需要2 k -1的一维数组。

缺点:空间利用率太低

链式:

二叉链表的结点结构:(lchild|data|rchild) 题型:空指针数的计算

三叉链表的结点结构:(parent|lchild|data|rchild)

5)遍历二叉树

递归:

void Inorder (BiTree T) 
{ // 中序遍历二叉树 
	if (T) { 
		Inorder(T->lchild); // 遍历左子树 
		cout<<T->data; // 访问结点 
		Inorder(T->rchild); // 遍历右子树 
	} 
}

非递归:(使用栈)

void InOrderTraverse(BiTree T){ 
	InitStack(S); p = T; 
	声明q;//存放栈顶弹出元素 
	while (p || !StackEmpty(S)){ 
		if (p){ 
			Push(S, p); 
			p = p->lchild; 
		}else{ 
			Pop(S, q); 
			cout << q->data; 
			p = q->rchild; 
		} 
	} 
}

6)使用字符串建立二叉树的存储结构:

void CreateBiTree(BiTree &T){  //先序次序输入结点 	cin>>ch; 
	if (ch==‘#’) T = NULL; 
	else { 
		T = new BiTNode; 
		T->data = ch;            // 生成根结点 			CreateBiTree(T->lchild); // 构造左子树 			CreateBiTree(T->rchild); // 构造右子树 
		} 
} // CreateBiTree

7)线索二叉树:遍历二叉树的结果是:求得结点的一个线性序列。指向该线性序列中的“前驱”和 “后继” 的指针, 称作“线索”。

包含“线索”的存储结构, 称作“线索链表”。 与其相应的二叉树,称作 “线索二叉树”。

作用:线索二叉树能简化找到下一个节点的这个过程

8)森林和二叉树的转换

由森林转换成二叉树的转换规则为:

若 F = Φ,则 B = Φ;否则,

由 ROOT( T1 ) 对应得到 Node(root);

由 (t11, t12, …, t1m ) 对应得到 LBT;

由 (T2, T3,…, Tn ) 对应得到 RBT。

由二叉树转换为森林的转换规则:

若 B = Φ, 则 F = Φ;否则,

由 Node(root) 对应得到 ROOT( T1 );

由LBT 对应得到 ( t11, t12, …,t1m);

由RBT 对应得到 (T2, T3, …, Tn)。

9)对树的访问没有中根遍历

10)如图

11)哈夫曼树:n在所有含 n 个叶子结点、并带相同权值的 m 叉树中,必存在一棵其带权路径长度取最小值的树,称为“最优树”。

12)哈夫曼编码:编码目标:总编码长度最短!(出现频率越大的字符,其Huffman编码越短。)

让待传字符串中出现次数较多的字符采用尽可能短的编码,则转换的二进制字符串便可以减少。

关键:要设计长度不等的编码,则必须使任一字符的编码都不是另一个字符的编码的前缀。

符合这种编码要求的编码方式称为前缀编码

*左分支用"0"表示,右分支用"1"表示。

13)二叉排序树

特性:

¨若它的左子树不空,则左子树上所有结点的值均小于根结点的值;

¨若它的右子树不空,则右子树上所有结点的值均大于根结点的值;

¨它的左、右子树也都分别是二叉排序树。

14)二叉排序树的查找(递归)

BSTree SearchBST(BSTree T,KeyType key) { 
	if((!T) || key==T->data.key) return T;       	else if (key<T->data.key) //在左子树中继续查找 			return SearchBST(T->lchild,key);
   else  //在右子树中继续查找 
   		return SearchBST(T->rchild,key);
} // SearchBST

15)二叉排序树的删除:

​ ◼被删除的结点是叶子

​ ◼被删除的结点只有左子树或只有右子树

​ ◼被删除的结点既有左子树又有右子树

16)二叉排序树的查找性能:

平均查找长度和二叉树的形态有关,即,

最好:log2n(形态匀称,与二分查找的判定树相似)

最坏: (n+1)/2(单支树)

17)平衡二叉树(又称AVL树): 是二叉排序树的另一种形式,其特点为: 树中每个结点左、右子树深度之差(称为平衡因子BF) 的绝对值不大于1。

AVL树的平均查找长度和logn是同数量级的

17)B-树(又称多路平衡查找树)】

定义:一棵m阶B-树或者是一棵空树,或者是满足 下列要求的m叉树:

• 每个节点至多m个孩子节点(至多有m-1个关键字)

• 除根节点外,其他节点至少有m/2个孩子节点(即 至少有m/2-1个关键字);(向上取整)

• 若根节点不是叶子节点,根节点至少两个孩子节点

18)B+树:

一棵m阶B+树满足下列条件:

每个分支节点至多有m棵子树。 根节点或者没有子树,或者至少有两棵子树

除根节点,其他每个分支节点至少有m/2棵子树 (向上取整)

有n棵子树的节点有n个关键字。

叶子节点包含全部关键字及指向相应记录的指针

⚫ 叶子节点按关键字大小顺序链接

⚫ 叶子节点直接指向数据文件中的记录。

所有分支节点(可看成是分块索引的索引表)

⚫ 包含它的各个子节点(即下级索引的索引块) 中最大关键字及指向子节点的指针。

三、疑难杂症:

①表达式二叉树

先缀表达式获取二叉树

分析:

从左往右遍历先缀表达式,发现操作符就将其入栈,发现操作符的第二个操作数之后,将它们组织成最小的子树,然后操作符出栈,继续遍历下一个字符。在这个过程中,操作数是不入栈的,栈里只有操作符,当操作符组织成最小计算单元之后就将其出栈。当栈空的时候,说明先缀表达式遍历完毕。

代码:

void ExpressionBinaryTree::buildBTreeByPreffixE()
{
  root = new BinaryTreeNode();
  char c;
  cout << "->请输入前缀表达式,以=结尾." << endl;
  cout << "->:";
  cin >> c;
  stack *> parentStack;//用于保存存放父结点
  BinaryTreeNode *pointer = root;//用于指向下一个保存数据的结点
  string blankStr = "";
  double tempDouble = 0;
  string tempStr;//用于输入流,将浮点数转换成字符串
  while (c != '='``)
  {
    switch (c)
    {
    case '+':
    case '-':
    case '*':
    case '/':
      pointer->setValue(c + blankStr);//设置当前结点的值
      pointer->setLeftChild(new BinaryTreeNode());//生成左结点
      parentStack.push(pointer);
      pointer = pointer->getLeftChild();
      break;
    }
    if (isdigit(c))
    {
      std::cin.putback(c);
      std::cin >> tempDouble;
      stringstream sss;
      sss << tempDouble;
      sss >> tempStr;
      pointer->setValue(tempStr);
      pointer = parentStack.top();
      while` `(pointer->getRightChild() != NULL)
      {
        parentStack.pop();//找到按前序遍历的下一个结点
        if (parentStack.empty())
          return;
        pointer = parentStack.top();
      }
      pointer->setRightChild(new BinaryTreeNode());//找到了按前序遍历的下一个结点位置并生成结点
      pointer = pointer->getRightChild();
    }
    std::cin >> c;
  }
}

⭐中缀表达式获取二叉树(括号以及优先级的处理)

如果从右开始计算,每次计算结果都是下一个操作符的第二个操作数,那么遍历结束之后,结果就出来了。用代码实现可以用两个栈,一个栈保存从左到右的操作符,另一个栈保存从左到右的操作数
其实括号也是优先级的问题。处理括号时按照处理优先级问题的逻辑,即右括号的优先级是最高的。在压入右括号的时候不用看后面的操作符了,右括号就是最高的,应该直接将从左括号到右括号中的表达式组成子树,然后压入到操作数栈中。

代码如下:

//比较优先级
bool ExpressionBinaryTree::aIsGreaterOrEqualThanB(char a, char b)
{
    switch (a)
    {
    case '*':
    case '/':
        return true;
    case '+':
    case '-':
        if (b == '*' || b == '/')
            return false;
        return true;
    case '(':
        return false;
    }
    return false;
}
 
//中缀表达式转换成二叉树
void ExpressionBinaryTree::buildBTreeByInfixE()//构造中缀表达式二叉树
{
    root = new BinaryTreeNode<string>();
    char c;
    cout << "->请输入中缀表达式,以=结尾." << endl;
    cout << "->:";
    cin >> c;
    stack<BinaryTreeNode<string> *> opd;//操作数栈 //为了方便统一管理,操作数和操作符全部定义为string类型
    stack<string> opt;//操作符栈
    double tempDouble = 0;
    string tempStr;//用于输入流,将浮点数转换成字符串
    string blankStr = "";
    while (c != '=')
    {
        switch (c)
        {
        case '+':
        case '-':
        case '*':
        case '/':
            while (!opt.empty() && aIsGreaterOrEqualThanB(opt.top().c_str()[0], c))//如果栈顶操作符优先级高于读入操作符优先级,则表名应该先计算栈顶操作符
            {
                BinaryTreeNode<string> *secondOpd = opd.top();
                opd.pop();
                BinaryTreeNode<string> *firstOpd = opd.top();
                opd.pop();//从操作数栈取出两个操作数
                opd.push(new BinaryTreeNode<string>(opt.top(), firstOpd, secondOpd));//将操作数和操作符组成一个新结点存入栈中
                opt.pop();
            }
            opt.push(c + blankStr);//将读入操作符入栈
            break;
        case '(':
            opt.push(c + blankStr);//遇到左括号直接入栈
            break;
        case ')':
            while (!opd.empty() && opt.top().c_str()[0] != '(')//为了防止冗赘括号,但未检测括号不匹配
            {
                BinaryTreeNode<string> *secondOpd = opd.top();
                opd.pop();
                BinaryTreeNode<string> *firstOpd = opd.top();
                opd.pop();//从操作数栈取出两个操作数
                opd.push(new BinaryTreeNode<string>(opt.top(), firstOpd, secondOpd));//将操作数和操作符组成一个新结点存入栈中
                opt.pop();
            }
            opt.pop();//将左括号出栈
            break;
        }
        if (isdigit(c))
        {
            std::cin.putback(c);
            std::cin >> tempDouble;
            stringstream sss;
            sss << tempDouble;
            sss >> tempStr;
            opd.push(new BinaryTreeNode<string>(tempStr));
        }
        std::cin >> c;
    }
    while (!opt.empty())
    {
        BinaryTreeNode<string> *secondOpd = opd.top();
        opd.pop();
        BinaryTreeNode<string> *firstOpd = opd.top();
        opd.pop();//从操作数栈取出两个操作数
        opd.push(new BinaryTreeNode<string>(opt.top(), firstOpd, secondOpd));//将操作数和操作符组成一个新结点存入栈中
        opt.pop();
    }
    root = opd.top();//此时操作数栈中唯一元素即为根元素
    opd.pop();
}

作者:vBackSlash
链接:http://www.imooc.com/article/273872?block_id=tuijian_wz
来源:慕课网

一组需要检索的数据(里面可能有多个属性,比如书本 记录,里面可能有ISBN号、书名、作者、出版社等),其 本身没有排序。如何对该组数据进行处理提高查找的效率? 算法的时间复杂度由谁来决定?(未解决)

标签:结点,操作数,opd,BinaryTreeNode,算法,查找,二叉树,节点
来源: https://www.cnblogs.com/dcftx/p/12780777.html