C-C++面试知识点总结(三)
2017-08-14 22:53
465 查看
目录:
一、基础知识
1.C/C++
2.STL
3.数据结构与算法
4.计算机网络
5.操作系统
6.数据库
二、项目经历
1.纯属个人YY
3.数据结构与算法基础
(1).基本的线性表的实现
顺序表:用数组实现,因为空间连续,所以叫做顺序表。
链表:用不连续的节点连接成一条链,所以叫做链表。
(2).双链表的实现
双链表:链表中最复杂,但是也是最高效的链表。
双链表因为维护了头指针和尾指针,以及前驱后继指针,所以在添加、插入、删除的时间复杂度都是O(1)。
(3).会用递归
递归:其实递归理解起来并不难,无疑就是自己调用自己,这是一个函数调用的特例,唯一难的是不懂如何活学活用。
当一个问题能划分为N个子问题去解决,那么就考虑用递归。你不需要去纠结其中的细节(当初我就是钻进去了,一直想它为什么会这样,其实用递归只需要它能这样就够了)
(4).字符串匹配
常用的就是BF(暴力算法)、KMP和BM算法。BF就是循环一个个匹配,这会进行很多次重复的比较。而KMP利用一个next数组来指导匹配,尽可能的将比较效率提高。然而经过测试,我发现当数据比较随机的时候(重复率低),BF的速度要高于KMP。所以KMP并不稳定。BM算法就很好的解决KMP的问题,所以BM算法的效率很高,其主要思想是每次后移的位数选择“坏字符规则”和“好后缀规则”两个的较大值。
KMP和BM算法对比参考http://blog.csdn.net/v_JULY_v/article/details/6545192这篇文章
(5).完全二叉树
这是满二叉树的子集,即满二叉树是完全二叉树。
满足的特点为:
1)只允许最后一层有空缺结点且空缺在右边,即叶子结点只能在层次最大的两层上出现;
2)对任一结点,如果其右子树的深度为j,则其左子树的深度必为j或j+1。 即度为1的点只有1个或0个
应用举例:堆排序中所用的数据结构就是完全二叉树。
(6).二叉查找树
这是一颗排序的树,里面的数据按照中序遍历就能得到从小到大的顺序排列。(一般而言,都假设左<根<右)
规则:
对于每一颗子树都有,左节点的值 < 根节点的值 < 右节点的值
没有键值相等的节点(可以加个count计数就行)
(7).平衡二叉树
平衡二叉树实现的方法也有很多种,例如红黑树、AVL、替罪羊树、Treap、伸展树等。
(8).B树
英文B-Tree,所以B-树和B树是等价的。为什么要需要B树这种数据结构?因为当数据量大于内存的时候,需要将数据存放到硬盘上,然后再进行数据的检索。若二叉树的深度过大,造成磁盘IO读写次数过大,效率就会低下。B树与红黑树最大的不同在于,B树的结点可以有许多子女,从几个到几千个。那为什么又说B树与红黑树很相似呢?因为与红黑树一样,一棵含n个结点的B树的高度也为O(lgn),但可能比一棵红黑树的高度小许多,应为它的分支因子比较大。所以,B树可以在O(logn)时间内,实现各种如插入(insert),删除(delete)等动态集合操作。
参考这篇文章http://blog.csdn.net/v_july_v/article/details/6530142
(9).B+树
这是一种应文件系统所需而出的一种B树变形树。严格意义来讲它已经不是树了,因为叶子结点都已经相连起来了。
B+树是B树的一种变形,它把所有的数据都存储在叶节点中,内部节点只存放关键字和孩子指针,因此最大化了内部节点的分支因子,所以B+树的遍历也更加高效(B树需要以中序的方式遍历节点,而B+树只需把所有叶子节点串成链表就可以从头到尾遍历)。B+树特别适合带有范围的查找,因为只需要查找到第一个符合的关键字,然后再在叶子结点按顺序查找到符合范围的所有记录即可。
(10).B*树
B*树是B+树的变体,在B+树的非根和非叶子结点再增加指向兄弟的指针,将结点的最低利用率从1/2提高到2/3。
(11).DFS深度优先搜索
图的一种搜索算法,但是其可以用在树中。它的思想是,一路走到黑再回头再走到黑。直到所有的节点都被访问过之后结束。从思想上可以看出,必须要一个标志数组来标记这个节点已经被访问过了。
官方的遍历方法定义:
优点:可以快速判断是否有解。
缺点:找最优解的实现比BFS复杂很多
(12).BFS广度优先搜索
故名思意是横向搜索的,所以肯定可以保存其路径信息。其思想是,从某一个节点出发,像周围扩散(上下左右的去遍历),直到所有的节点都遍历完为止。
官方的遍历方法定义:
优点:可以轻松找到最优解。
缺点:需要遍历完整张图。
DFS和BFS比较:一般说来,能用DFS解决的问题都能用BFS解决。DFS通过递归实现,易于实现,DFS的常数时间开销会比较少,所以大多数情况下优先考虑DFS实现。
(13).二分搜索
这个要求要查找的序列是有序的,一开始先定位到中间位置,然后依次比较,以两倍的速度缩小范围,直到left>=right为止,其时间复杂度为O(logN)。
例子:
(14).分治法
分治法的设计思想是:将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
使用分治法的特征:
具体的参考这篇文章:
https://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741370.html
(15).动态规划算法
参考这篇文章http://www.hawstein.com/posts/dp-novice-to-advanced.html
(16).回溯法
参考这篇文章http://blog.csdn.net/daniel_ustc/article/details/17040315
(17).双指针
参考这篇文章http://www.cnblogs.com/byrhuangqiang/p/4708608.html
(18).扫描线
参考这篇文章http://blog.csdn.net/orbit/article/details/7368996
(19).字典树(Trie树)
一种用来词频统计、前缀匹配的树。
参考文章
https://github.com/julycoding/The-Art-Of-Programming-By-July/blob/master/ebook/zh/06.09.md
(20).冒泡排序
(21).选择排序
(22).插入排序
(23).快速排序
(24).归并排序
(25).堆排序
一、基础知识
1.C/C++
2.STL
3.数据结构与算法
4.计算机网络
5.操作系统
6.数据库
二、项目经历
1.纯属个人YY
3.数据结构与算法基础
(1).基本的线性表的实现
顺序表:用数组实现,因为空间连续,所以叫做顺序表。
链表:用不连续的节点连接成一条链,所以叫做链表。
(2).双链表的实现
双链表:链表中最复杂,但是也是最高效的链表。
双链表因为维护了头指针和尾指针,以及前驱后继指针,所以在添加、插入、删除的时间复杂度都是O(1)。
(3).会用递归
递归:其实递归理解起来并不难,无疑就是自己调用自己,这是一个函数调用的特例,唯一难的是不懂如何活学活用。
当一个问题能划分为N个子问题去解决,那么就考虑用递归。你不需要去纠结其中的细节(当初我就是钻进去了,一直想它为什么会这样,其实用递归只需要它能这样就够了)
(4).字符串匹配
常用的就是BF(暴力算法)、KMP和BM算法。BF就是循环一个个匹配,这会进行很多次重复的比较。而KMP利用一个next数组来指导匹配,尽可能的将比较效率提高。然而经过测试,我发现当数据比较随机的时候(重复率低),BF的速度要高于KMP。所以KMP并不稳定。BM算法就很好的解决KMP的问题,所以BM算法的效率很高,其主要思想是每次后移的位数选择“坏字符规则”和“好后缀规则”两个的较大值。
KMP和BM算法对比参考http://blog.csdn.net/v_JULY_v/article/details/6545192这篇文章
(5).完全二叉树
这是满二叉树的子集,即满二叉树是完全二叉树。
满足的特点为:
1)只允许最后一层有空缺结点且空缺在右边,即叶子结点只能在层次最大的两层上出现;
2)对任一结点,如果其右子树的深度为j,则其左子树的深度必为j或j+1。 即度为1的点只有1个或0个
应用举例:堆排序中所用的数据结构就是完全二叉树。
(6).二叉查找树
这是一颗排序的树,里面的数据按照中序遍历就能得到从小到大的顺序排列。(一般而言,都假设左<根<右)
规则:
对于每一颗子树都有,左节点的值 < 根节点的值 < 右节点的值
没有键值相等的节点(可以加个count计数就行)
(7).平衡二叉树
平衡二叉树实现的方法也有很多种,例如红黑树、AVL、替罪羊树、Treap、伸展树等。
1)其中AVL是最早发明自平衡二叉查找树算法,它是一颗高度平衡的二叉树,在AVL中任何节点的两个子树的高度最大差为一。即|Height(left) - Height(right)| < 2 。 其核心是旋转树,分为单左旋转、单右旋转、左右旋转、右左旋转,目的就是让旋转后的树的任意|Height(left) - Height(right)| < 2 ,使其高度平衡。n个结点的AVL树最大深度约1.44log2n。 2)红黑树(RB-Tree)的实现就比较复杂,其核心就是保持从根节点到叶节点的路径上,黑节点的个数相等,这样就能保持树的比较平衡。其特点如下: a.每个结点要么是红的,要么是黑的。 b.根结点是黑的。 c.每个叶结点(叶结点即指树尾端NIL指针或NULL结点)是黑的。 d.如果一个结点是红的,那么它的俩个儿子都是黑的。 e.对于任一结点而言,其到叶结点树尾端NIL指针的每一条路径都包含相同数目的黑结点。 3)AVL树和RB树的效率比较 如果说单论查找效率的话,AVL树绝对是优于RB树的,但是实际应用上,你不仅仅只有查找而没有添加、插入、删除等操作,在添加操作的时候,AVL树最坏的情况需要从插入的那个位置开始递归的向上旋转树,且因为需要高度的平衡,所以旋转操作必定会很多。而RB树只需要少量的旋转,以及修改节点颜色即可,从这点看,当数据量大的时候,明显RB树的统计性能比AVL好,这也是为什么STL关联式容器都选用RB树作为底层实现。
(8).B树
英文B-Tree,所以B-树和B树是等价的。为什么要需要B树这种数据结构?因为当数据量大于内存的时候,需要将数据存放到硬盘上,然后再进行数据的检索。若二叉树的深度过大,造成磁盘IO读写次数过大,效率就会低下。B树与红黑树最大的不同在于,B树的结点可以有许多子女,从几个到几千个。那为什么又说B树与红黑树很相似呢?因为与红黑树一样,一棵含n个结点的B树的高度也为O(lgn),但可能比一棵红黑树的高度小许多,应为它的分支因子比较大。所以,B树可以在O(logn)时间内,实现各种如插入(insert),删除(delete)等动态集合操作。
参考这篇文章http://blog.csdn.net/v_july_v/article/details/6530142
(9).B+树
这是一种应文件系统所需而出的一种B树变形树。严格意义来讲它已经不是树了,因为叶子结点都已经相连起来了。
B+树是B树的一种变形,它把所有的数据都存储在叶节点中,内部节点只存放关键字和孩子指针,因此最大化了内部节点的分支因子,所以B+树的遍历也更加高效(B树需要以中序的方式遍历节点,而B+树只需把所有叶子节点串成链表就可以从头到尾遍历)。B+树特别适合带有范围的查找,因为只需要查找到第一个符合的关键字,然后再在叶子结点按顺序查找到符合范围的所有记录即可。
(10).B*树
B*树是B+树的变体,在B+树的非根和非叶子结点再增加指向兄弟的指针,将结点的最低利用率从1/2提高到2/3。
(11).DFS深度优先搜索
图的一种搜索算法,但是其可以用在树中。它的思想是,一路走到黑再回头再走到黑。直到所有的节点都被访问过之后结束。从思想上可以看出,必须要一个标志数组来标记这个节点已经被访问过了。
官方的遍历方法定义:
(1)访问顶点v; (2)依次从v的未被访问的邻接点出发,对图进行深度优先遍历;直至图中和v有路径相通的顶点都被访问; (3)若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到图中所有顶点均被访问过为止。
优点:可以快速判断是否有解。
缺点:找最优解的实现比BFS复杂很多
(12).BFS广度优先搜索
故名思意是横向搜索的,所以肯定可以保存其路径信息。其思想是,从某一个节点出发,像周围扩散(上下左右的去遍历),直到所有的节点都遍历完为止。
官方的遍历方法定义:
a.首先将根节点放入队列中。 b.从队列中取出第一个节点,并检验它是否为目标。 b.a.如果找到目标,则结束搜寻并回传结果。 b.b.否则将它所有尚未检验过的直接子节点加入队列中。 c.若队列为空,表示整张图都检查过了——亦即图中没有欲搜寻的目标。结束搜寻并回传“找不到目标”。 d.重复步骤2。
优点:可以轻松找到最优解。
缺点:需要遍历完整张图。
DFS和BFS比较:一般说来,能用DFS解决的问题都能用BFS解决。DFS通过递归实现,易于实现,DFS的常数时间开销会比较少,所以大多数情况下优先考虑DFS实现。
(13).二分搜索
这个要求要查找的序列是有序的,一开始先定位到中间位置,然后依次比较,以两倍的速度缩小范围,直到left>=right为止,其时间复杂度为O(logN)。
例子:
int binary_search(int* a,int n,int value) { int left = 1, right = n,mid = 0; while (left < right) { mid = (left + right) / 2; if (a[mid] == value) return mid; else if (a[mid] < value) left = mid; else right = mid; } return -1; }
(14).分治法
分治法的设计思想是:将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
使用分治法的特征:
1) 该问题的规模缩小到一定的程度就可以容易地解决 2) 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。 3) 利用该问题分解出的子问题的解可以合并为该问题的解; 4) 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。
具体的参考这篇文章:
https://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741370.html
(15).动态规划算法
参考这篇文章http://www.hawstein.com/posts/dp-novice-to-advanced.html
(16).回溯法
参考这篇文章http://blog.csdn.net/daniel_ustc/article/details/17040315
(17).双指针
就是两个用指针搞事情。 应用场景: a.用来判断链表是否有环以及寻找环入口 b.数组寻找范围 c.链表或者数组中移除重复的元素 d.用来找中点或中位数 e.倒数第n个 f.拆分链表
参考这篇文章http://www.cnblogs.com/byrhuangqiang/p/4708608.html
(18).扫描线
参考这篇文章http://blog.csdn.net/orbit/article/details/7368996
(19).字典树(Trie树)
一种用来词频统计、前缀匹配的树。
基本性质为: 1)根节点不包含字符,除根节点外每一个节点都只包含一个字符。 2)从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。 3)每个节点的所有子节点包含的字符都不相同。
参考文章
https://github.com/julycoding/The-Art-Of-Programming-By-July/blob/master/ebook/zh/06.09.md
(20).冒泡排序
/**************************************** * function 冒泡排序 * * param a[] 待排序的数组 * * param n 数组长度 * * return 无 * * good time O(n) * * avg time O(n^2) * * bad time O(n^2) * * space O(1) * * stable yes * *****************************************/ void BubbleSort(int a[],int n) { for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (a[i] < a[j]) swap(a[i], a[j]); } } }
(21).选择排序
/**************************************** * function 选择排序法 * * param a[] 待排序的数组 * * param n 数组长度 * * return 无 * * good time O(n^2) * * avg time O(n^2) * * bad time O(n^2) * * space O(1) * * stable no * *****************************************/ void SelectSort(int a[], int n) { int min = 0; for (int i = 0; i < n; i++) { min = i; for (int j = i + 1; j < n; j++) { if (a[j] < a[min]) min = j; } if (min != i) swap(a[min], a[i]); } }
(22).插入排序
/**************************************** * function 插入排序法 * * param a[] 待排序的数组 * * param n 数组长度 * * return 无 * * good time O(n) * * avg time O(n^2) * * bad time O(n^2) * * space O(1) * * stable yes * *****************************************/ void InsertSort(int a[], int n) { int temp = 0; for (int i = 1,j = 1; i < n; i++) { j = i; temp = a[i]; //待插入的数,假设第一个已经排好序 while (j > 0 && temp < a[j - 1])a[j] = a[j - 1],--j; //向前逐个比较,若该位置插入不了,则往后移 a[j] = temp; //插入 } }
(23).快速排序
/**************************************** * function 快速排序法--分区 * * param a[] 待分区的数组 * * param left 区间左 * * param right 区间右 * * return 返回新的基准keyindex * *****************************************/ int Partition(int a[], int left, int right) { int midIndex = left; while (left < right) { //从右往左扫描,若找到比基准key小的数,则与a[midIndex]交换 while (left < right && a[right] >= a[midIndex]) --right; if (left < right) { swap(a[right], a[midIndex]); midIndex = right; } //从左往右扫描,若找到比基准key大的数,则与a[midIndex]交换 while (left < right && a[left] <= a[midIndex]) ++left; if (left < right) { swap(a[left], a[midIndex]); midIndex = left; } } Print(a, 10); return midIndex; } /**************************************** * function 快速排序法 * * param a[] 待分区的数组 * * param left 区间左 * * param right 区间右 * * return 无 * * good time O(N*logN) * * avg time O(N*logN) * * bad time O(N^2) * * space O(NlogN) * * stable no * *****************************************/ void Quick_Sort(int a[], int left, int right) { if (left < right) { //1.随机取基准值,然后交换到left那里 // srand(GetTickCount()); // int m = (rand() % (right - left)) + left; //2.取前中后的中值,然后交换到left那里 // int m = Mid(left, (left + right / 2), right); // swap(a[m], a[left]); int midIndex = Partition(a, left, right); //获取新的基准keyindex Quick_Sort(a, left, midIndex - 1); //左半部分排序 Quick_Sort(a, midIndex + 1, right); //右半部分排序 } } void QuickSort(int a[], int n) { Quick_Sort(a, 0, n - 1); }
(24).归并排序
/**************************************** * function 堆调整(大顶堆) * * param a[] 待调整的数组 * * param n 数组 大小 * * return 无 * *****************************************/ void HeapAdjust(int a[],int i,int n) { int left = 2 * i + 1; //左孩子 int right = 2 * (i + 1); //右孩子 int maxIndex = 0; //如果左孩子是叶子节点,并且该节点比父节点大,则交换 maxIndex = (left < n && a[left] > a[i]) ? left : i; maxIndex = (right < n && a[right] > a[maxIndex]) ? right : maxIndex; //如果maxIndex != i,则说明要进行交换 if (maxIndex != i) { swap(a[i],a[maxIndex]); //继续递归调整,直到小的都沉到下面去 HeapAdjust(a, maxIndex, n); } } /**************************************** * function 建堆(大顶堆) * * param a[] 数组 * * param n 数组 大小 * * return 无 * *****************************************/ void MakeHeap(int a[],int n) { //建大顶堆 for (int i = n / 2 - 1; i >= 0; i--) HeapAdjust(a, i, n); } /**************************************** * function 堆排序法 * * param a[] 待排序的数组 * * return 无 * * good time O(NlogN) * * avg time O(NlogN) * * bad time O(NlogN) * * space O(NlogN) * * stable no * *****************************************/ void HeapSort(int a[], int n) { //排序 for (int i = n - 1;i > 0;i--) { swap(a[i], a[0]); //依次交换根节点和最后一个节点 HeapAdjust(a, 0, i); //然后调整剩余的节点 } }
(25).堆排序
/**************************************** * function 合并两个有序序列 * * param a[] 待合并的序列 * * param start 序列1的起始位置 * * param mid 分界线 * * param end 序列2的终点位置 * * param c[] 辅助数组 * return 无 * *****************************************/ void MergeSeqs(int a[],int start,int mid,int end, int c[]) { //相当于把一个数组一分为二,a[start...mid-1],a[mid...end]; int i = start, j = mid + 1, n = mid, m = end,k = 0; while (i <= n && j <= m) { if (a[i] < a[j]) c[k++] = a[i++]; else c[k++] = a[j++]; } while (i <= n) c[k++] = a[i++]; while (j <= m) c[k++] = a[j++]; for (i = 0;i < k;i++) a[i + start] = c[i]; //将排好序的数组重新赋值到原数组中 } /**************************************** * function 归并排序 * * param a[] 待排序的序列 * * param start 序列的起始位置 * * param end 序列的终点位置 * * param c[] 辅助数组 * return 无 * *****************************************/ void Merge_Sort(int a[], int start, int end,int c[]) { if (start < end) { int mid = (start + end) / 2; Merge_Sort(a, start, mid, c); Merge_Sort(a, mid + 1, end, c); MergeSeqs(a, start, mid, end, c); } } /**************************************** * function 归并排序 * * param a[] 待排序的序列 * * param start 序列的起始位置 * * param end 序列的终点位置 * * return 无 * * good time O(NlogN) * * avg time O(NlogN) * * bad time O(NlogN) * * space O(N) * * stable yes * *****************************************/ void MergeSort(int a[], int n) { int* c = new int ; memset(c, 0, n); Merge_Sort(a, 0, n - 1, c); delete[] c; }
相关文章推荐
- 面试知识点总结之c++基础(一)
- C++ 面试知识点总结
- 面试知识点总结之c++基础(二)
- C++及数据结构笔试面试常见知识点总结
- C/C++面试知识点总结(一)
- C++面试知识点总结
- C++ 面试知识点总结
- C++及数据结构笔试面试常见知识点总结
- C++笔试面试真题回顾与知识点总结
- 最近看的一些关于数据结构和C++的面试知识点总结
- C++面试知识点总结
- C/C++面试知识点总结(二)
- C/C++面试知识点总结
- C++知识点总结(1)
- C++面试宝典 知识点集锦
- java面试知识点总结
- C++面试常见问题总结
- 后端c++知识点总结
- 那些不能遗忘的知识点回顾——C/C++系列(笔试面试高频题)
- c++学习总结(一些零碎的小知识点)