LeetCode做题随记

自我随记

做题时遇到一些问题,或者学到了一些的东西

11.01

Queue中offer与add区别
Queue 中 add() 和 offer()都是用来向队列添加一个元素。
在容量已满的情况下,add() 方法会抛出IllegalStateException异常,offer() 方法只会返回 false 。

扩展到Queue中的不同
对于Queue来说,就是一个FIFO(先进先出)的队列,添加元素只能在队尾,移除只能在队首。
对于这一组方法,成功返回true,在操作失败时抛出异常,这是与下面一组方法的主要区别。
add(E e):添加一个元素到队尾
remove():获取队首的元素,并从队列中移除
element():获取队首的元素,但不从队列中移除
这一组,成功返回true,失败时返回一个特殊值(取决于操作,为NULL或false),offer(E e)操作是专为容量受限的队列实现而设计的;在大多数实现中,插入操作不会失败。
offer(E e):添加一个元素到队尾
poll():获取队首的元素,并从队列中移除
peek():获取队首的元素,但不从队列中移除

前序中序后序
皆是根节点(当前节点)访问的位置来命名,都是用dfs来实现。
前序:中左右
中序:左中右
后序:左右中

11.02

Morris遍历算法
通过设置线索来模拟递归的出栈,从而用一定的时间复杂度来换取o(1)的空间复杂度。
其中中序遍历用的多。后序遍历比较难,我暂时还没去弄明白。

11.3

如何获取当前索引下最小值
从栈那题getmin学到,维护一个最小栈,每插入一个元素,则与当前最小值比较,小的那个入栈,并更新最小值。从而,能够得到一个当前索引下的最小值。

从每日温度学到了单调栈
其实单调栈就是从数组中找到左右两边比你大的数或者比你小的数而且时间复杂度为O(N)
顾名思义,栈是单调的,它能够判断左(右)第一个比你大(小)的是哪一个。同时时间复杂度低。

初始化数组
对于int类型数组,初始化写起来很麻烦,所以baidu了一个方法:Arrays.fill(arr, value);,实测可用,但速度未知,也没看源码,凑合用

Deque和Stack
做题时,看到评论推荐使用Deque而不是Stack,说Stack效率低。
于是搜索了一下。
对于Stack,其是继承自Vector类。Vector和ArrayList、LinkedList并列,实现List接口。它内部通过数组实现,支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,所以它的访问比ArrayList慢。
对于Deque,其是Queue的子接口,它有两个实现类,ArrayDeque和LinkedList。它既可以充当栈,也可以充当队列。当充当栈时,用ArrayDeque的push和pop方法。充当队列时,使用ArrayDeque的add和remove方法。
(关于速度,我试了一下,瞬间时间从58%上升到了95%,确实快了很多很多,好东西好东西,以后就用Deque了)

11.6

前天没学到新东东,就是小码一下,这两天忙得要命,赶组会的工作量。
并查集
这里先mark一下,做题清单后面也有,看到一篇将并查集特别好玩的博客,先记录一下
https://blog.csdn.net/qq_41593380/article/details/81146850

11.8

学了manacher算法,附上我自己ide博文地址。https://blog.csdn.net/qq_34687559/article/details/109560059
关于manacher,可以用的场景其实不少,以后不会少打交道。
其次是关于对称的这个概念,可以继续牢记于心,对称即可减少复杂度。

11.9

零零散散看了一天,琐事太多了,全都是无关紧要且与我无关的事。好在最后也是基本上看懂了kmp算法,然后想好好的把它给写出来。
kmp算法也算是利用到对称这个点,在字符串中,对称真是用在了很多的地方。
不过我在翻资料的过程中还看到了ac自动机,它是多模的字符查找(kmp是单模字符查找,且kmp是ac自动机的基础),先mark一下,如果以后有机会接触的话再去好好的看看。

11.10

有序矩阵(行列皆满足一定的升序or降序)
做了240这一题,这是个排序矩阵,从左往右增大,从上往下增大,关于这题,暴力解法,dfs用栈代替来解也还可以,不过从左下角入手,迭代的去找很简单。原因在于:
这个算法的关键是找到合适的遍历起点,这个点肯定具有某种特殊性,这个二维矩阵,四个角就是四个特殊点,但他们的特点不同,左上和右下分别是矩阵的最小和最大值,左下和右上具有两面性,如果是所在行最大值那么就是所在列的最小值,反过来也一样。左上和右下与目标值比较不相等时,下一步既可以遍历行也可以遍历列是不确定的,而左下和右上是可以确定的,因为自身值的特点可以排除一个方向的路径,只有一个遍历路径。(看的评论,感觉很棒)
对于这种矩阵,以及类似的题,一定要主要找其特殊点,分析其特殊点的含义。
这一题可以说是教会了我一种新的思路。

关于归并排序
了解了归并排序实际上就是一个分支+双指针比较合并的一个过程。
一般而言,都是对于两个有序数组(or链表or whatever)来进行合并,用双指针(这个意思)就好了。
而很多时候我们需要对多个有序数组进行一个有序的合并,那么此时,就不能指望这用指针啊变量啊什么的来进行合并了,所以此时一个很好用的数据结构,优先队列,用优先队列来进行一个比较,特备的方便,特别的舒服。这里,在java中需要建立个比较器。
然后对于数组,可以选择往队列里放数组,数组第一个元素是数值,后面两个是坐标。比较器比价的也是数值。这样很方便,能够要多少就放多少进入优先队列,不用把全部放入,从而浪费时间复杂度。

11.15

并查集
最大作用是检查图中是否存在环
基本的原理是在于,每次维护连通片,当新出现的边两端点都存在于连通片中,则成圈。
难点在于Disjoint set的实现。
清楚了,关于Disjoint set的实现,这个确实是非常的有意思。
用数组来记录每个点的父节点。这样一直往上查的话,就能够查到最顶层的父节点,如果父节点相同,则说明两个节点是相连的。
即判断相连的依据变成了是否有相同的父节点。
主要实现的两个函数是find_root和union。
关于优化就是,因为相当于连成了树,可能会树的深度过深,所以每次find_root会很大,所以,可以考虑将树的高度给减小,一个公共父节点,然后很多子节点,这样的话,每次find_root只需一次就可以了。当然,这样实现的很麻烦。有一个比较好实现,开销又小的方法。
将深度低的树加到深度高的树上,这样总体树的深度不会变更大。这里,我的想法是从find_root那里返回,返回一个数组,0号位是root,1号位是深度(find的次数)。

11.16

又见马拉车算法,第5题,随便改改就行了。

int与long
java中int是4个字节,而long是8个字节。
在进行int的处理时,很多时候可能会导致int基本类型的数据实际上超过了4个字节,此时便会溢出。
在处理溢出时,有几个思路。
思路1:用long来避免溢出,最后计算完之后进行判断(int)res==res来看是否溢出
思路2:捕获异常,然后来执行对应的处理

	public int reverse(int x) {
        String xString = Integer.toString(x);
        String string = xString;
        int flag = 1;
        if (x < 0) {
            flag = -1;
            string = xString.substring(1);
        }
        try {
            return Integer.valueOf((new StringBuilder(string)).reverse().toString()) * flag;
        }catch (Exception e){
            return 0;
        }
    }

思路3:像对leetcode 07这种题,可以在迭代中每次计算自己/10之后与之前一次迭代的值是否相等,从而判断是否溢出。
eg:

class Solution {
    public int reverse(int x) {
        int result = 0;
        while(x != 0) {
            int tmp = result; // 保存计算之前的结果
            result = (result * 10) + (x % 10);
            x /= 10;
            // 将计算之后的结果 / 10,判断是否与计算之前相同,如果不同,证明发生溢出,返回0
            if (result / 10 != tmp) return 0; 
        }

        return result;
    }
}

11.17

有限状态机
好家伙,好家伙,我直接好家伙,能够帮助减少if-else的错误可能,不过使用情况其实还是不多,还是要好好分析才是其实。

字符串重复几次

String.join("",Collections.nCopies(quotient,str[start]))

不过这个其实,巨慢,方便,但不合适

11.18

双指针与hashset
在允许排序的情况下,双指针解决两数之和,三数之和的效率比hashset要高,虽然都是一个时间复杂度。

排序
在看排序的视频,选择排序感觉就是冒泡的反着来。
插入排序的话,就像扑克牌一样,每次进行插入。
经典快速排序的话,每次进行选择,将最后一个数X作为判断标准,将数组划分为<=X和>X。
改进的快速排序是在于:经典排序每一次其实都是只能确定一个数,就是我们的最后一个数X,但是问题在于其实X不只一个,可能会有不少的X。所以X改进的X希望能够将所有的X全都确定下来,这个挺重要的。
而随机快排产生的原因在于,前面的快排都是取决于最后一个元素X,这个X可能及其不稳定,每一次划分的不均匀,因为当均匀的能够将数组划分一半时,才是最好的,但是我们根本都无法确定,所以,最终,我们只能用随机的方式来选择X,当然,这会比最后一个位置要可能好一些,不过本质上来说,都是一样,因为都是随机,概率都是一样的。
这里排序看完了写个博客总结一下,因为其实很早之前都学过了,现在只是在复习,反反复复看了多次,还是得总结一下给确定一下,不能每次都看,太浪费时间了。
随机快排是一个十分快的排序算法,时间复杂度比较低,因为实现简单,常数项低,所以一般都用它。

11.19


对堆有了更深刻的理解,包括堆是由数组来构成,它是一个虚拟的假象结构,通过数组的映射来实现,它虚拟的结构式完全二叉树。
它可以解决持续流中位数(大根堆和小根堆来平分数据)的问题,在贪心上也十分重要。
堆排序
就是构建堆(大根),然后每次将堆顶弹出(堆顶与堆尾后一个交换,然后堆的大小减1),从而每次都保证当前最大数放在后面,进行n-1次弹出,就可以得到一个升序的序列。
reverse
似乎是java中没有啥方法能提供对数组reverse的,所以有点小麻烦,如果是有序可以用sort来解决,如果没有的话,老实的写4行代码来进行O(mid)的交换reverse吧。
sort
Arrays.sort(nums,0,i)可以实现数组中一定范围的排序,这个超好用。

11.26

字典序排字符串

	char[] chars = str.toCharArray();
    Arrays.sort(chars);
    String key = new String(chars);

List转数组
要规定数组大小,这样才能进行转换

return res.toArray(new int[res.size()][2]);

List无法clone
因为深拷贝的问题,经常会有List<List>这种返回的数据结构,但是List又无法clone,所以可以采样以下方法来拷贝并new一个List,

List<Integer> temp = new LinkedList<>(res.get(j));

01.01

之前没有写该博客是因为中间少遇到小的问题
题也在正常刷,也刷了不少了,项目走的也不错
本来想新开一篇来记录的,算了,还是就这里写吧

连续数组拷贝
java中有提供方法将一个数组的连续子串给拷贝到零一个数组

		int[] nums1 = new int[nums.length-1];
        System.arraycopy(nums,0,nums1,0,nums.length-1);
        int[] nums2 = new int[nums.length-1];
        System.arraycopy(nums,1,nums2,0,nums.length-1);

例如在上述代码中使用的System.arraycopy()方法,它是一个native方法,执行起来很快,推荐使用
该方法参数为(源数组,起始复制位置,目标数组,起始粘贴位置,复制长度),这里目标数组长度应该≥复制长度

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值