这里我们开始复习排序的一些面试题。

  首先我们来看一下各个排序方法的时间复杂度和稳定性的比较,见下面表格:

      

排序法 平均时间 最差情形 稳定度 额外空间 备注
冒泡 O(n2)     O(n2) 稳定 O(1) n小时较好
交换     O(n2)     O(n2) 不稳定 O(1) n小时较好
选择 O(n2) O(n2) 不稳定 O(1) n小时较好
插入 O(n2) O(n2) 稳定 O(1) 大部分已排序时较好
基数 O(logRB) O(logRB) 稳定 O(n)

B是真数(0-9),

R是基数(个十百)

Shell O(nlogn) O(ns) 1<s<2 不稳定 O(1) s是所选分组
快速 O(nlogn) O(n2) 不稳定 O(nlogn) n大时较好
归并 O(nlogn) O(nlogn) 稳定 O(1) n大时较好
O(nlogn) O(nlogn) 不稳定 O(1) n大时较好

  选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,                               冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法。

    1.冒泡排序:

  算法原理:比较相邻的两个元素,如果第一个比第二个大,就交换他们两个,持续一直比下去,直到比完一轮,最后一个元素应该是最大的元素;之后再重新从首部开始比较,重复上述步骤,数字越来越少,直到没有足够一对的数字可以进行比较。

  代码实现:

  

 #include<iostream>
using namespace std;
void bubble_sort(int a[],int n){
for(int i=;i<n;i++)
for(int j=;j=n--i;j++){
if(a[j]>a[j+]){
int temp=a[j];
a[j]=a[j+];
a[j+]=temp;
}
}
}

  算法分析:

      •    

        倒序(最糟情况)
        第一轮:10,9,8,7->10,9,7,8->10,7,9,8->7,10,9,8(交换3次)
        第二轮:7,10,9,8->7,10,8,9->7,8,10,9(交换2次)
        第一轮:7,8,10,9->7,8,9,10(交换1次)
        循环次数:6次
        交换次数:6次

        其他:
        第一轮:8,10,7,9->8,10,7,9->8,7,10,9->7,8,10,9(交换2次)
        第二轮:7,8,10,9->7,8,10,9->7,8,10,9(交换0次)
        第一轮:7,8,10,9->7,8,9,10(交换1次)
        循环次数:6次
        交换次数:3次

        上面我们给出了程序段,现在我们分析它:这里,影响我们算法性能的主要部分是循环和交换,
        显然,次数越多,性能就越差。从上面的程序我们可以看出循环的次数是固定的,为1+2+...+n-1。
        写成公式就是1/2*(n-1)*n。
        现在注意,我们给出O方法的定义:

        若存在一常量K和起点n0,使当n>=n0时,有f(n)<=K*g(n),则f(n) = O(g(n))。(呵呵,不要说没
        学好数学呀,对于编程数学是非常重要的!!!)

        现在我们来看1/2*(n-1)*n,当K=1/2,n0=1,g(n)=n*n时,1/2*(n-1)*n<=1/2*n*n=K*g(n)。所以f(n)
        =O(g(n))=O(n*n)。所以我们程序循环的复杂度为O(n*n)。
        再看交换。从程序后面所跟的表可以看到,两种情况的循环相同,交换不同。其实交换本身同数据源的
        有序程度有极大的关系,当数据处于倒序的情况时,交换次数同循环一样(每次循环判断都会交换),
        复杂度为O(n*n)。当数据为正序,将不会有交换。复杂度为O(0)。乱序时处于中间状态。正是由于这样的
        原因,我们通常都是通过循环次数来对比算法。

    2.交换排序:

  算法原理:每次用当前的元素同之后的元素一一的做比较。

  代码实现:

 #include<iostream>
using namespace std;
void exchange_sort(int a[],int n){
//这个算法和冒泡排序差不多,但是它是用当前的元素同之后的所有元素作比较
//而冒泡是一对一对之间做比较,还是有差别的
for(int i=;i<n;i++)
for(int j=i+;j<n;j++){
if(a[j]<a[i]){
int temp=a[j];
a[j]=a[i];
a[i]=temp;
}
}
}

  算法分析:

      •     倒序(最糟情况)
        第一轮:10,9,8,7->9,10,8,7->8,10,9,7->7,10,9,8(交换3次)
        第二轮:7,10,9,8->7,9,10,8->7,8,10,9(交换2次)
        第一轮:7,8,10,9->7,8,9,10(交换1次)
        循环次数:6次
        交换次数:6次其他:
        第一轮:8,10,7,9->8,10,7,9->7,10,8,9->7,10,8,9(交换1次)
        第二轮:7,10,8,9->7,8,10,9->7,8,10,9(交换1次)
        第一轮:7,8,10,9->7,8,9,10(交换1次)
        循环次数:6次
        交换次数:3次从运行的表格来看,交换几乎和冒泡一样糟。事实确实如此。循环次数和冒泡一样
        也是1/2*(n-1)*n,所以算法的复杂度仍然是O(n*n)。由于我们无法给出所有的情况,所以
        只能直接告诉大家他们在交换上面也是一样的糟糕(在某些情况下稍好,在某些情况下稍差)。所以它是不稳定的。

    

    3.选择排序:

  选择排序算法就是每一趟从待排序的记录中选出关键字最小(最大)的记录,顺序放在已排好序的子文件的最后(最前),直到全部记录排序完毕。常见的选择排序有直接选择排序(Selection Sort),堆排序(Heap Sort),平滑排序(Smooth Sort),笛卡尔树排序(Cartesian Sort),锦标赛排序(Tournament Sort),循环排序(Cycle)。下面介绍前两种:堆排序和选择排序。

  直接选择排序:

  算法原理:这是一种简单直观的排序算法。它首先在未排序序列中找到最小(大)元素,存放到排序序列的其起始位置,然后再从剩余未排序的序列元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素排序完毕。

  算法的实现举例:

  

  算法代码:

 #include<iostream>
using namespace std;
void select_sort(int a[],int n){
int min;//记录最小值元素的下标
for(int i=;i<n-;i++){
min=i;//赋予初值
//寻找最小值,循环一遍
for(int j=i+;j<n;j++){
if(a[j]<a[min])
min=j;
}
//如果循环一遍之后的最小值不为a[i],则交换元素
if(min!=i){
int temp=a[i];
a[i]=a[min];
a[min]=temp;
}
}
}

  /此处转载博客:http://blog.csdn.net/left_la/article/details/8648582

  堆排序:是利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子节点的键值或索引总是小于(或者大于)它的父节点。堆排序的时间复杂度是O(nlgN),与快速排序达到相同的时间复杂度。但是在实际应用中,我们往往采用快速排序而不是堆排序。这是因为快速排序的一个好的实现,往往比堆排序具有更好的表现。堆排序的主要用途,是在形成和处理优先级队列方面。另外,如果计算要求是类优先级队列(比如,只要返回最大或者最小元素,只有有限的插入要求等),堆同样是很适合的数据结构。

  算法原理:

    • 通常堆是通过一维数组来实现的,在起始数组为0的情形中,对于节点i:
      其左子节点的下标为 (2*i+1);
      其右子节点的下标为 (2*i+2);
      其父节点的下标为 floor((i-1)/2)。
      在堆的数据结构中,堆中的最大值总是位于根节点。堆中定义一下三个操作:
      1.最大堆调整(Max Heapify):在假定节点i的左右子节点为根的两颗二叉树都是最大堆的前提下,确保父节点大于子节点,否则下降原父节点,最终使以i为根的子树成为最大堆。
      2.创建最大堆(Build Max Heap):将堆所有数据重新排序,对所有非叶子节点调用一次Max Heapify。

      3.堆排序(Heap Sort):首先创建最大堆,然后依次将堆的根节点与末节点交换、剔除末节点、对根节点进行最大堆调整,直到堆中的节点数为1,排序结束。

      算法示意图:

  算法代码:

  

 // 最大堆调整
void MaxHeapify(int *a, int i, int heapSize)
{
int l = (i+)*-;
int r = (i+)*;
int largest; if (l<=heapSize && a[l]>a[i])
largest = l;
else
largest = i; if (r<=heapSize && a[r]>a[largest])
largest = r; if (largest!=i)
{
swap(a[i], a[largest]);
MaxHeapify(a, largest, heapSize);
}
} // 创建最大堆
void BuildMaxHeap(int *a, int len)
{
for (int i=len/-; i>=; i--)
{
MaxHeapify(a, i, len-);
}
} // 堆排序
void HeapSort(int *a, int len)
{
BuildMaxHeap(a, len);
for (int i=len-; i>; i--)
{
swap(a[], a[i]);
MaxHeapify(a, , i-);
}
}

    4.插入排序:每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子文件中的适当位置,直到全部记录插入完成为止。插入排序有很多,这里我么讲两种:直接插入排序和希尔排序。

  直接插入排序:

  算法思想:是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对未排序的数据,在已排序序列中从后向前扫描,找到相应位置并插入。

  //此处转载:  http://blog.csdn.net/left_la/article/details/8656425

  插入排序算法的一般步骤:
    1.从第一个元素开始,该元素可以认为已被排序;
    2.取出下一个元素,在已经排序的元素序列中从后向前扫描;
    3.如果该元素(已排序)大于新元素,将该元素移到下一个位置;
    4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
    5.将新元素插入到该位置后,重复2~5

  算法示意图:

  

  算法代码:

 #include<iostream>
using namespace std;
void insert_sort(int a[],int n){
for(int j=;j<len;j++){
int key=a[j];//该元素为未排序的部分的第一个元素
int i=j-;//已经排序的元素下标,从后向前扫描
while(i>= && a[i]>key){//如果该元素大于新元素key
a[i+]=a[i];//将该元素移到下一个位置
i--;
}
a[i+]=key;//找到插入位置,插入新元素到排序数组中
}
}

  希尔排序:也称为递减增量排序算法,是插入排序的一种高速而稳定的改进版本。希尔排序是基于插入排序的以下两点性质而提出改进方法的:1.插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率;2.但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位。

  算法步骤:

    1.先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为dl的倍数的记录放在同一个组中,在各组内进行直接插人排序。
    2.取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。

  步长的选择是希尔排序的重要部分。只要最终步长为1任何步长串行都可以工作。算法最开始以一定的步长进行排序。然后会继续以一定步长进行排序,最终算法以步长为1进行排序。当步长为1时,算法变为插入排序,这就保证了数据一定会被排序。

  算法示意图:

  

  算法代码:

 #include<iostream>
using namespace std;
void shellsort(int a[],int n){
int h=;
while(h<n)
h=*h+;//这个是希尔的增量
while(h>){
//对每一个增强里面的元素都用插入排序进行排序
for(int j=h;j<n;j++){
int key=a[j];
int i=j-h;
while(i>= && a[i]>key){
a[i+]=a[i];
i=i-h;
}
a[i+h]=key;
}
h=h/;//下一轮的希尔增量
}
}

    5.快速排序:详情查看:http://blog.csdn.net/liuchen1206/article/details/6954074

    6.归并排序:详情查看:http://blog.csdn.net/left_la/article/details/8656953

C++面试笔记--排序的更多相关文章

  1. Java高级开发工程师面试笔记

    最近在复习面试相关的知识点,然后做笔记,后期(大概在2018.02.01)会分享给大家,尽自己最大的努力做到最好,还希望到时候大家能给予建议和补充 ----------------2018.03.05 ...

  2. php面试笔记(5)-php基础知识-自定义函数及内部函数考点

    本文是根据慕课网Jason老师的课程进行的PHP面试知识点总结和升华,如有侵权请联系我进行删除,email:guoyugygy@163.com 在面试中,考官往往喜欢基础扎实的面试者,而函数相关的考点 ...

  3. php面试笔记(3)-php基础知识-运算符

    本文是根据慕课网Jason老师的课程进行的PHP面试知识点总结和升华,如有侵权请联系我进行删除,email:guoyugygy@163.com 在面试中,考官往往喜欢基础扎实的面试者,而运算符相关的考 ...

  4. php面试笔记(2)-php基础知识-常量和数据类型

    本文是根据慕课网Jason老师的课程进行的PHP面试知识点总结和升华,如有侵权请联系我进行删除,email:guoyugygy@163.com 面试是每一个PHP初学者到PHP程序员必不可少的一步,冷 ...

  5. GitHub标星125k!阿里技术官用3个月总结出的24万字Java面试笔记

    最近收到一位粉丝的回馈! 这位粉丝已经成功入职阿里了小编很是羡慕啊! 今天就把这份30w字Java面试笔记给大家分享出来,说来也巧这份资料也是由一位阿里技术官整理出来的这算不算是"搬起石头砸 ...

  6. 手游2dx面试笔记一

    第一轮IQ测试:都来面试程序了,相信IQ再怎么也坑不到哪里去吧.要问什么样的题,几页纸呐, 如:1.找出不同类:羚羊.斑马.鲨鱼 2.在()里添一字使2边都能组词:木()料 3.中间值?:1,2,4, ...

  7. <转载> C++笔试、面试笔记

    这些东西有点烦,有点无聊.如果要去C++面试就看看吧.几年前网上搜索的.刚才看到,就整理一下,里面有些被我改了,感觉之前说的不对或不完善. 1.求下面函数的返回值( 微软) int func(x)  ...

  8. C++面试笔记--树

    树 树的题目基本都是二叉树,但是面试官还没有说是不是二叉树的时候千万不要先把答案说出来,要是面试官说是多叉树,而你做的是二叉树就直接挂了! 一. 树的三种遍历.前序.中序.后序,如果直接考遍历,就肯定 ...

  9. 面试必备:排序算法汇总(c++实现)

    排序算法主要考点: 7种排序 冒泡排序.选择排序.插入排序.shell排序.堆排序.快速排序.归并排序 以上排序算法是面试官经常会问到的算法,至于其他排序比如基数排序等等,这里不列举. 以下算法通过c ...

随机推荐

  1. bzoj 5403 Marshland

    $n \leq 50$ sol: 放一个在 $x$ 处拐弯的 $L$ 形石头相当于在水平和垂直方向上各选一个与 $x$ 相邻的点,全局不能重复选 最小化危险度,相当于满足这些限制的情况下石头盖住的点危 ...

  2. scrapy入门实践1

    Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架. 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中. 这就是整个Scrapy的架构图了: 各部件职能: Scrapy ...

  3. 【转】Cron表达式简介

    Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式: Seconds Minutes Hours DayofMonth Month ...

  4. 谈谈Linux内核驱动的coding style

    最近在向Linux内核提交一些驱动程序,在提交的过程中,发现自己的代码离Linux内核的coding style要求还是差很多.当初自己对内核文档里的CodingStyle一文只是粗略的浏览,真正写代 ...

  5. java中io的详解

    注:本文全篇转载于:http://blog.csdn.net/taxueyingmei/article/details/7697042,觉得讲的挺详细,就借过来看看,挺不错的文章. 先贴一张图 Jav ...

  6. MySQL 常用启动,关闭,登录脚本

    MySQL 常用启动,关闭,登录脚本 规范化mysql的启动,关闭,登录 1 cat mysql_env.ini #set env MYSQL_USER=system #注意用户权限 MYSQL_PA ...

  7. 第二章 深入分析Java I/O的工作机制(待续)

    Java的I/O类库的基本架构 磁盘I/O工作机制 网络I/O工作机制 NIO的工作方式 I/O调优 设计模式解析之适配器模式 设计模式解析之装饰器模式 适配器模式与装饰器模式的区别

  8. NodeJs之项目构建(对文件及文件夹的操作)

    前提:需要使用:require('fs')引入外部模块 简单的模仿创建一个文件下有多个文件. 首先,准备一个主文件夹 然后,准备放在这个主文件夹下的文件加 在代码中通过对象,数字,json对象来装 代 ...

  9. 01-19asp.net网站--关于“应用程序中的服务器错误(需添加"Jquery"ScriptRescourseMapping)”

    一般打开网页进行加载时(有缓存),会弹出以下对话框. 但是如果网页加载后出现以下错误,就是应用程序的问题了.如果出现这种问题,就需要在安装Csharp的根目录下,找到一个名为.dll结尾的Jquery ...

  10. C#改变LInqToSQL的引用地址,读取config的数据库字符串

    C#改变LInqToSQL的引用地址,读取config的数据库字符串修改Properties 下 Settings.Settings 下 Settings.Designer.cs 下 return ( ...