您的位置:首页 > 职场人生

海涛老师的面试题-作业-链表专题代码及讲解

2012-06-28 18:32 387 查看
View Code

// 链表专题.cpp : 定义控制台应用程序的入口点。
//

/*************************************************
设计者:cslave
版本说明:本代码免费用于商用,拷贝,转移,使用,但是
有本代码导致的问题,本人概不负责。
设计时间:2012.6.25
分发原则:遵守GNU规范。
本专题主要针对链表的相关面试题进行展开,中间涉及一些
经典的链表面试题,有相关代码设计,使用,学习,分发本
代码,请注明本博客的地址 http://www.cnblogs.com/cslave/ 
该篇博文源于看何海涛老师的文章有感,去年何老师在CSDN举行
编程比赛送书活动,本人有幸得到一本,感恩何老师。
且代码中部分函数接口采用何老师的设计。
专题中存在很多不足,希望大家能够帮助改进,谢谢。
****************************************************/

#include "stdafx.h"
#include "List.h"
#include <stdlib.h>
#include <iostream>
using namespace std;

ListNode* CreateListNode(int Value)  //链表节点创建
{
ListNode* pNode= new ListNode();
pNode->Data=Value;
pNode->pNextNode=NULL;
return pNode;
}
/***************************************************

下面这个函数接口能够非常方便的创建链表,尤其是带环链表
。一个函数将两个独立的节点联系起来,使用灵活方便。
其中的好处自己去体会吧。

****************************************************/
void ConnectListNodes(ListNode* pCurrent,ListNode* pNext)
{
if(pCurrent==NULL)
{
throw new exception("Failed to Connect Nodes!");
}
pCurrent->pNextNode=pNext;
}

/******************************************************

打印节点函数

******************************************************/

void PrintNode(ListNode* pNode)
{
if(pNode==NULL)
{
cout<<"The Node Is Empty!"<<endl;
}
else
cout<<"The Value Of The Node Is:"<<pNode->Data<<endl;
}

/******************************************************

打印链表函数

******************************************************/

void PrintList(ListNode* pHead)
{
cout<<"List Print Begin!:"<<endl;
if(!pHead)
return;
ListNode* pTemp=pHead;
while(pTemp)
{
cout<<pTemp->Data<<"--->";
pTemp=pTemp->pNextNode;
}
cout<<"List Print End!:"<<endl;
}

/******************************************************

为链表添加尾节点函数

******************************************************/

void AddTail(ListNode** pHead,int Value)
{
ListNode *pNode=new ListNode();
pNode->Data=Value;
pNode->pNextNode=NULL;

if(*pHead==NULL)    //无节点
*pHead=pNode;
else
{
ListNode* pTemp=*pHead;
while(pTemp->pNextNode!=NULL)
{
pTemp=pTemp->pNextNode;
}
pTemp->pNextNode=pNode;
}
}

/******************************************************

移除节点函数,移除定点值节点。复杂度O(n)

******************************************************/

void RemoveNode(ListNode** pHead,int Value)
{
if(pHead==NULL||*pHead==NULL)
return;
ListNode* pToBeDelete=NULL;
if((*pHead)->Data==Value)
{
pToBeDelete=*pHead;
*pHead=(*pHead)->pNextNode;
}
else
{
ListNode *pNode=*pHead;
while(pNode->pNextNode!=NULL&&pNode->pNextNode->Data!=Value)
{
pNode=pNode->pNextNode;
}
if(pNode->pNextNode!=NULL&&pNode->pNextNode->Data==Value)
{
pToBeDelete=pNode->pNextNode;
pNode->pNextNode=pNode->pNextNode->pNextNode;
}
}
if(pToBeDelete!=NULL)
{
delete pToBeDelete;
pToBeDelete=NULL;
}
}

/******************************************************

移除节点函数,移除定点值节点。复杂度O(1)
这个函数的思想是,将删除节点的下一个节点的值赋给自己,这样
狸猫换太子,将自己成为合法的节点,这样只需删除下一个节点。
还有一个问题:如果删除的结点位于链表的尾部,没有下一个结点,
怎么办?我们需要遍历得到删除结点的前序结点。这个时候时间复杂度是O(n)。

假设链表总共有n个结点,
我们的算法在n-1总情况下时间复杂度是O(1),只有当给定的结点处于链表
末尾的时候,时间复杂度为O(n)。那么平均时间复杂度[(n-1)*O(1)+O(n)]/n,
仍然为O(1)。
******************************************************/

void DeleteNode(ListNode** pHead,ListNode* pToBeDelete) //删除链表定节点 O(1)算法
{
if(!pHead||!pToBeDelete)
return;
if(pToBeDelete->pNextNode!=NULL)
{
ListNode *pNext=pToBeDelete->pNextNode;
pToBeDelete->Data=pNext->Data;
pToBeDelete->pNextNode=pNext->pNextNode;
delete pNext;
pNext=NULL;
}
else if(*pHead==pToBeDelete)
{
delete pToBeDelete;
pToBeDelete=NULL;
*pHead=NULL;
}
else
{
ListNode *pNode=*pHead;
while(pNode->pNextNode!=pToBeDelete)
{
pNode=pNode->pNextNode;
}
pNode->pNextNode=NULL;
delete pToBeDelete;
pToBeDelete=NULL;
}
}

/******************************************************

查找链表倒数第K个节点,仅需遍历一遍链表。
遍历时维持两个指针,第一个指针从链表的头指针开始遍历,
在第k-1步之前,第二个指针保持不动;在第k-1步开始,
第二个指针也开始从链表的头指针开始遍历。由于两个指针的距离保持在k-1,
当第一个(走在前面的)指针到达链表的尾结点时,
第二个指针(走在后面的)指针正好是倒数第k个结点。

******************************************************/

ListNode* FindKthToTail(ListNode* pHead,unsigned int k)//倒数第k个节点
{
if(!pHead||k==0)
return NULL;
ListNode *pFront=pHead;
ListNode *pBack=pHead;
for(unsigned int i=0;i< k-1;i++)
{
if( pFront->pNextNode!=NULL)
pFront=pFront->pNextNode;
else
{
return NULL;
}
}
while(pFront->pNextNode!=NULL)
{
pFront=pFront->pNextNode;
pBack=pBack->pNextNode;
}
return pBack;
}

/******************************************************

求链表中间点函数,也很简单,设置两个指针,一个走两步,另外
一个走一步,这样第二个到链表尾时,第一个刚好一半,即为链表
中间点。

******************************************************/

//求链表的中间节点

ListNode* FindMiddleList(ListNode *pHead)
{
if(pHead==NULL)
return NULL;
if(!(pHead->pNextNode))
return pHead;
ListNode *pFront=pHead;
ListNode *pBack=pHead;
unsigned int i=1;
while(pFront->pNextNode)
{
pFront=pFront->pNextNode;
if(!(i&1))
pBack=pBack->pNextNode;
i++;
}
return pBack;
}

/******************************************************

判断链表是否有环函数,想法是设置两个指针,其中一个指针走两步,
另外一个指针走一步,这样这样如果有环,他们一定会相遇,如果没有环
则两步指针会在O(n)量级完成判断。有环也在同样量级,但是小于n,
比如环很大的时候。

******************************************************/

//求链表是否有环
bool JudgeListRing(ListNode* pHead)
{
if(!pHead||pHead->pNextNode==NULL) //单节点可能是环
return false;
if(pHead->pNextNode==pHead)        //单节点环
return true;
ListNode* pFront=pHead;
ListNode* pBack=pHead;
while(pFront->pNextNode->pNextNode!=NULL)
{
pBack=pBack->pNextNode;
pFront=pFront->pNextNode->pNextNode;
if(pFront==pBack)
return true;
if(pFront->pNextNode==NULL)
break;
}
return false;
}

/******************************************************

求环的起始点位置函数,这个我先来推导一个关系式。
设两个指针,同样一个一步,一个二步,设一步指针和二步指针相遇时
一步指针走了s  则二步指针走了2s ,他们相遇时必然在环里相遇,因为
环外无法相遇,设环长为r 则相遇时,二步指针比一步指针多走了nr,
n为整数且大于等于1.
2s=s+nr;
再设环起点到出发点为a,相遇时一步指针在环内走了x,链表总长度为L
则有:
s=a+x
a=s-x=nr-x=(n-1)r+r-x=(n-1)r+L-a-x
好了 我得到这个式子:
a=(n-1)r+L-a-x
(L – a – x)为相遇点到环入口点的距离,由此可知,
从链表头到环入口点等于(n-1)循环内环+相遇点到环入口点,
于是我们从链表头、与相遇点分别设一个指针,每次各走一步,
两个指针必定相遇,且相遇第一点为环入口点。

******************************************************/

//求链表环起点位置

ListNode* CalculateListRingPos(ListNode* pHead)
{
if(!JudgeListRing(pHead))
return NULL;
if(pHead->pNextNode==pHead||pHead->pNextNode->pNextNode==pHead)
return pHead;
ListNode *pFront=pHead;
ListNode *pBack =pHead;
ListNode *pComp =pHead;
while(pFront->pNextNode->pNextNode!=NULL)
{
pBack=pBack->pNextNode;
pFront=pFront->pNextNode->pNextNode;
if(pFront==pBack)
break;
}
if(pFront==pBack)
{
while(pFront!=pComp)
{
pFront=pFront->pNextNode;
pComp=pComp->pNextNode;
}
return pFront;
}
return NULL;
}

//链表反转
ListNode* ReverseList(ListNode* pHead)
{
ListNode* pReverseNode=NULL;
ListNode* pNode=pHead;
ListNode* pPrev=NULL;
while(pNode!=NULL)
{
ListNode* pNext=pNode->pNextNode;
if(pNext==NULL)
pReverseNode=pNode;
pNode->pNextNode=pPrev;
pPrev=pNode;
pNode=pNext;
}
return pReverseNode;
}

/******************************************************
判断两个链表相交的函数,两个链表相交分为4种情况
1 两个链表都没有环 只需要检查尾节点是否相等
2,3  其中一个链表有环,另外一个没有环,这种不可能相交
4 两个链表都有环,这里,两个链表相交地点必然是环入口点
或者入口点之前,想想为什么?
若链B和链A都有环,且链B在链A环内相交,则链A入口点到相交点的
元素不在环B上,那么链B不存在该环。
所以仅需判断两个链表环入口点是否相等,即可判断是否相交。

******************************************************/

//判断两个链表是否相交

bool  JudgeListCross(ListNode *pHead1,ListNode *pHead2)
{
if(!pHead1||!pHead2) return false;
bool judge1=JudgeListRing(pHead1);
bool judge2=JudgeListRing(pHead2);
int  Result=judge1*2+judge2;
ListNode *pNode1=pHead1;
ListNode *pNode2=pHead2;
ListNode *pRing1=NULL;
ListNode *pRing2=NULL;
ListNode *pRing=NULL;
switch(Result)
{
case 0:
while(!pNode1->pNextNode)
pNode1=pNode1->pNextNode;
while(!pNode2->pNextNode)
pNode2=pNode2->pNextNode;
if(pNode1==pNode2)
return true;
else
return false;
break;
case 1:
return false;
break;
case 2:
return false;
break;
case 3:
pRing1=CalculateListRingPos(pHead1);
pRing2=CalculateListRingPos(pHead2);
pRing=pRing1;
while(pRing1->pNextNode!=pRing)
{
if(pRing1==pRing2)
return true;
else
pRing1=pRing1->pNextNode;
}
return false;
break;
default:
break;
}
return false;
}

/******************************************************

带环链表销毁函数,要注意是带环的,所以写的较复杂些。

******************************************************/

void DestroyList(ListNode *pHead)
{
ListNode *pNode=pHead;
ListNode *pTemp=NULL;
int i=0;
if(JudgeListRing(pHead))
pTemp=CalculateListRingPos(pHead);
while(pNode!=NULL)
{
pHead=pHead->pNextNode;
if(pNode!=pTemp)
delete pNode;
else
{
if(!i)
{
delete pNode;
i++;
}
else
return;

}
pNode=pHead;
}
}

void Test1()
{
printf("=====Test1 测试链表的相关的操作=====\n");
ListNode* pNode1 = CreateListNode(1);
ListNode* pNode2 = CreateListNode(2);
ListNode* pNode3 = CreateListNode(3);
ListNode* pNode4 = CreateListNode(4);
ListNode* pNode5 = CreateListNode(5);

ConnectListNodes(pNode1, pNode2);
ConnectListNodes(pNode2, pNode3);
ConnectListNodes(pNode3, pNode4);
ConnectListNodes(pNode4, pNode5);
AddTail(&pNode1,6);
RemoveNode(&pNode1,5);
DeleteNode(&pNode1,pNode4);
printf("expected result: 3.\n");
ListNode* pNode = FindKthToTail(pNode1, 2);
PrintNode(pNode);
AddTail(&pNode1,7);
printf("expected result: 3.\n");
ListNode* Node=FindMiddleList(pNode1);
PrintNode(Node);
printf("链表反转后的结构为!\n");
Node=ReverseList(pNode1);
PrintList(Node);

DestroyList(pNode1);
}

void Test2()
{
printf("=====Test2 测试链表的是否存在环及位置=====\n");
ListNode* pNode1 = CreateListNode(1);
ListNode* pNode2 = CreateListNode(2);
ListNode* pNode3 = CreateListNode(3);
ListNode* pNode4 = CreateListNode(4);
ListNode* pNode5 = CreateListNode(5);

ConnectListNodes(pNode1, pNode2);
ConnectListNodes(pNode2, pNode3);
ConnectListNodes(pNode3, pNode4);
ConnectListNodes(pNode4, pNode5);
ConnectListNodes(pNode5, pNode3);
bool Exist= JudgeListRing(pNode1);
ListNode * Node=NULL;
if(Exist)
{
printf("链表存在环!入口点为:\n");
printf("期望的入口点为3:\n");
Node=CalculateListRingPos(pNode1);
PrintNode(Node);
}
else
printf("链表不存在环!\n");

DestroyList(pNode1);

}

void Test3()
{
printf("=====Test3 测试两个链表是否相交=====\n");
ListNode* pNode1 = CreateListNode(1);
ListNode* pNode2 = CreateListNode(2);
ListNode* pNode3 = CreateListNode(3);
ListNode* pNode4 = CreateListNode(4);
ListNode* pNode5 = CreateListNode(5);
ListNode* pNode6 = CreateListNode(6);
ListNode* pNode7 = CreateListNode(7);
ListNode* pNode8 = CreateListNode(8);
ListNode* pNode9 = CreateListNode(9);

ConnectListNodes(pNode1, pNode2);
ConnectListNodes(pNode2, pNode3);
ConnectListNodes(pNode3, pNode4);
ConnectListNodes(pNode4, pNode5);
ConnectListNodes(pNode5, pNode3);
ConnectListNodes(pNode6, pNode7);
ConnectListNodes(pNode7, pNode8);
ConnectListNodes(pNode8, pNode9);
ConnectListNodes(pNode9, pNode3);
bool Exist=JudgeListCross(pNode1,pNode6);
if(Exist)
{
printf("两个链表相交:\n");
}
else
printf("两个链表不相交!\n");
}

int _tmain(int argc, _TCHAR* argv[])
{
Test1();
Test2();
Test3();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐