【 声明:版权所有,欢迎转载。 联系信箱:yiluohuanghun@gmail.com】
在我的上一篇博客中我谈到了哈希表,采用哈希表对海量数据进行查找是一个不错的方法,它比普通查找算法的有点是不言而喻的。比如说作为一个100万的数据,如果使用普通的查找方法,那么每一个数据查找平均下来就要几十万次,如果采用二分查找20多次就可以搞定了,如果采用哈希表查找的话会更快。这个差别是很明显的。但就这三个查找方法来说,性能好的都是先对数据进行有序操作。在这篇博文中我们就来对排序算法做一个总结。
根据排序过程中数据元素是否完全在内存中分为两类:内部排序和外部排序。排序主要基于三个要素进行对比:时间复杂度,空间复杂度和算法的稳定性。但就我个人而言,我感觉排序算法主要分为递归排序和非递归排序。非递归排序,它主要按照非递归的方法对数据进行排序,也就是说主要数据的移位和循环来完成,递归方法,我们在排列当前数据的时候首先把子数据排列有序,然后才会排列当前的数据。这种不断递归调用的方法就是递归排序。 非递归排序的方法很多,这里主要介绍冒泡排序、插入排序、希尔排序;递归的方法也不少,这里介绍的方法是快速排序、归并排序和堆排序。排序的内容很多,本篇博客主要介绍非递归排序,递归排序的内容主要在下一节内容解决。
一、冒泡排序
在计算机程序设计中,我感觉冒泡排序算是最基础的排序算法了,其思想很简单,根据名字我们也知道应该是排序将大的或者小的“冒”出来。主要思想步骤如下:
1.比较相邻的前后二个数据,如果前面数据大于后面的数据,就将二个数据交换。
2.这样对数组的第0个数据到N-1个数据进行一次遍历后,最大的一个数据就“沉”到数组第N-1个位置。
3.N=N-1,如果N不为0就重复前面二步,否则排序完成。
void bubble_sort(int array[], int length)
{
int inner = 0, outer = 0;
int median = 0;
if(NULL == array || 0 == length)
return;
for(outer = length-1; outer >= 1; outer --){
for(inner = 0; inner < outer; inner ++){
if(array[inner] > array[inner + 1]){
median = array[inner];
array[inner] = array[inner + 1];
array[inner + 1] = median;
}
}
}
}
对于这样一个算法来说,或许更多的人说完美,但是就没有什么改进的地方么?答案是肯定的了。现在如果有一种情况就是发现在一次遍历循环之中,没有发生移位的现象,那么是不是可以判断这个排序可以结束呢?换句话说也就是我们待排序的数据原本就是有序数列,那我们还有必要按部就班的一步一步进行么?NO,我们当然应该说"NO"!说到这里我想大家应该已经知道该怎么做了吧,看下改进后的代码:
void bubble_sort(int array[], int length)
{
int inner = 0, outer = 0;
int median = 0;
int flag = 1; //用flag来标记进行一层遍历后是否有移位产生
if(NULL == array || 0 == length)
return;
for(outer = length-1; outer >= 1 && flag; outer --){
flag = 0;
for(inner = 0; inner < outer; inner ++){
if(array[inner] > array[inner + 1]){
median = array[inner];
array[inner] = array[inner + 1];
array[inner + 1] = median;
if(flag == 0)
flag = 1;
}
}
}
}
这样的话是不是感觉就完美多了呢!!!
二、插入排序
插入排序是一种简单直观的排序算法,其思想在于每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子序列中的合适位置,知道全部记录插入完成为止。一趟排序后,并没有是一个记录到达其最终位置,这是插入类排序方法的共同的特点。
其思想实现步骤如下:
设数组为a[0…n-1]。
1.初始时,a[0]自成1个有序区,无序区为a[1..n-1]。令i=1
2.将a[i]并入当前的有序区a[0…i-1]中形成a[0…i]的有序区间。
3.i++并重复第二步直到i==n-1。排序完成。
typedef int ElemType;
//假设记录类型为int,并且记录关键字就是其本身
void InsertSort(ElemType A[], int n)
{
ElemType x;
for(int i=1;i<n;++i)
{
x=A[i];
int j=i-1;
while(A[j]>x)
{
A[j+1]=A[j];
j--;
if(j < 0)
break;
}
A[j+1]=x;
}
}
当然了,有哥们不太喜欢用while,更倾向于for ,其实吧都是一样的,代码如下:
void Insertsort3(int a[], int n)
{
int i, j;
for (i = 1; i < n; i++)
for (j = i - 1; j >= 0 && a[j] > a[j + 1]; j--)
Swap(a[j], a[j + 1]);
}
在这里说个题外话,之前跟一哥们聊天,他说对于递归比较恐惧,感觉知不道递归是怎么一回事,然后我就跟他说其实你每天都在用递归,只不过是你没留意罢了,当时他和他的小伙伴都惊呆了!确实如此,我们平时用的循环其实就是递归,当然了我们在这里可以做一个实例,比如说要求1到n的和,我们一般都会像下面这样写程序:
int Fun(int n)
{
int count = 0;
if(n < 0)
return -1;
for(int i = 0; i <= n; i++)
count += i;
return count;
}
这应该是我们大部分人惯用的方法,那么用递归式怎样呢?如下:
int Fun(int n)
{
if(n == 0)
return 0;
else
return Fun(n - 1) + n;
}
这个时候你会恍然大悟,确实如此!!!递归原来就在我身边,而卧整天还在用,这次我真的和我的小伙伴都惊呆了!
三、希尔排序
希尔排序,我个人认为可以看成是冒泡排序的变种。它的基本思想是:首先按照一个序列递减的方法逐渐进行排序。比如说有10个数据,我们按照序列5、3、1的顺序进行排序。首先是5,那么我们对1和6、2和7、3和8、4和9、5和10进行排列;第二轮是3,那么对数据1、4、7、10排列,再对2、5、8进行排列,以及3、6、9排列;第三轮就和冒泡排序一样了,以此对每个数据进行排列。它的优势就是让整个队列基本有序,减少数据移动的次数,从而降低算法的计算复杂度。
void shell_sort(int array[], int length, int step)
{
int inner = 0;
int outer = 0;
int median = 0;
if(NULL == array || 0 == length)
return;
for(; step >= 1; step -=2){
for(int index = 0; index < step; index ++){
if((length -1) < (index + step))
continue;
else{
outer = index + step;
while( (outer + step) <= (length - 1))
outer += step;
}
for(; outer >= (index + step); outer -= step){
for(inner = index; inner <= outer - step; inner += step){
if(array[inner] >= array[inner + step]){
median = array[inner];
array[inner] = array[inner + step];
array[inner + step] = median;
}
}
}
}
}
}
转载于:https://blog.51cto.com/yiluohuanghun/1259946