zoukankan      html  css  js  c++  java
  • LeetCode 368Largest Divisible Subset

    原题链接: 368. Largest Divisible Subset

    Given a set of distinct positive integers nums, return the largest subset answer such that every pair (answer[i], answer[j]) of elements in this subset satisfies:

    • answer[i] % answer[j] == 0, or
    • answer[j] % answer[i] == 0

    If there are multiple solutions, return any of them.

    Example 1:
    Input: nums = [1,2,3]
    Output: [1,2]
    Explanation: [1,3] is also accepted.
    
    Example 2:
    Input: nums = [1,2,4,8]
    Output: [1,2,4,8]
    
    Constraints:
    • 1 <= nums.length <= 1000
    • 1 <= nums[i] <= 2 * 109
    • All the integers in nums are unique.

    算法分析

    首先想到要对数组进行升序排序
    从小向大遍历数组。假设数字 b % a == 0,那么可以考虑将数字b加入到包含a的链表里,b的前缀就是a;最终,我们挑选出长度最大的那个链表,将其中的全部数字放入到一个数组中,即可得到答案。
    但我们并不是找到任意一个满足 b % a == 0a 后,就立即将 b 加入到对应的链表里,而是应该选择 b 的全部约数中的最长的链表,将 b 加入该链表尾部。
    直接使用链表,不方便在O(1)的时间内直接得到链表的长度,所以我们可以创建一个数组 linkLen,linkLen[i] 即表示以 nums[i] 作为尾节点的链表的长度。另外,我们创建一个数组 parent,parent[i] 即表示 nums[i] 所在链表的前缀节点在 nums 数组的下标。如果 parent[i] == i,那么表示 nums[i] 是这个数组的头结点 ---- 没有前缀节点了。

    这里需要使用数学归纳法证明一下,我们将 b 加入到其所有约数中长度最大的链表中的正确性:

    将数字nums[i]加入到其全部约数中长度最大的链表尾部规则x
    初始令全部 linkLen[i] = 0

    • (1) 对于 nums 中的第一个nums[0],其没有前缀数字,所以作为链表头结点,有 parent[0] = 0,linkLen[0] = 1; 此时,最长链表即为 linkLen 中数字最大的元素对应的下标i,即 i = 0; 规则x正确;
    • (2) 假设当 i = k (k > 0),规则x 得到的 parent、linkLen 数组满足我们的要求 ---- 即 linkLen 数组中最大元素的下标,就是最长链表的尾节点元素在 nums 中的下标,假设该下标值为 index_k(至少有 linkLen[index_k] >= 1)。
      那么当 i = k + 1 时,遍历 j = 0 ~ k:
      • (2.1) 如果全部 nums[j] 均不为 nums[i] 的约数,那么应用规则x,nums[i] 将作为链表头结点,有 parent[i] = i, linkLen[i] = 1; 此时linkLen数组最大值依然为 linkLen[index_k]; 规则x正确。
      • (2.2) 如果某个 nums[j] 为 nums[i] 的约数,我们令 parent[i] = j, linkLen[i] = linkLen[j] + 1,那么:
        • (2.2.1) 如果 j == index_k,那么 linkLen[i] = linkLen[index_k] + 1,此时 linkLen[i] 为 linkLen 数组中的最大元素;规则x正确。
        • (2.2.2) 如果 j != index_k,那么 nums[index_k] 就不是 nums[i] 的约数,nums[i] 对以 nums[index_k] 为尾结点的链表无影响。linkLen[i] 或 linkLen[index_k] 为数组 linkLen 中的最大元素;规则x正确。

    综上所述,应用规则x,我们得到的 linkLen 数组,其最大元素对应的下标为i,那么以 nums[i] 为尾结点的链表即为最长链表。

    算法实现 Golang

    func largestDivisibleSubset(nums []int) []int {
    	sort.Sort(sort.IntSlice(nums))
    	N := len(nums) // 元素个数
    	parent := make([]int, N) // 使用类似并查集的算法,但是不对该并查集进行压缩
    	linkLen := make([]int, N)
    	maxLen := 0 // 最大链表长度
    	maxIndex := -1 // 最大链表尾结点元素在 nums 数组中的下标
    	for i, val := range nums {
    		parent[i] = i // 默认当前元素没有约数,则其前缀节点即为自身
    		linkLen[i] = 1 // 当前元素默认作为头结点,链表长度为 1
    		for j := 0; j < i && (val/nums[j] >= 2); j++ { // 因为 nums 数组是升序的,所以对于 val/nums[j] < 2 的 j 就无需再遍历了
    			if val%nums[j] == 0 {
    				if linkLen[j]+1 > linkLen[i] {
    					parent[i] = j
    					linkLen[i] = linkLen[j] + 1
    				}
    			}
    		}
    		// 内层 for 循环结束,nums[i] 即挂在到了正确的链表尾部
    
    		if linkLen[i] > maxLen {
    			// 记录全部链表中的最长链表长度和其尾元素的下标
    			maxLen = linkLen[i] 
    			maxIndex = i
    		}
    	}
    	cursor := maxIndex
    	ans := make([]int, maxLen) 
    	pos := linkLen[cursor] - 1
    	for parent[cursor] != cursor { // 如果 parent[cursor] == cursor,那么 nums[cursor] 即为链表头结点
    		ans[pos] = nums[cursor]
    		pos--
    		cursor = parent[cursor]
    	}
    	ans[pos] = nums[cursor] // 此时实际上 cursor = 0
    	return ans
    }
    

    运行结果

  • 相关阅读:
    Ubuntu配置sublime text 3的c编译环境
    ORA-01078错误举例:SID的大写和小写错误
    linux下多进程的文件拷贝与进程相关的一些基础知识
    ASM(四) 利用Method 组件动态注入方法逻辑
    基于Redis的三种分布式爬虫策略
    Go语言并发编程总结
    POJ2406 Power Strings 【KMP】
    nyoj 会场安排问题
    Server Tomcat v7.0 Server at localhost was unable to start within 45 seconds. If the server requires more time, try increasing the timeout in the server editor.
    Java的String、StringBuffer和StringBuilder的区别
  • 原文地址:https://www.cnblogs.com/dongling/p/15557933.html
Copyright © 2011-2022 走看看