栈
一个值是可选的拿来使用要强制解包 或者判空 .的时候可选的 一定要加?
232.用栈实现队列
-
你只能使用标准的栈操作 -- 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
-
void push(int x)
将元素 x 推到队列的末尾 -
int pop()
从队列的开头移除并返回元素 -
int peek()
返回队列开头的元素 -
boolean empty()
如果队列为空,返回true
;否则,返回false
栈只能用popLast() 函数
class MyQueue {
var stackIn = [Int]()
var stackOut = [Int]()
init() {
}
func push(_ x: Int) {
stackIn.append(x)
}
//
func pop() -> Int {
if stackOut.isEmpty {
while !stackIn.isEmpty {
stackOut.append(stackIn.popLast()!)
}
}
return stackOut.popLast() ?? -1
}
// 返回队列开头的元素
func peek() -> Int {
// 移除队列的最开始元素
var res = pop()
// 最开始元素移除了需要加到出栈的栈顶
stackOut.append(res)
return res
}
func empty() -> Bool {
return stackIn.isEmpty && stackOut.isEmpty
}
}
/**
* Your MyQueue object will be instantiated and called as such:
* let obj = MyQueue()
* obj.push(x)
* let ret
_2: Int = obj.pop()
* let ret_3: Int = obj.peek()
* let ret_4: Bool = obj.empty()
*/
225. 用队列实现栈
简单
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作
代码实现
// 用单个数组模拟队列
class Qunue {
var arr = [Int]()
init() {
}
func push(_ x: Int) {
arr.append(x)
}
func pop() -> Int {
return arr.removeFirst() ?? -1
}
func top() -> Int {
return arr.first ?? -1
}
func empty() -> Bool {
return arr.count == 0
}
func count() -> Int {
return arr.count
}
}
//2个队列模拟栈
class MyStack {
var qunueMain = Qunue()
var qunueTemp = Qunue()
init() {
}
func push(_ x: Int) {
qunueMain.push(x)
}
// 栈是先进后出
// 此处队列是先进先去
// 通过pop方法 拿到first
func pop() -> Int {
if qunueMain.empty() {
return -1
}
// 主队列不为空
while qunueMain.count() > 1 {
let res = qunueMain.pop()
// 主动列的后面拿出来,加入临时中
qunueTemp.push(res)
}
var res = qunueMain.pop()
while !qunueTemp.empty() {
qunueMain.push(qunueTemp.pop())
}
return res
}
func top() -> Int {
// 栈的顶部元素
var res = pop()
// 放到主队列的末尾 . 难点
qunueMain.push(res)
return res
}
func empty() -> Bool {
return qunueMain.empty() && qunueTemp.empty()
}
}
/**
* Your MyStack object will be instantiated and called as such:
* let obj = MyStack()
* obj.push(x)
* let ret_2: Int = obj.pop()
* let ret_3: Int = obj.top()
* let ret_4: Bool = obj.empty()
*/
20. 有效的括号
给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串 s
,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
示例 1:
输入: s = "()"
输出: true
示例 2:
输入: s = "()[]{}"
输出: true
示例 3:
输入: s = "(]"
输出: false
class Solution {
func isValid(_ s: String) -> Bool {
var arr = Array(s)
var res = [Character]()
for i in arr {
if i == "(" {
res.append(")")
} else if i == "[" {
res.append("]")
} else if i == "{" {
res.append("}")
} else {
if res.last == i {
res.removeLast()
} else {
return false
}
}
}
return res.isEmpty
}
}
1047. 删除字符串中的所有相邻重复项
leetcode.cn/problems/re…
给出由小写字母组成的字符串 S
,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
示例:
输入: "abbaca"
输出: "ca"
解释:
例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。
class Solution {
func removeDuplicates(_ s: String) -> String {
var array = Array(s)
var res = [Character]()
for i in array {
let last = res.last
if i == last {
res.removeLast()
} else {
res.append(i)
}
}
return String(res)
}
}
150. 逆波兰表达式求值
给你一个字符串数组 tokens
,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。
注意:
- 有效的算符为
'+'
、'-'
、'*'
和'/'
。 - 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
- 两个整数之间的除法总是 向零截断 。
- 表达式中不含除零运算。
- 输入是一个根据逆波兰表示法表示的算术表达式。
- 答案及所有中间计算结果可以用 32 位 整数表示。
示例 1:
输入: tokens = ["2","1","+","3","*"]
输出: 9
解释: 该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
示例 2:
输入: tokens = ["4","13","5","/","+"]
输出: 6
解释: 该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
class Solution {
func evalRPN(_ tokens: [String]) -> Int {
var stack = [Int]()
for i in tokens {
if isNumber(i) {
stack.append(Int(i)!)
} else {
let left = stack.removeLast()
let right = stack.removeLast() // 注意点 左右顺序
print("left right, \(left),\(right)")
// 注意点: switch 语句
switch i {
case "+":
let res = left + right
stack.append(res)
case "-":
let res = right - left
stack.append(res)
case "*":
let res = left * right
stack.append(res)
case "/":
// 错误 case语句
let res = right / left
stack.append(res)
default:
break
}
}
}
return stack.last!
}
// 错误 Bool
func isNumber(_ ch:String) -> Bool {
// 注意点 : 是不是数字
return Int(ch) != nil
}
}
347. 前 K 个高频元素
给你一个整数数组 nums
和一个整数 k
,请你返回其中出现频率前 k
高的元素。你可以按 任意顺序 返回答案。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
提示:
1 <= nums.length <= 105
k
的取值范围是[1, 数组中不相同的元素的个数]
- 题目数据保证答案唯一,换句话说,数组中前
k
个高频元素的集合是唯一的
进阶: 你所设计算法的时间复杂度 必须 优于 O(n log n)
,其中 n
**是数组大小。
class Solution {
func topKFrequent(_ nums: [Int], _ k: Int) -> [Int] {
// 出现的次数保存起来
var dic:Dictionary<Int,Int> = [:]
var arr:Array<Int> = []
for i in nums {
dic[i] = dic[i, default:0] + 1
}
// 对次数排序 ,注意点 字典的排序
var dicNew = dic.sorted{(d1,d2) -> Bool in
return d1.value > d2.value
}
// 拿到数据
for i in 0..<k {
arr.append(dicNew[i].key)
}
return arr
}
}
二叉树
看如下中间节点的顺序,就可以发现,中间节点的顺序就是所谓的遍历方式
- 前序遍历:中左右
- 中序遍历:左中右
- 后序遍历:左右中
二叉树的前序遍历
/**
* Definition for a binary tree node.
* public class TreeNode {
* public var val: Int
* public var left: TreeNode?
* public var right: TreeNode?
* public init() { self.val = 0; self.left = nil; self.right = nil; }
* public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; }
* public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) {
* self.val = val
* self.left = left
* self.right = right
* }
* }
*/
class Solution {
var arr: Array<Int> = []
func preorderTraversal(_ root: TreeNode?) -> [Int] {
if root == nil {
return []
}
traver(root)
return arr
}
func traver(_ root:TreeNode?) {
if root == nil {
return
}
arr.append(root!.val)
traver(root!.left)
traver(root!.right)
}
}
迭代
class Solution {
var arr: Array<Int> = []
var st: Array<TreeNode> = []
func preorderTraversal(_ root: TreeNode?) -> [Int] {
if root == nil {
return []
}
st.append(root!)
while !st.isEmpty {
let node = st.removeLast()
if node != nil {
arr.append(node.val)
}
if node.right != nil {
st.append(node.right!)
}
if node.left != nil {
st.append(node.left!)
}
}
return arr
}
}
后续遍历
class Solution {
var arr: Array<Int> = []
func postorderTraversal(_ root: TreeNode?) -> [Int] {
traver(root)
return arr
}
func traver(_ root: TreeNode?) {
if root == nil {
return
}
traver(root!.left)
traver(root!.right)
arr.append(root!.val)
}
}
class Solution {
var arr: Array<Int> = []
var st: Array<TreeNode> = []
func preorderTraversal(_ root: TreeNode?) -> [Int] {
if root == nil {
return []
}
st.append(root!)
while !st.isEmpty {
let node = st.removeLast()
if node != nil {
arr.append(node.val)
}
if node.right != nil {
st.append(node.right!)
}
if node.left != nil {
st.append(node.left!)
}
}
return arr
}
}
102. 二叉树的层序遍历
给你二叉树的根节点 root
,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
示例 1:
输入: root = [3,9,20,null,null,15,7]
输出: [[3],[9,20],[15,7]]
示例 2:
输入: root = [1]
输出: [[1]]
示例 3:
输入: root = []
输出: []
/**
* Definition for a binary tree node.
* public class TreeNode {
* public var val: Int
* public var left: TreeNode?
* public var right: TreeNode?
* public init() { self.val = 0; self.left = nil; self.right = nil; }
* public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; }
* public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) {
* self.val = val
* self.left = left
* self.right = right
* }
* }
*/
class Solution {
var result = [[Int]]()
func levelOrder(_ root: TreeNode?) -> [[Int]] {
guard root != nil else {
return []
}
var result = [[Int]]()
var qunue = [TreeNode]()
var nextQuene = [TreeNode]()
qunue.append(root!)
while qunue.count > 0 {
var temp = [Int]()
for node in qunue {
temp.append(node.val)
if node.left != nil {
nextQuene.append(node.left!)
}
if node.right != nil {
nextQuene.append(node.right!)
}
}
result.append(temp)
qunue.removeAll()
qunue = nextQuene
nextQuene.removeAll()
}
return result
}
}
226. 翻转二叉树
给你一棵二叉树的根节点 root
,翻转这棵二叉树,并返回其根节点。
示例 1:
输入: root = [4,2,7,1,3,6,9]
输出: [4,7,2,9,6,3,1]
示例 2:
输入: root = [2,1,3]
输出: [2,3,1]
示例 3:
输入: root = []
输出: []
提示:
- 树中节点数目范围在
[0, 100]
内 -100 <= Node.val <= 100
递归进行反转
// 前序遍历-递归
func invertTree(_ root: TreeNode?) -> TreeNode? {
guard let root = root else {
return root
}
let tmp = root.left
root.left = root.right
root.right = tmp
let _ = invertTree(root.left)
let _ = invertTree(root.right)
return root
}
迭代法进行反转
class Solution {
func invertTree(_ root: TreeNode?) -> TreeNode? {
if root == nil {
return nil
}
var qunue: Array<TreeNode> = []
qunue.append(root!)
while !qunue.isEmpty {
// 取出交换
let node : TreeNode = qunue.removeFirst()
(node.right, node.left) = (node.left, node.right)
if let node = node.left { qunue.append(node) }
if let node = node.right { qunue.append(node) }
}
return root!
}
}
101. 对称二叉树
简单
给你一个二叉树的根节点 root
, 检查它是否轴对称。
示例 1:
输入: root = [1,2,2,3,4,4,3]
输出: true
示例 2:
输入: root = [1,2,2,null,3,null,3]
输出: false
提示:
- 树中节点数目在范围
[1, 1000]
内 -100 <= Node.val <= 100
进阶: 你可以运用递归和迭代两种方法解决这个问题吗?
class Solution {
func isSymmetric(_ root: TreeNode?) -> Bool {
return compare(root!.left,root!.right)
}
func compare(_ leftRoot: TreeNode? , _ rightRoot: TreeNode? ) -> Bool {
if leftRoot == nil && rightRoot != nil {
return false
} else if leftRoot != nil && rightRoot == nil {
return false
}else if leftRoot == nil && rightRoot == nil {
return true
}else if(leftRoot!.val != rightRoot!.val) {
return false
}
// 数值相等 那么继续递归
let left = compare(leftRoot!.left,rightRoot!.right )
let right = compare(leftRoot!.right,rightRoot!.left)
return left&&right
}
}
class Solution {
func isSymmetric(_ root: TreeNode?) -> Bool {
if root == nil {
return true
}
var qunue:[TreeNode?] = []
// 注意点
qunue.append(root!.left)
qunue.append(root!.right)
while !qunue.isEmpty {
var left: TreeNode? = qunue.removeFirst()
var right: TreeNode? = qunue.removeFirst()
// 注意点
if left == nil && right == nil {
continue
}
if left == nil || right == nil || left?.val != right?.val {
return false
}
// 注意点
qunue.append(left!.left)
qunue.append(right!.right)
qunue.append(left!.right)
qunue.append(right!.left)
}
return true
}
}
104. 二叉树的最大深度
相关企业
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7]
,
3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。
class Solution {
func maxDepth(_ root: TreeNode?) -> Int {
if root == nil {
return 0
}
return 1 + max(maxDepth(root!.left),maxDepth(root!.right))
}
}
111.二叉树的最小深度
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
返回它的最小深度 2.
《代码随想录》算法视频公开课:看起来好像做过,一写就错! | LeetCode:111.二叉树的最小深度 (opens new window),相信结合视频在看本篇题解,更有助于大家对本题的理解。
看完了这篇104.二叉树的最大深度 (opens new window),再来看看如何求最小深度。
直觉上好像和求最大深度差不多,其实还是差不少的。
本题依然是前序遍历和后序遍历都可以,前序求的是深度,后序求的是高度。
- 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
- 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数后者节点数(取决于高度从0开始还是从1开始)
那么使用后序遍历,其实求的是根节点到叶子节点的最小距离,就是求高度的过程,不过这个最小距离 也同样是最小深度。
以下讲解中遍历顺序上依然采用后序遍历(因为要比较递归返回之后的结果,本文我也给出前序遍历的写法)。
本题还有一个误区,在处理节点的过程中,最大深度很容易理解,最小深度就不那么好理解,如图:
这就重新审题了,题目中说的是:最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 ,注意是叶子节点。
什么是叶子节点,左右孩子都为空的节点才是叶子节点!
class Solution {
func minDepth(_ root: TreeNode?) -> Int {
guard let root = root else {
return 0
}
var leftH = 0
var rightH = 0
if root.left == nil && root.right != nil {
return 1 + minDepth(root.right)
}
if root.right == nil && root.left != nil {
return 1 + minDepth(root.left)
}
return 1 + min(minDepth(root.left),minDepth(root.right))
}
}
迭代的解法
class Solution {
func minDepth(_ root: TreeNode?) -> Int {
guard let root = root else {
return 0
}
var qunue: Array<TreeNode> = []
qunue.append(root)
var res = 0
while !qunue.isEmpty {
res += 1
for _ in 0..<qunue.count {
var node: TreeNode = qunue.removeFirst()
if node.left == nil && node.right == nil {
return res
}
if node.left != nil {
qunue.append(node.left!)
}
if node.right != nil {
qunue.append(node.right!)
}
}
}
return res
}
}
222.完全二叉树的节点个数
给出一个完全二叉树,求出该树的节点个数。
示例 1:
- 输入:root = [1,2,3,4,5,6]
- 输出:6
示例 2:
- 输入:root = []
- 输出:0
示例 3:
- 输入:root = [1]
- 输出:1
提示:
- 树中节点的数目范围是[0, 5 * 10^4]
- 0 <= Node.val <= 5 * 10^4
- 题目数据保证输入的树是 完全二叉树
《代码随想录》算法视频公开课:要理解普通二叉树和完全二叉树的区别! | LeetCode:222.完全二叉树节点的数量 (opens new window),相信结合视频在看本篇题解,更有助于大家对本题的理解。
本篇给出按照普通二叉树的求法以及利用完全二叉树性质的求法。
首先按照普通二叉树的逻辑来求。
这道题目的递归法和求二叉树的深度写法类似, 而迭代法,二叉树:层序遍历登场! (opens new window)遍历模板稍稍修改一下,记录遍历的节点数量就可以了。
递归遍历的顺序依然是后序(左右中)。
如果对求二叉树深度还不熟悉的话,看这篇:二叉树:看看这些树的最大深度 (opens new window)。
- 确定递归函数的参数和返回值:参数就是传入树的根节点,返回就返回以该节点为根节点二叉树的节点数量,所以返回值为int类型。
代码如下:
int getNodesNum(TreeNode* cur) {
1
- 确定终止条件:如果为空节点的话,就返回0,表示节点数为0。
代码如下:
if (cur == NULL) return 0;
1
- 确定单层递归的逻辑:先求它的左子树的节点数量,再求右子树的节点数量,最后取总和再加一 (加1是因为算上当前中间节点)就是目前节点为根节点的节点数量。
代码如下:
int leftNum = getNodesNum(cur->left); // 左
int rightNum = getNodesNum(cur->right); // 右
int treeNum = leftNum + rightNum + 1; // 中
return treeNum;
class Solution {
func countNodes(_ root: TreeNode?) -> Int {
guard let root = root else {
return 0
}
let leftCount = countNodes(root.left)
let rightCount = countNodes(root.right)
return 1 + leftCount + rightCount
}
}
迭代法
class Solution {
func countNodes(_ root: TreeNode?) -> Int {
guard let root = root else {
return 0
}
var qunue = [root]
var res = 0
while !qunue.isEmpty {
for _ in 0..<qunue.count {
var node = qunue.removeFirst()
res += 1
if let node = node.left {
qunue.append(node)
}
if let node = node.right {
qunue.append(node)
}
}
}
return res
}
}
110.平衡二叉树
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
示例 1:
给定二叉树 [3,9,20,null,null,15,7]
返回 true 。
示例 2:
给定二叉树 [1,2,2,3,3,null,null,4,4]
返回 false 。
《代码随想录》算法视频公开课:后序遍历求高度,高度判断是否平衡 | LeetCode:110.平衡二叉树 (opens new window),相信结合视频在看本篇题解,更有助于大家对本题的理解。
咋眼一看这道题目和104.二叉树的最大深度 (opens new window)很像,其实有很大区别。
这里强调一波概念:
- 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数。
- 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数。
但leetcode中强调的深度和高度很明显是按照节点来计算的,如图:
关于根节点的深度究竟是1 还是 0,不同的地方有不一样的标准,leetcode的题目中都是以节点为一度,即根节点深度是1。但维基百科上定义用边为一度,即根节点的深度是0,我们暂时以leetcode为准(毕竟要在这上面刷题)。
因为求深度可以从上到下去查 所以需要前序遍历(中左右),而高度只能从下到上去查,所以只能后序遍历(左右中)
class Solution {
func countNodes(_ root: TreeNode?) -> Int {
guard let root = root else {
return 0
}
var qunue = [root]
var res = 0
while !qunue.isEmpty {
for _ in 0..<qunue.count {
var node = qunue.removeFirst()
res += 1
if let node = node.left {
qunue.append(node)
}
if let node = node.right {
qunue.append(node)
}
}
}
return res
}
}
257. 二叉树的所有路径
给定一个二叉树,返回所有从根节点到叶子节点的路径。
说明: 叶子节点是指没有子节点的节点。
示例:
《代码随想录》算法视频公开课:递归中带着回溯,你感受到了没?| LeetCode:257. 二叉树的所有路径 (opens new window),相信结合视频在看本篇题解,更有助于大家对本题的理解。
这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。
在这道题目中将第一次涉及到回溯,因为我们要把路径记录下来,需要回溯来回退一个路径再进入另一个路径。
前序遍历以及回溯的过程如图:
我们先使用递归的方式,来做前序遍历。要知道递归和回溯就是一家的,本题也需要回溯。 原文链接
class Solution {
var res = [String]()
func binaryTreePaths(_ root: TreeNode?) -> [String] {
guard let root = root else {
return []
}
var path = ""
path2(root, path)
return res
}
func path2(_ root:TreeNode? , _ path: String ) {
var path = path
guard let root = root else {
return
}
// 找到路劲
if root.left == nil && root.right == nil {
// 最终节点加到数组中
path += String(root.val)
res.append(path)
return
}
var pathNew = path + String(root.val) + "->"
path2(root.left, pathNew)
path2(root.right, pathNew)
}
}