1. 二分查找:针对有序数组、旋转数组查找
- 寻找旋转数组的最小值(旋转点):存在重复元素
class Solution {
public int findMin(int[] nums) {
/*
旋转数组的最小值
8.24
*/
//二分查找
int left = 0;
int right = nums.length-1;
while(left <= right){
int middle = left + (right - left)/2;
if(nums[middle] < nums[right]){
//如果比最右小,说明middle在右半数组,旋转点在左边。right = middle
right = middle;
}else if(nums[middle] > nums[right]){
//如果比最右大,说明middle在左半数组,旋转点在右边,left = middle+1;
left = middle+1;
}else{
//否则,right--。
right--;
}
}
return nums[left];
}
}
- 寻找旋转数组的最小值(旋转点):不存在重复元素
class Solution {
public int findMin(int[] nums) {
/*
旋转数组的最小值
7.25
*/
//二分查找
//判断空
if(nums.length == 0){
return 0;
}
int left = 0;
int right = nums.length-1;
while(left < right){
int middle = (left + right)/2;
if(nums[middle] < nums[right]){
//右半数组,旋转点在左边
right = middle;
}else{
left = middle + 1;
}
}
return nums[left];
}
}
- 搜索旋转数组:
class Solution {
public int search(int[] nums, int target) {
/*
搜索旋转数组
8.24
*/
//二分查找
int left = 0;
int right = nums.length-1;
while(left <= right){
int middle = left + (right - left)/2;
if(nums[middle] == target){
return middle;
}else if(nums[middle] <= nums[right]){
//middle小于right,在右半数组,可以分为[middle,target]和[target,right]
if(nums[middle] < target && target <= nums[right]){
//如果target大于middle并且target小于right,说明target依旧在右半数组,left移动到middle+1
left = middle + 1;
}else{
//如果target小于middle并且target大于right,说明target可能位于左半数组,right移动到middle-1
right = middle - 1;
}
}else if(nums[middle] > nums[right]){
//middle大于right,在左半数组,可以分为[left,target]和[target,middle]
if(nums[left] <= target && target < nums[middle]){
//如果target大于left并且target小于middle,target依旧在左半数组,right逼近到middle-1
right = middle - 1;
}else{
//如果target小于left并且target大于middle,说明target在右半数组,left逼近到middle+1
left = middle + 1;
}
}
}
return -1;
}
}
- 0-n-1中缺失的数字:二分查找可以用于查找递增数字中缺失的数
class Solution {
public int missingNumber(int[] nums) {
/*
0到n-1中缺失的数
7.27
*/
//有序数组查找,二分
int left = 0;
int right = nums.length-1;
while(left <= right){
//通过middle来判断缺失的数
int middle = (left + right)/2;
//数组中有这个索引对应的值,
if(nums[middle] == middle){
left = middle+1;
}else{
right = middle-1;
}
}
return left;
}
}
2. 双指针:二维矩阵查找、合并数组、三数之和、最接近的三数之和、移动数组元素
- 寻找两个正序数组的中位数
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
/*
寻找两个正序数组的中位数
7.22
*/
//合并数组 + 取中点
int index = 0;
int index1 = 0;
int index2 = 0;
int len1 = nums1.length;
int len2 = nums2.length;
int[] temp = new int[len1+len2];
for(int i=0;i<temp.length;i++){
if(index1 == len1){
temp[index++] = nums2[index2++];
}else if(index2 == len2){
temp[index++] = nums1[index1++];
}else if(nums1[index1] < nums2[index2]){
temp[index++] = nums1[index1++];
}else{
temp[index++] = nums2[index2++];
}
}
//提取中位数
int middle = temp.length/2;
//如果长度为奇数,返回中位数
if(temp.length%2 == 1){
return (double)temp[middle];
//如果长度为偶数。计算中位数+中位数前一位的和的平均数
}else{
return (double)(temp[middle] + temp[middle-1])/2;
}
}
}
- 搜索二维矩阵
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
/*
搜索二维矩阵
7.24
*/
//双指针,从右上角搜索
//判断空
if(matrix.length == 0 || matrix[0].length == 0){
return false;
}
int row = 0;
int col = matrix[0].length-1;
while(row < matrix.length && col >= 0){
if(matrix[row][col] == target){
return true;
}else if(matrix[row][col] > target){
//大,因为是从右上角开始搜索,往前一列,col--
col--;
}else if(matrix[row][col] < target){
//小,因为是从右上角开始搜索,往下一行,row++
row++;
}
}
return false;
}
}
- 排序数组查找元素的第一个和最后一个位置
class Solution {
public int[] searchRange(int[] nums, int target) {
/*
排序数组中查找元素的第一个和最后一个位置
7.23
*/
//二分查找,O(logn),实质是查找target的左右边界
int[] fail = new int[]{-1,-1};
int[] result = new int[2];
//左边界
int left = 0;
int right = nums.length-1;
while(left <= right){
int middle = (left + right)/2;
if(left <= right && nums[middle] < target){
left = middle + 1;
}else if(left <= right && nums[middle] >= target){
right = middle - 1;
}
}
//判断左指针是否越界
if(left > nums.length-1){
return fail;
}
//判断左指针是否指向target
if(nums[left] == target){
result[0] = left;
}else{
return fail;
}
//右边界
left = 0;
right = nums.length-1;
while(left <= right){
int middle = (left + right)/2;
if(left <= right && nums[middle] <= target){
left = middle + 1;
}else if(left <= right && nums[middle] > target){
right = middle - 1;
}
}
//判断右指针是否越界
if(right < 0){
return fail;
}
//判断右指针是否指向target
if(nums[right] == target){
result[1] = right;
}else{
return fail;
}
return result;
}
}
- 三数之和
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
/*
三数之和
7.22
*/
//三指针计算前缀和
ArrayList<List<Integer>> result = new ArrayList<>();
//排序,实现去重和剪枝
Arrays.sort(nums);
for(int i=0;i<nums.length;i++){
if(nums[i] > 0){
break;
}
if(i > 0 && nums[i] == nums[i-1]){
continue;
}
//提取双指针
int left = i+1;
int right = nums.length-1;
while(left < right){
//计算三指针的前缀和
int sum = nums[i] + nums[left] + nums[right];
if(sum == 0){
result.add(Arrays.asList(nums[i],nums[left],nums[right]));
//while去重
while(left < right && nums[left] == nums[left+1]){
left++;
}
while(left < right && nums[right] == nums[right-1]){
right--;
}
left++;
right--;
}else if(sum > 0){
//太大,right--
right--;
}else{
left++;
}
}
}
return result;
}
}
- 最接近的三数之和
class Solution {
public int threeSumClosest(int[] nums, int target) {
/*
最接近的三数之和
8.3
*/
//三数之和的变种,同样使用双指针实现,可通过Math.abs()比较开头三个数之和result和遍历过程中的三数之和sum的绝对值,如果target-sum < target-result,将result更新sum
//排序
Arrays.sort(nums);
int result = nums[0]+nums[1]+nums[2];
for(int i=0;i<nums.length;i++){
int left = i+1;
int rihgt = nums.length-1;
while(left < rihgt){
int sum = nums[i] + nums[left] + nums[rihgt];
if(Math.abs(target - sum) < Math.abs(target - result)){
result = sum;
}else if(sum < target){
left++;
}else if(sum > target){
rihgt--;
}else{
return result;
}
}
}
return result;
}
}
- 颜色分类
class Solution {
public void sortColors(int[] nums) {
/*
颜色分类(0,1,2问题)
7.24
*/
//三指针交换
//判断空
if(nums.length == 0){
return;
}
int left = 0;
int right = nums.length-1;
int cur = 0;
while(cur <= right){
if(nums[cur] == 2){
int temp = nums[cur];
nums[cur] = nums[right];
nums[right] = temp;
//right未搜索,cur不能++
right--;
}else if(nums[cur] == 0){
int temp = nums[cur];
nums[cur] = nums[left];
nums[left] = temp;
//left已经搜过,cur可以++
left++;
cur++;
}else{
cur++;
}
}
}
}
- 盛最多水的容器
class Solution {
public int maxArea(int[] height) {
/*
盛最多水的容器
7.22
*/
//双指针,如果左比右大,右为高,计算体积。反之
int left = 0;
int right = height.length-1;
int result = 0;
while(left <= right){
if(height[left] > height[right]){
int high = height[right];
int width = right-left;
result = Math.max(high*width,result);
right--;
}else{
int high = height[left];
int width = right-left;
result = Math.max(result,high*width);
left++;
}
}
return result;
}
}
- 网易笔试:字典序最小的排列:给出一个长度为m的序列T,求一个长度为n且字典序最小的排列S,要求不改变原序列中元素的相对位置。
第一行输入两个正整数n和m
第二行输入m个数,表示序列
5 3
2 1 5
输出
2 1 3 4 5
个人渣解:
import java.util.HashSet;
import java.util.Scanner;
public class Solution{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int len = sc.nextInt();
int len1 = sc.nextInt();
int[] nums1 = new int[len1];
HashSet<Integer> set = new HashSet<>();
for(int i=0;i<nums1.length;i++){
nums1[i] = sc.nextInt();
set.add(nums1[i]);
}
//nums2,长度为len-len1,存放互补的元素
int[] nums2 = new int[len-len1];
int index = 0;
for(int i=1;i<=len;i++){
if(!set.contains(i)){
nums2[index++] = i;
}
}
/*双指针判断
i指针用于控制nums1移动
j指针用于控制nums2移动
*/
int i = 0;
int j = 0;
int[] result = new int[len];
int Resultindex = 0;
//谁的第一个小谁就先存
if(nums1[0] < nums2[0]){
result[Resultindex++] = nums1[0];
i=1;
}else{
result[Resultindex++] = nums2[0];
j=1;
}
while(i < nums1.length && j < nums2.length){
//谁小存谁
if(nums1[i] < nums2[j]){
result[Resultindex++] = nums1[i++];
}else{
result[Resultindex++] = nums2[j++];
}
}
//补充存入最后一个数
while(i == nums1.length && j < nums2.length){
//如果nums1完但nums2未完,存入nums2
result[Resultindex++] = nums2[j++];
}
while(i < nums1.length && j == nums2.length){
//如果nums2完但nums1未完,存入nums1
result[Resultindex++] = nums1[i++];
}
for(int k=0;k<result.length-1;k++){
System.out.print(result[k] + " ");
}
System.out.print(result[result.length-1]);
}
}
3. 哈希表解决各类型问题
数组中元素出现次数问题(略,基本都是一套模板带走)
无重复字符的最长子串
class Solution {
public int lengthOfLongestSubstring(String s) {
/*
无重复字符的最长子串
7.22
*/
//哈希表判断字符,更新左边界为右边界
Map<Character,Integer> map = new HashMap<>();
int left = 0;
char[] str = s.toCharArray();
int result = 0;
for(int i=0;i<str.length;i++){
if(map.containsKey(str[i])){
left = Math.max(left,map.get(str[i])+1);
}
map.put(str[i],i);
result = Math.max(result,i-left+1);
}
return result;
}
}
- 和为K的子数组
class Solution {
public int subarraySum(int[] nums, int k) {
/*
和为k的子数组
7.26
*/
//哈希表统计出现次数
//判断空
if(nums.length == 0){
return 0;
}
Map<Integer,Integer> map = new HashMap<>();
int sum = 0;
int time = 0;
//预存前缀和
map.put(sum,1);
for(int i=0;i<nums.length;i++){
//前缀和
sum = sum + nums[i];
//如果包含
if(map.containsKey(sum-k)){
time = time + map.get(sum-k);
}
if(map.containsKey(sum)){
map.put(sum,map.get(sum)+1);
}else{
map.put(sum,1);
}
}
return time;
}
}
- 字母异位分组(同一组字母放到同一行)
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
/*
字母异位分组
7.24
*/
//哈希表判断字符串排序后的key,如果map中有,说明是同一组,存到同一行list,否则新开list
List<List<String>> result = new ArrayList<>();
Map<String,List<String>> map = new HashMap<>();
for(int i=0;i<strs.length;i++){
//字符串转换为字符数组
char[] str = strs[i].toCharArray();
//排序
Arrays.sort(str);
//转回字符串
String key = String.valueOf(str);
//map中含有key,说明key和strs[i]是同一组字母,存到同一行
if(map.containsKey(key)){
map.get(key).add(strs[i]);
}else{
ArrayList<String> list = new ArrayList<>();
//list存strs[i]
list.add(strs[i]);
//map存key和list,key作为同组的判断
map.put(key,list);
//result存入list
result.add(list);
}
}
return result;
}
}
- 同构字符串(面试高频题)
class Solution {
public boolean isIsomorphic(String s, String t) {
/*
字符串同构
8.18
*/
//使用哈希表,s-t映射,如果map中有key为s的字符,但是value却不是对应的t字符,返回false
HashMap<Character,Character> map = new HashMap<>();
for(int i=0;i<s.length();i++){
char s1 = s.charAt(i);
char t1 = t.charAt(i);
if(map.containsKey(s1)){
//再判断s1的value是否是t1,如果不是,说明不是abb和baa的类型,
if(map.get(s1) != t1){
return false;
}
}else{
//如果map中没有s1,但是却有t1
if(map.containsValue(t1)){
return false;
}
}
map.put(s1,t1);
}
return true;
}
}
- 设计hashmap(boolean数组和int数组解决最基本的put和get)
class MyHashMap {
/*
设计hashmap
7.26
*/
//两个数组,keys存放元素,contains判断是否存在
int[] keys;
boolean[] contains;
/** Initialize your data structure here. */
public MyHashMap() {
keys = new int[1000001];
contains = new boolean[1000001];
}
/** value will always be non-negative. */
public void put(int key, int value) {
//直接存
keys[key] = value;
contains[key] = true;
}
/** Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key */
public int get(int key) {
if(contains[key]){
return keys[key];
}else{
return -1;
}
}
/** Removes the mapping of the specified value key if this map contains a mapping for the key */
public void remove(int key) {
//直接将contains置为false
contains[key] = false;
}
}
- 两数之和(面试高频题)
class Solution {
public int[] twoSum(int[] nums, int target) {
/*
两数之和
7.22
*/
//思路:哈希表 + 前缀和
Map<Integer,Integer> map = new HashMap<>();
for(int i=0;i<nums.length;i++){
int sum = target - nums[i];
if(map.containsKey(sum)){
return new int[]{i,map.get(sum)};
}
map.put(nums[i],i);
}
return new int[0];
}
}