Skip to content
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

feat: add Swift codes for chapter_sorting articles #313

Merged
merged 6 commits into from Jan 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions codes/swift/Package.swift
Expand Up @@ -31,6 +31,11 @@ let package = Package(
.executable(name: "linear_search", targets: ["linear_search"]),
.executable(name: "binary_search", targets: ["binary_search"]),
.executable(name: "hashing_search", targets: ["hashing_search"]),
.executable(name: "bubble_sort", targets: ["bubble_sort"]),
.executable(name: "insertion_sort", targets: ["insertion_sort"]),
.executable(name: "quick_sort", targets: ["quick_sort"]),
.executable(name: "merge_sort", targets: ["merge_sort"]),
.executable(name: "radix_sort", targets: ["radix_sort"]),
],
targets: [
.target(name: "utils", path: "utils"),
Expand Down Expand Up @@ -60,5 +65,10 @@ let package = Package(
.executableTarget(name: "linear_search", dependencies: ["utils"], path: "chapter_searching", sources: ["linear_search.swift"]),
.executableTarget(name: "binary_search", path: "chapter_searching", sources: ["binary_search.swift"]),
.executableTarget(name: "hashing_search", dependencies: ["utils"], path: "chapter_searching", sources: ["hashing_search.swift"]),
.executableTarget(name: "bubble_sort", path: "chapter_sorting", sources: ["bubble_sort.swift"]),
.executableTarget(name: "insertion_sort", path: "chapter_sorting", sources: ["insertion_sort.swift"]),
.executableTarget(name: "quick_sort", path: "chapter_sorting", sources: ["quick_sort.swift"]),
.executableTarget(name: "merge_sort", path: "chapter_sorting", sources: ["merge_sort.swift"]),
.executableTarget(name: "radix_sort", path: "chapter_sorting", sources: ["radix_sort.swift"]),
]
)
55 changes: 55 additions & 0 deletions codes/swift/chapter_sorting/bubble_sort.swift
@@ -0,0 +1,55 @@
/**
* File: bubble_sort.swift
* Created Time: 2023-01-29
* Author: nuomi1 (nuomi1@qq.com)
*/

/* 冒泡排序 */
func bubbleSort(nums: inout [Int]) {
// 外循环:待排序元素数量为 n-1, n-2, ..., 1
for i in stride(from: nums.count - 1, to: 0, by: -1) {
// 内循环:冒泡操作
for j in stride(from: 0, to: i, by: 1) {
if nums[j] > nums[j + 1] {
// 交换 nums[j] 与 nums[j + 1]
let tmp = nums[j]
nums[j] = nums[j + 1]
nums[j + 1] = tmp
}
}
}
}

/* 冒泡排序(标志优化)*/
func bubbleSortWithFlag(nums: inout [Int]) {
// 外循环:待排序元素数量为 n-1, n-2, ..., 1
for i in stride(from: nums.count - 1, to: 0, by: -1) {
var flag = false // 初始化标志位
for j in stride(from: 0, to: i, by: 1) {
if nums[j] > nums[j + 1] {
// 交换 nums[j] 与 nums[j + 1]
let tmp = nums[j]
nums[j] = nums[j + 1]
nums[j + 1] = tmp
flag = true // 记录交换元素
}
}
if !flag { // 此轮冒泡未交换任何元素,直接跳出
break
}
}
}

@main
enum BubbleSort {
/* Driver Code */
static func main() {
var nums = [4, 1, 3, 1, 5, 2]
bubbleSort(nums: &nums)
print("冒泡排序完成后 nums = \(nums)")

var nums1 = [4, 1, 3, 1, 5, 2]
bubbleSortWithFlag(nums: &nums1)
print("冒泡排序完成后 nums1 = \(nums1)")
}
}
30 changes: 30 additions & 0 deletions codes/swift/chapter_sorting/insertion_sort.swift
@@ -0,0 +1,30 @@
/**
* File: insertion_sort.swift
* Created Time: 2023-01-29
* Author: nuomi1 (nuomi1@qq.com)
*/

/* 插入排序 */
func insertionSort(nums: inout [Int]) {
// 外循环:base = nums[1], nums[2], ..., nums[n-1]
for i in stride(from: 1, to: nums.count, by: 1) {
let base = nums[i]
var j = i - 1
// 内循环:将 base 插入到左边的正确位置
while j >= 0, nums[j] > base {
nums[j + 1] = nums[j] // 1. 将 nums[j] 向右移动一位
j -= 1
}
nums[j + 1] = base // 2. 将 base 赋值到正确位置
}
}

@main
enum InsertionSort {
/* Driver Code */
static func main() {
var nums = [4, 1, 3, 1, 5, 2]
insertionSort(nums: &nums)
print("插入排序完成后 nums = \(nums)")
}
}
67 changes: 67 additions & 0 deletions codes/swift/chapter_sorting/merge_sort.swift
@@ -0,0 +1,67 @@
/**
* File: merge_sort.swift
* Created Time: 2023-01-29
* Author: nuomi1 (nuomi1@qq.com)
*/

/**
* 合并左子数组和右子数组
* 左子数组区间 [left, mid]
* 右子数组区间 [mid + 1, right]
*/
func merge(nums: inout [Int], left: Int, mid: Int, right: Int) {
// 初始化辅助数组
let tmp = Array(nums[left ..< (right + 1)])
// 左子数组的起始索引和结束索引
let leftStart = left - left
let leftEnd = mid - left
// 右子数组的起始索引和结束索引
let rightStart = mid + 1 - left
let rightEnd = right - left
// i, j 分别指向左子数组、右子数组的首元素
var i = leftStart
var j = rightStart
// 通过覆盖原数组 nums 来合并左子数组和右子数组
for k in left ... right {
// 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++
if i > leftEnd {
nums[k] = tmp[j]
j += 1
}
// 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++
else if j > rightEnd || tmp[i] <= tmp[j] {
nums[k] = tmp[i]
i += 1
}
// 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
else {
nums[k] = tmp[j]
j += 1
}
}
}

/* 归并排序 */
func mergeSort(nums: inout [Int], left: Int, right: Int) {
// 终止条件
if left >= right { // 当子数组长度为 1 时终止递归
return
}
// 划分阶段
let mid = (left + right) / 2 // 计算中点
mergeSort(nums: &nums, left: left, right: mid) // 递归左子数组
mergeSort(nums: &nums, left: mid + 1, right: right) // 递归右子数组
// 合并阶段
merge(nums: &nums, left: left, mid: mid, right: right)
}

@main
enum MergeSort {
/* Driver Code */
static func main() {
/* 归并排序 */
var nums = [7, 3, 2, 6, 0, 1, 5, 4]
mergeSort(nums: &nums, left: 0, right: nums.count - 1)
print("归并排序完成后 nums = \(nums)")
}
}
124 changes: 124 additions & 0 deletions codes/swift/chapter_sorting/quick_sort.swift
@@ -0,0 +1,124 @@
/**
* File: quick_sort.swift
* Created Time: 2023-01-29
* Author: nuomi1 (nuomi1@qq.com)
*/

/* 元素交换 */
func swap(nums: inout [Int], i: Int, j: Int) {
let tmp = nums[i]
nums[i] = nums[j]
nums[j] = tmp
}

/* 快速排序类 */
// 快速排序类-哨兵划分
func quickSortPartition(nums: inout [Int], left: Int, right: Int) -> Int {
// 以 nums[left] 作为基准数
var i = left
var j = right
while i < j {
while i < j, nums[j] >= nums[left] {
j -= 1 // 从右向左找首个小于基准数的元素
}
while i < j, nums[i] <= nums[left] {
i += 1 // 从左向右找首个大于基准数的元素
}
swap(nums: &nums, i: i, j: j) // 交换这两个元素
}
swap(nums: &nums, i: i, j: left) // 将基准数交换至两子数组的分界线
return i // 返回基准数的索引
}

// 快速排序类-快速排序
func quickSort(nums: inout [Int], left: Int, right: Int) {
// 子数组长度为 1 时终止递归
if left >= right {
return
}
// 哨兵划分
let pivot = quickSortPartition(nums: &nums, left: left, right: right)
// 递归左子数组、右子数组
quickSort(nums: &nums, left: left, right: pivot - 1)
quickSort(nums: &nums, left: pivot + 1, right: right)
}

/* 快速排序类(中位基准数优化) */
// 快速排序类(中位基准数优化)-选取三个元素的中位数
func quickSortMedianThree(nums: [Int], left: Int, mid: Int, right: Int) -> Int {
if (nums[left] < nums[mid]) != (nums[left] < nums[right]) {
return left
} else if (nums[mid] < nums[left]) != (nums[mid] < nums[right]) {
return mid
} else {
return right
}
}

// 快速排序类(中位基准数优化)-哨兵划分(三数取中值)
func quickSortMedianPartition(nums: inout [Int], left: Int, right: Int) -> Int {
// 选取三个候选元素的中位数
let med = quickSortMedianThree(nums: nums, left: left, mid: (left + right) / 2, right: right)
// 将中位数交换至数组最左端
swap(nums: &nums, i: left, j: med)
return quickSortPartition(nums: &nums, left: left, right: right)
}

// 快速排序类(中位基准数优化)-快速排序
func quickSortMedian(nums: inout [Int], left: Int, right: Int) {
// 子数组长度为 1 时终止递归
if left >= right {
return
}
// 哨兵划分
let pivot = quickSortMedianPartition(nums: &nums, left: left, right: right)
// 递归左子数组、右子数组
quickSortMedian(nums: &nums, left: left, right: pivot - 1)
quickSortMedian(nums: &nums, left: pivot + 1, right: right)
}

/* 快速排序类(尾递归优化) */
// 快速排序类(尾递归优化)-哨兵划分
func quickSortTailCallPartition(nums: inout [Int], left: Int, right: Int) -> Int {
quickSortPartition(nums: &nums, left: left, right: right)
}

// 快速排序类(尾递归优化)-快速排序(尾递归优化)
func quickSortTailCall(nums: inout [Int], left: Int, right: Int) {
var left = left
var right = right
// 子数组长度为 1 时终止
while left < right {
// 哨兵划分操作
let pivot = quickSortTailCallPartition(nums: &nums, left: left, right: right)
// 对两个子数组中较短的那个执行快排
if (pivot - left) < (right - pivot) {
quickSortTailCall(nums: &nums, left: left, right: pivot - 1) // 递归排序左子数组
left = pivot + 1 // 剩余待排序区间为 [pivot + 1, right]
} else {
quickSortTailCall(nums: &nums, left: pivot + 1, right: right) // 递归排序右子数组
right = pivot - 1 // 剩余待排序区间为 [left, pivot - 1]
}
}
}

@main
enum QuickSort {
/* Driver Code */
static func main() {
/* 快速排序 */
var nums = [2, 4, 1, 0, 3, 5]
quickSort(nums: &nums, left: 0, right: nums.count - 1)
print("快速排序完成后 nums = \(nums)")

/* 快速排序(中位基准数优化) */
var nums1 = [2, 4, 1, 0, 3, 5]
quickSortMedian(nums: &nums1, left: 0, right: nums1.count - 1)
print("快速排序(中位基准数优化)完成后 nums1 = \(nums1)")

/* 快速排序(尾递归优化) */
var nums2 = [2, 4, 1, 0, 3, 5]
quickSortTailCall(nums: &nums2, left: 0, right: nums2.count - 1)
print("快速排序(尾递归优化)完成后 nums2 = \(nums2)")
}
}
70 changes: 70 additions & 0 deletions codes/swift/chapter_sorting/radix_sort.swift
@@ -0,0 +1,70 @@
/**
* File: radix_sort.swift
* Created Time: 2023-01-29
* Author: nuomi1 (nuomi1@qq.com)
*/

/* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */
func digit(num: Int, exp: Int) -> Int {
// 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
(num / exp) % 10
}

/* 计数排序(根据 nums 第 k 位排序) */
func countSort(nums: inout [Int], exp: Int) {
// 十进制的各位数字范围为 0~9 ,因此需要长度为 10 的桶
var bucket = Array(repeating: 0, count: 10)
let n = nums.count
// 借助桶来统计 0~9 各数字的出现次数
for i in nums.indices {
let d = digit(num: nums[i], exp: exp) // 获取 nums[i] 第 k 位,记为 d
bucket[d] += 1 // 统计数字 d 的出现次数
}
// 求前缀和,将“出现个数”转换为“数组索引”
for i in 1 ..< 10 {
bucket[i] += bucket[i - 1]
}
// 倒序遍历,根据桶内统计结果,将各元素填入暂存数组 tmp
var tmp = Array(repeating: 0, count: n)
for i in stride(from: n - 1, through: 0, by: -1) {
let d = digit(num: nums[i], exp: exp)
let j = bucket[d] - 1 // 获取 d 在数组中的索引 j
tmp[j] = nums[i] // 将当前元素填入索引 j
bucket[d] -= 1 // 将 d 的数量减 1
}
// 将 tmp 复制到 nums
for i in nums.indices {
nums[i] = tmp[i]
}
}

/* 基数排序 */
func radixSort(nums: inout [Int]) {
// 获取数组的最大元素,用于判断最大位数
var ma = Int.min
for num in nums {
if num > ma {
ma = num
}
}
// 按照从低位到高位的顺序遍历
for exp in sequence(first: 1, next: { ma >= ($0 * 10) ? $0 * 10 : nil }) {
// 对数组元素的第 k 位执行「计数排序」
// k = 1 -> exp = 1
// k = 2 -> exp = 10
// k = 3 -> exp = 100
// 即 exp = 10^(k-1)
countSort(nums: &nums, exp: exp)
}
}

@main
enum RadixSort {
/* Driver Code */
static func main() {
/* 基数排序 */
var nums = [23, 12, 3, 4, 788, 192]
radixSort(nums: &nums)
print("基数排序完成后 nums = \(nums)")
}
}