二叉树:是一种有层次的存储数据的结构;可以把查找的复杂度降低到logn;
struct node{
typename data;//数据域
node* lchild;
node* rchild;
node(int x):data(x),lchild(NULL),rchild(NULL){}
};
node* newnnode(typename x){//新建结点
node* Node=new node;
Node->data=v;
Node->lchild=Node->rchild=NULL;
return Node;//生成一个结点并返回
}
void insert(node*&,data){//注意&
if(root=NULL) {
root=newNode(x);
return root ;
}
if(case1) insert(root->lchild,data);//按条件插入到左子树
else insert(root->rchild,data);//或右子树
}
node* Create(int data[],int n){//新建二叉树
node*root=NULL;
for(int i=0;i<n;i++)
insert(root, data[i]);
return root;
}
区别
root=NULL:指针是不是空
*root=NULL:指针指向的位置是不是空
遍历
递归
三要素:参数和返回类型;递归终点;单层逻辑;
后序遍历:每个子结点可以是父结点;把子结点当父结点看,先递归穷尽其左子树,再走尽其右子树,最后是它;
//先序遍历
void preorder(node*root ){
if(node=NULL) return ;
printf("%d\n",root->data);
preorder(root->lchild);
preorder(root->rchild);
}
//非递归的先根遍历
void Order( Node* t){
stack S;
Node*p=t;
while(1){
while(p!=NULL){
printf("%d",p->data);//先根序列 :进栈序列
S.push(p);
p=p->lchild;
}
if(S.Isempty()) return ;
p=S.pop();
// printf("%d",p->data); 中根序列:出栈序列
p=p->rchild;
}
return ;
}
//中序遍历
//只有中序遍历可以完整的确定一颗二叉树
void preorder(node*root ){
if(node=NULL) return ;
preorder(root->lchild);
printf("%d\n",root->data);
preorder(root->rchild);
}
//后序遍历
//特点:最后一个一定是根节点;
void postorder(node*root ){
if(node=NULL) return ;
preorder(root->lchild);
printf("%d\n",root->data);
preorder(root->rchild);
}
迭代
把顺序访问到的数据存储在栈中,通过修改弹栈顺序实现 先根/中根/后根遍历
先根遍历:处理顺序和访问顺序一致
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;
if (root == NULL) return result;
st.push(root);
while (!st.empty()) { //终止状态
TreeNode* node = st.top();
st.pop(); //先把中间的树存储到结果中,并弹栈
result.push_back(node->val);
if (node->right) st.push(node->right); // 右 先入栈,后出栈
if (node->left) st.push(node->left); // 左
}
return result;
}
中根遍历:处理顺序和访问顺序不一致,需要指针
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
TreeNode* cur = root;
while (cur != NULL || !st.empty()) {
if (cur != NULL) { // 指针来访问节点,访问到最底层
st.push(cur); // 将访问的节点放进栈
cur = cur->left; // 左
} else {
cur = st.top(); // 从栈里弹出的数据,就是要处理的数据(放进result数组里的数据)
st.pop();
result.push_back(cur->val); // 中
cur = cur->right; // 右
}
}
return result;
}
问题:如何已知先序序列和中序序列重建二叉树?
思路:
找到pre1=intk;即中序遍历的根;对中序遍历,[1,k-1]的结点都是是左子树,[k+1,n]是右子树;
对先序遍历来说,(1,k]个结点对应左子树, [k+1,n]对应右子树;
对左/右子树,继续寻找k';直到左/右子树为空
//先序序列区间 [preL,preR],中序序列 [inL,inR]
node* Create(int preL,int preR,int inL,int inR){
if(preL>preR) return NULL;//
node* root=new node;
root->data=pre[preL];//当前根节点的值
for(int k=inL,k<=inR;i++){
if(in[k]==pre[preL]) break;//找到中序遍历的根结点位置
}
int numleft=k-inL;
root->lchild=Create(preL+1,preL+numleft,inL,k-1);
root->rchild=Create(preL+numleft+1,preR,k+1,inR );
return root;
}
结论:
中序遍历可以和其他任意顺序一起确定一颗二叉树;必须有中序遍历来区分左/右子树
而后序遍历,前序遍历,层序遍历中任意2/3个都无法构成二叉树!
问题2:
-先序序列(前序序列)为a,b,c,d 的不同二叉树的个数 / n个结点的二叉树有多少种的形态?
-栈混洗总数的数量?
思路:已知,前序序列和中序序列可以唯一地确定一棵二叉树。
把前序序列看作为入栈次序,把中序序列看作为出栈次序
那么题意相当于“以序列 a,b,c,d 为入栈次序,则出栈序列的个数为?”
一个入栈顺序可以确定的出栈顺序为 C(2n,n) / (n+1)(卡特兰数)。
层序遍历、分层存储的两种方式
int depth(TreeNode *root)
{
if (root == NULL)
return 0;
return max(depth(root->left), depth(root->right)) + 1;
}
void levelOrder(vector<vector<int>> &ans, TreeNode *node, int level)
{
if (!node)
return;
ans[level].push_back(node->val);
levelOrder(ans, node->left, level - 1);
levelOrder(ans, node->right, level - 1);
}
vector<vector<int>> levelOrderBottom(TreeNode *root)
{
int d = depth(root);
vector<vector<int>> ans(d, vector<int>{});
levelOrder(ans, root, d - 1);
return ans;
}
vector<vector<int>> levelOrder(TreeNode *root)
{
vector<vector<int>> ret;
if (!root)
return ret;
queue<TreeNode *> q;
q.push(root);
while (!q.empty())
{
int currNodeSize = q.size();
ret.push_back(vector<int>());
for (int i = 1; i <= currNodeSize; i++)
{
TreeNode *node = q.front();
q.pop();
ret.back().push_back(node->val);
if (node->left)
q.push(node->left);
if (node->right)
q.push(node->right);
}
}
return ret;
}