數據結構基礎知識——非線性數據結構(二叉樹、二叉排序樹、優先隊列、散列表)

數據結構基礎知識——非線性數據結構(二叉樹、二叉排序樹、優先隊列、散列表)

目錄:

  1. 基礎知識
    1. 二叉樹
    2. 二叉排序樹
    3. 優先隊列
    4. 哈夫曼樹
    5. 散列表
  2. 應用實例
    1. 哈夫曼樹【北京郵電大學】
    2. 複數集合【北京郵電大學】
    3. 二叉搜索樹【浙江大學】
    4. 二叉樹遍歷【清華大學】
    5. 二叉樹遍歷【華中科技大學】
    6. 二叉排序樹Ⅰ【華中科技大學】
    7. 二叉排序樹Ⅱ【華中科技大學】
    8. 查找學生信息【清華大學】
    9. 魔咒詞典【浙江大學】
    10. 子串計算【北京大學】
    11. 統計同成績學生人數【浙江大學】
    12. 開門人和關門人【浙江大學】

一、基礎知識

1、二叉樹(Binary Tree):

(1)定義:二叉樹要麼爲空,要麼由根結點(Root)、左子樹(Left Subtree)和右子樹(Right Subtree)構成,而左右兩個子樹又分別是一棵二叉樹。

(2)結構體定義:

  • struct TreeNode{
  •         ElementType data;
  •         TreeNode *lchild, *rchild;
  • };

(3)根據遍歷每個結點的左子樹L、結點本身N、右子樹R的順序不同,可將對二叉樹的遍歷方法分爲前序遍歷(NLR),中序遍歷(LNR),後序遍歷(LRN),其中的序是指根結點在何時被訪問。

  • 前序遍歷(PreOrder):
    • void PreOrder(TreeNode *T){
    •         if(T != NULL){
    •                 Visit(T->data);
    •                 PreOrder(T->leftchild);
    •                 PreOrder(T->rchild);
    •         }
    • }
  • 中序遍歷(InOrder):
    • void InOrder(TreeNode *T){
    •         if(T != NULL){
    •                 PreOrder(T->leftchild);
    •                 Visit(T->data);
    •                 PreOrder(T->rchild);
    •         }
    • }
  • 後序遍歷(PostOrder):
    • void PostOrder(TreeNode *T){
    •         if(T != NULL){
    •                 PreOrder(T->leftchild);
    •                 PreOrder(T->rchild);
    •                 Visit(T->data);
    •         }
    • }
  • 層次遍歷(LevelOrder):需要藉助一個隊列實現。先將二叉樹的根結點入隊,在隊伍非空的情況下訪問隊首結點,若它有左子樹,則將左子樹根結點入隊;若它有右子樹,則將右子樹根結點入隊。如此反覆,直到隊列爲空爲止。
    • void LevelOrder(TreeNode *T){
    •         queue<TreeNode *> q;
    •         if(T){
    •                 q.push(T);
    •         }
    •         while(!q.empty()){
    •                 TreeNode *current = q.front();
    •                 q.pop();
    •                 Visit(current->data);
    •                 if(current->lchild){
    •                         q.push(current->lchild);
    •                 }
    •                 if(current->rchild){
    •                         q.push(current->rchild);
    •                 }
    •         }
    • }

(4)根據先序遍歷、中序遍歷確定一棵二叉樹:

  • 在先序遍歷中,第一個結點必定是二叉樹的根結點;
  • 在中序遍歷中,該根結點必定可將中序遍歷的序列分爲兩個子序列,前一個子序列是根結點左子樹的中序遍歷序列,後一個子序列是根結點右子樹的中序遍歷序列;
  • 遞歸下去,則可以唯一確定該二叉樹。

2、二叉排序樹(Binary Search Tree):

(1)定義:對於一棵非空二叉排序樹,

  • 若左子樹非空,則左子樹上所有結點關鍵字的值均小於根結點關鍵字的值;
  • 若右子樹非空,則右子樹上所有結點關鍵字的值均大於根結點關鍵字的值;
  • 左右子樹本身也是一棵二叉排序樹。

(2)特點:

  • 各個數字的插入順序不同,則得到的二叉排序樹的形態可能不同。
  • 對二叉排序樹進行中序遍歷,會得到一個升序序列。
  • 通過建立一棵二叉排序樹,就能對原本無序的序列進行排序,並實現序列的動態維護。

3、優先隊列(Priority Queue):

(1)定義:能夠將元素按照事先規定的優先級次序進行動態組織,訪問元素時,只能訪問當前隊列中優先級最高的元素,即最高級先出(First-In Greatest-Out)。

(2)STL-priority_queue:

  • #include <queue>,先出隊列的元素是隊列中優先級最高的元素。用priority_queue<int> pq方式聲明一個隊列,取隊首元素的方法由queue的front()變爲了top()。
  • 常用方法:
    • priority_queue的狀態:
      • pq.size():返回當前優先隊列的元素個數。
      • pq.empty():返回當前優先隊列是否爲空。
    • priority_queue元素的添加或刪除:
      • pq.push():元素的入隊。
      • pq.pop():元素的出隊。
    • priority_queue元素的訪問:
      • pq.top():訪問當前優先隊列中優先級最高的元素。

4、哈夫曼樹/最優樹(Huffman Tree):

(1)定義:給定n個帶有權值的結點,以它們爲葉子結點構造一棵帶權路徑長度和最小的二叉樹。

  • 路徑:一棵樹中從任意一個結點到達另一個結點的通路。
  • 路徑長度:路徑上所需經過的邊的個數。
  • 帶權路徑長度:從根結點到達該結點的路徑長度再乘以該結點的權值稱爲該結點的帶權路徑長度。
  • 樹的帶權路徑長度:所有葉子結點的帶權路徑長度之和。

(2)特點:

  • n個帶有權值的結點構成的哈夫曼樹可能不唯一。

5、散列表(Hash Table)/ Map(映射/關聯數組):

(1)定義:一種根據關鍵字(key)直接訪問值(Value)的數據結構。

(2)特點:

  • 由於散列表摒棄了關鍵碼有序,因此在理想情況下可在期望的常數時間內實現所有接口操作。就平均時間複雜度的意義而言,這些操作的複雜度都是O(1).

(3)STL-map:

  • #include <map>,map是從鍵(key)到值(value)的映射。因爲重載了[]運算符,map像是數組的高級版,如可以用map<string, int> month_name來表示月份名字到月份編號的映射,然後用month_name["July"] = 7的方式來賦值。
  • map的底層由紅黑樹實現,內部仍然是有序的,其查找效率仍然爲O(logn)。
  • 無序映射unordered_map的底層是用散列表實現的,其期望的查找效率爲O(1)。
  • 常用方法:
    • map的狀態:
      • map.empty():返回當前映射是否爲空。
      • map.size():返回當前映射元素個數。
    • map元素的添加或刪除:
      • insert():實現元素的添加。
      • erase():實現元素的刪除。
    • map元素的訪問:
      • 由於map內部重載了[]運算符,因此可以通過[key]的方式訪問。
      • 通過at()進行訪問。
      • 通過迭代器進行訪問。
    • map元素操作:
      • find():查找特定的元素,找到則返回元素的迭代器,未找到則返回迭代器end()。
      • clear():將映射清空。
    • map迭代器操作:
      • begin():返回映射中首元素迭代器。
      • end():返回映射中尾元素之後一個位置的迭代器。

示例代碼:

#include <iostream>
#include <map>
#include <string>

using namespace std;

map<string, int> myMap;

int main(){
	//1、插入四個元素
	myMap["Dodo"] = 408;
	myMap["Tom"] = 308;
	myMap.insert(pair<string, int>("Jerry", 208));
	myMap.insert(pair<string, int>("Lisa", 108));
	//2、在map中鍵是唯一的,重複插入一個Dodo鍵,系統會忽略後面的對已存在的鍵的插入操作
	myMap.insert(pair<string, int>("Dodo", 333));
	//3、修改Lisa的值爲222,然後遍歷map
	myMap["Lisa"] = 222;
	for(map<string, int>::iterator iter = myMap.begin(); iter != myMap.end(); iter++){
		cout << (*iter).first << ": " << iter->second << endl;
	}
	//4、Jerry鍵的值,Lisa的值
	cout << myMap.at("Jerry") << ", " << myMap["Lisa"] << endl;
	//5、map的size
	cout << "map's size is : " << myMap.size() << endl;
	//6、刪除Jerry
	myMap.erase("Jerry"); 
	cout << "After delete Jerry, Size is:" << myMap.size() << endl;
	myMap.clear();
	if(myMap.empty()){
		cout << "it is empty" << endl;
	}
	//7、釋放內存
	map<string, int> tmp_map;
	myMap.swap(tmp_map);
	return 0;
}

二、應用實例

1、題目描述:哈夫曼樹,第一行輸入一個數n,表示葉結點的個數。需要用這些葉結點生成哈夫曼樹,根據哈夫曼樹的概念,這些結點有權值,即weight,題目需要輸出所有結點的權值與路徑長度的乘積之和。【北京郵電大學】

  • 輸入格式:輸入有多組數據。每組第一行輸入一個數n,接着輸入n個葉節點(葉節點權值不超過100,2<=n<=1000)。
  • 輸出格式:輸出所有結點的權值與路徑長度的乘積之和
  • 樣例輸入:
    • 5  
    • 1 2 2 5 9
  • 樣例輸出:
    • 37

示例代碼1:

#include <iostream>
#include <functional>
#include <queue>

using namespace std;

priority_queue<int, vector<int>, greater<int> > pq; //優先隊列按照小頂堆的方式輸出

int main(){
	int n;
	while(cin >> n){
		for(int i = 0; i < n; i++){
			int number;
			cin >> number;
			pq.push(number);
		}
		int answer = 0;
		while(pq.size() > 1){
			int min1 = pq.top();
			pq.pop();
			int min2 = pq.top();
			pq.pop();
			answer += min1 + min2;
			pq.push(min1 + min2);
		}
		cout << answer << endl;
		if(!pq.empty()){
			pq.pop();
		}
	}	
	return 0;
}

示例代碼2:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

vector<int> vec;

bool compDesc(const int &a, const int &b){
	return b < a;
}

int main(){
	int n;
	while(cin >> n){
		for(int i = 0; i < n; i++){
			int number;
			cin >> number;
			vec.push_back(number);
		}
		int result = 0;
		while(vec.size() >= 2){
			sort(vec.begin(), vec.end(), compDesc);//降序排序
			int min1 = vec.back();
			vec.pop_back();
			int min2 = vec.back();
			vec.pop_back();
			vec.push_back(min1 + min2);
			result += min1 + min2;
		}
		cout << result << endl;
		vec.clear();
	}
	return 0;
}

2、題目描述:一個複數(x+iy)集合,兩種操作作用在該集合上:     1、Pop 表示讀出集合中複數模值最大的那個複數,如集合爲空 輸出  empty  ,不爲空就輸出最大的那個複數並且從集合中刪除那個複數,再輸出集合的大小SIZE;     2 Insert a+ib  指令(a,b表示實部和虛部),將a+ib加入到集合中 ,輸出集合的大小SIZE;     最開始要讀入一個int n,表示接下來的n行每一行都是一條命令。【北京郵電大學】

  • 輸入格式:輸入有多組數據。每組輸入一個n(1<=n<=1000),然後再輸入n條指令。
  • 輸出格式:根據指令輸出結果。模相等的輸出b較小的複數。a和b都是非負數。
  • 樣例輸入:
    • 3
    • Pop
    • Insert 1+i2
    • Pop
  • 樣例輸出:
    • empty
    • SIZE = 1
    • 1+i2
    • SIZE = 0

示例代碼:

#include <iostream>
#include <queue>
#include <string>
#include <algorithm>

using namespace std;

struct Complex{
	int x;
	int iy;
	Complex(int x, int iy):x(x),iy(iy){}
};

priority_queue<Complex> pq;

bool operator<(Complex a, Complex b){ //返回true說明a的優先級低於b
	double aComp = pow(a.iy, 2) + pow(a.x, 2), bComp = pow(b.iy, 2) + pow(b.x, 2);
	if(aComp > bComp){
		return false;
	}else if(aComp < bComp){
		return true;
	}else{
		return a.iy > b.iy;
	}
}

int GetNumber(string s, int &index){
	int number = 0;
	while(isdigit(s[index])){
		number = number * 10 + s[index] - '0';
		index++;
	}
	return number;
}

int main(){
	int n;
	while(cin >> n){
		getchar();
		for(int i = 0; i < n; i++){
			string s;
			getline(cin, s);
			if(s == "Pop"){
				if(pq.empty()){
					cout << "empty" << endl;
				}else{
					cout << pq.top().x << "+i" << pq.top().iy << endl;
					pq.pop();
					cout << "SIZE = " << pq.size() << endl;
				}
			}else if(s.find("Insert") != -1){
				int index = 0;
				while(!isdigit(s[index])){
					index++;
				}
				int x = GetNumber(s, index);
				while(!isdigit(s[index])){
					index++;
				}
				int iy = GetNumber(s, index);
				pq.push(Complex(x, iy));
				cout << "SIZE = " << pq.size() << endl;
			}
		}
		if(!pq.empty()){
			pq.pop();
		}
	}
	return 0;
}

3、判斷兩序列是否爲同一二叉搜索樹序列【浙江大學】

  • 輸入格式:開始一個數n,(1<=n<=20) 表示有n個需要判斷,n= 0 的時候輸入結束。接下去一行是一個序列,序列長度小於10,包含(0~9)的數字,沒有重複數字,根據這個序列可以構造出一顆二叉搜索樹。接下去的n行有n個序列,每個序列格式跟第一個序列一樣,請判斷這兩個序列是否能組成同一顆二叉搜索樹。
  • 輸出格式:如果序列相同則輸出YES,否則輸出NO
  • 樣例輸入:
    • 2
    • 567432
    • 543267
    • 576342
    • 0
  • 樣例輸出:
    • YES
    • NO

示例代碼:

#include <iostream>
#include <string>
#include <vector>

using namespace std;

string originStr;
string tmpStr;

struct TreeNode{
	int data;
	struct TreeNode *left, *right;
	TreeNode(int x):data(x),left(NULL), right(NULL){}
};

TreeNode* InsertNode(TreeNode *T, int x){
	if(T == NULL){
		T = new TreeNode(x);
	}else if(x < T->data){
		T->left = InsertNode(T->left, x);
	}else if(x > T->data){
		T->right = InsertNode(T->right, x);
	}
	return T;
}

TreeNode* createTree(string s, TreeNode *T){
	for(int i = 0; i < s.size(); i++){
		T = InsertNode(T, s[i]);
	}
	return T;
}

void InOrder(TreeNode *T, vector<int> &v){
	if(T != NULL){
		InOrder(T->left, v);
		v.push_back(T->data);
		InOrder(T->right, v);
	}
}

void PreOrder(TreeNode *T, vector<int> &v){
	if(T != NULL){
		v.push_back(T->data);
		PreOrder(T->left, v);
		PreOrder(T->right, v);
	}
}

//判斷二者先序遍歷和中序遍歷序列是否一致
bool IsCorrespond(vector<int> a, vector<int> b){
	if(a.size() != b.size()){
		return false;
	}
	vector<int>::iterator iterA, iterB;
	for(iterA = a.begin(), iterB = b.begin(); 
		iterA != a.end() && iterB != b.end() && (*iterA) == (*iterB); iterA++, iterB++){}
	if(iterA == a.end()){
		return true;
	}
	return false;
}

int main(){
	int n;
	while(cin >> n && n){
		cin >> originStr;
		TreeNode *originalTree = NULL;
		originalTree = createTree(originStr, originalTree);
		vector<int> originInOrderVector, originPreOrderVector;
		InOrder(originalTree, originInOrderVector);
		PreOrder(originalTree, originPreOrderVector);
		vector<int> tmpInOrderVector, tmpPreOrderVector;
		for(int i = 0; i < n; i++){
			cin >> tmpStr;
			TreeNode *tmpTree = NULL;
			tmpTree = createTree(tmpStr, tmpTree);
			InOrder(tmpTree, tmpInOrderVector);
			PreOrder(tmpTree, tmpPreOrderVector);
			if(IsCorrespond(tmpInOrderVector, originInOrderVector) 
				&& IsCorrespond(tmpPreOrderVector, originPreOrderVector)){
					cout << "YES" << endl;
			}else{
				cout << "NO" << endl;
			}
			tmpInOrderVector.clear();
			tmpPreOrderVector.clear();
		}
		originInOrderVector.clear();
		originPreOrderVector.clear();
	}
	
	return 0;
}

4、題目描述:編一個程序,讀入用戶輸入的一串先序遍歷字符串,根據此字符串建立一個二叉樹(以指針方式存儲)。 例如如下的先序遍歷字符串: ABC##DE#G##F### 其中“#”表示的是空格,空格字符代表空樹。建立起此二叉樹以後,再對二叉樹進行中序遍歷,輸出遍歷結果。【清華大學】

  • 輸入格式:輸入包括1行字符串,長度不超過100。
  • 輸出格式:可能有多組測試數據,對於每組數據,輸出將輸入字符串建立二叉樹後中序遍歷的序列,每個字符後面都有一個空格。每個輸出結果佔一行。
  • 樣例輸入:
    • abc##de#g##f###
  • 樣例輸出:
    • c b e g d f a 

示例代碼:

#include <iostream>
#include <string>

using namespace std;

struct TreeNode{
	char data;
	struct TreeNode *left, *right;
	TreeNode(char data):data(data), left(NULL), right(NULL){}
};

TreeNode* InsertNode(string str, int &pos){
	char c = str[pos++];
	if(c == '#'){
		return NULL;
	}
	TreeNode *T = new TreeNode(c);
	T->left = InsertNode(str, pos);
	T->right = InsertNode(str, pos);
	return T;
}

void InOrder(TreeNode *T){
	if(T != NULL){
		InOrder(T->left);
		if(T->data != '#'){
			cout << T->data << " ";
		}
		InOrder(T->right);
	}
}

int main(){
	string s;
	while(cin >> s){
		int pos = 0;
		TreeNode *T = InsertNode(s, pos);
		InOrder(T);
		cout << endl;
	}
	return 0;
}

5、題目描述:二叉樹的前序、中序、後序遍歷的定義: 前序遍歷:對任一子樹,先訪問跟,然後遍歷其左子樹,最後遍歷其右子樹; 中序遍歷:對任一子樹,先遍歷其左子樹,然後訪問根,最後遍歷其右子樹; 後序遍歷:對任一子樹,先遍歷其左子樹,然後遍歷其右子樹,最後訪問根。 給定一棵二叉樹的前序遍歷和中序遍歷,求其後序遍歷(提示:給定前序遍歷與中序遍歷能夠唯一確定後序遍歷)。【華中科技大學】

  • 輸入格式:兩個字符串,其長度n均小於等於26。第一行爲前序遍歷,第二行爲中序遍歷。二叉樹中的結點名稱以大寫字母表示:A,B,C....最多26個結點。
  • 輸出格式:輸入樣例可能有多組,對於每組測試樣例,輸出一行,爲後序遍歷的字符串。
  • 樣例輸入:
    • ABC
    • BAC
    • FDXEAG
    • XDEFAG
  • 樣例輸出:
    • BCA
    • XEDGAF

示例代碼:

#include <iostream>
#include <string>

using namespace std;

struct TreeNode{
	char data;
	struct TreeNode *lchild, *rchild;
	TreeNode(char c):data(c), lchild(NULL), rchild(NULL){}
};

TreeNode* CreateTree(string preStr, string inStr){
	if(preStr.size() == 0 || inStr.size() == 0){
		return NULL;
	}
	char c = preStr[0];
	TreeNode *T = new TreeNode(c);
	int pos = inStr.find(c);
	T->lchild = CreateTree(preStr.substr(1, pos), inStr.substr(0, pos));
	T->rchild = CreateTree(preStr.substr(pos + 1), inStr.substr(pos + 1));
	return T;
}

void PostOrder(TreeNode *T){
	if(T != NULL){
		PostOrder(T->lchild);
		PostOrder(T->rchild);
		cout << T->data;
	}
}

int main(){
	string preStr, inStr;
	while(cin >> preStr && cin >> inStr){
		TreeNode *T = CreateTree(preStr, inStr);
		PostOrder(T);
		cout << endl;
	}
	return 0;
}

若已知中序遍歷序列和後序遍歷序列,求前序遍歷序列示例代碼:

#include <iostream>
#include <string>

using namespace std;

struct TreeNode{
	char data;
	struct TreeNode *lchild, *rchild;
	TreeNode(char c):data(c), lchild(NULL), rchild(NULL){}
};

TreeNode* CreateTree(string inStr, string postStr){
	if(inStr.size() == 0 || postStr.size() == 0){
		return NULL;
	}
	char c = postStr[postStr.size() - 1];
	TreeNode *T = new TreeNode(c);
	int pos = inStr.find(c);
	T->lchild = CreateTree(inStr.substr(0, pos), postStr.substr(0, pos));
	T->rchild = CreateTree(inStr.substr(pos + 1), postStr.substr(pos, postStr.size() - pos - 1));
	return T;
}

void PreOrder(TreeNode *T){
	if(T != NULL){
		cout << T->data;
		PreOrder(T->lchild);
		PreOrder(T->rchild);
	}
}

int main(){
	string inStr, postStr;
	while(cin >> inStr >> postStr){
		TreeNode *T = CreateTree(inStr, postStr);
		PreOrder(T);
		cout << endl;
	}
	return 0;
}

/* 測試樣例:
輸入:
BAC
BCA
XDEFAG
XEDGAF
DBHIEAFCGKJ
DIHEBFKJGCA

輸出:
ABC
FDXEAG
ABDEHICFGJK

*/

6、題目描述:輸入一系列整數,建立二叉排序樹,並進行前序,中序,後序遍歷。【華中科技大學】

  • 輸入格式:輸入第一行包括一個整數n(1<=n<=100)。接下來的一行包括n個整數。
  • 輸出格式:可能有多組測試數據,對於每組數據,將題目所給數據建立一個二叉排序樹,並對二叉排序樹進行前序、中序和後序遍歷。每種遍歷結果輸出一行。每行最後一個數據之後有一個空格。輸入中可能有重複元素,但是輸出的二叉樹遍歷序列中重複元素不用輸出。
  • 樣例輸入:
    • 5
    • 1 6 5 9 8
  • 樣例輸出:
    • 1 6 5 9 8 
    • 1 5 6 8 9 
    • 5 8 9 6 1 

示例代碼:

#include <iostream>
#include <string>

using namespace std;

struct TreeNode{
	int data;
	struct TreeNode *lchild, *rchild;
	TreeNode(int i):data(i), lchild(NULL), rchild(NULL){}
};

TreeNode* InsertNode(TreeNode *T, int data){
	if(T == NULL){
		T = new TreeNode(data);
	}else if(data > T->data){
		T->rchild = InsertNode(T->rchild, data);
	}else if(data < T->data){
		T->lchild = InsertNode(T->lchild, data);
	}
	return T;
}

int GetNumber(string s, int &index){
	int number = 0;
	while(isdigit(s[index])){
		number = number * 10 + s[index] - '0';
		index++;
	}
	return number;
}

TreeNode* CreateTree(string s){
	int i = 0;
	TreeNode *T = NULL;
	while(i < s.size()){
		if(isdigit(s[i])){
			T = InsertNode(T, GetNumber(s, i));
		}
		i++;
	}
	return T;
}

void PreOrder(TreeNode *T){
	if(T != NULL){
		cout << T->data << " ";
		PreOrder(T->lchild);
		PreOrder(T->rchild);
	}
}

void InOrder(TreeNode *T){
	if(T != NULL){
		InOrder(T->lchild);
		cout << T->data << " ";
		InOrder(T->rchild);
	}
}

void PostOrder(TreeNode *T){
	if(T != NULL){
		PostOrder(T->lchild);
		PostOrder(T->rchild);
		cout << T->data << " ";
	}
}

int main(){
	int n;
	string str;
	while(cin >> n){
		getchar();
		getline(cin, str);
		TreeNode *T = CreateTree(str);
		PreOrder(T);
		cout << endl;
		InOrder(T);
		cout << endl;
		PostOrder(T);
		cout << endl;
	}
	return 0;
}

7、題目描述:二叉排序樹,也稱爲二叉查找樹。可以是一顆空樹,也可以是一顆具有如下特性的非空二叉樹: 1. 若左子樹非空,則左子樹上所有節點關鍵字值均不大於根節點的關鍵字值; 2. 若右子樹非空,則右子樹上所有節點關鍵字值均不小於根節點的關鍵字值; 3. 左、右子樹本身也是一顆二叉排序樹。 現在給你N個關鍵字值各不相同的節點,要求你按順序插入一個初始爲空樹的二叉排序樹中,每次插入後成功後,求相應的父親節點的關鍵字值,如果沒有父親節點,則輸出-1。【華中科技大學】

  • 輸入格式:輸入包含多組測試數據,每組測試數據兩行。第一行,一個數字N(N<=100),表示待插入的節點數。第二行,N個互不相同的正整數,表示要順序插入節點的關鍵字值,這些值不超過10^8。
  • 輸出格式:輸出共N行,每次插入節點後,該節點對應的父親節點的關鍵字值。
  • 樣例輸入:
    • 5
    • 2 5 1 3 4
  • 樣例輸出:
    • -1
    • 2
    • 2
    • 5
    • 3

示例代碼:

#include <iostream>
#include <string>

using namespace std;

struct TreeNode{
	int data;
	struct TreeNode *lchild, *rchild;
	TreeNode(int i):data(i), lchild(NULL), rchild(NULL){}
};

TreeNode* InsertNode(TreeNode *T, int data, TreeNode *parent){
	if(T == NULL){
		T = new TreeNode(data);
		if(parent){
			cout << parent->data << endl;
		}else{
			cout << -1 << endl;
		}
	}else if(data > T->data){
		T->rchild = InsertNode(T->rchild, data, T);
	}else if(data < T->data){
		T->lchild = InsertNode(T->lchild, data, T);
	}
	return T;
}

int GetNumber(string s, int &index){
	int number = 0;
	while(isdigit(s[index])){
		number = number * 10 + s[index] - '0';
		index++;
	}
	return number;
}

TreeNode* CreateTree(string s){
	int i = 0;
	TreeNode *T = NULL;
	while(i < s.size()){
		if(isdigit(s[i])){
			T = InsertNode(T, GetNumber(s, i), NULL);
		}
		i++;
	}
	return T;
}

int main(){
	int n;
	string str;
	while(cin >> n){
		getchar();
		getline(cin, str);
		TreeNode *T = CreateTree(str);
		
	}
	return 0;
}

8、題目描述:輸入N個學生的信息,然後進行查詢。【清華大學】

  • 輸入格式:輸入的第一行爲N,即學生的個數(N<=1000)
    接下來的N行包括N個學生的信息,信息格式如下:
    01 李江 男 21
    02 劉唐 男 23
    03 張軍 男 19
    04 王娜 女 19
    然後輸入一個M(M<=10000),接下來會有M行,代表M次查詢,每行輸入一個學號,格式如下:
    02
    03
    01
    04
  • 輸出格式:輸出M行,每行包括一個對應於查詢的學生的信息。如果沒有對應的學生信息,則輸出“No Answer!”
  • 樣例輸入:
    • 4
    • 01 李江 男 21
    • 02 劉唐 男 23
    • 03 張軍 男 19
    • 04 王娜 女 19
    • 5
    • 02
    • 03
    • 01
    • 04
    • 03
  • 樣例輸出:
    • 02 劉唐 男 23
    • 03 張軍 男 19
    • 01 李江 男 21
    • 04 王娜 女 19
    • 03 張軍 男 19

示例代碼:

#include <iostream>
#include <map>
#include <string>

using namespace std;

struct Student{
	string name;
	string sex;
	int age;
	Student(){}
	Student(string n, string s, int a):name(n), sex(s), age(a){}
};

map<string, Student> myMap;



int main(){
	int n, searchN;
	cin >> n;
	getchar();
	string code, name, sex;
	int age;
	for(int i = 0; i < n; i++){
		cin >> code;
		cin >> name;
		cin >> sex;
		cin >> age;
		Student *stu = new Student(name, sex, age);
		myMap.insert(pair<string, Student>(code, *stu));
	}
	cin >> searchN;
	for(int i = 0; i < searchN; i++){
		cin >> code;
		map<string, Student>::iterator it = myMap.find(code);
		if(it == myMap.end()){
			cout << "No Answer!" << endl;
		}else{
			Student stu = myMap[code];
			cout << code << " " << stu.name << " " << stu.sex << " " << stu.age << endl;
		}
	}
	myMap.clear();
	return 0;
}

9、題目描述:哈利波特在魔法學校的必修課之一就是學習魔咒。據說魔法世界有100000種不同的魔咒,哈利很難全部記住,但是爲了對抗強敵,他必須在危急時刻能夠調用任何一個需要的魔咒,所以他需要你的幫助。     給你一部魔咒詞典。當哈利聽到一個魔咒時,你的程序必須告訴他那個魔咒的功能;當哈利需要某個功能但不知道該用什麼魔咒時,你的程序要替他找到相應的魔咒。如果他要的魔咒不在詞典中,就輸出“what?”【浙江大學】

  • 輸入格式:首先列出詞典中不超過100000條不同的魔咒詞條,每條格式爲:[魔咒] 對應功能
    其中“魔咒”和“對應功能”分別爲長度不超過20和80的字符串,字符串中保證不包含字符“[”和“]”,且“]”和後面的字符串之間有且僅有一個空格。詞典最後一行以“@END@”結束,這一行不屬於詞典中的詞條。
    詞典之後的一行包含正整數N(<=1000),隨後是N個測試用例。每個測試用例佔一行,或者給出“[魔咒]”,或者給出“對應功能”。
  • 輸出格式:每個測試用例的輸出佔一行,輸出魔咒對應的功能,或者功能對應的魔咒。如果魔咒不在詞典中,就輸出“what?”
  • 樣例輸入:
    • [expelliarmus] the disarming charm
    • [rictusempra] send a jet of silver light to hit the enemy
    • [tarantallegra] control the movement of one's legs
    • [serpensortia] shoot a snake out of the end of one's wand
    • [lumos] light the wand
    • [obliviate] the memory charm
    • [expecto patronum] send a Patronus to the dementors
    • [accio] the summoning charm
    • @END@
    • 4
    • [lumos]
    • the summoning charm
    • [arha]
    • take me to the sky
  • 樣例輸出:
    • light the wand
    • accio
    • what?
    • what?

示例代碼:

#include <iostream>
#include <map>
#include <string>

using namespace std;

map<string, string> magicMap; //key爲魔法名,value爲解釋
map<string, string> infoMap;  //key爲解釋,value爲魔法名

int main(){
	string str;
	while(getline(cin, str) && str != "@END@"){
		int index = str.find("]");
		magicMap.insert(pair<string, string>(str.substr(0, index + 1), str.substr(index + 2)));
		infoMap.insert(pair<string, string>(str.substr(index + 2), str.substr(1, index - 1)));
	}
	int N;
	cin >> N;
	getchar();
	for(int i = 0; i < N; i++){
		getline(cin, str);
		map<string, string>::iterator iter1 = magicMap.find(str);
		map<string, string>::iterator iter2 = infoMap.find(str);
		if(iter1 != magicMap.end()){
			cout << magicMap[str] << endl;
		}else if(iter2 != infoMap.end()){
			cout << infoMap[str] << endl;
		}else{
			cout << "what?" << endl;
		}
	}
	return 0;
}

10、給出一個01字符串(長度不超過100),求其每一個子串出現的次數。【北京大學】

  • 輸入格式:輸入包含多行,每行一個字符串。
  • 輸出格式:對每個字符串,輸出它所有出現次數在1次以上的子串和這個子串出現的次數,輸出按字典序排序。
  • 樣例輸入:
    • 10101
  • 樣例輸出:
    • 0 2
    • 01 2
    • 1 3
    • 10 2
    • 101 2

示例代碼:

#include <iostream>
#include <map>
#include <string>

using namespace std;

map<string, int> myMap;

int main(){
	string str;
	cin >> str;
	for(int i = 0; i < str.size(); i++){
		for(int j = 1; j <= str.size() - i; j++){
			if(myMap[str.substr(i, j)] == 0){
				myMap[str.substr(i, j)] = 1;
			}else{
				myMap[str.substr(i, j)]++;
			}
		}
	}
	for(map<string, int>::iterator iter = myMap.begin(); iter != myMap.end(); iter++){
		if(iter->second >= 2){
			cout << iter->first << " " << iter->second << endl;
		}
	}
	return 0;
}

附註:

(1)if else也可以直接寫爲:

for(int j = 1; j <= str.size() - i; j++){
    myMap[str.substr(i, j)]++; //map[key]不存在時返回value的缺省值
}

11、題目描述:讀入N名學生的成績,將獲得某一給定分數的學生人數輸出。【浙江大學】

  • 輸入格式:測試輸入包含若干測試用例,每個測試用例的格式爲第1行:N;第2行:N名學生的成績,相鄰兩數字用一個空格間隔;第3行:給定分數;當讀到N=0時輸入結束。其中N不超過1000,成績分數爲(包含)0到100之間的一個整數。
  • 輸出格式:對每個測試用例,將獲得給定分數的學生人數輸出。
  • 樣例輸入:
    • 3
    • 80 60 90
    • 60
    • 2
    • 85 66
    • 0
    • 5
    • 60 75 90 55 75
    • 75
    • 0
  • 樣例輸出:
    • 1
    • 0
    • 2

示例代碼:

#include <iostream>
#include <map>

using namespace std;

map<int, int> gradeMap;

int main(){
	int number;
	while(cin >> number && number){
		int grade, searchGrade;
		for(int i = 0; i < number; i++){
			cin >> grade;
			gradeMap[grade]++;
		}
		cin >> searchGrade;
		cout << gradeMap[searchGrade] << endl;
		gradeMap.clear();
	}
	return 0;
}

12、題目描述:每天第一個到機房的人要把門打開,最後一個離開的人要把門關好。現有一堆雜亂的機房簽到、籤離記錄,請根據記錄找出當天開門和關門的人。【浙江大學】

  • 輸入格式:每天的記錄在第一行給出記錄的條目數M (M > 0 ),下面是M行,每行的格式爲:證件號碼 簽到時間 籤離時間
    其中時間按“小時:分鐘:秒鐘”(各佔2位)給出,證件號碼是長度不超過15的字符串。
  • 輸出格式:對每一天的記錄輸出1行,即當天開門和關門人的證件號碼,中間用1空格分隔。注意:在裁判的標準測試輸入中,所有記錄保證完整,每個人的簽到時間在籤離時間之前,且沒有多人同時簽到或者籤離的情況。
  • 樣例輸入:
    • 3
    • CS301111 15:30:28 17:00:10
    • SC3021234 08:00:00 11:25:25
    • CS301133 21:45:00 21:58:40
  • 樣例輸出:
    • SC3021234 CS301133

示例代碼:

#include <iostream>
#include <map>
#include <string>

using namespace std;

map<string, string> comeMap; //到來Map,key爲簽到時間,value爲證件號碼
map<string, string> outMap;  //離開Map,key爲籤離時間,value爲證件號碼

int main(){
	int n;
	cin >> n;
	for(int i = 0; i < n; i++){
		string code, comeTime, outTime;
		cin >> code;
		cin >> comeTime;
		cin >> outTime;
		comeMap[comeTime] = code;
		outMap[outTime] = code;
	}
	cout << comeMap.begin()->second << " " << outMap.rbegin()->second << endl;
	return 0;
}

參考文獻:

[1]劉汝佳. 算法競賽入門經典(第2版). [M]北京:清華大學出版社,2014.06;

[2]楊澤邦、趙霖. 計算機考研——機試指南(第2版). [M]北京:電子工業出版社,2019.11;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章