线性表—顺序表 and 单链表

468 阅读5分钟

一、线性表——> 顺序存储 ——> 数组

开辟一块连续的内存来存储数据,(数据之间,逻辑相邻,物理地址相邻 )a[1] a[2] ==> 数组 int a[10] = {…}

//顺序表结构设计
typedef struct {
    ElemType *data;//ElemType类型根据实际情况而定,这里假设为int
    int length;
}Sqlist;
// 顺序表 初始化
int InitList(Sqlist *L){
    //为顺序表分配一个大小为MAXSIZE 的数组空间
    L->data =  malloc(sizeof(ElemType) * MAXSIZE);
    
    if(!L->data) exit(0);//存储分配失败退出

    L->length = 0;//空表长度为0

    return 1;
}
// 顺序表的插入    ==> 在顺序表的i位置插入e。起点从1开始,不是0。
int ListInsert(Sqlist *L,int i,ElemType e){
    
    //i值不合法判断
    if((i<1) || (i>L->length+1)) return ERROR;
    //存储空间已满
    if(L->length == MAXSIZE) return ERROR;
 
    //插入数据不在表尾,则先移动出空余位置
    if(i <= L->length){
        for(int j = L->length-1; j>=i-1;j--){
       
            //插入位置以及之后的位置后移动1位
            L->data[j+1] = L->data[j];
        }
    }
    
    L->data[i-1] = e;//将新元素e 放入第i个位置上
    
    ++L->length;//长度+1;
    
    return 1;
}
//顺序表的取值   读取第i个元素
Int temp = L->data[i-1];
//顺序表删除    ==> 删除第i个数据
int ListDelete(Sqlist *L,int i){
    
    //线性表为空
    if(L->length == 0) return 0;
    
    //i值不合法判断
    if((i<1) || (i>L->length+1)) return 0;
    
    for(int j = i; j < L->length;j++){
        //被删除元素之后的元素向前移动 ==》 覆盖第i个元素
        L->data[j-1] = L->data[j];
    }
    //表长度-1;
    L->length --;
    
    return 1;
}
//清空顺序表 顺序线性表L已存在。操作结果:将L重置为空表 */
int ClearList(Sqlist *L) {
    L->length=0;
    return 1;
}
//顺序表查找元素并返回位置 (L已存在)
int LocateElem(Sqlist L,ElemType e)
{
    if (L.length==0) return 0;

    int i;
    for(i=0;i<L.length;i++) {
        if (L.data[i]==e)
            break;
    }
  
    if(i>=L.length) return 0;
    return i+1;
}

二、单链表——>线性表的链式存储,在内存中是不连续的

对于非空的线性表和线性结构特点:

  • 存在唯一 被称为“第一个”的数据元素
  • 存在唯一 被称为“最后一个”的数据元素
  • 存在唯一 被称为“第一个”的数据元素

一对一,有头尾,有前驱和后继。

单链表节点

//定义结点
typedef struct Node{
    ElemType data;    //数据域
    struct Node *next;//带有后继的指针域
}Node;
typedef struct Node * LinkList;//

单链表逻辑状态——带有头结点的链表(头结点数据域为NULL)

//初始化单链表线性表     指针L指向头结点
int InitList(LinkList *L) {//*L == Node **L
    
    *L = (LinkList)malloc(sizeof(Node)); //产生头结点,并使用L指向此头结点
    
    if(*L == NULL) return 0;    //存储空间分配失败
    
    (*L)->next = NULL;//将头结点的指针域置空
    
    return 1;
}

单链表插入——逻辑

//单链表插入    在list 第i位置插入元素e
int ListInsert(LinkList *L,int i,ElemType e){
 
    int j = 1;
    LinkList p,s;
    p = *L;
    
    while (p && j<i) { //寻找第i-1个结点
        p = p->next;
        ++j;
    }
    
    //第i个元素不存在
    if(!p || j>i) return 0;
    
    
    s = (LinkList)malloc(sizeof(Node));//生成新结点s
    
    s->data = e;//将e赋值给s的数值域
   
    s->next = p->next; //将p的后继结点赋值给s的后继
   
    p->next = s; //将s赋值给p的后继
    
    return 1;
}

单链表删除元素 —逻辑

//单链表删除元素  删除L的第i个数据元素,并用e返回其值
int ListDelete(LinkList *L,int i,ElemType *e){
    
    int j = 1;
    LinkList p,q;
    p = *L;
    
    while (p && j<i) {//查找第i-1个结点,p指向该结点
        p = p->next;
        j += 1;
    }
    
    if (!(p->next) || (j>i-1)) return  0; //当i>n 或者 i<1 时,删除位置不合理
    
    q = p->next;//q指向要删除的结点
    
    p->next = q->next;//将q的后继赋值给p的后继
   
    *e = q->data; //将q结点中的数据给e
    
    free(q);//让系统回收此结点,释放内存;
    
    return 1;
}

**单链表取值 **

//单链表取值   获取第i个元素, 用e返回L中第i个数据元素的值
int GetElem(LinkList L,int i,ElemType *e){
    
    int j = 1;
    LinkList p;
    
    //p不为空,且计算j不等于i,则循环继续
    while (p && j<=i) {
        p = p->next; //p指向下一个结点
        ++j;
    }
    
    if(!p || j < i) return 0;//如果p为空或者j>i,则返回0
    
    *e = p->data;//e = p所指的结点的data
    return 1;
}

单链表前插入法 —逻辑

//单链表前插入法     随机产生n个元素值,建立带表头结点的单链线性表L
void CreateListHead(LinkList *L, int n){
    
    LinkList p;
    
    //建立1个带头结点的单链表
    *L = (LinkList)malloc(sizeof(Node));
    (*L)->next = NULL;
    
    for(int i = 0; i < n;i++) {//循环前插入随机数据
        
        p = (LinkList)malloc(sizeof(Node));//生成新结点
       
        p->data = i; //i赋值给新结点的data
        
        p->next = (*L)->next;//p->next = 头结点的L->next
        
        (*L)->next = p; //将结点P插入到头结点之后;
        
    }
}

单链表后插入法

//单链表后插入法    随机产生n个元素值,建立带表头结点的单链线性表L
void CreateListTail(LinkList *L, int n){
    
    LinkList p,r;
 
    //建立1个带头结点的单链表
    *L = (LinkList)malloc(sizeof(Node));
    //r指向尾部的结点
    r = *L;
    
    for (int i=0; i<n; i++) {
        p = (Node *)malloc(sizeof(Node)); //生成新结点
        p->data = i;
        
        r->next = p;//将表尾终端结点的指针指向新结点
       
        r = p; //将当前的新结点定义为表尾终端结点
    }
    
    //将尾指针的next = null
    r->next = NULL;
}

链表结构与顺序存储结构对比

存储分配方式

  • 顺序存储结构用一段连续的存储单元依次存储性表的数据元素。
  • 单链表采用链式存储结构,用一组任意的存储单元存放线性表的数据元素。

时间性能

  • 查找——顺序存储O(1), 单链表O(n)。
  • 插入和删除—— 存储结构需要平均移动半个表长的元素,时间O(n);单链表查找某位置后的指针后,插入删除为O(1)。

空间性能:

  • 顺序存储结构需要预先分配存储空间,分太大,浪费空间,分小了,易溢出。
  • 单链表不需要预先分配存储空间,只要有就可以分配,元素个数不受限制。