- 数据结构 :哈希表、堆、栈、队列、链表、二叉树
- 操作系统(iOS)的堆、栈
- 算法 :排序、冒泡、快排、二分查找
数据结构
哈希表(Hash table)
哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
哈希表hashtable(key,value) 就是把Key通过一个固定的算法函数既所谓的哈希函数转换成一个整型数字即哈希值,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将value存储在以该数字为下标的数组空间里。
而当使用哈希表进行查询的时候,就是再次使用哈希函数将key转换为对应的数组下标,并定位到该空间获取value,如此一来,就可以充分利用到数组的定位性能进行数据定位。
NSDictionary就是哈希表
堆(heap)、栈(stack)和队列(queue)
堆是一类特殊的数据结构的统称堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:
1、堆中某个节点的值总是不大于或不小于其父节点的值;
2、堆总是一棵完全二叉树。
栈是限定仅在表尾进行插入和删除操作的线性表, LIFO。
队列是只允许在一端进行插入操作,在另一端进行删除操作的线性表,FIFO。
链表是一种线性表,但是并不会按线性的链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
树是一种数据结构,它是由n(n>=1)个有限结点组成一个具有层次关系的集合。
树具有的特点有:
(1)每个结点有零个或多个子结点
(2)没有父节点的结点称为根节点
(3)每一个非根结点有且只有一个父节点
(4)除了根结点外,每个子结点可以分为多个不相交的子树。
https://www.cnblogs.com/manji/p/4903990.html
二叉树-你必须要懂!(二叉树相关算法实现-iOS)
下面是线性表定义
2.png
iOS操作系统中的堆栈
进程的内存分区
所有进程(执行的程序)都必须占用一定数量的内存,它或是用来存放从磁盘载入的程序代码,或是存放取自用户输入的数据等等。不过进程对这些内存的管理方式因内存用途不一而不尽相同,有些内存是事先静态分配和统一回收的,而有些却是按需要动态分配和回收的。
3.png
-
代码区:代码段是用来存放可执行文件的操作指令
-
全局(静态)区包含下面两个分区:
-
数据区:数据段用来存放可执行文件中已初始化全局变量,换句话说就是存放程序静态分配的变量和全局变量。
-
BSS区:BSS段包含了程序中未初始化全局变量。
-
常量区:常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量
-
堆(heap)区:堆是由程序员分配和释放,用于存放进程运行中被动态分配的内存段,它大小并不固定,可动态扩张或缩减。
当进程调用alloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用realse释放内存时,被释放的内存从堆中被剔除(堆被缩减),因为我们现在iOS基本都使用ARC来管理对象,所以不用我们程序员来管理,但是我们要知道这个对象存储的位置。 -
栈(stack)区:栈是由编译器自动分配并释放,用户存放程序临时创建的局部变量,存放函数的参数值,局部变量等。
也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味这在数据段中存放变量)。除此以外在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也回被存放回栈中。由于栈的先进后出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上将我们可以把栈看成一个临时数据寄存、交换的内存区。
举一个🌰
#import "ViewController.h"
int age = 24;//全局初始化区(数据区)
NSString *name;//全局未初始化区(BSS区)
static NSString *sName = @"Dely";//全局(静态初始化)区
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
int tmpAge;//栈
NSString *tmpName = @"Dely";//栈
NSString *number = @"123456"; //123456\\\\0在常量区,number在栈上。
NSMutableArray *array = [NSMutableArray arrayWithCapacity:1];//分配而来的8字节的区域就在堆中,array在栈中,指向堆区的地址
NSInteger total = [self getTotalNumber:1 number2:1];
//通常以这种方式创建对象:
NSObject *obj = [[NSObject alloc] init];
//系统会在栈上存储obj这个指针变量,它所指的对象在堆中。
//通过[NSObject alloc]系统会为其在堆中开辟一块内存空间,并为其生成NSObject所需内存结构布局。
/**这里有一个例外
block在创建的时候它的内存是默认是分配在栈(stack)上, 而不是堆(heap)上的.
所以它的作用域仅限创建时候的当前上下文(函数, 方法...).
当你使用block时候,如果你想对其保持引用,你需要对其进行copy操作,(从栈上copy到堆中,并返回一个指向他的指针)。
一个 block 刚声明的时候是在栈上
赋值给一个普通变量之后就会被 copy 到堆上
赋值给一个 weak 变量不会被 copy
作为属性:
用 strong 和 copy 修饰的属性会被 copy
用 weak 和 assign 修饰的属性不会被 copy
函数传参:
作为参数传入函数不会被 copy
作为函数的返回值会被 copy
*/
}
- (NSInteger)getTotalNumber:(NSInteger)number1 number2:(NSInteger)number2
{
return number1 + number2;//number1和number2 栈区
}
@end
堆(heap)和栈(stack)区别
-
申请方式和回收方式
栈区(stack) :由编译器自动分配并释放
堆区(heap):由程序员分配和释放 -
申请后系统的响应
栈区(stack):存储每一个函数在执行的时候都会向操作系统索要资源,栈区就是函数运行时的内存,栈区中的变量由编译器负责分配和释放,内存随着函数的运行分配,随着函数的结束而释放,由系统自动完成。只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
堆区(heap):操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。 -
申请大小的限制
栈区(stack):栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,栈的空间比较小,如果申请的空间超过栈的剩余空间时,将提示栈溢出。
堆区(heap):堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。 -
申请效率的比较
栈区(stack):由系统自动分配,速度较快。但程序员是无法控制的。
堆区(heap):是由alloc分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便. -
分配方式的比较
栈区(stack):有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloc函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
堆区(heap):堆都是动态分配的,没有静态分配的堆。 -
分配效率的比较
栈区(stack):栈是操作系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。
堆区(heap):堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多
总结一下优点缺点
栈对象:
优点:
1.高速,在栈上分配内存是非常快的。
2.简单,栈对象有自己的生命周期,你永远不可能发生内存泄露。因为他总是在超出他的作用域时被自动销毁了
缺点:栈对象严格的定义了生命周期也是其主要的缺点,栈对象的生命周期不适于Objective-C的引用计数内存管理方法。
堆对象:
优点:可以自己控制对象的生命周期。
缺点:需要程序员手动释放,容易造成内存泄漏。(我们有ARC)
网友评论