24. 两两交换链表中的节点
第一想法:看到提示说使用虚拟节点----那么为什么使用虚拟节点?--模拟节点交换的过程,看来是需要三个元素的关系的, 第一个节点指向 第三个节点,第三个节点指向第二个节点,第二个节点指向第三个节点的下一个节点。可以单独处理头结点,为了统一处理过程可以使用虚拟节点,让第一个节点当作第一轮的第一个节点
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func swapPairs(head *ListNode) *ListNode {
// 处理空链表和一个值的链表
if head == nil || head.Next ==nil{
return head
}
// 使用虚拟头结点方便一些
dummyHead := &ListNode{
Val: -99,
Next: head,
}
// 虚拟头结点,cur第一个节点
pre := dummyHead
cur := dummyHead.Next
for cur.Next != nil{
tmp := cur.Next.Next
pre.Next = cur.Next
cur.Next.Next = cur
cur.Next = tmp
pre = cur
cur = cur.Next
if cur == nil{
break
}
}
return dummyHead.Next
}
总结:使用虚拟节点;处理头结点的过程;循环结束的判断条件
19.删除链表的倒数第N个节点
第一想法:反转链表-删除第n个节点,再反转链表
其他的方法,如果是正序遍历 如何找到倒数第N个节点?想不到
看了讲解:使用双指针,两者之间相差n 然后fast指针指向最后一个节点的时候,slow另外一个指针就指向了倒数第N个节点。
写出了当两个指针初始指向头结点时,当删除第一个节点的时候会出错的代码,头结点不好处理,这时想到了虚拟节点。让两个指针初始指向虚拟节点。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func removeNthFromEnd(head *ListNode, n int) *ListNode {
if head == nil || head.Next==nil{
return nil
}
// 删除第一个节点的时候不好处理,使用虚拟节点
dummyHead := &ListNode{
Next: head,
}
slow , fast := dummyHead,dummyHead
for i:=1; i<= n;i++{
fast = fast.Next
}
for fast.Next != nil{
slow = slow.Next
fast = fast.Next
}
// 这时slow指向的是倒数第n个节点的前一个节点
slow.Next = slow.Next.Next
return dummyHead.Next
}
今日收获:删除单项链表中的倒数第n个链表,双指针可以实现,并且使用虚拟节点处理头节点的问题。
面试题 02.07. 链表相交
第一想法:第一感觉像是那种 双循环的暴力循环的题,
看了题解:需要从数学角度分析一下,因为相交及其之后的部分是相同的,所以如果两个链表相交其尾部一定有一段是共同的。
相交的情况:首先让其尾部对弃,两个指针指向两个链表重合的索引位置,判断两个位置是否是指向同一个元素,如果不是就移动到下一个,直到判断出出来指向同一个位置。
不相交的情况:任意一个链表为空的情况;指针走到最后一个元素也不是指向同一个位置
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func getIntersectionNode(headA, headB *ListNode) *ListNode {
// 先判断一下返回为nil的情况
// 两者不相交的情况下也是返回nil
nodeA := headA
lenA := 0
nodeB := headB
lenB := 0
for nodeA != nil{
lenA++
nodeA = nodeA.Next
}
for nodeB != nil{
lenB++
nodeB = nodeB.Next
}
// 指针初始位置
var offset int
var fast, slow *ListNode
if lenA > lenB{
fast = headA
slow = headB
offset = lenA-lenB
// for i:=1; i<=offset; i++{
// fast = fast.Next
// }
}else{
fast = headB
slow = headA
offset = lenB -lenA
}
for i:=1; i<=offset; i++{
fast = fast.Next
}
for fast != slow{
fast = fast.Next
slow = slow.Next
}
return fast
}
写后总结:如何让指针同时指向两个链表尾部对其后的初始位置,先计算出链表长度,两者的差值,让长度较长的链表的指针从头节点移动差值步,两个指针就对其了,然后进行循环判断两个指针是否指向同一个节点,灵感来自于 删除倒数第N个节点 中两个指针的操作 也是相差N个节点
最主要的还是数学思想,尾端对齐后开始遍历,节点相同的位置及其之后都是相同的
第一想法:遍历节点,判断该节点的下一个节点是否是指向了 前面的节点,返回指向的节点
可以用hash表存储已经遍历的节点,遍历到那个节点判断hash表中的是否存在;
如果发现某个节点指向了nil 说明这个链表没有环
但是这个方法肯定不是 空间复杂度为o(1)的方法
我会选择,slow遍历到一个节点,fast就继续遍历下面的节点 看他们是否指向他,如果遍历到最后都没有指向 就 slow下一个节点,fast接着便利--太暴力了 太不优雅了
看了题解之后:发现是一个数学题