数据结构之树相关的问题

实现二叉树的先序、中序、后序遍历,包括递归方式和非递归方式

  1. 递归方式:略
  2. 非递归方式
  • 先序遍历:先打印头结点,若头结点的右孩子不为空则先将右孩子压栈,然后再看其左孩子是否为空,若不为空将左孩子压栈,然后再将栈顶弹出打印。让头结点等于弹出的栈顶节点,再重复上述过程,直到栈为空为止。
  • 中序遍历:先将头结点压栈,若左孩子不为空,则将左孩子压栈,然后再看左孩子的左孩子是否为空,若不为空则将其压栈。一直找左孩子,将其压栈,直到没有左孩子为止。此时看栈顶元素的右孩子是否为空,若不为空,则将右孩子重复上述过程。若为空,则直接将其弹出打印,然后再弹出栈顶,重复栈顶元素找右孩子的过程。
  • 后序遍历:后序的遍历顺序是左-右-中,先序的遍历顺序是中-左-右,所以只需将先序遍历过程中的先压右孩子,变成先压左孩子,得到的遍历顺序就是中-右-左,这个顺序刚好和后序遍历相反。所以此时将中-右-左的顺序逆序即可(栈结构来实现)
  1. 代码
    
    import java.util.Stack;
    
    /**
     * 分别用递归和非递归的方式遍历二叉树
     */
    public class PreInPosTraversal {
    
        public static class Node {
            public int data;
            public Node left;
            public Node right;
    
            public Node(int data) {
                this.data = data;
            }
        }
    
        /**
         * 递归方式先序遍历
         * @param head
         */
        public static void preOrderRecur(Node head) {
            if (head == null) {
                return;
            }
            System.out.print(head.data+"  ");
            preOrderRecur(head.left);
            preOrderRecur(head.right);
        }
    
    
        /**
         * 递归方式中序遍历
         * @param head
         */
        public static void inOrderRecur(Node head) {
            if (head == null) {
                return;
            }
            inOrderRecur(head.left);
            System.out.print(head.data+"  ");
            inOrderRecur(head.right);
        }
    
    
        /**
         * 递归方式后序遍历
         * @param head
         */
        public static void posOrderRecur(Node head) {
            if (head == null) {
                return;
            }
            posOrderRecur(head.left);
            posOrderRecur(head.right);
            System.out.print(head.data+"  ");
        }
    
    
        /**
         * 递归方式先序遍历
         * @param head
         */
        public static void preOrderUnRecur(Node head) {
            if (head != null) {
                Stack stack = new Stack<>();
                stack.push(head);
                while (!stack.isEmpty()) {
                    head=stack.pop();
                    System.out.print(head.data+"  ");
                    if (head.right != null) {
                        stack.push(head.right);
                    }
                    if (head.left != null) {
                        stack.push(head.left);
                    }
                }
            }
    
        }
    
    
        /**
         * 递归方式中序遍历
         */
        public static void inOrderUnRecur(Node head) {
            if (head != null) {
                Stack stack = new Stack<>();
                while (head != null || !stack.isEmpty()) {
                    if (head != null) {
                        stack.push(head);
                        head = head.left;
                    } else {
                        head=stack.pop();
                        System.out.print(head.data+"  ");
                        head=head.right;
                    }
                }
            }
        }
    
    
        /**
         *
         */
        public static void posOrderUnRecur(Node head) {
            if (head != null) {
                Stack stack = new Stack<>();
                Stack res = new Stack<>();
                stack.push(head);
                while (!stack.isEmpty()) {
                    head=stack.pop();
                    res.push(head);
                    if (head.left != null) {
                        stack.push(head.left);
                    }
                    if (head.right != null) {
                        stack.push(head.right);
                    }
                }
    
                while (!res.isEmpty()) {
                    System.out.print(res.pop().data+"  ");
                }
            }
        }
    
    
        public static void main(String[] args) {
            Node head = new Node(5);
            head.left = new Node(3);
            head.right = new Node(8);
            head.left.left = new Node(2);
            head.left.right = new Node(4);
            head.left.left.left = new Node(1);
            head.right.left = new Node(7);
            head.right.left.left = new Node(6);
            head.right.right = new Node(10);
            head.right.right.left = new Node(9);
            head.right.right.right = new Node(11);
    
            // recursive
            System.out.println("==============recursive==============");
            System.out.print("pre-order: ");
            preOrderRecur(head);
            System.out.println();
            System.out.print("in-order: ");
            inOrderRecur(head);
            System.out.println();
            System.out.print("pos-order: ");
            posOrderRecur(head);
            System.out.println();
    
            // unrecursive
            System.out.println("============unrecursive=============");
            preOrderUnRecur(head);
            System.out.println();
            inOrderUnRecur(head);
            System.out.println();
            posOrderUnRecur(head);
    
        }
    
    }
    
    

如何直观打印一颗二叉树

public class Code_02_PrintBinaryTree {

	public static class Node {
		public int value;
		public Node left;
		public Node right;

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

	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 = new Node(1);
		head.left = new Node(-222222222);
		head.right = new Node(3);
		head.left.left = new Node(Integer.MIN_VALUE);
		head.right.left = new Node(55555555);
		head.right.right = new Node(66);
		head.left.left.right = new Node(777);
		printTree(head);

		head = new Node(1);
		head.left = new Node(2);
		head.right = new Node(3);
		head.left.left = new Node(4);
		head.right.left = new Node(5);
		head.right.right = new Node(6);
		head.left.left.right = new Node(7);
		printTree(head);

		head = new Node(1);
		head.left = new Node(1);
		head.right = new Node(1);
		head.left.left = new Node(1);
		head.right.left = new Node(1);
		head.right.right = new Node(1);
		head.left.left.right = new Node(1);
		printTree(head);

	}

}

在二叉树中找到一个节点的后继节点(前驱结点刚好和后继节点找法相反)

  1. 【题目】 现在有一种新的二叉树节点类型如下:
    public class Node { public int value; public Node left;
    public Node right; public Node parent;
    public Node(int data) { this.value = data; }
    }
    该结构比普通二叉树节点结构多了一个指向父节点的parent指针。假设有一棵Node类型的节点组成的二叉树,树中每个节点的parent指针都正确地指向自己的父节点,头节点的parent指向null。只给一个在二叉树中的某个节点node,请实现返回node的后继节点的函数。在二叉树的中序遍历的序列中,node的下一个节点叫作node的后继节点。
  2. 思路:
  • 如果node节点有右子树,则node节点的后继节点就是其右子树上最左的那个节点。
  • 如果node节点无右子树,则找后继节点就是找,到底node是作为哪一个节点的左子树的最后一个节点。
  1. 代码
    public class SuccessorNode {
        public static class Node {
            public int data;
            public Node parent;
            public Node left;
            public Node right;
    
            public Node(int data) {
                this.data = data;
            }
        }
    
        public static Node findSuccessorNode(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 = parent.parent;
                }
                return parent;
            }
    
        }
    
        private static Node getLeftMost(Node head) {
            if (head == null) {
                return head;
            }
            while (head.left != null) {
                head = head.left;
            }
            return head;
        }
    
    
        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.data + " next: " + findSuccessorNode(test).data);
            test = head.left.left.right;
            System.out.println(test.data + " next: " + findSuccessorNode(test).data);
            test = head.left;
            System.out.println(test.data + " next: " + findSuccessorNode(test).data);
            test = head.left.right;
            System.out.println(test.data + " next: " + findSuccessorNode(test).data);
            test = head.left.right.right;
            System.out.println(test.data + " next: " + findSuccessorNode(test).data);
            test = head;
            System.out.println(test.data + " next: " + findSuccessorNode(test).data);
            test = head.right.left.left;
            System.out.println(test.data + " next: " + findSuccessorNode(test).data);
            test = head.right.left;
            System.out.println(test.data + " next: " + findSuccessorNode(test).data);
            test = head.right;
            System.out.println(test.data + " next: " + findSuccessorNode(test).data);
            test = head.right.right; // 10's next is null
            System.out.println(test.data + " next: " + findSuccessorNode(test));
        }
    
    }
    
    

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

  1. 按先序的方式序列化和反序列化(中序和后序方式类似)
    代码:

import java.util.LinkedList;
import java.util.Queue;

/**
 * 序列化和反序列化二叉树
 */
public class SerializeAndReconstructTree {

    public static class Node {
        public int data;
        public Node left;
        public Node right;

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


    public static String preSerializeTree(Node head) {
        if (head == null) {
            return null;
        }
        String str = head.data + "_";
        if (head.left != null) {
            str += preSerializeTree(head.left);
        } else {
            str += "#_";
        }
        if (head.right != null) {
            str += preSerializeTree(head.right);
        } else {
            str += "#_";
        }

        return str;
    }


    public static Node preReconstructTree(String str) {
        if (str == null) {
            return null;
        }
        Queue queue = new LinkedList();
        Node head = null;
        Node res = null;
        String nodes[] = str.split("_");
        for (int i = 0; i < nodes.length; i++) {
            if (!"#".equals(nodes[i])) {
                queue.add(new Node(Integer.valueOf(nodes[i])));
            } else {
                queue.add(null);
            }

        }
        return process(queue);
    }


    public static Node process(Queue queue) {
        Node head = queue.poll();
        if (head == null) {
            return null;
        }

        head.left = process(queue);
        head.right = process(queue);
        return head;
    }


    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.data + 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 = new Node(1);
        head.left = new Node(2);
        head.right = new Node(3);
        String pre = preSerializeTree(head);
        System.out.println("serialize tree by pre-order: " + pre);
        Node head1 = preReconstructTree(pre);
        printTree(head1);

    }
}

判断一棵二叉树是否是平衡二叉树

  1. 平衡二叉树概念:二叉树中任何一个节点的左子树和右子树的高度差不超过1。
  2. 整体思路:一个节点的左子树若不平衡,那么整棵树就不平衡;如果左子树平衡,那么看右子树,若右子树不平衡则整棵树不平衡,否则右子树平衡。然后左子树高度和右子树高度差若大于1,则整棵树不平衡。否则平衡。对每个节点都用这种方式递归判断。
  3. 代码
    /**
     * 判断一颗二叉树是否是平衡二叉树
     */
    public class IsBalancedTree {
    
        public static class Node{
            public int data;
            public Node left;
            public Node right;
    
            public Node(int data) {
                this.data = data;
            }
        }
    
        public static class ReturnData{
            public int height;
            public boolean isB;
    
            public ReturnData(int height, boolean isB) {
                this.height = height;
                this.isB = isB;
            }
        }
    
    
        public static boolean isB(Node head) {
            return process(head).isB;
        }
    
        public static ReturnData process(Node head) {
            if (head == null) {
                return new ReturnData(0, true);
            }
            ReturnData leftData=process(head.left);
            if (!leftData.isB) {
                return new ReturnData(0, false);
            }
            ReturnData rightData=process(head.right);
            if (!rightData.isB) {
                return new ReturnData(0, false);
            }
            if (Math.abs(leftData.height - rightData.height) > 1) {
                return new ReturnData(0, false);
            }
            return new ReturnData(Math.max(leftData.height , rightData.height)+1, true);
        }
    
    
        public static void main(String[] args) {
            Node head = new Node(1);
            head.left = new Node(2);
            head.right = new Node(3);
            head.left.left = new Node(4);
            head.left.right = new Node(5);
            head.right.left = new Node(6);
            head.right.right = new Node(7);
            System.out.println(isB(head));
    
        }
    }
    
    

判断一棵树是否是搜索二叉树、判断一棵树是否是完全二叉树

  1. 相关概念:
  • 搜索二叉树:它或者是一棵空树,或者是具有下列性质的二叉树:
    若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
    若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
    它的左、右子树也分别为二叉排序树。
  1. 判断一棵树是否是二叉搜索树的思路:若二叉树的中序遍历序列的值是依次升序的,则满足条件
    代码:

    /**
     * 判断一棵树是否是搜索二叉树
     */
    public class IsBST {
        public static class Node{
            public int data;
            public Node left;
            public Node right;
    
            public Node(int data) {
                this.data = data;
            }
        }
    
    
        public static boolean isBST(Node head) {
            int res=Integer.MIN_VALUE;
            if (head == null) {
                return true;
            }
            Stack stack = new Stack<>();
            while (head != null || !stack.isEmpty()) {
                if (head != null) {
                    stack.push(head);
                    head=head.left;
                }else {
                    head=stack.pop();
                    res = Math.max(res, head.data);
                    if (res != head.data) {
                        return false;
                    }
                    head=head.right;
                }
            }
    
            return true;
        }
    
    
        public static void main(String[] args) {
            Node head = new Node(5);
            head.left = new Node(3);
            head.right = new Node(8);
            head.left.left = new Node(2);
            head.left.right = new Node(4);
            head.left.left.left = new Node(1);
            head.right.left = new Node(7);
            head.right.left.left = new Node(6);
            head.right.right = new Node(10);
            head.right.right.left = new Node(12);
            head.right.right.right = new Node(11);
            System.out.println(isBST(head));
        }
    }
    
    
  2. 判断一棵树是否是完全二叉树
    思路:

  • 若某个节点有右子树而没有左子树,则该树一定不是完全二叉树。
  • 如果某个节点的两个孩子不全,则有两种情况,有左孩子没有右孩子和左右两个孩子都没有。若是这种情况,则该节点后面的所有节点都是叶节点,则该树为完全二叉树,否则为非完全二叉树。

代码:


import java.util.LinkedList;
import java.util.Queue;

public class IsCBT {
    public static class Node{
        public int data;
        public Node left;
        public Node right;

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

    public static boolean isCBT(Node head) {
        if (head == null) {
            return true;
        }
        Queue queue=new LinkedList<>();
        Node left=null;
        Node right=null;
        boolean leaf=false;
        queue.add(head);
        while (!queue.isEmpty()) {
            head=queue.poll();
            left=head.left;
            right=head.right;
            if (leaf && (left!=null || right!=null) || (right != null && left == null)) {
                return false;
            }
            if (left != null) {
                queue.add(left);
            }
            if (right != null) {
                queue.add(right);
            }
            if (left == null || right == null) {
                leaf=true;
            }
        }
        return true;
    }


    public static void main(String[] args) {
        Node head = new Node(4);
        head.left = new Node(2);
        head.right = new Node(6);
        head.left.left = new Node(1);
        head.left.right = new Node(3);
        head.right.left = new Node(5);

        System.out.println(isCBT(head));
    }
}

##已知一棵完全二叉树,求其节点的个数(要求:时间复杂度低于O(N),N为这棵树的节点个数)

  1. 相关结论:若一颗满二叉树的高度为L,则这颗满二叉树的总节点数为2L-1;
  2. 思路:先求出整个二叉树的深度h,从头结点开始,若头结点的右子树的最大深度到达了h,则头结点的左子树是满二叉树,即可求出左子树的节点数,然后将右子树拿去递归。若头结点的右子树的最大深度小于h,则右子树是满二叉树,这时即可求出右子树的深度,然后再将左子树拿去递归。
  3. 代码
    
    public class CompleteTreeNodeNumber {
    
        public static class Node{
            public int data;
            public Node left;
            public Node right;
    
            public Node(int data) {
                this.data = data;
            }
        }
    
    
        public static int nodeNum(Node head) {
            if (head == null) {
                return 0;
            }
            return bs(head,1,mostLeftLevel(head,1));
        }
    
        private static int bs(Node node, int level, int h) {
            if (level == h) {
                return 1;
            }
            if (h == mostLeftLevel(node.right, level + 1)) {
                return (1 << (h - level)) + bs(node.right, level + 1, h);
            }else {
                return (1 << (h - level-1)) + bs(node.left, level + 1, h);
            }
        }
    
        private static int mostLeftLevel(Node node, int level) {
            while (node != null) {
                level++;
                node=node.left;
            }
            return level-1;
        }
    
        public static void main(String[] args) {
            Node head = new Node(1);
            head.left = new Node(2);
            head.right = new Node(3);
            head.left.left = new Node(4);
            head.left.right = new Node(5);
            head.right.left = new Node(6);
            System.out.println(nodeNum(head));
    
        }
    }
    
    

前缀树

  1. 介绍前缀树
    前缀数是经常用到的一种数据结构。主要思路是用边来记录数据,用点来记录有多少字符串经过某条边以及把某条边作为结尾。主要用于查找一个结构中有多少以某个字符开头的结果,或者某一个字符出现了几次。
  2. 基本性质:
  • 根节点不包含字符,除根节点外每一个节点都只包含一个字符。
  • 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
  • 每个节点的所有子节点包含的字符都不相同。
  1. 代码
    public class TrieTree {
    
        public static class TrieNode {
            public int path;
            public int end;
            public TrieNode[] nexts;
    
            public TrieNode() {
                this.path = 0;
                this.end = 0;
                this.nexts = new TrieNode[26];
            }
        }
    
        public static class Trie {
            TrieNode root;
    
            public Trie() {
                root = new TrieNode();
            }
    
            //向前缀树中添加一个字符串
            public void insert(String word) {
                if (word == null) {
                    return;
                }
                TrieNode node = root;
                char[] chs = word.toCharArray();
                for (int i = 0; i < chs.length; i++) {
                    int index = chs[i] - 'a';
                    if (node.nexts[index] == null) {
                        node.nexts[index] = new TrieNode();
                    }
                    node = node.nexts[index];
                    node.path++;
                }
                node.end++;
    
            }
    
    
            //在前缀树中查找字符串
            public int search(String word) {
                if (word == null) {
                    return 0;
                }
                TrieNode node = root;
                char[] chs = word.toCharArray();
                for (int i = 0; i < chs.length; i++) {
                    int index = chs[i] - 'a';
                    if (node.nexts[index] == null) {
                        return 0;
                    }
                    node = node.nexts[index];
                }
                return node.end;
            }
    
            //删除某个字符串
            public void delete(String word) {
                if (search(word) == 0) {
                    return;
                }
                TrieNode node = root;
                char[] chs = word.toCharArray();
                for (int i = 0; i < chs.length; i++) {
                    int index = chs[i] - 'a';
                    if (--node.nexts[index].path == 0) {
                        node.nexts[index] = null;
                        return;
                    }
                    node = node.nexts[index];
    
                }
                node.end--;
            }
    
    
            //查找以某一个字符串序列开头的字符串个数
            public int prefixNumber(String pre) {
                if (pre == null) {
                    return 0;
                }
                TrieNode node = root;
                char[] chs = pre.toCharArray();
                for (int i = 0; i < chs.length; i++) {
                    int index = chs[i] - 'a';
                    if (node.nexts[index] == null) {
                        return 0;
                    }
                    node = node.nexts[index];
                }
                return node.path;
            }
    
    
            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"));
    
            }
    
        }
    }
    
    

你可能感兴趣的:(数据结构与算法)