数据结构——单链表

来源:互联网 发布:java接口有哪些 编辑:程序博客网 时间:2024/06/13 18:28

1. 相关概念

1. 1 单链表定义

单链表是线性表链式存储的一种,其储存不连续。单链表的数据结构中包含两个变量:结点数据data和指向下一结点的指针next。一个结点只知道下一个结点的地址。
注:一个单链表必须有一个头指针,指向单链表中的第一个结点。否则链表会在内存中丢失。

1. 2 关于头指针

链表中第一个结点的存储位置叫做头指针,那么整个链表的存取就必须是从头指针开始进行了。之后的每一个结点,其实就是上一个的后继指针指向的位置。无论链表是否为空,头指针均不为空。头指针是链表的必要元素。
若链表有头结点,则头指针就是指向链表头结点的指针。如下图所示:

这里写图片描述

1. 3 关于头结点

为什么要设置头结点呢?

  • 头指针具有标识作用,故常用头指针冠以链表的名字。

  • 有了头结点后,对在第一个元素结点前插入结点和删除第一个结点,其操作与对其它结点的操作统一了。

  • 注:下面的程序均指带有头结点的单链表。

1.4 结点定义

typedef int datatype;typedef struct LNode{    datatype data;               /*结点数据*/    struct LNode *next;     /*下一结点的指针*/}LNode;typedef struct LNode *LinkList;

2. 链表的基本操作

2.1 ADT 单链表(List)

void InitList( LinkList *L ); //初始化空表void HeadCreatList(LinkList L); //头插法创建链表void RearCreatList( LinkList L ); //尾插法创建链表void PrintList(LinkList L); //遍历链表void HeadInsert( LinkList L, int e ); //头插法void TailInsert( LinkList L, int e ); //尾插法BOOL ListInsert( LinkList L, int i, int e ); //固定位置插入BOOL ListSearch( LinkList L, int i, int *e ); //固定位置寻找BOOL DeleteList( LinkList L, int i , int *e ); //固定位置删除元素int ListLength( LinkList L ); //链表长度BOOL DestroyList( LinkList *L ); //销毁链表BOOL IsListEmpty( LinkList L ); //链表是否为空

2 .2 相关实现

相关宏定义

#define OVERFLOW -2#define OK        1#define ERROR     0

2.2.1 单链表的初始化

void InitList( LinkList *L)   /* 这里的输入参数为头指针的地址,因为该函数是要对头指针进行分配地址,所以需要将头指针的地址输入,类似函数的传址*/{    /*申请一个头节点,L为指向头结点的头指针*/    *L = ( LinkList ) malloc ( sizeof(LNode) );      if( !*L )    {        printf("单链表创建失败!\n");        exit( OVERFLOW ); /*#define OVERFLOW -2*/    }       (*L) -> next = NULL;   /*将头结点的next指针设置为NULL,表明表为空*/}

2.2.2 单链表的整表创建

void CreatList( LinkList L )    /*函数的输入即为链表的头指针L*/{    int d= 0;    LinkList p, q;    q = L;    while( scanf( "%d" , &d) != EOF ) /*从终端读取数据,遇到ctrl+d结束*/    {        p = ( LinkList ) malloc ( sizeof(LNode) ); /*申请一个新的结点*/        if( !p )            exit( OVERFLOW );        p -> data = d;   /*给新结点赋值*/        q -> next = p;   /*修改指针,将上一个结点的next设置为当前结点*/        q = p;           /*将当前结点设置为上一结点*/    }    q -> next = NULL;  /*将最后一个结点的next设置为NULL,表示表尾*/}

2.2.3 单链表的打印

void PrintList( LinkList L ) /*函数输入为链表的头指针*/{    LinkList p;    p = L -> next; /*L是头指针,L->next是指向第一个结点的地址*/    while( p != NULL )  /*没有访问到表尾*/    {        printf( "%d" , p -> data );  /*打印当前结点p的数据*/        p = p -> next;    /*访问下一个结点*/    }    return OK;}

2.2.4 单链表的头插法

方法如下图所示:
这里写图片描述

void HeadInsert( LinkList L, int e )/*不改变头指针,所以函数的输入为单链表的头指针L, e为待插入的元素*/{    LinkList s, q;    s = ( LinkList )malloc( sizeof(LNode));  /*申请新结点s*/    if( !s )        exit( OVERFLOW );    s -> data = e;     /*将e写入新结点*/    q = L -> next;     /*指针修改*/    L -> next = s;    s -> next = q;    return OK;}

2.2.5 单链表的尾插法

方法如下图所示:
这里写图片描述

int TailInsert( LinkList L, int e )/*不改变头指针,所以函数的输入为单链表的头指针L, e为待插入的元素*/{    LinkList p, s;    p = L;    while( p->next )   /*判断是否到表尾*/    {        p = p -> next;    }    s = ( LinkList )malloc( sizeof(LNode));  /*申请新结点s*/    if( !s )        exit( OVERFLOW );    s -> data = e;     /*将e写入新结点*/    p -> next = s;     /*将原表尾的指针指向新结点*/    return OK;}

2.2.6 单链表的固定位置插入

方法如下图所示:
这里写图片描述

int ListInsert( LinkList L, int i, int e )/*不改变头指针,所以函数的输入为单链表的头指针L, i为插入的位置, e为待插入的元素*/{    int j = 1;    LinkList p, s;    p = L;    while( p && j < i) /*定位到第i-1个位置*/    {        p = p -> next;        j++;    }    if( !p || j > i )        return ERROR;    s = ( LinkList )malloc( sizeof(LNode));  /*申请新结点s*/    if( !s )        exit( OVERFLOW );    s -> data = e;     /*将e写入新结点*/    s -> next = p -> next;   /*调整指针*/    p -> next = s;    return OK;}

2.2.7 单链表的固定位置删除

方法如下图所示:
这里写图片描述

void DeleteList( LinkList L, int i , int *e ) /*不改变L,i代表位置,*e代表删除的数据*/{    int j = 1;    LinkList p, q;    p = L -> next;   /*p指向第一个结点*/     while( p && j < i - 1 ) /*将p指向第i-1个结点*/    {        p = p -> next;        j++;    }    if( p || j > i)       /*i的位置错误*/        return ERROR;    q = p -> next;              /*q指向第i个顶点*/    p -> next = q -> next;             *e = q -> data;               free(q);}

2.2.8 单链表的固定位置查找

int ListSearch( LinkList L, int i, int *e )/*不改变头指针,所以函数的输入为单链表的头指针L, i为查找的位置, *e为待查找位置的元素*/{    int j = 1;    LinkList p = L -> next; /*p指向第一个结点*/    while( p && j < i) /*定位到第i个位置*/    {        p = p -> next;        j++;    }    if( !p || j > i ){    /*i的位置输入错误*/        printf("查找的位置错误!\n");        return ERROR;    }    *e = p -> data;    return OK;}

2.2.9 单链表的空判断

int IsListEmpty( LinkList L ){    return L-> next == NULL;}

2.2.10 单链表的长度

int ListLength( LinkList L ){    int count = 0;    LinkList p = L -> next; /*p指向第一个结点*/    while( p ) {  /*遍历直到表尾*/        count++;    /*每遍历一个结点,计数+1*/        p = p -> next;    }    return count;}

2.2.11 单链表的清空

void ClearList( LinkList L ){    LinkList p, q;    p = L ->next;    /*p指向第一个结点*/    while( p )  /*没到表尾*/    {        q = p -> next;        free( p );   /*保存当前结点的下一节点地址,释放当前结点地址的内存*/        p = q;    }    L -> next = NULL; /*将头指针的next设置为NULL*/}

2.2.12 单链表的整表销毁

void DestroyList( LinkList *L ) /*为什么输入的不是头指针L,而是头指针的地址呢? */{    LinkList p;    if( NULL == *L )   /*单链表不存在*/        return ERROR;     while( *L )    {        p = *L -> next;        free( *L );        *L = p;    }}

3 完整的测试程序

#include <stdio.h>#include <stdlib.h>#define OVERFLOW -2#define OK        1#define ERROR     0#define BOOL      inttypedef int datatype;typedef struct LNode {    datatype data;               /*结点数据*/    struct LNode *next;     /*下一结点的指针*/}LNode;typedef struct LNode *LinkList;void InitList( LinkList *L ); //初始化空表void HeadCreatList(LinkList L); //头插法创建链表void RearCreatList( LinkList L ); //尾插法创建链表void PrintList(LinkList L); //遍历链表void HeadInsert( LinkList L, int e ); //头插法void TailInsert( LinkList L, int e ); //尾插法BOOL ListInsert( LinkList L, int i, int e ); //固定位置插入BOOL ListSearch( LinkList L, int i, int *e ); //固定位置寻找BOOL DeleteList( LinkList L, int i , int *e ); //固定位置删除元素int ListLength( LinkList L ); //链表长度BOOL DestroyList( LinkList *L ); //销毁链表BOOL IsListEmpty( LinkList L ); //链表是否为空int main(){    LinkList L; /*L为头指针,单链表带头结点*/    /*初始化空表*/    InitList(&L);    if( !IsListEmpty( L ) ){        printf("单链表非空,初始化错误!");        return ERROR;    }    else        printf("单链表初始化完成!\n输入你保存到链表中的数据:\n");    RearCreatList(L);  //尾插法创建链表    PrintList(L);       //打印    printf("\n");    /*插入操作*/    HeadInsert( L, 5 );  //头插法    HeadInsert( L, 6 );    HeadInsert( L, 11 );    PrintList(L);    printf("\n");    TailInsert( L, 7 ); //尾插法插法    TailInsert( L, 8 );    PrintList(L);    printf("\n");    ListInsert(  L, 3, 10 ); //固定位置插入    ListInsert(  L, 8, 12 );    PrintList(L);    printf("\n");    /*长度判断*/    printf("单链表的长度:%d\n", ListLength( L ));    int *d;    ListSearch( L, 6, d);    /*固定位置查找*/    printf( "第6个位置的元素值:%d\n", *d );    ListSearch( L, 20, d);    DeleteList( L, 5 , d );    printf( "第5个被删除的元素值:%d\n", *d );    DeleteList( L, 10 , d );    printf( "第10被删除的元素值:%d\n", *d );    PrintList(L);    printf("\n");    if(DestroyList( &L ) );        printf("单链表销毁完成!\n");    return 0;}void InitList( LinkList *L ) {    /*申请一个头节点,L为指向头结点的头指针*/    *L = (LinkList)malloc(sizeof(LNode));    if (!*L)    {        printf("单链表初始化失败!\n");        exit(OVERFLOW);    }    (*L)->next = NULL;    return ;}void HeadCreatList(LinkList L){    int d;    while ( scanf("%d", &d) != EOF )    {        LinkList p;        p = ( LinkList )malloc(sizeof(LNode));        if (!p)            exit(OVERFLOW);        p->data = d;        p->next = L->next;        L->next = p;    }}void RearCreatList( LinkList L ){    int d;    LinkList p, q;    q = L;    while ( scanf("%d", &d) != EOF )    {        p = ( LNode* ) malloc ( sizeof (LNode) );        if (!p)            exit(OVERFLOW);        p -> data = d;        q -> next = p;        q = p;    }    q -> next = NULL;}void PrintList(LinkList L){    LinkList p;    p = L->next;    printf("单链表中的元素为:");    while (p != NULL)    {        printf("%d ", p->data);        p = p->next;    }}void HeadInsert( LinkList L, int e ){    LinkList  q, s;    s = ( LinkList )malloc( sizeof(LNode));    s -> data = e;    q = L -> next;    L -> next = s;    s -> next = q;}void TailInsert( LinkList L, int e ){    LinkList p, s;    p = L;    while( p->next )    {        p = p -> next;    }    s = ( LinkList )malloc( sizeof(LNode));  /*申请新结点s*/    if( !s )        exit( OVERFLOW );    s -> data = e;     /*将e写入新结点*/    p -> next = s;}BOOL ListInsert( LinkList L, int i, int e )/*不改变头指针,所以函数的输入为单链表的头指针L, i为插入的位置, e为待插入的元素*/{    int j = 1;    LinkList p, s;    p = L -> next;     //p指向第一个结点    while( p && j < i-1) /*定位到第i-1个位置*/    {        p = p -> next;        j++;    }    if( !p || j > i )        return ERROR;    s = ( LinkList )malloc( sizeof(LNode));  /*申请新结点s*/    if( !s )        exit( OVERFLOW );    s -> data = e;     /*将e写入新结点*/    s -> next = p -> next;   /*调整指针*/    p -> next = s;}BOOL IsListEmpty( LinkList L ){    return L-> next == NULL;}BOOL ListSearch( LinkList L, int i, int *e )/*不改变头指针,所以函数的输入为单链表的头指针L, i为查找的位置, *e为待查找位置的元素*/{    int j = 1;    LinkList p = L -> next;    while( p && j < i) /*定位到第i个位置*/    {        p = p -> next;        j++;    }    if( !p || j > i ){        printf( "查找的位置 %d 错误!\n", i);        return ERROR;    }    *e = p -> data;    return OK;}int ListLength( LinkList L ){    int i = 0;    LinkList p = L -> next; /*p指向第一个结点*/    while( p ) {        i++;        p = p -> next;    }    return i;}BOOL DestroyList( LinkList *L ){    LinkList p ;    while( *L )    {        p = (*L) -> next;        free(*L);        (*L) = p;       }    return OK;}BOOL DeleteList( LinkList L, int i , int *e ){    int j = 1;    LinkList p = L -> next, q;    while( p && j < i-1 )    {        p = p -> next;        j++;    }    if( !p || j > i)        return ERROR;    q = p -> next;    p -> next = q -> next;    *e = q -> data;    free(q);    return OK;}

运行结果如下:
这里写图片描述