一、数据结构
①什么是数据结构
数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率。数据结构往往同高效的检索算法和索引技术有关。
关于数据结构的起源,请点击了解起源。
②基本概念和术语
具体请参考数据结构相关概念。
1)数据:指的是能输入到计算机中、并能被计算机程序处理的对象。相对应的就有数据类型(整型、字符型、浮点型等等)。
2)数据元素:指组成数据的、有意义的基本单位,也被称为记录。
3)数据项:是数据不可分割的最小单位,一个数据元素可以由若干数据项组成。
4)数据对象:指性质相同的数据元素的集合,是数据的子集。
5)数据结构:指互相之间存在一种或多种特定关系的数据元素的集合。
③数据结构类型
1、逻辑结构
逻辑结构是指反映数据元素之间的逻辑关系的数据结构,其中的逻辑关系是指数据元素之间的前后件关系,而与他们在计算机中的存储位置无关。
逻辑结构分为:集合结构、线性结构、树形结构、图形结构。
-
集合结构
数据结构中的元素之间除了“同属一个集合” 的相互关系外,别无其他关系; -
线性结构
数据结构中的元素存在一对一的相互关系; -
树性结构
数据结构中的元素存在一对多的相互关系; -
图形结构
数据结构中的元素存在多对多的相互关系。
2、物理结构
指数据的逻辑结构在计算机存储空间的存放形式。
数据的物理结构是数据结构在计算机中的表示(又称映像),它包括数据元素的机内表示和关系的机内表示。由于具体实现的方法有顺序、链接、索引、散列等多种,所以,一种数据结构可表示成一种或多种存储结构。
数据元素的机内表示(映像方法):用二进制位的位串表示数据元素。通常称这种位串为节点。当数据元素有若干个数据项组成时,位串中与个数据项对应的子位串称为数据域。因此,节点是数据元素的机内表示(或机内映像)。
关系的机内表示(映像方法):数据元素之间的关系的机内表示可以分为顺序映像和非顺序映像,常用两种存储结构:顺序存储结构和链式存储结构。顺序映像借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系。非顺序映像借助指示元素存储位置的指针来表示数据元素之间的逻辑关系。
-
顺序存储结构
把数据元素存放在地址连续的的存储单元里,其数据间的逻辑关系和物理关系一致; -
链式存储结构
把数据元素存放在任意的存储单元里,这组存储单元可以是连续的也可以是不连续的。数据元素的存储关系不反映其逻辑关系,用指针存放数据元素的地址,我们通过地址可以找到相关联数据元素的位置。
④常用的数据结构
数组
在程序设计中,为了处理方便, 把具有相同类型的若干变量按有序的形式组织起来。这些按序排列的同类数据元素的集合称为数组。在C语言中, 数组属于构造数据类型。一个数组可以分解为多个数组元素,这些数组元素可以是基本数据类型或是构造类型。因此按数组元素的类型不同,数组又可分为数值数组、字符数组、指针数组、结构数组等各种类别。队列
一种特殊的线性表,它只允许在表的前端进行删除操作,而在表的后端进行插入操作。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列是按照“先进先出”或“后进后出”的原则组织数据的。队列中没有元素时,称为空队列。栈
是只能在某一端插入和删除的特殊线性表。它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。堆
在计算机科学中,堆是一种特殊的树形数据结构,每个结点都有一个值。通常我们所说的堆的数据结构,是指二叉堆。堆的特点是根结点的值最小(或最大),且根结点的两个子树也是一个堆。链表
是一种物理存储单元上非连续、非顺序的存储结构,它既可以表示线性结构,也可以用于表示非线性结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
链表的种类:单向、双向、循环链表
1)单项链表
A->B->C->D->E->F->G->H,类似于这种结构就是单向链表,H是头,A 是尾。
2)双向链表
H<- A->B->C->D->E->F->G->H,类似于这种结构就是双向链表,有头没尾。
3)循环链表
A->B->C->D->E->F->G->H绕成一个圈,最后H->A,类似于这种结构就是循环链表。
详情请看链表的基本知识。树
是包含n(n>0)个结点的有穷集合K,且在K中定义了一个关系N,N满足 以下条件:
(1)有且仅有一个结点 K0,他对于关系N来说没有前驱,称K0为树的根结点。简称为根。
(2)除K0外,K中的每个结点,对于关系N来说有且仅有一个前驱。
(3)K中各结点,对关系N来说可以有m个后继(m>=0)。知识补充
1)堆和栈的内存管理
堆和栈的区别
堆是需要手动释放内存的,而栈是由系统自动释放内存的。
2)二叉树的基本知识
大话数据结构--二叉树
二叉树的遍历与排序
二、常用算法
①快速排序算法
算法简单学习--快速排序
快速排序的基本思想是:通过一轮排序将待排序元素分割成独立的两部分, 其中一部分的所有元素均比另一部分的所有元素小,然后分别对这两部分的元素继续进行快速排序,以此达到整个序列变成有序序列。快速排序的最坏时间复杂度为O(n²)),平均时间复杂度为O(n*log2n)。
一趟快速排序的算法是:
- 设置两个变量i、j,排序开始的时候:i=0,j=N-1;
- 以第一个数组元素作为关键数据,赋值给key,即key=A[0];
- 从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]互换;
- 从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换;
- 重复第3、4步,直到i = j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j = j-1,i = i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i == j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。
备注:
/**
快速排序
@param array 需要排序的数据集合,必须是可变数组,因为不可变数组不能进行元素交换
@param left 该数组的起始下标
@param right 该数组的末尾下标
*/
- (void)fastSortingArray:(NSMutableArray *)array low:(NSInteger)left high:(NSInteger)right
{
if (left >= right) {
return;
}
NSInteger i = left;
NSInteger j = right;
NSInteger key = [array[left] integerValue];
while (i<j) {
/*而寻找结束的条件就是,1,找到一个小于或者大于key的数(大于或小于取决于你想升
序还是降序)2,没有符合条件1的,并且i与j的大小没有反转*/
while (i<j && key <= [array[j] integerValue]) {
j--;
}
[self swap:array left:i right:j];
/*这是i在当组内向前寻找,同上,不过注意与key的大小关系停止循环和上面相反,
因为排序思想是把数往两边扔,所以左右两边的数大小与key的关系相反*/
while (i<j && key >= [array[i] integerValue]) {
i++;
}
[self swap:array left:i right:j];
}
[self fastSortingArray:array low:left high:i-1];
[self fastSortingArray:array low:i+1 high:right];
}
//交换数组中的两个数
- (void)swap:(NSMutableArray *)array left:(NSInteger)left right:(NSInteger)right
{
NSNumber *temp;
if (array != nil && array.count > 0) {
temp = array[left];
array[left] = array[right];
array[right] = temp;
}
}
//初始化
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *array = [NSMutableArray arrayWithObjects:@(1),@(10),@(8),@(7),@(4),@(3), nil];
[self fastSortingArray:array low:0 high:5];
for (int i = 0; i < 6; i++) {
NSLog(@"%@",array[i]);
}
}
//打印日志
2018-04-04 21:14:29.899014+0800 CommonAlgorithms[7000:405024] 1
2018-04-04 21:14:29.899151+0800 CommonAlgorithms[7000:405024] 3
2018-04-04 21:14:29.899266+0800 CommonAlgorithms[7000:405024] 4
2018-04-04 21:14:29.899424+0800 CommonAlgorithms[7000:405024] 7
2018-04-04 21:14:29.899524+0800 CommonAlgorithms[7000:405024] 8
2018-04-04 21:14:29.899643+0800 CommonAlgorithms[7000:405024] 10
②冒泡排序算法
算法简单学习--冒泡排序
冒泡排序的基本思想是:通过相邻两个元素之间的比较和交换,使较大的元素逐渐从前面移向后面(升序),就像水底下的气泡一样逐渐向上冒泡,所以被称为“冒泡”排序。冒泡排序的最坏时间复杂度为O(n²),平均时间复杂度为O(n²)。
//冒泡排序
- (void)bubbleSort:(NSMutableArray *)array
{
//比较的轮数
for (int i = 0; i < array.count-1; i++) {
//每轮比较的次数
for (int j = 0; j<array.count-1-i; j++) {
//进行升序排序
if ([array[j] integerValue] > [array[j+1] integerValue]) {
//交换元素
[self swap:array left:j right:j+1];
}
}
}
}
//交换
- (void)swap:(NSMutableArray *)array left:(NSInteger)left right:(NSInteger)right
{
NSNumber *temp;
if (array != nil && array.count > 0) {
temp = array[left];
array[left] = array[right];
array[right] = temp;
}
}
//初始化
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *array = [NSMutableArray arrayWithObjects:@(1),@(10),@(8),@(7),@(4),@(3),@(0),@(9), nil];
[self bubbleSort:array];
for (int i = 0; i < 8; i++) {
NSLog(@"%@",array[i]);
}
}
//打印结果
2018-04-04 22:31:07.721115+0800 CommonAlgorithms[7898:459440] 0
2018-04-04 22:31:07.721268+0800 CommonAlgorithms[7898:459440] 1
2018-04-04 22:31:07.721380+0800 CommonAlgorithms[7898:459440] 3
2018-04-04 22:31:07.721518+0800 CommonAlgorithms[7898:459440] 4
2018-04-04 22:31:07.721627+0800 CommonAlgorithms[7898:459440] 7
2018-04-04 22:31:07.721816+0800 CommonAlgorithms[7898:459440] 8
2018-04-04 22:31:07.721921+0800 CommonAlgorithms[7898:459440] 9
2018-04-04 22:31:07.722025+0800 CommonAlgorithms[7898:459440] 10
③选择排序算法
简单选择排序
选择排序算法又分直接/简单选择排序和堆排序。
直接选择排序的基本思想是:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。直接选择排序的时间复杂度为O(n²)。
//简单选择排序
- (void)selectionSort:(NSMutableArray *)array
{
for (int i = 0; i < array.count-1; i++) {
for (int j = i+1; j < array.count; j++) {
//进行升序排序
if ([array[i] integerValue] > [array[j] integerValue]) {
[self swap:array left:i right:j];
}
}
}
}
//交换
- (void)swap:(NSMutableArray *)array left:(NSInteger)left right:(NSInteger)right
{
NSNumber *temp;
if (array != nil && array.count > 0) {
temp = array[left];
array[left] = array[right];
array[right] = temp;
}
}
//初始化
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *array = [NSMutableArray arrayWithObjects:@(1),@(10),@(8),@(7),@(4),@(3),@(0),@(9), nil];
[self selectionSort:array];
for (int i = 0; i < 8; i++) {
NSLog(@"%@",array[i]);
}
}
//打印日志
2018-04-04 22:54:28.205613+0800 CommonAlgorithms[8140:476345] 0
2018-04-04 22:54:28.206531+0800 CommonAlgorithms[8140:476345] 1
2018-04-04 22:54:28.206671+0800 CommonAlgorithms[8140:476345] 3
2018-04-04 22:54:28.207158+0800 CommonAlgorithms[8140:476345] 4
2018-04-04 22:54:28.207461+0800 CommonAlgorithms[8140:476345] 7
2018-04-04 22:54:28.207605+0800 CommonAlgorithms[8140:476345] 8
2018-04-04 22:54:28.207789+0800 CommonAlgorithms[8140:476345] 9
2018-04-04 22:54:28.208033+0800 CommonAlgorithms[8140:476345] 10
④直接插入排序算法
直接插入排序
直接插入排序的基本思想是从第二个元素开始,依次和前面的元素比较,如果比前面的元素小则将元素依次向后移位,给需要插入的元素腾出空间。与选择排序类似的是当前索引左边的所有元素都是有序的,但是它们最终的位置不确定,因为后面可能还会出现更小或更大的元素。所以为了给更小或更大的元素腾出空间,它们随时都可能被移动。如果到达了数组的右端时,数组顺序就完成了。
//直接插入排序
- (void)insertSort:(NSMutableArray *)array
{
//从第二个元素开始
for (int i = 1; i < array.count; i++) {
int j = i;
//记录第下个元素
NSInteger temp = [array[i] integerValue];
//进行升序排序
while (j>0 && temp < [array[j-1] integerValue]) {
//把大于temp的值放到temp的位置
[array replaceObjectAtIndex:j withObject:array[j-1]];
j--;
}
//然后把temp的值放在前面的位置
[array replaceObjectAtIndex:j withObject:[NSNumber numberWithInteger:temp]];
}
}
//初始化
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *array = [NSMutableArray arrayWithObjects:@(1),@(10),@(8),@(7),@(4),@(3),@(0),@(9), nil];
[self insertSort:array];
for (int i = 0; i < 8; i++) {
NSLog(@"%@",array[i]);
}
}
//打印日志
2018-04-04 23:28:32.355955+0800 CommonAlgorithms[8696:503068] 0
2018-04-04 23:28:32.356091+0800 CommonAlgorithms[8696:503068] 1
2018-04-04 23:28:32.356209+0800 CommonAlgorithms[8696:503068] 3
2018-04-04 23:28:32.356301+0800 CommonAlgorithms[8696:503068] 4
2018-04-04 23:28:32.356422+0800 CommonAlgorithms[8696:503068] 7
2018-04-04 23:28:32.356687+0800 CommonAlgorithms[8696:503068] 8
2018-04-04 23:28:32.356801+0800 CommonAlgorithms[8696:503068] 9
2018-04-04 23:28:32.356916+0800 CommonAlgorithms[8696:503068] 10
⑤二分查找算法
二分查找--百度百科
二分查找也称折半查找,它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。
二分查找的基本思想是:与猜数字游戏类似,就是那个有人说“我正想着一个1到100的数字”的游戏。我们每回应一个数字,那个人就会说这个数字是高了、低了还是对了。
-
查找前提
所有数据必须有序(无序则先进行排序)。 -
算法描述
1)选择中间值;
2)如果选择的值是待搜索的值,算法结束并返回;
3)如果待搜索值比选中值要小,则返回步骤1)并在选中值左边的子数组中寻找。
4)如果待搜索值比选中值要大,则返回步骤1)并在选中值右边的子数组中寻找。
/**
二分查找
@param array 需要查找的数组且必须为有序数组
@param key 目标元素
@param lower 数组起始下标
@param upper 数组末尾下标
@return 目标元素在数组的下标
*/
- (NSInteger)twoPointLookup:(NSArray *)array target:(NSInteger)key lower:(NSInteger)lower upper:(NSInteger)upper
{
if (lower < upper) {
//获取中间数组下标
NSInteger middle = (lower+upper)/2;
//与目标元素相等则直接返回
if (key == [array[middle] integerValue]) {
return middle;
}
//目标元素小于中间值
else if (key < [array[middle] integerValue]) {
//递归调用,起始下标为lower,末尾下标则改为middle-1
return [self twoPointLookup:array target:key lower:lower upper:middle-1];
}
else {
//递归调用,起始下标则改为middle+1,末尾下标为upper
return [self twoPointLookup:array target:key lower:middle+1 upper:upper];
}
}
else {
return -1;
}
}
//初始化
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *array = @[@(1),@(2),@(3),@(4),@(5),@(6),@(7),@(8)];
NSInteger log = [self twoPointLookup:array target:6 lower:0 upper:7];
NSLog(@"%ld",log);
}
//打印日志
2018-04-05 00:08:13.526588+0800 CommonAlgorithms[9122:531299] 5
⑥堆排序算法
⑦哈希排序算法
⑧二叉树的先序遍历(非递归)
请期待后续更新