笔者在学习数据结构与算法时,尝试着将排序算法以动画的形式呈现出来更加方便理解记忆,本文配合Demo 在Object-C中学习数据结构与算法之排序算法阅读更佳。

目录

  • 选择排序
  • 冒泡排序
  • 插入排序
  • 快速排序
  • 双路快速排序
  • 三路快速排序
  • 堆排序
  • 总结与收获
  • 参考与阅读

选择排序

选择排序是一种简单直观的排序算法,无论什么数据进去都是 O(n²) 的时间复杂度。所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。

1.算法步骤

  1. 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置

  2. 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。

  3. 重复第二步,直到所有元素均排序完毕。

2.动画演示

3.代码实现

 1#pragma mark - /**选择排序*/
2- (void)mb_selectionSort{
3    for (int i = 0; i < self.count; i++) {
4        for (int j = i + 1; j < self.count ; j++) {
5            if (self.comparator(self[i],self[j]) == NSOrderedDescending) {
6                [self mb_exchangeWithIndexA:i  indexB:j];
7            }
8        }
9    }
10}

冒泡排序

冒泡排序(Bubble Sort)也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

1.算法步骤

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。

  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。

  3. 针对所有的元素重复以上的步骤,除了最后一个。

  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

2.动画演示

3.代码实现

 1#pragma mark - /**冒泡排序*/
2- (void)mb_bubbleSort{
3    bool swapped;
4    do {
5        swapped = false;
6        for (int i = 1; i < self.count; i++) {
7            if (self.comparator(self[i - 1],self[i]) == NSOrderedDescending) {
8                swapped = true;
9                [self mb_exchangeWithIndexA:i  indexB:i- 1];
10            }
11        }
12    } while (swapped);
13}

插入排序

插入排序的代码实现虽然没有冒泡排序和选择排序那么简单粗暴,但它的原理应该是最容易理解的了,因为只要打过扑克牌的人都应该能够秒懂。插入排序是一种最简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

1.算法步骤

  1. 将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。

  2. 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)

2.动画演示

3.代码实现

 1#pragma mark - /**插入排序*/
2- (void)mb_insertionSort{
3    for (int i = 0; i < self.count; i++) {
4        id e = self[i];
5        int j;
6        for (j = i; j > 0 && self.comparator(self[j - 1],e) == NSOrderedDescending; j--) {
7            [self mb_exchangeWithIndexA:j  indexB:j- 1];
8        }
9        self[j] = e;
10    }
11}

归并排序

归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

作为一种典型的分而治之思想的算法应用,归并排序的实现由两种方法:

  1. 自上而下的递归(所有递归的方法都可以用迭代重写,所以就有了第 2 种方法)
  2. 自下而上的迭代;

本文使用的是自顶向下的归并排序

1.算法步骤

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;

  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置;

  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;

  4. 重复步骤 3 直到某一指针达到序列尾;

  5. 将另一序列剩下的所有元素直接复制到合并序列尾。

2.动画演示

3.代码实现

 1#pragma mark - /**归并排序 自顶向下*/
2- (void)mb_mergeSort{
3    [self mb_mergeSortArray:self LeftIndex:0 rightIndex:(int)self.count - 1];
4}
5- (void)mb_mergeSortArray:(NSMutableArray *)array LeftIndex:(int )l rightIndex:(int)r{
6    if(l >= r) return;
7    int mid = (l + r) / 2;
8    [self mb_mergeSortArray:self LeftIndex:l rightIndex:mid];
9    [self mb_mergeSortArray:self LeftIndex:mid + 1 rightIndex:r];
10    [self mb_mergeSortArray:self LeftIndex:l midIndex:mid rightIndex:r];
11}
12
13- (void)mb_mergeSortArray:(NSMutableArray *)array LeftIndex:(int )l midIndex:(int )mid rightIndex:(int )r{
14
15    SEL func = NSSelectorFromString(@"resetSortArray:");
16    // 开辟新的空间 r-l+1的空间
17    NSMutableArray *aux = [NSMutableArray arrayWithCapacity:r-l+1];
18    for (int i = l; i <= r; i++) {
19        // aux 中索引 i-l 的对象 与 array 中索引 i 的对象一致
20        aux[i-l] = self[i];
21    }
22    // 初始化,i指向左半部分的起始索引位置l;j指向右半部分起始索引位置mid+1
23    int i = l, j = mid + 1;
24    for ( int k = l; k <= r; k++) {
25        if (i > mid) { // 如果左半部分元素已经全部处理完毕
26            self.comparator(nil, nil);
27            self[k] = aux[j - l];
28            j++;
29        }else if(j > r){// 如果右半部分元素已经全部处理完毕
30            self.comparator(nil, nil);
31            self[k] = aux[i - l];
32            i++;
33        }else if(self.comparator(aux[i - l], aux[j - l]) == NSOrderedAscending){// 左半部分所指元素 < 右半部分所指元素
34            self[k] = aux[i - l];
35            i++;
36        }else{
37            self.comparator(nil, nil);
38            self[k] = aux[j - l];
39            j++;
40        }
41
42        NSMutableArray *mutArray = [NSMutableArray array];
43        [self enumerateObjectsUsingBlock:^(MBBarView *  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
44            [mutArray addObject:[NSString stringWithFormat:@"%f",obj.frame.size.height]];
45        }];
46
47        objc_msgSendSortArray(self.vc,func,mutArray);
48    }
49
50}

快速排序

快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要 Ο(nlogn) 次比较。在最坏状况下则需要 Ο(n2) 次比较,但这种状况并不常见。事实上,快速排序通常明显比其他 Ο(nlogn) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。

快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。

快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。

快速排序的名字起的是简单粗暴,因为一听到这个名字你就知道它存在的意义,就是快,而且效率高!它是处理大数据最快的排序算法之一了。

1.算法步骤

  1. 从数列中挑出一个元素,称为 “基准”(pivot);

  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;

  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;

快速排序的优化可考虑当分区间隔小的的时候转而使用插入排序

2.动画演示

3.代码实现

 1#pragma mark - /**快速排序*/
2- (void)mb_quickSort{
3    //要特别注意边界的情况
4    [self mb_quickSort:self indexL:0 indexR:(int)self.count - 1];
5}
6- (void)mb_quickSort:(NSMutableArray *)array indexL:(int)l indexR:(int)r{
7    if (l >= r) return;
8    int p = [self __partition:array indexL:l indexR:r];
9    [self mb_quickSort:array indexL:l indexR:p-1];
10    [self mb_quickSort:array indexL:p + 1 indexR:r];
11}
12/**
13 对arr[l...r]部分进行partition操作
14 返回p, 使得arr[l...p-1] < arr[p] ; arr[p+1...r] > arr[p]
15
16 @param array array
17 @param l 左
18 @param r 右
19 @return 返回p
20 */
21- (int)__partition:(NSMutableArray *)array indexL:(int)l indexR:(int)r{
22    int j = l;// arr[l+1...j] < v ; arr[j+1...i) > v
23    for (int i = l + 1; i <= r ; i++) {
24        if ( self.comparator(array[i], array[ l]) == NSOrderedAscending) {
25            j++;
26            //交换
27            [self mb_exchangeWithIndexA:j indexB:i];
28        }
29    }
30    self.comparator(nil, nil);
31    [self mb_exchangeWithIndexA:j indexB:l];
32    return j;
33}

双路快速排序

过多重复键值使Quick Sort降至O(n^2)
使用双快速排序后, 我们的快速排序算法可以轻松的处理包含大量元素的数组
快速排序的优化可考虑当分区间隔小的的时候转而使用插入排序

1.算法图示

2.动画演示

3.代码实现

 1#pragma mark - /**双路快排*/
2///使用双快速排序后, 我们的快速排序算法可以轻松的处理包含大量元素的数组
3- (void)mb_identicalQuickSort{
4    //要特别注意边界的情况
5    [self mb_quickSort:self indexL:0 indexR:(int)self.count - 1];
6}
7- (void)mb_identicalQuickSort:(NSMutableArray *)array indexL:(int)l indexR:(int)r{
8    if (l >= r) return;
9    int p = [self __partition2:array indexL:l indexR:r];
10    [self mb_quickSort:array indexL:l indexR:p-1];
11    [self mb_quickSort:array indexL:p + 1 indexR:r];
12}
13- (int)__partition2:(NSMutableArray *)array indexL:(int)l indexR:(int)r{
14    // 随机在arr[l...r]的范围中, 选择一个数值作为标定点pivot
15    [self mb_exchangeWithIndexA:l indexB:(arc4random()%(r-l+1))];
16    id v = array[l];
17    // arr[l+1...i) <= v; arr(j...r] >= v
18    int i = l + 1, j = r;
19    while (true) {
20
21        while (i <= r && self.comparator(array[i],v) == NSOrderedAscending)
22            i++;
23
24        while (j > l + 1 && self.comparator(array[j],v) == NSOrderedDescending)
25            j--;
26
27        if (i > j) {
28            break;
29        }
30        [self mb_exchangeWithIndexA:i indexB:j];
31
32        i++;
33        j--;
34    }
35    [self mb_exchangeWithIndexA:l indexB:j];
36
37    return j;
38}

三路快速排序

对于包含有大量重复数据的数组, 三路快排有巨大的优势
对于一般性的随机数组和近乎有序的数组, 三路快排的效率虽然不是最优的, 但是是在非常可以接受的范围里
因此, 在一些语言中, 三路快排是默认的语言库函数中使用的排序算法。比如Java:)

快速排序的优化可考虑当分区间隔小的的时候转而使用插入排序

1.算法图示

2.动画演示

3.代码实现

 1#pragma mark - /**三路快排*/
2//对于包含有大量重复数据的数组, 三路快排有巨大的优势
3- (void)mb_quick3WaysSort{
4    //要特别注意边界的情况
5    [self mb_quick3WaysSort:self indexL:0 indexR:(int)self.count - 1];
6}
7/// 递归的三路快速排序算法
8- (void)mb_quick3WaysSort:(NSMutableArray *)array indexL:(int)l indexR:(int)r{
9
10    if (l >= r)  return;
11
12    self.comparator(nil, nil);
13    // 随机在arr[l...r]的范围中, 选择一个数值作为标定点pivot
14    [self mb_exchangeWithIndexA:l indexB:(arc4random_uniform(r-l+1) + l)];
15
16    id v = array[l];
17
18    int lt = l; // array[l+1...lt] < v
19    int gt = r + 1; // array[gt...r] > v
20    int i = l + 1; // array[lt+1...i) == v
21
22    while (i < gt) {
23        if ( [self compareWithBarOne:array[i] andBarTwo:v] == NSOrderedAscending) {
24            self.comparator(nil, nil);
25            [self mb_exchangeWithIndexA:i indexB:lt + 1];
26
27            i++;
28            lt++;
29        }else if  ([self compareWithBarOne:array[i] andBarTwo:v] == NSOrderedDescending){
30            self.comparator(nil, nil);
31            [self mb_exchangeWithIndexA:i indexB:gt - 1];
32            gt--;
33        }else{ //array[i] == v
34            i++;
35        }
36
37    }
38    self.comparator(nil,nil);
39    [self mb_exchangeWithIndexA:l indexB:lt];
40
41    [self mb_quick3WaysSort:array indexL:l indexR:lt-1];
42    [self mb_quick3WaysSort:array indexL:gt indexR:r];
43
44}

堆排序

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。分为两种方法:

大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;
堆排序的平均时间复杂度为 Ο(nlogn)。

1.算法步骤

  1. 创建一个堆 H[0……n-1];

  2. 把堆首(最大值)和堆尾互换;

  3. 把堆的尺寸缩小 1,并调用 shift_down(1),目的是把新的数组顶端数据调整到相应位置;

  4. 重复步骤 2,直到堆的尺寸为 1

2.动画演示

3.代码实现

 1///shift_down操作
2- (void)shiftDown:(int )k{
3    while (2 * k <= _count) {
4        int j = 2 * k;
5        if (j + 1 <= _count && [self heapCompareWithBarOne:_data[j + 1] andBarTwo:_data[j]] == NSOrderedDescending) j++;//左孩子小于右孩子
6        if ([self heapCompareWithBarOne:_data[k] andBarTwo:_data[j]] == NSOrderedDescending) break;//父节点大于子节点
7        self.comparator(nil, nil);
8        [_data mb_exchangeWithIndexA:k indexB:j];
9        k = j;
10    }
11}

如果各位读者看完有所收获欢迎在Github上给个star 谢谢各位

参考与阅读

在Object-C中学习数据结构与算法之排序算法的更多相关文章

  1. javascript数据结构与算法--高级排序算法

    javascript数据结构与算法--高级排序算法 高级排序算法是处理大型数据集的最高效排序算法,它是处理的数据集可以达到上百万个元素,而不仅仅是几百个或者几千个.现在我们来学习下2种高级排序算法-- ...

  2. 数据结构与算法——常用排序算法及其Java实现

    冒泡排序 原理:依次比较相邻的两个数,将小数放在前面(左边),大数放在后面(右边),就像冒泡一样具体操作:第一趟,首先比较第1个和第2个数,将小数放前,大数放后.然后比较第2个数和第3个数,将小数放前 ...

  3. javascript数据结构与算法--高级排序算法(快速排序法,希尔排序法)

    javascript数据结构与算法--高级排序算法(快速排序法,希尔排序法) 一.快速排序算法 /* * 这个函数首先检查数组的长度是否为0.如果是,那么这个数组就不需要任何排序,函数直接返回. * ...

  4. javascript数据结构与算法--基本排序算法(冒泡、选择、排序)及效率比较

    javascript数据结构与算法--基本排序算法(冒泡.选择.排序)及效率比较 一.数组测试平台. javascript数据结构与算法--基本排序(封装基本数组的操作),封装常规数组操作的函数,比如 ...

  5. JS中算法之排序算法

    1.基本排序算法 1.1.冒泡排序 它是最慢的排序算法之一. 1.不断比较相邻的两个元素,如果前一个比后一个大,则交换位置. 2.当比较完第一轮的时候最后一个元素应该是最大的一个. 3.按照步骤一的方 ...

  6. c/c++ 通用的(泛型)算法 之 只读算法,写算法,排序算法

    通用的(泛型)算法 之 只读算法,写算法,排序算法 只读算法: 函数名 功能描述 accumulate 求容器里元素的和 equal 比较2个容器里的元素 写算法 函数名 功能描述 fill 用给定值 ...

  7. 学习Java绝对要懂的,Java编程中最常用的几种排序算法!

    今天给大家分享一下Java中几种常见的排序算法的Java代码 推荐一下我的Java学习羊君前616,中959,最后444.把数字串联起来!     ,群里有免费的学习视频和项目给大家练手.大神有空时也 ...

  8. JAVA学习笔记(4)—— 排序算法

    排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作.排序算法,就是如何使得记录按照要求排列的方法. 排序算法大体可分为两种: 一种是比较排序,时间复杂度O(nlogn) ...

  9. [转]Java学习---7大经典的排序算法总结实现

    [原文]https://www.toutiao.com/i6591634652274885128/ 常见排序算法总结与实现 本文使用Java实现这几种排序. 以下是对排序算法总体的介绍. 冒泡排序 比 ...

随机推荐

  1. Python Flask学习笔记之Hello World

    Python Flask学习笔记之Hello World 安装virtualenv,配置Flask开发环境 virtualenv 虚拟环境是Python解释器的一个私有副本,在这个环境中可以安装私有包 ...

  2. dell T130服务器加内存

    需求:客户一台dell T130塔式服务器,由于本机只有一条8G内存,系统运行比较慢,需要再增加一条8G内存. 增加过程:第一次增加时由于没有注意机器上内存频率是2133的,所以新增加的一条2400频 ...

  3. 大数据之hiveSQL

    最近增加了学习java基础算法,包括几种排序算法,二叉树(前序,后序,中序),队列和栈,bmp搜索,广义搜索算法,迭代等等一些技巧(自己动手绝对比单纯的理论要强的多,多练练) HIVE是hadoop生 ...

  4. python学习总结(一)

    1.编码格式发展历史 ASCII 255 Ibytes --> 1980 gb2312  7000+ -->1995 GBK1.0 2W+ -->2000 GB18030 2.7W+ ...

  5. bash基础特性3(shell编程)

    Linux上文本处理三剑客: grep:文本过滤工具 sed:stream editor,文本编辑工具 awk:文本报告生成器 grep -v:显示不能够被pattern匹配到的行 -i:忽略字符大小 ...

  6. Android学习笔记(1):常用按钮点击事件处理方式

    1.从布局文件获取对应的控件然后对其添加点击监听器. Button loginBtn; @Override protected void onCreate(Bundle savedInstanceSt ...

  7. day24_雷神_django项目部署

    # django项目部署 ... curl -I www.baidu.com 得到响应头信息 vim 里shift + % 找括号的另一半 find / -name virtualenv 3.创建虚拟 ...

  8. Django 搭建博客记(二)

    当前博客实现的功能 实现 Markdown 语法功能 python 安装 markdown 模块 添加 markdown 过滤 实现代码高亮 通过 CSS 样本实现 分页功能 简单的关于页面和标签分类 ...

  9. FineCMS 5.0.10 多个 漏洞详细分析过程

    0x01 前言 已经一个月没有写文章了,最近发生了很多事情,水文一篇.今天的这个CMS是FineCMS,版本是5.0.10版本的几个漏洞分析,从修补漏洞前和修补后的两方面去分析. 文中的evai是特意 ...

  10. Javascript高级编程学习笔记(54)—— DOM2和DOM3(6)范围选择

    范围 为了让开发人员更加方便地控制页面“DOM2级遍历和范围”模块定义了“范围”接口 通过该接口开发人员可以选择文档中的一个区域,而不必考虑元素的界限 在常规操作不能有效地修改文档时,使用范围往往可以 ...