读《数据结构》1-4章[线性结构]

2016.06.07 – 06.30
读《数据结构》(严蔚敏 吴伟明)“1-4章”的个人笔记。

06.07

1 与数据结构相关的理解

对数据结构的理解(编程语言层面)。数据结构是相互之间存在一种或多种特定关系的数据元素的集合。

例 – 线性链表。

/* 描述数据元素的类型 */
typedef  struct  _link_list {
    char    ch;
    int     i;
    struct _link_list *pn;
}LinkList;

/* 描述数据元素之间关系的代码 */
...

用LinkList定义一个数据对象(变量)就得到一个数据元素,用LinkList定义多个数据对象就得到数据元素的集合;组织各个数据元素在内存中的存储关系(在内存中连续或者不连续存储)及各个数据元素间(被操作)的联系(如pn指向另外一个数据元素)就得到了各个数据元素之间的关系。

算法。算法是对特定问题求解步骤的一种描述。算法效率的度量需通过依据该算法编制的程序在计算机上运行(时)所消耗的时间来度量[代码的时间复杂度可作为算法执行效率的一种参考]。

时间复杂度。一般情况下,算法中基本操作重复执行的次数是问题规模n的一个函数f(n),算法的复杂度记作T(n) = O(f(n)),表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率是相同

06.08
f(n)的计算。对于不同的输入该算法有不同的执行次数时,可用平均值作为f(n)的值;有时各种输入数据输入的概率难以确定,所以更可行的方法是使用该算法最坏执行次数的值作为f(n)的值。

算法的存储空间 复杂度。空间复杂度S(n) = O(f(n)),其中n为问题规模。一个上机执行程序除了需要存储空间来寄存本身所用指令、常数、变量和输入数据外,也需要一些对数据进行操作的工作单元和存储一些为实现计算所需信息的存储空间。若输入数据所占空间只取决于问题本身,和算法无关,则只需要分析除输入和程序之外的额外空间。

Part-I 线性结构

线性结构的特点:在数据元素的非空有限集合中,(1) 存在唯一的一个被称做“第一个”的数据元素;(2) 存在唯一的一个被称做“最后一个”的数据元素;(3) 除了第一个外,集合中的每个数据元素均只有一个前驱;(4) 除最后一个外,集合中每个数据元素均只有一个后继。

2 线性表

线性表。一个线性表是n个数据元素(记录,可以有若干个数据项)的有限序列,其长度可变。[含有大量记录的线性表被称为文件]

2.1 顺序表

线性表的顺序表示 – 顺序表。线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素。

例 - 用C语言来实现一个简单的顺序表。先为顺序表动态分配以元素大小为单位的n个空间单元;若往顺序表中插入一个元素时若超过了当前所分配的空间则往当前空间中追加m个空间单元

06.14
[1] 代码简单实现
sequence_list.c

/* sequence_list.c * 实现一个满足线性表的顺序表的定义的顺序表及一些相关的简单操作 * 2016.06.13 - 06.14 */
#include <stdio.h>
#include <stdlib.h>


/* 函数退出状态 */
#define MALLOCFAILE -1 // 分配空间失败
#define OK 0 // 程序正常返回值
#define FAILE 1 // 函数返回失败


/* 线性表的顺序表动态分配顺序存储结构 */
#define LIST_INIT_SIZE 100 // 线性表存储空间的初始分配量
#define LIST_INCREMENT 10 // 线性表存储空间的分配增量
typedef char ElemType;
typedef struct {
    ElemType    *pelem; // 线性表存储空间基址
    int         len;    // 线性表长度
    int         lsize;  // 线性表当前分配的存储空间
}SqList;


/* 为线性表的顺序表创建存储空间 */
int malloc_sq_list(SqList *psql)
{
    if (!psql) return FAILE;
    // 构造一个空(无值)的线性表
    psql->pelem = (ElemType *)malloc(LIST_INIT_SIZE * sizeof(ElemType));
    if (!psql->pelem) exit(MALLOCFAILE);
    psql->len   = 0;
    psql->lsize = LIST_INIT_SIZE;
    return OK;
}

/* 释放线性表的顺序表的存储空间 */
void free_sq_list(SqList *psql)
{
    if (!psql || !psql->pelem) return ;
    free(psql->pelem);
    psql->pelem = NULL;
    psql->len   = 0;
    psql->lsize = 0;
}

/* 往线性表的顺序表中插入一个元素 */
int insert_elem2sq_list(SqList *psql,int i,ElemType elem)
{
    ElemType    *newbase,*p,*q;

    if (!psql || !psql->pelem) return FAILE;
    // 在顺序线性表psql第i个位置之前插入新的元素elem
    if (i < 1 || i > psql->len + 1) return FAILE;
    if (psql->len >= psql->lsize) {
        newbase = (ElemType *)realloc(psql->pelem,\
                (psql->lsize + LIST_INCREMENT) * sizeof(ElemType));
        if (!newbase) exit(MALLOCFAILE);
        psql->pelem = newbase;
        psql->lsize += LIST_INCREMENT;
    }
    q   = &psql->pelem[i - 1];  // 插入位置
    for (p = &psql->pelem[psql->len - 1]; p >= q; --p) {
        *(p + 1)    = *q;       // 插入位置及之后的元素后移
    }

    *q  = elem;
    ++psql->len;
    return OK;
}

/* 将两个顺序表合并到另一个顺序表中 */
void merge_sq_list(SqList *psqla,SqList *psqlb,SqList *psqlc)
{
    // 已知顺序线性表psqla,psqlb的元素按值非递减排列
    // 归并psqla,psqlb得到的新顺序线性表psqlc的元素也按值非递减排列
    ElemType    *pa,*pb,*pc;
    ElemType    *pa_last,*pb_last;

    if (!psqla || !psqlb || !psqla->pelem || !psqlb->pelem) return ;
    pa              = psqla->pelem;
    pb              = psqlb->pelem;
    psqlc->lsize    = psqlc->len    = psqla->len + psqlb->len;
    pc              = psqlc->pelem  = (ElemType *)malloc(psqlc->lsize * sizeof(ElemType));
    if (!pc) exit(MALLOCFAILE);

    pa_last = psqla->pelem + psqla->len - 1;
    pb_last = psqlb->pelem + psqlb->len - 1;
    while (pa <= pa_last && pb <= pb_last) {
        if (*pa <= *pb) *pc++ = *pa++;
        else *pc++ = *pb++;
    }
    while (pa <= pa_last) *pc++ = *pa++;
    while (pb <= pb_last) *pc++ = *pb++;
}

/* 简单测试以上各个函数 */
int main(void)
{
    int i,rv;
    ElemType elem;
    SqList psqla,psqlb,psqlc;

    i       = 1;
    elem    = 1;
    // 创建顺序线性表空间
    if ((rv = malloc_sq_list(&psqla)) != OK ||  \
        (rv = malloc_sq_list(&psqlb)) != OK ) {
        fprintf(stderr,"Create sequence list failed\n");
        exit(FAILE);
    }

    // 往顺序线性表中插入值
    insert_elem2sq_list(&psqla,i++,elem++);
    insert_elem2sq_list(&psqlb,i,elem);
    printf("%d %d\n",*psqla.pelem,psqlb.pelem[--i]);

    // 合并两个线性表
    merge_sq_list(&psqla,&psqlb,&psqlc);
    printf("%d %d\n",*psqlc.pelem,psqlc.pelem[i]);

    // 释放顺序线性表
    free_sq_list(&psqla);
    free_sq_list(&psqlb);
    free_sq_list(&psqlc);
    return 0;
}

06.15
-man realloc-
原型。void *realloc(void *ptr,size_t size);
功能。realloc函数将ptr所指向的内存块的大小改为size字节大小(原内容不会被改变)。如果新的大小大于原来的大小,新增的内存块不会被初始化。如果ptr为NULL,那么realloc相当于malloc(size);如果size等于0并且ptr不为NULL,则该调用相当于free(ptr)。除非ptr为NULL,否则ptr必须是之前malloc(),calloc()或realloc()的返回值。如果指向的区域被移动,则原来的内存区域会被自动的free(ptr)。
返回值。realloc()函数返回一个指向新内存区域的指针,若请求失败则返回NULL。如果size等于0,NULL或者传递给free()函数过后的指针将被返回。如果realloc()失败,则原来的内存块将不变;它不会被释放或移除。

[2] 算法复杂度分析
insert_elem2sq_list具有重复(循环)的是元素的移动操作,最坏的情况是将元素插入到表中的第一个元素之前(需要移动n个元素 – 假设表具有n个元素)。insert_elem2sq_list的复杂度可用O(n)来表示。同理,合并俩链表的操作的时间复杂度也为O(n)。[《CSAPP》中有更细致的分析]

线性表的特点是逻辑关系相邻的两个数据元素在内存中存储位置也是相邻关系,因此可以用一个简单的、直观的公式来表示(在C语言中也能够用简单的方式访问,如下标、偏移)。然而这种存储结构也有一个弱点:在作插入或删除操作时,需要移动大量元素。

2.2 链表

(1) 线性链表

线性链表。线性链表的存储结构特点是各个逻辑关系相邻的数据元素既可以用连续的内存单元存储也可以用非连续的内存单元存储。[见链表的结点 —— 因为每个结点中只含有一个指针域,故而称这样的链表为线性链表或单链表]

链表的结点。为了表示线性链表中ai与ai+1元素之间的逻辑关系(ai+1为ai的后继元素),ai除了要存储本身的信息外,还要存储ai+1的地址信息(指针域),ai中的这两部分信息被称为结点。

例 – 用C语言实现一个简单的单链表
06.17
[1] 代码简单实现
single_link_list.c

/* signle_link_list.c * 单链表线性表的简单表示和实现 * 2016.06.15 - 06.17 */
#include <stdlib.h>
#include <stdio.h>

/* 函数返回值 */
#define NK  -1  // 函数未正常返回
#define OK  0   // 函数正常返回
#define NP  1   // 函数参数不合法
#define ME  2   // 空间分配失败

/* 描述单链表的结构体类型 */
typedef char ElemType;
typedef struct _single_link_list_node {
        ElemType    data;                       // 数据域
        struct _single_link_list_node *next;    // 指针域
}SLNode,SLHead;

/* 创建一个包含n个结点的单链表,每个结点的数据域为创建时结点的顺序 */
SLHead *create_single_link_list(unsigned n)
{
    unsigned    i;
    SLHead      *head;
    SLNode      *pcn,*pln;

    i   = 0;
    if (n < 1) return NULL;

    // 头结点
    if (NULL == (head = (SLNode *)malloc(sizeof(SLNode)))) {
        fprintf(stderr,"head of link list malloc failed\n");
        exit(ME);   // 退出本进程,进程用户空间被回收从而堆空间自然被回收
    }
    head->data  = 0;
    head->next  = NULL;
    pln         = head;

    // 单链表中的结点
    while (n--) {
        if (NULL == (pcn = (SLNode *)malloc(sizeof(SLNode)))) {
            fprintf(stderr,"%d th node malloc failed\n",++i);
            exit(ME);
        }
        pcn->next   = NULL;
        pcn->data   = ++i;
        pln->next   = pcn;
        pln         = pcn;
    }
    return head;
}

/* 释放具有n个结点的单链表 */
void free_single_link_list(SLHead *head)
{
    SLNode  *pcn,*pnn;

    pcn = pnn = head;
    while (pcn) {
        pnn = pnn->next;
        free(pcn);
        pcn = pnn;
    }
}

/* 往单链表中的第i个结点前插入一个元素 */
int insert_node2link_list(SLHead *head,unsigned i,ElemType e)
{
    int     j;
    SLNode  *pcn,*pnewn;

    if (!head) return NP;
    j   = 0;
    pcn = head;
    while (pcn && j < i - 1) {  // 寻找i - 1个结点
        pcn = pcn->next;
        ++j;
    }

    if (!pcn || j > i - 1) return NK;
    if ((pnewn = (SLNode *)malloc(sizeof(SLNode))) == NULL) {
        fprintf(stderr,"insert %d element failed\n",e);
        return NK;
    }
    pnewn->data = e;
    pnewn->next = pcn->next;
    pcn->next   = pnewn;
    return OK;
}

/* 合并两个单链表 */
/* 已知两个单链表元素按值非减排列 */
/* 归并两个链表得到新链表,新链表也按照值非递减排列 */
SLHead *merge_link_list(SLHead *heada,SLHead *headb)
{
    SLNode *la,*lb,*lc,*headc;

    if (!heada || !headb) return ;
    la      = heada->next;
    lb      = headb->next;
    headc   = lc = heada;
    while (la && lb) {
        if (la->data <= lb->data) {
            lc->next    = la;
            lc          = la;
            la          = la->next;
        } else {
            lc->next    = lb;
            lc          = lb;
            lb          = lb->next;
        }
    }
    lc->next    = la ? la : lb;
    free(headb);
    return headc;
}


/* 输出单链表的所有元素 */
void print_link_list_node_value(SLHead *head)
{
    SLNode  *p;

    if (!head) return;
    p   = head->next;
    while (p) {
        printf("%d ",p->data);
        p   = p->next;
    }
    printf("\n");
}

/* 简单测试单链表 */
int main(void)
{
    int         i;
    SLHead      *ha,*hb,*hc;
    unsigned    la_len,lb_len;

    la_len  = lb_len = 7;
    if (NULL != (ha = create_single_link_list(la_len)))
        print_link_list_node_value(ha);

    if (NULL != (hb = create_single_link_list(lb_len)))
        print_link_list_node_value(hb);

    hc  = merge_link_list(ha,hb);
    print_link_list_node_value(hc);

    for (i = 1; i < 15; ++i)
        insert_node2link_list(hc,i);
    print_link_list_node_value(hc);

    free_single_link_list(hc);

    return 0;
}

在shell下编译并运行single_link_list.c得以下结果:

[2] 复杂度分析
同顺序表,单链表的插入操作的时间复杂度为O(n)。合并单链表的时间复杂度也为O(n)。

(2) 循环链表

循环链表。循环链表的最后一个数据元素指向第一个数据元素(其他同线性链表)。

(3) 双向链表

双向链表。双向链表的结点中有两个指针域,该结点中的一个指针域指向后驱元素,一个指针域指向前趋元素。

例 — 用C语言实现一个简单的双向链表
06.22
double_link_list.c

/* double_link_list.c * 双向链表的简单描述 * 2016.06.17 - 06.22 */
#include <stdio.h>
#include <stdlib.h>

/* 函数返回值 */
#define NK  -1  // 函数非正常返回
#define OK  0   // 函数正常返回
#define EP  1   // 参数不合法

typedef char ElemType;  // 双向链表中数据域中的数据类型
/* 描述双向链表的结构体 */
typedef struct _DulNode {
    ElemType        dt;
    struct _DulNode *pr;
    struct _DulNode *pn;
    unsigned        nm;
}DulNode,*DuLinkList;

/* 创建双向链表 */
/* 结点中的数据域的值为结点被创建时的个数 */
DuLinkList create_dul_link_list(unsigned n)
{
    int         i;
    DuLinkList  head,ps,nt;

    if (NULL == (ps = (DulNode *)malloc(sizeof(DulNode)))) {
        exit(NK);
    }
    i       = 1;
    ps->pr  = NULL;
    ps->pn  = NULL;
    ps->dt  = i;
    head    = ps;
    ps->nm  = i;
    while (--n) {
        if (NULL == (nt = (DulNode *)malloc(sizeof(DulNode)))) {
            exit(NK);
        }
        ps->pn  = nt;
        nt->pr  = ps;
        nt->dt  = ++i;
        ps      = nt;
        head->nm++;
    }
    ps->pn      = head;
    head->pr    = ps;
    return head;
}
/* 释放双向链表 */
void free_dul_link_list(DuLinkList dl)
{
    int         i;
    unsigned    n;
    DuLinkList  pr,pn;

    if (!dl) return;
    n   = dl->nm;
    pr  = dl;
    pn  = dl->pn;
    for (i = 1; i <= n; ++i) {
        if (pr) {
            free(pr);
            pr  = pn;
            pn  = pn->pn;
        }
    }
}
/* 合并俩双向链表 */
DuLinkList merge_dul_link_list(DuLinkList dla,DuLinkList dlb)
{
    // 已知双向链表dla和dlb按值递增排列
    // 将dla和dlb合并成一个双向链表(递增排列)
    unsigned    dla_nm,dlb_nm;
    DuLinkList  dlc,head,tail;

    if (!dla || !dlb) return NULL;
    dla_nm  = dla->nm;
    dlb_nm  = dlb->nm;
    if (dla->dt <= dlb->dt) {
        dlc = head = dla;
        dla = dla->pn;
        dla_nm--;
    } else {
        dlc = head = dlb;
        dlb = dlb->pn;
        dlb_nm--;
    }
    head->nm    = dla_nm + dlb_nm + 1;

    while (dla_nm && dlb_nm) {
        if (dla->dt <= dlb->dt) {
            dlc->pn = dla;
            dla->pr = dlc;
            dlc     = dla;
            dla     = dla->pn;
            dla_nm--;
        } else {
            dlc->pn = dlb;
            dlb->pr = dlc;
            dlc     = dlb;
            dlb     = dlb->pn;
            dlb_nm--;
        }
    }

    tail        = dla_nm ? dla : dlb;
    dlc->pn     = tail;
    tail->pr    = dlc;
    dlc         = tail;
    dlc->pn     = head;
    head->pr    = dlc;

    return head;
}

/* 打印双向链表中的内容 - 验证 */
void print_dul_node_value(DuLinkList dl)
{
    unsigned i,n;

    if (!dl) return ;
    i   = 1;
    n   = dl->nm;
    for (i = 1; i <= n; ++i) {
        printf("%d ",dl->dt);
        dl  = dl->pn;
    }
    printf("\n");
}

/* 简单测试双向链表 */
int main(void)
{
    unsigned    dl_len;
    DuLinkList  dla,dlb,dlc;

    dl_len  = 7;
    dla     = create_dul_link_list(dl_len);
    dlb     = create_dul_link_list(dl_len);
    print_dul_node_value(dla);
    print_dul_node_value(dlb);

    dlc = merge_dul_link_list(dla,dlb);
    print_dul_node_value(dlc);

    //free_dul_link_list(dlc);
    return 0;
}

运行double_link_list.c得以下结果:

2.3 一元多项式的表示及相加

06.23

3 栈和队列

3.1 栈及栈的简单应用

。栈是限定仅在表尾进行插入或删除操作的线性表。表尾端称为栈顶,表头端称为栈底。顺序栈即利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,同时设置一个指向栈顶元素的指针。[链栈的各个数据元素的存储单元不一定连续]

(1) 顺序栈

例 – 用C实现一个简单的顺序栈

06.23
一般来说,在初始化设空栈时不应限定栈的最大容量。一个较合理的做法是:先为栈分配一个基本容量,然后在应用过程中,当栈的空间不够使用时在逐段扩大。

[1] 描述栈的结构体类型

/* 描述栈的结构体类型 */
typedef SElemType   int;
typedef struct _SqStack{
    SElemType   *base;
    SElemType   *top;
    int         stacksize;
}SqStack;

其中,stacksize指示栈的当前可使用的最大容量。栈的初始化操作为:按设定的初始分配量进行第一次分配,base为栈底指针,在顺序栈中,它始终指向栈底的位置,若base的值为NULL,则表明栈结构不存在。称top为栈顶指针,其初值指向栈底,即top=base可作为栈空的标记,每当插入新的栈顶元素时,指针top增1;删除栈顶元素时,指针top减1,因此,非空栈中的栈顶指针始终在栈顶元素的下一个位置上。

[2] 跟栈相关的基本操作
06.26
stack_basic.c

/* stack_basic.c * 栈的简单描述与实现 * 2016.06.23 - 06.26 */
#include <stdio.h>
#include <stdlib.h>

#define STACK_INIT_SIZE 100 // 栈空间初始分配大小
#define STACKINCREMENT  10  // 栈空间分配增量
#define OK              0   // 函数正常返回
#define NK              11  // 函数非正常返回
#define NP              22  // 函数参数无效

/* 描述栈的结构体类型 */
typedef int SElemType;
typedef struct _SqStack{
    SElemType   *base;
    SElemType   *top;
    int         stacksize;
}SqStack;


/* 基本操作 */
// 构造一个空栈 - 为栈分配空间,指向栈顶与栈底的变量都指向栈底
int init_stack(SqStack *stack)
{
    if (!stack) return NP;
    stack->base         = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType));
    if (!stack->base) return NK;
    stack->top          = stack->base;
    stack->stacksize    = STACK_INIT_SIZE;
    return OK;
}

// 销毁栈 - 释放为栈分配的空间
void destroy_stack(SqStack *stack)
{
    if (!stack) return;
    if (stack->base) {
        stack->stacksize    = 0;
        free(stack->base);
        stack->base = NULL;
        stack->top  = NULL;
    }
}

// 将栈设为空栈 - 将指向栈顶的变量指向栈底
void clear_stack(SqStack *stack)
{
    if (!stack) return;
    if (stack->top && stack->base)
        stack->top  = stack->base;
}

// 判断是否为空栈
int is_stack_empty(SqStack *stack)
{
    if (stack && stack->base && stack->top)
        return (stack->base == stack->top);
    else
        return NP;
}

// 获取栈的长度
int get_stack_length(SqStack *stack)
{
    if (stack) return stack->stacksize;
    else return NP;
}

// 获取栈顶元素
SElemType get_top(SqStack *stack)
{
    SElemType *top;

    if (stack && stack->top && stack->base && \
        stack->top != stack->base) {
        top = stack->top - 1;
        return *top;
    }
    return NP;
}

// 往栈顶中插入一个元素
int push(SqStack *stack,SElemType e)
{
    int         status;
    SElemType   *base;

    status  = is_stack_empty(stack);
    if (NP != status) {
        if (stack->top - stack->base < stack->stacksize) {  // 栈未满
            *stack->top = e;
            stack->top++;
            return OK;
        } else {    // 栈已满
            base    = (SElemType *)realloc(stack->base,(stack->stacksize + STACKINCREMENT) * sizeof(SElemType));
            if (base) return (NK);
            stack->base         = base;
            stack->stacksize    += STACKINCREMENT;
            *stack->top = e;
            stack->top++;
            return OK;
        }
    }
    return NP;
}

// 删除栈顶元素
int pop(SqStack *stack)
{
    int status;

    status  = is_stack_empty(stack);
    if (NP != status && !status) {
        stack->top--;
        return OK;
    }
    return NK;
}
// 遍历栈
void traverse_stack(SqStack *stack)
{
    int         status;
    SElemType   *pe;

    status  = is_stack_empty(stack);
    if (NP != status && !status) {
        pe  = stack->top - 1;
        while (pe - stack->base) {
            printf("%d ",*pe--);   // int
        }
        printf("%d\n",*pe);
    }
}

/* 简单测试栈 - 部分函数 */
int main(void)
{
    int     i,rv;
    SqStack stack;

    if ((rv = init_stack(&stack)) != OK)    // 初始化栈
        exit(NK);
    for (i = 0; i < STACK_INIT_SIZE; ++i)   // 入栈
        push(&stack,i);
    traverse_stack(&stack);                 // 遍历栈
    destroy_stack(&stack);                  // 销毁栈

    return 0;
}

在shell中运行该程序,得到以下测试结果:

(2) 栈的简单应用

06.27
[还相差甚远]

[1] 数制转换

[2] 括号匹配的检验

[3] 行编辑程序

[4] 迷宫求解

[5] 表达式求值

3.2 栈与递归的实现

06.28
[…如汉诺塔、八皇后问题…]

3.3 队列

队列。队列是一种先进先出的线性表。它只允许在表的一端(队尾)进行插入,而在另一端(队头)删除元素。

(1) 链队列

!例 – 用C语言实现一个简单的链队列程序

(2) 循环队列

3.4 离散事件模拟

[..银行业务模拟程序..]

06.09

4 串

4.1 串的基本操作

(字符串)。串是由零个或多个字符组成的有限序列。

串定长顺序存储表示。用一组地址连续的内存单元存储串值的字符序列。

堆分配存储表示。仍以一组地址连续的内存单元存储串值的字符序列,但它们的存储空间是在程序执行过程中动态分配的。

串的块链存储表示。以结点方式存储各个串。

06.30
例 – 用C语言编写一个简单的关于串(顺序存储方式)的程序。(串比较、串连接、求串长度、串模式匹配等)[串相关的操作无须涉及任何与内核交互,《TCPL》等书涉及关于串函数的一些编写内容,本书中的模式匹配算法可以值得(因为没怎么看过)一看]。

4.2 串的简单应用

06.29

(1) 文本编辑

(2) 建立词索引表

信息检索主要操作。在大量存放的磁盘上的信息中查询一个特定的信息(为了提高查询效率,一个重要的问题就是建立一个好的索引系统)。

[2016.06.12 - 10:15]

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 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 的计算机程序设