LeetCode
void PostOrder(BiTree T){
InitStack(S);
p = T;
r = NULL;
while(p||!IsEmpty(S)){
if(p){ //走到最左边
push(S, p);
p = p->lchild;
}
else{
GetTop(S, p); //读栈顶结点(非出栈)
if(p->rchild&&p->rchild!=r){ // 若右子树存在,且未被访问过
p = p->rchild;
push(S, p); //右子树入栈
p = p->lchild;
}
else{ //否则,弹出结点并访问
pop(S, p);
visit(p->data);
r = p; //r指针标记当前结点已经访问过了
p = NULL;
}
}
}
}
层次遍历入栈,再弹栈即可
void InvertLevel(BiTree bt){
Stack s;
Queue Q;
if(bt!=NULL){
InitStack(s);
InitQueue(Q);
EnQueue(Q, bt);
while(IsEmpty(Q)==false){ //队列不空
Dequeue(Q, p);
Push(s, p); //出队入栈
if(p->lchild)
EnQueue(Q, p->lchild);
if(p->rchild)
EnQueue(Q, p->rchild);
}
while(IsEmpty(s)==false){ //访问栈
Pop(s, p);
visit(p->data);
}
}
}
采用层次遍历,设置变量level记录当前结点所在的层数,设置变量last指向当前层的最有结点,每次层次遍历出队时与last指针比较,若两者相等,则层数加1,并让last指向下一层的最右结点,直到遍历完成。level的值即为二叉树的高度。
int BtDepth(BiTree T){
//采用层次遍历的非递归方法求解二叉树的高度
if(!T)
return 0; //树空,高度为0
int front=-1, rear=-1;
int last=0,level=0;
BitTree Q[MaxSize];
Q[++rear] = T; //将根节点入队
BitTree p;
while(front<rear){ //队不空,则循环
p = Q[++front]; //对头元素出队
if(p->lchild)
Q[++rear] = p->lchild;
if(p->rchild)
Q[++rear] = p->rchild;
if(front==last){ //处理该层的最右结点
level++;
last = rear; // last指向下一层
}
}
return level;
}
版本2
最大深度
int maxDepth(TreeNode* root) {
if(root==NULL)
return 0;
vector<TreeNode*> q; //使用线性表模拟队列
TreeNode *p;
int i=0,j=1, level=0;
q.push_back(root);
while(i<j){
for(int k=i;k<j;k++){
p = q[k];
if(p->left)
q.push_back(p->left);
if(p->right)
q.push_back(p->right);
}
level++;
i=j;
j=q.size();
}
return level;
}
BiTree PreInCreat(ElemType A[], ElemType B[], int l1,int h1,int l2,int h2){
//l1,h1为先序的第一个和最后一个结点下标,l2,h2为中序的第一个和最后一个结点下标
//初始调用时,l1=l2=1, h1=h2=n
root = (BiTNode*)malloc(sizeof(BiTNode)); //建根节点
root->data=A[l1];
for(int i=l2;B[i]!=root->data;i++); // 根节点在中序序列中的划分
llen=i-l2;
rlen=h2-i;
if(llen)
root->lchild=PreInCreat(A, B, l1+1, l1+len, l2, l2+llen-1);
else
root->lchild=NULL;
if(rlen)
root->rchild=PreInCreat(A, B, h1-rlen+1, h1, h2-rlen+1, h2);
else
root->rchild=NULL;
return root;
}
C++语言
LeetCode
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
return reBuiltBTree(preorder, inorder, 0, preorder.size()-1, 0, inorder.size()-1);
}
TreeNode* reBuiltBTree(vector<int> A, vector<int> B, int l1, int r1, int l2, int r2){
if(l1>r1)
return NULL;
TreeNode* curr = new TreeNode();
curr->val = A[l1];
int root;
for(root=l2;B[root]!=A[l1];root++); //找到根节点在中序遍历中的位置
int llen=root-l2; //左子树的长度
int rlen=r2-root; //右子树的长度
if(llen)//左子树
curr->left = reBuiltBTree(A, B, l1+1, l1+llen, l2, root-1);
else
curr->left = NULL;
if(rlen) //右子树
curr->right = reBuiltBTree(A, B, l1+llen+1, r1, root+1, r2);
else
curr->right = NULL;
return curr;
}
};
bool IsComplete(BiTree T){
// 本算法判断给定二叉树是否为完全二叉树
InitQueue(Q);
if(!T)
return 1; // 空二叉树为满二叉树
EnQueue(Q, T);
while(!IsEmpty(Q)){
DeQueue(Q, p);
if(p){ //结点非空,将其左子树、右子树加入队列
EnQueue(Q, p->lchild);
EnQueue(Q, p->rchild);
}
else{ //结点为空,检查其后是否有非空结点
while(!IsEmpty(Q)){
DeQueue(Q, p);
if(p)
return 0;
}
}
}
return 1;
}
int DsonNodes(BiTree b){
if(b==NULL)
return 0;
else if(b->lchild!=NULL&&b->rchild!=NULL)
return DsonNodes(b->lchild) + DsonNodes(b->rchild) + 1;
else
return DsonNodes(b->lchild) + DsonNodes(b-rchild);
}
void swap(BiTree b){
if(b){
swap(b->lchild);
swap(b->rchild);
temp = b->lchild;
b->lchild = b->rchild;
b->rchild = temp;
}
}
int i=1;
ElemType PreNode(BiTree b, int k){
//本算法查找二叉树先序遍历中第k个结点的值
if(b==NULL)
return '#';
if(i==k)
return b->data;
i++;
ch = PreNode(b->lchild, k); //在左子树递归查找
if(ch!='#')
return ch;
ch = PreNode(b-rchild, k); // 在右子树递归查找
if(ch!='#')
return ch;
}
对于树的搜索,可以使用层次遍历,也可以是用前序遍历。
void DeleteXTree(BiTree bt){ //删除以bt为根的子树
if(bt){
DeleteXTree(bt->lchild);
DeleteXTree(bt->rchild);
free(bt);
}
}
// 在二叉树中查找所有以x为元素值的结点,并删除以其为根的子树
void Search(BiTree bt, ElemType x){
BiTree Q[]; //Q是存放二叉树结点指针的队列,容量足够大
if(bt){
if(bt->data==x){ //若根节点值为x,删除整棵树
DeleteXTree(bt);
exit(0);
}
InitQueue(Q);
EnQueue(Q, bt);
//采用层次遍历的方式,自上而下,非递归遍历每个结点
while(!IsEmpty(Q)){
DeQueue(Q, p);
if(p->lchild){ //左子树非空
if(p->lchild->data==x){
DeleteXTree(p->lchild);
p->lchild = NULL;
}
else
EnQueue(Q, p->lchild);
}
if(p->rchild){
if(p->rchild->data==x){
DeleteXTree(p->rchild);
p->rchild = NULL;
}
else
EnQueue(Q, p->rchild);
}
}
}
}
后序遍历,回溯到根节点中止,非王道课本,我觉得课本写的不太好,甚至有bug
int flag=0; //flag为1表示已经找到x
void Search(BiTree bt, ElemType x){
if(bt){
if(bt->data==x)
flag=1; //标记x已经找到
if(flag==0) // x已经找到的情况下,没必要再继续往深处遍历
Search(bt->lchild, x);
if(flag==0) //左子树没有搜到的情况下才会搜右子树
Search(bt->rchild, x);
if(flag==1&&bt->data!=x) //注意不要打印当前结点为x的结点
printf("%d\n",bt->data); // 以该结点为根节点,找到了x,那么打印当前结点的值
}
}
递归算法
BiTree Ancestor(BiTree ROOT, BiTNode *p, BiTNode *q){
/*求最近公共祖先的递归算法*/
if(ROOT==NULL||ROOT==q||ROOT==q)
return ROOT;
BiTNode *left = Ancestor(ROOT->lchild, p, q);
BiTNode *right = Ancestor(ROOT->rchild, p, q);
if(left==NULL) //左子树中不存在p或q
return right;
if(right==NULL) //右子树不存在p或者q
return left;
return ROOT; //左右子树分配p和q
}
这个看不懂啊,感觉很乱
typedef struct{
BiTree t;
int tag; //tag=0 表示左子女已经被访问,tag=1表示右子女已经被访问
}stack;
stack s[], s1[];
BiTree Ancestor(BiTree ROOT, BiTNode *p, BiTNode *q){
// 本算法求二叉树中p和q指向结点的最近公共结点
top = 0;
bt = ROOT;
while(bt!=ROOT||top>0){
while(bt!=NULL&&bt!=p&&bt!=q){ //结点入栈
s[++top].t = bt;
s[top].tag = 0;
bt=bt->lchild;
}
while(top!=0&&s[top].tag==1){
// 假定p在q的左侧,遇到p时,栈中元素为p的祖先
if(s[top].t==p){
for(i=1;i<=top;i++)
s1[i]=s[i];
top1=top;
}
if(s[top].t==q){
for(i=top;i>0;i--){ //将栈中元素的树结点到s1中去匹配
for(j=top1;j>0;j--){
if(s1[j].t==s[i].t)
return s[i].t;
}
}
}
top--;
}
if(top!=0){
s[top].tag=1;
bt=s[top].t->rchild;
}
}
return NULL;
}
王道课本给出的答案是层次遍历每个结点,遍历的过程中入队的时候标记该结点的层数,最后再遍历队列(未抹去信息)中的所有结点,统计每个层次的结点数量,输出最大值。
再次我就不总结王道课本的算法了,感觉是最笨的算法,代码也不简练。
自我感觉写的不是很好,起始可以依次遍历直接求最大值
int BTWidth(BiTree b){
if(b==NULL)
return 0;
vector<BiTree> q;
q.push_back(b);
int i=0,j=1;
int ans=0;
BiTree p;
while(i<j){
//每次出队的是一个层次的
for(int k=i;k<j;k++){
p = q[k];
if(p->lchild)
q.push_back(p->lchild);
if(p->rchild)
q.push_back(p->rchild);
}
if(j-i>ans) //i为该层次左端,j为该层次的有段,左闭右开
ans = j-i;
//更新i和j
i=j;
j=q.size();
}
return ans;
}
满二叉树的特点是任意根节点,左子树和右子树的长度是相等的。
例如前序:1 2 3 4 5 6 7,根节点为1,左子树为2 3 4,右子树为5 6 7
void PreToPost(ElemType pre[], int l1, int h1, ElemType post[], int l2, int h2){
int half;
if(h1>=l1){
post[h2] = pre[l1]
half = (h1-l1)/2;
PreToPost(pre, l1+1, l1+half, post, l2, l2+half-1);
PreToPost(pre, l1+half+1, h1, post, l2+half, h2-1);
}
}
LinkedList head, pre=NULL;
LinkedList InOrder(BiTree bt){
if(bt){
InOrder(bt->lchild); //中序遍历左子树
if(bt->lchild==NULL&&bt->rchild==NULL) //叶节点
if(pre==NULL){
head = bt;
pre = bt;
}
else{
pre->rchild=bt;
pre=bt;
}
InOrder(bt->rchild); //中序遍历右子树
pre->rchild=NULL; //我觉得这句加不加无所谓,因为叶子结点的右子树本身就指向NULL
}
return head;
}
题目的意思就是树的形态是一致的,不过根据题目的描述,直接编写递归代码即可,和王道课本略有不同。
int similar(BiTree T1, BiTree T2){
// 采用递归算法判断两棵二叉树是否相似
int leftS, rightS;
if(T1==NULL&&T2==NULL) // T1和T2都是空的二叉树
return 1;
else if(T1->lchild==NULL && T1->rchild==NULL
&& T2->lchild==NULL && T2->rchild==NULL) // T1 T2都只有一个根节点
return 1;
else{
leftS = similar(T1->lchild, T2->lchild);
rightS = similar(T1->rchild, T2->rchild);
return leftS&&rightS;
}
}
算法思想:若后序序列中,若结点p有右子女,则右子女是其前驱,若无右子女而有左子女,则左子女是其前驱。若结点p左、右子女均无,设其中序左线索指向某祖先结点f,(p是f右子树中按中序遍历的第一个结点),若f有左子女,则其左子女是结点p在后序下的前驱;若f无左子女,则顺其前驱找双亲的双亲,一直找到双亲有左子女(这时左子女是p的前驱)。还有一种情况,若p是中序遍历的第一个结点,则结点p在中序和后续下均无前驱。
BiThrTree InPostPre(BiThrTree t, BiThrTree p){
//在中序线索二叉树t中,求指定结点p在后序下的前驱结点q
BiThrTree q;
if(p->rtag==0)
q = p->rchild;
else if(p->ltag==0)
q = p->lchild;
else if(p->lchild==NULL) //该结点在中序线索二叉树中没有前驱
q = NULL;
else{ //顺左线索向上找p的祖先,若存在,再找祖先的左子女
while(p->ltag==1&&p->lchild!=NULL)
p = p->lchild;
if(p->ltag==0)
q = p->lchild; // p结点的祖先的左子女是其后序前驱
else
q = NULL; // 仅有单支树(p是叶子),已到根节点,p无后序前驱
}
return q;
}
(1)基于先序递归遍历的算法思想是用一个static变量记录wpl,把每个结点的深度作为递归函数的一个参数传递,算法步骤如下:
二叉结点的数据类型定义如下。
typedef struct BiTNode{
int weight;
struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;
前序遍历
int WPL(BiTree root){
return wpl_PreOrder(root, 0);
}
int wpl_PreOrder(BiTree root, int deep){
static int wpl=0;
if(root->lchild==NULL&&root->rchild==NULL)
wpl += deep*root->weight;
if(root->lchild!=NULL)
wpl_PreOrder(root->lchild, deep+1);
if(root->rchild!=NULL)
wpl_PreOrder(root->rchild, depp+1);
return wpl;
}
层次遍历,鉴于王道课本写的代码不太好,我不写了,我自己写一个吧。
int wpl_LevelOrder(BiTree root){
queue<BiTree> q;
queue<int> deep; //同步结点队列记录路径长度
int ans=0;
if(root==NULL)
return 0;
q.push(root);
deep.push(0);
while(!q.empty()){
BiTree curr = q.top();
q.pop(); //队首元素出队
int d = deep.top();
deep.pop();
if(curr->lchild==NULL && curr->rchild==NULL)
ans += deep*curr->weight;
if(curr->lchild){
q.push(curr->lchild);
deep.push(d+1);
}
if(curr->rchild){
q.push(curr->rchild);
deep.push(d+1);
}
}
return ans;
}
1)算法的基本设计思想。
表达式树的中序序列加上必要的括号即为等价的中缀表达式。可以基于二叉树的中序遍历策略得到所需的表达式。
表达式树中分支结点所对应的子表达式的计算次序,由该分支所处的位置决定。为得到正确的中缀表达式,需要在生成遍历序列的同时,在适当位置增加必要的括号。显然,表达式式的最外层(对应根节点)和操作数(对应叶节点)不需要添加括号。
2)算法实现。
将二叉树的中序遍历递归算法稍加改造即可得到本体的答案。除根节点和叶结点外,遍历其他结点时在遍历其左子树之前加上左括号,遍历完右子树后加上右括号。
void BtreeToE(BTree *root){
BtreeToExp(root, 1); //根的高度为1
}
void BtreeToExp(BTree *root, int deep){
if(root==NULL)
return;
else if(root->left==NULL&&root->right==NULL)// 叶子结点
printf("%s", root->data);
else{
if(deep>1)
printf("(");
BtreeToExp(root->left, deep+1);
prinft("%s", root->data);
BtreeTpExp(root->rchild, deep+1);
if(deep>1)
printf(")");
}
}
孩子兄弟表示法是一棵二叉树,森林的叶子结点,在二叉树中表示为左指针为空。
typedef struct node{
ElemType data;
struct node *fch, *nsib;
}*Tree;
int Leaves(Tree t){ //计算以孩子兄弟表示法存储的森林的叶子树
if(t==NULL)
return 0;
if(t->fch==NULL)
return 1 + Leaves(t->nsib);
else
return Leaves(t->fch) + Leaves(t->nsib);
}
树的深度等有所有子树中深度最大的树的深度+1
int Height(CSTree bt){
// 递归求以孩子兄弟链表表示的树的深度
int hc,hs;
if(bt==NULL)
return 0;
else{
hc = Height(bt->firstchild);
hs = Height(bt->nextsibling);
if(hc+1>hs)
return hc+1;
else
return hs;
}
}
层次序列和每个结点的度已知,可以还原整棵树,结点父子关系可以确定。
#define maxNodes 15
void createCSTree_Degree(CSTree &T, DataType e[], int degree[], int n){
// 根据树结点的层次序列e[]和各结点的度degree[]构造树的孩子-兄弟结点链表
// 参数n是树结点个数
CSNode *pointer = new CSNode[maxNodes]; //判断pointer[i]为空的语句未写
int i,j,d,k=0;
for(i=0;i<n;i++){ //初始化
pointer[i]->data = e[i];
pointer[i]->lchild=pointer[i]->rsibling = NULL;
}
for(i=0;i<n;i++){
d = degree[i]; // 结点i的度数
if(d){
k++;
pointer[i]->lchild=pointer[k]; //结点i的第一个孩子
for(j=2;j<=d;j++){ //结点i的剩下的d-1个孩子
k++;
pointer[k-1]->rsibing = pointer[k];
}
}
}
T = pointer[0];
delete [] pointer; //我觉得这里的释放内存空间不合适。
}
中序遍历升序
KeyType predt=-32767;
int JudgeBST(BiTree bt){
int b1,b2;
if(bt==NULL) // 空树
return 1;
else{
b1 = JudgeBST(bt->lchild);
if(b1==0||predt >= bt->data)
return 0;
predt = bt->data;
b2 = JudgeBST(bt->rchild);
return b2; //返回右子树的结果
}
}
递归算法:搜索的过程中标记层次,子结点的层次是父节点层次+1,初始化根节点层次为1
非递归算法,使用一个标记,每次层次增加,标记+1
/*算法假设给定的结点一定可以找到*/
int level(BiTree bt, BSTNode *p){
// 本算法计算给定结点*p在二叉排序树中的层次
int n=0;
BiTree t=bt;
if(bt!=NULL){
n++;
while(t->data != p->data)
if(p->data < t->data)
t = t->lchild;
else
t = t->rchild;
n++;
}
}
void Jundge_AVL(BiTree bt, int &balance, int &h){
// 本算法判断一个给定的二叉树是否未平衡二叉树
int bl=0, br=0, hl=0, hr=0; // 左右子树的平衡标记和高度
if(bt==NULL){
h=0;
balance = 1;
}
else if(bt->lchild==NULL&&bt->rchild==NULL){
h=1;
balance = 1;
}
else{
Jundge_AVL(bt->lchild, bl, hl);
Jundge_AVL(bt->rchild, br, hr);
h = (hl>hr?hl:hr)+1; // 取深度较深的那个子树再加1
if(abs(hl-hr)<2)
balance = b1&&br;
else
balance = 0;
}
}
最左结点和最有结点
KeyType MinKey(BSTNode *bt){
// 求出二叉排序树中最小关键字结点
while(bt->lchild!=NULL)
bt = bt->lchild;
return bt->data;
}
KeyType MaxKey(BSTNode *bt){
// 求出二叉排序树中最大关键字结点
while(bt->rchild!=NULL)
bt = bt->rchild;
return bt->data;
}
LNR遍历,遍历到第一个小于k的结点终止
void OutPut(BSTNode *bt, KeyType k){
// 本算法从大到小输出二叉排序树中所有值不小于k的关键字
if(bt==NULL)
return;
if(bt>rchild!=NULL)
OutPut(bt->rchild, k);
if(bt->data >= k)
printf("%d\n", bt->data); // 只输出大于等于k的结点的值
/*可以优化的是,如果确定了当前结点的值小于k,那么它的左子树一定也小于k,此时不用再遍历左子树*/
if(bt->lchild!=NULL)
OutPut(bt->lchild, k);
}
更改版本如下:
由于不访问小于k的左子树,效率提高。
void OutPut(BSTNode *bt, KeyType k){
if(bt==NULL) //当前结点为空,do nothing
return;
OutPut(bt->rchild, k); //访问右子树
if(bt->data >= k){ // 如果当前结点小于k,那么左子树不再需要遍历,一定不满足。
printf("%d\n", bt->data);
OutPut(bt->lchild, k); // 访问左子树
}
}
哈夫曼树的代码实现其实也可以完成,一般的题目都是手算
BSTNode* Search_Small(BSTNode *t, int k){
// 在以t为根的子树上寻找第k小的元素,返回其所在结点的指针。k从11开始计算
// 在树结点中增加一个count数据成员,存储以该结点为根的子树的结点个数
if(k<1 || k > t->count) //k的值不合法
return NULL;
if(t->lchild==NULL){
if(k==1)
return t;
else
return Search_Small(t->rchild, k-1);
}
else{
if(t->lchild->count == k-1) //左子树刚好有k-1个结点
return t;
else if(t->lchild->count > k-1)
return Search_Small(t->lchild, k); //去左子树搜索
else{ // 左子树的结点不足k-1,去右子树再找一些
return Search_Small(t->rchild, k - 1 - t->lchild->count)
}
}
}