您的位置:首页 > 职场人生

面试的基础算法和知识点

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函数实现

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属性。
const_cast: 主要对变量的常量性(const)或volatile属性进行操作, 移除变量的常量性, 即可以被非常量指向和引用, 详见代码; 以便对常量数据进行修改。

dynamic_cast : 向下安全转换 :

主要应用于继承体系, 可以由 "指向派生类的基类部分的指针", 转换成 "指向派生类"或 "指向兄弟类";
static_cast只能转换为"指向派生类",只能向下转换
在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;
在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
reinterpret_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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  面试 数据结构