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

单链表常见面试题

2016-04-02 12:37 609 查看
一、比较顺序表与链表的优缺点:

单链表:它是一种链式存储的线性表,用一组地址任意的存储单元存放线性的的数据元素,称存储单元为一个节点。
顺序表:采用顺序存储结构的线性表通常称为顺序表。
线性表的顺序存储结构:指用一组地址连续的存储单元依次存储线性表中的各个元素,使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间的逻辑上的相邻关系。
线性表优点:
因为逻辑上相邻的元素其存储的物理位置也是相邻的,所以无需为表示结点间的逻辑关系而增加额外的存储空间,并且具有可以方便的随机存取表中任一元素。
线性表缺点:
1、插入或删除运算不方便,除表尾的位置外,在表的其他位置上进行插入或者删除操作都必须移动大量的结点,其效率非常低。
2、由于顺序表要求占用连续的存储空间,存储分配只能预先进行静态分配。因此,当表的长度变化较大时,难以确定合适的存储规模。若按照可能达到的最大长度预先分配表空间,则可能造成一部分空间长期闲置而得不到充分利用;若事先对表的长度估计不足,则插入操作可能使表长超过预先分配的空间而造成溢出。
1、顺序表支持随机访问,单链表不支持随机访问。
2、顺序表插入/删除数据效率很低,时间复杂度为O(N)(除尾插尾删),单链表插入/删除效率更高,时间复杂度为O(1)。
3、顺序表的CPU高速缓存效率更高,单链表CPU高速缓存效率低。
//单链表头插
void PushFront(pSListNode *pHead , DataType data)
{
assert( pHead);
if (pHead == *pHead)
{
*phead = ByeNode( data);
}
else
{
pSListNode pNode = ByeNode( data);
pNode->pNext = * pHead;
* pHead = pNode;
}

}

//单链表头删
void PopFront(pSListNode *pHead )
{
assert( pHead != NULL);
if (*pHead == NULL)
{
return;
}
else
{
pSListNode pNode = * pHead;
* pHead = pNode->pNext;
free(pNode);
}
}

//单链表查找一个结点
pSListNode Find(pSListNode pHead, pSListNode Pos, DataType data )
{
pSListNode pNode = pHead;
while (pNode)
{
if (pNode->data == data )
{
return pNode;
}
pNode = pNode->pNext;
}
return NULL;
}

//删除单链表的某个结点
void Erase(pSListNode *pHead , pSListNode Pos)
{
pSListNode pPrePos = * pHead;
assert(NULL != pHead);
assert(NULL != Pos);
if (Pos == *pHead)
{
* pHead = Pos ->pNext;
free( Pos);
return;
}
while ((pPrePos != NULL) && (pPrePos->pNext != Pos))
{
pPrePos = pPrePos->pNext;
}
if (pPrePos->pNext == Pos )
{
pPrePos->pNext = Pos->pNext;
free( Pos);
}

}

//单链表插入
void Insert(pSListNode *pHead , pSListNode Pos, DataType data)
{
pSListNode pNewNode = NULL;
assert(NULL != pHead);
assert( Pos != NULL);
if (*pHead == NULL)
return;
pNewNode = ByeNode( data);
if (NULL != pNewNode)
{
pNewNode->pNext = Pos->pNext;
Pos->pNext = pNewNode;
}

}

//正向打印单链表
void PrintList(pSListNode pHead )
{
pSListNode pCurNode = pHead;
while (pCurNode)
{
printf( "%d->", pCurNode->data);
pCurNode = pCurNode->pNext;
}
printf( "NULL\n");
}

//逆向打印单链表
void PrintListTailToHead(pSListNode pHead )
{
if (NULL != pHead )
{
PrintListTailToHead( pHead->pNext);
printf( "%d->", pHead ->data);
}

}

//销毁单链表
void DestroyList(pSListNode *pHead )
{
pSListNode pNode = NULL;
pSListNode pPreNode = NULL;
assert( pHead != NULL);
pNode = * pHead;
while (pNode)
{
pPreNode = pNode;
pNode = pNode->pNext;
}
free(pPreNode);
* pHead = NULL;
}

//删除一个无头单链表的非尾结点

void DelNotTailNode(pSListNode Pos )
{
pSListNode pDelNode = NULL;
assert( Pos != NULL);
assert( Pos->pNext != NULL);
pDelNode = Pos->pNext;
Pos->data = pDelNode->data;
Pos->pNext = pDelNode->pNext;
free(pDelNode);
}

//在无头单链表的一个非头结点前插入一个结点

void InsertNotHead(pSListNode Pos , DataType data)
{
pSListNode pNewNode = NULL;
assert( Pos != NULL);
pNewNode = ByeNode( Pos->data);//新结点保存的是旧结点的值
if (pNewNode != NULL)
{
pNewNode->pNext = Pos->pNext;
Pos->pNext = pNewNode;
Pos->data = data ;
}

}

//查找单链表的中间结点,要求只能遍历一次链表

pSListNode FindMidNode(pSListNode pHead)
{
pSListNode pSlow = pHead;
pSListNode pFast = pHead;
while ((pFast != NULL) && (pFast->pNext != NULL))
{
pSlow = pSlow->pNext;
pFast = pFast->pNext->pNext;
}
return pSlow;
}

//单链表实现约瑟夫环
pSListNode JosephCircle(PsListNode pHead, int M)
{
pSListNode pNode = pHead;
pSListNode pDelNode = pHead;
int k = M ;
if ((pHead == NULL) || (M <= 0))
{
return NULL;
}
while (pNode->pNext != pNode)
{
k = M;
while (--k)
{
pNode = pNode->pNext;
}
pDelNode = pNode->pNext;
pNode->data = pDelNode->data;
pNode->pNext = pDelNode->data;
free(pDelNode);
}
return NULL;
}

//逆置与反转单链表
//头插法
void ReverseList(pSListNode *pHead )
{
pSListNode pNode = NULL;
pSListNode pPreNode = NULL;
pSListNode pNewNode = NULL;
assert( pHead != NULL);
if ((*pHead == NULL)||(*pHead)->pNext==NULL)
{
return;
}
pNode = * pHead;
while (pNode)
{
pPreNode = pNode;
pNode = pNode->pNext;
pPreNode->pNext = pNewNode;
pNewHead = pPreNode;
}
* pHead = pNewNode;
}

//单链表冒泡排序法

void BubbleSortList(pSListNode pHead )
{
pSListNode pTailNode = NULL;
pSListNode pNode = NULL;
pSListNode pPreNode = NULL;
if (pHead == NULL)
{
return;
}
//外循环:控制循环次数
//内循环:实现冒泡
while (pHead != pTailNode)
{
pNode = pHead;
while (pNode->pNext != pTailNode)
{
pPreNode = pNode;
pNode = pNode->pNext;
if (pPreNode->data > pNode->data)
{
DataType tmp = preNode->data;
pPreNode->data = pNode->data;
pNode->data = tmp;
}
}
pTailNode = pNode;
}
}

//合并两个有序单链表,且新链表也为有序单链表-------尾插法
pSListNode MergeList(pSListNode pList1, pSListNode pList2 )
{
pSListNode pNewHead = NULL;
pSListNode pL1Node = pList1;
pSListNode pL2Node = pList2;
pSListNode pNode = NULL;
pSListNode pTailNode = NULL;
//判断两个单链表是否为空
if (pL1Node == NULL)
{
return pL2Node;
}
if (pL2Node == NULL)
{
return pL1Node;
}
//两个单链表都不为空时比较两个单链表中的结点数据的大小,找出较小值赋给新头结点,然后在新头结点后面进行尾插
if (pL1Node->data > pL2Node->data)
{
pNode = pL2Node;
pL2Node = pL2Node->pNext;
}
else
{
pNode = pL1Node;
pL1Node = pL1Node->pNext;
}
pNewHead = pNode;
pTailNode = pNode;
//判断两个链表中是否有一个链表结束
while (pL1Node && pL2Node)
{
if (pL1Node->data > pL2Node->data)
{
pNode = pL2Node;
pL2Node = pL2Node->pNext;
}
else
{
pNode = pL1Node;
pL1Node = pL1Node->pNext;
}
pTailNode->pNext = pNode;
pTailNode = pTailNode->pNext;
}
//链表L1结束,则将L2直接链在尾结点的下一个位置上。否则反之。
if (pL1Node == NULL)
{
pTailNode->pNext = pL2Node;
}
else
{
pTailNode->pNext = pL1Node;
}
return pNewHead;
}

//查找单链表中的倒数第k个结点,要求只能遍历一次链表

pSListNode FindLastkNode(pSListNode pHead, int k)
{
pSListNode pFast = pHead; //使用快慢指针,快指针先走k-1步之后,慢指针(走一步)再与快指针同时走,最后返回慢指针即是倒数第k个结点。
pSListNode pSlow = pHead;
if ((pHead == NULL) || (k <= 0))
{
return NULL;
}
//先让快指针走k-1步
while ((--k ) && pFast) //可改为while(k--),则下面的判断k则不需要了,且线面的while(pFast->pNext)也要对应改为while(pFast)。
{
pFast = pFast->pNext;
}

//判断k是否大于结点数目
if (k )
{
return NULL;
}
while (pFast->pNext)
{
pSlow = pSlow->pNext;
pFast = pFast->pNext->pNext;
}
return pSlow;
}

//判断一个链表是否带环?若带环,求环的长度。

//可以使用快慢指针,快指针走两步,慢指针走一步,若相遇,则带环。
int HasCycle(pSListNode pHead )
{
pSListNode pFast = pHead;
pSListNode pSlow = pHead;
if (pHead == NULL)
{
return -1;
}
while (pFast && pFast->pNext)
{
pFast = pFast->pNext->pNext;
pSlow = pSlow->pNext;
if (pSlow == pFast)
{
return 1;//有环
}
else
{
return -1;
}
}
}

//求环的长度

int GetCycleLen(pSListNode pMeetNode )
{
int len = 1;//千万注意
pSListNode pNode = pMeetNode;
if (pMeetNode == NULL)
{
return 0;
}
while (pNode->pNext != pMeetNode )
{
len++;
}
return len;
}
//求环的入口点
//定义两个指针,一个指针从pMeetNode结点开始走,一个指针从头结点开始走,两个指针相遇的地方就是环的入口点。

pSListNode FindEnterNode(pSListNode pHead, pSListNode pMeetNode )
{
pSListNode pNode1 = pHead;
pSListNode pNodeM = pMeetNode;
if ((pHead == NULL) || (pMeetNode == NULL)) //pMeetNode为空,即此单链表无环。
{
return NULL;
}
while (pNode1 != pNodeM) //两指针不相等时指针继续往下走,直到两指针相等跳出循环。
{
pNode1 = pNode1->pNext;
pNodeM = pNodeM->pNext;
}
return pNode1;
}

//判断两个指针是否相交?
//方法一:相交的情况有Y型和V型等情况,若两个链表相交,则必定有相等的结点,或是从某个结点之后的所有结点都相等,或是最后一个结点相等,所以我们只需判断两个链表的最后一个结点是否相等即可。
//方法二:我们可以先求出两个链表的长度,然后定义两个指针,长度较长的链表指针为p1,长度为L1,另一个链表指针为p2,长度为L2,先让p1走上L1-L2步,再让p1与p2同时往下走,若p1=p2,则两个链表相交。

//方法一实现:

int IsListCrose(pSListNode pL1 , pSListNode pL2)
{
pSListNode pL1Node = pL1;
pSListNode pL2Node = pL2;
if ((pL1 == NULL) || (pL2 == NULL))
{
return 0;
}
while (pL1Node->pNext != NULL)
{
pL1Node = pL1Node->pNext;
}
while (pL2Node->pNext != NULL)
{
pL2Node = pL2Node->pNext;
}
if (pL1Node == pL2Node)
{
return 1;
}
return 0;
}

//方法二实现:

int LengthList(pSListNode pHead )
{
pSListNode pNode = NULL;
int Len = 0;
pNode = pHead->pNext;
while (pNode != NULL)
{
Len++;
pNode = pNode->pNext;
}
return Len;
}

int IsListCrose(pSListNode P1 , pSListNode P2)
{
int L1 = 0;
int L2 = 0;
int K = 0;
pSListNode pL1Node = P1;
pSListNode pL2Node = P2;
if ((P1 == NULL) || (P2 == NULL))
{
return 0;
}
L1=LengthList( P1);
L2=LengthList( P2);
K = L1 - L2;
if (K>0)
{
while (K--)
{
pL1Node = pL1Node->pNext;
}
while (pL1Node)
{
pL1Node = pL1Node->pNext;
pL2Node = pL2Node->pNext;
}
if (pL1Node == pL2Node)
{
return 1;
}
}
else
{
while (K++)
{
pL2Node = pL2Node->pNext;
}
while (pL2Node)
{
pL1Node = pL1Node->pNext;
pL2Node = pL2Node->pNext;
}
if (pL1Node == pL2Node)
{
return 1;
}
}
return 0;
}

//若两个链表可能相交,求他们的交点。
//相交有三种情况:
//(1)两个链表都不带环。
//(2)两个链表都带环。分别是环外相交与环内相交。
//(3)只有一个带环。此种情况不需要管。
//相交时两个链表公共一个环。

//设M1与M2分别是两个链表与环的交点,若两个链表相交,则M1与M2都在环内。
int IsListCroseWithCycle(pSListNode pL1, pSListNode pL2)
{
pSListNode pL1MeetNode = NULL;
pSListNode pL2MeetNode = NULL;
pSListNode pL1Node = pL1;
pSListNode pL2Node = pL2;
if ((pL1 == NULL) || (pL2 == NULL))
{
return 0;
}
//判断两个链表是否带环
pL1MeetNode = HasCycle( pL1);
pL2MeetNode = HasCycle( pL2);
//两个链表都不带环。
if ((pL1MeetNode == NULL) && (pL2MeetNode == NULL))
{
while (pL1Node->pNext != NULL)
{
pL1Node = pL1Node->pNext;
}
while (pL2Node ->pNext != NULL)
{
pL2Node = pL2Node->pNext;
}
if (pL1Node == pL2Node)
{
return 1;
}
return 0;
}
//两个链表都带环。若两个链表的pL1MeetNode与pL2MeetNode都在环内,则相交。
if ((pL1MeetNode != NULL) && (pL2MeetNode != NULL))
{
//先让pL1MeetNode走上一圈,如果在某个地方pL1MeetNode等于pL2MeetNode,则两个链表相交。
pSListNode pNode = pL1MeetNode;
while (pNode->pNext != pL1MeetNode) //确保pL1MeetNode走完一圈。
{
if (pL2MeetNode == pNode) //判断pL1MeetNode是否等于pL2MeetNode
{
return 1;
}
pNode = pNode->pNext;
}
return 0;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息