五、查找
1.动态查找
1.1二叉排序树
相关功能有查找,插入,删除;难点在于二级指针的使用。
下述代码的删除函数应当还有待优化。
#include <iostream>
using namespace std;
class TreeNode
{
public:
int data;
TreeNode *left, *right;
TreeNode() { left = right = NULL; }
TreeNode(int key) : data(key) { left = right = NULL; }
};
class BinTree
{
public:
TreeNode *root;
BinTree(int arr[], int n) : root(NULL)
{
for (int i = 0; i < n; i++)
{
Insert(root, arr[i]);
}
}
//当前节点,查找值,当前节点的双亲结点,如果找到则等于找到结点的指针,否则等于查找到的最后一个结点,函数返回false
//这个二级指针太关键了,传入的是外界指针p的指针地址,然后对*p的操作就是直接操作外界指针的地址,从而达到改变外界指针的目的
bool Search(TreeNode *r, int key, TreeNode *parent, TreeNode **p)
{
if (r == NULL)
{
*p = parent; //p等于查找到的最后一个结点,函数返回false
return false;
}
else if (r->data == key)
{
*p = r;
return true;
}
else if (r->data > key)
{
return Search(r->left, key, r, p);
}
else if (r->data < key)
{
return Search(r->right, key, r, p);
}
return false;
}
bool Insert(TreeNode *r, int key)
{
TreeNode *p;
if (!Search(r, key, NULL, &p))
{
TreeNode *temp = new TreeNode(key);
if (p == NULL) //表示此时根节点为空
root = temp; //注意这里需要直接对root进行修改,对r这个形参的修改是无效的,当然也可以用二级指针解决这个问题
else if (key > p->data)
p->right = temp;
else if (key < p->data)
p->left = temp;
return true;
}
else //树中存在一样的结点,则插入失败
return false;
}
void Delete(TreeNode **p)
{
TreeNode *temp = *p; //临时指针,用于指向需要删除的结点
if ((*p)->left == NULL) //没有左结点,就直接把右边的全部移上来
{
*p = (*p)->right;
delete temp;
}
else if ((*p)->right == NULL) //没有右结点,就直接把左边的全部移上来
{
*p = (*p)->left;
delete temp;
}
else //既有左结点又有右结点,两种方法
{
//左删法,找到本结点左边最右结点,与本结点替换后,删去
TreeNode *lchild = (*p)->left;
while (lchild->right)
{
temp = lchild; //让temp指向最右结点的上一个结点
lchild = lchild->right;
}
(*p)->data = lchild->data; //换值
if (temp == *p) //temp没有移动
temp->left = lchild->left;
else
temp->right = lchild->left;
delete lchild;
//右删法,找到本结点左边最左结点,与本结点替换后,删去
}
}
bool DeleteNode(TreeNode **r, int key) //必须把树根传进来,不然删不掉结点
{
if (!*r)
return false;
if (key == (*r)->data)
{
Delete(r);
return true;
}
else if (key < (*r)->data)
return DeleteNode(&(*r)->left, key);
else if (key > (*r)->data)
return DeleteNode(&(*r)->right, key);
return false;
}
void Inorder(TreeNode *r)
{
if (r == NULL)
return;
Inorder(r->left);
cout << r->data << ' ';
Inorder(r->right);
}
};
int main()
{
int t;
cin >> t;
while (t--)
{
int n;
cin >> n;
int *arr = new int[n];
for (int i = 0; i < n; i++)
cin >> arr[i];
BinTree bt(arr, n);
bt.Inorder(bt.root);
cout << endl;
int m;
cin >> m;
while (m--)
{
int x;
cin >> x;
bt.DeleteNode(&bt.root, x);
bt.Inorder(bt.root);
cout << endl;
}
delete[] arr;
}
return 0;
}
/*
样例输入
1
6
22 33 55 66 11 44
3
66
22
77
样例输出
11 22 33 44 55 66
11 22 33 44 55
11 33 44 55
11 33 44 55
*/
用引用代替二级指针
#include <iostream>
using namespace std;
class TreeNode
{
public:
int data;
TreeNode *left, *right;
TreeNode() { left = right = NULL; }
TreeNode(int key) : data(key) { left = right = NULL; }
};
class BinTree
{
public:
TreeNode *root;
BinTree(int arr[], int n) : root(NULL)
{
for (int i = 0; i < n; i++)
{
Insert(root, arr[i]);
}
}
bool Search(TreeNode *&r, int key, TreeNode *parent, TreeNode *&p)
{
if (r == NULL)
{
p = parent;
return false;
}
else if (r->data == key)
{
p = r;
return true;
}
else if (key < r->data)
return Search(r->left, key, r, p);
else if (key > r->data)
return Search(r->right, key, r, p);
return false;
}
bool Insert(TreeNode *&r, int key)
{
TreeNode *p;
if (!Search(r, key, NULL, p))
{
TreeNode *temp = new TreeNode(key);
if (p == NULL)
r = temp;
else if (key < p->data)
p->left = temp;
else if (key > p->data)
p->right = temp;
return true;
}
else
return false;
}
void Delete(int key) { Delete(root, key); }
void Delete(TreeNode *&r, int key)
{
if (!r)
return;
else if (key < r->data)
Delete(r->left, key);
else if (key > r->data)
Delete(r->right, key);
else
{
TreeNode *temp = r;
if (r->left == NULL)
{
r = r->right;
delete temp;
}
else if (r->right == NULL)
{
r = r->left;
delete temp;
}
else //找到左边最大的结点与需要删除的结点交换
{
TreeNode *lchild = r->left;
while (lchild->right)
{
temp = lchild;
lchild = lchild->right;
}
r->data = lchild->data;
if (temp == r)
temp->left = lchild->left;
else
temp->right = lchild->left;
delete lchild;
}
}
}
void InOrder(TreeNode *r)
{
if (!r)
return;
InOrder(r->left);
cout << r->data << ' ';
InOrder(r->right);
}
};
int main(int argc, char const *argv[])
{
int t;
cin >> t;
while (t--)
{
int n;
cin >> n;
int *arr = new int[n];
for (int i = 0; i < n; i++)
cin >> arr[i];
BinTree bt(arr, n);
bt.InOrder(bt.root);
cout << endl;
int m;
cin >> m;
while (m--)
{
int key;
cin >> key;
bt.Delete(key);
bt.InOrder(bt.root);
cout << endl;
}
delete[] arr;
}
return 0;
}
2.哈希查找
2.1线性探测
#include <iostream>
using namespace std;
#define NULLKEY -999
class HashTable
{
public:
int *table;
int length;
HashTable(int l) : length(l)
{
table = new int[length];
for (int i = 0; i < length; i++)
table[i] = NULLKEY;
int n; //关键字数
cin >> n;
while (n--)
{
int key;
cin >> key;
Insert(key);
}
}
int Hash(int key) { return key % 11; }
void Insert(int key)
{
int addr = Hash(key);
while (table[addr] != NULLKEY)
{
addr = (addr + 1) % length;
}
table[addr] = key;
}
bool Search(int key, int &cnt, int &addr)
{
cnt = 0;
addr = Hash(key);
while (true)
{
cnt++;
if (key == table[addr])
return true;
else if (table[addr] == NULLKEY)
return false;
addr = (addr + 1) % length;
}
}
void Search(int key)
{
int cnt, addr;
if (Search(key, cnt, addr))
cout << 1 << ' ' << cnt << ' ' << addr + 1 << endl;
else
cout << 0 << ' ' << cnt << endl;
}
void Show()
{
for (int i = 0; i < length; i++)
{
if (table[i] == NULLKEY)
cout << "NULL ";
else
cout << table[i] << ' ';
}
}
};
int main()
{
int t;
cin >> t;
while (t--)
{
int m; //哈希表长
cin >> m;
HashTable ht(m);
ht.Show();
int k;
cin >> k;
while (k--)
{
int key;
cin >> key;
ht.Search(key);
}
}
return 0;
}
2.2平方探测(二次探测)
#include <iostream>
using namespace std;
#define NULLKEY -99999
class HashTable
{
public:
int *table;
int length;
HashTable(int m) : length(m)
{
table = new int[length];
for (int i = 0; i < length; i++)
table[i] = NULLKEY;
int n;
cin >> n;
while (n--)
{
int key;
cin >> key;
Insert(key);
}
}
int Hash(int key) { return key % 11; }
void Insert(int key)
{
for (int i = 2, sym = 1, d = 0; d <= length / 2; i++)
{
int addr = Hash(key);
addr = (addr + sym * d * d) % length;
if (addr < 0)
addr += length;
if (table[addr] == NULLKEY)
{
table[addr] = key;
return;
}
if (i % 2 == 0)
{
sym = 1;
d++;
}
else
sym = -1;
}
}
bool Search(int key, int &cnt, int &addr)
{
cnt = 0;
for (int i = 2, sym = 1, d = 0; d <= length / 2; i++)
{
cnt++;
addr = Hash(key);
addr = (addr + sym * d * d) % length;
if (addr < 0)
addr += length;
if (key == table[addr])
return true;
if (table[addr] == NULLKEY)
return false;
if (i % 2 == 0)
{
sym = 1;
d++;
}
else
sym = -1;
}
return false;
}
void Search(int key)
{
int cnt, addr;
if (Search(key, cnt, addr))
cout << 1 << ' ' << cnt << ' ' << addr + 1;
else
cout << 0 << ' ' << cnt;
cout << endl;
}
void Show()
{
for (int i = 0; i < length; i++)
{
if (table[i] == NULLKEY)
cout << "NULL";
else
cout << table[i];
if (i < length - 1)
cout << ' ';
}
cout << endl;
}
};
int main()
{
int t;
cin >> t;
while (t--)
{
int m;
cin >> m;
HashTable ht(m);
ht.Show();
int k;
cin >> k;
while (k--)
{
int key;
cin >> key;
ht.Search(key);
}
}
return 0;
}
/*
样例输入:
1
12 10
22 19 21 8 9 30 33 4 41 13
4
22
15
30
41
样例输出:
22 9 13 NULL 4 41 NULL 30 19 8 21 33
1 1 1
0 3
1 3 8
1 6 6
*/
2.3链地址法
#include <iostream>
using namespace std;
#define NULLKEY -99999
#define TABLELEN 11
class Node
{
public:
int data;
Node *next;
Node() : data(NULLKEY) { next = NULL; }
Node(int key) : data(key) { next = NULL; }
};
class HashTable
{
public:
Node *table;
int length;
HashTable() : length(TABLELEN)
{
table = new Node[length];
for (int i = 0; i < length; i++)
table[i].data = 0; //头结点存放结点个数
int n;
cin >> n;
while (n--)
{
int key;
cin >> key;
Insert(key);
}
}
int Hash(int key) { return key % 11; }
void Insert(int key) //头插法
{
Node *temp = new Node(key);
int addr = Hash(key);
Node *p = &table[addr];
temp->next = p->next;
p->next = temp;
table[addr].data++;
}
// void Insert(int key) //尾插法
// {
// Node *temp = new Node(key);
// int addr = Hash(key);
// Node *p = &table[addr];
// while (p->next)
// {
// p = p->next;
// }
// p->next = temp;
// table[addr].data++;
// }
bool Search(int key, int &cnt, int &addr)
{
cnt = 0;
addr = Hash(key);
Node *p = &table[addr];
if (p->data == 0)
return false;
else
{
while (p->next)
{
cnt++;
if (key == p->next->data)
return true;
p = p->next;
}
return false;
}
}
void Search(int key)
{
int cnt, addr;
if (Search(key, cnt, addr))
cout << addr << ' ' << cnt;
else
{
cout << "error";
Insert(key);
}
cout << endl;
}
void Show()
{
for (int i = 0; i < length; i++)
{
cout << i << '-';
Node *p = &table[i];
do
{
cout << p->data << ' ';
p = p->next;
} while (p);
cout << endl;
}
}
};
int main()
{
HashTable ht;
int t;
cin >> t;
while (t--)
{
int key;
cin >> key;
ht.Search(key);
}
return 0;
}
/*
样例输入:
6
11 23 39 48 75 62
6
39
52
52
63
63
52
使用头插法样例输出:
6 1
error
8 1
error
8 1
8 2
使用尾插法样例输出:
6 1
error
8 1
error
8 2
8 1
*/