c语言实现链表及相关操作

实验二 链表及其应用
一 实验目的
(1)熟练掌握单链表的插入、删除、查找定位等基本算法;
(2)掌握循环链表的概念及其基本算法;
(3)能利用单链表解决简单的问题,如一元多项式的运算。

二 实验原理
课堂笔记中关于链表的全部内容中。(不包括算法)
1.链式储存结构
struct node
{
elemtype data;
Struct node *next;
};
typedef struct node Lnode,*Linklist;
2.说明
(1)采用链式储存结构的线性表称为链表。
(2)struct node 用来定义一个数据元素,链表中的一个数据元素称为一个结点。
(3)每个结点由两部分组成:
data:用来存储结点自身信息,称为数据域。
next:用来存储当前结点直接后继的地址。(指针域) NULL和null表空指针。
3.算法(略)
4.双向链表,循环链表。
5.总结
(1)单链表是为单个数据建立的储存结构。
(2)储存密度低。(节省储存空间)
(3)查找对象不方便。
(4)插入和删除结点不需要移动其他结点,插入和删除效率比较高。
(5)动态储存malloc开辟储存空间
(6)储存结构(物理结构)与逻辑结构(次序)不一致,通过结点的next指针访问或查找结点逻辑次序。

三 实验内容
已知一单链表l中的存储的数据元素的类型为整型。
1 设计算法,建立一个带有空的头结点的单链表,空表。
2 设计算法,向单链表中插入元素。可以采用头插法和尾插法两个方法中任何一种。以-1作为输入元素结束信号。
3 遍历单链表中结点。
4 求链表长度*/
5 按位序查找结点指针*/
6 按位序查找元素值*/
7 按值查找结点位序*/
8 在链表中第i个位置插入元素
9 按值删除节点*/
10 按位序删除节点*/
11查找单链表最大值*/
12查找单链表最小值*/
13 单链表链接

四、实验源代码

#define _CRT_SECURE_NO_WARNINGS 1
/*实验二  链表及其应用*/
#include  /* stdlib.h包括函数malloc()、free()等等。*/
#include

typedef int ElemType;   /* 定义链表中数据元素类型ElemType,int类型 */
#define TRUE 1
#define FALSE 0
#define flag -1

typedef struct node {  //单链表的结点类型
    ElemType data;   //数据域
    struct node* next;   //指针域
} LNode, * LinkList;   //LNode是此结构体的类型名,而LinkList是结构体指针的类型名

LinkList Init_LinkList()/* 初始化单链表,建立一个只有空的头结点的单链表 */
{
    LinkList H = (LinkList)malloc(sizeof(LNode));  //为头结点分配内存
    H->next=NULL;  //头结点的指针域为空,即头结点没有后继结点
    return H;
}

void Create_LinkList1(LinkList H)   /*头插法建立单链表*/
{
    LNode* s;   //指向单链表结点的指针s
    ElemType x;  //待插入的数据元素值x
    printf("请输入单链表结点的内容,以-1为结束标志:\n");//依次输入结点的值
    scanf("%d", &x);
    while (x != flag)
    {
        s=(LinkList)malloc(sizeof(LNode));  //为s指向的结点分配内存
        s->data=x;     //设置s指向的结点的数据域为x
        s->next=H->next;    //s结点的后继结点是头结点的后继结点
        H->next=s;   //头结点的后继结点是S结点
        scanf("%d", &x);
    }
}//后创建的排在前面,先创建的排在后面

void Create_LinkList2(LinkList H)   /*尾插法建立单链表*/
{
    LNode* s, * r = H;   //s指向待插入的结点,r为尾指针指向尾结点
    ElemType x;   //待插入的数据元素值x
    printf("请输入单链表结点的内容,以-1为结束标志:\n");//依次输入结点的值
    scanf("%d", &x);
    while (x != flag)
    {
        s = (LinkList)malloc(sizeof(LNode));    //为s指向的结点分配内存
        s->data=x;  //设置s指向的结点的数据域为x
        s->next=NULL;  //s结点的后继结点为空值
        r->next=s;  //r结点的后继结点为s结点
        r=s;   //尾指针指向新插入进来的s结点rear(尾部的)
        scanf("%d", &x);
    }
}//先创建的在前面,后创建的后面

void Traverse_LinkList(LinkList H)  /* 遍历单链表 */
{
    LinkList p;     //遍历是将单链表中的所有元素都依次访问一次,即从前向后依次打印结点内容
    p=H->next;   //p指向首元素结点--p指向的是空的头节点--无论是头插还是尾插H本身都是不变的仍然为空的头节点
    while (p!=NULL)   //当p结点存在
    {
        printf("%d ", p->data);  //输出p结点的值
        p=p->next;   //p指针后移
    }
    printf("\n");
}

int Length_LinkList(LinkList H)   /*求链表长度*/
{
    LinkList p=H;   //p指向首元素结点
    int n = 0;   //结点计数器j归零
    while (p!=NULL)   //当p结点存在
    {
        n++;   //计数器增1
        p=p->next;   //指针后移
    }
    return n-1;
}

LinkList Get_LinkList(LinkList H, int k)  /*按位序查找结点指针,返回待找结点地址*/
{
    LNode *p=H;  //p指向头结点
    int n = 0;  //结点计数器归零
    while (p->next!=NULL&&n<k )   //当p结点的后继结点存在,且计数器未达到指定位序k--促成立时n为k
    {
        n++;   //计数器+1
        p=p->next;   //指针后移
    }
    if (n==k) return p;  //当计数器达到位序要求,返回指向第k个结点的指针p||p->next!=NULL
    else return NULL;
}//空的头结点为位序1

ElemType Get_LinkList1(LinkList H, int k)  /*按位序查找结点,返回待找结点元素值*/
{
    LNode* p = H;
    int n = 0;//指向空节点--1指向第一个元素结点
    while (p!=NULL && n<k)//结点存在并且计数器没有到达k(0-k)--k+1次--第k个元素结点
    {
        n++;
        p=p->next;
    }
    if (p!=NULL)
        return p->data;//查找成功,返回待找结点值
    else return FALSE;
}

LinkList Get_LinkList2(LinkList H, ElemType x)  /*按值查找结点,返回待找结点地址*/
{
    LNode* p = H;  //p指向首元素结点
    while (p!=NULL)  //当p结点存在
    {
        if (p->data== x)//判断p结点的值是否等于x
            break;
        p=p->next;  //指针后移
    }
    if (p != NULL)  return p;
    else
    {
        printf("链表中不存在值为%d的元素\n", x);
        return NULL;
    }
}

int Get_LinkList3(LinkList H, ElemType x)  /*按值查找结点,返回待找结点位序*/
{
    LNode* p = H;   //p指向首元素结点
    int n = 1;  //设置函数返回位序的初始值为1
    while (p!=NULL && p->data!=x) //当p结点存在且p结点的值不等于待查找值x
    {
        n++;
        p=p->next;
    }  //指针后移,位序+1
    if (p->data==x) return n-1;
    else return FALSE;
}//空的头结点为次序0

int Insert_LinkList1(LinkList H, int i, ElemType x)  //在链表中第i个位置插入元素--类似头插法//不算头结点的位序
{
    LNode* p, * s;
        p = Get_LinkList( H, i-1);   //定位p到i-1个结点,直接调用算法完成

        if (p == NULL)
        {
            printf("插入位置溢出,插入失败!\n");
            return FALSE;
        }
        else
        {
            s = (LinkList)malloc(sizeof(LNode));  //为s结点分配空间
            s->data=x;  //将x写入s结点的数据域
            s->next=p->next;  //设置s结点的后继结点为p结点的后继结点
            p->next=s;  //设置p结点的后继为s结点
            return TRUE;
        }
}

int Del_LinkList1(LinkList H, ElemType x)  /*按值删除节点*/
{
    LinkList p, q;//结点p始终作为q结点的直接前驱,同时向后移动位置,直到找到q结点的值等于x
    p = H;  //设置p
    q = p->next;  //设置q

    while (q!=NULL && q->data!=x)  //当q结点存在且q结点的值不等于x
    {
        p=p->next;  //p结点后移
        q=q->next;  //q结点后移
    }

    if (q==NULL)
    {
        printf("链表中不存在值为%d的结点\n", x);
        return FALSE;
    }
    else
    {
        p->next=q->next;  //从单链表中踢出q结点,即设置p结点的后继为q结点的后继
        free(q);  //释放q结点
        return TRUE;
    }
}//删除节点的关键是找到待删除元素之前的节点

int Del_LinkList2(LinkList H, int i)  /*按位序删除节点*/
{
    LinkList p, q;
    p = Get_LinkList(H, i - 1);  //定位p到第i-1个结点,直接调用算法完成。
    if (p == NULL)
    {
        printf("第%d个结点不存在\n", i - 1);  return FALSE;//空节点也是节点
    }
    else
        if (p->next == NULL)
        {
            printf("第%d个结点不存在\n", i);  return FALSE;
        }
        else
        {
            q=p->next;   //定位q到待删除结点
            p->next=q->next;  //从单链表中踢出q
            free(q);  //释放q结点
            return TRUE;
        }
}

ElemType Max_LinkList(LinkList H)  /*查找单链表最大值*/
{
    LinkList  p;
    ElemType  max;
    p=H; //p指向首元素结点
    max=p->data; //设置首元素结点的值为最大值
    p=p->next; //为下一次比较做准备,p指针后移
    while (p!=NULL) //当p结点存在
    {
        if (p->data>max)  //max和p结点的值比较
            max=p->data; //刷新x的值
        p=p->next;  //p指针后移
    }

    return max;
}

ElemType Min_LinkList(LinkList H)  /*查找单链表最小值*/
{
    LinkList  p;
    ElemType  min;
    p=H->next; //p指向首元素结点(必须是元素结点)
    min=p->data; //设置首元素结点的值为最小值
    p=p->next; //为下一次比较做准备,p指针后移

    while (p!=NULL) //当p结点存在
    {
        if (p->data<min)  //p结点的值和当前最小值min比较
            min=p->data; //刷新x的值
        p=p->next;  //p指针后移
    }

    return min;
}

void  Cat_LinkList(LinkList H1,LinkList H2)//单链表连接,把单链表H2的结点依次连接在单链表H1后面
{
    while (H1->next != NULL)
    {
        H1 = H1->next;
    }
    H1->next = H2->next;
}




int main()
{
    int i, j;
    char ch;
    ElemType e;
    LinkList L,L2;
    ElemType x;

    printf("**************************************************\n");
    printf("               单 链 表 常 用 算 法\n");
    printf("**************************************************\n\n");

    printf("1、初始化单链表:设置其为空表\n");
    L = Init_LinkList();
    if (L)  printf("单链表初始化成功……\n\n");

    printf("2、创建单链表:\n");

    do
    {
        fflush(stdin);
        printf("请选择头插法(T)还是尾插法(W): ");
        scanf("%c", &ch);
    } while (ch != 'T' && ch != 't' && ch != 'W' && ch != 'w');

    if (ch == 'T' || ch == 't')
    {
        printf("您选择的是头插法\n");
        Create_LinkList1(L);
    }
    else
    {
        printf("您选择的是尾插法\n");
        Create_LinkList2(L);
    }
    printf("单链表创建成功……\n\n");

    printf("3、遍历单链表:\n");  /*依次访问单链表中所有元素*/
    Traverse_LinkList(L);

    printf("4、单链表长度为:%d\n\n", Length_LinkList(L));

    printf("5、单链表的插入操作:\n");
    printf("请输入待插入的位序(location):");
    scanf("%d", &i);
    printf("请输入待插入的数据(data):");
    scanf("%d", &e);
    if (Insert_LinkList1(L, i, e)) printf("插入操作执行成功……\n操作结果:");
    Traverse_LinkList(L);
    printf("\n");

    printf("6、单链表的删除操作:\n");
    do
    {
        printf("请选择按值删除(Z)还是按位序删除(X): ");
        scanf("%c", &ch);
    } while (ch != 'Z' && ch != 'z' && ch != 'X' && ch != 'x');
    if (ch == 'Z' || ch == 'z')
    {
        printf("请输入待删除的数据元素值:");
        scanf("%d", &x);
        if (Del_LinkList1(L, x)) printf("删除操作执行成功……\n操作结果:");
        else printf("删除操作未执行成功……\n操作结果:");
    }
    if (ch == 'X' || ch == 'x')
    {
        printf("请输入待删除的数据的位序:");
        scanf("%d", &e);
        if (Del_LinkList2(L, e)) printf("删除操作执行成功……\n操作结果:");
        else printf("删除操作失败……\n操作结果:");
    }
    Traverse_LinkList(L);
    printf("\n");

    printf("7、单链表的查找操作:\n");
    do
    {
        printf("请选择按值查找(Z)还是按位序查找(X): ");
        scanf("%c", &ch);
    } while (ch != 'Z' && ch != 'z' && ch != 'X' && ch != 'x');
    if (ch == 'Z' || ch == 'z')
    {
        printf("请输入待查找的数据元素值:");
        scanf("%d", &x);
        if (Get_LinkList3(L, x)) printf("待查找的值为%d数据元素的位序为:%d\n\n", x, Get_LinkList3(L, x));
        else printf("待查找的值为%d数据元素不存在\n\n", x);
    }
    if (ch == 'X' || ch == 'x')
    {
        do
        {
            printf("请输入待查找的数据元素的位序(1~%d):", Length_LinkList(L));
            scanf("%d", &i);
        } while (i<1 || i> Length_LinkList(L));
        x = Get_LinkList1(L, i);
        printf("待查找的位序为%d数据元素值为:%d \n\n", i, x);
    }

    printf("8、单链表的最大值是:");
    printf("%d\n\n", Max_LinkList(L));

    printf("9、单链表的最小值是:");
    printf("%d\n\n", Min_LinkList(L));

    printf("10、单链表的连接:");
    printf("初始化单链表L2:设置其为空表\n");
    L2=Init_LinkList();
    if(L2)  printf("单链表初始化成功……\n\n");

    printf("2、创建单链表:\n");

    do
    {
    fflush(stdin);
    printf("请选择头插法(T)还是尾插法(W): ");
    scanf("%c",&ch);
    }while(ch!='T' && ch!='t' && ch!='W' && ch!='w');

    if(ch=='T' || ch=='t')
    {
        printf("您选择的是头插法\n");
        Create_LinkList1(L2);}
    else
    {
         printf("您选择的是尾插法\n");
         Create_LinkList2(L2);
    }
    printf("单链表创建成功……\n");

      Cat_LinkList(L,L2);
      printf("单链表连接成功,操作结果是:");
     Traverse_LinkList(L);

    return 0;
}

五、实验结果
c语言实现链表及相关操作_第1张图片

六、实验总结
1.本实验中头结点的序号为0。
2.头插法的存储顺序和逻辑顺序(顺序储存)相反,而尾插法的储存顺序和逻辑顺序则相同(顺序储存)。
3.NULL在实际理解时可以把它当做一个什么都没有但存在的空节点。
4.返回序号或者利用序号时要注意循环结束的条件。
5.判断条件何时是p!=NULL何时是p->next!=NULL也需要注意。
6.注意删除结点的实现。
7.求最大,最小值时需要用链表中的data更新max和min的值。(注意不要用空链表的data值更新min值)

你可能感兴趣的:(c#,数据结构,链表)