转载自: http://blog.csdn.net/wzy_1988/article/details/11790511
前言
博主明天上午9点还有面试,今天突然看到某大牌IT公司笔试题目,必须做一下了
题目
一、单选题
1.假设把整数关键码K散列到N个槽列表,以下哪些散列函数是好的散列函数
A: h(K)=K/N;
B: h(K)=1;
C: h(K)=K mod N;
D: h(K)=(K+rand(N)) mod N, rand(N)返回0到N-1的整数
选择C,解释:开始纠结于C和D,但是hash的特性在于常数的时间执行插入、删除和查找操作,用D作为hash函数无法满足该条件,用C产生碰撞可以用链接法解决冲突,感谢@ zdw12242的纠正
2.下面排序算法中,初始数据集的排列顺序对算法的性能无影响的是:
A: 堆排序 B:插入排序
C: 冒泡排序 D:快速排序
选择A,解释:(1)堆排序的时间复杂度一直都是O(nlogn),不稳定(2)插入排序在初始有序情况下,时间复杂度为O(n),平均时间复杂度为O(n^2),稳定排序(3)冒泡排序在初始有序的情况下,增加交换标示flag可将时间复杂度降到O(n),稳定排序(4)快速排序在初始有序的情况下,可能会退化到O(n^2),不稳定排序
3. 下面说法错误的是:
A: CISC计算机比RISC计算机指令多
B: 在指令格式中,采用扩展操作码设计方案的目的是为了保持指令字长不变而增加寻址空间
C:增加流水线段数理论上可以提高CPU频率
D:冯诺依曼体系结构的主要特征是存储程序的工作方式
选择B,解释(1)CISC复杂指令集,RISC精简指令集,从名字上就可以得出A正确(2)保持指令字长度不变而增加指令操作的数量(3)看样子都觉得正确(4)冯诺依曼体系结构的主要特点:存储程序控制(要求计算机完成的功能,必须事先编制好相应的程序,并输入到存储器中,计算机的工作过程是运行程序的过程);程序由指令构成,指令和数据都用二进制表示;指令由操作码和地址码构成;机器以cpu为中心
4. 不属于冯诺依曼体系结构必要组成部分是:
A:CPU B: Cache C:RAM D:ROM
B,解释:冯诺依曼体系结构必要组成部分:运算器、控制器、存储器、输入设备、输出设备,Cache属于缓存了
5. 一个栈的入栈序列式ABCDE则不可能的出栈序列是:
A:DECBA B:DCEBA C:ECDBA D:ABCDE
C,不解释
6.你认为可以完成编写一个C语言编译器的语言是:
A:汇编 B:C语言 C:VB D:以上全可以
D,解释:见知乎的一个解答 http://www.zhihu.com/question/20822934,其实你学好编译原理用什么语言都能写出来
7. 关于C++/JAVA类中的static成员和对象成员的说法正确的是:
A:static成员变量在对象构造时候生成
B: static成员函数在对象成员函数中无法调用
C: 虚成员函数不可能是static成员函数
D: static成员函数不能访问static成员变量
C,解释:虽然博主主要以php和c为主,php也能面向对象,我来简单说明一下。(1)static成员变量可以直接定义,例如public statci $a = 10; 所以A错(2)在对象成员函数里可以通过类名::static函数名的方法调用,我的项目中超过静态方法(4)同样道理,类名::static成员变量名,这也是访问static成员变量唯一的方法(3)是正确的,虽然我都不知道什么是虚函数,排除法可以完成
9:某进程在运行过程中需要等待从磁盘上读入数据,此时进程的状态将:
A: 从就绪变为运行 B:从运行变为就绪
C: 从运行变为阻塞 D:从阻塞变为就绪
C,解释:I/O事件让进程从running->waitting,参考链接: http://blog.csdn.net/wzy_1988/article/details/11003521
10:下面算法的时间复杂度为:
Int f(unsigned int n)
{
If(n==0||n==1)
Return 1;
Else
Return n*f(n-1);
}
A: O(1) B:O(n) C:O(N*N) D:O(n!)
B,解释:没啥好解释的
11: n从1开始,每个操作可以选择对n加1或者对n加倍。若想获得整数2013,最少需要多少个操作。
A:18 B:24 C:21 D;不可能
A,解释:
数学方法->从 2013 倒推, 奇数 减一,偶数 除2,
编程实现->是一个明显的bfs题目,编程实现为18,共享一下自己的bfs代码:
- #include <stdio.h>
- #include <stdlib.h>
-
- #define FINAL 2013
-
- #define MAX 25
-
- typedef struct num {
- int d, time;
- } num;
-
-
- typedef struct queue {
- int front, rear, count;
- num data[10000000];
- } queue;
-
-
- void enQueue(queue *q, num d)
- {
- q->data[q->rear ++] = d;
- q->count ++;
- }
-
- num deQueue(queue *q)
- {
- num res;
- res = q->data[q->front ++];
- q->count --;
-
- return res;
- }
-
-
- int main(void)
- {
- int flag = 0;
-
- num bt, one, two, s;
-
- bt.d = 2;
- bt.time = 1;
-
- queue *q = (queue *)malloc(sizeof(queue));
- q->front = q->rear = q->count = 0;
-
- enQueue(q, bt);
-
- while (q->count > 0) {
- s = deQueue(q);
-
- if (s.d == FINAL) {
- flag = 1;
- printf("%d\n", s.time);
- break;
- }
-
- one.d = s.d + 1;
- one.time = s.time + 1;
- if (one.d <= FINAL && one.time <= MAX) {
- enQueue(q, one);
- }
-
- two.d = s.d * 2;
- two.time = s.time + 1;
- if (two.d <= FINAL && two.time <= MAX) {
- enQueue(q, two);
- }
-
- printf("%d\n", q->count);
- }
-
- if (flag == 0)
- printf("不可能!\n");
-
- return 0;
- }
12:对于一个具有n个顶点的无向图,若采用邻接表数据结构表示,则存放表头节点的数组大小为:
A: n B: n+1 C: n-1 D:n+边数
A,解释:感觉没啥好解释的,n个顶点数组大小应该就是n吧,如果非要从下标从1开始,那就是n+1,蛋疼的题目,话说在ACM上写bfs,dfs,最短路径全是用邻接矩阵,就谁会用邻接表这么蛋疼的设计,又不是hash
14:如下函数,在32bit系统foo(2^31-3)的值是:
Int foo(int x)
{
Return x&-x;
}
A: 0 B: 1 C:2 D:4
C,解释:我只想说注意运算符优先级,注意^是异或
15.对于顺序存储的线性数组,访问节点和增加节点删除节点的时间复杂度为:
A: O(n),O(n) B:O(n),O(1) C:O(1),O(n) D:O(n),O(n)
C,解释:给定下标,访问为O(1),增加和删除节点涉及到移动操作为O(n)
16:在32为系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是:
Struct A
{
int a;
short b;
int c;
char d;
};
Struct B
{
int a;
short b;
char c;
int d;
};
A: 16,16 B:13,12 C:16,12 D:11,16
C,解释:字节对齐包含了每个变量自身对齐和复杂类型整体对齐。pragma pack参考链接: http://blog.csdn.net/wzy_1988/article/details/11834881
对于A:
- int a自身对齐是4,pragma pack指定对齐也是4,因此其有效对齐为4,起始地址0x0000满足0x0000 % 4 == 0
- short b自身对齐是2,指定对齐是4,因此有效对齐为其最小值2,起始地址0x0004满足0x0004 % 2 == 0
- int c自身对齐是4,pragma pack指定对齐也是4,因此其有效对齐为4,起始地址0x0006不满足0x0006 % 4 == 0,因此需要填充空字节,起始地址为0x0008
- char d自身对齐是1,指定对齐是4,因此有效对齐为其最小值1,起始地址0x000C满足0x000C % 1 == 0
- 结构体还需要整体对齐,也就是结构体成员最大有效对齐的倍数,0x000D不满足 % 4 ==0, 需要需要补充3个字节,总字节数为16
对于B:
- int a自身对齐是4,pragma pack指定对齐也是4,因此其有效对齐为4,起始地址0x0000满足0x0000 % 4 == 0
- short b自身对齐是2,指定对齐是4,因此有效对齐为其最小值2,起始地址0x0004满足0x0004 % 2 == 0
- char d自身对齐是1,指定对齐是4,因此有效对齐为其最小值1,起始地址0x0006满足0x000C % 1 == 0
- int c自身对齐是4,pragma pack指定对齐也是4,因此其有效对齐为4,起始地址0x0007不满足 % 4 ==0,需要补充一个空字节,起始地址为0x0008
- 结构体需要整体对齐,结构体整体有效对齐是4,0x000C % 4 == 0,因此总字节为12
17.袋中有红球,黄球,白球各一个,每次任意取一个放回,如此连续3次,则下列事件中概率是8/9的是:
A: 颜色全相同 B:颜色全不相同C:颜色不完全相同 D:颜色无红色
C,解释:(1)颜色全相同:C(1,3) / 27 = 1 / 9(2)颜色全不相同:3 * 2 * 1 / 27 = 2 / 9 (4)颜色无红色: 2 * 2 * 2 / 27 = 8 / 27 (3)颜色不完全相同 = 1 - P(颜色完全相同) = 1 - 1 / 9 = 8 / 9
18.一个洗牌程序的功能是将n张牌的顺序打乱,以下关于洗牌程序的功能定义说法最恰当的是:
A: 每张牌出现在n个位置上的概率相等
B: 每张牌出现在n个位置上的概率独立
C: 任何连续位置上的两张牌的内容独立
D: n张牌的任何两个不同排列出现的概率相等
D,解释:乐乐说选D,其实我觉得A也挺对的,这到题目我写了一个测试洗牌的程序,但是自己测试问题很大,怀疑是随机数获取的问题,求大家帮忙指点:
- #include <stdio.h>
- #include <stdlib.h>
- #include <time.h>
-
- #define N 10
-
- int arr[N] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-
- void shuffleArray()
- {
- int i, loc, tmp;
- time_t t;
-
- srand((unsigned int)time(&t));
-
-
- for (i = 0; i < N; i ++) {
- loc = rand() % (i + 1);
- tmp = arr[loc];
- arr[loc] = arr[i];
- arr[i] = tmp;
- }
-
-
- for (i = 0; i < N; i ++)
- printf("%d ", arr[i]);
- printf("\n");
- }
-
-
- int main(void)
- {
- int i, n;
-
- while (scanf("%d", &n) != EOF) {
- for (i = 0; i < n; i ++) {
-
- shuffleArray();
- }
- }
-
- return 0;
- }
19.用两种颜色去染排成一个圈的6个棋子,如果通过旋转得到则只算一种,一共有多少种染色:
A: 10 B:11 C:14: D:15
不会,这么多概率题目啊,我去
20.递归式的先序遍历一个n节点,深度为d的二叉树,则需要栈空间的大小为:
A: O(n) B:O(d) C:O(logn) D:(nlogn)
B,解释:需要考虑最坏的情况,A和B我不确定啊,蛋疼
二、多选题
21.两个线程运行在双核机器上,每个线程主线程如下,线程1:x=1;r1=y;线程2:y=1;r2=x;
X和y是全局变量,初始为0。以下哪一个是r1和r2的可能值:
A: r1=1,r2=1
B: r1=1,r2=0
C:r1=0,r2=0
D:r1=0,r2=1
ABD,解释:r1和r2不可能同时为0,当一个有赋值时,必然完成了对另一个x或y的赋值
22.关于Linux系统的负载,以下表述正确的是:
A: 通过就绪和运行的进程数来反映
B: 通过TOP命令查看
C: 通过uptime查看
D: Load:2.5,1.3,1.1表示系统的负载压力在逐渐变小
BC,解释:ALINUX系统还需要包含处于waitting状态的进程 D说明系统负载变大,load average分别是系统1分钟,5分钟,15分钟的平均负载 参考链接:(1) http://hi.baidu.com/liheng_2009/item/20ddc822dee757192a0f1ca4(2) http://blog.csdn.net/wzy_1988/article/details/11849473
23.关于排序算法的以下说法,错误的是:
A: 快速排序的平均时间复杂度O(nlogn),最坏O(N^2)
B:堆排序平均时间复杂度O(nlogn),最坏O(nlogn)
C:冒泡排序平均时间复杂度O(n^2),最坏O(n^2)
D:归并排序的平均时间复杂度O(nlogn),最坏O(n^2)
D,解释:归并排序最坏的时间复杂度也是O(nlogn)
24.假设函数rand_k会随机返回一个【1,k】之间的随机数(k>=2),并且每个证书出现的概率相等。目前有rand_7,通过调用rand_7()和四则运算符,并适当增加逻辑判断和循环控制逻辑,下列函数可以实现的有:
A:rand_3 B:rand_21 C:rand_23 D:rand_49
ABCD,解释: 参考链接: http://blog.csdn.net/wzy_1988/article/details/11866973
填空和问答
25.某二叉树的前序遍历-+a*b-cd/ef,后续遍历abcd-*+ef/-,问其中序遍历序列为
扯淡啊,根据前序和后序没法唯一确定中序好不好,我擦
26.某缓存系统采用LRU,缓存容量为4,并且初始为空,那么在顺序访问以下数据项的时候:1,5,1,3,5,2,4,1,2
出现缓存直接命中的次数为:(),最后缓存即将淘汰的是()
3,5,参考链接: http://blog.csdn.net/wzy_1988/article/details/11714651
27.两个较长的单链表a和b,为了找出节点node满足node in a并且node in b。请设计空间使用尽量小的算法
求两个链表的公共节点题目
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
-
- typedef struct list {
- int value;
- struct list *next;
- } list;
-
- void addDataInList(list **root, int data)
- {
- list *pre, *current, *new;
- current = *root;
- pre = NULL;
-
- while (current != NULL) {
- pre = current;
- current = current->next;
- }
-
- new = (list *)malloc(sizeof(list));
- new->value = data;
- new->next = NULL;
-
- if (pre == NULL) {
- *root = new;
- } else {
- pre->next = new;
- }
- }
-
- void searchInList(list *first, int m, list *second, int n)
- {
- int i;
-
-
- if (m > n) {
- for (i = 0; i < m - n; i ++)
- first = first->next;
- } else {
- for (i = 0; i < n - m; i ++)
- second = second->next;
- }
-
-
- while (first != NULL && second != NULL && first->value != second->value) {
- first = first->next;
- second = second->next;
- }
-
- if (first == NULL && second == NULL)
- printf("My God\n");
- else
- printf("%d\n", first->value);
- }
-
-
- int main(void)
- {
- int i, n, m, data;
- list *first, *second;
-
-
- while (scanf("%d %d", &m, &n) != EOF) {
-
- for (i = 0, first = NULL; i < m; i ++) {
- scanf("%d", &data);
- addDataInList(&first, data);
- }
-
-
- for (i = 0, second = NULL; i < n; i ++) {
- scanf("%d", &data);
- addDataInList(&second, data);
- }
-
-
- searchInList(first, m, second, n);
- }
-
- return 0;
- }
28.存储数据量超出单节点数据管理能力的时候,可以采用的办法有数据库sharding的解决方案,也就是按照一定的规律把数据分散存储在多个数据管理节点N中(节点编号为0,1,2,,,,N-1)。假设存储的数据时a 请完成为数据a计算存储节点的程序
- #define N 5
- int hash(int element){
- return element*2654435761;
- }
- int shardingIndex(int a){
- int p = hash(a);
- _________________________;
- return p;
- }
p = p % N;解释:感觉没啥好解释的,基本的散列函数
29.宿舍内5个同学一起玩对战游戏,每场比赛有一些人作为红方,一些人作为蓝方,请问至少需要多少场比赛,才能使得任意两个人之间有一场红方对蓝方和蓝方对红方的比赛
4,被n多人指点之后的结果
后记
一面创新工厂感觉还不错,期待后续继续给力吧,话说一面结束没消息是闹哪样,先去吃饭,回来继续更新,昨天太累,今天竟然睡到下午才起床
前言
最近两个星期一直都在断断续续的学习二叉树的数据结构,昨晚突然有点融汇贯通的感觉,这里记录一下吧
题目要求
给定前序序列,abc##de#g##f###,构建二叉树,并且用递归和非递归两种方法去做前序,中序和后序遍历
二叉树的数据结构
- #define STACKSIZE 10005
-
-
-
-
- typedef struct btree {
- struct btree *lchild;
- struct btree *rchild;
- char item;
- } btree;
-
- typedef btree *bt;
-
-
-
-
- typedef struct stack {
- btree *db[STACKSIZE];
- int top;
- } stack;
递归构建二叉树
构建二叉树有固定的几种考察类型:
- 根据完整的先序序列构建二叉树
- 根据前序和中序序列构建二叉树
根据先序序列构建二叉树(c语言实现)
- char str[101] = "abc##de#g##f###";
- int count = 0;
-
-
-
-
- void createBtree(btree **t)
- {
- if (str[count ++] == '#') {
- *t = NULL;
- } else {
- *t = (btree *)malloc(sizeof(btree));
- (*t)->item = str[count - 1];
- createBtree(&(*t)->lchild);
- createBtree(&(*t)->rchild);
- }
- }
热潮
递归的前序、中序、后序算法(c语言实现)
-
-
-
- void recPreorder(btree *t)
- {
- if (t) {
- printf("%c", t->item);
- recPreorder(t->lchild);
- recPreorder(t->rchild);
- }
- }
-
-
-
-
- void recInorder(btree *t)
- {
- if (t) {
- recInorder(t->lchild);
- printf("%c", t->item);
- recInorder(t->rchild);
- }
- }
-
-
-
-
- void recPostorder(btree *t)
- {
- if (t) {
- recPostorder(t->lchild);
- recPostorder(t->rchild);
- printf("%c", t->item);
- }
- }
非递归前序、中序遍历算法(c语言实现)
-
-
-
- void preorderTraverse(btree *t)
- {
- btree *p = t;
-
-
- stack *s = (stack *)malloc(sizeof(stack));
- s->top = 0;
-
- while (p || s->top > 0) {
- if (p) {
- printf("%c", p->item);
- s->db[s->top ++] = p;
- p = p->lchild;
- } else {
- p = s->db[-- s->top];
- p = p->rchild;
- }
- }
- }
-
-
-
-
- void inorderTraverse(btree *t)
- {
- btree *p = t;
-
-
- stack *s = (stack *)malloc(sizeof(stack));
- s->top = 0;
-
- while (p || s->top > 0) {
- if (p) {
- s->db[s->top ++] = p;
- p = p->lchild;
- } else {
- p = s->db[-- s->top];
- printf("%c", p->item);
- p = p->rchild;
- }
- }
- }
非递归后序遍历算法(c语言实现)
算法思想:
- 首先,也是找到最左边的叶子结点并把路上遇到的节点依次入栈
- 然后,弹出栈顶元素(该元素为最左边的叶子),判断(1)它是否有右节点(2)如果有右节点,是否被访问过。如果满足(1)有右节点并且(2)右节点没有访问过,说明这是后序遍历的相对根节点,因此需要将这个节点再次入栈,并且它的右节点入栈,然后重新执行第一步。否则,就访问该节点,并且设置pre为此节点,同时把将遍历节点附空值,访问进入无限循环
算法代码:
-
-
-
- void postTraverse(btree *t)
- {
- btree *p, *pre;
- p = t;
- pre = NULL;
-
-
- stack *s = (stack *)malloc(sizeof(stack));
- s->top = 0;
-
- while (p || s->top > 0) {
- if (p) {
- s->db[s->top ++] = p;
- p = p->lchild;
- } else {
- p = s->db[-- s->top];
- if (p->rchild != NULL && p->rchild != pre) {
- s->db[s->top ++] = p;
- p = p->rchild;
- } else {
- printf("%c", p->item);
- pre = p;
- p = NULL;
- }
- }
- }
- }
注意:
严蔚敏的<<数据结构>>上有一段话很经典,摘录如下:”从二叉树遍历的定义可知,三种遍历算法之不同处仅在于访问根节点和遍历左、右子树的先后关系。如果在算法中暂且抹去和递归无关的visit语句,则三个遍历算法完全相同。因此,从递归执行过程的角度来看,前序、中序、后序遍历也完全相同。“ 这段话给我们的提示就是,前序、中序、后序遍历的算法相同,只是printf()语句位置而已。
根据前序序列、中序序列构建二叉树
函数定义
- bt rebuildTree(char *pre, char *in, int len);
参数:
* pre:前序遍历结果的字符串数组
* in:中序遍历结果的字符串数组
len : 树的长度
例如:
前序遍历结果: a b c d e f g h
中序遍历结果: c b e d f a g h
算法思想
- 递归思想,递归的终止条件是树的长度len == 0
- 在中序遍历的数组中找到前序数组的第一个字符,记录在中序数组中的位置index.如果找不到,说明前序遍历数组和中序遍历数组有问题,提示错误信息,退出程序即可;找到index后,新建一个二叉树节点t,t->item = *pre,然后递归的求t的左孩子和有孩子
- 递归的左孩子:void rebuildTree(pre + 1, in, index)
- 递归的右孩子:void rebuildTree(pre + (index + 1), in + (index + 1), len - (index + 1))
实现代码(c语言版)
-
-
-
- bt rebuildTree(char *pre, char *in, int len)
- {
- bt t;
- if(len <= 0)
- {
-
- t = NULL;
- }else
- {
-
- int index = 0;
-
- while(index < len && *(pre) != *(in + index))
- {
- index ++;
- }
-
- if(index >= len)
- {
- printf("前序遍历或者中序遍历数组有问题!\n");
- exit(-1);
- }
-
- t = (struct bintree *)malloc(sizeof(struct bintree));
- t->item = *pre;
- t->lchild = rebuildTree(pre + 1, in, index);
- t->rchild = rebuildTree(pre + (index + 1), in + (index + 1), len - (index + 1));
- }
- return t;
- }
根据中序序列、后序序列构建二叉树
函数定义
-
-
-
- btree* rebuildTree(char *order, char *post, int len);
算法思想
中序序列:C、B、E、D、F、A、H、G、J、I
后序序列:C、E、F、D、B、H、J、I、G、A
递归思路:
- 根据后序遍历的特点,知道后序遍历最后一个节点为根节点,即为A
- 观察中序遍历,A左侧CBEDF为A左子树节点,A后侧HGJI为A右子树节点
- 然后递归的构建A的左子树和后子树
实现代码(c代码)
-
-
-
-
-
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
-
- int n;
-
- typedef struct btree {
- struct btree *lchild;
- struct btree *rchild;
- char data;
- } btree;
-
-
-
-
- btree* rebuildTree(char *order, char *post, int len)
- {
- btree *t;
-
- if (len <= 0) {
- return NULL;
- } else {
- int index = 0;
-
- while (index < len && *(post + len - 1) != *(order + index)) {
- index ++;
- }
-
- t = (btree *)malloc(sizeof(btree));
- t->data = *(order + index);
- t->lchild = rebuildTree(order, post, index);
- t->rchild = rebuildTree(order + index + 1, post + index, len - (index + 1));
- }
-
- return t;
- }
-
-
-
-
- void preTraverse(btree *t)
- {
- if (t) {
- printf("%c ", t->data);
- preTraverse(t->lchild);
- preTraverse(t->rchild);
- }
- }
-
- int main(void)
- {
- int i;
- char *post, *order;
- btree *t;
-
- while (scanf("%d", &n) != EOF) {
- post = (char *)malloc(n);
- order = (char *)malloc(n);
-
- getchar();
- for (i = 0; i < n; i ++)
- scanf("%c", order + i);
-
- getchar();
- for (i = 0; i < n; i ++)
- scanf("%c", post + i);
-
- t = rebuildTree(order, post, n);
-
- preTraverse(t);
- printf("\n");
-
- free(post);
- free(order);
-
- }
-
- return 0;
- }
递归清理二叉树
复习了c语言的内存分配,参考链接: http://blog.csdn.net/zinss26914/article/details/8687859, 要点就是malloc分配的内存在堆上,使用完后应该由程序员手动释放,这里写一下递归清理二叉树的代码
-
-
-
- void cleanBtree(btree *t)
- {
- if (t) {
- cleanBtree(t->lchild);
- cleanBtree(t->rchild);
- free(t);
- }
- }
后记
2012年今天是最后一天了,哈哈,终于把二叉树该掌握的部分都掌握了,还是不错的,期待新的一年2013年有更多的收获,2013年可能又是我人生发生抉择和变化的一年,我依然会坚持自己的价值观,踏踏实实的走下去,现在我学会最多的就是坚持,坚忍,光说不做是没有的,写程序如此,做人亦如此!
今天是2013年7月23日,重写了部分二叉树操作的代码,对指针的掌握和对数据结构的掌握更加熟练,希望校招一切顺利,自己加油!
=========================二叉树内容==============================
前言
最近两个星期一直都在断断续续的学习二叉树的数据结构,昨晚突然有点融汇贯通的感觉,这里记录一下吧
题目要求
给定前序序列,abc##de#g##f###,构建二叉树,并且用递归和非递归两种方法去做前序,中序和后序遍历
二叉树的数据结构
- #define STACKSIZE 10005
-
-
-
-
- typedef struct btree {
- struct btree *lchild;
- struct btree *rchild;
- char item;
- } btree;
-
- typedef btree *bt;
-
-
-
-
- typedef struct stack {
- btree *db[STACKSIZE];
- int top;
- } stack;
递归构建二叉树
构建二叉树有固定的几种考察类型:
- 根据完整的先序序列构建二叉树
- 根据前序和中序序列构建二叉树
根据先序序列构建二叉树(c语言实现)
- char str[101] = "abc##de#g##f###";
- int count = 0;
-
-
-
-
- void createBtree(btree **t)
- {
- if (str[count ++] == '#') {
- *t = NULL;
- } else {
- *t = (btree *)malloc(sizeof(btree));
- (*t)->item = str[count - 1];
- createBtree(&(*t)->lchild);
- createBtree(&(*t)->rchild);
- }
- }
热潮
递归的前序、中序、后序算法(c语言实现)
-
-
-
- void recPreorder(btree *t)
- {
- if (t) {
- printf("%c", t->item);
- recPreorder(t->lchild);
- recPreorder(t->rchild);
- }
- }
-
-
-
-
- void recInorder(btree *t)
- {
- if (t) {
- recInorder(t->lchild);
- printf("%c", t->item);
- recInorder(t->rchild);
- }
- }
-
-
-
-
- void recPostorder(btree *t)
- {
- if (t) {
- recPostorder(t->lchild);
- recPostorder(t->rchild);
- printf("%c", t->item);
- }
- }
非递归前序、中序遍历算法(c语言实现)
-
-
-
- void preorderTraverse(btree *t)
- {
- btree *p = t;
-
-
- stack *s = (stack *)malloc(sizeof(stack));
- s->top = 0;
-
- while (p || s->top > 0) {
- if (p) {
- printf("%c", p->item);
- s->db[s->top ++] = p;
- p = p->lchild;
- } else {
- p = s->db[-- s->top];
- p = p->rchild;
- }
- }
- }
-
-
-
-
- void inorderTraverse(btree *t)
- {
- btree *p = t;
-
-
- stack *s = (stack *)malloc(sizeof(stack));
- s->top = 0;
-
- while (p || s->top > 0) {
- if (p) {
- s->db[s->top ++] = p;
- p = p->lchild;
- } else {
- p = s->db[-- s->top];
- printf("%c", p->item);
- p = p->rchild;
- }
- }
- }
非递归后序遍历算法(c语言实现)
算法思想:
- 首先,也是找到最左边的叶子结点并把路上遇到的节点依次入栈
- 然后,弹出栈顶元素(该元素为最左边的叶子),判断(1)它是否有右节点(2)如果有右节点,是否被访问过。如果满足(1)有右节点并且(2)右节点没有访问过,说明这是后序遍历的相对根节点,因此需要将这个节点再次入栈,并且它的右节点入栈,然后重新执行第一步。否则,就访问该节点,并且设置pre为此节点,同时把将遍历节点附空值,访问进入无限循环
算法代码:
-
-
-
- void postTraverse(btree *t)
- {
- btree *p, *pre;
- p = t;
- pre = NULL;
-
-
- stack *s = (stack *)malloc(sizeof(stack));
- s->top = 0;
-
- while (p || s->top > 0) {
- if (p) {
- s->db[s->top ++] = p;
- p = p->lchild;
- } else {
- p = s->db[-- s->top];
- if (p->rchild != NULL && p->rchild != pre) {
- s->db[s->top ++] = p;
- p = p->rchild;
- } else {
- printf("%c", p->item);
- pre = p;
- p = NULL;
- }
- }
- }
- }
注意:
严蔚敏的<<数据结构>>上有一段话很经典,摘录如下:”从二叉树遍历的定义可知,三种遍历算法之不同处仅在于访问根节点和遍历左、右子树的先后关系。如果在算法中暂且抹去和递归无关的visit语句,则三个遍历算法完全相同。因此,从递归执行过程的角度来看,前序、中序、后序遍历也完全相同。“ 这段话给我们的提示就是,前序、中序、后序遍历的算法相同,只是printf()语句位置而已。
根据前序序列、中序序列构建二叉树
函数定义
- bt rebuildTree(char *pre, char *in, int len);
参数:
* pre:前序遍历结果的字符串数组
* in:中序遍历结果的字符串数组
len : 树的长度
例如:
前序遍历结果: a b c d e f g h
中序遍历结果: c b e d f a g h
算法思想
- 递归思想,递归的终止条件是树的长度len == 0
- 在中序遍历的数组中找到前序数组的第一个字符,记录在中序数组中的位置index.如果找不到,说明前序遍历数组和中序遍历数组有问题,提示错误信息,退出程序即可;找到index后,新建一个二叉树节点t,t->item = *pre,然后递归的求t的左孩子和有孩子
- 递归的左孩子:void rebuildTree(pre + 1, in, index)
- 递归的右孩子:void rebuildTree(pre + (index + 1), in + (index + 1), len - (index + 1))
实现代码(c语言版)
-
-
-
- bt rebuildTree(char *pre, char *in, int len)
- {
- bt t;
- if(len <= 0)
- {
-
- t = NULL;
- }else
- {
-
- int index = 0;
-
- while(index < len && *(pre) != *(in + index))
- {
- index ++;
- }
-
- if(index >= len)
- {
- printf("前序遍历或者中序遍历数组有问题!\n");
- exit(-1);
- }
-
- t = (struct bintree *)malloc(sizeof(struct bintree));
- t->item = *pre;
- t->lchild = rebuildTree(pre + 1, in, index);
- t->rchild = rebuildTree(pre + (index + 1), in + (index + 1), len - (index + 1));
- }
- return t;
- }
根据中序序列、后序序列构建二叉树
函数定义
-
-
-
- btree* rebuildTree(char *order, char *post, int len);
算法思想
中序序列:C、B、E、D、F、A、H、G、J、I
后序序列:C、E、F、D、B、H、J、I、G、A
递归思路:
- 根据后序遍历的特点,知道后序遍历最后一个节点为根节点,即为A
- 观察中序遍历,A左侧CBEDF为A左子树节点,A后侧HGJI为A右子树节点
- 然后递归的构建A的左子树和后子树
实现代码(c代码)
-
-
-
-
-
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
-
- int n;
-
- typedef struct btree {
- struct btree *lchild;
- struct btree *rchild;
- char data;
- } btree;
-
-
-
-
- btree* rebuildTree(char *order, char *post, int len)
- {
- btree *t;
-
- if (len <= 0) {
- return NULL;
- } else {
- int index = 0;
-
- while (index < len && *(post + len - 1) != *(order + index)) {
- index ++;
- }
-
- t = (btree *)malloc(sizeof(btree));
- t->data = *(order + index);
- t->lchild = rebuildTree(order, post, index);
- t->rchild = rebuildTree(order + index + 1, post + index, len - (index + 1));
- }
-
- return t;
- }
-
-
-
-
- void preTraverse(btree *t)
- {
- if (t) {
- printf("%c ", t->data);
- preTraverse(t->lchild);
- preTraverse(t->rchild);
- }
- }
-
- int main(void)
- {
- int i;
- char *post, *order;
- btree *t;
-
- while (scanf("%d", &n) != EOF) {
- post = (char *)malloc(n);
- order = (char *)malloc(n);
-
- getchar();
- for (i = 0; i < n; i ++)
- scanf("%c", order + i);
-
- getchar();
- for (i = 0; i < n; i ++)
- scanf("%c", post + i);
-
- t = rebuildTree(order, post, n);
-
- preTraverse(t);
- printf("\n");
-
- free(post);
- free(order);
-
- }
-
- return 0;
- }
递归清理二叉树
复习了c语言的内存分配,参考链接: http://blog.csdn.net/zinss26914/article/details/8687859, 要点就是malloc分配的内存在堆上,使用完后应该由程序员手动释放,这里写一下递归清理二叉树的代码
-
-
-
- void cleanBtree(btree *t)
- {
- if (t) {
- cleanBtree(t->lchild);
- cleanBtree(t->rchild);
- free(t);
- }
- }
后记
2012年今天是最后一天了,哈哈,终于把二叉树该掌握的部分都掌握了,还是不错的,期待新的一年2013年有更多的收获,2013年可能又是我人生发生抉择和变化的一年,我依然会坚持自己的价值观,踏踏实实的走下去,现在我学会最多的就是坚持,坚忍,光说不做是没有的,写程序如此,做人亦如此!
今天是2013年7月23日,重写了部分二叉树操作的代码,对指针的掌握和对数据结构的掌握更加熟练,希望校招一切顺利,自己加油!