链表ADT设计模板的简单应用——链表的ADT的实现C++版

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
      在这里插入图片描述
  • 对于这种模板类,就很奇怪,因为不知道具体的类型,也不知道如何重置对应的头结点。也不知道其中的特殊表

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
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页