JavaScript编程题

278 阅读32分钟

leetcode

9. sqrt() 向下取整后的整数 输入:8 = 2.82 输出: 2

var mySqrt = function(x) {
    //暴力解法 
    if(x <= 1)return x;
    //parseInt(x / 2) + 2 一个1是取整后的值 一个1是遍历足够次数
    for(let i = 1; i < parseInt(x / 2) + 2; i++){
        //暴力访问每个值的平方
        if(i * i  > x) return i - 1;
    }
};
console.log(mySqrt(2));

var mySqrtI = function(x) {
    //二分法
    //考虑0
    if(x <= 0){
        return 0;
    }
    //left为1考虑n为1的情况
    let left = 1, right = x, mid;
    while(left + 1 < right){
        mid = parseInt((left + right) / 2);
        if(mid * mid === x) return mid;
        if(x > mid *  mid) left = mid;
        else right = mid;
    }
    return left;
};
console.log(mySqrtI(8));

72. 动态规划 最短编辑距离

/**
 * @param {string} word1
 * @param {string} word2
 * @return {number}
 */
//动态规划
var minDistance = function(word1, word2) {
    let dp = [];
    dp[0] = [];
    //初始化dp[i,j]word1中第i个匹配Word2中第j个的最短编辑距离
    for(let j = 0; j <= word2.length; j++){
        dp[0][j] = j;
    }
    for(let i = 0; i <= word1.length; i++){
        if(dp[i]){
            dp[i][0] = i;
         }else{
            dp[i] = [];
            dp[i][0] = i;
        }
    }
    //动态规划
    for(let k = 1; k <= word1.length; k++){
        for(let h = 1; h <= word2.length; h++){
            if(word1[k - 1] === word2[h - 1]){
                dp[k][h] = dp[k - 1][h - 1];
            }else{
                let a = dp[k - 1][h] + 1;
                let b = dp[k][h - 1] + 1;
                let c = dp[k - 1][h - 1] + 1;
                dp[k][h] = Math.min(a, b, c);
            }
        }
    }
    return dp[word1.length][word2.length];
};
//递归
var minDistance = function(word1, word2) {
    return minDistanceHelper(word1, word2, word1.length, word2.length);  
};
function minDistanceHelper(word1, word2, i, j){
    if(i === 0) return j; //添加
    if(j === 0) return i; //删除
    if(word1[i - 1] === word2[j - 1]){
        return minDistanceHelper(word1, word2, i - 1, j - 1);
    }else{
        let a = minDistanceHelper(word1, word2, i - 1, j) + 1;//删
        let b = minDistanceHelper(word1, word2, i, j - 1) + 1;//增
        let c = minDistanceHelper(word1, word2, i - 1, j - 1) + 1;//改
        return Math.min(a, b, c);
    }
}

73. 动态规划 编辑距离为1 //双指针考虑边界范围

//判断编辑距离为1
function editDistace(word1, word2){
    let distance = word1.length - word2.length;
    if(Math.abs(distance) > 1){
        return false;
    }
    let i = 0, j = 0, isEdit = false;
    while(i < word1.length && j < word2.length){
        if(word1[i] === word2[j]){
            i++;
            j++;
        }else{
            if(isEdit){
                return false;
            }else if(distance === 1){
                //删除
                i++;
            }else if(distance === -1){
                //增加
                j++;
            }else{
                //修改
                i++;
                j++;
            }
            isEdit = true;
        }
    }   
    if(i < word1.length){
        return !isEdit
    }
    if(j < word2.length){
        return !isEdit
    }
    return i === word1.length && j === word2.length && isEdit;
}

908. 给定一个整数数组 A,对于每个整数 A[i],我们可以选择任意 x 满足 -K <= x <= K,并将 x 加到 A[i] 中。

在此过程之后,我们得到一些数组 B。 返回 B 的最大值和 B 的最小值之间可能存在的最小差值。

var smallestRangeI = function(A, K) {
    let max = A[0],min= A[0] ;
    for(let i = 1; i < A.length; i++){
        if(A[i] > max){
            max = A[i];
        }
        if(A[i] < min){
            min = A[i];
        }
    }
    //重叠值则为0
    return Math.max(max - min - 2 * K , 0);
};
console.log(smallestRangeI([1,3,6], 3)); //3

910. 给定一个整数数组 A,对于每个整数 A[i],我们可以选择 x = -K 或是 x = K,并将 x 加到 A[i] 中。

在此过程之后,我们得到一些数组 B。 返回 B 的最大值和 B 的最小值之间可能存在的最小差值。

var smallestRangeII = function(A, K) {
    A.sort(function(x, y){
        return x - y;
    });
    let res = A[A.length -1] - A[0];
    for(let i = 0; i < A.length - 1; i++){
        //贪心算法,每个值都有+k -k 除max,min外 比最大值小,则加运算不会改变结果, 比最小值大,-k不会对结果造成影响
        //同时-k 相当于A[i]上+2 *k或者不变,进行问题转换
        let max =  Math.max(A[A.length - 1], A[i] + 2 * K) ;
        let min =  Math.min(A[0] + 2 * K, A[i + 1]);
        res = Math.min(res, max - min);
    }
    return res;
};
console.log(smallestRangeII([1,3,6],3));
//法2 贪心算法 从某个pos总是之前+k 之后-k
function smallRange(A, k){
    A.sort(function(a , b){
        return a - b;
    });
    let len = A.length;
    let res = A[len  - 1] - A[0];
    let temp = res, resArr = [];
    for(let i = 0; i < len - 1; i++){
        let max = Math.max(A[len - 1] - k, A[i] + k);
        let min = Math.min(A[0] + k , A[i + 1] - k);
        res = Math.min(res, max - min);
    }
    for(let j = 0; j < len - 1; j++){
        let max = Math.max(A[len - 1] - k, A[j] + k);
        let min = Math.min(A[0] + k , A[j + 1] - k);
        temp = Math.min(temp, max - min);
        if(temp === res){
            let str = [];
            for(let m = 0; m <= j; m++){
                str[m] = A[m] + k;
            }
            for(let m = j + 1; m < len; m++){
                str[m] = A[m] - k;
            }
            resArr.push(str);
        }
    }
    return resArr;
}
console.log(smallRange([1,3,6],3)); // 3

剑指offer66题

1. 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数?(二维数组中的查找)

  1. 暴力解题法(O(n^2))
function Find(target, array)
{
    //利用js内库实现暴力解题
    for(let value of array){
        if(value.includes(target)){
            return true;
        }
    }
    return false;
}
  1. 二分查找优化二重遍历(O(nlogn)
function Find(target, array)
{
    //利用二分查找
    let col  = array[0].length - 1;
    let row = array.length - 1;
    if(row === 0 || col === 0){
        return false;
    }
    if(target < array[0][0] || target > array[row][col]){
        return false;
    }
    for(let value of array){
        let left = 0; 
        let right = col;
        while(left <= right){
            let mid = parseInt((left + right) / 2);
            if(target > value[mid]){
                left = ++mid;
            }else if(target < value[mid]){
                right = --mid;
            }else{
                return true;
            }
        }
    }
    return false;
}
  1. 通过左下角或者右上角边界点优化(O(n))
function Find(target, array)
{
    //利用左上角或者右上角边界
    let col  = array[0].length - 1;
    let row = array.length - 1;
    if(row === 0 || col === 0){
        return false;
    }
    if(target < array[0][0] || target > array[row][col]){
        return false;
    }
    let i = 0, j = col;
    while(i <= row && j >= 0){
        if(target > array[i][j]){
            i++;
        }else if(target < array[i][j]){
            j--;
        }else{
            return true;
        }
    }
    return false;
}

2.请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。(替换空格)

  1. 利用js内的正则表达式 => 有多少空格就需要匹配多少次
function replaceSpace(str)
{
    let reg =/\s/g;
    return str.replace(reg,'%20'); 
}
  1. 通过后移插入法实现字符串替代
function replaceSpace(str)
{
    //在源字符串中替换
    if(str.length === 0 || !str){
        return str;
    }
    var spaceNum = 0;
    for(var value of str){
        if(value === ' '){
            spaceNum++;
        }
    }
    if(spaceNum === 0){
        return str;
    }
    //新数组长度
    var newStrLen = str.length + 2*spaceNum - 1;
    //str长度
    var oldStrLen = str.length - 1;
     var newStr = Array(newStrLen);
    while(oldStrLen >= 0 ){
        if(str.charAt(oldStrLen) !== ' '){
            newStr[newStrLen--] = str.charAt(oldStrLen--);
        }else{
            oldStrLen--;
            newStr[newStrLen--] ='0';            
            newStr[newStrLen--] ='2';
            newStr[newStrLen--] ='%';
        }
    }   //申请空间

    return newStr.join('');
}
3. 依次替换
function replaceStrII(str){
    let strArr = str.split("");
    let res = "";
    for(let val of strArr){
        if(val === ' '){
            res += '%20'; 
        }else{
            res += val;
        }
    }
    return res;
}
  1. 输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
/*function ListNode(x){
    this.val = x;
    this.next = null;
}*/
//1.递归 后进先出,通过全局变量保存值
function printListFromTailToHead(head)
{
    // write code here
    let res = []
    if(!head) return res;
    res = printListFromTailToHead(head.next);
    res.push(head.data)
    return res;
}
//2. 数组unshift方法
/*function printListFromTailToHead(head)
{
    // write code here
    if(!head) return [];
    var node = null,res = [],temp;
    while(head){
        res.unshift(head.val);
        head = head.next;
    }
    return res;
}*/
//3.头插法遍历
/*function printListFromTailToHead(head)
{
    // write code here
    if(!head) return [];    
    let temp,node = null;
    let arr = [];
    while(head){
        temp = head.next;
        head.next = node;
        node = head;
        head = temp;
    }
    while(node){
        arr.push(node.val);
        node = node.next;
    }
    return arr;
}*/

4. 例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

function reConstructBinaryTree(pre, vin)
{
    // write code here
    let tree, index, vinLeft,vinRight;
    if(!pre.length) return null;
    index = vin.indexOf(pre[0]);
    vinLeft = vin.slice(0, index);
    vinRight = vin.slice(index + 1);
    tree = new TreeNode(pre[0]);
    tree.left = createTree(pre.slice(1,vinLeft.length + 1), vinLeft);    
    tree.right = createTree(pre.slice(pre.length - vinRight.length),vinRight);
    return tree;
}
  1. 用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
let stack1 = [], stack2 = [];
function push(node)
{
    // write code here
    stack1.push(node);
}
function pop()
{
    if(!stack2.length){
        while(stack1.length){
            stack2.push(stack1.pop());
        }
    }
    return stack2.pop();
}
  1. 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
//二分法 利用旋转后的值的两部分进行比较 总有第一个部分的第一个值大于旋转后的第二部分
function minNumberInRotateArray(rotateArray)
{
    // write code here
    if(!rotateArray.length) return 0;
    let l = 0, r = rotateArray.length;
    while(l < r){ //l < r是用的length属性 比如 3 只会进入一次 4则会进入2次
        if(rotateArray[l] < rotateArray[r]){ //没有旋转
            return rotateArray[l];
        }
        let mid = parseInt((l + r) / 2);
        if(rotateArray[mid] > rotateArray[mid + 1]){ //比后一个数的大小
            return rotateArray[mid + 1];
        }
        if(rotateArray[mid - 1] > rotateArray[mid]){ //比前一个数的大小
            return rotateArray[mid];
        }
        if(rotateArray[mid] > rotateArray[0]){
            l = mid + 1;
        }else{
            r = mid - 1;
        }
    }
}
//O(n) 前一个数比后一个数大
function minNumberInRotateArray(rotateArray)
{
    // write code here
    if(!rotateArray.length) return 0;
    let temp = rotateArray[0];
    for(let i = 1; i < rotateArray.length; i++){
        if(temp <= rotateArray[i]){
            temp = rotateArray[i];
        }else{
            return rotateArray[i];
        }
    }
}
  1. 斐波那契数列
//递归
function Fibonacci(n)
{
    if(n === 0) return 0;
    if(n <= 2) return 1;
    return Fibonacci(n - 1) + Fibonacci(n - 2);
}
//迭代
function Fibonacci(n)
{

    if(n === 0) return 0;
    if(n <= 2) return 1;
    let first = second = 1,sum = 0, i = 2;
    while(i < n){
        sum = first + second;
        first = second;
        second = sum;
        i++;
    }
    return sum;
}
  1. 一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法
//递归 n- 1 n - 2次等价于前一梯的长度加前2梯的长度
function jumpFloor(number)
{
    // write code here
    if(number === 1) return 1;
    if(number === 2) return 2;
    return jumpFloor(number - 1) + jumpFloor(number - 2);
}
//迭代
function jumpFloor(number)
{
    // write code here
    if(number === 1) return 1;
    if(number === 2) return 2;
    let start = 2, first = 1, second = 2,sum = 0;
    while(start < number){
        sum = first + second;
        first = second;
        second = sum;
        start++;
    }
    return sum;
}
//利用全局变量的形式,递归至阶梯数为0
function jumpFloorI(number)
{
    let count = [0];
    jump(number, count);
    return count[0];
}
function jump(number,count){
    if(number === 0) {return count[0]++}
    if(number >= 1) jump(number - 1,count);    
    if(number >= 2) jump(number - 2,count);
}
  1. 变态跳台阶
//易知 f(n)=f(n-1)+f(n-2)+……f(1) f(n-1)=f(n-2)+……f(1) 两式相减得f(n)=2f(n-1)
function jumpFloorII(number)
{
    // write code here
    //return Math.pow(2, number - 1);
    let n = 1;
    for(let i = 1; i < number;i++){
        n = 2 * n;
    }
    return n;
}
//利用全局变量 至阶梯数为0为止
function jumpFloorII(number)
{
    // write code here
    let count = [0];
    jump(number, count);
    return count[0];
}
function jump(number,count){
    if(number === 0) return count[0]++;
    for(let i =1 ; i <= number; i++){
        jump(number - i, count);
    }
}
  1. 矩形覆盖
function rectCover(number)
{
    //找规律 对数据进行找规律 图形找规律实现递归不等式 
    if(number <= 2) return number;
    return rectCover(number - 1) + rectCover(number - 2);
}
  1. 幂运算
function Power(base, exponent)
{
     //幂运算 考虑底数和指数的边界条件 以及为负数的情况
    if(base === 0){
        return exponent < 0 ? false : 0;
    }
    if(!exponent){
        return 1;
    }
    let  exp = Math.abs(exponent);
    let res = 1;
    while(exp){
        res = res * base;
        exp--;
    }
    return exponent > 0 ? res : 1 / res;
}
  1. 所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
//偶数向最后插入,数组向前移动
function reOrderArray(array)
{
    //插入排序 + 快排的思想
    let  i = 0;
    while(i < array.length){
        if(array[i] % 2 === 0){
            let j = k = i;
            while(array[j] % 2 === 0){
                if(j === array.length - 1) return array;
                j++;
            }
            let temp = array[j];
            while(j > k){
                array[j] = array[j - 1];
                j--;
            }
            array[j] = temp;
        }
        i++;
    }
    return array;
}
//偶数向最后插入,数组向前移动
function reOrderArray(array)
{
    //将偶数放入数组末尾
    let move = i = 0;
    while(move + i !== array.length){
        if(array[i] % 2 === 0) {
           let k = i, j = array.length  - 1;
           let temp = array[i];
            while(k < j){
                array[k] = array[k + 1];
                k++;
            }
            array[j] = temp;
            move++;
        }else{
            i++;
        }
    }
    return array;
}
console.log(reOrderArray([1,2,3,4,5,6,7]));
  1. 倒数第k个节点
//双向指针移动 使之间隔为k至第一个指针为null时停止移动
function FindKthToTail(head, k)
{
   let p = head, q = head, count = 0;
    while(p){
        p = p.next;
        count++;
        if(count > k) q = q.next;
    }
    if(k > count) return null;
    return q;
}

//一次遍历链表找到倒数第K个节点
/*function FindKthToTail(head, k)
{
   let res = [];
    while(head){
        res.push(head);
        head = head.next;
    }
    return k > res.length ? null : res[res.length - k];
}*/
  1. 合并增序链表
//归并+头结点+尾插法
function Merge(pHead1, pHead2)
{
    // write code here
    let p = pHead1, q = pHead2, head = res = new ListNode(0);
    while(p && q){
        if(p.val > q.val){
            res.next = q;
            res = q;
            q = q.next;
        }else{
            res.next = p;
            res = p;
            p = p.next;
        }
    }
    if(!p){
        res.next = q;
    }
    if(!q){
       res.next = p;
    }
    return head.next;
}
  1. 树的子结构
function HasSubtree(pRoot1, pRoot2)
{
    // write code here
    if(!pRoot1 || !pRoot2) return false;
    //遍历大树看是否有相同的子结构
    return jugeTree(pRoot1, pRoot2) || HasSubtree(pRoot1.left, pRoot2) || HasSubtree(pRoot1.right, pRoot2); 
}
//判断两棵树是否相同
function jugeTree(tree1, tree2){
    if(!tree2){
        return true;
    }
    if(!tree1){
        return false;
    }
    if(tree1.val !== tree2.val){
        return false;
    }
    return jugeTree(tree1.left, tree2.left) && jugeTree(tree1.right,tree2.right);
}
  1. 交换左右子树
//交换左右子树
function Mirror(root)
{
    if(root){
        if(!root.left && !root.right) return;
        let temp = root.left;
        root.left = root.right;
        root.right = temp;
        Mirror(root.left);
        Mirror(root.right);
    }
    return root;
}

16.顺时针打印举证

//循环边界条件  检查循环退出时值的正确情况则可知晓
function printMatrix(matrix)
{
    //利用4个标志位判断是否打印完成本题技巧性较强
    let up = 0; down = matrix.length - 1, left = 0, right = matrix[0].length - 1;
    let res = [],row , col;
    while(true){
        //从左至右打印
        for(col = left; col <= right; col++){
            res.push(matrix[up][col]);
        }
        up++;
        //如果up >= down 则为打印up = down的情况 故为up > down
        if(up > down){
            break;
        }
        //从上到下打印
        for(row = up; row <= down; row++){
            res.push(matrix[row][right])
        }
        right--;
        if(left > right){
            break;
        }
        //从右至左打印
        for(col = right; col >= left; col--){
            res.push(matrix[down][col]);
        }
        down--;
        //上下重叠
        if(up > down){
            break;
        }
        //从下至上打印
        for(row = down; row >= up; row--){
            res.push(matrix[row][left]);
        }
        left++;
        //左右重叠
        if(left > right){
            break;
        }
    }
    return res;
}
  1. 定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数
//法一辅助栈压入最小值 弹栈时当辅助栈最小值=== 弹栈值 最小值出栈
function push(node) {
    stack1.push(node);
    if (stack2.length === 0) {
        stack2.push(node);
    } else if (node < stack2[stack2.length - 1]) {
        stack2.push(node);
    }
    return stack1.length;
}
function pop() {
    let node = stack1.pop();
    if (node === stack2[stack2.length - 1]) {
        stack2.pop();
    }
    return node;
}
function top() {
    return stack1[stack1.length - 1];
}
function min() {
    return stack2[stack2.length - 1];
}
var stack1 = [];
var stack2 = [];
//法二 辅助栈保存每个元素的最小值 压栈值 < 辅助栈最小值 辅助栈压入压栈值 否则压入辅助栈前一个最小值 弹栈时则可同时弹栈
function push(node) {
    stack1.push(node);
    if (stack2.length === 0) {
        stack2.push(node);
    } else if (node < stack2[stack2.length - 1]) {
        stack2.push(node);
    } else {
        stack2.push(stack2[stack2.length - 1]);
    }
    return stack1.length;
}
function pop() {
    stack2.pop();
    return stack1.pop();
}
function top() {
    return stack1[stack1.length - 1];
}
function min() {
    return stack2[stack2.length - 1];
}
var stack1 = [];
var stack2 = [];

  1. 出入栈顺序
//类似括号匹配
function IsPopOrder(pushV, popV)
{
    // write code here
    let res = [], j = 0;
    for(let i = 0; i < pushV.length; i++){
        res.push(pushV[i]);
        while(res.length !== 0 && popV[j] === res[res.length - 1]){
            res.pop();
            j++;
        }
    }
    return !res.length;
}
  1. 层次遍历
//判断树是否为空 注意写二叉树的时候
function PrintFromTopToBottom(root) {
    // write code here
    if (!root) return [];
    let queue = [root],
        res = [];
    while (queue.length) {
        let node = queue.shift();
        res.push(node.val);
        if (node.left) queue.push(node.left);
        if (node.right) queue.push(node.right);
    }
    return res;
}
  1. 后序bst
function VerifySquenceOfBST(sequence)
{
    if(!sequence.length) return false;
    return isBST(sequence, 0, sequence.length - 1);
}
//判断是否为二叉搜索树 左边值 < 根值 < 右边值
function isBST(arr,start,end){
    if(start >= end){ //递归至只有一个节点则返回
        return true;
    }
    let rootVal = arr[end], split = start;
    while(split < end && arr[split] < rootVal){ // 左边的值比根小
        split++;
    }
    //右边值比根大
    let i = split;
    while(i < end){
        if(arr[i] < rootVal){
            return false;
        }
        i++;
    }
    return isBST(arr, start, split - 1) && isBST(arr, split, end - 1);
}
function FindPath(root, expectNumber)
{
    if(!root) return [];
    let res = [];
    treePath(root, expectNumber, res, []);
    return res.sort(function(a,b){return b.length - a.length});
}
//二叉树遍历二叉树退出节点 为空时退出,满足相应条件执行相应操作即可
function treePath(root, target, res, temp){
    if(root){
        target -= root.val;
        temp.push(root.val);
        if(target === 0 && !root.left && !root.right){
               res.push(temp.slice());
         }
         treePath(root.left, target, res, temp);
         treePath(root.right, target, res, temp);
         temp.pop();
    }
   
}
  1. 复制链表
//通过映射关系找到对应的random next值
function Clone(pHead)
{
    if(!pHead){
        return null;
    }
    //头节点也要设置新旧节点的对应位置
    let p = pHead, res = q = new RandomListNode(pHead.label);
    let nodeMap = new Map();
    nodeMap.set(pHead, q);
    while(p){
        //next不存在则重新申请节点
        if(p.next && nodeMap.has(p.next)){
            q.next = nodeMap.get(p.next);
        }else if(p.next){
            let temp = new RandomListNode(p.next.label);
            nodeMap.set(p.next, temp);
            q.next = temp;
        }
        //random不在则也重新申请节点
        if(p.random && nodeMap.has(p.ramdom)){
            q.random = nodeMap.get(p.ramdom);
        }else if(p.random){
            let temp = new RandomListNode(p.random.label);
            nodeMap.set(p.random, temp);
            q.random = temp;
        }
        //遍历每一个节点 给节点的next 和 random赋值
        p = p.next;
        q = q.next;
    }
    return res;
}
//通过映射关系找到对应的random next值
function Clone(pHead)
{
    if(!pHead){
        return null;
    }
    let p = node = pHead, q;
    while(p){
        q = new RandomListNode(p.label);
        //先用后改插入法
        let temp = p.next;
        q.next = temp;
        p.next = q;
        p = temp;
    }
    //修改random节点
    while(node.next.next){
        if(node.random){
            //复制节点的下一个节点random 为random节点的下一个
            node.next.random = node.random.next;
        }
        node = node.next.next;
    }
    //获取偶数节点链成链表
    let res = pHead.next;
    while(pHead.next.next){
        let temp = pHead.next.next;//保存奇数个节点即源节点
        pHead.next.next = pHead.next.next.next;//修改指针
        pHead = temp;
    }
    return res;
}
//暴力解法 每次都要去找random 此时应试图找到一种映射关系直接获取ramdom值
function Clone(pHead)
{
    if(!pHead){
        return null;
    }
 
    let resList = res = new RandomListNode(0);
    let head = pHead;
    while(head){
        let node = new RandomListNode(head.label);
        res.next = node;
        res = node;
        head = head.next;
    }
    let list = resList.next;
    while(pHead){
        let temp = pHead;
        let listTemp = list;
        while(pHead.random){
            if(pHead.random === temp){
                break;
            }
            listTemp = listTemp.next;
            temp = temp.next;
        }
        list.random = listTemp;
        list = list.next;
        pHead = pHead.next;
    }
    return resList.next;
}
  1. 搜索二叉树变成双向链表
//递归找到当前节点和当前节点的前一个节点修改指针
function Convert(pRootOfTree)
{
    if(!pRootOfTree) return null;
    let pre = [null];
    convertHelper(pRootOfTree, pre);
    //回到头节点
    while(pRootOfTree.left){
        pRootOfTree = pRootOfTree.left;
    }
    return pRootOfTree;
}
//pre为地址任何时候保存同一值 否则pre值将不会是前一个值
function convertHelper(root, pre){
    if(root){
        convertHelper(root.left, pre);
        //进行当前节点和前一个节点的转换 
        root.left = pre[0];
        if(pre[0]) pre[0].right = root;
        pre[0] = root;
        convertHelper(root.right, pre);
    }
}
//迭代保存当前节点的前一个节点 中序情况下
function Convert(pRootOfTree)
{
    if(!pRootOfTree) return null;
    let pre = null, stack = [], p = pRootOfTree;
    //栈不空 节点不空都可以进入
    while(p || stack.length){
        //一直进入左子树
        while(p){
            stack.push(p);
            p = p.left;
        }
        let curr = stack.pop();
        //进行交换操作
        curr.left = pre;
        if(pre) pre.right = curr;
        pre = curr;
        //访问右子树
        p = curr.right;
    }
    while(pRootOfTree.left){
        pRootOfTree = pRootOfTree.left;
    }
    return pRootOfTree;
}
  1. 字符串全排列
function Permutation(str)
{
    //全排列:第一个数后之后的数相交换 依次递归 重复值的全排列:交换时为相同值则不交换
    if(str === '') return [];
    let res = [];
    permutationHelper(str.split(""),res,0);    
    res.sort();
    return res;
}
function permutationHelper(arr, res, i){
    if(i === arr.length - 1){
        if(!res.includes(arr.join(""))){ //去除重复交换的字符串
            res.push(arr.join(""));
        }
    }else{
        for(let j = i ; j < arr.length; j++){
                swap(arr, i , j);
                permutationHelper(arr, res, i + 1);
                swap(arr, i, j); //恢复原来序列 仅和第一个数交换
        }
    }
}
function swap(arr, i , j ){
    let temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}
  1. 次数超过一半数组长度的数字(限定存在仅有一个)
//map
function MoreThanHalfNum_Solution(numbers)
{
    let map = new Map();
    let res = 0;
    for(let val of numbers){
        if(map.has(val)){
            map.set(val, map.get(val) + 1);
        }else{
             map.set(val,1);
        }
    }
    //回调即是闭包 注意每个api的作用,回调函数的返回值并非当前函数的返回值
    map.forEach(function(count, key){
        if(count > (numbers.length / 2)){
            res = key;
        }
    })
    return res;
}
console.log(MoreThanHalfNum_Solution([1,2,3,2,2,2,5,4,2]));
//简便方法: val time 寻找最后的val是否存在大于一半的次数 
//如果存在该数 该数出现的次数比其他所有出现的次数和还要多
//简便方法: val time 寻找最后的val是否存在大于一半的次数 
//如果存在该数 该数出现的次数比其他所有出现的次数和还要多
function MoreThanHalfNum_Solution(numbers)
{
    let temp , time = 1;
    for(let val of numbers){
        if(val === temp){
            time++;    
        }else{
            time--;
        }
        if(time === 0){
            temp = val;
            time = 1;
        }
    }
    let count = 0;
    for(let num of numbers){
        if(num === temp){
            count++;
        }
    }
    return count > (numbers.length / 2) ? temp : 0;
}
//计数排序
function MoreThanHalfNum_Solution(numbers)
{
    let count = parseInt(numbers.length / 2);
    let min = max = numbers[0], arr = [];
    for(let val of numbers){
        if(min > val){
            min = val;
        }
        if(max < val){
            max = val;
        }
    }
    for(let i = 0; i <= max - min; i++){
        arr[i] = 0;
    }
    for(let j = 0; j < numbers.length; j++){
        arr[numbers[j] - min]++;
    }
    for(let m = 0; m < numbers.length; m++){
        if(arr[numbers[m] - min] > count){
           return numbers[m];
        }
    }
    return 0;
}
  1. 最小的K个数
//快速排序去掉递归条件
function GetLeastNumbers_Solution(input, k)
{
    //边界条件
    if(!k || !input.length || k > input.length) return [];
    let start = 0, end = input.length - 1;
    let mid = quickSort(input, start, end);
    while(mid !== (k - 1)){ //不存在分界线 左边的比mid小  右边的比mid大 最小可包含mid
        if(mid > (k - 1)){
            end = mid - 1;
            mid = quickSort(input, start, end);
        }else{
            start = mid + 1;
            mid = quickSort(input, start, end);
        }
    }
    return input.slice(0, k).sort(function(a, b){return a - b});
}
function quickSort(arr, l , r){ //分治并未排序
        let i = l, j = r, temp = arr[i];
        while(i < j){
            while(i < j && arr[j] >  temp) j--;
            if(i < j) arr[i++] = arr[j];
            while(i < j && arr[i] < temp) i++;
            if(i < j) arr[j--] = arr[i];
        }
        arr[i] = temp;
        return i;
}
//冒泡排序 从后往前
function GetLeastNumbers_Solution(input, k)
{
    //边界条件
    if(!k && !input.length && k > input.length) return [];
    for(let i = 0; i < input.length - 1; i++){
        for(let j = input.length - 1; j > i; j--){
            if(input[j] < input[j - 1]){
                let temp = input[j];
                input[j] = input[j - 1];
                input[j - 1] = temp;
            }
        }
        k--;
        if(!k){
            return input.slice(0, i + 1);
        }
    }
    return input;
}
  1. 求连续最大和
function FindGreatestSumOfSubArray(array)
{
    let max = array[0], sum = 0; 
    //数学问题 求最大值:为正则继续累加 为负重新计数 为负一定不可能是连续最大值
    for(let i = 0; i < array.length; i++){
        if(sum >= 0){
            sum += array[i];
        }else{
            sum = array[i];
        }
        if(sum > max){
            max = sum;
        }
    }
    return max;
}
function FindGreatestSumOfSubArray(array)
{
    let max = array[0], sum = 0, dp = [array[0]]; 
    //动态规划 最大值:当前元素 + 之前连续序列最大值
    for(let i = 1; i < array.length; i++){
        sum = dp[i - 1] + array[i];
        if(sum > array[i]){
            dp[i] = sum; //保存当前最大值
        }else{
            dp[i] = array[i]; //保存当前最大值
        }
        if(dp[i] > max){
            max = dp[i];
        }
    }
    return max;
}

function FindGreatestSumOfSubArray(array)
{
    let max = array[0], sum;
    //暴力解决法 求出所有连续串的和
    for(let i = 0; i < array.length; i++){
        sum = 0;
        for(let j = i; j < array.length; j++){
            sum += array[j];
            if(sum > max){
                max = sum;
            }
        }
    }
    return max;
}
  1. 计算1的个数
//计算各位 10位 100位 ...1的个数
function NumberOf1Between1AndN_Solution(n)
{
    let res = 0, a, b, time;
    for(let i = 1; i <= n; i *= 10){
        a = parseInt(n / i); //位
        b = n % i; //余数
        //temp = a % 10 === 1 ? b + 1 : 0; //判断为数是否为1 需要在加b + 1
        time = parseInt((a + 8) / 10) * i + (a % 10 === 1) * (b + 1); //1的次数 等于百位前的次数*百位
        res += time;
    }
    return res;
}
  1. 求组合最小值
function PrintMinNumber(numbers)
{
    //冒泡排序 ASCII码最大的放最后 a + b > b + a  把大数往右放,小数往前方则是最小值
    if(!numbers.length) return "";
    for(let i = 0; i < numbers.length - 1; i++){
        for(let j = 0; j < numbers.length - 1 - i; j++){
            let a = '' + numbers[j] + numbers[j + 1];
            let b = '' + numbers[j + 1] + numbers[j];
            if(Number(a) > Number(b)){
                swap(numbers, j , j + 1);
            }
        }
    }
    return Number(numbers.join(''));
}
function swap(arr, i, j){
    let temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

function PrintMinNumber(numbers)
{
    //暴力解法 排列后的最小值
    if(!numbers.length) return "";
    let res = [];
    com(numbers, 0, res);
    return Math.min(...res);
}
//排列得到所有可能的组合
function com(arr, i, res){
    if(i === arr.length - 1){
        if(!res.includes(Number(arr.join("")))){
            res.push(Number(arr.join("")));
        }
    }else{
            for(let j = i; j < arr.length ; j++){
                swap(arr, i, j);
                com(arr, i + 1, res);
                swap(arr,i, j);
            }                 
    }
}
//交换
function swap(arr, i, j){
    let temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}
  1. 丑数 倍数取最小
//质因数为能被该数整除的质数 1不是质数也不是合数 质数的质因数则是本身 合数则需进行分解即分解因数
function GetUglyNumber_Solution(index)
{
    if(index < 7) return index;
    let res = [1], p2 = 0, p3 = 0, p5 = 0; //三个队列 用下标标识 *2 *3 *5 的队列取最小值压入丑数数组
    while(res.length < index){
        let min = Math.min(res[p2] * 2, res[p3] * 3, res[p5] * 5);
        if(min === res[p2] * 2) p2++;
        if(min === res[p3] * 3) p3++;
        if(min === res[p5] * 5) p5++;
        res.push(min);
    }
    return res[index - 1];
}
  1. 逆序对
//归并排序 两两有序 从尾部开始排序交换 则arr[i] > arr[j]; 则i > arr[j]前的数组  count += j - mid; 
function InversePairs(data)
{
   let count = inversePairsHelper(data, 0, data.length - 1);
    return count;
}
//
function inversePairsHelper(arr, l, r){ //归并排序 分治法
    if(l < r){
        let mid = parseInt((l + r) / 2);
        let leftCount = inversePairsHelper(arr, l, mid); //计算每个数后面的逆序数 
        let rightCount = inversePairsHelper(arr, mid + 1, r); 
        let count = 0, temp = [], i = mid, j = r, len = r - l;
        //排序 申请空间方式
        while(i >= l && j > mid){
            if(arr[i] > arr[j]){
                count += j - mid;
                if(count >= 1000000007){
                    count %= 1000000007;
                }
                temp[len--] = arr[i--];
            }else{
                temp[len--] = arr[j--];
            }
        }
        while(i >= l){ //左边太长
            temp[len--] = arr[i--];
        }
        while(j > mid){ //右边太长
            temp[len--] = arr[j--];
        }
        len = 0;
        i = i + 1;
        while(i <= r){
            arr[i++] = temp[len++];
        }
        return (leftCount + rightCount + count) % 1000000007;
    }else{
        return 0;
    }
}
  1. 求一个数有序数组中出现的次数 通过插入位置 / 或者判断最开始出现次数 结束出现次数即可
//二分法
function GetNumberOfK(data, k)
{
    //找到 k + 0.5 k - 0.5 插入的位置进行相减即可
    return binSearch(data, k + 0.5) - binSearch(data, k - 0.5);
}
function binSearch(arr, num){
    let l = 0, r = arr.length - 1; //二分法 有等于条件
    while(l <= r){
         let mid = parseInt( ( r + l) /2 );
        if(arr[mid] > num){
            r = mid - 1;
        }else{
            l = mid + 1;
        }
    }
    return l;
}
//二分法 二分法l <= r 最后一个数需要查找, 而分治递归结束只需l < r 即递归到只有一个数时则返回
function GetNumberOfK(data, k)
{
    //找到 k + 0.5 k - 0.5 插入的位置进行相减即可
    return  binSearch(data, k) -  binSearchStack(data, k, 0, data.length - 1) + 1;
}
//开始位置 
function binSearchStack(arr, num, l , r){
    if(l <= r){
        let mid = parseInt(( l + r) / 2);
        if(arr[mid] > num){
           return binSearchStack(arr, num, l, mid - 1);
        }else if(arr[mid] < num){
             return binSearchStack(arr, num, mid + 1, r);
        }else if(mid - 1 >= 0 && arr[mid - 1] === num){
             return binSearchStack(arr, num, l, mid - 1);
        }else{
            return mid;
        }
    }else{
        return 0; //不存在最后返回 0 不存在时返回0
    }
}
//结束位置
function binSearch(arr, num){
    let l = 0, r = arr.length - 1; //二分法 有等于条件
    while(l <= r){
        let mid = parseInt( ( r + l) /2 );
        if(arr[mid] > num){
            r = mid - 1;
        }else if(arr[mid] < num){
            l = mid + 1;
        }else if(mid + 1 < arr.length && arr[mid + 1] === num){
            l = mid + 1;
        }else{
            return mid;
        }
    }
    return -1; //没有最后返回-1
}
  1. 求二叉树的高度
//返回左右两颗树高最大值
function TreeDepth(pRoot)
{
    if(pRoot){
        return Math.max(TreeDepth(pRoot.left), TreeDepth(pRoot.right)) + 1;
    }else{
        return 0 ;
    }  
}
//返回左右两颗树高最大值
function TreeDepth(pRoot)
{
    if(pRoot){
        let leftHeight = TreeDepth(pRoot.left);
        let rightHeight = TreeDepth(pRoot.right);
        return Math.max(leftHeight, rightHeight) + 1;
    }else{
        return 0 ;
    }  
}
  1. 判断平衡二叉树
//使用一个变量判断是否为平衡二叉树 只要不平衡则会一直返回-
function IsBalanced_Solution(pRoot)
{
    return isBalanceTree(pRoot) === -1 ? false : true;
}
function isBalanceTree(root){
    if(root){
        let leftHeight = isBalanceTree(root.left);
        if(leftHeight === -1){
            return -1;
        }
        let rightHeight = isBalanceTree(root.right);
        if(rightHeight === -1){
            return -1;
        }
        if(Math.abs(leftHeight - rightHeight) > 1){
            return -1;
        }
        return Math.max(leftHeight , rightHeight) + 1;
    }else{
        return 0;
    }
}

//使用一个变量判断是否为平衡二叉树 只要不平衡则会一直返回-
function IsBalanced_Solution(pRoot)
{
    isBalance = true;
    isBalanceTree(pRoot);
    return isBalance;
}
var isBalance = true;
function isBalanceTree(root){
    if(root){
        let leftHeight = isBalanceTree(root.left);
        let rightHeight = isBalanceTree(root.right);
        if(Math.abs(leftHeight - rightHeight) > 1){
            isBalance = false;
        }
        return Math.max(leftHeight, rightHeight) + 1;
    }else{
        return 0;
    }
}
  1. 求只出现一次的两个数
//异或法 将其分为两组数    每组里只有一个出现一次的数 其余出现2次
function FindNumsAppearOnce(array)
{
    //相异为1 相同为0 0异或任何数为本身
    if(array.length < 2) return null;
    let temp = apperOnce(array); // 得到两个不同的数异或值
    let bitIndex = findBitIndex(temp); // 找到第一个位为1的位下标进行分组 位为1代表两个数不同
    let a = 0, b = 0;
    for(let val of array){
        if(isBitOne(val, bitIndex)){
            a ^= val;
        }else{
            b ^= val;
        }
    }
    return [a , b];
}
function findBitIndex(num){
    let index = 1;
    //计算位为1的下标
    while( (num & 1) === 0 && index < 32){
        num = num >> 1;
        index++;
    }
    return index;
}
function isBitOne(num, index){ // 判断某个数的第几位是否为1 
    return (num >> (index - 1)) & 1 == 1; //>>带符号右移
}
function apperOnce(arr){
    return arr.reduce((pre, curr) => pre ^ curr);
}
  1. 求序列和
function FindContinuousSequence(sum)
{
    //双指针 确定范围 
    let start = 1, end = 2, res = [], total;
    while(start < end){
        //计算范围内的和
        total = (start + end) * (end - start + 1) / 2;
        if(total === sum){
            let temp = [], m = start, n = end;
            while(m <= n){
                temp.push(m);
                m++;
            }
            res.push(temp);
            start += 1;
        }else if(total < sum){
            end += 1;
        }else{
            start += 1;
        }
    }
    return res;
}

function FindContinuousSequence(sum)
{
   //数学方法 等差数列求和根据序列长度求解
    // sum = (n + 1) * n / 2 ;n < 根号2s 根据序列长度进行求解
    let res = [];
    for(let n = parseInt(Math.sqrt(2 * sum)) ; n >= 2; n--){
        if( ((n & 1) === 1 && sum % n === 0) || (sum % n) * 2 === n){ //奇数个序列 偶数个序列
            let temp = [];
            for(let i = 0, k = parseInt(sum / n - (n - 1) / 2); i < n; k++, i++ ){
                temp.push(k);
            }
            res.push(temp);
        }
    }
    return res;
}
  1. 求增序数组和的最小值
function FindNumbersWithSum(arr, sum)
{
    //首尾计算
    let l = 0, r = arr.length - 1 ;
    while(l < r){ //1个数时不存在两个数的和
        if(arr[l] + arr[r] === sum){
            return [arr[l], arr[r]]; //相差越大 值越小
        }else if(arr[l] + arr[r] < sum){
            l++;
        }else{
            r--;
        }
    }
    return []; //没有返回[]
}
  1. 循环移位
function LeftRotateString(str, n)
{
    if(n <= 0) return str;
    let count = n % str.length;
    let arr = str.split('');
    swap(arr, 0, count - 1);
    swap(arr, count, arr.length - 1);
    swap(arr, 0, arr.length - 1);
    return arr.join('');
}
//循环移位 交换3次即可
function swap(arr, l, r){
    while(l < r){
        let temp = arr[l];
        arr[l] = arr[r];
        arr[r] = temp;
        l++;
        r--;
    }
}
function LeftRotateString(str, n)
{
   if(!str) return '';
   let count = n % str.length;
   if(count ===  0) return str;
    str += str;
    return str.substr(n, len);
}

console.log(LeftRotateString('abcXYZdef',3));
  1. 翻转句子
function ReverseSentence(str)
{
     //古老解决 翻转每个单词 根据空格前后判断单词结尾
    if(!str.trim()) return str; //去掉空格
    let arr = str.split(''), i = 0;
    swapStr(arr, 0, arr.length - 1); //翻转整个字符串
    while(i < arr.length){ //翻转每个单词 单词定义从开始到空格结束
        if(arr[i] !== ' '){
            let start = i;
            while(arr[i] !== ' ' && i < arr.length){i++};
            swapStr(arr, start, i - 1);
        }else{
            i++;
        }
    }
    return arr.join('');
}
//字符串不可变 基本类型 非引用类型
function swapStr(arr, start, end){
    while(start < end){
        let temp = arr[start];
        arr[start] = arr[end];
        arr[end] = temp;
        start++;
        end--;
    }
}
  1. 判断是否是顺子
function IsContinuous(numbers)
{
    //数学思维 满足顺子 max - min < 5才成立 即个数 - 1 = max - min 超过5则一定不能组成顺子 不能重复
    if(numbers.length !== 5) return false;
    let min = 14, max = -1; // max 和min值不能为0
    let count = [];
    for(let num of numbers){
        if(num <0 || num > 14) return false; //异常判断
        if(num === 0) continue;
        if(!count[num]){
            count[num] = 1;
        }else{
            return false; //重复则不是顺子
        }
        if(num > max){
            max = num;
        }
        if(num < min){
            min = num;
        }
        if(max - min >= 5) return false; //超过个数一定不能成顺子
    }
    return true;
}
function IsContinuous(numbers)
{
    //数学思维 满足顺子 max - min < 5才成立 即个数 - 1 = max - min 超过5则一定不能组成顺子 不能重复
    if(numbers.length !== 5) return false;
    let min = 14, max = -1; // max 和min值不能为0
    let flag = 0;
    for(let num of numbers){
        if(num <0 || num > 14) return false; //异常判断
        if(num === 0) continue;
        if((flag >> num) & 1 === 1) return false; //有重复的移位
        flag |= 1 << num;
        if(num > max){
            max = num;
        }
        if(num < min){
            min = num;
        }
        if(max - min >= 5) return false; //超过个数一定不能成顺子
    }
    return true;
}
  1. 约瑟夫环问题
function LastRemaining_Solution(n, m)
{
    if(n < 1 || m < 1) return -1;
    let step = 0, i = -1, arr = Array(n), count = n
    while(count > 0){
        i++;
        if(i === n) i = 0;
        if(arr[i] === -1) continue; //已经出队
        step++; //计步
        if(step === m){ //满足条件出队
            arr[i] = -1;
            step = 0;
            count--;
        }
    }
    return i;
}
  1. 转化成整数
function StrToInt(str)
{
    if(!str.trim()) return 0;
    let sum = 0, sign = str[0];
    if(str.startsWith('-') || str.startsWith('+')){
        str = str.slice(1);
    }
    for(let val of str){
        if(val >= '0' && val <= '9'){
            sum = sum * 10 + (val - '0');
        }else{
            return 0;
        }
    }
    sum = (sign === '-') ? -sum : sum;
    //判断数据是否溢出  数据范围补码在 -Math.pow(2, 31) Math.pow(2, 31) - 1
    if(sum >= -Math.pow(2, 31) && sum <= Math.pow(2, 31) - 1){
        return sum;
    }else{
        return 0;
    }
}
  1. 累乘
function multiply(array)
{
    //使用倒三角累乘方法
    let res = [], resB = [], result = [];
    res[0] = 1;
    resB[array.length - 1] = 1;
    for(let i = 1; i < array.length; i++){
        res[i] = res[i - 1] * array[i - 1];
    }
    //下三角累乘
    for(let j = array.length - 2; j >= 0; j--){
        resB[j] = resB[j + 1] * array[j + 1];
    }
    //累乘
    for(let i = 0; i < array.length; i++){
        result[i] = res[i] * resB[i];
    }
    return result;
}
  1. 字符串匹配
//s, pattern都是字符串 类似最小编辑距离
function match(s, pattern)
{
    if(!s && !pattern){ //都为空匹配成功
        return true;
    }
    if(s && !pattern){ //s存在但patter为空匹配失败
        return false;
    }
    //s为空,pattern存在时可能匹配成功 a*a*a* 全为0个字符
    //当第二个字符不为*时 正常匹配
    if(pattern[1] !== '*'){
        if(s[0] === pattern[0] || (s.length !== 0 && pattern[0] === '.')){
            return match(s.slice(1), pattern.slice(1));
        }else{
            return false;
        }
    }else{
        if(s[0] === pattern[0] || (s.length !== 0 && pattern[0] === '.')){
            return match(s, pattern.slice(2)) || match(s.slice(1), pattern); //匹配0个或者多个
        }else{
            return match(s, pattern.slice(2)); //匹配0个 正则表达式后移2个
        }
    }
}
  1. 字符流中第一个出现的字符
  2. 判断是否为整形数字
  3. 链表中环的入口节点
  4. 删除链表中的重复节点

笔试编程题

Bigo笔试题(20190827)

  1. 网页上动态显示距离今年结束倒计时功能
function calNumLength(num){
    return num > 9 ? num : '0' + num;
}
function getRemainTime(timeDom){
    let year = (new Date()).getFullYear();
    let endYear = new Date(year,11,31);
    setInterval(() => {
        var nowTime = Date.now();
        var remainTimeMill = endYear.getTime() - nowTime;
        var timeDiff = Math.floor(remainTimeMill / 1000);
        //进制转换
        var day = parseInt(timeDiff / 3600 / 24);
        var hour = parseInt(timeDiff / 3600 % 24);
        var minute = parseInt(timeDiff / 60 % 60);
        var second  = parseInt(timeDiff % 60);
        timeDom.innerHTML = `据${year}还有${calNumLength(day)}${calNumLength(hour)}:${calNumLength(minute)}:${second}`;
    },1000);
}
window.onload  = () => {
    var timeDom  = document.getElementById('time');
    getRemainTime(timeDom);
}
  1. 用js实现链表,找出链表中的相同数据,不能重新申请空间
var findSameNum = (li1,li2) => {
    var p = li1, q = li2;
    var resultList = new Node(0);
    var res = resultList;
    var sortFlag = 0; //0升序/1降序
    if(!p || !q){
        return;
    }
    if(p.data > p.next.data){
        sortFlag = 1;//降序
    }
    while(p && q){
        var pData = p.data;
        var qData = q.data;
        if(pData === qData){
            let sameList = new Node(pData);
            resultList.next = sameList;
            resultList = sameList;
            p = p.next;
            q = q.next;
        }else if(pData > qData){
           if(sortFlag){
                p = p.next;
           }else{
                q = q.next;
           }
        }else{
            if(sortFlag){
                q = q.next;
           }else{
                p = p.next;
           }
        }
    }
    return res.next;
}
  1. Excel表的列字母转换,输入第几列,输出列字母组合,看不懂题?(excel列字母数字表示互转 26进制转换)
var letterToNum = (str) => {
    //判断是否为空
    if(!str){
        return ;
    }
    //判断是否'A-Z'
    var newStr = str.toUpperCase();
    var regStr =  /[A-Z]+/g;
    if(!newStr.match(regStr)){
        return ;
    }
    var num = 0, i = 0, bitNum;
    while(i < newStr.length){
        bitNum = newStr.charAt(i).charCodeAt() - 'A'.charCodeAt() + 1;
        //低位累加法
        // num += bitNum * Math.pow(26,(newStr.length - i - 1));
        //高位累乘法
        num = num * 26 + bitNum;
        i++;
    }
    return num;
}
var numToLetter = (num) => {
    if(num <= 0){
        return ;
    }
    var resStr = '';
    while(num > 0 ){
        num--;
        resStr = String.fromCharCode(((num % 26) + 'A'.charCodeAt())) + resStr;
        num = (num - num % 26) / 26;
    }
    return resStr;
}
  1. 无重复数组中找出和target的所有组合方式
  • 回溯思想 (dfs栈调用,多维解法)
//括号回溯法 递归 找约束条件 多维值 全局变量 分析问题画出状态树进行递归 一般回溯都用dfs遍历(递归或者栈调用)
//括号回溯法 递归 找约束条件 多维值 全局变量 分析问题画出状态树进行递归 一般回溯都用dfs遍历(递归或者栈调用)
var findCom = (res, k, l, r, str) => {
    if(k === 2 * n){
       return res.push(str.join(''));
    }
    if(l < n){
        str[k] = '(';
        findCom(res, k + 1, l+ 1, r, str);
    }
    if(r < n && l > r){
        str[k] = ')';
        findCom(res, k + 1, l , r + 1, str);
    }
}
var n = 3;
var res = [];
var str = [];
var l = 0; r = 0; //左右括号数
findCom(res, 0, l, r, str);//第二个参数为括号总数
console.log(res);
  • 数组不重复,可重复数字
//递归  二叉树 组合思想
var comSum = (candidates, target, start, temp, res) => {
    if(candidates.length === 0){
        return;
    }
    if(target === 0){
        res.push(temp.join(""));
        return;
    }
    for(var i = start; i < candidates.length; i++){
        var num = target - candidates[i];
        if(num >= 0){
            temp.push(candidates[i]);
            comSum(candidates, num, i, temp, res);
            temp.pop();
        }
        /*else {
            return; 当数组是升序时即可打开该代码,应为减去一个小值都小于0,减去一个大值必定小于0,无循环必要 
        }*/
    }
}
var res = [];
var arr = [3, 2, 5];
comSum(arr, 8, 0, [], res)
console.log(res);
  • 无重复数字
//排序去掉重复数字 sort()函数默认按字符串来比较
var comSum = (candidates, target, start, temp, res) => {
    if(!candidates.length){
        return;
    }
    if(!target){
        res.push(temp.slice());
    }
    for(var i = start; i < candidates.length; i++){
         //去除重复元素 i > start 这样重复的时候仍然可以取重复值 i >= 1 将会导致重复值无法取到
        if(i > start && candidates[i] === candidates[i - 1]){
            continue ; //排序去除重复元素
        }
        var num = target - candidates[i];
        if(num >= 0){
            temp.push(candidates[i]);
            comSum(candidates, num, i + 1, temp, res);
            temp.pop();
        }else{
            return;
        }
    }
}
function compare(a, b) {
   return a - b; //false不变序 升序排列
  }
var arr = [10, 1, 2, 7, 6, 1, 5];
var res = [];
//sort()默认转化为字符串进行ascii码排序
arr.sort(compare);//升序排列
comSum(arr, 8, 0, [], res);
console.log(res);

字节跳动笔试题(20190829)

小米笔试题(20190908)

  1. 二叉树根据括号表达式中序遍历树
function Node(data){
    this.left = this.right = null;
    this.data = data;
}
//根据广义表生成树
function createBinTreeByGList(str){
    const LEFT_CHILD = 1, RiGHT_CHILD = 2;
    let  childFlag = 0;
    let  currenNode = null, root = null;
    let stack = [], i = 0, top = -1;
    let len = str.length;
    // stack装的是相对的父元素,即(左边的元素
    while(i < len){
        let data = str.charAt(i++);
        switch(data){
            case '(':
                childFlag = LEFT_CHILD;
                stack[++top] = currenNode;
                break;
            case ',':
                childFlag = RiGHT_CHILD;
                break;
            case ')':
                childFlag = 0;
                top--;
                break;
            case '':
                break;
            default: 
                currenNode = new Node(data);
                if(!root){
                    root = currenNode;
                }else{
                    switch(childFlag){
                        case LEFT_CHILD:
                            stack[top].left = currenNode;
                            break;
                        case RiGHT_CHILD:
                            stack[top].right = currenNode;
                            break;
                    }
                }
                break;
        }
    }
    return root;
}
//中序遍历
function inOrderTree(tree, orderArr){
    if(tree){
        inOrderTree(tree.left, orderArr);
        orderArr.push(tree.data);
        inOrderTree(tree.right, orderArr);
    }
}
var str = '(A(B(C,D),E(,F)))';
var tree = createBinTreeByGList(str);
var orderArr = [];
var res = inOrderTree(tree, orderArr);
  1. 二叉树交换左右子树
//交换左右子树
function reverseTree(tree){
    if(tree){
        if(!tree.left && !tree.right){
            return;
        }
        let temp = tree.left;
        tree.left = tree.right;
        tree.right = temp;
        reverseTree(tree.left);
        reverseTree(tree.right);
    }
}
  1. 最少商品数,数量不限获取某金额买到的最少商品
回溯思想,同bigo笔试题(转化为商品可重复或者商品不可重复问题)

奇信安笔试题(20190916)

  1. 输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示,2进制转换。
// 补码: 正数的原反补码相同, 负数的反码和补码单独计算 负数的 补码用程序表达则为n(负数) (-n - 1)取反
function NumberOf1(num)
{
    // write code here
    let res;
    if(num >= 0){
        res = tenToBin(num, true);
    }else{
        //-n-1 为补码包括符号位取反前的值 如: n = -3 补码: 11111101取反 00000010 = 2 = -n - 1
        res = tenToBin(-num - 1, false);
    }
    return res;
}
function tenToBin(num, isPositive){
    let res = '', count = 0, len = 32;
    while(num){
        if(num % 2 === 1){
            count++;
        }
        //不用parseInt(num  / 2)即可获得商
        num = (num - num % 2) / 2;
    }
    //如果是负数,补码中1的个数 = 0的个数 = 32 - 1的个数
    if(!isPositive){
        count = len - count;
    }
    return count;
}
function numberOfone(n){ 
    let count = 0;
    for(let i = 0; i < 32; i++){ //计算机中数本来就是用补码表示的,直接移位可获得1的个数
        if((n >>> i) & 1 === 1){
            count++;
        }
    }
    return count;
}

招商银行笔试题(20190917)

1.在一根数轴上,1~n的每个点上都标有‘L’和‘R’,最初每个点上都有一个机器人,现在所有机器人同时一起执行以下操作10e100次:如果该点上标有‘L’,机器人左移;否则右移。保证点1上为‘R’,点n上为‘L’,最后每个点上有几个机器人?

var robatNum = (directionStr) => {
    let res = [];
    for(let k = 0; k < directionStr.length; k++){
        res[k] = 0;
    }
    let times = 8;
    for(let i = 0; i < directionStr.length; i++){
        let index = i, last;
        let temp = []; //计算每个机器人最后位置
        while(true){
            if(directionStr[index] === 'R'){
                index++;
            }else{
                index--;
            }
            if(temp.includes(index)){
                break;
            }else{
                temp.push(index);
            }
        }
        last = (times - temp.length - 1) % 2; //根据剩下的次数计算最后机器人的位置,推导过程
        index = last ? temp.pop() : temp[temp.length - 2];
        res[index]++;
    }
    return res;
}
console.log(robatNum('RRLRL'));

面试编程题

  1. 实现开根不借助其他工具
//二分法求算术平方根
function sqrtNum(num){
   if(num <= 0) return 0;
   let low = 1, high = num, precision = 1e-6;
   let mid;
   while(high - low > precision){
       mid = (low + high) / 2;
       if(mid * mid === num) return mid.toFixed(6);
       if(num > mid * mid){
           low = mid;
       }else{
           high = mid;
       }
   }
   // toFixed() => 四舍5入;
   return ((low + high) / 2).toFixed(6);
}
console.log(sqrtNum(3));
  1. 打印三角形:计算空格和*与行数的关系即可
//打印正三角
function printTriangleII(n){
    for(let i = 0; i < n; i++){
        let str = '';
        for(let j = n - 1; i < j; j--){
            str += ' ';
        }
        for(let k = 0; k <= i; k++){
            str += '* ';
        }
        console.log(str);
    }
}
printTriangleII(5);
//打印下三角
function printTriangleIII(n){
    for(let i = 0; i < n; i++){
        let str = '';
        //空格递增1
        for(let j = 0; j < i; j++){
            str += ' ';
        }
        //*递减2
        for(let k = n; 2 * i < k; k--){
            str += '*';
        }
        console.log(str);
    }
}
printTriangleIII(5);
//打印2个下三角
function printTriangleIIII(n){
    for(let i = 0; i < n; i++){
        let str = '';
        for(let k = 0; k < i; k++){
            str += ' ';
        }
        for(let j = n; 2 * i < j; j--){
            str += '*';
        }
        for(let k = 0; k < i; k++){
            str += ' ';
        }
        str += str;
        console.log(str);
    }
}
printTriangleIIII(5); 
  1. 计数排序
var sortArr = (arr) => {
    let countArr = [], min = arr[0], max = arr[0],resArr = [];
    //获取计数数组
    for(let i = 1; i < arr.length; i++){
        if(arr[i] > max){
            max = arr[i];
        }
        if(arr[i] < min){
            min = arr[i];
        }        
    }
    // 2的16次方 unicode
    for(let i = 0; i < max - min + 1; i++){
        countArr[i] = 0;
    }
    //计数
    for(let j = 0; j < arr.length; j++){
        countArr[arr[j] - min]++;
    }
    //展开值和下标
    let m = 0;
    for(let k = 0; k < countArr.length; k++){
        while(countArr[k]){
            resArr[m++] = k + min;
            countArr[k]--;
        }
    }
    /*
    //下标为数组值 ,值为相应数组值的结果数组中的下标
    for(let j = 1; j < max - min + 1; j++){
        countArr[j] = countArr[j] + countArr[j - 1]; 
    }
    //返回结果
    for(let j = 0; j < arr.length; j++){
        resArr[countArr[arr[j] - min] - 1] = arr[j];
        countArr[arr[j] - min]--;
    }*/
    return resArr;
};
var arr = [3, 5, 5, 1, 16,1, 66];
console.log(sortArr(arr));
  1. 括号匹配
//多种括号匹配配
function validBraces(str) {
    let reg = /[\(\{\[]/ ;// 正则表达式选中一个
    let arr = [], strArr = str.split('');
    for (let value of strArr) {
        if (reg.test(value)) { 
            arr.push(value);
        } else {
            switch (value) {
                case ')':
                    var temp = arr.pop();
                    if (temp !== '(') {
                        return false;
                    }
                    break;
                case '}':
                    var temp = arr.pop();
                    if (temp !== '{') {
                        return false;
                    }
                    break;
                case ']':
                    var temp = arr.pop();
                    if (temp !== '[') {
                        return false;
                    }
                    break;
            }
        }
    }
    return !arr.length;
}
console.log(validBraces('))))'));
var arr = [];