三个数的和小于等于k

给一个数组以及一个数K, 从这个数组里面选择三个数,使得三个数的和小于等于K, 有多少种选择的方法?(不包括重复的情况)

Example:
Input: 
nums = [3,2,5,2,1,4,2,3]
k = 7
Output:
6  # [1,2,4], [1,2,3], [1,2,2], [1,3,3], [2,2,2], [2,2,3]
解题思路:

这个题是“三个数的和等于K”的变形,主要难点在于去重。首先,还是先列表从小到大排序,然后外循环遍历 nums[0...n-2],将三个数问题转化为两个数问题。

在两个数的和小于等于K的问题中,同样设置高低指针,然后判断低指针指向的元素与高指针指向的元素之和是否小于等于K,如果不是,高指针向左移动;否则,数出高低指针中间有多少个不重复的组合,然后低指针向右移动。当高低指针相遇,内循环结束,也需要 O(n) 的时间。

总共需要的时间复杂度为 O(n^2)。

前面提到,难点在于去除重复的组合数。这里以上述例子来分析:

  1. 得到排序后的 nums = [1,2,2,2,3,3,4,5] ,外循环先取第一个数 1,将问题转化为在 [2,2,2,3,3,4,5] 中找到下于等于 k-1 = 6 的两个数。
  2. 内循环高低指针 low -> 2,high -> 5,然后 high 后移,high -> 4,这个时候,由第一个 2 作为第二个数,与其组合有的 [2,2] [2,2] [2,3] [2,3] [2,4],但是要去除重复的 [2,2] 和 [2,3]。
  3. 这里我们开辟一个 O(n) 的空间 dup[0..n-1],dup[i] 记录第 i 个元素之前共出现了多少次重复的元素。比如 nums = [1,2,2,2,3,3,4,5] ,我们可以得到 dup = [0,0,1,2,2,3,3,3]。我们记录了 low 和 high 之间有5种组合(high - low),还要去除 low->2、high -> 4 之间的位置出现的重复次数 3(dup[6] - dup[0] = 3)。但是这时相当于我们多去除了一种组合,即我们把第一个 [2,2] 也给去除了,所以要多补充1。但是,也存在这样的情况,比如 k = 10,low -> 4, high - > 5,这时我们用 (high - low)再减去 low、high 之间重复的元素 0 (dup[7] - dup[6] = 0),结果为1,就是 [4,5] 这种情况,但是这时不需要再加1,因为 4 和 5 两个数字不相同,所以不存在多除去 1 种组合的情况。因此,在这里,我们要注意分两个情况,一种是 nums[low] == nums[low+1],要多补充1,否则不用补充。
  4. 将 low 移动到下一个不重复的数字处 low -> 3,可以找到 [3,3],然后low、high相遇,内循环遍历结束。
  5. 外循环接着重复取数 nums[2...n-2](但是要跳过相同的数字,比如该次取得数为 2,下一次外循环要跳过后面所有相同的 2,直接到下一个不同的数字 3 那里),然后内循环进行指针移动操作,可以找到所有不重复的组合数。

空间复杂度:O(n)

Python 实现:
class Solution:
    """
    @param nums: 数组
    @param k: 3个数的和小于等于k
    @return: 3个数小于等于k的个数(相同的组合次数只记为一次)
    """
    def threeLtEqK(self, nums, k):
        if len(nums) <= 2:
            return 0
        nums.sort() # 排序
        dup = self.statisDupliNums(nums) # 统计重复的元素
        count = i = 0
        while i < len(nums) - 2:
            count += self.twoLtEqK(nums[i+1:], i+1, k-nums[i], dup) # 将3个数问题转化为两个数问题
            while nums[i] == nums[i+1]: # 去除重复走过的元素
                i += 1
            i += 1
        return count

    # 统计排好序的列表中第i个元素前累积出现的重复的元素次数
    # 如 [1,2,2,2,3,3],返回 [0,0,1,2,2,3]
    def statisDupliNums(self, nums):
        dup = [0] * len(nums)
        for i  in range(1, len(nums)):
            if nums[i] == nums[i-1]:
                dup[i] = dup[i-1] + 1
            else:
                dup[i] = dup[i-1]
        return dup
    
    # 转化为两个数的和小于等于k的问题
    def twoLtEqK(self, nums, nextIndix, k, dup):
        count = 0
        low, high = 0, len(nums) - 1
        while low < high:
            if nums[low] + nums[high] <= k:
                # 去重重复组合数后的数量
                if nums[low] == nums[low+1]:  # 这种情况下多去除了一次,所以补1
                    noDupNum = (high - low) - (dup[high+nextIndix] - dup[low+nextIndix]) + 1
                else:
                    noDupNum = (high - low) - (dup[high+nextIndix] - dup[low+nextIndix])                    
                count += noDupNum
                while nums[low] == nums[low+1]:
                    low += 1
                low += 1 # 从下一个非重复的元素开始
            else:
                high -= 1
        return count

a = [3,2,5,2,1,4,2,3]
print(Solution().threeLtEqK(a,7)) # 6
print(Solution().threeLtEqK(a,10)) # 16
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容

  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 12,716评论 0 33
  • 这篇文章是关于Java static关键字的使用,主要会介绍以下的内容: static 的概念 static的各种...
    byhieg阅读 1,107评论 1 49
  • 生命总是在不断挣扎求存的过程中获得意义与力量。 布克的父亲是一条体格魁梧的圣伯纳犬,母亲是苏格兰牧羊犬。体重一百四...
    来自世界的美意阅读 1,237评论 0 1
  • “也许他们真的老了” 这个国庆,本来是不太想回家的。妈妈多次电话里说想我回家,那就回去陪陪她吧。在学校每隔三天我总...
    水木易安V阅读 193评论 0 0