Leetcode算法学习日志-611 Valid Triangle Number

Leetcode 611 Valid Triangle Number

题目原文

Given an array consists of non-negative integers, your task is to count the number of triplets chosen from the array that can make triangles if we take them as side lengths of a triangle.

Example 1:

Input: [2,2,3,4]
Output: 3
Explanation:
Valid combinations are: 
2,3,4 (using the first 2)
2,3,4 (using the second 2)
2,2,3

Note:

  1. The length of the given array won't exceed 1000.
  2. The integers in the given array are in the range of [0, 1000].

题意分析

给一个数组,求该数组的子集,使得子集元素能构成三角形,求三角形个数。

解法分析

本题首先想到的是采用深度优先搜索(回溯)的方法,构造子集树,来得到所有满足条件的三元组,此时要注意,如果没有剪枝函数,只有上界函数,回溯法相当于暴力破解法,因为所有的可能的三元组合都被找到,只是判断了下是否能够成三角形,这种方法会超时。

为了加入剪枝操作,考虑三角形两边之和大于第三边的特性,如果对数据首先进行排序,则当两个较小的数(在子集树上部)之和小于第三个数时,它们之和一定小于更大的数(子集树下层),这样就避免了一些递归搜索操作。注意,剪枝造成的效果就是在pop之后,原本需要调用back_track()进入右子树,但如果剪枝操作认定右子树的所有数据都不能得到所求解时,就可以提前return,跳过函数调用(右子树递归调用完上一级函数也会return,所以剪枝函数的作用就是判断条件成立后提前return,减少调用次数)。C++代码如下:

class Solution {
private:
	vector<int> temp;
	vector<int> nums;
	int count;
	int n;
	int sig;
public:
	void back_track(int k) {
		sig = 1;
		if (temp.size() == 3) {
			if ((temp[0] + temp[1])>temp[2]) {
				count++;
				return;
			}
			else {
				sig = 0;
				//temp.pop_back();
				return;
			}
		}
		if (k>n)
			return;
		temp.push_back(nums[k - 1]);
		/*for (auto ww : temp)
			cout << ww << ends;
		cout << endl;*/
		back_track(k + 1);
		temp.pop_back();
		if (sig == 0)
		{
			sig = 1;//remenber to be 1
			return;

		}
		else
			back_track(k + 1);
	}
	int triangleNumber(vector<int>& number) {
		nums = number;
		sort(nums.begin(), nums.end());
		n = nums.size();
		count = 0;
		back_track(1);
		return count;
	}
};
无论如何,回溯法是一种近似遍历的方法,复杂度较高,本题有一种巧妙的思路,充分运用两边之和大于第三边的性质,并且,如果对数据进行排序后,如果两个较小数据之和大于较大数据,则一定能得到它们是三角形的边,因为两个较小边之差必然小于较大边,这两个条件结合在一起就能保证三角形的性质。

从最大数开始遍历,定义两个下标l和r,r=i-1,C++代码如下:

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        
        int count = 0, n = nums.size();
        for (int i = n-1; i >= 2; --i) {
            int l = 0, r = i-1;
            while (l < r) {
                if (nums[l]+nums[r] > nums[i]) {
                    count += r - l;
                    -- r;
                } else
                    ++ l;
            }
        }
        return count;
    }
};
例如{2,2,3,4},当最大数为4时,较小边为2和3,且2+3>4,它们能构成三角形,由于第一个2右边的数一定大于等于它,则2和3中间的数与3,4一起一定能构成三角形,这也就是下面两句代码的含义:

if (nums[l]+nums[r] > nums[i]) {
            count += r - l;
由于r和l是相向移动,复杂度为O(n),总的算法复杂度为O(n^2)。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值