简介:这是一份军队文职计算机类岗位备考用的数据结构与算法知识点总结,面向需要系统复习数据结构核心内容的考生。文档以docx格式呈现,共包含1个文件,资源包大小约661KB,内容涵盖基本概念、线性表、栈与队列、树与二叉树、图、查找与排序等模块,并收录了顺序存储与链式存储对比、插入删除时间复杂度、二叉树遍历、图的深度与广度优先搜索等关键要点。资源将零散考点整理为结构化笔记,配有顺序表与链表类型定义、栈与队列操作步骤、循环队列队空队满条件、满二叉树与完全二叉树性质、最小生成树等细节,便于对照理解、考前速记与查漏补缺。已有98人浏览学习,适合备考军队文职计算机岗或正在复习数据结构课程的读者使用。
1. 军队文职计算机类数据结构与算法:先拆成三份备考资产再动手
军队文职计算机岗位的笔试里,数据结构与算法往往占计算机基础模块的四成以上。很多人拿到那份知识点备考 docx 的第一反应是通读几遍,但实际效果并不好:当时看着都懂,合上文档做题就卡壳。问题不在备考资料本身,而在于没有把它拆成一套可执行的动作。
这份资料的正确打开方式,是先建立三个东西:按考纲排出的优先级清单、能反复默写的手写代码模板、记录每次错因的标签体系。我下面按这个思路拆解复习路径,让你拿到文档后能快速判断先看哪、背什么、怎么练,并把常见的失分点提前找出来。
2. 备考资料拆解:按考纲优先级把 docx 变成五份笔记
2.1 先读考纲再定复习优先级:三大模块如何划分
军队文职计算机类的数据结构和算法知识不是一把抓的。综合看历年考法,基本能分成三块:数据结构基础(线性表、栈、队列、树、图)、算法设计(排序、查找、动态规划、贪心)、复杂度分析(时间与空间)。这三块的复习深度不一样。数据结构基础考概念和手写代码,算法设计考方法和推导过程,复杂度分析考记忆与运算。拿到总结的第一件事不是通读,而是先把里面的知识点按这三类做归档,标出每一部分的熟悉度。
归档时可以借助文档大纲功能。把 docx 里的标题提取出来,按章节号排列后逐条问自己三个问题:这个概念能不能一句话讲清楚?能不能立刻写出对应的实现代码?能不能说出适用场景和不适用场景?三个问题都能答上标“掌握”,只能答一个或两个标“待复习”,完全没概念标“重学”。这个动作做完,你手里就有一张复习资源分配图,后续时间往哪投一看便知。
提示:归档时不要按章节顺序平均分配时间。掌握的内容一周后再回看,待复习和重学的内容隔天就过一遍,这样收益最高。
2.2 把知识点转换成五份笔记:概念卡、速查表、代码模板、对比表、错题
把总结做一次转换,我会把它变成五类复习产品。第一类核心概念卡片,每张只写定义、一句话解释、引用场景,控制在三十字左右。第二类复杂度速查表,把各算法的时间复杂度、空间复杂度、稳定性列成一张表。第三类手写代码模板集,把链表反转、树遍历、排序算法、查找算法的标准实现抄下来,反复默写。第四类是易混对比表,将相邻概念放在一起,比如顺序表和链表的差异、深搜和广搜的区别、稳定排序和不稳定排序的代表。第五类是错题记录,把做错的题按知识点归类,写上错误原因而不是正确答案。
这五个板块可以直接由 docx 的内容提炼而来,关键在用“手写”代替“复制粘贴”。手写过程中,大脑会对每一步操作做逻辑校验,这和读十遍的效果完全不同。我复习时有一个习惯:把代码模板打印出来,每天用一张 A4 纸默写,坚持到考前一周,考场上看到相似题目手指会自然写出来。
2.3 用状态标签管理复习进度:掌握、模糊、错误三态
文档里如果有大量例题,还需要一个标签系统来标记状态。我常用的做法是:在 docx 的目录树后直接加一列状态标记,或者用加粗、底色、批注三色区分。标记的意义不在美观,而在于第二轮、第三轮复习时能跳过掌握内容,把时间压在模糊和错误的内容上。
具体节奏:掌握的内容一个星期后不用再看;模糊的内容隔三天看一次;错误的内容天天看,直到连续三次做对才降级为“模糊”。这样一套状态机的好处是进度可视化,你的复习推进感来自状态变化,而不是来自“我今天看了十页”。考前几天的心态,很大程度上也是靠着这套可视化的运转撑起来的。
3. 数据结构核心板块:线性表、栈、树与图的复习路线
3.1 顺序表与链表:概念对比与两个必须默写的模板
线性表有两种实现方式:顺序表和链表。顺序表本质是连续内存存储,数组是典型实现;链表本质是不连续内存,靠指针串联。两者的差异集中在三处:随机访问速度、插入删除成本、额外空间开销。顺序表随机访问 O(1),链表 O(n);顺序表插入删除需要搬运元素 O(n),链表只要改指针 O(1),前提是先找到位置;顺序表无额外指针开销,链表每个节点多一个指针域。
考试里线性表的题几乎固定在这几类:单链表反转、链表合并、删除有序数组重复元素、判断链表是否有环。链表反转是高频中的高频,两种常见写法里我更建议背递归版本:
def reverse_list(head): # 递归反转:把后续链反转后,再把头节点接到尾部 if head is None or head.next is None: return head new_head = reverse_list(head.next) # 先反转后面的链表 head.next.next = head # 原下一个节点回头指向当前节点 head.next = None # 当前节点成为新链表的尾 return new_head这段代码的关键是 head.next.next = head 这一步。它的含义是“当前节点的下一个节点,把它的 next 指针指向当前节点”;执行完之后,再把当前节点的 next 置空。参数 head 表示当前子链表的头节点,递归出口是节点为空或只有一个节点时不需反转。很多人在这一步把前后方向写反,链就直接断裂。
删除有序数组重复项的常用做法是双指针原地去重:
def remove_duplicates(nums): # 双指针:slow 指向已去重区最后一个元素,fast 负责向前扫描 if not nums: return 0 slow = 0 for fast in range(1, len(nums)): if nums[fast] != nums[slow]: slow += 1 nums[slow] = nums[fast] return slow + 1slow 的位置是“当前不重复部分的末尾”,fast 每遇到一个新值就先把 slow 前进一位再写入。要注意这题的前置条件是数组已排好序,否则这个写法会漏掉非相邻的重复值。返回值是去重后数组的长度,参数数组在原地被改了前 slow+1 位。
3.2 栈与队列:应用场景以及循环队列的边界公式
栈的特点是先进后出,队列的特点是先进先出。使用场景是记忆重点:需要回溯的场景用栈,比如括号匹配、表达式求值、函数调用;需要排队消费的场景用队列,比如进程调度、消息缓冲。
循环队列也是常考的计算题,核心公式固定为:
# 进队:rear = (rear + 1) % MAX_SIZE # 出队:front = (front + 1) % MAX_SIZE # 队空:front == rear # 队满:(rear + 1) % MAX_SIZE == front为什么要牺牲一个存储单元?因为如果不浪费,队空和队满都会出现 front == rear,无法分辨。用这个代价换来清晰的判空判满条件,是循环队列最主流的实现方案。所以循环队列的实际可存元素数是 MAX_SIZE - 1,这个数经常出现在选择题干扰项里。
括号匹配是一道典型手写题,思路是用栈记录左括号,遇到右括号时弹栈比对:
def is_valid(s): # 栈记录左括号,右括号必须与栈顶匹配 stack = [] mapping = {')': '(', ']': '[', '}': '{'} for ch in s: if ch in mapping: # 右括号:弹栈比对 if not stack or stack.pop() != mapping[ch]: return False else: # 左括号:入栈等待 stack.append(ch) return not stack常见的错误是只考虑三种括号而没有处理“栈空时遇到右括号”的情况。比如输入字符串是“)[”,遍历到第一个字符时栈为空,此时应该立刻判 False。第二个错误点是在最终返回时直接写 True,漏掉了“栈里还有多余左括号”的情况,return not stack 同时覆盖了这两种错误。
3.3 二叉树与图:递归遍历框架和两种搜索的分工
树和图是数据结构里最庞大的两章,手写代码占比也最高。树的遍历是所有基础代码的核心,先序、中序、后序的递归写法只有访问节点的时机不同。很多人写不出来的原因不是不会递归,而是没搞清递归函数的参数、返回值和边界条件。
看一下中序递归模板:
def inorder(root, result): # 中序遍历:左子树 -> 当前节点 -> 右子树 if root is None: return inorder(root.left, result) # 左子树 result.append(root.val) # 处理当前节点 inorder(root.right, result) # 右子树参数 root 是当前节点,result 是收集遍历结果的列表;边界条件是节点为空立即返回。先序只需要把 result.append 放到两次递归之前,后序则放到两次递归之后,三种遍历其实是同一个框架。记忆时不要背“左右根”之类的顺序,而是记住“递归函数自己会处理子问题,你只负责按顺序调用”。
求二叉树最大深度也常用递归:
def max_depth(root): # 空节点高度为 0,当前层深度是子树深度加 1 if root is None: return 0 return max(max_depth(root.left), max_depth(root.right)) + 1这里加 1 表示当前节点自己算一层,边界条件是空节点返回 0。只要递归关系能写出来,代码基本不会写错。
图的部分在军队文职笔试中一般不考复杂算法,重点是存储方式和两种遍历。存储方式有邻接矩阵和邻接表:邻接矩阵适合稠密图,任意边访问 O(1);邻接表适合稀疏图,空间占用低。深度优先搜索天然采用栈结构,适合找路径;广度优先搜索采用队列结构,适合找最短路径和层次展开。
4. 算法核心板块:排序、查找与动态规划的判断与实现
4.1 排序算法:复杂度、稳定性与适用场景的决策表
排序是算法部分的起点,军队文职考试里排序题考得全但不算深,要求掌握每种排序的思想、时间与空间复杂度、稳定性,以及应对不同数据规模的表现。下面这张表建议直接用来做复习底稿:
| 排序算法 | 最好时间 | 最坏时间 | 平均时间 | 空间复杂度 | 稳定性 |
|---|---|---|---|---|---|
| 冒泡排序 | O(n) | O(n^2) | O(n^2) | O(1) | 稳定 |
| 选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 不稳定 |
| 插入排序 | O(n) | O(n^2) | O(n^2) | O(1) | 稳定 |
| 快速排序 | O(n log n) | O(n^2) | O(n log n) | O(log n) | 不稳定 |
| 归并排序 | O(n log n) | O(n log n) | O(n log n) | O(n) | 稳定 |
| 堆排序 | O(n log n) | O(n log n) | O(n log n) | O(1) | 不稳定 |
记忆这张表时不要逐行背,而是按机制分组:相邻元素两两比较并交换的排序是稳定的,比如冒泡和插入;存在跳跃比较或远距离交换的不稳定,比如选择、快排和堆排。归并排序虽然不断拆分子数组,但合并时优先取左半边的同值元素,因此稳定。快排的最佳时间不是稳定的 O(n log n):当每次基准都选到中位数时才是 O(n log n),最坏情况是每次分区都把数组切成 1 和 n-1 两段,此时退化成 O(n^2)。
手写模板里快排是必考的,这里给出挖坑填数的闭区间版本:
def quick_sort(arr, left, right): # 快排:取第一个元素为基准,双向扫描分区 if left >= right: return pivot = arr[left] # 基准值先拿出来,形成一个“坑” i, j = left, right while i < j: while i < j and arr[j] >= pivot: # 右边找小于基准的值 j -= 1 arr[i] = arr[j] # 填坑 while i < j and arr[i] <= pivot: # 左边找大于基准的值 i += 1 arr[j] = arr[i] # 再填坑 arr[i] = pivot quick_sort(arr, left, i - 1) quick_sort(arr, i + 1, right)两个最容易写错的地方:一是在内层 while 中丢掉了 i < j 条件,扫描会越界;二是 arr[i] = arr[j] 和 arr[j] = arr[i] 搞反,交换逻辑会乱。这个版本结束时手动把 pivot 写回 arr[i],因为整个过程中基准只是被暂存,并没有参与交换。
4.2 查找算法:二分查找的闭区间写法与哈希冲突
查找方面主要复习二分查找和哈希表。二分查找前提是有序数组,每次把搜索范围缩小一半。它看起来简单但边界条件容易出错,根本原因是区间开闭没统一。以下闭区间写法相对不容易踩坑:
def binary_search(nums, target): # 闭区间 [left, right],left 和 right 都可能是答案 left, right = 0, len(nums) - 1 while left <= right: mid = (left + right) // 2 if nums[mid] == target: return mid elif nums[mid] < target: left = mid + 1 # 目标在右半边 else: right = mid - 1 # 目标在左半边 return -1这个写法的关键是一个不变量:target 若存在,一定落在 [left, right] 内。所以循环条件写成 left <= right,更新时一定要用 mid + 1 和 mid - 1 而不是 mid。用 mid 更新会导致死循环,用 left < right 当循环条件则可能漏掉最后一个元素。mid 的计算在 left + right 很大时为了防止溢出可以改成 left + (right - left) // 2,但考试题一般用不到。
哈希表的考点集中在散列函数与冲突处理。散列函数把任意长的输入映射为固定下标;冲突处理有两种主流策略:开放地址法与链地址法。开放地址法在冲突时线性探测或平方探测找下一个空位;链地址法在同一槽位用链表挂多个元素。判定一个哈希策略的优劣主要看装载因子和冲突率,装载因子过高时查询性能会明显恶化。如果考试给一个哈希表让你算平均查找长度,要先明确它用的是开放地址还是链地址,两者的计数方式不同。
4.3 动态规划与贪心:状态定义、递推关系与边界处理
动态规划是算法题里最容易被劝退的部分,但军队文职的题型并不刁钻,通常考经典模型。复习动态规划要记住三问:状态是什么?状态怎么转移?边界条件是什么?三步想明白,代码只是顺手的事。
以爬楼梯为例:每次可以走一阶或两阶,问走到第 n 阶有多少种方法。状态定义 dp[i] 表示走到第 i 阶的方法数。转移方程 dp[i] = dp[i-1] + dp[i-2] 的物理含义是:第 i 阶只能从第 i-1 阶迈一步过来,或从第 i-2 阶迈两步过来。边界条件 dp[0] = 1、dp[1] = 1。
def climb_stairs(n): # dp[i] 表示到达第 i 阶的不同方法数 dp = [0] * (n + 1) dp[0] = 1 dp[1] = 1 for i in range(2, n + 1): dp[i] = dp[i - 1] + dp[i - 2] return dp[n]这个题还有一种空间优化写法:只保留 dp[i-1] 和 dp[i-2] 两个变量滚动,把空间降到 O(1)。但备考阶段先不要优化空间,先把状态表完整写出来,并用手工算一遍 n=5 的结果,确认递推关系没有错再谈优化。
贪心算法在考试中的难度不在写法,而在“用不用”的判断。贪心的成立条件是每一步的局部最优能推出全局最优。常见的活动选择、区间调度、部分背包问题都符合这个条件。硬币找零是一个反例:当面额为 1、3、4,需要凑 6 时,贪心会选 4+1+1 共 3 枚,而最优解是 3+3 共 2 枚。这个反例要记牢,题目一旦把硬币面额改成不规则的,贪心就要慎重。
5. 数据结构与算法备考避坑:五个典型翻车场景与排查方法
5.1 排序算法背混复杂度和稳定性
现象:考场里把快排最坏时间复杂度写成 O(n log n),或者把选择排序说成稳定排序,基础分直接丢掉。原因:靠死记硬背,没有从比较和交换的规律去推导。解决:按“是否相邻比较交换”和“是否存在远距离跳跃”分组记忆。相邻比较交换的冒泡和插入稳定;跳跃比较的选择、快排、堆排不稳定。最坏 O(n^2) 的触发条件也要记:快排每次选到极值基准,插入排序遇到倒序数据。
复习时,把排序算法按特征分组比逐行背表格更持久:稳定类普遍走相邻交换机制,不稳定类普遍存在跳跃交换。临考前再用那张决策表过一遍,把六个算法的名字和特征对应上,基本就能拿到这块分。
5.2 二叉树中序遍历写反左右顺序
现象:手写中序遍历时输出序列错乱,把左子树和右子树调换了位置。原因:没有先明确“中序是左根右”,而是把先序的“根左右”习惯套了进去。解决:动手前在题目旁写下“左根右”三个字,对照到代码的三行调用上。写完用三个节点的简单树验证一次输出,能立刻发现顺序问题。
这个坑在二叉树的递归和非递归写法里都会出现。非递归中序用栈模拟时,顺序是先一路压左子树到空,再弹出访问节点、转向右子树。如果对“左根右”的直觉来自先序,压栈和出栈的时机就会写错,导致结果连顺序都对不上。
5.3 循环队列容量多算一个
现象:选择题问循环队列最多能存多少元素,答案总是会选成 MAX_SIZE。原因:没考虑到实现上通常牺牲一个单元来区分队空和队满,这是队列实现教材的默认约定。解决:取 MAX_SIZE=5 画一下进队过程,连续进 5 个元素后 front 和 rear 的关系就会暴露问题。记住“实际容量是 MAX_SIZE - 1”这个结论,并知道为什么。
考试里这个点还会换个问法:给你 front、rear 和 MAX_SIZE,问当前队列有多少元素。公式是 (rear - front + MAX_SIZE) % MAX_SIZE,注意这里 rear 指向队尾的下一个位置,减出来的差值才是存活的元素数。
5.4 动态规划状态定义不清导致写错递推
现象:把 dp[i] 理解成“前 i 个元素”还是“第 i 个元素”没定清楚,递推关系越写越乱,跑数据也对不上。原因:没有先在纸上写状态定义就直接写代码。解决:动笔前把状态的区间含义用一句话写在注释里,例如“dp[i] 表示以第 i 个元素结尾的最大子序和”。然后用手工小规模数据把 dp 表推一遍,验证每个取值都说得通,再开始写循环。
状态定义差一个词,递推关系可能差一整条链。以“前 i 个元素”和“以第 i 个元素结尾”为例,前者的答案可以直接在 dp[n] 里取,后者则必须在整个 dp 数组里取最大值。考场上一旦混用,结果就飘了。
5.5 深搜和广搜的底层结构搞反
现象:判断“深搜用什么结构承载、广搜用什么结构”的选择题经常答反。原因:只记了“深搜用栈、广搜用队列”的结论,没有理解背后的语义。解决:开通思路。深搜是“一条路走到底再回头”,符合后进先出的回溯特性,所以用栈;广搜是“逐层向外扩展”,同一层的节点先处理,符合先进先出,所以用队列。考试时推一遍这个逻辑就不会混淆。
这个点还会和图遍历序列的题一起考。给你一个邻接表,让你写出深搜和广搜的访问序列,本质就是在考“栈弹出最近访问的节点”和“队列弹出最早进来的节点”的差异,把二者的消费顺序想清楚,序列就不会写错。
6. 用错题标签做最后冲刺:落地一套 30 分钟自测复盘流程
考前一周复习的重点不再是把知识读厚,而是验证已掌握的东西能不能稳定输出。我常用的这套 30 分钟自测流程,核心是把复习笔记从“读过”转成“能答”。
第一步做考点默写。拿出一张 A4 纸,把 docx 里的大纲标题抄下来,不看任何材料,在 30 分钟内把每个知识点用一句话写出来。能写出的打勾,写不出或写不全的打叉。这一步能快速暴露哪些知识点只是“眼熟”而不是“掌握”。
第二步做代码默写。把核心模板题列出来,包括链表反转、二叉树中序、快排、二分查找。每个题目给自己 5 分钟。写不出来的模板当天必须重新整理,否则第二天还是写不出来。这一步建议和第一步隔四个小时以上,避免短期记忆的干扰。模板自检点可以对照:
| 默写内容 | 标准写法 | 自检点 |
|---|---|---|
| 链表反转 | 递归或迭代 | 新链表的头是否正确返回 |
| 中序递归 | 三行递归调用 | 左右子树调用顺序 |
| 快速排序 | 闭区间挖坑法 | 内层 while 的 i < j 条件 |
| 二分查找 | 闭区间写法 | 循环条件和 mid ± 1 |
第三步做错题回顾。把平时标记为“错误”的题目翻一遍,回忆当时的错误原因,而不是只看正确答案。能说清楚“这道题上次错是因为什么”,就能把它降级为“模糊”;说不清楚就保持错误状态,继续纳入每日复习计划。考前两天的复习完全只做这些降级题和新整理出来的模板,不再接触新题。
这套流程的核心是把自己当成一个系统来迭代:默写验证输出能力,错题回顾验证反思能力,降级机制则清空低价值内容。我在模拟阶段坚持两周后,最明显的变化是每晚有效复习时间从两小时压缩到三十分钟,错题不再犯同样的原因,考前一天的心态也稳了很多。科普类的“多刷题”在这里不一定适用,这套可量化、可复现的复盘流程更适合考前冲刺。希望帮到你。
本文还有配套的精品资源,点击获取