數的遍歷有4種,分別是前序遍歷,中序遍歷和後序遍歷、層次遍歷。
前序遍歷:根左右
中序遍歷:左根右
後序遍歷:左右根
層次遍歷:第一層節點從左到右,第二層節點從左到右,以此類推。
分享下我怎麼記憶的,只需要記得第一個字表示根的位置,就不會搞混了。另外還要知道,如果只知道前序和後序,是不能唯一確定一棵樹的。
對於每種遍歷,又存在遞歸式和非遞歸式。遞歸式當然好實現,只要簡單的幾行代碼就可以實現;而非遞歸式呢,需要花點功夫,另外非遞歸式的後序遍歷還有點小麻煩。下面來直接看代碼吧
樹的結構體
struct BinaryTreeNode{
int value;
BinaryTreeNode *pLeft;
BinaryTreeNode *pRight;
};
遞歸式
遞歸式前序遍歷
void preOrder(BinaryTreeNode* root){
if(root==NULL){
return ;
}
else{
cout<<root->value;
preOrder(root->pLeft);
preOrder(root->pRight);
}
}
遞歸式中序遍歷
void InOrder(BinaryTreeNode* root){
if(root==NULL){
return ;
}
else{
InOrder(root->pLeft);
cout<<root->value;
InOrder(root->pRight);
}
}
遞歸式後序遍歷
void postOrder(BinaryTreeNode* root){
if(root==NULL){
return ;
}
else{
postOrder(root->pLeft);
postOrder(root->pRight);
cout<<root->value;
}
}
總結:可以看到,我們只需要短短的幾行代碼就可以實現。只需要記住前序遍歷、中序遍歷和後序遍歷的遍歷方式即可。
非遞歸式
非遞歸式前序遍歷
void PreOrder1(BinaryTreeNode* root){
if (root==NULL){
return;
}
BinaryTreeNode *node = root;
stack<BinaryTreeNode*>nodelist;
while(!nodelist.empty()||node!=NULL){
while(node!=NULL){
nodelist.push(node);
cout<<node->value;
node = node->pLeft;
}
node = nodelist.top();
nodelist.pop();
node = node->pRight;
}
}
非遞歸式中序遍歷
void InOrder1(BinaryTreeNode* root){
if (root==NULL){
return;
}
BinaryTreeNode *node = root;
stack<BinaryTreeNode*>nodelist;
while(!nodelist.empty()||node!=NULL){
while(node!=NULL){
nodelist.push(node);
node = node->pLeft;
}
node = nodelist.top();
cout<<node->value;
nodelist.pop();
node = node->pRight;
}
}
總結:
非遞歸式的前序和中序很相像,他們的區別在於修改了輸出value的位置。原因在於前序是先輸出根的值,再輸出左節點的值;而中序是先輸出左節點的值,再輸出根的值。
非遞歸式後序遍歷
void PostOrder1(BinaryTreeNode* root){
if (root==NULL){
return;
}
BinaryTreeNode *node = root;
stack<BinaryTreeNode*>nodelist;
BinaryTreeNode *pLastVisted = NULL;
while(node!=NULL){
nodelist.push(node);
node = node->pLeft;
}//先把所有的左節點放進去
while(!nodelist.empty()){
node = nodelist.top();
nodelist.pop();//取出棧頂節點
if(node->pRight==NULL || node->pRight==pLastVisted){//如果右子樹爲空或者右節點已經訪問過了
cout<<node->value;
pLastVisted=node;
}else{
nodelist.push(node);//先將節點放回去
node = node->pRight;//取右節點
while(node){
nodelist.push(node);
node = node->pLeft;//放入左節點
}
}
}
}
總結:非遞歸式後序遍歷用到了兩個指針,其中一個要記錄上一次遍歷的那個節點。由於先輸出左右節點的值,再輸出根的值,因此在右節點還沒有遍歷的時候我們先將跟節點放回去,先遍歷右子樹,當右子樹都遍歷完了,再對根節點進行遍歷。
最後附上我的main函數,一個簡單建樹的過程。
int main(){
//initial
BinaryTreeNode *node1 = new BinaryTreeNode();
BinaryTreeNode *node2 = new BinaryTreeNode();
BinaryTreeNode *node3 = new BinaryTreeNode();
BinaryTreeNode *node4 = new BinaryTreeNode();
BinaryTreeNode *node5 = new BinaryTreeNode();
BinaryTreeNode *node6 = new BinaryTreeNode();
node1->value = 1;
node1->pLeft = node2;
node1->pRight = node3;
node2->value = 2;
node2->pLeft = node4;
node2->pRight = node5;
node3->value = 3;
node3->pLeft = node6;
node3->pRight = NULL;
node4->value = 4;
node4->pLeft = NULL;
node4->pRight = NULL;
node5->value = 5;
node5->pLeft = NULL;
node5->pRight = NULL;
node6->value = 6;
node6->pLeft = NULL;
node6->pRight = NULL;
PreOrder1(node1);
cout<<endl;
InOrder1(node1);
cout<<endl;
PostOrder1(node1);
cout<<endl;
return 0;
}
層次遍歷
一般我們實現先序遍歷、中序遍歷和後序遍歷都用的是stack這個數據結構,而在層次遍歷中,我們用queue來實現。
···
vector< vector > levelOrder(TreeNode* root) {
vector< vector > result;
if(root==NULL)
return result;
queue< TreeNode* > que;
que.push(root);
while(!que.empty()){
vector<int> tmp;
int size = que.size();
for(int i=0;i< size;i++){
TreeNode* node = que.front();
que.pop();
tmp.push_back(node->val);
if(node->left){
que.push(node->left);
}
if(node->right){
que.push(node->right);
}
}
result.push_back(tmp);
}
return result;
}
···