笔记|第六章:链式描述
A:链表实现
题目
要求
- 封装链表类,链表迭代器类
- 链表类需提供操作:在指定位置插入元素,删除指定元素,搜索链表中是否有指定元素,原地逆置链表,输出链表
- 不得使用与链表实现相关的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);
样例 1
Input
10 10
6863 35084 11427 53377 34937 14116 5000 49692 70281 73704
4 6863
1 2 44199
5
4 21466
1 6 11483
5
4 34937
5
4 6863
1 10 18635
Output
0
398665
-1
410141
5
410141
0
限制
1s
数据范围
思路
- 需要封装链表类、链表迭代器类 (基本代码在笔记中有)
- 结构体
chainNode
:来定义节点的数据类型- 数据域
element
、链域*next
- 构造函数
- 数据域
- 类
chain
:实现关于链表的各种操作- 构造函数:头节点置空、长度为0
- 复制构造函数:首先复制链表长度,分两种情况
- 链表为空:头节点NULL
- 链表不为空:先初始化第一个节点,然后利用循环逐步复制下一个节点
- 析构函数:利用循环不断删除头节点
- 链表迭代器类:作为类chain的成员类
- 结构体
//非空
//初始化第一个节点
chainNode<T>* sourceNode = lis.firstNode;//要复制的节点
firstNode = new chainNode<T>(sourceNode->element);//要复制的首元素
sourceNode = sourceNode->next;//移向要复制额下一个节点
chainNode<T>* targetNode = firstNode;//当前链表的最后一个节点
//移向要复制的下一个节点
sourceNode = sourceNode->next;
while (sourceNode != NULL) {
targetNode->next = new chainNode<T>(sourceNode->element);//复制
targetNode = targetNode->next;//后移
sourceNode = sourceNode->next;
}
targetNode->next = NULL;//链表结束,置成空指针
关于链表类提供的操作
- 查询
indexOf
:参数theElement
- 从头节点开始,依次往后搜索,并用变量标记当前查询到的位置
- 终止条件:指针指向表尾(搜索指针为空
currentNode==NULL
) or 找到元素(不为空) - 判断属于哪种情况,返回对应的数
- 插入
insert
:idx,val
- 两种情况:在表头插入、在表中间插入
- 表头插入:更新头节点
firstNode = new chainNode<T>(theElement, firstNode);
- 表中间插入:
- 找到新元素的前驱节点
- 让前驱节点的next指向新元素
- 链表长度+1
- 删除
erase
- 两种情况:删除头节点、删除其他节点
- 删除头节点:头节点指向头结点的下一个节点
- 删除其他节点:
- 找到前驱节点
deleteNode=p->next
- 让前驱节点的下一个节点,指向删除元素后面的节点
- 删除deleteNode,链表长度-1
- 输出
Output
:利用链表迭代器,按照公式计算并返回 - 逆置
reverse
- 主函数:在删除部分要注意判断返回的是否是-1
AC代码
#include<iostream>
using namespace std;
//结构chainNode
template<class T>
struct chainNode {
//数据成员
T element;//节点的数据域,存储元素
chainNode<T>* next;//链域,存储下一个节点的指针
chainNode() {};
chainNode(const T& e) :element(e) {}
chainNode(const T& e, chainNode<T>* n) :element(e), next(n) {}
};
//chain链表类
template<class T>
class chain
{
public:
//链表的成员类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;//指向表节点的指针
};
//构造函数、复制构造函数、析构函数
chain();
chain(const chain<T>& lis);
~chain();
iterator begin() { return iterator(firstNode); }
iterator end() { return iterator(NULL); }
int indexOf(const T& theElement) const;//查询
void insert(int theIndex, const T& theElement);//插入
void erase(int theIndex);//删除
void reverse();//逆置
int Output();//输出
protected:
//指向链表中第一个节点的指针
chainNode<T>* firstNode;
//线性表的元素个数
int listSize;
};
//构造函数
template<class T>
chain<T>::chain() {
firstNode = NULL;
listSize = 0;
}
//复制构造函数
template<class T>
chain<T>::chain(const chain<T>& lis) {
listSize = lis.listSize;
//lis为空
if (listSize == 0) {
firstNode = NULL;
return;
}
//非空
//初始化第一个节点
chainNode<T>* sourceNode = lis.firstNode;//要复制的节点
firstNode = new chainNode<T>(sourceNode->element);//要复制的首元素
sourceNode = sourceNode->next;//移向要复制额下一个节点
chainNode<T>* targetNode = firstNode;//当前链表的最后一个节点
//移向要复制的下一个节点
sourceNode = sourceNode->next;
while (sourceNode != NULL) {
targetNode->next = new chainNode<T>(sourceNode->element);//复制
targetNode = targetNode->next;//后移
sourceNode = sourceNode->next;
}
targetNode->next = NULL;//链表结束,置成空指针
}
//析构函数
template<class T>
chain<T>::~chain() {
while (firstNode) {
chainNode<T>* nextNode = firstNode->next;
delete firstNode;
firstNode = nextNode;
}
}
//查询
template<class T>
int chain<T>::indexOf(const T& theElement) const {
//从firstNode开始
chainNode<T>* currentNode = firstNode;
int index = 0;
//指向表尾或找到元素
while (currentNode != NULL && currentNode->element != theElement) {
currentNode = currentNode->next;
index++;
}
//判断属于哪种情况
if (currentNode == NULL)
return -1;
else
return index;
}
//插入
template<class T>
void chain<T>::insert(int theIndex, const T& theElement) {
//在表头插入
if (theIndex == 0)
firstNode = new chainNode<T>(theElement, firstNode);
else {
chainNode<T>* p = firstNode;
for (int i = 0; i < theIndex - 1; i++)
p = p->next;//找到新元素的前驱
p->next = new chainNode<T>(theElement, p->next);
}
listSize++;
}
//删除
template<class T>
void chain<T>::erase(int theIndex) {
chainNode<T>* deleteNode = firstNode;
//删除头节点
if (theIndex == 0) {
deleteNode = firstNode;
firstNode = firstNode->next;
}
else {
chainNode<T>* p = firstNode;
//找到前驱节点
for (int i = 0; i < theIndex - 1; i++) {
p = p->next;
}
deleteNode = p->next;
p->next = p->next->next;
}
listSize--;
delete deleteNode;
}
//逆置
template<class T>
void chain<T>::reverse() {
if (firstNode == NULL || firstNode->next == NULL) return;
chainNode<T>* pastNode = firstNode;//用来遍历原链表的指针
chainNode<T>* currentNode = pastNode->next;//遍历原链表指针时,用来保存pastNode下一个节点的指针
chainNode<T>* nextNode = currentNode->next;
firstNode->next = NULL;
while (nextNode != NULL) {
currentNode->next = pastNode;
pastNode = currentNode;
currentNode = nextNode;
nextNode = nextNode->next;
}
currentNode->next = pastNode;
firstNode = currentNode;
}
//输出
template<class T>
int chain<T>::Output() {
int count = 0, index = 0;
iterator beginning = begin();
while (index < listSize) {
count += index ^ (*beginning);
beginning++;
index++;
}
return count;
}
int main() {
int N, Q;
cin >> N >> Q;
int* a = new int[N];
for (int i = 0; i < N; i++)
cin >> a[i];
chain<int>List;
for (int i = 0; i < N; i++)
List.insert(i, a[i]);
delete[]a;
for (int i = 0; i < Q; i++) {
int choice, idx, val, index;
cin >> choice;
switch (choice)
{
case 1:
cin >> idx >>val;
List.insert(idx, val);
break;
case 2:
cin >> val;
index = List.indexOf(val);
if (index != -1) List.erase(index);
else cout << "-1" << endl;
break;
case 3:
List.reverse();
break;
case 4:
cin >> val;
cout << List.indexOf(val) << endl;
break;
case 5:
cout << List.Output() << endl;
break;
default:
break;
}
}
return 0;
}
B:链表合并
题目
要求
- 使用题目 链表实现 中实现的链表类、迭代器类完成本题
- 不得使用与题目实现相关的STL
描述
给定两组整数序列,你需要分别创建两个有序链表,使用链表迭代器实现链表的合并,并分别输出这三个有序链表的索引与元素的异或和。
Note: 给定序列是无序的,你需要首先得到一个有序的链表
格式
输入
第一行两个整数 N 和 M。
第二行 N 个整数,代表第一组整数序列。
第三行 M 个整数,代表第二组整数序列。
输出
三行整数。分别代表第一组数、第二组数对应的有序链表与合并后有序链表的索引与元素的异或和。
链表异或和的计算方式如下:
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=0∑n−1i⊕chain[i],n=len(chain)
样例 1
输入
3 0
3 1 2
输出
5
0
5
限制
1s
数据范围
思路
- 链表类、迭代器类等与A题相同,不做赘述
- 增加排序函数、合并链表的函数
- 排序函数:有两种方法 (这里选择的第二种)
- 在输入的时候先将序列用数组存储,并进行排序,然后将有序序列进行插入链表
- 在输入的时候直接插入链表,然后在链表内进行排序操作
- 可选排序函数很多(一开始用的桶排序但是运行失败了)
- 冒泡排序
bubbleSort
:利用两个相邻的节点进行比较,并逐步后移
- 合并
merge
:对有序序列进行合并(双指针法)- 两个迭代器,用于遍历两个链表;一个计数器,用来记录合并后链表中的位置
- 由于链表长度不清楚,因此可能会出现一个链表已经遍历完,另一个链表还没有遍历完的情况。对于没遍历完的链表,直接插入到链表末尾即可
- 当两个链表都没遍历完时:比较当前元素大小,并将小的元素先插入,插入的元素所属链表的迭代器要后移,未插入的不变。(只移动取值的指针)
- 当至少有一个链表已经遍历完成,判断是否有没遍历完的链表,直接插入到表尾。
AC代码
#include<iostream>
using namespace std;
//结构chainNode
template<class T>
struct chainNode {
//数据成员
T element;//节点的数据域,存储元素
chainNode<T>* next;//链域,存储下一个节点的指针
chainNode() {};
chainNode(const T& e) :element(e) {}
chainNode(const T& e, chainNode<T>* n) :element(e), next(n) {}
};
//chain链表类
template<class T>
class chain
{
public:
//链表的成员类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;//指向表节点的指针
};
//构造函数、复制构造函数、析构函数
chain(int initial);
chain(const chain<T>& lis);
~chain();
iterator begin() { return iterator(firstNode); }
iterator end() { return iterator(NULL); }
int indexOf(const T& theElement) const;//查询
void insert(int theIndex, const T& theElement);//插入
void erase(int theIndex);//删除
void reverse();//逆置
int Output();//输出
void bubbleSort();//冒泡排序
void merge(chain<T>& a, chain<T>& b);//合并
protected:
//指向链表中第一个节点的指针
chainNode<T>* firstNode;
//线性表的元素个数
int listSize;
};
//构造函数
template<class T>
chain<T>::chain(int initial) {
firstNode = NULL;
listSize = 0;
}
//复制构造函数
template<class T>
chain<T>::chain(const chain<T>& lis) {
listSize = lis.listSize;
//lis为空
if (listSize == 0) {
firstNode = NULL;
return;
}
//非空
//初始化第一个节点
chainNode<T>* sourceNode = lis.firstNode;//要复制的节点
firstNode = new chainNode<T>(sourceNode->element);//要复制的首元素
sourceNode = sourceNode->next;//移向要复制额下一个节点
chainNode<T>* targetNode = firstNode;//当前链表的最后一个节点
//移向要复制的下一个节点
sourceNode = sourceNode->next;
while (sourceNode != NULL) {
targetNode->next = new chainNode<T>(sourceNode->element);//复制
targetNode = targetNode->next;//后移
sourceNode = sourceNode->next;
}
targetNode->next = NULL;//链表结束,置成空指针
}
//析构函数
template<class T>
chain<T>::~chain() {
while (firstNode) {
chainNode<T>* nextNode = firstNode->next;
delete firstNode;
firstNode = nextNode;
}
}
//查询
template<class T>
int chain<T>::indexOf(const T& theElement) const {
//从firstNode开始
chainNode<T>* currentNode = firstNode;
int index = 0;
//指向表尾或找到元素
while (currentNode != NULL && currentNode->element != theElement) {
currentNode = currentNode->next;
index++;
}
//判断属于哪种情况
if (currentNode == NULL)
return -1;
else
return index;
}
//插入
template<class T>
void chain<T>::insert(int theIndex, const T& theElement) {
//在表头插入
if (theIndex == 0)
firstNode = new chainNode<T>(theElement, firstNode);
else {
chainNode<T>* p = firstNode;
for (int i = 0; i < theIndex - 1; i++)
p = p->next;//找到新元素的前驱
p->next = new chainNode<T>(theElement, p->next);
}
listSize++;
}
//删除
template<class T>
void chain<T>::erase(int theIndex) {
chainNode<T>* deleteNode = firstNode;
//删除头节点
if (theIndex == 0) {
deleteNode = firstNode;
firstNode = firstNode->next;
}
else {
chainNode<T>* p = firstNode;
//找到前驱节点
for (int i = 0; i < theIndex - 1; i++) {
p = p->next;
}
deleteNode = p->next;
p->next = p->next->next;
}
listSize--;
delete deleteNode;
}
//逆置
template<class T>
void chain<T>::reverse() {
if (firstNode == NULL || firstNode->next == NULL) return;
chainNode<T>* pastNode = firstNode;
chainNode<T>* currentNode = pastNode->next;
chainNode<T>* nextNode = currentNode->next;
firstNode->next = NULL;
while (nextNode != NULL) {
currentNode->next = pastNode;
pastNode = currentNode;
currentNode = nextNode;
nextNode = nextNode->next;
}
currentNode->next = pastNode;
firstNode = currentNode;
}
//输出
template<class T>
int chain<T>::Output() {
int count = 0, index = 0;
iterator beginning = begin();
while (index < listSize) {
count += index ^ (*beginning);
beginning++;
index++;
}
return count;
}
//冒泡排序
template<class T>
void chain<T>::bubbleSort() {
chainNode<T>* p1, * p2;
for (int i = 0; i < listSize - 1; i++) {
p1 = firstNode;
p2 = p1->next;
T x;
for (int j = 0; j < listSize - 1; j++) {
if (p1->element > p2->element) {
x = p1->element;
p1->element = p2->element;
p2->element = x;
}
p1 = p2;
p2 = p2->next;
}
}
}
//合并
template<class T>
void chain<T>::merge(chain<T>& a, chain<T>& b) {
//两个迭代器,用于遍历两个链表
chain<T>::iterator i(a.firstNode);
chain<T>::iterator j(b.firstNode);
int x = 0;//计数器,记录合并后链表中的位置
//当两个链表都没合并完时
while (i != NULL && j != NULL) {
if (*i > *j) {//比较当前元素大小
insert(x, *j);//插入小的
j++;
x++;
}
else {
insert(x, *i);
i++;
x++;
}
}
//至少一个链表已经遍历完成,直接插入到链表末尾
while (i != NULL) {
insert(x, *i);
i++;
x++;
}
while (j != NULL) {
insert(x, *j);
j++;
x++;
}
}
int main() {
int N, M;
cin >> N >> M;
chain<int>chain1(N);
chain<int>chain2(M);
chain<int>chain3(M+N);
int result[3];
for (int i = 0; i < N; i++) {
int c = 0;
cin >> c;
chain1.insert(i, c);
}
for (int j = 0; j < M; j++) {
int c = 0;
cin >> c;
chain2.insert(j, c);
}
chain1.bubbleSort();
chain2.bubbleSort();
result[0] = chain1.Output();
result[1] = chain2.Output();
chain3.merge(chain1, chain2);
result[2] = chain3.Output();
for (int i = 0; i < 3; i++) {
cout << result[i] << endl;
}
return 0;
}