二叉木-経典問題復習

12814 ワード

ノード定義

typedef struct Btree {
    int v;
    struct Btree *left;
    struct Btree *right;
}*Btree;

ノードの挿入

Btree insertNode(Btree root, int value) {
    Btree ptr=root;
    Btree tmpNode;
    Btree newNode = malloc(sizeof(Btree));
    newNode->v = value;
    newNode->left = NULL;
    newNode->right = NULL;
    
    if (ptr == NULL)
        return newNode;
    while (ptr != NULL) {
        tmpNode = ptr;
        if (ptr->v < value)
            ptr = ptr->right;
        else
            ptr = ptr->left;
    }
    if (tmpNode->v < value)
        tmpNode->right = newNode;
    else
        tmpNode->left = newNode;
    return root;
}

ツリーの作成

Btree BtreeCreate(){
    Btree root = NULL;
    int value;
    printf("Please insert nodes(end with zero) %d:
",&value); while (value != 0){ root = insertNode(root,value); scanf("%d",&value); } return root; }

ツリーのノードの数を求めます


再帰解法:(1)ツリーが空の場合、ノード数が0(2)ツリーが空でない場合、ツリーノード数=左サブツリーノード数+右サブツリーノード数+1
int getNodeNum(Btree root){
    if (root == NULL)
        return 0;
    return getNodeNum(root->left)+getNodeNum(root->right)+1;
}

ツリーの深さを求めます


再帰解法:(1)ツリーが空の場合、ツリーの深さが0(2)ツリーが空でない場合、ツリーの深さ=max(左サブツリーの深さ、右サブツリーの深さ)+1
int getDepth(Btree root){
        if (root ==NULL)
            return0;
        int maxLeft = getDepth(root->left);
        int maxRight = getDepth(root->right);
        return maxLeft

前シーケンス中シーケンス後シーケンス遍歴


ぜんじゅんかんかいほう


(1)ツリーが空の場合、空の操作(2)ツリーが空でない場合は、ルートノードにアクセスし、左サブツリーを先頭に、右サブツリーを先頭に移動
void preOrderTranverse(Btree root){
    if (root == NULL)
        return ;
    visit(root);
    preOrderTranverse(root->left);
    preOrderTranverse(root->right);
}

ちゅうかんじゅんかんかいほう


(1)二叉木が空の場合、空操作.(2)ツリーが空でない場合は、左サブツリーを中順に、ルートノードにアクセスし、右サブツリーを中順に
void inOrderTranverse(Btree root){
    if (root == NULL)
        return;
    inOrderTranverse(root->left);
    visit(root);
    inOrderTranverse(root->right);
}

アフタループ再帰解法


(1)ツリーが空の場合、空の操作(2)ツリーが空でない場合、左サブツリーを後に、右サブツリーを後に、ルートノードにアクセス
void postOrderTranverse(Btree root){
    if (root == NULL)
        return;
    postOrderTranverse(root->left);
    postOrderTranverse(root->right);
    visit(root);
}

階層はツリーを巡回します(階層によって左から上から下へ)


広さ優先探索に相当し,キューを用いて実現する.キューを初期化し、ルートノードをキューに押し込みます.キューが空でない場合は、ノードをポップアップし、アクセスし、左サブノードまたは右サブノードが空でない場合は、キューに押し込みます.
void levelTranverse(Btree root){
    if (root == NULL)
        return;
    queue treeQueue;
    treeQueue.push(root);
    while(treeQueue.empty()!=0){
        Btree tmp = treeQueue.front();
        treeQueue.pop();
        visit(tmp);
        if (tmp->left!=NULL)
            treeQueue.push(tmp->left);
        if (tmp->right!=NULL)
            treeQueue.push(tmp->right);
    }
}

ツリーが整列した双方向チェーンテーブルになる


新しいノードを作成できず、ポインタのみを調整する必要があります.再帰解法:(1)ツリー検索ツリーが空の場合、変換する必要はありません.対応する双方向チェーンテーブルの最初のノードはNULLで、最後のノードはNULL(2)ツリー検索ツリーが空でない場合:
左サブツリーが空の場合、対応する双方向秩序チェーンテーブルの最初のノードはルートノードであり、左側には他の操作は必要ありません.
左サブツリーが空でない場合、左サブツリーを変換します.二叉ルックアップツリーに対応する双方向秩序チェーンテーブルの最初のノードは、左サブツリー変換後の双方向秩序チェーンテーブルの最初のノードです.ルートノードと左サブツリー変換後の双方向秩序チェーン
テーブルの最後のノード接続;
右サブツリーが空の場合、対応する双方向秩序チェーンテーブルの最後のノードがルートノードであり、右側には他の操作は必要ありません.
右サブツリーが空でない場合、対応する双方向秩序チェーンテーブルの最後のノードは、ルートノードと右サブツリー変換後の双方向秩序チェーンテーブルの最初のノードを接続しながら、右サブツリー変換後の双方向秩序チェーンテーブルの最後のノードです.
Btree BtreeToList(Btree root){
	Btree head,tail;
	BtreeToListRwcusion(root,head,tail);
	return head;
}
void BtreeToListRecusion(Btree root, Btree head, Btree tail) {
	if (root == NULL){
		head = NULL;
		tail = NULL;
		return;
	}
	Btree lt,rt;
	BtreeToList(root->left,head,lt);
	BtreeToList(root->right,rt,tail);
	
	if (root->left != NULL){
		root->ledt = lt;
		lt->right = root;
	}
	else
		head = root;
	if (root->right != NULL){
		rt->left = root;
		root->right = rt;
	}
	else 
		root = tail;
}

ツリーの第K層のノードの数を求めます


再帰解法:(1)二叉木が空である場合またはk<1が0を返す(2)二叉木が空でなくk=1である場合、1を返す(3)二叉木が空でなくk>1である場合、左サブツリーにおけるk-1層のノード個数と右サブツリーk-1層のノード個数の和を返す
int getNodeNumKthLevel(Btree root, int k){
    if (root == NULL)
        return 0;
    if (k == 1)
        return 1;
    int leftNum = getNodeNumKthLevel(root->left,k-1);
    int rightNum = getNodeNumKthLevel(root->right,k-1);
    return leftNum + rightNum;
}

二叉木の葉の節点の個数を求めます


再帰解法:(1)二叉木が空であれば0を返す(2)二叉木が空でなく左右の子木が空であれば1を返す(3)二叉木が空でなく左右の子木が同時に空でなければ、左の子木に戻る葉ノードの数に右の子木における葉ノードの数を加える
int getAllLeafNodes(Btree root){
    if (root == NULL)
        return;
    if (root->left == NULL && root->right == NULL){
        return 1;
    }
    int leftNum = getAllLeafNodes(root->left);
    int rightNum = getAllLeafNodes(root->right);
    return leftNum + rightNum;
}

2本の二叉木が同じ構造かどうかを判断する


データの内容を考慮しない.同じ構造は、対応する左サブツリーと対応する右サブツリーが同じ構造であることを意味します.再帰解法:(1)2本の二叉木が空の場合、真に戻る(2)2本の二叉木が1本空の場合、もう1本が空でない場合、偽に戻る(3)2本の二叉木が空でない場合、対応する左の木と右の木が同じ構造で真に戻る場合、その他の偽に戻る
注意:Unixユーザーは以下のプログラムをコンパイルするにはヘッダファイルstdboolを追加する必要がある.h(C 99規格)
bool structIosmor(Btree rootFirst, Btree rootSecond){
    if (rootFirst == NULL && rootSecond == NULL)
        return true;
    if (rootFirst == NULL || rootSecond == NULL)
        return false;
    bool first = structIosmor(rootFirst->left,rootSecond->left);
    bool second = structIosmor(rootFirst->right,rootSecond->right);
    return first&&second;
}

ツリーがバランスツリーかどうかを判断する


再帰解法:(1)二叉木が空の場合、真を返す(2)二叉木が空でない場合、左サブツリーと右サブツリーがAVLツリーで、左サブツリーと右サブツリーの高さの差が1以下の場合、真を返す、その他の偽を返す
bool isAVL(Btree root, int height){
    if (root == NULL){
        height = 0;
        return true;
    }
    int heightLeft;
    bool resultLeft = isAVL(root->left,heightLeft);
    int heightRight;
    bool resultRight = isAVL(root->right,heightRight);

    if (resultLeft && resultRight && abs(heightLeft-heightRight)<=1){
        height = max(heightLeft,heightRight)+1;
        return true;
    }
    else{
        height = max(heightLeft,heightRight)+1;
        return false;
    }
}

ツリーのミラーリング


再帰解法:(1)ツリーが空の場合は、空に戻ります(2)ツリーが空でない場合は、左サブツリーと右サブツリーのミラーを求め、左サブツリーと右サブツリーを交換します.
Btree treeImage(Btree root){
    if (root == NULL)
        return  NULL;
    Btree leftImage = treeImage(root->left);
    Btree rightImage = treeImage(root->right);
    root->left = rightImage;
    root->right = leftImage;
    return root;
}

二叉木の中で2つのノードの最も低い共通の祖先のノードを求めます


再帰解法(効率が低すぎる):(1)2つのノードがそれぞれルートノードの左サブツリーと右サブツリーにある場合、ルートノードに戻る(2)2つのノードが左サブツリーにある場合、再帰は左サブツリーを処理する.両方のノードが右サブツリーにある場合は、右サブツリーを再帰的に処理します.
bool findNode(Btree root, Btree node){
    if (root == NULL || node == NULL)
        return false;
    if (root == node)
        return true;
    bool found = findNode(root->left,node);
    if(!found){
         found = findNode(root->right,node);
    }
    return found;
}

Btree findLastParent(Btree root, Btree node1, Btree node2){
   if (root == NULL || node1 == NULL || node2 == NULL){
        return NULL;
   }
   if (findNode(root->left,node1)){
        if (findNode(root->right,node2)){
            return root;       
         }
        else{
         return findLastParent(root->left,node1,node2);  
        }
   }
   else{
        if (findNode(root->left,node2))
            return root;
        else
            return findLastParent(root->right,node1,node2);
   }
}

非再帰解法:ルートノードから2つのノードへのパスを求めてから、対応するパスのノードを比較すればいいです.最後の同じノード、すなわち、ツリー内の最も低い共通の祖先ノードです.
bool GetNodePath(BinaryTreeNode * pRoot, BinaryTreeNode * pNode, 
                 list & path)
{
	if(pRoot == pNode)
	{	
		path.push_back(pRoot);
		return true;
	}
	if(pRoot == NULL)
		return false;
	path.push_back(pRoot);
	bool found = false;
	found = GetNodePath(pRoot->m_pLeft, pNode, path);
	if(!found)
		found = GetNodePath(pRoot->m_pRight, pNode, path);
	if(!found)
		path.pop_back();
	return found;
}
BinaryTreeNode * GetLastCommonParent(BinaryTreeNode * pRoot, BinaryTreeNode * pNode1, BinaryTreeNode * pNode2)
{
	if(pRoot == NULL || pNode1 == NULL || pNode2 == NULL)
		return NULL;
	list path1;
	bool bResult1 = GetNodePath(pRoot, pNode1, path1);
	list path2;
	bool bResult2 = GetNodePath(pRoot, pNode2, path2);
	if(!bResult1 || !bResult2) 
		return NULL;
	BinaryTreeNode * pLast = NULL;
	list::const_iterator iter1 = path1.begin();
	list::const_iterator iter2 = path2.begin();
	while(iter1 != path1.end() && iter2 != path2.end())
	{
		if(*iter1 == *iter2)
			pLast = *iter1;
		else
			break;
		iter1++;
		iter2++;
	}
	return pLast;
}

ツリーノードの最大距離を求める


すなわち、ツリーの中で最も遠い2つのノード間の距離です.再帰解法:最も遠い2点は必ずあるノードAをルートとするサブツリー上にあり、それらの間の経路は必ずそのサブツリーのルートノードAを通過する.したがって、いずれかのノードBをルートとするサブツリーは、そのサブツリールートノードBを通過する最大距離を算出すると、全ての最大距離の最大値が所望の二叉木の最大距離、すなわち「ツリーの直径」となる.一方、ツリーを通るルートノードの最大距離は、左サブツリーの高さ+右サブツリーの高さ+2(空のノードの高さを-1と仮定)であるため、元の問題は「各ノードの左サブツリーと右サブツリーの高さの和を計算し、最大値をとる」ことに等しい.C言語では参照はサポートされていません.C++をサポートするコンパイラが必要です.
int treeDepth(Btree root,int& maxDistance){
    if (root == NULL)
        return -1;
    int leftDepth = treeDepth(root->left,maxDistance) + 1;
    int rightDepth = treeDepth(root->right,maxDistance) + 1;
    int distance = leftDepth + rightDepth;
    if (maxDistance < distance)
        maxDistance = distance;
    return max(leftDepth,rightDepth);
}
int findTreeDiameter(Btree root){
    if (root == NULL)
        return 0;
    int maxDistance = 0;
    treeDepth(root,maxDistance);
    return maxDistance;
}

前シーケンスループと中シーケンスループからツリーを再構築するには


ツリーの前順序ループシーケンスでは、最初の要素は常にツリーのルートノードの値です.中順遍歴シーケンスでは、左サブツリーのノードの値はルートノードの値の左側にあり、右サブツリーのノードの値はルートノードの値の右側にあります.再帰解法:(1)前シーケンスが空または中シーケンスで空またはノードの個数が0以下である場合、NULLを返します.(2)ルートノードを作成する.前順遍歴の最初のデータはルートノードのデータであり、中順遍歴でルートノードの位置を見つけ、左サブツリーと右サブツリーの前順と中順遍歴シーケンスをそれぞれ知り、左右のサブツリーを再構築することができる.
BinaryTreeNode * RebuildBinaryTree(int* pPreOrder, int* pInOrder, int nodeNum)
{
	if(pPreOrder == NULL || pInOrder == NULL || nodeNum <= 0)
		return NULL;
	BinaryTreeNode * pRoot = new BinaryTreeNode;
	//  
	pRoot->m_nValue = pPreOrder[0];
	pRoot->m_pLeft = NULL;
	pRoot->m_pRight = NULL;
	//  , , , 
	int rootPositionInOrder = -1;
	for(int i = 0; i < nodeNum; i++)
		if(pInOrder[i] == pRoot->m_nValue)
		{
			rootPositionInOrder = i;
			break;
		}
	if(rootPositionInOrder == -1)
	{
		throw std::exception("Invalid input.");
	}
	//  
	int nodeNumLeft = rootPositionInOrder;
	int * pPreOrderLeft = pPreOrder + 1;
	int * pInOrderLeft = pInOrder;
	pRoot->m_pLeft = RebuildBinaryTree(pPreOrderLeft, pInOrderLeft, nodeNumLeft);
	//  
	int nodeNumRight = nodeNum - nodeNumLeft - 1;
	int * pPreOrderRight = pPreOrder + 1 + nodeNumLeft;
	int * pInOrderRight = pInOrder + nodeNumLeft + 1;
	pRoot->m_pRight = RebuildBinaryTree(pPreOrderRight, pInOrderRight, nodeNumRight);
	return pRoot;
}

二叉木が完全に二叉木かどうかを判断する


二叉木の深さをhとすると、第h層を除いて、他の各層(1〜h−1)の結点数が最大個数に達し、第h層のすべての結点が連続して最左に集中し、これが完全二叉木である.階層(上から下、左から右)でツリーを巡り、ノードに遭遇した左サブツリーが空の場合、ノードの右サブツリーが空でなければなりません.また、後ろを通るノードの左右のサブツリーが空でなければなりません.そうしないと、完全なツリーではありません.
bool IsCompleteBinaryTree(BinaryTreeNode * pRoot)
{
	if(pRoot == NULL)
		return false;
	queue q;
	q.push(pRoot);
	bool mustHaveNoChild = false;
	bool result = true;
	while(!q.empty())
	{
		BinaryTreeNode * pNode = q.front();
		q.pop();
		if(mustHaveNoChild) //  , ( )
		{
			if(pNode->m_pLeft != NULL || pNode->m_pRight != NULL)
			{
				result = false;
				break;
			}
		}
		else
		{
			if(pNode->m_pLeft != NULL && pNode->m_pRight != NULL)
			{
				q.push(pNode->m_pLeft);
				q.push(pNode->m_pRight);
			}
			else if(pNode->m_pLeft != NULL && pNode->m_pRight == NULL)
			{
				mustHaveNoChild = true;
				q.push(pNode->m_pLeft);
			}
			else if(pNode->m_pLeft == NULL && pNode->m_pRight != NULL)
			{
				result = false;
				break;
			}
			else
			{
				mustHaveNoChild = true;
			}
		}
	}
	return result;
}