编程珠玑(开篇)--第一章习题

第一题  

    如果不缺内存,如何使用一个具有库的语言来实现一种排序算法以表示和排序集合?

解答:使用库函数sort,或者使用TreeSet,TreeMap


第二题
    如何使用位逻辑(例如与、或、移位)来实现位向量?

解答:

package test;

import java.util.Scanner;


public class Main {	
	final static int N = 10000000;//最多一千万个不重复数据
	final static int BITSTEPWORD = 32;//int类型32位
	static int[] arr = new int[1+N/BITSTEPWORD];//使用int[]数组来表示1千万位
	final static int SHIFT = 5;//右移5位
	final static int MARK = 0x1F;//掩码11111,用于保留某数除以32后的余数
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int i = 0;
		for(i = 0; i < N; i++){  
            clr(0);
		}      
		
		while ((i = sc.nextInt())>-1){			
			set(i);  
		}
           
		for(i = 0; i < N; i++){
            if (test(i)==1){            	
            	System.out.println(i);
            }
		}      
		
	}	

	/**
	 * 功能:把第i位置1
	 * i>>SHIFT 等于i整除32,取整数部分。表示i位在数组第几个元素里面
	 * i&MARK 取i的后五位,等于i%32的余数
	 * 1<<(i&MARK) 1右移(i&MARK),找到对应要置为1的那位
	 * arr[(i>>SHIFT)] |= 1<<(i&MARK) 或运算,把对应为置1
	 * @param i
	 */
	public static void set(int i){
		arr[(i>>SHIFT)] |= (1<<(i&MARK));
		System.out.println(arr[(i>>SHIFT)]);
	}
	
	/**
	 * 功能:把第i位置0
	 * i>>SHIFT 等于i整除32,取整数部分。表示i位在数组第几个元素里面
	 * i&MARK 取i的后五位,等于i%32的余数
	 * 1<<(i&MARK) 1右移(i&MARK),找到对应要置为0的那位
	 * ~(1<<(i&MARK)) 把找到的位,取反,使对应为0其余为1,类似0111111
	 * arr[(i>>SHIFT)] &= 1<<(i&MARK) 与运算,把对应为置0,其他为保留原值
	 * @param i
	 */
	public static void clr(int i){
		arr[(i>>SHIFT)] &= ~(1<<(i&MARK)); 
	}
	
	/**
	 * 功能:查找i位的值
	 * i>>SHIFT 等于i整除32,取整数部分。表示i位在数组第几个元素里面
	 * i&MARK 取i的后五位,等于i%32的余数
	 * 1<<(i&MARK) 1右移(i&MARK),找到对应要获取的那位	
	 * arr[(i>>SHIFT)] & 1<<(i&MARK) 获取对应位
	 * @param i
	 */
	public static int test(int i){
		return arr[i>>SHIFT] & (1<<(1&MARK));
	}
}

第三题、比较位图排序与系统排序

解答: 位图排序是最快的,针对这个问题而言,qsort比stl sort速度快。


第四题
    如何生成小于n且没有重复的k个整数的问题。

解答:先生成n为顺序数组,然后随机交换某两位

package test;

import java.util.Arrays;
import java.util.Random;

public class Main {		
	public static void main(String[] args) {
		int n = 100;//随机整数数量
		int arr[] = new int[n];
		//先生成顺序
		for(int i=0;i<n;i++){
			arr[i] =i+1;
		}
		int t = 0;
		int j = 0;
		Random r = new Random();
		//随机交换某两位
		for(int i=0;i<n;i++){
			j = r.nextInt(n);
			t = arr[i];
			arr[i] = arr[j];
			arr[j] = t;
		}
		//输出
		System.out.println(Arrays.toString(arr));
	}		
}

第五题
    那个程序员说他又1MB的可用存储空间,但是我们概要描述的代码需要1.25MB的空间。他可用不费力气的索取到额外的空间。如果1MB空间是严格的边界,你会推荐如何处理呢?

解答:可用多路归并排序进行解决。
    例如:我们可以将输入文件分成两个部分,第一部分保存[1,5000000]之间的数,第二个文件保存[5000001,10000000]的数字,然后分别进行排序,所用的内存就可以降到1MB以内。如果把文件分为k份(每份都保存一定区间的数),那么就可以再O(n)的时间内,n/k的空间内完成排序。


第六题
    如果那个程序员说的不是每个整数最多出现一次,而是每个整数最多出现10次,你又如何建议他呢?你的解决方案如何随着可用存储空间总量的变化而变化呢? 

解答:10<12,也就是说,对于每个数,我们还要用4位来存储它的数目。这时存储空间变成原来的4倍。

同时我们还可以利用第五题的结论,使用多路并归排序解决空间限制的问题


第七题、问题:[R. Weil]本书1.4 节中描述的程序存在一些缺陷。首先是假定在输入中没有出现两次的整数。如果某个数出现超过一次的话,会发生什么?在这种情况下,如何         修改程序来调用错误处理函数?当输入整数小于零或大于等于n时,又会发生什么?如果某个输入不是数值又如何?在这些情况下,程序该如何处理?程序还应该包含 哪些明智的检查?描述一些用以测试程序的小型数据集合,并说明如何正确处理上述以及其他的不良情况。

解答:如果某个数出现超过一次的话,会发生什么?

      会被忽略掉, 因为原来的程序本身就是用来处理只出现一次的情况的。

        在这种情况下,如何修改程序来调用错误处理函数?

while (scanf("%d", &i) != EOF)    
    if(test(i)) call_error_fun();    
    else set(i);  

当输入整数小于零或大于等于n时,又会发生什么?

      会出现访问越界的情况。-1访问时,会访问a[-1]的31个bit位。

如果某个输入不是数值又如何?在这些情况下,程序该如何处理?

    输入可能是浮点数,或是字符什么的~~

   可以先读入字符串,再用atoi转换成为整形数,如果失败,则进行出错处理。


第八题、当那个程序员解决该问题的时候,美国所有的免费电话的区号是800。现在免费电话的区号包括800、877和888,而且还在增多。如何在1MB空间内完成对所有这些免费电话的号码的排序?如何将免费电话号码存储在一个集合中,要求可以实现非常快速的查找以判定一个给定的免费电话号码是否可用或者已经存在?

解答:第一种方案:如果是一千万个电话号码都有可能成为免费号码,那么至需要1.25MB * (免费号码前缀个数)。

第二种方案:省空间,多次扫描文件:

                  1、首先扫描整个文件,看有哪个免费号码前缀。以及每个免费号码前缀下的号码个数。

                  2、设置区间映射表:比如800前缀有125个免费号码,找到最大的数,与最小的数,差值做为bit长度。

第三种方案:建立索引的方式来进行处理。以最后7位为索引,后面800,878什么的,为值。如果不是免费号码,应该是不用加入到这个hash表中。


第九题
    使用更多的空间来换取更少的运行时间存在一个问题:初始化空间本身需要消耗大量的时间。说明如何设计一种技术,在第一次访问向量的项时将其初始化为0。你的方案应该使用常量时间进行初始化和向量访问,使用的额外空间应正比于向量的大小。因为该方法通过进一步增加空间减少初始化的时间,所以仅在空间很廉价、时间很宝贵且向量很稀疏的情况下才考虑。

解答:使用两数组to,form,还有一个top来标记

使用下面代码来访问数组

from[i] = top;
to[top] = i;
data[i] = 0;
top++;

如果已经初始化了数组,那么

from[i]<top&to[from[i]]=i

top就是指当前输入的是第几个数,也就是top是循环的标记

from[i]=top,记录了i是第几个被记录的元素

由上面的说明知道,如果from[i]<top,说明当前被记录的元素在之前已经记录过了

to[top]=i,这个数组标记了第top个元素是i

那么,我们首先,利用from[i]找到i是第几个被记录的元素,然后利用to[from[i]]就可以知道第几个被记录的元素是多少(如果已经被初始化了,这个元素当然是i,否则是一个随机数)


第十题
    在成本低廉的隔日送达时代之前,商店允许顾客通过电话订购商品,并在几天后上门自取。商店数据库使用客户的电话号码作为其检索的主关键字(客户知道他们的电话号码,并且这些关键字几乎都是唯一的)。你如何组织商店的数据库,以允许高效的插入和检索操作?

解答:根据电话号码的最后两位作为客户的哈希索引,进行分类,当顾客打电话下订单的时候,它被放置在一个合适的位置。然后当顾客抵达进行检索商品时,营业员按顺序检索订单,这是经典的解决哈希冲突的解决方法:通过顺序检索。电话号码的最后两位数字是相当随机的,而电话号码的前两位作为索引行不通,因为很多电话号码的前两位是相同的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值