手撕代码

算法题

1.反转链表

2021.10.30
15:50

var reverseList = function(head) {
    var prev = null;
    var curr = head;
    while(curr){
        next=curr.next;
        curr.next=prev;
        prev=curr;
        curr=next;
    }
    return prev;
};

2.青蛙跳台阶(递归)

var numWays = function(n) {
    let arr=[1,1,2]
    if(n<=2){
        return arr[n]
    }else{
        for(let i=arr.length;i<=n;i++){
            arr[i]=(arr[i-1]+arr[i-2])%1000000007
        }
    }
    return arr[n]
};

3.链表中是否有环

//解题思路:设置快慢指针,若快指针可以追上慢指针,说明有环

var hasCycle = function(head) {
    if(head === null || head.next === null) {
    return false;
  }
  var slow=head;
  var fast=head.next;
  while(fast&&fast.next){
      if(fast==slow){
          return true
      }
      fast=fast.next.next;
      slow=slow.next;
  }
  return false
};

4.用两个栈实现队列

2021.11.05

//输入栈
let stack1 = []
//输出栈
let stack2 = []
function push(node)
{
    // write code here
    return stack1.push(node);
}
function pop()
{
    // write code here
    if(stack2.length === 0){
        while(stack1.length !== 0)
            {
                stack2.push(stack1.pop());
            }
    }
    return stack2.pop();
}

5.二分查找

var search = function(nums, target) {
    let left = 0;
    let right = nums.length-1;
    while (left<=right){
        var mid = left + Math.floor((right-left)/2);
        if(nums[mid]>target){
            right=mid-1
        }else if (nums[mid]<target) {
            left=mid+1
        }else{
            return mid;
        }
    }
    return -1
};

6.BFS层序遍历

2021.11.18

function levelOrder( root ) {
    if(!root) return []
    let res = [], queue = [root]
    while (queue.length !== 0) {
    const levelSize = queue.length
    const currLevel = []
    for (let i=0; i<levelSize; i++) 
         const node = queue.shift()
         currLevel.push(node.val)
          if (node.left) {
             queue.push(node.left)
             }
          if (node.right) {
             queue.push(node.right)
             }
            }
      res.push(currLevel)
    }
  return res
};

7.合并两个有序数组

替换功能,第一个参数(起始位置),第二个参数(删除的项数),第三个参数(插入任意数量的项)。
用法:array.splice(index,num,insertValue),返回值为删除内容,array为结果值。

思路:两个数字比较大小,如果nums1[i] > nums2[i] 就在nums1[i]前用splice(i, 0, nums2[i])
把nums2[i]插入
当循环到num1存0的位置直接把nums2剩下的插入就行了
var merge = function(nums1, m, nums2, n) {
  let j = 0;
  for (let i = 0; i < m + n; i++) {
    if (nums1[i] > nums2[j]) {
      nums1.splice(i, 0, nums2[j]);
      j++;
      nums1.pop();
    }
    if (i - j >= m) {
      nums1.splice(i, 0, nums2[j]);
      j++;
      nums1.pop();
    }
  }
};

8.经典排序

2021.11.10

(1)冒泡排序

时间复杂度为:O( n^2 );空间复杂度O(1)

function bubbleSort(nums) {
    //每轮循环都从最后一个元素开始 比较并交换 一次循环会把最小的数顶到最上面
    for (let i = 0; i < nums.length - 1; i++) { //只是控制次数为n-1
        for (let j = nums.length - 1; j > i; j--) {
            if (nums[j] < nums[j - 1]) {
                //交换
                let temp = nums[j];
                nums[j] = nums[j - 1];
                nums[j - 1] = temp;
            }
        }
    }
}

(2)快排

//快速排序最差O(n^2)最优O(nlogn)
function swap(nums, i, j) {
    let temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
}
function qsort(nums, l, r) {
    if (r <= l) return; //注意定义递归中止条件
    let pivot = nums[l]; //选择最左为轴值
    swap(nums, l, r); //把轴值与最右交换
    let i = l;
    for (let j = l; j < r; j++) {
        if (nums[j] < pivot) {
            swap(nums, i, j);
            i++;
        }
    }
    swap(nums, i, r); //此时i为轴值下标
    qsort(nums, l, i - 1);
    qsort(nums, i + 1, r);
}

(3)topK堆排序

//堆排序 建堆O(n) 删除n个O(nlogn)
function heapSort(nums) {
    if (nums.length <= 1) return nums;
    let res = [];
    let heapSize = nums.length;
    buildHeap(nums, heapSize);
    for (let i = 0; i < nums.length; i++) {
        res.push(nums[0]);
        swap(nums, 0, --heapSize);
        siftDown(nums, 0, heapSize);
    }
    return res;
}
//建堆 (最小堆)
function buildHeap(nums, heapSize) {
    for (let i = Math.floor(heapSize / 2) - 1; i >= 0; i--) {
        siftDown(nums, i, heapSize);
    }
}
//siftDown 调整堆
function siftDown(nums, i, heapSize) {
    let smallest = i;
    let l = 2 * i + 1;
    let r = 2 * i + 2;
    if (l < heapSize && nums[l] < nums[smallest]) {
        smallest = l;
    }
    if (r < heapSize && nums[r] < nums[smallest]) {
        smallest = r;
    }
    if (smallest != i) {
        swap(nums, i, smallest);
        siftDown(nums, smallest, heapSize);
    }
}
//交换
function swap(nums, i, j) {
    let temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
}

//test
nums = [5, 1, 4, 2, 8, 11, 2, 3];
let res = heapSort(nums);
console.log(res);

9.求平方根

var mySqrt = function(x) {
    if (x < 2) return x
    let left = 1
    let right = Math.floor(x / 2);
    while (left <= right) {
        const mid = Math.floor(left + (right - left) / 2)
        if (mid * mid === x) return mid
        if (mid * mid < x) {
            left = mid + 1
        }else {
            right = mid - 1
        }
    }
    return right
};

2020.12.07

10.移除元素

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]

var removeElement = function(nums, val) {
    var k =0;
    for(i=0;i<nums.length;i++){
        if(nums[i]!=val){
            nums[k++]=nums[i]
        }
    }
    return k;
};

11.有序数组平方

输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]

var sortedSquares = function(nums) {
    var result = [];
    for(i=0,j=nums.length-1;i<=j;){
        var right = Math.abs(nums[i]);
        var left = Math.abs(nums[j]);
        if(right>left){
            result.unshift(right*right);
            i++;
        }else{
            result.unshift(left*left);
            j--;
        }
    }
    return result;
};

12.长度最小的子数组

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 。
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2

var minSubArrayLen = function(target, nums) {
    var left=right=sum=0;
    var len=nums.length;
    var res = len+1;
    while(right<len){
        sum+=nums[right++];
        while(sum>=target){
            res=res<right-left?res:right-left;
            sum-=nums[left++];
        }
    }
    //最后判断别忘
    return res>len?0:res;
};

2021.12.08

13.移除链表元素

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

var removeElements = function(head, val) {
    var ret = new ListNode(0,head);
    var cur = ret;
    while(cur.next){
        if(cur.next.val===val){
            cur.next=cur.next.next;
            continue;
        }
        cur = cur.next
    }
    return ret.next
};

14.翻转链表

var reverseList = function(head) {
    var prev = null;
    var curr = head;
    while(curr){
        next=curr.next;
        curr.next=prev;
        prev=curr;
        curr=next;
    }
    return prev;
};

2021.12.09

15.删除链表的倒数第 n 个结点

双指针

var removeNthFromEnd = function(head, n) {
    let ret = new ListNode(0, head),
        slow = fast = ret;
    while(n--) fast = fast.next;
    while (fast.next) {
        fast = fast.next; 
        slow = slow.next
    };
    slow.next = slow.next.next;
    return ret.next;
};

2021.12.10

16.链表相交

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

var getListLen = function(head) {
    let len = 0, cur = head;
    while(cur) {
       len++;
       cur = cur.next;
    }
    return len;
} 
var getIntersectionNode = function(headA, headB) {
    let curA = headA,curB = headB, 
        lenA = getListLen(headA),
        lenB = getListLen(headB);
    if(lenA < lenB) {
        [curA, curB] = [curB, curA];
        [lenA, lenB] = [lenB, lenA];
    }
    let i = lenA - lenB;
    while(i-- > 0) {
        curA = curA.next
    }
    while(curA && curA !== curB) {
        curA = curA.next;
        curB = curB.next;
    }
    return curA;
};

17.链表是否有环

var hasCycle = function(head) {
    if(head === null || head.next === null) {
    return false;
  }
  var slow=head;
  var fast=head.next;
  while(fast&&fast.next){
      if(fast==slow){
          return true
      }
      fast=fast.next.next;
      slow=slow.next;
  }
  return false
};

18.字母异位

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

var isAnagram = function(s, t) {
    var map = {};
    for(v of s){
        if(map[v]){
            map[v]=map[v]+1
        }else{
            map[v]=1
        }
    }
    for(i of t){
        if(map[i]){
            map[i]=map[i]-1
        }else{
            map[i]=1
        }
    }
    return Object.values(map).every(v=>v==0);
};

2021.12.11

19.两个数组的交集

var intersection = function(nums1, nums2) {
    let hash1=new Set(nums1);
    let hash2=new Set();
    for(var i=0;i<nums2.length;i++){
        if(hash1.has(nums2[i])){
            hash2.add(nums2[i])
        } 
    }
    return [...hash2]
};

20.快乐数

对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为 1,那么这个数就是快乐数。

var isHappy = function (n) {
    let m = new Map()

    const getSum = (num) => {
        let sum = 0
        while (n) {
            sum += (n % 10) ** 2
            n = Math.floor(n / 10)
        }
        return sum
    }

    while (true) {
        // n出现过,证明已陷入无限循环
        if (m.has(n)) return false
        if (n === 1) return true
        m.set(n, 1)
        n = getSum(n)
    }
}

2021.12.13

21. 两数之和

// obj[target-nums[i]]=obj.[target-nums[i]]
var twoSum = function(nums, target) {
    let obj = {};
    for(let i = 0;i<nums.length;i++){
        if(obj[target-nums[i]]!==undefined){
            return[i,obj[target-nums[i]]]
        }
        obj[nums[i]]=i;
    }
};

2021.12.13

22. 赎金信

var canConstruct = function(ransomNote, magazine) {
    let table = new Array(26).fill(0);
    base = "a".charCodeAt();
    for(let s of magazine){
        table[s.charCodeAt()-base]++;
    }
    for(let s of ransomNote){
        const index = s.charCodeAt()-base;
        if(!table[index]) return false;
        table[index]--;
    }
    return true;
};

23. 字符串翻转

var reverseString = function(s) {
    var l =-1; var r = s.length;
    while(++l<--r){
        [s[l],s[r]]=[s[r],s[l]]
    }
    return s
};

24.翻转字符串(2)

给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。

        var reverseStr = function(s, k) {
            const len = s.length;
            let resArr = s.split("");
            for(let i = 0; i < len; i += 2 * k) {
                let l = i - 1, r = i + k > len ? len : i + k;
                while(++l < --r) [resArr[l], resArr[r]] = [resArr[r], resArr[l]];
            }
            return resArr.join("");
        };

2021.12.14

25.替换空格

把字符串 s 中的每个空格替换成"%20"。
示例 1:
输入:s = “We are happy.”
输出:“We%20are%20happy.”

var replaceSpace = function(s) {
   // 字符串转为数组
  const strArr = Array.from(s);
  let count = 0;

  // 计算空格数量
  for(let i = 0; i < strArr.length; i++) {
    if (strArr[i] === ' ') {
      count++;
    }
  }
  let left = strArr.length - 1;
  let right = strArr.length + count * 2 - 1;
  while(left >= 0) {
    if (strArr[left] === ' ') {
      strArr[right--] = '0';
      strArr[right--] = '2';
      strArr[right--] = '%';
      left--;
    } else {
      strArr[right--] = strArr[left--];
    }
  }
  // 数组转字符串
  return strArr.join('');
};

25.左旋转字符串

输入: s = “abcdefg”, k = 2
输出: “cdefgab”

var reverseLeftWords = function(s, n) {
    var length = s.length;
    let i = 0;
    while (i < length - n){
        s = s[length - 1] + s;
        i++;
    }
    return s.slice(0,length);
};

2021.12.15

26.实现strStr(),用kmp

输入:haystack = “hello”, needle = “ll”
输出:2

var strStr = function (haystack, needle) {
     if (needle.length === 0)
         return 0;
     const getNext = (needle) => {
         let next = [];
         let j = 0;
         next.push(j);
         for (let i = 1; i < needle.length; ++i) {
             while (j > 0 && needle[i] !== needle[j])
                 j = next[j - 1];
             if (needle[i] === needle[j])
                 j++;
             next.push(j);
         }
         return next;
     }
     let next = getNext(needle);
     let j = 0;
     for (let i = 0; i < haystack.length; ++i) {
         while (j > 0 && haystack[i] !== needle[j])
             j = next[j - 1];
         if (haystack[i] === needle[j])
             j++;
         if (j === needle.length)
             return (i - needle.length + 1);
     }
     return -1;
 };

2021.12.17

26.重复的子字符串

kmp,如果len % (len - (next[len - 1] + 1)) == 0 ,则说明 (数组长度-最长相等前后缀的长度) 正好可以被 数组的长度整除,说明有该字符串有重复的子字符串。

数组长度减去最长相同前后缀的长度相当于是第一个周期的长度,也就是一个周期的长度,如果这个周期可以被整除,就说明整个数组就是这个周期的循环。

var repeatedSubstringPattern = function (s) {
    if (s.length === 0)
        return false;

    const getNext = (s) => {
        let next = [];
        let j = 0;

        next.push(j);

        for (let i = 1; i < s.length; ++i) {
            while (j > 0 && s[i] !== s[j])
                j = next[j - 1];
            if (s[i] === s[j])
                j++;
            next.push(j);
        }

        return next;
    }

    let next = getNext(s);

    if (next[next.length - 1] !== 0 && s.length % (s.length - next[next.length - 1]) === 0)
        return true;
    return false;
};

10.斐波那契数列(动态规划)

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

var fib = function(n) {
   var dp = [];
   if(n==0){
       return 0
   }
   if(n == 1){
       return 1
   }
   dp[0] =0;
   dp[1]=1;
   for(var i =2;i<=n;i++){
       dp[i]=(dp[i-1]+dp[i-2])% 1000000007
   }
   return dp[n];
};

js手撕

2021.11.11

1.防抖和节流

防抖

触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间。

 <script>
        function pay() {
            console.log("点击")
        }
        function debounce (func,delay) {
            let timer;
            // 闭包
            return function () {
                let contest = this;
                clearTimeout(timer);
                timer = setTimeout(() => {
                    // 需要防抖的操作...
                    func().apply(contest, arguments);
                    console.log("防抖成功!");
                }, delay);
            }
        }
        var btn1 = document.getElementById('debounce');
        btn1.addEventListener("click", debounce(pay,5000));
    </script>

节流

函数被触发时,只响应在某个固定时间短内的第一次请求,后续的请求都不响应,直到下个时间周期,继续响应下个周期内的第一次请求,周而复始。

<button id="debounce">节流</button>
<script>
    function pay() {
        console.log("节流")
    }
    function debounce (func,delay) {
        let flag = true;
        return function () {
            if (!flag) return false;
            flag = false;
            setTimeout(() => {
                func.apply(this, arguments);
                flag = true;
            }, delay)
        }}
    var btn1 = document.getElementById('debounce');
    btn1.addEventListener("click", debounce(pay,1000));
</script>

2.函数柯里化

2021.11.14
reduce相当于一个累加器

function sum(...args) {
    //对第一层参数求和
    let x = args.reduce((acc, next) => {
        return acc + next;
    })
    // 返回一个新的函数 
    return function (...args2) { //第二层的参数
        // 当没有第二层参数时,返回x即可
        if (args2.length == 0) return x;
        let y = args2.reduce((acc, next) => {
            return acc + next;
        })
        return sum(x + y); //返回x+y作为新的第一层
    }
}
console.log(sum(1, 2)(3)(4)()); // 10

3.清空前后空格

String.prototype.trim = function () {
	// \s 为任意的空白符. 包括空格,换行符,制表 符,换页符
    return this.replace(/^\s+/g, "").replace(/\s+$/g, "");
}

4.数组拍平

//concat() 方法用于连接两个或多个数组。此方法返回一个新数组,不改变原来的数组。
function flatten(arr) {
    let res = [];
    for (let i = 0; i < arr.length; i++) {
        if (Array.isArray(arr[i])) {
            res = res.concat(flatten(arr[i]));
        } else {
            res.push(arr[i]);
        }
    }
    return res;
}

let arr = [1, 2, 3, [4, 5, 6, [7, 8, 9]], 10];
console.log(flatten(arr));

5.图片懒加载

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        img {
            display: block;
            width: 100%;
            height: 300px;
            margin-bottom: 10px;
        }
    </style>
</head>

<body>
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
</body>
<script>
    var imgs = document.querySelectorAll('img');

    //获得元素距离页面顶部的距离,(到浏览器顶部的距离)
    function getTop(e){
		 return e.offsetTop;
	}

    function lazyLoad(imgs) {
        //获取可视区高度
        var H = window.innerHeight || document.documentElement.clientHeight;
        //滚动区域高度
        var S = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
        for (let i = 0; i < imgs.length; i++) {
        //图片距离顶部的距离大于可视区域和滚动区域之和时懒加载
            if (H + S > getTop(imgs[i])) {
                imgs[i].src = imgs[i].getAttribute('data-src');
            }
        }
    }
    window.onload = window.onscroll = function () {
        lazyLoad(imgs);
    }
</script>
</html>

6.深拷贝和浅拷贝

        //浅拷贝
        function shallowCopy(obj) {
            var copy = {}
            for (var key in obj) {
                if (obj.hasOwnProperty(key)) {
                    copy[key] = obj[key]
                }
            }
            return copy;
        }

        //深拷贝
        function deepClone(obj){
            var clone;
            if (obj && typeof obj !== 'object') clone = obj;
            else {
                clone = Array.isArray(obj) ? [] : {};
                for (let k in obj) {
                    if (obj.hasOwnProperty(k)) {
                        if (obj[k] && typeof obj[k] === 'object') {
                            clone[k] = deepClone(obj[k]);
                        } else {
                            clone[k] = obj[k];
                        }
                    }
                }
            }
            return clone;
        }
        //test
        let obj = {
            name: 'vivian',
            age: 18,
            mark: {
                one: 1,
                two: 2
            },
            arr: [1, 2, 3]
        }
        let shallow=shallowCopy(obj)
        let deep = deepClone(obj);
        obj.arr[0]=10;
        console.log(shallow);
        console.log(deep);

7.call、apply、bind

//call,改变this指向 调用函数
Function.prototype.mycall = function (context, ...arg) {
    context.fn = this; //this是test函数,把他作为context的方法而存在
    let res = context.fn(...arg);
    delete context.fn;
    return res;
}
//test
let obj = {
    name: 'vivian'
}
function test(arg1, arg2, arg3) {
    console.log(this.name);
    console.log(arg1, arg2, arg3);
}
test.mycall(obj, 1, 2, 3);

//apply,改变this指向 调用函数参数为数组
Function.prototype.myApply = function (context, arg) {
    context.fn = this;
    let res;
    if (!arg) {
        res = context.fn();
    } else {
        res = context.fn(...arg);
    }
    delete context.fn;
    return res;
}

//bind改变this指向,不调用函数
Function.prototype.mybind = function (context, ...arg) {
    let fun = this;
    return function () {
        return fun.apply(context, arg)
    }
}

CSS手撕

1.两栏布局

(1)利用浮动,将左边元素宽度设置为200px,并且设置向左浮动。将右边元素的margin-left设置为200px,宽度设置为auto(默认为auto,撑满整个父元素)。

    <style>
        .outer{
            height: 500px;
        }
        .left{
            float: left;
            width: 200px;
            height: 500px;
            background-color: tomato;
        }
        .right{
            margin-left: 200px;
            height: 500px;
            width: auto;
            background-color: greenyellow;
        }
    </style>
    
   <div class="outer">
        <div class="left">left</div>
        <div class="right">right</div>
    </div>

利用浮动,左侧元素设置固定大小,并左浮动,右侧元素设置overflow: hidden; 这样右边就触发了BFC,BFC的区域不会与浮动元素发生重叠,所以两侧就不会发生重叠。

.left{
     width: 100px;
     height: 300px;
     background: red;
     float: left;
 }
 .right{
     height: 300px;
     background: blue;
     overflow: hidden;
 }

(2)利用flex布局,将左边元素设置为固定宽度200px,将右边的元素设置为flex:1。

.outer {
  display: flex;
  height: 100px;
}
.left {
  width: 200px;
  background: tomato;
}
.right {
  flex: 1;
  background: gold;
}

(3)利用绝对定位,将父级元素设置为相对定位。左边元素设置为absolute定位,并且宽度设置为200px。将右边元素的margin-left的值设置为200px。

.outer {
  position: relative;
  height: 100px;
}
.left {
  position: absolute;
  width: 200px;
  height: 100px;
  background: tomato;
}
.right {
  margin-left: 200px;
  background: gold;
}

利用绝对定位,将父级元素设置为相对定位。左边元素宽度设置为200px,右边元素设置为绝对定位,左边定位为200px,其余方向定位为0。

.outer {
  position: relative;
  height: 100px;
}
.left {
  width: 200px;
  background: tomato;
}
.right {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 200px;
  background: gold;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值