杭电数据结构课程实践-车厢调度

需求分析

  一列货运列车共有n节车厢,每节车厢将停放在不同的车站。假定n个车站的编号(由远到近)依次为1-n,即货运列车按照第n站至第1站的次序经过这些车站。为了便于从列车上卸掉相应的车厢,车厢的编号应与车站的编号相同,使各车厢从前至后按编号1-n的次序排序,这样,在每个车站只需卸掉最后一节车厢即可。因此,需要对给定任意次序的车厢进行重新排列。
  实现车厢重新排列的目的,可以通过转轨站来完成,在转轨站中有一个入轨、一个出轨和k个缓冲轨,缓冲轨位于入轨和出轨之间。开始时,任意的n节车厢入轨进入转轨站,转轨结束时各车厢按照编号1至n的次序离开转轨站进入出轨。假定缓冲轨按先进先出的方式运作,可将它们视为k个队列,并且禁止将车厢从缓冲轨移至入轨,也禁止从出轨移至缓冲轨。

概要设计

  基于队列及转轨站两种抽象数据类型,在分别对k个队列初始化、且设当前输出车厢号nowout=1、入轨车厢序号i=0,n节车厢重排的算法大致描述如下:

While(nowout<=n)
{
	如果各缓冲轨队列中有队头元素等于nowout
		则输出该车厢;
		nowout++;
	否则,求小于car[i]的最大队尾元素所在队列的编号j;
	如果j存在
		则把car[i]移至缓冲轨j;
	否则
		如果至少有一个空缓冲轨
		则把car[i]移至一 个空缓冲轨;
		否则,车厢无法重排,算法结束。
	i++;
}
车厢已重排,算法结束。

详细设计

  为由于车厢的数量并不确定,为避免溢出,本例采用带头结点的单链表实现队列的抽象数据类型。
  单链表实现的抽象数据LinkQueue.h文档如下:

#ifndef _LINKQUEUE_H_
#define _LINKQUEUE_H_

//链队列结点结构
struct LinkNode {
    QElemType data;
    LinkNode *next;
};

//带头结点的链队列结构 
struct LinkQueue {
    LinkNode  *front;         //队头指针
    LinkNode  *rear;          //队尾指针
};

//构造一个空的链队列。
void InitQueue( LinkQueue &Q )
{
    Q.front = Q.rear = new LinkNode ;
    Q.front->next = NULL;
}//LinkQueue

//将链队列清空。
void ClearQueue( LinkQueue &Q )
{
    LinkNode *p;
    while ( Q.front->next != NULL ) 
    {
        p = Q.front->next;
        Q.front->next = p->next;
        delete p;
    }
    Q.rear = Q.front;
}

//链队列结构销毁。
void DestroyQueue( LinkQueue &Q )
{
    ClearQueue( Q );  //成员函数Clear()的功能是释放链表中的所有元素结点
    delete Q.front;
    Q.front = Q.rear = NULL;
}

//判链队列是否为空,若为空,则返回true,否则返回false。
bool QueueEmpty( LinkQueue Q ) 
{
    return Q.front == Q.rear;
}

//返回链队列中元素个数。
int QueueLength( LinkQueue Q )
{
    int i = 0;
    LinkNode *p = Q.front->next;
    while ( p != NULL ) 
    {
        i++;
        p = p->next;
    }
    return i;
}

//取链队列队头元素的值。先决条件是队列不空。
QElemType GetHead( LinkQueue &Q )
{
    return Q.front->next->data;
}

//取链队列队尾元素的值。先决条件是队列不空。
QElemType GetLast( LinkQueue &Q ) 
{
    return Q.rear->data;
}

//链队列入队,插入e到队尾。
void EnQueue( LinkQueue &Q, QElemType e )
{
    LinkNode *p;
    p = new LinkNode ;
    p->data = e;
    p->next = NULL;
    Q.rear->next = p;
    Q.rear = p;
}

//链队列出队。先决条件是队列不空。
bool DeQueue( LinkQueue &Q,QElemType &e )
{   
    if ( QueueEmpty( Q ) ) 
        return false;
    LinkNode *p = Q.front->next;
    Q.front->next = p->next;
    e = p->data;
    if ( p == Q.rear )
	    Q.rear = Q.front;  //若出队后队列为空,需修改Q.rear。
    delete p;
    return true;
}

#endif

  转轨站抽象数据类型的实现,数据结构设计为队列的指针向量,其Realign.h文件如下:

#ifndef _Realign_H_
#define _Realign_H_

#include "LinkQueue.h"
struct	CTrackStation {
    LinkQueue *pTracks;  //各缓冲轨(队列)
    int trackCount; //缓冲轨数量
};

//初始化k个轨道 
void InitCTrackStation(CTrackStation &TS,int &k) {	
    TS.trackCount = k;
    TS.pTracks = new LinkQueue[ k ];
    for ( int i = 0; i < k; i++ )  
        InitQueue( TS.pTracks[ i ] ); 
}

//销毁k个轨道  
void DestroyCTrackStation( CTrackStation &TS )
{   
    for ( int i = 0; i < TS.trackCount; i++ )  
        DestroyQueue(TS.pTracks[i]);
    delete[] TS.pTracks;
    TS.trackCount = 0;
}

//将车厢car移到其中一个可用缓冲轨,成功返回true。
bool HoldIn( CTrackStation &TS, int &car, int &k )
{
    int bestTrack = - 1;		//目前最优的缓冲轨
    int bestLast = - 1;		//最优缓冲轨中的最后一节车厢
    int i;	
    for ( i = 0; i < TS.trackCount; i++ ) 
    {//查找最优缓冲轨
	    if ( ! QueueEmpty( TS.pTracks[ i ] ) ) 
        {
		    int last;		//车厢编号
		    last = GetLast( TS.pTracks[ i ] );
		    if(car > last && last > bestLast) 
            {//缓冲轨i尾部的车厢号较大
			    bestLast = last;
			    bestTrack = i;
		    }
	    }
    }
    if ( bestTrack == -1 ) 
    { //未找到合适缓冲轨,查找空闲缓冲轨 
	    for ( i = 0; i < TS.trackCount; i++ )
		    if ( QueueEmpty( TS.pTracks[ i ] ) ) 
            {
			    bestTrack = i;
			    break;
		    }
    }
    if ( bestTrack == -1 ) 
        return false;    //没有可用的缓冲轨
    EnQueue( TS.pTracks[ bestTrack ], car );
    k = bestTrack;//将车厢ca移入k号缓冲轨
    return true;
}

//将缓冲轨中车厢car移出,成功返回true。
bool HoldOut( CTrackStation &TS, int &car, int &k )
{   
    int i;
    for ( i = 0; i < TS.trackCount; i++ )
    {
	    if ( ! QueueEmpty( TS.pTracks[ i ] ) )
        {                                        
		    int headCar;    //车厢编号
	        headCar = GetHead( TS.pTracks[ i ] );
	        if ( headCar == car )
            {
		        DeQueue( TS.pTracks[ i ], headCar );
		        k = i; //将缓冲轨中车厢car从k号轨移出			
			    return true;
		    }
 	    }
    }
    return false;
}

#endif

  完整是程序包含对n节车厢进行重排的RealignCTrackStation函数及主函数等。
  车厢重排.cpp文档如下:

#include <iostream>
using namespace std;

typedef int QElemType;
#include "Realign.h"
//利用k个缓冲轨,对n节车厢重排
bool RealignCTrackStation( CTrackStation &TS, int *A, int &n )
{
    int k, nowOut = 1, i = 0;
    while ( nowOut <= n ) 
    {
        if ( HoldOut(TS, nowOut, k ) )
        {	
            cout << nowOut << " 号车厢从 "<< k << "号缓冲轨出队" << endl;
            nowOut++;
            continue;
        }
        if ( i >= n || ! HoldIn( TS, A[i], k ) ) 
            return false;
        cout << A[ i ] << " 号车厢进入 " << k << " 号缓冲轨" << endl;
        i++;
    }
    return true;
}

int main()
{   
    int i, m, k;
    cout << "请输入需重排的车厢数:";
    cin >> m;
    int car, A[ m ];
    cout << "请依次输入需重排的车厢序列编号:";
    for ( i = 0; i < m; i++ )  
        cin >> A[i];	
    cout << "请输入缓冲轨(队列)的数目:";
    cin >> k;
    cout << endl;
    CTrackStation trackStation; //构建缓冲轨站
    bool ok = false;
    do
    {  
        InitCTrackStation( trackStation, k );
        if ( RealignCTrackStation( trackStation, A, m ) ) 
        {//利用缓冲轨站重排车厢
            ok = true;
            cout << endl << "车厢已重排!" << endl;
        } 
        else 
        {
            DestroyCTrackStation( trackStation );
            cout << "缓冲轨的数目为"<< k <<"时,因车厢无法重排,请重输缓冲轨的数目:";
            cin >> k;
            cout << endl;
        }	
    }while ( ! ok );	
    DestroyCTrackStation( trackStation );	
   	
    return 0;
}

程序流程图

在这里插入图片描述

测试数据

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值