二叉树
一、对二叉树的理解
- 二叉树的概念:一棵二叉树是节点的一个有限集合,该集合或者为空,或者由一个根节点加上两棵左子树和右子树组成
- 二叉树的特点:
1、每个节点最多有两棵子树,即二叉树不存在度大于2的节点
2、二叉树的子树有左右之分,其子树的次序不能颠倒 - 二叉树的性质:
1、若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2i-1个节点
2、若规定只有根节点的二叉树的深度为1,则深度为k的二叉树的最大节点数是2k-1
3、对任何一棵二叉树,如果其叶子节点个数为n0,度为2的非叶子节点个数为n2,则n0=n2+1
4、具有n个节点的完全二叉树的深度k为log2(n+1)向上取整
5、对于具有n个节点的完全二叉树,如果按照从上至下从左至右的顺序对所有结点从0开始编号,则对于序号为i的节点有:
(1)如果i>=0,则序号为i节点的双亲结点的序号为(i-1)/2;如果i=0,则序号i节点无双亲结点
(2)如果2i+1<n,则序号i结点的左孩子的序号为2i+1,右孩子的序号为2i+2;如果2i+1>=n,则序号i节点无孩子节点
二、二叉树的遍历
二叉树的遍历是一个很常见的问题。二叉树的遍历方式主要有:先序遍历、中序遍历、后序遍历、层次遍历。先序、中序、后序其实指的是访问父节点的次序。在遍历过程中,若访问顺序是父节点-左孩子节点-右孩子节点,就是先序遍历;若访问顺序是左孩子节点-父节点-右孩子节点,就是中序遍历;若访问顺序是左孩子节点-右孩子节点-父节点,就是后序遍历。不论是先序遍历、中序遍历还是后序遍历,访问左右孩子节点的相对次序是不变的,总是先访问左孩子节点,再访问右孩子节点。而层次遍历,就是按照从上到下、从左到右的顺序访问二叉树的每个节点。
- 先序遍历递归算法
//filename: BinTreeNode.h
template <typename T>
void travPre_R(BinTreeNode<T> * root) {//二叉树先序遍历算法(递归版)
if (!root) return;
cout << root->data;
travPre_R(root->LeftChild);
travPre_R(root->RightChild);
}
- 中序遍历递归算法
template <typename T>
void travIn_R(BinTreeNode<T> * root) {//二叉树先序遍历算法(递归版)
if (!root)
return;
travPre_R(root->LeftChild);
cout << root->data;
travPre_R(root->RightChild);
}
- 后序遍历递归算法
template <typename T>
void travPost_R(BinTreeNode<T> * root) {//二叉树先序遍历算法(递归版)
if (!root)
return;
travPost_R(root->LeftChild);
travPost_R(root->RightChild);
cout << root->data;
}
sort
sort()函数:对数组进行排序
sort()函数在algorithm头文件中,存储在std命名空间中。
sort()函数有三个参数:第一个是数组的起始地址;第二个是数组的结束地址;第三个是如何排序(从大到小或从小到大)。
- 没有第三个参数,默认从小到大排序
#include<iostream>
#include<algorithm>
int main()
{
int arr[10]={4,6,23,324,7854,94,34,6,655,3456};
int i;
for(i=0;i<10;i++)
{
std::cout<<arr[i]<<"\t";
}
std::sort(arr,arr+10);
std::cout<<"\n";
for(i=0;i<10;i++)
{
std::cout<<arr[i]<<"\t";
}
return 0;
}
- 通过第三个参数来设置排序的方式
#include<iostream>
#include<algorithm>
bool com(int a,int b)
{
return a>b;
}
int main()
{
int arr[10]={4,6,23,324,7854,94,34,6,655,3456};
int i;
for(i=0;i<10;i++)
{
std::cout<<arr[i]<<"\t";
}
std::sort(arr,arr+10,com);
std::cout<<"\n";
for(i=0;i<10;i++)
{
std::cout<<arr[i]<<"\t";
}
return 0;
}
- less<数据类型>()//从小到大排序 ,greater<数据类型>()//从大到小排序
#include<iostream>
#include<algorithm>
int main()
{
int arr[10]={4,6,23,324,7854,94,34,6,655,3456};
int i;
for(i=0;i<10;i++)
{
std::cout<<arr[i]<<"\t";
}
//std::sort(arr,arr+10,std::less<int>());
std::sort(arr,arr+10,std::greater<int>());
std::cout<<"\n";
for(i=0;i<10;i++)
{
std::cout<<arr[i]<<"\t";
}
return 0;
}
DFS与BFS
一、对DFS的理解
- 深度优先搜索(Depth-First-Search)是搜索算法的一种。是沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节 v的所有边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。属于盲目搜索。
- DFS是图论里面的一种搜索算法,他可以由一个根节点出发,遍历所有的子节点,进而把图中所有的可以构成树的集合都搜索一遍,达到全局搜索的目的。所以很多问题都可以用dfs来遍历每一种情况,从而得出最优解,但由于时间复杂度太高,我们也叫做暴力搜索。
- DFS如同数据结构中的栈结构,是属于一种后进先出的结构,比如说一个羽毛球筒,把它装满之后,我们开始使用时,拿的总是最后放进去的那一个。所以这就导致了所有的点进入栈时有一个顺序,我们称之为 :DFS序。
- 例题
在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。
Sample Input
4 4
…#
…#.
.#…
#…
Sample Output
1
题目要求:棋子只能放在#上并且k个棋子必须在不同行不同列,所以说这里 进行了状态的限制,我们如果用BFS做肯定不太好做,但是我们刚好可以运用DFS的回溯特点做这个题目;
因为这个题目是二维的题目我们无法判断 该行该列到底有没有放棋子,我们只可以判断我们当前到的这一行放棋子了没有,于是我们在外面加一个数组VIS,它标记着第i列有没有放棋子,如果第i列放了棋子,那么我们就令vis[i]==true。然后我们DFS他的每一行,在其间遍历他的每一列具体可能说不太清楚,我把代码和注释附上:
bool vis[50];
int n,k;
char mp[50][50];//标记每一列
ll cnt=0;
void dfs(int x,int way)//用way记录我们放了多少棋子
{
if(way==k)
{
cnt++;//cnt记录方案数
return;//一定记得要return
}
if(x>=n) return;//这是判界 因为我们按行遍历,一共有n行不能多出去
for(int i=0;i<n;i++)//判断这一行的每一列
{
if(mp[x][i]=='#'&&!vis[i])//如果说这mp[x][i]刚好是#而且 第i列没有放棋子
{
vis[i]=1;//我们就放上
dfs(x+1,way+1);//在下一行放,这一行已经无法放了,不同行不同列
vis[i]=0;//这个地方比较关键,回溯的重点
}
}
dfs(x+1,way);//这一行找不到的话就直接进行下一行
}
int main()
{
while(~scanf("%d%d",&n,&k))
{
if(n==-1&&k==-1) break;
cnt=0;
memset(mp,0,sizeof(mp));
for(int i=0;i<n;i++)
scanf("%s",mp[i]);
dfs(0,0);
printf("%d\n",cnt);
}
return 0;
}
二、BFS的理解
- 与DFS相对的那就是BFS了,BFS称为宽度优先搜索也叫做广度优先搜索,他是按层遍历每一种状态的下一种状态。
- 例题
求迷宫最短路
int bfs()
{
queue<node>q;
q.push({1,1});
num[1][1]=0;
while(!q.empty)
{
node u=q.front();q.pop();//扔掉首元素
for(int i=0;i<4;i++)
{
int mx=u.x+dx[i],my=u.y+dy[i];
if(u.x==ex&&u.y==ey) return num[u.x][u.y];//放队列之前我们已经把距离更新了,只要找到这个点绝对是最小距离了。
if(num[mx][my]==-1&&str[mx][my]==0)
{
num[mx][my]=num[u.x][u.y]+1;
q.push({mx,my});
}
}
}
}