前言
此文章为本人第一次讲解算法,可能有些不完美,欢迎大家交流与指正
原题目
题目解析:
简单来说,这个题目就像是在检查一个人说话时用的括号是否正确一样。比如,如果说:“我今天(去超市买了[很多东西],然后回家)做饭。”这里的括号就是正确使用的,因为每种类型的括号都正确配对,并且按照正确的顺序闭合。如果括号使用错误,比如:“我今天(去超市买了[很多东西回家)做饭。”,那么括号就没有正确配对,说话的人可能需要重新组织语言。这个程序就是要自动检查这种括号的使用是否正确。
前置知识点:
Stack(栈):
栈(Stack)是一种基本的数据结构,它遵循后进先出(Last In, First Out,简称LIFO)的原则。这个概念在计算机科学中非常重要,因为它在很多算法和编程语言中都有应用。以下是栈的一些关键特点:
-
插入点固定:栈只允许在一端(称为“栈顶”,top)进行数据的添加或删除操作。
-
后进先出:最后添加到栈中的元素将是第一个被移除的元素。这就像一叠盘子,你总是先拿走最上面的盘子。
-
主要操作:
- 入栈(Push):将一个元素添加到栈顶。
- 出栈(Pop):移除并返回栈顶的元素。
- 查看栈顶元素(Peek/Top):查看但不移除栈顶的元素。
-
容量:有些栈有固定的大小,称为静态栈;而有些栈可以动态增长,称为动态栈。
-
应用场景:
- 函数调用:在编程中,每次函数调用时,新的函数调用会被压入一个隐式的“调用栈”中。
- 括号匹配:检查代码或文本中的括号是否正确配对。
- 撤销操作:在编辑器或绘图软件中,允许用户撤销最近的一系列操作。
-
实现方式:栈可以用数组或链表来实现。数组提供了快速的访问和修改,而链表则允许动态地增加栈的大小。
-
特点:栈是有序的,但不支持随机访问。你不能直接访问栈中间的元素,只能访问栈顶元素。
-
变体:有些栈的变体允许在两端添加或移除元素,称为双端栈(Deque)。
栈是一种非常有用的数据结构,因为它的简单性和在许多问题中自然出现的LIFO特性。在编程和算法设计中,栈经常被用来解决实际问题,比如解析表达式、处理回溯问题等。
.Map()
在JavaScript中,Map
是一种集合类型,用于存储键值对的集合,其中键可以是任何类型。Map
对象在ES6(ECMAScript 2015)中被引入,提供了一种更现代和灵活的方式来处理键值对集合,相较于传统的对象({}
),它有以下几个特点:
-
键的多样性:
- 键可以是任何类型,包括对象、函数、Symbol等,而不仅仅是字符串或Symbol。
-
保持插入顺序:
Map
对象中的元素会按照它们被添加的顺序进行迭代。
-
性能:
- 对于频繁增删键值对的场景,
Map
通常提供比传统对象更好的性能。
- 对于频繁增删键值对的场景,
-
内置方法:
Map
提供了多种方法来操作集合,例如:.set(key, value)
:添加一个新的键值对或更新现有键的值。.get(key)
:根据键获取对应的值。.has(key)
:检查Map中是否包含某个键。.delete(key)
:删除指定的键值对。.clear()
:移除Map中的所有键值对。.size
:返回Map中元素的数量。
-
迭代器:
Map
对象是可迭代的,可以使用for...of
循环或通过.keys()
、.values()
、.entries()
方法进行迭代。
-
弱引用Map:
- 有一种特殊的
Map
叫做WeakMap
,它允许键是对象,并且当键不再被其他引用引用时,该键值对会自动从WeakMap
中移除。
- 有一种特殊的
-
灵活性:
- 可以轻松地将
Map
转换为数组,例如使用Array.from(map)
或扩展运算符...
。
- 可以轻松地将
-
用途:
- 用于需要快速查找、添加和删除键值对的场景,尤其是在键的类型多样或需要保持元素顺序时。
答案(源码及详细注释)
核心思想
这段代码是一个用于检查字符串中括号是否正确闭合的函数,其核心思想是使用栈(Stack)数据结构来跟踪最近遇到的开括号,并按照顺序检查闭括号是否正确匹配。以下是核心思想的详细解释:
-
映射关系建立:
- 使用
Map
对象建立一个映射关系,将每一种开括号('(', '[', '{')映射到对应的闭括号(')', ']', '}')。
- 使用
-
栈的初始化:
- 创建一个空数组
stack
作为栈使用,用于存储尚未匹配的闭括号。
- 创建一个空数组
-
遍历字符串:
- 遍历输入字符串
s
的每个字符。
- 遍历输入字符串
-
处理开括号:
- 如果当前字符是开括号,根据映射关系找到对应的闭括号,并将其推入栈中。
-
匹配闭括号:
- 如果当前字符是闭括号:
- 尝试从栈中弹出一个元素(
pop()
)。 - 如果栈为空(没有对应的开括号),或者弹出的元素与当前闭括号不匹配,则字符串无效,返回
false
。
- 尝试从栈中弹出一个元素(
- 如果当前字符是闭括号:
-
检查栈是否为空:
- 遍历结束后,如果栈不为空,说明有未匹配的开括号,返回
false
。 - 如果栈为空,说明所有的开括号都找到了对应的闭括号,字符串有效,返回
true
。
- 遍历结束后,如果栈不为空,说明有未匹配的开括号,返回
-
优化:
- 最后两个
if
语句检查栈是否为空,实际上只需要一个。如果栈不为空,直接返回false
;否则,返回true
。
- 最后两个
核心思想的关键在于使用栈来跟踪需要匹配的开括号,并确保它们按照正确的顺序与闭括号配对。这种算法利用了栈的后进先出(LIFO)特性,确保了最后遇到的开括号最先被匹配,这与括号匹配的自然顺序相符合。
通过这种方式,算法能够有效地检查字符串中的括号是否有效闭合,满足题目要求的三个条件:相同类型的左括号必须用相同类型的右括号闭合,左括号必须以正确的顺序闭合,每个右括号都必须有一个对应的相同类型的左括号。