From 7943a2f79c11e31c04e75e7815e4b87607333d83 Mon Sep 17 00:00:00 2001 From: AlanLXG <1158684059@qq.com> Date: Wed, 14 Sep 2022 10:49:21 +0800 Subject: [PATCH 1/8] =?UTF-8?q?=E6=B7=BB=E5=8A=A0Swift=E8=B7=B3=E8=A1=A8?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- swift/17_skipList/skipList.swift | 197 +++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 swift/17_skipList/skipList.swift diff --git a/swift/17_skipList/skipList.swift b/swift/17_skipList/skipList.swift new file mode 100644 index 00000000..e5d1b172 --- /dev/null +++ b/swift/17_skipList/skipList.swift @@ -0,0 +1,197 @@ +// Created by AlanLXG on 2022/9/14. + +import UIKit +import Foundation +import Darwin + +// Config max level of all nodes. +let MAX_LEVEL_OF_SKIP_LIST = 16 + +// Class declaration of node. +public class SkipListNode +{ + var nodeData:Int + var nodeLevel:Int + // Next node of current node in all levels, whose value is first above current node. + var nodeForwardData:[SkipListNode?] + public init() + { + nodeData = -1 + nodeLevel = 0 + nodeForwardData = Array(repeating: nil, count: MAX_LEVEL_OF_SKIP_LIST) + } + // Return a string that containins node value and level. + open func nodeToString() -> String + { + let nodeString = "{ data: \(nodeData); level: \(nodeLevel) }" + return nodeString + } +} + +// Class declaration of skip list. +public class SkipList +{ + var levelCount:Int + // Use sentinel node to simplify insert and delete process of a skip list. + let sentinelNode:SkipListNode + + public init() + { + levelCount = 1 + sentinelNode = SkipListNode() + } + // Find a node with a qualified value. + + public func findNode(targetVaule:Int) -> SkipListNode? + { + var searchNode = sentinelNode + for level in (0.. Void + { + let newNode = SkipListNode() + newNode.nodeData = targetValue + // generate a random level via random function. + let randomLevel = genarateRandomLevel() + newNode.nodeLevel = randomLevel + // A temp array that contains nodes whose values are just below the current value in all levels. + var tempForwardNode = Array(repeating: nil, count: randomLevel+1) + var foundNode = sentinelNode + // First find the locations to be inserted. + for level in (0...randomLevel).reversed() + { + while foundNode.nodeForwardData[level] != nil && foundNode.nodeForwardData[level]!.nodeData < targetValue + { + foundNode = foundNode.nodeForwardData[level]! + } + tempForwardNode[level] = foundNode + } + for level in 0...randomLevel + { + newNode.nodeForwardData[level] = tempForwardNode[level]?.nodeForwardData[level] + tempForwardNode[level]?.nodeForwardData[level] = newNode + } + if levelCount < randomLevel + { + levelCount = randomLevel+1 + } + } + // Delete node with current value. + public func deleteNode(targetValue:Int) -> Int + { + var signal = -1 + var tempForwardNode = Array(repeating: nil, count: levelCount) + var tempNode = sentinelNode + // Need to find the value first. + for level in (0.. Void + { + var firstNode = sentinelNode.nodeForwardData[0] + while firstNode != nil + { + print(firstNode!.nodeToString()) + firstNode = firstNode?.nodeForwardData[0] + } + } + // Print nodes of qulified level. + public func printListOfSomeLevel(targetLevel:Int) -> Void + { + for level in (0.. 0 && targetLevel == level) + { + print("第\(level)级数据:") + while firstNode.nodeForwardData[level] != nil + { + print("\(firstNode.nodeForwardData[level]!.nodeData)") + firstNode = firstNode.nodeForwardData[level]! + } + } + } + } + // Generate a random number and give it to node's level. + internal func genarateRandomLevel() -> Int + { + var level = 0 + + for _ in 1.. Void +{ + let skipList = SkipList() + for value in 1..<50 + { + if value % 3 == 0 + { + skipList.insertNewNode(targetValue: value) + } + } + for value in 1..<50 + { + if value % 3 == 1 + { + skipList.insertNewNode(targetValue: value) + } + } + skipList.printCurrentList() + let findNode = skipList.findNode(targetVaule: 27) + if let tempNode = findNode + { + print("find node of value: \(tempNode.nodeData), level is \(tempNode.nodeLevel)") + } + else + { + print("Node not find.") + } + let sig = skipList.deleteNode(targetValue: 27) + sig == -1 ? print("No such node to delete.") : print("Successfully delete qulified node.") +} + +main() From 0a0802c26cea92c3c18c5cb92d3f0d251a550f2a Mon Sep 17 00:00:00 2001 From: AlanLXG <1158684059@qq.com> Date: Thu, 29 Sep 2022 20:02:26 +0800 Subject: [PATCH 2/8] =?UTF-8?q?=E6=B7=BB=E5=8A=A0Swift=E4=BA=8C=E5=8F=89?= =?UTF-8?q?=E6=9F=A5=E6=89=BE=E6=A0=91=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../binarySearchTree.swift | 199 ++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 swift/24_binarySearchTree/binarySearchTree.swift diff --git a/swift/24_binarySearchTree/binarySearchTree.swift b/swift/24_binarySearchTree/binarySearchTree.swift new file mode 100644 index 00000000..63d3a154 --- /dev/null +++ b/swift/24_binarySearchTree/binarySearchTree.swift @@ -0,0 +1,199 @@ +import UIKit + +public class TreeNode:Equatable +{ + var value:Int? + var leftSibling:TreeNode? + var rightSibling:TreeNode? + + init(treeValue:Int) + { + value = treeValue + } +} +// Make class TreeNode confirm to Equatable protocol for equal judgement. +extension TreeNode +{ + public static func == (lhs: TreeNode, rhs: TreeNode) -> Bool + { + if lhs.value == rhs.value && lhs.rightSibling == rhs.rightSibling && lhs.leftSibling == rhs.leftSibling + { + return true + } + else + { + return false + } + } +} + +public class BinarySearchTree +{ + // rootNode. + var treeRootNode:TreeNode? + public func findNodeInBinarySearchTree(targetValue:Int) -> TreeNode? + { + var tempNode = treeRootNode + while tempNode != nil + { + if let tempValue = tempNode?.value + { + if tempValue < targetValue + { + tempNode = tempNode?.rightSibling + } + else if tempValue > targetValue + { + tempNode = tempNode?.leftSibling + } + else + { + print("Successfully find node, value is \(tempValue).") + return tempNode + } + } + } + print("Value not found.") + return nil + } + + public func insertNodeToBinarySearchTree(targetValue:Int) -> Bool + { + if treeRootNode == nil + { + treeRootNode = TreeNode(treeValue: targetValue) + return true + } + var tempNode = treeRootNode + while tempNode != nil + { + if let tempValue = tempNode?.value, tempValue < targetValue + { + if tempNode?.rightSibling == nil + { + tempNode?.rightSibling = TreeNode(treeValue: targetValue) + return true + } + tempNode = tempNode?.rightSibling + } + if let tempValue = tempNode?.value, tempValue > targetValue + { + if tempNode?.leftSibling == nil + { + tempNode?.leftSibling = TreeNode(treeValue: targetValue) + return true + } + tempNode = tempNode?.leftSibling + } + // insert failed because of inserting a same value. + if let tempValue = tempNode?.value, tempValue == targetValue + { + print("The node to be inserted is already existed. Value is \(tempValue). Stopped.") + return false + } + } + print("Tree is not existed. Stopped.") + return false + } + + public func deleteNodeInBinarySearchTree(targetValue:Int) -> Bool + { + // find node to be deleted. + var nodeToBeDeleted = treeRootNode + var fatherNode:TreeNode? = nil + while let tempNode = nodeToBeDeleted, tempNode.value != targetValue + { + fatherNode = tempNode + if let tempValue = tempNode.value, tempValue < targetValue + { + nodeToBeDeleted = nodeToBeDeleted?.rightSibling + } + else if let tempValue = tempNode.value, tempValue >= targetValue + { + nodeToBeDeleted = nodeToBeDeleted?.leftSibling + } + } + // node not found in tree. + if nodeToBeDeleted == nil + { + print("The node to be deleted is not found in tree. Stopped.") + return false + } + let printValue = nodeToBeDeleted?.value + // case1: Node to be deleted has two siblings. + if nodeToBeDeleted?.leftSibling != nil && nodeToBeDeleted?.rightSibling != nil + { + var minNode = nodeToBeDeleted?.rightSibling + var fatherNodeOfMinNode = nodeToBeDeleted + while minNode?.leftSibling != nil + { + fatherNodeOfMinNode = minNode + minNode = minNode?.leftSibling + } + nodeToBeDeleted?.value = minNode?.value + nodeToBeDeleted = minNode + fatherNode = fatherNodeOfMinNode + } + + // case 2 and 3: Node to be deleted has one sibling or no sibling. + var siblingNode:TreeNode? = nil + if let _ = nodeToBeDeleted?.leftSibling + { + siblingNode = nodeToBeDeleted?.leftSibling + } + if let _ = nodeToBeDeleted?.rightSibling + { + siblingNode = nodeToBeDeleted?.rightSibling + } + // case: Node to be deleted is rootNode. + if fatherNode == nil + { + treeRootNode = siblingNode + } + + // if case2: set fatherNode's sibling to node's to be deleted sibling according to whether node to be deleted is its fatherNode's leftSibling or rightSibling. + // if case3: set fatherNode's sibling to nil according to whether node to be deleted is its fatherNode's leftSibling or rightSibling. + fatherNode?.leftSibling == nodeToBeDeleted ? (fatherNode?.leftSibling = siblingNode) : (fatherNode?.rightSibling = siblingNode) + print("Successfully deleted node. Value is \(printValue!).") + return true + } + + // inOrder visit all nodes, print the ordered array. + public func inOrderPrint(rootNode:TreeNode?) + { + guard let tempNode = rootNode else + { + return + } + inOrderPrint(rootNode: tempNode.leftSibling) + print("\(tempNode.value!) ", terminator: "") + inOrderPrint(rootNode: tempNode.rightSibling) + } +} +// test function. +func mainTest() +{ + let searchTree = BinarySearchTree() + let array = [3,6,1,2,7,9,21,33,11,34,55,22,10,8] + //let array = [3,6,1,9] + // test insert node. + for index in array + { + searchTree.insertNodeToBinarySearchTree(targetValue: index) + } + print("All tree nodes are: ", terminator: "") + searchTree.inOrderPrint(rootNode: searchTree.treeRootNode) + print("") + // test find and delete node. + searchTree.findNodeInBinarySearchTree(targetValue: 21) + searchTree.deleteNodeInBinarySearchTree(targetValue: 9) + searchTree.inOrderPrint(rootNode: searchTree.treeRootNode) + print("") + searchTree.findNodeInBinarySearchTree(targetValue: 25) + searchTree.deleteNodeInBinarySearchTree(targetValue: 5) + // test insert node value that is already existed. + searchTree.insertNodeToBinarySearchTree(targetValue: 34) + searchTree.inOrderPrint(rootNode: searchTree.treeRootNode) +} + +mainTest() From 409e86ba959ef8c117bd91e93c067f50e7a9f946 Mon Sep 17 00:00:00 2001 From: AlanLXG <1158684059@qq.com> Date: Tue, 25 Oct 2022 19:59:09 +0800 Subject: [PATCH 3/8] add BFSAndDFS.playground --- swift/31_BFSAndDFS/BFSAndDFS.swift | 264 +++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 swift/31_BFSAndDFS/BFSAndDFS.swift diff --git a/swift/31_BFSAndDFS/BFSAndDFS.swift b/swift/31_BFSAndDFS/BFSAndDFS.swift new file mode 100644 index 00000000..0f9bf0be --- /dev/null +++ b/swift/31_BFSAndDFS/BFSAndDFS.swift @@ -0,0 +1,264 @@ +import UIKit + +/// Class of node for linkedList. Its type is NodeType, which must be the same with LinkedList type and both confirm to protocol BinaryInteger. This means all the classes and methods below only take Int-Like type into processes. +class Node +{ + var nodeValue:NodeType? + var nextNode:Node? +} + +/// Class of LinkedList with some uncomplete oprations, including add a node to the tail of list. +class LinkedList +{ + var sentinalNode:Node? + var sumOfNode:Int = 0 + /// Default init method of LinkedList. + /// - Parameters: + /// - Returns: An instance of LinkedList. + init() + { + let node = Node() + sentinalNode = node + sentinalNode?.nodeValue = -1 + sentinalNode?.nextNode = nil + } + /// Add a new node to the tail of a LinkedList. + /// - Parameters: + /// - value: the new node value to be inserted. + func addNodeToTail(value: ItemType) -> Void + { + let newNode = Node() + if sentinalNode?.nextNode == nil + { + newNode.nodeValue = value + sentinalNode?.nextNode = newNode + sumOfNode += 1 + } + else + { + var previousNode = sentinalNode?.nextNode + while previousNode?.nextNode != nil + { + previousNode = previousNode?.nextNode + } + newNode.nodeValue = value + previousNode?.nextNode = newNode + sumOfNode += 1 + } + } + /// Return the node value with the specific subcript. + /// - Parameters: + /// - withSubScript: specify the subscript of list. For instance, withSubscript = 2 means return the 3rd value in the list. + /// - Returns: Value of the specific node. + func getNode(withSubscript:Int) -> ItemType? + { + var count = 0 + var nextNode = sentinalNode?.nextNode + while count != withSubscript + { + count += 1 + nextNode = nextNode?.nextNode + } + return nextNode?.nodeValue + } + + /// Print all node values in the list. + func printNodeValue() -> Void + { + while let tempNode = sentinalNode?.nextNode + { + print("\(tempNode.nodeValue!) ", terminator: "") + } + } +} +/// Class of a queue. Used only in BFS algorithm. +class Queue +{ + var valueArray:[QueueType] = [] + var front:QueueType? + { + if let node = valueArray.first + { + return node + } + else + { + return nil + } + } + var count:Int { valueArray.count } + + /// Add a new value to the queue, with specified value. + func enQueue(value:QueueType) -> Void + { + valueArray.append(value) + } + /// Pop the first value in the queue according to FIFO. + /// - Returns: The first value in queue. + func deQueue() -> QueueType? + { + guard count > 0 else + { + print("No value in queue. Stopped.") + return nil + } + let value = valueArray.first + valueArray.removeFirst() + return value! + } +} +/// Class declaration of undirected graphs. +class Graph +{ + var vertexCount:Int = 0 + var adjcentTable:[LinkedList] = [] + var previous:[VertexType] = [] + var isFoundInDFS:Bool = false + /// Default init method of graph. + /// - Parameters: + /// - numOfVertex: The total number of vertexes in a graph you wish to get. + init(numOfVertex:Int) + { + vertexCount = numOfVertex + for _ in (0..()) + } + } + + /// Build a graph using two vertexes, add two edges for each one of them. If you want build a graph with some vertexes, first you need to invoke this instant method. + /// - Parameters: + /// - vertex: The source vertex of two edges. + /// - anVertex: The destination vertex of two edges. + func addEdge(src vertex:Int, dst anVertex:Int) -> Void + { + adjcentTable[vertex].addNodeToTail(value: VertexType(anVertex)) + adjcentTable[anVertex].addNodeToTail(value: VertexType(vertex)) + } + + /// Breadth-First-Search method in undirected graph. Use it to find a shortest path from `startVertex` to `endVertex`. + /// - Parameters: + /// - startVertex: The start point of a path in a graph. + /// - endVertex: The end point of a path in a graph. + func BFS(startVertex:Int, endVertex:Int) -> Void + { + if startVertex == endVertex + { + print("Start is the end.") + return + } + var visited = Array(repeating: false, count: vertexCount) + visited[startVertex] = true + let queue:Queue = Queue() + queue.enQueue(value: VertexType(startVertex)) + for _ in (0.. Void + { + if startVertex == endVertex + { + print("Start is the end.") + return + } + var visited = Array(repeating: false, count: vertexCount) + for _ in (0.. Void + { + if isFoundInDFS == true + { + return + } + visitedArray[start] = true + if start == end + { + isFoundInDFS = true + print("Successfully found destination using DFS. The path is: ") + return + } + for index in (0.. Void + { + if previous[endVertex] != -1 && startVertex != endVertex + { + printValue(startVertex: startVertex, endVertex: Int(previous[endVertex])) + } + print("\(endVertex) ",terminator: "") + } +} + +/// Test function of building a graph, search paths using BFS and DFS. +func mainTest() -> Void +{ + // Graph.addEdge -> LinkedList.addNodeToTail + let graph = Graph(numOfVertex: 8) + graph.addEdge(src: 0, dst: 3) + graph.addEdge(src: 0, dst: 1) + graph.addEdge(src: 1, dst: 2) + graph.addEdge(src: 1, dst: 4) + graph.addEdge(src: 2, dst: 5) + graph.addEdge(src: 3, dst: 4) + graph.addEdge(src: 4, dst: 5) + graph.addEdge(src: 4, dst: 6) + graph.addEdge(src: 5, dst: 7) + graph.addEdge(src: 6, dst: 7) + + graph.BFS(startVertex: 0, endVertex: 7) + graph.DFS(startVertex: 0, endVertex: 7) +} + +// Tested only with integer, from 0 to another number with continuity. +mainTest() From 58aad42fa49f3507436781b78ae33354da9dc53d Mon Sep 17 00:00:00 2001 From: AlanLXG <1158684059@qq.com> Date: Thu, 27 Oct 2022 09:33:58 +0800 Subject: [PATCH 4/8] add BFAndRK.swift --- swift/32_BFAndRK/BFAndRK.swift | 219 +++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 swift/32_BFAndRK/BFAndRK.swift diff --git a/swift/32_BFAndRK/BFAndRK.swift b/swift/32_BFAndRK/BFAndRK.swift new file mode 100644 index 00000000..1be6aeae --- /dev/null +++ b/swift/32_BFAndRK/BFAndRK.swift @@ -0,0 +1,219 @@ +import UIKit + +/// Class of non-official implementation of Rabin-Karp algorithm for string matching. +/// +/// You can use this class to match any string format in a main string. This class offers three method to calculate hash value, including: +/// 1. Twenty-six Decimal hash function, but cannot process string that contains both numbers and characters and may cause integer overflow. +/// 2. Thirty-Six Decimal hash function, can process string that contains both numbers and characters, but may cause integer overflow. +/// 3. Thirty-Six Decimal hash function, can process string that contains both numbers and characters and No overflow will occur. Nonetheless, there may be a hash conflict in process which has been avoided. +/// +/// See more in three hash functions' implementation. +public class RKAlgorithm +{ + var mainString:String = "" + var matchString:String = "" + var mainStringSize:Int { mainString.count } + var matchSize:Int { matchString.count } + /// computational property, to get the string array of main string and match string. + public var mainStringArray:[Character] + { + var tempArray:[Character] = [] + for i in mainString + { + tempArray.append(i) + } + return tempArray + } + public var matchStringArray:[Character] + { + var tempArray:[Character] = [] + for i in matchString + { + tempArray.append(i) + } + return tempArray + } + lazy var hashValueOfMainString:[Int] = [] + lazy var hashValueOfMatchString:Int = 0 + lazy var mainStringASCII:[Int] = [] + lazy var matchStringASCII:[Int] = [] + lazy var powerSeries:[Int] = [] + + /// Default init method, set main string and match string. All string will be lowercased automatically for convenient calculation. + /// - Parameters: + /// - main: main string input. + /// - match: match string input. + public init(main:String, match:String) + { + mainString = main.lowercased() + matchString = match.lowercased() + } + + /// The function calculating power series using a given decimal. + /// - Parameters: + /// - baseOfPower: The base of power series to be given. Must be a decimal. + public func calculatePowerSeries(baseOfPower:Decimal) -> Void + { + for m in (0 ..< matchString.count) + { + let powerValue = (pow(baseOfPower, m) as NSDecimalNumber).intValue + powerSeries.append(powerValue) + } + } + + /// Twenty-six Decimal hash function, but cannot process string that contains both numbers and characters and may cause integer overflow if the match string is too long. + open func hashFunctionWithoutNumber() -> Void + { + let ASCIIOf_a = "a".utf8.first! + // n - m + 1 + let loopTime = mainStringSize - matchSize + 1 + hashValueOfMainString = Array(repeating: 0, count: loopTime) + + for offset in (0 ..< loopTime) + { + if offset == 0 + { + for matchIndex in (0.. Void + { + for char in mainString + { + // 87 + if char.utf8.first! > 96 && char.utf8.first! < 123 + { + let mapping = Int(char.utf8.first! - 87) + mainStringASCII.append(mapping) + } + else + { + let ASCIIOfNum = Int(char.utf8.first! - 48) + mainStringASCII.append(ASCIIOfNum) + } + } + for char in matchString + { + if char.utf8.first! > 96 && char.utf8.first! < 123 + { + let mapping = Int(char.utf8.first! - 87) + matchStringASCII.append(mapping) + } + else + { + let ASCIIOfNum = Int(char.utf8.first! - 48) + matchStringASCII.append(ASCIIOfNum) + } + } + } + + /// Thirty-Six Decimal hash function, can process string that contains both numbers and characters, but may cause integer overflow if the match string is too long. + open func hashFunctionWithNumber() -> Void + { + self.convertCharacterToASCII() + let loopTime = mainStringSize - matchSize + 1 + hashValueOfMainString = Array(repeating: 0, count: loopTime) + for offset in (0 ..< loopTime) + { + if offset == 0 + { + for matchIndex in (0.. Void + { + self.convertCharacterToASCII() + let loopTime = mainStringSize - matchSize + 1 + hashValueOfMainString = Array(repeating: 0, count: loopTime) + for offset in (0 ..< loopTime) + { + for index in (offset ..< (offset+matchSize)) + { + hashValueOfMainString[offset] += mainStringASCII[index] + } + } + for index in (0.. (Int?, Int?) + { + // Just like brute force. + for i in hashValueOfMainString.indices + { + if hashValueOfMainString[i] == hashValueOfMatchString + { + var tempArray:[Character] = [] + for index in (i ..< (i+matchSize)) + { + tempArray.append(mainStringArray[index]) + } + if tempArray == matchStringArray + { + print("Found match string in main string.") + print("From index \(i), to index \(i + matchString.count - 1) in main string.") + return (startIndex:i, endIndex:i + matchString.count - 1) + } + // if hash confict occurred, continue search next hash value in array because there may be match string behind. + // just like: mainString: bad12Def12, matchSting: 12d + else + { + print("Not match, and there is a hash confict occurred.") + print("Now checking strings behind...") + continue + } + } + } + print("Not find match string.") + return (nil, nil) + } +} + +func mainTest() +{ + let RK = RKAlgorithm(main:"bad12Def12", match:"12d") + // change baseOfPower according to the hash function you use. + RK.calculatePowerSeries(baseOfPower: 36) + //RK.simpleHashFunction() + RK.hashFunctionWithNumber() // baseOfPower: 36 + //RK.hashFunctionWithoutNumber() // baseOfPower: 26 + + RK.matchWithHashValue() +} + +mainTest() From 7f2216d8f6bccf3f91adb1cdd78512da3274b5be Mon Sep 17 00:00:00 2001 From: AlanLXG <1158684059@qq.com> Date: Fri, 28 Oct 2022 16:55:38 +0800 Subject: [PATCH 5/8] add BMAlgorithm.swift --- swift/33_BMAlgorithm/BMAlgorithm.swift | 156 +++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 swift/33_BMAlgorithm/BMAlgorithm.swift diff --git a/swift/33_BMAlgorithm/BMAlgorithm.swift b/swift/33_BMAlgorithm/BMAlgorithm.swift new file mode 100644 index 00000000..30fd6046 --- /dev/null +++ b/swift/33_BMAlgorithm/BMAlgorithm.swift @@ -0,0 +1,156 @@ +import UIKit + +/// Class of a simple impletation of Boyer-Moore algorithm for string matching. +/// +/// You can use this class to match any string format in a main string. BM algorithm contains two principles to match a string, which are bad character principle and good suffix principle. The algorithm will choose one of them by specific case automatically. To be noticed, it can ONLY process NOT more than 256 character kind in a set using ASCII. +/// +/// See more in these functions' implementation below. +public class BMAlgorithm +{ + let badCharAsciiTableSize = 256 + var mainString:String = "" + var matchString:String = "" + var mainStringSize:Int { mainString.count } + var matchStringSize:Int { matchString.count } + lazy var badCharTable = Array(repeating: -1, count: badCharAsciiTableSize) + lazy var suffix = Array(repeating: -1, count: matchStringSize) + lazy var prefix = Array(repeating: false, count: matchStringSize) + /// Get the string array of main string and match string because of the inconvenience of native string process in Swift. + public var mainStringArray:[Character] + { + var tempArray:[Character] = [] + for i in mainString + { + tempArray.append(i) + } + return tempArray + } + public var matchStringArray:[Character] + { + var tempArray:[Character] = [] + for i in matchString + { + tempArray.append(i) + } + return tempArray + } + /// Default init method, set main string and match string. + /// - Parameters: + /// - main: main string input. + /// - match: match string input. + init(main:String, match:String) + { + mainString = main + matchString = match + } + /// Function that generate ASCII table of 256 kinds of characters. + public func generateAsciiTable() -> Void + { + for index in (0 ..< matchStringSize) + { + let asciiValue = Int(matchStringArray[index].utf8.first!) + badCharTable[asciiValue] = index + } + } + + /// Function that prepare the suffix array and prefix array in process. These two arrays are very important to the algorithm. + open func prepareSuffixAndPrefix() -> Void + { + // 求模式串和主串的公共后缀字串,这样可以得到模式串中第一次出现好后缀的起始下标,以及出现模式串前缀和主串后缀匹配的情况,做法非常巧妙。 + // 有几个字串就循环几次 + for substringSize in (0 ..< matchStringSize-1) + { + var lastCharIndex = substringSize + var publicSubfixSize = 0 + // 求模式串子串与模式串之间的公共子串,得到第一次出现好后缀的起始下标。 + while lastCharIndex >= 0 && matchStringArray[lastCharIndex] == matchStringArray[matchStringSize-1-lastCharIndex] + { + // 如果相等,则说明后缀匹配成功一个字符,size+1,两个指针同时向前一个字符 + publicSubfixSize += 1 + // 使用后缀长度来记录suffix数组的值,值即为此后缀最后出现的index + suffix[publicSubfixSize] = lastCharIndex + lastCharIndex -= 1 + } + // 如果一直能够匹配到子串的第一个字符,则说明该后缀也是模式串的前缀 + if lastCharIndex == -1 + { + prefix[publicSubfixSize] = true + } + } + } + /// Calculate the move steps of next match using good suffix principle. + /// - Parameters: + /// - index: The index of bad character in match string. + /// - Returns: + /// The steps of match index will move in next match. + open func moveMatchString(badCharacterIndex index:Int) -> Int + { + // 好后缀的长度,可以使用坏字符的index和模式串的长度计算出 + let goodSuffixSize = matchStringSize - 1 - index + // 查找好后缀在suffix是否有值,如果有,则说明前面出现过好后缀,需要将字符串移动到好后缀最后一次出现的地方 + if suffix[goodSuffixSize] != -1 + { + return index - suffix[goodSuffixSize] + 1 + } + // 如果没有好后缀,则需要查找是否有好后缀的子后缀,子后缀从坏字符的后两个字符开始 + for suffixOfGoodSufIndex in (index+2 ..< matchStringSize) + { + // 如果找到了好后缀的子后缀,则将其移动 + if prefix[matchStringSize-suffixOfGoodSufIndex] == true + { + return suffixOfGoodSufIndex + } + } + return matchStringSize + } + /// Main function of BM algorithm. This method invokes other methods which are necessary. First generate ASCII table of 256 characters, then prepare suffix and prefix that will use. After that, using bad character principle to match. Also, if there is a good suffix, using good suffix principle too. Finally, choose the max steps that two principles get above to perform next match. + /// - Returns: + /// The start match index in main string. If not matched, it will return `nil` instead. + open func Boyer_Moore() -> Int? + { + self.generateAsciiTable() + self.prepareSuffixAndPrefix() + var matchIndex = 0 + var badCharForward = 0, goodSuffixForward = 0 + // 下面的循环用来寻找坏字符出现的位置 + while matchIndex < (mainStringSize - matchStringSize + 1) + { + // 初始值为-1,表示没有坏字符 + var badCharIndex = -1 + for index in (0.. Date: Mon, 31 Oct 2022 20:12:43 +0800 Subject: [PATCH 6/8] add KMPAlgorithm.swift --- swift/34_KMPAlgorithm/KMPAlgorithm.swift | 96 ++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 swift/34_KMPAlgorithm/KMPAlgorithm.swift diff --git a/swift/34_KMPAlgorithm/KMPAlgorithm.swift b/swift/34_KMPAlgorithm/KMPAlgorithm.swift new file mode 100644 index 00000000..88345144 --- /dev/null +++ b/swift/34_KMPAlgorithm/KMPAlgorithm.swift @@ -0,0 +1,96 @@ +import UIKit + +/// Class of a simple implementation of Knuth-Morris-Pratt algorithm for string matching. +/// +/// You can use this class to match any string format in a main string. What is the most important method is failure function, which is very hard to understand. +/// +/// See more in these methods' implementation below. +open class KMPAlgorithm +{ + var mainString:String = "" + var matchString:String = "" + open var mainStringSize:Int { mainString.count } + open var matchStringSize:Int { matchString.count } + /// Invert string to array using closure. If using closure, it must be declared as lazy var, because closure is using other properties to set a new property, which is not allowed in swift initialization stage. Using lazy will not access other properties until new property is used. + lazy var mainStringArray:[Character] = + { + var tempArray:[Character] = [] + for char in mainString + { + tempArray.append(char) + } + return tempArray + }() // Invoke this closure immediately, must have "()" after closure, otherwise it will be reconginzed as a compute property. + + lazy var matchStringArray:[Character] = + { + var tempArray:[Character] = [] + for char in matchString + { + tempArray.append(char) + } + return tempArray + }() + + lazy var nextArray = [Int](repeating: -1, count: matchStringSize) + /// Default initializtion method of class KMP. + /// - Parameters: + /// - main: main string input. + /// - match: match string input. + init(main:String, match:String) + { + mainString = main + matchString = match + } + /// Failure function of KMP algorithm. + /// - Returns: A array contains longest match preifx's index. + private func faliureFunction() -> [Int] + { + var index = -1 + for i in (1.. Void + { + nextArray = self.faliureFunction() + var badCharIndex = 0 + for mainIndex in mainStringArray.indices + { + while badCharIndex > 0 && mainStringArray[mainIndex] != matchStringArray[badCharIndex] + { + badCharIndex = nextArray[badCharIndex - 1] + 1 + } + if mainStringArray[mainIndex] == matchStringArray[badCharIndex] + { + badCharIndex += 1 + } + if badCharIndex == matchStringSize + { + print("Find match string in main string.") + print("From index \(mainIndex - matchStringSize + 1), to index \(mainIndex) in main string.") + return + } + } + print("Not found match string.") + } +} + +func mainTest() +{ + let kmp = KMPAlgorithm(main:"qwe321wwaaa&*12", match:"a&*1") + kmp.Knuth_Morris_Pratt() +} + +mainTest() From 615b63389a70900859add520f8d12923aed0df4d Mon Sep 17 00:00:00 2001 From: AlanLXG <1158684059@qq.com> Date: Tue, 1 Nov 2022 17:03:18 +0800 Subject: [PATCH 7/8] add Trie.swift --- swift/35_Trie/Trie.swift | 76 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 swift/35_Trie/Trie.swift diff --git a/swift/35_Trie/Trie.swift b/swift/35_Trie/Trie.swift new file mode 100644 index 00000000..856d2d96 --- /dev/null +++ b/swift/35_Trie/Trie.swift @@ -0,0 +1,76 @@ +import UIKit + +/// Class of TrieNode, a simple data structure in Trie that contains node data, children array and a symbol marking the end of a string. +class TrieNode +{ + var nodeData:Character? + lazy var nextNodeArray = [TrieNode?](repeating: nil, count: 26) + var isEndingChar = false + /// Default init method of class TrieNode. Use it to create a new node for Trie. + /// - Parameters: + /// - nextCharacter: A character of pattern string when visiting it from start to end. + init(nextCharacter:Character?) + { + nodeData = nextCharacter + } +} + + /// Class of simple implementation of Trie. This class has two methods for inserting a new string to Trie, and finding a present string in Trie. First you need to insert all string to make a string set. Then invoke find method to search the string you are interested in. +class Trie +{ + let rootNode = TrieNode(nextCharacter: nil) + /// Insert a new string to Trie. + /// - Paramrters: + /// - newString: The string you want to insert. + func insertStringToTrie(newString:String) -> Void + { + var node = rootNode + for index in newString.indices + { + let ascii = Int(newString[index].utf8.first!) - 97 + if node.nextNodeArray[ascii] == nil + { + let newNode = TrieNode(nextCharacter: newString[index]) + node.nextNodeArray[ascii] = newNode + } + node = node.nextNodeArray[ascii]! + } + node.isEndingChar = true + } + /// Find the pattern string in Trie. + /// - Parameters: + /// - pattern: The pattern string you wish to be found. + /// - Returns: A bool value which means whether the pattern is found in Trie. If found, returns `true`, otherwise returns `false`. + func findStringInTrie(toBeFind pattern:String) -> Bool + { + var node = rootNode + for index in pattern.indices + { + let ascii = Int(pattern[index].utf8.first!) - 97 + if let tempNode = node.nextNodeArray[ascii] + { + node = tempNode + } + else + { + print("Pattern string is not in Trie.") + return node.isEndingChar + } + } + node.isEndingChar ? print("Found pattern string in Trie.") : print("Pattern string is a prefix of some string that contains \"\(pattern)\". Not exactly found.") + return node.isEndingChar + } +} + +func mainTest() +{ + let trie = Trie() + trie.insertStringToTrie(newString: "hello") + trie.insertStringToTrie(newString: "else") + trie.insertStringToTrie(newString: "he") + + trie.findStringInTrie(toBeFind: "hell") + +} + +mainTest() From a2bb2cd002ec0ad5e3714d717b1c62ebea35a858 Mon Sep 17 00:00:00 2001 From: AlanLXG <1158684059@qq.com> Date: Fri, 4 Nov 2022 20:46:22 +0800 Subject: [PATCH 8/8] add ACAutoMachine.swift --- swift/36_ACAutoMachine/ACAutoMachine.swift | 166 +++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 swift/36_ACAutoMachine/ACAutoMachine.swift diff --git a/swift/36_ACAutoMachine/ACAutoMachine.swift b/swift/36_ACAutoMachine/ACAutoMachine.swift new file mode 100644 index 00000000..3b1370b8 --- /dev/null +++ b/swift/36_ACAutoMachine/ACAutoMachine.swift @@ -0,0 +1,166 @@ +import UIKit + +/// Class of the data structure queue which uses array to perform FIFO. +class Queue +{ + var valueArray:[QueueType?] = [] + var frontNode:QueueType? + { + if let node = valueArray.first + { + return node + } + else + { + return nil + } + } + /// A property that returns queue's length, using closure and lazy method to initialize it. + lazy var count:Int = { valueArray.count }() + /// Add a new value to the queue, with specified value. + func enQueue(value:QueueType?) -> Void + { + valueArray.append(value) + } + /// Pop the first value in the queue according to FIFO. + /// - Returns: The first value in queue. + func deQueue() -> QueueType? + { + guard count > 0 else + { + print("No value in queue. Stopped.") + return nil + } + let value = valueArray.first + valueArray.removeFirst() + return value! + } +} + +/// Class of ACAutoMachine's node. Because of comparing two node, it needs to confirm to protocol Equatable. +class ACNode:Equatable +{ + /// Method of protocol Equatable, to judge whether two ACNode is equal. + static func == (lhs: ACNode, rhs: ACNode) -> Bool + { + if lhs.nodeData == rhs.nodeData && lhs.isEndingChar == rhs.isEndingChar && lhs.failNode == rhs.failNode && lhs.childrenArray == rhs.childrenArray + { + return true + } + else + { + return false + } + } + var nodeData:Character? + lazy var childrenArray = [ACNode?](repeating: nil, count: 26) + var isEndingChar = false + /// Fail node, or fail pointer, which point to the node of one of the last level in Trie(AC). + var failNode:ACNode? + var matchSize:Int = -1 + /// Default init method of ACNode. + /// - Parameters: + /// - nextCharacter: New character to get in Trie. + init(nextCharacter:Character?) + { + nodeData = nextCharacter + } + +} +/// The class of a simple implementation of AC auto machine algorithm. AC auto machine is based on Trie, which is a multi-pattern match algorithm. One can use it to judge whether a string contains one or multiple substrings in Trie. AC auto machine is an improvement of Trie. +class ACAutoMachine +{ + let rootNode = ACNode(nextCharacter: nil) + /// Insert a new string in auto machine. Actually, it is the same method of Trie to insert a new string. + /// - Parameters: + /// - newString: The string which needs to be insert. + func insertStringToACAutoMachine(newString:String) -> Void + { + var node = rootNode + for index in newString.indices + { + let ascii = Int(newString[index].utf8.first!) - 97 + if node.childrenArray[ascii] == nil + { + let newNode = ACNode(nextCharacter: newString[index]) + node.childrenArray[ascii] = newNode + } + node = node.childrenArray[ascii]! + } + node.isEndingChar = true + } + /// The failure pointer builder method, which is the most important one in AC auto machine. + func buildFailureNode() -> Void + { + let nodeQueue:Queue = Queue() + rootNode.failNode = nil + nodeQueue.enQueue(value: rootNode) + while nodeQueue.count != 0 + { + let currentNode = nodeQueue.deQueue() + for index in (0..<26) + { + let currentNodeChild = currentNode?.childrenArray[index] + if currentNodeChild == nil { continue } + if currentNode == rootNode + { + currentNodeChild?.failNode = rootNode + } + else + { + var currentFailNode = currentNode?.failNode + while currentFailNode != nil + { + let currentFailNodeChild = currentFailNode?.childrenArray[Int((currentNodeChild?.nodeData?.utf8.first!)!) - 97] + if currentFailNodeChild != nil + { + currentNodeChild?.failNode = currentFailNodeChild + break + } + currentFailNode = currentFailNode?.failNode + } + if currentFailNode == nil + { + currentNodeChild?.failNode = rootNode + } + } + nodeQueue.enQueue(value: currentNodeChild) + } + } + } + /// Match with a string using AC auto machine. + func matchWithACAutoMachine(matchString:String) -> Void + { + var currentACNode = rootNode + var matchStringArray = [Character]() + for i in matchString.indices + { + matchStringArray.append(matchString[i]) + } + for index in (0..