C++面试笔记--排序
这里我们开始复习排序的一些面试题。
首先我们来看一下各个排序方法的时间复杂度和稳定性的比较,见下面表格:
排序法 | 平均时间 | 最差情形 | 稳定度 | 额外空间 | 备注 |
冒泡 | 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,排序结束。
算法示意图:
- 通常堆是通过一维数组来实现的,在起始数组为0的情形中,对于节点i:
算法代码:
// 最大堆调整
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++面试笔记--排序的更多相关文章
- Java高级开发工程师面试笔记
最近在复习面试相关的知识点,然后做笔记,后期(大概在2018.02.01)会分享给大家,尽自己最大的努力做到最好,还希望到时候大家能给予建议和补充 ----------------2018.03.05 ...
- php面试笔记(5)-php基础知识-自定义函数及内部函数考点
本文是根据慕课网Jason老师的课程进行的PHP面试知识点总结和升华,如有侵权请联系我进行删除,email:guoyugygy@163.com 在面试中,考官往往喜欢基础扎实的面试者,而函数相关的考点 ...
- php面试笔记(3)-php基础知识-运算符
本文是根据慕课网Jason老师的课程进行的PHP面试知识点总结和升华,如有侵权请联系我进行删除,email:guoyugygy@163.com 在面试中,考官往往喜欢基础扎实的面试者,而运算符相关的考 ...
- php面试笔记(2)-php基础知识-常量和数据类型
本文是根据慕课网Jason老师的课程进行的PHP面试知识点总结和升华,如有侵权请联系我进行删除,email:guoyugygy@163.com 面试是每一个PHP初学者到PHP程序员必不可少的一步,冷 ...
- GitHub标星125k!阿里技术官用3个月总结出的24万字Java面试笔记
最近收到一位粉丝的回馈! 这位粉丝已经成功入职阿里了小编很是羡慕啊! 今天就把这份30w字Java面试笔记给大家分享出来,说来也巧这份资料也是由一位阿里技术官整理出来的这算不算是"搬起石头砸 ...
- 手游2dx面试笔记一
第一轮IQ测试:都来面试程序了,相信IQ再怎么也坑不到哪里去吧.要问什么样的题,几页纸呐, 如:1.找出不同类:羚羊.斑马.鲨鱼 2.在()里添一字使2边都能组词:木()料 3.中间值?:1,2,4, ...
- <转载> C++笔试、面试笔记
这些东西有点烦,有点无聊.如果要去C++面试就看看吧.几年前网上搜索的.刚才看到,就整理一下,里面有些被我改了,感觉之前说的不对或不完善. 1.求下面函数的返回值( 微软) int func(x) ...
- C++面试笔记--树
树 树的题目基本都是二叉树,但是面试官还没有说是不是二叉树的时候千万不要先把答案说出来,要是面试官说是多叉树,而你做的是二叉树就直接挂了! 一. 树的三种遍历.前序.中序.后序,如果直接考遍历,就肯定 ...
- 面试必备:排序算法汇总(c++实现)
排序算法主要考点: 7种排序 冒泡排序.选择排序.插入排序.shell排序.堆排序.快速排序.归并排序 以上排序算法是面试官经常会问到的算法,至于其他排序比如基数排序等等,这里不列举. 以下算法通过c ...
随机推荐
- SaaS模式实现架构
SaaS模式实现架构 https://blog.csdn.net/xwq911/article/details/50630266 1. 数据库层: 数据库这一层的设计模式是很清晰的,无外乎只有3种方案 ...
- Git 版本管理工具(一)
转自:http://blog.csdn.net/ithomer/article/details/7527877 Git 是一个分布式版本控制工具,它的作者 Linus Torvalds 是这样给我们介 ...
- 三、python沉淀之路--列表(list)
一.列表提供的一些方法 1.append():在原值最后追加 li = [11,22,33,44,55,] li.append(99) print(li) li.append('中国') print( ...
- hadoop完全分布式文件系统集群搭建
一.准备工作: 1.找3台以上的主机(因为HDFS文件系统中保存的文件的blocak在datanode中至少要有3份或3份以上的备份,备份不能放于同一个机架上,更不能放于同一台主机上),我这里使用的是 ...
- 基于JQ的多选/全选/反选及获取选中的值
<!-- author:青芒 --> <!DOCTYPE html> <html lang="en"> <head> <met ...
- vertex shader must minimally write all four components of POSITION
Though the POSITION semantic must be written out by the vertex shader, it cannot be read in by the p ...
- mysql之 Innobackupex(全备+增量)备份恢复
MySQL的热备(物理备份)可以采取全备加增量备份的方式来减轻数据库I/O压力及系统资源的占用.增量备份主要是以全备或增量备份为基础,备份那些变更过的页面.其备份的原理是基于一个不断增长的LSN序列, ...
- DropShadowEffect导致下拉框控件抖动
<!--<Border.Effect> <DropShadowEffect Direction="180" BlurRadius="1" ...
- jenkins jacoco
1.pom中加jacoco依赖 <dependency> <groupId>org.jacoco</groupId> <artifactId>jacoc ...
- UEditor富文本编辑器的使用 http://fex.baidu.com/ueditor/
[转] http://fex.baidu.com/ueditor/ UEditor 介绍 UEditor 是由百度「FEX前端研发团队」开发的所见即所得富文本web编辑器,具有轻量,可定制,注重用户体 ...