- Notifications
You must be signed in to change notification settings - Fork24
Open
Labels
Description
链表基础知识
数组想必大家都很熟悉,几乎我们每天都会操作它,我们可以对比数组来学习链表。
首先要明确的是,链表和数组的底层存储结构不同,数组要求存储在一块连续的内存中,而链表是通过指针将一组零散的内存块串联起来。可见链表对内存的要求降低了,但是随机访问的性能就没有数组好了,需要 O(n) 的时间复杂度。
下图中展示了单链表及单链表的添加和删除操作,其实链表操作的本质就是处理链表结点之间的指针
。
在删除链表结点的操作中,我们只需要将需要删除结点的前驱结点的 next 指针,指向其后继即可。这样,当前被删除的结点就被丢弃在内存中,等待着它的是被垃圾回收器清除。
为了更便于你理解,链表可以类比现实生活中的火车,火车的每节车厢就是链表的一个个结点。车厢之间相互连接,可以添加或者移除掉。春运时,客运量比较大,列车一般会加挂车厢。
链表的结点结构由数据域和指针域组成,在 JavaScript 中,以嵌套的对象形式实现。
{// 数据域val:1,// 指针域next:{val:2,next: ...}}
回到本题,先明确想要交换节点共需要有三个指针进行改变。
1.所以我们需要在链表头部添加一个哨兵节点
2.循环中首先操作三个指针完成节点交换
3.指针右移,进行下一对节点的交换
迭代 + 哨兵节点
constswapPairs=(head)=>{constdummy=newListNode(0);dummy.next=head;// 头部添加哨兵节点letprev=dummy;while(head&&head.next){constnext=head.next;// 保存 head.nexthead.next=next.next;next.next=head;prev.next=next;// 下面两个操作将指针更新prev=head;head=head.next;}returndummy.next;};
- 时间复杂度:O(n)
- 空间复杂度:O(1)
递归
如果你对递归还觉得掌握的不够透彻,可以移步我的这篇专栏你真的懂递归吗?
回到本题的递归解法:
1.写递归解法的话,老套路,先明确终止条件,链表中没有节点或只有一个节点时无法进行交换。
2.接下来递归的进行两两交换节点并更新指针关系。
3.返回新链表的头节点 newHead。
constswapPairs=function(head){// 递归终止条件if(head===null||head.next===null){returnhead;}// 获得第 2 个节点letnewHead=head.next;// 将第 1 个节点指向第 3 个节点,并从第 3 个节点开始递归head.next=swapPairs(newHead.next);// 将第 2 个节点指向第 1 个节点newHead.next=head;returnnewHead;}
- 时间复杂度:O(n)
- 空间复杂度:O(n)