电信保温杯笔记——代码随想录 刷题攻略 数组

电信保温杯笔记——代码随想录 刷题攻略 数组

  • 电信保温杯笔记——代码随想录 刷题攻略
  • 1.数组过于简单,但你该了解这些!
  • 2.数组:每次遇到二分法,都是一看就会,一写就废
    • 704. 二分查找
    • 35.搜索插入位置
    • 34. 在排序数组中查找元素的第一个和最后一个位置
    • 69.x 的平方根
    • 367.有效的完全平方数
    • 总结
      • 核心代码
  • 3.数组:就移除个元素很难么?
    • 27. 移除元素
    • 26.删除排序数组中的重复项
    • 283.移动零
    • 小结
      • 核心代码
    • 844.比较含退格的字符串
    • 977.有序数组的平方
    • 小结
      • 核心代码
  • 4.数组:有序数组的平方,还有序么?
  • 5.数组:滑动窗口拯救了你
    • 209.长度最小的子数组
    • 小结
    • 904.水果成篮
    • 小结
    • 76.最小覆盖子串
    • 总结
      • 核心代码
  • 6.数组:这个循环可以转懵很多人!
    • 59.螺旋矩阵II
    • 54.螺旋矩阵
    • 剑指Offer 29.顺时针打印矩阵
  • 数组:总结篇

电信保温杯笔记——代码随想录 刷题攻略

代码随想录 刷题攻略
电信保温杯笔记——代码随想录 刷题攻略

1.数组过于简单,但你该了解这些!

讲义地址

2.数组:每次遇到二分法,都是一看就会,一写就废

讲义地址

均使用左闭右开划分区间,只要是有序的结构,就可以用这个方法

704. 二分查找

lettcode地址

class Solution {
    public int search(int[] nums, int target) {
        if (nums[0] > target || nums[nums.length -1] < target){
            return -1;
        }
        int left = 0;
        int right = nums.length;
        while(left < right){
            int mid = left + ((right - left ) >> 1);
            if (nums[mid] > target){
                right = mid;
            }else if (nums[mid] < target){
                left = mid +1;
            }else{
                return mid;
            }

        }
        return -1;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 数组_第1张图片

35.搜索插入位置

lettcode地址

class Solution {
    public int searchInsert(int[] nums, int target) {
        if (nums[0] > target){
            return 0;
        }
        if (nums[nums.length - 1] < target){
            return nums.length;
        }
        int left = 0;
        int right = nums.length;
        while (left < right){
            int mid = left + ((right - left) >> 1);
            if (nums[mid] > target){
                right = mid;
            }else if (nums[mid] < target){
                left = mid + 1;
            }else {
                return mid;
            }
        }
        // left==right时,就是搜索的最后区间,最接近
        // target的元素,target应该在它的左或右
        return nums[left] > target ? left : (left + 1);
    }
}

电信保温杯笔记——代码随想录 刷题攻略 数组_第2张图片

34. 在排序数组中查找元素的第一个和最后一个位置

leetcode地址

class Solution {
    public int[] searchRange(int[] nums, int target) {
        if (nums == null || nums.length == 0){
            return new int[]{-1, -1};
        }
        if ((nums[0] > target) || (nums[nums.length - 1] < target)){
            return new int[]{-1, -1};
        }
        int left = 0;
        int right = nums.length;
        boolean found = false;
        int mid = 0;
        while(left < right){
            mid = left + ((right - left) >> 1);
            if (nums[mid] > target){
                right = mid;
            }else if (nums[mid] < target){
                left = mid + 1;
            }else{
                 found = true;
                 break;
            }
        }
        // 找到插入的位置后,从该位置左右两个方向开始搜索
        if (found == true){
            left = mid;
            right = mid;
            for (int i = left - 1; i >= 0; i--){
                if (nums[i] != target){
                    break;
                }
                left--;
            }
            for (int i = right + 1; i < nums.length; i++){
                if (nums[i] != target){
                    break;
                }
                right++;
            }            
            return new int[]{left, right};
        }
        return new int[]{-1, -1};
    }
}

电信保温杯笔记——代码随想录 刷题攻略 数组_第3张图片

69.x 的平方根

leetcode地址

class Solution {
    public int mySqrt(int x) {
        if (x == 0 || x == 1){
            return x;
        }
        int left = 0;
        int right = x;
        while(left < right){
            int mid = left + ((right - left) >> 1);
            if (mid > x /mid){
                right = mid;
            }else if (mid < x /mid){
                left = mid + 1;
            }else{
                return mid;
            }
        }
        // left^2为最接近x的数
        return left > x / left ? (left - 1) : left;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 数组_第4张图片

367.有效的完全平方数

leetcode地址

class Solution {
    public boolean isPerfectSquare(int num) {
        if (num == 0 || num == 1){
            return true;
        }
        // 这题不用long 或者long long 容易出错,
        long left = 0;
        long right = num + 1;
        while (left < right){
            long mid = left + ((right - left) >> 1);
            if (mid * mid> num ){
                right = mid;
                // 下一行,如果不使用long,使用int,判断条件
                // mid < x /mid会在运行中出错,输入num=5的时候
            }else if (mid * mid< num ){
                left = mid + 1;
            }else{
                return true;
            }
        }
        return false;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 数组_第5张图片

总结

此类题目要求:有序,找一个位置

解法:使用2个边界指针,通过比较条件,如nums[mid] > target,不断缩小边界指针之间的范围。

核心代码

class Solution {
    public int search(int[] nums, int target) {
        if (nums[0] > target || nums[nums.length -1] < target){
            return -1;
        }
        int left = 0;
        int right = nums.length;
        while(left < right){
            int mid = left + ((right - left ) >> 1);
            if (nums[mid] > target){
                right = mid;
            }else if (nums[mid] < target){
                left = mid +1;
            }else{
                return mid;
            }

        }
        return -1;
    }
}

3.数组:就移除个元素很难么?

讲义地址

是要是删除重复元素或者将重复元素移动到一端的,都可以用双指针。

27. 移除元素

leetcode地址

可以使用上一节left right指针的方法,但这样会丧失其他元素的相对次序:

class Solution {
    public int removeElement(int[] nums, int val) {
        int left = 0;
        int right = nums.length;
        while(left < right){
            if(nums[left] == val){
                nums[left] = nums[--right];
                nums[right] = val;
            }else{
                left++;
            }
        }
        return right;

    }
}

应该使用快慢指针的方法:

class Solution {
    public int removeElement(int[] nums, int val) {
        int slow = 0;
        for(int fast = 0; fast < nums.length; fast ++){
            if(nums[fast] != val){
                nums[slow] = nums[fast];
                slow++;
            }
        }
        return slow;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 数组_第6张图片
慢指针指向新数组末端的后一个元素的位置。

26.删除排序数组中的重复项

leetcode地址

class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums == null){
            return -1;
        }
        if(nums.length == 0){
            return 0;
        }
        int slow = 1;
        int val = nums[0];
        for(int fast = 1; fast < nums.length; fast++){
            if(nums[fast] != val){
                nums[slow] = nums[fast];
                slow++;
                // 每个新遇到的数,要是没有重复,则重新赋值val
                val = nums[slow-1];
            }
        }
        return slow;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 数组_第7张图片

283.移动零

leetcode地址

class Solution {
    public void moveZeroes(int[] nums) {
        int slow = 0;
        for(int fast = 0; fast < nums.length; fast++){
            if(nums[fast] != 0){
                nums[slow] = nums[fast];
                slow++;
            }
        }
        while(slow < nums.length){
            nums[slow] = 0;
            slow++;
        }
        
    }
}

电信保温杯笔记——代码随想录 刷题攻略 数组_第8张图片

小结

题目要求:待保留的元素移动到头部,舍弃的元素放在尾部。

解法:使用同向双指针,即快慢指针,将当前数组划分为3部分 [保留部分,慢指针,舍弃部分,快指针,待比较部分],直至待比较部分缩小为0。

核心代码

class Solution {
    public int removeElement(int[] nums, int val) {
        int slow = 0;
        for(int fast = 0; fast < nums.length; fast ++){
            if(nums[fast] != val){
                nums[slow] = nums[fast];
                slow++;
            }
        }
        return slow;
    }
}

844.比较含退格的字符串

leetcode地址

关键在于从后往前读,每次读取一个有效字符进行比较,有效字符指的是无法被’#'删除的字符。

class Solution {
    public boolean backspaceCompare(String S, String T) {
        int i = S.length() - 1;// 用于遍历S
        int j = T.length() - 1;// 用于遍历T
        int skipS = 0;// 记录'#'字符,用于消去前面的字符
        int skipT = 0;

        while(i >= 0 || j >= 0){// 有可能一个读完,一个没读完,所用用或运算
            // 从末尾开始读取S中没有被'#'抹去的一个字符或读取不到并结束
            while(i >= 0){
                if(S.charAt(i) == '#'){
                    skipS++;
                    i--;
                }else if(skipS > 0){
                    skipS--;
                    i--;
                }else{
                    break;
                }
            }
            // 从末尾开始读取T中没有被'#'抹去的一个字符读取不到并结束
            while(j >= 0){
                if(T.charAt(j) == '#'){
                    skipT++;
                    j--;
                }else if(skipT > 0){
                    skipT--;
                    j--;
                }else{
                    break;
                }
            }

            if(i >= 0 && j >= 0){
                // 2个字符串都从末尾读取到一个有效字符,进行比计较
                if(S.charAt(i) != T.charAt(j)){
                    return false;
                }
            }else {
                // 有一个读取了字符,有一个没读取到直接结束了
                if(i >= 0 || j >= 0){
                    return false;
                }
            }
            // 能进入下一轮while循环的是2个有效字符一样或2个同时读取结束
            i--;
            j--;

        }
        return true;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 数组_第9张图片

977.有序数组的平方

leetcode地址

非递减,意思是有可能有重复的元素,不是严格单调递增。

关键在于,平方后,先找到最小值及其索引,将它放到新数组中,然后使用左右指针从最小值索引处向两端遍历,比较两端最小值,将最小值放入新数组中,然后指针往外拓展一步,直到有一个指针越出数组边界,然后将另一端的指针遍历放入新数组中。

class Solution {
    public int[] sortedSquares(int[] nums) {
        // if(nums.length == 1){
        //     return new int[]{nums[0] * nums[0]};
        // }
        // 平方后找最小值索引
        int left = 0;// 记录最小值索引
        int min = nums[0] * nums[0];
        for(int i = 0; i< nums.length; i++){
            nums[i] *= nums[i];
            if(nums[i] < min){
                min = nums[i];
                left = i;
            }
        }

        int[] result = new int[nums.length];
        result[0] = min;
        int index = 1;

        // 从最小值处,使用2个指针分别向两端的值比较大小
        int right = left + 1;
        left--;
        // 2个指针都没有超出边界时
        while(left >= 0 && right < nums.length){
            if(nums[left] <= nums[right]){
                result[index] = nums[left];
                left--;
            }else{
                result[index] = nums[right];
                right++;
            }
            index++;
        }
        // 此时其中一个边界越界,则遍历另一个方向直至边界也越界
        while(left >= 0){
            result[index] = nums[left];
            left--;
            index++;
        }
        while(right < nums.length){
            result[index] = nums[right];
            right++;
            index++;
        }
        return result;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 数组_第10张图片

小结

题目要求:2个部分的元素,逐个进行比较操作。

解法:使用同向或者反向双指针,要看这2个部分是不是源于同一个整体,题目844就是不是一个整体,而题目977是源于一个整体,所以它的指针是反向的。2个指针上的元素进行一次比较操作后,一个指针移动或2个都同时移动,至双方都移动到边界才停止。

核心代码

	while(left >= 0 || right < nums.length){
        while(left >= 0 && right < nums.length){
			// 2个指针上的元素进行一次比较操作后,一个
			// 指针移动或2个都同时移动
        }
        // 此时其中一个边界越界,则遍历另一个方向直至边界也越界
        while(left >= 0 || right == nums.length){
            left--;
        }
        while(left < 0 || right < nums.length){
            right++;
        }
   }

4.数组:有序数组的平方,还有序么?

讲义地址
就是上一题

5.数组:滑动窗口拯救了你

讲义地址

209.长度最小的子数组

leetcode地址

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        if(nums.length == 0){
            return 0;
        }
        int start = 0;
        int end = 0;
        int sum = 0;
        int result = Integer.MAX_VALUE;

        while(end < nums.length){
            sum += nums[end];
            while(sum >= target){
                sum -= nums[start];
                result = Integer.min(result, end - start + 1);
                start++;
            }
            end++;
        }
        return result == Integer.MAX_VALUE ? 0 : result;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 数组_第11张图片

小结

这题的核心代码其实和 目录第三章第一个小结里面核心代码类似,都是使用了快慢指针,只不过这题的慢指针会反复移动。大循环的边界条件都是快指针不超出边界。这种不知道遍历过程会不会得出非零结果的,都要使用一个变量result,去记录结果。

904.水果成篮

leetcode地址

class Solution {
    public int totalFruit(int[] tree) {
        int left = 0;
        int right = 0;
        int max = 0;
        HashMap<Integer, Integer> hashMap = new HashMap<>();
        while(right < tree.length){
            int cur = tree[right];

            right++;// 这行代码很关键

            if(hashMap.size() <= 2){
            	// 存在则count+1
                if(hashMap.containsKey(cur) == true){
                    hashMap.put(cur,hashMap.get(cur) + 1);
                }else{不存在则新建
                    hashMap.put(cur,1);
                }
            }
            while(hashMap.size() >2){
                int abandon = tree[left];
                // count-1
                hashMap.put(abandon,hashMap.get(abandon) - 1);
                // 如果减到0就移除
                if(hashMap.get(abandon) == 0){
                    hashMap.remove(abandon);
                }
                left++;
            }
            // 此时的最大长度数组窗口为[left,right)
            max = Integer.max(max, right - left);
        }
    return max;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 数组_第12张图片

小结

求最大数组,result设为Min或者0;
求最小数组,result设为Max;

76.最小覆盖子串

leetcode地址

class Solution {
    Map<Character, Integer> ori = new HashMap<Character, Integer>();
    Map<Character, Integer> cnt = new HashMap<Character, Integer>();

    public String minWindow(String s, String t) {
        int left = 0;
        int right = 0;
        String str = "";
        int min = Integer.MAX_VALUE;
        for(int i = 0; i < t.length(); i++){
            ori.put(t.charAt(i), ori.getOrDefault(t.charAt(i), 0) + 1);
        }
        while(right < s.length()){
            char cur = s.charAt(right);
            right++;
            // window +cur 不满足条件,就加入cur
            if(ori.containsKey(cur)){
                cnt.put(cur, cnt.getOrDefault(cur, 0) + 1);
            }
            // window + cur 满足条件,则记录最小长度,并且left应该++,+到window不满足条件
            while(check() && left < s.length()){
                if(ori.containsKey(s.charAt(left))){
                    cnt.put(s.charAt(left), cnt.getOrDefault(s.charAt(left), 0) - 1);
                }
                if(min > right - left){
                    str = (String) s.subSequence(left, right);
                    min = right - left;
                }
                left++;
            }
        }
        return str;
    }
    // 这里使用iterator迭代器比增强for循环效率要高
    public boolean check() {
        Iterator iter = ori.entrySet().iterator(); 
        while (iter.hasNext()) { 
            Map.Entry entry = (Map.Entry) iter.next(); 
            Character key = (Character) entry.getKey(); 
            Integer val = (Integer) entry.getValue(); 
            if (cnt.getOrDefault(key, 0) < val) {
                return false;
            }
        } 
        return true;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 数组_第13张图片

总结

hashmap常用函数:hashmap.getOrDefault(Object, 0)

滑动窗口使用双指针,left,right

核心代码

        while(right < nums.length){
        	right++;
        	前几轮循环,状态 != 题目条件
        	if(当前状态 != 题目条件){
        		更新状态
        	}
            while(当前状态 == 题目条件){
                使状态达不到题目条件
                left++;
            }
        }

6.数组:这个循环可以转懵很多人!

讲义地址

59.螺旋矩阵II

leetcode地址

完成一次上下左右为一轮,我以轮数为循环,占用内存少:

class Solution {
    public int[][] generateMatrix(int n) {
        int[][] matrix = new int[n][n];
        int x = 0;// 横坐标,向右为正
        int y = 0;// 纵坐标,向下为正
        int value = 1;// 当前值
        for (int loop = n - 1; loop > 0 ; loop -= 2) {
            // 上
            for (int i = 0; i < loop; i++) {
                matrix[y][x] = value;
                value++;
                x++;
            }
            // 右
            for (int i = 0; i < loop; i++) {
                matrix[y][x] = value;
                value++;
                y++;
            }
            // 下
            for (int i = loop; i > 0; i--) {
                matrix[y][x] = value;
                value++;
                x--;
            }
            // 左
            for (int i = loop; i > 0; i--) {
                matrix[y][x] = value;
                value++;
                y--;
            }
            x++;
            y++;
        }
        if(n % 2 == 1){
            matrix[n >> 1][n >> 1] = value;
        }
        return matrix;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 数组_第14张图片
别人的思路简单,代码简洁,以数字填完没有作为循环条件,用4个指针确定当前轮的边界:

class Solution {
    public int[][] generateMatrix(int n) {
        int l = 0, r = n - 1, t = 0, b = n - 1;
        int[][] mat = new int[n][n];
        int num = 1, tar = n * n;
        while(num <= tar){
            for(int i = l; i <= r; i++) mat[t][i] = num++; // left to right.
            t++;
            for(int i = t; i <= b; i++) mat[i][r] = num++; // top to bottom.
            r--;
            for(int i = r; i >= l; i--) mat[b][i] = num++; // right to left.
            b--;
            for(int i = b; i >= t; i--) mat[i][l] = num++; // bottom to top.
            l++;
        }
        return mat;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 数组_第15张图片

54.螺旋矩阵

leetcode地址

class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
        
        List<Integer> list = new ArrayList<>();
        int l = 0;
        int r = matrix[0].length -1;
        int t = 0;
        int b = matrix.length - 1;
        int size = matrix[0].length * matrix.length;
        while(list.size() < size){
            for (int j = l; j <= r && list.size() < size; j++) {
                list.add(matrix[t][j]);
            }
            t++;
            for (int j = t; j <= b && list.size() < size; j++) {
                list.add(matrix[j][r]);
            }
            r--;
            for (int j = r; j >= l && list.size() < size; j--) {
                list.add(matrix[b][j]);
            }
            b--;
            for (int j = b; j >= t && list.size() < size; j--) {
                list.add(matrix[j][l]);
            }
            l++;
        }
        return list;
    }
}

电信保温杯笔记——代码随想录 刷题攻略 数组_第16张图片

剑指Offer 29.顺时针打印矩阵

数组:总结篇

讲义地址

你可能感兴趣的:(算法与数据结构,leetcode,算法,排序算法)