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
}
// 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
}
// 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}
temp := pre
for temp.Next != nil && temp.Next.Next != nil {
a := temp.Next
b := temp.Next.Next
temp.Next = b
a.Next = b.Next
b.Next = a
temp = a
}
return pre.Next
}

兩兩翻轉,畫這個圖一目了然!!!
1.平時刷題一定要總結歸納,最好分類。比如關於樹的題型,鏈表的,數組等等,觀察它們的解題思路,總結出解題套路。
2.積累工具類算法。什么叫工具類算法?就是你解一道算法題需要用到另一種算法,這個被調用的算法就是解決這道算法題的工具。比如常見的「深度優先遍歷」、「廣度優先遍歷」、「01背包」、「KMP算法」以及常見的選擇和排序算法都是經常使用的工具類算法。
3.學會抽象題目。筆試算法題不同於面試算法,不會直白跟你說要使用哪種算法去解答,更多的要自己學會抽象,拋開題目本身,要明白內部講的是什么,別被題目的糖衣炮彈迷惑了。只有把題目抽象成最原始的算法你才能更好地使用工具類算法進行解答。
劍指offer算法---Go實現
這下面也有一些經典的題目:
https://segmentfault.com/a/1190000020062117
