广州大学学生实验报告
开课实验室:计算机科学与工程实验(电子楼) 2021年11月5日
学院 | 计算机科学与网络工程学院 | 年级、专业、班 | 姓名 | 学号 | ||||
实验课程 | 数据结构实验 | 成绩 | ||||||
实验项目 | 实验一 线性表、堆栈和队列的操作与实现 | 指导老师 | ||||||
评语: |
一、实验目的:
1、线性表的链表实现:遍历、查找、插入、删除、翻转
2、栈的链式存储结构实现:入栈、出栈
3、队列的链式存储结构的实现:入队、出队
4、线性表、栈和队列的应用实现
二、使用仪器、器材
微机一台
操作系统:WinXP
编程软件:C++
三、实验内容及原理
填入自己的内容(思路或算法流程图、源代码、说明等)
1、线性表的链表实现:
(1)用随机函数生成10个3位整数(100~999),把这些整数存于链表中;
(2)输出链表的内容;
(3)读入一个整数,查看该整数是否在表中,若在,输出其位置(首位置为1);
(4)读入一个整数,以及要插入的位置,把该整数插入到链表中,输出链表的内容(要求判断输入的位置是否合理);
(5)读入一个整数,若该整数在链表里,删除该整数,输出链表的内容;
(6)把链表的内容翻转,输出链表的内容。
#include<stdio.h>
#include<stdlib.h>
#include "time.h"//用到时间函数
#include "math.h"//用到随机函数
#define ERROR -1
#define OK 1
//定义
typedef struct Lnode
{
int data;
Lnode* next;
}Lnode, * LinkList;
//初始化
int InitList_L(LinkList& L)
{
//L = (LinkList)malloc(sizeof(Lnode));
L = new Lnode();//将结点的地址赋值给L,即L指向头结点
L->next = NULL;//L的指针域置空
return OK;
}
//输出链表
void PrintList(LinkList& L)
{
Lnode* p = L->next;
printf("Output the list: ");
while (p != NULL)//遍历链表并输出
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
//获取表长
int ListLength(LinkList& L)
{
LinkList p;
p = L->next;//p指向首元结点
int i = 0;
while (p)
{
i++;
p = p->next;
}
return i;
}
//查找,获取该数据的位置序号
void LocateElem_L(LinkList& L, int e)
{
Lnode* p;
p = L->next; int place = 1;
while (p && p->data != e)
{
p = p->next; place++;
}
if (p) printf("The location is %d\n", place);
else printf("The integer is not in the List\n");
}
//插入
void InsertElem_L(LinkList L, int place, int num)
{
Lnode* p = L; int j = 1;
while (p && (j != place))//p指向第i个结点
{
p = p->next; ++j;
}
if (!p || j < place)
{
printf("Insert failed!\n");
}
else
{
Lnode* s = new Lnode();
s->data = num;//生成新的结点s,并将*s的数据域置为e
s->next = p->next;//将结点s插入L中
p->next = s;
}
int n = ListLength(L);
PrintList(L);
}
//尾插法
void CreateList_L(LinkList& L, int n)
{
L = new Lnode; L->next = NULL;
LinkList r = L;//尾指针指向头结点
for (int i = 1; i <= n; i++)
{
LinkList p = new Lnode; //生成新结点,输入元素值
p->data = rand() % 900 + 100;
p->next = NULL;
r->next = p;//插入到表尾
r = p;//r指向新的尾结点
}
}
//删除
/*int ListDelete_L(LinkList& L, int place, int& num)
{
LinkList p = L; int delplace = 0;//定义一个位置delplace,用于判断要删除的位置place是否合理
while (p->next && delplace < place - 1) { p = p->next; delplace++; }
if (!(p->next) || delplace > place - 1)return ERROR;
LinkList q = p->next;//临时保存被删除结点的地址已备释放
p->next = q->next;//改变删除结点前驱结点的指针域
num = q->data;//保存删除结点的指针域
delete q;//释放删除结点的空间
return OK;
}*/
void ListDelete_num(LinkList& L, int n, int num)
{
Lnode* p = L; int pla = 1;
while (p->next && num != p->next->data)//若输入整数与p指向的元素的下一个元素不相等,将p指向下一个元素
{
p = p->next;
pla++;
}
if (pla <= n)//删除p
{
LinkList q = p->next;//临时保存被删除结点的地址已备释放
p->next = q->next;//改变删除结点前驱结点的指针域
num = q->data;//保存删除结点的指针域
delete q;//释放删除结点的空间
}
else
printf("Deleted failed!\n");
PrintList(L);
}
//翻转链表
void ListReverse(LinkList& L)//翻转后输出链表所有数据
{
Lnode* p = L->next;
Lnode* temp;
InitList_L(temp);
while (p != NULL)
{
Lnode* q = p->next;
p->next = temp->next;
temp->next = p;
p = q;
}
L = temp;
printf("Output the reverselist:\n");
PrintList(L);//输出链表翻转后的数据
}
int main()
{
LinkList L;
int n = 10,num = 0, num1, num2, num3, num4;//n为链表长度,num为输入整数
srand((unsigned int)time(NULL));
InitList_L(L);//初始化链表,让L指向头结点
CreateList_L(L, n);//使用尾插法生成链表
PrintList(L);//输出链表
printf("Please input an integer that you want to find:");
scanf_s("%d", &num1);
LocateElem_L(L, num1);//输出查找元素的位置
printf("Please input an integer and its location:");
scanf_s("%d%d", &num2, &num3); //输入异常
InsertElem_L(L, num3, num2);
printf("Please input an integer:");
scanf_s("%d", &num4);
ListDelete_num(L, n, num4);
ListReverse(L);
}
2、栈的链式存储结构实现
(1)用随机函数生成10个3位整数(100~999),把这些整数应用入栈操作存于堆栈中,在入栈接口处设置断点①,按“F5”启动调试,按“F10”逐句执行,直到数据全部入栈。程序暂停时观察栈顶数据和栈顶位置;
(2)应用出栈操作输出堆栈的内容,在出栈接口处设置断点②,按“F5”启动调试,按“F10”逐句执行,直到所有数据完全出栈,程序暂停时观察栈顶数据和栈顶位置的变化;
#include<stdio.h>
#include<stdlib.h>
#include<cmath>
#include<time.h>
#define OK 1;
#define ERROR 0;
//定义
typedef struct LNode {
int data;
struct LNode* next;
}LNode, * LinkStack;
//初始化链栈
int InitStack(LinkStack& S) {
S = NULL;
return OK;
}
//入栈
int PushStack(LinkStack& S) {
LNode* p = new LNode;
p->data = rand() % 900 + 100;
p->next = S;
S = p;
printf("%d进栈\n", S->data);
return OK;
}
//出栈
int PopStack(LinkStack& S) {
if (S == NULL) return ERROR;
LNode* p = S;
printf("%d已出栈\n", S->data);
S = S->next;
delete p;
return OK;
}
int main()
{
LinkStack S;
srand((int)time(0));
for (int i = 0; i < 10; i++)
{
PushStack(S);
}
for (int i = 0; i < 10; i++)
{
PopStack(S);
}
}
3、队列的链式存储结构的实现
(1)用随机函数生成10个3位整数(100~999),把这些整数应用入队操作存于队列中;
(2)应用遍历操作输出队列的内容;
(3)把队列的内容翻转,应用出队操作输出队列的内容。
#include <stdio.h>
#include<stdlib.h>
#include "time.h"
#include "math.h"
struct LinkQueue
{
int data;
LinkQueue* next;
};
struct LinkQueuePtr
{
LinkQueue* front;//队首头结点
LinkQueue* rear;//队尾
};
void InitQueue(LinkQueuePtr& q)//初始化队列指针
{
q.front = new LinkQueue;
q.rear = q.front;
q.front->next = NULL;
}
void PushQueue(LinkQueuePtr& q, int data)//数据压入队尾
{
LinkQueue* tmp = new LinkQueue;
tmp->data = data;
tmp->next = NULL;//新建一个队列指针
q.rear->next = tmp;//将队尾的next连接到添加节点
q.rear = q.rear->next;//队尾移到新添加节点
}
int PopQueue(LinkQueuePtr& q)//弹出队首元素并返回值
{
if (q.front == q.rear) { printf("队列为空"); return -1; }//队列为空
LinkQueue* tmp = q.front->next;
int data = tmp->data;
q.front->next = tmp->next;//将队列头结点指向下一个结点
if (q.rear == tmp) q.rear = q.front;//如果删除的为最后一个结点,队列空,队尾置为队首
free(tmp);
return data;
}
void ReverseQueue(LinkQueuePtr& q)
{
LinkQueue* m = q.front->next;//暂存原队列首元素空间
LinkQueue* move = q.front->next;
q.front->next = q.rear;//将首元素替换成末尾元素
for (int i = 0; i < 10; i++)
{
while (move)
{
if (move->next == q.rear)
{
q.rear->next = move;
q.rear = move;
break;
}
move = move->next;
}
move = m;
}
move->next = NULL;//最后一个元素的指针域置空
q.rear = move;
}
int main()
{
srand(unsigned int(time(0)));
LinkQueuePtr q;//创建队列指针
InitQueue(q);//初始化队列
for (int i = 0; i < 10; i++)
{//生成随机数并压入队列
int data = rand() % 900 + 100;
PushQueue(q, data);
}
printf("队列的遍历数据如下\n");
for (int i = 0; i < 10; i++)//遍历队列所有数据
{
int data = PopQueue(q);
printf("%d ", data);
PushQueue(q,data);
}
printf("\n");
ReverseQueue(q);
printf("队列翻转后输出内容\n");
for (int i = 0; i < 10; i++)//遍历输出数据
{
printf("%d ", PopQueue(q));
}
}
4、线性表、栈和队列的应用实现:
(1)用随机函数生成10个3位整数(100~999),把这些整数存于单链表中,然后读入一个整数,以该值为基准把单链表分割为两部分,所有小于该值的结点排在大于或等于该值的结点之前。
#include<stdio.h>
#include<stdlib.h>
#include "time.h"
#include "math.h"
#define OK 1
#define ERROR 0
typedef struct LNode {
int data;
struct LNode* next;
LNode* Rear;
}LNode, * LinkList;
//创建空表
int InitList(LinkList& L)
{
L = new LNode;
L->next = NULL;
L->Rear = L;
return OK;
}
//链表的存储
int StoreElem(LinkList& L, int& e)
{
LNode* p = new LNode;
p->data = e;
p->next = NULL;
L->Rear->next = p;
L->Rear = p;
return OK;
}
// 输出链表内容
int ShowElem(LinkList& L)
{
LNode* p = L->next;
printf("链表输出:\n");
while(p)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
return OK;
}
int CreateList(LinkList& L)
{
for (int i = 0; i < 10; ++i)
{
int j = rand() % 900 + 100;
StoreElem(L, j);
}
ShowElem(L);
int measure;//基准整数
printf("请输入一个整数,以该值为基准把单链表分割为两部分:\n");
scanf_s("%d", &measure);
LNode* p = L;//定义结点,将结点存放的数据与基准数据进行大小比较
for (int i = 0;i < 10; i++)
{
if (p->next->data >= measure)
{
L->Rear->next = p->next;
L->Rear = p->next;
p->next = p->next->next;
L->Rear->next = NULL;
}
else p = p->next;
}
ShowElem(L);
return OK;
}
int main()
{
srand((unsigned int)time(NULL));
LinkList L;
InitList(L);
CreateList(L);
}
- 假设一个字符串中可以包含三种括号:( )[ ]{},且这三种括号可以按任意次序嵌套使用(如:“...[...{...}...[...]...]...(...)” 为合法嵌套,“...[...{... )...[...]...]...(...)”为不合法嵌套)。编写判别给定表达式中所含括号是否正确配对出现的算法,如果是合法嵌套则返回为true,如果是不符合法嵌套则返回为false。
#include<stdio.h>
#include<stdlib.h>
#include "string.h"
typedef struct StackNode
{
char data;
StackNode* next;
}StackNode, *Stack;
void InitStack(Stack& S)//初始化栈
{
S = NULL;
}
void Push(Stack& S, char data)//前插法压入数据
{
StackNode* tmp = new StackNode;
tmp->data = data;
tmp->next = S;
S = tmp;
}
void Pop(Stack& S)//弹出栈顶数据
{
if (S == NULL) printf("栈空\n");
StackNode* tmp = S;
S = S->next;
delete tmp;
}
char GetTop(Stack& S)//获取栈顶元素
{
if (S == NULL) return '#';
return S->data;
}
bool match(Stack& S, char data)//配对函数
{
printf("输入元素为: %c\n", data);
if (data == '(' || data == '{' || data == '[') Push(S, data);
else if (data == ')' && GetTop(S) != '(') { printf("配对失败"); return false; }//如果匹配不成功返回错误
else if (data == '}' && GetTop(S) != '{') { printf("配对失败"); return false; }
else if (data == ']' && GetTop(S) != '[') { printf("配对失败"); return false; }
else if (data == ')' && GetTop(S) == '(') { printf("配对成功\n" ); Pop(S); }//匹配成功则弹出栈顶元素,继续匹配
else if (data == '}' && GetTop(S) == '{') { printf("配对成功\n"); Pop(S); }
else if (data == ']' && GetTop(S) == '[') { printf("配对成功\n"); Pop(S); }
return true;
}
bool main()
{
char test[10] = "[{)[]]()#";//测试数据
bool judge = false;
StackNode* S;
InitStack(S);
for (auto c : test)//遍历测试数据,逐一匹配
{
if (c == '#') { printf("测试结束!\n"); break; }//若非要求字符,则退出
judge = match(S, c);//判断是否匹配成功
if (judge == false) break;//如果错误,直接退出
}
if (S != NULL) judge = false;//匹配成功最后栈顶应为空,否则,则有剩余符号,属于匹配失败
if (judge) printf("最终匹配成功\n");
else printf("最终匹配失败\n");
}
- 实验结果
截屏显示程序的运行结果
2.
3.
4.
(1)
(2)
五、实验分析
分别截图显示实验过程中出现的问题,分析原因并给出解决方案。
- ①非递归先序遍历中,调试代码,无法显示出遍历结果
分析:刚开始以为是遍历代码逻辑的问题,但多次修改之后还是无法显示结果。后来发现是有关栈的基本操作出现了逻辑错误,在插入、删除部分,操作对象都是树结点。最后我通过用数组来实现栈的基本操作,调试成功。
②非递归先序遍历中,要输出根结点存放的数值及遍历右孩子时引发异常。
分析:在遍历过程中,列表访问到了尾结点之后还在继续遍历出界,发生越界行为。while循环处加上判断条件p->next即可
void ListDelete_num(LinkList& L, int n, int num)
{
Lnode* p = L; int pla = 1;
while (p->next && num != p->next->data)//若输入整数与p指向的元素的下一个元素不相等,将p指向下一个元素
{
p = p->next;
pla++;
}