第一课刷题
题目目录:
2、给定一个数组arr,和一个整数aim,请返回哪两个位置的数可以加出aim来。
5、给定数组arr和整数num,共返回有多少个子数组满足如下情况:max(arr[i..j]) - min(arr[i..j]) <= num
【题目】
【思路】
1、需要一个help变量=1
2、取n的最高位
a)将n/help,如果大于10,则help*=10
b)n/help = 最高位
3、取n的最低位
n%10
4、进行对比,如相等,则去除最高位和最低位,继续执行上述操作。
a)去除最高位:n=(n%help)
b)去除最低位:n/=10
c)Help/=100
【Code】
public static boolean isHuiWen(int n)
{
if(n<0)
return false;
int help = 1;
while(n/help >= 10)
help*=10;
while(n!=0)
{
if(n/help != n%10)//对比最高位和末尾
return false;
n = (n%help) /10;//去除最高位和末尾
help /=100;//同时去除两位,所以/10²
}
return true;
}
【题目】
给定一个数组arr,和一个整数aim,请返回哪两个位置的数可以加出aim来。
例如:arr = {2, 7, 11, 15},target = 9 返回{0,1},因为arr[0] + arr[1] = 2 + 7 = 9
可以假设每个数组里只有一组答案
【思路】
1、暴力穷举
2、哈希表:时间复杂度O(N),空间复杂度O(N)
3、双指针:时间复杂度O(N*logN),空间复杂度O(n)
a) 对数组排序,在排序过程中记下原数据在原数组中的下标,形成数组
b) 左指针L指向0位置,右指针R指向N-1位置
c) L+R>target?R--:L++
d) c步骤的实质就是从两端进行尝试,根据数组有序的特性和指针的和的大小来判别哪段指针移动
表面上看是哈希表方法最优,哈希表虽增删查等操作都是O(1),但哈希函数的处理过程中产生的常数项很高。如果追求极致的话双指针方法较优。
【Code】
public static int[] twoSum(int[] nums,int target)
{
int[] indices = new int[nums.length];//存放排完序后的数据元素在原未排序数组中的下标
//由于返回的需要是下标,且在排序过程中会丢失下标信息
//因此需要将下标数组在排序过程中与数据一起变换
for(int i = 0;i<indices.length;i++)
indices[i] = i;
sort(nums,indices);
int l = 0;
int r = nums.length-1;
int sum = 0;
while(l<r)
{
sum = nums[l]+nums[r];
if(sum>target)
r--;
else if (sum<target) {
l++;
}
else {
return new int[] {indices[l],indices[r]};
}
}
return new int[] {-1,-1};
}
拓展:
1、如果给有序数组让你返回所有不重复的组的答案呢?
例如133557 target=8 明显答案为1-7、3-5
还是双指针的办法,只不过当指向3和5时,第一次找到该满足组,然后移动左指针到下一个不是当前指向的元素,即如果下一元素仍与上一指向元素相同,则跳过,直到不等
2、如果给target,要返回3个数之和等于target?
对有序数组arr,从0位置元素a,对后面1-N-1找两数之和能够等于target-a的结果,与0位置组合,即为答案。如果后序有与上一位置相同的元素依旧执行跳过
【思路】
如果:
list = 1 调整之后1。
list = 1->2 调整之后1->2
list = 1->2->3 调整之后1->2->3
list = 1->2->3->4 调整之后1->3->2->4
list = 1->2->3->4->5 调整之后1->3->2->4->5
list = 1->2->3->4->5->6 调整之后1->4->2->5->3->6
list = 1->2->3->4->5->6->7 调整之后1->4->2->5->3->6->7
根据上面的规律,调整一个任意长度的链表。
【思路】
规律:
当链表长度为偶数时,将整个链表以中间做为分割点划分为左右两部分,调整为左1-右1-左2-右2-...
当链表长度为奇数时,先无视最后一个数,进行偶数调整,再讲最后一个数追加进链表末端。
笔试写法:需要辅助空间
1、遍历链表,计算链表长度
2、开辟数组存放链表节点,长度为奇数时数组长度为奇数-1
3、遍历数组,将链表节点存进数组
4、创建help数组,遍历数组,如果i小于size/2,将help[i*2]=arr[i],即先将左边一半的数放入调整后的对应位置中。等左边一半的数放入完毕后,i来到右边,则将右边一半的数放入其对应位置中,位置关系为:help[ (i-(arr.length/2)) *2+1 ] = arr[i];
面试解法:不需要辅助空间,直接在链表进行调整然后返回
【Code】
【题目四】
【Code】
public static Node ChangeTree2DoubleLink(Node head)
{
if(head==null)
return null;
return process(head)[0];
}
//process功能:将head转化为双向链表并返回链表头结点,返回整个链表的头结点和尾节点
public static Node[] process(Node head)
{
if(head==null)
return null;
//分别找左子树和右子树的头尾节点集合
Node[] leftNode = process(head.letf);
Node[] rightNode = process(head.right);
head.letf = null;
head.right= null;
//连接之前检查是否为空。非空才连接
if(leftNode[1]!=null)
{
leftNode[1].right = head;
head.letf = leftNode[1];
}
if(rightNode[0]!=null)
{
rightNode[0].letf =head;
head.right = rightNode[0];
}
//整个链表的头结点就是左子树的头结点,如果左子树为空,则头结点为head
Node left = leftNode[0]!=null?leftNode[0]:head;
//尾结点就是右子树的尾结点,如果右子树尾结点为空,则头结点为head
Node right = rightNode[1]!=null?rightNode[1]:head;
return new Node[] {left,right};
}
【题目五】
【思路】
使用两个窗口更新结构记录窗口内的最大值max与最小值min
从0开始往右扩,每扩一步都检查是否达标(max-min<=num),如达标则继续扩,一直扩到不达标或越界。
当达标时位置为R,则有R+1个从0开始的子数组达标。
下一步出0位置,从1位置为头,R位置起始继续尝试往右扩,一直扩到不达标或越界。此时有R2-1+1个子数组达标,累加子数组
【Code】
public static int getNum(int[] arr,int num){
if(arr==null || arr.length==0)
return 0;
//窗口用双头队列实现
LinkedList<Integer>qmin = new LinkedList<Integer>();
LinkedList<Integer>qmax = new LinkedList<Integer>();
int i=0;
int j=0;
int res = 0;//结果
while(i<arr.length)
{
while(j<arr.length)
{
//更新两窗口内的最大值与最小值内容
while(!qmin.isEmpty() && arr[qmin.peekLast()] >=arr[j] )
qmin.pollLast();
qmin.addLast(j);
while(!qmax.isEmpty() && arr[qmax.peekLast()] <=arr[j] )
qmax.pollLast();
qmax.addLast(j);
//判断是否达标
if(arr[qmax.getFirst()] - arr[qmin.getFirst()] > num )
break;
j++;
}
//窗口内最大最小值是否过期
if(qmin.peekFirst()==i)
qmin.pollFirst();
if(qmax.peekFirst()==i)
qmax.pollFirst();
//累加结果
res+=j-i;
i++;
}
return res;
}