经典写算法

19 篇文章 1 订阅

目录

一、闭包实现累加

二、跳台阶问题

三、回文系列:

四、js实现阶乘n!(递归形式与for循环形式)

五、溢出问题处理

六、数组乱序,前后相邻输出不一致

七、按照年龄进行分类:

八、去重系列

九、Eg[1,2,3,4,5] =1*2+2*2+3*2+4*2+5*2=30

十、零钱问题

十一、二叉树相关

十二、链表相关

十三、有效的括号

十四、两个数组的交集

十五、对象格式处理

十六、实现(5).add(3).minus(1),使得其结果为5+3-1=7

十七、按月份统计销售额

十八、正则相关

十九、随机生成一个数组,并按规则将其转化为二维数组

二十、旋转数组

二十一、移动零

二十二、求两个数组的中位数

二十三、数值范围内1的个数,从1开始的某个范围

二十四、判断出栈顺序是否合法

二十五、输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

二十六、输入一个链表,输出该链表中倒数第k个结点。

二十七、两个数不使用四则运算得出和

二十八、0 - 1背包问题


用对象处理相关算法:https://blog.csdn.net/qq_29918313/article/details/98032521

算法整理:https://blog.csdn.net/qq_32766999/article/details/100654382

最大子序和、最长递增子序列:https://blog.csdn.net/qq_29918313/article/details/100232087

最长公共连续子串序列:https://blog.csdn.net/qq_29918313/article/details/102407590

和为k的子数组:https://blog.csdn.net/qq_29918313/article/details/100533780

和为目标值的整数:https://blog.csdn.net/qq_29918313/article/details/88527939

和为K的两个数(一对或多对):https://blog.csdn.net/qq_29918313/article/details/102009832

括号匹配:https://blog.csdn.net/qq_29918313/article/details/89489220

十大排序算法:https://blog.csdn.net/qq_29918313/article/details/94136234

移除数组中的元素:https://blog.csdn.net/qq_29918313/article/details/94648883

最长公共前缀:https://blog.csdn.net/qq_29918313/article/details/106455601

该文章整理了相关算法:https://www.jianshu.com/p/7c2a672fa598

一、闭包实现累加

var count = 0
var fn1 = function () {
    return function (){
        count++
        console.log(count)
    }
}
 
var fn2 = fn1()
fn2()
fn2()
fn2()
fn2()

 闭包实现函数只有一次有效调用:

var fn1 = function (fn2) {
    var result = null
    return function () {
        if (fn2) {
            result = fn2()
            fn2 = null
        }
        return result
    }
}
 
var callOnce = fn1(function () {
    console.log('11111')
})
 
callOnce ()
callOnce ()
callOnce ()
callOnce ()

二、跳台阶问题

(1)一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

function jumpFloor(number)
{
    if (number === 1) return 1
    if (number === 2) return 2
    return jumpFloor(number - 1) + jumpFloor(number - 2)
}

(2) 一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

关于本题,前提是n个台阶会有一次n阶的跳法。分析如下:

f(1) = 1

f(2) = f(2-1) + f(2-2)         //f(2-2) 表示2阶一次跳2阶的次数。

f(3) = f(3-1) + f(3-2) + f(3-3) 

...

f(n) = f(n-1) + f(n-2) + f(n-3) + ... + f(n-(n-1)) + f(n-n) 

 

说明: 

1)这里的f(n) 代表的是n个台阶有一次1,2,...n阶的 跳法数。

2)n = 1时,只有1种跳法,f(1) = 1

3) n = 2时,会有两个跳得方式,一次1阶或者2阶,这回归到了问题(1) ,f(2) = f(2-1) + f(2-2) 

4) n = 3时,会有三种跳得方式,1阶、2阶、3阶,

    那么就是第一次跳出1阶后面剩下:f(3-1);第一次跳出2阶,剩下f(3-2);第一次3阶,那么剩下f(3-3)

    因此结论是f(3) = f(3-1)+f(3-2)+f(3-3)

5) n = n时,会有n中跳的方式,1阶、2阶...n阶,得出结论:

    f(n) = f(n-1)+f(n-2)+...+f(n-(n-1)) + f(n-n) => f(0) + f(1) + f(2) + f(3) + ... + f(n-1)

    

6) 由以上已经是一种结论,但是为了简单,我们可以继续简化:

    f(n-1) = f(0) + f(1)+f(2)+f(3) + ... + f((n-1)-1) = f(0) + f(1) + f(2) + f(3) + ... + f(n-2)

    f(n) = f(0) + f(1) + f(2) + f(3) + ... + f(n-2) + f(n-1) = f(n-1) + f(n-1)

    可以得出:

    f(n) = 2*f(n-1)

    

7) 得出最终结论,在n阶台阶,一次有1、2、...n阶的跳的方式时,总得跳法为:

              | 1       ,(n=0 ) 

f(n) =     | 1       ,(n=1 )

              | 2*f(n-1),(n>=2)
function jumpFloorII(number)
{
    if (number === 0) return 0
    if (number === 1) return 1
    if (number === 2) return 2
    return 2*jumpFloorII(number - 1)
}

(3)在你面前有一个n阶的楼梯(n>=100且n<500),你一步只能上1阶或3阶。请问计算出你可以采用多少种不同的方式爬完这个楼梯(到最后一层为爬完)。https://www.nowcoder.com/practice/1e6ac1a96c3149348aa9009709a36a6f

三、回文系列:

1.判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

示例 1:

输入: 121
输出: true
示例 2:

输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
示例 3:

输入: 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数。
var isPalindrome = function(x) {
  if (x < 0 || (x % 10 == 0 && x != 0)) {
    return false
  }
  var result = (x + "").split("").reverse().join("")
  if (result == x) {
    return true
  } else {
    return false
  }
};

2.给定一个字符串 s,找到 s 中最长的回文子串。

输入: "cbbd"
输出: "bb"
var s = 'abaaa'
var aa = function (s) {
  var result = ''
  for (var i = 0; i < s.length; i++) {
    for (var j = i + 1; j <= s.length; j++) {
      var str = s.slice(i, j)
      // console.log(str) // a ab aba abaa abaaa b ba baa baaa a aa aaa a aa a
      var f = str.split('').reverse().join('')
      console.log(f)      // a ba aba aaba aaaba b ab aab aaab a aa aaa a aa a
      if (str == f) {
        result = str.length > result.length ? str : result
      }
    }
  }
  console.log(result)
}
aa(s)

四、js实现阶乘n!(递归形式与for循环形式)

// 非递归实现
function jiecheng(n) {
  var result = 1
  for (var i = n; i > 0; i--) {
    result = result * i
  }
  console.log(result)
}
jiecheng(5)


// 递归实现
var result = 1
function jiecheng(n) {
  if (n == 1) {
    return 1
  } else {
    return n* jiecheng(n-1)
  }
}
console.log(jiecheng(5))


var result = 1
function jiecheng(n) {
  if (n != 1) {
    result = result * n
    var m = (--n)
    jiecheng(m)
  }
}
jiecheng(5)
console.log(result)

五、溢出问题处理

(1)大数相加

var arrA = a.split(''),//将数字转成字符串
    arrB = b.split(''),
    res = [],
    temp = '',
    carry = 0,

    distance = a.length - b.length,  //计算两个数值字符串的长度差
    len = distance > 0 ? a.length : b.length;
  
//在长度小的那个数值字符串前面添加distance个0,这样两个数值的位数就保持一致,
//如:9797979797、34646,需要将这两个数值转成['0','9','7','9','7','9','7','9','7','9','7']、['0','0','0','0','0','3','4','6','4','6']
if(distance > 0){
    for(let i = 0; i < distance; i++){
        arrB.unshift('0');
    }
     
}else{
    for(let i = 0; i < Math.abs(distance); i++){
        arrA.unshift('0');
    }
}

//从数组的最后一位开始向前遍历,把两个数组对应位置的数值字符串转成整形后相加,
//carry表示相加后的进位,比如最后一位相加是7+6=13,这里进位carry是1
//在遍历的时候每次都加上上次相加后的carry进位
for(let i = len - 1; i >= 0; i--){
    temp = +arrA[i] + (+arrB[i]) + carry;
    if(temp > 10){  
        carry = 1;
        res.push((temp + '')[1]);

    }else{
        carry = 0;
        res.push(temp);
    }
      
}
res = res.reverse().join('').replace(/^0/,'');
console.log(res)
var a = '843529812342341234'
var b = '236124361425345435'
function add(a, b) {
  var res = ''
  var c = 0
  a = a.split('')
  b = b.split('')
  while(a.length || b.length || c) {
    // ~是按位取反运算,~~是取反两次
    c += ~~a.pop() + ~~b.pop()
    res = c % 10 + res
    c = c > 9
  }
  console.log(res.replace('/^0+/', ''))
}
add(a, b)

(2)如果阶乘出现数据溢出如何处理

https://blog.csdn.net/qq_27626645/article/details/79028972

https://blog.csdn.net/qq_34587892/article/details/76070330

六、数组乱序,前后相邻输出不一致

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2]
function luanxu(arr) {
  var result = []
  var len = arr.length
  while(len > 0) {
    var a = Math.floor(Math.random() * len)
    result.push(arr[a])
    arr.splice(a, 1)
    len = arr.length
  }
  console.log(result)
}
luanxu(arr)

七、按照年龄进行分类:

[{name:”xiaoxiao’’,age:10},{name:”test”,age:10},{name:”dd”,age:9},{name:”ww”,age:6}]

var person = [{name:'xiaoxiao',age:10},{name:'test',age:10},{name:'dd',age:9},{name:'ww',age:6}]
var obj = {}
function ageClassify(person) {
  for (var i = 0; i < person.length; i++) {
    console.log(person[i].age)
    if (obj[person[i].age] == undefined) {
      obj[person[i].age] = []     
      obj[person[i].age][0] = person[i] 
    } else {
      obj[person[i].age].push(person[i])
    }
  }
  console.log(obj)
}
ageClassify(person)

// 根据年龄升序
var person = [{name:'test12',age:1},{name:'xiaoxiao',age:10},{name:'test',age:10},{name:'dd',age:9},{name:'ww',age:6}]
var obj = []
function ageClassify(person) {
  person.sort((a, b) => {
    return a.age - b.age
  })
  console.log(person)
}
ageClassify(person)

var arr =['abc','abb','abd']
console.log(arr.sort())
//[ 'abb', 'abc', 'abd' ]

八、去重系列

  1. 实现数组flaten(depth)传递具体的参数
    var arr = [1,2,[3,4,[5,6]]]
    function flaten(arr, depth) {
      let result = []
      for (var i = 0; i < arr.length; i++) {
        if (!Array.isArray(arr[i])) {
          result.push(arr[i])
        } else {
          if (depth == 0) {
            result.push(arr[i])
          } else {
            result.push(...flaten(arr[i], depth - 1))
          }
        }
      }
      return result
    }
    console.log(flaten(arr, 1))

     

  2. 扁平化数组:https://github.com/mqyqingfeng/Blog/issues/36
    console.log([1,2,3,[1,2,3],[1,2,[1,2]]].flat(2))  结果:[1, 2, 3, 1, 2, 3, 1, 2, 1, 2]
    
    如果不管有多少层嵌套,都要转成一维数组,可以用Infinity关键字作为参数。
    console.log([1,2,3,[1,2,3],[1,2,[1,2,[1,[1,[1]]]]]].flat(Infinity))
     结果:[1, 2, 3, 1, 2, 3, 1, 2, 1, 2, 1, 1, 1]
    
    console.log([1, [2, [3, 4, [1,2]]]].toString().split(','))
    // [ '1', '2', '3', '4', '1', '2' ]
  3. 数组去重:https://blog.csdn.net/qq_29918313/article/details/79135807
  4. 求数组求和 数组的每一项*2之后求和,利用reduce进行相加
  5. 对象去重:https://blog.csdn.net/qq_29918313/article/details/79135807
  6. 二维数组降为一维数组
    var arr = [1,2,3,[1,2,3],[1,2]]
    function aa(arr) {
      var result = arr.reduce((pre, cur) => {
        return pre.concat(cur)
      }, [])
      console.log(result)
    }
    aa(arr)

     

九、Eg[1,2,3,4,5] =1*2+2*2+3*2+4*2+5*2=30

  1. 数组交换元素,不改变原数组[1,5,7,3,6,8]将3与8交换
  2. Js map实现
  3. 记住展开的层级,用-代表深度

十、零钱问题

var coins = [1,2,5]
var count = 11
var dp = [0]
for (var i = 1; i <= count; i++) {
  dp[i] = count // 钱数为i时最少的硬币数量
}
for (var i = 0; i <= count; i++) {
  for(var j = 0; j < coins.length; j++) {
    if (coins[j] <= i) {
      // 当钱数为i时,最小的硬币数 = min(当前数量,dp[钱数j - 某种硬币面额] + 一张该面额)
      dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1)
    }
  }
}
console.log(dp[count] > count ? -1 : dp[count])

十一、二叉树相关

var root = {
  value: 'A',
  left: {
    value: 'B',
    left: {
      value: 'D',
      left: {
        value: 'H',
        left: null,
        right: null
      },
      right: {
        value: 'I',
        left: null,
        right: null
      }
    },
    right: {
      value: 'E',
      left: null,
      right: null
    }
  },
  right: {
    value: 'C',
    left: {
      value: 'F',
      left: null,
      right: null
    },
    right: {
      value: 'G',
      left: null,
      right: null
    }
  }
}
const preOrderTraverse = root => {
  console.log(root.value) // 访问节点(打印节点的值)
  root.left && preOrderTraverse(root.left) // 若节点的左子树存在,则遍历节点的左子树
  root.right && preOrderTraverse(root.right) // 若节点的右子树存在,则遍历节点的右子树
}
preOrderTraverse(root)
// A B D H I E C F G


const inOrderTraverse = root => {
  root.left && inOrderTraverse(root.left) // 若节点的左子树存在,则遍历节点的左子树
  console.log(root.value) // 访问节点(打印节点的值)
  root.right && inOrderTraverse(root.right) // 若节点的右子树存在,则遍历节点的右子树
}
inOrderTraverse(root)
// H D I B E A F C G


const postOrderTraverse = root => {
  root.left && postOrderTraverse(root.left) // 若节点的左子树存在,则遍历节点的左子树
  root.right && postOrderTraverse(root.right) // 若节点的右子树存在,则遍历节点的右子树
  console.log(root.value) // 访问节点(打印节点的值)
}
postOrderTraverse(root)
// H I D E B F G C A

十二、链表相关

十三、有效的括号

var str = "()[]{}"
function special(str) {
  var obj = {
    '(': -1,
    ')': 1,
    '[': -2,
    ']': 2,
    '{': -3,
    '}': 3
  }
  var result = []
  for (var i = 0; i < str.length; i++) {
    if (obj[str[i]] < 0) {
      result.push(str[i])
    } else {
      var last = result.pop()
      if (obj[last] + obj[str[i]] !== 0) {
        return false
      }
    }
  }
  if (result.length > 0) {
    return false
  } else {
    return true
  }
}

console.log(special(str))

十四、两个数组的交集

var nums1 = [1, 2, 2, 1, 3,3,4,5,3]
var nums2 = [2, 2, 3,3,4,3]
var result = []
nums1.forEach(function(val) {
	if(nums2.includes(val)) {
		result.push(val);
	}
})
console.log(result);  // [ 2, 2, 3, 3, 4, 3 ]


var nums1 = [1, 2, 2, 1, 3,3,4,5,3]
var nums2 = [2, 2, 3,3,4,3]
var result = []
function common(nums1, nums2) {
  for (var i = 0; i < nums1.length; i++) {
    for (var j = 0; j < nums2.length; j++) {
      if (nums1[i] === nums2[j]) {
        result.push(nums1[i])
        nums1.splice(i, 1)
        i--
        nums2.splice(j, 1)
        j--
      }
      continue
    }
  }
  return result
}
console.log(common(nums1, nums2)) // [ 2, 2, 3, 3, 4, 3 ]

十五、对象格式处理

已知:
var entry = {
  a: {
    b: {
      c: {
        dd: 'abcdd'
      }
    },
    d: {
      xx: 'adxx'
    },
    e: 'ae'
  }
}

// 要求转换成如下对象
var output = {
    'a.b.c.dd': 'abcdd',
    'a.d.xx': 'adxx',
    'a.e': 'ae'
}


var keys = []
var output = {}
function flatObj(entry, output) {
  for (var key in entry) {
    var res = entry[key];
    keys.push(key);
    if (typeof res === "object") {
      flatObj(res, output);
    } else {
      output[keys.join(".")] = res;
    }
    keys.pop();
    console.log(keys)
  }
}
flatObj(entry, output);
console.log(output);
// 已知:
var output = {
    'a.b.c.dd': 'abcdd',
    'a.d.xx': 'adxx',
    'a.e': 'ae'
}
// 要求转换成如下对象
var entry = {
  a: {
    b: {
      c: {
        dd: 'abcdd'
      }
    },
    d: {
      xx: 'adxx'
    },
    e: 'ae'
  }
}

对象entry的key中含有的.就是一个对象嵌套,所以可以用split()函数将其划分为一个array,所以array的length - 1下标所对应的元素就是entry的一个key的具体值。
利用对象为地址引用原理,进行增加元素。
采用reduce函数,将每一次的引用返回。
const entry = {
    'a.b.c.dd': 'abcdd',
    'a.d.xx': 'adxx',
    'a.e': 'ae',
  };
  const changObjStructureOfNormal = output => {
    const keys = Object.keys(output);
    const resObj = {};
    for (const key of keys) {
      const everyKey = key.split('.');
      everyKey.reduce((pre, next, index, array) => {
        if (index === array.length - 1) {
          pre[next] = output[key];
          return;
        }
        pre[next] = pre[next] || {};
        return pre[next];
      }, resObj);
    }
    return resObj;
  };
  console.log(changObjStructureOfNormal(entry))
// 原始数组
let arr =[
  {id:1,name:'部门A',parentId:0},
  {id:2,name:'部门B',parentId:0},
  {id:3,name:'部门C',parentId:1},
  {id:4,name:'部门D',parentId:1},
  {id:5,name:'部门E',parentId:2},
  {id:6,name:'部门F',parentId:3},
  {id:7,name:'部门G',parentId:2},
  {id:8,name:'部门H',parentId:4}
]
// 转换后的结果如下
let result = [
    {
      id: 1,
      name: '部门A',
      parentId: 0,
      children: [
        {
          id: 3,
          name: '部门C',
          parentId: 1,
          children: [
            {
              id: 6,
              name: '部门F',
              parentId: 3
            }, {
              id: 16,
              name: '部门L',
              parentId: 3
            }
          ]
        },
        {
          id: 4,
          name: '部门D',
          parentId: 1,
          children: [
            {
              id: 8,
              name: '部门H',
              parentId: 4
            }
          ]
        }
      ]
    },
  ···
];
function convert(arr) {
  let tree = [];
  arr.map((it, idx, array) => {
    console.log(array)
    let parent = it.parentId;
    if (parent === 0) {  // 根节点        
      tree.push(it);
    } else {        
      array.map(item => {
        if (item.id === parent) {
          if (!item.children) {
            item.children = [];
          }
          item.children.push(it);
        }
      });
    }
  });
  return tree;
}
console.log(convert(arr))

 

十六、实现(5).add(3).minus(1),使得其结果为5+3-1=7

Number.prototype.add = function (n) {
  return this.valueOf() + n
}

Number.prototype.minus = function (n) {
  return this.valueOf() - n
}
var a = (5).add(3).minus(1)
console.log(a) // 7

十七、按月份统计销售额

{1:222, 2:123, 5:888},请把数据处理为如下结构:[222, 123, null, null, 888, null, null, null, null, null, null, null]

var obj = {1:222, 2:123, 5:888}
var arr = [1, 2, 3, 4, 5,6,7,8,9,10,11,12]
var result = []
for (var i = 0; i < arr.length; i++) {
  for (var k in obj) {
    if (arr[i] == k) {
      result[i] = obj[k]
      break
    } else {
      result[i] = null
    }
  }
}
console.log(result)

// 生成月份
var arr1 = Array.from({length: 12}, (v, k) => k)
console.log(arr1)

十八、正则相关

g修饰符用语规定正则表达式执行全局匹配,也就是在找到第一个匹配之后仍然会继续查找。

1.驼峰式转换

var str = 'border-bottom-color'
var reg = /-(\w)/g
var str1 = str.replace(reg, function ($0, $1) {
  return $1.toUpperCase()
})
console.log(str1) // borderBottomColor
// $0代表匹配到的东西 -b,-c;$1代表第一个括号,就是用B去替换-b,用C去替换-c

2.校验email

var email = 'dsdfs_sdfsf@sina.com'
var reg2 = /^\w+@\w+\.[a-z]{2,4}$/i  // i为统一转化为小写
console.log(reg.test(email))

var reg1 = /^\w+@\w+\.[a-zA-Z]{2,4}$/
var reg2 = /^\w+@\w+\.[a-z]{2,4}$/i

3.校验手机号

var phone = '13865636710'
var reg = /^1[3|5|7|8|9](\d){9}$/

4.大小写转换

var str = 'vvQwesdfQWE'
var reg = /[a-z]/
var result = []
for (var i = 0; i < str.length; i++) {
  if (reg.test(str[i])) {
    result.push(str[i].toUpperCase())
  } else {
    result.push(str[i].toLowerCase())
  }
}
var result1 = result.join('')
console.log(result1)
    var str = 'border-bottom-color'
    var reg = /-(\w)(\w)/g
    str=str.replace(reg,function($0,$1,$2){
        return $1.toUpperCase()+$2.toUpperCase()
    })
    console.log(str) //borderBOttomCOlor

 5.千分位分割

var a = '2221221225.88';
var b = a.replace(/(\d{3})(?=\d)/g, '$1,')
解析:如下第一个括号(\d{3})代表的是3个数字;
第二个括号\d代表数字,也就是在满足前面的情况下,下一个是数字的就满足要匹配的东西;
$1代表第一个括号的内容;结果如下console.log(b)//222,122,122,5.88 
正则表达式(?=pattern)的语法
正向肯定预查(look ahead positive assert),在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,”Windows(?=95|98|NT|2000)”能匹配”Windows2000”中的”Windows”,但不能匹配”Windows3.1”中的”Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
同样,通过这三个列子的结果,我们也可以理解下面这句话了
预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始 
我们再来对比一下列1和列2
var a = '13245646';
//列1
var reg1 = /(\d{3})(\d)/g
var reg2 = /(\d{3})(?=\d)/g
console.log('reg1:', a.match(reg1) )
console.log('reg2:',a.match(reg2) )
//输出
//reg1: (2) ["1324", "5646"]
//reg2: (2) ["132", "456"]

可以看到reg1在下一次的匹配中,是从(\d)之后的下一个字符开始匹配,而reg2中的下一次匹配时,却是直接从(\d{3})的下一个字符开始匹配。

6.将某个字符替换中想要的字符

// 将所有的3替换成9
var phone = '13865636710'
var reg = /[3]/g
var a = phone.replace(reg, '9')
console.log(a)

7.判断输入的是否是正确的网址

var str = 'https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/138'
var reg =  /^(https?:\/\/)?([a-z0-9]\.|[a-z0-9][-a-z0-9]*[a-z0-9]\.)*([a-z]+)(:\d+)?(\/.*)?$/
console.log(reg.test(str))

十九、随机生成一个数组,并按规则将其转化为二维数组

随机生成一个长度为 10 的整数类型的数组,例如 [2, 10, 3, 4, 5, 11, 10, 11, 20],将其排列成一个新数组,要求新数组形式如下,例如 [[2, 3, 4, 5], [10, 11], [20]]

// 得到一个两数之间的随机整数,包括两个数在内
function getRandomIntInclusive(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min + 1)) + min; //含最大值,含最小值 
}
// 随机生成10个整数数组, 排序, 去重
let initArr = Array.from({ length: 10 }, (v) => { return getRandomIntInclusive(0, 99) });
initArr.sort((a,b) => { return a - b });
initArr = [...(new Set(initArr))];
var obj = {}
for (var i = 0; i < initArr.length; i++) {
  var result = Math.floor(initArr[i] / 10)
  if(!obj[result]) {
    obj[result] = []
    obj[result][0] = initArr[i]
  } else {
    obj[result].push(initArr[i])
  }
}
var result = []
for (var k in obj) {
  result.push(obj[k])
}
console.log(result) //[ [ 17 ],[ 23 ],[ 30, 37 ],[ 43, 46 ],[ 56 ],[ 63, 69 ],[ 77 ] ]

二十、旋转数组

原数组中的元素向右移动K位。

var arr = [1, 2, 3, 4, 5, 6, 7]
var k = 3
function rotate(arr, k) {
  if (k == 0) return arr
  var newArr = arr.splice(arr.length - k)
  // newArr.push.apply(newArr, arr)
  // console.log(newArr)
  var result = [...newArr, ...arr]
  console.log(result)
}
rotate(arr, k)

二十一、移动零

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

function move(arr){ 
  var n = 0; 
  for(var i = 0; i < arr.length; i++){ 
    if(arr[i] === 0){ 
       arr.splice(i, 1);
       n++; 
       i--; 
    }
  } 
  while(n--){ 
    arr.push(0); 
  }
  return arr;
}
console.log(move(arr))

二十二、求两个数组的中位数

var nums1 = [1, 2]
var nums2 = [3]
nums1.push.apply(nums1, nums2)
nums1.sort((a, b) => {
  return a - b
})
var result = 0
var mid = nums1.length/2
if (nums1.length % 2 == 0) {
  result = (nums1[mid - 1] + nums1[mid]) / 2
} else {
  result = nums1[Math.floor(mid)]
}
console.log(result)

二十三、数值范围内1的个数,从1开始的某个范围

function NumberOf1Between1AndN(n){
  let count = 0;
  for (let i = 0; i <= n; i++) {
    let temp = i;
    while (temp > 0) {
      if (Math.floor(temp % 10) == 1) {
        count++;
      }
      temp /= 10;
    }
  }
  return count;
}
var n = 20
console.log(NumberOf1Between1AndN(n))

二十四、判断出栈顺序是否合法

var stackA = [1,2,3,4,5]
var stackB = [3,1,2,5,4]
function aa(stackA, stackB) {
  var result = []
  var j = 0
  for (var i = 0; i < stackA.length; i++) {
    result.push(stackA[i])
    while(result.length != 0 && result[result.length - 1] == stackB[j]) {
      result.pop()
      j++
    }
  }
  if (result.length == 0) {
    return true
  } else {
    return false
  }
}
console.log(aa(stackA, stackB))

二十五、输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

function NumberOf1(n)
{
    if(n < 0) {
        n = n >>> 0
    }
    var newN = n.toString(2).split('1')
    return newN.length - 1
}

二十六、输入一个链表,输出该链表中倒数第k个结点。

function FindKthToTail(head, k)
{
   var arr = []
   while(head != null) {
       arr.push(head)
       head = head.next
   }
    return arr[arr.length - k]
}

二十七、两个数不使用四则运算得出和

function sum(a, b) {
  if (a == 0) return b
  if (b == 0) return a
  var newA = a ^ b
  var newB = (a & b) << 1
  return sum(newA, newB)
}
console.log(sum(10, 11))

二十八、0 - 1背包问题

该问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。每个问题只能放入至多一次。

假设我们有以下物品

物品 ID / 重量价值
13
27
312

对于一个总容量为 5 的背包来说,我们可以放入重量 2 和 3 的物品来达到背包内的物品总价值最高。

对于这个问题来说,子问题就两个,分别是放物品和不放物品,可以通过以下表格来理解子问题

物品 ID / 剩余容量012345
1033333
2037101010
3037121519

直接来分析能放三种物品的情况,也就是最后一行

  • 当容量少于 3 时,只取上一行对应的数据,因为当前容量不能容纳物品 3
  • 当容量 为 3 时,考虑两种情况,分别为放入物品 3 和不放物品 3
    • 不放物品 3 的情况下,总价值为 10
    • 放入物品 3 的情况下,总价值为 12,所以应该放入物品 3
  • 当容量 为 4 时,考虑两种情况,分别为放入物品 3 和不放物品 3
    • 不放物品 3 的情况下,总价值为 10
    • 放入物品 3 的情况下,和放入物品 1 的价值相加,得出总价值为 15,所以应该放入物品 3
  • 当容量 为 5 时,考虑两种情况,分别为放入物品 3 和不放物品 3
    • 不放物品 3 的情况下,总价值为 10
    • 放入物品 3 的情况下,和放入物品 2 的价值相加,得出总价值为 19,所以应该放入物品 3

以下代码对照上表更容易理解

/**
 * @param {*} w 物品重量
 * @param {*} v 物品价值
 * @param {*} C 总容量
 * @returns
 */
function knapsack(w, v, C) {
  let length = w.length
  if (length === 0) return 0

  // 对照表格,生成的二维数组,第一维代表物品,第二维代表背包剩余容量
  // 第二维中的元素代表背包物品总价值
  let array = new Array(length).fill(new Array(C + 1).fill(null))

  // 完成底部子问题的解
  for (let i = 0; i <= C; i++) {
    // 对照表格第一行, array[0] 代表物品 1
    // i 代表剩余总容量
    // 当剩余总容量大于物品 1 的重量时,记录下背包物品总价值,否则价值为 0
    array[0][i] = i >= w[0] ? v[0] : 0
  }

  // 自底向上开始解决子问题,从物品 2 开始
  for (let i = 1; i < length; i++) {
    for (let j = 0; j <= C; j++) {
      // 这里求解子问题,分别为不放当前物品和放当前物品
      // 先求不放当前物品的背包总价值,这里的值也就是对应表格中上一行对应的值
      array[i][j] = array[i - 1][j]
      // 判断当前剩余容量是否可以放入当前物品
      if (j >= w[i]) {
        // 可以放入的话,就比大小
        // 放入当前物品和不放入当前物品,哪个背包总价值大
        array[i][j] = Math.max(array[i][j], v[i] + array[i - 1][j - w[i]])
      }
    }
  }
  return array[length - 1][C]
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Delicia_Lani

你的鼓励将是我写作的动力。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值