二分木のアルゴリズム問題

目次

1. 非再帰形式により、前中後トラバーサルを実現

1.1 予約注文トラバーサル

1.2 ポストオーダー トラバーサル (2 つのスタックを使用)

1.3 ポストオーダー トラバーサル (スタックを使用) show 操作

1.4 順序通りのトラバーサル

編集

2. 二分木の層ごとの走査を実現する

1.キューを使用する

3. 二分木が最も多い層を数えます


二分木の構造:

class Node {
public:
	Node(int v) {
		value = v;
	}
public:
	int value;
	Node* left;
	Node* right;

};

1. 非再帰形式により、前中後トラバーサルを実現

1.1 予約注文トラバーサル

1.ポップアップしたらすぐに印刷する

2. 右があれば右を押す

3. 左の場合は、左を押します

void pre(Node* head) {
		cout << "前序遍历:" << endl;
		if (head != nullptr) {
			stack<Node*> stack;
			stack.push(head);
			while (!stack.empty()) {
				head = stack.top();
				stack.pop();
				cout << head->value << " ";
				if (head->right != nullptr) {
					stack.push(head->right);
				}
				if (head->left != nullptr) {
					stack.push(head->left);
				}
			}
		}
		cout << endl;
	}

1.2 ポストオーダー トラバーサル (2 つのスタックを使用)

頭の右左の順序  はおなじみ ですか?そうです、逆順トラバーサルじゃないですか。

1. 2 番目のスタック res にポップします。

2. 左の場合は、左を押します

3. 右があれば右を押す

4.最後に、すべてを解像度で印刷します

void pos(Node* head) {
		cout << "后序遍历:" << endl;
		if (head != nullptr) {
			stack<Node*> st1;
			stack<Node*> st2;
			st1.push(head);
			while (!st1.empty()) {
				head = st1.top();
				st1.pop();
				st2.push(head);
				if (head->left != nullptr) {
					st1.push(head->left);
				}
				if (head->right != nullptr) {
					st1.push(head->right);
				}	
			}
			while (!st2.empty()) {
				cout << st2.top()->value << " ";
				st2.pop();
			}
		}	
		cout << endl;
	}

1.3 ポストオーダー トラバーサル (スタックを使用) show 操作

1 つのスタックのみを使用して完了する

	void pos2(Node* h) {
		cout<<"后序遍历炫技版: "<<endl;
		if (h != nullptr) {
			stack<Node*> stack;
			stack.push(h);
			Node* c = nullptr;
			while (!stack.empty()) {
				c = stack.top();
				//说明这个c的左树都还没处理呢,肯定先要去处理左树
				if (c->left != nullptr && h != c->left && h != c->right) {
					stack.push(c->left);
				}
				//说明左树处理了,但右树没有处理,那就去处理右树
				else if (c->right != nullptr && h != c->right) {
					stack.push(c->right);
				}
				//左右树都处理完了,那就去处理头
				else {
					cout << stack.top()->value<<" ";
					stack.pop();
					h = c;//处理一个,那就让h记录刚处理的位置
				}
			}
		}
		cout << endl;
	}

1.4 順序通りのトラバーサル

 1.左ボーダー全体を順番にスタックにプッシュします

 2.1 実行できない場合はスタックをポップし、ポップされた右の木で実行を継続する

あなたはなぜこれをやっているのですか?

	void in(Node* cur) {
		cout<<"中序遍历 : "<<endl;
		if (cur != nullptr) {
			stack<Node*> stack;
			while (!stack.empty() || cur != nullptr) {
				if (cur != nullptr) {
					stack.push(cur);
					cur = cur->left;
				}
				else {
					cur = stack.top();
					stack.pop();
					cout << cur->value << " ";
					cur = cur->right;
				}
			}
		}
		cout << endl;
	}

2. 二分木の層ごとの走査を実現する

1.キューを使用する

ポップアップしたら印刷し、左の木に進み、右の木に入る

void level(Node* head) {
		if (head == nullptr) {
			return;
		}
		queue<Node*> queue;
		queue.push(head);
		
		while (!queue.empty()) {
			Node* cur = queue.front();
			cout << cur->value << " ";
			queue.pop();
			//System.out.println(cur.value);
			if (cur->left != nullptr) {
				queue.push(cur->left);
			}
			if (cur->right != nullptr) {
				queue.push(cur->right);
			}
		}
	}

Niuke の回答の方法を使用することもできます: ジグザグ印刷を実現する

  vector<vector<int> > levelOrder(TreeNode* root) {
        // write code here
        vector<vector<int>> vt;
        if(root==nullptr)
            return vt;
        
        //队列存储,进行层次遍历
        queue<TreeNode*> q;
        q.push(root);
        TreeNode* cur;
        while(!q.empty()){
            vector<int> temp;
            int n=q.size();
            for(int i=0;i<n;++i){
                cur=q.front();
                q.pop();
                temp.push_back(cur->val);
                if(cur->left)
                    q.push(cur->left);
                if(cur->right)
                    q.push(cur->right);
                
            }
            vt.push_back(temp);//一层都统计完之后再加入
        }
        return vt;
    }

3. 二分木が最も多い層を数えます

各レイヤーを印刷するだけでなく、レイヤーの開始位置と終了位置を検出することもできます

1. 地図を使う

int maxWidthUseMap(Node* head) {
		if (head == nullptr) {
			return 0;
		}
		queue<Node*> queue;
		queue.push(head);
		// key 在 哪一层,value
		unordered_map <Node*, int> levelMap;
		levelMap.insert(make_pair(head,1));

		int curLevel = 1; // 当前你正在统计哪一层的宽度
		int curLevelNodes = 0; // 当前层curLevel层,宽度目前是多少
		int Max = 0;
		while (!queue.empty()) {
			Node* cur = queue.front();
			queue.pop();

			int curNodeLevel = levelMap[cur];//当前的层数
			
			if (cur->left != nullptr) {
				levelMap.insert(make_pair(cur->left, curNodeLevel + 1));
				queue.push(cur->left);
			}
			if (cur->right != nullptr) {
				levelMap.insert(make_pair(cur->right, curNodeLevel + 1));
				queue.push(cur->right);
			}
			if (curNodeLevel == curLevel) {
				curLevelNodes++;
			}
			else {
				Max = max(Max, curLevelNodes);
				curLevel++;
				curLevelNodes = 1;//刚开始的时候下一层肯定是1
			}
		}
		Max = max(Max, curLevelNodes);
		return Max;
	}

2、地図なし

int maxWidthNoMap(Node* head) {
		if (head == nullptr) {
			return 0;
		}
		queue<Node*> queue;
		queue.push(head);
		Node* curEnd = head; // 当前层,最右节点是谁
		Node* nextEnd = nullptr; // 下一层,最右节点是谁
		int Max = 0;
		int curLevelNodes = 0; // 当前层的节点数
		while (!queue.empty()) {
			Node* cur = queue.front();
			queue.pop();

			if (cur->left != nullptr) {
				queue.push(cur->left);
				nextEnd = cur->left;
			}
			if (cur->right != nullptr) {
				queue.push(cur->right);
				nextEnd = cur->right;
			}
			curLevelNodes++;
			if (cur == curEnd) {
				Max = max(Max, curLevelNodes);
				curLevelNodes = 0;
				curEnd = nextEnd;
			}
		}
		return Max;
	}

質問: 二分木のノードが与えられた場合、その後続ノードを返します

 後続ノードは、彼が順番に通過する次のノードを参照します

1. 暴力:   

 最も簡単な方法は、親ノードが見つかるまで親を探し続け、次にトラバースして次のノードを見つけることです。

2. 後継者の元へ直行

インオーダトラバーサルなので、左頭右の順番に従わなければならず、

次に、ツリーに右サブツリーがある場合、その後続は直接右サブツリーの左端のサブツリーになります

 右のサブツリーがない場合は、サブツリーが親の左のサブツリーになる親子関係になるまで上に進みます (右のサブツリーの場合は上に進みます)。

 もちろん、最後のノードには後続ノードがないため、見つからないはずがないため、nullptr を返すだけです

コード:

Node* getSuccessorNode(Node* node) {
		if (node == nullptr) {
			return node;
		}
		if (node->right != nullptr) {
			return getLeftMost(node->right);
		}
		else { // 无右子树
			Node* parent = node->parent;
			while (parent != nullptr && parent->right == node) { // 当前节点是其父亲节点右孩子
				node = parent;									 // 那就继续向上去找
				parent = node->parent;
			}
			return parent;
		}
	}

	Node* getLeftMost(Node* node) {
		if (node == nullptr) {
			return node;
		}
		while (node->left != nullptr) {
			node = node->left;
		}
		return node;
	}

同様に、フロントドライブを探すのも逆です。

 このツリーはまったく存在しませんが、再帰でシミュレートしました

明確に指定されたツリーは、ツリーをまったく構築せずに印刷できます


 

おすすめ

転載: blog.csdn.net/flyingcloud6/article/details/128873987