目录
1. 实验内容及上机实验所用平台
1.1 实验内容
假设不带头结点的单链表结点类型如下:
struct ListNode
{ int val;
ListNode *next;
ListNode(int x):val(x),next(NULL){}
};
根据给定的数据序列构建循环单链表 L
,并设计一个算法,求该非空循环单链表 L
中最后一个最大节点的逻辑序号。
【输入形式】输入含有 n
个元素的单链表。
【输出形式】最后一个最大节点的逻辑序号(序号从 1
开始编号)。
【样例说明】根据读入数据构建循环单链表。
1.2 设计思路
根据输入数据构建一个非空循环单链表 L
,由于此循环单链表不带头结点,所以可以将输入的第 1
个数据作为循环单链表的首结点,方便后续查找和结束。另外用计数器得到每个元素的序号,通过链式查找找到最大的节点,输出其序号。
1.3 实验平台软件
Dev-C++.
2. 数据结构
循环单链表,有指向后继结点的 next
指针,且尾结点的 next
指向首结点。
3. 设计描述与分析
3.1 伪码
输入:字符串 str
。
输出:最大节点的序号 pre_i
。
// 将字符串str转化为数组num
// 利用数组num创建循环单链表
// 生成首结点first
// 生成结点*pre ← first→next,*max_pre ← pre
while pre != first
if pre指向的值 ≥ max_pre指向的值
max__ pre ← pre
// 保存其序号pre_i
end if
pre ← (pre → next)
end while
return pre_i
3.2 流程图
3.3 主要代码段
int Find() { // 找最后一个最大节点的逻辑序号
LinkNode *pre = first -> next, *max_pre = pre;
int i = 1, pre_i = i; // 初始化元素序号为1,最大结点为第一个元素
while (pre != first) { // 寻找最大结点
if (pre -> val >= max_pre -> val) { // 找到当前最大结点
max_pre = pre; // 将最大结点存下来
pre_i = i; // 将最大结点的序号存下来
}
pre = pre -> next; // 指针后移
i++; // 结点序号
}
return pre_i;
}
3.4 源代码
需要先在源代码目录下新建 in.txt
文件,在此文件下输入要测试的数据。
注意:数字之间的分隔符是中文的逗号。
#include <iostream>
#include <string>
using namespace std;
struct LinkNode { // 不带头结点的单链表结点类型
int val;
LinkNode *next;
LinkNode(int x): val(x), next(NULL) {}
};
class LinkList {
LinkNode *first; // 循环单链表的首结点
public:
void CreateList(int a[], int n) { // 建立循环单链表
first = new LinkNode(a[0]);
LinkNode *s, *r = first; // r为尾结点指针
for (int i = 0; i < n; i++) {
s = new LinkNode(a[i]); // 创建数据结点s
r -> next = s; // 将 s 结点链接到末尾
r = s;
}
r -> next = first; // 构成一个首结点为 first 的循环单链表
}
int Find() { // 找最后一个最大节点的逻辑序号
LinkNode *pre = first -> next, *max_pre = pre;
int i = 1, pre_i = i; // 初始化元素序号为1,最大结点为第一个元素
while (pre != first) { // 寻找最大结点
if (pre -> val >= max_pre -> val) { // 找到当前最大结点
max_pre = pre; // 将最大结点存下来
pre_i = i; // 将最大结点的序号存下来
}
pre = pre -> next; // 指针后移
i++; // 结点序号
}
return pre_i;
}
};
int main() {
cout << "\t\t第3题 - 最大节点序号\n\n";
cout << "---------------------------------------------------\n\n";
freopen("in.txt", "r", stdin);
string str;
while (cin >> str); // 读取文件数据(当字符串读取)
cout << "样例输入:" << str << endl;
string str_temp = "";
int num_temp[1001] = {0}, num[1001] = {0}, cnt_temp = 0, cnt = 0, x = 0;
bool flag = false; // 正数
for(int i = 0; i < str.length(); i++) num_temp[cnt_temp++] = str[i] - '0'; // 将字符全部转为数字
// 提取字符串中的数字
for (int i = 0; i < cnt_temp; i++) {
if (num_temp[i] >= 0 && num_temp[i] <= 9) x = x * 10 + num_temp[i];
else if (num_temp[i] == -3) flag = true;
else {
if (flag) {
x = -x;
flag = false;
}
num[cnt++] = x;
x = 0;
i++;
}
if (i == cnt_temp - 1) {
if (flag) {
x = -x;
flag = false;
}
num[cnt++] = x;
}
}
LinkList L;
L.CreateList(num, cnt); // 创建循环单链表
cout << "\n\n样例输出:";
cout << L.Find() << endl;
cout << "\n\n";
freopen("CON", "r", stdin); // 为了可直接查看exe可执行文件,需要将权限返回键盘
system("pause");
return 0;
}
4. 调试过程
-
测试1
a) 测试数据用例:9,-1,3,5,7,9,4
b) 测试数据用例1的特点:有两个最大结点,要输出最后最大结点的序号。
c) 测试结果:
-
测试2
a) 测试数据用例:-8,-6,-10,-1,-8
b) 测试数据用例2的特点:结点值均是负数。
c) 测试结果:
-
测试3
a) 测试数据用例:0,-6,-10,-1,-8
b) 测试数据用例3的特点:最大节点在表头。
c) 测试结果:
5. 实验总结
在此次实验中,遇到的问题主要是数据的提取,本来是想设计一个能够自动分割得到数字的程序,但是在分割时发现输入数据的分隔符是中文的逗号,由于中英文两种字符所占字节不同,无法做到统一标准,因此只能根据给定的中文字符进行分割。另外在分割时是用数组存每个单字符装换后的整数,所以在存储上浪费了很大的空间。而在找最大节点序号的程序上就没有什么问题,只要掌握的指针的操作,那么问题就不大。