前言
第七天,开启新篇章--链表。数据结构与算法这门课学了过去太久了,所以写题之前得捡捡。上午睡觉,下午第一节课还是困,第二节课考试,晚上才开始学。
内容
一、移除链表元素
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
先解释一下递归和迭代
递归
它涉及到一个函数直接或间接地调用自身的过程。
优点包括代码简洁和可读性强,因为它可以直观地描述问题分解的过程。然而,不恰当地使用递归可能导致性能问题,因为它涉及到大量的函数调用和内存消耗。
迭代
通过重复步骤来解决问题。
迭代的优点在于其性能通常优于递归,因为它避免了大量的函数调用和内存消耗。然而,迭代的代码可能不如递归代码简洁和可读性强。
递归法
递归地调用removeElements函数,以head.Next作为新的head,并删除其所有值等于val的节点。然后,将返回的新链表赋值给head.Next
如果头节点的值等于val,则返回下一个节点作为新的头节点。这样就删除了当前的头节点。
//8ms 4.77MB
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func removeElements(head *ListNode, val int) *ListNode {
if head==nil{
return head
}
head.Next=removeElements(head.Next,val)
if head.Val==val{
return head.Next
}
return head
}
迭代法
创建一个新的链表节点dummyHead,并将其Next字段设置为当前链表的头节点head。这样,dummyHead就成为了新链表的头节点,它指向原始链表的头节点。
返回处理后的链表的头节点。由于我们在前面使用了一个虚拟头节点,所以返回的是dummyHead.Next而不是dummyHead。
//4ms 4.34MB 优解
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func removeElements(head *ListNode,val int)*ListNode{
dummyHead := &ListNode{}
dummyHead.Next = head
for cur:=dummyHead;cur.Next!=nil;{
if cur.Next.Val==val{
cur.Next=cur.Next.Next
}else{
cur=cur.Next
}
}
return dummyHead.Next
}
比较两段代码
一,注意Val和val
二,leetcode里,链表的结点都默认定义好了,直接用就行了,都没有注意到链表的结点是如何定义的。而在面试的时候,一旦要自己手写链表,就写的错漏百出。
三,一个是在原链表进行删除,此时需要单独考虑头结点的情况,一个是创建一个虚拟头结点。
四,当从链表中删除一个结点时,Go语言会自动进行内存回收。不需要显式地释放链表中删除节点的内存,使得Go语言的内存使用更加高效,并且减少了因手动管理内存而引起的错误。(有的语言是没有的)
二、反转链表
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
本题为后端高频面试题,收录于《热招技术岗上岸指南》
递归法
这里就用官方解释,方便理解 //0ms 2.65MB
func reverseList(head *ListNode) *ListNode {
if head==nil||head.Next==nil{
return head
}
newHead:=reverseList(head.Next)
head.Next.Next=head
head.Next=nil
return newHead
}
迭代法/双指针法
定义pre和cur两个指针,temp保存cur的下一个结点。
cur.Next=pre是关键步骤,cur的下一个结点设置为pre,就指向了原本的前一个结点。
仔细想想 想清楚 //0ms 2.35MB
func reverseList(head *ListNode) *ListNode {
var pre *ListNode
cur:=head
for cur!=nil{
temp:=cur.Next//保存cur的下一个节点 因为接下来要改变cur.next
//将cur.next 指向pre ,此时已经反转了第一个节点了
cur.Next=pre// 翻转操作
// 更新pre 和 cur指针
pre=cur
cur=temp
}
return pre//此时我们return pre指针就可以了,pre指针就指向了新的头结点
}
最后
链表确实不熟,怎么变换确实得想清楚。
想到一个点,短时间的努力,是体现不出你和别人的差距的。所以,一定要坚持,不要放弃!