声明与定义
typedef char ElementType;
typedef struct TNode *Position;
typedef Position BinTree;
struct TNode
{
ElementType Data;
BinTree Left;
BinTree Right;
};
中、先、后序遍历
void InorderTraversal(BinTree BT)
{
if (BT)
{
InorderTraversal(BT->Left);
printf(" %c", BT->Data);
InorderTraversal(BT->Right);
}
}
void PreorderTraversal(BinTree BT)
{
if (BT)
{
printf(" %c", BT->Data);
PreorderTraversal(BT->Left);
PreorderTraversal(BT->Right);
}
}
void PostorderTraversal(BinTree BT)
{
if (BT)
{
PostorderTraversal(BT->Left);
PostorderTraversal(BT->Right);
printf(" %c", BT->Data);
}
}
回顾
为啥说树结构简单,其实不是容易理解,而是容易写代码,哪怕我真的一点也不懂。
层序遍历
C++版
void LevelorderTraversal(BinTree BT)
{
queue<BinTree> q;
q.push(BT);
while (!q.empty())
{
printf(" %c", q.front()->Data);
if (q.front()->Left)
q.push(q.front()->Left);
if (q.front()->Right)
q.push(q.front()->Right);
q.pop();
}
}
回顾
因为c++中的stl有现成的队列可以直接使用,所以毫不犹豫的使用了queue。
需要注意的是,队列里面存放的元素类型是二叉树指针(可以理解成放的是一整个结点,而不是结点所对应的数据值)。
一开始先把根节点放入队列中,然后进入while循环,循环退出条件是队列为空(队列里面放的是需要输出的元素,输出顺序即为队头到队尾的顺序)。
每一个循环分为三个操作,第一个操作是,假若队头元素的左、右儿子非空,那么入队;第二个操作是,输出队头元素的数据值;第三个操作是,让队头元素出队。
C语言版
顺序队列
void LevelorderTraversal(BinTree BT)
{
BinTree array[50];
for (int i = 0; i < 50; i++)
array[i] = NULL;
int in = 0, out = 0;
array[in++] = BT;
while (in > out)
{
if (array[out])
{
printf(" %c", array[out]->Data);
if (array[out]->Left)
array[in++] = array[out]->Left;
if (array[out]->Right)
array[in++] = array[out]->Right;
}
out++;
}
}
回顾
变量in可以理解为rear,变量out可以理解为front。( 动作 <-> 队列 )
数组元素千万不能是ElementType,一定要是指向结点的指针。
特殊情况 -> 树为空树的情况:仍然会进while,但是进去并不做什么,而且只进了一次。
每次入队的结点都是出队结点的儿子结点。
循环队列
void LevelorderTraversal(BinTree BT)
{
BinTree array[10];
for (int i = 0; i < 10; i++)
array[i] = NULL;
int rear = 0, front = 0;
array[rear++] = BT;
while (rear != front)
{
if (array[front])
{
printf(" %c", array[front]->Data);
if (array[front]->Left)
{
array[rear] = array[front]->Left;
rear = (rear + 1) % 10;
}
if (array[front]->Right)
{
array[rear] = array[front]->Right;
rear = (rear + 1) % 10;
}
}
front = (front + 1) % 10;
}
}
void LevelorderTraversal(BinTree BT)
{
BinTree array[10];
for (int i = 0; i < 10; i++)
array[i] = NULL;
int rear = 0, front = 0, len = 0;
array[rear++] = BT;
if (BT)
len++;
while (len)
{
if (array[front])
{
printf(" %c", array[front]->Data);
len--;
if (array[front]->Left)
{
array[rear] = array[front]->Left;
rear = (rear + 1) % 10;
len++;
}
if (array[front]->Right)
{
array[rear] = array[front]->Right;
rear = (rear + 1) % 10;
len++;
}
}
front = (front + 1) % 10;
}
}
void LevelorderTraversal(BinTree BT)
{
BinTree array[10];
for (int i = 0; i < 10; i++)
array[i] = NULL;
int rear = 0, front = 0, tag = 0;
array[rear++] = BT;
if (BT)
tag = 1;
while (rear != front || tag != 0)
{
if (array[front])
{
printf(" %c", array[front]->Data);
tag = 0;
if (array[front]->Left)
{
array[rear] = array[front]->Left;
rear = (rear + 1) % 10;
tag = 1;
}
if (array[front]->Right)
{
array[rear] = array[front]->Right;
rear = (rear + 1) % 10;
tag = 1;
}
}
front = (front + 1) % 10;
}
}
回顾:
循环队列解决假溢出现象的三种方法:(分别对应上面三段代码)
1)预留一个空间不存数值。空:rear==front ;满:(rear+1)% MAXQSIEZ == front。
(注:这里的一个空间不是指固定的某一个空间,确切地说是front指针所指位置的前一个空间始终不存元素,即始终为空)
2)设置len变量记录队列长度。空:len==0;满:len==MAXQSIZE。
(注:每次入队操作完成的时候,len++;每次出队操作完成的时候,len++)
3)设置tag变量。空:rear==front && tag==0;满:rear==front && tag==1。
(注:每次入队操作完成的时候,tag值改为1;每次出队操作完成的时候,tag值改为0)
二叉树的层序遍历用不上判满,因为如果队列满的话,那就遍历失败了。
方法二和方法三需要特别关注树为空树的情况,如果没有考虑到,则会进入死循环。
对比
循环队列好在哪?节省空间,解决了假溢出问题。
顺序队列不好在哪?浪费资源,出现假溢出现象。
同样的一棵树,我们如果事先评估,那么在设置数组容量时,循环队列这种数据结构可以为我们剩下很大的空间。
指针数组
指针一般初始化成NULL;指针数组也一样初始化成NULL。
这样可以判断指针如果NULL,则该指针可以分配;如果不等于NULL,则说明指针已经指向某一变量。
一般在用完了指针,收回了分配的内存,比如delete/free以后,也要及时将指针赋值为NULL,便于再次使用。
为了安全合法的使用指针数组,避免访问到其他预期不到的内存,我们必须对指针数组进行初始化。