二叉查找树通俗说就是左孩子比父亲小,右孩子比父亲大。构造这么一个树,树嘛,递归即可。
例如一棵树后序遍历是这样(下图的树):2 9 8 16 15 10 25 38 45 42 30 20。最后的20肯定是树根,这里要抓住一个规律:20是树根,那么2 9 8 16 15 10都是左子树,25 38 42 45 30在右子树,因为左边都小于根、右边都大于根嘛。然后递归即可。
下面是树的样子和代码和src.txt(后序遍历的结果)以及运行结果:
1 #include <iostream> 2 #include <vector> 3 #include <fstream> 4 5 using std::cin; 6 using std::cout; 7 using std::endl; 8 using std::vector; 9 10 #define MY_DEBUG 11 12 struct Node 13 { 14 int data; 15 Node* pLC; 16 Node* pRC; 17 }; 18 19 Node* creatBSTree(vector<int>& arr) 20 { 21 //数组里面没有元素 22 if (!arr.size()) 23 return nullptr; 24 25 Node* pNode = new Node; 26 int thisData = arr.back(); 27 pNode->data = thisData; 28 29 //只有一个元素就不要折腾了,它就是叶子节点,它没有左右孩子 30 if (1 == arr.size()) 31 { 32 pNode->pLC = pNode->pRC = nullptr; 33 return pNode; 34 } 35 36 //下面找出左半边 37 vector<int> arrLeft; 38 for (int i = 0; i < arr.size() - 1; i++) 39 { 40 if (arr[i] < thisData) 41 arrLeft.push_back(arr[i]); 42 else 43 break; 44 } 45 46 //下面找出右半边 47 vector<int> arrRight; 48 for (int i = arrLeft.size(); i < arr.size() - 1; i++) 49 arrRight.push_back(arr[i]); 50 51 #ifdef MY_DEBUG 52 for (int i = 0; i < arrLeft.size(); i++) 53 cout << arrLeft[i] << " "; 54 cout << endl; 55 56 for (int i = 0; i < arrRight.size(); i++) 57 cout << arrRight[i] << " "; 58 cout << endl << endl; 59 #endif 60 61 //递归处理左右孩子。arrLeft和arrRight可能为空,不要紧,在函数的开头处理了 62 pNode->pLC = creatBSTree(arrLeft); 63 pNode->pRC = creatBSTree(arrRight); 64 65 return pNode; 66 } 67 68 69 //中序遍历 70 void show(Node* pNode) 71 { 72 if (!pNode) 73 return; 74 75 show(pNode->pLC); 76 cout << pNode->data << " "; 77 show(pNode->pRC); 78 } 79 80 int main(void) 81 { 82 vector<int> arr; 83 std::ifstream fin("src.txt"); 84 85 int temp; 86 while (fin >> temp) 87 arr.push_back(temp); 88 89 Node* pHead = creatBSTree(arr); 90 show(pHead); 91 cout << endl; 92 cin.get(); 93 }
2 9 8 16 15 10 25 38 45 42 30 20
由其他的遍历方式得到树的道理类似。
题目:
输入一个整数数组,判断该数组是不是某二元查找树的后序遍历的结果。如果是返回true,否则返回false。
分析:
这不是很简单了嘛,由最后的根把输入分成三份,第一份是左孩子,第二份是右孩子,最后是树根。7、4、6、5就不能构成了,因为5是根,那么7,4,6都是右子树里面的,但是里面有小于5的4,所以不行。递归即可。
题目:
输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。要求不能创建任何新的结点,只调整指针的指向。
例如如果树如上图,那么得到的链表是 2=8=9=……=42=45。
分析:
树嘛,递归就是啦。
如上树,先递归处理左子树(根为10的树),处理完左子树就成了一个链表了,并要能够返回左子树的最大节点16;然后递归处理右子树(根为30的树),处理完右子树也成了一个有序链表,并返回右子树的最小节点25,然后把16、20、25串起来,不就是链表了嘛?
这里要注意一点:在处理根为20的树时,这是不是要递归处理根为10的左子树嘛,那么这个左子树怎么知道它要返回16节点呢?同样对于20的右子树,它怎么知道返回25以和20串起来呢?所以在处理子树的时候,要传给它一个标志,表明它是父亲的左子树还是右子树。我在这里纠结好久……
代码:
1 #include <iostream> 2 #include <set> 3 #include <fstream> 4 #include <queue> 5 6 using std::cin; 7 using std::cout; 8 using std::endl; 9 using std::set; 10 using std::vector; 11 12 struct Node 13 { 14 int data; 15 Node* pLC; 16 Node* pRC; 17 }; 18 19 Node* creatBSTree(vector<int>& arr) 20 { 21 //数组里面没有元素 22 if (!arr.size()) 23 return nullptr; 24 25 Node* pNode = new Node; 26 int thisData = arr.back(); 27 pNode->data = thisData; 28 29 //只有一个元素就不要折腾了,它就是叶子节点,它没有左右孩子 30 if (1 == arr.size()) 31 { 32 pNode->pLC = pNode->pRC = nullptr; 33 return pNode; 34 } 35 36 //下面找出左半边 37 vector<int> arrLeft; 38 for (int i = 0; i < arr.size() - 1; i++) 39 { 40 if (arr[i] < thisData) 41 arrLeft.push_back(arr[i]); 42 else 43 break; 44 } 45 46 //下面找出右半边 47 vector<int> arrRight; 48 for (int i = arrLeft.size(); i < arr.size() - 1; i++) 49 arrRight.push_back(arr[i]); 50 51 #ifdef MY_DEBUG 52 for (int i = 0; i < arrLeft.size(); i++) 53 cout << arrLeft[i] << " "; 54 cout << endl; 55 56 for (int i = 0; i < arrRight.size(); i++) 57 cout << arrRight[i] << " "; 58 cout << endl << endl; 59 #endif 60 61 //递归处理左右孩子。arrLeft和arrRight可能为空,不要紧,在函数的开头处理了 62 pNode->pLC = creatBSTree(arrLeft); 63 pNode->pRC = creatBSTree(arrRight); 64 65 return pNode; 66 } 67 68 //中序遍历 69 void show(Node* pNode) 70 { 71 if (!pNode) 72 return; 73 74 show(pNode->pLC); 75 cout << pNode->data << " "; 76 show(pNode->pRC); 77 } 78 79 //这个函数多传了一个参数 asLeft,表明树根是树根的左子树还是右子树 80 //因为如果是左子树的话,那么要返回左子树最大节点,右子树要返回最小节点 81 //不要这个标志的话 pNode 不知道指向的树到底是父亲的左子树还是右子树 82 Node* letStraight(Node* pNode, bool asLeft) 83 { 84 //如果是空树或者只是一个叶子节点,返回自身 85 if (!pNode || (!pNode->pLC && !pNode->pRC)) 86 return pNode; 87 88 //递归处理左右子树 89 Node* pLMax = nullptr; 90 if (pNode->pLC) 91 pLMax = letStraight(pNode->pLC, true); 92 93 Node* pRMin = nullptr; 94 if (pNode->pRC) 95 pRMin = letStraight(pNode->pRC, false); 96 97 //连接左子树、右子树、树根 98 if (pLMax) 99 { 100 pNode->pLC = pLMax; 101 pLMax->pRC = pNode; 102 } 103 if (pRMin) 104 { 105 pNode->pRC = pRMin; 106 pRMin->pLC = pNode; 107 } 108 109 //返回值处理,注意 asLeft 表明这棵树是它父亲的左子树还是右子树,所以它要重新找到合适的返回节点 110 // pNode 的左孩子最大或右孩子最小并不是它要返回的值,它要返回的还是要看这整棵树,而不是单单看左右孩子 111 Node* pRetutnNode = pNode; 112 if (asLeft) 113 { 114 while (pRetutnNode->pRC) 115 pRetutnNode = pRetutnNode->pRC; 116 } 117 else 118 { 119 while (pRetutnNode->pLC) 120 pRetutnNode = pRetutnNode->pLC; 121 } 122 return pRetutnNode; 123 } 124 125 int main(void) 126 { 127 vector<int> arr; 128 std::ifstream fin("src.txt"); 129 130 int temp; 131 while (fin >> temp) 132 arr.push_back(temp); 133 134 Node* pHead = creatBSTree(arr); 135 show(pHead); 136 cout << endl; 137 138 //顺序链表化并返回第一个节点 139 pHead = letStraight(pHead, false); 140 while (pHead) 141 { 142 cout << pHead->data << " "; 143 pHead = pHead->pRC; 144 } 145 cout << endl; 146 147 cin.get(); 148 }
结果: