C-C++面试知识点总结(三)

==> 学习汇总(持续更新)
==> 从零搭建后端基础设施系列(一)-- 背景介绍


目录:

一、基础知识

    1.C/C++

    2.STL

    3.数据结构与算法

    4.计算机网络

    5.操作系统

    6.数据库

二、项目经历


    1.纯属个人YY










3.数据结构与算法基础

(1).基本的线性表的实现

<a href="http://blog.csdn.net/qq_18297675/article/details/54577283>顺序表:用数组实现,因为空间连续,所以叫做顺序表。
<a href="http://blog.csdn.net/qq_18297675/article/details/54605202>链表:用不连续的节点连接成一条链,所以叫做链表。

(2).双链表的实现

<a href="http://blog.csdn.net/qq_18297675/article/details/54647699>双链表:链表中最复杂,但是也是最高效的链表。
双链表因为维护了头指针和尾指针,以及前驱后继指针,所以在添加、插入、删除的时间复杂度都是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[n];
	memset(c, 0, n);
	Merge_Sort(a, 0, n - 1, c);
	delete[] c;
}
  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值