数据结构(面试)

目录

线索二叉树

原理 :利用树节点的n+1个左右空指针指向其遍历序列的前驱和后继(线索)

优点:简化遍历,不需要额外的栈空间,快速访问前驱的后继

哈夫曼树

哈夫曼树定义 :在含有n个带权叶节点的二叉树中,其中带权路径(WPL)最小的二叉树称为哈夫曼树,也称最优二叉树
带权路径长度 :叶子节点×路径长度 的总和
构建方法 :每次选取根节点最小的集合进行两两组合

并查集

用法:判断图中是否有环,判断是否在一个集合中

思想:使用一个parent[]数组存储集合关系,对集合进行并查操作
:找x所属集合(返回x所属根节点)
:将两个集合合并为一个

为了优化,出现最坏的情况,在合并集合的时候可以按秩合并

cpp 复制代码
if(rank[x_root]>rank[y_root]){//将x作为根节点合并
		parent[y_root]=x_root;
}else{//将y作为根节点合并 
	rank[x_root]=y_root;
	if(rank[x_root]==rank[y_root]){//当秩相等时 
		rank[y_root]++;
	} 
} 

进一步优化,路径压缩,查找过程中将一个集合路径下的所有节点都挂到集合根节点下面

cpp 复制代码
int find(int x){//找根节点
	if(x==parent[x])return x;//返回根节点 
	return parent[x]=find(parent[x]);//路径压缩 
} 

并查集模板代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int parent[10005],rank[10005];
//找根节点 
int find(int x){
	if(x==parent[x])return x;//返回根节点 
	return parent[x]=find(parent[x]);//路径压缩 
}
//集合合并
void unionset(int x,int y){
	int x_root=find(x);
	int y_root=find(y);
	if(x_root==y_root)return;//在同一个集合中
	//按秩合并 
	if(rank[x_root]>rank[y_root]){
		parent[y_root]=x_root;	
	}
	else{
		parent[x_root]=y_root;
		if(rank[x_root]=rank[y_root]){
			rank[y_root]++;
		}
	}
}
//打印关系函数
void selectdots(){
	for(int i=1;i<=5;i++){
		printf("%d的根节点=%d\n",i,parent[i]);
	}
}

int main(){
	//初始化 
	for(int i=0;i<100;i++){
		parent[i]=i;
		rank[i]=0;
	}
	//初始化两个集合 A{1 ←2 ←3}  B{4 ←5} ;
	int con[3][2]={{2,1},{3,2},{5,4}};
	for(int i=0;i<3;i++){
		unionset(con[i][0],con[i][1]);//建立集合 
	} 
	printf("A{1 ←2 ←3}  B{4 ←5}两个集合没有合并:\n"); 
	selectdots();
	printf("2和4点是否有关系:");
	if(find(2)==find(4)){
		cout<<true<<endl;;
	}else{
		cout<<false<<endl;
	}
	
	printf("\n\n增加3 ←5关系:\n");
	unionset(5,3);
	selectdots();
	printf("2和4点是否有关系:");
	if(find(2)==find(4)){
		cout<<true<<endl;;
	}else{
		cout<<false<<endl;
	}
	
	printf("\n\n查询一次5的根节点:\n");
	find(5);
	selectdots(); 
	
	return 0;
}

最小生成树

生成树 :包含图中全部顶点的一个极小联通子图

最小生成树 :边权值之和最小的生成树

普利姆算法(选点,适合边稠密):

克鲁斯卡尔(选边,适合边稀疏):

最短路径


Dijkstra算法(带权图,无权图,不适用有负权值的图)

时间复杂度:O(|V|^2)
思想:

1.每次从未标记节点中选择距离出发点最近的节点,标记,收录到最优路径集合中。

2.计算刚加入节点A的邻近节点B的距离(不包含标记的节点),若(节点A的距离+节点A到节点B的边长)<节点B的距离,就进行松弛操作,更新节点B的距离

cpp 复制代码
if((dis[A]+e[A][B])<dis[B]) dis[B] = dis[A] + e[A][B];

Floyd算法(带权图,无权图,负权图,不能解决负权回路的图)

时间复杂度:O(|V|^3)
算法思想 :最开始允许经过1号中转,求任意两点最短距离中转,接下来只允许经过2号顶点中转...允许经过1~n号顶点中转

一句话概括:从i号顶点到j号顶点只经过前k号顶点的最短路径

cpp 复制代码
for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
		    for(int j=1;j<=n;j++){
		    	if(e[i][k]+e[k][j]<e[i][j]){
			    	e[i][j]=e[i][k]+e[k][j];
		    	}
	    	}
    	}
	} 

拓扑排序

思想,每次选择入度为0的顶点,加入排序序列,并删除所有出边

下面拓扑排序为:1,2,3,4,5

二叉排序树

定义:左子树节点值<根节点<右子树节点值

查找效率:最好O(logn) 最坏O(n)

平衡二叉树

定义:二叉排序树上任一节点的左子树和右子树的高度之差不超过1
缺点 :插入/删除 很容易破坏"平衡"特性,需要频繁调整树的形态。如:插入操作导致不平衡,则需要先计算平衡因子,找到最小不平衡子树(时间开销大),再进行LL/RR/LR/RL调整

红黑树



平衡二叉树:适用于以查为主 ,很少插入/删除的场景
红黑树:适用于频繁插入、删除的场景,实用性更强

折半查找

折半查找时间复杂度:O(log2n)

又称二分查找,仅适用于有序的顺序表,链表不具备随机访问特性,不能使用二分查找

大部分情况下折半查找更优,但是有的情况顺序查找更优(比如待查找元素在第一个位置)
思想:

1.使用双指针lowhigh分别指向有序表头和尾

2.计算 mid = (low+high)/2 将有序表一分为二,判断mid位置元素和待查找元素temp的大小关系,通过移动lowhigh保留temp可能的区间

3.重复第二步,直到low=high且此时指针指向的值等于temp查找成功(当low>high查找失败)

散列表

定义 :一直数据结构,特点是数据元素的关键字与其存储地址直接相关,通过散列函数(哈希函数):Addr=H(key)建立关键字与存储地址的联系

散列查找是一种空间换时间的方法

增删改时间复杂度:O(1)

散列函数:




处理冲突的方法:


堆排序

空间复杂度:O(1)

时间复杂度:O(nlogn)

物理结构:使用顺序存储
逻辑 结构:大根堆根节点大于左子树和右子树,小根堆根节点小于左子树和右子树

1.建立大根堆:

**思路:**把所有非终端节点都检查一遍,是否满足大根堆的要求,如果不满足,则进行调整
调整方式 :检查当前节点是否满足 根>=左、右 若不满足,将当前节点与更大的一个孩子互换,若元素互换破坏了下一级的堆,则采用相同的方法继续向下调整(小元素不断"下坠")

2.基于大根堆排序

方法:每一趟将堆顶元素加入有序子序列(与待排序序列中的最后一个元素交换),并将待排序元素序列再次调整为大根堆(小元素不断下坠)

归并排序

排序算法算法复杂度

相关推荐
wheeldown37 分钟前
【数据结构】选择排序
数据结构·算法·排序算法
鱼跃鹰飞3 小时前
大厂面试真题-简单说说线程池接到新任务之后的操作流程
java·jvm·面试
躺不平的理查德5 小时前
数据结构-链表【chapter1】【c语言版】
c语言·开发语言·数据结构·链表·visual studio
阿洵Rain5 小时前
【C++】哈希
数据结构·c++·算法·list·哈希算法
Leo.yuan5 小时前
39页PDF | 华为数据架构建设交流材料(限免下载)
数据结构·华为
半夜不咋不困5 小时前
单链表OJ题(3):合并两个有序链表、链表分割、链表的回文结构
数据结构·链表
钱钱钱端6 小时前
【压力测试】如何确定系统最大并发用户数?
自动化测试·软件测试·python·职场和发展·压力测试·postman
程序员清风6 小时前
浅析Web实时通信技术!
java·后端·面试
忘梓.6 小时前
排序的秘密(1)——排序简介以及插入排序
数据结构·c++·算法·排序算法
测试19987 小时前
外包干了2年,快要废了。。。
自动化测试·软件测试·python·面试·职场和发展·单元测试·压力测试