首页
学习
活动
专区
圈层
工具
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

前端最高频的算法题之一:反转链表

示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL 解法一:迭代(双指针) 在线链接 本方法是对链表进行遍历,然后在访问各节点时修改 next 的指向...初始化 cur 和 pre 两个节点,分别指向 head 和 null。 对链表进行循环,声明 temp 节点用来保存当前节点的下一个节点。...解法二:递归 在线链接 当使用递归对链表进行处理时,从链表的第一个节点出发,然后找到最后一个节点,该节点就是反转链表的头结点,然后进行回溯处理。 初始链表的头结点,head 标识。...如果 head 为空或者 head.next 为空,返回 head。 定义 reverseHead 节点,保存反转的链表值。 每次让 head 下一个节点的 next 指向 head,形成反转。...空间复杂度 O(N):n 是链表的长度,空间复杂度主要取决于递归调用的栈空间,最多为 n 层。 参考资料 剑指 offer

56700

数据结构之链表与递归

这些规模在一直减小。 28 // 调用私有的sum方法,将数组arr传入参数1,索引0位置传入参数2。...29 // 参数2传入的是0,是递归的初始调用,计算的是从0一直到n-1这些元素所有的和。...递归函数的调用,本质就是函数调用,和普通函数的调用没有区别,只不过调用的函数是自己而已。 5.1、数组求和,使用递归算法进行计算。递归调用的函数微观解读。 ?...总结,递归调用是有代价的,函数调用(需要时间的开销,需要记录当前的逻辑执行到那里了,当前的局部变量都是怎么样的)+ 系统栈空间(递归调用消耗系统栈的空间的)。...表示,在head为头部节点的链表删除val这个元素。

80920
  • 您找到你想要的搜索结果了吗?
    是的
    没有找到

    什么是递归--What does resursion mean?

    第一要素:明确你这个函数想要干什么 对于递归,我觉得很重要的一个事就是,这个函数的功能是什么,他要完成什么样的一件事,而这个,是完全由你自己来定义的。...也就是说,我们需要找出当参数为啥时,递归结束,之后直接把结果返回,请注意,这个时候我们必须能根据这个参数的值,能够直接知道函数的结果是什么。...当然可以,只要你觉得参数是什么时,你能够直接知道函数的结果,那么你就可以把这个参数作为结束的条件,所以下面这段代码也是可以的。...代码如下: Node reverseList(Node head){ } 2. 寻找结束条件 当链表只有一个节点,或者如果是空表的话,你应该知道结果吧?直接啥也不用干,直接把 head 返回呗。...但是我告诉你,它的等价条件中,一定是范围不断在缩小,对于链表来说,就是链表的节点个数不断在变小,所以,如果你实在找不出,你就先对 reverseList(head.next) 递归走一遍,看看结果是咋样的

    58720

    我画了20张图,终于让女朋友学会了翻转链表

    说了这么多理论,相信读者对数组和链表的区别应该有了更深刻地认识了,尤其是 程序局部性原理,是不是开了不少眼界^_^,如果面试中问到数组和链表的区别能回答到程序局部性原理,会是一个非常大的亮点!...别忘了这一步) 1、定义递归函数,明确函数的功能 根据以上分析,这个递归函数的功能显然是翻转某个节点开始的链表,然后返回新的头结点 /** * 翻转结点 node 开始的链表 */ public Node...非递归翻转链表(迭代解法) 我们知道递归比较容易造成栈溢出,所以如果有其他时间/空间复杂度相近或更好的算法,应该优先选择非递归的解法,那我们看看如何用迭代来翻转链表,主要思路如下 ?...难题多是在此基础了做了相应的变形而已 总结 本文详细讲解了链表与数组的本质区别,相信大家对两者的区别应该有了比较深刻的认识,尤其是程序局部性原理,相信大家看了应该会眼前一亮,之后通过对链表的翻转由浅入深地介绍...,相信之后的链表翻转对大家应该不是什么难事了,不过建议大家亲自实现一遍文中的代码哦,这样印象会更深刻一些!

    77620

    为什么你学不会递归?告别递归,谈谈我的经验

    也就是说,我们需要找出当参数为啥时,递归结束,之后直接把结果返回,请注意,这个时候我们必须能根据这个参数的值,能够直接知道函数的结果是什么。...当然可以,只要你觉得参数是什么时,你能够直接知道函数的结果,那么你就可以把这个参数作为结束的条件,所以下面这段代码也是可以的。...1、定义递归函数功能 假设函数 reverseList(head) 的功能是反转但链表,其中 head 表示链表的头节点。...代码如下: Node reverseList(Node head){ } 2. 寻找结束条件 当链表只有一个节点,或者如果是空表的话,你应该知道结果吧?...但是我告诉你,它的等价条件中,一定是范围不断在缩小,对于链表来说,就是链表的节点个数不断在变小,所以,如果你实在找不出,你就先对 reverseList(head.next) 递归走一遍,看看结果是咋样的

    84430

    递归专题BFS

    ,是抽象的,正常人是想不到那么深的,所以我们要想学会使用递归,就需要先克服对递归的恐惧; 递归的实质其实就是重复的做同样的事情; 第一步,知己知彼; 我们需要先了解清楚上面我说的几种算法究竟是什么;...的意思就是"打印cur_num到n的数";对吧; 我们在函数中调用了一个dfs(0,n);这时候就好像是下达了一个命令"你给我打印从0到n",然后函数开始执行,那么重点来了;递归就是自己调用自己;说明DFS...中里面肯定还要调用DFS,对吧;这时候在结合我上面说的"做好当下的任务";我在我这一层函数中我肯定要调用后面的一层函数呗,他怎么做我不管,我只要干好我现在的活就行了;那么在这个函数中,我现在的活是什么?...dfs的函数头使用;只需要给两个链表参数,然后dfs返回合并后的链表头指针; 好了,现在我们dfs函数的指令就是"你给我返回两个参数链表的合并后的头节点指针",那我们在自己这一层函数要做的就是确定返回谁和谁合并的问题...,如果n是负数,那么就应该调用函数1.0/dfs(x,-n);这里有个小细节,int的最大值是2^31-1如果是负数,那有可能传的是2^31,所以这种情况需要单独处理,或者使用long long解决;

    7500

    为什么你学不会递归?告别递归,谈谈我的一些经验

    也就是说,我们需要找出当参数为啥时,递归结束,之后直接把结果返回,请注意,这个时候我们必须能根据这个参数的值,能够直接知道函数的结果是什么。...当然可以,只要你觉得参数是什么时,你能够直接知道函数的结果,那么你就可以把这个参数作为结束的条件,所以下面这段代码也是可以的。...1、定义递归函数功能 假设函数 reverseList(head) 的功能是反转但链表,其中 head 表示链表的头节点。...代码如下: Node reverseList(Node head){ } 2. 寻找结束条件 当链表只有一个节点,或者如果是空表的话,你应该知道结果吧?直接啥也不用干,直接把 head 返回呗。...但是我告诉你,它的等价条件中,一定是范围不断在缩小,对于链表来说,就是链表的节点个数不断在变小,所以,如果你实在找不出,你就先对 reverseList(head.next) 递归走一遍,看看结果是咋样的

    50400

    为什么你学不会递归?告别递归,谈谈我的一些经验

    也就是说,我们需要找出当参数为啥时,递归结束,之后直接把结果返回,请注意,这个时候我们必须能根据这个参数的值,能够直接知道函数的结果是什么。...当然可以,只要你觉得参数是什么时,你能够直接知道函数的结果,那么你就可以把这个参数作为结束的条件,所以下面这段代码也是可以的。...1、定义递归函数功能 假设函数 reverseList(head) 的功能是反转但链表,其中 head 表示链表的头节点。...代码如下: Node reverseList(Node head){ } 2. 寻找结束条件 当链表只有一个节点,或者如果是空表的话,你应该知道结果吧?直接啥也不用干,直接把 head 返回呗。...但是我告诉你,它的等价条件中,一定是范围不断在缩小,对于链表来说,就是链表的节点个数不断在变小,所以,如果你实在找不出,你就先对 reverseList(head.next) 递归走一遍,看看结果是咋样的

    75230

    【超详细】一文学会链表解题

    说了这么多理论,相信读者对数组和链表的区别应该有了更深刻地认识了,尤其是 程序局部性原理,是不是开了不少眼界^_^,如果面试中问到数组和链表的区别能回答到程序局部性原理,会是一个非常大的亮点!...别忘了这一步) 1、定义递归函数,明确函数的功能 根据以上分析,这个递归函数的功能显然是翻转某个节点开始的链表,然后返回新的头结点 /** * 翻转结点 node 开始的链表 */ public Node...非递归翻转链表(迭代解法) 我们知道递归比较容易造成栈溢出,所以如果有其他时间/空间复杂度相近或更好的算法,应该优先选择非递归的解法,那我们看看如何用迭代来翻转链表,主要思路如下 ?...O(n),另外由于没有额外的空间使用,也未像递归那样调用递归函数不断压栈,所以空间复杂度是 O(1),对比递归,显然应该使用迭代的方式来处理!...总结 本文详细讲解了链表与数组的本质区别,相信大家对两者的区别应该有了比较深刻的认识,尤其是程序局部性原理,相信大家看了应该会眼前一亮,之后通过对链表的翻转由浅入深地介绍,相信之后的链表翻转对大家应该不是什么难事了

    50030

    为什么你学不会递归?告别递归,谈谈我的一些经验

    也就是说,我们需要找出当参数为啥时,递归结束,之后直接把结果返回,请注意,这个时候我们必须能根据这个参数的值,能够直接知道函数的结果是什么。...当然可以,只要你觉得参数是什么时,你能够直接知道函数的结果,那么你就可以把这个参数作为结束的条件,所以下面这段代码也是可以的。...1、定义递归函数功能 假设函数 reverseList(head) 的功能是反转但链表,其中 head 表示链表的头节点。...代码如下: 1Node reverseList(Node head){ 2 3} 2. 寻找结束条件 当链表只有一个节点,或者如果是空表的话,你应该知道结果吧?...但是我告诉你,它的等价条件中,一定是范围不断在缩小,对于链表来说,就是链表的节点个数不断在变小,所以,如果你实在找不出,你就先对 reverseList(head.next) 递归走一遍,看看结果是咋样的

    52110

    为什么你学不会递归?

    也就是说,我们需要找出当参数为啥时,递归结束,之后直接把结果返回,请注意,这个时候我们必须能根据这个参数的值,能够直接知道函数的结果是什么。...当然可以,只要你觉得参数是什么时,你能够直接知道函数的结果,那么你就可以把这个参数作为结束的条件,所以下面这段代码也是可以的。...1、定义递归函数功能 假设函数 reverseList(head) 的功能是反转但链表,其中 head 表示链表的头节点。...代码如下: 1Node reverseList(Node head){ 2 3} 2. 寻找结束条件 当链表只有一个节点,或者如果是空表的话,你应该知道结果吧?...但是我告诉你,它的等价条件中,一定是范围不断在缩小,对于链表来说,就是链表的节点个数不断在变小,所以,如果你实在找不出,你就先对 reverseList(head.next) 递归走一遍,看看结果是咋样的

    55920

    为什么你学不会递归?告别递归,谈谈我的一些经验

    也就是说,我们需要找出当参数为啥时,递归结束,之后直接把结果返回,请注意,这个时候我们必须能根据这个参数的值,能够直接知道函数的结果是什么。...当然可以,只要你觉得参数是什么时,你能够直接知道函数的结果,那么你就可以把这个参数作为结束的条件,所以下面这段代码也是可以的。...1、定义递归函数功能 假设函数 reverseList(head) 的功能是反转但链表,其中 head 表示链表的头节点。...代码如下: 1Node reverseList(Node head){ 2 3} 2. 寻找结束条件 当链表只有一个节点,或者如果是空表的话,你应该知道结果吧?...但是我告诉你,它的等价条件中,一定是范围不断在缩小,对于链表来说,就是链表的节点个数不断在变小,所以,如果你实在找不出,你就先对 reverseList(head.next) 递归走一遍,看看结果是咋样的

    95410

    Python升级之路(五) 函数

    eval 函数, 递归函数, 嵌套参数 最后, 通过几个实操练习来巩固本章所学知识 ---- 一、函数是什么 一个程序由一个一个的任务组成;函数就是代表一个任务或者一个功能(function), 是代码复用的通用机制...应尽量避免全局变量的使用 要在函数内改变全局变量的值,使用 global 声明一下 局部变量: 在函数体中(包含形式参数)声明的变量 局部变量的引用比全局变量快,优先考虑使用 如果局部变量和全局变量同名...,在循环的时候优先考虑使用 在特别强调效率的地方或者循环次数较多的地方,可以通过将全局变量转为局部变量提高运行速度 二、参数 我们都应该清楚: 一个完整的函数应包含: 函数名, 参数, 函数体(代码,...注释) 如果把一个函数比作人, 那么函数名就是人名, 函数体是人的身体, 而参数则是人类的灵魂. 1....比如:字符串中含有删除文件的语句. 因此使用时候要慎重!!! 递归函数 递归(recursion)是一种常见的算法思路,在很多算法中都会用到.

    55810

    【综合笔试题】难度 25,说难不难的翻转链表题

    k 是一个正整数,它的值小于或等于链表的长度。 如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。 进阶: 你可以设计一个只使用常数额外空间的算法来解决此问题吗?...链表和树的题目天然适合使用递归来做。 但这次我们先将简单的「递归版本」放一放,先搞清楚迭代版本该如何实现。...我们可以设计一个翻转函数 reverse : 传入节点 root 作为参数,函数的作用是将以 root 为起点的 个节点进行翻转。...当然,在 reverse 函数真正执行翻转前,需要先确保节点 root 后面至少有 个节点。...复杂度为 空间复杂度: 递归解法 搞懂了较难的「迭代哨兵」版本之后,常规的「递归无哨兵」版本写起来应该更加容易了。

    55230

    递归与尾递归总结

    递归一般用于解决三类问题: (1)数据的定义是按递归定义的。(Fibonacci函数,n的阶乘)  (2)问题解法按递归实现。(回溯)  (3)数据的结构形式是按递归定义的。...在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储,因此递归次数过多容易造成栈溢出。...2、尾递归  顾名思义,尾递归就是从最后开始计算, 每递归一次就算出相应的结果, 也就是说, 函数调用出现在调用者函数的尾部, 因为是尾部, 所以根本没有必要去保存任何局部变量....尾递归就是把当前的运算结果(或路径)放在参数里传给下层函数,深层函数所面对的不是越来越简单的问题,而是越来越复杂的问题,因为参数里带有前面若干步的运算路径。  ...尾递归是极其重要的,不用尾递归,函数的堆栈耗用难以估量,需要保存很多中间函数的堆栈。

    79410

    如何编写高质量的 JS 函数(1) -- 敲山震虎篇

    这里,我们先思考几个问题: 这个供代码执行的环境是什么? 这个栈内存是怎么分配出来的? 这个栈内存的内部是一种什么样的样子?...假设不是私有栈内存的,那么在执行一个递归时,基本就结束了,因为一个函数上下文堆栈中,有很多相同的 JS 代码,比如局部变量等,如果不私有化,那岂不乱套了?所以假设矛盾,私有栈内存成立。...看下面代码: function main() { say() // TODO: say() } 如上,在 main 函数中进行多次调用子程序 say ,在底层实现上面,是通过在栈结构中保存一个...,结果分别是如下截图: 第一个程序,输出 10 个 10 : 第二个程序,输出 0 到 9 : 那么问题来了,其内部的原理机制是什么呢?...kun 函数在声明时的内部情况,需要注意一下两点: 第一点:每一个 EC(kun) 中的 AO(kun) 中的 i 属性值都是不一样的,比如通过上面结构化表示,可以看到: result[0] 函数的父执行环境是

    1.3K20

    Python升级之路( Lv5 ) 函数

    eval 函数, 递归函数, 嵌套参数 最后, 通过几个实操练习来巩固本章所学知识 一、函数是什么 一个程序由一个一个的任务组成;函数就是代表一个任务或者一个功能(function), 是代码复用的通用机制...应尽量避免全局变量的使用 要在函数内改变全局变量的值,使用 global 声明一下 局部变量: 在函数体中(包含形式参数)声明的变量 局部变量的引用比全局变量快,优先考虑使用 如果局部变量和全局变量同名...,在循环的时候优先考虑使用 在特别强调效率的地方或者循环次数较多的地方,可以通过将全局变量转为局部变量提高运行速度 二、参数 我们都应该清楚: 一个完整的函数应包含: 函数名, 参数, 函数体(代码,...注释) 如果把一个函数比作人, 那么函数名就是人名, 函数体是人的身体, 而参数则是人类的灵魂. 1....比如:字符串中含有删除文件的语句. 因此使用时候要慎重!!! 递归函数 递归(recursion)是一种常见的算法思路,在很多算法中都会用到.

    1.2K10

    江哥带你玩转C语言 - 16-内存管理和链表

    ###进程空间图示 有了进程和程序的概念以后,我们再来看一下,程序被加载到内存以后内存空间布局是什么样的 ---- 栈内存(Stack) 栈中存放任意类型的变量,但必须是 auto 类型修饰的,即自动类型的局部变量...内存的分配和销毁系统自动完成,不需要人工干预 栈的最大尺寸固定,超出则引起栈溢出 局部变量过多,过大 或 递归层数太多等就会导致栈溢出 int ages[10240*10240]; // 程序会崩溃,...函数声明 void * malloc(size_t _Size); 所在文件 stdlib.h 函数功能 申请堆内存空间并返回,所申请的空间并未初始化。...所以malloc和free函数总是成对出现 函数声明 void free(void *p); 所在文件 stdlib.h 函数功能 释放申请的堆内存 参数及返回解析 参数 void* p 指向手动申请的空间...函数声明 void *realloc(void *ptr, size_t size); 所在文件 stdlib.h 函数功能 扩容(缩小)原有内存的大小。

    61700

    深入浅出C指针,细节之处见真章,拒绝一切无病呻吟!!!

    1、malloc的参数类型为size_t,如果传入参数为负数,是要出事儿的。 2、如果传入参数为0,要么返回NULL,要么返回一个0区的指针。 3、确定所分配的内存数。回忆一下上面那一点。...已释放的指针依然可能造成问题。如果我们试图解引一个已释放的指针,其后果是未可知的,所以有的时候我们显式的给指针赋值为NULL,表示该指针无效,此后再使用这种指针就会造成运行异常。...那么,什么叫“局部函数指针”? 顾名思义,就是在函数里面声明并分配内存的指针。 这种指针以及它分配到的内存,作用域属于这个函数,一旦函数结束,它理论上是没有了。...,如果说 void Hanshu();这是一个函数,那么它的地址就是 Hanshu。...---- 如果你非要我说函数指针存在的意义,那我也真不好给你扯个所以然出来,那我就,举几个用得到的地方吧: 自定义排序/搜索 不同的模式(如策略,观察者) 回调 ---- 本来这里应该有一个“字符串和指针

    30020

    CCPP中static的用法:全局变量与局部变量

    1.1static的引入 我们知道在函数内部定义的变量,当程序执行到它的定义处时,编译器为它在栈上分配空间,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一个问题: 如果想将函数中此变量的值保存至下一次调用时...这样,它的空间分配有三个可能的地方,一是作为类的外部接口的头文件,那里有类声明;二是类定义的内部实现,那里有类的成员函数定义;三是应用程序的main()函数前的全局数据声明和定义处。...静态变量与普通变量 静态全局变量有以下特点: (1)静态变量都在全局数据区分配内存,包括后面将要提到的静态局部变量; (2)未经初始化的静态全局变量会被程序自动初始化为0(在函数体内声明的自动变量的值是随机的...2.3静态局部变量有以下特点: (1)该变量在全局数据区分配内存; (2)静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化; (3)静态局部变量一般在声明处初始化,如果没有显式初始化...自动变量一般会随着函数的退出而释放空间,静态数据(即使是函数内部的静态局部变量)也存放在全局数据区。全局数据区的数据并不会因为函数的退出而释放空间。

    2.6K20
    领券