二叉树基础练习

binarytree.h

#include<stdio.h>
#include<windows.h>
#include<assert.h>

typedef int BTDataType;
typedef struct BinaryTreeNode
{
    struct BinaryTreeNode* _left;   //左孩子
    struct BinaryTreeNode* _right;  //右孩子
    BTDataType _data;               //数据域
}BTNode;//树的每个节点
#include"stack.h"
#include"queue.h"



BTNode *BuyNewNode(BTDataType x)//创建新节点
{
    BTNode *newnode = (BTNode *)malloc(sizeof(BTNode));
    assert(newnode);
    newnode->_data = x;
    newnode->_left = NULL;
    newnode->_right = NULL;
    return newnode;
}
BTNode *BinaryTreeCreateByPrev(int *a, int *index, int invalid)//先序创建二叉树
{
    //思路:
    //从数组头开始读取数据,遇到合法值,创建新节点,紧接着递归的创建左孩子和右孩子,V L R
    //遇到非法值时直接return NULL,创建完左右孩子return 当前节点的地址
    assert((*index) >= 0);
    if (a[*index] == invalid)
    {
        return NULL;
    }

    //到这一步时,可以判断数组的当前位置值为合法的,开始创建新节点
    BTNode *root = BuyNewNode(a[*index]);
    ++(*index);//数组下标后移
    root->_left = BinaryTreeCreateByPrev(a, index, invalid);//递归:创建左孩子
    ++(*index);//数组下标后移
    root->_right = BinaryTreeCreateByPrev(a, index, invalid);//递归:创建右孩子

                                                             //左右孩子都创建好,或者因非法return  NULL
                                                             //返回上层递归
    return root;

}
void BTreePrevOrder(BTNode *root)//先序,递归遍历二叉树   V L R
{
    //子问题:每个节点的左右孩子都要保证以R L T方式遍历
    //递归出口:抵达叶子节点,在向下遍历为空时

    if (root == NULL)
    {
        return;
    }
    //到这里说明还该处有节点,依照 V L R依次遍历,每个节点都是如此
    printf("%2d", root->_data);
    BTreePrevOrder(root->_left);
    BTreePrevOrder(root->_right);
}


void BTreeInOrder(BTNode* root)//中序遍历二叉树
{
    //子问题:每个节点的左右孩子必须按照L V R顺序遍历
    //递归出口:抵达叶子节点,在向下遍历为空时

    if (root == NULL)
    {
        return;
    }
    BTreeInOrder(root->_left);
    printf("%2d", root->_data);
    BTreeInOrder(root->_right);
}

void BTreePostOrder(BTNode* root)//后序遍历二叉树
{
    //子问题:每个节点的左右孩子都必须按照 L R V顺序遍历
    //递归出口:抵达叶子节点,在向下遍历为空时
    if (root == NULL)
    {
        return;
    }
    BTreePostOrder(root->_left);
    BTreePostOrder(root->_right);
    printf("%2d", root->_data);

}
size_t BTreeSize(BTNode* root)//二叉树的总结点数
{
    //子问题:一个树的节点个数都可以分解成 1 + 其左子树的节点个数 + 其右节点的节点个数
    if (root == NULL)
    {
        return 0;
    }

    return (1 + BTreeSize(root->_left) + BTreeSize(root->_right));
}
size_t BTreeLeafSize(BTNode* root)//二叉树的叶子节点数
{

    //如果树为空,返回0;
    //如果该节点为叶子节点,返回1
    //子问题:如果该节点不是叶子节点(有左右子树),返回其左子树的叶子节点个数 + 右子树节点个数
    if (root == NULL)
    {
        return 0;
    }
    if (root->_left == NULL&&root->_right == NULL)
    {
        return 1;
    }
    return BTreeLeafSize(root->_left) + BTreeLeafSize(root->_right);
}
size_t BTreeKLevelSize(BTNode* root, size_t k)//二叉树第k层节点数
{
    //子问题:如果当前层不是我们要查的,则k--,
    //且改为查其左子树的第k层的节点数+其右节点的第k层的节点数
    //递归出口:k==1,此时一定是我们要查的层数,返回当前节点数
    assert(k);//k至少为1
    if (root == NULL)
    {
        return 0;
    }
    if (k == 1)
    {
        return 1;
    }
    k--;
    return BTreeKLevelSize(root->_left, k) + BTreeKLevelSize(root->_right, k);
}
size_t BTreeDepth(BTNode* root)//二叉树的深度
{
    //子问题:从根结点开始,如果根节点有左右子树,那么他的深度为1+(左子树深度||右子树深度(取大值))
    //递归出口:如果当前节点已经是最深的节点,则返回0;
    if (root == NULL)
    {
        return 0;
    }
    size_t left = BTreeDepth(root->_left);
    size_t right = BTreeDepth(root->_right);
    return 1 + (left > right ? left : right);
}

void BTreeLevelOrder(BTNode* root)//广度遍历二叉树
{
    //思路:层序遍历需要借助一个队列完成,当根节点不为空时,将根节点指针入队,为空直接返回。
    //接着进入循环:如果队头节点有左节点或者右节点,则将他们的指针入队,然后对队头元素指针出队并打印,
    //直至队列为空
    Queue q;
    QueueInit(&q);
    if (root == NULL)
    {
        return;
    }
    QueuePush(&q, root);
    while (QueueEmpty(&q))
    {
        BTNode *front = QueueFront(&q);
        if (front->_left != NULL)
        {
            QueuePush(&q, front->_left);
        }
        if (front->_right != NULL)
        {
            QueuePush(&q, front->_right);
        }
        printf("%2d", front->_data);
        QueuePop(&q);
        free(front);
    }


}
BTNode* BTreeFind(BTNode* root, BTDataType x)//二叉树的查找
{
    //思路:若树为空树,则return NULL
    //      如果找到,return x的指针
    //      递归遍历左子树和右子树继续找
    if (root == NULL)
    {
        return NULL;
    }
    if (root->_data == x)
    {
        return root;
    }
    BTNode *ret = NULL;
    if (root->_left)
    {
        ret = BTreeFind(root->_left, x);
        if (ret)
        {
            return ret;
        }
    }

    if (root->_right)
    {
        ret = BTreeFind(root->_right, x);
        if (ret)
            return ret;
    }
    return NULL;
}


int IsCompleteBTree(BTNode* root)//判断一棵二叉树是否是完全二叉树
{
    //思路:完全二叉树的特性是前n-1层是满的,最后第n层的前k个是连续不间断的,后面不足无所谓。
    //那么就可以借助层序遍历,有左右子树就入队,没有对应的子树也入一个NULL进去,当第一次
    //遍历至NULL时,后面队列里的所有成员必须全是NULL,否则就不是完全二叉树(不满足连续)
    Queue q;
    QueueInit(&q);
    if (root == NULL)
    {
        return 0;
    }
    QueuePush(&q, root);
    while (QueueFront(&q) != NULL)
    {
        BTNode *front = QueueFront(&q);
        QueuePush(&q, front->_left);
        QueuePush(&q, front->_right);
        QueuePop(&q);
        free(front);
    }
    if (QueueEmpty(&q) == 0)
    {
        return 1;
    }
    else
    {
        while (QueueEmpty(&q))
        {
            if (QueueFront(&q) != NULL)
            {
                return 0;
            }
            QueuePop(&q);
        }
        return 1;
    }

}

void BTreePrevOrderNonR(BTNode* root)//前序非递归遍历二叉树
{
    //前序非递归遍历二叉树
    //思路:不用递归,可以用运营一个栈来模拟递归
    if (root == NULL)
    {
        return;
    }
    Stack s;
    StackInit(&s);
    BTNode *cur = root;
    while (cur || StackEmpty(&s))//cur不为空说明当前节点还可以遍历
    {                            //stack不为空说明栈内元素可能还有孩子没有被遍历
        while (cur)
        {
            printf("%d ", cur->_data);
            StackPush(&s, cur);
            cur = cur->_left;
        }
        //运行到这说明已经到达了最左节点,该遍历该节点的右子树了
        BTNode *top = StackTop(&s);
        cur = top->_right;
        //此处用cur记录了top的右节点,要及时出栈,否则如果有top有右子树,入栈后,就无法将top再出栈了
        StackPop(&s);

    }

}
void BTreeInOrderNonR(BTNode* root)//中序非递归遍历二叉树
{
    //思路与前序非递归类似,遇到一个节点时,先入栈,然后一直进入左子树
    Stack s;
    StackInit(&s);
    BTNode *cur = root;
    while (cur || StackEmpty(&s))//cur不为空说明当前节点还可以遍历
    {                            //stack不为空说明栈内元素可能还有孩子没有被遍历
        while (cur)
        {
            StackPush(&s, cur);
            cur = cur->_left;
        }
        //运行到这儿说明已经到达最左节点,该输出该节点了
        BTNode *top = StackTop(&s);
        printf("%d ", top->_data);
        cur = top->_right;//这里说明左孩子,中根节点已经遍历完毕,该右孩子遍历了
        StackPop(&s);
    }
}
void BTreePostOrderNonR(BTNode* root)//后序非递归遍历二叉树
{
    //后序非递归会比前序中序复杂一点,复杂的点就在于它的遍历顺序是左右中,
    //当把左孩子遍历完,该遍历右孩子了,这里cur会来到中根节点,当右孩子遍历完,cur又来到
    //中根节点,如何判断是否已经遍历右孩子?是后序遍历的关键。
    Stack s;
    StackInit(&s);
    BTNode *cur = root;
    BTNode *last = NULL;//新定义一个last记录右孩子是否遍历过,遍历过则将top->_right赋给last
    while (cur || StackEmpty(&s))
    {
        while (cur)
        {
            StackPush(&s, cur);
            cur = cur->_left;
        }
        BTNode *top = StackTop(&s);
        //如果此时发现top无右孩子或者右孩子已经被遍历过了,则直接输出top的data
        if (top->_right == NULL || last == top->_right)
        {
            printf("%d ", top->_data);
            StackPop(&s);
        }
        else
        {
            //进行到这里说明top有右孩子且尚未遍历过
            cur = top->_right;
            last = top->_right;

        }
    }
}

queue.h


typedef BTNode* QueueDataType;

typedef struct QueueNode
{
    QueueDataType _data;
    struct QueueNode* _next;
}QueueNode;

typedef struct Queue
{
    QueueNode* _head;
    QueueNode* _tail;
}Queue;

void QueueInit(Queue* q)
{
    assert(q);
    q->_head = NULL;
    q->_tail = NULL;
}

QueueNode* BuyQueueNode(QueueDataType x)
{
    QueueNode* node = (QueueNode*)malloc(sizeof(QueueNode));
    node->_data = x;
    node->_next = NULL;

    return node;
}

void QueuePush(Queue* q, QueueDataType x)
{
    assert(q);
    if (q->_tail == NULL)
    {
        q->_head = q->_tail = BuyQueueNode(x);
    }
    else
    {
        q->_tail->_next = BuyQueueNode(x);
        q->_tail = q->_tail->_next;
    }
}

void QueuePop(Queue* q)
{
    if (q->_head == NULL)
    {
        return;
    }
    else
    {
        QueueNode* next = q->_head->_next;
        free(q->_head);
        q->_head = next;

        if (q->_head == NULL)
        {
            q->_tail = NULL;
        }
    }
}

QueueDataType QueueFront(Queue* q)
{
    assert(q->_head);
    return q->_head->_data;
}

QueueDataType QueueBack(Queue* q)
{
    return q->_tail->_data;
}

size_t QueueSize(Queue* q)
{
    QueueNode* cur = q->_head;
    size_t size = 0;
    while (cur)
    {
        ++size;
        cur = cur->_next;
    }

    return size;
}

// 空 返回0
// 非空 返回1
int QueueEmpty(Queue* q)
{
    if (q->_head == NULL)
        return 0;
    else
        return 1;
}

stack.h

#pragma once
#include<stdio.h>
#include<windows.h>
#include<assert.h>
typedef BTNode* STDataType;
#define Stack_size 10
typedef struct Stack
{
    STDataType* _array;//数组指针
    size_t  _top; //栈顶 
    size_t  _end;//最大容量
}Stack;
// 栈的实现接口 
void StackInit(Stack* s)//栈的初始化
{
    assert(s);
    s->_array = (STDataType*)malloc(sizeof(STDataType)*Stack_size);
    s->_end = Stack_size;
    s->_top = 0;

}


void StackPush(Stack* s, STDataType x)//入栈
{
    assert(s);
    if (s->_end == s->_top)//栈已满
    {
        s->_end *= 2;
        s->_array = (STDataType*)realloc(s->_array, sizeof(STDataType)*(s->_end));
        s->_array[s->_top] = x;
        (s->_top)++;
    }
    else
    {
        s->_array[s->_top] = x;
        (s->_top)++;
    }
}


void Stack_print(Stack *s)
{
    if (s == NULL)
    {
        return;
    }
    while ((s->_top)--)
    {
        printf("%d\t", s->_array[s->_top]);
    }
}


void StackPop(Stack* s)//出栈
{
    assert(s);
    if (s->_top == 0)
    {
        printf("the stack is empty");
    }
    else
    {
        s->_top--;
    }
}


STDataType StackTop(Stack* s)//取栈顶元素
{
    assert(s);
    assert(s->_top);

        int num = s->_top;

        return  s->_array[(--num)];

}


size_t StackSize(Stack* s)//栈的长度
{
    assert(s);
    return s->_end;
}


int StackEmpty(Stack* s)//判断栈是否为空
{
    if (s->_top == 0)
    {
        return 0;
    }
    else
    {
        return 1;
    }
}

test.c

#include"binarytree.h"


void test()
{
    int a[] = { 1, 2, 3, '#', '#',4,'#', '#', 5, 6,'#' ,'#' ,'#' };
    int index = 0;
    BTNode *root = BinaryTreeCreateByPrev(a, &index, '#');
    BTreePrevOrder(root);
    printf("\n");
    BTreeInOrder(root);
    printf("\n");
    BTreePostOrder(root);
    printf("\n");
    printf("该树有%d个节点\n", BTreeSize(root));
    printf("该树有%d个叶子节点\n", BTreeLeafSize(root));
    printf("该树第3层的节点数为%d\n", BTreeKLevelSize(root, 3));
    printf("该树的深度为%d\n", BTreeDepth(root));

    printf("%d\n", BTreeFind(root, 3)->_data);
    printf("%d\n", IsCompleteBTree(root));
    /*BTreePrevOrderNonR(root);
    printf("\n");
    BTreeInOrderNonR(root);
    printf("\n");
    BTreePostOrderNonR(root);*/
}

int main()
{
    test();
    system("pause");
    return 0;
}


猜你喜欢

转载自blog.csdn.net/ferlan/article/details/80042580