首页 > 编程语言 >基本数据结构——算法学习(三)上

基本数据结构——算法学习(三)上

时间:2024-12-26 13:41:15浏览次数:3  
标签:链表 结点 NIL 元素 学习 算法 二叉树 数据结构 指针

数据结构——算法学习(三)上


前言

数据结构是计算机科学的基石,几乎所有的软件开发、算法设计都离不开对数据的组织与管理。它不仅是程序高效运行的保障,也是解决复杂问题的关键工具。学习数据结构的过程,不仅仅是掌握具体的知识点,更是培养逻辑思维能力和问题解决能力的重要途径。

在本章中,我们要讨论如何通过使用指针的简单数据结构来表示动态集合。虽然运用指针可以构造多种复杂的数据结构,但这里只介绍几种基本的结构:栈、队列、链表和有根树。

有限动态集合

概念

可以进行增大、缩小或其他变化的集合,集合中的每个元素都由一个对象表示

常见的动态集合操作

SEARCH(S,k):返回在集合S中指向关键字k的指针,没有则返回NIL

NSERT(S,x):将指针x指向的元素加入到集合S中

DELETE(S,x):将指针x指向的元素从集合S中删除

MINIMUM(S):返回指向全序集S中具有最小关键字元素的指针

MAXIMUM(S):返回指向全序集S中具有最大关键字元素的指针

SUCCESSOR(S,x):返回全序集S中比元素x大的下一个元素的指针,没有则返回NIL

PREDECESSOR(S,x):返回全序集S中比元素x小的前一个元素的指针,没有则返回NIL

线性数据结构

线性数据结构是指数据元素按照线性顺序排列,每个元素仅有一个直接前驱和一个直接后继,常见形式包括数组、链表、栈和队列,广泛应用于数据存储与算法设计中,因其简单直观的特性而成为计算机科学的重要基础。

栈(Stack)实现一种在进行DELETE操作时后进先出(last-in, first-out, LIFO)的策略

栈上的INSERT操作称为压入PUSH,DELETE操作称为弹出POP

对空栈的弹出操作称为栈下溢,若S.top超过了n则称为栈上溢,检查栈空的查询操作STACK_EMPTY

实现栈的空判断、push、pop的伪代码( 这三种栈操作的执行时间都为O(1) )

Function stack-empty(S):
	if S.top = 0 then return true;
	else return false;
end
Function push(S,x):
	if S.top = S.capactity then error "overflow";
	S.top <- S.top + 1;
	S[S.top] <- x;
end
Function pop(S):
	if stack-empty(S) then error "underflow";
	else
		S.top <- S.top - 1;
		return S[S.top+1];
	end
end

队列

队列实现一种在进行DELETE操作时先进先出的策略

队列上的INSERT操作称为入队(ENQUEUE),DELETE操作称为出队(DEQUEUE)

队列有队头(head)和队尾(tail)

实现队列ENQUEUE、DEQUEUE操作伪代码

Function enqueue(Q,x):
	if S.head = S.tail then error "overflow";
	Q[Q.tail] <- x;
	if Q.tail = Q.capacity then Q.tail <- 1;
	else Q.tail <- Q.tail + 1;
end
Function dequeue(Q):
	if S.head = S.tail then error "underflow";
	x <- Q[Q.head];
	if Q.head = Q.capacity then Q.head <- 1;
	else Q.head <- Q.head + 1;
	return x;
end

链表

链表是一种元素按线性顺序排列的数据结构,元素的顺序由元素内部的指针决定

链表的几种形式:

  • 单链接链表中元素没有prev指针,双链接链表不省略
  • 已排序链表中元素的线性顺序与关键字线性顺序一致,未排序链表元素可按任意顺序出现
  • 循环链表中表头元素的prev指针指向表尾元素,表尾元素的next指针指向表头元素

无哨兵的链表操作实现伪代码:

Function list-search(L,k):
	x <- L.head;
	while x != NIL and x.key != k do x <- x.next;
	return x;
end
Function list-insert(L,x):
	x.next <- L.head;
	if L.head !=NIL then L.head.prev <- x;
	L.head <- x;
	x.prev <- NIL;
end
Function list-delete(L,x):
	if x.prev != NIL then x.prev.next <- x.next;
	else L.head <- x.next;
	if x.next != NIL then x.next.next <- x.prev;
end

带哨兵的链表操作实现伪代码:

Function list-search(L,k):
	x <- L.nil.next;
	while x != L.nil and x.key != k do x <- x.next;
	return x;
end
Function list-insert(L,x):
	x.next <- L.nil.next;
	L.nil.next.prev <- x;
	L.nil.next <- x;
	x.prev <- L.nil;
end
Function list-delete(L,x):
	x.prev.next <- x.next;
	x.next.prev <- x.prev;
end

图和树

有向图G是一个二元组(V,E),其中V是有限集,E是V上的二元关系。集合V称为图G的顶点集,其元素称为顶点。集合E是G的边集,其元素称为边。图中可能存在自环

有向图G = (V,E),其中V = {1,2,3,4,5,6},E = {(1,2),(2,2),(2,4),(2,5),(4,1),(4,5),(5,4),(6,3)}

在无向图G = (V,E)中,边集E由无序的顶点对组成,其中V = {1,2,3,4,5,6},E={(1,2),(1,5),(2,5),(3,6)}

概念

一、自由树

自由树是一个连通的、无环的无向图,不连通的无环无向图称为森林,对于自由树G = (V,E),有|E| = |V|-1


二、有根树

有根树是一棵自由树,存在一个顶点作为树的根。

从有根树T中的一个结点x到根r的简单路径上的任一结点y称为x的一个祖先,x是y的后代。每个结点既是自己的祖先也是自己的后代。若y是x的祖先且y≠x,则y是x的真祖先;若y是x的后代且y≠x,则y是x的真后代

以x为根的子树是根为x由x的后代组成的有根树

如果从根r到结点x的简单路径上的最后一条边是(y,x),则y是x的父结点,而x是y的子结点。若两个结点有相同的父结点,则它们是兄弟。一个没有子结点的结点为叶结点(或外部结点),非叶结点也称内部结点

有根树T的结点x的度等于其子结点的数目,从根r到结点x的简单路径的长度即为x在T中的深度,有根树T的高度等于树中结点的最大深度


三、有序树

有序树是每个结点的子结点都有序有根树


四、二叉树

二叉树T是定义在有限结点集上的结构,它或者不包含任何结点,或者包含三个不相交的结点集合:一个根结点,一棵称为左子树的二叉树,一棵称为右子树的二叉树,不包含任何结点的二叉树称为空树或零树,有时用符号NIL表示

若左子树非空,则它的根结点称为整棵树的根的左孩子;类似地,非空右子树地根称为整棵树的根的右孩子,如果一棵子树是零树,则称该孩子是缺失的

二叉树不仅仅是一棵结点度数均为2的有序树,一棵满二叉树的每个结点或是叶结点或度为2


五、完美二叉树和完全二叉树

完美二叉树的所有内部结点均有2个子结点,所有叶结点深度相同,高度为k完美二叉树包含2^k-1个结点

•完全二叉树除最后一层外其余层都是满的,且最后一层要么是满的,要么在右边缺少若干连续结点,高度为k的完全二叉树最少有2(k-1)个结点,最多有2k-1个结点,一个包含n个结点的完全二叉树高度为⌊lg⁡n ⌋+1


二叉树的表示

每个结点具有三个属性p、left和right,分别存放指向父结点、左孩子和右孩子的指针,如果x.p=NIL,则x是根结点;如果x没有左孩子,则x.left=NIL;如果x没有右孩子,则x.right=NIL,T.root指向整棵树的根结点,如果T.root=NIL则该树为空

对于每个结点至多有k个孩子的有根树,每个结点维护k个指针可能造成大量空间浪费,使用左孩子右兄弟表示法来避免空间浪费,每个结点维护三个指针:

  1. 指向父结点的指针x.p
  2. 指向最左边孩子的指针x.left_child
  3. 指向右侧相邻兄弟的指针x.right_sibling

如果结点x没有孩子结点,则x.left_child=NIL;如果结点x是其父结点的最右孩子,则x.right_sibling=NIL

二叉堆

二叉堆是一棵完全二叉树

二叉堆有两种形式:

  • 最大堆(大根堆)
    除根结点外的所有节点满足A[parent(i)]≥A[i]
  • 最小堆(小根堆)
    除根结点外的所有节点满足A[parent(i)]≤A[i]

实现维护二叉堆的性质(假定根节点为left(i)和right(i)的二叉堆都是最大堆)

//递归
Function max-heapify(A,i): 
	l <- left(i); 
	r <- right(i); 
	if l <= A.heapSize and A[l] > A[i] then largest <- l; 
	else largest <- i; 
	if r <= A.heapSize and A[r] > A[i] then largest <- r; 
	if largest != i then 
		exchange A[j] with A[largest]; 
		max-heapify(A,largest); 
	end 
end

//非递归
Function max-heapify(A,i): 
	while i <- A.heapSize/2 do 
		l←left(i); 
		r←right(i); 
		if l <= A.heapSize and A[l] > A[i] then largest <- l; 
		else largest <- i; 
		if r <= A.heapSize and A[r] > A[i] then largest <- r; 
		if largest != i then 
			exchange A[i] with A[largest]; 
			i <- largest; 
		end 
		else break; 
	end 
end

建堆

Function build-max-heap(A):
	A.heapSize <- A.length;
	for i <- [A.length/2] downto 1 do
		max-heapify(A,i);
	end
end

标签:链表,结点,NIL,元素,学习,算法,二叉树,数据结构,指针
From: https://www.cnblogs.com/CloverJoyi/p/18632655

相关文章

  • PyCharm专项训练4 最小生成树算法
    一、实验目的:本文的实验目的是通过编程实践,掌握并应用Prime算法和Kruskal算法来求解给定图的最小生成树问题。二、实验内容:数据准备:使用networkx库创建一个图G,并添加指定的节点和带权重的边。算法实现:实现Kruskal算法,通过构建最小生成树T,并找出构成最小生成树的边......
  • PyCharm专项训练5 最短路径算法
    一、实验目的    本文的实验目的是通过编程实践,掌握并应用Dijkstra(迪杰斯特拉)算法和Floyd(弗洛伊德)算法来解决图论中的最短路径问题。二、实验内容数据准备:使用邻接表的形式定义两个图graph_dijkstra和graph_floyd,图中包含节点以及节点之间的边和对应的权重。算......
  • STM32 库函数的学习1
        初始化函数,结构体的定义。一直用,不过还真不知道是个结构体呢,所以对结构体这个用法不熟练呢,近期学习正点原子的视频了解到了。    h就是头文件函数库,结构就是:        #ifndef __LED_H__        #define__LED_H__//如果没有定义,下面就......
  • 开展深度学习项目所需要的数学基础|入门书籍·24-12-25
    小罗碎碎念深度学习作为一种复杂的机器学习方法,其核心在于构建和训练多层神经网络模型。为了深入理解和有效应用深度学习技术,掌握一定的数学基础是必不可少的。那么,**深度学习需要哪些数学基础呢?深度学习中的数学难点又在哪里?**这些问题常常困扰着初学者。在网络和书籍......
  • Java学习,文件写入
    Java中文件写入是一个常见的任务,可以使用java.io包中的类来实现这一点。需要注意,写入文件需要写入文件的权限,需要指定文件位置,绝对路径或相对路径来指定。使用FileWriter与BufferedWriter写入文件:importjava.io.BufferedWriter;importjava.io.FileWriter;importjava.io......
  • Java学习,continue关键字
    Javacontinue语句用来结束当前循环,并进入下一次循环,不是所有循环结束了。Java中continue关键字用于跳过,当前循环迭代中的剩余代码,并立即开始下一次迭代。它通常与循环结构(如 for、while 或 do-while)一起使用,不与switch语句一起使用。for循环使用continuepublicclassCo......
  • Java学习,读取文件内容
    Java中读取文件内容,是一个常见的任务,可以使用java.nio.file包中的Files类和Paths 类,或者使用java.io包中的BufferedReader和FileReader类来实现。使用Files和Paths,读取文件内容:importjava.io.IOException;importjava.nio.file.Files;importjava.nio.file.Paths;import......
  • 学习前端时做的一个博客的模板psd2html代码
    链接:https://files.cnblogs.com/files/blogs/779648/%E5%8D%9A%E5%AE%A2.zip?t=1735182491&download=true总结:1、制作过程中会使用ps切片切出有些需要的图或者图标。2、需要纯手写css,html代码,某些css代码可以参考ps图层的css复制。3、可先按页面布局写好网页中容器【div,cs......
  • (2-3-03)目标检测与分割:基于深度学习的分割方法
    2.3.5 基于深度学习的分割方法随着人工智能技术的发展和普及,我们也可以使用相关技术实现目标检测与分割功能功能。在现实应用中,基于深度学习的常用分割方法如下。(1)PointSeg:使用PointNet进行点云分割,可以将点云中的不同目标分割出来。(2)PointCNN:使用深度学习方法对点云进行......
  • 计算机毕业设计Python+Spark知识图谱酒店推荐系统 酒店价格预测系统 酒店可视化 酒店
    温馨提示:文末有CSDN平台官方提供的学长联系方式的名片!温馨提示:文末有CSDN平台官方提供的学长联系方式的名片!温馨提示:文末有CSDN平台官方提供的学长联系方式的名片!作者简介:Java领域优质创作者、CSDN博客专家、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO......