1.何谓程序设计?
程序 = 算法 + 数据结构
2.数据结构的定义
是相互之间存在一种或多种特定关系的数据元素的集合
3.数据、数据元素、数据对象的概念
数据(data):对客观事物的符号表示,含义很广,指数、图像、声音、文本等一切计算机可以处理的事物。
数据元素(data element):组成数据的基本单位。
数据对象(data object):性质相同的数据元素的集合。
4.四种基本的数据结构类型
集合结构、线性结构、树型结构、图形结构
5.两种存储结构(计算机中的实现方式)
顺序存储结构、链式存储结构
6.理解顺序存储与链式存储的方式、特点、优缺点、适用情况
7.数据类型、抽象数据类型
数据类型(Data Type):刻画程序对象的一种方式,包括值的集合与在这组值上的操作(一个成功的数据结构,就要设计成一种数据类型)
抽象数据类型(Abstract Data Type,ADT):是与数据类型相似的概念
ADT name
{ 数据对象 Data
数据关系 Relation
数据操作 Operation }
8.抽象数据类型的意义
ADT着重数据结构的操作接口,不关心具体的实现,主要是面向用户。ADT是数据结构设计所追求的目标。
9.何谓算法
算法是解决特定问题求解步骤的描述
10.算法特征
(1)有穷性:一个算法必须在执行有限步之后结束,每一步都在有穷时间内完成
(2)确定性:算法中每一步都有确切的含义,不会产生二义性
(3)可行性:算法的每一步操作都可以通过已有的可行的操作来完成
(4)输入:算法有零个或若干个数据输入
(5)输出:算法有一个或若干个数据输出
11.算法设计的要求
(1)正确性:能够解决问题
a.程序不含语法错误
b.对于几组正常的输入数据,能输出满足要求的结果
c.对于正常和异常数据,能给出满足要求的结果
d.对一切合法数据,都能够产生满足要求的结果
(2)可读性
(3)健壮性(要有异常处理机制)
(4)效率和低储存容量需求
12.算法好坏的衡量标准
(1)事后统计
(2)事前估计
a.时间复杂度估计
b.空间复杂度估计
13.时间复杂度与空间复杂度
时间复杂度:T(n) = O( f(n) ),n为问题的规模。撇开具体的程序运行的环境,可以得到一个大致的数量阶数。一般顺序:O(1) < O(log n) < O(n) < O(nk) < O(kn)
将算法中基本操作的执行次数作为算法时间复杂度的度量。多数情况下都是取最深层循环的语句所描述的操作作为基本操作。递归算法的时间复杂度只需要记忆那几个常用递归算法的即可。
如何算:
(1)确定算法中的基本操作以及数据量的大小(由算法所涉及的局部来看)。
(2)基本操作执行情况计算出规模n的函数f(n),并确定时间复杂度为T(n) = O(f(n))中增长最快的项/此项的系数。
空间复杂度:类似的算法所需储存空间 S(n) = O( f(n) ),n为问题的规模
1.何谓线性结构
在数据元素的非空有限集中,存在唯一的一个称为“第一个”的数据元素,存在唯一的一个称为“最后一个”的数据元素,除第一个之外,其他每个数据元素只有一个前驱,除最后一个之外,其他每个数据元素都只有一个后继。
2.线性结构主要有哪几种
线性表、栈、队列、串
3.线性表ADT,尤其是它的几个主要操作(插入元素、删除元素)
ADT List
{ 数据对象 Data={ ai , i=1,2,…n }
数据关系 (略)
数据操作:
InitList 初始化线性表
DestroyList 销毁线性表
ClearList 清空线性表内的元素
ListEmpty 判断是否为空表
ListLength 取得表长度
GetElement 取得某个位置的元素
LocateElement 定位某个元素的位置
PriorElement 返回某个元素的前驱
NextElement 返回某个元素的后继
InsertElement 在某位置插入一个元素
DeleteElement 在某位置删除一个元素
ListTraverse 遍历整个线性表
}
4.线性表的两种实现方式
顺序表、链表
5.顺序表的C语言实现
#ifndef ARRAY_LIST_H
#define ARRAY_LIST_H
#include "type.h"
array_list.c
#include
#include
#include "array_list.h"
static int get_array_index_from_logic_index(int logic_index) // 从逻辑下标得到数组下标
{ return logic_index - 1;}
static int get_logic_index_from_array_index(int array_index) // 从数组下标得到逻辑下标
{return array_index + 1;}
static int is_full(const struct array_list * list) // 是否满了
{return list->capacity == list->last;}
struct array_list * array_list_init(int capacity) // 初始化
{struct array_list * tmp = (struct array_list *) malloc(sizeof (struct array_list));
if (tmp == NULL)
{perror("顺序表创建失败\n"); return NULL;}
type * data = (type *) malloc(sizeof (type) * capacity);
if (data == NULL)
{perror("顺序表创建失败\n"); return NULL; }
tmp->capacity = capacity;
tmp->data = data;
tmp->last = 0;
return tmp;}
void array_list_destroy(struct array_list * list) // 销毁
{free(list->data); free(list);}
int array_list_is_empty(const struct array_list * list) // 判空
{return list->last == 0; }
void array_list_clear(struct array_list * list) // 清空
{list->last = 0; }
int array_list_length(const struct array_list * list) // 求长度
{return list->last;}
type array_list_get(const struct array_list * list, int index) // 取得index位置的元素
{return list->data[get_array_index_from_logic_index(index)];}
void array_list_insert(struct array_list * list, int index, type data) // 在index位置添加元素
{if (is_full(list)) {perror("表是满的"); return;}
if (index < 1 || index > array_list_length(list) + 1) {perror("下表超出范围"); return; }
int array_index = get_array_index_from_logic_index(index);
int i = list->last - 1;
for (; i >= array_index; i--) {list->data[i + 1] = list->data[i];}
list->data[i + 1] = data; list->last++;}
void array_list_insert_last(struct array_list * list, type data) // 在最后位置添加元素
{array_list_insert(list, array_list_length(list) + 1, data);}
type array_list_remove(struct array_list * list, int index) // 删除index位置的元素
{if (index < 1 || index > array_list_length(list)) {perror("index out of range"); return null_value();}
int array_index = get_array_index_from_logic_index(index);
type data = list->data[array_index];
for (int i = array_index + 1; i < list->last; i++) {list->data[i - 1] = list->data[i];}
list->last--;
return data;}
void array_list_traverse(const struct array_list * list, void (*access_function)(type data)) //遍历
{for (int i = 0; i < list->last; i++) {access_function(list->data[i]);}}
6.链表的C语言实现
单链表、循环链表、双向链表中结点的操作,比如插入、删除、查找、定位等等
7.实现的代码中,如何方便地更换数据元素的类型,以便代码复用
定义泛型泛型的定义主要有以下两种:
1.在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)
2.在程序编码中一些包含参数的类。其参数可以代表类或对象等等。(人们大多把这称作模板)不论使用哪个定义,泛型的参数在真正使用泛型时都必须作出指明。
8.顺序表和链表的优缺点、适用场景
顺序实现的优点:
1.利用计算机存储的特点,实现简单,无需额外存储元素间的逻辑关系
2.随机存储
顺序实现的缺点:
1.插入和删除操作常伴随大量的数据元素的移动
2.不利于数据规模的经常增大
a.对于预先分配空间的,无法增加空间
b.对于有自动扩充功能的,扩充之前需大 量复制已有数据元素
因此,顺序存储适合表示那些预先知道数据元素规模,经常的操作是取得某个位置的元素,但不经常进行插入删除操作的那些问题
链式实现的优点:
1.无需事先分配大块连续的内存,可根据插入删除的需要进行内存的分配和回收
2.插入和删除操作比较简单,无需进行数据的移动
链式实现的缺点
1.需要额外存储指针域
2.实现时相对比较复杂
适用场景:需要插入或删除操作较多的地方
1.栈的概念,ADT
栈(stack)是限定仅能在表尾进行插入和删除操作的线性表。
(因此,对栈来说,表尾端有 其特殊含义,称为栈顶,相应地,表头端称为栈底。不含元素的空表称为空栈)
ADT Stack{ 数据元素:
略
基本操作:
InitStack
DestroyStack
ClearStack
StackEmpty
GetTop
Push
Pop }
2.栈的三个基本操作
Push、Pop、GetTop
void push(struct stack * st, type data) // 进栈
{if (st->top == st->capacity)
{perror("stack is full, cannot push");return; }
st->data[st->top++] = data; }
type pop(struct stack * st) // 出栈
{if (stack_is_empty(st)) {perror("stack is empty, cannot pop"); return null_value();}
return st->data[--st->top]; }
type get_top(const struct stack * st) // 查看栈顶元素
{if (stack_is_empty(st)) {perror("stack is empty, got null"); return null_value();}
return st->data[st->top - 1]; }
3.栈的特点
先进后出(FILO)
4.判断能否根据某种操作顺序,得到某一个元素序列
具体分析
5.栈的一些应用:括号匹配、函数调用跟踪、递归跟踪、四则运算
括号匹配思路:
1.依次读入每一个符号。
2.如果该符号是左括号(大、中、小),则入栈;
3.如果该符号是右括号,则出栈一个符号,如果与该左括号匹配,则继续;如果栈空或者取出的左括号不匹配,则判定表达式括号不匹配。
4.读完表达式,如果栈空,判定表达式括号匹配;如果栈非空,则判定表达式括号不匹配
#include
#include
#include
#include
#define TURE 1
#define ERROR 0
#define OK TURE
using namespace std;
const int max = 10000;
char ch[max];
typedef int Status;
struct Stack //创建栈
{ char data[max];
int top; };
Status search(char ch[], int n)
{ Stack s;
s.top = -1;
for (int i = 0; i < n; i++)
{if (ch[i] == '(' || ch[i] == '[')
s.data[++s.top] = ch[i];
if (ch[i] == ')')//左括号
{if (s.top == -1) return ERROR;
else if (s.data[s.top] == '(')
s.top--;
else return ERROR; }
if (ch[i] == ']') //右括号
{if (s.top == -1) return ERROR;
else if (s.data[s.top] == '[')//匹配s.top--;
else//匹配失败return ERROR;
//处理完所有的括号查看栈是否为空
while (i == n)
{if (s.top == -1) {return OK; cout << "s.top is cleaned";}
else{return ERROR; cout << "s.top is uncleaned";}}
}}}
int main()
{ int n;//检测个数
cout << "要检测的个数:";
cin >> n;//输入n个数
int i = 1;//设置i=1
while (n--)//当n自减的时候
{cout << "第" << i << "个括号序列:"; cin >> ch;//输入括号
int len = strlen(ch);//把ch的字符长度赋值给Len
if (search(ch, len)) cout << "匹配成功" << endl;
else cout << "匹配失败" << endl; i++;}
system("pause");
return 0; }
四则运算思路:
1.设计算法将中序表达式转换成后序表达式
2.设计算法进行后序表达式的计算
跟踪思路:调用一次则入栈一次,
6.栈的顺序实现方式(top指针的变化方式)
#include
#include
#include "stack.h"
struct stack * stack_init(int capacity)
{struct stack * st = (struct stack *) malloc(sizeof (struct stack));
if (!st) {perror("stack init failed"); return NULL; }
st->data = (type *) malloc(sizeof (type) * capacity);
if (!st->data) {perror("stack init failed");return NULL; }
st->capacity = capacity;
st->top = 0; return st;}
7.队列的特点:FIFO
8.队列的头尾与两个基本操作
队首(front),队尾(rear),入队(en),出队(de)
void enq(struct queue * q, type data) // 入队列
{if (is_full(q)) {perror("queue is full, cannot enq"); return; }
if (queue_is_empty(q)) { q->data[q->front] = data; }
q->data[q->rear] = data; q->rear = move_to_next(q->rear, q->length); }
type get_front(const struct queue * q) // 取得队列首元素
{if (queue_is_empty(q)) { perror("queue is empty"); return null_value(); }
return q->data[q->front];}
type deq(struct queue * q) // 出队列
{type o = get_front(q);
q->front = move_to_next(q->front, q->length); return o; }
9.队列的顺序实现方式
front和rear的变化方式,何时表示队列满,何时表示队列空
int queue_is_empty(const struct queue * q) //判空
{return q->front == q->rear;}
static int is_full(const struct queue * q) // 判断队列满
{return move_to_next(q->rear, q->length) == q->front; }
一般规定:
当front与rear重合时,表示队列为空
当rear再移动一位就将与front重合时,表示队列满(即以牺牲一个存储单元的代价,表示队列满这个状态)
四.串
1.串的概念(串、子串)
串是由零个或多个字符组成的有限序列,如 str = ‘abcdefg’,str:串名,单引号中的部分,表示串值,串值中的字符个数,为串长度,长度为0的表示空串
串中的任意连续的字符子序列,称为该串的子串
当两个串的值完全相同时,称这两个串相等
2.串的顺序实现方式,主要操作
ADT String{ 数据对象:略
数据关系:略
基本操作:StrInit:串初始化
StrCopy:串拷贝
StrCompare:串比较
StrLength:求串长度
StrConcat:串连接
SubString:求串的某个子串
StrIndex:返回子串在串中的位置 }
3.模式匹配问题
模式匹配:在一个串中,对某子串的位置定位
假设主串长度为n,要匹配的子串长度为m,则常规匹配算法最坏情况下需要O(n*m)时间完成
KMP算法:该算法由Knuth,Morris,Pratt同时发现,因此命名为KMP算法,它是一种巧妙的算法,能在O(n+m)时间内完成串的模式匹配
1.数组(Array) 是用来描述一组具有连续的线性关系的相同事物的概念
2.ADT Array{ 数据对象:(略)
数据关系:(略)
基本操作:
InitArray 数组初始化
DestroyArray 数组销毁
GetValue 取得某个元素
SetValue 给某个元素赋值 }
数组可以是多维的,不同的维数用来表示不同的实际事物
比如:可以用一维数组表示向量,二维数组表示矩阵,等等
3.多维数组的定义方法
通过其前一维数组来定义
比如,int a[3][4],可以认为首先是一个具有3个元素(长度为3)的数组,然后每个元素是一个长度为4的数组
即:n维数组可以这样认为:它是一个1维数组,其每个元素是一个n-1维数组
4.数组的顺序实现
数组的基本元素在开始初始化后,一般其位置不会发生变化,数组的基本操作是取值和赋值,很少进行插入和删除操作,内存的使用特性符合数组的这种特点。故数组一般都采用顺序实现
5.两种保存数组的方法
1.按行存储2.按列存储
6.多维数组中基本元素的定位:
每一个多维数组中的基本元素,都会有一个坐标,只要知道数组第一个元素的位置(基地址),某一个元素的坐标,就能够计算出该元素的位置
7. 二维数组元素坐标
(以行存储为例):LOC(i, j) = LOC(0, 0) + (d2 * i + j)*L
其中d2为第二维的数组长度,L为每个基本元素所占的空间。由于有这样的计算公式,因此顺序实现的数组具有随机存取的特点。
8.矩阵的压缩存储
(1)存一半:对称矩阵(ai,j = aj,i),上、下三角矩阵(ai,j = 0, i>j 或反过来)
(2)存坐标:稀疏矩阵(矩阵中大部分元素为0)
9.广义表
广义表是一种特殊的线性表,他的每一个元素可以是一个基本元素,也可以是另外一个不定长的线性表,这种性质决定了广义表不太可能用顺序存储来实现,因此一般用链表来实现。
1.树的定义(递归方式)及表示方法
定义:树是n(≥0)个结点的有限集,树为:
空树 或者 非空树:有且仅有一个称为“根”(root)的结点,它有若干个子树
树的几种表示方法:
双亲表示法:尽管树的每个结点都有若干个孩子,但是每个孩子只有一个双亲。(该方法在求某结点的孩子时,需要花费较多的时间)
孩子表示法:
孩子兄弟表示法:对每个结点,设定两个指针,分别记录该结点的第一个孩子和下一个兄弟
2.树的主要术语(结点、叶子结点、孩子、兄弟、双亲、结点的度、树的度、层次、深度、有序树、森林)
结点(node):包含一个数据元素及指向其子树的分支
结点的度(Degree):一个结点拥有的子树的个数
叶子结点(Leaf):度为0的结点
树的度:树内各个结点的度的最大值
孩子(Child):结点的子树
双亲(Parent):结点的上层结点
兄弟(Sibling):具有同一个双亲的结点
祖先:从根到该结点所经过的分支上的所有结点
子孙:某结点的子树的所有结点
层次(Level):根为第一次,某个结点若为l层,则其孩子为l+1层
深度(Depth):树中结点的最大层次
有序树:各子树从左往右有顺序,反之称为无序树
森林(Forest):若干棵互不相交的树的集合
3.二叉树(Binary Tree)的概念(递归方式)
定义1:每个结点至多只有两棵子树的树(即二叉树中不存在度大于2的结点),并且两棵子树有左右之分,分别称为左子树和右子树
定义2:二叉树或为一棵空树,或是由一个根结点加上一棵左子树和一棵右子树,左子树和右子树也为二叉树。
4.二叉树的几个性质
1.在二叉树的第i层上至多有2i-1(i≥1)个结点
2.深度为k的二叉树至多有2k-1(k≥1)个结点
3.任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1
4.具有n个结点的完全二叉树的深度为(log2n)+1
5. 如果对一棵有n个结点的完全二叉树(其深度为(log2n)+1)的结点按层序编号(从第1层到第(log2n)+1)层,每层从左到右),则对任一结点i(1
(1).如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则双亲是结点(i/2)
(2).如果2i>n,则结点i无做孩子(结点i为叶子结点);否则其左孩子 是结点2i+1
(3).如果2i+1>n,则结点i无右孩子;否则其右孩子是结点2i+1
5.二叉树的链式实现,几个重要操作的实现(插入结点、删除结点)
#include "bintree.h"
#include "queue.h"
#define max(a,b) ((a)>(b)?(a):(b))
struct bintree * bintree_init()// 初始化
{struct bintree * p = (struct bintree *) malloc(sizeof (struct bintree));
if (!p) {perror("bintree init failed"); return NULL; }
p->root = NULL; return p; }
static int is_leaf(const struct tree_node * node)//创建叶子节点
{return node->left == NULL && node->right == NULL;}
struct tree_node * insert_root(struct bintree * tree, type data) // 在空树上插入第一个结点
{if (!bintree_is_empty(tree)) { perror("tree is not empty"); return NULL; }
struct tree_node * p = create_node(data); tree->root = p; return p; }
static int has_left_child(const struct tree_node * node)
{return node->left != NULL; }
static int has_right_child(const struct tree_node * node)
{return node->right != NULL; }
struct tree_node * insert_left(struct tree_node * node, type data) // 插入左孩子
{if (has_left_child(node)) // 判断是否有左孩子
{ perror("node already has left child"); return NULL; }
struct tree_node * p = create_node(data);
node->left = p;
p->parent = node;
return p; }
struct tree_node * insert_right(struct tree_node * node, type data) // 插入右孩子
{if (has_right_child(node)) // 判断是否有右孩子
{ perror("node already has right child"); return NULL; }
struct tree_node * p = create_node(data);
node->right = p;
p->parent = node;
return p; }
struct tree_node * remove_root(struct bintree * tree) // 删除根结点
{ if (!is_leaf(tree->root)) { perror("root is not leaf"); return NULL; }
struct tree_node * p = tree->root;
tree->root = NULL;
return p; }
static int is_left_child(const struct tree_node * child, const struct tree_node * parent)
{return child->parent == parent && parent->left == child; }
static int is_right_child(const struct tree_node * child, const struct tree_node * parent)
{return child->parent == parent && parent->right == child; }
struct tree_node * bintree_remove(struct tree_node * node) // 删除结点
{ if (!is_leaf(node)) {perror("node is not leaf"); return NULL; }
struct tree_node * parent = node->parent;
if (parent)
{ if (is_left_child(node, parent)) {parent->left = NULL; }
else if (is_right_child(node, parent)) {parent->right = NULL;}
node->parent = NULL; }
return node; }
6.四种二叉树遍历方法(前序、中序、后序、层次)
static void exec_pre_recurse(const struct tree_node * node, void (*access_function)(type data))
{ if (node == NULL) { return; }
access_function(node->data);
exec_pre_recurse(node->left, access_function);
exec_pre_recurse(node->right, access_function); }
void pre_order_traverse(const struct bintree * tree, void (*access_function)(type data)) // 前序遍历
{ exec_pre_recurse(tree->root, access_function); }
static void exec_in_recurse(const struct tree_node * node, void (*access_function)(type data))
{ if (node == NULL) { return; }
exec_in_recurse(node->left, access_function);
access_function(node->data);
exec_in_recurse(node->right, access_function); }
void in_order_traverse(const struct bintree * tree, void (*access_function)(type data)) // 中序遍历
{exec_in_recurse(tree->root, access_function); }
static void exec_post_recurse(const struct tree_node * node, void (*access_function)(type data)) { if (node == NULL) { return; }
exec_post_recurse(node->left, access_function);
exec_post_recurse(node->right, access_function);
access_function(node->data); }
// 后序遍历
void post_order_traverse(const struct bintree * tree, void (*access_function)(type data)) {
exec_post_recurse(tree->root, access_function);
}
struct bintree_queue_node // 层次遍历用到的队列,简单一点利用链表实现
{struct tree_node * data;
struct bintree_queue_node * next; };
struct bintree_queue
{struct bintree_queue_node * front;
struct bintree_queue_node * rear; };
static struct bintree_queue * bintree_queue_init()
{ struct bintree_queue * q = (struct bintree_queue *) malloc(sizeof (struct bintree_queue));
if (!q) { perror("bintree queue init failed"); return NULL; }
q->front = NULL;
q->rear = NULL;
return q; }
static int bintree_queue_is_empty(const struct bintree_queue * q) // 判空
{ return q->front == NULL && q->rear == NULL; }
static void bintree_enq(struct bintree_queue * q, struct tree_node * data) // 入队列
{struct bintree_queue_node * p = (struct bintree_queue_node *) malloc(sizeof (struct bintree_queue_node));
if (!p) { perror("bintree queue init failed"); return; }
p->data = data;
p->next = NULL;
if (bintree_queue_is_empty(q))
{ q->front = p;
q->rear = p; }
else { p->next = q->rear->next;
q->rear->next = p;
q->rear = p; } }
static struct tree_node * bintree_deq(struct bintree_queue * q) // 出队列
{ if (bintree_queue_is_empty(q))
{ perror("bintree queue is empty"); return NULL; }
struct bintree_queue_node * o = q->front;
q->front = o->next;
if (q->front == NULL) { q->rear = NULL; }
return o->data; }
void level_order_traverse(const struct bintree * tree, void (*access_function)(type data)) // 层次遍历
{ struct bintree_queue * q = bintree_queue_init();
bintree_enq(q, tree->root);
while (!bintree_queue_is_empty(q))
{ struct tree_node * node = bintree_deq(q);
access_function(node->data);
if (has_left_child(node)) { bintree_enq(q, node->left); }
if (has_right_child(node)) { bintree_enq(q, node->right); } } }
7.给定前序和中序、中序和后序的遍历序列,画出树的形状
前序:访问根结点→先序遍历左子树→先序遍历右子树
中序:中序遍历左子树→访问根结点→中序遍历右子树
后序:后序遍历左子树→后序遍历右子树→访问根结点
(前中后:根的位置)
层次:依次访问深度为1、2、…的结点,从上至下,从左至右
8.满二叉树与完全二叉树
满二叉树:深度为k,且有2k-1个结点
完全二叉树:深度为k,有n个结点的二叉树,与深度为k的满二叉树中编号从1至n的结点一一对应(叶子结点只存在于最大的两个层次上面)
9.如何把任意一棵树、森林转化为一棵等价的二叉树
森林是含有若干棵独立的树的一个集合,把森林中某棵树的根结点看成是前一棵树的根结点的兄弟,则可将该森林转换成一棵二叉树
10.Huffman树、如何构造
路径:树中一个结点到另一个结点间的分支
路径长度:路径中的分支数目
树的路径长度:从树根到每一个叶子结点的路径长度之和
树的带权路径长度:树中所有叶子结点的带权路径长度之和
WPL = ∑wk·lk , k = 1, …, n
Huffman树(最优二叉树):设有n个权值{w1,w2,…wn},某二叉树共有n个叶子结点,每个结点的权值分别为wi,则带权路径长度最小的二叉树称为Huffman树(最优二叉树)。
Huffman树的应用:二叉树由于只有两个分支,可以用来代表判定语句中的true和false
树的应用之一就是条件分支的判定,对于有些问题需要经常进行条件判断,则若采用合适的树(如Huffman树,则可以减少在树中走的路径长度)
构造Huffman树的算法:
1.已知n个结点与各自的权值
2.以这n个结点分别作为一个只有一个根结点的树,组成集合F
3.在集合F中,寻找根结点权值最小的两棵树,构造一个新的根结点,把这两棵树分别作为该新结点的左右孩子,新的根结点的权值为两子树根结点的权值之和
4.在集合F中加入新的树,并删除原来的两棵树
5.重复第二步和第三部,直到最后只剩下一棵树,该树即为Huffman树
11.Huffman编码构造方法
前缀编码:任一字符的编码都不是另一个字符编码的前缀,如给A、B、C、D分别编码为0,00,1,01,则字符串ABCAD的编码字符串为0001001,但反过来译码时,就会产生歧义,因为该编码方式非前缀编码
设计一棵树,把各个字符看做树的叶子结点,各个字符出现的频率看做结点的权值,每个结点到其左孩子的路径为0,到右孩子的路径为1,则前缀编码的构造就是一个构造Huffman树的过程,而译码就是遍历整个Huffman树的叶子结点的过程
12.树的非递归算法
push(root)
while(栈非空)
node←pop
PreOrder: if(not_visited(node.right)) push(node.right)
if(not_visited(node.left))
push(node.left)
if(should_visit(node)) visit(node)
else push(node)
InOrder: if(not_visited(node.right)) push(node.right)
if(should_visit(node)) visit(node)
else push(node)
if(not_visited(node.left)) push(node.left)
PostOrder: if(should_visit(node)) visit(node)
else push(node)
if(not_visited(node.right)) push(node.right)
if(not_visited(node.left)) push(node.left)
should_visit(node)
PreOrder: return TRUE
InOrder: 左孩子为空或左孩子已被访问过 return TRUE,否则 return FALSE
PostOrder: 左孩子为空或左孩子已被访问过 &&右孩子为空或右孩子已被访问过
return TRUE,否则 return FALSE
1.查找表、关键字、主关键字、次关键字、查找、平均查找长度(ASL)
查找表(Search Table):需要被查找的数据所在的集合,通常是同一类型的数据元素(记录)构成的集合
关键字(Key):数据元素中的某个数据项的值
主关键字(Primary Key):该关键字可以唯一地标识一个记录
次关键字(Secondary Key):不是可以唯一标识一个数据元素的关键字
查找(Searching):根据给定的值,在查找表中确定一个其关键字等于给定值的数据元素(记录)。若存在这样的记录,则称为查找成功,反之称为不成功
平均查找长度(Average Search Length):为确定记录在查找表中的位置,需和给定值进行比较的关键字个数的期望值。
2.静态查找表与动态查找表的区别
静态查找表:只做查找操作,查询某个特定的数据元素是否在表中,检索某个特定的数据元素和各种属性
动态查找表:在查找过程中同时还插入或删除元素
3.折半查找算法
折半查找(Binary Search),也叫“二分法查找”。给定一个有序的数据集,依次比较区间中间位置元素的关键字与给定值,若相等则查找成功,若不等,则把范围缩小一半,继续以上步骤,直到找到,或者区间长度小于0(未找到)。
算法描述(从a[N]中查找是否有元素b)
1.设立三个指标low、high、mid,初值分别为low=0,high=N-1
2.如果low>high,则表示未找到b