這不是我的面試題,是一個同學在百度的面試題。
要求將一顆二叉樹通過網絡傳輸到給另一個客戶端,並且在該客戶端恢復爲原始二叉樹。
這道題目可以理解爲如何將一顆二叉樹存儲到文件中,並且讀取後正確恢復。
以這樣的一棵二叉樹爲例:
我想到了三種解決方法:
1. 二叉樹補全法,將這課二叉樹補全,變成一顆完全二叉樹,再使用數組進行存儲,寫入文件中。這樣做需要在節點中增加一個屬性,標記是否爲補全的節點。
這種方法不太合理,因爲使用了補全操作,對於一顆很不規則的二叉樹,將會佔用非常大的存儲空間,並且修改了二叉樹的屬性。
2. 遊標實現法。定義一個新的結構體,其中的left和right指針修改爲結構體在數組中的位置。
就像下面這樣,數組的第一個位置表示NULL位置,剩餘的存放節點,left和right分別指向左右子節點所在數組索引。這是前序遍歷遞歸調用得到的數組。
3. 二叉樹位置描述實現。同2類似,不過這裏沒有左右子節點的指針,而是用一個整形來描述當前節點在一顆完全二叉樹中的位置。顯然,在上圖這樣的二叉樹中,節點1的位置爲1,節點2的位置爲2,節點3的位置爲3,節點4的位置爲4,節點5的位置爲6,依次類推。。。
依次,可以定義一個新的結構體描述節點信息,用於存儲。
- typedef struct BTreeNodeFile {
- Element e; //節點值
- Position p; //節點在完全二叉樹中的位置
- } BTreeNodeFile;
於是可以前序遍歷遞歸調用,得到這樣的一個數組。
恢復的時候,查找左右子節點,只需要查找p值2倍於自身以及2倍+1於自身的節點。
下面就是對與方法3的C++源碼。
- /*
- * tree.h
- *
- * Created on: 2011-3-31
- * Author: boyce
- */
- #ifndef TREE_H_
- #define TREE_H_
- typedef int Element;
- typedef struct BTreeNode{
- Element e;
- BTreeNode *left;
- BTreeNode *right;
- }BTreeNode;
- typedef struct BTree{
- BTreeNode *root;
- unsigned int count;
- }BTree;
- typedef BTreeNode* BTreeNodePtr;
- typedef BTree* BTreePtr;
- #endif /* TREE_H_ */
- /*
- * main.cpp
- *
- * Created on: 2011-3-31
- * Author: boyce
- */
- #include <iostream>
- #include <map>
- #include <stdio.h>
- #include <string.h>
- #include "tree.h"
- using namespace std;
- typedef int Position;
- typedef struct BTreeNodeFile {
- Element e; //節點值
- Position p; //節點在完全二叉樹中的位置
- } BTreeNodeFile;
- typedef map<int, BTreeNodePtr> NodeMap;
- const char fileName[] = "btree.dat";
- FILE *filePtr;
- void writeNode(const BTreeNodePtr btn, Position p) {
- if (!btn) {
- return;
- }
- BTreeNodeFile node;
- node.e = btn->e;
- node.p = p;
- //寫入當前節點
- fwrite(&node, sizeof(node), 1, filePtr);
- //寫入左子樹
- writeNode(btn->left, 2 * p);
- //寫入右子樹
- writeNode(btn->right, 2 * p + 1);
- }
- void writeBTree(const BTreePtr bt) {
- filePtr = fopen(fileName, "w");
- fwrite(&bt->count, sizeof(bt->count), (size_t) 1, filePtr); //寫入節點個數
- writeNode(bt->root, 1); //寫入節點
- fclose(filePtr);
- }
- BTreePtr readBTree() {
- BTreePtr bt = new BTree;
- NodeMap mapNode;
- BTreeNodeFile btnf;
- BTreeNode *btn;
- filePtr = fopen(fileName, "r");
- fread(&(bt->count), sizeof(bt->count), 1, filePtr); //讀入結點個數
- while (fread(&btnf, sizeof(btnf), (size_t) 1, filePtr) > 0) {
- btn = new BTreeNode;
- btn->e = btnf.e;
- mapNode.insert(NodeMap::value_type(btnf.p, btn));
- }
- NodeMap::iterator iter;
- NodeMap::iterator iter_t;
- for (iter = mapNode.begin(); iter != mapNode.end(); iter++) {
- iter_t = mapNode.find(2 * iter->first);
- if (iter_t != mapNode.end()) { //找到左兒子
- iter->second->left = iter_t->second;
- } else { //未找到左兒子
- iter->second->left = NULL;
- }
- iter_t = mapNode.find(2 * iter->first + 1);
- if (iter_t != mapNode.end()) { //找到右兒子
- iter->second->right = iter_t->second;
- } else { //未找到右兒子
- iter->second->right = NULL;
- }
- }
- iter_t = mapNode.find(1); //找root節點
- if (iter_t != mapNode.end()) {
- bt->root = iter_t->second;
- }
- fclose(filePtr);
- return bt;
- }
- BTreePtr buildBTree() {
- BTreePtr bt = new BTree;
- BTreeNodePtr btn = new BTreeNode[9];
- for (int i = 0; i < 9; i++) {
- memset(&btn[i], 0, sizeof(BTreeNode));
- btn[i].e = i;
- }
- btn[0].left = &btn[1];
- btn[1].left = &btn[3];
- btn[2].left = &btn[4];
- btn[5].left = &btn[7];
- btn[0].right = &btn[2];
- btn[2].right = &btn[5];
- btn[4].right = &btn[6];
- btn[5].right = &btn[8];
- bt->root = &btn[0];
- bt->count = 9;
- return bt;
- }
- void printSubBTree(BTreeNodePtr btn, int lvl) {
- int i;
- if (!btn)
- return;
- for (i = 0; i < lvl; i++)
- printf(" ");
- printf("%d/n", btn->e + 1);
- printSubBTree(btn->left, lvl + 1);
- printSubBTree(btn->right, lvl + 1);
- }
- void printBTree(BTreePtr bt) {
- printSubBTree(bt->root, 0);
- }
- int main() {
- BTreePtr bt = buildBTree();
- printBTree(bt);
- writeBTree(bt);
- bt = readBTree();
- printBTree(bt);
- return 0;
- }
這是該程序的輸出結果: