算法导论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("
");
    while (1) {
        scanf("%lf",&x);
        printf("A(%.2lf)=%.2lf
",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("
%d",k);
    return 0;
}
原文地址:https://www.cnblogs.com/itlqs/p/5095009.html