排序算法
排序算法 ,在编程中算一个最基础的问题之一 。
排序算法有很多的方法 ,比如:
1.冒泡排序 ;
2.插入排序;
3.希尔排序;
4.选择排序;
5.堆排序;
6.快速排序;
7.归并排序;
8.基数排序;
这个便是算 法界 中广为流传的 八大算法 。不知道的可以记一下 。
下面就来详细讲讲这些算法的优缺点:
这幅图,是我从网上找的串,但这不重要 ,它很好的向我们展示了 八大算法的 时间复杂度 、空间复杂度
以及稳定性
下面来详细讲讲这些算法
一、冒泡排序
基本思想:通过无序区中相邻元素间的比较和位置的交换,使最小的元素如气泡一般逐渐往上“漂浮”直至“水面”——就是数组尾部 ,在这里形成一个有序区。
关键点:设计交换判断条件,提前结束以排好序的序列循环。
时间复杂度:
最好情况下:正序有序,则只需要比较n次。故,为O(n)
最坏情况下: 逆序有序,则需要比较(n-1)+(n-2)+……+1,故,为O(n*n)
图形表达:
代码实现:
- void Bubble_Sort(int arr[],int len)//arr为数组名,len为数组长度
- {
- int i =0 ;
- int j = 0;
- for( i=0;i<len-1;i++)
- {
- for(j=0;j<(len-1-i);j++)//设置无序与有序序列的分割条件 (len-1-i)
- {
- if(arr[j]>arr[j+1])//前者小于后者
- {
- int temp=arr[j];//两数交换
- arr[j]=arr[j+1];
- arr[j+1]=temp;
- }
- }
- }
- }
二、插入排序
基本思想:将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据。每步将一个待排序的纪录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止。
算法适用于少量数据的排序,时间复杂度为O(n^2)。是稳定的排序方法。
算法的操作:
插入算法把要排序的数组分成两部分:第一部分包含了这个数组的所有元素,但将最后一个元素除外(让数组多一个空间才有插入的位置),而第二部分就只包含这一个元素(即待插入元素)。在第一部分排序完成后,再将这个最后元素插入到已排好序的第一部分中。
代码实现:
- void Insert_Sort(int arr[],int sz)//sz为数组的长度
- {
- int i =0 ,j=0;
- int temp = 0;
- for(i =1 ;i< sz;i++)
- {
- temp =arr[i];//记录下 要插入到序列中的数
- j = i -1;
- while(j>=0&& temp<arr[j])//要插入的序列
- {
- arr[j+1]= arr[j]; //将序列中的数字向后移上一位 直到插入的数大于序列中的数
- j--;
- }
- arr[j+1] = temp;//将要插入的数插到序列中
- }
- }
三、希尔排序
基本思想:希尔排序也是一种插入排序方法,实际上是一种分组插入方法。先取定一个小于n的整数d1作为第一个增量,把表的全部记录分成d1个组,所有距离为d1的倍数的记录放在同一个组中,在各组内进行直接插入排序;然后,取第二个增量d2(<d1),重复上述的分组和排序,直至所取的增量dt=1(dt<dt-1<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
例如:将 n 个记录分成 d 个子序列:
{ R[0], R[d], R[2d],…, R[kd] }
{ R[1], R[1+d], R[1+2d],…,R[1+kd] }
…
{ R[d-1],R[2d-1],R[3d-1],…,R[(k+1)d-1] }
算法 关键 :增量的选择以及排序最终以1为增量进行排序结束。
时间复杂度。
最好情况:由于希尔排序的好坏和步长d的选择有很多关系,因此,目前还没有得出最好的步长如何选择(现在有些比较好的选择了,但不确定是否是最好的)。所以,不知道最好的情况下的算法时间复杂度。
最坏情况下:O(n*logn),最坏的情况下和平均情况下差不多。
平均情况下:O(n*logn)
- void Shell_Sort(int arr[],int len)
- {
- int d = 0 ;
- int i = 0;
- int j = 0;
- int temp = 0;
- d= len/2;
- while(d > 0)
- {
- for(i = d;i< len ;i++)//各组内的插入排序
- {
- temp =arr[i];
- j =i-d;
- while(j >= 0 && arr[j] > temp )
- {
- arr[j+d] = arr[j];
- j -=d;
- }
- arr[j+d] =temp;
- }
- d=d/2;//递减增量
- }
- }
四、选择排序
基本思路:每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。
关键点:记住有序数列与无序数列的间隔
思路图:
代码实现
- void Select_Sort(int arr[],int len)//len为数组长度
- {
-
- int i = 0;
- int j = 0;
- int temp= 0;
- for(i =0 ;i< len-1;i++)//判断需要几次排序
- {
- int min =i;//min为最小元素的下标
- for(j =i;j< len;j++)//将无序区的最小的数下标的找出来给min
- {
- if(arr[j] <arr[min])
- {
- min= j;
- }
- }
- temp= arr[i];//将下标为min的数放到有序区
- arr[i]= arr[min];
- arr[min] = temp;
- }
- }
五、堆排序
堆排序(Heapsort)是指利用堆积树(堆)这种资料结构所设计的一种排序算法,可以利用数组的特点快速定位指定索引的元素。堆排序利用了大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。
关键点:建堆 ,交换 ,堆调整
堆排序的基本思想是:
利用完全二叉树中双亲节点和孩子节点之间的内在关系,在当前无序区中选择关键字最大(或者最小)的记录。也就是说,以最小堆为例,根节点为最小元素,较大的节点偏向于分布在堆底附近。
代码实现
这个代码是转载别人的
六、快速排序
基本思路:它是由冒泡排序改进而来的。在待排序的n个记录中任取一个记录(通常取第一个记录),把该记录放入适当位置后,数据序列被此记录划分成两部分 。
数组所有元素大小 比该记录小的放到前一部分,比它大的放到右边
并把该记录排在这两部分的中间(称为该记录归位),这个过程称作一趟快速排序。
最核心的思想是 左右 分割。
算法复杂度
最好的情况下:因为每次都将序列分为两个部分(一般二分都复杂度都和logn 相关),故为 O(n*logn)
最坏的情况下:基本有序时,退化为冒泡排序,几乎要比较n*n次,故为O(n*n)
举个例子吧
2 2 4 9 3 6 7 1 5
首先用2当作基准,使用i j两个指针分别从两边进行扫描,把比2小的元素和比2大的元素分开。
2 2 4 9 3 6 7 1 5 首先比较2和5,5比2大,j左移
2 2 4 9 3 6 7 1 5 比较2和1,1小于2,所以把1放在2的位置
2 1 4 9 3 6 7 1 5 比较2和4,4大于2,因此将4移动到后面
2 1 4 9 3 6 7 4 5 比较2和7,2和6,2和3,2和9,全部大于2,满足条件,因此不变
经过第一轮的快速排序,元素变为下面的样子
[1] 2 [4 9 3 6 7 5]
之后,在把2左边的元素进行快排,由于只有一个元素,因此快排结束。右边进行快排,递归进行,最终生成最后的结果。
代码实现
- void Quick_Sort(int arr[],int start,int end)//对数组中的下标为i - j的元素 进行快速排序
- {
- int temp = arr[start];//temp 表示 记录位
- int i = start;
- int j = end;
- if(start < end)//从两端交替向中间扫描
- {
- while(i < j)
- {
- while(i<j && arr[j] >=temp)
- {
- j--;
- }
- if(i< j)
- {
- arr[i]=arr[j];
- i++;
- }
- while(i<j && arr[i]<temp)
- {
- i++;
- }
- if(i<j)
- {
- arr[j]=arr[i];
- j--;
- }
-
- }
- arr[i] = temp;
- Quick_Sort(arr,start,i-1);//对左半部分进行递归调用
- Quick_Sort(arr,i+1,end);//对右半部分进行递归调用
- }
- }
七、归并排序
基本思路:
把待排序序列分为若干个有序的子序列,再把有序的子序列合并为整体有序序列。
如设有数列{6,202,100,301,38,8,1}
初始状态:6,202,100,301,38,8,1
第一次归并后:{6,202},{100,301},{8,38},{1},比较次数:3;
第二次归并后:{6,100,202,301},{1,8,38},比较次数:4;
第三次归并后:{1,6,8,38,100,202,301},比较次数:4;
总的比较次数为:3+4+4=11,;
归并排序具体工作原理如下(假设序列共有n个元素):
将序列每相邻两个数字进行归并操作(merge),形成floor(n/2)个序列,排序后每个序列包含两个元素
将上述序列再次归并,形成floor(n/4)个序列,每个序列包含四个元素
重复步骤2,直到所有元素排序完毕
八、基数排序
基本思想:
它是一种非比较排序。它是根据位的高低进行排序的,也就是先按个位排序,然后依据十位排序……以此类推。
设有一个初始序列为: R {50, 123, 543, 187, 49, 30, 0, 2, 11, 100}。
我们知道,任何一个阿拉伯数,它的各个位数上的基数都是以0~9来表示的。
所以我们不妨把0~9视为10个桶。
我们先根据序列的个位数的数字来进行分类,将其分到指定的桶中。例如:R[0] = 50,个位数上是0,将这个数存入编号为0的桶中。
分类后,我们在从各个桶中,将这些数按照从编号0到编号9的顺序依次将所有数取出来。
这时,得到的序列就是个位数上呈递增趋势的序列。
按照个位数排序: {50, 30, 0, 100, 11, 2, 123, 543, 187, 49}。
接下来,可以对十位数、百位数也按照这种方法进行排序,最后就能得到排序完成的序列。
0 0