//剑指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;
}