20. 有效的括号
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。
示例 1:
12>输入:s = "()">输出:true
示例 2:
12>输入:s = "()[]{}">输出:true
示例 3:
12>输入:s = "(]">输出:false
提示:
1 <= s.length <= 104
s 仅由括号 '()[]{}' 组成
解法一:使用堆栈
这个思考的过程其实就是栈的实现过程。因此我们考虑使用栈,当遇到匹配的最小括号对时,我们将这对括号从栈中删除(即出栈),如果最后栈为空,那么它是有效的括号,反 ...
21. 合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 1:
12输入:l1 = [1,2,4], l2 = [1,3,4]输出:[1,1,2,3,4,4]
示例 2:
12输入:l1 = [], l2 = []输出:[]
示例 3:
12输入:l1 = [], l2 = [0]输出:[0]
解法1:递归法
根据以上规律考虑本题目:
终止条件:当两个链表都为空时,表示我们对链表已合并完成。如何递归:我们判断 l1 和 l2 头结点哪个更小,然后较小结点的 next 指针指向其余结点的合并结果。(调用递归)
1234567891011121314class Solution { public ListNode mergeTwoLists(ListNode list1, ListNode list2) { if (list1==null)return list2; if (list2==null)return list1; if (lis ...
刷题笔记
未读假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
示例 1:
12345输入:n = 2输出:2解释:有两种方法可以爬到楼顶。1. 1 阶 + 1 阶2. 2 阶
示例 2:
123456输入:n = 3输出:3解释:有三种方法可以爬到楼顶。1. 1 阶 + 1 阶 + 1 阶2. 1 阶 + 2 阶3. 2 阶 + 1 阶
提示:
1 <= n <= 45
解法1:使用递归(容易超时)
123456class Solution { public int climbStairs(int n) { if (n <= 2)return n; return climbStairs(n-1) + climbStairs(n-2); }}
解法2:模拟法
相当于每次提升一个台阶都会让总体方法加一
12345678910111213class Solution { public int climbStair ...
79. 单词搜索
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例 1:
12输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"输出:true
示例 2:
12输入:board = [["A","B","C","E"],["S","F",&q ...
给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。
示例 1:
12输入:root = [1,null,2,3]输出:[1,3,2]
示例 2:
12输入:root = []输出:[]
示例 3:
12输入:root = [1]输出:[1]
提示:
树中节点数目在范围 [0, 100] 内
-100 <= Node.val <= 100
解法1:递归
直接使用递归进行操作比较简单,但是同样地也会消耗较多资源
12345678910111213141516class Solution { public List<Integer> inorderTraversal(TreeNode root) { List<Integer> temp = new ArrayList<Integer>(); inorder(root,temp); return temp; } public void inorder(TreeNode node, L ...
如何解决高并发场景下的消息丢失问题?该问题有可能会以这样的方式出现:高并发场景下如何保证消息只会被消费一次
又或者说多次消费的结果和只消费一次是相同的
为了保证能够只被消费一次,那么我们需要保证消费不会被丢失。首先服务器发送消息到达消费者的时候每一个环节都有可能出现消息丢失的情况
生产者出现消息丢失:
消息队列通常和生产者不在同一个服务器上,他们通过内网链接。如果出现网络波动则容易出现消息丢失。为了保证能够成功传输,需要在出现传输失败的时候能够进行重传,同时开启一个ACK确认机制。当消息队列成功接收到生产者的消息,需要同时回传一个ACK包给生产者标识已经成功接受,否则需要重新传输。
消息队列出现消息丢失的问题:
主要出现的原因是因为存在一个消息刷盘的过程,这里存在异步刷盘和同步刷盘的区别。如果开启了异步刷盘,那么接收到的消息会先存储到cache内存里面而没有存储进入磁盘。如果此时消息队列所在服务器出现重启(没有进行持久化),那么就会出现丢失。解决方案是使用同步刷盘机制。
消息队列到消费者之间出现消息丢失:
消费者没有消费完成提前将进度更新到消息队列
生产者发送多条重复的消息,消费者 ...
这里我们根据前面提到的这个抽象工厂进行代码编写
1. 定义抽象产品接口首先,我们定义手机和路由器的抽象接口。
123456789101112131415// 手机产品接口public interface IPhoneProduct { void start(); void shutdown(); void callup(); void sendSMS();}// 路由器产品接口public interface IRouterProduct { void start(); void shutdown(); void openWiFi(); void setting();}
2. 定义具体产品类接下来,我们实现这些接口,创建具体的产品类。
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071 ...
设计模式的6大基本原则
回答
“设计模式是软件开发中的一种经过验证的通用解决方案,旨在解决开发过程中常见的设计问题。它们提供了一套可复用的设计结构和方法,帮助开发者在设计系统时做出更好的决策。
设计模式的好处主要有:
提高代码复用性,使相同的解决方案可以应用于不同项目;
增强代码可维护性,通过清晰的结构和职责分离,简化了代码的维护;
提升系统的灵活性和可扩展性,使系统更容易适应变化和扩展;
促进团队协作,为团队提供了统一的语言和沟通基础。”
什么是设计模式设计模式是软件开发中一套被公认的、经过验证的最佳实践和解决方案,用于解决常见的设计问题。它们是面向对象编程中可复用的解决方案,在软件设计过程中,通过遵循设计模式,开发者可以创建更为结构化、可维护和扩展的软件。
设计模式可以被视为前人总结的“经验法则”,它们以一种可理解、可传达的方式来表达,并通过对模式的名称、意图、解决方案和应用场景的描述,帮助开发者更快、更好地解决设计难题。
设计模式的好处提高代码的复用性:设计模式提供了解决常见问题的通用方法,使得这些解决方案可以在多个项目中反复使用,减少了重复代码的出现。
增强代码的可维护性:通过使用设计模式,代码结 ...
刷题笔记
未读
适配器模式和装饰器模式都是结构型设计模式,它们的主要区别在于它们的目的和使用场景。虽然它们在某些情况下可能看起来相似,但它们解决的问题和应用方式是不同的。下面是它们的主要区别:
1. 目的不同
适配器模式:适配器模式的主要目的是解决接口不兼容的问题。它将一个类的接口转换为客户端希望的另一个接口,使得原本由于接口不兼容而不能一起工作的类能够协同工作。适配器模式通常用于将已有类的接口适配到新接口,以便与系统的其他部分一起工作。
装饰器模式:装饰器模式的主要目的是动态地扩展对象的功能。它通过将对象包装在一个装饰器对象中,增强或改变对象的行为,而无需修改对象的原始类。装饰器模式允许我们在运行时选择或组合多个装饰器,以灵活地为对象添加功能。
2. 结构和实现方式不同
适配器模式:
对象适配器:通过组合的方式实现,适配器类持有适配者类的引用。
类适配器:通过继承的方式实现,适配器类继承适配者类,同时实现目标接口。
适配器的作用是将不兼容的接口转化为兼容接口,以便客户端可以使用。
装饰器模式:
通过组合的方式实现,装饰器类持有组件对象的引用。
装饰器类实现与组件相同的接口,并在其方法中调用组 ...