数据结构与算法之排序: Leetcode 164. 最大间距 (Typescript版)

最大间距

  • https://leetcode.cn/problems/maximum-gap/description/

描述

  • 给定一个无序的数组 nums,返回 数组在排序之后,相邻元素之间最大的差值 。如果数组元素个数小于 2,则返回 0 。
  • 您必须编写一个在「线性时间」内运行并使用「线性额外空间」的算法。

示例 1

输入: nums = [3,6,9,1]
输出: 3
解释: 排序后的数组是 [1,3,6,9], 其中相邻元素 (3,6) 和 (6,9) 之间都存在最大差值 3。

示例 2

输入: nums = [10]
输出: 0
解释: 数组元素个数小于 2,因此返回 0。

备注

  • leetcode 的上的这一题,没有说明是整数的限定
  • 如果在用例中新增了小数,会报错,比如: xx.xx is not a valid value of type integer

提示

  • 1 <= nums.length <= 1 0 5 10^5 105
  • 0 <= nums[i] <= 1 0 9 10^9 109

算法实现

1 )借用原生sort + 迭代取相邻最大值

function maximumGap(nums: number[]): number {
    // 如果数组长度小于2返回0
    if (nums.length < 2) return 0;
    // 排序 这个在 leetcode 上要传入回调,否则排序是乱序的
    nums.sort((a, b) => a - b);
    // 用它来保存相邻元素的最大差值
    let max = 0;
    for (let i = 1; i < nums.length; i++) max = Math.max(max, nums[i] - nums[i - 1]);
    return max;
};
  • 这个结果是对的,但是不符合题意
  • 因为题目要求是线性的,一个原生sort的复杂度是 O(nlogn)
  • 注意这个sort要传入回调
    • 这个回调是和默认一致的从小到大,
    • 不传回调,则在 leetcode 环境中debug发现获得的排序是乱的,无法通过

2 )基数排序来解

// 基数排序
function radixSort(list: number[]) {
	const len = list.length; // 获取数组长度
	const max = Math.max(...list); // 获取数组中最大值
	let i = 0; // 用于计数的数组
	let radix = 1; // 基数(个:1, 十:10, 百: 100, ...)
	const tmp = new Array(len); // 临时倒腾数组
	while(max >= radix) {
		// 初始化存储桶 0 ~ 9, 全0填充
		const bucket = new Array(10).fill(0);
		// 对当前基数下的值进行累加计算总数
		for(i=0; i<len; i++) {
			// 获取当前基数下的值, 也就是对应的桶的下标
			const currentRadixNum = Math.floor(list[i] / radix) % 10;
			bucket[currentRadixNum] ++; // 从 0 开始自增, 这里桶中存储的是当前下标下对应数据的累计个数
		}
		// 计算当前数据位置: 基于桶内每个下标对应数据的累计个数,计算当前元素在整个桶中的位置,并反向赋值给当前桶
		for(i = 1; i<10; i++) {
			bucket[i] += bucket[i-1];
		}
		// 将a[i]的值按照桶存储的位置倒腾到临时数组tmp中
		for(i = len-1; i>=0; i--) {
			const currentRadixNum = Math.floor(list[i] / radix) % 10; // 当前基数下的数字
			const currentIndex = bucket[currentRadixNum] - 1; // bucket存储的是位置,位置-1为下标
			tmp[currentIndex] = list[i]; // 根据桶存储的位置, 
			bucket[currentRadixNum] --; // 桶内当前元素累计值 自减
		}
		// 再将临时数组中的元素倒腾回list中,此时完成了一轮的基于基数的处理
		list.splice(0, len, ...tmp);
		radix *= 10; // 基数进位处理
	}
}

function maximumGap(nums: number[]): number {
  // 如果数组长度小于2返回0
  if (nums.length < 2) return 0;
  radixSort(nums);
  // 用它来保存相邻元素的最大差值
  let max = 0;
  for (let i = 1; i < nums.length; i++) max = Math.max(max, nums[i] - nums[i - 1]);
  return max;
};
  • 参考: https://blog.csdn.net/Tyro_java/article/details/134191965
  • 虽然时间复杂度降下来了,满足题意,效率还行

3 )计数排序来解

function countSort(list:number[]) {
	const len: number = list.length;
	if (len < 2) return;
	// 获取最大值和最小值
	const max: number = Math.max(...list);
	const min: number = Math.min(...list);
	// 初始化计数数组
	const countList: number[] = new Array(max - min + 1).fill(0);
	// 初始化目标数组
	const result: number[] = new Array(len);
	// 对原数组的数据在计数数组中累计,原数组的值是计数数组的下标
	for(let i:number = 0; i < len; i++) countList[list[i] - min]++; // 遍历完成后计数数组就变成了累计数组
	// 计算位置: 由上面的累计结果,计算累加后的位置, countList数组从累计数组,变成了位置数组
	for(let j = 1; j < max - min + 1; j++) {
		countList[j] += countList[j - 1];
	}
	// 根据累加后的位置, 将原数组中的数据按照位置进行分配,就得到排好序的数组,备注:数据可能重复,使用过的数据记得-1,从后向前遍历更好理解
	for (let k:number = len - 1; k >= 0; k--) {
		const currentIndex = countList[list[k] - min] - 1;
		result[currentIndex] = list[k]; // 将原数组的当前元素整合到 result数组对应的位置之中
		countList[list[k] - min] --; // 使用一次后,累计的数量自减,防止重复使用导致错误
	}
    list.splice(0, len, ...result); // 重新洗牌,用 result 内元素替换
}

function maximumGap(nums: number[]): number {
  // 如果数组长度小于2返回0
  if (nums.length < 2) return 0;
  countSort(nums);
  // 用它来保存相邻元素的最大差值
  let max = 0;
  for (let i = 1; i < nums.length; i++) max = Math.max(max, nums[i] - nums[i - 1]);
  return max;
};
  • 参考: https://blog.csdn.net/Tyro_java/article/details/134172583
  • 备注:可以做,但计数排序更适用于密集型数据,不适合这个场景,如果用例数据中有: [99999999, 2] 这类,则非常低效
  • 无法通过 leetcode 的测试用例

4 )桶排序

function maximumGap(nums: number[]): number {
    const n: number = nums.length;
    if (n < 2) return 0;
    const minVal: number = Math.min(...nums);
    const maxVal: number = Math.max(...nums);
    const d: number = Math.max(1, Math.floor(maxVal - minVal) / (n - 1));
    const bucketSize = Math.floor((maxVal - minVal) / d) + 1;

    const bucket: number[][] = new Array(bucketSize).fill(0).map(x => new Array(2).fill(0));
    for (let i: number = 0; i < bucketSize; ++i) {
        bucket[i].fill(-1);
    }
    for (let i: number = 0; i < n; i++) {
        const idx: number = Math.floor((nums[i] - minVal) / d);
        if (bucket[idx][0] === -1) {
            bucket[idx][0] = bucket[idx][1] = nums[i];
        } else {
            bucket[idx][0] = Math.min(bucket[idx][0], nums[i]);
            bucket[idx][1] = Math.max(bucket[idx][1], nums[i]);
        }
    }

    let max: number = 0;
    let prev: number = -1;
    for (let i: number = 0; i < bucketSize; i++) {
        if (bucket[i][0] == -1) {
            continue;
        }
        if (prev != -1) {
            max = Math.max(max, bucket[i][0] - bucket[prev][1]);
        }
        prev = i;
    }
    return max;
};
  • 官方提供的 桶排序 解题思路

你可能感兴趣的:(Data,Structure,and,Algorithms,leetcode,算法,排序)