本篇代码包括实验报告与源码,
最终得分为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分。)
以下是实验报告的部分
处理的对象为数字及字符组成的字符串,也就是说,我们可以用char来解决,因此,模板类可以赋为char。处理对象以字符串的形式读入,并通过下标访问相应位置的字符。
判断输入的字符串是否是回文(回文,即首尾对称的字符串),也就是判断是否=该字符串是否对称。
处理后的结果输出yes表示是回文,输出no表示不是回文。
【样例输入1】sdsfegrhglp
【样例输入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,是回文。
template
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; //返回当前指针指向结点的值
};
本实验中用到的物理存储结构为单链表结构,设计是用一curr指针指向当前位置,同时保存头指针和尾指针的位置。另外还有char型数据域用于保存当前位置的字母值。
template
public:
E element; //当前节点的值
Link *next; //指向下一节点的指针
Link(const E& elemval, Link* nextval =NULL);
Link(Link* nextval =NULL);
};
template
private:
Link
Link
Link
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 //返回当前指针指向结点的值
};
两个指针left与right,分别从头与尾两端同时向中间前进,每次访问一个节点,并比较left与right所对应的值是否一致。初始一个flag为true,一旦发现有不一样的地方就将这个flag赋为false,当两个指针交换位置或者重合时结束循环,退出并检查flag是否为true,若true则表示是回文,反之就不是回文。
考虑到这个单链表左右指针向中间进近的步长相同,可以用控制变量i来模拟这种进近。i限制在0到总长度的一半之间。
如何用单指针实现两端取值
1、指针在头,char型left取0号位置所对应的值
2.指针跳至尾,char型right取末位置所对应的值
3、重复以上步骤多次,直至触发最终终止条件
该算法的时间复杂度是O(),因为主要矛盾是关键代码函数,在第一层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。再加上上面主要矛盾。可得,
该算法的空间复杂度是O(n)
因为实际上我们只能操纵一个指针,所以上述的做法实际上是模拟了两个指针。那我们不妨考虑另外一种形式,建立两个链表,存同样的东西,然后一个链表操纵指针从前往后,另外一个链表操纵指针由后往前,这样就可以在物理层面上实现两个指针的操作,在思维上理解起来也会快一点。
实际上,如果不考虑链表的实现方式,本题其实用线性表的顺序表来做是非常简单的,可以实现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;
}
};
最后的文件夹中需要包括以上三个部分。