啊哈,算法自学记——2nd
快速排序:
排序:6-1-2-7-9-3-4-5-10-8
冒泡排序:时间复杂度达到了 O(N2)
快速排序之所以比较快,是因为相比冒泡排序,每次交换是跳跃式的。
每次排序的时候设置一个基准点,将小于等于基准点的数全部放到基准点的左边,将大于等于基准点的数全部放到基准点的右边。
这样在每次交换的时候就不会像冒泡排序一样只能在相邻的数之间进行交换,交换的距离就大得多了。
因此总的比较和交换次数就少了,速度自然就提高了。当然在最坏的情况下,仍可能是相邻的两个数进行了交换。
因此快速排序的最差时间复杂度和冒泡排序是一样的,都是 O(N2),它的平均时间复杂度为 O (NlogN)。
#include <stdio.h>
//快速排序
int book[101],n;
void quicksort(int left,int right)
{
int i,j,t,temp;
if(left>right)
return;
temp=book[left];//temp存储的就是基准数,此时的基准数为第一个数字
i=left;//左边第一个
j=right;//右边第一个
while (i!=j)//两边不相遇
{
while (book[j]>=temp && i<j)//从右往左找比基准数temp小的数(大于基准数就j--,继续往左找),并且不能小于左边的i
{
j--;
}//找到比基准数小的数了
while (book[i]<=temp && i<j)//右边找完,左边找,从左往右找比基准数大的数,比基准数小就i++,接着往右找
{
i++;
}
//两边找完一次,互换,前提是i与j没有相遇
if(i<j)
{
t=book[i];
book[i]=book[j];
book[j]=t;
}
}
//完成一次位置互换,分为两个序列排序
book[left]=book[i];//left为形参,下面调用是0,也即数组第一个数字现在变为book[i](此时的book[i]是i j 相遇时的i位置的值)
book[i]=temp;//temp是基准值,这两步其实是把这一轮的基准值与这一轮找完时i所在位置的值互换,也就是把基准值调到中间,把前面序列新一轮的基准值放到第一个
quicksort(left,i-1);//继续处理左边的
quicksort(i+1,right);//继续处理右边的,这是个递归的过程
}
int main(int argc, char const *argv[])
{
int i,j,t;
printf("Input the num of data:\r\n");
scanf("%d",&n);
for (i = 0; i < n; i++)
{
scanf("%d",&book[i]);//保存输入的数据
}
quicksort(0,n);//快速排序调用
//打印数据
for (i = 0; i <= n; i++)
{
printf("%d--",book[i]);
}
return 0;
}
快速排序过程:
设第一个数为基准数
先 从右往左 找一个比基准数小的,标记此时的位置,停下来
然后在 从左往右 找一个比基准数大的,然后这两个数位置互换
如果从两边开始的位置没有相遇,就继续找,再互换,直至相遇
把相遇的位置的数与基准值互换;
这时,基准值左边的数就都比基准值小,右边的都比基准值大
然后再用相同的方法分别处理左右两边的序列就OK了
去重并排序
小哼的学校要建立一个图书角, 老师派小哼去找一些同学做调查,看看同学们都喜欢读哪些书。小哼让每个同学写出一个自己最想读的书的 ISBN 号,当然有一些好书会有很多同学都喜欢,这样就会收集到很多重复的 ISBN 号。小哼需要去掉其中重复的 ISBN 号,即每个 ISBN 号只保留一个,也就说同样的书只买一本(学校真是够抠门的)。然后再把这些 ISBN 号从小到大排序,小哼将按照排序好的 ISBN 号去书店买书。请你协助小哼完成“去重”与“排序”的工作
简单来说就是排序的同时要去重:
第一种方法:先将这 n 个图书的 ISBN 号去重,再进行从小到大排序并输出;
第二种方法:先从小到大排序,输出的时候再去重。这两种方法都可以。
第一种:(用桶排序的方法去重,虽然浪费空间,但是能去重也算是一个优点)
#include <stdio.h>
int main(int argc, char const *argv[])
{
int book[1001],t;
int n;
for (int i = 0; i <= 1000; i++)
{
book[i]=0;//清空数组
}
printf("Input the num of data:\r\n");
scanf("%d",&n);
printf("Input date:\r\n");
for (int i = 0; i < n; i++)
{
scanf("%d",&t);
book[t]=1;//标记数字t出现过,去重的话就只标记出现过就行,不去冲就得标记这货出现几次,++
}
//打印
for (int i = 0; i <= 1000; i++)
{
if (book[i]==1)//如果这个数出现过
{
printf("%d--",i);
}
}
return 0;
}
第二种:
先排序,输出的时候比较下和它下一位是否一样
#include <stdio.h>
int main(int argc, char const *argv[])
{
int book[101],t;
int n;
printf("Input the num of data:\r\n");
scanf("%d",&n);
printf("Input date:\r\n");
for (int i = 0; i < n; i++)
{
scanf("%d",&book[i]);//把输入的数据保存到数组中
}
for (int i = 0; i < n-1; i++)//循环n-1次
{
for (int j = 0; j < n-i; j++)
{
if (book[j]<book[j+1])
{
t=book[j];
book[j]=book[j+1];
book[j+1]=t;
}
}
}
//去重
printf("%d--",book[0]);
for (int i = 1; i < n; i++)
{
if (book[i]!=book[i-1])
{
printf("%d--",book[i]);
}
}
return 0;
}
就是冒泡排序加个去重操作
这种方法的时间复杂度由两部分组成, 一部分是冒泡排序的时间复杂度,是 N (N2),另一部分是读入和输出,都是 O(N),因此整个算法的时间复杂度是 O(2N+N 2)。相对于 N2 来说, 2N 可以忽略(我们通常忽略低阶),最终该方法的时间复杂度是 O(N2)。
接下来我们还需要看下数据范围。每个图书 ISBN 号都是 1~1000 之间的整数,并且参加调查的同学人数不超过 100,即 n≤100。之前已经说过,在粗略计算时间复杂度的时候,我们通常认为计算机每秒钟大约运行 10 亿次(当然实际情况要更快)。因此以上两种方法都可以在 1 秒钟内计算出解。如果题目中图书的 ISBN 号范围不是在1~1000之间,而是在(±)2147483647 之间的话,那么第一种方法就不可行了,因为你无法申请出这么大的数组来标记每一个 ISBN 号是否出现过。另外如果 n 的范围不是小于等于 100,而是小于等于 10 万,那么第二种方法的排序部分也不能使用冒泡排序。因为题目要求的时间限制是 1 秒,使用冒泡排序对 10 万个数进行排序,计算机要运行 100 亿次,需要 10 秒钟,因此要替换为快速排序,快速排序只需要 100000×log2100000≈100000×17≈170 万次,这还不到0.0017 秒。是不是很神奇?同样的问题使用不同的算法竟然有如此之大的时间差距,这就是算法的魅力!
三种排序算法的时间复杂度。
桶排序是最快的,它的时间复杂度是O(N+M);冒泡排序是 O(N 2);快速排序是 O(NlogN)。