第十一章 树结构(上)

基本定理:树中节点数等于边的数目加1,也等于所有节点的度数加1,,很显然嘛,因为边的个数就等于节点的度数

森林为m棵不想交的树,删除一个树的根,就能得到一个森林,相反,加上那个一个节点作为树根,森林就会变成树

树的核心在代码方面其实就是节点而已,任何一种结构不能只看他的逻辑结构,要看他的存储时的物理结构,而存储结构起始就只有两种,一种是顺序结构,另一种是连式结构,,以前在堆,二叉堆那里实现的树就是一种特殊的,用数组存储的树,但在实际中,并不多用数组来存储树,而是用链式来表示

import java.util.List;

class TreeNode<E>{
    public E key; //data
    public TreeNode<E> parent;
    public List<TreeNode<E>> children;

    public TreeNode(E key, TreeNode<E> parent) {
        this.key = key;
        this.parent = parent;
    }

    @Override
    public String toString() {
        return "TreeNode{" +
                "key=" + key +
                '}';
    }
}

然后是树的基本操作,,我把他定义为一个接口类


import java.util.List;

public interface ITree<E> {

  int getSize();

  TreeNode<E> getRoot();

 
  TreeNode<E> getParent(TreeNode<E> x);

  
  TreeNode<E> getFirstChild(TreeNode<E> x);

  TreeNode<E> getNextSibling(TreeNode<E> x);

  int getHeight(TreeNode<E> x);

 
  void insertChild(TreeNode<E> x, TreeNode<E> child);

  void deleteChild(TreeNode<E> x, int i);

  List<TreeNode<E>> preOrder(TreeNode<E> x);//xian

  List<TreeNode<E>> postOrder(TreeNode<E> x);//hou

  List<List<TreeNode<E>>> levelOrder(TreeNode<E> x);//ceng ci bian li 

  List<List<TreeNode<E>>> levelOrder();
}

这里只写的是一个接口类,具体的方法并没与去实现,然后下来我去实现一下具体的一个树,要只有一些些方法具有相关联的关系,所以记得改一个东西要去看看还有什么变化了吗,

另外,树中体现了深搜(树的高度)和宽搜(层次遍历) (层次遍历是一个很经典的问题哦)

深搜用递归,宽搜用队列 ,弹一个,(从前面弹出一个)加n个,,加n个意思是把他的n个孩子加进来(加到后面去),所以要用队列这样的数据结构,,,这样直接按层遍历也比较简单,但是还有一个小需求就是不要直接输出一个大列表,而是一个大列表里一行是一个小列表,这样也很简单啊,只要弹出的是上一层的最后一个节点,那就新开一个队列,

package org.lanqiao.algo.elementary._11_tree;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

import static java.lang.Math.max;

public class MyTree<E> implements ITree<E> {
  private int size = 0;
  private TreeNode root;

  public MyTree(TreeNode root) {
    this.root = root;
    size++;
  }

  @Override
  public int getSize() {
    return size;
  }

  @Override
  public TreeNode<E> getRoot() {
    return root;
  }

  @Override
  public TreeNode<E> getParent(TreeNode<E> x) {
    return x.parent;
  }

  @Override
  public TreeNode<E> getFirstChild(TreeNode<E> x) {
    return x.children.get(0);
  }

  @Override
  public TreeNode<E> getNextSibling(TreeNode<E> x) {
    List<TreeNode<E>> children = x.parent.children;
    int i = children.indexOf(x);
    // try {
    //   return children.get(i + 1);
    // } catch (Exception e) {
    //   return null;
    // }
    if (i == children.size() - 1) {
      return null;
    } else {
      return children.get(i + 1);
    }
  }

  public int getHeight() {
    return getHeight(root);
  }

  @Override
  public int getHeight(TreeNode<E> x) {
    if (x.children == null) {
      return 0;
    } else {
      int h = 0;
      for (int i = 0; i < x.children.size(); i++) {
        h = max(h, getHeight(x.children.get(i)));
      }
      return h + 1;
    }
  }

  @Override
  public void insertChild(TreeNode<E> p, TreeNode<E> child) {
    if (p.children == null) {
      p.children = new ArrayList<>();
    }
    p.children.add(child);
    child.parent = p;
    size++;
  }

  @Override
  public void deleteChild(TreeNode<E> p, int i) {
    p.children.remove(i);
    size--;
  }

  @Override
  public List<TreeNode<E>> preOrder(TreeNode<E> x) {
    return null;
  }

  @Override
  public List<TreeNode<E>> postOrder(TreeNode<E> x) {
    return null;
  }

  @Override
  public List<List<TreeNode<E>>> levelOrder(TreeNode<E> x) {  //安层打印,加一弹二
    List<List<TreeNode<E>>> res = new ArrayList<>();//list的list
    Queue<TreeNode<E>> q = new LinkedList<>();
    q.add(x);//初始化
    TreeNode<E> last = x;//标记上一行的最末节点
    TreeNode<E> nLast = null;//标记最新加入队列的节点
    List<TreeNode<E>> l = new ArrayList<>();//第一行的list
    res.add(l);

    while (!q.isEmpty()) {
      TreeNode<E> peek = q.peek();
      //把即将弹出的节点的子节点加入队列
      if (peek.children != null) {
        for (TreeNode<E> n : peek.children) {
          q.add(n);
          nLast = n;
        }
      }
      l.add(q.poll());//弹出,加入到当前层列表

      if (peek == last && !q.isEmpty()) {//如果现在弹出的节点是之前标记的最后节点,就要换列表
        l = new ArrayList<>();
        res.add(l);
        last = nLast;
      }
    }
    return res;
  }


  @Override
  public List<List<TreeNode<E>>> levelOrder() {
    return levelOrder(root);
  }
}

二叉树

度为0的节点数的个数 = 度数为2的节点个数+1 

完全二叉树:是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。

满二叉树:除最后一层无任何子节点外,每一层上的所有结点都有两个子结点二叉树。

有n个节点的完全二叉树的高度为logn

含有n>=1个节点的二叉树高度至多为n-1(直接退化成一根线,又叫斜二叉树),,高度至少为logn

对有n个节点的完全二叉树进行编号,则对于任意节点i(1=<i,,,意思死从1开始编号)

(1) i>1,他的双亲节点是i/2

(2)2×i>n,则i无左孩子,否则他左孩子是2×i;

(3)2×i+1>n,则i无右孩子,否则右孩子是2×i+1

那么如何写二叉树的代码呢?其实只需要把原来的树的List<TreeNode> children 改成2就可以了

二叉查找树(binary search tree,BST)

其实意思就是小的往左边走,大的往右边走,然后他的子树都是二叉树,如果按中序遍历一颗BST的话,就可以得到一个按照关键字递增的有序序列

public interface IBinarySearchTree<K,V> {
  /**
   * 新增节点
   * @param k 关键字
   * @param v 值
   */
  BSTNode<K, V> insert(K k, V v);
  /**
   * 中序遍历
   *
   * @param con
   *          处理中序遍历的每个元素的函数
   */
  void inorder(Consumer<K> con);
  /**
   * 查找元素
   * @param key
   * @return
   */
  V lookupValue(K key);
  /**
   * 获取最小关键字
   * @return
   */
  K min();
  /**
   * 获取最大关键字
   * @return
   */
  K max();

  /**
   * 移除关键字对应的节点
   * @param key
   */
  void remove(K key);

  /**
   * x的后继——比x大的第一个元素
   * 1、是其右子树的最小值
   * 2、没有右子树,则向上追溯,直到某个祖先节点是左孩子,返回这个祖先节点的父节点,它就是x的后继
   * @param x
   * @return
   */
  K successor(K x);

  /**
   * 前驱
   * @param x 关键字
   * @return
   */
  K predecessor(K x);

  boolean isBalance();

  /**
   * 返回节点数
   * @return
   */
  int getSize();

  /**
   * 高度
   * @return
   */
  int getHeight();

  List<List<BSTNode<K, V>>> levelOrder();
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值