实验4.1 链表实现
要求
- 封装链表类,链表迭代器类
- 不得使用与链表实现相关的STL
- 链表类需提供操作
- 在指定位置插入元素
- 删除指定元素
- 搜索链表中是否有指定元素
- 原地逆置链表
- 输出链表
描述
第一行两个整数 N 和 Q。
第二行 N 个整数,作为节点的元素值,创建链表。
接下来 Q 行,执行各个操作,具体格式如下:
- 插入操作 :
1 idx val
,在链表的idx
位置插入元素val
; - 删除操作 :
2 val
,删除链表中的val
元素。若链表中存在多个该元素,仅删除第一个。若该元素不存在,输出-1
; - 逆置操作 :
3
,原地逆置链表; - 查询操作 :
4 val
,查询链表中的val
元素,并输出其索引。若链表中存在多个该元素,仅输出第一个的索引。若不存在该元素,输出-1
; - 输出操作 :
5
,使用 链表迭代器 ,输出当前链表索引与元素的异或和: f ( chain ) = ∑ i = 0 n − 1 i ⊕ chain [ i ] , n = len(chain) f(\text{chain}) = \sum_{i=0}^{n-1}i\oplus \text{chain}[i], n=\text{len(chain)} f(chain)=∑i=0n−1i⊕chain[i],n=len(chain);
思路与探讨
线性表链式描述
整体思路描述
-
封装链表类,链表迭代器类;
-
提供链表类相关操作函数:在指定位置插入元素,删除指定元素,搜索链表中是否有指定元素,原地逆置链表,输出链表;
-
最后在主函数用枚举实现相关操作。
类chain函数的具体思路
构造函数
初始化建一个空表
复制构造函数
- 若链表
theList
为空:直接将首节点置空 - 若链表
theList
不为空- 由
sourceNode
依次指向theList
的节点 - 复制完成
firstNode
后 - 开一个
targetNode
作为*this
的最后一个节点 - 借助
sourceNode
和targetNode
两个节点依次进行复制和后移
- 由
析构函数
重复删除链表的首节点
成员类iterator
- 具备操作符:*、->、++、==、!
- *操作符,获得迭代器所指的数据
- ->操作符,获得迭代器所指数据的地址
- 前++、后++:迭代器移到下(后)一个元素
- ==、!=:判断是否相等
- 为类chain增加:
- begin():返回指向线性表首节点的指针
- end():返回指向线性表尾节点的下一个位置
方法get
返回索引为theIndex
的元素
- 移动到所需要的节点
- 找到后由节点指向元素并返回
方法indexOf
返回元素theElement首次出现时的索引 ,如果其不在表中,返回-1
- currentNode从firstNode开始,并标记currentNode的索引
- 循环查找元素
- 确认是否找到
方法erase
情况(1):删除非空表的第0个元素(theIndex = 0
)
- 首节点的位置
deleteNode
;firstNode
移到第1个节点 - 释放
deleteNode
节点所占用的存储空间 listSize
减1
情况(2):删除索引为theIndex
元素步骤
- 找到第
theIndex-1
和第theIndex
个节点的位置p
和deleteNode
; - 第
theIndex
个节点的后继成为第theIndex-1
个节点的后继 - 释放
deleteNode
节点所占用的存储空间 listSize
减1
方法insert
情况(1):在第theIndex = 0
位置插入
- 申请新节点
- 新节点中元素:theElement
- 新节点中指针:firstNode
- 新节点成为首节点
- listSize加1
情况(2):在第theIndex > 0
位置插入
-
找到第theIndex-1个元素的位置p
-
申请新节点
- 新节点中的元素:theElement
- 新节点中的指针:p的后继(p->next)
-
新节点成为p的后继
-
listSize加1
方法reverse
借助pastNode
, currentNode
, futureNode
依次改变每个节点的指向,从而实现逆置。
浅录了个视频梳理了一下流程
方法sum
借助迭代器逐步遍历计算即可
若已看懂思路,试着自己写~
实现代码
#include<iostream>
using namespace std;
//结构chainNode
template<class T>
struct chainNode
{
//数据成员
T element;
chainNode<T> *next;
//方法
chainNode() {}
chainNode(const T &element)
{
this->element = element;
}
chainNode(const T &element,chainNode<T>*next)
{
this->element = element;
this->next = next;
}
};
//定义链表类
template<class T>
class chain
{
public:
//构造函数
chain()
{
firstNode = NULL;
listSize = 0;
}
//复制构造函数
chain(const chain<T>& theList)
{
listSize = theList.listSize;
//情况(1)链表theList为空
if(listSize == 0)
{
firstNode = NULL;
return;
}
//情况(2)链表theList为非空
//要复制链表theList的节点
chainNode<T>*sourceNode = theList.firstNode;
//要复制链表theList的首元素
firstNode = new chainNode<T>(sourceNode->element);
//移向要复制的下一个节点
sourceNode = sourceNode->next;
//当前链表*this的最后一个节点
chainNode<T>*targetNode = firstNode;
//复制剩余元素
while(sourceNode != NULL)
{
//复制
targetNode->next = new chainNode<T>(sourceNode->element);
//后移
targetNode = targetNode->next;
sourceNode = sourceNode->next;
}
//链表结束 ,最后一个节点指向Null
targetNode->next = NULL;
}
//析构函数
~chain()
{
while(firstNode)
{//删除首节点
chainNode<T>*next = firstNode->next;//链式一节一节往后删
delete firstNode;
firstNode = next;
}
}
//链表的成员类iterator
class iterator
{
public:
//构造函数
iterator(chainNode<T>*theNode = NULL)
{node = theNode;}
//解引用操作符
T& operator*() const {return node->element;}
T* operator->() const {return &node->element;}
//迭代器加法操作
iterator& operator++()
{node = node->next; return *this;}//前加
iterator operator++(int)
{iterator old = *this;node = node->next;return old;}//后加
//相等检验
bool operator != (const iterator right) const
{return node != right.node;}
bool operator == (const iterator right) const
{return node == right.node;}
protected:
chainNode<T>*node;//指向表节点的指针
};
//返回指向线性表首元素的指针
iterator begin() {return iterator(firstNode);}
//返回指向线性表尾节点的下一个位置
iterator end() {return iterator(NULL);}
//返回索引为theIndex的元素
T& get(int theIndex) const
{
//移向所需要的节点
chainNode<T>*currentNode = firstNode;
for(int i = 0;i<theIndex;i++)
currentNode = currentNode->next;
//找到后由节点指向元素并返回
return currentNode->element;
}
//返回元素theElement首次出现时的索引
int indexOf(const T &theElement) const
{
//从firstNode开始
chainNode<T>*currentNode = firstNode;
//标记currentNode的索引
int index = 0;
//查找元素
while(currentNode != NULL && currentNode->element != theElement)
{
currentNode=currentNode->next;
index++;
}
//确定是否找到所需的元素
if(currentNode==NULL)
return -1;
else
return index;
}
//删除索引为theIndex的元素
void erase(int theIndex)
{
chainNode<T>*deleteNode;
//情况(1):删除链表的首节点
if(theIndex == 0)
{
deleteNode = firstNode;
firstNode = firstNode->next;
}
//情况(2):删除索引为theIndex(≠0)元素
else
{
//找到第theIndex-1和第theIndex个节点的位置p和deleteNode
chainNode<T>*p = firstNode;
for(int i = 0;i < theIndex - 1;i++)
p = p->next;
deleteNode = p->next;
//第theIndex个节点的后继成为第theIndex-1个节点的后继
p->next = p->next->next;
}
//listSize减1
listSize--;
//释放deleteNode节点所占用的存储空间
delete deleteNode;
}
//在索引theIndex处插入元素theElement
void insert(int theIndex,const T &theElement)
{
//情况(1)在链表头插入
if(theIndex == 0)
firstNode = new chainNode<T>(theElement,firstNode);
//情况(2)在第theIndex>0位置插入
else
{
//找到第theIndex-1个元素的位置p
chainNode<T>*p = firstNode;
for(int i = 0;i < theIndex-1;i++)
p = p->next;
//申请新节点(其元素为theElement,指针为p的后继)
//并让新节点成为p的后继
p->next=new chainNode<T>(theElement,p->next);
}
//listSize加1
listSize++;
}
//输出链表
void outPut(ostream &out) const
{
//把链表放入输出流
for(chainNode<T>*currentNode = firstNode;
currentNode != NULL;
currentNode = currentNode->next)
out<<currentNode->element<<" ";
}
//逆置链表
void reverse()
{
if(firstNode == NULL||firstNode->next == NULL) return;//此时无需逆转
chainNode<T>*pastNode = firstNode;
chainNode<T>*currentNode = pastNode->next;
chainNode<T>*futureNode = currentNode->next;
firstNode->next = NULL;
while(futureNode != NULL)
{
currentNode->next = pastNode;
pastNode = currentNode;
currentNode = futureNode;
futureNode = futureNode->next;
}
currentNode->next = pastNode;
firstNode = currentNode;
}
//计算链表索引与元素的异或和
int sum()
{
int S = 0,index = 0;
iterator beginning = begin();
while(index < listSize)
{
S += index^(*beginning);
beginning++;
index++;
}
return S;
}
private:
chainNode<T>*firstNode; //链表的头指针,即指向链表第一个节点的指针
int listSize; //链表的长度,即线性表元素的个数
};
int main()
{
int N,Q;
cin>>N>>Q; //输入N和Q
int *a=new int[N];
for(int i=0;i<N;i++) cin>>a[i]; //输入节点元素值
chain<int>Array; //创建链表
for(int i=0;i<N;i++) Array.insert(i,a[i]); //插入各节点
delete []a;
for(int i=0;i<Q;i++) //执行Q次操作
{
int choice;
cin>>choice; //输入操作序号
int idx,val,index;
switch(choice)
{
case 1:
cin>>idx>>val; //输入1时,输入idx与val
Array.insert(idx,val); //在idx处插入元素val
break;
case 2:
cin>>val; //输入2时,输入val
index=Array.indexOf(val); //查找val的位置
if(index!=-1) Array.erase(index); //若存在,删除该元素
else cout<<"-1"<<endl; //若不存在,输出-1
break;
case 3:
Array.reverse(); //输入3时,原地逆置链表
break;
case 4:
cin>>val; //输入4时,输入val
cout<<Array.indexOf(val)<<endl; //查询val的位置
break;
case 5:
cout<<Array.sum()<<endl; //输入5时,输出异或和
break;
default:
break;
}
}
return 0;
}