前記
上週我投遞出了簡歷,崗位是後端開發工程師。這周騰訊面試官給我進行了視頻面試。面試過程中他問了二叉樹的問題。二叉樹相關算法題,在面試中出現的次數非常非常多,所以我面試之前也有所準備。今天結合面試問題詳細講一講二叉樹,結合實例分析二叉樹的存儲結構的建立方法和遍歷過程。
面試問題
面試官大佬:看你的簡歷上寫熟悉數據結構,談談二叉樹遍歷的方式?
我:(這可難不倒我)
先序遍歷
先訪問根節點,後依次訪問左孩子和右孩子
遞歸算法
void PreOrder1(BTREE bt) //遞歸先根遍歷
{
if (bt)
{
if (bt->data != '#')
{
printf(" %c", bt->data);//結點不空 ,打印結點值
}
PreOrder1(bt->lchild);//依次訪問左右節點
PreOrder1(bt->rchild);
}
}
非遞歸算法
void PreOrder2(BTREE p)//非遞歸先根遍歷 ,先訪問根節點,後依次訪問左孩子和右孩子
{
int top = -1;
node *Q[N];
while (p != NULL || top != -1)
{
while (p != NULL)
{
if (p->data != '#')
{
printf(" %c", p->data);
}
Q[++top] = p;
p = p->lchild;
}
if (top != -1)
{
p = Q[top--];
p = p->rchild;
}
}
}
中序遍歷
先訪問左孩子,後依次訪問根節點和右孩子
遞歸算法
void InOrder1(BTREE bt)//遞歸中序遍歷
{
if (bt)
{
InOrder1(bt->lchild);//先訪問左節點
if (bt->data != '#')
{
printf(" %c", bt->data);//結點不空 ,打印結點值
}
InOrder1(bt->rchild);//先訪問右節點
}
}
非遞歸算法
void InOrder2(BTREE p)//非遞歸中序遍歷,先訪問左孩子,然後訪問根節點,後訪問右孩子
{
int top = -1;
node *Q[N];
while (p != NULL || top != -1)
{
while (p != NULL)
{
Q[++top] = p;
p = p->lchild;
}
if (top != -1)
{
p = Q[top--];
if (p->data != '#')
{
printf(" %c",p->data);
}
p = p->rchild;
}
}
}
後序遍歷
先訪問左孩子孩子,後依次訪問右孩子和根節點
遞歸算法
void PostOrder1(BTREE bt)//後序遍歷
{
if (bt)
{
PostOrder1(bt->lchild);//先訪問左,右孩子節點
PostOrder1(bt->rchild);
if (bt->data != '#')
{
printf(" %c", bt->data);//後訪問根節點
}
}
}
非遞歸算法
void PostOrder2(BTREE p)//非遞歸後序遍歷 ,先訪問左孩子,然後訪問右孩子,後訪問根節點
{
int top = -1;
node *Q[N];
int flag[N] = { 0 };
while (p != NULL || top != -1)
{
while (p != NULL)
{
top++;
Q[top] = p;
flag[top] = 1;
p = p->lchild;
}
while (top != -1 && flag[top] == 2)
{
if (Q[top]->data != '#')
{
printf(" %c", Q[top]->data);
top--;
}
}
if (top != -1)
{
flag[top] = 2;
p = Q[top]->rchild;
}
}
}
面試官大佬:你回答得很好,還有其他遍歷方式嗎
我:……
沉默了幾秒,我(這可難不倒我):還有一種層序遍歷
層序遍歷
從根開始,依次向下,對於每一層從左向右遍歷
//層序遍歷
void Sequense(BTREE bt)//建立棧,依次將根節點,左孩子,右孩子壓棧 ,並打印棧頂元素
{
node *Q[N];
node *p;
int front = 0, top = 0;
if (bt != NULL)
{
Q[++top] = bt;//將根節點壓棧
while (front < top) //遍歷棧
{
p = Q[++front];
if (p->data != '#')
{
printf(" %c", p->data);//打印棧頂元素
}
if (p->lchild)
{
Q[++top] = p->lchild;//將左孩子壓棧
}
if (p->rchild)
{
Q[++top] = p->rchild;//將右孩子壓棧
}
}
}
}
遍歷算法總結
面試官大佬:如何判斷是否完全二叉樹呢
我:(這可難不倒我)
判斷完全二叉樹
-
按層遍歷二叉樹, 從每層從左向右遍歷所有的結點
-
如果當前結點有右孩子, 但沒有左孩子, 那麼不是完全二叉樹
-
如果當前結點有左孩子但無右孩子, 那麼它之後的所有結點都必須爲葉子結點,否則不是完全二叉樹
-
如果當前結點有左孩子和右孩子, 繼續遍歷
int Compnode(BTREE G)//判斷是否是完全二叉樹
{
node *D[N], *p; //建立一個隊列D[N]
int front = 0, last = 0; //front是隊頭指針,last是隊尾指針
int tree_signal = 1;//tree_signal是判斷是否爲完全二叉樹的標誌
int odd_signal = 1;//odd_signal是判斷是否存在無左孩子的節點的標誌
if (G != NULL)
{
last++;
D[last] = G; //將根節點壓入隊尾
while (front != last)
{
front++;
p = D[front];
if (p->lchild == NULL ||(p->lchild)->data == '#') //*p節點沒有左孩子
{
odd_signal = 0;
if (p->rchild != NULL && (p->rchild)->data != '#') //沒有左孩子但有右孩子,不是完全二叉樹
tree_signal = 0;
}
else //*p節點有左子樹
{
if (odd_signal == 1) //目前不存在無左孩子的節點
{
last++; //左孩子進隊
D[last] = p->lchild;
if (p->rchild == NULL || (p->rchild)->data == '#') //*p有左孩子但沒有右孩子
{
odd_signal = 0;
}
else
{
last++; //右孩子進隊
D[last] = p->rchild;
}
}
else //目前存在有左孩子的節點,不是完全二叉樹
{
tree_signal = 0;
}
}
}
}
else
{
tree_signal = 0;//假設空樹不是完全二叉樹
}
return tree_signal;
}
總結
咱們玩歸玩,鬧歸鬧,別拿面試開玩笑。
二叉樹的遍歷雖然簡單,但遍歷方式多樣,也有遞歸算法和非遞歸算法之分。一旦問到了,大家一定要回答全面,不要丟三落四,回答到點上。二叉樹相關算法題,在面試中出現的次數非常非常多,大家面試前要把二叉樹等數據結構的基礎打牢。