树论复习–算法框架
本人自己整理的,觉得有所帮助的话麻烦关注点赞一下,你们的关注就是我的鼓励!!
树论
二叉树
使用结构体数据进行模拟的话,需要注意
-
单独定义结构体例如学生(分数,姓名)
-
初始化二叉树结构时 把数据域换成结构体指针
-
创建树
- 把数据换成结构体数据
- 记得给结构体对象分配内存
if(temp == -1 && name == null) { *tree = NULL; //相当于我们已经到了叶子节点 } Stu* stu = new Stu;
以整形数据模拟代码实例:
-
二叉树结构体初始化
typedef struct BiTNode { int data; struct BiTNode* left; struct BiTNode* right; }BiTNode, *BiTree;
-
前序中序后序递归遍历
//前序遍历 void preOrder(BiTree Tree) { if(Tree) { printf("%d ", Tree->data); preOrder(Tree->left); preOrder(Tree->right); } } //中序遍历 void InOrder(BiTree Tree) { if(Tree) { InOrder(Tree->left); printf("%d ", Tree->data); InOrder(Tree->right); } } //后序遍历 void postOrder(BiTree Tree) { if(Tree) { postOrder(Tree->left); postOrder(Tree->right); printf("%d ", Tree->data); } }
-
初始化创建二叉树
//创建树(根左右) void createBiTree(BiTree* tree) { int temp = 0; scanf("%d", &temp); if(temp == -1) { *tree = NULL; //结束输入了,说明已经到达叶子节点 } else { *tree = new BiTNode; (*tree)->data = temp; createBiTree(&((*tree)->left)); createBiTree(&((*tree)->right)); } }
-
求最大深度
//求深度,通过depth带回 void BiTreeDepth(BiTree Tree, int level, int* depth) { if(Tree) { //后序遍历的话先查找左子树再查找右子树深度 BiTreeDepth(Tree->right, level + 1, depth); BiTreeDepth(Tree->left, level + 1, depth); //最后比较一下 if(level > *depth) { *depth = level; } } }
-
求叶子结点个数
//求叶子节点个数 void CountLeaf(BiTree Tree, int* count) { if(Tree) { if((!Tree->left) && (!Tree->right)) { (*count)++; // printf("%d ",Tree->data); } CountLeaf(Tree->left, count); CountLeaf(Tree->right, count); } }
-
凹入表显示
//凹入表 void printTree(BiTree Tree, int depth, char sign) { if(!Tree) return; for(int i = 0; i < depth + 2; i++) { printf("-"); } printf("+"); printf("%d(%c)\n", Tree->data, sign); printTree(Tree->left, depth + 1, 'L'); printTree(Tree->right, depth + 1, 'R'); }
-
层序遍历(用整型数据进行模拟)==>使用的是链队列
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<malloc.h> #include<conio.h> typedef struct BiTNode { int data; struct BiTNode* left; struct BiTNode* right; }BiTNode, *BiTree; typedef struct QNode { BiTNode* ptr; struct QNode* next; }QNode, *QueuePtr; typedef struct { QueuePtr front; QueuePtr rear; }Queue; //创建树(根左右) void createBiTree(BiTree* tree) { int temp = 0; scanf("%d", &temp); if(temp == -1) { *tree = NULL; //结束输入了,说明已经到达叶子节点 } else { *tree = new BiTNode; (*tree)->data = temp; createBiTree(&((*tree)->left)); createBiTree(&((*tree)->right)); } } //初始化 int initQueue(Queue* q) { q->front = q->rear = new QNode; if(q->front == NULL) return 0; q->front->next = NULL; printf("初始化成功!\n"); return 1; } //进队列 void enQueue(Queue* q, BiTNode* temp) { QNode* newNode = new QNode; newNode->ptr = temp; newNode->next = NULL; //尾插 q->rear->next = newNode; q->rear = newNode; } //出队列 BiTNode* deQueue(Queue* q) { if(q->front == q->rear) { printf("此时队列为空,无法出队\n"); return NULL; } QNode* cur = q->front->next; BiTNode* delPtr = cur->ptr; q->front->next = cur->next; if(q->rear == cur) { //说明此时只有一个元素 q->rear = q->front; } free(cur); return delPtr; } int QueueEmpty(Queue queue) { if(queue.front == queue.rear) { return 1; // 空 } return 0; } void layer(BiTree tree) { Queue queue; initQueue(&queue); BiTNode* temp; if(tree) { enQueue(&queue, tree); } while(!QueueEmpty(queue)) { temp = deQueue(&queue); printf("%d ", temp->data); if(temp->left) enQueue(&queue, temp->left); if(temp->right) enQueue(&queue, temp->right); } } int main() { BiTree tree; createBiTree(&tree); layer(tree); return 0; }
-
读边法创建二叉树==>使用的是链队列
int QueueEmpty(Queue queue) { if(queue.front == queue.rear) { return 1; // 空 } return 0; } BiTNode* getHead(Queue queue) { QNode* cur = queue.front->next; return cur->ptr; } // 读入边的方式创建二叉树 void create_BiTree(BiTree *tree) { Queue queue; initQueue(&queue); *tree = NULL; char fa, ch; // fa表示双亲节点的值 ch是孩子节点的值 int flag = 0; // 表示双亲节点的左右孩子关系 BiTNode* cur; BiTNode* head; cin >> fa >> ch >> flag; // printf("%c%c%d", fa, ch, flag); // 说明此时不是只有一个节点 while(ch != '#') { cur = new BiTNode; cur->data = ch; // 创建孩子节点 cur->left = cur->right = NULL; enQueue(&queue, cur); if(fa == '#') { *tree = cur; // 表示此时这个节点是根节点 } else { // 取队头判断是不是读入边的父节点 head = getHead(queue); // 一直出队到此时队列的队头是读入边的双亲节点 while(head->data != fa) { deQueue(&queue); head = getHead(queue); } // 链接左右孩子 if(flag == 0) { head->left = cur; } else { head->right = cur; } } cin >> fa >> ch >> flag; } } void layer(BiTree tree) { Queue queue; initQueue(&queue); BiTNode* temp; if(tree) { enQueue(&queue, tree); } while(!QueueEmpty(queue)) { temp = deQueue(&queue); printf("%c ", temp->data); if(temp->left) enQueue(&queue, temp->left); if(temp->right) enQueue(&queue, temp->right); } }
-
层次读边法和层序遍历==>使用的是循环队列
BiTNode* getHead(Queue queue) { printf("此时队头元素是:%s\n", queue.info[queue.front]->data); return queue.info[queue.front]; } void create_BiTree(BiTree* tree) { Queue queue; initQueue(&queue); *tree = NULL; char fa[20], ch[20]; int flag = 0; cin >> fa >> ch >> flag; BiTNode* cur; // 当前节点放在外面定义 BiTNode* head; // 头 while(strcmp(ch, "#") != 0) { cur = new BiTNode; strcpy(cur->data, ch); cur->left = cur->right = NULL; enQueue(&queue, cur); if(strcmp(fa, "#") == 0) { *tree = cur; } else { // 取队头 head = getHead(queue); while(strcmp(head->data, fa) != 0) { deQueue(&queue); head = getHead(queue); } if(flag == 0) { head->left = cur; } else { head->right = cur; } } cin >> fa >> ch >> flag; } } // 求二叉树宽度 int cal_width(BiTree tree) { Queue queue; initQueue(&queue); if(tree) enQueue(&queue, tree); int level = queue.rear; int width = queue.rear - queue.front; BiTNode* cur; while(!QueueEmpty(queue)) { cur = deQueue(&queue); if(cur->left) enQueue(&queue, cur->left); if(cur->right) enQueue(&queue, cur->right); if(level == queue.front) { level = queue.rear; if(width < queue.rear - queue.front) { width = queue.rear - queue.front; } } } return width; } // 层次遍历 void layer(BiTree tree) { Queue queue; initQueue(&queue); if(tree) enQueue(&queue, tree); BiTNode* temp; while(!QueueEmpty(queue)) { temp = deQueue(&queue); printf("%s ", temp->data); //访问节点 if(temp->left) enQueue(&queue, temp->left); if(temp->right) enQueue(&queue, temp->right); } }
-
判断是不是完全二叉树
思路就是:层序遍历如果遍历到空节点之后后面还出现了数据就不是完全二叉树,如果后面遍历的都是空节点就是完全二叉树
// 判断是不是完全二叉树 int isComplete(BiTree tree) { if(!tree) return 0; Queue queue; BiTNode* cur; initQueue(&queue); if(tree) enQueue(&queue, tree); while(!QueueEmpty(queue)) { cur = deQueue(&queue); if(cur) { printf("%c", cur->data); } if(cur) { enQueue(&queue, cur->left); enQueue(&queue, cur->right); } else { while(!QueueEmpty(queue)) { cur = deQueue(&queue); if(cur) return 0; // 如果中间出现有数据的节点说明不是完全二叉树 } } } return 1; }
-
求二叉树的宽度
思路:
-
使用的是循环队列
-
层序遍历,用level来判断是不是到了需要更新长度时机
-
比较width和进入每一层时队列的此时长度的大小,如果队列长度此时大于width就更新
width < queue.rear - queue.front
代码实现:
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<malloc.h> #include<conio.h> #include<iostream> using namespace std; #define N 20 typedef struct BiTNode { char data[20]; struct BiTNode* left; struct BiTNode* right; }BiTNode, *BiTree; //用数组模拟队列 typedef struct { BiTNode* data[N]; int front; int rear; //指向的是队尾的后一个元素 int queueSize; //队列最大容量 }Queue; //初始化 void initQueue(Queue* queue) { queue->queueSize = N; queue->front = 0; queue->rear = 0; //指向的是队尾的后一个元素 } //进队列 void enQueue(Queue* queue, BiTNode* temp) { if((queue->rear + 1) % queue->queueSize == queue->front) { printf("队列已满,无法进队列\n"); return; } queue->data[queue->rear] = temp; queue->rear = (queue->rear + 1) % queue->queueSize; } //出队列 BiTNode* deQueue(Queue* queue) { if(queue->rear == queue->front) { printf("此时队列是空的,无法出队列\n"); return NULL; } BiTNode* deleteData = queue->data[queue->front]; // printf("此时出队列的是%d", deleteData); queue->front = (queue->front + 1) % queue->queueSize; return deleteData; } int QueueEmpty(Queue queue) { if(queue.front == queue.rear) { return 1; // 空 } return 0; } // 层次读边法创建二叉树 void create_BiTree(BiTree* tree) { Queue queue; initQueue(&queue); *tree = NULL; char fa[20], ch[20]; int flag = 0; cin >> fa >> ch >> flag; BiTNode* cur; // 当前节点放在外面定义 BiTNode* head; // 头 while(strcmp(ch, "#") != 0) { cur = new BiTNode; strcpy(cur->data, ch); cur->left = cur->right = NULL; enQueue(&queue, cur); if(strcmp(fa, "#") == 0) { *tree = cur; } else { // 取队头 head = getHead(queue); while(strcmp(head->data, fa) != 0) { deQueue(&queue); head = getHead(queue); } if(flag == 0) { head->left = cur; } else { head->right = cur; } } cin >> fa >> ch >> flag; } } // 层次遍历 void layer(BiTree tree) { Queue queue; initQueue(&queue); if(tree) enQueue(&queue, tree); BiTNode* temp; while(!QueueEmpty(queue)) { temp = deQueue(&queue); printf("%s ", temp->data); //访问节点 if(temp->left) enQueue(&queue, temp->left); if(temp->right) enQueue(&queue, temp->right); } } // 求二叉树宽度 int cal_width(BiTree tree) { Queue queue; initQueue(&queue); if(tree) enQueue(&queue, tree); int level = queue.rear; int width = queue.rear - queue.front; BiTNode* cur; while(!QueueEmpty(queue)) { cur = deQueue(&queue); if(cur->left) enQueue(&queue, cur->left); if(cur->right) enQueue(&queue, cur->right); if(level == queue.front) { level = queue.rear; if(width < queue.rear - queue.front) { width = queue.rear - queue.front; } } } return width; } int main() { BiTree tree; createBiTree(&tree); // preOrder(tree); int width = cal_width(tree); printf("width: %d\n", width); system("pause"); return 0; }
-
-
求二叉树的深度
思路:
- 层序遍历,使用level变量标志是不是需要更新深度
- 当
level == queue.front
时需要更新level为队尾,以及说明此时已经进入了下一深度depth++
代码实现:
// 求二叉树深度 int cal_depth(BiTree tree) { Queue queue; initQueue(&queue); if(tree) enQueue(&queue, tree); int level = queue.rear; int depth = 0; BiTNode* cur; while(!QueueEmpty(queue)) { cur = deQueue(&queue); if(cur->left) enQueue(&queue, cur->left); if(cur->right) enQueue(&queue, cur->right); if(level == queue.front) { level = queue.rear; depth++; } } return depth; }
树与森林
树
-
双亲表示法(方便查找双亲但是不方便查找孩子)
typedef struct { int data; // 树节点的数据 int parent; // 记录双亲节点的下标 }PNode; typedef struct { PNode tree[MAXLEN]; int n; // 节点数 int root; //根节点的下标 }PTree;
-
孩子链表表示法(方便查找孩子但是不方便查找双亲)
结构:
// 孩子链表(链式存储) // 孩子节点 typedef struct CTNode { int child; // 孩子节点在头节点数组中位置 struct CTNode* next; // 指向下一个孩子位置 }CTNode, *ChildPtr; // 双亲节点 typedef struct { int data; // 存储的数据 ChildPtr link; // 存储指向第一个孩子节点的指针 }CTBox; // 树类型 typedef struct { CTBox node[MAXSIZE]; int n; // 节点数 int root; // 根节点所在位置 }CListTree;
创建孩子链表
// 创建孩子链表 void createPTree(CListTree* tree) { printf("请输入节点数:"); int n = 0; int fa = 0; int ch = 0; scanf("%d",&n); tree->n = n; // 节点数 tree->root = 0; // 第一个位置就是根节点 // 初始化树 for(int i = 0; i < n; i++) { scanf("%d", tree->node[i].data); tree->node[i].link = NULL; } // 读边 scanf("%d%d", &fa, &ch); // 输入的是下标 while(ch != -1) { CTNode* cur = new CTNode; // 初始化一个孩子节点 cur->child = ch; cur->next = NULL; // 头插法 cur->next = tree->node[fa].link; tree->node[fa].link = cur; scanf("%d%d", &fa, &ch); } }
-
双亲孩子链表(其实只是在孩子链表做了一点改动,双亲节点中加入了记录双亲节点的下标)
// 双亲节点 typedef struct { int data; // 存储的数据 int parent; // 表示双亲节点的下标 ChildPtr link; // 存储指向第一个孩子节点的指针 }CTBox;
森林(都是基于孩子兄弟链表)
孩子兄弟链表结构体
// 孩子兄弟链表的数据类型
typedef struct CSNode
{
int data;
struct CSNode* firstChild;
struct CSNode* nextSibling;
}CSNode, *CSTree;
森林的遍历
先序遍历:
思路:若森林不空,
-
则访问森林中第一棵树的根结点;
-
先序遍历森林中第一棵树的子树森林;
-
先序遍历森林中(除第一棵树之外)其余树构成的森林
相当于 依次从左至右对森林中的每一棵树进行先序遍历
森林的先序遍历 === 二叉树的先序遍历
代码实现:
// 树的先根遍历 --> 二叉树的先序遍历(递归)
void preOrderTree(CSTree tree)
{
if(tree)
{
printf("%d ", tree->data);
preOrderTree(tree->firstChild);
preOrderTree(tree->nextSibling);
}
}
中序遍历:
思路: 若森林不空,
-
则中序遍历森林中第一棵树的子树森林;
-
访问森林中第一棵树的根结点;
-
中序遍历森林中(除第一棵树之外)其余树构成的森林
相当于 依次从左至右对森林中的每一棵树进行后序遍历
森林的后序遍历 === 二叉树的中序遍历
代码实现:
// 树的后根遍历 --> 二叉树的中序遍历(递归)
void postOrderTree(CSTree tree)
{
if(tree)
{
postOrderTree(tree->firstChild);
printf("%d ", tree->data);
postOrderTree(tree->nextSibling);
}
}
对应关系图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ixUG93Vo-1655261476335)(https://note.youdao.com/yws/res/3643/WEBRESOURCE008289f639955d4a3654084e54969afc)]
树与森林的基本操作
-
读边法创建孩子兄弟链表(树)
// 创建孩子兄弟链表模拟树->读边法 void createTree(CSTree* tree) { Queue queue; initQueue(&queue); *tree = NULL; int fa = 0; int ch = 0; scanf("%d%d", &fa, &ch); CSNode* head; // CSNode* temp = new CSNode; CSNode* temp; CSNode* cur; while(ch != -1) { cur = new CSNode; cur->data = ch; // 设置为叶子节点 cur->firstChild = NULL; cur->nextSibling = NULL; enQueue(&queue, cur); if(fa == -1) { // 说明此时是根节点 *tree = cur; } else { // 获取队头元素 head = getHead(queue); // 查询双亲节点 while(head->data != fa) { deQueue(&queue); head = getHead(queue); } // 出循环之后说明此时队头就是双亲节点 // 判断双亲节点此时的第一个节点是不是存在 // 不存在就放 存在就放在他的兄弟节点 if(!(head->firstChild)) { head->firstChild = cur; temp = cur; } else { temp->nextSibling = cur; temp = cur; } } scanf("%d%d", &fa, &ch); } }
其中用到了链队列进行模拟
typedef struct QNode { CSNode* ptr; struct QNode* next; }QNode, *QueuePtr; typedef struct { QueuePtr front; QueuePtr rear; }Queue; //初始化 int initQueue(Queue* q) { q->front = q->rear = new QNode; if(q->front == NULL) return 0; q->front->next = NULL; printf("初始化成功!\n"); return 1; } //进队列 void enQueue(Queue* q, CSNode* temp) { QNode* newNode = new QNode; newNode->ptr = temp; newNode->next = NULL; //尾插 q->rear->next = newNode; q->rear = newNode; printf("此时进队列的是%d\n", temp->data); } //出队列 void deQueue(Queue* q) { if(q->front == q->rear) { printf("此时队列为空,无法出队\n"); return; } QNode* cur = q->front->next; CSNode* delPtr = cur->ptr; q->front->next = cur->next; if(q->rear == cur) { //说明此时只有一个元素 q->rear = q->front; } free(cur); printf("此时出队列的是%d\n", delPtr->data); } CSNode* getHead(Queue queue) { QNode* cur = queue.front->next; printf("此时队头元素是:%d\n", cur->ptr->data); return cur->ptr; }
输入测试数据
-1 1 1 2 1 3 1 4 3 5 3 6 6 -1
-
求树的深度
思路:
从
firstchild
进入下一层递归,深度加1从
nextsibling
进入下一层递归,深度不变代码实现:
// 求树的深度 int cal_Depth(CSTree tree) { if(tree == NULL) return 0; else { int depth1 = cal_Depth(tree->firstChild); int depth2 = cal_Depth(tree->nextSibling); return depth1 + 1 > depth2 ? depth1 + 1 : depth2; } }
-
查找树的节点
-
查找第一个出现的节点返回
// 查找算法(查找单个) CSNode* searchTree(CSTree tree, int data) { if(!tree) return NULL; else { CSNode* temp; if(tree->data == data) return tree; else if(temp = searchTree(tree->firstChild, data)) { return temp; } else { return searchTree(tree->nextSibling, data); } } }
-
查找多个相同节点 以数组的形式返回
// 查找多个相同节点 // res表示返回的结果数组 n表示的是找到的个数 void preSearchTree(CSTree tree, int data, CSTree res[], int* n) { if(tree) { if(tree->data == data) { res[(*n)++] = tree; } preSearchTree(tree->firstChild, data, res, n); preSearchTree(tree->nextSibling, data, res, n); } }
-
-
输出根到叶子节点的路径
思路:先根遍历过程中,到达叶子时,输出一条路径,然后继续**沿着
nextsibling
**遍历,找下一条路径.叶子节点的判断条件就是:
p->firstChild == NULL
代码中的栈使用的是顺序栈,测试数据仿照创建树时的数据
代码实现:
// 输出根到叶子路径的算法描述 void LeavePath(CSTree tree, SqStack* stack) { if(tree) { push(stack, tree->data); if(!(tree->firstChild)) { // 找到叶子节点就输出路径 StackTravel(*stack); } else { // 一直递归到叶子节点 CSNode* p; for(p = tree->firstChild; p; p = p->nextSibling) { LeavePath(p, stack); // 找完之后就删除路径的最后一个节点 pop(stack); } } } }
-
树的节点的插入
思路:
- 找到双亲节点
- 双亲节点第一个孩子如果是空,新插入的节点就作为第一个叶子节点
- 如果不为空,就找到兄弟节点的最后一个插入
代码实现:
// 树的节点的插入 // fa是双亲节点数据 ch是子节点数据 void insertTree(CSTree* tree, int fa, int ch) { CSTree p = NULL; CSTree q, s; p = searchTree(*tree, fa); // 查找双亲节点 if(p) { // 找到了双亲节点 s = new CSNode; s->data = ch; s->firstChild = s->nextSibling = NULL; if(!(p->firstChild)) { // 如果双亲节点没有孩子节点,那就作为第一个叶子结点 p->firstChild = s; } else { // 不然就找到兄弟节点的最后一个插入到后面 q = p->firstChild; while(q->nextSibling) { q = q->nextSibling; } q->nextSibling = s; } } }
-
树的节点的删除
思路:
- 查找双亲节点和孩子节点,少一个都不可以
- 删除的是第一个孩子,那就重新链接左右子树
- 删除的不是第一个孩子,那就继续寻找该节点的前一个节点
代码实现:
// 后序遍历删除每一个节点 释放删除树每个节点的空间 void postDelTree(CSTree tree) { if(tree) { postDelTree(tree->firstChild); postDelTree(tree->nextSibling); free(tree); } } void Delete(CSTree p, CSTree f) { // 从树中删除以节点p为根的子树,f是p的双亲,并重接它的左子和右子树 if(f->firstChild == p) { f->firstChild = p->nextSibling; p->nextSibling = NULL; postDelTree(p); } // p和f是兄弟关系 if(f->nextSibling == p) { f->nextSibling = p->nextSibling; p->nextSibling = NULL; postDelTree(p); } } // 树的节点的删除 void DeleteTree(CSTree* tree, int fa, int ch) { // fa是双亲, ch是孩子, 删除以ch为根的子树 CSNode* pfa; CSNode* pch; if(fa == -1) { // 删除的是整个树 postDelTree(*tree); *tree = NULL; return; } else { pfa = searchTree(*tree, fa); // 查找双亲节点 pch = searchTree(*tree, ch); // 查找孩子节点 if(pfa == NULL || pch == NULL) { printf("输入的数据不对,无法删除\n"); return; } else { if(pfa->firstChild != pch) { // 不是第一个孩子 // 找删除节点的前一个节点 pfa = pfa->firstChild; while(pfa) { if(pfa->nextSibling == pch) break; pfa = pfa->nextSibling; } } // 如果此时pch是pfa的第一个孩子节点,那么pfa就是双亲,pch就是孩子 // 如果此时不是,那么pfa就是删除节点的前一个兄弟节点 Delete(pch, pfa); } } }
-
凹入表显示数据
// 凹入表显示 void dispTree(CSTree tree, int level) { int len, i, n, k; if(tree) { len = strlen(tree->data); for(i = 1; i < level; i++) { printf(" "); } printf("%s", tree->data); printf("+"); for(k = i + len; k < 70; k++) { printf("-"); } printf("\n"); dispTree(tree->firstChild, level + 4); dispTree(tree->nextSibling, level); } }