面试题链表总结
面试题链表总结
链表定义为:
struct Node
{
Node():next(nullptr) {};
Node(int val): value(val), next(nullptr){};
int value;
node *next;
}
规定错误处理方式:
- 如果返回值为指针,返回nullptr
- 如果返回值为数字,返回为 -1
O(1)删除链表中节点
题目描述: 给出该节点的指针,在链表中删除这个节点
分析:O(1)的复杂度要求, 直接排除遍历的这种情况。只能从自身需求突破,很容易想到,自身删除不易,但删除下一个节点却很容易,所以解法就出来了。(当该节点为最后一个节点的时候,没法办只能遍历了)
代码如下:
void delNode(Node *head, Node *node)
{
if(node->next)
{
Node *tmp = node->next;
node->value = tmp->value;
node->next = tmp->next;
delete(tmp);
}
else
{
Node *tmp = head;
while(tmp->next != node)
tmp = tmp->next;
tmp->next = nullptr;
delete(node);
}
}
翻转单链表
题目描述: 给出链表的头指针,将该链表翻转
分析:这个题目要求是不能申请额外的数组,所以只需要申请两个临时指针,一个记录下一个节点的位置,一个对当前节点进行操作。
代码如下:
Node* reverseList(Node *head)
{
if(head == nullptr)
return nullptr;
Node *tmp = head->next;
head->next = nullptr;
Node *res;
while(tmp)
{
res = tmp->next;
tmp->next = head;
head = tmp;
tmp = res;
}
return head;
}
链表的倒数第K个
题目描述: 给出链表的头指针,返回链表倒数第K个节点的值
分析:首先想到的是递归到底部,然后向上回溯K-1层即可,这样的做法链表将遍历两次。如果要求只能一次遍历呢?有没有好的方法?面试官这样问的话,好的方法是必然存在的。用两个值,来记录走的位置;首先第一个值走K-1部,那么此时第一个的位置应该是链表的第K个节点。然后两个节点一块进行移动,这样第一个节点到达最后一个节点时,第二个节点的位置,即为倒数第K个节点。
代码如下:
int getLastK(Node *head, int k)
{
if(k == 0)
return -1;
int e = 1;
Node* b = head;
while(e < k && b)
{
b = b->next;
e ++;
}
if(e < k)
return -1;
while(b->next)
{
b = b->next;
head = head->next;
}
return b->value;
}
链表的交点
题目描述:给出两个链表的头指针,返回两个链表相交的节点
分析:两个链表相交类似‘Y’字形;由于两个链表长度不定,所以很难找到相交的位置。所以首先想到,跑出两个链表的长度,然后在根据两个链表的长度差,在跑一下即可找出答案。此时还有一种思路就是两个链表的指针同时走,一个链表的头指针跑到尾部之后再从另一个链表的头部开始跑,最后两个链表会汇到相交的地方。
代码如下:
//一
Node* Intersect(Node* head_x, Node* head_y)
{
if(head_x == nullptr || head_y == nullptr)
return nullptr;
auto fun = [](Node *x)->int{
int res = 0;
while(x)
{
x = x->next;
res ++;
}
return res;
};
int x = fun(head_x);
int y = fun(head_y);
if(x < y)
{
while(y != x)
{
head_y = head_y->next;
y --;
}
}
else
{
while(y != x)
{
head_x = head_x->next;
x --;
}
}
//检查没有交点的情况
while(head_x != head_y && head_x)
{
head_x = head_x->next;
head_y = head_y->next;
}
return head_x;
}
//二
Node* Intersect(Node* head_x, Node* head_y)
{
if(head_x == nullptr || head_y == nullptr)
return nullptr;
Node *a = head_x, *b = head_y;
//x,y判断没有交点的情况
bool x = false, y = false;
while(a != b)
{
if(a->next)
a = a->next;
else if(x == false)
{
x = true;
a = head_y;
}
else
break;
if(b->next)
b = b->next;
else if(y == false)
{
y = true;
b = head_x;
}
else
break;
}
if(a != b)
return nullptr;
return a;
}
链表环的入口点
题目描述: 给出链表的头指针,返回链表中环的入口点
分析:刚拿到这个题目,如果之前没有做过这种题目,很难想到好的解决方案(大佬不算);
首先引入fast、slow指针,fast指针每次走两步,slow指针每次走一步,那么他们之间的间隔随着步数的增加而增加(每次加一),如果存在环,那么这两个指针必然会在某个时刻相遇(他们之间的间隔为环大小的整数倍)。记录一下这个相遇点(在环内),然后接着走,再次回到这个相遇点就能得到环的长度; 然后还是用两个指针,一个指针先走环的长度的长度,然后两个指针同时走, 第一次相遇的地方即为环的入口
代码如下:
Node* getRingGate(Node *head)
{
if(head == nullptr || head->next == nullptr)
return nullptr;
Node *fast=head->next->nxet, *slow = head->next;
while(fast != slow)
{
slow = slow->next;
if(fast && fast->next)
fast = fase->next->next;
else
return nullptr;
}
Node *tmp = slow;
slow = slow->next;
int size = 1;
while(slow != tmp)
{
slow = slow->next;
size ++;
}
fast = slow = head;
while(size)
{
fast = fast->next;
size --;
}
while(fast != slow)
{
fast = fast->next;
slow = slow->next;
}
return fast;
}