暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

数组

老码农空杯修行记 2021-05-08
293

数组相对其它结构比较简单,甚至很多人要跳槽时,不会专门去复习数组,这里只列出时间复杂度,然后给出几个leetcode关于数组的例子。

数组操作


查询通过下标索引直接定位,所以复杂度为 O(1)

插入插入位置处有多少元素就得向后移动多少个,所以复杂度为 O(n)

删除删除元素后面几个就得移动几个,所以复杂度 O(n)

leetcode

题目1:1.两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 的那 两个 整数,并返回它们的数组下标

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现

你可以按任意顺序返回答案。

拿到题目,再简单也详细看下要求,通过整理可得到要求如下
  • 数组是整数数组
  • 数组里可能有两个数的和为目标 target
  • 结果返回的是下标,不是哪两个加数
  • 每个输入target值对应一个答案
  • 两个加数在数组中的索引不能一样


    func twoSum(nums []int, target int) []int {
    数组的长度, 一般底层通过专门的变量来存储长度
    所以其时间复杂度为 O(1)
    lens := len(nums)


    for i:=0; i<lens-1; i++ {


    求另外一个加数
    tmp := target - nums[i]


    从当前元素的后面开始找该加数
    寻找的时间复杂度为 O(n)
    当然这里如果数组特别长,可以
    二分查找等加速,但是这里考察
    的是数组,因此暂时不用考虑
    for j:=i+1; j<lens; j++ {
    if nums[j] == tmp {
    题目中讲到每种输入只对应一个答案
    因此只要符合条件,返回即可
    return []int{i, j}
    }
    }
    }


    return nil
    }

    题目2:15. 三数之和

    给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

    注意:答案中不可以包含重复的三元组。

    这道题是两数之和的晋级,难度提升了不少,看看题目要点

    • a + b + c = 0, 数组中找出3个不同位置元素的和为0, 就是说 target=0
    • 结果集不能相互重复
    思路:排序,排序后的好处
    • 若第一个数大于0,后面的就无需计算
    • 第一个数为三元组第一个元素,那么三元组的第二个和第三个可以通过“两端夹逼法” 计算,若是和大于0,说明第三个数太大,夹逼法的右边指针左移,若是和小于0,说明第二个数太小,夹逼法的左指针右移


      func threeSum(nums []int) [][]int {
      size := len(nums)


      if size < 3 {
      return nil
      }


      // 预分配一定的空间,保证不频繁开空间导致的额外开销
      res := make([][]int, 0, 5)


      // 排好序,从小到大排序
      sort.Ints(nums)


      // size-2,三数之和, 保证当前元素后面流出2个元素
      for i:=0; i<size-2; i++ {


      // nums[i] > 0: 已经排好序,第一个数都大于0,那么后面加起来肯定大于0,直接跳过
      // i>0 && nums[i]==nums[i-1]: 说明当前这个数已经在上次循环中计算过了
      if nums[i] > 0 || (i>0 && nums[i] == nums[i-1]) {
      continue
      }


      // 另外两个数采用 “两端夹逼法"
      j, k := i+1, size-1


      for j < k {
      // 求和
      sum := nums[i] + nums[j] + nums[k]
      if sum == 0 {
      // 添加结果
      res = append(res, []int{nums[i], nums[j], nums[k]})


      // 向中间逼近,nums[j-1] == nums[j] 还是要排除重复的,否则
      // 重复计算会导致超时
      j++
      for j<k && nums[j-1] == nums[j] {
      j++
      }


      // 向中间逼近, nums[k] == nums[k+1] 还是要排除重复的,否则
      // 重复计算会导致超时
      k--
      for j<k && nums[k] == nums[k+1] {
      k--
      }
      } else if sum > 0 {
      // sum > 0 表示右边的数太大,应当缩小,所以索引 k 向
      // 左边移动,同时移动过程中应当过滤掉已经计算过的数,也就
      // 是 num[k] == num[k-1]
      k--
      for j<k && nums[k] == nums[k+1] {
      k--
      }
      } else {
      // sum < 0 表示左边的数太小,应当增大,所以索引 j 向
      // 右边移动,同时移动过程中应当过滤掉已经计算过的数,也就
      // 是 num[j] == num[j+1]
      j++
      for j<k && nums[j-1] == nums[j] {
      j++
      }
      }
      }
      }


      return res
      }


      题目3:283.移动零

      给定一个数组 nums
      ,编写一个函数将所有 0
       移动到数组的末尾,同时保持非零元素的相对顺序。

      题目本身不难,重要的是思路,遇到这种问题优先考虑双指针法或快慢指针法,本题通过快慢指针法解决

        func moveZeroes(nums []int)  {
        // 双指针法,slow <= fast
        // fast 指向扫描指针
        slow, fast := 0, 0


        for ; fast<len(nums); i++ {
        // 扫描遇到的元素是否为 0
        if nums[fast] != 0 {
        // 遇到元素不为0,两种情况
        // fast = slow, 表示slow 追得上 fast,原因是当前还没有扫描到 0,此时不用做任何操作,
        // 只需要 slow 跟着fast移动就好
        // fast > slow, 表示由于出现了0导致slow落后fast,而且slow此时指向fast之前为0的位置,
        // 此时需要 slow 处的 0 和 fast 处的非 0 交换
        if fast != slow {
        nums[slow], nums[fast] = nums[fast], nums[slow]
        }


        // 只要出现非0的元素,慢指针 slow 都需要移动
        slow++
        }
            }
        }

        题目4:11.盛最多水的容器

        给你 n 个

          func climbStairs(n int) int {
          // 1 -> [(1)] -> 1
          // 2 -> [(1, 1), (2)] -> 2
          // 3 -> [(1, 1, 1), (1, 2), (2, 1)] -> climb[1] = climb[2] + climb[1] = 3
          if n <= 2 {
          return n
          }


          // 存储两阶台阶的结果
          arr := []int{1, 2}


          for i:=3; i<=n; i++ {
          steps := arr[1] + arr[0]
          arr[0] = arr[1]
          arr[1] = steps
          }


          return arr[1]
          }


          题目4:11.盛最多水的容器

          给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

          说明:你不能倾斜容器。

          要点分析

          • 柱子高度(也就是数组元素的值,有高有低)不一样
          • 不能倾斜,说明盛水多少有低的柱子决定
          通过分析,盛水多少就是求面积,面积 = 高度 * 宽度 = min(left, right) * right-left,可以采用暴力法求出所有两个柱子之间的面积,然后比较得出最大,但是本题最好的方法还是 “两段夹逼法”
            func maxArea(height []int) int {
            size := len(height)


            // max 保存最大容量
            // left 容器的做边沿坐标
            // right 容器的右边沿坐标
            max, left, right := 0, 0, size - 1


            // 终止条件,左边沿和有边沿不能重合
            for left < right{
            // 计算容器的宽度 和 长度
            width := right - left
            hight := minInt(height[right], height[left])


            // 面积 tmp = 宽度 * 高度
            if tmp := width * hight; tmp > max {
            // 存储最大
            max = tmp
            }


            // 移动边沿低的那一边, 通过两段低的一段
            // 逐渐逼近,最终得到最优的解
            if height[left] > height[right] {
            right--
            } else {
            left++
            }
            }


            return max
            }


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


            return y
            }


            文章转载自老码农空杯修行记,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

            评论