算法导论2:几个习题 2016.1.2
一、在归并排序中对小数组采用插入排序(放在上一篇里了);
二、冒泡排序
冒泡排序效率几乎是所有排序里最低的,但却很流行,就是因为它的编程复杂度也是最低的。大多数时候,效率还不及插入排序,其实冒泡排序、插入排序、选择排序基本上效果是差不多的(这个效果不是功能。。功能上讲肯定差不多啊都是排序),只是过程略有区别。既然写到这里,就自己总结一下三者吧。
1.插入排序——摸扑克牌的过程
假定前一个是有序的,把第二个插进它应当在的位置,那么前两个就是有序的了,把第三个插进它应当在的位置,那么前三个就是有序的了……直到前n个有序。这与打扑克摸牌的时候我们做的事差不多。
时间主要耗费在插入和挪动上了。复杂度n^2。
2.冒泡排序——高矮个自行排队
首先对最后一个人说:“如果你前面的人比你高,你就和他换一下。”(现在倒数第二个人就是后两个人里最矮的了)
然后对倒数第二个人说:“如果你前面的人比你高,你就和他换一下。”(现在倒数第三个人就是后三个人里最矮的了)
然后对倒数第三个人说同样的话……一轮下来,第一个人就是最矮的那个(其实和选择排序有点像,不过选择的方式不太一样,而且排的过程中就有让队列趋向有序的倾向)
对后n-1个人执行同样的过程……直到对最后2个人执行同样的过程。
时间主要耗费在交换上。复杂度n^2。
3.选择排序——老师给排队
首先,老师从所有人里选一个最矮的,跟第一个人交换位置。然后从后n-1个人里选一个最矮的,跟第二个人交换位置……直到从后一个人里选一个最矮的,放在最后。
时间主要耗费在选择最值上了。复杂度n^2。(后面会有一个堆排序,就是优化了选择过程,从而实现了nlgn的复杂度)
可以看出,这三种都是正确的排序算法。下面是冒泡排序的代码:
#include<stdio.h> void bubblesort(int *a,int l,int r) { int i,j; for (i=l+1;i<=r;i++) { for (j=r;j>=i;j--) { if (a[j]<a[j-1]) { int temp=a[j]; a[j]=a[j-1]; a[j-1]=temp; } } } } int main() { int n; scanf("%d",&n); int a[11]={}; int i; for (i=1;i<=n;i++) { scanf("%d",&a[i]); } bubblesort(a,1,n); for (i=1;i<=n;i++) { printf("%d |",a[i]); } return 0; }
三、霍纳规则的正确性
这是一个求多项式函数的值的方法,看完代码简直跪了,竟然如此简洁的完成了这个功能!
霍纳规则是采用最少的乘法运算策略,求多项式A(x) = anxn+ an-1xn-1+...+ a1x + a0在x0处的值,该规则是A(x0)=(...((anx0+ an-1)x0+...+ a1)x0+ a0)
下面是实现的代码:
#include<stdio.h> double horner(double *a,int n,double x) { double y=0; int i; for (i=n;i>=0;i--) { y=a[i]+x*y; } return y; } int main() { int n; int i; double x; scanf("%d",&n); double a[11]={}; for (i=0;i<=n;i++) { scanf("%lf",&a[i]); } printf("A(x)="); printf("%.2lf",a[0]); for (i=1;i<=n;i++) { printf("+%.2lfx^%d",a[i],i); } printf("\n"); while (1) { scanf("%lf",&x); printf("A(%.2lf)=%.2lf\n",x,horner(a,n,x)); } return 0; }
四、逆序对问题
先是逆序对的定义:一个n个互异元素的数组a,求满足i<j时a[i]>a[j]条件的数对个数。
输入:n(元素个数),a数组 输出:逆序对个数
很容易想到就是逐个比较的n^2的算法,但是算法导论上引导出了nlgn的算法。(修改归并排序)
首先,如果一个数组的已经知道了,前半部分内部的逆序对的个数k1,后半部分的逆序对的个数k2,并且两部分都已经由小到大排好序了。那么在merge的过程中就可以顺便把两部分之间的逆序对个数k求出来(因为前半部分比后半部分大的数一定是个逆序对)。那么总的逆序对的个数就是k1+k2+k。
那么,怎么求出前半部分逆序对的个数呢?同样的方法,求出前半部分,后半部分,两部分之间。return和。所以就是一个递归的问题,只需要修改一下归并排序即可。
下面是代码:
#include<stdio.h> int mergenixu(int *a,int *b,int l,int mid,int r) { int k=0; int i=l,j=mid+1; int cou=l; while (i<=mid && j<=r) { if (a[i]<=a[j]) { b[cou]=a[i]; i++; cou++; } else { b[cou]=a[j]; k+=mid-i+1; j++; cou++; } } while (i<=mid) { b[cou]=a[i]; i++; cou++; } while (j<=r) { b[cou]=a[j]; j++; cou++; } for (i=l;i<=r;i++) { a[i]=b[i]; } return k; } int mergesortnixu(int *a,int *b,int l,int r) { if (r-l<1) return 0; int mid=(l+r)/2; int k1=mergesortnixu(a,b,l,mid); int k2=mergesortnixu(a,b,mid+1,r); int k=mergenixu(a,b,l,mid,r); return (k1+k2+k); } int main() { int n; int a[11]={},b[11]={}; scanf("%d",&n); int i; for (i=1;i<=n;i++) { scanf("%d",&a[i]); } int k=mergesortnixu(a,b,1,n); for (i=1;i<=n;i++) { printf("%d |",a[i]); } printf("\n%d",k); return 0; }