经典算法-链表(golang)

type ListNode struct {
	Val int
	Next *ListNode
}

// 83. Remove Duplicates from Sorted List  删除有序链表中的重复元素
// 解题思路:相同的只改变指针指向,不同的才会移动当前的cur指针,cur作为当前判断的指针
// Input: 1->1->2			Output: 1->2
// Input: 1->1->2->3->3		Output: 1->2->3
func deleteDuplicates(head *ListNode) *ListNode {
	if head == nil {
		return head
	}
	cur := head		// cur指向头结点,而且改变cur指向不会影响到head的指向
	for cur.Next != nil {
		if cur.Val == cur.Next.Val {	// 当前节点的值等于下个节点的值
			cur.Next = cur.Next.Next	// cur指向下下个节点(cur指针不会移动)
		}else {	// 当前节点的值不等于下个节点的值
			cur = cur.Next		// 当前指针后移到下一个不同值的节点
		}
	}
	return head
}

// 876. Middle of the Linked List	找到链表的中间点
// 解题思路:快慢指针,从head开始跑,快指针结束时,返回慢指针
// Input: 1->2->3->4->5 	Output: 3
// Input: 1->2->3->4->5->6 	Output: 4
func middleNode(head *ListNode) *ListNode {
	slow, fast := head, head
	for fast != nil && fast.Next != nil {
		slow = slow.Next
		fast = fast.Next.Next
	}

	return slow
}

// 206. Reverse Linked List   翻转链表
// 解题思路:将链表分为两个部分:第一个节点和剩余节点
//Input: 1->2->3->4->5->NULL	Output: 5->4->3->2->1->NULL
func reverseList(head *ListNode) *ListNode {
	if head == nil || head.Next == nil {
		return head
	}
	var pre *ListNode
	cur := head
	for cur != nil {
		//pre, cur, cur.Next = cur, cur.Next, pre //这句话最重要
		nextNode := cur.Next	// 先保存cur后面的节点
		cur.Next = pre			// 将cur指向pre,pre刚开始为nil,就相当于最后一位
		pre = cur				// 这时cur是最新的整个需要被链接的部分,这时赋值给pre,pre就是每次要被cur链接的
		cur = nextNode			// 继续处理后面的节点
	}
	return pre
}

// 反转从位置m到n的链表
func reverseBetween(head *ListNode, left int, right int) *ListNode { dummy := &ListNode{-1, head} prem := dummy for i := 0; i < left-1; i++ { prem = prem.Next } var pre *ListNode cur := prem.Next for i := left; i <= right; i++ { nextNode := cur.Next cur.Next = pre pre = cur cur = nextNode } // prem.Next为left节点,原始left节点应该指向right+1节点(left节点的下一个点应该指向翻转链表的尾节点) 画图最清晰!!! prem.Next.Next = cur prem.Next = pre // prem指向pre(反转之后的头节点) return dummy.Next }

 

// k个一组翻转链表(hard)
func reverseKGroup( head *ListNode ,  k int ) *ListNode {
    var pre, nextNode *ListNode
    cur, p := head, head
    index := 0
    for ; p != nil && index < k; index++ {
        p = p.Next
    }
    if index == k {
        for i := 0; cur != nil && i < k; i++ {
            nextNode = cur.Next
            cur.Next = pre
            pre = cur
            cur = nextNode
        }
    // 尾节点不为空,继续递归地执行上述过程
        if nextNode != nil {
            head.Next = reverseKGroup(nextNode, k)
        }
        return pre    // pre记录了为子链表翻转后的头结点
    } else {
        return head    // 子链表长度不足k,不翻转
    }
}

 

// 141. Linked List Cycle  	判断链表是否有环
// 解题思路:快慢指针,从head开始跑,快指针结束前,一直判断slow == fast
// Input: head = [3,2,0,-4], pos = 1 	Output: true
// Input: head = [1,2], 	 pos = 0 		Output: true
func hasCycle(head *ListNode) bool {
	if head == nil {
		return false
	}
	slow, fast := head, head
	for fast != nil && fast.Next != nil {
		slow = slow.Next
		fast = fast.Next.Next
		if slow == fast {
			return true
		}
	}
	return false
}

// 237. Delete Node in a Linked List	删除某个节点
// 解题思路:相当于用下个节点来替换当前节点
// Input: head = [4,5,1,9], node = 5		Output: [4,1,9]
// Input: head = [4,5,1,9], node = 1 	Output: [4,5,9]
func deleteNode(node *ListNode) {
	node.Val = node.Next.Val
	node.Next = node.Next.Next
}

// 203. Remove Linked List Elements	删除所有等于这个值的节点
// 解题思路:注意如何删除头结点,所以需要额外创建一个节点p,指向头结点,头结点是第一结点,只是一般没有数据
// Input: 1->2->6->3->4->5->6, val = 6	Output: 1->2->3->4->5
func removeElements(head *ListNode, val int) *ListNode {
	p := &ListNode{-1, head}
	cur := p
	for cur.Next != nil {
		if cur.Next.Val == val {	// 只要等于val,都要跳过该节点,cur此时不会移动,因为下一次判断都是cur.Next
			cur.Next = cur.Next.Next
		}else {
			cur = cur.Next			// 不等的时候,cur后移一步
		}
	}
	return p.Next					// 不能return head, 因为head有可能就是要删除的节点
}

// 234. Palindrome Linked List	判断是否为回文链表
// 解题思路:找到中心点,如果中心点是奇数需要+1(不需要比较这个节点),然后将链表后半段翻转和前半段进行比较(后半段链表个数作循环条件)
// Input: 1->2			Output: false
// Input: 1->2->2->1	Output: true
func isPalindrome(head *ListNode) bool {
	dummyP, midP := head, head
	for dummyP != nil && dummyP.Next != nil {
		dummyP = dummyP.Next.Next
		midP = midP.Next
	}
	// 如果是奇数
	if dummyP != nil {
		midP = midP.Next
	}
	midP = reverseList(midP)
	for midP != nil {
		if head.Val == midP.Val {
			head, midP = head.Next, midP.Next
			continue
		}
		return false
	}
	return true
}

// 160. Intersection of Two Linked Lists	找到两个链表(没有环)的交叉点
// 解题思路:不能使用暴力破解法,循环遍历A,B。 应该两个指针一起走,短链表先到达终点,从那一刻开始算,长链表继续走直到终点同时长链表的头指针也在走,
// 等到终点的时候,长短链表的长度一样了,最后循环判断他们,只要有一个节点相等就ok了
// Input: intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
// Output: Reference of the node with value = 8
func getIntersectionNode(headA, headB *ListNode) *ListNode {
	curA, curB := headA, headB
	for curA != nil && curB != nil {
		curA = curA.Next
		curB = curB.Next
	}
	for curA != nil {
		curA = curA.Next
		headA = headA.Next
	}
	for curB != nil {
		curB = curB.Next
		headB = headB.Next
	}
	for headA != headB {
		headA = headA.Next
		headB = headB.Next
	}
	return headA
}

 

 

// 19. Remove Nth Node From End of List		删除倒数第n个节点
// 解题思路: 定义快慢指针,快的先走n步,然后快慢再一起走,直到快指针Next为空, 记得返回的是p.Next而不是head,因为head也有可能被删
// input: list: 1->2->3->4->5, and n = 2
// Output: 1->2->3->5
func removeNthFromEnd(head *ListNode, n int) *ListNode {
	p := &ListNode{-1, head}
	slow, fast := p, p
	for ; n > 0; n-- {
		fast = fast.Next
	}
	for fast.Next != nil {
		slow = slow.Next
		fast = fast.Next
	}
	slow.Next = slow.Next.Next
	return p.Next

}

// 142. Linked List Cycle II	找到链表中环的起点
// 方法一:解题思路:使用额外内存map,将链表的节点存进map,判断如果有相同的点,则返回节点(即为环的起点)
func detectCycle(head *ListNode) *ListNode {
	m := make(map[*ListNode]int)
	cur := head
	for ; cur != nil; cur = cur.Next {
		if _, ok := m[cur]; ok {
			return cur
		}
		m[cur] = 1
	}
	return nil
}
// 方法二:链表中环的入口结点
func detectCycle(head *ListNode) *ListNode {
    if head == nil {
        return nil
    }
    slow, fast := head, head
    for fast != nil && fast.Next != nil {
        slow, fast = slow.Next, fast.Next.Next
        if slow == fast {
            break
        }
    }
    slow = head
    for slow != fast {
        slow, fast = slow.Next, fast.Next
    }
    return slow

}

  

 

// 148. Sort List	链表排序
// 解题思路(需要额外内存):使用额外内存slice,将链表的节点存进slice,然后sort.Ints排序后,再回写到链表
func sortList(head *ListNode) *ListNode {
	if head == nil || head.Next == nil {
		return head
	}
	var list []int
	cur, cur1 := head, head
	for cur != nil {
		list = append(list, cur.Val)
		cur = cur.Next
	}
	sort.Ints(list)
	for _, v := range list {
		cur1.Val = v
		cur1 = cur1.Next
	}
	return head

}


// 148. Sort List	链表排序
// 解题思路(不需要额外内存):分治法-归并排序,需要用到递归
func sortList(head *ListNode) *ListNode {
	if head == nil || head.Next == nil {
		return head
	}
	var pre *ListNode
	slow, fast := head, head
	for fast != nil && fast.Next != nil {
		pre = slow
		slow, fast = slow.Next, fast.Next.Next
	}
	pre.Next = nil  // 中间截断,slow的前面一个节点作为head的结束点
	l := sortList(head)
	r := sortList(slow)
	return func (l, r *ListNode) *ListNode {
		list := &ListNode{}
		cur := list
		for l != nil && r != nil {
			if l.Val <= r.Val {
				cur.Next = l
				l = l.Next
			}else {
				cur.Next = r
				r = r.Next
			}
			cur = cur.Next
		}
		if l == nil {
			cur.Next = r
		}
		if r == nil {
			cur.Next = l
		}
		return list.Next
	}(l, r)
}

func mergeList(l, r *ListNode) *ListNode {
	list := &ListNode{}
	cur := list
	for l != nil && r != nil {
		if l.Val <= r.Val {
			cur.Next = l
			l = l.Next
		}else {
			cur.Next = r
			r = r.Next
		}
		cur = cur.Next
	}
	if l == nil {
		cur.Next = r
	}
	if r == nil {
		cur.Next = l
	}
	return list.Next

}


// 21. Merge Two Sorted Lists	合并两个有序链表
// 解题思路:新创建一个结构体,比较l1和l2大小,赋值给cur.Next,然后都后移一步
// Input: 1->2->4, 1->3->4
// Output: 1->1->2->3->4->4
func mergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode {
	head := &ListNode{}
	cur := head
	for l1 != nil && l2 != nil {
		if l1.Val <= l2.Val {
			cur.Next = l1
			l1 = l1.Next
		}else {
			cur.Next = l2
			l2 = l2.Next
		}
		cur = cur.Next
	}
	if l1 == nil && l2 != nil {
		cur.Next = l2
	}
	if l1 != nil && l2 == nil {
		cur.Next = l1
	}
	return head.Next
}

// 23. Merge k Sorted Lists 合并k个有序链表
// 解题思路:使用额外内存slice,遍历将链表的节点存进slice,然后sort.Ints排序后,再回写到链表
/*Input:
 [
	1->4->5,
	1->3->4,
	2->6
 ]
Output: 1->1->2->3->4->4->5->6
*/
func mergeKLists(lists []*ListNode) *ListNode {
	if len(lists) == 0 {
		return nil
	}
	var sliceList []int
	for _, v := range lists {
		for v != nil {
			sliceList = append(sliceList, v.Val)
			v = v.Next
		}
	}
	if sliceList == nil {
		return nil
	}
	sort.Ints(sliceList)
	head := &ListNode{}
	cur := head
	for k, v := range sliceList {
		cur.Val = v
		if k + 1 == len(sliceList) {
			cur.Next = nil
		}else {
			cur.Next = &ListNode{}
			cur = cur.Next
		}
	}
	return head
}
第二种方法:不使用额外内存
func mergeKLists(lists []*ListNode) *ListNode {
    if len(lists) == 0 {
        return nil
    }
    if len(lists) == 1 {
        return lists[0]
    }
    mid := len(lists) >> 1
    return mergeTwoList(mergeKLists(lists[:mid]), mergeKLists(lists[mid:]))
}

func mergeTwoList(l1, l2 *ListNode) *ListNode {
    head := &ListNode{}
    cur := head
    for l1 != nil && l2 != nil {
        if l1.Val < l2.Val {
            cur.Next = l1
            l1 = l1.Next
        }else {
            cur.Next = l2
            l2 = l2.Next
        }
        cur = cur.Next
    }
    if l1 == nil {
        cur.Next = l2
    }
    if l2 == nil {
        cur.Next = l1
    }
    return head.Next
}

 

// https://segmentfault.com/a/1190000020730451?utm_source=tag-newest
// 24. Swap nodes in Pairs 两两翻转,或者翻转链表中的某一段
// 解题思路: 处理前置节点pre和b.next,然反转a,b节点
// input: 1->2->3->4 
// output: 2->1->4->3
func swapPairs(head *ListNode) *ListNode {
	pre := &ListNode{-1, head}
	cur := pre
	for cur.Next != nil && cur.Next.Next != nil {
		a := cur.Next
		b := cur.Next.Next
		cur.Next = b
		a.Next = b.Next
		b.Next = a
		cur = a
	}
	return pre.Next
}

 

 

 两两翻转,画这个图一目了然!!!

 

1.平时刷题一定要总结归纳,最好分类。比如关于树的题型,链表的,数组等等,观察它们的解题思路,总结出解题套路。

2.积累工具类算法。什么叫工具类算法?就是你解一道算法题需要用到另一种算法,这个被调用的算法就是解决这道算法题的工具。比如常见的「深度优先遍历」、「广度优先遍历」、「01背包」、「KMP算法」以及常见的选择和排序算法都是经常使用的工具类算法。

3.学会抽象题目。笔试算法题不同于面试算法,不会直白跟你说要使用哪种算法去解答,更多的要自己学会抽象,抛开题目本身,要明白内部讲的是什么,别被题目的糖衣炮弹迷惑了。只有把题目抽象成最原始的算法你才能更好地使用工具类算法进行解答。

剑指offer算法---Go实现

这下面也有一些经典的题目:

https://segmentfault.com/a/1190000020062117

posted @ 2019-05-13 11:09  天之草  阅读(2162)  评论(0编辑  收藏  举报