2022年寒假算法练习第三天(1.12)

前言:Golang编写
rank:323725

leetcode

题目:82. 删除排序链表中的重复元素 II
题意
解法:时间复杂度:O(n),空间复杂度:O(1),代码如下↓

逻辑说明:

  1. 当头结点元素为空或者个数为1时,直接返回head即可,我们创建一个ans节点,让head节点做他的下一个元素,因为head.Val有可能是重复的元素,这个时候就需要把头结点删掉,但是节点自己是不是把自己删掉的,所以需要借助其上一个节点,也就是咱们所创建的那一个ans节点
  2. 先说下结果,我们最后是需要返回ans.next的,因为ans节点的头结点使我们自己创建爱你的,本身是没有价值的,只供我们删除其他元素使用。
  3. 既然我们需要返回ans.next,那么我们不需要让ans来移动,应该创建一个变量,使其等于ans,然后通过cur来依次向右进行遍历查重。
  4. 因为我们要比较cur.next.val和cur.next.next.val进行比较,所以这两者不能为nil,所以这也就成为了我们for循环的条件,然后下面就开始比较,如果相等,那么就一直遍历到两者不相等为止,同时cur的指向也会一直往右走,如果不相等,那就直接遍历下一个,直到cur.next.next为nil或者cur.next为nil。
  5. 最后返回头结点的下一个节点。
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)

逻辑说明:
我写的这个其实代码比较长,但是思路不是很难:

  1. 首先将数组进行排序,利用双指针(lp, rp)记录遍历的位置
  2. 因为题意上说是找出三个数,和为0,那么双指针各指向一个元素,其余的那个元素必然在(lp, rp)范围之内,因为我们再上一步已经对数组排过序了,所以这个时候就可以直接用二分来搜索 0 - (nums[lp] + nums[rp])。
  3. 然后将二维数组排序,方便之后去重。
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++,暴力解法,时间复杂度较高,下面用的是一种比较好用的解法:

  1. 边界条件:k 小于等于 1的时候,数组里面肯定没有符合题意的,题上说了数组里面都是正整数。
  2. 我们遍历数组,然后用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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值