二叉树节点结构
class Node< V > {
V value;
Node left;
Node right;
}
用递归和非递归两种方式实现二叉树的先序、中序、后序遍历
import java.util.Stack;
public class PreInPosTraversal {
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int data) {
this.value = data;
}
}
public static void preOrderRecur(Node head) {
if (head == null) {
return;
}
System.out.print(head.value + " ");
preOrderRecur(head.left);
preOrderRecur(head.right);
}
public static void inOrderRecur(Node head) {
if (head == null) {
return;
}
inOrderRecur(head.left);
System.out.print(head.value + " ");
inOrderRecur(head.right);
}
public static void posOrderRecur(Node head) {
if (head == null) {
return;
}
posOrderRecur(head.left);
posOrderRecur(head.right);
System.out.print(head.value + " ");
}
//非递归序列
public static void preOrderUnRecur(Node head) {
System.out.print("pre-order: ");
if (head != null) {
Stack<Node> stack = new Stack<Node>();
stack.add(head);
while (!stack.isEmpty()) {
head = stack.pop();
System.out.print(head.value + " ");
if (head.right != null) {
stack.push(head.right);
}
if (head.left != null) {
stack.push(head.left);
}
}
}
System.out.println();
}
//先压左再压右
public static void inOrderUnRecur(Node head) {
System.out.print("in-order: ");
if (head != null) {
Stack<Node> stack = new Stack<Node>();
while (!stack.isEmpty() || head != null) {
if (head != null) {
stack.push(head);
head = head.left;
} else {
head = stack.pop();
System.out.print(head.value + " ");
head = head.right;
}
}
}
System.out.println();
}
public static void posOrderUnRecur1(Node head) {
System.out.print("pos-order: ");
if (head != null) {
Stack<Node> s1 = new Stack<Node>();
Stack<Node> s2 = new Stack<Node>();
s1.push(head);
while (!s1.isEmpty()) {
head = s1.pop();
s2.push(head);
if (head.left != null) {
s1.push(head.left);
}
if (head.right != null) {
s1.push(head.right);
}
}
while (!s2.isEmpty()) {
System.out.print(s2.pop().value + " ");
}
}
System.out.println();
}
public static void posOrderUnRecur2(Node h) {
System.out.print("pos-order: ");
if (h != null) {
Stack<Node> stack = new Stack<Node>();
stack.push(h);
Node c = null;
while (!stack.isEmpty()) {
c = stack.peek();
if (c.left != null && h != c.left && h != c.right) {
stack.push(c.left);
} else if (c.right != null && h != c.right) {
stack.push(c.right);
} else {
System.out.print(stack.pop().value + " ");
h = c;
}
}
}
System.out.println();
}
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.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
preOrderUnRecur(head);
inOrderUnRecur(head);
posOrderUnRecur1(head);
posOrderUnRecur2(head);
}
}
如何直观的打印一颗二叉树
public class 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);
}
}
如何完成二叉树的宽度优先遍历(常见题目:求一棵二叉树的宽度)
import java.util.HashMap;
import java.util.LinkedList;
public class TreeMaxWidth {
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int data) {
this.value = data;
}
}
public static int getMaxWidth2(Node head) {
if (head == null) {
return 0;
}
int maxWidth = 0;
int curWidth = 0;
int curLevel = 0;
HashMap<Node, Integer> levelMap = new HashMap<>();
levelMap.put(head, 1);
LinkedList<Node> queue = new LinkedList<>();
queue.add(head);
Node node = null;
Node left = null;
Node right = null;
while (!queue.isEmpty()) {
node = queue.poll();
left = node.left;
right = node.right;
if (left != null) {
levelMap.put(left, levelMap.get(node) + 1);
queue.add(left);
}
if (right != null) {
levelMap.put(right, levelMap.get(node) + 1);
queue.add(right);
}
if (levelMap.get(node) > curLevel) {
curWidth = 0;
curLevel = levelMap.get(node);
} else {
curWidth++;
}
maxWidth = Math.max(maxWidth, curWidth);
}
return maxWidth;
}
}
C++二叉树的实现
二叉树节点
BinNode模板类
#define BinNodePosi(T) BinNode<T>* //节点位置
#define stature(p) ((p) ? (p)->height : -1) //节点高度(与“空树高度为-1”的约定相统一)
typedef enum { RB_RED, RB_BLACK} RBColor; //节点颜色
template <typename T> struct BinNode { //二叉树节点模板类
// 成员(为简化描述起见统一开放,读者可根据需要进一步封装)
T data; //数值
BinNodePosi(T) parent; BinNodePosi(T) lc; BinNodePosi(T) rc; //父节点及左、右孩子
int height; //高度(通用)
int npl; //Null Path Length(左式堆,也可直接用height代替)
RBColor color; //颜色(红黑树)
// 构造函数
BinNode() :
parent ( NULL ), lc ( NULL ), rc ( NULL ), height ( 0 ), npl ( 1 ), color ( RB_RED ) { }
BinNode ( T e, BinNodePosi(T) p = NULL, BinNodePosi(T) lc = NULL, BinNodePosi(T) rc = NULL,
int h = 0, int l = 1, RBColor c = RB_RED ) :
data ( e ), parent ( p ), lc ( lc ), rc ( rc ), height ( h ), npl ( l ), color ( c ) { }
// 操作接口
int size(); //统计当前节点后代总数,亦即以其为根的子树的规模
BinNodePosi(T) insertAsLC ( T const& ); //作为当前节点的左孩子插入新节点
BinNodePosi(T) insertAsRC ( T const& ); //作为当前节点的右孩子插入新节点
BinNodePosi(T) succ(); //取当前节点的直接后继
template <typename VST> void travLevel ( VST& ); //子树层次遍历
template <typename VST> void travPre ( VST& ); //子树先序遍历
template <typename VST> void travIn ( VST& ); //子树中序遍历
template <typename VST> void travPost ( VST& ); //子树后序遍历
// 比较器、判等器(各列其一,其余自行补充)
bool operator< ( BinNode const& bn ) { return data < bn.data; } //小于
bool operator== ( BinNode const& bn ) { return data == bn.data; } //等于
};
快捷方式
/************************************************************************************
* BinNode状态与性质的判断
***********************************************************************************/
#define IsRoot(x) ( ! ( (x).parent ) )
#define IsLChild(x) ( ! IsRoot(x) && ( & (x) == (x).parent->lc ) )
#define IsRChild(x) ( ! IsRoot(x) && ( & (x) == (x).parent->rc ) )
#define HasParent(x) ( ! IsRoot(x) )
#define HasLChild(x) ( (x).lc )
#define HasRChild(x) ( (x).rc )
#define HasChild(x) ( HasLChild(x) || HasRChild(x) ) //至少拥有一个孩子
#define HasBothChild(x) ( HasLChild(x) && HasRChild(x) ) //同时拥有两个孩子
#define IsLeaf(x) ( ! HasChild(x) )
/************************************************************************************
* 与BinNode具有特定关系的节点及指针
***********************************************************************************/
#define sibling(p) /*兄弟*/
( IsLChild( * (p) ) ? (p)->parent->rc : (p)->parent->lc )
#define uncle(x) /*叔叔*/
( IsLChild( * ( (x)->parent ) ) ? (x)->parent->parent->rc : (x)->parent->parent->lc )
#define FromParentTo(x) /*来自父亲的引用*/
( IsRoot(x) ? _root : ( IsLChild(x) ? (x).parent->lc : (x).parent->rc ) )
二叉树节点操作接口
插入孩子节点
template <typename T> BinNodePosi(T) BinNode<T>::insertAsLC ( T const& e )
{ return lc = new BinNode ( e, this ); } //将e作为当前节点的左孩子插入二叉树
template <typename T> BinNodePosi(T) BinNode<T>::insertAsRC ( T const& e )
{ return rc = new BinNode ( e, this ); } //将e作为当前节点的右孩子插入二叉树
遍历
template <typename T> template <typename VST> //元素类型、操作器
//二叉树中序遍历算法统一入口
void BinNode<T>::travIn ( VST& visit ) {
switch ( rand() % 5 ) { //此处暂随机选择以做测试,共五种选择
case 1: travIn_I1 ( this, visit ); break; //迭代版#1
case 2: travIn_I2 ( this, visit ); break; //迭代版#2
case 3: travIn_I3 ( this, visit ); break; //迭代版#3
case 4: travIn_I4 ( this, visit ); break; //迭代版#4
default: travIn_R ( this, visit ); break; //递归版
}
}
二叉树
BinTree模板类
#include "BinNode.h" //引入二叉树节点类
template <typename T> class BinTree { //二叉树模板类
protected:
int _size; BinNodePosi(T) _root; //规模、根节点
virtual int updateHeight ( BinNodePosi(T) x ); //更新节点x的高度
void updateHeightAbove ( BinNodePosi(T) x ); //更新节点x及其祖先的高度
public:
BinTree() : _size ( 0 ), _root ( NULL ) { } //构造函数
~BinTree() { if ( 0 < _size ) remove ( _root ); } //析构函数
int size() const { return _size; } //规模
bool empty() const { return !_root; } //判空
BinNodePosi(T) root() const { return _root; } //树根
BinNodePosi(T) insertAsRoot ( T const& e ); //插入根节点
//e作为x的左孩子(原无)插入
BinNodePosi(T) insertAsLC ( BinNodePosi(T) x, T const& e );
//e作为x的右孩子(原无)插入
BinNodePosi(T) insertAsRC ( BinNodePosi(T) x, T const& e );
BinNodePosi(T) attachAsLC ( BinNodePosi(T) x, BinTree<T>* &T ); //T作为x左子树接入
BinNodePosi(T) attachAsRC ( BinNodePosi(T) x, BinTree<T>* &T ); //T作为x右子树接入
int remove ( BinNodePosi(T) x ); //删除以位置x处节点为根的子树,返回该子树原先的规模
BinTree<T>* secede ( BinNodePosi(T) x ); //将子树x从当前树中摘除,并将其转换为一棵独立子树
template <typename VST> //操作器
void travLevel ( VST& visit ) { if ( _root ) _root->travLevel ( visit ); } //层次遍历
template <typename VST> //操作器
void travPre ( VST& visit ) { if ( _root ) _root->travPre ( visit ); } //先序遍历
template <typename VST> //操作器
void travIn ( VST& visit ) { if ( _root ) _root->travIn ( visit ); } //中序遍历
template <typename VST> //操作器
void travPost ( VST& visit ) { if ( _root ) _root->travPost ( visit ); } //后序遍历
bool operator< ( BinTree<T> const& t ) //比较器(其余自行补充)
{ return _root && t._root && lt ( _root, t._root ); }
bool operator== ( BinTree<T> const& t ) //判等器
{ return _root && t._root && ( _root == t._root ); }
}; //BinTree
高度更新
template <typename T> int BinTree<T>::updateHeight ( BinNodePosi(T) x ) //更新节点x高度
//具体规则,因树而异
{ return x->height = 1 + max ( stature ( x->lc ), stature ( x->rc ) ); }
//更新高度
template <typename T> void BinTree<T>::updateHeightAbove ( BinNodePosi(T) x )
{ while ( x ) { updateHeight ( x ); x = x->parent; } } //从x出发,覆盖历代祖先。可优化
节点插入
template <typename T> BinNodePosi(T) BinTree<T>::insertAsRoot ( T const& e )
{ _size = 1; return _root = new BinNode<T> ( e ); } //将e当作根节点插入空的二叉树
template <typename T> BinNodePosi(T) BinTree<T>::insertAsRC ( BinNodePosi(T) x, T const& e )
//e插入为x的右孩子
{ _size++; x->insertAsRC ( e ); updateHeightAbove ( x ); return x->rc; }
//insertAsLC()完全对称,在此省略
子树接入
template <typename T> //二叉树子树接入算法:将S当作节点x的左子树接入,S本身置空
BinNodePosi(T) BinTree<T>::attachAsLC ( BinNodePosi(T) x, BinTree<T>* &S ) {
//x->lc == NULL
if ( x->lc = S->_root ) x->lc->parent = x; //接入
_size += S->_size; updateHeightAbove ( x ); //更新全树规模与x所有祖先的高度
//释放原树,返回接入位置
S->_root = NULL; S->_size = 0; release ( S ); S = NULL; return x;
}
template <typename T> //二叉树子树接入算法:将S当作节点x的右子树接入,S本身置空
BinNodePosi(T) BinTree<T>::attachAsRC ( BinNodePosi(T) x, BinTree<T>* &S ) { //x->rc == NULL
if ( x->rc = S->_root ) x->rc->parent = x; //接入
_size += S->_size; updateHeightAbove ( x ); //更新全树规模与x所有祖先的高度
//释放原树,返回接入位置
S->_root = NULL; S->_size = 0; release ( S ); S = NULL; return x;
}
子树删除
template <typename T> //二叉树子树分离算法:将子树x从当前树中摘除,将其封装为一棵独立子树返回
BinTree<T>* BinTree<T>::secede ( BinNodePosi(T) x ) { //assert: x为二叉树中的合法位置
FromParentTo ( *x ) = NULL; //切断来自父节点的指针
updateHeightAbove ( x->parent ); //更新原树中所有祖先的高度
BinTree<T>* S = new BinTree<T>; S->_root = x; x->parent = NULL; //新树以x为根
S->_size = x->size(); _size -= S->_size; return S; //更新规模,返回分离出来的子树
}
遍历
先序遍历
template <typename T, typename VST> //元素类型、操作器
void travPre_R ( BinNodePosi(T) x, VST& visit ) { //二叉树先序遍历算法(递归版)
if ( !x ) return;
visit ( x->data );
travPre_R ( x->lc, visit );
travPre_R ( x->rc, visit );
}
后序遍历
template <typename T, typename VST> //元素类型、操作器
void travPost_R ( BinNodePosi(T) x, VST& visit ) { //二叉树后序遍历算法(递归版)
if ( !x ) return;
travPost_R ( x->lc, visit );
travPost_R ( x->rc, visit );
visit ( x->data );
}
中序遍历
template <typename T, typename VST> //元素类型、操作器
void travIn_R ( BinNodePosi(T) x, VST& visit ) { //二叉树中序遍历算法(递归版)
if ( !x ) return;
travIn_R ( x->lc, visit );
visit ( x->data );
travIn_R ( x->rc, visit );
}
如何判断一颗二叉树是完全二叉树?
每一层都是满的,最后一层由左到右
- 有右无左
- 不违规条件1,如果遇到第一个左右子不全,后续都是叶节点
import java.util.LinkedList;
public class IsCBT {
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int data) {
this.value = data;
}
}
public static boolean isCBT(Node head) {
if (head == null) {
return true;
}
LinkedList<Node> queue = new LinkedList<>();
boolean leaf = false;
Node l = null;
Node r = null;
queue.add(head);
while (!queue.isEmpty()) {
head = queue.poll();
l = head.left;
r = head.right;
if ((leaf && (l != null || r != null)) || (l == null && r != null)) {
return false;
}
if (l != null) {
queue.add(l);
}
if (r != null) {
queue.add(r);
} else {
leaf = true;
}
}
return true;
}
}
二叉树的相关概念及其实现判断
树型DP
如何判断一颗二叉树是否是搜索二叉树?
左子树比右子树小,中序遍历判断是否升序
import java.util.LinkedList;
public class IsBST {
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int data) {
this.value = data;
}
}
public static boolean isBST(Node head) {
if (head == null) {
return true;
}
LinkedList<Node> inOrderList = new LinkedList<>();
process(head, inOrderList);
int pre = Integer.MIN_VALUE;
for (Node cur : inOrderList) {
if (pre >= cur.value) {
return false;
}
pre = cur.value;
}
return true;
}
public static void process(Node node, LinkedList<Node> inOrderList) {
if (node == null) {
return;
}
process(node.left, inOrderList);
inOrderList.add(node);
process(node.right, inOrderList);
}
}
递归套路,左右树都是搜索树
左max<x,右min>x
public static class ReturnData{
public boolean isBST;
public int min;
public int max;
public ReturnData(boolean is, int mi, int ma){
isBST = is;
min = mi;
max = ma;
}
}
public static ReturnData process(Node x){
if(x==null){
return null;
}
ReturnData leftData = process(x.left);
ReturnData rightData = process(x.right);
int min = x.value;
int max = x.value;
if(leftData!=null){
min = Math.min(min, leftData.min);
max = Math.max(max, leftData.max);
}
if(rightData!=null){
min = Math.min(min, rightData.min);
max = Math.max(max, rightData.max);
}
boolean isBST = true;
if(leftData!=null && (!leftData.isBST || leftData.max >= x.value)){
isBST = false;
}
if(rightData!=null && (!rightData.isBST || x.value >= rightData.min )){
isBST = false;
}
return new ReturnData(isBST, min, max);
}
如何判断一颗二叉树是否是满二叉树?
返回 整棵树的高度 和 整棵树的节点个数
public static boolean isF(Node head){
if(head == null){
return true;
}
Info data = f(head)
}
public static class Info{
public int height;
public int nodes;
public Info(int h, int n){
height = h;
nodes = n;
}
}
public static Info f(Node){
if(x==null){
return new Info(0,0);
}
Info leftData = f(x.left);
Info rightData = f(x.right);
int height = Math.max(leftData.height,rightData.height)+1;
int nodes = leftData.nodes + rightData.nodes + 1;
return new Info(height, nodes);
}
如何判断一颗二叉树是否是平衡二叉树?(二叉树题目套路)
左右树是平衡树
|左高-右高|=<1
public class IsBalancedTree {
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int data) {
this.value = data;
}
}
public static boolean isBalanced(Node head) {
return process(head).isBalanced;
}
public static class ReturnType {
public boolean isBalanced;
public int height;
public ReturnType(boolean isB, int hei) {
isBalanced = isB;
height = hei;
}
}
public static ReturnType process(Node x) {
if (x == null) {
return new ReturnType(true, 0);
}
ReturnType leftData = process(x.left);
ReturnType rightData = process(x.right);
int height = Math.max(leftData.height, rightData.height);
boolean isBalanced = leftData.isBalanced && rightData.isBalanced
&& Math.abs(leftData.height - rightData.height) < 2;
return new ReturnType(isBalanced, height);
}
}
最低公共祖先节点
给定两个二叉树的节点node 1和node2,找到他们的最低公共祖先节点
// o1和o2一定属于head为头的树
// 返回o1和o2的最低公共祖先
public static Node lca(Node head, Node o1. o2){
HashMap<Node, Node> fatherMap = new HashMap<>();
fatherMap.put(head,head);
process(head, fatherMap);
HashSet<Node> set1 = new HashSet<>();
Node cur = o1l;
while(cur != fatherMap.get(cur)){
set1.add(cur);
cur = fatherMap.get(cur);
}
set1.add(head);
//while () o2
}
public static void process(Node head, HashMap<Node, Node> fatherMap){
if(head == null){
return;
}
fatherMap.put(head.left,head);
fatherMap.put(head.right,head);
process(head.left,fatherMap);
process(head.right,fatherMap);
}
方法二
- o1是o2的公共祖先或o2是o1的公共祖先
- o1与o2互不为公共祖先,往上才能找到
public static Node lowestAncestor(Node head, Node o1, Node o2){
if (head == null || head == o1 || head == o2) {
return head;
}
Node left = lowestAncestor(head.left, o1, o2);
Node right = lowestAncestor(head.right, o1, o2);
if (left != null && right != null) {
return head;
}
//左右两棵树,并不都有返回值
return left != null ? left : right;
}
在二叉树中找到一个节点的后继节点
【题目】现在有一种新的二叉树节点类型如下:
public class Node {
public int value;
public Node left;
public Node right; public Node parent;
public Node (int val) {
value = val;
}
}
该结构比普通二叉树节点结构多了一个指向父节点的parent指针。
假设有一棵Node类型的节点组成的二叉树,树中每个节点的parent指针都正确地指向自己的父节点,头节 点的parent指向nul l。
只给一个在二叉树中的某个节点node,请实现返回node的后继节点的函数。 在二叉树的中序遍历的序列中,node的下一个节点叫作node的后继节点。
后继节点,中序遍历的下一个
- X有右树的时候
- X无右树
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 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.offer(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;
}
折纸问题
请把一段纸条竖着放在桌子上,然后从纸条的下边向上方对折1次,压出折痕后展开。
此时折痕是凹下去的,即折痕突起的方向指向纸条的背面。
如果从纸条的下边向上方连续对折2次,压出折痕后展开,此时有三条折痕,从 上到下依次是下折痕、下折痕和上折痕。
给定一个输入参数N,代表纸条都从下边向上方连续对折N次。
请从上到下打印所有折痕的方向。
例如:N=1 时,打印:down N=2时,打印:down down up
public class Code10_PaperFolding {
public static void printAllFolds(int N) {
printProcess(1, N, true);
}
public static void printProcess(int i, int N, boolean down{
if (i > N) {
return;
}
printProcess(i + 1, N, true);
System.out.println(down ? "down " : "up ");
printProcess(i + 1, N, false);
}
public static void main(String[] args) {
int N = 1;
printAllFolds(N);
}
}