japari

这个作业属于哪个班级 数据结构--网络2011/2012
这个作业的地址 DS博客作业05--查找
这个作业的目标 学习图结构设计及相关算法
姓名 杨振鹏

0.PTA得分截图

1.本周学习总结

1.1 查找的性能指标

ASL(Average Search Length)

ASL,是查找算法的查找成功时的平均查找长度的缩写,是为确定记录在查找表中的位置,需和给定值进行比较的关键字个数的期望值。

ASL:关键字的平均比较次数,也叫做平均搜索长度。
公式:ASL=∑(i=1->n)p(i)*c(i);其实就是ASL=n个关键字找到时的比较次数之和/n

1.2 静态查找

  • 顺序查找
    定义:按照序列原有顺序对数组进行遍历比较查询的基本查找算法
    类似与数组查找,一个一个按顺序的找
int SeqSearch(Seqlist R,int n,KeyType k)
{
	int i=0;
	while(i<n&&R[i].key!=k)i++;//从表头开始找 
		if(i>=n)return 0;//没找到返回0 
		else return i+1;//找到返回序号i+1 
 } 

  • 二分查找
    二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列.
int binarySearch(SeqList R,int n,KeyType k){
    int low = 0, high = n-1, mid;
    while(low <= high){
        mid = (low + high) / 2;
        if(R[mid.key] == k)return mid;//找到就返回下标 
		else if(R[mid].key > k)high = mid - 1; 
		elss low = mid + 1;
    }
    return 0;//找不到返回0 
}

//递归法
int  BinarySearch(SeqList R,int low,int hight,KeyType k)
 {
 	int mid;
 	if(low<=high){//查找的区间存在一个以上的元素 
 		mid=(low+high)/2;//找中间位置 
 		if(R[mid].key==k)return mid+1;//查找成功,返回序号mid+1 
 		
 		if(R[mid].key>k)BinarySearch(R,low,miid-1,k);//在[low…mid-1]区间递归寻找 
 		else BinarySearch(R,mid+1,high,k);	//在[mid+1…high]区间递归寻找 	
	 }
	 else return 0;
 }

ASL成功=每一层节点数层次数的总和/总结点数
ASL不成功=其他不存在于树中的每一层节点数(层次数-1)/总结点数

1.3 二叉搜索树

1.3.1 如何构建二叉搜索树(操作)

eg.12 7 17 11 16 2 13 9 21 4








1.3.2 如何构建二叉搜索树(代码)

  • 二叉树搜索树结构体定义
typedef char* InfoType;	//其他数据类型
typedef struct node
{
	KeyType key;			//关键字域
	InfoType data;			//其他数据域
        struct node *lchild, *rchild;/*左右孩子*/
}BSTNode, * BSTree;
  • 二叉搜索树的构建
void CreateBST(BinTree& BST, int n)/*建树函数*/
{
	int x;
	BST = NULL;//树初始化为空
	for (int i = 0; i < n; i++)
	{
		cin >> x;
		Insert(BST, x);
	}
}
  • 二叉搜索树的插入
void Insert(BinTree& BST, int X)
{
	if (BST == NULL)/*树空*/
	{
		BST = new TNode;/*申请*/
		BST->Data = X;
		BST->Left = NULL;
		BST->Right = NULL;
	}
	else if (X > BST->Data)/*大于插左边*/
	{
		Insert(BST->Left, X);
	}
	else if (X < BST->Data)/*小于插右边*/
	{
		Insert(BST->Right, X);
	}
}
  • 二叉搜索树的查找
BinTree Find(BinTree BST, ElementType X)
{
    if (BST == NULL)
    {
        return NULL;/*为空返回空*/
    }
    if (X > BST->Data)
    {
        Find(BST->Right, X);/*大于在右*/
    }
    else if (X < BST->Data)
    {
        Find(BST->Left, X);/*小于在左*/
    }
    else
    {
        return BST;/*找到则返回*/
    }
}
  • 二叉搜索树的删除
BinTree Delete(BinTree BST, ElementType X)
{
    BinTree storage;

    if (BST == NULL)/*为空*/
    {
       printf( "Not Found
");
       return BST;/*输出并返回根节点*/
    }
    if (X > BST->Data)
    {
        BST->Right = Delete(BST->Right, X);
    }
    else if (X < BST->Data)
    {
        BST->Left = Delete(BST->Left, X);
    }
    else
    {
        if (BST->Left && BST->Right)/*左右子树都存在*/
        {
            storage = FindMin(BST->Right);/*找右子树的最小点*/
            BST->Data = storage->Data;
            BST->Right = Delete(BST->Right, BST->Data);
        }
        else 
        {
            storage = BST;
            if(BST->Left==NULL)/*左为空*/
            {
                BST = BST->Right;
            }
            else/*右为空*/
            {
                BST = BST->Left;
            }
            free(storage);/*删除*/
        }
    }
    return BST;
}

时间复杂度:最好:O(logn),最差:O(n)
用递归实现插入、删除的优势

保留父子关系,便于删除和插入顺利找到父亲和孩子
代码简短,便于书写,树的递归更能掌握

1.4 AVL树

由于二叉搜索树存在数据分配不平衡的现象,会导致查找时的时间复杂度提高,所以诞生了AVL解决此类问题。
AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为1,所以它也被称为高度平衡树。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树

  • 结构体定义
typedef struct node
{
	KeyType key;			//关键字域
        int bf;                        //平衡因子
	InfoType data;			//其他数据域
        struct node *lchild, *rchild;/*左右孩子*/
}BSTNode, * BSTree;
  • 4种调整




    AVL树的高度和树的总节点数n的关系:h≈log2N(h)+1

map--STL
基本概念
map是STL中非常有用的一个容器
和函数类似,map是通过键值key查询对应value
对于数组而言,其下标必须非负整数的数值,且数值不能过大,而map就不同了,map的键值可以是任意类型,包括最常用的string类型,map的value也同样可以为任意类型
用途
map的内部是红黑树实现,其具有自动排序的功能
通过键值排序,以递增的方式排序
若键值为整形,以数值递增排序
若键值为string,以字符串升序排序

  • 用法
    begin() 返回指向 map 头部的迭代器
    clear() 删除所有元素
    count() 返回指定元素出现的次数
    empty() 如果 map 为空则返回 true
    end() 返回指向 map 末尾的迭代器
    erase() 删除一个元素
    find() 查找一个元素
    insert() 插入元素
    key_comp() 返回比较元素 key 的函数
    lower_bound() 返回键值>=给定元素的第一个位置
    max_size() 返回可以容纳的最大元素个数
    rbegin() 返回一个指向 map 尾部的逆向迭代器
    rend() 返回一个指向 map 头部的逆向迭代器
    size() 返回 map 中元素的个数
    swap() 交换两个 map
    upper_bound() 返回键值>给定元素的第一个位置
    value_comp() 返回比较元素 value 的函数

1.5 B-树和B+树

B-树和AVL树区别
AVL树结点仅能存放一个关键字,树的敢赌较高,而B-树的一个结点可以存放多个关键字,降低了树的高度,可以解决大数据下的查找
B-树定义:一棵m阶B-树或者是一棵空树,或者满足一下要求的树就是B-树

每个结点之多m个孩子节点(至多有m-1个关键字);
除根节点外,其他结点至少有⌈m/2⌉个孩子节点(至少有⌈m/2⌉-1个关键字);
若根节点不是叶子结点,根节点至少两个孩子

  • 结构体定义
typedef int KeyType;		//关键字类型
typedef struct node
{
	int keynum;/*节点当前存储的关键字个数*/
	KeyType key[MAX];			//关键字域
	struct node* parent;/*双亲节点指针*/
	struct node* ptr[MAX];/*孩子节点指针数组*/
}BTNode;
  • 插入

  • 删除

1.6 散列查找

哈希表(又称散列表),是一种存储结构,适用于记录的关键字与存储的地址存在某种关系的数据。

基本思路是,设要存储的元素个数为n,设置一个长度为m的连续内存单元,以每个元素的关键字ki(i取值为0~n-1)为自变量,通过一个称为哈希函数的函数h(ki),把ki映射为内存单元的地址(或下标),并把该元素存储在这个内存单元中,这个地址也称为哈希地址。这样构造的线性存储结构就是哈希表。

  • 直接取地址法
  • 除留余数法
  • 数字分析法
  1. 开放地址法
  2. 拉链法

2.PTA题目介绍

2.1 是否完全二叉搜索树

  • 思路
    构造二叉搜索树

根据二叉搜索树的定义,若左子树不空,左子树的所有结点的值都要比根节点小;若右子树不空,右子树所有节点的值都比根节点大,且左右子树也都是二叉排序树。同时,中序遍历的二叉搜索树可以得到一个递增的序列。

根据题意,该题定义的二叉搜索树是初始为空的且左子树键值大,右子树键值小。所以,该中序遍历后是一个递减的序列,在构造的时候需要让待插入比当前结点小的结点作为右子树,比当前结点大的作为左子树。

判断是否为完全二叉搜索树

根据完全二叉搜索树的定义,若设二叉树的高度为h,除最后一层外,其它各层 的结点数都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布,这就是完全二叉树。且层次遍历时遇到第一个空结点层次遍历恰好结束,如果遍历到一个结点只有左孩子,或者左右孩子均为空时,那么剩余未遍历的结点,全都是叶子结点。

  • 伪代码
函数:
void Insert(BinTree*& BST, int k);//把结点k插入二叉排列树中
bool IsCompleteBinaryTree(BinTree*& BST, int n);//判断是否为完全二叉树
void LevelOutput(BinTree *BST);//层次遍历输出

构造二叉搜索树://注意:题目要求左子树键值大,右子树键值小
    如果BST为空
        建立新结点
    如果结点k<BST->key 
        递归进入右子树
    如果结点k>BST->key
        递归进入左子树  

判断是否为完全二叉树://即判断是否所有结点都已经遍历
   queue存储树结点的队列
   定义变量num来判断是否所有结点都已经遍历
   if 树空
      也算作是完全二叉树,返回true
   end if
   先把树的根节点放入队列
   while 1
       取队首node
       如果为空则跳出循环
       不为空则让左右子树进栈,先左后右
       出栈队首并使得输出个数num增1
   end while
   判断num的值,等于节点个数n则说明在遇到空节点前树节点遍历完成,返回true,反之,则返回false

层次遍历输出类似于判断是否为完全二叉树的函数,其实两个函数可以结合
  • 代码
#include<stdio.h>
#include<stdlib.h>

struct TNode {
    int Data;
    struct TNode *Left, *Right;
};

struct TNode *Insert(struct TNode *BST, int X)  //搜索二叉树的插入
{ 
    if(!BST) {
        BST = (struct TNode *)malloc(sizeof(struct TNode));
        BST->Data = X;
        BST->Left = BST->Right = NULL;
    }
    else {
        if(X > BST->Data) {
            BST->Left = Insert(BST->Left, X);
        }
        else if(X < BST->Data) {
            BST->Right = Insert(BST->Right, X);
        }
    }

    return BST;
}

int LevelorderTraversal(struct TNode *BT) //层序遍历 
{
    int t = 0, k = 1, flag = 1;
    /*t是队头指针,k为队尾指针,flag用于标记是否为完全二叉树,非0即真
     *flag == 2为标记,如果flag==2之后的所有结点不全为叶子节点,为否*/ 
    struct TNode *s[21]; //队列大小 
    s[t] = BT; //第一个二叉树根结点入队列 
    while(t != k) { //t于k相等说明,队列为空(此非循环队列)。 
        if(!t) {
            printf("%d", s[t]->Data); //第一个打印不用空格 
        } 
        else {
            printf(" %d", s[t]->Data); //除第一个以外,其他前面加空格,这样保证了结尾不会有空格 
        }
        if(s[t]->Left && s[t]->Right) { //若左右结点都不为空 
            if(flag == 2) {  // 如果此结点已被标记,而此节点不为叶子,则不为完全二叉树 
                flag = 0; //标记为否 
            }
            //遍历继续 
            s[k++] = s[t]->Left; //左子树入队 
            if(t == k) {
                break;
            }
            s[k++] = s[t]->Right; //右子树入队 
            if(t == k) {
                break;
            }
        } 
        else if(s[t]->Left && (!s[t]->Right)){ //如果左子树不为空,右子树为空 
            if(flag == 2) { // 如果此结点已被标记,而此节点不为叶子,则不为完全二叉树 
                flag = 0; //标记为否 
            }
            //遍历继续 
            s[k++] = s[t]->Left;//左子树入队  
            if(flag != 0) { //如果此树已经确定不为完全二叉树,则不必标记 
                flag = 2; //若此树未被标记,则在此处标记,后面的节点需都为叶子才是完全二叉树,否则不是 
            }
            if(t == k) {
                break;
            }
        }
        else if(!s[t]->Left && s[t]->Right){ //左子树为空,而右子树不为空,则此树一定是非完全二叉树 
            flag = 0; //标记为否 
            s[k++] = s[t]->Right; //右子树入队 
            if(t == k) {
                break;
            }
        }
        else { //如果左右子树都为空 
            if(flag == 1) { //并且此树还认为正确的 
                flag = 2; //标记为不一定正确 
            }
        }
        t ++; //队头指针右移,用于出队 
    }

    return flag; //返回flag的值 
}

int main()
{
    int i, n, X, H;
    struct TNode *BST = NULL;
    scanf("%d", &n);
    for(i = 0; i < n; i ++) {
        scanf("%d", &X);
        BST = Insert(BST, X);
    }
    if(LevelorderTraversal(BST) != 0) { //flag的值非0即真 
        printf("
YES
");
    }
    else {
        printf("
NO
");
    }

    return 0;
}
  • 提交列表
  • 知识点
    完全二叉树
    完全二叉树是特殊形态的满二叉树,在满二叉树的基础上,它的叶子结点可以不全满,且叶子结点整体偏左。

层次遍历
借助队列来储存二叉树每一层的元素并依次输出。

二叉搜索树的插入操作

边查找边插入,算法中的根节点指针要用引用类型,这样才能将改变后的值传回给实参

2.2 航空公司VIP客户查询

  • 思路
    建立哈希链,取乘客的身份证号后5位作为哈希地址进行存储,如果不是会员则添加,是会员就看里程数

但这题借用map容器更加方便,但因为身份证最后一位校验码有x,则需要将x转化为数字

时间复杂度:O(n²)

  • 伪代码
获得哈希表地址:
	for i = 12 to i = 16
		取后五位数字作为哈希地址
	if 最后以为为x
	temp乘10再累加
	else
	直接转换为五位数;
	return temp;

在哈希链中寻找用户信息:
	node = h[temp]->next;
	while 当前用户有信息存储
		if 用户信息与存档相匹配
			return node;
		else 用户信息与存档不相匹配
			读取下一个信息;
	end while

向哈希链中插入数据:
	temp = GetID(ID);//获得信息·
	p = Find(h, ID, temp);//核对信息
	if 乘客是会员
		if 要走的距离小于最小里程数
			在本来的里程数上加上k算作最大里程
		else
			直接在原里程数上增加
		end if
	end if
	else 乘客不是会员
		新录入ta的信息成为会员
		根据里程大小计算总里程
		将新用户信息插入node中
	end else

创建哈希链:
	for i = 0 to i = n-1
		输入ID以及航程dist
		Insert(h, ID, dist, k);
	end for
  • 代码
#include <stdio.h>
#include<stdlib.h>
#include<string.h>


typedef struct node *Node;
struct node {
    char*IDnumber;
    int mileages;
    Node Left,Right;
};
Node Tree[11]= {0};
Node Insert(Node,char*,int);
void query(Node,char*);

int main() {
    int n,standard;
    scanf("%d%d",&n,&standard);

    char ID[19];
    int mileage;
    while(n--) {
        scanf("%s %d",ID,&mileage);
        if(mileage<standard)mileage=standard;
        int Index=ID[17]-'0';
        if(Index>9)Index=10;
        Tree[Index]=Insert(Tree[Index],ID,mileage);
    }

    scanf("%d",&n);
    while(n--) {
        scanf("%s",ID);
        int Index=ID[17]-'0';
        if(Index>9)Index=10;
        query(Tree[Index],ID);
    }
    return 0;
}


void query(Node T,char*ID) {
    if(!T) {
        printf("No Info
");
        return;
    }
    int flag=strcmp(T->IDnumber,ID);
    if(flag==0) {
        printf("%d
",T->mileages);
    } else if(flag>0)query(T->Left,ID);
    else query(T->Right,ID);
}

Node Insert(Node h,char*a,int K) {
    if(!h) {
        Node temp=(Node)malloc(sizeof(struct node));
        temp->IDnumber=(char*)malloc(sizeof(char)*19);
        strcpy(temp->IDnumber,a);
        temp->mileages=K;
        temp->Left=NULL;
        temp->Right=NULL;
        return temp;
    } else {
        int flag=strcmp(h->IDnumber,a);
        if(!flag)h->mileages+=K;
        else if(flag>0)h->Left=Insert(h->Left,a,K);
        else h->Right=Insert(h->Right,a,K);
        return h;
    }
}

  • 提交列表
  • 知识点
    哈希链的构造创建和寻找,建链前需要对哈希链进行初始化和申请空间。
    哈希链拉链法的使用

2.3 基于词频的文件相似度

  • 思路
    本题设计一个倒排索引表结构实现,单词作为关键字。我使用的方法,即将文件按照单词的分类,存储到每个单词的结构中,最后一个结构中保存了该单词所含有的文件。对于每个单词而言,可以使用哈希链来做,不过这里可以用 STL 库的 map 容器来存放。
  • 伪代码
定义int类型变量的两个文件内编号 file_a,file_b作为待查找的文件编号
定义变量类型为<string,int[]>的map容器构建单词索引表
根据单词在单词索引表中构建映射
for i=0 to 文件组数
    输入文件编号
    for iterator=map.begin() to !map.end()遍历单词
        if 单词在两个文件都出现过
            修正重复单词数,合计单词数
        else if 单词在其中一个文件中出现过
            修正合计单词数
        end if
    end for
end for
计算输出文件相似度
  • 代码

#include<stdio.h>
#include<math.h>
#include<string.h>
#include<string>
#include<map>
#include<stack>
#include<vector>
#include<algorithm>
#include<sstream>
#include<set>
#include<iostream>
using namespace std;
#define inf 0x3f3f3f
int main()
{
	int n,m,x,y,i,j,l;
	cin>>n;cin.get();
	map<string,int> a[105];
	string s;	
	for(j=1;j<=n;j++)
	{
		while(getline(cin,s))
		{
			if(s=="#")break;
			stringstream ss(s);
			while(ss>>s)
			{
				string op="";
				i=0;
				while(i<s.size())
				{
					if(s[i]>='A'&&s[i]<='Z')
					op+=s[i]+32;
					else if(s[i]>='a'&&s[i]<='z')
					op+=s[i];
					else 
					{
						if(op.size()<3);
						else if(op.size()>10)
						{
							op=op.substr(0,10);
							a[j][op]++;
						}
						else a[j][op]++;
						op.clear();
					}
					i++;
				}	
				if(op.size()<3)continue;
				else if(op.size()>10)
				{
					op=op.substr(0,10);
					a[j][op]++;
				}
				else a[j][op]++;				
			}
		}
	}
	scanf("%d",&m);
	while(m--)
	{
		scanf("%d%d",&x,&y);
		int sum=0;
		for(map<string,int>::iterator it=a[x].begin();it!=a[x].end();it++)
		{
			if(a[y].count(it->first))sum++;
		}
		if((int)a[x].size()+(int)a[y].size()-sum==0)printf("0.0%%
");
		else printf("%.1f%%
",100.*sum/((int)a[x].size()+(int)a[y].size()-sum));
	}
}

  • 提交列表
原文地址:https://www.cnblogs.com/RocEye/p/14883647.html