八大排序算法及对应的Swift & OC实现

251 阅读17分钟

八大排序算法

八大排序算法是指常见的八种排序算法,它们分别是:选择排序、冒泡排序、插入排序、希尔排序、归并排序、快速排序、堆排序和基数排序。下面将对这八种排序算法进行详细介绍。

  1. 选择排序:选择排序是一种简单直观的排序算法,它的基本思想是每次从待排序的元素中选择最小(或最大)的元素放到已排序序列的末尾。这样,经过多次选择和交换,就可以得到一个有序序列。选择排序的时间复杂度为O(n^2) [1].

  2. 冒泡排序:冒泡排序是一种交换排序算法,它的基本思想是通过相邻元素的比较和交换,将最大(或最小)的元素逐渐“冒泡”到待排序序列的末尾。冒泡排序的时间复杂度为O(n^2) [2].

  3. 插入排序:插入排序是一种简单直观的排序算法,它的基本思想是将待排序的元素插入到已排序序列的合适位置,从而得到一个有序序列。插入排序的时间复杂度为O(n^2) [3].

  4. 希尔排序:希尔排序是一种改进的插入排序算法,它通过将待排序序列分割成若干个子序列,分别进行插入排序,最后再对整个序列进行一次插入排序。希尔排序的时间复杂度为O(nlogn) [3].

  5. 归并排序:归并排序是一种分治算法,它的基本思想是将待排序序列分成两个子序列,分别进行排序,然后将两个有序子序列合并成一个有序序列。归并排序的时间复杂度为O(nlogn) [3].

  6. 快速排序:快速排序是一种分治算法,它的基本思想是通过一趟排序将待排序序列分割成独立的两部分,其中一部分的所有元素都比另一部分的所有元素小,然后再分别对这两部分进行排序。快速排序的时间复杂度为O(nlogn) [3].

  7. 堆排序:堆排序是一种选择排序算法,它的基本思想是将待排序序列构建成一个大顶堆(或小顶堆),然后依次取出堆顶元素,得到一个有序序列。堆排序的时间复杂度为O(nlogn) [3].

这些排序算法各有特点,适用于不同的排序场景。选择合适的排序算法可以提高排序效率。


Learn more:

  1. 8大排序算法图文讲解 - 腾讯云
  2. 八大排序算法详解(动图演示 思路分析 实例代码java 复杂度分析 适用场景) - 测试开发喵 - 博客园
  3. 八大排序算法总结与java实现 | iTimeTraveler

选择排序

选择排序的Swift实现代码如下所示:

func selectionSort(_ array: inout [Int]) {
    guard array.count > 1 else {
        return
    }
    
    for i in 0..<array.count {
        var minIndex = i
        for j in (i + 1)..<array.count {
            if array[j] < array[minIndex] {
                minIndex = j
            }
        }
        if minIndex != i {
            array.swapAt(i, minIndex)
        }
    }
}

注释:

  • selectionSort函数用于对传入的数组进行选择排序。
  • array是待排序的数组,使用inout关键字表示可以在函数内部修改传入的数组。
  • guard语句用于判断数组长度是否大于1,如果不满足条件,则直接返回,不进行排序。
  • 外层循环for i in 0..<array.count用于遍历数组中的每个元素。
  • 内层循环for j in (i + 1)..<array.count用于找到未排序部分中的最小元素。
  • 如果找到了比当前最小元素更小的元素,则更新最小元素的索引minIndex
  • 如果最小元素的索引minIndex不等于当前元素的索引i,则交换这两个元素的位置。
  • 最终完成选择排序后,原数组会被修改。

Objective-C:

#import <Foundation/Foundation.h>

void selectionSort(NSMutableArray *array) {
    NSInteger count = [array count];
    
    for (NSInteger i = 0; i < count - 1; i++) {
        NSInteger minIndex = i;
        
        for (NSInteger j = i + 1; j < count; j++) {
            if ([array[j] compare:array[minIndex]] == NSOrderedAscending) {
                minIndex = j;
            }
        }
        
        if (minIndex != i) {
            [array exchangeObjectAtIndex:i withObjectAtIndex:minIndex];
        }
    }
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSMutableArray *array = [NSMutableArray arrayWithArray:@[@5, @2, @8, @1, @9]];
        
        NSLog(@"Before sorting: %@", array);
        
        selectionSort(array);
        
        NSLog(@"After sorting: %@", array);
    }
    return 0;
}

冒泡排序

冒泡排序是一种简单直观的排序算法,下面是冒泡排序的Swift实现代码,并附有详细注释:

func bubbleSort(_ array: inout [Int]) {
    let n = array.count
    
    // 外层循环控制排序的轮数
    for i in 0..<n {
        // 内层循环用于比较相邻元素并交换位置
        for j in 0..<(n - 1 - i) {
            // 如果前一个元素大于后一个元素,则交换位置
            if array[j] > array[j + 1] {
                array.swapAt(j, j + 1)
            }
        }
    }
}

// 使用示例
var numbers = [5, 3, 8, 2, 1]
bubbleSort(&numbers)
print(numbers) // 输出:[1, 2, 3, 5, 8]

这段代码实现了冒泡排序算法,其中array是待排序的数组,通过inout关键字使得函数可以修改传入的数组。算法的核心是通过两层循环,每次比较相邻的两个元素并交换位置,直到整个数组有序为止。

下面是冒泡排序的Objective-C实现代码,并附带详细的注释。

// 冒泡排序的Objective-C实现
- (NSArray *)bubbleSort:(NSArray *)array {
    NSMutableArray *mutableArray = [array mutableCopy]; // 将输入的数组转换为可变数组
    
    NSInteger count = mutableArray.count;
    BOOL swapped; // 用于标记是否发生了交换
    
    for (NSInteger i = 0; i < count - 1; i++) {
        swapped = NO; // 每一轮开始时,将交换标记设置为NO
        
        for (NSInteger j = 0; j < count - i - 1; j++) {
            if ([mutableArray[j] compare:mutableArray[j+1]] == NSOrderedDescending) {
                // 如果前一个元素大于后一个元素,则交换它们的位置
                [mutableArray exchangeObjectAtIndex:j withObjectAtIndex:j+1];
                swapped = YES; // 标记发生了交换
            }
        }
        
        if (!swapped) {
            // 如果在一轮遍历中没有发生交换,说明列表已经有序,可以提前结束排序
            break;
        }
    }
    
    return [mutableArray copy]; // 将可变数组转换为不可变数组并返回
}

使用示例:

NSArray *array = @[@8, @1, @14, @23, @5, @9, @7, @4];
NSArray *sortedArray = [self bubbleSort:array];
NSLog(@"原数组:%@", array);
NSLog(@"排序后的数组:%@", sortedArray);

输出结果:

原数组:(8, 1, 14, 23, 5, 9, 7, 4)
排序后的数组:(1, 4, 5, 7, 8, 9, 14, 23)

这段代码中,我们首先将输入的数组转换为可变数组,然后使用两个嵌套的循环来遍历数组并比较相邻的元素。如果前一个元素大于后一个元素,则交换它们的位置。在每一轮遍历结束后,我们检查是否发生了交换,如果没有发生交换,说明列表已经有序,可以提前结束排序。最后,我们将可变数组转换为不可变数组并返回。

插入排序

下面是插入排序的Swift代码实现,并附有详细注释:

func insertionSort(_ array: [Int]) -> [Int] {
    var a = array // 创建一个可变的数组,用于存储排序结果
    for x in 1..<a.count { // 从第二个元素开始遍历数组
        var y = x // 记录当前元素的索引
        while y > 0 && a[y] < a[y - 1] { // 如果当前元素比前一个元素小,则交换它们的位置
            a.swapAt(y - 1, y)
            y -= 1 // 继续向前比较,直到找到合适的位置
        }
    }
    return a // 返回排序后的数组
}

这段代码使用了插入排序算法来对数组进行排序。它通过不断地将未排序的元素插入到已排序的部分中,从而实现排序的目的。

算法步骤:

  1. 创建一个可变的数组,用于存储排序结果。
  2. 从第二个元素开始遍历数组。
  3. 记录当前元素的索引。
  4. 如果当前元素比前一个元素小,则交换它们的位置,并继续向前比较,直到找到合适的位置。
  5. 返回排序后的数组。

这段代码使用了两个循环:外部循环遍历数组的每个元素,内部循环用于将当前元素插入到已排序的部分中的正确位置。

以下是插入排序的OC代码实现,并附有详细注释:

/**
 插入排序
 
 @param array 需要排序的数组
 */
- (void)insertionSort:(NSMutableArray *)array {
    // 遍历数组,从第二个元素开始
    for (int i = 1; i < array.count; i++) {
        // 将当前元素保存到临时变量
        NSNumber *temp = array[i];
        int j = i - 1;
        
        // 在已排序的部分中,从后往前比较,找到插入位置
        while (j >= 0 && [array[j] intValue] > [temp intValue]) {
            // 将比当前元素大的元素后移一位
            array[j + 1] = array[j];
            j--;
        }
        
        // 将当前元素插入到正确的位置
        array[j + 1] = temp;
    }
}

这段代码实现了插入排序算法。它通过遍历数组,从第二个元素开始,将当前元素与已排序的部分进行比较,找到插入位置,并将比当前元素大的元素后移一位,最后将当前元素插入到正确的位置。

希尔排序

希尔排序是一种高效的排序算法,下面是希尔排序的Swift代码实现,并附有详细注释:

func shellSort(_ array: inout [Int]) {
    let n = array.count
    var gap = n / 2 // 初始化间隔为数组长度的一半
    
    while gap > 0 {
        // 对每个间隔进行插入排序
        for i in gap..<n {
            let temp = array[i]
            var j = i
            
            // 在当前间隔内进行插入排序
            while j >= gap && array[j - gap] > temp {
                array[j] = array[j - gap]
                j -= gap
            }
            
            array[j] = temp
        }
        
        gap /= 2 // 缩小间隔
    }
}

// 测试希尔排序
var array = [9, 5, 7, 1, 3, 6, 2, 8, 4]
shellSort(&array)
print(array) // 输出:[1, 2, 3, 4, 5, 6, 7, 8, 9]

这段代码实现了希尔排序算法,具体步骤如下:

  1. 首先,定义一个间隔gap,初始值为数组长度的一半。
  2. 在每次循环中,对每个间隔进行插入排序。从间隔的位置开始,依次与前面的元素进行比较,如果前面的元素较大,则将其后移gap个位置。
  3. 在当前间隔内完成插入排序后,缩小间隔为原来的一半。
  4. 重复上述步骤,直到间隔为1时,完成整个排序过程。

这样,通过不断缩小间隔并进行插入排序,可以使得数组逐渐有序,最终得到一个有序的数组。

希尔排序(Shell Sort)是一种插入排序的改进算法,也被称为“缩小增量排序”(Diminishing Increment Sort)。它是由D.L.Shell于1959年提出的,是非稳定排序算法。希尔排序通过将待排序的元素按照一定的增量分组,对每组使用直接插入排序算法进行排序,随着增量的逐渐减小,最后整个数组被分成一组,排序完成。

下面是希尔排序的OC代码实现,并附有详细注释:

void shellSort(NSMutableArray *array) {
    NSInteger count = array.count;
    
    // 计算初始增量
    NSInteger gap = count / 2;
    
    // 增量逐渐减小,直到为1
    while (gap >= 1) {
        // 对每个增量进行插入排序
        for (NSInteger i = gap; i < count; i++) {
            // 当前元素与前面同组的元素进行比较并插入正确位置
            NSInteger j = i;
            while (j >= gap && [array[j] integerValue] < [array[j - gap] integerValue]) {
                [array exchangeObjectAtIndex:j withObjectAtIndex:j - gap];
                j -= gap;
            }
        }
        
        // 缩小增量
        gap /= 2;
    }
}

使用示例:

NSMutableArray *array = [@[@9, @5, @2, @7, @1, @8, @3, @6, @4] mutableCopy];
shellSort(array);
NSLog(@"%@", array);

输出结果:

1, 2, 3, 4, 5, 6, 7, 8, 9

这段代码实现了希尔排序算法,通过不断缩小增量的方式,对数组进行分组并进行插入排序,最终得到有序的数组。

归并排序

下面是归并排序的Swift代码实现,并附有详细的注释:

func mergeSort<Element: Comparable>(_ array: [Element]) -> [Element] {
    // 如果数组只有一个元素或为空,则直接返回
    guard array.count > 1 else {
        return array
    }
    
    // 将数组分成两半
    let middle = array.count / 2
    let left = mergeSort(Array(array[..<middle]))
    let right = mergeSort(Array(array[middle...]))
    
    // 合并左右两个子数组
    return merge(left, right)
}

private func merge<Element: Comparable>(_ left: [Element], _ right: [Element]) -> [Element] {
    var leftIndex = 0
    var rightIndex = 0
    var result: [Element] = []
    
    // 比较左右两个子数组的元素,并按顺序合并到结果数组中
    while leftIndex < left.count && rightIndex < right.count {
        let leftElement = left[leftIndex]
        let rightElement = right[rightIndex]
        
        if leftElement < rightElement {
            result.append(leftElement)
            leftIndex += 1
        } else if leftElement > rightElement {
            result.append(rightElement)
            rightIndex += 1
        } else {
            result.append(leftElement)
            leftIndex += 1
            result.append(rightElement)
            rightIndex += 1
        }
    }
    
    // 将剩余的元素添加到结果数组中
    if leftIndex < left.count {
        result.append(contentsOf: left[leftIndex...])
    }
    if rightIndex < right.count {
        result.append(contentsOf: right[rightIndex...])
    }
    
    return result
}

这段代码实现了归并排序算法。它使用了递归的方式将数组分割成较小的子数组,然后再将这些子数组合并排序。具体实现如下:

  • mergeSort函数:该函数接受一个泛型数组作为输入,并返回排序后的数组。首先判断数组的长度,如果只有一个元素或为空,则直接返回。然后将数组分成两半,分别对左右两个子数组进行递归调用,最后将左右两个子数组合并排序并返回结果。

  • merge函数:该函数接受两个泛型数组作为输入,并返回合并排序后的数组。函数内部使用了三个变量来记录左右两个子数组的当前索引和结果数组。通过比较左右两个子数组的元素大小,按顺序将较小的元素添加到结果数组中,直到其中一个子数组遍历完。最后,将剩余的元素添加到结果数组中。

测试代码如下:

let ints = [4, 5, 5467, 73, 234, 678, 87, 989]
let sortedInts = mergeSort(ints)
print(sortedInts)

输出结果为:[4, 5, 73, 87, 234, 678, 989, 5467],表示数组已按从小到大的顺序排序。

下面是OC语言实现归并排序的代码,并附有详细注释:

// 归并排序
- (NSArray *)mergeSort:(NSArray *)array {
    NSMutableArray *tmpArray = [[NSMutableArray alloc] init];
    
    // 将原始数组中的每个元素放入临时数组的子数组中
    for (int i = 0; i < array.count; i++) {
        NSMutableArray *subArray = [[NSMutableArray alloc] init];
        [subArray addObject:array[i]];
        [tmpArray addObject:subArray];
    }
    
    // 当临时数组中的子数组数量不为1时,进行合并操作
    while (tmpArray.count != 1) {
        NSUInteger j = 0;
        while (j < tmpArray.count - 1) {
            // 合并相邻的两个有序子数组
            tmpArray[j] = [self mergeSortedArray:tmpArray[j] secondArray:tmpArray[j + 1]];
            [tmpArray removeObjectAtIndex:j + 1];
            j++;
        }
    }
    
    return tmpArray.firstObject;
}

// 合并两个有序数组
- (NSArray *)mergeSortedArray:(NSArray *)firstArray secondArray:(NSArray *)secondArray {
    NSMutableArray *resultArray = [[NSMutableArray alloc] init];
    NSUInteger firstIndex = 0;
    NSUInteger secondIndex = 0;
    
    // 比较两个数组中的元素,将较小的元素放入结果数组中
    while (firstIndex < firstArray.count && secondIndex < secondArray.count) {
        if (firstArray[firstIndex] <= secondArray[secondIndex]) {
            [resultArray addObject:firstArray[firstIndex]];
            firstIndex++;
        } else {
            [resultArray addObject:secondArray[secondIndex]];
            secondIndex++;
        }
    }
    
    // 将剩余的元素放入结果数组中
    while (firstIndex < firstArray.count) {
        [resultArray addObject:firstArray[firstIndex]];
        firstIndex++;
    }
    
    while (secondIndex < secondArray.count) {
        [resultArray addObject:secondArray[secondIndex]];
        secondIndex++;
    }
    
    return resultArray;
}

这段代码实现了归并排序算法,具体注释如下:

  • mergeSort: 方法用于对传入的数组进行归并排序。首先,将原始数组中的每个元素放入临时数组的子数组中。然后,通过不断合并相邻的有序子数组,直到临时数组中只剩下一个有序数组。最后,返回临时数组中的唯一一个子数组作为排序后的结果数组。

  • mergeSortedArray:secondArray: 方法用于合并两个有序数组。通过比较两个数组中的元素,将较小的元素依次放入结果数组中。最后,将剩余的元素依次放入结果数组中。

快速排序

以下是一个用Swift实现的快速排序算法的示例代码,并附有详细的注释:

func quickSort(_ array: inout [Int], low: Int, high: Int) {
    if low < high {
        // 将数组分区,并获取分区点的索引
        let partitionIndex = partition(&array, low: low, high: high)
        
        // 对分区点左边的子数组进行快速排序
        quickSort(&array, low: low, high: partitionIndex - 1)
        
        // 对分区点右边的子数组进行快速排序
        quickSort(&array, low: partitionIndex + 1, high: high)
    }
}

func partition(_ array: inout [Int], low: Int, high: Int) -> Int {
    // 选择最后一个元素作为基准值
    let pivot = array[high]
    
    // 初始化分区点的索引
    var partitionIndex = low
    
    // 遍历数组,将小于基准值的元素放在分区点的左边
    for i in low..<high {
        if array[i] <= pivot {
            array.swapAt(i, partitionIndex)
            partitionIndex += 1
        }
    }
    
    // 将基准值放在分区点的位置上
    array.swapAt(partitionIndex, high)
    
    // 返回分区点的索引
    return partitionIndex
}

// 测试代码
var array = [5, 2, 8, 1, 9, 3]
quickSort(&array, low: 0, high: array.count - 1)
print(array)

这段代码实现了快速排序算法。快速排序是一种分治算法,它通过选择一个基准值,将数组分成两个子数组,并对子数组进行递归排序,最终将数组排序完成。在每一次递归中,选择一个基准值,并将小于基准值的元素放在基准值的左边,大于基准值的元素放在基准值的右边。通过不断递归地对子数组进行排序,最终实现整个数组的排序。

以下是快速排序的OC代码实现,并附有详细注释:

// 快速排序函数
- (void)quickSortArray:(NSMutableArray *)array withLeftIndex:(NSInteger)leftIndex andRightIndex:(NSInteger)rightIndex {
    // 如果数组长度为0或1时返回
    if (leftIndex >= rightIndex) {
        return;
    }
    
    NSInteger i = leftIndex;
    NSInteger j = rightIndex;
    // 记录比较基准数
    NSInteger key = [array[i] integerValue];
    
    while (i < j) {
        // 从右边开始查找比基准数小的值
        while (i < j && [array[j] integerValue] >= key) {
            j--;
        }
        // 将查找到的小值调换到i的位置
        array[i] = array[j];
        
        // 从左边开始查找比基准数大的值
        while (i < j && [array[i] integerValue] <= key) {
            i++;
        }
        // 将查找到的大值调换到j的位置
        array[j] = array[i];
    }
    
    // 将基准数放到正确位置
    array[i] = @(key);
    
    // 递归排序基准数左边的数组
    [self quickSortArray:array withLeftIndex:leftIndex andRightIndex:i - 1];
    // 递归排序基准数右边的数组
    [self quickSortArray:array withLeftIndex:i + 1 andRightIndex:rightIndex];
}

// 使用示例
NSMutableArray *arr = [[NSMutableArray alloc] initWithObjects:@(6), @(1), @(2), @(5), @(9), @(4), @(3), @(7), nil];
[self quickSortArray:arr withLeftIndex:0 andRightIndex:arr.count - 1];
NSLog(@"%@", arr);

堆排序

以下是堆排序的Swift代码实现,并附有详细注释:

// 堆排序函数
func heapSort(_ array: inout [Int]) {
    // 构建大顶堆
    buildMaxHeap(&array)
    
    // 从最后一个元素开始,依次将堆顶元素与末尾元素交换,并重新调整堆
    for i in stride(from: array.count - 1, through: 1, by: -1) {
        array.swapAt(0, i)
        heapify(&array, 0, i)
    }
}

// 构建大顶堆
func buildMaxHeap(_ array: inout [Int]) {
    let n = array.count
    // 从最后一个非叶子节点开始,依次向上调整堆
    for i in stride(from: n / 2 - 1, through: 0, by: -1) {
        heapify(&array, i, n)
    }
}

// 调整堆
func heapify(_ array: inout [Int], _ i: Int, _ length: Int) {
    var largest = i
    let left = 2 * i + 1
    let right = 2 * i + 2
    
    // 找到左子节点和右子节点中的最大值
    if left < length && array[left] > array[largest] {
        largest = left
    }
    if right < length && array[right] > array[largest] {
        largest = right
    }
    
    // 如果最大值不是当前节点,则交换节点,并继续调整堆
    if largest != i {
        array.swapAt(i, largest)
        heapify(&array, largest, length)
    }
}

这段代码实现了堆排序算法。首先,通过buildMaxHeap函数构建一个大顶堆,然后从最后一个元素开始,依次将堆顶元素与末尾元素交换,并重新调整堆,直到整个数组有序。

下面是堆排序的OC代码实现:

// 堆排序函数
void heapSort(int arr[], int n) {
    // 构建大根堆
    for (int i = n / 2 - 1; i >= 0; i--) {
        heapify(arr, n, i);
    }
    
    // 交换堆顶元素和最后一个元素,并重新调整堆
    for (int i = n - 1; i > 0; i--) {
        swap(&arr, &arr[i]);
        heapify(arr, i, 0);
    }
}

// 调整堆
void heapify(int arr[], int n, int i) {
    int largest = i; // 初始化最大元素为根节点
    int left = 2 * i + 1; // 左子节点
    int right = 2 * i + 2; // 右子节点
    
    // 如果左子节点大于根节点,更新最大元素的索引
    if (left < n && arr[left] > arr[largest]) {
        largest = left;
    }
    
    // 如果右子节点大于根节点,更新最大元素的索引
    if (right < n && arr[right] > arr[largest]) {
        largest = right;
    }
    
    // 如果最大元素不是根节点,交换根节点和最大元素,并继续调整堆
    if (largest != i) {
        swap(&arr[i], &arr[largest]);
        heapify(arr, n, largest);
    }
}

// 交换两个元素的值
void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

使用上述代码,可以对一个整型数组进行堆排序。首先,调用heapSort函数,传入待排序的数组和数组长度。该函数会先构建一个大根堆,然后进行交换和调整堆的操作,直到数组完全有序。

这段代码中,heapify函数用于调整堆,它会比较根节点和左右子节点的值,将最大的元素放在根节点,并递归调整子堆。swap函数用于交换两个元素的值。

请注意,上述代码是一个简单的示例,实际使用时可能需要根据具体情况进行适当的修改和优化。