数据结构课程设计学习实践

数据结构课程设计预习

实验一:图书信息管理系统的设计与实现

1. 实验内容

设计并实现一个图书信息管理系统。根据实验要求设计该系统的菜单和交互逻辑,并编码实现增删改查的各项功能。 该系统至少包含以下功能:
(1) 根据指定图书个数,逐个输入图书信息;
(2) 逐个显示图书表中所有图书的相关信息;
(3) 能根据指定的待入库的新图书的位置和信息,将新图书插入到图书表中指定的位置;
(4) 根据指定的待出库的旧图书的位置,将该图书从图书表中删除;
(5) 能统计表中图书个数;
(6) 实现图书信息表的图书去重;
(7) 实现最爱书籍查询,根据书名进行折半查找,要求使用非递归算法实现,成功返回此书籍的书号和价格;
(8) 图书信息表按指定条件进行批量修改;
(9) 利用快速排序按照图书价格降序排序;
(10) 实现最贵图书的查找;

2. 数据结构

图书信息与顺序表的定义:

//图书信息的定义:
typedef struct {
    char no[8];   //8位书号
    char name[20]; //书名
    int price;     //价格
}Book;
//顺序表的定义
typedef  struct {
  Book  *elem;     //指向数据元素的基地址
  int  length;       //线性表的当前长度                                                           
 }SqList;

3. 代码设计

图书信息管理系统的实验我用的是顺序表的顺序存储结构,相较于链式存储结构,它的各种操作实现方式太单一,尤其在图书信息记录的插入和删除上比较费力。但它同时也有优势,就是它的代码可读性相较于链表要好,本次第一个实验较简单所以我用了顺序表。
·下面是我针对实验要求的代码设计

//初始化
int InitList(SqList& L)
{
    L.elem = new Book[MAXSIZE];
    L.length = 0;
    return OK;
}
//创建图书信息
void InitInformation(SqList& L)
{
    cout << "请输入图书信息(书号 书名 价格)用空格隔开!" << endl;
    for (int i = 0; i < MAXSIZE; i++)
    {
        cin >> L.elem[i].no >> L.elem[i].name >> L.elem[i].price;
        if ((strcmp(L.elem[i].no, "0") == 0) && (strcmp(L.elem[i].name, "0") == 0) && (L.elem[i].price == 0))
            break;
        ++L.length;
    }
    cout << "图书信息表创建成功!" << endl;
}
//输出图书信息表
void PrintBook(SqList& L)
{
    int i = 0;
    cout << "========图书列表如下========" << endl;
    while (i < L.length)
    {
        printf("%s%c%s%c%.2f%c", L.elem[i].no, ' ', L.elem[i].name, ' ', L.elem[i].price, '\n');
        ++i;
    }
}
//指定位置插入新图书
int InsertNewBook(SqList& L, int i)
{
    Book b;
    int j;
    if ((i < 1) || (i > L.length) || L.length == MAXSIZE + 1)
    {
        cout << "抱歉,入库位置非法!" << endl;
        return ERROR;
    }
    for (j = L.length - 1; j >= i - 1; j--)
        L.elem[j + 1] = L.elem[j];
    cout << "输入新入库的图书信息(依次输入书号、书名、价格):" << endl;
    cin >> b.no >> b.name >> b.price;
    L.elem[i - 1] = b;
    ++L.length;
    cout << "插入成功!" << endl;
    PrintBook(L);
    return OK;
}
//删除表中指定位置的图书信息记录
int DeleteOldBook(SqList& L, int i)
{
    if ((i < 1) || (i > L.length) || L.length == MAXSIZE + 1)
    {
        cout << "出库失败,未找到该图书!" << endl;
        return ERROR;
    }
    for (int j = i - 1; j < L.length - 1; j++)
    {
        L.elem[j] = L.elem[j + 1];
    }
    --L.length;
    cout << "删除成功!" << endl;
    PrintBook(L);
    return OK;
}
//统计表中图书数量
void BookNumber(SqList L)
{
    cout << "现有图书数量为:" << L.length << endl;
}
//去重删除
void ExcludeSameDele(SqList& L, int i)
{
    for (int j = i - 1; j < L.length - 1; j++)
    {
        L.elem[j] = L.elem[j + 1];
    }
    --L.length;
}
//图书去重
void ExcludeSame(SqList& L)
{//从第二本书开始遍历,分别与前几本书比较看是否重复
    bool flag = false;//判断是否有重复,默认无重复
    for (int i = 1; i < L.length; i++)
    {
        for (int j = 0; j < i; j++)
        {//如果书号相同删除后面一个
            if (strcmp(L.elem[i].no, L.elem[j].no) == 0)
            {
                ExcludeSameDele(L, i + 1);//删除重复书号的图书记录
                i = 0;//删完之后有序表长缩减,所以i要重新置0再由i++置为1
                flag = true;//更改标记
            }
        }
    }
    if (!flag)
        cout << "图书没有重复,无需去重!" << endl;
    else {
        cout << "去重成功!" << endl;
        BookNumber(L);//输出去重后有多少本书
        PrintBook(L);//输出具体书籍信息
    }
}
//查找最爱书籍
void FavoriteBook(SqList L)
{
    char target[40];//存储待查的书名
    int count = 0;//待查书目出现次数
    cout << "请输入最爱的图书书名:";
    cin >> target;
    for (int i = 0; i < L.length; i++)
        if (strcmp(L.elem[i].name, target) == 0)
            count++;
    cout << "找到了" << count << "本" << endl;
    if (count)
    {
        for (int i = 0; i < L.length; i++)
            if (strcmp(L.elem[i].name, target) == 0)
                printf("%s%c%s%c%.2f%c", L.elem[i].no, ' ', L.elem[i].name, ' ', L.elem[i].price, '\n');
    }
    else cout << "抱歉,没有你的最爱!" << endl;
}
//图书均价
double average(SqList L)
{
    double aver;//所有图书平均价格
    double sum = 0;//所有图书总价
    for (int i = 0; i < L.length; i++)
    {
        sum += L.elem[i].price;
    }
    aver = sum / L.length;
    return aver;
}
//图书信息修改
void ModifyBook(SqList& L)
{
    printf("%s%.2f%c", "修改前图书均价", average(L), '\n');
    for (int i = 0; i < L.length; i++)
    {
        if (L.elem[i].price < average(L))
        {//价格低于均价的提高20%
            L.elem[i].price *= 1.2;
        }
        else
        {//价格等于或高于均价的提高10%
            L.elem[i].price *= 1.1;
        }
    }
    printf("%s%.2f%c", "修改前图书均价", average(L), '\n');
    PrintBook(L);
}
//将图书按价格降序排序
int Partition(SqList& L, int low, int high)
{
    Book temp = L.elem[low];         //定义一个变量来存储low的值 
    while (low < high)
    {
        //如果high的值大于等于temp的值,high则减1;反之则将high的值赋值给low
        while (low < high && L.elem[high].price <= temp.price) high--;
        L.elem[low] = L.elem[high];
        //如果low的值小于等于temp的值,low则加1;反之则将low的值赋值给high
        while (low < high && L.elem[low].price >= temp.price) low++;
        L.elem[high] = L.elem[low];
    }
    L.elem[low] = temp;//当high=low,跳出循环,并且将temp的值赋给low
    return low;//返回low的位置 
}
//快速排序(递归方法)
void QuickSort(SqList& L, int low, int high)
{
    if (low < high)	//跳出递归的条件 
    {
        int pivotpos = Partition(L, low, high);
        QuickSort(L, low, pivotpos - 1);   //左边递归 
        QuickSort(L, pivotpos + 1, high);   //右边递归 
    }
}
//查找最贵图书
void SearchExpensiveBook(SqList L)
{
    double max = 0;//定义最大值并初始化为0
    for (int i = 0; i < L.length; i++)
        if (L.elem[i].price > max)
            max = L.elem[i].price;
    for (int i = 0; i < L.length; i++)
        if (L.elem[i].price == max)
            printf("%s%c%s%c%.2f%c", L.elem[i].no, ' ', L.elem[i].name, ' ', L.elem[i].price, '\n');
}
//UI功能界面
void OutputUI()
{
    cout << "=========欢迎进入图书信息管理系统=========" << endl;
    cout << "10.创建图书的信息表" << endl;
    cout << "1.逐个显示图书信息表     2.插入新的图书信息" << endl;
    cout << "3.删除指定的图书信息     4.显示图书数量" << endl;
    cout << "5.对图书进行去重操作     6.查找最爱的图书" << endl;
    cout << "7.对图书进行修改操作     8.将图书按价格降序排序" << endl;
    cout << "9.查找最贵的图书操作     0.退出系统!" << endl;
}

实验二:隐式图的搜索问题

1. 实验内容

编写九宫重排问题的启发式搜索(A*算法)求解程序。
在3х3组成的九宫棋盘上,放置数码为1~8的8个棋子,棋盘中留有一个空格,空格周围的棋子可以移动到空格中,从而改变棋盘的布局。根据给定初始布局和目标布局,编程给出一个最优的走法序列。输出每个状态的棋盘。
测试数据:初始状态:123456780 目标状态:012345678

2. 实验提示

1)存储结构的定义
typedef struct node//八数码结构体
{
int nine[N][N];//数码状态
int f;//估价值
int direct;//空格移动方向
struct node *parent;//父节点
}pNode;
2)启发式搜索算法的描述:
(1)把初始节点S0 放入Open表中,f(S0)=g(S0)+h(S0);
(2)如果Open表为空,则问题无解,失败退出;
(3)把Open表的第一个节点取出放入Closed表,并记该节点为n;
(4)考察节点n是否为目标节点。若是,则找到了问题的解,成功退出;
(5)若节点n不可扩展,则转到第(2)步;
(6)扩展节点n,生成子节点ni(i=1,2,……),计算每一个子节点的估价值f(ni) (i=1,2,……),并为每一个子节点设置指向父节点的指针,然后将这些子节点放入Open表中;
(7)根据各节点的估价函数值,对Open表中的全部节点按从小到大的顺序重新进行排序;
(8)转到第(2)步。
启发式搜索算法的工作过程:

3. 输入输出要求

【输入格式】
输入包含三行,每行3各整数,分别为0-8这九个数字,以空格符号隔开,标识问题的初始状态。0表示空格,例如:
2 0 3
1 8 4
7 6 5
在这里插入图片描述

4. 代码设计

1)数据结构

const int N = 3;      //数组边长大小
struct node {         //状态类
	int data[N][N];   //九宫数据
	int G, H, F;      //3个估价函数值
	node* parent;     //前继指针
	//默认构造函数
	node() :G(0), H(0), F(0), parent(NULL) {}
};

2)部分函数声明

class Astar {
public:
	Astar(int startmaze[N][N], int endmaze[N][N]);//初始化Astatr
	list<node*> GetPath();//获取全部路径
	void Print();//显示最佳路径的每一步状态
private:
	bool isok();//判断是否有解
	node* findPath();//获取最佳路径
	vector<node*> getSurroundPoints(node* Sudoku) const;//获取0周围的可以移动获得的状态表
	bool isInList(list<node*>& list, node point);//判断开启列表中是否包含某点
	bool isSame(node* p, node point);//判断两个9宫内数据是否相同
	node* getLeastFpoint();//从开启列表中返回 F 值最小的状态指针
	node* finallyload();//在开启列表中寻找结束状态并返回,否则返回空
	node* upSudoku(node*, int, int) const;//返回给定状态0上移的状态指针
	node* downSudoku(node*, int, int) const;//返回给定状态0下移的状态指针
	node* leftSudoku(node*, int, int) const;//返回给定状态0左移的状态指针
	node* rightSudoku(node*, int, int) const;//返回给定状态0右移的状态指针
	//从初始状态到指定状态的移动代价(如果是初始节点,则其父节点是空)
	int GetG(node* Sudoku) { return Sudoku->parent == NULL ? 1 : Sudoku->parent->G + 1; }
	int GetH(node* Sudoku);//从指定状态到目标状态的估算成本
	int GetF(node* Sudoku) { return GetH(Sudoku) + GetG(Sudoku); }      //G,H之和
private:
	node startSudoku;     //初始九宫
	node endSudoku;       //目标九宫
	list<node*> openList; //开启列表
	list<node*> closeList;//关闭列表
	list<node*> path;     //最佳路径

3)实现想法

这个项目实现目标看上去很简单,但实际操作起来还是有很大难度的,我暂且还没有完全弄明白,所以算法的设计我用声明代替(参照网上资料的算法思维结构),具体设计过程和代码实现我还要回头研究!

更新:
A*算法伪代码:
把起始格添加到 "开启列表"
do {
 	寻找开启列表中 F 值最低的格子, 我们称它为当前格.
 	把它切换到关闭列表.
 	对当前格相邻的4格中的每一个
 	if (它不可通过 || 已经在 "关闭列表") {
 		什么也不做.
 	}
 	if (它不在开启列表中) {
 		把它添加进 "开启列表", 把当前格作为这一格的父节点, 计算这一格的 FGH
 	}
 	if (它已经在开启列表中) {
 		if (以 G 为参考值检查新的路径是否更好, G 越小路径越好) {
 			把这一格的父节点改成当前格, 并且重新计算这一格的 GF 值.
 		}
 	}
} while( 目标格已经在 "开启列表", 这时候路径被找到)
如果开启列表已经空了, 说明路径不存在.
最后从目标格开始, 沿着每一格的父节点移动直到回到起始格, 这就是路径.

实验三:基于二叉排序树的低频词过滤系统

1. 实验内容

1)对于一篇给定的英文文章,利用线性表和二叉排序树来实现单词频率的统计,实现低频词的过滤,并比较两种方法的效率。具体要求如下:
2)读取英文文章文件(Infile.txt),识别其中的单词。
3)分别利用线性表和二叉排序树构建单词的存储结构。当识别出一个单词后,若线性表或者二叉排序树中没有该单词,则在适当的位置上添加该单词;若该单词已经被识别,则增加其出现的频率。
4)统计结束后,删除出现频率低于五次的单词,并显示该单词和其出现频率。
5)其余单词及其出现频率按照从高到低的次序输出到文件中(Outfile.txt),同时输出用两种方法完成该工作所用的时间。
6)计算查找表的ASL值,分析比较两种方法的效率。

2. 实验提示

1、在统计的过程中,分词时可以利用空格或者标点符号作为划分单词依据,文章中默认只包含英文单词和标点符号。
2、对单词进行排序时,是按照字母序进行的,每个结点还应包含该单词出现的频率。
3、存储结构的定义
二叉排序树的存储表示:
typedef struct BSTNode{
string WordName; //单词名称
int count; //单词出现频率
struct BSTNode *next;
} BSTNode, *BSTree;
4、实现过程可参见教材上线性表和二叉排序树的相关算法。

3. 显示界面(UI)

系统运行后主菜单如下:
在这里插入图片描述
当选择1后进入以下界面:
在这里插入图片描述
其中选择2时显示利用线性表来实现所有功能所用的时间。
当在主菜单选择2二叉排序树后,进入的界面与上图类同。

4.算法思想

从包含大量单词的文本中找到出现次数最少的单词。如果有多个单词都出现最少的次数,则将这些单词都比较下来用于后面输出。怎么判断一个完整的单词呢?我们可以依次遍历字符串遇到空格或非字母的字符时就跳出,一个单词就出来了,除此以外我们应该在遍历时将大小写统一防止误判,因为C++中大小写字母是完全不同的字符。分别用两种方法去实现遍历存放单词,一种是线性表,一种是二叉树。然后将两种方法的ASL值进行比较即可。

重要代码设计

存储单词及数量

//存储文件中的单词及数量
double RecognizeWord(ifstream& infile, vector<vector<char>>& WordList, vector<int>& WordNum) {
	double ASL = 0;        //平均查找次数
	vector<char> word;     //单词存储器
	if (infile) {
		char ch;    //字母存储器
		int i;      //记录单词在单词表中的位置
		while ((ch = infile.get()) != EOF) {
			if (ch >= 65 && ch <= 90)ch += 32;   //大写字母转换成小写
			if (ch >= 97 && ch <= 122)           //小写字母放在单词后面
				word.push_back(ch);
			else if (i = isRepeat(WordList, word)) {  //判断单词表中是否存在新单词word
				WordNum[i - 1]++;      //新单词的频率加1
				word.clear();     //清空单词存储器
				ASL += i;    //查找次数增加找到新单词的位置
			}
			else {                         //如果没找到 
				WordNum.push_back(1);      //新单词的频率为1
				WordList.push_back(word);  //在单词表中加入新单词
				word.clear();              //清空单词存储器
				ASL += WordList.size();    //查找次数增加单词表的长度
			}
		}
	}

5. 界面设计

1)根据运行要求的主函数设计

int main() {
	int n;             //记录用户的输入
	bool flag = true;  //控制程序退出
	while (flag) {
		system("cls");//清屏操作
		//UI功能提示界面
		cout << "1.线性表\n" << "2.二叉排序树\n" << "3.退出系统\n" << endl;
		cout << "请选择你需要的服务,输入数字(1~3):" << endl;
	    cin >> n;
		switch (n) {
		case 1:Linear(); break;     
		case 2:BinarySortTree(); break;
		case 3:flag = false; break;
		default:cout << "选择错误,请重新输入:";
		}
	}
	return 0;
}

2)选择线性表界面

这是伪代码,并不是具体代码!

void Linear() {            //线性表的人机交互界面
	ifstream infile;       //输入流对象
	infile.open("Infile.txt");   //打开要读取的文件
	bool t = true;         //控制程序退出
	while (t) {
		system("cls");     //防止页面过于繁杂,执行一次清屏
		cout << "1.连续执行至完毕\n";
		cout << "2.显示执行时间\n"; 
		cout << "3.单步执行,识别并统计单词\n";
		cout << "4.单步执行,删除并显示出现频率低单词\n";
		cout << "5.单步执行,输出其余单词及其频率\n";
		cout << "6.单步执行,计算并输出ASL值\n";
		cout << "7.返回主菜单" << endl;
	infile.close();    //关闭输入流对象
}

3)选择二叉排序树界面

这是伪代码,不是具体代码!

void BinarySortTree() {              //排序二叉树的人机交互界面
	ifstream infile;                 //输入流对象
	infile.open("Infile.txt");       //打开要读取的文件
	bool t = true;                   //控制程序退出
	while (t) {
		system("cls"); //执行清屏操作
		cout << "1.连续执行至完毕\n";
		cout << "2.显示执行时间\n"; 
		cout << "3.单步执行,识别并统计单词\n";
		cout << "4.单步执行,删除并显示出现频率低单词\n";
		cout << "5.单步执行,输出其余单词及其频率\n";
		cout << "6.单步执行,计算并输出ASL值\n";
		cout << "7.返回主菜单" << endl;
	infile.close();                //关闭输入流对象
}

实验源代码:

https://blog.csdn.net/qq_52942550/article/details/118190238?spm=1001.2014.3001.5501

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值