二叉树的基础操作
二叉树
一:创建以及初始化赋值:
struct BiTree { char data; BiTree* lchild; BiTree* Rchild; }; //初始化 BiTree* create() {//先序创建二叉树 BiTree *T; char a; cin >> a; if (a == '.')return NULL; else { T = (BiTree*)malloc(sizeof(BiTree)); T->data = a; T->lchild = create(); T->Rchild = create(); } //若想中序或后序创建,则只需改变函数中 //T->data=a;T->lchild=create();T->rchild=create();这三条语句的顺序 //先给T->data=a在先的话是先序,在中间的话是中序,在最后的话是后序。 }
二:二叉树遍历方法:
/* 先序的遍历顺序是根节点->左子树->右子树。 中序的遍历顺序是左子树->根节点->右子树。 后序的遍历顺序是右子树->根节点->左子树。 层序的遍历顺序是按层顺次遍历。 先序、中序、后序的代码基本相同 */ void pre(BiTree *root) { BiTree* p = root; if (p) { cout << p->data; pre(p->lchild); pre(p->Rchild); } } void mid(BiTree* root) { BiTree* p = root; if (root) { mid(p->lchild); cout << p->data; mid(p->Rchild); } } void post(BiTree* root) { BiTree* p = root; if (p) { post(p->Rchild); post(p->lchild); cout << p->data; } }
三:二叉树插入操作:
//插入操作,没有重复的元素 //插入法1: BiTree* BSTInsert(BiTree* L, int key) { if (!L) {//若是一个空表,那么就创建最开始的 L = (BiTree*)malloc(sizeof(BiTree)); L->data = key; L->lchild = L->Rchild = NULL; } else {//若不是空表就按照二叉树组成规则遍历插入 if (L->data < key) L->Rchild = BSTInsert(L->Rchild, key); else if (L->data > key) L->lchild = BSTInsert(L->lchild, key); } return L; } //插入法2:整列树的插入 //int data[9]={8,3,10,13,14,1,6,7,4}; BiTree* Insert(BiTree* L, int data[], int n) { int i; for (int i = 0; i < n; i++) { L = BSTInsert(L, data[i]); } return L; }
四:二叉树查询:
//查询元素位置: /* 查询法1: 寻找最小、最大元素的方法是相同的。根据二叉树的特点, 若左右子树不为空,则根节点的值一定大于左子树的值、 小于右子树的值,那么这棵树的根节点的最左子树和最右子树即为最小、最大元素 */ BiTree* Searchmin(BiTree* L) {//查询最小元素或最大值 if (L == NULL) { return NULL; } if (L->lchild == NULL) { return NULL; } else return Searchmin(L->lchild);//使用递归,不断搜索左子树。同理也可以搜索右子树来寻找最大值 } /* 查询法2: 而查找特定元素也利用了排序树的特点。将结点的值与待查值做比较, 若结点值大,则说明待查值在结点的左子树;若结点值小, 则说明待查值在结点的右子树,然后递归或不递归地查询结点的左子树或右子树即可。 */ BiTree* Search_1(BiTree* L, int key) { if (L == NULL) return NULL; if (key > L->data) return Search_1(L->Rchild, key);//查找右子树 else if (key < L->data) return Search_1(L->lchild, key);//查找左子树 else return L;//找到则返回指针 } // 查询法3:在此基础上,我们可以省掉递归过程,用while循环来实现结点的遍历。非递归查找代码如下: BiTree* Search_2(BiTree* L, int key) {//重要:不使用递归来查找 BiTree* p = L; while (p) { if (p->data == key) return p; else p = (p->data > key) ? p->lchild : p->Rchild;//判断下一个寻找的是左子树还是右子树 } return NULL;//找不到就返回空 }
五:二叉树删除节点:
/*删除节点: 一、要求 1.没子节点时,直接删除 2.有子节点时,删除该节点。 如果该节点有左节点,则左节点为父节点,右节点不变,如果没只有左节点或者右节点,则直接提升为父节点。 3.若左右子节点都有的话,那么就找右子树的最左子树p或者寻找左子树的最右子树,然后把p放到删除的那个节点的位置 */ void DeleTree(BiTree* T, int data) { //1.//寻找需要删除的点 BiTree* p = T, * f = T, *temp; while (p) { if (p->data == data) break; else if (data > p->data) { f = p; p = p->Rchild; } else if (data < p->data) { f = p; p = p->lchild; } }/*找到后,p为目标结点,f为其父结点*/ //正式开始删除节点操作 if (!p) //找不到 printf("错误,无此数据"); //以下为找到的情况 else if (p->lchild == NULL) //情况1:p无左子树 { if (f->Rchild = p) //p为f的右子树 f->Rchild = p->Rchild; //直接将p的右子树连接在f的右子树上 else if (f->lchild = p) //p为f的左子树 f->lchild = p->Rchild; //直接将p的右子树连接在f的右子树上 free(p); } else if (p->Rchild == NULL) //情况2:p无右子树 { if (f->Rchild = p) //p为f的右子树 f->Rchild = p->lchild; //直接将p的左子树连接在f的右子树上 else if (f->lchild = (p)) //p为f的左子树 f->lchild = (p)->lchild; //直接将p的左子树连接在f的右子树上 free(p); } else if ((p)->lchild == NULL && (p)->Rchild == NULL)//情况3:p无左子树和右子树 { if ((f)->Rchild = (p)) //p为f的右子树 (f)->Rchild = NULL; //f的右子树直接置空 else if ((f)->lchild = (p)) //p为f的左子树 (f)->lchild = NULL; //f的右子树直接置空 free(p); } else if ((p)->lchild != NULL && (p)->Rchild != NULL)//情况4:p有左子树和右子树 { /* 注意:这里是可以寻找被删除节点的左子树的最右节点(下面的方法就是使用这个的) 还可以使用右子树的最左节点 */ if ((f)->Rchild == (p)) //p为f的右子树 { temp = (p)->lchild; //temp为p的左子树 while (temp->Rchild) //在temp的右支寻找空位,注意,在左子树那就是找最右元素,在右子树,就找最左元素 temp = temp->lchild; //注意:要是判断没有对应方向的子树就结束循环 temp->Rchild = (p)->Rchild; //将p的右子树连接在temp的右子树上,这一步是不是很难理解,循环后的节点将会代替删除的节点,所以右子树会变成被删除的的右子树 (f)->Rchild = (p)->lchild; //再将p的左子树连接在f的右子树上,这一步也是,原本被删除的节点是父节点的右子树,所以这时候会把寻找到的中序后继节点的左子树分配给 free(p); } else if ((f)->lchild = (p)) //注意:这种虽然不是同一种节点,但是本质上想要删除都是需要通过右边的中级后节点来代替实现删除 { temp = (p)->lchild; while (temp->Rchild) temp = temp->lchild; temp->Rchild = (p)->Rchild; (f)->lchild = (p)->lchild; free(p); } } }