数据结构实验2.1:单链表的基本操作


一,问题描述

设有线性表(34,12,45,64,28,36,45,56),采用链式存储结构。编程实现下列有关单链表的基本操作及运算:
(1) 用表头插入法创建单链表;
(2) 用表尾插入法创建单链表;
(3) 在单链表的第i个结点前插入一个结点;
(4) 删除单链表中第i个结点;
(5) 删除单链表中指定值的结点;
(6) 删除单链表中结点值等于e的所有结点;
(7) 输出链表元素;
(8) 清空单链表。

二,基本要求

(1)链表必须带有附加表头结点,设计链表结点的存储结构;
(2)掌握单链表各种基本操作的算法设计方法和技巧,分析各算法的性能;
(3)在参考程序中的下划线处填上适当的语句或文字,完善参考程序;
(4)设计测试用例,上机调试、测试参考程序,打印测试结果,对测试结果进行分析;
(5)每完成一个步骤,必须及时输出链表中的所有元素,便于观察操作结果。

三,数据结构设计

设计单链表结点结构如下:

typedef struct LNode{
    
    
		ElemType  data;	       //数据域
		struct LNode *next;       //指针域
}LNode, *LinkList;

四,算法分析与设计

(1)插入算法设计

在带头结点的单链表L中第i个结点前插入一个元素值为item的新结点。算法步骤:

  1. 定位指针p指向第i-1个结点;
  2. 若定位失败,算法结束;
  3. 申请新结点s,将新元素item写入s的数据域;
  4. 令新结点s指向p的下一个结点(即第i个结点);
  5. 令p结点指向新结点s。

(2)删除算法设计

在带头结点的单链表L中删除第i个结点,被删结点的元素通过参数e返回。算法步骤:

  1. 定位指针p指向第i-1个结点,q指向p的下一个结点(被删结点);
  2. 若定位失败,算法结束;
  3. 令p结点的指针域指向q结点的下一个结点;
  4. 将q结点的元素保存到参数e中;
  5. 释放q结点空间。

五,示例代码

#define OVERFLOW -1
#define OK 1
#define ERROR 0
#define TRUE 1
#define FLASE 0
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
typedef int Status;
typedef int ElemType;

typedef struct LNode{
    
    
    ElemType  data;	       //数据域
    struct LNode *next;       //指针域
}LNode, *LinkList;

Status  CreatLinkL1(LinkList  &L, int n, ElemType *E) {
    
    
    //用表头插入法逆序建立带头结点的单链表
    int i;
    LinkList p;
    L = (LinkList) malloc(sizeof(LNode));
    if(!L)  return ERROR;
    L->next = NULL;				//建立头结点,假定与元素结点结构相同
    for (i = n-1; i>= 0; --i) 	 					//从后向前输入元素 
    {
    
    
        if(!(p=(LinkList)malloc(sizeof(LNode))))	//生成新结点
            return ERROR;
        p->data=E[i];
        p->next = L->next;  				//新结点插入到表头
        L->next = p;  
    }
    return OK;
}

Status  CreatLinkL2(LinkList  &L, int n, ElemType *E) {
    
    
    //用表尾插入法建立带头结点的单链表
    int i;
    LinkList p, r;
    L = (LinkList) malloc(sizeof(LNode));
    if(!L)  return ERROR;
    r=L;										//初始化尾指针
    for (i = 0; i<n; i++) 	 			
    {
    
    
        if(!(p=(LinkList)malloc(sizeof(LNode))))	//生成新结点
            return ERROR;
        p->data=E[i];
        r->next = p;  				//新结点插入到表尾
        r = p;  
    }
    r->next=NULL;
    return OK;
}

Status  InsertLinkL(LinkList &L, int i, ElemType e) {
    
    
    //在带头结点的单链表L中第i个位置之前插入元素e 
    LinkList  s,  p=L;
    int  k=0;           				//初始化,p指向头结点,k为计数器
    while (p->next!=NULL && k<i-1) {
    
      //定位指针p指向第i-1个元素或p为空
        p=p->next;
        ++k;
    }
    if(!p->next||k>i-1)  return ERROR; 		//定位插入点失败
    if(!(s=(LinkList)malloc(sizeof(LNode)))) 	//申请元素e的结点空间 
        return  OVERFLOW;      	//内存无空闲单元,无法申请到空间 
    s->data=e;
    s->next = p->next; 	   			// 将新结点s插入到链表中 
    p->next = s;
    return  OK;
} // InsertList1 

Status  Del_LinkL1(LinkList L, int i, ElemType &e)    {
    
    
    //在带头结点的单链表L中,删除第i个元素,并由e返回其值
    int k=0;			//初始化,p指向头结点,k为计数器
    LinkList q, p=L;
    while(p->next!=NULL && k<i-1) {
    
        
        //逐一移动指针p,直到p指向第i-1个结点或p为空
        p=p->next;  ++k;
    }
    if(!p->next||k>i-1)  return  ERROR;      //无法删除 
    q=p->next;  	    			// 令q指向待删除的元素结点; 
    p->next = q->next; 			// 将q结点从链表中删除
    e=q->data;  free(q); 
    return  OK;
}

Status  Del_LinkL2(LinkList &L, ElemType  e)
// 在带头结点的单链表中删除键值为e的结点,成功返回OK,失败返回ERROR
{
    
    
    LinkList p, q;    				// p为被删结点前驱,q为被删除结点
    p=L;
    q=L->next; 
    while(q != NULL && q->data != e) {
    
    	//定位指针q指向被删结点
        p=q;
        q=q->next;
    }
    if(q==NULL)   				// 链表为空
        return ERROR;
    p->next=q->next ;   			// 被删结点从链表中脱离
    free(q);        				// 释放被删除结点的空间
    return OK;          			// 删除成功
}

Status  Del_LinkL3(LinkList &L, ElemType  e)
// 在带头结点的单链表L中删除键值为e的所有结点,
// 成功返回TRUE,没找到返回FLASE 
{
    
    
    LinkList p, q;  				// p为被删结点前驱,q为被删除结点
    int tag=FLASE;
    p=L;
    q=L->next; 
    while(q!=NULL)  {
    
    				//定位指针q指向被删结点
        if(q->data==e)
        {
    
    	
            p->next = q->next;		// 让q结点从链表中脱离
            free(q);  tag=TRUE;
        }
        else
            p=q;
        q=p->next;
    }
    return tag; 
}
void PrintLinkL(LinkList L)
// 输出带头结点的单链表中所有结点的键值
{
    
    
    LinkList p=L->next;
    while(p) {
    
    
        printf("%d→", p->data);
        p=p->next;                	// 遍历下一个结点
    }
    printf("∧\n");
}
void FreeLinkL(LinkList &L)
// 释放带头结点的链表空间(含头结点)
{
    
    
    LinkList p, q;
    p=L;
    while(p!=NULL) {
    
    
        q = p;
        p=p->next;
        free(q) ; 
    } 
    L=NULL;  					// 将链表头指针置空
}

void main()						//  主函数。
{
    
    
    ElemType  E[]={
    
    34,12,45,64,28,36,45,56};		// 准备线性表
    int  i, n=8;
    LinkList  head;
    ElemType  rc;
    printf("(1)表头插入法创建单链表……\n");
    if(!CreatLinkL1(head, n, E)) {
    
    
        printf("  内存空间不够,创建失败!\n");
        return;
    }
    else {
    
    
        printf("  创建完成。链表输出:");
        PrintLinkL(head);
    }
    printf("(2)表尾插入法创建单链表……\n");
    FreeLinkL(head);
    if(!CreatLinkL2(head, n, E)) {
    
    
        printf("  内存空间不够,创建失败!\n");
        return;
    }
    else {
    
    
        printf("  创建完成。链表输出:");
        PrintLinkL(head);
    }
    printf("(3)指定位置插入……\n");
    printf("  输入插入位置和新元素值==>");
    scanf("%d%d", &i, &rc);
    if(!InsertLinkL(head, i, rc)) 
        printf("  参数错误或内存空间不够,插入失败!\n");
    else {
    
    
        printf("  插入成功!链表输出:");
        PrintLinkL(head);
    }
    printf("(4)删除指定位置结点……\n");
    printf("  输入被删结点位置==>");
    scanf("%d", &i);
    if(!Del_LinkL1(head, i, rc)) 
        printf("  参数错误,删除失败!\n");
    else {
    
    
        printf("  删除成功!被删结点键值是:%d\n  链表输出:", rc);
        PrintLinkL(head);
    }
    printf("(5)删除指定值结点……\n");
    printf("  输入被删结点的键值==>");
    scanf("%d", &rc);
    if(!Del_LinkL2(head, rc)) 
        printf("  输入的键值不存在!\n");
    else {
    
    
        printf("  删除成功!链表输出:");
        PrintLinkL(head);
    }
    printf("(6)删除指定值所有结点……\n");
    printf("  输入被删结点的键值==>");
    scanf("%d", &rc);
    if(!Del_LinkL3(head, rc)) 
        printf("  输入的键值不存在!\n");
    else {
    
    
        printf("  删除成功!链表输出:");
        PrintLinkL(head);
    }
    printf("(7)清空链表……\n");
    FreeLinkL(head);
    if(!head)
        printf("  链表已清空\n");
    else
        printf("  清空链表失败!");
}

六,操作步骤

1.双击程序图标,启动程序。
在这里插入图片描述

2.新建项目。
在这里插入图片描述
3.选择”空项目“——输入项目名称——单击”确认“按钮。
在这里插入图片描述
4.右击”源文件“——”添加“——选择”新建项“。
在这里插入图片描述

5.选择”C++文件“——输入文件名——单击”添加“按钮。
在这里插入图片描述
6.编写代码。
在这里插入图片描述

7.编译代码。
在这里插入图片描述

8.查看编译结果。
在这里插入图片描述

9.单击绿色小三角,运行项目。
在这里插入图片描述

七,运行效果

1.按照实验要求,进行测试程序。

在这里插入图片描述

2.调试结果。
在这里插入图片描述