数据结构与算法Day18----二叉树

一、树:


  A节点是B节点的父节点, B节点是A节点的子节点。 B、 C、 D这三个节点的父节点是同一个节点,所以它们之间互称为兄弟节点。没有父节点的节点叫作根节点,也就是图中的节点E。没有子节点的节点叫作叶子节点或者叶节点,比如图中的G、 H、 I、 J、 K、 L都是叶子节点。
节电的高度:节点到叶子节点的最长路径(边数)。A的高度为:2
节点的深度:根节点到该节点所经历的边的个数。 A的深度为:1
节点的层数:节点的深度 + 1。A的层数为:2
树的高度:根节点的高度。树的高度为:3

二、二叉树:

1、概念:

  每个节点最多有两个“叉”,也就是两个子节点,分别是左子节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只有左子节点,有的节点只有右子节点。

2、特殊二叉树:

满二叉树和完全二叉树

<1>、满二叉树:

  叶子节点全都在最底层,除了叶子节点之外,每个节点都有左右两个子节点,这种二叉树就叫作满二叉树。如①。

<2>、完全二叉树:

  叶子节点都在最底下两层,最后一层的叶子节点都靠左排列,并且除了最后一层,其他层的节点个数都要达到最大,这种二叉树叫作完全二叉树。如②。

三、二叉树的存储:

1、基于指针或者引用的二叉链式存储法:

<1>、存储方法:

  每个节点有三个字段,其中一个存储数据,另外两个是指向左右子节点的指针。我们只要拎住根节点,就可以通过左右子节点的指针,把整棵树都串起来。这种存储方式我们比较常用。大部分二叉树代码都是通过这种结构来实现的。

<2>、图示:
二叉链式存储

2、基于数组的顺序存储法:

<1>、存储方法:

  把根节点存储在下标i = 1的位置,左子节点存储在下标2 * i = 2的位置,右子节点存储在2 * i + 1 = 3的位置。如果节点X存储在数组中下标为i的位置,下标为2 * i 的位置存储的就是左子节点,下标为2 * i + 1的位置存储的就是右子节点。反过来,下标为i/2的位置存储就是它的父节点。通过这种方式,我们只要知道根节点存储的位置(一般情况下,为了方便计算子节点,根节点会存储在下标为1的位置),这样就可以通过下标计算,把整棵树都串起来。

<2>、图示:
顺序存储

四、二叉树的遍历:

三种遍历

1、前序遍历:

<1>、概念:

  对于树中的任意节点来说,先打印这个节点,然后再打印它的左子树,最后打印它的右子树。

<2>、递归的递推公式:

preOrder(r) = print r->preOrder(r->left)->preOrder(r->right)

<3>、算法:

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

2、中序遍历:

<1>、概念:

  对于树中的任意节点来说,先打印它的左子树,然后再打印它本身,最后打印它的右子树。

<2>、递归的递推公式:

inOrder(r) = inOrder(r->left)->print r->inOrder(r->right)

<3>、算法:

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

3、后序遍历:

<1>、概念:

  对于树中的任意节点来说,先打印它的左子树,然后再打印它的右子树,最后打印这个节点本身。

<2>、递归的递推公式:

postOrder(r) = postOrder(r->left)->postOrder(r->right)->print r

<3>、算法:

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

4、二叉树遍历的时间复杂度是O(n)。

五、二叉查找树(Binary Search Tree):

1、概念:

  在树中的任意一个节点,其左子树中的每个节点的值,都要小于这个节点的值,而右子树节点的值都大于这个节点的值。

2、图示:
二叉查找树

3、二叉查找树的操作;

1、查找操作:

<1>、二叉查找树中查找一个节点的方法:

  先取根节点,如果它等于我们要查找的数据,那就返回。如果要查找的数据比根节点的值小,那就在左子树中递归查找;如果要查找的数据比根节点的值大,那就在右子树中递归查找。

<2>、图示:
二叉查找树的查找
<3>、算法:

public class BinarySearchTree {
  private Node tree;
  public Node find(int data) {
    Node p = tree;
    while (p != null) {
      if (data < p.data) p = p.left;
      else if (data > p.data) p = p.right;
      else return p;
    }
  return null;
  }
  public static class Node {
    private int data;
    private Node left;
    private Node right;
    public Node(int data) {
      this.data = data;
     }
  }
}

2、插入操作:

<1>、二叉查找树中插入一个节点的方法:

  新插入的数据一般都是在叶子节点上,所以只需要从根节点开始,依次比较要插入的数据和节点的大小关系。如果要插入的数据比节点的数据大,并且节点的右子树为空,就将新数据直接插到右子节点的位置;如果不为空,就再递归遍历右子树,查找插入位置。同理,如果要插入的数据比节点数值小,并且节点的左子树为空,就将新数据插入到左子节点的位置;如果不为空,就再递归遍历左子树,查找插入位置。

<2>、图示:
二叉查找树的插入
<3>、算法:
public void insert(int data) {
    if (tree == null) {
        tree = new Node(data);
        return;
    }
    Node p = tree;
    while (p != null) {
        if (data > p.data) {
            if (p.right == null) {
                p.right = new Node(data);
                return;
            }
            p = p.right;
        } else { // data < p.data
            if (p.left == null) {
                p.left = new Node(data);
                return;
            }
        p = p.left;
        }   
    }
}

3、删除操作:

<1>、二叉查找树中删除一个节点的方法:

 针对要删除节点的子节点个数的不同,分三种情况来处理。
  第一种情况是,如果要删除的节点没有子节点,只需要直接将父节点中,指向要删除节点的指针置为null。比如图中的删除节点55。
  第二种情况是,如果要删除的节点只有一个子节点(只有左子节点或者右子节点),只需要更新父节点中,指向要删除节点的指针,让它指向要删除节点的子节点就可以了。比如图中的删除节点13。
  第三种情况是,如果要删除的节点有两个子节点,需要找到这个节点的右子树中的最小节点,把它替换到要删除的节点上。然后再删除掉这个最小节点,因为最小节点肯定没有左子节点(如果有左子结点,那就不是最小节点了),所以,可以应用上面两条规则来删除这个最小节点。比如图中的删除节点18。

<2>、图示:
二叉查找树的删除
<3>、算法:
class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        if(root == null){
            return null;
        }
        TreeNode node=root;
        TreeNode lastNode=root;
            if(key < node.val){
                //删除节点的值小于当前节点,删除节点在左子树上 
                root.left=deleteNode(root.left,key);
            }else if(key > node.val){
                //删除节点的值大于当前节点,删除的节点在右子树上
                root.right= deleteNode(root.right,key);             
            }else{
                if(root.left ==null || root.right == null){
                    //无子节点或仅有一个子节点
                    root= root.left == null ? root.right:root.left;
                }else{
                    //同时有左节点和右节点
                    TreeNode cur = root.right;
                    while (cur.left !=null) {
                    //找到右分支的最小值
                        cur = cur.left;
                    }
                root.val = cur.val;
                root.right = deleteNode(root.right, cur.val);
                }
        }
        return root;
    }
}

4、中序遍历二叉查找树,可以输出有序的数据序列,时间复杂度是O(n)。

5、如果存储的两个对象键值相同时,解决方法:

  ①、通过链表和支持动态扩容的数组等数据结构,把值相同的数据都存储在同一个节点上。
  ②、每个节点仍然只存储一个数据。在查找插入位置的过程中,如果碰到一个节点的值,与要插入数据的值相同,就将这个要插入的数据放到这个节点的右子树,也就是说,把这个新插入的数据当作大于这个节点的值来处理。当要查找数据的时候,遇到值相同的节点,我们并不停止查找操作,而是继续在右子树中查找,直到遇到叶子节点,才停止。这样就可以把键值等于要查找值的所有节点都找出来。对于删除操作,也需要先查找到每个要删除的节点,然后再按前面讲的删除操作的方法,依次删除。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容