贪心

86 阅读2分钟

1.最长递增子序列

为了使序列上升尽可能的慢,不断记录长度为i的子序列中最大的数。 不断迭代dp[i]

/**
 * @param {number[]} nums
 * @return {number}
 */
var binarySearch = function(arr, target) {
    let start = 0, end = arr.length - 1;
    if (target == arr[start] || target == arr[end]) return -1;
    if (target < arr[start]) return 0;
    if (target > arr[end]) return end + 1;
    while (start <= end) {
        let middle = Math.floor((start + end) / 2);
        if (target == arr[middle]) return -1;
        if (target < arr[middle] && (middle == 0 || target > arr[middle-1])) {
            return middle;
        }
        if (target > arr[middle]) {
            start = middle + 1;
        } else {
            end = middle - 1;
        }
    }

    return -1;
};

var lengthOfLIS = function(nums) {
    let dp = [nums[0]];
    for (let i = 1; i < nums.length; i++) {
        let pos = binarySearch(dp, nums[i]);
        if (pos != -1) {
            if (pos == dp.length) {
                dp.push(nums[i]);
            } else {
                dp[pos] = nums[i];
            }
        }
    }
    return dp.length;
};
  1. CPU调度使用的最小时间

题目:相同任务间有冷却时间。给定任务名和需要执行的次数,求怎样执行使得总时间最小。

解法:贪心。每个时间片选择,可以执行的任务中剩余次数最多的。

/**
 * @param {character[]} tasks
 * @param {number} n
 * @return {number}
 */

//每个时间片内,选择不在冷冻期的剩余次数最多的任务
var leastInterval = function(tasks, n) {
    let map = {}, nextTime;

    //统计每个任务出现的频率,初始可执行时间为1
    for (let i = 0; i < tasks.length; i++) {
        if (!map[tasks[i]]) {
            map[tasks[i]] = {
                nextValidTime: 1,
                rest: 1
            };
        } else {
            map[tasks[i]].rest++;
        }
    }

    //不遍历任务,只依次选择n个任务
    for (let i = 0; i < tasks.length; i++) {
        nextTime = Infinity;
        //首先找到有可执行任务的最早时间
        for (let taskName in map) {
            if (map.hasOwnProperty(taskName) && map[taskName].rest > 0) {
                if (map[taskName].nextValidTime < nextTime) {
                    nextTime = map[taskName].nextValidTime;
                }
            }
        }
        
        //找到可执行时间在nextTime时候的任务中的次数最多者
        let nextTask = -1, maxRest = 0;
        for (let taskName in map) {
            if (map.hasOwnProperty(taskName) && map[taskName].rest > 0 ) {
                if (map[taskName].nextValidTime == nextTime 
                && map[taskName].rest > maxRest) {
                    maxRest = map[taskName].rest;
                    nextTask = taskName;
                }
            }
        }

        //已经找到了下一个任务,那么
        //首先将该任务频次-1,其次将同任务下次开始时间全部调整为nextTime + n + 1, 
        //其他任务调整为nextTime + 1
        for (let taskName in map) {
            if (map.hasOwnProperty(taskName) && map[taskName].rest > 0) {
                if (taskName == nextTask) {
                    map[taskName].rest--;
                    map[taskName].nextValidTime = nextTime + n + 1;
                } else {
                    map[taskName].nextValidTime = Math.max(nextTime + 1, 
                                                map[taskName].nextValidTime);
                }
            }
        }
    }   
    //最后结果为nextTime
    return nextTime;
};

数组移动:

  1. 找出重复数

在长度为n的数组nums中,所有数<n,找出重复数。

解法:将每一个数字移动到他的值-1,如果所在数与自己相同,那么重复,否则不重复。

线性循环,循环不变量是0~i-1的数各归其位

    /**
 * @param {number[]} nums
 * @return {number[]}
 */
var findDuplicates = function(nums) {
    let ans = [], i = 0;
    while (i < nums.length) {
        if (nums[i] == i + 1 || nums[i] == 0) {
            i++;
        } else {
            let tmp = nums[nums[i] - 1];
            if (tmp == nums[i]) {
                ans.push(nums[i]);
                nums[i] = 0;
            } else {
                nums[nums[i] - 1] = nums[i];
                if (tmp != -1) {
                    nums[i] = tmp;
                }
            }
        }
    }

    return ans;
};

两两淘汰

  1. 找出出现次数超过1/3的数

解法:保留大小为2窗口。遍历数组。 如果在窗口中,那么对应元素次数+1。 如果不在窗口中,有次数为0的元素,取代。 如果不在窗口中,无次数为0的元素,两数递减。

最后还要判断是否2个数真的出现了超过1/3次。