数据结构之二叉树

来源:互联网 发布:adc0804连单片机 编辑:程序博客网 时间:2024/06/01 10:07

1、基本概念

  二叉树,顾名思义,就是子节点最多只有两个的树。由于最多只有两个,可以区分左右,我们称子节点为左子女和右子女,次序不可颠倒。根据二叉树的结构,我们可以得到以下特性:
1. 第i层的节点个数最多为2^(i-1)(i>=1)个;
2. 深度为h的二叉树最多有2^h -1(h>=1)个节点,最少为h个;
3. 若叶节点的个数为n1,度数为2的节点个数为n2,则n1=n2+1;

2、特殊结构

  当二叉树满足一些特殊的条件时,会形成特殊的结构,可以很好的作为其他数据结构的底层结构,目前有如下几种特殊情况:
1. 完全二叉树。若二叉树的高度为h,则除了第h层的其他层都达到该层节点的最大个数,同时第h层的节点是从左至右连续排列的。由于节点顺序是紧密排列的,可以使用序列容器进行存储,主要应用于二叉堆。以下是完全二叉树的特性,设定根节点的索引位置为1:
* 对于索引位置为i的节点,其到根节点的节点总数也为i;
* 对于索引位置为i的节点,其左子女的索引位置为2*i,右子女的索引位置为2*i+1;
* 对于索引位置为i(i>1)的节点,其父节点的索引位置为int(i/2);
* 对于索引位置为i的节点,若节点总数为n且i>int(n/2),则该节点为叶子节点;
* 对于索引位置为i的节点,若节点总数为n且int((n-1)/2)>i,则该节点必有两个子节点;
* 对于任意节点,若其右子树的深度为d,则左子树的深度为d或d+1;
* 若子节点总数为n,则叶子节点个数为l= math.ceil(n/2)。
2. 满二叉树。二叉树的每一层的节点个数都达到最大个数,因而满二叉树一定是完全二叉树,但完全二叉树不一定是满二叉树。
3. 平衡二叉树。对于树中的任意节点,其左右子树的高度相差不超过1。平衡二叉树要保证高度平衡,避免出现链表式的树。

3、查找节点

  在树中查找节点,就是采用递归方法,从根节点开始,一次判读根节点、左子树、右子树,从而找的对应的节点,一下是实现代码:

bool BTree::contains(int key, BTreeNode* &node){    if (node == NULL)    {        return false;    }    if (key == node->data)    {        return true;    }    return contains(key,node->leftChild) || contains(key,node->rightChild);}

4、插入节点

  二叉树在插入时是没有固定规则,可自行定义,下面我使用构造满二叉树的方式进行插入,这里通过queue找到节点的插入位置,代码如下:

void BTree::insert(int key, BTreeNode* &node){    if (node == NULL)    {        node = new BTreeNode(key);    }    else    {        //按照插入顺序放置        queue<BTreeNode*> q;        q.push(node);        while(!q.empty())        {            BTreeNode* temp = q.front();            q.pop();            if (temp->leftChild == NULL)            {                temp->leftChild = new BTreeNode(key);                return;            }            if (temp->rightChild == NULL)            {                temp->rightChild = new BTreeNode(key);                return;            }            q.push(temp->leftChild);            q.push(temp->rightChild);        }    }}

5、删除节点

  二叉树删除节点也是没有规则的,但不管使用什么规则,都要考虑下面三种情况:
1. 删除节点没有子节点。这种情况最简单,将该节点的父节点指向该节点的指针置为空,然后删除该节点即可;
2. 删除节点有一个子节点。此时将该节点的父节点指向该节点的指针置为指向改节点的子节点,然后删除该节点;
3. 删除节点有两个子节点。这里采用的策略是先查找到该节点的右子树中的最后一个节点,然后将查找到的节点的键值与该节点的键值进行交换,最后使用递归方法删除查找到的节点。
  下面是二叉树删除的实现代码:

void BTree::remove(int key, BTreeNode* &node){    if (node == NULL)    {        return;    }    if (key == node->data)    {        if (node->leftChild != NULL && node->rightChild != NULL)        {            //查找子树的最后一个节点            queue<BTreeNode*> q;            q.push(node->leftChild);            q.push(node->rightChild);            while(true)            {                BTreeNode* curNode = q.front();                q.pop();                if (curNode->leftChild != NULL)                {                    q.push(curNode->leftChild);                }                if (curNode->rightChild != NULL)                {                    q.push(curNode->rightChild);                }                if (q.empty())                {                    node->data = curNode->data;                    curNode->data = key;                    remove(key,node);                    return;                }            }        }         else        {            BTreeNode* temp = node;            node = (node->leftChild != NULL)?node->leftChild:node->rightChild;            delete temp;            return;        }    }    remove(key,node->leftChild);    remove(key,node->rightChild);}

6、清理操作

  若要清理整棵树的节点,可以使用递归的方法清理节点的左右子树,然后释放节点自己,下面是实现代码:

void BTree::clear(BTreeNode* &node){    if (node == NULL)    {        return;    }    clear(node->leftChild);    clear(node->rightChild);    delete node;    node = NULL;}

7、综合结果

  下面,我们对算法进行测试,结果如下:

BTree* tree = new BTree();for (int i = 0; i < 10; ++i){    tree->insert(i);}cout<<"contains 5:"<<tree->contains(5)<<endl; //contains 5:1cout<<"contains 10:"<<tree->contains(10)<<endl; //contains 10:0BTreeNode* minNode = tree->findMin();if (minNode != NULL){    cout<<"findMin:"<<minNode->data<<endl; //findMin:0}BTreeNode* maxNode = tree->findMax();if (minNode != NULL){    cout<<"findMax:"<<maxNode->data<<endl; //findMax:9}tree->breadthFirstTravel();  //bft:0 1 2 3 4 5 6 7 8 9tree->depthFirstTravel_DLR(); //depthFirstTravel_DLR:0 1 3 7 8 4 9 2 5 6tree->depthFirstTravel_LDR(); //depthFirstTravel_LDR:7 3 8 1 9 4 0 5 2 6tree->depthFirstTravel_LRD(); //depthFirstTravel_LRD:7 8 3 9 4 1 5 6 2 0tree->remove(1);tree->breadthFirstTravel();  //bft:0 9 2 3 4 5 6 7 8tree->depthFirstTravel_DLR(); //depthFirstTravel_DLR:0 9 3 7 8 4 2 5 6tree->depthFirstTravel_LDR(); //depthFirstTravel_LDR:7 3 8 9 4 0 5 2 6tree->depthFirstTravel_LRD(); //depthFirstTravel_LRD:7 8 3 4 9 5 6 2 0tree->clear();tree->breadthFirstTravel();  //bft:

8、效率分析

  二叉树的查找操作需要遍历整棵树,其算法复杂度为O(n);插入操作根据插入策略的不同而不同的,平均查找到插入位置的算法复杂度为O(logn);删除操作由于要先查找到节点,其算法复杂度以查找操作一致。

0 0
原创粉丝点击