笔试算法题(27):判断单向链表是否有环并找出环入口节点 & 判断两棵二元树是否相等

出题:判断一个单向链表是否有环,如果有环则找到环入口节点;

分析:

  • 第一个问题:使用快慢指针(fast指针一次走两步,slow指针一次走一步,并判断是否到达NULL,如果fast==slow成立,则说明链表有环);

  • 第二个问题:fast与slow相遇时,slow一定还没有走完一圈(反证法可证明);






     示意图


    A为起始点,B为环入口点,C为相遇点,则a1=|AB|表示起始点到换入口的距离,a2=|CB|表示相遇点到环入口点的距离,s1=|AB|+|BC|表示slow指针走的长度,s2表示fast指针走的长度,C=|BCB|表示环的长度
    由于fast的速度是slow的2倍,所以相遇的时候走过的长度也是2倍
    s2=2*s1=a1+N*C+(s1-a1)
    (1)
    N表示fast在环中走的圈数,化解(1)得到:
    s1=N*C
    (2)
    找到a1和a2的关系:
    a2=C-(s1-a1)
    (3)
    将(2)代入(3)得到:
    a1=a2+(N-1)*C
    (4)
    所以如果指针m从起始点A出发,指针n从相遇点C出发,n绕行(N-1)圈环之后最终跟m指针在B点相遇

解题:


 1 struct Node {
2 int v;
3 Node *next;
4 };
5 Node* IsCycle(Node *head) {
6 Node *fast=head, *slow=head;
7
8 while(true) {
9 if(fast!=NULL)
10 fast=fast->next;
11 if(fast!=NULL)
12 fast=fast->next;
13 else
14 return NULL;
15 if(slow!=NULL)
16 slow=slow->next;
17 else
18 return NULL;
19
20 if(fast==slow)
21 return fast;
22 }
23 }
24 Node* FindEntry(Node *head, Node *joint) {
25 Node *m=head, *n=joint;
26 while(true) {
27 if(m==n)
28 return m;
29 m=m->next;
30 n=n->next;
31 }
32 }
33 int main() {
34 Node* b1=new Node(); b1->v=1;
35 Node* b2=new Node(); b2->v=2;b1->next=b2;
36 Node* b3=new Node(); b3->v=3;b2->next=b3;
37 Node* b4=new Node(); b4->v=4;b3->next=b4;
38 Node* b5=new Node(); b5->v=5;b4->next=b5;
39 Node* b6=new Node(); b6->v=6;b5->next=b6;
40 Node* b7=new Node(); b7->v=7;b6->next=b7;
41 Node* b8=new Node(); b8->v=8;b7->next=b8; b8->next=b3;
42
43 Node* temp;
44 if((temp=IsCycle(b1))!=NULL) {
45 printf("\nthe joint point is: %d",temp->v);
46 printf("\nthe entry of cycle is: %d",FindEntry(b1,temp)->v);
47 }
48 else
49 printf("\nthere is no cycle.");
50 return 0;
51 }

出题:判断两棵二元树是否相等(左右子树不能交叉比较);

分析:使用递归实现,在树的K层,有2^K 个节点,所以会进行(2^K)*2次调用,所以时间复杂度为O(N);

解题:


 1 struct Node {
2 int value;
3 Node *left;
4 Node *right;
5 };
6
7 bool CompareTree(Node *first, Node *second) {
8 if(first==NULL && second==NULL)
9 return true;
10 if((first==NULL && second!=NULL) ||
11 (first!=NULL && second==NULL))
12 return false;
13 if(first->value!=second->value)
14 return false;
15 return CompareTree(first->left,second->left) &&
16 CompareTree(first->right, second->right);
17 }

时间: 2024-08-03 06:40:46

笔试算法题(27):判断单向链表是否有环并找出环入口节点 & 判断两棵二元树是否相等的相关文章

判断单向链表是否有环,环起点,环长,链表长

今天在微信上看到一篇介绍如何判断单向链表是否有环的文章,感觉很有意思,整理一下读后的思路. 一.判断单向链表是否有环 方法1:设置一个Hashset,顺序读取链表中的节点,判断Hashset中是否有该节点的唯一标识(ID).如果在Hashset中,说明有环:如果不在Hashset中,将节点的ID存入Hashset. 这种方法时间复杂度已经最优,但是因为额外申请了Hashset,所以空间复杂度不算最优. 方法2:设置2个指针,指向头节点.第1个指针每次指向下一个节点:第2个指针指向下一个节点的下一

判断单向链表是否有环,以及环入口与链表头节点的距离

第一步:检测链表是否有环. 方法还是比较多的,这里先讲一个:快慢指针. 快慢指针的方法,就是让两个指针同时指向链表.在向后遍历的时候,一个指针每次走两步,称为快指针:一个指针每次走一步,称为慢指针.如果快慢指针相遇,则说明链表有环,否则无环.(后面证明.) 代码实现如下 // 如果有环,则返回快慢指针相遇点.如果没环,则返回NULL. position IsLoop(list l) { if (l == NULL) { printf("Invalid parameter for function

判断单向链表是否有环

1.设立2个指针i,j指向头结点 2.i走1步,j走2步.如果有环,j一定能追上i: 3.如果j不为空,且i和j相等此链表即为有环. 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 typedef struct student //定义链表结构 5 { 6 int num; 7 struct student *pnext; 8 }stu,*pstu; 9 void link_tail_

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

出题:定义一个复杂链表:在单向链表的基础上,每个节点附加一个指向链表中其他任意节点的指针sibling,实现CNode* Clone(Cnode *head)函数复制这个复杂链表: 分析: 解法1:将head复制到CHead中,第一次遍历创建CHead中对应head的各个节点(next),第二次遍历创建CHead中对应head各个节 点的sibling链接,由于需要在CHead中找到对应head中的sibling节点,所以需要遍历CHead链表,但是可以用空间换时间的方法:使 用Hash Tab

11.判断单链表是否有环

判断单链表是否有环:   这里也是用到两个指针,如果一个链表有环,那么用一个指针去遍历,是永远走不到头的.   因此,我们用两个指针去遍历:first指针每次走一步,second指针每次走两步,如果first指针和second指针相遇,说明有环.时间复杂度为O (n). 方法 // 方法:检测单链表是否有环 public boolean hasCycle(Node head) { if (head == null) { return false; } Node first = head; Nod

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

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

笔试算法题(54):快速排序实现之单向扫描、双向扫描(single-direction scanning, bidirectional scanning of Quick Sort)

议题:快速排序实现之一(单向遍历) 分析: 算法原理:主要由两部分组成,一部分是递归部分QuickSort,它将调用partition进行划分,并取得划分元素P,然后分别对P之前的部分和P 之后的部分递归调用QuickSort:另一部分是partition,选取划分元素P(随机选取数组中的一个元素,交换到数组末尾位置),定义两个标记 值left和right,随着划分的进行,这两个标记值将数组分成三部分,left之左的部分是小于划分元素P的值,left和right之间的部分是大 于等于划分元素P的

堆排序的实现(联发科子公司创发科技笔试)及判断单向链表的环

先上代码: #include <iostream> #include <algorithm> using namespace std; void HeapAdjust(int data[],int i,int length) { int nChild; int nTemp; for(nTemp=data[i]; 2*i+1<length ;i=nChild) { nChild=2*i+1; if(nChild<length-1&&data[nChild+

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