> 文档中心 > 学会链表了还不赶紧刷题(力扣刷题笔记)

学会链表了还不赶紧刷题(力扣刷题笔记)


  个人主页:欢迎大家光临——>沙漠下的胡杨

  各位大帅哥,大漂亮

 如果觉得文章对自己有帮助

 可以一键三连支持博主

 你的每一分关心都是我坚持的动力

 

 ☄: 本期重点:链表的力扣刷题笔记

  希望大家每天都心情愉悦的学习工作。 


  目录    

1. 移除链表题目分析:

代码实现:

思路1:

思路2:

2. 反转链表题目理解:

代码实现:

思路1:

思路2:

3. 链表中间结点题目分析:

代码思想实现:

4. 链表倒数第k个结点题目分析:

代码实现:

5. 合并链表题目分析:

代码实现:

6.分割链表题目分析:

代码实现:


我们链表的章节已经结束啦,我们学会了多少呢?来刷题验证下吧!

203. 移除链表元素

1. 移除链表题目分析:

简单来说就是遍历链表然后删除其中值为 val 的节点,最后返回链表的头即可。

思路1:

我们可以创建一个新的链表,然后遍历原链表,把值不等于 val 的节点拿下来尾插,最后返回我们创建的链表。

思路2:

我们可以再原链表的基础上进行直接删除值为 val 的节点,然后返回原来的头就好啦。

在上面的思路下,我们可以使用头结点,也可以不使用头结点,实现方式随意,下面就简单实现。

代码实现:

思路1:

struct ListNode* removeElements(struct ListNode* head, int val){    struct ListNode* cur = head;    struct ListNode* Newhead = NULL;    Newhead = head = (struct ListNode*)malloc(sizeof(struct ListNode));    Newhead->next = NULL;    while(cur)    { if(cur->val != val) {     Newhead->next = cur;     Newhead = Newhead->next;      }  cur = cur->next;     }    Newhead->next = NULL;    struct ListNode* tmp = head->next;    free(head);    return tmp;}

1. 我们先创建一个 cur 遍历指针

2. 然后我们开辟一个链表,把head 和我们开辟的Newhead都赋值为开辟的链表,把开辟的链表的 next 置为NULL

3. 然后我们进行遍历,如果遇到节点的值不等于 val 那么我们就把节点拿下来尾插,更新Newhead,让 cur 向后走,如果节点的值等于 val ,那么我们直接跳过。

4 .我们要把Newhead的next置空,否则会有访问NULL问题。

5. 再用指针值保存要返回的结点,把开辟的空间free掉就好啦。

需要注意的是步骤 4 和 5 ,很容易忘记置NULL和 形成内存泄漏。 

 

思路2:

struct ListNode* removeElements(struct ListNode* head, int val){    struct ListNode* cur = head;    struct ListNode* prev = NULL;  while(cur)    { if(cur->val == val) {     if(cur == head)     {  head = cur->next;  free(cur);  cur = head;     }     else     {  prev->next = cur->next;  free(cur);  cur = prev->next;     } } else {     prev = cur;     cur = cur->next; }    }    return head;}

1. 我们创建一个指针 cur 和 prev,cur为了遍历,prev是为了删除结点后链接剩下的结点。

2. 遍历链表,分为2种情况,分别是头部需要删除,其他部位删除。

3.头部删除时,先用head 保存 cur的下一个结点,free掉cur指针,把cur赋值为head。

4. 其他部位删除,此时的prev不为NULL了,我们使用 prev->next 保存 cur->next,然后free掉 cur 结点,然后把cur赋值为prev->next。

5.如果cur->val 不等于 val 时,我们直接把 cur赋值给 prev,然后让cur向后就好了。

注意:我们要删除时,要考虑头部的删除,并且我们还要注意不要出现访问NULL的问题。 

206. 反转链表

2. 反转链表题目理解:

这个要看的话比较简单,只需要把链表指向反着来就好了。

 

思路1:

我们可以在原链表的基础上改变链表的指向就好了。

思路2:

我们可以把链表每个结点拿下来头插,然后把第一个节点置NULL。 

代码实现:

思路1:

struct ListNode* reverseList(struct ListNode* head){    if(head == NULL)    { return NULL;    }    struct ListNode* n1 = NULL;    struct ListNode* n2 = head;    struct ListNode* n3 = n2->next;    while(n2)    { n2->next = n1;   n1 = n2; n2 = n3; if(n3) {     n3 = n3->next; }    }    return n1;}

1. 我们创建3个指针,分别是n1 n2 n3,n1为空,n2为head,n3为n2->next,这样使三个指针相邻,我们可以方便迭代和转指向。

2. 接着我们就直接用 n2 遍历链表,转指向就是把 n2->next 指向n1。

3. 迭代,就是把n2 给 n1,n3 给 n2,n3等于n3->next,如果n3为空,那么n3就不动,移动剩下的n1 和 n2,一直到n2为NULL时,就结束。

4. 我们最后返回的是n1。

注意:我们考虑下极端情况,当链表为NULL时,我们操作就会访问NULL,所以链表为空时我们直接返回。

思路2:

struct ListNode* reverseList(struct ListNode* head){    struct ListNode* Newnode = NULL;    struct ListNode* cur = head;    while(cur)    { struct ListNode *tmp = cur->next; cur->next = Newnode; Newnode = cur; cur = tmp;    }    return Newnode;}

1. 我们创建两个指针,cur 为了遍历,Newnode为了插入。

2.  遍历链表,再创建一个结点保存cur的下一个结点。

3. 然后链接,把cur->next 赋值为 Newnode,把Newnode 赋值为 cur,最后把cur向后走。

4.最后返回Newnode就好了。

注意:我们第一次cur->next就把第一个节点赋值为NULL了,就不用做特殊处理了。剩下的就是一个个结点的链接。

876. 链表的中间结点

3. 链表中间结点题目分析:

这道题就是用一个思想,就是快慢指针的思想,必须要往快慢指针想才可以解出来。

快指针一次走两步,慢指针一次走一步,这样当快指针走到链表结尾时,慢指针的位置刚好是中间的结点,这道题重点不是代码了,而是实现的思路,以及为什么必须要一个指针走两步,一个指针走一步,其他方式实现不行吗? 

代码思想实现:

struct ListNode* middleNode(struct ListNode* head){    struct ListNode* fast = head;    struct ListNode* slow = head;    while(fast && fast->next)    { fast = fast->next->next; slow = slow->next;    }    return slow;}

定义快慢指针,然后让它们向后走,一直到快指针为空,或者快指针的 next 为空了停止,然后返回慢指针。

注意:我们写终止条件时,我们判断终止的条件是  快指针为空,或者快指针的 next 为空,但是我们写的是继续的条件,我们想的都是终止条件,写出来的都是继续条件,这点逻辑相反,需要注意。

 

为什么这样刚好能返回中间结点:

首先,一个指针走两步,一个指针走一步,当快的指针走完链表时,慢指针刚好才走到一半的位置,如果快指针走其他步数时,我们不能够追上,因为其他步数时,要么是快指针走完了链表,结果慢指针还走到一半,要么是快指针走完了,慢指针走过了中间结点,不能保证没次刚好都在中间结点。

剑指 Offer 22. 链表中倒数第k个节点

4. 链表倒数第k个结点题目分析:

这道题我们还是用快慢指针的思路来求解,首先我们可以让快指针先走 k-1 步,然后在让快慢指针一起走,当快指针的next为空时,返回慢指针就是倒数第k个节点。

代码实现:

struct ListNode* getKthFromEnd(struct ListNode* head, int k){    struct ListNode* slow = NULL;    struct ListNode* fast = NULL;    slow = fast = head; while(--k)    { if(fast->next == NULL) {     return NULL; } fast = fast->next;    }    while(fast->next)    { slow = slow->next; fast = fast->next;    }    return slow;}

1.首先我们创建快慢指针,然后先让快指针先走 k-1 步,循环条件为 --k,因为k-- 就是k次循环,而 --k 就是 k-1 次循环吗,我们要走 k-1 步,所以使用 --k 。

2. 如果在快指针先走的过程中,快指针为NULL,那么我们直接返回,说明这个链表没有倒数 k 个结点。

3. 最后就让快慢指针一起走,结束的条件为 fast->next 为空时我们就停止,因为我们上述为了方便,我们只走了 k-1 步,所以这里我们要少走一步,为的是和上面的对应起来。

注意事项:首先我们正常情况下,是直接让快指针走k步,然后让快指针走到NULL时停止,但是我们有些极端情况使得我们不能走k步,所以我们这里选择走k-1步,然后在最后少走一步,走到快指针的next为NULL时就停止。还有就是我们要判断这个链表有没有倒数第k个节点,就是在快指针先走时,走的每一步都判断快指针是否为NULL,如果为NULL,那么就证明,快指针还没有走k-1步时,链表到头了,就证明链表没有倒数第 k 个节点。

21. 合并两个有序链表

5. 合并链表题目分析:

 我们看图分析,首先两个链表都是升序,然后要求我们返回一个新的链表,并且也是升序的,所以我们就创建一个链表,然后同时遍历这两个链表,谁的值小,拿下来尾插,最后让剩余的链表链接到后面就好了。

代码实现:

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){    struct ListNode* head = NULL;    struct ListNode* tail = NULL;    head = tail = (struct ListNode*)malloc(sizeof(struct ListNode));    head->next = NULL;    while(list1 && list2)    { if(list1->val val) {     tail->next = list1;     tail = list1;     list1 = list1->next; } else {     tail->next = list2;     tail = list2;     list2 = list2->next; }    }    if(list1)    { tail->next = list1;    }    if(list2)    { tail->next = list2;    }    struct ListNode* next = head->next;    free(head);    return next;}

1. 首先我们创建链表,定义两个指针,分别指向头尾。

2. 接着我们同时遍历两个链表,循环结束的条件是两个链表有一个为空截止,继续的条件就是两个链表都不为空。

3. 遍历中比较值较小的拿下来尾插,让该链表向后移动。

4. 最后判断那个链表不为空,就链接到创建的链表的尾指针后面。

5. free掉头结点,防止内存泄漏。

 

注意事项:我们在循环条件处容易出错,还容易不考虑最后循环完的链表是否不为空,进行尾插,最后就是内存泄漏。

面试题 02.04. 分割链表

6.分割链表题目分析:

这道题简单来说就是把小于 x 的值放到 x 的前面,把大于 和 等于 x 的值放在后面,并且顺序不能打乱,比如 4 在 3 的前面,合并后也要 4 在 3的前面。所以我们采用两个链表,分别的插入小于 x 的和其他的元素,最后把两个链表合并一下。 

代码实现:

struct ListNode* partition(struct ListNode* head, int x){    struct ListNode* Minhead, *Mintail, *Maxhead, *Maxtail;    Minhead = Mintail = (struct ListNode*)malloc(sizeof(struct ListNode));    Maxhead = Maxtail = (struct ListNode*)malloc(sizeof(struct ListNode));    Maxhead->next = NULL;    Minhead->next = NULL;    struct ListNode* cur = head;    while (cur)    { if (cur->val next = cur;     Mintail = Mintail->next; } else {     Maxtail->next = cur;     Maxtail = Maxtail->next; } cur = cur->next;    }    Mintail->next = Maxhead->next;    Maxtail->next = NULL;    struct ListNode* Newhead = Minhead->next;    free(Minhead);    free(Maxhead);    return Newhead;}

1. 首先我们创建两个带头的链表,和一个遍历指针 cur。

2.然后遍历分别插入链表即可。

3. 我们把链表连接起来时,首先我们的链表是带头的,所以我们要跳过头节点,并且我们的Max链表的最后一个元素不一定为NULL,我们要手动置为NULL。

4.我们返回时,保存节点时也要跳过头指针,然后一个个free掉空间

注意:太多的节点不要使用错误啦。

今天到这里结束啦!

小胡杨祝大家六一快乐。

饲料网