多杈树的邻接矩阵实现--插入、删除、前序遍历

  分支任意的树构造的方法主要有两种,邻接矩阵和兄弟链表。邻接矩阵主要用于输入的范围很小的情况,因为矩阵反应的是对应情况,比如树的每一个节点都给与对应顺序的编号的话,如果有100个输入,就需要一个100*100的数组来存储联接关系。

  链表的形式一般用于输入量很大的情况,可以很方便的适应。(需要仔细体会一下区别)

邻接矩阵的使用方法:

  邻接矩阵的行数代表父节点ID,每一行用来存储其各子节点ID。比如 0 节点下面添加了一个子节点1的话,就需要在数组的第0行的第Tree(邻接数组名)[0]的位置上写入1.这里涉及到下面的小技巧中的每一行的第0位的使用。

邻接矩阵的使用小技巧:

  邻接矩阵的每一行的第0位放当前父亲节点的子节点的数量,这样在遍历的时候可以很方便的控制循环。而且在插入新的节点的时候第0位里面的数字就是当前要放入数据的数组的下标(这里的方法很像之前学过的计数排序,把计数值从前一位累加到后一位,这样对应位置里面放的数值就是当前数据应该放在输出数组中的位置。计数排序一会可以再写一个随笔记录一下)。

用法举例:

Tree[fatherID][0]++;//这里是放的当前父亲节点的子节点的数量
Tree[fatherID][Tree[fatherID][0]] = childID;//把当前要插入的子节点放在它应该对应的数组位置上

如果单单是插入的话只需要一个邻接矩阵存储父节点对应的子节点的关系就可以了。但是如果要删除的话,我们往往只是给定当前要删除的节点的标号。这样的话我们没办法找到他的父节点(除非从root节点开始遍历我们刚才生成的邻接数组),这时候我们又需要一个数组来记录子对于父的连接关系(这里就有点像双向链表了,双向链表要实现的也是类似的功能,如果要删除当前节点,只要通过node->pre找到他的父亲节点,然后通过指针操作,把父亲节点的->next指向下一个node,把下一个node的->pre指向前面一个node即可)。于是我们创建一个Father[]数组来存储每一个节点的父亲节点标号。因为所有的节点肯定只有一个父亲(可以有多个孩子,但是只可能有一个父亲,这里仔细体会一下)。

int FatherID[MAX_ID];//存放每一个节点的父节点

我们在插入的时候要把fatherID放到FatherID数组的对应位置上,这样只要知道子节点和FatherID[]数组就可以知道当前的子节点的父亲节点是谁。

对应代码:

void insertNode(int fatherID,int childID){

    Tree[fatherID][0]++;
    Tree[fatherID][Tree[fatherID][0]] = childID;

    FatherID[childID] = fatherID;

}

所以在删除的时候我们要做的就是通过子节点和FatherID[childID] 找到他的父亲节点,然后维护自己创建的数据结构。这里面只有两个数据结构,1.FatherID[childID]  2.int Tree[MAX_ID][MAX_ID]; 因为我们只是通过FatherID数组来找父亲节点,所以嗯。。(纠结了一下)也可以把FatherID对应子节点位置的数组置零,主要是要更改Tree数组的值,

1.要更改Tree[fatherID][0]的值,子节点少了一个。需要把子节点的数目减1

2.要更改Tree[fatherID][Tree[fatherID][0]]的值,子节点已经删去了。需要把对应位置的对应关系删去,即把Tree[fatherID][Tree[fatherID][0]]清零

for(int i=1;i<MAX_ID;i++){
        if(Tree[fatherId][i]==Id){
            Tree[fatherId][i]=0;
            Tree[fatherId][0]--;
            return;
        }
    }

好了进行到最后一个步骤,需要前序遍历生成的树。

刚开始我总是想着要用FatherID这个数组,因为这个数组初始化的时候把对应位置初始化成了自己,这样在向上遍历的时候只要遍历到了自己的话就证明他是根节点。可以作为递归的出口(这里也不是想的很明白。。。汗-_-||等有时间再比较一下数组构建的二叉树的前序,然后更新一下)

但是用当前节点子节点的个数作为递归变量会更好一点(有点像那道给出前序和中序求后序遍历的题目一样,每一次递归的传入量就是当前的前序和中序,用长度来控制传入的前序和中序的字符串,根据每一次的前序提供的根再做下一次的划分,直到当前中序的长度为1,证明不能向下分割了,就是递归的出口)

所以我先把根打印出来,然后对于替换当前的父节点位置,和父节点下面的子节点的个数,如果子节点的个数为零的时候就return。

void preorder(int fatherID,int n){//父亲节点有几个根节点

    if(n==0){return;}

    for(int i=1;i<n+1;i++){
        printf("%d ",Tree[fatherID][i]);//root
        preorder(Tree[fatherID][i],count(Tree,Tree[fatherID][i]));//从前到后面遍历每一个节点
    }

}

哦,count函数就是为了计算当前的节点下面有几个子节点:利用了Tree[][]数组。(这里面特别骚气的循环是和金老师学的。这样可以少定义一个ret遍历,而且可以少一步传值的过程)

int count(int data[MAX_ID][MAX_ID],int fatherID){
    int i=1;
    for(;data[fatherID][i]!=0;i++){
        ;
    }
    return i-1;
}

最终的代码:

#include <stdio.h>
#define MAX_ID 20
int Tree[MAX_ID][MAX_ID];//存放对应关系
int FatherID[MAX_ID];//存放每一个节点的父节点

void insertNode(int fatherID,int childID){

    Tree[fatherID][0]++;
    Tree[fatherID][Tree[fatherID][0]] = childID;

    FatherID[childID] = fatherID;

}
void delNode(int Id){

    int fatherId = FatherID[Id];
    FatherID[Id] = 0;

    for(int i=1;i<MAX_ID;i++){
        if(Tree[fatherId][i]==Id){
            Tree[fatherId][i]=0;
            Tree[fatherId][0]--;
            return;
        }
    }

}
//计算当前父亲节点有几个子节点
int count(int data[MAX_ID][MAX_ID],int fatherID){
    int i=1;
    for(;data[fatherID][i]!=0;i++){
        ;
    }
    return i-1;
}
void preorder(int fatherID,int n){//父亲节点有几个根节点

    if(n==0){return;}

    for(int i=1;i<n+1;i++){
        printf("%d ",Tree[fatherID][i]);//root
        preorder(Tree[fatherID][i],count(Tree,Tree[fatherID][i]));//从前到后面遍历每一个节点
    }

}

void init(){
    for(int i=0;i<MAX_ID;i++){
        for(int j=0;j<MAX_ID;j++){
            Tree[i][j] = 0;
        }
    }
    for(int i=0;i<MAX_ID;i++){
        FatherID[i] = i;//指向它自己
    }
}
int main(){

    insertNode(0,1);
    insertNode(0,2);
    insertNode(0,3);
    insertNode(0,4);
    insertNode(1,5);
    insertNode(1,7);
    insertNode(2,8);
    insertNode(3,9);
    insertNode(4,10);
    insertNode(4,11);
    insertNode(11,12);
    insertNode(10,13);
    delNode(12);
    delNode(13);

    int num = count(Tree,0);
    printf("0 ");
    preorder(0,num);
    return 0;
}

递归的过程还是不是那么的清晰,要再好好想想。

时间: 2024-10-18 21:04:21

多杈树的邻接矩阵实现--插入、删除、前序遍历的相关文章

根据前序遍历和中序遍历树构造二叉树

样例: 给出中序遍历:[1,2,3]和前序遍历:[2,1,3]. 返回如下的树: 2 /  \ 1  3 我们知道前序遍历是中->左->右,中序遍历是左->中->右.因此根据前序遍历的第一个数,即为根节点,我们可以在中序遍历中找到根节点的左子树和右子树,同样递归在左子树中找到左子树的根节点和其左右子树,对右子树也一样.这样理清思路后代码就不难写出来了. /** * Definition of TreeNode: * class TreeNode { * public: * int

AVL树非递归插入删除思路

AVL树是最先发明的自平衡二叉查找树.在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下都是O(log n).增加和删除可能需要通过一次或多次树旋转来重新平衡这个树.AVL树得名于它的发明者G.M. Adelson-Velsky和E.M. Landis,他们在1962年的论文<An algorithm for the organization of information>中发表了它. 节点的平衡因子是它的左子树的高度减去它的右子树的

AVL树C++实现(插入,删除,查找,清空,遍历操作)

AVL.h文件代码 #pragma once #include<iostream> #include<stack> #include <assert.h> using namespace std; using namespace std; template<class T> struct AVLNode{ T data; AVLNode<T>*left, *right; int bf; AVLNode() :left(NULL), right(N

双向循环链表 初始化 插入 删除

#include <stdio.h> #include <stdlib.h> #define OK 1 #define ERROR -1 #define TRUE 1 #define FALSE -1 #define NULL 0 #define OVERFLOW -2 #define ElemType int #define Status int typedef int ElemType typedef int Status #define LEN sizeof(DuLNode)

顺序表 初始化 插入 删除 查找 合并 交换 判断为空 求长度

#include <stdio.h> #include <stdlib.h> #define OK 1 #define TRUE 1 #define ERROR -1 #define FALSE -1 #define OVERFLOW -2 #define ElemType int #define Status int typedef int ElemType typedef int Status #define LEN sizeof(SqList) #define MLC (Li

静态链表 初始化 定位 Malloc Free 插入 删除

#include <stdio.h> #include <stdlib.h> #define OK 1 #define TRUE 1 #define ERROR -1 #define FALSE -1 #define OVERFLOW -2 #define ElemType int #define Status int typedef int ElemType typedef int Status #define MAX_SIZE 1000;//表最大空间 /* //线性表的基本操

单链表 初始化 创建 头插法 尾插法 插入 删除 查找 合并 长度

#include <stdio.h> #include <stdlib.h> #define OK 1 #define ERROR -1 #define TRUE 1 #define FALSE -1 #define NULL 0 #define OVERFLOW -2 #define ElemType int #define Status int typedef int ElemType typedef int Status #define LEN sizeof(LNode) #

B树的查找、插入操作

B树的性质: 根节点至少有两个孩子 每个非根节点有[,M]个孩子 每个非根节点有[-1,M-1]个关键字,并且以升序排列 key[i]和key[i+1]之间的孩子节点的值介于key[i].key[i+1]之间 所有的叶子节点都在同一层 以下代码实现了B树的查找和插入操作,删除操作暂未实现 插入:先找到插入结点的位置,插入后判断关键字的个数是否小于M(M为阶数),小于返回,不小于进行分裂. 删除:任一关键字K的中序前趋(后继)必是K的左子树(右子树)中最右(左)下的结点中最后(最前)一个关键字.

红黑树插入删除节点过程分析 &amp;&amp; C代码实现

红黑树的插入和删除规则: 红黑树的五个性质 1.    每个节点要么是红的,要么是黑的 2.    根节点时黑色的 3.    每个叶节点(叶节点既指树尾端NIL指针或NULL节点)是黑色的 4.    如果一个节点时红的,那么它的两个儿子都是黑色的 5.    对每个节点,其到叶节点树尾端NIL指针的每一条路径都包含相同数目的黑节点 这里所说的"叶节点"或者"NULL节点",它不包含数据而只充当树在此结束的知识. 二叉树的左旋和右旋这里不再讲解 红黑树的插入操作: