查找算法之顺序查找,折半查找,二叉查找树

查找表的概念

  查找表是由同一类型的数据元素构成的集合。例如电话号码簿和字典都可以看作是一张查找表。
  在查找表中只做查找操作,而不改动表中数据元素,称此类查找表为静态查找表;反之,在查找表中做查找操作的同时进行插入数据或者删除数据的操作,称此类表为动态查找表。

顺序查找

  顺序查找的查找过程为:从表中的最后一个数据元素开始,逐个同记录的关键字做比较,如果匹配成功,则查找成功;反之,如果直到表中第一个关键字查找完也没有成功匹配,则查找失败
同时,在程序中初始化创建查找表时,由于是顺序存储,所以将所有的数据元素存储在数组中,但是把第一个位置留给了用户用于查找的关键字。例如,在顺序表{1,2,3,4,5,6}中查找数据元素值为 7 的元素,则添加后的顺序表为:

在这里插入图片描述


            图1
  顺序表的一端添加用户用于搜索的关键字,称作“监视哨”。
  图 1 中监视哨的位置也可放在数据元素 6 的后面(这种情况下,整个查找的顺序应有逆向查找改为顺序查找)。
  放置好监视哨之后,顺序表遍历从没有监视哨的一端依次进行,如果查找表中有用户需要的数据,则程序输出该位置;反之,程序会运行至监视哨,此时匹配成功,程序停止运行,但是结果是查找失败。
代码实现:

/*
 * @Description: 顺序查找算法
 * @Version: V1.0
 * @Autor: Carlos
 * @Date: 2020-05-22 15:52:11
 * @LastEditors: Carlos
 * @LastEditTime: 2020-06-03 16:56:06
 */ 
#include <stdio.h>
#include <stdlib.h>
#define keyType int
typedef struct {
    //查找表中每个数据元素的值
    keyType key;
    //如果需要,还可以添加其他属性
}ElemType;

typedef struct{
    //存放查找表中数据元素的数组
    ElemType *elem;
    //记录查找表中数据的总数量
    int length;
}SSTable;
/**
 * @Description: 创建查找表
 * @Param: SSTable **st 指向结构体指针的指针,即指针变量的指针,int length 创建的二叉树的长度
 * @Return: 无
 * @Author: Carlos
 */
void Create(SSTable **st,int length){
    (*st)=(SSTable*)malloc(sizeof(SSTable));
    (*st)->length=length;
    //结构体指针分配空间
    (*st)->elem =(ElemType*)malloc((length+1)*sizeof(ElemType));
    printf("输入表中的数据元素:\n");
    //根据查找表中数据元素的总长度,在存储时,从数组下标为 1 的空间开始存储数据
    for (int i=1; i<=length; i++) {
        scanf("%d",&((*st)->elem[i].key));
    }
}
/**
 * @Description: 查找表查找的功能函数,其中key为关键字
 * @Param: SSTable *st指向结构体变量的指针,keyType key 要查找的元素
 * @Return: key在查找表中的位置
 * @Author: Carlos
 */
int Search_seq(SSTable *st,keyType key){
    //将关键字作为一个数据元素存放到查找表的第一个位置,起监视哨的作用
    st->elem[0].key=key;
    int i=st->length;
    //从查找表的最后一个数据元素依次遍历,一直遍历到数组下标为0
    while (st->elem[i].key!=key) {
        i--;
    }
    //如果 i=0,说明查找失败;反之,返回的是含有关键字key的数据元素在查找表中的位置
    return i;
}
int main(int argc,const char * argv[]) {
    SSTable *st;
    Create(&st,6);
    getchar();
    printf("请输入查找数据的关键字:\n");
    int key;
    scanf("%d",&key);
    int location=Search_seq(st,key);
    if (location==0) {
        printf("查找失败");
    }else{
        printf("数据在查找表中的位置为:%d",location);
    }
    return 0;
}

折半查找

  折半查找,也称二分查找,在某些情况下相比于顺序查找,使用折半查找算法的效率更高。但是该算法的使用的前提是静态查找表中的数据必须是有序的。
  例如,在{5,21,13,19,37,75,56,64,88,80,92}这个查找表使用折半查找算法查找数据之前,需要首先对该表中的数据按照所查的关键字进行排序:{5,92}。
  在折半查找之前对查找表按照所查的关键字进行排序的意思是:若查找表中存储的数据元素含有多个关键字时,使用哪种关键字做折半查找,就需要提前以该关键字对所有数据进行排序。

折半查找算法

  对静态查找表{5,92}采用折半查找算法查找关键字为 21 的过程为:

在这里插入图片描述


              图2
后一个关键字,指针 mid 指向处于 low 和 high 指针中间位置的关键字。在查找的过程中每次都同 mid 指向的关键字进行比较,由于整个表中的数据是有序的,因此在比较之后就可以知道要查找的关键字的大致位置。
  例如在查找关键字 21 时,首先同 56 作比较,由于21 < 56,而且这个查找表是按照升序进行排序的,所以可以判定如果静态查找表中有 21 这个关键字,就一定存在于 low 和 mid 指向的区域中间。
  因此,再次遍历时需要更新 high 指针和 mid 指针的位置,令 high 指针移动到 mid 指针的左侧一个位置上,同时令 mid 重新指向 low 指针和 high 指针的中间位置。如图3所示:

在这里插入图片描述


              图3
  同样,用 21 同 mid 指针指向的 19 作比较,19 < 21,所以可以判定 21 如果存在,肯定处于 mid 和 high 指向的区域中。所以令 low 指向 mid 右侧一个位置上,同时更新 mid 的位置。

在这里插入图片描述


              图4
  当第三次做判断时,发现 mid 就是关键字 21 ,查找结束。
  注意:在做查找的过程中,如果 low 指针和 high 指针的中间位置在计算时位于两个关键字中间,即求得 mid 的位置不是整数,需要统一做取整操作。
  折半查找的实现代码:

/*
 * @Description: 折半查找.前提是静态查找表中的数据必须是有序的。
 * @Version: V1.0
 * @Autor: Carlos
 * @Date: 2020-05-22 16:09:01
 * @LastEditors: Carlos
 * @LastEditTime: 2020-06-03 16:58:14
 */ 
#include <stdio.h>
#include <stdlib.h>
#define keyType int
typedef struct {
    //查找表中每个数据元素的值
    keyType key;
    //如果需要,还可以添加其他属性
}ElemType;

typedef struct{
    //存放查找表中数据元素的数组
    ElemType *elem;
    //记录查找表中数据的总数量
    int length;
}SSTable;
/**
 * @Description: 创建查找表
 * @Param: SSTable **st 指向结构体指针的指针,即指针变量的指针,int length){
    (*st)=(SSTable*)malloc(sizeof(SSTable));
    (*st)->length=length;
    (*st)->elem = (ElemType*)malloc((length+1)*sizeof(ElemType));
    printf("输入表中的数据元素:\n");
    //根据查找表中数据元素的总长度,在存储时,从数组下标为 1 的空间开始存储数据
    for (int i=1; i<=length; i++) {
        scanf("%d",&((*st)->elem[i].key));
    }
}
//折半查找算法
/**
 * @Description: 折半查找算法
 * @Param: SSTable *ST 指向结构体的指针,keyType key 要插入的元素
 * @Return: 成功的返回key在查找表中的位置,否则返回0
 * @Author: Carlos
 */
int Search_Bin(SSTable *ST,keyType key){
    //初始状态 low 指针指向第一个关键字
    int low=1;
    //high 指向最后一个关键字
    int high=ST->length;
    int mid;
    while (low<=high) {
        //int 本身为整形,所以,mid 每次为取整的整数
        mid=(low+high)/2;
        //如果 mid 指向的同要查找的相等,返回 mid 所指向的位置
        if (ST->elem[mid].key==key)
        {
            return mid;
        }
        //如果mid指向的关键字较大,则更新 high 指针的位置
        else if(ST->elem[mid].key>key)
        {
            high=mid-1;
        }
        //反之,则更新 low 指针的位置
        else{
            low=mid+1;
        }
    }
    return 0;
}

int main(int argc,11);
    getchar();
    printf("请输入查找数据的关键字:\n");
    int key;
    scanf("%d",&key);
    int location=Search_Bin(st,key);
    //如果返回值为 0,则证明查找表中未查到 key 值,
    if (location==0) {
        printf("查找表中无该元素");
    }else{
        printf("数据在查找表中的位置为:%d",location);
    }
    return 0;
}

二叉查找树

  动态查找表中做查找操作时,若查找成功可以对其进行删除;如果查找失败,即表中无该关键字,可以将该关键字插入到表中。
  动态查找表的表示方式有多种,本节介绍一种使用树结构表示动态查找表的实现方法——二叉排序树(又称为“二叉查找树”)。

二叉查找树概念

  二叉排序树要么是空二叉树,要么具有如下特点:

  • 二叉排序树中,如果其根结点有左子树,那么左子树上所有结点的值都小于根结点的值;
  • 二叉排序树中,如果其根结点有右子树,那么右子树上所有结点的值都大小根结点的值;
  • 二叉排序树的左右子树也要求都是二叉排序树;

  例如,图 5 就是一个二叉排序树:

在这里插入图片描述


              图5

使用二叉排序树查找关键字

  二叉排序树中查找某关键字时,查找过程类似于次优二叉树,在二叉排序树不为空树的前提下,首先将被查找值同树的根结点进行比较,会有 3 种不同的结果:

  1. 如果相等,查找成功;
  2. 如果比较结果为根结点的关键字值较大,则说明该关键字可能存在其左子树中;
  3. 如果比较结果为根结点的关键字值较小,则说明该关键字可能存在其右子树中;
    实现函数为:(运用递归的方法)
/**
 * @Description: 二叉排序树查找算法
 * @Param: BiTree T  KeyType key  BiTree f BiTree *p
 * @Return: 删除成功 TRUE 删除失败 FALSE
 * @Author: Carlos
 */
int SearchBST(BiTree T,KeyType key,BiTree f,BiTree *p)
{
    //如果 T 指针为空,说明查找失败,令 p 指针指向查找过程中最后一个叶子结点,并返回查找失败的信息
    if (!T)
    {
        *p = f;
        return FALSE;
    }
    //如果相等,令 p 指针指向该关键字,并返回查找成功信息
    else if (key == T->data)
    {
        *p = T;
        return TRUE;
    }
    //如果 key 值比 T 根结点的值小,则查找其左子树;反之,查找其右子树
    else if (key < T->data)
    {
        return SearchBST(T->lchild,key,T,p);
    }
    else
    {
        return SearchBST(T->rchild,p);
    }
}

二叉排序树中插入关键字

  二叉排序树本身是动态查找表的一种表示形式,有时会在查找过程中插入或者删除表中元素,当因为查找失败而需要插入数据元素时,该数据元素的插入位置一定位于二叉排序树的叶子结点,并且一定是查找失败时访问的最后一个结点的左孩子或者右孩子。
  例如,在图 1 的二叉排序树中做查找关键字 1 的操作,当查找到关键字 3 所在的叶子结点时,判断出表中没有该关键字,此时关键字 1 的插入位置为关键字 3 的左孩子。
  所以,二叉排序树表示动态查找表做插入操作,只需要稍微更改一下上面的代码就可以实现,具体实现代码为:

/**
 * @Description: 二叉排序树查找算法
 * @Param: BiTree T  KeyType key  BiTree f BiTree *p
 * @Return: 删除成功 TRUE 删除失败 FALSE
 * @Author: Carlos
 */
int SearchBST(BiTree T,p);
    }
}
/**
 * @Description: 二叉树插入函数
 * @Param: BiTree *T 二叉树结构体指针的指针  ElemType e 要插入的元素
 * @Return: 删除成功 TRUE 删除失败 FALSE
 * @Author: Carlos
 */
int InsertBST(BiTree *T,ElemType e)
{
    BiTree p = NULL;
    //如果查找不成功,需做插入操作
    if (!SearchBST((*T),e,NULL,&p))
    {
        //初始化插入结点
        BiTree s = (BiTree)malloc(sizeof(BiTree));
        s->data = e;
        s->lchild = s->rchild = NULL;
        //如果 p 为NULL,说明该二叉排序树为空树,此时插入的结点为整棵树的根结点
        if (!p)
        {
            *T = s;
        }
        //如果 p 不为 NULL,则 p 指向的为查找失败的最后一个叶子结点,只需要通过比较 p 和 e 的值确定 s 到底是 p 的左孩子还是右孩子
        else if (e < p->data)
        {
            p->lchild = s;
        }
        else
        {
            p->rchild = s;
        }
        return TRUE;
    }
    //如果查找成功,不需要做插入操作,插入失败
    return FALSE;
}

  通过使用二叉排序树对动态查找表做查找和插入的操作,同时在中序遍历二叉排序树时,可以得到有关所有关键字的一个有序的序列。
  例如,假设原二叉排序树为空树,在对动态查找表 {3,5,7,2,1} 做查找以及插入操作时,可以构建出一个含有表中所有关键字的二叉排序树,过程如图6 所示:

在这里插入图片描述


              图6
  通过不断的查找和插入操作,最终构建的二叉排序树如图 6(5) 所示。当使用中序遍历算法遍历二叉排序树时,得到的序列为:1 2 3 5 7 ,为有序序列。
一个无序序列可以通过构建一棵二叉排序树,从而变成一个有序序列。

二叉排序树中删除关键字

  在查找过程中,如果在使用二叉排序树表示的动态查找表中删除某个数据元素时,需要在成功删除该结点的同时,依旧使这棵树为二叉排序树。
  假设要删除的为结点 p,则对于二叉排序树来说,需要根据结点 p 所在不同的位置作不同的操作,有以下 3 种可能:

  1. 结点 p 为叶子结点,此时只需要删除该结点,并修改其双亲结点的指针即可;
  2. 结点 p 只有左子树或者只有右子树,此时只需要将其左子树或者右子树直接变为结点 p 双亲结点的左子树即可;
  3. 结点 p 左右子树都有,此时有两种处理方式:
      (1).令结点 p 的左子树为其双亲结点的左子树;结点 p 的右子树为其自身直接前驱结点的右子树,如图7所示;

    在这里插入图片描述


                  图7
      (2)用结点 p 的直接前驱(或直接后继)来代替结点 p,同时在二叉排序树中对其直接前驱(或直接后继)做删除操作。如图 8 为使用直接前驱代替结点 p:

    在这里插入图片描述


                  图8
      图 8中,在对左图进行中序遍历时,得到的结点 p 的直接前驱结点为结点 s,所以直接用结点 s 覆盖结点 p,由于结点 s 还有左孩子,根据第 2 条规则,直接将其变为双亲结点的右孩子。
      具体实现代码:(可运行)
/*
 * @Description: 二叉查找树
 * @Version: V1.0
 * @Autor: Carlos
 * @Date: 2020-06-02 15:50:31
 * @LastEditors: Carlos
 * @LastEditTime: 2020-06-03 16:49:46
 */
#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
#define ElemType int
#define KeyType int
/* 二叉排序树的节点结构定义 */
typedef struct BiTNode
{
    int data;
    struct BiTNode *lchild,*rchild;
} BiTNode,*BiTree;
/**
 * @Description: 二叉排序树查找算法
 * @Param: BiTree T  KeyType key  BiTree f BiTree *p
 * @Return: 删除成功 TRUE 删除失败 FALSE
 * @Author: Carlos
 */
int SearchBST(BiTree T,&p))
    {
        //初始化插入结点
        BiTree s = (BiTree)malloc(sizeof(BiTree));
        s->data = e;
        s->lchild = s->rchild = NULL;
        //如果 p 为NULL,说明该二叉排序树为空树,此时插入的结点为整棵树的根结点
        if (!p)
        {
            *T = s;
        }
        //如果 p 不为 NULL,则 p 指向的为查找失败的最后一个叶子结点,只需要通过比较 p 和 e 的值确定 s 到底是 p 的左孩子还是右孩子
        else if (e < p->data)
        {
            p->lchild = s;
        }
        else
        {
            p->rchild = s;
        }
        return TRUE;
    }
    //如果查找成功,不需要做插入操作,插入失败
    return FALSE;
}
/**
 * @Description: 删除节点的函数
 * @Param: BiTree *p 指向结构体指针的指针
 * @Return: 删除成功 TRUE
 * @Author: Carlos
 */
int Delete(BiTree *p)
{
    BiTree q,s;
    //情况 1,结点 p 本身为叶子结点,直接删除即可
    if (!(*p)->lchild && !(*p)->rchild)
    {
        *p = NULL;
    }
    //左子树为空,只需用结点 p 的右子树根结点代替结点 p 即可;
    else if (!(*p)->lchild)
    {
        q = *p;
        *p = (*p)->rchild;
        free(q);
        q = NULL;
    }
    //右子树为空,只需用结点 p 的左子树根结点代替结点 p 即可;
    else if (!(*p)->rchild)
    {
        q = *p;
        //这里不是指针 *p 指向左子树,而是将左子树存储的结点的地址赋值给指针变量 p
        *p = (*p)->lchild;
        free(q);
        q = NULL;
    }
    //左右子树均不为空,采用第 2 种方式
    else
    {
        q = *p;
        s = (*p)->lchild;
        //遍历,找到结点 p 的直接前驱
        while (s->rchild)
        {
            //指向p节点左子树最右边节点的前一个。保留下来
            q = s;
            s = s->rchild;
        }
        //直接改变结点 p 的值
        (*p)->data = s->data;
        //判断结点 p 的左子树 s 是否有右子树,分为两种情况讨论  如果有右子树,s一定会指向右子树的叶子节点。q 此时指向的是叶子节点的父节点。 q != *p二者不等说明有右子树
        if (q != *p)
        {
            //若有,则在删除直接前驱结点的同时,令前驱的左孩子结点改为 q 指向结点的孩子结点
            q->rchild = s->lchild;
        }
        else
        //q == *p ==NULL 说明没有右子树
        {
            //否则,直接将左子树上移即可
            q->lchild = s->lchild;
        }
        free(s);
        s = NULL;
    }
    return TRUE;
}
/**
 * @Description: 删除二叉树中的元素
 * @Param: BiTree *T 指向二叉树结构体的指针 int key 要删除的元素
 * @Return: 删除成功 TRUE 删除失败 FALSE
 * @Author: Carlos
 */
int DeleteBST(BiTree *T,int key)
{
    if (!(*T))
    { //不存在关键字等于key的数据元素
        return FALSE;
    }
    else
    {
        if (key == (*T)->data)
        {
            Delete(T);
            return TRUE;
        }
        else if (key < (*T)->data)
        {
            //使用递归的方式
            return DeleteBST(&(*T)->lchild,key);
        }
        else
        {
            return DeleteBST(&(*T)->rchild,key);
        }
    }
}
/**
 * @Description: 中序遍历输出二叉树
 * @Param: BiTree t 结构体变量
 * @Return: 无
 * @Author: Carlos
 */
void order(BiTree t) 
{
    if (t == NULL)
    {
        return;
    }
    order(t->lchild);
    printf("%d ",t->data);
    order(t->rchild);
}
int main()
{
    int i;
    int a[5] = {3,9};
    BiTree T = NULL;
    for (i = 0; i < 5; i++)
    {
        InsertBST(&T,a[i]);
    }
    printf("中序遍历二叉排序树:\n");
    order(T);
    printf("\n");
    printf("删除3后,中序遍历二叉排序树:\n");
    DeleteBST(&T,3);
    order(T);
}

有任何问题,均可通过公告中的二维码联系我

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


【啊哈!算法】算法3:最常用的排序——快速排序       上一节的冒泡排序可以说是我们学习第一个真正的排序算法,并且解决了桶排序浪费空间的问题,但在算法的执行效率上却牺牲了很多,它的时间复杂度达到了O(N2)。假如我们的计算机每秒钟可以运行10亿次,那么对1亿个数进行排序,桶排序则只需要0.1秒,而冒泡排序则需要1千万秒,达到115天之久,是不是很吓人。那有没有既不浪费空间又可以快一点的排序算法
匿名组 这里可能用到几个不同的分组构造。通过括号内围绕的正则表达式就可以组成第一个构造。正如稍后要介绍的一样,既然也可以命名组,大家就有考虑把这个构造作为匿名组。作为一个实例,请看看下列字符串: “08/14/57 46 02/25/59 45 06/05/85 18 03/12/88 16 09/09/90 13“ 这个字符串就是由生日和年龄组成的。如果需要匹配年两而不要生日,就可以把正则
选择排序:从数组的起始位置处开始,把第一个元素与数组中其他元素进行比较。然后,将最小的元素方式在第0个位置上,接着再从第1个位置开始再次进行排序操作。这种操作一直到除最后一个元素外的每一个元素都作为新循环的起始点操作过后才终止。 public void SelectionSort() { int min, temp;
public struct Pqitem { public int priority; public string name; } class CQueue { private ArrayList pqueue; public CQueue() { pqueue
在编写正则表达式的时候,经常会向要向正则表达式添加数量型数据,诸如”精确匹配两次”或者”匹配一次或多次”。利用数量词就可以把这些数据添加到正则表达式里面了。 数量词(+):这个数量词说明正则表达式应该匹配一个或多个紧紧接其前的字符。 string[] words = new string[] { "bad", "boy", "baad", "baaad" ,"bear", "b
来自:http://blog.csdn.net/morewindows/article/details/6678165/归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列
插入排序算法有两层循环。外层循环会啄个遍历数组元素,而内存循环则会把外层循环所选择的元素与该元素在数组内的下一个元素进行比较。如果外层循环选择的元素小于内存循环选择的元素,那么瘦元素都想右移动以便为内存循环元素留出位置。 public void InsertionSort() { int inner, temp;
public int binSearch(int value) { int upperBround, lowerBound, mid; upperBround = arr.Length - 1; lowerBound = 0; while (lowerBound <= upper
虽然从表内第一个节点到最后一个节点的遍历操作是非常简单的,但是反向遍历链表却不是一件容易的事情。如果为Node类添加一个字段来存储指向前一个节点的连接,那么久会使得这个反向操作过程变得容易许多。当向链表插入节点的时候,为了吧数据复制给新的字段会需要执行更多的操作,但是当腰吧节点从表移除的时候就能看到他的改进效果了。 首先需要修改Node类来为累增加一个额外的链接。为了区别两个连接,这个把指
八、树(Tree)树,顾名思义,长得像一棵树,不过通常我们画成一棵倒过来的树,根在上,叶在下。不说那么多了,图一看就懂:当然了,引入了树之后,就不得不引入树的一些概念,这些概念我照样尽量用图,谁会记那么多文字?树这种结构还可以表示成下面这种方式,可见树用来描述包含关系是很不错的,但这种包含关系不得出现交叉重叠区域,否则就不能用树描述了,看图:面试的时候我们经常被考到的是一种叫“二叉树”的结构,二叉
Queue的实现: 就像Stack类的实现所做的一样,Queue类的实现用ArrayList简直是毋庸置疑的。对于这些数据结构类型而言,由于他们都是动态内置的结构,所以ArrayList是极好的实现选择。当需要往队列中插入数据项时,ArrayList会在表中把每一个保留的数据项向前移动一个元素。 class CQueue { private ArrayLis
来自:http://yingyingol.iteye.com/blog/13348911 快速排序介绍:快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地
Stack的实现必须采用一种基本结构来保存数据。因为再新数据项进栈的时候不需要担心调整表的大小,所以选择用arrayList.using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Collecti
数组类测试环境与排序算法using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace Data_structure_and_algorithm{ class CArray { pr
一、构造二叉树 二叉树查找树由节点组成,所以需要有个Node类,这个类类似于链表实现中用到的Node类。首先一起来看看Node类的代码。 public class Node { public int Data; public Node Left; public Node Right; public v
二叉树是一种特殊的树。二叉树的特点是每个结点最多有两个儿子,左边的叫做左儿子,右边的叫做右儿子,或者说每个结点最多有两棵子树。更加严格的递归定义是:二叉树要么为空,要么由根结点、左子树和右子树组成,而左子树和右子树分别是一棵二叉树。 下面这棵树就是一棵二叉树。         二叉树的使用范围最广,一棵多叉树也可以转化为二叉树,因此我们将着重讲解二叉树。二叉树中还有连两种特殊的二叉树叫做满二叉树和
上一节中我们学习了队列,它是一种先进先出的数据结构。还有一种是后进先出的数据结构它叫做栈。栈限定只能在一端进行插入和删除操作。比如说有一个小桶,小桶的直径只能放一个小球,我们现在向小桶内依次放入2号、1号、3号小球。假如你现在需要拿出2号小球,那就必须先将3号小球拿出,再拿出1号小球,最后才能将2号小球拿出来。在刚才取小球的过程中,我们最先放进去的小球最后才能拿出来,而最后放进去的小球却可以最先拿
msdn中的描述如下:(?= 子表达式)(零宽度正预测先行断言。) 仅当子表达式在此位置的右侧匹配时才继续匹配。例如,w+(?=d) 与后跟数字的单词匹配,而不与该数字匹配。此构造不会回溯。(?(零宽度正回顾后发断言。) 仅当子表达式在此位置的左侧匹配时才继续匹配。例如,(?此构造不会回溯。msdn描述的比较清楚,如:w+(?=ing) 可以匹配以ing结尾的单词(匹配结果不包括ing),(
1.引入线索二叉树 二叉树的遍历实质上是对一个非线性结构实现线性化的过程,使每一个节点(除第一个和最后一个外)在这些线性序列中有且仅有一个直接前驱和直接后继。但在二叉链表存储结构中,只能找到一个节点的左、右孩子信息,而不能直接得到节点在任一遍历序列中的前驱和后继信息。这些信息只有在遍历的动态过程中才能得到,因此,引入线索二叉树来保存这些从动态过程中得到的信息。 2.建立线索二叉树 为了保
排序与我们日常生活中息息相关,比如,我们要从电话簿中找到某个联系人首先会按照姓氏排序、买火车票会按照出发时间或者时长排序、买东西会按照销量或者好评度排序、查找文件会按照修改时间排序等等。在计算机程序设计中,排序和查找也是最基本的算法,很多其他的算法都是以排序算法为基础,在一般的数据处理或分析中,通常第一步就是进行排序,比如说二分查找,首先要对数据进行排序。在Donald Knuth 的计算机程序设