给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
- 暴力求解,循环2次,时间复杂度为O(n2)
- 使用HashMap,key为target-当前遍历的值,value为遍历的当前值;hashmap的key包含当遍历的值时,输出对应的key-value
public class Q1_两数之和 {
public static void main(String[] args) {
int[] nums = {
2, 5, 5, 15};
int target = 10;
int[] sum = twoSum(nums, target);
int[] sum1 = twoSum1(nums, target);
System.out.println(Arrays.toString(sum));
System.out.println(Arrays.toString(sum1));
}
public static int[] twoSum(int[] nums, int target) {
int[] res = new int[2];
for (int i = 0; i < nums.length; i++) {
for (int j = i+1; j < nums.length; j++) {
if(nums[i] + nums[j] == target){
res[0] = i;
res[1] = j;
}
}
}
return res;
}
public static int[] twoSum1(int[] nums, int target){
int[] res = new int[2];
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
if(map.containsKey(nums[i])){
res[0] = map.get(nums[i]);
res[1] = i;
}else{
map.put(target-nums[i], i);
}
}
return res;
}
}
给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。
请你找出这两个正序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
nums1 = [1, 3]、nums2 = [2];则中位数是 2.0
nums1 = [1, 2]、nums2 = [3, 4];则中位数是 (2 + 3)/2 = 2.5
利用归并排序将两个数组排序,然后找到中位数
public class Q4_寻找两个正序数组的中位数 {
public static void main(String[] args) {
int[] nums1 = {
1,3,4,7,8,9};
int[] nums2 = {
2,3,4,6,7,8};
double medianSortedArrays = findMedianSortedArrays(nums1, nums2);
System.out.println(medianSortedArrays);
}
public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
double res;
if(nums1 == null){
res = nums2[nums2.length / 2];
return res;
}
if(nums2 == null){
res = nums1[nums1.length / 2];
return res;
}
ArrayList<Integer> list = new ArrayList<>();
int length = nums1.length + nums2.length;
int l1 = 0;
int l2 = 0;
while (l1 < nums1.length && l2 < nums2.length){
if(nums1[l1] <= nums2[l2]){
list.add(nums1[l1]);
l1++;
}else{
list.add(nums2[l2]);
l2++;
}
}
while (l1 == nums1.length && l2 < nums2.length){
list.add(nums2[l2]);
l2++;
}
while (l2 == nums2.length && l1 < nums1.length){
list.add(nums1[l1]);
l1++;
}
if(length % 2 == 0){
res = (list.get(list.size()/2 - 1) + list.get(list.size()/2)) / 2.0;
}else{
res = list.get(list.size()/2);
}
return res;
}
}
给定 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。
图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例:
输入: [1,8,6,2,5,4,8,3,7] 输出: 49
- 暴力求解
- 使用两个指针;分别指向头和尾,由于面积等于两个下标的差乘以较小的高,所以每次移动小的那根
public class Q11_盛最多水的容器 {
public static void main(String[] args) {
int[] height = {
1,11,8,6,2,5,4,8,3,7,1,8,6,2,5,4,8,3,7,1,8,6,2,5,4,8,3,7};
int area1 = maxArea1(height);
System.out.println(area1);
}
//暴力匹配
public static int maxArea(int[] height) {
int area = 0;
if(height.length < 2){
return -1;
}
//i表示底的长度,j表示遍历数组下标
for (int i=height.length-1; i>=1; i--){
for (int j=0; j<height.length-i; j++){
area= Math.max(i * Math.min(height[j + i], height[j]), area);
}
}
return area;
}
public static int maxArea1(int[] height){
int area = 0;
int left = 0;
int right = height.length -1;
while (left < right){
area = Math.max(area, (right-left)*Math.min(height[left], height[right]));
int r = height[left] <= height[right] ? left++ : right--;
}
return area;
}
}
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
将数组排序,然后遍历数组
在遍历每一位时,使用两个指针,一个指向当前位的下一位,另一个指向最后一位,然后移动两个指针使两个数的和等于0-arr[i]
public class Q15_三数之和 {
public static void main(String[] args) {
int[] nums = {
-1,0,1,2,-1,4};
List<List<Integer>> lists = threeSum(nums);
for(List list: lists){
System.out.println(list);
}
}
public static List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> lists = new ArrayList<>();
//先判断数组长度是否大于3,小于3直接返回
if(nums.length<3){
return lists;
}
//对数组进行了排序
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
//如果当前数大于0,则后面的数也都大于0,不可能和为0
if(nums[i]>0){
break;
}
//如果当前i的值与前一个相同,跳过这个i
if(i>0 && nums[i]==nums[i-1]){
continue;
}
//左指针指向i的后一个
int l = i+1;
//右指针指向最后一个
int r = nums.length-1;
//满足左<右
while(l<r){
int res = nums[i]+nums[l]+nums[r];
if(res == 0){
lists.add(Arrays.asList(nums[i],nums[l],nums[r]));
//去重
while(l<r && nums[l]==nums[l+1]){
l++;
}
while(l<r && nums[r]==nums[r-1]){
r--;
}
//移动左右指针继续找
l++;
r--;
}else if(res<0){
l++;
}else if(res>0){
r--;
}
}
}
return lists;
}
}
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。
返回这三个数的和。假定每组输入只存在唯一答案。
例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.
与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).
类似于上一题,只是需要记录一个最小值,不需要保存所有结果
public class Q16_最接近的三数之和 {
public static void main(String[] args) {
int[] nums = {
-1,2,1,-4};
int target = 1;
int closest = threeSumClosest(nums, target);
System.out.println(closest);
}
public static int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int res = nums[0] + nums[1] + nums[2];
for (int i = 0; i < nums.length-1; i++) {
int l = i + 1;
int r = nums.length-1;
while(l < r){
int sum = nums[i] + nums[l] + nums[r];
if(Math.abs(sum-target) < Math.abs(res-target)){
res = sum;
}
if(sum > target){
r--;
}else if(sum < target){
l++;
}else{
return res;
}
}
}
return res;
}
}
给定一个包含 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]
]
可以转化为三数之和
public class Q18_四数之和 {
public static void main(String[] args) {
int[] nums = {
0,0,0,0};
List<List<Integer>> lists = fourSum(nums, -13);
for (List<Integer> list : lists) {
System.out.println(list);
}
}
public static List<List<Integer>> fourSum(int[] nums, int target) {
ArrayList<List<Integer>> res = new ArrayList<>();
if(nums==null || nums.length<4) return res;
Arrays.sort(nums);
for (int a = 0; a < nums.length - 3; a++) {
//去重
if(a>0 && nums[a]==nums[a-1]){
continue;
}
//当前元素可以组成的最小/大值比target大/小,则直接比较下一个值
if(nums[a] + nums[a+1] + nums[a+2] + nums[a+3] > target
|| nums[a] + nums[nums.length-1] + nums[nums.length-2] + nums[nums.length-3] < target){
continue;
}
//当前元素的下一个值
for (int b = a+1; b < nums.length - 2; b++) {
//去重
if(b > a+1 && nums[b] == nums[b-1]){
continue;
}
//可以组成的最小/大值比target大/小,则直接比较下一个值
if(nums[a] + nums[b] + nums[nums.length-1] + nums[nums.length-2] < target
|| nums[a] + nums[b] + nums[b+1] + nums[b+2] > target){
continue;
}
int c = b+1;
int d = nums.length-1;
while(c < d){
if(nums[a] + nums[b] + nums[c] + nums[d] == target){
res.add(Arrays.asList(nums[a],nums[b],nums[c],nums[d]));
c++;
while(c < d && nums[c] == nums[c-1]){
c++;
}
d--;
while(c < d && b < d && nums[d] == nums[d+1]){
d--;
}
}else if(nums[a] + nums[b] + nums[c] + nums[d] > target){
d--;
}else{
c++;
}
}
}
}
return res;
}
}
给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);
在函数里修改输入数组对于调用者是可见的。
根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}
两个指针,一个用来遍历数组,另一个用来找到与当前不同的第一个数,然后交换
public class Q26_删除排序数组中的重复项 {
public static void main(String[] args) {
int[] arr = {
0,0,1,1,1,2,2,3,3,4};
System.out.println(removeDuplicates(arr));
}
public static int removeDuplicates(int[] nums) {
if(nums == null || nums.length == 0) return 0;
if(nums.length == 1) return 1;
int i = 0;
int j = 1;
while (j < nums.length){
if (nums[j] == nums[i]){
j++;
}else{
//将不同的第一个数与相同的第一个数交换
nums[++i] = nums[j++];
}
}
return i+1;
}
}
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
给定 nums = [3,2,2,3], val = 3,
函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
你不需要考虑数组中超出新长度后面的元素。
- 使用两个指针,将与val相同的数与第一个不同的数交换,最后还要遍历数组找到不为val的最后一个下标
- 使用两个指针,指向头和尾,将头开始遍历的与val相同的和从尾开始遍历的不同的数交换,返回头遍历的下标
public class Q27_移除元素 {
public static void main(String[] args) {
int[] arr = {
2,2};
int i = removeElement1(arr, 2);
System.out.println(i);
}
public static int removeElement(int[] nums, int val) {
if(nums == null || nums.length == 0 || (nums.length == 1 && nums[0] == val)) return 0;
for (int i = 0; i < nums.length; i++) {
if(nums[i] == val){
for (int j = i+1; j < nums.length; j++) {
if(nums[j] != val){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
}
}
//System.out.println(Arrays.toString(nums));
int i;
for (i = 0 ; i < nums.length; i++) {
if(nums[i] == val){
break;
}
}
return i;
}
public static int removeElement1(int[] nums, int val){
if(nums == null || nums.length == 0 || (nums.length == 1 && nums[0] == val)) return 0;
int left = 0;
int right = nums.length - 1;
while (left <= right){
if(nums[left] == val){
if(nums[right] != val){
nums[left] ^= nums[right];
nums[right] ^= nums[left];
nums[left] ^= nums[right];
}
right--;
}else{
left++;
}
}
//System.out.println(Arrays.toString(nums));
return left;
}
}
实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
先在数组中从后往前找到第一对逆序对,该逆序对后面的数一定是最大的排列
从数组的最后一位开始,找到第一个比逆序对中前面的数大的数,并交换两数
从逆序对前面那一位的下标开始将数组后面的数反转,则后面的数会由最大变成最小
反转过后的数组即结果
public class Q31_下一个排列 {
public static void main(String[] args) {
int[] nums = {
1,1};
nextPermutation(nums);
System.out.println(Arrays.toString(nums));
}
public static void nextPermutation(int[] nums) {
if(nums == null || nums.length == 0 || nums.length == 1) {
return;
}
int i = nums.length - 2;
//先在数组中从后往前找到第一对逆序对
while (i >= 0 && nums[i] >= nums[i+1]) {
i--;
}
if(i >= 0) {
int j = nums.length - 1;
//找到第一个比逆序对中前面的数大的数
while (j >= 0 && nums[j] <= nums[i]) {
j--;
}
//交换
swap(nums, i, j);
}
//反转
reverse(nums, i+1);
}
private static void swap(int[] nums, int i, int j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
private static void reverse(int[] nums, int start){
int i = start;
int j = nums.length - 1;
while (i < j){
swap(nums, i, j);
i++;
j--;
}
}
}
给你一个未排序的整数数组,请你找出其中没有出现的最小的正整数。
示例 1:
输入: [1,2,0]
输出: 3
示例 2:
输入: [3,4,-1,1]
输出: 2
示例 3:
输入: [7,8,9,11,12]
输出: 1
使用一个辅助数组help[nums.length+2],大小为数组长度+1,记录所出现的数字
小于0的数记录到help[0],大于数组长度的数记录到help[nums.length+1]
遍历help数组,从第2位开始,第一个为0的数即结果
public class Q41_缺失的第一个正数 {
public static void main(String[] args) {
int[] nums = {
7,8,9,11,12};
int missingPositive = firstMissingPositive(nums);
System.out.println(missingPositive);
}
public static int firstMissingPositive(int[] nums) {
int[] help = new int[nums.length + 2];
for (int i = 0; i < nums.length; i++) {
if(nums[i] > nums.length){
help[nums.length+1]++;
}else if(nums[i] <= 0){
help[0]++;
} else{
help[nums[i]]++;
}
}
//System.out.println(Arrays.toString(help));
for (int i = 1; i < help.length; i++) {
if(help[i] == 0){
return i;
}
}
return help[help.length-1];
}
}