二叉树(一)

一、数据结构定义

定义二叉树数据结构:

/* 二叉樹的二叉鏈表存儲表示 */
 typedef struct BiTNode {
   TElemType data;
   struct BiTNode *lchild,*rchild; /* 左右孩子指針 */
 }BiTNode,*BiTree;

二叉树的三叉链表数据结构:

基于三叉链表的二叉树的逻辑结构

/* 二叉樹的三叉鏈表存儲表示 */
 typedef struct BiTPNode {
   TElemType data;
   struct BiTPNode *parent,*lchild,*rchild; /* 父、左右孩子指針 */
 }BiTPNode,*BiPTree;



二、二叉树遍历

1.中序遍历

void visit(BiTPNode *node){
    if node.left  != null then visit(node.left)
    print node.value
    if node.right != null then visit(node.right)
}

   中序遍历比较常用,能用递增的顺序来遍历所有值

2.前序遍历

void visit(BiTPNode *node){
    print node.value
    if node.left  != null then visit(node.left)
    if node.right != null then visit(node.right)
}

3.后序遍历

void visit(BiTPNode *node){
    if node.left  != null then visit(node.left)
    if node.right != null then visit(node.right)
    print node.value
}

Bitree.JPG

用二叉树表示下述表达式:a+b*(c-d)-e/f

  • 前序遍历的串行是:-+a*b-cd/ef

  • 中序遍历的串行是:a+b*c-d-e/f

  • 后序遍历的串行是:abcd-*+ef/-


4.深度优先遍历

     希望从根结点访问最远的结点

     前序,中序和后序遍历都是深度优先遍历的特例。

5.广度优先遍历

     有限访问离根结点最近的结点


三、多叉树转二叉树

思想:

(1)加线:在各兄弟结点之间加一虚线;

(2)抹线:对于每一个结点,除其最左的一个孩子结点之外,抹掉该结点原来与其余孩子的连线;

(3)旋转:将图形上原有的实线均向左斜,加上的虚线均向右斜,调整成二叉树形结构.

算法实现:

 #include <stdio.h>
 #include <sys/time.h>
 #include <limits.h>
 #include <stdlib.h>
 
 typedef struct   TreeNode{   
     int   child_count;   
     int   value;   
     struct   TreeNode*   child[0];
 } TreeNode_t;
 
 typedef struct   BinaryTreeNode{   
     struct   BinaryTreeNode*   leftChild;   
     struct   BinaryTreeNode*   rightChild;   
     int   value;   
 } BinaryTreeNode_t; 
 
 BinaryTreeNode_t* ToBinaryTree(TreeNode_t* root){   
     if(root == NULL) {     
         return   NULL;   
     }
 
     BinaryTreeNode_t* binaryRoot =(BinaryTreeNode_t*)malloc(sizeof(BinaryTreeNode_t));   
     binaryRoot->value = root->value;   
     binaryRoot->leftChild = ToBinaryTree(root->child[0]);   
 
     BinaryTreeNode_t* brother = binaryRoot->leftChild; 
     int i;  
     for(i = 1; i < root->child_count;i++){   
          brother->rightChild = ToBinaryTree(root->child[i]);   
          brother = brother->rightChild;   
     }   
     return binaryRoot;   
 }
    
 TreeNode_t* Tn;/*多叉树的根*/
 BinaryTreeNode_t* Bn;/*二叉树的根*/
 
 int main (int argc, char *argv[])
 {
     /*构造一棵简单的多叉树,用于测试*/
     Tn = (TreeNode_t*)malloc(40);
     TreeNode_t* t[5];
     memset(t,0,sizeof(t));
 
     TreeNode_t* tmp;
     TreeNode_t* tmp1;
     TreeNode_t* tmp2;
     TreeNode_t* tmp3;
     TreeNode_t* tmp4;
     TreeNode_t* tmp5;
 
     tmp = (TreeNode_t*)malloc(sizeof(TreeNode_t) + sizeof(TreeNode_t*) * 5 );
     tmp->value = 'b';
     tmp->child_count = 3;
     tmp1 = (TreeNode_t*)malloc(40);
     tmp1->value = 'c';
     tmp2 = (TreeNode_t*)malloc(40);
     tmp2->value = 'd';
 
     tmp3 = (TreeNode_t*)malloc(40);
     tmp3->value = 'e';
     tmp4 = (TreeNode_t*)malloc(40);
     tmp4->value = 'f';
     tmp5 = (TreeNode_t*)malloc(40);
     tmp5->value = 'g';
 
     tmp->child[0] = tmp3;
     tmp->child[1] = tmp4;
     tmp->child[2] = tmp5;
     for(i = 0;i<tmp->child_count;i++) {
         printf("%c/r/n",tmp->child[i]->value);
     }
 
     Tn->value = 'a';
     Tn->child_count = 3;
     memset(t,0,sizeof(t));
     t[0] = tmp;
     t[1] = tmp1;
     t[2] = tmp2;
     memcpy(Tn->child,t,sizeof(t));
     
     /*转化为二叉树*/
     Bn = ToBinaryTree(Tn);
 
     return(0);
 }


四、二叉查找树

  1. 添加

      二叉树(一) 

 public BinaryNode<K, V> Add(K key, V value, BinaryNode<K, V> tree)
   {             
     if (tree == null)                 
     tree = new BinaryNode<K, V>(key, value, null, null); //新建一个新结点
                 
     //左子树            
     if (key.CompareTo(tree.key) < 0)                 
     tree.left = Add(key, value, tree.left);  
               
     //右子树
     if (key.CompareTo(tree.key) > 0)           
     tree.right = Add(key, value, tree.right); 
                
     //将value追加到附加值中(也可对应重复元素)             
     if (key.CompareTo(tree.key) == 0)                
     tree.attach.Add(value);            
     
     return tree;
   }


2.范围查找(使用二叉树的最终目的)


第一步:我们要在树中找到min元素,当然min元素可能不存在,但是我们可以找到min的上界,耗费时间为O(logn)。

第二步:从min开始我们中序遍历寻找max的下界。耗费时间为m。m也就是匹配到的个数。

最后时间复杂度为M+logN

代码实现如下:

 public HashSet<V> SearchRange(K min, K max, HashSet<V> hashSet, BinaryNode<K, V> tree)
        {
            if (tree == null)
                return hashSet;
            //a.compareTo(b)实际上就是a-b
            //遍历左子树(寻找下界)即min小于tree.key
            if (min.CompareTo(tree.key) < 0)
                SearchRange(min, max, hashSet, tree.left);

            //当前节点是否在选定范围内,是就收了
            if (min.CompareTo(tree.key) <= 0 && max.CompareTo(tree.key) >= 0)
            {
                //等于这种情况
                foreach (var item in tree.attach)
                hashSet.Add(item);
            }

            //遍历右子树(当当前key小于min,继续往右子树搜索min下界直到找到下界)
            if (min.CompareTo(tree.key) > 0 || max.CompareTo(tree.key) > 0)
                SearchRange(min, max, hashSet, tree.right);

            return hashSet;
        }


3.删除

(1)单孩子情况(有左孩子就顶上左孩子,有右孩子就顶上右孩子)

二叉树(一)

(2)双孩子情况

可以仿造数组的方式来删除元素


只不过二叉树是按照“中序遍历”来排序的,并且删除元素后,也是按照这样的顺序往前顶上

二叉查找树“中序遍历”正好是升序的序列

二叉树(一)

这里attach是针对重复元素的“附加域”,在删除的时候就可以判断,是否大于1

 public void Remove(K key, V value){
           node = Remove(key, value, node);     
  }
 
 
 
  public BinaryNode<K, V> Remove(K key, V value, BinaryNode<K, V> tree)
        {
            if (tree == null)
                return null;

            //左子树
            if (key.CompareTo(tree.key) < 0)
                tree.left = Remove(key, value, tree.left);

            //右子树
            if (key.CompareTo(tree.key) > 0)
                tree.right = Remove(key, value, tree.right);

            /*相等的情况*/
            if (key.CompareTo(tree.key) == 0)
            {
                //判断里面的HashSet是否有多值
                if (tree.attach.Count > 1)
                {
                    //实现惰性删除(惰性删除另说)
                    tree.attach.Remove(value);
                }
                else
                {
                    //有两个孩子的情况
                    if (tree.left != null && tree.right != null)
                    {
                        //根据二叉树的中顺遍历,需要找到”有子树“的最小节点
                        tree.key = FindMin(tree.right).key;

                        //删除右子树的指定元素
                        tree.right = Remove(key, value, tree.right);
                    }
                    else
                    {
                        //单个孩子的情况
                        tree = tree.left == null ? tree.right : tree.left;
                    }
                }
            }



4.测试

假如现在我们有一张User表,我要查询"2012/7/30 4:30:00"到"2012/7/30 4:40:00"这个时间段登陆的用户,我在txt中生成一个

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
using System.Diagnostics;

namespace DataStruct
{
    class Program
    {
        static void Main(string[] args)
        {
            List<long> list = new List<long>();

            Dictionary<DateTime, int> dic = new Dictionary<DateTime, int>();

            BinaryTree<DateTime, int> tree = new BinaryTree<DateTime, int>();

            using (StreamReader sr = new StreamReader(Environment.CurrentDirectory + "//1.txt"))
            {
                var line = string.Empty;

                while (!string.IsNullOrEmpty(line = sr.ReadLine()))
                {
                    var userid = Convert.ToInt32(line.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)[0]);

                    var time = Convert.ToDateTime(line.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)[1]);

                    //防止dic出错,为了进行去重处理
                    if (!dic.ContainsKey(time))
                    {
                        dic.Add(time, userid);

                        tree.Add(time, userid);
                    }
                }
            }

            var min = Convert.ToDateTime("2012/7/30 4:30:00");

            var max = Convert.ToDateTime("2012/7/30 4:40:00");

            var watch = Stopwatch.StartNew();

            var result1 = dic.Keys.Where(i => i >= min && i <= max).Select(i => dic[i]).ToList();

            watch.Stop();

            Console.WriteLine("字典查找耗费时间:{0}ms,获取总数:{1}", watch.ElapsedMilliseconds, result1.Count);

            watch = Stopwatch.StartNew();

            var result2 = tree.SearchRange(min, max);

            watch.Stop();

            Console.WriteLine("二叉树耗费时间:{0}ms,获取总数:{1}", watch.ElapsedMilliseconds, result2.Count);
        }
    }

    #region 二叉树节点
    /// <summary>
    /// 二叉树节点
    /// </summary>
    /// <typeparam name="K"></typeparam>
    /// <typeparam name="V"></typeparam>
    public class BinaryNode<K, V>
    {
        /// <summary>
        /// 节点元素
        /// </summary>
        public K key;

        /// <summary>
        /// 节点中的附加值
        /// </summary>
        public HashSet<V> attach = new HashSet<V>();

        /// <summary>
        /// 左节点
        /// </summary>
        public BinaryNode<K, V> left;

        /// <summary>
        /// 右节点
        /// </summary>
        public BinaryNode<K, V> right;

        public BinaryNode() { }

        public BinaryNode(K key, V value, BinaryNode<K, V> left, BinaryNode<K, V> right)
        {
            //KV键值对
            this.key = key;
            this.attach.Add(value);

            this.left = left;
            this.right = right;
        }
    }
    #endregion

    public class BinaryTree<K, V> where K : IComparable
    {
        public BinaryNode<K, V> node = null;

        #region 添加操作
        /// <summary>
        /// 添加操作
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void Add(K key, V value)
        {
            node = Add(key, value, node);
        }
        #endregion

        #region 添加操作
        /// <summary>
        /// 添加操作
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="tree"></param>
        /// <returns></returns>
        public BinaryNode<K, V> Add(K key, V value, BinaryNode<K, V> tree)
        {
            if (tree == null)
                tree = new BinaryNode<K, V>(key, value, null, null);

            //左子树
            if (key.CompareTo(tree.key) < 0)
                tree.left = Add(key, value, tree.left);

            //右子树
            if (key.CompareTo(tree.key) > 0)
                tree.right = Add(key, value, tree.right);

            //将value追加到附加值中(也可对应重复元素)
            if (key.CompareTo(tree.key) == 0)
                tree.attach.Add(value);

            return tree;
        }
        #endregion

        #region 是否包含指定元素
        /// <summary>
        /// 是否包含指定元素
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool Contain(K key)
        {
            return Contain(key, node);
        }
        #endregion

        #region 是否包含指定元素
        /// <summary>
        /// 是否包含指定元素
        /// </summary>
        /// <param name="key"></param>
        /// <param name="tree"></param>
        /// <returns></returns>
        public bool Contain(K key, BinaryNode<K, V> tree)
        {
            if (tree == null)
                return false;
            //左子树
            if (key.CompareTo(tree.key) < 0)
                return Contain(key, tree.left);

            //右子树
            if (key.CompareTo(tree.key) > 0)
                return Contain(key, tree.right);

            return true;
        }
        #endregion

        #region 树的指定范围查找
        /// <summary>
        /// 树的指定范围查找
        /// </summary>
        /// <param name="min"></param>
        /// <param name="max"></param>
        /// <returns></returns>
        public HashSet<V> SearchRange(K min, K max)
        {
            HashSet<V> hashSet = new HashSet<V>();

            hashSet = SearchRange(min, max, hashSet, node);

            return hashSet;
        }
        #endregion

        #region 树的指定范围查找
        /// <summary>
        /// 树的指定范围查找
        /// </summary>
        /// <param name="range1"></param>
        /// <param name="range2"></param>
        /// <param name="tree"></param>
        /// <returns></returns>
        public HashSet<V> SearchRange(K min, K max, HashSet<V> hashSet, BinaryNode<K, V> tree)
        {
            if (tree == null)
                return hashSet;

            //遍历左子树(寻找下界)
            if (min.CompareTo(tree.key) < 0)
                SearchRange(min, max, hashSet, tree.left);

            //当前节点是否在选定范围内
            if (min.CompareTo(tree.key) <= 0 && max.CompareTo(tree.key) >= 0)
            {
                //等于这种情况
                foreach (var item in tree.attach)
                    hashSet.Add(item);
            }

            //遍历右子树(两种情况:①:找min的下限 ②:必须在Max范围之内)
            if (min.CompareTo(tree.key) > 0 || max.CompareTo(tree.key) > 0)
                SearchRange(min, max, hashSet, tree.right);

            return hashSet;
        }
        #endregion

        #region 找到当前树的最小节点
        /// <summary>
        /// 找到当前树的最小节点
        /// </summary>
        /// <returns></returns>
        public BinaryNode<K, V> FindMin()
        {
            return FindMin(node);
        }
        #endregion

        #region 找到当前树的最小节点
        /// <summary>
        /// 找到当前树的最小节点
        /// </summary>
        /// <param name="tree"></param>
        /// <returns></returns>
        public BinaryNode<K, V> FindMin(BinaryNode<K, V> tree)
        {
            if (tree == null)
                return null;

            if (tree.left == null)
                return tree;

            return FindMin(tree.left);
        }
        #endregion

        #region 找到当前树的最大节点
        /// <summary>
        /// 找到当前树的最大节点
        /// </summary>
        /// <returns></returns>
        public BinaryNode<K, V> FindMax()
        {
            return FindMin(node);
        }
        #endregion

        #region 找到当前树的最大节点
        /// <summary>
        /// 找到当前树的最大节点
        /// </summary>
        /// <param name="tree"></param>
        /// <returns></returns>
        public BinaryNode<K, V> FindMax(BinaryNode<K, V> tree)
        {
            if (tree == null)
                return null;

            if (tree.right == null)
                return tree;

            return FindMax(tree.right);
        }
        #endregion

        #region 删除当前树中的节点
        /// <summary>
        /// 删除当前树中的节点
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public void Remove(K key, V value)
        {
            node = Remove(key, value, node);
        }
        #endregion

        #region 删除当前树中的节点
        /// <summary>
        /// 删除当前树中的节点
        /// </summary>
        /// <param name="key"></param>
        /// <param name="tree"></param>
        /// <returns></returns>
        public BinaryNode<K, V> Remove(K key, V value, BinaryNode<K, V> tree)
        {
            if (tree == null)
                return null;

            //左子树
            if (key.CompareTo(tree.key) < 0)
                tree.left = Remove(key, value, tree.left);

            //右子树
            if (key.CompareTo(tree.key) > 0)
                tree.right = Remove(key, value, tree.right);

            /*相等的情况*/
            if (key.CompareTo(tree.key) == 0)
            {
                //判断里面的HashSet是否有多值
                if (tree.attach.Count > 1)
                {
                    //实现惰性删除
                    tree.attach.Remove(value);
                }
                else
                {
                    //有两个孩子的情况
                    if (tree.left != null && tree.right != null)
                    {
                        //根据二叉树的中序遍历,需要找到”右子树“的最小节点
                        tree.key = FindMin(tree.right).key;

                        //删除右子树的指定元素
                        tree.right = Remove(tree.key, value, tree.right);
                    }
                    else
                    {
                        //单个孩子的情况
                        tree = tree.left == null ? tree.right : tree.left;
                    }
                }
            }

            return tree;
        }
        #endregion
    }
}


这里注意一点,普通查找树最坏情况为链表,复杂度由O(logn)退化到O(n)

你可能感兴趣的:(数据结构,算法,二叉树)