还有一些题目是没有特别复杂的技巧,但是考察仔细程度以及基本的链表处理能力,从而考察在基本的逻辑处理上考虑的全面和细致层度。这类题目对于业务代码编码能力的鉴定的确是有一些帮助。
分隔链表(86)
题目:86. 分隔链表
把大于等于的放到一个新的链表上,当然第一个可能也被操作,直接用虚拟头节点,最后链表拼接,记得确保新链表的尾部是nil
新链表采用尾插法实现保障顺序
旧链表记得手摸模拟去掉一个节点的逻辑
func partition(head *ListNode, x int) *ListNode {dummyHead := &ListNode{Next:head}p := dummyHeadnewDummyHead := &ListNode{}pNew := newDummyHeadfor p.Next != nil {if p.Next.Val >= x {cur := p.Nextp.Next = p.Next.Next// 接到新的后面pNew.Next = curpNew = pNew.Next} else {p = p.Next}}pNew.Next = nilp.Next = newDummyHead.Nextreturn dummyHead.Next
}
奇偶链表(328)
题目:328. 奇偶链表
这个题目也是模拟其实和分割链表一样
使用尾插法,使用两个虚拟头,一个用来删除,一个用来尾插,最后记得封尾,然后再接上即可
本题和分割链表,都需要考虑一下移除一个节点之后,当前指针的位置
func oddEvenList(head *ListNode) *ListNode {dummyHead := &ListNode{Next:head}p1 := dummyHeaddummyHeadNew := &ListNode{}p2 := dummyHeadNewflag := false// 奇在前,第一个是奇for p1.Next != nil {if flag {// 从添加到p2 从p1删除cur := p1.Nextp1.Next = p1.Next.Nextp2.Next = curp2 = p2.Next} else {p1 = p1.Next}flag = !flag}p2.Next = nilp1.Next = dummyHeadNew.Nextreturn dummyHead.Next
}
复制带随机指针的链表(138)
题目:138. 随机链表的复制
这个也属于技巧,用map 新节点的Random存放复制时旧的Random,map 存放旧节点和新节点的映射关系
得用尾插法,所以要用虚拟头
func copyRandomList(head *Node) *Node {dummyHead := &Node{}p := dummyHeadm := make(map[*Node]*Node)for head != nil {cur := &Node{Val:head.Val,Random:head.Random,}m[head] = curhead = head.Nextp.Next = curp = p.Next}p = dummyHead.Nextfor p != nil {p.Random = m[p.Random]p = p.Next}return dummyHead.Next
}
两数相加(445)
题目:445. 两数相加 II
因为相加需要考虑进位问题,链表倒着走有很费劲,所以先将链表翻转,求和之后再翻转回来
求和就有很多细节要处理,进位问题、两个链表剩余的数量
最后的进位是1的情况
func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {// 将两个加到l1上l1 = reverse(l1)l2 = reverse(l2)// 1 2 3// 4 9 // 5 1 4l1 = &ListNode{Next:l1}p1 := l1p2 := l2rest := 0for p1.Next != nil && p2 != nil {sum := p1.Next.Val + p2.Val + restif sum >= 10 {rest = 1sum -= 10} else {rest = 0}p1.Next.Val = sump1 = p1.Nextp2 = p2.Next}if p2 != nil {fmt.Println(p2.Val)p1.Next = p2 // 写到这里发现不好接了,所以需要保留p1 不为nil,所以上面处理要用p1.Next,所以l1要用虚拟头}// 剩余的继续for p1.Next != nil {sum := p1.Next.Val + restif sum >= 10 {rest = 1sum -= 10} else {rest = 0}p1.Next.Val = sump1 = p1.Next}if rest != 0 {p1.Next = &ListNode{Val:rest}}return reverse(l1.Next)
}
func reverse(head *ListNode) *ListNode {var newNode *ListNodefor head != nil {cur := headhead = head.Nextcur.Next = newNodenewNode = cur}return newNode
}
LRU
题目:146. LRU 缓存
LRU:最近最久未使用的会被淘汰–>最近使用的优先级要高–>使用链表记录优先级,前面的优先级高
–>使用双向链表,便于处理–>使用虚拟头、尾部,处理没有节点是的特殊情况,这样就没有特殊情况了。
使用Map实现O1查找
Get时如果存在,那就需要判断如果不在最前面,就需要移动到最前面
Put如果存在,则更新值,同时移动到最前面,如果不存在,则构建,之后放到最前面,同时需要先判断如果已经满了则需要链表中删除、清理map
移动到最前面:这里采用先从原位置删除,再放到最前面的方式
type Node struct {Key intVal intPre *NodeNext *Node
}type LRUCache struct {Cap intm map[int]*NodedummyHead *NodedummyTail *Node
}func Constructor(capacity int) LRUCache {lru := LRUCache{Cap:capacity,m:make(map[int]*Node, capacity),dummyHead:&Node{},dummyTail:&Node{},}lru.dummyHead.Next = lru.dummyTaillru.dummyTail.Pre = lru.dummyHeadreturn lru
}func (this *LRUCache) Get(key int) int {node, ok := this.m[key]if !ok {return -1}this.MoveToHead(node)return node.Val
}func (this *LRUCache) Put(key int, value int) {node, ok := this.m[key]if ok {node.Val = valuethis.MoveToHead(node)return}node = &Node{Val:value,Key:key,}if len(this.m) >= this.Cap {lastNode := this.dummyTail.Predelete(this.m, lastNode.Key)this.removeNodeFromList(lastNode)}this.m[node.Key] = nodethis.addNodeToHead(node)
}func (this *LRUCache) MoveToHead(node *Node) {if node.Pre == this.dummyHead {return}this.removeNodeFromList(node)this.addNodeToHead(node)
}func (this *LRUCache) addNodeToHead(node *Node) {// h,2 -- > h,1,2node.Next = this.dummyHead.Nextnode.Pre = this.dummyHeadthis.dummyHead.Next = nodenode.Next.Pre = node
}func (this *LRUCache) removeNodeFromList(node *Node) {// 1 2 3 --> 1 3node.Pre.Next = node.Nextnode.Next.Pre = node.Pre
}