图像处理职位面试题汇总(1)

Matlab编程部分

1. Matlab 中读、写及显示一幅图像的命令各是什么?

解:第一、Matlab中读图像函数是imread( )。imread 函数用于读入各种图像文件,其一般的用法为:[X,MAP]=imread(‘filename’,‘fmt’)

其中,X,MAP分别为读出的图像数据和颜色表数据,fmt为图像的格式,filename为读取的图像文件(可以加上文件的路径)。如: [X,MAP]=imread(’flowers.tif’,’tif’);比较读取二值图像,灰度图像,索引图像,彩色图像的X和MAP的特点。

第二、Matlab中读图像函数是imwrite( ),imwrite函数用于输出图像,其语法格式为: imwrite(X,map,filename,fmt)

按照fmt指定的格式将图像数据矩阵X和调色板map写入文件filename。

第三、Matlab中显示图像函数是imshow( ),imshow函数是最常用的显示各种图像的函数,其语法如:imshow(X,map)

其中X是图像数据矩阵,map是其对应的颜色矩阵,若进行图像处理后不知道图像数据的值域可以用[]代替map。

  • 二进制(二值)图像显示方法,在MATLAB中一幅二值图像是uint8或双精度的,该矩阵仅包含0和1。如果希望工具箱中的函数能将图像理解为二进制的,那么所有数据都要是逻辑数据,必须对其进行设置(将所有数据标志均设置on).可以对数据利用“~”取反操作实现图像逆转即黑白反色。
  • 灰度图像的显示方法,正常情况下无需指定灰度图像的调色板格式。可以是使用imshow函数指定要显示灰度级数目,格式 imshow(I,n),n为指定的灰度级数目。用户也可以指定数据的范围,格式imshow(I,[low high])其中low 和high参数分别为数据数组的最小值和最大值。如果为空矩阵([]),那么imshow函数将自动进行数据标度。
  • 索引图像,imshow(x,map)对于x的每个个像素,imshow显示存储map中相应行的颜色。
  • RGB图像的显示,它直接对颜色进行描述而不使用调色板,格式imshow(RGB)。 RGB(:,:,1) RGB(:,:,2)   RGB(:,:,3)
  • 特殊显示,如多幅图像的显示,需要显示多幅图像时。可以使用figure语句,它的功能就是重新打开一个图像显示窗口.

2. Matlab 与VC++混合编程有哪几种方式?

解:Matlab与VC++混合编程主要有三种方式:Matlab引擎方式、Matlab编译器及COM组件。

第一种:Matlab引擎方式。Matlab引擎采用客户机/服务器(Client/Server)的方式,提供了一组Matlab API函数,通过调用这些函数实现以用程序进程之间的数据传递。VC程序作为前端客户机,向Matlab引擎传递命令和数据,并从Matlab引擎接受数据信息,实现动态通信。采用这种方法几乎能利用MATLAB全部功能,但是需要在机器上安装MATLAB软件,而且执行效率低,因此在实际应用中不采用这种方法,在软件开发中也不可行。

第二种:Matlab编译器。MATLAB Compiler可以将M语言函数文件自动转化产生独立应用程序或者软件组件,生成的组件或者独立应用程序可以与其他用户共享。使用MATLAB Compiler创建的独立应用程序或者软件组件能够完全脱离MATLAB环境。MATLAB Compiler能够显著的缩短桌面应用程序的开发时间,仅仅通过简单的指令就可以将M语言函数转变为独立的应用程序或者软件组件,然后将它们打包发布给最终用户。这里所指的利用M语言开发的MATLAB应用程序可以包括数学计算、图形应用和GUIDE开发的图形界面等,而最终用户根本不需要拥有MATLAB。

其特点:1、自动将M语言函数文件转换为可执行应用程序,脱离MATLAB环境2、简单易用的发布过程3、支持所有MATLAB的M语言特性,例如MATLAB对象、Java对象等等。4、支持大多数工具箱函数,允许将MATLAB基本算法免费发布使用强大功能。 5、用户可以利用MATLAB集成环境开发自己的算法原型和应用程序,然后利用MATLAB Compiler将开发的算法发布给最终用户,最终用户可以通过可执行应用程序或者软件组件应用开发完好的算法。用户在进行算法维护的时候无需针对C代码进行操作。

算法开发仅仅需要三个步骤:第一步:创建算法,MATLAB本身是一种集成化的算法开发环境,它提供了各种工具用于完成算法快速原型的开发、测试。其中包括了高级的基于矩阵运算的向量化算法语言M语言,并且内建了大量用于数学计算、数据分析和图形可视化的算法函数。MATLAB开发工具提供了语言编辑器、调试工具和性能分析器,并且可以利用交互式图形界面开发工具开发自定义的图形界面工具。第二步:转化应用程序。使用MATLAB Compiler可以将开发好的M语言算法函数转变成为:独立可执行应用程序;C/C++算法共享库;软件组件,例如COM对象或者Excel插件;独立可执行应用程序;MATLAB Compiler可以自动地将MATLAB应用程序转变为独立可执行应用程序;自动确定相关的MATLAB函数 ;生成C/C++接口代码文件 ;将所有相关的文件打包压缩保存在单一压缩文件中;可在最终的应用程序中集成用户的C或C++代码。第三步:算法函数库。使用与创建独立可执行应用程序相同的指令就可以创建MATLAB函数库。MATLAB  Compiler将自动创建相应的头文件和共享库文件,以便集成自定义的C/C++代码文件,最终完成应用程序的开发。通过MATLAB Compiler完成应用程序的发布之后,可以将应用程序打包、发布给任意的最终用户。MATLAB Compiler提供了相应的应用软件可以将运行应用程序必需的库文件打包。

第三种:COM组件COM(Component Object Model,组件对象模型)是以组件为发布单元的对象模型,是一系列面向对象技术和工具的集合。由于COM是建立在二进制级别上的规范,所以组件对象之间的交互规范不依赖于任何特定的语言。MATLAB提供了COM生成器。COM生成器提供了实现MATLAB独立应用的一种新途径。它能把MATLAB开发的算法做成组件,这些组件作为独立的COM对象,可以直接被C++、VB、VC、C#、JAVA或其他支持COM的语言所引用,只要相应的MATLAB编译器和C/C+ +编译器都已经安装及配置成功,MATLAB COM编译器即可开始使用,并不需要特别的设置。该方法实现简单,通用性强,而且几乎可以使用MATLAB的任何函数(注意:不支持脚本文件,脚本文件使用时要改为函数文件),因此在程序较大、调用工具箱函数或调用函数较多时推荐使用。Matlab的COM 编译器是在Matlab6.5中才开始提供的一个新工具,从Matlab7.0起,这个产品改名为MatlabBuilder for COM。基于COM的混合编程方法也是Mathworks公司推荐使用的方法。

以上3种方法中,采用Matlab引擎方式,应用程序整体性能好,Matlab引擎支持功能全面,但需要Matlab后台运行,不能脱离Matlab 环境。而MCC方法和COM组件方法均可以脱离Matlab环境,应用程序运行效率高,利于软件的开发。

3.Matlab运算中 . *和 * 的区别?

解:MATLAB中 带“.” (读作“点”)的运算符都表示点运算。这就要求A.*B中的A、B必须同规格,然后对应点的数据相乘,结果也是一个与A、B相同规格的矩阵。(标量是1*1矩阵)MATLAB的数据单元是矩阵,*表示的是矩阵相乘。要求A*B中A的列数等于B的行数。

图像处理基础部分

1. Intel指令集中MMX,SSE,SSE2,SSE3和SSE4指的是什么?

解:Intel指令集中MMX(Multi Media eXtension,多媒体扩展指令集指令集是Intel公司于1996年推出的一项多媒体指令增强技术。MMX指令集中包括有57条多媒体指令,通过这些指令可以一次处理多个数据,在处理结果超过实际处理能力的时候也能进行正常处理,这样在软件的配合下,就可以得到更高的性能。MMX的益处在于,当时存在的操作系统不必为此而做出任何修改便可以轻松地执行MMX程序。但是,问题也比较明显,那就是MMX指令集与x87浮点运算指令不能够同时执行,必须做密集式的交错切换才可以正常执行,这种情况就势必造成整个系统运行质量的下降。

Intel指令集中SSE(Streaming SIMD Extensions,单指令多数据流扩展)指令集是Intel在Pentium III处理器中率先推出的。其实,早在PIII正式推出之前,Intel公司就曾经通过各种渠道公布过所谓的KNI(Katmai New Instruction)指令集,这个指令集也就是SSE指令集的前身,并一度被很多传媒称之为MMX指令集的下一个版本,即MMX2指令集。究其背景,原来"KNI"指令集是Intel公司最早为其下一代芯片命名的指令集名称,而所谓的"MMX2"则完全是硬件评论家们和媒体凭感觉和印象对"KNI"的 评价,Intel公司从未正式发布过关于MMX2的消息。而最终推出的SSE指令集也就是所谓胜出的"互联网SSE"指令集。SSE指令集包括了70条指令,其中包含提高3D图形运算效率的50条SIMD(单指令多数据技术)浮点运算指令、12条MMX 整数运算增强指令、8条优化内存中连续数据块传输指令。理论上这些指令对目前流行的图像处理、浮点运算、3D运算、视频处理、音频处理等诸多多媒体应用起到全面强化的作用。S SE指令与3DNow!指令彼此互不兼容,但SSE包含了3DNow!技术的绝大部分功能,只是实现的方法不同。SSE兼容MMX指令,它可以通过SIMD和单时钟周期并行处理多个浮点数据来有效地提高浮点运算速度。

Intel指令集中SSE2(Streaming SIMD Extensions 2,Intel官方称为SIMD 流技术扩展 2或数据流单指令多数据扩展指令集 2)指令集是Intel公司在SSE指令集的基础上发展起来的。相比于SSE,SSE2使用了144个新增指令,扩展了MMX技术和SSE技术,这些指令提高了广大应用程序的运行性能。随MMX技术引进的SIMD整数指令从64位扩展到了128 位,使SIMD整数类型操作的有效执行率成倍提高。双倍精度浮点SIMD指令允许以 SIMD格式同时执行两个浮点操作,提供双倍精度操作支持有助于加速内容创建、财务、工程和科学应用。除SSE2指令之外,最初的SSE指令也得到增强,通过支持多种数据类型(例如,双字和四字)的算术运算,支持灵活并且动态范围更广的计算功能。SSE2指令可让软件开发员极其灵活的实施算法,并在运行诸如MPEG-2、MP3、3D图形等之类的软件时增强性能。Intel是从Willamette核心的Pentium 4开始支持SSE2指令集的,而AMD则是从K8架构的SledgeHammer核心的Opteron开始才支持SSE2指令集的。

Intel指令集中SSE3(Streaming SIMD Extensions 3,Intel官方称为SIMD 流技术扩展 3或数据流单指令多数据扩展指令集 3)指令集是Intel公司在SSE2指令集的基础上发展起来的。相比于SSE2,SSE3在SSE2的基础上又增加了13个额外的SIMD指令。SSE3 中13个新指令的主要目的是改进线程同步和特定应用程序领域,例如媒体和游戏。这些新增指令强化了处理器在浮点转换至整数、复杂算法、视频编码、SIMD浮点寄存器操作以及线程同步等五个方面的表现,最终达到提升多媒体和游戏性能的目的。Intel是从Prescott核心的Pentium 4开始支持SSE3指令集的,而AMD则是从2005年下半年Troy核心的Opteron开始才支持SSE3的。但是需要注意的是,AMD所支持的SSE3与Intel的SSE3并不完全相同,主要是删除了针对Intel超线程技术优化的部分指令。

Intel指令集中SSE4 (Streaming SIMD Extensions 4) 是英特尔自从SSE2之后对ISA扩展指令集最大的一次的升级扩展。新指令集增强了从多媒体应用到高性能计算应用领域的性能,同时还利用一些专用电路实现对于特定应用加速。IntelSSE4 由一套全新指令构成,旨在提升一系列应用程序的性能和能效。Intel SSE4 构建于英特尔64指令集架构(Intel64 ) (ISA)。Intel SSE4 是英特尔与其独立软件开发商 (ISV) 团体精诚合作的成果,它可以支持开发人员轻松改进产品,同时保持必要的应用级兼容性,以适应处理器不断迭代的需求。

2. 并行计算有哪些实现方式?

解:并行计算就是在并行计算或分布式计算机等高性能计算系统上所做的超级计算。实现方式有:单指令多数据流SIMD、对称多处理机SMP、大规模并行处理机MPP、工作站机群COW、分布共享存储DSM多处理机。

3. 彩色图像、灰度图像、二值图像和索引图像区别?

解:彩色图像,每个像素通常是由红(R)、绿(G)、蓝(B)三个分量来表示的,分量介于(0,255)。RGB图像与索引图像一样都可以用来表示彩色图像。与索引图像一样,它分别用红(R)、绿(G)、蓝(B)三原色的组合来表示每个像素的颜色。但与索引图像不同的是,RGB图像每一个像素的颜色值(由RGB三原色表示)直接存放在图像矩阵中,由于每一像素的颜色需由R、G、B三个分量来表示,M、N分别表示图像的行列数,三个M x N的二维矩阵分别表示各个像素的R、G、B三个颜色分量。RGB图像的数据类型一般为8位无符号整形,通常用于表示和存放真彩色图像,当然也可以存放灰度图像。

灰度图像(gray image)是每个像素只有一个采样颜色的图像,这类图像通常显示为从最暗黑色到最亮的白色的灰度,尽管理论上这个采样可以任何颜色的不同深浅,甚至可以是不同亮度上的不同颜色。灰度图像与黑白图像不同,在计算机图像领域中黑白图像只有黑色与白色两种颜色;但是,灰度图像在黑色与白色之间还有许多级的颜色深度。灰度图像经常是在单个电磁波频谱可见光内测量每个像素的亮度得到的,用于显示的灰度图像通常用每个采样像素8位的非线性尺度来保存,这样可以有256级灰度(如果用16位,则有65536级)。

二值图像(binary image),即一幅二值图像的二维矩阵仅由0、1两个值构成,“0”代表黑色,“1”代白色。由于每一像素(矩阵中每一元素)取值仅有0、1两种可能,所以计算机中二值图像的数据类型通常为1个二进制位。二值图像通常用于文字、线条图的扫描识别(OCR)和掩膜图像的存储。

索引图像即它的文件结构比较复杂,除了存放图像的二维矩阵外,还包括一个称之为颜色索引矩阵MAP的二维数组。MAP的大小由存放图像的矩阵元素值域决定,如矩阵元素值域为[0,255],则MAP矩阵的大小为256Ⅹ3,用MAP=[RGB]表示。MAP中每一行的三个元素分别指定该行对应颜色的红、绿、蓝单色值,MAP中每一行对应图像矩阵像素的一个灰度值,如某一像素的灰度值为64,则该像素就与MAP中的第64行建立了映射关系,该像素在屏幕上的实际颜色由第64行的[RGB]组合决定。也就是说,图像在屏幕上显示时,每一像素的颜色由存放在矩阵中该像素的灰度值作为索引通过检索颜色索引矩阵MAP得到。索引图像的数据类型一般为8位无符号整形(int8),相应索引矩阵MAP的大小为256Ⅹ3,因此一般索引图像只能同时显示256种颜色,但通过改变索引矩阵,颜色的类型可以调整。索引图像的数据类型也可采用双精度浮点型(double)。索引图像一般用于存放色彩要求比较简单的图像,如Windows中色彩构成比较简单的壁纸多采用索引图像存放,如果图像的色彩比较复杂,就要用到RGB真彩色图像。

4. 常用边缘检测有哪些算子,各有什么特性?

解:常用边缘检测算子如下所述:

  1. Sobel算子其主要用于边缘检测,在技术上它是以离散型的差分算子,用来运算图像亮度函数的梯度的近似值, Sobel算子是典型的基于一阶导数的边缘检测算子,由于该算子中引入了类似局部平均的运算,因此对噪声具有平滑作用,能很好的消除噪声的影响。Sobel算子对于象素的位置的影响做了加权,与Prewitt算子、Roberts算子相比因此效果更好。Sobel算子包含两组3x3的矩阵,分别为横向及纵向模板,将之与图像作平面卷积,即可分别得出横向及纵向的亮度差分近似值。缺点是Sobel算子并没有将图像的主题与背景严格地区分开来,换言之就是Sobel算子并没有基于图像灰度进行处理,由于Sobel算子并没有严格地模拟人的视觉生理特征,所以提取的图像轮廓有时并不能令人满意。

  2. Isotropic Sobel算子 Sobel算子另一种形式是(Isotropic Sobel)算子,加权平均算子,权值反比于邻点与中心点的距离,当沿不同方向检测边缘时梯度幅度一致,就是通常所说的各向同性Sobel(Isotropic Sobel)算子。模板也有两个,一个是检测水平边沿的 ,另一个是检测垂直平边沿的 。各向同性Sobel算子和普通Sobel算子相比,它的位置加权系数更为准确,在检测不同方向的边沿时梯度的幅度一致。
  3. Roberts算子 罗伯茨算子、Roberts算子是一种最简单的算子,是一种利用局部差分算子寻找边缘的算子,他采用对角线方向相邻两象素之差近似梯度幅值检测边缘。检测垂直边缘的效果好于斜向边缘,定位精度高,对噪声敏感,无法抑制噪声的影响。1963年,Roberts提出了这种寻找边缘的算子。Roberts边缘算子是一个2x2的模板,采用的是对角方向相邻的两个像素之差。从图像处理的实际效果来看,边缘定位较准,对噪声敏感。适用于边缘明显且噪声较少的图像分割。Roberts边缘检测算子是一种利用局部差分算子寻找边缘的算子,Robert算子图像处理后结果边缘不是很平滑。经分析,由于Robert算子通常会在图像边缘附近的区域内产生较宽的响应,故采用上述算子检测的边缘图像常需做细化处理,边缘定位的精度不是很高。
  4. Prewitt算子 Prewitt算子是一种一阶微分算子的边缘检测,利用像素点上下、左右邻点的灰度差,在边缘处达到极值检测边缘,去掉部分伪边缘,对噪声具有平滑作用 。其原理是在图像空间利用两个方向模板与图像进行邻域卷积来完成的,这两个方向模板一个检测水平边缘,一个检测垂直边缘。经典Prewitt算子认为:凡灰度新值大于或等于阈值的像素点都是边缘点。即选择适当的阈值T,若P(i,j)≥T,则(i,j)为边缘点,P(i,j)为边缘图像。这种判定是欠合理的,会造成边缘点的误判,因为许多噪声点的灰度值也很大,而且对于幅值较小的边缘点,其边缘反而丢失了。Prewitt算子对噪声有抑制作用,抑制噪声的原理是通过像素平均,但是像素平均相当于对图像的低通滤波,所以Prewitt算子对边缘的定位不如Roberts算子。因为平均能减少或消除噪声,Prewitt梯度算子法就是先求平均,再求差分来求梯度。该算子与Sobel算子类似,只是权值有所变化,但两者实现起来功能还是有差距的,据经验得知Sobel要比Prewitt更能准确检测图像边缘
  5. Laplacian算子Laplace算子是一种各向同性算子,二阶微分算子,在只关心边缘的位置而不考虑其周围的象素灰度差值时比较合适。Laplace算子对孤立象素的响应要比对边缘或线的响应要更强烈,因此只适用于无噪声图象。存在噪声情况下,使用Laplacian算子检测边缘之前需要先进行低通滤波。所以,通常的分割算法都是把Laplacian算子和平滑算子结合起来生成一个新的模板。拉普拉斯算子也是最简单的各向同性微分算子,具有旋转不变性。一个二维图像函数的拉普拉斯变换是各向同性的二阶导数。拉式算子用来改善因扩散效应的模糊特别有效,因为它符合降制模型。扩散效应是成像过程中经常发生的现象。Laplacian算子一般不以其原始形式用于边缘检测,因为其作为一个二阶导数,Laplacian算子对噪声具有无法接受的敏感性;同时其幅值产生算边缘,这是复杂的分割不希望有的结果;最后Laplacian算子不能检测边缘的方向;所以Laplacian在分割中所起的作用包括:(1)利用它的零交叉性质进行边缘定位;(2)确定一个像素是在一条边缘暗的一面还是亮的一面;一般使用的是高斯型拉普拉斯算子(Laplacian of a Gaussian,LoG),由于二阶导数是线性运算,利用LoG卷积一幅图像与首先使用高斯型平滑函数卷积改图像,然后计算所得结果的拉普拉斯是一样的。所以在LoG公式中使用高斯函数的目的就是对图像进行平滑处理,使用Laplacian算子的目的是提供一幅用零交叉确定边缘位置的图像;图像的平滑处理减少了噪声的影响并且它的主要作用还是抵消由Laplacian算子的二阶导数引起的逐渐增加的噪声影响。
  6. Canny算子Canny算子是一个具有滤波,增强,检测的多阶段的优化算子,在进行处理前,Canny算子先利用高斯平滑滤波器来平滑图像以除去噪声,Canny分割算法采用一阶偏导的有限差分来计算梯度幅值和方向,在处理过程中,Canny算子还将经过一个非极大值抑制的过程,最后Canny算子还采用两个阈值来连接边缘。边缘提取的基本问题是解决增强边缘与抗噪能力间的矛盾,由于图像边缘和噪声在频率域中同是高频分量,简单的微分提取运算同样会增加图像中的噪声,所以一般在微分运算之前应采取适当的平滑滤波,减少噪声的影响。Canny运用严格的数学方法对此问题进行了分析,推导出由# 个指数函数线性组合形式的最佳边缘提取算子网,其算法的实质是用一个准高斯函数作平滑运算,然后以带方向的一阶微分定位导数最大值,Canny算子边缘检测是一种比较实用的边缘检测算子,具有很好的边缘检测性能。Canny边缘检测法利用高斯函数的一阶微分,它能在噪声抑制和边缘检测之间取得较好的平衡。
  7. Laplacian of Gaussian(LoG)算子 利用图像强度二阶导数的零交叉点来求边缘点的算法对噪声十分敏感,所以,希望在边缘增强前滤除噪声.为此,将高斯滤波和拉普拉斯边缘检测结合在一起,形成LoG(Laplacian of Gaussian, LoG)算法,也称之为拉普拉斯高斯算法.LoG边缘检测器的基本特征是: 平滑滤波器是高斯滤波器.增强步骤采用二阶导数(二维拉普拉斯函数).边缘检测判据是二阶导数零交叉点并对应一阶导数的较大峰值.使用线性内插方法在子像素分辨率水平上估计边缘的位置.这种方法的特点是图像首先与高斯滤波器进行卷积,这一步既平滑了图像又降低了噪声,孤立的噪声点和较小的结构组织将被滤除.由于平滑会导致边缘的延展,因此边缘检测器只考虑那些具有局部梯度最大值的点为边缘点.这一点可以用二阶导数的零交叉点来实现.拉普拉斯函数用作二维二阶导数的近似,是因为它是一种无方向算子.为了避免检测出非显著边缘,应选择一阶导数大于某一阈值的零交叉点作为边缘点.

5.  简述BP神经网络,AdBoost的基本原理

解:BP神经网络模型处理信息的基本原理是:输入信号Xi通过中间节点(隐层点)作用于输出节点,经过非线形变换,产生输出信号Yk,网络训练的每个样本包括输入向量X和期望输出量t,网络输出值Y与期望输出值t之间的偏差,通过调整输入节点与隐层节点的联接强度取值Wij和隐层节点与输出节点之间的联接强度Tjk以及阈值,使误差沿梯度方向下降,经过反复学习训练,确定与最小误差相对应的网络参数(权值和阈值),训练即告停止。此时经过训练的神经网络即能对类似样本的输入信息,自行处理输出误差最小的经过非线形转换的信息。

AdBoost是一个广泛使用的BOOSTING算法,其中训练集上依次训练弱分类器,每次下一个弱分类器是在训练样本的不同权重集合上训练。权重是由每个样本分类的难度确定的。分类的难度是通过分类器的输出估计的。

C/C++部分

1.  关键字static的作用是什么?

解:1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。2)在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数,它是一个本地的全局变量。3)在模块内,一个被声明为静态的函数只可被这一模块的它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

2. 嵌入式系统总是用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一个设置a的bit3,第二消除a的 bit 3。在以上两个操作中,要保持其它位不变.

解:

#define BIT3(0x1<<3)
static int a;
void set_bit3(void)
{
   a|=BIT3;
}
void clear_bits(void)
{
    a&=~BIT3;
}

3. 简述C,C++程序编译的内存分配情况?

解:C,C++中内存分配方式可以分为三种:

  1. 从静态存储区域分配:内存在程序编译时就已经分配好,这块内存在程序的整个运行期间都存在。速度快,不容易出错,因有系统自行管理。
  2. 在栈上分配:在执行函数时,函数内局部变量的存储单元都在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
  3. 从堆上分配:即运态内存分配。程序在运行时候用malloc或new申请任意大小的内存,程序员自己负责在何进用free 和delete释放内存。

一个C、C++程序编译时内存分为5大存储区:堆区、栈区、全局区、文字常量区和程序代码区。

4. 给定一个矩阵intmaxtrixA[m][n],每行和每列都是增序的,实现一个算法去找矩阵中的某个元素element.

解:方法一:

#include<iostream>  
using namespace std;  
  
const int M = 4;  
const int N = 4;  
int main  
{  
    int matrix[M][N] = {};  
    double element;  
      
    int flag = 1;  
    for(int j=0; j<N; j++)  
    {  
        if(matrix[i][j] == element)  
            cout<<"位置"<<endl;  
        while( flag<M && matrix[i][j]<element )  
            --flag;  
        while( flag<M && matrix[i][j]>element )  
            ++flag;  
    }   
}  
方法二:

bool Find(int *matrixA, int m, int n, int element)  
{  
    bool found = false;  
    if(matrixA != NULL & m & n)  
    {  
        int i,j;  
        i=0;j=n-1;  
        while(i<m;j>=0)  
        {  
            if(maxtrixA[i*n+j] == element)  
            {  
                found = true;  
                break;  
            }  
            else if(matrix[i*n+j]>element  
                --j;  
            else  
                ++i  
        }  
    }  
} 

5. 从1到500的500个数,第一次删除奇数位,第二次删除剩下来的奇数位,以此类推,最后剩下的唯一一位数是什么?

解:比如:1,2,删除奇数位,那剩下的是2,1,2,3,删除奇数位,剩下的是2,1,2,3,4,剩下的是4,1,2,3,4,5,6,7,剩下的是4,1,2,3,4,5,6,7,8 和1,2,3,4,5,6,7,8,9,10,11,12,13,14,15剩下的是8,这里有什么规律:就是当1~n,2^i<n<2^(i+1)时候,这样删除剩下的是2^i。2^8<500<2^9,所以剩下的就是2^8=256。

6. 给出了一个n*n的矩形,编程求从左上角到右下角的路径数(n > =2),限制只能向右或向下移动,不能回退。例如当n=2时,有6条路径

解:一是利用数学知识,从左上角到右下角总共要走2n步,其中横向要走n步,所以总共就是C2n~n。二是利用递归实现

int getTotalPath(int m, int n)  
{  
    if(m == 1)  
        return n + 1;   
    if(n == 1)  
        return m + 1;   
    return getTotalPath(m-1, n) + getTotalPath(m, n-1);  
}  

7. 给出一棵二叉树的前序和中序遍历,输出后续遍历的结果,假设二叉树中存储的均是ASCII码。如前序:ABDHECFG,中序:HDBEAFCG,则输出后序为:HDECFGCA,改正为:HDECFGBA,再次改正HDEBFGCA

解:先利用前序和中序构建出二叉树,然后后序遍历输出结果

Node* getBinaryTree(char* preOrder, char* inOrder, int len)  
{  
    if(preOrder == NULL || *preOrder == '\0' || len<=0)  
        return NULL;  
  
    Node* root = (Node*) malloc(sizeof(Node));  
    if(root == NULL)  
        exit(EXIT_FAILURE);  
   
    root->data = *preOrder;  
  
    int pos = 0; 
    while(true)  
    {  
        if(*(inOrder+pos) == root->data)  
            break;  
        pos++;  
    }  
  
    if(pos == 0)  
        root->lchild = NULL;  
    else  
        root->lchild = getBinaryTree(preOrder+1, inOrder, pos);  
  
    if(len-pos-1 == 0)  
        root->rchild = NULL;  
    else  
        root->rchild = getBinaryTree(preOrder+pos+1, inOrder+pos+1,len-pos-1);  
    return root;  
}  
   
void postOrder(Node* root)  
{  
    if(root == NULL)  
        return;  
  
    postOrder(root->lchild);  
    postOrder(root->rchild);  
    printf("%c", root->data);  
}  
  
void printPostOrderViaPreOrderAndInorder(char* preOrder, char* inOrder)  
{  
    Node* root = getBinaryTree(preOrder, inOrder, strlen(preOrder));  
    postOrder(root);  
}  

8.自定义实现字符串转为整数的算法,例如把“123456”转成整数123456.(输入中可能存在符号,和数字)

解:

enum Status {VALID,IN_VALID};  
int gStatus = VALID;  
  
int strToInt(const char* str)  
{  
    long long result = 0;  
    gStatus = IN_VALID;   
    if(str != NULL) {  
        const char* digit = str;  
  
        bool minus = false;  
  
        if(*digit == '+')  
            digit++;  
        else if(*digit == '-'){  
            digit++;  
            minus = true;  
        }  
  
        while(*digit != '\0') {  
            if(*digit >= '0' && *digit <= '9'){  
                result = result * 10 + (*digit -'0');   
                if(result > std::numeric_limits<int>::max()){  
                    result = 0;  
                    break;  
                }  
                digit++;  
            }  
   
            else {  
                result = 0;  
                break;  
            }  
        }  
  
        if(*digit == '\0'){  
            gStatus = VALID;  
            if(minus)  
                result = 0 - result;  
        }  
    }   
    return static_cast<int>(result);  
}  

9. 求2个字符串最长公共部分

解:方法一:2个循环,遍历2个字符串,这里记得要回溯,2个字符串都要有,否则,找不出来正确的结果

int main(){
	char a[80],b[80],c[80],d[500][80]; 
	memset(c, 0, 80);
	int i,j,m,n,t,k; 
	gets(a); 
	gets(b);//输入字符串 
	puts(a); 
	puts(b);//输出字符串 
	m=strlen(a); 
	n=strlen(b);//计算字符串实际长度 

	int nFlag = 0;
	k = 0;
	int nTemp = 0;
	
	for(i=0;i<m;i++){
		nFlag = 0;
		t=0; 
		nTemp = i;
		for(j=0;j<n;j++){        
			if(a[i]==b[j]){      
				nFlag = 1;           
				c[t]=a[i];            
				t=t+1;           
				i=i+1;            
				//j=j+1;        
			}
			else{
				if (nFlag == 1){
					strcpy(d[k++], c);
					memset(c, 0, 80);
					nFlag = 0;
					t=0;
					i = nTemp;
					j=j-1;
				}
				
			}
		} 
		if (nFlag == 1){
			strcpy(d[k++], c);
			memset(c, 0, 80);
			i = nTemp;
		}
	}
	strcpy(c,d[0]);
	for(i=1;i<k-1;i++){       
		if(strlen(c)<strlen(d[i]))            
			strcpy(c,d[i]);//如果d[i]字符串长度比c长就把d[i]中的字符串复制给c    
	}    
	puts(c); 
	return 0; 
}
方法二

//最长公共子序列字符个数 
//c[i][j]保存字符串 {xi},{yj},(长度分别为i,j)的最长公共子序列的字符个数 
//i=0或者是j=0 时,c[i][j]必定为零, i,j>=0 且 xi=yj, c[i][j]=c[i-1][j-1]+1 
//若 i,j>0 但xi!=yj, c[i][j]=max{ c[i-1][j] , c[i][j-1] } 
#include <stdio.h> 
#include <string.h> 
int c[1001][1001]; 
void lcs(int a, int b, char x[], char y[], int c[][1001]){     
    int i,j;     
    for(i=1;i<a;i++)         
        c[i][0]=0; //if b==0, set c[i][j]=0;     
    for(i=1;i<b;i++)         
        c[0][i]=0; // if a==0; set c[i][j]=0;     
    for(i=1;i<=a;i++) // if a!=0,b!=0 loop         
        for(j=1;j<=b;j++){             
            if(x[i-1]==y[j-1])                 
                c[i][j]=c[i-1][j-1]+1;             
            else if (c[i-1][j]>=c[i][j-1])                 
                c[i][j]=c[i-1][j];             
            else                
                c[i][j]=c[i][j-1];         
         } 
} 

int main() {     
    char x[1001],y[1001];     
    while ( scanf("%s%s",x,y)!=EOF ){         
        int a=strlen(x);         
        int b=strlen(y);         
        memset(c,0,sizeof(c));
        lcs(a,b,x,y,c);
        printf("%d\n",c[a][b]);
    }    
    return 0; 
}

10.请实现一个函数:最长顺子。输入很多个整数(1<=数值<=13),返回其中可能组成的最长的一个顺子(顺子中数的个数代表顺的长度); 其中数字1也可以代表14;

顺子包括单顺\双顺\3顺;单顺的定义是连续5个及以上连续的数,比如1,2,3,4,5、3,4,5,6,7,8和10,11,12,13,1等; 双顺的定义是连续3个及以上连续的对(对:两个相同的数被称为对),比如1,1,2,2,3,3、4,4,5,5,6,6,7,7和11,11,12,12,13,13,1,1等; 3顺的定义是连续2个及以上连续的3张(3张:3个相同的数被称为3张),比如1,1,1,2,2,2、3,3,3,4,4,4,5,5,5,6,6,6和13,13,13,1,1,1等等;比如:输入数组[1,5,2,3,4,4,5,9,6,7,2,3,3,4], 输出数组[2,2,3,3,4,4,5,5]。

解:实现代码如下:

int putList(int k, map<int, map<int, int>* >& listData, map<int, int>* mapData){  
	int nFlag =0;
	if (0 == k && mapData->size() >= 5){  
		nFlag =1;
		//listData.put(mapData.size(), mapData);  
		listData.insert(pair <int, map<int, int>* >( mapData->size(), mapData));
	}  
	if (1 == k && mapData->size() >= 3){  
		nFlag =1;
		//listData.put(2 * mapData.size(), mapData);  
		listData.insert(pair <int,  map<int, int>* >(2* mapData->size(), mapData));
	}  
	if (2 == k && mapData->size() >= 2){  
		nFlag =1 ;
		//listData.put(3 * mapData.size(), mapData); 
		listData.insert(pair <int, map<int, int>* >( 3*mapData->size(), mapData));
	}  
	return nFlag;
}

map<int, int>* getList(int* count, int k, int num, int& nMaxCount){  
	map<int, map<int, int>* > listData;//= new map<int, map<int, int>*>();  
	map<int, int>* mapTemp = NULL;  

	int flag = 0;  

	int nRet = 0;
	for (int i = 1; i < num; i++){  
		if (count[i] > k && flag == 0){  
			flag = 1;  
			mapTemp = new map<int, int>;
			//mapTemp.put(i, count[i]);  
			mapTemp->insert(pair <int, int>( i, count[i]));
		} 
		else if (count[i] > k && flag == 1) {  
			//mapTemp.put(i, count[i]);  
			mapTemp->insert(pair <int, int>( i, count[i]));
			if (13 == i){  
				if (count[14 - i] > k) {  
					//mapTemp.put(14 - i, count[14 - i]); 
					mapTemp->insert(pair <int, int>( 14 - i, count[14 - i]));
					nRet = putList(k, listData, mapTemp); 
					
					if (nRet==0){
						delete mapTemp;
						mapTemp = NULL;
					}
				} 
				else {  
					flag = 0;  
					nRet=putList(k, listData, mapTemp);  
					if (nRet==0){
						delete mapTemp;
						mapTemp = NULL;
					}
				}  
			}  
		} 
		else if (count[i] <= k && flag == 1){  
			flag = 0;  
			nRet=putList(k, listData, mapTemp);  
			if (nRet==0){
				delete mapTemp;
				mapTemp = NULL;
			}
		}  
	} 
	
	if (listData.size() > 0){
		listData.rend();
		map<int, map<int, int>* >::iterator it = listData.begin();

		nMaxCount = (*it).first;
		map<int, int>* mapReturn = (*it).second;
		map<int, int>* maptemp;

		it++;

		for (; it!=listData.end(); it++){
			maptemp = (*it).second;
			delete maptemp;
			maptemp = NULL;
		}
		return mapReturn;
	}
	else  
		return NULL;  
}  

int* GetLongeststr(int* array, int nCount, int& outCount, int MaxNum){
	int* count = new int[MaxNum+1];   
	memset(count, 0, MaxNum*sizeof(int));

	int nMaxLoop=0;
	int nMaxTemp;
	int nMax1Loop=0;
	int nMax2Loop=0;
	int nMax3Loop=0;
	int nMaxkey =0;

	for (int i = 0; i < nCount; i++){  
		if (array[i] < 1 || array[i] > MaxNum)   
			return NULL;  
		++count[array[i]];
	}

	map<int, map<int, int>*> allList;
	map<int, int>* mapTemp = NULL;  
	map<int, int>* map1Temp = NULL;
	map<int, int>* map2Temp = NULL;
	map<int, int>* map3Temp = NULL;
	for (int k = 0; k < 3; k++){  
		mapTemp = getList(count, k, MaxNum, nMaxTemp);
		if (NULL != mapTemp){  
			if (0 == k) {
				map1Temp = mapTemp;
				nMax1Loop = nMaxTemp;
				nMaxLoop=nMaxTemp;
			}
			else if (1 == k){
				if (nMaxTemp>=nMaxLoop)	{
					map2Temp = mapTemp;
					nMax2Loop = nMaxTemp;
					nMaxLoop = nMaxTemp;
				}
				else{
					delete mapTemp;
					mapTemp =NULL;
				}
			}
			else{
				if (nMaxTemp>=nMaxLoop)	{
					map3Temp = mapTemp;
					nMax3Loop = nMaxTemp;
					nMaxLoop = nMaxTemp;
				}
				else{
					delete mapTemp;
					mapTemp =NULL;
				}
			}
		}  
	}

	delete[] count;
	count = NULL;

	if (nMaxLoop>0)	{
		if (nMaxLoop == nMax3Loop){
			nMaxkey = 3;
			mapTemp = map3Temp;
		}
		else if (nMaxLoop == nMax2Loop)	{
			nMaxkey = 2;
			mapTemp = map2Temp;
		}
		else{
			nMaxkey = 1;
			mapTemp = map1Temp;
		}

		outCount = nMaxLoop;
		int* result = new int[outCount]; 
		int k; 
		int nAllCount = 0;
		
		map<int, int>::iterator itorResult;
		for (itorResult = mapTemp->begin(); itorResult!=mapTemp->end(); itorResult++){
			k = itorResult->first;
			for (int j =0; j<nMaxkey; j++){				
				result[nAllCount++] = k;				
				cout << itorResult->first <<",";
			}
		}
		cout << endl;

		if (map1Temp!=NULL){
			delete map1Temp;
			map1Temp = NULL;
		}
		if (map2Temp!=NULL){
			delete map2Temp;
			map2Temp = NULL;
		}
		if (map3Temp!=NULL){
			delete map3Temp;
			map3Temp = NULL;
		}
		return result;
	}
	else {
		outCount = 0;
		return NULL;
	}
} 


关于Image Engineering & Computer Vision的更多讨论与交流,敬请关注本博和新浪微博songzi_tea.


已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付 9.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值