5.树(上)

0.概念

二叉树

 

1.二叉树基本操作

1.1 前中后序

/**
* 前中后序遍历:只需改变处理的次序
*/
//从根节点入口时判空
public void preOrder(){
//处理当前节点
   System.out.println();
   //递归左右子树
   if(this.left != null){
       this.left.preOrder();
  }
   if(this.right != null){
       this.right.preOrder();
  }
}
/**非递归法
* 利用栈实现前序遍历
*/
public ArrayList<Integer> preorderTraversal (TreeNode root) {      
       ArrayList<Integer> ans = new ArrayList<Integer>();
       Stack<TreeNode> st = new Stack<TreeNode>();
       if(root == null) return ans;
       st.push(root);
       while(!st.empty()){
           TreeNode t = st.pop();
           ans.add(t.val);
           //注意入栈次序
           if(t.right != null) st.push(t.right);
           if(t.left != null) st.push(t.left);
      }
       return ans;
  }
/**
* 利用栈实现中序遍历
*/
public ArrayList<Integer> inorderTraversal (TreeNode root) {      
       ArrayList<Integer> ans = new ArrayList<Integer>();
       Stack<TreeNode> st = new Stack<TreeNode>();
       
       TreeNode p = root;
           
       while(!st.empty() || p != null){
           //一直向左到最左边
           while(p != null){
               st.push(p);
               p = p.left;
          }
           //此时遍历栈顶元素
           p = st.pop();
           ans.add(p.val);
           //并转向其右边,即使为空,在下一轮循环时会转向栈顶          
           p = p.right;
      }
       return ans;
  }
/**
* 实现非递归后序遍历
*/

1.2 层序遍历

层序遍历二叉树,每一层分别保存

 public ArrayList<ArrayList<Integer>> levelOrder(TreeNode root) {
       ArrayList<ArrayList<Integer>> ans = new ArrayList<ArrayList<Integer>>();
       Queue<TreeNode> queue = new LinkedList<TreeNode>();
       if (root == null) return ans;
       queue.offer(root);
       while (!queue.isEmpty()) {
           ArrayList<Integer> temp = new ArrayList<Integer>();
           int len = queue.size();
           for (int i = 0; i < len; i++) {
               TreeNode t = queue.peek();
               if (t.left != null) {
                   queue.offer(t.left);
              }
               if (t.right != null) {
                   queue.offer(t.right);
              }
               temp.add(queue.poll().val);
          }
           ans.add(temp);
      }
       return ans;
  }

1.3 构造二叉树

由中序和另一遍历确定唯一二叉树

/**
* 由中序与前序确定
*/
public TreeNode build(int[] pre,int preBegin,int preEnd,int[] in,int inBegin,int inEnd){
       if(preBegin >= preEnd){
           return null;
      }
       
       TreeNode temp = new TreeNode(pre[preBegin]);
       if(preBegin == preEnd - 1) return temp;
       int index = 0;
       for(int i = inBegin;i < inEnd;i++){
           if(temp.val == in[i]){
               index = i;
               break;
          }
      }
       int len = index - inBegin;
       temp.left = build(pre,preBegin+1,preBegin+len+1,in,inBegin,index);
       temp.right = build(pre,preBegin+len+1,preEnd,in,index+1,inEnd);
       return temp;
  }
/**
* 由中序和后序确定,同理
*/

1.4 静态二叉树

public class ArrayTree {
   private int[] arr;
   public ArrayTree(int[] arr) {
       this.arr = arr;
  }

   public void preOrder(int index) {
       if (arr == null || arr.length == 0) {
           System.out.println("空树");
      }
       System.out.println(arr[index]);
       //递归遍历
       if ((index * 2 + 1) < arr.length) preOrder(2 * index + 1);//左子树
       if ((index * 2 + 2) < arr.length) preOrder(2 * index);//右子树
  }
}

2.线索二叉树

2.1线索化

public class ThreadedTree {
   private TreeNode root;
   //保存遍历时前一个节点
   private TreeNode pre = null;
   //以中序线索为例
   public void threadedTree(TreeNode root){
       if(root == null) {
           return;
      }
       //左子树
       threadedTree(root.left);
       //处理当前节点的前驱
       if(root.left == null){
           root.left = pre;//左为空时,左指向前驱
           root.leftType = 1;//修改左指针类型
      }
       //处理后继节点
       if(pre != null && pre.right == null){
           pre.right = root;//前驱右指向root
           pre.rightType = 1;//修改右指针类型
      }
       pre = root;//更新前驱
       //右子树
       threadedTree(root.right);
  }
}

3.堆排序

大顶/小顶:每个节点大于等于其子节点

public class HeapSort {
   //堆排序
   public static void heapSort(int[] arr) {
       int temp = 0;
       //调整建堆,从下面的非叶节点开始再向上
       for (int i = arr.length / 2 - 1; i >= 0; i--) {
           adjustHeap(arr, i, arr.length);
      }
       //排序
       //每次调整堆(待排序部分)将最大值(根节点)往后插入有序部分
       for (int j = arr.length - 1; j > 0; j--) {
           temp = arr[j];
           arr[j] = arr[0];
           arr[0] = temp;
           adjustHeap(arr, 0, j);
      }
  }
   /**
    * 向下调整:将以i 节点的树调整为大顶堆
    *
    * @param arr 数组
    * @param i   非叶子节点的索引
    * @param len 元素数量
    */
   public static void adjustHeap(int[] arr, int i, int len) {
       int temp = arr[i];//当前元素
       //往左子节点
       for (int k = i * 2 + 1; k < len; k = 2 * 2 + 1) {
           //比较左右子节点,找最大
           if (k + 1 < len && arr[k] < arr[k + 1]) {
               k++;
          }
           //子节点大于父
           if (arr[k] > temp) {
               arr[i] = arr[k];//子往上,i下沉
               i = k;
          } else {
               break;//由于是从下往上开始调整,所以不满足时下面的已调整好
          }
      }
       arr[i] = temp;
  }
}

4.霍夫曼树

WPL最小的树:

节点的带权路径长度=路径 X 节点权值

WPL所有叶子节点的带权路径长度之和

节点大的越往上

只有叶节点才放数据

4.1 创建霍夫曼树

class Node implements Comparable<Node> {
   int value;
   Node left;
   Node right;

   public Node(int value) {
       this.value = value;
  }

   @Override
   public int compareTo(Node o) {
       //从小到大
       return this.value - o.value;
  }
}
public class HuffmanTree {
       //创建霍夫曼树
       public static Node createTree(int[] arr) {
           //建立节点集
           List<Node> nodes = new ArrayList<>();
           for (int i : arr) {
               nodes.add(new Node(i));
          }
           while (nodes.size() > 1) {
               //节点排序
               Collections.sort(nodes);//实现集合排序
               //取出最小的两个节点
               Node leftNode = nodes.get(0);
               Node rightNode = nodes.get(1);
               //建立新树,父节点值为左右子之和
               Node parent = new Node(leftNode.value + rightNode.value);
               parent.left = leftNode;
               parent.right = rightNode;
               //用过的节点删除,并加入新节点
               nodes.remove(leftNode);
               nodes.remove(rightNode);
               nodes.add(parent);
          }
           return nodes.get(0);
      }      
}

4.2 数据压缩

//通过字符出现的次数为权值,建立节点集,再建立树;
//不同的字符用对应的霍夫曼编码

 

5.BST

任何非叶子节点左子小于他,右子大于他

中序遍历序列为非减递增

class BNode {
   int value;
   BNode left;
   BNode right;

   public BNode(int value) {
       this.value = value;
  }

   //递归添加节点
   public void add(BNode node) {
       if (node == null) {
           return;
      }
       if (node.value < this.value) {
           if (this.left == null) this.left = node;
           else this.left.add(node);
      } else {
           if (this.right == null) this.right = node;
           else this.right.add(node);
      }
  }

   //查找节点
   public BNode search(int value) {
       if (this.value == value) return this;
       else if (this.value > value) {
           if (this.left == null) return null;
           else return this.left.search(value);
      } else {
           if (this.right == null) return null;
           else return this.right.search(value);
      }
  }

   //查找删除节点的父节点
   public BNode searchf(int value) {
       if ((this.left != null && this.left.value == value) || (this.right != null && this.right.value == value)) {
           return this;
      } else {
           if (this.value > value && this.left != null) {
               return this.left.searchf(value);
          } else if (this.value < value && this.right != null) {
               return this.right.searchf(value);
          }
           return null;
      }
  }
   
}
public class BSTree {
   private BNode root;

   //建树,插入节点
   public void add(BNode node) {
       if (root == null) {
           root = node;
      } else root.add(node);
  }

   //查找节点
   public BNode search(int value) {
       if (root == null) return null;
       else return root.search(value);
  }

   //查找删除节点的父节点
   public BNode searchf(int value) {
       if (root == null) {
           return null;
      } else return root.searchf(value);
  }

   //删除节点
   public void delNode(int value) {
       if (root == null) return;
       else {
           BNode target = search(value);
           if (target == null) return;
           BNode parent = searchf(value);
           //删除叶子节点
           if (target.left == null && target.right == null) {
               if (parent.left.value == value) {
                   parent.left = null;
              } else {
                   parent.right = null;
              }
          } else if (target.left != null && target.right != null) {//删除节点有两个子节点
               //删除目标右子树最小的节点并获取此节点
               int min = deleteMin(target.right);
               //替换目标,即删除了目标节点
               target.value = min;
          } else {//删除节点有一个子节点
               //删除的节点只有左子
               if (target.left != null) {
                   if (parent.left.value == value) {
                       parent.left = target.left;
                  } else {
                       parent.right = target.left;
                  }
              } else {//删除的节点只有右子
                   if (parent.left.value == value) {
                       parent.left = target.right;
                  } else {
                       parent.right = target.right;
                  }
              }
          }
      }
  }

   //删除子树最小的节点并返回
   public int deleteMin(BNode root) {
       BNode target = root;
       //向左一直找
       while (target.left != null) {
           target = target.left;
      }
       //此节点也可能有一个右节点,但必然是最小
       delNode(target.value);
       return target.value;
  }
}

 

 

posted @ 2021-04-13 19:31  FremontUltimate  阅读(39)  评论(0)    收藏  举报