一、树的相关概念
1、

节点的度:一个节点含有的子树的个数称为该节点的度;如上图:A的度为6
叶节点或终端节点:度为0的节点称为叶节点; 如上图:B、C、H、I...等节点为叶节点
非终端节点或分支节点:度不为0的节点; 如上图:D、E、F、G...等节点为分支节点
双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点
孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点; 如上图:B是A的孩子节点
兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点
树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为6
节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
树的高度或深度:树中节点的最大层次; 如上图:树的高度为4
堂兄弟节点:双亲在同一层的节点互为堂兄弟;如上图:H、I互为兄弟节点
节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先
子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙
森林:由m(m>0)棵互不相交的树的集合称为森林;
2、如何用代码来表示树
方式一、链表方法
假设说明了树的度是N
struct TreeNode
{
int data;
struct TreeNode* subs[N];
};
存在的问题:
(1)、可能会存在空间的浪费;
(2)、万一没有限定树的度是多少,此方法就无法使用;
方式二、数组方法
typedef struct TreeNode* SLDataType;
struct TreeNode
{
int data;
SeqList s;
};
存在的问题:
结构相对复杂;
方式三、结构数组存储(双亲表示法)
struct TreeNode
{
int parent;
int data;
};
存储的是自己的数值和父亲的下标;
以上三种方式都各有优缺点,最有表示法为左孩子右兄弟表示法;
方法四、左孩子右兄弟表示法
typedef int DataType;
struct Node
{
struct Node* _firstChild1; //第一个孩子节点
struct Node* _pNextBrother; //指向其下一个兄弟节点
DataType _data; // 节点中的数据域
};
二、二叉树概念
1、概念
一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵别称为左子树和右子树
的二叉树组成。(度最大为2)
二叉树的特点:
1. 每个结点最多有两棵子树,即二叉树不存在度大于2的结点。
2. 二叉树的子树有左右之分,其子树的次序不能颠倒。
注意:对于任意的二叉树都是由集中情况复合而成。
空数、只有根节点、只有左子树、只有右子树、左右子树都存在。
2、特殊的二叉树
(1). 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是
说,如果一个二叉树的层数为K,且结点总数是(2^k) -1 ,则它就是满二叉树。
(2). 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K
的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对
应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。
满二叉树:
第k层有2^(k-1)个节点;
假设树的高度是h的满二叉树,节点个数为2^h-1;
一颗满二叉树有N个节点,高度h=log2(N-1)个;
对任何一棵二叉树, 如果度为0其叶结点个数为 n0, 度为2的分支结点个数为 n2,则有n0=n2+1
完全二叉树:
前N-1层是满的;最后一层不满,但最后一层从左到右是连续的;
例
在具有 2n 个结点的完全二叉树中,叶子结点个数为( )
A n
B n+1
C n-1
D n/2
设度为2的节点个数为X2;
度为1的节点个数为X1;
度为0的几点个数为X0;
可得: X0+X1+X2=2n
因为对任何一棵二叉树, 如果度为0其叶结点个数为 n0, 度为2的分支结点个数为 n2,则有n0=n2+1;
所以X2=X0-1;
又因为X1是度为1的节点个数,在完全二叉树中,度为1的节点个数只可能是0或1个;
而此时X1必须是1才能保证n是整数;
故求得X0=n;答案是B;
三、堆(完全二叉树)
1、适合用数组存储;
2、堆分为大堆和小堆;
大堆:树中一个树及子树中,任何一个父亲都大于等于孩子;
小堆:树中一个数及子树中,任何一个父亲都小于等于孩子;
堆的应用:堆排序;topK问题。在N个数中,找出最大/最小的前K个;
Heap.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
typedef int HPDataType;
typedef struct Heap
{
int* a;
int size;
int capacity;
}HP;
void Swap(HPDataType* px, HPDataType* py);
void HeapInit(HP* hp);
void HeapDestroy(HP* hp);
void HeapPush(HP* hp, HPDataType x);
void HeapPop(HP* hp);
void HeapPrint(HP* hp);
bool HeapEmpty(HP* hp);
int HeapSize(HP* hp);
Heap.c
#include "Heap.h"
void Swap(HPDataType* px, HPDataType* py)
{
HPDataType* tmp = *px;
*px = *py;
*py = tmp;
}
void HeapInit(HP* hp)
{
assert(hp);
hp->a = NULL;
hp->size = hp->capacity = 0;
}
void HeapDestroy(HP* hp)
{
assert(hp);
free(hp->a);
hp->capacity = hp->size = 0;
}
void AdjustUp(int* a, int child)
{
assert(a);
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 HeapPush(HP* hp, HPDataType x)
{
assert(hp);
if (hp->size == hp->capacity)
{
size_t newCapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
HPDataType* tmp = (HPDataType*)malloc(sizeof(HPDataType)*newCapacity);
if (tmp == NULL)
{
printf("malloc fail!\n");
exit(-1);
}
hp->a = tmp;
hp->capacity = newCapacity;
}
hp->a[hp->size] = x;
hp->size++;
AdjustUp(hp->a, hp->size-1);
}
void* AdjustDown(int* a, int n, int parent)
{
int child = parent * 2 + 1;
while (child<n)
{
if (child + 1 < n&&a[child + 1] < a[child])
{
++child;
}
if (a[child] < a[parent])
{
Swap(&a[child], &a[parent]);
parent = child;
child = parent / 2 + 1;
}
else
{
break;
}
}
}
void HeapPop(HP* hp)
{
assert(hp);
assert(!HeapEmpty(hp));
Swap(&hp->a[0], &hp->a[hp->size - 1]);
hp->size--;
AdjustDown(hp->a, hp->size, 0);
}
void HeapPrint(HP* hp)
{
for (int i = 0; i < hp->size; ++i)
{
printf("%d", hp->a[i]);
}
printf("\n");
}
bool HeapEmpty(HP* hp)
{
assert(hp);
return hp->size == 0;
}
int HeapSize(HP* hp)
{
assert(hp);
return hp->size;
}
1、topK问题
在N个数中找出最大的前K个 或者 在N个数中找出最小的前K个;
方式一、先排降序,前K个就是最大的:时间复杂度:O(n*logn);浪费资源,只要前K个,但将所有的数都排了一遍;
方式二、直接将N个数依次插入大堆,Pop K 次,每次取堆顶的数,就是前K个,时间复杂度:O(N+K*logN);一般K是远小于N的。
方式三、假设N非常大,N是10亿,内存中存不下这些数,它们存在文件中,K是100,此时方法一和方法二都不能用了。
此时,(1)、用前K个数建立一个K个数的小堆;
(2)、剩下的N-K个数,依次跟堆顶的数据进行比较,如果比堆顶的数据大,就替换堆顶的数据,再向下调整;
(3)、最后堆里边的K个数就是最大的K个数;
时间复杂度:O(K+(N-K)*logK);
代码如下:
void PrintTopK(int* a, int n, int k)
{
HP hp;
HeapInit(&hp);
//创建一个k的数的小堆
for (int i = 0; i < k; i++)
{
HeapPush(&hp, a[i]);
}
//剩下的N-K个数跟堆顶的数据比较,比他大,就替换他进堆;
for (int i = k; i < n; ++i)
{
if (a[i] > HeapTop(&hp))
{
HeapPop(&hp);
HeapPush(&hp, a[i]);
}
}
HeapDestroy(&hp);
}
2、堆排序
排升序,用大堆;
排降序,用小堆;
void HeapSort(int* a, int n)
{
/*for (int i = 1; i < n; i++)
{
AdjustUp(a, i);
}*/
for (int i = (n - 1 - 1) / 2; i >= 0; --i)
{
AdjustDown(a, n, i);
}
for (int end = n - 1; end > 0; --end)
{
Swap(&a[end], &a[0]);
AdjustDown(a, end, 0);
}
}
三、二叉树的前中后序遍历
前序遍历:访问根节点的操作发生在遍历其左右子树之前;
中序遍历:访问根节点的操作发生在遍历其左右子树之中;
后序遍历:访问根节点的操作发生在遍历其左右字数之后;
代码如下
#include <stdio.h>
typedef int BTDataType;
typedef struct BinaryTreeNode
{
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
BTDataType data;
}BTNode;
//申请新节点
BTNode* BuyNode(BTDataType x)
{
BTNode* node = (BTNode*)malloc(sizeof(BTNode));
if (node == NULL)
{
printf("malloc fail!\n");
exit(-1);
}
node->data = x;
node->left = node->right = NULL;
return node;
}
BTNode* CreatBinaryTree()
{
BTNode* node1 = BuyNode(1);
BTNode* node2 = BuyNode(2);
BTNode* node3 = BuyNode(3);
BTNode* node4 = BuyNode(4);
BTNode* node5 = BuyNode(5);
BTNode* node6 = BuyNode(6);
node1->left = node2;
node1->right = node4;
node2->left = node3;
node4->left = node5;
node4->right = node6;
return node1;
}
//前序遍历
void PreOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL\n");
return;
}
printf("%c ", root->data);
PreOrder(root->left);
PreOrder(root->right);
}
//中序遍历
void InOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL\n");
return;
}
InOrder(root->left);
printf("%c ", root->data);
InOrder(root->right);
}
//后序遍历
void PostOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL\n");
return;
}
PostOrder(root->left);
PostOrder(root->right);
printf("%c ", root->data);
}
计算节点个数:
方法一
int BinaryTreeSize(BTNode* root)
{
if (root == NULL)
{
return;
}
static int count = 0;
++count;
BinaryTreeSize(root->left);
BinaryTreeSize(root->right);
return count;
}
此方法无法准确计算出
方法二
int BinaryTreeSize(BTNode* root,int* pn)
{
if (root == NULL)
{
return;
}
++(*pn);
BinaryTreeSize(root->left,pn);
BinaryTreeSize(root->right,pn);
}
方法三
int BinaryTreeSize(BTNode* root)
{
return root == NULL ? 0 : 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->left);
}
二叉树第K层节点的个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
assert(k > 1);
if (root == NULL)
{
return 0;
}
if (k == 1)
{
return 1;
}
return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->left, k - 1);
}
二叉树深度/高度
int BinaryTreeDepth(BTNode* root)
{
if (root == NULL)
{
return 0;
}
int leftDepth = BinaryTreeDepth(root->left);
int rightDepth = BinaryTreeDepth(root->right);
return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
}