作者序
题目均来自于往年试卷,给出的代码都能够编译通过。
例题一:哈希表的使用
题面: 假设线性表采用顺序存储结构,试实现函数int DelRepeat(),用以删除所有重复元素,并返回删除元素的个数。要求算法的时间复杂度为O ( n ) O(n) O ( n ) 。线性表结构定义如下:
1 2 3 4 5 6 7 8 9 10 11 class seqList { private : int * data; int currentLength; int noData; public : int DelRepeat () { } };
代码实现如下,注意:测试代码未给出,请读者自行构造。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 class seqList { private : int * data; int currentLength; int noData; public : seqList (int n){ currentLength=n; data=new int [n]; cout<<"input data:" <<endl; for (int i=0 ;i<n;++i){ cin>>data[i]; } } void display () { cout<<"the data are:" <<endl; for (int i=0 ;i<currentLength;++i){ cout<<data[i]<<"\t" ; } cout<<endl; } int delRepeat (int n) { noData=n; bool * status=new bool [currentLength]; for (int i=0 ;i<currentLength;++i){ status[i]=false ; } int * hashtable=new int [2 *currentLength]; for (int i=0 ;i<2 *currentLength;++i){ hashtable[i]=noData; } for (int i=0 ;i<currentLength;++i){ int pos=data[i]%(2 *currentLength); while (hashtable[pos]!=noData&&hashtable[pos]!=data[i]) (++pos)%(2 *currentLength); if (hashtable[pos]==data[i]){ status[i]=true ; } else { hashtable[pos]=data[i]; } } int i=0 ,j=0 ; int newLength=0 ; int oldLength=currentLength; int * tmp=new int [currentLength]; for (int i=0 ;i<currentLength;++i){ if (!status[i]){ tmp[j]=data[i]; ++j; newLength++; } } delete [] data; data=tmp; delete [] hashtable; currentLength=newLength; return (oldLength-currentLength); } };
总结: 对于需要反复回溯已遍历元素中某个元素出现与否的,哈希表是一个很好的解决方式,可以减少O ( n ) O(n) O ( n ) 的算法复杂度。注意哈希表的重点:哈希函数 和冲突解决 。
例题二:二叉树
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。请完善下列算法,实现将上述两个二叉树合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。注意:合并后的二叉树中结点允许直接使用这两个二叉树上的结点。给出二叉树的定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class binaryTree { private : struct node { int value; node* left; node* right; node (int val){ value=val; left=NULL ; right=NULL ; } }; node* root; public : node* mergeTrees (node* t1, node* t2) { } };
想法是:合并二树可以看成三个过程:合并根节点 、递归调用合并左右子树 。因此,合并二树的操作并不难,只需要注意递归跳出的条件:其中一棵树为空树即可。为了完成代码的检查,我们还需要复习一遍二叉树的构建和层次遍历的知识点。以下代码包含了测试代码,可以直接运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 #include <iostream> #include <queue> using namespace std;class binaryTree { private : struct node { int value; node* left; node* right; node (int val){ value=val; left=NULL ; right=NULL ; } }; node* root; public : binaryTree (){ int val; int lval; int rval; cout<<"input the root:" ; cin>>val; cout<<endl; root=new node (val); queue<node*> que1; que1. push (root); while (!que1. empty ()){ node* tmp=que1.f ront(); que1. pop (); cout<<"input the two children of the given node " <<tmp->value<<":" ; cin>>lval>>rval; cout<<endl; if (lval){ tmp->left=new node (lval); que1. push (tmp->left); } if (rval){ tmp->right=new node (rval); que1. push (tmp->right); } } cout<<"binarytree has been built!" <<endl; } binaryTree (node* r){ root=r; } void display () { queue<node> que1; que1. push (*root); while (!que1. empty ()){ node tmp=que1.f ront(); que1. pop (); cout<<tmp.value<<" " ; if (tmp.left){ que1. push (*(tmp.left)); } if (tmp.right){ que1. push (*(tmp.right)); } } cout<<"display completed!" <<endl; } node* findRoot () { return root; } node* mergeTrees (node* t1, node* t2) { if (!t1) return t2; if (!t2) return t1; node* t3=new node (t1->value+t2->value); t3->left=mergeTrees (t1->left,t2->left); t3->right=mergeTrees (t1->right,t2->right); return t3; } }; int main () { binaryTree tree1; tree1. display (); binaryTree tree2; tree2. display (); binaryTree tree3 (tree1. mergeTrees(tree1.f indRoot(),tree2.f indRoot())) ; tree3. display (); return 0 ; }
例题三:拓扑排序
题面: 学生需要修读完所有的课程才能毕业,这些课程之间有先导关系(比如要修读数据结构,必须先修读程序设计思想方法)。假设任意一门课程可以在任何一个学期给满足条件的学生选修,且学生每个学期可以选修的课程数不限。先给出一些课程与课程之间的关系,求能够修完所有课程的最少学期数。
输入格式: 第1行:n m //正整数n ,代表课程的数量。非负整数m代表要给出几个先导关系。第2行到第1+m行: a b //每行两个整数:代表要选修编号为a的课程,必须先修读编号为b的课程。
输出格式: 一个整数,即修完所有课程的最少学期数。
思路: 本题可以参考拓扑排序的方式,即:选取入度为零的点,将其移除,然后更新有向图;再移除入度为零的点,如此循环。实现的代码如下,主函数也已经给出,且能够编译通过。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 #include <iostream> #include <queue> using namespace std;class adjListGraph { private : struct edgeNode { int end; edgeNode* next; edgeNode (int e,edgeNode* n=NULL ){ end=e; next=n; } }; struct verNode { int ver; edgeNode* head; verNode (int val=0 ){ ver=val; head=NULL ; } }; verNode* verList; int Vers; int * inDegree; public : adjListGraph (int vSize){ Vers=vSize; verList=new verNode[Vers+1 ]; inDegree=new int [Vers+1 ]; for (int i=1 ;i<=Vers;++i){ verList[i].ver=i; inDegree[i]=0 ; } } void insert (int end,int in) { verList[in].head=new edgeNode (end,verList[in].head); inDegree[end]++; } int topSort () { int counter=0 ; int remain=Vers; int current; queue<verNode> que1; while (remain!=0 ){ for (int i=1 ;i<=Vers;++i){ if (inDegree[i]==0 ){ que1. push (verList[i]); } } while (!que1. empty ()){ current=que1.f ront().ver; que1. pop (); edgeNode*p=verList[current].head; while (p){ inDegree[p->end]--; p=p->next; } remain--; inDegree[current]=-1 ; } counter++; } return counter; } }; int main () { int n,m; cin>>n>>m; adjListGraph graph (n) ; for (int i=1 ;i<=m;++i){ int a,b; cin>>a>>b; graph.insert (a,b); } cout<<graph.topSort ()<<endl; return 0 ; }
上述程序中指定队列的原因是,为了区分出每一学期修了多少门课。每一个学期进行一次统一的入队操作,而后出队直至队空为止。