算法入门篇七 前缀树

牛客网 左程云老师的算法入门课

找二叉树的节点的后继节点

原则

  • 如果节点有右子树,那么后继节点就是右子树的最左边的第一个节点
  • 如果节点没有右子树,如果节点是父节点的右孩子,就继续往上找,直到找到一个父节点是沿途节点的父节点,且沿途节点是其的左孩子

题目要求

新的二叉树的类型如下

public class Node{
    public int value;
    public Node left;
    public Node right;
    public Node parent;
    public Node(int val){
        value = val;
    }
}
  • 比传统的二叉树节点相比,多了一个指向父节点的parent指针
  • 头节点的parent指向空
  • 只有一个二叉树节点的node,请实现返回node节点的后继函数。二叉树的中序遍历中,node的下一个节点叫做节点的后继节点。请编写程序实现此功能: 

代码

package class05;
 
public class Code08_SuccessorNode {
 
    public static class Node {
        public int value;
        public Node left;
        public Node right;
        public Node parent;
 
        public Node(int data) {
            this.value = data;
        }
    }
 
    public static Node getSuccessorNode(Node node) {
        if (node == null) {
            return node;
        }
        if (node.right != null) {
            return getLeftMost(node.right);
        } else { // 无右子树
            Node parent = node.parent;
            while (parent != null && parent.left != node) { // 当前节点是其父亲节点右孩子
                node = parent;
                parent = node.parent;
            }
            return parent;
        }
    }
 
    public static Node getLeftMost(Node node) {
        if (node == null) {
            return node;
        }
        while (node.left != null) {
            node = node.left;
        }
        return node;
    }
 
    public static void main(String[] args) {
        Node head = new Node(6);
        head.parent = null;
        head.left = new Node(3);
        head.left.parent = head;
        head.left.left = new Node(1);
        head.left.left.parent = head.left;
        head.left.left.right = new Node(2);
        head.left.left.right.parent = head.left.left;
        head.left.right = new Node(4);
        head.left.right.parent = head.left;
        head.left.right.right = new Node(5);
        head.left.right.right.parent = head.left.right;
        head.right = new Node(9);
        head.right.parent = head;
        head.right.left = new Node(8);
        head.right.left.parent = head.right;
        head.right.left.left = new Node(7);
        head.right.left.left.parent = head.right.left;
        head.right.right = new Node(10);
        head.right.right.parent = head.right;
 
        Node test = head.left.left;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.left.left.right;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.left;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.left.right;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.left.right.right;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.right.left.left;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.right.left;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.right;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.right.right; // 10's next is null
        System.out.println(test.value + " next: " + getSuccessorNode(test));
    }
 
}

二叉树的序列化/反序列化

  • 使用#代表null
  • 每输出一个数值,就输出一个_来隔离元素
  • 由树形结构到字符串的过程叫做序列化,由字符串到树形结构的过程是反序列化

代码

package class05;
 
import java.util.LinkedList;
import java.util.Queue;
 
public class Code09_SerializeAndReconstructTree {
 
    public static class Node {
        public int value;
        public Node left;
        public Node right;
 
        public Node(int data) {
            this.value = data;
        }
    }
 
    // 以head为头的树,请序列化成字符串返回
    public static String serialByPre(Node head) {
        if (head == null) {
            return "#_";
        }
        String res = head.value + "_";
        res += serialByPre(head.left);
        res += serialByPre(head.right);
        return res;
    }
 
    public static Node reconByPreString(String preStr) {
        String[] values = preStr.split("_");
        Queue<String> queue = new LinkedList<String>();
        for (int i = 0; i != values.length; i++) {
            queue.add(values[i]);
        }
        return reconPreOrder(queue);
    }
 
    public static Node reconPreOrder(Queue<String> queue) {
        String value = queue.poll();
        if (value.equals("#")) {
            return null;
        }
        Node head = new Node(Integer.valueOf(value));
        head.left = reconPreOrder(queue);
        head.right = reconPreOrder(queue);
        return head;
    }
 
    public static String serialByLevel(Node head) {
        if (head == null) {
            return "#!";
        }
        String res = head.value + "_";
        Queue<Node> queue = new LinkedList<Node>();
        queue.add(head);
        while (!queue.isEmpty()) {
            head = queue.poll();
            if (head.left != null) {
                res += head.left.value + "_";
                queue.add(head.left);
            } else {
                res += "#_";
            }
            if (head.right != null) {
                res += head.right.value + "_";
                queue.add(head.right);
            } else {
                res += "#_";
            }
        }
        return res;
    }
 
    public static Node reconByLevelString(String levelStr) {
        String[] values = levelStr.split("_");
        int index = 0;
        Node head = generateNodeByString(values[index++]);
        Queue<Node> queue = new LinkedList<Node>();
        if (head != null) {
            queue.add(head);
        }
        Node node = null;
        while (!queue.isEmpty()) {
            node = queue.poll();
            node.left = generateNodeByString(values[index++]);
            node.right = generateNodeByString(values[index++]);
            if (node.left != null) {
                queue.add(node.left);
            }
            if (node.right != null) {
                queue.add(node.right);
            }
        }
        return head;
    }
 
    public static Node generateNodeByString(String val) {
        if (val.equals("#")) {
            return null;
        }
        return new Node(Integer.valueOf(val));
    }
 
    // for test -- print tree
    public static void printTree(Node head) {
        System.out.println("Binary Tree:");
        printInOrder(head, 0, "H", 17);
        System.out.println();
    }
 
    public static void printInOrder(Node head, int height, String to, int len) {
        if (head == null) {
            return;
        }
        printInOrder(head.right, height + 1, "v", len);
        String val = to + head.value + to;
        int lenM = val.length();
        int lenL = (len - lenM) / 2;
        int lenR = len - lenM - lenL;
        val = getSpace(lenL) + val + getSpace(lenR);
        System.out.println(getSpace(height * len) + val);
        printInOrder(head.left, height + 1, "^", len);
    }
 
    public static String getSpace(int num) {
        String space = " ";
        StringBuffer buf = new StringBuffer("");
        for (int i = 0; i < num; i++) {
            buf.append(space);
        }
        return buf.toString();
    }
 
    public static void main(String[] args) {
        Node head = null;
        printTree(head);
 
        String pre = serialByPre(head);
        System.out.println("serialize tree by pre-order: " + pre);
        head = reconByPreString(pre);
        System.out.print("reconstruct tree by pre-order, ");
        printTree(head);
 
        String level = serialByLevel(head);
        System.out.println("serialize tree by level: " + level);
        head = reconByLevelString(level);
        System.out.print("reconstruct tree by level, ");
        printTree(head);
 
        System.out.println("====================================");
 
        head = new Node(1);
        printTree(head);
 
        pre = serialByPre(head);
        System.out.println("serialize tree by pre-order: " + pre);
        head = reconByPreString(pre);
        System.out.print("reconstruct tree by pre-order, ");
        printTree(head);
 
        level = serialByLevel(head);
        System.out.println("serialize tree by level: " + level);
        head = reconByLevelString(level);
        System.out.print("reconstruct tree by level, ");
        printTree(head);
 
        System.out.println("====================================");
 
        head = new Node(1);
        head.left = new Node(2);
        head.right = new Node(3);
        head.left.left = new Node(4);
        head.right.right = new Node(5);
        printTree(head);
 
        pre = serialByPre(head);
        System.out.println("serialize tree by pre-order: " + pre);
        head = reconByPreString(pre);
        System.out.print("reconstruct tree by pre-order, ");
        printTree(head);
 
        level = serialByLevel(head);
        System.out.println("serialize tree by level: " + level);
        head = reconByLevelString(level);
        System.out.print("reconstruct tree by level, ");
        printTree(head);
 
        System.out.println("====================================");
 
        head = new Node(100);
        head.left = new Node(21);
        head.left.left = new Node(37);
        head.right = new Node(-42);
        head.right.left = new Node(0);
        head.right.right = new Node(666);
        printTree(head);
 
        pre = serialByPre(head);
        System.out.println("serialize tree by pre-order: " + pre);
        head = reconByPreString(pre);
        System.out.print("reconstruct tree by pre-order, ");
        printTree(head);
 
        level = serialByLevel(head);
        System.out.println("serialize tree by level: " + level);
        head = reconByLevelString(level);
        System.out.print("reconstruct tree by level, ");
        printTree(head);
 
        System.out.println("====================================");
 
    }
}

如何判断一棵子树是不是另外一棵二叉树的子树

  • KMP算法

折纸问题

要求

参考

代码

package class05;
 
public class Code10_PaperFolding {
 
    public static void printAllFolds(int N) {
        printProcess(1, N, true);
    }
 
    // 递归过程,来到了某一个节点,
    // i是节点的层数,N一共的层数,down == true  凹    down == false 凸
    public static void printProcess(int i, int N, boolean down) {
        if (i > N) {
            return;
        }
        printProcess(i + 1, N, true);
        System.out.println(down ? "凹 " : "凸 ");
        printProcess(i + 1, N, false);
    }
 
    public static void main(String[] args) {
        int N = 3;
        printAllFolds(N);
    }
}

前缀树

  • 存储在边上,不是节点
  • 节点存放信息:pass创建节点的时候通过这个节点几次;end:当前节点是多少字符串的结尾
  • 沿途节点的pass++;末尾节点的end++;

例子

加入节点

  • 加入节点ab,使得沿途a之后节点p=3,沿途b之后节点p=2,e=1;
  • 加入节点bcs,使得沿途bc之后节点++,使得s的p=2,e=2;

实现功能

  • 可以查询abc加入的次数,看的是e的值
  • 可以查询以ab作为前缀的次数,看的是p的值

删除节点

  • 沿途节点的p值减1,被删除的节点的p减1,e减1;并且内存释放。

代码

package class07;
 
import java.util.HashSet;
 
public class Code01_TrieTree {
 
    public static class TrieNode {
        public int pass;
        public int end;
        // HashMap<Char, Node> nexts;
        // TreeMap<Char, Node> nexts;
        public TrieNode[] nexts;
 
        public TrieNode() {
            pass = 0;
            end = 0;
            // nexts[0] == null 没有走向‘a’的路
            // nexts[0] != null 有走向‘a’的路
            // ...
            // nexts[25] != null 有走向‘z’的路
            nexts = new TrieNode[26];
        }
    }
 
    public static class Trie {
        private TrieNode root;
 
        public Trie() {
            root = new TrieNode();
        }
 
        public void insert(String word) {
            if (word == null) {
                return;
            }
            char[] chs = word.toCharArray();
            TrieNode node = root;
            node.pass++;
            int index = 0;
            for (int i = 0; i < chs.length; i++) { // 从左往右遍历字符
                index = chs[i] - 'a'; // 由字符,对应成走向哪条路
                if (node.nexts[index] == null) {
                    node.nexts[index] = new TrieNode();
                }
                node = node.nexts[index];
                node.pass++;
            }
            node.end++;
        }
 
        public void delete(String word) {
            if (search(word) != 0) { // 确定树中确实加入过word,才删除
                char[] chs = word.toCharArray();
                TrieNode node = root;
                node.pass--;
                int index = 0;
                for (int i = 0; i < chs.length; i++) {
                    index = chs[i] - 'a';
                    if (--node.nexts[index].pass == 0) {
                        // java C++ 要遍历到底去析构
                        node.nexts[index] = null;
                        // ...
                        return;
                    }
                    node = node.nexts[index];
                }
                node.end--;
            }
        }
 
        public void deleteCPP(String word) {
            if (search(word) != 0) { // 确定树中确实加入过word,才删除
                char[] chs = word.toCharArray();
                TrieNode node = root;
                node.pass--;
                int index = 0;
 
                TrieNode a = null;
                int deleteIndex = -1;
 
                HashSet<TrieNode> deleteSet = new HashSet<>();
 
                for (int i = 0; i < chs.length; i++) {
                    index = chs[i] - 'a';
                    if (--node.nexts[index].pass == 0) {
                        a = a == null ? node : a;
                        deleteIndex = deleteIndex == -1 ? index : deleteIndex;
 
                        deleteSet.add(node.nexts[index]);
                    }
                    node = node.nexts[index];
                }
                node.end--;
                 
                a.nexts[deleteIndex] = null;
                 
                // deleteSet ... 析构
 
            }
        }
 
        // word这个单词之前加入过几次
        public int search(String word) {
            if (word == null) {
                return 0;
            }
            char[] chs = word.toCharArray();
            TrieNode node = root;
            int index = 0;
            for (int i = 0; i < chs.length; i++) {
                index = chs[i] - 'a';
                if (node.nexts[index] == null) {
                    return 0;
                }
                node = node.nexts[index];
            }
            return node.end;
        }
 
        // 所有加入的字符串中,有几个是以pre这个字符串作为前缀的
        public int prefixNumber(String pre) {
            if (pre == null) {
                return 0;
            }
            char[] chs = pre.toCharArray();
            TrieNode node = root;
            int index = 0;
            for (int i = 0; i < chs.length; i++) {
                index = chs[i] - 'a';
                if (node.nexts[index] == null) {
                    return 0;
                }
                node = node.nexts[index];
            }
            return node.pass;
        }
    }
 
    public static void main(String[] args) {
        Trie trie = new Trie();
        System.out.println(trie.search("zuo"));
        trie.insert("zuo");
        System.out.println(trie.search("zuo"));
        trie.delete("zuo");
        System.out.println(trie.search("zuo"));
        trie.insert("zuo");
        trie.insert("zuo");
        trie.delete("zuo");
        System.out.println(trie.search("zuo"));
        trie.delete("zuo");
        System.out.println(trie.search("zuo"));
        trie.insert("zuoa");
        trie.insert("zuoac");
        trie.insert("zuoab");
        trie.insert("zuoad");
        trie.delete("zuoa");
        System.out.println(trie.search("zuoa"));
        System.out.println(trie.prefixNumber("zuo"));
 
    }
 
}

猜你喜欢

转载自blog.csdn.net/CHYabc123456hh/article/details/106746037