单链表

线性表的链式存储

线性表的链式表示又称为非顺序映像或链式映像

结点在存储器中位置任意的,即逻辑上相邻数据元素在物理上不一定相邻;链表中的逻辑次序和物理次序不一定相同;访问时只能通过头指针进入链表,并通过每个结点的指针域依次向后顺序扫描其他结点(顺序存取法),因此访问各个结点时间不同

用一组物理位置任意的存储单元来存放线性表的数据元素


链表:n个结点由指针链组成一个链表

结点:数据元素的存储映像

  • 数据域:存储元素数值数据
  • 指针域 :存储直接后继的存储位置

单链表/线性链表:结点只有一个指针域(后继结点地址)的链表;单链表由头指针唯一确定,因此单链表可以用头指针名字来命名

双链表:结点有两个指针域的链表(一个存放后继结点地址,一个存放直接前驱地址)

循环链表:首尾相连的链表

image


头指针:指向链表第一个结点的指针

首元结点:链表中存储第一个数据元素a1的结点

头结点:在链表的首元结点之前附设的一个结点;数据域内可以为,也可以存放线性表长等附加信息,但此结点不能计入链表长度值

  • 便于首元结点的处理

  • 便于空表和非空表的统一处理

image


空表:

  • 带头结点

    image

  • 不带头结点

    image


例:26个英文字母小写存储结构

逻辑结构(a,b,c,d,...,z)

链式存储结构

image


若使用链式存储结构:

image


单链表的定义

typedef struct Lnode{
    int data;
    struct Lnode *next;
}LNode,*LinkList;

单链表的初始化

int InitList_L(LinkList L){
    L= (LinkList)malloc(sizeof(LNode));
    L->next=NULL;
    return 1;
}

单链表的是否为空

int ListEmpty(LinkList L){
    if(L->next){
        return 0;
    } else return 1;
}

单链表的销毁(销毁头结点)

从头指针开始依次释放所有结点:P=L;L=L->next;free(P)

结束条件:L==NULL

int DestroyList_L(LinkList L){
    LinkList p;
    while (L){
        p=L;
        L=L->next;
        free(p);
    }
  return 1;
}

清空单链表(保留头结点)

结束条件:p==NULL

int ClearList(LinkList L){
    LinkList p,q;
    p=L->next;
    while(p){
        q=p->next;
        free(p);
        p=q;
    }
    L->next=NULL;
    return 1;
}

求单链表的表长

int ListLength_L(LinkList L){
    LinkList p;
    p=L->next;
    int i=0;
    while (p){
        i++;
        p=p->next;
    }
    return i;
}

单链表的取值(第i个元素的内容)

循环中的i++, ++i结果一样,但大量数据时++i性能比i++好,i++由于是在使用当前值之后再+1,需要一个临时的变量来转存;而++i则是在直接+1,省去了对内存的操作的环节

int GetElem(LinkList L, int i,int *e){
    LinkList p;
    p=L->next;
    int j=1;
    while(p&&j<i){
        p=p->next;
        j++;
    }
    if(!p||j>i){
        return  0;
    }
    e=p->data;
    return 1;
}

单链表的按值查找(返回地址)

LinkList LocateElem_L(LinkList L,int e){
    LinkList p;
    p=L->next;
    while (p&&p->data!=e){
        p=p->next;
    }
    return p;
}


单链表的按值查找(返回位置序号)

int LocatedElem_L(LinkList L,int e){
    LinkList p;
    p=L->next;
    int j=1;
    while (p&&p->data!=e){
        p=p->next;
        j++;
    }
    if(p){
        return j;
    }else return 0;
}

单链表的插入操作

在第i个结点前插入值为e的新结点:s->next=p->next;p->next=s;

int ListInsert_L(LinkList L,int i,int e){
    LinkList p,s;
    p=L;
    int j=0;
    while (p&&j<i-1){
        p=p->next;
        j++;
    }
    if(!p||j>i-1) return 0;
    s->data=e;
    s->next=p->next;
    p->next=s;
    return 1;
}

单链表删除第i个元素

p->next=p->next->next; 保存删除结点的数据域

int ListDelete(LinkList L,int i,int *e){
    LinkList p,q;
    p=L;
    int j=0;
    while (p&&j<i-1){
        p=p->next;
        j++;
    }
    if(!(p->next)||j>i-1) return 0;
    q=p->next;
    p->next=q->next;
    *e=q->data;
    free(q);
    return 1;
}

单链表的插入和删除:不需要移动元素,只需要修改指针,时间复杂度为O(1),如果从头查找前驱结点时间复杂度为O(n)

单链表的查找:因只能顺序存取,在查找时要从头指针开始,查找时间复杂度为O(n)


头插法建立单链表

从一个空表开始将最后一个结点依次重复将各结点插入链表的头部(倒位序):p->next=L->next;L->next=p;

void CreateList_H(LinkList L,int n){
    L=(LinkList)malloc(sizeof(LNode));
    L=NULL;
    for (int i = n; i >0 ; i--) {
        LinkList p;
        p=(LinkList)malloc(sizeof(LNode));
        scanf((const char *) p->data);
        p->next=L->next;
        L->next=p;
    }
}

算法时间复杂度是O(n)


尾插法建立单链表

从一个空表开始将最后一个结点依次重复将各结点插入链表的尾部,尾指针r指针链表的尾结点(正位序):p->data=ai;p->next=NULL;r->next=p;r=p;

void CreateList_R(LinkList L,int n){
    LinkList r;
    L=(LinkList)malloc(sizeof(LNode));
    L->next=NULL;
    r=L;
    for (int i = 0; i <n ; i++) {
        LinkList p;
        p=(LinkList)malloc(sizeof(LNode));
        scanf((const char *) p->data);
        p->next=NULL;
        r->next=p;
        r=p;
    }
}

算法时间复杂度是O(n)


线性表链式存储的合并

  1. 如用La的头结点作Lc的头结点:Lc=pc=La;
  2. 比较La与Lb表中值大小:pa->data<pb->data; pc->next=pa;pc=pa;pa=pa->next;
  3. 将剩余表中元素插入Lc:pc->next=pa?pa:pb;
typedef struct Lnode{
    int data;
    struct Lnode *next;
}LNode,*LinkList;

void MergeList_L(LinkList La,LinkList Lb,LinkList Lc){
    LinkList pa,pb,pc;
    pc=Lc=La;
    pa=La->next;
    pb=Lb->next;
    while (pa&&pb){
        if(pa->data<=pb->data){
            pc->next=pa;
            pc=pa;
            pa=pa->next;
        } else{
            pc->next=pb;
            pc=pb;
            pb=pb->next;
        }
    }
    pc->next=pa?pa:pb;
    free(Lb);
}

时间复杂度是O(ListLength(La)+ListLength(Lb))

posted @ 2023-01-30 17:26  原语  阅读(268)  评论(0)    收藏  举报