算法------编程珠玑(ProgrammingPeals)第一章习题(JAVA)

package code_01_chapter;

import java.util.Random;
import java.util.Scanner;

/*
 *Created by William on 2018/6/14 0014
 */
public class QuestionsInChapter1 {
    /**
     * Q1:
     * 如果不缺内存,如何使用一个具有库的语言来实现一种排序算法以表示和排序集合?
     * Array.sort();
     */

    /**
     * Q2:
     * 如何使用位逻辑(例如与、或、移位)来实现位向量?
     * 给定一个整型(32位)数组,我们输入一个参数i,然后设置数组的i位是1,或是对第i位清零,或是探测第i位的值。
     */
    static class Q2 {
        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后的余数

        /**
         * 功能:把第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 find(int i) {
            return arr[i >> SHIFT] & (1 << (i & MARK));
        }
//        public static void main(String[] args){
//            int i = 10;
//            set(i);
//            System.out.println(find(10));
//        }
    }

    /**
     * Q3:
     * 比较位图排序与系统排序
     * 解答: 位图排序是最快的,针对这个问题而言,qsort比stl sort速度快。
     */

    /**
     * Q4:
     * 如何生成小于n且没有重复的k个整数的问题。
     * 解答:先生成n为顺序数组,然后随机交换某两位
     */
    public static class Q4 {
        public static void numberGenerator(int number, int beginNumber) {
            int end = beginNumber + number;
            int[] arr = new int[number];
            int j = 0;
            for (int i = beginNumber; i < end; i++) {
                arr[j] = i;
                j++;
            }
            Random random = new Random();
            for (int i = 0; i < number; i++) {
                swap(arr, i, random.nextInt(number));
            }
            printArray(arr);
        }

        public static void numberGenerator(int number) {
            numberGenerator(number, 0);
        }

        public static void swap(int[] arr, int i, int j) {
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
        public static void printArray(int[] arr) {
            for (int number : arr){
                System.out.print(number+" , ");
            }
        }
//        public static void main(String[] args) {
//            numberGenerator(100,1000);
//        }
    }

    /**
     * Q5:
     * 那个程序员说他又1MB的可用存储空间,但是我们概要描述的代码需要1.25MB的空间。
     * 他可用不费力气的索取到额外的空间。如果1MB空间是严格的边界,你会推荐如何处理呢?
     * 解答:可用多路归并排序进行解决。
     * 例如:我们可以将输入文件分成两个部分,第一部分保存[1,5000000]之间的数,
     * 第二个文件保存[5000001,10000000]的数字,然后分别进行排序,所用的内存就可以降到1MB以内。
     * 如果把文件分为k份(每份都保存一定区间的数),那么就可以再O(n)的时间内,n/k的空间内完成排序。
     */

    /**
     * Q6:
     * 如果那个程序员说的不是每个整数最多出现一次,而是每个整数最多出现10次,你又如何建议他呢?
     * 你的解决方案如何随着可用存储空间总量的变化而变化呢?
     * 解决:
     * 若每个整数最多重复出现10次,采用统计重复次数的方案,我们需要至少4位二进制(即半个byte)来记录每个输入整数的重复次数,
     * 因为4位二进制能记录0~15个数值而3个二进制位只能记录0~7数值。
     * 这意味着每个数字在内存中不再是仅以1位来表示,而是需要4位,这需要腾出更多的内存用来存储重复的次数。
     * 设共有1000万个数字,则内存总空间需要4*1000万bit大约等于5MB内存。设内存仍被限制在1Mb,这样需要划分为5趟来完成。
     */

    /**
     * Q7:
     * 问题:[R. Weil]本书1.4 节中描述的程序存在一些缺陷。首先是假定在输入中没有出现两次的整数。
     * 如果某个数出现超过一次的话,会发生什么?在这种情况下,如何修改程序来调用错误处理函数?
     * 当输入整数小于零或大于等于n时,又会发生什么?如果某个输入不是数值又如何?
     * 在这些情况下,程序该如何处理?程序还应该包含 哪些明智的检查?
     * 描述一些用以测试程序的小型数据集合,并说明如何正确处理上述以及其他的不良情况。
     * 解答:代码如下:
     * 如果某个输入不是数值,那么可能是字符、浮点数、字符串。如果是字符串,可以通过先读入字符串再通过atoi转换为整数,
     * 若失败则报告出错。当然,对于不合理的输入,我们可以直接报告出错,或者进行错误输入的忽略而读取下一个合法的输入整数。
     */
    public static class Q7{
        public static void numberCheck(int i, int n) throws Exception {
            if(i < 0 || i >= n){
                throw new Exception("input number is out of bound");
            }
            if(Q2.find(i) > 0){
                throw new Exception("input number is repeated");
            }
        }
    }

    /**
     * Q8:
     * 当那个程序员解决该问题的时候,美国所有免费电话的区号都是800。现在免费电话的区号包括800、877和888,而且还在增多。
     * 如何在1MB空间内完成对所有这些免费号码的排序?如何将免费电话号码存储在一个集合中,
     * 要求可以实现非常快速的查找以判定一个给定的免费电话号码是否可用或者己经存在?
     *
     *  这个问题没有给出答案,网上有的答案是这样的:每个区号建立一个set,查找的时候效率是O(logn)。
     *  我想的是进行排序的时候,可以把800、877和899的区号的号码分别提取出来,然后分别进行排序,最后在归并到一个结果文件中。
     *  不知道有没有更好的办法。
     */

    /**
     * Q9:
     * 使用更多的时间来换取更少的运行时间存在一个问题:初试化空间本身需要消耗大量的时间。
     * 说明如何设计一种技术,在第一次访问向量的项时将其初始化为0,。
     * 你的方案应该尽可能的使用常量的时间进行初始化和向量访问,使用的额外空间应正比于向量的大小。
     * 因为该方法通过进一步增加空间来减少初始化时间,所以仅在空间很廉价、时间很宝贵且向量很稀疏的情况下才考虑使用。
     *
     * 个人理解是动态扩容的问题,但看不懂参考答案,不做深究
     */
}

猜你喜欢

转载自blog.csdn.net/weianluo/article/details/80719651