各种排序算法总结及代码

 排序算法作为最基本的算法,也是各大IT公司最喜欢的笔试题目,下面就各种排序算法作以总结并附代码(C/C++),代码亲自编写与验证,没有问题。

  主要涉及冒泡排序,选择排序,插入排序,希尔排序,堆排序,快速排序,归并排序,计数排序,基数排序,桶排序

1、冒泡排序

基本思想:比较相邻的两个数,小的往前放,大的往后放(默认从小到大),这很类似冒泡,故称冒泡排序。这样每躺下来都能得到一个最大的数(即最后那个数),反复重新,即可完成所有排序。

时间复杂度:平均时间复杂度O(n^2),最坏情况O(n^2),这种算法简单易懂,稳定排序,但复杂度也太大了吧

代码如下:(各大算法中经常涉及交换算法,先放前面)

void swap(int *p, int *q)
{
int temp;


temp = *p;
*p = *q;
*q = temp;
}

void BubbleSort(int *p, int len)
{
int i;
int j;


for(i=0; i<len-1; i++)
{
for(j=0; j<len-i-1; j++)
{
if(p[j] > p[j+1])
{
swap(&p[j], &p[j+1]);
}
}
}
}

2、选择排序

基本思想:每一趟比较,从待排序的元素中选择出一个最大(或最小)的数,反复重复,即可完成所有排序工作,其实类似冒泡排序

时间复杂度:平均时间复杂度=最坏复杂度=O(n^2),不稳定排序(由于不同位置数的交换),据说n比较小时比冒泡要好点

代码如下:

void SelectSort(int *p, int len)
{
int i;
int j;
int temp;


for(i=0; i<len-1; i++)
{
temp = i;
for(j=i+1; j<len; j++)
{
if(p[j] < p[temp])
{
temp = j;
}
}
if(i != temp)
{
swap(&p[temp], &p[i]);
}
}
}

3、插入排序

基本思想:每次将待排序的数插入到已排好序的适当位置(按大小)即将a[i]插入到已排好序的a[0...i-1]中,直到插完所有数据元素为止。插入排序步适合数据量较大的排序

时间复杂度:平均时间复杂度O(n^2)=最坏时间复杂度,属于稳定排序

4、希尔排序

基本思想:它对插入排序的一种改进,本质也是插入排序,只是按照步长进行插入排序。先选择一个大的步长把序列分成若干小序列,再进行插入排序,然后逐渐减少排序步长,重复插入排序,当最终步长=1时表示整个序列排序完成。它没有快速排序(O(n^2))等快,但比冒泡、选择排序快,其实现简单,代码量也少,所以对于中等规模数据排序比较好

时间复杂度:会比O(n^2)小,但得和步长的选择有关,也是不稳定的

代码如下:

void ShellSort(int *p, int len)
{
int step = len / 2;//增量,可自己设置,一般取n/2
int i;
int j;
int temp;


while(step > 0)//直到增量为1,排序结束
{
for(i=step; i<len; i++)//以不同的步长(增量)插入,相当于对每个组列排序
{                      //step = 1,则所有顺序排好
temp = p[i];
for(j=i-step; j>=0 && p[j]>p[j+step]; j-=step)
{
//swap(&p[j], &p[j+step]);//对每组序列对应元素比较大小
p[j+step] = p[j];//为了减少交换函数的调用
p[j] = temp ;
}
}


step /= 2;//增量变化规则,可以自己定
}
}

5、快速排序

基本思想:其实是对冒泡排序的一种改进,首先选择一个元素key,将小于key的元素放在左边,大于key的元素放右边。然后再对两组元素才用同样的方法,反复重复,但最后只有一个元素时,则排序结束。貌似这是最长使用的一种排序,复杂度也低,事实上比其他nlg(n)的算法还快点
时间复杂度:平均时间复杂度O(nlg(n)) ,最坏时间复杂度O(n^2),

代码如下:

void QuickSort(int *p, int s, int e)
{
int q;//分界值的下标

if( s < e)
{
q = Quickpass(p, s, e);
QuickSort(p, s, q);
QuickSort(p, q+1, e);
}
}

int Quickpass(int *p, int s, int e)
{
int key = p[s];//key即分界值随便取
int l = s;//分界值左部
int r = e;//分界值右部
//以下采用挖坑法进行快速排序,挖一坑填一坑,key即为第一个坑.这种方法几乎不用交换
while(l < r)//左右相等则完成
{
while((l < r) && (p[r] >= key) )//从右向左
r--;
if(l < r)
p[l++] = p[r];
while((l < r) && (p[l] < key))//从左向右
l++;
if(l < r)
p[r--] = p[l];
}
p[r] = key;
return r;
}

6、堆排序

基本思想:将数据用树结构来表示的选择排序,不断的把数组建立成大根堆(或小根堆),如建立大根堆后最顶元素a[0]最大,然后与最底元素a[n]交换,其实也就是把最大元素抛弃后,再重新建立n-1的大根堆,如此反复,最后即可完成所有排序。因为初始堆需要的比较次数较多,所以步适合个数较少的元素。

时间复杂度:平均时间复杂度O(nlg(n))=最坏时间复杂度,为不稳定排序

代码如下:

oid HeapSort(int *p, int len)
{
int i;


BuildMaxHeap(p, len);
for(i=len-1; i>=1; i--)
{
swap(&p[0], &p[i]);//把堆顶最大值p[0]放在最下面,相当于找到最大值,类似选择排序
len--;
MaxHeapify(p, 0, len);//重新调整最大堆,不过元素个数变为len-1
//一次循环,即可把数组从小到大排好序,若采用最小堆,就是从大到小
}
}

void MaxHeapify(int *p, int i, int len)
{
int l = 2 * i + 1;//左节点
int r = 2 * i + 2;//右节点
int largest ;


if( l<len && p[l]>p[i])
largest = l;
else 
largest = i;
if(r<len && p[r]>p[largest])
largest = r;
if(largest != i)
{
swap(&p[i], &p[largest]);
MaxHeapify(p, largest, len);//保证子树最大堆性质
}

}
//把排序数组建立成最大堆
void BuildMaxHeap(int *p, int len)//
{
int i;


for(i= len/2-1; i>=0; i--)//节点i对于父节点为(i-1)/2
{
MaxHeapify(p, i, len);
}
}

7、归并排序

基本思想:采用分治法,利用递归思想。归并就是把两个已排好序的序列和并成一个序列。具体步骤就是首先将相邻两数归并形成n/2个数列,每个序列包含两个元素,排完序后再归并形成n/4个序列,每个序列包含4个元素,如此反复最终变形成一个已排好序的序列。用于总体无序,各子项有许的数列比较有效。

时间复杂度:平均时间复杂度O(nlg(n)) = z最坏时间复杂度,稳定排序,速度仅次于快速排序

代码如下:

void MergeSort(int *p, int s, int e, int *temp)
{
int mid;
if(s < e)
{
mid = (s + e) / 2;
MergeSort(p, s, mid, temp);//左边有序
MergeSort(p, mid+1, e, temp);//右边有序
merge(p, s, mid, e, temp);
}
}

void merge(int *p, int s, int mid, int e, int *temp)
{
int m = mid + 1;
int i;
int k = s;
int j = s;


while(s <= mid && m <= e)
{
if(p[s] < p[m])
{
temp[j++] = p[s++];
}
else
{
temp[j++] = p[m++];
}
}


while(s <= mid)
{
temp[j++] = p[s++];
}

while( m <= e)
{
temp[j++] = p[m++];
}


for(i=k; i<=e; i++)
{
p[i] = temp[i];
}
}

   

以上算法都是比较排序算法即通过比较各个元素大小来排序,下面介绍几个线性时间排序,不需要比较

8、计数排序

基本思想:对于每一个输入元素x,确定小于x元素的个数。利用这一信息,就可以直接把x放在合适的位置了。如有18个元素小于x,那x应该就在19位置上

时间复杂度:O(n+k)(元素取值范围为:0-k),但有限制:对元素取值范围有限制,需要知道数据中元素的取值范围,这也需要比较大的内存,稳定排序

代码如下:

void CountSort(int *in, int *out, int len)
{
int *c = NULL;//假设元素大小从0-max,数组c用来记录个元素位置
    int max;
int i;
int j;


max = MaxElement(in, len);//得到数组中最大元素
c = (int *)malloc((max+1)*sizeof(int));
if(NULL == c)
{
printf("no memory allocated for c!\n ");
exit(-1);
}
for(i=0; i<=max; i++)
{
c[i] = 0;//先初始化
}
for(i=0; i<len; i++)
{
c[in[i]]++;//统计元素出现的个数
}
for(j=1; j<=max; j++)
{
c[j] += c[j-1];//j表示小于等于j的元素的个数
}
for(j=len-1; j>=0; j--)
{
out[c[in[j]]-1] = in[j];//out就是最后输出数组
c[in[j]]--;
}


free(c);
}

int MaxElement(int *in, int len)
{
int max ;
int i;
max = in[0];
for(i=1; i<len; i++)
{
if(in[i] > max)
{
max = in[i];
}
}
return max;
}


9、基数排序又称桶子法

基本思想:将要排序的元素分配到各个桶中,再对各桶排序,最后倒出来即完成排序。如,取十个桶,编号为0-9,先取各元素的各位数字,根据编号倒到桶中,再从0-9桶依次倒出即完成排序,然后再对十分位、百分为依次重复,最终即可完成排序。

时间复杂度:O (nlog(r)m),r为基数,m为堆数,但也常称O(n)

代码如下:

void RadixSort(int *p, int len)
{
int maxnum;//最大元素
int d;//最大元素的位数
int i;


maxnum = MaxElement(p, len);//调用计数排序的函数
d = GetDigit(maxnum);
for(i=1; i<=d; i++)//对每位进行桶分配
{
sort(p, len, i);
}
}

int GetDigit(int maxnum)
{
int count = 1;
int temp;


temp = maxnum / 10;
while(temp != 0)
{
count++;
temp /= 10;
}


return count;
}
void sort(int *p, int len, int d)
{
int bucket[10][BUCKET_NUM] ;//bucket[10][bucket_num],
int *count = NULL;//统计bucket[i]的个数
int c = 0;
int i;
int j;
int index;//用于求元素各位上的值
int d_index;
int k;
/*
bucket = (int **)malloc(sizeof(int*)*10);
if(bucket == NULL)
{
printf("malloc erro!\n");
exit(-1);
}

for(i=0; i<10; i++)
{
bucket[i] = (int *)malloc(sizeof(int)*BUCKET_NUM);
if(bucket[i] == NULL)
{
printf("malloc erro!\n");
exit(-1);
}
}
*/
index = (int)pow(10.0,(d-1));
//将数组元素装进各桶
for(i=0; i<len; i++)
{
d_index = (p[i]/index)%10;
for(j=0; j<BUCKET_NUM; j++)
{  
if(bucket[d_index][j] < 0)//数组没值时会有一个很大的负数

{
bucket[d_index][j] = p[i];
break;
}
}
}
//将桶中各元素有序倒出
k = 0;
for(i=0; i<10; i++)
{
for(j=0; j< BUCKET_NUM; j++)
{
if(bucket[i][j] >= 0)
p[k++] = bucket[i][j];
}
}
/*
for(i=0; i<10; i++)
{
free(bucket[i]);
}
free(bucket);
*/
}

10、桶排序

基本思想:类似计数和基数排序,不过输入是随机产生的0-1的均匀分布的实数,将0-1分成n个大小相等的子区间(桶),然后对桶中的元素排序。

时间复杂度:线性的O(N+C),其中C=N*(logN-logM),最好的时间复杂度为O(N)

代码和计数或基数代码,有兴趣者可自行编写





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值