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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM