数据结构

专题-数据结构

  • 数据结构相关基本是现场面试中出现频率最高的问题。

    • 因为现场面试的时间限制,更难的问题需要大量的思考时间,所以一般只要求需要阐述思路(比如动态规划);

    • 数据结构相关的问题,因为有很强的先验知识,通常要求手写代码

  • 本专题只收录基础数据结构相关问题,不包括高级数据结构及数据结构的设计,比如线段树或者 LRU 缓存,这些问题可以参考数据结构_Advanced

Index

二叉树

二叉树的深度

二叉树的深度 - 牛客

C++

二叉树的宽度

思路

  • 层序遍历(队列)

C++

二叉树最大宽度(LeetCode)

LeetCode - 662. 二叉树最大宽度

问题描述

思路

  • 本题在二叉树宽度的基础上加入了满二叉树的性质,即每层都有 2 ^ (n-1)个节点。某节点的左孩子的标号是2n, 右节点的标号是2n + 1。

  • :如果在循环中会增删容器中的元素,则不应该在 for 循环中使用 size() 方法,该方法的返回值会根据容器的内容动态改变

C++

二叉树中的最长路径

思路

  • 对任一子树而言,则经过该节点的一条最长路径为其左子树的深度 + 右子树的深度 + 1

  • 遍历树中每个节点的最长路径,其中最大的即为整个树的最长路径

    为什么最长路径不一定是经过根节点的那条路径?

判断平衡二叉树 TODO

判断树 B 是否为树 A 的子结构

树的子结构 - 牛客

题目描述

  • 图示

思路

  • 递归

  • 有两个递归的点:一、递归寻找树 A 中与树 B 根节点相同的子节点;二、递归判断子结构是否相同

Code(递归)

利用前序和中序重建二叉树

重建二叉树 - 牛客

题目描述

思路

  • 前序遍历的第一个值为根节点的值,使用这个值将中序遍历结果分成两部分,左部分为左子树的中序遍历结果,右部分为右子树的中序遍历的结果。

  • 根据左右子树的长度,可以从前序遍历的结果中划分出左右子树的前序遍历结果

  • 接下来就是递归过程

  • 注意:必须序列中的值不重复才可以这么做

  • 示例

Code(Python)

C++ 版本 > 题解-剑指Offer/重建二叉树

```Python

class TreeNode:

def init(self, x):

self.val = x

self.left = None

self.right = None

class Solution:

请实现两个函数,分别用来序列化和反序列化二叉树。 接口如下:

空节点用 '#' 表示,节点之间用空格分开

最近公共祖先

《剑指 Offer》 7.2 案例二

问题描述

如果树是二叉搜索树

  • 找到第一个满足 p1 < root < p2 的根节点,即为它们的最小公共父节点;

  • 如果寻找的过程中,没有这样的 root,那么 p1p2 的最小公共父节点必是它们之一,此时遍历到 p1p2 就返回。

如果树的节点中保存有指向父节点的指针

如果只是普通的二叉树

236. 二叉树的最近公共祖先 - LeetCode

  • 利用两个辅助链表/数组,保存分别到 p1p2 的路径;

    获取节点的路径

  • p1p2 的最小公共父节点就是这两个链表的最后一个公共节点

  • C++

获取节点的路径

二叉树

非二叉树

链表

旋转链表(Rotate List)

LeetCode/61. 旋转链表

问题描述

思路

  • 双指针 l, r 记录两个位置,其中 l 指向倒数第 k+1 个节点,r 指向最后一个非空节点;

  • 然后将 r 指向头结点 hh 指向 l 的下一个节点,最后断开 l 与下一个节点;

  • 注意 k 可能大于链表的长度,此时可能需要遍历两次链表

代码 1

  • 比较直观的写法,代码量稍大

    ```python

    class ListNode:

    def init(self, x):

    self.val = x

    self.next = None

class Solution: def rotateRight(self, h, k): """ :type h: ListNode :type k: int :rtype: ListNode """ if not h or k == 0: return h

反转链表

反转链表 - 牛客

题目描述

  • 要求:不使用额外空间

思路

  • 辅助图示思考

Code(迭代)

Code(递归)

合并排序链表

合并两个排序的链表 - 牛客

问题描述

迭代

递归

两个链表的第一个公共节点

思路 1

  • 先求出两个链表的长度 l1l2,然后让长的链表先走 |l1-l2| 步,此时两个指针距离第一个公共节点的距离相同,再走相同的步数即可在第一个公共节点相遇

  • 时间复杂度 O(m + n)

  • 代码(未测试)

思路 2

  • 两个指针同时开始遍历,

  • 当其中一个指针到达尾节点时,转到另一个链表继续遍历;

  • 当另一个指针也到达尾节点时,也转到另一个链表继续遍历;

  • 此时两个指针距离第一个公共节点的距离相同,再走相同的步数即可在第一个公共节点相遇

  • 时间复杂度 O(m + n)

  • 代码(未测试)

链表排序

链表排序(冒泡、选择、插入、快排、归并、希尔、堆排序) - tenos - 博客园

链表快排

LeetCode/148. 排序链表

问题描述

思路

  • 与数组快排几乎一致,只是 partition 操作需要从左向右遍历;

  • 因为涉及指针,还是用 C++ 写比较方便;

  • 另外 LeetCode 讨论区反映 Python 可能会超时;

  • 时间复杂度:最好 O(NlogN),最坏 O(N^2)

代码 1 - 只交换节点内的值

  • 参考数组快排中的写法,这里选取第一个元素作为枢纽

    ```C++

    /**

    • Definition for singly-linked list.

    • struct ListNode {

    • int val;

    • ListNode *next;

    • ListNode(int x) : val(x), next(NULL) {}

    • };

      */

class Solution {

public: ListNode sortList(ListNode head) { if (head == nullptr || head->next == nullptr) return head;

};

链表归并

LeetCode/148. 排序链表

问题描述

思路

  • 用快慢指针的方法找到链表中间节点,然后递归的对两个子链表排序,把两个排好序的子链表合并成一条有序的链表

  • 归并排序比较适合链表,它可以保证了最好和最坏时间复杂度都是 O(NlogN),而且它在数组排序中广受诟病的空间复杂度在链表排序中也从O(n)降到了 O(1)

    • 因为链表快排中只能使用第一个节点作为枢纽,所以不能保证时间复杂度

  • 还是使用 C++

  • 时间复杂度:最好/最坏 O(NlogN)

C++

链表插入排序

LeetCode/147. 对链表进行插入排序

注意:以下代码在148. 排序链表也能 AC(要求时间复杂度 O(NlogN)

问题描述

  • 插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。

    每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。

思路

  • 见代码注释

  • 时间复杂度:最好/最坏 O(N^2)

代码 1 - 非原地

  • 实际上,对链表来说,不存在是否原地的问题,不像数组

  • 这里所谓的非原地是相对数组而言的,因此下面的代码只针对链表,不适用于数组。

代码 2 - 原地

  • 即不使用新链表,逻辑与数组一致;

  • 此时链表拼接的逻辑会复杂一些

链表选择排序

LeetCode/147. 对链表进行插入排序

注意:以下代码在148. 排序链表也能 AC(要求时间复杂度 O(NlogN)

思路

  • 见代码注释

  • 时间复杂度:最好/最坏 O(N^2)

C++

链表冒泡排序

LeetCode/147. 对链表进行插入排序

思路

  • 见代码注释

  • 时间复杂度:最好 O(N),最坏 O(N^2)

C++

二维数组

二分查找

搜索二维矩阵 1

LeetCode - 74. 搜索二维矩阵

问题描述

思路

  • 当做一维有序数组二分查找

C++

搜索二维矩阵 2

LeetCode - 240. 搜索二维矩阵 II

思路

  • 1)从右上角开始查找,时间复杂度 O(M+N)

  • 2)每一行二分查找,时间复杂度 O(MlogN)

打印二维数组

回形打印

蛇形打印

堆的调整(自上而下)

用两个栈模拟队列

用两个栈实现队列 - 牛客

题目描述

思路

  • 假设 stack_in 用于处理入栈操作,stack_out用于处理出栈操作

  • stack_in 按栈的方式正常处理入栈数据;

  • 关键在于出栈操作

    • stack_out为空时,需要先将每个stack_in中的数据出栈后压入stack_out

    • 反之,每次弹出stack_out栈顶元素即可

Code(C++)

最后更新于

这有帮助吗?