“捡到一束光,日落时还给太阳”

最近笔者在初学数据结构时遇到几道题目,做完后有些吃力,很多经验还没有总结,笔者打算针对这几道题目进行系统性的总结。由于笔者是个菜鸟,思路还没有那么熟练清晰,后续还会加以完善,大佬轻喷。另外欢迎各位大佬指出不足。

1.移除元素

题目来源:https://leetcode.cn/problems/remove-element/description/

题目原文:

在这里插入图片描述

题意解析:

给定一个数组和一个数值,要求删除数组中等于该数值的元素,并返回删除后数组的新长度。
注意:不能创建新数组来操作,以及删除后数组里的元素可以任意位置排序。

个人思路:

首先最开始的思路是创建一个新数组,然后遍历原数组,将不为删除的值的元素放到新数组中。但是题目要求不能创建新数组,所以这种思路在本题不适用。
第二种思路是在原数组中进行操作遍历删除。此方法也叫双指针法。
(1)创建两个指针src、dist,它们都指向第一个元素
(2)src遍历原数组,若元素 = 删除的值,src++
若元素 != 删除的值,nums[dist] = nums[src],dist++,src++
(3)src遍历完数组,循环结束,此时数组为新的数组。

还需要注意一点的是本题目要求返回新数组的长度,需要返回一个整形,所以我们使用数组下标的形式来写代码,本质上无疑异,方便做题。

个人代码:

int removeElement(int* nums, int numsSize, int val) 
{
    int src,dst;
    src = dst = 0;//初始化第一个元素下标为0
    while(src < numsSize)
    {
        if(nums[src] == val)
        {
            src++;
        }
        else
        {
            nums[dst] = nums[src];
            src++;
            dst++;
        }
    }
    return dst;//此时下标dst就是新数组的长度
}

2.合并两个有序数组

题目来源:https://leetcode.cn/problems/merge-sorted-array/description/

题目原文:

在这里插入图片描述

题意解析:

给定两个递增的数组,然后合并两个数组,要求新数组中元素也是递增的。

个人思路:

第一开始的思路是先把两个元素放到一个数组中去,然后在一个数组中进行冒泡排序。
但此方法代码量大,程序效率低。

第二种思路是比大小,两个数组中的元素比大小,从后往前比大小,元素大的往后放。在这个方法中不要遗漏了关键点,就是比大小的时候,两个数组都是递增的。

个人代码:

	 int l1 = m - 1;
     int l2 = n - 1;
     int l3 = m + n - 1;
     while(l1 >= 0 && l2 >= 0)
     {
         if(nums1[l1] > nums2[l2])
         {
             nums1[l3--] = nums1[l1--];
         }
         else
         {
             nums1[l3--] = nums2[l2--];
         }
     }
     while(l2 >= 0)//结束条件是第二个数组中的元素全都遍历到第一个元素中去
     {
        nums1[l3--] = nums2[l2--];
     }

3.移除链表元素

题目来源:https://leetcode.cn/problems/remove-linked-list-elements/description/

题目原文:

在这里插入图片描述

题意解析:

给定一个链表和节点的值,删除给定节点,返回新链表。

个人思路:

第一种思路是创建新链表,将不为给定删除的节点放到新链表中。

第二种思路是在原链表中进行删除操作。

特别注意链表可以是空链表,这点需要特别思考。

个人代码:

1.创建新链表
struct ListNode* removeElements(struct ListNode* head, int val) 
{
    typedef struct ListNode ListNode;
    
    ListNode* newhead,*newtail;
    newhead = newtail = NULL;
    ListNode* pcur = head;
    while(pcur)
    {
        if(pcur->val != val)
        {
            if(newhead == NULL)//新链表为空链表
            {
                newhead = newtail = pcur;
            }
            else
            {
                newtail->next = pcur;
                newtail = newtail->next;
            }
        }
        pcur = pcur->next;
    }
    if(newtail)//此时需要将新链表的尾节点置为空,不然会死循环。
    {
        newtail->next = NULL;
    }
    return newhead;
2.遍历原链表,删除指定位置链表
struct ListNode* removeElements(struct ListNode* head, int val) 
{
    typedef struct ListNode ListNode;
    while ( NULL != head & head->val == val) //判断头节点是否为空节点
    {
        head = head->next;
    }//此时头节点不可能是val了
    if (NULL == head) {
        return head;
    }
    ListNode* pcur = head->next; 
    ListNode* prev = head;
    while(pcur)
    {
        
        if(pcur->val == val)
        {
    		ListNode* Next = pcur->next;
            prev->next = Next;
            pcur = Next;
            
        }
        else
        {
            prev = pcur;
            pcur = pcur->next;
            
        }
        
    }
    return head;
}

4.反转链表

题目来源:https://leetcode.cn/problems/reverse-linked-list/description/

题目原文:

在这里插入图片描述
提示:

链表中节点的数目范围是 [0, 5000]
-5000 <= Node.val <= 5000

题意解析:

给定一个链表,现将原链表的头尾顺序倒置,然后输出。

个人思路:

第一个思路是创建新链表,将原链表的节点头插到新链表中,这样就实现了
第二个思路是使用三个指针,完成链表翻转,此方法难以想到,需要多加注意。

个人代码:

2.三个指针,完成链表翻转
    if(head == NULL)
    {
        return head;
    }
    ListNode* n1,*n2,*n3;
    n1 = NULL;
    n2 = head;
    n3 = head->next;
    while(n2)
    {
        n2->next = n1;//改变n2的指向
        n1 = n2;
        n2 = n3;
        if(n3)
        {
            n3 = n3->next;
        }
        
    }
    return n1;

1.创建新链表,进行头插操作
struct ListNode* reverseList(struct ListNode* head) 
{
    typedef struct ListNode ListNode;
    //1.遍历头插
    ListNode* pcur = head;
    ListNode* newhead = NULL;
    ListNode* newtail = NULL;
    while(pcur)
    {
        if(newhead == NULL)
        {
            newhead = newtail = pcur;
            pcur = pcur->next;
        }
        else
        {
            ListNode* next = pcur->next;
            pcur->next = newhead;
            newhead = pcur; 
            pcur = next;
        }
        
        
    }
    if(newtail)
    {
        newtail->next = NULL;
    }
    return newhead;

5.合并两个有序链表

题目来源:https://leetcode.cn/problems/merge-two-sorted-lists/description/

题目原文:

在这里插入图片描述
提示:

两个链表的节点数目范围是 [0, 50]
-100 <= Node.val <= 100
l1 和 l2 均按 非递减顺序 排列

题意解析:

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

个人思路:

创建新的空的链表,遍历原链表,将节点值小的拿到新链表中进行尾插操作。遍历的结果只有两者情况:第一个链表为空或者第二个链表为空。

个人代码:

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) 
{
    typedef struct ListNode ListNode;
    ListNode* l1 = list1;
    ListNode* l2 = list2;
    if(list1 == NULL)//判断原链表是否为空
    {
        return list2;
    }
    if(list2 == NULL)
    {
        return list1;
    }
    ListNode* newhead,*newtail;//创建新链表
    newhead = newtail = (ListNode*)malloc(sizeof(ListNode));//创建哨兵位,不必再判断新链表是否为空
    while(l1 && l2)
    {
        if(l1->val < l2->val)
        {
            newtail->next = l1;
            newtail = newtail->next;
            l1 = l1->next;
        }
        else
        {
            newtail->next = l2;
            newtail = newtail->next;
            l2 = l2->next;
        }
    }
    if(l1)
    {
        newtail->next = l1;
    }
    if(l2)
    {
        newtail->next = l2;
    }
    ListNode* ret = newhead->next;
    free(newhead);
    newhead = NULL;
    return ret;
}

6.链表的中间结点

题目来源:https://leetcode.cn/problems/middle-of-the-linked-list/description/

题目原文:

在这里插入图片描述

题意解析:

给定单链表的头结点 head ,请你找出并返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

个人思路:

快慢指针法:slow每次走一步,fast每次走两步
(1)若节点为奇数个,循环条件是fast->next== NULL,此时slow指向的是中间节点
(2)若节点为偶数个,循环条件是fast==NULL, 此时slow指向的是中间节点

个人代码:

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

7.环形链表的约瑟夫问题

题目来源:https://www.nowcoder.com/practice/41c399fdb6004b31a6cbb047c641ed8a

题目原文:

在这里插入图片描述

题意解析:

编号为 1 到 n 的 n 个人围成一圈。从编号为 1 的人开始报数,报到 m 的人离开。
下一个人继续从 1 开始报数。n-1 轮结束以后,只剩下一个人,问最后留下的这个人编号是多少?

个人思路:

创建环形链表,遍历链表,计数计到m的节点销毁。直到链表只剩一个节点时退出循环

个人代码:

typedef struct ListNode ListNode;
 ListNode* buynode(int x)
 {
    ListNode* node = (ListNode*)malloc(sizeof(ListNode));
    if(node == NULL)
    {
        exit(1);
    }
    node->val = x;
    node->next = NULL;
    return node;
 }
 ListNode* createcic(int n)
 {
    //创建第一个节点
    ListNode* phead = buynode(1);
    ListNode* ptail = phead;
    for(int i = 2;i <= n;i++)
    {
        ptail->next = buynode(i);
        ptail = ptail->next;
    }
    ptail->next = phead;
    return ptail;
 }
int ysf(int n, int m ) 
{
    //1.创建带环链表
    ListNode* prev = createcic(n);
    //2.遍历链表,删除节点
    ListNode* pcur = prev->next;
    int count = 1;
    while(pcur->next != pcur)
    {
        if(count == m)
        {
            prev->next = pcur->next;
            free(pcur);
            pcur = prev->next;
            count = 1;

        }
        else 
        {
            prev = pcur;
            pcur = pcur->next;
            count++;
        }
    }
    return pcur->val;
}

8.分割链表

题目来源:https://leetcode.cn/problems/partition-list-lcci/description/

题目原文:

在这里插入图片描述

题意解析:

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。你不需要 保留 每个分区中各节点的初始相对位置。

个人思路:

第一种思路在原链表上进行修改,遍历原链表,若节点的值大于或等于x,尾插再原链表后,然后删除旧的链表。
此方法需要定义多个指针变量,删除链表需要定义prev指针,尾插需要定义新的尾部变量。

第二种思路是创建大小链表,分别遍历大、小链表,然后将小链表的尾节点和大节点的第一个节点首位相连。

个人代码:

struct ListNode* partition(struct ListNode* head, int x)
{
    typedef struct ListNode ListNode;
    if(head == NULL)
    {
        return head;
    }
    //创建两个带头链表
    ListNode* lesshead,*lesstail;
    ListNode* bighead,*bigtail;
    lesshead = lesstail = (ListNode*)malloc(sizeof(ListNode));
    bighead = bigtail = (ListNode*)malloc(sizeof(ListNode));
    //遍历原链表,将原链表中的节点尾插到大小链表中
    ListNode* pcur = head;
    while(pcur)
    {
        if(pcur->val < x)
        {
            //尾插到小链表中
            lesstail->next = pcur;
            lesstail = lesstail->next;
        }
        else
        {
            //尾插到大链表中
            bigtail->next = pcur;
            bigtail = bigtail->next;
        }
        pcur = pcur->next;
    }
    //将bigtail->next初始化的同时还置为NULL,防止死循环
    bigtail->next = NULL;
    //首位相连
    lesstail->next = bighead->next;
    
    ListNode* ret = lesshead->next;
    free(lesshead);
    free(bighead);
    lesshead = bighead = NULL;
    return ret;
}

完。。。

有感:其实我早就死了,死在破碎的三观里;死在飘渺的理想里;死在幻想的感情里;死在虚无的回忆里。但我好像我还活着,活在生活的压力里;活在社会的角落里;活在亲人的期盼里;活在儿时的梦里。

  • 17
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Filex;

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值