遇到涂鸦的两个题目,拿来写一下,有理解不对的地方,望大家不吝指正。
1、将链表中的所有元素为奇数的节点移到元素为偶数节点的前面,并保证奇数之间顺序不变,偶数之间顺序不变。
示例:
4→5→3→1→2 ==> 5→3→1→4→2
1==>1
2→1 ==> 1→2
==> (链表为空)
链表节点定义为:
class Node {
public Node next;
public int value;
}
Node swap(Node list)
要求如下:
1. swap函数要求对节点的指针/引用进行操作(不得创建任何新的链表节点)
2. 不得使用任何库函数/API,如需使用类似功能, 请自行实现
3. 不得将链表转化为其他类型数据结构再进行交换,如数组等
解题思路:
定义:以p2表示当前节点,pre表示上一节点,p1表示上一个奇数节点
调整方式:遍历链表,当前节点为p2,若为偶数节点,不作处理,继续下一个节点;若为奇数节点,则将p1和p2之间的节点放于p2之后,p2调整为p1之后,如图:
以p2作为p1,nex作为p2,继续执行,只需遍历一遍,即可调整奇偶节点顺序。
参考代码
public class t{
public static void main(String[] args){
int []arr=new int[]{1,3,4,6,8,5,7,10,21,3,7,15,17,22,36,19};//测试用例
Node root=new Node(2);//首节点
Node n=root;
for(int i:arr){//构造链表
Node s=new Node(i);
n.next=s;
n=s;
}
root=Swap.swap(root);//调整移动函数
while(root!=null){//输出调整后结果
System.out.print(root.value+" ");
root=root.next;
}
System.out.println();
}
}
class Node{//简单起见,不考虑set,get方法,直接公有属性访问
public Node next=null;
public int value;
public Node(int value){
this.value=value;
}
}
class Swap{//提供静态调整移动函数的类,此处简写为Swap
/*
定义:
在循环中,当前节点为p2,为奇数节点,上一个节点为pre,上一个奇数节点为p1
调整:
将两个奇数节点之间的节点调整到后面一个奇数节点之后
即 将p2的next作为pre的next,p1的next作为p2的next,p2作为p1的next
迭代:
为了避免重复遍历两个奇数节点之间的节点,从原奇数节点之后的节点继续迭代
即 下个迭代节点从原p2节点后一个节点开始,即调整后pre节点的next属性指向的节点开始
结论:
只需要遍历一次即可完成移动调整
*/
public static Node swap(Node list){//移动函数
if(list==null){//为空则直接返回
return null;
}
Node p2=list;//当前节点
Node pre=null;//循环中当前节点的上一个节点
Node p1=null;//上一个奇数节点
while(p2!=null){
if(p2.value%2!=0){//到达奇数节点
if(p1==null){//是第一个奇数节点
if(p2!=list){//且不是第一个节点
pre.next=p2.next;
p2.next=list;
list=p2;
p1=p2;//设置上一个奇数节点
p2=pre;
}else{
p1=p2;//设置上一个奇数节点
}
}else{//不是第一个奇数节点
if(pre==p1){//如果上一个节点即为上一个奇数节点
p1=p2;//只需要更新p1指向即可
}else{
pre.next=p2.next;
p2.next=p1.next;
p1.next=p2;
p1=p2;//设置上一个奇数节点
p2=pre;
}
}
}
pre=p2;
p2=p2.next;
}
return list;
}
}
2、给一个正整数 n, 找到若干个完全平方数(比如1, 4, 9, ... )使得他们的和等于 n。你需要让平方数的个数最少。示例:
给出 n = 12, 返回 3 因为 12 = 4 + 4+ 4。
给出 n = 13, 返回 2 因为 13 = 4 + 9。
思路:先举个典型的例子:
已上图为例,a1~a7表示七个整数,求由根到叶子节点路径上节点元素值之和最大是多少。
方式1:计算所有路径上的和,找出最大值
如果不存储临时数据,则重复计算量太大,如计算a1+a2+a4,之后计算a1+a2+a5,(a1+a2)的部分属于重复计算,此处数据量小,性能消耗不大,参考斐波那契数列,fi(1000)=fi(999)+fi(998),如果不保存临时数据,则fi(500)需要重复计算多少次。
方式2:由下向上,以a2保存(a2+a4,a2+a5)中的较大值,a3保存(a3+a6,a3+a7)的较大值,a1保存(a1+a2,a1+a3)的较大值,以此避免重复计算。
题目分析:
任何一个正整数n可以表达为:n=a+b*b ,以dp[a]表示a需要的平方数的个数,则dp[n]的值为dp[a]+1,如果a不为零,则令n=a,可以继续表示为:n=a+b*b,参考斐波那契数列的计算过程可知,将中间计算数据存储起来,可以极大幅度的提升计算效率(fi(n)=fi(n-1)+fi(n-2),如果没有储存中间计算结果,则fi(n-i)可能计算许多次)。
此处表现的结果则是,n=a+b*b,即dp[n]=dp[a]+1,而dp[a]已知。
参考代码
public class t3{
public static void main(String[] args){
Scanner in =new Scanner(System.in);
int s=0;
while(in.hasNextInt()){
s=in.nextInt();
System.out.println(Square.get_len(s));
}
}
}
class Square {
/*
分析:
任何一个正整数n可以表达为:n=a+b*b ,以dp[a]表示a需要的平方数的个数,
则dp[n]的值为dp[a]+1,如果a不为零,则令n=a,可以继续表示为:n=a+b*b,
参考斐波那契数列的计算过程可知,将中间计算数据存储起来,可以极大幅度的
提升计算效率(fi(n)=fi(n-1)+fi(n-2),如果没有储存中间计算结果,
则fi(n-i)可能计算许多次)。
难点:
由分析可知该问题属于典型的动态规划问题。需要解决的难点在于选择最少数量的
平方数(当然这也是出题的目的)
解决方式:
本程序中解决上述难点的方式是遍历比较,以 n=12 为例
1、12=3+9,即 a=3,b=3
2、12=8+4,即 a=8,b=2
3、12=11+1,即 a=11,b=1
只需要比较 dp[3]、dp[8]、dp[11]的大小即可,如果是普通程序只需要重复迭代新n直至
分解参数都为平方数,不过这里已经将计算结果保留,所以可以直接获得结果
结论:
为了减少重复迭代计算的性能消耗,采取以空间换取时间的方式,保留计算结果值
*/
public static int get_len(int n) {
int q=(int)Math.sqrt(n);
if(q*q==n){//如果刚好可以开平方
return 1;
}
int[] arr=new int[n+1];
for(int i=1;i<=n;i++){//所有元素赋极大值
arr[i]=Integer.MAX_VALUE;
}
for(int i=1;i*i<=n;i++){//能开平凡的赋值1
arr[i*i]=1;
}
for(int b=1;b*b<=n;b++){
for(int a=0;a+b*b<=n;a++){
arr[a+b*b]=Math.min(arr[a+b*b],arr[a]+1);//赋值,也就是所谓的动态转移方程
}
}
return arr[n];
}
}