Java数据结构学习之树

一、树

1.1 概念

与线性表表示的一一对应的线性关系不同,树表示的是数据元素之间更为复杂的非线性关系。

直观来看,树是以分支关系定义的层次结构。 树在客观世界中广泛存在,如人类社会的族谱和各种社会组织机构都可以用树的形象来表示。

简单来说,树表示的是1对多的关系。

定义(逻辑结构):

树(Tree)是n( n>=0 )个结点的有限集合,没有结点的树称为空树,在任意一颗非空树中: 有且仅有一个特定的称为根(root)的结点 。

当n>1的时,其余结点可分为 m( m>0 ) 个互不相交的有限集T1,T2,…, Tm,其中每一个集合 Ti 本身又是一棵树,并且称之为根的子树。

注意:树的定义是一个递归定义,即在树的定义中又用到树的概念。

1.2 术语

(1)一个结点的子树的根,称为该结点的孩子(儿子),相应的该结点称为子树的根的父亲。

(2)没有孩子的结点称为树叶,又叫叶子结点 。(国外, nil叫叶子) 具有相同父亲的结点互为兄弟(同胞, 姐妹)。

(3)从结点n1 到 nk 的路径定义为节点 n1 n2 … nk 的一个序列,使得对于 1 <= i < k,节点 ni是 ni+1 的父亲。这条路径的长是为该路径上边的条数,即 k-1。从每一个结点到它自己有一条长为 0 的路径。注意,在一棵树中从根到每个结点恰好存在一条路径。 如果存在从n1到n2的一条路径,那么n1是n2的一位祖先 ,而n2是n1的一个后裔。如果n1 != n2,那么n1是n2的真祖先, 而n2是n1的真后裔。

(4)结点的层级从根开始定义,根为第一层,根的孩子为第二层。若某结点在第i层,则其孩子就在i+1层。(树有层级定义)

(5)对任意结点ni,ni的深度为从根到ni的唯一路径的长。因此,根的深度为0。(深度)

(6)一颗树的高等于它根的高。一颗树的深度等于它最深的树叶的深度; 该深度总是等于这棵树的高。

(7)性质:如果一棵树有n个结点,那么它有n-1条边。(为什么呢?)

每一结点都有一个边指向它(除了根节点)
每一条边都指向一个结点

(8) 概念: 度 (图这种数据结构) 对图这种数据结构: 每个结点的度: 一般指有几个结点和我这个结点相关

树这种数据结构: 度: 一般指有几个孩子

1.3 树的实现

怎么通过代码来模拟一个树
集合类: 数据容器
数组 链表, 数组+链表
数据结构表现形式:树

1.3.1 用数组来实现一棵树?

如果非要用数组存储一棵树的话, 也可以, 不过会存在各种问题。

1.3.2 用链表实现一棵树?

如果用链表存储一棵树也会有一些问题( 1, 牺牲内存, 2, 多种结点类型)

1.3.3 树的转化

(1)经过转化的树比较容易存储: 这种根据下面特点转化的树 被称为 二叉树。

① 如果一个结点 有孩子, 那么让他的第一个孩子, 作为这个结点的left子结点。
②如果一个结点有兄弟结点, 那么让他的兄弟结点, 作为这个结点的right子结点。

1.4 二叉树

概念: 一个树, 每一个结点最多有两个孩子, 孩子有严格的左右之分

1.4.1 二叉树的性质

(1)二叉树具有以下重要性质:

①二叉树在第i层至多有2的(i-1)次方个节点
②层次为k的二叉树至多有2的k次方 - 1个节点

(2)对任何一颗二叉树T,如果其叶子节点数为n0 , 度为2的节点数为n2,则n0 = n2 + 1

(3)具有n个节点的完全二叉树,树的高度为log2n (向下取整)。

(4)如果对一颗有n个结点的完全二叉树的结点按层序从1开始编号,则对任意一结点有:

如果编号i为1,则该结点是二叉树的根;
如果编号i > 1,则其双亲结点编号为 parent(i) = i/2, 
若 2i > n 则该结点没有左孩子,否则其左孩子的编号为 2i,
若 2i + 1 > n 则该结点没有右孩子,否则其右孩子的编号为 2i + 1。

(5)二叉树的父子结点关系: 2倍 或者 2倍+1关系

–> 二叉树可以用数组存储 就是根据上述性质(但是一般在实际应用和开发中, 我们一般用链表存储二叉树)

1.4.2 二叉树的遍历

深度优先遍历: 栈

(1)先序遍历:先遍历根结点, 再遍历左子树, 再遍历右子树
(2)中序遍历:先遍历左子树, 再遍历根结点, 再遍历右子树
(3)后序遍历:先遍历左子树, 再遍历右子树, 再遍历根结点

广度优先遍历: 队列

树的广度优先遍历一般为层级遍历。(广度优先遍历–>图的广度遍历)

1.4.3 二叉树的建树

给一些序列(前中后序), 我们还原出一颗树原本的样子

Q1: 如果我们只知道前序,中序,后序中的某一种,能否构建出一棵二叉树?如果能,为什么?如果不能,试着举出反例。
答: 能构建一颗二叉树, 但是不能构建出一颗唯一的二叉树

Q2:如果我们只知道前序,中序,后序中的某两种,能否构建出一棵唯一的二叉树?如果能,为什么?如果不能,试着举出反例。

前序 + 中序 可以–> 前序可以确定根节点, 中序可以根据根节点划分左右子树
后序 + 中序 可以–> 后序可以确定根节点, 中序可以根据根节点划分左右子树
前序 + 后序, 不可以, 都只能确定根节点

二、BST(二叉查找树, 二分查找树, 二叉排序树)

就是在二叉树的基础上增减一个限定条件: 对树中每一个结点 它的左子树的结点比这个结点都小, 右子树上的结点比这个结点都大

2.1 代码实现

注意: 递归需要注意的事情

1, 递归的核心思想: 设计的时候不要考虑开始和结束是怎么回事, 抓住核心逻辑, 局部样本
2, 注意出口问题: 递归要有出口
3, 如果实现一个递归方法, 不要让这个方法被外界直接访问(没有语法问题, 只不过操作行为比较危险)
4, 一定要注意问题规模。

/**
 * @author: Mr.Du
 * @description: 二叉搜索树
 * @date: 2021/05/04 17:00
 */
public class MyBSTree<T extends Comparable<T>> {

    private Node root;//二叉搜索树根节点
    private int size;//二叉搜索树结点个数

    //添加结点
    public boolean add(T value) {
        // 对于一个二叉搜索树来讲我们不存储null: null不能比较大小
        if (value == null)
            throw new IllegalArgumentException("The param is null");
        //判断原本的树是否为空
        if (root == null) {
            // 如果原本的树是一个空树, 那么这个添加的元素就是根节点
            root = new Node(value, null, null);
            size++;
            return true;
        }

        //目前来看,树不空,值也不是null
        Node index = root;//比较结点
        Node indexF = null;//比较结点的父结点
        int com = 0;//比较value大小结果
        while (index != null) {
            // 把要存储的值, 和遍历结点作比较, 进一步确定相对于mid存储的位置
            com = index.value.compareTo(value);
            indexF = index;
            if (com > 0) {
                index = index.left;
            } else if (com < 0) {
                index = index.right;
            } else {
                // com = 0
                // value 和 index存储的值一样
                // 对于重复元素的处理方式
                //       理论上:
                //                1, 计数法:  对于每一个结点都额外维护一个参数, 记录这个元素的重复数量
                //                2, 拉链法: 在某个结点位置维护一个链表, 用一个链表代表一个结点
                //                3, 修正的BST: 如果比较的过程中发现了重复元素, 向左存储
                //       实际上:
                //             不让存
                return false;
            }
        }

        if (com > 0) {
            indexF.left = new Node(value, null, null);
        } else {
            indexF.right = new Node(value, null, null);
        }
        size++;
        return true;
    }

    //是否存在指定值
    public boolean contains(T value) {
        // 对于一个二叉搜索树来讲我们不存储null: null不能比较大小
        if (value == null)
            throw new IllegalArgumentException("The param is null");

        Node index = root;
        int com = 0;
        while (index != null) {
            com = value.compareTo(index.value);
            if (com > 0) {
                index = index.right;
            } else if (com < 0) {
                index = index.left;
            } else return true;
        }
        //如果代码走到这个位置, 意味着上述循环跳出条件是: index == null 意味着没有这个元素
        return false;
    }
    //递归方法删除二叉搜索树结点
    public boolean removeByRecursive(T value){
        int oldSize = size;
        root = removeByRe(root,value);
        return size<oldSize;
    }
    // 实现以root为根节点的子树上删除值为value的结点
    private Node removeByRe(Node root,T value){
        if (root == null) return null;
        int com = value.compareTo(root.value);
        if (com>0){
            //如果value存在, 在right子树上
            root.right = removeByRe(root.right,value);
            return root;
        }else if (com<0){
            //如果value存在, 在left子树上
            root.left = removeByRe(root.left,value);
            return root;
        }else{
            // 找到了要删除的结点
            if (root.left!=null&&root.right!=null){
                // 删除的结点是双分支结点
                // 获取right子树的最小值
                Node rightMin = root.right;
                while (rightMin.left!=null){
                    rightMin = rightMin.left;
                }
                //替换
                root.value = rightMin.value;
                // 接下来, 去right子树上删除rightMin(此时rightMin一定不是双分支结点)
                // 递归调用删除方法, 在这个root的right子树上删除这个替换值
                root.right = removeByRe(root.right,root.value);
                return root;
            }
            // 删除的是叶子或者单分支
            Node node = root.left != null? root.left : root.right;
            size--;
            return node;
        }
    }
    //非递归方法删除二叉搜索树结点
    public boolean removeByNonRecursive(T value) {
        //不存储null: null不能比较大小
        if (value == null)
            throw new IllegalArgumentException("The param is null");
        /*
        思路:
            先找到要删除的结点
            判断要删除的结点是不是双分支: 如果是双分支, 先替换
            删除单分支或者叶子
         */
        Node index = root;
        Node indexF = null;
        int com;
        while (index != null) {
            com = value.compareTo(index.value);
            if (com > 0) {
                indexF = index;
                index = index.right;
            } else if (com < 0) {
                indexF = index;
                index = index.left;
            } else
                break;
        }
        // indexF 是要删除结点的父结点
        // index 是找到的要删除的结点

        //如果index是null,没有包含删除的元素,返回false
        if (index == null)
            return false;

        //到这里,说明包含需要删除的元素
        if (index.left != null && index.right != null) {
            //去right子树找一个最小值, 替换这个删除结点
            Node rightMin = index.right;
            //替换结点的父结点
            Node rightMinF = index;
            //找index.right子树的最小值, 最left的元素
            while (rightMin.left != null) {
                rightMinF = rightMin;
                rightMin = rightMinF.left;
            }

            //到达这里:rightMin.left=null
            //用查找的right子树上的最小值, 替换这个要删除的双分支结点
            index.value = rightMin.value;
            //将替换结点设置为后面需要删除的单分支结点
            indexF = rightMinF;
            index = rightMin;
        }

        // 有可能原本就是叶子或者单分支
        // 也有可能双分支已经替换了, 现在要删除的是哪个替换了的, 叶子或者单分支

        // 必定是个叶子或者单分支: index
        // 同时我们还记录了index 的 父结点 indexF

        //寻找index的儿子结点ch:
        // 如果index是叶子 ,那么ch = null
        // 如果index是单分支, ch = 不为null单分支子结点
        Node ch = index.left != null ? index.left : index.right;

        // 如果删除的是根节点, 并且根节点还是个单分支的结点, 对于上述代码会导致midF = null
        if (indexF == null) {
            root = ch;
            size--;
            return true;
        }

        //删除结点
        if (indexF.left == index) {
            indexF.left = ch;
        } else
            indexF.right = ch;
        size--;
        return true;
    }

    //用栈来实现先中后序遍历:
    //①先序
    public List<T> preOrder() {
        //保存遍历结果
        List<T> list = new ArrayList<>();
        //用栈来临时存储结点
        MyLinkedStack<Node> stack = new MyLinkedStack<>();
        //根节点入栈
        stack.push(root);
        while (!stack.isEmpty()) {
            Node pop = stack.pop();
            list.add(pop.value);
            if (pop.right != null)
                stack.push(pop.right);
            if (pop.left != null)
                stack.push(pop.left);
        }
        return list;
    }

    //②中序
    public List<T> inOrder() {
        Stack<Node> stack = new Stack<>();
        List<T> list = new ArrayList<>();
        Node index = root;
        while (index != null || !stack.empty()) {
            while (index != null) {
                stack.push(index);
                index = index.left;
            }
            Node pop = stack.pop();
            list.add(pop.value);
            index = pop.right;
        }
        return list;
    }

    //③后序
    public List<T> postOrder() {
        Stack<Node> stack = new Stack<>();
        List<T> list = new ArrayList<>();
        stack.push(root);
        while (!stack.empty()) {
            Node pop = stack.pop();
            list.add(0, pop.value);
            if (pop.left != null)
                stack.push(pop.left);
            if (pop.right != null)
                stack.push(pop.right);
        }
        return list;
    }

    //用递归来实现先中后序遍历
    //①先序
    public List<T> preOrderRecursive() {
        List<T> list = new LinkedList<>();
        preRecursive(list, root);
        return list;
    }

    // 先序:根 左 右
    private void preRecursive(List<T> list, Node node) {
        if (node == null)
            return;
        list.add(node.value);
        preRecursive(list, node.left);
        preRecursive(list, node.right);
    }

    //②中序
    public List<T> inOrderRecursive() {
        List<T> list = new LinkedList<>();
        inRecursive(list, root);
        return list;
    }

    // 中序遍历: 左 根 右
    private void inRecursive(List<T> list, Node node) {
        if (node == null)
            return;
        inRecursive(list, node.left);
        list.add(node.value);
        inRecursive(list, node.right);
    }

    //③ 后序遍历
    public List<T> postOrderRecursive() {
        List<T> list = new LinkedList<>();
        postRecursive(list, root);
        return list;
    }

    // 后序: 左 右 根
    private void postRecursive(List<T> list, Node node) {
        if (node == null)
            return;
        preRecursive(list, node.left);
        preRecursive(list, node.right);
        list.add(node.value);
    }

    // 层级: 广度优先搜索(BFS)
    public List<T> levOrder() {
        List<T> list = new ArrayList<>();
        Queue<Node> queue = new LinkedBlockingQueue<>();

        //根节点入队列
        queue.offer(root);
        while (!queue.isEmpty()) {
            //出队列元素
            Node poll = queue.poll();
            //遍历
            list.add(poll.value);
            //把出队列元素的左右子节点入队列
            if (poll.left != null)
                queue.offer(poll.left);
            if (poll.right != null)
                queue.offer(poll.right);
        }
        return list;
    }

    //  建树: 给定前中序, 或者给定中后序,  构建出一棵二叉树

    //  中序 [-50, -25, -20, -10, -5, 1, 2, 7, 10, 25, 30, 100]
    //  后序 [-20, -25, -50, -10, -5, 7, 2, 25, 30, 100, 10, 1]
    public Node buildTreeByInAndPostOrder(List<T> inOrder, List<T> postOrder) {
        Node treeRoot = buildTreeByInAndPostOrder2(inOrder, postOrder);
        return treeRoot;
    }

    private Node buildTreeByInAndPostOrder2(List<T> inOrder, List<T> postOrder) {
        if (inOrder.size() == 0) return null;
        if (inOrder.size() == 1) return new Node(inOrder.get(0), null, null);
        //找根结点: 后序的最后一个元素
        T rootValue = postOrder.get(postOrder.size() - 1);
        //获得根节点在中序的位置
        int rootAtInOrderIndex = inOrder.indexOf(rootValue);

        // 左子树的中序(中序中切割): 0 ~ rootAtInOrderIndex-1
        // 左子树的后序(后序中切割): 0 ~ rootAtInOrderIndex -1

        // 右子树的中序(中序中切割): rootAtInOrderIndex + 1 ~ size -1
        // 右子树的后序(后序中切割): rootAtInOrderIndex ~ size - 2

        //左子树
        //subList():左闭右开
        List<T> leftInOrder = inOrder.subList(0, rootAtInOrderIndex);
        List<T> leftPostOrder = postOrder.subList(0, rootAtInOrderIndex);

        //右子树
        //subList():左闭右开
        List<T> rightInOrder = inOrder.subList(rootAtInOrderIndex + 1, inOrder.size());
        List<T> rightPostOrder = postOrder.subList(rootAtInOrderIndex, postOrder.size() - 1);
        //构建这次递归的根节点
        Node node = new Node(rootValue, null, null);
        // 用递归方法处理, 获得左子树
        node.left = buildTreeByInAndPostOrder2(leftInOrder, leftPostOrder);
        // 用递归方法处理, 获得右子树
        node.right = buildTreeByInAndPostOrder2(rightInOrder, rightPostOrder);

        return node;
    }

    //  中序 [-50, -25, -20, -10, -5, 1, 2, 7, 10, 25, 30, 100]
    //  前序 1  -5  -10  -50  -25  -20   10  2  7  100  30  25
    public Node buildTreeByInAndPreOrder(List<T> inOrder, List<T> preOrder) {
        Node treeRoot = buildTreeByInAndPreOrder2(inOrder, preOrder);
        return treeRoot;
    }

    private Node buildTreeByInAndPreOrder2(List<T> inOrder, List<T> preOrder) {
        if (inOrder.size() == 0) return null;
        if (inOrder.size() == 1) return new Node(inOrder.get(0), null, null);

        T rootValue = preOrder.get(0);
        int rootAtInOrderIndex = inOrder.indexOf(rootValue);

        //左子树
        //subList():左闭右开
        List<T> leftInOrder = inOrder.subList(0, rootAtInOrderIndex);
        List<T> leftPreOrder = preOrder.subList(1, rootAtInOrderIndex + 1);
        //右子树
        //subList():左闭右开
        List<T> rightInOrder = inOrder.subList(rootAtInOrderIndex+1,inOrder.size());
        List<T> rightPreOrder = preOrder.subList(rootAtInOrderIndex+1,preOrder.size());

        Node node = new Node(rootValue,null,null);
        node.left = buildTreeByInAndPreOrder2(leftInOrder,leftPreOrder);
        node.right = buildTreeByInAndPreOrder2(rightInOrder,rightPreOrder);
        return node;
    }

    //判空
    public boolean isEmpty() {
        return size == 0;
    }

    //返回结点个数
    public int size() {
        return size;
    }

    class Node {
        T value;
        Node left;
        Node right;

        public Node(T value, Node left, Node right) {
            this.value = value;
            this.left = left;
            this.right = right;
        }
    }
}

到此这篇关于Java数据结构学习之树的文章就介绍到这了,更多相关Java树内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java数据结构之链表、栈、队列、树的实现方法示例

    本文实例讲述了Java数据结构之链表.栈.队列.树的实现方法.分享给大家供大家参考,具体如下: 最近无意中翻到一本书,闲来无事写几行代码,实现几种常用的数据结构,以备后查. 一.线性表(链表) 1.节点定义 /**链表节点定义 * @author colonel * */ class Node { public int data; Node next=null; public Node(int data){ this.data=data; } } 2.链表操作类 /**链表操作类 * @auth

  • java 数据结构二叉树的实现代码

    1. 二叉树接口 public interface BinaryTreeInterface<T> { public T getRootData(); public int getHeight(); public int getNumberOfRoot(); public void clear(); public void setTree(T rootData); // 用rootData设置树 public void setTree(T rootData,BinaryTreeInterface

  • Java中二叉树数据结构的实现示例

    来看一个具体的习题实践: 题目 根据二叉树前序遍历序列例如:7,-7,8,#,#,-3,6,#,9,#,#,#,-5,#,#,构建二叉树,并且用前序.中序.后序进行遍历 代码 import java.util.Scanner; public class BinaryTree { public static String[] str; public static int count; /** * 静态内部类,定义二叉树节点 */ static class TreeNode { public Str

  • Java数据结构与算法之树(动力节点java学院整理)

    为什么使用树: 树结合了两种数据结构的有点:一种是有序数组,树在查找数据项的速度和在有序数组中查找一样快:另一种是链表,树在插入数据和删除数据项的速度和链表一样.既然这样,就要好好去学了.... (最主要讨论的是二叉树中的二叉搜索树,即一个节点的左子节点关键值小于这个节点,右子节点的关键值大于这个节点) 设计前的思考: 树-->元素(节点) class Node { public int iData ; public float fData ; public Node left ; public

  • java数据结构排序算法之树形选择排序详解

    本文实例讲述了java数据结构排序算法之树形选择排序.分享给大家供大家参考,具体如下: 这里我们就来说说选择类排序之一的排序:树形选择排序 在简单选择排序中,每次的比较都没有用到上次比较的结果,所以比较操作的时间复杂度是O(N^2),想要降低比较的次数,则需要把比较过程中的大小关系保存下来.树形选择排序是对简单选择排序的改进. 树形选择排序:又称锦标赛排序(Tournament Sort),是一种按照锦标赛的思想进行选择排序的方法.首先对n个记录的关键字进行两两比较,然后在n/2个较小者之间再进

  • Java数据结构之红黑树的真正理解

    真正的帮助大家理解红黑树: 一.红黑树所处数据结构的位置: 在JDK源码中, 有treeMap和JDK8的HashMap都用到了红黑树去存储 红黑树可以看成B树的一种: 从二叉树看,红黑树是一颗相对平衡的二叉树 二叉树-->搜索二叉树-->平衡搜索二叉树--> 红黑树 从N阶树看,红黑树就是一颗 2-3-4树 N阶树-->B(B-)树 故我提取出了红黑树部分的源码,去说明红黑树的理解 看之前,理解红黑树的几个特性,后面的操作都是为了让树符合红黑树的这几个特性,从而满足对查找效率的O

  • java数据结构之树基本概念解析及代码示例

    Java中树的存储结构实现 一.树 树与线性表.栈.队列等线性结构不同,树是一...节点与节点之间的父子关系,可以为每个节点增加一个parent域,用以记录该节点的父点 树是一种抽象数据类型(ADT)或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合.它是由n(n>0)个有限节点组成一个具有层次关系的集合.把 它叫做"树"是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的. 树定义和基本术语 定义 树(Tree)是n(n≥0)个结点的有限集T,并且当

  • Java数据结构学习之树

    一.树 1.1 概念 与线性表表示的一一对应的线性关系不同,树表示的是数据元素之间更为复杂的非线性关系. 直观来看,树是以分支关系定义的层次结构. 树在客观世界中广泛存在,如人类社会的族谱和各种社会组织机构都可以用树的形象来表示. 简单来说,树表示的是1对多的关系. 定义(逻辑结构): 树(Tree)是n( n>=0 )个结点的有限集合,没有结点的树称为空树,在任意一颗非空树中: 有且仅有一个特定的称为根(root)的结点 . 当n>1的时,其余结点可分为 m( m>0 ) 个互不相交的

  • Java数据结构学习之栈和队列

    一.栈 1.1 概述 Java为什么要有集合类: 临时存储数据. 链表的本质: 对象间通过持有和引用关系互相关联起来. 线性表: 普通线性表, 操作受限线性表(某些操作受到限制 --> 某一个线性表它的增删改操作受到限制) --> 栈 & 队列 1.1.1 线性表的概念 (1)线性表:n个数据元素的有序序列. ①首先,线性表中元素的个数是有限的. ②其次,线性表中元素是有序的. (2)那这个"序"指的是什么呢? ①除表头和表尾元素外,其它元素都有唯一前驱和唯一后继,

  • Java数据结构学习之二叉树

    1 背景知识:树(Tree) 在之前的笔记中,我们介绍的链表.栈.队列.数组和字符串都是以线性结构来组织数据的.本篇笔记要介绍的树采用的是树状结构,这是一种非线性的数据组织形式. 树结构由节点和边构成,且不存在环.我们曾在线性表型的数据结构中介绍过循环链表和循环队列,这两种数据结构使得存储容器中的元素形成一个闭环,具体可参看"数据结构学习笔记"系列的相关博文,链接贴在下面: 链表:https://www.jb51.net/article/215278.htm 队列:https://ww

  • Java数据结构之线段树详解

    目录 介绍 代码实现 线段树构建 区间查询 更新 总结 介绍 线段树(又名区间树)也是一种二叉树,每个节点的值等于左右孩子节点值的和,线段树示例图如下 以求和为例,根节点表示区间0-5的和,左孩子表示区间0-2的和,右孩子表示区间3-5的和,依次类推. 代码实现 /** * 使用数组实现线段树 */ public class SegmentTree<E> { private Node[] data; private int size; private Merger<E> merge

  • Java数据结构之线段树的原理与实现

    目录 简介 实现思路 节点定义 构建线段树 求解区间和 更新线段树 简介 线段树是一种二叉搜索树,是用来维护区间信息的数据结构.可以在O(logN)的时间复杂度内实现单点修改.区间修改.区间查询(区间求和,求区间最大值,求区间最小值)等操作.接下来我以实现区间求和为例子来讲解线段树(最大值和最小值与求和实现方式几乎无异),假设存在一个数组[1,4,6,3,9]. 实现思路 从线段树的定义,我们首先需要定义一个树节点,节点包含区间和(23),区间([1-5]),左节点,右节点等.(如果要实现求区间

  • Python描述数据结构学习之哈夫曼树篇

    前言 本篇章主要介绍哈夫曼树及哈夫曼编码,包括哈夫曼树的一些基本概念.构造.代码实现以及哈夫曼编码,并用Python实现. 1. 基本概念 哈夫曼树(Huffman(Huffman(Huffman Tree)Tree)Tree),又称为最优二叉树,指的是带权路径长度最小的二叉树.树的带权路径常记作: 其中,nnn为树中叶子结点的数目,wkw_kwk​为第kkk个叶子结点的权值,lkl_klk​为第kkk个叶子结点与根结点的路径长度. 带权路径长度是带权结点和根结点之间的路径长度与该结点的权值的乘

  • Java数据结构之哈夫曼树概述及实现

    一.与哈夫曼树相关的概念 概念 含义 1. 路径 从树中一个结点到另一个结点的分支所构成的路线 2. 路径长度 路径上的分支数目 3. 树的路径长度 长度从根到每个结点的路径长度之和 4. 带权路径长度 结点具有权值, 从该结点到根之间的路径长度乘以结点的权值, 就是该结点的带权路径长度 5. 树的带权路径长度 树中所有叶子结点的带权路径长度之和 二.什么是哈夫曼树 定义: 给定n个权值作为n个叶子结点, 构造出的一棵带权路径长度(WPL)最短的二叉树,叫哈夫曼树(), 也被称为最最优二叉树.

随机推荐