1
0
mirror of https://github.com/142vip/408CSFamily.git synced 2026-04-13 10:49:45 +08:00

refactor: 配置全局采用ts改写,优化导航栏

- ts改写配置文件
- 导航栏内容优化,修复一些问题
- 新增一些文档,调整样式显示
This commit is contained in:
妹妹下雨回不去
2023-03-02 16:38:11 +08:00
parent a144154f93
commit 7bd3072ee1
84 changed files with 1987 additions and 1752 deletions

View File

@@ -1,30 +0,0 @@
import themeConfig from "./config/theme.config";
import pluginsConfig from "./config/plugins.config";
// 用于区分base路径是否nginx代理
const PROXY_DOMAIN=process.env.PROXY_DOMAIN||false
export default {
title: "计算机应试全家桶",
description: "磨刀不误砍柴工,读完硕士再打工",
base: PROXY_DOMAIN ? "/408CSFamily/":"/",
port: 4200,
head: [
[
"link", {rel: "icon", href: "/408_favicon.ico"}
],
[
// 百度统计
'script', {}, `
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?3515cc46ae60747b778140f0e5e22dfe";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();`
]
],
...themeConfig,
...pluginsConfig
}

61
docs/.vuepress/config.ts Normal file
View File

@@ -0,0 +1,61 @@
import pluginsConfig from "./config/plugins.config";
import themeConfig from "./config/theme.config";
import {defineUserConfig} from "vuepress";
import {fileURLToPath} from 'node:url'
import {path} from "@vuepress/utils";
// @ts-ignore
const __dirname = path.dirname(fileURLToPath(import.meta.url))
// 用于区分base路径是否nginx代理
const PROXY_DOMAIN = process.env.PROXY_DOMAIN || false
export default defineUserConfig({
title: "计算机应试全家桶",
description: "磨刀不误砍柴工,读完硕士再打工",
base: PROXY_DOMAIN ? "/408CSFamily/" : "/",
port: 4200,
head: [
["link", {rel: "icon", href: "/fight_favicon.ico"}],
// vercel统计 相关配置
['script', {type: 'text/javascript', src: '/_vercel/insights/script.js'}],
[
"link", {rel: "icon", href: "/408_favicon.ico"}
],
// 百度统计
[
'script', {}, `
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?3515cc46ae60747b778140f0e5e22dfe";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();`
]
],
markdown: {
// todo 引入代码文件时的路径替换
importCode: {
handleImportPath: (str) => {
if (str.includes('@code')) {
return str.replace(/^@code/, path.resolve(__dirname, '../../code/'))
}
if (str.includes('~@')) {
return str.replace(/^~@/, path.resolve(__dirname, '../../'))
}
return str
},
},
// md doc formatter headerDepth
headers: {
level: [2, 3, 4]
}
},
// 主题配置
...themeConfig,
// 插件配置
...pluginsConfig,
shouldPrefetch: false,
})

View File

@@ -0,0 +1,83 @@
import {ThemeLocaleData} from "vuepress-theme-hope";
/**
* 支持中文
* 参考https://theme-hope.vuejs.press/zh/config/i18n.html
*/
const localCN: ThemeLocaleData = {
lang: "zh-CN",
navbarLocales: {
langName: "简体中文",
selectLangAriaLabel: "选择语言",
},
metaLocales: {
author: "作者",
date: "写作日期",
origin: "原创",
views: "访问量",
category: "分类",
tag: "标签",
readingTime: "阅读时间",
words: "字数",
toc: "标题大纲",
prev: "上一页",
next: "下一页",
lastUpdated: "上次编辑于",
contributors: "贡献者",
editLink: "编辑此页",
print: "打印",
},
blogLocales: {
article: "文章",
articleList: "文章列表",
category: "分类",
tag: "标签",
timeline: "时间轴",
timelineTitle: "昨日不在",
all: "全部",
intro: "个人介绍",
star: "收藏",
},
paginationLocales: {
prev: "上一页",
next: "下一页",
navigate: "跳转到",
action: "前往",
errorText: "请输入 1 到 $page 之前的页码!",
},
outlookLocales: {
themeColor: "主题色",
darkmode: "外观",
fullscreen: "全屏",
},
encryptLocales: {
iconLabel: "文章已加密",
placeholder: "输入密码",
remember: "记住密码",
errorHint: "请输入正确的密码",
},
routeLocales: {
skipToContent: "跳至主要內容",
notFoundTitle: "访问的页面不存在,嘤嘤嘤",
notFoundMsg: [
"这里什么也没有",
"我们是怎么来到这儿的?",
"这 是 四 零 四 !",
"看起来你访问了一个失效的链接",
],
back: "返回",
home: "首页",
openInNewWindow: "新窗口打开",
},
}
/**
* 自定义语言,支持文案
*/
export const langConfig = {
"/": localCN
}

View File

@@ -0,0 +1,72 @@
export const navbar = [
{
text: '首页',
link: '/'
},
// {
// text: "算法恶补",
// children: [{
// text: '习题狂刷',
// link: '/manuscripts/algorithm/topic_practice'
// }, {
// text: '刷题笔记',
// link: '/manuscripts/algorithm/algorithm_note'
// }, {
// text: '在线刷题',
// children: [{
// text: '杭电OJ', link: 'http://acm.hdu.edu.cn/'
// },
// {
// text: '牛客网', link: 'https://www.nowcoder.com/'
// }, {
// text: 'LeetCode', link: 'https://leetcode-cn.com/'
// }]
// }]
// },
{
text: "数据结构",
link: "/manuscripts/ds"
},
{
text: "操作系统",
link: "/manuscripts/os"
},
{
text: "计算机组成原理",
link: "/manuscripts/ccp"
},
{
text: "计算机网络",
link: "/manuscripts/cn"
},
{
text: '其他资料',
children: [{
text: '考研相关',
children: [{
text: '111',
link: '/333'
}]
}, {
text: "思维导图",
children: [{
text: '数据结构',
link: '/note-map/ds-map'
}, {
text: '操作系统',
link: '/note-map/os-map'
}, {
text: '计算机组成原理',
link: '/note-map/ccp-map'
}, {
text: '计算机网络',
link: '/note-map/cn-map'
}]
}]
},
{
text: "大事记",
link: "/manuscripts/big-event-history"
},
];

View File

@@ -1,71 +0,0 @@
export default [
{
text: '主页',
link: '/'
},
{
text: "算法恶补",
children: [{
text: '习题狂刷',
link: '/algorithm/topic_practice'
}, {
text: '刷题笔记',
link: '/algorithm/algorithm_note'
}, {
text: '在线刷题',
children: [{
text: '杭电OJ', link: 'http://acm.hdu.edu.cn/'
},
{
text: '牛客网', link: 'https://www.nowcoder.com/'
}, {
text: 'LeetCode', link: 'https://leetcode-cn.com/'
}]
}]
},
{
text: "数据结构",
link: "/ds/basic_introduction"
},
{
text: "操作系统",
link: "/os/"
},
{
text: "计算机组成原理",
link: "/ccp/"
},
{
text: "计算机网络",
link: "/cn/"
}, {
text: '其他资料',
children: [{
text: '考研相关',
children: [{
text: '111',
link: '/333'
}]
}, {
text: "思维导图",
children: [{
text: '数据结构',
link: '/note-map/ds-map'
}, {
text: '操作系统',
link: '/note-map/os-map'
}, {
text: '计算机组成原理',
link: '/note-map/ccp-map'
}, {
text: '计算机网络',
link: '/note-map/cn-map'
}]
}]
},
{
text: "大事记",
link: "/big-event-history"
},
];

View File

@@ -8,7 +8,7 @@ export default {
// 为分类和标签添加索引
customFields: [
{
getter: (page) => page.frontmatter.category,
getter: (page:any) => page.frontmatter.category,
formatter: "分类:$content",
},
{

View File

@@ -0,0 +1,13 @@
import {algorithmSidebar} from "../../manuscripts/algorithm/algorithm.sidebar";
import {dsSidebar} from "../../manuscripts/ds/ds.sidebar";
import {cppSidebar} from "../../manuscripts/ccp/cpp.sidebar";
import osSidebar from "../../manuscripts/os/os.sidebar";
export const sidebar = {
"/ds": dsSidebar,
"/ds/coding": algorithmSidebar,
"/manuscripts/ds":dsSidebar,
"/manuscripts/os":osSidebar,
"/manuscripts/ccp":cppSidebar,
"/manuscripts/cn":algorithmSidebar,
}

View File

@@ -1,2 +0,0 @@
export default[
]

View File

@@ -1,80 +0,0 @@
export default [
{
text: '基础入门',
link: "/ds/basic-introduction",
collapsible: false,
children: [{
text: '1.1 基本概念',
link: '/ds/basic-introduction/1.basic_concepts.md'
}, {
text: '1.2 数据结构三要素',
link: '/ds/basic-introduction/2.three_elements_of_data_structure.md'
}, {
text: '1.3 算法和算法评价',
link: '/ds/basic-introduction/3.algorithm_and_algorithm_evaluation.md'
}],
},
{
text: '线性表',
collapsible: true,
link: "/ds/linear-table",
children: [{
text: '2.1 基础概念和操作',
link: '/ds/linear-table/1.basic_concept_and_operation.md'
}, {
text: '2.2 线性表的顺序表示',
link: '/ds/linear-table/2.sequential_representation.md'
}, {
text: '2.3 基础概念和操作',
link: '/ds/linear-table/3.chain_representation.md'
}, {
text: '2.4 基础概念和操作',
link: '/ds/linear-table/4.double_linked_list.md'
}, {
text: '2.5 基础概念和操作',
link: '/ds/linear-table/5.circular_list.md'
}, {
text: '2.6 基础概念和操作',
link: '/ds/linear-table/6.static_linked_list.md'
}, {
text: '2.7 基础概念和操作',
link: '/ds/linear-table/7.comparison_of_sequential_list_and_linked_list.md'
}, {
text: '2.8 存储结构的选取',
link: '/ds/linear-table/8.selection_of_storage_structure.md'
}, {
text: '2.9 零碎知识补充',
link: '/ds/linear-table/9.piecemeal_knowledge_supplement.md'
}],
},
{
text: "栈和队列",
link: "/ds/栈和队列/",
collapsible: true,
children: [{
text: '3.1 栈的基本概念和基本操作',
link: '/ds/栈和队列/1.栈的基本概念和基本操作.md'
}, {
text: '3.2 栈的顺序存储结构',
link: '/ds/栈和队列/2.栈的顺序存储结构.md'
}, {
text: '3.3 栈的基本概念和基本操作',
link: '/ds/栈和队列/1.栈的基本概念和基本操作.md'
}, {
text: '3.4 栈的基本概念和基本操作',
link: '/ds/栈和队列/1.栈的基本概念和基本操作.md'
}, {
text: '3.5 栈的基本概念和基本操作',
link: '/ds/栈和队列/1.栈的基本概念和基本操作.md'
}, {
text: '3.6 栈的基本概念和基本操作',
link: '/ds/栈和队列/1.栈的基本概念和基本操作.md'
}, {
text: '3.7 栈和队列的应用',
link: '/ds/栈和队列/7.栈和队列的应用.md'
}, {
text: '3.8 特殊矩阵的压缩存储',
link: '/ds/栈和队列/8.特殊矩阵的压缩存储.md'
}]
}
]

View File

@@ -1,7 +0,0 @@
import dsSidebar from "./ds.sidebar";
import algorithmSidebar from "./algorithm.sidebar";
export default{
"/ds": dsSidebar,
"/ds/coding": algorithmSidebar
};

View File

@@ -1,14 +1,17 @@
import {hopeTheme} from "vuepress-theme-hope";
import navbar from "./navbar";
import sidebar from "./sidebar";
import {FOOTER_HTML_INFO} from "./constant.config";
import {hopeTheme} from "vuepress-theme-hope";
import {langConfig} from "./lang.config";
import {navbar} from "./navbar";
import {sidebar} from "./sidebar";
/**
* hope主题配置
* https://theme-hope.vuejs.press/zh/config/
*/
export default {
theme: hopeTheme({
locales: langConfig,
darkmode:"toggle",
// 支持全屏
// fullscreen: true,
@@ -45,7 +48,7 @@ export default {
intro:'',
roundAvatar:true,
timeline:"时间轴的顶部文字",
articleInfo:"",
// articleInfo:"",
// sidebarDisplay:"always",
medias:{
"BiliBili": "https://space.bilibili.com/350937042?spm_id_from=333.1007.0.0"

View File

@@ -1,175 +1,175 @@
/* 此处自定义样式,对主题进行覆盖 */
//首页导航按钮
.actions>a{
margin-left: 5px;
margin-right: 5px;
}
// 隐藏 全文搜索
.DocSearch-Logo{
// display:none;不占位隐藏
visibility:hidden; // 占位隐藏
}
.DocSearch-Button-Keys{
visibility:hidden
}
// 首页title描述
.home .hero .description{
max-width: fit-content;
}
// 搜索框
#docsearch-container>button{
border-radius: 5px;
}
.code-group__nav-tab-active{
color:var(--c-brand)
}
.open-info-div{
text-align: center;
align-content: center;
a{
margin: 5px;
}
}
// 全站主题色
:root {
// brand colors
// --c-brand: #3eaf7c;
// --c-brand-light: #4abf8a;
--c-brand: #4ce9ad;
--c-brand-light: #42b983;
// background colors
--c-bg: #ffffff;
--c-bg-light: #f3f4f5;
--c-bg-lighter: #eeeeee;
--c-bg-navbar: var(--c-bg);
--c-bg-sidebar: var(--c-bg);
--c-bg-arrow: #cccccc;
// text colors
--c-text: #2c3e50;
--c-text-accent: var(--c-brand);
--c-text-light: #3a5169;
--c-text-lighter: #4e6e8e;
--c-text-lightest: #6a8bad;
--c-text-quote: #999999;
// border colors
--c-border: #eaecef;
--c-border-dark: #dfe2e5;
// custom container colors
--c-tip: #42b983;
--c-tip-bg: var(--c-bg-light);
--c-tip-title: var(--c-text);
--c-tip-text: var(--c-text);
--c-tip-text-accent: var(--c-text-accent);
--c-warning: #e7c000;
--c-warning-bg: #fffae3;
--c-warning-title: #ad9000;
--c-warning-text: #746000;
--c-warning-text-accent: var(--c-text);
--c-danger: #cc0000;
--c-danger-bg: #ffe0e0;
--c-danger-title: #990000;
--c-danger-text: #660000;
--c-danger-text-accent: var(--c-text);
--c-details-bg: #eeeeee;
// badge component colors
--c-badge-tip: var(--c-tip);
--c-badge-warning: var(--c-warning);
--c-badge-danger: var(--c-danger);
// transition vars
--t-color: 0.3s ease;
--t-transform: 0.3s ease;
// code blocks vars
--code-bg-color: #282c34;
--code-hl-bg-color: rgba(0, 0, 0, 0.66);
--code-ln-color: #9e9e9e;
--code-ln-wrapper-width: 3.5rem;
// font vars
--font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
--font-family-code: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
// layout vars
--navbar-height: 3.6rem;
--navbar-padding-v: 0.7rem;
--navbar-padding-h: 1.5rem;
--sidebar-width: 20rem;
--sidebar-width-mobile: calc(var(--sidebar-width) * 0.82);
--content-width: 740px;
--homepage-width: 960px;
}
// plugin-back-to-top
.back-to-top {
--back-to-top-color: var(--c-brand);
--back-to-top-color-hover: var(--c-brand-light);
}
// plugin-docsearch
.DocSearch {
--docsearch-primary-color: var(--c-brand);
--docsearch-text-color: var(--c-text);
--docsearch-highlight-color: var(--c-brand);
--docsearch-muted-color: var(--c-text-quote);
--docsearch-container-background: rgba(9, 10, 17, 0.8);
--docsearch-modal-background: var(--c-bg-light);
--docsearch-searchbox-background: var(--c-bg-lighter);
--docsearch-searchbox-focus-background: var(--c-bg);
--docsearch-searchbox-shadow: inset 0 0 0 2px var(--c-brand);
--docsearch-hit-color: var(--c-text-light);
--docsearch-hit-active-color: var(--c-bg);
--docsearch-hit-background: var(--c-bg);
--docsearch-hit-shadow: 0 1px 3px 0 var(--c-border-dark);
--docsearch-footer-background: var(--c-bg);
}
// plugin-external-link-icon
.external-link-icon {
--external-link-icon-color: var(--c-text-quote);
}
// plugin-medium-zoom
.medium-zoom-overlay {
--medium-zoom-bg-color: var(--c-bg);
}
// plugin-nprogress
#nprogress {
--nprogress-color: var(--c-brand);
}
// plugin-pwa-popup
.pwa-popup {
--pwa-popup-text-color: var(--c-text);
--pwa-popup-bg-color: var(--c-bg);
--pwa-popup-border-color: var(--c-brand);
--pwa-popup-shadow: 0 4px 16px var(--c-brand);
--pwa-popup-btn-text-color: var(--c-bg);
--pwa-popup-btn-bg-color: var(--c-brand);
--pwa-popup-btn-hover-bg-color: var(--c-brand-light);
}
// plugin-search
.search-box {
--search-bg-color: var(--c-bg);
--search-accent-color: var(--c-brand);
--search-text-color: var(--c-text);
--search-border-color: var(--c-border);
--search-item-text-color: var(--c-text-lighter);
--search-item-focus-bg-color: var(--c-bg-light);
}
///* 此处自定义样式,对主题进行覆盖 */
//
// //首页导航按钮
//.actions>a{
// margin-left: 5px;
// margin-right: 5px;
//}
//// 隐藏 全文搜索
//.DocSearch-Logo{
// // display:none;不占位隐藏
// visibility:hidden; // 占位隐藏
//}
//.DocSearch-Button-Keys{
// visibility:hidden
//}
//// 首页title描述
//.home .hero .description{
// max-width: fit-content;
//}
//
//// 搜索框
//#docsearch-container>button{
// border-radius: 5px;
//}
//
//.code-group__nav-tab-active{
// color:var(--c-brand)
//}
//
//.open-info-div{
// text-align: center;
// align-content: center;
// a{
// margin: 5px;
// }
//}
//
//
//// 全站主题色
//:root {
// // brand colors
// // --c-brand: #3eaf7c;
// // --c-brand-light: #4abf8a;
//
// --c-brand: #4ce9ad;
// --c-brand-light: #42b983;
//
// // background colors
// --c-bg: #ffffff;
// --c-bg-light: #f3f4f5;
// --c-bg-lighter: #eeeeee;
// --c-bg-navbar: var(--c-bg);
// --c-bg-sidebar: var(--c-bg);
// --c-bg-arrow: #cccccc;
//
// // text colors
// --c-text: #2c3e50;
// --c-text-accent: var(--c-brand);
// --c-text-light: #3a5169;
// --c-text-lighter: #4e6e8e;
// --c-text-lightest: #6a8bad;
// --c-text-quote: #999999;
//
// // border colors
// --c-border: #eaecef;
// --c-border-dark: #dfe2e5;
//
// // custom container colors
// --c-tip: #42b983;
// --c-tip-bg: var(--c-bg-light);
// --c-tip-title: var(--c-text);
// --c-tip-text: var(--c-text);
// --c-tip-text-accent: var(--c-text-accent);
// --c-warning: #e7c000;
// --c-warning-bg: #fffae3;
//--c-warning-title: #ad9000;
//--c-warning-text: #746000;
//--c-warning-text-accent: var(--c-text);
//--c-danger: #cc0000;
//--c-danger-bg: #ffe0e0;
//--c-danger-title: #990000;
//--c-danger-text: #660000;
//--c-danger-text-accent: var(--c-text);
//--c-details-bg: #eeeeee;
//
//// badge component colors
//--c-badge-tip: var(--c-tip);
//--c-badge-warning: var(--c-warning);
//--c-badge-danger: var(--c-danger);
//
//// transition vars
//--t-color: 0.3s ease;
//--t-transform: 0.3s ease;
//
//// code blocks vars
//--code-bg-color: #282c34;
//--code-hl-bg-color: rgba(0, 0, 0, 0.66);
//--code-ln-color: #9e9e9e;
//--code-ln-wrapper-width: 3.5rem;
//
//// font vars
//--font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
// Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
//--font-family-code: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
//
//// layout vars
//--navbar-height: 3.6rem;
//--navbar-padding-v: 0.7rem;
//--navbar-padding-h: 1.5rem;
//--sidebar-width: 20rem;
//--sidebar-width-mobile: calc(var(--sidebar-width) * 0.82);
//--content-width: 740px;
//--homepage-width: 960px;
//}
//
//// plugin-back-to-top
//.back-to-top {
//--back-to-top-color: var(--c-brand);
//--back-to-top-color-hover: var(--c-brand-light);
//}
//
//// plugin-docsearch
//.DocSearch {
//--docsearch-primary-color: var(--c-brand);
//--docsearch-text-color: var(--c-text);
//--docsearch-highlight-color: var(--c-brand);
//--docsearch-muted-color: var(--c-text-quote);
//--docsearch-container-background: rgba(9, 10, 17, 0.8);
//--docsearch-modal-background: var(--c-bg-light);
//--docsearch-searchbox-background: var(--c-bg-lighter);
//--docsearch-searchbox-focus-background: var(--c-bg);
//--docsearch-searchbox-shadow: inset 0 0 0 2px var(--c-brand);
//--docsearch-hit-color: var(--c-text-light);
//--docsearch-hit-active-color: var(--c-bg);
//--docsearch-hit-background: var(--c-bg);
//--docsearch-hit-shadow: 0 1px 3px 0 var(--c-border-dark);
//--docsearch-footer-background: var(--c-bg);
//}
//
//// plugin-external-link-icon
//.external-link-icon {
//--external-link-icon-color: var(--c-text-quote);
//}
//
//// plugin-medium-zoom
//.medium-zoom-overlay {
//--medium-zoom-bg-color: var(--c-bg);
//}
//
//// plugin-nprogress
//#nprogress {
//--nprogress-color: var(--c-brand);
//}
//
//// plugin-pwa-popup
//.pwa-popup {
//--pwa-popup-text-color: var(--c-text);
//--pwa-popup-bg-color: var(--c-bg);
//--pwa-popup-border-color: var(--c-brand);
//--pwa-popup-shadow: 0 4px 16px var(--c-brand);
//--pwa-popup-btn-text-color: var(--c-bg);
//--pwa-popup-btn-bg-color: var(--c-brand);
//--pwa-popup-btn-hover-bg-color: var(--c-brand-light);
//}
//
//// plugin-search
//.search-box {
//--search-bg-color: var(--c-bg);
//--search-accent-color: var(--c-brand);
//--search-text-color: var(--c-text);
//--search-border-color: var(--c-border);
//
//--search-item-text-color: var(--c-text-lighter);
//--search-item-focus-bg-color: var(--c-bg-light);
//}

View File

@@ -1,2 +1,6 @@
// 主题色
$theme-color: #2196f3;
$theme-color: #2980B9;
$sidebar-width: 20rem;
$sidebar-mobile-width: 16rem;
$content-width: 75rem;
$home-page-width:80rem;

View File

@@ -1,5 +0,0 @@
## 计算机网络
doing

View File

@@ -1,4 +1,4 @@
export default [
export const algorithmSidebar = [
{
text: '基础入门',
link: "/ds/basic_introduction",

View File

@@ -7,7 +7,8 @@
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2022-04-24 08:12:21
-->
## 计算机组成原理
# 计算机组成原理
doing

View File

@@ -0,0 +1 @@
export const cppSidebar=[]

View File

@@ -0,0 +1,5 @@
# 计算机网络
doing

View File

@@ -0,0 +1 @@
export const cnSidebar=[]

View File

Before

Width:  |  Height:  |  Size: 650 KiB

After

Width:  |  Height:  |  Size: 650 KiB

View File

@@ -0,0 +1,80 @@
export const dsSidebar = [
{
text: '基础入门',
prefix: "basic-introduction",
collapsible: false,
children: [{
text: '1.1 基本概念',
link: '1.basic_concepts.md'
}, {
text: '1.2 数据结构三要素',
link: '2.three_elements_of_data_structure.md'
}, {
text: '1.3 算法和算法评价',
link: '3.algorithm_and_algorithm_evaluation.md'
}],
},
{
text: '线性表',
collapsible: false,
prefix: "linear-table",
children: [{
text: '2.1 基础概念和操作',
link: '1.basic_concept_and_operation.md'
}, {
text: '2.2 线性表的顺序表示',
link: '2.sequential_representation.md'
}, {
text: '2.3 基础概念和操作',
link: '3.chain_representation.md'
}, {
text: '2.4 基础概念和操作',
link: '4.double_linked_list.md'
}, {
text: '2.5 基础概念和操作',
link: '5.circular_list.md'
}, {
text: '2.6 基础概念和操作',
link: '6.static_linked_list.md'
}, {
text: '2.7 基础概念和操作',
link: '7.comparison_of_sequential_list_and_linked_list.md'
}, {
text: '2.8 存储结构的选取',
link: '8.selection_of_storage_structure.md'
}, {
text: '2.9 零碎知识补充',
link: '9.piecemeal_knowledge_supplement.md'
}],
},
{
text: "栈和队列",
prefix: "栈和队列",
collapsible: false,
children: [{
text: '3.1 栈的基本概念和基本操作',
link: '1.栈的基本概念和基本操作.md'
}, {
text: '3.2 栈的顺序存储结构',
link: '2.栈的顺序存储结构.md'
}, {
text: '3.3 栈的基本概念和基本操作',
link: '1.栈的基本概念和基本操作.md'
}, {
text: '3.4 栈的基本概念和基本操作',
link: '1.栈的基本概念和基本操作.md'
}, {
text: '3.5 栈的基本概念和基本操作',
link: '1.栈的基本概念和基本操作.md'
}, {
text: '3.6 栈的基本概念和基本操作',
link: '1.栈的基本概念和基本操作.md'
}, {
text: '3.7 栈和队列的应用',
link: '7.栈和队列的应用.md'
}, {
text: '3.8 特殊矩阵的压缩存储',
link: '8.特殊矩阵的压缩存储.md'
}]
}
]

View File

@@ -10,7 +10,7 @@
## 线性表的基础概念和基本操作
# 线性表的基础概念和基本操作
> 强调线性表是一种逻辑结构,不是存储结构

View File

@@ -9,7 +9,7 @@
-->
## 线性表的顺序表示
# 线性表的顺序表示
### 定义

View File

@@ -11,7 +11,7 @@
## 线性表的链式表示
# 线性表的链式表示
顺序表的插入、删除操作需要移动大量元素影响了运行效率虽然时间复杂度为O(1)的情况也存在)。

View File

@@ -11,7 +11,7 @@
## 双链表
# 双链表
从单链表的结构上来看

View File

@@ -11,7 +11,7 @@
## 循环链表
# 循环链表
- 循环单链表
- 循环双链表

View File

@@ -11,7 +11,7 @@
## 静态链表
# 静态链表
> 借助数组来描述线性表的链式存储结构,结点元素同样存在数据域`data`和指针域`next`

View File

@@ -10,7 +10,7 @@
## 顺序表和链表的比较
# 顺序表和链表的比较
### 存取方式

View File

@@ -10,7 +10,7 @@
## 零碎知识补充
# 零碎知识补充
- 无论是链表的插入还是删除操作,必须保证不断链【重要】
- 顺序存储结构可以随机存取也能顺序存取,链式结构只能进行顺序存取

View File

@@ -8,4 +8,4 @@
-->
![](./线性表_水印.png)
![](线性表_水印.png)

View File

Before

Width:  |  Height:  |  Size: 2.0 MiB

After

Width:  |  Height:  |  Size: 2.0 MiB

View File

@@ -0,0 +1,62 @@
<!--
* @Description: 基本概念和基本操作
* @Version: Beta1.0
* @Author: 【B站&公众号】Rong姐姐好可爱
* @Date: 2020-01-11 23:56:02
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2021-03-12 00:02:55
-->
# 基本概念和基本操作
`栈` 只允许在一端进行插入或者删除操作的**线性表**`后进先出的线性表`
- 明确栈是一种线性表
- 限定栈只能在某一端进行插入或者删除操作
![栈的顺序结构](数据结构/栈和队列/images/栈的基本结构.png)
`栈顶`:线性表允许进行插入和删除的一端。
`栈底`:不允许进行插入和删除的另外一端,是**固定的**。类似杯底这中概念
`空栈`:不含任何元素的空表,也叫**栈空**
基本结构如下:
在上面的基本结构中可以假设存在栈S=(a1,a2,a3,a4,a5,a6,a7,a8),很明显
- 栈顶元素a1
- 栈底元素a8
栈只能在栈顶进行插入和删除操作
- 进栈顺序a1->a2->a3->a4->a5->a6->a7->a8
- 出栈顺序a8->a7->a6->a5->a4->a3->a2->a1
可以得出结论栈是后进先出先进后出LIFOLast In First Out也可以叫**后进先出的线性表**
## 栈的基本操作
- `InitStack(&S)`: 初始化一个空栈`S`,栈顶指针初始化为-1
- `StackEmpty(S)`: 判断一个栈是否为空,如果栈空则返回`true`,否则返回`false`
- `Push(&S,x)`: 进栈,若栈未满,`x`进栈操作,插入到栈内成为`新的栈顶元素`
- `Pop(&S,&x)`: 出栈,若栈非空,出栈操作,**弹出栈顶元素**,用指针`x`进行返回。
- `GetTop(S,&x)`: 读栈顶元素,若栈`S`非空用x返回栈顶元素。
- `ClearStack(&S)`: 销毁栈,释放栈`S`占用的存储空间。
> Tips: `&`是C++特有的,可以用来表示引用调用,类似`传址目的`,可以类比指针。 当然在C语言中*代表指针,指向存储地址,也是具有`传址目的`

View File

@@ -0,0 +1,232 @@
<!--
* @Description: 栈的顺序存储结构
* @Version: Beta1.0
* @Author: 【B站&公众号】Rong姐姐好可爱
* @Date: 2020-01-15 16:57:37
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2021-03-12 08:05:50
-->
# 栈的顺序存储结构
`顺序栈`:栈的顺序存储,利用一组地址连续的存储单元存放自栈底到栈顶的所有元素,同时**附加一个用来指向当前栈顶位置的指针**
> 指针指向栈顶top主要是因为栈在线性表的一端操作操作的那端就是栈顶方便操作。
### 顺序栈的存储类型
```C++
// 定义栈中元素的最大个数
# define MaxSize 50
// 结构体定义
typedef struct{
ElemType data[MaxSize]; // 存放栈中元素
int top; // 栈顶指针
}SqStack;
```
假设存在顺序栈S
- 栈顶指针S.top 初始化时设置S.top=-1
- 栈顶元素S.data[S.top]其中S.top为栈顶指针
- 进栈操作:栈不满时,栈顶指针+1再送值到栈顶元素
- 出栈操作:栈非空时,先取栈顶元素值,再将栈顶指针-1
- 栈空条件S.top=-1
- 栈满条件S.top=MaxSize-1
- 栈长S.top+1
> Tips: 进栈先移动指针,避免占满,元素无法添加,出现外溢;出栈先取栈顶元素,保证指针永远指向栈顶。
顺序栈的存储结构体定义可以很明显看出,顺序栈的入栈操作会受到数组上界(MaxSize)的约束。
**当对栈的最大使用空间估计不足时,容易出现栈上溢(外溢),需要主动向用户报告反馈,避免出现错误;**
### 顺序栈的基本运算
- `InitStack(&S)`: 初始化一个空栈`S`,栈顶指针初始化为-1
- `StackEmpty(S)`: 判断一个栈是否为空,如果栈空则返回`true`,否则返回`false`
- `Push(&S,x)`: 进栈,若栈未满,`x`进栈操作,插入到栈内成为`新的栈顶元素`。
- `Pop(&S,&x)`: 出栈,若栈非空,出栈操作,**弹出栈顶元素**,用指针`x`进行返回。
- `GetTop(S,&x)`: 读栈顶元素,若栈`S`非空用x返回栈顶元素。
- `ClearStack(&S)`: 销毁栈,释放栈`S`占用的存储空间。
#### 初始化
`InitStack(&S)`: 初始化一个空栈`S`,栈顶指针初始化为-1
```C++
void InitStack(&S){
// 栈顶指针-1
s.top=-1;
}
```
#### 栈空判断
`StackEmpty(S)`: 判断一个栈是否为空,即:栈顶指针是否为-1如果栈空则返回`true`,否则返回`false`
```C++
bool StackEmpty(S){
if(S.top==-1){
// 栈空
return true;
}else{
// 栈非空
return false;
}
}
```
#### 进栈
`Push(&S,x)`: 进栈,若栈未满,`x`进栈操作,插入到栈内成为`新的栈顶元素`。
```C++
bool Push(SqStack &S,ElemType x){
if(S.top==MaxSize-1){
// 栈满返回false元素无法进行进栈操作
return false;
}else{
// 可进栈,栈顶指针+1再元素入栈
S.data[++S.top]=x;
// 入栈成功
return true;
}
}
```
注意:
- 进栈先移动栈顶指针+1再操作入栈元素
- `++i`是简写先对变量i进行递加操作再进行使用先加后用
#### 出栈
`Pop(&S,&x)`: 出栈,若栈非空,出栈操作,**弹出栈顶元素**,用指针`x`进行返回。
```C++
bool Pop(SqStack &S,ElemType &x){
if(S.top==-1){
// 栈空无栈顶元素可出栈返回false
return false;
}else{
// 栈非空,先元素出栈,再进行指针-1
x=S.data[S.top--];
// 出栈成功返回true
return true;
}
}
```
注意:
- 出栈操作,先让元素出栈,获取栈顶元素,再移动指针-1
- `i--`是先使用变量i再对变量做递减操作先用后加
#### 读(获取)栈顶元素
`GetTop(S,&x)`: 读栈顶元素,若栈`S`非空用x返回栈顶元素。
```C++
bool GetTop(SqStack S,ElemType &x){
if(S.top==-1){
// 栈空,无栈顶元素,返回false
return false;
}else{
// 通过栈顶指针获取栈顶元素赋值给变量x
x=S.data[S.top];
// 读取栈顶元素成功返回true
return true;
}
}
```
**上面的这些操作都是基于栈顶指针初始化为`-1`的情况**
当栈顶指针初始化为`S.top=0`,相关操作操作会有区别:
- 入栈: `S.data[S.top++]=x`
- 出栈: `x=S.data[--S.top]`
**同时, 栈空、栈满条件也会有变化,要仔细对比揣摩**
### 共享栈
`共享栈`:利用栈底位置相对不变的特性,可以让两个顺序栈共享一个`一维存储空间`,将两个栈的栈底分别设置在共享空间的两端,两个栈顶则向共享空间的中间延伸
>Tips: 类似头对头,一致对外这种感觉,噗呲哈哈
![顺序栈共享存储空间](数据结构/栈和队列/images/顺序栈共享存储空间.png)
在上面的共享栈结构图中两个栈0、1号顺序栈的栈顶指针都指向栈顶元素
- 0号栈栈顶指针`top=-1`时0号栈为空
- 1号栈栈顶指针`top=MaxSize`时1号栈为空
当且仅当两个栈的栈顶指针相邻(`top1-top0=1`),可以判断共享栈栈满
#### 进栈
> 进栈:先移动指针,后进行赋值
- 当0号栈进栈时0号栈栈顶指针top0`先加1后赋值`
- 当1号栈进栈时0号栈栈顶指针top1`先减1后赋值`
#### 出栈
> 出栈:先进行赋值,后移动指针
- 当0号栈进栈时0号栈栈顶指针top0`先赋值后减1`
- 当1号栈进栈时0号栈栈顶指针top1`先赋值后加1`
共享栈能够更有效的利用存储空间两个栈空间进行相互调节。只有当这个存储空间共享空间被占满时才会发生上溢。存取数据的时间复杂度都为O(1),在栈顶操作。
**共享栈对存取效率没有什么影响**

View File

@@ -0,0 +1,140 @@
<!--
* @Description: 栈的链式存储结构
* @Version: Beta1.0
* @Author: 【B站&公众号】Rong姐姐好可爱
* @Date: 2020-03-12 08:15:40
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2021-03-13 17:05:26
-->
# 栈的链式存储结构
`链栈` 采用链式存储的栈
`栈满`:对于链栈来说,是基于链式存储的,基本不存在栈满的情况,除非内存已经没有使用空间了。
`栈空`:对于空栈来说,链表原来的定义是头指针指向空,那么链栈的空其实就是`topNULL`,链栈元素总数为0
栈只是栈顶在做插入和删除操作,栈顶应该放在单链表的头部,所以单链表中的头结点也就失去了意义。
**通常对于链栈来说,是不需要头结点的,当然也存在带头结点的链栈**
![](数据结构/栈和队列/images/栈的链式存储结构.png)
栈的链式存储类型:
```C++
// 链栈类型定义【基础】
typedef struct LinkNode{
ElemType data; // 栈元素结点数据域
struct LinkNode *next; // 栈元素结点指针域
} *LinkStack;
// 更为详细的定义
typedef struct StackNode
{
int data;//结点数据域
struct StackNode* next;//结点指针域
}StackNode,* Linktop;
//链栈的数据结构
typedef struct LinkStack
{
Linktop top; //栈顶结点,定义了一个指向上个结构体的指针
int count;//元素个数
}LinkStack;
```
### 优点
- 便于多个栈共享存储空间
- 不存在栈满上溢的情况,避免程序因溢出导致出错
- 有效的提高存取效率
### 进栈
- 如果链栈不存在则栈满入栈操作失败返回false;
- 如果链栈存在,进行单链表的结点插入操作,移动指针,结点元素赋值,再将结点压入链栈中,移动链栈栈顶指针,最后链栈元素总数+1返回true
```C++
/*
* @Description: 基于单链表链栈的进栈操作
* @Version: Beta1.0
* @Author: 【B站&公众号】Rong姐姐好可爱
* @Date: 2020-03-04 07:36:04
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2020-03-04 11:39:16
*/
bool linkStackPushNode(LinkStack* linkStack,int e){
// 判断链栈是否存在
if (!linkStack){
//链栈不存在无法进栈操作返回false
return false;
}
// 开辟栈结点元素内存控件
StackNode* node = (StackNode*)malloc(sizeof(StackNode));
// 新结点指针域指向链表,即栈顶指针位置,元素加入链表
node->next = linkStack->top;
// 新结点数据域赋值
node->data = e;
// 元素进栈,移动栈顶指针,指向新入栈的元素
linkStack->top = node;
// 链栈元素总数+1
linkStack->count++;
//链栈入栈成功返回true
return true;
}
```
### 出栈
- 如果链栈不存在或者为空栈则无法进行出栈操作返回false
- 如果链栈满足出栈条件则通过栈顶指针获取到链栈栈底结点将其数据域赋值给变量e移动栈顶指针指向待出栈元素的后继结点同时释放待出栈元素的内存空间链栈元素总数-1 出栈成功返回true.
```C++
/*
* @Description: 基于单链表链栈的出栈操作
* @Version: Beta1.0
* @Author: 【B站&公众号】Rong姐姐好可爱
* @Date: 2020-03-04 23:38:04
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2020-03-04 23:39:16
*/
bool linkStackPopNode(LinkStack* linkStack,int *e){
// 判断链栈是否存在及是否为空
if (!linkStack || linkStack->count==0){
//出栈失败返回false
return false;
}
// 获取栈顶元素结点
StackNode* node = stack->top;
// 结点元素数据域赋值给变量e
*e = linkStack->data;
// 移动栈顶指向,栈顶指针指向待出栈结点的后继结点
linkStack->top = node->next;
// 变量e已被赋值释放链栈出栈元素的内存控件
free(node);
// 链栈元素个数-1
linkStack->count--;
// 出栈成功返回true.
return true;
}
```
以上是基于单链表的链栈入栈、出栈操作,很明显**时间复杂度都为O(1**,重点`注意移动指针,保持不断链`

View File

@@ -0,0 +1,53 @@
<!--
* @Description: 队列的基本概念和基础操作
* @Version: Beta1.0
* @Author: 【B站&公众号】Rong姐姐好可爱
* @Date: 2020-03-18 06:15:40
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2021-03-14 11:08:32
-->
# 队列的基本概念和基础操作
### 基本概念
`队列`:和栈一样,是一种操作受限制的线性表,只允许在表的一端进行插入,在表的另外一端进行删除,简称为`队`,常记作:`Queue`
`入队` 向队列中插入元素,也叫做`进队`
`出队` 删除队列元素,也叫做`离队`
![](/数据结构/栈和队列/images/队列的基本结构.png)
结合生活中排队的经验,在群体素质高、无人插队的情况下(`薛定谔排队`,噗呲,哈哈哈),**一般最早排队的也是最早离队的**,和栈的`后进先出`不一样的是,队列是`先进先出`First In Frist Out
> Tips
> - 栈:又叫做后进先出的线性表
> - 队列:又叫做先进先出的线性表
`队头`:允许进行删除操作的一端,也叫做`队首`,常记作:`Front`
`队尾`:允许进行插入操作的一端,常记作:`Rear`
`空队列`:不含任何元素的空表,注意这个表是指`线性表`
### 基础操作
> 和栈一样,队列是操作受限的线性表,具有`先进先出`的特性,不是任何对线性表的操作都可以作为队列的操作。例如:无法随便读取到队列中间的某个数据,需要将前面的元素先出队
- `InitQueue(&Q)` 初始化一个队列构造空队列Q
- `QueueEmpty(Q)` 判断队列是否为空队空返回true,否则返回false
- `EnEmpty(&Q,x)` 入队如果队列Q未满将x入队成为新的队尾元素
- `DeEmpty(&Q,&x)` 出队如果队列Q非空删除队头元素复制给x返回
- `GetHead(Q,&x)` 读取队头元素如果队列Q非空则将队头元素赋值给x

View File

@@ -0,0 +1,346 @@
<!--
* @Description:
* @Version: Beta1.0
* @Author: 【B站&公众号】Rong姐姐好可爱
* @Date: 2020-03-1 07:23:48
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2021-03-18 23:51:35
-->
# 队列的顺序存储结构
> 队列的顺序实现是指分配一块连续的存储单元用来存放队列中的元素,并且附加两个指针。
> - `front指针` 指向队头元素的位置
> - `rear指针` 指向队尾元素的位置
队列顺序存储类型:
```C++
// 队列最大存储元素个数
#define MaxSize 50
// 结构体定义
typedef struct {
// 存放队列元素
ElemType data[MaxSize];
// 队头指针和队尾指针
int front,rear;
} SqQueue;
```
假定:
- 队头指针指向队头元素
- 队尾指针指向队尾元素的下一个位置
则:
- 初始状态(**队空条件**`Q.front`===`Q.rear`===0
- 入队操作:队不满时,先赋值给队尾元素,再移动队尾指针+1
- 出队操作: 队不空时,先取队头元素值,再移动队头指针+1
![](/数据结构/栈和队列/images/入队.png)
在空队列中,初始状态为`Q.front===Q.rear==0`,当元素a入队时,队尾指针rear后移+1入队成功后`Q.front==0`、`Q.rear==1`,在队不满的情况下进队,都是`先赋值给队尾元素再移动队尾指针rear+1`,通过上面的图宝贝可以看到,队列被元素打满的时:
- 在这个进队的过程中,没有元素出队,队头指针并没有做改变,`Q.front==0`
- 进队操作直接影响队尾指针的变化,队列满的时候`Q.rear==Maxsize`
> Tips: MaxSize为队列结构体定义中最大存储元素个数哦~
![](/数据结构/栈和队列/images/出队.png)
进队说完了,那给宝贝来说说出队吧。以上图为例,队列中`Q.rear==Maxsize`、`Q.front==0`;当出现元素在队首出队,就会直接影响队首指针,从上面的流程上看:
- 元素出队front指针后移+1在队不空的情况下操作为`先取队头元素值,再移动队头指针+1`
- 当队列中的元素都陆续出队,抛弃了宝贝(都是渣男,噗呲,哈哈哈),指针会是:`Q.rear==Q.front==MaxSize`
从上面两张图中,我们来思考:
> 前面队空条件为:`Q.front===Q.rear===0`,那能用`Q.rear==MaxSize`来表示队满嘛?
傻瓜,你在瞅瞅前面的图,明显存在`Q.rear==MaxSize`,但队列确实空的情况呀。队满要灵活判断,可不要死记书上总结的。书上说的很多结论都是有前提的,老师记结论不记前提容易张冠李戴、含糊不清的呀~
很显然,也存在下面这种情况:
- 队头指针指向队头元素的前一个位置
- 队尾指针指向队尾元素
此时的入队、出队过程就宝贝自己去画流程图咯
### 循环队列
在上面的顺序队列中,当队满后进行出队列,由于顺序队列出队只在队首进行操作,并且只会修改队首指针,这时候就会出现队尾指针一直`Q.rear===MaxSize`情况,就如下:
![队列队满情况分析](/数据结构/栈和队列/images/队列队满情况分析.png)
可以很明显的看到,明明队列不满,但是由于进队列只能在队尾操作,因此不能进行进队操作;通常在这种情况下入队就会出现“上溢出”。
> 需要明确的是:上溢出并不是真正的溢出,只是表明在顺序队列中队不满却无法入队的情况,是一种假的”溢出“
这种情况在顺序队列中是非常常见的,也是顺序队列的一大缺点。为了克服这个缺点,计算机先贤们总是能够有很多很好的办法,这里不得不佩服!!,所以就有了循环队列,**一个将顺序队列臆想为一个环状的空间**
> 很多时候就是这样,为了解决一个问题,从而衍生出一个新的知识
`循环队列`:把顺序队列臆想为一个环状的空间,将存储队列元素的表从逻辑上看做为一个环
![](/数据结构/栈和队列/images/循环队列初始化.png)
当队首指针`Q.front=MaxSize-1`后,再有元素`出队`就前进一个位置自动到位置0了【注意可以结合时钟来理解一圈转完了】
- 初始时:`Q.front=Q.rear=0`
- 队首指针进1 `Q.front=(Q.front+1)%MaxSize`
- 队尾指针进1 `Q.rear=(Q.rear+1)%MaxSize`
- 队列长度: `(Q.rear+MaxSize-Q.front)%MaxSize`
> 是不是理解起来有点抽象,其实我最开始学到这里的时候,也不明白为什么要用`除法取余运算(%`来实现。后来我看看了手机上的时钟指针,一圈两圈三圈的转,好像就开始悟了...其实这种取余操作在计算机知识体系中还是非常常见的例如组成原理中将会学到的补码据说idea就是来源于时钟..
**和时钟一样顺时钟进行时间变换在出队、入队时队首、队尾指针都是按顺时针方向进1**
![](/数据结构/栈和队列/images/循环队列入队.png)
如上图,循环队列从最开始初始化为空队列时:`Q.front==Q.rear==0`,经过元素a入队队尾指针顺时针前移`Q.rear+1`到元素a、b、c、d陆续入队就好像时钟转完了一圈循环队列已满此时发现`Q.front==Q.rear==0`在队满时候依然成立,所以结合前面提到的初始化对空条件:`Q.front==Q.rear==0`,用`Q.front==Q.rear`来区分`队空`和`队满`是非常不合适的。
#### 如何区别队空还是队满
> 为了很好的区别循环队列的`队空`还是`队满`的情况,一般有三种处理方式.
##### 牺牲一个单元来区分队空和队满
这种方式**要求在入队时少用一个队列单元**,是一种比较普遍的做法。约定:
**队头指针在队尾指针在队尾指针的下一个位置作为队满标志【重要】**
- 队满条件:`(Q.rear+1)%MaxSize==Q.front`
- 队空条件:`Q.front==Q.rear`
- 队列中元素个数:`(Q.rear+MaxSize-Q.front)%MaxSize`
##### 类型中增设表示元素个数的数据成员
这种就很直接了直接和MaxSize去比较就可以有
- 队空条件: `Q.count=0`
- 队满条件: `Q.count=MaxSize`
值的注意的是:在这个前提下,不论是`队空`还是`队满`,对会存在`Q.front=Q.rear`,这个可以通过前面方案解决。
##### 类型中增设tag数据成员标记
通过添加tag标记的方式区分`队空`还是`队满`
- `tag==0`的情况下,如果因为删除导致`Q.front==Q.rear`,则队空;
- `tag==1`的情况下,如果因为插入导致`Q.front==Q.rear`,则队满;
可能你会对上面的这两种情况有迷惑,说实话我第一次看的时候,也挺迷惑的,这里我按照我的理解来解释一下:
> 在循环队列中增加tag数据成员标记tag的主要作用
> - 在有元素入队的时候设置tag=1
> - 在有元素出队的时候设置tag=0
对应的算法实现:
```C++
// 入队算法
// 尾插法Q.data[Q.rear]=x;Q.rear=(Q.rear+1)%Maxsize;Q.tag=1
// 队空条件Q.front== Q.rear且Q.tag==0
int EnLoopQueue(SqQueue &Q, ElemType x){
if(Q.front==Q.rear&&Q.tag==1){
return 0;
}
Q.data[Q.rear]=x;
Q.rear=(Q.rear+1)%MaxSize;
Q.tag=1;
return 1;
}
// 出队算法
// 头结点删除x=Q.data[Q.front];Q.front=(Q.front +1)%Maxsize;Q.tag=0
// 队满条件Q.front == Q.rear且Q.tag=1
// 注意:当删除之后链表为空时,还需增加一步,将尾指针指向头结点
int DeLoopQueue(SqQueue &Q, ElemType &x){
if (Q.front==Q.rear&&Q.tag==0){
return 0;
}
x=Q.data[Q.front];
Q.front=(Q.front+1)%MaxSize;
Q.tag=0;
return 1;
}
```
#####
### 代码实现
#### 初始化空队列
```C++
/*
* @Description: 循环队列初始化,队列为空
* @Version: Beta1.0
* @Author: 【B站&公众号】Rong姐姐好可爱
* @Date: 2019-09-27 14:17:28
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2021-03-18 22:15:06
*/
void InitLoopQueque(&Q){
Q.front=Q.rear=0;
}
```
#### 队列是否为空
```C++
/*
* @Description: 判断循环队列是否为空
* @Version: Beta1.0
* @Author: 【B站&公众号】Rong姐姐好可爱
* @Date: 2019-09-27 14:17:28
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2021-03-18 22:15:06
*/
bool isEmpatyLoopQueue(Q){
// 注意循环队列对空条件Q.rear=Q.front
if(Q.rear=Q.front){
// 队空
return true;
}else{
// 非空
return false;
}
}
```
#### 入队操作
```C++
/*
* @Description: 循环队列元素入队
* @Version: Beta1.0
* @Author: 【B站&公众号】Rong姐姐好可爱
* @Date: 2019-09-27 14:17:28
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2021-03-18 22:15:06
*/
bool EnLoopQueue(SqQueue &Q, ElemType x){
// 判断循环队列是否已满 注意判断条件:(Q.rear+1)%MaxSize===Q.front
if((Q.rear+1)%MaxSize===Q.front){
// 循环队列满
return false;
}
// 队列未满,可进行入队操作【队尾进行】
// 队尾指针指向的数据域进行赋值
Q.data[Q.rear]=x;
//队尾指针后移+1【类似时钟的顺时针方向】
Q.rear=((Q.rear+1)%MaxSize);
// 入队成功返回true
return true;
}
```
#### 出队操作
```C++
/*
* @Description: 循环队列元素出队
* @Version: Beta1.0
* @Author: 【B站&公众号】Rong姐姐好可爱
* @Date: 2019-09-27 14:17:28
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2021-03-18 20:32:18
*/
bool DeLoopQueue(SqQueue &Q, ElemType &x){
// 判断循环队列是否为空队列
if(Q.rear==Q.front){
// 队列为空无法进行出队操作返回false
return false;
}
// 循环队列非空,元素可出队【队首操作】
// 将循环队列队首指针指向的元素的数据域赋值给变量x
x=Q.data[Q.front];
// 移动队首指针,顺时针后移+1
Q.front=(Q.front+1)%MaxSize;
// 出队成功返回true
return true;
}
```
#### 获取队头元素
```C++
/*
* @Description: 获取循环队列队头元素
* @Version: Beta1.0
* @Author: 【B站&公众号】Rong姐姐好可爱
* @Date: 2019-09-27 14:17:28
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2021-03-18 20:15:33
*/
bool GetLoopQueueHead(SqQueue &Q, ElemType &x){
// 判断循环队列是否为空队列
if(Q.front==Q.rear){
// 队列为空没有队头元素返回false
return false;
}else{
// 获取队头指针指向元素的数据域赋值给x
x=Q.data[Q.front];
// 获取队头元素成功返回true
return true;
}
}
```

View File

@@ -0,0 +1,240 @@
<!--
* @Description: 队列的链式存储结构
* @Version: Beta1.0
* @Author: 【B站&公众号】Rong姐姐好可爱
* @Date: 2021-03-19 08:22:39
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2021-03-20 16:10:16
-->
# 队列的链式存储结构
`链队列`:和顺序队列一样,基于队列的链式表示叫做`链队列`,实际上为:**一个同时带有队头指针和队尾指针的单链表**
- 头指针指向队头结点
- 尾指针指向队尾结点(单链表的最后一个结点)
这里复习下顺序队列的定义,进行比较学习
> 队列的顺序实现是指分配一块连续的存储单元用来存放队列中的元素,并且附加两个指针。
> - 队头指针指向队头元素
> - 队尾指针指向队尾元素的下一个位置
[//]: # (![]&#40;/数据结构/栈和队列/images/不带头结点的链式队列.png&#41;)
队列的链式存储结构:
```C++
// 链式队列节点定义
typedef struct{
// 结果点数据域
ElemType data;
// 结点指针域
struct LinkNode *next;
}LinkNode;
// 链式队列定义
typedef struct{
// front 队头指针rear 队尾指针
LinkNode *front,*rear;
}LinkQueue;
```
结合上面的`不带头结点`链式队列结构图,假设存在链式队列<LinkQueue,linkQueue>
- 队空: `linkQueue.front==NULL`且`linkQueue.rear==NULL`
- 出队: 先判断队列是否为空,非空队列则取出队头元素,从链表中闪出去,`队头指针Q.front指向下一个结点`如果出队的结此为尾结点出队后队空需要将Q.front和Q.rear都置为NULL
- 入队: 建立一个新的结点将新的结点插入到链表的尾部修改队尾指针Q.rear指向新插入的结点。如果原队列为空需要将队首指针也指向该结点
仔细思考上面的入队、出队操作都需要考虑队空的情况下的特殊处理不带头结点的队列导致队空队首和队尾指针都为NULL比较麻烦结合之前整理、学习过的单链表套用一下先贤的思路也整上一个头结点就会发现容易处理很多
![](/数据结构/栈和队列/images/带头结点的链式队列.png)
链式队列加上头结点后,之前较为复杂的入队、出队操作就统一起来了。
- 队空:`Q.front==Q.rear`,都指向头结点,一般数据域可以为空
- 出队:判断队列是否为空,队列非空则在队首移动指针,将队首指针指向下一个元素。如果队列中就一个元素,则出队后将成为空队,`Q.rear==Q.front`,最后释放元素内存空间。
- 入队:将元素插入队尾,移动队尾指针,即便为空队列入队,由于队列带有头结点,此时就很好的避免操作队首指针了。
特别注意:
- 用单链表表示的链式队列非常适合频繁出队、入队、元素变化大的场景
- 不存在队满情况,也不会出现溢出情况;
- 链式队列不会出现存储分配不合理、“溢出”的情况,内存动态分配
### 基本操作
> Tips: 基于带头结点链式队列的基础操作
#### 队列初始化
```C++
/*
* @Description: 链式队列初始化
* @Version: Beta1.0
* @Author: 【B站&公众号】Rong姐姐好可爱
* @Date: 2020-06-27 14:17:28
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2021-02-18 22:15:06
*/
voide InitLinkQueue(LinkQueue &Q){
// 创建头结点
Q.front=Q.rear=(LinkNode*)malloc(sizeof (LinkNode));
// 头结点的指针域指向的下一个结点为空
Q.front->next=NULL;
}
```
注意这个初始化操作,我第一次看的时候,非常不理解为什么在队首指针和队尾指针都指向已经创建好的头结点后,突然写一行`Q.front->next=NULL;`,后来的的理解是:
> 链式队列本质是基于单链表的队列那带头结点的链式队列其实强调的也就是单链表要带头结点。队列的队首指针和队尾指针确定指向单表的队首和队尾就ok初始化的时候带头结点的单链表实质就只有一个头结点。而此时的链式队列需要将队首指针和队尾指针指向单链表的头结点就行好像到这里就完了。但是这样却忽视了单链表只是注重的队列的front和rear指针。单链表的结点元素是分数据域和指针域的即便是头结点的数据域可以不存什么当然也常会存一些链表信息什么的,此处的`Q.front->next=NULL`就是用来处理链表的头结点的指针域的,让其指向下一个单链表元素为空,这里是非常需要处理的,非常细节!
#### 判断队空
```C++
/*
* @Description: 判断链式队列是否为空
* @Version: Beta1.0
* @Author: 【B站&公众号】Rong姐姐好可爱
* @Date: 2020-06-27 14:24:22
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2021-02-16 22:15:06
*/
bool IsEmptyLinkQueue(LinkQueue Q){
if(Q.front==Q.rear){
// 队首、队尾指针指向同一个结点内存地址,队空
return true;
}else{
// 非空
return false;
}
}
```
#### 入队
```C++
/*
* @Description: 链式队列入队操作
* @Version: Beta1.0
* @Author: 【B站&公众号】Rong姐姐好可爱
* @Date: 2020-06-46 14:17:28
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2021-02-18 22:15:06
*/
void EnLinkQueue(LinkQueue &Q, ElemType x){
//创建入队结点元素
node=(LinkNode *)malloc(sizeof(LinkNode));
// 赋值给结点数据域
node->data=x;
// 单链表中结点的指针指向下一个元素,为空
node->next=NULL;
// 队尾进队将结点node和队尾结点链接起来确保不断链
Q.rear->next=node;
// 修改队尾指针指向新入队的结点node
Q.rear=node;
}
```
#### 出队
```C++
/*
* @Description: 链式队列出队操作
* @Version: Beta1.0
* @Author: 【B站&公众号】Rong姐姐好可爱
* @Date: 2020-05-18 11:25:28
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2021-02-22 06:15:06
*/
bool DeLinkQueue(LinkQueue &Q, ElemType &x){
// 判断队列是否为空
if(Q.front==Q.rear){
// 队列为空,没有元素出队
return false;
}
// Q.front指向单链表的头结点出队需要出单链表的除头结点外的第一个结点头结点的下一个结点Q.front->next
temp=Q.front->next;
// 变量x赋值
x=temp->data;
// 修改单例表的头指针,指向链表中待删除元素的下一个元素
Q.front->next=temp->next;
// 队尾指针和队首指针的下一个结点重合,表示当前链式队列只有一个元素,删除后,队列为空
if(Q.rear==temp){
// 链表中队首、队尾指针重合,队列为空
Q.rear=Q.front;
}
// 释放删除元素的内存空间,注意,这里释放的是头结点的下一个结点
free(temp);
// 出队成功
return true;
}
```
出队的时候明显有些绕,需要明确队列中头结点的存在,出队出的是单链表中头结点的后一个结点,同时要确保整个过程`不断链`
![](/数据结构/栈和队列/images/链式队列出队.png)
### 双端队列
`双端队列`: 允许在两端都可以进行入队和出队操作的队列,元素的逻辑结构仍然是线性结构
![](/数据结构/栈和队列/images/双端队列.png)
**双端队列的两端分别称为`前端`和`后端`,两端都可以`入队`和`出队`**
- 进队:前端进的元素排列在队列中后端进的元素的前面,后端进的元素排列在队列前端进的元素后面;
- 出队:无论是前端还是后端出队,先出的的元素排列在后出的元素的前面
在双端队列的基础上,还衍生出:
- `输入受限的双端队列`:允许在一端进行插入和删除操作,但在另外一端只允许进行删除的双端队列
- `输出受限的双端队列`:允许在一端进行插入和删除曹组,但在另外一端只允许进行插入的双端队列
当然,如果`限定双端队列从某个断点插入的元素只能从该端点删除`,那么此时的双端队列就演变为两个栈底相邻的栈了。
### 队列的补充
- 最适合用来链队的链表是:`带队首指针和队尾指针的非循环单链表`
- 栈和队列的逻辑结构都是线性表,存储结构可能是顺序的(顺序栈、顺序队列),也可能是链式的(链栈、链队)
- 不论是顺序存储还是链式存储,**栈和队列都只能进行顺序存取(本质是线性表)**。数组是可以做到随机存取(本质是顺序表)
- 队列先进先出的特性:`先进队列的元素先出队列,后进队列的元素后出队列`
特别注意:
队列用链表形式存储时,删除元素,需要从队头删除,一般情况下,仅仅修改头指针,但是如果此时队列中
仅有一个元素,则尾指针也是需要被修改的。**因为队列中只有一个元素时,删除后队列为空,需要修改尾指针为:`rear=front`**

View File

@@ -0,0 +1,138 @@
<!--
* @Description: 栈和队列的应用
* @Version: Beta1.0
* @Author: 【B站&公众号】Rong姐姐好可爱
* @Date: 2021-03-14 21:48:49
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2021-03-14 21:49:02
-->
# 栈和队列的应用
> Tips: 这里不会做过多文字介绍相关应用,具体需要自己看书、查资料揣摩
### 栈的应用
- 括号匹配
- 表达式求值
> 表达式求值是程序设计语言编译中一个最基本的问题,类似有前缀、中缀、后缀表达式转换等典型问题。
- 递归
这里重点总结下递归,递归非常重要,是一种很经典的程序设计方法
> 递归的简单定义: 如果在一个函数、过程或数据结构的定义中又应用了自身,那么这个函数、过程或者数据结构称为递归定义的,简称递归。
递归通常把一个大型的复杂问题,层层转化为一个与原问题相似的规模较小的问题来求解。
**递归策略只需要少量的代码就可以描述出解题过程所需要的多次重复的计算,很大程度上减少了程序的代码量,当时通常情况下,递归的效率并不是很高**
经典的斐波拉切数列,可以用递归来实现:
```C++
// 定义递归函数,实现斐波拉切数列
int Fibonacci(n){
if(n==0){
return 0;
}else if(n==1){
return 1;
}else{
// 调用Fibonacci函数进行递归
return Fibonacci(n-1)+Fib(n-2);
}
}
```
上面很基础的代码,是分`n=0`和`n=1`的情况,先进行过滤,其他情况下则进行递归,其实在日常开发中,经常会有简化的函数封装
```C++
// 定义递归函数,实现斐波拉切数列
int Fibonacci(n){
if(n<2){
return n;
}else{
// 调用Fibonacci函数进行递归
return Fibonacci(n-1)+Fib(n-2);
}
}
```
上面的简化则需要程序考虑变量n是整数。
当然,前面有说道:**通常情况下,递归的效率并不搞**,主要原因就是递归通过返回调用自己本身,导致往往时间复杂度较高。
可以采用空间换时间的思路,来降低算法的时间复杂度
```C++
// 使用非递归实现
int Fibonacci(int n) {
if(n < 2)
return n;
int f = 0, g = 1;
int result = 0;
// 迭代
for(int i = 1; i < n; i++){
result = f + g;
f = g;
g = result;
}
return result;
}
// 利用数组,空间换时间
int Fibonacci(n){
// 注意溢出
int arr[100]={0,1,1}
// 叠加,结果存放在数组中
for(let i=3;i<=n;i++){
arr[i]=arr[i-1]+arr[i-2]
}
return arr[n]
}
```
必须注意递归模型不能是循环定义的,满足条件:
- 递归表达式(递归体)
- 边界条件(递归出口),即:算法结束条件
递归的精髓在于是否能够将原来的问题转化为属性相同但规模较小的问题。有点类似实现目标过程中将大目标转化为小目标1亿....)来解决,噗呲啊哈哈哈
**递归次数过多容易造成栈溢出,效率不高的主要原因是递归调用过程中包含很多重复的计算**
### 队列的应用
- 层次遍历
> 例如:二叉树的遍历
- 计算机系统
队列在计算机系统中的应用非常广泛,作用:
- 解决主机和外部设备之间速度不匹配的问题(例如:内存和打印机)
- 解决由多用户引起的资源竞争问题(例如:操作系统中的进程调度...
其实,队列在计算机系统的中应用, 在看完操作系统那本书后,就会很好理解,建议学到这里,也去翻翻操作系统,汤晓丹那本很经典哟...

View File

@@ -0,0 +1,67 @@
<!--
* @Description: 特殊矩阵的压缩存储
* @Version: Beta1.0
* @Author: 【B站&公众号】Rong姐姐好可爱
* @Date: 2021-03-20 16:18:51
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2021-03-20 17:04:44
-->
# 特殊矩阵的压缩存储
> 这部分知识我个人觉得以了解为主,复习、学习的时候还是要以前面的部分为主!
矩阵在`计算机图形学``工程计算`中占有举足轻重的地位。
### 数组的定义
`数组` 由n(n≥1)个相同类型的数据元素构成的有限序列。
每个数据元素称为一个数组元素同时每个元素受n个线性关系的约束**每个元素在n个线性关系中的序号称为元素的`下标`**称为该数组为n的数组。
数组和线性表的关系:
- 数组是线性表的推广。
- 数组一旦被定义,维数和维界就不再改变。
- 除了结构的初始化和销毁外,数组只会有存取元素和修改元素的操作。
一维数组可以看做是一个线性表
二维数组可以看做元素是线性表的线性表
....
### 矩阵的压缩存储
`压缩存储`:多个值相同的元素只分配一个存储空间,对零元素不分配存储空间---->节省存储空间。
`特殊矩阵`:具有很多相同矩阵元素或零元素,并且这些相同矩阵元素或零元素的分布有一定规律性的矩阵。
- 对称矩阵
- 上、下三角矩阵
- 对角矩阵(带状矩阵)
- ....
这里如果学过线性代数这本书,其实也就很好理解(赶紧去把数学知识捡起来鸭,噗呲哈哈啊哈)
`稀疏矩阵`矩阵元素个数s相对于矩阵中非零元素的个数t来说非常多、差距非常大`s>>t的矩阵`可以叫`稀疏矩阵`
注意:
- 常规方法来存储稀疏矩阵,会想当浪费存储空间,所以稀疏矩阵只需要存储非零元素
- 通常非零元素的分布是没有规律的,除了存储非零元素外,还需要存储元素所在位置的行和列
- 寻相互存储三元组 `<行标,列表,值>`
![](/数据结构/栈和队列/images/稀疏矩阵的存储变换.png)
三元组的结点存储了行标(row)、列表(col)、值(value)三种信息,是主要用来存储稀疏矩阵的一种数据结构。
**注意:矩阵压缩存储的目的就是为了节省空间,已经存过的就不存或者少存(经典想法)**

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -0,0 +1,24 @@
<!--
* @Description: 数据结构-栈和队列
* @Version: Beta1.0
* @Author: 【B站&公众号】Rong姐姐好可爱
* @Date: 2021-03-22 08:02:29
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
* @LastEditTime: 2021-03-22 08:02:43
-->
# 栈和队列
### 主要内容
- [栈的基本概念和基本操作](1.栈的基本概念和基本操作.md)
- [栈的顺序存储结构](2.栈的顺序存储结构.md)
- [栈的链式存储结构](3.栈的链式存储结构.md)
- [队列的基本概念和基础操作](4.队列的基本概念和基础操作.md)
- [队列的顺序存储](5.队列的顺序存储结构.md)
- [队列的链式存储](6.队列的链式存储结构.md)
- [栈和队列的应用](7.栈和队列的应用.md)
- [特殊矩阵的压缩存储](8.特殊矩阵的压缩存储.md)
![](栈和队列_水印.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@@ -0,0 +1,2 @@
# 计算机网络思维导图

View File

@@ -0,0 +1,3 @@
# 操作系统
doing

View File

@@ -1,2 +0,0 @@
## 计算机网络思维导图

View File

@@ -1,6 +0,0 @@
---
title: 操作系统
---
## 操作系统
doing

View File

@@ -159,68 +159,99 @@ yarn run dev
### 提交记录
---
## 赞赏列表
### 赞赏列表
以下排名不分先后! [详细统计]()
以下排名不分先后! [传送门→]() **赞赏过的一定要微信跟我说呀!!!!!!**
<div>
<a href="https://github.com/ChiefPing" target="_blank">
<a href="https://github.com/ChiefPing" target="_blank" style="margin: 5px">
<img src="https://avatars2.githubusercontent.com/u/34122068?s=460&v=4" width="50px" style="brder-radius:5px;"/>
</a>
<a name="gzh"></a>
<a href="https://github.com/xiaoliuxin" target="_blank">
<a href="https://github.com/xiaoliuxin" target="_blank" style="margin: 5px">
<img src="https://avatars2.githubusercontent.com/u/60652527?s=460&v=4" style="border-radius:5px;" width="50px"/>
</a>
</div>
### 赞助商
## 赞助商
以下排名不分先后! 哈哈哈,还木有收到赞助,先留坑
**以下排名不分先后! 还木有收到赞助,哈哈哈,先留坑**
---
### 联系作者
<div class="open-info-div">
<!-- <a href="#gzh" target="self_blank"><img src="https://img.shields.io/badge/WeChat-公众号-5wd.svg"></a>
<a href="#wechat" target="_blank"><img src="https://img.shields.io/badge/WeChat-微信-yellow.svg"></a> -->
<a href="https://space.bilibili.com/350937042" target="_blank"><img src="https://img.shields.io/badge/Bilibili-B站-green.svg"></a>
<a href="https://142vip.cn" target="_blank"><img src="https://img.shields.io/badge/142vip-网站-orange.svg"></a>
<a href="https://blog.142vip.cn" target="_blank"><img src="https://img.shields.io/badge/blog-博客-blue.svg"></a>
<a href="https://github.com/mmdapl" target="_blank"><img src="https://img.shields.io/badge/Github-Github-9ac.svg"></a>
<a href="https://gitee.com/mmdapl" target="_blank"><img src="https://img.shields.io/badge/Gitee-码云-4ed.svg"></a>
<a href="https://blog.csdn.net/Mmdapl" target="_blank"><img src="https://img.shields.io/badge/csdn-CSDN-8ea.svg"></a>
<a href="https://juejin.im/user/448256476724807" target="_blank"><img src="https://img.shields.io/badge/JueJin-掘金-75c.svg"></a>
## 联系作者
若系列文章对你有所帮助,欢迎订阅公众号或微信”骚扰“,获取更多内容。**商务合作请备注来意**
<div style="text-align: center">
<div align="center" >
<table style="border:none;cell-padding:0; cell-spacing:0;border-collapse:collapse;" border="0">
<img src="https://cdn.staticaly.com/gh/142vip/cdn_service@main/media/fairy-sister-450x450.jpg"
width="250px"
title="欢迎关注公众号:Rong姐姐好可爱" alt="关注公众号"/>
<img src="https://cdn.staticaly.com/gh/142vip/cdn_service@main/media/chu-fan-443-650x650.jpg"
width="250px"
title="欢迎添加微信chufan443 " alt="联系作者"/>
</table>
</div>
<div style="text-align: center;padding: 10px" align="center">
<a
href="https://github.com/mmdapl"
rel="nofollow noreferrer"
target="_blank"
title="点击跳转Github主页"
>
<img src="https://cdn.staticaly.com/gh/142vip/cdn_service@main/main-vip/svg/github.svg"
style="margin: 5px;width: 24px;height: 24px;">
</a>
<a
href="https://gitee.com/Mmdapl"
rel="nofollow noreferrer"
target="_blank"
title="点击跳转码云主页"
>
<img src="https://cdn.staticaly.com/gh/142vip/cdn_service@main/main-vip/svg/gitee.svg"
style="margin: 5px;width: 24px;height: 24px;">
</a>
<a
href="https://juejin.im/user/448256476724807"
rel="nofollow noreferrer"
target="_blank"
title="点击跳转掘金主页"
>
<img src="https://cdn.staticaly.com/gh/142vip/cdn_service@main/main-vip/svg/juejin.svg"
style="margin: 5px;width: 24px;height: 24px;">
</a>
<a
href="https://space.bilibili.com/350937042"
rel="nofollow noreferrer"
target="_blank"
title="点击跳转B站主页"
>
<img src="https://cdn.staticaly.com/gh/142vip/cdn_service@main/main-vip/svg/bilibili.svg"
style="margin: 5px;width: 24px;height: 24px;">
</a>
<a
href="https://blog.csdn.net/Mmdapl"
rel="nofollow noreferrer"
target="_blank"
title="点击跳转CSDN博客主页"
>
<img src="https://cdn.staticaly.com/gh/142vip/cdn_service@main/main-vip/svg/csdn.svg"
title="点击跳转CSDN博客主页"
style="margin: 5px;width: 24px;height: 24px;">
</a>
</div>
</div>
若系列文章对你有所帮助,欢迎订阅公众号,获取更多内容。也可微信”骚扰“,商务合作请备注来意
<!-- <div align="left">
<img src="https://cdn.jsdelivr.net/gh/lir0115/images@main/qr_code/wechat_code.jpg" width="300" height="300" style="border-radius:5px;"/>
</div> -->
<a name="gzh"></a>
<p>
<img src="https://cdn.jsdelivr.net/gh/lir0115/images@main/qr_code/gongzhonghao.jpg" style="border-radius:10px;">
</p>
交流/加群/互看朋友圈
当然:**聊天 /提问 /建议 /提需求** 可以在本公众号直接**私信**,后台可以看到,有时间即会回复,偶尔的延迟和疏漏还请小伙伴们谅解,蟹蟹。
### 外链