编程珠玑第二章习题

时间:2022-11-18 00:16:22

1、提供单词和词典,找到该单词的所有变位词,可以事先花时间和空间处理下该词典。

因为要查找变位词,事先遍历词典,得到每个单词签名,并建立签名列表,签名列表的每个项包含了该签名的所有单词。有了这个列表,直接得到提供单词的签名去遍历签名列表就能找到所有变位词。

2、给定一个4300000000个32位整数的顺序文件,请问你如何可以找到一个至少出现两次的整数

因为2^32>4300000000所以肯定存在至少出现两次的整数

而且是顺序文件,更提醒我们要用二分查找法

1、把所有整数(N个)看成二进制表示法,将第一位bit为1的数目和第一位bit为0的比较,必有一个数目大于等于另一个。

2、在数目大的那堆数字中继续比较第二bit位,按照1的方法比较,以此类推最后能得到重复出现的数字。

同样的道理,假设我们有N个顺序数字他们都在[1,2^n]范围,且N<2^n,如何找到至少一个没有出现的数字,思路和刚刚的一样

1、把所有整数(N个)看成二进制表示法,将第一位bit为1的数目和第一位bit为0的比较,必有一个数目小于等于另一个。

2、在数目小的那堆数字中继续比较第二bit位,按照1的方法比较,以此类推最后能得到没有出现的数字。


#include <stdio.h>
#include <stdlib.h>
int getRepeat(unsigned char *a, int length, int bitlen)
{
	unsigned char result = 0;
	int start = 0;
	for(int i = 1; i <= bitlen; i++)
	{
		int bit0 = 0;
		int bit1 = 0;
		int mod = 1 << (bitlen - i);
		int len = length/(1<<(i-1));
		for(int j = start; j < start + len; j++)
		{
			if((a[j] & mod) == 0)
			{
				bit0++;
			}
			else
			{
				bit1++;
			}
		}
		if(bit0 > bit1)
		{
			result |= 0<<(bitlen - i);
		}
		else
		{
			result |= 1<<(bitlen - i);
			start+=len/2;
		}
	}
	return result;
}
int getLost(unsigned char *a, int length, int bitlen)
{
	unsigned char result = 0;
	int start = 0;
	for(int i = 1; i <= bitlen; i++)
	{
		int bit0 = 0;
		int bit1 = 0;
		int mod = 1 << (bitlen - i);
		int len = length/(1<<(i-1));
		for(int j = start; j < start + len; j++)
		{
			if((a[j] & mod) == 0)
			{
				bit0++;
			}
			else
			{
				bit1++;
			}
		}
		if(bit0 < bit1)
		{
			result |= 0<<(bitlen - i);
		}
		else
		{
			result |= 1<<(bitlen - i);
			start+=len/2;
		}
	}
	return result;
}
int main()
{
	unsigned char a[] = {1,1,1,4,4,5,5,6,7,8,9,10,11,12,13,14,15};
	printf("%d\n",getRepeat(a,17,4));
	unsigned char b[] = {1,3,4,5,5,6,7,8,9,10,11,12,13,14,15};
	printf("%d\n",getLost(b,15,4));
	return 1;
}

3、两个向量的转置算法,i,n的最大公约数怎么出现在程序中。

i,n的最大公约数其实就是从头开始进行a[i]=a[2i] a[2i]=a[3i]的次数

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
void reverse1(char c[], int i, int n)
{
	int p = 0;
	int count = 0;
	if(i > 0 && n>=i)
	{
		while(count!=n)
		{
			int t = c[p];
			int q = p + i;
			while((q%n)!=p)
			{
				c[(q-i)%n] = c[q%n];
				count++;
				q+=i;
			}
			c[(q-i)%n] = t;
			count++;
			p++;
		}
		
	}
	//p为i,n最大公约数,即从第一个数开始进行c[i]=c[2i]  c[2i]=c[3i]循环的次数
	printf("%d",p);
}
int main()
{
	int i = 0;
	char c[] ={'a','b','c','d','e','f','g','h'};
	DWORD start, stop;
    start = GetTickCount();
	reverse1(c,3,8);
	stop = GetTickCount();
    printf("time: %lld ms\n", stop - start);
	for(i = 0; i < 8;i++)
	{
		printf("%c",c[i]);
	}
	printf("%c",'\n');
	return 1;
}


4、三个旋转向量算法

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>

void reverse1(char c[], int i, int n)
{
	int p = 0;
	int count = 0;
	if(i > 0 && n>=i)
	{
		while(count!=n)
		{
			int t = c[p];
			int q = p + i;
			while((q%n)!=p)
			{
				c[(q-i)%n] = c[q%n];
				count++;
				q+=i;
			}
			c[(q-i)%n] = t;
			count++;
			p++;
		}
		
	}
	//p为i,n最大公约数,即从第一个数开始进行c[i]=c[2i]  c[2i]=c[3i]循环的次数
	printf("%d",p);
}
void reverse2(char c[], int i, int j)
{
	char p;
	while(i<j)
	{
		p = c[i];
		c[i] = c[j];
		c[j] = p;
		i++;
		j--;
	}
}
int main()
{
	int i = 0;
	char c[] ={'a','b','c','d','e','f','g','h'};
	DWORD start, stop;
    start = GetTickCount();
	reverse1(c,3,8);
	stop = GetTickCount();
    printf("time: %lld ms\n", stop - start);
	for(i = 0; i < 8;i++)
	{
		printf("%c",c[i]);
	}
	printf("%c",'\n');
	char d[] ={'a','b','c','d','e','f','g','h'};
	start = GetTickCount();
	reverse2(d,0,2);
	reverse2(d,3,7);
	reverse2(d,0,7);
	stop = GetTickCount();
    printf("time: %lld ms\n", stop - start);
	for(i = 0; i < 8;i++)
	{
		printf("%c",d[i]);
	}
	printf("%c",'\n');
	return 1;
}

5、将向量abc转为cba

我想到的是在两向量转置的基础上abc->bac->cba

网上百度的方法是对abc取abr cr 再取(arbrcrr  r表示对向量的转置。

6、给定某一个名字的按钮编码,返回匹配集合

这个题目跟查找变位词差不多,给每个名字编码,将编码一样的放在一起,并顺序排列,查找的时候二分查找就可以了。

7、题意都理解得不是很来。。。。。。。

8、n个元素的实数集,确认是否有k个元素的子集可以让总和至多为t

将n个元素排序,计算头k个数和与尾k个数和,如果t介于二者之间就有。