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

出题:给定一个乱序链表,节点值为ASCII字符,但是其中有重复项,要求去除重复项并保证不改变剩余项的原有顺序;

分析:创建一个256(2^8)大小的bool数组,初始化为false,顺序读取链表,将字母对应位置为false的重新标记为true并保留节点,将字母对
应位置为true的保持并删除节点;时间复杂度为O(N),空间复杂度为常量。注意删除节点和不删除节点的情况下,pre和cur的移动操作不相同;

解题:


 1 struct Node {
2 char value;
3 Node* next;
4 };
5 void DeleteDup(Node *head) {
6 if(head==NULL) {
7 printf("\nthe list is NULL");
8 return;
9 }
10
11 Node *pre=NULL, *cur=head;
12 /**
13 * 设置一个256的bool数组记录char是否
14 * 已经出现
15 * */
16 bool haveChar[256];
17 for(int i=0;i<256;i++)
18 haveChar[i]=false;
19 /**
20 * 如果haveChar对应为true,说明当前节点
21 * 的值已经出现过,则进行删除
22 * 如果haveChar对应为false,说明当前节点
23 * 的值第一次出现,则将其设置为true
24 * */
25 while(cur!=NULL) {
26 /**
27 * 注意删除节点的情况和不删除节点的情况
28 * pre和cur的需要不同的处理
29 * */
30 if(!haveChar[(cur->value)-‘0‘]) {
31 haveChar[(cur->value)-‘0‘]=true;
32 pre=cur;
33 cur=cur->next;
34 }
35 else {
36 pre->next=cur->next;
37 delete cur;
38 cur=pre->next;
39 }
40 }
41 }
42 int main() {
43 Node *a1=new Node();a1->value=‘a‘;
44 Node *a2=new Node();a2->value=‘d‘;
45 Node *a3=new Node();a3->value=‘s‘;
46 Node *a4=new Node();a4->value=‘d‘;
47
48 a1->next=a2;a2->next=a3;
49 a3->next=a4;a4->next=NULL;
50
51 DeleteDup(a1);
52
53 Node *temp=a1;
54 while(temp!=NULL) {
55 printf("\n%c",temp->value);
56 temp=temp->next;
57 }
58 return 0;
59 }

出题:给定两个已排序的数组,要求找出共同的元素;

分析:

  • 如果两个数组大小接近,则分别使用指针first和second遍历两个序列,由于数组已经排序,所以遍历过的元素不会再次访问,所以时间复杂度为O(M+N);

  • 如果两个数组大小差距较大,则在针对小数组中的每个元素在大数组中使用二分查找(每处理一个元素之后,大数组的范围都可以调整到上一个元素的后面),时间复杂度为O(NlogM),N足够小(M>N^2);

解题:


 1 /**
2 * 时间复杂度O(M+N)
3 * */
4 void FindCommonInt1(int *first, int fl, int *second, int sl) {
5 int ft=0, st=0;
6 while (ft<fl && st<sl) {
7 if(first[ft]>second[st]) {
8 st++;
9 } else if(first[ft]<second[st]) {
10 ft++;
11 } else {
12 printf("\n%d",first[ft]);
13 ft++;st++;
14 }
15 }
16 }
17 /**
18 * 时间复杂度小于O(NlogM),其中M不断变小
19 * */
20 void FindCommonInt2(int *first, int fl, int *second, int sl) {
21 int start=0, end=fl-1;
22 int s,e,m;
23 for(int i=0;i<sl;i++) {
24 s=start;e=end;
25 while(s<=e) {
26 m=(s+e)/2;
27 if(first[m]>second[i]) {
28 e=m-1;
29 } else if(first[m]<second[i]) {
30 s=m+1;
31 } else {
32 printf("\n%d",first[m]);
33 start=m+1;
34 break;
35 }
36 }
37 }
38 }
39 int main() {
40 int first[]={1,2,3,4,5,6,10,11,12};
41 int second[]={1,4,9,10};
42 FindCommonInt2(first, 9, second, 4);
43 return 0;
44 }

时间: 2024-10-21 13:27:20

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

笔试算法题(07):还原后序遍历数组 &amp; 半翻转英文句段

出题:输入一个整数数组,判断该数组是否符合一个二元查找树的后序遍历(给定整数数组,判定其是否满足某二元查找树的后序遍历): 分析:利用后序遍历对应到二元查找树的性质(序列最后一个元素必定是根节点,从左向右第一个比根节点大的元素开始直到根节点之前的所有元素必定在右子树,之前的所有元素必定在左子树): 解题: 1 bool PostOrderCheck(int *array, int i, int j) { 2 /** 3 * 如快速排序一样,解决小子文件 4 * */ 5 if(j-i+1 ==

笔试算法题(41):线索二叉树(Threaded Binary Tree)

出题:线索二叉树(Threaded Binary Tree) 分析: 为除第一个节点外的每个节点添加一个指向其前驱节点的指针,为除最后一个节点外的每个节点添加一个指向其后续节点的指针,通过这些额外的指针可以某种遍历方式对二叉树进行遍历,而加了这些额外指针的二叉树就是线索二叉树: 对于含有N个节点的二叉树而言,一共有2N个指针,但除了根节点的其他节点都有来自其父节点的指针,所以耗用了N-1个指针,则最终剩下2N-(N- 1)=N+1个空指针:线索二叉树就是利用这些空指针存储具有某种遍历顺序的前驱和

笔试算法题(43):布隆过滤器(Bloom Filter)

议题:布隆过滤器(Bloom Filter) 分析: BF由一个很长的二进制向量和一系列随机映射的函数组成,通过多个Hash函数将一个元素映射到一个Bit Array中的多个点,查询的时候仅当所有的映射点都为1才能判断元素存在于集合内:BF用于检索一个元素是否在一个集合中,记忆集合求交集:优点是空间 和时间效率都超过一般查询算法,缺点是有一定的误判概率和删除困难: 如下图,使用三个哈希函数对每个元素进行映射,这样每个元素对应HashTable中的三个位置,如果查找w是否在HashTable中则仍

笔试算法题(11):Josephus环 &amp; Fibonacci序列

出题:Josephus Cycle,约瑟夫环问题.k个数字连成一个环,第一个数字为1.首先从1开始计数删除第m个数字:然后从上次被删除的数字的下一个数字开始计数,删除第m个数字:重复进行第二步直到只剩下一个数字:输出最后剩下的一个数字: 分析: 解法1:考虑到问题的特殊性,可以使用哑元素表示删除的元素从而避免由于删除元素带来的额外操作,所以链表实现的话不用考虑删除操作,数组实现的话不用考虑内存移动操作.当然完全可以不适用哑元素,对于链表而言可以节省查找时间,数组的话需要增加数组元素的平移开销:

笔试算法题(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

笔试算法题(24):找出出现次数超过一半的元素 &amp; 二叉树最近公共父节点

出题:数组中有一个数字出现的次数超过了数组长度的一半,请找出这个数字: 分析: 解法1:首先对数组进行排序,时间复杂度为O(NlogN),由于有一个数字出现次数超过了数组的一半,所以如果二分数组的话,划分元素肯定就是这个数字: 解法2:首先创建1/2数组大小的Hash Table(哈希表可以替代排序时间,由于一个数字出现超过了数组的一半,所以不同元素个数肯定不大于数组的一半),空间复杂度O(N),顺序扫描映射数 组元素到Hash Table中并计数,最后顺序扫描Hash Table,计数超过数组

笔试算法题(47):简介 - B树 &amp; B+树 &amp; B*树

B树(B-Tree) 1970年由R. Bayer和E. Mccreight提出的一种适用于外查找的树,一种由BST推广到多叉查找的平衡查找树,由于磁盘的操作速度远小于存储器的读写速度,所以要求在尽量少 的操作次数内完成CPU分配的任务,B树就按照此原则设计,B树与红黑树的主要区别在于B树节点可以有超过2个子女,从而大大降低树的高度以减少查询时 间: 一棵M阶B树(Balanced Tree of Order M)是一棵平衡的M路搜索树,满足性质: 根节点至少有两个子女: 除根节点和叶子节点外的

笔试算法题(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]插入到

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

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