二叉树相关算法总结

删除节点原理示意图
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
构造队列方法

public class Queue<T>implements Iterable<T> {
		//记录首结点
		private Node head;
		//记录首结点
		private Node last;
		//当前栈的元素个数
		private int N;
		
	 public Queue() {
			// TODO 自动生成的构造函数存根
			head=new Node(null,null);
			last=null;
			N=0;
		}
		//定义节点类,以便上面可以使用Node,类的定义方法是private/private class 类名{}
		private class Node{
			//定义数据域
			public T item;
			//指向下一个节点
			public Node next;
			
			//生成构造方法
			public Node(T item, Node next) {
				this.item = item;
				this.next = next;
			}
		}
		//获取栈中元素
		public int size() {
			return N;
		}
		//判断当前栈中元素是否为0
		public boolean isEmpty() {
			return N==0;
		}
		//要明确无论是插入,都是从尾结点开始的,因为每插入一个节点都是尾结点
		//向队列中插入元素
		public void enqueue(T t) {
			if (last==null) {
				//如果对列为空,则存入的结点就是尾结点
				last=new Node(t, null);
				//收结点的下一个节点指向尾结点,来建立对列之间的联系
				head.next=last;
			}else {
				//插入前老尾结点为尾结点
				Node oldLast=last;
				//令新插入的节点为尾结点
				last=new Node(t, null);
				//老尾结点的下一个节点为新尾结点
				oldLast.next=last;
			}
				//元素个数加1
				N++;
					
		}
		
		//从队列中拿出一个元素
		public T dequeue() {
			if (isEmpty()) {
				return null;
			}
			//首节点的下一个节点等变成首节点的下两个节点,说明首节点下一个节点被删除了
			Node oldFirst=head.next;
			head.next=oldFirst.next;
			N--;
			if (isEmpty()) {
				last=null;
			}
			return oldFirst.item;
			
		}
		
		@Override
		//Iterator<T>是接口,需要一个类去实现方法,实现返回,所以定义了public class SIterator implements Iterator<T>
		//去实现hasnext和next方法
		public Iterator<T> iterator() {
			
			return new QIterator();
		}
			
		public class QIterator implements Iterator<T> {
			//Iterator<T>接口实现循环遍历,是从头开始遍历,所以如此定义n为头结点。
			private Node n=head;
			@Override
			public boolean hasNext() {
				
				return n.next!=null;
			}

			@Override
			public T next() {		
				//因为head并不存储数据,所以head.next,让指针后移一位开始找数据
				n=n.next;
				return n.item;
			}

		}

	}

实现代码

public class BinaryTree<Key extends Comparable<Key>,Value> {
    //记录根节点
    private Node root;
    //记录树中元素个数
    private int N;

//节点类
private class Node {
    //存储键
    public Key key;
    public Value value;
    public Node left;
    public Node right;

    public Node(Key key, Value value, Node left, Node right) {
        this.key = key;
        this.value = value;
        this.left = left;
        this.right = right;
    }
}

    //获取树中元素个数
    public int size() {
        return N;
    }
    
    
插入方法put实现思想:
1.如果当前树中没有任何一个结点,则直接把新结点当做根结点使用
2.如果当前树不为空,则从根结点开始:
 2.1如果新结点的key小于当前结点的key,则继续找当前结点的左子结点;
 2.2如果新结点的key大于当前结点的key,则继续找当前结点的右子结点;
 2.3如果新结点的key等于当前结点的key,则树中已经存在这样的结点,替换该结点的value值即可。
    //添加元素key-value
    public void put(Key key, Value value) {
        root = put(root, key, value);
    }

    //向指定树x中添加key-value,并返回添加元素后的新树
    private Node put(Node x, Key key, Value value) {
        if (x == null) {
            N++;
            return new Node(key, value, null, null);
        }
        int cmp = key.compareTo(x.key);
        if (cmp > 0) {
            //新结点的key大于当前节点的key,递归调用继续寻找当前节点的右子节点,知道找到相同
            x.right = put(x.right, key, value);
        } else if (cmp < 0) {
            //新结点的key小于当前节点的key,递归调用继续寻找当前节点的左子节点,知道找到相同
            x.left = put(x.left, key, value);
        } else {
            //新结点的key等于当前节点的key,把当前节点的value值替换
            x.value = value;
        }
        //返回添加完元素后的新树
        return x;
    }
    
查询方法get实现思想:
从根节点开始:
 1.如果要查询的key小于当前结点的key,则继续找当前结点的左子结点;
 2.如果要查询的key大于当前结点的key,则继续找当前结点的右子结点;
 3.如果要查询的key等于当前结点的key,则树中返回当前结点的value。
    //查询树中指定key对应value
    public Value get(Key key) {
        return get(root, key);
    }

    private Value get(Node x, Key key) {
        if (x == null) {
            return null;
        }
        int cmp = key.compareTo(x.key);
        if (cmp > 0) {
            return get(x.right, key);
        } else if (cmp < 0) {
            return get(x.left, key);
        } else {
            return x.value;
        }

    }

    //找出树中最小节点
    public Key min() {
        return min(root).key;
    }

    private Node min(Node x) {
        if (x.left != null) {
            return min(x.left);
        } else {
            return x;
        }
    }

    //找出树中最大节点
    public Key max() {
        return max(root).key;
    }

    private Node max(Node x) {
        if (x.right != null) {
            return max(x.right);
        } else {
            return x;
        }
    }
    
删除方法delete实现思想:
 1.找到被删除结点;
 2.找到被删除结点右子树中的最小结点minNode
 3.删除右子树中的最小结点
 4.让被删除结点的左子树称为最小结点minNode的左子树,让被删除结点的右子树称为最小结点minNode的右子
树
 5.让被删除结点的父节点指向最小结点minNode
    //删除树中key对应的value
    public void delete(Key key) {
        root = delete(root, key);
    }

    //删除指定树x中key对应的value,并返回删除后的新树
    public Node delete(Node x, Key key) {
        if (x == null) {
            return null;
        }
        int cmp = key.compareTo(x.key);
        //新结点的key大于当前节点的key,则递归调用继续寻找当前节点的右子节点
        if (cmp > 0) {
            x.right = delete(x.right, key);
            //新结点的key小于当前节点的key,则递归调用继续寻找当前节点的左子节点
        } else if (cmp < 0) {
            x.left = delete(x.left, key);
            //新结点的key等于当前节点的key,当前x就是要删除的节点
        } else {
            //1.如果当前节点的右子节点不存在,则直接返回当前节点的左子节点
            if (x.right == null) {
                return x.left;
            }
            //2.如果当前节点的左子节点不存在,则直接返回当前节点的右子节点
            if (x.left == null) {
                return x.right;
            }
            //3.1找到右子树中最小的节点
            Node minNode = x.right;
            while (minNode.left != null) ;
            minNode = minNode.left;
            //3.2删除右字树中最小的节点
            Node n = x.right;
            while (n.left != null) {
                if (n.left.left != null) {
                    n.left = null;
                } else {
                    n = n.left;
                }
            }
            //3.3让删除节点的左子树成为最小节点minNode的左子树,让删除节点的右子树成为最小节点minNode的右子树
            minNode.left = x.left;
            minNode.right = x.right;
            //3.4让删除节点的父节点指向最小节点minNode
            x = minNode;
            N--;
        }
        return x;

    }
    
 1.前序遍历;
 先访问根结点,然后再访问左子树,最后访问右子树
 2.中序遍历;
 先访问左子树,中间访问根节点,最后访问右子树
 3.后序遍历;
 先访问左子树,再访问右子树,最后访问根节点


前序遍历实现步骤:
1.把当前结点的key放入到队列中;
2.找到当前结点的左子树,如果不为空,递归遍历左子树
3.找到当前结点的右子树,如果不为空,递归遍历右子树
    //使用前序遍历,获取整个树中的键
    public Queue<Key> preErgodic() {
        Queue<Key> keys = new Queue<>();
        preErgodic(root, keys);
        return keys;
    }

    private void preErgodic(Node x, Queue<Key> keys) {
        if (x == null) {
            return;
        }
        //1.把当前节点的key放入到队列中
        keys.enqueue(x.key);
        //2.找到当前结点的左子树,如果不为空,递归遍历左子树
        if (x.left != null) {
            preErgodic(x.left, keys);
        }
        // 3.找到当前结点的右子树,如果不为空,递归遍历右子树
        if (x.right != null) {
            preErgodic(x.right, keys);
        }
    }

中序遍历实现步骤:
1.找到当前结点的左子树,如果不为空,递归遍历左子树
2.把当前结点的key放入到队列中;
3.找到当前结点的右子树,如果不为空,递归遍历右子树
    //使用中序遍历,获取整个树中的所有键
    public Queue<Key> midErgodic() {
        Queue<Key> keys = new Queue<>();
        midErgodic(root, keys);
        return keys;
    }

    //使用中序遍历,把指定树x中的所有键放入到keys队列中
    private void midErgodic(Node x, Queue<Key> keys) {
        if (x == null) {
            return;
        }
        //1.找到当前结点的左子树,如果不为空,递归遍历左子树
        if(x.left!=null) {
            midErgodic(x.left, keys);
        }
        //2.把当前结点的key放入到队列中;
        keys.enqueue(x.key);
        //3.找到当前结点的右子树,如果不为空,递归遍历右子树
        if(x.right!=null) {
            midErgodic(x.right, keys);
        }
}

后序遍历实现步骤:
1.找到当前结点的左子树,如果不为空,递归遍历左子树
2.找到当前结点的右子树,如果不为空,递归遍历右子树
3.把当前结点的key放入到队列中;
    //使用后序遍历,获取整个树中的所有键
    public Queue<Key> afterErgodic(){
        Queue<Key> keys = new Queue<>();
        afterErgodic(root,keys);
        return keys;
    }
    //使用后序遍历,把指定树x中的所有键放入到keys队列中
    private void afterErgodic(Node x,Queue<Key> keys){
        if(x==null){
            return;
        }
        //1.找到当前结点的左子树,如果不为空,递归遍历左子树
        if (x.left!=null){
            afterErgodic(x.left,keys);
        }
        //2.找到当前结点的右子树,如果不为空,递归遍历右子树
        if (x.right!=null){
            afterErgodic(x.right,keys);
        }
        //3.把当前结点的key放入到队列中;
        keys.enqueue(x.key);
    }
层序遍历实现步骤:
1.创建队列,存储每一层的结点;
2.使用循环从队列中弹出一个结点:
 2.1获取当前结点的key;
 2.2如果当前结点的左子结点不为空,则把左子结点放入到队列中
 2.3如果当前结点的右子结点不为空,则把右子结点放入到队列中
    //使用层序遍历,获取整个树中的键(获取每个节点的key就是为了遍历使用,所以才创建了其队列)
    public Queue<Key> layerErgodic(){
        //定义两个队列,分别存储树中的键和节点
        Queue<Key> keys=new Queue<>();
        Queue<Node> nodes=new Queue<>();
        //默认往队列中放入根节点
        nodes.enqueue(root);
        while (!nodes.isEmpty()) {
            //从队列中弹出一个节点,把key放入keys中
            Node n = nodes.dequeue();
            keys.enqueue(n.key);
            //判断当前节点还有没有左子节点,如果有,放入到nodes中
            if (n.left != null) {
                nodes.enqueue(n.left);
            }
            if (n.right != null) {
                nodes.enqueue(n.right);
            }
        }
        return keys;
    }
    
求最大深度实现步骤:
1.如果根结点为空,则最大深度为02.计算左子树的最大深度;
3.计算右子树的最大深度;
4.当前树的最大深度=左子树的最大深度和右子树的最大深度中的较大者+1
    //求最大深度
    public int maxDepth(){
        return maxDepth(root);
    }

    private int maxDepth(Node x){
        if (x==null){
            return 0;
        }
        int max=0;
        int maxL=0;
        int maxR=0;
        if (x.left!=null){
            maxL=maxDepth(x.left);
        }
        if (x.right!=null){
            maxR=maxDepth(x.right);
        }
        max=maxL > maxR ? maxL+1:maxR+1;
        return max;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值