这篇博客记录了链表和顺序表的一些常见面试题。
包含了以下题目:
// 从尾到头打印单链表
void PrintFromTailToFront(PNode pHead);
// 删除无头单链表的非尾结点,要求:不能遍历链表
void DeleteNotTailNode(PNode pos);
// 在无头单链表pos位置前插入值为结点data的结点
void InsertPosFront(PNode pos, DataType data);
// 用单链表模拟实现约瑟夫环
void JosephCircle(PNode* pHead, const int M);
// 使用冒泡排序方法对单链表进行排序
void BubbleSort(PNode pHead);
// 单链表的逆置--三个指针
void ReverseSList(PNode* pHead);
// 单链表的逆置--头插法
PNode ReverseSListOP(PNode pHead);
// 合并两个有序链表,合并之后依然有序
PNode MergeSList(PNode pHead1, PNode pHead2);
// 查找链表的中间结点,要求只能遍历一次链表
PNode FindMiddleNode(PNode pHead);
// 查找链表的倒数第K个结点,要求只能遍历链表一次
PNode FindLastKNode(PNode pHead, int K);
// 删除链表的倒数第K个结点,要求只能遍历链表一次
PNode DeleteLastKNode(PNode pHead, int K);
// 判断单链表是否相交?链表不带环
int IsCrossWithoutCircle(PNode pHead1, PNode pHead2);
// 求不带环单链表相交交点
PNode GetCrossNode(PNode pHead1, PNode pHead2);
// 判断链表是否带环
PNode IsCircle(PNode pHead);
// 求环的长度
int GetCircleLen(PNode pHead);
// 求环的入口点--注意推断过程
PNode GetEnterNode(PNode pHead, PNode pMeetNode);
// 判断链表是否带环,链表可能带环
int IsListCrossWithCircle(PNode pHead1, PNode pHead2);
//复杂链表
typedef struct CListNode
{
struct CListNode *next;
struct CListNode *Random;
DataType data;
}CListNode,*PCListNode;
// 复杂链表的复制
PCListNode CopyComplexList(PCListNode pHead);
//求两个已排序单链表中的相同数据
void UnionSet(PNode L1, PNode L2);
实现:
#include "PSList.h"
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
//从尾到头打印单链表
void PrintFromTailToFront(PNode pHead)
{
if (NULL == pHead)
return;
PNode pCur = pHead;
if (pCur)
{
PrintFromTailToFront(pCur->_next);
printf("%d ", pCur->_data);
}
}
// 删除链表的非尾结点,要求不能遍历链表
void DeleteNotTailNode(PNode pos)
{
if (NULL == pos||pos->_next==NULL)
return;
PNode pDelNode = pos->_next;
pos->_data = pDelNode->_data;
pos->_next = pDelNode->_next;
free(pDelNode);
}
// 在链表pos位置前插入值为data的结点,不能遍历链表
void InsertPosFront(PNode pos, DataType data)
{
if (NULL == pos)
return;
PNode PNewNode = BuySListNode(pos->_data);
PNewNode->_next=pos->_next;
pos->_next = PNewNode;
pos->_data = data;
}
// 约瑟夫环
void JosephCircle(PNode* pHead, const int M)
{
assert(pHead);
PNode pCur = *pHead;
if (pHead == NULL)
return;
while (pCur->_next != pCur)
{
//1.报数
int count = M;
while (--count)
pCur = pCur->_next;
//2.删除
PNode PDelNode = pCur->_next;
pCur->_data = PDelNode->_data;
pCur->_next = PDelNode->_next;
free(PDelNode);
}
*pHead = pCur;
}
// 使用冒泡方式对单链表进行排序
void BubbleSort(PNode pHead)
{
if (pHead == NULL || pHead->_next == NULL)
return;
int i = 0;
PNode pNext = NULL;
for (i = 0; i < SListSize(pHead); ++i)
{
int flag = 0;
PNode pCur = pHead;
pNext = pCur->_next;
int j = 0;
for (j = 0; j < SListSize(pHead)-1 - i; ++j)
{
if (pCur->_data > pNext->_data)
{
int temp = pCur->_data;
pCur->_data = pNext->_data;
pNext->_data = temp;
flag = 1;
}
pCur = pCur->_next;
pNext = pCur->_next;
}
if (flag == 0)
break;
}
}
// 单链表的逆序---三个指针
void ReverseSList(PNode* pHead)
{
assert(pHead);
if (NULL == *pHead || NULL == (*pHead)->_next)
return;
PNode pPreNode = NULL;
PNode pCurNode = *pHead;
PNode pNextNode = NULL;
while (pCurNode)
{
pNextNode = pCurNode->_next;
pCurNode->_next = pPreNode;
pPreNode = pCurNode;
pCurNode = pNextNode;
}
*pHead = pPreNode;
}
// 单链表的逆序---使用头插法
PNode ReverseSListOP(PNode pHead)
{
if (pHead == NULL || pHead->_next == NULL)
return pHead;
PNode pNewHead = NULL;
PNode pCurNode = pHead;
while (pCurNode)
{
PNode pNextNode = pCurNode->_next;
pCurNode->_next = pNewHead;
pNewHead = pCurNode;
pCurNode = pNextNode;
}
return pNewHead;
}
// 合并两个有序链表,合并起来依然要有序
PNode MergeSList(PNode pHead1, PNode pHead2)
{
if (NULL == pHead1 )
return pHead2;
if (NULL == pHead2)
return pHead1;
PNode pNewHead = NULL;
PNode pCur1 = pHead1;
PNode pCur2 = pHead2;
//确定第一个节点的值
if (pCur1->_data < pCur2->_data)
{
pNewHead = pCur1;
pCur1 = pCur1->_next;
}
else
{
pNewHead = pCur2;
pCur2 = pCur2->_next;
}
//向新链表中插入值
PNode pTail = pNewHead;
while (pCur1&&pCur2)
{
if (pCur1->_data < pCur2->_data)
{
pTail->_next = pCur1;
pCur1 = pCur1->_next;
}
else
{
pTail->_next = pCur2;
pCur2 = pCur2->_next;
}
pTail = pTail->_next;
}
//若一个链表有剩余,放在新链表后面
if (pCur1)
pTail->_next = pCur1;
else
pTail->_next = pCur2;
return pNewHead;
}
// 查找链表的中间结点,要求只能遍历一次链表
PNode FindMiddleNode(PNode pHead)
{
//1->2->3->4->5
//1->2->3->4->5->6
PNode pFast = pHead;
PNode pSlow = pHead;
PNode pPre = pHead;
while (pFast && pFast->_next)
{
pPre = pSlow;
pFast = pFast->_next->_next;
pSlow = pSlow->_next;
}
//求奇数个节点的中间节点前一个节点
/*if (NULL == pFast)
return pPre;*/
return pSlow;
}
// 查找链表的倒数第K个结点
PNode FindLastKNode(PNode pHead, int K)
{
if (NULL == pHead || K <= 0)
return NULL;
PNode pFast = pHead;
PNode pSlow = pHead;
int count = K;
//快的指针先走K步
while (count--)
{
if (NULL == pFast)
return NULL;
pFast = pFast->_next;
}
//然后快慢指针一起走,慢指针指向的就是倒数第K个节点
while (pFast)
{
pFast = pFast->_next;
pSlow = pSlow->_next;
}
return pSlow;
}
// 删除链表的倒数第K个结点,要求只能遍历链表一次
PNode DeleteLastKNode(PNode pHead, int K)
{
if (NULL == pHead || K <= 0)
return NULL;
PNode pFast = pHead;
PNode pSlow = pHead;
PNode pPre = NULL;
int count = K;
//快的指针先走K步
while (count--)
{
if (NULL == pFast)
return NULL;
pFast = pFast->_next;
}
//然后快慢指针一起走,慢指针指向的就是倒数第K个节点
while (pFast)
{
pPre = pSlow;
pFast = pFast->_next;
pSlow = pSlow->_next;
}
//删除
pPre->_next = pSlow->_next;
free(pSlow);
return pHead;
}
// 判断两个单链表是否相交---链表不带环
int IsCrossWithoutCircle(PNode pHead1, PNode pHead2)
{
if (NULL == pHead1 || NULL == pHead2)
return 0;
PNode pTail1 = pHead1;
PNode pTail2 = pHead2;
//找链表1和链表2的尾节点
while (pTail1)
pTail1 = pTail1->_next;
while (pTail2)
pTail2 = pTail2->_next;
//若尾节点相同,则俩链表相交
return pTail1 == pTail2;
}
// 如果相交 获取交点
PNode GetCrossNode(PNode pHead1, PNode pHead2)
{
int size1 = 0;
int size2 = 0;
PNode pCur1 = pHead1;
PNode pCur2 = pHead2;
if (!IsCrossWithoutCircle(pHead1, pHead2))
return NULL;
//两个链表相差的长度
while (pCur1)
{
size1++;
pCur1 = pCur1->_next;
}
while (pCur2)
{
size2++;
pCur2 = pCur2->_next;
}
int gap = size1 - size2;
pCur1 = pHead1;
pCur2 = pHead2;
//让长链表先走差值步
if (gap > 0)
{
while (gap--)
pCur1 = pCur1->_next;
}
else
{
while (gap++)
pCur2 = pCur2->_next;
}
//然后两个一起走直到相等,即为交点
while (pCur1 != pCur2)
{
pCur1 = pCur1->_next;
pCur2 = pCur2->_next;
}
return pCur1;
}
//链表是否带环
PNode IsCircle(PNode pHead)
{
PNode pFast = pHead;
PNode pSlow = pHead;
while (pFast&&pFast->_next)
{
pFast = pFast->_next->_next;
pSlow = pSlow->_next;
if (pFast == pSlow)
return pFast;
}
return NULL;
}
//求环的长度
int GetCircleLen(PNode pHead)
{
PNode pMeetNode = IsCircle(pHead);
PNode pCur = pMeetNode->_next;
int size = 1;
if (NULL == pMeetNode)
return 0;
while (pCur != pMeetNode)
{
pCur = pCur->_next;
size++;
}
return size;
}
//求环的入口点
PNode GetEnterNode(PNode pHead, PNode pMeetNode)
{
if (NULL == pHead || NULL == pMeetNode)
return NULL;
PNode pH = pHead;
PNode pM = pMeetNode;
while (pH != pM)
{
pH = pH->_next;
pM = pM->_next;
}
return pH;
}
// 判断链表是否相交,链表可能带环
int IsListCrossWithCircle(PNode pHead1, PNode pHead2)
{
if (NULL == pHead1 || NULL == pHead2)
return 0;
//1.判断两个链表是否带环
PNode pMeetNode1 = IsCircle(pHead1);
PNode pMeetNode2 = IsCircle(pHead2);
//2.两个链表都不带环
//求两个链表的最后一个元素,若最后一个元素相等则相交
if (NULL == pMeetNode1&&NULL == pMeetNode2)
{
PNode pTail1 = pHead1;
while (pTail1->_next)
pTail1 = pTail1->_next;
PNode pTail2 = pHead2;
while (pTail2->_next)
pTail2 = pTail2->_next;
if (pTail1 == pTail2)
return 1;
}
//3.两个链表都带环
else if (pMeetNode1&&pMeetNode2)
{
PNode pCur = pMeetNode1;
do
{
if (pCur == pMeetNode2)
return 2;
pCur = pCur->_next;
} while (pCur != pMeetNode1);
}
return 0;
}
// 复杂链表的复制
PCListNode CopyComplexList(PCListNode pHead)
{
//1.向链表的每个元素后面插入值相同的元素
PCListNode pOldNode = pHead;
PCListNode pNewNode = NULL;
while (pOldNode)
{
pNewNode = (PCListNode)malloc(sizeof(CListNode));
pNewNode->next = NULL;
pNewNode->Random = NULL;
pNewNode->data = pOldNode->data;
if (NULL == pNewNode)
return NULL;
pNewNode->next = pOldNode->next;
pOldNode->next = pNewNode;
pOldNode = pNewNode->next;
}
//2.改变链表的随机域
pOldNode = pHead;
while (pOldNode)
{
pNewNode = pOldNode->next;
if (pOldNode->Random == NULL)
pNewNode->Random = NULL;
else
pNewNode->Random = pOldNode->Random->next;
pOldNode = pNewNode->next;
}
//3.将新链表卸下来
pOldNode = pHead;
PCListNode pNewHead = pOldNode->next;
while (pOldNode->next)
{
pNewNode = pOldNode->next;
pOldNode->next = pNewNode->next;
pOldNode = pNewNode;
}
return pNewHead;
}
//求两个已排序单链表中的数据
void UnionSet(PNode L1, PNode L2)
{
if (NULL == L1 || NULL == L2)
return;
int data = 0;
while (L1&&L2)
{
if (L1->_data == L2->_data)
{
if(L1->_data!=data)
printf("%d ", L1->_data);
data = L1->_data;
L1 = L1->_next;
}
else if (L1->_data < L2->_data)
L1 = L1->_next;
else
L2 = L2->_next;
}
}
顺序表和链表的比较
(1)顺序表支持随机访问,单链表不支持随机访问
(2)顺序表插入/删除数据效率很低,时间复杂度为o(N),除尾插尾删,
单链表插入/删除效率高,时间复杂度为O(1),容易造成内存碎片
(3)顺序表的CPU高速缓存效率更高,单链表CPU高速缓存效率低