各种基本算法实现小结(五)—— 排序算法

各种基本算法实现小结(五)—— 排序算法

(均已测试通过)

* 选择排序 |____简单选择排序 |____堆排序 |____归并排序

* 交换排序 |____冒泡排序 |____快速排序

* 插入排序 |____直接插入排序 |____折半排序 |____希尔排序

* 分配排序 |____箱排序 |____基数排序

======================================================================

简单排序算法

1、 冒泡排序

测试环境:VC 6.0 (C)

#include
<stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX 11
void input(int num[])
{
int i;

srand((unsigned)time(NULL));
for(i=1; i<MAX; i++)
num[i]=rand()%100;
}
void output(int num[])
{
int i;

for(i=1; i<MAX; i++)
{
printf("%5d", num[i]);
if(0 == i%10)
printf("/n");
}
printf("/n");
}
void sort(int num[])
{
int i, j, tmp;
for(i=1; i<MAX-1; i++)
{
printf("bubble.../n");
for(j=1; j<MAX-i; j++)
{
printf("%5d", num[j]);
if(num[j]>num[j+1])
{
tmp=num[j];
num[j]=num[j+1];
num[j+1]=tmp;
}
}
printf("%5d/n", num[MAX-i]);

printf("bubble after.../n");
for(j=1; j<MAX; j++)
printf("%5d", num[j]);
printf("/n");
}
}
/* bubble sort */
/*
void sort(int num[])
{
int i, j, tmp;
for(i=1; i<MAX-1; i++)
{
for(j=1; j<MAX-i; j++)
if(num[j]>num[j+1])
{
tmp=num[j];
num[j]=num[j+1];
num[j+1]=tmp;
}
}
}
*/
void main()
{
int num[MAX];
printf("sort before.../n");
input(num);
output(num);
sort(num);
printf("sort after.../n");
output(num);
}

运行结果:

=======================================================

2、双向冒泡排序

据说可以提高效率,减少比较次数和交换次数

但仔细分析可得,每次while循环,都for循环比较了两次

因此每次low和high各减1,总体上比较次数并未减少,两次for交换也未减少

个人认为双向冒泡算法并未有效的减少比较次数和交换次数,但此算法也富含编程思想,值得学习

测试环境:VC 6.0 (C)

#include
<stdio.h>
#include <stdlib.h>
#include <time.h>
#define swap(x, y){int tmp; tmp=x; x=y; y=tmp;}
#define MAX 11
void input(int num[])
{
int i;

srand((unsigned)time(NULL));
for(i=1; i<MAX; i++)
num[i]=rand()%100;
}
void output(int num[])
{
int i;

for(i=1; i<MAX; i++)
{
printf("%5d", num[i]);
if(0 == i%10)
printf("/n");
}
}
void sort(int num[], int low, int high)
{
int i;

while(low<high)
{
for(i=low; i<high; i++) /* bubble to high */
if(num[i]>num[i+1])
swap(num[i], num[i+1]);
high--;

for(i=high; i>low; i--) /* bubble to low */
if(num[i]<num[i-1])
swap(num[i], num[i-1]);
low++;
}
}
void main()
{
int num[MAX];
input(num);

printf("sort before.../n");
output(num);

sort(num, 1, MAX-1);
printf("sort after.../n");
output(num);
}

运行结果:

=======================================================

3、选择排序

测试环境:VC 6.0 (C)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX 101
void input(int num[])
{
int i;

srand((unsigned)time(NULL));
for(i=1; i<MAX; i++)
num[i]=rand()%100;
}
void output(int num[])
{
int i;

for(i=1; i<MAX; i++)
{
printf("%5d", num[i]);
if(0 == i%10)
printf("/n");
}
printf("/n");
}
void sort(int num[])
{
int i, j, k, tmp;
for(i=1; i<MAX-1; i++)
{
k=i;
for(j=i+1; j<MAX; j++)
if(num[k]>num[j])
k=j;
if(i<k)
{
tmp=num[i];
num[i]=num[k];
num[k]=tmp;
}
}
}
void main()
{
int num[MAX];
printf("sort before.../n");
input(num);
output(num);
sort(num);
printf("sort after.../n");
output(num);
}

运行结果:

=======================================================

中级排序算法

向部分已排好序数列插入新值,使整个数列最终都有序

1、直接插入排序

测试环境:VC 6.0 (C)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX 11
void input(int num[])
{
int i;

srand((unsigned)time(NULL));
for(i=1; i<MAX; i++)
num[i]=rand()%100;
}
void output(int num[])
{
int i;

for(i=1; i<MAX; i++)
{
printf("%5d", num[i]);
if(0 == i%10)
printf("/n");
}
printf("/n");
}
void sort(int num[])
{
int i, pos, tmp;
for(i=2; i<MAX; i++) /* from 2 to sorting */
{
pos=i;
tmp=num[pos];
while(pos>1 && tmp<num[pos-1])
{
num[pos]=num[pos-1];
pos--;
}
num[pos]=tmp;
}
}
void main()
{
int num[MAX];
printf("sort before.../n");
input(num);
output(num);
sort(num);
printf("sort after.../n");
output(num);
}

运行结果:

=======================================================

2、 折半插入排序

折半插入排序算法是一种稳定的排序算法,比直接插入算法明显减少了关键字之间比较的次数

因此速度比直接插入排序算法快,但记录移动的次数没有变,

所以折半插入排序算法的时间复杂度仍然为O(n^2),

测试环境:VC 6.0 (C)

#include
<stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX 11
void input(int num[])
{
int i;

srand((unsigned)time(NULL));
for(i=1; i<MAX; i++)
num[i]=rand()%100;
}
void output(int num[])
{
int i;

for(i=1; i<MAX; i++)
{
printf("%5d", num[i]);
if(0 == i%10)
printf("/n");
}
printf("/n");
}
void sort(int num[], int low, int high)
{
int i, j, mid;
int l, h;
for(i=2; i<=high; i++)
{
l=low;
h=i-1;
num[0]=num[i]; /* save */

while(l<=h)
{
mid=(l+h)/2;
if(num[0]<num[mid])
h=mid-1;
else
l=mid+1;
}

for(j=i; j>l; j--) /* move */
num[j]=num[j-1];
num[l]=num[0];
}
}
void main()
{
int num[MAX];

input(num);

printf("sort before.../n");
output(num);
sort(num, 1, MAX-1);

printf("sort after.../n");
output(num);
}

运行结果:

=======================================================

3、 2-路插入排序

2-路插入排序:是在折半插入排序的基础上再次改进,其目的是减少排序过程中记录移动的次数

测试环境:VC 6.0 (C)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX 11
int num2[MAX];
void input(int num[])
{
int i;

srand((unsigned)time(NULL));
for(i=1; i<MAX; i++)
num[i]=rand()%100;
}
void output(int num[])
{
int i;

for(i=1; i<MAX; i++)
{
printf("%5d", num[i]);
if(0 == i%10)
printf("/n");
}
printf("/n");
}
void bi_insertsort(int num[], int len)
{
int i, j, pos, head, tail;
head=tail=1;
num2[1]=num[1];
for(i=2; i<=len; i++)
{
if(num[i]>num2[1]) /* larger, save to tail */
{
for(j=tail; j>1; j--)
{
if(num[i]<num2[j])
num2[j+1]=num2[j];
else
break;
}
num2[j+1]=num[i];
tail++;
}
else /* smaller, save to head */
{
if(head==1) /* first to end, then to head... */
{
num2[len]=num[i];
head=len;
}
else
{
for(j=head; j<=len; j++)
{
if(num[i]>num2[j])
num2[j-1]=num2[j];
else
break;
}
num2[j-1]=num[i];
head--;
}
}
}

pos=1;
for(i=1; i<=len; i++) /* move back data from num2[] to num[] */
{
if(head<=len)
num[i]=num2[head++];
else if(pos<=tail)
num[i]=num2[pos++];
}
}
int main()
{
int num[MAX]; /* 1 - len is num, 0->null */
input(num);
printf("sort before.../n");
output(num);

bi_insertsort(num, MAX-1);
printf("sort after.../n");
output(num);

return 0;
}

运行结果:

=======================================================

4、合并插入排序(数组实现)

将两个有序数组A、B合并成另一个有序的大数组C

测试环境:VC 6.0 (C)

#include <stdio.h>
void input_num1(int num[], int n1)
{
int i;
for(i=1; i<=n1; i++)
num[i]=3*(i-1);
printf("/nnum 1.../n");
for(i=1; i<=n1; i++)
{
printf("%5d", num[i]);
if(0 == i%10)
printf("/n");
}
}
void input_num2(int num[], int n2)
{
int i;
for(i=1; i<=n2; i++)
num[i]=i;
printf("/nnum 2.../n");
for(i=1; i<=n2; i++)
{
printf("%5d", num[i]);
if(0 == i%10)
printf("/n");
}
}
void output_num3(int num1[], int n1, int num2[], int n2, int num3[], int n3)
{
int pos1, pos2, pos3;
pos3=pos2=pos1=1;

while(pos1<=n1 && pos2<=n2)
{
if(num1[pos1]<num2[pos2])
num3[pos3++]=num1[pos1++];
else
num3[pos3++]=num2[pos2++];
}
while(pos1<=n1)
num3[pos3++]=num1[pos1++];
while(pos2<=n2)
num3[pos3++]=num2[pos2++];
printf("/n/nnum 3.../n");
for(pos3=1; pos3<=n3; pos3++)
{
printf("%5d", num3[pos3]);
if(0 == pos3%10)
printf("/n");
}
}
void main()
{
int num1[11];
int num2[21];
int num3[31];

input_num1(num1, 10);
input_num2(num2, 20);
output_num3(num1, 10, num2, 20, num3, 30);
}

运行结果:

=======================================================

5、合并插入排序(链表实现)

将两个有序链表A、B合并成另一个有序的大链表C(链表单元来自A和B)

测试环境:VC 6.0 (C)

#include <stdio.h>
#include <malloc.h>
struct _link
{
int data;
struct _link *next;
};
typedef struct _link link, *plink;
plink init_link()
{
plink p;

p=(plink)malloc(sizeof(link));
if(!p) /* if(p == NULL) */
{
printf("Error. malloc fail.../n");
return NULL;
}
p->data=-1;
p->next=NULL;
return p;
}
plink input_num1(plink plk, int n1)
{
int i, count;
plink p, s;

p=plk;

for(i=1; i<=n1; i++)
{
s=(plink)malloc(sizeof(link));
if(!s) /* if(p == NULL) */
{
printf("Error. malloc fail.../n");
return NULL;
}
s->data=3*(i-1);
s->next=NULL;
p->next=s;
p=p->next;
}

count=0;
s=plk->next;
while(s)
{
count++;
printf("%5d", s->data);
s=s->next;
if(0 == count%10)
printf("/n");
}
printf("/n");
return plk;
}
plink input_num2(plink plk, int n2)
{
int i, count;
plink p, s;

p=plk;

for(i=1; i<=n2; i++)
{
s=(plink)malloc(sizeof(link));
if(!s) /* if(p == NULL) */
{
printf("Error. malloc fail.../n");
return NULL;
}
s->data=i;
s->next=NULL;
p->next=s;
p=p->next;
}

count=0;
s=plk->next;
while(s)
{
count++;
printf("%5d", s->data);
s=s->next;
if(0 == count%10)
printf("/n");
}
printf("/n");
return plk;
}
void output_num3(plink plk1, plink plk2, plink plk3)
{
int count;
plink p1, p2, p3;
p1=plk1->next;
p2=plk2->next;
p3=plk3;
while(p1 && p2)
{
if(p1->data < p2->data)
{
p3->next=p1;
p3=p3->next;
p1=p1->next;
}
else
{
p3->next=p2;
p3=p3->next;
p2=p2->next;
}
}
p3->next = p1 ? p1 : p2; /* NOTE: directly link to not NULL address, OK */
count=0;
p3=plk3->next;
while(p3)
{
count++;
printf("%5d", p3->data);
p3=p3->next;
if(0 == count%10)
printf("/n");
}
printf("/n");
}
void main()
{
plink plk1, plk2, plk3;

plk1=init_link();
plk2=init_link();
plk3=init_link();
printf("num 1.../n");
plk1=input_num1(plk1, 10);
printf("num 2.../n");
plk2=input_num2(plk2, 20);

printf("num 3.../n");
output_num3(plk1, plk2, plk3);
}

运行结果:

=======================================================

高级排序算法

1、 快速排序

测试环境:VC 6.0 (C)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX 21
void input(int num[])
{
int i;

srand(time(NULL));
for(i=0; i<MAX; i++)
num[i]=rand()%100;
}
void output(int num[])
{
int i;

for(i=1; i<MAX; i++)
{
printf("%5d", num[i]);
if(0 == i%10)
printf("/n");
}
}
void sort(int num[], int low, int high)
{
int l, h;
l=low;
h=high;
if(low < high)
{
num[0]=num[l]; /* num[0] save pivot */

while(l<h)
{
while(l<h && num[h]>=num[0]) h--;
num[l]=num[h];
while(l<h && num[l]<=num[0]) l++;
num[h]=num[l];
}
num[l]=num[0];
sort(num, low, l-1);
sort(num, l+1, high);
}
}
void main()
{
int num[MAX];
input(num);
printf("/nsort before.../n");
output(num);
sort(num, 1, MAX-1);
printf("/nsort before.../n");
output(num);
}

运行结果:

=======================================================

2、 希尔排序

说明:本示例仅测试10或11个数的3趟希尔排序

由于希尔排序的增量step至今尚无精准的数学论证,无法给出科学、高效的序列函数

据严蔚敏的《数据结构(C语言版)》介绍说:希尔排序的分析是一个复杂的问题,因为它的时间是所取“增量”序列的函数,这涉及一些数学上尚未解决的难题(P272).

因此,本示例仅是实际问题实际解决的一个特例。

本算法基本思想仍是上述直接排序算法的改进,仅仅步长由1变成了step而已

如果大家想需要增添或减少数组元素个数,请一并修改input()函数中的step等趟数序列

如果大家对希尔排序算法有更好的改进,或有较好步长的函数和通用模板,希望能拿出来共同学习交流分享,谢谢!

测试环境:VC 6.0 (C)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX 11 /* num[] */
#define STEP 4 /* jump[] */
void input(int num[], int jump[])
{
int i;
srand((unsigned)time(NULL));
for(i=1; i<MAX; i++)
num[i]=rand()%100;
for(i=1; i<STEP; i++)
jump[i]=7-2*i; /* 1->5; 2->3; 3->1 */
}
void output(int num[])
{
int i;

for(i=1; i<MAX; i++)
{
printf("%5d", num[i]);
if(0 == i%10)
printf("/n");
}
}
void sort(int num[], int jump[])
{
int i, j, pos, step;
for(i=1; i<STEP; i++)
{
step=jump[i];
for(j=1+step; j<MAX; j++)
{
pos=j;
num[0]=num[pos]; /* save num[j] where (i+step)<j<MAX */
while(num[0]<num[pos-step])
{
num[pos]=num[pos-step];
pos=pos-step; /* shell: jump step */
}
num[pos]=num[0];
}
}
}
void main()
{
int num[MAX];
int jump[STEP];

input(num, jump);
printf("sort before.../n");
output(num);
sort(num, jump);

printf("sort after.../n");
output(num);
}

运行结果:

=======================================================

2、 希尔排序(网摘)

在学习希尔排序算法时,看到网上有如下一段希尔排序代码,也可以直接运行

但看代码来真得很费解,感觉变量定义不够直观,算法设计也不太简洁

因此,在最大程度保留源代码时,仅对变量名和算法逻辑简单修改

力争做到变量名清晰,逻辑顺畅,达到不用注释读者也能看明白

希望对大家学习有点帮助

测试环境:VC 6.0 (C)

摘录原代码:http://apps.hi.baidu.com/share/detail/5669244(百度空间)

#include <iostream.h>
void ShellSort(int* pData,int Count)
{
int step[4];
step[0] = 9;
step[1] = 5;
step[2] = 3;
step[3] = 1;
int iTemp;
int k,s,w;
for(int i=0;i<4;i++)
{
k = step[i];
s = -k;
for(int j=k;j<Count;j++)
{
iTemp = pData[j];
w = j-k;//求上step个元素的下标
if(s ==0)
{
s = -k;
s++;
pData[s] = iTemp;
}
while((iTemp<pData[w]) && (w>=0) && (w<=Count))
{
pData[w+k] = pData[w];
w = w-k;
}
pData[w+k] = iTemp;
}
}
}
void main()
{
int data[] = {10,9,8,7,6,5,4,3,2,1,-10,-1};
ShellSort(data,12);
for (int i=0;i<12;i++)
cout<<data[i]<<" ";
cout<<"/n";
}

修改后代码:

#include <iostream.h>
void ShellSort(int* pData,int Count)
{
int iTemp;
int steplen, pos;
int step[4];
step[0] = 9;
step[1] = 5;
step[2] = 3;
step[3] = 1;

for(int i=0;i<4;i++)
{
steplen = step[i];
for(int j=0+steplen; j<Count; j++)
{
iTemp = pData[j];
pos=j;
while(iTemp<pData[pos-steplen] && pos>0)
{
pData[pos] = pData[pos-steplen];
pos=pos-steplen;
}
pData[pos] = iTemp;
}
}
}
void main()
{
int data[] = {10,9,8,7,6,5,4,3,2,1,-10,-1};
cout<<endl<<"sort before..."<<endl;
for (int i=0;i<12;i++)
cout<<data[i]<<" ";
cout<<endl;
ShellSort(data,12);
cout<<endl<<"sort before..."<<endl;
for (i=0;i<12;i++)
cout<<data[i]<<" ";
cout<<endl;
}

运行结果:

=======================================================

3、 堆排序

测试环境:VC 6.0 (C)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX 11
void input(int num[])
{
int i;

srand((unsigned)time(NULL));
for(i=1; i<MAX; i++)
num[i]=rand()%100;
}
void output(int num[])
{
int i;

for(i=1; i<MAX; i++)
{
printf("%5d", num[i]);
if(0 == i%10)
printf("/n");
}
printf("/n");
}
void heapadjust(int num[], int s, int len) /* build large heap */
{
int j;
num[0]=num[s]; /* save temp data */

for(j=2*s; j<=len; j*=2)
{
if(j<len && num[j]<num[j+1]) /* num[j+1] is limited by j<len beyond the border */
j++;
if(num[0]>num[j])
break;
num[s]=num[j];
s=j;
}
num[s]=num[0];
}
void heapsort(int num[], int len)
{
int i, tmp;
for(i=len/2; i>0; i--) /* build heap */
heapadjust(num, i, len);

for(i=len; i>1; i--) /* sort heap */
{
tmp=num[1]; /* change largest data to end */
num[1]=num[i];
num[i]=tmp;
heapadjust(num, 1, i-1); /* rebuild large heap for (i-1) data */
}
}
int main()
{
int num[MAX]; /* 1 - len is num, 0->null */
input(num);
printf("sort before.../n");
output(num);

heapsort(num, MAX-1);
printf("sort after.../n");
output(num);

return 0;
}

运行结果:

=======================================================

4、 归并排序

测试环境:VC 6.0 (C)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX 11
int num2[MAX]; /* copy array */
void input(int num[])
{
int i;

srand((unsigned)time(NULL));
for(i=1; i<MAX; i++)
num[i]=rand()%100;
}
void output(int num[])
{
int i;

for(i=1; i<MAX; i++)
{
printf("%5d", num[i]);
if(0 == i%10)
printf("/n");
}
printf("/n");
}
void merge(int num[], int low, int mid, int high)
{
int l, h, i, j;
l=low;
h=high;
for(i=mid+1, j=low; low<=mid && i<=high; j++)
{
if(num[low]<num[i])
num2[j]=num[low++];
else
num2[j]=num[i++];
}
if(low<=mid)
for(; j<=high; j++, low++)
num2[j]=num[low];
if(i<=high)
for(; j<=high; j++, i++)
num2[j]=num[i];
for(i=l; i<=h; i++)
num[i]=num2[i];
}
void mergesort(int num[], int low, int high)
{
int mid;
if(low==high)
num2[low]=num[low];
else
{
mid=(low+high)/2;
mergesort(num, low, mid); /* to low */
mergesort(num, mid+1, high); /* to high */
merge(num, low, mid, high); /* recursion */
}
}
int main()
{
int num[MAX]; /* 1 - len is num, 0->null */
input(num);
printf("sort before.../n");
output(num);

mergesort(num, 1, MAX-1);
printf("sort after.../n");
output(num);

return 0;
}

运行结果:

=======================================================

排序算法的知识扩展(网摘)

内部排序算法的比较和实现
引用网址:http://www.cppblog.com/feosun/archive/2008/10/12/63831.html(直接摘录,尚未测试)

参考网址:http://sjjp.tjuci.edu.cn/sjjg/DataStructure/DS/web/paixu/paixu8.1.1.1.htm(数据结构)

排序是数据处理中经常使用的一种重要运算,在计算机及其应用系统中,花费在排序上的时间在系统运行时间中占有很大比重;并且排序本身对推动算法分析的发展 也起很大作用。目前已有上百种排序方法,但尚未有一个最理想的尽如人意的方法,本文介绍常用的如下排序方法的C/C++实现,并对它们进行分析和比较。

选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,而冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法。

首先,排序算法的稳定性大家应该都知道,通俗地讲就是能保证排序前2个相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同。在简单形式化一下,如果Ai = Aj, Ai原来在位置前,排序后Ai还是要在Aj位置前。

其次,说一下稳定性的好处。排序算法如果是稳定的,那么从一个键上排序,然后再从另一个键上排序,第一个键排序的结果可以为第二个键排序所用。基数排序就 是这样,先按低位排序,逐次按高位排序,低位相同的元素其顺序再高位也相同时是不会改变的。另外,如果排序算法稳定,对基于比较的排序算法而言,元素交换 的次数可能会少一些(个人感觉,没有证实)。

回到主题,现在分析一下常见的排序算法的稳定性,每个都给出简单的理由。

(1)冒泡排序
        冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,我想你是不会再无 聊地把他们俩交换一下的;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并没有改 变,所以冒泡排序是一种稳定排序算法。

(2)选择排序
      选择排序是给每个位置选择当前元素最小的,比如给第一个位置选择最小的,在剩余元素里面给第二个元素选择第二小的,依次类推,直到第n-1个元素,第n个 元素不用选择了,因为只剩下它一个最大的元素了。那么,在一趟选择,如果当前元素比一个元素小,而该小的元素又出现在一个和当前元素相等的元素后面,那么 交换后稳定性就被破坏了。比较拗口,举个例子,序列5 8 5 2 9,我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。

(3)插入排序
     插入排序是在一个已经有序的小序列的基础上,一次插入一个元素。当然,刚开始这个有序的小序列只有1个元素,就是第一个元素。比较是从有序序列的末尾开 始,也就是想要插入的元素和已经有序的最大者开始比起,如果比它大则直接插入在其后面,否则一直往前找直到找到它该插入的位置。如果碰见一个和插入元素相 等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳 定的。

(4)快速排序
    快速排序有两个方向,左边的i下标一直往右走,当a[i] <= a[center_index],其中center_index是中枢元素的数组下标,一般取为数组第0个元素。而右边的j下标一直往左走,当a[j] > a[center_index]。如果i和j都走不动了,i <= j, 交换a[i]和a[j],重复上面的过程,直到i>j。交换a[j]和a[center_index],完成一趟快速排序。在中枢元素和a[j]交 换的时候,很有可能把前面的元素的稳定性打乱,比如序列为 5 3 3 4 3 8 9 10 11,现在中枢元素5和3(第5个元素,下标从1开始计)交换就会把元素3的稳定性打乱,所以快速排序是一个不稳定的排序算法,不稳定发生在中枢元素和 a[j] 交换的时刻。

(5)归并排序
    归并排序是把序列递归地分成短序列,递归出口是短序列只有1个元素(认为直接有序)或者2个序列(1次比较和交换),然后把各个有序的段序列合并成一个有 序的长序列,不断合并直到原序列全部排好序。可以发现,在1个或2个元素时,1个元素不会交换,2个元素如果大小相等也没有人故意交换,这不会破坏稳定 性。那么,在短的有序序列合并的过程中,稳定是是否受到破坏?没有,合并过程中我们可以保证如果两个当前元素相等时,我们把处在前面的序列的元素保存在结 果序列的前面,这样就保证了稳定性。所以,归并排序也是稳定的排序算法。

(6)基数排序
   基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优 先级排序,最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,分别收集,所以其是稳定的排序算法。

(7)希尔排序(shell)
    希尔排序是按照不同步长对元素进行插入排序,当刚开始元素很无序的时候,步长最大,所以插入排序的元素个数很少,速度很快;当元素基本有序了,步长很小, 插入排序对于有序的序列效率很高。所以,希尔排序的时间复杂度会比o(n^2)好一些。由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元 素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。

(8)堆排序
   我们知道堆的结构是节点i的孩子为2*i和2*i+1节点,大顶堆要求父节点大于等于其2个子节点,小顶堆要求父节点小于等于其2个子节点。在一个长为n 的序列,堆排序的过程是从第n/2开始和其子节点共3个值选择最大(大顶堆)或者最小(小顶堆),这3个元素之间的选择当然不会破坏稳定性。但当为n /2-1, n/2-2, ...1这些个父节点选择元素时,就会破坏稳定性。有可能第n/2个父节点交换把后面一个元素交换过去了,而第n/2-1个父节点把后面一个相同的元素没 有交换,那么这2个相同的元素之间的稳定性就被破坏了。所以,堆排序不是稳定的排序算法。

/*
   冒泡排序  插入排序 二路插入排序 希尔排序   快速排序 选择排序 归并排序  堆排序算法的
   C/C++实现。
  
   作者:feosun

日期:2008年10月12日

参考资料:数据结构(C语言版) 清华大学出版社

*/

#include <iostream>
using namespace std;

//交换两个数的值
void swap(int &a,int &b)
{
    int tmp;
    tmp=a;
    a=b;
    b=tmp;
}

//屏幕输出数组
void display(int array[],int len)
{
    cout<<"the result is:"<<endl;
    for (int i = 0 ;i < len;i++ )
    {
        cout<<array[i]<<"  ";
    }
    cout<<endl;
}

/*
冒泡排序

算法思想:将被排序的记录数组R[1..n]垂直排列,每个记录R[i]看作是重量为R[i].key的气泡。
          根据轻气泡不能在重气泡之下的原则,从下往上扫描数组 R:凡扫描到违反本原则的
          轻气泡,就使其向上"飘浮"。如此反复进行,直到最后任何两个气泡都是轻者在上,
          重者在下为止。

时间复杂度 o(n^2)

空间复杂度 o(1)

比较次数 n(n+1)/2
*/
void bubble_sort(int array[],int len)
{
   
    for (int i = len-1 ;i >= 0;i-- )
    {
        for(int j = 0;j < i;j++)
            if(array[j] > array[j+1])
                swap(array[j],array[j+1]);
    }
}

/*
直接插入排序

算法思想:把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元
          素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,将它
          插入到有序表中的适当位置,使之成为新的有序表,重复n-1次可完成排序过程。

时间复杂度 o(n^2)

空间复杂度 o(1)

比较次数 n(n+1)/2
*/
void insert_sort(int array[],int len)
{
    int tmp,i,j;

for(i = 1;i < len;i++)
    {
        if (array[i] < array[i-1])
        {
            tmp = array[i];
            array[i] = array[i-1];

//插入到相应位置
            for (j = i-2;j >= 0;j--)
            {
                //往后移
                    if (array[j] > tmp )
                        array[j+1] = array[j];
                    else
                    {
                        array[j+1] = tmp;
                        break;
                    }
            }
            if(j == -1)
            array[j+1] = tmp;
        }
    }
}

/*
2-路插入排序

算法思想:增加一个辅助空间d,把r[1]赋值给d[1],并将d[1]看成是排好序后处于中间
          位置的记录。然后从r[2]开始依次插入到d[1]之前或之后的有序序列中。

时间复杂度 o(n^2)

空间复杂度 o(1)

比较次数 n(n+1)/2
*/

void bi_insert_sort(int array[],int len)
{
    int* arr_d = (int *)malloc(sizeof(int) * len);

arr_d[0] = array[0];
    int head = 0,tail = 0;
    for (int i = 1;i < len; i++ )
    {
        if (array[i] > arr_d[0])
        {
            int j;
            for ( j= tail;j>0;j--)
            {
                if (array[i] < arr_d[j])
                    arr_d[j+1] = arr_d[j];
                else
                    break;
            }
            arr_d[j+1] = array[i];
            tail += 1;
        }

else
        {
            if (head ==0)
            {
                arr_d[len-1] = array[i];
                head =len-1;
            }
            else
            {
                int j;
                for (j = head;j <= len-1;j++)
                {
                    if (array[i] > arr_d[j])
                        arr_d[j-1] = arr_d[j];
                    else
                        break;
                }
                arr_d[j-1] = array[i];
                head -= 1;
            }
        }

}

for (int i = 0;i < len; i++)
    {
        int pos = (i + head );
        if(pos >= len) pos -= len;
        array[i] = arr_d[pos];
    }

free(arr_d);
}

/*
希尔排序

算法思想:先将整个待排序记录分割成若干子序列分别进行直接插入排
          序,待整个序列中的记录基本有序时,再对全体记录进行一
          次直接插入排序

时间复杂度 o(n^2)

空间复杂度 o(1)

比较次数 ?
*/

void shell_insert(int array[],int d,int len)
{
    int tmp,j;

for (int i = d;i < len;i++)
    {
        if(array[i] < array[i-d])
        {
            tmp = array[i];
            j = i - d;
            do
            {
                array[j+d] = array[j];
                j = j - d;
            } while (j >= 0 && tmp < array[j]);

array[j+d] = tmp;
        }
    }
}
void shell_sort(int array[],int len)
{
    int inc = len;

do
    {
        inc = inc/2;
        shell_insert(array,inc,len);
    } while (inc > 1);
}

/*
快速排序

算法思想:将原问题分解为若干个规模更小但结构与原问题相似的子问题。递
          归地解这些子问题,然后将这些子问题的解组合成为原问题的解。

时间复杂度 o(nlogn)

空间复杂度 o(logn)

比较次数  ?
*/

int partition(int array[],int low,int high)
{
    int  pivotkey = array[low];

while (low < high)
    {
        while(low < high && array[high] >= pivotkey)
            --high;
        swap(array[low],array[high]);

while(low < high && array[low] <= pivotkey)
            ++low;
        swap(array[low],array[high]);
    }

array[low] = pivotkey;

return low;
}

void quick_sort(int array[],int low,int high)
{
    if (low < high)
    {
        int pivotloc = partition(array,low,high);
        quick_sort(array,low,pivotloc-1);
        quick_sort(array,pivotloc+1,high);
    }
}

/*
直接选择排序

算法思想:每一趟在n-i+1个记录中选取关键字最小的记录作为有序序列中的第i个记录

时间复杂度 o(n^2)

空间复杂度 o(1) ?

比较次数  n(n+1)/2
*/

int SelectMinKey(int array[],int iPos,int len)
{
    int ret = 0;

for (int i = iPos; i < len; i++)
    {
        if (array[ret] > array[i])
        {
            ret = i;
        }
    }
    return ret;
}

void select_sort(int array[],int len)
{
    for (int i = 0; i < len; i++)
    {
        int j = SelectMinKey(array,i,len);
        if (i != j)
        {
            swap(array[i],array[j]);
        }
    }
}

/*
归并排序

算法思想:设两个有序的子文件(相当于输入堆)放在同一向量中相邻的位置上:R[low..m],R[m+1..high],先
          将它们合并到一个局部的暂存向量R1(相当于输出堆)中,待合并完成后将R1复制回R[low..high]中。

时间复杂度 o(nlogn)

空间复杂度 o(n)

比较次数  ?
*/

void merge(int array[],int i,int m, int n)
{
    int j,k;

int iStart = i, iEnd = n;

int arrayDest[256];

for ( j = m + 1,k = i; i <= m && j <= n; ++k)
    {
        if (array[i] < array[j])
            arrayDest[k] = array[i++];
        else
            arrayDest[k] = array[j++];
    }

if (i <= m)
        for (;k <= n; k++,i++)
            arrayDest[k] = array[i];
    if(j <= n)
        for (;k <= n; k++,j++)
            arrayDest[k] = array[j];

for(j = iStart; j <= iEnd; j++)
        array[j] = arrayDest[j];
}

void merge_sort(int array[],int s,int t)
{
    int m;

if (s < t)
    {
        m = (s + t )/2;
        merge_sort(array,s,m);
        merge_sort(array,m+1,t);
        merge(array,s,m,t);
    }
}

/*
堆排序

算法思想:堆排序(Heap Sort)是指利用堆(heaps)这种数据结构来构造的一种排序算法。
          堆是一个近似完全二叉树结构,并同时满足堆属性:即子节点的键值或索引总是
          小于(或者大于)它的父节点。
时间复杂度 o(nlogn)
空间复杂度 o(1)
比较次数  较多
*/

void heap_adjust(int array[],int i,int len)
{
    int rc = array[i];

for(int j = 2 * i; j <len; j *= 2)
    {
        if(j < len && array[j] < array[j+1]) j++;
        if(rc >= array[j]) break;

array[i] = array[j]; i = j;
    }
    array[i] = rc;
}

void heap_sort(int array[],int len)
{
    int i;
    for(i = (len-1)/2; i >= 0; i--)
        heap_adjust(array,i,len);
    for(  i = (len-1); i > 0; i--)
    {
        swap(array[0],array[i]);   //弹出最大值,重新对i-1个元素建堆
        heap_adjust(array,0,i-1);
    }
}

int main() {

int array[] = {45,56,76,234,1,34,23,2,3,55,88,100};

int len = sizeof(array)/sizeof(int);

//bubble_sort(array,len);           //冒泡排序

/*insert_sort(array,len);*/         //插入排序

/*bi_insert_sort(array,len);*/      //二路插入排序
   
    /*shell_sort(array,len);*/          //希尔排序

/*quick_sort(array,0,len-1);*/      //快速排序
   
    /*select_sort(array,len);*/         //选择排序

/*merge_sort(array,0,len-1);*/      //归并排序
   
    heap_sort(array,len);               //堆排序
    display(array,len);

return 0;
}

参考推荐:

学习算法之路

各种基本算法实现小结(一)—— 链 表

各种基本算法实现小结(二)—— 堆 栈

各种基本算法实现小结(三)—— 树与二叉树

各种基本算法实现小结(四)—— 图及其遍历

各种基本算法实现小结(五)—— 排序算法

各种基本算法实现小结(六)—— 查找算法

各种基本算法实现小结(七)—— 常用算法

12个有趣的C语言面试题

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!希望你也加入到我们人工智能的队伍中来!http://www.captainbed.net

原文地址:https://www.cnblogs.com/siwnckhhw/p/10142948.html

时间: 2024-10-16 00:02:52

各种基本算法实现小结(五)—— 排序算法的相关文章

基本算法研究1-冒泡排序算法测试

基本算法研究1-冒泡排序算法测试 1.经典冒泡排序法基本原理 先看一个动态图,感觉比较形象: 冒泡排序(Bubble Sort)是一种简单的排序算法.默认是从小到大排序,即把最大的数据排在最后,相当于每次把最大数据像气泡一样浮到水面一样.它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地进行直到没有再需要交换. 基本步骤: 1.比较相邻的元素.如果第一个比第二个大,就交换他们两个.        2.对每一对相邻元素作同样的工作,从开始第一对

【JavaScript】【算法】JavaScript版排序算法

JavaScript版排序算法:冒泡排序.快速排序.插入排序.希尔排序(小数据时,希尔排序会比快排快哦) 1 //排序算法 2 window.onload = function(){ 3 var array = [0,1,2,44,4, 4 324,5,65,6,6, 5 34,4,5,6,2, 6 43,5,6,62,43, 7 5,1,4,51,56, 8 76,7,7,2,1, 9 45,4,6,7,8]; 10 //var array = [4,2,5,1,0,3]; 11 array

链表插入和删除,判断链表是否为空,求链表长度算法的,链表排序算法演示——C语言描述

关于数据结构等的学习,以及学习算法的感想感悟,听了郝斌老师的数据结构课程,其中他也提到了学习数据结构的或者算法的一些个人见解,我觉的很好,对我的帮助也是很大,算法本就是令人头疼的问题,因为自己并没有学习过算法的系统性的课程,现在还是处于不断摸索的阶段,好多算法题目根本就没有什么思路,导致自己对好多题目都很是头疼,就算是自己做过的一些算法的题目,再次遇到也还是不一定会做出来,他给出的建议就是,看懂别人的程序,然后自己去敲,一定会出错,然后调试,有错误接着调试,一直到没有错误为止,并且要时常的去复习

算法导论专题一--排序算法

排序算法作为许多程序的中间步骤,是计算机科学中的一个基本操作. 一.问题描述 排序算法输入的是n个数的一个序列<a1,a2…..an>,输出为输入的一个排列<a1’,…..an’>,满足a1’<a2’<….<an’ 简言之就是输入一个序列,输出的是这个数组元素从小到大排列的另一序列. 二.方法思想综述 从算法导论这本书上根据算法的复杂度可以将排序算法分为三种,,.,这两种方法都需要数据间的比较,而不需要. 其中有三种为选择,冒泡,插入. 选择排序:最直观,简单但是

算法导论专题一--排序算法(2)

上节分析了O(n^2)的算法,这节就分析O(nlgn)的算法-归并,快速和堆排序. 一:综述 O(nlgn) 的算法可以分为两大类,两者所用的技术差别较大.归并和快速排序采用的是分治策略,这两者相当于一个对称的过程,一个是自顶向上合并子问题,另一个则自上向下分解子问题.而堆排序利用堆这一数据结构元素间的特殊关系来排序一个序列,另外采用二叉树的方式组织数据使其效率大大提高. 二:分治策略排序算法 1.为什么使用分治? 在上节算法的分析中,不管是冒泡.选择还是插入都不适用于大规模的数据,因为数据一大

冒泡排序算法和简单选择排序算法的js实现

之前已经介绍过冒泡排序算法和简单选择排序算法和原理,现在有Js实现. 冒泡排序算法 let dat=[5, 8, 10, 3, 2, 18, 17, 9]; function bubbleSort(data) { for(let i=0;i<data.length-1;i++){ for(let j=0;j<data.length-1-i;j++){ if(data[j]>data[j+1]){ [data[j],data[j+1]]=[data[j+1],data[j]]; } } }

python数据结构与算法第八天【排序算法】

1.排序算法的稳定性 稳定排序算法会让原本有相同键值的记录维持相对次序 例如:对以下元组按照元组的第一个元素升序排列,元组如下: (4,1) (3,1) (3,7) (5,6) 若要满足条件,则可能的排序有: 情况一: (3,1) (3,7) (4,1) (5,6) 情况二: (3,7) (3,1) (4,1) (5,6) 虽然情况一和情况二都是满足条件的,但是情况二在满足条件下打破了原本无需改变的顺序 原文地址:https://www.cnblogs.com/liuzhiqaingxyz/p/

数据结构与算法系列十(排序算法概述)

1.引子 1.1.为什么要学习数据结构与算法? 有人说,数据结构与算法,计算机网络,与操作系统都一样,脱离日常开发,除了面试这辈子可能都用不到呀! 有人说,我是做业务开发的,只要熟练API,熟练框架,熟练各种中间件,写的代码不也能“飞”起来吗? 于是问题来了:为什么还要学习数据结构与算法呢? #理由一: 面试的时候,千万不要被数据结构与算法拖了后腿 #理由二: 你真的愿意做一辈子CRUD Boy吗 #理由三: 不想写出开源框架,中间件的工程师,不是好厨子 1.2.如何系统化学习数据结构与算法?

Dijkstra算法以及各种海量数据排序算法

一.Dijkstra最短路径算法 是从一个顶点到其余各顶点的最短路径算法,解决的是有向图中最短路径问题.迪杰斯特拉算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止. 实现一 // // Dijkstra // ACM // Find the number of minimal path // // Created by Rachel on 18-2-12. // Copyright (c) 2014年 ZJU. All rights reserved. // #include <io

数据结构与算法系列研究九——排序算法的一些探讨

四种排序 一.实验内容     输入20个整数,分别用希尔排序.快速排序.堆排序和归并排序实现由小到大排序并输出排序结果.二.关键数据结构与核心算法   关键数据结构:由于是排序为了简单起见,选用线性表中的数组作为存储结构.   核心算法:   1.希尔排序    希尔排序的核心还是直接插入法,但是插入的位置有所讲究.要把数组分为许多段,每一段的长度除了最后的有可能不同之外,其他的都相同.该段的长度即为增量,在最后一次必须为一,此时程序变成了直接插入.每次进行隔段插入,不断地调整是的数组变得隔段