1. 数组
数组是最基本的数据结构,在swift中以前Objcective-C中将NSMutableArray 和NSArray份额开的做法被统一到了唯一的数据结构Array。
// 声明不可变数组
let nums = [1,2,3]
let strs:[String] = ["1","2","3"];
// 声明可变数组
var nums = [Int](repeating: 0, count: 5)
nums.append(4)
nums.append(contentsOf: [1,2,3])
// 对原数组进行升序排序
nums.sort()
let anotherNums = Array(nums[0..<nums.count - 1])
用数组实现栈
class Stack<Element> {
var stack: [Element]
var isEmpty:Bool { return stack.isEmpty}
var peek:Element? {return stack.last}
init(){
stack = [Element]()
}
func push(object:Element) {
stack.append(object)
}
func pop()->Element? {
if(stack.isEmpty) {
return nil
} else {
return stack.removeLast()
}
}
}
2. 字典和集合
集合
let primeNums: Set = [3,5,7,11,13]
let oddNums: Set = [1,3,5,7,9]
// 交并差集
let primeAddOddNums = primeNums.intersection(oddNums)
let primeOrOddNums = primeNums.union(oddNums)
let oddNotPrimNums = oddNums.subtracting(oddNums)
- 给出一个整形数组和一个目标值,判断数组中是否有两个数之和等于目标值
func twoSum1(nums:[Int],_ target: Int) -> Bool {
var set = Set<Int>()
for num in nums {
if set.contains(target - num) {
return true
}
set.insert(num)
}
return false
}
- 给出一个整形数组有且仅有两个数之和等于目标值,求这两个数咋数组中的序号
func twoSum2(nums:[Int],_ target: Int) -> [Int] {
var dict = Dictionary<Int,Int>()
for (num, i) in nums.enumerated() {
if let lastIndex = dict[target-num] {
return [lastIndex,i]
}
dict[num] = i;
}
fatalError("No valid output!")
}
3. 字符串
字符串在算法中极其常见,在Swift中,字符串不同于其他语言,它是值类型,而非引用类型。
var str = "3"
let num = Int(str)
let number = 3
let string = String(describing: num)
// 字符串长度
let len = str.count
// 访问单个字符
//let char = str[str.index(str.startIndex, offsetBy: n)]
// 修改字符串
//str.remove(at: n)
str.append("c")
str += "hello world"
// 检测字符串是否由数字组成
func isStrNum(str:String) ->Bool {
return Int(str) != nil;
}
// 将字符串按照字母顺序不考虑大小写
func sortStr(str:String) -> String {
return String(str.sorted())
}
给出一个字符串,要求将其按照单词顺序进行反转。
func reverseString(_ s: inout [Character]) {
if s.count == 0 {
return
}
s = s.reversed()
}
func reverseString1(_ s: inout [Character]) {
if s.count == 0 {
return
}
var x = 0
var y = s.count - 1
while(x < y){
s.swapAt(x, y)
x += 1
y -= 1
}
}
3. 链表
常见的数据结构,不再赘述。
// 链表节点
class ListNode {
var val:Int
var next:ListNode?
init(_ val:Int) {
self.val = val
self.next = nil
}
}
class List {
var head:ListNode?
var tail:ListNode?
//尾插法
func appendToTail(_ val:Int) {
if tail == nil {
tail = ListNode(val)
} else {
tail!.next = ListNode(val)
tail = tail!.next;
}
}
// 头插法
func appendToHead(_ val:Int) {
if head == nil {
head = ListNode(val)
} else {
let temp = ListNode(val)
temp.next = head
head = temp;
}
}
}
- 给出一个链表和一个值x,要求将链表中所有小于x的值放到左边,所有大于x的值放到右边,并且原链表的节点顺序不能变。 例如 1->5->3->2->4->2 给定2 返回1->2->2->5->3->4
func partition(_ head:ListNode? , _ x:Int) ->ListNode? {
// 引入Dummy节点
let preDummy = ListNode(1),postDummy = ListNode(0)
var prev = preDummy, post = postDummy
var node = head
while node != nil {
if (node!.val < x) {
prev.next = node;
prev = node!;
}else {
post.next = node;
post = node!;
}
node = node!.next;
}
// 防止构成环
post.next = nil
// 左右拼接
prev.next = postDummy.next;
return preDummy.next
}
- 快慢指针,如何检测一个链表中是否有环?
func hasCycle(_ head:ListNode?)-> Bool {
var slow = head,fast = head
while fast != nil && fast?.next != nil {
fast = fast!.next!.next;
slow = slow!.next
if slow === fast {
return true
}
}
return false
}
3.删除链表中倒数第k个节点 例: 1->2->3->4->5,n=2, 返回1->2->4->5 思路:快慢指针 但是速度一样, 一开始第一个指针落后第二个指针n个节点,接着二者同时移动,当第二个指针移动到尾结点时,第一个节点的下一个节点就是我们要删除的节点
func removeNthFromEnd(_ head: ListNode? ,_ n: Int)-> ListNode? {
//安全处理
guard let head = head else {
return nil
}
let dummy = ListNode(0)
dummy.next = head
var pre:ListNode? = dummy
var post:ListNode? = dummy
// 1. 提前走N步
for _ in 0..<n {
if post == nil {
break
}
post = post!.next
}
// 2. 同时移动快慢指针
while post != nil && post?.next != nil {
post = post!.next
pre = pre!.next
}
// post== nil || 或者是最后一个节点
// 删除对应节点
pre!.next = pre!.next!.next
return dummy.next
}
- 链表逆序 两种方法:
- 头插法(创建新节点,代表返回的链表,不断遍历链表,头插入这条链表)
- 直接置换法 (思路同方法一) 只不过是 没有创建新节点
func reverse(head:ListNode?) -> ListNode?{
//实现思路
// 1. 头插法
let temp = ListNode()
var cur = head
var next = cur
while(cur != nil) {
next = cur!.next
// 头插法
cur!.next = temp.next
temp.next = cur
cur = next
}
return temp.next
}
直接替换法
func reverse(head:ListNode?) -> ListNode?{
// 直接倒置法
var pre:ListNode? = nil
var cur = head
var next = head
while (cur != nil) {
next = cur?.next
cur?.next = pre
pre = cur
cur = next
}
return pre
}
- 以K为一组反转链表 1->2->3->4->5 k =2 输出 5->2->3->4->1 k =3 输出 4->5->1->2->3
// 以K个节点为一组进行链表反转
func reverseKthList(head :ListNode?, k : Int) -> ListNode? {
// 实现思路:跟倒序是一样的,只不过是 以K为一组,保存下一个第K个位置防止断掉 保存下一个位置
var cur = head
var next = head
let temp:ListNode = ListNode()
while (cur != nil) {
let (tail,hasMore) = nextKthNode(head: cur, k: k-1)
next = tail?.next
tail?.next = temp.next
temp.next = cur
cur = next;
if (hasMore == false) {
break;
}
}
return temp.next
}
func nextKthNode(head :ListNode? , k: Int) ->(ListNode? ,Bool){
var times: Int = k
var cur = head
while (times > 0 && cur?.next != nil) {
cur = cur?.next
times -= 1
}
return (cur,times == 0)
}
直接替换法: 核心是保留链表前后节点,然后四步走.
// 以K个节点为一组进行链表反转
func reverseKthList(head :ListNode?, k : Int) -> ListNode? {
var cur = head
var pre:ListNode? = nil
while (cur != nil) {
let temp = cur;
let tail = nextKthNode(head: cur, k: k-1).0
let next = tail?.next
tail?.next = pre
pre = temp
cur = next
}
return pre
}
func nextKthNode(head :ListNode? , k: Int) ->(ListNode? ,Bool){
var times: Int = k
var cur = head
while (times > 0 && cur?.next != nil) {
cur = cur?.next
times -= 1
}
return (cur,times == 0)
}
4. 栈和队列
栈是一个非常重要的数据结构,比如对于一些递归算法,也是通过栈来完成,通过栈,也能模拟递归的调用过程。然而,swift中没有内设的栈和队列,很多扩展库中使用Generic Type 来实现栈和队列。
无论是在面试还是在写App时,主要关注栈的几个基本操作:push,pop,isEmpty,peek,size.
protocol Stack {
// 持有的元素类型
associatedtype Element
// 是否为空
var isEmpty:Bool {get}
var size:Int {get}
var peek:Element? {get}
// 进栈
mutating func push(_ newElement:Element)
// 出栈
mutating func pop() -> Element?
}
struct IntegerStack:Stack {
typealias Element = Int
var isEmpty: Bool { return stack.isEmpty }
var size: Int { return stack.count }
var peek: Int? { return stack.last}
private var stack = [Element]()
mutating func push(_ newElement: Int) {
stack.append(newElement)
}
mutating func pop() -> Int? {
return stack.popLast()
}
}
主要关注队列的几个基本操作:enqueue,dequeue,isEmpty,peek,size.
protocol Queue {
// 持有元素类型
associatedtype Element
// 是否为空
var isEmpty:Bool {get}
var size:Int {get}
// 队首元素
var peek:Element? {get}
// 入队
mutating func enqueue(_ newElement:Element)
// 出队
mutating func dequeue() -> Element?
}
struct IntegerQueue: Queue {
typealias Element = Int
var isEmpty: Bool { return left.isEmpty && right.isEmpty }
var size: Int { return left.count + right.count }
var peek: Int? { return left.isEmpty ? right.first : left.last}
private var left = [Element]()
private var right = [Element]()
mutating func enqueue(_ newElement: Int) {
right.append(newElement)
}
mutating func dequeue() -> Int? {
if left.isEmpty {
left = right.reversed()
right.removeAll()
}
return left.popLast()
}
}
- 给出一个文件的绝对路径,要求将其简化,举一个例子,路径是”/home/“简化后为"/home" 路径是"/a/./b/../../c" 简化后为"/c" 思路根据"/" 拆分成String数组,对于".."相当于做pop操作,其他情况不做处理
func simplifyPath(path:String) -> String {
var pathStack = [String]()
let paths = path.components(separatedBy: "/")
for path in paths {
guard path != "." else {
continue
}
if path == ".." {
if (pathStack.count > 0) {
pathStack.removeLast()
}
} else if path != "" {
pathStack.append(path)
}
}
// 将栈中的内容转化为优化后的新路径
let res = pathStack.reduce("") { total, dir in "\(total)\(dir)"}
// 注意空路径结果是 "/"
return res.isEmpty ? "/" : res
}
5. 二叉树
节点定义
public class TreeNode {
var left:TreeNode?
var right:TreeNode?
var val: Int
init(_ val:Int) {
self.val = val
}
}
- 递归计算树的最大深度
func maxDepth(root:TreeNode?) -> Int {
guard let root = root else {
return 0
}
return 1+max(maxDepth(root: root.left),maxDepth(root: root.right))
}
- 栈实现二叉树前序遍历
func preorderTraversal(root: TreeNode?) -> [Int] {
var res = [Int]()
var stack = [TreeNode]()
var node = root
while !stack.isEmpty || node != nil {
if node != nil {
// 先遍历自己
res.append(node!.val)
stack.append(node!)
// node 左子树节点遍历
node = node!.left
} else {
node = stack.removeLast().right
}
}
return res
}
- 队列实现二叉树层序遍历
func levelOrder(root: TreeNode?) -> [[Int]] {
var res = [[Int]]()
var queue = [TreeNode]()
if let root = root {
queue.append(root)
}
while queue.count > 0 {
let size = queue.count
var level = [Int]()
for _ in 0..<size {
// 出队
let node = queue.removeFirst()
level.append(node.val)
// 入队
if let left = node.left {
queue.append(left)
}
if let right = node.right {
queue.append(right)
}
}
res.append(level)
}
return res
}
算法
1. 排序与搜索
- 归并排序
func mergeSort(_ array:[Int]) -> [Int] {
var helper = [Int](repeating: 0, count: array.count),array = array
mergeSort(&array, &helper, 0, array.count-1)
return array
}
func mergeSort(_ array: inout [Int],_ helper : inout [Int], _ low: Int ,_ high:Int) {
guard low < high else {
return
}
let middle = (high - low)/2 + low
mergeSort(&array, &helper, low, middle)
mergeSort(&array, &helper, middle+1, high)
}
func merge(_ array: inout [Int],_ helper : inout [Int], _ low: Int , _ middle:Int, _ high:Int) {
// copy both halves int a helper array
for i in low ... high {
helper[i] = array[i]
}
var helperLeft = low, helperRight = middle+1,current = low
//iterate through helper array and copy the right one to orignial array
while helperLeft<=middle && helperRight <= high {
if helper[helperLeft] <= helper[helperRight] {
array[current] = helper[helperLeft];
helperLeft += 1
} else {
array[current] = helper[helperRight];
helperRight += 1
}
current += 1
}
// handle the rest
guard middle - helperLeft >= 0 else {
return
}
for i in 0 ... middle - helperLeft {
array[current+i] = helper[helperLeft + i]
}
}
- 快速排序
func quickSort(_ array:[Int]) -> [Int] {
guard array.count > 1 else {
return array
}
let priot = array[array.count/2]
let left = array.filter{ $0 < priot }
let middle = array.filter{ $0 == priot }
let right = array.filter{ $0 > priot }
return quickSort(left) + middle + quickSort(right)
}