平均查找长度与时间复杂度的区别:平均查找长度更关注于具体算法查找某个元素需要比较的次数,而时间复杂度则更强调算法执行时间与数据规模之间的关系。因此,两者虽然有些相似之处,但实际上是不同的概念。
一、平均查找长度与时间复杂度的区别
平均查找长度(Average Search Length,ASL)和时间复杂度(Time Complexity)都是衡量算法效率的指标,但它们从不同角度出发。ASL反映了查找过程中平均需要比较的关键字数,而时间复杂度则描述了算法运行时间与问题规模之间的关系。具体来说ASL更关注具体算法查找某个元素需要比较的次数,而时间复杂度更强调算法执行时间与数据规模之间的关系;ASL的计算涉及到顺序查找、折半查找和哈希表等算法,而时间复杂度通常使用大O符号来表示算法执行所需时间随着问题规模n的增加而增长的上限;ASL和时间复杂度都可以用来衡量算法效率,但它们的主要应用场合略有不同。
二、如何计算散列表的平均查找长度
1、查找成功
查找成功意味着需要查找的元素一定在表中,所以需要计算表中的每个元素通过 散列法&处理冲突的方法 得出的查找次数,求和再除以元素个数。
查询目标:散列表中的每个元素
查询过程
- 通过散列函数查询该元素地址;
- 访问地址,此次元素的查找次数+1;
- 若地址元素和目标元素不等,通过解决冲突的方法得到下一个地址,并转向第二步(解决冲突的方法 考试常用线性探测法);
- 若地址元素和目标元素相等,停止此元素的查找。
计算方法:散列表中所有元素的查找次数之和/元素个数
2、查找失败
查找不成功意味着查找的元素一定不在表中,所以与表中元素个数无关。每次查询查到地址为空为止,查到空意味着不需要再查询,也就是失败。
查询目标:使用散列函数可能得到的所有地址(数组下标),假设H(key)=7,则意味着查找失败时可能对应的地址有7个。
查询过程:
- 访问地址,此次元素的查找次数+1;
- 若地址不为空,通过解决冲突的方法得到下一个地址,并转向名列前茅步;
- 若地址为空则停止。
计算方法:散列函数对应所有地址元素的查找次数之和/散列值
3、易混淆
为何查找成功除以散列表元素个数,而查找失败除以散列值呢?
- 假设把所有元素对应的关键字分为两类,一类是在散列表中的数(查找会成功),一类是不在散列表中的数(查找会失败)。
- 要求查找成功的平均查找长度时,意味着这个数一定在散列表中,所以我们只需要考虑散列表中的元素即可。
- 要求查找不成功的平均查找长度时,意味着这个数一定不在散列表中,这个时候我们需要将不在散列表中的数再次分类,假设散列值为p,就可以将不在散列表中的数再次分为p 类,每一类分别为地址映射到i(0≤i≤p-1)的数,分别计算p类中每一类的查找次数即可。
为何查找成功以地址元素和目标元素相等为停止, 而查找失败以地址为空为停止?
- 查找成功意味着目标元素一定在散列表中,即一定会查到。
- 查找失败意味着目标元素不在散列表中,若地址元素不为空,那地址元素肯定和目标元素不等。但只有到地址为空,才能断定查找失败。
三、时间复杂度的分析与计算方法
1、循环次数非常多原则
当n变得越来越大时,公式中的低阶,常量,系数三部分影响不了其增长趋势,可以直接忽略他们,只记录一个最大的量级就可以了。因此我们在计算时间复杂度时,只需关注循环次数非常多的那段代码即可。
int sumFunc(int n) {
int sum = 0; //执行1次,忽略不计
for (int i = 0; i < n; i++) {
sum += i; // 循环内执行次数非常多,执行次数为n次,因此时间复杂度记为O(n)
}
return sum; //执行1次,忽略不计
}
2、加法原则
int sumFunc(int n) {
int sum = 0; //常量级,忽略
for (int i = 0; i < 99; i++) {
sum += i; //执行100次,还是常量级,忽略
}
for (int i = 0; i < n; i++) {
sum += i; //执行n次
}
for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++) {
sum += i; //执行n*n次
}
}
return sum;
}
上述例子中,最大的两块代码时间复杂度分别为 O(n)和 O(n*n),其结果本应该是:T(n)=O(n)+O(n * n),我们取其中最大的量级,因此整段代码的复杂度为:O(n * n)。所以得出结论:量级最大的那段代码时间复杂度=总的时间复杂度
3、乘法原则
嵌套代码的复杂度等于嵌套内外代码复杂度的乘积。
void Func1(int n) {
for (int i = 0; i < n; i++) {
Func2(n); //执行n次,每次都会调用Func2函数执行n次
}
}
void Func2(int n) {
int sum = 0;
for (int i = 0; i < n; i++)
{
sum += 1; //执行n次
}
}
因此这段代码时间复杂度为O(n) * O(n) = O(n*n) = O(n*n),同理,如果将其中一个n换成m,那么它的时间复杂度就是O(n*m)。
延伸阅读1:时间复杂度定义
在计算机科学中,时间复杂性,又称时间复杂度,算法的时间复杂度是一个函数,它定性描述该算法的运行时间。这是一个代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述,不包括这个函数的低阶项和首项系数。使用这种方式时,时间复杂度可被称为是渐近的,亦即考察输入值大小趋近无穷时的情况。