一、链表
1、移除链表元素
func removeElements(head *ListNode, val int) *ListNode {
p := &ListNode{}
p.Next = head
pre := p
cur := head
for cur != nil {
if cur.Val == val {
pre.Next = cur.Next
cur = cur.Next
}else {
pre = cur
cur = cur.Next
}
}
return p.Next
}
2、设计链表 707
type MyLinkedList struct {
head *ListNode
size int
}
func Constructor() MyLinkedList {
return MyLinkedList{&ListNode{}, 0}
}
func (this *MyLinkedList) Get(index int) int {
if index < 0 || index >= this.size {
return -1
}
cur := this.head
for i := 0; i <= index; i++ {
cur = cur.Next
}
return cur.Val
}
func (this *MyLinkedList) AddAtHead(val int) {
this.AddAtIndex(0, val)
}
func (this *MyLinkedList) AddAtTail(val int) {
this.AddAtIndex(this.size, val)
}
func (this *MyLinkedList) AddAtIndex(index int, val int) {
if index > this.size {
return
}
this.size++
pre := this.head
for i := 0; i < index; i++ {
pre = pre.Next
}
cur := &ListNode{Val: val, Next: nil}
cur.Next = pre.Next
pre.Next = cur
}
func (this *MyLinkedList) DeleteAtIndex(index int) {
if index < 0 || index >= this.size {
return
}
this.size--
p := this.head
for i := 0; i < index; i++ {
p = p.Next
}
p.Next = p.Next.Next
}
/**
* Your MyLinkedList object will be instantiated and called as such:
* obj := Constructor();
* param_1 := obj.Get(index);
* obj.AddAtHead(val);
* obj.AddAtTail(val);
* obj.AddAtIndex(index,val);
* obj.DeleteAtIndex(index);
*/
3、反转链表
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
//1 2 3 4 5
func reverseList(head *ListNode) *ListNode {
var pre *ListNode = nil //*
cur := head
for cur != nil {
next := cur.Next //先指向 (位置很关键)
cur.Next = pre // 反转指针
pre = cur
cur = next
}
return pre
}
4、两两交换链表中的节点 24
//递归
func swapPairs(head *ListNode) *ListNode {
if head == nil || head.Next == nil {
return head
}
newHead := head.Next
head.Next = swapPairs(newHead.Next)
newHead.Next = head
return newHead
}
//非递归
func swapPairs(head *ListNode) *ListNode {
newHead := &ListNode{}
newHead.Next = head
temp := newHead
for temp.Next != nil && temp.Next.Next != nil {
node1 := temp.Next
node2 := temp.Next.Next
temp.Next = node2
node1.Next = node2.Next
node2.Next = node1
temp = node1
}
return newHead.Next
}
5、删除链表的倒数第n个节点——19
func removeNthFromEnd(head *ListNode, n int) *ListNode {
if head == nil {
return head
}
size := 0
p := head
for p != nil {
p = p.Next
size++
}
newHead := &ListNode{0,head}
cur := newHead
for i := 0; i < size-n; i++ {
cur = cur.Next
}
if cur.Next.Next != nil {
cur.Next = cur.Next.Next
} else {
cur.Next = nil
}
return newHead.Next
}
6、链表相交
思路:双指针
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func getIntersectionNode(headA, headB *ListNode) *ListNode {
if headA == nil || headB == nil {
return nil
}
curA := headA
curB := headB
for curA != curB {
if curA == nil {
curA = headB
}else{
curA = curA.Next
}
if curB == nil {
curB = headA
}else{
curB = curB.Next
}
}
return curA
}
7、环形链表2——142
题目:找到环的入口位置
//方法一 快慢指针
func detectCycle(head *ListNode) *ListNode {
slow, fast := head, head
for fast != nil {
slow = slow.Next
if fast.Next == nil {
return nil
}
fast = fast.Next.Next
if fast == slow {
p := head
for p != slow {
p = p.Next
slow = slow.Next
}
return p
}
}
return nil
}
1、Go 语言中,map 的键是唯一的,如果尝试将相同的键再次插入 map,旧的值会被新的值覆盖。
2、值的类型是 struct{},实际上并不关心值的内容,只关心键的存在性。
3、struct{} 表示一个不包含任何字段的空结构体。这个结构体是零字节大小的,也就是说它不占用任何内存空间。
4、因为它没有字段,所以它的实例在内存中仅占用一个零字节的空间。
5、res[head] 表示从 res 中获取键为 head 的值。通过 _, ok := res[head],我们只关心 ok 这个变量,它表示是否存在键 head。
//方法二 遍历链表,定义map存起来,利用map的键唯一的特性查重
func detectCycle(head *ListNode) *ListNode {
if head == nil || head.Next == nil {
return nil
}
res := make(map[*ListNode]struct{})
for head != nil {
if _, ok := res[head]; ok {
return head
}
res[head] = struct{}{}
head = head.Next
}
return nil
}
哈希表
1、有效的字母异位词
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。
//方法一 切片 没有固定长度,固定长度是数组,可以==直接比较
unc isAnagram(s string, t string) bool {
if len(s) != len(t) {
return false
}
//切片 没有固定长度,固定长度是数组,可以==直接比较
var arr_s []int
var arr_t []int
for i := 0; i < len(s); i++ {
arr_s = append(arr_s, int(s[i])-int('a'))
arr_t = append(arr_t, int(t[i])-int('a'))
}
sort.Ints(arr_t)
sort.Ints(arr_s)
for i := 0; i < len(arr_s); i++ {
if arr_t[i] != arr_s[i] {
return false
}
}
return true
}
// sort.Slice 函数提供了一种对切片进行排序的通用方法。它允许你提供一个自定义的比较函数,用于定义切片元素的排序规则。
// 返回 true 表示 i 位置的元素应该排在 j 位置的元素之前。
func test(s, t string) bool {
s1, s2 := []byte(s), []byte(t)
sort.Slice(s1, func(i, j int) bool { return s1[i] < s1[j] })
fmt.Println(s1)
sort.Slice(s2, func(i, j int) bool { return s2[i] < s2[j] })
fmt.Println(s2)
return string(s1) == string(s2)
}
//方法三 哈希法:维护一个26长的数组,记录出现的字母的数量
func isAnagram(s, t string) bool {
var c1, c2 [26]int
for _, ch := range s {
c1[ch-'a']++
}
for _, ch := range s {
c2[ch-'a']++
}
return c1 == c2
}
2、两个数组的交集——349
给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序
// 方法一:暴力解法:利用set的键值为一保证唯一性
func intersection(nums1 []int, nums2 []int) []int {
res := make(map[int]struct{})
//var res []int
for i := 0; i < len(nums1); i++ {
for j := 0; j < len(nums2); j++ {
if nums1[i] == nums2[j] {
res[nums1[i]] = struct{}{}
}
}
}
var resArr []int
for key, _ := range res {
resArr = append(resArr, key)
}
return resArr
}
//方法二 两个集合(和方法一类似)
func intersection(nums1 []int, nums2 []int) []int {
set1 := map[int]struct{}{}
set2 := map[int]struct{}{}
for _, v := range nums1 {
set1[v] = struct{}{}
}
for _, v := range nums2 {
set2[v] = struct{}{}
}
var res []int
//为了防止下面代码循环一次
if len(set1) < len(set2) {
set1, set2 = set2, set1
}
for v := range set2 {
if _, has := set1[v]; has {
res = append(res, v)
}
}
return res
}
// 方法三:双指针
func intersection(nums1 []int, nums2 []int) []int {
sort.Ints(nums1)
sort.Ints(nums2)
var res []int
for i, j := 0, 0; i < len(nums1) && j < len(nums2); {
x, y := nums1[i], nums2[j]
if x == y {
if res == nil || x > res[len(res)-1] {
res = append(res, x)
}
i++
j++
} else if x < y {
i++
} else {
j++
}
}
return res
}
3、快乐数——202
//方法一:暴力+ set(利用map构造出set)
func getNum(num int) []int {
var res []int
for num != 0 {
res = append(res, num%10)
num /= 10
}
fmt.Println(res)
return res
}
func isHappy(n int) bool {
newNum := 0
set1 := make(map[int]struct{})
res := getNum(n)
for newNum != n {
newNum := 0
for _, v := range res {
newNum += v * v
}
if _, has := set1[newNum]; has {
return false
} else {
set1[newNum] = struct{}{}
}
if newNum == 1 {
return true
}
res = getNum(newNum)
}
return false
}
//方法二:快慢指针法(官网)
//慢速在链表中前进 1 个节点,快跑者前进 2 个节点(对 getNext(n) 函数的嵌套调用)
//如果 n 是一个快乐数,即没有循环,那么快跑者最终会比慢跑者先到达数字 1。
//如果 n 不是一个快乐的数字,那么最终快跑者和慢跑者将在同一个数字上相遇。
func isHappy(n int) bool {
slow, fast := n, step(n)
for fast != 1 && slow != fast {
slow = step(slow)
fast = step(step(fast))
}
return fast == 1
}
func step(n int) int {
sum := 0
for n > 0 {
sum += (n%10) * (n%10)
n = n/10
}
return sum
}
4、两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1]
// 方法一:暴力法
func twoSum(nums []int, target int) []int {
for i := 0; i < len(nums)-1; i++ {
for j := i + 1; j < len(nums); j++ {
if nums[i]+nums[j] == target {
var res []int
res = append(append(res, i), j)
return res
}
}
}
return nil
}
// 方法二:哈希
//我们创建一个哈希表,对于每一个 x,我们首先查询哈希表中是否存在 target - x,然后将 x 插入到哈希表中,即可保证不会让 x 和自己匹配。
func twoSum(nums []int, target int) []int {
hashTable := map[int]int{}
for i, x := range nums {
if p, ok := hashTable[target-x]; ok {
return []int{p, i}
}
hashTable[x] = i
}
return nil
}
5、两数相加——454
给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:
0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
示例 1:
输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
输出:2
解释: 两个元组如下:
- (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
- (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0
//分组 + 哈希表
func fourSumCount(nums1 []int, nums2 []int, nums3 []int, nums4 []int) int {
countAB := map[int]int{}
// 遍历nums1和nums2数组,统计两个数组元素之和,和出现的次数,放到map中
for _, v := range nums1 {
for _, w := range nums2 {
countAB[v+w]++
}
}
res := 0
// 遍历nums3和nums4数组,找到如果 0-(c+d) 在map中出现过的话,就把map中key对应的value也就是出现次数统计出来
for _, v := range nums3 {
for _, w := range nums4 {
res += countAB[-v-w]
}
}
return res
}
6、三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释: nums[0] +nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。
//排序+双指针
func threeSum(nums []int) [][]int {
n := len(nums)
sort.Ints(nums)
ans := make([][]int, 0)
for first:=0;first<n;first++{
if first>0 && nums[first] == nums[first-1]{
continue
}
third := n-1
target := -1 * nums[first]
for second := first+1;second<n; second++ {
if second > first + 1 && nums[second] == nums[second-1] {
continue
}
for second < third && nums[second] + nums[third] > target{
third--
}
if second == third {
break
}
if nums[second] + nums[third] == target{
ans = append(ans,[]int{nums[first],nums[second],nums[third]})
}
}
}
return ans
}
7、四数之和
给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):
- 0 <= a, b, c, d < n
- a、b、c 和 d 互不相同
- nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。
示例 1:
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
//排序+双指针
func fourSum1(nums []int, target int) [][]int {
sort.Ints(nums)
n := len(nums)
res := [][]int{}
for first := 0; first < n; first++ {
if first > 0 && nums[first] == nums[first-1] {
continue
}
for second := first + 1; second < n; second++ {
if second > first+1 && nums[second] == nums[second-1] {
continue
}
sum := target - (nums[first] + nums[second])
third, forth := second+1, n-1
for third < forth {
if nums[third]+nums[forth] < sum {
third++
} else if nums[third]+nums[forth] > sum {
forth--
} else {
resTem := []int{}
resTem = append(resTem, nums[first], nums[second], nums[third], nums[forth])
res = append(res, resTem)
for third < forth && nums[third] == nums[third+1] && nums[forth] == nums[forth-1] {
third++
forth--
}
third++
forth--
//和下面功能相同
//for left++; left < right && nums[left] == nums[left-1]; left++ {
//}
//for right--; left < right && nums[right] == nums[right+1]; right-- {
//}
}
}
}
}
return res
}
- left++: 在循环的开始,left 变量会递增。
- left++::在每次循环迭代结束时,left 的值会再次递增 1。
8、赎金信——383
给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。
如果可以,返回 true ;否则返回 false 。
magazine 中的每个字符只能在 ransomNote 中使用一次。
示例 1:
输入:ransomNote = “a”, magazine = “b” 输出:false
示例 2:输入:ransomNote = “aa”, magazine = “ab” 输出:false
示例 3:输入:ransomNote = “aa”, magazine = “aab” 输出:true
//哈希表——map
func canConstruct(ransomNote string, magazine string) bool {
hashMap := make(map[string]int)
for i := range magazine {
hashMap[string(magazine[i])]++
//fmt.Print(string(magazine[i]))
}
for i := 0; i < len(ransomNote); i++ {
if hashMap[string(ransomNote[i])] > 0 {
hashMap[string(ransomNote[i])]--
} else {
return false
}
}
return true
}
字符串
1、反转字符串——344
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
func reverseString(s []byte) []byte {
for i, j := 0, len(s)-1; i < len(s)/2 && j >= len(s)/2; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
return s
}
2、反转字符串II——541
给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。
- 如果剩余字符少于 k 个,则将剩余字符全部反转。
- 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
示例 1:
输入:s = “abcdefg”, k = 2 输出:“bacdfeg”
示例 2:输入:s = “abcd”, k = 2 输出:“bacd”
//注意:go中字符串不能修改,需要转换成字节数组
func reverseBytes(s []byte) []byte {
for i, j := 0, len(s)-1; i < len(s)/2 && j >= len(s)/2; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
fmt.Println("sss:", string(s))
return s
}
func reverseStr(s string, k int) string {
sBytes := []byte(s)
num := 2 * k
i := 0
for num <= len(s) {
reverseBytes(sBytes[i : i+k])
i = num
num += 2 * k
}
lastNum := len(s) - (num - 2*k)
if lastNum > 0 && lastNum < k {
reverseBytes(sBytes[num-2*k:])
} else if lastNum < 2*k && lastNum >= k {
reverseBytes(sBytes[num-2*k : num-k])
}
return string(sBytes)
}
知识点:
//字符串转字节数组
sBytes := []byte(s)
//字节数组转字符串
s = string(sBytes)
3、替换数字
给定一个字符串 s,它包含小写字母和数字字符,请编写一个函数,将字符串中的字母字符保持不变,而将每个数字字符替换为number。 例如,对于输入字符串 “a1b2c3”,函数应该将其转换为 “anumberbnumbercnumber”。
//方法一:定义一个新的数组接收最后的结果
func replaceNum(s []byte) string {
var res []byte
flag := 0
for i := range s {
if s[i] <= '9' && s[i] >= '0' {
inserElement := []byte{'n', 'u', 'm', 'b', 'e', 'r'}
res = append(append(res, s[flag:i]...), inserElement...)
flag = i + 1
}
}
res = append(res, s[flag:]...)
return string(res)
}
//方法二:在原数组上修改
func replaceNum(s []byte) string {
for i := 0; i < len(s); i++ {
if s[i] <= '9' && s[i] >= '0' {
inserElement := []byte{'n', 'u', 'm', 'b', 'e', 'r'}
s = append(s[:i], append(inserElement, s[i+1:]...)...)
i = i + len(inserElement) - 1
}
}
return string(s)
}
知识点:
- …是展开数组
- 在数组中特定位置插入元素,上述方法
- 灵活应用数组中冒号:如 arr[:3]
4、反转字符串中的单词
package main
import (
"fmt"
"strings"
)
func reverseWords(s string) string {
//去除字符串两边空格
s = strings.TrimSpace(s)
//以空格 分割成 数组
arr := strings.Split(s, " ")
for i, j := 0, len(arr)-1; i < len(arr)/2 && j >= len(arr)/2; i, j = i+1, j-1 {
arr[i], arr[j] = arr[j], arr[i]
}
var filteredArr []string
//过滤arr中的空元素
for _, str := range arr {
if str != "" {
filteredArr = append(filteredArr, str)
}
}
res := strings.Join(filteredArr, " ")
return res
}
func main() {
s := "a good example"
fmt.Print(reverseWords(s))
//strings.Contains(str, " ") 用于检查每个元素是否包含空格
//fmt.Println(strings.Contains(s, " "))
}
知识点:
- 用于检查每个元素是否包含空格,返回值是boolen
strings.Contains(str, " ")
fmt.Println(strings.Contains(s, " "))
- 去除字符串两边的空格
s = strings.TrimSpace(s)
- 去除字符串中全部的空格
s = strings.ReplaceAll(s, " ", "")
- 以空格 分割成 数组
arr := strings.Split(s, " ")
5、找出字符串中第一个匹配项的下标——28
- 暴力解法
// 暴力匹配 复杂度:O(m*n)
func strStr(haystack string, needle string) int {
len1 := len(haystack)
len2 := len(needle)
if len1 < len2 {
return -1
}
flag := 1
i, j := 0, 0
for i < len1 {
for j < len2 && i < len1 {
if haystack[i] == needle[j] {
i++
j++
} else {
j = 0
i = flag
break
}
}
if j == len2 {
return flag - 1
}
flag++
}
return -1
}
- KMP算法(理解不透彻)
func strStr(haystack, needle string) int {
n, m := len(haystack), len(needle)
if m == 0 {
return 0
}
//求next数组
pi := make([]int, m)
for i, j := 1, 0; i < m; i++ {
for j > 0 && needle[i] != needle[j] {
j = pi[j-1]
}
if needle[i] == needle[j] {
j++
}
pi[i] = j
}
fmt.Println(pi)
//根据next回退
for i, j := 0, 0; i < n; i++ {
for j > 0 && haystack[i] != needle[j] {
j = pi[j-1]
}
if haystack[i] == needle[j] {
j++
}
if j == m {
return i - m + 1
}
}
return -1
}
6、 重复的子字符串
- 暴力枚举
//检测传过来的part子串是否可以组成s
func match(s, part string) bool {
i := len(part)
j := 0
if len(part) > len(s)/2 {
return false
}
for i < len(s) {
flag := i
for j < len(part) && i < len(s) {
if s[i] != part[j] {
return false
}
j++
i++
}
if i == len(s) && j < len(part) {
return false
}
j = 0
i = flag + len(part)
}
return true
}
func repeatedSubstringPattern(s string) bool {
i := 0
var res []bool
for i < len(s)/2 {
part := s[:i+1]
res = append(res, match(s, part))
i++
}
fmt.Println(res)
for k := range res {
if res[k] == true {
return true
}
}
return false
}
- KMP解法
贪心
1、分发饼干——455
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
示例 1:
输入: g = [1,2,3], s = [1,1] 输出: 1 解释: 你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。 所以你应该输出1。示例 2:
输入: g = [1,2], s = [1,2,3] 输出: 2 解释: 你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
你拥有的饼干数量和尺寸都足以让所有孩子满足。 所以你应该输出2.
//优先满足胃口最大的孩子
func findContentChildren(g []int, s []int) int {
sort.Ints(g)
sort.Ints(s)
count := 0
i, j := len(g)-1, len(s)-1
for i >= 0 && j >= 0 {
if s[j] >= g[i] {
i--
j--
count++
} else {
i--
}
}
return count
}
2、摆动序列——376
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。
例如, [1, 7, 4, 9, 2, 5] 是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。
相反,[1, 4, 7, 2, 5] 和 [1, 7, 4, 5, 5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。
子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。
给你一个整数数组 nums ,返回 nums 中作为 摆动序列 的 最长子序列的长度 。
示例 1:
输入:nums = [1,7,4,9,2,5] 输出:6 解释:整个序列均为摆动序列,各元素之间的差值为 (6, -3, 5, -7, 3)
示例 2:输入:nums = [1,17,5,10,13,15,10,5,16,8] 输出:7 解释:这个序列包含几个长度为 7 摆动序列。
其中一个是 [1, 17, 10, 13, 10, 16, 8] ,各元素之间的差值为 (16, -7, 3, -3, 6, -8)
。
//贪心
func wiggleMaxLength(nums []int) int {
if len(nums) == 1 {
return len(nums)
}
if len(nums) == 2 && nums[0] != nums[1] {
return 2
}
preNum := 0
curNum := 0
res := 1
for i := 0; i < len(nums)-1; i++ {
curNum = nums[i+1] - nums[i]
if (preNum >= 0 && curNum < 0) || (preNum <= 0 && curNum > 0) {
res++
preNum = curNum
}
}
return res
}
3、最大子数组和
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4] 输出:6 解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:输入:nums = [1] 输出:1
示例 3:输入:nums = [5,4,-1,7,8] 输出:23
- 暴力(复杂度太大 超时)
// 暴力
func sum(num []int) int {
res := 0
for i := range num {
res += num[i]
}
return res
}
func maxSubArray(nums []int) int {
if len(nums) == 1 {
return nums[0]
}
i := 0
var res []int
for i < len(nums) {
j := i + 1
for j <= len(nums) {
res = append(res, sum(nums[i:j]))
j++
}
i++
}
fmt.Println(res)
sort.Ints(res)
return res[len(res)-1]
}
- 贪心:
- 遍历 nums,从头开始用 count 累积,如果 count 一旦加上 nums[i]变为负数,那么就应该从 nums[i+1]开始从 0 累积 count 了,因为已经变为负数的 count,只会拖累总和。
- 当前“连续和”为负数的时候立刻放弃,从下一个元素重新计算“连续和”,因为负数加上下一个元素 “连续和”只会越来越小。
func maxSubArray(nums []int) int {
count := 0
maxNum := math.MinInt
for i := 0; i < len(nums); i++ {
count += nums[i]
if count > maxNum {
maxNum = count
}
if count <= 0 {
count = 0
}
}
return maxNum
}