一.树的定义
树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。
1.有一个特殊的结点,称为根结点,根结点没有前驱结点。
注意:树形结构中,子树之间不能有交集,否则就不是树形结构。
树有很多种表示方式如:双亲表示法,孩子表示法、孩子双亲表示法以及孩子兄弟表示法
二.树的相关概念
-
深度:从根节点到最远叶子节点的最长路径上的节点数。
-
高度:从当前节点到叶子节点的最长路径上的节点数。
-
度:节点的子节点数, 如上图:A的为 6 (二叉树中节点的度不超过2)。
-
叶子节点:没有子节点的节点 (度为0的结点)。
-
内部节点:至少有一个子节点的节点。
-
父节点:若一个结点含有子节点,则这个结点称为其子节点的父节点。
-
节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推。
三.二叉树的概念及结构


3.1.特殊的二叉树
-
完全二叉树:判断二叉树是否是完全二叉树(所有层都填满,除了最后一层,且最后一层从左到右填充)。完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。
-
满二叉树:判断二叉树是否是满二叉树(每个节点都有0或2个子节点)。也就是说,如果一个二叉树的层数为K,且结点总数是(2的k次方-1)则它就是满二叉树。
-
二叉搜索树(BST):判断二叉树是否是二叉搜索树(左子树所有节点小于根节点,右子树所有节点大于根节点)。(后续文章会说明)
3.2.二叉树的性质
1. 若规定根结点的层数为1,则一棵非空二叉树的第i层上最多有2的i-1次方个节点。
2. 若规定根结点的层数为1,则深度为h的二叉树的最大结点数是2的h次方-1个节点。
3.对任何一棵二叉树, 如果度为0其叶结点个数为m,度为2的分支结点个数为n,则有m=n+1。
3.3.二叉树的存储结构
3.3.1.顺序结构

堆
堆是一种完全二叉树,可以用数组实现,分为最大堆和最小堆。主要操作是插入和删除,时间复杂度为O(log n),构建堆的时间复杂度是O(n)。

堆的实现
Heap.h
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int HPDataType;
typedef struct Heap
{
HPDataType* a;
int size;
int capacity;
}HP;
void Swap(HPDataType* p1, HPDataType* p2);
void AdjustUp(HPDataType* a, int child);
void AdjustDown(HPDataType* a, int n, int parent);
void HPInit(HP* php);
void HPDestroy(HP* php);
void HPPush(HP* php, HPDataType x);
void HPPop(HP* php);
HPDataType HPTop(HP* php);
bool HPEmpty(HP* php);
Heap.c
#include"Heap.h"
void HPInit(HP* php)
{
assert(php);
php->a = NULL;
php->size = php->capacity = 0;
}
void HPDestroy(HP* php)
{
assert(php);
free(php->a);
php->a = NULL;
php->size = php->capacity = 0;
}
void Swap(HPDataType* pa, HPDataType* pb)
{
HPDataType tmp = *pa;
*pa = *pb;
*pb = tmp;
}
void AdjustUp(HPDataType* a, int child)
{
//计算父亲数组下标 满足这种关系
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 HPPush(HP* php, HPDataType x)
{
assert(php);
if (php->size == php->capacity)
{
int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
HPDataType* tmp = (HPDataType*)realloc(php->a, newcapacity * sizeof(HPDataType));
if (tmp == NULL)
{
perror("realloc fail!");
return;
}
php->a = tmp;
php->capacity = newcapacity;
}
php->a[php->size] = x;
php->size++;
AdjustUp(php->a, php->size - 1);
}
void AdjustDown(HPDataType* 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 HPPop(HP* php)
{
assert(php);
assert(php->size > 0);
Swap(&php->a[0], &php->a[php->size - 1]);
php->size--;
AdjustDown(php->a, php->size,0);
}
HPDataType HPTop(HP* php)
{
assert(php);
assert(php->size > 0);
return php->a[0];
}
bool HPEmpty(HP* php)
{
assert(php);
return php->size==0;
}
建堆之向上调整算法和向下调整算法
#define _CRT_SECURE_NO_WARNINGS 1
#include "Heap.h"
#include <time.h>
void test01()
{
int a[] = { 4,2,8,1,5,6,9,7};
HP hp;
HPInit(&hp);
int sz = sizeof(a) / sizeof(int);
for (size_t i = 0; i < sz; i++)
{
HPPush(&hp, a[i]);
}
int i = 0;
while (!HPEmpty(&hp))
{
a[i++] = HPTop(&hp);
HPPop(&hp);
}
for (i = 0; i < sz; i++)
{
printf("%d ",a[i]);
}
}
int main()
{
test01();
return 0;
}
运行结果为:。
我们也就可以衍生为TopK问题
#define _CRT_SECURE_NO_WARNINGS 1
#include "Heap.h"
#include <time.h>
void test01()
{
int a[] = { 4,2,8,1,5,6,9,7};
HP hp;
HPInit(&hp);
int sz = sizeof(a) / sizeof(int);
for (size_t i = 0; i < sz; i++)
{
HPPush(&hp, a[i]);
}
int k = 0;
scanf("%d", &k);
//找出最大的前k个
while (k--)
{
printf("%d ", HPTop(&hp));
HPPop(&hp);
}
HPDestroy(&hp);
}
我们也就可以将向上调整算法与向下调整算法结合进行堆排序
(1)向上调整建堆 排序
//堆排序 O(N*logN)
void HeapSort(int* a, int n)
{
//想要得到的最终数据为降序 建小堆
//想要得到的最终数据为升序 建大堆
//向上调整建堆 时间复杂度O(N*logN)
for (int i = 1; i < n; i++)
{
AdjustUp(a, i);
}
int end = n - 1;
//O(N*log N)
while (end > 0)
{
Swap(&a[0], &a[end]);
AdjustDown(a, end, 0);
--end;
}
}
(2)向下调整建堆
void HeapSort(int* a, int n)
{
//向下调整建堆 时间复杂度O(N) 更好
//从最后一个数的父亲开始调整
for (int i = (n - 1 - 1) / 2; i >= 0; i--)
{
AdjustDown(a, n, i);
}
int end = n - 1;
//O(N*log N)
while (end > 0)
{
Swap(&a[0], &a[end]);
AdjustDown(a, end, 0);
--end;
}
}
大量数据的Topk问题
#define _CRT_SECURE_NO_WARNINGS 1
#include "Heap.h"
#include <time.h>
void GreateNData()
{
//造数据
int n = 100000;
srand(time(0));
const char* file = "data.txt";
FILE* fin = fopen(file, "w");
if (fin == NULL)
{
perror("fopen errpr");
return;
}
for (int i = 0; i < n; ++i)
{
int x = (rand()+i)%10000000;
fprintf(fin, "%d\n", x);
}
fclose(fin);
}
//top k问题
//思路:用前k个数,建一个小堆,剩下的数据与堆顶数据比较
//如果比堆顶的数据大,就替代堆顶进堆 (覆盖根位置,然后向下调整)
//最后 这个小堆中的k个,就是最大的前 k 个
void HeapTest03()
{
int k = 0;
printf("请输入k:");
scanf("%d", &k);
int* arr = (int*)malloc(sizeof(int) * k);
if (arr == NULL)
{
perror("malloc fail!");
return;
}
const char* file = "data.txt";
FILE* fout = fopen(file, "r");
if (fout == NULL)
{
perror("fopen error");
return;
}
//读取文件的前k个
for (int i = 0; i < k; i++)
{
fscanf(fout, "%d", &arr[i]);
}
//建小堆
for(int i = (k - 1 - 1) / 2;i >= 0; i--)
{
AdjustDown(arr, k, i);
}
//读取剩下的N-K个数据
int x = 0;
while (fscanf(fout, "%d", &x) > 0)
{
if (x > arr[0])
{
arr[0] = x;
AdjustDown(arr, k, 0);
}
}
printf("最大的前%d个数:", k);
for (int i = 0; i < k; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
GreateNData();
HeapTest03();
return 0;
}
3.3.2.链式结构
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType _data;
struct BinaryTreeNode* _left;
struct BinaryTreeNode* _right;
}BTNode;
二叉树的遍历
二叉树遍历主要有三种基本方法:前序遍历、中序遍历和后序遍历。这三种都属于深度优先搜索(DFS)的方式。另外还有层序遍历,也就是广度优先搜索(BFS),按层次遍历节点。
1.前序遍历 根节点 → 左子树 → 右子树
前序遍历的顺序是根节点、左子树、右子树。也就是说,先访问根节点,然后递归地前序遍历左子树,接着递归地前序遍历右子树。
假设空节点为N
void PrevOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
printf("%d ", root->data);
PrevOrder(root->left);
PrevOrder(root->right);
}
递归展开图
前序遍历结果:1 2 3 4 5 6 。
2.中序遍历 左子树 → 根节点 → 右子树
void InOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
InOrder(root->left);
printf("%d ", root->data);
InOrder(root->right);
}
3.后序遍历 左子树 → 右子树 → 根节点
同理可得
void PostOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
LeftOrder(root->left);
LeftOrder(root->right);
printf("%d ", root->data);
}
4.层序遍历
(用c++写会更方便)
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
// 二叉树节点定义
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> result;
if (!root) return result;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
int levelSize = q.size();
vector<int> currentLevel;
for (int i = 0; i < levelSize; ++i) {
TreeNode* node = q.front();
q.pop();
currentLevel.push_back(node->val);
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
result.push_back(currentLevel);
}
return result;
}
// 测试代码
int main()
{
TreeNode* root = new TreeNode(1);
root->left = new TreeNode(2);
root->right = new TreeNode(3);
root->left->left = new TreeNode(4);
root->left->right = new TreeNode(5);
root->right->right = new TreeNode(6);
vector<vector<int>> traversal = levelOrder(root);
cout << "层序遍历结果:" << endl;
for (auto& level : traversal) {
cout << "[";
for (size_t i = 0; i < level.size(); ++i) {
cout << level[i];
if (i < level.size()-1) cout << ", ";
}
cout << "]" << endl;
}
delete root->left->left;
delete root->left->right;
delete root->right->right;
delete root->left;
delete root->right;
delete root;
return 0;
}
二叉树其他字段
1.TreeSize(二叉树结点个数)
int TreeSize(BTNode* root)
{
return root == NULL ? 0 :
TreeSize(root->left) + TreeSize(root->right) + 1;
}
2.TreeLeafSize(二叉树叶子结点个数)
int TreeLeafSize(BTNode* root)
{
if (root == NULL)
return 0;
if (root->left == NULL && root->right == NULL)
return 1;
return TreeLeafSize(root->left)
+ TreeLeafSize(root->right);
}
3.TreeLevelKSize (二叉树第k层结点个数)
int TreeLevelKSize(BTNode* root, int k)
{
if (root == NULL)
return 0;
if (k == 1)
return 1;
// 子问题
return TreeLevelKSize(root->left, k - 1)
+ TreeLevelKSize(root->right, k - 1);
}
4.TreeFind(二叉树查找值为x的结点)
// 二叉树查找值为x的节点
BTNode* TreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
return NULL;
if (root->data == x)
return root;
BTNode* ret1 = TreeFind(root->left, x);
if (ret1)
return ret1;
BTNode* ret2 = TreeFind(root->right, x);
if (ret2)
return ret2;
return NULL;
}
5.TreeHeight(子树高度)
int TreeHeight(BTNode* root)
{
if (root == NULL)
return 0;
int leftHeight = TreeHeight(root->left);
int rightHeight = TreeHeight(root->right);
return leftHeight > rightHeight ?
leftHeight + 1 : rightHeight + 1;
}
6.BinaryTreeDestory(二叉树的销毁)
void TreeDestory(BTNode* root)
{
if (root == NULL)
return;
TreeDestory(root->left);
TreeDestory(root->right);
free(root);
}
7.判断二叉树是否是满二叉树
// 判断是否为满二叉树
bool isFullBinaryTree(TreeNode* root) {
if (root == nullptr)
return true;
if (root->left == nullptr && root->right == nullptr)
return true;
if (root->left != nullptr && root->right != nullptr)
{
return isFullBinaryTree(root->left) && isFullBinaryTree(root->right);
}
return false;
}
8.判断二叉树是否为完全二叉树
// 判断是否为完全二叉树
bool isCompleteBinaryTree(TreeNode* root)
{
if (root == nullptr)
return true; // 空树是完全二叉树
queue<TreeNode*> q;
q.push(root);
bool foundNull = false; // 标记是否遇到空节点
while (!q.empty())
{
TreeNode* current = q.front();
q.pop();
// 检查当前节点
if (current == nullptr)
{
foundNull = true; // 遇到空节点
}
else
{
// 如果已经遇到空节点,又发现非空节点,则不是完全二叉树
if (foundNull)
{
return false;
}
// 将左右子节点入队(即使是空节点也入队)
q.push(current->left);
q.push(current->right);
}
}
return true; // 所有节点都满足条件
}
二叉树OJ题目
1.单值二叉树
bool isUnivalTree(struct TreeNode* root) {
if(root==NULL)
return true;
if(root->left&&root->left->val!=root->val)
return false;
if(root->right&&root->right->val!=root->val)
return false;
return isUnivalTree(root->left)&&isUnivalTree(root->right);
}
2.相同的树
bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{
if(p == NULL && q == NULL)
return true;
if(p == NULL||q ==NULL)
return false;
if(p->val!=q->val)
return false;
return isSameTree(p->left,q->left)&&isSameTree(p->right,q->right);
}
3.对称二叉树
bool _isSymmetric(struct TreeNode* p,struct TreeNode* q)
{
if(p == NULL && q ==NULL)
return true;
//其中有一个为空 则为false
if(p == NULL || q == NULL)
return false;
if(p->val!=q->val)
return false;
if(p->left == q->right && p->right == q->left)
return true;
return _isSymmetric(p->left,q->right)&&_isSymmetric(p->right,q->left);
}
bool isSymmetric(struct TreeNode* root) {
return _isSymmetric(root->left,root->right);
}
4.二叉树的前序遍历
//前端遍历后的值放入数组中
int Treesize(struct TreeNode* root)
{
return root == NULL ? 0 :Treesize(root->left) + Treesize(root->right) + 1;
}
void BinaryTreePrevOrder(struct TreeNode* root, int* arr, int* pi)
{
if (root == NULL)
return;
arr[(*pi)++] = root->val;
BinaryTreePrevOrder(root->left, arr, pi);
BinaryTreePrevOrder(root->right, arr, pi);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
*returnSize = Treesize(root);
int* arr = (int*)malloc(sizeof(int) * (*returnSize));
int i = 0;
BinaryTreePrevOrder(root, arr, &i);
return arr;
}
5. 二叉树的中序遍历
void BinaryTreeMiddleOrder(struct TreeNode* root,int* arr,int* pi)
{
if (root == NULL)
return;
BinaryTreeMiddleOrder(root->left, arr, pi);
arr[(*pi)++] = root->val;
BinaryTreeMiddleOrder(root->right, arr, pi);
}
int TreeSize(struct TreeNode* root)
{
if(root == NULL)
return 0;
return TreeSize(root->left) + TreeSize (root->right) +1;
}
int* inorderTraversal(struct TreeNode* root, int* returnSize) {
*returnSize = TreeSize(root);
int* array = (int*)malloc(sizeof(int)*(*returnSize));
int i =0;
BinaryTreeMiddleOrder(root,array,&i);
return array;
}
6.二叉树的后序遍历
同理4.5。
7.另一颗树的子树
bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{
if(p==NULL&&q==NULL)
return true;
if(p==NULL||q==NULL)
return false;
if(p->val!=q->val)
return false;
return isSameTree(p->left,q->left)
&&isSameTree(p->right,q->right);
}
//找出root中所有的子树和Subroot进行比较
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot)
{
//subRoot 不可能为空
if(root==NULL)
return false;
if(root->val==subRoot->val&&isSameTree(root,subRoot))
return true;
return isSubtree(root->left,subRoot) || isSubtree(root->right,subRoot);
}
8 .二叉树的构建及遍历
#include <stdio.h>
#include <stdlib.h>
typedef struct BinaryTreeNode {
char val;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
} BTNode;
BTNode* CreatBinaryTree(char* a,int* pi)
{
if(a[*pi]=='#')
{
(*pi)++;
return NULL;
}
BTNode* root=(BTNode*)malloc(sizeof(BTNode));
root->val=a[(*pi)++];
root->left=CreatBinaryTree(a,pi);
root->right=CreatBinaryTree(a,pi);
return root;
}
//中序遍历
void BinaryTreeInOrder(BTNode* root) {
if (root == NULL) {
return;
}
BinaryTreeInOrder(root->left);
printf("%c ", root->val);
BinaryTreeInOrder(root->right);
}
int main()
{
char arr[100];
scanf("%s",arr);
int i=0;
BTNode* root=CreatBinaryTree(arr,&i);
BinaryTreeInOrder(root);
return 0;
}