玩转算法面试(一)数组问题(Java)

数组中的问题其实最常见

  • 排序:选择排序;插入排序;归并排序;快速排序
  • 查找:二分查找法
  • 数据结构:栈;队列;堆

1、如何写出正确的程序

二分查找法

对于有序数列,才能使用二分查找法(排序的作用)

package com.wanghong.array;

public class BinarySearch {
    public int binarySearch(int arr[], int n, int target) {
        int l = 0, r = n - 1;
        while (l <= r) {
            //int mid = (l + r) / 2;
            int mid = l + (r - l)/2;
            if (arr[mid] == target) return mid;
            if (target > arr[mid]) l = mid + 1;
            else r = mid - 1;
        }

        return -1;
    }

    public static void main(String[] args) {
        int[] arr = new int[]{1,2,3,4,5,6,7,8};
        BinarySearch binarySearch = new BinarySearch();
        System.out.println(binarySearch.binarySearch(arr,8,3));
    }
}

总结

  • 明确变量的含义
  • 循环不变量
  • 小数据量调试
  • 大数据量调试

2、面试问题实战

LeetCode 283 Move Zeros

https://leetcode-cn.com/problems/move-zeroes/

image.png

  1. 暴力解法
class Solution {
    public void moveZeroes(int[] nums) {
        // 暴力
        // 时间复杂度O(n)
        // 空间复杂度O(n)
        List<Integer> list = new ArrayList<>();
        for (int num:nums) {
            if (num != 0 ) list.add(num);
        }

        for (int i = 0; i < list.size();i++ ) {
            nums[i] = list.get(i);
        }

        for (int i = list.size();i < nums.length;i++ ) {
            nums[i] = 0;
        }
    }
}
  1. 优化(双指针)

    k- [0…k)中保存所有当前遍历过的非0元素

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

        for (int i = k;i < nums.length;i++) {
            nums[i] = 0;
        }
    } 
}
  1. 双指针+交换
class Solution {
    public void moveZeroes(int[] nums) {
        int k = 0;
        //遍历到第i个元素后, 保证[0...1]中所有非0元素
        //都按照顺序排列在[0...k)中
        //同时,[k...i] 为0
        for (int i = 0; i < nums.length;i++ ) {
            if(nums[i] != 0 ) 
                if( k != i ) swap(nums,k++,i);
                else k++;
        }
    } 

    private void swap(int[] arr, int i, int j) {
        int t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }
}

练习

LeetCode 27 Remove Element

https://leetcode-cn.com/problems/remove-element/

给定一个数组nums和一个数值val,将数组中所有等于val的元

素删除,并返回剩余的元素个数。

  • 如何定义删除?从数组中去除?还是放在数组末尾?
  • 剩余元素的排列是否要保证原有的相对顺序?
  • 是否有空间复杂度的要求? O(1)

image.png

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

        return k + 1;
    }
}
LeetCode 26 Remove Duplicated from Sorted Array

https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/

  • 如何定义删除?从数组中去除?还是放在数组末尾?
  • 剩余元素的排列是否要保证原有的相对顺序?
  • 是否有空间复杂度的要求? O(1)

image.png

**解题思路:**https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/solution/shuang-zhi-zhen-shan-chu-zhong-fu-xiang-dai-you-hu/

image.png

class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums == null || nums.length == 0) return 0;  
        int left = 0 ;
        for ( int right = 1;right < nums.length;right++ ) {
            if (nums[left] != nums[right]) {
                if(right - left > 1)
                    nums[left+1] = nums[right];
                left++;
            }
        }

        return left + 1;
    }
}
LeetCode 80 Remove Duplicated from Sorted Array II

https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array-ii/

image.png

class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums == null || nums.length == 0) return 0;  
        int left = 0 ;
        for ( int right = 1;right < nums.length;right++ ) {
            if (nums[left] != nums[right]) {
                if(right - left > 2) // 关键 分析可知
                    nums[left+2] = nums[right];
                left++;
            }
        }

        return left + 2;
    }
}

3、基础算法思路的应用

LeetCode 75 Sort Colors

给定一个有n个元素的数组,数组中元素的取值只有0, 1,2三种可能。为这个数组排序。

  • 可以使用任意一-种排序算法
  • 没有使用.上题目中给出的特殊条件

image.png

  1. 计数排序
class Solution {
    //时间复杂度: 0(n)
    //空间复杂度: 0(1)
    public void sortColors(int[] nums) {
        int[] count  = new int[3];
        for (int num:nums) {
            count[num]++;
        }
        int index = 0;
        for(int i = 0; i < count[0];i++ ) {
            nums[index++] = 0;
        }
        for(int i = 0; i < count[1];i++ ) {
            nums[index++] = 1;
        }
        for(int i = 0; i < count[2];i++ ) {
            nums[index++] = 2;
        }
    }
}
  1. 三路快排
class Solution {
    public void sortColors(int[] nums) {
        int zero = 0,two = nums.length - 1;
        for (int i = 0; i <= two; ) {
            if(nums[i] == 1) i++;
            else if (nums[i] == 0) swap(nums,i++,zero++);
            else {
                swap(nums, i,two--);
            }
        }
    }

    private void swap(int[] nums, int i ,int j) {
        int t = nums[i];
        nums[i] = nums[j];
        nums[j] = t;
    }
}

练习

LeetCode Merge Sorted Array
LeetCode 215 Kth Largest Element in an Array

LeetCode 167 Two Sum |I - Input array is sorted

给定一个有序整型数组和一个整数target,在其中寻找两个元素,使其和为target。返回这两个数的索引。

  • 如numbers= [2, 7, 11, 15], target= 9
  • 返回数字2,7的索引1, 2 (索引从1开始计算)
  • 如果没有解怎样?保证有解
  • 如果有多个解怎样?返回任意解

image.png

  1. 最直接的思考:暴力解法。双层遍历,O(n^2)

暴力解法没有充分利用原数组的性质——有序

有序?二分搜索?

  1. 二分查找
  2. 双指针(对撞指针)
class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int i = 0, j = numbers.length - 1;
        while ( i < j ) {
            if (numbers[i] + numbers[j] == target) return new int[]{i + 1,j + 1};
            else if (numbers[i] + numbers[j]  < target) i++;
            else j--;
        }

        return new int[2];
    }
}

练习

125 Valid P alindrome

给定一个字符串,只看其中的数字和字母,忽略大小写,判断

  • 这个字符串是否为回文串?
  • 空字符串如何看?
  • 字符的定义?
  • 大小写问题

image.png

class Solution {
    public boolean isPalindrome(String s) {
        if(s == null ||s.length() == 0) return true;
        int i = 0, j = s.length() - 1;
        char[] chs = s.toLowerCase().toCharArray();// 将字符串转成字符数组
        while (i <= j) {
            while (!isCharaterOrNumber(chs[i]) && i < j ) i++;
            while (!isCharaterOrNumber(chs[j]) && i < j ) j--;
            if (chs[i] != chs[j]) return false;
            i++;
            j--;
        }
        return true;
    }
    // 判断某个字符是不是字母或者数字
    // 这个函数可以用Character.isLetterOrDigit()代替
    private boolean isCharaterOrNumber(char c) {
        if((c >= 97 && c<=122)||(c >= 65 && c<=90)||(c >= 48 && c<=57)) return true;
        else return false;
    } 
}
344 Reverse String

image.png

class Solution {
    public void reverseString(char[] s) {
        int len = s.length;
        for ( int i = 0;i < len/2;i ++ ) {
            char c = s[i];
            s[i] = s[len - 1 - i];
            s[len - 1 - i] = c;
        }
    }
}


class Solution {
    public void reverseString(char[] s) {
        int n = s.length;
        for (int left = 0, right = n - 1; left < right; ++left, --right) {
            char tmp = s[left];
            s[left] = s[right];
            s[right] = tmp;
        }
    }
}
345 Reverse Vowels of a String

image.png

class Solution {
    public String reverseVowels(String s) {
        char[] chs = s.toCharArray();
        int l = 0,r = chs.length - 1;
        while (l < r) {
            while(!isVowel(chs[l]) && l < r) l++;
            while(!isVowel(chs[r]) && l < r) r--;

            swap(chs,l,r);
            l++;
            r--;
        }

        return new String(chs);//字符数组转字符串
    }

    private boolean isVowel(char c) {
        // 不能忘记元音字母的大小写
        if(c == 'a' || c == 'o' ||c == 'u' ||c == 'i' ||c == 'e' ) return true;
        else if(c == 'A' || c == 'O' ||c == 'U' ||c == 'I' ||c == 'E') return true;
        else return false;
    }

    private void swap(char[] chs,int i ,int j) {
        char t = chs[i];
        chs[i] = chs[j];
        chs[j] = t;
    }
}
11 Container With Most Water

image.png

class Solution {
    public int maxArea(int[] height) {
        int l = 0, r = height.length - 1, maxArea = 0;
        while(l < r) {
            maxArea = Math.max(maxArea,(r - l) * Math.min(height[l],height[r]));
            if(height[l] < height[r]) l++;
            else r--;
        }

        return maxArea;
    }
}

4、双索引技术Two Pointer

滑动窗口

209 Minimum Size Subarray Sum

给定一个整型数组和一个数字s,找到数组中最短的一个连续子数组,使得连续子数组的数字和sum>= s,返回这个最短的连续子数组的长度值

  • 如,给定数组[2,3, 1,2, 4,3], s= 7
  • 答案为[4, 3],返回2
  • 什么叫子数组
  • 如果没有解怎么办?返回0
  1. 暴力解:遍历所有的连续子数组[i…j]
    • 计算其和sum,验证sum>= s
    • 时间复杂度O(n^3)

暴力解的问题:大量的重复计算

  1. 滑动窗口
class Solution {
    public int minSubArrayLen(int s, int[] nums) {
        //时间复杂度: 0(n)
        //空间复杂度: 0(1)
        int l = 0 , r = -1, sum = 0;// nums[...r]为我们的滑动窗口
        int res = nums.length + 1;
        while (l < nums.length) {
            if (r + 1 < nums.length && sum < s) sum += nums[++r];//注意 r + 1 < nums.length
            else sum -= nums[l++];

            if (sum >= s) res = Math.min(res,r - l + 1);
        }
        if(res == nums.length + 1) return 0;
        return res; 
    }
}


// 第二种写法
class Solution {
    public int minSubArrayLen(int s, int[] nums) {
        //时间复杂度: 0(n)
        //空间复杂度: 0(1)
        int l = 0 , r = 0, sum = 0;
        int res = nums.length + 1;
        while(r < nums.length) {
            sum += nums[r++];
            while(sum >= s && r > l) {
                res = Math.min(res,r - l);
                sum -= nums[l++];
            }
        }
        if (res == nums.length + 1) return 0;
        return res;
    }
}

3 Longest Substring Without Repeating Characters

在一个字符串中寻找没有重复字母的最长子串

  • 如"abcabcbb",则结果为abc
  • 如"bbbbb",则结果为b
  • 如"pwwkew",则结果为"wke"
  • 字符集?只有字母?数字+字母? ASCII?
  • 大小写是否敏感?

image.png

class Solution {
    public int lengthOfLongestSubstring(String s) {
        // 双指针
        int l = 0,r = -1 ,res = 0;
        int[] freq = new int[256];
        while (l < s.length()) {
            if(r + 1 < s.length() && freq[s.charAt(r+1)] == 0) freq[s.charAt(++r)]++;
            else freq[s.charAt(l++)]--;

            res = Math.max(res,r - l + 1);
        }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值