数组类总结

目录

1.1 两数之和(简单):map存储 + 和变减

1.2 盛最多水的容器(中等):双指针

1.3 三数之和(中等):排序+双指针

1.4 最接近的三数之和(中等):排序 + 双指针

1.5 四数之和(中等):排序 + 双指针

1.6 删除有序数组中的重复项(简单):双指针

1.7 移除元素(简单):双指针

1.8 整数数组的最长连续序列

1.9 下一个排列(中等):数学题(很难)

1.10 在排序数组中查找元素的第一个和最后一个位置(中等):二分查找

1.11 全排序:回溯算法

1.12 二分查找(简单)

1.13 数组总结!!!


1.1 两数之和(简单):map存储 + 和变减

题目:给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。你可以按任意顺序返回答案。

思想:使用一个Map集合,将数组值和对应索引存入集合,当目标值 target - nus[i]的结果出现在Map中,说明找到了这两个数


总结:重点是知道list.add()方法的语法:

  • void add(int index,E element):将element插入指定元素index

  • void add(E e):将e插入到集合中


代码

class Solution {
    public int[] twoSum(int[] nums, int target) {
        //创建一个Map存储数组元素及索引
        Map map = new HashMap<>();
        for(int i = 0; i < nums.length; i++){
            //如果map中存在一个值与nums[i]相加等于target,则就是目标结果,返回二者的索引
            if(map.containsKey(target - nums[i])){
                return new int[]{i, map.get(target - nums[i])};
            }else{
                //将数组值和索引放入
                map.put(nums[i], i);
            }
        }
        //若都没有则返回null
        return null;
    }
}

1.2 盛最多水的容器(中等):双指针

题目:给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0)(i, height[i])

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。说明:你不能倾斜容器。

思想:使用两个指针,一开始两个指针r,l指向数组的左右两个边界,计算此时左右指针指向位置能够储存的最大水量为min(l,r) * (r-l),此时移动两个指针中较小的一个(移动较大的一个,最大水量值不会变大,只有移动较小的那个节点才能使得这个表达式的结果变大),将每次移动过程中取得的函数值求出,最终最大值就是目标值


总结:重点是知道list.add()方法的语法:

  • void add(int index,E element):将element插入指定元素index

  • void add(E e):将e插入到集合中


代码

class Solution {
    public int maxArea(int[] height) {
        //定义左右指针指向边界
        int l = 0;
        int r = height.length - 1;
        //定义结果值
        int res = 0;
​
        //求解存储的最大水量
        while(l < r){
            int temp = Math.min(height[l], height[r]) * (r - l);
            res = Math.max(res, temp);
            //比较左右指针所在索引对应的值,将较小值移动
            if(height[l] <= height[r]){
                l++;
            }else{
                r--;
            }
        }
        return res;
    }
}

1.3 三数之和(中等):排序+双指针

题目:给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

思想:对于不重复的要求,我们可以先对数组排序,然后使用三重循环即可满足要求;为了找到三数之和,可以先固定第一个数,剩下的两个数就可以使用双指针的方式寻找(因为a + b + c = 0,则固定a,b 和 c 就会相互变化,b、c可以使用双指针的形式变化)

  • 数组排序,利于不可重复性的实现

    • 每次取a或者b先判断是否和上一个值相等,相等则直接结束当前循环

  • 先枚举a,然后再a的右边取b,在数组的最左边取c,b与c构成双指针

    • 可以将三数之和的形势转为两数之和等于第三数(此时是c + b = -a)的形式

    • 注意b指针永远在c指针的左边

    • 当b == c时,指针相遇还没有找到目标就说明没有目标值


总结:重点是知道list.add()方法的语法:

  • void add(int index,E element):将element插入指定元素index

  • void add(E e):将e插入到集合中


代码

class Solution {
    public List> threeSum(int[] nums) {
        //存放结果的数组
        List> res = new ArrayList<>();
​
        //对数组排序,满足每次相加的不可重复性
        int n = nums.length;
        Arrays.sort(nums);
​
        //先枚举a
        for(int a = 0; a < n; a++){
            //排除下一次和这一次值相等的情况; 保证不可重复性
            if(a > 0 && nums[a] == nums[a - 1]){
                continue;
            }
​
            //让 c 从排好序数组的最左边开始取,进行双指针取值
            int c = n - 1;
            //设置一个目标值:因为a + b + c = 0; 故 b + c = -a
            int targer = -nums[a];
​
            //枚举b, b从a的右边开始取,c从数组的最右侧开始取,这就是双指针
            for(int b = a + 1; b < n; b++){
                //排除下一次和这一次值相等的情况
                if(b > a + 1 && nums[b] == nums[b - 1]){
                    continue;
                }
​
                //保证b 在 c 的左侧
                //计算b + c 的值,如果b + c > -a (因为是排好序的,从小到大排序),说明不是目标值
                while(b < c && nums[b] + nums[c] > targer){
                    c--;
                }
​
                //指针重合还没有目标值,就说明没有满足的情况了
                if(b == c){
                    break;
                }
                
                //如果有目标值
                if(nums[b] + nums[c] == targer){
                    List list = new ArrayList<>();
                    list.add(nums[a]);
                    list.add(nums[b]);
                    list.add(nums[c]);
                    res.add(list);
                }
            }
        }
​
        return res;
    }
}

1.4 最接近的三数之和(中等):排序 + 双指针

题目:给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。

返回这三个数的和。假定每组输入只存在恰好一个解。

思想:和上一道题很像,只不过上一道题是三数之和,这一道题是最接近三数之和,因此我们需要先求出三数之和,然后与target做差,取到最小差值即可

  • 先将三数之和求出sum,将sum与target比较

    • 若sum == target,直接返回sum;

    • 若不相等,则将sum和target的差值取最小值存下

      • 若sum < target, 说明需要将sum增大,将左指针增大

      • 若sum > target, 说明需要将sum减小, 将右指针减小


总结:重点是知道list.add()方法的语法:

  • void add(int index,E element):将element插入指定元素index

  • void add(E e):将e插入到集合中


代码

class Solution {
    public int threeSumClosest(int[] nums, int target) {
        Arrays.sort(nums);
        int n = nums.length;
​
        //结果一开始较大,为了与target作差取较小值
        int ans = 100000;
        
        for(int i = 0; i < n; i++){
            //判断重复性
            if(i > 0 && nums[i] == nums[i - 1]){
                continue;
            }
​
            int j = i + 1, k = n - 1;
​
            //当左指针小于右指针时循环
            while(j < k){
                int sum = nums[i] + nums[j] + nums[k];
​
                //比较sum和target的大小,取绝对值最小的情况,并根据大小移动指针
                if(sum == target){
                    return sum;
                }
                if(Math.abs(sum - target) < Math.abs(ans - target)){
                    ans = sum;
                }
                //较大,右指针左移,并判断重复性
                if(sum > target){
                    int k0 = k - 1;
                    while(j < k0 && nums[k0] == nums[k]){
                        k0--;
                    }
                    k = k0;
                }else{
                    int j0 = j + 1;
                    while(j0 < k && nums[j0] == nums[j]){
                        j0++;
                    }
                    j = j0;
                }
            }
        }
        return ans;
    }
}

1.5 四数之和(中等):排序 + 双指针

题目:给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  • 0 <= a, b, c, d < n

  • abcd 互不相同

  • nums[a] + nums[b] + nums[c] + nums[d] == target

思想:和前两道题很像,只不过上一道题是三数之和,这一道题是四数之和,一样的思路,先进行两次循环,然后对剩下的两个数使用双指针操作

  • 对数组排序,满足不可重复要求

  • 求和sum然后和与target比较

    • sum < target,则左指针右移

    • sum > target,则右指针左移

  • 剪枝操作:

    • 确认一个数之后:

      • nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target,说明后面的值都只能大于target(排序后),直接退出第一重循环

      • nums[i] + nums[n - 1] + nums[n - 2] + nums[n - 3] < target,说明再选的值只能小于target;直接进入下一轮循环,选择nums[i + 1]

    • 确认两个数之后:

      • nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target,说明后面的数都只能大于target,直接退出第二重循环

      • nums[i] + nums[j] + nums[n - 1] + nums[n - 2] < target,说明后面的数都只能小于target,直接进入下一轮循环,选择nums[j + 1]


总结:重点是知道list.add()方法的语法:

  • void add(int index,E element):将element插入指定元素index

  • void add(E e):将e插入到集合中


代码

class Solution {
    public List> fourSum(int[] nums, int target) {
        List> res = new ArrayList<>();
        //如果nums为null或长度小于0则直接返回
        if(nums == null || nums.length < 4){
            return res;
        }
        //拿到数组长度
        int n = nums.length;
        //对数组排序,满足不可重复要求
        Arrays.sort(nums);
​
        //双重循环i与j,对另外两个数进行双指针操作
        for(int i = 0; i < n - 3; i++){
            //对第一个数去重
            if(i > 0 && nums[i] == nums[i - 1]){
                continue;
            }
​
            //对第一个数的特殊情况剪枝
            //`nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target`,说明后面的值都只能大于target(排序后),直接退出第一重循环
            if(nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target){
                break;
            }
            //`nums[i] + nums[n - 1] + nums[n - 2] + nums[n - 3] < target`,说明再选的值只能小于target;直接进入下一轮循环,选择`nums[i + 1]`
            if(nums[i] + nums[n - 1] + nums[n - 2] + nums[n - 3] < target){
                continue;
            }
            for(int j = i + 1; j < n - 2; j++){
                //对第二个数去重
                if(j > i + 1 && nums[j] == nums[j - 1]){
                    continue;
                }
​
                //对前二个数的特殊情况剪枝
                //`nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target`,说明后面的数都只能大于target,直接退出第二重循环
                if(nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target){
                    break;
                }
                //`nums[i] + nums[j] + nums[n - 1] + nums[n - 2] < target`,说明后面的数都只能小于target,直接进入下一轮循环,选择`nums[j + 1]`
                if(nums[i] + nums[j] + nums[n - 1] + nums[n - 2] < target){
                    continue;
                }
​
                //对后两个数取双指针
                int k = j + 1, l = n - 1;
​
                //判断sum和target大小
                //sum较大,右指针左移
                while(k < l){
                    int sum = nums[i] + nums[j] + nums[k] + nums[l];
                    if(sum == target){
                        res.add(Arrays.asList(nums[i], nums[j], nums[k], nums[l]));
                        //去掉重复值
                        while(k < l && nums[k] == nums[k + 1]){
                            k++;
                        }
                        k++;
                        while(k < l && nums[l] == nums[l - 1]){
                            l--;
                        }
                        l--;
                    }else if(sum < target){
                        k++;
                    }else{
                        l--;
                    }
​
                }
            }
        }
        return res;
    }
}

1.6 删除有序数组中的重复项(简单):双指针

题目:给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

  • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。

  • 返回 k

思想:使用双指针p, q,一个在前,一个在后

  • 一开始比较p和q对应值

    • 若相等,则q向后移一位

    • 若不相等,则将q的位置元素复制到p+1上,qp都向右移动,直到q = nums.length


总结:重点是知道list.add()方法的语法:

  • void add(int index,E element):将element插入指定元素index

  • void add(E e):将e插入到集合中


代码

class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums == null || nums.length == 0) {
            return 0;
        }
        //定义两个指针指向开始的两个元素
        int p = 0;
        int q = 1;
​
        while(q < nums.length){
            //p 与 q值不相等,则将q所在值赋给p + 1,p向右移动;其他情况q++
            if(nums[p] != nums[q]){
                nums[p + 1] = nums[q];
                p++;
            }
            q++;
        }
        return p + 1;
    }
}

1.7 移除元素(简单):双指针

题目:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

思想:使用两个指针,left指向要赋值的数组的位置(在原数组上进行赋值),right指向当前元素

  • 如果right对应的值等于val,说明这个值需要移除,不能在输出数组中,此时left不动,right右移

  • 如果right对应的值不等于val,说明这个只需要添加在输出数组中,则将right复制给leftrightleft一起右移


总结:利用双指针满足题目不能使用额外数组空间的要求


代码

class Solution {
    public int removeElement(int[] nums, int val) {
        //设置左右指针
        int left = 0;
        int right = 0;
        for(right = 0; right < nums.length; right++){
            //如果`right`对应的值不等于`val`,说明这个只需要添加在输出数组中,则将`right`复制给`left`,`right`和`left`一起右移
            if(nums[right] != val){
                nums[left] = nums[right];
                left++;
            }
            //else的情况就是将right++;但for循环中已经给定了,所以不需要再处理
        }
        return left;
    }
}

1.8 整数数组的最长连续序列

题目:给你一个数组 nums ,求出其中的最长连续序列。比如输入为[1,2,3,5,6,9],输出为[1,2,3]

思想:将数组中的元素存入Set集合中:

  • Set中不包含num - 1这个值,则说明该值是一个可能结果值的起点(比如1和5)

    • 从这个值开始,若Set中包含currNum + 1,则说明这是一个连续序列

    • 判断这一个序列长度是否是最大的,更改最长序列长度,最终将该值存储下来


总结:先找到连续序列的起点,然后根据起点与集合元素找到该序列的其他值


代码

public class test{
    public static int[] longestSub(int[] nums){
        //数组为空,直接返回
        if(nums == null || nums.length == 0){
            return new int[0];
        }
        
        //创建一个Set集合存储数组的每个值
        Set set = new HashSet<>();
        for(int num : nums){
            set.add(nums);
        }
        
        //记录一个结果的最大长度值和序列的起始值
        int maxLength = 0;
        int start = 0;
        
        for(int num : nums){
        //如果当前值 - 1不包含在Set中,说明这是一个序列起点    
            if(!set.contains(num - 1)){
                int currNum = num;
                int currLength = 1;
                
                //从当前值出发,如果set集合中还存在currNum + 1的值,说明是结果序列的值
                while(set.contains(currNum + 1)){
                    currLength++;
                    currNums++;
                }
                
                //如果当前序列的长度大于最大长度,则更改最长长度和起始值
                if(currLength > maxLength){
                    maxLength = currLength;
                    start = num;
                }
            }
        }
        int[] res = new int[maxLength];
        //结果值为:start + i(第一个值就是1,第二个是start + 1, 第三个是start + 2、、、、、、)
        for(int i = 0; i < maxLength; i++){
            res[i] = start + i;
        }
        return res;
    }
    
    public static void main(String[] args){
        int[] arr = {1,2,3,5,6,9};
        int[] res = longestSub(arr);
        for(int n : res){
            System.out.pringln(n);
        }
    }
}

1.9 下一个排列(中等):数学题(很难)

题目:整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。

  • 例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3][1,3,2][3,1,2][2,3,1]

整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。

  • 例如,arr = [1,2,3] 的下一个排列是 [1,3,2]

  • 类似地,arr = [2,3,1] 的下一个排列是 [3,1,2]

  • arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。

给你一个整数数组 nums ,找出 nums 的下一个排列。

必须 原地 修改,只允许使用额外常数空间。

思想:

  • 我们需要将一个左边的「较小数」与一个右边的「较大数」交换,以能够让当前排列变大,从而得到下一个排列。

  • 同时我们要让这个「较小数」尽量靠右,而「较大数」尽可能小。当交换完成后,「较大数」右边的数需要按照升序重新排列。这样可以在保证新排列大于原来排列的情况下,使变大的幅度尽可能小。

以排列 [4,5,2,6,3,1] 为例:

  • 我们能找到的符合条件的一对「较小数」与「较大数」的组合为 222 与 333,满足「较小数」尽量靠右,而「较大数」尽可能小。

  • 当我们完成交换后排列变为[4,5,3,6,2,1],此时我们可以重排「较大数」右边的序列,序列变为[4,5,3,1,2,6]。

具体的:

对排列a从后往前排序,找到第一个顺序对(i, i + 1),满足a[i] < a[i + 1],较小数为a[i][i + 1, n]为下降序列

  • 找到(i, i + 1)后,从[i + 1, n]中从后往前查找第一个元素j满足a[i] < a[j],较大数为a[j]

  • 交换a[i]a[j],此时[i + 1, n]必为降序,反转[i + 1, n]使其变为升序


总结:先找到连续序列的起点,然后根据起点与集合元素找到该序列的其他值


代码

class Solution {
    public void nextPermutation(int[] nums) {
        //从后往前找到一个`较小数`
        int i = nums.length - 2;
        while(i >= 0 && nums[i] >= nums[i + 1]){
            i--;
        }
        //排除是最后一种排列的情况(比如[6,5,4,3,2,1])
        if(i >= 0){
            //从后往前找到一个`较大数`,且值要大于nums[i]
            int j = nums.length - 1;
            //从后往前找到
            while(j >= 0 && nums[i] >= nums[j]){
                j--;
            }
            //说明找到了符合条件的 i 和 j ;交换i 和 j 对应的值
            swap(nums, i, j);
        }
        //若是最后一种排列情况(降序排列,此时i = -1),则直接反转整个数组;否则,反转i + 1后的数组
        reverse(nums,i + 1);
    }
​
    //交换数组中 i 和 j 索引对应的位置
    public void swap(int[] nums, int i, int j){
        // int temp = nums[j];
        // nums[j] = nums[i];
        // nums[i] = temp;
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
​
    //反转数组中 i 后元素的顺序
    public void reverse(int[] nums, int i){
        int left = i;
        int right = nums.length - 1;
        while(left < right){
            swap(nums, left, right);
            left++;
            right--;
        }
    }
}

1.10 在排序数组中查找元素的第一个和最后一个位置(中等):二分查找

题目:给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

思想:由于数组nums是非递减的,因此可以使用二分查找满足题目需要的复杂度;

  • 目标值在数组中开始位置:在数组中第一个寻找到的大于等于target的值

  • 目标值在数组中结束位置:在数组中第一个寻找到的大于target的值的位置减1

  • target不存在于数组中,则返回[-1, -1]

  • 定义一个binarySearch(nums, target, flag)表示在数组中二分查询target的位置

    • flag为true,表示查询第一个寻找到的大于等于target的值

    • flag为false,表示查询第一个寻找到的大于target的值的位置


总结:使用二分查找时,注意记录最终查找值的索引应该是


代码

class Solution {
    public int[] searchRange(int[] nums, int target) {
        //找出数组中target的开始位置和结束位置
        int leftIndex = binarySearch(nums, target, true);
        int rightIndex = binarySearch(nums, target, false) - 1;
​
        //判断是否符合要求
        if(leftIndex <= rightIndex && nums[leftIndex] == target && nums[rightIndex] == target && rightIndex < nums.length){
            return new int[]{leftIndex, rightIndex};
        }
        return new int[]{-1, -1};
    }
​
    //根据flag值来执行:寻找target在nums中的第一个位置或者大于target的第一个位置
    public int binarySearch(int[] nums, int target, boolean flag){
        //定义二分查找的左右指针
        int left = 0;
        int right = nums.length - 1;
​
        //定义开始位置或者大于target的第一个位置:必须用nums.length;若数组只有一个值,则返回0
        int res = nums.length;
​
        //进行二分查找
        while(left <= right){
            int middle = (left + right) / 2;
            //如果是nums[middle] > target,此时求的是大于target的第一个值
            //如果是nums[middle] >= target,此时求得是等于target的第一个值
            if(nums[middle] > target || (flag && nums[middle] >= target)){
                right = middle - 1;
                res = middle;
            }else{
                left = middle + 1;
            }
        }
        return res;
    }
}

1.11 全排序:回溯算法

题目:给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案

思路:可以看作有n个排成一行的空格,从数组nums中取一个数向空格填入,每个数只能用一次

  • 定义一个标记数组vis标记已填过的数

  • 定义递归函数backtrack(first,output)表示从左往右填到第first位置,当前排列为output;存在两种状态:

    • first = n,说明我们已经填完了n个位置(下标从0开始),找到一个可行解就将output放入答案数组中,结束

    • first < n,此时考虑first位置要填写哪个数,如果要填写的数未被标记过,才将其填入,然后将其标记,并填写下一个数

  • 标记数组的另一种表达方式:将题目给定的n个数组nums划分为左右两个部分,左边表示已经填过的数的集合,右边表示待填数,在回溯时动态维护这个数组即可

    • 比如已经填写到first位置,则nums中的[0,fitst - 1]为已填过的数集合,[first,n - 1]表示待填数的集合,使用[first,n - 1]中的某一个数来填写第first位置(假设索引为i),填写完毕后将第i个数和第first个数交换,从而使得[0,first]为已填写过的数,[first + 1, n - 1]为待填写的数

总结:

代码:

class Solution {
    public List> permute(int[] nums) {
        //创建二维集合保存结果
        List> res = new ArrayList<>();
​
        //如果nums为空直接返回
        if(nums == null || nums.length == 0){
            return res;
        }
​
        //进行深度优先搜索的准备
        //1.记录每次搜索过的量
        boolean[] used = new boolean[nums.length];
        //2.记录当前存过的深度
        int depth = 0;
        //3.记录每一次的全排列内容(不能再dfs里面创建,否则会产生每次都创建一次数组的情况,浪费空间和时间)
        List list = new ArrayList<>();
        dfs(nums, used, depth, res, list);
​
        return res;
    }
​
    public void dfs(int[] nums, boolean[] used, int depth, List> res, List path){
        //若depth等于数组长度,说明已经搜索到了一个全排列
        if(depth == nums.length){
            //将path创建到res中,不能直接add(path),只能复制path;因为path是一个对象,每次只有一个,若每次直接add就会变为一样的path值,只能通过拷贝对象
            res.add(new ArrayList<>(path));
        }
​
        for(int i = 0; i < nums.length; i++){
            //从没有搜索过的节点开始
            if(!used[i]){
                //将nums[i]加入列表;并记录搜索情况
                path.add(nums[i]);
                used[i] = true;
​
                //搜索下一个情况,深度加1
                dfs(nums, used, depth + 1, res, path);
​
                //每次一个全排列搜索完毕,回溯到上一个状态
                path.remove(path.size() - 1);
                used[i] = false;
            }
        }
​
    }
}

1.12 二分查找(简单)

题目:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1

思想:二分查找:

  • 查找范围[left, right]

  • 查找条件left < right

  • 每次查找取查找范围的重点mid,然后比较nums[mid]、target的大小,重新划分数组即可


总结:先找到连续序列的起点,然后根据起点与集合元素找到该序列的其他值


代码

class Solution {
    public int search(int[] nums, int target) {
        //确定左右指针
        int left = 0;
        int right = nums.length - 1;
        
        //二分查找条件:left <= right
        while(left <= right){
            //找出中点
            int middle = (left + right) / 2;
​
            //判断
            if(nums[middle] == target){
                return middle;
            }else if(nums[middle] > target){
                right = middle - 1;
            }else{
                left = middle + 1;
            }
        }
        //查询完毕没有值,则返回-1
        return -1;
    }
}

1.13 数组总结!!!

像数组这种具有索引的可以随机访问的数据类型,往往处理问题时可以通过索引、排序、双指针、与集合转换等方面来进行:

  • 与集合相转换:

    • 我们需要用到数组的值和索引两部分,则创建一个Map集合,将其存入,以便使用;

    • 若只需要数组的值,则创建一个Set集合,存入使用;

  • 双指针:

    • 由于数组具有索引,通过两个指针的左右移动指向数组元素往往具有比较好的效果,占用时间和空间会更少

  • 排序:

    • 数组是可排序的,当需要查找元素时,通过排序可以解决很多问题

你可能感兴趣的:(leetcode,算法,职场和发展)