数据结构与算法(七)数组系列——双指针在数组中的使用

数组 — 双指针

数组是我们在开发中最常见到的数据结构了,用于按顺序存储元素的集合。但是元素可以随机存取,因为数组中的每个元素都可以通过数组索引来识别。插入和删除时要移动后续元素,还要考虑扩容问题,插入慢。

数组与日常的业务开发联系非常紧密,如何巧妙的用好数组是我们能否开发出高质量代码的关键

1 双指针在数组中的使用

上面链表中提到的一类题目,主要是利用两个或多个不同位置的指针,通过速度和方向的变换解决问题。注意这种技巧经常在排序数组中使用。

1.1 调整数组顺序使奇数位于偶数前面

题目

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分

思路

两个指针start、end,一个从前开始,一个从后开始,比较指针所指元素。

  1. 若arr[start]为偶数,arr[end]为奇数,交换两个元素,start++,end–。
  2. 若arr[start]为偶数,arr[end]也为偶数,end–,直至end指针所指为奇数,交换。
  3. 若arr[start]为奇数,start++,直至start指向偶数。
  4. 当start>end交换完成。
代码
function reOrderArray(arr) {
  let start = 0;
  let end = arr.length - 1;
  while (start < end) {
    //奇数
    while (arr[start] % 2 === 1) {
      start++;
    }
    //此时start所指为偶数,对后面判断
    while (arr[end] % 2 === 0) {
      end--;
    }
    //此时end所指为奇数,若start<end交换
    if (start < end) {
      // let temp = arr[start];
      // arr[start] = arr[end];
      // arr[end] = temp;
      // 使用解构赋值:
      [arr[start], arr[end]] = [arr[end], arr[start]]
    }
  }
  return arr;
}

若需要保证相对顺序不变,则不能用上面的写法,需要让两个指针同时从左侧开始

1.2 和为S的两个数字

题目

输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。

思路

数组中可能有多对符合条件的结果,而且要求输出乘积最小的,说明要分布在两侧 比如 3,8 5,7 要取3,8

看了题目了,很像leetcode的第一题【两数之和】,但是题目中有一个明显不同的条件就是数组是有序的,可以使用使用大小指针求解,不断逼近结果,最后取得最终值。

  • 设定一个小索引left,从0开始
  • 设定一个大索引right,从array.length开始
  • 判断array[left] + array[right]的值s是否符合条件
  • 符合条件 - 返回
  • 大于sumright向左移动
  • 小于sumleft向右移动
  • left=right,没有符合条件的结果
代码
function FindNumbersWithSum(array, sum) {
  let left = 0;
  let right = array.length - 1;
  while (left < right) {
    const s = array[left] + array[right];
    if (s > sum) {
      right--;
    } else if (s < sum) {
      left++;
    } else {
      return [array[left], array[right]]
    }
  }
  return [];
}

1.3 和为S的人连续正整数序列

题目

输入一个正数S,打印出所有和为S的连续正数序列。

例如:输入15,有序1+2+3+4+5 = 4+5+6 = 7+8 = 15 所以打印出3个连续序列1-55-67-8

思路
  • 创建一个容器child,用于表示当前的子序列,初始元素为1,2
  • 记录子序列的开头元素small和末尾元素big
  • big向右移动子序列末尾增加一个数 small向右移动子序列开头减少一个数
  • 当子序列的和大于目标值,small向右移动,子序列的和小于目标值,big向右移动
代码
function FindContinuousSequence(sum) {
    const result = [];
    const child = [1, 2];
    let big = 2;
    let small = 1;
    let currentSum = 3;
    while (big < sum) {
        //序列和小于目标值 且big小于目标值执行,直至超过目标值
        //存入数组,sum变化,big右移
        while (currentSum < sum && big < sum) {
            child.push(++big);
            currentSum += big;
        }
        //序列和大于目标值,且指针没越位,直至越位
        while (currentSum > sum && small < big) {
            //移除child的第一个元素
            child.shift();
            //左指针自增 sum变化。
            currentSum -= small++;
        }
        //如果值符合,且序列个数>1,存入结果数组,继续寻找。
        if (currentSum === sum && child.length > 1) {
            result.push(child.slice());
            child.push(++big);
            currentSum += big;
        }
    }
    return result;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值