LeetCode每周五题_2024/01/01~2024/01/05

本文介绍了多个编程问题的解决方案,涉及摩天轮运营策略、字符串重复计算、链表节点移除、矩阵覆盖、队列中可见人数和链表插入最大公约数等技术,展示了如何在IT技术场景中运用算法求解实际问题。
摘要由CSDN通过智能技术生成

1599. 经营摩天轮的最大利润 [2024/01/01]

题目

你正在经营一座摩天轮,该摩天轮共有 4 个座舱 ,每个座舱 最多可以容纳 4 位游客 。你可以 逆时针 轮转座舱,但每次轮转都需要支付一定的运行成本 runningCost 。摩天轮每次轮转都恰好转动 1 / 4 周。
给你一个长度为 n 的数组 customers , customers[i] 是在第 i 次轮转(下标从 0 开始)之前到达的新游客的数量。这也意味着你必须在新游客到来前轮转 i 次。每位游客在登上离地面最近的座舱前都会支付登舱成本 boardingCost ,一旦该座舱再次抵达地面,他们就会离开座舱结束游玩。
你可以随时停下摩天轮,即便是 在服务所有游客之前 。如果你决定停止运营摩天轮,为了保证所有游客安全着陆,将免费进行所有后续轮转 。注意,如果有超过 4 位游客在等摩天轮,那么只有 4 位游客可以登上摩天轮,其余的需要等待 下一次轮转 。
返回最大化利润所需执行的 最小轮转次数 。 如果不存在利润为正的方案,则返回 -1 。

题解

/**
 * @param {number[]} customers
 * @param {number} boardingCost
 * @param {number} runningCost
 * @return {number}
 */
var minOperationsMaxProfit = function (customers, boardingCost, runningCost) {
  let remainder = 0; //等待人数
  let profit = 0; //盈利
  let orders = 0; //轮次
  let maxProfit = 0; //最大盈利
  let maxIndex = 0; //最大盈利轮次
  let profitOnce = 4 * boardingCost - runningCost; //一轮4个人盈利
  // 轮次游客
  for (let i = 0; i < customers.length; i++) { 
    const customer = customers[i];
    // 运行一轮后剩余人数 
    const remainderPeople = remainder + customer - 4;
    if (remainderPeople > 0) {
      remainder = remainderPeople;
      const num = profit + profitOnce;
      maxProfit = Math.max(maxProfit, profit, num);
      if (maxProfit >= profit && num > profit) {
        maxIndex = orders + 1;
      }
      profit = num;
    } else {
      remainder = 0;
      const num = profit + (4 + remainderPeople) * boardingCost - runningCost;
      maxProfit = Math.max(maxProfit, profit, num);
      if (maxProfit >= profit && num > profit) {
        maxIndex = orders + 1;
      }
      profit = num;
    }
    orders++;
  }
  // 只需要让剩余人做完摩天轮
  while (remainder > 0) {
    if (remainder > 4) {
      const num = profit + profitOnce;
      maxProfit = Math.max(maxProfit, profit, num);
      profit = num;
      if (maxProfit >= profit) {
        maxIndex = orders + 1;
      }
    } else {
      const num = profit + remainder * boardingCost - runningCost;
      maxProfit = Math.max(maxProfit, profit, num);
      if (maxProfit > profit && profit > num) {
        maxIndex = orders;
      } else if (maxProfit > profit) {
        maxIndex = orders + 1;
      }
      profit = num;
    }
    remainder = remainder - 4;
    orders++;
  }
  return profit > 0 ? (maxIndex ? maxIndex : orders) : -1;
};

466. 统计重复个数 [2024/01/02]

题目

定义 str = [s, n] 表示 str 由 n 个字符串 s 连接构成。
例如,str == [“abc”, 3] ==“abcabcabc” 。
如果可以从 s2 中删除某些字符使其变为 s1,则称字符串 s1 可以从字符串 s2 获得。
例如,根据定义,s1 = “abc” 可以从 s2 = “abdbec” 获得,仅需要删除加粗且用斜体标识的字符。
现在给你两个字符串 s1 和 s2 和两个整数 n1 和 n2 。由此构造得到两个字符串,其中 str1 = [s1, n1]、str2 = [s2, n2] 。
请你找出一个最大整数 m ,以满足 str = [str2, m] 可以从 str1 获得。

题解

/**
 * @param {string} s1
 * @param {number} n1
 * @param {string} s2
 * @param {number} n2
 * @return {number}
 */
var getMaxRepetitions = function (s1, n1, s2, n2) {
  let indexMap = new Map();
  let countS1 = 0,
    countS2 = 0;
  let s2p = 0;
  while (countS1 < n1) {
    let prev = indexMap.get(s2p);
    if (!prev) {
      indexMap.set(s2p, [countS1, countS2]);
    } else {
      // 循环节 下一个s1 对应的 s2p 索引有相同时

      // 循环节循环的次数 向下取整
      let t = ((n1 - prev[0]) / (countS1 - prev[0])) | 0;
      countS2 = prev[1] + t * (countS2 - prev[1]);
      countS1 = prev[0] + t * (countS1 - prev[0]);
      // 清楚之前的循环记录
      indexMap.clear();
      // 整除
      if (countS1 === n1) break;
    }
    // 循环s1
    for (let i = 0; i < s1.length; i++) {
      if (s1[i] === s2[s2p]) {
        s2p++;
        if (s2p === s2.length) {
          s2p = 0;
          countS2++;
        }
      }
    }
    countS1++;
  }
  return (countS2 / n2) | 0;
};

2487. 从链表中移除节点 [2024/01/03]

题目

给你一个链表的头节点 head 。
移除每个右侧有一个更大数值的节点。
返回修改后链表的头节点 head 。

题解

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */

 
/**
 * @desc 反转链表
 * @param {ListNode} head
 */
var reverseList = function(head) {
    let prev = null;
    let curr = head;
    while(curr){
        let nextCurr = curr.next;
        curr.next = prev;
        prev = curr;
        curr = nextCurr;
    }
    return prev;
};


/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var removeNodes = function(head) {
    head = reverseList(head);
    let maxVal = 0;
    let newList = new ListNode(0);
    let newHead = newList;
    let curr = head;

    while(curr){
        if(curr.val>=maxVal){
            newHead.next = curr;
            newHead =  newHead.next;
            maxVal = curr.val;
        }
        curr=curr.next;
    }
    newHead.next =null;
    return reverseList(newList.next)
}

2397. 被列覆盖的最多行数 [2024/01/04]

题目

给你一个下标从 0 开始、大小为 m x n 的二进制矩阵 matrix ;另给你一个整数 numSelect,表示你必须从 matrix 中选择的 不同 列的数量。
如果一行中所有的 1 都被你选中的列所覆盖,则认为这一行被 覆盖 了。
形式上,假设 s = {c1, c2, …, cnumSelect} 是你选择的列的集合。对于矩阵中的某一行 row ,如果满足下述条件,则认为这一行被集合 s 覆盖:
对于满足 matrix[row][col] == 1 的每个单元格 matrix[row][col](0 <= col <= n - 1),col 均存在于 s 中,或者
row 中 不存在 值为 1 的单元格。
你需要从矩阵中选出 numSelect 个列,使集合覆盖的行数最大化。
返回一个整数,表示可以由 numSelect 列构成的集合 覆盖 的 最大行数 。

题解

/**
 * @param {number[][]} matrix
 * @param {number} numSelect
 * @return {number}
 */
var maximumRows = function (matrix, numSelect) {
    const n = matrix[0].length; // 列
    const zeroCol = numSelect,  // 覆盖列数 覆盖后为0
        oneCol = n - numSelect; // 未覆盖列, 为1
    const list = []; // 覆盖列组合数量
    function dfs(zeroCol, oneCol, str) {
        if (str.length === n) {
            list.push(parseInt(str, 2)) // 二进制解析为十进制
            return;
        }
        if (zeroCol > 0) dfs(zeroCol - 1, oneCol, str + '0');
        if (oneCol > 0) dfs(zeroCol, oneCol, str + '1');
    }
    dfs(zeroCol, oneCol, '');
    let maxCover = 0;
    for (let i = 0; i < list.length; i++) {//每种覆盖列方案得到的覆盖数量
        let cover = 0; // 每种方案的覆盖数量
        for (let j = 0; j < matrix.length; j++) {
            const item = parseInt(matrix[j].join(''), 2);// 当前行转2进制
            if ((item & list[i]) === 0) { // 利用位运算 判断行是否被覆盖
                cover++;
            }
            if(j === matrix.length - 1){
                maxCover = Math.max(cover, maxCover);
            }

        }

    }
    return maxCover;
};

1944. 队列中可以看到的人数

题目

有 n 个人排成一个队列,从左到右 编号为 0 到 n - 1 。给你以一个整数数组 heights ,每个整数 互不相同,heights[i] 表示第 i 个人的高度。
一个人能 看到 他右边另一个人的条件是这两人之间的所有人都比他们两人 矮 。更正式的,第 i 个人能看到第 j 个人的条件是 i < j 且 min(heights[i], heights[j]) > max(heights[i+1], heights[i+2], …, heights[j-1]) 。
请你返回一个长度为 n 的数组 answer ,其中 answer[i] 是第 i 个人在他右侧队列中能 看到 的 人数 。

题解

/**
 * @param {number[]} heights
 * @return {number[]}
 */
var canSeePersonsCount = function (heights) {
  const n = heights.length;
  const seeArr = new Array(n).fill(0);
  const stack = []; // 单调栈递减
  for (let i = n - 1; i >= 0; i--) {
    const currentHeight = heights[i]; //当前人高度
    let prevHeight = stack[stack.length - 1]; //栈里最低的
    while (stack.length > 0 && currentHeight > prevHeight) {
      // 当前人高度比栈里人高,后面的人看不到挡住了,移除矮的
      stack.pop();
      seeArr[i] += 1; //当前人可以看到
      prevHeight = stack[stack.length - 1];
    }
    if (stack.length) {
      //必定有一个人可以被当前人看到,由于不比栈里的人高,所以只能看到一个
      seeArr[i] += 1;
    }
    stack.push(currentHeight);
  }
  return seeArr;
};

2807. 在链表中插入最大公约数

题目

给你一个链表的头 head ,每个结点包含一个整数值。
在相邻结点之间,请你插入一个新的结点,结点值为这两个相邻结点值的 最大公约数 。
请你返回插入之后的链表。
两个数的 最大公约数 是可以被两个数字整除的最大正整数。

题解

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var insertGreatestCommonDivisors = function (head) {
    //辗转相除法 较大的值除以较小的,直到余数为0 取除数
    function getDivisor(a, b) {
        let divisor = null; // 余数
        let min = null; // 最小的值
        while (divisor !== 0) {
            if (a > b) {
                divisor = a % b;
                min = b;
            } else {
                divisor = b % a;
                min = a;
            }
            a = min;
            b = divisor;
        }
        return min;
    }

    let list = new ListNode(head.val);
    let prevHead = head; // 上一个节点
    let newHead = head.next; // 当前节点
    while (newHead) {
        const divisor = getDivisor(prevHead.val, newHead.val); //最大公约数
        let newNode = new ListNode(divisor, new ListNode(newHead.val)); // 创建最大公约数节点
        // 循环找到list最后一个节点
        let listNext = list;
        while (listNext) {
            if (listNext.next) {
                listNext = listNext.next;
            } else {
                break;
            }
        }
        // 把新节点插入末尾
        listNext.next = newNode;
        prevHead = newHead;
        newHead = newHead.next;
    }
    return list;
};

383. 赎金信

题目

给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。
如果可以,返回 true ;否则返回 false 。
magazine 中的每个字符只能在 ransomNote 中使用一次。

题解

/**
 * @param {string} ransomNote
 * @param {string} magazine
 * @return {boolean}
 */
var canConstruct = function (ransomNote, magazine) {
    let wordMap = {}; // magazine字符每个字母出现次数
    let i = 0,
        j = 0;
    // 统计 magazine字符26个字母出现次数
    while (i < magazine.length) {
        const key = magazine[i];
        const wordNum = wordMap[key];
        wordMap[key] = wordNum ? wordNum + 1 : 1;
        i++;
    }
    // 循环ransomNote字符消耗map里对应字母次数
    while (j < ransomNote.length) {
        const ranKey = ransomNote[j];
        if (wordMap[ranKey] > 0) {
            wordMap[ranKey] -= 1;
        } else {
            return false;
        }
        j++;
    }
    return true;
};

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值