数据结构课程设计_排名估计


/*
*本文仅供学习参考使用
**/

一、选题

1、所选题目

  题目五:排名估计

2、选题内容

(1)问题描述

某次考试有n个选手参加,知情人泄露了若干组排序信息,每组信息包括两个人的排中情况,如A>B A

(2)设计要求

要求一:选手测试数据请自行构造,存储在文本文件中。

要求二:一个测试数据文件可以有多个测试用例。

要求三:需要包含三种测试用例,一是数据不矛盾,但无法完整排

序:二是数据可以完整排序;三是数据有矛盾,如A>B、B>C、 C>A,无法排序。

要求四:选手姓名用逗号分隔,每个姓名可以很长;采用哈希表索引选手。(可选)

相关数据结构和算法:

并查集、图、邻接表、拓扑排序、哈希表。

 

 

二、实现分析

1.题目分析与设计方案

  针对题目要求,鉴于c++语言高效的运行速度及强大的标准模板库STL,我们小组选用c++语言进行本次课程设计。

  题目要求我们将按照题目要求所自行制定的测试数据存入文本文件(TXT文件)中,文本中有三类数据信息:A>B(A排名在B之前),A

    整体的解答思路就是首先将数据从TXT文本读入内存中,先使用并查集将相等的数据合并为一个集合,选择一个节点为该集合的代表节点,后续对该集合中所有节点的操作都将转移到代表节点上,集合中除代表节点外,其余节点均不再参与后续的构图等过程,只需在输出结果时插入到代表节点的前面。然后构造哈希表和有向图,哈希表采用数据项字符串的ASCII码相加然后对哈希表的最大存储空间取模,采用线性探测解决哈希冲突。图使用邻接表存储,图中节点使用哈希表作为索引。然后对有向图进行拓扑排序,当图有环的时候证明数据是矛盾的,就是出现了A>B, B>C,C>A的情况,这种情况构图是有环的,拓扑排序完整与否可以判断是否有环。当拓扑排序时同时有两个节点入度为0就是无法排序的情况,也就是A>C, B>C,A和B的关系没有给出所以无法确定A和B的关系,A和B就无法排序,排除这两种情况,拓扑排序的结果就是最终排序答案

2.输入输出说明

输入:

  程序从工程文件中直接读取预置的TXT文本作为程序输入,我们小组一共预置了三种测试数据,包括可以完整排序样例1(无排名相等)、可以完整排序样例2(有排名相等)、数据不矛盾但无法完整排序、数据有矛盾无法排序共四组测试样例,测试样例不可增加,但可以在四组样例中修改测试数据。

输出:

  程序使用控制台界面进行结果输出,使用win32函数封装了易操作、结果清晰的用户交互界面,为了避免个别姓名过长或过短造成输出的冗余和获得更加的直观的程序结果,我们规定测试样例中不同两个姓名的首字母不能相同,并在输出时用首字母代替姓名,程序输出有三种结果,分别对应三种测试样例:

  (1) 可以排序:输出完整的排序情况

  (2) 数据不矛盾但无法排序:输出数据在哪个节点之后出现了无法排序的情况

  (3) 数据矛盾:输出数据在哪个节点之后出现了矛盾

3.数据结构使用分析

  程序中主要运用了:并查集、哈希表、图、队列、线性表等数据结构。

  并查集使用数组(ufs[])表示,如果节点没有相等节点,则ufs[i] = i, 即它的代表节点就是它本身,如图2.1中,ufs[4] = 4。如果节点有相等节点,则所有相等节点组成一个集合,若a = b = c,a为其代表节点,则有ufs[c] = ufs[b] = ufs[a] = a, 如图2.1中, 节点1 = 节点3 = 节点8, 节点1为其代表节点,则有ufs[8] = ufs[3] = ufs[1] = 1,这样当我们索引节点8时,得到的是其代表节点:节点1,对节点8的所有操作都将在节点1进行。

 

   

 图2.1 并查集关系

  哈希表节点HashNode包含两项属性:节点存储的姓名string name和当前节点的状态enum EntryType Info, 状态值是一个枚举类型:

1 enum EntryType { Legitimate, Empty, Deleted };

  分别代表当前节点的状态:已存放数值、空节点、节点已删除哈希表数据结构HashTable包含两项属性:已经存入的数据大小TableSize和哈希节点数组指针HashNode *Cells。

  哈希表提供了三个访问接口,分别为:

1 int hash_func(string name);//哈希函数,返回应插入下标,以姓名的ASCII码总和作为哈希索引
2 
3 int hash_insert(HashTable& tab, string name); //哈希表插入,采用线性探测,返回插入位置的下标
4 
5 int hash_find(HashTable& tab, string name);//哈希查找,返回查找元素下标

  

图2.2 哈希表结构

  有向图节点Tunode包含四项属性:节点数据name、入度indegree、出度outdegree、节点状态state,state是一个枚举类型:

1 enum State { Hang, Using, DataContra };
2 
3 /* 节点属性里增加的一个状态:(挂起、使用中, 矛盾),当u v相等时,u代替v所有的操作,
4 
5 |* v的状态变为:挂起,不再加入图中参与后面的图算法,在输出结果时,插入到u的前面。
6 
7 |* 矛盾代表数据在该节点出现无法完整排序的情况
8 
9 */

  以节点指针储存有向图信息,vector数组存储图邻接表信息,对有向图节点的访问使用哈希表索引进行访问。

1 Tunode* Ceils;//图信息,与邻接链表下标一一对应
2 
3 vector<int> adj[MAXN];//图邻接表

  对有向图提供了拓扑排序接口,拓扑排序返回返回值是一个枚举类型,为了能够在后续将“挂起”的节点插入到结果中进行输出,拓扑排序的结果保存在链表RES中:

1 list<int> RES;//用于存储拓扑排序结果
2 
3 enum TopState { True, Contra, False };//拓扑排序返回的三种状态,分别代表数据能排序、数据不矛盾但无法排序、数据矛盾
4 
5 enum TopState TopSort();//拓扑排序

图2.3 有向图结构

3、算法设计与分析

(1)算法正确性分析

  程序中主要使用了拓扑排序算法,我们主要针对拓扑排序算法进行分析

图3.1 拓扑排序算法

  ?初始化:进入拓扑排序后申请辅助空队列q,选出入度为零的有效节点加入队列,使用临时变量count统计加入队列中的节点数, flag标记数据状态, cnt统计存入结果链表的数据数量,将第一个入度为零的节点加入队列和结果链表中。

     ?保持:不断弹出队列的队顶元素,加入到结果链表中,并将其邻接节点加入到队列中,在判断邻接节点时,会使用临接节点的代表节点,如果数据集是正确的,一定会有且仅有一个节点加入到队列中,如果是队列中存在两个以上元素,证明数据集存在无法排序的节点,将flag标记为Contra,标记有数据无法排序。

  ?终止:当队列为空时,程序终止,根据存入结果链表的节点数量和flag的状态判断拓扑排序返回值,数据集按出队顺序排列即为数据集的拓扑排序结果,依次存储在RES结果链表中,遍历RES结果链表即为最终答案,因此算法正确。

(2)复杂度分析

  ?时间复杂度:程序中各算法时间复杂度如图所示:

图3.2 各函数时间复杂度

  拓扑排序中e为有向图中边的数量,整体的时间复杂度为O(n ),若设计算机单次运行时间为1e-9s,此程序的性能趋势图如下:

图3.3 理论性能趋势图

  ?空间复杂度:

  拓扑排序算法额外申请了一个队列,空间复杂度为O(n)。

  ?不同数据结构的性能差异:

  我们可以在程序中取消哈希表索引而采用直接存储姓名,这样可以节省一定的空间,但是代价是每次使用姓名时都会在姓名数组中查找并返回所在位置,每次索引都将耗费O(n)的时间,得不偿失。

一、实验结果截图及分析

1、实验结果截图

1菜单选择

四、实验代码

  Interface.h

 1 #pragma once
 2 /*
 3 |*界面封装头文件,用于界面封装,便于交互
 4 */
 5 #include 
 6 #include 
 7 using namespace std;
 8 #define WIDTH 100//界面宽度100
 9 #define HIGHT 26//界面高度25
10 #define LEFTOTHER 10//界面左留白10
11 #define TOPOTHER 1//界面顶部留白1
12 #define FORECOLOR 7//默认字体前景色白色
13 #define BACKCOLOR 0//默认字体背景色黑色
14 
15 void setTitle(const char* title);//设置控制台标题
16 void setConseColor(int foreground, int background);//设置字体颜色
17 //0 黑色    //5 紫色
18 //1 蓝色    //6 黄色
19 //2 绿色    //7 白色
20 //3 天蓝色    //8 灰色
21 //4 红色    //9 蓝色
22 void setPos(int x, int y); //设置打印光标位置
23 void mainFrame();//主要界面框架

  Interface.cpp

 1 #include "Interface.h"
 2 /*用于封装一些美化控制台的界面函数**/
 3 void setTitle(const char* title) {//设置控制台标题
 4     WCHAR wszClassName[256];//char wszClassName[256]
 5     memset(wszClassName, 0, sizeof(wszClassName));
 6     MultiByteToWideChar(CP_ACP, 0, title, strlen(title) + 1, wszClassName, sizeof(wszClassName) / sizeof(wszClassName[0]));//char*转wchar*
 7     SetConsoleTitle(wszClassName);
 8 }
 9 void setConseColor(int foreground, int background) {//设置字体颜色
10     HANDLE winhandle;//句柄
11     winhandle = GetStdHandle(STD_OUTPUT_HANDLE);//拿到标准输出设备的句柄,即控制台
12     SetConsoleTextAttribute(winhandle, foreground + background * 0x10);
13 }
14 void setPos(int x, int y) {//设置光标打印位置
15     HANDLE winhandle;
16     COORD pos;//坐标类型
17     pos.X = x;
18     pos.Y = y;
19     winhandle = GetStdHandle(STD_OUTPUT_HANDLE);
20     SetConsoleCursorPosition(winhandle, pos);
21 }
22 void mainFrame() {//主要框架
23     string topHenStrTemp(WIDTH - 2, '-');//两端打+
24     string topHenStr = "+";
25     topHenStr += topHenStrTemp;
26     topHenStr += "+";
27     setPos(LEFTOTHER, TOPOTHER);
28     cout << topHenStr;
29     for (int i = TOPOTHER + 1; i < HIGHT + TOPOTHER; i++) {
30         if (i == TOPOTHER + 4) {//头部横杠
31             continue;
32         }
33         setPos(LEFTOTHER, i);
34         cout << "|";
35         setPos(WIDTH + LEFTOTHER - 1, i);
36         cout << "|";
37     }
38     setPos(LEFTOTHER, TOPOTHER + 4);
39     cout << topHenStr;
40     setPos(LEFTOTHER, TOPOTHER + HIGHT);//底部横杠
41     cout << topHenStr;
42     string title = "排名估计管理系统";
43     setPos((WIDTH - title.size()) / 2 + LEFTOTHER, TOPOTHER + 2);
44     cout << title;
45     setPos(LEFTOTHER, HIGHT + TOPOTHER - 4);
46     cout << topHenStr;
47     setPos(0, TOPOTHER + 26);
48 }

  数据结构课程设计_排名估计.cpp

  1 #include 
  2 #include 
  3 #include 
  4 #include <string>
  5 #include 
  6 #include 
  7 #include 
  8 #include "Interface.h"
  9 using namespace std;
 10 
 11 const int MAXN = 100;//最大存储空间
 12 int ResCnt;//最终加入图中参与图算法数据的数量
 13 enum State { Hang, Using, DataContra };
 14 /* 节点属性里增加的一个状态:(挂起、使用中, 矛盾),当u v相等时,u代替v所有的操作,
 15 |* v的状态变为:挂起,不再加入图中参与后面的图算法,在输出结果时,插入到u的前面。
 16 |* 矛盾代表数据在该节点出现无法完整排序的情况
 17 */
 18 struct Tunode {
 19     string name;//节点数据
 20     int indegree;//入度
 21     int outdegree;//出度
 22     enum State state;
 23 };
 24 int ufs[MAXN];//并查集,当u v排名相等时,将u v作为一个集合,所有对v的操作转移到对u进行操作,即使ufs[v] = u
 25 Tunode* Ceils;//图信息,与邻接链表下标一一对应
 26 vector<int> adj[MAXN];//图邻接表
 27 
 28 /***********************    TXT文件读取    ************************/
 29 vector<string> fileMsg;//用于存储读取的文件信息
 30 void Read_File(string filename);//读取文件内容到fileMsg
 31 
 32 /***********************    拓扑排序    ************************/
 33 list<int> RES;//用于存储拓扑排序结果
 34 enum TopState { True, Contra, False };//拓扑排序返回的三种状态,分别代表数据能排序、数据不矛盾但无法排序、数据矛盾
 35 enum TopState TopSort();//拓扑排序
 36 
 37 /***********************    哈希表    ************************/
 38 enum EntryType { Legitimate, Empty, Deleted };//用于哈希索引的节点的状态 已存、空、已删除
 39 struct HashNode {//哈希表数据节点
 40     string name;
 41     enum EntryType Info;//用于哈希索引的状态值
 42 };
 43 struct HashTable {//哈希表
 44     int TableSize = 0;//已经存入的数据大小
 45     struct HashNode* Cells; /* 存放节点的数组 */
 46 };
 47 int hash_func(string name);//哈希函数,返回应插入下标,以姓名的ASCII码总和作为哈希索引
 48 int hash_insert(HashTable& tab, string name); //哈希表插入,采用线性探测,返回插入位置的下标
 49 int hash_find(HashTable& tab, string name);//哈希查找,返回查找元素下标
 50 HashTable tab;//哈希表
 51 
 52 /***********************    并查集    ************************
 53 |*ufs_find: 并查集路径压缩查找,返回x所在集合的代表节点*/
 54 int ufs_find(int x) {
 55     if (x == ufs[x])
 56         return x;
 57     else {
 58         ufs[x] = ufs_find(ufs[x]);  //父节点设为根节点
 59         return ufs[x];//返回父节点
 60     }
 61 }
 62 inline void merge(int u, int v) {//并查集,合并,将 v 合并到 u, 简单来说,即ufs[v] = u, 这样在使用ufs[v]时,得到的是u
 63     ufs[ufs_find(v)] = ufs_find(u);
 64 }
 65 
 66 /***********************    过程封装函数    ************************/
 67 void __Init__();//初始化全局变量, 读取TXT文件
 68 void Read_Msg_equal();//读出排名相等的条目,进行并查集合并
 69 void Read_Msg_unequal();//读出大小条目,构造图邻接表
 70 void PrintRes(enum TopState res);//根据拓扑排序的状态返回值输出结果
 71 void __Free__();//释放内存
 72 
 73 int main() {
 74     setTitle("排名估计");//设置控制台标题
 75     while (1) {//循环读入,非法输入退出
 76         /***            一、初始化全局变量, 读取TXT文件          ***/
 77         __Init__();
 78 
 79         /***            二、解析fileMsg,构图           ***
 80         |*解析读出来的fileMsg,将姓名存入哈希表便于索引,利用哈希表的索引构建图邻接表
 81         |*使用索引时,会判断节点有没有代表节点,
 82         |*    1.先读出排名相等的条目,进行并查集合并    */
 83         Read_Msg_equal();
 84         /**    2.读出大小条目,构造图邻接表    */
 85         Read_Msg_unequal();
 86 
 87         /***            三、对图进行拓扑排序           ***/
 88         for (int i = 0; i < MAXN; i++) {
 89             if (Ceils[i].name != "-1" && Ceils[i].state != Hang) {//统计有效数据数量
 90                 ResCnt++;
 91             }
 92         }
 93         enum TopState res = TopSort();//拓扑排序,得到排序结果状态返回值
 94 
 95         /***            四、输出结果           ***/
 96         PrintRes(res);//根据拓扑排序的状态返回值输出结果
 97         __Free__();//释放内存
 98     }
 99     return 0;
100 }
101 void __Init__() {//初始化函数
102     system("cls");//清屏
103     mainFrame();//主要界面框架
104     ResCnt = 0;
105     tab.Cells = new HashNode[MAXN];
106     Ceils = new Tunode[MAXN];
107     for (int i = 0; i < MAXN; i++) {
108         ufs[i] = i;//并查集初始化,所有节点"各自为战"
109         Ceils[i].state = Using; Ceils[i].name = "-1"; Ceils[i].indegree = 0; Ceils[i].outdegree = 0;
110         tab.Cells[i].Info = Empty;
111     }
112     string file[4] = { "可以完整排序样例1(无排名相等).txt", "可以完整排序样例2(有排名相等) .txt",
113                        "数据不矛盾但无法完整排序.txt",      "数据有矛盾无法排序.txt" };
114     setPos(LEFTOTHER + 25, TOPOTHER + 7);
115     cout << "当前可选择四个初始化样例:";
116     for (int i = 1; i <= 4; i++) {
117         setPos(LEFTOTHER + 25, TOPOTHER + 7 + i * 3);
118         cout << i << ". " << file[i - 1];
119     }
120     string tip = "请选择读入的文件:";
121     setPos((WIDTH - tip.size()) / 2 + LEFTOTHER - 1, HIGHT + TOPOTHER - 2);
122     cout << tip;
123     
124     int choise;
125     cin >> choise;
126     if (choise < 1 || choise > 4) {
127         system("cls");//清屏
128         tip = "非法输入!程序退出";
129         setPos((WIDTH - tip.size()) / 2 + LEFTOTHER, TOPOTHER + 10);//居中打印
130         cout << tip;
131         mainFrame();
132         exit(-1);
133     }
134     Read_File(file[choise - 1]);
135 }
136 void Read_Msg_equal() {//读出排名相等的条目,进行并查集合并
137     for (int i = 0; i < fileMsg.size(); i++) {//先读出排名相等的条目,进行并查集合并
138         int pos;
139         if ((pos = fileMsg[i].find("-", 0)) != -1) {//数据集中a-b代表ab排名相等
140             /*    根据符号分割,将姓名分割出来储存到临时变量u v中   **/
141 
142             string u = fileMsg[i].substr(0, pos);
143             string v = fileMsg[i].substr(pos + 1, fileMsg[i].size());
144             //查找哈希表中是否名字已经存在
145             int u_index = hash_find(tab, u);//u, v 在哈希表中的索引
146             int v_index = hash_find(tab, v);
147             if (u_index == -1) {//姓名不存在,将姓名插入哈希表,更新下标
148                 u_index = hash_insert(tab, u);
149                 Ceils[u_index].name = u;
150 
151             }
152             if (v_index == -1) {//姓名不存在,将姓名插入哈希表,更新下标
153                 v_index = hash_insert(tab, v);
154                 Ceils[v_index].name = v;
155             }
156             /*并查集,将u设为v的代表节点**/
157             merge(u_index, v_index);
158             Ceils[v_index].state = Hang;//v的状态变更为挂起,将不再参与后续的构图
159             //cout << Ceils[u_index].name << " = " << Ceils[v_index].name << " /*v的代表 " << Ceils[ufs_find(v_index)].name << endl;
160         }
161     }
162 }
163 void Read_Msg_unequal(){//读出大小条目,构造图邻接表
164     for (int i = 0; i < fileMsg.size(); i++) {
165         int pos;
166         if ((pos = fileMsg[i].find(">", 0)) != -1) {
167             /*    根据符号分割,将姓名分割出来储存到临时变量u v中   **/
168             string u = fileMsg[i].substr(0, pos);
169             string v = fileMsg[i].substr(pos + 1, fileMsg[i].size());
170 
171             //查找哈希表中是否名字已经存在
172             int u_index = hash_find(tab, u);//u, v 在哈希表中的索引
173             int v_index = hash_find(tab, v);
174             if (u_index == -1) {//姓名不存在,将姓名插入哈希表,更新下标
175                 u_index = hash_insert(tab, u);
176                 Ceils[u_index].name = u;
177             }
178             if (v_index == -1) {//姓名不存在,将姓名插入哈希表,更新下标
179                 v_index = hash_insert(tab, v);
180                 Ceils[v_index].name = v;
181             }
182             adj[ufs_find(u_index)].push_back(ufs_find(v_index)); //u > v 即u 指向v
183             Ceils[ufs_find(u_index)].outdegree++;
184             Ceils[ufs_find(v_index)].indegree++;
185             //cout << Ceils[u_index].name << " > " << Ceils[v_index].name << " /*图由 " << Ceils[ufs_find(u_index)].name << " 指向 "<< Ceils[ufs_find(v_index)].name << endl;
186         }
187         else if ((pos = fileMsg[i].find("<", 0)) != -1) {
188             /*    根据符号分割,将姓名分割出来储存到临时变量u v中   **/
189             string u = fileMsg[i].substr(0, pos);
190             string v = fileMsg[i].substr(pos + 1, fileMsg[i].size());
191 
192             //查找哈希表中是否名字已经存在
193             int u_index = hash_find(tab, u);//u, v 在哈希表中的索引
194             int v_index = hash_find(tab, v);
195             if (u_index == -1) {//姓名不存在,将姓名插入哈希表,更新下标
196                 u_index = hash_insert(tab, u);
197                 Ceils[u_index].name = u;
198             }
199             if (v_index == -1) {//姓名不存在,将姓名插入哈希表,更新下标
200                 v_index = hash_insert(tab, v);
201                 Ceils[v_index].name = v;
202             }
203             adj[ufs_find(v_index)].push_back(ufs_find(u_index)); //u < v 即v 指向 u
204             Ceils[ufs_find(u_index)].indegree++;
205             Ceils[ufs_find(v_index)].outdegree++;
206             //cout << Ceils[u_index].name << " < " << Ceils[v_index].name << " /*图由 " << Ceils[ufs_find(v_index)].name << " 指向 "<< Ceils[ufs_find(u_index)].name << endl;
207         }
208     }
209 }
210 void __Free__() {//释放内存
211     for (int i = 0; i < MAXN; i++) adj[i].clear();//清空图邻接表
212     fileMsg.clear();//清空Msg中的元素
213     RES.clear();//清空结果数组
214     ResCnt = 0;
215     delete [] tab.Cells;
216     delete [] Ceils;
217 }
218 void Read_File(string filename) {
219     fstream input(filename, ios::in);//c++文件输入流读取文件,ios::in 以读方式打开
220     if (!input) {//如果没有找到文件,程序终止
221         cout << "没有找到文件!" << endl;
222         exit(-1);
223     }
224     string msg;
225     while (getline(input, msg)) {//逐行取存
226         fileMsg.push_back(msg);//存入fileMsg
227     }
228     input.clear();
229 }
230 enum TopState TopSort() {
231     queue<int> q;//用于拓扑排序的辅助队列
232     int count = 0;//入度为零的节点数,超过2个代表无法排序
233     enum TopState flag = True;//标记数据
234     int cnt = 0;//结果存储下标
235     for (int i = 0; i < MAXN; i++) {
236         if (Ceils[i].name != "-1" && Ceils[i].state != Hang && Ceils[i].indegree == 0) {//节点有效并且入度为0
237             q.push(i);//加入队列
238             count++;
239         }
240     }
241     if (count >= 2) flag = Contra;
242     while (!q.empty()) {
243         int t = q.front();
244         q.pop();
245         count--;
246         RES.push_back(t);
247         cnt++;
248         for (int i = 0; i < adj[t].size(); i++) {//寻找新的入度为0的节点,这个节点必然在上一个入度为零节点的邻接表中
249             int next = ufs_find(adj[t][i]);
250             Ceils[next].indegree--;
251             if (Ceils[next].indegree == 0) {
252                 count++;
253                 q.push(next);
254             }
255         }
256         if (count >= 2) {
257             flag = Contra;
258             Ceils[t].state = DataContra;
259         }
260     }
261     if (cnt == ResCnt && flag == True)
262         return True;
263     else if (cnt == ResCnt && flag == Contra)
264         return Contra;
265     else
266         return False;
267 }
268 void PrintRes(enum TopState res) {//输出结果
269     system("cls");
270     mainFrame();
271     if (res == True) {
272         string re = "数据可以完整排序, 排序结果为:";
273         setPos((WIDTH - re.size()) / 2 + LEFTOTHER, TOPOTHER + 10);//居中打印
274         cout << re << endl;
275         for (int i = 0; i < MAXN; i++) {//将"挂起"的节点插入到输出结果列表中,插入位置为其代表节点后方
276             if (Ceils[i].name != "-1" && Ceils[i].state == Hang) {
277                 RES.insert(find(RES.begin(), RES.end(), ufs_find(i)), i);
278             }
279         }
280         re = "";
281         setPos(LEFTOTHER + 14, TOPOTHER + 13);
282         for (list<int>::iterator lt = RES.begin(); lt != RES.end(); lt++) {
283             if (ufs[*lt] != *lt) {
284                 re += Ceils[*lt].name[0];
285                 re += "=";
286             }
287             else {
288                 re += Ceils[*lt].name[0];
289                 re += "->";
290             }  
291         }
292         cout << re.substr(0, re.size() - 2);//-2去掉最后一个箭头
293         string tip = "按任意键继续...";
294         setPos((WIDTH - tip.size()) / 2 + LEFTOTHER - 1, HIGHT + TOPOTHER - 2);
295         system("pause");
296     }
297     else if (res == Contra) {
298         string re = "数据不矛盾但无法完整排序: ";
299         setPos((WIDTH - re.size()) / 2 + LEFTOTHER, TOPOTHER + 10);//居中打印
300         cout << re;
301         re = "数据在 ";
302         for (list<int>::iterator lt = RES.begin(); lt != RES.end(); lt++) {
303             if (Ceils[*lt].state == DataContra) {
304                 re += Ceils[*lt].name[0];
305                 re += " ";
306             }
307         }
308         re += "后方出现无法排序的情况";
309         setPos((WIDTH - re.size()) / 2 + LEFTOTHER, TOPOTHER + 13);
310         cout << re;
311         string tip = "按任意键继续...";
312         setPos((WIDTH - tip.size()) / 2 + LEFTOTHER - 1, HIGHT + TOPOTHER - 2);
313         system("pause");
314     }
315     else {
316         string re = "数据矛盾无法排序: ";
317         setPos((WIDTH - re.size()) / 2 + LEFTOTHER, TOPOTHER + 10);//居中打印
318         cout << re;
319         re = "数据在 ";
320         re += Ceils[RES.back()].name[0];
321         re += " 后方出现数据矛盾";
322         setPos((WIDTH - re.size()) / 2 + LEFTOTHER, TOPOTHER + 13);
323         cout << re;
324         string tip = "按任意键继续...";
325         setPos((WIDTH - tip.size()) / 2 + LEFTOTHER - 1, HIGHT + TOPOTHER - 2);
326         system("pause");
327     }
328     
329 }
330 int hash_func(string name) {//哈希函数,返回应插入下标,以姓名的ASCII码总和作为哈希索引
331     int char_ascii_sum = 0;
332     for (int i = 0; i < name.size(); i++) {
333         char_ascii_sum += (int)name[i];//累加ASCII码
334     }
335     return char_ascii_sum % MAXN;
336 }
337 int hash_insert(HashTable& tab, string name) {//哈希表插入,采用线性探测,返回插入位置的下标
338     int index = hash_func(name);
339     while (tab.Cells[index].Info != Empty) {
340         index++;
341         if (index == MAXN) index = 0;
342     }
343     tab.Cells[index].name = name;
344     tab.Cells[index].Info = Legitimate;
345     tab.TableSize++;
346     return index;
347 }
348 int hash_find(HashTable& tab, string name) {
349     int index = hash_func(name);
350     while (tab.Cells[index].Info == Legitimate) {
351         if (tab.Cells[index].name == name) {
352             return index;
353         }
354         index++;
355         if (index == MAXN) index = 0;
356     }
357     return -1;
358 }