线性表的查找技术
线性表一般有两种存储结构:顺序存储结构和链接存储结构。
顺序查找
基本思想:
- 从线性表的一端向另一端逐个将关键码与给定值进行比较;
- 若相等,则查找成功,给出该记录在表中的位置;
- 若查找失败,则给出失败信息。
(1)顺序表的顺序查找
设置哨兵,将它放在查找方向的“尽头”处,这样每次比较后都无需判断查找位置是否越界。
Search.h
//
// Copyright: Created by 野尽, Ye Jin. 596859576@qq.com
// Author: 野尽
// Date: 2020/4/7
// Description:
//
#ifndef SEQLIST_CLASSTEMPLATEINHFILE__SEARCH_H
#define SEQLIST_CLASSTEMPLATEINHFILE__SEARCH_H
#include <iostream>
using namespace std;
//
// Function: SeqSearchSeqList
// Description: 顺序表的顺序查找
// Calls: NULL
// Table Accessed: NULL
// Table Updated: NULL
// Input: int r[] 待查找序列, int n 序列长度, int k 待查找元素
// Output: 待查找元素下标,若查找失败则返回0
// Return: 待查找元素下标,若查找失败则返回0
// Others: NULL
//
int SeqSearchSeqList(int r[], int n, int k)
{
// 设置下标0作为哨兵
r[0] = k;
int i = n - 1;
while (r[i] != k) {
cout<<r[i]<<" ";
i--;
}
cout<<endl;
return i;
}
#endif //SEQLIST_CLASSTEMPLATEINHFILE__SEARCH_H
main.cpp
#include "Search.h"
int main() {
SeqSearchSeqList
const int n = 11;
// 待查找序列的下标0元素是哨兵,此位置初始化任意一个数,此例中初始为0
// 序列从下标1开始
int a[n] = {0,7,5,4,2,6,9,1,8,3,10};
int key = 7;
// 当查找位置为0时,说明查找失败
int pos = SeqSearchSeqList(a, n, key);
if (pos != 0)
cout<<"待查找值 "<<key<<" 的位置是 " << pos <<endl;
else
cout<<"未发现待查找值 "<<key<<endl;
cout<<endl;
key = 12;
// 当查找位置为0时,说明查找失败
pos = SeqSearchSeqList(a, n, key);
if (pos != 0)
cout<<"待查找值 "<<key<<" 的位置是 " << pos <<endl;
else
cout<<"未发现待查找值 "<<key<<endl;
cout<<endl;
return 0;
}
测试结果:
10 3 8 1 9 6 2 4 5
待查找值 7 的位置是 1
10 3 8 1 9 6 2 4 5 7
未发现待查找值 12
(2)单链表的顺序查找
Search.h
//
// Copyright: Created by 野尽, Ye Jin. 596859576@qq.com
// Author: 野尽
// Date: 2020/4/7
// Description:
//
#ifndef SEQLIST_CLASSTEMPLATEINHFILE__SEARCH_H
#define SEQLIST_CLASSTEMPLATEINHFILE__SEARCH_H
#include <iostream>
#include "LinkList.h"
using namespace std;
//
// Function: SeqSearchLinkList
// Description: 单链表的顺序查找
// Calls: NULL
// Table Accessed: NULL
// Table Updated: NULL
// Input: Node<int> * first 链表的头指针, int k 待查找值
// Output: NULL
// Return: NULL
// Others: NULL
//
int SeqSearchLinkList(Node<int> * first, int k)
{
Node<int> * p = first->next;
int pos = 1;
while (p != NULL && p->data != k)
{
p = p->next;
pos++;
}
if (p != NULL && p->data == k)
return pos;
else
return 0;
}
#endif //SEQLIST_CLASSTEMPLATEINHFILE__SEARCH_H
main.cpp
#include "Search.h"
int main() {
// SeqSearchLinkList
const int n = 10;
int a[n] = {7,5,4,2,6,9,1,8,3,10};
int key = 9;
LinkList<int> linkList(a, n);
linkList.PrintList();
// 得到链表头指针
Node<int> * p = linkList.GetFirstPointer();
// 当查找位置为0时,说明查找失败
int pos = SeqSearchLinkList(p, key);
if (pos != 0)
cout<<"待查找值 "<<key<<" 的位置是 " << pos <<endl;
else
cout<<"未发现待查找值 "<<key<<endl;
cout<<endl;
key = 12;
// 当查找位置为0时,说明查找失败
pos = SeqSearchLinkList(p, key);
if (pos != 0)
cout<<"待查找值 "<<key<<" 的位置是 " << pos <<endl;
else
cout<<"未发现待查找值 "<<key<<endl;
cout<<endl;
return 0;
}
测试结果:
7 5 4 2 6 9 1 8 3 10
待查找值 9 的位置是 6
未发现待查找值 12
注意:单链表的查找需要引用#include "LinkList.h",这个头文件在我博客中有,复制下来直接用就行了。
折半查找
折半查找要求线性表中的记录必须 按关键码有序
,并且必须采用 顺序存储
。
基本思想:
- 在
有序表
中,取中间记录作为比较对象,若给定值与中间记录的关键码相等,则查找成功; - 若给定值
小于
中间记录的关键码,则在中间记录的左半区
继续查找; - 若给定值
大于
中间记录的关键码,则在中间记录的右半区
继续查找; - 不断重复上述步骤,直到查找成功,或所查找的区域无记录。
(1)非递归算法
Binary.h
//
// Copyright: Created by 野尽, Ye Jin. 596859576@qq.com
// Author: 野尽
// Date: 2020/4/7
// Description:
//
#ifndef SEQLIST_CLASSTEMPLATEINHFILE__SEARCH_H
#define SEQLIST_CLASSTEMPLATEINHFILE__SEARCH_H
#include <iostream>
#include "LinkList.h"
using namespace std;
//
// Function: BinarySearchRecurse
// Description: 折半查找的非递归算法
// Calls: NULL
// Table Accessed: NULL
// Table Updated: NULL
// Input: int r[] 待查找序列, int n 序列长度, int k 待查找值
// Output: 若查找成功,则返回待查找值的位置;若失败,则返回0.
// Return: 若查找成功,则返回待查找值的位置;若失败,则返回0.
// Others: NULL
//
int BinarySearchRecurse(int r[], int n, int k)
{
int low = 1;
int high = n;
while (low<=high)
{
int mid = (low + high) / 2;
if (k < r[mid])
high = mid - 1;
else if (k > r[mid])
low = mid + 1;
else
return mid;
}
return 0;
}
#endif //SEQLIST_CLASSTEMPLATEINHFILE__SEARCH_H
main.cpp
#include "Search.h"
int main() {
// BinarySearchRecurse
const int n = 15;
int a[n] = {1,2,3,9,12,15,22,25,27,31,33,34,35,36,41};
int key = 12;
// 当查找位置为0时,说明查找失败
int pos = BinarySearchRecurse(a, n, key);
if (pos != 0)
cout<<"待查找值 "<<key<<" 的位置是 " << pos <<endl;
else
cout<<"未发现待查找值 "<<key<<endl;
cout<<endl;
key = 45;
// 当查找位置为0时,说明查找失败
pos = BinarySearchRecurse(a, n, key);
if (pos != 0)
cout<<"待查找值 "<<key<<" 的位置是 " << pos <<endl;
else
cout<<"未发现待查找值 "<<key<<endl;
cout<<endl;
return 0;
}
测试结果:
待查找值 12 的位置是 4
未发现待查找值 45
(2)折半查找的递归算法
Binary.h
//
// Copyright: Created by 野尽, Ye Jin. 596859576@qq.com
// Author: 野尽
// Date: 2020/4/7
// Description:
//
#ifndef SEQLIST_CLASSTEMPLATEINHFILE__SEARCH_H
#define SEQLIST_CLASSTEMPLATEINHFILE__SEARCH_H
#include <iostream>
#include "LinkList.h"
using namespace std;
//
// Function: BinarySearch
// Description: 折半查找的递归算法
// Calls: NULL
// Table Accessed: NULL
// Table Updated: NULL
// Input: int r[] 待查找序列, int low 区间低端, int high 区间高端, int k 待查找值
// Output: 若查找成功,则返回待查找值的位置;若失败,则返回0.
// Return: 若查找成功,则返回待查找值的位置;若失败,则返回0.
// Others: NULL
//
int BinarySearch(int r[], int low, int high, int k)
{
if (low>high)
return 0;
else
{
int mid = (low + high) / 2;
if (k<r[mid])
return BinarySearch(r, low, mid - 1, k);
else if (k>r[mid])
return BinarySearch(r, mid + 1, high, k);
else
return mid;
}
}
#endif //SEQLIST_CLASSTEMPLATEINHFILE__SEARCH_H
main.cpp
#include "Search.h"
int main() {
// BinarySearch
key = 15;
// 当查找位置为0时,说明查找失败
pos = BinarySearch(a, 1, n, key);
if (pos != 0)
cout<<"待查找值 "<<key<<" 的位置是 " << pos <<endl;
else
cout<<"未发现待查找值 "<<key<<endl;
cout<<endl;
key = 5;
// 当查找位置为0时,说明查找失败
pos = BinarySearchRecurse(a, n, key);
if (pos != 0)
cout<<"待查找值 "<<key<<" 的位置是 " << pos <<endl;
else
cout<<"未发现待查找值 "<<key<<endl;
cout<<endl;
return 0;
}
测试结果:
待查找值 15 的位置是 5
未发现待查找值 5
树表的查找技术
(1)二叉排序树
二叉排序树又称为二叉查找树,具有如下性质:
- 若它的左子树不空,则左子树上所有结点的值均小于根结点的值;
- 若它的右子树不空,则右子树上所有节点的值均大于根结点的值;
- 它的左右子树均为二叉排序树。
中序遍历二叉排序树可以得到一个按关键码有序的序列。
二叉树的插入 :
- 若 root 为空,则将结点 s 作为根结点插入;
- 否则,若s->data小于root->data,则把结点 s 插入到 root 的左子树中;
- 否则把结点 s 插入到 root 的右子树中。
二叉树的构造 :
- 依次取每个记录 r[i] ,执行下述操作;
- 申请一个数据域为 r[i] 的结点 s ,令结点 s 的左右指针域为空;
- 调用插入算法InsertBST,将结点 s 插入到二叉排序树中。
二叉排序树的 删除
:
二叉排序树执行删除操作时,需要注意执行删除之后,需要保证删除之后其仍为二叉排序树。
下面讨论在二叉排序树中删除最小结点
。设待删除结点为 p,其双亲结点为 f,p 为 f 的左孩子。
有以下三种情况:
<1> p 为叶子结点,即 p 没有左子树也没有右子树
- 由于删除 p 之后没有影响二叉排序树的特性,所以只需将被删除结点的双亲结点的相应指针域置为空指针,即 f->lchild = NULL。
<2> p 只有左子树 pL 或 只有右子树 pR
- 此时,只需将 pL 或 pR 替换为 f 的左子树,即 f->lchild = p->lchild 或 f->lchild = p->rchild。
<3> p 既有左子树 pL 也有右子树 pR
- 此时需要在被删除结点 p 的子树上找到一个大于 p 结点的最小值,或小于 p 结点的最大值来代替 p 的值,然后再删除结点 s。
实现: