数据结构--线性表、串、树

数据结构

  • 线性表
    • 特点
    • 基本操作
    • 顺序表
    • 链表
    • 队列
    • 串的匹配模式
    • KMP
    • 树的性质
    • 二叉树
    • 树的遍历
    • 并差集
    • 线索二叉树
    • 二叉排序树
    • 二叉平衡树
    • 哈夫曼树

线性表

线性表是具有相同类型的N(N>=0)个元素的有限序列,其中n为表长,当N==0时为空表.。

特点

  1. 个数有限
  2. 顺序性:逻辑上的顺序性,每个元素的排序都有先后次序
  3. 都是元素:每个元素都是单个元素
  4. 数据类型都相同:每个元素占有相同大小的储存空间
  5. 具有抽象性:既只讨论元素间一对一的关系,而不探究表示内容
  6. 逻辑结构:表示元素之间一对一的关系

基本操作

  1. 初始化表
  2. 销毁操作
  3. 按值查找(按关键词)
  4. 按位查找(按位置)
  5. 插入操作
  6. 删除操作
  7. 输出操作
  8. 判空操作
  9. 求表长

顺序表

一组地址连续存放的储存单元依次存放在线性表的元素,从而使得逻辑上的相邻两个元素物理位置也相邻

template<class T> class SqList
{
     
    private:
       T *Data;
       int length=0;
       int MaxSize=16;
    public:
    SqList(){
     
        Data=new T[MaxSize];
    }
    ~SqList(){
     
       delete Data;
    }
    bool ListInsert(int index,T data){
     
        if(index<0||index>length)return false;
        if(length==MaxSize){
     
            T *Data_T=new T[MaxSize*2];
            for(int i=0;i<MaxSize;i++)Data_T[i]=Data[i];
            delete Data;
            Data=Data_T;
            MaxSize*=2;
        }
        for(int i=length;i>index;i--)Data[i]=Data[i-1];
        Data[index]=data;
        length++;
        return true;
    }
    bool ListDelete(int index){
     
        for(int i=index;i+1<length;i++)Data[i]=Data[i+1];
        length--;
    }
    int ListFindByKey(T x){
     
        for(int i=0;i<length;i++)if(x==Data[i])return i;
        return -1;
    }
    T ListFindByIndex(int index){
     
        if(index<0||index>=length)return NULL;
        return Data[index];
    }
    bool IsEmpty(){
     
        return length==0;
    }
    int ListLength(){
     
        return length;
    }
};

链表

线性表的链式储存称为单链表

template<class T> class LinkNode
{
     
    public:
    T data;
    LinkNode* next;
    LinkNode(T Data){
     
        data=Data;
        next=NULL;
    }
};
template<class T> class LinkList
{
     
    private:
    LinkNode<T> *head;
    public:
        LinkList(){
     
            head=new LinkNode<T>(NULL);
        }
    LinkNode<T>* LinkListByKey(T data){
     
        LinkNode<T>*p=head;
        while(p!=NULL&&p->data!=data)p=p->next;
        return p;
    }
    LinkNode<T>* LinkFindByIndex(int Index){
     
        LinkNode<T>*p=head;
        for(int i=0;i<Index&&p!=NULL;i++)p=p->next;
        return p;
    }
    void LinkInsert(LinkNode<T>* pre,T data){
     
        LinkList* p=new LinkList(data);
        p->next=pre->next;
        pre->next=p;
    }
    void LinkDelete(LinkList* Elemte){
     
        if(Elemte==NULL)return;
        LinkNode<T>*p=head;
        while(p->next!=Elemte)p=p->next;
        if(p->next==Elemte){
     
            p=Elemte->next;
            delete Elemte;
        }
    }
    bool IsEmpty(){
     return head->next==NULL;}
    int getLength(){
     
        int length=0;
        LinkNode<T>*p=head;
        while(p->next!=NULL)length++,p=p->next;
        return length;
    }
};

只允许在一段插入或者删除的线性表
特点:后入先出
栈的基本操作

  1. 初始化栈
  2. 判断栈是否为空
  3. 入栈
  4. 出栈
  5. 获取栈顶元素

本质:受限制的顺序表
储存方式:顺序表、链表

template<class T> class Stack
{
     
    private:
       T *Data;
       int length=0;
       int MaxSize=16;
    public:
    Stack(){
     
        Data=new T[MaxSize];
    }
    ~Stack(){
     
       delete Data;
    }
    void Push(T data){
     
        if(length==MaxSize){
     
            T* Data_T=new T[MaxSize*2];
            for(int i=0;i<MaxSize;i++)Data_T[i]=Data_T[i];
            delete Data;Data=Data_T;
            MaxSize*=2;
        }
        Data[length++]=data;
    }
    void pop(){
     
        if(length==0)return NULL;
        length--;
    }
    T top(){
     
        if(length==0)return NULL;
        return Data[length-1];
    }
    bool IsEmpty(){
     
        return length==0;
    }
};

队列

只允许在一段插入在另一段者删除的线性表
特点:先入先出
栈的基本操作

  1. 初始化队列
  2. 判断队列是否为空
  3. 入队
  4. 出队
  5. 获取队首元素

本质:受限制的顺序表
储存方式:顺序表、链表

template<class T> class Queue
{
     
    private:
       T *Data;
       int L=0,R=0;
       int MaxSize=16;
    public:
    Queue(){
     
        Data=new T[MaxSize];
    }
    ~Queue(){
     
       delete Data;
    }
    void Push(T data){
     
        if((R+1)%MaxSize==L){
     
            T* Data_T=new T[MaxSize*2];
            for(int i=0;i<MaxSize;i++)Data_T[i]=Data[(L+i)%MaxSize];
            delete Data;
            Data=Data_T;
            L=0;R=MaxSize-1;
            MaxSize*=2;
        }
        Data[(R+1)%MaxSize]=data;
        R=(R+1)%MaxSize;
    }
    void pop(){
     
        if(L<R)
        L=(L+1)%MaxSize;
    }
    T top(){
     
        return Data[(R+MaxSize-1)%MaxSize];
    }
    bool IsEmpty(){
     
        return L==R;
    }
};

是由零个或者多个有限字符组成的序列
子串串中任意连续个字符组成的序列

串的匹配模式

暴力匹配 时间复杂的O(mn)

struct SString{
     
    int length;
    char *ch;
};
int SString_match(SString S,SString T){
     
    for(int i=0;i+T.length<S.length;i++){
     
        int flag=0;
        for(int j=0;j<T.length;j++)
           if(S.ch[i+j]!=T.ch[j]){
     
              flag=1;break;
           }
        if(flag==0)return i;
    }
    return -1;
}

KMP

一种进行快速字符串匹配的算法 时间复杂度O(m+n)
需要一个next数组辅助计算.

next[y]内储存一个位置x ,x是最大的可以满足满足子串[0,x]是子串[0,y]的后缀的位置

求next数组过程,可以通过一个线性递推求出

匹配的时候利用next数值 直接跳到可以可能匹配的最大位置,不需要回溯重新匹配。

struct SString{
     
    int length;
    char *ch;
};
int GetNext(SString S,int Next[]){
     
    Next[0]=-1;
    int K=0;
    for(int i=1;i<S.length;i++){
     
        while(K!=-1&&S.ch[K]!=S.ch[i])K=Next[K];
        Next[i]=K++;
    }
}
int KMP_match(SString S,SString T,int Next[]){
     
    for(int i=0,j=-1;i<S.length;i++){
     
        while(j!=-1&&S.ch[i]!=T.ch[j+1])j=Next[j];
        if(S.ch[i]==T.ch[j+1])j++;
        if(j+1==T.length)return i-T.length+1;
    }
    return -1;
}

树是n(n>=0)个结点的有限集合,n=0时称为空树
对于任意非空树满足

  1. 有且仅有一个特定的称为根的结点
  2. 当n>1时,其他结点克分为m个互不相交的有限集合,其中每一个集合本身都是一棵树,称为根节点的子树

树中一个结点的子结点的个数称为该结点的
树中最大的度称为 树的度
度大于0的结点是 分支结点
度等于0的结点是 叶子结点

树的性质

  1. 树的结点个数等于树上所有结点的度之和加一
  2. 度为m的树上di层最多有 m^(i-1)个结点
  3. 高度为h、度为m的树最有拥有 (m^h-1)/ (h-1)个结点
  4. 具有n个结点的m叉树最少有[ logm[n(m-1)+1] ]层

二叉树

二叉树n(n>=0)个结点的有限集合

  1. n=0时,二叉树为空树
  2. n>0时,由根结点和两个互不相交的左子树和右子树组成,左右子树分别都是一颗二叉树

二叉树和度为2的树的区别

  • 二叉树可以为空树 ,度为二的树最少拥有3个结点
  • 二叉树的左右子结点有顺序之分,度为二的树的子结点没有顺序之分

** 特殊二叉树**

  • 满二叉树:高度为h,且含有 2^h-1个结点的二叉树
  • 完全二叉树:高度为h,n个结点,且每个结点都有高度为h的满二叉树中编号1~n的结点对应的二叉树
  • 二叉排序树:对于任意结点,若存在左子树或右子树,那么左子树上所有结点的关键字均小于该节点,右子树上所有结点的关键字均大于该结点。
  • 平衡二叉树 :树上任意结点的左子树和右子树的深度的差不超过1

二叉树的储存

  1. 使用数组顺序储存
  2. 定义结构体
struct BiTree{
     
    TElemType  data;
    BiTree*lchild,*rchild;
    BiTree(TElemType Data){
     
        data=Data;
        lchild=rchild=NULL;
    }
};

树的遍历

先序遍历

void PreOrder(BiTree* root){
     
    if(root!=NULL){
     
        visit(root);
        PreOrder(root->lchild);
        PreOrder(root->rchild);
    }
}

中序遍历

void InOrder(BiTree* root)
{
     
    if(root!=NULL)
    {
     
        InOrder(root->lchild);
        visit(root);
        InOrder(root->rchild);
    }
}

后序遍历

void PostOrder(BiTree* root)
{
     
    if(root!=NULL)
    {
     
        PostOrder(root->lchild);
        PostOrder(root->rchild);
        visit(root);
    }
}

层序遍历

void LevelOrder(BiTree* root)
{
     
    Queue<BiTree* >q;
    q.Push(root);
    while(q.IsEmpty()==false){
     
        BiTree* rt=q.top();
        q.pop();
        visit(rt);
        if(rt->lchild!=NULL)q.Push(rt->lchild);
        if(rt->rchild!=NULL)q.Push(rt->rchild);
    }
}

并差集

一种简单的集合表示
类似于树,只有一个指针指向根结点。
并差集的基本操作

  1. 初始化:首先将所有结点的根节点设置为自己
  2. 查找:判断自己的根点是否为自己,如果不是继续查找根结点的根结点,直到找到根结点是自身的结点,那么那个点就是当前结点所在位置的树的根结点
  3. 合并:找到需要合并的两个点所在的树的根节点,使得一方的根节点指向另外一个结点 ,则两个树合并为一颗树
#define MaxSize 100;
int fa[MaxSize];
void init(){
     
    for(int i=0;i<MaxSize;i++)fa[i]=i;
}
int find(int x){
     
    return fa[x]==x?x:find(fa[x]);
}
void join(int x,int y){
     
    x=find(x);y=find(y);
    fa[y]=x;
}

对于并差的查找可以做一下优化,每次查找到树的根节点后,将经过的结点全部指向根节点,减小树链的深度。

int find(int x){
     
    return fa[x]==x?x:fa[x]=find(fa[x]);
}

并差集常常用于判断二个结点是否属于同一棵树,或者判断是否有环。

线索二叉树

给二叉树加上线索,使得二叉树线性化。

  • 线索化
    若无左子树,则将其指针指向其前驱结点
    若无右子树,则将其指针指向其后驱结点
  • 中序遍历线索化
void InThread(ThreadNode* root,ThreadNode* &pre){
     
    if(root!=NULL){
     
        InThread(root->lchild,pre);
        if(root->lchild==NULL){
     
            root->ltag=1;
            root->lchild=pre;
            if(pre!=NULL){
     
                pre->rchild=root;
                pre->ltag=1;
            }
        }
        pre=root;
        InThread(root->rchild,pre);
    }
}

二叉排序树

二叉排序树 BST 又称作 二叉查找树。
二叉排序树的性质

  • 任意结点,其左子树的所有结点的关键词一定小于当前结点
  • 任意结点,其右子树的所有结点的关键词一定大于当前结点
  • 二叉排序树的左右子树也是一颗二叉排序

二叉排序树的基本操作

  1. 初始化二叉排序树
  2. 销毁二叉排序树
  3. 查找操作:根据二叉排序的性质 我们可以判断答案是在左子树还是右子树,递归下去 就可以找到对应关键字结点。
  4. 插入操作:根据二叉排序的性质 我们可以判断应该插入在左子树还是右子树,递归下去 就可以找到对应插入结点的位置,然后插入。
  5. 删除操作:先找到应该被删除的节点,然后分三种情况处理
    • 被删除的结点是叶子:直接删掉就好
    • 被删除的结点有一个儿子:把这个结点删掉,儿子补上
    • 被删除的结点有量个儿子:选择该结点的前继,用前继替换掉当前结点并把前继删掉。
#define TElemType  int
struct BiTree{
     
    TElemType  data;
    BiTree*lchild,*rchild;
    BiTree(TElemType Data){
     
        data=Data;
        lchild=rchild=NULL;
    }
};
void BiTree_init(BiTree * root){
     
    root=NULL;
}
void BiTree_destory(BiTree* root){
     
    if(root->lchild!=NULL)BiTree_destory(root->lchild);
    if(root->rchild!=NULL)BiTree_destory(root->rchild);
    delete root;
}
void BiTree_insert(BiTree *&root,TElemType data){
     
    if(root==NULL)root=new BiTree(data);
    if(root->data!=data){
     
        if(data < root->data)BiTree_init(root->lchild);
        else BiTree_init(root->rchild);
    }
}
BiTree* BiTree_sreach(BiTree * root,TElemType data){
     
    if(root==NULL)return NULL;
    if(root->data==data)return root;
    if(data<root->data)return BiTree_sreach(root->lchild,data);
    return BiTree_sreach(root->rchild,data);
}
void BiTree_delete(BiTree *&root,TElemType data){
     
    if(root!=NULL){
     
        if(root->data==data){
     
            if(root->lchild!=NULL&&root->lchild!=NULL){
     
                    BiTree *p=root->lchild;
                    while(p->rchild!=NULL)p=p->rchild;
                    TElemType tmp_Data=p->data;
                    BiTree_delete(root,tmp_Data);
                    root->data=tmp_Data;
            }else{
     
                if(root->lchild==NULL&&root->rchild==NULL){
     
                    delete root;
                    root=NULL;
                }else
                if(root->lchild!=NULL){
     
                    BiTree *p=root->lchild;
                    delete root;
                    root=p;
                }else{
     
                    BiTree *p=root->rchild;
                    delete root;
                    root=p;
                }
            }
        }else{
     
            if(data<root->data)BiTree_delete(root->lchild,data);
            else BiTree_delete(root->rchild,data);
        }
    }
}

二叉平衡树

二叉平衡树的性质

  • 左右两个子树的高度差的绝对值不超过1
  • 左右两个子树都是一棵平衡二叉树

哈夫曼树

给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。

你可能感兴趣的:(数据结构,链表,队列,算法,二叉树)