算法入门【上】

289 阅读5分钟

一、求最小值的函数

(一)必备知识

1.数据结构: 数组[1,2,3,4]

2.编程知识: ?:表达式

(二)从两个数中找出最小的那个

  • 给一个二元数组
  • 如果第一个数小于第二个数
  • 就输出第一个数
  • 否则输出第二个数

1.第一版

let minOf2 = (numbers) => {
    if (numbers[0]<numbers[1]) {return numbers[0] }
    else {return numbers[1]}
}

2.第二版代码:优化

let minOf2 = (numbers) => number[0]<number[1]?number[0]:number[1]

3.第三版代码: 析构赋值

let minOf2 = ([a,b]) => a<b ? a : b

4.现成API

JS内置了Math.min

Math.min(1,2) //1
Math.min.call(null,1,2) 
Math.min.call(null,[1,2])

关于Math

  • 看起来Math像Object一样是构造函数
  • 实际上Math只是一个普通对象
  • 这是唯一的特例:首字母大写是构造函数

(三)从三个数中找出最小的那个

  • 给一个三元数组
  • 我们已经会从两个数中找最小值了
  • 那就先找出数组中后两个数的最小值
  • 再从数组中第一个数和刚才得到的最小值两者中找最小值

1.第一版代码

let minOf3 = numbers => {
    return minOf2([numbers[0],minOf2(numbers.slice(1))])
}

2.第二版代码:析构赋值

let minOf3 = ([a,b,c]) => minOf2([a,minOf2([b,c])])

(四)从四个数中找出最小的那个

  • 给一个四元数组
  • 我们已经会从三个中找出最小数
  • 那就先从数组的后三个中找出最小数
  • 在从数组中第一个数和上面那个最小数两者中找出最小数

1.第一版代码

let minOf4 = ([a,b,c,d]) => minOf2([a,minOf3([b,c,d])])

(五)发现规律

  • 求一个n元数组中的最小值
  • 先把第一个数提出来
  • 再求出剩下的的最小值
  • 最后求出第一个数和后来求出的那个最小值两者的最小值

(六)任意长度数组求最小值

第一版代码

let min = (numbers) => {
   return min([numbers[0],min(numbers.slice(1))])
}

但是这个代码会死循环不停调用自己,应该添加一个终止条件

第二版代码

let min = (numbers) => {
    if(numbers.length>2){  //当数组的长度大于2时才如此循环调用自己
        return min([numbers[0],min(numbers.slice(1))])
    }
    else {   //当数组长度小于等于2时就用现成的简单的一个函数执行
          return Math.min.apply(null,numbers)
    }
}

这就是递归

二、递归

1.特点

  • 函数不停调用自己,每次调用的参数略有不同
  • 当满足某个简单条件时,则实现一个简单的调用
  • 最终算出结果 2.理解 可以用代入法快速理解递归 可以用调用栈快速理解递归
递进          min([2,4,3,1])
递进 4>2  === min([2,min([4,3,1])])
递进 3>2  === min([2,min([4,min([3,1])])])
递进 2<=2 === min([2,min([4,Math.min.apply(null,[3,1])])])
回归      === min([2,min([4,1])])
回归 2<=2 === min([2,Math.min.apply(null,[4,1])])
回归      === min([2,1])
回归 2<=2 === Math.min.apply(null,[2,1])
回归      === 1


递进          sort([2,4,3,1])
递进 4>2  === [1,sort([2,4,3])] 
递进 3>2  === [1,[2,sort([4,3])]]
回归 2<=2 === [1,[2,[3,4]]]
回归 2<=2 === [1,[2,3,4]]
回归 2<=2 === [1,2,3,4]

三、将正整数数组从小到大排序(选择排序的递归写法)

(一)思路

  • 数据结构:数组
  • 逻辑:循环、递归

(二)对两个数进行排序

  • 给定个二元数组
  • 如果第一个数大于第一个数
  • 就两者调换位置
  • 否则不变

第一版代码

let sort2 = ([a,b]]) => a>b ? [b,a] : [a,b]

第二版代码

let sort2 = numbers => numbers[0]>numbers[1] ? numbers.reverse() : numbers

(三)对三个数进行排序

  • 给定一个三元数组
  • 先选出最小值
  • 我们已经知道两元数组怎么排序
  • 那么就可以让剩下的两位排序
  • 最后把最小值和上面的两位排序结合起来就行了

第一版代码:析构赋值

let sort3 = ([a,b,c]) => {
    return[min([a,b,c]),sort2(???)]
}

发现无法将最小值从数组里删掉

第二版代码

let sort3 = (numbers) => {
    let index=numbers.indexOf(min(numbers)) //找出最小值的下标,并且命名为index
    let min = numbers[index] //找出最小值并且命名为min
    numbers.splice(index,1)  //从数组中删去最小值,因此该数组变成了一个新数组了
    return [min].concat(sort2(numbers))
}

(四)对四个数进行排序

  • 给定一个四元数组
  • 找出最小值
  • 剩下的三个数字排序
  • 把最小值和上面的排序结合起来就行了

代码

let sort4 = (numbers) => {
    let index=numbers.indexOf(min(numbers)) //找出最小值的下标,并且命名为index
    let min = numbers[index] //找出最小值并且命名为min
    numbers.splice(index,1)  //从数组中删去最小值,因此该数组变成了一个新数组了
    return [min].concat(sort3(numbers))
}

(五)总结规律:对N个数从小到大排序

每次找到最小的数放前面,然后对剩下的数组做同样的事情

  • 给定一个数组
  • 先找出最小值(因为要排在第一个)放在最前面
  • 剩下的数组进行排序(套娃开始了)
  • 把最小值和上面的排序结合起来就行了。
  • 排到什么时候不排了呢?排到数组里只剩下两个数就不用排了,直接比较谁大谁小。

第一版代码

let sort = (numbers) => {
    let index=numbers.indexOf(min(numbers)) //找出最小值的下标,并且命名为index
    let min = numbers[index] //找出最小值并且命名为min
    numbers.splice(index,1)  //从数组中删去最小值,因此该数组变成了一个新数组了
    return [min].concat(sort(numbers))
}

死循环又出现了

第二版代码

let min = (numbers) => {
  if(numbers.length > 2){
    return min(
      [numbers[0], min(numbers.slice(1))]
    )
  }else{
    return Math.min.apply(null, numbers)
  }
}

let minIndex = (numbers) =>
  numbers.indexOf(min(numbers))


let sort = (numbers) => {
  if(numbers.length > 2){           //当数组的长度大于2时才如此循环调用自己
    let index = minIndex(numbers)  //拿到numbers最小值的下标
    let min = numbers[index]  //拿到最小值
    numbers.splice(index, 1)  //从numbers里删除 min(最小值)
    return [min].concat(sort(numbers))
    }else{                    //当数组长度小于等于2时就用现成的简单的一个函数执行
    return numbers[0]<numbers[1] ? numbers : numbers.reverse()
  }
}

四、找出数组中最小值的下标

方法一

取巧的办法,以后会有更好的

let minIndex = (numbers) => numbers.indexOf(min(numbers))

方法二:循环

  1. 首先让index就是表示最小值的下标。首先默认第一个数就是最小值。那么刚开始index=0
  2. 从数组中第二个数开始,每个数都和最小值比较,如果哪个数比最小值还小,就让这个数成为新的最小值。
  3. 直到数组中最后一个数和最小值比完就可以结束了。
let minIndex = (numbers) =>
let index = 0
for(let i=1; i<numbers.length; i++){
if(numbers[i] < numbers[index]){
index = i
}
}
return index
}

五、选择排序的循环写法

思路不变:

  1. 每次找到最小的数放前边,然后i++

  2. 但是是在新的波浪线数组里找到最小的,放到波浪线新数组的最前面。

  3. 对应原数组的话,那就是在原数组生成的波浪线数组里面找到最小的,放到第i个。

代码:

let sort = (numbers) => {
  for (let i = 0; i < numbers.length - 1; i++) {
    let index = minIndex(numbers.slice(i)) + i  //找到波浪线数组中的最小值,在原数组中对应的下标
    if (index !== i) { swap(numbers, index, i) }  //把第i个和最小的交换
  }
  return numbers
}


//辅助函数,交换一个数组里面两个元素
let swap = (array, i, j) => {
  let temp = array[i]
  array[i] = array[j]
  array[j] = temp
}

//辅助函数,找到一个数组里的最小值的下标
let minIndex = (numbers) => {
  let index = 0
  for (let i = 1; i < numbers.length; i++) {
    if (numbers[i] < numbers[index]) {
      index = i
    }
  }

用log看,更明显

let sort = (numbers) => {
  for (let i = 0; i < numbers.length - 1; i++) {
    console.log(`----`) 
    console.log(`i: ${i}`)
    let index = minIndex(numbers.slice(i)) + i
    console.log(`index: ${index}`)
    console.log(`min: ${numbers[index]}`)
    if (index !== i) {
      swap(numbers, index, i)
      console.log(`swap ${index}: ${i}`)
      console.log(numbers)
    }
  }
  return numbers
}

代码出错解决方法

console,log调试