待续算法
(原创,转发须注明原处)编程
算法是个很奇妙的东西,无论在招聘的笔试仍是面试中,也无论你是学的什么语言或者什么方向的编程,出题者或面试官总会问问,尤为是关于排序算法的问题,对滴,在此次秋招中,我就被面试官问了无数次(有点夸张)。下面,我就好好总结总结下,我相信,之后绝对有人用得上的,方便点。。。数组
1、关于排序的概念大数据
对的,就是概念,要学好一个东西,必需要先搞清概念,别一上来就看题敲代码,这是初学者浮躁的表现(我当初就是这样的),总想急于求成,想快点作点东西,当时确实学到了很多零碎的知识,但随着内容的深刻与扩充,发现本身学的东西变得多而杂了,因而,又花更多的时间去理清知识体系。因此,我劝你们应该有个好的起点,从开始就弄清本身到底要学啥,学了些啥,有个清晰的知识体系会有助于我更好的深刻。废话就很少说了。。。
spa
排序法指针 |
最差时间分析 | 平均时间复杂度 | 稳定度 | 空间复杂度 |
冒泡排序 | O(n2) | O(n2) | 稳定 | O(1) |
快速排序 | O(n2) | O(n*log2n) | 不稳定 | O(log2n)~O(n) |
选择排序 | O(n2) | O(n2) | 稳定 | O(1) |
二叉树排序 | O(n2) | O(n*log2n) | 不一顶 | O(n) |
插入排序code |
O(n2) | O(n2) | 稳定 | O(1) |
堆排序 | O(n*log2n) | O(n*log2n) | 不稳定 | O(1) |
希尔排序 | O | O | 不稳定 | O(1) |
2、算法讲解与实现部分htm
一、简单选择排序
大体过程:首先,选出数组中最小的元素,将它与数组中第一个元素进行交换,而后找出次小的元素,并将它与数组中第二个元素进行交换。按照这种方法一直进行下去,知道整个数组排序完为止。图解以下:
代码以下:
void selectsort(int a[],int length) { int i, j; for (i = 0; i < length; i++) { int min = i; for (j = i + 1; j < length; j++) if (a[j] < a[min]) min = j; if (min != i) { int t = a[i]; a[i] = a[min]; a[min] = t; } } }
缺点:比较次数多,不能利用数据的已有序的特色,对已有序的部分依赖弱
优势:属于稳定排序,对于元素较大,但关键字又比较小的数据的排序,选择排序较适合
二、插入排序
大体思路:每次将一个待排序的元素与已排序的元素进行逐一比较,直到找到合适的位置按大小插入。
代码以下:
void insertion(int a[], int length) { int i, j; for (i = 1; i < length; i++) for (j = i; j > 0; j--) if (a[j - 1] > a[j]) { int t = a[j - 1]; a[j - 1] = a[j]; a[j] = t; } }
改进下:
void insertion(int a[], int length) { for (int i = 1; i < length; i++)//从第2个数据开始插入 { int j = i - 1; int t = a[i];//记录要插入的数据 while (j >= 0 && a[j] > t)//从后向前,找到比其小的数的位置 a[j + 1] = a[j--];//向后挪动 if (j != i - 1)//存在比其小的数 a[j + 1] = t; } }
优势:稳定,而且对数据的初始排列顺序很敏感,若是数据量很大时,且关键字已经部分排序,则插入排序速度比较快
缺点:对于顺序结构的数据,比较次数不必定,比较次数越少,插入点后的数据移动越多,特别是当数据总量庞大的时候。
三、冒泡排序
大体思路:遍历待排序的数据,若是邻近的两个元素大小顺序不对,就将两者进行交换操做,重复此操做直到全部数据排序好为止。图解以下:
代码以下:
void bubble(int a[], int length) { for(int i=0;i<length-1;i++)//进行(n-1)次 for(int j=length-1;j>i;j--) if(a[j-1]>a[j]) { int t = a[j]; a[j] = a[j - 1]; a[j - 1] = t; } }
改进:
void bubble(int a[], int length) { //若是a[0..i]已经是有序区间,上次的扫描区间是a[i..length], //记上次扫描时最后一次执行交换的位置为pos, //则pos在i与length之间,则a[i..pos]区间也是有序的, //不然这个区间也会发生交换;因此下次扫描区间就能够由a[i..length] //缩减到[pos..n] int pos = 0, tpos = 0; for (int i = 0; i < length - 1; i++) { pos = tpos; for (int j = length - 1; j > pos; j--) { if (a[j - 1] > a[j]) { int t = a[j]; a[j] = a[j - 1]; a[j - 1] = t; tpos = j; } } //若是通过一次扫描后位置没变则表示已经所有有序 if (pos == tpos) break; } }
附上双向冒泡(抖动冒泡):
void ImpButtle(int a[], int length) { //先让冒泡排序由左向右进行,再来让冒泡排序由右往左进行"抖动"排序 int left = 0, right = length - 1, i, flag=0; while (left < right) { for(i=left;i<right;i++) if (a[i] > a[i + 1]) { int t = a[i]; a[i] = a[i + 1]; a[i + 1] = t; flag = i; } right = flag; for(i=right;i>left;i--) if (a[i - 1] > a[i]) { int t = a[i - 1]; a[i - 1] = a[i]; a[i] = t; flag = i; } left = flag; } }
(注:插入排序不能预知各个数据在数组中的最终位置,选择排序不会改变已排序好的数据的位置。插入排序每步中要平均移动已排序好的数据的通常长度,选择排序和冒泡排序每步都要访问全部还没有排序的数据,其中,冒泡排序将全部顺序不对的相邻的元素进行交换,而选择排序每步中只交换一次。
选择排序:N^2/2次比较操做 N次交换操做;插入排序:平均状况下,大约N^2/4次比较操做 N^2/4次交换操做,在最坏状况下都须要2倍的数量;冒泡排序:在平均和最坏状况下,执行大约N^2/2次比较操做和N^2/2次交换操做
对于数据项较大,关键字较小的数据,选择排序的运行时间是线性的)
四、希尔排序(缩小增量排序)
大体思路:是直接插入排序算法的一种更高效的改进版本。它容许非相邻的元素进行交换来提升执行效率。先取一个小于n的整数d1做为第一个增量,把文件的所有记录分组。全部距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;而后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量 =1(
<
…<d2<d1),即全部记录放在同一组中进行直接插入排序为止。图解以下:
代码以下:
void Shellsort(int a[], int length) { for(int gap=length/2;gap>0;gap/=2)//此处步长每次减半
for(int i=gap;i<length;i++)//各组中分别进行插入排序 for (int j = i - gap; j >= 0 && a[j] > a[j + gap]; j -= gap) { int t = a[j]; a[j] = a[j + gap]; a[j + gap] =t; } }
优势:快,数据移动少; 缺点:不稳定,d的取值是多少,应取多少个不一样的值,都没法确切知道,只能凭经验来取(即不一样的步长致使算法效率不一样)。