本文首发于微信公众号:聊点技术,原文标题《 面试腾讯时遇到了"链表"的原题,so easy!》
找工作的过程中,不论是参加笔试还是面试,我们都会遇到大量和链表有关的题目。我在找实习的时候,经历的第一场面试是腾讯的电话面试,两道编程题目中有一道就是本文中提到的:复杂链表的复制。秋招期间,在经历了多次笔试和面试后,我把曾经遇到过的与链表有关的题目进行了分类总结,总结成此文期待能够对各位朋友有所帮助。
本文总结了在面试中遇到频率最高的与链表有关的题目,并使用了C++进行编码实现。
链表节点定义如下:
struct List{
int val;
struct List* next;
List(int x) : val(x), next(nullptr){}
};
1.从尾到头打印单链表
非递归算法:利用两个指针,指针cur指向链表尾部,指针tail指向链表头部。每当tail从头循环到尾部cur时,输出表尾的值。让表尾cur指向tail,再次循环。
void PrintTailToHead(List* head){ //非递归O(n^2)
List* cur = nullptr;
while(cur != head){
List* tail = head; //重新指向头节点
while(tail->next != cur){ //循环移动到尾
tail = tail->next;
}
cout<< tail->val << endl;
cur = tail; //更新尾节点
}
}
void TailToHead(List* head){ //递归算法 O(n)
if(head == nullptr){
return ;
}
TailToHead(head->next);
cout<< head->val << endl;
}
2. 删除单链表的节点
a. 删除给定单链表中的节点
若要删除的链表节点非尾
若要删除的节点位于链表的尾部,那么它就没有下一个节点——需要从链表头节点开始,顺序遍历得到该节点的前序节点,并完成删除操作
如果链表中只有一个节点,又要删除链表的头节点,那么在删除节点后,需要把链表头节点置空
void DeleteNode(List** head, List* pos){
if(head == nullptr || pos == nullptr){
return;
}
if(pos->next != nullptr){ /*要删除节点不是尾节点,采用向前替换法*/
List* next = nullptr;
next = pos->next;
pos->val = next->val;
pos->next = next->next;
delete next;
next = nullptr;
}
else if (*head == pos){ /* pos->next为空跳过第一个if. 在第二个if内判断 */
delete pos; /* pos->next为空,且pos和*head相同,此时只有一个节点*/
pos = nullptr;
*head= nullptr; /* 使用指向指针的指针的原因 */
}
else{ /* pos->next为空,且pos!=*head。此时要删除尾节点*/
List* p = *head;
while(p->next != pos){
p = p->next;
}
p->next = nullptr;
delete pos;
pos = nullptr;
}
}
b. 简化版:删除一个无头单链表的非尾节点。
//采用向前替换法
void ListDelNode(List* pos){
List* cur = nullptr;
cur = pos->next;
pos->val = cur->val;
pos->next = cur->next;
free(cur);
cur = nullptr;
}
c. 删除排序链表中的重复节点
从头遍历整个链表。如果当前节点的值与下一个节点的值相同,那么他们就是重复的节点,都可以被删除。为了保证被删除之后的链表仍然是相连的,要把当前节点的前一个节点(pPreNode)和后面值比当前节点的值大的节点相连。
void DeleteDuplication(ListNode** pHead){
//头结点也可能被删除,因此采用指向指针的指针
if (pHead == nullptr || *pHead == nullptr) return;
ListNode* pPreNode = nullptr;
ListNode* pNode = *pHead;
while (pNode != nullptr){
ListNode* pNext = pNode->next;
bool needDelete = false;
if (pNext != nullptr && pNext->val == pNode->val){
//next节点不为空且当前节点和next节点值相等
needDelete = true; //说明他们就是重复节点,都可以被删除
}
if (!needDelete){ //不是重复节点。指针后移
pPreNode = pNode;
pNode = pNode->next;
}
else{ //遇到重复节点
int value = pNode->val; //保存值,因后边会删除该节点
ListNode* ToBeDel = pNode;
while (ToBeDel != nullptr && ToBeDel->val == value){
//直至遇到值不等节点
pNext = ToBeDel->next;
delete ToB