算法[4] 二叉树

部分递归练习题在后面的二叉树模版中

二叉树

二叉树的先序、中序、后序遍历

先序:任何子树的处理顺序都是,先头节点、再左子树、然后右子树

中序:任何子树的处理顺序都是,先左子树、再头节点、然后右子树

后序:任何子树的处理顺序都是,先左子树、再右子树、然后头节点

递归序

每个结点都会经过三次

前序,第一到达打印

中序,第二次

后序,第三次

非递归遍历

前序遍历

准备一个栈,

1.第一步将非空根结点压栈

出栈并打印

2.先将非空右子树结点压栈

3.后将非空左子树结点压栈

出栈并打印

重复2,3

public static void pre(Node head) {
    
    
    System. out. print("pre-order: ");
    if (head != nu1l) {
    
    
        Stack<Node> stack = new Stack<Node>();
        stack. add(head); .
        while (!stack. isEmpty()) {
    
    
            head = stack. pop();
            Sys tem . out . print(head. value +");
                if (head.right != nu1l) {
    
    
                stack. push(head. right);
                }
                if (head.left != nu11) {
    
    
                stack. push(head.1eft);
                }
            }
        }
    System. out . print1n();
}

这样打印出来是 头 左右

如果先压入左孩子结点

打印顺序就是 头 右 左

逆序之后即为,左右头,即后序遍历的顺序

后序非递归:

public static void pos1(Node head) {
    
    
    System. out . print(" pos-order: ");
    if (head != nu1l) {
    
    
    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.1eft != nu11) {
    
    
        	s1. push(head.1eft);
        }
        if (head.right != nu11) {
    
    
        	s1. push(head.right);
        }
        while (!s2.isEmpty()) {
    
    
        System . out . print(s2. pop().value + " ");
        }
    }
	System. out . println();
}

中序非递归

1、先依次压入左边的结点,直到最左结点为空

2、弹出并打印,如果有右子数,压入栈中

​ 继续执行1

public static void in(Node head) {
    
    
    System. out . print(" in-order: ");
    if (head != null) {
    
    
        Stack<Node> stack = new Stack<Node>();
        while (!stack. isEmpty()|| head != null) {
    
    
            if (head != nu1l) {
    
    
            stack. push(head);
            head = head.left;
            } else {
    
    
            head = stack. pop();
            System. out . print(head.value + " ");
            head = head.right;
            }
		}
    }
System. out . println( );
}


    public static void main(String[] args) {
    
    
        TreeNode node = new TreeNode(1);
        TreeNode node2 = new TreeNode(2);
        TreeNode node3 = new TreeNode(3);
        TreeNode node4 = new TreeNode(4);
        node.left=node2;
        node.right=node3;
        node2.right=node4;
        in(node);
    }

实现二叉树的按层遍历

1 其实就是宽度优先遍历,用队列

2可以通过设置flag变量的方式,来发现某一层的结束(看题目)

public static void 1evel(Node head) {
    
    
    if (head == nu11) {
    
    
    return;
    }
    Queue<Node> queue = new  LinkedList<>();
    queue . add(head);
    while (!queue . isEmpty()) {
    
    
        Node cur = queue.poll();
        System . out . println(cur .value);
        if (cur.left != null) {
    
    
        queue. add(cur .left);
        }
        if (cur.right != null) {
    
    
        queue. add(cur .right);
        }
    }
}

求二叉树最大宽度

此方法也可以求最大深度

public static int maxWidthUseMap(Node head) {
    
    
    if (head == nu11) {
    
    
    return 0;
    Queue<Node> queue = new LinkedList<>();
    queue . add(head);
    //key在哪--层,value
    HashMap<Node, Integer> levelMap = new HashMap<>();
    levelMap. put(head, 1);
    int curLevel = 1; //当前你正在统计哪一层的宽度
    int curLevelNodes = 0; //当前层curLeve1层, 宽度目前是多少.
    int max = 0;
    while (!queue . isEmpty()) {
    
    
        Node cur = queue. po11();
        int curNodeLevel = levelMap. get(cur);
        if (cur.1eft != nu1l) {
    
    
        	levelMap. put(cur .1eft, curNodeLevel + 1);	
        	queue. add( cur.left);
        }
        if (cur .right != nu1l) {
    
    
        	levelMap. put(cur. right, curNodeLevel + 1);
        	queue . add(cur.right);
        }
        if (curNodeLevel == curLevel) {
    
    
        	curLeve 1Nodes++;
        } else {
    
    
        	max = Math .max(max, curLevelNodes); 
       		 curLevel++;
        	curLevelNodes = 1;
        }
     }
        // 因为最后一层没有结算,所以需要再比较一次
        max = Math . max(max, curLevelNodes);
        return max;
}


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

1)可以用先序或者中序或者后序或者按层遍历,来实现二叉树的序列化

2)用了什么方式序列化,就用什么样的方式反序列化

即实现保存整个树,然后再重塑这个树

比如

1 1

​ 1 1

1 1

两颗树,但是只记录顺序是无法还原的

解决办法

1.不要忽略空结点,然后正常遍历这个树

public static Queue<String> preSerial(Node head) {
    
    
    Queue<String> ans = new     LinkedList<>();
    pres(head, ans);
    return ans ;
}
    public static void pres(Node head, Queue<String> ans) {
    
    
        if (head == nu1l) {
    
    
        ans. add(nu11) ;
        } else {
    
    
        ans . add( String . valueOf(head. value));
        pres(head.left, ans);
        pres(head.right, ans);
        }
}

层序遍历序列化

public static Queue<String> levelSerial(Node head) {
    
    
    Queue<String> ans = new LinkedList<>();
    if (head == nu11) {
    
    
    ans . add(nu11);
    } else {
    
    
    ans . add(String . value0f(head.value));
    Queue<Node> queue = new LinkedL ist<Node>() ;
    queue. add(head);
    while (!queue . isEmpty()) {
    
    
        head = queue. po11();
        if (head.1eft != nu11) {
    
    
        ans . add(String. value0f(head. left.value));
        queue. add(head.1eft);
        } else {
    
    
        ans . add(nu11);
            if (head.right != nu1l) {
    
    
            ans. add(String. valueOf(head. right .value));
            queue . add( head. right) ;
            } else {
    
    
            ans . add(nu11);
            }
        }
    }
return ans;
}

反序列化

public static Node preb(Queue<String> prelist) {
    
    
    String value = prelist.pol1();
    if (value == nu1l) {
    
    
    return nu11 ; 
    }
    Node head = new Node(Integer . value0f(value));
    head.1eft = preb(prelist);
    head.right = preb(prelist);
    return head;
}

层序反序列

public static Node generateNode(String val) {
    
    
    if (val == nu1l) {
    
    
    return nu11 ;
    }
    return new  Node(Integer . value0f(val));
}
public static Node buildByLeve1Queue (Queue<String> levelList) {
    
    
    if (levelList == nu1l | | levelList.size() == 0) {
    
    
    return nu1l ; 
    }
    Node head = generateNode(levelList. po11());
    Queue<Node> queue = new LinkedL ist<Node>();
    if (head != nu1l) {
    
    
    queue . add (head);
    }
    Node node = nu1l;
 
    while (!queue . isEmpty()) {
    
    
        node = queue. pol1( );
        node.1eft = generateNode (levelList. pol1());
        node.right = generateNode(levelList.po11());
        if (node.1eft != nu1l) {
    
    
        queue . add(node.1eft);
        }
        if (node.right != nu11) {
    
    
        queue . add( node. right);
        }
    }
    return head;
}

如何设计一个打印整棵树的打印函数

1.把树补成满二叉树

2.将每颗子树都按照头右左的顺序打印

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 == nu1l) {
    
    
    return;
    }
    printInOrder(head.right, height + 1, "V", len);
    // 控制空格数,
    String val = to + head.value + to;
    int 1enM = val.length();
    int lenL = (1en - 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);
}

给你二叉树中的某个节点,返回该节点的后继节点

二叉树结构如下定义:

Class Node {
V value;

Node left;

Node right;

Node parent;
}

后继节点指按中序遍历遍历后,该结点后面的节点即为当前节点的后继节点

1.找到头节点,然后中序遍历,找后继节点

2.能够直接找到?

  • 某个节点的后续是右子数上的最左节点

  • 某个节点没有右子树,往上找父亲节点的左孩子,该节点是该节点 父亲节点的左孩子 就停

    那个父亲节点就是后继节点

3.整棵树的最右节点是没有后继的,返回null

public static Node getSuccessorNode(Node node) {
    
    
    if (node = nu1l) {
    
    
    return node;
    }
    if (node.right != null) {
    
    
        // 情况1 ,有右树
    	return getLeftMost(node.right);
    } else {
    
     //无右子树
        Node parent = node . parent ;
        while (parent != null && parent.right == node) {
    
     //当前节点是其父亲节点右孩子
        	node = parent;
        	parent = node. parent;
        }
        //只有情况2.3是一样的
    }
    return parent;
}
public static Node getLeftMost(Node node) {
    
    
    if (node == nu1l) {
    
    
    return node ;
    }
    while (node.1eft != nu11) {
    
    
    node = node .left;
    }
    return node ;
}

变式题:找前驱节点

纸条折痕问题

请把一段纸条竖着放在桌子上,然后从纸条的下边向上方对折1次,压出折痕
后展开。此时折痕是凹下去的,即折痕突起的方向指向纸条的背面。如果从
纸条的下边向,上方连续对折2次,压出折痕后展开,此时有三条折痕,从上到
下依次是下折痕、下折痕和上折痕。
给定一个输入参数N,代表纸条都从下边向上方连续对折N次。请从上到下打
印所有折痕的方向。
例如:N=1时,打印: down N=2时,打印: down down up

n-1次对折后,n-1次的折痕上方都是 凹折痕,下方都是凸折痕

3次的结果:

3凹

​ 2凹

3凸

​ -----1凹-----

3凹

​ 2凸

3凸

其实就是一个满二叉树,要想从上到下打印,就是中序遍历

左根右

N代表层数

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) {
    
    
    intN=3;
    printAllFolds(N);
}

递归开N层,空间复杂度O(N)

参考

猜你喜欢

转载自blog.csdn.net/qq_41852212/article/details/120919232