线性表的链式存储——单链表的遍历与优化

1,如何遍历单链表中的每一个数据元素?

       1,当前单链表遍历方法:

             

              1,插入的时间复杂度为 O(n),而遍历的时间复杂度为 O(n*n);

               

2,遗憾的事实:

       1,不能以线性的时间复杂度完成单链表的遍历;

   新的需求:

       1,为单链表提供新的方法,在线性时间内完成遍历;

 

3,设计思路(游标):

       1,在单链表内部定义一个游标(Node* m_current);

       2,遍历开始前将游标指向位置为 0 的数据元素;

       3,获取游标指向的数据元素;

       4,通过结点中的 next 指针移动游标;

   (5),从程序的角度来看,从链表的第 0 个元素一直遍历到最后一个元素,一个 next 指针就够了;

      

4,设计思路(游标):

       1,提供一组遍历相关的函数,以线性的时间复杂度遍历链表;

              1,move(),将游标定位到目标位置;

              2,next(),移动游标;

              3,current(),获取游标所指向的数据元素;

              4,end(),获取是否达到尾部(是否为空);

             

5,遍历函数原型设计:

       1,bool move(int i, int step = 1);

       2,bool end();

       3,T current();

       4,bool next();

 

6,单链表的遍历实现:

 1 /* 以下四个函数move(),end(),next(),current()是为了将遍历输出函数时间复杂度由O(n*n)降为O(n);其中 move() 函数时间复杂度为 i,其后三个函数在 for() 循环中加起来的时间复杂度为才为 O(n),很经典 */
 2 
 3     virtual bool move(int i, int step = 1)  // 从第 i 个位置移动,每次移动 1 个//位置; O(n)
 4     {
 5         bool ret = ( (0<= i) && (i<m_length) && (0<step));
 6 
 7         if( ret )
 8         {
 9             m_current = position(i)->next;   // 定位到节点i,不是第  i 个节点,//所以要加上next,这里时间复杂度严格来说是 i,配合着后面的 next() 函数的移动,//则最终的时间复杂度才是 n;
10             m_step = step;    // 将每次要移动的值传进来
11         }
12         return ret;
13    }
14 
15     virtual bool end()     // 判断当前的游标是否结束
16     {
17         return (m_current == NULL);    // 这里不可写成赋值了
18    }
19 
20     virtual T current()   // 获取游标当前位置的值
21     {
22         if( !end() ) // m_current != NULL ==> !end(),这里是判断当前指针的有效性;
23         {
24             return m_current->value;
25         }
26         else
27         {
28             THROW_EXCEPTION(InvalidOperationException, "No value at current position ...");
29         }
30    }
31 
32     virtual bool next()   // 移动游标
33     {
34         int i = 0;
35 
36         while( (i<m_step) && (!end()) )  // 这里的 !end() 是为了判m_step 步幅是否//会将指针指向空,所以在 move() 不用判断 step 的小于范围;
37         {
38                 m_current = m_current->next;
39                 i++;
40         }
41 
42         return (i == m_step);
43    }

 

7,单链表内部的一次封装:

      

       1,这样的封装是因为面向对象里面的主导思想有封装,所以采纳这样的封装;

       2,保护的函数,因为是内部的;

             

8,小结:

       1,单链表的遍历需要在线性时间内完成;

       2,在单链表内部定义游标变量,通过游标变量提高效率;

       3,遍历相关的成员函数是相互依赖、相互配合的关系;

       4,封装结点的申请和删除操作更有利于增强扩展性(见后续静态单链表的实现);

posted @ 2019-05-25 11:48  子宇24  阅读(1085)  评论(0编辑  收藏  举报