二叉树常见算法

持续更新…

1.二叉树的遍历

(1)前序遍历(根左右)
在这里插入图片描述

void Tree::PreSearch2(tree * root)
{
	stack<tree*>st;
	tree*p = root;
	while (p|| !st.empty()) {
		while (p) {
			//边入栈遍输出,从上到下输出的(根到左)
			cout << p->val<<" ";
			st.push(p);
			p = p->pLeft;//一直往左,不进while说明这棵树左遍历完
		}
	 //走到最左开始回退向右一个节点,等循环到上边把此右节点和他的左子树入栈。
		if (!st.empty()) {
			p = st.top();
			st.pop();
			p = p->pRight;//回退向右,转向右子树
		}
//之后再向右子树的左
	}
}

(2)中序遍历
跟前序差不多,就是输出的时候不是边入栈边输出,是边出栈(回退)边输出,左在根前边,所以应该先入栈完毕之后回退输出。

void Tree::InSearch(tree * root)
{
	stack<tree*>st;
	tree*p = root;

	while (p || !st.empty()) {
		while (p) {
			st.push(p);
			p = p->pLeft;//一直往左,不进while说明这棵树左遍历完
		}
		if (!st.empty()) {
			p = st.top();
			cout << p->val << " ";//输出根
			st.pop();
			p = p->pRight;//回退向右,转向右子树
		}

	}
}

(3)后序遍历
后序根在右后边,所以输出根时要看右节点输出了没,使用pLast查看上一个遍历的节点是不是此根节点的右。
如果遍历了右,就直接回退输出,说明 下边的都遍历完了。
如果没有,那么跟前两个遍历差不多,就是入栈向右,再入右节点的左子树。

void Tree::BackSearch2(tree * root)
{
	stack<tree*>st;
	tree*p = root;
	tree*pLast = NULL;
	//先到左子树底 倒着遍历
	while (p) {
		st.push(p);
		p = p->pLeft;
	}

	while (!st.empty()) {
		//开始倒着找
		p = st.top();
		st.pop();
		if (p->pRight == NULL || p->pRight == pLast) {//看是否右遍历了
			pLast = p;
			cout << p->val << " ";
		}
		else//先找右
		{
			st.push(p);//根再入栈
			p = p->pRight;
			while (p) {
				st.push(p);
				p = p->pLeft;
			}
		}
	}
}

(4)层序遍历
使用双向队列,从前向后,将队头节点的左右入队后再将此节点出队。

void cengci(tree*t)
{
   queue<tree*>q;
   q.push(t);
    while(!q.empty())
    {
       t=q.front();
       q.pop();
       if(t)
       {
           cout<<t->data;
           q.push(t->l);
           q.push(t->r);
       }

    }
}

2.二叉树转链表

(1)转单链表(前序遍历的顺序)
使用right这根指针进行节点间指向,将左指针全都赋空。
(还有一种分治法:将左、右子树变为单链表,分别返回左右链表的最后一个节点,让左链表的最后一个节点指向右节点的第一个节点,完成合并。右链表的最后一个节点即为整个链表的最后一个节点,接下来双链表采用这种方法)
1)递归
前序遍历的倒序,倒着走,先向右使用last指针进行回退指向,递归回退。

TreeNode* last = nullptr;
 void flatten(TreeNode* root) {
        if (root == nullptr) return;

        flatten(root->right);
        flatten(root->left);
//从右左出来后,这时左右子树都已经转成单链表,从根这里连起来
        root->right = last;
        root->left = nullptr;
        last = root;
    }

在这里插入图片描述
2)非递归,思路相同

class Solution {
public:
    void flatten(TreeNode* root) {
        if(root==NULL)return;
        TreeNode *cur=root;
        while(cur){
            if(cur->left){
                TreeNode *p=cur->left;
                while(p->right)p=p->right;
                p->right=cur->right;
                cur->right=cur->left;
                cur->left=NULL;
            }
            cur=cur->right;
        }
    }
};

(2)二叉搜索树转有序双链表
使用递归,将左右子树都变成有序双链表,返回两边子树的第一个和最后一个节点,再回退将两个子树连起来。
左指向上一个,右指向下一个

   void ConvertNode(BSTree tree, BSNode **last_node)
    {
        if(tree == NULL)
            return;
        //对tree的左子树进行转换,last_node是转换后链表最后一个结点的指针
        if(tree->left != NULL)
            ConvertNode(tree->left, last_node);
    //调整tree的left指针
        tree->left = *last_node;
        //调整指向最后一个结点,right指向下一个结点
        if(*last_node != NULL)
            (*last_node)->right = tree;
        //调整指向最后链表一个结点的指针
        *last_node = tree;
        //对tree的右子树进行转换,last_node是转换后链表最后一个结点的指针
        if(tree->right != NULL)
            ConvertNode(tree->right, last_node);
    }

3.二叉树求高度和是否平衡

求高度
(1)递归
求左右子树高度,返回最大的加一。

int Tree::getdeepsml(tree * root, int dept) {
	int dept1;
	int dept2;
	if (root == NULL)
		return dept;
	if (root->pLeft)
		 dept1 = getdeepsml(root->pLeft, dept + 1);
	if (root->pRight)
		 dept2 = getdeepsml(root->pRight, dept + 1);
	if (dept1 < dept2) {
		return dept2;
	}
	else
		return dept1;
}

(2)非递归
使用层序遍历,每增加一层dept++;
层序遍历的层数是此层在队列长度

class Solution {
public:
    int TreeDepth(TreeNode* pRoot)
    {
        if(pRoot==NULL)return 0;
        list<TreeNode*>lst;
        lst.push_back(pRoot);
        int dept=0;int size;
        TreeNode*temp;
        while(!lst.empty()){
          dept++;
          size=lst.size();
          for(int i=0;i<size;i++){
              temp=lst.front();
              lst.pop_front();
              if(temp->left)lst.push_back(temp->left);
              if(temp->right)lst.push_back(temp->right);
          }
        }
        return dept;
    }
};

4.根据前序中序、后序中序重建二叉树

(1)前序和中序重建

tree* createtreeprein(vector<int>&pre, vector<int>&in, int inbeg, int prebeg, int inend, int preend) {
	if (inbeg > inend || prebeg > preend)return NULL;
	tree *tr = new tree;
//前序第一个为根
	tr->val = pre[prebeg];
	int i;
//找中序中根的所在位置,此位置分开了左右子树,记录定位和长度
	for (i = inbeg; i <= inend; i++) {
		if (in[i] == tr->val)break;
	}
	int flag = i - inbeg;
//左右子树,将中序根两边的部分放入,前序左右子树序列页放入
	tr->pLeft = createtreeprein(pre, in, inbeg, prebeg + 1, i - 1, prebeg + flag);
	tr->pRight = createtreeprein(pre, in, i + 1, prebeg + flag + 1, inend, preend);
	return tr;
}
tree* createtreeprein(vector<int>&pre, vector<int>&in) {
	if (pre.empty() || in.empty())return NULL;
	//用递归,一段是一棵子树,用前序的arr【begin】分割。
	tree* tr=createtreeprein(pre, in, 0, 0, in.size() - 1, pre.size() - 1);
	return tr;
}

(2)后序和中序
根前序和中序差不多,后序根在最后,倒着拿根的位置,正着分开左右子树,定位不同而已。

tree* createtreeafterin(vector<int>&pre, vector<int>&in, int inbeg, int afterbeg, int inend, int afterend) {
	if (inbeg > inend || afterbeg > afterend)return NULL;
	tree *tr = new tree;
	tr->val = pre[afterend];
	int i;
	for (i = inbeg; i <= inend; i++) {
		if (in[i] == tr->val)break;
	}
	int flag = i - inbeg;
	tr->pLeft = createtreeprein(pre, in, inbeg, afterbeg + 1, i - 1,i-1);
	tr->pRight = createtreeprein(pre, in, i + 1,i, inend, afterend-1);
	return tr;
}
tree* createtreeafterin(vector<int>&pre, vector<int>&in) {
	if (pre.empty() || in.empty())return NULL;
	//用递归,一段是一棵子树,用后序的arr【end】分割。
	tree* tr = createtreeprein(pre, in, 0, 0, in.size() - 1, pre.size() - 1);
	return tr;
}

5.二叉树翻转递归+非递归。

就是一个遍历的过程其实,将每个节点遍历到之后将他的左右子树交换,所以非递归可采用层序遍历或者先序遍历找到所有节点然后翻转孩子。
递归:

tree*invert(tree *root) //二叉树翻转
{
	if (root == NULL)return NULL;
	tree*left=invert(root->pLeft);
	tree*right = invert(root->pRight);
	root->pLeft = right;
	root->pRight = left;
	return root;
}

非递归:
跟遍历差不多,不过就是拿到节点时交换孩子,层序遍历就很简单,先序在这里,先序比层序复杂多了我觉得,面试我还是要写层序。

void Tree::mirror(tree *root) {
	//非递归翻转、我写先序遍历吧
	if (root == NULL)return;;
	stack<tree*>sta;
	tree*p = root;
	while (p||!sta.empty()) {
		//到最左
		while (p) {
			//交换左右
			if (p->pLeft == NULL&&p->pRight == NULL)
			{
			}
			else {
				tree*temp =p->pLeft;
				p->pLeft = p->pRight;
				p->pRight = temp;
			}
			sta.push(p);
			p = p->pLeft;
		}		
		if (!sta.empty()) {
			
			p = sta.top();
			p = p->pRight;
			sta.pop();
		}
	}


}

层序:

就是跟入队,出队时交换左右子树,然后将不为NULL的入队。

6.二叉树求最低公共祖先

(1)如果有父节点,直接从两个节点向上找找到根得到两个链表,找两个链表的第一个公共节点
(2)如果没有父亲节点,有两种情况:
一是要找的这两个节点(a, b),在要遍历的节点(root)的两侧,那么这个节点就是这两个节点的最近公共父节点;
二是两个节点在同一侧,则 root->left 或者 root->right 为 NULL,另一边返回a或者b。那么另一边返回的就是他们的最小公共父节点。
递归有两个出口,一是没有找到a或者b,则返回NULL;二是只要碰到a或者b,就立刻返回。

tree * findLowestCommonAncestor(tree* root, tree* a, tree* b) {
	//两个结束条件
if (root == NULL || a == NULL || b == NULL)return NULL;
	if (root == a || root == b)//代表在一边找到了最高的一个节点
		return root;
	tree*left = findLowestCommonAncestor(root->pLeft, a, b);
	tree*right = findLowestCommonAncestor(root->pRight, a, b);
	//如果 左和右都找到了,说明在两边,此为祖先节点。
	if (left&&right)return root;
	else//只有一边找到,说明ab在同一侧,返回不为NULL的一侧。
		return left ? left : right;
	
}

(3)二叉树是个二叉查找树,且root和两个节点的值(a, b)已知

tree * findLowestCommonAncestor(tree* root, tree* a, tree* b)
{
// 二叉树是个二叉查找树,且root和两个节点的值(a, b)已知
	char min, max;
	if (a->val < b->val)
		min = a->val, max = b->val;
	else
		min = b->val, max = a->val;
	while (root)
	{
		if (root->val >= min && root->val <= max)//两边
			return root;
		else if (root->val < min && root->val < max)//一边,继续走
			root = root->pRight;
		else
			root = root->pLeft;
	}
	return NULL;
}

7.判断两个节点是不是兄弟、堂兄弟

(1)兄弟,递归判断,如果左右为那两个节点

bool judgebro(tree* root, tree* a, tree* b) {
	if (root == NULL)
		return false;
	if ((root->pLeft == a&&root->pRight == b) || root->pLeft == b&&root->pRight == a)
return true;
	if (judgebro(root->pLeft, a, b) || judgebro(root->pRight, a, b))
		return true;
}

(2)堂兄弟,两个节点深度相同,父亲节点不同
算出深度,先检查是否深度相同,再检查两个父亲节点是否相同。

class Solution {
public:
    int xfather,yfather,xdep,ydep;//成员变量
    void getlen(TreeNode*root,int x,int y,int dept,int father){//参数记录此时深度和父亲节点的值
        if(root==NULL)return;
        if(root->val==x){
            xfather=father;
            xdep=dept;
        }
        else if(root->val==y){
            yfather=father;
            ydep=dept;
        }
        else{
            getlen(root->left,x,y,dept+1,root->val);
             getlen(root->right,x,y,dept+1,root->val);
        }
    }
    bool isCousins(TreeNode* root, int x, int y) {
        getlen(root->left,x,y,1,root->val);
        getlen(root->right,x,y,1,root->val);
        return (xfather!=yfather&&xdep==ydep);
    }
};

8.判断是否为完全二叉树(层序遍历变形),层序遍历如何获取的层数?

通过当此层全部进入,再全部弹出入他们的孩子,每次全部入队后队列长度就是一层的全部节点。
判断是否完全只要出队的节点有一个只有一个孩子或者没有孩子,就说明他之后的节点都没有孩子,如果有,就不是完全二叉树。

9.二叉树的左/右视图

层序遍历第一个

10.二叉树叶子节点个数

11.二叉树第k层节点个数

12.二叉树求一个数的值是否是某条路径和

13.二叉树最大路径和/最长路径

14.二叉树节点总数

16.二叉树之字形打印

17.判断两棵树是否相等、是否为子结构

18.判断是否为二叉搜索树

19.给定一个二叉搜索树找第k小的节点

中序遍历。

class Solution {
public:
    int count=0;
    TreeNode* KthNode(TreeNode* pRoot, int k)
    {
        //中序遍历
        if(pRoot==NULL)return NULL;
        //左
        TreeNode *tr=KthNode(pRoot->left,k);
        if(tr!=NULL)//如果左找到了就返回,没找到不返回继续往下走
            return tr;
//没找到继续计数
        count++;
        //中 判断是否到了
        if(count==k)return pRoot;
        //向右
        tr=KthNode(pRoot->right,k);
        if(tr!=NULL)
            return tr;
        return NULL;
    }

    
};

20.给定一个二叉树和一个节点,找中序下一个节点并返回,树有父节点。

class Solution {
public:
    TreeLinkNode* GetNext(TreeLinkNode* pNode)
    {
        if(pNode==NULL)return NULL;
         TreeLinkNode*node=NULL;
        //如果有右
        if(pNode->right){
            node=pNode->right;
            while(node->left){
                node=node->left;
            }
            return node;
        }
        //没有,就向上,找第一个为祖宗是左孩子
        while(pNode->next){
            node=pNode->next;
            if(node->left==pNode)return node;
            pNode=pNode->next;
        } 
        //是根且没有右
        return NULL;
    }
};

21.BST中任意两个节点差的最小值

22.序列化和反序列化二叉树

class Solution {
public:
   //前序遍历,存入数组,到null存0xfffff;
    vector<int>buf;
    void dfs1(TreeNode*root){
       if(root==NULL){
           buf.push_back(0xfffffff);
           return;}
        else{
            buf.push_back(root->val);
            dfs1(root->left);
            dfs1(root->right);
        }
    }
    TreeNode* dfs2(int* &s){
        if(*s==0xfffffff){
           ++s;
           return NULL;
       }else{
            TreeNode*p=new TreeNode(*s);
            s++;
            p->left=dfs2(s);
           p->right=dfs2(s);
           return p;
       }
    }
    char* Serialize(TreeNode *root) {    
      dfs1(root);
      int * s=new int[buf.size()];
      for(int i=0;i<buf.size();i++)
         s[i]=buf[i];
      return (char*)s;
    }
    TreeNode* Deserialize(char *str) {
     int *s=(int *)str;
        return dfs2(s);
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值