数据结构--线性表

1.线性表的定义

        相较于数组,线性表可以有效的利用存储空间,是一种由同一数据类型构成的有序序列的线性结构。对于线性表的实现,有利用数组连续存储空间的顺序存储方式,也有通过链表实现的存储方式。为了加深对线性表的理解,以下对线性表的操作都将使用c语言进行演示:

2.顺序表的各种操作:

2.1 定义

#include<stdio.h>
#include<stdlib.h>
#define max 105
//实现一个存放int类型数据的顺序表
typedef struct ArrayList
{
    int *data;//指针模拟数组
    int maxsize;//数组的最大容量
    int length;//顺序表中元素的实际个数
}MyArray;

2.2 初始化

//初始化顺序表
MyArray initArray()
{
    MyArray b;
    //申请最大容量空间
    b.data=(int*)malloc(sizeof(int)*max);
    if(b.data==NULL)
    {
        printf("内存分配失败");
    }
    //顺序表最大容量
    b.maxsize=max;
    //顺序表实际容量
    b.length=0;
    return b;
}

2.3 插入(顺序插入与指定插入)

//顺序插入
void add(MyArray *a,int k)
{
    if(a->length<a->maxsize)
    {
        a->data[a->length]=k;
        a->length++;
    }
    else{
        printf("顺序表已满,不能插入\n");
    }
}

//制定位置i插入k
void insert(MyArray *a,int i,int k)
{

    if(a->length<a->maxsize)
    {
        //空出i的位置
        for(int j=a->length-1;j>=i;j--)
        {
            a->data[j+1]=a->data[j];
        }
        a->data[i]=k;
        a->length++;
    }
    else{
        printf("顺序表已满,不能插入\n");
    }  
}

2.4 查找

//查找某个数据的的位置
int  find(MyArray *a,int k)
{
    for(int i=0;i<a->length;i++)
    {
        if(a->data[i]==k)
        {
            return i;
        }
    }
    return -1;//k不存在
}

2.5 删除

//删除数据k
void delet(MyArray *a,int k)
{
    //查找k的位置
    int i=find(a,k);
    if(i==-1)
    {
        printf("被删除的数据不存在\n");
    }
    for(int j=i;j<a->length-1;j++)
    {
        a->data[j]=a->data[j+1];
    }
    a->length--;
}

 2.5 修改

//将k改为x
void change(MyArray *a,int k,int x)
{
    //查找k的位置
    int i=find(a,k);
    if(i==-1)
    {
        printf("被修改的数据不存在\n");
    }
    a->data[i]=x;

}

3.链式表的各种操作

        链表相较于顺序表,它的存储空间并不固定,而是通过上一节点的指针域链接,因此再链表的节点结构体中,不仅需要存储数据,还需要有个指针域,存储下一节点的地址或者是上一节点的地址(双向链表)。

3.1 单链表

#include<stdio.h>
#include<stdlib.h>

typedef struct Node
{
    int data;//节点数据
    struct Node *next;//下一个数据所在节点地址
}Node,*LinkList;
//LinkList=Node*,增加可读性:LinkList指头指针,Node*节点的指针,两者其实没有本质区别

//c中main外调用函数必须申明
Node* findNode(LinkList L,int k);

//初始化带头节点的空链表
LinkList initLinkList()
{
    Node* s=(Node*)malloc(sizeof(Node));
    if(s==NULL)
    {
        printf("空间分配失败\n");
    }
    else
    {
        s->next=NULL;
    }
    
}

//头插法
LinkList HeadInsert(LinkList L,int k)
{
    //先申请新节点保存数据k
    Node* s=(Node*)malloc(sizeof(Node));
    if(s==NULL)
    {
        printf("空间分配失败\n");
        return L;
    }
    else
    {
        s->data=k;
        s->next=L->next;
        L->next=s;
        return L;
    }
    return L;
}

//尾插法
LinkList RearInsert(LinkList L,int k)
{
    Node* p=L;//p先指向头结点
    while(p->next!=NULL)
    {
        p=p->next;
    }
    Node* s=(Node*)malloc(sizeof(Node));
    if(s==NULL)
    {
        printf("空间分配失败\n");
    }
    else
    {
        s->data=k;
        s->next=NULL;
        p->next=s;
    }
    return L;
}

//中间插入
LinkList mid_insert(LinkList L,int x,int k)
{
    Node* p=findNode(L,x);
    if(p==NULL)
    {
       printf("数据%d不存在,插入失败\n",x);
       return L; 
    }
    Node* s=(Node*)malloc(sizeof(Node));
    if(s==NULL)
    {
        printf("空间分配失败\n");
    }
    else
    {
        s->data=k;
        s->next=p->next;
        p->next=s;
    }
    return L;
}

//删除k所在的节点位置
LinkList Delet(LinkList L,int k)
{
    if(L->next==NULL)
    {
        printf("空链表,删除失败\n");
        return L;
    }
    Node* pre=L;
    Node* p=L->next;
    while(p!=NULL&&p->data!=k)
    {
        pre=p;
        p=p->next;
    }
    if(p==NULL)
    {
       printf("数据%d不存在,删除失败\n",k);
       return L; 
    }
    pre->next=p->next;
    free(p);
    p=NULL;//防止p成为野指针

    return L;

}

//查找数据k所在节点地址
Node* findNode(LinkList L,int k)
{
    Node* p=L->next;
    while(p!=NULL&&p->data!=k)
    {
        p=p->next;
    }
    return p;
}

void printff(LinkList L)
{
    Node* p=L->next;
    while(p!=NULL)
    {
        printf("%d ",p->data);
        p=p->next;
    }
    printf("\n");
}
int main()
{
    LinkList L=NULL;
    //初始化空链表 
    L=initLinkList();
    //头插法插入
    L=HeadInsert(L,5);
    L=HeadInsert(L,8);
    L=HeadInsert(L,6);
    printff(L);
    //尾插
    L=RearInsert(L,9);
    L=RearInsert(L,1);
    printff(L);
    //中间插入
    L=mid_insert(L,5,7);
    printff(L);
    //删除
    L=Delet(L,8);
    printff(L);

    return 0;
}

3.2 双向链表

#include <stdio.h>
#include <stdlib.h>
//带头结点的双链表的节点结构
typedef struct Node
{
    int data;//节点数据
    struct Node* next;//下一个数据所在节点地址
    struct Node* pre;//上一个数据所在节点地址
}Node,*LinkList;
//LinkList=Node*,增加可读性:LinkList指头指针,Node*节点的指针,两者其实没有本质区别

LinkList find(LinkList L,int k);

//初始化带头结点的空链表
LinkList initlist()
{
    Node* p=(Node*)malloc(sizeof(Node));
    if(p==NULL)
    {
        printf("空间分配失败\n");
    }
    else
    {
        p->next=p->pre=NULL;
    }
    return p;
}

//头插法
LinkList headInsert(LinkList L, int k)
{
    Node* s=(Node*)malloc(sizeof(Node));
    if(s==NULL)
    {
        printf("空间分配失败\n");
        return L;
    }    
    s->data=k;

    s->pre=L;
    s->next=L->next;
    L->next=s;
    if(s->next!=NULL)//空节点
    {
        s->next->pre=s; 
    }

    return L;
}

//尾插法
LinkList rearInsert(LinkList L,int k)
{
    Node* s=(Node*)malloc(sizeof(Node));
    if(s==NULL)
    {
        printf("空间分配失败\n");
        return L;
    }    
    s->data=k;

    //找尾节点
    Node* p=L;
    while(p->next!=NULL)
    {
        p=p->next;
    }
    s->next=p->next;
    s->pre=s;
    p->next=s;

    return L;    
}

//中间插入,数据x后面插入数据k
LinkList midInsert(LinkList L,int x,int k)
{
    Node* s=(Node*)malloc(sizeof(Node));
    if(s==NULL)
    {
        printf("空间分配失败\n");
        return L;
    }    
    s->data=k;

    //找x所在节点
    Node* p=find(L,x);
    if(p==NULL)
    {
       printf("数据%d不存在,插入失败\n",x);
       return L; 
    }

    s->pre=p;
    s->next=p->next;
    p->next=s;
    if(s->next!=NULL)//空节点
    {
        s->next->pre=s; 
    }

    return L;

}

//查找
Node* find(LinkList L,int k)
{
    Node* p=L->next;
    while(p!=NULL&&p->data!=k)
    {
        p=p->next;
    }
    return p;
}

//删除
LinkList delet(LinkList L,int k)
{
    if(L->next==NULL)
    {
        printf("空链表,删除失败\n");
        return L;
    }
    Node* p=L->next;
    if(p==NULL)
    {
       printf("数据%d不存在,删除失败\n",k);
       return L; 
    }

    p->pre->next=p->next;
    if(p->next!=NULL)
    {
        p->next->pre=p->pre;
    }

    //傻瓜式
    // Node* pp=p->pre;
    // Node* pn=p->next;
    // pp->next=pn;
    // if(pn!=NULL)
    // {
    //     pn->pre=pp;
    // }
    free(p);
    p=NULL;
    return L;
}

void printff(LinkList L)
{
    Node* p=L->next;
    while(p!=NULL)
    {
        printf("%d ",p->data);
        p=p->next;
    }
    printf("\n");
}

int main()
{
    LinkList L=initlist();
    L=headInsert(L,4);
    L=headInsert(L,5);
    L=headInsert(L,6);
    printff(L);

    L=rearInsert(L,7);
    L=rearInsert(L,9);
    printff(L);

    L=midInsert(L,5,1);
    L=midInsert(L,9,8);
    printff(L);

    L=delet(L,6);
    L=delet(L,7);
    printff(L);

    return 0;
}

3.3 循环链表(单向与双向)

#include<stdio.h>
#include<stdlib.h>

typedef struct Node
{
    int data;//节点数据
    struct Node *next;//下一个数据所在节点地址
}Node,*LinkList;

Node* findNode(LinkList L,int k);

//初始化带头节点的空循环单链表
LinkList initLinkList()
{
    Node* s=(Node*)malloc(sizeof(Node));
    if(s==NULL)
    {
        printf("空间分配失败\n");
    }
    else
    {
        s->next=s;
    }
    return s;
}

//头插法
LinkList HeadInsert(LinkList L,int k)
{
    //先申请新节点保存数据k
    Node* s=(Node*)malloc(sizeof(Node));
    if(s==NULL)
    {
        printf("空间分配失败\n");
        return L;
    }
    else
    {
        s->data=k;
        s->next=L->next;
        L->next=s;
        return L;
    }
    return L;
}

//尾插法
LinkList RearInsert(LinkList L,int k)
{
    Node* p=L;//p先指向头结点
    while(p->next!=L)
    {
        p=p->next;
    }
    Node* s=(Node*)malloc(sizeof(Node));
    if(s==NULL)
    {
        printf("空间分配失败\n");
    }
    else
    {
        s->data=k;
        s->next=L;
        p->next=s;
    }
    return L;
}

LinkList mid_insert(LinkList L,int x,int k)
{
    Node* p=findNode(L,x);
    if(p==L)
    {
       printf("数据%d不存在,插入失败\n",x);
       return L; 
    }
    Node* s=(Node*)malloc(sizeof(Node));
    if(s==NULL)
    {
        printf("空间分配失败\n");
    }
    else
    {
        s->data=k;
        s->next=p->next;
        p->next=s;
    }
    return L;
}

//删除k所在的节点位置
LinkList Delet(LinkList L,int k)
{
    if(L->next==L)
    {
        printf("空链表,删除失败\n");
        return L;
    }
    Node* pre=L;
    Node* p=L->next;
    while(p!=L&&p->data!=k)
    {
        pre=p;
        p=p->next;
    }
    if(p==L)
    {
       printf("数据%d不存在,删除失败\n",k);
       return L; 
    }
    pre->next=p->next;
    free(p);
    p=NULL;//防止p成为野指针

    return L;

}


//查找数据k所在节点地址
Node* findNode(LinkList L,int k)
{
    Node* p=L->next;
    while(p!=L&&p->data!=k)
    {
        p=p->next;
    }
    return p;
}

void printff(LinkList L)
{
    Node* p=L->next;
    while(p!=L)
    {
        printf("%d ",p->data);
        p=p->next;
    }
    printf("\n");
}

int main()
{
    LinkList L=NULL;
    //初始化空链表 
    L=initLinkList();
    //头插法插入
    L=HeadInsert(L,5);
    L=HeadInsert(L,8);
    L=HeadInsert(L,6);
    printff(L);
    //尾插
    L=RearInsert(L,9);
    L=RearInsert(L,1);
    printff(L);
    //中间插入
    L=mid_insert(L,5,7);
    printff(L);
    //删除
    L=Delet(L,8);
    printff(L);

    return 0;
}

#include <stdio.h>
#include <stdlib.h>
//带头结点的循环双链表的节点结构
typedef struct Node
{
    int data;//节点数据
    struct Node* next;//下一个数据所在节点地址
    struct Node* pre;//上一个数据所在节点地址
}Node,*LinkList;
//LinkList=Node*,增加可读性:LinkList指头指针,Node*节点的指针,两者其实没有本质区别

LinkList find(LinkList L,int k);

//初始化带头结点的空链表
LinkList initlist()
{
    Node* p=(Node*)malloc(sizeof(Node));
    if(p==NULL)
    {
        printf("空间分配失败\n");
    }
    else
    {
        p->next=p->pre=p;
    }
    return p;
}

//头插法
LinkList headInsert(LinkList L, int k)
{
    Node* s=(Node*)malloc(sizeof(Node));
    if(s==NULL)
    {
        printf("空间分配失败\n");
        return L;
    }    
    s->data=k;

    s->pre=L;
    s->next=L->next;
    L->next=s;
    s->next->pre=s; 

    return L;
}

//尾插法
LinkList rearInsert(LinkList L,int k)
{
    Node* s=(Node*)malloc(sizeof(Node));
    if(s==NULL)
    {
        printf("空间分配失败\n");
        return L;
    }    
    s->data=k;

    //找尾节点
    Node* p=L;
    while(p->next!=L)
    {
        p=p->next;
    }
    s->next=p->next;
    s->pre=s;
    p->next=s;
    s->next->pre=s;

    return L;    
}

//中间插入,数据x后面插入数据k
LinkList midInsert(LinkList L,int x,int k)
{
    Node* s=(Node*)malloc(sizeof(Node));
    if(s==NULL)
    {
        printf("空间分配失败\n");
        return L;
    }    
    s->data=k;

    //找x所在节点
    Node* p=find(L,x);
    if(p==L)
    {
       printf("数据%d不存在,插入失败\n",x);
       return L; 
    }

    s->pre=p;
    s->next=p->next;
    p->next=s;
    s->next->pre=s;

    return L;

}

//查找
Node* find(LinkList L,int k)
{
    Node* p=L->next;
    while(p!=L&&p->data!=k)
    {
        p=p->next;
    }
    return p;
}

//删除
LinkList delet(LinkList L,int k)
{
    if(L->next==L)
    {
        printf("空链表,删除失败\n");
        return L;
    }
    Node* p=L->next;
    if(p==L)
    {
       printf("数据%d不存在,删除失败\n",k);
       return L; 
    }

    p->pre->next=p->next;
    p->next->pre=p->pre;

    free(p);
    p=NULL;
    return L;
}

void printff(LinkList L)
{
    Node* p=L->next;
    while(p!=L)
    {
        printf("%d ",p->data);
        p=p->next;
    }
    printf("\n");
}

int main()
{
    LinkList L=initlist();
    L=headInsert(L,4);
    L=headInsert(L,5);
    L=headInsert(L,6);
    printff(L);

    L=rearInsert(L,7);
    L=rearInsert(L,9);
    printff(L);

    L=midInsert(L,5,1);
    L=midInsert(L,9,8);
    printff(L);

    L=delet(L,6);
    L=delet(L,7);
    printff(L);

    return 0;
}

        实际上作为最简单的数据结构,链表在操作中要遵守的原则就是不能断表,其他也没什么难点,复习时根据代码画画结构图,就能很清晰的明白线性表的相关原理与操作了。

4.两者的选择

        简单的来说,顺序表操作比较简单,表长变化不大,适合查找某个元素,但没办法动态分配空间,不适合经常进行插入和删除的情况。而链表就适用插入和删除,缺点是存储密度低,查找时间复杂度相对较高。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值