数据结构与C语言实现(三)——树(上):二叉树

来源:互联网 发布:弗洛伊德算法 例子 编辑:程序博客网 时间:2024/06/05 07:20
/*查找1.静态查找:集合中的记录是固定的,没有插入和删除。2.动态查找:集合中的记录是动态变化的,要执行插入和删除的操作等。*/ #include <stdio.h>#include <stdlib.h>#define MAXSIZE 100typedef struct LNode *List;struct LNode{int data[MAXSIZE];int Length;//数组长度};/*哨兵查找法指的是,将要查找的值K放在数组的一端(比如说data[0],然后从另一端开始查找,直到碰到K的时候退出)。*/int SequentialSearch(List Tb1, int K){int i; Tb1->data[0] = K;//设置哨兵for (i = Tb1->Length; Tb1->data[i] != K; i++);//这个循环只有两个退出条件1.找到K2.找到末尾的Kreturn i;}/*二分查找要求:1.顺序存放2.数组,不能用链表*/int BinarySearch(List Tb1, int K){int left, right, mid, NoFound = -1;left = 1;right = Tb1->Length;while (left <= right){mid = (left + right) / 2;if (Tb1->data[mid] > K){right = mid - 1;//如果K在mid的左侧,那么右边的游标就要变成mid - 1}else if (Tb1->data[mid] < K){left = mid + 1;//如果K在mid的右侧,那么左边的右边就要变成mid + 1}else return mid;//如果恰好等于,那就说明找到了这样的下标}return NoFound;//如果没找到,那么久返回-1}  

由二分查找法,我们引入了二叉树的概念。对于二叉树而言,最重要的是遍历,我们一共有三种遍历的方式,先序,中序和后序,在这里我们分别给出了递归和非递归的两种实现方案。

由于能力有限,还没办法自己独立完成非递归的后序历遍程序。

//对于链表实现的二叉树的先序遍历#include <stdio.h>#include <stdlib.h>#include <iostream>struct BNode{struct BNode *left;struct BNode *right;int data;};typedef struct BNode * BinTree;struct SNode{struct SNode *next;BNode* tree;};typedef struct SNode * Stack;struct LNode{BinTree tree;struct LNode * next;};typedef struct LNode* List;struct QNode{List front;List rear;};typedef struct QNode *Queue;/*先序遍历又叫先根遍历,先根后左再右1.访问根节点2.先序历遍其左子树3.先序历遍其后子树*/void PreOrderTraversal(BinTree BT){if (BT){printf("%d", BT->data);PreOrderTraversal(BT->left);PreOrderTraversal(BT->right);}}/*中序遍历左根右*/void PostOrderTraversal(BinTree BT){if (BT){PreOrderTraversal(BT->left);printf("%d", BT->data);PreOrderTraversal(BT->right);}}/*后序遍历左右根*/void InOrderTraversal(BinTree BT){if (BT){PreOrderTraversal(BT->left);PreOrderTraversal(BT->right);printf("%d", BT->data);}}/********************************堆栈的相关代码*******************************************/Stack CreatStack(){Stack S = (Stack)malloc(sizeof(struct SNode));S->next = NULL;return S;}int SIsEmpty(Stack S){if (S->next == NULL)//空1,非空0return 1;elsereturn 0;}void Push(Stack S, BinTree T){Stack new_s = (Stack)malloc(sizeof(struct SNode));new_s->next = S->next;S->next = new_s;new_s->tree = T;}BinTree Pop(Stack S){Stack new_s = S->next;BinTree new_t = new_s->tree;S->next = new_s->next;free(new_s);return new_t;}/********************************堆栈的相关代码*******************************************//********************************队列的相关代码*******************************************/Queue CreatQueue(){Queue Q = (Queue)malloc(sizeof(struct QNode));Q->rear = Q->front = NULL;return Q;}int QIsEmpty(Queue Q)//非空0,空1{if (Q->front == Q->rear) return 1;else return 0;}void AddQ(Queue Q, BinTree T){List new_list = (List)malloc(sizeof(struct LNode));new_list->next = NULL;new_list->tree = T;Q->rear->next = new_list;Q->rear = Q->rear->next;}BinTree ExitQ(Queue Q){List FrontCell = Q->front;BinTree new_tree = (BinTree)malloc(sizeof(struct BNode));if (FrontCell->next == NULL)return NULL;if (Q->front == Q->rear)Q->front = Q->rear = NULL;else Q->front = FrontCell->next;new_tree = FrontCell->tree;free(FrontCell);return new_tree;}/********************************队列的相关代码*******************************************///用堆栈实现中序非递归遍历void StackInOrderTraversal(BinTree BT){BinTree T = BT;Stack S = CreatStack();while (T || !SIsEmpty(S)){while (T)//如果还有左节点,那就继续向左遍历{Push(S, T);//将数中的一个节点压栈至堆栈中。第一次碰到节点T = T->left;//向左移动}if (!SIsEmpty(S))//S不为空时返回0,取反为1,继续循环。如果S为空,返回1,取反为0,跳出循环。{T = Pop(S);//将一个节点Pop出,第二次碰到节点printf("%5d", T->data);T = T->right;//向右走,转向右子树}}}//用堆栈实现先序非递归遍历void StackInOrderTraversal(BinTree BT){BinTree T = BT;Stack S = CreatStack();while (T || !SIsEmpty(S)){while (T)//如果还有节点,那就继续向左遍历{Push(S, T);//将数中的一个节点压栈至堆栈中。第一次碰到节点printf("%5d", T->data);T = T->left;//向左移动}if (!SIsEmpty(S)){T = Pop(S);//将一个节点Pop出,第二次碰到节点T = T->right;//向右走,转向右子树}}}//用堆栈实现后序非递归遍历/*第二种思路:要保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点P,先将其入栈。如果P不存在左孩子和右孩子,则可以直接访问它;或者P存 在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点。若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证了 每次取栈顶元素的时候,左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。*///void postOrder3(BinTree *root)     //非递归后序遍历//{//stack<BinTree*> s;//BinTree *cur;                      //当前结点 //BinTree *pre = NULL;                 //前一次访问的结点 //s.push(root);//while (!s.empty())//{//cur = s.top();//if ((cur->lchild == NULL&&cur->rchild == NULL) ||//(pre != NULL && (pre == cur->lchild || pre == cur->rchild)))//{//cout << cur->data << " ";  //如果当前结点没有孩子结点或者孩子节点都已被访问过 //s.pop();//pre = cur;//}//else//{//if (cur->rchild != NULL)//s.push(cur->rchild);//if (cur->lchild != NULL)//s.push(cur->lchild);//}//}//}//用队列实现层序遍历/*层序遍历:1.从队列中去除一个元素2.访问该元素所指向的节点3.如果该元素梭指向的左右孩子节点非空,那就把左右孩子的指针顺序入列*/void LevelOrderTraversal(Queue Q,BinTree BT){AddQ(Q, BT);while (!QIsEmpty(Q))//当队列为空时,说明所有元素都已经被遍历完毕{//首先我们先取出一个树BinTree new_t = ExitQ(Q);printf("%d", new_t->data);//然后把左儿子放进来,如果没左儿子就放右儿子,如果两个儿子都没有,就进大循环if (new_t->left != NULL){AddQ(Q, new_t->left);}if(new_t->right)AddQ(Q, new_t->right);}}//二叉树的应用:输出二叉树中的叶子节点void PreOrderPrintLeaves(BinTree BT){if (BT->left == NULL && BT->right == NULL)//如果没有左儿子也没有右儿子,就说明是叶节点printf("%d  ", BT->data);PreOrderPrintLeaves(BT->left);PreOrderPrintLeaves(BT->right);}//二叉树的应用:求二叉树的高度。//二叉树的高度为左右子树中较高的那一个+1int PostOrderGetHeight(BinTree BT){int HL, HR, MaxH;if (BT){HL = PostOrderGetHeight(BT->left);HR = PostOrderGetHeight(BT->right);MaxH = (HL > HR) ? HL : HR;return(MaxH + 1);}else{return 0;}}

这边是一个小demo,用于判断两个数是否为同构树。

#include <stdio.h>#include <stdlib.h>#define MaxTree 100#define ElementType char#define Tree int#define Null -1struct TreeNode{ElementType Element;Tree Left;Tree Right;}T1[MaxTree],T2[MaxTree];Tree BuildTree(struct TreeNode T[]){int N, cr, cl,j,Root;int check[MaxTree];scanf("%d\n", &N);if (N){for (int i = 0; i < N; i++) check[i] = 0;//类似于flag[3],3是否出现过for (int i = 0; i < N; i++){scanf("%c %c %c\n", &T[i].Element, &cl, &cr);//将自己和左右儿子输入if (cl != '-')//如果有左儿子{T[i].Left = cl - '0';//把左儿子的下标拿出check[T[i].Left] = 1;//左儿子的下标出现过}else{T[i].Left = Null;//如果没有做儿子,那么赋值为-1}if (cr != '-'){T[i].Right = cr - '0';check[T[i].Right] = 1;}else{T[i].Right = Null;}}for (j = 0; j < N; j++){if (!check[j]) break;//如果遇到没出现过的下标,就弹出j的数据 ,为根节点}Root = j;}return Root;}int Isomorphic(Tree R1, Tree R2)//R1,R2分别是树1和树2的根{if ((R1 == Null) && (R2 == Null)) return 1;if (((R1 == Null) && (R2 != Null)) || ((R1 != Null) && (R2 == Null))) return 0;//一个空,一个不空if (T1[R1].Element != T2[R2].Element)return 0; //根不同if ((T1[R1].Left == Null) && (T2[R2].Left == Null))//左都为空,相同{return Isomorphic(T1[R1].Right, T2[R2].Right);//判断右边是否相同}if (((T1[R1].Left != Null) && (T2[R2].Left != Null)) && ((T1[T1[R1].Left].Element) == (T2[T2[R2].Left].Element)))//左都不为空且左儿子相等return ((Isomorphic(T1[R1].Left, T2[R2].Left)) && (Isomorphic(T1[R1].Right, T2[R2].Right)));//判断左孙子是否相等,在判断右算子是否相等elsereturn((Isomorphic(T1[R1].Left, T2[R2].Right)) && (Isomorphic(T1[R1].Right, T2[R2].Left)));}

下面主要考虑了对二叉树的动态寻找,即查找,插入,删除。

#include <stdio.h>#include <stdlib.h>struct TNode{int data;struct TNode* right;struct TNode* left;};typedef struct TNode* BinTree;BinTree Find(int X, BinTree BST){if (!BST) return NULL;if (X > BST->data)return Find(X,BST->right);else if (X < BST->data)return Find(X, BST->left);else return BST;}//尾递归都可以通过循环实现//用循环实现BinTree IterFind(int X,BinTree BST){while (BST){if (X > BST->data)BST = BST->right;else if (X < BST->data)BST = BST->left;elsereturn BST;}return NULL;}//用迭代实现查找最大BinTree FindMax(BinTree BST){BinTree Tmax;while (BST){Tmax = BST;BST = BST->right;}return Tmax;}//用递归实现查找最小BinTree FindMin(BinTree BST){if (BST == NULL) return NULL;else if (BST->left != NULL){FindMin(BST->left);}elsereturn BST;}//用递归实现插入BinTree Insert(int X, BinTree BST)//最后返回的是树的第一个{if (!BST)//如果树中没有元素,则插入{BST = (BinTree)malloc(sizeof(struct TNode));BST->right = NULL;BST->left = NULL;BST->data = X;}else if (X > BST->data){BST->right = Insert(X, BST->right);//直到最后,创建一个节点放在现在的BST的右边。}else if (X < BST->data){BST->left = Insert(X, BST->left);//注意,这里不能用return,因为要把这个节点放置到前一个的下一个}else{;}return BST;/*注意,这个递归有点意思。1.首先从外向内。一层一层判定应该插入到哪里去。2.然后从内向外。一层一层的将自己赋值为上一个的子节点。3.最后返回的是这个树的根节点。 */}/*二叉树搜索树的删除如果要删除的节点有两个儿子1.可以取右子树中的最小元素2.可以用左子树中的最大元素代替*/BinTree Delete(int X, BinTree BST){BinTree Tmp;if (!BST)printf("Error:要删除的元素未找到");else if (X < BST->data)//找X所在位置{BST->left = Delete(X, BST->left);}else if (X > BST->data){BST->left = Delete(X, BST->right);//为什么有这一句赋值,从内向外rreturn,一直return到根节点。如果这里不赋值而是用return,会导致找到符合的就返回}else//找到了X所在的位置{if (BST->left && BST->right)//如果两个儿子都有{Tmp = FindMin(BST->right);//那么就从右子树中寻找到最小值BST->data = Tmp->data;//把右子树最小的数据给找到的X所在的节点BST->right = Delete(BST->data, BST->right);//把替换的那个给删除,删除的这个一定至多只有一个儿子,所以后面执行的是下面的}else{Tmp = BST;//如果只有一个儿子或者没有儿子if (!BST->left)//没有左儿子BST = BST->right;//把自己变成右儿子else if (!BST->right)BST = BST->left;//把自己变成左儿子free(Tmp);}}return BST;//返回的是根节点}

下一章介绍平衡二叉树的相关内容。

阅读全文
1 0