Skip to content

添加Swift跳表实现 #529

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 197 additions & 0 deletions swift/17_skipList/skipList.swift
Original file line number Diff line number Diff line change
@@ -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<SkipListNode?>(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..<levelCount).reversed()
{
while searchNode.nodeForwardData[level] != nil && searchNode.nodeForwardData[level]!.nodeData < targetVaule
{
searchNode = searchNode.nodeForwardData[level]!
}
}
if searchNode.nodeForwardData[0] != nil && searchNode.nodeForwardData[0]!.nodeData == targetVaule
{
return searchNode.nodeForwardData[0]
}
return nil
}
// Insert a new node.
public func insertNewNode(targetValue:Int) -> 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<SkipListNode?>(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<SkipListNode?>(repeating: nil, count: levelCount)
var tempNode = sentinelNode
// Need to find the value first.
for level in (0..<levelCount).reversed()
{
while tempNode.nodeForwardData[level] != nil && tempNode.nodeForwardData[level]!.nodeData < targetValue
{
tempNode = tempNode.nodeForwardData[level]!
}
tempForwardNode[level] = tempNode
}
// Delete value.
if tempNode.nodeForwardData[0] != nil && tempNode.nodeForwardData[0]?.nodeData == targetValue
{
for level in (0..<levelCount).reversed()
{
if tempForwardNode[level]?.nodeForwardData[level] != nil && tempForwardNode[level]?.nodeForwardData[level]?.nodeData == targetValue
{
tempForwardNode[level]?.nodeForwardData[level] = tempForwardNode[level]?.nodeForwardData[level]?.nodeForwardData[level]
signal = 0
}
}
}
return signal
}
// Print all nodes with values and levels of current list.
public func printCurrentList() -> 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..<MAX_LEVEL_OF_SKIP_LIST)
{
var firstNode = sentinelNode
if targetLevel < 0 || (targetLevel > 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..<MAX_LEVEL_OF_SKIP_LIST
{
let randomSeed = arc4random()
// Using random number to be the seed of function "arc4random_uniform()".
let randomNum = arc4random_uniform(randomSeed)
if randomNum % 3 == 1
{
level += 1
}
}
return level
}
}
// Test all methods.
func main() -> 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()
199 changes: 199 additions & 0 deletions swift/24_binarySearchTree/binarySearchTree.swift
Original file line number Diff line number Diff line change
@@ -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()
264 changes: 264 additions & 0 deletions swift/31_BFSAndDFS/BFSAndDFS.swift
Original file line number Diff line number Diff line change
@@ -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<NodeType: BinaryInteger>
{
var nodeValue:NodeType?
var nextNode:Node<NodeType>?
}

/// Class of LinkedList with some uncomplete oprations, including add a node to the tail of list.
class LinkedList<ItemType: BinaryInteger>
{
var sentinalNode:Node<ItemType>?
var sumOfNode:Int = 0
/// Default init method of LinkedList.
/// - Parameters:
/// - Returns: An instance of LinkedList.
init()
{
let node = Node<ItemType>()
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<ItemType>()
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<QueueType:BinaryInteger>
{
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<VertexType:BinaryInteger>
{
var vertexCount:Int = 0
var adjcentTable:[LinkedList<VertexType>] = []
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..<vertexCount)
{
adjcentTable.append(LinkedList<VertexType>())
}
}

/// 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<Bool>(repeating: false, count: vertexCount)
visited[startVertex] = true
let queue:Queue<VertexType> = Queue()
queue.enQueue(value: VertexType(startVertex))
for _ in (0..<vertexCount)
{
previous.append(-1)
}
while queue.count != 0
{
let currentVer = queue.deQueue()
for index in 0..<adjcentTable[currentVer as! Int].sumOfNode
{
let vertexInTable = adjcentTable[currentVer as! Int].getNode(withSubscript: index)
if visited[vertexInTable as! Int] == false
{
previous[Int(vertexInTable!)] = currentVer!
if vertexInTable! == endVertex
{
print("Successfully found destination using BFS. The shortest path is: ")
printValue(startVertex: startVertex, endVertex: endVertex)
print("\n")
return
}
visited[Int(vertexInTable!)] = true
queue.enQueue(value: vertexInTable!)
}
}
}
}
/// Depth-First-Search method in undirected graph. Use it to find a path from `startVertex` to `endVertex`. Noticed that it may not be the shortest path.
/// - Parameters:
/// - startVertex: The start point of a path in a graph.
/// - endVertex: The end point of a path in a graph.
func DFS(startVertex:Int, endVertex:Int) -> Void
{
if startVertex == endVertex
{
print("Start is the end.")
return
}
var visited = Array<Bool>(repeating: false, count: vertexCount)
for _ in (0..<vertexCount)
{
previous.append(-1)
}
recursionOfDFS(start: startVertex, end: endVertex, visitedArray: &visited)
printValue(startVertex: startVertex, endVertex: endVertex)
}

/// Recursion method in DFS. DFS uses it to search path.
/// - Parameters:
/// - start: The start point of a path in a sub-graph.
/// - end: The end point of a path in a sub-graph.
/// - visitedArray: An array that records whether the vertex has been visited.
func recursionOfDFS(start:Int, end:Int, visitedArray:inout [Bool]) -> 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..<adjcentTable[start].sumOfNode)
{
let currentVer = adjcentTable[start].getNode(withSubscript: index)
if visitedArray[currentVer as! Int] == false
{
previous[currentVer as! Int] = VertexType(start)
recursionOfDFS(start: currentVer as! Int, end: end, visitedArray: &visitedArray)
}
}
}

/// Print the vertex value in the path.
/// - Parameters:
/// - startVertex: The start point of a path in a graph.
/// - endVertex: The end point of a path in a graph.
func printValue(startVertex:Int, endVertex:Int) -> 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<Int>(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()
219 changes: 219 additions & 0 deletions swift/32_BFAndRK/BFAndRK.swift
Original file line number Diff line number Diff line change
@@ -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<Int>(repeating: 0, count: loopTime)

for offset in (0 ..< loopTime)
{
if offset == 0
{
for matchIndex in (0..<matchSize)
{
hashValueOfMainString[offset] += Int(mainStringArray[matchIndex].utf8.first! - ASCIIOf_a) * powerSeries[matchSize-matchIndex-1]
}
}
// Next hashvalue can be calculated by last hashvalue.
else
{
hashValueOfMainString[offset] = (hashValueOfMainString[offset-1] - powerSeries[matchSize-1]*Int(mainStringArray[offset-1].utf8.first! - ASCIIOf_a)) * powerSeries[1]
hashValueOfMainString[offset] += Int((mainStringArray[offset+matchSize-1].utf8.first! - ASCIIOf_a))
}
}
for index in (0..<matchSize)
{
hashValueOfMatchString += Int(matchStringArray[index].utf8.first! - ASCIIOf_a) * powerSeries[matchSize-index-1]
}
}

/// The mapping function. Mapping numbers and characters to 0~35, in order to using thirty-six decimal hash function.
open func convertCharacterToASCII() -> 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<Int>(repeating: 0, count: loopTime)
for offset in (0 ..< loopTime)
{
if offset == 0
{
for matchIndex in (0..<matchSize)
{
hashValueOfMainString[offset] += mainStringASCII[matchIndex] * powerSeries[matchSize-matchIndex-1]
}
}
else
{
hashValueOfMainString[offset] = (hashValueOfMainString[offset-1] - powerSeries[matchSize-1] * mainStringASCII[offset-1]) * powerSeries[1]
hashValueOfMainString[offset] += mainStringASCII[offset+matchSize-1]
}
}
for index in (0..<matchSize)
{
hashValueOfMatchString += matchStringASCII[index] * powerSeries[matchSize-index-1]
}
}

/// Thirty-Six Decimal hash function, can process string that contains both numbers and characters and integer overflow will not occur. Nonetheless, there may be a hash conflict in process which has been avoided.
open func simpleHashFunction() -> Void
{
self.convertCharacterToASCII()
let loopTime = mainStringSize - matchSize + 1
hashValueOfMainString = Array<Int>(repeating: 0, count: loopTime)
for offset in (0 ..< loopTime)
{
for index in (offset ..< (offset+matchSize))
{
hashValueOfMainString[offset] += mainStringASCII[index]
}
}
for index in (0..<matchSize)
{
hashValueOfMatchString += matchStringASCII[index]
}
}

/// Match with main string and match string.
/// - Returns: A tuple contains start and end indexes of match string in main string.
public func matchWithHashValue() -> (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()
156 changes: 156 additions & 0 deletions swift/33_BMAlgorithm/BMAlgorithm.swift
Original file line number Diff line number Diff line change
@@ -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<Int>(repeating: -1, count: badCharAsciiTableSize)
lazy var suffix = Array<Int>(repeating: -1, count: matchStringSize)
lazy var prefix = Array<Bool>(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..<matchStringSize).reversed()
{
if mainStringArray[matchIndex+index] != matchStringArray[index]
{
badCharIndex = index
break
}
}
// 经过上方的寻找之后如果还是-1,则说明已经匹配,没有坏字符
if badCharIndex < 0
{
print("Find match string.")
print("From index \(matchIndex), to index \(matchIndex + matchStringSize - 1) in main string.")
return matchIndex
}
// 坏字符原则计算得到的向前移动的步数
badCharForward = badCharIndex - badCharTable[Int(mainStringArray[matchIndex+badCharIndex].utf8.first!)]
// 好后缀原则计算得到的向前移动的步数
if badCharIndex < matchStringSize - 1
{
goodSuffixForward = self.moveMatchString(badCharacterIndex: badCharIndex)
}
// 在上述二者之间取最大值,来确定要移动的步数
matchIndex += max(badCharForward, goodSuffixForward)
}
print("Not find match string in main string.")
return nil
}
}

func mainTest()
{
let BM = BMAlgorithm(main:"qwe321wwaaa&*12", match:"a&*1")
BM.Boyer_Moore()
}

mainTest()
96 changes: 96 additions & 0 deletions swift/34_KMPAlgorithm/KMPAlgorithm.swift
Original file line number Diff line number Diff line change
@@ -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..<matchStringSize)
{
while index != -1 && matchStringArray[index+1] != matchStringArray[index]
{
index = nextArray[index]
}
if matchStringArray[index+1] == matchStringArray[i]
{
index += 1
}
nextArray[i] = index
}
return nextArray
}
/// Main method of KMP. First it invoke instance method `failureFunction()` to calculate failure function, then use it to match string.
open func Knuth_Morris_Pratt() -> 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()
76 changes: 76 additions & 0 deletions swift/35_Trie/Trie.swift
Original file line number Diff line number Diff line change
@@ -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()
166 changes: 166 additions & 0 deletions swift/36_ACAutoMachine/ACAutoMachine.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import UIKit

/// Class of the data structure queue which uses array to perform FIFO.
class Queue<QueueType>
{
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<ACNode> = 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..<matchString.count)
{
let ASCII = Int(matchStringArray[index].utf8.first!) - 97
while currentACNode.childrenArray[ASCII] == nil && currentACNode != rootNode
{
currentACNode = currentACNode.failNode!
}
currentACNode = currentACNode.childrenArray[index]!
if currentACNode == nil
{
currentACNode = rootNode
}
var tempNode = currentACNode
while tempNode != rootNode
{
// Successfully match string. Print start index and length.
if tempNode.isEndingChar == true
{
let matchPosition = index - tempNode.matchSize + 1
print("Find a match string.")
print("From index \(matchPosition), with length \(tempNode.matchSize).")
}
tempNode = tempNode.failNode!
}
}
}
}