马上找工作了,最近又重新学起了数据结构,打算从现在开始,把学习过程中的心得体会和大家分享一下。当然这些内容会显得肤浅,但是希望会对新手有些帮助。大牛可以绕路咯。
好了,我们直奔主题,我们开始分析一下现实中的一中典型需求,以此作为开始:
实际问题:
一个运动会:有game_num个项目;
有anthelete_num名运动员;
每个运动员最多的参加max个项目;
问:怎么安排比赛才能使比赛组数最少(即如何安排各项比赛,既没有冲突,又使得比赛时间最短?)。
分析:
首先我们根据报名情况可以建立一个运动员参赛冲突关系矩阵collusion,是game_num*game_num的二维矩阵,元素0代表无冲突,1代表有冲突。例如,某个运动员报名的项目为(0,2,4)则对应的collusion[0][2],collusion[0][4],collusion[2][0],collusion[4][0],collusion[4][2],collusion[2][4]的值为1.
然后我们借助一个队列记录还未分组的项目,一个数组clash记录与该组冲突的比赛项目,其大小为game_num。
过程:初始时,把分组的组号初始化为0,并且把所有比赛项目进队列,表示所有项目都没有分组。
然后逐一出队列,进行分组,知道队列为空,分组完毕。
对于出队列的项目i,首先判断是否开始一轮新的比赛项目。一般出队列的项目号都大于先前出队列的项目号,如果小于,则说明开始一轮新的比赛项目,一个新的分组开始。此时,把分组的组号加1,数组clash置0, 准备下面的同组成员判定。
(1)如果数组项目i对应的元素clash[i]为0,表示当前组可以接受该项目,记录该项目i的分组号,且把该项目的比赛冲突信息叠加到数组clash中,即把运动员参赛冲突关系矩阵collusion的第i+1行叠加到数组clash上。理由是,当前组可以接受项目i,则与项目i冲突的比赛项目,也就是与该组冲突的项目,所以叠加记载到数组clash中。
(2)如果数组clash中项目i对应的元素clash[i]不为0,表示当前组不可以接受该项目,要将其重新进队列,等待下一轮的判定。
实现:
在程序中我们会用到队列这种数据结构(循环队列),所以我们对队列进行定义,定义数据结构的代码中难免有些冗余,这是为了我对数据结构学习的更全面一些。为了代码重用性更强一些,用到了类模板。
简单介绍下循环队列:队头指针指向第一个元素,队尾指针指向最后一个元素的下一个位置,队头指针前移方式front = (front + 1) % queueSize,队尾指针前移方式:rear = (rear + 1) % queueSize。
定义完队列以后,我们便开始程序的其他实现。
代码如下:
PreDefine.h
1 //代码中一些常量定义 2 #ifndef PREDEFINE_H 3 #define PREDEFINE_H 4 5 #define TRUE 1 6 #define ERROR 0 7 #define OK 1 8 #define FALSE 0 9 10 #endif
1 / 2 //SqQueue.h循环顺序队列数据结构C++定义 3 // 4 #include "PreDefine.h" 5 #include<assert.h> 6 #ifndef SQQUEUE_H 7 #define SQQUEUE_H 8 template <typename ElemType> 9 class SqQueue 10 { 11 public: 12 //把循环顺序队列置空 13 void clear(); 14 //判断循环队列是否为空 15 bool isEmpty(); 16 //出队列(删除循环顺序队列对头元素) 17 bool deQueue(ElemType& e); 18 //判断循环顺序队列是否满 19 bool isFull(); 20 //进队列 21 bool enQueue(ElemType e); 22 //读循环顺序队列头的元素 23 bool getFront(ElemType& e); 24 //求循环顺序队列中元素的个数 25 int getLenght(); 26 //重载赋值运算符的定义 27 SqQueue<ElemType>operator = (SqQueue<ElemType>rightQ); 28 29 //*************下面为系统自动调用的构造函数及析构函数声明********// 30 //构造函数 31 SqQueue(int size = 0); 32 //析构函数 33 ~SqQueue(); 34 //拷贝初始化构造函数 35 SqQueue(const SqQueue<ElemType>& otherQ); 36 37 protected: 38 int rear;//队尾指针 39 int front;//队头指针 40 int queueSize;//队列大小 41 ElemType *base;//队列基指针 42 }; 43 //******************循环顺序队列的实现*********************** 44 //队列置空 45 template<typename ElemType> 46 void SqQueue<ElemType>::clear() 47 { 48 front = rear; 49 } 50 //判读队列是否为空 51 template<typename ElemType> 52 bool SqQueue<ElemType>::isEmpty() 53 { 54 return front == rear ? TRUE : FALSE; 55 } 56 //出队列 57 template<typename ElemType> 58 bool SqQueue<ElemType>::deQueue(ElemType& e) 59 { 60 if (isEmpty()) 61 return FALSE; 62 e = base[front]; 63 front = (front + 1) % queueSize;//对头指针前移 64 return OK; 65 } 66 //判断队列是否满 67 template <typename ElemType> 68 bool SqQueue<ElemType>::isFull() 69 { 70 return (rear + 1) % queueSize == front% queueSize ? TRUE : FALSE; 71 } 72 //进队列 73 template<typename ElemType> 74 bool SqQueue<ElemType>::enQueue(ElemType e) 75 { 76 if (isFull()) 77 return FALSE; 78 base[rear] = e; 79 rear = (rear + 1) % queueSize;//队尾指针前移 80 return OK; 81 } 82 //读队列的头元素 83 template <typename ElemType> 84 bool SqQueue<ElemType>::getFront(ElemType& e) 85 { 86 if (isEmpty()) 87 return ERROR; 88 e = base[front]; 89 return OK; 90 } 91 //求队列中元素的个数 92 template <typename ElemType> 93 int SqQueue<ElemType>::getLenght() 94 { 95 return (rear - front + queueSize) % queueSize; 96 } 97 //重载赋值运算符 98 template<typename ElemType> 99 SqQueue<ElemType> SqQueue<ElemType>::operator=(SqQueue<ElemType> rightQ) 100 { 101 if (this != &rightQ)//如果左右队列不一样 102 { 103 if (queueSize != rightQ.queueSize)//如果左右队列的大小不一样,则重新为左边队列分配空间 104 { 105 delete[] base; 106 base = new ElemType[rightQ.queueSize]; 107 assert(base != 0); 108 queueSize = rightQ.queueSize; 109 110 } 111 //将右边的队列元素赋值到左边的队列中 112 front = rightQ.front; 113 rear = rightQ.rear; 114 for (int j = fron; j %queueSize != rear;) 115 { 116 base[j] = rightQ.base[j]; 117 j = (j + 1) % queueSize;//指针前移 118 119 } 120 } 121 return *this; 122 } 123 124 125 //******************************下面为系统自动调用的构造函数和析构函数的实现*********** 126 //构造函数 127 template <typename ElemType> 128 SqQueue<ElemType> ::SqQueue(int size) 129 { 130 base = new ElemType[size]; 131 assert(base != 0); 132 front = rear = 0; 133 queueSize = size; 134 } 135 //析构函数 136 template <typename ElemType> 137 SqQueue<ElemType>::~SqQueue() 138 { 139 delete[]base; 140 } 141 //赋值构造函数 142 template<typename ElemType> 143 SqQueue<ElemType> ::SqQueue(const SqQueue<ElemType>& otherQ) 144 { 145 base = new ElemType[otherQ.queueSize]; 146 aseert(base != 0); 147 queueSize = otherQ.queueSize; 148 front = otherQ.front; 149 rear = otherQ.rear; 150 for (int j = front; j%queueSize != rear;) 151 { 152 base[j] = otherQ.base[j]; 153 j = (j + 1) % queueSize; 154 155 } 156 } 157 #endif
MySportsManagement.h
1 #ifndef MYSPORTSMANAGEMENT_H 2 #define MYSPORTSMANAGEMENT_H 3 #include "PreDefine.h" 4 #include"SqQueue.h" 5 #include<assert.h> 6 #include<iostream> 7 using namespace std; 8 //********MySportsManagement.h定义*************** 9 class MySportsManagement 10 { 11 public: 12 //安排比赛 13 void arrangeSports(); 14 //显示项目冲突矩阵 15 void displayCollusion()const; 16 17 //**********下面为系统自动调用的构造函数、析构函数 和输入输出函数的声明 18 //构造函数 19 MySportsManagement(int a=9,int b=3,int c=7,int* d=NULL,int* e=NULL); 20 //析构函数 21 ~MySportsManagement(); 22 //输入运动会项目及比赛情况 23 void read(istream& in); 24 //输出运动会项目以及比赛情况 25 void display(ostream& out)const; 26 private: 27 int game_num;//比赛项目数 28 int max;//每个运动员最多可以报的项目数 29 int anthelete_num;//运动员数量 30 int *collusion;//项目冲突矩阵 31 int *game_arrange;//比赛安排数组 32 }; 33 34 ///***********MySportsManagement C++实现************* 35 //安排比赛的实现 36 void MySportsManagement::arrangeSports() 37 { 38 int pre = game_num;//预设前一个项目号 39 int group = 0;//存放当前分组的组号,初始化为0,表示还没有分组 40 int *clash;//预存放某组项目的冲突关系 41 SqQueue<int> Q;//预存放待分组项目的队列 42 if (!collusion) 43 { 44 cout << "没有冲突矩阵,比赛无法安排" << endl; 45 } 46 //队列Q被初始化为所有比赛项目的下标 47 for (int i = 0; i < game_num; i++) 48 { 49 Q.enQueue(i); 50 } 51 clash = new int[game_num];//申请冲突关系数组 52 assert(clash != 0); 53 //所有比赛项目逐一分组 54 while (!Q.isEmpty()) 55 { 56 int i; 57 Q.deQueue(i);//一个项目出队列 58 //第一个项目代表新的一轮比赛项目 59 if (i <= pre) 60 { 61 ++group;//组号加1 62 //clash全部元素赋值为0 63 for (int j = 0; j < game_num; j++) 64 clash[j] = 0; 65 } 66 67 if (clash[i] == 0) 68 { 69 game_arrange[i] = group;//将第i个项目分配为第group组 70 //将collusion数组第i行的元素加到clash数组上 71 for (int j = 0; j < game_num; ++j) 72 clash[j] += *(collusion + i*game_num + j); 73 } 74 //如果clash数组中已经存在冲突项目,则将这个项目放回队列中,等待重新分配 75 else 76 { 77 Q.enQueue(i); 78 } 79 80 pre = i;//将前一个出队列项目号设置为刚刚出队列的项目号 81 }//while 82 } 83 84 //显示冲突矩阵 85 void MySportsManagement::displayCollusion()const 86 { 87 int i, j; 88 char no[5] = "[ i]"; 89 //根据比赛项目数,显示项目冲突矩阵的列号 90 cout << " "; 91 for (i = 0; i < game_num; ++i) 92 { 93 if (i<9) 94 { 95 no[2] = i + '0'; 96 } 97 else 98 { 99 no[1] = i / 10 + '0'; 100 no[2] = i % 10 + '0'; 101 } 102 cout.width(4);//每个比赛项目序号显示占4个字符 103 cout.fill(' ');//不足空格补齐 104 cout.setf(ios::right, ios::adjustfield);//靠右对其 105 cout << no;//输出列号 106 } 107 cout << endl;//换行 108 //逐行显示项目冲突矩阵 109 for (i = 0; i < game_num; ++i) 110 { 111 cout << " "; 112 if (i < 9) 113 no[2] = i + '0'; 114 else 115 { 116 no[1] = i / 10 + '0'; 117 no[2] = i % 10 + '0'; 118 } 119 //每个比赛项目序号显示占4个字符,不足空格补齐,靠右对齐 120 cout.width(4); 121 cout.fill(' '); 122 cout.setf(ios::right, ios::adjustfield); 123 cout << no; 124 //显示项目冲突矩阵的元素 125 for (j = 0; j < game_num; ++j) 126 { 127 cout << " " << *(collusion + i*game_num + j) << " "; 128 } 129 cout << endl; 130 } 131 } 132 133 //***********下面为系统自动调用的构造函数、析构函数和输入输出函数********** 134 //构造函数 135 MySportsManagement::MySportsManagement(int a, int b, int c, int* d, int* e) 136 { 137 game_num = a;//比赛项目 138 max = b;//每个运动员最多允许参加的比赛项目数 139 anthelete_num = c;//运动员人数 140 //申请项目冲突矩阵的存储空间 141 collusion = new int[game_num * game_num]();//冲突数组的大小为game_num的平方个,并初始化为0 142 assert(collusion != 0); 143 144 //比赛安排 145 game_arrange = new int[game_num]();//申请比赛安排的存储空间,并初始化为0 146 assert(game_arrange != 0); 147 } 148 //析构函数 149 MySportsManagement::~MySportsManagement() 150 { 151 delete[] collusion; 152 delete[] game_arrange; 153 } 154 //输入运动会项目及运动员参赛情况 155 void MySportsManagement::read(istream& in) 156 { 157 int i, j, k;//预控制循环的变量 158 int x;//预存放某个运动员的参赛项目 159 int num;//预存放某个运动员的参赛数目 160 int temp[10];//预存放某个运动员的所有参赛项目 161 162 cout << " 请输入比赛项目数:"; 163 in >> game_num; 164 165 cout << "请输入每个运动员最多允许参加的比赛项目:"; 166 in >> max; 167 168 cout << "请输入运动员人数:"; 169 in >> anthelete_num; 170 171 172 collusion = new int[game_num*game_num]();//申请冲突矩阵的存储空间,并初始化为0 173 assert(collusion != 0); 174 175 cout << "\n 请输入每个运动员参赛情况:" << endl << endl; 176 177 for (k = 1; k <= anthelete_num; ++k) 178 { 179 cout << " 请输入第" << k << "个运动员报名参赛情况:" << endl; 180 while (1) 181 { 182 cout << " 输入第" << k << "个运动员参赛数目:"; 183 in >> num; 184 if (num>0 && num <= max) 185 { 186 break; 187 } 188 else 189 { 190 cout << " 每个运动员最多允许参加的比赛项目为:" << max << endl; 191 } 192 } 193 194 //输入第i个运动员所有参赛项目(不能有重复的参赛项目) 195 196 j = 0; 197 while (j<num) 198 { 199 cout << " 第" << j + 1 << "个参赛项目为:"; 200 in >> x; 201 if (x>-1 && x<game_num) 202 {//判断该项目是否已经输入 203 for (i = 0; i<j; ++i) 204 { 205 if (temp[i] == x) 206 { 207 break; 208 } 209 } 210 if (i == j) 211 temp[j++] = x; 212 else 213 { 214 cout << " 该比赛项目已经输入" << endl; 215 } 216 } 217 else 218 { 219 cout << " 参赛项目应该在0到" << game_num - 1; 220 cout << "之间,请重新输入!" << endl; 221 } 222 }//while 223 //根据每个运动员的参赛项目temp数组,填写项目冲突矩阵 224 for (int i = 0; i < num; ++i) 225 for (j = i + 1; j < num; ++j) 226 { 227 //对称矩阵 228 *(collusion + temp[i] * game_num + temp[j]) = 1; 229 *(collusion + temp[j] * game_num + temp[i]) = 1; 230 } 231 cout << endl; 232 }//for 233 //显示冲突矩阵 234 displayCollusion(); 235 } 236 //重载输入运算符的定义 237 istream& operator>>(istream& in, MySportsManagement& sQ) 238 { 239 sQ.read(in); 240 return in; 241 } 242 //输出运动会项目及比赛情况 243 void MySportsManagement::display(ostream& out)const 244 { 245 char no[5] = "[ i]";//比赛项目序号的字符表示 246 247 if (!game_num) 248 { 249 out << " 没有任何运动会的信息!" << endl; 250 return; 251 } 252 253 out << " 比赛项目数:" << game_num << endl; 254 255 out << " 每个运动员最多允许参加的比赛项目数:" << max << endl; 256 257 if (!anthelete_num) 258 {//如果运动员的数目为0,则提示还没有运动员报名,返回 259 out << " 还没有运动员报名!" << endl; 260 return; 261 } 262 out << " 项目冲突矩阵:"; 263 264 displayCollusion(); 265 if (!game_arrange) 266 {//如果比赛安排为空,则提示各项比赛分组还没有安排,返回 267 out << " 各项比赛分组还没有安排!" << endl; 268 return; 269 } 270 out << "\n 各项比赛分组安排如下:" << endl; 271 out << " "; 272 //为显示各项比赛的分组安排,事先显示一行比赛项目号 273 for (int i = 0; i < game_num; ++i) 274 { 275 if (i < 9) 276 no[2] = i + '0'; 277 else 278 { 279 no[1] = i / 10 + '0'; 280 no[2] = i % 10 + '0'; 281 } 282 //每个比赛项目号显示占4个字符,不足以空格补齐,靠右对齐 283 out.width(4); 284 out.fill(' '); 285 out.setf(ios::right, ios::adjustfield); 286 out << no; 287 } 288 out << endl; 289 out << " "; 290 for (int i = 0; i < game_num; ++i) 291 { 292 out.width(4); 293 out.fill(' '); 294 out.setf(ios::right, ios::adjustfield); 295 out << *(game_arrange + i); 296 297 } 298 out << endl;//换行 299 } 300 //重载输出运算符的定义 301 ostream& operator<<(ostream& out, MySportsManagement& sQ) 302 { 303 sQ.display(out); 304 return out; 305 } 306 307 #endif
1 //main.cpp 2 3 #include"MySportsManagement.h" 4 #include<iostream> 5 using namespace std; 6 int main() 7 { 8 cout << "***********************开始安排比赛*********************" << endl << endl; 9 MySportsManagement MSM; 10 cin >> MSM; 11 MSM.arrangeSports(); 12 cout << MSM; 13 return 0; 14 }
运行结果为:
上述过程难免有错误,大家批评指正。
另附:链式队列的实现代码
#ifndef LINKQUEUE_H #define LINKQUEUE_H #include<assert.h> //非循环链队数据结构C++类声明 template<typename ElemType> class LinkQueue { private: class LinkNode { public: ElemType data; LinkNode* next; }; typedef LinkNode* NodePointer; public: //循环链队置空 void clear(); //出队列 bool deQueue(ElemType& e); //进队列 bool enQueue(ElemType e); //读非循环练队列头结点的数据域 bool getFront(ElemType& e); //判断链队是否为空 bool isEmpty(); //求链队中结点的个数 int getLength(); //重载赋值运算符的定义 LinkQueue<ElemType> operator= (LinkQueue<ElemType> rightQ); //****************下面为系统自动调用的构造函数以及析构函数声明*************** //构造函数 LinkQueue(); //析构函数 ~LinkQueue(); //初始化构造函数 LinkQueue(const LinkQueue<ElemType>& otherQ); protected: NodePointer rear; NodePointer front; }; //**********非循环链队的定义********************** //把链队置空 template<typename ElemType> void LinkQueue<ElemType>::clear() { NodePointer q; NodePointer p = front; while (p) { q = p; p = p->next; delete q; } front = rear = 0; } //出队列 template<typename ElemType> bool LinkQueue<ElemType>::deQueue(ElemType& e) { if (!front) { return -1; } NodePointer s = front; e = s->data; front = front->next; delete s; if (!front) {//rear还指向原来的内存,所以需要置空 rear = 0 } return 0; } //进队列 template<typename ElemType> bool LinkQueue<ElemType>::enQueue(ElemType e) { NodePointer s; s = new(LinkNode); assert(s != 0); s->data = e; s ->next = 0; if (!front) { front = rear = s; } else { rear->next = s; rear = s; } } //读队列头结点的数据域 template<typename ElemType> bool LinkQueue<ElemType>::getFront(ElemType& e) { if (!front) { return -1; } e = front->data; front = front->next; } //判断队列是否为空 template<typename ElemType> bool LinkQueue<ElemType>::isEmpty() { return (!front ? 0 : -1); } //求队列的元素个数 template<typename ElemType> int LinkQueue<ElemType>::getLength() { int lenghth = 0; NodePointer p = front; while (p) { ++lenght; p = p->next; } return lenghth; } //重载赋值运算符 template<typename ElemType> LinkQueue<ElemType> LinkQueue<ElemType>::operator=(LinkQueue<ElemType> rightQ) { NodePointer s; NodePointer rp = rightQ.front; if (this!=&right) { clear(); while (rp) { s = new(LinkNode); assert(s != 0); s->data = rp->data; s->next=NULL: if (!fron) { front = rear = s; } else { rear->next = s; rear = s; } } } return *this; } //***********下面为系统自动调用的构造函数以及析构函数的定义************** //构造函数 template<typename ElemType> LinkQueue<ElemType>::LinkQueue() { front=rear=NULL: } //析构函数 template <typename ElemType> LinkQueue<Elemtype>::~LinkQueue() { clear(); } //拷贝初始化构造函数 template<typename ElemType> LinkQueue<ElemType>::LinkQueue(const LinkQueue<ElemType>& otherQ) { NodePointer s; NodePointer op = otherQ.front; rear = front = NULL; while (op) { s = new(LinkNode); assert(s != 0); s->data = op->data; s->next = NULL; if (!fron) { front = rear = s; } else { rear->next = s; rear = s; } op = op->next; } } #endif