几种指针类型:
普通指针:两指针同一方向或不同方向
对撞指针:两只针互相靠拢
快慢指针:一块一慢
一:环形链表
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func hasCycle(head *ListNode) bool {
/**
分析题意:
题中的pos只是说明下 在 第几个做表有环交接,所以不要被迷惑,这里可以用快慢指针,一块一慢,有环的话必然会重合
**/
//开始
if head == nil || head.Next == nil {
return false
}
//定义快慢两个指针
fast := head.Next
slow := head
for fast != slow {
//快的结束,说明不是环形
if fast == nil || fast.Next == nil {
return false
}
//快走2步
fast = fast.Next.Next
//慢走一步
slow = slow.Next
}
return true
}
二:环形链表II
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func detectCycle(head *ListNode) *ListNode {
/**
思路分析:
先用快慢指针判断是不是环形链表,然后使用循环找到重复的节点
**/
//开始
//定义fast和slow
var fast, slow = head, head
if head == nil {
return nil
}
//fast == nil说明不是环形
for fast != nil {
//这里先判断slow,因为fase.next上面已经判断了,所以slow.Next目前肯定是存在的
slow = slow.Next
if fast.Next == nil {
return nil
}
fast = fast.Next.Next
if fast == slow {
//是环形链表
//开始找连接的节点
//这里已经定位到 当前slow是交接的地方,所以循环 head找到这个点就行
p := head
for p != slow {
p = p.Next
slow = slow.Next
}
return p
}
}
return nil
}
三:两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:
输入:nums = [3,3], target = 6
输出:[0,1]
func twoSum(nums []int, target int) []int {
/**
思路分析:
除了暴力解法,可以没循环一个,把差值存进map,然后看剩下的值里是不是在map里
**/
//开始
goal := 0
n := len(nums)
var memory = make(map[int]int, n)
var res = make([]int, 2)
//先写主干
for i:=0; i<n; i++ {
//对应 i 需求的 值
goal = target - nums[i]
//如果当前值的key 存在,说明memory[nums[i]]的值就是拿一个数的索引
if v,ok := memory[goal];ok {
res[0] = v
res[1] = i
return res
}
//需求是返回做标,所以每循环一次就把当前的值和索引存起来,如果后面的目标值已经存在就可以了
memory[nums[i]] = i
}
return res
}
四:三数只和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例 2:
输入:nums = []
输出:[]
示例 3:
输入:nums = [0]
输出:[]
func threeSum(nums []int) [][]int {
/**
思路分析:
两数只和,可以通过map存差值来实现,三数只和如果暴力其实就是3个for循环,不暴力,就是可以用差值来减少一层循环,然后利用双指针来进行计算
**/
//开始
var n = len(nums)
var res = make([][]int, 0)
//为了保证不重复,方便计算,需要排序
sort.Ints(nums)
//循环主体
for i:= 0; i<n; i++ {
//为了保证不重复,方便计算,需要排序
//针对 -1 -1 0 1 2,相同的话就会造成 -1 1 2 和下一个-1 1 2
if i > 0 && nums[i] == nums[i-1] {
continue
}
target := -nums[i]
//接下来就是判断两数只和 == target
//初始指针要在 a 的后面
first := i+1
last := n-1
if first > n-1 {
break
}
//最终就获得到了 nums[first] + nums[last] == target
//为了不重复需要first < last
for first < last {
//过滤重复的 比如 -1 -1 -1 2 这里 -1 + 2 = 1, 后面也是就是重复
if first > i+1 && nums[first] == nums[first - 1] {
//相同要继续前进
first++
continue
}
//因为排好序后,默认右边大于左边的,所以下面的情况一定是右边的要缩小
if nums[first] + nums[last] > target {
last--
} else if nums[first] + nums[last] == target {
//这是找到了一对,存起来
res = append(res, []int{nums[i], nums[first], nums[last]})
//****很重要,这里一定要 first++, 因为是排好序的,所以判断是否和前一个相等,一定是按照顺序前进,不能last--
first++
} else {
//反之左边增加
first++
}
}
}
return res
}
五:相交链表
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
自定义评测:
评测系统 的输入如下(你设计的程序 不适用 此输入):
intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
listA - 第一个链表
listB - 第二个链表
skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数
评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA 和 headB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案 。
示例 1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func getIntersectionNode(headA, headB *ListNode) *ListNode {
/**
思路分析:
不想交 A:a,B:b,想交 c
假设都走完 : A: a+c B : b+c
走完后 A走B 相交的距离 就是 a+c+b B走A相交的距离就是b+c+a
此时交点就是返回的值
**/
//开始
if headA == nil || headB == nil {
return nil
}
var a = headA
var b = headB
for a != b {
if a == nil{
a = headB
} else {
a = a.Next
}
if b == nil{
b = headA
} else {
b = b.Next
}
}
return a
}
六:链表的中间节点
给定一个头结点为 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例 1:
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
示例 2:
输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func middleNode(head *ListNode) *ListNode {
/**
思路分析:
一块一慢
快走2慢走1,快到终点,慢到中点
**/
if head == nil {
return head
}
slow := head
fast := head
//注意这个条件,一定要先是fast != nil,fast.Next不为Nil,不然会报错
for fast != nil && fast.Next != nil {
slow = slow.Next
fast = fast.Next.Next
}
return slow
}
七:合并两个有序数组(88)
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
示例 1:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。
示例 2:
输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
解释:需要合并 [1] 和 [] 。
合并结果是 [1] 。
func merge(nums1 []int, m int, nums2 []int, n int) {
/**
思路分析:
因为是非递减的,那最后一个最大,比较那个大放到最后一位就行,
**/
//开始
last := n+m-1
//我们的目的是把n放到m中,所以n还有就一直放,因为nums的长度是m+n所以不会越界
for n > 0 {
//m没有就只赋值n
//最大的m和n比较
if m == 0 || (nums1[m-1] <= nums2[n-1]) {
nums1[last] = nums2[n-1]
n -= 1
} else {
nums1[last] = nums1[m-1]
m -= 1
}
last -= 1
}
}