2016-07-15更新
160.Intersection of Two Linked Lists
Write a program to find the node at which the intersection of two singly linked lists begins.
For example, the following two linked lists:
begin to intersect at node c1.
Notes:
If the two linked lists have no intersection at all, return null.
The linked lists must retain their original structure after the function returns.
You may assume there are no cycles anywhere in the entire linked structure.
Your code should preferably run in O(n) time and use only O(1)memory.
题目大意是求两个相交单链表的交点,最初我想到的方法是用两个指针从两个链表的首尾开始扫描,扫描到第一个相等的点就是交点。不过后来发现此法并不可行,在参考了他人的解法之后,归纳出了下面三种解法:
- 暴力破解法
- 哈希表法
- 双指针法
第一种方法的思路是:对于表a中的每一个元素,寻找b中是否有一个元素与之对应。时间复杂度是O(mn),超出了题目要求的O(n).
第二种方法的思路是:将a中的元素存入一个Hash Table,然后遍历b中的元素,如果b中某一个元素出现在了Hash Table中,则该元素为交点。时间复杂度 O(m+n),空间复杂度O(m)或O(n).超出了题目要求。
第三种方法最符合题目要求:首先算出a,b两链表的长度,然后计算长度之差的绝对值delta,之后较长的链表上的工作指针pmove先向前移动delta个节点。之后当pmove没有到表尾时,两个链表的工作指针同时向后移动,当两工作指针指向的值相等时,说明找到了交点;否则继续向后,如果依然找不到交点,返回NULL.时间复杂度O(m+n),空间复杂度O(1).
代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
int getLen(struct ListNode * headA){
int length=0;
struct ListNode * pmove=headA;
while(pmove){
length++;
pmove=pmove->next;
}
return length;
}
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode * pmove=headA;
struct ListNode * qmove=headB;
int lenA=getLen(pmove);
int lenB=getLen(qmove);
int delta=0;
if(!pmove||!qmove)
return NULL;
if(lenA>lenB){
delta=lenA-lenB;
while(delta){
pmove=pmove->next;
delta--;
}
}
else{
delta=lenB-lenA;
while(delta){
qmove=qmove->next;
delta--;
}
}
while(pmove){
if(pmove->val==qmove->val)
return pmove;
else{
pmove=pmove->next;
qmove=qmove->next;
}
}
return NULL;
}
203.Remove Linked List Elements
Remove all elements from a linked list of integers that have value val.
Example
Given: 1 –> 2 –> 6 –> 3 –> 4 –> 5 –> 6, val = 6
Return: 1 –> 2 –> 3 –> 4 –> 5
题目大意是删除链表中与给定值相等的结点,题目看似简单,然而在实现过程中还是遇到了不少问题.由于要对链表进行修改,所以声明的工作指针pmove应该是ListNode ** pmove;也就是说pmove应该是一个节点的指针的指针.只要注意了这一点,就很容易处理了,代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val) {
struct ListNode **pmove=&head;
struct ListNode *qmove=NULL;
if(!head)
return NULL;
else{
while(*pmove){
qmove=(*pmove);
if((*pmove)->val==val){
*pmove=qmove->next;
free(qmove);
}
else
pmove=&qmove->next;
}
}
return head;
}
206.Reverse Linked List
Reverse a singly linked list.
Hint:
A linked list can be reversed either iteratively or recursively. Could you implement both?
经典问题,链表逆置.有递归和非递归两种办法,首先看非递归算法,就是将单链表的节点的指针反向即可,之前的博文中也有记录,就不再赘述了。
代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head) {
if(!head)
return NULL;
else{
struct ListNode * current=head;
struct ListNode * pnext=current->next;
struct ListNode * prev=NULL;
current->next=NULL;
while(pnext){
prev=pnext->next;
pnext->next=current;
current=pnext;
pnext=prev;
}
head=current;
return head;
}
}
下面介绍递归解法:
假设有这样一个链表,a->b->c->d->e->f,现在用递归的方式将其逆置。那么我们从简单的情况开始考虑,如果是空链表,那么直接返回NULL即可;如果不是空链表,那么我们假设从b开始,之后的链表已经被逆置,也就是这样的状态:a->b<-c<-d<-e<-f,那么如何让b指向a的指针反向呢?
很简单,a->next->next=a.
这样,我们就得到了递归解法的代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head) {
if(!head)
return head;
struct ListNode * p=reverselist(head->next);/*递归部分*/
head->next-next=head;/*迭代部分*/
head->next=NULL;
return p;
}
这段代码的核心是迭代部分,用来反向指针,递归部分只是用于遍历链表。
234.Palindrome Linked List
Given a singly linked list, determine if it is a palindrome.
Follow up:
Could you do it in O(n) time and O(1) space?
题目大意:判断某一链表是否为回文链表.
思路如下,快慢指针法找到链表中点,然后把中点后的所有节点(不包括中点)逆序,将其与中点前的一半进行比较,相同则为回文;
代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode * reverselist(struct ListNode * head){
if(!head)
return;
struct ListNode * current=head;
struct ListNode * pnext=current->next;
struct ListNode * prev=NULL;
current->next=NULL;
while(pnext){
prev=pnext->next;
pnext->next=current;
current=pnext;
pnext=prev;
}
head=current;
return head;
}
bool isPalindrome(struct ListNode* head) {
if(!head||!head->next)
return true;
struct ListNode * fast=head;
struct ListNode * slow=head;
while(fast->next&&fast->next->next){
fast=fast->next->next;
slow=slow->next;
}
slow->next=reverselist(slow->next);
slow=slow->next;
while(slow){
if(head->val!=slow->val)
return false;
head=head->next;
slow=slow->next;
}
return true;
}
237.Delete Node in a Linked List
Write a function to delete a node (except the tail) in a singly linked list, given only access to that node.
Supposed the linked list is 1 -> 2 -> 3 -> 4 and you are given the third node with value 3, the linked list should become 1 -> 2 -> 4 after calling your function.
题目大意:给出一个指向链表中某结点的指针,头指针没有给出,要求删除该节点.
之前的博文中有一道类似的的题目,由于只知道指向该节点的指针,所以不能用经典的单链表删除的方法来解决这个题目.但我们可以用后值覆盖前值,然后释放这块空间。
代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
void deleteNode(struct ListNode* node) {
if(!node)
return;
else{
struct ListNode* tmp;
tmp=node->next;
node->val=tmp->val;
node->next=tmp->next;
}
}
第一版的代码用了4ms,但是代码比较繁琐,后来参考了讨论区的答案,写出了第二版代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
void deleteNode(struct ListNode* node) {
struct ListNode* next = node->next;
*node = *next;
free(next);
}