基于堆栈的广度优先搜索树遍历(二)

实验数据

使用 -DSPACEDATA 选项提供编译空间数据,使用 -DTIMEDATA 选项提供编译时间数据,使用 -DLPRINT 编译行打印,使用 -DNOPRINT 则不打印数据。 请注意:‘---------------’ 行之间的行是命令和它生成的输出。

子节点级别数为 7 且树深度为 3 的两种类型的正常打印

 ---------------
 >a.out -l3 -c7
 queue based, allocated for queue size 50 ,each node size 4 bytes
 printing queue based
 a0123456ABCDEFGABCDEFGABCDEFGABCDEFGABCDEFGABCDEFGABCDEFG
 printing stack based
 a0123456ABCDEFGABCDEFGABCDEFGABCDEFGABCDEFGABCDEFGABCDEFG
 printing done
 ---------------

打印基于堆栈的 BFS 方法的逐行输出

 ----------------
 >gcc -DLPRINT -lm stackbasedBFS.c
 >./a.out -l3 -c7
 queue based, allocated for queue size 50 ,each node size 4 bytes
 printing queue based
 a0123456ABCDEFGABCDEFGABCDEFGABCDEFGABCDEFGABCDEFGABCDEFG
 printing stack based
 a
 0123456
 ABCDEFGABCDEFGABCDEFGABCDEFGABCDEFGABCDEFGABCDEFG
 printing done
 ----------------

打印空间数据和时间数据

 ---------------
 >./a.out -l5 -c2
 queue based, allocated for queue size 17 ,each node size 4 bytes
 printing queue based
 a01ABABabababab0101010101010101
 queue based, queue usage size 16
 diff time 0 sec 26 micro

 printing stack based
 a01ABABabababab0101010101010101
 stack used 128
 diff time 0 sec 14 micro

 printing done
 ---------------

空间和时间分析

 --------------
 > cc -DNOPRINT -DSPACEDATA -DTIMEDATA -lm  stackbasedBFS.c
 > ./a.out -l10 -c10
 queue based, allocated for queue size 1000000001 ,each node size 4 bytes
 > ./a.out -l10 -c9
 queue based, allocated for queue size 387420490 ,each node size 4 bytes
 printing queue based
 Segmentation fault
 > ./a.out -l9 -c10
 queue based, allocated for queue size 100000001 ,each node size 4 bytes
 printing queue based
 queue based, queue usage size 100000000
 diff time 28 sec 490083 micro

 printing stack based
 stack used 256
 diff time 1 sec 469060 micro

 printing done
 > ./a.out -l5 -c10
 queue based, allocated for queue size 10001 ,each node size 4 bytes
 printing queue based
 queue based, queue usage size 10000
 diff time 0 sec 2891 micro

 printing stack based
 stack used 128
 diff time 0 sec 164 micro

 printing done
 > ./a.out -l10 -c7
 queue based, allocated for queue size 40353608 ,each node size 4 bytes
 printing queue based
 queue based, queue usage size 40353607
 diff time 11 sec 874163 micro

 printing stack based
 stack used 288
 diff time 0 sec 788580 micro

 printing done
 > ./a.out -l20 -c2
 queue based, allocated for queue size 524289 ,each node size 4 bytes
 printing queue based
 queue based, queue usage size 524288
 diff time 0 sec 333929 micro

 printing stack based
 stack used 608
 diff time 0 sec 40476 micro

 printing done
 > ./a.out -l25 -c2
 queue based, allocated for queue size 16777217 ,each node size 4 bytes
 printing queue based
 queue based, queue usage size 16777216
 diff time 10 sec 635081 micro

 printing stack based
 stack used 768
 diff time 1 sec 482634 micro

 printing done
 > ./a.out -l30 -c2
 queue based, allocated for queue size 536870913 ,each node size 4 bytes
 allocation failed, exiting

 > ./a.out -l28 -c2
 queue based, allocated for queue size 134217729 ,each node size 4 bytes
 allocation failed, exiting
 > ./a.out -l27 -c2
 queue based, allocated for queue size 67108865 ,each node size 4 bytes
 printing queue based
 queue based, queue usage size 67108864
 diff time 43 sec 22479 micro

 printing stack based
 stack used 832
 diff time 5 sec 773319 micro

 printing done
 -----------------

在空间分析中,我们可考虑最糟糕的场景,也就是 9 级和 10 个子节点。这里,基于队列的方法所使用的节点数量为 100,000,000,如果每个节点 4 个字节,累计内存大小为 100000000*4=400 MB(arr)。基于堆栈的方法使用的内存为 256 字节。在此程序中,它仅计算 8 级,而不是 9 级。对于 9 级,合计为 (9/8)*256=288,也就是每级 288/9=32 字节。在下一节中,我们将证明理想情况下这一数据为 20 字节(其中没有局部变量)。

在时间分析中,我们可考虑最糟的场景,也就是 27 级和 2 个子节点。基于队列的方法花费的时间为 42 秒和 22,479 微秒。基于堆栈的方法花费的时间为 5 秒和 773,319 微秒。

在这里,我们将讨论一下树的每个级别所用的 32 字节。

 ----------------
 > gdb
 GNU gdb 6.3
 Copyright 2004 Free Software Foundation, Inc.
 GDB is free software, covered by the GNU General Public License, and you are
 welcome to change it and/or distribute copies of it under certain conditions.
 Type "show copying" to see the conditions.
 There is absolutely no warranty for GDB.  Type "show warranty" for details.
 This GDB was configured as "i686-pc-linux-gnu".
 (gdb) file a.out
 Reading symbols from /home/pravinsi/a.out...done.
 Using host libthread_db library "/lib/tls/libthread_db.so.1".
 (gdb) b printtree
 Breakpoint 1 at 0x8048802: file stackbasedBFS.c, line 79.
 (gdb) run
 Starting program: /home/pravinsi/a.out
 queue based, allocated for queue size 50 ,each node size 4 bytes
 printing queue based
 a0123456ABCDEFGABCDEFGABCDEFGABCDEFGABCDEFGABCDEFGABCDEFG
 queue based, queue usage size 49
 diff time 0 sec 82 micro

 printing stack based

 Breakpoint 1, printtree (node=0x804b008, target=0, level=0) at stackbasedBFS.c:79
 79          __asm__("movl %%ebp, %0;" : "=r" ( i));
 (gdb) c
 Continuing.

 Breakpoint 1, printtree (node=0x804b008, target=1, level=0) at stackbasedBFS.c:79
 79          __asm__("movl %%ebp, %0;" : "=r" ( i));
 (gdb) bt
 #0  printtree (node=0x804b008, target=1, level=0) at stackbasedBFS.c:79
 #1  0x080488d6 in printbfstree (root=0x804b008) at stackbasedBFS.c:99
 #2  0x08048f51 in main (argc=1, argv=0xbffff894) at stackbasedBFS.c:216
 (gdb) c
 Continuing.

 Breakpoint 1, printtree (node=0x804b018, target=1, level=1) at stackbasedBFS.c:79
 79          __asm__("movl %%ebp, %0;" : "=r" ( i));
 (gdb) bt
 #0  printtree (node=0x804b018, target=1, level=1) at stackbasedBFS.c:79
 #1  0x0804886c in printtree (node=0x804b008, target=1, level=0) at stackbasedBFS.c:84
 #2  0x080488d6 in printbfstree (root=0x804b008) at stackbasedBFS.c:99
 #3  0x08048f51 in main (argc=1, argv=0xbffff894) at stackbasedBFS.c:216
 (gdb) f 1
 #1  0x0804886c in printtree (node=0x804b008, target=1, level=0) at stackbasedBFS.c:84
 84    for(i=0;i<CCOUNT;i++) if(printtree(node->child+i,target,level+1) ) returnval=true;
 (gdb) info reg ebp
 ebp            0xbffff7a8       0xbffff7a8
 (gdb) f 0
 #0  printtree (node=0x804b018, target=1, level=1) at stackbasedBFS.c:79
 79          __asm__("movl %%ebp, %0;" : "=r" ( i));
 (gdb) info reg ebp
 ebp            0xbffff788       0xbffff788
 (gdb) x/10x 0xbffff788
 0xbffff788:     0xbffff7a8      0x0804886c      0x0804b018      0x00000001
 0xbffff798:     0x00000001      0x08048d5b      0x000490cf      0x00000000
 0xbffff7a8:     0xbffff7c8      0x080488d6
 (gdb) x 0x0804886c
 0x804886c <printtree+112>:      0x8410c483
 (gdb) x 0x08048d5b
 0x8048d5b <BFS+413>:    0xc910c483
 -----------------

可以看到,两个连续基址指针(也就是 0xbffff7a8 和 0xbffff788)之间的内存区域为:

 0xbffff788:     0xbffff7a8      0x0804886c      0x0804b018      0x00000001
 0xbffff798:     0x00000001      0x08048d5b      0x000490cf      0x00000000
 0xbffff7a8:     0xbffff7c8

一个基址指针 (0xbffff7a8) 与下一个基址指针 (0xbffff788) 之间相差 32 字节。其中两个是局部变量 0x00000000(‘i‘) 和 0x000490cf(‘returnval‘),一个是缓存的数据 0x08048d5b。如果我们删除实现细节,仅查看一般内存使用,那么结果为每个级别 8-3=5. 5*4=20 字节。


读者练习

由于函数递归方法在内部使用了堆栈,所以另一种方法是显式使用堆栈而不是使用函数递归。读者可练习使用另一种非递归方法。它可以节省更多的内存。


结束语

实验数据符合我们的理论假设。我们已说明,在本文中提供的示例程序中,基于堆栈的 BFS 的树的每个级别占 32 字节,其中有 8 字节是算法树递归函数的局部变量。对于以另一种方式实现的树,编程人员可选择排除 8 字节的局部变量。同时,在此示例中,我们的二叉树中每个级别有 32 字节,总共 27 级,使用了 864 字节。但是,基于队列的 BFS 使用了 67108864*4 字节(约 256 MB)。这是一笔重大的空间节省。实验数据在时间方面仍然具有优势(这是一种新方法)。在最糟糕的情况下,基于堆栈的 BFS 花费了 5.7 秒,而基于队列的 BFS 花费了 43 秒。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define CCOUNT 7
#define false 0
#define true 1

typedef char bool;

typedef struct NODE {
       char data;
       struct NODE* child;
}NODE;

/*Tree Intializatin routine ** Not part of the algorithm*/
NODE* initializetree(NODE* root, int childrencount, int level) {
int i;
static int si;
if(root == NULL) {
si=level;
root=(NODE*)malloc(sizeof(NODE));
root->data=‘a‘;
root->child=NULL;
}
if(--level == 0) return root;
root->child=(NODE*)malloc(childrencount * sizeof(NODE));
for(i=0;i<childrencount;i++){
(root->child+i)->data=((si+1-level)%3?((si-level)%3?48:97):65)+i;
(root->child+i)->child=NULL;
initializetree(root->child+i,childrencount,level);
}
return root;
}

/*Tree deallocation routine ** Not part of the algorithm*/
bool deallocatetree(NODE* root,int level) {
int i;
if(root == NULL) return false;
if(root->child != NULL) {
for(i=0;i<CCOUNT;i++) if(deallocatetree(root->child+i,level+1)) break;
}else {
free(root);
return true;
}
if(i == CCOUNT) free(root->child);
if(level == 1) free(root);
return false;
}

/*BFS sub routine*/
bool printtree(NODE* node, int target, int level) {
int i;
bool returnval=false;
if (target > level) {
for(i=0;i<CCOUNT;i++) if(printtree(node->child+i,target,level+1) ) returnval=true;
}
else {
printf("%c",(node->data));
if(node->child != NULL) returnval=true;
}
return returnval;
}

/*BFS routine*/
void printbfstree(NODE* root) {
if(root == NULL) return;
int target=0;
while(printtree(root,target++,0)) {
printf("\n");
}
}

/*Main routine*/
int main(int argc, char **argv){
int c;
int desiredlevel=3;
while ((c=getopt(argc, argv, "hl:")) != -1)
switch(c) {
case ‘l‘ :
desiredlevel=atoi(optarg);
break;
case ‘h‘ :
printf("stackbasedBFS [-l level] [-h]\n");
return -1;
}
NODE* root=NULL;
root=initializetree(NULL,CCOUNT,desiredlevel);
printbfstree(root);
deallocatetree(root,1);
return 0;
}
时间: 2024-12-14 18:45:38

基于堆栈的广度优先搜索树遍历(二)的相关文章

SDUT 2141 【TEST】数据结构实验图论一:基于邻接矩阵的广度优先搜索遍历

数据结构实验图论一:基于邻接矩阵的广度优先搜索遍历 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Discuss Problem Description 给定一个无向连通图,顶点编号从0到n-1,用广度优先搜索(BFS)遍历,输出从某个顶点出发的遍历序列.(同一个结点的同层邻接点,节点编号小的优先遍历) Input 输入第一行为整数n(0< n <100),表示数据的组数.对于每组数据,第一行是三个整数k,m,t(0<

数据结构实验图论一:基于邻接矩阵的广度优先搜索遍历

数据结构实验图论一:基于邻接矩阵的广度优先搜索遍历 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 给定一个无向连通图,顶点编号从0到n-1,用广度优先搜索(BFS)遍历,输出从某个顶点出发的遍历序列.(同一个结点的同层邻接点,节点编号小的优先遍历) 输入 输入第一行为整数n(0< n <100),表示数据的组数. 对于每组数据,第一行是三个整数k,m,t(0<k<100,0<m<(k-1)*k/2,

数据结构之 图论---基于邻接矩阵的广度优先搜索遍历(输出bfs遍历序列)

数据结构实验图论一:基于邻接矩阵的广度优先搜索遍历 Time Limit: 1000MS Memory limit: 65536K 题目描述 给定一个无向连通图,顶点编号从0到n-1,用广度优先搜索(BFS)遍历,输出从某个顶点出发的遍历序列.(同一个结点的同层邻接点,节点编号小的优先遍历) 输入 输入第一行为整数n(0< n <100),表示数据的组数. 对于每组数据,第一行是三个整数k,m,t(0<k<100,0<m<(k-1)*k/2,0< t<k),

SDUT OJ 2413 数据结构实验图论一:基于邻接矩阵的广度优先搜索遍历

#include<iostream> #include<memory.h> using namespace std; int p[1010][1010]; int visit[110]; int c[1010]; int a=0; int b=1; int k; int t; void bfs(int n) { a++; for(int i=0;i<k;i++) { if(p[n][i]==1&&visit[i]==0) { c[b++]=i; visit[i

数据结构实验之图论二:基于邻接表的广度优先搜索遍历

数据结构实验之图论二:基于邻接表的广度优先搜索遍历 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 给定一个无向连通图,顶点编号从0到n-1,用广度优先搜索(BFS)遍历,输出从某个顶点出发的遍历序列.(同一个结点的同层邻接点,节点编号小的优先遍历) 输入 输入第一行为整数n(0< n <100),表示数据的组数. 对于每组数据,第一行是三个整数k,m,t(0<k<100,0<m<(k-1)*k/2,

SDUT 2142 【TEST】数据结构实验之图论二:基于邻接表的广度优先搜索遍历

数据结构实验之图论二:基于邻接表的广度优先搜索遍历 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Discuss Problem Description 给定一个无向连通图,顶点编号从0到n-1,用广度优先搜索(BFS)遍历,输出从某个顶点出发的遍历序列.(同一个结点的同层邻接点,节点编号小的优先遍历) Input 输入第一行为整数n(0< n <100),表示数据的组数.对于每组数据,第一行是三个整数k,m,t(0<

图的广度优先(BFS)遍历

广度优先搜索对图G中的边进行系统性的探索来发现可以从源节点s到达的所有节点. 该算法能够计算从源节点到所有可达节点的最小的边数. 所有节点在一开始的时候均被涂上了白色. 在算法推进过程中, 这些节点可能变成灰色或者黑色. 在搜索过程中, 第一次遇到一个节点就称该节点被发现, 此时该节点的颜色将发生改变. 为了理解或调试算法, 我们用灰色代表已经发现但还没有探索邻接边(出边), 用黑色代表已经发现且完成探索邻接边. 实际应用中无需区分灰色和黑色结点. 在执行广度优先搜索的过程中, 将构造一棵广度优

基于深度及广度优先搜索的迷宫问题的演示

1 时间复杂度分析 由于该图采用邻接矩阵存储,整个算法遍历的过程所花费的时间复杂度为该矩阵的N(row*col).而由于其需要分别访问已经定位,需要进行分别2次操作,如下: visited = new bool[col*row];//访问标记 for (i=0; i<row; i++) for (j=0; j<col; j++) visited[i*col+j] = false;//初始为未访问状态 position = new POSITION[col*row]; for (i=0; i&l

堆栈 Cookie 检测代码检测到基于堆栈的缓冲区溢出

 报错:0x000CC3C9 处有未经处理的异常(在 image_opencv2.exe 中):  堆栈 Cookie 检测代码检测到基于堆栈的缓冲区溢出. 主要检查代码中有没有对数组的越界操作,就解决了这个bug. 其它的相关知识查后再补充.