C++关于链表操作的八个常见面试题

1、从链表的末尾添加节点

2、删除链表节点

3、链表中倒数第K个节点

4、反转链表

5、从尾到头打印链表

6、合并两个排序的链表

7、两个链表的第一个公共节点

8、判断两个链表是否有环相关问题

 

struct ListNode
{
 int m_data;
 ListNode *m_pNext;
};

一、从链表的末尾添加节点:

ListNode *AddToTail(ListNode**pHead, int data)
{

//创建新节点将数据保存下来
 ListNode *pNew = new ListNode(); 
 pNew->m_data = data;
 pNew->m_pNext = NULL;

//头节点为空,则指向新节点,组成有一个节点的链表
 if (*pHead == NULL)
 {
  *pHead = pNew;
 }
 else
 {

//链表有部分数据的的话,定义指向头节点的指针偏移到链表的尾部
  ListNode *pNode = *pHead;
  while (pNode->m_pNext != NULL)
  {
   pNode = pNode->m_pNext;
  }

//在链表的尾部连接上新的节点
  pNode->m_pNext = pNew;
 }
 return *pHead;  //返回整条链表
}

二、删除链表节点:

ListNode *RemoveNode(ListNode **pHead, int data)
{
 if (*pHead == NULL || pHead == NULL)
  return NULL;

 ListNode *pBeDelNode = NULL;
 if ((*pHead)->m_data == data)
 {
  pBeDelNode = *pHead;
  *pHead = (*pHead)->m_pNext;
 }
 else
 {
  ListNode *pNode = *pHead;
  while (pNode->m_pNext != NULL && pNode->m_pNext->m_data != data)
  {
   pNode = pNode->m_pNext;
  }

  if (pNode->m_pNext != NULL && pNode->m_pNext->m_data == data)
  {
   pBeDelNode = pNode->m_pNext;
   pNode->m_pNext = pNode->m_pNext->m_pNext;
  }
 }

 if (pBeDelNode->m_pNext != NULL)
 {
  delete pBeDelNode;
  pBeDelNode = NULL;
 }
}

三、找链表中倒数第K个节点

 

//为了能够只遍历一次就能找到倒数第k个节点,可以定义两个指针:
//(1)第一个指针从链表的头指针开始遍历向前走k - 1,第二个指针保持不动;
//(2)从第k步开始,第二个指针也开始从链表的头指针开始遍历;
//(3)由于两个指针的距离保持在k - 1,当第一个(走在前面的)指针到达链表的尾结点时,第二个指针(走在后面的)指针正好是倒数第k个结点。

ListNode *Find_K_Node(ListNode *pHead, int k)
{
 if (pHead == NULL || k <= 0)
  return NULL;
 ListNode *pAhead = pHead;
 ListNode *pBhend = pHead;
 int i = 0;
 for (int i = 0; i < (k - 1); i++)
 {
  if (pAhead->m_pNext == NULL)
   return NULL;
  pAhead = pAhead->m_pNext;
 }

  while (pAhead->m_pNext != NULL)
  {
   pAhead = pAhead->m_pNext;
   pBhend = pBhend->m_pNext;
  }
 return pBhend;
}

 

四、反转链表

//思路 这个博主讲的很清晰,这里不再复述,链接:https://www.cnblogs.com/GODYCA/archive/2012/12/27/2835185.html

1、迭代方法:

ListNode* ReverseNode(ListNode *pHead)
{
 if (pHead == NULL)
  return NULL ;
 if (pHead->m_data == NULL)
  return NULL ;
 
 ListNode *pCurrNode = pHead;
 ListNode *RetNode = NULL;
 while (pCurrNode != NULL)
 {
  ListNode *pTemNode = pCurrNode->m_pNext; //指向当前节点的下一节点
  pCurrNode->m_pNext = RetNode;           //当前节点指向前一个节点
  RetNode = pCurrNode;    //前一节点指向当前节点
  pCurrNode = pTemNode;   //当前节点指向下一节点
 }
 return RetNode;
}

2、递归方法

ListNode *ReverseNode2(ListNode* pNode)
{
 ListNode *pPerNode = pNode;
 if (pNode->m_pNext == NULL)
  return pNode;
 else
 {
  ReverseNode2(pNode->m_pNext);
  pNode->m_pNext->m_pNext = pNode;
  if (pNode == pPerNode)
   pPerNode->m_pNext = NULL;
 }
}

五、从尾到头打印链表

//思路:利用栈的特性,先进先出,后进后出。从头到尾部遍历链表保存到栈中,再从栈顶开始输出值

#include<stack>
void PrintListNode(ListNode *pNode)
{
 std::stack<ListNode*>Node;
 ListNode*pTemNode = pNode;
 while (pTemNode != NULL)
 {
  Node.push(pTemNode);
  pTemNode = pTemNode->m_pNext;
 }
 
 while (!Node.empty())
 {
  pTemNode = Node.top();
  cout << pTemNode->m_data << endl;
  Node.pop();
 }
}

六、合并两个排序的链表

//该算法与将两个有序的数组合并是同一个思想

ListNode *MergeNode(ListNode *pNodeA, ListNode*pNodeB)
{
 if (pNodeA == NULL || pNodeB == NULL)
  return NULL;

 ListNode *NewNode = new ListNode();
 ListNode *RetNode = NewNode;
 while (pNodeA != NULL && pNodeB != NULL)
 {
  if (pNodeA->m_data < pNodeB->m_data)
  {
   NewNode = pNodeA;
   pNodeA = pNodeA->m_pNext;
  }
  else
  {
   NewNode = pNodeB;
   pNodeB = pNodeB->m_pNext;
  }
  NewNode = NewNode->m_pNext;
 }

 //其中一个还有剩余字节的链表接在新链表的后面就可以了
 if (pNodeA != NULL)
  NewNode->m_pNext = pNodeA; 
 if (pNodeB != NULL)
  NewNode->m_pNext = pNodeB;

return NewNode;
}

七、两个链表的第一个公共节点

1、借助栈的特性,从尾部到头开始比较节点是否相同,相同则把栈顶弹出,接着比较下一节点直到找到最后一个公共节点。

2、不借助外部空间法:先分别遍历两个链表的长度, 找出长的链表,并计算长表链表比短的链表长是多少,然后将长链表走两个链表长度的差,然后再一起向后遍历找到相同的点。

int GetNodeLen(ListNode *pNode)
{
 int len = 0;
 ListNode *pTemNode = pNode;
 while (pTemNode!= NULL)
 {
  pTemNode = pTemNode->m_pNext;
  len++;
 }
 return len;
}

ListNode *FindFirstCommonNode(ListNode *pNodeA, ListNode *pNodeB)
{
 int lenA = GetNodeLen(pNodeA);
 int lenB = GetNodeLen(pNodeB);
 int diff = lenA - lenB;
 ListNode *NodeLong = NULL;
 ListNode *NodeShort = NULL;
 if (diff > 0)
 {
  NodeLong = pNodeA;
  NodeShort = pNodeB;
 }
 else
 {
  diff = lenB - lenA;
  NodeLong = pNodeB;
  NodeShort = pNodeA;
 }

 for (int i = 0; i < diff; i++)
  NodeLong = NodeLong->m_pNext;

 while (NodeLong != NULL && NodeShort != NULL && NodeLong->m_data != NodeShort->m_data)
 {
  NodeLong = NodeLong->m_pNext;
  NodeShort = NodeShort->m_pNext;
 }
 ListNode *commentNode = NodeLong;
 //ListNode *commentNode = NodeShort; //或
 return commentNode;
}

八、判断链表是否有环相关问题

1、判断是否又环

bool FindLoopNode(ListNode *pNode)
{
 if (pNode == NULL)
  return NULL;
 ListNode *pFast, *pSlow;
 pFast = pSlow = pNode;
 
 while (pSlow != NULL && pFast->m_pNext != NULL)
 {
  pSlow = pSlow->m_pNext;
  pFast = pFast->m_pNext->m_pNext;
  if (pSlow == pSlow)
   return true;
 }
 return false;
}

2、找到环的入口点 ,详细思路请见链接:https://www.cnblogs.com/dancingrain/p/3405197.html

 

ListNode* getLoopNode(ListNode *pNode)
{
 if (pNode == NULL)
  return NULL;
 ListNode *pFast, *pSlow;
 pFast = pSlow = pNode;

 

 while (pSlow != NULL && pFast->m_pNext != NULL)
 {
  pSlow = pSlow->m_pNext;
  pFast = pFast->m_pNext->m_pNext;
  if (pSlow == pSlow)
   break;
 }
 if (pSlow == NULL || pFast == NULL)
  return NULL;
 pSlow = pNode;
 while (pSlow != pFast)
 {
  pSlow = pSlow->m_pNext;
  pFast = pFast->m_pNext;
 }
 return pSlow;
}

 

转载于:https://www.cnblogs.com/kevinsharif/p/9216807.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值