二叉樹的簡單面試題彙總

目錄

1.前中後序遍歷(遞歸與非遞歸)

1.前序遍歷

1.遞歸

2.非遞歸

2.中序遍歷

1.遞歸

2.非遞歸

3.後序遍歷

1.遞歸

2.非遞歸

2.層序遍歷

3.求二叉樹高度(深度)

4.求葉子節點個數

5.求二叉樹第K層節點個數

1.求二叉樹第K層節點個數(遞歸)

2.非遞歸實現求二叉樹第k層的節點數

6.判斷一個節點是否在一棵二叉樹上

7.求兩個節點的最近公共祖先

1.搜索樹

2.三叉鏈

3.一般二叉樹

8.判斷一棵二叉樹是否是平衡二叉樹

9.求二叉樹中最遠兩個節點的距離

10. 由前序遍歷和中序遍歷重建二叉樹

11. 判斷一棵樹是否是完全二叉樹

12.求二叉樹的鏡像(即左右子樹交換)

13.將二叉搜索樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只能調整樹中結點指針的指向。


1.前中後序遍歷(遞歸與非遞歸)

1.前序遍歷

1.遞歸

void preOrder1(BinTree *root)     
{
	if (root != NULL)
	{
		cout << root->data << " ";
		preOrder1(root->lchild);
		preOrder1(root->rchild);
	}
}

2.非遞歸

對於任一結點P:

     1)訪問結點P,並將結點P入棧;

     2)判斷結點P的左孩子是否爲空,若爲空,則取棧頂結點並進行出棧操作,並將棧頂結點的右孩子置爲當前的結點P,循環至1);若不爲空,則將P的左孩子置爲當前的結點P;

     3)直到P爲NULL並且棧爲空,則遍歷結束。

void preOrder2(BinTree *root)     
{
	stack<BinTree*> s;
	BinTree *p = root;
	while (p != NULL || !s.empty())
	{
		while (p != NULL)
		{
			cout << p->data << " ";
			s.push(p);
			p = p->lchild;
		}
		if (!s.empty())
		{
			p = s.top();
			s.pop();
			p = p->rchild;
		}
	}
}

2.中序遍歷

1.遞歸

void inOrder1(BinTree *root)     
{
	if (root != NULL)
	{
		inOrder1(root->lchild);
		cout << root->data << " ";
		inOrder1(root->rchild);
	}
}

2.非遞歸

 對於任一結點P,

   1)若其左孩子不爲空,則將P入棧並將P的左孩子置爲當前的P,然後對當前結點P再進行相同的處理;

   2)若其左孩子爲空,則取棧頂元素並進行出棧操作,訪問該棧頂結點,然後將當前的P置爲棧頂結點的右孩子;

   3)直到P爲NULL並且棧爲空則遍歷結束。

void inOrder2(BinTree *root)     
{
	stack<BinTree*> s;
	BinTree *p = root;
	while (p != NULL || !s.empty())
	{
		while (p != NULL)
		{
			s.push(p);
			p = p->lchild;
		}
		if (!s.empty())
		{
			p = s.top();
			cout << p->data << " ";
			s.pop();
			p = p->rchild;
		}
	}
}

3.後序遍歷

1.遞歸

void postOrder1(BinTree *root)    
{
	if (root != NULL)
	{
		postOrder1(root->lchild);
		postOrder1(root->rchild);
		cout << root->data << " ";
	}
}

2.非遞歸

要保證根結點在左孩子和右孩子訪問之後才能訪問,因此對於任一結點P,先將其入棧。如果P不存在左孩子和右孩子,則可以直接訪問它;或者P存 在左孩子或者右孩子,但是其左孩子和右孩子都已被訪問過了,則同樣可以直接訪問該結點。若非上述兩種情況,則將P的右孩子和左孩子依次入棧,這樣就保證了 每次取棧頂元素的時候,左孩子在右孩子前面被訪問,左孩子和右孩子都在根結點前面被訪問。

void postOrder3(BinTree *root)     
{
	stack<BinTree*> s;
	BinTree *cur;                      //當前結點 
	BinTree *pre = NULL;                 //前一次訪問的結點 
	s.push(root);
	while (!s.empty())
	{
		cur = s.top();
		if ((cur->lchild == NULL&&cur->rchild == NULL) ||
			(pre != NULL && (pre == cur->lchild || pre == cur->rchild)))
		{
			cout << cur->data << " ";  //如果當前結點沒有孩子結點或者孩子節點都已被訪問過 
			s.pop();
			pre = cur;
		}
		else
		{
			if (cur->rchild != NULL)
				s.push(cur->rchild);
			if (cur->lchild != NULL)
				s.push(cur->lchild);
		}
	}
}

2.層序遍歷

void printTree(BinTree *root)//層序遍歷
{
	queue<BinTree *> s;
	s.push(root);
	while (!s.empty())
	{
		BinTree *p = s.front();
		cout << p->data << "";
		s.pop();
		if (p->lchild != NULL)
			s.push(p->lchild);
		if (p->rchild != NULL)
			s.push(p->rchild);
	}
}

3.求二叉樹高度(深度)

int Depth(BinTree* root)
{
	if (root == NULL)
		return 0;
	int left = Depth(root->lchild);
	int right = Depth(root->rchild);
	return left > right ? left + 1 : right + 1;
}

4.求葉子節點個數

size_t GetLeafSize(BinTree* root)//求葉子節點數
{
	if (root == NULL)
		return 0;

	if (root->lchild == NULL
		&& root->rchild == NULL)
	{
		return 1;
	}

	return GetLeafSize(root->lchild)
		+ GetLeafSize(root->rchild);
}

5.求二叉樹第K層節點個數

1.求二叉樹第K層節點個數(遞歸)

int get_k_level_number(BinTree *root, int k)
{
	if (root == NULL || k <= 0)
		return 0;
	if (root != NULL && k == 1)
		return 1;

	return (get_k_level_number(root->lchild, k - 1) +
		get_k_level_number(root->rchild,k - 1));
}

2.非遞歸實現求二叉樹第k層的節點數

6.判斷一個節點是否在一棵二叉樹上

bool IsInTree(BinTree* root, int x)
{
	if (root == NULL)
		return false;
	if (root->data == x)
		return true;
	return IsInTree(root->lchild, x)
		|| IsInTree(root->rchild, x);
}

7.求兩個節點的最近公共祖先

1.搜索樹

1 某一個節點爲根節點,那麼公共節點就是根節點了。

2 這倆個節點在不同子樹,那麼公共節點就是根節點。

3 這倆個節點在同一子樹。

所以根據以上分析,可得我們可以通過不斷的遞歸直到找到一個子樹,在該子樹中倆節點分別位於它的左子樹和右子樹,那麼該子樹節點就是公共節點。

BinTree* findCom_ancestor(BinTree *root, BinTree *x, BinTree *x2)
{
	if (root == NULL) return NULL;
	if (x == root || x2 == root) return root;
	if (x->data > root->data&&x2->data < root->data)
	{
		return root;
	}
	if (x->data < root->data&&x2->data > root->data)
	{
		return root;
	}
	if (x->data < root->data)
	{
		return findCom_ancestor(root->lchild, x, x2);
	}
	else
	{
		return findCom_ancestor(root->rchild, x, x2);
	}
}

2.三叉鏈

1.給定的兩個節點都含有父節點,因此,可將這兩個節點看做是兩個鏈表的頭結點,將求兩個節點的最近公共祖先節點轉化爲求兩鏈表的交點,這兩個鏈表的尾節點都是根節點。(O(n))

int Hight(BinTree* root, BinTree* node)

{
	int len = 0;
	while (node != NULL)
	{
		len++;
		node = node->parent;
	}
	return len;
}
BinTree* GetLastCommonAncestor(BinTree* root, BinTree* node1, BinTree* node2)

{
	if (root == NULL || node1 == NULL || node2 == NULL)

		return NULL;

	int len1 = Hight(root, node1);

	int len2 = Hight(root, node2);

	for (; len1 > len2; len1--)

		node1 = node1->parent;

	for (; len2 > len1; len2--)

		node2 = node2->parent;

	while (node1 && node2 && node1 != node2)
	{
		node1 = node1->parent;

		node2 = node2->parent;
	}

	if (node1 == node2)

		return node1;

	else

		return NULL;

}

2.首先給出node1的父節點node1->_parent,然後將node1的所有父節點依次和node2->parent作比較,如果發現兩個節點相等,則該節點就是最近公共祖先,直接將其返回。如果沒找到相等節點,則將node2的所有父節點依次和node1->_parent->_parent作比較......直到node1->_parent==NULL。O(n^2)

BinTree * GetLastCommonAncestor(BinTree * root, BinTree * node1, BinTree * node2)

{

	BinTree * temp;

	while (node1 != NULL)

	{

		node1 = node1->parent;

		temp = node2;

		while (temp != NULL)

		{

			if (node1 == temp->parent)

				return node1;

			temp = temp->parent;

		}

	}

}

3.一般二叉樹

從根節點開始遍歷,如果node1和node2中的任一個和root匹配,那麼root就是最低公共祖先。

如果都不匹配,則分別遞歸左、右子樹,

如果有一個 節點出現在左子樹,並且另一個節點出現在右子樹,則root就是最低公共祖先. 

如果兩個節點都出現在左子樹,則說明最低公共祖先在左子樹中,否則在右子樹。

該函數當一個節點是另一個節點的祖先時,返回的是離根節點最近的那個節點,要想返回最近公共祖先節點需進行判斷兩節點是否有祖孫關係,

若兩節點爲F,D,則判斷出B的左子樹中的D節點後,繼續判斷判斷節點D的左右子樹中是否含有節點F,若有,則返回B,若沒有則繼續判斷右子樹中的另一個節點。

BinTree* GetLastCommonAncestor(BinTree* root, BinTree* node1, BinTree* node2)

{

	if (root == NULL || node1 == NULL || node2 == NULL)

		return NULL;



	if (node1 == root || node2 == root)

		return root;



	BinTree* cur = NULL;



	BinTree* left_lca = GetLastCommonAncestor(root->lchild, node1, node2);

	if (NULL != left_lca)

	{

		cur = GetLastCommonAncestor(left_lca->lchild, node1, node2);

		if (cur == NULL)

			cur = GetLastCommonAncestor(left_lca->rchild, node1, node2);

		if ((cur == node1) && (left_lca == node2) || (cur == node2) && (left_lca == node1))

			return root;

	}

	BinTree* right_lca = GetLastCommonAncestor(root->rchild, node1, node2);

	if (NULL != right_lca)

	{

		cur = GetLastCommonAncestor(right_lca->rchild, node1, node2);

		if (cur == NULL)

			cur = GetLastCommonAncestor(right_lca->rchild, node1, node2);

		if ((cur == node1) && (right_lca == node2) || (cur == node2) && (right_lca == node1))

			return root;

	}

	if (left_lca && right_lca)

		return root;

	if (left_lca == NULL)

		return right_lca;

	else

		return left_lca;

}

8.判斷一棵二叉樹是否是平衡二叉樹

//1
bool IsBalanceTree(BinTree* root)//(時間複雜度是N^2)
{
	if (root == NULL)
		return true;
	int left = Depth(root->lchild);
	int right = Depth(root->rchild);
	return abs(left - right)<2	   
		&& IsBalanceTree(root->lchild)
		&& IsBalanceTree(root->rchild);
}
//2
bool IsBalance(BinTree* root, int& depth)//(時間複雜度是N)
{
	if (root == NULL)
	{
		depth = 0;
		return true;
	}
	int leftDepth = 0;
	if (IsBalance(root->lchild, leftDepth) == false)
		return false;
	int rightDepth = 0;
	if (IsBalance(root->rchild, rightDepth) == false)
		return false;
	depth = leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
	return abs(leftDepth - rightDepth) < 2;
}

9.求二叉樹中最遠兩個節點的距離

int _FindMaxLen(BinTree* root, int& maxLen)
{
	if (root == NULL)
		return 0;

	int left = _FindMaxLen(root->lchild, maxLen);
	int right = _FindMaxLen(root->rchild, maxLen);

	if (left + right > maxLen)
	{      
		maxLen = left + right;
	}

	return left > right ? left + 1 : right + 1;
}

10. 由前序遍歷和中序遍歷重建二叉樹

BinTree* _RebuildBinaryTree(int* startPre, int* endPre, int* startIn, int* endIn)
{
	//根據前序遍歷保存根節點
	BinTree *Root;
	Root = new BinTree();
	Root->data = startPre[0];
	Root->lchild = NULL;
	Root->rchild = NULL;
	//判斷是否找完了此次中序遍歷,若是找完了,則返回Root;
	if (startIn == endIn&&*startIn == *endIn)
		return Root;
	//在中序遍歷中找根節點所在的位置
	int* RootIn = startIn;
	while (*startPre != *RootIn)
		RootIn++;
	//根據根節點在中遍歷中的位置,遞歸還原左子樹
	int leftlen = RootIn - startIn;
	if (leftlen > 0)
	{
		Root->lchild = _RebuildBinaryTree(startPre + 1, startPre + leftlen, startIn, RootIn-1);
	}
	//左子樹長度加上中序的起始位置後若仍小於整個中序長度,則說明該節點右子樹存在,遞歸還原右子樹
	if (leftlen + startIn < endIn)
	{
		Root->rchild = _RebuildBinaryTree(startPre + leftlen + 1, endPre, startIn + leftlen + 1, endIn);
	}
	return Root;
}

11. 判斷一棵樹是否是完全二叉樹

//->關鍵:找第一個度不爲2的結點->後序結點:如果有孩子則不是完全二叉樹,否則是
bool IsCompleteBinaryTree(BinTree* root)
{
	if (NULL == root)  //
		return false;
	bool isOnlyLeft = false;  //標記僅有左節點的結點
	queue<BinTree*> q;
	q.push(root);
	while (!q.empty())
	{
		BinTree* pCur = q.front();
		q.pop();
		if (isOnlyLeft)
		{
			if (pCur->lchild || pCur->rchild)
				return false;
		}
		else
		{
			if (NULL == pCur->lchild && NULL != pCur->rchild
				)  //存在右孩子沒有左孩子,一定不爲完全二叉樹
				return false;
			else if (NULL != pCur->lchild && NULL == pCur->rchild)  //存在左孩子沒有右孩子可能不是,記錄標記結點看後續結點
			{
				q.push(pCur->lchild);
				isOnlyLeft = true;  //只有左孩子是非滿結點
			}
			else if (NULL != pCur->lchild  && NULL != pCur->rchild)  // 左右孩子都存在,入隊列繼續循環判斷 
			{
				q.push(pCur->lchild);
				q.push(pCur->rchild);
			}
			else
				isOnlyLeft = true; //左右孩子都存在,爲非滿結點,看後續結點
		}
	}
	return true;
}

12.求二叉樹的鏡像(即左右子樹交換)

BinTree * Mirror(BinTree * Root)
{
	if (Root == NULL) // 返回NULL  
		return NULL;
	BinTree * pLeft = Mirror(Root->lchild); // 求左子樹鏡像  
	BinTree * pRight = Mirror(Root->rchild); // 求右子樹鏡像  
	// 交換左子樹和右子樹  
	Root->lchild = pRight;
	Root->rchild = pLeft;
	return Root;
}

13.將二叉搜索樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只能調整樹中結點指針的指向。

BinTree* Convert(BinTree* root)
{
	if (root == NULL){//假如根節點爲空,返回空
		return NULL;
	}
	if (root->lchild == NULL&&root->rchild == NULL){//假如只有一個根節點,則返回根節點
		return root;
	}
	//1、將左子樹構造成雙鏈表,並返回該鏈表頭結點left
	BinTree* left = Convert(root->lchild);

	//2、定位到左子樹鏈表的最後一個節點(左子樹最右邊的節點)
	BinTree* p = left;//創建一個臨時節點P,用來遍歷找到左鏈表的最後一個節點(左子樹最右邊的節點),p初始化指向做左子樹的根節點,
	while (p != NULL&&p->rchild != NULL)
	{
		p = p->lchild;//最終p爲左子樹最右邊的節點
	}

	//3、如果左子樹鏈表不爲空,將當前root追加到左子樹鏈表後
	if (left != NULL){//左子樹鏈表不爲空
		p->rchild = root;//左子樹鏈表的最後一個節點p(左子樹最右邊節點)的右指針指向當前root節點
		root->lchild = p;//當前root節點的左指針指向左子樹鏈表的最後一個節點p(左子樹最右邊節點)
	}

	//4、將右子樹構造成雙鏈表,並返回該鏈表的頭結點right
	BinTree* right = Convert(root->rchild);

	//5、如果右子樹鏈表不爲空,將右子樹鏈表追加到當前root後
	if (right != NULL)
	{//右子樹鏈表不爲空
		right->lchild = root;//右子樹鏈表的頭結點right的左指針指向當前root
		root->rchild = right;//當前root的右指針指向右子樹鏈表的頭結點right
	}
	return left != NULL ? left : root;//根據左子樹鏈表是否爲空返回整個雙向鏈表的頭指針。  
}

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