线性&链式存储

简述

线性表

线性表是具有相同特性的数据元素组成的一个优有限序列。

线性表的特征

  • 线性表中有且只有一个开始结点(头结点),这个开始结点没有后继结点
  • 线性表中有且只有一个末尾结点(尾结点),这个末尾结点没有后继结点
  • 除去头结点和尾结点,其他结点都有一个前驱结点和一个后继结点

线性表在存储结构上有顺序存储和链式存储两种,不管哪种存储方式,结构均具有以下特征

  • 均匀性:对于同一个线性表来说,数据元素必须具有相同的数据类型和长度
  • 有序性:数据之间的位置是线性的,即存在唯一的“第一个”和“最后一个”数据元素

线性存储

原理

顺序存储:就是在存储器中分配一段连续的存储空间,逻辑上相邻的数据元素,其物理存储地址也是相邻的。

  • 线性表的顺序存储结构是一种随机存取的结构,在高级语言中顺序存储使用数组来实现的
  • 在顺序存储中,系统不需要为表元素之间的逻辑关系增加额外的存储空间,它可以根据给出的下表快速的计算出元素的存储地址。
  • 在顺序表中插入或删除元素,效率会特别的低

操作

  • 插入
  • 删除
  • 修改
  • 查找(顺序查找,二分查找)

实现

首先定义数组的长度

#define MAX 20

定义线性表结构

typedef struct {
    int data[MAX];//用数组存储数据元素,最大容量MAX
    int len = 0;//线性表长度
}seqlist;

初始化

void init(seqlist *list){
  int scan;
    printf("请输入要录入的个数(0~20)");
    scanf("%d",&scan);
    if (scan <=MAX && scan >=0)
    {
        for (int i = 0; i < scan; i++
        	scanf("%d",&list->data[i]);
    list->len = scan;
    }
    else
        printf("超出线性表长度\n");
}

查看线性表元素

void look(seqlist *list){
    for (int i = 0; i < list->len; i++)
    {
        printf("%d ",list->data[i]);
    }
    
}

插入数据

//插入元素(x:插入的位置。y:插入的数字)
void insertData(seqlist * list,int x,int y){
    if(list -> len == MAX){
        printf("error:线性表已满");
    }
    else if(x <=1 && x >= list -> len+1){
        printf("error:线性表插入的数据要与上一个连续");
    }
    else{
        for(int i = list -> len - 1;i >= x-1;i--)
            list -> data[i+1] = list -> data[i];
        list -> data[x-1] = y;
        list -> len++;//每插入一个元素线性表长度加一
    }
}

删除元素

//删除指定位置元素
void deleteData(seqlist *list,int x){
    if(list->len == 0){
        printf("error:线性表为空");
    }
    else if(x <= 1 && x >= list->len){
        printf("error:找不到该位置元素");
    }
    else{
        for(int i = x -1;i < list->len;i++){
            list->data[i] = list->data[i+1];
        }
        list->len--;
    }
}

修改数据

//直接覆盖原来位置上的数据
//修改表中数据(x:要修改的位置,y:修改后的数字)
void reData(seqlist *list,int x,int y){
     if(x >=1 && x <= list->len){
         list ->data[x-1] = y;
     }
    else
        printf("error:该位置为空或线性表中不存在该元素"); 
    look(list);
}

顺序查找

//顺序查找(带哨兵):浪费一个空间。将需要查找的元素(哨兵)
//存入线性表下表为零的位置,从MAX开始遍历,如果线性表中没有该元素,返回的
//下表为0(即哨兵所在位置),否则返回非零值。
int  shun(int a[],int n){
    int i = MAX;
    a[0] = n;
    while (a[i] != n)
    {
        i--;
    }
    return i;
}

二分查找

  首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。如果 begin>last这时候搜索区间为空,说明未找到该元素

int twoFen(int a[],int n,int begin,int last){
//mid中间数下表,begin最小数下标,last最大数下标:
  int mid = (last + begin)/2;
 
    if (a[mid] == n)
    {
        return mid;
    }
    if (begin >= last)
    {
        return -1;
    }
   else if (n < a[mid])
    {
        return twoFen(a,n,begin,mid-1);
    }
    else if(n > a[mid])
    {
        return twoFen(a,n,mid+1,last);
    }

}

链式存储

原理

在链式存储中,结点之间的存储单元地址是不连续的。链式存储中每个结点都包含两个部分:存储元素本身的数据域和存储结点地址的指针域
一般在链表中会一个头结点来保存链表的信息,然后有一个指针指向下一个结点,下一个结点又指向它后面的一个结点,这样直到最后一个结点,它没有后继结点,就指向NULL。
在这里插入图片描述

当在链表中某个位置插入元素时,从空闲空间中分配一个存储单元,然后将两个结点之间的指针断开,上一个结点的指针指向新分配的存储单元,新分配的结点中指针指向下一个结点,不需要移动原来元素的位置。
同样,当删除链表中某个元素时,就断开它与前后两个结点的指针,然后将它的前后两个结点连接起来。
链表不能随机查找元素。(图:在链表中插入数据)
在链表任意位置插入数据

实现

创建链表结构(这里定义一个变量lens用来统计链表长度,也可以单独定义一个包含长度信息的Header类型的结构体,后面不再详细描述)

int lens = 0;//累计长度
typedef struct NodeList{
    int data;//数据域
    struct NodeList *next;//(指针域)指向下一个节点
}list;

//包含长度信息的Header类型结构体
struct Header
{
int length;//记录链表长度
struct Node* next;//指向第一个结点的指针
}

开辟一个链表头结点

//开辟头结点
list *initList(){
//malloc函数在头文件include<stdlib.h>中
//malloc:向系统申请分配指定size个字节的内存空间。
//返回类型是void*类型.void*表示未确定类型的指针。C,C++规定,
//void*类型可以强制转换为任何其它类型的指针。
    list *h  = (list*)malloc(sizeof(list));//头节点分配空间
    h->next = NULL;//初始化,头指针
    return h;//返回头节点地址
}

查看链表所有元素
从头结点(或开始结点)出发,通过工作指针的反复后移而将整个单链表“审视”一遍的方法称为扫描(或遍历)。

//遍历结点
void seeList(list *h){
    list *p;//工作指针
     p = h->next;
    while (p != NULL)
    {
        printf("%d ",p->data);
        p = p->next;//工作指针后移
    }
    printf("\n");
}

初始化(尾插法)
将待插入结点插在终端结点的后面。

//尾插法
void initTail(list *h){
    list *ls,*r = h;//定义一个尾指针r
    int n;
    scanf("%d",&n);
    for (int i = 0; i < n; i++)
    {
        ls = (list*)malloc(sizeof(list));//分配空间
        scanf("%d",&ls->data);
        ls->next = NULL;//新分配的结点next指向NULL(新结点插入后就变成了尾结点)
        r->next = ls;//该结点的地址赋给上一个结点的next
        r = ls;//该节点变成上一个结点,
        lens++;
    }
}

头插法创建单链表是将待插入结点插在头结点的后面(不再详细介绍)
头插法创建单链表

插入元素

//插入元素
void insertData(list *h){
    int loc,x;
    list *p = h;
    printf("请输入插入的位置和数据:\n");
    scanf("%d%d",&loc,&x);
    list *ins = (list*)malloc(sizeof(list));//分配空间
    ins->data = x;
    if(loc <= lens && loc >= 1){
        for(int i = 1;i <loc;i++){
            p = p->next;
        }//找到插入的位置
    ins->next = p->next;
    p->next = ins;
    lens++;
    printf("单链表长度:%d\n",lens);
    }
    else
        printf("插入位置错误!\n");
    seeList(h);
}

删除指定元素


//删除指定元素
void removeLoc(list *h){
    list *p = h;
    int loc;
    printf("请输入删除的位置:\n");
    scanf("%d",&loc);
    for (int i = 1; i < loc; i++)
    {
        p = p->next;
    }
    if (p != NULL)
    {
        list *q = p->next;
        p->next = q->next;
        lens--;//链表长度减一
        free(q);//释放空间
    }
    else
        printf("error\n");
   seeList(h);
    printf("单链表长度:%d\n",lens);
}

查找指定位置元素

//查找指定位置元素
void selectLoc(list *h){
    list *p = h->next;
    int loc;
    printf("请输入要查找的位置:\n");
    scanf("%d",&loc);
    if (loc >= 1 && loc <= lens)
    {
        for (int i = 1; i < loc; i++)
        {
            p = p->next;
        }
        printf("第%d个位置的数据为:%d",loc,p->data);
    }
    else
        printf("找不到该元素\n");
 
}

销毁链表

//销毁表
void Destroy(list *h){
    list *p = h;
    list *q;//保证链表未处理的部分不断开
    while (p ->next != NULL)
    {
        q = p->next;
        free(p);
        p = q;
    }
    lens = 0;
    h ->next = NULL;
}

----------其他操作不再详细叙述----------

循环链表

循环链表是首尾相接的一种链表,它的未结点的后继指针又指向链表的第一个结点。
对于循环链表,从表中任意一个结点出发,都能找到其他所有的结点。

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值