[数据结构]查找相关算法

1.6 查找

1.6.1 顺序表的查找

#include<limits.h> /* INT_MAX等 */
#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include <cstdlib>

#define OK 1
#define ERROR 0
#define N 5 /* 数据元素个数 */
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */
typedef int KeyType; /* 设关键字域为整型 */
typedef struct /* 数据元素类型(以教科书图9.1高考成绩为例) */
{
    long number; /* 准考证号 */
    char name[10]; /* 姓名(4个汉字加1个串结束标志) */
    int politics; /* 政治 */
    int Chinese; /* 语文 */
    int English; /* 英语 */
    int math; /* 数学 */
    int physics; /* 物理 */
    int chemistry; /* 化学 */
    int biology; /* 生物 */
    KeyType key; /* 关键字类型应为KeyType,域名应为key,与bo9-1.c中一致 */
} ElemType;
ElemType r[N] = {{179324, "何芳芳", 85, 89, 98, 100, 93, 80, 47},
                 {179325, "陈红",  85, 86, 88, 100, 92, 90, 45},
                 {179326, "陆华",  78, 75, 90, 80,  95, 88, 37},
                 {179327, "张平",  82, 80, 78, 98,  84, 96, 40},
                 {179328, "赵小怡", 76, 85, 94, 57,  77, 69, 44}}; /* 全局变量 */
#define total key /* 定义总分(total)为关键字 */
#define EQ(a, b) ((a)==(b))
#define LT(a, b) ((a)<(b))
#define LQ(a, b) ((a)<=(b))
typedef struct {
    ElemType *elem; /* 数据元素存储空间基址,建表时按实际长度分配,0号单元留空 */
    int length; /* 表长度 */
} SSTable;

Status Creat_Seq(SSTable *ST, int n) { /* 操作结果: 构造一个含n个数据元素的静态顺序查找表ST(数据来自全局数组r) */
    int i;
    (*ST).elem = (ElemType *) calloc(n + 1, sizeof(ElemType)); /* 动态生成n个数据元素空间(0号单元不用) */
    if (!(*ST).elem)
        return ERROR;
    for (i = 1; i <= n; i++)
        *((*ST).elem + i) = r[i - 1]; /* 将全局数组r的值依次赋给ST */
    (*ST).length = n;
    return OK;
}

void Ascend(SSTable *ST) { /* 重建静态查找表为按关键字非降序排序 */
    int i, j, k;
    for (i = 1; i < (*ST).length; i++) {
        k = i;
        (*ST).elem[0] = (*ST).elem[i]; /* 待比较值存[0]单元 */
        for (j = i + 1; j <= (*ST).length; j++)
            if LT((*ST).elem[j].key, (*ST).elem[0].key) {
                k = j;
                (*ST).elem[0] = (*ST).elem[j];
            }
        if (k != i) /* 有更小的值则交换 */
        {
            (*ST).elem[k] = (*ST).elem[i];
            (*ST).elem[i] = (*ST).elem[0];
        }
    }
}

Status Creat_Ord(SSTable *ST, int n) { /* 操作结果: 构造一个含n个数据元素的静态按关键字非降序查找表ST */
    /* 数据来自全局数组r */
    Status f;
    f = Creat_Seq(ST, n);
    if (f)
        Ascend(ST);
    return f;
}

Status Destroy(SSTable *ST) { /* 初始条件: 静态查找表ST存在。操作结果: 销毁表ST */
    free((*ST).elem);
    (*ST).elem = NULL;
    (*ST).length = 0;
    return OK;
}

int Search_Seq(SSTable ST, KeyType key) { /* 在顺序表ST中顺序查找其关键字等于key的数据元素。若找到,则函数值为 */
    /* 该元素在表中的位置,否则为0。*/
    int i;
    ST.elem[0].key = key; /* 哨兵 */
    for (i = ST.length; !EQ(ST.elem[i].key, key); --i); /* 从后往前找 */
    return i; /* 找不到时,i为0 */
}

int Search_Bin(SSTable ST, KeyType key) { /* 在有序表ST中折半查找其关键字等于key的数据元素。若找到,则函数值为 */
    /* 该元素在表中的位置,否则为0。*/
    int low, high, mid;
    low = 1; /* 置区间初值 */
    high = ST.length;
    while (low <= high) {
        mid = (low + high) / 2;
        if EQ(key, ST.elem[mid].key)  /* 找到待查元素 */
            return mid;
        else if LT(key, ST.elem[mid].key)
            high = mid - 1; /* 继续在前半区间进行查找 */
        else
            low = mid + 1; /* 继续在后半区间进行查找 */
    }
    return 0; /* 顺序表中不存在待查元素 */
}

Status Traverse(SSTable ST, void(*Visit)(ElemType)) { /* 初始条件: 静态查找表ST存在,Visit()是对元素操作的应用函数 */
    /* 操作结果: 按顺序对ST的每个元素调用函数Visit()一次且仅一次。 */
    /* 一旦Visit()失败,则操作失败 */
    ElemType *p;
    int i;
    p = ++ST.elem; /* p指向第一个元素 */
    for (i = 1; i <= ST.length; i++)
        Visit(*p++);
    return OK;
}

void print(ElemType c) /* Traverse()调用的函数 */
{
    printf("%-8ld%-8s%4d%5d%5d%5d%5d%5d%5d%5d\n", c.number, c.name, c.politics, c.Chinese, c.English, c.math, c.physics,
           c.chemistry, c.biology, c.total);
}

int main() {
    SSTable st;
    int i, s;
    for (i = 0; i < N; i++) /* 计算总分 */
        r[i].total =
                r[i].politics + r[i].Chinese + r[i].English + r[i].math + r[i].physics + r[i].chemistry + r[i].biology;
    Creat_Seq(&st, N); /* 由全局数组产生静态查找表st */
    printf("准考证号  姓名  政治 语文 外语 数学 物理 化学 生物 总分\n");
    Traverse(st, print); /* 按顺序输出静态查找表st */
    printf("请输入待查找人的总分: ");
    scanf("%d", &s);
    i = Search_Seq(st, s); /* 顺序查找 */
    if (i)
        print(*(st.elem + i));
    else
        printf("没找到\n");
    Destroy(&st);
    return 0;
}

运行结果

1.6.2 静态树表的查找

#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include <cstdlib>
#include <math.h>

#define FALSE 0
#define OK 1
#define ERROR 0
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
#define N 9 /* 数据元素个数 */
typedef char KeyType; /* 设关键字域为字符型 */
typedef struct /* 数据元素类型 */
{
    KeyType key;
    int weight;
} ElemType;
ElemType r[N] = {{'A', 1},
                 {'B', 1},
                 {'C', 2},
                 {'D', 5},
                 {'E', 3},
                 {'F', 4},
                 {'G', 4},
                 {'H', 3},
                 {'I', 5}}; /* 数据元素(以教科书例9-1为例),全局变量 */
int sw[N + 1]; /* 累计权值,全局变量 */
#define EQ(a, b) ((a)==(b))
#define LT(a, b) ((a)<(b))
typedef struct {
    ElemType *elem; /* 数据元素存储空间基址,建表时按实际长度分配,0号单元留空 */
    int length; /* 表长度 */
} SSTable;

Status Creat_Seq(SSTable *ST, int n) { /* 操作结果: 构造一个含n个数据元素的静态顺序查找表ST(数据来自全局数组r) */
    int i;
    (*ST).elem = (ElemType *) calloc(n + 1, sizeof(ElemType)); /* 动态生成n个数据元素空间(0号单元不用) */
    if (!(*ST).elem)
        return ERROR;
    for (i = 1; i <= n; i++)
        *((*ST).elem + i) = r[i - 1]; /* 将全局数组r的值依次赋给ST */
    (*ST).length = n;
    return OK;
}

void Ascend(SSTable *ST) { /* 重建静态查找表为按关键字非降序排序 */
    int i, j, k;
    for (i = 1; i < (*ST).length; i++) {
        k = i;
        (*ST).elem[0] = (*ST).elem[i]; /* 待比较值存[0]单元 */
        for (j = i + 1; j <= (*ST).length; j++)
            if LT((*ST).elem[j].key, (*ST).elem[0].key) {
                k = j;
                (*ST).elem[0] = (*ST).elem[j];
            }
        if (k != i) /* 有更小的值则交换 */
        {
            (*ST).elem[k] = (*ST).elem[i];
            (*ST).elem[i] = (*ST).elem[0];
        }
    }
}

Status Creat_Ord(SSTable *ST, int n) { /* 操作结果: 构造一个含n个数据元素的静态按关键字非降序查找表ST */
    /* 数据来自全局数组r */
    Status f;
    f = Creat_Seq(ST, n);
    if (f)
        Ascend(ST);
    return f;
}

Status Traverse(SSTable ST, void(*Visit)(ElemType)) { /* 初始条件: 静态查找表ST存在,Visit()是对元素操作的应用函数 */
    /* 操作结果: 按顺序对ST的每个元素调用函数Visit()一次且仅一次。 */
    /* 一旦Visit()失败,则操作失败 */
    ElemType *p;
    int i;
    p = ++ST.elem; /* p指向第一个元素 */
    for (i = 1; i <= ST.length; i++)
        Visit(*p++);
    return OK;
}

typedef ElemType TElemType;
typedef struct BiTNode {
    TElemType data;
    struct BiTNode *lchild, *rchild; /* 左右孩子指针 */
} BiTNode, *BiTree;

Status
SecondOptimal(BiTree *T, ElemType R[], int sw[], int low, int high) { /* 由有序表R[low..high]及其累计权值表sw(其中sw[0]==0)递归构造 */
    /* 次优查找树T。*/
    int i, j;
    double min, dw;
    i = low;
    min = fabs(sw[high] - sw[low]);
    dw = sw[high] + sw[low - 1];
    for (j = low + 1; j <= high; ++j) /* 选择最小的△Pi值 */
        if (fabs(dw - sw[j] - sw[j - 1]) < min) {
            i = j;
            min = fabs(dw - sw[j] - sw[j - 1]);
        }
    *T = (BiTree) malloc(sizeof(BiTNode));
    if (!*T)
        return ERROR;
    (*T)->data = R[i]; /* 生成结点 */
    if (i == low)
        (*T)->lchild = NULL; /* 左子树空 */
    else
        SecondOptimal(&(*T)->lchild, R, sw, low, i - 1); /* 构造左子树 */
    if (i == high)
        (*T)->rchild = NULL; /* 右子树空 */
    else
        SecondOptimal(&(*T)->rchild, R, sw, i + 1, high); /* 构造右子树 */
    return OK;
}

void FindSW(int sw[], SSTable ST) { /* 按照有序表ST中各数据元素的Weight域求累计权值表sw */
    int i;
    sw[0] = 0;
    for (i = 1; i <= ST.length; i++)
        sw[i] = sw[i - 1] + ST.elem[i].weight;
}

typedef BiTree SOSTree; /* 次优查找树采用二叉链表的存储结构 */
Status CreateSOSTree(SOSTree *T, SSTable ST) { /* 由有序表ST构造一棵次优查找树T。ST的数据元素含有权域weight。*/
    if (ST.length == 0)
        *T = NULL;
    else {
        FindSW(sw, ST); /* 按照有序表ST中各数据元素的Weight域求累计权值表sw */
        SecondOptimal(T, ST.elem, sw, 1, ST.length);
    }
    return OK;
}

Status Search_SOSTree(SOSTree *T, KeyType key) { /* 在次优查找树T中查找关键字等于key的元素。找到则返回OK,否则返回FALSE */
    while (*T) /* T非空 */
        if ((*T)->data.key == key)
            return OK;
        else if ((*T)->data.key > key)
            *T = (*T)->lchild;
        else
            *T = (*T)->rchild;
    return FALSE; /* 顺序表中不存在待查元素 */
}

void print(ElemType c) /* Traverse()调用的函数 */
{
    printf("(%c %d) ", c.key, c.weight);
}

int main() {
    SSTable st;
    SOSTree t;
    Status i;
    KeyType s;
    Creat_Ord(&st, N); /* 由全局数组产生非降序静态查找表st */
    Traverse(st, print);
    CreateSOSTree(&t, st); /* 由有序表构造一棵次优查找树 */
    printf("\n请输入待查找的字符: ");
    scanf("%c", &s);
    i = Search_SOSTree(&t, s);
    if (i)
        printf("%c的权值是%d\n", s, t->data.weight);
    else
        printf("表中不存在此字符\n");
    return 0;
}

运行结果

1.6.3 二叉排序树的基本操作

#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include <cstdlib>

#define TRUE 1
#define FALSE 0
#define OK 1
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */
#define N 10 /* 数据元素个数 */
typedef int KeyType; /* 设关键字域为整型 */
typedef struct {
    KeyType key;
    int others;
} ElemType; /* 数据元素类型 */
typedef ElemType TElemType;
typedef struct BiTNode {
    TElemType data;
    struct BiTNode *lchild, *rchild; /* 左右孩子指针 */
} BiTNode, *BiTree;
#define EQ(a, b) ((a)==(b))
#define LT(a, b) ((a)<(b))

Status InitDSTable(BiTree *DT) /* 同bo6-2.c */
{ /* 操作结果: 构造一个空的动态查找表DT */
    *DT = NULL;
    return OK;
}

void DestroyDSTable(BiTree *DT) /* 同bo6-2.c */
{ /* 初始条件: 动态查找表DT存在。操作结果: 销毁动态查找表DT */
    if (*DT) /* 非空树 */
    {
        if ((*DT)->lchild) /* 有左孩子 */
            DestroyDSTable(&(*DT)->lchild); /* 销毁左孩子子树 */
        if ((*DT)->rchild) /* 有右孩子 */
            DestroyDSTable(&(*DT)->rchild); /* 销毁右孩子子树 */
        free(*DT); /* 释放根结点 */
        *DT = NULL; /* 空指针赋0 */
    }
}

BiTree SearchBST(BiTree T, KeyType key) { /* 在根指针T所指二叉排序树中递归地查找某关键字等于key的数据元素, */
    /* 若查找成功,则返回指向该数据元素结点的指针,否则返回空指针。*/
    if ((!T) || EQ(key, T->data.key))
        return T; /* 查找结束 */
    else if LT(key, T->data.key) /* 在左子树中继续查找 */
        return SearchBST(T->lchild, key);
    else
        return SearchBST(T->rchild, key); /* 在右子树中继续查找 */
}

void SearchBST1(BiTree *T, KeyType key, BiTree f, BiTree *p, Status *flag) { /* 在根指针T所指二叉排序树中递归地查找其关键字等于key的数据元素,若查找 */
    /* 成功,则指针p指向该数据元素结点,并返回TRUE,否则指针p指向查找路径上 */
    /* 访问的最后一个结点并返回FALSE,指针f指向T的双亲,其初始调用值为NULL */
    if (!*T) /* 查找不成功 */
    {
        *p = f;
        *flag = FALSE;
    } else if EQ(key, (*T)->data.key) /*  查找成功 */
    {
        *p = *T;
        *flag = TRUE;
    } else if LT(key, (*T)->data.key)
        SearchBST1(&(*T)->lchild, key, *T, p, flag); /* 在左子树中继续查找 */
    else
        SearchBST1(&(*T)->rchild, key, *T, p, flag); /*  在右子树中继续查找 */
}

Status InsertBST(BiTree *T, ElemType e) { /* 当二叉排序树T中不存在关键字等于e.key的数据元素时,插入e并返回TRUE, */
    /* 否则返回FALSE。*/
    BiTree p, s;
    Status flag;
    SearchBST1(T, e.key, NULL, &p, &flag);
    if (!flag) /* 查找不成功 */
    {
        s = (BiTree) malloc(sizeof(BiTNode));
        s->data = e;
        s->lchild = s->rchild = NULL;
        if (!p)
            *T = s; /* 被插结点*s为新的根结点 */
        else if LT(e.key, p->data.key)
            p->lchild = s; /* 被插结点*s为左孩子 */
        else
            p->rchild = s; /* 被插结点*s为右孩子 */
        return TRUE;
    } else
        return FALSE; /* 树中已有关键字相同的结点,不再插入 */
}

void Delete(BiTree *p) { /* 从二叉排序树中删除结点p,并重接它的左或右子树。*/
    BiTree q, s;
    if (!(*p)->rchild) /* 右子树空则只需重接它的左子树(待删结点是叶子也走此分支) */
    {
        q = *p;
        *p = (*p)->lchild;
        free(q);
    } else if (!(*p)->lchild) /* 只需重接它的右子树 */
    {
        q = *p;
        *p = (*p)->rchild;
        free(q);
    } else /* 左右子树均不空 */
    {
        q = *p;
        s = (*p)->lchild;
        while (s->rchild) /* 转左,然后向右到尽头(找待删结点的前驱) */
        {
            q = s;
            s = s->rchild;
        }
        (*p)->data = s->data; /* s指向被删结点的"前驱"(将被删结点前驱的值取代被删结点的值) */
        if (q != *p)
            q->rchild = s->lchild; /* 重接*q的右子树 */
        else
            q->lchild = s->lchild; /* 重接*q的左子树 */
        free(s);
    }
}

Status DeleteBST(BiTree *T, KeyType key) { /* 若二叉排序树T中存在关键字等于key的数据元素时,则删除该数据元素结点, */
    /* 并返回TRUE;否则返回FALSE。*/
    if (!*T) /* 不存在关键字等于key的数据元素 */
        return FALSE;
    else {
        if EQ(key, (*T)->data.key) /* 找到关键字等于key的数据元素 */
            Delete(T);
        else if LT(key, (*T)->data.key)
            DeleteBST(&(*T)->lchild, key);
        else
            DeleteBST(&(*T)->rchild, key);
        return TRUE;
    }
}

void TraverseDSTable(BiTree DT, void(*Visit)(ElemType)) { /* 初始条件: 动态查找表DT存在,Visit是对结点操作的应用函数 */
    /* 操作结果: 按关键字的顺序对DT的每个结点调用函数Visit()一次且至多一次 */
    if (DT) {
        TraverseDSTable(DT->lchild, Visit); /* 先中序遍历左子树 */
        Visit(DT->data); /* 再访问根结点 */
        TraverseDSTable(DT->rchild, Visit); /* 最后中序遍历右子树 */
    }
}

void print(ElemType c) {
    printf("(%d,%d) ", c.key, c.others);
}

int main() {
    BiTree dt, p;
    int i;
    KeyType j;
    ElemType r[N] = {{45,  1},
                     {12,  2},
                     {53,  3},
                     {3,   4},
                     {37,  5},
                     {24,  6},
                     {100, 7},
                     {61,  8},
                     {90,  9},
                     {78,  10}};
    InitDSTable(&dt); /* 构造空表 */
    for (i = 0; i < N; i++)
        InsertBST(&dt, r[i]); /* 依次插入数据元素 */
    TraverseDSTable(dt, print);
    printf("\n请输入待查找的值: ");
    scanf("%d", &j);
    p = SearchBST(dt, j);
    if (p) {
        printf("表中存在此值。");
        DeleteBST(&dt, j);
        printf("删除此值后:\n");
        TraverseDSTable(dt, print);
        printf("\n");
    } else
        printf("表中不存在此值\n");
    DestroyDSTable(&dt);
    return 0;
}

运行结果

1.6.4 平衡二叉树的基本操作

#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include <cstdlib>

#define TRUE 1
#define FALSE 0
#define OK 1
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
#define N 5 /* 数据元素个数 */
typedef char KeyType; /* 设关键字域为字符型 */
typedef struct {
    KeyType key;
    int order;
} ElemType; /* 数据元素类型 */
#define EQ(a, b) ((a)==(b))
#define LT(a, b) ((a)<(b))
typedef struct BSTNode {
    ElemType data;
    int bf; /* 结点的平衡因子 */
    struct BSTNode *lchild, *rchild; /* 左、右孩子指针 */
} BSTNode, *BSTree;

Status InitDSTable(BSTree *DT) /* 同bo6-2.c */
{ /* 操作结果: 构造一个空的动态查找表DT */
    *DT = NULL;
    return OK;
}

void DestroyDSTable(BSTree *DT) /* 同bo6-2.c */
{ /* 初始条件: 动态查找表DT存在。操作结果: 销毁动态查找表DT */
    if (*DT) /* 非空树 */
    {
        if ((*DT)->lchild) /* 有左孩子 */
            DestroyDSTable(&(*DT)->lchild); /* 销毁左孩子子树 */
        if ((*DT)->rchild) /* 有右孩子 */
            DestroyDSTable(&(*DT)->rchild); /* 销毁右孩子子树 */
        free(*DT); /* 释放根结点 */
        *DT = NULL; /* 空指针赋0 */
    }
}

BSTree SearchBST(BSTree T, KeyType key) { /* 在根指针T所指二叉排序树中递归地查找某关键字等于key的数据元素, */
    /* 若查找成功,则返回指向该数据元素结点的指针,否则返回空指针。*/
    if ((!T) || EQ(key, T->data.key))
        return T; /* 查找结束 */
    else if LT(key, T->data.key) /* 在左子树中继续查找 */
        return SearchBST(T->lchild, key);
    else
        return SearchBST(T->rchild, key); /* 在右子树中继续查找 */
}

void R_Rotate(BSTree *p) { /* 对以*p为根的二叉排序树作右旋处理,处理之后p指向新的树根结点,即旋转 */
    /* 处理之前的左子树的根结点。*/
    BSTree lc;
    lc = (*p)->lchild; /* lc指向p的左子树根结点 */
    (*p)->lchild = lc->rchild; /* lc的右子树挂接为p的左子树 */
    lc->rchild = *p;
    *p = lc; /* p指向新的根结点 */
}

void L_Rotate(BSTree *p) { /* 对以*p为根的二叉排序树作左旋处理,处理之后p指向新的树根结点,即旋转 */
    /* 处理之前的右子树的根结点。*/
    BSTree rc;
    rc = (*p)->rchild; /* rc指向p的右子树根结点 */
    (*p)->rchild = rc->lchild; /* rc的左子树挂接为p的右子树 */
    rc->lchild = *p;
    *p = rc; /* p指向新的根结点 */
}

#define LH +1 /* 左高 */
#define EH 0  /* 等高 */
#define RH -1 /* 右高 */

void LeftBalance(BSTree *T) { /* 对以指针T所指结点为根的二叉树作左平衡旋转处理,本算法结束时, */
    /* 指针T指向新的根结点。*/
    BSTree lc, rd;
    lc = (*T)->lchild; /* lc指向*T的左子树根结点 */
    switch (lc->bf) { /* 检查*T的左子树的平衡度,并作相应平衡处理 */
        case LH: /* 新结点插入在*T的左孩子的左子树上,要作单右旋处理 */
            (*T)->bf = lc->bf = EH;
            R_Rotate(T);
            break;
        case RH: /* 新结点插入在*T的左孩子的右子树上,要作双旋处理 */
            rd = lc->rchild; /* rd指向*T的左孩子的右子树根 */
            switch (rd->bf) { /* 修改*T及其左孩子的平衡因子 */
                case LH:
                    (*T)->bf = RH;
                    lc->bf = EH;
                    break;
                case EH:
                    (*T)->bf = lc->bf = EH;
                    break;
                case RH:
                    (*T)->bf = EH;
                    lc->bf = LH;
            }
            rd->bf = EH;
            L_Rotate(&(*T)->lchild); /* 对*T的左子树作左旋平衡处理 */
            R_Rotate(T); /* 对*T作右旋平衡处理 */
    }
}

void RightBalance(BSTree *T) { /* 对以指针T所指结点为根的二叉树作右平衡旋转处理,本算法结束时, */
    /* 指针T指向新的根结点 */
    BSTree rc, rd;
    rc = (*T)->rchild; /* rc指向*T的右子树根结点 */
    switch (rc->bf) { /* 检查*T的右子树的平衡度,并作相应平衡处理 */
        case RH: /* 新结点插入在*T的右孩子的右子树上,要作单左旋处理 */
            (*T)->bf = rc->bf = EH;
            L_Rotate(T);
            break;
        case LH: /* 新结点插入在*T的右孩子的左子树上,要作双旋处理 */
            rd = rc->lchild; /* rd指向*T的右孩子的左子树根 */
            switch (rd->bf) { /* 修改*T及其右孩子的平衡因子 */
                case RH:
                    (*T)->bf = LH;
                    rc->bf = EH;
                    break;
                case EH:
                    (*T)->bf = rc->bf = EH;
                    break;
                case LH:
                    (*T)->bf = EH;
                    rc->bf = RH;
            }
            rd->bf = EH;
            R_Rotate(&(*T)->rchild); /* 对*T的右子树作右旋平衡处理 */
            L_Rotate(T); /* 对*T作左旋平衡处理 */
    }
}

Status InsertAVL(BSTree *T, ElemType e, Status *taller) { /* 若在平衡的二叉排序树T中不存在和e有相同关键字的结点,则插入一个 */
    /* 数据元素为e的新结点,并返回1,否则返回0。若因插入而使二叉排序树 */
    /* 失去平衡,则作平衡旋转处理,布尔变量taller反映T长高与否。*/
    if (!*T) { /* 插入新结点,树"长高",置taller为TRUE */
        *T = (BSTree) malloc(sizeof(BSTNode));
        (*T)->data = e;
        (*T)->lchild = (*T)->rchild = NULL;
        (*T)->bf = EH;
        *taller = TRUE;
    } else {
        if EQ(e.key, (*T)->data.key) { /* 树中已存在和e有相同关键字的结点则不再插入 */
            *taller = FALSE;
            return FALSE;
        }
        if LT(e.key, (*T)->data.key) { /* 应继续在*T的左子树中进行搜索 */
            if (!InsertAVL(&(*T)->lchild, e, taller)) /* 未插入 */
                return FALSE;
            if (*taller) /*  已插入到*T的左子树中且左子树"长高" */
                switch ((*T)->bf) /* 检查*T的平衡度 */
                {
                    case LH: /* 原本左子树比右子树高,需要作左平衡处理 */
                        LeftBalance(T);
                        *taller = FALSE;
                        break;
                    case EH: /* 原本左、右子树等高,现因左子树增高而使树增高 */
                        (*T)->bf = LH;
                        *taller = TRUE;
                        break;
                    case RH:
                        (*T)->bf = EH; /* 原本右子树比左子树高,现左、右子树等高 */
                        *taller = FALSE;
                }
        } else { /* 应继续在*T的右子树中进行搜索 */
            if (!InsertAVL(&(*T)->rchild, e, taller)) /* 未插入 */
                return FALSE;
            if (*taller) /* 已插入到T的右子树且右子树"长高" */
                switch ((*T)->bf) /* 检查T的平衡度 */
                {
                    case LH:
                        (*T)->bf = EH; /* 原本左子树比右子树高,现左、右子树等高 */
                        *taller = FALSE;
                        break;
                    case EH: /* 原本左、右子树等高,现因右子树增高而使树增高 */
                        (*T)->bf = RH;
                        *taller = TRUE;
                        break;
                    case RH: /* 原本右子树比左子树高,需要作右平衡处理 */
                        RightBalance(T);
                        *taller = FALSE;
                }
        }
    }
    return TRUE;
}

void TraverseDSTable(BSTree DT, void(*Visit)(ElemType)) { /* 初始条件: 动态查找表DT存在,Visit是对结点操作的应用函数 */
    /* 操作结果: 按关键字的顺序对DT的每个结点调用函数Visit()一次且至多一次 */
    if (DT) {
        TraverseDSTable(DT->lchild, Visit); /* 先中序遍历左子树 */
        Visit(DT->data); /* 再访问根结点 */
        TraverseDSTable(DT->rchild, Visit); /* 最后中序遍历右子树 */
    }
}

void print(ElemType c) {
    printf("(%d,%d)", c.key, c.order);
}

int main() {
    BSTree dt, p;
    Status k;
    int i;
    KeyType j;
    ElemType r[N] = {{13, 1},
                     {24, 2},
                     {37, 3},
                     {90, 4},
                     {53, 5}}; /* (以教科书图9.12为例) */
    InitDSTable(&dt); /* 初始化空树 */
    for (i = 0; i < N; i++)
        InsertAVL(&dt, r[i], &k); /* 建平衡二叉树 */
    TraverseDSTable(dt, print); /* 按关键字顺序遍历二叉树 */
    printf("\n请输入待查找的关键字: ");
    scanf("%d", &j);
    p = SearchBST(dt, j); /* 查找给定关键字的记录 */
    if (p)
        print(p->data);
    else
        printf("表中不存在此值");
    printf("\n");
    DestroyDSTable(&dt);
    return 0;
}

运行结果

1.6.5 B树的基本操作

#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include <cstdlib>
/* 函数结果状态代码 */
#define TRUE 1
#define FALSE 0
#define OK 1
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
#define m 3 /* B树的阶,暂设为3 */
#define N 16 /* 数据元素个数 */
#define MAX 5 /* 字符串最大长度+1 */
typedef int KeyType; /* 设关键字域为整型 */
typedef struct {
    char info[MAX];
} Others;/* 记录的其它部分 */
typedef struct {
    KeyType key; /* 关键字 */
    Others others; /* 其它部分(由主程定义) */
} Record; /* 记录类型 */

typedef struct BTNode {
    int keynum; /* 结点中关键字个数,即结点的大小 */
    struct BTNode *parent; /* 指向双亲结点 */
    struct Node /* 结点向量类型 */
    {
        KeyType key; /* 关键字向量 */
        struct BTNode *ptr; /* 子树指针向量 */
        Record *recptr; /* 记录指针向量 */
    } node[m + 1]; /* key,recptr的0号单元未用 */
} BTNode, *BTree; /* B树结点和B树的类型 */

typedef struct {
    BTNode *pt; /* 指向找到的结点 */
    int i; /* 1..m,在结点中的关键字序号 */
    int tag; /* 1:查找成功,O:查找失败 */
} Result; /* B树的查找结果类型 */
Status InitDSTable(BTree *DT) { /* 操作结果: 构造一个空的动态查找表DT */
    *DT = NULL;
    return OK;
}

void DestroyDSTable(BTree *DT) { /* 初始条件: 动态查找表DT存在。操作结果: 销毁动态查找表DT */
    int i;
    if (*DT) /* 非空树 */
    {
        for (i = 0; i <= (*DT)->keynum; i++)
            DestroyDSTable(&(*DT)->node[i].ptr); /* 依次销毁第i棵子树 */
        free(*DT); /* 释放根结点 */
        *DT = NULL; /* 空指针赋0 */
    }
}

int Search(BTree p, KeyType K) { /* 在p->node[1..keynum].key中查找i,使得p->node[i].key≤K<p->node[i+1].key */
    int i = 0, j;
    for (j = 1; j <= p->keynum; j++)
        if (p->node[j].key <= K)
            i = j;
    return i;
}

Result SearchBTree(BTree T, KeyType K) { /* 在m阶B树T上查找关键字K,返回结果(pt,i,tag)。若查找成功,则特征值 */
    /* tag=1,指针pt所指结点中第i个关键字等于K;否则特征值tag=0,等于K的 */
    /* 关键字应插入在指针Pt所指结点中第i和第i+1个关键字之间。*/
    BTree p = T, q = NULL; /* 初始化,p指向待查结点,q指向p的双亲 */
    Status found = FALSE;
    int i = 0;
    Result r;
    while (p && !found) {
        i = Search(p, K); /* p->node[i].key≤K<p->node[i+1].key */
        if (i > 0 && p->node[i].key == K) /* 找到待查关键字 */
            found = TRUE;
        else {
            q = p;
            p = p->node[i].ptr;
        }
    }
    r.i = i;
    if (found) /* 查找成功 */
    {
        r.pt = p;
        r.tag = 1;
    } else /*  查找不成功,返回K的插入位置信息 */
    {
        r.pt = q;
        r.tag = 0;
    }
    return r;
}

void Insert(BTree *q, int i, Record *r, BTree ap) { /* 将r->key、r和ap分别插入到q->key[i+1]、q->recptr[i+1]和q->ptr[i+1]中 */
    int j;
    for (j = (*q)->keynum; j > i; j--) /* 空出q->node[i+1] */
        (*q)->node[j + 1] = (*q)->node[j];
    (*q)->node[i + 1].key = r->key;
    (*q)->node[i + 1].ptr = ap;
    (*q)->node[i + 1].recptr = r;
    (*q)->keynum++;
}

void split(BTree *q, BTree *ap) { /* 将结点q分裂成两个结点,前一半保留,后一半移入新生结点ap */
    int i, s = (m + 1) / 2;
    *ap = (BTree) malloc(sizeof(BTNode)); /* 生成新结点ap */
    (*ap)->node[0].ptr = (*q)->node[s].ptr; /* 后一半移入ap */
    for (i = s + 1; i <= m; i++) {
        (*ap)->node[i - s] = (*q)->node[i];
        if ((*ap)->node[i - s].ptr)
            (*ap)->node[i - s].ptr->parent = *ap;
    }
    (*ap)->keynum = m - s;
    (*ap)->parent = (*q)->parent;
    (*q)->keynum = s - 1; /* q的前一半保留,修改keynum */
}

void NewRoot(BTree *T, Record *r, BTree ap) { /* 生成含信息(T,r,ap)的新的根结点*T,原T和ap为子树指针 */
    BTree p;
    p = (BTree) malloc(sizeof(BTNode));
    p->node[0].ptr = *T;
    *T = p;
    if ((*T)->node[0].ptr)
        (*T)->node[0].ptr->parent = *T;
    (*T)->parent = NULL;
    (*T)->keynum = 1;
    (*T)->node[1].key = r->key;
    (*T)->node[1].recptr = r;
    (*T)->node[1].ptr = ap;
    if ((*T)->node[1].ptr)
        (*T)->node[1].ptr->parent = *T;
}

void InsertBTree(BTree *T, Record *r, BTree q, int i) { /* 在m阶B树T上结点*q的key[i]与key[i+1]之间插入关键字K的指针r。若引起 */
    /* 结点过大,则沿双亲链进行必要的结点分裂调整,使T仍是m阶B树。*/
    BTree ap = NULL;
    Status finished = FALSE;
    int s;
    Record *rx;
    rx = r;
    while (q && !finished) {
        Insert(&q, i, rx, ap); /* 将r->key、r和ap分别插入到q->key[i+1]、q->recptr[i+1]和q->ptr[i+1]中 */
        if (q->keynum < m)
            finished = TRUE; /* 插入完成 */
        else { /* 分裂结点*q */
            s = (m + 1) / 2;
            rx = q->node[s].recptr;
            split(&q, &ap); /* 将q->key[s+1..m],q->ptr[s..m]和q->recptr[s+1..m]移入新结点*ap */
            q = q->parent;
            if (q)
                i = Search(q, rx->key); /* 在双亲结点*q中查找rx->key的插入位置 */
        }
    }
    if (!finished) /* T是空树(参数q初值为NULL)或根结点已分裂为结点*q和*ap */
        NewRoot(T, rx, ap); /* 生成含信息(T,rx,ap)的新的根结点*T,原T和ap为子树指针 */
}

void TraverseDSTable(BTree DT, void(*Visit)(BTNode, int)) { /* 初始条件: 动态查找表DT存在,Visit是对结点操作的应用函数 */
    /* 操作结果: 按关键字的顺序对DT的每个结点调用函数Visit()一次且至多一次 */
    int i;
    if (DT) /* 非空树 */
    {
        if (DT->node[0].ptr) /* 有第0棵子树 */
            TraverseDSTable(DT->node[0].ptr, Visit);
        for (i = 1; i <= DT->keynum; i++) {
            Visit(*DT, i);
            if (DT->node[i].ptr) /* 有第i棵子树 */
                TraverseDSTable(DT->node[i].ptr, Visit);
        }
    }
}

void print(BTNode c, int i) /* TraverseDSTable()调用的函数 */
{
    printf("(%d,%s)", c.node[i].key, c.node[i].recptr->others.info);
}

int main() {
    Record r[N] = {{24,  "1"},
                   {45,  "2"},
                   {53,  "3"},
                   {12,  "4"},
                   {37,  "5"},
                   {50,  "6"},
                   {61,  "7"},
                   {90,  "8"},
                   {100, "9"},
                   {70,  "10"},
                   {3,   "11"},
                   {30,  "12"},
                   {26,  "13"},
                   {85,  "14"},
                   {3,   "15"},
                   {7,   "16"}}; /* (以教科书中图9.16为例) */
    BTree t;
    Result s;
    int i;
    InitDSTable(&t);
    for (i = 0; i < N; i++) {
        s = SearchBTree(t, r[i].key);
        if (!s.tag)
            InsertBTree(&t, &r[i], s.pt, s.i);
    }
    printf("按关键字的顺序遍历B_树:\n");
    TraverseDSTable(t, print);
    printf("\n请输入待查找记录的关键字: ");
    scanf("%d", &i);
    s = SearchBTree(t, i);
    if (s.tag)
        print(*(s.pt), s.i);
    else
        printf("没找到");
    printf("\n");
    DestroyDSTable(&t);
    return 0;
}

1.6.6 按关键字符串的遍历双链键树

#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include<math.h> /* floor(),ceil(),abs() */
#include <cstring>
/* 函数结果状态代码 */
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
#define N 16 /* 数据元素个数 */
#define OVERFLOW 3
typedef struct {
    int ord;
} Others; /* 记录的其它部分 */
#define Nil ' ' /* 定义结束符为空格(与教科书不同) */
#define MAXKEYLEN 16 /* 关键字的最大长度 */
typedef struct {
    char ch[MAXKEYLEN]; /* 关键字 */
    int num; /* 关键字长度 */
} KeysType; /* 关键字类型 */
typedef struct {
    KeysType key; /* 关键字 */
    Others others; /* 其它部分(由主程定义) */
} Record; /* 记录类型 */
typedef enum {
    LEAF, BRANCH
} NodeKind; /* 结点种类:{叶子,分支} */
typedef struct DLTNode /* 双链树类型 */
{
    char symbol;
    struct DLTNode *next; /* 指向兄弟结点的指针 */
    NodeKind kind;
    union {
        Record *infoptr; /* 叶子结点的记录指针 */
        struct DLTNode *first; /* 分支结点的孩子链指针 */
    } a;
} DLTNode, *DLTree;

Status InitDSTable(DLTree *DT) { /* 操作结果: 构造一个空的双链键树DT */
    *DT = NULL;
    return OK;
}

void DestroyDSTable(DLTree *DT) { /* 初始条件: 双链键树DT存在。操作结果: 销毁双链键树DT */
    if (*DT) /* 非空树 */
    {
        if ((*DT)->kind == BRANCH && (*DT)->a.first) /* *DT是分支结点且有孩子 */
            DestroyDSTable(&(*DT)->a.first); /* 销毁孩子子树 */
        if ((*DT)->next) /* 有兄弟 */
            DestroyDSTable(&(*DT)->next); /* 销毁兄弟子树 */
        free(*DT); /* 释放根结点 */
        *DT = NULL; /* 空指针赋0 */
    }
}

Record *SearchDLTree(DLTree T, KeysType K) { /* 在非空双链键树T中查找关键字等于K的记录,若存在, */
    /* 则返回指向该记录的指针,否则返回空指针。*/
    DLTree p;
    int i;
    if (T) {
        p = T; /* 初始化 */
        i = 0;
        while (p && i < K.num) {
            while (p && p->symbol != K.ch[i]) /* 查找关键字的第i位 */
                p = p->next;
            if (p && i < K.num) /* 准备查找下一位 */
                p = p->a.first;
            ++i;
        } /* 查找结束 */
        if (!p) /* 查找不成功 */
            return NULL;
        else /* 查找成功 */
            return p->a.infoptr;
    } else
        return NULL; /* 树空 */
}

void InsertDSTable(DLTree *DT, Record *r) { /* 初始条件: 双链键树DT存在,r为待插入的数据元素的指针 */
    /* 操作结果: 若DT中不存在其关键字等于(*r).key.ch的数据元素, */
    /*           则按关键字顺序插r到DT中 */
    DLTree p = NULL, q, ap;
    int i = 0;
    KeysType K = r->key;
    if (!*DT && K.num) /* 空树且关键字符串非空 */
    {
        *DT = ap = (DLTree) malloc(sizeof(DLTNode));
        for (; i < K.num; i++) /* 插入分支结点 */
        {
            if (p)
                p->a.first = ap;
            ap->next = NULL;
            ap->symbol = K.ch[i];
            ap->kind = BRANCH;
            p = ap;
            ap = (DLTree) malloc(sizeof(DLTNode));
        }
        p->a.first = ap; /* 插入叶子结点 */
        ap->next = NULL;
        ap->symbol = Nil;
        ap->kind = LEAF;
        ap->a.infoptr = r;
    } else /* 非空树 */
    {
        p = *DT; /* 指向根结点 */
        while (p && i < K.num) {
            while (p && p->symbol < K.ch[i]) /* 沿兄弟结点查找 */
            {
                q = p;
                p = p->next;
            }
            if (p && p->symbol == K.ch[i]) /* 找到与K.ch[i]相符的结点 */
            {
                q = p;
                p = p->a.first; /* p指向将与K.ch[i+1]比较的结点 */
                ++i;
            } else /* 没找到,插入关键字 */
            {
                ap = (DLTree) malloc(sizeof(DLTNode));
                if (q->a.first == p)
                    q->a.first = ap; /* 在长子的位置插入 */
                else /* q->next==p */
                    q->next = ap; /* 在兄弟的位置插入 */
                ap->next = p;
                ap->symbol = K.ch[i];
                ap->kind = BRANCH;
                p = ap;
                ap = (DLTree) malloc(sizeof(DLTNode));
                i++;
                for (; i < K.num; i++) /* 插入分支结点 */
                {
                    p->a.first = ap;
                    ap->next = NULL;
                    ap->symbol = K.ch[i];
                    ap->kind = BRANCH;
                    p = ap;
                    ap = (DLTree) malloc(sizeof(DLTNode));
                }
                p->a.first = ap; /* 插入叶子结点 */
                ap->next = NULL;
                ap->symbol = Nil;
                ap->kind = LEAF;
                ap->a.infoptr = r;
            }
        }
    }
}

typedef struct {
    char ch;
    DLTree p;
} SElemType; /* 定义栈元素类型 */
#define STACK_INIT_SIZE 10 /* 存储空间初始分配量 */
#define STACKINCREMENT 2 /* 存储空间分配增量 */
typedef struct SqStack {
    SElemType *base; /* 在栈构造之前和销毁之后,base的值为NULL */
    SElemType *top; /* 栈顶指针 */
    int stacksize; /* 当前已分配的存储空间,以元素为单位 */
} SqStack; /* 顺序栈 */
Status InitStack(SqStack *S) { /* 构造一个空栈S */
    (*S).base = (SElemType *) malloc(STACK_INIT_SIZE * sizeof(SElemType));
    if (!(*S).base)
        exit(OVERFLOW); /* 存储分配失败 */
    (*S).top = (*S).base;
    (*S).stacksize = STACK_INIT_SIZE;
    return OK;
}

Status StackEmpty(SqStack S) { /* 若栈S为空栈,则返回TRUE,否则返回FALSE */
    if (S.top == S.base)
        return TRUE;
    else
        return FALSE;
}

Status Push(SqStack *S, SElemType e) { /* 插入元素e为新的栈顶元素 */
    if ((*S).top - (*S).base >= (*S).stacksize) /* 栈满,追加存储空间 */
    {
        (*S).base = (SElemType *) realloc((*S).base, ((*S).stacksize + STACKINCREMENT) * sizeof(SElemType));
        if (!(*S).base)
            exit(OVERFLOW); /* 存储分配失败 */
        (*S).top = (*S).base + (*S).stacksize;
        (*S).stacksize += STACKINCREMENT;
    }
    *((*S).top)++ = e;
    return OK;
}

Status Pop(SqStack *S, SElemType *e) { /* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */
    if ((*S).top == (*S).base)
        return ERROR;
    *e = *--(*S).top;
    return OK;
}

void TraverseDSTable(DLTree DT, void(*Vi)(Record)) { /* 初始条件: 双链键树DT存在,Vi是对结点操作的应用函数, */
    /*           ViR是对记录操作的应用函数 */
    /* 操作结果: 按关键字的顺序输出关键字及其对应的记录 */
    SqStack s;
    SElemType e;
    DLTree p;
    int i = 0, n = 8;
    if (DT) {
        InitStack(&s);
        e.p = DT;
        e.ch = DT->symbol;
        Push(&s, e);
        p = DT->a.first;
        while (p->kind == BRANCH) /* 分支结点 */
        {
            e.p = p;
            e.ch = p->symbol;
            Push(&s, e);
            p = p->a.first;
        }
        e.p = p;
        e.ch = p->symbol;
        Push(&s, e);
        Vi(*(p->a.infoptr));
        i++;
        while (!StackEmpty(s)) {
            Pop(&s, &e);
            p = e.p;
            if (p->next) /* 有兄弟结点 */
            {
                p = p->next;
                while (p->kind == BRANCH) /* 分支结点 */
                {
                    e.p = p;
                    e.ch = p->symbol;
                    Push(&s, e);
                    p = p->a.first;
                }
                e.p = p;
                e.ch = p->symbol;
                Push(&s, e);
                Vi(*(p->a.infoptr));
                i++;
                if (i % n == 0)
                    printf("\n"); /* 输出n个元素后换行 */
            }
        }
    }
}

void print(Record e) {
    int i;
    printf("(");
    for (i = 0; i < e.key.num; i++)
        printf("%c", e.key.ch[i]);
    printf(",%d)", e.others.ord);
}

int main() {
    DLTree t;
    int i;
    char s[MAXKEYLEN + 1];
    KeysType k;
    Record *p;
    Record r[N] = {{{"CAI"},   1},
                   {{"CAO"},   2},
                   {{"LI"},    3},
                   {{"LAN"},   4},
                   {{"CHA"},   5},
                   {{"CHANG"}, 6},
                   {{"WEN"},   7},
                   {{"CHAO"},  8},
                   {{"YUN"},   9},
                   {{"YANG"},  10},
                   {{"LONG"},  11},
                   {{"WANG"},  12},
                   {{"ZHAO"},  13},
                   {{"LIU"},   14},
                   {{"WU"},    15},
                   {{"CHEN"},  16}};
    /* 数据元素(以教科书式9-24为例) */
    InitDSTable(&t);
    for (i = 0; i < N; i++) {
        r[i].key.num = strlen(r[i].key.ch);
        p = SearchDLTree(t, r[i].key);
        if (!p) /* t中不存在关键字为r[i].key的项 */
            InsertDSTable(&t, &r[i]);
    }
    printf("按关键字符串的顺序遍历双链键树:\n");
    TraverseDSTable(t, print);
    printf("\n请输入待查找记录的关键字符串: ");
    scanf("%s", s);
    k.num = strlen(s);
    strcpy(k.ch, s);
    p = SearchDLTree(t, k);
    if (p)
        print(*p);
    else
        printf("没找到");
    printf("\n");
    DestroyDSTable(&t);
    return 0;
}

运行结果

1.6.7 按关键字符串的遍历Trie树

#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include <cstring>
#include <cctype>
#include <cstdlib>

#define OK 1
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
#define N 16 /* 数据元素个数 */
#define LENGTH 27 /* 结点的最大度+1(大写英文字母) */
typedef struct {
    int ord;
} Others; /* 记录的其它部分 */
#define Nil ' ' /* 定义结束符为空格(与教科书不同) */
#define MAXKEYLEN 16 /* 关键字的最大长度,同c9-4.h */
typedef struct {
    char ch[MAXKEYLEN]; /* 关键字 */
    int num; /* 关键字长度 */
} KeysType;
typedef struct {
    KeysType key; /* 关键字 */
    Others others; /* 其它部分(由主程定义) */
} Record;
typedef enum {
    LEAF, BRANCH
} NodeKind;
typedef struct TrieNode /* Trie键树类型 */
{
    NodeKind kind;
    union {
        struct /* 叶子结点 */
        {
            KeysType K;
            Record *infoptr;
        } lf;
        struct /* 分支结点 */
        {
            struct TrieNode *ptr[LENGTH]; /* LENGTH为结点的最大度+1,在主程定义 */
            /*  int num; 改 */
        } bh;
    } a;
} TrieNode, *TrieTree;
#define EQ(a, b) (!strcmp((a),(b)))

Status InitDSTable(TrieTree *T) { /* 操作结果: 构造一个空的Trie键树T */
    *T = NULL;
    return OK;
}

void DestroyDSTable(TrieTree *T) { /* 初始条件: Trie树T存在。操作结果: 销毁Trie树T */
    int i;
    if (*T) /* 非空树 */
    {
        for (i = 0; i < LENGTH; i++)
            if ((*T)->kind == BRANCH && (*T)->a.bh.ptr[i]) /* 第i个结点不空 */
                if ((*T)->a.bh.ptr[i]->kind == BRANCH) /* 是子树 */
                    DestroyDSTable(&(*T)->a.bh.ptr[i]);
                else /* 是叶子 */
                {
                    free((*T)->a.bh.ptr[i]);
                    (*T)->a.bh.ptr[i] = NULL;
                }
        free(*T); /* 释放根结点 */
        *T = NULL; /* 空指针赋0 */
    }
}

int ord(char c) {
    c = toupper(c);
    if (c >= 'A' && c <= 'Z')
        return c - 'A' + 1; /* 英文字母返回其在字母表中的序号 */
    else
        return 0; /* 其余字符返回0 */
}

Record *SearchTrie(TrieTree T, KeysType K) { /* 在键树T中查找关键字等于K的记录。*/
    TrieTree p;
    int i;
    for (p = T, i = 0; p && p->kind == BRANCH && i < K.num; p = p->a.bh.ptr[ord(K.ch[i])], ++i);
    /* 对K的每个字符逐个查找,*p为分支结点,ord()求字符在字母表中序号 */
    if (p && p->kind == LEAF && p->a.lf.K.num == K.num && EQ(p->a.lf.K.ch, K.ch)) /* 查找成功 */
        return p->a.lf.infoptr;
    else /* 查找不成功 */
        return NULL;
}

void InsertTrie(TrieTree *T, Record *r) { /* 初始条件: Trie键树T存在,r为待插入的数据元素的指针 */
    /* 操作结果: 若T中不存在其关键字等于(*r).key.ch的数据元素, */
    /*           则按关键字顺序插r到T中 */
    TrieTree p, q, ap;
    int i = 0, j;
    KeysType K1, K = r->key;
    if (!*T) /* 空树 */
    {
        *T = (TrieTree) malloc(sizeof(TrieNode));
        (*T)->kind = BRANCH;
        for (i = 0; i < LENGTH; i++) /* 指针量赋初值NULL */
            (*T)->a.bh.ptr[i] = NULL;
        p = (*T)->a.bh.ptr[ord(K.ch[0])] = (TrieTree) malloc(sizeof(TrieNode));
        p->kind = LEAF;
        p->a.lf.K = K;
        p->a.lf.infoptr = r;
    } else /* 非空树 */
    {
        for (p = *T, i = 0; p && p->kind == BRANCH && i < K.num; ++i) {
            q = p;
            p = p->a.bh.ptr[ord(K.ch[i])];
        }
        i--;
        if (p && p->kind == LEAF && p->a.lf.K.num == K.num && EQ(p->a.lf.K.ch, K.ch)) /* T中存在该关键字 */
            return;
        else /* T中不存在该关键字,插入之 */
        {
            if (!p) /* 分支空 */
            {
                p = q->a.bh.ptr[ord(K.ch[i])] = (TrieTree) malloc(sizeof(TrieNode));
                p->kind = LEAF;
                p->a.lf.K = K;
                p->a.lf.infoptr = r;
            } else if (p->kind == LEAF) /* 有不完全相同的叶子 */
            {
                K1 = p->a.lf.K;
                do {
                    ap = q->a.bh.ptr[ord(K.ch[i])] = (TrieTree) malloc(sizeof(TrieNode));
                    ap->kind = BRANCH;
                    for (j = 0; j < LENGTH; j++) /* 指针量赋初值NULL */
                        ap->a.bh.ptr[j] = NULL;
                    q = ap;
                    i++;
                } while (ord(K.ch[i]) == ord(K1.ch[i]));
                q->a.bh.ptr[ord(K1.ch[i])] = p;
                p = q->a.bh.ptr[ord(K.ch[i])] = (TrieTree) malloc(sizeof(TrieNode));
                p->kind = LEAF;
                p->a.lf.K = K;
                p->a.lf.infoptr = r;
            }
        }
    }
}

void TraverseDSTable(TrieTree T, Status(*Vi)(Record *)) { /* 初始条件: Trie键树T存在,Vi是对记录指针操作的应用函数 */
    /* 操作结果: 按关键字的顺序输出关键字及其对应的记录 */
    TrieTree p;
    int i;
    if (T) {
        for (i = 0; i < LENGTH; i++) {
            p = T->a.bh.ptr[i];
            if (p && p->kind == LEAF)
                Vi(p->a.lf.infoptr);
            else if (p && p->kind == BRANCH)
                TraverseDSTable(p, Vi);
        }
    }
}

Status pr(Record *r) {
    printf("(%s,%d)", r->key.ch, r->others.ord);
    return OK;
}

int main() {
    TrieTree t;
    int i;
    char s[MAXKEYLEN + 1];
    KeysType k;
    Record *p;
    Record r[N] = {{{"CAI"},   1},
                   {{"CAO"},   2},
                   {{"LI"},    3},
                   {{"LAN"},   4},
                   {{"CHA"},   5},
                   {{"CHANG"}, 6},
                   {{"WEN"},   7},
                   {{"CHAO"},  8},
                   {{"YUN"},   9},
                   {{"YANG"},  10},
                   {{"LONG"},  11},
                   {{"WANG"},  12},
                   {{"ZHAO"},  13},
                   {{"LIU"},   14},
                   {{"WU"},    15},
                   {{"CHEN"},  16}};
    /* 数据元素(以教科书式9-24为例) */
    InitDSTable(&t);
    for (i = 0; i < N; i++) {
        r[i].key.num = strlen(r[i].key.ch) + 1;
        r[i].key.ch[r[i].key.num] = Nil; /* 在关键字符串最后加结束符 */
        p = SearchTrie(t, r[i].key);
        if (!p)
            InsertTrie(&t, &r[i]);
    }
    printf("按关键字符串的顺序遍历Trie树(键树):\n");
    TraverseDSTable(t, pr);
    printf("\n请输入待查找记录的关键字符串: ");
    scanf("%s", s);
    k.num = strlen(s) + 1;
    strcpy(k.ch, s);
    k.ch[k.num] = Nil; /* 在关键字符串最后加结束符 */
    p = SearchTrie(t, k);
    if (p)
        pr(p);
    else
        printf("没找到");
    printf("\n");
    DestroyDSTable(&t);
    return 0;
}

运行结果

1.6.8 哈希表的基本操作

#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include<math.h> /* floor(),ceil(),abs() */

#define OK 1
#define ERROR 0
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
#define NULLKEY 0 /* 0为无记录标志 */
#define N 10 /* 数据元素个数 */
#define OVERFLOW 3
typedef int KeyType; /* 设关键字域为整型 */
typedef struct {
    KeyType key;
    int ord;
} ElemType; /* 数据元素类型 */
#define EQ(a, b) ((a)==(b))
int hashsize[] = {11, 19, 29, 37}; /* 哈希表容量递增表,一个合适的素数序列 */
int m = 0; /* 哈希表表长,全局变量 */
typedef struct {
    ElemType *elem; /* 数据元素存储基址,动态分配数组 */
    int count; /* 当前数据元素个数 */
    int sizeindex; /* hashsize[sizeindex]为当前容量 */
} HashTable;
#define SUCCESS 1
#define UNSUCCESS 0
#define DUPLICATE -1

Status InitHashTable(HashTable *H) { /* 操作结果: 构造一个空的哈希表 */
    int i;
    (*H).count = 0; /* 当前元素个数为0 */
    (*H).sizeindex = 0; /* 初始存储容量为hashsize[0] */
    m = hashsize[0];
    (*H).elem = (ElemType *) malloc(m * sizeof(ElemType));
    if (!(*H).elem)
        exit(OVERFLOW); /* 存储分配失败 */
    for (i = 0; i < m; i++)
        (*H).elem[i].key = NULLKEY; /* 未填记录的标志 */
    return OK;
}

void DestroyHashTable(HashTable *H) { /* 初始条件: 哈希表H存在。操作结果: 销毁哈希表H */
    free((*H).elem);
    (*H).elem = NULL;
    (*H).count = 0;
    (*H).sizeindex = 0;
}

unsigned Hash(KeyType K) { /* 一个简单的哈希函数(m为表长,全局变量) */
    return K % m;
}

void collision(int *p, int d) /* 线性探测再散列 */
{ /* 开放定址法处理冲突 */
    *p = (*p + d) % m;
}

Status SearchHash(HashTable H, KeyType K, int *p, int *c) { /* 在开放定址哈希表H中查找关键码为K的元素,若查找成功,以p指示待查数据 */
    /* 元素在表中位置,并返回SUCCESS;否则,以p指示插入位置,并返回UNSUCCESS */
    /* c用以计冲突次数,其初值置零,供建表插入时参考。*/
    *p = Hash(K); /* 求得哈希地址 */
    while (H.elem[*p].key != NULLKEY && !EQ(K, H.elem[*p].key)) { /* 该位置中填有记录.并且关键字不相等 */
        (*c)++;
        if (*c < m)
            collision(p, *c); /* 求得下一探查地址p */
        else
            break;
    }
    if EQ(K, H.elem[*p].key)
        return SUCCESS; /* 查找成功,p返回待查数据元素位置 */
    else
        return UNSUCCESS; /* 查找不成功(H.elem[p].key==NULLKEY),p返回的是插入位置 */
}

Status InsertHash(HashTable *, ElemType); /* 对函数的声明 */
void RecreateHashTable(HashTable *H) /* 重建哈希表 */
{ /* 重建哈希表 */
    int i, count = (*H).count;
    ElemType *p, *elem = (ElemType *) malloc(count * sizeof(ElemType));
    p = elem;
    printf("重建哈希表\n");
    for (i = 0; i < m; i++) /* 保存原有的数据到elem中 */
        if (((*H).elem + i)->key != NULLKEY) /* 该单元有数据 */
            *p++ = *((*H).elem + i);
    (*H).count = 0;
    (*H).sizeindex++; /* 增大存储容量 */
    m = hashsize[(*H).sizeindex];
    p = (ElemType *) realloc((*H).elem, m * sizeof(ElemType));
    if (!p)
        exit(OVERFLOW); /* 存储分配失败 */
    (*H).elem = p;
    for (i = 0; i < m; i++)
        (*H).elem[i].key = NULLKEY; /* 未填记录的标志(初始化) */
    for (p = elem; p < elem + count; p++) /* 将原有的数据按照新的表长插入到重建的哈希表中 */
        InsertHash(H, *p);
}

Status InsertHash(HashTable *H, ElemType e) { /* 查找不成功时插入数据元素e到开放定址哈希表H中,并返回OK; */
    /* 若冲突次数过大,则重建哈希表。*/
    int c, p;
    c = 0;
    if (SearchHash(*H, e.key, &p, &c)) /* 表中已有与e有相同关键字的元素 */
        return DUPLICATE;
    else if (c < hashsize[(*H).sizeindex] / 2) /* 冲突次数c未达到上限,(c的阀值可调) */
    { /* 插入e */
        (*H).elem[p] = e;
        ++(*H).count;
        return OK;
    } else
        RecreateHashTable(H); /* 重建哈希表 */
    return ERROR;
}

void TraverseHash(HashTable H, void(*Vi)(int, ElemType)) { /* 按哈希地址的顺序遍历哈希表 */
    int i;
    printf("哈希地址0~%d\n", m - 1);
    for (i = 0; i < m; i++)
        if (H.elem[i].key != NULLKEY) /* 有数据 */
            Vi(i, H.elem[i]);
}

Status Find(HashTable H, KeyType K, int *p) { /* 在开放定址哈希表H中查找关键码为K的元素,若查找成功,以p指示待查数据 */
    /* 元素在表中位置,并返回SUCCESS;否则,返回UNSUCCESS */
    int c = 0;
    *p = Hash(K); /* 求得哈希地址 */
    while (H.elem[*p].key != NULLKEY && !EQ(K, H.elem[*p].key)) { /* 该位置中填有记录.并且关键字不相等 */
        c++;
        if (c < m)
            collision(p, c); /* 求得下一探查地址p */
        else
            return UNSUCCESS; /* 查找不成功(H.elem[p].key==NULLKEY) */
    }
    if EQ(K, H.elem[*p].key)
        return SUCCESS; /* 查找成功,p返回待查数据元素位置 */
    else
        return UNSUCCESS; /* 查找不成功(H.elem[p].key==NULLKEY) */
}

void print(int p, ElemType r) {
    printf("address=%d (%d,%d)\n", p, r.key, r.ord);
}

int main() {
    ElemType r[N] = {{17, 1},
                     {60, 2},
                     {29, 3},
                     {38, 4},
                     {1,  5},
                     {2,  6},
                     {3,  7},
                     {4,  8},
                     {60, 9},
                     {13, 10}};
    HashTable h;
    int i, p;
    Status j;
    KeyType k;
    InitHashTable(&h);
    for (i = 0; i < N - 1; i++) { /* 插入前N-1个记录 */
        j = InsertHash(&h, r[i]);
        if (j == DUPLICATE)
            printf("表中已有关键字为%d的记录,无法再插入记录(%d,%d)\n", r[i].key, r[i].key, r[i].ord);
    }
    printf("按哈希地址的顺序遍历哈希表:\n");
    TraverseHash(h, print);
    printf("请输入待查找记录的关键字: ");
    scanf("%d", &k);
    j = Find(h, k, &p);
    if (j == SUCCESS)
        print(p, h.elem[p]);
    else
        printf("没找到\n");
    j = InsertHash(&h, r[i]); /* 插入第N个记录 */
    if (j == ERROR) /* 重建哈希表 */
        j = InsertHash(&h, r[i]); /* 重建哈希表后重新插入第N个记录 */
    printf("按哈希地址的顺序遍历重建后的哈希表:\n");
    TraverseHash(h, print);
    printf("请输入待查找记录的关键字: ");
    scanf("%d", &k);
    j = Find(h, k, &p);
    if (j == SUCCESS)
        print(p, h.elem[p]);
    else
        printf("没找到\n");
    DestroyHashTable(&h);
    return 0;
}

posted @ 2019-11-14 11:16  Xu_Lin  阅读(224)  评论(0编辑  收藏  举报