HNU数据结构与算法分析-实验二---现在判断输入的字符串是否是回文

本篇代码包括实验报告与源码,

最终得分为92/100

下面我将先给出实验报告,再附上源码,

其中算法的精髓我在报告中彩色标注了,如果有不理解的同学可以先看看,我觉得应该会有帮助

最后,如果感觉有帮助的话还请点个赞,你的重要支持是我继续创作的动力。
 

数据结构与算法分析-实验二

【问题描述】

回文,即首尾对称的字符串。现在判断输入的字符串是否是回文,如果是,输出“yes”,如果不是,输出”no”。

要求:基于ADT实现,使用单链表存储输入的字符串。

【输入形式】

单行输入由数字及字符组成的字符串,字符串长度不大于1000。

【输出形式】

输出yes表示是回文,输出no表示不是回文。

【样例输入】   

sdsfegrhglp

【样例输出】  

no

【样例说明】

  sdsfegrhglp不是回文,输出no。

【样例输入】 

helloolleh

【样例输出】

yes

【样例说明】

  helloolleh是回文,输出yes。

【样例输入】

 asas232sasa

【样例输出】

 yes

【样例说明】

  asas232sasa是回文,输出yes。

【代码提交及评分要求】

源代码请提交工程压缩包,压缩包内至少包含以下三个文件:

1)XXX.h:线性表ADT的定义和声明

2)XXX.h:线性表ADT的实现

3)XXXXX.cpp:主程序

(要求基于ADT实现,否则计0分。)

我会先给出实验报告的部分,再给出具体的实现代码。

以下是实验报告的部分

1.问题分析

l1.1处理的对象(数据)

处理的对象为数字及字符组成的字符串也就是说,我们可以用char来解决,因此,模板类可以赋为char。处理对象以字符串的形式读入,并通过下标访问相应位置的字符。

l1.2实现的功能

    判断输入的字符串是否是回文(回文,即首尾对称的字符串),也就是判断是否=该字符串是否对称。

l1.3处理后的结果如何显示

    处理后的结果输出yes表示是回文,输出no表示不是回文。

l1.4请用题目中样例,详细给出样例求解过程。

【样例输入1】sdsfegrhglp

  1. 读入字符串sdsfegrhglp,存入单链表中;
  2. 当前指针指向头,返回0位置值为“s”;
  3. 当前指针指向尾,返回末位置值为“p”;
  4. 判断s不等于p,触发flag=false,并跳出判断循环;
  5. Flag=false,输出no,不是回文。

【样例输入2】 helloolleh

1、读入字符串helloolleh,存入单链表中;

2、当前指针指向头,返回0位置值为“h”;

3、当前指针指向尾,返回末位置值为“h”;

4、判断h等于h,继续下一循环;

5、重复2-3步骤多次,直至o等于o,此时两指针相邻,结束循环;

6、flag=true,输出yes,是回文。

【样例输入3】 asas232sasa

1、读入字符串helloolleh,存入单链表中;

2、当前指针指向头,返回0位置值为“a”;

3、当前指针指向尾,返回末位置值为“a”;

4、判断a等于a,继续下一循环;

5、重复2-3步骤多次,直至两指针重合,结束循环;

6、flag=true,输出yes,是回文。

2.数据结构和算法设计

2.1抽象数据类型设计

template    class  List  {  //  List  ADT

private:

        void  operator  =(const  List&)  {}       //  Protect  assignment

        List(const  List&)  {}                //  Protect  copy  constructor

public:

        List()  {}                    //构造函数

        virtual  ~List()  {}            //析构函数

        virtual  void  insert(const  E&  item)  =  0;//在当前位置插入

        virtual  void  moveToStart()  =  0;    //将当前指针移到头指针位置

        virtual  void  prev()  =  0;     //将当前指针向前移动一位

        virtual  void  next()  =  0;     //将当前指针向后移动一位

        virtual  int  length()  const  =  0;   //返回链表长度

        virtual  void  moveToPos(int  pos)  =  0; //将当前指针移到pos位置

        virtual  const  E& getValue() const = 0; //返回当前指针指向结点的值

};

2.2物理数据对象设计(不用给出基本操作的实现)

   本实验中用到的物理存储结构为单链表结构,设计是用一curr指针指向当前位置,同时保存头指针和尾指针的位置。另外还有char型数据域用于保存当前位置的字母值。

template    class  Link  {

public:

        E  element;               //当前节点的值

        Link  *next;               //指向下一节点的指针

        Link(const  E&  elemval,  Link*  nextval  =NULL);

        Link(Link*  nextval  =NULL);

};

template    class  LList:  public  List  {

private:

        Link*  head;            //链表头指针

        Link*  tail;              //链表尾指针

        Link*  curr;             //当前指针

        int  cnt;                   //链表长度

        void  init();                //初始化

public:

        LList(int  size=100);          //构造函数

        ~LList();                    //析构函数

        void  removeall();           //析构函数调用的清空操作

        void  insert(const  E&  it);  //在当前位置插入

        void  moveToStart()         //将当前指针移到头指针位置

        void  prev()                //将当前指针向前移动一位

        void  next()                //将当前指针向后移动一位

        int  length()                //返回链表长度

        void  moveToPos(int  pos)   //将当前指针移到pos位置

        const  E&  getValue()  const //返回当前指针指向结点的值

};

2.3算法思想的设计

两个指针left与right,分别从头与尾两端同时向中间前进,每次访问一个节点,并比较left与right所对应的值是否一致。初始一个flag为true,一旦发现有不一样的地方就将这个flag赋为false,当两个指针交换位置或者重合时结束循环,退出并检查flag是否为true,若true则表示是回文,反之就不是回文。

考虑到这个单链表左右指针向中间进近的步长相同,可以用控制变量i来模拟这种进近。i限制在0到总长度的一半之间。

2.4关键功能的算法步骤(不能用源码)

如何用单指针实现两端取值

1、指针在头,char型left取0号位置所对应的值

HNU数据结构与算法分析-实验二---现在判断输入的字符串是否是回文_第1张图片

2.指针跳至尾,char型right取末位置所对应的值

HNU数据结构与算法分析-实验二---现在判断输入的字符串是否是回文_第2张图片

3、重复以上步骤多次,直至触发最终终止条件

3、算法性能分析

3.1时间复杂度 

该算法的时间复杂度是O(n^{2}),因为主要矛盾是关键代码函数,在第一层for (int i=0;i<=n;i++)上,积累了n的复杂度,然后mylist.moveToPos(i);这里积累了n的复杂度,与此并列的mylist.moveToPos(mylist.length()-1-i);也积累了n的复杂度,简单来说n*n,为O()的复杂度。

严格意义来说mylist.insert(str[i])是1,嵌套for循环,是n。再加上上面主要矛盾。可得,

T=c_{1}n+c_{2}n^{2}=O(n^{2})

3.2空间复杂度

该算法的空间复杂度是O(n)

4.不足与反思

  因为实际上我们只能操纵一个指针,所以上述的做法实际上是模拟了两个指针。那我们不妨考虑另外一种形式,建立两个链表,存同样的东西,然后一个链表操纵指针从前往后,另外一个链表操纵指针由后往前,这样就可以在物理层面上实现两个指针的操作,在思维上理解起来也会快一点。

    实际上,如果不考虑链表的实现方式,本题其实用线性表的顺序表来做是非常简单的,可以实现O(n)的时间复杂度,通俗来说就是用数组来实现。这样会很简单,但可能这是教师想要锻炼我们的链表能力。

以下是源代码的部分

这是主函数main.cpp,即3)XXXXX.cpp:主程序

#include 
#include "list.h"
#include "alist.h"
using namespace std;

int main()
{
	LList mylist;
	bool flag=true;
	string str;
	cin>>str;
	int len=str.length();
	int n=len/2;
	mylist.moveToStart();
    for(int i=0;i

这是ADT部分,即1)XXX.h:线性表ADT的定义和声明

#ifndef LIST
#define LIST
template    class  List  {  //  List  ADT
private:
        void  operator  =(const  List&)  {}            //  Protect  assignment
        List(const  List&)  {}                      //  Protect  copy  constructor
public:
        List()  {}                    //  Default  constructor
        virtual  ~List()  {}  //  Base  destructor
        virtual  void  insert(const  E&  item)  =  0;
        virtual  void  moveToStart()  =  0;
        virtual  void  prev()  =  0;
        virtual  void  next()  =  0;
        virtual  int  length()  const  =  0;
        virtual  void  moveToPos(int  pos)  =  0;
        virtual  const  E&  getValue()  const  =  0;
};
#endif

这是ADT的实现部分,即2)XXX.h:线性表ADT的实现

#include "list.h"
#include 

using namespace std;

//断言工具函数:如果val为假则输出s然后中断程序
void  Assert(bool  val,string  s){
        if(!val){
                cout<<"断言失败:"<  class  Link  {
public:
        E  element;            //  Value  for  this  node
        Link  *next;                //  Pointer  to  next  node  in  list
        //  Constructors
        Link(const  E&  elemval,  Link*  nextval  =NULL)  {
        	element=elemval;
        	next=nextval;
        }
        Link(Link*  nextval  =NULL)  {      
			next=nextval;
        }
};

template    class  LList:  public  List  {
private:
        Link*  head;              //  Pointer  to  list  header
        Link*  tail;              //  Pointer  to  last  element
        Link*  curr;              //  Access  to  current  element
        int  cnt;                              //  Size  of  list

        void  init()  {                //  Intialization  helper  method      
			curr=tail=head=new Link;
			cnt=0;
        }

public:
        LList(int  size=100)  {
                init();        //  Constructor
        }
        ~LList()  {
                removeall();        //  Destructor
        }

        void  removeall()  {      
			while (head!=NULL)
			{			
				curr=head;
				head=head->next;
				delete curr;
			}
        }
        
        void  insert(const  E&  it)  {
			curr->next=new Link(it,curr->next);
			if (tail==curr) tail=curr->next;
			cnt++;
        }

        void  moveToStart()  {      
			curr=head;
        }

        void  prev()  { 
			if (curr==head) return;
			Link* temp=head;
			while (temp->next!=curr) temp=temp->next;
			curr=temp;
        }

        void  next()  {
			if ( curr != tail ) curr=curr->next;
        }

        int  length()  const    {       
			return cnt;
        }

        void  moveToPos(int  pos)  {
                Assert((pos>=0)&&(pos<=cnt),"Position  out  of  range");
                curr=head;
				for (int i=0;inext;
        }

        const  E&  getValue()  const  {
                Assert(curr->next  !=  NULL,  "No  value");
                return curr->next->element;
        }
};

最后的文件夹中需要包括以上三个部分。

你可能感兴趣的:(c++)