2022年9月的力扣每日一题...

【LeetCode】9月 每日一题

9.1

题目

1475. 商品折扣后的最终价格

思路

模拟即可

代码

function finalPrices(prices: number[]): number[] {
    let len: number = prices.length
    let res: number[] = new Array(len)
    for (let i = 0; i < len - 1; i++) {
        let discount: number = 0
        let price: number = prices[i]
        for (let j = i + 1; j < len; j++) {
            if (price >= prices[j]) {
                discount =prices[j]
                break
            }
        }
        res[i] = price - discount
    }
    res[len - 1] = prices[len - 1]
    return res
}

9.2

题目

687. 最长同值路径

思路

dfs即可,但是是在dfs的过程中找到最大值,dfs的返回值是左右选择后的最长路径,最终需要返回的结果是左右所有的路径加起来

代码

/**
 * 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 longestUnivaluePath(root: TreeNode | null): number {
    let res = 0
    const find = (node: TreeNode) => {
        if (node == null) return 0
        let l = find(node.left), r = find(node.right)
        let al = 0, ar = 0
        if (node.left && node.left.val == node.val)  al = l + 1
        if (node.right && node.right.val == node.val) ar = r +  1
        res = Math.max(res, al + ar)
        return Math.max(al, ar)
    }
    find(root)
    return res
}

9.3

题目

646. 最长数对链

思路

dp思想,先排序,按照头一个元素升序,如果头一个元素相同,就按照后一个减去前一个的顺序

然后就是dp[i]表示0到1的最长数对链的长度

每次遍历选取当前遍历坐标前面的最长的长度,如果当前这个可以加入链中,dp[i]+1

代码

function findLongestChain(pairs: number[][]): number {
    let len = pairs.length
    if (len == 1) return 1
    pairs.sort((a,b) => a[0] == b[0] ? b[1] - a[0] : a[0] - b[0])
    let dp = new Array(len).fill(0)
    dp[0] = 1
    let res = -Infinity
    for (let i = 1; i < len; i++) {
        let tmp = 0
        for (let j = 0; j < i; j++) {
            if (pairs[i][0] > pairs[j][1] && pairs[i][1] > pairs[j][1]) {
                tmp = Math.max(dp[j], tmp)
            }
            dp[i] = tmp + 1
            res = Math.max(res, dp[i])
        }
    }
    return res
}

9.4

题目

1582. 二进制矩阵中的特殊位置

思路

暴力梭哈遍历

代码

function numSpecial(mat: number[][]): number {
    const rows: number = mat.length
    const cols: number = mat[0].length
    let res: number = 0
    const isSpecial: Function = (x: number, y: number): boolean => {
        if (mat[x][y] == 0) return false
        for (let col = 0; col < cols; col++) {
            if (col == y) continue
            if (mat[x][col] != 0) return false
        }
        for (let row = 0; row < rows; row++) {
            if (row == x) continue
            if (mat[row][y] != 0) return false
        }
        return true
    }
    for (let i = 0; i < rows; i++) {
        for (let j = 0; j < cols; j++) {
            if (isSpecial(i, j)) res++
        }
    }
    return res
}

9.5

题目

652. 寻找重复的子树

思路

众所周知我不会dfs,只能用bfs混混日子这样子(主要是脑容量太小了无法用程序的逻辑在脑子里模拟一个个stack,只能用que的空间换脑容量

这题其实思路很简单,hash+serialization,记录每次序列化后的结果,比较是否出现过,出现过就加入最后的返回结果中

但是因为我是使用bfs,第一次直接malloc空间不足了,给我整不会了

后来想明白了,bfs必然过不了,向左右扩展肯定是会挂的,因为每次都会重复计算大量相同的数据

还是得回归dfs,挖到最deep的一层,从下向上serialization,这样就不会重复计算,下方计算好的结果str可以交给上方重复利用

代码

第一版wa的bfs代码,内存不够用

/**
 * 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)
 *     }
 * }
 */

type Tree = TreeNode | null
function findDuplicateSubtrees(root: TreeNode | null): Array<TreeNode | null> {
    // 前序遍历进行序列化
    const frontOrder = (node: TreeNode) => {
        let res: string = ""
        let stack: Tree[] = [node]
        while (stack.length) {
            let cur: Tree = stack.pop()
            if (cur == null) res += "#"
            else res += cur.val
            res += ','
            if (cur != null) {
                stack.push(cur.right)
                stack.push(cur.left)
            }
        }
        return res
    }
    let stack: Tree[] = [root]
    let map: Map<string, number> = new Map()
    let res: Tree[] = []
    // 前序遍历对比序列化结果
    while (stack.length) {
        let node: Tree = stack.pop()
        let serial = frontOrder(node)
        // console.log(set, serial)
        if (map.get(serial) == undefined) {
            map.set(serial, 1)
        } else if (map.get(serial) == 1){  // 出现过一次就加入结果
            res.push(node)
            map.set(serial, map.get(serial) + 1)
        } else {
            map.set(serial, map.get(serial) + 1)
        }
        if (node.right) stack.push(node.right)
        if (node.left) stack.push(node.left) 
    }
    return res
}

执行执行几个运行示例是完全没压力的,看了一眼数据范围,10的四次方,必然超内存

image-20220905152526021

于是使用dfs的版本来了

/**
 * 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)
 *     }
 * }
 */

type Tree = TreeNode | null
function findDuplicateSubtrees(root: TreeNode | null): Array<TreeNode | null> {
    const map: Map<string, number> = new Map() // 记录出现的序列化的结果的次数
    const res: Tree[] = []
    const frontOrder = (node: Tree) => {
        if (node == null) return "#"
        let serializtion = node.val + "," + frontOrder(node.left) + "," +frontOrder(node.right)
        if (map.get(serializtion) == undefined) {
            map.set(serializtion, 1)
        } else if (map.get(serializtion) == 1) {
            res.push(node)
            map.set(serializtion, 2)
        } else {
            map.set(serializtion, map.get(serializtion) + 1)
        }
        return serializtion
    }
    frontOrder(root)
    return res
}

这题真的思路一下子打开了,以前总以为dfs和bfs在实际应用可以互相转换,但似乎这题不用dfs真滴很难使用bfs来进行树的向下模拟

image-20220905152909053

顺带一提,使用c++版本的dfs还是可以勉强过的,估计是语言特性,typescript这种封装类型的语言就是比不过原生类型的,为了这题我甚至专门写了一个题解

image.png

9.6

题目

828. 统计子串中的唯一字符

思路

典中典奥数题,还是得看题解

对每一个字符i,向前找到相同的字符j,向后找到相同的字符k。当前字符对最终结果的贡献是:(i-j)*(k-i)。

这相当于两种方案的拼接:在字符串A(j到i)当中,字符i贡献的次数是(i-j)次。在字符串B(k-i)当中,字符i贡献的次数是(k-i)。那么当两者拼接的时候,字符i对子串(j到k)的贡献就是两种方案的乘积(符合乘法公式)。

遍历两次,分别得到每个字符最左边出现和最右边出现的位置,结果两个距离相乘

代码

function uniqueLetterString(s: string): number {
    const len: number = s.length
    const l: number[] = new Array(len)
    const r: number[] = new Array(len)
    const cnts: number[] = new Array(26).fill(-1)
    for (let i = 0; i < len; i++) {
        let tmp: number = s[i].charCodeAt(0) - 'A'.charCodeAt(0)
        l[i] = cnts[tmp]
        cnts[tmp] = i
    }
    cnts.fill(len)
    for (let i = len - 1; i >= 0; i--) {
        let tmp: number = s[i].charCodeAt(0) - 'A'.charCodeAt(0)
        r[i] = cnts[tmp]
        cnts[tmp] = i
    }
    let res: number = 0
    for (let i = 0; i < len; i++) {
        console.log(l[i], i, r[i])
        res += (i - l[i]) * (r[i] - i)
    }
    return res
}

9.7

题目

1592. 重新排列单词间的空格

思路

纯纯的模拟,有啥好思路

好思路就是我用python

代码

class Solution:
    def reorderSpaces(self, text: str) -> str:
        space_cnt = 0
        words = []
        word = ''
        for i in range(len(text)):
            if text[i] == ' ':
                space_cnt += 1
                if word != '':
                    words.append(word)
                    word = ''
            else:
                word += text[i]
            if i == len(text) - 1 and word != '':
                words.append(word)
        words_cnt = len(words)
        if (words_cnt == 1):
            return words[0] + " " * space_cnt
        average_spaec_cnt = space_cnt // (words_cnt - 1)
        ramain_space_cnt = space_cnt  % (words_cnt - 1)
        res = (" " * average_spaec_cnt).join(words) + " " * ramain_space_cnt
        return res

9.8

题目

667. 优美的排列 II

思路

由于最近回溯写得多,以看这种题目又以为是回溯,直接全排列的dfs+剪枝,以看数据范围1e4,直接wa

最后还是得去找规律

当n = 50k = 20[1,21,2,20,3,19,4,18,5,17,6,16,7,15,8,14,9,13,10,12,11,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50]
当n = 50k = 17[1,18,2,17,3,16,4,15,5,14,6,13,7,12,8,11,9,10,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50]
当n = 80k = 30[1,31,2,30,3,29,4,28,5,27,6,26,7,25,8,24,9,23,10,22,11,21,12,20,13,19,14,18,15,17,16,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80]
是不是发现了规律就是
下标从[0, k]偶数下标填充[1,2,3…],奇数下标填充[k + 1, k, k - 1…],后面[k + 1, n - 1]都是顺序填充

代码

失败的dfs

function constructArray(n: number, k: number): number[] {
    // n个数
    const used = new Array(n+1).fill(false)
    used[0] = true
    let res: number[]
    const set: Set<number> = new Set()
    const dfs = (num: number, arr: number[]) => {
        if (num > n && set.size == k+1 && arr.length == n) {
            console.log(arr, set, num)
            res = [...arr]
            return true
        }
        for (let i = 1; i <= n; i++) {
            const sub = Math.abs(arr[arr.length - 1] - i)
            if (!used[i]) {
                set.add(sub)
                used[i] = true
                arr.push(i)
                if (dfs(i+1, arr)) return true
                set.delete(sub)
                used[i] = false
                arr.pop()
            }
        }
        return false
    }
    dfs(1, [])
    return res
}

找规律

function constructArray(n: number, k: number): number[] {
    const res: number[] = new Array(n)
    let tmp: number = 1, retmp: number = k + 1
    for (let i = 0; i <= k; i+=2) {
        res[i] = tmp++
    }
    for (let i = 1; i <= k; i+=2) {
        res[i] = retmp--
    }
    for (let i = k + 1; i < n; i++) {
        res[i] = i + 1
    }
    return res
}

9.9

题目

1598. 文件夹操作日志搜集器

思路

模拟遍历判断即可

代码

function minOperations(logs: string[]): number {
    let res: number = 0
    for (const log of logs) {
        if (log.charAt(0) != '.') {
            res++
        } else if (log.charAt(1) == '.') {
            if (res > 0) res--
        }
    }
    return res
}

9.10

题目

669. 修剪二叉搜索树

思路

递归或者迭代,递归就是如果遇到小于low的,就去修剪右树,如果大于high,就去修剪左树

迭代一样的思路,只不过得用while两

代码

递归版本

function trimBST(root: TreeNode | null, low: number, high: number): TreeNode | null {
    if (!root) return null
    if (root.val < low) return trimBST(root.right, low, high)
    if (root.val > high) return trimBST(root.left, low, high)
    root.left = trimBST(root.left, low, high)
    root.right = trimBST(root.right, low, high)
    return root
}

迭代版本

function trimBST(root: TreeNode | null, low: number, high: number): TreeNode | null {
    if (!root) return null
    // 先去找到[low, high] 区间的根节点位置
    while (root && (root.val < low || root.val > high)) {
        if (root.val < low) root = root.right
        else root = root.left
    }
    // 修剪左树
    let cur : TreeNode | null = root
    while (cur) {
        while (cur.left && cur.left.val < low) {
            cur.left = cur.left.right
        }
        cur = cur.left
    }
    // 修剪右树
    cur = root
    while (cur) {
        while (cur.right && cur.right.val > high) {
            cur.right = cur.right.left
        }
        cur = cur.right
    }
    // 返回根节点
    return root
}

9.11

题目

857. 雇佣 K 名工人的最低成本

思路

看了题解的贪心才写的,将所有员工按性价比排序,同时建立一个大根堆用来存放员工的价值

遍历所有的按照性价比排序后的员工,计算每次的钱,确保在k个范围之内,得到最少的钱就是结果

ps:typescrit和JavaScript在leetcode上的支持感觉我还是不太习惯,根本永不会他给的优先队列,直接网上抄了一份来

代码

function mincostToHireWorkers(quality: number[], wage: number[], k: number): number {
    const map: number[][] = []
    const len = quality.length
    for (let i = 0; i < len; i++) {
        map.push([wage[i] / quality[i], quality[i]])  // 工人对应的性价比和质量
    }   

    map.sort((a, b) => a[0] - b[0]) // 按照性价比排序
    //@ts-ignore
    const heap = new PriorityQueue6<number>((a, b) => a - b)
    let qsum: number = 0
    let res: number =  Infinity
    for (let i = 0; i < map.length; i++) {
        const val = map[i]
        qsum += val[1]
        heap.enqueue(-val[1])
        if (i >= k-1) {
            res = Math.min(res, qsum * val[0])
            qsum += heap.dequeue()
        }
    }
    return res
}

//@ts-ignore
var e,t,r,i=Object.create,s=Object.defineProperty,a=Object.getOwnPropertyDescriptor,o=Object.getOwnPropertyNames,h=Object.getPrototypeOf,n=Object.prototype.hasOwnProperty,p=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),u=p((e=>{var t=class{constructor(e,t,r){if("function"!=typeof e)throw new Error("Heap constructor expects a compare function");this._compare=e,this._nodes=Array.isArray(t)?t:[],this._leaf=r||null}_hasLeftChild(e){return 2*e+1<this.size()}_hasRightChild(e){return 2*e+2<this.size()}_compareAt(e,t){return this._compare(this._nodes[e],this._nodes[t])}_swap(e,t){let r=this._nodes[e];this._nodes[e]=this._nodes[t],this._nodes[t]=r}_shouldSwap(e,t){return!(e<0||e>=this.size()||t<0||t>=this.size())&&this._compareAt(e,t)>0}_compareChildrenOf(e){if(!this._hasLeftChild(e)&&!this._hasRightChild(e))return-1;let t=2*e+1,r=2*e+2;return this._hasLeftChild(e)?this._hasRightChild(e)&&this._compareAt(t,r)>0?r:t:r}_compareChildrenBefore(e,t,r){return this._compareAt(r,t)<=0&&r<e?r:t}_heapifyUp(e){let t=e,r=Math.floor((t-1)/2);for(;this._shouldSwap(r,t);)this._swap(r,t),t=r,r=Math.floor((t-1)/2)}_heapifyDown(e){let t=e,r=this._compareChildrenOf(t);for(;this._shouldSwap(t,r);)this._swap(t,r),t=r,r=this._compareChildrenOf(t)}_heapifyDownUntil(e){let t,r=0,i=1,s=2;for(;i<e;)t=this._compareChildrenBefore(e,i,s),this._shouldSwap(r,t)&&this._swap(r,t),r=t,i=2*r+1,s=2*r+2}insert(e){return this._nodes.push(e),this._heapifyUp(this.size()-1),(null===this._leaf||this._compare(e,this._leaf)>0)&&(this._leaf=e),this}push(e){return this.insert(e)}extractRoot(){if(this.isEmpty())return null;let e=this.root();return this._nodes[0]=this._nodes[this.size()-1],this._nodes.pop(),this._heapifyDown(0),e===this._leaf&&(this._leaf=this.root()),e}pop(){return this.extractRoot()}sort(){for(let e=this.size()-1;e>0;e-=1)this._swap(0,e),this._heapifyDownUntil(e);return this._nodes}fix(){for(let e=Math.floor(this.size()/2)-1;e>=0;e-=1)this._heapifyDown(e);return this}isValid(){let e=t=>{let r=!0,i=!0;if(this._hasLeftChild(t)){let i=2*t+1;if(this._compareAt(t,i)>0)return!1;r=e(i)}if(this._hasRightChild(t)){let r=2*t+2;if(this._compareAt(t,r)>0)return!1;i=e(r)}return r&&i};return e(0)}clone(){return new t(this._compare,this._nodes.slice(),this._leaf)}root(){return this.isEmpty()?null:this._nodes[0]}top(){return this.root()}leaf(){return this._leaf}size(){return this._nodes.length}isEmpty(){return 0===this.size()}clear(){this._nodes=[],this._leaf=null}static heapify(e,r){if(!Array.isArray(e))throw new Error("Heap.heapify expects an array of values");if("function"!=typeof r)throw new Error("Heap.heapify expects a compare function");return new t(r,e).fix()}static isHeapified(e,r){return new t(r,e).isValid()}};e.Heap=t})),f=p((e=>{var{Heap:t}=u(),r=e=>(t,r)=>("function"==typeof e?e(t):t)<("function"==typeof e?e(r):r)?-1:1,i=class{constructor(e,i){this._getCompareValue=e,this._heap=i||new t(r(e))}insert(e){return this._heap.insert(e)}push(e){return this.insert(e)}extractRoot(){return this._heap.extractRoot()}pop(){return this.extractRoot()}sort(){return this._heap.sort()}fix(){return this._heap.fix()}isValid(){return this._heap.isValid()}root(){return this._heap.root()}top(){return this.root()}leaf(){return this._heap.leaf()}size(){return this._heap.size()}isEmpty(){return this._heap.isEmpty()}clear(){this._heap.clear()}clone(){return new i(this._getCompareValue,this._heap.clone())}static heapify(e,s){if(!Array.isArray(e))throw new Error("MinHeap.heapify expects an array");let a=new t(r(s),e);return new i(s,a).fix()}static isHeapified(e,s){let a=new t(r(s),e);return new i(s,a).isValid()}};e.MinHeap=i})),l=p((e=>{var{Heap:t}=u(),r=e=>(t,r)=>("function"==typeof e?e(t):t)<("function"==typeof e?e(r):r)?1:-1,i=class{constructor(e,i){this._getCompareValue=e,this._heap=i||new t(r(e))}insert(e){return this._heap.insert(e)}push(e){return this.insert(e)}extractRoot(){return this._heap.extractRoot()}pop(){return this.extractRoot()}sort(){return this._heap.sort()}fix(){return this._heap.fix()}isValid(){return this._heap.isValid()}root(){return this._heap.root()}top(){return this.root()}leaf(){return this._heap.leaf()}size(){return this._heap.size()}isEmpty(){return this._heap.isEmpty()}clear(){this._heap.clear()}clone(){return new i(this._getCompareValue,this._heap.clone())}static heapify(e,s){if(!Array.isArray(e))throw new Error("MaxHeap.heapify expects an array");let a=new t(r(s),e);return new i(s,a).fix()}static isHeapified(e,s){let a=new t(r(s),e);return new i(s,a).isValid()}};e.MaxHeap=i})),_=p((e=>{var{Heap:t}=u(),{MinHeap:r}=f(),{MaxHeap:i}=l();e.Heap=t,e.MinHeap=r,e.MaxHeap=i})),c=(e=_(),r=null!=e?i(h(e)):{},((e,t,r,i)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let h of o(t))!n.call(e,h)&&h!==r&&s(e,h,{get:()=>t[h],enumerable:!(i=a(t,h))||i.enumerable});return e})(!t&&e&&e.__esModule?r:s(r,"default",{value:e,enumerable:!0}),e)),{Heap:y,MinHeap:d,MaxHeap:w}=c,{default:m,...x}=c,H=void 0!==m?m:x,O=Object.create,b=Object.defineProperty,g=Object.getOwnPropertyDescriptor,C=Object.getOwnPropertyNames,z=Object.getPrototypeOf,A=Object.prototype.hasOwnProperty;(e=>{"undefined"!=typeof require?require:"undefined"!=typeof Proxy&&new Proxy(e,{get:(e,t)=>("undefined"!=typeof require?require:e)[t]})})((function(e){if("undefined"!=typeof require)return require.apply(this,arguments);throw new Error('Dynamic require of "'+e+'" is not supported')}));var E=((e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports))((e=>{var{Heap:t}=H,r=class{constructor(e,r){if("function"!=typeof e)throw new Error("PriorityQueue constructor expects a compare function");this._heap=new t(e,r),r&&this._heap.fix()}front(){return this._heap.root()}back(){return this._heap.leaf()}enqueue(e){return this._heap.insert(e)}push(e){return this.enqueue(e)}dequeue(){return this._heap.extractRoot()}pop(){return this.dequeue()}size(){return this._heap.size()}isEmpty(){return this._heap.isEmpty()}clear(){this._heap.clear()}toArray(){return this._heap.clone().sort().reverse()}static fromArray(e,t){return new r(t,e)}};e.PriorityQueue=r})),P=((e,t,r)=>(r=null!=e?O(z(e)):{},((e,t,r,i)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let s of C(t))!A.call(e,s)&&s!==r&&b(e,s,{get:()=>t[s],enumerable:!(i=g(t,s))||i.enumerable});return e})(!t&&e&&e.__esModule?r:b(r,"default",{value:e,enumerable:!0}),e)))(E()),{PriorityQueue:M}=P,{default:j,...v}=P,R=void 0!==j?j:v;
const PriorityQueue6=M

9.12

题目

1608. 特殊数组的特征值

思路

直接暴力,两个for去计算

代码

function specialArray(nums: number[]): number {
    for (let i = 0; i <= nums.length; i++) {
        let cnt = 0
        for (let j = 0; j < nums.length; j++) {
            if (nums[j] >= i) cnt++
        }
        if (cnt == i) return i
    }
    return -1
}

9.13

题目

670. 最大交换

思路

暴力回溯

代码

function maximumSwap(num: number): number {
    let max: number = num
    const strs: string[] = num.toString().split("")

    const swap = (arr: string[], i: number, j: number) => {
        const tmp: string = arr[i]
        arr[i] = arr[j]
        arr[j] = tmp
    }

    const flashback = (arr: string[]) => {
        for (let i = 0; i < strs.length; i++) {
            for (let j = 0; j < strs.length; j++) {
                if (i != j) {
                    swap(arr, i, j)
                    max = Math.max(parseInt(arr.join("")), max)
                    swap(arr, i ,j)
                }
            }
        }
    }
    flashback(strs)
    return max
}

9.14

题目

1619. 删除某些元素后的数组均值

思路

暴力有啥好说的,简单题简单做

代码

function trimMean(arr: number[]): number {
    arr.sort((a,b) => a-b)
    const len: number = arr.length
    const remove_len = Math.floor(len * 0.05)
    for (let _ = 0; _ < remove_len; _++) {
        arr.shift()
        arr.pop()
    }
    let res: number = 0
    arr.forEach(it => res += it)
    return res / arr.length
}

9.15

题目

672. 灯泡开关 Ⅱ

思路

看了下题解,十分巧妙,最终只要看前三个灯的状态就行了,其余少于三个灯的情况直接硬枚举

代码

function flipLights(n: number, presses: number): number {
    if (presses == 0) return 1
    if (n == 1) return 2
    if (n == 2) return presses == 1 ? 3 : 4
    return presses > 2 ? 8 : presses == 1 ? 4 :7
}

9.16

题目

850. 矩形面积 II

思路

完全没思路,先码个题解

代码

cv题解的,到时候再看

class Solution {
    public int rectangleArea(int[][] rectangles) {
        final int MOD = 1000000007;
        int n = rectangles.length;
        Set<Integer> set = new HashSet<Integer>();
        for (int[] rect : rectangles) {
            // 下边界
            set.add(rect[1]);
            // 上边界
            set.add(rect[3]);
        }
        List<Integer> hbound = new ArrayList<Integer>(set);
        Collections.sort(hbound);
        int m = hbound.size();
        // 「思路与算法部分」的 length 数组并不需要显式地存储下来
        // length[i] 可以通过 hbound[i+1] - hbound[i] 得到
        int[] seg = new int[m - 1];

        List<int[]> sweep = new ArrayList<int[]>();
        for (int i = 0; i < n; ++i) {
            // 左边界
            sweep.add(new int[]{rectangles[i][0], i, 1});
            // 右边界
            sweep.add(new int[]{rectangles[i][2], i, -1});
        }
        Collections.sort(sweep, (a, b) -> {
            if (a[0] != b[0]) {
                return a[0] - b[0];
            } else if (a[1] != b[1]) {
                return a[1] - b[1];
            } else {
                return a[2] - b[2];
            }
        });

        long ans = 0;
        for (int i = 0; i < sweep.size(); ++i) {
            int j = i;
            while (j + 1 < sweep.size() && sweep.get(i)[0] == sweep.get(j + 1)[0]) {
                ++j;
            }
            if (j + 1 == sweep.size()) {
                break;
            }
            // 一次性地处理掉一批横坐标相同的左右边界
            for (int k = i; k <= j; ++k) {
                int[] arr = sweep.get(k);
                int idx = arr[1], diff = arr[2];
                int left = rectangles[idx][1], right = rectangles[idx][3];
                for (int x = 0; x < m - 1; ++x) {
                    if (left <= hbound.get(x) && hbound.get(x + 1) <= right) {
                        seg[x] += diff;
                    }
                }
            }
            int cover = 0;
            for (int k = 0; k < m - 1; ++k) {
                if (seg[k] > 0) {
                    cover += (hbound.get(k + 1) - hbound.get(k));
                }
            }
            ans += (long) cover * (sweep.get(j + 1)[0] - sweep.get(j)[0]);
            i = j;
        }
        return (int) (ans % MOD);
    }
}

9.17

题目

1624. 两个相同字符之间的最长子字符串

思路

我选择暴力,记录每个字符串出现的下标,遍历26个字母出现下标的最大差值即可

代码

function maxLengthBetweenEqualCharacters(s: string): number {
    const map: number[][] = new Array(26)
    for (let i = 0; i < 26; i++) map[i] = new Array()
    for (let i = 0; i < s.length; i++) {
        map[s.charCodeAt(i) - "a".charCodeAt(0)].push(i)
    }
    let res: number = -1
    map.forEach(it => {
        if (it.length >= 2) {
            res = Math.max(res, it[it.length-1] - it[0] - 1)
        }
    })
    return res
}

9.18

题目

827. 最大人工岛

思路

不是很困难的困难题,我这里用了回溯遍历的思想,耗时非常大,到时候再剪枝优化下吧

代码

function largestIsland(grid: number[][]): number {
    const len: number = grid.length
    const used: boolean[][] = new Array(len)
    for (let i = 0; i < len; i++) used[i] = new Array(len).fill(false)
    const dirs: number[][] = [[0,1],[1,0],[0,-1],[-1,0]]

    let res = -Infinity

    for (let i = 0; i < len; i++) {
        for (let j = 0; j < len; j++) {
            if (!used[i][j] && grid[i][j] == 0) {
                used[i][j] = true
                grid[i][j] = 1
                const que = [[i,j]]
                let cnt = 1
                const s: Set<number> = new Set()
                s.add(j*len+i)
                while (que.length) {
                    const [curX, curY] = que.pop()
                    // console.log("curX, curY", [curX, curY])
                    for (const [x,y] of dirs) {
                        const nextX = x + curX
                        const nextY = y + curY
                        // console.log("nextX, nextY", [nextX, nextY])
                        if (nextX < 0 || nextY < 0 || nextX >= len || nextY >= len) continue
                        if (grid[nextX][nextY] == 1 && !s.has(nextY*len+nextX)) {
                            // console.log("add --> ", [nextX, nextY])
                            que.push([nextX, nextY])
                            cnt++
                            s.add(nextY*len+nextX)
                        }
                    }
                }
                res = Math.max(res, cnt)
                grid[i][j] = 0
                used[i][j] = false
            }
        }
    }

    return res == -Infinity ? len*len : res
}

9.19

题目

1636. 按照频率将数组升序排序

思路

直接一个map存放频率,然后排序

代码

function frequencySort(nums: number[]): number[] {
    nums.sort((a,b) => b - a)
    const map: Map<number, number> = new Map()
    nums.forEach(it => {
        if (map.has(it)) map.set(it, map.get(it)+1)
        else map.set(it, 1)
    })
    const arr = Array.from(map)
    const res = []
    // console.log(arr)
    arr.sort((a,b) => a[1] - b[1])
    arr.forEach(([num,cnt]) => {
        for (let _ = 0; _ < cnt; _++) res.push(num)
    })
    return res
}

9.20

题目

698. 划分为k个相等的子集

思路

能用回溯就用回溯解决,直接剪枝i > 0 && bkts[i] == bkts[i-1],如果两个桶内的数据是一样的,就跳过

代码

function canPartitionKSubsets(nums: number[], k: number): boolean {
    const len: number = nums.length
    if (len < k) return false
    let sum: number = 0
    nums.forEach(it => sum += it)
    if (sum % k != 0) return false
    let avg = Math.floor(sum / k)
    // console.log(avg)
    nums.sort((a, b) => a-b) 
    let res: boolean = false
    const flashback = (idx: number, bkts: number[]) => {
        if (idx == nums.length) {
            console.log(res)
            res = true
            return
        }
        const num = nums[idx]
        for (let i = 0; i < bkts.length; i++) {
            if (bkts[i] + num <= avg) {
                if (i > 0 && bkts[i] == bkts[i-1]) continue
                bkts[i] += num
                flashback(idx + 1, bkts)
                bkts[i] -= num
            }
        }
    }

    flashback(0,new Array(k).fill(0))
    return res
}

9.21

题目

854. 相似度为 K 的字符串

思路

piao来的思路,遇到困难题,直接睡大觉

bfs

1、首先BFS过程中存下每一种中间状态,因为第一次搜到肯定是到达的最小步数,重复搜索直接跳过;

2、进行字符串之间的状态转移的时候,如果某一位已经和最终状态相同了,那么这位就不要动它,因为动了只会使得步数增加,不是最优解;

3、同样的,每次交换,至少要是使得中间状态的某一位变成最终状态的字符,否则这一步就白交换了,得到的也一定不是最小次数;

4、每一步只考虑交换一位,并且要寻找所有的这一位后边可以交的位置拓展出一些新的中间状态

代码

class Solution {
    public int kSimilarity(String s1, String s2) {
        int n=s1.length();
        char c[]=s2.toCharArray();
        Map<String,Integer> map=new HashMap<>();
        List<String> list=new ArrayList<>();
        list.add(s1);
        map.put(s1,0);
        for(int i=0;i<list.size();i++){
           String s=list.get(i);
            char c1[]=s.toCharArray();
            int a=map.get(s);
            for(int j=0;j<n;j++){
                if(c1[j]!=c[j]){
                    for(int k=j+1;k<n;k++){
                        if(c1[k]==c[j]&&c1[k]!=c[k]){
                            exchange(c1,j,k);
                            String t=new String(c1);
                            if(t.equals(s2)){return 1+a;}
                            if(!map.containsKey(t)){
                                list.add(t);
                                map.put(t,a+1);
                            }
                            exchange(c1,j,k);
                        }
                    }
                    break;
                }
            }
        }
        return 0;
    }
    void exchange(char c[],int x,int y){
        char ch=c[x];
        c[x]=c[y];
        c[y]=ch;
    }
}

9.22

题目

1640. 能否连接形成数组

思路

模拟,每次找到contain的,然后模拟连接,最后判断连个给字符是否相等

代码

function canFormArray(arr: number[], pieces: number[][]): boolean {
    let pr = []
    for(let i=0; i<arr.length; i++){
        let a1 = arr[i]
        for(let j=0; j<pieces.length; j++) {
            let ps = pieces[j].toString()
            if(pieces[j][0] === a1 && !pr.includes(ps)) pr.push(ps)
        }
    }
    return arr.toString() === pr.join(',')
};

9.23

题目

707. 设计链表

思路

数据结构题,看到链表直接用Java来写

代码

class Node {
    public Node next;
    public int val;
    public Node() {
    }
    
    public Node(int val, Node next) {
        this.val = val;
        this.next = next;
    } 

    public Node(int val) {
        this.val = val;
        this.next = null;
    }

    public String toString() {
        return this.val+"";
    }
}

class MyLinkedList {
    private Node head;
    private int len;

    public MyLinkedList() {
        this.head = new Node();
        this.len = 0;
    }
    
    public int get(int index) {
        // test
        // Node t = this.head;
        // while (t != null) {
        //     System.out.println("测试 --> " + t.val);
        //     t = t.next;
        // }
        if (len == 0) return -1;
        if (index >= len) return -1;
        Node tmp = this.head.next;
        for (int i = 0; i < index; i++) {
            tmp = tmp.next;
        }
        return tmp.val;
    }
    
    public void addAtHead(int val) {
        this.len++;
        Node tmp = new Node(val);
        tmp.next = this.head.next;
        this.head.next = tmp;
    }
    
    public void addAtTail(int val) {
        this.len++;
        Node tmp = this.head;
        while (tmp.next != null) {
            tmp = tmp.next;
        }
        tmp.next = new Node(val);
    }
    
    public void addAtIndex(int index, int val) {
        if (index > len) return;
        this.len++;
        Node tmp = this.head;
        for (int i = 0; i < index; i++) {
            tmp = tmp.next;
        }
        tmp.next = new Node(val, tmp.next);
    }
    
    public void deleteAtIndex(int index) {
        if (!(index >= 0 && index < len)) return;
        this.len--;
        Node tmp = this.head;
        for (int i = 0; i < index; i++) {
            tmp = tmp.next;
        }
        tmp.next = tmp.next.next;
    }
}

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList obj = new MyLinkedList();
 * int param_1 = obj.get(index);
 * obj.addAtHead(val);
 * obj.addAtTail(val);
 * obj.addAtIndex(index,val);
 * obj.deleteAtIndex(index);
 */

9.24

题目

1652. 拆炸弹

思路

纯纯的模拟

代码

class Solution {
    public int[] decrypt(int[] code, int k) {
        int len = code.length;        
        int ans [] = new int[len];        
        int e = k >= 0 ? k == 0 ? 0 : 1 : -1;
        for(int i = 0; i < len; i++){
            int sum = 0;
            for(int j = e; j != k + e; j += e){                
                sum += code[(i + j + len) % len];
            }
            ans[i] = sum;
        }
        return ans;
    }
}

9.25

题目

788. 旋转数字

思路

判断有没有2、5、6、9存在,如果存在3、4、7那就是寄咯

代码

class Solution {
    public int rotatedDigits(int n) {
        int count = 0;
        for(int i=1;i<=n;i++){
            if(goodNum(i)) count++;
        }
        return count;
    }

    private boolean goodNum(int num){
        boolean flag = false;
        while(num>0){
            int v = num%10;
            if(v==3||v==4||v==7){
                return false;
            }
            if(v==2||v==5||v==6||v==9){
                flag = true;
            }
            num = num/10;
        }
        return flag;
    }
}

9.26

题目

面试题 17.19. 消失的两个数字

思路

第一次自己写,赶时间,当时就没用O(n)来考虑,直接排序后模拟,纯纯的懒人做法

第二次写,想起了异或运算,因为刚好消失两个,所以在补齐[1,N]之后所有的数字异或后得到的就是剩下两个数字异或后的值,因为两个数必然不同,那么xor后存在的1就是两个数不同的二进制位(有点crypto的感觉了捏)

代码

暴力模拟,shit mountain

function missingTwo(nums: number[]): number[] {
    nums.sort((a,b) => a-b)
    const len = nums.length
    const last = nums[len-1]
    if (last == len+2) {
        if (last == 3 && len == 1) {
            return [2,1]
        }
        // 如果最后一个是n,那么缺失在了nums中
        const res = [-1,-1]
        for (let i = 1; i < len; i++) {
            if (res[0] >= 0 && res[1] >= 0) break
            if (nums[i] != nums[i-1] + 1) {
                if (nums[i] - nums[i-1] == 2) {
                    if (res[0] == -1) {
                        res[0] = nums[i] - 1
                    } else {
                        res[1] = nums[i] - 1
                    }
                } else {
                    res[0] = nums[i]-1
                    res[1] = nums[i]-2
                }
            }
        }
        if (res[1] < 0) res[1] = 1
        return res
    } else {
        if ((len + 2) - last == 2) {
            return [last+1, last+2]
        } else {
            for (let i = 1; i < len; i++) {
                if (nums[i] != nums[i-1] + 1) {
                    return [last+1, nums[i] - 1]
                }
            }
        }
        return [1, last+1]
    }
}

异或版本

class Solution:
    def missingTwo(self, nums: List[int]) -> List[int]:
        xor = 0
        for num in nums:
            xor ^= num
        for i in range(1, len(nums) + 3):
            xor ^= i
        # 此时xor的值就是两个未知数的异或后的值, 求出lowbit就是两个数不同的二进制位
        lowbit = xor & ((~xor)+1)
        num1 = 0
        for num in nums:
            if num & lowbit:
                num1 ^= num
        for i in range(1, len(nums) + 3):
            if i & lowbit:
                num1 ^= i
        return [num1, xor ^ num1]

9.27

题目

面试题 01.02. 判定是否互为字符重排

思路

桶排序,算字符出现的次数

代码

class Solution {
    public boolean CheckPermutation(String s1, String s2) {
        int[] bk = new int[26];
        for (char s : s1.toCharArray()) {
            bk[s-'a']++;
        }
        for (char s : s2.toCharArray()) {
            bk[s-'a']--;
        }
        for (int s : bk) {
            if (s != 0) return false;
        }
        return true;
    }
}

9.28

题目

面试题 17.09. 第 k 个数

思路

由题意的,某一个满足结果的数,一定是之前的某个 resultA3 或者是 resultB5 或者是 resultC*7 的结果

并且结果一定是 这三个乘积的最小值

因此,只要能够记录 resultA、resultB、resultC 的值,再相互与 3、5、7 相乘,取其中的最小值,就是当前的目标值

代码

久违用用Java

class Solution {
    public int getKthMagicNumber(int k) {
         int [] result = new int[k];
        result[0] = 1;
        int point3 = 0;
        int point5 = 0;
        int point7 = 0;
        for (int i = 1; i < k; i++) {
            int resultN = Math.min(Math.min(result[point3] * 3, result[point5] * 5), result[point7] * 7);
            if (resultN % 3 == 0) {
                point3++;
            }
            if (resultN % 5 == 0) {
                point5++;
            }
            if (resultN % 7 == 0) {
                point7++;
            }
            result[i] = resultN;
        }
        return result[k - 1];
    }
}

9.29

题目

面试题 01.09. 字符串轮转

思路

将第二个字符串contact,判断第一个字符串是否出现在连接后的字符串中

代码

一句话返回

function isFlipedString(s1: string, s2: string): boolean {
    return s1.length == s2.length && (s2+s2).indexOf(s1) != -1;
}

9.30

题目

面试题 01.08. 零矩阵

思路

直接模拟,但是模拟也要又方便的手段

建立两个数组,用来存放行和列中存在0的位置

下次遍历的时候,如果该两个数组标记了需要被置0,就去置0,原地修改

代码

/**
 Do not return anything, modify matrix in-place instead.
 */
function setZeroes(matrix: number[][]): void {
    const rows = new Array(matrix.length).fill(false)
    const cols = new Array(matrix[0].length).fill(false)
    for (let i = 0; i < matrix.length; i++)
        for (let j = 0; j < matrix[0].length; j++)
            if (matrix[i][j] == 0) {
                rows[i] = true
                cols[j] = true
            }
    
    for (let i = 0; i < matrix.length; i++)
        for (let j = 0; j < matrix[0].length; j++)
            if (rows[i] || cols[j]) matrix[i][j] = 0
                

};

final

image-20221007140413899