递归书写方法
- 数学归纳法中的数学/自然语言<–>程序语言(证明递归函数正确执行)
- 严格定义递归函数作用,包括参数,返回值,Side-effect
- 先一般,后特殊
- 每次调用必须缩小问题规模
- 每次问题规模缩小程度必须为1
- 递归缺点
- 调用堆栈Stack太深,开销大
- 如果数据大,就会Stack Overflow!
- 不要尝试递归改成非递归
- 一般化的方法仍需要栈
- 代码复杂
- 不根本解决问题
Side-effect 函数副作用。理想的状态是函数运行完没有side-effect。 比较函数执行中会修改全局的一些属性,当执行完,也要将这些全局属性还原
链表
package interview.recursion;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import interview.common.Node;
public class LinkedListCreator {
/**
* Creates a linked list.
*
* @param data the data to create the list
* @return head of the linked list. The returned linked list
* ends with last node with getNext() == null.
*/
public <T> Node<T> createLinkedList(List<T> data) {
if (data.isEmpty()) {
return null;
}
Node<T> firstNode = new Node<>(data.get(0));
firstNode.setNext(
createLinkedList(data.subList(1, data.size())));
return firstNode;
}
public Node<Integer> createLargeLinkedList(int size) {
Node<Integer> prev = null;
Node<Integer> head = null;
for (int i = 1; i <= size; i++) {
Node<Integer> node = new Node<>(i);
if (prev != null) {
prev.setNext(node);
} else {
head = node;
}
prev = node;
}
return head;
}
public static void main(String[] args) {
LinkedListCreator creator = new LinkedListCreator();
Node.printLinkedList(
creator.createLinkedList(new ArrayList<>()));
Node.printLinkedList(
creator.createLinkedList(Arrays.asList(1)));
Node.printLinkedList(
creator.createLinkedList(Arrays.asList(1, 2, 3, 4, 5)));
}
}
链表反转
package interview.recursion;
import java.util.ArrayList;
import java.util.Arrays;
import interview.common.Node;
public class LinkedListReverser {
/**
* Reverses a linked list.
*
* @param head the linked list to reverse
* @return head of the reversed linked list
*/
public <T> Node<T> reverseLinkedList(Node<T> head) {
// size == 0 or size == 1
if (head == null || head.getNext() == null) {
return head;
}
// 第一步(对应上图)
Node<T> newHead = reverseLinkedList(head.getNext());
// 第二步
head.getNext().setNext(head);
// 第三步 over
head.setNext(null);
return newHead;
}
public static void main(String[] args) {
LinkedListCreator creator = new LinkedListCreator();
LinkedListReverser reverser = new LinkedListReverser();
Node.printLinkedList(reverser.reverseLinkedList(
creator.createLinkedList(new ArrayList<>())));
Node.printLinkedList(reverser.reverseLinkedList(
creator.createLinkedList(Arrays.asList(1))));
Node.printLinkedList(reverser.reverseLinkedList(
creator.createLinkedList(Arrays.asList(1, 2, 3, 4, 5))));
System.out.println("Testing large data. Expect exceptions.");
reverser.reverseLinkedList(
creator.createLargeLinkedList(1000000));
System.out.println("done");
}
}
combinations([1,2,3,4],2)
- 缩小问题规模
- 选1>combinations([2,3,4],1)
- 不选1>combinations([2,3,4],2)
package interview.recursion;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Combinations {
/**
* Generates all combinations and output them,
* selecting n elements from data.
*/
public void combinations(
List<Integer> selected, List<Integer> data, int n) {
if (n == 0) {
// output all selected elements
for (Integer i : selected) {
System.out.print(i);
System.out.print(" ");
}
System.out.println();
return;
}
if (data.isEmpty()) {
return;
}
// select element 0
selected.add(data.get(0));
combinations(selected, data.subList(1, data.size()), n - 1);
// un-select element 0
selected.remove(selected.size() - 1);
combinations(selected, data.subList(1, data.size()), n);
}
public static void main(String[] args) {
Combinations comb = new Combinations();
System.out.println("Testing normal data.");
comb.combinations(
new ArrayList<>(), Arrays.asList(1, 2, 3, 4), 2);
System.out.println("==========");
System.out.println("Testing empty source data.");
comb.combinations(
new ArrayList<>(), new ArrayList<>(), 2);
System.out.println("==========");
comb.combinations(
new ArrayList<>(), new ArrayList<>(), 0);
System.out.println("==========");
System.out.println("Selecting 1 and 0 elements.");
comb.combinations(
new ArrayList<>(), Arrays.asList(1, 2, 3, 4), 1);
System.out.println("==========");
comb.combinations(
new ArrayList<>(), Arrays.asList(1, 2, 3, 4), 0);
System.out.println("==========");
System.out.println("Testing large data");
comb.combinations(
new ArrayList<>(),
Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 4);
}
}
循环控制(循环不变式 loop invariant)
边界控制
- 列表项
- 初始值
- 特殊值
- null
- 空字符串
数据结构
树的遍历
算法复杂度
- 代表最坏情况用时
- f(x)=O(g(x))asx-oo 当且仅当
- If(x)1≤Mlg(x)l for all x >=xo 相当于高等数学中极限的定义
广义的“极限”是指“无限靠近而永远不能到达”的意思。数学中的“极限”指:某一个函数中的某一个变量,此变量在变大(或者变小)的永远变化的过程中,逐渐向某一个确定的数值A不断地逼近而“永远不能够重合到A”(“永远不能够等于A,但是取等于A‘已经足够取得高精度计算结果)的过程中,此变量的变化,被人为规定为“永远靠近而不停止”、其有一个“不断地极为靠近A点的趋势”。极限是一种“变化状态”的描述。此变量永远趋近的值A叫做“极限值”(当然也可以用其他符号表示)。
面向对象思想
商业代码复杂性,从用户角度思考问题
摒弃完全基于逻辑的思维
类与对象
- 类的成员变量>对象状态
- 类的成员函数>对象行为
- 类的静态变量
- 类的静态函数
- 没有this引用,静态变量全局唯一—份
- 普通函数可以引用静态变量、函数
- 对象上引用静态变量、函数会产生编译器警告
- 静态函数引用普通成员变量、函数会编译错误
类的特殊函数
- 构造函数
- equals
- hashCode
- toString
- a.equals(b)说明a.hashCode)==b.hashCode) 必要条件
接口与实现
- 与类相比
- 由编译器强制的一个模块间协作的合约(Contract)
- 无成员变量
- 成员函数只有申明不能有实现
- 接口的申明
- Java:interface BankEndPoint{……
- C++:一个全部是纯虚函数的类
- Python/大部分动态语言:依靠注释申明
接口和抽象类有什么不同?
- 从实现角度看
- 抽象类可以有成员变量
- 抽象类可以有部分实现
- 抽象类不可以多重继承,接口可以
- 接口强调合约
- 强制协作双方无法犯错(编译器)
- 但是抽象类提供公共的实现
继承与封装
不可变对象 Immutable Objects
- 可以引用传递,可以缓存
- 线程安全
- final 关键字
- 类申明>类不可以被继承
- 函数申明>函数不可以在派生类中重写
- 变量申明>变量不可以指向其它对象
- static final变量>用于定义常量,名称一般大写
- final关键字无法保证不可变性
- 从接口定义,类的实现上保证不可变性
- Collections.unmodifiableXXX
泛型
c++虚函数表
void dispatch_work(Employee* p){
p->doWork();
dispatch_ work(&manager);
设计模式(解耦和)
设计模式当初的提出就是因为GOF(Erich Gamma,Richard Helm,Ralph Johnson和John Vlissides)的博士论文(这才是真正的程序员)
再谈Singleton(单例模式)
- 确保全局至多只有一个对象
- 用于:构造缓慢的对象,需要统一管理的资源
- 缺点:很多全局状态,线程安全性
- 双重锁 Double checked locking
- 第一次check全局对象是不是null,不是null直接拿来用,如果是null,锁住,再check一次(防止其他人在这个空挡创建)
- 作为Java类的静态变量(全局只有一份,不过程序员初始化就要创建这个静态变量,所以也就增加了初始化的时间(如果是比较缓慢的对象))
- 使用框架提供的能力
- 依赖注入
变继承关系为组合关系(状态模式State)
- 描述is-a关系
- 不要用继承关系来实现复用
- 状态逻辑和动作实现没有分离。在很多的系统实现中,动作的实现代码直接写在状态的逻辑当中。这带来的后果就是系统的扩展性和维护得不到保证。
- 使用设计模式来实现复用
Decotator装饰器模式
interface Runnable{
void run);
如何实现LoggingRunnable,TransactionalRunnable.…
对象如何创建
使用new来创建的缺点
- 编译时必须决定创建哪个类的对象
- 参数意义不明确
Abstract Factory Pattern 抽象工厂
task=new Logging Task(new Coding Task);
task=taskFactory.createCodingTask); (好)
Builder Pattern 生成器
解决参数意义不明确的问题
不可变对象往往配合Builder使用
employee=new Employee(
oldEmployee.getName),15000);
employee=Employee.fromExisting(oldEmployee)
.withSalary(15000)
.build();
Beautiful Numbers
1,11,111…是beautiful的
- 3>2进制>11
- 13>3进制>111(选位数多的)
- 133+1*3+1=13
- 13%3=1,13/3=4
- 4%3=1,4/3=1
- 1%3=1,1/3=0
- 13>12进制>11
- N>r进制>111…1(k个) 求大数据集思路
- N=r(k-1)+r(k-2)+.…+r+1
- N=(1-r^k)/(1-r)
- 假设N能转化成k个1组成的Beautiful Number
- 那么这个Beautiful Number是几进制?r=?
- 因为k最多是64个(最低两位),但是r最多就太大了
O(nlogn) 10*8 ~= 1s
package interview.google;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Scanner;
public class BeautifulNumber {
public static void main(String[] args) {
Scanner in = new Scanner(
new BufferedReader(new InputStreamReader(System.in)));
int cases = in.nextInt();
for (int i = 1; i <= cases; ++i) {
long n = in.nextLong();
System.out.println("Case #" + i + ": "
+ beautiful(n));
}
}
private static long beautiful(long n) {
for (long radix = 2; radix < n; radix++) {
if (isBeautiful(n, radix)) {
return radix;
}
}
throw new IllegalStateException("Should not reach here.");
}
private static boolean isBeautiful(long n, long radix) {
while (n > 0) {
if (n % radix != 1) {
return false;
}
n /= radix;
}
return true;
}
}
O(logn*logn*logn) 64*64*64
package interview.google;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Scanner;
public class BeautifulNumberLarge {
public static void main(String[] args) {
Scanner in = new Scanner(
new BufferedReader(new InputStreamReader(System.in)));
int cases = in.nextInt();
for (int i = 1; i <= cases; ++i) {
long n = in.nextLong();
System.out.println("Case #" + i + ": "
+ beautiful(n));
}
}
private static long beautiful(long n) {
for (int bits = 64; bits >= 2; bits--) {
long radix = getRadix(n, bits);
if (radix != -1) {
return radix;
}
}
throw new IllegalStateException("Should not reach here.");
}
/**
* Gets radix so that n is 111...1 (bits 1 in total) in that
* radix.
*
* @return the radix. -1 if there's no such radix.
*/
private static long getRadix(long n, int bits) {
long minRadix = 2;
long maxRadix = n;
while (minRadix < maxRadix) {
// 二分查找法
long m = minRadix + (maxRadix - minRadix) / 2;
long t = convert(m, bits);
if (t == n) {
return m;
} else if (t < n) {
minRadix = m + 1;
} else {
maxRadix = m;
}
}
return -1;
}
/**
* Returns the value of 111...1 (bits 1 in total) in radix.
* 等比数列求和
*/
private static long convert(long radix, int bits) {
long component = 1;
long sum = 0;
for (int i = 0; i < bits; i++) {
if (Long.MAX_VALUE - sum < component) {
sum = Long.MAX_VALUE;
} else {
sum += component;
}
// 防止内存溢出
if (Long.MAX_VALUE / component < radix) {
component = Long.MAX_VALUE;
} else {
component *= radix;
}
}
return sum;
}
}
点击查看更多内容
5人点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦