数据结构

[数据结构与算法 03] 最好、最坏、平均、均摊 时间复杂度

荒凉一梦 提交于 2020-02-09 01:29:34
由来 /**** 在一个无序的数组(array)中 查找变量 x 第一次出现的位置。如果没有找到,就返回 -1 ****/ // n 表示数组array的长度 int find(int[] array, int n, int x) { int i = 0; int pos = -1; for (; i < n; ++i) { if (array[i] == x) pos = i; } return pos; } 分析出此函数的时间复杂度为 O(n) 在数组中查找一个数据,并不需要每次都把整个数组都遍历一遍, 因为有可能中途找到就可以提前结束循环了。 所以我们可以这样优化一下这段查找代码 /**** 在一个无序的数组(array)中 查找变量 x 第一次出现的位置。如果没有找到,就返回 -1 ****/ // n 表示数组array的长度 int find(int[] array, int n, int x) { int i = 0; int pos = -1; for (; i < n; ++i) { if (array[i] == x) { pos = i; break; // 已经找到了,就不必继续找了 } } return pos; } 那么问题来了,如此优化以后,时间复杂度还是 O(n) 吗??? 可能第一次就找到了,只循环了一次,那时间复杂度就是 O(1)

数据结构-堆

有些话、适合烂在心里 提交于 2020-02-09 01:25:10
数据结构-堆 堆可以用来解决动态区间查询最值问题。 堆就是一个完全二叉树,可以插入节点,删除根节点(也可以删除特定节点)。 为了方便,普通的堆节点 i i i 的父亲就是 [ i ÷ 2 ] [i\div2] [ i ÷ 2 ] ( [ x ] [x] [ x ] 表示不超过 x x x 的最大整数)。 节点 i i i 的左儿子是 i × 2 i\times2 i × 2 ,右儿子是 i × 2 + 1 i\times2+1 i × 2 + 1 。 对于一个大顶堆: 每次插入节点的时候,就把节点插在完全二叉树的最后,如果它比它的父亲节点大,就把它和父亲交换,然后一直和父亲比较交换,直到父亲的值比它大,或者它已经成为树根。 每次删除根节点的时候,就把完全二叉树最后的那个节点放到根节点上,然后让最后那个节点原来的位置消失。然后把单前的根节点,跟它的左儿子比较,如果比左儿子小,就跟左儿子交换,然后不停跟左儿子比较交换知道它比左儿子大或着他没有左儿子。 时间复杂度 O ( n log ⁡ n ) O(n\log n) O ( n lo g n ) 。 如果你掌握了这些,那蒟蒻就放代码了: # include <bits/stdc++.h> using namespace std ; const int N = 1e5 + 10 ; class heap { public : int v

数据结构之搜索算法一:二分查找

巧了我就是萌 提交于 2020-02-08 23:14:25
  前面回顾了几个主要的排序算法,排完序就该搜索了,主要的查找类型分为静态查找表和动态查找表两大类,先看看静态查找表常用算法,二分搜索。 二分搜索原理如下:   二分查找又称折半查找,它是一种效率较高的查找方法。   【二分查找要求】: * 1.必须采用顺序存储结构 * 2.必须按关键字大小有序排列。 *   【优缺点】折半查找法的优点是比较次数少,查找速度快,平均性能好; * 其缺点是要求待查表为有序表,且插入删除困难。 * 因此,折半查找方法适用于不经常变动而查找频繁的有序列表。 *   【算法思想】首先,将表中间位置记录的关键字与查找关键字比较, * 如果两者相等,则查找成功; * 否则利用中间位置记录将表分成前、后两个子表, * 如果中间位置记录的关键字大于查找关键字, * 则进一步查找前一子表,否则进一步查找后一子表。   重复以上过程,直到找到满足条件的记录,使查找成功, * 或直到子表不存在为止,此时查找不成功。 代码 public int Search( int [] seqList, int key) { int flag = - 1 ; int mid; int low = 0 ; int high = seqList.Length - 1 ; while (low < high) { mid = (high + low) / 2 ; if (seqList

数据结构 耿国华老师讲

依然范特西╮ 提交于 2020-02-08 22:05:38
第一章$绪论 第一讲$数据结构的基础 数据是表征客观事物的可记录可识别的符号集合。数据是信息处理的核心基础。 数据结构有关的基本概念术语: 1.数据 2.数据元素 3.数据对象 4.数据类型 5.数据类型 6.抽象数据类型 7.数据结构 数据结构是指相互之间存在一种或多种特定关系的数据元素集合。它强调的是带有结构的数据元素的集合,数据元素之间的相互关系,即数据的组织形式。数据的组织方法与效率密切相关,采用不同数据的组织方法其处理效率不同。 第二讲$数据结构的内容简介 数据结构的内容,即数据结构研究范围:逻辑结构、存储结构、运算集合。数据结构注重的是数据元素之间的相互关系。 数据元素的相互关系表示为数据元素间的逻辑关系即逻辑结构。数据元素之间存在四种基本的逻辑结构: 1.集合结构 2.线性结构 3.树形结构 4.图形结构 数据元素之间关系在计算机中的表示方法分为: 顺序映象 (顺序存储结构,如数组,就是一组连续配置的单元); 非顺序映象 (非顺序存储结构,如链表,是一组任意配置的单元,通过指针连接起来,维持逻辑关系)。 第三讲$数据结构与C语言…… 一本正经划水总结一下(咳咳) 当需要用一个形式参数直接改变对应实参的值时,改形式参数应说明为:与实参同类型指针参数。 第四讲$算法性能评价 算法性能评价指标算法的执行时间,和占用空间两个方面,通过引入问题规模,语句频度等概念

数据结构——二叉树的遍历(c/c++)

给你一囗甜甜゛ 提交于 2020-02-08 19:12:10
递归遍历: 二叉树的三种递归遍历为先序遍历,中序遍历和后续遍历。它们相似之处在于都是对二叉树的递归遍历且对任何一个结点都经过三次,区别在于哪一次对该结点进行访问,由此分为先,中,后序遍历。所以对于任一结点都有:三次经过,一次访问。 先序遍历: void preorder(btNode* p) { if (p != NULL) { visit(p); preorder(p->lc); preorder(p->rc); } } 中序遍历: void inorder(btNode* p) { if (p != NULL) { inorder(p->lc); visit(p); inorder(p->rc); } } 后序遍历: void postorder(btNode* p) { if (p != NULL) { postorder(p->lc); postorder(p->rc); visit(p); } } 对于下图中的树: 先序遍历结果为:ABDECF 根 左 右 中序遍历结果为:DBEAFC 左 根 右 后序遍历结果为:DEBFCA 左 右 根 中序与先(后)序可唯一确定一颗二叉树。 层序遍历: 二叉树的层序遍历的实现思路:建立一个辅助数据结构队列,将二叉树头节点入队,在出队时将其左右孩子挨个入队,每出队一个结点,将其左右孩子入队,直至队列为空。 void level

以太坊的数据结构(状态树、交易树、收据树)

倾然丶 夕夏残阳落幕 提交于 2020-02-08 18:17:42
文章目录 一、状态树 1. trie 2. Patricia tree(trie) 3. Merkle Patricia tree(trie) 4. Modified Merkle Patricia tree(trie) 5. 账户状态值存储 6. 区块代码分析 二、交易树、收据树 一、状态树 以太坊是基于账户的账本,因此需要进行账户地址和账户状态的映射,如下所示: 我们尝试寻找一种合适的数据结构来完成这个需求: 如果以哈希表的形式保存状态数据,可以非常有效率地查找、更新账户状态数据,但是由于状态数据只保存在区块体中,轻节点难以进行Merkle Proof,因此考虑构建Merkle tree; 如果将账户数据简单组织成Merkle tree,不进行排序,就需要发布所有账户到区块中,保证根哈希一致,但是数量级太大,不可行;如果只发布状态变化的账户,就会导致所有节点的根哈希不一致,无法共识; 如果使用排序的Merkle tree,各个节点的根哈希就会相同,但是增加账户时,需要重构Merkle tree,代价太大。另外Merkle tree不能够快速查找、更新状态数据。因此需要考虑一种新的数据结构Merkle Patricia trie。 1. trie trie是一种字典前缀树,信息检索较为方便。如果有General、Genesis、Go、God、Good这几个单词

数据结构二叉树的遍历(二)

自作多情 提交于 2020-02-08 17:47:15
数据结构二叉树的遍历(二) 1、二叉树用二叉链表存储,编写算法采用先序遍历查找值为x的结点,找到返回其指针,否则返回NULL。 <span style="font-size:18px;">status serch_x(BinTree t,TreeType x){ if(!t) return NULL; //查找失败 if(t->data==x){ return t;// 查找成功 else{ p=serche_x(p->lchild,x); //查找左子树 if(p) return p; else return serch_x(p->rchild,x) </span><span style="font-size: 18px; font-family: Arial, Helvetica, sans-serif;">//查找右子树</span><span style="font-size:18px;"> } } }</span> 2、二叉树用二叉链表存储,编写算法要求返回二叉树的后序序列中的第一个结点的指针。 <span style="font-size:18px;"><span style="font-size:18px;">status serch_x(BinTree t){ p=t; if(p){ while(p->lchile||p->rchild){ //有孩子 while

数据结构-树状数组

流过昼夜 提交于 2020-02-08 17:40:42
数据结构-树状数组 树状数组是较堆功能更强大的 RMQ 数据结构。 数组数组的前置知识: 位运算 。 数组数组的功能:单点修改区间查询,区间修改单点查询(用差分)。 首先讲 l o w b i t ( x ) lowbit(x) l o w b i t ( x ) ,这是个位运算知识。表示 x x x 二进制下为 1 1 1 的最高位,如 l o w b i t ( ( 1110010 ) 2 ) = ( 10 ) 2 lowbit((1110010)_2)=(10)_2 l o w b i t ( ( 1 1 1 0 0 1 0 ) 2 ​ ) = ( 1 0 ) 2 ​ , l o w b i t ( ( 110000 ) 2 ) = ( 10000 ) 2 lowbit((110000)_2)=(10000)_2 l o w b i t ( ( 1 1 0 0 0 0 ) 2 ​ ) = ( 1 0 0 0 0 ) 2 ​ , C++ \texttt{C++} C++ 中的 x & − x x\&-x x & − x 正好能达到求 l o w b i t ( x ) lowbit(x) l o w b i t ( x ) 的效果。 然后树状数组中的 c c c 数组是个有跳跃性的前缀和数组,它通过 l o w b i t ( ) lowbit() l o w b i t (

9内核同步介绍

拥有回忆 提交于 2020-02-08 17:20:26
一、临界区与竞争条件   临界区 就是访问和操作共享数据的代码段。   如果两个执行线程有可能处于同一临界区中同时执行,那么我们就称它们为 竞争条件 (race conditions)   避免并发和防止竞争条件称为同步 (synchronization) 二、加锁   2.1 锁的介绍   我们需要一种方法确保一次有且只有一个线程对数据结构进行操作,或者当一个线程在对临界区标记时,就禁止其他访问。线程持有锁,而锁保护了数据。如请求队列的例子,可以使用一个单独的锁保护队列,每当有新的请求到来时,线程会首先占住锁,然后就可以安全的将请求加入队列中,结束操作后再释放该锁。   锁具有多种多样的形式,而且加锁的粒度范围也各不相同,各种锁机制之间的区别主要在于:当锁已经被其他线程持有,不可用时的行为表现——一些锁会执行忙等待,另外一些锁则可能会使当前任务睡眠直到锁可用为止。   2.2 为什么加锁?     用户空间之所以要同步是因为用户程序会被调度程序抢占和调度。用户进程可能在任何时刻被调度程序选择另一个高优先级的进程到处理器上执行,所以就会使得当前程序正处于临界区时,被非自愿的抢占了。   内核中造成并发执行的原因有:     1.中断,异步时刻发生,可能随时打断当前正在执行的代码。     2.软中断和tasklet—内核可在任意时刻唤醒或调度软中断和tasklet     3