1、给定一个长度为 n 的整数数组,你的任务是判断在最多改变 1 个元素的情况下,该数组能否变成一个非递减数列。
我们是这样定义一个非递减数列的: 对于数组中所有的 i (1 <= i < n),满足 array[i] <= array[i + 1]。
示例 1:
输入: [4,2,3]
输出: True
解释: 你可以通过把第一个4变成1来使得它成为一个非递减数列。
示例 2:
输入: [4,2,1]
输出: False
解释: 你不能在只改变一个元素的情况下将其变为非递减数列。
class Solution {
public boolean checkPossibility(int[] nums) {
if(nums.length < 3){
return true;
}
int count = 0;
for(int i=0;i<nums.length-1;i++){
if(nums[i] > nums[i+1]){
count++;
if(count > 1){
break;
}
if(i-1 >=0&&nums[i-1] > nums[i+1]){
nums[i+1] = nums[i];
}else{
nums[i] = nums[i+1];
}
}
}
return count <= 1;
}
}
2、给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:
可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
public class Solution {
public int lengthOfLIS(int[] nums) {
if (nums.length == 0) {
return 0;
}
int[] dp = new int[nums.length];
dp[0] = 1;
int maxans = 1;
for (int i = 1; i < dp.length; i++) {
int maxval = 0;
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
maxval = Math.max(maxval, dp[j]);
}
}
dp[i] = maxval + 1;
maxans = Math.max(maxans, dp[i]);
}
return maxans;
}
}
3、给定在 xy 平面上的一组点,确定由这些点组成的矩形的最小面积,其中矩形的边平行于 x 轴和 y 轴。
如果没有任何矩形,就返回 0。
示例 1:
输入:[[1,1],[1,3],[3,1],[3,3],[2,2]]
输出:4
示例 2:
输入:[[1,1],[1,3],[3,1],[3,3],[4,1],[4,3]]
输出:2
class Solution {
public int minAreaRect(int[][] points) {
Map<Integer, List<Integer>> rows = new TreeMap();
for (int[] point: points) {
int x = point[0], y = point[1];
rows.computeIfAbsent(x, z-> new ArrayList()).add(y);
}
int ans = Integer.MAX_VALUE;
Map<Integer, Integer> lastx = new HashMap();
for (int x: rows.keySet()) {
List<Integer> row = rows.get(x);
Collections.sort(row);
for (int i = 0; i < row.size(); ++i)
for (int j = i+1; j < row.size(); ++j) {
int y1 = row.get(i), y2 = row.get(j);
int code = 40001 * y1 + y2;
if (lastx.containsKey(code))
ans = Math.min(ans, (x - lastx.get(code)) * (y2-y1));
lastx.put(code, x);
}
}
return ans < Integer.MAX_VALUE ? ans : 0;
}
}
4、给定一个字符串 S,计算 S 的不同非空子序列的个数。
因为结果可能很大,所以返回答案模 10^9 + 7.
示例 1:
输入:“abc”
输出:7
解释:7 个不同的子序列分别是 “a”, “b”, “c”, “ab”, “ac”, “bc”, 以及 “abc”。
示例 2:
输入:“aba”
输出:6
解释:6 个不同的子序列分别是 “a”, “b”, “ab”, “ba”, “aa” 以及 “aba”。
示例 3:
输入:“aaa”
输出:3
解释:3 个不同的子序列分别是 “a”, “aa” 以及 “aaa”。
public static int distinctSubseqII(String s) {
int mod = (int)1e9+7;//因为字符串会很长,子串个数很多,取模保存,不会越界
char[] strTochar = s.toCharArray();
int[] dp = new int[s.length()];//保存读取到该字符时候字符串的子串个数
for(int i=0;i<s.length();i++) {
if(i==0)
dp[i] = 1;//如果字符串长度是1,直接返回1.
else {
dp[i] = (2*dp[i-1]+1)%mod;
for(int j=i-1;j>=0;j--) {
if(strTochar[j]==strTochar[i]) {//找到在[0,i-1]区间内距离此时字符相同最近的字符
/*三元运算符,来判断是不是查找到起始位置了,
* 如果此时的字符与起始字符一致,那么就只需要
* 减去一个该字符即可。
*/
dp[i] = j==0?(dp[i]+mod-1)%mod:(dp[i]+mod-dp[j-1]-1)%mod;
break;//找到直接跳出循环,因为我们只找最近的那个相同的字符。
}
}
}
}
return dp[s.length()-1]%mod;//返回结果
}
5、
在经典汉诺塔问题中,有 3 根柱子及 N 个不同大小的穿孔圆盘,盘子可以滑入任意一根柱子。一开始,所有盘子自上而下按升序依次套在第一根柱子上(即每一个盘子只能放在更大的盘子上面)。移动圆盘时受到以下限制:
(1) 每次只能移动一个盘子;
(2) 盘子只能从柱子顶端滑出移到下一根柱子;
(3) 盘子只能叠在比它大的盘子上。
请编写程序,用栈将所有盘子从第一根柱子移到最后一根柱子。
你需要原地修改栈。
示例1:
输入:A = [2, 1, 0], B = [], C = []
输出:C = [2, 1, 0]
示例2:
输入:A = [1, 0], B = [], C = []
输出:C = [1, 0]
class Hanno{
public static void main(String[] args){
//盘子的个数 出发 中间 目的
hanno(3,"X","Y","Z");
}
public static void hanno(int n,String begin,String mid,String end){
if(n==1){
System.out.println(begin+" -> "+end);
}else{
hanno(n-1,begin,end,mid);//第二个盘子从x出发,目的地是y
System.out.println(begin+" -> "+end);
hanno(n-1,mid,begin,end);//前两个盘子,从y到z
}
}
}
6、递归乘法。 写一个递归函数,不使用 * 运算符, 实现两个正整数的相乘。可以使用加号、减号、位移,但要吝啬一些。
示例1:
输入:A = 1, B = 10
输出:10
示例2:
输入:A = 3, B = 4
输出:12
class Solution {
public int multiply(int A, int B) {
if(A < 1000){
if(A == 1)
return B;
return multiply(--A, B)+B;
}else{
if(B == 1)
return A;
return multiply(A, --B)+A;
}
}
}
7、给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。
class Solution {
public List<List<Integer>> generate(int numRows) {
List<List<Integer>> ans = new ArrayList<List<Integer>>();
for(int i = 0; i < numRows; i++){
List<Integer> array = new ArrayList<Integer>();
for(int j = 0; j <= i; j++){
if(j == 0 || j == i){
array.add(1);
}else{
array.add(ans.get(i-1).get(j-1) + ans.get(i-1).get(j));
}
}
ans.add(array);
}
return ans;
}
}
8、给定一副牌,每张牌上都写着一个整数。
此时,你需要选定一个数字 X,使我们可以将整副牌按下述规则分成 1 组或更多组:
每组都有 X 张牌。
组内所有的牌上都写着相同的整数。
仅当你可选的 X >= 2 时返回 true。
示例 1:
输入:[1,2,3,4,4,3,2,1]
输出:true
解释:可行的分组是 [1,1],[2,2],[3,3],[4,4]
示例 2:
输入:[1,1,1,2,2,2,3,3]
输出:false
解释:没有满足要求的分组。
示例 3:
输入:[1]
输出:false
解释:没有满足要求的分组。
示例 4:
输入:[1,1]
输出:true
解释:可行的分组是 [1,1]
示例 5:
输入:[1,1,2,2,2,2]
输出:true
解释:可行的分组是 [1,1],[2,2],[2,2]
class Solution {
public boolean hasGroupsSizeX(int[] deck) {
int N = deck.length;
int[] count = new int[10000];
for (int c: deck)
count[c]++;
List<Integer> values = new ArrayList();
for (int i = 0; i < 10000; ++i)
if (count[i] > 0)
values.add(count[i]);
search: for (int X = 2; X <= N; ++X)
if (N % X == 0) {
for (int v: values)
if (v % X != 0)
continue search;
return true;
}
return false;
}
}
9、给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:
答案中不可以包含重复的四元组。
示例:
给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
满足要求的四元组集合为:
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> ans=new ArrayList<List<Integer>>();
Arrays.sort(nums);
for(int i=0;i<nums.length-2;i++)
{
if(i!=0 && nums[i]==nums[i-1])
continue;
for(int j=i+1;j<nums.length;j++)
{
if(j!=i+1 && nums[j]==nums[j-1])
continue;
int l=j+1,r=nums.length-1;
while(l<r)
{
if(l!=j+1 && nums[l]==nums[l-1] || nums[i]+nums[j]+nums[l]+nums[r]<target)
l++;
else if(r!=nums.length-1 && nums[r]==nums[r+1] || nums[i]+nums[j]+nums[l]+nums[r]>target)
r--;
else
{
List<Integer> temp=new ArrayList<Integer>();
temp.add(nums[i]);
temp.add(nums[j]);
temp.add(nums[l]);
temp.add(nums[r]);
ans.add(temp);
l++;r--;
}
}
}
}
return ans;
}
}
10、给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,2,2,3,3,4],
函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
你不需要考虑数组中超出新长度后面的元素。
class Solution {
public int removeDuplicates(int[] nums) {
if(nums == null || nums.length == 0) return 0;
int p = 0;
int q = 1;
while(q < nums.length){
if(nums[p] != nums[q]){
nums[p + 1] = nums[q];
p++;
}
q++;
}
return p + 1;
}
}
11、给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1:
给定 nums = [3,2,2,3], val = 3,
函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
你不需要考虑数组中超出新长度后面的元素。
class Solution {
public int removeElement(int[] nums, int val) {
int ans = 0;
for(int num: nums) {
if(num != val) {
nums[ans] = num;
ans++;
}
}
return ans;
}
}
12、给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。
如果数组中不存在目标值,返回 [-1, -1]。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]
class Solution
{
public:
vector<int> searchRange(vector<int>& nums, int target)
{
vector<int> res={-1,-1};
int low=0,high=nums.size()-1;
while(low<=high)
{
int mid= low+(high-low)/2;
if(target==nums[mid])
{
if(mid==nums.size()-1) res[1]=mid; ///找上边界
else if(nums[mid+1]>target) res[1]=mid;
}
if(target<nums[mid]) high=mid-1;
else if(target>=nums[mid]) low=mid+1;
}
int low1=0,high1=nums.size()-1;
while(low1<=high1)
{
int mid1= low1+(high1-low1)/2;
if(target==nums[mid1])
{
if(mid1==0)res[0]=mid1;
else if(nums[mid1-1]<target)res[0]=mid1; ///找下边界
}
if(target<=nums[mid1]) high1=mid1-1;
else if(target>nums[mid1]) low1=mid1+1;
}
return res;
}
13、给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
示例 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
class Solution {
public int searchInsert(int[] nums, int target) {
int ans = 0;
for(int i = 0; i < nums.length; i++) {
if (nums[i] < target) ans = i+1;
if (nums[i] >= target) return i;
};
return ans;
}
}