PAT甲級的題目有關於樹的題目,1053,1086,1090,1102,1106,1115,1119,1110
A1053
這題目比較簡單,給定一棵樹,給定一個數字,要你找到所有和等於給定數字的路徑。這個題目的樹沒有給出是二叉樹,自然不能按照原來二叉樹的方法來構建了。其實就是BFS,用DFS也是可以的,不過考慮到DFS還要新開一個數組來存儲路徑,還是使用BFS。
首先是樹的結構,只需要在原來二叉樹的基礎上修改一下就好了。
typedef struct treeNode {
int val;
int weight;
vector<struct treeNode *> generator;
int parent = -1;
} *TreeNode, treeNode;
把原來的左右子樹修改成一個vector存儲即可。樹的結構中新添加了一個parent,是用於找到根節點,因爲題目給出的樹是一個列表,接着是樹的父節點,不添加parent找不到父節點。如果parent是-1那麼就是根節點了。
TreeNode createTree(int n, TreeNode *array, int allNodes) {
for (int i = 0; i < n; ++i) {
int index;
int num;
cin >> index >> num;
index_map[index] = true;
for (int j = 0; j < num; ++j) {
int next;
cin >> next;
array[next]->parent = index;
array[index]->generator.push_back(array[next]);
}
sort(array[index]->generator.rbegin(), array[index]->generator.rend(), cmp);
}
for (int k = 0; k < allNodes; ++k) {
if (array[k]->parent == -1) {
return array[k];
}
}
return nullptr;
}
創建樹的過程,注意後面有一個排序操作,題目的輸出要求按照次序輸出。然後就是BFS操作了。
void findWeights(TreeNode root, int sum, vector<int> weightsAdd, int equalWeights) {
if (sum > equalWeights) {
return;
} else if (sum < equalWeights) {
sum += root->weight;
weightsAdd.push_back(root->weight);
if (sum == equalWeights) {
if (!index_map[root->val]) {
weights_path.push_back(weightsAdd);
weightsAdd.clear();
}
return;
}
for (int i = 0; i < root->generator.size(); ++i) {
TreeNode current = root->generator[i];
findWeights(current, sum, weightsAdd, equalWeights);
}
}
}
BFS把weightsAdd當成是一個拷貝過來的局部變量,不需要回滾操作,比DFS簡單多了。然後就是輸出了,只需要注意最後不要空格就好。
其實也可以用數組來完成,所佔空間可能更小。
1086
這個題目做了很久,要是在上機考試碰見這個題目應該就只有10來分了。題目大意是給定一個入棧出棧的序列,這個出入棧完成後得到的出棧序列是中序遍歷的序列,要你求這棵樹的後續遍歷。我直接從序列入手,發現push操作就是:如果當前節點有左子樹,就插入左子樹,否則就插入右子樹;pop操作是回退到上一課樹,按照這個操作就可以創建出一棵樹,然後後續遍歷即可。然而內存爆炸了。
之前考408的時候遇到過這樣的操作,好像是天勤ds裏面,中序遍歷如果使用棧來實現,入棧順序就是前序遍歷次序。所以題目表面上是給了一個序列,實際上給了前序和中序遍歷,讓你求後續遍歷。這個就很簡單了。
TreeNode create(int preL, int preR, int inL, int inR, int *preOrder, int *inOrder) {
int rootNum = preOrder[preL];
TreeNode root = new treeNode;
root->val = rootNum;
if (preL == preR) {
root->val = rootNum;
root->leftChild = root->rightChild = nullptr;
return root;
}
if (preL > preR) {
return nullptr;
}
int mid = -1;
for (int i = inL; i <= inR; ++i) {
if (inOrder[i] == rootNum) {
mid = i;
break;
}
}
int leftNumber = mid - inL;
int rightNumber = inR - mid;
root->leftChild = create(preL + 1, preL + leftNumber, inL, mid - 1, preOrder, inOrder);
root->rightChild = create(preR - rightNumber + 1, preR, mid + 1, inR, preOrder, inOrder);
return root;
}
關鍵就是創建樹的操作,天勤的代碼題出過這類,主要就是前中序遍歷的邊界數對就行。
1090
這個題目一開始讀的沒太懂,兩次都超了,只有14分。一開始還以爲最後要給出的是路徑上零售商的數量,沒想到是路徑的個數。
很明顯是BFS,和上上題一樣:
//typedef struct treeNode {
// int val;
// vector<struct treeNode *> children;
// int parent = -1;
//} *TreeNode, treeNode;
double maxPrice = -1;
int maxNumber = -1;
//void BFS(TreeNode root, double currentPrice, double r, vector<int> path) {
// if (root) {
// path.push_back(root->val);
// currentPrice = currentPrice * ((100 + r) / 100);
// if (maxPrice < currentPrice) {
// maxPrice = currentPrice;
// maxNumber = path.size();
// }
// for (int i = 0; i < root->children.size(); ++i) {
// BFS(root->children[i], currentPrice, r, path);
// }
// }
//}
建樹是不行的,直接就內存爆炸。如此那就直接用數組吧,於是想到從尾巴開始,然而題目有關鍵一句:It is assumed that each member in the supply chain has exactly one supplier except the root supplier, and there is no supply cycle.假設在供應鏈上的每名成員都只有一個供應者,除了根部的供應者,沒有環,那麼就不需要像圖一樣設置訪問標記。
// for (int j = 0; j < n; ++j) {
// if (!visited[j]) {
// int paths = 0;
// int fa = array[j];
// while (fa != -1) {
// visited[fa] = true;
// fa = array[fa];
// paths++;
// }
// maxNumber = ((maxNumber > paths) ? maxNumber : paths);
// }
// }
還是超時了,因爲如果從底部開始,會出現很多重複的道路,所以最好還是從底部開始訪問,我也是在這裏看了別人題解之後才知道,原來輸出的最後一個數字應該是路徑的個數,翻譯錯了。
vector<int> v[100010];
void bfs(int root, int depth) {
if (v[root].size() == 0) {
if (maxPrice < depth) {
maxPrice = depth;
maxNumber = 1;
} else if (maxPrice == depth) {
maxNumber++;
}
return;
}
for (int i = 0; i < v[root].size(); ++i) {
bfs(v[root][i], depth + 1);
}
}
用一個vector數組把所有兒子串起來,遍歷訪問即可。
1102
輸入詳情:每一個輸入包含了一個測試樣例。對於每一個例子,第一行給出一個正整數,也及是樹的總節點數,因此,節點編號從0到N-1,然後下面N行每一行對應節點0到N-1,每一行給出左右子樹。如果子樹不存在,用-來代替,每一對孩子用空格分離。
輸出詳情:對於每一個樣例,第一行打印翻轉樹的層序遍歷序列,然後第二行打印翻轉樹的中序遍歷序列,用空格隔開,最後不能有空格。
這個題目就非常簡單了,唯一的考點就是交換兩樹的左右子樹,直接後序遍歷交換就好了,釋放樹的內存也是後序遍歷實現。
for (int i = 0; i < n; ++i) {
array[i].val = i;
string left, right;
cin >> left >> right;
if (left == "-") {
array[i].leftChild = nullptr;
} else {
array[i].leftChild = &array[string2Number(left)];
array[string2Number(left)].parent = i;
}
if (right == "-") {
array[i].rightChild = nullptr;
} else {
array[i].rightChild = &array[string2Number(right)];
array[string2Number(right)].parent = i;
}
}
創建樹和前面幾個題目一樣。
void postExchange(TreeNode root) {
if (root) {
postExchange(root->leftChild);
postExchange(root->rightChild);
TreeNode temp = root->leftChild;
root->leftChild = root->rightChild;
root->rightChild = temp;
}
}
1106
一條供應鏈網絡由零售商,經銷商,供應商組成,每一總角色在這條網絡(產品從生產者到消費者)中都起到相應的作用。從根節點的供應者開始,每個人從供應商以P的價格購買,以比P價格高上百分之r的價格賣給經銷商或者是零售商。只有零售商纔會面對客戶,假設每一個成員只有一個提供者,而且沒有循環。
輸出詳情:每一個輸入包含一個測試樣例。對於每一個樣例,第一行包含一個正整數,標識供應鏈中成員總數,P標識根供應者的價格,也就是初始價格。r是比率,每經過一個經銷商或者是零售商價格需要提升r比率。接下來N行中,每一行描述的格式:K,ID,......。第i行中Ki表示總共有Ki個接受者,其實就是說第i號的提供者有這麼多個接受者。中間空格隔開。
輸出詳情:對於每一個測試樣例,打印出零售商的最低價格,也就是最後一個`葉子的最低價格,精確到四位小數,有多少個零售商可以拿到最低價格。
讀完題,一看就知道BFS,和前面那道題沒有什麼區別。
void BFS(int root, int depth, treeNode *array) {
if (depth > minDepth && minNumber > 0) {
return;
}
if (array[root].childen.size() == 0) {
if (depth < minDepth) {
minDepth = depth;
minNumber = 1;
} else if (depth == minDepth) {
minNumber++;
}
return;
}
for (int i = 0; i < array[root].childen.size(); ++i) {
BFS(array[root].childen[i], depth + 1, array);
}
}
僅僅修改此處即可。不過值得注意的是,我一開始是創建了一顆樹,後來發現超時,我一開始還以爲是遞歸調用超時,於是我添加了:
if (depth > minDepth && minNumber > 0) {
return;
}
如果已經找到了一個更短的,那麼如果還有,要麼和其相同,要麼更短,所以如果還有比他長的直接去掉。但是加上後PAT評測的時間有減少,但是還是有兩個數據是段錯誤,修改一下,把一些多餘數據去掉就可以了。推測原因應該是內存超限了。
1115
這個題目就不翻譯了,非常簡單,就要你找倒數第一和倒數第二層的節點數加起來。層序遍歷是最爲適合的了,BFS也可以。關鍵有幾個地方,構建樹:
TreeNode insert(TreeNode root, int num) {
if (root) {
if (root->val < num) {
root->rightChild = insert(root->rightChild, num);
} else {
root->leftChild = insert(root->leftChild, num);
}
return root;
} else {
root = new treeNode;
root->val = num;
root->leftChild = root->rightChild = nullptr;
return root;
}
}
在樹的結構中添加一個層次屬性,記錄屬於第幾層。
void level(TreeNode root) {
fill(depth, depth + MAX, 0);
queue<TreeNode> Queue;
Queue.push(root);
while (!Queue.empty()) {
TreeNode cur = Queue.front();
Queue.pop();
depth[cur->depth]++;
maxDepth = ((maxDepth > cur->depth) ? maxDepth : cur->depth);
if (cur->leftChild) {
cur->leftChild->depth = cur->depth + 1;
Queue.push(cur->leftChild);
}
if (cur->rightChild) {
cur->rightChild->depth = cur->depth + 1;
Queue.push(cur->rightChild);
}
}
}
每一次取出節點的時候順便直接存進數組裏面計數,免得害得遍歷一次數組。水題一道。