-
(力扣88)给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。
说明:初始化 nums1 和 nums2 的元素数量分别为 m 和 n。你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
示例:
输入:nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
输出: [1,2,2,3,5,6]
- 解析:合并两个数组,也就是将nums2放到nums1当中,那么我们想一想num1里面的元素要移动吗?当然要移动了,nums1和nums2中的元素都要做相应的移动,很明先给出来的两个数组都是有序数组,我们要将nums2放到nums1中的话,我们要先找到每个元素要放的位置。如果我们将nums2中的元素和num1中的元素从第一个开始比较,也就是比如要将nums2中的5放入到nums1中,我们就要从1开始和5比较,直至到5>3,然后将5放到3后面,那么依次类推,那么这样时间复杂度就是O(n)了,当然这样是可以解决这个问题的,但我们有更好的解决方法吗?当然,我们很明显的看到我们的数组是升序排列的,所以我们两个数组都从后面开始比较,降序的顺序,先比较6和3,6>3,所以6就是最大的,所以放到nums1中的最后一个位置,接下来比较5和3,同样的方法,同样的操作,最后比较2和3,2<3,3往后移,然后2和2比较,2=2,所以2就放入到mus1中2的后面。
所以我们定义一个指针指向nums1中的最后一个位置,定义两个角标分别表示两个数组的最后一个元素的角标,当6放入到nums1中的最后一个位置时,指向nums1最后一个位置的指针前移,同时数组nums2指向最后一个元素的角标也前移,指向数字5,再比较5和3,依次比较。(如果nums2没有元素了nums1还有元素直接跳出循环;如果nums1没有元素了,nums2还有元素,就将nums2中的元素依次放入到nums1中)
public void merge(int[] nums1, int m, int[] nums2, int n) {
int index1=m-1;
int index2=n-1;
int index=m+n-1;
while(true){
if(index1<0||index2<0){
break;
}
if(nums1[index1]>nums2[index2]){//nums1元素后移
nums1[index--]=nums1[index1--];
}else{//nums2元素后移
nums1[index--]=nums2[index2--];
}
}
if(index2>=0){
for(int i=0;i<=index2;i++){
nums1[i]=nums2[i];
}
}
}
2.(力扣35)给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
示例 1:
输入: [1,3,5,6], 5
输出: 2
示例 2:
输入: [1,3,5,6], 2
输出: 1
示例 3:
输入: [1,3,5,6], 7
输出: 4
示例 4:
输入: [1,3,5,6], 0
输出: 0
- 思路解析:其实这道题就是二分查找的运用,二分查找又称折半查找,为什么要用二分查找?很简单我们看题目已经很明确的告诉我们给定一个排序的数组,也就是数组已经有序,然后要求要找到目标值,那么很符合二分查找的要求,在二分查找中我们定义三个指针,一个指向头(最小)low,一个指向尾(最大)high,还有一个指针指向数组中间的位置((low+high)/2)mid;先根据low和high求出mid,比较mid和目标数的大小,如然目标数小于mid,则在目标数的左边查找,且high前移到mid前面的角标处;如果mid等于目标值,输出mid位置就是目标值的位置;如果mid比目标值小,则目标数在mid的右半部分,low指针右移至mid的后一个角标处,然后再次进行折半查找,比较方法和上面相同,直至找到目标数的角标。如果没找到就将元素插入到当前数组,然后返回所在数组的角标
public int searchInsert(int[] nums, int target) {
if(nums==null||nums.length==0){
return 0;
}
int low=0;
int high=nums.length-1;
int mid=(low+high)/2;
if(nums[low]==target){ //如果目标等于low,返回low
return low;
}
if(nums[high]==target){ //如果目标等于high,返回high
return high;
}
while(low<=high){
if(nums[mid]==target){ //如果目标等于mid,返回mid
return mid;
}
if(nums[mid]<target){
low=mid+1; //如果mid<目标,Low右移至mid+1
}else{
high=mid-1; //如果mid>目标,high左移至mid-1
}
mid=(low+high)/2;
}
return low;
}
3.(力扣66)给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。你可以假设除了整数 0 之外,这个整数不会以零开头。
示例 1:
输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。
示例 2:
输入: [4,3,2,1]
输出: [4,3,2,2]
解释: 输入数组表示数字 4321。
- 思路解析:看到这个题我们可以看到输入的是一个数组,输出的也是一个数组,我们可以理解为数组的后面给了一个进位,比如说是[1,2,3],在该数字的基础上加1,也就是数组给3所在的位子上给了一个进位,结果就为[1,2,4];在如数组[1,2,9],给该数组加1按照我们的思路9给了一个进位,这个时候数组9所在的位置是不是变成了0,也就是9+1=10,我们需要给前一位一个进位,2就变成了3,最后数组为[1,3,0],也就是加完之后对10进行取余运算
public int[] plusOne(int[] digits) {
int carry=1;
int num=0;
for(int i=digits.length-1;i>=0;i--){ //对数组进行遍历
num=digits[i]+carry;
digits[i]=num%10;
carry=num/10;
if(carry==0){
break;
}
}
if(carry==1){
int[] arr=new int[digits.length+1];
arr[0]=1;
return arr;
}else{
return digits;
}
}
4.(力扣20)给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:
输入: "()"
输出: true
示例 2:
输入: "()[]{}"
输出: true
示例 3:
输入: "(]"
输出: false
示例 4:
输入: "([)]"
输出: false
示例 5:
输入: "{[]}"
输出: true
- 思路解析:一拿到这个问题首先我们要明白这是一个有关栈的问题,所以我们先要创建一个栈对象出来,然后要将字符串中的值遍历出来,这个时候我们要调用charAt()方法,将字符串的值取出来,接下来我们要判断这个栈是不是为空,如果为空,则直接将字符串值进栈,如果不为空我们要先取出栈顶元素再判断(根据括号相对于的ASCII值相减等-1或者-2),如果是,则栈里面的元素出栈,如果不是再进栈。
public boolean isValid(String s) {
Stack<Character> stack=new Stack<Character>();// 创建栈对象
for(int i=0;i<s.length();i++){ //遍历栈元素
char c=s.charAt(i); //取字符串的值
if(stack.isEmpty()){ //判空,如果为空,c进栈
stack.push(c);
}else{
char topc=stack.peek(); //获取栈顶元素
if(topc-c==-1||topc-c==-2){ //判断相减是否为-1或者-2
stack.pop(); //是的话出栈
}else{
stack.push(c);//不是,c进栈
}
}
}
return stack.isEmpty();
}
5.(力扣240)编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target。该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
示例:
现有矩阵 matrix 如下:
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
给定 target = 5,返回 true。
给定 target = 20,返回 false。
- 思路解析:看到这个题很多人第一反应就是二分查找,包括我在内!当然这种方法是可以的,但是题目要求编写一个高效的算法,所以这种方法pass,再者我们可以试着每一行进行二分查找,这样时间复杂度就是O(mlogn),这样时间复杂度也很高,并没有很高效,我们可以采用宏观的二分查找并不是真的二分查找,从矩阵18的位置开始网右上方开始查找,目标数小于该数字往上走,如果大于则往右走,你肯定问我为什么要从18开始走?我们很明显的看到如果从1或者30走,因为1是最小的,30是最大的,所以没法判断从哪个方向走,So我们可以选择从15或者18开始进行宏观的二分查找,那什么时候找不到了呢?当位置X小于0或者Y超出列,就找不到指定元素。
public boolean searchMatrix(int[][] matrix, int target) {
if(matrix==null||matrix.length==0||matrix[0].length==0){
return false;
}
int row=matrix.length;
int col=matrix[0].length;
int x=row-1;
int y=0;
while(true){
if(x<0||y>=col){
return false;
}
if(matrix[x][y]<target){
y++;
}else if(matrix[x][y]>target){
x--;
}else{
return true;
}
}
}
6.(力扣209)给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。如果不存在符合条件的连续子数组,返回 0。
示例:
输入: s = 7, nums = [2,3,1,2,4,3]
输出: 2
解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。
- 思路解析:·不知道是否还记得之前所讲过的滑窗问题,那么这个问题我们就可以用滑窗的思想来解决,但这里是连续的滑窗,我们定义一个i指向滑窗的头,定义一个j指向滑窗的尾,就拿给的例子[2,3,1,2,4,3]来讲i和j最开始指向2,当j往后走走到3,2+3=5<7,j继续后移2+3+1=6<7,j继续后移2+3+1+2=8>=7,这时候长度等于4,大于等于7的时候我们要缩,剪掉元素,那么剪掉i所指的元素2后为6<7,j后移3+1+2+4=10>=7,此时长度为4,所以再缩10-3=7>=7,此时长度为3,还有缩7-1=6<7,(此时i在2的位置)j后移2+4+3=9>=7,此时长度为3,我们要缩i后移,9-2=7>=7,此时长度为2,只要大于等于7就要缩,所以i后移,7-4=3<7,这是j在最后一个元素,所以不能再往后走,综上最小的连续数组长度为2.
public int minSubArrayLen(int s, int[] nums) {
if(nums==null||nums.length==0){
return 0;
}
int len=0; //长度
int i=0; //滑窗头
int sum=0; //滑窗中元素和
for(int j=0;j<nums.length;j++){
sum+=nums[j];
while(sum>=s){
len=len==0?j-i+1:Math.min(len, j-i+1);
sum-=nums[i++];
}
}
return len;
}
7.(力扣54)给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。
示例 1:
输入:
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
输出: [1,2,3,6,9,8,7,4,5]
示例 2:
输入:
[1, 2, 3, 4],
[5, 6, 7, 8],
[9,10,11,12]
输出: [1,2,3,4,8,12,11,10,9,5,6,7]
- 思路解析:从给出的示例我们可以看到,是将矩阵从右到左,从上到下,从右到左,从下到上依次将元素遍历出来了,那么怎样判断拐点,要么走超了,要么这个元素已经遍历过了,所以我们考虑到将方向定义到数组里面,向右走X不变Y+1,向下走x+1,Y不变,所以X={0,1,0,-1},Y={1,0,-1,0}这样两个方向数组,然后我们再依次判断依次遍历,每次加上方向数组的对于角标值,大致顺序图如下:
public List<Integer> spiralOrder(int[][] matrix) {
ArrayList<Integer> list=new ArrayList<Integer>(); //创建list集合对象
if(matrix==null||matrix.length==0||matrix[0].length==0){
return list;
}
int x=0; //X从0开始
int y=0; //Y从0开始
int R=matrix.length; //行
int C=matrix[0].length; //列
boolean[][] visited=new boolean[R][C]; //标记是否走过
int[] deltX={0,1,0,-1}; //X方向变化数组
int[] deltY={1,0,-1,0}; //Y方向变化数组
int dir=0; //刚开始的方向(方向数组角标)
for(int i=0;i<R*C;i++){
list.add(matrix[x][y]);
visited[x][y]=true;
int nx=x+deltX[dir];
int ny=y+deltY[dir];
if(nx>=0&&nx<R&&ny>=0&&ny<C&&!visited[nx][ny]){
x=nx;
y=ny;
}else{
dir=(dir+1)%4;
x+=deltX[dir];
y+=deltY[dir];
}
}
return list;
}