数组 题解(剑指Offer与leetcode,持续更新中)

数组 题解(剑指Offer与leetcode,持续更新中)

leetcode 39 组合总和

题目

给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。

candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。 

对于给定的输入,保证和为 target 的唯一组合数少于 150 个。

分析

数组与动态规划结合的也比较紧密

实现方式
	1.回溯算法
	2.动态规划算法中的背包问题

代码


leetcode 215 数组中第k个最大元素

题目

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

示例 1:

输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:

输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4

分析

用到了堆排序

--堆排序
	建立一个大根堆,做k-1次删除操作后堆顶元素就是要找的答案。要搞懂【建堆】、【调整】、【删除】过程
	
--基于快速排序的选择方法
	【快速选择】
	通过改进快速排序来解决这个问题:
		在分解过程中,会对子数组进行划分
			如果划分得到的q正好就是我们需要的下标,就直接返回a[q];
			否则,如果q比目标下标小,就递归右子区间,否则递归左子区间。
		这样就把原来递归的两个区间变为递归一个区间,提高了效率,这就是快速选择算法。	

代码

--【快速选择】
class Solution{
    public int findKthLargest(int[] nums,int k){
        return quickSelect(nums,0,k-1,nums.length-1); 
    }
    private int quickSelect(int[] arr,int left,int kth,int right){
        int curPartition = partition(arr,left,right);
        if(curPartition==kth){
            return arr[curPartition];
        }else if(curPartition<kth){
            return quickSelect(arr,curPartition+1,kth,right);
        } else{
            return quickSelect(arr,left,kth,curPartition-1);
        }
    }
    partition(int[] arr,int left,int right){
        int pivotIndex = left + (int)(Math.random()*(right-left+1));
        swap(arr,pivotIndex,right);
        int index = left -1;
        for(int i=left;i<right;i++){
            if(arr[i]>arr[right]){
                index+=1;
                swap(arr,index,i);
            }
        }
        index+=1;
        swap(arr,index,right);
        return index;
    }
    private void swap(int[] arr,int left,int right){//交换 arr[left] 与 arr[right]
        int temp=arr[left];
        int arr[left]=arr[right];
        int arr[left]=temp;    
    }
}

leetcode 11 盛水最多的容器

题目

给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai)(i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

分析

 二维平面求容器最多容纳的水,可以转换为求矩形的最大面积
 或者说是求滑动窗口的最大面积
 area=宽*高
  =(left-right)*Math.min(height[left],height[right])
 问题是如何去更新 滑动窗口的左右边界?
 	1.用穷举的办法
 		for(int i=0;i<height.length;i++){
 			for(int j=0;j<i;j++){
 				area =(i-j)* Math.min(height[j],height[i]);
 				maxArea=Math.max(area,maxArea);
 			}
 		}
 		return maxArea;
 		很遗憾,超时了
 	2.考虑用双指针实现该问题
    	左指针,右指针
    	left		right
    	核心一点就是:
    	一旦height[left]<height[right] 则将left++;反之将 right--

代码

class Solution {
    public int maxArea(int[] height) {
        if(height.length==0) return 0;
        int left=0;//左指针
        int right=height.length-1;//右指针
        int area=0;//矩形的面积
        int maxArea=0;//要返回的最大面积
        while(left<right){
            area=Math.min(height[left],height[right])*(right-left);
            maxArea=Math.max(area,maxArea);
            if(height[left]<=height[right]){
                left++;
            }else{
                right--;
            }
        }
        return maxArea;
        // for(int i=1;i<height.length;i++){
        //     for(int j=i-1;j>=0;j--){
        //         area=(i-j)*Math.min(height[i],height[j]);
        //         maxArea=Math.max(area,maxArea);
        //     }
        // }
        // return maxArea; 这个方法超时
    }
}

leetcode 739 每日温度

题目

请根据每日 气温 列表 temperatures ,请计算在每一天需要等几天才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 0 来代替。
例 1:

输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]
示例 2:

输入: temperatures = [30,40,50,60]
输出: [1,1,1,0]
示例 3:

输入: temperatures = [30,60,90]
输出: [1,1,0]

分析

1.暴力解法
	i~n j~n (j=i+1开始),遍历整个数组
2.单调栈
https://leetcode-cn.com/problems/daily-temperatures/solution/leetcode-tu-jie-739mei-ri-wen-du-by-misterbooo/
定义一个栈
	对数组进行遍历,用数组中的每一个元素去 和 temperatures[栈顶元素]比较
	

代码

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int n=temperatures.length;
        int [] arr=new int[n];
        for(int i=0;i<n;i++){
            for(int j=i+1;j<n;j++){
                if(temperatures[j]>temperatures[i]){
                    arr[i]=j-i;
                    break;//找到就break,进入下一个循环
                }
            }
        }
        return arr;        
    }
}
//单调栈
class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        Stack<Integer> stack=new Stack<>();
        int length=temperatures.length;
        int[] result=new int[length];
        for(int i=0;i<length;i++){
            while(!stack.isEmpty() && temperatures[i]>temperatures[stack.peek()]){
                int pre=stack.pop();
                result[pre]=i-pre;
            }
            stack.add(i);
        }
        return result;
    }
}

leetcode 621 任务调度器

题目

给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表。其中每个字母表示一种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。在任何一个单位时间,CPU 可以完成一个任务,或者处于待命状态。

然而,两个 相同种类 的任务之间必须有长度为整数 n 的冷却时间,因此至少有连续 n 个单位时间内 CPU 在执行不同的任务,或者在待命状态。

你需要计算完成所有任务所需要的 最短时间 。

示例 1:

输入:tasks = ["A","A","A","B","B","B"], n = 2
输出:8
解释:A -> B -> (待命) -> A -> B -> (待命) -> A -> B
     在本示例中,两个相同类型任务之间必须间隔长度为 n = 2 的冷却时间,而执行一个任务只需要一个单位时间,所以中间出现了(待命)状态。 
示例 2:

输入:tasks = ["A","A","A","B","B","B"], n = 0
输出:6
解释:在这种情况下,任何大小为 6 的排列都可以满足要求,因为 n = 0
["A","A","A","B","B","B"]
["A","B","A","B","A","B"]
["B","B","B","A","A","A"]
...
诸如此类
示例 3:

输入:tasks = ["A","A","A","A","A","A","B","C","D","E","F","G"], n = 2
输出:16
解释:一种可能的解决方案是:
     A -> B -> C -> A -> D -> E -> A -> F -> G -> A -> (待命) -> (待命) -> A -> (待命) -> (待命) -> A

分析

代码

leetcode 581 最短无序连续子数组

题目

给你一个整数数组 nums ,你需要找出一个 连续子数组 ,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。
请你找出符合题意的 最短 子数组,并输出它的长度。
示例 1:

输入:nums = [2,6,4,8,10,9,15]
输出:5
解释:你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。
示例 2:

输入:nums = [1,2,3,4]
输出:0
示例 3:

输入:nums = [1]
输出:0

分析

连续子数组是什么意思?

升序排序又是什么意思?

双指针如何解决此类问题?

可以用单调栈来实现?

代码

leetcode 448 找到所有数组中消失的数字

题目

给你一个含 n 个整数的数组 nums ,其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字,并以数组的形式返回结果。
示例 1:

输入:nums = [4,3,2,7,8,2,3,1]
输出:[5,6]
示例 2:

输入:nums = [1,1]
输出:[2]

分析

数组下标与数组元素之前的关系
由于 \textit{nums}nums 的数字范围均在 [1,n][1,n] 中,我们可以利用这一范围之外的数字,来表达「是否存在」的含义。

具体来说,遍历 \textit{nums}nums,每遇到一个数 xx,就让 \textit{nums}[x-1]nums[x−1] 增加 n。

代码

class Solution {
    public List<Integer> findDisappearedNumbers(int[] nums){
        int n=nums.length;
        for(int num:nums){
            int x=(num-1) % n;
            nums[x]+=n;
        }
        
        List<Integer> ret= new ArrayList<Integer>();
        for(int i=0;i<n;i++){
            if(nums[i]<=n){
                ret.add(i+1);
            }
        }
        return ret;
    }
}

leetcode 136 只出现一次的数字

题目

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:
输入: [2,2,1]
输出: 1
示例 2:

输入: [4,1,2,1,2]
输出: 4

分析

对时间复杂度有要求,要求线性时间,然后不使用额外的空间
1.hashmap 实现
    HashMap<Integer,Integer> hashmap=new HashMap<>();
    hashmap.put(num,count);
2.异或

	class Solution {
    public int singleNumber(int[] nums) {
        int ans= nums[0];
        if(nums.length>=1){
            for(int i=1;i<nums.length;i++){
                ans=ans^nums[i];
            }
        }
        return ans;
    }
}

代码

class Solution {
    public int singleNumber(int[] nums) {
        HashMap<Integer,Integer> hashmap=new HashMap<>();
        for(Integer num:nums){
            Integer count = hashmap.get(num);
            if(count==null){
                count=1;
            }else{
                ++count;
            }
            hashmap.put(num,count);
        }
        for(Integer num:hashmap.keySet()){
            Integer count=hashmap.get(num);
            if(count==1){
                return num;
            }            
        }
        return -1;
    }
}

leetcode 347 前k个高频元素

题目

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

示例 1:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

示例 2:
输入: nums = [1], k = 1
输出: [1]

分析

1.桶排序法
	使用哈希表统计频率,统计完成后,创建一个数组,将频率作为数组下标,对于出现频率不同的数字集合,存入对应的下标即可
2.最小堆
	
	

代码

1.桶排序法
class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        List<Integer> res=new ArrayList();
        HashMap<Integer,Integer> map = new HashMap<>();
        for(int num:nums){
            if(map.containsKey(num)){
                map.put(num,map.get(num)+1);//map中已经存在了num,将num+1即可。
            }else{
                map.put(num,1);
            }
        }      
        //桶排序
        //将频率作为数组下标,对于出现频率不同的数字集合,存入对应的数组下标
        List<Integer>[] list=new List[nums.length+1];
        
        for(int key:map.keySet()){
            int i=map.get(key);
            if(list[i]==null){
                list[i]=new ArrayList();//这个是什么意思?防止空指针,防止i第一次进来的时候报错
            }
            list[i].add(key);
        }
        for(int i=list.length-1;i>=0 && res.size()<k;i--){
            if(list[i]==null) continue;
            res.addAll(list[i]);
        }
        int[] res1 = res.stream().mapToInt(Integer::valueOf).toArray(); 
        return res1;
    }
}
class Solution{
    public List<Integer> topKFrequent(int[] nums,int k){
        //使用字典,统计每个元素出现的次数,元素为键,元素出现的次数为值
        HashMap<Integer,Integer> map= new HashMap();
        for(int num:nums){
            if(map.containsKey(num)){
                map.put(num,map.get(num)+1);
            }else{
                map.put(num,1);
            }
        }
        //遍历map,用最小堆保存频率最大的k个元素
        PriorityQueue<Integer> pq=new PriorityQueue<>(new Comparator<Integer>(){
            @override
            public int compare(Integer a,Integer b){
                return map.get(a)-map.get(b);
            }
        });
        for(Integer key:map.keySet()){
            if(pq.size() < k){
                pq.add(key);
            } else if(map.get(key)>map.get(pq.peek())){
                pq.remove();
                pq.add(key);
            }
        }
        //取出最小堆中的元素
        List<Integer> res=new ArrayList<>();
        while(!pq.isEmpty()){
            res.add(pq.remove());
        }
        return res;
    }
}

leetcode 406 根据身高重建队列

题目

假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。

请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。

输入:people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]
输出:[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
解释:
编号为 0 的人身高为 5 ,没有身高更高或者相同的人排在他前面。
编号为 1 的人身高为 7 ,没有身高更高或者相同的人排在他前面。
编号为 2 的人身高为 5 ,有 2 个身高更高或者相同的人排在他前面,即编号为 01 的人。
编号为 3 的人身高为 6 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
编号为 4 的人身高为 4 ,有 4 个身高更高或者相同的人排在他前面,即编号为 0、1、2、3 的人。
编号为 5 的人身高为 7 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
因此 [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]] 是重新构造后的队列。

分析

	1.对数组[hi,ki]的第一个元素hi降序排序,第二个元素ki升序排序

代码

	public int[][] reconstructQueue(int[][] people){
		//按数组第一个元素进行降序,按第二个元素进行升序
		Arrays.sort(people,new Comparator<int[]>(){
			@override
			public int compare(int[] person1,int[] person2){
				if(person1[0] != person2[0]){
					//第一个元素不相等时,第一个元素降序
					return person2[0] - person1[0];
				}else{
					//第一个元素相等时,第二个元素升序
					return person1[1] - person2[1];
				}
			}
		});
		//新建一个list,用于保存结果集
		List<int[]> list = new LinkedList<>();
		for(int i=0;i<people.length;i++){
			//结果集中元素个数大于第i个人前面应有的人数时,将第i个结果插入到结果集的people[i][1]
			if(list.size() > people[i][1]){
				list.add(people[i][1],people[i]);
			}else{
				list.add(list.size(),people[i]);
			}	
		}
		//将list转化为数组
		return list.toArray(new int[list.size()] []);
	}

leetcode 46 全排列

题目

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

示例 1:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

示例 2:
输入:nums = [0,1]
输出:[[0,1],[1,0]]

示例 3:
输入:nums = [1]
输出:[[1]]

分析

用到了回溯算法

## 回溯算法与深度优先遍历
	状态重置

代码

class Solution{
    public List<List<Integer>> permute(int[] nums){
        int len=nums.length;
        //使用动态数组保存所有可能的全排列
        List<List<Integer>>  res = new ArrayList<>();
        if(len==0){
            return res;
        }
        List<Integer> curr=new ArrayList<>();//用于存储每次全排列的结果
        helper(nums,curr,res);
        return res;        
    }
    private void helper(int[] nums,List<Integer> curr,List<List<Integer>> res){
        if(curr.size() == nums.length){//已经全排列完毕
            res.add(new ArrayList<>(curr));
            return;
        }
        for(int i=0;i<nums.length;i++){
            if(!curr.contains(nums[i])){//curr中不包含 nums[i] 元素
                curr.add(nums[i]);//加入curr中不存在的元素
                helper(nums,curr,res);
                curr.remove(curr.size()-1);//状态重置
            }
        }
        return;
    }
}

leetcode 48 旋转图像

题目

给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。

你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]

分析

## 1.使用辅助数组
	对于矩阵中的元素matrix[row][col] 矩阵旋转后的新位置为 matrixNew[col][n-row-1],n为数组行数
## 2.原地旋转
	需要找到旋转的规律
	

代码

class Solution{
    public void rotate(int[][] matrix) {
        int n=matrix.length;
        int[][] matrixNew = new int[n][n];
        
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                matrixNew[j][n-i-1]= matrix[i][j];
            }
        }
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                matrix[i][j]= matrixNew[i][j];
            }
        }
    }
}
//旋转
class Solution{
    public void rotate(int[][] matrix){
        int n=matrix.length;
        for(int i=0;i<n/2;i++){
            for(int j=0;j< (n+1)/2;j++ ){
                int temp=matrix[i][j];
                matrix[i][j] = matrix[n-j-1][i];
                matrix[n-j-1][i] =  matrix[n-i-1][n-j-1];
                matrix[n-i-1][n-j-1]=matrix[j][n-i-1];
                matrix[j][n-i-1]=temp;
            }
        }
    }
}

leetcode 287 寻找重复数

题目

给定一个包含 n + 1 个整数的数组 nums ,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。

假设 nums 只有 一个重复的整数 ,找出 这个重复的数 。

你设计的解决方案必须不修改数组 nums 且只用常量级 O(1) 的额外空间。

分析

1.二分查找
2.hashmap 

代码

//二分查找
class Solution {
    public int findDuplicate(int[] nums) {
        //二分法
        int left=1;
        int right=nums.length-1;
        while(left<right){
            int mid = left+(right-left)/2;//从[left,right]中间位置取一个元素
            int cnt = 0;
            for(int num:nums){
                if(num<=mid){
                    cnt+=1;//计算小于等于mid元素的数量
                }
            }
            if(cnt>mid){
                right=mid;//重复的元素在此区间[left,mid]
            }else{
                left=mid+1;//重复的元素在此区间[mid+1,right]
            }
        }
        return left;
    }
}
// hashmap
class Solution {
    public int findDuplicate(int[] nums) {
        HashMap<Integer,Integer> hashmap=new HashMap<>();
        for(int i=0;i<nums.length;i++){
            if(hashmap.containsKey(nums[i])){
                return nums[i];
            }else{
                hashmap.put(nums[i],1);
            }
        }
        return 0;
    }
}

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

题目

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

进阶:你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?

示例 1:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]

示例 2:
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]

示例 3:
输入:nums = [], target = 0
输出:[-1,-1]

分析

## 二分法
如何判断最后一个位置
	先找>=target的第一个
	再找>target的第一个

代码

class Solution{
    //先找 >= target的第一个
    //再找 > target的第一个
    
    public int[] searchRange(int[] nums,int target){
        int left =search(nums,target);
        int right=search(nums,target+1);
        if(left==nums.length|| nums[left]!=target ){//说明找不到
        	return new int[]{-1,-1};
        }
        return new int[]{left,right-1};
    }
    //找>=target的第一个
    public int search(int[] nums,int target){
        int left =0;
        int right=nums.length;
        while(left<right){
            int mid=(right+left)>>1;
            if(nums[mid]>=target){//顺序递增的数组
                right=mid;//在左半边
            }else{
                left=mid+1;//在右半边
            }
        }
     	return left;   
    }
}

leetcode 560 和为k的子数组

题目

给你一个整数数组 nums 和一个整数 k ,请你统计并返回该数组中和为 k 的连续子数组的个数。
示例 1:

输入:nums = [1,1,1], k = 2
输出:2
示例 2:

输入:nums = [1,2,3], k = 3
输出:2

分析

## 1.暴力枚举

## 2. 前缀和+Hash表优化
	前缀和
	pre[i] = pre[i-1]+nums[i];
	[j...i]子数组的前缀和
	pre[i]-pre[j-1] ==k ;
	转换为:
	pre[j-1]=pre[i]-k;
	
	哈希表hashmap,以和为键,出现次数为对应的值,记录pre[i]出现的次数

代码

//暴力枚举
class Solution {
    public int subarraySum(int[] nums, int k) {
        int count=0;
        for(int i=0;i<nums.length;i++){
            int res=0;
            for(int j=i;j>=0;j--){
                res+=nums[j];
                if(res==k){
                    count++;
                }
            }
        }
        return count;
    }
}
//前缀和,数组优化
public class Solution{
    public int subarraySum (int[] nums ,int k){
        int count=0;//次数
        int pre=0;//存储前缀和的变量
        HashMap<Integer,Integer> mp = new HashMap<>();//
        map.put(0,1);//初始化
        
        for(int i=0;i<nums.length;i++){
            pre += nums[i];
            if(mp.containsKey(pre-k)){
                count += mp.get(pre-k);//找到了和为k的子数组,次数+1
            }
            map.put(pre,mp.getDefault(pre,0)+1);
        }
        return count;
    }
}

leetcode 581 最短无序连续子数组

题目

给你一个整数数组 nums ,你需要找出一个 连续子数组 ,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。
请你找出符合题意的 最短 子数组,并输出它的长度。

示例 1:

输入:nums = [2,6,4,8,10,9,15]
输出:5
解释:你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。
示例 2:

输入:nums = [1,2,3,4]
输出:0
示例 3:

输入:nums = [1]
输出:0

分析

算法思想:
	无序子数组中最小元素的正确位置可以决定左边界,最大元素的正确位置可以决定右边界。

#1.排序
	将nums表示为三段子数组拼接的形式,分别记作numsA,numsB,numsC。当对numsB进行排序的时候,整个数组变为有序。
	要求找到最短的numsB,即找到最大的numsA和numsC之和。
	将原数组nums排序与原数组进行比较,取最长的相同前缀numsA,取最长的相同后缀numsC,从而取得最短的numsB。
	
#2.一次遍历

代码

//1.排序
class Solution{
    public int findUnsortedSubarray (int[] nums){
        if(isSorted(nums)){
            return 0;//如果是升序数组,则直接返回0
        }
        int[] numsSorted = new int[nums.length];
        System.arraycopy(nums,0,numsSorted,0,nums.length);//数组拷贝
        Arrays.sort(numsSorted);//数组排序
        int left=0;//左指针
        
        while(nums[left] == numsSorted[left]){
            left++;
        }
        int right=nums.length-1;
        while(nums[right] == numsSorted[right]){
            right--;
        }
        return right-left+1;//找到了无序数组的左右边界,计算无序子数组的长度
        
    }
    public boolean isSorted(int[] nums){
        for(int i=1;i<nums.length;i++){
            if(nums[i]<nums[i-1]){//判断是否为升序数组
                return false;
            }
        }
        return true;
    }
}

leetcode 283 移动零

题目

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:

输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:

必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。

分析

## 1.两次遍历
	第一次遍历:记录非零元素位置
	第二次遍历,将非零元素填充进数组,其余位置置为0
## 左右指针
	参考快速排序的思想,将0作为中间点
	把不等于0的放到中间点的左边,等于0的放到其右边
	
	使用两个指针 i,j,只要nums[i] != 0,就交换nums[i]和 nums[j],这样非0的就会被交换到后面去了	

代码

class Solution {
	public void moveZeroes(int[] nums) {
		if(nums==null) {
			return;
		}
		//两个指针i和j
		int j=0;

        for(int i=0;i<nums.length;i++){
            if(nums[i]!=0){
                int temp=nums[i];
                nums[i]=nums[j];
                nums[j++]=temp;
            }
        }
	}
}	

leetcode 300 最长递增子序列

动态规划问题

题目

用动态规划解决此类问题:

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
 
示例 1:

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
示例 2:

输入:nums = [0,1,0,3,2,3]
输出:4
示例 3:

输入:nums = [7,7,7,7,7,7,7]
输出:1

分析

用动态规划的思想解决此类问题
状态转移定义:
	dp[i]的值代表前i个数字的最长序列长度。
转移方程:
	dp[i]=max(dp[i],dp[j]+1) for j in [0,i);

初始状态:
	dp[i]所有元素置为1,每个元素都至少可以单独成为子序列,长度为1
返回值:
	dp列表最大值,即可得到全局上升子序列长度

代码

class Solution {
    public int lengthOfLIS(int[] nums) {
        if(nums.length==0) return 0;
        int res=0;//返回值
        int[] dp=new int[nums.length];//动态规划数组
        Arrays.fill(dp,1);//初始化为1
        for(int i=0;i<nums.length ;i++){
            for(int j=0;j<i;j++){
                if(nums[j]<nums[i]){//如果是递增的子序列
                    dp[i]=Math.max(dp[i],dp[j]+1);//判断
                }
            }
            res=Math.max(dp[i],res);//比较,选出局部最大值
        }
        return res;//全局最大值
    }
}

leetcode 33 搜索旋转排序数组

题目

整数数组 nums 按升序排列,数组中的值 互不相同 。

在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。

示例 1:

输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4
示例 2:

输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1
示例 3:

输入:nums = [1], target = 0
输出:-1

分析

首先要判断要找的数据在哪一个递增的数组里,然后用常规的二分法解决

代码

//正确的代码
class Solution {
    public int search(int[] nums, int target) {
        if(nums.length==0) return -1;
        int left=0;
        int right=nums.length-1;
        
        while(left<=right){
            int mid=left+(right-left)/2;
            
            if(nums[mid]==target) return mid;
            if(nums[mid]<nums[nums.length-1]){//右边有序
                //右半边
                if(target <=nums[right]&& target>nums[mid]){
                    left=mid+1;
                }else{
                    right=mid-1;
                }
            }else{//左边有序
                //目标值在左边
                if(target >= nums[left] && target < nums[mid]){        
                    right=mid-1;
                //目标值在右边    
                }else{
                    left=mid+1;
                }
            }
        }
        return -1;//如果还找不到就返回-1
    }
}
// 有问题的代码
class Solution {
    public int search(int[] nums, int target) {
        if(nums.length==0) return -1;
        int reovte=0;
        for(int i=1;i<nums.length;i++){
            if(nums[i]<nums[i-1]){
                reovte =i ;
                break;
            }
        }
        int [] numspre =Arrays.copyOf(nums,reovte-1);
        int [] numspost=Arrays.copyOfRange(nums, reovte, nums.length);        
        if(target>nums[nums.length-1]){
            binarySearch(numspre,target);
        }else{
            binarySearch(numspost,target);
        }
        return -1;
    }
    //二分查找
    public  int binarySearch(int[] nums,int target){
        int left=0;
        int right=nums.length-1;
        while(left<=right){
            int mid=left+(right -left)/2;
            if(nums[mid]==target) return mid;
            if(target>nums[mid]&&target<=nums[right]){
                left=mid+1;
            }else{
                right=mid-1;
            }
        }
        return -1;
    }
}

leetcode 75 颜色分类

题目

给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

此题中,我们使用整数 012 分别表示红色、白色和蓝色。

示例 1:

输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
示例 2:

输入:nums = [2,0,1]
输出:[0,1,2]
示例 3:

输入:nums = [0]
输出:[0]
示例 4:

输入:nums = [1]
输出:[1]

分析

方法一:升序排序
	快速排序

方法二:partition
	考察的是快速排序的子过程 partition:通过一次遍历把数组分为三个部分
	
	“循环不变量”

代码

//普通排序算法
class Solution {
    public void sortColors(int[] nums) {
         Arrays.sort(nums);//选择一种排序算法,例如快速排序
    }
}
//“循环不变量” 排序

leetcode 78 子集

题目

给定一个整数数组 nums,数组中的元素互不相同。返回该数组所有可能的子集(幂集)
    解集不能包含重复的子集,可以按任意顺序返回解集。
    
示例 1:

输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例 2:

输入:nums = [0]
输出:[[],[0]]

分析

//1.迭代

//2.回溯

代码

//2.回溯
class Solution{
    public List<List<Integer>> subsets(int[] nums){
        List<List<Integer>> res=new ArrayList<>();
        backtree(0,nums,res,new ArrayList<Integer>());
        return res;
    }
    public void backtree(int i,int[] nums,List<List<Integer>> res,ArrayList<Integer> tmp){//tmp 子集路径,
        res.add(new ArrayList<>(tmp));
        for(int j=i;j<nums.length;j++){
            tmp.add(nums[j]);//将当前元素加入路径
            backtree(j+1,nums,res,tmp);//递归进入下一层
            tmp.remove(tmp.size()-1);//撤销选择
        }
    }
}

leetcode 79 单词搜索

题目

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true


分析

如何确定 那些水平相邻或者垂直相邻的单元格?

如何判断数组与矩阵元素相等?

##1.回溯
	首先找到矩阵中第一个与数组中相等的元素
	然后从找到的元素的前后左右中再与数组中元素进行比较
		如果找到则继续对数组中的下一位元素继续查找
		找不到则返回false
	一直将数组中所有元素遍历完	

代码

//这段代码需要好好理解一下

public class Solution {

    private static final int[][] DIRECTIONS = {{-1, 0}, {0, -1}, {0, 1}, {1, 0}};
    private int rows;
    private int cols;
    private int len;
    private boolean[][] visited;
    private char[] charArray;
    private char[][] board;

    public boolean exist(char[][] board, String word) {
        rows = board.length;
        if (rows == 0) {
            return false;
        }
        cols = board[0].length;
        visited = new boolean[rows][cols];

        this.len = word.length();
        this.charArray = word.toCharArray();
        this.board = board;
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (dfs(i, j, 0)) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean dfs(int x, int y, int begin) {
        if (begin == len - 1) {
            return board[x][y] == charArray[begin];
        }
        if (board[x][y] == charArray[begin]) {
            visited[x][y] = true;
            for (int[] direction : DIRECTIONS) {
                int newX = x + direction[0];
                int newY = y + direction[1];
                if (inArea(newX, newY) && !visited[newX][newY]) {
                    if (dfs(newX, newY, begin + 1)) {
                        return true;
                    }
                }
            }
            visited[x][y] = false;
        }
        return false;
    }

    private boolean inArea(int x, int y) {
        return x >= 0 && x < rows && y >= 0 && y < cols;
    }
}

作者:liweiwei1419
链接:https://leetcode-cn.com/problems/word-search/solution/zai-er-wei-ping-mian-shang-shi-yong-hui-su-fa-pyth/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

leetcode 105 从前序与中序遍历序列构造二叉树

题目

从前序与中序遍历序列构造二叉树:
	给定一棵树的前序遍历preorder与中序遍历 inorder,请构造二叉树并返回其根节点

分析

1.迭代
    利用栈和一个指针辅助进行二叉树的重建构造。初始时栈中存放了根节点
    
    
    
2.递归    

代码

class Solution{
    public TreeNode buildTree(int[] preorder,int [] inorder){
        if(preorder==null || preorder.length == 0){
            return null;
        }
        TreeNode root = new TreeNode(preorder[0]);//二叉树根节点
        Deque<TreeNode> stack= new LinkedList<TreeNode>();//建一个新栈
        stack.push(root);//将根节点入栈
        int inorderIndex = 0;//中序遍历的索引
        for(int i=1;i<preorder.length;i++){//对前序遍历的数组进行遍历
            int preorderVal=preorder[i];
            TreeNode node = stack.peek();//栈顶节点
			if(node.val != inorder[InorderIndex]){//不相等时
                node.left = new TreeNode(preorderVal);//加入到左子节点
                stack.push(node.left);
            }else{//节点值与中序遍历元素相等时
                while(!stack.isEmpty()&&stack.peek().val==inorder[InorderIndex]){//栈顶元素也要与中序遍历元素相等
                    node=stack.pop();
                    inorderIndex++;
                }
                node.right = new TreeNode(preorderVal);//加入到右子节点
                stack.push(node.right);
            }
        }
        return root;
    }
}

leetcode 128 最长连续序列

题目

给定一个未排序的整数数组nums,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度

示例 1:

输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
示例 2:

输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9

分析

怎样判断是连续的?
## 1. 利用哈希表 
		HashSet<Integer> hashset = new HashSet<>();
		hashset.add(num);
	如果当前元素存在过,那么接着判断比当前元素-1的元素是否存在过,依次判断	

代码

//1.哈希表
class Solution{
    public class int longestConsecutive(int[] nums){
        Set<Integer> num_set = new HashSet<Integer>();//建立一个哈希表
        for(int num:nums){
            num_set.add(num);
        }//将数组中的元素放入哈希表里(不包含重复元素)
        int longestStreak = 0; //定义最长连续序列长度
        for(int num:num_set){//对哈希表里的元素进行遍历
            if(!num_set.contains(num-1)){//如果哈希表里包含比当前元素还要小1的元素
                int currentNum = num;//当前元素
                int currentStreak =1;//当前长度
                
                while(num_set.contains(currentNum+1)){//一直找下去,直到不连续为止
                    currentNum+=1;
                    currentStreak += 1;
                }
                longestStreak = Math.max(longestStreak,currentStreak);//将当前的最长序列长度与最终的最终序列长度比较,返回较大值
            }
        }
        return longestStreak;//返回最大值
    }
}

leetcode 152 乘积最大子数组

动态规划

题目

给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。

示例 1:

输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:

输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。

分析

动态规划:
	需要根据nums[i]的正负性进行讨论:
		nums[i]正数,
			maxF[i]=Math.max(maxF[i-1]*nums[i],Math.max(nums[i],minF[i-1]*nums[i]));		
		nums[i]负数,
			minF[i]=Math.min(minF[i-1]*nums[i],Math.min(nums[i],maxF[i-1]*nums[i]));
	由于第i个状态只和第i-1个状态相关,根据滚动数组思想,可以只用两个变量来维护i-1时刻的状态,一个维护fmax,一个维护fmin

代码

class Solution{
    public int maxProduct(int[] nums){
        int length = nums.length;
        int[] maxF = new int[length];
        int[] minF = new int[length];
        System.arraycopy(nums,0,maxF,0,length);
        System.arraycopy(nums,0,minF,0,length);
        for(int i=1;i<length;i++){
            maxF[i]=Math.max(maxF[i-1]*nums[i],Math.max(nums[i],minF[i-1]*nums[i]));
            minF[i]=Math.min(minF[i-1]*nums[i],Math.min(nums[i],maxF[i-1]*nums[i]));
        }
        int ans=maxF[0];
        for(int i=1;i<length;++i){
            ans=Math.max(ans,maxF[i]);
        }
        return ans;
    }
}

leetcode 169 多数元素

题目

给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:

输入:[3,2,3]
输出:3
示例 2:

输入:[2,2,1,1,1,2,2]
输出:2

分析

## 1.哈希表
## 2.排序

代码

// 2.排序
class Solution {
    public int majorityElement(int[] nums) {
        Arrays.sort(nums);
        return nums[nums.length / 2];
    }
}

leetcode 200 岛屿数量

题目

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。
示例 1:

输入:grid = [
  ["1","1","1","1","0"],
  ["1","1","0","1","0"],
  ["1","1","0","0","0"],
  ["0","0","0","0","0"]
]
输出:1
示例 2:

输入:grid = [
  ["1","1","0","0","0"],
  ["1","1","0","0","0"],
  ["0","0","1","0","0"],
  ["0","0","0","1","1"]
]
输出:3

分析

深度优先搜索DFS
	网格结构比二叉树结构要更复杂一些,其实是一种简化版的图结构。
广度优先搜索BFS
并查集

代码

class Solution{
    private int res;
    public int numIslands(char[][] grid){
     	res=0;//初始化为零
        for(int i =0;i<grid.length;i++){//遍历
            for(int j=0;j<grid[0].length;j++){
                if(grid[i][j] == '1'){//陆地
                    dfsGrid(grid,i,j);//深度优先遍历
                    res++;//岛屿数量+1
                }
            }
        }
        return res;
    }
    private void dfsGrid(char[][] grid ,int row,int col){
     	if(row>=grid.length||col>=grid[0].length||row<0||col<0){//出界
            return ;
        }   
        if(grid[row][col]!= '1' ){
            return;
        }
        grid[row][col]='2';//已经遍历过
        dfsGrid(grid,row-1,col);//左
        dfsGrid(grid,row+1,col);//右
        dfsGrid(grid,row,col-1);//上
        dfsGrid(grid,row,col+1);//下
    }
}

leetcode 221 最大正方形

题目

在一个由 '0''1' 组成的二维矩阵内,找到只包含 '1' 的最大正方形,并返回其面积。

输入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
输出:4

输入:matrix = [["0","1"],["1","0"]]
输出:1

输入:matrix = [["0"]]
输出:0

分析

动态规划:
	对于每个位置i,j,检查在矩阵中该位置的值:
		- 如果该位置的值是0,则dp(i,j)=0,当前位置不可能在由1组成的正方形中;
		- 如果该位置的值是1,则dp(i,j)的值由其上方、左方、左上方三个相邻位置的dp值决定。
			dp(i,j)=min(dp(i-1,j),dp(i-1,j-1),dp(i,j-1))+1;	

代码

class Solution {
    public int maximalSquare(char[][] matrix) {
        int maxSide = 0;
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
            return maxSide;
        }
        int rows = matrix.length, columns = matrix[0].length;
        int[][] dp = new int[rows][columns];
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < columns; j++) {
                if (matrix[i][j] == '1') {
                    if (i == 0 || j == 0) {
                        dp[i][j] = 1;
                    } else {
                        dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;
                    }
                    maxSide = Math.max(maxSide, dp[i][j]);
                }
            }
        }
        int maxSquare = maxSide * maxSide;
        return maxSquare;
    }
}

leetcode 238 除自身以外数组的乘积

题目

给你一个长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。

输入: [1,2,3,4]
输出: [24,12,8,6]

分析

## 两次遍历
	1.第一次遍历(从左向右),求出nums[i]左边的乘积和
	2.第二次遍历(从右向左),求出nums[i]右边的乘积和

代码

class Solution{
    public int[] productExceptSelf(int[] nums){
        int length = nums.length;
        int[] answer = new int[length];
        //answer[i] 表示索引i左侧所有元素的乘积
        //因为 索引为'0'的元素左侧没有元素,所以 answer[0]=1
        answer[0] = 1;
        for(int i=1;i<length;i++){
            answer[i] = nums[i-1]*answer[i-1];
        }
        
        //R 为右侧所有元素的乘积
        //刚开始右边没有元素,所以R=1
        int R = 1;
        for(int i=length-1;i>=0;i--){
            answer[i]*=R;
            //R 需要包含右边的乘积
            R*=nums[i];
        }
        return answer;//要返回的元素
    }
}

leetcode 56 合并区间

题目

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。
示例 1:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3][2,6] 重叠, 将它们合并为 [1,6].
示例 2:

输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4][4,5] 可被视为重叠区间。

分析

数组 左右端点 
合并子区间

代码


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值