//剑指offer 之 链表 //面试题6 从尾到头打印链表 /***************************************************************************************** 问题描述: 输入一个链表的头节点,从尾到头反过来打印出每个节点的值 链表节点定义如下: struct ListNode{ int m_nValue; ListNode* m_pNext; }; ******************************************************************************************/ //解答如下: //首先实现栈 由于是用C语言 所以需要自己构建栈 int a[100]; //用数组实现栈 int p = 0; //栈顶 //入栈 void push(int a[], int value) { if(p == 99) { /*扩大栈*/ } a[p++] = value; } //出栈 int pop(int a[]); { if(p <= 25) { /*压缩栈*/ } int res = a[--p]; return res; } //判断是否空栈 int isEmpty(int a[]) { return p == 0; } //从尾到头打印链表 //栈实现 void PrintListReversingly_Iteratively(ListNode* pHead) { if(pHead == NULL) return; ListNode *tmp = pHead; while(tmp != NULL) { push(a,tmp->value); tmp = tmp->m_pNext; } int value; while(!isEmpty(a)) { value = pop(a); printf("%d-",value); } } //递归实现 void PrintListReversingly_Recursively(ListNode* pHead) { if(pHead == NULL) return; if(pHead->m_pNext != NULL) { PrintListReversingly_Recursively(pHead->m_pNext); } printf("%d-",pHead->m_nValue); return; } //面试题18:删除链表的节点 /********************************************************** 18-1: 在O(1)时间内删除链表节点 给定单向链表的头指针和一个节点指针 链表节点定义如问题6所述, 函数定义如下 ***********************************************************/ void DeleteNode(ListNode** pListNode, ListNode* pToBeDeleted) { /* 1)节点位于中间:将下一个节点内容复制到该节点,删除下一个节点 时间复杂度为O(1) 2)节点位于尾部:从头遍历至该节点; 3)链表只有一个节点:删除并将头指针置为NULL */ if( pListNode == NULL || pToBeDeleted == NULL) return; //处理情况1 if(pToBeDeleted->m_pNext != NULL) { ListNode *pNext = pToBeDeleted->m_pNext; pToBeDeleted->m_nValue = pNext->m_nValue; pToBeDeleted->m_pNext = pNext->m_pNext; free(pNext); pNext = NULL; } //处理情况3 else if(*pListNode == pToBeDeleted) { free(pToBeDeleted); *pListNode = NULL; pToBeDeleted = NULL; } //处理情况2 else { ListNode *pNext = *pListNode; while(pNext->m_pNext != pToBeDeleted) { pNext = pNext->m_pNext; } pNext->m_pNext = NULL; free(pToBeDeleted); pToBeDeleted = NULL; } /* 复杂度分析:假设链表上有n个节点 对于前n-1个节点,时间复杂度为O(1) 对于最后一个节点,时间复杂度为O(n) 因此,总的为((n-1)*O(1) + O(n)) /n = O(1) */ } /********************************************* 18-2: 在一个排序链表中,如何删除重复节点? **********************************************/ void DeleteDuplication(ListNode** pHead) { if(pHead == NULL || *pHead == NULL) return; ListNode* pPreNode = NULL; //围绕前面节点 此节点 此节点的后续节点 操作 ListNode* pNode = *pHead; while(pNode != NULL) { ListNode *pNext = pNode->m_pNext; int deleteFlag = 0; //判断是否需要删除操作 if(pNode->m_nValue == pNext->m_nValue) deleteFlag = 1; //不需要删除,则指针向后移 if(!deleteFlag) { pPreNode = pNode; pNode = pNode->m_pNext; } else { int value = pNode->m_nValue; ListNode *pToBeDel = pNode; //不断执行删除操作 从删除当前节点开始 //前面节点用于连接后面的节点 while(pToBeDel != NULL && pToBeDel->m_nValue == value) { pNext = pToBeDel->m_pNext; free(pToBeDel); pToBeDel = pNext; } //将需要删除的节点已删除完毕 判断之前删除的节点中是否包含头节点 if(pPreNode == NULL) *pHead = pNext; else pPreNode->m_pNext = pNext; pNode = pNext; } } } /*********************************************************************************************** 面试题22:链表中倒数第k个节点 输入一个链表,输出该连表中倒k个节点 双指针 注意判断边界以及k小于链表个数 ************************************************************************************************/ ListNode* FindKthToTail(ListNode* pListNode, unsigned int k) { if(pListNode == NULL || k <= 0) return NULL; ListNode *pAhead = pListNode; ListNode *pBhead = NULL; for(unsigned int i=0;i<k-1;i++) { if(pAhead->m_pNext != NULL) { pAhead = pAhead->m_pNext; } else { return NULL; } } pBhead = pListNode; while(pAhead != NULL) { pBhead = pBhead->m_pNext; pAhead = pAhead->m_pNext; } return pBhead; } /* 拓展:求链表中间节点 方法:快慢指针 */ /*********************************************************************************************** 面试题23: 链表中环的入口节点 如果一个链表中有环,如何找出出环的入口节点 解题思路: 1)先判断有无环存在: 设定快慢指针,若慢指针会追上快指针,则存在环 2)得出环中节点数目n: 在1)基础上,返回两指针相遇点,该点在环内,然后让指针从 该点出发走一圈,记录数目 3)根据环中节点数目n: 还是两指针,一个在另一个前面n步,同时走,则相遇处为入口 ************************************************************************************************/ ListNode* MeettingNode(ListNode *pHead) { if(pHead == NULL || pHead->m_pNext == NULL) return NULL; ListNode *pFast = pHead; ListNode *pSlow = pHead; while(pFast != NULL && pSlow!= NULL) { if(pFast == pSlow) return pFast; pSlow = pSlow->m_pNext; pFast = pFast->m_pNext; if(pFast != NULL)//当链表中无环时起到了判断作用 pFast = pFast->m_pNext; } return NULL; } ListNode* EntryNodeOfLoop(ListNode* pHead) { ListNode *meetingNode = MeettingNode(pHead); if(meetingNode == NULL) return NULL; //得环中节点数目n int num_of_nodes = 1; ListNode *tmp1 = meetingNode; while(tmp1->m_pNext != meetingNode) { num_of_nodes++; tmp1 = tmp1->m_pNext; } //移动第一个指针 n步 tmp1 = pHead; for(int i=0;i<num_of_nodes;i++) { tmp1 = tmp1->m_pNext; } //两指针一起走 ListNode *tmp2 = pHead; while(tmp1 != tmp2) { tmp1 = tmp1->m_pNext; tmp2 = tmp2->m_pNext; } return tmp1; } /***************************************************************************************** 面试题24: 反转链表 定义一个函数,输入一个链表的头节点, 反转该链表并输出反转后链表的头节点 *******************************************************************************************/ ListNode *ReverseList(ListNode* pHead) { if(pHead == NULL || pHead->m_pNext == NULL) return pHead; ListNode *pPreNode = NULL; ListNode *pNode = pHead; ListNode *pNext = NULL; ListNode *pReverseHead = NULL; while(pNode != NULL) { pNext = pNode->m_pNext; //判断是否到尾部 if(pNext == NULL) pReverseHead = pNode; pNode->m_pNext = pPreNode; pPreNode = pNode; pNode = pNext; } return pReverseHead; } /********************************************************************************************* 面试题25:合并两个排序链表 输入两个递增排序的链表,合并这两个链表并使新链表中的 节点仍然是排序的 *********************************************************************************************/ ListNode *Merge(ListNode *pHead1, ListNode *pHead2) { if(pHead1 == NULL) return pHead2; else if(pHead2 == NULL) return pHead1; ListNode* pMergedHead = NULL; if(pHead1->m_nValue < pHead2->m_nValue) { pMergedHead = pHead1; pMergedHead -> m_pNext = Merge(pHead1->m_pNext,pHead2); } else { pMergedHead = pHead2; pMergedHead -> m_pNext = Merge(pHead1,pHead2->m_pNext); } return pMergedHead; }
/* 面试题35 复杂链表的复制 实现函数ComplexListNode * Clone(ComplexListNode* pHead); 复制一个复杂链表,每个节点除了有一个m_pNext指针指向下一个节点,还有一个 m_pSibling指针指向链表中的任意节点orNULL */ struct ComplexListNode{ int m_nValue; ComplexListNode* m_pNext; ComplexListNode* m_pSibling; }; /* 1)复制每个节点链接在初始节点后面; 2)将复制出来的节点指向SIBLING; 3)拆分两个链表; */ void CloneNodes(ComplexListNode* pHead) { if(pHead == NULL) return; ComplexListNode *pNode = pHead; while(pNode != NULL) { ComplexListNode *pClone = (ComplexListNode *)malloc(sizeof(struct ComplexListNode)); pClone->m_nValue = pNode->m_nValue; pClone->m_pNext = pNode->m_pNext; pClone->m_pSibling = NULL; pNode->m_pNext = pClone; pNode = pClone->m_pNext; } } void ConnectSiblingNodes(ComplexListNode* pHead) { ListNode *pNode = pHead; while(pNode != NULL) { ComplexListNode *pClone = pNode->m_pNext; if(pNode->m_pSibling != NULL) { pClone->m_pSibling = pNode->m_pSibling->m_pNext; } pNode = pClone->m_pNext; } } ComplexListNode* ReconnectNodes(ComplexListNode* pHead) { ListNode *pNode = pHead; ListNode *pCloneHead - NULL; ListNode *pCloneNode = NULL; if(pNode != NULL) { pCloneHead = pCloneNode = pNode->m_pNext; pNode->m_pNext = pCloneNode->m_pNext; pNode = pNode->m_pNext; } while(pNode != NULL) { pCloneNode->m_pNext = pNode->m_pNext; pCloneNode = pCloneNode->m_pNext; pNode->m_pNext = pCloneNode->m_pNext; pNode = pNode->m_pNext; } return pCloneHead; } //完整步骤 ComplexListNode* Clone(ComplexListNode* pHead) { CloneNodes(pHead); ConnectSiblingNodes(pHead); return ReconnectNodes(pHead); } /* 面试题52:两个链表的第一个公共节点 输入两个链表,找出它们的第一个公共节点 */ struct ListNode{ int m_nKey; ListNode* m_pNext; }; ListNode* FindFirstCommonNode(ListNode* pHead1, ListNode* pHead2) { if(pHead1 == NULL || pHead2 == NULL) return NULL; unsigned int nLength1 = GetListLength(pHead1); unsigned int nLength2 = GetListLength(pHead2); int nLengthDif = nLength1 - nLength2; ListNode *pListHeadLong = pHead1; ListNode *pListHeadShort = pHead2; if(nLength1 > nLength2) { nLengthDif = nLength2 - nLength1; ListNode *pListHeadLong = pHead2; ListNode *pListHeadShort = pHead1; } //先让长链表上的指针先走几步 for(int i=0;i<nLengthDif;i++) { pListHeadLong = pListHeadLong->m_pNext; } //两指针同时走 直到节点值相同 while((pListHeadLong != NULL) && (pListHeadShort != NULL) && (pListHeadLong != pListHeadShort)) { pListHeadLong = pListHeadLong->m_pNext; pListHeadShort = pListHeadShort->m_pNext; } return pListHeadLong; } unsigned int GetListLength(ListNode* pHead) { unsigned int nLength = 0; ListNode* pNode = pHead; while(pNode != NULL) { nLength++; pNode = pNode->m_pNext; } return nLength; }