CList是一个双向链表类。CList的优势是快速灵活的插入和删除。但是在访问元素的时候,会多一个N的时间复杂度。在元素很多的时候,建议把数据分段存储。
1、包含头文件Afxtempl.h
在你要使用CList链表类的地方,使用#include
2、理解CList的声明和构造方法
CList的声明如下:
template< class TYPE, class ARG_TYPE >
class CList : public CObject
CList的声明有2种方式,决定的是参数的使用是引用,还是复制。
例如:
CList
CList
第一个参数,表示的是链表的元素的数据类型,这里类似一个声明,声明你使用的链表中的数据类型是什么。
第二个参数,分2种形式,一种是引用形式,一种是复制形式,那么这个引用和复制,体现的作用在哪里呢?其实,这个引用和复制,只是体现在元素的添加,查找上面。比如我们往刚才声明的这个链表list1中添加元素。
CList
CString str = "abc";
list1.AddTail(str); //str是以引用的方式,添加到list1中的,类似声明一个函数 void fun(CString &a);
再看另外一个中声明方式:
CList
CString str = "abc";
list2.AddTail(str); //str是以复制的方式添加的。
其实2种方式都能达到向链表中添加数据的目的,在数据结构很小的时候,其实是没有什么差别的,但是在数据结构很大的时候,比如一个数据元素就有1M,甚至10M,引用就变得有意义了,这个时候使用引用,链表是直接把str的地址中的内容添加到链表中了,使用复制,还需要先在内存中复制一下str,然后再把复制的内容添加到list中去。好了,现在懂了吗?如果没有,还得看下C++函数的形参的设置。是引用,还是非引用~
3、添加元素
例如:CList
(1)在CList的尾部添加元素
POSITION AddTail( ARG_TYPE newElement ); //在尾部添加一个元素,返回值是新插入元素的位置
void AddTail( CList* pNewList ); //在尾部添加另一个链表中的内容。链表的数据类型要一致。
比如:myList.AddTail("abc");
(2) 在CList的头部添加元素
POSITION AddHead( ARG_TYPE newElement ); //在链表最开始插入一个元素,返回值是新插入的元素的位置
void AddHead( CList* pNewList );
例如:myList.AddHead(CString("ABC"));
(3)在一个位置之前添加元素
POSITION InsertBefore( POSITION position, ARG_TYPE newElement ); //在position前面插入一个元素,返回新插入元素的位置。其实就是position前面一个位置
例如:
POSITION pos = myList.GetHeadPostion();
pos = myList.InsertBefore(pos, CString("ABC"));
pos = myList.InsertBefore(pos, CString("123"));
(4)在一个位置之后添加元素
POSITION InsertAfter( POSITION position, ARG_TYPE newElement ); //在postion后面插入一个元素,返回新插入元素的位置
例如:
POSITION pos = myList.GetTailPostion();
pos = myList.InsertAfter(pos, CString("ABC"));
pos = myList.InsertAfter(pos, CString("123"));
4、 查找元素:CList有2个函数,是非常方便我们查找的。
(1)直接查找元素
POSITION Find( ARG_TYPE searchValue, POSITION startAfter = NULL) const; 返回的是查找到的元素的第一个位置(如果链表中有多个重复的元素)。没有找到就返回空
例如:
CList
myList.AddHead(CString("XYZ"));
myList.AddHead(CString("ABC"));
myList.AddHead(CString("123"));
POSITION pos = myList.GetAt(pos));
然后就可以通过pos来操作这个元素了。比如GetAt(pos),InsertAfter(pos)等。
(2)通过下标来查找位置,通过下标是非常方便的。
POSITION FindIndex( int nIndex ) const; //返回第i个元素的位置,然后可以通过位置来对元素进行操作。
例如:
CList
myList.AddTail(CString("XYZ"));
myList.AddTail(CString("ABC"));
myList.AddTail(CString("123"));
POSITION pos1 = myList.FindIndex(0);
POSITION pos2 = myList.FindIndex(2);
CString str1 = myList.GetAt(pos1); //str1就等于"XYZ"
CString str2 = myList.GetAt(pos2); //str2就等于“123”
CStirng str3 = myList.GetAt(myList.FindIndex(1)); //str3等于"ABC"
(3)遍历查找
//如果链表是const类型的,这些函数,返回的是一个复制元素,如果不是const的,返回的是链表中元素的引用。但是怎么设置成一个const类型的,我都还不知道。所以,一般我们使用这些函数得到的元素,都是引用的,可以直接对链表的元素复制:比如myList.GetHead() = "AAA";就能把表头置为"AAA"。
TYPE& GetHead( ); //得到第一个元素,确保链表不为空,使用IsEmpty()
TYPE GetHead( ) const;
TYPE& GetTail( ); //得到表尾元素
TYPE GetTail() const;
TYPE& GetNext( POSITION& rPosition );
TYPE GetNext( POSITION& rPosition ) const;
TYPE& GetPrev( POSITION& rPosition );
TYPE GetPrev( POSITION& rPosition ) const;
TYPE& GetAt( POSITION position );
TYPE GetAt( POSITION position ) const;
5、删除元素
(1)删除全部元素,删除后,链表为空。
void RemoveAll( );
例如: myList.RemoveAll();
(2)删除position处的元素
void RemoveAt( POSITIONposition ); //这里要保证position是有效的,不是空的,如果是空的,调试版本会报错。release版本不会报错。
例如:myList.RemoveAt(myList.FindIndex(1));
(3)删除表头
TYPE RemoveHead( ); //返回值是没有删除的时候的表头元素。
(4)删除表尾
TYPE RemoveTail( ); //返回值是没有删除的时候的表尾元素。
6、链表的遍历
方式1:
POSITION pos = myList.GetHeadPostion();
while(pos != NULL)
{
CString str = myList.GetNext(pos); //GetNext(pos),先返回pos位置的元素的值,再把pos指向当前位置的下一个。
}
方式2:
for(int i = 0 ; i < myList.GetCount(); i++)
{
CString str = myList.GetAt(myList.FindIndex(i));
}
总结:方式2,明显比方式1慢.方式1只需要执行N此就可以了,方式2,需要执行1+2+。。。+N = (1 + N)*N/2次
由于时间关系,写的也不是非常完整,需要这方面知识的,请多看MSDN帮助信息,祝你顺利~
最后还是请教一下大家,怎么把CList声明为const类型?欢迎留言~