B和B+树
B树查找的本质与二叉树的查找类似,不同的是二叉树每个节点最多两个分支,B树每个结点x拥有n[x]个关键字,我们需要进行多路分支,如图为B树示例:
同时B树中每个结点的关键字是有序的,节点中关键字限定了其分支的根节点的关键字范围。
查找步骤:
(1)从根节点开始,对每一个结点的所有关键字分块查找,寻找关键字k
(2)如果根节点中存在关键字则返回关键字的位置,否则进入相应的块(分支根节点)进行递归查找,如果所有结点查找完仍未找到,返回NULL
B树的算法实现:
B树和B+树都是用作外查找的数据结构,都是平衡多路查找树。两者的差异如下:
1.在B+树中,具有n个关键字的结点含有n棵子树,即每个关键字对应一棵子树,而在B树中,具有n个关键字的结点含有(n+1)棵子树。
2.在B+树中,除根结点外,每个结点中的关键字个数n的取值范围是m/2~ m,根结点n的取值范围是2~ m;而在B树中,除根结点外,其他所有非叶结点的关键字个数n的取值范围是[m/2]-1~ m-1,根结点n的取值范围是1~m-1,
3.B+树中的所有叶结点包含了全部关键字,即其他非叶结点中的关键字包含在叶结点中,而在B树中,关键字是不重复的。
4。B+树中的所有非叶结点仅起到索引的作用,即结点中的每个索引项只含有对应子树的最大关键字和指向该子树的指针,不含有该关键字对应记录的存储地址,而在B树中,每个关键字对应一个记录的存储地址。
5.通常在B+树上有两个头指针,一个指向根结点,另一个指向关键字最小的叶结点,所有叶结点链接成一个不定长的线性链表所以B树只能进行随机查找,而B+树可以进行随机查找和顺序查找
哈希查找
哈希查找算法的基础是哈希表,核心是在关键字的存储位置和关键字的值之间建立一定的函数关系,由于哈希查找基本不需要进行元素之间的比较,查找时间与记录的长度无关,所以它效率很高,最好时间复杂度为o(1)
用开放定址法解决哈希冲突的哈希查找算法
class HashTable{
public:
HashTable(int size)
{
maxSize = size;
count = 0;
element = new DataType[size];分配空间
if(element == NULL)
exit(1);//判断是否空间分配成功
for(int i = 0;i < size;i++)
element[i] = NULL;//初始化每个存储空间的值
}
~HashTable()
{
delete[] element;
}
int hash(DataType value);//散列函数
{
return value%13;//采用除留余法计算散列地址
}
int searchHash(DataType value)
{
int p = hash(value);//计算散列地址
if(element[p]==value) return p;//如果相等,表示没有发生冲突,返回p
int rp = (p+1)%maxsize;//线性探测法处理冲突,选取d=1
while(rp !=p)
{
if(element[rp] == value) return rp;//如果新地址的值与value相等 返回新地址
if(element[rp]==NULL)break;如果找到空白地址
rp = (rp+1) %maxSize;//循环使用线性探测法找空白地址
}
if(rp == p) return -2;//表示查找失败
else
//element[rp] = value;在空白地址上插入此元素并返回地址
return rp;
}
DataType getDate(int i)
{//获取散列表第i个元素的值
if(i <=0)
std::cout<<"索引值错误,必须为正整数";
return element[i-1];
}
bool insertHash(DataType value);
private:
int maxSize;
int count;//当前元素数
DataType* element;//数据域
}
链地址法:
将具有相同散列地址的不同关键字放在一个单链表中,这些单链表称为同义词子表,散列表中存储的是这些单链表的头指针,如果有n个关键字存储在长度为m的散列表中,同义词子表的长度为n/m,
利用哈希表查找一个字符串中第一个只出现一次的字符
#include <iostream>
#include <cstdlib>
#include <string>
#include <cctype>
using namespace std;
class FirstChar {
public:
int n;//一共52个大小写字母
string s;//输入的字符串 只含有大小写
/***哈希表单元,存储字符和次数***/
typedef struct Hash {
char ch;
int num;
};
Hash* HashTable;
char first;//第一次只出现一次的字符
FirstChar(const string& str) {
//分配空间
n = 52;
HashTable = new Hash[n];
if (!HashTable)
{
cerr << "内存不足,程序退出!";
exit(1);
}
int i;
for (i = 0; i < n; i++) {
HashTable[i].ch = '\0';//初始化操作
HashTable[i].num = 0;
}
s = str;//将用户输入的字符串str赋给s
}
~FirstChar() {
delete[] HashTable;
}
int HashFunction(char ch);
void LoadHashTable();
char findOnlyOneChar();
};
/* 哈希函数
* 小写字母存放 0-25的位置
* 大写字母存放 26-51的位置*/
int FirstChar::HashFunction(char ch) {
if (islower(ch)) {
return ch - 'a';
}
else return ch - 'A' + n / 2;
}
void FirstChar::LoadHashTable() {
int pos;
for (int i = 0; i < s.length(); i++)
{
pos = HashFunction(s[i]);
if (!HashTable[pos].ch)//如果该位置还没有字母
{
HashTable[pos].ch = s[i];//注意,在typedef struct Hash中ch为char类型
HashTable[pos].num = 1;
}
else HashTable[pos].num++;
}
}
char FirstChar::findOnlyOneChar()
{
LoadHashTable();
int position;
for (int i = 0; i < s.length(); i++)
{
position = HashFunction(s[i]);
if (this->HashTable[position].num == 1)
return this->HashTable[position].ch;
}
return NULL;
}
int main()
{
cout << "请输入一个字符串:";
string str;
cin >> str;
if (!cin.good()) {
cerr << "输入异常!" << endl;
exit(1);
}
for (int i = 0; i < str.length(); i++) {
if (!islower(str[i]) && !isupper(str[i])) {
cerr << "字符串只能含有大小写字符!" << endl;
return 0;
}
}
FirstChar FirstChar(str);
char answer = FirstChar.findOnlyOneChar();
if (answer == '\0')
cout << "该字符串没有不重复的字符!" << endl;
else
cout << "第一次出现的不重复字符为:" << answer << endl;
}
该算法虽然用了较多的空间,却换来了时间效率的提高,是一种空间换时间的算法。
hash_map和unordered_map
hash_map:重复键值的元素不会被插入
unordered_map:重复键值的元素不会被插入
用法示例:
#include<unordered_map>
int main() {
pair<int, string> s1(2, "李明"),s2(4,"小红"),s3(5,"沉");
unordered_map<int, string>my;
my.insert(make_pair(1, "mary"));
my.insert(s2);
my.insert(s3);
unordered_map<int, string>::iterator it;
for (it = my.begin(); it != my.end(); it++)
cout << it->first << "," << it->second;
}
find方法:
如果key存在,则find返回key对应的迭代器,如果key不存在,则find返回unordered_map::end
设计算法删除重复的元素
void deleteSame(int a[], int &n)
{
unordered_map<int, int>amap;
int k = 0;
for (int i = 0; i < n; i++)
{
if (amap.count(a[i]) == 0)
{
a[k] = a[i];//修改原数组,注意k从0开始,这样重复的a[i]会被跳过
k++;
}
amap.insert(make_pair(a[i], i));//将a[i]作为关键字插入
}
n = k;//新的长度
}
设计算法找出元素之和为target的元素下标
#include<unordered_map>
vector<int>tSum(int a[], int n, int target)
{
int temp;
unordered_map<int, int>amap;
vector<int>vcc;
for (int i = 0; i < n; i++)
amap[a[i]] = i;//先将a的元素均插入,元素值作为键
for (int i = 0; i < n; i++)
{
temp = target - a[i];
if (amap.find(temp) != amap.end() && amap[temp] > i)//这条语句表示查找成功的条件
{
vcc.push_back(temp);
vcc.push_back(amap[temp]);
break;//退出
}
}
return vcc;
}
由于unordered_map表查找时间为常量,上述时间复杂度为o(n),属于高效算法
给出一组字符串,按组返回拥有相同变位词的字符串
#include<unordered_map>
void FindSame(vector<string>str, unordered_map<string, vector<string>>& tmap)
{
string temp;
vector<string>now;
//unordered_map<string, vector<string>>myhash;
unordered_map<string, vector<string>>::iterator it;
for (int i =0;i <str.size();i++)
{
temp = str[i];
sort(temp.begin(), temp.end());
now.clear();
now.push_back(str[i]);
it = tmap.find(temp);
if (tmap.find(temp) == tmap.end())
tmap.insert(make_pair(temp, now));//如果没有相同的
else
it->second.push_back(str[i]);//如果有相同变位词,放入另一个unordermap中
}
}
void display(unordered_map<string, vector<string>> tmap)
{
unordered_map<string, vector<string>>::iterator it;
vector<string>::iterator strit;
for (it = tmap.begin(); it != tmap.end(); it++)
{
cout << it->first << ":";
for (strit = it->second.begin(); strit != it->second.end(); strit++)
cout << *strit << ",";
cout << endl;
}
}
int main() {
unordered_map<string, vector<string>> tmap;
vector<string>my;
my.push_back("abc");
my.push_back("cba");
my.push_back("1456");
my.push_back("6145");
my.push_back("1645");
FindSame(my, tmap);
display(tmap);
}
设计算法判断a中的字符能否组成b
bool Compose(string a, string b)
{
unordered_map<char, int> amap;
for (int i = 0; i < a.size();i++)
{
amap[a[i]]++;
}
for (int i = 0; i < b.size(); i++)
{
if (amap.count(b[i]) == 0) return false;//如果b[i]是没有出现过的字符
if (--amap[b[i]] < 0) return false;//注意这种情况容易被遗漏,即b[i]出现的次数更多的时候
}
return true;
}