4.排序算法练习

快速选择

215. 数组中的第K个最大元素(medium)

题目:给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

 

示例 1:

输入: [3,2,1,5,6,4], k = 2
输出: 5
示例 2:

输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4
 

提示:

1 <= k <= nums.length <= 105
-104 <= nums[i] <= 104

分析
这题是个典型的第几大的问题。不考虑时间复杂度的话,可以先排序再返回值。但是题目要求时间复杂度是O(n)。有一个经典的题型可以去套,第几大时,使用快速排序。但是有一个问题是,如果出现重复的值该怎麽办,这题根据示例来看,直接忽略这个问题,有重复的会把重复的也算进去。比如第二大,但是有两个重复的最大的,这时候选择最大值。
首先处理一下数值,第几大可以转换成数组里从左到右第几个,时nums.length-k对应的下标。
之后排序结合快速排序的方法,随便取一个值,这里取数组的左下标的值 为标记点。从数组右边开始比较,如果比目标值小,那右边指向的数赋值给左边指向的数,否则右边的指标向左移动。如果右边指标左移,开始找左边指标,看指向的值会不会大于目标值,如果大于就把左边指标指向的数,赋值给右边指标指向的数。直到左右指标重合,在重合的位置,赋值目标key。最后返回重合下标。这时,可以知道比k小的数有多少个了,与之前处理的下标对比,小了右移,大了左移。最后可以得到目标值。

var findKthLargest = function (nums, k) {
    let target = nums.length - k;
    let l = 0;
    let r = nums.length - 1;
    while (l < r) {
        let mid = quickSort(nums, l, r);
        if (mid == target) return nums[mid];
        else if (mid < target) {
            l = mid + 1;
        } else {
            r = mid - 1;
        }
    }
    return nums[l]
};
function quickSort(nums, left, right) {
    let key = nums[left];
    while (left < right) {
        while (key < nums[right] && left < right) {
            right--
        }
        nums[left] = nums[right];
        while (nums[left] <= key && left < right) {
            left++
        }
        nums[right] = nums[left]
    }
    nums[left] = key;
    return left;
}

桶排序

347. 前 K 个高频元素(medium)

题目:给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

 

示例 1:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:

输入: nums = [1], k = 1
输出: [1]
 

提示:

1 <= nums.length <= 105
k 的取值范围是 [1, 数组中不相同的元素的个数]
题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的
 

进阶:你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组大小。

分析
堆排序,核心点是,利用一个新的空间去记录当前的数组状态。这里可以先使用一个map去记录数组里每个item出现的频率。再对频率进行排序,最后输出前K个值就可以了。

var topKFrequent = function (nums, k) {
    let map = new Map();
    let arr = [...new Set(nums)]
    for (let i = 0; i < nums.length; i++) {
        let temp = nums[i]
        if (map.has(temp)) {
            map.set(temp, map.get(temp) + 1)
        } else {
            map.set(temp, 1)
        }
    }
    return arr.sort((a, b) => map.get(b) - map.get(a)).slice(0, k)
};

练习

451. 根据字符出现频率排序(medium)

给定一个字符串 s ,根据字符出现的 频率 对其进行 降序排序 。一个字符出现的 频率 是它出现在字符串中的次数。

返回 已排序的字符串 。如果有多个答案,返回其中任何一个。

 

示例 1:

输入: s = "tree"
输出: "eert"
解释: 'e'出现两次,'r''t'都只出现一次。
因此'e'必须出现在'r''t'之前。此外,"eetr"也是一个有效的答案。
示例 2:

输入: s = "cccaaa"
输出: "cccaaa"
解释: 'c''a'都出现三次。此外,"aaaccc"也是有效的答案。
注意"cacaca"是不正确的,因为相同的字母必须放在一起。
示例 3:

输入: s = "Aabb"
输出: "bbAa"
解释: 此外,"bbaA"也是一个有效的答案,但"Aabb"是不正确的。
注意'A''a'被认为是两种不同的字符。
 

提示:

1 <= s.length <= 5 * 105
s 由大小写英文字母和数字组成

分析
这题和上面的题目思路很相似,都是先记录数组里的状态,然后通过状态返回我们需要的数据。我们可以手动把字符串变成数组,然后采用上题相同的思想来做。是桶排序的变形

var frequencySort = function (s) {
    let arr = s.split('');
    let newarr = [... new Set(arr)]
    let map = new Map();
    arr.map(chart => {
        if (map.has(chart)) {
            map.set(chart, map.get(chart) + 1)
        } else {
            map.set(chart, 1)
        }
    })
    newarr = newarr.sort((a, b) => map.get(b) - map.get(a));
    let res = []
    for (let item of newarr) {
        res = res.concat(new Array(map.get(item)).fill(item))
    }
    return res.join("")
};

提升

75. 颜色分类(medium)

给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

我们使用整数 012 分别表示红色、白色和蓝色。

必须在不使用库内置的 sort 函数的情况下解决这个问题。

 

示例 1:

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

输入:nums = [2,0,1]
输出:[0,1,2]
 

提示:

n == nums.length
1 <= n <= 300
nums[i]012
 

进阶:

你能想出一个仅使用常数空间的一趟扫描算法吗?

分析
这题想让我们通过排序来写,可以选择的方法有冒泡、快速、选择、插入等方法。但是也可以换种方法,因为数组内的元素种类是已知的,所以采用桶排序只用遍历一次,然后根据出现的频率把数据依次填上去会快很多。

var sortColors = function (nums) {
    let map = new Map();
    nums.map((num) => {
        if (map.has(num)) {
            map.set(num, map.get(num) + 1)
        } else {
            map.set(num, 1)
        }
    })
    let colors = [0, 1, 2]
    let i = 0;
    colors.map((color) => {
        if (map.has(color)) {
            let time = map.get(color)
            while (time > 0) {
                nums[i] = color;
                time--;
                i++
            }
        }
    })
};
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值