leetcode刷题记录 js算法与数据结构 基础篇(上)

立志做一个情感博主的程序员


leetcode刷题记录 js算法与数据结构 基础篇(上)_第1张图片
WechatIMG30.jpeg

1 #### 反转字符串中的单词
给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。
输入: "Let's take LeetCode contest"
输出: "s'teL ekat edoCteeL tsetnoc"

export default str => {
  // 1 将字符串切割成数组
  let arr = str.split(/\s+/g);
  // 2 然后再讲数据的每一项再切成数组[[abc], [bcd], [efg]]  从而利用数组的  reverse进行反转
  let result = arr.map(item => {
      return item.split('').reverse().join('')
  })
  return result.join(' ')
}
// 不懂split reverse join 和map方法的请自行百度

2 #### 计数二进制子串 (稍微有点复杂, 闲麻烦的直接跳过)
给定一个字符串 s,计算具有相同数量0和1的非空(连续)子字符串的数量,并且这些子字符串中的所有0和所有1都是组合在一起的。
重复出现的子串要计算它们出现的次数。
示例1
输入: "00110011"
输出: 6
解释: 有6个子串具有相同数量的连续1和0:“0011”,“01”,“1100”,“10”,“0011” 和 “01”。
请注意,一些重复出现的子串要计算它们出现的次数。
另外,“00110011”不是有效的子串,因为所有的0(和1)没有组合在一起。

示例2
输入: "10101"
输出: 4
解释: 有4个子串:“10”,“01”,“10”,“01”,它们具有相同数量的连续1和0。
注意:s.length 在1到50,000之间。 s 只包含“0”或“1”字符。

规律图

leetcode刷题记录 js算法与数据结构 基础篇(上)_第2张图片
image.png

位运算符链接 不懂自己吸纳哦 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators

var countBinarySubstrings = function(s) {
    let matchFn = (str) => {
        // 找出一边相同的数字
        let startNum = str.match(/^(1+|0+)/)[0];
        // ^ 不懂看官方解释    对于每一个比特位,当两个操作数相应的比特位有且只有一个1时,结果为1,否则为0。
        let endNum = (startNum[0] ^ 1).toString().repeat(startNum.length);
        // startNum[0] 这里说一下 为什么要取[0], 因为 ^ 操作符比较的事 比特位
        // 比特(位):英文bit,是计算机晶体管的一种状态(通电与断电).就是0与1,真与假,是计算机最基本的传输单位.
        // 所以只取一位
        let target = `${startNum}${endNum}`
        // 注意 只能从字符串的最开始匹配  。
        // 或者用正则完成 let regs = new RegExp(`^(${startNum}${endNum})`); return regs.test(str) ? `${startNum}${endNum}` : ''; 但是如果数字(比特位太多 动态生成的正则会爆)
          if (str.slice(0, target.length) === target) {
            return `${startNum}${endNum}`;
        }
        return '';
    };

    let arr = [];
    for(i = 0; i < s.length - 1; i++) {
        let match = matchFn(s.slice(i));
        if (match) {
            arr.push(match);
        }
    }
    return arr.length;
};
countBinarySubstrings('00110011');

3#### 电话号码的字母组合
示例:
输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].

leetcode刷题记录 js算法与数据结构 基础篇(上)_第3张图片
1562230212545.jpg
var letterCombinations = function (digits) {
    // 存放数字对应的字母 
    let arr = ['', 1, 'abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz'];
    let newDigits = digits.split('');
    // 输入数字 所对应的字母  比如输入'23' => ['abc', 'def']
    let proveArr = newDigits.map(index => {
        return arr[index];
    });

    // 如果输入为'', 则返回[]
    if (!digits) {
        return [];
    }
    // 如果输入的是个位数 则直接返回
    else if (digits.length === 1) {
        return proveArr[0].toString().split('');   
    }
    else {
        let fn = proveArr => {
            let endResult = [];
            // 第三步 需要便利proveArr, 让每一个字母进行组合
            for (let i = 0; i < proveArr[0].length; i++) {
                for (let j = 0; j < proveArr[1].length; j++) {
                    endResult.push(`${proveArr[0][i]}${proveArr[1][j]}`);
                }
            }
            // 这一步很关键, 每次合并数组的前两组字母, 然后再递归  直到数组长度只剩下1
            proveArr.splice(0, 2, endResult);
            if (proveArr.length > 1) {
                fn(proveArr);
            }
            else {
                return endResult;
            }
            return proveArr[0];
        };
        return fn(proveArr);
    }  
};

4: #### 重复的子字符串
给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。
示例 1:
输入: "abab"
输出: True
解释: 可由子字符串 "ab" 重复两次构成。

示例 2:
输入: "aba"
输出: False

示例 3:
输入: "abcabcabcabc"
输出: True
解释: 可由子字符串 "abc" 重复四次构成。 (或者子字符串 "abcabc" 重复两次构成。)

var repeatedSubstringPattern = function(s) {
    // 正则一步到位, 补货括号, 不了解此正则用法的自己去百度 (下边贴图)
    var reg = /^(\w+)\1+$/;
    return reg.test(s);
};
WechatIMG4119.png

5: #### 按奇偶排序数组 II
给定一个非负整数数组 A, A 中一半整数是奇数,一半整数是偶数。
对数组进行排序,以便当 A[i] 为奇数时,i 也是奇数;当 A[i] 为偶数时, i 也是偶数。
你可以返回任何满足上述条件的数组作为答案

示例:
输入:[4,2,5,7]
输出:[4,5,2,7]
解释:[4,7,2,5],[2,5,4,7],[2,7,4,5] 也会被接受。

var sortArrayByParityII = function(arr) {
     arr = arr.sort((a, b) => a - b);
    let odd = 1;
    let event = 0;
    let r = [];
    arr.forEach(item => {
        if (item % 2 === 0) {
            r[event] = item;
            event += 2;
        } else {
            r[odd] = item;
            odd += 2;
        }
    })
    return r;
};

6: #### 种花问题
假设你有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花卉不能种植在相邻的地块上,它们会争夺水源,两者都会死去。
给定一个花坛(表示为一个数组包含0和1,其中0表示没种植花,1表示种植了花),和一个数 n 。能否在不打破种植规则的情况下种入 n 朵花?能则返回True,不能则返回False。

示例 1:
输入: flowerbed = [1,0,0,0,1], n = 1
输出: True

示例 2:
输入: flowerbed = [1,0,0,0,1], n = 2
输出: False

var canPlaceFlowers = function (flowerbed, n) {
    // 如果是n = 0, 或者 只有 flowerbed = [0]
    if (!n || (flowerbed.length === 1 && flowerbed[0] === 0)) {
        return true;
    }
    // 将所有的树坑 切为数组
    var arr = flowerbed.join().split(/,?1,?/);
    if (!arr) {
        return false;
    }
    var maxArr = 0;
    arr.forEach((item, ind) => {
        if (item.length) {
            maxArr += fn(item, ind);
        }
    });
   // 去查询数组里的每一组树坑做多可以种多少树
    function fn(value, ind) {
        var len = value.replace(/,/g, '').length;
        // 种最多树的 规律
        var maxNum = Math.round((len - 2) / 2);
        // 处理边界问题
        if ((ind === 0 || ind === arr.length - 1) && value.length >= 2) {
            return maxNum + 1;
        } else {
            return maxNum;
        }
    }
    return maxArr >= n;
};

排序类

7: 冒泡排序
冒泡排序算法运作原理:
1: 比较相邻的元素。如果第一个比第二个大,就交换他们两个
2: 针对所有的元素重复以上的步骤,除了最后一个。

// 外层循环控制右边界
for (let i = arr.length - 1, num; i > 0; i--) {
        // 内层循环控制左边界
        for (let j = 0; j < i; j++) {
            num = arr[j];
            if (num > arr[j + 1]) {
                arr[j] = arr[j + 1];
                arr[j + 1] = num;
            }
        }
    }

8: 选择排序
工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完

    for (let i = 0, num; i < arr.length; i++) {
            num = arr[i];
            for (let j = i + 1; j < arr.length; j++) {
                if (arr[j] < num) {
                    let prove = num;
                    num = arr[j];
                    arr[j] = prove;
                }
            }
            arr[i] = num;
        }

9: 快速排序
数组中制定一个元素作为标尺,然后跟数组每一项做对比, 比它大的放元素后边, 小的放它前边, 如此利用递归重复, 直至全部正序排列

快速排序 - 基础算法

缺点, 因为需要新开辟空间left right, 去存储元素, 所以导致不必要的空间损耗,造成空间复杂度增大

function quickSort(arr) {
    // 思路 从数组当中取出来一个值, 然后依次跟数组的每一项做对比, 小的放左边, 大的放右边
    // 目标元素
    let flag = arr[0];
    let left = [];
    let right = [];
    if (arr.length < 2) {
        return arr;
    } else {
        for (let i = 1, len = arr.length, tem; i < len; i++) {
            tem = arr[i];
            if (tem < flag) {
                left.push(tem);
            }
            else {
                right.push(tem);
            }
        };
        return [...quickSort(left), arr[0], ...quickSort(right)];
    }
};

快速排序 - 高级算法(in-place) 利用就地排序, 在原数组上进行换位操作

function inPlace(arr) {
    // 转换位置
    let swap = (arr, i, j) => {
        let tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    };
    // 找中间数下标 并且操作换位
    let findCenter = (arr, l, r) => {
        let flag = arr[l];
        // index 记载需要调换位置的下标
        let index = l + 1;
        for (let i = index; i <= r; i++) {
            if (arr[i] < flag) {
                // 如果找到比flag小的数字 就调换位置
                swap(arr, index, i);
                index++;
            }
        }
        swap(arr, l, index - 1);
        return index;
    };
    // 递归调用
    let sort = (arr, l, r) => {
        if (l < r) {
            // 中间位数的下标
            let cindex = findCenter(arr, l, r);
            sort(arr, l, cindex - 1);
            sort(arr, cindex, r);
        }
    };  
    sort(arr, 0, arr.length - 1);
    return arr;
}

9 : #### 最大间距
给定一个无序的数组,找出数组在排序之后,相邻元素之间最大的差值。
如果数组元素个数小于 2,则返回 0。

示例 1:
输入: [3,6,9,1]
输出: 3
解释: 排序后的数组是 [1,3,6,9], 其中相邻元素 (3,6) 和 (6,9) 之间都存在最大差值 3。

示例 2:
输入: [10]
输出: 0
解释: 数组元素个数小于 2,因此返回 0。

var maximumGap = function(arr) {
        if (arr.length === 1 || arr.length === 0) {
            return 0;
        }
        // 冒泡排序
        let len = arr.length - 1;
        let num = null;
        let space = 0;
        for (let i = len; i > 0; i--) {
            for (j = 0; j < i; j++) {
                if (arr[j] > arr[j+1]) {
                    num = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = num;
                }
            }
            let reduce = arr[j+1] - arr[j]
            if (reduce > space) {
                space = reduce;
            }
        }
        return Math.max(space, arr[1] - arr[0]);
    };

10: #### 缺失的第一个正数
给定一个未排序的整数数组,找出其中没有出现的最小的正整数。

示例 1:
输入: [1,2,0]
输出: 3

示例 2:
输入: [3,4,-1,1]
输出: 2

示例 3:
输入: [7,8,9,11,12]
输出: 1

// 方法1 直接使用sort排序  但会造成多余的循环 浪费性能
var firstMissingPositive = function (nums) {
    // 1 筛选出来 正整数
    let newArr = nums.filter(item => item > 0);
    // 2 排序 - 升序
    newArr.sort((a, b) => a - b);
    if (newArr[0] !== 1) {
        return 1;
    } else {
        for (let i = 0, len = newArr.length - 1; i < len; i++) {
            if (newArr[i + 1] - newArr[i] > 1) {
                return newArr[i] + 1;
            }
        }
        return newArr.pop() + 1;
    }
};

// 其实最优化的是我们在排序过程中就已经可以去找寻 差值, 所以接下来我们自己去写选择排序, 然后在排序内部, 这样便会减少一层循环
var firstMissingPositive = function (nums) {
    // 1 筛选出来 正整数
    let newArr = nums.filter(item => item > 0);
    // 2 排序 - 选择排序
    // newArr.sort((a, b) => a - b);
    let len = newArr.length;
    for (let i = 0, num; i < len; i++) {
        num = newArr[i];
        for (let j = i + 1; j < len; j++) {
            if (num > newArr[j]) {
                let c = num;
                num = newArr[j];
                newArr[j] = c;
            }
        }
        newArr[i] = num;
        // 判断i 是否大于 0
        if (i > 0) {
            if (newArr[i] - newArr[i - 1] > 1) {
                return newArr[i - 1] + 1;
            }
        } else {
            if (num === 0) { continue; }
            if (num !== 1) {
                return 1;
            }
        }
    }
    return newArr.length ? newArr.pop() + 1 : 1;
};

你可能感兴趣的:(leetcode刷题记录 js算法与数据结构 基础篇(上))