LeetCode刷题日记

LeetCode刷题日记

一、链表类

1、LeetCode第21题(合并两个有序链表)【简单】

// 解法一:双指针拉链法,比较当前指针位所在值的大小,若比较a1和a2时a1>=a2,直接将a2的指针指向a1依次类推
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        // 定义新链表
        ListNode temp=new ListNode(-1);
        // 定义头指针
        ListNode p=temp;
        ListNode p1=list1;// 自己能点出来的,直接赋值就好
        ListNode p2=list2;
        while(p1!=null && p2!=null){
            if(p1.val<p2.val){
                p.next=p1;
                p1=p1.next;
            }else{
                p.next=p2;
                p2=p2.next;
            }
            p=p.next;
        }
        if(p1!=null){
            p.next=p1;
        }
        if(p2!=null){
            p.next=p2;
        }
        return temp.next;
    }
}







// 二刷:心得=》必要时一定要定义一个辅助指针,需要一个始终不表的头指针来链接上所有的数据
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        // 1.定义一个虚拟头指针(新的链表),目的存放合并完成的链表
        ListNode temp=new ListNode(-1);
        // 需要一个一直在移动的指针,一个不动的头指针
        ListNode dummy=temp;
        // 2.分别给两个链表定义头指针
        ListNode p1=list1;
        ListNode p2=list2;
        // 3.循环拉拉链
        while(p1!=null&&p2!=null){
            if(p1.val<=p2.val){
                // 链到新链表后面
                dummy.next=p1;
                // 后移
                p1=p1.next;
            }else{
                dummy.next=p2;
                p2=p2.next;
            }
            dummy=dummy.next;
        }
        // 4.判断最后一个值的位置
        if(p1!=null){
            dummy.next=p1;
        }
        if(p2!=null){
            dummy.next=p2;
        }
        return temp.next;
    }
}

2、LeetCode第141题(环形链表【判断是否为环形链表】)【简单】

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
       // 使用快慢指针,再环形链表中必会相交的特点来判断
       // 1.定义快慢指针
       ListNode fast=head;
       ListNode slow=head;
       // 2.(退出循环的条件,一快指针已经null没环;相遇满足成环条件)
       while(fast!=null&&fast.next!=null){
           fast=fast.next.next;
           slow=slow.next;
           // 每次走完判断
           if(fast==slow){
               return true;
           }
       }
       return false;
    }
}

3、LeetCode第141题目(判断环形链表交点位置)【中等】

在这里插入图片描述

分析:

1、按照套圈问题总结,fast一定是slow的2k倍

2、设slow到相遇点时为k,那么fast为2k

3、设环形起点到相遇点是m=》那么head到环形起点位置为=k-m(slow那一块)

4、那么按照这样计算=》fast的角度出发就是(head到环形起点位置为=2k-k-m)

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
// 交点处,无非就是走完前面一小段到环的位置的地方(如图就是3=》2这段距离)
// 难点解出这段距离的长度
// 使用快慢指针,当相遇时,将任何一个指针返回到头节点,然后再两一个一起走,那么再次相遇的时候,就是目标
// 在代码中指针定义走两倍之差
// 思路如图:假设一个相遇点,想象一下从相遇点截断甩到头头指针位置就是这段目标距离
        // 1.定义快慢指针
        ListNode fast=head;
        ListNode slow=head;
        // 2.执行快慢指针的常规操作
        while(fast!=null&&fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
            // 2.1找到相遇点跳出循环
            if(slow==fast){
                slow=head;
                break;
            }
        }
        // 注意判断不成环的情况
        if(fast==null||fast.next==null){
            return null;
        }
        // 3.这个时候就一步一步走了,当他们再次相遇时跳出循环
        // 3.1记录链表的索引下标位置
        int count = 0;
        while(fast!=slow){
            fast=fast.next;
            slow=slow.next;
            count ++;
        }
        System.out.println("index:"+count);
        return slow;
    }
}

4、LeetCode第876题(链表中间节点)【简单】

// 解法一:计算链表的长度后,size/2得中间得位置即可

// 解法二:使用快慢指针的技巧(当速度是两倍的快指针到尾部时,慢指针刚好到中间)
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode middleNode(ListNode head) {
        // 使用快慢指针,快指针一次走两步,慢指针一次走一步
        // 1、定义快慢指针
        ListNode fast=head;
        ListNode slow=head;
        // 2.遍历
        while(fast!=null&&fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
        }
        return slow;
    }
}

5、LeetCode第160题(相交链表)【简单】

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

图示两个链表在节点 c1 开始相交:

解法分析:

这种解法就是将指针遍历到等长的位置找到对应的交点;


// 解法一:化为等长的链表去找交点
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        // 概述:找到交点,问题是如何定位
        // 难点:两个长度不一样的链表如何将长度变为一样,定位到交点
        // 使用双指针,当两个指针到到达末尾时,马上让他们指向另一个链表,当相交时到交点
        // 1.定义双指针
        ListNode p1=headA;
        ListNode p2=headB;
        // 2.遍历
        while(p1!=p2){
            // 2.1判断是后移还是当一个指针到null就指向另一条链表
            if(p1==null){
                p1=headB;
            }else{ 
                 p1=p1.next;
            }
            if(p2==null){
                p2=headA;
            }else{
                p2=p2.next;  
            }
        }
        return p1;
    }
}

6、LeetCode第19题(删除倒数第n个节点)【中等】

// 法一:遍历找到目标节点,再遍历一边删除

// 难点:1、遍历一次就拿捏它;2、控制虚拟头节点的恰当使用,避免NPE

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
// 概述:删除倒数第n个节点;就是删除正数第【总长-(n-1)】个节点
// 使用虚假节点和双指针技巧(遍历一次)解决
// 快指针先走n步;然后慢指针再走=》当快指针走到末尾,那么慢指针删除正数第【总长-(n-1)】个节点
        // 使用虚假指针避免空指针
        ListNode dummy=new ListNode(-1);
        dummy.next=head;
        // 1.定义快慢指针
        ListNode fast=dummy;
        ListNode slow=dummy;
        // 3.快指针先走n步(这里走n-1步是为了找到目标接节点的前一个节点,便于删除目标节点)
        // 可以考虑封装一个方法,去找到对应的节点
        for(int i=0;i<n+1;i++){
            fast=fast.next;
        }
        // 4.慢指针和快指针同时开始走
        while(fast!=null){
            fast=fast.next;
            slow=slow.next;
        }
        slow.next=slow.next.next;
        return dummy.next;
    }
}

===》链表双指针–快慢指针;虚拟头指针

7、LeetCode第206题 反转链表(反转全部)【简单】

//解法一:优雅解法递归--装B必备(在递归中当最后一次进入base case,所以他的头节点是尾节点的前一个)
// 最后一轮压栈后,直接触发base case 就return了
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        // 递归
        // base case(指针遍历到队尾)
        if(head==null||head.next==null){
            return head;
        }
        // 递归到尾节点
        ListNode last=reverseList(head.next);
        // 将尾节点的next指向该节点的头节点(递归节点的,前一个节点---就是当前节点的头节点)
        head.next.next=head;
        // 将尾节点的头节点指向null(递归节点的,前一个节点---就是当前节点的头节点)
        head.next=null;
        // 返回尾节点
        return last;
    }
}
// 法二:迭代法 空间复杂度低,优选与递归

8、LeetCode第92题 反转链表II(反转部分链表)【中等】

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {
        // base case
        if (left == 1) {
            return reverseN(head, right);
        }
        // 前进到反转的起点触发 base case
        
        /*如果 m != 1 怎么办?如果我们把 head 的索引视为 1,那么我们是想从第 m 个元素开始反转对吧;如		   果把 head.next 的索引视为 1 呢?那么相对于 head.next,反转的区间应该是从第 m - 1 个元素			  开始的;那么对于 head.next.next 呢……【m为left,n为right】*/
        /*如果left=1,直接反转前n个就行了;如果不是1的话就要把他变成1,就以next的方法后移*/
        head.next = reverseBetween(head.next, left - 1, right - 1);
        return head;
    }
    // 记录后驱节点
    ListNode successor=null;
    // 方法:反转前n个链表
    private ListNode reverseN(ListNode head,int n){
        // base case
        if(n==1){
            // 记录后驱节点(这里的头节点以最后一轮压栈,触发base case的reverseN为head)
            // 触发base case后直接出栈
            successor=head.next;
            return head;
        }
        // 递归
        ListNode last=reverseN(head.next,n-1);
        // 将目标尾节点指向前一个节点(递归开始由第n-1个head开始回溯)
        head.next.next=head;
        // 将最后一个节点指向后驱节点
        head.next=successor;
        // 
        return last;
    } 
}

===>2022.04.11 链表反转

70、爬楼梯

法一:递归=》

分析:

​ 一阶楼梯:仅一种方法;

​ 两阶楼梯:两种;

​ 刚开始走楼梯时,要么走一步要么走两步==》那么总种类数其实就是第一次走一步或者走两步两种方式相加的 总和

函数:

在这里插入图片描述

class Solution {
    // 1、使用递归的方式[运行超时,原因每轮递归有一些值会重复计算]
    // public int climbStairs(int n) {
    //     // base case
    //     if(n==1){
    //         return 1;
    //     }
    //     if(n==2){
    //         return 2;
    //     }
    //     return climbStairs(n-1)+climbStairs(n-2);
    // }

    // 2、优化解法1,利用缓存的方法
    private final static HashMap<Integer,Integer> NUM_POOL=new HashMap<>();
    public int climbStairs(int n) {
        // base case
        if(n==1){
            return 1;
        }
        if(n==2){
            return 2;
        }
        // 判断缓存中是否有值
        if(NUM_POOL.get(n)!=null){
            return NUM_POOL.get(n);
        }
        // 存放到缓存中
        int num=climbStairs(n-1)+climbStairs(n-2);
        NUM_POOL.put(n,num);
        return num;
    }
}

法二:循环=》

法三:=》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值