CC150小结概念与算法

几个常用算法需要注意的点:
二分:while终止条件,mid是否取两个,mid偏左还是偏右,left和right的变更条件,跳出后的验证
递归(dfs):dfs终止条件,结果存储,下一次dfs的位置以及条件限定(重要)

位操作

常用操作 与& 、或|、非~、异或^, 左移,右移。
获取,得到第i位的知否为1。置位,将第i位设置为1.清零,00010000 取反得到掩码 11101111,然后利用与操作。将最高位到i(含)清零,(1<< i) - 1,进行与操作。将i位至0位(含)清零~((1 << (i + 1)) - 1)再与
~0 表示1串1。
取出一个数的最低为1的数。 x & ~(x - 1);

5.2给定一个介于0和1之间的实数(如0.72),类型为double,打印它的二进制表示。如果无法精确地用32位以内的二进制表示,则打印“ERROR”。

    public String numTobin(double num){
        if (num >= 1 || num <= 0){
            return "ERROR";
        }
        StringBuffer sb = new StringBuffer();
        sb.append("0.");
        while(num > 0){
            num *= 2;
            if (num >= 1){
                num -= 1;
                sb.append(1);
            }else {
                sb.append(0);
            }
            if (sb.length() >= 32){
                return "ERROR";
            }
        }
        return sb.toString();
    }

5.3给定一个正整数,找出其二进制表示中1的个数相同、且大小最相近的那两个数,一个略大,一个略小。

思路:1.位操作,较大的数:注意要判断非拖尾0的存在,要将右边有1的第一个0(位置为p = C0 (拖尾0的个数)+ C1(右边1的个数))变为1,将p后面的数据全部清零,然后补上C1个1,要判断更大值是否存在。
较小的数 : 注意C1是拖尾1的个数,C0是紧邻拖尾1的左方一连串的个数,要从1变为0 的数右侧必须包含0.否则数会大。
根据位操作的简化,可以得到out of box的算法,依据之前的数。注意拖尾与非拖尾。

5.6交换某个整数的二级制表示下的奇数位和偶数位,使用指令越少越好。

思路:利用掩码提取奇数位,右移。利用掩码提取偶数位,左移。然后相或得到。
掩码的得到直接就是0xaa以及0x55

智力题

6.5有栋建筑高100层,若从N层或者更高层扔鸡蛋,鸡蛋破,若从第N层以下的楼层扔下来不会破。给两个鸡蛋,最差的情况下,扔鸡蛋的次数最小的情况下得到N

思路: 负载均衡下,要求x1 + x2 的值保持稳定,则假设从 a扔,若有一种算法保证x1多扔一次,x2就少扔一次。a + (a - 1) + (a - 2) + … + 1 = 100, a = 14.

数学与概率

生成素数的数列:埃拉托斯尼筛法,设计出一个boolean数组,从小到大划去因子出现数组中的数。
两个子函数 void cossOff(boolean[] flags, int prime),从prime * prime 开始
int getNextPrime(boolean[] flags, int prime) 找到数组长度内,大于prime的下一个值

       for(int i = 2;i < n+1;i++){    
            result1[i-1] = i;
        }
        for(int i = 2;i < n;i++){
            for(int j = i+1;j < n+1;j++){
                if(j % i == 0)          //如果j能够整除i,使result[j-1]等于0
                    result1[j-1] = 0;
            }
        }

java类型的范围

在JAVA中一共有八种基本数据类型,他们分别是

byte、short、int、long、float、double、char、boolean
其中byte、short、int、long都是表示整数的,只不过他们的取值范围不一样
byte的取值范围为-128~127,占用1个字节(-2的7次方到2的7次方-1)
short的取值范围为-32768~32767,占用2个字节(-2的15次方到2的15次方-1)
int的取值范围为(-2147483648~2147483647),占用4个字节(-2的31次方到2的31次方-1) +-21亿的数据量,一般可以满足需求
long的取值范围为(-9223372036854774808~9223372036854774807),占用8个字节(-2的63次方到2的63次方-1)
在通常情况下,如果JAVA中出现了一个整数数字比如35,那么这个数字就是int型的,如果我们希望它是byte型的,可以在数据后加上大写的 B:35B,表示它是byte型的,同样的35S表示short型,35L表示long型的,表示int我们可以什么都不用加,但是如果要表示long型的,就一定要在数据后面加“L”。
float和double是表示浮点型的数据类型,他们之间的区别在于他们的精确度不同
float 3.402823e+38 ~ 1.401298e-45(e+38表示是乘以10的38次方,同样,e-45表示乘以10的负45次方)占用4个字节
double 1.797693e+308~ 4.9000000e-324 占用8个字节
double型比float型存储范围更大,精度更高,所以通常的浮点型的数据在不声明的情况下都是double型的,如果要表示一个数据是float型的,可以在数据后面加上“F”。

7.4实现整数的乘法、减法和除法操作

面试官想要找的就是这种能以逻辑思考逐步解决问题的人
显示代码的复用能力
思路:自己实现正号变负号,负号变正号的操作,加与乘法都可以处理,

public int negate(int a){
    int neg = 0;
    int d = a < 0 ? 1 : -1;
    while(a != 0){
        neg += d;
        a += d;
    }
    return neg;
}
乘法是注意判断,让小的数a迭代

7.6在二维平面上,有一些点,找出经过点数最多的那条线。

思路:两点之间确定直线,时间复杂度O(n*n),利用HashMap进行次数的存储。

7.7有些数的因子只有3、5、7,找出其中的第k个数。

思路:这个方法与素数筛选法类似,关键是要想得到,首先插入数据1,然后将3*1输入到队列3Q,5*1输入到5Q,然后将7*1输入到7Q,下一个数从这三个队列中给出来。
没有最优解原因,算法不是最简算法,部分拼写错误

也可以使用3个index的方法
public int getKNum(int k){
        if (k < 0){
            return 0;
        }
        Queue quene3 = new LinkedList();
        Queue quene5 = new LinkedList();
        Queue quene7 = new LinkedList();
        quene3.add(1);
        int val = 1;
        for (int i = 0; i < k; i++){
            int v3 = quene3.size() > 0 ? quene3.peek() : Integer.MAX_VALUE;
            int v5 = quene5.size() > 0 ? quene5.peek() : Integer.MAX_VALUE;
            int v7 = quene7.size() > 0 ? quene7.peek() : Integer.MAX_VALUE;
            val = (int)Math.min(v3, Math.min(v5, v7));
            if (val == v3){
                quene3.remove();
                quene3.add(3 * val);
                quene5.add(5 * val);    
            }  else if (val == v5){
                quene5.remove();
                quene5.add(5 * val);
            } else {
                quene7.remove();
            }
            quene7.add(7 * val);
        }   
        return val;
    }

面向对象设计

打造优雅,易于维护的面向对象代码。
单例模式,只构造这一个对象,访问与修改都是通过内部方法统一进行。

8.3运用面向对象原则,设计一款音乐点唱机。

处理本类问题思路: 处理不明确的地方,定义核心对象,分析对象关系,设计对象动作
系统组件:点唱机(Jukebox),CD,歌曲(Song),艺术家(Artist),播放列表(PlayList),显示屏(Display,在屏幕上显示详细信息)
动作:新建播放列表(新增、删除、随机播放)、CD选择器、歌曲选择器、将歌曲放进到播放队列、获取播放列表中的下一首;
用户:添加,删除

public class Jukebox(){
    private CDPlayer cdPlayer;
    private User user;
    private Set cdCollection;
    private SongSelector ts;
    public Jukebox(CDPlayer cdPlayer, User user, Set cdCollection, SongSelector ts){
    }
    public Song getCurrentSong(){
        return ts.getCurrentSong();
    }
    public void setUser(User u){
        this.user = u;
    }
}
public class CDPlayer{
    private Playlist p;
    private CD c;
    /*构造函数*/

    /*播放歌曲*/

    /*getter and setter */

}
Playlist类管理当前播放的歌曲和待播放的下一首歌曲。本质上是播放队列的包裹类,还提供了一些操作上更方便的方法。
public class PlayList {
    private Song song;
    private Quene quene;
    public Playlist(Song song, Quene quene){
        ````
    }
    public Song getNextSToPlay(){
        return quene.peek();
    }
    public void queneUpSong(Song s){
        quene.add(s);
    }
}
public class CD {
    /* 识别码、艺术家、歌曲等 */
}
public class Song{
    /* 识别码、CD(可能为空)
}
public class User {
    private String name;
    private long ID;
    public User getUser(){
        return this;
    }
}

8.7设计一个聊天服务器系统

系统的难点:如何确定某人在线,如何处理冲突信息,如何让服务器在任何负载下,都能应付自如,如何预防拒绝服务攻击。

8.10设计并实现一个散列表,使用链接(即链表)处理碰撞冲突。

思路:存储一个由链表组成的数组,链表元素为包含key 以及 value 的对象,防止冲突。

final 它可以修饰非抽象类、非抽象类成员方法和变量。你可能出于两种理解而需要阻止改变:设计或效率。static变量前可以有private修饰,表示这个变量可以在类的静态代码块中,或者类的其他静态成员方法中使用(当然也可以在非静态成员方法中使用–废话),但是不能在其他类中通过类名来直接引用,这一点很重要。实际上你需要搞明白,private是访问权限限定,static表示不要实例化就可以使用,这样就容易理解多了。static前面加上其它访问权限关键字的效果也以此类推。

构造函数前面不要加void
没有ac的原因 模板类没有处理好,未考虑到items[index]为null的情况

public class MyHashMap {
    private final int MAX_SIZE = 100;
    private LinkedList>[] items;

    public MyHashMap(){
        items = (LinkedList>[])new LinkedList[MAX_SIZE];
    }
    public int getIndex(K k){
        return k.toString().length() % items.length;    
    }
    public void put(K k, V v){
        int index = getIndex(k);
        if (items[index] == null){
            items[index] = new LinkedList>();
        }
        LinkedList> temp = items[index];
        Cell data = new Cell(k, v);
        for (Cell c : temp){
            if (c.isEqual(data)){
                temp.remove(c);
            }
        }
        temp.add(data);
    }
    public V get(K key){
        int index = getIndex(key);
        if (null == items[index]){
            return null;
        }
        LinkedList> temp = items[index];
        for (Cell c : temp){
            if (c.getKey().equals(key)){
                return (V) c.getValue();
            }
        }
        return null;
    }
}
public class Cell {
    private K key;
    private V value;

    public  Cell(K key, V value){
        this.key = key;
        this.value = value;
    }
    public boolean isEqual(K k){
        return key.equals(k);
    }
    public boolean isEqual(Cell c){
        return isEqual(c.getKey());
    }
    public K getKey(){
        return this.key;
    }
    public V getValue(){
        return this.value;
    }
}

递归与动态规划

递归结束条件,得到递归返回值,计算本身返回值,
动态规划有时是缓存了递归的结果的递归优化版。
能用迭代尽量不适用递归。

9.1爬台阶问题,n阶台阶,小孩一次可以上1,2,3阶,实现算法计算多少种爬法。

思路:斐波那契数列改进版,

9.2设想有个机器人在坐在X*Y网格的左上角,只能向下、向右移动,机器人从(0,0)到(X,Y)有多少种移动方法?假如有些点不能访问,找出一条合理路径。

最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap 就必须为之提供外同步(Collections.synchronizedMap)。 Hashtable和HashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。

//如果需要,可以设计出一个对象,包含boolean 和 LinkedlList
    public boolean getPath(int x, int y, LinkedList path, Map cache){
        Point temp = new Point(x, y);
        if (cache.containsKey(temp)){
            return cache.get(temp);
        }
        if (x == 0 && y == 0){
            for (Point p : path ){
                System.out.print(" (" + p.x +"," + p.y + ")");
            }
            return true;
        }
        path.add(temp);
        System.out.print(" 访问节点 (" + temp.x +"," + temp.y + ")");
        boolean success = false;
        if (!success && isFree(x, y - 1)){
            success = getPath(x, y-1, path, cache);
        }
        if (x >=1 && isFree(x - 1, y) ){
            success = getPath(x - 1, y, path, cache);
        }
        if (!success){
            path.remove(temp);
            //此处进行了优化,原来的设置是path.add.书中意思应该是看一下都访问了哪些结点。
            //递归与动态规划的结合
        }
        cache.put(p, success);
        return success;
    }

9.3魔术索引,改造二分法

9.4返回某集合所有子集

思路:1.递归进化迭代,第n个子集是第n - 1个子集再加上 第 n - 1个子集与第n个数的和
2.数学组合方法,对每一个元素的存在与否进行1,0表示,1表示选择,0表示不选择
注意操作链表的时候,需要新建并且加入,不可以直接赋值,否则无法完成操作

迭代的方法
public ArrayList<ArrayList<Integer>> getAll(ArrayList<Integer> list){
        if (null == list){ return null; }
        int len = list.size();
        ArrayList<ArrayList<Integer>> resu = new ArrayList<ArrayList<Integer>>();
        ArrayList<Integer> temp = new ArrayList<Integer>();
        resu.add(temp);
        for (int i = 0; i < len; i++){
            ArrayList<ArrayList<Integer>> newresu = new ArrayList<ArrayList<Integer>>();
            newresu.addAll(resu);
            for (ArrayList<Integer> single : resu){
                ArrayList<Integer> singletemp = new ArrayList<Integer>();
                singletemp.addAll(single);
                singletemp.add(list.get(i));
                newresu.add(singletemp);
            }
            resu = newresu;
        }
        return resu;
    }
    //数学组合方法
    public ArrayList<ArrayList<Integer>> getSubsets2(ArrayList<Integer> list){
        ArrayList<ArrayList<Integer>> newresu = new ArrayList<ArrayList<Integer>>();
        int max = 1 << list.size();
        for (int k = 0; k < max; k++){
            newresu.add(coverIntToList(k, list));
        }
        return newresu;
    }
    public ArrayList<Integer> coverIntToList(int num, ArrayList<Integer> list){
        ArrayList<Integer> temp = new ArrayList<Integer>();
        int index = 0;
        for (int k = num; k > 0; k >>= 1){
            if ((k & 1) != 0){
                temp.add(list.get(index));
            }
            index++;
        }
        return temp;
    }

9.5编写一个方法确定字符串的所有排列组合

思路:从字符串的单个字符串出发,每次都进行递归添加。
没有bugfree的原因为: Set的底层实现应该是 HashSet

    public Set  getSubStrings(String str){
        return getSubHelp(str, str.length() - 1); 
    }
    public Set getSubHelp(String str, int index){
        if (null == str){
            return null;
        }
        if (index == 0){
            Set set = new HashSet();
            set.add(str.substring(0, 1));
            return set;
        }
        Set set = getSubHelp(str, index - 1);
        Set resu = new HashSet();
        for (String single : set){
            for (int i = 0; i <= single.length(); i++){
                resu.add(addChar(single, str.charAt(index), i));
            }
        }
        return resu;
    } 
    public String addChar(String str, char c, int pox){
        return str.substring(0, pox) + c + str.substring(pox, str.length());
    }

9.6实现一个算法,打印n对括号的全部有效组合

思路:采用递归的方式,是由前一个进行增加处理,插入括号的位置依据左括号的位置进行处理,注意重复问题,用set。
还有一种记录左右括号的数目,如果还有左括号可以使用,就插入左括号,然后递归,如果左括号用的多,就插入右括号。

enum表示枚举 例如 public enum Color {Black, White, Red, Yellow, Green}

9.8给定数量不限的25,10,5,1类型硬币,计算有几种表示法

当涉及到路径问题时,递归的参数要有single路径

public void makeChange(ArrayList<ArrayList<Integer>> list, int count, ArrayList<Integer> single){
        if (count < 0){
            return;
        }
        if (count == 0){
            list.add(single);
        }
        if (count >= 25){
            ArrayList<Integer> temp = new ArrayList<Integer>();
            temp.addAll(single);
            temp.add(25);
            makeChange(list, count - 25, temp);
        }
        if (count >= 10){
            ArrayList<Integer> temp = new ArrayList<Integer>();
            temp.addAll(single);
            temp.add(10);
            makeChange(list, count - 10, temp);
        }
        if (count >= 5){
            ArrayList<Integer> temp = new ArrayList<Integer>();
            temp.addAll(single);
            temp.add(5);
            makeChange(list, count - 5, temp);
        }
        if (count >= 1){
            ArrayList<Integer> temp = new ArrayList<Integer>();
            temp.addAll(single);
            temp.add(1);
            makeChange(list, count - 1, temp);
        }
    }
    public ArrayList<ArrayList<Integer>> getChange(int count){
        ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
        ArrayList<Integer> single = new ArrayList<Integer>();
        makeChange(result, count, single);
        return result;
    }

插补一下动态规划中的最长子序列问题,用一个数组进行缓存,index + 1位置上放的是处于当前位置上最小的值

9.10一堆箱子,宽w,高h,深d,将箱子堆起来时,下面箱子的宽,高,深必须大于上面的箱子。实现一个方法,打出最高的一堆箱子。

思路:典型的解决思路是利用递归得到每一个箱子作为底的高度,然后取出最大值,加上动态规划的缓存,不存在顺序问题。

扩展性与存储限制

评估分解棘手问题的能力,以及运用所学知识解决问题的能力。
步骤 大胆假设,切合实际,解决问题

10.2设计超大型社交网站,设计算法实现两个人之间的“连接关系”或“社交途径”

思路:查找表,分批进行存储,在两个端点进行广度优先查找

10.4给定一个数组,包含1到N的整数,N最大为32000,N取值不定,输出数组中重复元素,4KB内存,也就是空间复杂度。

没有bugfree的原因,忽略了数据是从1开始,而bitmap却是从0开始操作的。

    public void prinDup(int [] nums){
        MyBitSet bit = new MyBitSet(32000);
        for (int num : nums){
            if (bit.get(num - 1)){
                System.out.print(" " + num);
            } else {
                bit.set(num - 1);
            }
        }

    }
    public class MyBitSet{
        private int[] bitset;
        public MyBitSet(int size){
            //int 类型数据包含4个字节变量
            bitset = new int[size >> 5];//32
        }
        public boolean get(int index){
            int wordNum = index >> 5;
            int bitNum = index & 0x1f;//对32 取余数
            return ((bitset[wordNum] >> bitNum) & 0x01) == 1;
        }
        public void set(int index){
            int wordNum = index >> 5;
            int bitNum = index & 0x1f;//对32 取余数
            bitset[wordNum] |= 0x01 << bitNum;
        }
    }

排序与查找

学习掌握常见的排序和查找算法,回报巨大
归并排序(Merge Sort),时间复杂度 O(n*log(n)),空间复杂度看情况,
快速排序(Quick Sort),平均时间O(n*log(n)),最差O(n平方),空间O(n*log(n))
基数排序(Radix Sort)(桶排序)

归并
    public void mergeSort(int[] nums, int left, int right){
        if (left < right){
            int mid = left + ((right - left) >> 1);
            mergeSort(nums, left, mid);
            mergeSort(nums, mid + 1, right);
            merge(nums, left, mid, right);
        }
    }
    public void merge(int[] nums,int left,int mid,int right){
        int[] temp = new int[nums.length];
        for (int i = left; i <= right; i++){
            temp[i] = nums[i];
        }
        int c = left;
        int num = mid + 1;
        while(c <= right){
            if ((left <= mid) && ((temp[left] < temp[num]) || (num > right))){
                nums[c] = temp[left++];
            } else {
                nums[c] = temp[num++];
            }
            c++;
        }
    }
快排注意的问题,函数调用,符号注意,敲黑板记下来!!!
    public void quickSort(int nums[], int left, int right){
        int index = position(nums, left, right);
        if (left < index - 1){
            quickSort(nums, left, index - 1);
        }
        if (index < right){
            quickSort(nums, index, right);
        }
    }
    public int position(int nums[] , int left, int right){
        int mid = nums[left + ((right - left) >> 1)];
        while (left <= right){
            while (nums[left] < mid){
                left++;
            }
            while (nums[right] > mid){
                right--;
            }
            if (left <= right){
                int temp = nums[left];
                nums[left] = nums[right];
                nums[right] = temp;
                left ++;//找到并交换后,要进行位置处理操作
                right--;
            }
        }
        return left;
    }
        public static String[] getSplit(String str, String[] dict) {
        List list = new ArrayList();
        int left = 0, minlen = Integer.MAX_VALUE;
        // O(2n)
        StringBuffer sb = new StringBuffer();
        while (left < str.length()) {
            String temp = str.substring(left);
            boolean flag = false;
            for (String s : dict) {
                if (temp.startsWith(s)) {
                    left += s.length(); 
                    flag = true;// 包含
                    break;
                }
            }
            if (flag) {
                if(sb.length() > 0) {
                    list.add(sb.toString());
                    sb = new StringBuffer();
                }
            } else {
                if (left < str.length()) {
                    sb.append(temp.substring(0, 1));
                    left += 1;
                }
            }
        }
        if (sb.length() > 0) {
            list.add(sb.toString());
        }
        String[] resu = new String[list.size()];
        list.toArray(resu);
        return resu;
    }
    public static String[] getSplit2(String str, String[] dict) {   
        Trie root = new Trie();
        for (String s : dict) {
            root.insert(s);
        }
        List list = new ArrayList();
        int left = 0;
        StringBuffer sb = new StringBuffer(); 
        while (left < str.length()) {
            String temp = str.substring(left);
            int num = root.search(root, temp, 0);
            if (num < 0) {
                sb.append(str.charAt(left));
                left++;
            } else {
                list.add(sb.toString());
                sb = new StringBuffer();
                left += num;
            }
        } 
        if (sb.length() > 0) {
            list.add(sb.toString());
        }
        String[] resu = new String[list.size()];
        list.toArray(resu);
        return resu;
    }
    class Trie {
        Trie[] child = new Trie[26];
        boolean isWord = false;
        public void insert(String s) {
            Trie temp = this;
            int left = 0;
            while (left < s.length()) {
                int index = s.charAt(left) - 'a';
                if (temp.child[index] == null) {
                    temp.child[index] = new Trie();
                } 
                temp = temp.child[index];
                left++;
            }
            temp.isWord = true;
        }
        public int search(Trie root,String tar, int num) {
            int index = tar.charAt(num) - 'a';
            if (root.isWord) {
                return num;
            }
            if (root.child[index] == null) {
                return -1;
            }
            return search(root.child[index],tar, num + 1);
        }
}

11.2编写一个方法,对字符串数组进行排序,将所有变位词排在相邻的位置。

思路:由于没有要求总体排序方法,可以用桶排序修改来的。

桶排序的一个例子
    public void RadixSort(String[] strs){
        Map<String, ArrayList<String>> map = new HashMap<String, ArrayList<String>>();
        String key = null;
        for (String s : strs){
            key = changeSort(s.toCharArray());
            if (!map.containsKey(key)){
                map.put(key, new ArrayList<String>());
            }
            List<String> temp = map.get(key);
            temp.add(s);
        }
        int index = 0;
        for (String mapkey : map.keySet()){
            List<String> list = map.get(mapkey);
            for (String s : list){
                System.out.println(s);
                strs[index++] = s;
            }
        }
    }
    public String changeSort(char[] carray){
        Arrays.sort(carray);
        return new String(carray);
    }

11.3给定一个排序后的数组,包含n个整数,但这个数组已经被旋转过,旋转次数不详,找出数组中的某个元素。

思路:不论如果旋转,二分查找必然有一块是正常顺序排列的,先对正常顺序的判断,不在就递归非正常那一半。注意多个数值连续相等的 情况。
没有bugfree的原因为,在递归调用函数的时候,没有return返回值

    public int findIndex(int[] nums, int tar, int left, int right){
        if (left > right){
            return -1;
        }
        int mid = left + ((right - left) >> 1);
        if (nums[mid] == tar){
            return mid;
        }
        if (nums[left] < nums[mid]){//左
            if ((tar >= nums[left]) && (tar <= nums[mid])){
                return findIndex(nums, tar, left, mid -1);
            } else {
                return findIndex(nums, tar, mid + 1, right);
            }
        } else if (nums[mid] < nums[right]){//右
            if ((tar >= nums[mid]) && (tar <= nums[right])){
                return findIndex(nums, tar, mid + 1, right);
            } else {
                return findIndex(nums, tar, left, mid -1);
            }
        } else {
            if (nums[left] == nums[mid]){//左
                if (nums[mid] != right){
                    return findIndex(nums, tar, mid + 1, right);
                } else {
                    int result = findIndex(nums, tar, left, mid - 1);
                    if (result == -1){
                        result = findIndex(nums, tar, mid + 1, right);
                    }
                    return result;
                }
            }
        }
        return -1;
    }
// leetcode 更新过的
public class Solution {
    public int search(int[] nums, int target) {
        int start = 0;
        int end = nums.length - 1;
        while (start <= end){
            int mid = (start + end) / 2;
            if (nums[mid] == target)
                return mid;

            if (nums[start] <= nums[mid]){
                 if (target < nums[mid] && target >= nums[start]) 
                    end = mid - 1;
                 else
                    start = mid + 1;
            } 

            if (nums[mid] <= nums[end]){
                if (target > nums[mid] && target <= nums[end])
                    start = mid + 1;
                 else
                    end = mid - 1;
            }
        }
        return -1;
    }
}

11.7叠罗汉表演,上面的人要比下面的矮一点、轻一点。最多叠加几个人。

思路:先用典型的动态规划与递归解一下

递归一般代码量较小
而且基本上可以通过规则编程来实现
    public int getNum(Employ[] nums, Employ em, Map map){
        if (em != null && map.containsKey(em)){
            return (int)map.get(em);
        }
        int max = 0;
        int value = 0;
        for (int i = 0; i < nums.length; i++){
            if ((nums[i].wei < em.wei) && (nums[i].hei < em.hei)){
                value = getNum(nums, nums[i], map);
            }
            if (value > max){
                max = value;
            }
        }
        max++;
        map.put(em, max);
        return max;
    }
    public int getNum(Employ[] nums){
        Map map = new HashMap();
        int max = 0;
        int temp = 0;
        for (int i = 0; i < nums.length; i++){
            temp = getNum(nums, nums[i], map);
            if (temp > max){
                max = temp;
            }
        }
        return max;
    }

11.8假设你正在对去一串整数。每隔一段时间,你希望找出x的秩(小于或等于x的数目)。实现数据结构和算法支持这一操作。

思路:现在给出二叉查找树的操作:

public class RankNode {
        public int left_size;
        public int data;
        public RankNode left;
        public RankNode right;
        public RankNode(int d){
            data = d;
        }
        public void insert(int d){
            if (d <= data){ //left
                if (left == null){
                    left = new RankNode(d);
                } else {
                    left.insert(d);
                }
                left_size++;
            } else {
                if (right == null){
                    right = new RankNode(d);
                } else {
                    right.insert(d);
                }
            }
        }
        public int getRankNum(int d){
            if (data == d){
                return left_size;
            } else if (d < data){
                if (left == null){
                    return -1;
                }
                return left.getRankNum(d);
            } else {
                if (right == null){
                    return -1;
                }
                return left_size + 1 + right.getRankNum(d);
            }
        }
}

14 java

14.6实现CircularArray类,支持类似数组的数据结构,这些数据结构可以高效地进行旋转。使用泛型,通过标准的 for (Obj o : circularArray)语法支持迭代操作。

错误点: 注意 items = (T[]) new Object[size];

这个可以快速的完成移位的操作。
public class CircularArray <T> implements Iterable<T>{
        private T[] items;
        private int head = 0;
        public CircularArray(int size){
            items = (T[])new Object[size];
        }
        private int convert(int index){
            if (index < 0){
                index += items.length;
            }
            return (head + index) % items.length;
        }
        public void rotate(int shiftright){
            head = convert(shiftright);
        }
        public T get(int i){
            if (i < 0 || i >= items.length){
                throw new java.lang.IndexOutOfBoundsException("out of bound");
            }
            return items[convert(i)];
        }
        public void set(int i, T item){
            items[convert(i)] = item;
        }
        @Override
        public Iterator iterator() {
            return new CircularArrayIterator(this);
        }
        private class CircularArrayIterator<TI> implements Iterator<TI>{
            private int _current = -1;
            private TI[] _items;

            public CircularArrayIterator(CircularArray  ite){
                _items = ite.items;
            }
            @Override
            public boolean hasNext() {
                return _current < _items.length - 1;
            }

            @Override
            public TI next() {
                _current ++;
                return _items[convert(_current)];
            }

        }
}

15.数据库

15.1编写SQL查询,列出租住不只一套公寓的承租人。

having字句可以让我们筛选成组后的各种数据,where字句在聚合前先筛选记录,也就是说作用在group by和having字句前。而 having子句在聚合后对组记录进行筛选。group by 就会进行分组统计。
select home_page from aaa表 where char_length(trim(home_page))<10 and char_length(trim(home_page))>1;

SELECT TenantName
FROM Tenants
INNER JOIN 
    (SELECT TenantID
     FROM AptTenants 
     GROUP BY TenantID
     HAVING count(*) > 1) C
ON Tenants.TenantID = C.TenantID;    

15.2编写SQL查询,列出所有的建筑物,并取得状态为“Open”的申请数量(Request 表中Status为Open的条目)

诸如这种有子查询的查询,务必要求全面测试,先测试内层,后测试外层。
子查询的括号结束后,必须要跟一个字符串名字

SELECT BuildingName, isnull(CountNumber, 0) as 'CountNumber'
FROM BUildings 
LEFT JOIN 
    (select Apartments.BuildingID, count(*) as  'CountNumber'
     from Requests inner join Apartements 
     on Requests.AptID = Apartements.AptID
     where Requests.Status = 'Open'
     group by Apartments.BuildingID) Reqcounts
on  Reqcounts.BuildingID =  BUildings.BuildingID;

15.3 11号建筑物正在进行大翻修。编写SQL查询,关闭这栋建筑物里所有公寓的入住申请

Update Requests set Status = 'Closed'
where AptID in
    (select AptID from Apartments where BuildingID = 11);

15.4 连接有哪些不同的类型?请说明这些类型之间的差异,以及为何在某些情况下,某种连接会比较好。

JOIN用于合并两个表的结果,每个表里至少要有一个字段可以配对另一个表里的记录。
INNER JOIN 结果集只包含有配对成功的数据。
LEFT JOIN 结果会包含左表的所有数据
RIGHT JOIN 结果会包含右表的所有数据
OUTER JOIN 结果会合并两个表格的数据

15.7给定一个存储有学生成绩的简单数据库。设计这个数据库的样子,编写SQL查询,返回优等生名单(排名前10%),以平均分排序

Student (学生) Courses(课程)CourseEnrollment(选修课程)
mysql对于procedure的中参数declare的定义不使用@符号

CREATE PROCEDURE p11 () 
    BEGIN 
    DECLARE GPAOff float;
    set GPAOff = (select min(Grade)  from CourseEnrollment);
    select * from CourseEnrollment where Grade > GPAOff;
    END; 

16线程与锁

在java扩展多线程的时候,相比扩展Thread类,实现Runnable接口可能更优: java不支持多重继承,实现runnable接口还可以继续扩展其他的类。类可能只要求执行,继承Thread类的开销过大。
同步和锁
同步方法(方法名前加synchronized), 同步块(synchronized(this){})
锁 在构造函数里面加上 lock = new ReentrantLock(); lock.lock() lock.unlock();
死锁:互斥,持有并等待, 没有抢占, 循环等待,从循环等待入手处理。

16.3哲学家问题,

拿不到右筷子,放下左筷子。

校招leet

424. Longest Repeating Character Replacement

允许修改k个,滑动串口机制,s = “AABABBA”, k = 1 Output: 4 BBBB

449. Serialize and Deserialize BST

串行化与反串行化二叉树

 public String serialize(TreeNode root) {
        StringBuilder sb = new StringBuilder();
        builderString(root, sb);
        return sb.toString();
    }
    public void builderString(TreeNode root, StringBuilder sb) {
        if (root == null) {
            sb.append(NN).append(spliter);
        } else {
            sb.append(root.val).append(spliter);
            builderString(root.left, sb);
            builderString(root.right, sb);
        }
    }
    public TreeNode deserialize(String data) {
        if (data == null || data.length() < 1) {
            return null;
        }        
        Deque<String> nodes = new LinkedList<String>();
        nodes.addAll(Arrays.asList(data.split(spliter)));
        return getTree(nodes);
    }
    public TreeNode  getTree(Deque<String> list) {
        String val = list.remove();
        if (val.equals(NN)) {
            return null;
        } 
        TreeNode root = new TreeNode(Integer.valueOf(val));
        root.left = getTree(list);
        root.right = getTree(list);
        return root;
    }

650. 2 Keys Keyboard

灵活的动态规划,A, 拷贝,粘贴 , n个A,求操作次数

    int[] dp = new int[n+1];
        for (int i = 2; i <= n; i++) {
            dp[i] = i;
            for (int j = i-1; j > 1; j--) {
                if (i % j == 0) {
                    dp[i] = dp[j] + (i/j);
                    break;
                }
            }
        }
        return dp[n];
循环版本
       int s = 0;
        for (int d = 2; d <= n; d++) {
            while (n % d == 0) {
                s += d;
                n /= d;
            }
        }
        return s;

319. Bulb Switcher

开关灯的问题(int)Math.sqrt(n);

672. Bulb Switcher II

分析题,最多只有8种情况

394. Decode String

s = “3[a2[c]]”, return “accaccacc”.

计算各种表达式,最常用的数据结构就是栈,逆波兰表达式,以及这个都是采用两个stack
  1. Maximum Width of Binary Tree
    二叉搜索树之间的最大宽度,只是加了index,
ArrayDeque  count = new ArrayDeque<>();
  count.offer(index*2);
 count.offer(index*2 + 1);

303. Range Sum Query - Immutable

给定数组,计算i到j的累加和

307. Range Sum Query - Mutable

两个方法,Binary index tree,或者是segment tree
例如binary index tree;

其中i & -i是取出i的左后一位数字
int[] nums;
    int[] BIT;
    int n;

    public NumArray(int[] nums) {
        this.nums = nums;

        n = nums.length;
        BIT = new int[n + 1];
        for (int i = 0; i < n; i++)
            init(i, nums[i]);
    }

    public void init(int i, int val) {
        i++;
        while (i <= n) {
            BIT[i] += val;
            i += (i & -i);
        }
    }

    void update(int i, int val) {
        int diff = val - nums[i];
        nums[i] = val;
        init(i, diff);
    }

    public int getSum(int i) {
        int sum = 0;
        i++;
        while (i > 0) {
            sum += BIT[i];
            i -= (i & -i);
        }
        return sum;
    }

    public int sumRange(int i, int j) {
        return getSum(j) - getSum(i - 1);
    }

678. Valid Parenthesis String

Input: “())” Output: True,号表示是不是常规

这里面存放的是id
        while (!leftID.isEmpty() && !starID.isEmpty()) {
            if (leftID.pop() > starID.pop()) 
                return false;
        }

673. Number of Longest Increasing Subsequence

单调递增子序列的进化版本,最长有多少个可以序列可以达到当前的标准

    public int findNumberOfLIS(int[] nums) {
        int n = nums.length, res = 0, max_len = 0;
        int[] len = new int[n], cnt = new int[n];
        for (int i = 0; i < n; i++) {
            len[i] = cnt[i] = 1;
            for (int j = 0; j < i; j++) {
                if (nums[i] > nums[j]) {
                    if (len[i] == len[j] + 1) cnt[i] += cnt[j];
                    if (len[i] < len[j] + 1) {
                        len[i] = len[j] + 1;
                        cnt[i] = cnt[j];
                    }
                }

            }
            if (len[i] == max_len) res += cnt[i];
            if (len[i] > max_len) {
               max_len = len[i];
               res = cnt[i];
            }
        }
        return res;
    }

322. Coin Change

找硬币的两个题目:都是采用动态规划,
第一个题目:硬币数目结果最小

   public int coinChange(int[] coins, int amount) {
       if (amount < 0) {
           return -1;
       }
       int[] dp = new int[amount + 1];
       Arrays.fill(dp, Integer.MAX_VALUE);
       dp[0] = 0;
       for (int i = 1; i <= amount; i++) {
           for (int j :coins) {
               if (i - j >=0 && dp[i - j] != Integer.MAX_VALUE){
                    dp[i] = Math.min(dp[i], dp[i - j] + 1);
               }
           }
       }
       return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];
   }

518. Coin Change 2

这个是看下一共有多少个不同种类的方案,

    public int change(int amount, int[] coins) {
        if (amount == 0) return 1;
        if (coins.length == 0) return 0;
        int[] dp = new int[amount + 1];
        Arrays.fill(dp, 0);
        dp[0] = 1;
        for (int val : coins) {
            for (int i = val; i <= amount; i++) {
                dp[i] += dp[i - val];
            }
        }
        return dp[amount];
    }

你可能感兴趣的:(算法小结)