千家信息网

各大排序算法的Objective-C实现以及图形化演示比较是怎样的

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,这期内容当中小编将会给大家带来有关各大排序算法的Objective-C实现以及图形化演示比较是怎样的,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。用Objectiv
千家信息网最后更新 2026年11月01日各大排序算法的Objective-C实现以及图形化演示比较是怎样的

这期内容当中小编将会给大家带来有关各大排序算法的Objective-C实现以及图形化演示比较是怎样的,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

用Objective-C实现几种基本的排序算法,并把排序的过程图形化显示。其实算法还是挺有趣的 ^ ^.

  • 选择排序

  • 冒泡排序

  • 插入排序

  • 快速排序

选择排序

以升序为例。

选择排序比较好理解,一句话概括就是依次按位置挑选出适合此位置的元素来填充。

  1. 暂定***个元素为最小元素,往后遍历,逐个与最小元素比较,若发现更小者,与先前的"最小元素"交换位置。达到更新最小元素的目的。

  2. 一趟遍历完成后,能确保刚刚完成的这一趟遍历中,最的小元素已经放置在前方了。然后缩小排序范围,新一趟排序从数组的第二个元素开始。

  3. 在新一轮排序中重复第1、2步骤,直到范围不能缩小为止,排序完成。


选择排序

以下方法在NSMutableArray+JXSort.m中实现

- (void)jx_selectionSortUsingComparator:(JXSortComparator)comparator didExchange:(JXSortExchangeCallback)exchangeCallback {  if (self.count == 0) {  return;  }  for (NSInteger i = 0; i < self.count - 1; i ++) { for (NSInteger j = i + 1; j < self.count; j ++) {  if (comparator(self[i], self[j]) == NSOrderedDescending) {  [self jx_exchangeWithIndexA:i indexB:j didExchange:exchangeCallback];  }  }  }  }

冒泡排序

在一趟遍历中,不断地对相邻的两个元素进行排序,小的在前大的在后,这样会造成大值不断沉底的效果,当一趟遍历完成时,***的元素会被排在后方正确的位置上。

然后缩小排序范围,即去掉***方位置正确的元素,对前方数组进行新一轮遍历,重复第1步骤。直到范围不能缩小为止,排序完成。

冒泡排序
- (void)jx_bubbleSortUsingComparator:(JXSortComparator)comparator didExchange:(JXSortExchangeCallback)exchangeCallback {  if (self.count == 0) {  return;  }  for (NSInteger i = self.count - 1; i > 0; i --) {  for (NSInteger j = 0; j < i; j ++) {  if (comparator(self[j], self[j + 1]) == NSOrderedDescending) {  [self jx_exchangeWithIndexA:j indexB:j + 1 didExchange:exchangeCallback];  }  }  }  }

插入排序

插入排序是从一个乱序的数组中依次取值,插入到一个已经排好序的数组中。

这看起来好像要两个数组才能完成,但如果只想在同一个数组内排序,也是可以的。此时需要想象出两个区域:前方有序区和后方乱序区。

1、分区。开始时前方有序区只有一个元素,就是数组的***个元素。然后把从第二个元素开始直到结尾的数组作为乱序区。

2、从乱序区取***个元素,把它正确插入到前方有序区中。把它与前方无序区的***一个元素比较,亦即与它的前一个元素比较。

  • 如果比前一个元素要大,则不需要交换,这时有序区扩充一格,乱序区往后缩减一格,相当于直接拼在有序区末尾。

  • 如果和前一个元素相等,则继续和前二元素比较、再和前三元素比较……如果往前遍历到头了,发现前方所有元素值都长一个样的话(囧),那也可以,不需要交换,这时有序区扩充一格,乱序区往后缩减一格,相当于直接拼在有序区末尾。如果比前一个元素大呢?对不起作为有序区不可能出现这种情况。如果比前一个元素小呢,请看下一点。

  • 如果比前一个元素小,则交换它们的位置。交换完后,继续比较取出元素和它此时的前一个元素,若更小就交换,若相等就比较前一个,直到遍历完成。至此,把乱序区***个元素正确插入到前方有序区中。

3、往后缩小乱序区范围,继续取缩小范围后的***个元素,重复第2步骤。直到范围不能缩小为止,排序完成。

插入排序
- (void)jx_insertionSortUsingComparator:(JXSortComparator)comparator didExchange:(JXSortExchangeCallback)exchangeCallback {  if (self.count == 0) {  return;  }  for (NSInteger i = 1; i < self.count; i ++) {  for (NSInteger j = i; j > 0 && comparator(self[j], self[j - 1]) == NSOrderedAscending; j --) {  [self jx_exchangeWithIndexA:j indexB:j - 1 didExchange:exchangeCallback];  }  }  }

快速排序

快排的版本有好几种,粗略可分为:

  1. 原始的快排。

  2. 为制造适合高效排序环境而事先打乱数组顺序的快排。

  3. 为数组内大量重复值而优化的三向切分快排。

这里只讨论原始的快排。

关于在快排过程中何时进行交换以及交换谁的问题,我看见两种不同的思路:

  1. 当左右两个游标都停止时,交换两个游标所指向元素。枢轴所在位置暂时不变,直到两个游标相遇重合,才更新枢轴位置,交换枢轴与游标所指元素。

  2. 当右游标找到一个比枢轴小的元素时,马上把枢轴交换到游标所在位置,而游标位置的元素则移到枢轴那里。完成一次枢轴更新。然后左游标再去寻找比枢轴大的元素,同理。

第1种思路可以有效降低交换频率,在游标相遇后再对枢轴进行定位,这步会导致略微增加了比较的次数;

第2种思路交换操作会比较频繁,但是在交换的过程中同时也把枢轴的位置不断进行更新,当游标相遇时,枢轴的定位也完成了。

在两种思路都尝试实现过后,我还是喜欢第2种,即便交换操作会多一些,但实质上的交换只是对数组特定位置的赋值,这种操作还是挺快的。

  1. 从待排序数组中选一个值作为分区的参考界线,一般选***个元素即可。这个选出来的值可叫做枢轴pivot,它将会在一趟排序中不断被移动位置,只终移动到位于整个数组的正确位置上。

  2. 一趟排序的目标是把小于枢轴的元素放在前方,把大于枢轴的元素放在后方,枢轴放在中间。这看起来一趟排序实质上所干的事情就是把数组分区。接下来考虑怎么完成一次分区。

  3. 记一个游标i,指向待排序数组的首位,它将会不断向后移动;
    再记一个游标j,指向待排序数组的末位,它将会不断向前移动。
    这样可以预见的是,i 、j终有相遇时,当它们相遇的时候,就是这趟排序完成时。

  4. 现在让游标j从后往前扫描,寻找比枢轴小的元素x,找到后停下来,准备把这个元素扔到前方去。

  5. 在同一个数组内排序并不能扩大数组的容量,那怎么扔呢?
    因为刚才把首位元素选作为pivot,所以当前它们的位置关系是pivot ... x。
    又排序目标是升序,x是个小值却放在了pivot的后方,不妥,需要交换它们的位置。

  6. 交换完后,它们的位置关系变成了x ... pivot。此时j指向了pivot,i指向了x。

  7. 现在让游标i向后扫描,寻找比枢轴大的元素y,找到后停下来,与pivot进行交换。
    完成后的位置关系是pivot ... y,此时i指向pivot,即pivot移到了i的位置。

  8. 这里有个小优化,在i向后扫描开始时,i是指向x的,而在上一轮j游标的扫描中我们已经知道x是比pivot小的,所以完全可以让i跳过x,不需要拿着x和pivot再比较一次。
    结论是在j游标的交换完成后,顺便把i往后移一位,i ++。
    同理,在i游标的交换完成后,顺便把j往前移一位,j --。

  9. 在扫描的过程中如果发现与枢轴相等的元素怎么办呢?
    因我们不讨论三向切分的快排优化算法,所以这里答案是:不理它。
    随着一趟一趟的排序,它们会慢慢被更小的元素往后挤,被更大的元素往前挤,***的结果就是它们都会和枢轴一起移到了中间位置。

  10. 当i和j相遇时,i和j都会指向pivot。在我们的分区方法里,把i返回,即在分区完成后把枢轴位置返回。

  11. 接下来,让分出的两个数组分别按上述步骤各自分区,这是个递归的过程,直到数组不能再分时,排序完成。

快速排序是很天才的设计,实现不复杂,关键是它真的很快~

快速排序.gif

  1. - (void)jx_quickSortUsingComparator:(JXSortComparator)comparator didExchange:(JXSortExchangeCallback)exchangeCallback {

  2. if (self.count == 0) {

  3. return;

  4. }

  5. [self jx_quickSortWithLowIndex:0 highIndex:self.count - 1 usingComparator:comparator didExchange:exchangeCallback];

  6. }

  7. - (void)jx_quickSortWithLowIndex:(NSInteger)low highIndex:(NSInteger)high usingComparator:(JXSortComparator)comparator didExchange:(JXSortExchangeCallback)exchangeCallback {

  8. if (low >= high) {

  9. return;

  10. }

  11. NSInteger pivotIndex = [self jx_quickPartitionWithLowIndex:low highIndex:high usingComparator:comparator didExchange:exchangeCallback];

  12. [self jx_quickSortWithLowIndex:low highIndex:pivotIndex - 1 usingComparator:comparator didExchange:exchangeCallback];

  13. [self jx_quickSortWithLowIndex:pivotIndex + 1 highIndex:high usingComparator:comparator didExchange:exchangeCallback];

  14. }

  15. - (NSInteger)jx_quickPartitionWithLowIndex:(NSInteger)low highIndex:(NSInteger)high usingComparator:(JXSortComparator)comparator didExchange:(JXSortExchangeCallback)exchangeCallback {

  16. id pivot = self[low];

  17. NSInteger i = low;

  18. NSInteger j = high;

  19. while (i < j) {

  20. // 略过大于等于pivot的元素

  21. while (i < j && comparator(self[j], pivot) != NSOrderedAscending) {

  22. j --;

  23. }

  24. if (i < j) {

  25. // i、j未相遇,说明找到了小于pivot的元素。交换。

  26. [self jx_exchangeWithIndexA:i indexB:j didExchange:exchangeCallback];

  27. i ++;

  28. }

  29. /// 略过小于等于pivot的元素

  30. while (i < j && comparator(self[i], pivot) != NSOrderedDescending) {

  31. i ++;

  32. }

  33. if (i < j) {

  34. // i、j未相遇,说明找到了大于pivot的元素。交换。

  35. [self jx_exchangeWithIndexA:i indexB:j didExchange:exchangeCallback];

  36. j --;

  37. }

  38. }

  39. return i;

  40. }

UI实现

现在讲下UI的实现思路。

NSMutableArray+JXSort.h

从前面的排序代码可以看到,我是给NSMutableArray写了个分类,排序逻辑写在分类里面,完全与视图无关。

typedef NSComparisonResult(^JXSortComparator)(id obj1, id obj2);  typedef void(^JXSortExchangeCallback)(id obj1, id obj2);  @interface NSMutableArray (JXSort)  // 选择排序  - (void)jx_selectionSortUsingComparator:(JXSortComparator)comparator didExchange:(JXSortExchangeCallback)exchangeCallback;  // 冒泡排序  - (void)jx_bubbleSortUsingComparator:(JXSortComparator)comparator didExchange:(JXSortExchangeCallback)exchangeCallback;  // 插入排序  - (void)jx_insertionSortUsingComparator:(JXSortComparator)comparator didExchange:(JXSortExchangeCallback)exchangeCallback;  // 快速排序  - (void)jx_quickSortUsingComparator:(JXSortComparator)comparator didExchange:(JXSortExchangeCallback)exchangeCallback;  @end

外部调用者只需要传入两个参数:

  • comparator代码块。这是遵循苹果原有API的风格设计,在需要比较数组内的两个元素时,排序方法将会调用这个代码块,回传需要比较的两个元素给外部调用者,由外部调用者实现比较逻辑,并返回比较结果给排序方法。

  • exchangeCallback代码块。这个参数是实现视图变化的关键。排序方法在每次完成两个元素的交换时,都会调用这个代码块。外部调用者,比如ViewController就可以知道排序元素每一次变换位置的时机,从而同步视图的变化。

- (void)jx_exchangeWithIndexA:(NSInteger)indexA indexB:(NSInteger)indexB didExchange:(JXSortExchangeCallback)exchangeCallback {  id temp = self[indexA];  self[indexA] = self[indexB];  self[indexB] = temp;  if (exchangeCallback) {  exchangeCallback(temp, self[indexA]);  }  }  ViewController.m

视图控制器持有待排序的数组,这个数组是100条细长的矩形,长度随机。

@property (nonatomic, strong) NSMutableArray *barArray;

由于我们加强了NSMutableArray,它现在可以支持多种指定类型的排序了,同时也可以把排序过程反馈给我们,当需要给barArray排序时,只需要这样调用:

- (void)quickSort {  [self.barArray jx_quickSortUsingComparator:^NSComparisonResult(id obj1, id obj2) {  return [self compareWithBarOne:obj1 andBarTwo:obj2];  } didExchange:^(id obj1, id obj2) {  [self exchangePositionWithBarOne:obj1 andBarTwo:obj2];  }];  }

每一次didExchange的回调,ViewController都会对两个视图的位置进行交换。如此形成不断进行排序的视觉效果。

控制排序速度

为了能够让肉眼感知排序的过程,我们需要放慢排序的过程。

这里我的办法是延长两个元素比较操作的耗时,大约延长到0.002秒。结果很明显,当某个算法所需要进行的比较操作越少时,它排序就会越快(根据上面四张图的比较,毫无疑问快排所进行的比较操作是最少啦~)。

那么如何模拟出比较操作的耗时时间呢?

这里我的办法是借助信号量,在两条线程间通讯。

1.让排序在子线程中进行,当需要进行比较操作时,阻塞线程,等待信号的到来。这里的思想是得到一个信号才能进行一次比较。

- (NSComparisonResult)compareWithBarOne:(UIView *)barOne andBarTwo:(UIView *)barTwo {  // 模拟进行比较所需的耗时  dispatch_semaphore_wait(self.sema, DISPATCH_TIME_FOREVER);  CGFloat height1 = CGRectGetHeight(barOne.frame);  CGFloat height2 = CGRectGetHeight(barTwo.frame);  if (height1 == height2) {  return NSOrderedSame;  }  return height1 < height2 ? NSOrderedAscending : NSOrderedDescending;  }

2.主线程启用定时器,每隔0.002秒发出一个信号,唤醒排序线程。

self.sema = dispatch_semaphore_create(0);  NSTimeInterval nowTime = [[NSDate date] timeIntervalSince1970];  // 定时器信号  __weak typeof(self) weakSelf = self;  self.timer = [NSTimer scheduledTimerWithTimeInterval:0.002 repeats:YES block:^(NSTimer * _Nonnull timer) {  // 发出信号量,唤醒排序线程  dispatch_semaphore_signal(weakSelf.sema);  // 更新计时  NSTimeInterval interval = [[NSDate date] timeIntervalSince1970] - nowTime;  weakSelf.timeLabel.text = [NSString stringWithFormat:@"耗时(秒):%2.3f", interval];  }];

上述就是小编为大家分享的各大排序算法的Objective-C实现以及图形化演示比较是怎样的了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注行业资讯频道。

排序 元素 数组 位置 枢轴 游标 两个 有序 指向 过程 不断 范围 算法 信号 就是 线程 代码 思路 方法 视图 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 新华互联网科技 河南真二互联网科技有限公司 金融科技与互联网金融 西安互联网科技公司 沃里互联网科技 滨湖区互联网智能科技产品供应 哈尔滨新华互联网科技学院 互联网科技寓意 注册互联网科技公司经营范围大全 互联网科技扶贫时间 中国科技创新与世界互联网大会 卓翼科技产业互联网合作 科技赋能视角下互联网保险发展 山东有哪些互联网科技公司 美国互联网科技实力 江苏互联网智能科技软件 互联网风格科技公司名称 互联网科技手抄报图片 青岛互联网科技园 株洲百竞科技的互联网营销顾问 互联网与科技金融数据 互联网 科技服务平台 常德市电业局互联网科技部 中化能源互联网科技招聘 翼展科技 首家互联网医院 工业互联网展湾科技股份有限公司 深圳互联网科技的发展 启辰互联网科技 四川互联网展示科技馆设计 互联网科技数字货币
0