代码记录

186 阅读4分钟

字符串数组反转,要求空间复杂度为O(1)

// 使用o(1)空间复杂度实现字符串反转 双指针

function reverse(arr){
    let right = arr.length - 1
    let left = 0
    while(left < right){
        const temp = arr[left]
        arr[left] = arr[right]
        arr[right] = temp
        left++
        right--
    }
    return arr
}
console.log(reverse(['a', 'b', 'c', 'd']));

数组扁平化-复杂

> 输入:obj ={ 
            a:1, 
            b:{ 
                h:[{f: '2'}, {g: '3'}] 
              }, 
            c: { 
                d: [1, 2, {e: true}] 
               } 
            }
> 输出:{
          a: 1,
          'b.h[0].f': '2',
          'b.h[1].g': '3',
          'c.d[0]': 1,
          'c.d[1]': 2,
          'c.d[2].e': true
        }
function flattenObj(obj) {
    let res = {}
    for (let key in obj) {
        // 如果是一个对象 就要继续深挖
        if (typeof obj[key] === "object" && obj[key] !== null) {
            flatten(res,obj[key],`${key}`)
        }
        else {
            res[key]= obj[key]
        }
    }
    function flatten (res, obj ,keyname){
        for(let key in obj){
            if (typeof obj[key] === "object" && obj[key] !== null ) {
                // 如果内部是数组就要按照索引输出
                if (Array.isArray(obj[key])){
                    for(let i = 0; i < obj[key].length; i++){
                        // 如果数组中的数据项还是对象,就要继续调用函数
                        if (typeof obj[key][i] === 'object'){
                            flatten(res, obj[key][i],`${keyname}.${key}[${i}]`)
                        }
                        else{
                            res[`${keyname}.${key}[${i}]`]= obj[key][i]
                        }
                        // flatten(res, obj[key],`${keyname}${key}[$i]`)
                        
                    }
                }else{
                    flatten(res, obj[key],`${keyname}.${key}`)
                }
            }else {
                res[`${keyname}.${key}`]= obj[key]
            }
        }
    }
    return res
}
const obj ={
    a:1,
    b:{
        h:[{f: '2'},{g: '3'}]
    },
    c: {
        d: [1, 2, {e: true}]
    }
}
console.log(flattenObj(obj));

数组扁平化-简单

function flatten(arr){
    let res = []
    for(let i = 0; i < arr.length; i++){
        if(Array.isArray(arr[i])){
            next = flatten(arr[i])
            res = res.concat(next)
        }
        else{
            res.push(arr[i])
        }
    }
    return res
}
const arr = [1,2,[3,4,[5,6]]]
console.log(flatten(arr))

实现一个函数用于在字符串中查找出现次数最多的字符以及其出现次数

var str = "aabcdobdackoppz";
var obj = {};
for (var i = 0; i < str.length; i++) {
  var chars = str.charAt(i); // chars 是 字符串的每一个字符
  if (obj[chars]) {
    // o[chars] 得到的是属性
    obj[chars]++;
  } else {
    obj[chars] = 1;
  }
}
// console.log(obj);
var max = 0;
var ch = '';
for (var k in obj) {  // 遍历对象需要使用 for in
    // k 得到的是属性名,obj[k]得到的是属性值
    // 此处的属性名k 只有在循环中才有用 , 所以我们需要创建一个 ch 来存储 k
    if (obj[k] > max) {
        max = obj[k];
        ch = k;
    }
}
console.log( ch + '出现次数最多' + '次数为:' + max);

this指向

var a = 1;
var obj = {
    a : 2,
    A : function(){
        let B = () => {
            console.log(this.a);
        }
        return B;
    }
}
obj.A()()

根据函数调用方式的不同,this的值也不同:
1. 以函数形式调用,this是window
2. 以方法形式调用,this是调用方法的对象
3. 构造函数中,this是新建的对象
4. 箭头函数没有自己的this,由外层作用域决定
5. 通过call和apply调用的函数,它们的第一个参数就是函数的this
6. 通过bind返回的函数,this由bind第一个参数决定(无法修改)
bind() 是函数的方法,可以用来创建一个新的函数
- bind可以为新函数绑定this
- bind可以为新函数绑定参数
7. 箭头函数没有自身的this,它的this由外层作用域决定,
也无法通过call apply 和 bind修改它的this
箭头函数中没有arguments

var let const变量提升

var name = 'World'
function A(){
    if(!name){
        var name = 'jack'
        console.log('Hello' + name)
    }else{
        console.log('GoodBye' + name)
    }
}

var存在变量提升 var name; name即undefined !undefined=True

若是用const或者let声明的变量,即输出goodbye

image.png

url参数(即后边的查询字符串)解析为对象

function parseStr(url_string){
    const paramArr = url_string.slice(url_string.indexOf("?")+1).split("&");
    const params = {};
    paramArr.map((param) => {
        const [key, value] = param.split("=")
        params[key] = value
    });
    return params;
}
const url_string = 'https://www.baidu.com/s?rsv_dl=tb&rsv_sug3=15&rsv_sug1=13&rsv_sug7=101&rsv_sug2=0&rsv_btype=i&rsp=5&inputT=3051&rsv_sug4=8729';
console.log(parseStr(url_string))

补充 image.png

从一个不重复的有序数组中找到2个数A,B的和为sum

// 利用对象存储已经遍历过的数字,数字值为key,数字的索引为value
function findTwoNumbersWithTargetSum(nums, target){
    const numMap = {}
    for(let i = 0; i < nums.length; i++){
        const complement = target - nums[i]
        if(numMap[complement] !== undefined){
            return [complement, nums[i]]
        }else{
            // {1: 0}
            numMap[nums[i]] = i
        }
    }
    return null;
}

// 利用new Map() 思路同上
function findTwoNumbersWithTargetSum(nums, target){
    const numMap = new Map();
    for(let i = 0; i < nums.length; i++){
        const complement = target - nums[i]
        if(numMap.has(complement)){
            return [complement, nums[i]] 
        }else{
            numMap.set(nums[i], i)
        }
    }
}

// 利用双指针法
function findTwoNumbersWithTargetSum(nums,target){
    let left = 0
    let right = nums.length - 1;
    while (left < right){
        const sum = nums[left] + nums[right];
        if (sum === target) {
            return [nums[left], nums[right]];
        }else if (sum < target) {
            left++;
        }else{
            right--
        } 
    }
    return null;
}
nums = [1,3,4,6,8,9];
const target = 12
const result = findTwoNumbersWithTargetSum(nums,target);
console.log(result); 


const sumtable = (nums, target) => { // 得到的是 对应数字的索引
    const newMap = new Map()
    for(let i = 0; i < nums.length; i++){
        if(newMap.has(nums[i])){
            return [newMap.get(nums[i]), i];
        }else{
            newMap.set(target - nums[i], i)
        }
    }
}
const nums = [1,3,4,6,8,9];
const target = 12
result = sumtable(nums, target);
console.log(nums[result[0]], nums[result[1]]);

版本号比较

function compare(v1, v2){
    v1 = v1.split('.') // 字符串
    v2 = v2.split('.')
    const len = Math.max(v1.length, v2.length)
    while(v1.length < len){
        v1.unshift('0') // 首部添加元素
    }
    while(v2.length < len){
        v2.unshift('0')
    }
    for(let i = 0; i < len; i++){
        const a = parseInt(v1[i])
        const b = parseInt(v2[i])
        if (a > b){
            return 1
        }else if(a < b){
            return -1
        }
    }
    return 0
}

const v1 = '0.1'
const v2 = '0.0.1'
console.log(compare(v1, v2));

布局设计:左边固定 右边自适应

(5条消息) 实现两栏布局(左侧固定 + 右侧自适应布局)_左侧固定右侧自适应布局_Falicitas的博客-CSDN博客

事件循环机制代码阅读

async function async1() {
  console.log("res1");
  await async2();
  console.log("res2");
}

async function async2() {
  console.log("res3");
}

console.log("res4");
async1();

setTimeout(() => {
  console.log("res5");
}, 0);

new Promise((resolve, reject) => {
  console.log("res6");
  resolve();
}).then(() => {
  console.log("res7");
});
4 1 3 6 2 7 5

image.png

image.png

发布订阅原理代码编写

(5条消息) 实现发布订阅模式-----手撕js系列_手撕发布订阅模式_万希&的博客-CSDN博客

class EventCenter{
    //1.定义事件容器,用来装事件数组
   handlsers={};
   //事件名,事件参数,事件方法
    addEventListener(type ,handlser)
    {
        //创建新的数组
        if(!this.handlsers[type])
        {
            this.handlsers[type]=[]
        }
        this.handlsers[type].push(handlser)
    }
    //2.触发事件
    dispatchEvent(type)
    {
        //没有注册抛出错误
        if(!this.handlsers[type])
        {
            return new Error("该事件未注册")
        }
        //触发事件
        this.handlsers[type].forEach(handlser=>{
            handlser(...params)
        })
    }
    //3.事件移除
    removeEventListener(type,handlser)
    {
        //不存在该事件
        if(!this.handlsers[type])
        {
            return new Error("该事件无效")
        }
        //无事件方法,删除该事件的订阅和发布
        if(!handlser)
        {
            delete this.handlsers[type]
        }else{
            //找出对应事件方法的发布订阅数组的下标
            const index=this.handlsers[type].findIndex(el=>el===handlser)
            //未找到
            if(index===-1)
            return new Error("无绑定该事件")
            //找到下标,删除该事件的订阅和发布事件
            this.handlsers[type].splice(index,1)
            if(this.handlsers[type].length===0)
            {
                delete this.handlsers[type]
            }
        }
    }


}

数组中的最大值

let arr = [1, -1, '1',[-1, [2, [3, [4, 5]]], 16],7, true, 6,true, '6',false, false, 'true', 'a', {}, {}];
// 1.数组求其中的最大数值
function flatArr(arr) {
    let res = []
    for(let i = 0; i < arr.length; i++){
        if(Array.isArray(arr[i])){
            next = flatArr(arr[i])
            res = res.concat(next)
        }else{
            res.push(arr[i])
        }
    }
    return res
}

function maxVal(arr) {
    // 1 自行书写扁平化代码
    let newArr = flatArr(arr)
    // 2 利用js内置函数,无限层扁平化处理
    let newArr = arr.flat(Infinity)
    let newNum = []
    for(let i = 0; i < newArr.length; i++){
        if(typeof newArr[i] === 'number'){
            newNum.push(newArr[i])
        }else{
            new_val = Number(newArr[i])
            if(!isNaN(new_val)){
                newNum.push(new_val)
            }
 
        }
    }
    return Math.max(...newNum)
}

let arr = [1, -1, '1',[-1, [2, [3, [4, 5]]], 16],7, true, 6,true, '6',false, false, 'true', 'a', {}, {}];
console.log(maxVal(arr));

深拷贝

// 深拷贝的实现 function deepCopy(object) { 
    if (!object || typeof object !== "object") return; 
    let newObject = Array.isArray(object) ? [] : {}; 
    for (let key in object) { 
        if (object.hasOwnProperty(key)) { 
            newObject[key] = typeof object[key] === "object" ? deepCopy(object[key]) : object[key]; 
        } 
    } 
    return newObject;
}

井字棋

3*3 提供的参数是棋手下棋的路径

var tictactoe = function(moves) {
    // 构造棋盘 
    let res = new Array(3); //表格有numRows行
    let flag = ''
    for (var i = 0; i < res.length; i++) {
      res[i] = new Array(3).fill(0); //每行有i+1列
    }
    for(let i = 0; i < moves.length; i++){
        if((i + 1) % 2 === 0){
            res[moves[i][0]][moves[i][1]] = 'o'
        }else{
            res[moves[i][0]][moves[i][1]] = 'x'
        }
    }
    // 行
    for(let i = 0; i < 3; i++){
        if(res[i][0] !== 0 && res[i][0] === res[i][1] && res[i][1] === res[i][2]){
            flag = res[i][0]
        }else{
            continue
        }
    }
    // 列
    for(let j = 0; j < 3; j++){
        if(res[0][j] !== 0 && res[0][j] === res[1][j] && res[1][j] === res[2][j]){
            flag = res[0][j]
        }else{
            continue
        }
    }
    // 主对角线
    if(res[0][0] !== 0 && res[0][0] === res[1][1] && res[1][1] === res[2][2]){
        flag = res[0][0]
    }
    // 副对角线
    if(res[0][2] !== 0 && res[0][2] === res[1][1] && res[1][1] === res[2][0]){
        flag = res[0][2]
    }
    if(flag === 'x') return 'A'
    else if(flag === 'o') return 'B'
    else if(flag === ''){
        for(let i = 0; i < 3; i++){
            if(res[i].indexOf(0) !== -1){
                return 'Pending' 
            }else{
                continue
            }
        }
        return 'Draw'
    }
};
const moves = [[2,2],[0,2],[1,0],[0,1],[2,0],[0,0]]
console.log(tictactoe(moves));

n*n —— 字符串

var tictactoe = function (board) {
  let isPending = false;
  let len = board.length;
  let crossL = "";
  let crossR = "";
  // 行
  for (let i = 0; i < len; i++) {
    // 生成行
    let row = board[i];
    // 有空格的情况(没有走完棋子)
    if (!isPending && / /.test(row)) isPending = true;
    // 接下来,执行正则表达式 / /.test(row) 的匹配操作。
    // 正则表达式 / / 表示匹配一个空格字符。
    // .test(row) 是一个 JavaScript 的正则表达式方法,用于检查字符串 row 是否匹配给定的正则表达式
    // 匹配行
    if(checkLine(row)) return checkLine(row)
  }
  // 列
  for(let i = 0; i < len; i++){
    let col = ''
    for(let j = 0; j < len; j++){
        col += board[j][i]
    }
    if(checkLine(col)) return checkLine(col)
  }
  // 主对角线, 匹配右上到左下交叉线
  for(let i = 0; i < len; i++){
    crossR += board[i][i]
  }
  if(checkLine(crossR)) return checkLine(crossR)
  // 副对角线
  for(let i = 0; i < len; i++){
    crossL += board[i][len - i - 1]
  }
  // 匹配左上到右下交叉线
  if(checkLine(crossL)) return checkLine(crossL)
  return isPending ? "Pending" : "Draw";
};
function checkLine(line){
    const x = 'XXX'
    const o = 'OOO'
    if (line.indexOf(x) !== -1) {
        return x[0]; // 匹配项的第一个捕获组的子串,并返回该子串。
    } else if(line.indexOf(o) !== -1){
        return o[0]
    }
}
const board = ["O X"," XO","X O"]
console.log(tictactoe(board));

n*n —— 数组

var tictactoe = function (board) {
  let isPending = false;
  let len = board.length;
  let crossL = [];
  let crossR = [];
  // 行
  for (let i = 0; i < len; i++) {
    // 生成行
    let row = board[i];
    // 有空格的情况(没有走完棋子)
    if (!isPending && / /.test(row)) isPending = true;
    // 接下来,执行正则表达式 / /.test(row) 的匹配操作。
    // 正则表达式 / / 表示匹配一个空格字符。
    // .test(row) 是一个 JavaScript 的正则表达式方法,用于检查字符串 row 是否匹配给定的正则表达式
    // 匹配行
    if (checkLine(row)) return checkLine(row);
  }
  // 列
  for (let i = 0; i < len; i++) {
    let col = [];
    for (let j = 0; j < len; j++) {
      // col += board[j][i]
      col.push(board[j][i]);
    }
    if (checkLine(col)) return checkLine(col);
  }
  // 主对角线, 匹配右上到左下交叉线
  for (let i = 0; i < len; i++) {
    // crossR += board[i][i]
    crossR.push(board[i][i]);
  }
  if (checkLine(crossR)) return checkLine(crossR);
  // 副对角线
  for (let i = 0; i < len; i++) {
    // crossL += board[i][len - i - 1]
    crossL.push(board[i][len - i - 1]);
  }
  // 匹配左上到右下交叉线
  if (checkLine(crossL)) return checkLine(crossL);
  return isPending ? "Pending" : "Draw";
};
function checkLine(line) {
  const newline = line.join("");
  const x = "XXX";
  const o = "OOO";
  if (newline.indexOf(x) !== -1) {
    return x[0]; // 匹配项的第一个捕获组的子串,并返回该子串。
  } else if (newline.indexOf(o) !== -1) {
    return o[0];
  }
}
const board = [
  ["O", " ", "X"],
  [" ", "X", "O"],
  ["X", " ", "O"],
];
console.log(tictactoe(board));

检查数组中是否有三个连续相等的字符

// 暴力解法
function hasConsecutiveTriple(array) {
  let count = 1; // 初始化计数器为1,因为我们要检查的是连续的三次
  for (let i = 1; i < array.length; i++) {
    if (array[i] === array[i - 1]) {
      count++; // 如果当前元素与前一个元素相同,增加计数器
      if (count === 3) {
        return true; // 如果计数器达到3,表示存在连续三次的值,返回true
      }
    } else {
      count = 1; // 如果当前元素与前一个元素不同,重置计数器为1
    }
  }
  return false; // 遍历完数组后没有找到连续三次的值,返回false
}

// 利用some
function hasConsecutiveTriple(array) {
  return array.some((value, index) => {
    return value === array[index + 1] && value === array[index + 2];
  });
}
/*
    这个简化的版本使用 some() 方法遍历数组,并在每个元素上检查当前元素与下两个元素是否相等。
    如果找到满足条件的元素,即连续出现三次的值,some() 方法将返回 true,否则返回 false。
*/

function hasConsecutiveTriple(array) {
  const str = array.join(""); // 将数组转换为字符串
  const regex = /(\d)\1{2}/; // 匹配连续三次的数字
  return regex.test(str);
}
/*
    这个版本将数组转换为字符串,然后使用正则表达式 /(\d)\1{2}/ 进行匹配。
    其中,(\d) 表示匹配任意数字,并使用捕获组。
    \1{2} 表示捕获组的反向引用,匹配前一个捕获组匹配的数字连续出现两次。
    如果字符串中存在连续三次的匹配,test() 方法将返回 true,否则返回 false。
*/

// 双指针
function hasConsecutiveTriple(array) {
  let start = 0; // 起始指针
  let end = 0; // 结束指针

  while (end < array.length) {
    if (array[end] === array[start]) {
      if (end - start === 2) {
        return true; // 找到连续三次的值
      }
      end++;
    } else {
      start = end;
    }
  }

  return false; // 遍历完数组后没有找到连续三次的值
}
/*
这个版本使用两个指针 start 和 end 来表示当前连续序列的起始位置和结束位置。
在遍历数组时,如果 end 指向的元素与 start 指向的元素相同,则增加 end,直到序列的长度为 3,返回 true。如果元素不相同,则将 start 移动到 end 的位置。如果遍历完数组后都没有找到连续三次的值,返回 false。
这种方法利用了双指针的特性,可以在一次遍历中完成判断,而不需要额外的计数器或字符串操作。
*/

// 转换为字符串
function checkLine(line) {
  const newline = line.join("");
  const x = "XXX";
  const o = "OOO";
  if (newline.indexOf(x) !== -1) {
    return x[0]; // 匹配项的第一个捕获组的子串,并返回该子串。
  } else if (newline.indexOf(o) !== -1) {
    return o[0];
  }
}

const array1 = [1, 2, 2, 2, 3, 4, 4, 4, 5];
console.log(hasConsecutiveTriple(array1)); // 输出: true

const array2 = [1, 2, 2, 3, 4, 4, 5];
console.log(hasConsecutiveTriple(array2)); // 输出: false

数组移动

function rightMove(arr, move) {
    const len = arr.length;
    if (Math.abs(move) > len) move = move % len;
    if (move < 0) move = move + len
    return arr.splice(len - move, len).concat(arr);
}
const arr = [1, 2, 3, 4, 5];
const move = -7;
console.log(rightMove(arr, move));

对大量数据排序

image.png