前序遍历二叉树
使用栈进行前序历遍二叉树,在前序遍历中,首先访问根节点,然后递归地前序遍历左子树,最后递归地前序遍历右子树。
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
};
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
int* ans = (int*)malloc(sizeof(int) * 100);
*returnSize = 0;
struct TreeNode* node = root;
struct TreeNode* stack[300];
int top = 0;
while (top > 0 || node) {
while (node) {
ans[(*returnSize)++] = node->val;
stack[top++] = node;//即为进入更深一层函数。
node = node->left;
}//当一次循环终止代表,当前节点位于左子树的左空指针。
node = stack[--top];//回溯一层,进入关于右子树函数
node = node->right;
}
return ans;
}
在第13行,循环记录根节点数据域到数组,在将根节点存入栈中后,访问根节点的左孩子(每次访问节点时,第一个操作为记录根节点的数据域,以保证前序遍历的正确性)即不断增加深度,直到叶节点的左子树为空。后从栈(注意栈,后进先出)取出叶节点的根节点,访问右子树,后续操作与上面相同,记录数据域,访问更深度位置。
上述为使用栈模拟递归进行前序遍历。
解决实际问题时,与进行遍历二叉树类似,不同的是,在对子树的访问时,常常有更多种情况,并不是简单的左,右孩子,为降低复杂度,对显然错误的根节点及其子树不再访问,称之为剪枝。
这里以实际问题为例:
7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为Nπ的M层生日蛋糕,每层都是一个圆柱体。
设从下往上数第i(1 <= i <= M)层蛋糕是半径为Ri, 高度为Hi的圆柱。当i < M时,要求Ri > Ri+1且Hi > Hi+1。
由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积Q最小。
令Q = Sπ
请编程对给出的N和M,找出蛋糕的制作方案(适当的Ri和Hi的值),使S最小。
(除Q外,以上所有数据皆为正整数)
试规定
s
i
g
m
a
(
1
,
m
)
a
[
i
]
=
a
[
1
]
+
a
[
2
]
+
a
[
3
]
+
…
a
[
m
−
1
]
+
a
[
m
]
;
试规定{sigma(1, m)a[i]} = a[1]+a[2]+a[3]+…a[m-1]+a[m];
试规定sigma(1,m)a[i]=a[1]+a[2]+a[3]+…a[m−1]+a[m];
那么体积
N
∗
P
I
=
s
i
n
g
m
a
(
1
,
m
)
P
I
∗
R
i
∗
R
i
∗
H
i
,
那么体积 N*PI = singma(1,m)PI*Ri*Ri*Hi,
那么体积N∗PI=singma(1,m)PI∗Ri∗Ri∗Hi,
注意蛋糕外表面(最下一层的下底面除外)
所以 Q = S ∗ P I = R 1 ∗ R 1 + s i g m a ( 1 , n ) 2 ∗ P I ∗ R i ∗ H i 所以 Q = S*PI =R1*R1 + {sigma(1,n) 2*PI*Ri*Hi} 所以Q=S∗PI=R1∗R1+sigma(1,n)2∗PI∗Ri∗Hi
考虑到半径不断减少,并且为正整数,第m层最小为1,那么第1层最小为m,第i层最小半径为m-i+1。
类似的,第i层最小高度为m-i+1,那么就有最小体积,侧面积记作min_v[i],。
那么 对于(V[i-1] + min_v[i] > N) 的节点,不再进行访问。
但是该操作为静态操作,我们考虑更动态的剪枝: i在[k,m]区间内,就有Ri>=Rk,变形为:1 >= Ri/Rk
那么k层到m层增加的侧面积:
2
s
i
g
m
a
(
k
,
m
)
(
R
i
∗
H
i
)
>
=
2
s
i
g
m
a
(
k
,
m
)
[
R
i
∗
R
i
∗
H
i
]
/
R
k
=
二倍的剩余体积除以
R
k
2sigma(k,m)(Ri * Hi) >= 2sigma(k,m)[ Ri*Ri*Hi] /Rk = 二倍的剩余体积除以Rk
2sigma(k,m)(Ri∗Hi)>=2sigma(k,m)[Ri∗Ri∗Hi]/Rk=二倍的剩余体积除以Rk
因此 ,我们可以对不符合这该条件的部分进行剪枝。