行业知识
二叉树(详解)
���录
二叉树
一、二叉树概念及结构
1.概念
2.数据结构中的二叉树
3.特殊的二叉树
4.二叉树的存储结构
4.1顺序存储
4.2链式存储
5.二叉树的性质
二、二叉树顺序结构及概念
1.二叉树的顺序结构
2.堆的概念及结构
3.堆的实现
三、二叉树链式结构及实现
1.二叉树链式结构的遍历
2.二叉树的链式实现
一、 二叉树概念及结构
1.概念
一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成。
二叉树的特点:
- 每个结点最多有两棵子树,即二叉树不存在度大于2的结点。
- 二叉树的子树有左右之分,其子树的次序不能颠倒。
2.数据结构中的二叉树
3.特殊的二叉树
a.满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是(2^k) -1 ,则它就是满二叉树。
b.完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。
4.二叉树的存储结构
4.1顺序存储
顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。
4.2链式存储
二叉树的链式存储结构是指:用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。链式结构又分为二叉链和三叉链.
// 二叉链 struct BinaryTreeNode { struct BinTreeNode* _pLeft; // 指向当前节点左孩子 struct BinTreeNode* _pRight; // 指向当前节点右孩子 BTDataType _data; // 当前节点值域 } // 三叉链 struct BinaryTreeNode { struct BinTreeNode* _pParent; // 指向当前节点的双亲 struct BinTreeNode* _pLeft; // 指向当前节点左孩子 struct BinTreeNode* _pRight; // 指向当前节点右孩子 BTDataType _data; // 当前节点值域 }; 5.二叉树的性质
a.若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2^(i-1) 个结点.
b.若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2^h- 1.
c.对任何一棵二叉树, 如果度为0其叶结点个数为 n0, 度为2的分支结点个数为 n2,则有n0=n2+1
d.若规定根节点的层数为1,具有n个结点的满二叉树的深度,h=Log2(n+1). (ps:Log2(n+1)是log以2为
底,n+1为对数)
e.对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对于序号为i的结点有:
(1) 若i>0,i位置节点的双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点
(2) 若2i+1=n否则无左孩子
(3) 若2i+2=n否则无右孩子
二、二叉树顺序结构及概念
1.二叉树的顺序结构
普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。
2.堆的概念及结构
如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki = K2i+2) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
注意:
- 堆中某个节点的值总是不大于或不小于其父节点的值;
- 堆总是一棵完全二叉树。
3.堆的实现
typedef int HPDataType;typedef struct Heap{HPDataType* _a;int _size;int _capacity;}Heap;void swap(int *a, int *b);void AdjustDown(int *a, int parent, int n);void AdjustUp(int *a, int child, int n);// 堆的构建void HeapCreate(Heap* hp, HPDataType* a, int n);// 堆的销毁void HeapDestory(Heap* hp);// 堆的插入void HeapPush(Heap* hp, HPDataType x);// 堆的删除void HeapPop(Heap* hp);// 取堆顶的数据HPDataType HeapTop(Heap* hp);// 堆的数据个数int HeapSize(Heap* hp);// 堆的判空int HeapEmpty(Heap* hp);// 对数组进行堆排序void HeapSort(int* a, int n);void swap(int *a, int *b){int tmp = *a;*a = *b;*b = tmp;}void AdjustUp(int *a, int child, int n){int parent = (child - 1) / 2;while (child > 0){if (a[child] > a[parent]){swap(&a[child], &a[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}void AdjustDown(int *a, int parent,int n){int child = parent * 2 + 1;while ( child _a = (HPDataType*)malloc(sizeof(HPDataType)*n);if (hp->_a == NULL){printf("malloc fail");exit(-1);}for (int i = 0; i _a[i] = a[i];}hp->_size = hp->_capacity = n;for (int i = (n - 2) / 2; i >= 0; --i){AdjustDown(hp->_a,i, hp->_size);}}// 堆的销毁void HeapDestory(Heap* hp){assert(hp);hp->_size = hp->_capacity = 0;free(hp);}// 堆的插入void HeapPush(Heap* hp, HPDataType x){assert(hp);if (hp->_size == hp->_capacity){HPDataType* tmp = (HPDataType*)realloc(hp->_a, sizeof(HPDataType)* 2 * hp->_capacity);if (tmp == NULL){printf("realloc fail");exit(-1);}hp->_a = tmp;hp->_a[hp->_size] = x;++hp->_size;hp->_capacity *= 2;}else{hp->_a[hp->_size] = x;++hp->_size;}AdjustUp(hp->_a, hp->_size-1, hp->_size);}// 堆的删除void HeapPop(Heap* hp){assert(hp);assert(hp->_size>0);swap(&hp->_a[hp->_size-1], &hp->_a[0]);--hp->_size;AdjustDown(hp->_a, 0, hp->_size);}// 取堆顶的数据HPDataType HeapTop(Heap* hp){assert(hp);assert(hp->_size>0);return hp->_a[0];}// 堆的数据个数int HeapSize(Heap* hp){assert(hp);return hp->_size;}// 堆的判空int HeapEmpty(Heap* hp){assert(hp);return hp->_size == 0 ? 1 : 0;for (int i = 0; i = 0; --i){AdjustDown(a, i, n);}int end = n - 1;while (end > 0){swap(&a[0], &a[end]);AdjustDown(a, 0, end);--end;}}三、二叉树链式结构及实现
1.二叉树链式结构的遍历
所谓遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问。访问结点所做的操作依赖于具体的应用问 题。 遍历是二叉树上最重要的运算之一,是二叉树上进行其它运算之基础。
前序/中序/后序的递归结构遍历:是根据访问结点操作发生位置命名
前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。由于被访问的结点必是某子树的根,所以N(Node)、L(Left subtree)和R(Right subtree)又可解释为根、根的左子树和根的右子树。NLR、LNR和LRN分别又称为先根遍历、中根遍历和后根遍历。
层序遍历:除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。2.二叉树的链式实现
typedef char BTDataType;typedef struct BinaryTreeNode{BTDataType _data;struct BinaryTreeNode* _left;struct BinaryTreeNode* _right;}BTNode;typedef BTNode* QDataType;// 链式结构:表示队列 typedef struct QListNode{struct QListNode* _next;QDataType _data;}QNode;// 队列的结构 typedef struct Queue{QNode* _front;QNode* _rear;}Queue;BTNode* CreateBTNode(BTDataType x);// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi);// 二叉树销毁void BinaryTreeDestory(BTNode** root);// 二叉树节点个数int BinaryTreeSize(BTNode* root);// 二叉树叶子节点个数int BinaryTreeLeafSize(BTNode* root);// 二叉树第k层节点个数int BinaryTreeLevelKSize(BTNode* root, int k);// 二叉树查找值为x的节点BTNode* BinaryTreeFind(BTNode* root, BTDataType x);// 二叉树前序遍历 void BinaryTreePrevOrder(BTNode* root);// 二叉树中序遍历void BinaryTreeInOrder(BTNode* root);// 二叉树后序遍历void BinaryTreePostOrder(BTNode* root);// 初始化队列 void QueueInit(Queue* q);// 队尾入队列 void QueuePush(Queue* q, QDataType data);// 队头出队列 void QueuePop(Queue* q);// 获取队列头部元素 QDataType QueueFront(Queue* q);// 获取队列队尾元素 QDataType QueueBack(Queue* q);// 获取队列中有效元素个数 int QueueSize(Queue* q);// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 int QueueEmpty(Queue* q);// 销毁队列 void QueueDestroy(Queue* q);// 层序遍历void BinaryTreeLevelOrder(BTNode* root);// 判断二叉树是否是完全二叉树int BinaryTreeComplete(BTNode* root);// 初始化队列 void QueueInit(Queue* q){assert(q);q->_front = q->_rear = NULL;}// 队尾入队列 void QueuePush(Queue* q, QDataType data){assert(q);QNode *newnode = ((QNode*)malloc(sizeof(QNode)));newnode->_data = data;newnode->_next = NULL;if (q->_rear == NULL){q->_front = q->_rear = newnode;}else{q->_rear->_next = newnode;//q->_rear = q->_rear->_next;q->_rear = newnode;}}// 队头出队列 void QueuePop(Queue* q){assert(q);assert(!QueueEmpty(q));if (q->_front == q->_rear){free(q->_front);//free(q->_rear);q->_front = q->_rear = NULL;}else{QNode *cur = q->_front->_next;free(q->_front);q->_front = cur;}}// 获取队列头部元素 QDataType QueueFront(Queue* q){assert(q);assert(!QueueEmpty(q));return q->_front->_data;}// 获取队列队尾元素 QDataType QueueBack(Queue* q){assert(q);assert(!QueueEmpty(q));return q->_rear->_data;}// 获取队列中有效元素个数 int QueueSize(Queue* q){assert(q);int size = 0;QNode* cur = q->_front;while (cur){++size;cur = cur->_next;}return size;}// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 int QueueEmpty(Queue* q){assert(q);return q->_front == NULL ? 1 : 0;}// 销毁队列 void QueueDestroy(Queue* q){assert(q);QNode *cur = q->_front;while (cur){QNode *next = cur->_next;free(cur);cur = next;}q->_front = q->_rear = NULL;}BTNode* CreateBTNode(BTDataType x){BTNode *node = (BTNode*)malloc(sizeof(BTNode));node->_data = x;node->_left = NULL;node->_right = NULL;return node;}// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi){if (a[*pi] == '#'){return NULL;}BTNode *node = (BTNode*)malloc(sizeof(BTNode));node->_data = a[*pi];++*pi;node->_left = BinaryTreeCreate(a, n, pi);++*pi;node->_right = BinaryTreeCreate(a, n, pi);return node;}// 二叉树销毁void BinaryTreeDestory(BTNode** root){if (*root != NULL){if ((*root)->_left) // 有左孩子BinaryTreeDestory(&(*root)->_left); // 销毁左孩子子树if ((*root)->_right) // 有右孩子BinaryTreeDestory(&(*root)->_right); // 销毁右孩子子树free(*root); // 释放根结点*root = NULL; // 空指针赋NULL}}// 二叉树节点个数int BinaryTreeSize(BTNode* root){if (root == NULL){return 0;}return BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 1;}// 二叉树叶子节点个数int BinaryTreeLeafSize(BTNode* root){if (root == NULL){return 0;}if (root->_left == NULL&&root->_right == NULL){return 1;}return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);}// 二叉树第k层节点个数int BinaryTreeLevelKSize(BTNode* root, int k){if (root == NULL){return 0;}if (k == 1){return 1;}return BinaryTreeLevelKSize(root->_left, k - 1) + BinaryTreeLevelKSize(root->_right, k - 1);}// 二叉树查找值为x的节点BTNode* BinaryTreeFind(BTNode* root, BTDataType x){if (root == NULL){return NULL;}if (root->_data == x){return root;}BTNode* ret=BinaryTreeFind(root->_left,x);if (ret != NULL){return ret;}ret = BinaryTreeFind(root->_right, x);if (ret != NULL){return ret;}return NULL;}// 二叉树前序遍历 void BinaryTreePrevOrder(BTNode* root){if (root == NULL){//printf("NULL ");return;}printf("%c ", root->_data);BinaryTreePrevOrder(root->_left);BinaryTreePrevOrder(root->_right);}// 二叉树中序遍历void BinaryTreeInOrder(BTNode* root){if (root == NULL){//printf("NULL ");return;}BinaryTreeInOrder(root->_left);printf("%c ", root->_data);BinaryTreeInOrder(root->_right);}// 二叉树后序遍历void BinaryTreePostOrder(BTNode* root){if (root == NULL){//printf("NULL ");return;}BinaryTreePostOrder(root->_left);BinaryTreePostOrder(root->_right);printf("%c ", root->_data);}// 层序遍历void BinaryTreeLevelOrder(BTNode* root){Queue q;QueueInit(&q);if (root){QueuePush(&q, root);}while (!QueueEmpty(&q)){BTNode *front = QueueFront(&q);QueuePop(&q);printf("%c ", front->_data);if (front->_left){QueuePush(&q, front->_left);}if (front->_right){QueuePush(&q, front->_right);}}}// 判断二叉树是否是完全二叉树int BinaryTreeComplete(BTNode* root){Queue q;QueueInit(&q);if (root){QueuePush(&q, root);}while (!QueueEmpty(&q)){BTNode *front = QueueFront(&q);QueuePop(&q);if (front == NULL){break;}printf("%s ", front->_data);if (front->_left){QueuePush(&q, front->_left);}if (front->_right){QueuePush(&q, front->_right);}}while (!QueueEmpty(&q)){BTNode *front = QueueFront(&q);QueuePop(&q);if (front != NULL){return 0;}}return 1;}