二叉树的创建
结构体定义
#include <stdio.h>
#include <malloc.h>
#define QUEUE_SIZE 5
typedef struct BTNode{
char element;
BTNode* left;//左儿子
BTNode* right;//右儿子
}BTNode,*BTNODE;
typedef struct BTNodePtrQueue{
BTNODE* nodePtr;
int front;
int rear;
}BTNodePtrQueue,*QUEUE;
初始化
QUEUE initQueue()
{
QUEUE resultPtr = (QUEUE)malloc(sizeof(BTNodePtrQueue));
resultPtr->nodePtr = (BTNODE*)malloc(QUEUE_SIZE*sizeof(BTNODE));
resultPtr->front = 0;
resultPtr->rear = 1;
return resultPtr;
}//初始化二叉树
创建子树
BTNODE constructBTNode(char paraChar)
{
BTNODE resultPtr = (BTNODE)malloc(sizeof(BTNode));
resultPtr->element = paraChar;
resultPtr->left = NULL;
resultPtr->right = NULL;
return resultPtr;
} //创建子树
字符串转化为二叉树``
这里使用的是顺序结构存储,就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储,二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。
从字符串转化为二叉树,就是先询问用户根节点是谁,然后每次都询问某个节点的左孩子是谁,右孩子是谁。如图:
代码如下:
BTNODE stringToBTree(char* paraString)
{
int i;
char ch;
QUEUE tempQueue = initQueue();
BTNODE resultHead;
BTNODE tempPa,tempLc,tempRc;
i = 0;
ch = paraString[i];
resultHead = constructBTNode(ch);
enQueue(tempQueue,resultHead);
while(!is_empty(tempQueue))
{
tempPa = deQueue(tempQueue);
i++;
ch = paraString[i];
if(ch == '#')
{
tempPa->left = NULL;
}else
{
tempLc = constructBTNode(ch);
enQueue(tempQueue,tempLc);
tempPa->left = tempLc;
}
i++;
ch = paraString[i];
if(ch == '#')
{
tempPa->right = NULL;
}else
{
tempRc = constructBTNode(ch);
enQueue(tempQueue,tempRc);
tempPa->right = tempRc;
}
}
return resultHead;
}//字符串转化为二叉树
二叉树的遍历
层次遍历
层次遍历从根节点出发,依次访问左右孩子结点,再从左右孩子出发,依次它们的孩子结点,直到节点访问完毕
让我们先看图:
层次遍历得到的结果是 ABCDEFGHI。
层次遍历和前中后序遍历不同。前中后序遍历利用了递归的思想,而层次遍历体现了队列的思想。
如上图。层序遍历的思路是,创建一个队列,先将根节点(A)入队,然后再将根节点出队,接下来看刚才的根节有没有左孩子或右孩子,如果有,先左(B)后右(C)入队,最后输出根节点的值,只要队列还不为空,就说明还没有遍历完,就进行下一次循环,这时的队头元素则为刚才入队的左孩子(B),然后出队,再把它的左右孩子拉进来(如果有),因为队列的先进先出性质,B的左右孩子D是排在C后面的,然后输出B,下一次循环将会拉人C的左右孩子EF,一直循环直到没有入队元素,队列变为空,当队列为空时,整颗树就层序遍历完成了,结束循环。
代码如下:
入队:
void enQueue(QUEUE paraQueue,BTNODE paraBTNode)
{
printf("front = %d,rear = %d\r\n",paraQueue->front,paraQueue->rear);
if((paraQueue->rear+1)%QUEUE_SIZE == paraQueue->front )
{
printf("the queue is full.\r\n");
return;
}
paraQueue->nodePtr[paraQueue->rear] = paraBTNode;
paraQueue->rear = (paraQueue->rear+1)%QUEUE_SIZE;
printf("enqueue %c ends.\r\n",paraBTNode->element);
}//添加子节点
出队
BTNODE deQueue(QUEUE paraQueue)
{
if(is_empty(paraQueue))
{
printf("can not delete,the queue is empty.\r\n");
return NULL;
}
paraQueue->front = (paraQueue->front+1)%QUEUE_SIZE;
printf("dequeue %c end.\r\n",paraQueue->nodePtr[paraQueue->front]->element);
return paraQueue->nodePtr[paraQueue->front];
} //删除子节点
主要循环过程:
void levelwise(BTNODE paraTree)
{
char tempString[100];
int i = 0;
QUEUE tempQueue = initQueue();
BTNODE tempNode;
enQueue(tempQueue,paraTree);
while(!is_empty(tempQueue))
{
tempNode = deQueue(tempQueue);
tempString[i] = tempNode->element;
i++;
if(tempNode->left!= NULL)
{
enQueue(tempQueue,tempNode->left);
}
if(tempNode->right != NULL)
{
enQueue(tempQueue,tempNode->right);
}
}
tempString[i] = '\0';
printf("Levelwise: %s\r\n",tempString);
}//层次遍历
前中后序遍历
在一颗二叉树中,我们将树根作为中间,然后分别是左子树和右子树。前,中,后序遍历也就是依次将根放到最开始,中间和最后进行的遍历方式。
前序遍历就是按照中左右的顺序遍历整个二叉树。例如下图中的二叉树
:
从前序遍历的话,我们从根出发,也就是A,接着到左子树B,然后把B当作新的根,接下来就是D,完了是E,E结束之后轮到以D为根的右子树H,这样B这整个左子树就遍历完了,然后我们再从C开始遍历右子树,依旧是中左右的顺序。
这样下来我们得到的结果就是: ABDGHCEIF。
那中序遍历是按照左中右的顺序遍历整个二叉树,道理和前序遍历一样,我们直接看图吧:
我们可以看到,中序遍历的时候我们从最左边接着是最下面作为起点开始遍历,得到的结果也就是: CDHBAEICF。
那同理我们也可以得到后序遍历:也就是以左右中的方式,将根放到最后来进行遍历。如图:
得到的结果是:GHDBIEFCA。
代码如下:
void preorder(BTNODE tempPtr)
{
if(tempPtr == NULL)
{
return;
}
printf("%c",tempPtr->element);
preorder(tempPtr->left);
preorder(tempPtr->right);
}//前序遍历
void inorder(BTNODE tempPtr)
{
if(tempPtr == NULL)
{
return;
}
inorder(tempPtr->left);
printf("%c",tempPtr->element);
inorder(tempPtr->right);
}//中序遍历
void postorder(BTNODE tempPtr)
{
if(tempPtr == NULL)
{
return;
}
postorder(tempPtr->left);
postorder(tempPtr->right);
printf("%c",tempPtr->element);
}//后序遍历
测试代码
int main(){
BTNODE tempHeader;
tempHeader = constructBTNode('c');
printf("There is only one node. Preorder visit: ");
preorder(tempHeader);
printf("\r\n");
char* tempString = "acde#bf######";
tempHeader = stringToBTree(tempString);
printf("Preorder: ");
preorder(tempHeader);
printf("\r\n");
printf("Inorder: ");
inorder(tempHeader);
printf("\r\n");
printf("Postorder: ");
postorder(tempHeader);
printf("\r\n");
printf("Levelwise: ");
levelwise(tempHeader);
printf("\r\n");
return 1;
}
运行结果
There is only one node. Preorder visit: c
front = 0,rear = 1
enqueue a ends.
dequeue a end.
front = 1,rear = 2
enqueue c ends.
front = 1,rear = 3
enqueue d ends.
dequeue c end.
front = 2,rear = 4
enqueue e ends.
dequeue d end.
front = 3,rear = 0
enqueue b ends.
front = 3,rear = 1
enqueue f ends.
dequeue e end.
dequeue b end.
dequeue f end.
Preorder: acedbf
Inorder: ecabdf
Postorder: ecbfda
Levelwise: front = 0,rear = 1
enqueue a ends.
dequeue a end.
front = 1,rear = 2
enqueue c ends.
front = 1,rear = 3
enqueue d ends.
dequeue c end.
front = 2,rear = 4
enqueue e ends.
dequeue d end.
front = 3,rear = 0
enqueue b ends.
front = 3,rear = 1
enqueue f ends.
dequeue e end.
dequeue b end.
dequeue f end.
Levelwise: acdebf
总结
在二叉树的创建与遍历的时候,我们都用到了之前学过的栈和队列那一部分的知识。
在二叉树的遍历中,用到了递归算法的思想,递归实现的本质也是系统帮我们建立了栈结构,而系统栈需要记住每个节点的值,所以空间复杂度仍为O(N),时间复杂度也为O(N)。