几个常用算法需要注意的点:
二分: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);
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();
}
思路:1.位操作,较大的数:注意要判断非拖尾0的存在,要将右边有1的第一个0(位置为p = C0 (拖尾0的个数)+ C1(右边1的个数))变为1,将p后面的数据全部清零,然后补上C1个1,要判断更大值是否存在。
较小的数 : 注意C1是拖尾1的个数,C0是紧邻拖尾1的左方一连串的个数,要从1变为0 的数右侧必须包含0.否则数会大。
根据位操作的简化,可以得到out of box的算法,依据之前的数。注意拖尾与非拖尾。
思路:利用掩码提取奇数位,右移。利用掩码提取偶数位,左移。然后相或得到。
掩码的得到直接就是0xaa以及0x55
思路: 负载均衡下,要求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类型的范围
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”。
面试官想要找的就是这种能以逻辑思考逐步解决问题的人
显示代码的复用能力
思路:自己实现正号变负号,负号变正号的操作,加与乘法都可以处理,
public int negate(int a){
int neg = 0;
int d = a < 0 ? 1 : -1;
while(a != 0){
neg += d;
a += d;
}
return neg;
}
乘法是注意判断,让小的数a迭代
思路:两点之间确定直线,时间复杂度O(n*n),利用HashMap进行次数的存储。
思路:这个方法与素数筛选法类似,关键是要想得到,首先插入数据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;
}
打造优雅,易于维护的面向对象代码。
单例模式,只构造这一个对象,访问与修改都是通过内部方法统一进行。
处理本类问题思路: 处理不明确的地方,定义核心对象,分析对象关系,设计对象动作
系统组件:点唱机(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;
}
}
系统的难点:如何确定某人在线,如何处理冲突信息,如何让服务器在任何负载下,都能应付自如,如何预防拒绝服务攻击。
思路:存储一个由链表组成的数组,链表元素为包含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;
}
} | | | | |
递归结束条件,得到递归返回值,计算本身返回值,
动态规划有时是缓存了递归的结果的递归优化版。
能用迭代尽量不适用递归。
思路:斐波那契数列改进版,
最大的不同是,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;
}
思路: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;
}
思路:从字符串的单个字符串出发,每次都进行递归添加。
没有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());
}
思路:采用递归的方式,是由前一个进行增加处理,插入括号的位置依据左括号的位置进行处理,注意重复问题,用set。
还有一种记录左右括号的数目,如果还有左括号可以使用,就插入左括号,然后递归,如果左括号用的多,就插入右括号。
enum表示枚举 例如 public enum Color {Black, White, Red, Yellow, Green}
当涉及到路径问题时,递归的参数要有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位置上放的是处于当前位置上最小的值
思路:典型的解决思路是利用递归得到每一个箱子作为底的高度,然后取出最大值,加上动态规划的缓存,不存在顺序问题。
评估分解棘手问题的能力,以及运用所学知识解决问题的能力。
步骤 大胆假设,切合实际,解决问题
思路:查找表,分批进行存储,在两个端点进行广度优先查找
没有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);
}
}
思路:由于没有要求总体排序方法,可以用桶排序修改来的。
桶排序的一个例子
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);
}
思路:不论如果旋转,二分查找必然有一块是正常顺序排列的,先对正常顺序的判断,不在就递归非正常那一半。注意多个数值连续相等的 情况。
没有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;
}
}
思路:先用典型的动态规划与递归解一下
递归一般代码量较小
而且基本上可以通过规则编程来实现
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;
}
思路:现在给出二叉查找树的操作:
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);
}
}
}
错误点: 注意 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)];
}
}
}
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;
诸如这种有子查询的查询,务必要求全面测试,先测试内层,后测试外层。
子查询的括号结束后,必须要跟一个字符串名字
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;
Update Requests set Status = 'Closed'
where AptID in
(select AptID from Apartments where BuildingID = 11);
JOIN用于合并两个表的结果,每个表里至少要有一个字段可以配对另一个表里的记录。
INNER JOIN 结果集只包含有配对成功的数据。
LEFT JOIN 结果会包含左表的所有数据
RIGHT JOIN 结果会包含右表的所有数据
OUTER JOIN 结果会合并两个表格的数据
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;
在java扩展多线程的时候,相比扩展Thread类,实现Runnable接口可能更优: java不支持多重继承,实现runnable接口还可以继续扩展其他的类。类可能只要求执行,继承Thread类的开销过大。
同步和锁
同步方法(方法名前加synchronized), 同步块(synchronized(this){})
锁 在构造函数里面加上 lock = new ReentrantLock(); lock.lock() lock.unlock();
死锁:互斥,持有并等待, 没有抢占, 循环等待,从循环等待入手处理。
拿不到右筷子,放下左筷子。
允许修改k个,滑动串口机制,s = “AABABBA”, k = 1 Output: 4 BBBB
串行化与反串行化二叉树
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;
}
灵活的动态规划,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;
开关灯的问题(int)Math.sqrt(n);
分析题,最多只有8种情况
s = “3[a2[c]]”, return “accaccacc”.
计算各种表达式,最常用的数据结构就是栈,逆波兰表达式,以及这个都是采用两个stack
ArrayDeque count = new ArrayDeque<>();
count.offer(index*2);
count.offer(index*2 + 1);
给定数组,计算i到j的累加和
两个方法,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);
}
Input: “())” Output: True,号表示是不是常规
这里面存放的是id
while (!leftID.isEmpty() && !starID.isEmpty()) {
if (leftID.pop() > starID.pop())
return false;
}
单调递增子序列的进化版本,最长有多少个可以序列可以达到当前的标准
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;
}
找硬币的两个题目:都是采用动态规划,
第一个题目:硬币数目结果最小
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];
}
这个是看下一共有多少个不同种类的方案,
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];
}