java实现树_数据结构与算法

时间:2022-04-23 11:57:18

1.二叉树:n个具有相同类型的数据元素的有限集合;左子树-根-右子树;有序。

2.二叉树的度:最大的节点度数

3.满二叉树:所有分支节点均存在左子树和右子树,且所有叶子节点均在同一层上

4.完全二叉树:存在的节点与满二叉树的节点编号相同,叶子节点只存在最后一层及倒数第二层

5.非空二叉树的i层最多有 2【i-1】个节点

6.深度为k的二叉树至多有2【k】-1个节点

7.给出完全二叉树的节点数则完全二叉树的深度可以确定

8二叉树的顺序存储结构:

  完全二叉树可以存放在n个连续存储单元中(子节点与父节点之间的关系用下标进行表现)

/**Java中
 * treemap,treemap类是使用红黑树实现的,红黑树是一个自平衡的二叉搜索树
 * 自己实现可以采用顺序表或是链表结构进行树的存储
 */

public class HelloWorld{
    public static void main(String[] args){
        mytree_array mytree = new mytree_array(4);
        mytree.print();
        mytree.setNode(1,1,20);
        mytree.print();
        mytree.setNode(2,1,12);
        mytree.setNode(2,2,13);
        mytree.print();
        mytree.setNode(3,2,14);
        mytree.setNode(3,3,15);
        mytree.print();
        mytree.setNode(4,4,16);
        mytree.print();

    }
}

class mytree_array{
    //如果输入直接是完全二叉树的表现形式就失去意义了,这个地方应该从头构建树
    private int[] tree ;
    public mytree_array(int deep){
        //深度为k的二叉树至多节点数为2的k次方-1
        //数组第一个节点不存,方便计算
        this.tree = new int[(int)(Math.pow(2,deep))];
    }
    //按照层数和个数来插入节点
    public int setNode(int cengshu,int geshu,int data){
        //计算应该放置该节点的坐标
        int index = (int)(Math.pow(2,cengshu-1))-1+geshu;
//        System.out.print(index);
        //判断是否该位置已经有节点
        if (this.tree[index]!=0){
            System.out.print("already token");
            return -1;
        }
        //判断是否为根节点(根节点无父节点)
        if (index == 1){
            this.tree[index] = data;
            return 1;
        }
        int father;
        //判断是左孩子还是右孩子,根据坐标的奇偶
        if (index%2 == 0){
            father = index/2;
        }else {
            father = (index-1)/2;
        }
        //判断是否存在父节点
        if (this.tree[father] == 0){
            System.out.print("no father node");
            return -1;
        }else {
            this.tree[index] = data;
            return 1;
        }
    }

    public void print(){
        System.out.println(Arrays.toString(this.tree));
    }
}

  普通二叉树->补充为完全二叉树->按完全二叉树的方法进行存储(空间浪费较大)

9.二叉树的链表存储结构:

  二叉链表:数据域,左孩子指针域,右孩子指针域

  三叉链表:parent域,leftchild,rightchild

//之后的遍历应该是用不到查找parent的,在这里就实现二叉链表,三叉链表就是多加一个parent域
class mytree_Linkedlist{
    private Node head;
    class Node{
        int data;
        Node leftchild;
        Node rightchild;
        public Node(int num){
            this.data = num;
            this.leftchild = null;
            this.rightchild = null;
        }
    }
    public mytree_Linkedlist(){
        this.head = null;
    }
    
public void addNode(Node parent,int isleft,Node addnode){
    //输入是头结点且当且树为空
    if (parent == null){
        if (this.head == null){
            this.head = addnode;
        }else {
            System.out.print("already have root");
        }
    }else {
        //输入的是一般节点

        if ((isleft == 1) && (parent.leftchild == null)){
            parent.leftchild = addnode;
        }else {
            if (parent.rightchild == null){
                parent.rightchild = addnode;
            }else {
                System.out.print("already have rightchild");
            }
        }

    }

}
//链表打印二叉树则涉及到遍历算法,后续实现}

10.二叉树的遍历:

  递归遍历:栈实现,顺序唯一

                   先序遍历:根->左子树->右子树

                   中序遍历:左子树->根 ->右子树

                   后序遍历:右子树 -> 根 ->左子树

   层次遍历:从上到下逐层遍历,用队列操作,根入队->访问该节点->若有左孩子则入队->若有右孩子则入队

package java_practice;


import java.util.AbstractQueue;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;

/**Java中
 * treemap,treemap类是使用红黑树实现的,红黑树是一个自平衡的二叉搜索树
 * 自己实现可以采用顺序表或是链表结构进行树的存储
 */



class Helloworld
{
    public static void main(String[] args)
    {
        mytree_Linkedlist mytree = new mytree_Linkedlist();
        Node n1 = new Node(1);
        Node n2 = new Node(2);
        Node n3 = new Node(3);
        Node n4 = new Node(4);
        Node n5 = new Node(5);
        Node n6 = new Node(6);
        mytree.addNode(null,1,n1);
        mytree.addNode(n1,1,n2);
        mytree.addNode(n1,0,n3);
        mytree.addNode(n2,0,n4);
        mytree.addNode(n3,1,n5);
        mytree.addNode(n3,0,n6);
        mytree.preOrder(mytree.getRoot());
        System.out.print("\n");
        mytree.InOrder(mytree.getRoot());
        System.out.print("\n");
        mytree.PostOrder(mytree.getRoot());
        System.out.print("\n");
        mytree.LevelOrder();
    }
}

class Node{
    int data;
    Node leftchild;
    Node rightchild;
    public Node(int num){
        this.data = num;
        this.leftchild = null;
        this.rightchild = null;
    }
}

class mytree_Linkedlist{
    private Node head;
    public mytree_Linkedlist(){
        this.head = null;
    }

    public void addNode(Node parent,int isleft,Node addnode){
        //输入是头结点且当且树为空
        if (parent == null){
            if (this.head == null){
                this.head = addnode;
            }else {
                System.out.print("already have root");
            }
        }else {
            //输入的是一般节点

            if ((isleft == 1) && (parent.leftchild == null)){
                parent.leftchild = addnode;
            }else {
                if (parent.rightchild == null){
                    parent.rightchild = addnode;
                }else {
                    System.out.print("already have rightchild");
                }
            }

        }

    }

    public Node getRoot(){
        return this.head;
    }
    //链表打印二叉树
    //先序
    public void preOrder(Node temp){
        if (temp != null){
            System.out.print(temp.data);
            preOrder(temp.leftchild);
            preOrder(temp.rightchild);
        }
    }

    public void InOrder(Node temp){
        if (temp != null){
            InOrder(temp.leftchild);
            System.out.print(temp.data);
            InOrder(temp.rightchild);
        }
    }

    public void PostOrder(Node temp){
        if (temp != null){
            PostOrder(temp.leftchild);
            PostOrder(temp.rightchild);
            System.out.print(temp.data);
        }
    }

    //层次遍历,用队列思想,队列用java提供的LinkedList来做
    public void LevelOrder() {
        LinkedList<Node> que = new LinkedList<Node>();
        que.addLast(this.head);
        if(this.head == null){
            return;
        }
        //当队列不空时
        while (que.size()!=0){
            Node now = que.removeFirst();
            System.out.print(now.data);
            if (now.leftchild != null){
                que.addLast(now.leftchild);
            }
            if (now.rightchild != null){
                que.addLast(now.rightchild);
            }
        }
    }
}
//中序,从左子树返回时遇到节点就访问
public void printbystack_2(){
        Stack<Node> st = new Stack<Node>();

        Node temp = this.head;
        while ((temp!=null) || (!st.empty())){
            while (temp!=null){
                st.push(temp);
                temp = temp.leftchild;
            }
            if (!st.empty()){
                Node e = st.pop();
                System.out.print(e.data);
                temp = e.rightchild;
            }
        }
    }
//先序,在深入时遇到节点就访问
    public void printbystack_1(){
        Stack<Node> st = new Stack<Node>();

        Node temp = this.head;
        while ((temp!=null) || (!st.empty())){
            while (temp!=null){
                System.out.print(temp.data);
                st.push(temp);
                temp = temp.leftchild;
            }
            if (!st.empty()){
                Node e = st.pop();
                temp = e.rightchild;
            }
        }
    }
//后序遍历需要多用空间进行记录,从右子树返回时遇到节点就访问
    public static void printbystack_3(Node t) {
        Stack<Node> s = new Stack<Node>();
        Stack<Integer> s2 = new Stack<Integer>();
        Integer i = new Integer(1);
        while (t != null || !s.empty()) {
            while (t != null) {
                s.push(t);
                s2.push(new Integer(0));
                t = t.leftchild;
            }
            while (!s.empty() && s2.peek().equals(i)) {
                s2.pop();
                System.out.print(s.pop().data);
            }

            if (!s.empty()) {
                s2.pop();
                s2.push(new Integer(1));
                t = s.peek();
                t = t.rightchild;
            }
        }
    }

*二叉树的先序及中序,或是后序及中序可以唯一确定一棵二叉树;

先序+中序按照书上伪代码实现的。居然真的能直接运行。

 public void reConstructBinaryTreeCore(int[] pre, int[] in, int preStart, int preEnd, int inStart, int inEnd,Node tree) {
        //创建当前节点
        tree = new Node(pre[preStart]);
        int m = inStart;
        //在中序中定位根
        while (in[m] != pre[preStart]){
            m++;
        }
        //递归建立左子树
        if (m == inStart){
            tree.leftchild = null;
        }else {
            reConstructBinaryTreeCore(pre,in,preStart+1,preStart+m-inStart,inStart,m-1,tree.leftchild);
        }
        //递归建立右子树
        if (m == inEnd){
            tree.rightchild = null;
        }else {
            reConstructBinaryTreeCore(pre,in,preStart+m-inStart+1,preEnd,m+1,inEnd,tree.rightchild);
        }
    }
        int[] a = {1,2,4,3,5,6};
        int[] b = {2,4,1,5,3,6};
        mytree.reConstructBinaryTreeCore(a, b, 0, 5, 0, 5, mytree.getRoot());
        //输入分别是前序,中序,起始角标,个数,起始角标,个数,当前用于存储的节点;

中序加后序的过程是类似的,应该也可以用这个递归的思路进行。重点在于递归时脚标的确定。

 public void reConstructBinaryTreeCore(int[] pre, int[] in, int preStart, int preEnd, int inStart, int inEnd,Node tree) {
        tree = new Node(pre[preEnd]);
        int m = inStart;
        //在中序中定位根
        while (in[m] != pre[preEnd]){
            m++;
        }
        //递归建立左子树
        if (m == inStart){
            tree.leftchild = null;
        }else {
            reConstructBinaryTreeCore(pre,in,preStart,preStart+m-inStart-1,inStart,m-1,tree.leftchild);
        }
        //递归建立右子树
        if (m == inEnd){
            tree.rightchild = null;
        }else {
            reConstructBinaryTreeCore(pre,in,preStart+m-inStart,preEnd-1,m+1,inEnd,tree.rightchild);
        }
    }

11.二叉树的叶子统计:递归

 public int getleefnum(Node temp){
        if (temp == null){
            return 0;
        }
        if (temp.leftchild == null & temp.rightchild == null){
            return 1;
        }
        return getleefnum(temp.leftchild)+getleefnum(temp.rightchild);
    }

   二叉树的深度计算: 递归

  public int getdeep(Node temp){
        if (temp == null){
            return 0;
        }
        if (temp.leftchild == null & temp.rightchild == null){
            return 1;
        }
        int deep_1 = getdeep(temp.leftchild);
        int deep_2 = getdeep(temp.rightchild);
        return deep_1>deep_2?1+deep_1:1+deep_2;
    }

12.线索二叉树:将二叉树以某种次序遍历使其成为线索二叉树

     lchild Ltag data Rtag rchild 

     前驱节点:(中序为例)直接指向或是以该节点的左孩子为根节点的子树的最右最下节点

     后驱节点:(中序为例)直接指向或是以该节点的右孩子为根节点的子树的最左最下节点

     线索化过程:检查节点的左右指针域是否为空,若为空则修改为其前驱或后继节点的指针,可以先得到遍历顺序再进行检查和修改

13.二叉排序树:左子树节点的值小于根节点,右子树节点的值大于根节点,且均为二叉排序树

class orderTree extends Tree{
    //二叉排序树的中序遍历序列应为有序序列,在这个地方对其进行了判断(手动)

    ArrayList<Integer> li = new ArrayList<Integer>();
    boolean index = true;

    public void checkanswer(){
        checkorder(this.getRoot());
        System.out.print(this.index);
    }

    public void checkorder(Node st){
        if (st!=null) {
            checkorder(st.leftchild);

            if (this.li.size() != 0) {
                int temp = this.li.get(this.li.size() - 1);
                int now = st.data;
                System.out.println(temp + ":" + now);
                if (temp > now) {
                    this.index = false;
                }
                this.li.add(now);
            } else {
                this.li.add(st.data);
            }
            checkorder(st.rightchild);
        }
    }
}

     查找:与根比较。若小于则转向左子树,若大于则转向右子树

    public Node getbydata(int data){
        if (this.index == false){
            System.out.print("not a order tree");
            return null;
        }
        if (this.head == null){
            System.out.print("null tree");
            return null;
        }
        return compare(this.head,data);

    }
    public Node compare(Node compre,int data){
        if (compre == null || compre.data == data){
            return compre;
        }
        if (compre.data>data){
            return compare(compre.leftchild,data);
        }
        else{
            return compare(compre.rightchild,data);
        }
    }

     插入:访问一个节点,若大于它,则查看其是否有右孩子,没有则新节点作为右孩子,若有则继续和其右节点比较。其余思路类似。采用记录最后访问的一个节点的思路较复杂。

   public void insert(int data){
       Node inse = new Node(data);
       if (this.head == null){
           this.head = inse;
       }

       Node temp = this.head;
       while (temp!=null){
           if (temp.data > data){
                if (temp.leftchild == null){
                    temp.leftchild = inse;
                    return;
                }else {
                    temp = temp.leftchild;
                }
           }
           else if (temp.data<data){
               if (temp.rightchild == null){
                   temp.rightchild = inse;
                   return;
               }else {
                   temp = temp.rightchild;
               }
           }
           else {
               System.out.print("already exit");
               return;
           }

           }
       }

     删除:若为叶子节点,直接删除;若单分支,删除,分支挂在父节点上;双分支,按中序遍历保持有序,中序中的前驱代替该节点,删除该前驱。

    public void delete(int data){
        Node need = getbydata(data);
        if (need == null){
            System.out.print("no such node");
            return;
        }
        //叶子节点情况
        if (need.leftchild == null & need.rightchild == null){
            Node father = findfater(data);
            if (father!=null){
                if (father.leftchild != null && father.leftchild.data == data){
                    father.leftchild = null;
                }else {
                    father.rightchild = null;
                }
            }else {
                //此节点无父节点
                this.head = null;
            }
            return;
        }
        //单分支结构
        else if (need.leftchild == null){
            Node father = findfater(data);
            if (father!=null){
                if (father.leftchild != null && father.leftchild.data == data){
                    father.leftchild = need.rightchild;
                }else {
                    father.rightchild = need.rightchild;
                }
            }else {
                //此节点无父节点
                this.head = need.rightchild;
            }
            return;
        }

        else if (need.rightchild == null){
            Node father = findfater(data);
            if (father!=null){
                if (father.rightchild != null && father.rightchild.data == data){
                    father.rightchild = need.leftchild;
                }else {
                    father.leftchild = need.leftchild;
                }
            }else {
                //此节点无父节点
                this.head = need.leftchild;
            }
            return;
        }

        //左右节点均在,在节点的左分支延右指针向下找,直到右指针为空
        else {
            Node temp = need.leftchild;
            while (temp.rightchild!=null){
                temp = temp.rightchild;
            }



            //删除temp节点
            Node father_temp = findfater(temp.data);
            if (father_temp.rightchild!=null && father_temp.rightchild.data == temp.data){
                if (temp.leftchild!=null){
                    father_temp.rightchild = temp.leftchild;
                }else {
                    father_temp.rightchild = null;
                }
            }else {
                if (temp.leftchild!=null){
                    father_temp.leftchild = temp.leftchild;
                }else {
                    father_temp.leftchild = null;
                }
            }

            //替换该节点
            Node copy = new Node(temp.data);
            copy.leftchild = need.leftchild;
            copy.rightchild = need.rightchild;

            Node father = findfater(data);
            if (father!=null){
                if (father.rightchild != null && father.rightchild.data == data){
                    father.rightchild = copy;
                }else {
                    father.leftchild = copy;
                }
            }else {
                //此节点无父节点
                this.head = copy;
            }
            return;
        }

    }
    public Node findfater(int data){
        Node temp = this.head;
        while (temp!=null){
            if (temp.data == data){
                return null;
            }
            else if (temp.data<data){
                if (temp.rightchild!=null){
                    if (temp.rightchild.data == data){
                        return temp;
                    }else {
                        temp = temp.rightchild;
                    }
                }else {
                    return null;
                }
            }
            else {
                if (temp.leftchild!=null){
                    if (temp.leftchild.data == data){
                        return temp;
                    }else {
                        temp = temp.leftchild;
                    }
                }else {
                    return null;
                }

            }
        }
        return null;
    }

14.平衡二叉树:每个节点的左右子树深度之差不超过1的二叉排序树

     调整策略见 P101

15.Huffman tree(最优二叉树):带权路径长度最小

     构造算法:选最小的两个,求和再比较再选最小的两个

/**
 * for the test
 *
 * @auther:dan
 * @version:1.0
 * @date:2017.11.16
 */
package java_practice;

import org.omg.PortableInterceptor.INACTIVE;
import test_package.Tree;

import java.util.*;

/**Java中
 * treemap,treemap类是使用红黑树实现的,红黑树是一个自平衡的二叉搜索树
 * 自己实现可以采用顺序表或是链表结构进行树的存储
 */

public class HelloWorld {
    public static void main(String[] args) {
        Node t1 = new Node(7,"a");
        Node t2 = new Node(5,"b");
        Node t3 = new Node(3,"c");
        Node t4 = new Node(4,"d");
        List<Node> st = new ArrayList<Node>();
        st.add(t1);
        st.add(t2);
        st.add(t3);
        st.add(t4);
        huffmanTree tre = new huffmanTree();
        Node root = tre.create(st);
        System.out.print(tre.toString(root));

        List<Integer> hh= tre.getcode("d");
        System.out.print(hh);
//
        Node output = tre.decode(hh);
        System.out.print(output);
    }
}

class Node implements Comparable{
    String data;
    int weight;
    Node leftchild;
    Node rightchild;
    Node parent;
    Node(int weight,String data){
        this.weight = weight;
        this.data = data;
    }

    @Override
    public int compareTo(Object o) {
        Node other = (Node)o;
        if (this.weight>other.weight){
            return -1;
        }
        else if (this.weight<other.weight){
            return 1;
        }else{
            return 0;
        }
    }

    public String toString(){
        return this.data;
//         return String.valueOf(this.weight);

    }
}


class huffmanTree{
    Node head = null;
    public Node create(List<Node> nodes){
        while (nodes.size()>1){
            //排序
            Collections.sort(nodes);
            //最小的两个组成叶子,生成新节点
            Node left = nodes.get(nodes.size()-1);
            Node right = nodes.get(nodes.size()-2);
            Node partent = new Node(left.weight+right.weight,"----");
            partent.leftchild = left;
            partent.rightchild = right;
            left.parent = partent;
            right.parent = partent;
            //构造新序列
            nodes.remove(left);
            nodes.remove(right);
            nodes.add(partent);
        }
        this.head = nodes.get(0);
        return nodes.get(0);
    }
    //按层次打印haffman树
    public static List<Node> toString(Node root){
        List<Node> nodes = new ArrayList<Node>();
        Queue<Node> que = new ArrayDeque<Node>();

        if (root!=null){
            //插入末尾
            que.offer(root);
        }
        while (que.isEmpty() == false){
            //获取但不移除
            nodes.add(que.peek());
            Node temp = que.poll();
//            nodes.add(temp);
            if (temp.leftchild!=null){
                que.offer(temp.leftchild);
            }
            if (temp.rightchild!=null){
                que.offer(temp.rightchild);
            }
        }
        return nodes;
     }

    //编码
    public List getcode(String data){
        List<Node> li = toString(this.head);
        Node st = null;
        for(Node temp :li){
            if (temp.data == data){
                st = temp;
            }
        }
        if (st == null){
            return null;
        }
        ArrayList<Integer> re = new ArrayList<Integer>();
        Node temp = st.parent;
        while (temp!=null){
            if (st.parent.leftchild == st){
                re.add(1);
            }else {
                re.add(0);
            }
            temp = temp.parent;
        }
        Collections.reverse(re);
        return re;
    }

    //解码
    public Node decode(List<Integer> input){
        Node temp = this.head;
        for (int e:input){
            if (e == 1){
                temp = temp.leftchild;
            }else {
                temp = temp.rightchild;
            }
        }
        return temp;
    }
}

16.树的存储结构

     双亲孩子表示法:数组+链表

     兄弟表示法:二叉链表 firstchild ,nextsibiling 记录第一个孩子节点及下一个兄弟节点

17.树转化为二叉树:对一棵树可以找到一棵二叉树与之对应,二叉树转换回来->树/森林

     兄弟节点相连->保留与最左孩子的连线,其余删除(兄弟表示法)

18.树的遍历:先根遍历,后根遍历,层次遍历

19.B树:文件系统的重要管理方式,多路平衡查找树

    介绍:https://www.cnblogs.com/George1994/p/7008732.html

    B+树:

内部节点中,关键字的个数与其子树的个数相同,不像B树种,子树的个数总比关键字个数多1个
所有指向文件的关键字及其指针都在叶子节点中,不像B树,有的指向文件的关键字是在内部节点中。换句话说,B+树中,内部节点仅仅起到索引的作用,
在搜索过程中,如果查询和内部节点的关键字一致,那么搜索过程不停止,而是继续向下搜索这个分支

    优点: 

B+树的磁盘读写代价更低 
B+树的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说I/O读写次数也就降低了。

B+树的查询效率更加稳定 
由于内部结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

B+树更有利于对数据库的扫描 
B树在提高了磁盘IO性能的同时并没有解决元素遍历的效率低下的问题,而B+树只需要遍历叶子节点就可以解决对全部关键字信息的扫描,所以对于数据库中频繁使用的range query,B+树有着更高的性能。

20.堆:ki<=k2i,ki<=k2i+1,完全二叉树中所有非终端节点均不大于或小于左右孩子的值。大顶堆则根是最大的一个节点。

     堆排序算法:输出堆顶,剩余元素再次形成一个堆。nlog2[n]。

     小顶堆筛选:去掉堆顶,把最后一个元素放在堆顶,进行交换。

     堆建立:从最后一个非叶子节点开始到根重复筛选过程。

      

/**
 * for the test
 *
 * @auther:dan
 * @version:1.0
 * @date:2017.11.16
 */
package java_practice;

import org.omg.PortableInterceptor.INACTIVE;
import test_package.Tree;

import java.util.*;

/**Java中
 * treemap,treemap类是使用红黑树实现的,红黑树是一个自平衡的二叉搜索树
 * 自己实现可以采用顺序表或是链表结构进行树的存储
 */

public class HelloWorld {
    public static void main(String[] args) {
        //顺序表存储的树
        int[] data = { 15, 13, 1, 5, 20, 12, 8, 9, 11 };
        // 测试建堆
        heap he = new heap();
//        he.creat(data, data.length - 1);
//        System.out.println(Arrays.toString(data));
        he.heapsort(data);
        System.out.print(Arrays.toString(data));
    }
}

class heap{
    //i是要替换的位置,n是数组最后一位,只是一个节点的调整过程
    public static void fixDown(int[] data, int i, int n) {
        int num = data[i];
        //左儿子,因为没有头结点
        int son = i * 2 + 1;
        while (son <= n) {
            //如果有右儿子且右儿子更小,切换到右儿子
            if (son + 1 <= n && data[son + 1] < data[son])
                son++;
            //如果当前节点小于儿子,结束
            if (num < data[son])
                break;
            //和儿子交换
            data[i] = data[son];
            i = son;
            son = i * 2 + 1;
        }
        data[i] = num;
    }

    public static void creat(int[] data, int n) {
        //从一半的元素开始调整,一直调整到根
        for (int i = (n - 1) / 2; i >= 0; i--)
            fixDown(data, i, n);
    }

    //第n个数向上调整的过程
    public static void fixUp(int[] data, int n) {
        int num = data[n];
        int father = (n - 1) / 2;
        // data[father] > num是进入循环的基本条件,father减到0就不会减少了
        // 当n等于0时,father=0;进入死循环,所以当n==0时,需要跳出循环
        while (data[father] > num && n != 0) {
            data[n] = data[father];
            n = father;
            father = (n - 1) / 2;
        }
        data[n] = num;
    }

    // 删除,n表示堆的最后一个元素的索引
    public static void delete(int[] data, int n) {
        data[0] = data[n];
        data[n] = -1;
        fixDown(data, 0, n - 1);
    }

    // 增加,i表示要增加的数字,n表示增加位置的索引,是堆的最后一个元素
    public static void insert(int[] data, int num, int n) {
        data[n] = num;
        fixUp(data, n);
    }

    //堆排序
    public void heapsort(int[] data){
        creat(data,data.length-1);
        for (int i = data.length-1;i>=1;i--){
            int temp = data[0];
            data[0] = data[i];
            data[i] = temp;
            fixDown(data,0,i-1);
        }
    }
}