树:
定义:一个树至多有一个根节点,每一个路径的终端都叫终端节点,也叫叶子结点。既不是根也不是叶子节点叫中间节点,节点与节点连接的线叫边。从底下往上看叫高度,从上往低看叫深度。
PS:树整体高度和深度取最大值。
度:看当前节点有n个子节点就叫n度。
二叉树(Binary Tree)
定义:树里的每个节点至多允许有两个节点。
满二叉树
定义:每一层都是满节点(2个节点)除最后一层的二叉树。
完全二叉树
定义:只允许最后一层有空缺,且空缺的方式从右向左的二叉树。
ps:满二叉树是完全二叉树的一种特殊形势。
平衡二叉树
定义:树中任意节点,左右子树高度差不超过1。
ps:完全二叉树一定是平衡二叉树。
排序二叉树
定义:左孩子的值比父亲小,右节点的值比父节点的大。
二叉树的性质
- $k$层满二叉树,叶子节点的个数是$ 2^{k-1} $
- $k$层满二叉树,总节点个数为$2^{k}-1$
- 对于任意一个二叉树而言,度为0的节点比度为2的节点少一个。($n_0=n_2+1$),PS:对于一个完全二叉树而言,度为1的节点至多有一个。
- 一个有$n$个节点的完全二叉树,它的深度是$ K=\lfloor log2^{n}\rfloor +1$(向下取整) 例如:3.33 取3
- 对一个有$n$个节点的完全二叉树,按照从上到下,从左到右的顺序从1开始,开始编号,
- $i=1$时是根节点。
- $2i<=n $ ,$i$有左子树,否则没有。
- $2i+1<=n$时,$i$有右子树,否则没有。
- 前序遍历:根左右
- 中序遍历:左根右
- 后序遍历:左右根
- 广度遍历(层序遍历):从上到下,从左到右123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126typedef struct Node{int nValue;struct Node *pLeft;struct Node *pRight;}BinaryTree;typedef struct node3{BinaryTree *nValue;struct node3 *pNext;}MyQueue;typedef struct node4{int nCount;MyQueue* pHead;MyQueue *pTail;}Queue;BinaryTree * CreateTree();void RecPreOrderTraversal(BinaryTree *pRoot){if(pRoot == NULL)return;//前序遍历 根左右printf("%d ",pRoot->nValue);RecPreOrderTraversal(pRoot->pLeft);RecPreOrderTraversal(pRoot->pRight);}void RecInOrderTraversal(BinaryTree *pRoot){if(pRoot == NULL)return;//中序遍历 左根右RecInOrderTraversal(pRoot->pLeft);printf("%d ",pRoot->nValue);RecInOrderTraversal(pRoot->pRight);}void RecLastOrderTraversal(BinaryTree *pRoot){if(pRoot == NULL)return;//后序遍历 左右根RecLastOrderTraversal(pRoot->pLeft);RecLastOrderTraversal(pRoot->pRight);printf("%d ",pRoot->nValue);}void q_Init(Queue **pQueue){if(pQueue == NULL)return;*pQueue = (Queue*)malloc(sizeof(Queue));(*pQueue)->nCount = 0;(*pQueue)->pHead = NULL;(*pQueue)->pTail = NULL;}void q_Push(Queue *pQueue,BinaryTree *nNum){MyQueue *pTemp = NULL;if(pQueue == NULL)return;pTemp = (MyQueue*)malloc(sizeof(MyQueue));pTemp->nValue = nNum;pTemp->pNext = NULL;if(pQueue->pHead == NULL){pQueue->pHead = pTemp;}else{pQueue->pTail->pNext = pTemp;}pQueue->pTail = pTemp;pQueue->nCount++;}BinaryTree* q_Pop(Queue *pQueue){MyQueue *pDel = NULL;BinaryTree *Temp = NULL;if(pQueue == NULL || pQueue->pHead == NULL)return NULL;pDel = pQueue->pHead;Temp = pDel->nValue;pQueue->pHead = pQueue->pHead->pNext;free(pDel);pDel = NULL;pQueue->nCount--;if(pQueue->nCount == 0){pQueue->pTail = NULL;}return Temp;}int q_IsEmpty(Queue *pQueue){if(pQueue == NULL )return -1;return pQueue->nCount ? 0:1;}void EveryTravelsalTree(BinaryTree *Tree){//层序遍历Queue *M_Queue = NULL;BinaryTree *Temp = NULL;if(Tree == NULL)return;q_Init(&M_Queue);q_Push(M_Queue,Tree);while(!q_IsEmpty(M_Queue)){Temp = q_Pop(M_Queue);printf("%d ",Temp->nValue);if(Temp->pLeft != NULL){q_Push(M_Queue,Temp->pLeft);}if (Temp->pRight != NULL){q_Push(M_Queue,Temp->pRight);}}}
例二叉树
前序:X Y F X R B K G D
中序:X B R Y Z F D G K
后序:X Y F Z R B K G D
二叉树的创建
手动创建二叉树,最慢最基础零技术的创建方法
|
|
递归创建二叉树(反序列创建二叉树)
根据层序遍历特点来创建二叉树
动态创建二叉树(用数组的形势)
根据二叉树的性质5
二叉树的循环高级遍历(非递归)
叙述流程
- 申请栈stack
- 检验参数
- (前序:打印节点)节点入栈
- 判断节点是否有左
- 有,重复3 4
- 没有,下一步(中序:打印节点)
- 栈顶弹出,弹出栈顶是否为空
- 是,结束
- 无,重复5
- 判断弹出节点是否有右
- 有,把右变成当前节点,之后重复3 4
- 无,重复5
用循环实现前序遍历
|
|
用循环实现中序遍历
|
|
用循环实现后序遍历
|
|
在二叉树中插入一个结点
首先先写一个查找的API
在一个二叉树中查找一个结点
|
|
在一个二叉树中插入一个结点,传入方向,值
|
|
创建一个排序二叉树
创建流程
- 将值放入结点
- 检测树是否为空树,若是空结点,则新结点即为根,结束
- 若树为非空树
比较结点值大小
若值比结点值大,则向右子树方向走
- 空树:放入结点
非空树:向右走,重复3步骤
若值比结点值小,则向左子树方向走
- 空树:放入结点
- 非空树:向左走,重复3步骤
值相同的话,违背排序二叉树的步骤,释放新建结点空间,结束
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374void InsertNode(BinaryTree **pRoot,int nNum){BinaryTree *pTemp = NULL;BinaryTree *pMark = NULL;if(pRoot == NULL)return;pTemp = (BinaryTree*)malloc(sizeof(BinaryTree));pTemp->nValue = nNum;pTemp->pLeft = NULL;pTemp->pRight = NULL;//空树if(*pRoot == NULL){*pRoot = pTemp;return;}//标记根pMark = *pRoot;while(1){//新来的节点值小if(nNum < pMark->nValue){//左空if(pMark->pLeft == NULL){pMark->pLeft = pTemp;return;}//非空 向左走pMark = pMark->pLeft;}//新来的节点值大else if(nNum > pMark->nValue){//右空if(pMark->pRight == NULL){pMark->pRight = pTemp;return;}//非空 向右走pMark = pMark->pRight;}//新来值已经存在 违背性质 结束else{free(pTemp);pTemp = NULL;return;}}}BinaryTree *CreateSrtTree(int arr[],int nLength){BinaryTree *pRoot = NULL;int i;if(arr == NULL || nLength <=0)return NULL;for(i = 0;i<nLength;i++){InsertNode(&pRoot,arr[i]);}return pRoot;}
从排序二叉树中删除一个结点
删除的流程
- 叶子节点,直接删除
- 有一个孩子(即只有一个左子树或者右子树),删除当前结点,用孩子代替它
- 有两个孩子,找到其右的最左或者左的最右的结点,将找到的结点的值覆盖删除结点的值,之后进行步骤1,2
在一个排序二叉树查找一个结点的代码
1234567891011121314151617181920212223242526272829303132//参数:树,被查找的值,被删除节点的地址,被删除节点的父亲void Search(BinaryTree *pTree,int nNum,BinaryTree **pDel,BinaryTree **pDelFather){if(pTree == NULL)return;while(pTree){//找到 记住被删除位置 结束if(pTree->nValue == nNum){*pDel = pTree;return;}//查找的值比当前节点的小 记住被删除节点的父亲 而后向左走else if(pTree->nValue > nNum){*pDelFather = pTree;pTree = pTree->pLeft;}//查找的值比当前节点的大 记住被删除节点的父亲 而后向右走else{*pDelFather = pTree;pTree = pTree->pRight;}}//查找结束 没找到 清空标记删除节点父亲的指针*pDelFather = NULL;return ;}
在排序二叉树删除一个节点代码
|
|
排序二叉树转换成一个排序双向链表
流程
- 根据中序遍历特点,左根右
- 将二叉树左看成链表的上指针,将二叉树的右看链表的下指针.
- 在中序遍历输出的位置,将二叉树变成链表式连接
代码
1234567891011121314151617181920212223242526void SortTreeToList(BinaryTree *pRoot, BinaryTree **pHead,BinaryTree **pTail){if(pRoot == NULL)return;if(pHead == NULL || pTail == NULL)return;//找到左侧SortTreeToList(pRoot->pLeft,pHead,pTail);//尾添加节点if(*pHead == NULL){*pHead = pRoot;}else{//双向指向关联//left = 上一个pre//right = 下一个next(*pTail)->pRight = pRoot;pRoot->pLeft = *pTail;}*pTail = pRoot;//找到右侧SortTreeToList(pRoot->pRight,pHead,pTail);}
平衡二叉树的两种旋转
普通二叉树深度:后序遍历查栈里元素的个数
将差一步的平衡二叉树变成平衡二叉树
右旋
在原有的二叉树里,添加一个父亲的指针
在左子树的左子树添加一个结点F,进行右旋
A交支点长的一边B - D - F掰下来
两个步骤:
记:A->pLeft = pMark
先处理儿子:
- A ->Father->left = pmark
- pmark->right = A
- pmark = pmark->right
父亲: - pmark ->father = A->father
- A->father = pmark
- pmark->right->father = A
左旋
与右旋相反创建一个不平衡二叉树
1234567891011121314151617181920212223242526272829303132333435363738394041void CreateBiTree(BinaryTree **root){if(root == NULL)return;(*root) = (BinaryTree*)malloc(sizeof(BinaryTree));(*root)->nValue = 1;(*root)->pFather = NULL;//左子树(*root)->pLeft = (BinaryTree*)malloc(sizeof(BinaryTree));(*root)->pLeft->nValue = 2;(*root)->pLeft->pFather = *root;//右子树(*root)->pRight = NULL;//左的左(*root)->pLeft->pLeft = (BinaryTree*)malloc(sizeof(BinaryTree));(*root)->pLeft->pLeft->nValue = 3;(*root)->pLeft->pLeft->pFather = (*root)->pLeft;//左的左的左(*root)->pLeft->pLeft->pLeft = (BinaryTree*)malloc(sizeof(BinaryTree));(*root)->pLeft->pLeft->pLeft->nValue = 5;(*root)->pLeft->pLeft->pLeft->pFather = (*root)->pLeft->pLeft;(*root)->pLeft->pLeft->pLeft->pLeft = NULL;(*root)->pLeft->pLeft->pLeft->pRight = NULL;//左的左的右(*root)->pLeft->pLeft->pRight = (BinaryTree*)malloc(sizeof(BinaryTree));(*root)->pLeft->pLeft->pRight ->nValue = 6;(*root)->pLeft->pLeft->pRight ->pFather = (*root)->pLeft->pLeft;(*root)->pLeft->pLeft->pRight ->pLeft = NULL;(*root)->pLeft->pLeft->pRight ->pRight = NULL;//左的右(*root)->pLeft->pRight = (BinaryTree*)malloc(sizeof(BinaryTree));(*root)->pLeft->pRight->nValue = 4;(*root)->pLeft->pRight->pFather = (*root)->pLeft;(*root)->pLeft->pRight->pLeft = NULL;(*root)->pLeft->pRight->pRight = NULL;}
右旋,左旋代码
void RightRotate(BinaryTree **pTree)
{
BinaryTree *pMark = NULL;
if(pTree == NULL)return;
//右旋标记左侧
pMark = (*pTree)->pLeft;
//处理儿子关系
(*pTree)->pLeft = pMark->pRight;
pMark->pRight = *pTree;
//支点父亲是否存在
if((*pTree)->pFather != NULL)
{
//将支点的左和支点的父亲关联起来
((*pTree)->pFather->pLeft == *pTree)?((*pTree)->pFather->pLeft = pMark):((*pTree)->pFather->pRight= pMark);
}
//关联父亲
//支点的左是否存在
if((*pTree)->pLeft != NULL)
{
(*pTree)->pLeft->pFather = *pTree;
}
pMark->pFather = (*pTree)->pFather;
(*pTree)->pFather = pMark;
//支点是根节点 旋转之后根节点更改
if(pMark->pFather == NULL)
{
*pTree = pMark;
}
}
void LeftRotate(BinaryTree **pTree)
{
BinaryTree *pMark = NULL;
if(pTree == NULL)return;
//右旋标记左侧
pMark = (*pTree)->pRight;
//处理儿子关系
(*pTree)->pRight = pMark->pLeft;
pMark->pLeft = *pTree;
//支点父亲是否存在
if((*pTree)->pFather != NULL)
{
//将支点的左和支点的父亲关联起来
((*pTree)->pFather->pLeft == *pTree)?((*pTree)->pFather->pLeft = pMark):((*pTree)->pFather->pRight= pMark);
}
//关联父亲
//支点的左是否存在
if((*pTree)->pRight != NULL)
{
(*pTree)->pRight->pFather = *pTree;
}
pMark->pFather = (*pTree)->pFather;
(*pTree)->pFather = pMark;
//支点是根节点 旋转之后根节点更改
if(pMark->pFather == NULL)
{
*pTree = pMark;
}
}