代码随想录算法训练营第3天|链表理论基础、203.移除链表元素、707.设计链表、206.反转链表

链表理论基础

一、c语言链表节点定义

typedef struct ListNodeT {
    int val;
    struct ListNodeT next;
} ListNode;

二、其他

1.术语

singly-linked list 单链表
LinkList 链表
ListNode 链表节点

2.注意事项

①删除节点,需要释放(free)节点!


203.移除链表元素

题目链接:link
文章链接:link
视频链接:link

一、做题感受&第一想法

思路较为清晰。(单独处理头结点)
但是实现起来漏了一种情况(当该节点不是val也不是头结点时,直接让p和pre指向后一个节点),导致一开始超出时间限制,因为跳不出循环。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* removeElements(struct ListNode* head, int val) {
    struct ListNode* p = head, *pre = head;
    while(p != NULL){
        if( p == head && p->val == val ){
            p = p->next;
            free(head);
            head = p;
            pre = p;
        }
        else if( p == head ){
            p = p->next;
        }
        else if( p != head && p->val == val ){
            pre -> next = p -> next;
            free(p);
            p = pre -> next;
        }
        else if( p != head && p->val != val){ //一开始漏掉了这种情况
            p = p -> next;
            pre = pre -> next;
        }
    }
    return head;
}

二、学习文章后收获

1.删除单链表节点总体思路两种

(1)单独处理头结点
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        // 删除头结点
        while (head != NULL && head->val == val) { // 注意这里不是if
            ListNode* tmp = head;
            head = head->next;
            delete tmp;
        }

        // 删除非头结点
        ListNode* cur = head;
        while (cur != NULL && cur->next!= NULL) {
            if (cur->next->val == val) {
                ListNode* tmp = cur->next;
                cur->next = cur->next->next;
                delete tmp;
            } else {
                cur = cur->next;
            }
        }
        return head;
    }
};

用一个while循环单独处理头结点,直到头结点不等于val。
之后,用cur表示当前节点,检查cur->next是否等于val。如果相等则删除节点,否则cur向后移动检查后续节点。

(2)创建虚拟头结点
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
        dummyHead->next = head; // 将虚拟头结点指向head,这样方便后面做删除操作
        ListNode* cur = dummyHead;
        while (cur->next != NULL) {
            if(cur->next->val == val) {
                ListNode* tmp = cur->next;
                cur->next = cur->next->next;
                delete tmp;
            } else {
                cur = cur->next;
            }
        }
        head = dummyHead->next;
        delete dummyHead;
        return head;
    }
};

创建dummyNode作为虚拟头结点,不存储任何元素。
这样只需要cur即可,不用处理头结点。
注意:返回的是头节点,所以应该返回dummyNode->next!

三、过程中遇到的问题

Line 12: Char 14: runtime error: member access within null pointer of type ‘struct ListNode’ [solution.c]
报错原因:试图访问空指针。

707.设计链表

题目链接:link
文章讲解:link
视频讲解:link

一、做题感受&第一想法

使用虚拟头结点,同时头结点的val域可用于记录链表实际长度。


206.反转链表

题目链接:link
文章讲解:link
视频讲解:link

一、做题感受&第一想法

翻转链表:头插法建立一个新链表

struct ListNode* reverseList(struct ListNode* head) {
    struct ListNode* p = NULL, *q = head, *t = NULL;
    p->next = NULL; //p作为新链表的dummy head
    while(q != NULL){
        t = q;
        q = q->next;
        t->next = p->next;
        p->next = t;
    }
    return p->next;
}

②一开始想到了头插法,但是犯了一个错误:让节点不断头插回“原链表”,导致循环无法结束,超出时间限制。

二、学习文章后收获

1.翻转链表方法一:双指针法

struct ListNode* reverseList(struct ListNode* head) {
    struct ListNode *pre = NULL, *q = head, *t = NULL;
    while(q != NULL){
        t = q->next;
        q->next = pre;
        pre = q;
        q = t;
    }
    return pre;
}

2.翻转链表方法二:递归法(略难理解,暂时还没有完全理解吸收。)

class Solution {
public:
    ListNode* reverse(ListNode* pre,ListNode* cur){
        if(cur == NULL) return pre;
        ListNode* temp = cur->next;
        cur->next = pre;
        // 可以和双指针法的代码进行对比,如下递归的写法,其实就是做了这两步
        // pre = cur;
        // cur = temp;
        return reverse(cur,temp);
    }
    ListNode* reverseList(ListNode* head) {
        // 和双指针法初始化是一样的逻辑
        // ListNode* cur = head;
        // ListNode* pre = NULL;
        return reverse(NULL, head);
    }
};

3.很多情况下,有一个虚拟头结点都更为方便。

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值