二叉树

  • 二叉树的第 (i) 层结点数不多于 (2^{i-1})
  • 深度为 (k) 的二叉树结点数不多于 (2^k-1)

满二叉树 :指的是深度为 (k) 且含有 (2^k - 1) 个结点的二叉树。
完全二叉树 :树中所含的 (n) 个结点和满二叉树中编号为 1 至 n 的结点一一对应。

public class BinaryTree<T> {

	private Node root;

	class Node {
		T val;
		Node left;
		Node right;

		Node(T val) {
			this.val = val;
		}
	}
}

遍历

1. 前序遍历

根 - 左 -右

// 非递归
public List<T> preorder() {
	List<T> res = new LinkedList<>();
	Stack<Node> stack = new Stack<>();
	if (root != null)
		stack.push(root);
	while (!stack.isEmpty()) {
		Node node = stack.pop();
		res.add(node.val);
		if (node.right != null)			// 栈 先保存 后访问
			stack.push(node.right);
		if (node.left != null)
			stack.push(node.left);
	}
	return res;
}

// 递归
public void preorder(Node node) {
	if (node == null)
		return;
	System.out.print(node.val + " ");
	preorder(node.left);
	preorder(node.right);
}
2. 中序遍历

左 - 根 - 右

// 非递归
public List<T> inorder() {
	List<T> res = new LinkedList<>();
	Stack<Node> stack = new Stack<>();
	Node node = root;
	while (!stack.isEmpty() || node != null) {
		while (node != null) {
			stack.add(node);
			node = node.left;
		}
		if (!stack.isEmpty()) {
			node = stack.pop();
			res.add(node.val);
			node = node.right;
		}
	}
	return res;
}

// 递归
public void inorder(Node node) {
	if (node == null)
		return;
	inorder(node.left);
	System.out.print(node.val + " ");
	inorder(node.right);
}
3. 后序遍历

左 - 右 - 根

// 非递归
// 后序遍历等价于镜像树前序遍历的逆序
public List<T> postorder() {
	List<T> res = new LinkedList<>();
	Stack<Node> stack = new Stack<>();
	if (root != null)
		stack.push(root);
	while (!stack.isEmpty()) {
		Node node = stack.pop();
		res.add(node.val);
		if (node.left != null)
			stack.push(node.left);
		if (node.right != null)
			stack.push(node.right);
	}
	Collections.reverse(res);
	return res;
}

// 递归
public void postorder(Node node) {
	if (node == null)
		return;
	postorder(node.left);
	postorder(node.right);
	System.out.print(node.val + " ");
}
4. 层次遍历
public List<T> level() {
	List<T> res = new LinkedList<>();
	Queue<Node> queue = new LinkedList<>();
	if (root != null)
		queue.offer(root);
	while (!queue.isEmpty()) {
		int num = queue.size();
		for (int i = 0; i < num; i++) {
			Node node = queue.poll();
			res.add(node.val);
			if (node.left != null)
				queue.offer(node.left);
			if (node.right != null)
				queue.offer(node.right);
		}
	}
	return res;
}

树高

public int height(Node node) {
	if (node == null)
		return 0;
	return Math.max(height(node.left), height(node.right)) + 1;
}

直径

即树中任两结点间的最长路径。
一棵二叉树的直径长度是任意两个结点路径长度中的最大值。
这条路径可能穿过也可能不穿过根结点。

private int res = 0;

public int diameter(Node root) {
    height(root);
    return res;
}

private int height(Node node) {
    if (node == null)
        return 0;
    int l = height(node.left);
    int r = height(node.right);
    res = Math.max(res, l + r);
    return Math.max(l, r) + 1;
}

翻转

public Node invert(Node node) {
	if (node == null)
		return null;
	Node tmp = invert(node.left);
	node.left = invert(node.right);
	node.right = tmp;
	return node;
}

对称

public boolean isSymmetric() {
	return isSymmetric(root, root);
}

private boolean isSymmetric(Node n1, Node n2) {
	if (n1 == null && n2 == null)
		return true;
	if (n1 == null || n2 == null)
		return false;
	return n1.val == n2.val && isSymmetric(n1.left, n2.right) && isSymmetric(n1.right, n2.left);
}

森林和二叉树

将森林中的每一棵树采用二叉链表(孩子-兄弟)法表示,根结点的右子树为空,再将所有树的根结点依次连接起来。

孩子-兄弟:二叉树结点的左孩子为该结点第一个孩子,右孩子为兄弟结点。

每棵树的后序遍历等价于二叉树的中序遍历。

字典树

Trie,字典树,或称前缀树,用于存储字符串,判断字符串前缀,检索字符串集中的键。如:

  • 自动补全
  • 拼写检查
  • IP路由 最长前缀匹配
  • 九宫格打字预测
  • 单词游戏
public class Trie {

	private TrieNode root;

	public Trie() {
		root = new TrieNode();
	}

	public void insert(String word) {
		TrieNode node = root;
		for (int i = 0; i < word.length(); i++) {
			char currentChar = word.charAt(i);
			if (!node.containsKey(currentChar)) {
				node.put(currentChar, new TrieNode());
			}
			node = node.get(currentChar);
		}
		node.setEnd();
	}

	private TrieNode searchPrefix(String word) {
		TrieNode node = root;
		for (int i = 0; i < word.length(); i++) {
			char curLetter = word.charAt(i);
			if (node.containsKey(curLetter)) {
				node = node.get(curLetter);
			} else {
				return null;
			}
		}
		return node;
	}

	public boolean search(String word) {
		TrieNode node = searchPrefix(word);
		return node != null && node.isEnd();
	}

	public boolean startsWith(String prefix) {
		TrieNode node = searchPrefix(prefix);
		return node != null;
	}

	class TrieNode {
		// TrieNode 中并不需要保存字符信息
		// 该结点储存的字符记录在它的父结点中
		private TrieNode[] links;

		private final int R = 26;	// 26 个小写字母

		private boolean isEnd;		// 是否为一个单词的结尾

		public TrieNode() {
			links = new TrieNode[R];
		}

		public boolean containsKey(char ch) {
			return links[ch - 'a'] != null;
		}

		public TrieNode get(char ch) {
			return links[ch - 'a'];
		}

		public void put(char ch, TrieNode node) {
			links[ch - 'a'] = node;
		}

		public void setEnd() {
			isEnd = true;
		}

		public boolean isEnd() {
			return isEnd;
		}
	}
}

哈夫曼树

哈夫曼编码:根据数据出现的频率对数据进行编码,从而压缩原始数据。

思路: 贪心策略
每次选取频率最小的两个结点,生成一个新结点,其频率为这两个结点的和,作为他们的父结点。
(n) 个结点进行 (n-1) 次选择,生成一棵 (2n-1) 个结点的树。树的叶子结点为数据。

编码:每个结点的左侧路径标记为 0,右侧路径标记为 1。从根结点出发,到叶结点结束,经过的路径即为该叶子结点的编码。

原文地址:https://www.cnblogs.com/JL916/p/12565450.html