链表类算法题

Posted Kris_u

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了链表类算法题相关的知识,希望对你有一定的参考价值。

1、反转链表leetcode 206:

// ListNode Definition for singly-linked list.
type ListNode struct 
    Val  int
    Next *ListNode


func reverseList(head *ListNode) *ListNode 
    if head == nil || head.Next == nil 
        return head
    

    var prev *ListNode
    cur := head
    for cur != nil 
        cur.Next, prev, cur = prev, cur, cur.Next
    
    return prev

2、将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

递归实现: 

func mergeLists(l1,l2 ListNode) 
    //1.递归终止条件
    if(l1 == nil)return l2 
    if(l2 == nil)return l1
    //2.递归合并
    if(l1.val < l2.val)
        l1.next = mergeTwoLists(l1.next,l2)  //对l1.next, l2为头节点的两个链表进行合并
        return l1
    else
        l2.next = mergeTwoLists(l2.next,l1) //对l2.next, l1为头节点的两个链表进行合并
        return l2
    

3、234leetcode   :判断链表是否为回文链表

/***/
 // Definition for singly-linked list.
  type ListNode struct 
      Val int
      Next *ListNode
  
 
func isPalindrome(head *ListNode) bool 
	if head == nil || head.next ==nil 
		return true
	
	firstEnd := endOfFirstHalfList(head)
	//后半段反转列表
	reverseHead := reverseList(firstEnd.Next)
	//回文序列的后半段反转之后跟前半段是相同的
	p1 := head
	p2 := reverseHead
	for p2 != nil 
		if p1.Val != p2.Val 
			return false
		
		p1 = p1.Next
		p2 = p2.Next
	
	return true


//慢指针走到列表的一半时,快指针则走到了列表末尾;那么,返回的slow就是列表的前半段
func endOfFirstHalfList(head *ListNode) *ListNode 
	slow := head
	fast := head
	for fast.Next != nil && fast.Next.Next != nil 
		fast = fast.Next.Next
		slow = slow.Next
	
	return slow

//反转列表
func reverseList(head *ListNode) *ListNode 
	var prev *ListNode = nil
	current := prev
	current = head
	for current != nil 
		current.Next, prev, current = prev, current, current.Next
	
	return prev
package main

import "fmt"

func main() 
	head := new(ListNode)
	head.Val = 1
	ln2 := new(ListNode)
	ln2.Val = 2
	ln3 := new(ListNode)
	ln3.Val = 1
	// ln4 := new(ListNode)
	// ln4.Val = 1
	head.Next = ln2
	ln2.Next = ln3
	// ln3.Next = ln4
	fmt.Println("head", head)
	fmt.Println("head", head.Next)
	fmt.Println("head", head.Next.Next)
	fmt.Println("head", head.Next.Next.Next)

	head1 := new(ListNode)
	head1.Val = 1
	node2 := new(ListNode)
	node2.Val = 4
	node3 := new(ListNode)
	node3.Val = 5
	head1.Next = node2
	node2.Next = node3
	fmt.Println("head1", head1)
	fmt.Println("head1", head1.Next)
	fmt.Println("head1", head1.Next.Next)

	fmt.Println("isPalindrome:", isPalindrome(head))
	fmt.Println("isPalindrome:", isPalindrome(head1))


输出:
head &1 0xc0000881f0
head &2 0xc000088200
head &1 <nil>
head <nil>
head1 &1 0xc000088250
head1 &4 0xc000088260
head1 &5 <nil>
isPalindrome: true
isPalindrome: false

 ​​​​​​25. K 个一组翻转链表 - 力扣(LeetCode) (leetcode-cn.com)

func reverseKGroup(head *ListNode, k int) *ListNode 
	dummy := new(ListNode)
	dummy.Val = 0
	dummy.Next = head

	pre := dummy // 链表头节点head之前加一个dummy节点
	end := dummy

	for end.Next != nil 
		//开始pre、end处于同一个节点,end指针前进K个节点,通过for循环实现
		for i := 0; i < k && end != nil; i++ 
			end = end.Next
		
		if end == nil 
			break
		
		start := pre.Next //start表示子链表的头节点
		next := end.Next  //next表示下一个子链表的头节点(每个子链表有K个节点)
		end.Next = nil    //
		pre.Next = reverseList(start)
		//设置新的pre、end
		start.Next = next
		pre = start
		end = pre
	
	return dummy.Next

链表排序:归并排序思想

func sortList(head *ListNode) *ListNode 
	left, right := cutList(head)
	// 单个的链表不再切割 否则会一直切下去
	if left != nil && left.Next != nil
		left = sortList(left)
	

	if right != nil && right.Next != nil
		right = sortList(right)
	
	return mergeList(left, right)


// 利用快慢指针 找到链表中点,对半切分为两个链表
func cutList(head *ListNode)(*ListNode, *ListNode)
	if head == nil
		return nil, nil
	

	fast := head.Next
	slow := head

	for fast != nil && fast.Next != nil
		fast = fast.Next.Next
		slow = slow.Next
	

	slowNext := slow.Next  //low.Next为后半个子链表第一个节点(头部)
	slow.Next = nil   //slow是前半个子链表的尾部,则其Next应为nil

	return head, slowNext


func mergeList(list1 *ListNode, list2 *ListNode) *ListNode 
	if list1 == nil 
		return list2
	 else if list2 == nil 
		return list1
	

	var head = new(ListNode)
	var cur = head

	for list1 != nil && list2 != nil 
		if list1.Val < list2.Val 
			cur.Next = list1
			list1 = list1.Next
		 else 
			cur.Next = list2
			list2 = list2.Next
		
		cur = cur.Next
	

	if list1 == nil 
		cur.Next = list2
	 else 
		cur.Next = list1
	

	return head.Next

4、2. 两数相加 - 力扣(LeetCode) (leetcode-cn.com) 

 给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

/**
 * Definition for singly-linked list.
 * type ListNode struct 
 *     Val int
 *     Next *ListNode
 * 
 */
func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode 
    result := &ListNode
    tmp := result
    sur := 0
    for l1 != nil || l2 != nil 
        sum := 0
        tmp.Next = &ListNode
        if l1 != nil 
            sum += l1.Val
            l1 = l1.Next
        
        if l2 != nil 
            sum += l2.Val
            l2 = l2.Next
        
        tmp.Next.Val = (sum+sur) % 10
        sur = (sum+sur) / 10

        tmp = tmp.Next
    

    if sur != 0 
        tmp.Next = &ListNode
        tmp.Next.Val = sur
    
    return result.Next

5、判断链表是否有环

思路:快指针一次走两步,慢指针一次走一步,如果链表有环,那么两个指针始终会相遇。 

type ListNode struct 
        Data interface
        Next *ListNode


func HasCycle(head *ListNode) bool 
        if head == nil 
                return false
        

        fast, slow := head, head
        for fast != nil && slow != nil && fast.Next != nil 
                slow = slow.Next
                fast = fast.Next.Next
                if slow == fast 
                        return true
                
        
        return false

3、LRU缓存 leetcode

LRU,全称是 Least Recently Used,即最近最少使用。是一种缓存淘汰策略算法。

算法的思想:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。所以,当指定的空间已存满数据时,应当把最久没有被访问到的数据淘汰。

参考:
链接:https://leetcode-cn.com/problems/lru-cache/solution/jian-dan-shi-li-xiang-xi-jiang-jie-lru-s-

type Node struct 
	key, val   int
	prev, next *Node


func initNode(key, val int) *Node 
	return &Node
		key: key,
		val: va,
	


type DlinkList struct 
	head, tail *Node
	size       int //链表元素数


func initDlinkList() *DlinkList 
	dll := &DlinkList
		head: initNode(0, 0),
		tail: initNode(0, 0),
		size: 0,
	
	dll.head.next = dll.tail
	dll.tail.prev = dll.head
	return dll


//在链表尾部添加新节点
func (dl *DlinkList) addNode(x *Node) 
	x.next = dl.tail
	x.prev = dl.tail.prev
	dl.tail.prev.next = x
	dl.tail.prev = x
	dl.size++


//删除链表中的x节点
func (dl *DlinkList) del(x *Node) 
	x.prev.next = x.next
	x.next.prev = x.prev
	x.prev, x.next = nil, nil
	dl.size--


//删除链表中的第一个节点,并返回该节点
func (dl *DlinkList) delFirstnode() *Node 
	if dl.head.next == dl.tail.prev 
		return
	
	first := dl.head.next
	dl.del(first)
	return first


//双链表只能从尾部插入,尾部节点是最近使用的,靠近头部的数据是最久使用的
type LRUCache struct 
	capacity int
	keyMap   map[int]*Node
	cache    *DlinkList


func initLRUCache(capacity int) LRUCache 
	lru := LRUCache
		capacity: capacity,
		keyMap:   map[int]*Node,
		cache:    initDlinkList(),
	
	return lru


func (lru *LRUCache) makeRecentNode(key int) 
	node := lru.keyMap[key]
	lru.cache.del(node) //删除旧的key的节点
	lru.cache.addNode(node)  //添加到链尾

//在链表尾部添加新节点
func (lru *LRUCache) addRecentNode(key, value int) 
	node := initNode(key, value)
	lru.cache.addNode(node) //添加至双链表
	lru.keyMap[key] = node  //添加至字典


// 删除某一个 key 及对应元素
func (lru *LRUCache) delKey(key int) 
	node := lru.keyMap[key]
	lru.cache.del(node)     //删除双向链表中的key节点
	delete(lru.keyMap,key) //删除字典中的key节点


// 删除最近最少使用的元素
func (lru *LRUCache) deleteLRU() 
	// 链表的第一个就是最近最少使用的元素
	delFirstNode := lru.cache.delFirstnode()
	// 删除字典中的key节点
	delete(lru.keyMap, delFirstNode.key)


func (lru *LRUCache) Get(key int) int 
	node, exist := lru.keyMap[key]
	if !exist 
		return -1
	
	lru.makeRecentNode(key)
	return node.val


/*
①若节点key已经存在,删除旧的节点,再添加到链尾
②若节点不存在缓存容量已满,则删除使用最少的节点,添加到链尾
③链尾节点数据是最新的
*/
func (lru *LRUCache) Put(key, value int) 
	_, exist := lru.keyMap[key]
	if exist 
		lru.delKey(key) //删除旧节点
	 else 
		if lru.capacity == lru.cache.size 
			lru.deleteLRU()
		
	
	lru.addRecentNode(key, value) //链尾数据是最新的

3、全排列:

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

 本题的算法如下:
对于第一个数,可以采用任意数组中任意一个数作为开头,所以遍历数组取出一个数,这一步得到的中间结果就是遍历出的这个数,并且得到新的数组,就是原入口参数数组去除掉我们遍历出的数的数组,利用这个数组,再次遍历取出下一个数
如果入口参数的数组中不包含任何元素,那么说明我们把入口参数数组已经深度遍历完成,就记录这个结果
如果入口参数数组还包含元素,那么我们遍历入口参数数组中剩下的元素,依次取出一个元素追加到结果中,作为中间结果。 


func permute(num []int) [][]int 
	res := make([][]int, 0)
	tmp := make([]int, 0)
	res = recursion(num, tmp, res)
	return res


func recursion(num, tmp []int, res [][]int) [][]int 
	if len(num) == 0 
		res = append(res, tmp)
		return res
	
	fmt.Println("num", num)
	for index, value := range num 
		newNum := make([]int, len(num)-1)
		newTmp := append(tmp, value)
		//
		count := 0
		for i, v := range num 
			if index != i 
				newNum[count] = v
				count++
			
		
		res = recursion(newNum, newTmp, res)
	
	return res

以上是关于链表类算法题的主要内容,如果未能解决你的问题,请参考以下文章

链表类算法题

链表类算法题

链表类常见算法题总结

Leetcode练习(Python):链表类:第23题:合并K个排序链表:合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

Leetcode练习(Python):链表类:第206题:反转链表:反转一个单链表。

链表相关算法题