【数据结构】—堆详解(手把手带你用C语言实现)
食用指南:本文在有C基础的情况下食用更佳
🔥这就不得不推荐此专栏了:C语言
♈️今日夜电波:水星—今泉愛夏
1:10 ━━━━━━️💟──────── 4:23
🔄 ◀️ ⏸ ▶️ ☰
💗关注👍点赞🙌收藏您的每一次鼓励都是对我莫大的支持😍
目录
❤️什么是堆?
堆的分类
💛堆的实现思路
使用什么实现?
怎么分辨父节点以及子节点?
总体实现思路
💜堆的实现
结构体以及接口的实现
💯堆的两种建堆方式(调整方法)究极无敌重要!!!
向上调整方法
向下调整方法
堆的构建
堆的销毁
堆的插入
⭕️堆的删除 (较重要)
取堆顶的数据
堆的数据个数
堆的判空
💚总体代码
❤️什么是堆?
堆是一种基于树结构的数据结构,它是一棵二叉树,具有以下两个特点:
1. 堆是一个完全二叉树,即除了最后一层,其他层都是满的,最后一层从左到右填满。
2. 堆中每个节点都满足堆的特性,即父节点的值要么等于或者大于(小于)子节点的值。
堆的分类
堆一般分为两类:大堆和小堆。大堆中,父节点的值大于或等于子节点的值,而小堆中,父节点的值小于或等于子节点的值。堆的主要应用是在排序和优先队列中。
以下分别为两个堆(左大堆,右小堆):
💛堆的实现思路
使用什么实现?
逻辑结构如上, 然而这仅仅是我们想像出来的而已,而实际上的堆的物理结构是一个完全二叉树,通常是用数组实现的。如下:
对此,这就要引申出一个问题?我们该如何分辨父节点以及子节点呢?如下:
怎么分辨父节点以及子节点?
通常我们的数组下标为“0”处即为根节点,也就是说我们一定知道一个父节点!并且我们也有计算公式一个来计算父节点以及子节点(先记住,后面实现有用!!!)也就是说我们也可以通过公式从每一个位置计算父节点以及子节点!如下:
总体实现思路
先建立一个结构体,由于堆的结构实际上是一颗完全二叉树,因此我们的结构跟二叉树一样即可!接着,想想我们的堆需要实现的功能?构建、销毁、插入、删除、取堆顶的数据、取数据个数、判空。(⊙o⊙)…基本的就这些吧哈~
接着按照 定义函数接口->实现各个函数功能->测试测试->收工(-_^) o(* ̄▽ ̄*)ブ
💜堆的实现
结构体以及接口的实现
typedef int HPDataType;typedef struct Heap
{HPDataType* _a;int _size;int _capacity;
}Heap;// 堆的构建
void HeapCreate(Heap* hp, HPDataType* a, int n);
// 堆的销毁
void HeapDestory(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HPDataType x);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
HPDataType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* hp);
// 堆的判空
int HeapEmpty(Heap* hp);
💯堆的两种建堆方式(调整方法)究极无敌重要!!!
在实现以上的接口之前,我们必须必须要知道堆的两种建堆方式!!!
并且仅仅通过调整两种建堆方式的<和>符号我们就可以轻易控制大小堆,具体看代码注释!
建堆有两种方式,分别是自底向上建堆以及自顶向下建堆。具体简介如下:
1. 自底向上建堆:自底向上建堆是指按照原始数组顺序依次插入元素,然后对每个插入的元素执行向上调整的操作,使得堆的性质保持不变。这种方法需要对每个元素都进行调整操作,时间复杂度为 O(nlogn)。
2. 自顶向下建堆:自顶向下建堆是指从堆顶开始,对每个节点执行向下调整操作,使得堆的性质保持不变。这种方法需要从根节点开始递归向下调整,时间复杂度为 O(n)。因此,自顶向下建堆的效率比自底向上建堆要高。
以上两种建堆方式 实际上是基于两种调整方法,接下来将详细介绍:
向上调整方法
堆的向上调整方法将新插入的节点从下往上逐层比较,如果当前节点比其父节点大(或小,根据是大根堆还是小根堆),则交换这两个节点。一直向上比较,直到不需要交换为止。这样可以保证堆的性质不变。
具体步骤如下:
1.将新插入的节点插入到堆的最后一位。
2.获取该节点的父节点的位置,比较该节点与其父节点的大小关系。
.如果该节点比其父节点大(或小,根据是大根堆还是小根堆),则交换这两个节点。
4.重复步骤2-3,直到不需要交换为止,堆的向上调整完成。
堆的向上调整的时间复杂度为O(logn),其中n为堆的大小。
一图让你了解~(以大堆为例)
实现如下:
void swap(HPDataType* s1, HPDataType* s2)
{HPDataType temp = *s1;*s1 = *s2;*s2 = temp;
}void Adjustup(HPDataType* a, int child)//向上调整
{int parent = (child - 1) / 2;while (child > 0){if (a[child] > a[parent])//建大堆,小堆则<{swap(&a[child], &a[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}
向下调整方法
堆的向下调整方法是指将某个节点的值下放至其子节点中,以维护堆的性质的过程。
假设当前节点为 i,其左子节点为 2i+1,右子节点为 2i+2,堆的大小为 n
则向下调整的步骤如下:
-
从当前节点 i 开始,将其与其左右子节点中较小或较大的节点比较,找出其中最小或最大的节点 j。
-
如果节点 i 小于等于(或大于等于,取决于是最小堆还是最大堆)节点 j,则说明它已经满足堆的性质,调整结束;否则,将节点 i 与节点 j 交换位置,并将当前节点 i 更新为 j。
-
重复执行步骤 1 和步骤 2,直到节点 i 没有子节点或已经满足堆的性质。
一图让你了解~(以大堆为例)
实现如下:
void swap(HPDataType* s1, HPDataType* s2)
{HPDataType temp = *s1;*s1 = *s2;*s2 = temp;
}void Adjustdown(HPDataType* a, int n, int parent)//向下调整
{int child = parent * 2 + 1;while (child < n){if (child + 1 < n && a[child + 1] > a[child])//找出两个孩子中较大的那个,此为大堆,如果要实现小堆则 改 >{++child;}if (a[child] > a[parent])//此为大堆,如果要实现小堆则 改 >{swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}
堆的构建
void HeapCreate(Heap* hp, HPDataType* a, int n)
{assert(hp);assert(a);hp->_a = (HPDataType*)malloc(sizeof(HPDataType) * n);if (!hp->_a){perror("malloc fail");exit(-1);}hp->_capacity = hp->_size = n;//将a中的元素全部转移到堆中memcpy(hp->_a, a, sizeof(HPDataType) * n);//建堆for (int i = 1; i < n; i++){Adjustup(hp->_a, i);//按向上调整,此建立大堆}}
本文的构建方法是通过传递一个数组以及传递一个数组大小来构建的,里面包括了堆结构体的初始化操作,基本的建堆方式则是通过向上调整方法建堆。
堆的销毁
void HeapDestory(Heap* hp)
{assert(hp);free(hp->_a);hp->_a = NULL;hp->_capacity = hp->_size = 0;
}
就正常的销毁操作?大家应该都懂(确信) (o°ω°o)
堆的插入
void HeapPush(Heap* hp, HPDataType x)
{assert(hp);if (hp->_capacity == hp->_size)//扩容{int newcapacity = hp->_capacity == 0 ? 4 : hp->_capacity * 2;HPDataType* new = (HPDataType*)realloc(hp->_a, sizeof(HPDataType) * newcapacity);if (!new){perror("realloc fail");exit(-1);}hp->_a = new;hp->_capacity = newcapacity;}hp->_a[hp->_size++] = x;Adjustup(hp->_a, hp->_size - 1);}
实现是对于堆的空间进行判断,不够则是扩容操作,当然也有初始化的意思,接着是通过向上调整的方式插入操作。
⭕️堆的删除 (较重要)
void HeapPop(Heap* hp)//先将最后一个数与堆顶交换,然后再让size--,再进行向下调整
{assert(hp);swap(&hp->_a[0], &hp->_a[hp->_size - 1]);hp->_size--;Adjustdown(hp->_a, hp->_size, 0);}
进行删除操作,我们当然是删除堆顶啦,这个删除操作先将最后一个数与堆顶交换,然后再让size--,再进行向下调整。
一图让你了解~
取堆顶的数据
HPDataType HeapTop(Heap* hp)//取堆顶
{assert(hp);assert(hp->_size > 0);return hp->_a[0];
}
堆的数据个数
int HeapSize(Heap* hp)//堆大小
{assert(hp);return hp->_size;
}
堆的判空
int HeapEmpty(Heap* hp)//判堆空
{assert(hp);return hp->_size==0;
}
💚总体代码
pile.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 01
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>typedef int HPDataType;typedef struct Heap
{HPDataType* _a;int _size;int _capacity;
}Heap;// 堆的构建
void HeapCreate(Heap* hp, HPDataType* a, int n);
// 堆的销毁
void HeapDestory(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HPDataType x);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
HPDataType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* hp);
// 堆的判空
int HeapEmpty(Heap* hp);
pile.c
#include"pile.h"void swap(HPDataType* s1, HPDataType* s2)
{HPDataType temp = *s1;*s1 = *s2;*s2 = temp;
}void Adjustup(HPDataType* a, int child)//向上调整
{int parent = (child - 1) / 2;while (child > 0){if (a[child] > a[parent])//建大堆,小堆则<{swap(&a[child], &a[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}void Adjustdown(HPDataType* a, int n, int parent)//向下调整
{int child = parent * 2 + 1;while (child < n){if (child + 1 < n && a[child + 1] > a[child])//找出两个孩子中较大的那个,此为大堆,如果要实现小堆则 改 >{++child;}if (a[child] > a[parent])//此为大堆,如果要实现小堆则 改 >{swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}void HeapCreate(Heap* hp, HPDataType* a, int n)
{assert(hp);assert(a);hp->_a = (HPDataType*)malloc(sizeof(HPDataType) * n);if (!hp->_a){perror("malloc fail");exit(-1);}hp->_capacity = hp->_size = n;//将a中的元素全部转移到堆中memcpy(hp->_a, a, sizeof(HPDataType) * n);//建堆for (int i = 1; i < n; i++){Adjustup(hp->_a, i);//按向上调整,此建立大堆}}void HeapDestory(Heap* hp)
{assert(hp);free(hp->_a);hp->_a = NULL;hp->_capacity = hp->_size = 0;
}void HeapPush(Heap* hp, HPDataType x)
{assert(hp);if (hp->_capacity == hp->_size)//扩容{int newcapacity = hp->_capacity == 0 ? 4 : hp->_capacity * 2;HPDataType* new = (HPDataType*)realloc(hp->_a, sizeof(HPDataType) * newcapacity);if (!new){perror("realloc fail");exit(-1);}hp->_a = new;hp->_capacity = newcapacity;}hp->_a[hp->_size++] = x;Adjustup(hp->_a, hp->_size - 1);}void HeapPop(Heap* hp)//先将最后一个数与堆顶交换,然后再让size--,再进行向下调整
{assert(hp);swap(&hp->_a[0], &hp->_a[hp->_size - 1]);hp->_size--;Adjustdown(hp->_a, hp->_size, 0);}HPDataType HeapTop(Heap* hp)//取堆顶
{assert(hp);assert(hp->_size > 0);return hp->_a[0];
}int HeapSize(Heap* hp)//堆大小
{assert(hp);return hp->_size;
}int HeapEmpty(Heap* hp)//判堆空
{assert(hp);return hp->_size==0;
}
test.c
#include"pile.h"void test()
{Heap hp;int arr[] = { 1,6,2,3,4,7,5 };HeapCreate(&hp, arr, sizeof(arr) / sizeof(arr[0]));//HeapPush(&hp, 10);printf("%d\n", HeapSize(&hp));while (!HeapEmpty(&hp)){printf("%d %d \n", HeapTop(&hp), HeapSize(&hp));HeapPop(&hp);}printf("%d\n", HeapSize(&hp));HeapDestory(&hp);HeapSort(arr, sizeof(arr) / sizeof(arr[0]));printf("\n");
}int main()
{test();return 0;
}
测试结果:
感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o!
给个三连再走嘛~
相关文章:
【数据结构】—堆详解(手把手带你用C语言实现)
食用指南:本文在有C基础的情况下食用更佳 🔥这就不得不推荐此专栏了:C语言 ♈️今日夜电波:水星—今泉愛夏 1:10 ━━━━━━️💟──────── 4:23 …...
关于算法复杂度的几张表
算法在改进今天的计算机与古代的计算机的区别 去除冗余 数据点 算法复杂度 傅里叶变换...
蓝桥杯每日一题2023.10.1
路径 - 蓝桥云课 (lanqiao.cn) 题目分析 求最短路问题,有多种解法,下面介绍两种蓝桥杯最常用到的两种解法 方法一 Floyd(求任意两点之间的最短路)注:不能有负权回路 初始化每个点到每个点的距离都为0x3f这样才能对…...
第三章:最新版零基础学习 PYTHON 教程(第十节 - Python 运算符—Python 中的运算符重载)
运算符重载意味着赋予超出其预定义操作含义的扩展含义。例如,运算符 + 用于添加两个整数以及连接两个字符串和合并两个列表。这是可以实现的,因为“+”运算符被 int 类和 str 类重载。您可能已经注意到,相同的内置运算符或函数对于不同类的对象显示不同的行为,这称为运算符…...
Nacos 实现服务平滑上下线(Ribbon 和 LB)
前言 不知道各位在使用 SpringCloud Gateway Nacos的时候有没有遇到过服务刚上线偶尔会出现一段时间的503 Service Unavailable,或者服务下线后,下线服务仍然被调用的问题。而以上问题都是由于Ribbon或者LoadBalancer的默认处理策略有关,其…...
c/c++里 对 共用体 union 的内存分配
对union 的内存分配,是按照最大的那个成员分配的。 谢谢...
博途SCL区间搜索指令(判断某个数属于某个区间)
S型速度曲线行车位置控制,停靠位置搜索功能会用到区间搜索指令,下面我们详细介绍区间搜索指令的相关应用。 S型加减速行车位置控制(支持点动和停车位置搜索)-CSDN博客S型加减速位置控制详细算法和应用场景介绍,请查看下面文章博客。本篇文章不再赘述,这里主要介绍点动动和…...
(三)激光线扫描-中心线提取
光条纹中心提取算法是决定线结构光三维重建精度以及光条纹轮廓定位准确性的重要因素。 1. 光条的高斯分布 激光线条和打手电筒一样,中间最亮,越像周围延申,光强越弱,这个规则符合高斯分布,如下图。 2. 传统光条纹中心提取算法 传统的光条纹中心提取算法有 灰度重心法、…...
递归与分治算法(1)--经典递归、分治问题
目录 一、递归问题 1、斐波那契数列 2、汉诺塔问题 3、全排列问题 4、整数划分问题 二、递归式求解 1、代入法 2、递归树法 3、主定理法 三、 分治问题 1、二分搜索 2、大整数乘法 一、递归问题 1、斐波那契数列 斐波那契数列不用过多介绍,斐波那契提出…...
Java之SpringCloud Alibaba【六】【Alibaba微服务分布式事务组件—Seata】
一、事务简介 事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。 在关系数据库中,一个事务由一组SQL语句组成。 事务应该具有4个属性: 原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。 原子性(atomicity) ∶个事务…...
Android逆向学习(五)app进行动态调试
Android逆向学习(五)app进行动态调试 一、写在前面 非常抱歉鸽了那么久,前一段时间一直在忙,现在终于结束了,可以继续更新android逆向系列的,这个系列我会尽力做下去,然后如果可以的话我看看能…...
音频编辑软件Steinberg SpectraLayers Pro mac中文软件介绍
Steinberg SpectraLayers Pro mac是一款专业的音频编辑软件,旨在帮助音频专业人士进行精细的音频编辑和声音处理。它提供了强大的频谱编辑功能,可以对音频文件进行深入的频谱分析和编辑。 Steinberg SpectraLayers Pro mac软件特点 1. 频谱编辑ÿ…...
基于.Net Core实现自定义皮肤WidForm窗口
前言 今天一起来实现基于.Net Core、Windows Form实现自定义窗口皮肤,并实现窗口移动功能。 素材 准备素材:边框、标题栏、关闭按钮图标。 窗体设计 1、创建Window窗体项目 2、窗体设计 拖拉4个Panel控件,分别用于:标题栏、关…...
【Rust】操作日期与时间
目录 介绍 一、计算耗时 二、时间加减法 三、时区转换 四、年月日时分秒 五、时间格式化 介绍 Rust的时间操作主要用到chrono库,接下来我将简单选一些常用的操作进行介绍,如果想了解更多细节,请查看官方文档。 官方文档:chr…...
blender快捷键
1, shift a 添加物体 2,ctrl alt q 切换四格视图 3, ~ 展示物体的各个视图按钮,(~ 就是tab键上面的键) 4,a 全选,全选后,点 ctrl 鼠标框选 减去已经选择的;…...
java Spring Boot 自动启动热部署 (别再改点东西就要重启啦)
上文 java Spring Boot 手动启动热部署 我们实现了一个手动热部署的代码 但其实很多人会觉得 这叫说明热开发呀 这么捞 写完还要手动去点一下 很不友好 其实我们开发人员肯定是希望重启这种事不需要自己手动去做 那么 当然可以 我们就让它自己去做 Build Project 这个操作 我们…...
TouchGFX之后端通信
在大多数应用中,UI需以某种方式连接到系统的其余部分,并发送和接收数据。 它可能会与硬件外设(传感器数据、模数转换和串行通信等)或其他软件模块进行交互通讯。 Model类 所有TouchGFX应用都有Model类,Model类除了存…...
cesium gltf控制
gltf格式详解 glTF格式本质上是一个JSON文件。这一文件描述了整个3D场景的内容。它包含了对场景结构进行描述的场景图。场景中的3D对象通过场景结点引用网格进行定义。材质定义了3D对象的外观,动画定义了3D对象的变换操作(比如选择、平移操作)。蒙皮定义了3D对象如何进行骨骼…...
Spring的依赖注入(DI)以及优缺点
Spring的依赖注入(DI):解释和优点 依赖注入(Dependency Injection,简称DI)是Spring框架的核心概念之一,也是现代Java应用程序开发的重要组成部分。本文将深入探讨DI是什么,以及它的…...
【强化学习】05 —— 基于无模型的强化学习(Prediction)
文章目录 简介蒙特卡洛算法时序差分方法Example1 MC和TD的对比偏差(Bias)/方差(Variance)的权衡Example2 Random WalkExample3 AB 反向传播(backup)Monte-Carlo BackupTemporal-Difference BackupDynamic Programming Backup Boot…...
【计算机组成原理】考研真题攻克与重点知识点剖析 - 第 1 篇:计算机系统概述
前言 本文基础知识部分来自于b站:分享笔记的好人儿的思维导图,感谢大佬的开源精神,习题来自老师划的重点以及考研真题。此前我尝试了完全使用Python或是结合大语言模型对考研真题进行数据清洗与可视化分析,本人技术有限ÿ…...
【Java-LangChain:面向开发者的提示工程-8】聊天机器人
第八章 聊天机器人 使用一个大型语言模型的一个令人兴奋的事情是,我们可以用它来构建一个定制的聊天机器人 (Chatbot) ,只需要很少的工作量。在这一节中,我们将探索如何利用聊天的方式,与个性化(或专门针对特定任务或…...
利用t.ppft.interval分别计算T分布置信区间[实例]
scipy.stats.t.interval用于计算t分布的置信区间,即给定置信水平时,计算对应的置信区间的下限和上限。 scipy.stats.t.ppf用于计算t分布的百分位点,即给定百分位数(概率)时,该函数返回给定百分位数对应的t…...
软件工程第三周
可行性研究 续 表达工作量的方式 LOC估算:Line of Code 估算公式S(Sopt4SmSpess)/6 FP:功能点 1. LOC (Line of Code) 估算 定义:LOC是指一个软件项目中的代码行数。 2. FP (Function Points) 估算 定义:FP是基于软件的功能性和…...
动态链接那些事
1、为什么要动态链接 1.1 空间浪费 对于静态链接来说,在程序运行之前,会将程序所需的所有模块编译、链接成一个可执行文件。这种情况下,如果 Program1 和 Program2 都需要用到 Lib.o 模块,那么,内存中和磁盘中实际上就…...
力扣:118. 杨辉三角(Python3)
题目: 给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中,每个数是它左上方和右上方的数的和。 来源:力扣(LeetCode) 链接:力扣(LeetCode)官…...
QGIS文章二——DEM高程裁剪和3D地形图
经常看到别人基于高程文件制作出精美的3D地图,笔者按照互联网几种制作方式进行尝试后,写的DEM高程裁剪和3D地形图教程,或许其中有一些错误的,也请指出。 本文基于海南省的shp文件和海南省DEM高程文件,制作海口地区的3D…...
【kubernetes】kubernetes中的StatefulSet使用
TOC 1 为什么需要StatefulSet 常规的应用通常使用Deployment,如果需要在所有机器上部署则使用DaemonSet,但是有这样一类应用,它们在运行时需要存储一些数据,并且当Pod在其它节点上重建时也希望这些数据能够在重建后的Pod上获取&…...
创建文件夹
/storage/emulated/0/代码文件/ 没有就创建 文件名命名方法:编号. 库 时间戳 使用Python的os模块来检查目录是否存在,并在不存在时创建它。下面是一个示例代码,演示了如何检查指定路径下的目录是否存在,若不存在则创建…...
点击router-link时候会发生什么?
当你点击链接或按钮时,将会导航到 User 组件,就会显示相应的用户 ID。 这里说一下执行流程,当点击一个 router-link 时,Vue Router会执行以下流程: 1)点击事件触发: 当你点击 router-link 组件时…...
做网站之前要备案是什么意思/全国教育培训机构平台
Spring框架已经成为Java开发人员的必备知识,而且Spring 3引入了强大的新特性,例如SpEL、Spring 表达式语言、loC 容器的新注解以及用户急需的对REST的支持。无论你是刚刚接触Spring还是被 Spring 3.0的新特性所吸引,这份笔记都是掌握Spring的…...
广州公司网站制作公司/淘宝大数据查询平台
笔者浏览电商平台的时候发现向来以低配高价著称的vivo居然有一款手机售价低至799元,这应该是它有史以来最低价的手机,去年四季度它在国内前五名当中下跌幅度最大、下跌幅度创下了去年四个季度的新高,似乎显示出它面临着出货量的巨大压力下被迫…...
怎样创建自己公司的网站/站内关键词排名优化软件
win10 ubuntu 16.04 VNC 6.18 VNC和windows之间的复制粘贴模板不共享,想要开启他们之间的复制粘贴,只需要如下命令: 安装autocutsel sudo apt install autocutsel 命令行中输入: autocutsel或者后台运行: auto…...
电子方案网站建设方案/怎么做微信推广和宣传
字符串类型详解 1、正常字符串我们使用单引号,或者双引号包裹 2、注意转义字符\ \ \n \t \u4e2d \u#### unicode字符 \x41 Asc11字符3、多行字符串编写 //tab 上面iesc键下面 var msg he11owor1d你好ya你好4、模板字符串 //tab 上面 esc键下面 let name &qu…...
产品设计流程/电商网站商品页的优化目标是什么
《计算机组装与维护》虚拟实验界面设计与制作(本科)毕业论文设计本科毕业设计(论文)《计算机组装与维护》虚拟实验界面设计与制作摘 要随着计算机技术的发展,计算机的平面图像处理技术已经日益发展。Photoshop是一款功能强大的平面图像处理软件,广泛应用…...
濮阳做网站优化/青岛做网站推广
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式 目录 解决的问题 设计模式:模板方法 解决的问题 当功能内部一部分实现是确定的&am…...