数据结构学习记录连载3(链表的学习)
基本要求:
1) 用模板方法建立链表的结点类ListNode和单链表类LinList,编写程序实现向单链表插入100整数,然后,以插入次序删除这100个整数。
提高要求:
1) 简单修改程序,将单链表类LinList改为双向循环链表类。向双向循环链表插入100字符,然后,以插入次序删除这100个字符。
2) 编写函数实现单链表类LinList的对象B连接到单链表类LinList的对象A的尾部:Void Concatenate(LinList& A, LinList& B)。
1.ListNode.h结点定义:
/*
* Copyright (c) 2009,FreshAir团队嵌入式软件研发组
* All rights reserved.
*
* 文件名称:ListNode.h
* 摘 要: 节点类的定义
*
* 当前版本:1.0
* 作 者: 吴友强
* 完成日期:2009年10月13日
*
* 取代版本:
* 原作者 :
* 完成日期:
*/
#include<iostream.h>
#include<stdio.h>
template<class T> class LinkList; //前视定义,否则,无法定义友元
template<class T> class ListNode
{
friend class LinkList<T>; //定义友元
private:
ListNode<T> *next; //指向下一个结点的指针
public:
T data;
ListNode(ListNode<T> *ptrNext = NULL); //构造函数,用于构造头结点
ListNode(const T& item, ListNode<T> *ptrNext = NULL);//构造函数,非头结点
~ListNode() {}; //析构函数
friend void Concatenate(LinkList<T>& target, LinkList<T>& souce);
};
template<class T>
ListNode<T>::ListNode(ListNode<T> *ptrNext):next(ptrNext)
{
}
template<class T>
ListNode<T>::ListNode(const T &item, ListNode<T> *ptrNext)
{
data = item;
next = ptrNext;
}
2.LinkList.h定义单链表类及实现:
/*
* Copyright (c) 2009,FreshAir团队嵌入式软件研发组
* All rights reserved.
*
* 文件名称:LinkList.h
* 摘 要: 单链表类的定义
*
* 当前版本:1.0
* 作 者: 吴友强
* 完成日期:2009年10月13日
*
* 取代版本:
* 原作者 :
* 完成日期:
*/
#include "ListNode.h"
template <class T>
class LinkList
{
private:
ListNode<T> *head; //指向头结点的指针
int size; //单链表的元素个数
ListNode<T> *currPtr; //当前结点
public:
LinkList(void); //构造函数
~LinkList(); //析构函数
//线性表操作要求的成员函数
int GetListSize(void) const; //返回链表的元素个数
int ListIsEmpty(void) const; //判断单链表是否为空
ListNode<T> *Index(int pos); //定位
void Insert(const T& item, int pos); //插入
T Delete(int pos); //删除
T GetData(int pos); //取元素
void ClearList(void); //清空链表
//遍历单链表的成员函数
ListNode<T> *Reset(int pos=0); //把第pos个结点设置为当前结点currPtr
ListNode<T> *Next(void); //currPtr指向下一个结点
int EndOfList(void) const; //currPtr是否指在链表尾
friend void Concatenate(LinkList<T>& target, LinkList<T>& souce);
};
/*
* Copyright (c) 2009,FreshAir团队嵌入式软件研发组
* All rights reserved.
*
* 文件名称:LinkList.cpp
* 摘 要: 单链表的各个功能函数的现实
*
* 当前版本:1.0
* 作 者: 吴友强
* 完成日期:2009年10月13日
*
* 取代版本:
* 原作者 :
* 完成日期:
*/
template <class T>
LinkList<T>::LinkList(void)
{
size = 0;
head = new ListNode<T>();
}
template <class T>
LinkList<T>::~LinkList()
{
ClearList(); //释放所有非头结点的结点
delete head; //释放头结点
}
/*
* 函数名称: GetListSize
* 输 入:
* 输 出:
* 功能描述: 返回顺序表的元素个数size
* 作 者: 吴友强
* 日 期: 2009年10月13日
* 修 改:
* 日 期:
*/
template <class T>
int LinkList<T>::GetListSize(void) const
{
return size;
}
/*
* 函数名称: ListIsEmpty
* 输 入:
* 输 出:
* 功能描述: 判断是否为空,空返回1;非返回0
* 作 者: 吴友强
* 日 期: 2009年10月13日
* 修 改:
* 日 期:
*/
template <class T>
int LinkList<T>::ListIsEmpty(void) const
{
return size == 0 ? 1 : 0;
}
/*
* 函数名称: Index
* 输 入: pos
* pos: 定位数据元素结点
* 输 出:
* 功能描述: 定位至第pos个数据元素结点,返回指向第pos个结点的指针
* 作 者: 吴友强
* 日 期: 2009年10月13日
* 修 改:
* 日 期:
*/
template <class T>
ListNode<T> *LinkList<T>::Index(int pos)
{
if (pos < -1 || pos > size)
{
cout << "参数pos越界出错!" << endl;
exit(0);
}
if (pos == -1)
{
return head;
}
ListNode<T> *p = head->next;
int i = 0;
while (p != NULL && i <pos)
{
p = p->next;
i++;
}
return p;
}
/*
* 函数名称: Insert
* 输 入: pos
* pos: 插入数据元素结点的位置
* item: 插入数据结点的数据
* 输 出:
* 功能描述: 在pos个结点之前插入一个data域为item的新结点
* 作 者: 吴友强
* 日 期: 2009年10月13日
* 修 改:
* 日 期:
*/
template <class T>
void LinkList<T>::Insert(const T& item, int pos)
{
if (pos < 0 || pos > size)
{
cout << "参数pos越界出错!" << endl;
exit(0);
}
ListNode<T> *p = Index(pos - 1);
ListNode<T> *newNode = new ListNode<T>(item, p->next);
p->next = newNode;
size++;
}
/*
* 函数名称: Delete
* 输 入: pos
* pos: 删除数据元素结点的位置
* 输 出:
* 功能描述: 删除第pos个结点,并返回被删除结点的data
* 作 者: 吴友强
* 日 期: 2009年10月13日
* 修 改:
* 日 期:
*/
template <class T>
T LinkList<T>::Delete(int pos)
{
T temp;
if (pos < 0 || pos > size-1)
{
cout << "参数pos越界出错!" << endl;
exit(0);
}
ListNode<T> *q, *p = Index(pos - 1);
q = p->next;
p->next = p->next->next;
temp = q->data;
delete q;
size--;
return temp;
}
/*
* 函数名称: ClearList
* 输 入: pos
* pos: 删除数据元素结点的位置
* 输 出:
* 功能描述: 清空表为初始化状态
* 作 者: 吴友强
* 日 期: 2009年10月13日
* 修 改:
* 日 期:
*/
template <class T>
void LinkList<T>::ClearList(void)
{
ListNode<T> *p = head->next;
ListNode<T> *q;
while (p != NULL)
{
q = p;
p = p->next;
delete q;
}
size = 0;
}
/*
* 函数名称: Reset
* 输 入: pos
* pos: 数据元素结点的位置
* 输 出:
* 功能描述: 使currPtr指向结点pos,并返回currPtr
* 作 者: 吴友强
* 日 期: 2009年10月13日
* 修 改:
* 日 期:
*/
template <class T>
ListNode<T> *LinkList<T>::Reset(int pos)
{
if (pos < -1 || pos > size - 1)
{
cout << "参数pos越界出错!" << endl;
exit(0);
}
if (pos == -1)
{
return head;
}
if (pos == 0)
{
currPtr = head->next;
}
else
{
currPtr = head->next;
ListNode<T> prevPtr = head;
for (int i=0; i<pos; i++)
{
prevPtr = currPtr;
currPtr = currPtr->next;
}
}
return currPtr;
}
/*
* 函数名称: Next
* 输 入:
* 输 出:
* 功能描述: 使currPtr指向下一个结点
* 作 者: 吴友强
* 日 期: 2009年10月13日
* 修 改:
* 日 期:
*/
template <class T>
ListNode<T> *LinkList<T>::Next(void)
{
if (currPtr != NULL)
{
currPtr = currPtr->next;
}
return currPtr;
}
/*
* 函数名称: EndOfList
* 输 入:
* 输 出:
* 功能描述: currPtr是否指在链表尾
* 作 者: 吴友强
* 日 期: 2009年10月13日
* 修 改:
* 日 期:
*/
template <class T>
int LinkList<T>::EndOfList(void) const
{
return currPtr == NULL ? 1 : 0;
}
/*
* 函数名称: GetData
* 输 入: pos
* pos: 需要得到结点数据的位置
* 输 出:
* 功能描述: 获取pos处结点的数据
* 作 者: 吴友强
* 日 期: 2009年10月13日
* 修 改:
* 日 期:
*/
template <class T>
T LinkList<T>::GetData(int pos)
{
if (pos < 0 || pos > size-1)
{
cout << "参数pos越界出错!" << endl;
exit(0);
}
ListNode<T> *p = Index(pos);
return p->data;
}
4.LinkListTest.cpp测试程序:
/*
* Copyright (c) 2009,FreshAir团队嵌入式软件研发组
* All rights reserved.
*
* 文件名称:SeqListTest.cpp
* 摘 要: 测试链表的功能
*
* 当前版本:1.0
* 作 者: 吴友强
* 完成日期:2009年10月13日
*
* 取代版本:
* 原作者 :
* 完成日期:
*/
#include <iostream.h>
#include <stdlib.h>
#include "LinkList.h"
int main(int argc, char *argv[])
{
LinkList<int> targetList, sourceList;
for (int i=0; i<10; i++)
{
targetList.Insert(i+10, i);
}
for (i=0; i<5; i++)
{
sourceList.Insert(i+5, i);
}
// cout << "测试GetData()成员函数结果如下:" << endl;
// int n = linkList.GetListSize();
// for (i=0; i<n; i++)
// {
// cout << linkList.GetData(i) << " ";
// }
//
// cout << endl << "测试遍历成员函数结果如下:" << endl;
// ListNode<int> *p = linkList.Reset(0);
//
// while (!linkList.EndOfList())
// {
// cout << p->data << " ";
// p = linkList.Next();
// }
//
// cout << endl << "测试Delete()成员函数结果如下:" << endl;
// n = linkList.GetListSize();
// for (i=n-1; i>=0; i--)
// {
// linkList.Delete(i);
// }
//
// cout << "测试GetData()成员函数结果如下:" << endl;
// n = linkList.GetListSize();
// for (i=0; i<n; i++)
// {
// cout << linkList.GetData(i) << " ";
// }
Concatenate(targetList, sourceList);
int n = targetList.GetListSize();
for (i=0; i<n; i++)
{
cout << targetList.GetData(i) << " ";
}
return 0;
}
5.注意事项:(1)如果单链表的定义与接口的实现分开到两个不同的头文件和CPP文件,编译会出错,解决把法是把CPP文件删除在头文件最后加上一句:#include "LinkList.cpp"
(2)上面注释部分是测试基本函数功能的,我同时在里面测试了提高要求2,提高2的现实在下一篇中给出。