算法合集 | 反转链表 | Leetcode 206.反转链表

102 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情

前言

本系列文章主要会总结一些常见的算法题目以及算法的易错点,难点,以及一些万用的公式,并且结合实际的 Leetcode 题目来进行加深理解以及实际应用,算法这种东西,属于是一到用时方恨少的类型,在平时总结一些常见的简单算法,经常磨练自己的算法思维,对于日常的开发还是能有不少的帮助的。

  • 今天来介绍一下反转链表的操作

什么是反转链表

下面是一个正常的链表,里面有三个元素,最后指向null

image.png

进行翻回以后,应该变为:

image.png

头结点变成了 值为 3 的结点,尾结点变成了值为 1 的结点,这就是反转链表的一个大概操作。

假如说原理的对象为 :

const node = {
	val:1,
	next: {
		val: 2,
		next: {
			val: 3,
			next: null,
		}
	}
}

那么反转过后就会变成

const node = {
	val: 3,
	next: {
		val: 2,
		next: {
			val: 1,
			next: null,
		}
	}
}

Leetcode 206.反转链表

题目的解法存在双指针解法以及递归解法,双指针的解法会比较简单,所以这里先优先介绍双指针的解法。

双指针

image.png

看图,首先我们需要定义两个指针,其中一个cur指针,一开始指向头结点,还有一个pre指针,指向null,然后进行反转,头结点在反转过后一定是指向null的,所以第一次反转让cur改变指向,指向pre也就是null,然后cur指针向下移位,并且pre也向下以为,cur移动到值为2的结点处,pre移动到值为1的结点处,然后在让cur指向pre,也就是第二个结点的next就变为第一个结点,一直遍历下去,直到结束,也就完成了反转。

image.png

通过图也能够看出来,遍历的结束时刻为 cur 指针指向 null 的时候

需要注意的是,在cur要指向结点二的时候,上图中也能够看的出来,因为结点一的next已经指向null了那么cur要如何去找到结点二呢,它们之前原本存在的联系next已经被修改了,所以说,我们需要一个临时存储结点的参数,在一开始的时候,优先把当前结点的next结点保存下来,这样就可以在cur指向pre之后,为cur提供下一个遍历值。

那么综上所述,反转链表的双指针写法就已经可以很简单的完成了。

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     val: number
 *     next: ListNode | null
 *     constructor(val?: number, next?: ListNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.next = (next===undefined ? null : next)
 *     }
 * }
 */
function reverseList(head: ListNode | null): ListNode | null {
    let preListNode | null = null
    let curListNode | null = head
    let temListNode | null
    while (cur) {
        tem = cur.next;
        cur.next = pre;
        pre = cur;
        cur = tem;
    }
    return pre;
};

image.png

递归

递归的思路其实和双指针是完全一样的,只不过是把双指针的比较容易看懂的写法,给改成了递归,我们可以根据上面的双指针的实现过程来一步步实现递归写法。

首先递归就需要一个递归函数,我们定义为 reverse,既然要在这个递归函数里面反转链表,那么它也需要转入cur和pre两个指针,在上面的定义当中,cur的初始值为head头结点,pre为null。

那么我们就定义好了递归的初始传入参数

function reverseList(head: ListNode | null): ListNode | null {
   const  reverse = (cur, pre) => {
	   ...
   }
   return reverse(head,null)
};

并且前面的遍历结束条件为cur结点为null的时候,那么递归中的结束条件就也可以写出来了。

然后也是一样在递归当中去保存临时结点,并且进行方向改变。

function reverseList(head: ListNode | null): ListNode | null {
   const  reverse = (cur, pre) => {
	    if (cur === nullreturn pre;
        let temListNode | null = cur.next;
        cur.next = pre;
        pre = cur;
        cur = tem;
        return reverse(cur, pre);
   }
   return reverse(head,null)
};

这道到这里结束了,简单的把上面的双指针写法改为了递归,先理解双指针的写法思路再照着写递归会发现很简单。