3.13《LeetCode零基础指南》第六讲贪心学习反思(有标记题目)哈希线性探测

一、

945. 使数组唯一的最小增量

难度中等203

给你一个整数数组 nums 。每次 move 操作将会选择任意一个满足 0 <= i < nums.length 的下标 i,并将 nums[i] 递增 1

返回使 nums 中的每个值都变成唯一的所需要的最少操作次数。

示例 1:

输入:nums = [1,2,2]
输出:1
解释:经过一次 move 操作,数组将变为 [1, 2, 3]。

示例 2:

输入:nums = [3,2,1,2,1,7]
输出:6
解释:经过 6 次 move 操作,数组将变为 [3, 4, 1, 2, 5, 7]。
可以看出 5 次或 5 次以下的 move 操作是不能让数组的每个值唯一的。

三种方法解题:

第一种排序法:

第二种计数法:

计算每个数字出现次数,再便利往后推即可

class Solution {
public:
    int minIncrementForUnique(vector<int>& nums) {
        vector<int>a(400001,0);
        int Max=0;
        int move=0;
        for(int i=0;i<nums.size();i++)
        {
            Max=max(Max,nums[i]);
            a[nums[i]]++;
        }
        for(int i=0;i<=Max;i++)
        {
            if(a[i]>1)
            {
                int t=a[i]-1;
                move+=t;
                a[i+1]+=t;
            }
        }
        int d=a[Max+1]-1;
        move+=d*(1+d)/2;
        return move;
    }
};

***********第三种线性探测法:(未学没搞太懂)

class Solution {
    int[] pos = new int [80000];
    public int minIncrementForUnique(int[] A) {
        Arrays.fill(pos, -1); // -1表示空位
        int move = 0;
        // 遍历每个数字a对其寻地址得到位置b, b比a的增量就是操作数。
        for (int a: A) {
            int b = findPos(a); 
            move += b - a;
        }
        return move;
    }
    
    // 线性探测寻址(含路径压缩)
    private int findPos(int a) {
        int b = pos[a];
        // 如果a对应的位置pos[a]是空位,直接放入即可。
        if (b == -1) { 
            pos[a] = a;
            return a;
        }
        // 否则向后寻址
        // 因为pos[a]中标记了上次寻址得到的空位,因此从pos[a]+1开始寻址就行了(不需要从a+1开始)。
        b = findPos(b + 1); 
        pos[a] = b; // 寻址后的新空位要重新赋值给pos[a]哦,路径压缩就是体现在这里。
        return b;
    }
}

---------------------------------------------------------------------------------------------------------------------------------

二、

611. 有效三角形的个数

难度中等354收藏分享切换为英文接收动态反馈

给定一个包含非负整数的数组 nums ,返回其中可以组成三角形三条边的三元组个数。

示例 1:

输入: nums = [2,2,3,4]
输出: 3
解释:有效的组合是: 
2,3,4 (使用第一个 2)
2,3,4 (使用第二个 2)
2,2,3

示例 2:

输入: nums = [4,2,3,4]
输出: 4

思路:先排序,然后使用找最后一个在有序数组中小于nums[i] + nums[j]的数的下标,与j相减就可以得到nums[i] 和nums[j]为小两边的情况下的配对情况;k值的设立:记录最后一个小于nums[i] + nums[j]的数,防止与nums[i] + nums[j]相等

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        int i,j;
        int num=0;
        for(i=0;i<nums.size();i++)
        {
            for(j=i+1;j<nums.size();j++)
            {
                int left = j + 1, right = nums.size() - 1, k = j;
                while (left <= right) {
                    int mid = (left + right) / 2;
                    if (nums[mid] < nums[i] + nums[j]) {
                        k = mid;
                        left = mid + 1;
                    }
                    else {
                        right = mid - 1;
                    }
                }
                num += k - j;
            }
        }
        return num;
    }
};

排序+双指针:

j和k作双指针,当j向右移动,nums[i] + nums[j]增大,则第三个数一定比原来的下标靠后,则k是一直增大的,所以k只用了n次;

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        int n = nums.size();
        sort(nums.begin(), nums.end());
        int ans = 0;
        for (int i = 0; i < n; ++i) {
            int k = i;
            for (int j = i + 1; j < n; ++j) {
                while (k + 1 < n && nums[k + 1] < nums[i] + nums[j]) {
                    ++k;
                }
                ans += max(k - j, 0);
            }
        }
        return ans;
    }
};

打败94%的双指针:

class Solution {
    public int triangleNumber(int[] nums) {
        Arrays.sort(nums);
        int n = nums.length;
        int res = 0;
        for (int i = n - 1; i >= 2; --i) {
            int l = 0, r = i - 1;
            while (l < r) {
                if (nums[l] + nums[r] > nums[i]) {
                    res += r - l;
                    --r;
                } else {
                    ++l;
                }
            }
        }
        return res;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值