盛最多的水
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
思路:两个指针,l,r 两边往中间遍历,
height[i]<height[j],i++;
height[i]>height[j],j–;
class Solution {
public int maxArea(int[] height) {
int maxArea=0;//最大面积
int tmp=0;//记录临时面积
int n=height.length;
int i=0;
int j=n-1;
//双指针
while(i<j){
if(height[i]<height[j]){
tmp=(j-i)*height[i];
i++;
}else{
tmp=(j-i)*height[j];
j--;
}
maxArea=Math.max(maxArea,tmp);
}
return maxArea;
}
}
三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
思路:先排序,三个指针k,i,j ,固定k,然后i,j往中间遍历,找到目标值,就i++,j–,同时要注意不能有重复的三元组
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
//排序
Arrays.sort(nums);
int n=nums.length;
int k=0,i=0,j=0;
List<List<Integer>> res=new ArrayList<>();
if(n<3||nums[0]>0){
return res;
}
while(k<n){
if(nums[k]>0){
break;
}
i=k+1;
j=n-1;
while(i<j){
if(nums[k]+nums[i]+nums[j]==0){
List<Integer> list=new ArrayList<>();
list.add(nums[k]);
list.add(nums[i]);
list.add(nums[j]);
res.add(list);
//避免相同的nums[i]和nums[j]再次出现
while(i<j&&nums[i]==nums[i+1]){
i++;
}
i++;
while(i<j&&nums[j]==nums[j-1]){
j--;
}
j--;
}else if(nums[k]+nums[i]+nums[j]<0){
i++;
}else{
j--;
}
}
//避免相同的nums[k]的出现
while(k<n-1&&nums[k]==nums[k+1]){
k++;
}
k++;
}
return res;
}
}
数对和
设计一个算法,找出数组中两数之和为指定值的所有整数对。一个数只能属于一个数对。
示例 1:
输入: nums = [5,6,5], target = 11
输出: [[5,6]]
思路:先排序,两个指针l,r首尾开始往中间遍历
class Solution {
public List<List<Integer>> pairSums(int[] nums, int target) {
Arrays.sort(nums);
int n=nums.length;
int i=0,j=n-1;
List<List<Integer>> res=new ArrayList<>();
while(i<j){
if(nums[i]+nums[j]<target){
i++;
}else if(nums[i]+nums[j]>target){
j--;
}else{
List<Integer> list=new ArrayList<>();
list.add(nums[i]);
list.add(nums[j]);
res.add(list);
i++;
j--;
}
}
return res;
}
}
面试题 16.06. 最小差
给定两个整数数组a和b,计算具有最小差绝对值的一对数值(每个数组中取一个值),并返回该对数值的差
示例:
输入:{1, 3, 15, 11, 2}, {23, 127, 235, 19, 8}
输出:3,即数值对(11, 8)
思路:
* 先对两个数组进行排序,
* 然后两个指针分别指向两个数组的初始位置 i=0,j=0计算两个数的差值的绝对值abs(a[i]-b[j]
* min=Math.min(min,abs(a[i]-b[j]),
* 然后把两数中的较小的数的指针右移,如此循环,如果min=0,结束return
注:两个数的 差值可能超出int的范围,所以计算的时候用long型,返回的时候转成int
import static java.lang.Math.abs;
class Solution {
public int smallestDifference(int[] a, int[] b) {
//排序
Arrays.sort(a);
Arrays.sort(b);
long min=Integer.MAX_VALUE;
int i=0,j=0;
while(i<a.length&&j<b.length){
long tmp=abs((long)a[i]-(long)b[j]);//取两数差的绝对值
if(tmp<=min){//如果小于最小值,更新最小值
min=tmp;
}
//如果两数差的绝对值最小值已经是0,就结束循环
if(min==0){
break;
}
if(a[i]<b[j]){
i++;
}else{
j++;
}
}
//返回结果,转成int型
return (int)min;
}
}
面试题 16.16. 部分排序
给定一个整数数组,编写一个函数,找出索引m和n,只要将索引区间[m,n]的元素排好序,整个数组就是有序的。注意:n-m尽量最小,也就是说,找出符合条件的最短序列。函数返回值为[m,n],若不存在这样的m和n(例如整个数组是有序的),请返回[-1,-1]。
示例:
输入: [1,2,4,7,10,11,7,12,6,7,16,18,19]
输出: [3,9]
思路:
双指针,start,end
end:从前往后找,同时保持数组递增,如果出现导致数组不递增的元素,就用end标记,遍历一遍数组之后,end的标记位置,就是需要排序的区间的右区间
start:同理,从后往前,遍历一遍数组,保持递减,start标记的最终位置就是需要排序的左区间
例如:
1 2 3 5 4 5 6 1 7 8 9
从前往后:1 2 3 5 4 5 6 1 7 8 9
end标记的元素有:4,1,但最终右区间end=元素1 的下标
从后往前:1 2 3 5 4 5 6 1 7 8 9
start标记的元素有:6,5,4,5,3,2,但最终左区间start=元素2的下标
class Solution {
public int[] subSort(int[] array) {
int n=array.length;
int start=-1,end=-1;
int max=Integer.MIN_VALUE,min=Integer.MAX_VALUE;
int[] res=new int[2];
//从前往后找,保持数组的递增,找到最后一个导致数组不递增的那个点,就是无序的end
for(int i=0;i<n;i++){
if(array[i]>=max){
max=array[i];
}else{
end=i;
}
}
//如果end==-1,那么说明该数组是有序递增的,不需要排序
if(end==-1){
res[0]=-1;
res[1]=-1;
return res;
}
//从后往前,保持数组的递减,找到最后一个导致数组不递减的那个点,就是无序的start
for(int i=n-1;i>=0;i--){
if(array[i]<=min){
min=array[i];
}else{
start=i;
}
}
res[0]=start;
res[1]=end;
return res;
}
}
寻找重复数
给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。
你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。
思路:
快慢指针,low,fast,
1.low=nums[low],fast=nums[nums[fast]]两个指针第一次相与是确定有重复的数,
2.恢复快指针的速度,low=nums[low],fast=nums[fast],第二次相遇是确定重复的数
class Solution {
public int findDuplicate(int[] nums) {
//快慢指针,第一次相与判定有环,把快指针重置开始的位置,速度恢复慢指针相同的速度,
//第二次相与就是开始的入口
int low=0,fast=0;
do{
fast=nums[nums[fast]];
low=nums[low];
}while(fast!=low);
fast=0;
while(fast!=low){
fast=nums[fast];
low=nums[low];
}
return fast;
}
}
移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作
思路:
两个指针l,r,刚开始都指向索引0位置,有指针找到不为零的数和左指针的0互换,这样就把0换到了后面
class Solution {
public void moveZeroes(int[] nums) {
//两个指针,l,r,右指针遍历数组,遇到非零的数就和左指针的0互换
int l=0,r=0;
while(r<nums.length){
if(nums[r]!=0){
swap(nums,l,r);
l++;
}
r++;
}
}
public void swap(int[] nums,int l,int r){
int tmp=nums[l];
nums[l]=nums[r];
nums[r]=tmp;
}
}
环形链表
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
public class Solution {
public ListNode detectCycle(ListNode head) {
if(head==null){
return null;
}
ListNode low=head,fast=head;
while(fast!=null){
low=low.next;
if(fast.next!=null){
fast=fast.next.next;
}else{
return null;
}
//如果两个指针相遇,说明链表存在环
if(low==fast){
fast=head;//把fast重新指向head
while(fast!=low){
fast=fast.next;
low=low.next;
}
return low;
}
}
return null;
}
}
相交链表
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
思路:
我把你走过的路走一遍,你把我走过的路走一遍,如果有缘,总有一天我们会相遇
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA==null||headB==null){
return null;
}
ListNode p=headA,q=headB;
while(p!=q){
p=p==null?headB:p.next;
q=q==null?headA:q.next;
}
return p;
}
}