数据结构(面试)

目录

线索二叉树

原理 :利用树节点的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.基于大根堆排序

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

归并排序

排序算法算法复杂度

相关推荐
搬砖的小码农_Sky6 小时前
C语言:数组
c语言·数据结构
Swift社区7 小时前
LeetCode - #139 单词拆分
算法·leetcode·职场和发展
先鱼鲨生8 小时前
数据结构——栈、队列
数据结构
一念之坤8 小时前
零基础学Python之数据结构 -- 01篇
数据结构·python
IT 青年8 小时前
数据结构 (1)基本概念和术语
数据结构·算法
熬夜学编程的小王8 小时前
【初阶数据结构篇】双向链表的实现(赋源码)
数据结构·c++·链表·双向链表
Dong雨8 小时前
力扣hot100-->栈/单调栈
算法·leetcode·职场和发展
liujjjiyun9 小时前
小R的随机播放顺序
数据结构·c++·算法
周三有雨9 小时前
【面试题系列Vue07】Vuex是什么?使用Vuex的好处有哪些?
前端·vue.js·面试·typescript
爱米的前端小笔记9 小时前
前端八股自学笔记分享—页面布局(二)
前端·笔记·学习·面试·求职招聘