数组算法
目录
数组算法
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)
}
选择了IT,必定终身学习