排序方法 |
平均情况 |
最坏情况 |
最好情况 |
稳定性 |
1.冒泡排序 |
N^2 |
N^2 |
n |
稳定 |
2.选择排序 |
N |
N^2 |
N^2 |
不稳定 |
3.归并排序 |
nlogn |
nlogn |
nlogn |
稳定 |
4.基数排序 |
Nlog(r)m |
Nlog(r)m |
Nlog(r)m |
稳定 |
5.计数排序 |
N |
N |
N |
稳定 |
6.快速排序 |
nlogn |
N^2 |
nlogn |
不稳定 |
7.随机化快速排序 |
nlogn |
Nlogn |
Nlogn |
不稳定 |
8.插入排序 |
N^2 |
N^2 |
nlogn |
不稳定 |
void bubble_sort(int a[], int n) //下面是函数bubble_sort的程序
{
int i,j,temp; //定义三个整型变量
for (j=0;ja[i+1]) //从大到小排就把左边的">"改为"<" !!!
{
temp=a[i]; //a[i]与a[i+1](即a[i]后面那个) 交换
a[i]=a[i+1]; //基本的交换原理"c=a;a=b;b=c"
a[i+1]=temp;
}
}
}
}
def get_mid(arr, left, right):
pivot = arr[left] # 保存基准
while left < right:
while left < right and arr[right] >= pivot:
right -= 1 # 游标左移
arr[left] = arr[right]
while left < right and arr[left] < pivot:
left += 1
arr[right] = arr[left]
arr[left]=pivot # 此时left和right相遇了,所以把枢轴赋给谁都可以
return left
def quick_sort(arr, left, right):
if left
void kuaisu_sheng(int left,int right)
{
if(left>=right) //如果左边索引大于或者等于右边的索引就代表已经整理完成一个组了
return ;
int i=left; //将区间记录下来
int j=right;
int key=a[i]; //记录参考值
while(i=a[i]) //这是i在当组内向前寻找,同上,不过注意与key的大小关系停止循环和上面相反,因为排序思想是把数往两边扔,所以左右两边的数大小与key的关系相反
i++;
a[j]=a[i];
}
a[i]=key; //当在当组内找完一遍以后就把中间数key回归
kuaisu_sheng(left,i-1); //最后用同样的方式对分出来的左边的小组进行同上的做法
kuaisu_sheng(i+1,right); //用同样的方式对分出来的右边的小组进行同上的做法
//当然最后可能会出现很多分左右,直到每一组的i = j 为止
}
1 它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,
然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
2 算法介绍
设要排序的数组是A[0]……A[N−1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。
值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
一趟快速排序的算法是:
1)设置两个变量i、j,排序开始的时候:i=0,j=N−1;
2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];
3)从j开始向前搜索,即由后开始向前搜索 j−−,找到第一个小于key的值A[j,将A[j和A[i]互换;
4)从i开始向后搜索,即由前开始向后搜索 i++,找到第一个大于key的A[i],将A[i]和A[j]互换;
5)重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,44中A[i]不大于key的时候改变j、i的值,使得j=j−1,i=i+1,
直至找到为止。找到符合条件的值,进行交换的时候i,j指针位置不变。另外,i==j这一过程一定正好是 i++或 j−−完成的时候,此时令循环结束)
3 是对冒泡排序的一种改进,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
4 以标兵为轴,将数组分段,轴左边都比轴小,轴右边都比轴大,再将其两段分别进行快排,
直到分段后最左边的数的下角标等于或大于最右边的数的下角标时停止。
5 快排比大部分算法都要快,除特殊情况下,一般情况下没有比它更快的,它不适用于数据量非常大时,容易发生堆栈溢出错误,
当数列有大到小或由小到大时为最坏情况,选取的轴在最两侧。
void select_sort(int R[],int n) //定义选择排序函数"select_sort"
{
int i,j,k,index; //定义变量
for(i=0;i" !!!
k=j; //这里是区分冒泡排序与选择排序的地方,冒泡没这句
}
if(k!=j) //为了严谨,去掉也行
{
index=R[i]; //交换R[i]与R[k]中的数
R[i]=R[k]; //简单的交换c=a,a=b,b=c
R[k]=index;
}
}
}
1 是一种简单直观的排序算法。1它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,
直到全部待排序的数据元素排完。
2 选择排序是不稳定的排序方法(比如序列[5, 5, 3]第一次就将第一个[5]与[3]交换,导致第一个5挪动到第二个5后面)
3 思想:n个记录的文件的直接选择排序可经过n−1趟直接选择排序得到有序结果:
①初始状态:无序区为R[1…n],有序区为空。
②第1趟排序
在无序区R[1…n]中选出关键字最小的记录R[k],将它与无序区的第1个记录R[1]的值进行交换,使无序区记录个数减1(R[2…n]),有序区记录个数加1(R[1…1])
……
③第i趟排序
第i趟排序开始时,当前有序区和无序区分别为R[1…i−1]和R(i…n)。该趟排序从当前无序区中选出关键字最小的记录 R[k],将它与无序区的第1个记录交换,使R[1…i]和R分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区。
4 通俗的解释:对比数组中前一个元素跟后一个元素的大小,如果后面的元素比前面的元素小则用一个变量k来记住他的位置,接着第二次比较,
前面“后一个元素”现变成了“前一个元素”,继续跟他的“后一个元素”进行比较如果后面的元素比他要小则用变量kk记住它在数组中的
位置(下标),等到循环结束的时候,我们应该找到了最小的那个数的下标了,然后进行判断,如果这个元素的下标不是第一个元素的下标,
就让第一个元素跟他交换一下值,这样就找到整个数组中最小的数了。然后找到数组中第二小的数,让他跟数组中第二个元素交换一下值,
以此类推。
5 选择排序和冒泡排序差不多,使用较少
void Insertion_Sort(int a[],int n)
{
int i,j;
for(i=1;i0&&a[j-1]>temp;--j)
a[j]=a[j-1];
a[j]=temp;
}
}
插入排序它的思路是:从第二个数开始,与已排好的序列的最后一个数比较,如果其值小于最后一个数,则与前一个数比较,到找到小于它的数,将其插入到这个数的后面
每次从未排好的序列中选出第一个元素插入到已排好的序列中。
它的算法步骤可以大致归纳如下:
插入排序不适合数据量比较大或重复次数较多的时候,当序列由小到大时为最好情况,当序列由大到小时情况最差
void shell_sort(int array[], int length){
int i;
int j;
int k;
int gap; //gap是分组的步长
int temp; //希尔排序是在直接插入排序的基础上实现的,所以仍然需要哨兵
for(gap=length/2; gap>0; gap=gap/2){
for(i=0; i=0 && array[k]>temp){
array[k + gap] = array[k];
k = k - gap;
}
array[k + gap] = temp;
}
}
}
}
}
1 插入排序每次只能移动数据一位,而shell算法成功的解决了这个问题.
2 希尔排序的原理
shell算法的核心还是分组,但是这个分组就有门道儿了,因为它会实现取一个小于总数据长度的整数值gap作为分组的步长,什么意思呢?
假如我们的待排序数组为:
索引 1 2 3 4 5 6 7 8 9 10
49 38 65 97 76 13 27 49 55 04
设置gap的值为长度10的一半也就是5,那么第一个和第六个元素就是一组,第二个和第七个元素就是一组,第三个和第八个元素就是一组,第四个和第九
个元素就是一组,第五个和第十个元素就是一组,所以一共分为了gap = 5组,
(这里两个数一组是假想的目的是为了交换起来看着更容易理解,实际上分组以后数组仍然是这种形式:49, 38 65 97 76 13 27 49 55 04)
组 一 二 三 四 五
序号 1 6 2 7 3 8 4 9 5 10
数据 49 13 38 27 65 49 97 55 76 04
交换后 13 49 27 38 49 65 55 97 04 76
然后如上面每一组之间进行再直接插入排序,比较如果前一个元素比较大,则交换两个元素的位置,直至5组全部交换完毕.此时数组的顺序为
13 27 49 55 04 49 38 65 97 26.
然后gap的值再减半为2,重新分组,也就是第一个 第三个 第五个 第七个 第九个 元素为第一组是13 49 4 38 97, 第二个 第四个
第六个 第八个 第十个元素为一组是27 55 49 65 26.
组 一 二
序号 1 3 5 7 9 2 4 6 8 10
数据 13 49 04 38 97 27 55 49 65 26
交换后04 13 38 49 97 26 27 49 55 65
然后如上面对它们两个组分别进行直接插入排序,得到结果为
4 26 13 27 38 49 49 55 97 65,
之后gap的值再减半为1(要知道gap的值小于1的时候在分组就没意义了,一位你的每一个组至少要有一个元素才能组成一个序列.)这次我们直接对上一次的结果进行一次真正的直接插入排序(为什么说是真正的呢,因为此时步长已经为1)直至得出最终结果:
4 13 26 27 38 49 49 55 65 97.
代码略,只理解思想
1 堆排序是利用堆的性质进行的一种选择排序。
2 堆分为大顶堆和小顶堆,大顶堆的堆顶的关键字肯定是所有关键字中最大的,小顶堆的堆顶的关键字是所有关键字中最小的
3 堆排序的思想
利用大顶堆的堆顶最大这一特性,使得每次从无序中选择最大记录变得简单。
其基本思想为(大顶堆):
1)将初始待排序关键字序列(R1,R2....Rn)构建成大顶堆,此堆为初始的无须区;
2)将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,......Rn-1)和新的有序区(Rn),且满足R[1,2...n-1]<=R[n];
3)由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,......Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2....Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。
操作过程如下:
1)初始化堆:将R[1..n]构造为堆;
2)将当前无序区的堆顶元素R[1]同该区间的最后一个记录交换,然后将新的无序区调整为新的堆。
因此对于堆排序,最重要的两个操作就是构造初始堆和调整堆,其实构造初始堆事实上也是调整堆的过程,只不过构造初始堆是对所有的非叶节点都进行调整。
下面举例说明:
给定一个整形数组a[]={16,7,3,20,17,8},对其进行堆排序。
1 首先根据该数组元素构建一个完全二叉树,得到
2 构造初始堆,则从最后一个非叶节点开始调整,调整过程如下:
3 有了初始堆之后就可以进行排序了。
这样整个区间便已经有序了。
从上述过程可知,堆排序其实也是一种选择排序,是一种树形选择排序。只不过直接选择排序中,为了从R[1…n]中选择最大记录,需比较n-1次,然后从R[1…n-2]中选择最大记录需比较n-2次。
事实上这n-2次比较中有很多已经在前面的n-1次比较中已经做过,而树形选择排序恰好利用树形的特点保存了部分前面的比较结果,因此可以减少比较次数。对于n个关键字序列,最坏情况下每个节点需比较log2(n)次,
因此其最坏情况下时间复杂度为nlogn。堆排序为不稳定排序,不适合记录较少的排序。
代码略
1 冒泡排序,插入排序,选择排序,他们的时间复杂度都是O(n^2),适用于小规模的数据集,今天我们讲一下比前3者更加高效的排序,
归并排序和快排,他们的时间复杂度是O(nlogn),适用于大数据集.归并排序和快排都用到了分而治之思想,
2 原理:所谓归并排序,就是将两个排好序的序列归并在一起,形成一个新序列。
那么,如何得到排好序的序列呢,这里就体现了分治的思想。
我们可以将一个序列,分成两个,四个,八个….分到每个序列只有一个元素。那么这些序列就都是排好序的(因为每个序列只有一个元素啊)。
这之后,再将这些序列两个两个归并起来。
这样先分后合,就从初始的乱序序列变成归并排序后的有序序列。
3 归并排序不适合数据量较大的时候用,因为运用递归,数据量较大时,容易堆栈溢出错误。
代码略
1 基本思想是:将整数按位切割成不同的数字,然后对每个数的同一位进行排序。
2 具体做法:将所有待排序数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序操作。
这样从最低位排序一直到最高位排序完毕,数组就变成一个有序数组了
3 简单例子
将数组{27, 91, 1, 97, 17, 23, 84, 28, 72, 5, 67, 25}排序的流程如下
(这里把0-9想象成10个桶,从桶口放数据,从桶底取数据,这样拿出来就是按照个位有序)
4 基数排序(LSD)适用于位数较少的数列排序,如果位数较大,采用(MSD)方法,(MSD:与LDS相反从高位开始比较),适合用于字符串或整数。
参考链接:https://www.cnblogs.com/wuchanming/p/3821607.html
https://blog.csdn.net/lchad/article/details/43564001
https://blog.csdn.net/sty20030818/article/details/81123961
https://blog.csdn.net/qq_39936389/article/details/82724405
https://blog.csdn.net/w5201314ws6123/article/details/86037656
https://blog.csdn.net/yang03_26/article/details/80773280