1. 程式人生 > >二叉樹的簡單面試題彙總

二叉樹的簡單面試題彙總

目錄

1.遞迴

2.非遞迴

1.遞迴

2.非遞迴

1.遞迴

2.非遞迴

1.搜尋樹

2.三叉鏈

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;//根據左子樹連結串列是否為空返回整個雙向連結串列的頭指標。  
}