刷leetCode算法题+解析(四)

合并两个有序链表

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

示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

这个要多讲讲,因为完全涉及到了我的知识盲区!!!有序链表这种东西真的没怎么接触过,树也接触的少,毕竟我就是一个非科班,在小外包公司成长起来的,一直以来对这种数据结构还有算法什么的保有憧憬和向往(这也是现在刷算法的原因)。刚一接触题目,这个ListNode都够让我看好久了,就是不明白为啥next就是下一个节点。还是我狗群友给了我指点,在此表示感谢。步入正题。
思路:我自己做的麻烦了,因为是先把两个链表取出来排序然後在放到一个链表了,看了大佬思路才发现可以两个链表取出来直接放到链表里,省去了中间的过程。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if(l1==null && l2==null){
            return new ListNode(0).next;
        }
        //遍历俩链表,元素都取出来
        List list = new ArrayList();
        while(l1!=null){
            list.add(l1.val);
            l1=l1.next;
        }
        while(l2!=null){
            list.add(l2.val);
            l2=l2.next;
        }   
        //为了保证链表的有序性,所以把list排序     
        Collections.sort(list);
        //保存头节点
        ListNode result = new ListNode(0);
        //用cur来控制指针往下挂节点?
        ListNode cur = result;
        for(Integer i:list){
            cur.next = new ListNode(i);
            cur = cur.next;
        }
        return result.next;
    }
}

这个其实不夸张的说真的是一道题卡半天,因为关于链表,节点什么的没什么意识,差不多做点就问群里人,然後现在为止也不能说多了解链表吧。但是勉强上面的代码可以解释的通,理解面上的意思了(我不知道我注解是不是错了,如果错了欢迎指出)。
然後我都做完了,甚至哪怕看了大神的解法开始分析了,还是在群友的提醒下发现了问题:题目要求是要拼接两个链表节点,我这里取了巧,用的list排序。所以下面老老实实介绍大神的写法


刷leetCode算法题+解析(四)_第1张图片
题目

其实大神的写法思路也很清晰,如果懂了链表的结构也很简单就能看懂。我反正现在是勉勉强强可以写出来。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        //定义好头节点
        ListNode result = new ListNode(0);
        //用这个cur来挂载节点
        ListNode cur = result;
        //两个链表都不为空才有必要比较,只剩一个了直接全挂上就好了
        while(l1!=null && l2!=null){
            //因为要按照顺序挂,所以先挂小的
            if(l1.val

删除排序数组中的重复项

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

示例 1:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,2,2,3,3,4],
函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
你不需要考虑数组中超出新长度后面的元素。

思路:这个题目其实挺唬人的,什么不使用额外的数组空间,就是不能创建新数组呗。O(1)这个概念还是我现去百度的
O(1):就是最低的时空复杂度了,也就是耗时/耗空间与输入数据大小无关,无论输入数据增大多少倍,耗时/耗空间都不变。 哈希算法就是典型的O(1)时间复杂度,无论数据规模多大,都可以在一次计算后找到目标(不考虑冲突的话)
但是抛除了这几句话开始正常考虑思路其实也没那么难。不就是要做到两点:一使数组中没有重复元素,二返回数组长度么。首先不能创建新数组,所以只能在原数组中改动,其次因为是有序数组,最多就是一样的数组连续排在一起,不会存在隔过一个跟之前的相同,所以只要比较上一个元素就行而不是要遍历比较之前所有的元素。
接下来就是实现的代码:

    public int removeDuplicates(int[] nums) {
        //打标
        int j = 0;
        int i = 1;
        while(i

然后因为这个理清楚逻辑是很好写出来的,说句题外话,我最近用了LeetCode才深感编译器的重要性。无论是自动提示还是报错提醒都特别有用。因为用LeetCode经常遇到忘记加标点符号或者后半个括号什么的,还非要运行才报错,麻烦死了。
上面的代码一开始我用的for循环,跑出了两次数组下标越界才改成现在这个版本的!
然后贼自豪的是跑出来的用时超过了百分百的用户,第一次哎!


刷leetCode算法题+解析(四)_第2张图片
image.png

上面代码中用了两个标:一个j是数组的下标、我们都知道数组的元素数是下标+1.因为下标从0开始。一个是原数组中的下标i。这个只是为了判断是否还有元素没有比较到。等到数组中所有元素比较完了,把唯一出现的存进去,重复出现的不用管了反正不占位就放那放着,占位了的就用新的把老的替换了。
之前看了下大神的解析,差不多都是用的这种思路。只不过细节上可能是略有不同,有的for循环有的do-while也有的和我一样是while。
然后好像这种思路有一个专有名词,叫做双指针解法?反正思路是大同小异,所以我也不多说了。

移除元素

题目:给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例 1:
给定 nums = [3,2,2,3], val = 3,
函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,1,2,2,3,0,4,2], val = 2,
函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。
注意这五个元素可为任意顺序。
你不需要考虑数组中超出新长度后面的元素。

这个题目其实只要是上道题会做了这个也自然会做了。只不过上题判断的是重复,这个题判断的是等于某值而已。不多说了,上代码:

    public int removeElement(int[] nums, int val) {
        //打标,作为原数组下标
        int i = 0;
        //打标,作为现在的数组下标
        int j = 0;
        //原数组下标不越界就ok,所以这里用j

这个题没什么可解释的了,除了那个if条件剩下一样一样的代码。不过这个是从第0个开始判断,上一个因为判重,所以第一个元素肯定不会重复,所以是从第一个开始判断。

今天因为一些工作的原因,还有链表耽误了很多时间,所以几天就这三道题,如果稍微帮助到了你点个喜欢点个关注,也祝大家工作顺顺利利!

你可能感兴趣的:(刷leetCode算法题+解析(四))