14、数据结构
1)二叉树
1.常用操作
struct TreeNode{
int data;
TreeNode *leftChild;
TreeNode *rightChild;
};
//前序遍历
void PreOrder(TreeNode *root){
if(root == NULL) return;
visit(root->data);
PreOrder(root->leftChild);
PreOrder(root->rightChild);
return;
}
//层序遍历
void levelOrder(TreeNode *root){
queue<TreeNode*> myQueue;
if(root != NULL) myQueue.push(root);
while(!myQueue.empty()){
TreeNode *current = myQueue.front();
myQueue.pop();
visit(current->data);
if(current->leftChild != NULL)
myQueue.push(current->leftChild);
if(current->rightChild != NULL)
myQueue.push(current->rightChild);
}
}
2.二叉树遍历(清华大学复试上机题)
题目描述:
编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一棵二叉树(以指针方式存储)。例如,先序遍历字符串 ABC##DE#G##F###,其中“#”表示空格,空格字符代表空树。建立这棵二叉树后,再对二叉树进行中序遍历,输出遍历结果。
#include <bits/stdc++.h>
using namespace std;
struct TreeNode{
char data;
TreeNode *leftChild;
TreeNode *rightChild;
TreeNode(char c): data(c), leftChild(NULL), rightChild(NULL){}
};
TreeNode *Build(int &position, string str){
char c = str[position++];
if(c == '#') return NULL;
TreeNode *root = new TreeNode(c);
root->leftChild = Build(position,str);
root->rightChild = Build(position, str);
return root;
}
void InOrder(TreeNode *root){
if(root == NULL) return;
InOrder(root->leftChild);
cout<<root->data<<" ";
InOrder(root->rightChild);
return;
}
int main(){
string str;
while(cin>>str){
int position = 0;
TreeNode *root = Build(position, str);
InOrder(root);
}
cout<<endl;
return 0;
}
2)二叉排序树
1.二叉排序树(华中科技大学复试上机题)
题目描述:
二叉排序树也称二叉查找树。它可以是一棵空树,也可以是一棵具有如下特性的非空二叉树:1. 若左子树非空,则左子树上所有结点的关键字值均不大于根结点的关键字值。2. 若右子树非空,则右子树上所有结点的关键字值均不小于根结点的关键字值。3. 左、右子树本身也是一棵二叉排序树。 现在给你 N 个关键字值各不相同的结点,要求你按顺序将它们插入一个初始为空树的二叉排序树,每次插入成功后,求相应父结点的关键字值,若没有父结点,则输出-1。
#include <bits/stdc++.h>
using namespace std;
struct TreeNode{
int data;
TreeNode *leftChild;
TreeNode *rightChild;
TreeNode(int x): data(x),leftChild(NULL),rightChild(NULL){}
};
TreeNode *insert(TreeNode *root,int x,int father){
if(root == NULL){
root = new TreeNode(x);
cout<<father<<endl;
}else if(x < root->data){
root->leftChild = insert(root->leftChild,x,root->data);
}else{
root->rightChild = insert(root->rightChild,x,root->data);
}
return root;
}
int main(){
int n;
while(cin>>n){
TreeNode *root = NULL;
for(int i = 0;i < n;i++){
int x;
cin>>x;
root = insert(root,x,-1);
}
}
return 0;
}
3)优先队列
1.常用操作
#include <bits/stdc++.h>
using namespace std;
int main(){
priority_queue<int> queue;
cout<<queue.size()<<endl;
queue.push(20);
queue.push(100);
queue.push(30);
queue.push(50);
cout<<queue.top()<<endl;
cout<<queue.size()<<endl;
int sum = 0;
while(!queue.empty()){
cout<<queue.top()<<endl;
sum += queue.top();
queue.pop();
}
cout<<sum<<endl;
return 0;
}
2.哈夫曼树(北京邮电大学复试上机题)
题目描述:
哈夫曼树,第一行输入一个数 n,表示叶结点的个数。需要用这些叶结点生成哈夫曼树,根据哈夫曼树的概念,这些结点有权值,即 weight,题目需要输出所有结点的值与权值的乘积之和。
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
while(cin>>n){
priority_queue<int, vector<int>, greater<int> > queue;
while(n--){
int x;
cin>>x;
queue.push(x);
}
int answer = 0;
while(1 < queue.size()){
int a = queue.top();
queue.pop();
int b = queue.top();
queue.pop();
answer += a + b;
queue.push(a+b);
}
cout<<answer<<endl;
}
return 0;
}
3.查找第 K 小的数(北京邮电大学复试上机题)
题目描述:
查找一个数组中第 K 小的数,注意同样大小算一样大。例如,在 2, 1, 3, 4, 5, 2 中,第三小的数是3。
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
while(cin>>n){
priority_queue<int, vector<int>, greater<int> > queue;
int pre;
for(int i = 0;i < n;i++){
cin>>pre;
queue.push(pre);
}
int k;
cin>>k;
pre = queue.top();
for(int i = 1;i < k;i++){
queue.pop();
if(pre == queue.top())
i--;
pre = queue.top();
}
cout<<queue.top()<<endl;
}
return 0;
}
4.搬水果(吉林大学复试上机题)
题目描述:
在一个果园里,小明已经将所有的水果打了下来,并按水果的不同种类分成了若干堆,小明决定把所有的水果合成一堆。每一次合并,小明可以把两堆水果合并到一起,消耗的体力等于两堆水果的重量之和。当然,经过 n 1 次合并之后,就变成一堆了。小明在合并水果时总共消耗的体力等于每次合并所耗体力之和。假定每个水果的重量都为 1,并且已知水果的种类数和每种水果的数目,你的任务是设计出合并的次序方案,使小明耗费的体力最少,并输出这个最小的体力耗费值。例如有 3 种水果,数目依次为 1, 2, 9。可以先将 1, 2 堆合并,新堆数目为3,耗费体力为3。然后将新堆与原先的第三堆合并得到更新的堆,耗费体力为 12。所以小明总共耗费的体力为3 + 12 = 15,可以证明 15 是最小的体力耗费值。
#include <bits/stdc++.h>
using namespace std;
int main(){
int n,t;
while(cin>>n){
if(n == 0) break;
priority_queue<int, vector<int>, greater<int> > queue;
for(int i = 0;i < n;i++){
cin>>t;
queue.push(t);
}
int answer = 0;
while(queue.size() > 1){
int a = queue.top();
queue.pop();
int b = queue.top();
queue.pop();
answer += a + b;
queue.push(a+b);
}
cout<<answer<<endl;
}
return 0;
}
4)散列表
1.常用操作
#include <bits/stdc++.h>
using namespace std;
int main(){
map<string, int> myMap;
myMap["Emma"] = 67;
myMap["Benedict"] = 100;
myMap.insert(pair<string,int>("Bob",72));
myMap.insert(pair<string,int>("mary",20));
cout<<myMap.size()<<endl;
cout<<myMap["Bob"]<<endl;
cout<<myMap.at("mary")<<endl;
myMap.erase("mary");
map<string,int>::iterator it;
for(it = myMap.begin();it != myMap.end();it++){
cout<<it->first<<endl;
cout<<it->second<<endl;
}
myMap.clear();
if(myMap.empty()){
cout<<"myMap is empty"<<endl;
}else{
cout<<"myMap is not empty"<<endl;
}
it = myMap.find("Bob");
if(it != myMap.end()){
cout<<"Bob is found"<<endl;
}else{
cout<<"Bob is not found"<<endl;
}
return 0;
}
2.魔咒词典(浙江大学复试上机题)
题目描述:
哈利·波特在魔法学校的必修课之一就是学习魔咒。据说魔法世界有100000 种不同的魔咒,波特很难全部记住,但为了对抗强敌,他必须在危急时刻能够调用任何一个需要的魔咒,所以他需要你的帮助。给你一部魔咒词典。当哈利听到一个魔咒时,你的程序必须告诉他那个魔咒的功能;当哈利需要某个功能但不知道该用什么魔咒时,你的程序要替他找到相应的魔咒。如果他要的魔咒不在词典中,那么你的程序就输出“what?”。
#include <bits/stdc++.h>
using namespace std;
int main(){
map<string,string> dictionary;
string str;
while(getline(cin,str)){
if(str == "@END@") break;
int pos = str.find("]");
string key = str.substr(0,pos+1);
string value = str.substr(pos+2);
dictionary[key] = value;
dictionary[value] = key;
}
int n;
cin>>n;
getchar();
while(n--){
string key;
getline(cin,key);
string answer = dictionary[key];
if(answer == ""){
answer = "what?";
}else if(answer[0] == '['){
answer = answer.substr(1,answer.size() - 2);
}
cout<<answer<<endl;
}
return 0;
}
3.子串计算(北京大学复试上机题)
题目描述:
给出一个 01 字符串(长度不超过 100),求其每个子串出现的次数。
#include <bits/stdc++.h>
using namespace std;
int main(){
string str;
while(cin>>str){
map<string, int> number;
for(int i = 0;i <= str.size();i++){
for(int j = 0;j < i;j++){
string key = str.substr(j,i-j);
number[key]++;
}
}
map<string, int>::iterator it;
for(it = number.begin();it != number.end();it++){
if(it->second > 1)
cout<<it->first<<" "<<it->second<<endl;
}
}
return 0;
}
15、图论
1)并查集
1.畅通工程(浙江大学复试上机题)
题目描述:
某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1000;
int father[MAXN];
int height[MAXN];
void initial(int n){
for(int i = 0;i <= n;i++){
father[i] = i;
height[i] = 0;
}
}
int find(int x){
if(x != father[x]){
father[x] = find(father[x]);
}
return father[x];
}
void Union(int x,int y){
x = find(x);
y = find(y);
if(x != y){
if(height[x] < height[y]) father[x] = y;
else if(height[y] < height[x]) father[y] = x;
else{
father[y] = x;
height[x]++;
}
}
}
int main(){
int n,m;
while(cin>>n){
if(n == 0) break;
cin>>m;
initial(n);
while(m--){
int x,y;
cin>>x>>y;
Union(x,y);
}
int answer = -1;
for(int i = 1;i <= n;i++){
if(find(i) == i) answer++;
}
cout<<answer<<endl;
}
return 0;
}
2.连通图(吉林大学复试上机题)
题目描述:
给定一个无向图和其中的所有边,判断这个图是否所有顶点都是连通的。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1000;
int father[MAXN];
int height[MAXN];
void initial(int n){
for(int i = 0;i <= n;i++){
father[i] = i;
height[i] = 0;
}
}
int find(int x){
if(x != father[x]){
father[x] = find(father[x]);
}
return father[x];
}
void Union(int x,int y){
x = find(x);
y = find(y);
if(x != y){
if(height[x] < height[y]) father[x] = y;
else if(height[y] < height[x]) father[y] = x;
else{
father[y] = x;
height[x]++;
}
}
}
int main(){
int n,m;
while(cin>>n){
if(n == 0) break;
cin>>m;
initial(n);
while(m--){
int x,y;
cin>>x>>y;
Union(x,y);
}
int answer = 0;
for(int i = 1;i <= n;i++){
if(find(i) == i) answer++;
}
if(answer == 1) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
2)最小生成树
1.还是畅通工程(浙江大学复试上机题)
题目描述:
某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1000;
struct Edge{
int from;
int to;
int length;
bool operator< (const Edge& e) const{
return length < e.length;
}
};
Edge edge[MAXN * MAXN];
int father[MAXN];
int height[MAXN];
void initial(int n){
for(int i = 0;i <= n;i++){
father[i] = i;
height[i] = 0;
}
}
int find(int x){
if(x != father[x]){
father[x] = find(father[x]);
}
return father[x];
}
void Union(int x,int y){
x = find(x);
y = find(y);
if(x != y){
if(height[x] < height[y]) father[x] = y;
else if(height[y] < height[x]) father[y] = x;
else{
father[y] = x;
height[x]++;
}
}
}
int Kruskal(int n,int edgeNumber){
initial(n);
sort(edge, edge + edgeNumber);
int sum = 0;
for(int i = 0;i < edgeNumber;i++){
Edge current = edge[i];
if(find(current.from) != find(current.to)){
Union(current.from, current.to);
sum += current.length;
}
}
return sum;
}
int main(){
int n,m;
while(cin>>n){
if(n == 0) break;
int edgeNumber = n * (n-1) / 2;
for(int i = 0;i < edgeNumber;i++){
cin>>edge[i].from>>edge[i].to>>edge[i].length;
}
int answer = Kruskal(n,edgeNumber);
cout<<answer<<endl;
}
return 0;
}
3)最短路径
1.畅通工程续(浙江大学复试上机题)
题目描述:
某省自从实行了很多年的畅通工程计划后,终于修建了很多路。不过路多了也不好,每次要从一个城镇到另一个城镇时,都有许多种道路方案可以选择,而某些方案要比另一些方案行走的距离短很多。这让行人很困扰。 现在,已知起点和终点,请你计算出要从起点到终点,最短需要行走多少距离。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200;
const int INF = INT_MAX;
struct Edge{
int to;
int length;
Edge(int t,int l): to(t),length(l){}
};
struct Point{
int number;
int distance;
Point(int n,int d): number(n), distance(d){}
bool operator< (const Point& p) const{
return distance > p.distance;
}
};
vector<Edge> graph[MAXN];
int dis[MAXN];
void Dijkstra(int s){
priority_queue<Point> q;
dis[s] = 0;
q.push(Point(s,dis[s]));
while(!q.empty()){
int u = q.top().number;
q.pop();
for(int i = 0;i < graph[u].size();i++){
int v = graph[u][i].to;
int d = graph[u][i].length;
if(dis[v] > dis[u] + d){
dis[v] = dis[u] + d;
q.push(Point(v,dis[v]));
}
}
}
}
int main(){
int n,m;
while(cin>>n>>m){
memset(graph,0,sizeof(graph));
fill(dis,dis+n,INF);
while(m--){
int from,to,length;
cin>>to>>length;
graph[from].push_back(Edge(to,length));
graph[to].push_back(Edge(from,length));
}
int s,t;
cin>>s>>t;
Dijkstra(s);
if(dis[t] == INF) dis[t] = -1;
cout<<dis[t]<<endl;
}
return 0;
}
4)拓扑排序
1.确定比赛名次
题目描述:
有 N 个比赛队(1 <= N <= 500),依次以编号 1, 2, 3,…, N 进行比赛,比赛结束后,裁判委员会要将所有参赛队伍从前往后依次排名,但现在裁判委员会不能直接获得每个队的比赛成绩,只知道每场比赛的结果,即 P1 赢 P2,用 P1, P2 表示,排名时 P1 在 P2 之前。现在请你编写程序确定排名。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 501;
vector<int> graph[MAXN];
int inDegree[MAXN];
vector<int> TopologicalSort(int n){
vector<int> topology;
priority_queue<int, vector<int>, greater<int> > node;
for(int i = 1;i <= n;i++){
if(inDegree[i] == 0){
node.push(i);
}
}
while(!node.empty()){
int u = node.top();
node.pop();
topology.push_back(u);
for(int i = 0;i < graph[u].size();i++){
int v = graph[u][i];
inDegree[v]--;
if(inDegree[v] == 0) node.push(v);
}
}
while(!node.empty()){
int u = node.top();
node.pop();
topology.push_back(u);
for(int i = 0;i < graph[u].size();i++){
int v = graph[u][i];
inDegree[v]--;
if(inDegree[v] == 0) node.push(v);
}
}
return topology;
}
int main(){
int n,m;
while(cin>>n>>m){
memset(graph,0,sizeof(graph));
memset(inDegree,0,sizeof(inDegree));
while(m--){
int from,to;
cin>>from>>to;
graph[from].push_back(to);
inDegree[to]++;
}
vector<int> answer = TopologicalSort(n);
for(int i =0;i < answer.size();i++){
if(i == 0) cout<<answer[i]<<" ";
else cout<<answer[i]<<" ";
}
cout<<endl;
}
return 0;
}