一个菜鸟的数据结构学习之路——(线性表篇)

线性链表的基本操作如下:

  • 相关结构体:

一个线性链表的结构必须包含数据域和指针域

#define OVERFLOW 1
#define ERROR 0
#define OK 1

typedef int Status;

typedef strcut LNode /*节点类型*/
{
    ElemType data; //数据域
    struct LNode *next; //指针域
}*Link,*LinkList,LNode;
  • 函数声明:
Status InitList(LinkList &L); /*构造一个空的线性链表*/

Status ListEmpty(LinkList L); /*线性链表是否为空*/

Status CreatList(LinkList &L,int n); /*逆序输入n个元素的值,建立带头结点L*/

Status ClearList(LinkList &L); /*将线性链表重置为空,并释放原表的节点空间*/

Status DestroyList(LinkList &L); /*销毁线性链表L*/

Status ListLength(LinkList L); /*线性链表长度*/

void FreeNode(Link &p); /*释放p指向的节点*/

Status LocatePos(LinkList L,int i,Link &p); /*返回p指示L中第i个节点的位置*/

LinkList PriorPos(LinkList L,Link p); /*返回p的直接前驱*/

LinkList NextPos(LinkList L,Link p); /*返回p的直接后继*/

LinkList GetHead(LinkList L); /*返回L中头结点的位置*/

LinkList GetLast(LinkList L); /*返回L中尾节点的位置*/

ElemType GetCurElem(LinkList L,int i,ElemType &e); /*已知p指向L中的第i个节点,则返回该节点的值*/

Status InsFirst(Link h,Link s); /*已知h指向头结点,将s所指向的节点插到头结点之前*/

Status ListInsert(LinkList &L,int i,ElemType e); /*在表L第i个位置之前插入元素e*/

Status ListDelete(LinkList &L,int i,ElemType &e); /*删除表第i个位置元素,并用e返回其值*/

Status ListTraverse(LinkList L); /*遍历整个线性链表L*/
  • 构造一个空的线性链表L:

一个带头结点空的线性单链表它的头结点的指针必须指向空

Status InitList(LinkList &L); /*构造一个空的线性链表*/
{
    L = (LinkList)malloc(sizeof(LNode)); //分配内存空间
    if(!L)
        return ERROR;
    L->next = NULL; 
    return OK;
}
  • 判断线性链表是否为空:

只需判断其头指针是否指向空

Status ListEmpty(LinkList L); /*线性链表是否为空*/
{
    if(!L->next) //首节点是否为空
        return ERROR;
    else
        return OK;
}
  • 逆序输入n个元素的值,建立带头结点L:

先创建一个带头结点,并让其指针指向空,然后再创建新节点,并向新节点的数据域内写入相应的值,插入到表头即头结点之后,然后再继续进行该操作,直至插入完n个值

Status CreatList(LinkList &L,int n); /*逆序输入n个元素的值,建立带头结点L*/
{
    L = (LinkList)malloc(sizeof(LNode)); //头结点的创建
    L->next = NULL;
    for(int i = 0;i < n;i++)
    {
        LinkList p = (LinkList)malloc(sizeof(LNode)); //生成新节点
        scanf("%d",&p->data); //输入元素值
        p->next = L->next; //插入到表头
        L->next = p;
    }
    return OK;
}
  • 将线性链表重置为空,并释放原表的节点空间:

先取表L的头指针,然后从头指针指向节点开始删除,删除具体操作为不断的后移头指针指向节点,前一个覆盖后一个,直至指向空,并依次释放无用节点 ,保留头结点

Status ClearList(LinkList &L); /*将线性链表重置为空,并释放原表的节点空间*/
{
    LinkList p; 
    p = L->next; //指针p等于L头指针
    while(p) //p中指针指向非空条件执行循环
    {
        L->next = p->next; //删除除头结点之外所有节点
        free(p); //释放无用节点空间
        p = L->next;
    }
    return OK;
}
  • 销毁线性链表L:

    从头结点开始,不断后移一位,并释放此节点空间,包括头结点

Status DestroyList(LinkList &L); /*销毁线性链表L*/
{
    LinkList p;
    while(L) //L中指针指向非空条件执行循环
    {
        p = L->next; //p指向为头指针L指向节点
        free(p); //释放p指向的节点
        L = p; //后移一位
    }
    return OK;
}
  • 求线性链表长度:

取表的头指针,然后不断后移并计数,最后计数大小即为长度

Status ListLength(LinkList L); /*线性链表长度*/
{
    int i = 0;
    LinkList p;
    p = L->next; //p指向头指针指向空间
    while(p) //p中指针指向非空条件执行循环
    {
        n++; //长度计数
        p = p->next; //后移一位
    }
    return OK;
}
  • 释放p指向的节点:

从p的指向的第一个节点起开始释放并依次后移

void FreeNode(Link &p); /*释放p指向的节点*/
{
    while(p)
    {
    Link q = p->next;
    free(p);
    p = q;
    }
}
  • 返回p指示L中第i个节点的位置:

从表头开始计数,并不断后移节点,直至为空或找到与i相等的节点,此时返回i的上一节点的指针

Status LocatePos(LinkList L,int i,Link &p); /*返回p指示L中第i个节点的位置*/
{
    int n = 0;
    p = L->next; //指针p指向表头
    while(p)
    {
        n++;
        if(n == i) //判断是否为第i个节点
            break;
        p = p->next;
    }
    return p;
}
  • 返回p的直接前驱:

因为直接前驱不能在头指针之前,所以先取p为第二个节点,然后带入查找函数,用p表示位置i的节点,先判断是否第i个节点是否在表中存在,即不能是头结点,然后直接取前驱即可

LinkList PriorPos(LinkList L,Link p); /*返回p的直接前驱*/
{
    ElemType &pre; //直接前驱
    p = p->next->next;
    p = Status LocatePos(L,i,p); //查找p的位置
    if(!p)
    return ERROR;
    else
    {
        L->next = p->next;
        p->next = L; //取直接前继
        pre = p->data;
    }
    return OK;
}
  • 返回p的直接后继:

先取p为头指针,找到p的位置i后,然后带入查找函数,用p表示位置i的节点,然后去取直接后继即可

LinkList NextPos(LinkList L,Link p); /*返回p的直接后继*/
{
    Link next; //直接前驱
    p = L->next; //将第一个元素的地址赋给p
    p = Status LocatePos(L,i,p); //查找p的位置
    if(!p->next)
    return ERROR;
    else
    {
        next = p->next->data; //取直接后继
    }
    return OK;
}
  • 返回L中头结点的位置:

直接取表头地址即为头节点位置

LinkList GetHead(LinkList L); /*返回L中头结点的位置*/
{
    Link p;
    p = L->next; //p为头结点地址
    return p;
}
  • 返回L中尾节点的位置:

先取表头地址,然后不断后移,直到为空时该地址的上一位即为尾节点位置

LinkList GetLast(LinkList L); /*返回L中尾节点的位置*/
{
    Link p;
    p = L->next; //p为头结点地址
    while(p)
    {
        p = p->next;
    }
    return p;
}
  • 已知p指向L中的第i个节点,则返回该节点的值:

从头节点开始计数,直到计数大小等于第i个节点,且对应的该节点必须是线性链表中的节点,最后返回该节点的值

ElemType GetCurElem(LinkList L,int i,ElemType &e); /*已知p指向L中的第i个节点,则返回该节点的值*/
{
    int j = 1;
    Link p = L->next; //p指向L的首节点
    for(;p&&j < i;++j)
    {
        p = p->next; //后移一位
    }
    if(!p||j > i)
        return ERROR;
    e = p->data; //将第i个位置元素值赋给e
    return OK;
}
  • 已知h指向头结点,将s所指向的节点插到头结点之前:

用头插法进行插入

Status InsFirst(Link h,Link s); /*已知h指向头结点,将s所指向的节点插到头结点之前*/
{
    s->next = h->next; //插到表头
    s->next = h;
    return OK;
}
  • 在表L第i个位置之前插入元素e:

从表头开始计数,因为是在i的前一个节点进行插入,所以当计数大小等于i时,且此时节点仍是表中节点时,再创建一个新节点,将元素e写入新节点中,最后将其用头插法插第i个位置处

Status ListInsert(LinkList &L,int i,ElemType e); /*在表L第i个位置之前插入元素e*/
{
    int j = 1;
    LinkList p = L;
    while(p&&j < i)
    {
        p = p->next; //寻找第i个节点
        j++;
    }
    if(!p||j > i) //i小于1或大于i
        return ERROR;
    LinkList s = (LinkList)malloc(sizeof(LNode));
    s->data = e; //给新节点赋值
    s->next = p->next;
    s->next = p; //将新节点插在第i个位置处
}
  • 删除表第i个位置元素,并用e返回其值:

从表头开始计数,当计数大小等于i时,且此时节点仍是表中节点时,取第i个位置节点指针的前驱p,再创一个空节点,令它等于前驱节点的下一个节点,最后返回该节点的数据即可

Status ListDelete(LinkList &L,int i,ElemType &e); /*删除表第i个位置元素,并用e返回其值*/
{
    int j = 1;
    LinkList p = L;
    while(p->next&&j < i) // 寻找第i个节点,并让p指向其前驱
    {
        p = p->next; 
        j++;
    }
    if(!(p->next)||j > i) //i小于1或大于i-1
        return ERROR;
    Link q = p->next;
    q->next = p->next; //删除并释放节点
    free(p->next);
    e = q->data;
    return OK;
}
  • 遍历整个线性链表L:

取表的头节点,不断后移,并输出其中数据,直至节点为空

Status ListTraverse(LinkList L); /*遍历整个线性链表L*/
{
    LinkList p;
    p = L->next; //p指向L的头结点
    while(p != NULL)
    {
        printf("%d",p->data) //依次输出L中的元素
        p = p->next;
    }
    printf("\n")
        return OK;
}
  • 主函数:
#include<stdio.h>
#include<stdlib.h>

typedef strcut LNode /*节点类型*/
{
    ElemType data; //数据域
    struct LNode *next; //指针域
}*Link,*LinkList,LNode;


int main()
{
    LinkList L;
    InitList (L);
    int n,i;
    Link p,q;
    ElemType e;

    while(1)
    {
        printf("*************************************************\n");
        printf("*[1]判断是否为空表    [2]写入n个数              *\n");
        printf("*[3]将表L清空         [4]销毁表L               *\n");
        printf("*[5]线性链表长度      [6]查找第i个元素位置      *\n");
        printf("*[7]返回i的直接前驱   [8]返回i的直接后继        *\n");
        printf("*[9]表L头结点位置     [10]表L中尾节点位置       *\n");
        printf("*[11]返回第i个节点的值[12]将新节点插到头结点前  *\n");
        printf("*[13]表第i个位置前插入[14]删除表第i位置元素     *\n");
        printf("*             [15]遍历整个线性链表              *\n");
        printf("*************** [0]退出系统 ******************\n");
        printf("*************************************************\n");
        printf("请选择:  ");
        scanf("%d",&choice);
        switch(choice)
        {
            case 0: break;

            case 1: ListEmpty(L);
            break;

            case 2: printf("请输入数据个数n,然后依次输入数据\n");
                    scanf("%d",&n);
                    Status CreatList(L,n); //逆序输入n个元素的值,建立带头结点L
            break;

            case 3: ClearList(L); //将线性链表重置为空,并释放原表的节点空间
            break;

            case 4: DestroyList(L); //销毁线性链表L
            break;

            case 5: ListLength(L); //线性链表长度
                    printf("%d\n",n);
            break;

            case 6: printf("请输入位置i\n");
                    scanf("%d",&i);
                    LocatePos(L,i,p);//返回p指示L中第i个节点的位置
            break;

            case 7: printf("请输入位置i\n");
                    scanf("%d",&i);
                    PriorPos(L,p); //返回p的直接前驱
            break;

            case 8: printf("请输入位置i\n");
                    scanf("%d",&i);
                    NextPos(L,p); //返回p的直接后继
            break;

            case 9: GetHead(L); //返回L中头结点的位置
            break;

            case 10: GetLast(L); //返回L中尾节点的位置
            break;

            case 11: printf("请输入位置i\n");
                     scanf("%d",&i);
                     GetCurElem(L,i,e); //已知p指向L中的第i个节点,则返回该节点的值
                     printf("%d\n",e);
            break;

            case 12: printf("请输入s节点中数据\n");
                     scanf("%d",&s->data);
                     InsFirst(Link h,Link s); //已知h指向头结点,将s所指向的节点插到头结点之前
            break;

            case 13: printf("请输入位置i\n");
                     scanf("%d",&i);
                     ListInsert(L,i,e); //在表L第i个位置之前插入元素
            break;

            case 14: printf("请输入位置i\n");
                     scanf("%d",&i);
                     ListDelete(L,i,e); //删除表第i个位置元素,并用e返回其值
                     printf("%d\n",e);
            break;

            case 15: ListTraverse(L); /、遍历整个线性链表L
        }
    }
    FreeNode(p); //释放p指向的节点

以上就是一些线性链表的基本操作,不足的地方会继续完善,希望各位大佬指出不足!互相关注交流学习。

猜你喜欢

转载自blog.csdn.net/qq_42014895/article/details/80273134
今日推荐