This commit is contained in:
nonstriater
2014-05-15 02:14:18 +08:00
parent 9f93f74a18
commit c9b164bf1f
5 changed files with 315 additions and 77 deletions
+119
View File
@@ -0,0 +1,119 @@
1. 面问的主要是知识点。
2. 面问主要考察的是设计解决方案的能力,另外辅助追问的方式,考察深度和广度,回答过程中需要列出适合的具体例子,方案还需要细致到具体的关键的函数名称,方法。
3. 考察设计模式的理解,算法
###UI系统
设计一个progress bar解决方案,追问到 Core Graphic、CGPath、maskLayer。
设计一个popup view被追问到 keyWindow、UIWindow的layer、UIView hierarchy。
CALayer和View的关系
一个UIView包含CALayer树,CALayer是一个数据模型,包含了一些用于显示的对象,但本身不用于显示。
CALayer相当于photoshop的一个层,很多动画可以通过设置CALayer来实现。据说有人用CALayer显示图片来播放视频。
Core animation应该是用CAlayer来实现各种动画。
这里有篇博文,介绍的还可以http://www.cnblogs.com/uyoug321/archive/2011/01/22.html
###消息系统
从设计模式的角度分析Delegate、Notification、KVO的区别。被追问到自己写的library和开源的项目中用到哪些设计模式,为什么使用,有哪些好处和坏处,现在能否改进。
算是问题3的追问,设计一个方案来检测KVO的同步异步问题。willChange和 didChange的不同点,然后被追问到有没有其他地方也有类似情况,被追问到Core Data 中falut object。
这个是问题4的追问,设计一个KVO系统。
###多线程
Multithreading,什么时候采用Multithreading方案,以及理由。追问到系统还有哪些在后台运行的thread,
Multithreading中常常遇到的问题,追问到死锁,优先级翻转,线程池等。
###运行时
Objective C runtime library:这些都需要背后C一层的描述,
Objective C的对象模型,
block的底层实现结构,
消息发送,消息转发,
内存管理
Multithreading:什么时候处理多线程,几种方式,优缺点。
Object c研究的比较深入的教程
http://www.cnblogs.com/yaski/ 第6章写的尤其好
KVO/KVC 实现机理分析
http://archive.cnblogs.com/a/1886332/
###数据处理
Core Data:中多线程中处理大量数据同步时的操作。
缓存设计
sqlite中插入特殊字符的方法和接收到处理方法。
###网络部分
asihttp代码原理 ,异步请求的原理,异步请求最大数目,为什么只能这么多?
叙述TCP/UDP协议,他们的应用,TCP的三次握手,滑动窗口,超时重传;HTTP协议和COOKIE机制和一些COOKIE的问题
http协议,tcp/ip,TCP/UDP
### 数据结构和算法
C++里STL的几个库是怎么实现的
(类似:stl有哪些容器?各个容器的区别?以及各容器的数据结构?)
哈希表的遍历时间复杂度
(分析:这个跟hash表的存储结构和hash函数有关)
### 系统设计
设计一个服务器的存储模式
大数据量缓冲服务器的架构设计
手机汉字输入法的架构设计
### 杂谈
简述push原理,push的证书和其它的右什么不一样?
内存寻址方式,
寄存器直接寻址
基址寻址:方便访问k条记录
基址+变址寻址:访问一条记录的字段
比较32位平台和64位平台不同
内存寻址空间变大
基本数据类型的长度也会有些变化(long ,longlong, ptr)
iphone app为什么会被打回来,如何避免??
1) 阅读文档
2) 看别人被拒的原因,吸取教训
为啥离开团队?
在小团队里面工作,虽然比较有机会复杂整个项目,有很多的决策权,如项目框架选型,规范等
“格局” “要求”
当然,我也希望自己身边有一堆牛人,相互学习啦
+106 -41
View File
@@ -16,34 +16,6 @@
### 快速排序
递归一次,pivot 左边都比它小,右边都比它大。这是递归,分治的思想。
对 A[p...r] :
1. 分解:A[p..q-1] A[q+1..r],使得 A[p...q-1]<A[q]<A[q+1..r]
2. 解决:递归调用 快排,,对子数组A[p..q-1],A[q+1..r]排序
3. 合并(子问题相互独立的,因此用分治算法就可以了)
具体步骤:
1. 从数列中选择一个元素,作为基准 pivot。通常去分区的第一个或最后一个
2. 重排数列,比 pivot 小得排左边,比pivot大的排右边,相等的随便。 一句话就是`挖坑填数`
3. 递归的,使用相同的方式,重排左右两边的子序列
扫描过程分2种:
1. 挖坑排序,2头向中间扫描,先从后向前找,再从前向后找。
2. 单向扫描
平均复杂度 O(n*logn) ,
最坏O(n^2)
空间复杂度
快速排序是对冒泡排序的改进,划分交换排序。
### 冒泡排序
@@ -65,25 +37,88 @@
5. 重复2
最坏的情况: 待排序的是一个逆序排放的数组,这样导致每一轮都要移动元素;此时复杂度是是0(n^2)
最好的情况: 待排序的是一已经顺序排放的数字,此时只需要做一轮比较就够了 0(n)
最好的情况: 待排序的是一已经顺序排放的数字,此时只需要做一轮比较就够了 0(n)。因此可以看到,对大部分数据已经有序这样的数组排序,使用`插入排序`非常有优势
空间复杂度O(1)
### 希尔排序
### 归并排序merge
递减增量排序算法,对`插入排序`的改进,实质是分组插入排序,又叫`缩小增量排序`
分治算法
1. 先将待排数列分割成若干子序列(增量为m)
2. 对每个子序列使用`插入排序`
3. 减小增量,再排序
4. 对全体元素做一次`插入排序`
2个有序数组的合并操作是O(n)的复杂度
因此我们可以将无序的数组,分成2个子数组分别排序,然后再merge,依次类推
`希尔排序`提升排序的奥秘就在于`数据元素越有序,使用插入排序效率越高`
归并排序的步骤
1. 分解 , 将一个数组分成2个子数组
2. 解决 , 将2个子数组都排好序,然后 merge 2个有序数组
3. 合并
### 快速排序
递归一次,pivot 左边都比它小,右边都比它大。这是递归,分治的思想。
对 A[p...r] :
1. 分解:A[p..q-1] A[q+1..r],使得 A[p...q-1]<A[q]<A[q+1..r]
2. 解决:递归调用 快排,,对子数组A[p..q-1],A[q+1..r]排序
3. 合并(子问题相互独立的,因此用分治算法就可以了)
具体步骤:
1. 从数列中选择一个元素,作为基准 pivot。通常取分区的第一个或最后一个
2. 重排数列,比 pivot 小得排左边,比pivot大的排右边,相等的随便。 一句话就是`挖坑填数`
3. 递归的,使用相同的方式,重排左右两边的子序列
扫描过程分2种:
1. 挖坑排序,2头向中间扫描,先从后向前找,再从前向后找。
2. 单向扫描
void quicksort(int *a, int left, int right){
if (left<right)//加上这个,不然有死循环,造成堆栈溢出,这也是递归结束条件
{
int i = partion(a,left,right);//使得局部有序,i作为分隔
quicksort(a,left,i-1);
quicksort(a,i+1,right);
}
}
// 挖坑填数,2边向中间扫描
int partion(int *a, int start,int end){
int i=start,j=end;
int tmp = a[i]; // 这里要做越界检查
while(i<j){
// 从后向前扫描,找到第一个小于tmp的值,来填a[i]
while(i<j && a[j]>=tmp){
j--;
}
if (i<j)//找到了,这时候a[j]为坑
{
a[i++] = a[j];
}
// 从左向右扫描,找一个大于 tmp的 数, 去填坑a[j]
while(i<j && a[i]<tmp){
i++;
}
if (i<j)
{
a[j++]=a[i];
}
}
//扫描完成后,i==j
a[i]=tmp;
return i;
}
平均复杂度 O(n*logn) ,
最坏O(n^2)
空间复杂度
快速排序是对冒泡排序的改进,划分交换排序。
### 堆排序
@@ -107,7 +142,7 @@
插入:都是插入到数组最后,然后再调整满足堆次序
删除:删除总是发生在 A[0]处,也就是只删除根节点
这样难怪`堆`被称为 `优先队列`。插入和删除分别在 数组尾部和 头部,只是需要再次调整以满足堆次序。
这样难怪`堆`被称为 `优先队列`。插入和删除分别在 数组尾部和头部,只是需要再次调整以满足堆次序。
`堆`的应用场景有:
优先队列 如iOS中的NSOperationQueue 就是维护一个优先队列
@@ -126,14 +161,44 @@
堆排序主要时间花在建堆期间和`堆化数组`,找数列中最大树只需要O(1)时间复杂度
void heap_sort(int *a, int length){
// 建立堆 大根堆,递增排序
heap_build(a,length);
for (int i = length-1; i >0; --i)
{
//交换
heap_swop(&a[0],&a[i]);
//调整
heap_adjust(a,i);
}
}
推排序还可以用来求 top-K 大(小)的问题。
### 希尔排序
### 归并排序merge
分治算法,必然用到递归
2个有序数组的合并操作是O(n)的复杂度
因此我们可以将无序的数组,分成2个子数组分别排序,然后再merge,依次类推
归并排序的步骤
1. 分解 , 将一个数组分成n/2个子数组,每个序列2个元素,(2路归并)
2. 解决 , 将各个子数组都排好序,然后 merge 2个有序数组
3. 合并
if (length>1)
{
merge_sort(a,length/2);
merge_sort(a+length/2,length-length/2);
merge_array(a,length/2,a+length/2,length-length/2);
}
递减增量排序算法,对`插入排序`的改进
### 小结
@@ -146,7 +211,7 @@
## 3种线性排序算法
上面的算法都是基于比较的排序,时间复杂度最好也是 NlogN.
上面的算法都是基于比较的排序,时间复杂度最好也是 NlogN.而非基于比较的排序,可以突破NlogN的时间下限。当然,非比较的排序,也是需要有一些限定条件的。
### 桶排序 bucket sort
+65 -22
View File
@@ -126,17 +126,6 @@ void quicksort(int *a, int left, int right){
}
// 归并排序
void merge_sort(int *a, int length){
}
// 堆排序
void heap_swop(int *a,int *b){
@@ -210,34 +199,88 @@ void heap_sort(int *a, int length){
{
//交换
heap_swop(&a[0],&a[i]);
//调整
heap_adjust(a,i);
}
}
// 归并排序
// 合并2个有序数组,分配一个临时空间,装a,b的结果,最后,将合并结果拷贝到数组A,是否临时空间
void merge_array(int *a,int size_a,int *b, int size_b){
int *tmp = malloc( (size_a+size_b)*sizeof(int) );
int i,j,k;
i=j=k=0;
while(i<size_a && j<size_b){
tmp[k++] = (a[i]>b[j])?b[j++]:a[i++];
}
while(i<size_a){
tmp[k++]=a[i++];
}
while(j<size_b){
tmp[k++]=b[j++];
}
for (int p = 0; p < k; ++p)
{
a[p] = tmp[p];
}
free(tmp);
}
void merge_sort(int *a, int length){
if (length>1)
{
merge_sort(a,length/2);
merge_sort(a+length/2,length-length/2);
merge_array(a,length/2,a+length/2,length-length/2);
}
}
///////////////////////////////////////////////
#define Max_Number 50
#define Max_Number 5000
int main(){
int a[] = {4,87,2,32,5,2,9,49,49,23,45,2,41};
// 准备5000个数
// int a[Max_Number];
// for (int i = 0; i < Max_Number; ++i)
// {
// a[i]=rand()%Max_Number;
// }
//int a[] = {4,87,2,32,5,2,9,49,49,23,45,2,41};
//准备5000个数
int a[Max_Number];
for (int i = 0; i < Max_Number; ++i)
{
a[i]=rand()%Max_Number;
}
clock_t start,finish;
start = clock();
heap_sort(a,sizeof(a)/sizeof(int)); // 有buggggggg
merge_sort(a,sizeof(a)/sizeof(int)); // 0.002s,可以看到,归并排序还是很快的
//heap_sort(a,sizeof(a)/sizeof(int)); // 有buggggggg
//quicksort(a,0,sizeof(a)/sizeof(int)-1); // 0.01s
//insert_sort(a,sizeof(a)/sizeof(int)); // 3.85s
//select_sort(a,sizeof(a)/sizeof(int)); // 5.3s
@@ -249,7 +292,7 @@ int main(){
{
printf(" %d ",a[i]);
}
printf("time eclipse: %.4f sec\n", (double)(finish-start)/CLOCKS_PER_SEC); // CLOCKS_PER_SEC 1000 clock()是毫秒
printf("time eclipse: %.6f sec\n", (double)(finish-start)/CLOCKS_PER_SEC); // CLOCKS_PER_SEC 1000 clock()是毫秒
return 0;
}
+18 -14
View File
@@ -105,6 +105,24 @@ int div(const int x, const int y)
求两个或N个数的最大公约数和最小公倍数。
大整数乘法(或 大整数阶乘)
请使用代码计算1234567891011121314151617181920*2019181716151413121110987654321 。
1. 注意结果可能超出长整形的最大范围 2^64-1
2. 采用分治算法,将大整数相乘转换为小整数计算
规律分析:
任意位数的整数相乘,最终都可以转化为2位数相乘
两个数相乘,小数点后位数没有限制,请写一个高精度算法
@@ -122,20 +140,6 @@ int div(const int x, const int y)
求两个或N个数的最大公约数和最小公倍数。
大整数乘法(或 大整数阶乘)
请使用代码计算1234567891011121314151617181920*2019181716151413121110987654321 。
1. 注意结果可能超出长整形的最大范围 2^64-1
2. 采用分治算法,将大整数相乘转换为小整数计算
规律分析:
任意位数的整数相乘,最终都可以转化为2位数相乘
+7
View File
@@ -19,6 +19,13 @@ A说不知道,B说不知道,C说不知道,然后A说知道了。
如果用程序,又怎么实现呢?
有A、B、C、D四个人,要在夜里过一座桥。他们通过这座桥分别需要耗时1、2、5、10分钟,只有一支手电,并且同时最多只能两个人一起过桥。请问,如何安排,能够在17分钟内这四个人都过桥?
有12个小球,外形相同,其中一个小球的质量与其他11个不同,给一个天平,问如何用3次把这个小球找出来,并且求出这个小球是比其他的轻还是重
27.跳台阶问题