目录
[leetcode] 203.移除链表元素
首先分析思路:
第一种思路是用一前一后两个指针同时往后走,为什么呢,因为你删除某个结点需要前一个结点找到它的下一个结点,这就意味着,你需要找到要去除的这个结点的前一个结点,所以需要两个相邻的指针同时向后走,当前一个指针cur走到要删除的结点时,让prev指针先指向cur的next,然后再让cur指向prev的next,继续往后走,直到cur为NULL。
ok,根据这个思路我们写一下代码:
ListNode* removeElements(ListNode* head, int val) {
struct ListNode* prev = NULL;
struct linked* cur = head;
while(cur)
{
if(cur->val!=val)
{
prev=cur;
cur=cur->next;
}
else
{
prev->next = cur->next;
free(cur);
cur=prev->next;
}
}
return head;
}
接下就需要我们分析一下特殊情况,看上述代码是否可以满足:
1.如果val都为x,要删除的val也是x呢?也就是最后会删空呢?
我们发现代码走到else的第一句就出问题了,因为此时prev还是空指针,没有next,相当于头删
所以我们需要对这种情况单独处理:
ok,这样这个题就解决了
ListNode* removeElements(ListNode* head, int val) {
struct ListNode* prev = NULL;
struct linked* cur = head;
while(cur)
{
if(cur->val!=val)
{
prev=cur;
cur=cur->next;
}
else
{
if(prev==NULL)
{
head=cur->next;
free(cur);
cur=head;
}
else
{
prev->next = cur->next;
free(cur);
cur=prev->next;
}
}
}
return head;
}
那么还有第二种思路是:把不是val的值,尾插到新链表
定义一个头指针newhead用于最后返回新链表的头,再定义一个尾指针tail时刻指向新链表的尾:
struct ListNode* removeElements(struct ListNode* head, int val){
struct ListNode* newhead = NULL,*tail = NULL;
struct ListNode* cur=head;
while(cur)
{
if(cur->val!=val)
{
//尾插
if(tail==NULL)
{
newhead=tail=cur;
}
else
{
tail->next=cur;
tail=tail->next;
}
cur=cur->next;
}
else
{
struct ListNode* next=cur->next;
free(cur);
cur=next;
}
}
if(tail)
tail->next=NULL;
return newhead;
}
[leetcode]206.反转链表
首先分析思路:
首先我们考虑能不能让这个链表反转过来,下一个结点指向上一个结点,那么怎么实现呢?用一个双指针,一前一后,走在前面的指针指向后边的指针,,但我们容易发现这样的话,前面的指针就不再指向原来的下一个结点了,导致我们找不到下一个结点,那么怎么办呢,我们再用第三个指针指向下一个结点就好了,那么这道题就转换成了三指针问题。
如图,当n2为NULL时结束
struct ListNode* reverseList(struct ListNode* head){
if(head==NULL)
return NULL;
struct ListNode* n1,*n2,*n3;
n1=NULL;
n2=head;
n3=n2->next;
while(n2)
{
n2->next=n1;
n1=n2;
n2=n3;
if(n3)
n3=n3->next;
}
return n1;
}
由图可知,结束时要对n3进行非空判断,同时也需要对空链表进行特殊处理。
还有一种思路是取原结点头插到新链表,我们需要先保存下一个结点,然后把当前结点头插到新链表中,然后对下一个结点进行同样的操作,直到全部头插完,返回新链表的头。
struct ListNode* reverseList(struct ListNode* head){
struct ListNode* cur = head,*newhead=NULL;
while(cur)
{
struct ListNode*next=cur->next;
cur->next=newhead;
newhead=cur;
cur=next;
}
return newhead;
}
[leetcode]876.链表的中间结点
首先分析一下思路:
这里给出一种常用的方法:快慢指针(slow/fast),这个方法适用于很多题 ,什么意思呢,我们让两个指针从头同时开始走,slow指针一次走一个,fast指针一次走两个,这样当fast走到尾时,slow的位置就是中间结点。
这里需要考虑的时奇数偶数问题,奇数时fast走到尾;偶数时fast走到NULL
ok,我们代码实现一下:
struct ListNode* middleNode(struct ListNode* head){
struct ListNode* slow ,*fast;
slow=fast=head;
while(fast && fast->next)
{
slow=slow->next;
fast=fast->next->next;
}
return slow;
}
这道题很简单,这里我们主要是引出快慢指针的方法,后面还会用到这种方法哟~
[牛客]链表中倒数第k个结点
ok我们还是先分析思路:首先它要求输出倒数第k个结点,那么我们很容易想到的思路是可以先求出链表的长度n,然后输出第n-k个结点就可以了;但是嘞,我想说的是我们上一道题说的快慢指针的方法,只是这里的快慢指针的快慢体现在距离上,我们可以先让快指针走k步,然后和慢指针同时走,当快指针走到NULL时,慢指针的位置就是要找的倒数第k个结点。
struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
if(pListHead==NULL)
return NULL;
struct ListNode*slow,*fast;
slow=fast=pListHead;
while(k--)
{
if(fast==NULL)
return NULL;
fast=fast->next;
}
while(fast)
{
slow=slow->next;
fast=fast->next;
}
return slow;
}
总结
这篇文章只是挑选了一部分简单的经典链表oj题,难度不大,重点在于感受其中的思路,我们尤其注意在很多题中都有出现需要特殊处理的情况,这就要求我们思维全面,对链表足够熟悉 ,ok下一篇文章依然是一些链表oj题,但是难度是呈不断上升趋势,所以,感兴趣的老铁点个关注,我们下一篇再见~