数据结构,算法与应用 第二章 练习题 Part-A(初级排序乐园) / 简单的内存消耗判断

练习: 解决问题P时所需要的空间 - S(P) = c + Sp(实例特征)

什么是实例特征 -> 由于是实例,所带来的特别之处有那些?

例1:简单情况下,一个函数所需要的固定空间c 与 变化空间Sp

template<class T>
T Abc(T& a, T& b, T& c){
    return a+b+b*c+(a+b-c)/(a+b)+4;
}

那些因素有可能成为实例特征: (1).数据类型T (2). abc大小

  • 观察本例,我们所用的形参是引用类型,因此具体T是什么类型与调用时所消耗内存无关
  • 因为是引用,所以实际本函数永远只是在操作着一个指针,一个指针永远是两字节

    • 所以本函数在调用的时候会消耗掉6字节拿来存储a,b,c的指针,无论T到底是什么,永远6字节
    • 因此由于是实例所带来的特别之处 -> 0
  • 但是如果这里不是引用,调用的时候需要将函数拷贝进去,那么T是什么,就与函数调用时,占用多少内存相关了

    • 在常规参数的情况下,因为是实例所带来的特别之处,也就是S-p(实例特征) = 3* sizeof(T)
    • 同样,如果你使用doule作为T的类型,那就会造成3*8的数据空间消耗

例二:

template<class T>
int SequentialSearch(T a[], const T& x, int n){
    int i;
    for (i = 0; i < n && a[i] != x; i++);
    if (i == n) return -1 ;
    return i;
}

(1). 无论是什么类型,a[ ]作为数组传过来的永远只是一个2字节的指针,a[ ]多大都不计入本函数消耗掉的内存

(2). 同样,x作为一个引用传过来,只消耗两字节的指针内存

(3). 本例中,两个整形常量0 与 -1全都要分配两字节内存

为什么a[ ]无论多大都不计入内存呢?

  • 因为该数组所需要的空间,在定义实参,也就是a[ ]被给出的时候已经分配好了,这个函数所需要的空间,只是一个地址的大小。这个时候,a[ ]无论多大都与本函数所需要的内存空间无关了。显然 Sp(n)=0;

例三: 递归函数的实例特征与函数内存消耗

template<class T>
T Rsum(T a[], int n) { 
    if (n > 0)
    return Rsum(a, n-1) + a[n-1];
    return 0;
}
  • 本函数的实例特征是,n,不同实例所造成的差别只有数字n

  • 递归调用函数 -> 涉及函数体内再调用函数 ->

    • 函数体内调用一次函数所消耗的内存:实参所消耗的内存 + 函数返回地址所消耗的内存
    • 一个数组a[ ]地址 两字节 + 整形n消耗两字节 + 返回地址两字节 -> 一次调用消耗6字节内存
  • 递归深度n+1 因此一共会调用n+1次函数,一次6字节,因为实例不同所造成的差距6*(n+1)


时间复杂度估计

例一:

template<class T>
int Max(T a[], int n){//寻找最大元素
    int pos = 0;
    for (int i = 1; i < n; i++)
    if (a[pos] < a[i])
    pos = i;
    return pos;
}

本函数中一共执行了这些比较:

  • 在一个for循环里,每一回合都执行了 a[pos]与a[i]的比较,共计执行了n-1次
  • for的每一个循环结束的时候i 都要与 n 进行一次比较

例二:

template <class T>
T PolyEval(T coeff[], int n, const T& x){ 
    for ( int i = 1; i <= n; i++){ 
            y *= x;
            value += y * c [ i ] ;
    }
    return value;
}
  • 可以模拟为这是一个循环,并且这个循环被执行了n次
  • 每执行一次包含有一次加法 + 两次乘法
  • 在这里面,实例特征是整数n
  • 可能还包含有每次循环时,i与n做出的比较环节

例三: 利用if法判断一个元素在一个array里面的名次

template <class T>
void Rank(T a[], int n, int r[]){ 
    for ( int i = 1; i < n; i++)
    r[i] = 0; //初始化
    for ( int i = 1; i < n; i++)
    for ( int j = 1; j < i; j++)
    if (a [j] <= a[i]) r[i] + + ;
    else r[j ] + + ;
}

这样的排序行为可以理解为:

  • 首先将代表每一个数排名的r[i]初始化
  • 然后从两个数字开始讨论,a[i]=a[2]代表了第二个元素,如果第二个元素更大一点,给r[2]加一,代表在两个数字的排序行为里,a[2]更胜一筹
  • 接下来从三个数排序里,a[1],a[2]分别与a[3]做出比较,如果a[1]更大,则在两轮比较中,r[1]会被加上两次
  • 如果a[2]第二大,则说明,在两轮比较中,一轮r[2]没加,另一轮r[2]加一,最小的a[3]则在两轮比较中什么都没加
  • 总的来说,i一共经历了四个数字,每个数字执行i-1次比较,因此一共执行了1+2+3…n-1次比较


冒泡排序:一种比较经典的排序模型
(1). 在一回合里,可以做到将最大的数放到程序的最右边
(2). 如果经历n-1回合,就可以实现对array的排序

(1). 一个回合是怎么把最大的数放到最右边的

template <class T>
void Bubble (T a[], int n){ 
    for ( int i = 0; i < n-1; i++)
    if( a[i] > a[i +1]) Swap(a[i], a[i + 1 ] ) ;
}   
  • 从头到尾,将数据0/1,1/2,2/3….. 比对,如果谁大就放置到右边,这样下来就可以做到最大的一定在右边
  • 本函数一共经历了n-1次比对

(2). 如何做到排序整个数组的 - 将本函数执行n-1回合

template <class T>
void BubbleSort (T a[], int n){ 
    for ( int i = n; i>1; i- -)
    Bubble(a,i);
}
  • 从长度n开始,逐渐从n-1,n-2……一直执行到长度为2的数组,每一个都经历了一次比对
  • 所以一共经历了 1+2+…..n-1次数比对,即完成了 排序+比对 过程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值