第三周总结
一、二叉树
1、概念:
一棵二叉树是节点的一个有限集合,该集合或者为空,或者由一个根节点加上两棵左子树和右子树组成
2、特点:
① 每个节点最多有两棵子树,即二叉树不存在度大于2的节点
② 二叉树的子树有左右之分,其子树的次序不能颠倒
3、四种遍历及其代码
① 先序遍历
Status PreOrderTraverse(BiTree T)
{
if (T != NULL) {
cout << T->data << " ";
PreOrderTraverse(T->lchild);
PreOrderTraverse(T->rchild);
}
return OK;
}
②中序遍历
Status InOrderTraverse(BiTree T)
{
if (T != NULL) {
InOrderTraverse(T->lchild);
cout << T->data << " ";
InOrderTraverse(T->rchild);
}
return OK;
}
③后序遍历
Status PostOrderTraverse(BiTree T)
{
if (T != NULL) {
PostOrderTraverse(T->lchild);
PostOrderTraverse(T->rchild);
cout << T->data << " ";
}
return OK;
}
④层次遍历
Status LevelOrderTraverse(BiTree T)
{
queue<BiTree> q;
BiTree front;
if (T == NULL)
return ERROR;
q.push(T);
while (!q.empty()) {
front = q.front();
q.pop();
if (front->lchild)
q.push(front->lchild);
if (front->rchild)
q.push(front->rchild);
if (q.size()==0)
cout << front->data;
else
cout << front->data << " ";
}
return OK;
}
二、递归
1、定义
在运行的过程中调用自己即为递归
2、构成递归需具备的条件:
①子问题须与原始问题为同样的事,且更为简单
②不能无限制地调用本身,须有个出口,化简为非递归状况处理
3、举例
如汉诺塔问题
汉诺塔是一个发源于印度的益智游戏,也叫河内塔。相传它源于印度神话中的大梵天创造的三个金刚柱,一根柱子上叠着上下从小到大64个黄金圆盘。大梵天命令婆罗门将这些圆盘按从小到大的顺序移动到另一根柱子上,其中大圆盘不能放在小圆盘上面。当这64个圆盘移动完的时候,世界就将毁灭
代码实现
#include<stdio.h>
#include<stdlib.h>
void hanoi(int n, int x, int y, int z) //x,y,z代表我的A杆、B杆、C杆
{
if (n == 1)
printf("%c-->%c\n", x, z);
else
{
hanoi(n - 1, x, z, y);
printf("%c-->%c\n", x, z);
hanoi(n - 1, y, x, z);
}
}
int main(void)
{
int m;
printf("Input unmber:");
scanf("%d", &m);
printf("moving %2d diskes:\n", m);
hanoi(m, 'a', 'b', 'c');
system("pause");
}
三、快速排序与归并排序
快速排序
原理
设要排序的数组是A[0]……A[N-1]
首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它左边,所有比它大的数都放到它右边,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
过程
(1)首先设定一个分界值,通过该分界值将数组分成左右两部分。
(2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
(3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
(4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。
排序演示
假设一开始序列{xi}是:5,3,7,6,4,1,0,2,9,10,8。
此时,ref=5,i=1,j=11,从后往前找,第一个比5小的数是x8=2,因此序列为:2,3,7,6,4,1,0,5,9,10,8。
此时i=1,j=8,从前往后找,第一个比5大的数是x3=7,因此序列为:2,3,5,6,4,1,0,7,9,10,8。
此时,i=3,j=8,从第8位往前找,第一个比5小的数是x7=0,因此:2,3,0,6,4,1,5,7,9,10,8。
此时,i=3,j=7,从第3位往后找,第一个比5大的数是x4=6,因此:2,3,0,5,4,1,6,7,9,10,8。
此时,i=4,j=7,从第7位往前找,第一个比5小的数是x6=1,因此:2,3,0,1,4,5,6,7,9,10,8。
此时,i=4,j=6,从第4位往后找,直到第6位才有比5大的数,这时,i=j=6,ref成为一条分界线,它之前的数都比它小,之后的数都比它大,对于前后两部分数,可以采用同样的方法来排序。
代码举例
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>
using namespace std;
bool cmp(int a,int b)///实现从大到小排序的比较函数
{
return a>b;
}
int main()
{
int i,j;
int arr[10]={1,93,89,44,34,64,5,43,43,943};
sort(arr,arr+10,cmp);
for(i=0;i<10;i++)
cout<<arr[i]<<" "<<endl;
return 0;
}
归并排序
定义
归并排序(Merge Sort)是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
代码举例
#include<bits/stdc++.h>
using namespace std;
void merge (int arr[],int l,int m,int r)
{
int i,j,k;
int n1=m-l+1;
int n2=r-m;
int L[n1],R[n2];
for (i=0;i<n1;i++)
L[i]=arr[l+i];
for (j=0;j<n2;j++)
R[j]=arr[m+j+1];
i=0;
j=0;
k=l;
while (i<n1&&j<n2)
{
if (L[i]<=R[j])
{
arr[k]=L[i];
i++;
}
else
{
arr[k]=R[j];
j++;
}
k++;
}
while(i<n1)
{
arr[k]=L[i];
i++;
k++;
}
while (j<n2)
{
arr[k]=R[j];
j++;
k++;
}
}
void mergesort(int arr[],int l,int r)
{
if (l<r)
{
int m=l+(r-l)/2;
mergesort(arr,l,m);
mergesort(arr,m+1,r);
merge(arr,l,m,r);
}
}
void printarray(int arr[],int n)
{
for (int i=0;i<n;i++)
{
cout<<arr[i]<<" ";
}
cout<<endl;
}
int main()
{
int arr[]={38,27,43,3,9,82,10};
int n=sizeof(arr)/sizeof(int);
cout<<"given arr:"<<endl;
printarray(arr,n);
mergesort(arr,0,n-1);
cout<<"sorted array:"<<endl;
printarray(arr,n);
return 0;
}
四、DFS与BFS
深度优先遍历(DFS)
一、DFS的简介:
深度优先遍历(DFS)也叫du深度优先搜索。它的定义是:不断地沿着顶点dao的深度方向遍历。顶点的深度方向是指它的邻接点方向。
二、DFS的实现步骤:
1、从顶点出发。
2、访问顶点,也就是根节点。
3、依次从顶点的未被访问的邻接点出发,进行深度优先遍历;直至和顶点有路径相通的顶点都被访问。
4、若此时尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到所有顶点均被访问过为止。
代码举例
一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然25-24-23-…-3-2-1更长。事实上,这是最长的一条。
Input
输入的第一行表示区域的行数R和列数C(1 <= R,C <= 100)。下面是R行,每行有C个整数,代表高度h,0<=h<=10000。
Output
输出最长区域的长度。
Sample Input
5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
Sample Output
25
#include<iostream>
#include<cstdio>
using namespace std;
int R, C;
int map[105][105];
int mark[105][105] = { 0 };
int dfs(int i, int j)
{
int k;
if (mark[i][j]) return mark[i][j];
if (i != 0 && map[i - 1][j] < map[i][j])
{
k = dfs(i - 1, j) + 1;
if (k> mark[i][j]) mark[i][j] = k;
}
if (i != R - 1 && map[i + 1][j] < map[i][j])
{
k = dfs(i + 1, j) + 1;
if (k>mark[i][j]) mark[i][j] = k;
}
if (j != 0 && map[i][j - 1]<map[i][j])
{
k = dfs(i, j - 1) + 1;
if (k>mark[i][j]) mark[i][j] = k;
}
if (j != C - 1 && map[i][j + 1]<map[i][j])
{
k = dfs(i, j + 1) + 1;
if (k>mark[i][j]) mark[i][j] = k;
}
return mark[i][j];
}
int main()
{
int i, j, k, sum = 0;
scanf("%d%d", &R, &C);
for (i = 0; i < R; i++)
{
for (j = 0; j < C; j++)
{
scanf("%d", &map[i][j]);
}
}
for (i = 0; i < R; i++)
{
for (j = 0; j < C; j++)
{
k = dfs(i, j);
if (k>sum) sum = k;
}
}
cout << sum + 1 << endl;
return 0;
}
广度优先遍历(BFS)
定义
所谓宽度(广度)优先,就是每次都尝试访问同一层的节点。 如果同一层都访问完了,再访问下一层。这样做的结果是,BFS 算法找到的路径是从起点开始的 最短 合法路径。换言之,这条路所包含的边数最小。在 BFS 结束时,每个节点都是通过从起点到该点的最短路径访问的。
核心代码
*/
bool BFS(Node& Vs, Node& Vd){
queue<Node> Q;
Node Vn, Vw;
int i;
//初始状态将起点放进队列Q
Q.push(Vs);
hash(Vw) = true;//设置节点已经访问过了!
while (!Q.empty()){//队列不为空,继续搜索!
//取出队列的头Vn
Vn = Q.front();
//从队列中移除
Q.pop();
while(Vw = Vn通过某规则能够到达的节点){
if (Vw == Vd){//找到终点了!
//把路径记录,这里没给出解法
return true;//返回
}
if (isValid(Vw) && !visit[Vw]){
//Vw是一个合法的节点并且为白色节点
Q.push(Vw);//加入队列Q
hash(Vw) = true;//设置节点颜色
}
}
}
return false;//无解
}