算法导论

二叉搜索树——算法导论(14)

拥有回忆 提交于 2020-01-30 05:39:41
1. 什么是二叉搜索树 顾名思义,二叉搜索树是以一棵二叉树来组织的。如下图,这样的一棵树可以使用一个链表数据结构来表示,其中的每一个节点是一个对象。除了key和卫星数据之外,每个节点还包含属性left(左孩子)、right(右孩子)、和p(双亲)(若不存在,则值为NIL)。 二叉搜索树中的关键字总是以满足 二叉搜索树性质 的方式存储: 设x是二叉搜索树的一个节点。如果y是x左子树中的一个节点,那么y.key≤x.key。如果y是x右子树中的一个节点,那么y.key≥x.key。 二叉搜索树性质允许我们使用一种简单的递归算法来按一定的顺序输出二叉搜索树中的所有关键字。我们常用的有 先序遍历 (输出的子树根的关键字位置位于左子树关键字和右子树关键字之间)、 中序遍历 、 后序遍历 。 下面给出先序遍历的递归算法: 我们可以证明:遍历一棵有n个节点的二叉搜索树需要花费θ(n)的时间(证明略)。 2. 查询二叉搜索树 (1) 这一小节,我们来讨论二叉搜索树的诸如:SEARCH,MINIMUM,MAXIMUN,SUCCESSOR,PERDECESSOR操作。 (2) search 我们使用如下一种算法去查询一棵二叉搜索树。该方法要求输入一个指向根节点的指针x和待查找的关键字k;输出为指向关键字为k的节点的指针(若存在。否则输出NIL)。 我们很容易知道,该查询算法的时间为O(h)

算法导论笔记

为君一笑 提交于 2020-01-30 02:22:30
算法导论笔记 第2章 2.1插入排序 输入: n个数的一个序列。 输出: 单调递增的数列。 伪码: INSERTION - SORT ( A ) // 1 for j = 2 to A . length 2 key = A [ j ] 3 // Insert A[j] into the sorted sequence A[1 .. j - 1] 4 i = j - 1 5 while i > 0 and A [ i ] > key 6 A [ i + 1 ] = A [ i ] 7 i = i - 1 8 A [ i + 1 ] = key 插入排序算法的简单c语言实现: void insertion_Sort ( int A_lengh ) { int j , i , key ; for ( j = 1 ; j <= A_lengh ; j ++ ) { key = A [ j ] ; i = j - 1 ; while ( i >= 0 && A [ i ] > key ) { A [ i + 1 ] = A [ i ] ; i = i - 1 ; } A [ i + 1 ] = key ; } } 图片描述 来源: CSDN 作者: 大脸蛋子 链接: https://blog.csdn.net/qq_41256768/article/details/104109553

算法导论第7章习题

纵然是瞬间 提交于 2020-01-26 00:34:46
做题要猜出题目的意图。 7.1-1 略 7.1-2 r-1, add if q=r-1, then $q = \lfloor (p+r)/2 \rfloor$。这说明元素全都一样的时候,quicksort伤不起。worst-case了,要$\Theta(n^2)$了。快排弱点1. 7.1-3 pivot要比较所有的数组元素,自然是n 7.1-4 PARTITION中Line4的<= changed to >= 7.2-1 T(n)=T(n-1)+\Theta(n)=T(0)+n*\Theta(n)=\Theta(n^2) 7.2-2 同7.1-2 7.2-3 These exercises demonstrate the weakness of the original quicksort. It’s the worst case. T(n) = T(n-1)+\Theta(n)=\Theta(n^2)。快排弱点2. 7.2-4 银行喜欢时间排序,用户喜欢支票号排序。这道题又说明了在nearly sorted list中,插入排序beat了快排。快排弱点3.因为用户使用多张支票是连号的,所以在一个时间段内,顶多是这几张支票打乱了顺序。而不是一天24小时之内所有的支票全部乱序。所以这个序列是差不多有序的。如果n个用户每次使用k张支票,插入排序最多需要O(nk)时间,如果k=1

算法导论第7章

心不动则不痛 提交于 2020-01-24 14:49:04
看CLRS一般把笔记写在书的margin里。Cormen的提纲也是蛮好的,适合复习。这里用的Partition方法是Lomuto Partition,不是原始的也是第1版中的Hoare Partition。后者被留作习题。快排Quicksort的时间复杂度worst-case是\Theta(n^2), 期望时间是\Theta(nlgn), 复杂度前面的常数很小,sort in place。 Descriptions of quicksort 快排是Divide-and-conquer的典范。 Divide: 用pivot把数组划分为两半A[p,…,q-1]和A[q+1,…,r]。前面的数组比A[q]小,后面的数组比A[q]大。 Conquer: 递归调用quicksort Combie: 不需要,sort in place 用来划分的函数叫做PARTITION,返回下标q。 Quicksort函数 def QUICKSORT(A, p, r): if p < r: q = PARTITION(A, p, r) QUICKSORT(A, p, q - 1) QUICKSORT(A, q + 1, r) Python美~ PARTITION函数 def PARTITION(A, p, r ): x = A[r ] i = p - 1 for j = p to r - 1: if A[

《算法导论》笔记 第7章 总结与思考

我们两清 提交于 2020-01-23 11:43:43
【思考题】 7-1 Hoare 划分的正确性 a) 说明 HOARE-PARTITION 算法在数组 A = <13, 19, 9, 5, 12, 8, 7, 4, 11, 2, 6, 21> 上的运行过程, 并说明在第 4~11 行中 while 循环的每一轮迭代后,数组中各元素的之和辅助值的情况。 p=1, r=12 [i=0] 13 19 9 5 12 8 7 4 11 2 6 21 [j=13] 6 [i=1] 19 9 5 12 8 7 4 11 2 [j=11] 13 21 6 2 [i=2] 9 5 12 8 7 4 11 [j=10] 19 13 21 i=10, j=9 return j = 9 b) 下标i和j满足这样的特点,即我们从不会访问数组A的、在子数组A[p..r]之外的元素。 第一次迭代后,若i与j相同,则只有 A[i] = A[j] = x = A[p] 一种情况,直接返回j。 若i与j不相同,则交换A[i] A[j],而在之后某次导致i>=j的迭代中,i至少会被之前的值为A[i]的元素拦截下来,同理,j也会被过去的A[j]拦截而不会越界。之后迭代结束返回j。 因此,不会访问到子数组之外的元素。 c) 当过程 HOARE-PARTITION 结束时,他返回的j值满足p<=j<r。 注意到只有 i>=j 时过程结束,每次迭代,i至少+1,j至少-1。

算法导论——求最大子数组问题

倖福魔咒の 提交于 2020-01-22 08:48:05
利用分治算法求最大子数组问题 #include<iostream> #include<stdio.h> #define _CRT_SECURE_NO_WARNINGS #pragma warning(disable:4996) int const N = 100; using namespace std; struct ARRAY { int high; int low; int sum; }; int A[N]; ARRAY FIND_MAX_SUBARRAY(int* A, int low, int high); ARRAY FIND_MAX_CROSSING_SUBARRAY(int* A, int low, int mid, int high); int main() { int i; int n=0; for (i = 1; i <= N-1; i++) { cin >> A[i]; n++; char c;//= cin.get(); c = getchar(); if (c != ' ') break; } printf("\n"); ARRAY array; array= FIND_MAX_SUBARRAY(A, 1, n); for (i = array.low; i <= array.high; i++) { printf("%d ", A[i]); }

《算法导论》第11章 散列表 (3)开放寻址

╄→尐↘猪︶ㄣ 提交于 2020-01-22 01:50:52
前一节介绍是最简单的冲突解决方法-链接法。开放寻址与链接法不同,所有元素都放在散列表内。 在这种方法中,散列表可能会被填满。开放寻址不需要指针,只需要计算出要存取的各个槽。 由于不用存储指针而节省的空间可以提供更多的槽。 有三种技术常用来计算开放寻址法中的探查序列:线性探查、二次探查和双重探查。 下面的实现中,三种方法的差别只在计算探查序列的那一行代码。 #include <stdio.h> #include <stdlib.h> #include <string.h> #define SIZE 20 typedef struct _Entry { char *key; char *val; } Entry; // 指针数组 Entry *hashmap[SIZE]; // Same as hashcode when String put to HashMap unsigned hashcode(char *key) { // Ensure >> is logical shift unsigned h = 0; // String.hashcode() do h = 31 * h + *key++; while (*key != '\0'); // HashMap.hash() h ^= (h >> 20) ^ (h >> 12); return h ^ (h >> 7) ^ (h

《算法导论》笔记 第11章 11.2 散列表

家住魔仙堡 提交于 2020-01-22 01:16:40
【笔记】 函数h将关键字域U映射到散列表T[0..m-1]的槽位上。h(k)是关键字k的散列值。 碰撞:两个关键字映射到同一个槽上。 链接法:把散列到同一槽中的所有元素都放在一个链表中。 给定一个能存放n个元素的、具有m个槽位的散列表T,定义T的装载因子α为n/m,即一个链中平均存储的元素数。 对一个用链接技术来解决碰撞的散列表,在简单一致散列的假设下,一次不成功查找的期望时间为θ(1+α)。 在简单一致散列的假设下,对于用链接技术解决碰撞的散列表,平均情况下一次成功的查找需要θ(1+α)时间。 平均来说,查找操作需要常数常量的时间。 【练习】 11.2-1 假设用一个散列函数h,将n个不同的关键字散列到一个长度为m的数组T中。假定采用的是简单一致散列法,那么期望的碰撞数是多少?更准确的,集合{{k,l}:k!=l,且h(k)=h(l)}的期望的基是多少? 不会! 11.2-2 对于一个用链接法解决碰撞的散列表,说明将关键字5、28、19、15、20、33、12、17、10插入到该表中的过程。设该表中有9个槽位,并设散列函数为h(k)=k mod 9。 11.2-3 Marley教授做了这样一个假设,即如果将链接模式改动一下,使得每个链表都能保持已排序顺序,散列的性能就可以有很大的提高。Marley教授的改动对成功查找、不成功查找、插入和删除操作的运行时间有什么影响?

《算法导论》第11章 散列表 (2)散列表

╄→尐↘猪︶ㄣ 提交于 2020-01-22 01:14:37
用散列表来解决直接寻址表的那两个问题。但由此带来的散列值的碰撞问题。 最简单的解决方法是链接法,以及下一节介绍的开放寻址法。 链接法,即把散列到同一槽中的所有元素都放在一个链表中。 链表是无序的,在查找一个元素时需要遍历链表。 对于删除函数,假如参数是要删除的结点,那么如果链表是双向的,删除操作可以O(1)内完成。 在下面的删除函数中,参数是关键字,这样更为方便。 #include <stdio.h> #include <stdlib.h> #include <string.h> #define SIZE 20 // 链表结点的定义 typedef struct _ListNode { struct _ListNode *prev, *next; char *key; char *val; } ListNode; // 定义全局的各个槽链表的指针数组 ListNode *hashmap[SIZE]; // 这里的散列函数与Java中String及HashMap中的散列相同 // 注意为了保证向右逻辑运算(用0而不是符号位补高位) // 要将h声明为无符号的 unsigned hashcode(char *key) { // Ensure >> is logical shift unsigned h = 0; // String.hashcode() do h = 31 * h +

《算法导论》第11章 散列表 (2)散列表

妖精的绣舞 提交于 2020-01-22 01:12:06
用散列表来解决直接寻址表的那两个问题。但由此带来的散列值的碰撞问题。 最简单的解决方法是链接法,以及下一节介绍的开放寻址法。 链接法,即把散列到同一槽中的所有元素都放在一个链表中。 链表是无序的,在查找一个元素时需要遍历链表。 对于删除函数,假如参数是要删除的结点,那么如果链表是双向的,删除操作可以O(1)内完成。 在下面的删除函数中,参数是关键字,这样更为方便。 #include <stdio.h> #include <stdlib.h> #include <string.h> #define SIZE 20 // 链表结点的定义 typedef struct _ListNode { struct _ListNode *prev, *next; char *key; char *val; } ListNode; // 定义全局的各个槽链表的指针数组 ListNode *hashmap[SIZE]; // 这里的散列函数与Java中String及HashMap中的散列相同 // 注意为了保证向右逻辑运算(用0而不是符号位补高位) // 要将h声明为无符号的 unsigned hashcode(char *key) { // Ensure >> is logical shift unsigned h = 0; // String.hashcode() do h = 31 * h +