和二叉樹相關的面試題
所謂的二叉樹就是樹中的每個節點最多有兩個孩子節點。滿二叉樹:除最後一層沒有子節點外,其它層的節點都具有兩個子節點。完全二叉樹:若二叉樹的高度為h,除最後一層外,其它層的節點數目都達到最大,並且最後一層的節點都集中在樹的左側。
二叉樹的節點結構:
typedef struct node
{
char data;
struct node *lchild, *rchild;
}Btree_node,*BTree;
1、二叉樹的建立
先序:先建立根節點,再建立左子樹,最後建立右子樹
void first_create(BTree *root) { char temp; cin >> temp; if (temp != '#') { *root = (BTree)malloc(sizeof(Btree_node)); (*root)->data = temp; (*root)->lchild = NULL; (*root)->rchild = NULL; first_create( &(*root)->lchild); first_create( &(*root)->rchild); } }
2、二叉樹的遍歷
先序:先遍歷根節點,再遍歷左子樹,最後遍歷右子樹
/*先序遍歷---遞迴*/ void first_traverse(BTree root) { if (root != NULL) { cout << root->data; first_traverse(root->lchild); first_traverse(root->rchild); } } /*先序遍歷--非遞迴*/ void first_traverse_no(BTree root) { stack<BTree> sk; while ( !sk.empty() || root != NULL)//棧非空 { if (root != NULL) { cout << root->data; sk.push(root); root = root->lchild; } else { root = sk.top(); sk.pop(); root = root->rchild; } } }
中序:先遍歷左子樹,再遍歷根節點,最後遍歷右子樹
/*中序遍歷---遞迴*/ void mid_traverse(BTree root) { if (root != NULL) { mid_traverse(root->lchild); cout << root->data; mid_traverse(root->rchild); } } /*中序遍歷---非遞迴*/ void mid_traverse_no(BTree root) { stack<BTree> sk; while (!sk.empty() || root != NULL) { if (root != NULL) { sk.push(root); root = root->lchild; } else { root = sk.top(); sk.pop(); cout << root->data; root = root->rchild; } } }
後序:先遍歷左子樹,再遍歷右子樹,最後遍歷根節點
void last_traverse(BTree root)
{
if (root != NULL)
{
last_traverse(root->lchild);
last_traverse(root->rchild);
cout << root->data;
}
}
層次遍歷:一層一層的遍歷
/*按層次遍歷*/
void level_traverse(BTree root)
{
queue<BTree> qe;
if (root != NULL)
qe.push(root);
while ( !qe.empty())
{
root = qe.front();
qe.pop();
cout << root->data;
if (root->lchild != NULL)
qe.push(root->lchild);
if (root->rchild != NULL)
qe.push(root->rchild);
}
}
3、二叉樹的節點的個數
int count_node_num(BTree root)
{
if (root == NULL)
return 0;
int numleft, numright, num;
numleft = count_node_num(root->lchild);
numright = count_node_num(root->rchild);
num = numleft + numright + 1;
return num;
}
4、二叉樹葉子節點的個數
葉子節點的特徵是既沒有左孩子也沒有右孩子,我們根據這個特徵在層次遍歷的過程中統計出葉子節點的個數
int get_leaf(BTree root)
{
int leaf = 0;//用來記錄樹中葉子節點的個數
queue<BTree> qu;
if (root == NULL)
return true;
qu.push(root);
BTree tmp;
while ( !qu.empty() )
{
tmp = qu.front();
qu.pop();
if (tmp->lchild != NULL || tmp->rchild != NULL)
{
if (tmp->lchild != NULL)
qu.push(tmp->lchild);
if (tmp->rchild != NULL)
qu.push(tmp->rchild);
}
else
leaf++;
}
return leaf;
}
5、二叉樹的高度
int cout_high(BTree root)
{
if (root == NULL)
return 0;
int left_high, right_high, high;
left_high = cout_high(root->lchild);
right_high = cout_high(root->rchild);
high = 1 + (left_high > right_high ? left_high : right_high);
return high;
}
6、二叉樹是否為平衡二叉樹
對於樹中的任意節點,其左右子樹的高度差不大於1。
int count_high(BTree root)
{
if (root == NULL)
return 0;
int left_high, right_high, high;
left_high = count_high(root->lchild);
right_high = count_high(root->rchild);
high = 1 + (left_high > right_high ? left_high : right_high);
return high;
}
bool banlance_tree(BTree root)
{
if (root == NULL)
return true;
int left_high, right_high,diff;
left_high = count_high(root->lchild);
right_high = count_high(root->rchild);
diff = left_high - right_high;
if (diff > 1 || diff < -1)
return false;
return (banlance_tree(root->lchild) && banlance_tree(root->rchild));
}
7、把二叉樹轉換成雙向連結串列
void Btree_to_list(BTree root,BTree *head)
{
if (root == NULL)//如果是棵空樹
{
*head = NULL;
return;
}
/*如果不是空樹則尋找此樹的最左邊的節點,作為連結串列的頭節點*/
BTree tmp = root;
while (tmp->lchild != NULL)
tmp = tmp->lchild;
*head = tmp;
BTree last_node = NULL;//用來記錄連結串列的最後一個節點
convert_node(root, &last_node);
}
void convert_node(BTree root, BTree *last_node)
{
if (root == NULL)
return;
/*先轉換樹的左子樹*/
if (root->lchild != NULL)
convert_node(root->lchild, last_node);
root->lchild = *last_node;
if (*last_node != NULL)
(*last_node)->rchild = root;
/*調整last_node指向連結串列的最後一個元素*/
*last_node = root;
/*對樹的右子樹進行調整*/
if (root->rchild != NULL)
convert_node(root->lchild, last_node);
}
8、二叉樹是否對稱
左右子樹同時遍歷,如果出現不一致,則不是對稱的
bool jude_tree(BTree root)
{
if (root == NULL)
return true;
return jude(root->lchild, root->rchild);
}
bool jude(BTree left, BTree right)
{
if (left != NULL && right != NULL)
{
if (jude(left->lchild, right->rchild) && jude(left->rchild, right->lchild))
return true;
return false;
}
else if (left == NULL && right == NULL)
return true;
else
return false;
}
9、兩個節點的最近公共祖父節點
最近公共祖先(Lowest Common Ancestor)指的是:給定一棵有根樹T和兩個節點x、y,找到一個距離T最遠的節點z,使得z即是x的祖父節點同時也是y的祖父節點。
BTree get_lowest_common_ancestor(BTree root, BTree a, BTree b)
{
if (root == NULL || root == a || root == b)
return root;
BTree left = get_lowest_common_ancestor(root->lchild, a, b);
BTree right = get_lowest_common_ancestor(root->rchild, a, b);
if (left && right)
return root;
return left ? left : right;
}
10、二叉樹中和為某一值的路徑
void get_path(BTree root, int target)
{
if (root == NULL)
return;
static int sum = 0;
static deque<BTree> qu;
/*如果當前路徑為目標值*/
if (sum + root->data == target)
{
/*打印出路徑*/
for (int i = 0; i < qu.size(); i++)
cout << qu[i] << "->";
cout << root->data << endl;
return;
}
else if (sum + root->data > target)
return;
else//當前路徑值小於目標值
{
qu.push_back(root);
sum += root->data;
get_path(root->lchild, target);
get_path(root->rchild, target);
qu.pop_back();
sum -= root->data;
}
}
11、求二叉樹中第K層的節點個數
int get_Klevel_nodenum(BTree root, int k)
{
if (root == NULL || k <= 0)
return 0;
if (k == 1)
return 1;
return(get_Klevel_nodenum(root->lchild, k - 1) + get_Klevel_nodenum(root->rchild, k - 1));
}
12、求二叉樹中兩個節點的最大距離
二叉樹中兩個節點的最大距離分為兩種情況:①路徑經過左子樹的最深節點,通過根節點,再經過右子樹的最深節點;②路徑不經過根節點,而是左子樹或右子樹的最大距離,取二者的最大值。
這個問題的核心是,情況①、②需要不同的資訊:情況①需要子樹的最大深度,情況②需要子樹的最大距離。如果程式能在同一個節點返回這兩個資訊,那麼演算法將會變得很簡單:
RESULT get_max_distance(BTree root)
{
if (root == NULL)
{
RESULT empty = { 0, -1 };
return empty;
}
RESULT rsl = get_max_distance(root->lchild);
RESULT rsr = get_max_distance(root->rchild);
RESULT resut;
resut.maxDepth = max(rsl.maxDepth, rsr.maxDepth) + 1;
resut.maxDistace = max(max(rsl.maxDistace, rsr.maxDistace), rsl.maxDepth + rsr.maxDepth + 2);
return resut;
}
13、判斷二叉樹是否為滿二叉樹
滿二叉樹的總節點個數和層數之間滿足如下關係:節點個數 = 2^K - 1(其中K為二叉樹的層數)。我們可以利用此關係來判斷是否為滿二叉樹。
bool jude_full(BTree root)
{
int level;;//用來記錄樹的層數
int node_num = 0;//用來記錄樹中節點的個數
queue<BTree> qu;
if (root == NULL)
return true;
qu.push(root);
BTree tmp;
while ( !qu.empty() )
{
tmp = qu.front();
qu.pop();
node_num++;
if (tmp->lchild != NULL)
qu.push(tmp->lchild);
if (tmp->rchild != NULL)
qu.push(tmp->rchild);
}
level = get_depth(root);
if (node_num == (pow(2, level) - 1))
return true;
return false;
}
14、判斷二叉樹是否為完全二叉樹
我們採用樹的按層次遍歷,那麼在遍歷的過程中,如果遇到沒有左孩子或右孩子的節點,我們設定標誌位,如果在後序的遍歷中遇到了有左孩子或右孩子的節點,那麼該樹不是完全二叉樹。
bool jude_full(BTree root)
{
if (root == NULL)
return true;
queue<BTree> qu;
qu.push(root);
BTree tmp;
int flag = 1;
while ( !qu.empty())
{
tmp = qu.front();
qu.pop();
if (tmp->lchild != NULL)
{
if (flag == 0)
return false;
qu.push(tmp->lchild);
}
else
flag = 0;
if (tmp->rchild != NULL)
{
if (flag == 0)
return false;
qu.push(tmp->rchild);
}
else
flag = 0;
}
return true;
}
15、根據前序和中序遍歷結果,重建二叉樹
void rebuild(const char *preOrder, const char *midOrder, int nodeNum, BTree *root)
{
if (preOrder == NULL || midOrder == NULL || nodeNum <= 0)
return;
/*用前序遍歷的第一個節點構造根節點*/
(*root) = new Btree_node;
(*root)->data = *preOrder;
(*root)->lchild = NULL;
(*root)->rchild = NULL;
/*如果樹中還剩一個節點則結束*/
if (nodeNum == 1)
return;
/*尋找子樹的長度*/
int tmpLen = 0;
const char *leftEnd = midOrder;
/*找到左子樹的結尾*/
while (*preOrder != *leftEnd)
{
tmpLen++;
/*檢查是否溢位*/
if (tmpLen > nodeNum)
return;
leftEnd++;
}
/*計算左子樹的節點個數*/
int leftNode = leftEnd - midOrder;
/*計算右子樹的節點個數*/
int rightNode = nodeNum - leftNode - 1;
/*重建左子樹*/
if (leftNode > 0)
rebuild( preOrder + 1, midOrder, leftNode, &((*root)->lchild) );
/*重建右子樹*/
if (rightNode > 0)
rebuild(preOrder + leftNode + 1, midOrder + leftNode + 1, rightNode, &((*root)->rchild));
}