代码训练营-3day:移除链表元素、创建链表、翻转链表

一、基础知识

根据代码随想录上面介绍基础知识点,我们需要清楚的点:

1 链表和数组之间的差异;

2 链表结构体怎么定义;

3 链表的类型,一般使用较多的是单向链表,双向链表也需要了解。

4 链表的相关操作:插入 删除 搜索;

5 链表操作时候,一般head节点不作为数据存储单元,只是作为链表头存在,所以在设计时候,需要注意。

6  链表的相关题目中 ,头节点的插入或者删除,都需要特别注意。

链表和数组差异:

二、链表删除

给定一个链表 删除指定的value,设计到链表的两个操作方式:(根据代码随想录上面思路)

  • 直接使用原来的链表来进行删除操作。
  • 设置一个虚拟头结点在进行删除操作。

所以考虑时候,不使用虚拟头结点的话,就需要从头开始遍历,链表需要注意的是,头结点的value等于target,那么直接指向下一个节点,free掉当前的节点;

/**注意条件先后,head不为空,才能取值**/
while(head && head->val == val)

{
   ListNode* tmp = head;
   head = head->next;
   free(tmp);
}

/**如果head->val 一直都是target, 那么一直往下移动,直到不等于target value**/

然后head更新到第一个不等于target的节点,继续遍历,直到链表结束。

/**非头结点情况**/
listNode* cur = head;
while(cur && cur->next)
{

   if(cur->next->val == val)
   {
      //free
      listNode* tmp = cur->next;
      cur->next = cur->next->next;
      free(cur);
   }
   else
   {
      cur = cur->next;
   }
}

另外一个思路: 如果增加了虚拟头节点,那么从下一个节点开始遍历

listNode* pre = (listNode*)malloc(sizof(listNode));
pre->next = head;
listNode* cur = pre;
while(cur && cur->next)
{
    if(cur->next->val == val)
    {
        listNode* tmp = cur->next;
        cur->next = cur->next->next;
        free(tmp);
    }
    else
    {
      cur = cur->next;
    }
}

return pre->next;

三、创建链表

链表创建过程,遇到的问题感觉特别多,需要注意几个方面:

1 head作为头节点,不参与存储;!!!特别重要

2 插入和删除时候,头部处理

3 插入是在index之前插入,且是0 base;

首先,创建链表,申请一个头节点。

其次,头部插入,每次申请一个新节点,作为新的头结点;

然后,尾部插入,找到最末端节点,申请新节点,插入末端,并指向NULL;

然后,获取index位置节点value,需要考虑的是,index是否会超界,如果超了,return -1;

int myLinkedListGet(MyLinkedList* obj, int index) {
    //head除去开始 base0
    MyLinkedList* cur = obj->next;
    for(int i = 0; i < index; i++)
    {  
         
        if(cur == NULL)//一旦cur为空,说明index是超过了链表的长度的
        {
           return -1; 
        }
       cur = cur->next;
    }
    /**需要判断,刚好index次后,cur=NULL退出**/
    return cur==NULL ? -1: cur->val;

    /**下面是代码随想录的写法:**/
    // MyLinkedList *cur = obj->next;
    // for (int i = 0; cur != NULL; i++){
    //     if (i == index){
    //         return cur->val;
    //     }
    //     else{
    //         cur = cur->next;
    //     }
    // }
    // return -1;
}

然后,插入value到index位置,需要判断头结点位置,以及删除index位置的元素:

void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
    /**添加到index之前位置**/
    //indx == 0 添加到头
    if(index == 0)
    {
        myLinkedListAddAtHead(obj, val); 
        return;
    }
    //index >0 
    MyLinkedList* cur  = obj->next;
    for(int i = 1; cur != NULL; i++)
    {
        if(index == i)
        {
            MyLinkedList* newnode = (MyLinkedList*)malloc(sizeof(MyLinkedList));
            newnode->next = cur->next;
            cur->next = newnode;
            newnode->val = val;
            return;
        }
        else
        {
            cur = cur->next;
        }
    }
}

void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
    /**删除 idx = 0,头结点都没有参与存储**/
    if(index == 0)
    {
        MyLinkedList *tmp = obj->next;
        if (tmp != NULL){
            obj->next = tmp->next;
            free(tmp);     
        }
        return;
    }
    MyLinkedList* cur = obj->next;
    for(int i = 1; cur && cur->next; i++)
    {
        if(index == i)
        {
            MyLinkedList* tmp = cur->next;
            if(tmp)
            {
                cur->next = tmp->next;
                free(tmp);
            }
            return;
        }
        else
        {
            cur = cur->next;
        }
    } 
}

三、翻转链表

翻转链表,首先想的是用另外一个链表保存,然后再输出,类似于栈的处理;

另外,双指针法也可以解决,需要虚拟一个节点,pre和cur两个,每一次遍历,翻转一下:


struct ListNode* reverseList(struct ListNode* head) {
    //双指针法
    // struct ListNode* pre = NULL;
    // struct ListNode* cur = head;
    // //遍历
    // while(cur)
    // {
    //     struct ListNode* tmp = cur->next;
    //     cur->next = pre;
    //     //pre->next = NULL;
    //     pre = cur;
    //     cur = tmp;
    // }
    // return pre;

    //递归
    //1 边界条件:
    if(head == NULL) return NULL;
    if(head->next == NULL) return head;

    struct ListNode * end = reverseList(head->next);//找到递归的最后一层 往前推

    head->next->next = head;
    head->next = NULL;
    return end; 
}

还有就是使用递归,递归不太好理解,这样去思考的:找到递归的最后一层,往前推(迭代是正向推),递归的最后一层的处理,调用完成后,后面回复函数现场时候,所有的函数处理都是这样的操作。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值