排序可以看成是对线性表的一种操作。
当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序序。
快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;
排序的稳定性
假设ki=kj(1≤i≤n,1≤j≤n,i≠j),且排序前的序列中ri领先于rj(即i<j)。如果排序后ri扔领先于rj,则称所用的排序方法是稳定的;反之,若可能使得排序后的序列rj领先ri,则称所用的排序方法是不稳定的。
根据在排序过程中待排序的记录是否全部被放置在内存中,排序分为:内排序和外排序。
内排序:是排序整个过程中,待排序的所有记录全部被放置在内存中。
外排序:是由于排序的记录个数太多,不能同时放置在内存,整个排序过程需要在内外存之间多次交换数据才能进行。
1、内排序(插入排序、交换排序、选择排序和归并排序)
对于内排序来说,影响排序算法性能有三方面(时间性能、辅助空间、算法的复杂性)。
1、时间性能:排序算法的时间开销是衡量其好坏的重要标志。在内排序中,主要进行两种操作:比较和移动。高效率的内排序算法应该是具有尽可能少的关键字比较次数和尽可能少的记录移动次数。
2、辅助空间:执行算法所需要的负重存储空间。辅助存储空间是除了存放待排序所占用的存储空间之外,执行算法所需要的其他存储空间。
3、算法的复杂性:指算法本身的复杂度,而不是算法的时间复杂度。
一)、插入排序
1、直接插入排序:每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子序列中的适当位置,直到全部记录插入完成为止。时间复杂度O(n²)。性能要好于冒泡排序和简单选择排序。
2、希尔排序
二)、交换排序
1、冒泡排序:两两比较相邻记录的关键字,如果反序则交换,知道没有反序的记录为止。时间复杂度O(n²)
2、快速排序:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另外一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。
快速排序主要思路是递归。特别注意
:快速排序中用到的Partition函数,它的作用是进行一趟快速排序,返回基准元素记录所在的位置privotKey。“基准元素”记录就是一个参考记录,经过Partition处理之后,p左边的记录关键字均不大于基准记录的关键字,p右边的记录关键字均不小于基准记录的关键字。 Partition函数在找出数组中最大或最小的k个记录也很有用。
非递归快速排序的办法是使用栈,记录的是需要排序数组每一分割端的low和high下标,再遍历、使用Partition排序。
//OC实现快速排序。
//快速排序-->递归方法
+ (NSArray *)sortWithQuick:(NSMutableArray *)array low:(NSInteger)low high:(NSInteger)high{
if (low < high) {
NSInteger privotloc = [self partition:array low:low high:high];
NSLog(@"%ld",privotloc);
[self sortWithQuick:array low:low high:privotloc-1];
[self sortWithQuick:array low:privotloc+1 high:high];
}
NSLog(@"%@",array);
return array;
}
///快速排序-->非递归方法
+ (NSArray *)sortWithQuickNoRecursion:(NSMutableArray *)array low:(NSInteger)low high:(NSInteger)high{
if (low < high) {
NSMutableArray *stacks = [[NSMutableArray alloc] init];//初始化栈
if (low < high) {
[stacks addObject:@(low)];
[stacks addObject:@(high)];
}
while (stacks.count > 0) {
NSInteger end = [stacks.lastObject integerValue];
[stacks removeLastObject];
NSInteger start = [stacks.lastObject integerValue];
[stacks removeLastObject];
NSInteger mid = [self partition:array low:start high:end];
if (start < mid-1) {
[stacks addObject:@(low)];
[stacks addObject:@(mid-1)];
}
if (mid+1<end) {
[stacks addObject:@(mid+1)];
[stacks addObject:@(end)];
}
}
}
return array;
}
//快速排序,主要实现方法
+ (NSInteger)partition:(NSMutableArray *)array low :(NSInteger)low high:(NSInteger) high{
NSNumber *privotKey = array[low];//基准元素
NSInteger keyNum = low;//保存开始的low
//从表的两端交替地向中间扫描
while (low < high) {
//从high 所指位置向前搜索,至多到low+1 位置。找到第一个比基准元素小的
while (low < high && [array[high] integerValue] >= [privotKey integerValue]) {
--high;
}
//从low 所指位置向前搜索,至多到high-1 位置。找到第一个比基准元素大的
while (low < high && [array[low] integerValue] <= [privotKey integerValue]) {
++low;
}
[self swapWithArray:array low:low high:high];//交换两个值
}
if ([array[low] integerValue] > [privotKey integerValue]) {
[self swapWithArray:array low:keyNum high:low-1];
}else{
[self swapWithArray:array low:keyNum high:low];
}
return low;
}
//交换方法
+ (void)swapWithArray:(NSMutableArray *)array low:(NSInteger )low high:(NSInteger )high {
NSNumber *temp = array[low];
array[low] = array[high];
array[high] = temp;
}
三)、选择排序
2、快速排序:时间复杂度O(n²)。
1、简单选择排序:通过n-i次关键字间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i(1≤i≤n)个记录交换之。时间复杂度O(n²)。尽管复杂度一样,但是简单选择排序的性能还是要略优于冒泡排序。
2、堆排序:是具有下列性质的完全二叉树,每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆。或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。时间复杂度为O(nlogn)。不稳定
四、归并排序
时间复杂度O(nlogn)。空间复杂度O(n+nlogn)。稳定。