算法集合-数组

算法集合-数组

数组: 是存放在连续内存空间上的相同数据类型的集合.

注意两点:

  • 数组的下表都是从0开始的
  • 数组的内存空间都是连续的

内存空间是连续的,所以数据查询数据的是非常快速的,只要知道开始数据的地址,加上索引,就是该元素的内存空间的位置.

力扣算法题
编号35: 搜索插入位置

题目:

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

你可以假设数组中无重复元素。

示例 1:
输入: [1,3,5,6], 5
输出: 2

示例 2:
输入: [1,3,5,6], 2
输出: 1

示例 3:
输入: [1,3,5,6], 7
输出: 4

示例 4:
输入: [1,3,5,6], 0
输出: 0

小编思路

func searchInsert(nums []int, target int) int {
	left := 0
	right := len(nums) - 1

	if nums[left] > target{
		return 0
	}
	if nums[right] < target {
		return right + 1
	}

	for {
		if left > right {
			return left
		}

		avg := (left + right) / 2
		if nums[avg] > target {
			right = avg - 1
		} else if nums[avg] < target {
			left = avg + 1
		} else {
			return avg
		}
	}

}
// 可以看出,是不断拼凑出来的,比较生硬

官方解题思路:

func searchInsertAnswer(nums []int, target int) int {
	/*
		解题分析:
			排序数组中寻找索引--二分法不二选择
		解题思路:
			假设这个插入的位置索引为pos
			它成立的条件是: nums[pos-1]<target<=nums[pos]
			所以这两个合并起来就是: 在一个有序的数组中, 找到第一个大于等于target的下标
	*/
	ans := len(nums)
	left, right := 0, ans-1

	for left <= right {
		// >>n: 右移n位, 就是除以2的n次方, 这里>>1, 相当于除于 2的1次方, 除于2
		mid := (right-left)>>1 + left

		if nums[mid] >= target {
			ans = mid
			right = mid - 1
		} else {
			left = mid + 1
		}
	}

	return ans
}

总结:
二分法对区间定义要相当清楚, 确定要查找的区间是左闭右开,还是左闭右闭. 这就是不变量
在循环过程中,坚持循环不变量的原则.

编号27: 移除元素

题目:

给你一个数组 nums和一个值 val,你需要 原地 移除所有数值等于val的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝
int len = removeElement(nums, val);

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
 	print(nums[i]);
}


示例 1:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
示例 2:

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。


提示:

0 <= nums.length <= 100
0 <= nums[i] <= 50
0 <= val <= 100

小编思路:

func removeElement(nums []int, val int) int {
	n := len(nums)
	b := 0
	for i, v := range nums{
		ok := true
		if val != v{
			b = i + 1
			continue
		}
		for j := n-1; j >i; j--{
			if nums[j] != val{
				nums[i], nums[j] = nums[j], nums[i]
				ok = false
				break
			}
		}
		if ok{
			b = i
			break
		}
	}
	return b
}
// 小编这里是如果遇到要删除的数据时,就从末尾开始循环到当前索引,遇到不是要移除的数据覆盖上去

官方解答: 双指针方法

func removeElementAnwser(nums []int, val int) int {
	j := 0
	for i, v := range nums{ // i==0
		if v != val{ // 如果不相等, 快指针就覆盖慢指针位置的值
			nums[j] = nums[i]
			j ++ // 这里是比快指针提前移动了一下 j == 1
		}
		// 所以如果i==1时, v和val相等了, j也是==1的, 所以j不用动, 等着下次被覆盖就行了
	}
	return j
}

//双指针法(快慢指针法):通过一个快指针和慢指针在一个for循环下完成两个for循环的工作

编号209: 长度最小的子数组
题目:

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

示例 1:

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例 2:

输入:target = 4, nums = [1,4,4]
输出:1
示例 3:

输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
 

提示:

1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105

小编思路

func minSubArrayLen(target int, nums []int) int {
	n := len(nums)
	i := 1
	for i <= n {
		for j, _ := range nums {
			if j+i > n {
				break
			}
			num := 0
			for k := j; k < j+i; k++ {
				num += nums[k]
				fmt.Println(j, i, num)
				if num >= target {
					return i
				}
			}
		}
		i++
	}
	return 0
}
// 从长度1开始到数组的长度, 一个值一个值往后排查

官方答案

func minSubArrayLen(target int, nums []int) int {
	n := len(nums)
	if n == 0 {
		return 0
	}
	ans := math.MaxInt32
	start, end, sum := 0, 0, 0
	for end < n {
		sum += nums[end]
		for sum >= target {
			ans = min(ans, end - start + 1)
			sum -= nums[start]
			start++
		}
		end++
	}
	if ans == math.MaxInt32 {
		return 0
	}
	return ans
}

func min(x, y int) int {
	if x < y {
		return x
	}
	return y
}

/* 
	滑动窗口: 相当于每次循环过程中, 固定长度的窗口向后移动, 
	然后判断是否需要变动起始位置, 也就是缩小窗口长度
	
	本题中, 窗口的条件就是, 是否其和是否满足>=target的最小连续子数组;
	起始位置: 如果大于s了, 就要向前移动了,就是缩小窗口了
	终止位置: 整个数组
*/

数组的经典题目
二分法
二分法时间复杂度o(logn)
二分法找到循环不变量, 只有在循环中坚持对区间的定义, 才能清楚的把握循环中的各种细节

双指针法
通过一个快慢指针在一个for循环中完成两个for循环的操作
双指针时间复杂度: o(n)

滑动窗口
滑动窗口时间复杂度: o(n)
滑动窗口的精妙之处在于根据当前子序列和大小情况,不断调节子序列的起始位置

模拟行为
模拟类的题目在数组中是很常见的,不涉及什么算法,就是单纯的模拟.
在这种题目当中,再一次介绍了循环不变量

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值