- 博客(39)
- 收藏
- 关注
原创 Linux权限
在Linux中,一切皆文件,因此对文件权限的理解,便是对Linux的权限理解。开始先对用户有个理解,用户有两种一种事超级用户 root,是#号提示符,普通用户,$提示符。文件对于操作来说有三个对象,拥有者,所属组,和其他人,在ll显示文件的情况,第一行的-标识文件,如果是d就是目录,然后有九行,分别标识三种对应的人,拥有者,所属组,其他人的权限,有三种权限,r读,w写,x执行。然后,后面是文件的拥有者,所属组,文件大小,修改时间,文件名chmod命令修改权限,u/g/o,分别表示user拥有者,gr
2021-06-07 11:41:18 113
原创 平衡搜索二叉树~AVL树
作为搜索结构,二叉搜索树有其高效的搜索效率,但是同时也有着其本质的缺陷,就是在数据呈现顺序的时候,效率低下,因此不适合大多数的数据存储,但是如果将其修改,成为一个平衡树,结合两者特点,就可以完成高效的查找,因此我们学习平衡搜索二叉树~AVL树AVL树的特点是,左子树和右子树的高度差不超过一,也就是说,左右子树的高度最多差一,并且要保证所有的子树都满足这个条件,因此分部均匀,且具有相对优秀的效率。插入和删除都是二叉搜索树的方式,但是在这之后,需要进行平衡的旋转工作,因此我们一般是随着节点往上走来调节平衡,
2021-06-07 11:00:25 132 1
原创 Linux命令
首先在认识Linux前,我们先要知道,Linux是一款操作系统,操作系统是一款用来管理的软件,管理软件和硬件,Linux的开源操作系统,一定是安全高效的,因为有许多人在对其进行改进,漏洞就很少。而对Linux的操作是使用命令行界面的,一般我们的普通电脑都是使用gui的图形化界面,使用点击的方式进行操作,但是Linux是命令行界面,因此需要输入指令来进行操作,因此首先学习Linux就得学习Linux的命令行操作。ls -a -l命令后可以加参数,会呈现出不同的效果,...
2021-06-06 18:41:09 112 1
原创 哈希对于大数据的处理
哈希对于大数据的处理一般借助位图和布隆过滤器。首先我们先对于这两种数据结构进行应用。第一:大数据整数,找只出现一次或者两次或者三次的小次数重复的数。如何寻找?我们借助于map统计次数的思想,并加以对位图进行改装,使用两个位图进行操作。底层封装两个位图,一个位表示存在,另一个位标识是否出现两次或者以上。比如01表示一次,10表示两次以上,而更多的有限次就可以使用这种方式来表示,其本质的思路,还是利用较小的空间表示更多的状态,还是一种抓特性特点的处理数据方式。#include "bitset.h"usi
2021-06-06 17:27:25 523
原创 布隆过滤器
位图在优点上,效率高效,并且具有空间小的优点,但缺点很明显,只能处理整型,因为其属性全部是描述整型的,因此在处理日常生活中很多的字符串上,就很有必要了,,因此将位图进行改造,变成布隆过滤器,就可以解决这个问题。首先我们的思路肯定是,将字符串变成整型,这是毋庸置疑的,哈希的字符串算法很多,所以这点不是很难,但是问题是,在位图中,ps:这里的整型都是无符号的,整型是具有唯一的属性,因此它是一一映射,所以当你找到的时候是一定存在的,但是字符串无论使用什么算法,都可能导致重叠,因为对属性的描述不具有唯一性。因此字
2021-06-06 10:50:09 101
原创 哈希应用:位图
哈希在很多方面都有着其特殊的作用,今天我们来介绍两种关于哈希的应用方面,对于大数据的处理。第一 位图如果说,要在4000亿的无符号整型中,找存在的那个数,该如何处理呢,明显是key的模型,找一个数是否存在,但是内存不够啊,1G是10亿的数据,那么4000亿的话是400G,还要有整形4个字节,总来说要1600G,服务器能有这么大吗?肯定是有这么大的服务器的,但是呢不能全去处理这个数据吧,因此对于大数据的处理,对于空间的要求就比较的高了,我们一方面要考虑数据的查找效率,还有空间的储存可能。因此根据哈希的
2021-06-05 23:19:14 121 1
原创 unordered_map set的模拟实现
设计容器时,哈希注重的是如何处理哈希冲突,显然,开散列的处理方式要优于闭散列,因此使用开散列处理哈希冲突,模拟实现的本质是对迭代器如何处理,因为其他的接口都是很好完成的,但是迭代器如何形成遍历的方式呢?在封装迭代器前,我们先思考一个问题,如何进行类型的不同调整,因为底层是用除留余数法进行映射的,但是除了整形,还有什么能取余呢,因此需要对不同的类型进行不同的转换,我们只是模拟实现,因此处理最多使用的string类,使用模板的特化,对string类进行特殊处理,这样将其转换成对应的整形,这里也要使用一下仿函数
2021-06-05 21:37:39 106
原创 哈希与哈希冲突
哈希本质上是一种映射关系,利用映射关系进行快速查找,它的查找效率可以到O(1)的程度,因此在c++11中引进了unordered_set和unordered_map来进行使用,因为在大数据下,map和set的查找效率不是很高,所以引进了以哈希为底层结构的关联容器。怎么理解哈希的映射呢?哈希的底层是数组,因此其实更多的是对于数组能够随机访问的支持,将数据以数组下标的形式进行一一对应,那么直接用数组下标进行访问数据,查找的效率岂能不快?但是上述做法是名为直接定制法的哈希函数,一般我们都是使用除留余数法来进行
2021-06-04 21:06:53 135
原创 map和set的使用
map和set是关联式容器,它们的底层的实现是都是红黑树,只是它们使用的模型不同,一个是key模型,一个key-value模型,之所以是关联性容器,是因为它们的数据存放是与其特性有关联性的,这里我们简单讲一下使用,因为其使用的方式和其他容器的区别不是很大。set,因为是二叉树的结构,所以只有insert和erase,删除的是可以用迭代器来删也可以用值来删,如果要用迭代器,就要使用find函数,来查找,也可以用algorithm中的find函数,但是效率上,前者是logn后者是n,set容器的主要使用是判断
2021-05-23 11:48:36 178
原创 二叉搜索树
二叉搜素树是基于二叉树的结构,给予了一些特性的二叉树,它具有左节点比根小,右节点比根大的特性,这让可以让搜索一个值的时间复杂度到达log(n),这里二叉搜索树有两种模型,一是key模型,只有一个键值,它可以进行一个值的搜索,在不在,比如门禁卡,一扫就会匹配这个是不是用户,就用的这个模型,还有一种,key-value的模型,通过一个键值来匹配相对应得意义,这里也有统计次数的作用。因为在有序或者接近有序的时候,二叉搜索树的效率很低,所以模型的使用都建立在平衡二叉搜索树上面,因此二叉搜索树重点就是插入和删除的代码
2021-05-23 10:55:23 59
原创 多态的实现
首先要了解多态的底层,我们需要了解一个概念:虚函数表虚函数表是一个函数指针的数组,它存放着虚函数的指针,可以用来调用虚函数,在构造虚函数时候,会将虚函数的地址放入虚表中,而在对象中又会存有一个虚表指针,指向虚表,子类继承父类,会将父类的虚表copy继承,然后重写虚函数,将重写后的虚函数新地址覆盖掉,原来父类虚函数的地址,这样就完成了多态,所以重写也叫覆盖,这样我们通过父类的指针访问子类,子类将父类的成员切片给父类,也包含了虚表指针,而虚函数的调用是由虚表指针来调用的,所以这就和对象有关,而不是和类型有关。
2021-05-15 12:18:13 88
原创 多态的语法
多态作为面向对象的三大特性之一,它主要表现是利用父类的指针引用,可以指向子类的函数,实现父类指针的多种形态,既能指向父类,亦可指向子类,这是一种泛型技术,是为了用尽量少的代码逻辑,来实现多的场景应用,模板技术也是如此。今天我们来说说,多态的一些事。我们用生活中的实例来解释一下多态的应用,比如你要买票,那么普通人一个价,学生票一个价,那么同样是买票这个行为,不同的人做有不同的效果,多态就是为了,让不同的类做同一个事,但是产生的效果却不同,在实现方面就是,调用了同一个函数,即做了同一个事,但是实现方式不同,即
2021-05-15 11:36:53 212
原创 继承
继承是面向对象的三大特性之一,继承是类复用的一个手段,如果要定义重复的成员变量或者函数,就可以继承。class A{};class B : public A{ };一般像这样使用,类名加冒号继承方式加继承对象。一般被继承的叫父类或者基类,继承的叫子类或者派生类,一般情况,子类是具有父类全部的成员变量和函数,并且自己可以扩展。继承的访问方式变化:子类继承父类的私有成员是不可见的。不可见即不可访问,如类里的私有成员对类外就是不可见的。因此保护成员和私有成员在不继承的情况下是一样的,但继承
2021-05-14 10:10:16 89
原创 模板进阶
模板可以针对某些类型进行特殊化处理:模板的特化 template<> bool isequal <char*>(char* p1,char* p2) { return strcmp(p1,p2) == 0; } 如果参数固定,且不需要别的参数,就给空模板,否则还要给模板参数。类的模板的特化是在类名后面,这里有偏特化和全特化之区别。...
2021-05-13 08:43:58 68
原创 优先级队列
stl中,有优先级队列这样一个适配器,priority_queue,这个容器是将数据放入,然后按特定优先级排序,默认是大的优先级高。通常我们使用的接口有,size top push pop empty。那么它的底层是什么呢。是一个堆,默认是大堆,将最大的数一个个选上来,首先将数据插入,然后用向上调整算法进行调整。删除是将顶的和最后一个数据交换,然后删除,最后向下调整,为什么这样做呢,因为优先级队列适配的容器是vector,因此保证效率就需要尾部删除。namespace whc{ template&l
2021-05-12 17:05:47 65
原创 STL的栈和队列
在stl库中,栈和队列是有其相对应的库的,所以我们不必再去写栈和队列,直接可以用库里的就行了,但是栈和队列在库中是如何实现的呢?让我们走进他们吧!首先我们可以了解一下使用,使用基本没啥差别,stack中有栈顶的插入删除,有大小,是否空等接口。push 栈顶插入pop 栈顶删除size 大小empty 判空top 栈顶数据queue中只有取数据的时候,是front 和back,队头和队尾的数据。了解完使用后,我们可以去想想,既然我们在C语言中实现它们是用数组和链表的,是不是stl中也是这样的
2021-05-12 12:35:17 585
原创 list
list是为了弥补vector的缺陷而设计出来的数据结构,vector有两个缺点:一 头部或者中间插入,效率低,因为需要挪动数据。二 增容开新空间,释放旧空间,会付出很大代价。但是vector也有个不容质疑的优点,就是随机访问,这使得它支持了很多操作。所以list的优点就是vector的缺点,但是list的缺点却是vector的优点。两者相辅相成。构造: list t; list(n,val); list(t); 可以直接构造空的,也可以构造n个val值,拷贝构造也可以。遍历无【】
2021-05-10 11:56:04 59
原创 vector的实现
vector底层中,是使用了指针来作成员变量的,利用指针的差值,来表示大小容量,那么要警惕指针在增容时候的变化。namespace whc{ template<class T> class vector { public: typedef T* iterator; typedef const T* const_iterator; vector() :_start(nullptr) , _finish(nullptr) , _endofstorage(
2021-05-08 21:51:00 140
原创 栈和队列
栈和队列是基本的数据结构,是对于线性表的一种拓展,其实个人理解就是限制了顺序表和链表的一些操作,使其在一些特定的场合可以使用。栈,stack,first in last out,先进后出,这是栈的基本特性,它犹如一个竖着的柜子,只能从栈顶出入,就像你将衣服放进柜子中,一定是最后放的最后才能取出来,当然如果你是个规则的人的话,因此当我们学习了顺序表和链表的操作后,对于实现栈,就不那么困难了,因为它只是将顺序表改造了一下。栈的实现底层链表和数组都可以,但是因为其在尾删尾插的特性,显然,数组更加契合其属性。
2021-05-08 21:29:24 134
原创 vector使用
vector的使用与string的使用是基本一样的,底层是实现上,都是用数组来进行底层的实现,因此,基本的接口使用都几乎无差别,实质上,vector就是一个顺序表。那么vector和string类型有何区别呢?在实现上,vector是没有‘\0’的,而string有,并且在具体的操作上,也有着不同的区别。而在使用上string一般是存单项信息的。构造: vector<int>v1; vector<int>v2(v1); vector<int>v3(4,100);
2021-05-08 12:59:17 135
原创 string的实现
一般来说,为了防止与标准库中的string冲突,我们都会开一个命名空间。构造函数,如果用最基本的构造函数,会怎么样 class string { public: string() :_str(nullptr) {} string(char* s) :_str(s) { }这样子初始化的常量字符串,不能被修改,被存放再代码段,且扩容也不好处理。因此构造时候,不能直接传,要用new来动态开辟空间储存,因此其实string的底层是数组。 string(const cha
2021-05-07 11:27:48 236
原创 string的基本函数使用
string是c++中常见的一个使用类,因为对字符串的操作的频繁性,string类的重要性是不言而喻的,那么了解string类,首先我们从对它的使用开始。构造:一般情况下,构造的使用是用字符或者字符串去初始化的,...
2021-05-06 17:50:36 290
原创 模板与STL
C语言中,如果写一个交换函数,那么类型不同的交换,就要写不同的交换。那么一个简单的交换函数,不仅要求名字不同,还需要写多个。这种处理的方式,不仅使得代码难写,调用起来也不方便,效率也不高。所以c++出现了泛型编程,来编写出模板根据模板自动推导出函数这样子大大简化了对于数据类型要求。template<class T>template<typename T>void swap(T& a,T& b){ T tmp = a; a=b; b=tmp;}
2021-04-29 11:42:56 58
原创 c++内存管理
、C语言中,我们用malloc,realloc,calloc,free四种种函数来进行内存管理,而在c++中则用new和delete来进行内存开辟和管理。 int* p1 = new int; int* p2 = new int(10); int* p3 = new int[10]; delete p1; delete[] p3;如果要开辟一个空间则使用p1方式,如果开辟一个连续的空间,则用p3的方式,如果要释放则需要相对应的格式。后面加小括号是初始化的方式,对应其构造函数。但是为何要写
2021-04-29 09:59:32 55
原创 类和对象
c++相对于c来说,是面向对象的语言,相对于c更注重于解题的过程,c++更喜欢去将解题过程来做成一个模板,去探究不同题的解题方法,这样去研究。所以c++提出了类和对象的概念,将所定义的数据,与数据的处理方式,放入一个类中,封装起来,那么这个类就是一个抽象的类别,如同人这个类,而每个人就是类的实例化,而人是由什么组成的呢,那就是成员变量,对这些组成部分该怎么操作,那就是对身体机能的操作,是成员方法,这便是封装的含义,不可能说是将各个组织器官暴露出来,这样每个人都能去操作别人的组织器官,那不乱套了吗?那么类
2021-04-26 17:44:25 67
原创 顺序表与链表
顺序表和链表都是最最基本的数据结构,顺序表是数组形式来实现的,具有连续的物理结构,与逻辑结构。而链表则是在逻辑上连续,物理空间上不连续。顺序表,利用动态开辟数组空间来实现。那么需要实现的接口有哪些呢。#pragma once#include <stdio.h>#include <stdlib.h>#include <assert.h>typedef int data;typedef struct seplist{ data* a; size_t s
2021-04-24 11:28:51 107
原创 c++基础语法
第一 命名空间 :namespace命名空间是c++为了处理命名冲突的一种方式,定义一个命名空间后,用空间名 ::变量 ,来访问里面的变量。这样可以在工程项目中,防止一些命名冲突所引发的错误,但是在我们具体练习中,这样的使用场景并不多见。命名空间也可以使用展开方式来操作,这样子使得使用变量时,是其命名空间中的变量定义,等于将命名空间中作用域放大到了全局。使用方法有,using N :: n; 展开命名空间N中的n变量using namespace N; 这样子的展开就是将整个命名空间全部展开成全局。像
2021-04-22 21:37:04 54
原创 大数据的文件归并排序
试想一个场景,如果一份很大很大的数据需要排序,如果你没有足够的内存放入数据,这样子针对内存的内部排序是无法完成的,所以我们必须依靠磁盘去解决这个问题。第一 将数据分成若干份(我们这里以10份为例子),每份都接近内存的最大数据量,这样子,将文件上的数据读入内存中,将它排序,结束后我们就可以得到10份有序的文件数据了。const char* MergeFile(const char* file){ FILE* fout = fopen(file, "r");//传出文件名,打开此文件 if (fout
2021-04-22 09:05:56 685
原创 计数排序
计数排序,是一种线性排序,在一定条件下可以使用并且时间复杂度为O(N+K),可以看出时间是非常快的,但是因为其使用条件的限制,所以其实并不流行。基本的思想是,给定一个范围,然后统计一组数据出现的次数,放入到开的范围数组中,然后遍历范围数组,将其一个个的拷到原数组中。void CountSort(int* a, int n){ int max = a[0]; int min = a[0]; for (int i = 1; i < n; i++) { if (a[i] > max
2021-04-20 21:40:26 86
原创 华山论“序”
华山之上,武林四大门派,决定一决高下,北方插入派宗师希尔,先来于华山,宗师希尔,传承于插入门派,讲原先内功心法改至化境,终成一代宗师插入排序,起源已久,其根本思想是将无序插入有序,比较相邻的元素。如果第一个比第二个大,就交换它们两个,对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数。虽然排序相比而言,最好情况是在已经有序的情况下,存在O(N)时间复杂度,但是最坏依然是,O(N*N)的时间复杂度。O(1)的空间复杂度InsertSort(int* a, int
2021-04-19 18:06:44 50
原创 二叉树的实现
二叉树是以递归的方式进行定义的,所以构建的方式,给予特定数组,将空指针进行标识,最后利用其提供的排序方式进行构建。数组为 char a[100]=“ABC##DE#G##F###”;‘#’ 为 NULL这里提供前序遍历构建方法BTNode* BinaryTreeCreate(BTtypedata* a, int* pi){ if (a[*pi] == '#') { return NULL; } else { BTNode* root = (BTNode*)malloc(sizeo
2021-04-13 15:54:15 45
原创 二叉树的前序遍历(返回形式为数组)
先将二叉树节点计算出来int TreeSize(struct TreeNode* root){ if(root==NULL) return 0; return 1+TreeSize(root->left)+TreeSize(root->right);}如果想返回一个数组,那么前序遍历函数必须具有,数组的地址,下标,和被遍历的二叉树三个参数void prevorder(struct TreeNode* root,int* retarr,int* pi)
2021-04-13 11:40:53 541
原创 判断平衡二叉树
给定一个二叉树,判断它是否是高度平衡的二叉树。本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1显然,需要一个判断高度的函数,判断左子树和右子树的差是否在1内,判断完根后,在去判断以左右子树的根节点,各子树的高度,如果所有节点都满足,则成立。int TreeHigh(struct TreeNode* root){ if(root==NULL) return 0; int high1 = TreeHigh(root-&
2021-04-13 11:29:24 46
原创 判断镜像二叉树
1 / \ 2 2 / \ / \ 3 4 4 3这种树是镜像二叉树,可看出来,每次递归需要多一次的判断,比如第一次是根的判断,第二次就要判断左子树,和右子树,第三次就要判断左子树的左子树和右子树的右子树,与左子树的右子树和右子树的左子树。可见,判断次数是增加的,所以递归是两次的递归判断流程也是如此bool _isSymmetric(struct TreeNode* p,struct TreeNode* q)//判断两颗树相同,但是判断的是对称的子树{ ...
2021-04-13 11:23:46 348
原创 Top k 问题的解决
情景概况 :如果你要去找网络游戏中排名前10的选手,那么从大基数中寻找前10,没有必要将所有基数排序进行寻找,这样效率太低所以有Top k问题的解法。解法一建立10个数的小堆,然后遍历基数,如果遇到大于根的数,则将根和此数交换,然后再次调堆,重复至遍历完毕,则找到了Top k。空间复杂度O(k) 时间复杂度O(k+logN*(N-k))void Topk(int* a, int n, int k){ Heap hp; HeapInit(&hp, a, k); for (int i
2021-04-08 20:21:55 86
原创 堆排序算法
堆排序本质是利用堆的特性,进行多次对最小数字的选取。升序是大顶堆降序是小顶堆将顶部的数与最后一个叶子交换,然后形成新的二叉树,进行向下调整,向下调整时,将上一个已经排好的数,忽略掉,即数组下标访问的位数减一,这样子就能获得新的最小数,依此类推。这样子,将0下标的和8下标的交换,然后再次进行向下调整时候,将数组大小减一,这样不会访问到最大(小)的数,然后循环往复。//堆排序算法 时间复杂度O(N*logN)void Swap(int* a, int* b){ int tmp = *a;
2021-04-07 21:13:34 47
原创 小程序进度条
首先我们需要了解缓冲区的概念,一般来说,我们都是行缓冲的,就是如果不满一行,我们所编写的代码执行结果不会输出到显示屏上(需要你用sleep函数去体会)一般我们有换行符\n所以当碰到\n时,执行结果会直接显示出来,\r表示回车,所以每次执行时会返回到行首。利用这两个关系,去实现进度条的动态化void pbar() 4 { 5 int i=0; 6 char bar[101]; 7 memset(bar,'\0',sizeof(bar)); 8 const char
2021-04-06 21:52:54 167
原创 循环列表的实现(数组方式非动态)
循环队列的实现,首先需要一个空的缓冲器,假设有k个空间,若只给k个空间,那么何时是满,何时是空呢?所以此时就需要一个空间来缓冲这便是基本的设计思路,删除就是front往后移,插入就是rear往后移,但每次都要判空和取余,防止越界的发生。typedef struct { int* a; int front; int rear; int k;} MyCircularQueue;bool myCircularQueueIsEmpty(MyCircularQueu
2021-04-06 21:40:23 189
原创 数组的加法
对于非负整数 X 而言,X 的数组形式是每位数字按从左到右的顺序形成的数组。例如,如果 X = 1231,那么其数组形式为 [1,2,3,1]。给定非负整数 X 的数组形式 A,返回整数 X+K 的数组形式。这种题型由于数字太大,一般来说字节的储存空间是不够的,所以利用竖式加法进行。int* addToArrayForm(int* A, int ASize, int K, int* returnSize){ int i = K;int ksize = 0;int k = K;while
2021-04-06 21:10:16 1733
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人