力扣学习day4

学习目标:

1.争取在2021年2月底刷完力扣10%题目。 2.从零开始学习C,C++,python。 3.用多种方法写出解题思路,以及理解代码。

意外收获

在做题前看到一位前辈总结的一些资料,看我我得马上完成学习目标,然后去学习了。
网址地址:https://github.com/youngyangyang04/leetcode-master

学习内容:

18. 四数之和

网址
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。

注意:

答案中不可以包含重复的四元组。

思考:方法还是排序+双指针,以后无论多少之数之和都可以用这个方法,当然两数之和的话哈希最快,但双指针也是可行的。首先排序,然后确定i,j的位置,在i,j的右端开始遍历。遍历思路还是一个L,一个R分别指向max(i,j)+1,和最右端,再往根据target-sum的差值往中心取值。因为(i,j)的遍历时间复杂度为O(n^2) ,而(L,R)的遍历时间复杂度为O(n),所以总体的时间复杂度就为O(n^3)。

往深处思考,这类题目都是相同的算法,区别在于对基本值的遍历,假设是n数之和,则外轮有n-2次循环,把除确认(L,R)的所有可能都计算一遍。虽然我觉得可能还有更优秀的算法,但那需要更严谨的数学思考,但对于现阶段的我来说就算了。

19. 删除链表的倒数第 N 个结点

网址

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

进阶:你能尝试使用一趟扫描实现吗?

思考:应该是换知识点了,这应该是对链表操作的考察。还是使用双指针吧,设置两个指针first和second,其中first=head,second->next=head,为了方便返回头节点,存储下当前的second。接着使用first遍历n次,second也随着遍历,当first指向第n个节点的时候,second指向n-1个节点,这时只要second->next=second->next->next即可删除,然后再把first节点free掉,返回之前存储的头节点即可。

20. 有效的括号

网址

给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。

思考:CSP和面试常考题目,方法就是使用栈,对于C语言选手来说,只能写个数组,然后自己编写下入栈,出栈操作。假设检测字符为(或{则入栈,遇到),}则出栈,如果出栈时判断不匹配,则无效字符串。假设,字符串已经为空,但是检测栈中元素不为0,则字符串无效。

21. 合并两个有序链表

网址

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

思考:通常作为选择题来进行出题,考察时间复杂度和空间复杂度。我的思路也很简单,因为本身就是升序链表,因此只需要依次遍历即可,每次遍历小的则加入到新的链表当中,然后指针后移,直到链表为空。这种简单算法的时间复杂度和空间复杂度均为O(m+n),m,n分别是两个链表的长度。

22. 括号生成

网址

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

思考:感觉思路还是很清晰,但是代码动手起来有点不知所措,考点应该时回溯法。根据我的经验,C语言用递归很容易奔溃,只要层数大起来,就会栈溢出。

首先输入只有"(“和”)",我们按从左到右的顺序输入,因为只有两个选择,所以脑海里可以很容易的想到二叉树的图形。

那么我们简单以二叉树为例,左子树表示输入左括号,右子树输入右括号。且制定一个规则来结束不规则的括号组合,当右括号数量大于左括号时停止进行剪枝;另一种情况就是当左括号数量大于n时停止剪枝。

回溯法也是重点考察的地方,还要注意和递归的区别之处,我感觉这也是我之后学习的地方之一。

这是我看到的一个最容易懂的代码,很有借鉴意义。

class Solution(object):
    def generateParenthesis(self, n):
        """
        :type n: int
        :rtype: List[str]
        """
        res = []
        self.dfs(res, n, n, '')
        return res
        
    def dfs(self, res, left, right, path):
        if left == 0 and right == 0:
            res.append(path)
            return
        if left > 0:
            self.dfs(res, left - 1, right, path + '(')
        if left < right:
            self.dfs(res, left, right - 1, path + ')')

23. 合并K个升序链表

网址

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

照搬下官方的思路,因为这道题我也曾在面试的时候被问及过,感觉十分有纪念价值。
思路:
在解决「合并K个排序链表」这个问题之前,我们先来看一个更简单的问题:如何合并两个有序链表?假设链表 aa 和 bb 的长度都是 nn,如何在 O(n)O(n) 的时间代价以及 O(1)O(1) 的空间代价完成合并? 这个问题在面试中常常出现,为了达到空间代价是 O(1)O(1),我们的宗旨是「原地调整链表元素的 next 指针完成合并」。

先我们需要一个变量 head 来保存合并之后链表的头部,你可以把 head 设置为一个虚拟的头(也就是 head 的 val 属性不保存任何值),这是为了方便代码的书写,在整个链表合并完之后,返回它的下一位置即可。

我们需要一个指针 tail 来记录下一个插入位置的前一个位置,以及两个指针 aPtr 和 bPtr 来记录 aa 和 bb 未合并部分的第一位。注意这里的描述,tail 不是下一个插入的位置,aPtr 和 bPtr 所指向的元素处于「待合并」的状态,也就是说它们还没有合并入最终的链表。 当然你也可以给他们赋予其他的定义,但是定义不同实现就会不同。

当 aPtr 和 bPtr 都不为空的时候,取 val 熟悉较小的合并;如果 aPtr 为空,则把整个 bPtr 以及后面的元素全部合并;bPtr 为空时同理。

在合并的时候,应该先调整 tail 的 next 属性,再后移 tail 和 *Ptr(aPtr 或者 bPtr)。那么这里 tail 和 *Ptr 是否存在先后顺序呢?它们谁先动谁后动都是一样的,不会改变任何元素的 next 指针。

感想

前面做题没有针对性,感觉收效不大,而今天又找到大佬之前的学习总结,感觉可以借鉴下学习顺序。另外,因为最近还要看相关深度学习和面试技巧,打算之后的力扣学习以专题为主。

猜你喜欢

转载自blog.csdn.net/qq_41928947/article/details/113765994