算法题:最大获利,哲学家就餐,和为N整数,爬楼梯,大文件交集,分堆大量硬币

本部分包括几个算法题:(1)股价序列求最大获利 (2)哲学家就餐问题 (3)求和N的所有非重复整数的集合 (4)一步或两步走楼梯,到N共有多少走法 (5)求两个超大放置url的文件的交集 (6)如何分出两堆正面相等的硬币


(1) 股价序列求最大获利:

题目:一个股价序列Input,里面有N个数值,每个数值表示不同时间段的股价,问求什么时间买卖获利最大及获得的最大利润。时间复杂度O(N),空间复杂度O(1),可以破坏原股价序列。


思路:股票肯定是卖的股价要比买的股价高,才能获得最大的利润,所以本题的思路是首先截取上升的曲线,然后比较每条上升的曲线的gap值,比较gap值即可。本题可以继续优化,后续再优化吧。


public class Counter1 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
	    int[] input={10,9,2,3,4,3,2,4,5,6,5,4,2,1,8,9};
	    System.out.println("original length:"+input.length);
		int j=0;
		boolean begin=false;
		for(int i=1;i<input.length;i++){
			int temp=input[i]-input[i-1];
			if(temp>0){
				if(!begin){
					input[j]=input[i-1];
					input[j+1]=input[i];
					j++;
					begin=true;
					continue;
				}
				if(input[j]!=input[i-1]){
					input[j+1]=input[i-1];
					input[j+2]=input[i];
					j+=2;
				}else{
					input[j+1]=input[i];
					j+=1;
				}
			}
		}
		j++;
		input[j]=-1;
		for(int i=0;i<input.length;i++){
			if(input[i]!=-1){
				System.out.println("get upper line i="+i+",val="+input[i]);
			}else{
				break;
			}
		}
		System.out.println("extract the gap");
		j=0;
		begin=false;
		for(int i=1;i<input.length;i++){
			if(input[i]==-1){
				break;
			}else{
				if(input[i]>input[i-1]){
					if(!begin){
						input[j]=input[i-1];
						begin=true;
						j++;
					}
					input[j]=input[i];
				}else{
					j++;
					input[j]=input[i];
					j++;
				}
			}
		}
		j++;
		input[j]=-1;
		for(int i=0;i<input.length;i++){
			if(input[i]!=-1){
				System.out.println("extract the upper i="+i+",val="+input[i]);
			}else{
				break;
			}
		}
		System.out.println("compute the gap");
        int gap=0;
		for(int i=1;i<input.length;i+=2){
			if(input[i]==-1){
				break;
			}else{
				int tgap=input[i]-input[i-1];
				if(tgap>gap){
					gap=tgap;
				}
			}
		}
		System.out.println("the biggest gap="+gap);
	}

}

(2) 哲学家就餐问题, 五个哲学家围在圆桌边, : 每两人间有一只筷子, : 哲学家的行动顺序为 思考-》拿筷子-》吃饭 -》放筷子 -》思考(每一步时间随机),哲学家要吃饭,必须把左右两边的筷子都拿起, : 这样就有可能产生死锁,比如每个哲学家都可能拿起左边的筷子,等待右边的筷子。 设一个数组state[],保存每个哲学家的状态:thinking,hungry,eating


import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;


public class Philosopher extends Thread {

	public static final int THINKING = 0;
	public static final int START = 1;
	public static final int EATING = 2;
	public static final int FINISHED = 3;
	// 每步等待0 - 50毫秒
	public static final int MAX_WAIT = 50;
	public static Random ran = new Random();
	private Table table;
	private int chair;
	private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss:SS");

	public Philosopher(Table table, int chair) {
		this.table = table;
		this.chair = chair;
	}

	public void run() {
		try {
			do {
				switch (table.getStatus(this.chair)) {
				case Philosopher.THINKING: {
					Thread.sleep(ran.nextInt(Philosopher.MAX_WAIT));
					if (table.test(this.chair)) {
						table.setStatus(this.chair, Philosopher.START);
						log(chair + " 拿起了筷子");
					}
					break;
				}
				case Philosopher.START: {
					Thread.sleep(ran.nextInt(Philosopher.MAX_WAIT));
					table.setStatus(this.chair, Philosopher.EATING);
					log(chair + " 开始吃饭 ");
					break;
				}
				case Philosopher.EATING: {
					Thread.sleep(ran.nextInt(Philosopher.MAX_WAIT));
					table.setStatus(this.chair, Philosopher.FINISHED);
					log(chair + " 吃饭完毕, 放下了筷子");
					break;
				}
				case Philosopher.FINISHED: {
					Thread.sleep(ran.nextInt(Philosopher.MAX_WAIT));
					table.setStatus(this.chair, Philosopher.THINKING);
					log(chair + " 开始下一次思考");
					break;
				}
				}
			} while (table.isRunning());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void log(String msg) {
		System.out.println(sdf.format(new Date()) +",哲学家"+ msg);
	}

	public static void main(String[] args) {
		// 10个哲学家, 这下搞大了...
		Table table = new Table(2);
		table.start();
	}
}

class Table {
	private Philosopher[] all;
	private int[] status_list;
	private boolean running = true;
	private long start_time;
	// 3秒后结束运行
	public static final int TIME_OUT = 2000;

	public Table(int count) {
		all = new Philosopher[count];
		status_list = new int[count];
		for (int i = 0; i < count; i++) {
			all[i] = new Philosopher(this, i);
			status_list[i] = Philosopher.THINKING;
		}
	}

	public void start() {
		start_time = System.currentTimeMillis();
		for (int i = 0, len = all.length; i < len; i++) {
			new Thread(all[i]).start();
		}
	}

	public int getStatus(int chair) {
		return status_list[chair];
	}

	public void setStatus(int chair, int status) {
		status_list[chair] = status;
	}

	public boolean test(int chair) {
		synchronized (this) {
			if (System.currentTimeMillis() - start_time > Table.TIME_OUT) {
				this.running = false;
			}
			boolean result = false;
			int previous = this.getStatus((chair == 0) ? all.length - 1: chair - 1); 每个chair的左边筷子,注意第0个chair
			int next = this.getStatus((chair == all.length - 1) ? 0 : chair + 1); 每个chair的右边筷子,注意最后一个chair
			if ((previous == Philosopher.THINKING || previous == Philosopher.FINISHED)
					&& (next == Philosopher.THINKING || next == Philosopher.FINISHED)) {
				result = true;
			}
			return result;
		}
	}

	public boolean isRunning() {
		return this.running;
	}

}

还有使用图形化界面的方式更加形象的解决此问题

http://www.cnblogs.com/rollenholt/archive/2011/09/15/2178004.html


(3)求和为N的所有数的集合, 描述:对于和20进行分解,分解成一大一小,大的从19开始依次递减,对于小的进行再次分解,递归求解,如:可分解成13 7 其中7又可分解成 6 1,5  2,4 3 3 可分解成2 1,然后依次类推

public class AnySumN {
	 
    int n;
    AnySumN(int n){
        this.n = n;
    }
     
    public void f(String string,int num,int max){
        for(int i=max;i>0;i--){
            String tempString;
            if(num+i==n){
                tempString = string + " " + String.valueOf(i);
                System.out.println(tempString);
            }else if(num+i<n && i-1>0){
                tempString = string + " " + String.valueOf(i);
                f(tempString,num+i,i-1);
            }
        }
    }
     
    public void direct(){
        if(n-1>0)
            for(int i=n-1;i>0;i--)
                if(i-1>0)
                    f(String.valueOf(i),i,i-1);
    }
    public static void main(String[] args) {
    	AnySumN asn = new AnySumN(6);
        asn.direct();
    }
}
来源于: http://my.csdn.net/oceanethan/code/detail/40605


http://blog.csdn.net/ryj111/article/details/5192969

(3) 一个楼梯有20级,每次走1级或两级,请问从底走到顶一共有多少种走法?
 分析:假设从底走到第n级的走法有f(n)种,关键的关键:走到第n级有两个方法,一个是从(n-1)级走一步,另一个是从第(n-2)级走两步,前者有f(n-1)种方法,后者有f(n-2)种方法,所以有f(n)=f(n-1)+f(n-2),还有f(0)=1,f(1)=1.
递归编程实现

方法1

#include <stdio.h>
int f(int n)
{
 if(n==0 || n==1) return 1;
 else return f(n-1)+f(n-2);
}
int main()
{
    printf("%d/n",f(20));
    return 0;
}

现在来说说动态规划的基本思想

动态规划的关键是发现子问题和怎么记录子问题,以上面的例子说明
(1) 对子问题可递归的求解,当n>1时,f(n)=f(n-1)+f(n-2);否则,f(1)=f(0)=1;
(2) 这些子问题是有重叠的,即求解某个问题时,某些子问题可能需要求解多次。例如求解f(5)时,f(2)就被求解了3次。
在上面两个条件下,用动态规划的方式来求解会高效很多。就是把子问题记录下来,每个子问题只求解一次,从而提高了效率。
方法二

#include <stdio.h> 

int result[100]; 
int f(int n) {   
   int res;   
   if(result[n]>=0) 
      return result[n];   
   if(n==0 || n==1) res=1;   
   else res=f(n-1)+f(n-2);   
   result[n]=res;   
   return res; } 
int main() {   
   int i;   
   for(i=0;i<=20;i++)     
       result[i]=-1;   
   printf("%d/n",f(20));   
   return 0; 
}
方法三
#include <stdio.h>

int f[100];

int main(){  
   int i;  f[0]=1;  f[1]=1;  
   for(i=2;i<=20;i++)    
      f[i]=f[i-1]+f[i-2];  
   printf("%d",f[20]);  return 0;
}


方法三是否让你想起了那个兔子繁殖的问题呢?
小结一下
动态规划,采用分治的策略,把求最优解问题分解为求若干子问题的最优解,记录子问题的解,化繁为简,很实用,也很高效。

(5) 给你A,B两个文件,各存放50亿条URL,每条URL占用64字节,内存限制是4G,让你找出A,B文件共同的URL。如果是三个乃至n个文件呢?

有人使用Bloom Filter 来做处理,但是过于麻烦,比较好的方式还是进行(1)将A和B大文件的数据,通过Hash(url)/1000 hash到1000个小文件中,并给这些小文件取带有规则的名字,比如第几个,来源于哪个文件(2)因为Hash和取模后,相同的URL肯定位于顺序相同的文件名中如A-100.txt 和 B-100.txt (3)将相同顺序的小文件分别读到Set中,使用Collection的retainAll即可获取相同的URL

关于字符串的Hash值,可以参考 http://www.blogjava.net/jinfei0627/articles/219543.html
不过对于Java来说,应用的是
 /**
     * Returns a hash code for this string. The hash code for a
     * <code>String</code> object is computed as
     * <blockquote><pre>
     * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
     * </pre></blockquote>
     * using <code>int</code> arithmetic, where <code>s[i]</code> is the
     * <i>i</i>th character of the string, <code>n</code> is the length of
     * the string, and <code>^</code> indicates exponentiation.
     * (The hash value of the empty string is zero.)
     *
     * @return  a hash code value for this object.
     */
    public int hashCode() {
            int h = hash;
            if (h == 0) {
            int off = offset;
            char val[] = value;
            int len = count;
            for (int i = 0; i < len; i++) {
                h = 31*h + val[off++]; //31 上文构造方法参数mulBase
            }
            hash = h;
        }
        return h;
    }

使用正则表达式判断一个字符串是否为IP地址
import java.util.regex.Matcher; 
import java.util.regex.Pattern; 
public class TestRegex {  
      public static boolean isboolIP(String ipAddress){ 
          String one="(2[5][0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})";	    
	      String ip=one+"\\."+one+"\\."+one+"\\."+one;  
		  Pattern pattern = Pattern.compile(ip);  
		  Matcher matcher = pattern.matcher(ipAddress);   
		  return matcher.matches();   
		}
	}

(6)一大堆硬币,有正有反,现在蒙着眼睛如何让你分,并且硬币的正反面摸不出来,只可以进行翻转,如何分堆才能将分成的两堆硬币中正面个数相同。

答案在下面:
设有M个硬币,N个正面,从M中拿出N个来,如果剩下的一堆中个中有L个正面,那么N个中正面的个数为N-L,则再将这个N个全部翻转,则正面的个数为N-(N-L)) =L。
由此两堆中的正面个数必定相等。之前为啥没有想到这个呢?总是将难点定位在从M中拿出几个硬币来,又分不清正面和反面怎么办?何不继续深想一下,更通用的想一下呢?!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值