数据结构(面试)

线索二叉树

原理:利用树节点的n+1个左右空指针指向其遍历序列的前驱和后继(线索)
优点:简化遍历,不需要额外的栈空间,快速访问前驱的后继
在这里插入图片描述

哈夫曼树

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

并查集

用法:判断图中是否有环,判断是否在一个集合中
思想:使用一个parent[]数组存储集合关系,对集合进行并查操作
:找x所属集合(返回x所属根节点)
:将两个集合合并为一个
在这里插入图片描述
在这里插入图片描述
为了优化,出现最坏的情况,在合并集合的时候可以按秩合并

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]++;
	} 
} 

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

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

并查集模板代码:

#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的距离

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号顶点的最短路径

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.基于大根堆排序
方法:每一趟将堆顶元素加入有序子序列(与待排序序列中的最后一个元素交换),并将待排序元素序列再次调整为大根堆(小元素不断下坠)

归并排序

在这里插入图片描述

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值