js双指针递归 数组求最大最小第n大 第n小的练习

356 阅读2分钟

1 双指针

/**
 *
 * aaaaabbbbasaeaedccccccccccd 找出连续字符最长的 输出它的个数
 *
 * 思路
 * 从头到尾遍历
 * 找到一个子字符串的开始和结尾
 *
 * 那就需要两个指针
 * 第一个 p2 一个指针一直从头跑到尾
 * 另一个指针 p1当前项和下一项不一样的时候 另一个指针追上第一个指针
 *
 * 第一个指针和另一个指针的差值就是 每个相同子串的长度
 *
 * 还需要一个变量保存当前最大字符的长度 maxLen
 * 以及最大字符的值repeatStr
 *
 * 时间复杂度是O(n)
 * 空间复杂度用了两个指针可以算作O(1)
 */

var str = "aaaaabbbbasaeaedccccccccccd ";
function solution(str) {
  let p1 = 0,
    len = str.length,
    maxLen = 0,
    repeatStr = "";
  for (let p2 = 1; p2 < len; p2++) {
    if (str[p1] !== str[p2]) {
      //让 慢追上快
      console.log(`输出 ${p1} 到 ${p2} 的距离 差了 ${p2 - p1}`);
      if (p2 - p1 > maxLen) {
        maxLen = p2 - p1;
        repeatStr = str[p1];
      }
      p1 = p2;
    }
  }
  return `重复最长的字符是${repeatStr} 重复了 ${maxLen}`;
}

console.log(solution(str));
// 输出 0 到 5 的距离 差了 5
// 输出 5 到 9 的距离 差了 4
// 输出 9 到 10 的距离 差了 1
// 输出 10 到 11 的距离 差了 1
// 输出 11 到 12 的距离 差了 1
// 输出 12 到 13 的距离 差了 1
// 输出 13 到 14 的距离 差了 1
// 输出 14 到 15 的距离 差了 1
// 输出 15 到 16 的距离 差了 1
// 输出 16 到 26 的距离 差了 10
// 输出 26 到 27 的距离 差了 1
// 重复最长的字符是c 重复了 10

/**
 *
 * @param {输入字符串} str
 * "aaaaabbbbasaeaedccccccccccd ";
 * 期望输出
 * a5b4a1s1a1e1a1e1d1c10d1
 *
 */
function solution3(str) {
  let p1 = 0,
    len = str.length,
    outStr = "";
  for (let p2 = 1; p2 < len; p2++) {
    if (str[p1] !== str[p2]) {
      //两个指针对应的值不等 做处理
      // console.log(`输出 ${str[p1]} 到 ${str[p2]} 的距离  ${p1} ---${p2}`);
      outStr += str[p1] + (p2 - p1);
      p1 = p2;
    }
  }
  return outStr;
}

console.log(solution3(str));

2 递归思想

    1. 求fib数列的值
    1. 带缓存的递归
总体思路: 
递归终止条件
递归执行
//fib 函数 
/**
 *
 *
 * 1 1 2 3 5 8 11
 *
 * f(0 ) = 1
 * f(1)  = 1
 * f(2) = f(2-1) + f(2-2)
 * f(3) = f(3-1) + f(2-2)
 */

function fib(n) {
  console.count("fib invoked"); //167 次
  if (n == 0 || n == 1) return 1;
  return fib(n - 1) + fib(n - 2);
}

for (let i = 0; i < 9; i++) {
  fib(i);
}


//带缓存的fib函数 减少无效重复的递归

var cache = {};
function fibWithCache(n) {
  console.count("fib invoked"); //23 次
  if (cache[n]) return cache[n];
  let v = n == 0 || n == 1 ? 1 : fibWithCache(n - 1) + fibWithCache(n - 2);
  cache[n] = v;
  return v;
}
for (let i = 0; i < 9; i++) {
  fibWithCache(i);
}

//动态规划的解决方式 后面再补充

高维数组转成对象 形式转换问题(规则:出现了就要想到用递归)

/**
 *输入 [1, 2, [3, [4, 5], 6], 7, [8], 9]
  输出
 *
 */
  var arr = [1, 2, [3, [4, 5], 6], 7, [8], 9];
  function transformArrToJson(arr) {
    let res = [];

    for (let i = 0, len = arr.length; i < len; i++) {
      if (Array.isArray(arr[i])) {
        //数组的话 构造对应的结构 递归即可
        res.push({
          children: transformArrToJson(arr[i])
        });
      } else if (typeof arr[i] === "number") {
        res.push({
          value: arr[i]
        });
      }
    }
    return res;
  }

  console.log(transformArrToJson(arr));
  
  

image.png

//函数写法 但是这种上来就要递归 因为入参是数组
 var arr = [1, 2, [3, [4, 5], 6], [[[1], [33], 99]], 7, [8], 9];
  function transformArrToJson(i) {
    let res = [];

    if (typeof i === "number") {
      res.push({
        value: i
      });
    } else if (Array.isArray(i)) {
      res.push({
        children: i.map(_i => transformArrToJson(_i))
      });
    }

    return res;
  }

  console.log(transformArrToJson(arr));

image.png

3 数组求最大最小值

    1. for 循环实现
    1. 数组的高阶函数实现 +假设法, 假设第一项是最大或者最小值,遍历一遍,遍历过程中进行取舍
    1. Math.max || Math.min + apply 实现 (Math.max Math.min 怎么实现? 下文有介绍)
    1. Math.max || Math.min + es6 ... 实现
const arrs = [3, 2, 1, 6, 10, 45];

//for loop
let minVal = arrs[0],
  maxVal = arrs[0];
for (let i = 0, len = arrs.length; i < len; i++) {
  let nowValue = arrs[i];
  nowValue < minVal ? (minVal = nowValue) : null;
  nowValue > maxVal ? (maxVal = nowValue) : null;
}
console.log(`max value is ${maxVal}, min value is ${minVal}`);
//max value is 45, min value is 1

//1 find min element in arr
const finMin = () => {
  let min = arrs[0];
  arrs.forEach(item => {
    if (item < min) {
      min = item;
    }
  });
  return min;
};
console.log(finMin(arrs));
//2 find max element in arr
const finMax = () => {
  let max = arrs[0];
  arrs.forEach(item => {
    if (item > max) {
      max = item;
    }
  });
  return max;
};
console.log(finMax(arrs));

//use math.max and apply
console.log(Math.max.apply(null, arrs));
console.log(Math.min.apply(null, arrs));

// use math.max and es6  ...
console.log(Math.max(...arrs));
console.log(Math.min(...arrs));

//sort array

var desc = (a, b) => a - b;
var increase = (a, b) => b - a;
// arrs.sort(desc);
arrs.sort(increase);
console.log(arrs);

// n-th max or min element in array
//sort array then visit arr  by index arr first index have the  address in memory then the  other element can be visted by the base address + offset offset is  the now array index


那么 Math.max Math.min 是怎么实现的呢? 可以看ecma262的规定:

15.8.2.11 max(x, y)
Returns the larger of the two arguments.
    • If either argument is NaN, the result is NaN.
    • If x>y, the result is x.
    • If y>x, the result is y.
    • If x is +0 and y is +0, the result is +0.If x is +0 and y is −0, the result is +0.If x is −0 and y is +0, the result is +0.If x is −0 and y is −0, the result is −0.

15.8.2.12 min(x, y)
    Returns the smaller of the two arguments.
    • If either argument is NaN, the result is NaN.
    • If x<y, the result is x.
    • If y<x, the result is y.
    • If x is +0 and y is +0, the result is +0.If x is +0 and y is −0, the result is −0.If x is −0 and y is +0, the result is −0.If x is −0 and y is −0, the result is −0.

Math.max 按照上面规定调用一下的结果:

image.png

Math.min 按照上面规定调用一下的结果:

image.png

自己如何大致实现?可以分为两个参数和多个参数的情况:

function min() {
  var result= Infinity;
  for(var i in arguments) {
    if(arguments[i] < result) {
      result = arguments[i];
    }
  }
  return result;
}

function max() {
  var result= -Infinity;
  for(var i in arguments) {
    if(arguments[i] > result) {
      result = arguments[i];
    }
  }
  return result;
}

//Tests
console.log(min(5,3,-2,4,14));       //-2
console.log(Math.min(5,3,-2,4,14));  //-2

console.log(max(5,3,-2,4,14));       //14
console.log(Math.max(5,3,-2,4,14));  //14

console.log(min());                  //Infinity
console.log(Math.min());             //Infinity

console.log(max());                  //-Infinity
console.log(Math.max());             //-Infinity

chrome V8的实现:


function MathMax(arg1, arg2) {  // length == 2
  var length = %_ArgumentsLength();
  if (length == 2) {
    arg1 = TO_NUMBER(arg1);
    arg2 = TO_NUMBER(arg2);
    if (arg2 > arg1) return arg2;
    if (arg1 > arg2) return arg1;
    if (arg1 == arg2) {
      // Make sure -0 is considered less than +0.
      return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg2 : arg1;
    }
    // All comparisons failed, one of the arguments must be NaN.
    return NaN;
  }
  var r = -INFINITY;
  for (var i = 0; i < length; i++) {
    var n = %_Arguments(i);
    n = TO_NUMBER(n);
    // Make sure +0 is considered greater than -0.
    if (NUMBER_IS_NAN(n) || n > r || (r === 0 && n === 0 && %_IsMinusZero(r))) {
      r = n;
    }
  }
  return r;
}

难一点的,求数字组成的乱序数组中的第k大的元素 最大还是最小堆实现 以及递归加排序实现 后面再说