数据结构与算法分析
参考书籍:[1]Clifford A.Shaffer.数据结构与算法分析[M].北京:电子工业出版社,2020.
Chapter 1 Data Structures and Algorithms
1.4 Problems, Alogorithms, and Programs
Q:What can be called an algorithm?
1.It must be correct.
2.It is composed of a series of concrete steps.
3.It is no ambiguity.
4.It must be composed of a finite number of steps.
5.It must terminate.
Chapter 2 Mathematical Preliminaries
2.3 Logarithms 对数
若 log 2 x = y \log_2 x=y log2x=y,则2可省略: log x = y \log x=y logx=y。
Chapter 3 Algorithm Analysis*
Asymptotic analysis attempts to estimate the resource consumption of an algorithm.
3.1 Introduction
growth rate
2 <
log
2
x
\log_2 x
log2x <
n
2
/
3
n^{2/3}
n2/3 < n <
n
2
n^2
n2 <
3
n
3^n
3n < n!
3.4 Asymptotic Analysis 渐近分析
Upper Bounds 上限:
O
(
g
(
n
)
)
O\left(g(n)\right)
O(g(n))
Lower Bounds 下限:
Ω
(
g
(
n
)
)
\varOmega \left(g(n) \right)
Ω(g(n))
当上限下限一样时用:
Θ
(
g
(
n
)
)
\varTheta \left(g(n) \right)
Θ(g(n))
Simplifying Rules 化简法则
1.省略常系数:
Θ
(
k
g
(
n
)
)
=
Θ
(
g
(
n
)
)
\varTheta \left(kg(n) \right)=\varTheta \left(g(n) \right)
Θ(kg(n))=Θ(g(n))
2.取最高次项:
Θ
(
g
(
n
)
+
f
(
n
)
)
=
Θ
(
max
(
g
(
n
)
,
f
(
n
)
)
)
\varTheta \left(g(n)+f(n) \right)=\varTheta \left(\max (g(n),f(n)) \right)
Θ(g(n)+f(n))=Θ(max(g(n),f(n)))
3.5 Calculating the Running Time for a Program
1.sum = 0;
赋值:
Θ
(
1
)
\varTheta \left(1 \right)
Θ(1)
2.for (i=1;i<=n;i++) { sum += n;}
循环:
Θ
(
n
)
\varTheta \left(n \right)
Θ(n)
3.
for(i=1;i<=n;i*=2)
for(j=1;j<=n;j++)
sum++;
多重循环(内外层相乘): Θ ( n log n ) \varTheta \left(n\log n \right) Θ(nlogn)
Chapter 4 Lists, Stacks and Queues
4.1 Lists 线性表
线性表存在唯一的 head 和 tail ,数据有次序。
<20, 23 | 12, 15> : current element is 12, current position is 2.
4.1.1 Array-based Lists 顺序表:所有数据依次排列储存
Insert 插入数据
for(int i=listSize; i>curr; i--)
listArray[i] = listArray[i-1];
将数据后移来留出空间,
Θ
(
n
)
\varTheta \left(n \right)
Θ(n)
Performance of Array-based lists
Θ
(
n
)
\varTheta \left(n \right)
Θ(n):Insert, delete (remove) and locate
Θ
(
1
)
\varTheta \left(1 \right)
Θ(1):clear, empty, full, length, replace, get, prev(当前位置向前一步)
4.1.2 Linked Lists 链表:数据可以不连续储存,可以扩充
链表由结点(Node)组成,Node = element + next (元素和指针)
Singly Linked List 单链表
带表头结点,位于表的最前端,不储存数据,可以简化链表操作。
tip:在 curr 处插入元素不等于在 i 处插入(前者直接插入,后者需要从头查找 i 的位置)
在链表第 i 个结点插入新元素 x
if(head == NULL || i==0){ //插在表前
newnode -> next = head;
if(head == NULL) tail = newnode; //原来为空表
head = newnode;} //转移表头
else{ //插在表中或末尾
newnode -> next = p -> next;
if(p -> next == NULL) tail = newnode;
p -> next = newnode;} //更改了两个指针的指向,使新节点插入
Performance of Singly Linked List
Θ
(
1
)
\varTheta \left(1 \right)
Θ(1):Insert, delete (remove)
Θ
(
n
)
\varTheta \left(n \right)
Θ(n):prev(当前位置向前一步), direct access
Space Comparison
n:列表中元素的数量
E:每个数据占据的空间
P:每个指针占据的空间
D:列表中最多可有元素数量
DE<n(P+E)时,Singly Linked List 比 Array-based Lists储存效率更低。
Circular List 循环列表
循环列表最后一个结点的next指针指向表的前端
Doubly Linked Lists 双向列表
相比单向列表增加了向前的指针,解决了单向列表不方便向前移动查找的缺点。但更多的指针意味着需要更多的储存空间来存放指针。
4.2 Stacks 栈
先进后出(LIFO)的线性表 (包括线性表和链表) ,栈的顶部在线性表的尾部(即 push 和 pop 同等与线性表在尾部 insert 和 remove)。
入栈PUSH:(如果装多了会overflow溢出)
出栈POP:(如果取多了会underflow下溢)
Stack Application
函数的嵌套,递归等
4.3 Queues 队列
先进先出(FIFO)的线性表,头部front,尾部rear。
Empty queue: rear == front
Full queue: (rear+1)%maxSize==front
Chapter 5 Binary Tree
5.1 Definition 定义
node 结点
subtree 子树,左右子树不同 degree 结点的子树数,二叉数
⩽
\leqslant
⩽ 2
children 子结点
~~~~~~~~~~~~~~~~~~
parent 父结点
edge 边
path 从
n
1
n_1
n1到
n
k
n_k
nk的路径
~~~~~~~~~
length 路径长度
k
−
1
k-1
k−1
ancestor 祖先(多个)
~~~~~~~
descendant 后代(多个)
depth 根为0,向下依次+1
~~~
height 树的层数
leaf node 无后代的结点
~~~~~~~
internal node 有后代的结点
sibling node 同父的结点
full binary tree 若有子树,子树必有两个(a)
complete binary tree 除最后一层全满,最后一层从左到右填上(b)
定理:
leaf node (
n
0
n_0
n0)=internal node(
n
2
n_2
n2)+1 叶子结点数=内部结点数+1 (所有满二叉树都成立)
推论:非空二叉树中的空子树数=结点数+1
5.2 Binary Tree Traversals 遍历二叉树
5.2.1 遍历方法
将二叉树的结点简写:N-root, L-left child, R-right child
遍历方法:1.Preorder: NLR, NRL
~~~~~~~~~~~~~~~~
2.Inorder: LNR, RNL
~~~~~~~~~~~~~~~~
3.Postorder: LRN, RLN (都是以N为基准)
画一个二叉树:
a
+
b
∗
(
c
−
d
)
−
e
/
f
a+b*(c-d)-e/f
a+b∗(c−d)−e/f (最后计算的在最根部)
5.2.2 用遍历序列还原二叉树
由preorder和inorder 或 postorder和inorder可唯一地确定一个二叉树。
5.3 Binary Tree Node Implementations 二叉树实现
内部结点IntlNode包括元素和左右两个指针,分别指向leftChild和rightchild
叶子结点LeafNode仅包括元素
(二叉树实现和遍历代码见数据结构与算法代码部分)
5.4 Binary Search Tree 二叉查找树(BST)
二叉查找树元素依次增大放置,即满足leftchild < < <root ⩽ \leqslant ⩽rightchild
remove函数
(1)0 child:叶子结点,直接移除 (2)1 child:移除该结点并把子结点上移
(3)2 child:找结点代替该结点,一般选右边最小的结点或左边最大的结点
查找树比较平衡时,remove函数的时间复杂度为
Θ
(
l
o
g
n
)
\varTheta(logn)
Θ(logn),
n
n
n为树的深度。
(BST代码同样见数据结构与算法代码部分)
5.5 Heaps and Priority Queues 堆与优先队列
heap堆是完全二叉树,分为大顶堆max-heap (root大于两个子结点) 和 小顶堆min-heap (root小于两个子结点)
siftdown 下沉调整算法:结点不符合条件时,与左右子结点进行比较并交换位置,然后继续与子结点进行比较。
void siftdown(int pos){
while(!isLeaf(pos)){ //stop if pos is a leaf
int j=leftchild(pos);
int rc=rightchild(pos);
if((rc<n)&&Comp::prior(Heap[rc],Heap[j]))
j=rc; //如果右子节点存在且其值优先于左子节点的值,则将j更新为右子节点的位置
if(Comp::prior(Heap[pos],Heap[j]))
return; //如果位置pos的元素优先于j位置的元素,则不需要进行任何交换,函数返回
swap(pos,j); //move down下移
pos=j;
}
}
堆比较平衡时,插入,删除,寻找元素函数的时间复杂度为
Θ
(
l
o
g
n
)
\varTheta(logn)
Θ(logn),
n
n
n为树的深度。
(大顶堆实现代码同样见数据结构与算法代码部分)
5.6 Huffman Coding Trees 哈夫曼编码树
weighted path length 带权路径长度
external path weight 外部路径权重
编码步骤:
(Huffman编码树实现代码同样见数据结构与算法代码部分)
Chapter 6 Non-Binary Trees
forest:一棵树或多棵树
6.1 ADT for Nodes and General Tree Traversals 树的结点的ADT及遍历
结点与二叉树类似,增加了sibling 同父结点
//General tree node ATD
template <typename E>
class GTNode{
public:
E value();
bool isLeaf();
GTNode* parent();
GTNode* leftmostChild();
GTNode* rightSibling(); //return right sibling同父结点
void setValue(E&);
void insertFirst(GTNode<E>*);//insert first child
void insertNext(GTNode<E>*);//insert next sibling
void removeFirst();
void removeNext();
}
遍历:只有前序preorder与后序postorder遍历
6.2 The Parent Pointer Implementation 父指针表示法
6.2.1 advantages
Parent Pointer:指针由子结点指向父结点
优点:
(1)判断2个结点是否在同一树 (找祖父结点,若祖父相同则在同一树)
(2)合并2树(直接将一棵树的root接到另一棵树的root上)
6.2.2 classes 并查集
classes 并查集包括weighted union rule权重合并规则和path compression路径压缩
例题:Using the weighted union rule and path compression, show the array for the parent pointer implementation that results from the following series of equivalences on a set of objects indexed by the values 0 through 15. Initially, each element in the set should be in a separate equivalence class. When two trees to be merged are the same size, make the root with greater index value be the child of the root with lesser index value.
(0,2) (1,2) (3,4) (3,1) (3,5) (9,11) (12,14) (3,9) (4,14) (6,7) (8,10) (8,7) (7,0) (10,15) (10,13)
解答
1.题目要求小数在大数上面(指针由大数指向小数)
2.依次加入,加入时同时进行压缩
将(x,y)(例如(4,14))进行合并时,先找到x和y分别的root,比较root大小,若root(x)>root(y),将root(x)指向root(y),合并两树。
6.3 General Tree Implemetations 树的实现
(以下实现都没有代码实现,仅有图示)
1.List of Children 子结点表表示法
Val:结点名
Par:父结点的Index值
2.Left-Child/Right-sibling 左子结点/右兄弟结点表示法
Left:最左子结点位置指针
Right:右边第一个兄弟结点位置指针
3&4.Dynamic Node 两种动态结点表示法
6.4 K-ary Trees K叉树
与二叉树类似,分为Full k-ary trees和Complete k-ary trees.
6.5 Sequential Tree Implementations 树的顺序表示法
1.用 / 表示空结点(AB/D//CEG///FH//I//)
2.用 ’ 表示内部结点,/ 表示空结点(A’B’/DC’E’G/F’HI)
3.用 ) 表示向父结点退一次<ABD))CEG))FH)I)))>不区分左右结点