第九篇博客——数据结构2
第九篇博客——数据结构二
二叉树
一般地,二叉树的定义:
struct TreeNode{ char data; TreeNode *lc; TreeNode *rc; TreeNode(char c):data(c),lc(NULL),rc(NULL){} }; //构造函数不可以少
深度优先遍历方法有前序、中序和后序之分。其中知道中序遍历次序和前/后序遍历次序是可以还原出二叉树的形状的。一般构建二叉树使用x序和#符号来建立。
例题1:编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以指针方式存储)。 例如如下的先序遍历字符串: ABC##DE#G##F### 其中“#”表示的是空格,空格字符代表空树。建立起此二叉树以后,再对二叉树进行中序遍历,输出遍历结果。
先序序列 ABC##DE#G##F### 对应的二叉树如下:
#include <iostream> #include<cstdio> #include<string> using namespace std; struct TreeNode{ char data; TreeNode* lc; TreeNode* rc; TreeNode(char c):data(c),lc(NULL),rc(NULL){} }; /* 先序建立二叉🌲的思路和先序遍历的差不多, 先new TreeNode,然后build左子树和右子树, 返回的就是当下新建的root节点 */ TreeNode* Build(int& position,string str){ char c=str[position++]; if(c=='#') return NULL; TreeNode* root=new TreeNode(c); root->lc=Build(position, str); root->rc=Build(position, str); return root; } void InOrder(TreeNode* root){ if(root==NULL) return; InOrder(root->lc); cout<<root->data<<' '; InOrder(root->rc); return; } int main(){ string str; while(cin>>str){ int position=0; TreeNode* root=Build(position,str); InOrder(root); cout<<endl; } return 0; }
例题2:依据前序和中序序列推出后序序列。二叉树的前序、中序、后序遍历的定义: 前序遍历:对任一子树,先访问跟,然后遍历其左子树,最后遍历其右子树; 中序遍历:对任一子树,先遍历其左子树,然后访问根,最后遍历其右子树; 后序遍历:对任一子树,先遍历其左子树,然后遍历其右子树,最后访问根。 给定一棵二叉树的前序遍历和中序遍历,求其后序遍历(提示:给定前序遍历与中序遍历能够唯一确定后序遍历)。
输入描述:
两个字符串,其长度n均小于等于26。
第一行为前序遍历,第二行为中序遍历。
二叉树中的结点名称以大写字母表示:A,B,C....最多26个结点。
#include<iostream> #include<cstdio> #include<string> using namespace std; struct TreeNode{ char data; TreeNode *lc; TreeNode *rc; TreeNode(char c):data(c),lc(NULL),rc(NULL){} }; //build函数的第一个参数是前序,第二个参数是中序 TreeNode* Build(string str1,string str2){ //如果前序序列是0,那么就可以返回null if(str1.size()==0) return NULL; //定义临时参数c=前序序列的第一个字符,也就是当前的root char c=str1[0]; TreeNode *root=new TreeNode(c); //寻找中序序列中的根root int pos=str2.find(c); //左子树的前序序列就是str1的1到pos;中序序列是str2的0到pos-1 //右子树就是从pos+1到末尾 root->lc=Build(str1.substr(1,pos), str2.substr(0,pos)); root->rc=Build(str1.substr(pos+1), str2.substr(pos+1)); //返回根root return root; } int PostOrder(TreeNode* root){ if(root==NULL){ return 0; } PostOrder(root->lc); PostOrder(root->rc); cout<<root->data; return 0; } int main(){ string str1,str2; while(cin>>str1>>str2){ TreeNode* root=Build(str1,str2); PostOrder(root); cout<<endl; } return 0; }
二叉排序树
二叉排序树的左子树和右子树都是二叉排序树,左子树一定小于根节点,右子树一定大于根节点。
练习:按顺序插入一个初始为空树的二叉排序树中,每次插入后成功后,求相应的父亲节点的关键字值,如果没有父亲节点,则输出-1。
#include<iostream> #include<cstdio> using namespace std; struct TreeNode{ int data; TreeNode *lc; TreeNode* rc; TreeNode(int x):data(x),lc(NULL),rc(NULL){} }; TreeNode *Insert(TreeNode* root,int x,int father){ if(root==NULL){ root=new TreeNode(x); cout<<father<<endl; } else if(x<root->data) root->lc=Insert(root->lc, x, root->data); else root->rc=Insert(root->rc, x, root->data); return root; } int main(){ int n; while(cin>>n){ TreeNode* root=NULL; for(int i=0;i<n;i++){ int x; cin>>x; root=Insert(root, x, -1); } } return 0; }
练习:输入一系列整数,建立二叉排序树,并进行前序,中序,后序遍历。
#include <iostream> #include <cstdio> using namespace std; struct node { int val; node *left, *right; node(int v):val(v),left(NULL),right(NULL){} }; void insert(node *&root, int x) { if (root == NULL) { root = new node(x); return; } if (root->val == x) { return; } if (x < root->val) { insert(root->left, x); } else { insert(root->right, x); } } void preOrder(node *root) { if (root == NULL) { return; } printf("%d ", root->val);//首先访问当前节点 preOrder(root->left);//然后访问左右节点 preOrder(root->right); } void inOrder(node *root) { if (root == NULL) { return; } inOrder(root->left); printf("%d ", root->val); inOrder(root->right); } void postOrder(node *root) { if (root == NULL) { return; } postOrder(root->left); postOrder(root->right); printf("%d ", root->val); } int main() { int n; while (cin >> n) { node *root = NULL; while (n--) { int x; cin >> x; insert(root, x); } preOrder(root); printf("\n"); inOrder(root); printf("\n"); postOrder(root); printf("\n"); } }
练习:二叉搜索树。判断两个序列是否是同一个二叉搜索树序列(浙大上机题)
#include <iostream> #include <cstdio> #include <string> using namespace std; struct node{ char val; node *lc,*rc; node(int c):val(c),lc(NULL),rc(NULL){} }; bool flag; node *insert(node *root,int x){ if(root==NULL){ root=new node(x); return root; } if(root->val>x) root->lc=insert(root->lc, x); else root->rc=insert(root->rc, x); return root; } void check(node* root,node*judge){ if(root&&judge){ if(root->val!=judge->val) flag=false; check(root->lc, judge->lc); check(root->rc, judge->rc); } else if(root||judge) flag=false; } int main(){ int n; while(cin>>n){ node *root=NULL; string s; cin>>s; for(int i=0,len=s.length();i<len;i++) root=insert(root, s[i]); for(int i=0;i<n;i++){ flag=true; node *judge=NULL; cin>>s; for(int i=0,len=s.length();i<len;i++) judge=insert(judge, s[i]); check(root,judge); if(flag) cout<<"YES"<<endl; else cout<<"NO"<<endl; } } }
优先队列
在优先队列中每个元素都被赋予了优先级,每次访问元素的时候只能访问当前队列中优先级最高的元素。
定义一个优先队列的模版
priority_queue<Type, Container, Functional> name;
Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。STL里面默认用的是vector),Functional 就是比较的方式。
当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大顶堆。
值得注意的地方,无论如何,每次访问元素的时候只能访问当前队列中优先级最高的元素,如果涉及到自定义的大小,比如用时最短的优先级最大,结构体自定义优先级等。这些情况下需要重载运算符。
//升序队列,小顶堆 priority_queue <int,vector<int>,greater<int> > q; //降序队列,大顶堆 priority_queue <int,vector<int>,less<int> >q; //greater和less是std实现的两个仿函数(就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了)
优先队列的应用——哈夫曼树
#include<iostream> #include<cstdio> #include<queue> using namespace std; int main(){ int n; while(cin>>n){ priority_queue<int,vector<int>,greater<int>> myq; while(n--){ int x; cin>>x; myq.push(x); } int ans=0; while(1<myq.size()){ int a=myq.top(); myq.pop(); int b=myq.top(); myq.pop(); ans+=a+b; myq.push(a+b); } cout<<ans<<endl; } return 0; }