1.两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
你可以按任意顺序返回答案。

示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]

1.双重循环暴力求解

  1. function twoSum (nums: number[], target: number): number[] {
  2. const l:number = nums.length
  3. for (let i = 0; i <= l - 1; i++) {
  4. for (let j = i + 1; j < l; j++) {
  5. if (nums[i] + nums[j] === target) {
  6. return [i, j]
  7. }
  8. }
  9. }
  10. return []
  11. };
  12. twoSum([3, 2, 3], 6)

image.png
image.png
image.png
image.png

2.查找表法思路

因为是两数之和,所以返过来想的话, 利用哈希表的优势,就是我们先计算 目标值减去当前值,得到的结果,直接从哈希表中去取对应的下标,如果没有,就把当前值和当前下标存进去,下一个计算完成的进来匹配,就可以直接看,他需要的key,有没有对应的value,如果有就返回前面的值(值就是位置),和当前的位置(i)

function twoSum1 (nums: number[], target: number): number[] {
  const l: number = nums.length
  const numMap = new Map()
  for (let i = 0; i <= l - 1; i++) {
    if (numMap.get(target - nums[i]) !== undefined) {
      return [numMap.get(target - nums[i]), i]
    } else {
      numMap.set(nums[i], i)
    }
  }
  return []
};
twoSum1([3, 2, 3], 6)

image.png

2.两数相加

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
image.png

思路

1.正好使用初代数学的理念,正好链表顺序就是 个位十位到百位的,所以就可以从后往前加,然后遇到进位就+1
2.先初始一个头,就是当前个位相加后的取余数,然后重新计算carry,然后next 和自身都变成最新的ListNode

function addTwoNumbers (l1: ListNode | null, l2: ListNode | null): ListNode | null {
  // 初始化头和 下一个
  let head = null, tail = null
  // 初始化仅为
  let carry = 0
  循环 ,如果l1 或者 l2 有值都继续往下走
     while (l1 || l2) {
    const n1 = l1 && l1.val || 0
    const n2 = l2 && l2.val || 0
    // 计算出这次相加的值
    const sum = n1 + n2 + carry;
    // 如果没有头,新创建一个
    if (!head) {
      head = tail = new ListNode(sum % 10)
    } else {
      // 否则就改变的是接下来的
      tail.next = new ListNode(sum % 10)
      // 然后把接下来的也变化掉
      tail = tail.next
    }
    // 重置进位
    carry = Math.floor(sum / 10);
    // 继续往下找
    if (l1) { l1 = l1.next }
    if (l2) { l2 = l2.next }
  }
  // 如果计算完成了 l1,l2都没有了,但是进位里还有值,就需要再加一层新的
  if (carry > 0) {
    tail.next = new ListNode(carry)
  }
  // 返回最后的树    
  return head
};

image.png

3.在区间范围内统计奇数数目

给你两个非负整数 lowhigh 。请你返回 low high 之间(包括二者)奇数的数目。

示例 1:
输入:low = 3, high = 7
输出:3
解释:3 到 7 之间奇数数字为 [3,5,7]

function countOdds (low: number, high: number): number {
  let nums = 0
  for (let i = low; i <= high; i++) {
    if (i % 2 === 1) {
      nums++
    }
  }
  return nums
};

4. 唯一摩尔斯密码词

国际摩尔斯密码定义一种标准编码方式,将每个字母对应于一个由一系列点和短线组成的字符串, 比如: “a” 对应 “.-“, “b” 对应 “-…”, “c” 对应 “-.-.”, 等等。

为了方便,所有26个英文字母对应摩尔斯密码表如下:

[“.-“,”-…”,”-.-.”,”-..”,”.”,”..-.”,”—.”,”….”,”..”,”.—-“,”-.-“,”.-..”,”—“,”-.”,”—-“,”.—.”,”—.-“,”.-.”,”…”,”-“,”..-“,”…-“,”.—“,”-..-“,”-.—“,”—..”]
给定一个单词列表,每个单词可以写成每个字母对应摩尔斯密码的组合。例如,”cab” 可以写成 “-.-..—…”,(即 “-.-.” + “.-“ + “-…” 字符串的结合)。我们将这样一个连接过程称作单词翻译。

返回我们可以获得所有词不同单词翻译的数量。

例如:
输入: words = [“gin”, “zen”, “gig”, “msg”]
输出: 2
解释:
各单词翻译如下:
“gin” -> “—…-.”
“zen” -> “—…-.”
“gig” -> “—…—.”
“msg” -> “—…—.”

共有 2 种不同翻译, “—…-.” 和 “—…—.”.

思路

先创建一个 密码和字母的 映射关系,然后就是循环遍历 传入的字符数组,利用map自动去重的性质,达到去重目的,最后返回size

function uniqueMorseRepresentations (words: string[]): number {
  let init = initMap()
  let newMap = new Map()
  words.forEach((x) => {
    let str = ''
    for (let i of x) {
      str += init.get(i)
    }
    newMap.set(str, 1)
  })
  return newMap.size
};
function initMap () {//构建字典
  let charmap = new Map();
  let morse = [".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--.."]
  charmap.set("a", morse[0]);
  charmap.set("b", morse[1]);
  charmap.set("c", morse[2]);
  charmap.set("d", morse[3]);
  charmap.set("e", morse[4]);
  charmap.set("f", morse[5]);
  charmap.set("g", morse[6]);
  charmap.set("h", morse[7]);
  charmap.set("i", morse[8]);
  charmap.set("j", morse[9]);
  charmap.set("k", morse[10]);
  charmap.set("l", morse[11]);
  charmap.set("m", morse[12]);
  charmap.set("n", morse[13]);
  charmap.set("o", morse[14]);
  charmap.set("p", morse[15]);
  charmap.set("q", morse[16]);
  charmap.set("r", morse[17]);
  charmap.set("s", morse[18]);
  charmap.set("t", morse[19]);
  charmap.set("u", morse[20]);
  charmap.set("v", morse[21]);
  charmap.set("w", morse[22]);
  charmap.set("x", morse[23]);
  charmap.set("y", morse[24]);
  charmap.set("z", morse[25]);
  return charmap;
}

5. 独特的电子邮件地址

每封电子邮件都由一个本地名称和一个域名组成,以 @ 符号分隔。

例如,在 alice@leetcode.com中, alice 是本地名称,而 leetcode.com 是域名。

除了小写字母,这些电子邮件还可能包含 ‘.’ 或 ‘+’。

如果在电子邮件地址的本地名称部分中的某些字符之间添加句点(’.’),则发往那里的邮件将会转发到本地名称中没有点的同一地址。例如,”alice.z@leetcode.com” 和 “alicez@leetcode.com” 会转发到同一电子邮件地址。 (请注意,此规则不适用于域名。)

如果在本地名称中添加加号(’+’),则会忽略第一个加号后面的所有内容。这允许过滤某些电子邮件,例如 m.y+name@email.com 将转发到 my@email.com。 (同样,此规则不适用于域名。)

可以同时使用这两个规则。

给定电子邮件列表 emails,我们会向列表中的每个地址发送一封电子邮件。实际收到邮件的不同地址有多少?
示例:
输入:[“test.email+alex@leetcode.com”,”test.e.mail+bob.cathy@leetcode.com”,”testemail+david@lee.tcode.com”]
输出:2
解释:实际收到邮件的是 “testemail@leetcode.com” 和 “testemail@lee.tcode.com”。

思路

想要在别人的地盘混好,首先要熟悉别人的规则:

  1. 有邮件地址诸如 aaaa@qq.com
  2. @ 符号是关键的分割符号;
  3. @ 符号前面的 . 符号会被忽略;
  4. @ 符号前面如果有 + 符号,则 + 符号到 @ 符号之间的字符串会被过滤掉;
  5. 拿到给定的邮件地址列表,计算出一共有多少实际上是不同的地址~

解读完毕,立马出来答案:

function numUniqueEmails(emails: string[]): number {
    return new Set(emails.map((x)=>{
        let sliceArr = x.split('@')
        return sliceArr[0].split('+')[0].split('.').join('') +'@' +sliceArr[1]
    })).size
};

6. 检查数组是否经排序和轮转得到

给你一个数组 numsnums 的源数组中,所有元素与 nums 相同,但按非递减顺序排列。
如果 nums 能够由源数组轮转若干位置(包括 0 个位置)得到,则返回 true ;否则,返回 false
源数组中可能存在 重复项
注意:我们称数组 A 在轮转 x 个位置后得到长度相同的数组 B ,当它们满足 A[i] == B[(i+x) % A.length] ,其中 % 为取余运算。

示例 1:
输入:nums = [3,4,5,1,2]
输出:true
解释:[1,2,3,4,5] 为有序的源数组。
可以轮转 x = 3 个位置,使新数组从值为 3 的元素开始:[3,4,5,1,2] 。

示例 2:
输入:nums = [2,1,3,4]
输出:false
解释:源数组无法经轮转得到 nums 。

示例 3:
输入:nums = [1,2,3]
输出:true
解释:[1,2,3] 为有序的源数组。
可以轮转 x = 0 个位置(即不轮转)得到 nums 。

示例 4:
输入:nums = [1,1,1]
输出:true
解释:[1,1,1] 为有序的源数组。
轮转任意个位置都可以得到 nums 。

示例 5:
输入:nums = [2,1]
输出:true
解释:[1,2] 为有序的源数组。
可以轮转 x = 5 个位置,使新数组从值为 2 的元素开始:[2,1] 。

思路

1.如果开始小于结束,且只出现了一次 12345, 12453,12354,13245,13452,23415,开始小于结束的话,其实就已经不可以轮换了,所以我们只需要考虑开始大于结束的
2.如果开始大于结束,那么有且只有一次是非递增的 ,就在当前节点可以轮换,如果有两次就肯定是不可以了,
也就是说如果刚开始就是12345,开关就是false ,如果遍历到中间又发现一个值前面大于后面,那肯定就是不可以轮换的,如果第一个值大于了第二个值,就在遍历到中间第一次出现前一个值大于后一个值记录下来,第二次又有的话就可以直接输出了,就不可以轮换
image.png

function check (nums: number[]): boolean {
  let flag: boolean = nums[0] >= nums[nums.length - 1]
  for (let i = 1; i < nums.length; i++) {
    if (nums[i] < nums[i - 1]) {
      if (flag) {
        flag = false
      } else {
        return false
      }
    }
  }
  return true
};

7. 左叶子之和

计算给定二叉树的所有左叶子之和。

示例:

3<br />   / \<br />  9  20<br />    /  \<br />   15   7

在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24

思路

1.叶子节点,也就是完全没有子节点的,也就是没有左节点也没有右节点
2.使用递归要增加 结束条件,如果没有当前节点就返回 0
3.要把所有res相加

/**
 * Definition for a binary tree node.
 * class TreeNode {
 *     val: number
 *     left: TreeNode | null
 *     right: TreeNode | null
 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.left = (left===undefined ? null : left)
 *         this.right = (right===undefined ? null : right)
 *     }
 * }
 */

function sumOfLeftLeaves(root: TreeNode | null ): number {
    if(!root) return 0;
    let res = 0
    if(root.left && root.left.left == null && root.left.right == null ){
        res += root.left.val
    }
    return  sumOfLeftLeaves(root.left) +   sumOfLeftLeaves(root.right) + res
};