【3 编程语言】3.1 数据结构与算法(上)

3.1 数据结构与算法

【考点讲解】

数据结构和算法是面试中的一个难点,需要求职者具备较强的逻辑能力和编程能力。
很多编程基础薄弱的求职者甚至“谈算法色变”,在这个手撕代码的环节败下阵来。
其实有部分记忆力比较强的同学,可能之前没有专门学习过数据结构和算法,但通过面试之前疯狂刷leetcode,把常考的题目背下来,在面试中也能“碰巧”通过,但这样的复习方式,其实笔者是不太认可的。一来,单纯的背题目,并不能真正的掌握数据结构和算法,就算碰巧通过面试,到了工作中,阅读代码时,还是很难理解别人在代码中运用到的数据结构和算法的设计巧思;二来,假设在面试中,你遇到背过的题目,虽然可以把代码快速的写出来,但假如面试官一问你代码的思路,就很容易露怯,一旦答不上来,就会很尴尬。
所以,建议大家系统的去学习一下数据结构和算法,并且至少要掌握一门编程语言。当有一定基础后,再去leetcode刷算法题,再次重申,这里的刷题不是去背答案,而是先经过自己的思索,尝试自己去解题,实在不会的,再去看题解,然后反复练习,归纳每种数据结构、每种算法的使用场景,这样才能以不变应万变,就算面试官出了没见过的新题,也能运用合适的数据结构和算法进行解题。
另外,需要注意的是:一般来说,编程题(算法题),在面试环节中只能算得上是必须但不是最重要的,只是众多考核方面中的其中一个,不一定就直接决定求职者的成败,但可以确定的是,能够在这个环节回答出色,必然会成为面试的加分项。
新手在解答数据结构和算法题时的一些注意事项:
  • 面试中假如遇到编程题,不要一上来就盲目作答,首先应该弄清题意,和面试官确认好限定条件,比如:排序算法,如果面试官要求处理的数据量比较大,并且要求空间复杂度比较低,那么你就不应该用计数排序去作答,因为计数排序会占用更多的空间资源。
  • 题意清晰之后,如果还是没有头绪,可以先采用归纳法寻找解题规律,找到规律后,再组织成代码;如果涉及大数据量的问题,应当优先想到分治法去解题,把大数据量拆分成一个一个小单元去处理,最后再合并结果。
  • 如果编程能力比较薄弱,真的无法完成题目,可以尝试去设计一下该编程题的测试用例,把输入参数和预期结果列举出来。如果可以把编程题的解题思路讲清晰,就算没有真正能够写出完整的代码,也总比吭哧吭哧想半天之后,写出来的代码错漏百出的强。
当你有一定数据结构和算法的基础后,你在解题时,可以遵循四大解题步骤去完成算法题:
  1. 模拟:尝试去模拟题目运行。
  2. 规律:通过模拟运行,找出题目的规律和解题突破口。
  3. 匹配:找到合适的数据结构与算法去进行套用解题。
  4. 边界值:考虑到边界情况,避免程序出Bug。
常考的数据结构有:链表、数组、树、堆、栈、队列、向量、哈希表等。
常考的算法有:递归、二分查找、排序、树的操作、图论、Hash算法、分治法、动态规划等。
此外还需要掌握时间复杂度和空间复杂度,一般空间不足可以加大运力,但是时间不足将成为程序运行的硬伤。所以我们一般更注重时间复杂度,大家应该掌握时间复杂度的计算方式(大O表示法)。
数据结构和算法在面试中常考的考点有:
  • 数组
  • 链表
  • 字符串
  • 二叉树
  • 哈希表
  • 贪心算法
  • 动态规划
  • 双指针迭代
  • 搜索
  • 排序
  • 分治法
  • 递归
  • 循环

【例题示例】

3.1.1 数组和链表的区别?

【考点映射】
  • 数组
  • 链表
【出现频度】★★★★☆
【难度】★★☆

【参考答案】
数组:
数组中的元素在内存中是连续存放的,每个元素占用内存相同,可以通过下标迅速访问数组中任何元素。如果要在数组中增加一个元素,需要移动大量元素,在内存中空出一个元素的空间,然后将要增加的元素放在其中。同样的道理,如果想删除一个元素,同样需要移动大量元素去填掉被移动的元素。如果应用需要快速访问数据,很少或不插入和删除元素,就应该用数组。
数组操作的时间复杂度:
假如数组的长度为 n。
访问:O(1) (访问特定位置的元素)
插入:O(n )(最坏的情况发生在插入发生在数组的首部并需要移动所有元素时)
删除:O(n) (最坏的情况发生在删除数组的开头发生并需要移动第一元素后面所有的元素时)
链表
链表恰好相反,链表中的元素在内存中不是顺序存储的,而是通过存在元素中的指针联系到一起。比如:上一个元素有个指针指到下一个元素,以此类推,直到最后一个元素。如果要访问链表中一个元素,需要从第一个元素开始,一直找到需要的元素位置。但是增加和删除一个元素对于链表数据结构就非常简单了,只要修改元素中的指针就可以了。如果应用需要经常插入和删除元素你就需要用链表数据结构了。
对比
  • 数组静态分配内存,链表动态分配内存;
  • 数组在内存中连续,链表不连续;
  • 数组元素在栈区,链表元素在堆区;
  • 数组利用下标定位,时间复杂度为O(1),链表定位元素时间复杂度O(n);
  • 数组插入或删除元素的时间复杂度O(n),链表的时间复杂度O(1)。


3.1.2 如何反转链表?

【考点映射】
  • 链表
【出现频度】★★★☆
【难度】★★★
【题目描述】
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
(leetcode 206题)

【参考答案】
思路分析:
在解这道链表反转的题目时,不要被链表存储的数据所迷惑,不要纠结链表的一个节点,当前存储的是什么值,这样容易把自己给绕糊涂了。其实这一道题,我们更需要关心的是链表的指针。
另外,我们在leetcode上刷题的时候,leetcode是不给我们提供测试用例的验证过程的,这让很多初学算法的求职者很困惑,看了leetcode官方题解的示例代码,拿到自己的IDE工具上,却不知道该如何把算法跑起来。
其实,我们在解算法题的时候,考察的更多是求职者的思路,有时候甚至在面试过程中,写出来的也是一些伪代码,如果面试中还要考察代码的运行情况,那么写出来的代码就太多了,你需要自己去构造一个节点类,还需要去构造一个链表类,还要去写一个链表反转的测试类,这样下来,其实是非常没有必要的,写了很多代码,但是解题的关键代码却很少。
所以,我们在面试中解算法题的时候,我们只要把自己的算法思路写正确,没有特别明显的逻辑漏洞和语法漏洞,基本上就可以得分了。然后在leetcode刷题过程中,要求就是代码能够通过leetcode的测试即可。
题归正转,反转链表的解法主要有两种:双指针迭代法递归法。

(1)双指针迭代法

双指针迭代法的思路就是:遍历链表,依次把当前链表的指针指向前一个指针,然后把当前节点的前一个指针再指向当前节点的下一个节点。由于是单链表,没有引用其前一个节点,所以必须事先用一个变量存储其前一个节点。在更改引用之前,还要存储后一个节点,最后返回新的头引用。
解算法可以分为4个步骤:模拟、规律、匹配和边界。
第一步、模拟:

第二步、规律:
本质上就是让当前节点的后继指针指向前驱节点(用pre变量存储),而前驱节点(pre)的后继指针指向当前节点的后继节点(next),最终再返回遍历所有节点后的头节点的引用即可完成。
第三步、匹配:
采用双指针迭代算法。
第四步、边界值:
当链表只有一个节点(next指针指向null),或者链表为空,则无需进行遍历操作。
当分析完成后,我们就可以很容易写出代码(Java):
/**
 * Definition for singly-linked list.
 * 题目已经帮我们构造好了一个链表节点定义
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        // 边界值判断:当链表只有一个节点,或者链表为空,无需反转链表
        if (head == null || head.next == null){
            return head
        }
        // 定义一个pre变量,用于存储当前节点的前驱节点
        ListNode pre = null;
        // 定义一个cur变量,用于存储当前节点,链表的第一个节点是传入的 head 节点。
        ListNode cur = head;

        while (cur != null) {
            // next 变量存储当前节点的后继节点
            ListNode next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        head = pre
        return head;
    }
}
双指针迭代法的算法复杂度:
  • 时间复杂度:O(n),其中n是链表的长度。需要遍历链表一次。
  • 空间复杂度:O(1)。

(2)递归法

使用递归法解题会更加简洁,但是我们需要特别注意递归的终止条件。
模拟:
递归就好比是俄罗斯套娃,我们假设已经有了一个反转链表的方法,然后丢入一个启动节点,比如:节点1让其反转。接下来,又把节点1的后继节点——节点2,丢入反转链表的方法里,让节点2也进行反转...依次类推,我们最终把最后一个节点——节点5也进行反转,再把最后一个节点当作新的头引用返回。这样就可以达到反转链表的目的。但值得注意的是,反转完毕后,第一个节点要指向null,否则节点就会有环,递归将无止境进行下去。
代码(Java):
class Solution {
    public ListNode reverseList(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode newHead = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return newHead;
    }
}
递归法的算法复杂度:
  • 时间复杂度:O(n),其中 n 是链表的长度。需要对链表的每个节点进行反转操作。
  • 空间复杂度:O(n),其中 n 是链表的长度。空间复杂度主要取决于递归调用的栈空间,最多为 n 层。

【知识点延伸】
链表(Linked List)是一种常见的基础数据结构,是一种线性表。物理存储结构上是非连续、非顺序的存储结构。
链表分为单链表和多链表,
单链表的特点就是:一个节点包含数据和 next 指针两个部分,next 指针指向下一个节点;
双链表的特点就是,一个节点不仅包含数据和 next 指针,还包含 pre 指针。 pre 指针指向前一个节点。
我们在面试中,遇到的大多数都是单链表,所以大家在学习链表的时候,应该以了解单链表的特性和常用操作为主。
学习一种数据结构,要对该数据结构的常用操作要有所了解,需要懂得推算常用操作的时间复杂度。
另外,数据结构一般要和编程语言相结合,我们能够轻松的用编程语言把数据结构给表达出来,然后也知道你所熟悉的编程语言,是如何操作该数据结构的。比如,下面是链表的常用操作,大家应该能够用编程语言去表达出来,因为在面试过程中,关于链表的题型,其实本质上就是在操作链表。
以下题目也是链表的常考题,大家下来可以上牛客题霸去多多练习


3.1.3 求数组中第k个最大元素?

【考点映射】
  • 排序算法
  • 快速排序
【出现频度】★★★☆
【难度】★★★★
【题目描述】
在未排序的数组中找到第 k 个最大的元素。请注

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

测试岗笔面试真题宝典 文章被收录于专栏

测试工作无非就是点点点,没有太深的技术难度?<br/> 开发转投测试岗,原以为自身的条件能轻松胜任测试岗却反被面试官虐?<br/> 测试岗究竟要准备哪些技术知识去应对面试?<br/> 如何才能在测试岗面试中做到游刃有余?<br/> <p> <span>本专刊从测试岗面试考察的知识点和能力出发,精选出经典的测试岗面试题,不仅给出面试的典型回答和考点分析,还会剖析知识点,将其讲清讲透,让你彻底领悟题目背后所考察的能力,帮你梳理复习测试岗的知识体系。</span> </p> <h3> <span><br /> </span><span><strong>专刊主要分为3大模块:</strong></span> </h3> <p> <span>1. 岗位校招情况介绍:</span> </p> <p> <span>将对整个测试岗位进行详细的介绍,包括测试岗位的分类、市场需求量、薪资情况和校招概况,都会逐一做介绍,让同学们能对测试岗位的校招情况有个大概的了解<br /> 2. 面试考点和面试题讲解:</span> </p> <p> <span>这是本章最为核心的部分,将会以面试题讲解的形式,不仅给出面试题参考答案,还会对考点进行分析,剖析其中的知识点,把知识点讲清讲透,帮助同学们梳理复习测试岗的知识体系。本章涉及的知识板块有:软件测试基础知识、测试用例设计、排查问题的思路、常用的测试工具、计算机操作系统、计算机网络、编程语言和常考的智力题。内容丰富,基本上涵盖了测试面试常考的知识点。<br /> 3. 求职经验分享:</span> </p> <p> <span>本章将详细讲解面试的注意事项:从面试前的准备、面试当天到面试结束收到offer整个过程,都会进行逐一讲解。</span> </p> <p> <span><br /> </span> </p> <h3> <span>专刊大纲:</span> </h3> <p> <span><img src="https://uploadfiles.nowcoder.com/images/20210625/691666214_1624592824918/B4749CDE6B040FF304C11BA36D1276D5" alt="" width="700" height="1692" title="" align="" /><br /> <br /> </span> </p> <h3> <span>购买须知:</span> </h3> <span>①订阅成功后,用户即可通过牛客网 PC 端、App 端享有永久阅读的权限;<br /> ②牛客专刊为虚拟内容服务,订阅成功后概不退款;<br /> ③在专刊阅读过程中,如有任何问题,可在文章评论区底部留言,或添加牛客求职导师,加入读者交流群;<br /> ④想成为牛客作者,请邮件联系pandengfeng@nowcoder.com,邮件主题【牛客作者+写作方向】,并附上个人简历一份及近期作品一份;<br /> ⑤牛客专刊版权归本平台所有,任何机构、媒体、网站或个人未经本网协议授权不得转载、链接、转贴或以其他方式复制发布 / 发表,违者将依法追究责任。<br /> </span> <p> <span>了解专刊更多详细信息,请扫码添加丸子老师微信~</span> </p> <p> <br /> </p> <p> <img src="https://uploadfiles.nowcoder.com/images/20210526/999991364_1622023901811/2E767EB5BD55BF57B67C8E5427B978D8" alt="" /> </p>

全部评论

相关推荐

点赞 2 评论
分享
牛客网
牛客企业服务