前言:Golang编写
rank:323725
leetcode
题目:82. 删除排序链表中的重复元素 II
解法:时间复杂度:O(n),空间复杂度:O(1),代码如下↓
逻辑说明:
- 当头结点元素为空或者个数为1时,直接返回head即可,我们创建一个ans节点,让head节点做他的下一个元素,因为head.Val有可能是重复的元素,这个时候就需要把头结点删掉,但是节点自己是不是把自己删掉的,所以需要借助其上一个节点,也就是咱们所创建的那一个ans节点
- 先说下结果,我们最后是需要返回ans.next的,因为ans节点的头结点使我们自己创建爱你的,本身是没有价值的,只供我们删除其他元素使用。
- 既然我们需要返回ans.next,那么我们不需要让ans来移动,应该创建一个变量,使其等于ans,然后通过cur来依次向右进行遍历查重。
- 因为我们要比较cur.next.val和cur.next.next.val进行比较,所以这两者不能为nil,所以这也就成为了我们for循环的条件,然后下面就开始比较,如果相等,那么就一直遍历到两者不相等为止,同时cur的指向也会一直往右走,如果不相等,那就直接遍历下一个,直到cur.next.next为nil或者cur.next为nil。
- 最后返回头结点的下一个节点。
func deleteDuplicates(head *ListNode) *ListNode {
if head == nil || head.Next == nil {
return head
}
// 到这里的话,那么链表里至少有两个节点
ans := &ListNode{
Val: 0,
Next: head,
}
cur := ans
for cur.Next != nil && cur.Next.Next != nil {
if cur.Next.Val == cur.Next.Next.Val {
x := cur.Next.Val
for cur.Next != nil && cur.Next.Val == x {
cur.Next = cur.Next.Next
}
} else {
cur = cur.Next
}
}
return ans.Next
}
题目:剑指 Offer II 007. 数组中和为 0 的三个数
解法:时间复杂度:O(nnlog n),空间复杂度:O(n*n)
逻辑说明:
我写的这个其实代码比较长,但是思路不是很难:
- 首先将数组进行排序,利用双指针(lp, rp)记录遍历的位置
- 因为题意上说是找出三个数,和为0,那么双指针各指向一个元素,其余的那个元素必然在(lp, rp)范围之内,因为我们再上一步已经对数组排过序了,所以这个时候就可以直接用二分来搜索 0 - (nums[lp] + nums[rp])。
- 然后将二维数组排序,方便之后去重。
func threeSum(nums []int) [][]int {
var ans [][]int
if len(nums) < 3 {
return [][]int{}
}
sort.Ints(nums)
lp, rp := 0, len(nums)-1
for lp < rp {
sub := 0 - (nums[rp] + nums[lp])
var elem []int
// 二分查找
left, right := lp+1, rp-1
for left <= right {
mid := (left + right) / 2
// 找到之后, 存进elem数组里,然后再将elem存进ans二维数组里
if nums[mid] == sub {
elem = append(elem, nums[lp], nums[mid], nums[rp])
ans = append(ans, elem)
break
} else if nums[mid] < sub {
left = mid + 1
} else {
right = mid - 1
}
}
// 筛选部分重复值
for lp < rp && nums[lp] == nums[lp+1]{
lp++
}
lp++
// 当lp 到达 rp-1时, rp--(最后一个值已经用完了),lp重新从0开始
if lp >= rp-1 {
lp = 0
rp--
}
}
// 给二维数组进行排序,按照每一个ans[i]的三个值从小到大排序,为了方便去重
sort.Slice(ans, func(i, j int) bool {
for k := 0; k < 3; k++ {
if ans[i][k] != ans[j][k] {
return ans[i][k] < ans[j][k]
}
}
return i < j
})
// 取出ans数组中重复的值
for i := 0; i < len(ans)-1; i++ {
flag := true
for j := 0; j < 3; j++ {
if ans[i][j] != ans[i+1][j] {
flag = false
break
}
}
if flag {
ans = append(ans[:i], ans[i+1:]...)
i--
}
}
return ans
}
题目:剑指 Offer II 008. 和大于等于 target 的最短子数组
解法:时间复杂度:O(n),空间复杂度:O(n)
逻辑说明:
利用前缀和和双指针解出来,求前缀和的目的就是之后,我们利用前后值求差然后和target进行比较的形式,如果大于等于target,那么返回前后值的索引的差,即中间隔了多少个元素,即子数组的长度,一直遍历完,取满足题意的最小长度值。
func minSubArrayLen(target int, nums []int) int {
if len(nums) == 1 {
if nums[0] == target {
return 1
} else {
return 0
}
}
var arr = make([]int, len(nums)+1)
arr[0] = 0
for index, _ := range nums {
arr[index+1] = arr[index] + nums[index]
}
l, r, ans := 0, 1, 100001
for l <= r && r < len(arr) {
if arr[r]-arr[l] >= target {
if r-l < ans {
ans = r - l
}
l++
} else if arr[r]-arr[l] < target {
r++
}
}
if ans == 100001 {
ans = 0
}
return ans
}
题目:剑指 Offer II 009. 乘积小于 K 的子数组
解法:时间复杂度:O(n),空间复杂度:O(1)
逻辑说明:
前提要了解:
比如:数组:nums:[10, 5,1, 3], k = 200
10 小于 k,ans+=1,这一个包含10
10 * 5 小于 k,ans+=2,这两个包含 5 和 5*10
10 * 5 * 1小于 k,ans += 3,这三个包含5 * 10 * 1, 1,1 * 5
10 * 5 * 1 * 3小于k,ans+=4,这四个包含 3,3 * 1, 3 * 1 * 5, 3 * 1 * 5 * 10
找出规律来了吗?
题意上也是说的是连续子数组,所以考虑的情况比数组子序列少很多,那么我们首先第一步肯定是想着要依次求积然后和k进行比较如果小于k,则ans++,暴力解法,时间复杂度较高,下面用的是一种比较好用的解法:
- 边界条件:k 小于等于 1的时候,数组里面肯定没有符合题意的,题上说了数组里面都是正整数。
- 我们遍历数组,然后用prod乘以当前被循环的元素,之后和k进行比较,如果小于k,直接加索引值之间的差就行(right-left+1),但是如果大于或者等于k的话,那么就用prod相继除以开头的元素(因为开头的元素,咱已经统计过了),除完之后,left++,直到prod小于k为止,举个例子:当prod循环到了索引值为2的位置, 1052大于100,那么除以开头的元素,就是prod=52,left = 1,满足prod < k,退出内循环,则ans就加上了2-1+1=2,即包含了52和2,就是当几个正整数的乘积小于k的话,那么这几个正整数以及这里面所有的组合乘积都小于k。
func numSubarrayProductLessThanK(nums []int, k int) int {
if k <= 1 {
return 0;
}
prod, ans, left := 1, 0, 0
for right := 0; right < len(nums); right++ {
// 用prod来暂时存储相邻数的乘积
prod *= nums[right];
for prod >= k {
// 回溯prod
prod /= nums[left];
left++
}
ans += right - left + 1;
}
return ans;
}