24、单链表的遍历与优化

如何遍历单链表的每个元素?

for(int i=0;i<5;i++) //o(n)
{
list.insert(0,i);
}
for(int i=0;i<5;i++)   //o(n^2)
{
cout<<list.get(i)<<endl;

}

遗憾的事实:不能以线性的时间复杂度完成单链表的遍历。

新的需求:为单链表提供新的方法,在线性时间内完成遍历。

设计思路(游标):

在单链表的内部定义一个游标(Node* m_current),遍历开始前将游标指向位置为0的数据元素,获取游标指向的元素,通过节点中的next指针移动游标。

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

函数         功能说明

move()      将游标定位到目标位置 

next()        移动游标    

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

end()        游标是否到达尾部(是否为空)

原型设计:

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

bool end();

T current();

bool next();

实现:

bool move(int i,int step=1)  //move(0)表示position(0)头结点->next,定位到第0个元素
{
bool ret=((i>=0)&&(i<m_length)&&(step>0));
if(ret)
{
m_current=position(i)->next;  //当前结点中的指针,也就是下一个结点的地址
m_step=step;
}
return ret;
 }
bool end()
{
return (m_current==NULL);
}
T current()
{
if(m_current != NULL)  // 等价于 if(!end)
{
return m_current->value;
}
else 
{
THROW_EXCEPTION(InvalidOperationException, "No value at current position ...");
}
}
bool next()
{
int i=0;
while((i<m_step)&& (!end()))   //循环m_step次,并且游标不为空
{
m_current=m_current->next;
i++;
}
return(i==m_step); //表示移动成功

}

#include <iostream>
#include "LinkList.h"
using namespace WSlib;
using namespace std;
int main()
{
LinkList<int>list;
for(int i=0;i<5;i++)
{
list.insert(0,i);
}
for(list.move(0);!list.end();list.next()) //O(n)
{
cout<<list.current()<<endl;
}
for(list.move(0,2);!list.end();list.next()) //O(n)
{
cout<<list.current()<<endl;
}
for(list.move(0,10);!list.end();list.next()) //O(n)
{
cout<<list.current()<<endl;
}
return 0;

}


单链表内部的一次封装:

virtual Node* create()
{
return new Node();
}
virtual void destory(Node* pn)
{
delete pn;

}

封装create和destroy函数的意义是什么?

单链表的遍历需要在线性时间内完成。在单链表内部定义游标变量,通过游标变量提高效率。遍历相关的成员函数是相互依赖,相互配合的关系,封装结点的申请和删除操作有利于增强扩展性。

单向循环链表:表尾节点存储的指针不为NULL,而是该链表表头节点的指针。

双向循环链表:每个节点都存储了两个指针,一个指向前驱节点,另一个指向后驱节点。一个双向循环链表有一个表头节点,他同样有两个指针,一个指向链表的最后一个节点,一个指向链表最前端的第一个节点x(0)。

数组和链表都是线性数据结构。

猜你喜欢

转载自blog.csdn.net/ws857707645/article/details/80373772