《数据结构》学习笔记1

前言:本文内容均整理自学堂在线《数据结构》(上/下)课程的视频、课件(特别棒!免费学习,还有专业回答),仅用于个人概念复习和思路回顾。对学堂在线及相关人员,特别是邓公,表示衷心感谢!

摘要:这里主要包括(1)算法概述(2)排序和查找(3)四种结构(4)图的概念和算法,此外还有BST专题篇,串匹配专题篇,以及其他高级数据结构(课件里还好多美味!)

算法概述

算法:特定计算模型下,旨在解决特定问题的指令序列(输入,输出,正确性,确定性,可行性,有穷性)

Turing Machine(列表), Random Access Machine(向量)

image

大O记号(上界),大θ记号(确界),大Ω记号(下界)

image

幂方级数:比幂次高出一阶(1+2+3+4……)

几何级数:与末项同阶(1+2+4+8……)

收敛级数:O(1),  1+1/4+1/9+1/16……

调和级数:1+1/2+1/3+1/4…… = Θ(logn)

对数级数:log1+log2+log3+log4…… = Θ(nlogn)

封底估计 back-of-the-envelope calculations (BotEC):

一天≈10^5秒;1生≈1世纪≈3X10^4天;三生三世 ≈ 300 yr = 10^10 秒

普通PC:1GHz,10^9 flops 

 

排序和查找

冒泡排序, swap one by one

冒泡排序的优化:记录最后一次冒泡位置,使得输入敏感。

选择排序,record the max (or min) position, swap once

循环节:每一步迭代,元素所属循环节长度恰好减少一个单位;该元素脱离原属循环节,自成长度为1的循环节;有多少个循环节,就出现几次无需swap的情况,最大值为n,期望值为θ(logn)

堆排序,heap + select_sort

建堆,不断delMax():交换--下滤;(不稳定)

不需全排序,找出前K个词条 O(klogn)的select_sort

插入排序,put one into sorted sequence,

输入敏感性

逆序对:每次比较次数为逆序对数量+1

归并排序,put one(in a sorted sequence) into sorted sequence.(稳定)

 1 void merge_sort(int *s, int p, int n){
 2      // sort from s[p] to s[p+n-1]
 3      if (n < 2) return;
 4      int mi = n >> 1;
 5      int *temp = new int[mi];
 6      for (int i = 0; i < mi; ++i) {
 7          temp[i] = s[p+i];
 8      }
 9      merge_sort(temp, 0, mi);
10      merge_sort(s, p+mi, n-mi);
11      int i = 0, j = p+mi, t = p;
12      while (i < mi) {  // till temp[] is empty.
13          s[t++] = (j == p + n || temp[i] < s[j]) ? temp[i++] : s[j++];
14      }
15      delete[] temp;
16 }
void merge_sort(int *s, int p, int n)

快速排序

不稳定,就地O(1)空间,LUG版本(从左右向中间扫描)和LGU版本(从左到右扫描)

构造轴点 pivot(k选取问题),linearSelect算法选取中位数,渐进O(n)

希尔排序

步长序列shell sequence(g,h,… ordered 会不断积累>>推论(g,h)的线性组合都是有序的)

例如PS序列:{2^k-1};pratt’s Sequence,{1,2,3,4,6,8,9,12…};Sedgewick Sequence:实践最佳的

实现:用by-rank的向量和输入敏感的排序算法(如插入排序)。

桶排序 bucket sort,也称基数排序 radix sort【不是基于比较的排序】,体现分而治之的策略

整数排序 :数量/值域 符合常对数密度 log(n)/log(n^d) ,问题转化为d个域的桶排序问题。

计数排序:小集合+大数据:统计每个桶数量--累加桶数量--扫描每个数据填补对应秩(稳定性处理)

二分查找, 简洁版(时间复杂度必定为logn)

1 int bin_search(int *s, int tar, int lo, int hi) {
2 
3     // search tar in s[lo] to s[hi], return the index: s[index] <= tar < s[index + 1]
4      while (lo < hi) {
5          int mi = (lo + hi) >> 1;
6          (tar < s[mi]) ? hi = mi : lo = mi + 1;
7      }
8      return --lo;
9 }
int bin_search(int *s, int tar, int lo, int hi)

众数查找

中位数查找

 

四种结构

向量-静态操作,by rank   》》栈 stack  队列 queue  优先级队列 heap

列表-动态操作, by position

树-层次结构,by key 兼顾vector和list的优点:高效查找、插入、删除,半线性结构

散列,by value 关键码之间只需支持比对判等,不必支持大小比较

 

栈的典型应用场景:

image

栈混洗(个数 = Catalan数)

逆波兰表达式(RPN)

path(v) – v – subtree(v) :节点的深度|path(v)|,树的高度(空树为-1)max(|subtree(v)|)

真二叉树(proper binary tree,所有节点度数为2) huffman编码树

完全二叉树(向量存储)

长子-兄弟表示法(多叉树--二叉树转化)

树的先序遍历,中序遍历,后序遍历,层次遍历(根据根节点访问的位置)

pre-order traversal: root node >> left child >> right child

in-order traversal: left child >> root node >> right child

post-order traversal: left child >> right child >> root node   ==RPN

散列表 hash table/桶数组 bucket array

散列函数:确定 determinism,快速 efficiency, 满射 surjection,均匀 uniformity

除余法:hash(key) = key % M 。 M素数;缺陷:不动点,零阶均匀。

MAD: hash(key) = (a*key+b)%M。Multiply-Add-Divide 一阶均匀。多项式法

数字分析 selecting deigits;平方取中 mid-square;折叠法 folding;位异或法 XOR;

(伪)随机数法 hash(key) = rand(key) = [rand(0) * a ^ key] % M :可移植性差

closed hashing:多槽位法,独立链法 linked-list chaining ,公共溢出区(顺序存储冲突数据)

开放定址 open addressing(任何散列都可以接纳任何词条):线性试探;双向平方试探(表长取4k+3的素数)

装填因子,小于50%;重散列。

懒惰删除:仅作删除标记;查找:while (ht[r] && (k!=ht[r]->key) || !ht[r] && lazilyRemoved(r))

优先级队列 priority queue:向量实现的完全二叉树,堆

三个接口:delMax(), insert(), merge()

(保持结构性和堆序性)插入--至最后一个节点--上滤;删除--与最后一个节点交换--下滤。

Floyd建堆:自下而上的下滤(合并子树),O(n)

锦标赛树:N叶子,O(N)次两两比较,决出胜者(N空间记录);更新(上一优胜者的祖先进行logN次比较);渐进意义上与效率相等,常数意义上更小。

多叉堆d-heap:基于向量实现parent(k) = math.floor((k-1)/d), child(k,i) = kd+i;降低了堆高,使得上滤成本降低,下滤成本增加。图算法(Prim和Dijkstra)中,d=e/n+2时,性能最优。不能借助移位加速秩的换算,特别适用于大规模跨越存储层次时。

左式堆:npl(x) = 1+min{ npl(x.lc), npl(x.rc) } 到外部节点的最近距离。主要用于解决堆合并问题(效率Ologn),借助二叉树结构和heap方法。

 

图的概念及算法

vertex 顶点 tail 尾 head 头

edge 边 undirected edge 无向边 directed edge 有向边

adjacency 邻接 self-loop 自环 incidence 关联

degree/valency 度

simple graph 简单图(无重边和自环)

undigraph 无向图 digraph 有向图 mixed graph 混合图 DAG(Directed Acyclic Graph) 有向无环图

欧拉环路(边);哈密尔顿环路(顶点)

spanning tree 支撑树 weighted network 带权网络 MST(minimum spanning tree) 最小支撑树

adjacency matrix 邻接矩阵(点和点),incidence matrix 关联矩阵(点和边),邻接表

planar graph 平面图 Euler’s formula:v-e+f-c = 1

sparse graph 稀疏图

BFS(Breadth-First Search)

DFS(Depth-First Search) 深度优先搜索  活跃期:dTime, fTime

 

图的算法(根据讲稿整理,主要思路实现)

拓扑排序:有向无环图DAG的排序

数据结构:

    node[] queue(记录排列结果)

    int[] vector_fronts(记录入度变化)

    邻接矩阵(vector * 单向list,记录关系)

算法:BFS

    初始化queue(入度为0的点),出queue,减入度,if 入度为0,进queue。

prim 算法:无向图求最小支撑树 MST

数据结构:

    node[] vector_parent(记录顶点到MST的最小跨边对应的顶点)

    bool[] vector_flag(记录顶点是否纳入MST中)

    邻接矩阵(记录关系)

算法:贪心 + PFS(优先级遍历) O(n2)

    每次 (1) 在 vector_parent( not in MST ) 中选择一条极短跨边 shortest crossing edge,(2) vector_flag中标记node加入MST,(3) vector_parent( not in MST )中到node的边更短?更新:不更新。

Dijkstra 算法:连通图求单点源(single-source)到其他顶点的最短路径 (Shortest Path Tree)

要求:边权值非负

数据结构:

    node[] vector_parent (记录顶点到SPF的最少代价对应的顶点)

    int[] vector_cost (记录顶点到SPF的最少代价)

    bool[] vector_flag (记录顶点是否纳入SPF中)

算法:贪心 + PFS

    每次 (1) 在vector_parent( not in SPF ) 中选择代价最小的顶点,(2) vector_flag 标记node加入SPF,(3) vector_parent( not in SPF )中转自node的代价更小?更新cost代价和parent父节点标记:不更新

双联通分量

关节点 articulatioin point, cut-vertex :删掉后原图连通分量增多(一个连通图变成多个子连通图)

双(重)连通图 bi-connectivity :无关节点的图

极大双连通子图,双连通分量 bi-connected components

数据结构:栈存放已访问的边

算法:DFS, O(n+e)

    用dtime和hca(Highest Connected Ancestor)记录DFS遍历次序,tarjan算法

imageimageimage

Kruskal 算法:无向图求最小支撑树(功能同prim算法)

将所有边组成优先队列(eloge),每次选最短边(不构成环的),迭代n-1次结束。【用并查集检测环路】

稀疏图时e≈n,效率比prim算法高

Floyd-Warshall 算法:连通图中所有点对的最短距离

算法1:每个顶点用Dijkstra算法,O(n3)

算法2:FW,也是O(n3),允许负权边,单不能有负权环路。动态规划,代码简单

    三重循环:中间节点k, 两边节点i, j

posted @ 2019-01-17 23:50  StupiDeMon  阅读(775)  评论(1编辑  收藏  举报