邻接表:图的高效存储方式,比邻接矩阵好在哪?
这篇文章介绍了图的基本概念及两种核心存储方式:邻接矩阵与邻接表。图由顶点(如社交网络用户)和边(如好友关系)构成。 邻接矩阵是二维数组,用0/1表示顶点间是否有边,空间需n²(n为顶点数),查找边时间O(1),但稀疏图(边少)时空间浪费大。邻接表则为每个顶点维护邻居列表(如用户好友列表),空间n+e(e为边数),仅存实际边,查找需遍历邻居表(时间O(degree(i)),i为顶点),遍历邻居更高效。 对比显示,邻接表在稀疏图(多数实际场景)中空间和时间效率均优于邻接矩阵,是处理图问题(如最短路径)的主流存储方式,更省空间且遍历更快。
阅读全文并查集的路径压缩:并查集优化,让查找更快
并查集用于解决集合合并与元素归属问题(如连通性判断)。核心操作是`find`(查找根节点)和`union`(合并集合),基础版通过`parent`数组记录父节点实现,但长链结构会导致`find`效率极低。为优化,引入**路径压缩**:在`find`过程中,将路径上所有节点直接指向根节点,使树结构扁平化,查找效率接近O(1)。路径压缩通过递归或迭代实现,将长链转化为“一步到位”的短路径。结合按秩合并等优化,可高效处理大规模集合问题,成为解决连通性、归属判断的核心工具。
阅读全文红黑树:平衡二叉树的一种,简单理解它的规则
红黑树是自平衡二叉搜索树,通过颜色标记和5条规则保证平衡,使插入、删除、查找复杂度稳定在O(log n)。核心规则包括:节点非红即黑;根为黑色;空叶子(NIL)为黑色;红节点子节点必为黑色(避免连续红节点);任一节点到后代NIL路径的黑节点数(黑高)一致。规则4阻止连续红节点,规则5确保黑高相等,共同限制树高在O(log n)。插入新节点为红色,若父红需调整(变色或旋转)。广泛应用于Java TreeMap、Redis有序集合等,以平衡结构实现高效有序操作。
阅读全文前缀树:前缀树如何存储和查找单词?实例讲解
前缀树(字典树)是处理字符串前缀问题的数据结构,核心是利用公共前缀节省空间、提升查找效率。其节点含字符、最多26个子节点(假设小写字母)及isEnd标记(是否为单词结尾)。 插入时从根节点开始,逐个字符处理,无对应子节点则新建,处理完字符后标记结尾节点isEnd为true。查找时同样从根开始逐个字符匹配,最后检查isEnd确认是否存在。 实例中,“app”与“apple”共享前缀“app”,“banana”与“bat”共享“ba”,体现空间优势。其优势在于空间更省(共享前缀)、查找快(时间复杂度O(n),n为单词长度),且支持前缀查询。
阅读全文栈与队列的应用:括号匹配问题,用栈解决超简单
### 括号匹配问题:栈的"超简单"应用 文章介绍了利用栈(后进先出特性)解决括号匹配问题的方法。括号匹配需判断由`()`、`[]`、`{}`组成的字符串是否合法,即左括号与右括号一一对应且顺序正确。 栈的"后进先出"特性适合此类问题:左括号入栈暂存,右括号需匹配最近入栈的左括号。具体步骤为:初始化栈,遍历字符串时,左括号直接压栈;右括号则检查栈顶元素是否匹配(通过字典映射右括号与对应左括号),匹配则弹出栈顶,否则非法;遍历结束后栈为空则合法,否则非法。 关键细节包括:区分括号类型(用字典映射)、右括号空栈时直接非法、最终栈为空是合法的必要条件。通过左压右查、匹配弹栈的逻辑,可高效判断任意括号串合法性。
阅读全文二叉搜索树:如何用二叉搜索树实现高效查找?
二叉搜索树(BST)是一种高效的数据结构,用于解决日常数据查找中“快速定位目标”的问题。它是特殊二叉树,每个节点满足:左子树所有节点值小于当前节点值,右子树所有节点值大于当前节点值。 其高效性源于“左小右大”规则:查找时从根节点开始,每次比较目标值与当前节点值,若小于则递归左子树,大于则递归右子树,可排除一半节点,效率稳定在O(log n),优于无序数组(O(n))和有序数组二分查找(插入效率低)。 查找过程核心是“比较-缩小范围”:从根节点出发,等于则找到,小于去左子树,大于去右子树,递归执行。递归或迭代均可实现,如递归法从根开始逐层比较,迭代法则用循环缩小范围。 需注意,若BST不平衡(如退化为链表),效率会退化为O(n);平衡树(如红黑树、AVL树)可保持稳定O(log n)。BST通过“导航式”缩小范围,实现高效有序查找。
阅读全文二分查找:二分查找的适用场景,零基础也能学会
这篇文章介绍了二分查找算法,其核心是在有序数组中通过比较中间元素,逐步缩小查找范围,快速定位目标。它适用于有序、大数据量、静态(少修改)且需快速查找的场景,如字典或配置文件。 查找过程通过左右指针`left`和`right`确定中间值`mid`,根据目标与中间值的大小调整指针:若中间值等于目标则找到;若目标更大,右移`left`;若更小,左移`right`,直至找到或范围无效。 Python迭代实现的核心代码通过`left <= right`循环,计算`mid = (left + right)//2`,边界处理确保数组为空或目标不存在时返回-1。时间复杂度为O(log n)(每次范围减半),空间复杂度为O(1)(仅用常数变量)。 关键细节包括处理重复元素需扩展遍历,单元素数组直接判断,找不到目标返回-1。二分查找的“减治”思想高效解决有序大数据的快速查找问题,是算法基础中的重要工具。
阅读全文并查集:并查集是什么?解决“朋友关系”问题的方法
并查集是管理元素分组的高效数据结构,核心解决“合并组”(Union)和“查询是否同组”(Find)问题,适用于快速判断元素是否属于同一集合的场景。其底层以parent数组维护父节点关系,每组视为一棵树,根节点为组代表,初始各元素自成一组。 关键优化是**路径压缩**(查询时压缩路径,使节点直接指向根)和**按秩合并**(小树依附大树,避免树退化为链表),确保操作接近常数时间复杂度。核心方法`find`(查找根节点并压缩路径)和`union`(合并两组,小树根指向大树根)实现高效分组。 应用广泛,如网络连接判断、家族关系查询、最小生成树(Kruskal算法)及等价类问题等,是处理分组场景的简洁强大工具。
阅读全文前缀和:前缀和数组如何快速计算区间和?
前缀和数组是一种用于快速计算区间和的辅助数组。定义为:原数组A的前缀和数组S,其中S[0]=0,S[k](k≥1)是A[1]到A[k]的和,即S[k] = S[k-1] + A[k]。例如原数组A=[1,2,3,4,5],其前缀和数组S=[0,1,3,6,10,15]。 计算区间和的核心公式是:原数组从第i个到第j个元素的和 = S[j] - S[i-1]。例如计算A[2]到A[4]的和,用S[4]-S[1]=10-1=9,结果正确。 优势在于:预处理S数组需O(n)时间,每次区间和查询仅需O(1)时间,总复杂度O(n+q)(q为查询次数),远快于直接计算的O(qn)。需注意索引对齐(如原数组从0开始时公式调整)、区间合法性及空间换时间的特点。 前缀和数组通过“提前累积”实现
阅读全文图:图的基本概念,邻接表表示法初学者指南
图由顶点(点)和边(连接关系)组成,顶点是基本单元,边可单向(有向图)或双向(无向图),有权图边带权重(如距离),无权图仅存连接关系。邻接表是高效表示法,解决邻接矩阵在稀疏图(边远少于顶点平方)中空间浪费问题,核心是每个顶点对应存储直接相连顶点的列表。无向图邻接表如顶点0连接1、2、3,邻接表为[1,2,3];有权图可存“邻接点+权重”元组。邻接表空间复杂度O(V+E)(V顶点数,E边数),适合稀疏图,遍历邻接点方便,但判断两点是否有边需遍历邻接表。掌握邻接表为图遍历、最短路径等算法奠定基础。
阅读全文链表:单链表与双链表的区别,初学者一看就会
文章以游戏玩家列表存储为例,说明链表解决数组删除中间元素需移动节点的问题。链表是由节点组成的线性结构,节点含数据域和指针域,非连续内存存储,插入删除仅需修改指针。 单链表最简单,节点仅含next指针,单向遍历(从头至尾),插入删除需先找前驱节点改指针,省内存,适合单向场景(如队列)。 双链表节点多一个prev指针,支持双向遍历,插入删除直接通过prev/next指针操作,无需找前驱,内存稍高,适合双向操作(如浏览器历史、通讯录)。 单双链表对比:单链表结构简单省内存,双链表功能全但稍占内存。根据需求选择:单向用单链表,双向或频繁操作用双链表。
阅读全文使用Java实现选择排序算法
选择排序是一种简单直观的排序算法,核心思想是每次从无序部分选取最小(或最大)元素,放入已排序部分末尾,重复此过程直至全部有序。其基本思路为:外层循环确定已排序部分的末尾位置,内层循环在未排序部分中寻找最小值,交换该最小值与外层循环当前位置的元素,直至完成排序。 Java实现中,`selectionSort`方法通过两层循环实现:外层循环遍历数组(`i`从0到`n-2`),内层循环(`j`从`i+1`到`n-1`)寻找未排序部分的最小值索引`minIndex`,最后交换`i`位置元素与`minIndex`位置元素。以数组`{64,25,12,22,11}`为例,每轮交换后逐步构建有序数组,最终结果为`[11,12,22,25,64]`。 时间复杂度为O(n²),适用于小规模数据。该算法逻辑简单、代码易实现,是理解排序基础思想的典型示例。
阅读全文扑克牌排序启示:插入排序的生活类比与实现
这篇文章介绍了插入排序算法。核心思想是逐步构建有序序列:默认首个元素已排序,从第二个元素起,将每个元素(待插入元素)插入到前面已排序序列的正确位置(需移动较大元素腾出空间)。以数组`[5,3,8,4,2]`为例,演示了从后往前比较、移动元素的过程,关键步骤为:遍历数组,暂存待插入元素,移动已排序元素,插入位置。 算法特点:优点是简单直观、原地排序(空间复杂度O(1))、稳定且适合小规模或近乎有序数据;缺点是最坏时间复杂度O(n²),移动操作较多。总结而言,插入排序是理解排序算法的基础,通过生活类比(如整理扑克牌)帮助理解,适用于简单场景或小规模数据排序。
阅读全文排序算法的‘速度密码’:时间复杂度与冒泡排序
这篇文章围绕排序算法的“速度密码”展开,核心是时间复杂度与冒泡排序。时间复杂度用大O表示法衡量,常见类型有O(n)(线性,随数据量线性增长)、O(n²)(平方,数据量大时效率极低)、O(log n)(对数,速度极快),其是判断算法快慢的关键。 冒泡排序作为基础算法,原理是通过相邻元素比较交换,将小元素“上浮”、大元素“下沉”。以数组[5,3,8,4,2]为例,重复遍历比较相邻元素,直至完成排序。其时间复杂度为O(n²),空间复杂度O(1)(原地排序),优点是简单易懂、逻辑直观,缺点是效率低,数据量大时耗时极久。 总结:冒泡排序虽慢(O(n²)),但作为入门工具,能帮助理解排序思想与时间复杂度,为学习快速排序等高效算法(优化至O(n log n))奠定基础。
阅读全文二分查找:比线性查找快多少?数据结构中的查找技巧
文章介绍了计算机中的查找算法,从线性查找和二分查找两方面展开。线性查找(顺序查找)是基础方法,通过从头到尾逐个检查数据,时间复杂度为O(n),适用于数据量小或无序的场景,最坏情况需遍历全部数据。二分查找则需在有序数组中使用,核心是每次排除一半数据,时间复杂度O(log n),数据量大时效率远超线性查找(如n=100万,二分仅需20次,线性需100万次)。两者适用场景不同:二分适用于有序、大数据量且频繁查找的场景;线性适用于无序、小数据量或动态变化的数据。总结:二分查找通过“对半排除”大幅提升效率,是大数据量有序数据的高效选择,而线性查找在小数据量或无序场景更灵活。
阅读全文哈希表怎么存数据?哈希函数让查找变简单
文章用找书类比引出问题:顺序查找数据(如数组)效率低,哈希表是高效存储工具。哈希表核心是哈希函数,将数据映射到“桶”(数组位置),实现快速存取。哈希函数把数据转为哈希值(桶编号),如学号取后两位得哈希值。存储时,先算哈希值定位桶,若多数据哈希值相同(冲突),用链表法(桶内挂链表)或开放寻址法(找下一个空桶)解决。查找时,直接算哈希值定位桶,再在桶内查找,无需遍历全部数据,速度极快。哈希表应用广泛(如通讯录、缓存),核心是用哈希函数将查找从“翻遍”转为“直达”。
阅读全文