leetcode-954. 二倍数对数组

leetcode-954. 二倍数对数组

题源

知识点

  • 哈希表
    • 对于哈希表,我有了新认识,以前我感觉哈希是哈希,数组是数组,虽然现在我也是这样理解的;但是我发现哈希表也是通过索引来查找的,和数组的查找差不多,只不过数组中的索引只能是大于登录0的整数,而哈希表中即可以满足大于登录0的整数的数组特性,还可以是其他类型的数。哈希中的索引是通过哈希算法得到的。
  • 排序
    • 对于这道题目来说,排序也至关重要,因为解决此题,遍历的顺序尤为重要。而这里排序的是哈希的键,而非输入的数组。

思路

  • 这一题其实也就是问,能不能分成n/2对的元素,且每一对的元素相差一倍即(value1 = 2 * value2)
  • 思路
    • 先将数组转化为哈希表
    • 单独考虑哈希表中0的个数是否符合条件。
      • 如果0的个数为奇数,则不满足条件返回假(false)
      • 如果0的个数为偶数,则满足条件,继续执行下面的代码。
    • 对哈希表中的key进行绝对值的排序
    • 对拍好序的哈希表中的key进行遍历
      • 如果哈希表中的key的个数大于哈希表中的2 *key 的个数则返回假(false)
      • 哈希表中的2 *key的个数等于哈希表中的2 *key的个数减哈希表中的key的个数。
    • 成功遍历成功,返回真(true)

代码

python

  • 第一个是直接用字典写的,第二个是用collections模块中的Counter写的。
  • python写代码过程的总结:
    • 字典用此方式[key]访问一个没有存在的key的值会报错
    • .get(key)方法访问一个没有存在的key的值默认返回None,如果需要更改默认返回需加第二个参数,如默认返回1.get(key, 1)
    • .setdefault(key, value)设置key的默认值为value,如果改字典存在该key,则此方法失效。
    • collections模块中的Counter(arr)就是统计arr中元素的个数。并且可以通过[key]访问,并且就算访问一个没有存在的key的值也不会报错,默认返回0
class Solution:
    def canReorderDoubled(self, arr: list[int]) -> bool:
        arr_dict = self.arrToDict(arr)  # 将数组转换为字典或者说是哈希化
        if arr_dict.get(0, 0) % 2 == 1:# 单独判断数组中0的个数
            return False
        for key in sorted(arr_dict, key=abs):
            if arr_dict.get(key, 0) > arr_dict.get(2 * key, 0):
                return False
            arr_dict[2 * key] = arr_dict.get(2 * key, 0) - arr_dict.get(key, 0)
        return True

    def arrToDict(self, arr: list[int]) -> list[int]:
        re = {}
        for num in arr:
            re.setdefault(num, 0)
            re[num] += 1
        return re

执行用时:104 ms, 在所有 Python3 提交中击败了79.72%的用户
内存消耗:17.1 MB, 在所有 Python3 提交中击败了37.06%的用户

import collections as cs

class Solution:
    def canReorderDoubled(self, arr: List[int]) -> bool:
        arr_dict = cs.Counter(arr)
        if arr_dict[0] % 2 == 1:
            return False
        for key in sorted(arr_dict, key=abs):
            if arr_dict[key] > arr_dict[2 * key]:
                return False
            arr_dict[2 * key] -= arr_dict[key]
        return True

执行用时:100 ms, 在所有 Python3 提交中击败了79.72%的用户
内存消耗:17 MB, 在所有 Python3 提交中击败了50.35%的用户

javascript

  • javascript写代码过程的总结:
    • Map()和对象的差异
    • Map.prototype.get或者set重写默认的方法,并且遍历的时候不会出现重写的方法,如果你Map.prototype.asd等自定义的方法时候,你通过for in 遍历map的时候会把自定义的方法命遍历出来
    • map和对象访问不存在的key时,不会报错,而是会返回undefined
/**
 * @param {number[]} arr
 * @return {boolean}
 */
var canReorderDoubled = function(arr) {
    let arrMap = arrToMap(arr)
    if(arrMap.get(0) % 2 === 1) return false
    for(let key of mapSort(arrMap)){
        if(arrMap.get(key) > arrMap.get(2 * key)) return false
        arrMap[2 * key] = arrMap.get(2 * key) - arrMap.get(key)
    }
    return true
};

const arrToMap = (arr) => {
    let re = new Map()
    Map.prototype.get = function(key){
        if(this[key] === undefined) return 0
        return this[key]
    }
    Map.prototype.set = function(key){
        if(this[key] === undefined) this[key] = 1
        else this[key]++
    }
    for(let num of arr) re.set(num)
    return re
}

const mapSort = (map) =>{
    let arr = []
    for(let key in map) arr.push(key)
    return arr.sort((a, b) => Math.abs(a) - Math.abs(b))
}

执行用时:124 ms, 在所有 JavaScript 提交中击败了60.00%的用户
内存消耗:47.8 MB, 在所有 JavaScript 提交中击败了60.00%的用户

java

  • 对java确实不够熟悉,总共写了5次才过。
  • java写代码过程的总结:
    • if语句中只写一个只能用布尔类型(真假),如果用其他类型则需要用运算符(!=、==等)。
    • map.getOrDefault(key, defaultValue)就是当key不存在时,返回defaultValue,存在则返回当前key的值。
    • map.keySet()获取的键值,需要重新赋值给其他对象,如List集合,之后再进行相应的操作。
    • Collections.sort(list, cmp)进行排序。
class Solution {
    public boolean canReorderDoubled(int[] arr) {
        Map<Integer, Integer> map = Solution.arrToMap(arr);
        if(map.getOrDefault(0, 0) % 2 == 1) return false;
        List<Integer> list = new ArrayList<>();
        for(int num : map.keySet()) list.add(num);
        Collections.sort(list, (a, b) -> Math.abs(a) - Math.abs(b));
        for(int key : list){
            int key1Count = map.getOrDefault(key, 0);
            int key2Count = map.getOrDefault(key * 2, 0);
            if(key1Count > key2Count) return false;
            map.replace(2 * key, key2Count - key1Count);
        }
        return true;
    }
    public static Map<Integer, Integer> arrToMap(int[] arr){
        Map<Integer, Integer> map = new HashMap<>();
        for(int key : arr){
            if(map.containsKey(key)) map.replace(key, map.get(key) + 1);
            else map.put(key, 1);
        }
        return map;
    }
}

执行用时:28 ms, 在所有 Java 提交中击败了89.17%的用户
内存消耗:49.1 MB, 在所有 Java 提交中击败了10.30%的用户

C

  • 对C哈希参考文献
  • 对C的总结:
    • 使用uthash中其实也是用了双向链表,故每一次添加map的地址都会变化,故我写函数的时候,需要返回当前位置。
    • 自己写的函数效率不高。
    • 下面是uthash的常用方法。
HASH_ADD_INT(map, key, temp); //key为你自己定义结构体key的名字,为添加的结构体temp 
HASH_FIND_INT(map, &one, find); //找不到返回NULL 
HASH_REPLACE_INT(map, key, newtemp1, temp); // 替换
HASH_DEL(map, newtemp1); // 删除
HASH_CLEAR(hh, map);// 清空 
int HASH_COUNT(map) // 返回int类型,个数 
HASH_SORT( users, name_sort ); // 排序
  • 程序代码
typedef struct{ // C语言哈希结构体
	int key;
	int value;
	UT_hash_handle hh; // 这个就是必写
}Map;

int get(Map *map, int key){ // get函数,如果存在则返回相应的int值,如果不存在则返回0
	Map *result;
	HASH_FIND_INT(map, &key, result);
	if(result == NULL) return 0;
	else return result->value;
}

bool find(Map *map, int key){  // find函数,存在则为true,不存在则返回false
	Map *result;
	HASH_FIND_INT(map, &key, result);
	if(result == NULL) return false;
	else return true;
}

Map* set(Map *map, int key, int value){ // set函数,设置键值
	Map *add = (Map *)malloc(sizeof(Map));
	add->key = key;
	add->value = value;
	if(find(map, key)){
		Map *old = (Map *)malloc(sizeof(Map));
		HASH_REPLACE_INT(map, key, add, old);
	}  
	else HASH_ADD_INT(map, key, add);
	return map;
}

Map* arrToMap(int *arr, int arrSize){   // 数组转化为map数据类型
	Map* map = (Map *)malloc(sizeof(Map));
	map = NULL;
	for(int i = 0; i < arrSize; i++){
		if(find(map, arr[i])) map = set(map, arr[i], get(map, arr[i]) + 1);
		else map = set(map, arr[i], 1);
	}
	return map;
}

int *getKeys(Map *map, int *keysLen){   // 已数组形式返回哈希所有的键
	int *arr = (int *)malloc(sizeof(int) * HASH_COUNT(map));
	*keysLen = 0;
	Map *temp;
	for(temp = map; temp != NULL; temp = (Map *)temp->hh.next){
		arr[(*keysLen)++] = temp->key;
	}
	return arr;
}

void mapPrintAll(Map *map){   // 遍历map的函数,在这道题目测试使用
	Map *temp;
	for(temp = map; temp != NULL; temp = (Map *)temp->hh.next){
		printf("%d: %d\n", temp->key, temp->value);
	}
}

int cmp(const void *a, const void *b){  // qsort函数的比较函数
	return abs(*((int *)a)) - abs(*((int *)b)); 
}

bool canReorderDoubled(int* arr, int arrSize){
	Map* map = arrToMap(arr, arrSize);
	int *keysLen = (int *)malloc(sizeof(int));
	int *keys = getKeys(map, keysLen);
	qsort(keys, *keysLen, sizeof(int), cmp);
	for(int i = 0; i < *keysLen; i++){
		if(get(map, keys[i]) > get(map, 2 * keys[i])) return false;
		set(map, 2 * keys[i], get(map, 2 * keys[i]) - get(map, keys[i])); 
	} 
	return true;
}

执行用时:196 ms, 在所有 C 提交中击败了5.71%的用户
内存消耗:85.8 MB, 在所有 C 提交中击败了5.72%的用户

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值