【433】COMP9024 复习
目录:
- 01. Week01 - Lec02 - Revision and setting the scene
- 02. Week02 - Lec01 - Data structures - memory allocation
- 03. Week02 - Lec02 - Input - Output
- 04. Week03 - Lec01 - ADTs
- 05. Week03 - Lec02 - Dynamic memory allocation
- 06. Week04 - Lec01 - LinkedLists
- 07. Week06 - Lec01 - Heaps
- 08. Week06 - Lec02 - Priority Queues and Heap Sort
- 09. Week07 - Lec01 - Graphs
- 10. Week07 - Lec02 - Graph search
- 11. Week08 - Lec02 - Applications of Graph Search
- 12. Week09 - Lec01 - Weighted Graph Algorithms
- 13. Week09 - Lec02 - Binary Search Trees
- 14. Week10 - Lec01 - Splay Trees
01. Week01 - Lec02 - Revision and setting the scene
- C语言除法只有一个符号,与 Python 不同,如果想要得到 float 类型,首先变量需要为 float,另外就是 除数 或者 被除数 之一是 float 类型
如果变量为 int,则自动获取整数部分,不管 除数 与 被除数 是否为 floatfloat a = 10.0/3, a = 3.333333
int a = 10.0/3, a = 3 - Array 可以通过下面方式声明
int fac[20]; //对于每个元素具体赋值
int v[5] = {1, 2, 3, 4, 5}; //直接赋值的话,需要加上大括号 - 字符数组需要保留最后为'\0'
char s[6] = {'h', 'e', 'l', 'l', 'o'};
char t[6] = "hello"; - 数组作为函数参数的时候,同时需要加入数组的长度
int sumArray(int harry[], int length) {...}
or
int sumArray(int *harry, int length) {...}其中 Harry[] 与 *harry 可以达到一样的效果
- 二维数组可以看成数组的数组
m[2][3] 包含2个元素,每个元素都有一个包含3个元素的数组
m[0] 包含 m[0][0], m[0][1], m[0][2],第1行
m[1] 包含 m[1][0], m[1][1], m[1][2],第2行
int m[2][3] = {{1, 2, 3}, {4, 5, 6}};m[3][2][4] 包含3个元素,其中每个元素一个2*4的数组
分层,最外层3个元素,然后一层是2个元素,最里面是4个元素int m[3][2][4] = {{{1, 2, 3, 4}, {5, 6, 7, 8}},
{{9, 10, 11, 12}, {13, 14, 15, 16}},
{{17, 18, 19, 20}, {21, 22, 23, 24}}}; - struct date {...};
其中 struct date 本身是一个新的数据类型,因此定义新的变量需要这样:
struct date birthday; - #define 用来定义常量
typedef 用来将数据类型重命名
注意,前面有符号的,后面没有分号,前面没有符号的,后面需要分号#define MAX 20
typedef struct date Date;
typedef float Real;
typedef struct {
int day, month, year;
} Date;
Date birthdays[MAX]; // an array of structs - 结构体访问
structTypeName s, *sp;
sp = &s;
s.element
(*sp).element
sp->element int main(int argc, char *argv[])
argc: number of arguments
argv[]: allow access to arguments represented as strings
atoi(Origin_string): string to number
sscanf(Origin_string, "%d", &num): string to everything(num)
02. Week02 - Lec01 - Data structures - memory allocation
- a char takes 1 byte (8 bits)
an int takes 4 bytes (32 bits)
a pointer takes 8 bytes (64 bits) (64 bit computers)
sizeof structs
组成 8 bytes,无法组成的就空缺,可以设想成 8 bytes 一个卡壳,而且按顺序放struct s1 {
char c;
int i;
char *s;
};其中 1 + 4 (+ 3) + 8 = 16
struct s2 {
char c;
char *s;
int i;
};其中 1 (+ 7) + 8 + 4 (+4) = 24
03. Week02 - Lec02 - Input - Output
- 3 main sources of input for programs:
from the command line: argc, argv[]
from standard input (also called stdin)
stdin can be the keyboard, a data file, or the output of other program
from an 'internally-defined' file
open a file, use fscanf(), and don't forget to close the file - command line
sscanf(argv[1], "%d", &num) != 1
如果赋值成功的话,会返回赋值的个数,将字符串 argv[1] 转换为 数字 num - standard input (stdin)
scanf("%d", &num) != 1
or
scanf("%d%d, &num1, &num2) != 2 //表示两个数字,中间可以间隔whitespace没有 string 参数,直接获取 stdin 的信息,或者是输入的,或者是文件
pipe command: |, 将前一个输出当做后一个输入使用 getchar()
一次获取一个字符,检测是否到达 '\n'char c = getchar();
while (c != '\n') {...} - a user file
fopen(), fscanf(), fclose() - stdout, stderr
fprinf(stream, ...)
stdin: scanf()
stdout: printf() - scanf(): to read from stdin,只能读取 keyboard 或者 文件,与 printf 对应
多个含有空格的字符,无线调用 scanf 可以读取数字
sscanf(): to read from the command line,从字符串获取信息
printf(): to write to stdout,只能输出到控制台 或者 文件
fprintf(): to write to stderr
04. Week03 - Lec01 - ADTs
- 实现 struct
typedef struct point {
float x;
float y;
} *Point;struct point 用来计算 malloc 中空间大小
Point 以指针形式,可以直接修改地址对应的数据Point p;
p = malloc(sizeof(struct point));
p->x
p->y - Stacks and queues
stacks: Last in, first out
通过数组实现,快速简单,不过要固定长度
通过 linkedlist 实现,慢,不过可以灵活增加内存
a postfix calculator
queues: First in, first out
‘circular’ queue
数组实现:
stacks:尾部push,尾部pop
queues:头部qush,尾部pop
05. Week03 - Lec02 - Dynamic memory allocation
- 声明的变量放在 stack 里面
malloc 的内存空间放在 heap 里面 - For every malloc() there should be a corresponding free()
when free'd, that heap space may be re-used by the system
free(p);
p = NULL; - When a program teminates, all memory the program uses is automatically freed.
It is not dangerous to forget to free memory at the end of a program,
but it's poor style. Sometimes it's dangerous. - Realloc
If the space returned by a malloc() is too little, you can create more by calling realloc().char *p = malloc(SIZEINBYTES)
.
.
char *pext = realloc(p, BIGGERSIZEINBYTES);
.
.
06. Week04 - Lec01 - LinkedLists
- 与数组相比的优点:
- 动态分配空间,可以是任意大小
- 数组需要连续的空间,不好把控
- 更容易删除节点
- 与数组相比的缺点:声明:
- 不能随机访问,数组可以
- 只能连续访问,但是数组更快
- 操作量更大
- 每个元素都有一个额外的指针
- Sedgwick
typedef struct node *Link;
struct node{
int data;
Link next;
}Albert
typedef struct node {
int data;
struct node *next;
} List; - 通过 LinkedList 来实现 Quack
- push:将 node 放在 head 之后,就是最前面
- qush:需要找到最后一个节点,然后直接连接上
- pop:Quack qs,qs 不能是 NULL,不能使 Empty, 删除第一个 node 并且释放
- destroyQuack:释放出 head 之外的所有节点
- makeEmptyQuack:pop 掉所有节点
- isEmptyQuack:是否头节点指向 NULL
- showQuack:按顺序输出 data
- Head nodeA linkedlist ADT interface
- 创建出来的
- 存储的是 dummy data,最大值,没意义
- 不能被删除,不是 linkedlist 信息内容
- 如果 quack 为空,指向 NULL
- 如果 quack 不为空,指向 top node
- 返回 head node 给 client
- typedef struct node *List;
...
因此 interface 里面可以用到 List - A linkedlist ADT
struct node {
int data;
struct node *Next;
};
...因此 ADT 里面可以用访问结构体内部信息
07. Week06 - Lec01 - Heaps
- heap memory
- a programmer can request heap memory by using malloc()
- the memory is 'returned' to the heap by using free()
- heap data structure
- higher-level than array and linked list
- a heap data structrue is built on an array, or linked list
- two-dimensional because it forms a tree of data
- a binary tree (at most 2 children per node)
- higher-level than array and linked list
- Max-heaps
- the element with the largest key is at the root
- the children of a node have keys smaller than the parent
- Min-heaps
- the element with the smallest key is at the root
- the children of a node have keys larger than the parent
- 2 properties
- HOP (Heap-Order Property): 子节点的值比父节点小或者大
- CTP (Complete-Tree Property): 完全二叉树,可以保证子节点与父节点的位置是2倍关系
- heaps 用来实现 priority queues,使用在最短路径算法
- Heap Implementation
- 根节点索引值为 1
- 给定节点索引 i:
- 它的左节点是 2i
- 它的右节点是 2i + 1
- 它的父节点是 i/2(使用整数结果)
- Operation on heaps
- Get the maximum element
- 根节点,位置 1
- Insert an element
- 把新要素放到最下面最右边的位置,放在数组的最后
- fixUp:随着单一路径向根部传递,按照 Heap-Order Property
- Delete the maximum element
- delMax 大根堆
- delMin 小根堆
- 删除叶子节点,可以直接操作
- 删除根节点
- 删除root,将最后的元素替换掉,并将其删除,然后
- fixDown from the root (Heap-Order Property)
- return the saved root node
- fixDown:随着单一路径从根部向叶子节点传递
- Get the maximum element
08. Week06 - Lec02 - Priority Queues and Heap Sort
- A priority queue supports 2 abstract operations:
- insert: a new item (push, qush)
- delMax (pop)
- A Priority Queue ADT
- insertPQ:通过 fixUp,构建大根堆
- delMaxPQ:删掉第一个值,然后执行 fixDown
- Three implementations of the PQ ADT
- an unordered array
struct pqRep {
int nItems; // actual count of Items
int *items; // array of Items
int maxsize; // maximum size of array
};- 插入数据,放置在最后
- 删除最大值,通过遍历来获取
- an ordered array
- 插入数据,查找数据按照顺序的位置,然后将后面的数据逐个后移,将数据插入
- 删除最大值,就是最后一个元素
- a heapified array
- 插入数据,调用 fixUp,将数据进行排序
- 删除最大值,删除一个元素,最后一个元素补入,通过 fixDown 操作
- an unordered array
09. Week07 - Lec01 - Graphs
- Complete graph
- 任何两点都有相连的边
- |E| = V(V-1)/2 (排列组合里面 V 个顶点,里面随机选取 2 个的可能性)
- Clique
- a complete subgraph
- a subset of vertices that form a complete graph
- Sparseness/denseness of a gragh
- dense graphs: |E| is closer to V(V-1)/2
- sparse graphs: |E| is closer to V (minimum is V-1)
- Tree
- a connected (sub)graph with no cycles
- Spanning Tree
- a tree that contains all vertices in the graph (可以是所有孤立的点,只删除 edges,点集不变)
- 包含所有 graph 中顶点的 tree,例如 最小生成树
- Terminology
- graph;顶点集与边集
- subgraph:顶点集与边集都是 上面graph 的子集
- induced subgraph:导出子图,顶点集是子集(只删除顶点的子集,相连的边会自动删除)
ref: https://www.youtube.com/watch?v=dPHkyRvLtIU - cycle:首尾相接
- connected graph:连接所有顶点的路径
- adjacency:两个相连接的顶点
- degree:一个顶点与其相连的顶点个数
- Hamiltonian path and cycle
- 每个顶点只访问一次,path
- 如果首尾相接,cycle(所有顶点的degree >= n/2)
- Eulerian path and cycle
总结:- 遍历每条边正好一次,path(恰好只有2个顶点有奇数的degree)
- 如果收尾相接,cycle(所有的顶点都是偶数的degree)
- 两者都不是:看奇数degree顶点个数,如果1个或者大于2个,都是没有的
- 1. 不可能同时有欧拉path或者cycle
2. 有Hamiltonian cycle一定有path
3. 判断H 回路 的方法就是所有顶点的度都≥n/2
4. 判断欧拉path:只有两个是奇数的度
5. 判断欧拉cycle:所有都为偶数的度 - 其他
- Undirected Graphs
- edge(u, v) = edge(v, u)
- no self-loops (i.e. no edge(v, v))
- Directed Graphs
- edge(u, v) ≠ edge(v, u)
- can have self-loops (i.e. edge(v, v))
- Weighted Graphs
- 有权值的
- road map
- Multi-graph
- 两个顶点间可以拥有多条边相连
- 去一个地方的不同方式,线路等
- Undirected Graphs
- Implementing Graphs
- Vertex that is represented by an int
- Edge that is represented by 2 vertices
- Graph that is represented by an Adjacency matrix, or as an Adjacency list.
- building:
create a graph
create an edge
add an edge to a graph
deleting
remove an edge from a graph
remove and free a graph
printing
'show' a graph - Edge
typedef int Vertex; // define a VERTEX typedef struct { // define an EDGE
Vertex v;
Vertex w;
} Edge;
- GraphAM.c: based on an Adjacency Matrix
-
struct graphRep {
int nV; // #vertices
int nE; // #edges
int **edges; // matrix of Booleans ... THIS IS THE ADJACENCY MATRIX
};
-
- GraphAL.c: based on an Adjacency Linked List
-
typedef struct node *List;
struct node {
Vertex name;
List next;
}; struct graphRep {
int nV; // #vertices
int nE; // #edges
List *edges; // array of linked lists ... THIS IS THE ADJACENCY LIST
}; - insertEdge: 两个顶点都要加,并且是插入到头部的位置
-
- Reading a graph
- scanf("%d", &v) != EOF,说明没有读取到文件结尾
- scanf("%d", &v) != '\n',说明没有读取到换行符
- scanf 会自动跳过空格、制表符或者回车
-
int readNumV(void) { // returns the number of vertices numV or -1
int numV;
char w[WHITESPACE];
scanf("%[ \t\n]s", w); // skip leading whitespace
if ((getchar() != '#') ||
(scanf("%d", &numV) != 1)) {
fprintf(stderr, "missing number (of vertices)\n");
return -1;
}
return numV;
} -
int readGraph(int numV, Graph g) { // reads number-number pairs until EOF
int success = true; // returns true if no error
int v1, v2;
while (scanf("%d %d", &v1, &v2) != EOF && success) {
if (v1 < 0 || v1 >= numV || v2 < 0 || v2 >= numV) {
fprintf(stderr, "unable to read edge\n");
success = false;
}
else {
insertEdge(newEdge(v1, v2), g);
}
}
return success;
}
10. Week07 - Lec02 - Graph search
- Depth-first search (BFS)
- stack
-
//HANDLES CONNECTED GRAPHS ONLY
void dfsQuack(Graph g, Vertex v, int numV) {
int *visited = mallocArray(numV);
Quack s = createQuack();
push(v, s);
showQuack(s);
int order = 0;
while (!isEmptyQuack(s)) {
v = pop(s);
if (visited[v] == UNVISITED) { // we visit only unvisited vertices
printArray("Visited: ", visited, numV);
visited[v] = order++;
for (Vertex w = numV - 1; w >= 0; w--) { //push adjacent vertices
if (isEdge(newEdge(v,w), g)) { // ... in reverse order
push (w, s); // ... onto the stack
}
}
}
showQuack(s);
}
printArray("Visited: ", visited, numV);
free(visited);
return;
}
- dfsQuack() (disconnected graphs)
- 设置一个参数 allVis,判断是否全部顶点都访问了
-
//HANDLES CONNECTED GRAPHS ONLY
void dfsQuack(Graph g, Vertex v, int numV) {
int *visited = mallocArray(numV);
Quack s = createQuack();
push(v, s);
showQuack(s);
int order = 0;
while (!isEmptyQuack(s)) {
v = pop(s);
if (visited[v] == UNVISITED) { // we visit only unvisited vertices
printArray("Visited: ", visited, numV);
visited[v] = order++;
for (Vertex w = numV - 1; w >= 0; w--) { //push adjacent vertices
if (isEdge(newEdge(v,w), g)) { // ... in reverse order
push (w, s); // ... onto the stack
}
}
}
showQuack(s);
}
printArray("Visited: ", visited, numV);
free(visited);
return;
}
- Recursive Depth-First Search
- 包含了 disconnected graphs 的情况,另外函数内部加入指针,可以改变地址同时相当于全局变量
-
void dfs(Graph g, Vertex rootv, int numV) {//'wrapper' for recursive dfs
int *visited = mallocArray(numV); // ... handles disconnected graphs
int order = 0;
Vertex startv = rootv; // this is the starting vertex
int allVis = 0; // assume not all visited
while (!allVis) { // as long as there are vertices
dfsR(g, startv, numV, &order, visited);
allVis = 1; // are all visited now?
for (Vertex w = 0; w < numV && allVis; w++) { // look for more
if (visited[w] == UNVISITED) {
printf("Graph is disconnected\n"); // debug
allVis = 0; // found an unvisited vertex
startv = w; // next loop dfsR this vertex
}
}
}
printArray("Visited: ", visited, numV);
free(visited);
return;
} void dfsR(Graph g, Vertex v, int numV, int *order, int *visited) {
visited[v] = *order; // records the order of visit
*order = *order+1;
for (Vertex w = 0; w < numV; w++) {
if (isEdge(newEdge(v,w), g) && visited[w]==UNVISITED) {
dfsR(g, w, numV, order, visited);
}
}
return;
}
- Breadth First Search
- queue
-
void bfsQuack(Graph g, Vertex v, int numV) { //name change
int *visited = mallocArray(numV);
Quack q = createQuack();
qush(v, q); //qush, not push
showQuack(q);
int order = 0;
while (!isEmptyQuack(q)) {
v = pop(q);
if (visited[v] == UNVISITED) {
printf("Visit %d\n", v); // debug
visited[v] = order++;
for (Vertex w = 0; w < numV; w++) {//vertex order
if (isEdge(newEdge(v,w), g)) {
qush(w, q); //qush, not push
}
}
}
showQuack(q);
}
printArray("Visited: ", visited, numV);
free(visited);
makeEmptyQuack(q);
return;
}
11. Week08 - Lec02 - Applications of Graph Search
- Breadth-First Search: Shortest path
- ref: https://www.cnblogs.com/alex-bn-lee/p/11242024.html#A42
-
void shortestPath(Graph g, Vertex start, Vertex goal, int numV) {
int *visited = mallocArray(numV);
int *parent = mallocArray(numV); // THIS IS NEW Quack q = createQuack();
qush(start, q);
int order = 0;
visited[start] = order++;
int found = 0;
while (!isEmptyQuack(q) && !found) { // FOUND TELLS US TO STOP
Vertex v = pop(q);
for (Vertex w = 0; w < numV && !found; w++) {
if (isEdge(newEdge(v,w), g)) { // for adjacent vertex w ...
if (visited[w] == UNVISITED) { // ... if w is unvisited ...
qush(w, q); // ... queue w
printf("Doing edge %d-%d\n", v, w); // DEBUG
visited[w] = order++; // w is now visited
parent[w] = v; // w's PARENT is v
if (w == goal) { // if w is the goal ...
found = 1; // ..FOUND IT! NOW GET OUT
}
}
}
}
}
if (found) {
printf("SHORTEST path from %d to %d is ", start, goal);
printPath(parent, numV, goal); // print path from START TO GOAL
putchar('\n');
}
else {
printf("no path found\n");
}
printArray("Visited: ", visited, numV); // debug
printArray("Parent : ", parent, numV); // debug
free(visited);
free(parent);
makeEmptyQuack(q);
return;
} // head recursion
void printPath(int parent[], int numV, Vertex v) {
if (parent[v] != UNVISITED) { // parent of start is UNVISITED
if (0 <= v && v < numV) {
printPath(parent, numV, parent[v]);
printf("-->");
}
else {
fprintf(stderr, "\nprintPath: invalid goal %d\n", v);
}
}
printf("%d", v); // the last call will print here first
return;
}void printPath(int parent[], int numV, Vertex v) {
printf("%d", v);
if (0<=v && v<numV) {
Vertex p = parent[v];
while (p != UNVISITED) {
printf("<--%d", p);
p = parent[p];
}
}
else {
fprintf(stderr, "printPath: illegal vertex in parent[]\n");
}
}
- Depth-First Search: Cycle detection
- ref: https://www.cnblogs.com/alex-bn-lee/p/11242024.html#A43
-
// a wrapper for the recursive call to hasCycle()
void searchForCycle(Graph g, int v, int numV) {
int *visited = mallocArray(numV);
int order = 0; if (hasCycle(g, numV, v, v, &order, visited)) {
printf("found a cycle\n");
}
else {
printf("no cycle found\n");
}
printArray("Visited ", visited, numV);
free(visited);
return;
} int hasCycle(Graph g, int numV, Vertex fromv, Vertex v, int *order, int *visited) {
int retval = 0;
visited[v] = *order;
*order = *order+1;
for (Vertex w = 0; w < numV && !retval; w++) {
if (isEdge(newEdge(v,w), g)) {
if (visited[w] == UNVISITED) {
printf("traverse edge %d-%d\n", v, w);
retval = hasCycle(g, numV, v, w, order, visited);
}
else {
if (w != fromv) { // exclude the vertex we've just come from
// WE HAVE REVISITED A VERTEX ==> CYCLE printf("traverse edge %d-%d\n", v, w);
retval = 1;
}
}
}
}
return retval;
}
- Depth-First Search: Eulerian cycles
- https://www.cnblogs.com/alex-bn-lee/p/11242024.html#A44
-
void findEulerianCycle(Graph g, int numV, Vertex startv) {
Quack s = createQuack();
printf("Eulerian cycle: "); push(startv, s);
while (!isEmptyQuack(s)) {
Vertex v = pop(s); // pop and then ...
push(v, s); // ... push back on, so no change
Vertex w = getAdjacent(g, numV, v); // get largest adj. v
if (w >= 0) { // if true, there is an adj. vertex
push(w, s); // push this vertex onto stack
removeEdge(newEdge(v, w), g); // remove edge to vertex
}
else { // top v on stack has no adj. vertices
w = pop(s);
printf("%d ", w);
}
}
putchar('\n');
} Vertex getAdjacent(Graph g, int numV, Vertex v) {
Vertex retv = -1; // assume no adj. vertices
for (Vertex w = numV-1; w >= 0 && retv == -1; w--) {
if (isEdge(newEdge(v, w), g)) {
retv = w; // found largest adj. vertex
}
}
return retv;
}
- Depth-First Search: Finding a path
- https://www.cnblogs.com/alex-bn-lee/p/11242024.html#A45
void dfsR(Graph g, Vertex v, int numV, int *order, int *visited) {
visited[v] = *order;
*order = *order+1;
for (Vertex w = 0; w < numV; w++) {
if (isEdge(newEdge(v,w), g) && visited[w]==UNVISITED) {
dfsR(g, w, numV, order, visited);
}
}
return;
} int isPath(Graph g, Vertex v, Vertex goalv, int numV, int *order, int *visited) {
int found = 0;
visited[v] = *order;
*order = *order+1;
if (v == goalv) {
found = 1;
}
else {
for (Vertex w = 0; w < numV && !found; w++) {
if (isEdge(newEdge(v,w), g)) {
if (visited[w] == UNVISITED) {
found = isPath(g, w, goalv, numV, order, visited);
printf("path %d-%d\n", w, v);
}
}
}
}
return found;
}
12. Week09 - Lec01 - Weighted Graph Algorithms
- Dijkstra 算法
- Prim 算法Kruskal 算法
- Firefox 崩溃了,数据丢了,坑爹~
13. Week09 - Lec02 - Binary Search Trees
- Height:具体根节点的高度,root 为 0
- Node level or depth:跟 Height 差不多
- Types of trees
- ordered tree: 有序树
- binary tree: 二叉树
- full binary tree: 满二叉树,每个节点都有2个孩子,可能参差不齐
- perfect binary tree: 完美二叉树,类似三角形那种,都铺满了
- ordered binary tree:有序二叉树,也叫做 binary search tree,二叉搜索树
- left subtree values <= parent value
- right subtree values >= parent value
- full m-ary tree: 类似满二叉树
- Binary Trees
- balanced:尽量满,最短的
- degenerate:退化的,一边倒,最长的
- Height of a binary tree
- 最长路径的长度
- degenerate: height is n-1
- balanced: height is ln(n)
- Data structure for a binary tree
typedef struct node *Tree;
struct node {
int data;
Tree left;
Tree right;
};
- Comparing Binary Search Trees and Heaps
Searching in BSTs
- 结束条件就是 顶点 为 NULL,返回 0,或者查到了 v 值,返回 1
int searchTree(Tree t, int v){ // Search for the node with value v
int ret = 0;
if (t != NULL) {
if (v < t->data) {
ret = searchTree(t->left, v);
}
else if (v > t->data) {
ret = searchTree(t->right, v);
}
else { // v == t->data
ret = 1;
}
}
return ret;
} // returns non-zero if found, zero otherwise
Creating a node in a BST
- 构建结构体,建立数,结构体是一个 node 点,但是树是一个指针 Tree,连接着所有的点
- 创建树,首先就是分配内存空间,然后就是赋值
默认左右节点都为 NULL typedef struct node *Tree;
struct node {
int data;
Tree left;
Tree right;
}; Tree createTree(int v) {
Tree t;
t = malloc(sizeof(struct node));
if (t == NULL) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
t->data = v;
t->left = NULL;
t->right = NULL;
return t;
}
- Freeing a node in a BST
- 结束条件就是遇到叶子节点,因为其左右节点均为 NULL
void freeTree(Tree t) { // free in postfix fashion
if (t != NULL) {
freeTree(t->left);
freeTree(t->right);
free(t);
}
return;
}
- Inserting a node in a BST
- 重点是返回值是 Tree
结束条件就是遇到叶子节点,因为其左右节点均为 NULL,因此会创建新的 node 点,然后将值依次返回
由于返回值为 Tree,因此自动连接的起来 Tree insertTree(Tree t, int v) {
if (t == NULL) {
t = createTree(v);
}
else {
if (v < t->data) {
t->left = insertTree (t->left, v);
}
else {
t->right = insertTree (t->right, v);
}
}
return t;
}- do it iteratively
- 通过遍历找到对应的叶子节点,然后连接上 node
Tree insertTreeI(Tree t, int v) { // An iterative version of the above
if (t == NULL) {
t = createTree(v);
}
else { // t != NULL
Tree parent = NULL; // remember the parent to link in new child
Tree step = t;
while (step != NULL) { // this is the iteration
parent = step;
if (v < step->data) {
step = step->left;
}
else {
step = step->right;
}
} // step == NULL
if (v < parent->data) {
parent->left = createTree(v);
}
else {
parent->right = createTree(v);
}
}
return t;
}
- 重点是返回值是 Tree
- Printing the tree
- 遍历打印,先左 再中 后右
void printTree(Tree t) { // not the final version
if (t != NULL) {
printTree (t->left);
printf ("%d ", t->data);
printTree (t->right);
}
return;
}- 2D printing
- 每次递归,depth 自增,打印数据的时候,先将制表符打印出来,出现层次感
void printTree(Tree t, int depth) { // extra depth parameter
if (t != NULL) {
depth++;
printTree (t->left, depth);
for (int i=1; i<depth; i++){ // 'depth'*whitespace
putchar('\t');
}
printf ("%d\n", t->data); // node to print
printTree (t->right, depth);
}
return;
}- 反方向打印
void printTree2(Tree t, int depth) {
if (t != NULL) {
depth++;
printTree2 (t->right, depth);
int i;
for (i=1; i<depth; i++){
putchar('\t');
}
printf ("%d\n", t->data);
printTree2 (t->left, depth);
}
return;
}- 效果如下:
1
2
3
4
5
6
7
8
====================================
8
7
6
5
4
3
2
1
- Count the number of nodes in a BST
- 遍历相加,每个节点为 1
结束条件就是叶子节点,因为其左右节点均为 NULL,自动返回 1,然后不停回溯。。 int count(Tree t){
int countree = 0;
if (t != NULL) {
countree = 1 + count(t->left) + count(t->right);
}
return countree;
}
- 遍历相加,每个节点为 1
- Find the height of a BST
- 对于叶子节点本身,高度为 0,因此 NULL 节点的高度就为 -1
parent 节点的高度 = 1 + MAX (left height, right height) int max(int a, int b){
if (a >= b) {
return a;
}
return b;
} int height(Tree t){
int heightree = -1;
if (t != NULL){
heightree = 1 + max(height(t->left), height(t->right));
}
return heightree;
}
- 对于叶子节点本身,高度为 0,因此 NULL 节点的高度就为 -1
- How balanced is a BST?
How do the number of nodes in the left sub-tree and the right sub-tree compare?- 分别计算出左右子树的节点数,然后相减即可
int balance (Tree t){ // calculates the difference between left and right
int diff = 0; if (t != NULL) {
diff = count(t->left) - count(t->right); // count declared elsewhere
if (diff < 0) {
diff = –diff;
}
}
return diff;
}
- Deleting a node from a BST
- 3 cases
- node is a leaf: unlink node from parent
- node has 1 child: replace the node by its child
- node has 2 children: rearrange the tree in some way
- DLMD: Deepest Left-Most Descendent
- DRMD: Deepest Right-Most Descendent
- 把 DLMD 摘下来,左边连接 t1,右边连接 t2
DLMD 为最左,因此其次需要将其 parent 节点的 left 与 DLMD 的 right 相连
p->left = nuroot->right
注意需要保留 DLMD 的 parent 节点 Tree deleteTree(Tree t, int i){ // delete node with value 'v'
if (t != NULL) {
if (v < t->data) {
t->left = deleteTree(t->left, v);
}
else if (v > t->data) {
t->right = deleteTree(t->right, v);
}
else { // v == t->data, so the node 't' must be deleted
// next fragment of code violates style, just to make logic clear
Tree n; // temporary
if (t->left==NULL && t->right==NULL) n=NULL; // 0 children
else if (t->left ==NULL) n=t->right; // 1 child
else if (t->right==NULL) n=t->left; // 1 child
else n=joinDLMD(t->left,t->right);
free(t);
t = n;
}
}
return t;
}// Joins t1 and t2 with the deepest left-most descendent of t2 as new root.
Tree joinDLMD(Tree t1, Tree t2){
Tree nuroot;
if (t1 == NULL) { // actually should never happen
nuroot = t2;
}
else if (t2 == NULL) { // actually should never happen
nuroot = t1;
}
else { // find the DLMD of the right subtree t2
Tree p = NULL;
nuroot = t2;
while (nuroot->left != NULL) {
p = nuroot;
nuroot = nuroot->left;
} // nuroot is the DLMD, p is its parent
if (p != NULL){
p->left = nuroot->right; // give nuroot's only child to p
nuroot->right = t2; // nuroot replaces deleted node
}
nuroot->left = t1; // nuroot replaces deleted node
}
return nuroot;
}
- 3 cases
14. Week10 - Lec01 - Splay Trees
- Right rotation
- its right child moves to the root's left child the root moves to its right child
- 折下来,parent变成child
- Left rotation
- its left child moves to the root's right child the root moves to its left child
- 翻上去,child变成parent
- 相当于告诉了root,然后获取转折点,第一个是 E 是root,然后获取 C,同理。。
然后就是连接起来即可 // NOT REAL CODE: CHECKS REQUIRED: ACTUAL CODE BELOW
Tree rotateR(Tree root) { // old root
Tree newr = root->left; // newr is the new root
root->left = newr->right; // old root has new root's right child
newr->right = root; // new root has old root as right child
return newr; // return the new root
}
// NOT REAL CODE: CHECKS REQUIRED: ACTUAL CODE BELOW
Tree rotateL(Tree root) { // old root
Tree newr = root->right; // newr will become the new root
root->right = newr->left; // old root has new root's left child
newr->left = root; // new root has old root as left child
return newr; // return the new root
}
- root insertion
assume that we have just inserted node G in the BST below
rotate right the subtree with root the parent of G (i.e. H)
rotate right the subtree with root the parent of G (i.e. R)
rotate left the subtree with root the parent of G (i.e. E)
- 一次只是转一次,保证 G 不停向上走
- Counting the number of nodes in a subtree
typedef struct node *Tree;
struct node {
int data;
Tree left;
Tree right;
int count;
}; Tree createTree(int v) {
Tree t = malloc (sizeof(struct node));
if (t == NULL) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
t->data = v;
t->left = NULL;
t->right = NULL;
t->count = 1;
return t;
} int sizeTree(Tree t) {
int retval = 0;
if (t != NULL){
retval = t->count;
}
return retval;
} Tree insertTree(Tree t, int v) {
if (t == NULL) {
t = createTree(v);
}
else {
if (v < t->data) {
t->left = insertTree (t->left, v);
}
else {
t->right = insertTree (t->right, v);
}
t->count++; // update the counter at each ancestor
}
return t;
}
- Selecting the i-th element from a BST
获取某一索引的节点值
类似数组里面 arr[3] 的意思
Balanced Trees- check the number of nodes in the left subtree
- if the left subtree contains numOnLeft elements, and
- numOnLeft>i, we recursively look in this left subtree for element i, else if
- numOnLeft<i, we recursively look in the right subtree for element i-(numOnLeft+1), else if
- numOnLeft=i, return with the current element's data
- if the left subtree contains numOnLeft elements, and
- 如果在左子树直接递归
如果在右子树,需要减掉所有左子树+root的节点数 // For tree with n nodes – indexes are 0..n-1
int selectTree(Tree t, int i) { // for tree with n nodes - indexed from 0..n-1
int retval = 0;
if (t != NULL) {
int numOnLeft = 0; // this is if there is no left branch
if (t->left != NULL) {
numOnLeft = t->left->count; // this is if there is a left branch
}
if (numOnLeft > i) { // left subtree or ...
retval = selectTree(t->left, i);
}
else if (numOnLeft < i) { // ... right subtree ?
retval = selectTree(t->right, i-(numOnLeft+1));
}
else {
retval = t->data; // value index i == numOnLeft
}
}
else {
printf("Index does not exist\n");
retval = 0;
}
return retval;
}
- check the number of nodes in the left subtree
- Approach 1: Global rebalancing
- Rotating with a count
- Example of left rotation
- 算法与上面一致,只是增加了 count 的计算
对于 E 的count就是等于前面 A 的count
对于 A 的count就直接计算他的左右节点 Tree rotateLeft(Tree t) { // Rotate left code: includes count field
Tree retval = NULL;
if (t != NULL) {
Tree nuroot = t->right; // left rotate: hence right root-child will become new root
if (nuroot == NULL) {
retval = t;
}
else {
t->right = nuroot->left; // the left child of nuroot becomes old root's right child
nuroot->left = t; // nuroot's left child is the old root
nuroot->count = t->count; // nuroot and old root have the same count
t->count = 1 + sizeTree(t->left) + sizeTree(t->right); // recompute count in old root
retval = nuroot; // return with the new root
}
}
return retval;
}
- 算法与上面一致,只是增加了 count 的计算
- Example of right rotation
Tree rotateRight(Tree t) { // Rotate right code: includes count field
Tree retval = NULL;
if (t != NULL) {
Tree nuroot = t->left;
if (nuroot == NULL) {
retval = t;
}
else {
t->left = nuroot->right;
nuroot->right = t;
nuroot->count = t->count;
t->count = 1 + sizeTree(t->left) + sizeTree(t->right);
retval = nuroot;
}
}
return retval;
}
- Example of left rotation
- Partitioning,分隔,分区
We can rewrite select() to partition around the ith element in the BST:
- we descend recursively
to the ith element of each subtree
rotate-with-count that element in the opposite direction to its position
if it is a left child: do a right rotation
if it is a right child: do a left rotation
this will make the ith element the root of the BST
- we descend recursively
- 将第 i 个元素作为 root
不停的左右移动 Tree partition(Tree t, int i) { // make node at index i the root
Tree retval = NULL;
if (t != NULL) {
int numOnLeft = 0;
if (t->left != NULL) {
numOnLeft = t->left->count;
}
if (numOnLeft > i) {
t->left = partition(t->left, i);
t = rotateRight(t);
}
if (numOnLeft < i) {
t->right = partition(t->right, i-(numOnLeft+1));
t = rotateLeft(t);
}
retval = t;
}
else {
printf("Index does not exist\n");
retval = NULL;
}
return retval;
}
- Balancing the BST
Approach 2: Local RebalancingMove the median node to the root by partitioning on i = count/2
- balance the left sub-tree
- balance the right sub-tree
- 先把 median 移动到 root
递归移动左右,最终所有的都是 median 在中间 Tree balance(Tree t) {
Tree retval = NULL;
if (t != NULL) {
if (t->count <= 1){
retval = t;
}
else {
t = partition(t, t->count/2);
t->left = balance(t->left);
t->right = balance(t->right);
retval = t;
}
}
return retval;
}
Splaying an item to the top means to move an item to the root of the BST, using one of the operations:
zigzag
zigzig
zigzag operation (先下后上)
the item is the right child of the parent, which is a left child of the grandparent
- do a rotate-left of the parent followed by a rotate-right of the grandparent
- /
\ 先是左转parent
/
/ 然后再右转grandparent /\
the item is the left child of the parent, which is a right child of the grandparent
- do a rotate-right of the parent followed by a rotate-left of the grandparent
- \
/ 先是右转parent
\
\ 然后再左转grandparent /\
zigzig operation (先上后下)
the item is the left child of the parent, which is a left child of the grandparent
do a rotate-right of the grandparent followed by a rotate-right of the parent
- (not a rotate-right of the parent followed by a rotate-right of the grandparent
- /
/ 先是右转grandparent /\ 然后再右转parent
\
\
the item is the right child of the parent, which is a right child of the grandparent
do a rotate-left of the grandparent followed by a rotate-left of the parent
- (not a rotate-left of the parent followed by a rotate-left of the grandparent
- \
\ 先是左转grandparent /\ 然后再左转parent
/
/
- Example
H is the right child of the left child so requires a zigzag splay
- this is simply a left-rotate followed by a right-rotate
P is the left child of the right child so requires a zigzag splay
- this is simply a right-rotate followed by a left-rotate
C is the left child of the left child so requires a zigzig splay
a zigzig splay does a right-rotate of the grandparent M, followed by another right-rotate of the parent E
T is the right child of the right child so requires a zigzig splay
a zigzig splay does a left-rotate of the grandparent M, followed by a left-rotate of the parent Q
The first double rotation below is not a zigzig rotation, the second is:
区别在于,第一个图是 先下后上
而 zigzig 是 先上后下
- Operation splay-search
If we search for an item, and move the item to the root by using normal left and right rotations then there is no rebalancing
- for example: a normal search for item 1 would result in
每次移动一个节点,就是 1 不停向上移动
- for example: a normal search for item 1 would result in
If we splay-search for an item, once found we
- splay the item to the root
1作为目标节点,整体是一个左左型,从grandparent右转,然后parent右转
- splay the item to the root
If the item is not found
- the last item (leaf) on the path is splayed to the root
For example, in the following splay tree we search for item 3, and because it fails, the 'last' item accessed, which is 4, is splayed:
找3,结果找到了4,那就移动4,就是判断点是否为叶子节点,是的话就动
- Operation splay-insertion
- We have seen 2 types of BST insertion, leaf insertion and root insertion. Assume that we wish to insert the sequence of numbers 3, 2, 4, 5, 1.
leaf insertion
- each node is simply added as a leaf
按照 zigzig,zigzag 来操作
分别 先上后下,先下后上
- each node is simply added as a leaf
root insertion
- each node is added as a leaf, and then
- that leaf is promoted to the root position rotating the parent node to the left or right at each step
就是一个一个移动到 root
- Operation splay deletion
Normally, when an item is deleted in a BST, the node is simply replaced by its DLMD predecessor or DRMD successor.
In splay-deletion, if you wish to delete a node y:
delete the node y as normal using either:
DRMD: if the predecessor of y is x, and the parent of x is p, then
follow the deletion by splaying p
DLMD: if the successor node of y is z, and the parent of z is p, then
follow the deletion by splaying p
首先把 DLMD 替换 root,然后再将 DLMD 的 parent 移动到 root
For example, let us splay-delete item 2 from the following splay tree
we will use DLMD (DRMD would be similar)
要删除2节点
successor为4,4的父节点是5
把4替换为2节点,作为root
再把5移动上去
- Minimum or maximum of a splay tree
- search for the minimum or maximum item in a BST
- splay the item to the root
For example, we show below a splay tree after
- a search for the minimum and then
- a search for the maximum item
in summary
2-3-4 trees
We note that in each operation:
search for an item in a splay tree
- the found item is splayed
- if not found, the last accessed item is splayed
insertion of an item in a splay tree
- the new item is splayed
deletion of an item in a splay tree
- the parent of the item that replaces the deleted item is splayed
search for the minimum/maximum item in a splay tree
- the found minimum/maximum is splayed
A 'splay' of an item involves:
zigzig whenever the item is left-left or right-right
zigzag whenever the item is left-right or right-left
zig whenever the item is the child of the root (so a zigzag or zigzig is not possible)
Splay Tree Analysis
number of comparisons per operation is O(log(n))
- gives good (amortized) cost overall
no guarantee for any individual operation: worst-case behaviour may still be O(N)
remember, amortisation means averaging over a large number k operations
- is self-balancing
- is commonly used to implement dictionaries
- has the property that all external nodes are at the same depth
- generalises the concept of a node
- 3 types of nodes:
2nodes: 1 key, a left link to smaller nodes, a right link to larger nodes (this is the normal node)
里面只有一个key就是一个字母,类似二叉树,因此只存在大小之分3nodes: 2 keys, a left link to smaller, a middle link to in-between, and right link to larger
里面两个字母,小于,中间,大于三部分4nodes: 3 keys: links to the 4 ranges
三个字母, M O S 分成四个部分
In the above example:
- 'A' is an example of a 2node
- 'LM' of a 3node
- 'EGH' of a 4node
Example of a search, for 'P':
- start at the root 'I'
- larger, go right to 'NR'
- middle, go middle to 'P'
Examples of insertions:
- 'B': easy, change node 'A' into 'AB'
- 'O': easy, change node 'NR' into 'NOR'
'D': the node is full ??
4node insertions
- split the 4node分裂4度节点:
- left key becomes a new 2node 左节点变成一个新的2度节点
- middle key goes up to parent 中间节点上提到parent里面
- right node becomes a new 2node 右节点变成一个新的2度节点
- add new key to one of the new 2nodes, creating a 3node
Example: insert 'D' in a 2-3-4 tree
This insertion assumes that you can move the 'middle' key up to the parent.
What happens if the parent is a 4node, and its parent is a 4node, and its parent ... ?
Solution, during insertion, on the way down:
[A] transform 2node->4node into 3node->(2node+2node)
[B] transform 3node->4node into 4node->(2node+2node)
[C] if the root is a 4node, transform into 2node->(2node+2node)
Example:
Basic insertion strategy:
because of the transformations (splits) on inserts on the way down, we often end on a 2node or 3node, so easy to insert
- insert at the bottom:
- if 2node or 3node, simply include new key
- if 4node, split the node
4node需要将中间节点上调,上调后parent又需要上调,以此类推
相当于递归了- if the parent is a 4node, this needs to be split
- if the grandparent is a 4node, this needs to be split
- ...
- if the grandparent is a 4node, this needs to be split
- if the parent is a 4node, this needs to be split
Results:
- trees are
split from the top down
(split and) grow from the bottom up
after insertions or deletions, 2-3-4 trees remain in perfect balance
What do you notice about these search trees?
2-3-4 trees are actually implemented using BSTs!
- 3 types of nodes:
【433】COMP9024 复习的更多相关文章
- iOS总结_UI层自我复习总结
UI层复习笔记 在main文件中,UIApplicationMain函数一共做了三件事 根据第三个参数创建了一个应用程序对象 默认写nil,即创建的是UIApplication类型的对象,此对象看成是 ...
- vuex复习方案
这次复习vuex,发现官方vuex2.0的文档写得太简略了,有些看不懂了.然后看了看1.0的文档,感觉很不错.那以后需要复习的话,还是先看1.0的文档吧.
- 我的操作系统复习——I/O控制和系统调用
上篇博客介绍了存储器管理的相关知识——我的操作系统复习——存储器管理,本篇讲设备管理中的I/O控制方式和操作系统中的系统调用. 一.I/O控制方式 I/O就是输入输出,I/O设备指的是输入输出设备和存 ...
- 复习(1)【Maven】
终于开始复习旧知识了,有输入必然要有输出.输入和输出之间的内化过程尤为重要,在复习的同时,真正把学到的东西积淀下来,加深理解. Maven项目概念与配置 Maven是一个项目管理和综合工具.Maven ...
- 《CSS权威指南》基础复习+查漏补缺
前几天被朋友问到几个CSS问题,讲道理么,接触CSS是从大一开始的,也算有3年半了,总是觉得自己对css算是熟悉的了.然而还是被几个问题弄的"一脸懵逼"... 然后又是刚入职新公司 ...
- JS复习--更新结束
js复习-01---03 一 JS简介 1,文档对象模型 2,浏览器对象模型 二 在HTML中使用JS 1,在html中使用<script></script>标签 2,引入外部 ...
- jQuery 复习
jQuery 复习 基础知识 1, window.onload $(function(){}); $(document).ready(function(){}); 只执行函数体重的最后一个方法,事 ...
- jQuery5~7章笔记 和 1~3章的复习笔记
JQery-05 对表单和表格的操作及其的应用 JQery-06 jQuery和ajax的应用 JQery-07 jQuery插件的使用和写法 JQery-01-03 复习 之前手写的笔记.实在懒得再 ...
- HTML和CSS的复习总结
HTML(Hypertext Markup Language)超文本标记语言:其核心就是各种标记!<html> HTML页面中的所有内容,都在该标签之内:它主要含<head>和 ...
随机推荐
- PLSQl的使用技巧与快捷键
最近在开发过程中,遇到一些麻烦,就是开发效率问题,有时候其他同事使用PLSQL 编程效率明显高于自己,观察了好久,才发现他使用PLSQL 已经很长时间了而且,他自己也在其中添加了好多快捷方式, ...
- KVM虚拟机,如何设置虚拟机的CPU型号与物理机是一样的
1.在kvm主机上修改配置文件 [root@node160 ~]# virsh edit CentOS-7.3-X86_64 将xml配置文件中的: <cpu mode='custom' mat ...
- JUC-10-ReadWriteLock读写锁
ReadWriteLock同Lock一样也是一个接口,提供了readLock和writeLock两种锁的操作机制,一个是只读的锁,一个是写锁
- linux下Boost序列化问题解决
由于项目需要,要使用boost,所以在网上找了一些例子帮助理解,其中遇到很多问题,再次总结记录一下.#include <boost/archive/text_oarchive.hpp> # ...
- springboot开发人员工具(自动重启及相关的配置)
导入依赖: <dependencies> <dependency> <groupId>org.springframework.boot</groupId> ...
- zeebe 集成elasticsearch exporter && 添加operate
zeebe 的operate是一个功能比较强大的管理工具,比simple-monitor 有好多方面上的改进 安全,支持用户账户的登陆 界面更友好,界面比较符合开团队工作流引擎的界面 系统监控更加强大 ...
- SAM:后缀自动机
好文转载 luoguP3804 代码: /* 定义.对给定字符串s的后缀自动机是一个最小化确定有限状态自动机,它能够接收字符串s的所有后缀. 对给定字符串s的后缀自动机是一个最小化确定有限状态自动机, ...
- I9300 国行联通定制修复手记
同事拿来个I9300给我修,说是打不了电话,试了一下,打电话的时候会提示“网络未注册”,于是果断找了个国行的4.1.2五件套刷砖.5分钟后,刷机成功,开机拨号,依旧是这个问题,怎么回事呢?上百度一查, ...
- 如何使用git把本地代码上传到github
Git Bash Here git init git add . git commit -m '说明' git remote add origin https://github.com//.git g ...
- [转]MyEclipse基础学习:Java EE Learning Center
我就不翻译了,直接给出Java EE学习中心的原文链接: Java EE Learning Center 另外,给出MyEclipse IDE 环境中Apache Tomcat server服务器正常 ...