代码随想录算法训练营day04 | leetcode
基础知识
记录一下栈实现及操作
public class ArrayDequeStack
{
    public void main()
    {
        ArrayDeque stack = new ArrayDeque();
        // 大小
        System.out.println(stack.size());
        // 依次将三个元素push入"栈"
        stack.push("循循渐进Linux");
        stack.push("小学语文");
        stack.push("时间简史");
        // 输出:[时间简史, 小学语文, 循循渐进Linux]
        System.out.println(stack);
        // 访问第一个元素,但并不将其pop出"栈",输出:时间简史
        System.out.println(stack.peek());
        // 依然输出:[时间简史, 小学语文, 循循渐进Linux]
        System.out.println(stack);
        // pop出第一个元素,输出:时间简史
        System.out.println(stack.pop());
        // 输出:[小学语文, 循循渐进Linux]
        System.out.println(stack);
    }
}
记录一下Map常用操作
HashMap<String, String > myMap  = new HashMap<String, String>(){{
    put("a","b");
    put("b","b");
}};
{
    Object object = new Object();
    Object key = new Object();
    Object value = new Object();
    Map map1 = new HashMap<>();
    Map map2 = new TreeMap();
    Map map3 = new LinkedHashMap();
    map1.get(key);
    map1.put("string", object);
    map1.putIfAbsent(1, object);
    map1.remove(key);
    map1.isEmpty();
    map1.containsKey(key);
    map1.containsValue(value);
    map1.getOrDefault(13, 0);
    map1.replace(key, value, value);
    // 存不存在key 或 value
    // 如果指定的键尚未与某个值相关联(或映射到 null )将其与给定值相关联并返回 null ,否则返回当前值。
    for (Object key1 : map1.keySet()) {
        Object value1 = map1.get(key);
        System.out.println(key1 + " = " + value1);
    }
    // 根据泛型的类型判断该如何进行排序 直接根据key 或者 根据key获取value进行lambda比较器排序
    Map<Character, Integer> map = new HashMap<Character, Integer>();
    List<Character> list = new ArrayList<Character>(map.keySet());
    // 按value 降序排序
    Collections.sort(list, (a, b) -> map.get(b) - map.get(a));
    // 按key 降序排序
    Collections.sort(list, (a, b) -> b.compareTo(a));
}
LeetCode 24
分析1.0
交换相邻的两个节点,需要两个指针 pre p,不修改节点内部的值,意味着只改变原节点next指向,
- 空链 或链中只有一个节点 直接返回
 - ans指向 第一对交换后的相邻节点左侧
 - 循环终止条件只剩下0或1个元素,可退出终止
 
失误
- 只考虑到了仅有两个元素的交换,如果有3个以上元素的交换,还需要设置last指针指向已交换完成的链表段的最后一个元素
 - 应将链表分为三部分 ①已操作好链表段 last指向这段尾节点 ②待交换的2个节点③剩余链表段 first指向这段首节点
 - if(p1.next == null || p1 == null) 就近原则可能报错
 
class Solution {
    public ListNode swapPairs(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        ListNode ans = new ListNode(-1);
        ListNode p1 = head, p2 = head.next, first, last = new ListNode(-2);
        // 至少有两个节点
        while(true){
            first = p2.next;
            p2.next = p1;
            p1.next = first;
            last.next = p2;
            last = p1;
            if(ans.val == -1){
                ans = p2;
            }
            if(first == null){
                break;
            }
            p1 = first;
            p2 = p1.next;
            if(p1== null || p2 == null){
                break;
            }
        }
        return ans;
    }
}
分析2.0
应将链表分为三部分 ①已操作好链表段 last指向这段尾节点 ②待交换的2个节点③剩余链表段 first指向这段首节点
class Solution {
    public ListNode swapPairs(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        ListNode ans = new ListNode(-1);
        ListNode p1 = head, p2 = head.next, first = p2.next, last = new ListNode(-1,head);
        // 至少有两个节点
        while(true){
            // last.next 指向p2 p2指向p1 p1指向first
            last.next = p2;
            p2.next = p1;
            p1.next = first;
            last = p1;
            if(ans.val == -1){
                ans = p2;
            }
            p1 = last.next;
            if(p1 == null || p1.next == null){
                break;
            }
            p2 = p1.next;
            first = p2.next;
        }
        return ans;
    }
}
LeetCode 19
分析1.0
要求删除链表的倒数第n个节点 思路:设置两个指针,p pre, 间隔n个元素,当p = null 时pre.next = 待删节点
失误 遇到一个奇怪的点 pre.next = head pre.next=pre.next.next head竟然没变
订正 确实是没变
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if(head == null){
            return null;
        }
        // p最终要指向的是最后一个节点 所以循环终止条件是p.next != null
        ListNode pre = new ListNode(101,head), p = head;
        int cnt = 1;
        while(cnt < n && p != null){
            p = p.next;
            cnt++;
        }// 遍历结束时,p指针指向第n个节点 pre.next = head
        System.out.println("p----"+p.val);
        System.out.println("pre----"+pre.val);
        while(p.next != null){
            System.out.println("循环");
            p = p.next;
            pre = pre.next;
        }
        // pre指向倒数第n+1个节点
        System.out.println(pre.next == head);
        pre.next = pre.next.next;
        System.out.println(pre.next == head);
        System.out.println("head----"+head.val);
        System.out.println("pre.next----"+pre.next);
        return head;
    }
}
修改 返回虚拟头结点的next
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if(head == null){
            return null;
        }
        // p最终要指向的是最后一个节点 所以循环终止条件是p.next != null
        ListNode pre = new ListNode(101,head), p = head, virtualNode = pre;
        int cnt = 1;
        while(cnt < n && p != null){
            p = p.next;
            cnt++;
        }// 遍历结束时,p指针指向第n个节点 pre.next = head
        while(p.next != null){
            p = p.next;
            pre = pre.next;
        }
        // pre指向倒数第n+1个节点
        pre.next = pre.next.next;
        return virtualNode.next;
    }
}
分析2.0
p可走n+1步后,pre接着走,这样pre直接就指向了待删除元素的前一个
LeetCode 面试题 02.07. 链表相交
分析1.0
这题一看过去首先想到的就是要逆向遍历,但是又要求不能改变原来链表的结构,便用两个list装下所有数据完成转置,接着开始遍历两个list,找到最后一个相等节点,
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB == null){
            return null;
        }
        ListNode p1 = new ListNode(-1,headA), p2 = new ListNode(-2,headB);
        List<ListNode> list1 = new LinkedList(), list2 = new LinkedList();
        while(p1.next != null){
            list1.add(p1.next);
            p1 = p1.next;
        }
        while(p2.next != null){
            list2.add(p2.next);
            p2 = p2.next;
        }
        /*if(headA.val != headB.val){
            System.out.println("尾节点不同");
            return null; // headA headB是始终不变的这么比比的是原链表的首节点
        }*/
        // 通过日志发现了问题 压根儿就没有转置
        for(int i = 0; i < list1.size(); i++){
            System.out.println(list1.get(i).val);
        }
        int cnt = -1;
        while(list1.get(cnt+1) == list2.get(cnt+1) && list1.get(cnt+1) != null && list2.get(cnt+1) != null){
            System.out.println(list1.get(cnt+1));
            cnt++;
        }
        return list1.get(cnt);
    }
}
失误:真是闹了个乌龙
分析2.0
直接插入采用的是尾插法,根本就没有实现元素转置 转置可用栈 可惜超出内存限制了
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB == null){
            return null;
        }
        ListNode p1 = new ListNode(-1,headA), p2 = new ListNode(-2,headB);
        ArrayDeque<ListNode> stack1 = new ArrayDeque(), stack2 = new ArrayDeque();
        while(p1.next != null){
            stack1.push(p1.next);
        }
        while(p2.next != null){
            stack2.push(p2.next);
        }
        // 开始比较选出首个不等节点
        ListNode ans = null;
        while(true){
            p1 = stack1.pop();
            p2 = stack2.pop();
            if(p1.val != p2.val){
                break;
            }
            ans = p1;
        }
        return ans;
    }
}
分析3.0
看了卡哥的思路,发现自己还是没有准确理解节点相等这个要求
将链表以当前遍历节点为基准分为三段,若A B指向了相同的节点,那之后的那一段一定是相同的 而比较节点是否相同比较的也是ListNode这个引用是否相等(和数组的区别之一)
于是① 获取A B长度,移动长链表头指针headA至以新的headA为头结点的链表长度等于原短链表的长度,开始比较,节点引用相等的点为所求
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode curA = headA;
        ListNode curB = headB;
        int lenA = 0, lenB = 0;
        while (curA != null) { // 求链表A的长度
            lenA++;
            curA = curA.next;
        }
        while (curB != null) { // 求链表B的长度
            lenB++;
            curB = curB.next;
        }
        curA = headA;
        curB = headB;
        // 让curA为最长链表的头,lenA为其长度
        if (lenB > lenA) {
            //1. swap (lenA, lenB);
            int tmpLen = lenA;
            lenA = lenB;
            lenB = tmpLen;
            //2. swap (curA, curB);
            ListNode tmpNode = curA;
            curA = curB;
            curB = tmpNode;
        }
        // 求长度差
        int gap = lenA - lenB;
        // 让curA和curB在同一起点上(末尾位置对齐)
        while (gap-- > 0) {
            curA = curA.next;
        }
        // 遍历curA 和 curB,遇到相同则直接返回
        while (curA != null) {
            if (curA == curB) {
                return curA;
            }
            curA = curA.next;
            curB = curB.next;
        }
        return null;
    }
}
分析4.0
- 抽象思维 链表分3段
 - 明确所求结果落实到链表状态上是个什么情形
 - 从结果倒推循环起点
 
LeetCode 142
分析1.0
从图的出入度考虑,入环节点入度为2,简历<ListNode,<index,int>>哈希表,访问到某一节点时,入度+1,如果当前节点入度为2,返回当前节点index,若节点为null,return -1
但是这样会报错
HashSet<ListNode, <Integer,Integer>> set = new HashSet();
好吧 分析1.0都是错的
订正
要使用的结构为Map
public class Solution {
    public ListNode detectCycle(ListNode head) {
        HashMap<ListNode, Integer> map = new HashMap();// 节点,索引
        ListNode cur = head;
        int index = 0;
        while(true){
            if(cur == null){
                return null;
            }
            if(map.containsKey(cur)){
                return cur;
            }else{
                map.put(cur,index++);
            }
            cur = cur.next;
        }
    }
}
分析2.0
这题其实用不着记录入度,用Set<ListNode> 就行,遍历时查重即可
分析3.0
空间复杂度为O(1) 考虑双指针 滑动窗口这类思想
快慢指针 a指针一次走两步,b指针一次走一步,相当于a指针一次走一步b指针没动 这个思路Mark一下
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {// 有环
                ListNode index1 = fast;
                ListNode index2 = head;
                // 两个指针,从头结点和相遇结点,各走一步,直到相遇,相遇点即为环入口
                while (index1 != index2) {
                    index1 = index1.next;
                    index2 = index2.next;
                }
                return index1;
            }
        }
        return null;
    }
}
参考卡哥 https://programmercarl.com/0142.%E7%8E%AF%E5%BD%A2%E9%93%BE%E8%A1%A8II.html

总结
- 总结一个做题抽象思维,把数据看成几个整体,整体之间如何联系、整体之内如何联系
 - if(p1.next == null || p1 == null) 就近原则可能报错
 - ListNode pre = new ListNode(5,head) 自定义val值时考虑题目限制 我这里设为负数被系统强制改为绝对值
 - 链表题考虑虚拟头结点!!!
 - pre.next = head pre.next=pre.next.next head是不会变的,一直指向那块内存区域 而链表的结构是发生了变化的
 - 搞清循环终止条件是 p != null 还是 p.next !=null 计数器定位可采取特殊法
 - 删除节点定位它的前一个节点
 - 特殊情况一定要优先考虑,有时候不光考虑一个结构有无元素 返回的结果要留意是否为null
 - 循环是重点 循环的其实条件 终止条件 注意循环前变量值循环后变量值
 - 快慢指针 a指针一次走两步,b指针一次走一步,相当于a指针一次走一步b指针没动
 
常用变量名增量更新
size、val、ans、cnt、cur、pre、next、left、right、index、gap
代码随想录算法训练营day04 | leetcode的更多相关文章
- 【算法训练营day4】LeetCode24. 两两交换链表中的结点 LeetCode19. 删除链表的倒数第N个结点 LeetCode面试题 02.07. 链表相交 LeetCode142. 环形链表II
		
[算法训练营day4]LeetCode24. 两两交换链表中的结点 LeetCode19. 删除链表的倒数第N个结点 LeetCode面试题 02.07. 链表相交 LeetCode142. 环形链表 ...
 - 【算法训练营day7】LeetCode454. 四数相加II LeetCode383. 赎金信 LeetCode15. 三数之和 LeetCode18. 四数之和
		
[算法训练营day7]LeetCode454. 四数相加II LeetCode383. 赎金信 LeetCode15. 三数之和 LeetCode18. 四数之和 LeetCode454. 四数相加I ...
 - 【算法训练营day1】LeetCode704. 二分查找 LeetCode27. 移除元素
		
[算法训练营day1]LeetCode704. 二分查找 LeetCode27. 移除元素 LeetCode704. 二分查找 题目链接:704. 二分查找 初次尝试 看到题目标题是二分查找,所以尝试 ...
 - 【算法训练营day8】LeetCode344. 反转字符串 LeetCode541. 反转字符串II 剑指Offer05. 替换空格 LeetCode151. 翻转字符串里的单词 剑指Offer58-II. 左旋转字符串
		
[算法训练营day8]LeetCode344. 反转字符串 LeetCode541. 反转字符串II 剑指Offer05. 替换空格 LeetCode151. 翻转字符串里的单词 剑指Offer58- ...
 - 【算法题 14 LeetCode 147 链表的插入排序】
		
算法题 14 LeetCode 147 链表的插入排序: 解题代码: # Definition for singly-linked list. # class ListNode(object): # ...
 - 代码随想录第十三天 | 150. 逆波兰表达式求值、239. 滑动窗口最大值、347.前 K 个高频元素
		
第一题150. 逆波兰表达式求值 根据 逆波兰表示法,求表达式的值. 有效的算符包括 +.-.*./ .每个运算对象可以是整数,也可以是另一个逆波兰表达式. 注意 两个整数之间的除法只保留整数部分. ...
 - 代码随想录第八天 |344.反转字符串 、541. 反转字符串II、剑指Offer 05.替换空格 、151.翻转字符串里的单词 、剑指Offer58-II.左旋转字符串
		
第一题344.反转字符串 编写一个函数,其作用是将输入的字符串反转过来.输入字符串以字符数组 s 的形式给出. 不要给另外的数组分配额外的空间,你必须原地修改输入数组.使用 O(1) 的额外空间解决这 ...
 - Manacher算法学习笔记 | LeetCode#5
		
Manacher算法学习笔记 DECLARATION 引用来源:https://www.cnblogs.com/grandyang/p/4475985.html CONTENT 用途:寻找一个字符串的 ...
 - 程序员进阶之算法练习:LeetCode专场
		
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由落影发表 前言 LeetCode上的题目是大公司面试常见的算法题,今天的目标是拿下5道算法题: 题目1是基于链表的大数加法,既考察基本 ...
 - 【算法题目】Leetcode算法题思路:两数相加
		
在LeetCode上刷了一题比较基础的算法题,一开始也能解出来,不过在解题过程中用了比较多的if判断,看起来代码比较差,经过思考和改进把原来的算法优化了. 题目: 给出两个 非空 的链表用来表示两个非 ...
 
随机推荐
- pandas中loc和iloc的使用细节
			
1.缘由 前段时间在使用pandas库中的索引和切片的时候,突然就感觉有点懵,赋值和索引的操作总是报错. 网上的很多资料讲的也非常的浅显,而且使用起来非常不顺手. 于是我就找到很多的网上资料,然后自己 ...
 - C#从实习到搬砖
			
日常唠唠 没事就聊聊我在c#上踩过的那些坑,和一些笔记 少点比较,多些谦虚 会者不难 原博:轩先生大冒险 2022.4.19 datagridview 修改表头 dataGridView1.Colum ...
 - Django静态文件配置、form表单、request对象、连接数据库、ORM
			
目录 静态文件配置 静态文件相关配置 1.接口前缀 浏览器停用缓存 2.接口前缀动态匹配 form表单 action 控制数据提交的地址 method 控制数据提交的方法 请求方法补充 get: 朝服 ...
 - TCPView工具
			
TCPView:一个查看端口和线程的小工具.(不需安装) 主界面: 启动程序之后,你就发现TCPView将你目前在使用的所有进程都列举出来了,并时不时的会用红.黄.绿三种颜色标注某些进程: 红色代表该 ...
 - nuxt.js框架 如何打包 build
			
nuxt脚手架开发好项目后怎么打包 以下是脚手架的package.json部分代码 "scripts": { "dev": "cross-env NO ...
 - [图像处理] YUV图像处理入门4
			
9 yuv420图像截取 本程序中的函数主要是对YUV420P视频数据流的第一帧图像进行截取.类似opencv中的rect函数,函数的代码如下所示: /** * @file 9 yuv_clip.cp ...
 - [常用工具] Python视频处理库VidGear使用指北
			
VidGear是一个高性能的Python视频处理库,它在预载多个专业视频图像处理库的基础上,如OpenCV.FFmpeg.ZeroMQ.picamera.starlette.yt_dlp.pyscre ...
 - Android applink 踩坑指南
			
Android applink 踩坑指南 原理 接入步骤 将链接与activity关联起来 加入meta data 生成身份验证JSON 真机测试 结论 官方文档 原理 与url scheme不同的地 ...
 - NC14501 大吉大利,晚上吃鸡!
			
题目链接 题目 题目描述 最近<绝地求生:大逃杀>风靡全球,皮皮和毛毛也迷上了这款游戏,他们经常组队玩这款游戏. 在游戏中,皮皮和毛毛最喜欢做的事情就是堵桥,每每有一个好时机都能收到不少的 ...
 - Spark通信框架RPC介绍
			
Spark通信框架RPC介绍 内容安排: 1.RPC原理 2.nio操作 3.netty简单的api 4.自定义RPC框架 RPC原理学习 什么是RPC RPC(Remote Procedure Ca ...