链表题目之模拟类

还有一些题目是没有特别复杂的技巧,但是考察仔细程度以及基本的链表处理能力,从而考察在基本的逻辑处理上考虑的全面和细致层度。这类题目对于业务代码编码能力的鉴定的确是有一些帮助。

分隔链表(86)

题目:86. 分隔链表
把大于等于的放到一个新的链表上,当然第一个可能也被操作,直接用虚拟头节点,最后链表拼接,记得确保新链表的尾部是nil
新链表采用尾插法实现保障顺序
旧链表记得手摸模拟去掉一个节点的逻辑

func partition(head *ListNode, x int) *ListNode {
	dummyHead := &ListNode{Next:head}
	p := dummyHead
	newDummyHead := &ListNode{}
	pNew := newDummyHead
	for p.Next != nil {
		if p.Next.Val >= x {
			cur := p.Next
			p.Next = p.Next.Next
			// 接到新的后面
			pNew.Next = cur
			pNew = pNew.Next
		} else {
			p = p.Next
		}
	}
	pNew.Next = nil
	p.Next = newDummyHead.Next
	return dummyHead.Next
}

奇偶链表(328)

题目:328. 奇偶链表
这个题目也是模拟其实和分割链表一样
使用尾插法,使用两个虚拟头,一个用来删除,一个用来尾插,最后记得封尾,然后再接上即可
本题和分割链表,都需要考虑一下移除一个节点之后,当前指针的位置

func oddEvenList(head *ListNode) *ListNode {
	dummyHead := &ListNode{Next:head}
	p1 := dummyHead
	dummyHeadNew := &ListNode{}
	p2 := dummyHeadNew
	flag := false
	// 奇在前,第一个是奇
	for p1.Next != nil {
		if flag {
			// 从添加到p2 从p1删除
			cur := p1.Next
			p1.Next = p1.Next.Next

			p2.Next = cur
			p2 = p2.Next

		} else {
			p1 = p1.Next
		}
		flag = !flag
	}
	p2.Next = nil
	p1.Next = dummyHeadNew.Next

	return dummyHead.Next
}

复制带随机指针的链表(138)

题目:138. 随机链表的复制
这个也属于技巧,用map 新节点的Random存放复制时旧的Random,map 存放旧节点和新节点的映射关系
得用尾插法,所以要用虚拟头

func copyRandomList(head *Node) *Node {
    dummyHead := &Node{}
    p := dummyHead
    m := make(map[*Node]*Node)
    for head != nil {
    	cur := &Node{
    		Val:head.Val,
    		Random:head.Random,
    	}
    	m[head] = cur
    	head = head.Next
    	p.Next = cur
    	p = p.Next
    }
    p = dummyHead.Next
    for 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 4
	l1 = &ListNode{Next:l1}
	p1 := l1
	p2 := l2
	rest := 0
	for p1.Next != nil && p2 != nil {
		sum := p1.Next.Val + p2.Val + rest
		if sum >= 10 {
			rest = 1
			sum -= 10
		} else {
			rest = 0
		}
		p1.Next.Val = sum
		p1 = p1.Next
		p2 = 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 + rest
		if sum >= 10 {
			rest = 1
			sum -= 10
		} else {
			rest = 0
		}
		p1.Next.Val = sum
		p1 = p1.Next
	}
	if rest != 0 {
		p1.Next = &ListNode{Val:rest}
	}

	return reverse(l1.Next)
}
func reverse(head *ListNode) *ListNode {
	var newNode *ListNode
	for head != nil {
		cur := head
		head = head.Next
		cur.Next = newNode
		newNode = cur
	}
	return newNode
}

LRU

题目:146. LRU 缓存
LRU:最近最久未使用的会被淘汰–>最近使用的优先级要高–>使用链表记录优先级,前面的优先级高
–>使用双向链表,便于处理–>使用虚拟头、尾部,处理没有节点是的特殊情况,这样就没有特殊情况了。
使用Map实现O1查找
Get时如果存在,那就需要判断如果不在最前面,就需要移动到最前面
Put如果存在,则更新值,同时移动到最前面,如果不存在,则构建,之后放到最前面,同时需要先判断如果已经满了则需要链表中删除、清理map

移动到最前面:这里采用先从原位置删除,再放到最前面的方式

type  Node struct {
    Key int
    Val int
    Pre *Node
    Next *Node
}

type LRUCache struct {
    Cap int
    m map[int]*Node
    dummyHead *Node
    dummyTail *Node
}

func Constructor(capacity int) LRUCache {
    lru := LRUCache{
        Cap:capacity,
        m:make(map[int]*Node, capacity),
        dummyHead:&Node{},
        dummyTail:&Node{},
    }
    lru.dummyHead.Next = lru.dummyTail
    lru.dummyTail.Pre = lru.dummyHead
    return 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 = value
        this.MoveToHead(node)
        return
    }
    node = &Node{
        Val:value,
        Key:key,
    }
    if len(this.m) >= this.Cap {
        lastNode := this.dummyTail.Pre
        delete(this.m, lastNode.Key)
        this.removeNodeFromList(lastNode)
    }
    this.m[node.Key] = node
    this.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,2
    node.Next = this.dummyHead.Next
    node.Pre = this.dummyHead

    this.dummyHead.Next = node

    node.Next.Pre = node
}

func (this *LRUCache) removeNodeFromList(node *Node)  {
    // 1 2 3 --> 1 3
    node.Pre.Next = node.Next

    node.Next.Pre = node.Pre
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值