数组算法

数组算法

1.删除有序数组中的重复项

/*
题目:有序数组,原地删除排序数组中的重复项
leetcode:26
输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
思路:第一个肯定不重复,从下标1开始遍历。当前值!=前一个值,证明不重复,下标index赋值,index++
*/
func removeDuplicates(nums []int) int {
	index := 1 //
	for i := 1; i < len(nums); i++ {
		if nums[i] != nums[i-1] {
			nums[index] = nums[i]
			index++
		}
	}
	return index
}

func main() {
	a := []int{1, 2, 3, 3, 4, 5, 6, 6, 7, 7, 8, 8}
	b := removeDuplicates(a)
	fmt.Println(a[:b])
	fmt.Println(b)
}

2.删除有序数组中的重复项 II

/*
题目:有序数组,原地删除排序数组中的重复项,使得出现次数超过两次的元素只出现两次
leetcode:80
输入:nums = [1,1,1,2,2,3]
输出:5, nums = [1,1,2,2,3]
思路:双指针方式,slow代表已经处理的数据索引下标 fast代表检查过的数据索引下标
slow前面数据全是合法的,fast前面的数据全是检查过的
*/
func removeDuplicates2(nums []int) int {
	n := len(nums)
	if n <= 2 {
		return n
	}
	slow, fast := 2, 2
	for fast < n {
		if nums[slow-2] != nums[fast] {
			nums[slow] = nums[fast]
			slow++
		}
		fast++
	}
	return slow
}
func RemoveDuplicates2Run() {
	a := []int{1, 1, 1, 2, 2, 3}
	b := removeDuplicates2(a)
	fmt.Println(a[:b])
	fmt.Println(a)
	fmt.Println(b)
}

3.轮转数组

/*
题目:轮转数组:
leetcode:189
给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]

思路:前后截断再拼接
*/
func rotate(li []int, n int) {
	lenth := len(li)
	n = n % lenth
	li1 := li[:lenth-n]
	li2 := li[lenth-n:]
	li2 = append(li2, li1...)

	for k, v := range li2 {
		li[k] = v
	}
	return
}
func main(){
  li:=[]int{1,2,3,4,5,6,7}
  rotate(li,3)
  fmt.Println(li)
}

/*
解法2:
思路:
第一步:反转整个数组
第二步:以k为界,划分分前后两个数组
第三步:反转前后两个数组
*/
func rotate2(nums []int, k int) {
	k%=len(nums)
	rever(nums)
	rever(nums[k:])
	rever(nums[:k])
}

func rever(nums []int) {
	for i,n := 0,len(nums); i < n/2; i++ {
		nums[i], nums[len(nums)-1-i] = nums[len(nums)-1-i], nums[i]
	}
}

func main(){
  li:=[]int{1,2,3,4,5,6,7}
  rotate2(li,3)
  fmt.Println(li)
}

4.合并两个有序数组

/*
题目:合并两个有序数组
leetcode:88
输入: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] 。

思路:把nums2拷贝到nums1后面的0位置,然后再排序下
*/
func merge(nums1 []int, m int, nums2 []int, _ int) {
	copy(nums1[m:], nums2)
	sort.Ints(nums1)
}

func MergeRun() {
	nums1 := []int{1, 2, 3, 0, 0, 0}
	m := 3
	nums2 := []int{2, 5, 6}
	n := 3
	merge(nums1, m, nums2, n)
	fmt.Println(nums1)
}

5.移除元素

/*
题目:移除元素
leetcode:27
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
思路:定义一个初始为0下标,每次检查不等于val就赋值,left++
*/
func removeElement(nums []int, val int) int {
	left := 0
	for _, v := range nums { // v 即 nums[right]
		if v != val {
			nums[left] = v
			left++
		}
	}
	return left
}

func main() {
	nums := []int{3, 2, 4, 3}
	val := 3
	k := removeElement(nums, val)
	fmt.Println(nums[:k])
	fmt.Println(k)
}

6.最长递增子序列

/*
题目:最长递增子序列
leetcode:
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
思路:
1.利用v在有序数组中应该存在的位置
2.下标位置=len(li) 证明应该追加在最后,否则就覆盖
[10]
[9]
[2]
[2 5]
[2 3]
[2 3 7]
[2 3 7 101]
[2 3 7 18]
*/
func sengthOfLIS(nums []int) []int {
	li := []int{}
	for _, v := range nums {
		temp := sort.SearchInts(li, v) //有序数组中v应该存在的位置索引下标
		if temp == len(li) {
			li = append(li, v) //放在最后一个,追加
		} else {
			li[temp] = v //当前索引下标位置赋值
		}
	}
	return li
}

func main() {
	li := []int{10, 9, 2, 5, 3, 7, 101, 18}
	l2 := sengthOfLIS(li)
	fmt.Println(l2)
}

7.两个数组的交集

/*
题目:两个数组的交集
leetcode:350
给你两个整数数组 nums1 和 nums2 ,请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值)。可以不考虑输出结果的顺序。
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]
思路:利用map存储num1中出现的次数,nums2检查map是否存在。如果存在则证明存在交集,添加新的数组中,并map次数减1
*/
func intersect(nums1 []int, nums2 []int) []int {
	if len(nums1) > len(nums2) {
		nums1, nums2 = nums2, nums1
		intersect(nums1, nums2)
	}
	// 此时nums1是短的
	m := map[int]int{}
	for _, v := range nums1 {
		m[v]++
	}
	li := []int{}
	for _, v := range nums2 {
		if m[v] > 0 {
			li = append(li, v)
			m[v]--
		}
	}
	return li
}

func main() {
	l1 := []int{1, 2, 2, 1}
	l2 := []int{2, 2}
	n := intersect(l1, l2)
	fmt.Println(n)
}

8.数组加一

/*
题目:数组加一
leetcode:66
输入:digits = [1,2,3]
输出:[1,2,4]
解释:输入数组表示数字 123。
输入:digits = [9,9,9]
输出:[1,0,0,0]
解释:输入数组表示数字 1000。

思路:当我们对数组digits加一时,我们只需要关注digits的末尾出现了多少个 9即可
分为三种情况
1.末尾!=9,直接末尾++。如:1,2,3变成1,2,4
2.末尾=9,末尾改为0,继续向前推进,前面一位++之后如果!=9,就返回。 如1,2,9变成1,3,0
3.末尾=9,向前推进全部都为9,当前位改为0,最后就为0,0,0,然后在最前面加1。如:9,9,9变为1000
*/
func plusOne(nums []int) []int {
	latest := len(nums) - 1
	if nums[latest] != 9 {
		nums[latest]++
		return nums
	}
	if nums[latest] == 9 {
		for i := latest; i >= 0; i-- {
			if i == 0 && nums[i] == 9 {
				nums[i] = 0
				nums = append([]int{1}, nums...)
				break
			}
			if nums[i] == 9 {
				nums[i] = 0
			} else {
				nums[i]++
				break
			}
		}
	}
	return nums
}

func main() {
	l1 := []int{9, 9, 9}
	n := plusOne(l1)
	fmt.Println(n)
}

9.移动0

/*
题目:移动0
leetcode:283
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
思路:把非0的往前挪,挪完之后,后面的就都是0了,然后在用0覆盖后面的
left记录移动的位置
right检查的位置
*/
func moveZeroes(nums []int) {
	left, right := 0, 0
	for _, v := range nums {
		if v != 0 {
			if left != right {
				nums[left], nums[right] = nums[right], nums[left]
			}
			left++
			right++
		} else {
			right++
		}
	}
}

func main() {
	l1 := []int{1, 0, 1, 2, 0, 3}
	moveZeroes(l1)
	fmt.Println(l1)
}

10.两数之和

/*
题目:移动0
leetcode:1
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

思路:两层for循环,依次相加判断是否等于目标值
*/
func twoSum(nums []int, target int) []int {
	li := make([]int, 2)
	for k, v := range nums {
		for i := k + 1; i < len(nums); i++ {
			if v+nums[i] == target {
				li[0] = k
				li[1] = i
				return li
			}
		}
	}
	return li
}
func main() {
	l1 := []int{3, 3, 4}
	n := twoSum(l1, 6)
	fmt.Println(n)
}

11.斐波那契数列

/*斐波那契数列是这样的数列:
0、1、1、2、3、5, 8、13、21、34 ……
下一项是上两项的和。
2 是上两项的和(1+1) 3 是上两项的和(1+2)、 5 是(2+3)、 依此类推!
*/
func demo1(n int) []int {
	li := make([]int, n)
	li[0] = 0
	li[1] = 1
	if n < 2 {
		return li[:n]
	}

	for i := 2; i < n; i++ {
		li[i] = li[i-2] + li[i-1]
	}
	return li
}

func main() {
	li := demo1(10)
	fmt.Println(li)
}

12.多数元素

/*
题目:多数元素
leetcode:169
给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
输入:nums = [3,2,3]
输出:3
思路:利用map记录nums中值出现的次数,然后循环map
*/
func majorityElement(nums []int) int {
	m := map[int]int{}
	for _, v := range nums {
		m[v]++
	}
	lenth2 := len(nums) / 2
	for k, v := range m {
		if v > lenth2 {
			return k
		}
	}
	return -1
}

func main() {
	li := []int{2, 3, 2}
	li2 := majorityElement(li)
	fmt.Println(li2)
}

13.买卖股票的最佳时机(只能买卖一次)

/*
题目:买卖股票的最佳时机(一次买卖交易限制)
leetcode:121
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。

	注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

思路:一次遍历
1.定义两个变量,成本,利润。初始成本最大值
2.每天的价格如果比成本低,则成本变量替换成本变量
3.每天计算利润,如果利润比之前的大就替换利润变量
*/
func maxProfit(prices []int) int {
	min := 99999 //初始最大值
	max := 0
	for _, v := range prices {
		if v < min {
			min = v
		} else if v-min > max {
			max = v - min
		}
	}
	return max
}
func main() {
	li := []int{7, 1, 5, 3, 6, 4}
	n := maxProfit(li)
	fmt.Println(n)
}

14.买卖股票的最佳时机II(在周期内无交易次数限制)

/*
题目:买卖股票的最佳时机II(无交易次次数限制)
leetcode:121
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
输入:[7,1,5,3,6,4]
输出:7
解释:第二天买,第三天卖,利润5-1=4。第四天买,第五天卖,利润6-3=3。总利润4+3=7

思路:第二天开始遍历,设想前一天买,后一天卖。如果利润大于0就操作,否则就不应该买
*/
func maxProfit(prices []int) (ans int) {
	for i := 1; i < len(prices); i++ {
		ans += max(0, prices[i]-prices[i-1])
	}
	return
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func main() {
	li := []int{1, 2, 3, 4, 5}
	n := maxProfit(li)
	fmt.Println(n)
}

15.跳跃游戏

/*
题目:跳跃游戏
leetcode:55
给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。
输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。

思路:下标索引与值相加,就是最大的步长索引。遍历记录最大max的步长,如果max小于下标位置,则不可到达末尾
*/
func canJump(nums []int) bool {
	max := 0
	for k, v := range nums {
		if max < k {
			return false
		}
		max = maxMath(max, k+v)
		if max >= len(nums)-1 {
			return true
		}
	}
	return false
}

func maxMath(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func main() {
	li := []int{3, 2, 1, 0, 4}
	n := canJump(li)
	fmt.Println(n)
}

16.最长公共前缀

/*
题目:最长公共前缀
leetcode:14
编写一个函数来查找字符串数组中的最长公共前缀。
输入:strs = ["flower","flow","flight"]
输出:"fl"
思路:
1.把第一个字符串取出来,循环遍历第一个字符串
2.第一个字符串的每一位遍历与其他比较,如果匹配,index++,否则直接返回 str[:index]
*/
func longestCommonPrefix(strs []string) string {
	pre := strs[0]
	for k, s := range pre {
		for i := 1; i < len(strs); i++ {
			if k > len(strs[i])-1 {
				return pre[:k]
			}
			if string(s) != string(strs[i][k]) {
				return pre[:k]
			}
		}
	}
	return pre
}
func main() {
	sList := []string{"ab", "a"}
	n := longestCommonPrefix(sList)
	fmt.Println(n)
}
posted @ 2024-01-30 18:40  Jeff的技术栈  阅读(1)  评论(0编辑  收藏  举报
回顶部