每周算法(一)

1370 上升下降字符串

给你一个字符串 s ,请你根据下面的算法重新构造字符串:

从 s 中选出 最小 的字符,将它 接在 结果字符串的后面。
从 s 剩余字符中选出 最小 的字符,且该字符比上一个添加的字符大,将它 接在 结果字符串后面。
重复步骤 2 ,直到你没法从 s 中选择字符。
从 s 中选出 最大 的字符,将它 接在 结果字符串的后面。
从 s 剩余字符中选出 最大 的字符,且该字符比上一个添加的字符小,将它 接在 结果字符串后面。
重复步骤 5 ,直到你没法从 s 中选择字符。
重复步骤 1 到 6 ,直到 s 中所有字符都已经被选过。
在任何一步中,如果最小或者最大字符不止一个 ,你可以选择其中任意一个,并将其添加到结果字符串。

请你返回将 s 中字符重新排序后的 结果字符串

class Solution {
    public String sortString(String s) {
        //不停地按字典序    升序追加,降序追加,每次加一个
        // 动态数组 Stringbuffer其实是动态字符串数组 append 放在一起

        int[] tmp = new int[26];
        for(int i=0; i < s.length(); i++){
            tmp[s.charAt(i) -'a']++;
        }
        StringBuffer ret = new StringBuffer();
        while (ret.length() < s.length()) {
            for(int i =0; i < 26; i++){
                if (tmp[i] > 0){
                    ret.append((char) (i + 'a'));
                    tmp[i]--;
                }
            }
            for(int i = 25; i >= 0; i--){
                if (tmp[i] > 0){
                    ret.append((char) (i + 'a'));
                    tmp[i]--;
                }
            }
        }
        return ret.toString();
    }
}

1470.重新排序数组

给你一个数组 nums ,数组中有 2n 个元素,按 [x1,x2,…,xn,y1,y2,…,yn] 的格式排列。

请你将数组按 [x1,y1,x2,y2,…,xn,yn] 格式重新排列,返回重排后的数组。

给定的数组nums拆分成两个数组,分别存放X和Y,这样在从0遍历一次n,依次将分的2个数组存放到新数组中通过双指针的方式进行解决

class Solution {
    public int[] shuffle(int[] nums, int n) {
        int temp[] = new int[nums.length];
        int index = 0;
        for(int i = 0; i < n; i++){
            temp[index++] = nums[i];
            temp[index++] = nums[i+n];
        }
        return temp;
    }
}

283.移动0

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
class Solution {
    public void moveZeroes(int[] nums) {
        int n = nums.length, left = 0,right = 0;
        while (right < n) {
            if(nums[right] != 0){
                mobile(nums, left, right);
                left++;
            }
            right++;
        }
    }
    public void mobile(int[] nums, int left, int right){
        int temp = nums[left];
        nums[left] = nums[right];
        nums[right] = temp;
    }
}

164.最大间距

给定一个无序的数组,找出数组在排序之后,相邻元素之间最大的差值。

如果数组元素个数小于 2,则返回 0。

示例 1:

输入: [3,6,9,1]
输出: 3
解释: 排序后的数组是 [1,3,6,9], 其中相邻元素 (3,6) 和 (6,9) 之间都存在最大差值 3。

示例 2:

输入: [10]
输出: 0
解释: 数组元素个数小于 2,因此返回 0。

方法一:基数排序

一种最简单的思路是将数组排序后再找出最大间距,但传统的基于比较的排序算法(快速排序、归并排序等)均需要 O(N\log N)O(NlogN) 的时间复杂度。我们必须使用其他的排序算法。例如,基数排序可以在 O(N)O(N) 的时间内完成整数之间的排序。

class Solution {
    public int maximumGap(int[] nums) {
        //需要了解stream 还有四种状态
        int n = nums.length;
        if(n < 2){
            return 0;
        }
        long exp = 1;
        int[] buf = new int[n];
        int maxVal = Arrays.stream(nums).max().getAsInt();

        while(maxVal >= exp) {
            int[] cnt = new int[10];
            for(int i = 0; i < n;i++){
                int digit = (nums[i] / (int) exp) % 10;
                cnt[digit]++;
            }
            for(int i = 1; i < 10; i++){
                cnt[i] += cnt[i - 1];
            }
            for(int i = n - 1; i >= 0; i--){
                int digit= (nums[i] / (int) exp) % 10;
                buf[cnt[digit] -1] = nums[i];
                cnt[digit]--;
            }
            System.arraycopy(buf, 0, nums, 0, n);
            exp *= 10;
        }

        int ret = 0;

        for(int i = 1; i < n; i++){
            ret = Math.max(ret, nums[i] - nums[i - 1]);
        }
        return ret;

    }
}

方法二

思路与算法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9ssZ7OME-1607478247424)(C:\Users\heng\Desktop\snipaste20201126_185559.png)]

因此,在找出每个元素所在的桶之后,我们可以维护每个桶内元素的最大值与最小值。随后,只需从前到后不断比较相邻的桶,用后一个桶的最小值与前一个桶的最大值之差作为两个桶的间距,最终就能得到所求的答案

class Solution {
    public int maximumGap(int[] nums) {
        int n = nums.length;
        if (n < 2) {
            return 0;
        }
        int minVal = Arrays.stream(nums).min().getAsInt();
        int maxVal = Arrays.stream(nums).max().getAsInt();
        int d = Math.max(1, (maxVal - minVal) / (n - 1));
        int bucketSize = (maxVal - minVal) / d + 1;

        int[][] bucket = new int[bucketSize][2];
        for (int i = 0; i < bucketSize; ++i) {
            Arrays.fill(bucket[i], -1); // 存储 (桶内最小值,桶内最大值) 对, (-1, -1) 表示该桶是空的
        }
        for (int i = 0; i < n; i++) {
            int idx = (nums[i] - minVal) / d;
            if (bucket[idx][0] == -1) {
                bucket[idx][0] = bucket[idx][1] = nums[i];
            } else {
                bucket[idx][0] = Math.min(bucket[idx][0], nums[i]);
                bucket[idx][1] = Math.max(bucket[idx][1], nums[i]);
            }
        }

        int ret = 0;
        int prev = -1;
        for (int i = 0; i < bucketSize; i++) {
            if (bucket[i][0] == -1) {
                continue;
            }
            if (prev != -1) {
                ret = Math.max(ret, bucket[i][0] - bucket[prev][1]);
            }
            prev = i;
        }
        return ret;
    }
}

454.四数相加 ||

给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。

为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N,且 0 ≤ N ≤ 500 。所有整数的范围在 -228 到 228 - 1 之间,最终结果不会超过 231 - 1 。

输入:
A = [ 1, 2]
B = [-2,-1]
C = [-1, 2]
D = [ 0, 2]

输出:
2

解释:
两个元组如下:
1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0

使用二重循环对它们进行遍历,得到所有A[i] + B[j]的值 并存入哈希映射中

方法一 : 分组 + 哈希表

class Solution {
    public int fourSumCount(int[] A, int[] B, int[] C, int[] D) {
        Map<Integer,Integer> countAB = new HashMap<Integer,Integer>();
        for(int u : A){
            for(int v : B){
                countAB.put(u + v,countAB.getOrDefault(u + v, 0) + 1);	
            }
        }
        int ans = 0;
        for(int u : C){
            for(int v : D){
                if(countAB.containsKey(-u -v)){
                    ans += countAB.get(-u -v);
                }
            }
        }
        return ans;
    }
}

方法二 : 暴力干

class Solution {
    public int fourSumCount(int[] A, int[] B, int[] C, int[] D) {
        Map<Integer, Integer> map = new HashMap<>();
        //分组  循环 得总数 判断 是否有总数值 在添加进map  +1 是数组从0开始
        int res = 0;
        for(int i = 0;i<A.length;i++){
            for(int j= 0;j<B.length;j++){
                int sumAB = A[i]+B[j];
                if(map.containsKey(sumAB)) map.put(sumAB,map.get(sumAB)+1);
                else map.put(sumAB,1);
            }
        }

        for(int i = 0;i<C.length;i++){
            for(int j = 0;j<D.length;j++){
                int sumCD = -(C[i]+D[j]);
                if(map.containsKey(sumCD)) res += map.get(sumCD);
            }
        }
        return res;
    }
}

1.两数之和

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

方法二 : 哈希表

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer,Integer> hashtable = new HashMap<Integer,Integer>();
        for(int i = 0; i < nums.length; i++){
            if(hashtable.containsKey(target - nums[i])){
                return new int[] {
                hashtable.get(target - nums[i]), i
            };
            }
            hashtable.put(nums[i], i);
        }
        return new int[0];
    }   
}

493.翻转对

给定一个数组 nums ,如果 i < j 且 nums[i] > 2*nums[j] 我们就将 (i, j) 称作一个重要翻转对。

你需要返回给定数组中的重要翻转对的数量。

方法一: 归并排序

思路及解法(官方)

在归并排序的过程中,假设对于数组 \textit{nums}[l…r]nums[l…r] 而言,我们已经分别求出了子数组 \textit{nums}[l…m]nums[l…m] 与 \textit{nums}[m+1…r]nums[m+1…r] 的翻转对数目,并已将两个子数组分别排好序,则 \textit{nums}[l…r]nums[l…r] 中的翻转对数目,就等于两个子数组的翻转对数目之和,加上左右端点分别位于两个子数组的翻转对数目。

我们可以为两个数组分别维护指针 i,ji,j。对于任意给定的 ii 而言,我们不断地向右移动 jj,直到 \textit{nums}[i] \le 2\cdot \textit{nums}[j]nums[i]≤2⋅nums[j]。此时,意味着以 ii 为左端点的翻转对数量为 j-m-1j−m−1。随后,我们再将 ii 向右移动一个单位,并用相同的方式计算以 ii 为左端点的翻转对数量。不断重复这样的过程,就能够求出所有左右端点分别位于两个子数组的翻转对数目。

个人理解

class Solution {
    public int reversePairs(int[] nums) {
        if(nums.length == 0){
            return 0;
        }
        return reversePairsRecursive(nums, 0, nums.length - 1);
    }

    public int reversePairsRecursive(int[] nums, int left, int right){
        if(left == right){
            return 0;
        } else {
            int mid = (left + right) / 2;
            int n1 = reversePairsRecursive(nums, left, mid);
            int n2 = reversePairsRecursive(nums, mid + 1, right);
            int ret = n1 + n2;

            //首先 统计 下标对的数量
            int i = left;
            int j = mid + 1;
            while (i <= mid){
                while (j <= right && (long) nums[i] > 2 * (long) nums[j]){
                    j++;
                }
                ret += j - mid - 1;
                i++;
            }

            //随后合并两个排序数组
            int[] sorted = new int[right - left + 1]; 
            int p1 = left, p2 = mid + 1;
            int p = 0;
            while(p1 <= mid || p2 <= right) {
                if(p1 > mid){
                    sorted[p++] = nums[p2++];
                } else if(p2 > right){
                    sorted[p++] = nums[p1++];
                } else {
                    if(nums[p1] < nums[p2]){
                        sorted[p++] = nums[p1++];
                    } else {
                        sorted[p++] = nums[p2++];
                    }
                }
            }
            for(int k = 0; k < sorted.length; k++){
                nums[left + k] = sorted[k];
            }
            return ret;
        }
    }
}

976.三角形的最大周长

给定由一些正数(代表长度)组成的数组 A,返回由其中三个长度组成的、面积不为零的三角形的最大周长。

如果不能形成任何面积不为零的三角形,返回 0

示例 1:

输入:[2,1,2]
输出:5

示例 2:

输入:[1,2,1]
输出:0

方法: 贪心 + 排序

class Solution {
    public int largestPerimeter(int[] A) {
        Arrays.sort(A);
        for(int i = A.length - 1; i >= 2; --i){
            if(A[i - 2] + A[i - 1] > A[i]){
                return A[i - 2] + A[i - 1] + A[i];
            }
        }
        return 0;
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/unique-paths/solution/bu-tong-lu-jing-by-leetcode-solution-hzjf/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值