数据结构与算法-数组

二分查找 

1. 基础版(返回插入点) 

 public static int version1(int[] nums, int target) {
        /*
         * 要求:
         *    参数:一个有序,从小到大,无重复元素的数组;一个目标值
         *    找到返回目标值索引,没找到返回-i-1
         *
         * 复习的时候自己重新走一遍过程吧,自己走一遍才能真理解
         * */
        int i = 0;
        int j = nums.length - 1;
        int m = 0;
        while (i <= j) {
            m = (i + j) / 2;
            if (nums[m] < target) {
                i = m + 1;
            } else if (target < nums[m]) {
                j = m - 1;
            } else {
                return m;
            }
        }
        return -i - 1;
    }

2. leftmost

public static int version52(int[] nums, int target) {
        int i = 0;
        int j = nums.length - 1;
        int candidate = -1;
        int m = 0;
        while (i <= j) {
            m = (i + j) / 2;
            if (nums[m] < target) {
                i = m + 1;
            } else if (target < nums[m]) {
                j = m - 1;
            } else {
                candidate = m;
                j = m - 1;
            }
        }
        return candidate;
    }

3. leftmost(返回插入值)

public static int version6(int[] nums, int target) {
        int i = 0;
        int j = nums.length - 1;
        int m = 0;
        while (i <= j) {
            m = (i + j) / 2;
            if (nums[m] < target) {
                i = m + 1;
            } else {
                j = m - 1;
            }
        }
        return i;
    }

4. 应用场景

求排名,求前任,求后任,求最近邻居,范围查询(后面两个都是基于前任和后任)

双指针

1. 双指针

(移动元素,删除数组中的重复项,有序数组的平方)

2. 滑动窗口

2.0 下面用到的API方法
用到的字符串方法
    toCharArray()
    charAt()
    substring()
用到的map集合方法
    put()
    get()
    getOrDefault()
    size()
    containKey()
2.1 长度最小的子数组
public static int minSubArrayLen(int target, int[] nums) {
        int fastIndex = 0;
        int slowIndex = 0;
        int ans = Integer.MAX_VALUE;
        int sum = 0;
        //target的范围>=1,没有0和负数
        while (fastIndex < nums.length) {
            sum = sum + nums[fastIndex];
            while (sum >= target) {
                //保留并追踪最小的长度值
                ans = Math.min(ans, fastIndex - slowIndex + 1);
                
                sum = sum - nums[slowIndex];
                slowIndex++;
            }
            fastIndex++;
        }

        //重点!!还有考虑没有一个值的情况,通过ans的赋值情况来判断
        //return ans;
        return ans == Integer.MAX_VALUE ? 0 : ans;
    }
2.2 水果成篮
public static int test(int[] fruits) {
        /*
        就两重点,一个是这个题中HashMap是什么用的,起什么作用
                另一个是滑动窗口类题目的通用解法
                (最重要的是要有一个跟踪最大值或最小值的变量)
         */
        HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
        int maxFruit = 0;
        int slowIndex = 0;

        for (int fastIndex = 0; fastIndex < fruits.length; fastIndex++) {
            //这句很重要,一行代码干了很多事,厉害
            map.put(fruits[fastIndex], map.getOrDefault(fruits[fastIndex], 0) + 1);
            while (map.size() > 2) {
                //当集合中元素大于2时,就要开始从左到右缩小滑动窗口了
                // 要逐渐增加满指针,然后减少map集合中的元素
                // 这步减少map集合中的元素应该正确吧 考古:正确的
                map.put(fruits[slowIndex], map.get(fruits[slowIndex]) - 1);
                if (map.get(fruits[slowIndex]) == 0) {
                    map.remove(fruits[slowIndex]);
                }
                slowIndex++;
            }
            maxFruit = Math.max(maxFruit, fastIndex - slowIndex + 1);
        }
        return maxFruit;
    }
2.3 最小覆盖字串
public static String test(String s, String t) {
        //初始化集合
        HashMap<Character, Integer> need = new HashMap<>();
        HashMap<Character, Integer> window = new HashMap<>();
        for (char c : t.toCharArray()) {
            need.put(c, need.getOrDefault(c, 0) + 1);
        }

        //现在开始定义变量
        int slowIndex = 0;
        int fastIndex = 0;
        int valid = 0;
        int start = 0;
        int minLen = Integer.MAX_VALUE;

        //现在开始进入第一层循环
        while (fastIndex < s.length()) {
            //更新窗口数据,valid+1,
            char c = s.charAt(fastIndex);//获取此时的key!!
            fastIndex++;
            if (need.containsKey(c)) {
                //下面两个连犯两次低级错误
                //window.put(c,need.get(c)+1);
                //window.put(c,need.getOrDefault(c,0)+1);
                window.put(c,window.getOrDefault(c,0)+1);
                if (need.get(c).equals(window.get(c))) valid++;
            }

            //现在开始进入第二层循环
            while (need.size() == valid) {
                //更新固定数据
                if (fastIndex - slowIndex + 1 <= minLen) {//滑动窗口的常见做法,例如Math.min(minLen, fastIndex-slowIndex+1)
                    start = slowIndex;
                    minLen = fastIndex - slowIndex ;//+ 1;//这步是为什么//稍微动脑子想一想,start到start+minLen
                }

                //更新窗口数据,valid-1
                char d = s.charAt(slowIndex);
                slowIndex++;
                if (need.containsKey(d)) {
                    if (need.get(d).equals(window.get(d))) valid--;
                    window.put(d,window.get(d)-1);
                }

            }

        }

        return minLen==Integer.MAX_VALUE?"":s.substring(start,start+minLen);
    }

3. 收获

3.1 双指针技巧
3.2 滑动窗口的通用做法

两次判断:快指针何时移动,慢指针何时移动。慢指针的判断在快指针的判断中嵌套

一个用来跟踪窗口最大值或最小值的变量

3.3 哈希表用处
3.4 预处理思想

通过对数据的预处理,可以简化问题的复杂度,或是减少算法中的冗余操作。

预处理思想在很多算法问题中都有应用,比如在处理大量数据时先进行排序、构建索引等,以减少后续操作的复杂度。

3.5 动态调整策略 

解决问题时,根据当前的情况动态调整策略是一种非常重要的思维方式。在滑动窗口算法中,就体现在根据窗口内数据的变化动态调整窗口的大小。

这种思维方式可以应用于很多算法设计中,比如在动态规划中根据前一状态来调整当前的决策,在贪心算法中根据当前情况来做出最优选择等。

螺旋矩阵

public static int[][] generateSpiralMatrix(int n, int m) {
        /*
        思路:
            初始化一个数组,一个变量用来填充数组,四个变量为边界
            进入循环,条件:是否填充完成
                以左为始,右为终,不断填充,然后收缩该边界
                为了处理最后的情况,加一个if判断
         */
        int[][] matrix = new int[m][n];
        int num = 1; // 用于填充的数字,从1开始
        int top = 0, bottom = m - 1, left = 0, right = n - 1;

        //while (left <= right && top <= bottom) {
        while (num <= n * m) {
            /*
                一个循环,变量i等于最左值,条件i<=right,i就++
                然后循环内给二维数组赋值
                这层循环完,top++
             */
            // 填充上边
            for (int i = left; i <= right; i++) {
                matrix[top][i] = num++;
            }
            top++;
            // 填充右边
            for (int i = top; i <= bottom; i++) {
                matrix[i][right] = num++;
            }
            right--;
            //这两个判断是用来处理最中间的情况的
            // 填充下边
            if (top <= bottom) { //如果上下还有没处理的行的话,那就继续填充,否则直接退出
                for (int i = right; i >= left; i--) {
                    matrix[bottom][i] = num++;
                }
                bottom--;
            }
            // 填充左边
            if (left <= right) {
                for (int i = bottom; i >= top; i--) {
                    matrix[i][left] = num++;
                }
                left++;
            }
        }

        return matrix;
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值