数组习题总结2

目录

一.单词搜索(深度优先遍历)

1.题目

2.思路图解

3.代码

 二.集合中的所有子集(子集有序且不能有重复元素)

1.题目

2.图解

3.代码

三.求滑动窗口中的最大值

1.题目

2.思路图解

3.代码

四.下一个排列

1.题目

2.思路图解

3.代码实现

五.组合总和(二)

1.题目

2.图解思路

3.代码

六.三数之和

1.题目

2.思路图解

3.代码

七.搜索旋转排序数组

1.题目

2.思路图解

3.代码


一.单词搜索(深度优先遍历)

1.题目

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/word-search
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

示例:

2.思路图解

3.代码

 public boolean exist(char[][] board, String word) {
        boolean[][] valid = new boolean[board.length][board[0].length];
        //每一个位置开始进行寻找
        for(int i=0; i<board.length; i++) {
            for(int j=0; j<board[0].length; j++) {
                if(dfs(board, word, i, j, 0, valid)){
                    return true;
                }
            }
        }
        return false;
    }
    public boolean dfs(char[][] b, String str, int i, int j, int begin, boolean[][] valid) {
        //位置不合法
        if(i<0 || j<0 || i>=b.length || j>=b[0].length) {
            return false;
        }
        //当前位置已经访问过
        if(valid[i][j] ==true) {
            return false;
        }
        //当前字符不匹配
        if(str.charAt(begin) != b[i][j]) {
            return false;
        }
        //找到
        if(begin == str.length()-1) {
            return true;
        }
        //当前字符匹配,从该字符开始上下左右依次进行寻找
        valid[i][j] = true;
        boolean f = dfs(b, str, i+1, j, begin+1, valid) ||
        dfs(b, str, i-1, j, begin+1, valid) ||
        dfs(b, str, i, j+1, begin+1, valid) ||
        dfs(b, str, i, j-1, begin+1, valid);
        //如果找到就放回true
        if(f==true) {
            return true;
        }
        //没找到,回溯到上一个位置,继续寻找
        valid[i][j] = false;
        return false;  
    }

 二.集合中的所有子集(子集有序且不能有重复元素)

1.题目

给定一个整数数组 nums ,其中可能包含重复元素,请你返回这个数组的所有可能子集。

返回的答案中不能包含重复的子集,将答案按字典序进行排序。

示例:

输入:[1,2]

返回值:[[],[1],[1,2],[2]]

2.图解

3.代码

    ArrayList<ArrayList<Integer>> res = new ArrayList<>();
    public ArrayList<ArrayList<Integer>> subsets (int[] nums) {
        //保持子集有序,需要先进行排序
        Arrays.sort(nums);
        dfs(nums,0, new ArrayList<>());
        return res;
    }
    public void dfs(int[] nums, int begin, ArrayList<Integer> list) {
        //添加每一个创建的有序子集放到结果集合中
        res.add(new ArrayList<>(list));
        for(int i=begin; i<nums.length; i++) {
            //排除当前已经重复且处理过的元素
            if(i>begin && nums[i]==nums[i-1]) {
                continue;
            }
            //添加元素到子集合中
            list.add(nums[i]);
            //以当前元素为起点依次向后进行处理
            dfs(nums, i+1, list);
            //删除当前集合最后一个结点
            list.remove(list.size()-1);
        }
        
    }

三.求滑动窗口中的最大值

1.题目

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值 

如图所示:

2.思路图解

借助优先级队列存储最大值,借助数组来存储当前元素索引位置,之后根据计算判断该优先级队列堆顶元素是否在滑动窗口内。

3.代码

 public int[] maxSlidingWindow(int[] nums, int k) {
        //使用优先级队列和数组(数组中存储值和索引位置,通过索引位置判断是否最大元素在窗口中
        //不在就进行删除)
        //建立大堆
        PriorityQueue<int[]> queue = new PriorityQueue<>((int[] a1, int[] a2)->{
            return a1[0]==a2[0]?a2[1]-a1[1]:a2[0]-a1[0];
        });
        //先存储一部分滑动窗口的值
        for(int i=0; i<k; i++) {
            queue.offer(new int[]{nums[i], i});
        }
        int[] res = new int[nums.length-k+1];
        res[0] = queue.peek()[0];
        int index =1;
        //通过比较并添加
        for(int i=k; i<nums.length; i++) {
            //将下一个滑动窗口的值加入到队列中
            queue.offer(new int[]{nums[i], i});
            //说明最大值没有包含在滑动窗口中,需要进行删除
            while(queue.peek()[1]+k<=i) {
                queue.poll();
            }
            //找到
            res[index++] = queue.peek()[0];
        }
        return res;

    }

四.下一个排列

1.题目

给定一个数组,将数组重新排列,得到一系列数组排列S,请你从S中,找出恰好比当前数组排列字典序大于1的数组排列。

1.[1,2,3]的得到的数组排列S有:[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]。

2.该题数组排列的字典序大小排序规则:2个数组排列的元素按顺序比较,直到数组元素不相等为止,不相等的第一个元素,谁的元素大,谁的字典序比较大,比如数组a=[1,2,3]与数组b=[1,3,2]比较:a[0]=b[0],a[1]<b[1],此时出现了第一个不相同的,且a[1]<b[1],则a的字典序小于b的字典序。且[1,3,2]的字典序在排列S中,正好在[1,2,3]的后面,视为[1,3,2]的字典序比[1,2,3]的字典序大于1。

3.如果不存在更大的数组排列,则返回最小的数组排列。

示例:

输入:[1,2,3]

返回值:[1,3,2]

2.思路图解

思路:

首先从后往前找第一个非递减的位置(目的是为了让最近位置的元素变大,从而使影响的变化最小)。
然后再从后面找第一个比当前位置元素大的值(最后面是低位,影响的结果值最小),将这两个位置的元素进行交换(说明该结果就变大了)。
再将该元素后面的所有元素进行反转(因为刚开始找的位置的元素肯定比后一个元素值小,进行交换后,该元素到了后面,为了增加的最少,反转之后,大的值就到了后面)。

图解:

3.代码实现

 public int[] nextPermutation (int[] nums) {
        int n = nums.length;
        int i = n-2;
        int j = n-1;
        int k = n-1;
        //从后向前找,首先找非递减的第一个数,然后找比当前数大的最后出现的元素
        //然后再将第一个出现得数得后面的反转即可
        while(i>=0 && nums[i]>=nums[j]) {
            i--;
            j--;
        }
        if(i>=0) {
            while(nums[i]>nums[k]) {
                k--;
            }
            swap(nums, i, k);
        }
        reverse(nums, j, n-1);
        return nums;
        
    }
    //交换指定位置的元素
    public void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
    //反转指定位置的元素
    public void reverse(int[] arr, int beign, int end) {
        while(beign<end) {
            swap(arr, beign, end);
            beign++;
            end--;
        }
    }

五.组合总和(二)

1.题目

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 一次 。

注意:解集不能包含重复的组合。 

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/combination-sum-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

示例:

输入: candidates = [2,5,2,1,2], target = 5,
输出:[[1,2,2], [5]]

2.图解思路

3.代码

 List<Integer> list = new ArrayList<>();
    List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        //先排序
        Arrays.sort(candidates);
        dfs(candidates, target, 0);
        return res;
    }
    public void dfs(int[] arr, int target, int index) {
        //求得结果值,加入结果集中
        if(target==0) {
            res.add(new ArrayList<>(list));
            return;
        }
        //依次向后处理每一位数字
        for(int i=index; i<arr.length; i++) {
            //说明之后的所有元素都超过了target的结果(有序),相当于剪枝
            if(target-arr[i]<0) {
                break;
            }
            //说明在同一层,且为同一个元素,可以跳过(相当于去重)
            if(i>index && arr[i]==arr[i-1]) {
                continue;
            }
            //没有超过结果,加入到临时结果集中
            list.add(arr[i]);
            //继续向下一层进行递归处理
            dfs(arr, target-arr[i], i+1);
            //说明当前层当前位置的元素已经处理完成,删除掉处理当前层的下一个元素
            list.remove(list.size()-1);
        }   
    }

六.三数之和

1.题目

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/3sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

示例:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]

2.思路图解

首先对数组进行排序,然后再固定一个元素(如果前一个元素和当前元素相等,就跳过当前元素,相当于去重),另外两个元素从当前元素的后面进行寻找,寻找的方法是使用双指针来进行寻找,如果固定元素+左边界元素+右边界元素>0,说明需要左移右边界;如果<0,就需要右移左边界;如果相等,就将结果存储在结果集中,存储之后,还需要判断左边界的后一个是否和左边界相等,右边界的前一个是否和右边界相等,如果有相等的,就移动边界,相当于去重,之后处理完当前左右边界的元素后,同时移动左右边界继续寻找满足的结果集。

3.代码

 public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> res = new ArrayList<>();
        //思路:固定一个元素,然后寻找另外两个元素
        for(int i=0; i<nums.length; i++) {
            if(i>0 && nums[i]==nums[i-1]) continue;
            int left = i+1;
            int right = nums.length-1;
            while(left<right) {
                //计算3个元素的和
                int tmp = nums[i]+nums[left]+nums[right];
                if(tmp==0) {
                    List<Integer> list = new ArrayList<>();
                    list.add(nums[i]);
                    list.add(nums[left]);
                    list.add(nums[right]);
                    res.add(list);
                    //去重
                    while(left<right && nums[left]==nums[left+1]) left++;
                    while(left<right && nums[right]==nums[right-1]) right--;
                    //处理完当前符合结果的左右边界,向里继续处理
                    left++;
                    right--;
                }else if(tmp<0) {
                    //说明结果值需要扩大,左边界右移
                    left++;
                }else {
                    right--;
                }
            }
        }
        return res;
    }

 

七.搜索旋转排序数组

1.题目

整数数组 nums 按升序排列,数组中的值 互不相同 。

在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。

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

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/search-in-rotated-sorted-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

示例:

输入:nums = [4,5,6,7,0,1,2], target = 4
输出:0

2.思路图解

由于二分查找只能在有序的数组区间中进行查找,所以根据这个思路来进行求解。根据题意得,如果将一个有序旋转过的数组分成两份,一部分的元素全部有序,一部分的元素部分有序,我们可以根据需要查找的元素和有序数组区间的首尾进行比较,如果在当前有序序列中,就缩小为当前有序范围;否则可能在另一部分的数组中。

 

3.代码

 public int search(int[] nums, int target) {
        //在有序的数组区间中进行查找
        int left = 0;
        int right = nums.length;
        while(left<right) {
            int mid = left+(right-left)/2;
            if(nums[mid] == target) {
                return mid;
            }else if(nums[left]<nums[mid]){//说明左边有序
                if(nums[left]<=target && nums[mid]>=target) {
                    //说明结果值在有序的一边
                    right = mid;
                }else {
                    //说明结果值在无序的一边
                    left = mid+1;
                }
            }else {
                //说明右边有序
                if(nums[mid]<=target && nums[right-1]>=target) {
                    //说明在右边有序的序列中
                    left = mid+1;
                }else {
                    //说明在无序的左边
                    right = mid;
                }
            }
        }
        return -1;
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值