面试编程题:(三)单链表
来源:互联网 发布:酷宝数据 作假 编辑:程序博客网 时间:2024/05/18 06:45
(一)链表结构
struct ListNode {int val;struct ListNode *next;ListNode(int x) :val(x), next(NULL) {}};
(二)链表操作
(1) 链表中倒数第k个结点。(滴滴)
要求:只遍历一遍链表
思路:定义两个指针,第一个指针从链表头指针开始前走k-1步,第二个指针保持不动;从第k步开始,第二个指针也开始从头遍历。由于两个指针的距离保持在k-1,因此当第一个指针到达链表尾结点时,第二个指针指向的正好是倒数第k个节点。
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) { if(pListHead==NULL||k<=0) return NULL; ListNode* first=pListHead; ListNode* second=pListHead; for(unsigned int i=0;i<k-1;i++){ if(first->next!=NULL) first=first->next; else return NULL; } while(first->next!=NULL){ first=first->next; second=second->next; } return second; }(2)求单链表中间节点
如果链表中节点总数是奇数,返回中间节点;如果节点总数是偶数,返回两个节点中的任意一个。
定义两个指针,同时从链表头节点出发,一个指针一次走一步,另一个指针一次偶两步。当走的快的指针走到链表的末尾时,走的慢的指针正好在链表的中间。
(3)判断一个单链表中是否有环
定义两个指针,同时从链表的头节点出发,一个指针一次走一步,另一个指针一次走两步。如果走得快的指针追上走得慢的指针,那么链表就是环形链表;如果走得快的指针到达链表的末尾都没有追上链表的第一个指针,那么链表就不是环形链表。
(4)如果一个单链表中有环,判断环的入口点(滴滴)
定义两个指针P1和P2指向链表的头节点,如果链表中环有k个节点,指针P1先在链表中向前移动k步,然后两个指针以相同的速度向前移动。当第二个指针指向环的入口点时,第一个指针已经绕环走了一圈回到了入口点。两个指针的相遇点即为环的入口点。(原因:假设链表总共有n个节点,则P2到环的入口需走n-k步;P1已经走了k步,到达环的入口点也只需再走n-k步)
单链表中环长度的计算:在(3)的基础上,让慢指针每次往前移动一步,并计数。当指针再次到达当前节点时,计数个数即为环的长度
ListNode* EntryNodeOfLoop(ListNode* pHead) { if(pHead==NULL) return NULL; ListNode* pSlow=pHead->next; if(pSlow==NULL) return NULL; ListNode* pFast=pSlow->next; while(pSlow!=NULL&&pFast!=NULL) { if(pSlow==pFast) break;//如果链表中有环,则停止 pSlow=pSlow->next; pFast=pFast->next; if(pFast!=NULL)//防止链表无环时,非法访问NULL节点的next pFast=pFast->next; } if(pFast==NULL)//如果到达NULL节点,说明无环 return NULL; ListNode* pNode=pSlow->next; int count=1;//防止下面循环开始即pNode==pSlow,让pNode=pSlow->next,count初始为1 while(pNode!=pSlow)//统计环中的节点数 { count++; pNode=pNode->next; } pFast=pHead; pSlow=pHead; for(int i=0;i<count;i++) pFast=pFast->next; while(pFast!=pSlow) { pFast=pFast->next; pSlow=pSlow->next; } return pFast; }
(5)反转单链表(华为)
1)非递归实现
ListNode* ReverseList(ListNode* pHead) { if(pHead==NULL) return NULL; ListNode* pNode=pHead; ListNode* reverseHead=NULL; ListNode* pPrev=NULL; while(pNode!=NULL){ ListNode* pNext=pNode->next;//保存链表下一节点,防止断链 if(pNext==NULL) reverseHead=pNode; pNode->next=pPrev; pPrev=pNode; pNode=pNext; } return reverseHead; }2)递归实现
ListNode* ReverseList(ListNode* pHead) { if(pHead==NULL||pHead->next==NULL){ return pHead; } ListNode* prevHead=ReverseList(pHead->next); pHead->next->next=pHead; pHead->next=NULL; return prevHead; }
(6)合并两个有序的单链表,合并后链表有序。(瑞晟)
1)非递归实现
ListNode* Merge(ListNode* pHead1, ListNode* pHead2) { if(pHead1==NULL) return pHead2; else if(pHead2==NULL) return pHead1; ListNode* mergerList=NULL; ListNode* mergerListHead=NULL; //处理头节点 if(pHead1->val<pHead2->val) { mergerListHead=pHead1; pHead1=pHead1->next; mergerList=mergerList->next; } else { mergerListHead=pHead2; mergerList=mergerList->next; pHead2=pHead2->next; } mergerList=mergerListHead; //处理若两个链表都不为空时的情况 while(pHead1!=NULL&&pHead2!=NULL) { if(pHead1->val<=pHead2->val) { mergerList->next=pHead1; mergerList=mergerList->next; pHead1=pHead1->next; } else { mergerList->next=pHead2; mergerList=mergerList->next; pHead2=pHead2->next; } } //处理其中一个链表为空时的情况 while(pHead1!=NULL) { mergerList->next=pHead1; mergerList=mergerList->next; pHead1=pHead1->next; } while(pHead2!=NULL) { mergerList->next=pHead2; mergerList=mergerList->next; pHead2=pHead2->next; } return mergerListHead; }
2)递归实现
ListNode* Merge(ListNode* pHead1, ListNode* pHead2) { if(pHead1==NULL) return pHead2; else if(pHead2==NULL) return pHead1; ListNode* mergerListHead=NULL; if(pHead1->val<pHead2->val) { mergerListHead=pHead1; mergerListHead->next=Merge(pHead1->next,pHead2); } else { mergerListHead=pHead2; mergerListHead->next=Merge(pHead1,pHead2->next); } return mergerListHead; }
(7)两个单链表的第一个公共结点
思路:因为是单链表,每个节点只有一个next节点。因此从第一个公共节点开始,之后的节点都是重合的,不可能在出现分叉。
方法一:首先遍历两个链表得到它们的长度,就知道哪个链表比较长,以及长链表比短链表多的节点数n。第二次遍历的时候,在较长的链表上先走n步,接着再同时在两个链表上遍历,找到第一个相同的点就是它们的第一个公共点。
ListNode* FindFirstCommonNode( ListNode *pHead1, ListNode *pHead2) { if(pHead1==NULL||pHead2==NULL) return NULL; ListNode* pNode1=pHead1; ListNode* pNode2=pHead2; int count1=0; int count2=0; while(pNode1!=NULL)//遍历统计链表1的长度 { pNode1=pNode1->next; count1++; } while(pNode2!=NULL)//遍历统计链表2的长度 { pNode2=pNode2->next; count2++; } int distance=(count1>count2)?(count1-count2):(count2-count1); for(int i=0;i<distance;i++)//链表1,2中比较长的先移动distance距离 { if(count1>count2) pHead1=pHead1->next; else pHead2=pHead2->next; } while(pHead1!=pHead2)//然后同时移动直到找到第1个公共点 { pHead1=pHead1->next; pHead2=pHead2->next; } return pHead1; }
方法二:利用双栈;把链表中的节点分别插入栈中。从链表的尾部开始比较,最后一个相同的节点就是要找的节点。
ListNode* FindFirstCommonNode( ListNode *pHead1, ListNode *pHead2) { if(pHead1==NULL||pHead2==NULL) return NULL; ListNode* pNode1=pHead1; ListNode* pNode2=pHead2; stack<ListNode*> ListStack1; stack<ListNode*> ListStack2; while(pNode1!=NULL) { ListStack1.push(pNode1); pNode1=pNode1->next; } while(pNode2!=NULL) { ListStack2.push(pNode2); pNode2=pNode2->next; } ListNode* firstCommNode=NULL; while(!ListStack1.empty()&&!ListStack2.empty()) { if(ListStack1.top()==ListStack2.top()) { firstCommNode=ListStack1.top(); ListStack1.pop(); ListStack2.pop(); } else break; } return firstCommNode; }
- 面试编程题:(三)单链表
- 常见的C语言面试编程题(三)
- 面试算法题(三)
- 面试编程题(一)
- java 面试 笔试题 大全 (三)
- 面试常备题(三)
- 面试真题(三)
- 面试(三)
- android面试(三)
- php面试(三)
- 面试智力题(三)
- 腾讯面试(三)
- C++面试(三)
- 百度面试(三)
- 面试记录(三)
- 面试宝典(三)
- 面试(三)
- 腾讯面试(三)
- 生成一个0-N的随机数
- 画图板
- js实现List的部分功能
- 刷题笔记(计算机网络)
- Spring开发步骤
- 面试编程题:(三)单链表
- 二叉树的反转镜像
- 敏捷开发学习总结(3): 思考开发文档的利与弊
- CF #374 div2 C 树状dp
- iOS6、7、8、9新特性汇总和适配说明
- 个人记录-LeetCode 8.String to Integer (atoi)
- LeetCode : Find Peak Element
- 类文件结构(二)
- js学习笔记(一)实现Bingo卡片