数组中的问题其实最常见
- 排序:选择排序;插入排序;归并排序;快速排序
- 查找:二分查找法
- 数据结构:栈;队列;堆
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/
- 暴力解法
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;
}
}
}
-
优化(双指针)
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;
}
}
}
- 双指针+交换
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)
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)
**解题思路:**https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/solution/shuang-zhi-zhen-shan-chu-zhong-fu-xiang-dai-you-hu/
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/
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三种可能。为这个数组排序。
- 可以使用任意一-种排序算法
- 没有使用.上题目中给出的特殊条件
- 计数排序
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;
}
}
}
- 三路快排
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开始计算)
- 如果没有解怎样?保证有解
- 如果有多个解怎样?返回任意解
- 最直接的思考:暴力解法。双层遍历,O(n^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
给定一个字符串,只看其中的数字和字母,忽略大小写,判断
- 这个字符串是否为回文串?
- 空字符串如何看?
- 字符的定义?
- 大小写问题
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
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
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
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
- 暴力解:遍历所有的连续子数组[i…j]
-
- 计算其和sum,验证sum>= s
- 时间复杂度O(n^3)
暴力解的问题:大量的重复计算
- 滑动窗口
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?
- 大小写是否敏感?
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;
}
}