“簡單不先于復(fù)雜,而是在復(fù)雜之后.” —— Alan Perlis
序言
當(dāng)我們第一次數(shù)組排序的時(shí)候,通常都會(huì)介紹一種排序算法,那就是冒泡排序.冒泡排序的思路是最簡單的,最容易讓人理解的.可能看這篇文章的各位大神都在程序的世界遨游已久,會(huì)對(duì)此嗤之以鼻,說不就是兩層For循環(huán)嘛,不是簡單的很?而我卻要說,NO,NO,NO,今天的文章可能讓你眼前一亮,請(qǐng)耐心看下去.你會(huì)發(fā)現(xiàn)一些你認(rèn)知中所不一樣的東西.
冒泡概念簡介以及最簡單的排序?qū)崿F(xiàn)
冒泡排序是一種交換排序,它的實(shí)現(xiàn)原理是:兩兩比較相鄰的記錄值,如果反序則交換,直到?jīng)]有反序的記錄為準(zhǔn),如上圖,這就是一次完整的冒泡排序.我們看一下代碼是如何實(shí)現(xiàn)的.還是以O(shè)C語言為基礎(chǔ).在ViewController中實(shí)現(xiàn)代碼.
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic,strong)NSMutableArray *dataArray;//數(shù)據(jù)源數(shù)組
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.dataArray = [NSMutableArray arrayWithArray:@[@6,@5,@3,@1,@8,@7,@2,@4]];
for (int i = 0; i< self.dataArray.count; i++) {
for (int j = i +1 ; j <self.dataArray.count; j++) {
if (self.dataArray[i] < self.dataArray[j]) {
NSNumber *number = self.dataArray[i];
self.dataArray[i] = self.dataArray[j];
self.dataArray[j] = number;
}
}
}
NSLog(@"%@",self.dataArray);
}
@end
</br>
上面的冒泡排序算法應(yīng)該是我們平常最常用的冒泡排序了,但是其實(shí)從嚴(yán)格意義上來說,上面的冒泡排序并不能成為嚴(yán)格意義上的冒泡排序,因?yàn)樗粷M足"兩兩比較相鄰記錄"的冒泡排序的思想,相比叫它冒泡排序,不如叫它最簡單的交換排序.它的思路就是讓每一個(gè)元素與它后面的每一個(gè)元素比較,如果大則交換,這樣第一個(gè)位置的元素在第一排序之后一定活成為最大值.如上圖,這就是一次排序的整個(gè)過程.上面的排序方式只能算是最簡單的排序方式了,不過這樣簡單的代碼卻有缺陷的,比如在第一次排序完成的數(shù)組為[5,3,1,8,7,6,2,4]對(duì)其他的元素卻沒有幫助.也就說,這個(gè)方法是非常低效的,那么接下來,我們看一下正宗的冒泡排序有沒有什么改進(jìn)的地方.
冒泡排序算法
我們先看一下正宗的冒泡排序代碼是如何實(shí)現(xiàn)的
self.dataArray = [NSMutableArray arrayWithArray:@[@6,@5,@3,@1,@8,@7,@2,@4]];
for (int i = 0; i< self.dataArray.count; i++) {
//注意j是從后往前循環(huán),從后往前遍歷
for (int j = (int)self.dataArray.count-1 ; j >= i ; j--) {
if (self.dataArray[i]< self.dataArray[j]) {
NSNumber *number = self.dataArray[i];
self.dataArray[i] = self.dataArray[j];
self.dataArray[j] = number;
}
}
}
NSLog(@"%@ ",self.dataArray);
}
那么,我們就看第一次遍歷,先從最后一個(gè)元素4開始,因?yàn)?>2,所以交換位置,然后4和7比較,不交換位置,7和8比較,交換位置,最后,8分別和1,3,5,6比較,然后全都交換位置,然后數(shù)組的第一次遍歷就完成,數(shù)組為[8,6,5,3,1,7,4,2];相比于簡單排序第一完成之后的數(shù)組[5,3,1,8,7,6,2,4],除了元素8往前進(jìn)了一位,元素6到了數(shù)組的后半部分了.這一正宗的算法果然要比前面的要有所進(jìn)步,可能數(shù)組中的元素個(gè)數(shù)少的時(shí)候,可能效率差的不會(huì)是很大,如果在上十萬條數(shù)據(jù)的排序過程中,這種差異就會(huì)體現(xiàn)出來.但是,這樣正宗的冒泡排序有沒有改進(jìn)的方案呢?答案是有的.首先,我們來打印一下冒泡排序和簡單的執(zhí)行次數(shù).
那么,為什么會(huì)出現(xiàn)這種情況,我們改如何改進(jìn)呢?
冒泡排序的優(yōu)化
為什么冒泡排序會(huì)比最賤的排序方式執(zhí)行次數(shù)多呢?比如正宗的冒泡排序第一次執(zhí)行完成之后的數(shù)組為[8,6,5,3,1,7,4,2],我們當(dāng)執(zhí)行第二次的時(shí)候,最后執(zhí)行的的是7和8 的比較,明明元素8 的位置就贏改第一位,毋庸置疑,卻還要比較一次,盡管沒有交換數(shù)據(jù),但是比較還是多余的.往后的遍歷中,這樣的無用的比較缺不改變位置很多,我們改如何改進(jìn)呢?我們的改進(jìn)思想是這樣的,設(shè)置一個(gè)BOOL值,在一次的遍歷
過程中只要有位置的變動(dòng),我們就允許下一次遍歷,否則就不再執(zhí)行下一次遍歷,代碼實(shí)現(xiàn)如下.
self.runNumber = 0;
self.isContinue = YES;
self.dataArray = [NSMutableArray arrayWithArray:@[@6,@5,@3,@1,@8,@7,@2,@4]];
for (int i = 0; i< self.dataArray.count && self.isContinue; i++) {
self.isContinue = NO;
for (int j = (int)self.dataArray.count-1 ; j >= i ; j--) {
if (self.dataArray[i]< self.dataArray[j]) {
self.isContinue = YES;
NSNumber *number = self.dataArray[i];
self.dataArray[i] = self.dataArray[j];
self.dataArray[j] = number;
}
self.runNumber++;
}
}
NSLog(@"冒泡排序優(yōu)化:\n %@ \n 執(zhí)行次數(shù):%ld",self.dataArray,self.runNumber);
代碼和普通的冒泡排序最大的區(qū)別就是多了一個(gè)BOOL值,來判斷一下程序是否有必要執(zhí)行下去,進(jìn)過這樣的改進(jìn),冒泡排序在性能上就有了一些提升,可以避免因有序的情況下無意義的循環(huán)判斷.
那么,程序到底優(yōu)化了多少呢?來,我們打印三次的執(zhí)行次數(shù),如下圖,我們可以從圖中可以看出優(yōu)化的執(zhí)行次數(shù)為30次,而冒泡排序的執(zhí)行次數(shù)是36次,效率提高了16.67%,大大的優(yōu)化程序性能.
由于一直是使用OC做開發(fā),C語言已經(jīng)忘了差不多了,所以我這里附上一份OC的冒泡排序Demo,如果有任何疑問,請(qǐng)?jiān)谙旅嬖u(píng)論區(qū)評(píng)論,我會(huì)及時(shí)的回復(fù)您的,謝謝!