0.PTA得分截图
1.1查找内容总结
-
静态查找与动态查找
静态查找:数据集合稳定,在查找过程中不需要对所查找数据集合进行修改。
动态查找:数组集合在查找过程中需要进行删除、添加等操作。 -
查找的性能指标ASL
ASL,是查找算法的查找成功时的平均查找长度的缩写,是为确定记录在查找表中的位置,需和给定值进行比较的关键字个数的期望值。
其中ASL的值越大,代表该查找算法的时间性能越差。
- 顺序查找:
//顺序查找的函数实现
int SeqSearch(SeqList R,int n,int K) //R为顺序表,n为顺序表元素个数,K为待查找元素
{
int i= 0;
while(i<n && R[i].key != k)
{
i++;
}
if(i>=n)
{
return -1;
}
else
{
return i;
}
}
时间复杂度为O(n)。
顺序查找比较简单粗暴,就是按照顺序一个个的排除。
2.二分查找(折半查找)
//二分查找代码实现
int Binsearck(SeqList R,int n,KeyType K)
{
int low = 0;
int high = n-1;
int mid = (low + high)/2;
if(R[mid].key == K)
{
return mid;
}
else if(R[mid].key > K)
{
right = mid-1;
}
else
{
left = mid+1;
}
return -1;
}
二分查找的时间复杂度为O(log²n),查找效率比顺序查找要高得多。
- 动态查找(二叉搜索树)
二叉搜索树的结构体定义
typedef struct TNode
{
int Data;
struct TNode * Left;
struct TNode * Right;
int level;
}BSTNode,*BSTree;
二叉搜索树的创建及插入
int Insert(BSTree &T, int X,int i)
{
if (T == NULL)
{
T = new BSTNode;
T->Data = X;
T->Left = T->Right = NULL;
T->level = i;
return 1;
}
else if (X == T->Data)
{
return 0;
}
else if (X < T->Data)
{
return Insert(T->Right, X,i+1);
}
else
{
return Insert(T->Left, X,i+1);
}
}
二叉搜索树的删除
int DeleteBST(BSTree &bt,KeyType k)
{
if (bt==NULL) //空树删除失败
{
return 0;
}
else
{
if (k<bt->key) //递归在左子树中删除为k的节点
{
return DeleteBST(bt->lchild,k);
}
else if (k>bt->key) //递归在右子树中删除为k的节点
{
return DeleteBST(bt->rchild,k);
}
else
{
Delete(bt);//删除*bt节点
return 1;
}
}
}
void Delete(BSTree &p)//从二叉排序树中删除*p节点
{
BSTNode *q;
if (p->rchild==NULL)//*p节点没有右子树的情况
{
q=p; p=p->lchild;delete q;
}
else if (p->lchild==NULL)//*p节点没有左子树
{ q=p; p=p->rchild;delete q;
}
else Delete1(p,p->lchild);//*p节点既有左子树又有右子树的情况
}
void Delete1(BSTNode *p,BSTNode *&r)//被删节点:p,p的左子树节点:r
{
BSTNode *q;
if (r->rchild!=NULL)
{
Delete1(p,r->rchild); //递归找最右下节点
}
else //找到了最右下节点*r
{
p->key=r->key;
q=r; r=r->lchild;delete q; //将*r的关键字值赋给*p
}
}
-
AVL树的定义及4种调整做法
在AVL树中任何节点的两个子树的高度最大差别为1,所以它也被称为高度平衡树。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。
AVL树有四种调整方式:LL调整、RR调整、LR调整、RL调整
1.LL调整:这类调整是因为A结点的左孩子B的左子树上插入结点而导致的不平衡,调整方法一般为将B结点向上升从而替代A结点作为根结点,A结点作为B结点的右孩子,B结点原来的右子树作为A结点现在的左子树。
2.RR调整:这是因为A结点的右孩子B的右子树上插入结点而导致的不平衡,调整方法和LL调整类似,将B结点向上升替代A结点作为根结点,A结点作为B结点的左孩子,B结点原来的左子树作为A结点现在的右子树。
3.LR调整:这个是因为在A结点的左孩子B的右子树上插入结点C造成的不平衡,调整方法是把C结点上升作为根结点,B结点作为C结点的左孩子,A结点作为C结点的右孩子,C结点原来的左子树作为B结点的右子树,C结点原来的右子树作为A结点的左子树。
4.RL调整:是由于A结点的右孩子B的左子树上插入结点C造成的不平衡,调整方法和LR调整基本一样,把C结点上升作为根结点,A结点作为C结点的左孩子,B结点作为C结点的右孩子,C结点原来的左子树作为A结点的右子树,C结点原来的右子树作为B结点的左子树。
-
B-树和B+树
1.b-树:
B-树是一种平衡的多路查找树。
定义:一棵m 阶的B-树,或者为空树,或为满足下列特性的m 叉树:
(1)树中每个结点至多有m 棵子树;
(2)若根结点不是叶子结点,则至少有两棵子树;
(3)除根结点之外的所有非终端结点至少有[m/2] 棵子树;
(4)所有的叶子结点都出现在同一层次上,并且不带信息(可以看作是外部结点或查找失败的结点,实际上这些结点不存在,指向这些结点的指针为空)。
2.B+树:
B+树是B-树的变形树。一棵m 阶的B+树和m 阶的B-
树的差异在于:
⑴有n 棵子树的结点中含有n 个关键码;
⑵所有的叶子结点中包含了全部关键码的信息,及指向含有这些关键码记录的指针,且
叶子结点本身依关键码的大小自小而大的顺序链接。
⑶所有的非终端结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键码。
3.B-树的插入
4.B-树的删除
- 散列查找(哈希查找)
散列技术是在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置。
这里把这种对应关系f称为散列函数,又称为哈希(Hash)函数。
采用散列技术将记录存在在一块连续的存储空间中,这块连续存储空间称为散列表或哈希表。那么,关键字对应的记录存储位置称为散列地址。散列技术既是一种存储方法也是一种查找方法。散列技术的记录之间不存在什么逻辑关系,它只与关键字有关,因此,散列主要是面向查找的存储结构。
1.哈希表
#define NULLKEY -1 //定义空关键字
#define DELKEY -2 //定义被删关键字
typedef int KeyType; //关键字类型
typedef struct
{
KeyType key; //关键字域
int count; //探测次数域
}HashTable; //哈希表单位类型
int InsertHT(HashTable ha,int p,int k,int &n)
{
int adr,i;
adr=k % p;
if(adr==NULLKEY || adr==DELKEY) //地址为空,可插入数据
{
ha[adr].key=k;ha[adr].count=1;
}
else
{
i=1;
while(ha[adr].key!=NULLKEY && ha[adr].key!=DELKEY)
{
adr=(adr+1) % m;
i++;
}//查找插入位置
ha[adr].key=k;ha[adr].count=i; //找到插入位置
}
n++;
}
void CreatHT(HashTable ha,int &n,int m,int p,KeyType keys[],int nl)
{
for(int i=0;i<m;i++
{
ha[i].key=NULLKEY;
ha[i].count=0;
}
n=0;
for(i=0;i<nl;i++)
InsertHT(ha,p,keys[i],int &n);
}
ASL=(1+2+1+1+1+3+3)/7=12/7
2.哈希链
typedef int KeyType; //关键字类型
typedef struct node
{
KeyType key; //关键字域
struct node*next; //下一个结点指针
}NodeType; //单链表结点类型
typedef struct
{
NodeType *firstp; //首结点指针
}HashTable; //哈希表单元类型
void InsertHT(HashTable ha[],int &n,int p,KeyType k) //将关键字k插入到哈希表中
{
int adr;
adr=k%p; //计算哈希函数值
NodeType *q;
q=(NodeType*)malloc(sizeof(NodeType));
q->key=k; //创建一个结点q,存放关键字k
q->next=NULL;
if(ha[adr].firstp==NULL)
ha[adr].firstp=q;
else //若单链表adr不为空
{
q->next=ha[adr].firstp; //采用头插法插入到ha[adr]的单链表中
ha[adr].firstp=q;
}
n++; //哈希表中结点的总个数增1
}
void CreatHT(HashTable ha[],int &n,int m,int p,KeyType keys[],int nl) //由关键字序列keys[0..nl-1]创建哈希表
{
for(int i=0;i<m;i++) //哈希表置初值
ha[i].firstp=NULL;
n=0;
for(i=0;i<nl;i++)
InsertHT(ha,n,p,keys[i]); //插入n个关键字
}
哈希链的ASL计算:
1.2
对查找主要学了线性表查找、树表查找以及哈希表的查找。线性表查找主要是顺序查找、折半查找和分块查找,树表查找主要是二叉排序树、AVL树、B-树和B+树,哈希表查找主要是开发地址法建哈希表查找和拉链法建哈希表查找。无论哪种查找,都要涉及插入、创建、删除、查找和计算成功和不成功的ASL等操作。查找算法可以广泛应用到各种搜索引擎上,也可以用于用户查找个人信息等。
2.PTA题目介绍
2.1二叉搜索树中的最近公共祖先
2.1.1 该题的设计思路
将本题所给的数据建成搜索二叉树,对二叉树进行遍历,看看能不能找到数据,若有数据在树中找不到,则返回对应结果,若能找到,则调用寻找LCA的函数来寻找LCA
2.1.2 该题的设计思路
int LCA( )
{
if(树空)
返回ERROR;
if(FindKey( T, u)&&FindKey( T, v)) //调用FindKey( )函数;
{
if(T->Key大于u和T->Key小于v或T->Key大于v&&T->Key小于u)
返回 T->Key;
else if(T->Key等于u或T->Key等于v)
返回 T->Key;
else if(T->Key大于u或T->Key大于v)
返回 LCA( T->Left, u, v ); //调用LCA()函数,用左子树,u,v做参数;
else
return LCA( T->Right, u, v ); //调用LCA()函数,用右子树,u,v做参数;
}
else
{
返回 ERROR;
}
}
int FindKey( )
{
if(树空)
返回ERROR;
if(T->Key等于u)
{
返回1;
}
if(T->Key大于u)
{
返回FindKey(T->Left, u ) ,在左子树中找;
}
else(T->Key小于u)
{
返回FindKey(T->Right, u ) ,在右子树中找;
}
}
2.1.3代码截图
2.1.4 本题设计的知识点
1.搜索二叉树的建立:将本题所本题设计的知识点给的数据建成搜索二叉树
2.二叉树的遍历:首先在对二叉树进行遍历,看看能不能找到数据,若有数据在树中找不到,则返回对应结果,若能找到,则调用寻找LCA的函数来寻找LCA
2.2 是否完全二叉搜索树
2.2.1 该题的设计思路
首先将数据建成二叉搜索树,然后对树进行遍历,先检测是否为二叉搜索树,若为二叉搜索树,则进一步检测是否为完全二叉树
2.2.2 伪代码
bool LevelOrder(BinTree T)
{
定义队列qu,根结点进队qu.push(BST);
while (队不为空时)
取队首BT = qu.front();
输出数据,然后出队qu.pop();
若左孩子不为空,左孩子进队qu.push(BT->Left);
若右孩子不为空,右孩子进队qu.push(BT->Right);
end while
定义赋值数组judge[100]
定义队列q,根结点入队q.push(BST);
存入数据judge[i++] = BST->Data;
while (队不空时)
取队首bt = q.front(); 出队q.pop();
若左孩子为空,右孩子不为空
存入数据judge[i++] = NULLNODE;
不为空的孩子进队q.push(bt->Right);
存入数据judge[i++] = bt->Right->Data;
若左孩子不为空,右孩子为空
不为空的孩子进队q.push(bt->Left);
存入数据judge[i++] = bt->Left->Data;
存入数据judge[i++] = NULLNODE;
若左右孩子都为空
存入两次数据judge[i++] = NULLNODE;
否则
若左孩子不为空,进队q.push(bt->Left);
存入数据judge[i++] = bt->Left->Data;
若右孩子不为空,进队q.push(bt->Right);
存入数据judge[i++] = bt->Right->Data;
end while
遍历数组judge,根据层次遍历的特点,即一层一层的依次遍历,输出是否是完全二叉搜索树
}
2.2.3代码截图
2.2.4 本题设计的知识点
1.建立搜索二叉树
2.对二叉搜索树进行遍历
3.使用队列辅助遍历二叉树
2.3 QQ帐户的申请与登陆
2.3.1 该题的设计思路
2.3.2伪代码
定义n为操作次数;
定义order存放命令字符;
定义name为账号;
定义password为密码;
定义map容器QQ;
输入n
while n-- do
输入order;
if order='N' then
输入账号密码;
// 用是否存在键值对判断是否已经有该账号
if 该账号已经存在 then
输出ERROR:Exist;
end if
if 该账号不存在 then
创建name-password键值对;
输出NEW:OK;
end if
end if
if order='L' then
输入账号密码;
if 该账号已经存在 then
if 键值对正确匹配 then
输出Login:OK;
end if
if 键值对匹配不成功 then
输出ERROR:Wrong PW;
end if
end if
if 该账号不存在 then
输出ERROR:Not Exist
end if
end if
end while
2.3.3代码截图
2.3.4本题涉及知识点
1.map容器的使用