因为面试可能须要手写算法,网上搜罗了一些资料,整理了下算法的OC的实现代码,虽然平时开发中通常用不到,可是多积累一些技术知识,仍是对之后发展大有裨益的git
github上搜集的几大算法原理和实现代码,只有JavaScript、Python、Go、Java的实现代码github
相邻元素进行比较,按照升序或者降序,交换两个相邻元素的位置 是一种“稳定排序算法”web
是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,若是他们的顺序错误就把他们交换过来。走访数列的工做是重复地进行直到没有再须要交换,也就是说该数列已经排序完成。这个算法的名字由来是由于越小的元素会经由交换慢慢“浮”到数列的顶端。
做为最简单的排序算法之一,冒泡排序给个人感受就像 Abandon 在单词书里出现的感受同样,每次都在第一页第一位,因此最熟悉。冒泡排序还有一种优化算法,就是立一个 flag,当在一趟序列遍历中元素没有发生交换,则证实该序列已经有序。但这种改进对于提高性能来讲并无什么太大做用。面试
当输入的数据已是正序时。算法
当输入的数据是反序时。shell
- (void)bubbleSortWithArray:(NSMutableArray *)array { for (int i = 0; i < array.count - 1; i++) { //外层for循环控制循环次数 for (int j = 0; j < array.count - 1 - i; j++) { //内层for循环控制交换次数 if ([array[j] integerValue] > [array[j + 1] integerValue]) { [array exchangeObjectAtIndex:j withObjectAtIndex:j + 1]; } } } }
快速排序是由东尼·霍尔所发展的一种排序算法。在平均情况下,排序 n 个项目要 Ο(nlogn) 次比较。在最坏情况下则须要 Ο(n2) 次比较,但这种情况并不常见。事实上,快速排序一般明显比其余 Ο(nlogn) 算法更快,由于它的内部循环(inner loop)能够在大部分的架构上颇有效率地被实现出来。数据结构
快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。架构
快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。ide
快速排序的名字起的是简单粗暴,由于一听到这个名字你就知道它存在的意义,就是快,并且效率高!它是处理大数据最快的排序算法之一了。虽然 Worst Case 的时间复杂度达到了 O(n²),可是人家就是优秀,在大多数状况下都比平均时间复杂度为 O(n logn) 的排序算法表现要更好,但是这是为何呢,我也不知道。好在个人强迫症又犯了,查了 N 多资料终于在《算法艺术与信息学竞赛》上找到了满意的答案: 快速排序的最坏运行状况是 O(n²),好比说顺序数列的快排。但它的平摊指望时间是 O(nlogn),且 O(nlogn) 记号中隐含的常数因子很小,比复杂度稳定等于 O(nlogn) 的归并排序要小不少。因此,对绝大多数顺序性较弱的随机数列而言,快速排序老是优于归并排序。
递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,可是这个算法总会退出,由于在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
- (void)quickSortArray:(NSMutableArray *)array leftIndex:(NSInteger)left rightIndex:(NSInteger)right { if (left > right) { return; } NSInteger i = left; NSInteger j = right; //记录基准数 pivoty NSInteger key = [array[i] integerValue]; while (i < j) { //首先从右边j开始查找(从最右边往左找)比基准数(key)小的值<--- while (i < j && key <= [array[j] integerValue]) { j--; } //若是从右边j开始查找的值[array[j] integerValue]比基准数小,则将查找的小值调换到i的位置 if (i < j) { array[i] = array[j]; } //从i的右边往右查找到一个比基准数小的值时,就从i开始日后找比基准数大的值 ---> while (i < j && [array[i] integerValue] <= key) { i++; } //若是从i的右边往右查找的值[array[i] integerValue]比基准数大,则将查找的大值调换到j的位置 if (i < j) { array[j] = array[i]; } } //将基准数放到正确的位置,----改变的是基准值的位置(数组下标)--- array[i] = @(key); //递归排序 //将i左边的数从新排序 [self quickSortArray:array leftIndex:left rightIndex:i - 1]; //将i右边的数从新排序 [self quickSortArray:array leftIndex:i + 1 rightIndex:right]; }
它的改进(相比较冒泡算法)在于:先并不急于调换位置,先从A[0]开始逐个检查,看哪一个数最小就记下该数所在的位置P,等一躺扫描完毕,再把A[P]和A[0]对调,这时A[0]到A[n]中最小的数据就换到了最前面的位置。是一个“不稳定排序算法”
它是一种简单直观的排序算法,不管什么数据进去都是 O(n²) 的时间复杂度。因此用到它的时候,数据规模越小越好。惟一的好处可能就是不占用额外的内存空间。
- (void)selectSortWithArray:(NSMutableArray *)array { for (int i = 0; i < array.count; i++) { for (int j = i + 1; j < array.count; j++) { if (array[i] > array[j]) { [array exchangeObjectAtIndex:i withObjectAtIndex:j]; } } } }
堆排序是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似彻底二叉树的结构,并同时知足堆积的性质:即子结点的键值或索引老是小于(或者大于)它的父节点。堆排序能够说是一种利用堆的概念来排序的选择排序。分为两种方法:
- (void)heapSortWithArray:(NSMutableArray *)array { //循环创建初始堆 for (NSInteger i = array.count * 0.5; i >= 0; i--) { [self heapAdjustWithArray:array parentIndex:i length:array.count]; } //进行n-1次循环,完成排序 for (NSInteger j = array.count - 1; j > 0; j--) { //最后一个元素和第一个元素进行交换 [array exchangeObjectAtIndex:j withObjectAtIndex:0]; //筛选R[0]结点,获得i-1个结点的堆 [self heapAdjustWithArray:array parentIndex:0 length:j]; NSLog(@"第%ld趟:", array.count - j); [self printHeapSortResult:array begin:0 end:array.count - 1]; } } - (void)heapAdjustWithArray:(NSMutableArray *)array parentIndex:(NSInteger)parentIndex length:(NSInteger)length { NSInteger temp = [array[parentIndex] integerValue]; //temp保存当前父结点 NSInteger child = 2 * parentIndex + 1; //先得到左孩子 while (child < length) { //若是有右孩子结点,而且右孩子结点的值大于左孩子结点,则选取右孩子结点 if (child + 1 < length && [array[child] integerValue] < [array[child + 1] integerValue]) { child++; } //若是父结点的值已经大于孩子结点的值,则直接结束 if (temp >= [array[child] integerValue]) { break; } //把孩子结点的值赋值给父结点 array[parentIndex] = array[child]; //选取孩子结点的左孩子结点,继续向下筛选 parentIndex = child; child = 2 * child + 1; } array[parentIndex] = @(temp); } - (void)printHeapSortResult:(NSMutableArray *)array begin:(NSInteger)begin end:(NSInteger)end { for (NSInteger i = 0; i < begin; i++) { } for (NSInteger i = begin; i <= end; i++) { } //打印堆排序 NSLog(@"堆排序升序结果是--->%@",array); }
插入排序的代码实现虽然没有冒泡排序和选择排序那么简单粗暴,但它的原理应该是最容易理解的了,由于只要打过扑克牌的人都应该可以秒懂。插入排序是一种最简单直观的排序算法,它的工做原理是经过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
插入排序和冒泡排序同样,也有一种优化算法,叫作拆半插入。
- (void)insertSortWithArray:(NSMutableArray *)array { NSInteger j; for (NSInteger i = 1; i < array.count; i++) { //取出每个待插入的数据,从array[1]开始查找 NSInteger temp = [array[i] integerValue]; for (j = i - 1; j >= 0 && temp < [array[j] integerValue]; j--) { //若是以前的数比temp大,就将这个数日后移动一个位置,留出空来让temp插入,和整理扑克牌相似 [array[j + 1] integerValue] = [array[j] integerValue]]; array[j] = [NSNumber numberWithInteger:temp]; } } }
做为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个个人iOS交流群:519832104 无论你是小白仍是大牛欢迎入驻,分享经验,讨论技术,你们一块儿交流学习成长!
另附上一份各好友收集的大厂面试题,须要iOS开发学习资料、面试真题,能够添加iOS开发进阶交流群,进群可自行下载!
归并排序(Merge sort)是创建在归并操做上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个很是典型的应用。
做为一种典型的分而治之思想的算法应用,归并排序的实现由两种方法:
在《数据结构与算法 JavaScript 描述》中,做者给出了自下而上的迭代方法。
和选择排序同样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,由于始终都是 O(nlogn) 的时间复杂度。代价是须要额外的内存空间。
//自顶向下的归并排序 /** 递归使用归并排序,对array[left...right]的范围进行排序 @param array 数组 @param left 左边界 @param right 右边界 */ - (void)mergeSortWithArray:(NSMutableArray *)array left:(NSInteger)left right:(NSInteger)right { //判断递归到底的状况 if (left >= right) { //这时候只有一个元素或者是不存在的状况 return; } //中间索引的位置 NSInteger middle = (right + left) / 2; //对 left --- middle 区间的元素进行排序操做 [self mergeSortWithArray:array left:left right:middle]; //对 middle + 1 ---- right 区间的元素进行排序操做 [self mergeSortWithArray:array left:middle + 1 right:right]; //两边排序完成后进行归并操做 [self mergeSortWithArray:array left:left middle:middle right:right]; } /** 对 [left middle] 和 [middle + 1 right]这两个区间归并操做 @param array 传入的数组 @param left 左边界 @param middle 中间位置 @param right 右边界 */ - (void)mergeSortWithArray:(NSMutableArray *)array left:(NSInteger)left middle:(NSInteger)middle right:(NSInteger)right { //拷贝一个数组出来 NSMutableArray *copyArray = [NSMutableArray arrayWithCapacity:right - left + 1]; for (NSInteger i = left; i <= right; i++) { //这里要注意有left的偏移量,因此copyArray赋值的时候要减去left copyArray[i - left] = array[i]; } NSInteger i = left, j = middle + 1; //循环从left开始到right区间内给数组从新赋值,注意赋值的时候也是从left开始的,不要习惯写成了从0开始,还有都是闭区间 for (NSInteger k = left; k <= right; k++) { //当左边界超过中间点时 说明左半部分数组越界了 直接取右边部分的数组的第一个元素便可 if (i > middle) { //给数组赋值 注意偏移量left 由于这里是从left开始的 array[k] = copyArray[j - left]; //索引++ j++; } else if (j > right) {//当j大于右边的边界时证实有半部分数组越界了,直接取左半部分的第一个元素便可 array[k] = copyArray[i - left]; //索引++ i++; } else if (copyArray[i - left] > copyArray[j - left]) {//左右两半部分数组比较 //当右半部分数组的第一个元素要小时 给数组赋值为右半部分的第一个元素 array[k] = copyArray[j - left]; //右半部分索引加1 j++; } else {//右半部分数组首元素大于左半部分数组首元素 array[k] = copyArray[i - left]; i++; } } }
希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法。
希尔排序是基于插入排序的如下两点性质而提出改进方法的:
希尔排序的基本思想是:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
- (void)shellAscendingOrderSort:(NSMutableArray *)ascendingArr { NSMutableArray *buckt = [self createBucket]; NSNumber *maxnumber = [self listMaxItem:ascendingArr]; NSInteger maxLength = numberLength(maxnumber); for (int digit = 1; digit <= maxLength; digit++) { // 入桶 for (NSNumber *item in ascendingArr) { NSInteger baseNumber = [self fetchBaseNumber:item digit:digit]; NSMutableArray *mutArray = buckt[baseNumber]; [mutArray addObject:item]; } NSInteger index = 0; for (int i = 0; i < buckt.count; i++) { NSMutableArray *array = buckt[i]; while (array.count != 0) { NSNumber *number = [array objectAtIndex:0]; ascendingArr[index] = number; [array removeObjectAtIndex:0]; index++; } } } NSLog(@"希尔升序排序结果:%@", ascendingArr); } - (NSMutableArray *)createBucket { NSMutableArray *bucket = [NSMutableArray array]; for (int index = 0; index < 10; index++) { NSMutableArray *array = [NSMutableArray array]; [bucket addObject:array]; } return bucket; } - (NSNumber *)listMaxItem:(NSArray *)list { NSNumber *maxNumber = list[0]; for (NSNumber *number in list) { if ([maxNumber integerValue] < [number integerValue]) { maxNumber = number; } } return maxNumber; } NSInteger numberLength(NSNumber *number) { NSString *string = [NSString stringWithFormat:@"%ld", (long)[number integerValue]]; return string.length; } - (NSInteger)fetchBaseNumber:(NSNumber *)number digit:(NSInteger)digit { if (digit > 0 && digit <= numberLength(number)) { NSMutableArray *numbersArray = [NSMutableArray array]; NSString *string = [NSString stringWithFormat:@"%ld", [number integerValue]]; for (int index = 0; index < numberLength(number); index++) { [numbersArray addObject:[string substringWithRange:NSMakeRange(index, 1)]]; } NSString *str = numbersArray[numbersArray.count - digit]; return [str integerValue]; } return 0; }
基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不一样的数字,而后按每一个位数分别比较。因为整数也能够表达字符串(好比名字或日期)和特定格式的浮点数,因此基数排序也不是只能使用于整数。
基数排序有两种方法:
这三种排序算法都利用了桶的概念,但对桶的使用方法上有明显差别:
- (void)radixAscendingOrderSort:(NSMutableArray *)ascendingArr { NSMutableArray *buckt = [self createBucket]; NSNumber *maxnumber = [self listMaxItem:ascendingArr]; NSInteger maxLength = numberLength(maxnumber); for (int digit = 1; digit <= maxLength; digit++) { // 入桶 for (NSNumber *item in ascendingArr) { NSInteger baseNumber = [self fetchBaseNumber:item digit:digit]; NSMutableArray *mutArray = buckt[baseNumber]; [mutArray addObject:item]; } NSInteger index = 0; for (int i = 0; i < buckt.count; i++) { NSMutableArray *array = buckt[i]; while (array.count != 0) { NSNumber *number = [array objectAtIndex:0]; ascendingArr[index] = number; [array removeObjectAtIndex:0]; index++; } } } NSLog(@"基数升序排序结果:%@", ascendingArr); }
计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。做为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有肯定范围的整数。
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的肯定。为了使桶排序更加高效,咱们须要作到这两点:
同时,对于桶中元素的排序,选择何种比较排序算法对于性能的影响相当重要。
当输入的数据能够均匀的分配到每个桶中。
当输入的数据被分配到了同一个桶中。