每日算法总结——二叉树的打印、二叉树的宽度优先遍历、求二叉树最大宽度

一、二叉树的打印

打印函数网上右很多,这里这记录一种,目的是用来调试。

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

        public Node(int data) {
            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 - lenL - lenM;
        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 bfsBinaryTree(Node head) {
    if (head == null) {
        return;
    }
    Queue<Node> queue = new LinkedList<>();
    queue.add(head);
    while (!queue.isEmpty()) {
        Node cur = queue.poll();
        System.out.print(cur.value + " ");
        if (cur.left != null) {
            queue.add(cur.left);
        }
        if (cur.right != null) {
            queue.add(cur.right);
        }
    }
}

三、求二叉树的最大宽度

一棵树的最大宽度是指二叉树上某层包含的最多节点数

方法一、使用HashMap
  • 算法思想:在宽度优先遍历的基础上,设立一个HashMap,存储每个节点所在的层号;初始时,设立几个变量:当前层号curLevel、当前层的节点数curLevelNodes、最大宽度max。将节点放入队列的同时,在HashMap中存储该节点所在的层数;弹出节点时,哈希表判断当前节点所在层是否与curLevel相同,相同则说明该节点属于该层,curLevelNodes++,不相同则说明该节点不属于该层,已经进入到了下一层,则curLeve++,同时更新max
/**
 * 使用HashMap求数的最大宽度
 */
public static int treeMaxWidth1(Node head) {
    if (head == null) {
        return 0;
    }
    Map<Node, Integer> map = new HashMap<>();
    Queue<Node> queue = new LinkedList<>();
    int curLevel = 1;
    int curLevelNodes = 0;
    int max = Integer.MIN_VALUE;
    queue.add(head);
    map.put(head, curLevel);
    while (!queue.isEmpty()) {
        Node cur = queue.poll();
        if (map.get(cur) == curLevel) {
            curLevelNodes++;
        } else {
            curLevel++;
            max = Math.max(max, curLevelNodes);
            curLevelNodes = 1;
        }
        if (cur.left != null) {
            map.put(cur.left, curLevel + 1);
            queue.add(cur.left);
        }
        if (cur.right != null) {
            map.put(cur.right, curLevel + 1);
            queue.add(cur.right);
        }
    }
    return Math.max(max, curLevelNodes);
}
方法二、使用count变量遍历每层
  • 算法思想:与使用HashMap的方法不同,该方法使用两个循环,外循环遍历,内循环遍历每层的节点,每次内循环开始之前,都用count记录此时队列中的节点数量size,每次内循环都从队列中弹出一个节点进行处理,同时count--,直到count == 0本层遍历结束。
/**
 * 使用count计数每层
 */
public static int treeMaxWidth2(Node head) {
    if (head == null) {
        return 0;
    }
    Queue<Node> queue = new LinkedList<>();
    int curLevelNodes = 0;
    int max = Integer.MIN_VALUE;
    queue.add(head);
    while (!queue.isEmpty()) {
        int count = queue.size();
        curLevelNodes = 0;
        while (count > 0){
            Node cur = queue.poll();
            curLevelNodes++;
            if (cur.left != null) {
                queue.add(cur.left);
            }
            if (cur.right != null) {
                queue.add(cur.right);
            }
            count--;
        }
        max = Math.max(max, curLevelNodes);
    }
    return max;
}
方法三、滚动更新每层的最后一个节点
  • 算法思想
    • 设定四个变量Node curEnd(当前层最后一个节点),Node nextEnd(当前队列中最后一个节点),int curLevelNode(当前层节点数),int max(最大宽度)
    • 初始时,将root放入队列中,curEnd = rootnextEnd = null,之后每弹出一个节点,就将其左右节点放入队列(如果有的话),同时更新nextEnd为当前队列中的最后一个节点,然后判断当前节点是否等于curEnd,如果等于,说明已经到了该层的最后一个节点,此时更新curEnd = nextEnd; nextEnd = null; max = Math.max(curLevelNode, max);如果不等于,只更新nextEnd。重复直到队列为空。
/**
 * 滚动更新每层的最后一个节点
 */
public static int treeMaxWidth3(Node head) {
    if (head == null) {
        return 0;
    }
    Queue<Node> queue = new LinkedList<>();
    queue.add(head);
    Node curEnd = head;
    Node nextEnd = null;
    int curLevelNodes = 0;
    int max = 0;
    while (!queue.isEmpty()) {
        Node cur = queue.poll();
        curLevelNodes++;
        if (cur.left != null) {
            queue.add(cur.left);
            nextEnd = cur.left;
        }
        if (cur.right != null) {
            queue.add(cur.right);
            nextEnd = cur.right;
        }
        if (curEnd == cur) {
            curEnd = nextEnd;
            nextEnd = null;
            max = Math.max(max, curLevelNodes);
            curLevelNodes = 0;
        }
    }
    return max;
}
实战
实战:二叉树的层序遍历
  • leetcode 原题:102. 二叉树的层序遍历 - 力扣(LeetCode)
  • 难度等级: Medium
  • 思路:该题使用count遍历每层要比使用HashMap快得多。
实战:二叉树最大宽度
  • leetcode 原题:662. 二叉树最大宽度 - 力扣(LeetCode)
  • 难度等级: Medium
  • 思路
    • 需要注意这题要求的宽度与上面所说的宽度不一致,每层两端点之间的null节点也计入该层的宽度。因此我们不能简单的记录每层有多少个节点,而是需要记录每层节点的下标(即把二叉树看做是一个存储在数组中的满二叉树),下标的记录我们可以使用额外的HashMap来存储,或者直接在队列中存储一个Pair,这样会快不少。
    • 但其实对于该题来说Nodeval是没有用的,可以直接将下标存储在val中,但后面就没法复原了,所以刷题时可用,但实际运行效率相比Pair快了那么一丢丢。
    • 记录了下标,还要维护一对边界变量left,right,用于记录每层左右端点的下标。
class Solution {
    public int widthOfBinaryTree(TreeNode root) {
        int max = -1;
        int left = 0, right = 0;
        Queue<Pair<TreeNode, Integer>> queue = new LinkedList<>();
        queue.add(new Pair<TreeNode, Integer>(root, 0));
        while (!queue.isEmpty()) {
            left = 0;
            right = 0;
            int count = queue.size();
            while (count > 0) {
                Pair<TreeNode, Integer> cur = queue.poll();
                int index = cur.getValue();
                TreeNode curNode = cur.getKey();
                if (left == 0) {
                    left = index;
                    right = index;
                } else {
                    right = index;
                }
                if (curNode.left != null) {
                    queue.add(new Pair<>(curNode.left, 2 * index + 1));
                }
                if (curNode.right != null) {
                    queue.add(new Pair<>(curNode.right, 2 * index + 2));
                }
                count--;
            }
            max = Math.max(max, right - left + 1);
        }
        return max;
    }
}

你可能感兴趣的:(每日算法,算法,宽度优先,java,数据结构)