数据结构与算法(Java版) | 就让我们来看看几个实际编程中遇到的问题吧!

Posted 李阿昀

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构与算法(Java版) | 就让我们来看看几个实际编程中遇到的问题吧!相关的知识,希望对你有一定的参考价值。

上一讲,我给大家简单介绍了一下数据结构,以及数据结构与算法之间的关系,照理来说,接下来我就应该要给大家详细介绍线性结构和非线性结构了,但是在此之前,我决定还是先带着大家看几个实际编程中遇到的问题,看完之后咱们再说。

第一个问题:一个有关单链表的面试题

如下所示,有这样一段Java代码,我想这段Java代码大家应该都能看得懂吧!无非就是使用字符串类的replaceAll方法将指定字符串中的Java子串全部替换成了李阿昀~,So Easy!replaceAll方法大家应该都见过吧!它就是用来做字符串替换的。

public static void main(String[] args) 
    String str = "Java, Java, hello, world!";
    String newStr = str.replaceAll("Java", "李阿昀~"); // 算法
    System.out.println("newStr = " + newStr);

那么,我现在就要问大家了,replaceAll方法的提供者在进行字符串替换时,它到底是怎么做的呢?想都不用想,里面必定会有一个算法来做支撑,而且这个算法你还要能看得懂,否则,坏事势必就会频发,你想啊,我们去开发一个程序,如果你连别人底层的代码都看不懂,那么你自己去优化程序不就是成无稽之谈了嘛!

总之,replaceAll方法内部有一个算法,而且它是专门来研究字符串是如何进行替换的。

接下来,我们不妨来看一个有关单链表的面试题。

试写出用单链表表示的字符串类及字符串结点类的定义,并依次实现它的构造函数,以及计算串长度、串赋值、判断两串相等、求子串、两串连接、求子串在串中位置等7个成员函数。

注意,以上题目中有个函数的概念,而它其实就是我们Java里面的方法。

想必大家在大学里面学数据结构时,老师一定给你们布置过很多的练习题吧!而这其中不乏就有关于链表的练习题,而且初看还具有一定难度,例如以上那道有关单链表的面试题,你应该遇到过这样的练习题吧,要是没遇到过,那我想你的数据结构一定没学好,因为学数据结构你不大量的练习,你怎么学得好啊!

还是说回来上面那道有关单链表的面试题,其实这道面试题要求很明确,就是用单链表来实现字符串类的相关功能,而单链表正是数据结构中的一种,显然,如果你没有学过单链表这种数据结构,那么这道面试题明显你就完成不了,总之,这道面试题必须建立在大家对单链表了解的基础上才能完成得了。

第二个问题:一个五子棋程序

接下来,我们来看一个比较经典的、同学们也玩过的游戏,即五子棋程序。

从上图中,能看到一个五子棋程序最基本的要求有:

  1. 判断游戏的输赢,不管是黑子赢也好,还是蓝子赢也好。

    相信大家都知道五子棋游戏输赢的规则吧!很简单,就是看哪一方先把5颗子下得粘成一串,谁先就谁赢。于是,你是不是得要写个算法来判断游戏输赢啊!

  2. 存盘退出。

  3. 继续上局。

  4. 悔棋。

    就是这一步我不满意,我要悔一步棋。

大家想一下,如果这个五子棋程序让你来实现,那么你会怎么去做呢?要不我先来说说我的分析吧!

首先,我们需要把五子棋盘映射成一个二维数组,这样当别人每下一个子的时候,便会有一个元素来记录所下的子到底是一颗黑子,还是一颗蓝子了;然后,将二维数组写入磁盘文件,这样我们就可完成存档退出的功能了;接着,从磁盘读取文件,读取文件过后,重新将其恢复成原先的二维数组,当把这个二维数组再次恢复成我们棋盘的形式时,我们也就完成了续上局的功能。

可想而知,如果你没有学过二维数组这种数据结构,那么你怎么可能会写出这样一个五子棋程序呢,必然是写不来的,对吧!

而且,要想能写出性能优秀的五子棋程序,你还得考虑一个问题,什么问题呢?就是压缩问题。你看啊,在上面我们那个棋盘里,其实我们只放了5个棋子,但是整个棋盘却很大,故这必然会导致所映射成的二维数组里面将记录很多没有意义的数据,即默认值0,也即白白浪费掉了那些存储空间。于是,大家可能就会想了,能不能对二维数组进行压缩啊,就只让它记录有意义的数据?其实这是可以的,因为二维数组里面有一种数组就可以实现压缩和解压的效果,它便是稀疏数组。

有了稀疏数组这种数据结构之后,五子棋程序中的存档退出、续上局的功能就得像下面这样去实现了,即:

  • 存档退出:首先,我们需要把五子棋盘映射成一个二维数组,这样当别人每下一个子的时候,便会有一个元素来记录所下的子到底是一颗黑子,还是一颗蓝子了;然后,将二维数组转成稀疏数组(即压缩);接着再写入磁盘文件,这样我们就完成了存档退出的功能。
  • 续上局:首先,从磁盘读取文件,注意,这时读取出来的可是一个稀疏数组哟;然后,将读取出来的稀疏数组转成原先的二维数组(即解压),当把这个二维数组再次恢复成我们棋盘的形式时,我们也就完成了续上局的功能。

大家试想一下,如果你没有学过稀疏数组这种数据结构,那么你怎么可能会写出这样一个五子棋程序呢,必然是写不来的,对吧!就算能写,你最多也就只能做到将五子棋盘映射成一个二维数组然后再写入磁盘文件这一步了,但你要是学过稀疏数组,那情况可就大不同了,你是可以对程序进行优化的,说得直白点,就是可以使用较少的数据来保存棋盘。

嘻嘻😁,数组这种数据结构的重要性是不是就这样被凸显出来了啊!

第三个问题:约瑟夫(Josephu)问题(丢手帕问题)

接下来,我们再来看一个非常经典的案例,即约瑟夫问题,当然,它也被叫作丢手帕问题。

我依稀还记得,当年我大学刚毕业找工作时还做过这样一个面试题,回头一看,那已经是2014年6月份的事情了,那时候我大学刚毕业,还很年轻,而今离毕业都快9年了,真是岁月如梭啊,都说时间是把杀猪刀,可我怎么依旧还是这么的帅气呢,哈哈哈😂,有点恬不知耻了,主要是我还没秃头啊!

思绪还是回到我大学刚毕业找工作那会啊,我记得当时遇到的那道面试题,即约瑟夫问题,是被要求要在Unix环境下用C语言写代码来解决的,而且,我还记得我有用到环形链表,因为那个时候人们都知道约瑟夫问题得用环形链表来解决。

下面,我就来给大家简单讲讲这个约瑟夫问题吧!

约瑟夫问题很简单,它是这样描述的:设编号为1,2,···,n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,他的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,此时便能由此产生一个出队编号的序列了,请问这个出队编号的序列是什么?

经过我上面的介绍,现在大家该知道非常之经典的约瑟夫问题到底是一个什么问题了吧!知道之后,接下来我们就要分析约瑟夫问题该怎么去解决了。

这里,我就给大家直说吧!我们可以用一个不带头结点的循环链表来处理约瑟夫问题,即:先构成一个有n个结点的单循环链表(单循环链表也叫单向环形链表),然后由k结点起从1开始计数,计到m时,对应结点从链表中删除,接着再从被删除结点的下一个结点又从1开始计数,直到最后一个结点从链表中删除,算法即结束。

注意,单向环形链表是我们后面要讲的一个重点,所以大家到时候可一定要认真学哟,不然的话,你怎么使用它来解决约瑟夫问题啊,你还想不想知道最终的出队编号序列了!

总之,如果你有学过单向环形链表这种数据结构,那么恭喜你,你就能使用这种数据结构来解决约瑟夫问题了,要知道使用单向环形链表这种数据结构来解决约瑟夫问题可是非常形象的哟;如果你没有学过这种数据结构,那么很可惜,你就只能退而求其次使用数组来解决了,说实话,这样做还是有些困难的。

其他常见算法问题

最后,我们再来看一些其他常见算法问题吧!

这儿我罗列出来了如下这样一些问题,大家看完是不是感觉异常熟悉啊,这不就是我们在面试的时候经常遇到的面试题嘛!

  • 磁盘问题。
  • 公交车。
  • 画图。
  • 矩阵中查找单词路径数。
  • 球和篮子。
  • 修路问题。
  • 最短路径问题。
  • 汉诺塔游戏。
  • 八皇后问题。

我想,有经验的同学应该都知道以上这些问题具体都会用到哪些算法吧!就拿修路问题来说,它实质上就是一个求最小生成树的问题(注意,这个最小生成树一般是加权值的),因此该问题就需要用到普里姆算法;对于最短路径问题来说,它需要用到什么算法呢?弗洛伊德算法,用弗洛伊德算法即可解决最短路径问题;那对于汉诺塔游戏来说呢,它又需要用到什么算法呢?汉诺塔游戏,我之前应该有给大家详细介绍过吧,既然介绍过了,那大家就应该知道它需要用到分治算法;那对于八皇后问题来说呢,它又需要用到什么算法呢?我不说,想必大家也已经知道了,它需要用到回溯算法。

给大家说清楚以上问题各自需要用到的算法之后,接下来有一点我还得给大家重点阐述一下。

就是,以上这些问题,有些用数据结构就能搞定,例如上面讲到的五子棋程序,它用到的算法就非常简单,你自己写都可以写出来,所以你只要学一个数组这样的数据结构其实基本上就可以搞定了,还有约瑟夫问题,同样也不需要你专门学什么算法就能搞定,因为它仅仅只须使用单向环形链表就可以搞定了;然而,有些则是需要数据结构加算法综合起来才能搞得定的,显然,后者对我们的要求更高,因为除了学数据结构之外我们还得学算法。

当然,不得不说,大部分问题往往都是需要数据结构加算法综合起来才能搞得定的。例如修路问题(或者最小生成树问题),如果你要想解决它,那么你得先学会树这种数据结构才行,但是你光会树这种数据结构又不顶事,因为你还得学普里姆算法,只有这样你才能解决这个修路问题(或者最小生成树问题);再比如最短路径问题,你光会图这种数据结构是不行的,你还得会弗洛伊德算法;至于汉诺塔游戏嘛,上面我们也说了,它需要用到分治算法,但除此之外,它还需要用到递归这种调用机制。

说这么多,我无非就是想说,有些问题我们用数据结构就能搞定,而有些问题则是需要数据结构加算法综合起来才能搞得定的,且往往大部分问题都是这样!

以上就是几个实际编程中遇到的问题所引出的一些讨论,希望大家看完能对数据结构与算法之间的区别及联系有一个比较清晰的认识!至于数据结构所包括的内容嘛,即线性结构和非线性结构,那就留到下一讲再来给大家进行详细介绍吧,嘻嘻😂!

以上是关于数据结构与算法(Java版) | 就让我们来看看几个实际编程中遇到的问题吧!的主要内容,如果未能解决你的问题,请参考以下文章

数据结构与算法(Java版) | 数据结构与算法的重要性

数据结构与算法(Java版) | 本套系列教程的内容介绍和授课方式

比较图片相似度算法介绍与应用(Java版)

比较图片相似度算法介绍与应用(Java版)

比较图片相似度算法介绍与应用(Java版)

初识广度优先搜索与解题套路