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
    }
    

    运行结果

  • 相关阅读:
    HDU 1941 Justice League
    HDU 1960 Taxi Cab Scheme
    POJ 1986 Distance Queries
    UVA 11991 Easy Problem from Rujia Liu?
    sql的跟踪与Tkprof工具
    ORA04031 错误
    Oracle_spatial的空间索引
    oracle发生重启动的介绍
    expdp\impdp及exp\imp
    oracle锁
  • 原文地址:https://www.cnblogs.com/dongling/p/15557933.html
Copyright © 2011-2022 走看看