(四)两种实现的效率

  1. 算法的基本操作(basic operation)是那些对其总的时间需求贡献最大的因素。
  2. 为什么复杂度只关注最高次项:比如(n2+n)/2---> n2。因为对于大的n值n2比n大得多,所以(n2+n)/2的表现n2/2一样,其次,当n变大时,n2/2的表现n2一样,所以,对于大的n,(n2+n)/2与n2相差不大,且可被忽略。【同一问题考虑大的问题:因为在小问题上,两种方案在执行时间上的差异通常微不足道
  3. 常见增长率函数的相对大小(log的底数不成问题

1 < log(log n) < log n < log2 n < n < n log n < n2 < n3 < 2n < n!

  4. 最优、最差和平均:最优情况下仍然很慢,说明需要更换算法;最差情形下可以接受,说明算法就是可以接受的。但是最优和最差在实际中很少出现,所以需要考虑算法的平均情形(不是最优和最差的平均值)。---> 依赖于数据本身

  不依赖于数据本身,而依赖于数据个数---> 寻找n个数里的最小值  

  5. O的形式化定义

函数f(n)具有最多g(n)阶(即f(n)是O(g(n))),如果

  • 给定正实数c和正整数N,对于所有的n ≥ N,存在f(n) ≤ c x g(n)。

 即,当n足够大时, c x g(n)是f(n)的上界。

  f(n)是O(g(n))意味着,当n充分大时,c x g(n)为f(n)的增长率提供了一个上界。对于足够大的数据集(set),算法总是需要少于c x g(n)个基本操作。

6. 分析实现ADT包的效率

6.1 基于固定数组的实现

1)添加add   O(1)

复制代码
public boolean add(T newEntry) {
  checkInitialization();   // O(1)
  boolean result = true;   // O(1)
  if(isArrayFull()) {      // O(1) 条件
    result = false;
  }
  else {                   // O(1)
    // Assertion : result is true here
    bag[numberOfEntries++] = newEntry;
  } // end if
  return result;
}
// end add private void checkInitialization() {   if(!initialized) {     throw new SecurityException("ArrayBag object is not initialized properly.");   } } // end checkInitialized private boolean isArrayFull() {   return numberOfEntries >= bag.length; } // end isArrayFull
复制代码

  add方法内部没有循环结构,只有if,else条件分支,均为O(1),所以add为O(1)

 

2)查找contains    O(n)

复制代码
public boolean contains(T anEntry) {
  checkInitialization();            // O(1)
  return getIndexOf(anEntry) > -1;   // O(n)
} // end contains

private int getIndexOf(T anEntry) {
  int getIndex = -1;             // O(1)
  if (numberOfEntries > 0) {     // O(1)
    for (int index = 0; index < numberOfEntries; index++) {    // O(n)
      if (bag[index].equals(anEntry)) {
        getIndex = index;
        break;
      } // if
    } // end for
  } // end if
  return getIndex;
} // end getIndexOf
复制代码

  因为函数getIndexOf需要遍历数组查找,所以getIndexOf是O(n),函数contains方法体调用checkInitialization函数,为O(1),return调用getIndexOf方法,所以函数contains为O(n)。

 

6.2 基于链式的实现

1)添加add    O(1)

public boolean add(T newEntry) {  // OutOfMemoryError posiible
    Node newNode = new Node(newEntry);  // O(1)
    newNode.next = firstNode;    //  O(1)
    firstNode = newNode;         //  O(1)
    numberOfEntries++;          //  O(1)
    return true;
} // end add

  只涉及一些赋值操作,时间为O(1)

 

2)查找contains  O(n)

复制代码
public boolean contains(T anEntry) {
    Node currentNode = firstNode;             // O(1)
    boolean found = false;                    // O(1)
    while(!found && (currentNode != null)) {   // O(n)
        if(currentNode.data.equals(anEntry)) {   
            found = true;
            break;
         }
        else {
            currentNode = currentNode.next;
        } // end if
    } // end while
    return false;
} // end contains
复制代码

  同样需要遍历操作,时间O(n)

 

6.3 两种实现的比较

操作

固定大小数组

链式

add(newEntry)

O(1)

O(1)

remove()

O(1)

O(1)

remove(anEntry)

O(n)

O(n)

clear()

O(n)

O(n)

getFrequencyOf(anEntry)

O(n)

O(n)

contains(anEntry)

O(n)

O(n)

toArray()

O(n)

O(n)

getCurrentSize(),isEmpty()

O(1)

O(1)

posted @   dedication  阅读(246)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术
点击右上角即可分享
微信分享提示