从零开始学算法

408 阅读6分钟

起手诗

迎着晨风想一想,今天该如何努力
踏着夕阳问一问,今天有什么收获
从今天起,从零开始学算法,不忘初心,继续前进
I hava a drame 从算法小白,一步步走向大神
做人就是敢吹牛,牛逼的人就是吹完牛,像牛一样工作!!!

第一天 【2020-03-31】

给定一个仅包含数字 2-9的字符串,返回所有它能表示的字母组合。 给出数字与电话按键相同。注意 1 不对应任何字母。

/*电话号码的组合*/
function phone(str){
  // 建立电话号码键盘映射 
  let map = ['', 1, 'abc', 'def', 'ghi', 'jkl',
  'mno', 'pqrs', 'tuv', 'wxyz'],
  // 把输入字符串按单字符分割变成数组 234 => [2,3,4]
    num = str.split(''),
  // 保存键盘映射后的字母内容 , 如 23 => ['abc','def']
    code = []
    num.forEach(item => {
    if (map[item]) {
       code.push(map[item])
     }
    })

    let comb = (arr) => {
  // 临时变量用来保存前两个组合的结果
    let tmp = []
  // 最外层的循环剩遍历第一个元素里层的循环是遍历第二个元素
    for (let i = 0, il = arr[0].length; i < il; i++) {
        for (let j = 0, jl = arr[1].length; j < jl; j++) {
            tmp.push(`${arr[0][i]}${arr[1][j]}`)
        }
    }
   
   arr.splice(0, 2, tmp);
   if (arr.length > 1) {
     comb(arr)
   } else {
     return tmp;
   }
       return arr[0];  
 }
    return comb(code)
}
console.log(phone("23"))
[
  'ad', 'ae', 'af',
  'bd', 'be', 'bf',
  'cd', 'ce', 'cf'
]

第二天 【2020-4-1】

nums包含从0到n的所有整数,但其中缺失了一个。请编写代码找出那个缺失的整数。

var missingNumber = function (nums) {
    let arr = []; 
    let brr = [];
    //计数器
    let count = 0; 
    // =>从小到大排序
    nums = nums.sort((A, B) => A - B)  
    //=>找出数组中的最大值
    let maxNum = Math.max.call(null, ...nums);
    for (let i = 0; i <= maxNum; i++) {  
        if (i <= maxNum) {
            arr.push(i);
        }
    }
    //=>arr =>从小到大的完整数组
    for (let i = 0; i < arr.length; i++) {
        if (nums.includes(arr[i])) {
            count++;
            //=>说明,两个数组完全相同
            if (count === arr.length) { 
                //=>如果两个数组一样,则返回传递进来数组的长度
                return nums.length;
            }
        } else {
            //=> arr[i]=>缺失的整数
            brr.push(arr[i]) 
        }
    }
    return brr  //=>以一个数组的形式,返回缺失的数字
};

第三天 【2020-4-2】

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

var removeElement = function (nums, val) {
        <!--获取数组的长度-->
     let ans = nums.length;
        for (let i = 0; i < ans;) {
            //=>如果当前项 = 查找的那一项
            if (nums[i] == val) { 
            //=>把最后一项,赋值给当前项,再把最后一项他给删了
                nums[i] = nums[ans - 1]; 
                ans--;
            } else {
                //=>如果没找到,i++ 走循环,判断下一项
                i++; 
            }
        }
        return ans;
};
console.log(removeElement([0, 1, 2, 2, 3, 0, 4, 2], 2));
//=> 5 

第四天 【2020-4-3】

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值

不存在数组中,返回它将会被按顺序插入的位置

<!--一开始老想转换成字符串,其实这样不仅麻烦,而且还让面试官感到LOU-->
var searchInsert = function(nums, target) {
    <!--先循环出每一项-->
    for (let i = 0; i < nums.length; i++) {
        <!--目标值和数组的每一项都不相等-->
        if (nums[i] !== target) {
            if(target > nums[nums.length - 1]){
                return nums.length
            }else{
                 <!--如果目标值小于数组的某一项,直接返回当前项的索引-->
                 <!--
                 首先还是要分析好逻辑,我一开始想用splice插入
                 然后转换成字符串,然后获取索引了,太麻烦.
                 -->
               if (target < nums[i]) {
                    return i
                } 
            }
        }else{
            <!--目标值和数组中的某一项相等,直接返回对应的索引-->
            return nums.indexOf(target)
            break;
        }
    }
};

第五天 【2020-4-6】

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

let maxSubArray = function(nums) {
    <!--首先取出数组的第一项-->
    let item = nums[0];
    let sum = 0;
    <!--对数组进行遍历-->
    for(const num of nums) {
        if(sum > 0) {
            sum += num;
        } else {
            sum = num;
        }
        <!--上一次计算的结果,都和sum进行比对并取出最大值-->
        item = Math.max(item, sum);
    }
    <!--最后返回最大子序列的和-->
    return item; 
    
};

第六天 【2020-4-7】

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

var isPalindrome = function (x) {
    <!--考虑边界case和正常case-->
    if (x < 0 || (x % 10 === 0 && x !== 0)) {
        return false;
    }
    <!--~ 按位非(反转操作数的比特位,即0变成11变成0)。-->
    <!--写两个主要是为了去除小数点后面的数字-->
    let rev = 0;
    while (rev < x) {
        rev = rev * 10 + x % 10;
        x = ~~(x / 10);
    }
    <!--分别是考虑的奇数情况和偶数情况-->
    return (rev === x) || (~~(rev / 10) === x)
};

console.log(isPalindrome(121))

第七天 【2020-7-6】

如何格式化成美元的格式

function formateValue(num) {
    if (!num) return;
    // 先将数值取两位小数
    let str = (Math.round(num * 100) / 100).toString();
    // 找到小数点的下标
    let pointIdx = str.indexOf(".");
    // 如果没有小数点
    if (pointIdx < 0) {
      // 将数值进行千位符转换
      str = str.replace(/(\d)(?=(?:\d{3})+$)/g, "$1,");
      // 添加小数点
      pointIdx = str.length;
      str += ".";
      while (str.length <= pointIdx + 2) {
        str += "0";
      }
    } else {
      // 如果有小数点
      // 取整数部分, 也可以用 parseInt
      let int = str.substr(0, pointIdx);
      // 整数部分进行千位符转换
      int = int.replace(/(\d)(?=(?:\d{3})+$)/g, "$1,");
      str = int + str.substr(pointIdx);
      while (str.length <= pointIdx + 3) {
        str += "0";
      }
    }
    return "US$" + str;
  }

第八天 【2020-7-14】

要求返回数组中属性值最大的对象

let list = [{ name: '11' }, { sex: '22' }, { age: "33" }]
let obj = {};
let otherObj = {};
list.map((item => {
  Object.assign(obj, item)
}))

let val = Math.max.apply(Math, Object.values(obj));
let keys = Object.keys(obj);
for(let i = 0;i<keys.length;i++){
      let item = keys[i];
      if(obj[item] == val){
          return otherObj[item] = val
      }
}
<!--写完这个的时候还遇到一个小问题 ,我之前return的时候是这样写的:
return {item:obj[item]}==>{item:33},后来一想,肯定不行啊
这是对象,对象是heap(堆),肯定不会像上级作用域查找啊-->

第九天 【2021-3-8】

判断括号是否合法

 /* 给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
 * 有效字符串需满足:
 *  左括号必须用相同类型的右括号闭合。
 *  左括号必须以正确的顺序闭合。
 * (输入: "()"
 * 输出: true
 * 输入: "([)]"
 * 输出: false
 */
let isValid = function (s) {
    if (s == " " || s == "") {
        return true;
    }
    let stack = [];
    let left = ["{", "(", "["];
    let right = ["}", ")", "]"];
    for (let i = 0; i < s.length; i++) {
        if (left.indexOf(s[i]) > -1) {
            stack.push(s[i]);
        } else if (s[i] === " ") {
            continue;
        } else {
            if (stack.pop() != left[right.indexOf(s[i])]) {
                return false;
            }
        }
    }
    if (stack.length != 0) {
        return false;
    }
    return true;
};
console.log(isValid("[(])")) 

第十天 【2021-3-9】

身高问题

/**
  * 获取队中从前到后每个人与前方身高高于自己的人的最短距离
  * @param height int整型一维数组 队中从前到后每个人与前方身高高于自己的人的最短距离
  * @return int整型一维数组
  */

function DistanceToHigher(height) {
  let arr = [0];
  for (let i = 0; i < height.length; i++) {
    
}
// 0 1 2 1 0 1
let a = [175, 173, 174, 163, 182, 177];
console.log(DistanceToHigher(a))

排序算法在总结

冒泡排序

  • 第一项和第二项相比,如果大于第二项则互换位置,每次循环完成一遍都会把最大的值放到数组的最后一个 所以内层循环为了不再重复比较,一定要 -i
function bubbleSort(arr) {
    var len = arr.length;
    for (var i = 0; i < len - 1; i++) {
        for (var j = 0; j < len - 1 - i; j++) {
            if (arr[j] > arr[j+1]) {        // 相邻元素两两对比
                var temp = arr[j+1];        // 元素交换
                arr[j+1] = arr[j];
                arr[j] = temp;
            }
        }
    }
    return arr;
}

插入排序

  • 会打牌就会插入排序
function insert(A, i, x) {
  let p = i - 1;
  while (p >= 0 && A[p] > x) {
    A[p + 1] = A[p];
    p--;
  }
  A[p + 1] = x;
}
function insert_sort(A) {
  for (let i = 1; i < A.length; i++) {
    insert(A, i, A[i]);
  }
  return A
}
console.log(insert_sort([3,2,1]));
// 3,2,1
// 3,3,1 ===> 2,3,1
// 2,3,3 ===> 2,2,3===>1,2,3

归并排序

  • 自顶向下
function merge(A, p, q, r) {
  let A1 = A.slice(p, q);
  let A2 = A.slice(q, r);
  A1.push(Number.MAX_SAFE_INTEGER);
  A2.push(Number.MAX_SAFE_INTEGER);
  for (let k = p, i = 0, j = 0; k < r; k++) {
    A[k] = A1[i] < A2[j] ? A1[i++] : A2[j++];
  }
}
function merge_sort(A, p, r) {
  if (r - p < 2) {
    return;
  }
  const q = Math.ceil((p + r) / 2);
  merge_sort(A, p, q);
  merge_sort(A, q, r);
  merge(A, p, q, r);
}
const arr = [3, 2, 1];
merge_sort(arr, 0, arr.length);
#### 选择排序 
- **假设法**
```js
function selectionSort(arr) {
    var len = arr.length;
    var minIndex, temp;
    for (var i = 0; i < len - 1; i++) {
        minIndex = i;
        for (var j = i + 1; j < len; j++) {
            if (arr[j] < arr[minIndex]) {     // 寻找最小的数
                minIndex = j;                 // 将最小数的索引保存
            }
        }
        temp = arr[i];
        arr[i] = arr[minIndex];
        arr[minIndex] = temp;
    }
    return arr;
}

计数排序

  • 先统计数组中每个数出现了多少次,放到对应的数组下标中
function countingSort(arr) {
  let maxValue = Math.max(arr)
  let bucket = new Array(maxValue+1),
      sortedIndex = 0;
      arrLen = arr.length,
      bucketLen = maxValue + 1;

  for (let i = 0; i < arrLen; i++) {
      if (!bucket[arr[i]]) {
          bucket[arr[i]] = 0;
      }
      bucket[arr[i]]++;
  }

  for (let j = 0; j < bucketLen; j++) {
      while(bucket[j] > 0) {
          arr[sortedIndex++] = j;
          bucket[j]--;
      }
  }
  return arr;
}

基数排序

function radix_sort(arr) {
    // 取最大值 最大值的位数就是要循环遍历的次数
    const max = Math.max(...arr);
  
    // 定义一个桶
    const buckets = Array.from({ length: 10 }, () => []);
  
    // 定义当前要遍历的位数 个位 十位 百位...
    let m = 1;
    while (m < max) {
      // m < 最大值
      // 下方m要 m*=10 -> 每次遍历增加一位
      // 保证遍历完所有可能的位数
  
      // 放入桶
      arr.forEach(number => {
        // digit表示某位数的值
        const digit = ~~((number % (m * 10)) / m);
  
        // 把该位数的值放到桶buckets中
        // 通过索引确定顺序 类比计数排序
        buckets[digit].push(number);
      });
  
      // 从桶buckets中取值
      // 完成此步后 就完成了一次位数排序
      let ind = 0;
      buckets.forEach(bucket => {
        while (bucket.length > 0) {
          // shift从头部取值
          // 保证按照队列先入先出
          arr[ind++] = bucket.shift();
        }
      });
  
      // 每次最外层while循环后m要乘等10
      // 也就是要判断下一位 比如当前是个位 下次就要判断十位
      m *= 10;
    }
  }

桶排序

function countingSort(arr) {
  let maxValue = Math.max(arr)
  let bucket = new Array(maxValue+1),
      sortedIndex = 0;
      arrLen = arr.length,
      bucketLen = maxValue + 1;

  for (let i = 0; i < arrLen; i++) {
      if (!bucket[arr[i]]) {
          bucket[arr[i]] = 0;
      }
      bucket[arr[i]]++;
  }

  for (let j = 0; j < bucketLen; j++) {
      while(bucket[j] > 0) {
          arr[sortedIndex++] = j;
          bucket[j]--;
      }
  }
  return arr;
}
function bucketSort(arr, bucketSize) {
  if (arr.length === 0) {
    return arr;
  }

  let i;
  let minValue = arr[0];
  let maxValue = arr[0];
  for (i = 1; i < arr.length; i++) {
    if (arr[i] < minValue) {
        minValue = arr[i];                // 输入数据的最小值
    } else if (arr[i] > maxValue) {
        maxValue = arr[i];                // 输入数据的最大值
    }
  }

  // 桶的初始化
  bucketSize = (maxValue - minValue)/arr.length +1
  let bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1;  
  let buckets = new Array(bucketCount);
  for (i = 0; i < buckets.length; i++) {
      buckets[i] = [];
  }

  // 利用映射函数将数据分配到各个桶中
  for (i = 0; i < arr.length; i++) {
      buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);
  }

  arr.length = 0;
  for (i = 0; i < buckets.length; i++) {
       // 对每个桶进行排序,这里使用了插入排序
      for (let j = 0; j < buckets[i].length; j++) {
          arr.push(buckets[i][j]);                     
      }
  }

  return arr;
}
console.log(bucketSort([8,7,3,3,18]));