数据结构五:树+堆(DataWhale系列)

简介: Datawhale 系列数据结构这一部分内容大多参考网上前辈的分享,由于,当时没有保存浏览记录,所以找不到链接。如果侵权,联系我删除,或者加您的原帖链接在头部。谢谢!!!Task5.1树5.1.

Datawhale 系列数据结构

这一部分内容大多参考网上前辈的分享,由于,当时没有保存浏览记录,所以找不到链接。如果侵权,联系我删除,或者加您的原帖链接在头部。谢谢!!!

Task5.1树

5.1.1实现一个二叉查找树(支持插入,删除,查找操作)

public class BSTree<T extends Comparable<T>> {

    private BSTNode<T> mRoot;    // 根结点

    @SuppressWarnings("hiding")
    public class BSTNode<T extends Comparable<T>> {
        T key;                // 关键字(键值)
        BSTNode<T> left;    // 左孩子
        BSTNode<T> right;    // 右孩子
        BSTNode<T> parent;    // 父结点

        public BSTNode(T key, BSTNode<T> parent, BSTNode<T> left, BSTNode<T> right) {
            this.key = key;
            this.parent = parent;
            this.left = left;
            this.right = right;
        }

        public T getKey() {
            return key;
        }

        public String toString() {
            return "key:"+key;
        }
    }

    public BSTree() {
        mRoot=null;
    }

    /*
     * 前序遍历"二叉树"
     */
    private void preOrder(BSTNode<T> tree) {
        if(tree != null) {
            System.out.print(tree.key+" ");
            preOrder(tree.left);
            preOrder(tree.right);
        }
    }

    public void preOrder() {
        preOrder(mRoot);
    }

    /*
     * 中序遍历"二叉树"
     */
    private void inOrder(BSTNode<T> tree) {
        if(tree != null) {
            inOrder(tree.left);
            System.out.print(tree.key+" ");
            inOrder(tree.right);
        }
    }

    public void inOrder() {
        inOrder(mRoot);
    }


    /*
     * 后序遍历"二叉树"
     */
    private void postOrder(BSTNode<T> tree) {
        if(tree != null)
        {
            postOrder(tree.left);
            postOrder(tree.right);
            System.out.print(tree.key+" ");
        }
    }

    public void postOrder() {
        postOrder(mRoot);
    }


    /*
     * (递归实现)查找"二叉树x"中键值为key的节点
     */
    private BSTNode<T> search(BSTNode<T> x, T key) {
        if (x==null)
            return x;

        int cmp = key.compareTo(x.key);
        if (cmp < 0)
            return search(x.left, key);
        else if (cmp > 0)
            return search(x.right, key);
        else
            return x;
    }

    public BSTNode<T> search(T key) {
        return search(mRoot, key);
    }

    /*
     * (非递归实现)查找"二叉树x"中键值为key的节点
     */
    private BSTNode<T> iterativeSearch(BSTNode<T> x, T key) {
        while (x!=null) {
            int cmp = key.compareTo(x.key);

            if (cmp < 0) 
                x = x.left;
            else if (cmp > 0) 
                x = x.right;
            else
                return x;
        }

        return x;
    }

    public BSTNode<T> iterativeSearch(T key) {
        return iterativeSearch(mRoot, key);
    }

    /* 
     * 查找最小结点:返回tree为根结点的二叉树的最小结点。
     */
    private BSTNode<T> minimum(BSTNode<T> tree) {
        if (tree == null)
            return null;

        while(tree.left != null)
            tree = tree.left;
        return tree;
    }

    public T minimum() {
        BSTNode<T> p = minimum(mRoot);
        if (p != null)
            return p.key;

        return null;
    }
     
    /* 
     * 查找最大结点:返回tree为根结点的二叉树的最大结点。
     */
    private BSTNode<T> maximum(BSTNode<T> tree) {
        if (tree == null)
            return null;

        while(tree.right != null)
            tree = tree.right;
        return tree;
    }

    public T maximum() {
        BSTNode<T> p = maximum(mRoot);
        if (p != null)
            return p.key;

        return null;
    }

    /* 
     * 找结点(x)的后继结点。即,查找"二叉树中数据值大于该结点"的"最小结点"。
     */
    public BSTNode<T> successor(BSTNode<T> x) {
        // 如果x存在右孩子,则"x的后继结点"为 "以其右孩子为根的子树的最小结点"。
        if (x.right != null)
            return minimum(x.right);

        // 如果x没有右孩子。则x有以下两种可能:
        // (01) x是"一个左孩子",则"x的后继结点"为 "它的父结点"。
        // (02) x是"一个右孩子",则查找"x的最低的父结点,并且该父结点要具有左孩子",找到的这个"最低的父结点"就是"x的后继结点"。
        BSTNode<T> y = x.parent;
        while ((y!=null) && (x==y.right)) {
            x = y;
            y = y.parent;
        }

        return y;
    }
     
    /* 
     * 找结点(x)的前驱结点。即,查找"二叉树中数据值小于该结点"的"最大结点"。
     */
    public BSTNode<T> predecessor(BSTNode<T> x) {
        // 如果x存在左孩子,则"x的前驱结点"为 "以其左孩子为根的子树的最大结点"。
        if (x.left != null)
            return maximum(x.left);

        // 如果x没有左孩子。则x有以下两种可能:
        // (01) x是"一个右孩子",则"x的前驱结点"为 "它的父结点"。
        // (01) x是"一个左孩子",则查找"x的最低的父结点,并且该父结点要具有右孩子",找到的这个"最低的父结点"就是"x的前驱结点"。
        BSTNode<T> y = x.parent;
        while ((y!=null) && (x==y.left)) {
            x = y;
            y = y.parent;
        }

        return y;
    }

    /* 
     * 将结点插入到二叉树中
     *
     * 参数说明:
     *     tree 二叉树的
     *     z 插入的结点
     */
    private void insert(BSTree<T> bst, BSTNode<T> z) {
        int cmp;
        BSTNode<T> y = null;
        BSTNode<T> x = bst.mRoot;

        // 查找z的插入位置
        while (x != null) {
            y = x;
            cmp = z.key.compareTo(x.key);
            if (cmp < 0)
                x = x.left;
            else
                x = x.right;
        }

        z.parent = y;
        if (y==null)
            bst.mRoot = z;
        else {
            cmp = z.key.compareTo(y.key);
            if (cmp < 0)
                y.left = z;
            else
                y.right = z;
        }
    }

    /* 
     * 新建结点(key),并将其插入到二叉树中
     *
     * 参数说明:
     *     tree 二叉树的根结点
     *     key 插入结点的键值
     */
    public void insert(T key) {
        BSTNode<T> z=new BSTNode<T>(key,null,null,null);

        // 如果新建结点失败,则返回。
        if (z != null)
            insert(this, z);
    }

    /* 
     * 删除结点(z),并返回被删除的结点
     *
     * 参数说明:
     *     bst 二叉树
     *     z 删除的结点
     */
    private BSTNode<T> remove(BSTree<T> bst, BSTNode<T> z) {
        BSTNode<T> x=null;
        BSTNode<T> y=null;

        if ((z.left == null) || (z.right == null) )
            y = z;
        else
            y = successor(z);

        if (y.left != null)
            x = y.left;
        else
            x = y.right;

        if (x != null)
            x.parent = y.parent;

        if (y.parent == null)
            bst.mRoot = x;
        else if (y == y.parent.left)
            y.parent.left = x;
        else
            y.parent.right = x;

        if (y != z) 
            z.key = y.key;

        return y;
    }

    /* 
     * 删除结点(z),并返回被删除的结点
     *
     * 参数说明:
     *     tree 二叉树的根结点
     *     z 删除的结点
     */
    public void remove(T key) {
        BSTNode<T> z, node; 

        if ((z = search(mRoot, key)) != null)
            if ( (node = remove(this, z)) != null)
                node = null;
    }

    /*
     * 销毁二叉树
     */
    private void destroy(BSTNode<T> tree) {
        if (tree==null)
            return ;

        if (tree.left != null)
            destroy(tree.left);
        if (tree.right != null)
            destroy(tree.right);

        tree=null;
    }

    public void clear() {
        destroy(mRoot);
        mRoot = null;
    }

    /*
     * 打印"二叉查找树"
     *
     * key        -- 节点的键值 
     * direction  --  0,表示该节点是根节点;
     *               -1,表示该节点是它的父结点的左孩子;
     *                1,表示该节点是它的父结点的右孩子。
     */
    private void print(BSTNode<T> tree, T key, int direction) {

        if(tree != null) {

            if(direction==0)    // tree是根节点
                System.out.printf("%2d is root\n", tree.key);
            else                // tree是分支节点
                System.out.printf("%2d is %2d's %6s child\n", tree.key, key, direction==1?"right" : "left");

            print(tree.left, tree.key, -1);
            print(tree.right,tree.key,  1);
        }
    }

    public void print() {
        if (mRoot != null)
            print(mRoot, mRoot.key, 0);
    }
}

5.1.2 实现查找二叉查找树中某个节点的后继,前驱节点

/**
* 前驱元素
* **/
public BSTreeNode<T> Pred(BSTreeNode<T> node) {
    if (node.left != null) {
        return Max(node.left);
    }
    BSTreeNode<T> parent = node.parent;
    while (parent != null && node != parent.right) {
        node = parent;
        parent = node.parent;
    }
    return parent;
}

/**
* 后继元素
* **/
public BSTreeNode<T> Succ(BSTreeNode<T> node) {
    if (node.right != null) {
        return Min(node.right);
    }
    BSTreeNode<T> parent = node.parent;
    while (parent != null && node != parent.left) {
        node = parent;
        parent = node.parent;
    }
    return parent;
}

5.1.3 实现二叉树前,中,后序以及层次遍历

/*
*在5.1.1中已经实现
*/

5.1.4 练习:翻转二叉树

class Solution{
    public TreeNode invertTree(TreeNode root){
        if(root==null)
            return root;
        TreeNode temp=root.left;
        root.left=root.right;
        root.right=temp;
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }
}

5.1.5 二叉树的最大深度

class Solution {
    public int maxDepth(TreeNode root) {
        if(root==null) return 0;
        return 1+Math.max(maxDepth(root.left),maxDepth(root.right));
        
    }

5.1.6 验证二叉查找树

class Solution {
    private TreeNode pre = null;
    public boolean isValidBST(TreeNode root) {
       return helper(root);
    }    
    private boolean helper(TreeNode root){
         if(root == null)
            return true;
         if(!helper(root.left))
             return false;
         if(pre != null && pre.val >= root.val)
             return false;
         pre = root;        
         return helper(root.right);
    }
}

TASK5.2 堆

5.2.1 实现一个小顶堆,大顶堆

public class MaxHeap<T extends Comparable<T>> {
    private List<T> mHeap; // 存放元素的动态数组
 
    public MaxHeap() {
        this.mHeap = new ArrayList<>();
    }
    /**
     * 大顶堆的向上调整算法(添加节点的时候调用) 注:数组实现的堆中,第N个节点的左孩子的索引值是(2N+1),右孩子的索引是(2N+2)。
     * 
     * @param start
     *            -- 被上调节点的起始位置(一般为数组中最后一个元素的索引)
     */
    protected void filterup(int start) {
 
        int c = start; // 需要调整的节点的初始位置
        int p = (c - 1) / 2; // 当前节点的父节点的位置
        T tmp = mHeap.get(c); // 被调整节点的值
 
        while (c > 0) {
            // 父节点的值和被调整节点的值进行比较
            int cmp = mHeap.get(p).compareTo(tmp);
            if (cmp >= 0) {
                // 父节点大
                break;
            } else {
                // 被调整节点的值大,交换
                mHeap.set(c, mHeap.get(p));
                c = p;
                p = (c - 1) / 2;
            }
        }
        // 找到被调整节点的最终位置了
        mHeap.set(c, tmp);
    }
 
    /**
     * 大顶堆的向下调整算法(删除节点的时候需要调用来调整大顶堆)
     * 注:数组实现的堆中,第N个节点的左孩子的索引值是(2N+1),右孩子的索引是(2N+2)。
     * 
     * @param start
     *            -- 被下调节点的起始位置(一般为0,表示从第1个开始)
     * @param end
     *            -- 截至范围(一般为数组中最后一个元素的索引)
     */
    protected void filterdown(int start, int end) {
 
        int c = start; // 被下调节点的初始位置
        int l = 2 * c + 1; // 左孩子节点的位置
        T tmp = mHeap.get(c); // 当前节点的值(大小)
 
        while (l <= end) {
            // 当前节点的左右节点进行比较
            int cmp = mHeap.get(l).compareTo(mHeap.get(l + 1));
            // 取大的
            if (l < end && cmp < 0) {
                l++;
            }
            // 当前节点和大的那个再比较一下
            cmp = tmp.compareTo(mHeap.get(l));
            if (cmp >= 0) {
                // 当前节点大,不用动
                break;
            } else {
                // 当前节点小,交换
                mHeap.set(c, mHeap.get(l));
                c = l; // 更新当前节点的位置
                l = 2 * c + 1; // 更新当前节点的左孩子位置
            }
        }
        mHeap.set(c, tmp);
    }
 
    /**
     * 向大顶堆中插入新元素
     * 
     * @param data
     */
    public void insert(T data) {
 
        int insertIndex = mHeap.size(); // 获取插入的位置
        // 将新元素插入到数组尾部
        mHeap.add(data);
        // 调用filterup函数,调整大顶堆
        filterup(insertIndex);
    }
    /**
     * 删除大顶堆中的data节点
     * 
     * @param data
     * @return 返回-1表示出错, 返回0表示删除成功
     */
    public int remove(T data) {
 
        // 大顶堆空
        if (mHeap.isEmpty()) {
            return -1;
        }
 
        // 获取data在数组中的索引
        int index = mHeap.indexOf(data);
        if (index == -1) {
            return -1;
        }
 
        // 堆中元素的个数
        int size = mHeap.size();
        // 删除了data元素,需要用最后一个元素填补,然后调用filterdown算法进行调整
        mHeap.set(index, mHeap.get(size - 1)); // 用最后一个元素填补
        mHeap.remove(size - 1); // 删除最后一个元素
 
        if (mHeap.size() > 1 && index < mHeap.size()) {
            // 调整成大顶堆
            filterdown(index, mHeap.size() - 1);
        }
        return 0;
    }
    
    
    @Override
    public String toString() {
        
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < mHeap.size(); i++) {
            sb.append(mHeap.get(i) + " ");
        }
        return sb.toString();
    }
    
    public static void main(String[] args) {
        
        int a[] = {10, 40 ,30, 60, 90, 70, 20, 50 ,80};
        
        //大顶堆
        MaxHeap<Integer> maxHeap = new MaxHeap<>();
        
        //添加元素
        System.out.println("=== 依次添加元素:");
        for(int i = 0; i < a.length; i++) {
            System.out.println(a[i]);
            maxHeap.insert(a[i]);
        }
        
        //生成的大顶堆
        System.out.println("=== 生成的大顶堆:");
        System.out.println(maxHeap);
        
        //添加新元素85
        int data = 85;
        maxHeap.insert(data);
        System.out.println("=== 添加新元素" + data + "之后的大顶堆:");
        System.out.println(maxHeap);
        
        //删除元素90
        data = 90;
        maxHeap.remove(data);
        System.out.println("=== 删除元素" + data + "之后的大顶堆:");
        System.out.println(maxHeap);
    }
}

5.2.2 实现优先级队列

/**
 * 优先队列类(最大优先队列)
 */
public class PriorityHeap {

    // ------------------------------ Instance Variables

    private int[] arr;
    private int size;

    // ------------------------------ Constructors

    /**
     * 优先队列数组默认大小为64
     */
    public PriorityHeap() {
        this(64);
    }

    public PriorityHeap(int initSize) {
        if (initSize <= 0) {
            initSize = 64;
        }
        this.arr = new int[initSize];
        this.size = 0;
    }

    // ------------------------------ Public methods

    public int max() {
        return this.arr[0];
    }

    public int maxAndRemove() {
        int t = max();

        this.arr[0] = this.arr[--size];
        sink(0, this.arr[0]);
        return t;
    }
    public void add(int data) {
        resize(1);
        this.arr[size++] = data;
        pop(size - 1, data);
    }

    // ------------------------------ Private methods

    /**
     * key下沉方法
     */
    private void sink(int i, int key) {
        while (2 * i <= this.size - 1) {
            int child = 2 * i;
            if (child < this.size - 1 && this.arr[child] < this.arr[child + 1]) {
                child++;
            }
            if (this.arr[i] >= this.arr[child]) {
                break;
            }

            swap(i, child);
            i = child;
        }
    }

    /**
     * key上浮方法
     */
    private void pop(int i, int key) {
        while (i > 0) {
            int parent = i / 2;
            if (this.arr[i] <= this.arr[parent]) {
                break;
            }
            swap(i, parent);
            i = parent;
        }
    }

    /**
     * 重新调整数组大小
     */
    private void resize(int increaseSize) {
        if ((this.size + increaseSize) > this.arr.length) {
            int newSize = (this.size + increaseSize) > 2 * this.arr.length ? (this.size + increaseSize) : 2 * this.arr.length;
            int[] t = this.arr;

            this.arr = Arrays.copyOf(t, newSize);
        }
    }

    /**
     * Swaps arr[a] with arr[b].
     */
    private void swap(int a, int b) {
        int t = this.arr[a];
        this.arr[a] = this.arr[b];
        this.arr[b] = t;
    }
}

5.2.3 实现堆排序

/*堆排序分为三个步骤:
*    创建最大堆
*    确保最大堆中父节点的值比子节点的值都大
*    将根节点与最后一个叶子节点比较,择其大者剔除出堆,再重复第2、3步。
*第二步是整个堆排序的关键。
*/
public static void maxHeapify(int[] array, int heapsize, int i){
    int l = 2*i + 1;
    int r = 2*i + 2;
    int large = i;
    if (l < heapsize && array[i] < array[l]) {
        large = l;
    }else {
        large = i;
    }
    if (r < heapsize && array[large] < array[r]) {
        large = r;
    }
    if (large != i) {
        int temp = array[i];
        array[i] = array[large];
        array[large] = temp;
        //因为将最大值和父节点交换了位置,新的子节点并不能保证一定是比它的子节点大
        //所以需要递归,确定交换的子节点比它的子节点都大
        //而没有动的子节点是不需要进行递归的,因为它的数值没有变,如果之前满足最大堆条件,现在就还是满足的
        maxHeapify(array, heapsize, large);
    }
}
//创建堆
public static void buildMaxHeap(int[] array){
    int heapsize = array.length;
    for (int i = heapsize/2; i >= 0; i--) {
        maxHeapify(array,heapsize,i);
    }
}
public static void heapSort(int[] array){
    int heapsize = array.length;
    for (int i = heapsize - 1; i > 0; i--) {
        if (array[i] < array[0]) {
            int temp = array[0];
            array[0] = array[i];
            array[i] = temp;
            heapsize --;
            maxHeapify(array, heapsize, 0);
        }
    }
}

5.2.4 利用有限集队列合并K个有序数组

class Solution {
    public ListNode mergeKLists(ListNode[] lists){
        if(lists.length == 0)
            return null;
        if(lists.length == 1)
            return lists[0];
        if(lists.length == 2){
           return mergeTwoLists(lists[0],lists[1]);
        }

        int mid = lists.length/2;
        ListNode[] l1 = new ListNode[mid];
        for(int i = 0; i < mid; i++){
            l1[i] = lists[i];
        }

        ListNode[] l2 = new ListNode[lists.length-mid];
        for(int i = mid,j=0; i < lists.length; i++,j++){
            l2[j] = lists[i];
        }

        return mergeTwoLists(mergeKLists(l1),mergeKLists(l2));

    }
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) return l2;
        if (l2 == null) return l1;

        ListNode head = null;
        if (l1.val <= l2.val){
            head = l1;
            head.next = mergeTwoLists(l1.next, l2);
        } else {
            head = l2;
            head.next = mergeTwoLists(l1, l2.next);
        }
        return head;
    }
}

5.2.5 :求一组动态数据集合的最大Top K

/*
*不太明白这道题的具体意思,如果是需要求一组数据的Top K,
*那直接,最大堆排序,然后取TopK加入到一个list中返回,就行
*/
 public static void maxHeapify(int[] array, int size, int i) {
        int left = 2 * i + 1;
        int right = 2 * i + 2;
        int small = i;
        if (left < size) {
            if (array[small] > array[left]) {
                small = left;
            }
        }
        if (right < size) {
            if (array[small] > array[right]) {
                small = right;
            }
        }
        if (small != i) {
            int temp = array[small];
            array[small] = array[i];
            array[i] = temp;
            maxHeapify(array, size, small);
        }
    }

    public static void buildHeap(int[] array, int size) {
        for (int i = size - 1; i >= 0; i--) {
            maxHeapify(array, size, i);
        }
    }
    
    public static List findKByHeap(int[] array, int k) {
        buildHeap(array, k);
        for (int i = k + 1; i < array.length; i++) {
            if (array[i] > array[0]) {
                int temp = array[i];
                array[i] = array[0];
                array[0] = temp;
                maxHeapify(array, k, 0);
            }
        }
        List list=new ArrayList();
        for(int i =0;i<k;i++){
            list.add(array[i])
        }
        return list;
    }

5.2.6 练习:路径总和

class Solution {
    public boolean hasPathSum(TreeNode root, int sum) {
        if(root == null) return false;
        
        int t=sum-root.val;
        
       if(root.left==null && root.right==null)
           return t==0 ? true : false;
        
        return hasPathSum(root.left,t) || hasPathSum(root.right,t);
        
    }
}
目录
相关文章
|
18天前
|
存储 JavaScript 前端开发
为什么基础数据类型存放在栈中,而引用数据类型存放在堆中?
为什么基础数据类型存放在栈中,而引用数据类型存放在堆中?
54 1
|
20天前
|
存储 算法 关系型数据库
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
这篇文章主要介绍了多路查找树的基本概念,包括二叉树的局限性、多叉树的优化、B树及其变体(如2-3树、B+树、B*树)的特点和应用,旨在帮助读者理解这些数据结构在文件系统和数据库系统中的重要性和效率。
14 0
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
|
17天前
|
Java C++
【数据结构】探索红黑树的奥秘:自平衡原理图解及与二叉查找树的比较
本文深入解析红黑树的自平衡原理,介绍其五大原则,并通过图解和代码示例展示其内部机制。同时,对比红黑树与二叉查找树的性能差异,帮助读者更好地理解这两种数据结构的特点和应用场景。
22 0
|
20天前
|
存储 算法
探索数据结构:分支的世界之二叉树与堆
探索数据结构:分支的世界之二叉树与堆
|
20天前
|
存储 算法
数据结构与算法学习十六:树的知识、二叉树、二叉树的遍历(前序、中序、后序、层次)、二叉树的查找(前序、中序、后序、层次)、二叉树的删除
这篇文章主要介绍了树和二叉树的基础知识,包括树的存储方式、二叉树的定义、遍历方法(前序、中序、后序、层次遍历),以及二叉树的查找和删除操作。
16 0
|
21天前
|
存储 算法 Java
【用Java学习数据结构系列】用堆实现优先级队列
【用Java学习数据结构系列】用堆实现优先级队列
28 0
|
22天前
05(数据结构考研)树相关操作代码
05(数据结构考研)树相关操作代码
26 0
|
23天前
|
存储 算法 Java
数据结构和算法--分段树
数据结构和算法--分段树
11 0
|
24天前
|
存储 算法
【数据结构】二叉树——顺序结构——堆及其实现
【数据结构】二叉树——顺序结构——堆及其实现