文章目录
- ADT
- LinkList(LinkList &List);
- operator=(LinkList &List) 、ListDestroy()和ClearList()
- ListDestroy()
- ListClear()
- ListLength()和ListEmpty()
- InsFirst
- GetElem(int pos) 、ListInsert(int pos,ElemType e)和DelFirst(ElemType &e)
- CreateList_Head(int n,ElemType *A)和CreateList_Tail(int n,ElemType *A)
- ListDelete(int pos)
- compare(ElemType a,ElemType b)
- LocateElem(const ElemType &e, LinkList *pos)
- PriorElem(ElemType cur_e, ElemType &pri_e)
- NextElem(LinkNode *p, ElemType &e)
- ListTraverse() const
- 测试代码以及效果
- 异常处理现场
ADT
/* 单链表的结点定义 */
template<class ElemType>
struct LinkNode
{
ElemType data;
LinkNode<ElemType> *next;
LinkNode(LinkNode<ElemType> *ptr = NULL){next = ptr;}
LinkNode(const ElemType &item, LinkNode<ElemType> *ptr = NULL)
//函数参数表中的形参允许有默认值,但是带默认值的参数需要放后面
{
next = ptr;
data = item;
}
};
//带头结点的单链表
template<class ElemType>
class LinkList
{
private:
LinkNode<ElemType> *head; // 头结点
public:
//the constructer without parameters
LinkList(){head = new LinkNode<ElemType>;}
//the construtor to construct the first node with value
LinkList(const ElemType &item){head = new LinkNode<ElemType>(item);}
//the constructor to copy the linkedlist
LinkList(LinkList<ElemType> &List);
//deconstructor of the function
~LinkList(){ListDestroy();}
//the overload the operator =
LinkList<ElemType>& operator=(LinkList<ElemType> &List);
//销毁链表
void ListDestroy();
//清空链表
void ListClear();
//返回链表的长度
int ListLength() const;
//判断链表是否为空表
bool ListEmpty() const;
//在首节点之前插入一个结点
bool InsFirst( ElemType e );
//获取链表头结点
LinkNode<ElemType>* GetHead() const{ return head;}
//设置链表头结点
//void SetHead(LinkNode<ElemType> *p);
//用e返回链表的第i个元素
ElemType GetElem(int pos);
//在链表的第pos个位置之前插入e元素
bool ListInsert(int pos,ElemType e);
//删除链表的首结点
//bool DelFirst( ElemType &e);
//表头插入法动态生成链表
void CreateList_Head(int n, ElemType *A);
//表尾插入法动态生成链表
void CreateList_Tail(int n, ElemType *A);
//删除链表的第pos个位置的元素
ElemType ListDelete(int pos);
//compare函数,用来判断a和b是否相等
//bool compare(ElemType a, ElemType *b);
//按指定条件查找,返回指向第一个符合条件(=e)的元素的指针
bool LocateElem(const ElemType &e, LinkNode<ElemType> *pos);
//返回链表给定数据元素的前驱数据元素的值
//bool PriorElem(ElemType cur_e, ElemType &pri_e);
//返回链表给定数据元素的后继数据元素的值
bool NextElem(LinkNode<ElemType> *p, ElemType &e);
//遍历链表
bool ListTraverse() const;
};
分析总结
- 题目中定义节点的时候,使用的是struct,乍一看没什么,详细一看,居然还有相关的构造函数,一想这还是我认识的结构体吗?这和class怎么是如此的相像?搜了一下,主要有以下的认知
LinkList(LinkList &List);
//the constructor to copy the linkedlist
template <class ElemType>
LinkList<ElemType>::LinkList(LinkList<ElemType> &List)
{
//traverse the whole linkedlist to copy all the elements
//copy the first node of the linkedlist
LinkNode<ElemType> *temp = List.head;
head = new LinkNode<ElemType>(*temp);
//copy the other nodes apart from the first node
LinkNode<ElemType> *temp2 = head;
while(temp)
{
temp2->next = new LinkNode<ElemType>(*temp->next);
temp = temp->next;
temp2 = temp2->next;
}
}
分析与总结
- 首先复制头结点,这里可以直接调用相关的构造函数
- 关于new函数,当传入的参数是有相关的引用的时候,会将申请空间并将传入引用完全复制到刚申请的内存空间,简而言是就是申请和原来等大的空间,并复制
- 反是调用next指针域,都需要进一步判定其指针域是否为空
operator=(LinkList &List) 、ListDestroy()和ClearList()
/*
description:the overload of the operator "="
function:assign the value to other linkedlist
*/
template<class ElemType>
LinkList<ElemType>& LinkList<ElemType>::operator=(LinkList<ElemType> &List)
{
//case 1:the linkedlist is NULL,just a pointer
if(!head)
{
this = LinkList(List);
}
else
{
//case 2:the linkedlist is not NULL
LinkNode<ElemType> *temp1 = List->head;
LinkNode<ElemType> *temp2 = head;
//copy the common the element that both have
while(temp1 && temp2)
{
*temp1 = *temp2;
temp1 = temp1->next;
temp2 = temp2->next;
}
//deal with the left element of the linklist
//the right linklist is longer and copy the left part of the linklist
if(temp1)
{
while(temp1)
{
temp2 = new ElemType(*temp1);
temp1 = temp1->next;
temp2 = temp2->next;
}
return *this;
}
//the left linklist is longer and delete the left part of the linklist
if(temp2)
{
LinkNode<ElemType> temp3 = temp2->next;
while(temp2)
{
temp3 = temp2->next;
delete temp2;
temp2 = temp3;
}
return *this;
}
}
}
ListDestroy()
/*
description:delete all the element of the of the linkedlist
*/
template<class ElemType>
void LinkList<ElemType>::ListDestroy()
{
LinkNode<ElemType> *temp = NULL;
while(head)
{
temp = head->next;
delete head;
head = temp;
}
cout<<"delete the object of the Linklist"<<endl;
}
ListClear()
/*
description:clear all the element of the linklist
*/
template<class ElemType>
void LinkList<ElemType>::ListClear()
{
//delete other element apart from the first element
LinkNode<ElemType> *temp = head->next;
LinkNode<ElemType> *temp2 = NULL;
while(temp)
{
temp2 = temp->next;
delete temp;
temp = temp2;
}
//reset the value of the first element
head->next = NULL;
}
分析与总结
- 赋值,相比较于带参的构造函数,不同在于“=”左边的引用是否已经有相关的空间了,如果有的话,我又是否还涉及到释放。所以对这类问题,要分几种情况去处理。
- 第一,如果给的链表的指针是空的,那就是完全调用相关的构造函数了
- 第二,如果给的链表不是空的,有一定的结点,那就有长或者短两种情况了
- 对于删除剩余结点的情况,在判定了当前结点不为空的情况下,才能利用相关结点的相关的属性。
- 如下:temp2不为空,进而才能使用temp2的next属性,在将其删除之前,需将其下一个结点转移到temp3,删除之后再将temp3转给temp2
- 如下:temp2不为空,进而才能使用temp2的next属性,在将其删除之前,需将其下一个结点转移到temp3,删除之后再将temp3转给temp2
- 对于这种模板类,就很奇怪,因为不知道具体的类型,也不知道如何重置对应的头结点。也不知道其中的特殊表
ListLength()和ListEmpty()
/*
description:return the length of the linklist
*/
template<class ElemType>
int LinkList<ElemType>::ListLength() const
{
int length = 0;
LinkNode<ElemType> *temp = head->next;
while(temp)
{
length ++;
temp = temp->next;
}
return length;
}
分析与总结
- 不知道头结点中的存放的具体信息,不能用头头结点来存储表长,所以只能完整的遍历一次
InsFirst
/*
description:judge whether the linklist is empty or not
*/
template<class ElemType>
bool LinkList<ElemType>::ListEmpty() const
{
if(head->next)
{
return false;
}
else
{
return true;
}
}
分析与总结
- 通过判定头结点是否为空,判定链表是否已经存在
GetElem(int pos) 、ListInsert(int pos,ElemType e)和DelFirst(ElemType &e)
/*
description:return the value of the element whose index is pos
*/
template<class ElemType>
ElemType LinkList<ElemType>::GetElem(int pos)
{
//get the index of the element which is previous to the element
LinkNode<ElemType> *temp = head;
int index = 0;
while(temp && index < pos)
{
temp = temp->next;
index ++;
}
//judge whether you have found the correct idnex
if(!temp || index < pos-1)
{
cout<<"the index of the element you want is wrong"<<endl;
return;
}
//reutrn the target
return temp->data;
}
/*
description:return the value of the element whose index is pos
*/
template<class ElemType>
bool LinkList<ElemType>::ListInsert(int pos,ElemType e)
{
//get the index of the element which is previous to the element
LinkNode<ElemType> *temp = head;
int index = 0;
while(temp && index < pos - 1)
{
temp = temp->next;
index ++;
}
//judge whether you have found the correct idnex
if(!temp || index < pos-1)
{
cout<<"the index of the element you want is wrong"<<endl;
return false;
}
//create the new linknode and insert it in the suitable position
LinkNode<ElemType> *temp2 = new LinkNode<ElemType>();
temp2->data = e;
temp2->next = temp->next;
temp->next = temp2;
}
/*
description:delete the first element of the linklist and return it to the e
*/
template<class ElemType>
bool LinkList<ElemType>::DelFirst(ElemType &e)
{
LinkNode<ElemType> *temp = head->next;
e = *temp;
if(!temp) return false;
head->next = temp->next;
delete temp;
return true;
}
分析与总结
- 注意两种方式,如果插入节点,是需要获取插入位置的前一个结点指针,但是获取结点仅仅只需要获取对应结点的指针就行了。进而逐一去下一个指针的次数
CreateList_Head(int n,ElemType *A)和CreateList_Tail(int n,ElemType *A)
/*
description:construct the linklist by inserting the element at the beginning of linklist
parament:A is the pointer of the array of the ElemType
n is the length of the array
*/
template<class ElemType>
void LinkList<ElemType>::CreateList_Head(int n,ElemType *A)
{
ElemType *temp = NULL;
//traverse the whole linklist and create the relevent element to insert at the start of the linklist
for(int i = 0;i < n;i ++)
{
temp = A + i;
InsFirst(*temp);
}
}
/*
description:construct the linklist by inserting the element at the end of linklist
parament:A is the pointer of the array of the ElemType
n is the length of the array
*/
template<class ElemType>
void LinkList<ElemType>::CreateList_Tail(int n,ElemType *A)
{
LinkNode<ElemType> *temp = head;
//get the element at the end of the linklist
int length = ListLength(),cont = 0;
while(temp && cont < length)
{
temp = temp->next;
cont++;
}
LinkNode<ElemType> *temp2 = temp;
//maybe some special situation may happen .in fact the possibility is very low
if(!temp || cont<length)
{
return;
}
cout<<"LINK_END"<<endl;
//traverse the whole linklist and create the relevent element to insert at the start of the linklist
for(int i = 0;i < n;i ++)
{
temp->next = new LinkNode<ElemType>(*(A+i));
temp = temp->next;
}
}
分析与总结
-
对于这类的创建新的列表的问题,需要判定链表的有无,如果没有初始化,那就需要自己创建,如果是有相关的链表,不用自己再创建,只需要将对应的数组作为成员依次插入即可
- 好吧,对于我们这道题就不要了,是一个linklist具体的对象调用的,既然是对象说明已经实例化过了,所以无论如何是一定存在linklist的基本的结构
-
要尝试使用已经定义好的方法,来简化代码的书写。但是要注意你调用地函数地返回值,时返回一个新的复制的值还是一个新的结点。以下就是原来的错的写法
ListDelete(int pos)
/*
descrition:delete the element whose index is pos
*/
template<class ElemType>
ElemType LinkList<ElemType>::ListDelete(int pos)
{
//find the element previous to the target
LinkNode<ElemType> *temp = head;
int i = 0;
while(temp && i < pos - 1)
{
temp = temp->next;
i ++;
}
//judge whether find
if(!temp || i < pos - 1)
{
cout<<"the position you input is invalid"<<endl;
return;
}
//delete the revelent
LinkNode<ElemType> *temp2 = NULL;
temp2 = temp->next;
temp->next = temp2->next;
ElemType result = temp2->data;
delete temp2;
return result;
}
分析与总结
- 注意GetElem(int pos)返回位置为pos的元素的值,但是并不是返回相关的地址,仅仅是返回相关的值,所以并没有任何的用处
- 注意题设要求返回的是对应的结点的data域,同时要删除对应的结点即删除对应的空间,所以,注意需要将对应的data域的信息保存下来,然后再删除对应的空间,然后再返回相关的数据值
compare(ElemType a,ElemType b)
/*
description:compare A with B and judge whether the elements are equal
return: 0 A = B
1 A > B
-1 A < B
*/
template<class ElemType>
bool LinkList<ElemType>::compare(ElemType a,ElemType b)
{
if(a < b)
{
return -1;
}
if(a = b)
{
return 0;
}
if(a > b)
{
return 1;
}
}
分析与总结
- 这是一个问题,并不知道ElemType的具体的数据类型,如果是几种基本的数据类型,那就好说了,直接比较。但是如果是用户自定义的数据类型,那还需要自己去重载相关的函数
- 这个方法就很奇怪,不建议写进你的程序中
LocateElem(const ElemType &e, LinkList *pos)
/*
description:return the pointer which point to eligible element
parament: ElemType &e :the target element you want to search
pos:the pointer which point to LinkNode
*/
template<class ElemType>
bool LinkList<ElemType>::LocateElem(const ElemType &e, LinkNode<ElemType> *pos)
{
//traverse the whole linklist
LinkNode<ElemType> *temp = head;
while(temp)
{
if(temp->data == e)
{
pos = temp;
return true;
}
temp = temp->next;
}
//dont find the revelent element
return false;
}
分析与总结
- 这个方法确实很重要,外界传入相关的存储索引的地址,函数根据指针改变外界的该内存的地址
- 不要忘记了迭代条件。每次都等到不停的调试了之后才发现没有迭代条件
PriorElem(ElemType cur_e, ElemType &pri_e)
/*
description:return the value which is previous to the element you input
parameters:cur_e :the current element you input
&pri_e:the reference to the element privious to the current element
*/
template<class ElemType>
bool LinkList<ElemType>::PriorElem(ElemType cur_e, ElemType &pri_e)
{
//traverse the whole linklist
LinkNode<ElemType> *temp = head;
while(temp->next)
{
if(temp->next->data == cur_e)
{
pri_e = temp->data;
return true;
}
temp = temp->next;
}
//dont find the revelent element
return false;
}
分析与总结
- 不要忘记了迭代条件,不要每次都等到调试之后才发现
- 这个方法完全是在查找特定的元素的方法上改写的,将temp改成了temp->next
NextElem(LinkNode *p, ElemType &e)
/*
description:return pointer which is next to the element whose value is e
parameters:p:the pointer pointing to the element next to the target
e:the value of the target element
*/
template<class ElemType>
bool LinkList<ElemType>::NextElem(LinkNode<ElemType> *p, ElemType &e)
{
//find the eligible element and return the index
if(LocateElem(e,p))
{
p = p->next;
return true;
}
else
{
return false;
}
}
分析与总结
- 其实可以完全不使用中间变量的,直接用p就可以了
- 这个函数设置的时候有一点问题,那就是既然要获得指向对应结点的指针,就该传入指针的指针,因为你修改的是指针的,现在传入的是指针,外界还得有一个实体才能继续创建
ListTraverse() const
/*
description:traverse the whole linklist
*/
template<class ElemType>
bool LinkList<ElemType>::ListTraverse()const
{
//caution£ºyou have refer the index of the next element of the first
//so,you should judge if the element does exist
if(ListEmpty())
{
return false;
}
LinkNode<ElemType> *temp = head->next;
while(temp)
{
cout<<temp->data<<" ";
temp = temp->next;
}
cout<<endl;
}
分析与总结
- 无
测试代码以及效果
#include <iostream>
#include "Linkedlist.h"
using namespace std;
int main()
{
//testment1
cout<<"simply insert some elements and try to show the linklist"<<endl;
LinkList<int> test;
int a[] = {1,2,3,4,5,6};
cout<<"insert at the start"<<endl;
test.CreateList_Head(6,a);
test.ListTraverse();
cout<<"insert at the end "<<endl;
LinkList<int> test2;
test2.CreateList_Tail(6,a);
test2.ListTraverse();
cout<<"testment 1 ends"<<'\n'<<endl;
//testment2
cout<<"testment of deleting the first element of the linklist"<<endl;
int result = 0;
test2.DelFirst(result);
test2.ListTraverse();
cout<<"the deleted the element is "<<result<<endl;
cout<<"testment of deleting the element whose position is x"<<endl;
result = test2.ListDelete(3);
cout<<"the deleted list is:";
test2.ListTraverse();
cout<<"the deleted element is :"<<result<<" the position is "<<3<<endl;
cout<<"testment2 ends"<<'\n'<<endl;
//testment3
cout<<"testment of get the privous or the next element of the target"<<endl;
int targetIndex = 4;
int privious ;
test.PriorElem(targetIndex,privious);
cout<<"the element before the target is:"<<privious<<endl;
LinkNode<int> *temp = new LinkNode<int>();
test.NextElem(temp,targetIndex);
cout<<"the element next the target is:"<<temp->data<<endl;
cout<<"testment3 ends"<<endl;
return 0;
}
分析与总结
-
一定要记得将类的声明文件和实现文件放到一个文件夹中
-
出现编译陷入死循环的情况,请浏览我的连接
编译陷入死循环 -
为什么不能写一个,测试一个,一下子写那么多,一个一个测试,测试地我要自闭了
-
总是因为判定是否为空,写反了,导致程序运行不起来,致使只能一点一点地去排序。是否为空,一种是使用指针是否为空去判定,另外一种是使用自己写的empty,记住你自己地返回结果
-
一个一个函数的写,然后一个一个函数的测试
异常处理现场
missing template arguments before ‘*’ token
- 使用模板类的定义变量的时候,“<…>”要写上,
error’ElemType’ was not declared in this scope
- 在模板外声明的时候忘记加上对应template的声明
error:cannot convert ‘LinkNode’ to ‘int’ in intialization
- 类型转换错误,head是LinkNode节点类,不是ElemType类
想要源码或者讨论学习,可以留言或者加扣扣651378276