对照本人有关数据结构专栏第四讲的内容,为了加强对栈的了解,我精心挑选如下习题,供有需要的读者参考
其中具体函数可以参照我的上一条博客:【数据结构】五分钟自测主干知识(四)
配合食用效果更佳~
http://t.csdnimg.cn/Gw0D4http://t.csdnimg.cn/Gw0D4
例1
背包问题求解。假设有一个体积为的背包和
个体积分别为
的物品,任意从
个物品中抽取
个,使其恰好装满背包(体积之和为
),即
。
求解的这类问题需要给出所有可能的解,因此需要穷举所有的物品组合。那么怎样才
能遍历所有可能的组合而又简化编程代码呢?通常使用“回溯”的设计思想来实现。
首先将所有物品排成一列,然后顺序选取物品放入背包,如果当前选取的物品不能装入背包(即包中已有的物品体积加上当前选取的物品体积超出了),则放弃它继续选取下一个物品,直至背包装满为止。如果装入某个物品后,剩下的物品都“不合适”再装入背包(即一直尝试到最后一个物品都不能放进背包),那么说明我们最后一个放进背包的物品“不合适”,需要取出它,并从它下一个物品继续尝试。如果某个物品放入背包后,恰巧装满背包,那么就需要输出当前的背包内物品作为一组解,然后再取出最后一个放进去的物品,从它下一个物品继续尝试下一个解。
这种从背包中取出最后一个放进去的物品再继续尝试的策略称为回溯。很显然,回溯时取出物品的次序和之前放入的次序刚好相反,最后放进去的最先被取出来,这和栈的“后进先出”特性是一致的。如果把背包用一个栈来表示,每次放入一个物品就是入栈,那么回溯时取出物品的操作实际上就是一个出栈操作。
还需要注意的问题是,入栈时栈元素的数据域内容不是物品的体积,而是物品排列的序号,这样方便回溯时容易找到“下一个”序号的物品。如果用
来存储各个物品的体积,那么
的取值为
,
号物品的体积是
,当
号物品不合适时,就需要尝试
号物品。
void knapsack(int w[], int T, int n) {//体积为T,总量为n
LinkStack S = new LNode;
InitStack(S);
int k = 0;//已经试过的
do {
while (k < n && T>0) {
if (T >= w[k]) {
Push(S, k);
T -= w[k];
}
k++;
}
if (T == 0) {
Traceback(S);//回溯
}
if (!StackEmpty(S)) {//无论是运行到最后一条,还是已经找到解法,都要回溯
Pop(S, k);
T += w[k];
k++;
}
} while (!StackEmpty(S) || k != n);
DestroyStack(S);
}
新增了两条函数
输出函数TraceBack(S),即执行输出的函数
void Traceback(LinkStack& S) {
ElemType k;
printf("找到一组解法: ");
for (int i = 0;; i++) {
if (Pop(S, k)) {
printf("%d ", k);
}
else {
printf("\n");
break;
}
}
}
还有一个判断栈是否为空的函数StackEmpt(S)
bool StackEmpty(LinkStack& S) {
return (S == NULL);
}
正在扩充ing……
搜寻好题还会放在此贴下~