騰訊面試官這樣問我二叉樹,我剛好都會

前記

上週我投遞出了簡歷,崗位是後端開發工程師。這周騰訊面試官給我進行了視頻面試。面試過程中他問了二叉樹的問題。二叉樹相關算法題,在面試中出現的次數非常非常多,所以我面試之前也有所準備。今天結合面試問題詳細講一講二叉樹,結合實例分析二叉樹的存儲結構的建立方法和遍歷過程。

面試問題

面試官大佬:看你的簡歷上寫熟悉數據結構,談談二叉樹遍歷的方式?

我:(這可難不倒我)

先序遍歷

先訪問根節點,後依次訪問左孩子和右孩子

遞歸算法

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;//將右孩子壓棧
			}
		}
	}
}

遍歷算法總結

在這裏插入圖片描述

面試官大佬:如何判斷是否完全二叉樹呢

我:(這可難不倒我)

判斷完全二叉樹

  1. 按層遍歷二叉樹, 從每層從左向右遍歷所有的結點

  2. 如果當前結點有右孩子, 但沒有左孩子, 那麼不是完全二叉樹

  3. 如果當前結點有左孩子但無右孩子, 那麼它之後的所有結點都必須爲葉子結點,否則不是完全二叉樹

  4. 如果當前結點有左孩子和右孩子, 繼續遍歷

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;
}

總結

咱們玩歸玩,鬧歸鬧,別拿面試開玩笑。

二叉樹的遍歷雖然簡單,但遍歷方式多樣,也有遞歸算法非遞歸算法之分。一旦問到了,大家一定要回答全面,不要丟三落四,回答到點上。二叉樹相關算法題,在面試中出現的次數非常非常多,大家面試前要把二叉樹等數據結構的基礎打牢。

如果有收穫?希望老鐵們來個雙連擊,給更多的人看到這篇文章

1、老鐵們,關注我的原創微信公衆號「程序猿的進階」,主要是IT與競賽

2、創作不易,順便點個讚唄,可以讓更多的人看到這篇文章,激勵一下我這個小白。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章