奇偶节点的排序和完全平方数问题

遇到涂鸦的两个题目,拿来写一下,有理解不对的地方,望大家不吝指正。大笑

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];
    }
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值