zoukankan      html  css  js  c++  java
  • 浅谈二叉树

    为什么要使用二叉树

      因为二叉树结合了有序数组和链表的特点。在二叉树中查找数据的速度和在有序数组中查找一样快,并且插入数据项和删除数据项的速度也和链表一样快

    查找

      从根开始进行查找,当前节点的key值与所查key值相等时返回该节点;当前节点的key值小于所查key值时,则去其左子树进行查找;当前节点的key值大于所查key值时,则去其右子树进行查找。

    public Node find(int key){
    	Node current = root;
    	while(current.iData != key){
    		if(key < current.iData){
    			current = current.leftChild;
    		} else {
    			current = current.rightChild;
    		}
    		
    		if(current == null){
    			return null;
    		}
    	}
    	return current;
    }
    

    插入

      二叉树的插入首先需要确定新节点插入的位置,即先找插入位置,再插入新节点

    public void insert(int id, double dd){
    	Node newNode = new Node();
    	newNode.iData = id;
    	newNode.fData = dd;
    	
    	if(root == null){
    		root = newNode;
    	} else {
    		Node current = root;
    		Node parent;
    		
    		while(true){
    			parent = current;
    			if(id < current.iData){
    				current = current.leftChild;
    				if(current == null){
    					parent.leftChild = newNode;
    					return;
    				}
    			} else {
    				current = current.rightChild;
    				if(current == null){
    					parent.rightChild = newNode;
    					return;
    				}
    			}
    		}
    	}
    }
    

    遍历

      遍历树的意思是根据一种特定的顺序访问树的每一个节点,树的遍历分为以下三种形式:

    • 前序遍历
      1.访问节点
      2.调用自身遍历该节点的左子树
      3.调用自身遍历该节点的右子树

        public void preOrder(Node localRoot){
        	if(localRoot != null){
                System.out.print(localRoot.iData + " ");
        		inOrder(localRoot.leftChild);
            	inOrder(localRoot.rightChild);
            }
        }
      
    • 中序遍历
      1.调用自身遍历该节点的左子树
      2.访问节点
      3.调用自身遍历该节点的右子树

        public void inOrder(Node localRoot){
            if(localRoot != null){
        	    inOrder(localRoot.leftChild);
        	    System.out.print(localRoot.iData + " ");
        	    inOrder(localRoot.rightChild);
            }
        }
      
    • 后序遍历
      1.调用自身遍历该节点的左子树
      2.调用自身遍历该节点的右子树
      3.访问节点

        public void preOrder(Node localRoot){
            if(localRoot != null){
        	    inOrder(localRoot.leftChild);
        	    inOrder(localRoot.rightChild);
                System.out.print(localRoot.iData + " ");
            }
        }
      

    查找最大值和最小值

      查找最小值时,先走到根的左子节点处,然后接着走到那个节点的左子节点,如此类推,知道找到一个没有左子节点的节点,这个节点即为最小值所在的节点。

        public Node mininum(){
    	    Node current = root;
    	    Node last = null;
    	    while(current != null){
    		    last = current;
    		    current = current.leftChild;
    	    }
    	    return last;
        }
    

      查找最大值时,先走到根的右子节点处,然后接着走到那个节点的右子节点,如此类推,知道找到一个没有右子节点的节点,这个节点即为最大值所在的节点。

        public Node maxnum(){
    	    Node current = root;
    	    Node last = null;
    	    while(current != null){
    		    last = current;
    		    current = current.righttChild;
    	    }
    	    return last;
        }
    

    删除节点

      删除节点是二叉树中最复杂的常用操作,删除节点的方法为先查找到所要删除的节点,找到该节点后有以下三种情况需要考虑:

    • 该节点是叶节点(没有子节点)
        要删除叶节点,只需要改变该节点的父节点的对应字段的值,由该节点改为null就可以了。要删除的节点仍然存在,但是它已经不是树的一部分了。

        if(current.leftChild == null && current.rightChild == null){
        		    if(current == root){
        			    root = null;
        		    }else if(isLeftChild){
        			    parent.leftChild = null;
        		    }else{
        			    parent.rightChild = null;
        		    }
        	    }
      
    • 该节点有一个子节点
        该节点只有两个连接:连向父节点和连向它唯一的子节点。删除这个节点的时候,需要从这个序列中“剪断”这个节点,把它的子节点直接连到它的父节点上。这个过程要求改变父节点适当的引用(左子节点还是右子节点),指向要删除节点的子节点。

        if(current.rightChild == null){
        		    if(current == root){
        			    root = current.leftChild;
        		    }else if(isLeftChild){
        			    parent.leftChild = current.leftChild;
        		    }else{
        			    parent.rightChild = current.leftChild;
        		    }
        	    }else if(current.leftChild == null){
        		    if(current == root){
        			    root =current.rightChild;
        		    }else if(isLeftChild){
        			    parent.leftChild = current.rightChild;
        		    }else{
        			    parent.rightChild = current.rightChild;
        		    }
        	    }
      
    • 该节点有两个子节点
        如果要删除的节点有两个子节点,就不能只是用它的一个子节点代替它。删除有两个子节点的节点,用它的中序后继来代替该节点。删除的过程分为以下三个步骤:
      1. 找后继节点
        首先,找到初始节点的右子节点,它的关键字值一定比初始节点大。然后转到初始节点的右子节点的左子节点那里(如果有的话),然后到这个左子节点的左子节点,以此类推,顺着左子节点的路径一直向下找。这个路径上的最后一个左子节点就是初始节点的后继。

        private Node getSuccessor(Node delNode) {
            Node successorParent = delNode;
            Node successor = delNode;
            Node current = delNode.rightChild;
            while(current != null){
        	    successorParent = successor;
        	    successor = current;
        	    current = current.leftChild;
            }    
            return successor;
        }
      

    2. 后继节点是删除节点的右子节点
      如果后继节点是current节点的右子节点,只需要把以后继为根的子树移到删除的节点的位置。这个操作需要两个步骤:把current从它父节点的rightChild字段删掉(也有可能是leftChild),把这个 字段指向后继;把current的左子节点移出来,把它插到后继的leftChild字段。

        Node successor = getSuccessor(current);
        if(current == root){
    	    root = successor;
        }else if(isLeftChild){
    	    parent.leftChild = successor;
        }else {
    	    parent.rightChild = successor;
        }
        successor.leftChild = current.leftChild;
    

    **3. 后继节点是删除节点的左子节点 **
      如果后继节点是要删除节点右子节点的左后代,执行删除操作需要以下四个步骤:把后继父节点的leftChild字段置为后继的右节点;把后继的rightChild字段置为要删除节点的右子节点;把current从它父节点的rightChild字段移除,把这个字段置为后继;把current的左子节点从current移除,把后继的leftChild字段置为current的左子节点。

        if(successor != delNode.rightChild){
    		    successorParent.leftChild = successor.rightChild;
    		    successor.rightChild = delNode.rightChild;
    	    }
        if(current == root){
    	    root = successor;
    	    }else if(isLeftChild){
    		    parent.leftChild = successor;
    	    }else {
    		    parent.rightChild = successor;
    	    }
    	    successor.leftChild = current.leftChild;
    

    删除是必要的么

      二叉树的删除是一个比较复杂的操作,实际上因为它非常复杂,一些程序言都尝试着躲开它。他们会在Node类中加一个Boolean字段,名称如isDeleted。要删除一个节点时,就把此节点的这个字段置为true。其他操作,如find(),在查找之前先判断这个节点是不是标志为已经删除了。这样删除的节点不会改变树的结构。当然,这样做存储中还保留着这种“已经删除”的节点。

  • 相关阅读:
    关于面试总结8-http协议相关面试题
    关于面试总结7-linux篇
    关于面试总结6-SQL经典面试题
    关于面试总结5-python笔试题(递归)
    关于面试总结4-python笔试题
    关于面试总结3-SQL查询
    关于面试总结2-SQL学生表
    关于面试总结1-SQL学生表
    浅谈多变量线性回归中的数据规范化
    浅谈KL散度
  • 原文地址:https://www.cnblogs.com/siguoyi/p/5406911.html
Copyright © 2011-2022 走看看