面试的基础算法和知识点
2015-07-13 00:00
513 查看
摘要: 面试 数据结构
一、 外部文件排序: 内存大小为n,总的数据大小为m
a. 每次读入数据n, quick_sort。生成m/n个有序队列, 依次归并
( 算法实现=> 如果存在 X个初始的有序队列, 则需要logx 趟归并)
b. 每次读入数据n, quick_sort。生成m/n个有序队列, 把每个队列的最小节点建立最小堆,从堆中提取数据并不断插入提取数据的下一个节点
( 算法实现=> 主要过程是建堆, 堆的删除)
c. k-win赢者树 (算法实现=>)
d. k-lost败着树 (算法实现=>)
思考: b, c, d 的比较区别 !!!
2 排序算法:
1. 基数排序: a 高位优先 b 低位优先 ( 算法实现=>)
2. 归并排序 : 时间 nlogn 空间(n)
3. 堆排序: swiftdown 函数的实现 ( 递归 和 非递归的实现)
4. 快速排序: 递归的实现 + partition函数实现
5. 直接插入 , 折半插入, shell插入, 选择排序
稳定排序: 直接插入,冒泡排序,归并排序
3. 查找算法:
1. 静: 直接查找,折半查找(查找上界, 查找下届),分块查找
2. 二叉搜索树, 二叉平衡树, B-树、B+树 {算法实现=>}
a. 二叉搜索树的查找,插入,删除,(度外存次数太多,不宜处理外存)
b. 平衡(AVL)二叉树: 二叉搜索树 + (左树高度 - 右树高度 = [-1,0,1]): 调整过程
B-树, B+ 主要用于外部查找
c. m介 B-树 (算法实现 b-树的结构,查找, 插入, 删除)
根节点 2, m 个子树
其他节点 m/2 , m个子树; 所有叶节点 在同一层
d. m介 b+树 (算法实现=>)
只有叶节点参与索引,两个跟节点(一个 root, 一个min )
3. 哈喜 : 闭环哈西, 开环哈西
4. 拓扑排序,(判断是否存在环路)
关键路径:
单源最短路径:Dijiksa
定点之间的最段路径:floyed
5 最小生成树
prim:
kruskal:
6. 图搜索
dfs()
bfs() :二分图算法
7. 树:
DLR, LDR, LRD的 递归 非递归
huffman树:
二叉树 与 树 森林的转换
8 . 字符串匹配
朴素:
KMP:
9. 栈与队列的实现(环形队列){push, pop, top, empty()} STL的使用
10. 四种类型转换 static_cast dynamic_cast, const_cast , interpret_cast;
static_cast: 用于数据类型转换,还可以用于继承
const_cast: 主要对变量的常量性(const)或volatile属性进行操作, 移除变量的常量性, 即可以被非常量指向和引用, 详见代码; 以便对常量数据进行修改。
dynamic_cast : 向下安全转换 :
主要应用于继承体系, 可以由 "指向派生类的基类部分的指针", 转换成 "指向派生类"或 "指向兄弟类";
static_cast只能转换为"指向派生类",只能向下转换
reinterpret_cast(重新解释) :
主要是对2进制数据进行重新解释(re-interpret),不改变格式, 而static_cast会改变格式进行解释;
reinterpret_cast 只能在指针之间转换。
在上面的代码段中,如果pb指向一个D类型的对象,pd1和pd2是一样的,并且对这两个指针执行D类型的任何操作都是安全的; //向下转换
但是,如果pb指向的是一个B类型的对象,那么pd1将是一个指向该对象的指针,对它进行D类型的操作将是不安全的(如访问m_szName),
而pd2将是一个空指针。
另外要注意:B要有虚函数,否则会编译出错;static_cast则没有这个限制。
这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表(
只有定义了虚函数的类才有虚函数表, 没有定义虚函数的类是没有虚函数表的。
另外,dynamic_cast还支持交叉转换(cross cast)。如下代码所示。
在函数foo中,使用static_cast进行转换是不被允许的,将在编译时出错;而使用 dynamic_cast的转换则是允许的,结果是空指针。
static_cast 和 reinterpret_cast 操作符修改了操作数类型。它们不是互逆的; static_cast 在编译时使用类型信息执行转换,在转换执行必要的检测(诸如指针越界计算, 类型检查). 其操作数相对是安全的。另一方面;reinterpret_cast 仅仅是重新解释了给出的对象的比特模型而没有进行二进制转换, 例子如下:
上面的例子中, 我们将一个变量从 int 转换到 double。 这些类型的二进制表达式是不同的。 要将整数 9 转换到 双精度整数 9,static_cast 需要正确地为双精度整数 d 补足比特位。其结果为 9.0。而reinterpret_cast 的行为却不同:
这次, 结果有所不同. 在进行计算以后, d 包含无用值. 这是因为 reinterpret_cast 仅仅是复制 n 的比特位到 d, 没有进行必要的分析.
因此, 你需要谨慎使用 reinterpret_cast.
复制构造函数 与 复制运算符函数
sizeof
程序的内存空间 5类
栈 : 编译器分配,程序形参, 局部变量
堆 : 程序员分配
全局/静态
常量区 : 字符串常量
char *str1 = "hello";
char *str2 = "hello";
"hello" 在常量区,str1 = str2 指向相同的内存地址
代码区 : 二进制代码
构造函数的执行顺序
virtual 函数多态
一、 外部文件排序: 内存大小为n,总的数据大小为m
a. 每次读入数据n, quick_sort。生成m/n个有序队列, 依次归并
( 算法实现=> 如果存在 X个初始的有序队列, 则需要logx 趟归并)
b. 每次读入数据n, quick_sort。生成m/n个有序队列, 把每个队列的最小节点建立最小堆,从堆中提取数据并不断插入提取数据的下一个节点
( 算法实现=> 主要过程是建堆, 堆的删除)
c. k-win赢者树 (算法实现=>)
d. k-lost败着树 (算法实现=>)
思考: b, c, d 的比较区别 !!!
2 排序算法:
1. 基数排序: a 高位优先 b 低位优先 ( 算法实现=>)
2. 归并排序 : 时间 nlogn 空间(n)
3. 堆排序: swiftdown 函数的实现 ( 递归 和 非递归的实现)
4. 快速排序: 递归的实现 + partition函数实现
void quickSort(vector<int>& arr, int left, int right) { int p = partition(arr, left, right); quickSort(arr, left, p-1); quichSort(arr, p+1, right); }
5. 直接插入 , 折半插入, shell插入, 选择排序
稳定排序: 直接插入,冒泡排序,归并排序
3. 查找算法:
1. 静: 直接查找,折半查找(查找上界, 查找下届),分块查找
2. 二叉搜索树, 二叉平衡树, B-树、B+树 {算法实现=>}
a. 二叉搜索树的查找,插入,删除,(度外存次数太多,不宜处理外存)
b. 平衡(AVL)二叉树: 二叉搜索树 + (左树高度 - 右树高度 = [-1,0,1]): 调整过程
B-树, B+ 主要用于外部查找
c. m介 B-树 (算法实现 b-树的结构,查找, 插入, 删除)
根节点 2, m 个子树
其他节点 m/2 , m个子树; 所有叶节点 在同一层
d. m介 b+树 (算法实现=>)
只有叶节点参与索引,两个跟节点(一个 root, 一个min )
3. 哈喜 : 闭环哈西, 开环哈西
4. 拓扑排序,(判断是否存在环路)
关键路径:
单源最短路径:Dijiksa
定点之间的最段路径:floyed
5 最小生成树
prim:
kruskal:
6. 图搜索
dfs()
bfs() :二分图算法
7. 树:
DLR, LDR, LRD的 递归 非递归
huffman树:
二叉树 与 树 森林的转换
8 . 字符串匹配
朴素:
KMP:
9. 栈与队列的实现(环形队列){push, pop, top, empty()} STL的使用
10. 四种类型转换 static_cast dynamic_cast, const_cast , interpret_cast;
static_cast: 用于数据类型转换,还可以用于继承
①用于类层次结构中基类和子类之间指针或引用的转换。 进行上行转换(把子类的指针或引用转换成基类表示)是安全的; 进行下行转换(把基类指针或引用转换成子类表示)时,由于没有动态类型检查,所以是不安全的。 此处使用dynamic_cast ②用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。 ③把空指针转换成目标类型的空指针。 ④把任何类型的表达式转换成void类型。 static_cast 不能转换掉 expression 的const、volitale、或者__unaligned属性。 |
dynamic_cast : 向下安全转换 :
主要应用于继承体系, 可以由 "指向派生类的基类部分的指针", 转换成 "指向派生类"或 "指向兄弟类";
static_cast只能转换为"指向派生类",只能向下转换
在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的; 在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。 |
主要是对2进制数据进行重新解释(re-interpret),不改变格式, 而static_cast会改变格式进行解释;
reinterpret_cast 只能在指针之间转换。
/*常量性移除指针详解*/ struct S { S() : value(0) {} int value; }; void CastConst (void) { const S s; std::cout << "s.value = " << s.value << std::endl; //S* ps= &s; //error, 指向常量, 不能访问常亮 S* ps = const_cast<S*>(&s); ps->value = 1; // 可以改变值 std::cout << "s.value = " << s.value << std::endl; //S& rs = s; //error, 引用常量 S& rs = const_cast<S&>(s); rs.value = 2; std::cout << "s.value = " << s.value << std::endl; } /*安全向下转型*/ struct B /*基类B*/ { virtual void f() { std::cout << "Base::f" << std::endl; } void thisf() { std::cout << "Base::thisf" << std::endl;} virtual ~B() {} }; struct B2 /*基类B2*/ { virtual void g() { std::cout << "Base2::g" << std::endl; } void thisg() { std::cout << "Base2::thisg" << std::endl;} virtual ~B2() {} }; struct D : public B, public B2 /*派生类D*/ { virtual void f() { std::cout << "Derived::f" << std::endl; } virtual void g() { std::cout << "Derived::g" << std::endl; } virtual ~D() {} }; void CastDynamic (void) { B* pB_D = new D; pB_D->f(); //pB_D->g(); //error, 只包含B部分, 只能访问B的部分 D *pD_D = dynamic_cast<D*>(pB_D); //基类转换为派生类 pD_D->g(); B2* pB2_D = dynamic_cast<B2*>(pB_D); //基类转换为兄弟类 pB2_D->g(); D *pD_Ds = static_cast<D*>(pB_D); //只能把 基类 转换成 派生类 pD_Ds->g(); //B2* pB2_Ds = static_cast<B2*>(pB_D); //error, 不能转换为兄弟类 } /*重新解释转型*/ struct rA { int m_a; }; struct rB { int m_b; }; struct rC : public rB, public rA {}; //可能根据继承的顺序分配地址空间 void CastReinterpret (void) { int *i= new int; *i = 10; std::cout << "*i = " << *i << std::endl; std::cout << "i = " << i << std::endl; double *d=reinterpret_cast<double*> (i); //只能使用指针进行转换 std::cout << "*d = " << *d << std::endl; std::cout << "d = " << d << std::endl; // rC c; std::cout << "&c = " << &c << std::endl << "reinterpret_cast<rB*>(&c) = " <<reinterpret_cast<rB*>(&c) << std::endl << "static_cast <rB*>(&c) = " << static_cast <rB*>(&c) << std::endl << "reinterpret_cast<rA*>(&c) = " <<reinterpret_cast<rA*>(&c) << std::endl << "static_cast <rA*>(&c) = " << static_cast <rA*>(&c) << std::endl << std::endl; } /*输出 &c = 0x7fff54f469f8 reinterpret_cast<rB*>(&c) = 0x7fff54f469f8 static_cast <rB*>(&c) = 0x7fff54f469f8 reinterpret_cast<rA*>(&c) = 0x7fff54f469f8 static_cast <rA*>(&c) = 0x7fff54f469fc //rA数据的地址,指向继承该基类的数据地址, */
class B{ public: int m_iNum; virtual void foo(); }; class D:public B{ public: char *m_szName[100]; }; void func(B *pb){ D *pd1 = static_cast<D *>(pb); D *pd2 = dynamic_cast<D *>(pb); }
在上面的代码段中,如果pb指向一个D类型的对象,pd1和pd2是一样的,并且对这两个指针执行D类型的任何操作都是安全的; //向下转换
但是,如果pb指向的是一个B类型的对象,那么pd1将是一个指向该对象的指针,对它进行D类型的操作将是不安全的(如访问m_szName),
而pd2将是一个空指针。
另外要注意:B要有虚函数,否则会编译出错;static_cast则没有这个限制。
这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表(
只有定义了虚函数的类才有虚函数表, 没有定义虚函数的类是没有虚函数表的。
另外,dynamic_cast还支持交叉转换(cross cast)。如下代码所示。
class A{ public: int m_iNum; virtual void f(){} }; class B:public A{ }; class D:public A{ }; void foo(){ B *pb = new B; pb->m_iNum = 100; D *pd1 = static_cast<D *>(pb); //compile error D *pd2 = dynamic_cast<D *>(pb); //pd2 is NULL delete pb; }
在函数foo中,使用static_cast进行转换是不被允许的,将在编译时出错;而使用 dynamic_cast的转换则是允许的,结果是空指针。
static_cast 和 reinterpret_cast 操作符修改了操作数类型。它们不是互逆的; static_cast 在编译时使用类型信息执行转换,在转换执行必要的检测(诸如指针越界计算, 类型检查). 其操作数相对是安全的。另一方面;reinterpret_cast 仅仅是重新解释了给出的对象的比特模型而没有进行二进制转换, 例子如下:
int n=9; double d=static_cast < double > (n);
上面的例子中, 我们将一个变量从 int 转换到 double。 这些类型的二进制表达式是不同的。 要将整数 9 转换到 双精度整数 9,static_cast 需要正确地为双精度整数 d 补足比特位。其结果为 9.0。而reinterpret_cast 的行为却不同:
int n=9; double d=reinterpret_cast<double & > (n);
这次, 结果有所不同. 在进行计算以后, d 包含无用值. 这是因为 reinterpret_cast 仅仅是复制 n 的比特位到 d, 没有进行必要的分析.
因此, 你需要谨慎使用 reinterpret_cast.
复制构造函数 与 复制运算符函数
//复制构造函数 A& A(const A &a) //1. const不能改变a的值, //2. 使用引用:防止实参赋值给形参时,循环调用 该函数 导致内存崩溃。 //3. 返回对象的引用: return *this. 否则不能连续赋值 s1=s2=s3; { if(this == &a) return; // 4. 要判断是否是同一个 delete []data; //5.要重新分配内存。 data = NULL; data = new char[10]; return *this; } //赋值函数 A& operator = (const A &a) //注意事项相同 { }
sizeof
int getSize(int data[]) { return sizeof(data); } int main() { char str[] = "hello"; char str2[3][10]; char *s = str; int data[] = {1,2,3,4}; int *d = data; sizeof(str) = 6 // + '\0' vs strlen sizeof(str2) = 30 //空间大小 sizeof(s) = 4 //指针 sizeof(data) = 16 // 4*4 sizeof(d) = 4 //指针 getSize(data) = 4 // 数组做行参,传递指针, 或者看代码,与传入数据无关。 }
程序的内存空间 5类
栈 : 编译器分配,程序形参, 局部变量
堆 : 程序员分配
全局/静态
常量区 : 字符串常量
char *str1 = "hello";
char *str2 = "hello";
"hello" 在常量区,str1 = str2 指向相同的内存地址
代码区 : 二进制代码
构造函数的执行顺序
class Base1 { public: Base1(){ cout << "Base1 constructor" << endl; } ~Base1() { cout << "Base1 destructor" << endl; } }; class Base2 { public: Base2(){ cout << "Base2 constructor" << endl; } ~Base2() { cout << "Base2 destructor" << endl; } }; class A1 { public: A1(){ cout << "A1 constructor" << endl; } ~A1(){ cout << "A1 destructor" << endl; } }; class A2 { public: A2(){ cout << "A2 constructor" << endl; } ~A2(){ cout << "A2 destructor" << endl; } }; class Child:public Base2, public Base1 { public: Child(){ cout << "Child constructor" << endl; } A2 a2; A1 a1; ~Child() { cout << "Child destructor" << endl; } }; int main() { Child c; return 0; } =》输出:先执行基类构造函数(按声明的顺序+再执行成员构造函数(按声明的顺序+在执行自己的构造函数 Base2 constructor Base1 constructor A2 constructor A1 constructor Child constructor Child destructor A1 destructor A2 destructor Base1 destructor Base2 destructor
virtual 函数多态
#include <iostream> using namespace std; class Base { public: Base(); virtual ~Base(); virtual void print(); }; Base::Base() { cout << "this is base constructor.\n"; } Base::~Base() { cout << "this is base destroy.\n"; } void Base::print() { cout << "this is base.\n"; } class Child: public Base { public: Child(); ~Child(); void print(); }; Child::Child() { cout << "this is Child consructor.\n"; } Child::~Child() { cout << "this is Child destroy.\n"; } void Child::print() { cout << "this is child.\n"; } class A{ void print(){}; }; int main() { Base *p = new Child(); p->print(); delete p; Base b; p = &b; p->print(); A a; cout << sizeof(a) << '-' << sizeof(A) << endl; } 输出:主要表现的基类的指针
this is base constructor. this is Child consructor. this is child. this is Child destroy. this is base destroy. this is base constructor. this is base. 1-1 this is base destroy.
相关文章推荐
- Lua教程(七):数据结构详解
- 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解
- PHP程序员面试 切忌急功近利(更需要注重以后的发展)
- C#数据结构揭秘一
- 数据结构之Treap详解
- C#常见算法面试题小结
- JavaScript数据结构和算法之图和图算法
- Java数据结构及算法实例:冒泡排序 Bubble Sort
- php 面试碰到过的问题 在此做下记录
- asp.net 面试+笔试题目第1/2页
- Java数据结构及算法实例:插入排序 Insertion Sort
- Java数据结构及算法实例:考拉兹猜想 Collatz Conjecture
- java数据结构之java实现栈
- java数据结构之实现双向链表的示例
- Java数据结构及算法实例:选择排序 Selection Sort
- Java数据结构及算法实例:朴素字符匹配 Brute Force
- Java数据结构及算法实例:汉诺塔问题 Hanoi
- Java数据结构及算法实例:快速计算二进制数中1的个数(Fast Bit Counting)
- java数据结构和算法学习之汉诺塔示例
- Java数据结构及算法实例:三角数字