标签:ima 日期 完成 点击 一键 关系 两种 ogr pivot
由于面试可能需要手写算法,网上搜罗了一些资料,整理了下算法的OC的实现代码,虽然平时开发中一般用不到,但是多积累一些技术知识,还是对以后发展大有裨益的
github上搜集的几大算法原理和实现代码,只有JavaScript、Python、Go、Java的实现代码
相邻元素进行比较,按照升序或者降序,交换两个相邻元素的位置 是一种“稳定排序算法”
是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
作为最简单的排序算法之一,冒泡排序给我的感觉就像 Abandon 在单词书里出现的感觉一样,每次都在第一页第一位,所以最熟悉。冒泡排序还有一种优化算法,就是立一个 flag,当在一趟序列遍历中元素没有发生交换,则证明该序列已经有序。但这种改进对于提升性能来说并没有什么太大作用。
当输入的数据已经是正序时。
当输入的数据是反序时。
- (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)。
快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。
快速排序的名字起的是简单粗暴,因为一听到这个名字你就知道它存在的意义,就是快,而且效率高!它是处理大数据最快的排序算法之一了。虽然 Worst Case 的时间复杂度达到了 O(n2),但是人家就是优秀,在大多数情况下都比平均时间复杂度为 O(n logn) 的排序算法表现要更好,可是这是为什么呢,我也不知道。好在我的强迫症又犯了,查了 N 多资料终于在《算法艺术与信息学竞赛》上找到了满意的答案: 快速排序的最坏运行情况是 O(n2),比如说顺序数列的快排。但它的平摊期望时间是 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(n2) 的时间复杂度。所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间。
- (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);
}
计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。为了使桶排序更加高效,我们需要做到这两点:
同时,对于桶中元素的排序,选择何种比较排序算法对于性能的影响至关重要。
当输入的数据可以均匀的分配到每一个桶中。
当输入的数据被分配到了同一个桶中。
标签:ima 日期 完成 点击 一键 关系 两种 ogr pivot
原文地址:https://www.cnblogs.com/chengxyyh/p/13157806.html