文章简要概述
- 本文主要进行快速排序相关的算法题刷题题解记录,记录快速排序相关算法以及如何解。
- 这文一共有3道题,主要介绍leetcode中
347. 前 K 个高频元素、973. 最接近原点的 K 个点、和451. 根据字符出现频率排序的解题思路。
与快速排序相关算法
347. 前 K 个高频元素
题目大意:
给你一个整数数组 `nums` 和一个整数 `k` ,请你返回其中出现频率前 `k` 高的元素。你可以按 **任意顺序** 返回答案。
示例
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
解题思路:
- 将数组分成已排序和待排序
- 看已排序的小于k,证明数不够,从后面拿过来
- 同理,看已排序的大于k,证明排好的数已经多了,从前面拿过来。
代码:
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
var topKFrequent = function(nums, k)
// 把 list[right] 当成是 value
// 把大于等于 value 的数字放到左边
// 把小于 value 的数字放到右边
function partition(list, left, right, comparator) {
if (typeof comparator != 'function') {
comparator = function(item, value) {
return item >= value;
}
}
let i = left;
for (let j = left; j < right; j++) {
if (comparator(list[j], list[right])) {
[list[i], list[j]] = [list[j], list[i]];
i++;
}
}
[list[i], list[right]] = [list[right], list[i]];
return i;
}
// 寻找第 k 大元素
// 如果 value 的下标刚好是 k - 1,那我们就找到了
// 如果下标大于 k - 1,那就在 [left, index - 1] 这段找第 k 大元素
// 如果下标小于 k - 1,那就对 [pivotIndex + 1, right] 这段找第 k - pivotIndex + left - 1 大元素
function quickSelect(list, k, left = 0, right = list.length - 1, comparator) {
if (left >= right) return list[left];
const index = partition(list, left, right, comparator);
if (index - left === k - 1) return list[index];
else if (index - left > k - 1) {
return quickSelect(list, k, left, index - 1, comparator);
} else {
return quickSelect(list, k - index + left - 1, index + 1, right, comparator);
}
}
// 统计出现次数
const map = {};
for(const key of nums) {
key in map || (map[key] = 0);
map[key]++;
}
const list = Object.entries(map);
quickSelect(list, k, 0, list.length - 1, function(item, value) {
return item[1] >= value[1];
})
return list.slice(0, k).map(item => item[0]);
};
973. 最接近原点的 K 个点
题目大意:
给定一个数组 `points` ,其中 `points[i] = [xi, yi]` 表示 **X-Y** 平面上的一个点,并且是一个整数 `k` ,返回离原点 `(0,0)` 最近的 `k` 个点。
这里,平面上两点之间的距离是 **欧几里德距离**( `√(x1 - x2)2 + (y1 - y2)2` )。
你可以按 **任何顺序** 返回答案。除了点坐标的顺序之外,答案 **确保** 是 **唯一** 的。
示例:
输入:points = [[1,3],[-2,2]], k = 1
输出:[[-2,2]]
解释:
(1, 3) 和原点之间的距离为 sqrt(10),
(-2, 2) 和原点之间的距离为 sqrt(8),
由于 sqrt(8) < sqrt(10),(-2, 2) 离原点更近。
我们只需要距离原点最近的 K = 1 个点,所以答案就是 [[-2,2]]。
解题思路:
- 先根据勾股公式求出两点的距离,利用sort进行排序
- 截取前k个数据
代码:
/**
* @param {number[][]} points
* @param {number} k
* @return {number[][]}
*/
var kClosest = function(points, k) {
return points.sort((a,b) => a[0]*a[0] - b[0]*b[0] + a[1]*a[1] - b[1]*b[1]).slice(0,k)
};
// 快排
var kClosest = function (points, K) {
if (points.length <= K) {
return points;
}
quickSelect(points, 0, points.length - 1, K); // 范围是整个数组
return points.slice(0, K); // 排完后,取前K个
};
function quickSelect(points, start, end, K) {
const pivot = distance(points[start]);
let l = start, r = end;
while (l <= r) { // 左右两个指针
if (distance(points[l]) <= pivot) { // 左指针指向的元素比pivot小,没毛病,看下一个,指针右移
l++;
continue;
}
if (distance(points[r]) > pivot) { // 右指针指向的元素比pivot大,没毛病,看下一个,指针左移
r--;
continue;
}
// 左指针指向的元素比pivot大,右指针指向的元素比pivot小,交换左右指针指向的元素
[points[l], points[r]] = [points[r], points[l]];
l++;
r--; // 指针同时收缩1
}
[points[start], points[r]] = [points[r], points[start]]; // 交换pivot元素和右指针指向的元素
if (r == K) { // 排好了
return;
} else if (r < K) { // 左边还不够K个,则[r+1:end]要继续排
quickSelect(points, r + 1, end, K);
} else { // 左边大于K个,则对左边继续排
quickSelect(points, start, r - 1, K);
}
}
function distance(point) { // 求point到原点的距离
return point[0] * point[0] + point[1] * point[1];
}
451. 根据字符出现频率排序
题目大意:
给定一个字符串,请将字符串里的字符按照出现的频率降序排列。
示例:
输入: "tree"
输出: "eert"
解释:
'e'出现两次,'r'和't'都只出现一次。
因此'e'必须出现在'r'和't'之前。此外,"eetr"也是一个有效的答案。
解题思路:
- 先遍历字符串,统计每一个字符出现的次数
- 将统计的字符次数进行排序(这里借助了sort)
- 按照统计的次数按照从大到小遍历,记录对应的字符
- 返回记录后的字符串顺序
代码:
/**
* @param {string} s
* @return {string}
*/
var frequencySort = function(s) {
const map = new Map();
const len = s.length;
for(let i = 0; i < len; i++) {
const key = s[i];
let count = (map.get(key) || 0) + 1;
map.set(key, count);
}
const list = [...map.keys()];
list.sort((a,b) => map.get(b) - map.get(a));
const size = list.length;
const res = [];
for(let i = 0; i < size; i++) {
const k = list[i];
const count = map.get(k);
for(j = 0; j < count; j++) {
res.push(k);
}
}
return res.join('')
};
结束语
数据结构与算法相关的练习题会持续输出,一起来学习,持续关注。当前是快速排序部分。后期还会有其他类型的数据结构,题目来源于leetcode。往期文章:
树形结构-对应算法详解一 树形结构-对应算法详解二 链表相关算法复习一
链表相关算法复习二 链表相关算法复习三 数据结构与算法-栈一
数据结构与算法-栈二 数据结构与算法-队列一 数据结构与算法-队列二
数据结构与算法-链表一 数据结构与算法-链表二 数据结构与算法-链表三
有兴趣的可以一起来刷题,求点赞👍 关注!