第10章_查找

查找是对数据进行操作或处理时经常使用的操作。查找是在一个数据元素集合中查找关键字等于某个给定关键字数据元素的过程。在一个数据元素集合中进行查找的方法很多,主要有静态查找、动态查找和哈希表查找等方法。查找算法的优劣对计算机应用系统的效率影响很大。

静态查找表

静态查找表主要有顺序表、有序顺序表和索引顺序表三种结构。

顺序表

在顺序表上查找的基本思想是:从顺序表的一端开始,用给定数据元素的关键字逐个与顺序表中各数据元素的关键字比较,若在顺序表中查找到要查找的数据元素,则查找成功,函数返回该数据元素在顺序表中的位置;否则查找失败,函数返回-1。

#include <stdio.h>
#define MaxSize 100

typedef int KeyType;
typedef struct
{
    KeyType key;
} DataType;
#include "SeqList.h"

int SeqSearch(SeqList S,DataType x)
{
    int i = 0;
    while(i < S.size && S.list[i].key != x.key) i++;
    if(S.list[i].key == x.key) return i;
    else return -1;
}

void main(void)
{
    SeqList myS = {
        {710,342,45,686,6,841,429,134,68,264},10
    };
    DataType x = {686};
    int i;

    if((i = SeqSearch(myS,x)) != -1)
        printf("该数据元素位置为%d\n",i);
    else
        printf("查找失败\n");
}
/*
该数据元素位置为3
*/

有序顺序表

有序顺序表上的查找算法主要有顺序查找和二分查找两种方法。
1. 顺序查找

#include <stdio.h>
#define MaxSize 100

typedef int KeyType;
typedef struct
{
    KeyType key;
} DataType;
#include "SeqList.h"

int OrderSeqSearch(SeqList S,DataType x)
{
    int i = 0;
    while(i < S.size && S.list[i].key < x.key) i++;
    if(S.list[i].key == x.key) return i;
    else return -1;
}

void main(void)
{
    SeqList myS = {
        {1,2,3,4,5,6,7,8,9,10},10
    };
    DataType x = {7};
    int i;

    if((i = OrderSeqSearch(myS,x)) != -1)
        printf("该数据元素位置为%d\n",i);
    else
        printf("查找失败\n");
}
/*
该数据元素位置为635.    */

2.二分查找

#include <stdio.h>
#define MaxSize 100

typedef int KeyType;
typedef struct
{
    KeyType key;
} DataType;
#include "SeqList.h"

int BinarySearch(SeqList S,DataType x)
{
    int low = 0,high = S.size - 1;//确定初始查找区间上下界
    int mid;
    while(low <= high)
    {
        mid = (low + high) / 2;
        if(S.list[mid].key == x.key) return mid;        //查找成功
        else if(S.list[mid].key < x.key) low = mid + 1;
        else if(S.list[mid].key > x.key) high = mid - 1;
    }

    return -1;
}

void main(void)
{
    SeqList myS = {
        {1,2,3,4,5,6,7,8,9,10},10
    };
    DataType x = {7};
    int i;

    if((i = BinarySearch(myS,x)) != -1)
        printf("该数据元素位置为%d\n",i);
    else
        printf("查找失败\n");
}
/*
该数据元素位置为6
*/

动态查找表

动态查找表主要有二叉树结构和树结构两种类型。二叉树结构有二叉排序树、平衡二叉树等。树结构有B_树B+树。

  1. 二叉排序树
    二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:(1)若左子树不空,则左子树上所有结点的关键字值均小于根结点的关键字值;(2)若右子树不空,则右子树上所有结点的关键字值均大于等于根结点的关键字值;(3)左右子树也均为二叉排序树。

2.二叉排序树的查找算法

typedef int KeyType;
typedef struct
{
    KeyType key;
} DataType;

typedef struct node
{
    DataType data;
    struct node* leftChild;
    struct node* rightChild;
} BiTreeNode;

int Search(BiTreeNode* root,DataType item)
{
    BiTreeNode* p;
    if(root != NULL)
    {
        p = root;
        while(p != NULL)
        {
            if(p->data.key == item.key) return 1;   //查找成功
            if(item.key > p->data.key) p = p->rightChild;
            else p = p->leftChild;
        }
    }

    return 0;
}

3.二叉排序树的插入算法


//在二叉排序树root中查找数据元素item是否存在,若存在则返回0,否则把
//item结点插入到当前结点的左指针或右指针上并返回1
int Insert(BiTreeNode** root,DataType item)
{
    BiTreeNode* current,*parent = NULL,*p;
    current = *root;
    while(current != NULL)
    {
        if(current->data.key == item.key) return 0;     //数据元素已存在
        parent = current;
        if(current->data.key < item.key) current = current->rightChild;
        else current = current->leftChild;
    }

    p = (BiTreeNode*)malloc(sizeof(BiTreeNode));
    if(p == NULL)
    {
        printf("空间不够!");
        exit(1);
    }

    //生成新结点
    p->data = item;
    p->leftChild = NULL;
    p->rightChild = NULL;

    if(parent == NULL) *root = p;       //新结点成为根结点
    else if(item.key < parent->data.key)
        parent->leftChild = p;          //新结点为该结点的左孩子结点
    else
        parent->rightChild = p;         //新结点为该结点的右孩子结点
    return 1;
}

main.c

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

typedef int KeyType;
typedef struct
{
    KeyType key;
} DataType;

typedef struct node
{
    DataType data;
    struct node* leftChild;
    struct node* rightChild;
} BiTreeNode;



//中序遍历显示二叉排序树结点信息函数
void InTraverse(BiTreeNode* root)
{
    if(root == NULL) return ;

    if(root->leftChild != NULL)
        InTraverse(root->leftChild);

    printf("%d ",root->data.key);

    if(root->rightChild != NULL)
        InTraverse(root->rightChild);
}

void main(void)
{
    DataType test[] = {4,5,7,2,1,9,8,11,3},x = {9};
    int n = 9,i,s;
    BiTreeNode* root = NULL;

    for(i = 0;i < n; i++)//依次插入建立二叉排序树
    {
        Insert(&root,test[i]);
    }

    InTraverse(root);       //调用中序遍历函数

    s = Search(root,x);     //调用查找函数

    if(s == 1)
        printf("\n数据元素%d存在!\n",x.key);
    else
        printf("\n数据元素不存在!\n");
}
/*
1 2 3 4 5 7 8 9 11
数据元素9存在!
Press any key to continue
*/

哈希表

青态查找表和动态查找表中,数据元素的存放位置和数据元素的关键字之间没有关系,因此,查找过程是一系列比较的过程。如果我们构造一个查找表,使数据元素的存放位置和数据元素的关键字之间存在某种对应关系,则我们可以直接由数据元素的关键字得到该数据元素的存放位置。这样的查找表就是哈希表。我们把数据元素的关键字和该数据元素的存放位置间的映射函数称为哈希函数。因此可以说,哈希表是通过哈希函数来确定数据元素存放位置的一种特殊表结构。

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

typedef int KeyType;

typedef enum
{
    Empty,Active,Deleted
} KindOfItem;           //表项状态的枚举类型

typedef struct 
{
    KeyType key;
} DataType;

typedef struct
{
    DataType data;
    KindOfItem info;
} HashItem;                 //表项结构体

typedef struct 
{
    HashItem * ht;          //哈希表数组
    int tableSize;          //数组个数
    int currentSize;        //当前表项个数
} HashTable;                //哈希表结构体

//函数设计:哈希表的操作主要有初始化、查找、插入、删除和撤消操作
int Initiate(HashTable* hash,int mSize)     //初始化函数
{
    hash->tableSize = mSize;
    hash->ht = (HashItem*)malloc(sizeof(HashItem)*mSize);
    if(hash->ht == NULL) return 0;
    else
    {
        hash->currentSize = 0;
        return 1;
    }
}

//函数返回数据元素x的哈希地址。查找成功返回值>=0;查找失败返回值<0
int Find(HashTable* hash,DataType x)
{
    int i = x.key % hash->tableSize;
    int j = i;
    //存在冲突
    while(hash->ht[j].info == Active && hash->ht[j].data.key != x.key)
    {
        j = (j + 1) % hash->tableSize;      //用哈希冲突继续查找
        if(j == i)                          //说明已遍历整个哈希表未找到且表已满
            return -hash->tableSize;
    }

    if(hash->ht[j].info == Active) return j;        //找到,返回正值
    else
        return -j;                                  //未找到,返回负值
}

//插入函数
int Insert(HashTable* hash,DataType x)
{
    int i = Find(hash,x);           //调用Find()函数
    if(i > 0) return 0;             //数据元素x已存在
    else if(i != -hash->tableSize)
    {
        hash->ht[-i].data = x;      //数据元素赋值
        hash->ht[-i].info = Active; //置活动标记
        hash->currentSize++;        //当前表项个数加1
        return 1;                   //返回插入成功
    }
    else
        return 0;                   //返回插入失败
}

//删除函数
int Delete(HashTable* hash,DataType x)
{
    int i = Find(hash,x);               //调用Find函数
    if(i >= 0)                          //查找到
    {
        hash->ht[i].info = Deleted;     //置删除标记
        hash->currentSize--;            //当前表项个数减1
        return 1;                       //返回删除成功
    }
    else 
        return 0;                       //返回删除失败
}

//撤消函数
void Destroy(HashTable* hash)
{
    free(hash->ht);
}

void main(void)
{
    HashTable myHashTable;
    DataType a[] = {
        180,750,600,430,541,900,460
    }
    ,item = {430};
    int i,j,k,n = 7,m = 13;

    Initiate(&myHashTable,m);
    for(i = 0;i < n; i++)
        Insert(&myHashTable,a[i]);

    for(i = 0;i < n; i++)
    {
        j = Find(&myHashTable,a[i]);
        printf("j = %d ht[] = %d\n",j,myHashTable.ht[j].data.key);
    }

    k = Find(&myHashTable,item);
    if(k >= 0)
        printf("查找成功,元素%d的哈希地址为%d\n",item.key,k);
    else
        printf("查找失败\n");

    Delete(&myHashTable,item);

    k = Find(&myHashTable,item);
    if(k >= 0)
        printf("查找成功,元素%d的哈希地址为%d\n",item.key,k);
    else
        printf("查找失败\n");

    Destroy(&myHashTable);
}
/*
j = 11 ht[] = 180
j = 9 ht[] = 750
j = 2 ht[] = 600
j = 1 ht[] = 430
j = 8 ht[] = 541
j = 3 ht[] = 900
j = 5 ht[] = 460
查找成功,元素430的哈希地址为1
查找失败
Press any key to continue
*/
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值