20190917练习题总结——编程题

  1. (力扣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;
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值