趣说数据结构(练习1) —— 顺序表/链表力扣刷题

news/2024/7/7 22:57:51

练习 1 —— 顺序表/链表力扣刷题

1. 合并两个有序链表

力扣题目地址:https://leetcode.cn/problems/merge-two-sorted-lists/

问题描述:将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
在这里插入图片描述

解题思路:

  1. 给定的是一个有序列表,因此不需要重新排序;
  2. 典型的双指针问题,不过此处的双指针分别指两个链表的对应的指针;

解题方法一:

此份代码是通过创建一个新的链表分别存储两个链表组合后的结果。这个方法比较简单,简单来说就是两个步骤,第一步逐个比较,你们俩谁小,谁小就移动谁的指针;第二步收尾,还有谁有剩余的存库,快快交出来。

这份代码是比较 low 的,因为它需要额外开销空间,并且代码看起来多少有点不够美观。

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode* result = new ListNode(-1);
        ListNode* point = result;
        while(list1 != nullptr && list2 != nullptr) {
            if(list1->val <= list2->val) {
                point->next = list1;
                list1 = list1->next;
            } else {
                point->next = list2;
                list2 = list2->next;
            }
            point = point->next;
        }
        if (list1 != nullptr) {
            point->next = list1;
        }
        if (list2 != nullptr) {
            point->next = list2;
        } 
        return result->next;
    }
};

解题方法二:

以下代码来自官方文档
作者:LeetCode-Solution
链接:https://leetcode.cn/problems/merge-two-sorted-lists/solution/he-bing-liang-ge-you-xu-lian-biao-by-leetcode-solu/

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if (l1 == nullptr) {
            return l2;
        } else if (l2 == nullptr) {
            return l1;
        } else if (l1->val < l2->val) {
            l1->next = mergeTwoLists(l1->next, l2);
            return l1;
        } else {
            l2->next = mergeTwoLists(l1, l2->next);
            return l2;
        }
    }
};

比较的简洁美观,看起来也比较舒服,除了 l1l2 这种不太科学的变量命名,读起来还是非常的简单。

  1. 排除二者存在空的情况;
  2. 递归解决问题,每次递归只解决一个问题;
  3. 每次递归返回的数据都是已经完成 “部分合并” 后的结果。

坏处:此份代码使用递归需要申请额外的栈存储空间。

2. 删除排序链表中的重复元素

力扣原题地址:https://leetcode.cn/problems/remove-duplicates-from-sorted-list/

题目描述:给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。

题目的示例:
在这里插入图片描述

输入:head = [1,1,2]
输出:[1,2]

问题分析:抓住关键字:已经排序的链表。

接下来的时间就简单了,我们用一个指针p,不要轻易移动,只有遇到 “与自己不一样” 的小伙伴才移动,自己才能变成它,最终就绕开了重复的元素,实现了去重的目的。

解题方法一:


class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if (!head) {
            return head;
        }

        ListNode* now = head;
        while (now -> next) {
        	// 如果遇到的和自己相等,就绕开它到它的next
            if (now -> val == now -> next->val) {
                now -> next = now -> next->next;
            } else {
                // 不一样就保留它,加到自己的队伍
                now = now -> next;
            }
        }

        return head;
    }
};

这个解题方法比较简单,也未申请额外的存储空间,所以时间复杂度为 O ( n ) O(n) O(n),而空间复杂度为 O ( 1 ) O(1) O(1)

3. 反转链表

题目描述

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例

在这里插入图片描述

代码实现 1

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* result = new ListNode();
        ListNode* p = result;
        while (head != nullptr) {
            ListNode* q = new ListNode(head -> val);
            q -> next = p -> next;
            p -> next = q;
            head = head -> next;
        }

        return result -> next;
    }
};

时间复杂度:O(n)

空间复杂度:O(n)

时间复杂度已经无法降低,空间复杂度应当有改善空间,如下所示:

代码实现 2

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* prev = nullptr;
        ListNode* curr = head;
        while (curr != nullptr) {
            ListNode* next = curr -> next;
            curr -> next = prev;
            prev = curr;
            curr = next;
        }
        return prev;
    }
};

时间复杂度:O(n)

空间复杂度:O(1)

这个地方有疑问的小伙伴可以尝试先返回 curr,输出的结果是 [ ],也就是说,之所以能够节省空间,只是我们使用两个指针移来移去的,这里我们绘制一个简单图片描述一下。

在这里插入图片描述
第一步,next = curr -> next 记录一下当前结点的下个结点的位置;
第二步,curr -> next = prev 当前结点指向的下一个结点等于 prev,请看图片中的下面一行图片,也就是说这个时候 prev 准备挪到 2 那个位置了,我们先把 curr -> next = prev,然后 prev = curr,这个时候对应的 prev 已经指向了 2 的位置;
第三步,curr = next,把 curr 拉回正轨。因为刚刚出去协助 prev 指针了,所以这里把它拉回来。

循环前面的步骤,即可完成链表的反转。归根结蒂就是利用指针的灵活性,反过来再写一遍。

有点迷糊

总结

本章的练习部分算是 “小试牛刀”,两个非常简单的题目,用来练练手感受一下使用指针 -> 挪动数据的方法。

Smileyan
2023.04.30 00:25


http://lihuaxi.xjx100.cn/news/1085097.html

相关文章

尚融宝26-投标

目录 一、需求 &#xff08;一&#xff09;投资人投标 &#xff08;二&#xff09;流程 二、标的详情 &#xff08;一&#xff09;需求 &#xff08;二&#xff09;后端 &#xff08;三&#xff09;前端 三、计算收益 &#xff08;一&#xff09;四种还款方式 &#…

(数学知识)试除法判断质数,分解质因数,埃式与线性筛质数

质数 质数是指在大于1的自然数中&#xff0c;除了1和它本身以外不再有其他因数的自然数。 试除法判定质数 你会发现如果说一个数要分成两个数相乘的话&#xff0c;那么这两个数肯定都是成对出现的&#xff0c;有一大一小的相对关系。因此不需要从2遍历到n&#xff0c;循环的…

深入与浅谈 Vue 中计算属性和侦听器的区别和使用(Vue3版本为例)

#五一技术创作马拉松# &#x1f4cb;前言 计算属性 computed 和侦听器 watch 都是 Vue.js 框架中用来响应式更新视图的重要概念。在 Vue 项目开发中&#xff0c;这两个技术点是非常重要的&#xff0c;同时也是 Vue 基础中不可缺少的知识点。在面试中&#xff0c;计算属性 comp…

2022年新能源汽车专题讲座

2022年新能源汽车专题讲座 单选题&#xff08;共5题&#xff0c;每题6分&#xff09; 1、《中华人民共和国数据安全法》自&#xff08;&#xff09;起施行。 正确答案&#xff1a;C、2021年9月1日 2、典型的智能汽车结构主要分为&#xff08;&#xff09;个层次。 正确答案…

《Effective Python 编写高质量Python代码的59个有效方法》学习笔记6(完)

用functools.wraps定义函数修饰器 python为修饰器提供了专门的语法&#xff0c;它使得程序在运行的时候&#xff0c;能够用一个函数来修饰另一个函数 对于调试器这种依靠内省机制的工具&#xff0c;直接编写修饰器会引发奇怪的行为 内置的functools模块提供了名为wraps的修饰器…

从零开始学习Linux运维,成为IT领域翘楚(三)

文章目录 &#x1f525;Linux超级用户与伪用户&#x1f525;Linux文件基本属性&#x1f525;Linux权限字与权限操作 &#x1f525;Linux超级用户与伪用户 Linux下用户分为三类&#xff1a;超级用户、普通用户、伪用户 ⭐ 超级用户&#xff1a;用户名为root&#xff0c;具有一切…

计算机网络中常见的数据传输方式(电路交换,报文交换,分组交换)

前言&#xff1a;大家好&#xff0c;我是小威&#xff0c;24届毕业生&#xff0c;在一家满意的公司实习。本篇文章将详细介绍计算机网络中常见的数据传输方式&#xff0c;如电路交换&#xff0c;报文交换&#xff0c;分组交换。 如果文章有什么需要改进的地方还请大佬不吝赐教&…

XR技术在手术中的应用调研

虚拟现实、增强现实、混合现实等概念和技术是最近几年发展起来的&#xff0c;相信你对去年大火的元宇宙深有感触&#xff0c;元宇宙属于虚拟现实的技术范畴&#xff0c;头号玩家电影也让虚拟现实走进大众的视野中。早在2015年&#xff0c;笔者参加一次展会时就有接触&#xff0…