第一篇:实践十四 计算机木马检测
实践十四 计算机木马检测
一、实践目的和要求
1.了解木马程序的攻击原理。2.掌握检测木马程序的方法。
二、实践设备及材料
常用木马检测软件软件。
三、实践指导和实践内容(一)实践指导
1、手工方法:
(1)检查网络连接情况
由于不少木马会主动侦听端口,或者会连接特定的IP和端口,所以我们可以在没有正常程序连接网络的情况下,通过检查网络连情情况来发现木马的存在。具体的步骤是点击“开始”->“运行”->“cmd”,然后输入netstat-an这个命令能看到所有和自己电脑建立连接的IP以及自己电脑侦听的端口,它包含四个部分——proto(连接方式)、local address(本地连接地址)、foreign address(和本地建立连接的地址)、state(当前端口状态)。通过这个命令的详细信息,我们就可以完全监控电脑的网络连接情况。(2)查看目前运行的服务
服务是很多木马用来保持自己在系统中永远能处于运行状态的方法之一。我们可以通过点击“开始”->“运行”->“cmd”,然后输入“net start”来查看系统中究竟有什么服务在开启,如果发现了不是自己开放的服务,我们可以进入“服务”管理工具中的“服务”,找到相应的服务,停止并禁用它。(3)检查系统启动项 由于注册表对于普通用户来说比较复杂,木马常常喜欢隐藏在这里。检查注册表方法如下:点击“开始”->“运行”->“regedit”,检查KEY_LOCAL_MACHINESoftwareMicrosoftWindows CurrentVersion所有以“run”开头键值;HKEY_CURRENT_USERSoftwareMicrosoftWindowsCurrent Version下以“run”开头键值;HKEY-USERS.DefaultSoftwareMicrosoftWindowsCurrentVersion。
下所有以“run”开头的键值。Windows安装目录下的System.ini也是木马喜欢隐蔽的地方。打开这个文件看看,在该文件的[boot]字段中,是不是有shell=Explorer.exe file.exe这样的内容,如有这样的内容,那这里的file.exe就是木马程序了!
(4)检查系统帐户 恶意的攻击者喜在电脑中留有一个账户的方法来控制你的计算机。他们采用的方法就是激活一个系统中的默认账户,但这个账户却很少用的,然后把这个账户的权限提升为管理员权限,这个帐户将是系统中最大的安全隐患。恶意的攻击者可以通过这个账户任意地控制你的计算机。针对这种情况,可以用以下方法对账户进行检测。点击“开始”->“运行”->“cmd”,然后在命令行下输入net user,查看计算机上有些什么用户,然后再使用“net user 用户名”查看这个用户是属于什么权限的,一般除了Administrator是administrators组的,其他都不应该属于administrators组,如果你发现一个系统内置的用户是属于administrators组的,那几乎可以肯定你被入侵了。快使用“net user用户名/del”来删掉这个用户吧!如果检查出有木马的存在,可以按以后步骤进行杀木马的工作。
1、运行任务管理器,杀掉木马进程。
2、检查注册表中RUN、RUNSERVEICE等几项,先备份,记下可以启动项的地址,再将可疑的删除。
3、删除上述可疑键在硬盘中的执行文件。
4、一般这种文件都在WINNT,SYSTEM,SYSTEM32这样的文件夹下,他们一般不会单独存在,很可能是有某个母文件复制过来的,检查C、D、E等盘下有没有可疑的.exe,.com或.bat文件,有则删除之。
(5)检查注册表HKEY_LOCAL_MACHINE和HKEY_CURRENT_USERSOFTWAREMicrosoftInternet ExplorerMain中的几项(如Local Page),如果被修改了,改回来就可以。
(6)检查HKEY_CLASSES_ROOTtxtfileshellopencommand和HKEY_CLASSES_ROOTxtfileshellopen command等等几个常用文件类型的默认打开程序是否被更改。这个一定要改回来。很多病毒就是通过修改.txt文件的默认打开程序让病毒在用户打开文本文件时加载的。2.利用工具
查杀木马的工具有LockDown、The Clean、木马克星、金山木马专杀、木马清除大师、木马分析专家等,其中有些工具,如果想使用全部功能,需要付一定的费用,木马分析专家是免费授权使用。
(二)实践内容
按照实践指导,检测本机上是否有木马程序、做好检查步骤和实践记录。
五、实践注意事项
在对注册表进行操作前,最好对注册表做一个备份。
第二篇:Web安全之网页木马的检测与防御
Web安全之网页木马的检测与防御
随着Web2.0、社交网络、微博等等一系列新型的互联网产品的诞生,基于Web环境的互联网应用越来越广泛,企业信息化的过程中各种应用都架设在Web平台上,Web业务的迅速发展也引起黑客们的强烈关注,接踵而至的就是Web安全威胁的凸显,黑客利用网站操作系统的漏洞和Web服务程序的SQL注入漏洞等得到Web服务器的控制权限,轻则篡改网页内容,重则窃取重要内部数据,更为严重的则是在网页中植入恶意代码,使得网站访问者受到侵害。在Web安全的攻击种类中,网页木马一直是一个不容忽视的问题。黑客把一个木马程序上传到一个网站里面然后用木马生成器生一个网马,再上到空间里面,再加代码使得木马在打开网页里运行,从而获取用户的隐私信息。随着网页木马破坏性的增大,人们对网页木马的重视程度也在逐渐增加。网页木马简介
1.1网页木马的定义
网页木马是在宏病毒、木马等恶意代码基础上发展出来的一种新形态的恶意代码.类似于宏病毒通过Word 等文档中的恶意宏命令实现攻击.网页木马一般通过HTML 页面中的一段恶意脚本达到在客户端下载、执行恶意可执行文件的目的,而整个攻击流程是一个“特洛伊木马式”的隐蔽的、用户无察觉的过程,因此,国内研究者通常称该种攻击方式为“网页木马”.目前,学术界对网页木马尚无一个明确的、统一的定义.Wiki 将它定义为一种用户不知情的下载,发生的场景可以是用户阅览邮件、访问网站页面以及点击一个欺诈性的弹出框时,等等,该定义属于字面解释,包括的内容比较宽泛,如利用社会工程学手段欺骗用户下载也属于其涵盖范围.此外,Wiki 还用Drive-by-Install 这一术语来表示下载并安装恶意程序的过程;Google 的Provos 等人将网页木马限定为客户端在访问页面时受到攻击并导致恶意可执行文件的自动下载、执行,该定义指出了“访问页面时受到攻击”和“自动下载、自动执行恶意可执行文件”两个关键点;UCSB 的Cova 等人进一步指出,网页木马是以一段JavaScript 代码作为攻击向量的一种客户端攻击方式,而实际上,还存在一些通过CSS 元素、VBScript 脚本发起攻击的网页木马。
综上所述,网页木马定义为:一种以JavaScript,VBScript,CSS 等页面元素作为攻击向量,利用浏览器及插件中的漏洞,在客户端隐蔽地下载并执行恶意程序的基于Web 的客户端攻击。
1.2网页木马的攻击流程
网页木马采用一种被动攻击模式(即pull-based 模式):攻击者针对浏览器及插件的某个特定漏洞构造、部署好攻击页面之后,并不像发送垃圾邮件那样主动将内容推送给受害用户(即push-based 模式),而是被动地等待客户端发起的页面访问请求.其典型攻击流程如图1 所示:1.客户端访问攻击页面;2.服务器做出响应,将页面内容返回给客户端;3.页面被浏览器加载、渲染,页面中包含的攻击向量在浏览器中被执行并尝试进行漏洞利用;4,5.存在该漏洞的客户端被攻破,进而下载、安装、执行恶意程序。
从网页木马的攻击流程可以看出,网页木马是一种更加隐蔽、更加有效的向客户端传播恶意程序的手段:相比较蠕虫以主动扫描等方式来定位受害机并向其传播恶意程序,网页木马采用一种“守株待兔”的方式等待客户端主动对页面发出访问请求,这种被动式的攻击模式能够有效地对抗入侵检测、防火墙等系统的安全检查.1.3可能被网页木马利用的漏洞
1.利用URL格式漏洞
此类网页木马是利用URL格式漏洞来欺骗用户。构造一个看似JPG格式的文件诱惑用户下载,但事实上用户下载的却是一个EXE文件。此类攻击,具有相当的隐蔽性,利用URL欺骗的方法有很多种,比如起个具有诱惑性的网站名称或使用易混的字母数字掉包进行银行网络钓鱼,还有漏洞百出的“%30%50”之类的Unicode编码等等。2.通过ActiveX控件制作网页木马。
通过 ActiveX 把普通的软件转化为可以在主页直接执行的软件的网页木马,此类网页木马对所有的系统和IE版本都有效,缺点是浏览网页木马时会弹出对话框,询问是否安装此插件。病毒作者通常是伪造微软、新浪、Google等知名公司的签名,伪装成它们的插件来迷惑用户。
3.利用WSH的缺陷
利用WSH修改注册表,使IE安全设置中“没有标记为安全的的activex控件和插件”的默认设置改为启用,然后再利用一些可以在本地运行EXE程序的网页代码来运行病毒。它的危害在于,可以利用IE的安全漏洞提升权限达到本地运行任意程序的后果。4.利用MIME漏洞制做的网页木马。
它利用了Microsoft Internet Explorer中MIME/BASE64处理的漏洞,MIME(Multipurpose Internet Mail Extentions),一般译作“多用途的网络邮件扩充协议”。顾名思义,它可以传送多媒体文件,在一封电子邮件中附加各种格式文件一起送出。现在它已经演化成一种指定文件类型(Internet的任何形式的消息:E-mail,Usenet新闻和Web)的通用方法。在使用CGI程序时你可能接触过 MIME类型,其中有一行叫作Content-type的语句,它用来指明传递的就是MIME类型的文件(如text/html或 text/plain)。MIME在处理不正常的MIME类型时存在一个问题,攻击者可以创建一个Html格式的E-mail,该E-mail的附件为可执
行文件,通过修改MIME头,使IE不能正确处理这个MIME所指定的可执行文件附件。5.利用系统的图片处理漏洞
这是种只要浏览图片就可以传播的网页木马。这种木马是把一个EXE文件伪装成一个BMP或JPG图片文件,在网页中添加如 的代码来欺骗IE自动下载,然后利用网页中的Java Script脚本查找客户端的Internet临时文件夹,寻找下载的BMP格式文件,把它拷贝到TEMP目录.再利用脚本把找到的BMP文件用 DEBUG还原成EXE,并添加到注册表启动项中,达到随系统的目的。6.HTML文件木马
首先利用工具把EXE格式的文件转化成HTML文件的木马,这样.EXE文件看起来就变成了Htm文件,欺骗访问者访问假冒的Htm文件从而达到运行病毒的目的。它和BMP网页木马运用了同一个原理,也会利用JAVASCRIPT脚本和debug程序来转换回EXE文件 7.IFRAME溢出
IE IFRAME漏洞利用了MS05020中提到的IE溢出漏洞,IE在处理iframe标签的时候,会调用一个叫作SHDOCVW!CBaseBrowser2::SetFramName函数来进行unicode copy(wcscpy),在拷贝iframe的name时,没有进行边界检查,构造恶意的代码就会导致IE的溢出。
8.Microsoft Internet Explorer Javaprxy.DLL COM对象堆溢出漏洞
Microsoft Internet Explorer存在一个堆溢出漏洞。当一个恶意的网页实例化'javaprxy.dll' COM对象时,可能导致堆溢出,成功利用该漏洞能够在客户端上下载执行任意代码。网页木马的主要检测技术
在互联网中大规模进行页面检查,检测出被挂马页面,是网页木马防御的重要环节:一方面,可以通知被挂马网站的管理员及时清除页面中嵌入的恶意内容,从而改善互联网浏览环境;另一方面,有助于让防御方掌握网页挂马的范围、趋势以及捕获最新的网页木马样本.2.1监测基本思想
大规模网页挂马检测的基本思路为:使用爬虫爬取互联网页面,将爬取到的URL 输入到检测环境——客户端蜜罐中进行网页木马检测.客户端蜜罐(client honeypot)这一概念首先由国际蜜网项目组(The HoneynetProject)的Spitzner 针对网页木马这种被动式的客户端攻击而提出.在网页挂马检测时,客户端蜜罐根据URL 主动地向网站服务器发送页面访问请求,并通过一定的检测方法分析服务器返回的页面是否带有恶意内容.在互联网中大规模进行网页挂马检测有两个关键点: 1)提高爬虫爬取的覆盖率,这方面可以通过采用大规模的计算平台、设计均衡的并行爬取分配策略等来实现
2)在客户端蜜罐中实现高效、准确的网页木马检测方法。
2.2主要监测方法
• 基于反病毒引擎扫描的检测
基于反病毒引擎扫描是研究人员进行网页挂马检测时较早使用的方法,该方法主要基于规则或特征码匹配来检测页面中是否含有恶意内容.该方法的优点在于可以快速地对页面进行检测,但其缺点在于存在较高的漏报率:一方面,仅仅依赖一种纯静态的特征匹配无法对抗网页木马为了提高隐蔽性而普遍采用的混淆免杀机制,根据互联网安全技术北京市重点实验室在2008 年底作的统计发现,9 款国内外主流反病毒软件对在中国互联网上发现的网页木马样本最高仅达到36.7%的检测率;另一方面,研究人员通常仅对单页面进行扫描,而不进一步检测页面动态视图中的内嵌脚本/内嵌页面,从而无法有效检测出被挂马页面.• 基于行为特征的检测
基于行为特征的网页木马检测方法通常被用于高交互式客户端蜜罐中:即在检测环境中安装真实浏览器和一些带有漏洞的插件,驱动浏览器访问待检测页面,通过监测页面访问时注册表变化、文件系统变化、新建进程等行为特征来判定页面是否被挂马.为了便于感染后快速恢复以及防止恶意程序在本地网络中的传播,高交互式客户端蜜罐一般部署在虚拟机中并作一定的网络隔离.• 基于统计或机器学习的检测
由于网页木马经常对恶意脚本进行混淆来躲避基于特征码的检测,一种检测思路是按照页面的混淆程度来进行恶意性判断.文献提出了基于判断矩阵法的恶意脚本检测方法,但该方法仅对经过encode escape 函数混淆的恶意脚本有效.随着越来越多的大型网站为保障代码知识产权都对自己的脚本进行了一定的混淆或加密,因此,这类按照混淆程度进行恶意性判定的方法会带来较多的误报.• 基于漏洞模拟的检测
该类方法的典型代表是PHoneyC.PHoneyC 在低交互式客户端蜜罐环境中解析页面并用一个独立的脚本引擎执行提取出的脚本,通过在脚本引擎上下文中模拟出一些已知的漏洞插件,并结合运行时参数检查来检测恶意脚本.作为低交互式客户端蜜罐,PhoneyC 比高交互式客户端蜜罐更迅速、更容易加载(模拟)更多的漏洞模块甚至同一漏洞模块的不同版本.但是PhoneyC 也具有其自身局限性:由于采用一个独立的脚本引擎,脚本执行过程中常常由于缺少页面上下文环境或浏览器提供给脚本的一些API 而导致脚本执行失败、检测被迫停止.此外,PhoneyC 只能模拟已知的漏洞插件,无法检测利用零日漏洞的恶意脚本.在网页挂马检测中,低交互式客户端蜜罐中的基于反病毒引擎扫描、基于统计或机器学习、基于漏洞模拟等方法和高交互式客户端蜜罐中的基于行为特征的检测方法各有优缺点:低交互式客户端蜜罐的实现和配置灵活、便于扩展;高交互式客户端蜜罐主要根据行为特征进行检测,误报率相对较低,但时间代价和系统代价比较大.3 网页木马防御技术研究
网页木马都是利用漏洞来执行本不应该执行的代码,而这些代码都需要下载一个原生型木马到受害机执行,这个木马程序就是服务端。如果能拦截掉恶意代码的执行、木马的下载及木马的运行中的任意一步,就可有效地防御网页木马。拦截恶意代码的执行可以通过为系统及应用软件打补丁,封堵系统漏洞或通过杀毒软件来实现,但它们都存在一定的不可靠性。
下面是综合各种网页木马的攻击手段与攻击方式而提出的一些应对方法,能够从各个角度堵住网页木马的下载与运行。
3.1系统安全配置
及时给系统及应用软件打上补丁,让网页木马无漏洞可利用,可将中网页木马的几率降到最低。经常关注最新漏洞的一些信息,有助于及时防止漏洞被网页木马制作者利用;打开操作系统的自动更新功能,如果微软公司提供新的漏洞补丁可以及时自动下载并安装。
3.2浏览器安全配置
IE是占据市场份额最多的浏览器,它支持多种类型的网页文件,例如HTML,DHTML, ActiveX, Java, JavaScript, VBScript, CSS等格式的文件。而正是由于对这些格式文件的支持,使得IE经常由于各种漏洞的产生饱受攻击,如果我们确定不需要运行一些格式文件,我们可以在IE浏览器中“Internet选项一>安全一>自定义级别”页面里面设置相应的配置,对一些不必要的加载和设置进行取消,即提高浏览器安全级别。
上网浏览时我们经常会遇到提示是否安装第三方软件,这些第三方软件可以完成某些特殊的功能,如Google工具条、3721网络助手等,这些软件也可能隐含漏洞,可以在IE的工具一>Internet选项一>高级中取消/启用第三方浏览器扩展。
3.3使用第三方浏览器
由于目前互联网上常见的网页木马所使用的是针对IC浏览器及其ActiveX控件的漏洞,因此,使用Firefox/Opera等非工E内核的第三方浏览器可以从源头上堵住网页木马的攻击;不过第三方浏览器在页面兼容性上稍逊IE浏览器,而且一些特别的网页,如各种使用ActiveX密码登陆控件的网上银行不能使用第三方浏览器登陆,因此也需要综合考虑取舍。
3.4安装安全工具
安全工具软件包括防火墙、杀毒软件、HIPS(Host Intrusion Prevent System)以及其他一些专门用途的安全软件。这些工具从网络到主机,从RingO到Ring3,从防病毒到防流氓软件等不同的角度来保护计算机的安全。防火墙可以防止他人在未授权的情况下连结到本机上,杀毒软件扫描指定的文件和内存,通过与病毒库的比较查杀病毒,H工PS能监控计算机中程序的运行和对文件的访问以及对注册表的修改,并向你提出是否许可警示,如果你阻止了,它将无法进行运行或更改。
3.5禁用远程注册表服务
远程注册表服务Remote Registry Service可以使得远程用户修改或查看本地计算机的注册表信息。木马在利用各种手段诱使受害机下载木马到本地后还远远不够,还必须想办法让木马运行起来,但一般情况下远程客户是没有权限运行当前机子的文件的,而远程注册表服务则使得远程用户利用在注册表中添加开机启动信息而达到木马程序的自动加载,在受害机下次启动之后,攻击者便可以实施攻击。因此我们可以选择关闭远程注册表服务,这样即便攻击者成功实现了木马的挂载,但由于无法让木马运行起来,无法实施攻击,这样用户的安全就得到了保证。总的来说这个服务就是用来使远程机器可以管理本机的注册表(前提是有本地机器中有该权限的用户的用户名和密码),一旦该服务关闭,远程用户将不能访问本机的注册表,其他一些依赖这个服务的程序(或其他服务)也将受到影响,因此也有一定的负面作用。
3.6过滤指定网页
IE浏览器Internet选项提供了一个可以添加信任与不信任网站的站点,对安全性要求高的网络和设备可以采取白名单管理方式,把认为安全的网站添加到白名单列表中管理,其他的则完全不同意访问,这样用户浏览的网站只能浏览安全的网站,故而不可能中网页木马。如果安全性要求相对较低的情况下可以添加黑名单的管理方式,只把用户认为不安全或不能确信是否安全的网站添加到黑名单中,那浏览器在浏览过程中可以自动屏蔽这些网址,那也就切断了本机与网页木马联系,除去了中网页木马的可能性。
3.7卸载或升级WSH WSH是造成很多网页木马横行其道的原因,而且危害性非常大,如果卸载了WSH则可以控制很多网页木马的操作,但WSH的正面作用也非常大,如负责对实现网页动态交互功能的JavaScript等脚本语言的支持,如果完全卸载会使得一些原本需要的功能无法实现,因此升级到最新版本的WSH是最好的选择。最新版WSH在安全性上又有了新的改进。
3.8提高防马意识
用户对于来历不明的链接不要轻易点击,对于来历不明的程序不要轻易安装运行,对于来历不明的多媒体文件也不要轻易下载打开。经常持有对来历不明的文件的警惕性是非常必要的,可以避免很多有害代码的侵入。网页木马的发展趋势
• 网页木马的利用向“大众化”发展
网页木马的利用,近年来有着从“专业化”向“大众化”的发展趋势:早期,攻击页面的编写及网页挂马需要具备一定攻防技术基础的黑客才能完成;而现在,普通网民也可以随意构建并发布网页木马.这种变化趋势在于两方面原因:一是大量的网页木马自动构建工具的存在,如在著名黑客会议Black Hat 和DEF CON 上发布的drivesploit等,普通网民仅简单操作这些工具便可定制出针对特定漏洞的网页木马.另一方面,普通网民不需要掌握任何复杂的网页挂马手段,仅仅通过社交网站就可以大范围地推送恶意链接;加之Twitter、新浪微博等会对用户上传的URL 做短链接处理,这种恶意链接有很强的隐蔽性.随着网页木马的利用向“大众化”的发展,网页木马在互联网上的分布更加广泛.一个可能的研究方向是根据网页木马自动生成工具的指纹特征进行网页木马检测与防范.• 攻击向量载体向PDF,Flash 文档格式扩展
网页木马以 HTML 页面作为攻击向量载体,在浏览器加载、渲染HTML 页面时,利用浏览器及其插件的漏洞实现恶意程序的下载、执行.近年来,攻击者开始在PDF 和Flash 等文档中嵌入恶意脚本,利用PDF,Flash 阅读器的漏洞来下载、执行恶意程序.攻击向量的载体已经从HTML 页面扩展到PDF,Flash 等文档.通过PDF 文档进行客户端攻击,呈一种上升趋势[60].以PDF,Flash 为攻击向量载体进行的客户端攻击已经开始得到学术界的关注.虽然PDF 和Flash 在文档格式上与HTML 页面有所区别,但攻击手段本质上都是在文档中嵌入恶意脚本等攻击向量,利用浏览器/阅读器中的漏洞实现恶意程序的自动下载和执行.对于该类攻击的防御,可以借鉴网页木马防御中的一些思路,如Tzermias 等人借鉴PhoneyC 的思路检测恶意PDF 文档。
• 目标攻击平台向手机平台扩展
近年来,智能手机的市场份额在逐步扩大,技术也在不断发展.智能手机浏览环境的逐步发展给用户带来了越来越好的使用体验,但同时也将攻击者的攻击目标吸引到了手机平台.据统计,iPhone,Android 等主流手机平台均存在大量的安全漏洞,而浏览环境的安全漏洞又占据了其中的大部分.2007 年iPhone 首次发布后不久,便被攻击者通过Safari 浏览器上的漏洞攻破,而在2010 年3 月Pwn2Own 全球攻击者大赛上,两名欧洲黑客仅用20s的时间便通过Safari 浏览器成功攻破iPhone.只要用户用智能手机中特定版本的浏览器访问攻击者精心构造的页面,就可能触发恶意代码在智能手机平台上自动执行.目前,针对手机平台的网页木马虽然没有大规模爆发,但由于手机平台浏览环境中存在大量漏洞,针对手机平台的网页木马仍然是一种潜在的安全威胁,值得研究人员关注.5 总结
网页木马作为恶意程序传播的一种重要方式,能够在客户端访问页面的过程中高效、隐蔽地将恶意程序植入客户端,基于Web 的被动式攻击模式使网页木马能十分隐蔽并有效地感染大量客户端,通过内嵌链接构成的树状多页面结构以及灵活多变的隐蔽手段,使网页木马在结构和组成等方面与一些传统的恶意代码形态有所区别.安全研究人员基于对网页木马机理、特点等的把握,在挂马检测、特征分析、防范等方面提出了应对方法.围绕网页木马的攻防博弈仍在继续,网页木马在载体和攻击平台上的扩展也给研究人员带来了新的挑战与机遇.对于安全研究人员来说,与网页木马的对抗是一项任重而道远的工作.参考文献
[1] 张慧琳,诸葛建伟,宋程昱,邹维.基于网页动态视图的网页木马检测方法.清华大学学报(自然科学版),2009,49(S2):2126−2132.[2] 张慧琳,邹维,韩心慧.网页木马机理与防御技术.软件学报,2013,24(4):843−858.http://
第三篇:《计算机实践》 实验报告
《计算机实践》 实验报告 I
—数据结构
班号: 0316102 学号: 031650106
姓名: 郭砚璞 Email: 2755858446@qq.com
签名:
南京航空航天大学
2018年10月21日
目录
目录…………………………………………………………2
实验一:约瑟夫斯问题求解………………………………3
实验二:停车场管理问题…………………………………12
实验三:管道铺设施工的最佳方案问题…………………25
实验四:内部排序算法的实现与比较……………………35
参考资料……………………………………………………44
源程序清单…………………………………………………44
实验一、约瑟夫斯问题求解
一、问题描述
1)问题描述
约瑟夫斯(Josephus)问题的一种描述是:编号为 1,2,…,n 的 n 个人按顺时针方向
围坐一圈,每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值 m,从
第一个人开始按顺时针方向自 1 开始报数,报到 m 时停止报数。报 m 的人出列,将他的密码作为新的 m 值,从他在顺时针方向下一个人开始重新从 1 报数,如此下去,直至所有的人全部出列为止。试设计一个程序,按出列顺序印出各人编号。
2)实验目的与基本要求
利用单向循环链表存储结构模拟此过程,按照出列的顺序印出各人的编号。
3)测试数据
n=7,7 个人的密码依次为:3,1,7,2,4,8,4。m 初值为 6(正确的出列顺序应为
6,1,4,7,2,3,5)。
4)提示
程序运行后,首先要求用户指定初始报数上限 m,然后读取个人的密码。可设 n≤30。注意链表中空表和非空表的界限。
5)输入输出
输入数据:建立输入处理,输入 n 输入以及每个人的密码;m 的初值。
输出形式:建立一个输出函数,输出正确的序列。
6)选作内容
添加采用顺序存储结构实现问题求解的模块。
以上是本实验的题目要求,下面我将介绍我的实现算法,程序调试结果以及编程过程中遇到的具体问题,并且谈谈我的收获和感悟!
二、需求分析
1、本实验用于求出一组排列数的约瑟夫斯出列顺序。
2、程序运行后显示提示信息,提示用户输入人数n和初始报数上限m,程序需判断m与n的大小,如果m>n,需提示用户重新输入n值和m值。
3、m和n输入有效后,程序提示用户继续输入n个数据,作为n个人的密码。
4、用户输入完毕后,程序需自动显示输入的数据,并且按照出列顺序将n个出列者的编号结果显示出来。
三、概要设计
为了实现上述功能,应以循环链表的数据结构存储n个人的编号、密码信息和顺序关系,因此需要一个循环链表的数据类型。
1、循环链表抽象数据类型定义:
ADT CircularLinkList{
数据对象:一个循环链表,每个结点数据域是一个人的编号和密码。
数据关系:一个结点的指针域指向按照编号下一个结点。
基本操作:
CREATE_SL(SLNODE*);//构建顺序链表
ShowOutput(SLNODE *h, int m);//递归程序,按顺序依次输出出列者的编号
}ADT CircularLinkList
2、本程序保护模块
主函数模块
建立链表模块:将输入数据赋给结点数据域,并构建循环链表的数据关系
核心算法模块:按照算法移动指针、删除节点,依次输出出列者的编号。
调用关系:
3、算法流程图
四、详细设计
1.元素类型、结点类型和结点指针类型:
#define ElemType int
#define SLNODE struct sl_node
SLNODE
{
ElemType data[2];
SLNODE *next;
};
2、建立链表的伪码
SLNODE *CREATE_SL(SLNODE *h,int n)//创建一个h为头指针的链表,h指向的结点数据域用不到
{
ElemType data;
int i = 1;
SLNODE *p, *s;
p = h;
while(i<=n)
{
printf(“请输入第%d个元素:t”,i);
scanf_s(“%d”, &data);
s =(SLNODE *)malloc(sizeof(SLNODE));
s->data[0]=i;
s->data[1] = data;
if(h->next == NULL)
{
h->next = s;
}
else
{
p->next = s;
}
p = s;
i++;
}
p->next = h;
return h;
}
3、主函数伪码
int main()
{
int m,n,mistake=1;
SLNODE *Head;
PrintInformation1();//输出程序信息和个人信息
while(mistake)
{
printf(“输入人数n:n”);
scanf_s(“%d”, &n);
printf(“请指定初始报数上限m(m应必须小于等于n):n”);
scanf_s(“%d”, &m);
if(m>n)
{
printf(“输入数据有误,请重新输入n”);
}
else mistake=0;
}
Head =(SLNODE *)malloc(sizeof(SLNODE));
Head->next = NULL;
Head = CREATE_SL(Head,n);
ShowInput(Head,n);
printf(“正确的出列顺序为:rn”);
ShowOutput(Head,m);
PrintInformation2();//程序结束信息
system(“pause”);
return 0;
}
五、调试分析与核心算法解释
程序的调试主要是针对循环链表,所以调试对象明确,按照算法思路来调试,难度不大。
本题在建立好链表以后,开始执行核心程序ShowOutput递归函数,也就是想办法按照约瑟夫斯问题的规则来删除指针指向的结点、移动指针等,从而以一定排列输出n个人的密码。程序刚开始,先由用户依次输入人数n和初始报数上限m,并判断是否m<=n,如果m>n,则显示重新输入(程序并不会结束)。
然后程序建立头指针Head并且执行CREATE_SL函数,创建链表。
执行ShowInput函数,将n个人的密码依次输出。
下面开始执行本程序的关键函数,也是整个算法的核心函数ShowOutput,这也是整个算法的体现:
函数整体是个递归函数,运用递归的思想,效率很高,占用内存小,不需要设置标志位来判断是否某个结点被访问,而是访问到一个应该出列的结点“出列”,然后以此结点为下一次递归的头结点,然后删除本次递归程序充当头结点的那个结点。
图解:
h,p,s均为指向节点的指针,第一次执行此函数时,h指向头结点。
p指针寻找到要“出列”的节点,h指向要出列的结点作为下一次递归的新的头结点。
利用p指针和s指针重新改造循环链表,然后s指针用于记录本次递归的头结点位置,即将用于删除、释放空间。
下面执行free(s),删除所指内存空间,开始下一次递归调用。
后面执行递归函数时,h刚开始指向的是上一次输出的结点(还没删除),程序一开始先让s指向h,等这一趟程序快结束找到下一个要出列的结点时,h指向该结点作为下一次递归函数的头结点,并且让p找到s指向的充当本次头结点的结点,把它删除,再执行此递归程序。
终止条件是p->next=h,说明没有需要输出的节点了,于是便实现了递归。如图所示:
截图如图:
六、使用说明
按照程序界面指示输入即可,逐个整数数字输入,每输入一个数字按一个回车。
七、调试结果
按照测试要求进行了测试,结果如下:
八、遇到的问题及解决方法(程序调试日志)
2018/9/不详
问题:先建立了一个链表实现插入算法的小功能,再进行约瑟夫问题求解,结果程序不停的输出不完整的数据,陷入死循环。
解决:p指针所指空间逻辑上不该释放但被不正确释放,删去free(p)代码,一切正常。
2018/9/不详
问题:递归程序死循环。
解决:单步执行发现递归的终止条件不正确,修改为p->next=h,程序功能实现!
2018/10/20
最后一天,完善程序,将程序中的注释写清楚,百度找到了显示系统时间的方法,按格式输出,截屏、书写报告。
九、实验的收获与感想
个人感想:循环链表虽然不是很容易理解,但是处理约瑟夫斯这样的删除元素的操作真的十分便利,比起数组要方便的多。但是代价是编程要仔细,不要释放错内存空间。
个人方法优点:
1、建立链表时,将头结点和头指针同时运用,使头指针一开始指向头结点,这样操作方便,同时个人的算法要利用s删除上一次h指向的头结点,所以一开始让头指针指向一个头结点对于个人的算法是有一定好处的。
2、本人采用递归的算法,每次找到要出列的点后,先不马上删除结点,而是将h指针指向此结点作为下一次递归的h结点,等下一次递归找到新的出列结点并用h指向来作为头结点后,再将废弃结点删除,这样“改变头结点”,操作简便,很适合递归的条件。
个人方法缺点:本人认为此程序缺点是陌生人刚看到此程序不能马上理解,因为递归程序本身不易理解。所以为了更好给别人使用,本人已将程序注释的很清楚了。
建议:本实验很好,建议像第四题那样,增加一项计算程序空间复杂度和时间复杂度(移动次数)的要求,组织同学进行讨论,展示最优的算法。
十、源程序
见源程序清单。
实验二、停车场管理问题
一、问题描述
1)问题描述
设停车场是一个可停放 n 辆汽车的狭长通道,且只有一个大门可供汽车进出。汽车在停车场内按车辆到达时间的先后顺序,依次由北向南排列(大门在最南端,最先到达的第一辆车停放在车场的最北端)。若停车场内已经停满 n 辆车,那么后来的车只能在门外的便道上等候。一旦有车开走,则排在便道上的第一辆车即可开入。当停车场内某辆车要离
开时,在它之后进入的车辆必须先退出车场为它让路,待该辆车开出大门外,其他车辆再
按原次序进入车场。每辆停放在车场的车在它离开停车场时必须按它停留的时间长短缴纳
费用。试为停车场编制按上述要求进行管理的模拟程序。
2)基本要求
以栈模拟停车场,以队列模拟车场外的便道,按照从终端读入数据的序列进行模拟管
理。每一组输入数据包括三个数据项:汽车的“到达”(‘A’表示)或“离去”(‘D’表示)信息、汽车标识(牌照号)以及到达或离去的时刻。对每一组输入数据进行操作后的输出信息为:若是车辆到达,则输出汽车在停车场内或者便道上的停车位置;若是车辆离去,则输出汽车在停车场停留的时间和应缴纳的费用(便道上停留的时间不收费)。栈以顺序
结构实现,队列以链表结构实现。
3)测试数据
设 n=2,输入数据为:(‘A’,1,5),(‘A’,2,10),(‘D’,1,15),(‘A’,3,20),(‘A’,4,25),(‘A’,5,30),(‘D’,2,35),(‘D’,4,40),(‘E’,0,0)。每一组输入数据包括三个数据项:汽车 “到达”或“离去”信息、汽车牌照号码及到达或离去的时刻,其中,‘A’表示到达;‘D’表示离去,‘E’表示输入结束。其中:(‘A’,1,5)表示 1 号牌照车在 5 这个时刻到达,而(‘D’,1,15)表示 1 号牌照车在 15 这个时刻离去。
4)提示
需另设一个栈,临时停放为给要离去的汽车让路而从停车场退出来的汽车。输入数据
按到达或离去的时刻有序。栈中每个元素表示一辆汽车,包含两个数据项:汽车的牌照号 码和进入停车场的时刻。
5)输入输出:
输入数据:
程序接受 5 个命令,分别是:到达(‘A’,车牌号,时间);离去(‘D’,车牌号,时 间);停车场(‘P’, 0, 0)显示停车场的车数;候车场(‘W’, 0, 0)显示候车场的车数;退出(‘E’, 0, 0)退出程序。
输出数据:
对于车辆到达,要输出汽车在停车场内或者便道上的停车位置;对于车辆 离去,则输出汽车在停车场停留的时间和应缴纳的费用(便道上不收费)。
二、需求分析
1、本程序用来模拟停车场进场、进便道、场外等候、出场、收费等操作。
2、程序运行后显示提示信息,提示用户输入停车每小时需缴纳的费用,用户输入后,提示信息提示用户输入命令:驶入停车场A,离开停车场D,查看停车场内车数P 0 0,查看候车厂内车数W 0 0,程序结束E 0 0。
3、程序需判断用户输入的进场出场时间的有效性,后来输入的时间要大于以前输入的时间。
4、用户输入有效命令后,程序显示汽车进出场信息,若是车辆到达,则输出汽车在停车场内或者便道上的停车位置;若是车辆离去,则输出汽车在停车场停留的时间和应缴纳的费用。
三、概要设计
为了实现上述功能,需要用栈模拟停车场,用一个备用的栈存储暂时出场的外部汽车,用队列模拟便道。
1、停车场“栈”抽象数据类型定义:
ADT stack{
数据对象:车牌号、进场时间、汽车数量
数据关系:先入后出
基本操作:
*Init_Parking();//置空栈
IsEmpty_Parking(Parking *s);//判空栈
Push_Parking(Parking *s,ElemType license,ElemType timeIn);//入栈
Pop_Parking(Parking *s,ElemType *license,ElemType *timeIn);//出栈
}ADT stack
2、备用存储“栈”抽象数据类型定义:
ADT stack1
{
数据对象:车牌号、进场时间、汽车数量
数据关系:先入后出
基本操作:
*Init_Backup();//置空栈
IsEmpty_Backup(Backup *s);//判空栈
Push_Backup(Backup *s, ElemType license, ElemType timeIn);//入栈
Pop_Backup(Backup *s, ElemType *license,ElemType* timeIn);//出栈
}
3、链式队列抽象数据类型定义:
ADT linkqueue
{
数据对象:车牌号、汽车数量
数据关系:先入先出
基本操作:
*Init_LQueue();//创建一个带头结点的空队
In_LQueue(LinkQueue *q, ElemType license);//入队
IsEmpty_LQueue(LinkQueue *q);//判队空
Out_LQueue(LinkQueue *q, ElemType *license);//出队
}
4、本程序保护模块
主函数模块
进场模块:对于进场的命令进行完善地操作处理并进行状态显示的模块
出场模块:对于出场的命令进行完善地操作处理并进行状态显示的模块
置空栈、置空队、进出栈、进出队、判栈空栈满、判队空队满模块:对栈、备用栈、队列进行的基本操作
调用关系:
5、算法流程图
四、详细设计
1、元素类型、结点类型和结点指针类型:
#define Maxsize 2 //停车场最多能停的车数
#define ElemType int
int Prize;//每停一个时刻收费多少元
static int num = 0;//num用于记录入队的车所在的位置
typedef struct stack //模拟停车场的栈
{
ElemType license[Maxsize];//用于存放车牌号
ElemType timeIn[Maxsize];//用于存放入停车场时间
int top;
}Parking;
typedef struct stack1 //退车时暂时存放
{
ElemType license[Maxsize-1];
ElemType timeIn[Maxsize-1];
int top;
}Backup;
typedef struct road
{
ElemType license;
struct road *next;
}Road;
typedef struct linkqueue//队列模拟便道
{
Road *front,*rear;
}LinkQueue;
2、部分基本操作的伪码类型
//给停车场用的配置函数
Parking *Init_Parking()//置空栈
{
Parking *s;
s=(Parking*)malloc(sizeof(Parking));
s->top=-1;
return s;
}
int IsEmpty_Parking(Parking *s)//判空栈
{
if(s->top==-1)
return 1;
else return 0;
}
int Push_Parking(Parking *s,ElemType license,ElemType timeIn)//入栈
{
if(s->top==Maxsize-1)
return 0;
else
{
s->top++;
s->license[s->top]=license;
s->timeIn[s->top]=timeIn;
return 1;
}
}
int Pop_Parking(Parking *s,ElemType *license,ElemType *timeIn)//出栈
{
if(IsEmpty_Parking(s))
return 0;
else
{
*license = s->license[s->top];
*timeIn = s->timeIn[s->top];
s->top--;
return 1;
}
}
//给备用栈配置的函数
Backup *Init_Backup()//置空栈
{
Backup *s;
s =(Backup*)malloc(sizeof(Backup));
s->top =-1;
return s;
}
int IsEmpty_Backup(Backup *s)//判空栈
{
if(s->top ==-1)
return 1;
else return 0;
}
int Push_Backup(Backup *s, ElemType license, ElemType timeIn)//入栈
{
if(s->top == Maxsize-1)
return 0;
else
{
s->top++;
s->license[s->top] = license;
s->timeIn[s->top] = timeIn;
return 1;
}
}
int Pop_Backup(Backup *s, ElemType *license,ElemType* timeIn)//出栈
{
if(IsEmpty_Backup(s))
return 0;
else
{
*license = s->license[s->top];
*timeIn = s->timeIn[s->top];
s->top--;
return 1;
}
}
//给候车便道链式队列配置的函数
LinkQueue *Init_LQueue()//创建一个带头结点的空队
{
LinkQueue *q;
Road *p;
q =(LinkQueue*)malloc(sizeof(LinkQueue));
p =(Road*)malloc(sizeof(Road));
p->next = NULL;
q->front = q->rear = p;
return q;
}
void In_LQueue(LinkQueue *q, ElemType license)//入队
{
Road *p;
p =(Road*)malloc(sizeof(Road));
p->license = license;
p->next = NULL;
q->rear->next = p;
q->rear = p;
}
int IsEmpty_LQueue(LinkQueue *q)//判队空
{
if(q->front == q->rear)
return 1;
else
return 0;
}
int Out_LQueue(LinkQueue *q, ElemType *license)//出队
{
Road *p;
if(IsEmpty_LQueue(q))
{
printf(”队空“);
return 0;
}
else
{
p = q->front->next;
q->front->next = p->next;
*license = p->license;
free(p);
if(q->front->next == NULL)
q->rear = q->front;
return 1;
}
}
3、主函数的伪码
void main()
{
ElemType license, time,timelast=0;//timeInlast是最近一次有车进停车场的时间,提示以后输入的进场时间要大于此数值
char command;//进入A还是离开D
Parking *s;
Backup *s1;
LinkQueue *q;
PrintInformation1();//输出程序信息和个人信息
s = Init_Parking();//停车场
q = Init_LQueue();//便道队列
s1 = Init_Backup();//退车时的备用栈
printf(”请输入停车每小时需交纳的费用(元)rn“);
setbuf(stdin, NULL);//使stdin输入流由默认缓冲区转为无缓冲区,必不可少
scanf(”%d“,&Prize);
setbuf(stdin, NULL);//使stdin输入流由默认缓冲区转为无缓冲区,必不可少
while(1)
{
setbuf(stdin, NULL);//使stdin输入流由默认缓冲区转为无缓冲区,必不可少
Loop:printf(”请输入操作的命令,驶入停车场A,离开停车场D,查看停车场内车数P 0 0,查看候车厂内车数W 0 0,程序结束E 0 0:n“);
scanf(”%c%d%d“,&command, &license,&time);
setbuf(stdin, NULL);//使stdin输入流由默认缓冲区转为无缓冲区,必不可少
if(command == A)
{
if(time <= timelast)
{
printf(”输入的时间必须大于上一次输入的时间:t“);
printf(”%dt“, timelast);
printf(”请重新输入n“);
goto Loop;
}
else
{
timelast = time;
GetIn(s,q,license,time);
}
}
if(command == D)
{
if(time <= timelast)
{
printf(”输入的时间必须大于上一次输入的时间:t“);
printf(”%dt“, timelast);
printf(”请重新输入n“);
goto Loop;
}
else
{
timelast = time;
GetOut(s, s1, q, license, time);//车开走的函数
}
}
if(command == P)
{
if(license == 0 && time == 0)
printf(”停车场内停了%d辆车n“,s->top+1);//显示停车场车数
}
if(command == W)
{
if(license == 0 && time == 0)
printf(”侯车场内停了%d辆车n“,num);//显示候车场车数
}
if(command == E)
{
if(license == 0 && time == 0)
{
PrintInformation2();//程序结束信息
system(”pause“);
return;
}
}
}
}
五、调试分析与核心算法解释
程序本身是利用栈、队列的进出完成停车场汽车进出场的模拟的,只要按照模块化的函数,按照基本的逻辑编写,调试是比较容易的。
程序一开始会要求用户输入每小时缴纳多少元费用,显示“请输入停车每小时需交纳的费用(元)”。
以栈模拟停车场,以队列模拟车场外的便道,在while循环开始让用户输入命令、车牌号、时间三个变量,对三个变量进行判断,并执行相应函数。
程序显示“请输入操作的命令,驶入停车场A,离开停车场D,查看停车场内车数P 0 0,查看候车厂内车数W 0 0,程序结束E 0 0:”
如果后来输入的时间比之前输入的时间小,程序会提示“输入的时间必须大于上一次输入的时间: xx 请重新输入”。
其中驶入函数先判断是否栈满,如果栈满则执行入队操作,否则入栈,并将车牌号、驶入时间存入栈元素数组,top值加一,用于记录车的数量,用于判断是否栈满。
如果栈不满,程序显示“车牌号为xx的汽车在xx时刻停在xx车位”。
如果栈满,程序显示“xx号车停在候车便道的第xx个位置”。
离开函数先判断是否栈空,如果栈空则输出没有车辆提示,否则进一步比较是否有栈内车牌号元素与命令的离开的车牌号一致,有则出栈,计算停车时间和计价,无则输出没有此车牌号的车的提示。
如果没有命令的车牌号的汽车,则显示“对不起!停车场内没有车牌号为xx的车”。
如果停车场已空,会显示“对不起!停车场已空!”。
如果原先栈满,离开一辆车以后,最早到便道上的一辆车进栈,并显示“xx号汽车此时退出便道,进入停车场最后一个位置”。
队列和栈的top记录了车的数量,可用于输出内部车数,也可用于判断是否栈满。
如果三个变量分别为P,0,0,则输出停车场内车的个数。
如果三个变量分别为E,0,0,则输出便道上车的个数。
如果三个变量分别为E ,0, 0,则结束程序。
六、使用说明
程序接受 5 个命令,分别是:到达(‘A’,车牌号,时间);离去(‘D’,车牌号,时 间);停车场(‘P’, 0, 0)显示停车场的车数;候车场(‘W’, 0, 0)显示候车场的车数;退出(‘E’, 0, 0)退出程序。
注意:命令字符(A D P W E)用大写,输入的三个数据之间用空格隔开。如A 1 5。代表1号1号汽车在5时刻进场。
七、调试结果
按照测试要求给的数据进行了测试:
八、遇到的问题和解决方法(程序调试日志)
2018/9/不详
问题:程序全部结束,执行时能基本完成功能,但是总是会间隔有一次命令无效,重复输出“请输入命令.......”。
解决方法:后来百度发现,这是因为数据缓存区没有清除的缘故,每次回车都会在数据缓存区遗留下来一个Enter字符,可被char型变量读取,因此在每次输入数据前后加入一个清除数据缓存区的函数setbuf(stdin, NULL)即可解决问题。
2018/10/20
最后一天,完善程序,将程序中的注释写清楚,百度找到了显示系统时间的方法,按格式输出,截屏、书写报告。
九、实验的收获与感想
个人感想:
这道题目和后面的第四道题目都是属于比较透彻的题目,先做了第二题,还是对第二题印象最深,首先这道题很讲究编程的条理性,里面的初始化栈、判栈空、栈满、入栈、出栈以及初始化队列、判队空、队满、入队、出队等函数在书上都有,主要的工作是利用模块化的思想,由整体到局部逐渐求精地去写整个程序,从而把整个停车场的5个命令功能给实现,感觉收获很多,模块化的思想很厉害!
方法的优点:
整体程序思路比较简单,因此个人没有什么更优算法,只是在这里面有一个逻辑,就是后面输入的时间不能比之前输入的时间小,因为这不符合日常逻辑,同时也影响了入栈出栈次序,因此我程序里使用了不常用的goto函数,一般这个函数是不建议用的,它会跳转程序,但是在这里判断后面输入的时间大于之前输入的时间后,goto函数可以让程序跳转到while循环开始的地方,让用户重新输入命令,这里感觉很方便!
建议:
希望多多布置这样的习题,有助于教会学生利用模块化思想,不过希望布置的题目可以是多方向的比如有关界面制作的题目,把模块化的函数给大家,大家进行使用,根据自己爱好设计出相应的模块化功能的程序。
十、源程序
见源程序清单。
实验三、管道铺设施工的最佳方案问题
一、问题描述
1)问题描述
需要在某个城市 n 个居民小区之间铺设煤气管道,则在这 n 个居民小区之间只需要铺设 n-1 条管道即可。假设任意两个小区之间都可以铺设管道,但由于地理环境不同,所需要的费用也不尽相同。选择最优的方案能使总投资尽可能小,这个问题即为求无向网的最小生成树。
2)基本要求
在可能假设的 m 条管道中,选取 n-1 条管道,使得既能连通 n 个小区,又能使总投资最小。每条管道的费用以网中该边的权值形式给出,网的存储采用邻接表的结构。
3)测试数据
使用下图给出的无线网数据作为程序的输入,求出最佳铺设方案。右侧是给出的参考解。
以上是本实验的题目要求,下面我将介绍我的实现算法,程序调试结果以及编程过程中遇到的具体问题,并且谈谈我的收获和感悟!
二、需求分析
1、此程序用来求无向带权网的最小生成树。
2、程序的输入数据放在一个dat格式的文件中,想要修改输入数据,用记事本打开文件直接修改即可。任何时刻想看到结果,点击一下exe文件即可一键解决。
3、输入数据的形式是一个矩阵形式,行和列的元素依次代表A、B、C....,两点之间有边,则数据是一个不为0的数,否则为0。
4、程序运行结果不仅会在控制台显示出来,同时还要在一个新的文件中显示出来,点击一下exe文件即可一键解决,满足用户“即点即看”,输出文件令用户能“随时且长久”看到结果,不必每一次都要运行程序。
三、概要设计
为实现上述功能,应以图的邻接表数据结构,即链表与数组相结合的数据结构。
1、邻接表抽象数据类型定义:
ADT ALGraph{
数据对象:结点A、B、C....数据关系:存储结构上链表式指针相连,物理结构上点之间相连成边
基本操作:CreateALGraph(ALGraph *G);//建立无向图的邻接表存储
}ADT ALGraph
2、文件操作模块
基本操作:
read_data(void);//只读方式打开文件
read_array(double *array,int n1,int n2,FILE *fp);//读取文件中的边值信息
cout_data(void);//只写方式打开或建立文件
cout_array(double *array,int n1,int n2,FILE *fp);//向文件中写入边值信息
3、本程序保护模块
主程序模块
建立无向图模块:根据读入文件的边值信息创建无向图邻接表存储
最小生成树模块:由无向图生成最小生成树
读入数据模块:只读方式读取文件数据
输出结果模块:将结果输出到控制台
输出到文件模块:将结果输出到文件
调用关系:
四、详细设计
需要建立一个无向图的邻接表存储结构,进行最小生成树的提取。
1、元素类型、结点类型和结点指针类型:
#define MaxVertexNum 100 //设置小区数最多为100
#define VertexNum 9
double LeastNum;//用于记录最短边长度
double LongestNum;//用于记录最长边长度,程序过程中不改变
double adjacent[VertexNum][VertexNum];
//邻接矩阵,不用于处理数据,而是用于暂时存储从文件读来的数据
//进而在邻接表存储数据时读取此数组数据即可
//二维数组数据为0的元素说明之间没有通道
//在处理完数据后,此数组会用来暂时存储处理后的数据,并写入到另一个文件中
typedef struct vnode
{//顶点表结点
char vertex;
EdgeNode *firstedge;
}VertexNode;
typedef struct
{
VertexNode adjlist[MaxVertexNum];
int n,e;
}ALGraph;
2、有序表类型:
typedef struct node
{//边表结点
char adjvex;
double weight;//权值
struct node *next;
}EdgeNode;
3、主函数的伪码:
void main()
{
ALGraph *G;
PrintInformation1();
G =(ALGraph*)malloc(sizeof(ALGraph));
G->n = VertexNum;//为G指向的图分配空间,设置点数(小区数)
read_data();
CreateALGraph(G);
MinimumSpanningTree(G);
cout_data();//输出到文件
ResultOutput((double *)adjacent,VertexNum,VertexNum);//输出到控制台
PrintInformation2();
system(”pause“);
}
4、算法流程图
五、调试分析与核心算法解释
此题按照Prim算法,设置一个用于存储点的点集,找短边从一个点向两点、三个....更多逐渐扩张,即可得到最小生成树。
1).输入:一个加权连通图,其中顶点集合为V,边集合为E;
2).初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;
3).重复下列操作,直到Vnew = V:
a.在集合E中选取权值最小的边,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
b.将v加入集合Vnew中,将边加入集合Enew中;
4).输出:使用集合Vnew和Enew来描述所得到的最小生成树。
具体可以参考资料:https://baike.baidu.com/redirect/cb1fkztQc2TOscNlcMemKsGZ1fsgTprJsdBq_APeZx74W4q4TzbKXHsvSYW_6aM1DqhF56eTgD8EZLzBCQHKBGa6ExWmgXbju_gm13Qbbu0KxbHuiIS_DxEp2fgT3BU
六、使用说明
输入数据已经在”gyp_program3_Input.dat“中写好,如果想做修改可以在该文件中修改即可,输出的数据既在控制台显示,也会输出到”gyp_program3_Output.dat“。
七、调试结果
输入数据文件:
这是一个矩阵,例如:第2行,第3列代表B,C两点间的边,0代表无边,不为0代表有边。
控制台程序显示:
输出数据到输出文件
八、遇到的问题和解决方法(程序调试日志)
2018/9/不详
问题:一进入建立无向图的函数程序就中止出错。
解决方法:给建立无向图函数传递的形参是指针,而在主函数中G指针没有赋给内存空间,所以函数无法对G所指的空间进行初始化,在主程序中加入这样一句代码就好了:
G =(ALGraph*)malloc(sizeof(ALGraph));
2018/10/17
问题:输出的二维数组只有一位正常,其余全是0。如下图所示:
解决方法:后来发现是里面flag2这个标志位刚开始是1,程序执行过程中被置0用于判断,但是判断以后没有再次重置为1,导致要用很多次的标志位只发挥了一次作用,后面就误判了,在循环结束加入重置1的语句即可。
问题:输出的二维数组输出数据不完整,里面总有最小的边(问题切入点)。
解决方法:单步执行后发现,某一次循环中Leastnum这个变量得到的最小值恰好是所有边中最小的一个,它在发挥完比较作用后,没有被置为一个很大的数(不小于这些边中最大值即可),结果在下一次循环的时候,导致后面的边都比它大,以至于后面的边都被舍去了,解决方法就是每次循环第一句先把原图的最大边赋给leastnum。
问题:直接运行debug文件夹里的exe程序,且debug程序终止,怀疑是文件数据读取失败,无法打开文件,即文件不存在。
解决方法:如果不详细说明文件路径,程序本身会在当前文件夹内找文件,存数据的文件gyp_program3_Input.dat要保存在当前文件中。也就是和源代码在同一个文件夹中,但是这样子的话,只有用编程软件打开代码运行,dat文件才有效,若想在debug里直接运行exe程序,则把存数据的输入文件同名复制到debug文件夹里即可。
九、实验的收获和感想
个人感想:这个程序可以说是算法感十足了,也是我遇到问题最大的一个程序,但是也证明了自己的能力,学到了很多,收获最大的就是:对于用的不止一次的标志位,一定不可能只单方向置位,一定是双向置位,一次循环结束一定要记得置位,否则用了这一次以后,后面的循环就会误判!
Prim算法真的很好用,不仅适合求带球图的最小生成树,还适合寻找一个点到另一点的最短路径。
个人方法优点:输入方便,输出简洁。读取文件中的数据,不用手动输入,想修改数据在文件中修改就可以,另外输出有两种形式,一种是控制台输出,一种是输出到文件,输出的是一个二维数组,行和列都有表头,看起来很清晰,两个点之间若有边,相应二维坐标的元素就是边值,无边则相应的位置为0。
建议:可以要求用顺序表的方法,因为从理解和编程难度角度考虑,图的邻接表找边会很慢每次都要顺着其中一个链表头结点去寻找,不过这也很锻炼人的能力!
十、源程序
见源程序清单。
实验四、内部排序算法的实现与比较
一、问题描述
1)问题描述
在教科书中,各种内部排序算法的时间复杂度分析结果只给出了算法执行时间的阶,或大概执行时间。试通过随机数据比较各算法的关键字比较次数和关键字移动次数,以取得直观感受。
2)基本要求
(1)对常用的内部排序算法进行比较:直接插入排序、简单选择排序、冒泡排序、快速排序、希尔排序。
(2)利用随机函数产生N(如30000)个随机整数,作为输入数据作比较;比较的指标为关键字参加的比较次数和关键字的移动次数(关键字交换计为3次移动)。
(3)对结果作出简要分析。
3)测试数据
随机函数产生。
4)提示
主要工作是设法在已知算法中适当位置插入对关键字的比较次数和移动次数的计数操 作。注意采用分块调试的方法。
5)输入输出:
输入数据:参加排序的整数个数 n(如 n=30000);
输出数据:各种排序方法的关键字比较次数和移动次数(从小到大排列)。
二、需求分析
1、本程序用来比较5种排序的关键字比较次数和关键字移动次数。
2、本程序用srand()和rand()函数产生N个随机数用于排序方法的比较,用户无需输入。
3、运行程序后,程序需按从小到大的顺序分别输出5种排序的关键字比较次数和移动次数。
三、概要设计
为实现上述功能,程序需要产生N个随机数,并且需要写出这5种排序的排序函数。
1、产生N个随机数的方法
srand((unsigned)(time(NULL)));
for(int j = 0;j < N;j++)
{
num0[j] = rand();
}
2、保证每次排序的数据都相同的方法
num0数组用于得到随机数,num数组每次排序前先用num0数组赋值即可。
for(int j=0;j { num[j] = num0[j]; } InsertSort(num,N);3、5种排序函数 void InsertSort(int R[], int n);//插入排序 void SelectSort(int R[], int n);//选择排序 void BubbleSort(int R[], int n);//冒泡排序 void QuickSort(int R[],int low,int high);//快速排序 void ShellInsert(int R[],int m,int n);//快速排序 void Shellsort(int R[],int n);//希尔排序 4、本程序保护模块 主程序模块 排序模块 比较输出模块 调用关系: 4、算法流程图 四、详细设计 1、排序函数基本操作的伪码实现 void InsertSort(int R[], int n)//插入排序 { int i, j; for(i = 2;i <= n;i++) { R[0] = R[i]; MoveNum[0]++; j = i-1; while(R[0] < R[j]) { KeyCompareNum[0]++; R[j + 1] = R[j]; MoveNum[0]++; j--; } R[j + 1] = R[0]; MoveNum[0]++; } } void SelectSort(int R[], int n)//选择排序 { int i, j, k; for(i = 1;i < n;i++) { k = i; for(j = i + 1;j <= n;j++) if(R[j] < R[k]) { k = j; KeyCompareNum[1]++; } if(k!= i) { R[0] = R[k]; R[k] = R[i]; R[i] = R[0]; MoveNum[1]+=3; } } } void BubbleSort(int R[], int n)//冒泡排序 { int i, j, flag = 0; for(i = 1;(i < n && flag == 0);i++) { flag = 1; for(j=1;j if(R[j + 1] < R[j]) { KeyCompareNum[2]++; flag = 0; R[0] = R[j]; R[j] = R[j+1]; R[j + 1] = R[0]; MoveNum[2]+=3; } } } void QuickSort(int R[],int low,int high)//快速排序 { int i,j; i=low; j=high; R[0]=R[i]; MoveNum[3]++; while(i { while((R[j]>=R[0])&&(j>i)) { j--; KeyCompareNum[3]++; } if(j>i) { R[i]=R[j]; MoveNum[3]++; i++; } while((R[i]<=r[0])&&(j>i)) { i++; KeyCompareNum[3]++; } if(j>i) { R[j]=R[i]; MoveNum[3]++; j--; } } R[i]=R[0]; MoveNum[3]++; if(low QuickSort(R,low,i-1); if(i QuickSort(R,j+1,high); } void ShellInsert(int R[],int m,int n) {//一趟希尔排序,按间隔m划分子序列 int temp,j; for(int i=m;i { temp=R[i]; MoveNum[4]++; j=i; while(j>=m && temp { KeyCompareNum[4]++; R[j]=R[j-m]; MoveNum[4]++; j-=m; } R[j]=temp; MoveNum[4]++; } } void Shellsort(int R[],int n)//希尔排序 { int m; m=n/2; while(m>=1) { ShellInsert(R,m,n); m=(m==2?1:(m/2)); } } 2、主函数伪码实现 int main() { //数组设为N+1个是因为有些算法是从数组1到N存储的,0位有的不用,有的用作了哨兵 int num0[N+1];//记录最原先随机产生的N个数字,因为num[N]每排序一次都会改变 int num[N+1];//每次排序前先读入num0[N]数据 //srand函数只增加一次就够了,用系统当前时间设置rand()随机序列种子,保证每次运行随机序列不一样 srand((unsigned)(time(NULL))); for(int j = 0;j < N;j++) { num0[j] = rand(); } PrintInformation1(); for(int j=0;j { num[j] = num0[j]; } InsertSort(num,N); for(int j = 0;j < N;j++) { num[j] = num0[j]; } SelectSort(num,N); for(int j = 0;j < N;j++) { num[j] = num0[j]; } BubbleSort(num,N); for(int j = 0;j < N;j++) { num[j] = num0[j]; } Shellsort(num,N); for(int j = 0;j < N;j++) { num[j] = num0[j]; } QuickSort(num,0,N-1); printf(”关键字比较次数按从小到大排列分别是:rn“); KeyCompareNum_pailie(); printf(”rn“); printf(”移动次数按从小到大排列分别是:rn“); MoveNum_pailie(); PrintInformation2(); system(”pause“); return 0; } 五、调试分析与核心算法解释 程序的5种排序函数可以自己写,也可以百度找源码,总之五种排序函数写完以后,调试的关键是设置计数变量,并在这些函数的合适位置进行加1计数或加3计数,因此注意分块调试,分函数调试,做到这几点调试难度总体不大。 六、使用说明 随机数产生,5种排序用的都是同一组N个随机数,用户无需输入,直接运行程序即可输出运算结果。 七、调试结果 八、遇到的问题和解决方法(程序调试日志) 2018/10/19 问题:第一种排序输出很大,后面输出很小,几乎没作用。 解决方法:每经过一个排序函数,数组已经排好序了,不能直接调用其余的排序函数,而是要在程序一开始就用另一个数组num0[N]记录了随机数产生的数组num[N],因此只需要在每次排序前,把num0数组中数据重新赋给num数据即可参与排序了。 问题:数据输出个数少于5个,单步执行发现循环不到5次。 解决方案:分析后发现,程序的几个for循环,内层的循环里面的计数加1变量如i,j,和外层循环设置的加1变量用的是一个变量,导致内层循环计数变量的改变会影响外层循环,导致输出个数小于5。将这些变量设置成不同变量就不影响了(相互独立的循环可能不会影响,但内外层关系的两个循环绝对不能用同一个计数变量)。 问题:程序末尾会跳出一个程序中止警告框,说是栈释放的问题。 解决方案:百度发现,这实际上是数组越界的问题,把数组个数设置为N+1大小的,就好了。 九、实验的收获和感想 个人感想:这个程序难度不大,关键在于在合适的位置插入用于记录的变量+1或者+3,真正有技术含量的在于按照从小到大的顺序输出相应数据。收获比较大的一个技巧:相互独立的循环计数变量可能不会影响,但内外层关系的两个循环绝对不能用同一个计数变量,否则程序循环次数就会乱! 从关键字比较次数和移动次数来看,选择和快速排序都是非常高效的,希尔排序也不错,不要用或少用冒泡排序和直接插入排序。 个人方法的优点:只产生了1次随机数,5次排序的用的都是同一组数据,可以更好地更严谨地比较出这5种算法的优劣。 个人方法的缺点:因为要“长久”保留产生的随机数,因此需多设置一个数组,占用内存空间比较大。 十、源程序 源程序见源程序清单。 参考资料 https://blog.csdn.net/guanyasu/article/details/53153705 https://jingyan.baidu.com/article/49711c616b8a1ffa441b7cdc.html https://zhidao.baidu.com/question/***684.html 源程序清单 实验一、约瑟夫斯问题求解 #include #include #include #define ElemType int #define SLNODE struct sl_node SLNODE { ElemType data[2]; SLNODE *next; }; SLNODE *CREATE_SL(SLNODE*,int); void ShowInput(SLNODE*h,int n);//每个人的密码输入 void ShowOutput(SLNODE*,int);//排列输出 void PrintInformation1();//输出程序信息和个人信息 void PrintInformation2();//程序结束信息 void PrintTime();//输出时间 int main() { int m,n,mistake=1; SLNODE *Head; PrintInformation1();//输出程序信息和个人信息 while(mistake) { printf(”输入人数n:n“); scanf_s(”%d“, &n); printf(”请指定初始报数上限m(m应必须小于等于n):n“); scanf_s(”%d“, &m); if(m>n) { printf(”输入数据有误,请重新输入n“); } else mistake=0; } Head =(SLNODE *)malloc(sizeof(SLNODE)); Head->next = NULL; Head = CREATE_SL(Head,n); ShowInput(Head,n); printf(”正确的出列顺序为:rn“); ShowOutput(Head,m); PrintInformation2();//程序结束信息 system(”pause“); return 0; } void PrintTime() { time_t t; time(&t); printf(”%s“, ctime(&t)); printf(”rn“); } void PrintInformation1() { printf(”实验名称:实验一.约瑟夫斯问题求解rn“); printf(”学号:031650106rn“); printf(”姓名:郭砚璞rn“); printf(”====================rn“); printf(”程序运行开始,Current local time and date:“); PrintTime(); } void PrintInformation2() { printf(”rn====================rn“); printf(”程序运行结束,Current local time and date:“); PrintTime(); } SLNODE *CREATE_SL(SLNODE *h,int n) //创建一个h为头指针的链表,h指向的结点数据域用不到 { ElemType data; int i = 1; SLNODE *p, *s; p = h; while(i<=n) { printf(”请输入第%d个元素:t“,i); scanf_s(”%d“, &data); s =(SLNODE *)malloc(sizeof(SLNODE)); s->data[0]=i; s->data[1] = data; if(h->next == NULL) { h->next = s; } else { p->next = s; } p = s; i++; } p->next = h; return h; } void ShowInput(SLNODE *h,int n) { SLNODE *p; p = h; printf(”%d“,n); printf(”个人的密码依次为:“); while((p->next)!= h) { p = p->next; printf(”%dt“, p->data[1]); } printf(”n“); } void ShowOutput(SLNODE *h, int m) { SLNODE *p, *s;//s用于记录上一个节点,从而使p结点找到它将其删除 int j = 0; s = h;//第一次执行此函数时,h指向头结点;后面递归执行时,h刚开始指向的是上一//次输出的结点(还没删除) //都用s来记录,等那一趟程序快结束找到下一个要出列的的点时,h指向那个结点作为头结点,并且让p找到s指向的 //上一个结点,把它删除。 p = h; while(j < m-1) { p = p->next; if(p->next==h)//不能让h所指向的结点(上一次输出的结点,暂时用作头结点所以//还未删除)影响小循环次数 { p=p->next;//所以此处让p多移动一下,等下一次小循环让p顺利移动到下一//个节点(从而忽略掉h指向的结点) } //等找到下一个该出列的结点时,h指向那个结点(充当下一次的头节点),充当上一次头//结点的结点利用s删除 j++; }//此时p指向第m-1个结点 if(p->next == h)//整个程序的终止条件,依次回到上个函数结尾,相当于全部结束了 { return; } h= p->next; p = h;//此时h和p均指向要出列的结点 printf(”%dt“, h->data[0]); j = 0;//j用于while循环,使h指针指向要出列的点的前一个结点,所以及时清零 while(p->next!=s)//找s废弃节点 { p = p->next; } s = p->next; p->next = s->next;//连接新结点 free(s);//释放s所指空间 ShowOutput(h,h->data[1]); } 实验二、停车场管理问题 #include ”stdio.h“ #include ”stdlib.h“ #include ”time.h“ #define Maxsize 2 //停车场最多能停的车数 #define ElemType int int Prize;//每停一个时刻收费多少元 static int num = 0;//num用于记录入队的车所在的位置 typedef struct stack //模拟停车场的栈 { ElemType license[Maxsize];//用于存放车牌号 ElemType timeIn[Maxsize];//用于存放入停车场时间 int top; }Parking; typedef struct stack1 //退车时暂时存放 { ElemType license[Maxsize-1]; ElemType timeIn[Maxsize-1]; int top; }Backup; typedef struct road { ElemType license; struct road *next; }Road; typedef struct linkqueue//队列模拟便道 { Road *front,*rear; }LinkQueue; void GetIn(Parking *s, LinkQueue *q, ElemType license, ElemType timeIn);//有车进来 void GetOut(Parking *s, Backup *s1, LinkQueue *q, ElemType license, ElemType timeOut);//有车//出去 void PrintInformation1();//输出程序信息和个人信息 void PrintInformation2();//程序结束信息 void PrintTime() { time_t t; time(&t); printf(”%s“, ctime(&t)); printf(”rn“); } void PrintInformation1() { printf(”实验名称:实验二.停车场管理问题rn“); printf(”学号:031650106rn“); printf(”姓名:郭砚璞rn“); printf(”====================rn“); printf(”程序运行开始,Current local time and date:“); PrintTime(); } void PrintInformation2() { printf(”rn====================rn“); printf(”程序运行结束,Current local time and date:“); PrintTime(); } //给停车场用的配置函数 Parking *Init_Parking()//置空栈 { Parking *s; s=(Parking*)malloc(sizeof(Parking)); s->top=-1; return s; } int IsEmpty_Parking(Parking *s)//判空栈 { if(s->top==-1) return 1; else return 0; } int Push_Parking(Parking *s,ElemType license,ElemType timeIn)//入栈 { if(s->top==Maxsize-1) return 0; else { s->top++; s->license[s->top]=license; s->timeIn[s->top]=timeIn; return 1; } } int Pop_Parking(Parking *s,ElemType *license,ElemType *timeIn)//出栈 { if(IsEmpty_Parking(s)) return 0; else { *license = s->license[s->top]; *timeIn = s->timeIn[s->top]; s->top--; return 1; } } //给备用栈配置的函数 Backup *Init_Backup()//置空栈 { Backup *s; s =(Backup*)malloc(sizeof(Backup)); s->top =-1; return s; } int IsEmpty_Backup(Backup *s)//判空栈 { if(s->top ==-1) return 1; else return 0; } int Push_Backup(Backup *s, ElemType license, ElemType timeIn)//入栈 { if(s->top == Maxsize-1) return 0; else { s->top++; s->license[s->top] = license; s->timeIn[s->top] = timeIn; return 1; } } int Pop_Backup(Backup *s, ElemType *license,ElemType* timeIn)//出栈 { if(IsEmpty_Backup(s)) return 0; else { *license = s->license[s->top]; *timeIn = s->timeIn[s->top]; s->top--; return 1; } } //给候车便道链式队列配置的函数 LinkQueue *Init_LQueue()//创建一个带头结点的空队 { LinkQueue *q; Road *p; q =(LinkQueue*)malloc(sizeof(LinkQueue)); p =(Road*)malloc(sizeof(Road)); p->next = NULL; q->front = q->rear = p; return q; } void In_LQueue(LinkQueue *q, ElemType license)//入队 { Road *p; p =(Road*)malloc(sizeof(Road)); p->license = license; p->next = NULL; q->rear->next = p; q->rear = p; } int IsEmpty_LQueue(LinkQueue *q)//判队空 { if(q->front == q->rear) return 1; else return 0; } int Out_LQueue(LinkQueue *q, ElemType *license)//出队 { Road *p; if(IsEmpty_LQueue(q)) { printf(”队空“); return 0; } else { p = q->front->next; q->front->next = p->next; *license = p->license; free(p); if(q->front->next == NULL) q->rear = q->front; return 1; } } void main() { ElemType license, time,timelast=0; //timeInlast是最近一次有车进停车场的时间,提示以后输入的进场时间要大于此数值 char command;//进入A还是离开D Parking *s; Backup *s1; LinkQueue *q; PrintInformation1();//输出程序信息和个人信息 s = Init_Parking();//停车场 q = Init_LQueue();//便道队列 s1 = Init_Backup();//退车时的备用栈 printf(”请输入停车每小时需交纳的费用(元)rn“); setbuf(stdin, NULL);//使stdin输入流由默认缓冲区转为无缓冲区,必不可少 scanf(”%d“,&Prize); setbuf(stdin, NULL);//使stdin输入流由默认缓冲区转为无缓冲区,必不可少 while(1) { setbuf(stdin, NULL);//使stdin输入流由默认缓冲区转为无缓冲区,必不可少 Loop:printf(”请输入操作的命令,驶入停车场A,离开停车场D,查看停车场内车数P 0 0,查看候车厂内车数W 0 0,程序结束E 0 0:n“); scanf(”%c%d%d“,&command, &license,&time); setbuf(stdin, NULL);//使stdin输入流由默认缓冲区转为无缓冲区,必不可少 if(command == A) { if(time <= timelast) { printf(”输入的时间必须大于上一次输入的时间:t“); printf(”%dt“, timelast); printf(”请重新输入n“); goto Loop; } else { timelast = time; GetIn(s,q,license,time); } } if(command == D) { if(time <= timelast) { printf(”输入的时间必须大于上一次输入的时间:t“); printf(”%dt“, timelast); printf(”请重新输入n“); goto Loop; } else { timelast = time; GetOut(s, s1, q, license, time);//车开走的函数 } } if(command == P) { if(license == 0 && time == 0) printf(”停车场内停了%d辆车n“,s->top+1);//显示停车场车数 } if(command == W) { if(license == 0 && time == 0) printf(”侯车场内停了%d辆车n“,num);//显示候车场车数 } if(command == E) { if(license == 0 && time == 0) { PrintInformation2();//程序结束信息 system(”pause“); return; } } } } //停车函数 void GetIn(Parking *s, LinkQueue *q,ElemType license, ElemType timeIn) { if(Push_Parking(s, license, timeIn)== 1)//说明成功进入停车场 { printf(”%d号车在%d时刻停在停车场第%d个位置nn“,license,timeIn,s->top+1); } else //栈满,汽车要进入便道,即入队 { num++; In_LQueue(q,license); printf(”%d号车停在候车便道的第%d个位置nn“,license,num); } } void GetOut(Parking *s, Backup *s1,LinkQueue *q, ElemType license, ElemType timeOut) { ElemType *licen, *tim;//两个指针赋给出栈函数,用于读取车牌号和进停车场时间 ElemType ParkTime;//汽车在停车场停留的时间 ElemType Money;//汽车应缴金额 licen =(ElemType*)malloc(sizeof(ElemType)); tim=(ElemType*)malloc(sizeof(ElemType)); if(IsEmpty_Parking(s))//先判断停车场内是否有车 { printf(”对不起!停车场已空!nn“); return; } while(s->license[s->top]!= license) { Pop_Parking(s,licen,tim); Push_Backup(s1, *licen, *tim); if(IsEmpty_Parking(s)==1) { printf(”对不起!停车场内没有车牌号为%d的车nn“,license); while(IsEmpty_Backup(s1)!= 1) { Pop_Backup(s1, licen, tim); Push_Parking(s, *licen, *tim); } return; } } if(s->license[s->top] == license) { ParkTime = timeOut-s->timeIn[s->top]; Money = ParkTime * Prize; printf(”汽车在停车场内停留的时间为%d小时,应缴费%d元nn“,ParkTime,Money); Pop_Parking(s, licen, tim); while(IsEmpty_Backup(s1)!= 1) { Pop_Backup(s1,licen,tim); Push_Parking(s,*licen,*tim); } if(IsEmpty_LQueue(q)!= 1) { Out_LQueue(q,licen); Push_Parking(s,*licen,timeOut); printf(”%d号汽车此时退出便道,进入停车场最后一个位置nn“,*licen); num--; } } } 实验三、管道铺设施工的最佳方案问题 #include ”stdio.h“ #include ”stdlib.h“ #include ”time.h“ #define MaxVertexNum 100 //设置小区数最多为100 #define VertexNum 9 double LeastNum;//用于记录最短边长度 double LongestNum;//用于记录最长边长度,程序过程中不改变 double adjacent[VertexNum][VertexNum]; //邻接矩阵,不用于处理数据,而是用于暂时存储从文件读来的数据 //进而在邻接表存储数据时读取此数组数据即可 //二维数组数据为0的元素说明之间没有通道 //在处理完数据后,此数组会用来暂时存储处理后的数据,并写入到另一个文件中 typedef struct node {//边表结点 char adjvex; double weight;//权值 struct node *next; }EdgeNode; typedef struct vnode {//顶点表结点 char vertex; EdgeNode *firstedge; }VertexNode; typedef struct { VertexNode adjlist[MaxVertexNum]; int n,e; }ALGraph; void PrintInformation1();//输出程序信息和个人信息 void PrintInformation2();//程序结束信息 void CreateALGraph(ALGraph *);//将文件中数据导入进来构建无向图邻接表 void MinimumSpanningTree(ALGraph *);//将无向图转化为最小生成树 void ResultOutput(double *array,int n1,int n2);//将数据在控制台显示出来 void read_data(void);//从输入文件读取数据到邻接矩阵 void cout_data(void);//将邻接矩阵中的数据输出到输出文件 void read_array(double *array,int n1,int n2,FILE *fp);//内部函数,用户无需调用 void cout_array(double *array,int n1,int n2,FILE *fp);//内部函数,用户无需调用 void main() { ALGraph *G; PrintInformation1(); G =(ALGraph*)malloc(sizeof(ALGraph)); G->n = VertexNum;//为G指向的图分配空间,设置点数(小区数) read_data(); CreateALGraph(G); MinimumSpanningTree(G); cout_data();//输出到文件 ResultOutput((double *)adjacent,VertexNum,VertexNum);//输出到控制台 PrintInformation2(); system(”pause“); } void PrintTime() { time_t t; time(&t); printf(”%s“, ctime(&t)); printf(”rn“); } void PrintInformation1() { printf(”实验名称:实验三.管道铺设施工的最佳方案问题rn“); printf(”学号:031650106rn“); printf(”姓名:郭砚璞rn“); printf(”====================rn“); printf(”程序运行开始,Current local time and date:“); PrintTime(); } void PrintInformation2() { printf(”rn====================rn“); printf(”程序运行结束,Current local time and date:“); PrintTime(); } void CreateALGraph(ALGraph *G) { //建立无向图的邻接表存储 int i=0,j=0,k=0; EdgeNode *s; for(i = 0;i < G->n;i++) { G->adjlist[i].vertex = 65 + i; printf(”t%c“,(G->adjlist[i].vertex));//控制台输出列表头 G->adjlist[i].firstedge=NULL; } printf(”n“); for(k=0;k { for(j=0;j { if(adjacent[k][j]!=0) { s =(EdgeNode*)malloc(sizeof(EdgeNode)); s->adjvex = 65+j; s->weight=adjacent[k][j]; s->next = G->adjlist[k].firstedge; G->adjlist[k].firstedge = s; } } } } void read_data(void) { int i,j; FILE *fp; fp=fopen(”gyp_program3_Input.dat“,”r“);// 输入数据文件 read_array((double *)adjacent,VertexNum,VertexNum,fp); fclose(fp); for(i = 0;i < VertexNum;i++) { for(j = 0;j < VertexNum;j++) { if(adjacent[i][j] > LongestNum) { LongestNum = adjacent[i][j];//即给LongestNum设置的初值为最大边边值 } } } } void read_array(double *array,int n1,int n2,FILE *fp) { int i,j; float q; for(i=0;i for(j=0;j { fscanf(fp,”%f“,&q); *array++ =(double)q; } } void cout_data(void) { FILE *fp; fp=fopen(”gyp_program3_Output.dat“,”w“);//输出数据文件 cout_array((double *)adjacent,VertexNum,VertexNum,fp); fclose(fp); } void cout_array(double *array,int n1,int n2,FILE *fp) { int i,j; for(i = 0;i < n2;i++) { fprintf(fp, ”t%c“, 65 + i);//输出文件里打印列表头 } fprintf(fp,”n“); for(i=0;i { fprintf(fp,”%ct“,65+i);//输出文件里打印行表头 for(j=0;j { fprintf(fp,”%.1ft“,*array); array++; } fprintf(fp,”n“); } } void ResultOutput(double *array,int n1,int n2) { int i,j; for(i=0;i { printf(”%ct“,65+i);//控制台输出行表头 for(j=0;j { printf(”%.1ft“,*array); array++; } printf(”n“); } } void MinimumSpanningTree(ALGraph *G) {//将无向图转化为最小生成树 int i, j, k; int flag2=1,point; int start, end; EdgeNode *s; char NowAdjacent[VertexNum]; for(i=0;i { for(j = 0;j < VertexNum;j++) { adjacent[i][j] = 0;//先将邻接矩阵所有值清零 } } NowAdjacent[0]=G->adjlist[0].vertex;//初始点放入点集 for(i=0;i //刚开始只有一个起始点,之后加入剩余的VertexNum个点,即VertexNum-1次循环 { LeastNum = LongestNum;//这一步很重要,每加入一个点,应把LeastNum初始化为//最大值,避免受之前数值的影响 for(j = 0;j < i + 1;j++)//第i次点集里有i+1个点,即比较这i+1个点与生于点边的大//小,找最小边的另一个点 { point = NowAdjacent[j]-A;//用于指示已经存进点集中的点是图的第几个点 s = G->adjlist[point].firstedge; while(s!= NULL) { for(k = 0;k < i + 1;k++) { if(s->adjvex == NowAdjacent[k]) { flag2 = 0;//flag2=0用于指示此时s所指的点已经在点集内 break; } } if(flag2 == 1)//确保仅当s指向的点是不在点集里的点时,才被比较处理 { if((LeastNum > s->weight)&&(s->weight!=0)) { end = s->adjvex-A;//flag用于指示最短边是第几个点 start = point; LeastNum = s->weight; } } s = s->next; flag2 = 1;//标志位有可能已经被清0,必须重设为1,确保不影响下一次 } //启发:对于用的不止一次的标志位,一定不可能只单方向置位,一定是双向置位,//否则用了一次,后面就会误判 } adjacent[start][end] = LeastNum; adjacent[end][start] = LeastNum; NowAdjacent[i + 1] = G->adjlist[end].vertex;//向点集加入新点 } } 实验四、内部排序算法的实现与比较 #include ”stdio.h“ #include ”stdlib.h“ #include ”time.h“ const int N=3000;//随机产生N个随机整数 void PrintInformation1();//输出程序信息和个人信息 void PrintInformation2();//程序结束信息 void PrintTime(); void InsertSort(int R[], int n);//插入排序 void SelectSort(int R[], int n);//选择排序 void BubbleSort(int R[], int n);//冒泡排序 void QuickSort(int R[],int low,int high);//快速排序 void ShellInsert(int R[],int m,int n);//快速排序 void Shellsort(int R[],int n);//希尔排序 void KeyCompareNum_pailie(); void MoveNum_pailie(); int KeyCompareNum[5]={0};//分别存储这5种排序的关键字比较次数 int MoveNum[5]={0};//分别存储这5种排序的移动次数 int main() { //数组设为N+1个是因为有些算法是从数组1到N存储的,0位有的不用,有的用作了哨兵 int num0[N+1];//记录最原先随机产生的N个数字,因为num[N]每排序一次都会改变 int num[N+1];//每次排序前先读入num0[N]数据 //srand函数只增加一次就够了,用系统当前时间设置rand()随机序列种子,保证每次运行随机序列不一样 srand((unsigned)(time(NULL))); for(int j = 0;j < N;j++) { num0[j] = rand(); } PrintInformation1(); for(int j=0;j { num[j] = num0[j]; } InsertSort(num,N); for(int j = 0;j < N;j++) { num[j] = num0[j]; } SelectSort(num,N); for(int j = 0;j < N;j++) { num[j] = num0[j]; } BubbleSort(num,N); for(int j = 0;j < N;j++) { num[j] = num0[j]; } Shellsort(num,N); for(int j = 0;j < N;j++) { num[j] = num0[j]; } QuickSort(num,0,N-1); printf(”关键字比较次数按从小到大排列分别是:rn“); KeyCompareNum_pailie(); printf(”rn“); printf(”移动次数按从小到大排列分别是:rn“); MoveNum_pailie(); PrintInformation2(); system(”pause“); return 0; } void KeyCompareNum_pailie()//关键字比较次数排列 { int i,j,k,m; int printnum=0;//用于记录上一次输出的是数组KeyCompareNum的第几个数 int minimum;//用于输出最小的数字 int maximum=0; for(i = 0;i < 5;i++) { if(maximum < KeyCompareNum[i]) maximum = KeyCompareNum[i]; } for(m = 0;m< 5;m++) { minimum = maximum; //minimum在每次输出是都是全新的,不能受之前数值的影响,因此每次输出第一步先//把minimum设为最大值 for(j = 0;j < 5;j++) { if((minimum >=KeyCompareNum[j])&&(KeyCompareNum[j]!= 0)) { minimum = KeyCompareNum[j]; } } for(k = 0;k < 5;k++)//编程时k原先用的还是i,结果i=4这个情况,break以后到紧接//着的外层循环时,i的数值影响到了外层循环,跳了出来,因此换成了k { if(minimum == KeyCompareNum[k]) { KeyCompareNum[k] = 0; printnum = k; break; } } if(printnum == 0) printf(”直接插入排序:%dt“, minimum); if(printnum == 1) printf(”选择排序:%dt“,minimum); if(printnum == 2) printf(”冒泡排序:%dt“, minimum); if(printnum == 3) printf(”快速排序:%dt“, minimum); if(printnum == 4) printf(”希尔排序:%dt“, minimum); } printf(”rn“); } void MoveNum_pailie()//移动次数排列 { int i, j, k, m; int printnum = 0;//用于记录上一次输出的是数组KeyCompareNum的第几个数 int minimum;//用于输出最小的数字 int maximum = 0; for(i = 0;i < 5;i++) { if(maximum < MoveNum[i]) maximum = MoveNum[i]; } for(m = 0;m < 5;m++) { minimum = maximum;//minimum每次输出是都是全新的,不能受之前数值的影响,//因此每次输出第一步先把minimum设为最大值 for(j = 0;j < 5;j++) { if((minimum >= MoveNum[j])&&(MoveNum[j]!= 0)) { minimum = MoveNum[j]; } } for(k = 0;k < 5;k++)//编程时k原先用的还是i,结果i=4这个情况,break以后到紧接//着的外层循环时,i的数值影响到了外层循环,跳了出来 {//因此换成了k if(minimum == MoveNum[k]) { MoveNum[k] = 0; printnum = k; break; } } if(printnum == 0) printf(”直接插入排序:%dt“, minimum); if(printnum == 1) printf(”选择排序:%dt“, minimum); if(printnum == 2) printf(”冒泡排序:%dt“, minimum); if(printnum == 3) printf(”快速排序:%dt“, minimum); if(printnum == 4) printf(”希尔排序:%dt“, minimum); } printf(”rn“); } void InsertSort(int R[], int n)//插入排序 { int i, j; for(i = 2;i <= n;i++) { R[0] = R[i]; MoveNum[0]++; j = i-1; while(R[0] < R[j]) { KeyCompareNum[0]++; R[j + 1] = R[j]; MoveNum[0]++; j--; } R[j + 1] = R[0]; MoveNum[0]++; } } void SelectSort(int R[], int n)//选择排序 { int i, j, k; for(i = 1;i < n;i++) { k = i; for(j = i + 1;j <= n;j++) if(R[j] < R[k]) { k = j; KeyCompareNum[1]++; } if(k!= i) { R[0] = R[k]; R[k] = R[i]; R[i] = R[0]; MoveNum[1]+=3; } } } void BubbleSort(int R[], int n)//冒泡排序 { int i, j, flag = 0; for(i = 1;(i < n && flag == 0);i++) { flag = 1; for(j=1;j if(R[j + 1] < R[j]) { KeyCompareNum[2]++; flag = 0; R[0] = R[j]; R[j] = R[j+1]; R[j + 1] = R[0]; MoveNum[2]+=3; } } } void QuickSort(int R[],int low,int high)//快速排序 { int i,j; i=low; j=high; R[0]=R[i]; MoveNum[3]++; while(i { while((R[j]>=R[0])&&(j>i)) { j--; KeyCompareNum[3]++; } if(j>i) { R[i]=R[j]; MoveNum[3]++; i++; } while((R[i]<=r[0])&&(j>i)) { i++; KeyCompareNum[3]++; } if(j>i) { R[j]=R[i]; MoveNum[3]++; j--; } } R[i]=R[0]; MoveNum[3]++; if(low QuickSort(R,low,i-1); if(i QuickSort(R,j+1,high); } void ShellInsert(int R[],int m,int n) {//一趟希尔排序,按间隔m划分子序列 int temp,j; for(int i=m;i { temp=R[i]; MoveNum[4]++; j=i; while(j>=m && temp { KeyCompareNum[4]++; R[j]=R[j-m]; MoveNum[4]++; j-=m; } R[j]=temp; MoveNum[4]++; } } void Shellsort(int R[],int n)//希尔排序 { int m; m=n/2; while(m>=1) { ShellInsert(R,m,n); m=(m==2?1:(m/2)); } } void PrintTime() { time_t t; time(&t); printf(”%s“, ctime(&t)); printf(”rn“); } void PrintInformation1() { printf(”实验名称:实验四.内部排序算法的实现与比较rn“); printf(”学号:031650106rn“); printf(”姓名:郭砚璞rn“); printf(”====================rn“); printf(”程序运行开始,Current local time and date:“); PrintTime(); } void PrintInformation2() { printf(”rn====================rn“); printf(”程序运行结束,Current local time and date:"); PrintTime(); } 计算机实践报告 本次实践的主题主要围绕湖南省刺绣瑰丽--湘绣而进行,实践过程主要分以下几个步骤: 一、主题的选择。自古以来,湘绣作为中国刺绣史上的一大艺术宝库,一直有着重要的地位,了解湘绣能让我们更加了解祖国的传统文化,培养民族自豪感,增强青年人保护国家传统文化的责任心。主题将主要包括湘绣的起源、历史发展、湘绣的主要特色、品种分类以及湘绣的欣赏等等。 二、具体操作。以网页的制作为主体,再结合Flash动画制作和幻灯片技术装饰网页页面以增强网页的美观性、同时更详细生动地介绍湘绣的一些情况。 1、第一张网页即首页,主要是设置超链接的按纽,在这张网页上可以点击按纽进入自己想要进入观看的网页,另外“我的幻灯片”超链接也设置在这张网页上。这张网页上另外还会有用Flash制作的动画以及几张关于湘绣的图片,其中动画里应用了辉光和遮罩。具体操作是先导入一张湘绣图片,再创建“湘绣”和“辉光”元件:执行“插入/新建元件”命令,名称是“湘绣”,再单击工具箱中的“文字工具”中的“A”的,在上面输入“湘绣”两个字,并在“属性”当中设置字体的形式,选中字体,执行“修改/分离”命令两次,把字体打散。重复执行“插入/新建元件”命令,新建一个图片,名称为“辉光1”,重复以上操作,再创创建一个“辉光2”元件,在“窗中/设计面板/混色器”上设置辉光的颜色和亮度。然后再开始创建动画,切换到“主场景1”建立图层,从库里调出湘绣图片、“湘绣1”“辉光1”元件、“辉光2”元件放到相应图层上,现在主要介绍一下“辉光1”元件的动画设置。把“辉光1”元件从库里调出以后分别放到放到左边,并用工具栏上的“任意变形工具”使元件产生一定的倾斜度。在第50帧和第100帧上添加关键帧,在第1帧和第100处将元件放在左边,在第50帧处将元件放到右边处,并在第1帧和第100帧处建立动作补间动画。这样就创建好了动画,再创建一个遮罩层。首页的动画就创建好了,说起来容易做起来难,做动画时遇到好多困难。特别是刚开始时一点不懂,把湘绣图片导入到库的时候我老是找不到图片,因为电脑上默认的是列表形式的排列,所以我每次都要先单独打开保存图片的那个文件找到我要的图片并记住图片的名称后,再进行将图片导入到库的操作。后来在室友的帮助和提醒下才知道我可以更改图片的排列方式,只要将其改为“缩略图”形式就可以直接在上面看到图片了。这样方便了好多,虽然这个看起来很简单,但是对于并不懂计算机的我们来说也是一大收获啊。 2、第二张网页主要是围绕“湘绣的起源”这个主题而展开的。这一页主要是介绍湘绣在历史上的兴起,关于这个问题的有关说明。这张网页求同样式以设置了动画,此动画为文字逐帧动画,先单击“工具箱”中的“文本工具”,并打开“属性”面板,设置好字体。选择时间轴的第一帧,输入“湘”,在第5帧、第10帧、第15帧上按F6键插入3个关键帧,选中第5帧将“湘”改为“绣”,选中第10帧将“湘”改为“起”,选中第15帧将“湘”改为“源”。再在第这些帧之间创建动作补间动画,将其动作设置为顺时针。再创建7个图层,画圆将其设置为动作补间动画,将整个画面设置成“雪花飘零”的感觉。另外将再用文字详细说明湘绣的起始。 3、第三张网页的主题是“湘绣的发展”。湘绣从出现开始就一直很受上至贵族下至平民百姓们的欢迎,所以她在历史上的发展很快,特别是近代在国际上都受到了极大的欢迎,多次在国际上获奖。这一页“湘绣的发展”这几个字将用网页特效表示出来,是在网上找的网页特效,并在其指导下将相应制作网页代码更改一下就可以了,再创建形状补间动画。用图层1中的导入的图片作为背景,创建图层2,单击第一帧在舞台上画椭圆设置其颜色为黄色,再单击第30帧插入空白关键帧,用椭圆工具按住Shift键在舞台上画圆设置其颜色为桔黄色,再单击第60帧在舞台上画方框,同样设置其颜色为紫色,再以同样的方法在第70、80、90 帧处画椭圆,圆、方框并设置在不同的位置,这样在放动画时就能收到更好的效果。另外再设置文字动画,新建一个图层在第一帧的舞台上输入文本“锦”,再在第5帧处插入关键帧在舞台上输入文本“绣”,如此重复在上面输入“锦绣屏中如月升”,再以同样的方法在下方输入“欲言其美语难成”创建同样的效果。再用文字说明湘绣的具体发展过程以及近百年来的成就。 4、第四张网页的主题是“湘绣的特色”。这一张嘛,同样还是有动画的噢,只不过这个动画采用的又是另外一种格式,利用时间轴特效制作动态文字。首先输入文本“字格簪花,迹来针线”再执行“插入/时间轴特效/效果/展开”命令,打开“展开”对话框在里面设置动画的效果。另外动画上还会设置三张湘绣图片的动作补间动画。然后下面同样用文字说明湘绣的工艺特色。 5、至于第五张网页我主要会利用动画提供一些关于湘绣的图片欣赏,大饱观者的眼福。所利用的方法也差不多就是前面所用到的一些了,因为我水平实在有限。 网页的内容差不多就这些了,现在我还是来讲讲我的幻灯片吧。做幻灯片主要是为了更好地让观者了解湘绣,同时丰富网页的内容。每幅幻灯片都会有一些关于湘绣的图片,特别是幻灯片4当中的由长沙马王堆出土的绢地长寿绣,我在网上查了好几个网站才把它给搜出来的。这些幻灯片分别使用不同的图片作为背景,但由于从网上搜索的图片尺寸有限,所以放大后就有点模糊了,不过作为背景图片,效果也还不算很烂。幻灯片2和5还超链接了Flash动画。刚开始时我不知道还要修改动画的格式之后才能插入,所以我直接点击“视图/工具箱/控件工具箱”,在弹出的面板上点击“其他控件”并选择“Shockwave Flash Object”,然后在上面有鼠标拖动一个方框之后点击右键,并选择“属性”在窗口中的自定义中输入文件名。这样做完之后我打开幻灯片预览却发现只有个白色的方框。后来一问同学才知我没改动画的格式自然上不成功了,而改文件格式的方法就是打开Flash动画,执行“文件/发布”命令就可以了。虽然是这样可我还是没有成功,因为我在Flash“属性页”对话框里输入文件名时多加了个点,结果一个点害我停留在那个操作上将近半个小时。我现在是深刻体会到细心的重要了。另外在插入动画的时候还要注意的是设置的边框要和插入的影片大小一致,要不然做出来的幻灯片就不好看了。另外还在幻灯片中插入了音乐,会随着幻灯片的放映而播放。插入音乐之前也必须把音乐的MP3格式转换为Wav格式才能插入进去。 到这里我的制作也差不多完成了。这次实践给我带来的收获确实是非常大的,我原本是一个 “计算机盲”,但现在我却能制作自己设计的网页,使用的还是我自己创建的动画。虽然还有很多不懂,但重要的是我已经迈出了一步,比以前进步了。这次对我来讲也是个不小的挑战,因为我本来就不专业,而我自己的电脑上的Microsoft 2003又全是英文版的,所以在制作幻灯片时更增加了难度。一次本来要设置播放效果的,结果却把其中的一张图片进行了动作设置,将其链接到下一张幻灯片上,弄得自己刚开始时也莫名其妙。还有就是关于Flash动画的制作,以前是从不敢想自己有一天要弄这个东西的。总觉得这是个太奇妙的东西,不是我们所能理解的。可是现在,动画做多了,也好多了。就在这次实践刚开始的时候我还只知道照着书本依样画葫芦地设置辉光,就像其他同学说的也只会弄个有辉光遮罩的动画。但现在我不只是仅限于此了,我还知道如何使用引导层、如何使用时间轴特效制作更动感的动画。还知道其实形状补间动画灵活性介于逐帧动画和动作补间动画之间,用起来效果也很不错。在这种启示下我还制作了文字的动态改变。 总而言之,本次实践使我对计算机有了更进一步的认识,在完成作业的同时也扩大了自己的计算机方面的知识,也让我对自己以后的计算机学习增强了信心。 在广州xxxx新材料科技有限公司毕业实践报告 一. 毕业实践目的 通过理论联系实际,巩固所学的知识,提高处理实际问题的能力,为顺利毕业进行做好充分的准备,并为自己能顺利与社会环境接轨做准备。通过这次实践,使我们进一步理解和领会所学的基本理论,了解计算机技术和信息管理技术的发展及应用,较为系统地掌握计算机应用技能和信息管理技能,把所学知识与解决实际问题相联系,能够利用计算机处理工作中的各种信息,培养我们发现问题、分析问题和解决问题的能力,从而提高我们从事实际工作的能力。 通过理论联系实际,巩固所学的知识,提高处理实际问题的能力,了解设计专题的主要内容,使我们能够了解社会、学校的需要,在实践单位领导的帮助,对自己今后所从事的事业有一个实习了解的过程。为自己能顺利与社会环境接轨做准备。 实践可以加强锻炼我们的能力,了解社会、熟悉民生,看清自己的定位是很有帮助的。而从就业角度来看,拥有丰富实习经历的学生在就业时的优势也是比较明显的。 二. 毕业实践单位概况 广州xxxx新材料科技有限公司,地处广州市中心城区,是一家专业从事新材料技术开发与产品应用推广的技术型企业,主要发展方向为前沿技术、环保等领域,产品应用于化工、建筑等行业。 广州xxx科技有限公司应势而生,通过整合优势资源,强化核心竞争力,做好品牌推广与产品应用工作。公司已与广州市xxx科技有限公司形成战略合作,成为xxx混凝土增效剂产品全国总经销商。目前共有三十多家分销商,xxx混凝土增效剂产品自2009年投放市场,已成为混凝土外加剂应用行业新技术、新思路的创造者和引导者,市场前景广阔。 xxx坚持“客户至上,倾心服务”的经营理念,组建了一支富有经验的市场服务团队,建立了完善的产品研发、供应和质量管理系统,能不断满足市场对产品和服务的需求。 与您携手,是我们的荣耀!我们坚信,展现在我们面前的必将是一个规范、有序、可持续发展的美好未来!毕业实践内容 我在学校做代码的时候多一点,计算机硬件这里,接触的还真不多,技术成度介于初学者和高手之间,那种尴尬的中间部分。于是,我在毕业的一个月内,来到了这家公司,大部分都是我这种刚毕业出来的。公司的办公地点可以称做那种在闹市区的小店,但是老板告诉我们说有分公司,而且要在半年内做大做强,成人高等学历教育计算机应用技术2013级专科xxx毕业实践报告 把的自己的产品推销出去,所以需要招一个懂电脑方面的帮公司维护电脑和网络的畅通能正常的使用。俗话说:我不在乎有没有钱,我在乎的是有没有前途,大家看到这位老板有这么大的雄心壮志后,就决定在这里好好的干,当然做为新人的第一个月,工资非常少,1600元一个月,没有补助,半个月放一回假(新来的人都很有干劲,头一个月谁都没有休息)。做为一个有雄心壮志的人,这些问题对我来说没有什么,我真实的想法是希望通过在这家公司的工作,可以帮助我在技术上得到提高,使自己能达到一个质的变化,有所成就。 我实习所在的部门是技术部,主要从事网络、电脑维修工作。刚开始实习时,办公室的同事给了我一些有关部门运营和计算机维护的公司的规章制度,让我对公司运营情况和计算机计网络维护--特别是系统维护有了一定的认识,真正体会到了一个企业单位对人事的重视,理解了技术部的电脑维护工作虽然是企业部门运营的一个小侧面,但关系到企业在广大市民心目中的形象,我们中有经验的前辈还给我仔细讲解了计算机维护的每一款注意事项。 刚开始的时候有点手忙脚乱,不是这边搞错就是那边忘了,很多用的器材都叫不上名字,环境也很不熟悉,毕竟是刚出来工作的嘛,很多都是同事指导和帮助我。由于后来心态的调整和渐渐的熟练,慢慢地学得差不多了,就呆办公室里,有工作就安排给技术员,没有就闲着,忙的时候一直忙,闲的时候就发呆,和同事套近乎,期盼着能有多点事情安排给我做,不然实在是太无聊了。在部门领导及全体同事的帮助指导下,经过多天的学习、工作,我已熟悉整个维护服务的流程,可以独立、熟练地比较准确地报出各种网络故障问题。 我主要做的是电脑组装和硬件维修,电脑组装这个活不难,可是重在实际操作,因为在学样的时候,偏爱软件方面多一些,所以这硬件的活还真生疏了不少,不过在一些同事的带领下,我很快就掌握了这些本领,并且通过实际工作,很快就可以独立掌握给用户安装电脑了。 安装电脑 组装电脑这个活,经过我总结发现,有以下几点: 1、实际操作能力。工作时,如果你只是把书看的明白,可没有实战过,那么在实际操作的时候,你会变的手足无措,不知道从何处下手。 2、工作时,要细心、注意细节。电脑硬件这些东西,有些部件是十分的娇贵的,要小心对待,那种感觉就像是对待初生的婴儿似的,比如CPU就要物别小心,不能把上面的针角弄坏了,哪怕是一个,都会影响它的使用,还有就是主板,使用的时候也要小心,因为主板就像是人的身体,身体一坏也就不能工作了,这些都是注意的地方。 3、及时了解市场动态信息。因为电脑硬件更新快,价格变化也快,所以只 成人高等学历教育计算机应用技术2013级专科xxx毕业实践报告 有经常浏览相关信息和到批发市场上了解最新的产品才可以,否则,就会在激烈的市场竞争下被别人的店给淘汰的。组装电脑的工作流程: 组装电脑时,我们会根据公司的需求,列出电脑配置单,然后去采集相关贷物,在安装时,首先拿出主板,然后安装CPU和风扇,这时在将已安装CPU的主板放入机箱内固定,安装其它硬件,如显卡、网卡、硬盘等,最后进行跳线,在跳线时要注意线序,因为有的前置USB接口没有标识,所以很容易安错,结果导致跳线烧毁,这都是要注意的。当安装完毕后,我们还要安装操作系统,这个可以使用特定的系统盘,十分钟左右就可以搞定。最后,一台电脑安装完毕,我们拿给有需要的同事试用,看看有没有需要其它的软件要安装的。 电脑维修服务 电脑维修也是我经常要做的工作,通常我们会接到其它办公室的电话去解决问题,一般用户电脑出现的故障主要有: 1、操作系统损坏或种毒 使用自己从网上找来的系统文件,按照在学校的学到的知识,简单熟练的把系统给安装完了,而用户一般出现安装系统的情况大多是,系统种毒,或因使用不当造成系统文件损坏。 有一次,办公室的同事拿来一台新电脑,叫我帮我重装一下系统,以前在学校觉得装系统是很简单的一件,于是我大声的说没问题。下午可以过来拿,我记录他的对电脑系统的要求,需要什么软件我一一的记录了下来。就要开始我的装系统之旅,原本的系统是新买时的原WIN8系统,叫我换成WIN7,然而我像平时那样操作了起来,但装完我发现没装上,当时还以为是我没有安装到位,这一次我认真了起来,对系统重新装了一次,但还是没有装上去,于是我开始慌了。毕竟这是我刚来公司,对同事所做的第一件事,我必须得留下好印象,于是我开始了在网上找资料看一下是什么原因。网上说是硬盘接口类型要改一下,要在BIOS改一下硬盘接口类型,才能装上系统,于是我进入了BIOS后怎么都没有发现网上所说的那个选项,当时我就懵了,当时在学校学习的时候以WIN7为主,还没有WIN8的系统。当时我冷静了一下,在想着要怎么解决这个问题。后面我突然想到有一个朋友在学校实习,于是我立刻打电话过去请教他。经过他的指导,我把系统的硬盘全格后,然后在分区工具里面把硬盘的接口类型改了另外一个,然后重新分区。然后再装系统。最后把它给装上去了。到最后我不得不感叹科技的发展实在是太快了,人只有在不断的实践、学习才能跟得上科技的发展。才能在这个残酷的社会上生存。 2、计算机上不了网 成人高等学历教育计算机应用技术2013级专科xxx毕业实践报告 用户计算机上不了网,是第二大问题,多数原因是路由器配置出现问题,或ip调控出现问题,一般我们会帮助用户调整IP或处理路由器。也有机子是系统方便的问题。这个两个方面都还是不能解决的话上不了网,我会抱歉的跟同事们只能装重做系统。遇到这种问题是我最不想出现的。这样会让同事感觉你的技术不是那么扎实,给人的印象不是很好。当然有的机器上不了网是硬件出现问题,这样我就会登记好是什么硬件,然后像财务报备一下,需要采购一下硬件。 3、计算机无法进入系统 这种情况大多是出在计算机业卡上,或者是计算机电源上,这时我们需要使用专门的故障检测卡来检查计算机硬件,并对出现问题的硬件进行更换处理。 通过在电脑维修工作,使我的技术得到了极大提高,也使我的个人社会交往能力有了进步,因为上门服务除了修机器就是接触人,通过接触客户,和客户进行沟通,使我自己在与人交往上变的更加有自信了。尊敬的老师,通过这次实习,我在各方面取得了进步,在今后,我将会更加努力的工作,已报答老师和学校对我的培养。 作为实习生,我严格的要求自己,甘于吃苦,任劳任怨,尽心尽力,遵守公司的规章制度,主动打扫办公室卫生,尊重领导,维护领导的威信,适应领导的工作习惯、工作方法、工作风格以及工作特点。主动向领导、向办公室同志学习,取长补短,加强沟通,增进了解,提升能力。对领导和办公室交办的日常文字材料,即接即办,保证按领导的要求按时、准确办结,不断提高工作效率。 我的领导逻辑性较强,凡事爱有个前因后果,对这样的领导就要在谈工作时要讲事实,摆观点,有分析有推理,了解领导的工作风格和特点,目的是为了更好地做好工作。当然,在尊重领导的前提下,要敢于提出自己的观点,要有维护真理的精神。在协调与本部门领导的关系时,对待当事人要热情周到,庄重大方,言行得体,不卑不亢,树立起一言一行,都直接、间接地影响办公室或领导的形象的意识。 我所学的,我知道的知识还远远不够。每个礼拜,我们要做一次材料计划表。其实这个表是很简单的。这些简单的文档操作平时觉得是微不足道的一些知识好像根本就不用多说的问题。但真正自己在实际操作中确实是模棱两可的。这说明自己对自己所学知识太浮了们有深入,这种眼高手低的毛病在工作中是最忌讳的。所以在工作中端正态度是必须的。 感触最深的是自己的社会经验不够丰富,我觉得工作并没有想象的那么轻松,接触的都是有很多年社会经验的大人,跟他们在一起,需要把自己摆在大人的立场上说话,思考问题,说话办事都要有所注意,有时会觉得很累,但是我把这次实习当成是锻炼自己,接触社会的平台,不管有多困难,也要努力克服,要 成人高等学历教育计算机应用技术2013级专科xxx毕业实践报告 抓住这次机遇,不断的向他们学习,充实自己。 虽然实践用到的计算机专业的知识不多,但是我仍然能够感觉到自己的计算机知识还是不太扎实,如果要学以致用,还需要进一步加深自己的知识水平。 在实践期间,我深切地感受到,技术服务部是一个团结、上进、充满活力的集体。每天大家都是笑脸相迎,即使面临很大的工作压力,办公室里仍然会听到笑声;面对客户,大家总是热情真诚;面对工作上的困难,大家总是互相帮助,直至解决难题。整个部门和睦相处,就像一个温馨的大家庭。而部门领导就是这个家庭中的家长,给每个人很大的空间自由发挥。从他们身上,我真正体会到了“敬人、敬业、高效、高水平服务”的真实意义,体会到了服务部“服务、奉献”的意义,体会到了“创造完美、服务社会”的服务理念,知道了什么是“创新就是生活”。特别令我感动的是,每当我遇到困难向大家求助时,谁都会无私的告诉我;我对能到这样的公司实践感到骄傲,感到自豪。我很庆幸自己能在这样有限的时间里,在这么和谐的气氛中工作、学习,和同事们一起分享快乐,分担工作。所以我努力向同事学习,不懂就问,认真完成领导和同事交给我的每一项工作。部门领导和同事也都尽力帮助我,给我有关知识.三. 毕业实践心得体会 在这段的实习期间中,我学到了许多书本上学不到的知识。从学校走向社会,首要面临的问题便是角色转换的问题。从一个学生转化为一个单位人,在思想的层面上,必须认识到二者的社会角色之间存在着较大的差异。学生时代只是单纯的学习知识,你可以有很好的同学,很好的朋友,大家相互嘘寒问暖,不必勾心斗角。而社会实践则意味着继续学习,并将知识应用于实践,学生时代可以自己选择交往的对象,而社会人则更多地被他人所选择。存在着利益关系,又工作繁忙,就多了份人情世故。诸此种种的差异。不胜枚举。但仅仅在思想的层面上认识到这一点还是不够的,而是必须在实际的工作和生活中潜心体会,并自觉的进行这种角色的转换。 通过实践使我对计算机有了更具体认识,通过对计算机的具体操作和亲自实践巩固了课本上学的知识,在这个基础上把所学的计算机应用专业理论知识与实践密切结合起来,培养自己实际工作能力与分析能力,达到学以致用的目的。理论与实际的结合,学校与社会的勾通,进一步提高了自己的思想觉悟,业务水平;尤其是观察分析和解决问题的实际工作能力,实习的一个重要功能,在于运用教学成果,检验教学成果。运用教学成果,就是把课堂上学到的系统化的理论知识、尝试性地应用与实际的工作中。我在就业心态上也有了很大的改变,以前我总想找一份适合自己爱好,专业对口的工作,可现在我知道找工作很难,要专业对口更难,很多东西我们初到社会才接触。所以我现在不能再像以前那样等待更好机 成人高等学历教育计算机应用技术2013级专科xxx毕业实践报告 会的到来,要应尽快丢掉对学校的依赖心理,学会在社会上独立,敢于参加与社会竞争,敢于承受社会压力,使自己能够在社会上快速成长。再就是时常要保持一颗学习、思考的心。作为一名大学生,最重要的就是自己学习和思考的能力。在环保公司这样一个新环境中,有我们很多值得学习、值得思考的地方,这就需要自己保持一颗学习、思考的心。首先在技术方面,要刻苦的补充自己的不足,认真地对待工作,时时刻刻的思考和学习。同时,在企业的环境中,更要注重学习他们先进的管理和人文文化,以丰富自己的社会知识和管理文化知识。这样,可以为自己日后的职业生涯打下良好的基础。 总之,这次实习使我获得了人生第一笔宝贵的工作经验,虽然在步入社会后,还有很多东西要学习,很多教训要吸收,我知道这些给我的仅是初步的经验积累,对于迈向社会还是远远不够的但我想我已经做好了足够的准备,无论是心态上还是技能上。现代社会的竞争是残酷的,但只要努力地付出,我的职业生涯就必定会开出希望的花,我相信我的未来不是梦,只要自己努力过,成功是不会把你拒之门外的。 实习,是开端也是结束。实习的同时也让我了解到了自己的不足,在今后的工作和生活中,我会继续努力,完善自我。更加努力的奋斗下去。 对毕业实践单位改进的意见、建议和总结 感谢广州xxx科技有限公司的各位领导和同事,是他们对我孜孜不倦的支持、帮助和指导,才使我满载而归,是他们给我上了步入社会前的第一堂课。我会加倍努力的学习,为真正的步入社会打下坚实的基础。在这实习期间我对公司提出一点意见仅代表个人意见。 1、公司有经济的条件下可以换个更高级一点的路由器,在实习中我发现公司的路由器的功能不是很齐全。虽然能满足现在的工作量。但有些功能还是达不到的虽然那些功能暂时不怎么要用但为了长期的我建议可以换掉。 2、公司的模式流程个人觉得有点复杂。例如报销费用不用大小都经过总经理签名才能报销,可以设置报销金额的大小还决定。有时候报销一笔小费还要等上个把星期等到经理来才能报销。 以上两点是我对公司的意见。希望公司在后期能改进一下。第四篇:计算机实践报告
第五篇:计算机实践报告