文章标题

来源:互联网 发布:windows找不到cmd.exe 编辑:程序博客网 时间:2024/06/05 05:46

算法:解决特定问题求解步骤的描述
好的算法:时间效率高和存储量低

判断一个算法的效率时,函数中的常数和其他次要项常常可以忽略,而更应该关注最高阶项的阶数:输入规模越大,次要项影响越来越小

算法事件复杂度:随着问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同;O(n) ;(n就是程序语句执行多少次)

对数阶:2的x次方等n,x=O(logn)

int count = 1;while(count < n)    count = count * 2;

顺序表,在存、读数据时,不管哪个位置,时间复杂度都是O(1);而插入或删除时,时间复杂度都是O(n)

链表的每个节点中只包含一个指针域:单链表

头指针:链表指向第一个节点的指针,若链表有头节点,则是指向头节点的指针;无论链表是否为空,头指针均不为空;头指针是链表的必要元素
头结点:不一定是链表的必须要素;为了操作的统一和方便而设立的

typedef struct Node{    ElemType data;    struct Node* next;}Node;typedef  struct Node*  LinkList;

单链表的读取(第i个数据)

链表插入:注意要重新申请一个节点的内存空间
链表删除:主要要释放删除节点的内存

头插法:p =(LinkList*)malloc(sizeof(Node)); p -> next = head->next;
head -> next = p;

尾插法: p = (LinkList*) malloc(sizeof(Node)); head -> next = p; head = p;

整表删除:p = head->next; while(p) { q = p->next; free(p); p = q;}

单链表和顺序表

  • 存储方式:用一段连续的存储单元依次存储线性表的数据元素;单链表用一组任意的存储单元存放线性表的元素
  • 时间性能:查找:O(1)、O(n);插入和删除:O(n)、O(1)
  • 空间性能:顺序存储结构需要预分配空间

静态链表:用数组描述的链表;数组的元素由两个数据域组成:data存放数据元素,cur存放该元素的后继在数组中的下表

#define MAX_SIZE 1000typedef struct{    ElemType data;    int cur;    }Component,StaticLinkList[MAXSIZE];

数组第一个元素,即下标为0的元素的cur就存放数组空闲空间的第一个元素的下标
数组最后一个元素的cur存放第一个有数值的元素的下表(相当于头结点)

循环链表:将单链表中终端节点指针由空指针改为指向头节点,就使整个单链表形成一个环,这种头尾衔接的单链表称为循环链表

循环链表和单链表的主要差异就在于循环的判断条件上,原来是判断p->next 是否为空,现在则是p->next不等于头结点,则循环未结束

单链表有了头节点,我们可以用O(1)的时间访问第一个节点,但对于要访问最后一个节点,却需要O(n)时间

我们用指向终端结点的尾指针来表示循环链表,此时查找开始节点和终端节点都很方便了:终端节点用read指针指示,查找终端节点:rear;查找开始节点rear->next->next

将两个链表合并成一个表时,有了尾指针也非常简单:已知rearA和rearB

p = rearA->next;rearA ->next = rearB->next->next;rearB->next = p;free(rearB->next);

双向链表:在单链表的每个节点中,再设置一个指向其前驱节点的指针域

单链表根据next指针要查找上一节点的话,最坏的时间复杂度就是O(n),因为每次都要从头开始遍历查找

插入操作:将s节点插入到节点p和p->next之间

s->prior = p;s->next = p->next;p->next ->prior = s;p->next  = s;

删除操作:删除节点p

p->prior->next = p->next;p->next->prior = p->prior;

:限定仅在表尾进行插入和删除操作的线性表;栈顶和栈底;后进先出

栈的顺序存储结构(需要事先确定数组存储空间大小)

  • 数组下标为0的一端作为栈底;
  • top栈顶元素在数组中的位置,top必须小于StackSize
  • 空栈:top = -1

    typedef int SElemType;typedef struct{    SElemType data[MAXSIZE];    int top;  //用于栈顶指针}SqStack;Status Push(SqStack* s,SElemType e){    if(s->top == MAXSIZE-1)        return ERROR;    s->top++;    s->data[s->top]  = e;    return OK;}Status Pop(SqStack* s, SElemType e){    if(s->top == -1)        return ERROR;    e = s->data[s->top];    s->top--;    return OK;}

栈的链式存储结构:栈顶指针就是指向链表的头部(第一个节点),对于链栈来说,是不需要头节点的。

typedef struct StackNode{    ElemType data;    struct StackNode* next;}StackNode,*LinkStackPtr;typedef struct LinkStack{    LinkStackPtr top;    int count;}LinkStack;(链栈)Status Push(LinkStack* s,SElemType e){    LinkStackPtr s = (LinkStackPtr)malloc(sizeof(StackNode));    s->data = e;    s->next = s->top;    s->top = s;    s->count++;    return OK;}Status Pop(LinkStack* s,SElemType* e){    LinkStackPtr p;    if(StackEmpty(*s))        return ERROR;    *e = s->top->data;    p = s->top;    s->top = s->top->next;    free(p);    s->count--;    return OK;}

如果栈的使用过程元素变化不可预料,有时大有时小,那么最好用链栈;反之,栈的大小变化范围在可控范围内,建议使用顺序栈

栈的应用–递归:在前行阶段,对于每一层递归,函数的局部变量、参数值以及返回地址都被压入栈中,在退回阶段,位于栈顶的局部变量、参数值和返回地址被弹出,恢复调用的状态

栈的应用–四则运算表达式

  • 后缀表达式:所有的符号都是在要运算数字的后面出现
    • “9+(3-1)X3+10/2”:“931-3*+102/+”
  • 规则:从左到右遍历表达式的每个数字和符号,遇到数字就进栈,遇到符号,就将处于栈顶两个数字出栈,进行运算,运算结果进栈,已知到最终获得结果

中缀表达式转后缀表达式:从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即称为后缀表达式的一部分;若是符号,则判断其与栈顶符号的优先级,是右括号或优先级低于栈顶符号,则栈顶元素依次出栈并输出

队列:先进先出;只允许在一端进行插入操作,而在另一端进行删除操作的线性表

为了避免当只有一个元素时,队头和队尾重合使处理变得麻烦,引入两个指针,front指针指向队头元素,rear指针指向队尾元素的下一个位置;当front等于rear时,空队列

循环队列:队列的头尾相接的顺序存储结构

typedef struct{    QElemType data[MAXSIZE];    int front;    int rear;   }SqQueue;

队列的链式存储结构:其实就是单链表,只不过是尾进头出

  • 队头指针指向链队列的头结点,队尾指针指向终端节点

    typedef struct QNode{    QElemType data;    struct QNode* next;}QNode,*QueuePtr;

    typedef struct
    {
    QueuePtr front, rear;
    }LinkQueue;

:由零个或多个字符组成的有限序列

查找子串位置

int Index(String s,String t,int pos){    int n = strlen(s);    int m = strlen(t);      int i = pos;    String sub;     while(i <= n-m+1)    {        SubStr(sub,s,i,m);        if(StrCompare(sub,t) != 0)        {            ++i;                    }               else        {            return i;                   }           }       return 0;   }

串的模式匹配

从主串“goodgoogle”中找到”google”这个子串的位置:对主串的每一个字符作为子串的开头,与要匹配的字符串进行匹配

int Index(String s,String t,int pos){    int n = strlen(s);    int m = strlen(t);    int i = pos;    int j = 0;      while(i < n && j < m)    {        if(s[i] == t[j])        {            i++;            j++;        }        else        {            i = i-j+1;             j = 0;          }       }       if(j>m)        return i-m;    else        return 0;   }

:n个节点的有限集;n=0时称为空树;

在任意一颗非空树中:

  • 有且仅有一个特定的称为根的节点
  • 当n>1时,其余节点可分为m个互不相交的有限集T1, T2, T3…..Tm,其中每一个集合本身又是一棵树,并称为根的子树

  • 节点拥有的子树数称为节点的度;度为0的节点称为叶节点;树的度就是树内各节点的度的最大值

  • 节点的祖先是从根到该节点所经分支上的所有节点。
  • 树中节点的最大层次称为树的深度或高度

如果将树中节点的各子树看成从左到右是有次序的,不能互换的,则称该树为有序树

双亲表示法:每个节点:data(数据域)和parent(指针域:存储该节点的双亲在数组中的下标)

孩子表示法:每个节点有多个指针域,其中每个指针指向一颗子树的根节点

孩子兄弟表示法:设置两个指针:分别指向该节点的第一个孩子和此节点的右兄弟

二叉树:n个结点的有限集合,该集合或者为空集,或者由一个根节点和两颗互不相交的、分别称为根节点的左子树和右子树的二叉树组成

  • 每个结点最多有两颗子树,二叉树不存在度大于2的结点
  • 左子树和右子树是有顺序的,次序不能颠倒

斜树:所有的结点都只有左子树的二叉树叫左斜树;所有的结点只有右子树的二叉树叫右斜树

满二叉树:在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上

完全二叉树:对一棵具有n个结点的二叉树按层序编号,如果编号为i(1<=i<=n)的结点与同样深度的满二叉树编号为i的结点在二叉树中位置完全相同

满二叉树一定是一棵完全二叉树,但完全二叉树不一定是满的

二叉树的性质

  • 在二叉树的第i层上至多有2的i-1次方个结点
  • 深度为k的二叉树至多有2的k次方-1个结点
  • 对任何一颗二叉树T,如果其终端节点数为N0,度为2的节点数N2,N0 = N2+1
  • 具有n个结点的完全二叉树的深度为[log2n]+1([x]表示不大于x的最大整数)

二叉树的顺序存储结构

  • 顺序存储结构一般只用于完全二叉树

二叉树的链式存储结构

data、lchild和rchild

typedef struct BiTNode{    TElemType data;    struct BiTNode* lchild,*rchild; }BiTNode,*BiTree;

二叉树的遍历:从根节点出发,按照某种次序依次访问二叉树中所有节点,使得每个结点被访问一次且仅被访问一次

  • 前序遍历:若二叉树为空,则空操作返回,否则先访问根节点,然后前序遍历左子树,再前序遍历右子树
  • 中序遍历:若二叉树为空,则空操作返回,否则从根节点开始(注意不是先访问根节点),中序遍历根节点的左子树,然后是访问根节点,最后中序遍历右子树;
  • 后序遍历:若二叉树为空,则空操作返回;从左到右先叶子后节点的方式遍历左右子树,最后是访问根节点

前序遍历算法:

void PreOrderTraverse(BiTree T){    if(T == NULL)        return;    printf("%c\n",T->data);    PreOrderTraverse(T->lchild);    PreOrderTraverse(T->rchild);    }

中序遍历算法:

void InOrderTraverse(BiTree T){    if(T==UNLL)        return NULL;    InOrderTraverse(T->lchild);    printf("%c",T->data);    InOrderTraverse(T->rchild); }

后序遍历算法

void PostOrderTraverse(BiTree T){    if(T==NULL)        return;    PostOrderTraverse(T->lchild);    PostOrderTraverse(T->rchild);    printf("%c",T->data);   }
原创粉丝点击