持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第16天,点击查看活动详情 下面是我整理的跟着b站左程云的数据结构与算法学习笔记,欢迎大家一起学习。
搜索二叉树
搜索二叉树:每一颗子树,左树的节点都比他小,右树的节点都比他大 判断搜索二叉树:中序遍历时一定升序 思路分析 先判断左树上节点,将其赋值给一个全局变量,将头节点和右树上的节点依次比较,若有小于情况,则返回false,左树上的节点和头节点都已经比较,最后返回右树的比较结果 代码实现:采用递归的方法
public static int preValue=Integer.MIN_VALUE;
public static boolean isBST(Node head){
if (head==null){
return true;
}
//先判断左树
boolean isLeftBST = isBST(head.left);
if (!isLeftBST){
return false;
}
if (head.value<=preValue){
return false;
}else {
preValue=head.value;
}
return isBST(head.right);
}
实质是利用中序遍历再比较判断是否为升序
// 相当于
public static boolean isBST2(Node head){
List<Node> inOrderList = new ArrayList<>();
process(head, inOrderList);
//此时inOrderList存放的是中序遍历的结果
for (int i = 0; i < inOrderList.size()-1 ; i++) {
if (inOrderList.get(i).value>inOrderList.get(i+1).value){
return false;
}
}
return true;
}
public static void process(Node head, List<Node> inOrderList ){
if (head==null){
return;
}
process(head.left, inOrderList);
inOrderList.add(head);
process(head.right, inOrderList);
}
也可以采用非递归的方法
//非递归方式
public static boolean isBST3(Node head){
if (head==null){
return true;
}
int preValue=Integer.MIN_VALUE;
Stack<Node> stack = new Stack<>();
while (!stack.isEmpty()||head!=null){
if (head!=null){
stack.push(head);
head=head.left;
}else {
head=stack.pop();
if (head.value<preValue){
return false;
}else {
preValue=head.value;
}
head=head.right;
}
}
return true;
}
其他思路 左右树都为搜索二叉树,左树得到max,右树得到min,可以将递归得到该树是否为搜索二叉树,最小值和最大值 代码实现
public static class ReturnData{
public boolean isBST;
public int max;
public int min;
public ReturnData(boolean isBST, int max, int min) {
this.isBST = isBST;
this.max = max;
this.min = min;
}
}
public static ReturnData process(Node head){
if (head==null){
return null;
}
ReturnData leftData = process(head.left);
ReturnData rightData = process(head.right);
int min=head.value;
int max= head.value;
if (leftData!=null){
min=Math.min(min, leftData.min);
max=Math.min(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>= head.value)){ isBST=false; } if (rightData!=null&&(!rightData.isBST||rightData.min<= head.value)){ isBST=false; }*/// 等效于
boolean isBST=false;
if (
(leftData!=null?(leftData.isBST&&leftData.max<head.value):true)
&&
(rightData!=null?(rightData.isBST&&rightData.min> head.value):true)
){
isBST=true;
}
return new ReturnData(isBST, max,min);
}
完全二叉树
完全二叉树 从左到右依次编满 判断完全二叉树的方法
- 即任一节点有右无左则返回false
- 在1条件满足下,如果遇到第一个左右孩子不全,后续皆为叶节点
诸如
代码实现
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;
}
满二叉树
节点个数N和最大深度L满足 N=2^L-1 可以通过得到节点个数N和最大深度L判断
平衡二叉树
平衡二叉树:对于任何一个子树来说,左树和右树的高度差都不超过1
代码实现
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);
}
题型总结 二叉树的树形DP问题,即可以通过左树要信息和右树要信息,都可以通过递归的方法求解
找到node1和node2得到最低公共祖先
以该图为例,E和F的最低公共节点为E,D与F的最低公共节点为B
将D节点的父节点依次遍历放进set中,不断遍历f的父节点,当set中包含节点时,遍历结束 代码实现
public static Node leastAncestor(Node head,Node o1,Node o2){
Node resultNode=null;
HashMap<Node,Node> ancestorMap = new HashMap<>();
ancestorMap.put(head, head);
accomplishNodeToMap(head, ancestorMap);
HashSet<Node> set1 = new HashSet<>();
Node cur=o1;
while (cur!=ancestorMap.get(cur)){
set1.add(cur);
cur=ancestorMap.get(cur);
}
set1.add(head);
cur=o2;
//此时set里面保存了
while (cur!=ancestorMap.get(cur)){
if (set1.contains(cur)){
resultNode=cur;
break;
}
}
return resultNode;
}
public static void accomplishNodeToMap(Node head,HashMap<Node,Node> ancestorMap ){
if (head==null){
return;
}
ancestorMap.put(head.left,head);
ancestorMap.put(head.right, head);
accomplishNodeToMap(head.left, ancestorMap);
accomplishNodeToMap(head.right, ancestorMap);
}
代码简化
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;
}
情况分析:
- O1,O2之间有一个为最低公共祖先
- O1与O2不互为最低公共祖先,需要往上汇聚才能找到
代码运行解析
- 在第一种情况中,可能两者节点都在左边,右边返回null,左边返回最低公共祖先,返回正确情况
- 在第二种情况中 会命中左边不为空和右边不为空,返回本身
问题:找到后继节点
查找后继节点
把所有节点的后继节点找到,需要的代码复杂度为O(N)
如果有父节点D可以很方便找到自己的后继节点B,E可以遍历两次找到自己的后继节点A,可将代码的时间复杂度优化到 O(K) k为两者相距的距离
情况分析:
- x有右树的时候 后继节点为右树上的最左节点
- x无右树
如果是最右节点,后继为空
如果不是最右上的节点 不断往上找 当发现有一个节点是其父节点上的左孩子,返回父节点
当父节点走到空,即头节点,此时节点为最右节点,没有后继节点,返回空
问题 二叉树的序列化和反序列化
先序方式遍历的序列化
其中#表示为空 ,__ 表示值的地址
代码实现
//序列化
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.add(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;
}
不断消费队列重构树
折纸问题
中序遍历的方式打印,但是只使用了N的空间