-
树与二叉树
pat中树最实用的性质:满足连通,边数等于顶点数减一的图一定是一棵树(这个性质在图里面可能会考)。其他普通树的性质一般会在题目中提及,所以不必记忆。
pat中树的写法:1. 指针写法:适用于所有二叉树, 但是普通的树最好用静态写法。
2. 静态写法:普通树一般都是用这种写法(最简形式是不带数据域,直接一个vector数组描述节点关系)
3. 数组写法:最常用于完全二叉树(数组的存储顺序就是二叉树的层序遍历),普通二叉树也可以当作完全二叉 树,具体看题目要求。
-
二叉树的遍历
由于pat考试的性质,二叉树只会考和遍历有关的东西,所以拿到一个二叉树的题目第一想到的是怎么用遍历去解决。
一个特别重要的题型:根据中序遍历和前/后序遍历确定一棵二叉树,这是个很固定的题型。
具体做法太熟悉了,直接给出这题递归结束条件(递归结束条件是重中之重)当左子树指针比右子树指针还大的时候结束递归。
#include<cstdio>
#include<cstdlib>
#include<queue>
using namespace std;
typedef struct treenode{
int data;
struct treenode *Left;
struct treenode *Right;
}TreeNode;
typedef TreeNode *Tree;
Tree create_tree(int p1, int p2, int i1, int i2, int *post, int *in);
void levelOrder(int N, Tree root);
int Count = 0;
int main()
{
int N;
int post[31];
int in[31];
scanf("%d", &N);
for(int i = 0; i < N; i++)
scanf("%d", &post[i]);
for(int j = 0; j < N; j++)
scanf("%d", &in[j]);
Tree root = create_tree(0, N - 1, 0, N - 1, post, in);
levelOrder(N, root);
return 0;
}
void levelOrder(int N, Tree root)
{
queue<Tree> q;
q.push(root);
while(!q.empty())
{
Tree current = q.front();
q.pop();
if(current != NULL)
{
printf("%d", current -> data);
Count++;
if(Count < N)
printf(" ");
q.push(current -> Left);
q.push(current -> Right);
}
}
}
Tree create_tree(int p1, int p2, int i1, int i2, int *post, int *in)
{
if(i1 > i2)
return NULL;
Tree root = (Tree)malloc(sizeof(TreeNode));
root -> data = post[p2];
int position;
for(position = i1; position <= i2; position++)
{
if(in[position] == post[p2])
break;
}
int left_length = position - i1;
int right_length = i2 - position;
root -> Left = create_tree(p1, p1 + left_length - 1, i1, position - 1, post, in);
root -> Right = create_tree(p2 - right_length, p2 - 1, position + 1, i2, post, in);
return root;
}
之前说过,看到二叉树,就要想到四种遍历和根据前/后序遍历和中序遍历确定一棵二叉树。这题对于怎么构建一棵二叉树的条件弄得特别隐晦。
这个题目已经把中序遍历给你了,现在想怎么构建一棵二叉树出来,只得寻找前序遍历或者后序遍历。结果发现前序遍历就是push的顺序。
#include<cstdio>
#include<cstdlib>
#include<stack>
#include<cstring>
using namespace std;
typedef struct treenode{
int data;
struct treenode *Left;
struct treenode *Right;
}TreeNode;
typedef TreeNode *Tree;
int sum = 0;
Tree treeCreate(int p1, int p2, int i1, int i2, int *pre, int *in);
void postOrder(int N, Tree root);
int main()
{
int N;
scanf("%d", &N);
int pre[31], in[31];
stack<int> s;
char str[5];
int node, p = 1, q = 1;
for(int i = 1; i <= N * 2; i++)
{
scanf("%s", str);//注意scanf输入字符串时,以空格和回车作为结束标志,所以会直接接着下一步的数字输入
if(strcmp(str, "Push") == 0)
{
scanf("%d", &node);
s.push(node);
pre[p++] = node;
}
else
{
node = s.top();
s.pop();
in[q++] = node;
}
}
Tree root = treeCreate(1, N, 1, N, pre, in);
postOrder(N, root);
return 0;
}
void postOrder(int N, Tree root)
{
if(root != NULL)
{
postOrder(N, root -> Left);
postOrder(N, root -> Right);
printf("%d", root -> data);
sum++;
if(sum < N)
printf(" ");
}
}
Tree treeCreate(int p1, int p2, int i1, int i2, int *pre, int *in)
{
if(i1 > i2)
return NULL;
Tree current = (Tree)malloc(sizeof(TreeNode));
current -> data = pre[p1];
int x;
for(x = i1; x <= i2; x++)
{
if(pre[p1] == in[x])
break;
}
int len_left = x - i1;
int len_right = i2 - x;
current -> Left = treeCreate(p1 + 1, p1 + len_left, i1, x - 1, pre, in);
current -> Right = treeCreate(p2 - len_right + 1, p2, x + 1, i2, pre, in);
return current;
}
题目:invert tree
这个题目有两种解法,第一种是暴力解法,直接从根一路反转到叶子,虽说暴力,但是一点也不复杂。第二种就要想到树的遍历,只要看到二叉树就要想到树的遍历方式,用后序遍历可以解决,之要访问到根,就反转左右儿子,方法很巧妙。
代码不给出来了,太简单了。
-
树的遍历
树看起来比二叉树复杂多了,但是其实考法极为单一,只有两种:树的先根遍历和树的层序遍历。树的建立方式也特别的单一,大概只有静态写法了吧!具体的一些有用的点结合题目来说。
第一:深度优先搜索相比于广度优先搜索的好处就是,深度优先搜索查找顺序是按照一条树中存在的路径来寻找节点的(利用一个path数组来记录访问路径)而不像广度优先搜索需要一个数组来反向记录路径,然后再用递归给出路径。并且深度优先搜索的父节点和子节点是在递归过程中紧密关联的,所以,碰到树的遍历题目,第一要想到的是深度优先遍历。
第二:要善于利用外部数据结构来存储树在遍历过程中所产生的数据,比如这一题的路径就要存储在path数组中,但是一般树本身的数据比如:节点层数,节点权值等,记录在树的结点中访问比较方便。
这个题目就是根据上面两点写出来的。
#include<cstdio>//这个题目真的有点难度
#include<vector>
#include<algorithm>
using namespace std;
typedef struct treenode{
int weight;
vector<int> child;
}Tree;
Tree head[100];
bool com(int x, int y);
void DFS(int root, int sum, int lever, int *path, int S);
int main()
{
int N, M, S;
scanf("%d%d%d", &N, &M, &S);
int w;
for(int m = 0; m < N; m++)
{
scanf("%d", &w);
head[m].weight = w;
}
int root = 0, node, num, c;
for(int i = 0; i < M; i++)
{
scanf("%d%d", &node, &num);
for(int j = 0; j < num; j++)
{
scanf("%d", &c);
head[node].child.push_back(c);
}
sort(head[node].child.begin(), head[node].child.end(), com);//注意前面两个项是起始位置,传进函数的是起始位置对应的元素
}
int path[100];
DFS(root, head[root].weight, 1, path, S);
return 0;
}
void DFS(int root, int sum, int lever, int *path, int S)
{
if(sum < S)
path[lever] = root;
else if(sum == S && head[root].child.size() == 0)
{
path[lever] = root;
for(int i = 1; i <= lever; i++)
{
if(i != lever)
printf("%d ", head[path[i]].weight);
else
printf("%d", head[path[i]].weight);
}
printf("\n");
}
int lable;
for(int j = 0; j < head[root].child.size(); j++)
{
lable = head[root].child[j];
DFS(lable, head[lable].weight + sum, lever + 1, path, S);
}
}
bool com(int x, int y)
{
return head[x].weight > head[y].weight;
}
这题主要是要利用好外部数据结构来记录遍历过程中产生的数据,这题利用了一个hashTable数组来记录每层产生的叶子个数。
#include<cstdio>//此题题目都看错,导致浪费太多时间
#include<vector>
#include<queue>
using namespace std;
typedef struct treenode{
int level;
vector<int> child;
}Tree;
int hashTable[100] = {0};
void BFS(int root, Tree *ptr);
int main()
{
int N, M;
scanf("%d%d", &N, &M);
Tree head[N + 1];
int node, num, root = 1, c;
head[root].level = 1;
for(int i = 1; i <= M; i++)
{
scanf("%d%d", &node, &num);
for(int j = 1; j <= num; j++)
{
scanf("%d", &c);
head[node].child.push_back(c);
}
}
BFS(root, head);
int Max = -1, level;
for(int m = 0; m < 100; m++)
{
if(hashTable[m] > Max)
{
Max = hashTable[m];
level = m;
}
}
printf("%d %d", Max, level);
return 0;
}
void BFS(int root, Tree *ptr)
{
queue<int> q;
q.push(root);
int current, lable;
while(!q.empty())
{
current = q.front();
hashTable[ptr[current].level]++;
q.pop();
for(int i = 0; i < ptr[current].child.size(); i++)
{
lable = ptr[current].child[i];
ptr[lable].level = ptr[current].level + 1;
q.push(lable);
}
}
}
-
二叉查找树
二叉查找树的性质(往后继续补充):1. 二叉查找树的中序遍历是有序的。
2. 确定一棵二叉查找树之后,根据这颗树的先序遍历再去以二叉查找树的构建方法构建一 棵树,和原来的树相同。
二叉树的插入,删除,查找这些性质一定要多些,很重要。
题目:Search tree?
这个题目其实就是利用了性质2,书里面没有介绍。代码过于简单,不粘贴了。
题目:Complete Binary Search Tree
这个题目已经说明了要你给出一个完全二叉树,如何构建一个完全二叉树,答案很明显,给一个数组就已经是一个完全二叉树了,所以此题只要给出数据进行排序,然后根据二叉查找树中序遍历有序来逐步填充数据就行了。
最后要求给出层序遍历,直接顺序输出数组就行了。(代码里没这么干,因为当时没想到)
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
int child = 1, num = 1;
bool comp(int x, int y);
void inOrder(int N, int root, int *in, int *tree);
void levelOrder(int N, int root, int *tree);
int main()
{
int N;
scanf("%d", &N);
int in[N + 1];
for(int i = 1; i <= N; i++)
scanf("%d", &in[i]);
sort(in + 1, in + N + 1, comp);//注意是从开始位置到结束位置的下一位
int tree[N + 1];//数组本身就是层序,靠
inOrder(N, 1, in, tree);
levelOrder(N, 1, tree);
return 0;
}
void levelOrder(int N, int root, int *tree)
{
queue<int> q;
q.push(root);//存的是下标
int current;
while(!q.empty())
{
current = q.front();
if(num < N)
{
printf("%d ", tree[current]);
num++;
}
else
printf("%d", tree[current]);
q.pop();
if(current * 2 <= N)
q.push(current * 2);
if(current * 2 + 1 <= N)
q.push(current * 2 + 1);
}
}
void inOrder(int N, int root, int *in, int *tree)
{
if(root <= N)
{
inOrder(N, root * 2, in, tree);
tree[root] = in[child++];
inOrder(N, root * 2 + 1, in, tree);
}
}
bool comp(int x, int y)
{
return x < y;
}
-
并查集
-
优先队列
1.将无序的数组调整成堆:把每一个子树都调整成堆(这个思想可以运用在很多算法上),整个无序的数组就会被调整成堆了。从下往上,从右往左调整!!!
void adjust_heap(void)
{
for(int i = Size / 2; i >= 1; i--)
downAdjust(i);
}
2.堆的插入:把元素插在数组尾部,向上调整,upAdjust不会影响堆序性。
3.堆的删除:将尾部元素覆盖根节点,然后再把这个节点向下调整,downAdjust不会影响堆序性。