线性表

本文详细介绍了线性表的抽象数据类型(ADT),包括顺序存储结构和链式存储结构。顺序存储使用数组实现,适用于小线性表,但插入删除操作效率低。链式存储通过指针连接元素,解决了插入删除效率问题,但检索效率较低。文中还讨论了链表的基本概念,头指针、头结点和首元结点的区别,并提供了线性表链式实现的简介。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线性表ADT定义如下:

ADT 线性表 (List) Data Operation
void initList(L); //创建并初始化一个空线性表,如果成功返回true,修改表传指针
bool listEmpty(L); //判断一个线性表是否为空,不修改表传值
void clearList(
L); //清空一个线性表,成功返回true
bool getElem(L,i,e); //从某个位置取出元素并赋值给e(i的范围是[1,L.length]),修改e的值所以传递一个指针,成功返回true
int locateElem(L,e); //查找线性表中是否有e,如果有返回它的位置(从1开始),否则返回0表示失败
bool listInsert(
L,i,e); //插入一个元素e在第i个元素之前(i的取值范围是[1,L.length+1]) ,成功返回true
bool listDelete(L,i,e); //删除在第i个位置上的元素(i的取值范围是[1,L.length]),删除的元素赋给e,成功返回true
int listLength(L); //返回线性表的元素个数
endADT

线性表有多种如下图: Alt text

顺序存储结构

线性表的顺序存储结构,指的是用一段地址连续的存储单元依次存储线性表的数据元素。

根据线性表的特性,我们会想到用数组来实现它,直接上代码吧,这个多说无意,最好自己去实现一遍。

注意:你会发现函数中的参数虽然都是链表,但有的传得是指针,有的传的是链表本身,会发生这种情况取决于,c语言是按值传递。

  • 当参数是L时,传到函数里面的并不是链表本身,而是他的一个复制品,所以这样会导致你无法修改链表
  • 当参数是*L时,则传到函数里面的是链表的地址,此时就可以通过地址来修改链表。
#include<stdio.h>

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 20  /* 存储空间初始配量 */
typedef int ElemType ; //可以自定义,这里假设为int
typedef int Status ; //Status是函数的类型,其值是函数结果状态码

typedef struct 
{
    ElemType data[MAXSIZE] ;
    int length ;
}SqList;

/* Operation */
/* 当需要修改表的时候传入的参数是*L,查看的时候函数参数为L,因为c是按值传递,不传指针则无法修改原本的链表 */
void InitList(SqList *L) ;
bool ListEmpty(SqList L) ;
void ClearList(SqList *L) ;
bool GetElem(SqList L,int i,ElemType *e) ;
int LocateElem(SqList L,ElemType e) ;
bool ListInsert(SqList *L,int i,ElemType e) ;
bool ListDelete(SqList *L,int i,ElemType *e) ;
void ListPrint(SqList L) ;

main()
{
    SqList L ;
    InitList(&L) ;
    if(ListEmpty(L))
        printf("empty") ;
    ListInsert(&L,1,1) ;

    int i ;
    GetElem(L,1,&i) ;
    printf("%d",i) ;
    printf("%d",LocateElem(L,1)) ;

    ListPrint(L) ;
    ListDelete(&L,1,&i) ;
    ListPrint(L) ;
}

/* 初始化顺序线性表 */
void InitList(SqList *L)
{
    L->length = 0 ;
}

bool ListEmpty(SqList L)
{
    return L.length==0 ;
}

void ClearList(SqList *L)
{
    L->length = 0 ;
}

/*在L中第i个位置之前插入新元素e,L的长度加1 */
bool ListInsert(SqList *L,int i,ElemType e)
{
    int k ;
    if(L->length==MAXSIZE||i<1||i>L->length+1)
        return ERROR ;

    if(i<=L->length)   //插入元素的位置不在表尾
    {
        for(k=L->length-1;k>=i-1;++i)
            L->data[k+1] = L->data[k] ;
    }
    L->data[i-1] = e ;
    L->length++ ;

    return OK ;

}

bool GetElem(SqList L,int i,ElemType *e)
{
    if(i<1||i>L.length)
        return ERROR ;
   *e = L.data[i-1] ; 
   return OK ;
}

int LocateElem(SqList L,ElemType e)
{
    int i ;
    for(i=0;i<L.length||L.data[i]==e;++i)
        ;
    return i==L.length?ERROR:i ;
}

bool ListDelete(SqList *L,int i,ElemType *e)
{
    int k;
    if (L->length==0)               /* 线性表为空 */
        return ERROR;
    if (i < 1 || i>L->length)         /* 删除位置不正确 */
        return ERROR;
    *e=L->data[i-1];
    if (i < L->length)                /* 如果删除不是最后位置 */
    {
        for(k=i;k < L->length; k++)/* 将删除位置后继元素前移 */
            L->data[k-1]=L->data[k];
    }
    L->length--;
    return OK;
}

void ListPrint(SqList L)
{
    if(L.length==0)
        printf("list is empty!") ;
    else
    {
    int i ;
        for(i=0;i<L.length;++i)
        printf("%d->",L.data[i]) ;
    }
}

至此我们的顺序存储结构完成了,那么顺序存储有什么优缺点呢?

优点: 具有简单、运算方便等优点,特别是对于小线性表或长度固定的线性表,采用顺序存储结构的优越性更为突出;

缺点

  1. 顺序存储插入与删除一个元素,必须移动大了的数据元素,以此对大的线性表,特别是在元素的插入和删除很频繁的情况下,采取顺序存储很是不方便,效率低;
  2. 顺序存储空间容易满,出现上溢,程序访问容易出问题,顺序存储结构下,存储空间不便扩充;
  3. 顺序存储空间的分配问题,分多了浪费,分少了空间不足上溢。

由于顺序存储有这些缺点,于是我们就有了另外一种实现方式即链式存储结构

链式存储结构

链式存储不再像顺序存储一样是连续的了,它们存储在内存中并不是连续的,通过指针来将他们相连。

区别: 链式存储结构很好的解决了顺序存储结构插入和删除效率低的问题,但是他检索元素效率低。

链表的基本概念

  • 数据域:我们把存储数据元素信息的域称为数据域。
  • 指针域:存储直接后继位置的域称为指针域。
  • 指针/链:指针域中存储的信息称做指针或链。
  • 结点(Node):数据域与指针域这两部分信息组成数据元素ai的存储映像,称为结点(Node)

image

头指针、头结点与首元结点

对头指针概念的理解,这个很重要。“链表中第一个结点的存储位置叫做头指针”,如果链表有头结点,那么头指针就是指向头结点数据域的指针。画一个图吧。

image

  • 头指针: L存的地址是头结点的地址(如果头结点存在的话),*L表示头结点值(头结点的数据为空)
  • 头结点: 数据域为空,下一个元素即链表中第一个元素。
  • 首元结点: 即图中的元素a1

下图是没有头结点的情况,(有没有完全看个人兴趣,不过建议最好有,因为我发现高手写的代码里面都存在头结点,这样可以保持程序的一致性。)

image

好的,理解了上面这点之后,here we go !

线性表链式实现:

linkedlist.h

#ifndef _List_H

struct Node ;
typedef void* ElementType ;
typedef struct Node *PtrToNode ;
typedef PtrToNode List ;
typedef PtrToNode Position ;

List InitList() ;
List MakeEmpty(List L) ;
int IsEmpty(List L) ;
int IsLast(Position P,List L) ;
Position Find(ElementType X,List L) ;
Position FindPrevious(ElementType X,List L) ;
void Delete(ElementType X,List L) ;
void Insert(ElementType X,List L,Position P) ;
void DeleteList(List L) ;
Position Header(List L) ;
Position First(List L) ;
void PrintList(List L) ;
#endif

struct Node
{
    ElementType Element ;
    Position Next ;
};

linkedlist.c文件

#include<stdio.h>
#include<stdlib.h>
#include "List.h"
main()
{
    int i = 1 ;
    int j = 2 ;
    int k = 3 ;

    List L = InitList() ;
    Insert(&i,L,L) ;
    Insert(&j,L,L->Next) ;
    Insert(&k,L,L->Next->Next) ;
    //printf("%d\n",*(int*)First(L)->Element) ;

    PrintList(L) ;
    Delete(&j,L) ;
    PrintList(L) ;
    DeleteList(L) ;
    PrintList(L) ;
}

List InitList()
{    
    List L = (List)malloc(sizeof(Node)) ;
    if(!L){
        printf("out of space!") ;
        exit(-1) ;
    }
    L->Next = NULL ;
    return L ;
}
int IsEmpty(List L) 
{
   return L->Next == NULL ;
}

int IsLast(Position P,List L) 
{
    return P->Next == NULL ;
}

Position Find(ElementType X,List L)
{
    Position P = L->Next ;
    while(P!=NULL&&X!=P->Element)
        P = P->Next ;
    return P ;
}

//if X not exit ,return NULL
Position FindPrevious(ElementType X,List L)
{
    Position P = L ;
    while(P->Next->Element!=X&&P!=NULL)
        P = P->Next ;
    return P ;
}

void Delete(ElementType X,List L)
{
    Position P ,TempPo ;
    P = FindPrevious(X,L) ;
    if(P)            //if X not exit P = NULL
    {
       TempPo = P->Next ;
       P->Next = TempPo->Next ;
       free(TempPo) ;
    }
}

//insert X to P's back
void Insert(ElementType X,List L,Position P)
{
    Position TempPo ;
    TempPo = (Node*)malloc(sizeof(struct Node)) ;
    if(TempPo == NULL){
        printf("out of space!!!") ;
        exit(-1);
    }
    TempPo->Element = X ;
    TempPo->Next = P->Next ;
    P->Next = TempPo ;
}

void DeleteList(List L)
{
    Position P,Tmp ;
    P = L->Next ;
    L->Next = NULL ;

    while(P!=NULL)
    {
        Tmp = P->Next ;          //notice
        free(P) ;
        P = Tmp ;
    }
}

Position Header(List L)
{
    return L ;
}

Position First(List L)
{
    return L->Next ;
}

void PrintList(List L)
{
    if(IsEmpty(L)){
        printf("list is empty!") ;
        exit(-1) ;
    }
    Position pCurrent ;
    pCurrent = L->Next ;
    while(pCurrent){
        printf("%d->",*(int*)pCurrent->Element) ;
        pCurrent = pCurrent->Next ;
    }
    printf("\n") ;
}

循环链表

image

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值