链表是最基本的数据结构,凡是学计算机的必须的掌握的,在面试的时候经常被问到,关于链表的实现,百度一下就知道了。在此可以讨论一下与链表相关的练习题。
1、在单链表上插入一个元素,要求时间复杂度为O(1)
解答:一般情况在链表中插入一元素是在末尾插入的,这样需要从头遍历一次链表,找到末尾,时间为O(n)。要在O(1)时间插入一个新节点,可以考虑每次在头节点后面插入,即每次插入的节点成为链表的第一个节点。
2、给定一个链表,判断是否有环。
解答:这个是一个经典的问题了,思路也很简单,我们首先设置两个指针p1,p2同时指向链表的头部,然后p1每次向后走1步,p2每次向后走2步。如果有环,那么有一步会出现p1=p2,如果p2已经到达了尾结点,则无环。复杂度:时间:O(n),空间:O(1)
扩展:给定一个链表,找出环的入口位置。思路也是一样,用p1,p2指针。只是需要多做一步,那就是当p1=p2的时候,将p1重新指向链表的头结点,然后p1和p2都每次向后走一步,下一次p1=p2的结点就是环的入口。复杂度:时间:O(n),空间:O(1)
3、遍历单链表一次,找出链表中间节点
解答:定义两个指针p和q,初始都指向链表头节点。然后开始向后遍历,p每次移动2步,q移动一步,当p到达末尾的时候,p正好到达了中间位置。
4、单链表逆置,不允许额外分配存储空间,不允许递归,可以使用临时变量,执行时间为O(n)
解答:这个题目在面试笔试中经常碰到,基本思想上将指针逆置。如下图所示:
实现:
Node* reverse_list(Node *head){
Node *cur=head;
Node *pre = NULL;
Node *post = cur->next;
// Node *reverse_head = cur;
while(post){
cur->next = pre;
pre = cur;
cur = post;
post = post->next;
}
cur->next = pre;
// reverse_head = cur;
return cur;
}
扩展:链表翻转。给出一个链表和一个数k,比如,链表为1→2→3→4→5→6,k=2,则翻转后2→1→6→5→4→3,若k=3,翻转后3→2→1→6→5→4,若k=4,翻转后4→3→2→1→6→5,用程序实现。
实质是也是逆置,只不过是两个链表逆置后再串联起来。实现如下:
bool rotate_list(Node *head,int k,Node* &newhead){
if(k < 0)
return false;
else if(0 == k)
return true;
int len = 0;
Node *node=head;
while(node){
++len;
node = node->next;
}
if(k > len)
return false;
Node *one_end,*two_start;
node = head;
Node *post = node->next;
int n=k;
if(1 == n){
}else{
while(n > 1){// rotate sublist one
node->next = post->next;
post->next = head;
head = post;
post = node->next;
--n;
}
}
if(len-k <= 1){ // rotate sublist two
}else{
one_end = node;
node = post;
post = post->next;
two_start = node;
n = len-k;
while(n>1){
one_end->next = post;
node->next = post->next;
post->next = two_start;
two_start = post;
post = node->next;
--n;
}
}
newhead = head;
return true;
}
5、用一个单链表L实现一个栈,要求push和pop的操作时间为O(1)
解答:根据栈中元素先进后出的特点,可以在链表的头部进行插入和删除操作
6、用一个单链表L实现一个队列,要求enqueue和dequeue的操作时间为O(1)
解答:队列中的元素是先进先出,在单链表结构中增加一个尾指针,数据从尾部入队,从头
部入队。
7、给定两个链表(无环),判断是否有相交。
解答:首先明确一点,如果两个链表相交,那么从第一个交点开始到尾结点结束,所有的结点都是公共结点。所以,两个有公共结点而部分重合的链表,拓扑形状看起来像一个Y,而不可能像X。
这也就是说,如果两个链表相交,那么这两个链表的尾结点肯定是公共结点,如果尾结点不是公共结点,那么这两个链表肯定不相交。
所以我们可以如下操作:依次遍历两个链表,最后判断尾结点是否相同,如果相同,则相交,如果不相同,则不相交。复杂度:时间:O(m+n),空间:O(1)
或者一个链表的头结点指向另一个链表的尾节点,判断是否有环。
8、给定两个链表(无环),找到第一个公共节点。
解答:我们最容易想到的是从尾结点开始挨个向前比较,最后一个相同的就是第一个公共结点。(从后往前遍历)
但是单链表只能从前往后进行遍历,如果想要从后往前的话则需要先从前向后遍历一次,同时用栈来记录每一个结点,最后出栈,然后挨个对比,这样的确可行,但是却要额外付出O(m+n)的空间,时间复杂度O(mn)。(单链表+栈)
仔细想想,我们可以先分别遍历两个单链表,记录长度m和n(无妨假设m>n),然后先让长度为m的链表向后走(m-n)步,接着两个链表同时向后遍历,第一个相同的结点就是要求的第一个公共结点。复杂度:O(m+n)m,n分别为两个链表的长度;空间:O(1)
PS:另外还有一种巧妙的方法是把在一个链表尾部插入另一个链表,然后判断合成的新链表是否有环。环入口即为第一个公共点。
可参考: