程序员面试金典: 9.2链表 2.6给定有环链表,实现算法返回环路的开头节点
2016-12-21 17:00
323 查看
#include <iostream> #include <stdio.h> using namespace std; /* 问题:给定一个有环链表,实现一个算法返回环路的开头节点 分析:快慢指针,一个每次走一步,一个每次走两步,如果能够相遇,必定存在环 现在关键是如何找到环路初始节点 未解决:关键是要寻找相遇点y,环起始点x,环的长度L 三者之间的关系,目前关系忘了 书上分析:设头结点距离环的起始点距离为L,设从环起始点走了长度k处相遇,设环的总长度为m 则第一次相遇时: 快指针走的距离S1=L + m + k (1) 慢指针走的距离S2=L + k (2) 满足: S1=2*S2 (3) 解方程得L = m - k 该式子的意思是: 头结点距离环的起始点距离=相遇点距离环起始点的距离 推得规律: 1 头指针和相遇点处指针同时每次走一步,那么相遇的地方就是环的起始点 2 环的长度=快慢指针在相遇点出继续走再次相遇时所走的步数 输入: 5(链表长度) 2(环的起始下标) 0 1 2 3 4(链表中每个节点的元素) 输出: 又换 换的其实地址 环的长度3 关键: 1 设头结点距离环的起始点距离为L,设从环起始点走了长度k处相遇,设环的总长度为m 则第一次相遇时: 快指针走的距离S1=L + m + k (1) 慢指针走的距离S2=L + k (2) 满足: S1=2*S2 (3) 解方程得L = m - k 该式子的意思是: 头结点距离环的起始点距离=相遇点距离环起始点的距离 推得规律: 1 头指针和相遇点处指针同时每次走一步,那么相遇的地方就是环的起始点 2 环的长度=快慢指针在相遇点出继续走再次相遇时所走的步数 2 环的起始点=头结点,相遇节点分别每次走一步后,相遇之后的节点 3 环的长度=快慢指针在相遇节点处再次相遇所走的步数 */ typedef struct Node { int value; Node* pNext; }Node; //构建连边,按照尾插法来做,返回节点值到出现次数的映射,k表示环形链表的值 void buildList(int* pArray , Node* head , int num , int k) { if(pArray == NULL) { return; } if(head == NULL) { return; } //尾插法: 保留最后一个结尾节点,将新生成的节点插入在结尾节点,并令结尾节点为当前节点 Node* pLast = head; Node* pCyclicNode = NULL; for(int i = 0 ; i < num ; i++) { int value = *(pArray + i); Node* pNode = new Node(); pNode->value = value; pLast->pNext = pNode; pLast = pNode; if(i == k) { pCyclicNode = pNode; } } //找到环形节点后,就令最后一个节点指向环形节点 pLast->pNext = pCyclicNode; } //如果有环,则返回相遇的节点 Node* isHasCycle(Node* pHead) { if(pHead == NULL) { return false; } Node* pFast = pHead->pNext; Node* pSlow = pFast; while( pSlow && pFast) { pFast = pFast->pNext->pNext; pSlow = pSlow->pNext; //相遇 if(pSlow == pFast) { break; } } //如果没有环,pFast应该已经为空了 if(pFast == NULL) { return NULL; } return pFast; } //寻找环形链表的起始环节点,提供相遇的节点,根据 头结点距离环形节点的距离k=相遇节点距离环形节点的距离,则从头结点和相遇节点分别继续向下走,相遇处即为所求 Node* findCyclicBeginNode(Node* pHead , Node* meetNode) { Node* tempNode = meetNode; if(pHead == NULL || meetNode == NULL) { return NULL; } Node* pNode = pHead->pNext; while(pNode != meetNode) { pNode = pNode->pNext; meetNode = meetNode->pNext; } meetNode = tempNode; return pNode; } //计算环形链表中环的长度 = 快慢指针在相遇处继续按照快慢步走后,下次相遇时所走的步数 int calculateCycleLength(Node* pHead , Node* meetNode) { if(pHead == NULL || meetNode == NULL) { return -1; } Node* pFast = meetNode->pNext->pNext; Node* pSlow = meetNode->pNext; int count = 1; while(pFast != pSlow) { pFast = pFast->pNext->pNext; pSlow = pSlow->pNext; count++; } return count; } void printList(Node* pHead) { if(NULL == pHead) { return; } Node* pNode = pHead->pNext; while(pNode) { cout << pNode->value << " "; pNode = pNode->pNext; } cout << endl; } //注意去除环形链表中环形节点 void releaseList(Node* pHead) { if(NULL == pHead) { return; } Node* pNode = pHead->pNext; Node* pPrevious = pHead; while(pNode) { Node* pDeleteNode = pNode; pPrevious->pNext = pNode->pNext; pNode = pNode->pNext; pPrevious = pPrevious->pNext; delete pDeleteNode; } //删除头结点 delete pHead; } int main(int argc, char* argv[]) { int n , k; while(cin >> n >> k) { int* pArr = new int ; for(int i = 0 ; i < n ; i++) { cin >> pArr[i]; } Node* pHead = new Node(); buildList(pArr , pHead , n , k); Node* meetNode = isHasCycle(pHead); Node* beginNode = findCyclicBeginNode(pHead , meetNode); int length = calculateCycleLength(pHead , meetNode); if(meetNode != NULL) { cout << "链表存在环" << endl; cout << "环的起始节点值为:" << beginNode->value << endl; cout << "环的长度为:" << length << endl; } else { cout << "链表不存在环" << endl; } //printList(pHead); releaseList(pHead); delete[] pArr; } system("pause"); return 0; }
相关文章推荐
- 9.2链表(六)——给定一个有环链表,实现一个算法返回环路的开头结点
- 【Java】给定一个有环链表,实现算法返回环路的开头结点
- 给定一个有环链表,实现一个算法返回环路的开头结点
- 给定一个有环链表,实现一个算法返回环路的开头结点
- CCI 2.6 寻找有环链表环路的开头节点
- 010给定一个循环链表,实现一个算法返回这个环的开始结点 (keep it up)
- 给定一个循环链表,实现一个算法返回这个环的开始结点
- java实现输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head
- 给定一个链表,一次颠倒链表k的节点并返回其修改列表。如果节点的数量不是k的倍数,那末最后的剩余节点应该保持原样。
- 给定一个链表,删除链表中倒数第n个节点,返回链表的头节点。
- 给定一个链表,交换每两个相邻节点并返回其头部。
- 一个链表的每个节点,有一个指向next指针指向下一个节点,还有一个rand指针指向这个链表中的一个随机节点或NULL,现在要求复制一个单链表来实现这个链表,返回复制后的新链表。
- 程序员面试金典: 9.2链表 2.4以给定值x为基准将链表分割成两部分
- 程序员面试金典: 9.2链表 2.1移除未排序链表中重复的节点
- 给你一个链表和一个 random函数, 设计一个算法能随机返回链表的某个节点, 要求每个节点被返回的概率一样。限制条件是只能遍历链表一次并且不能用额外空间。
- 原串翻转 请实现一个算法,在不使用额外数据结构和储存空间的情况下,翻转一个给定的字符串(可以使用单个过程变量)。 给定一个string iniString,请返回一个string,为翻转后的字符串
- 【算法之链表(四)】在不使用额外节点存储空间的情况下,实现单链表逆序
- 请实现一个算法,在不使用额外数据结构和储存空间的情况下,翻转一个给定的字符串(可以使用单个过程变量)。 给定一个string iniString,请返回一个string,为翻转后的字符串。保证字符串的
- c实现对给定线性链表head按节点值非递减次序输出所有节点,并把刚输出的节点从链表中删除
- 给定一个单向链表(长度未知),请设计一个既节省时间又节省空间的算法来找出该链表中的倒数第m个元素。实现这个算法,并为可能出现的特例情况安排好处理措施。“倒数第m个元素”是这样规定的:当m=0时,链表的