算法与数据结构(五)

一、c++二叉树结构

在这里插入图片描述

typedef struct BiNode 
{
    
    
	int data;  //结点内值域
	struct BiNode* lchild, * rchild; //左孩子 右孩子
} BiNode, * BiTree;

或者:

 
class Node
{
    
    
public:
	char data;// data表示该节点代表的内容;
	Node* L;//左子树
	Node* R;//右子树
}; 
Node* T;//创个指针,指向二叉树的根节点;

二、用递归和非递归两种方式实现二叉树的先序、中序、后序遍历

有如下的树:
在这里插入图片描述

递归方法:
先序遍历:
先遍历头,再遍历左子树的结点、再遍历右子树的结点,遍历结果如下:
在这里插入图片描述
递归方法代码如下:

void preOrder(Node*& T)
{
    
    
	if (T == NULL)
	{
    
    
		return;//如果该结点为空就返回;
	}
 	//递归遍历 
	else
	{
    
    
		cout << T->data << " ";  //先输出根节点的那个数字
		preOrder(T->L);          //然后访问左孩子,开始递归
		preOrder(T->R);          //左孩子访问完成,开始右孩子递归
	}
}

以上代码输出即为先遍历
递归序的遍历结果为:
在这里插入图片描述
我们只需要在递归遍历过程中数字第一次出现时打印即可。
非递归:
思路:
先将根节点放到栈中
1、然后从栈中弹出一个结点cur
2、打印cur;
3、先右再左(如果有),否则什么也不做
4、循环1-3,直至栈中没有数据为止。

class Solution {
    
    
public:
    void preorderTraversal(TreeNode* root) {
    
    
    	if(root != NULL){
    
    
    	//申请一个临时栈
        stack<TreeNode*> st;
        //将根节点首先压入栈中
        st.push(root);
   		//如果栈不为空,则开始循环
        while(!st.empty())
        {
    
    		//弹出栈中的一个结点
                root = st.pop();
                //打印弹出的结点
                cout << root->val << " ";
                //如果叶结点不为空,则开始先将出栈节点的右结点和左结点先后压入栈中
                if(root->right != NULL)
                {
    
    
                	st.push(root->right);
				}
                if(root->left != NULL)
                {
    
    
                	st.push(root->left);
				}
		}
	}
}		

中序遍历:
先遍历左,再遍历头的结点、再遍历右子树的结点,遍历结果如下:
在这里插入图片描述
递归方法代码如下:

void inOrder(Node*& T)
{
    
    
	if (T == NULL)
		return;
	else
	{
    
    
		inOrder(T->L);
		cout << T->data << " ";
		inOrder(T->R);
	}
}

非递归方法:
思路:
1、将压入结点下的每颗子树整棵树左边界进栈(第一次先从根节点开始),对于下图,进栈的为(1,2,4)
2、弹出结点,弹出的过程中对弹出结点打印
3、压入弹出节点的右结点
4、循环以上两个步骤
在这里插入图片描述

class Solution {
    
    
public:
    void inOrderTraversal(TreeNode* root) {
    
    
    	if(root != NULL){
    
    
    	//申请一个临时栈
        stack<TreeNode*> st;
   		//如果栈不为空,则开始循环
        while(!st.empty() || root != NULL)
        {
    
    	
        	//如果当前结点不为空,则一直将其左结点压栈	
			if(root != NULL){
    
    
				st.push(root);        		
                root = root->left;
                }
            //否则从栈中弹出一个结点,并打印,将其右结点返回
			else{
    
    
				root = st.pop();
                cout << root->val << " ";
                root = root->right;
			}
		}
	}
}            
	

后序遍历:
先遍历左,再遍历右、再遍历头结点,遍历结果如下:
在这里插入图片描述
递归方法代码如下:

 
void posOrder(Node*& T)
{
    
    
	if (T == NULL)
		return;
	else
	{
    
    
		posOrder(T->L);
		posOrder(T->R);
		cout << T->data << " ";
	}
}

非递归方法:
思路:
先将根节点放到栈中
1、然后从栈中弹出一个结点cur
2、将弹出的结点放到一个回收栈中;
3、先左再右(如果有),否则什么也不做
4、循环1-3,直至栈中没有数据为止。
5、挨个从回收栈中弹出数据并打印

class Solution {
    
    
public:
    void posOrderTraversal(TreeNode* root) {
    
    
    	if(root != NULL){
    
    
    	//申请一个临时栈
        stack<TreeNode*> st1;
        stack<TreeNode*> st2;
        //将根节点首先压入栈中
        st1.push(root);
   		//如果栈不为空,则开始循环
        while(!st1.empty())
        {
    
    		//弹出栈中的一个结点
                root = st1.pop();
                //将弹出的结点压入回收栈中
                st2.push(root);
                //如果叶结点不为空,则开始先将出栈节点的左结点和右结点先后压入栈中
                if(root->left != NULL)
                {
    
    
                	st1.push(root->left);
				}
                if(root->right != NULL)
                {
    
    
                	st1.push(root->right);
				}
		}
		//将回收栈中的数据打印
		while(!st2.empty())
		{
    
    
			cout<<st2.pop()->val<< " ";
		}
	}
}		

三、二叉树的宽度优先遍历(常见题目:求一棵二叉树的宽度)

在这里插入图片描述
有如上的树。宽度优先遍历输出为:1,2,3,4,5,6,7,8,9
思路:
本题我们使用队列的方式来解决:
1、首先将根节点放到队列中
2、弹出队列中的第一个结点,然后打印该结点,并将该节点的左右子节点先后放到队列中(没有左或者右则直接跳过放另外一个结点)。
3、重复步骤二知道队列为空

#include<stdio.h>
#include<vector>
#include<queue>
//树的机构体
struct TreeNode
{
    
    
 int val;
 TreeNode* left;
 TreeNode* right;
 TreeNode(int x): val(x), left(NULL), right(NULL){
    
    }
};
//开始进行宽度优先遍历
void BFS(TreeNode* root)
{
    
    
	//实例化一个队列
	 std::queue<TreeNode*> Q;
	
	//将根节点压入队列中
	 Q.push(root);
	//如果该队列非空,则一直循环操作
	 while (!Q.empty())
	 {
    
    
		//将第一个元素返回并打印
		  TreeNode* node = Q.front();
		  printf("[%d]", node->val);
		//弹出第一个元素
		  Q.pop();
		//开始先放左结点再放右节点的操作
		  if (node->left)
		  {
    
    
		   	Q.push(node->left);
		  }
		  if (node->right)
		  {
    
    
		   	Q.push(node->right);
		  }
	 }
}
//初始化树
int main()
{
    
    
 TreeNode a(1);
 TreeNode b(2);
 TreeNode c(5);
 TreeNode d(3);
 TreeNode e(4);
 TreeNode f(6);
 a.left = &b;
 a.right = &c;
 b.left = &d;
 b.right = &e;
 c.right = &f;
 BFS(&a);
 return 0;
}

四、求二叉树的最大宽度

思路:
1)、首先,我们需要知道每一个节点的层数:每一个节点的层数是其父节点层数+1所以,我们在push一个节点的孩子节点进入队列时,可以保存其孩子节点的层级:当前层级+1。
2)、其次,我们需要获取每一层的宽度,并所有层级宽度取最大值返回。因为宽度遍历方式相同层级的节点肯定是依次输出遍历,我们就很方便对某层宽度进行计数。

int MaxWidth(TreeNode* root)
{
    
    
	int widMax = 0; //记录结果:最大宽度
	if (nullptr == root)
	{
    
    
		return 0;
	}
	queue<TreeNode*> nodeQueue; //遍历二叉树的辅助队列
	unordered_map< TreeNode*, int> levelMap; //记录每个节点所在层数的哈希表
	//根节点入队列
	nodeQueue.push(root);
	levelMap[root] = 1; //根节点定义为在第1层
	int curLevel = 1; //当前所在层数
	int curWidth = 0; //当前层记录的宽度
	while (!nodeQueue.empty())
	{
    
    
		TreeNode* temp = nodeQueue.front();
		//弹出队首节点
		nodeQueue.pop();
		//获取队首节点的层数,如果和上一个队首节点的层数curLevel相同,则curWidth+1;否则,说明已经开始进入下一层,则重置curWidth并更新curLevel
		if (levelMap[temp] == curLevel)
		{
    
    
			++curWidth;
		}
		//已经进入下一层
		else
		{
    
    
			//在进入下一层结点之前先要保存最大宽度
			widMax = curWidth > widMax ? curWidth : widMax;
			//将宽度置一
			curWidth = 1;
			//重置层数
			curLevel = levelMap[temp];
		}
 
		if (temp->left != NULL)
		{
    
    
			nodeQueue.push(temp->left);
			//保存当前节点左孩子的层数:当前层数+1
			levelMap[temp->left] = curLevel + 1;
		}
		if (temp->right != NULL)
		{
    
    
			nodeQueue.push(temp->right);
			//保存当前节点右孩子的层数:当前层数+1
			levelMap[temp->right] = curLevel + 1;
		}
	}
	return widMax;
}

我们也可以不使用哈希表来实现这种操作。
思路:
1)进队列
2)cur_end记录最后一个结点
next_end记录每次进队列的结点
curlevel记录每层结点个数
3)每当出来的结点不等cur_end时,curlevel++;
每当出来的结点等于cur_end时,curlevel++,并将next_end赋给cur_end,next_end置为NULL,
max更新与curlevel比较,curlevel置为0

#include<iostream>
#include<stdlib.h> 
#include<deque>  //插入标准库中的头文件
using namespace std;
 
typedef struct treenode
{
    
    
	char data;
	treenode *right;
	treenode *left;
}*Node;
 
//创建二叉树
void creat_tree(treenode *&rt)
{
    
    
	char ch;
	ch = getchar();
	if ('#' == ch) {
    
    
		rt = NULL;
	} else {
    
    
		rt = new treenode;
		rt->data = ch;
		creat_tree(rt->left);        //构造左子树
		creat_tree(rt->right);    //构造右子树    
	}
}
 
int noMapWeight(Node* head) {
    
    
	//如果没有结点,则返回
	if (head == NULL)
		return 0;
	
	//创建一个队列
	queue<Node*> q;
	q.push(head);//先将头装进去
	Node* cur_end = head; //定义当前层最后一个结点
	Node* next_end = NULL; //定义下一层最后一个节点
	int curlevel = 0, max = 0; //定义每层的节点数以及最大的节点数
	while (!q.empty()) {
    
    
		Node* out = q.front(); //取出第一个元素的值
		q.pop();//弹出第一个元素
		if (out->left != NULL) {
    
    //将左结点压入,并且下一层的最后一个结点赋值
			q.push(out->left);
			next_end = out->left;
		}	
		if (out->right != NULL) {
    
    //将右结点压入,并对下一层最后一个节点赋值
			q.push(out->right);
			next_end = out->right;
		}//如果输出的元素不为当前层最后一个结点,则宽度加一
		if (out != cur_end) {
    
    
			curlevel++;
		//否则宽度先加一,然后将将所有变量移动到最后一层,并且暂存最大值
		}else {
    
    
			curlevel++;
			cur_end = next_end;
			next_end = NULL;
			max = max < curlevel ? curlevel : max;
			curlevel = 0;
		}
	}
	return max;//返回最大值
}
 
int main() {
    
    
	treenode *root = NULL;
	int height, width;  //表示高度和最大宽度
	cout << "请输入二叉树,空值以#代表:" << endl;
	creat_tree(root);        //创建二叉树
	Complete_binary_tree(root,height,width);
	cout << "高度为:" << height << endl;
	cout<<"最大宽度为:" << width << endl;
	system("pause");
	return 0;
}

五、如何判断一颗二叉树是搜索二叉树

搜索二叉树:左侧结点都比头节点小,右侧结点都比头节点大。经典搜索二叉树没有重复值。
在这里插入图片描述
上图即为经典搜索二叉树。从上图可以看出,在中序遍历中,所有结点都是升序的,即为搜索二叉树。如果某一位置是降序的,则该树就不是搜索二叉树。
法一:直接通过递归的方式挨个子树遍历比较。

/*
* 概念:每个结点都比左儿子大,比右儿子小,严格的搜索二叉树没有重复的值
step:   1)中序遍历得到的数组的升序的
*/
int preData = INT_MIN;
bool isBST(Node* head) {
    
    
	if (head == NULL)
		return true;
	bool isLeftBST = isBST(head->left);
	if (!isLeftBST)//如果左树不是,就直接返回false
		return false;
	if (preData >= head->data)//如果根的data比左的小,就返回false
		return false;
	else {
    
    
		preData = head->data;
	}
	return isBST(head->right);
}

法二:将每次中序遍历的结点全放到list容器中,然后再判断list容器中结点的值是否为升序。

//判断是否为搜索二叉树2
#include<list>
#include<algorithm>
bool isBST2(Node* head) {
    
    
	if (head == NULL)
		return false;
	list<int> L;
	process(head, L);
	int sz = L.size();
	list<int>::iterator end = --L.end();
	for (list<int>::iterator it = L.begin();it != end;)
		if (*it >= *(++it))
			return false;
	return true;
}
void process(Node* head, list<int>& L) {
    
    
	if (head == NULL)
		return;
	process(head->left, L);
	L.push_back(head->data);
	process(head->right, L);
}

法三:非递归过程中进行比较

bool isBST(node* head){
    
    
    if (head == NULL){
    
    
        return true;
    }
    stack<node*>newstack;
    int pre = INT_MIN;
    while (!newstack.empty() || head != NULL){
    
    
        if (head != NULL){
    
    
            newstack.push(head);
            head = head->left;
        }
        else{
    
    
            head = newstack.top();
            //cout << head->value << ",";
            if (pre < head->value){
    
    
                pre = head->value;
            }else{
    
    
                return false;
            }
            newstack.pop();
            head = head->right;
        }
    }
    return true;
}

猜你喜欢

转载自blog.csdn.net/qq_52302919/article/details/131102062