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

203.移除链表元素

题目链接:203. 移除链表元素 - 力扣(LeetCode)
文档讲解:代码随想录 (programmercarl.com)
视频讲解:手把手带你学会操作链表 | LeetCode:203.移除链表元素_哔哩哔哩_bilibili
解题思路:

//标准解法
struct ListNode* removeElements(struct ListNode* head, int val) {
    while (head != NULL && head -> val == val) {  
//判断头结点是否为目标值,是则删除
      struct ListNode *tmp = head; 
//创建临时节点储存需要回收的节点
      head = head -> next;  
//返回新的头结点
      free(tmp);  
//释放临时节点
    }
    struct ListNode *cur = head; 
 //创建用于遍历链表的结构体指针cur
    while (cur != NULL && cur -> next != NULL) { 
//判断头结点以及头结点的下一个节点是否为空
      if (cur -> next -> val == val) {  
//判断下一个节点是否需要删除的节点
        struct ListNode *tmp = cur -> next; 
 //创建临时节点储存需要回收的节点
        cur -> next = cur -> next -> next; 
 //删除该节点并让cur继续遍历
        free(tmp);  
//释放该节点
      } else {
        cur = cur -> next;
  //不是要删除的值则正常进行遍历
      }
    }
    return head;
}
//虚拟头结点法
struct ListNode* removeElements(struct ListNode* head, int val) {
    typedef struct ListNode* ListNode;  
//自定义结构体类型,方便后续使用
    ListNode dummyhead = (ListNode)malloc(sizeof(struct ListNode)); 
//为虚拟头结点分配内存空间
/*这里是sizeof(struct ListNode)而不是sizeof(ListNode),
因为我在上面自定义类型时是将一个结构体指针定义为ListNode,
直接sizeof(ListNode)的话会导致分配的内存不足,
这是我在写代码时遇到的错误*/
    dummyhead -> next = head;
 //把虚拟头结点赋值为该链表的头结点
    ListNode cur = dummyhead; 
//创建用于遍历链表的结构体指针cur
    while (cur && cur -> next) { 
 //确保该链表不为空
      if(cur -> next -> val == val) {
 //判断判断下一个节点是否需要删除的节点
        ListNode tmp = cur -> next; 
//创建临时节点储存需要回收的节点
        cur -> next = cur -> next -> next;  
//删除该节点并让cur继续遍历
        free(tmp);
      } else {
        cur = cur ->next;
      }
    }
    return dummyhead -> next;
}


时间复杂度:O(n)
该题总结:这题难度不大,主要是要理解虚拟头结点思想,这个思想对之后的链表理解很重要。

707.设计链表

题目链接:707. 设计链表 - 力扣(LeetCode)
文档讲解:代码随想录 (programmercarl.com)
视频讲解:帮你把链表操作学个通透!LeetCode:707.设计链表_哔哩哔哩_bilibili
解题思路:

//定义单链表
typedef struct MyLinkedList{
    int val;
    struct MyLinkedList *next;
} MyLinkedList;

//创建链表
MyLinkedList* myLinkedListCreate() {
    //记录链表的长度
    MyLinkedList *dummyhead =(MyLinkedList*)malloc(sizeof (MyLinkedList));
    //为虚拟头结点分配内存空间
    dummyhead -> next = NULL;
    return dummyhead;
}

int myLinkedListGet(MyLinkedList* obj, int index) {
    //此时obj就是虚拟头结点
    MyLinkedList *cur = obj -> next;
    //创建用于遍历的链表指针,该指针指向下标为index的节点
    for (int i = 0; cur; i++) {
        if (i == index) {
            return cur -> val;
        } else {
            cur = cur -> next;
        }
    }
    return -1;
}

void myLinkedListAddAtHead(MyLinkedList* obj, int val) {
    MyLinkedList *Newnode = (MyLinkedList*)malloc(sizeof (MyLinkedList));
    //创建新节点并分配内存空间
    Newnode -> val = val;
    //为Newnode节点赋值
    Newnode -> next = obj -> next;
    //先让Newnode的下一个节点指向虚拟头结点的下一个节点
    obj -> next = Newnode;
    //再让虚拟头结点的下一个节点指向Newnode,先后顺序很重要
}

void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
    MyLinkedList *cur = obj;
    //创建用于遍历的链表指针,该指针指向添加位置的上一个节点
    while (cur -> next) {
        //循环到链表的末尾
        cur = cur ->next;
    }
    MyLinkedList *Newnode = (MyLinkedList*)malloc(sizeof (MyLinkedList));
    //创建新节点
    Newnode -> val = val;
    Newnode -> next = NULL;
    //为Newnode节点赋值
    cur -> next = Newnode;
    //在末尾插入新节点
}

void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
    MyLinkedList *cur = obj;
    //创建用于遍历的链表指针,该指针指向添加位置的上一个节点
    MyLinkedList *Newnode = (MyLinkedList*)malloc(sizeof (MyLinkedList));
    //创建新节点
    Newnode -> val = val;
    //为Newnode节点赋值
    for (int i = 0; cur; i++) {
        if (index == i) {
            Newnode -> next = cur -> next;
            cur -> next = Newnode;
            return;
        } else {
            cur = cur -> next;
        }
    }
    //遍历到添加位置的上一个节点,并插入新节点
}

void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
    MyLinkedList *cur = obj;
    //创建用于遍历的链表指针,该指针指向添加位置的上一个节点
    for (int i = 0; cur -> next; i++) {
        if (index == i) {
            MyLinkedList *tmp = cur -> next;
            cur -> next = cur -> next -> next;
            free(tmp);
            return;
        } else {
            cur = cur -> next;
        }
    //遍历到需删除节点的上一个节点,创建临时节点用于后续的释放内存
    //并让需删除节点的上一个节点指向需删除节点的下一个节点
    } 
}

void myLinkedListFree(MyLinkedList* obj) {
    while (obj) {
        MyLinkedList *tmp = obj;
        obj = obj -> next;
        free(tmp);
    }
}


时间复杂度:涉及到index的为O(n),其余的为O(1)
该题总结:这题简直了,用C语言做出来跟卡尔讲的方法还是有一定区别的,因为没办法用size,涉及到index的函数就只能用for循环去遍历来确保index合法,而且每一个函数都要去思考怎么写,只能说一题更比六题强,做出来这题大概用了三个小时。

206.反转链表

题目链接:206. 反转链表 - 力扣(LeetCode)
文档讲解:代码随想录 (programmercarl.com)
视频讲解:帮你拿下反转链表 | LeetCode:206.反转链表 | 双指针法 | 递归法_哔哩哔哩_bilibili
解题思路:

//双指针法
struct ListNode* reverseList(struct ListNode* head) {
    struct ListNode *cur = head;
    //定义遍历链表的指针
    struct ListNode *pre = NULL;
    //指向cur的前一个元素的指针
    while (cur) {
        struct ListNode *tmp = cur -> next;
        //临时储存cur的后一个元素的指针
        cur -> next = pre;
        //翻转cur所指的元素的next的方向
        pre = cur;
        //pre指针向右移动一位
        cur = tmp;
        //cur指针向右移动一位
    }
    return pre;
}
//递归解法
struct ListNode* reverse(struct ListNode *cur, struct ListNode *pre) {
    if (!cur) {
        return pre;
    }
    //设置终止条件
    struct ListNode *tmp = cur -> next;
    //临时储存cur的下一个元素的指针
    cur -> next = pre;
    return reverse(tmp, cur);
}
struct ListNode* reverseList(struct ListNode* head) {
    return reverse(head, NULL);
}


时间复杂度:O(n)
该题总结:双指针法的又一种应用场景,真的很巧妙,而递归的方法我之前一直没有理解清楚什么是递归,这次通过双指针法来推导出递归法让我对递归的理解又更进了一步。

总用时:约5小时

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值