笔试算法题(25):复制拥有多个指针的链表 & 判断二元树B是否为A的子树

出题:定义一个复杂链表:在单向链表的基础上,每个节点附加一个指向链表中其他任意节点的指针sibling,实现CNode*
Clone(Cnode *head)函数复制这个复杂链表;

分析:

  • 解法1:将head复制到CHead中,第一次遍历创建CHead中对应head的各个节点(next),第二次遍历创建CHead中对应head各个节
    点的sibling链接,由于需要在CHead中找到对应head中的sibling节点,所以需要遍历CHead链表,但是可以用空间换时间的方法:使 用Hash
    Table存储CHead和head对应的节点对,这样head中定位了sibling之后,就可以知道CHead中对应sibling节点,时间复杂度
    为O(N),空间复杂度为O(N);

  • 解法2:注意给出的函数头中参数并没有使用const,而一般情况下的copy函数的参数都是const,所以可以推断可能需要改变原始数据的结构。使用
    奇偶链表实现,将新创建的CHead中的各个节点对应安插到head中各个节点之后,这样CHead中与head中对等的节点就在head中节点的后面,
    所以可以很容易确定sibling节点,最后仅读取偶数节点就是CHead,时间复杂度为O(N),空间复杂度为O(1)。海涛老师再次威武!此方法参考
    海涛老师的博客:
    http://zhedahht.blog.163.com/blog/static/254111742010819104710337/

解题:


  1 struct CNode {
2 int value;
3 CNode *next;
4 CNode *sibling;
5 };
6 CNode* Clone(CNode *head) {
7 if(head==NULL) return NULL;
8
9 CNode *CHead=new CNode();
10 CHead->value=head->value;
11 CNode *cur, *pre=CHead, *temp=head->next, *CTemp;
12 /**
13 * 以next指针为线索创建链表节点
14 * */
15 while(temp!=NULL) {
16 cur=new CNode();
17 cur->value=temp->value;
18 pre->next=cur;
19 pre=cur;
20
21 temp=temp->next;
22 }
23 /**
24 * 以next指针为线索创建sibling链接
25 * */
26 temp=head;CTemp=CHead;
27 while(temp!=NULL) {
28 /**
29 * 将下面这句代码换成HashTable存储的head和CHead中
30 * 对等节点的存储对
31 * */
32 //CTemp->sibling=temp->sibling;
33 temp=temp->next;
34 CTemp=CTemp->next;
35 }
36 return CHead;
37 }
38 /**
39 * 在head中每个节点的后面创建一个新节点,
40 * 并复制之前节点的value,链接之前节点的
41 * next节点
42 * */
43 void CreateNewNode(CNode *head) {
44 CNode *index=head, *nNode, *next;
45 while(index!=NULL) {
46 nNode=new CNode();
47 nNode->value=index->value;
48 nNode->sibling=NULL;
49
50 next=index->next;
51 index->next=nNode;
52 nNode->next=next;
53 index=next;
54 }
55 }
56 /**
57 * 顺序遍历head中奇数索引的节点,如果其sibling非NULL,则
58 * 其sibling的next节点就是CHead中的对应节点的sibling
59 * */
60 void CopySibling(CNode *head) {
61 CNode *index=head;
62 while(index!=NULL) {
63 if(index->sibling!=NULL) {
64 index->next->sibling=
65 index->sibling->next;
66 }
67 index=index->next->next;
68 }
69 }
70 /**
71 * 将head拆分成奇数索引节点和偶数索引节点,后者就是新clone
72 * 的复杂链表
73 * */
74 CNode* Separate(CNode *head) {
75 CNode *index=head, *clone;
76 if(index!=NULL) {
77 clone=index->next;
78 index->next=index->next->next;
79 index=index->next;
80 } else
81 return NULL;
82 CNode *temp;
83 while(index!=NULL) {
84 temp=index->next;
85 clone->next=temp;
86 index->next=temp->next;
87
88 clone=temp;
89 index=temp->next;
90 }
91
92 return clone;
93 }
94 void Deletion(CNode *head) {
95 CNode *cur=head, *next;
96 if(cur!=NULL) {
97 next=cur->next;
98 delete cur;
99 cur=next;
100 }
101 }

出题:输入两棵二元树的根节点A和B,判断B是否是A的一个子树结构;

分析:

  • 首先在A中找到与B根节点的值相同的节点AB1(注意处理节点值相同的情况,首先判断当前找到的AB1是否匹配,如果不匹配则继续寻找下一个AB1),然
    后同时递归AB1和B,如果B中某节点的值与AB1某节点的值不等,则返回false;

  • 如果B中某节点有子节点(左右),而AB1中没有,则返回 false;当B递归完全之后(也就是到达NULL)返回true;

解题:


 1 struct Node {
2 int value;
3 Node *left;
4 Node *right;
5 };
6 /**
7 * son只能是father的一部分,其他情况都返回false
8 * */
9 bool compare(Node *father, Node *son) {
10 if(son==NULL) return true;
11 if(father==NULL) return false;
12 if(father->value != son->value)
13 return false;
14 else
15 return compare(father->left,son->left) &&
16 compare(father->right,son->right);
17 }
18 /**
19 * 注意处理节点值重复的情况,因为只能找到第一个节点,而正确
20 * 的匹配可能发生在之后的拥有相同节点值的节点
21 * */
22 Node* findNode(Node *root, Node *target) {
23 Node *temp=NULL; bool ismatch=true;
24 if(root->value==target->value) {
25 if(compare(root, target)) {
26 printf("\nb1 is part of a1");
27 exit(0);
28 } else
29 ismatch=false;
30 }
31 if(!ismatch && root->left!=NULL)
32 temp=findNode(root->left,target);
33 if(temp==NULL && root->right!=NULL)
34 temp=findNode(root->right,target);
35 return temp;
36 }

时间: 2024-11-13 08:51:15

笔试算法题(25):复制拥有多个指针的链表 & 判断二元树B是否为A的子树的相关文章

笔试算法题(21):将stack内外颠倒 & 判断扑克牌顺子

出题:要求用递归将一个栈结构的元素内外颠倒: 分析: 本题再次说明系统栈是程序员最好的帮手,但递归度较高所以时间复杂度较大,可以使用空间换时间的方法(额外数组保存栈元素,然后逆向压入): 第一层递归(清空栈元素,并使用系统栈保存):[1,2,3,4,5],栈顶元素为1,将1弹出之后,递归处理[2,3,4,5]: 第二层递归(将栈顶元素插入到栈底,同样使用系统栈保存):当[2,3,4,5]已经逆序之后,需要将1插入到栈底,所以将1作为参数传递到递归调用中,之后递归处理2和[3,4,5]: 解题:

笔试算法题(28):删除乱序链表中的重复项 & 找出已经排好序的两个数组中的相同项

出题:给定一个乱序链表,节点值为ASCII字符,但是其中有重复项,要求去除重复项并保证不改变剩余项的原有顺序: 分析:创建一个256(2^8)大小的bool数组,初始化为false,顺序读取链表,将字母对应位置为false的重新标记为true并保留节点,将字母对 应位置为true的保持并删除节点:时间复杂度为O(N),空间复杂度为常量.注意删除节点和不删除节点的情况下,pre和cur的移动操作不相同: 解题: 1 struct Node { 2 char value; 3 Node* next;

笔试算法题(46):简介 - 二叉堆 & 二项树 & 二项堆 & 斐波那契堆

二叉堆(Binary Heap) 二叉堆是完全二叉树(或者近似完全二叉树):其满足堆的特性:父节点的值>=(<=)任何一个子节点的键值,并且每个左子树或者右子树都是一 个二叉堆(最小堆或者最大堆):一般使用数组构建二叉堆,对于array[i]而言,其左子节点为array[2*i],其右子节点为 array[2*i+1]:二叉堆支持插入,删除,查找最大(最小)键值的操作,但是合并二叉堆的复杂度较高,时间复杂度为O(N):但是二项堆或者斐波 那契堆则仅需要O(logN): 二项树(Binomial

笔试算法题(42):线段树(区间树,Interval Tree)

议题:线段树(Interval Tree) 分析: 线段树是一种二叉搜索树,将一个大区间划分成单元区间,每个单元区间对应一个叶子节点:内部节点对应部分区间,如对于一个内部节点[a, b]而言,其左子节点表示的区间为[a, (a+b)/2],其右子节点表示的区间为[1+(a+b)/2, b]: 对于区间长度为N的线段树,由于其单元节点都是[a, a]的叶子节点,所以其叶子节点数为N,并且整棵树为平衡二叉树,所以总节点数为2N-1,树的深度为log(N)+1: 插入操作:将一条线段[a, b]插入到

笔试算法题(53):四种基本排序方法的性能特征(Selection,Insertion,Bubble,Shell)

四种基本算法概述: 基本排序:选择,插入,冒泡,希尔.上述算法适用于小规模文件和特殊文件的排序,并不适合大规模随机排序的文件.前三种算法的执行时间与N2成正比,希尔算法的执行时间与N3/2(或更快)成正比: 前三种算法在平均,最坏情况下都是N2,而且都不需要额外的内存:所以尽管他们的运行时间只相差常数倍,但运行方式不同: 对于已经就序的序列而言,插入排序和冒泡排序的运行时间都是O(N),但是选择排序的时间仍旧是O(N^2): 因为Insertion和Bubble都是相邻项间的比较交换,所以不会出

笔试算法题(04):实现 string &amp; memcpy &amp; strcpy &amp; strlen

出题:请实现给定String的类定义: 分析:注意检查标准类构造注意事项: 解题: 1 #include <stdio.h> 2 #include <string.h> 3 /** 4 * 检查是否需要构造函数 5 * 检查是否需要无参构造函数 6 * 检查是否需要成员变量(函数)私有 7 * 检查是否需要在构造函数预初始化成员变量 8 * 检查是否需要析构函数 9 * 检查是否需要虚拟析构函数 10 * 检查是否需要复制构造函数(参数为const) 11 * 检查是否需要赋值重载

笔试算法题

转自:http://www.cnblogs.com/xwdreamer/archive/2011/12/13/2296910.html 1.把二元查找树转变成排序的双向链表 题目:输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表.要求不能创建任何新的结点,只调整指针的指向.   10  / \  6  14 / \ / \4  8 12 16 转换成双向链表4=6=8=10=12=14=16.  首先我们定义的二元查找树 节点的数据结构如下: struct BSTreeNode{  i

笔试算法题(09):查找指定和值的两个数 &amp; 构造BST镜像树

出题:输入一个已经升序排序的数组和一个数字:要求在数组中查找两个数,这两个数的和正好等于输入的那个数字,输出任意一对数字就可以,要求时间复杂度是O(n): 分析:对于升序排序的数组{-i-j-k-m--},只有可能是i+m=j+k(j和k可能是同一个数),所以可以从两边往中间收缩而忽视其他交叉相加的情况: 解题: 1 void FindSumFactor(int *array, int length, int sum) { 2 int left=0, right=length-1; 3 whil

笔试算法题(08):输出倒数第K个节点

出题:输入一个单向链表,要求输出链表中倒数第K个节点 分析:利用等差指针,指针A先行K步,然后指针B从链表头与A同步前进,当A到达链表尾时B指向的节点就是倒数第K个节点: 解题: 1 struct Node { 2 int v; 3 Node *next; 4 }; 5 Node* FindLastKth(Node *head, int k) { 6 if(head==NULL) { 7 printf("\nhead is NULL\n"); 8 exit(0); 9 } 10 Nod