数据结构实验:线性表的顺序表示和链式表示及插入、删除、查找运算

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构实验:线性表的顺序表示和链式表示及插入、删除、查找运算相关的知识,希望对你有一定的参考价值。

用C语言完成以下实验:
实验内容及要求:
(1)分别建立包含10个数据元素的顺序线性表和链式线性表;
(2)从键盘输入一个数据元素,插入到线性表中第k(包含0号位置)个位置;
(3)从键盘输入一个数据元素关键字或位置k(包含1号位置),从线性表中删除相应数据元素;
(4)能完成查找功能;
(5)给出程序及插入、删除前和插入、删除后线性表结果。

我不会做。。。,希望有高手能帮帮忙,要C语言的啊,谢谢了

这是我的第一次上机实验课的内容来呢!
#include <stdio.h>
#include <stdlib.h>
#include<conio.h>

struct list //结点类型
int data;
struct list *next;
;

struct list *head;//声明结点指针
int static length;//声明表长变量

struct list *creat_n()//创建有n个元素的链表

struct list *q,*p,*head=NULL;

printf("\n输入你所要创建的结点数: ");
scanf("%d",&length);
head=p=(list*)malloc(sizeof(list)); //创建一个新结点并用头指针指向它
printf("输入该结点的值: ");
scanf("%d", &p->data);
p->next=NULL;
for(int i=length-1;i>=1;i--)

q=p;
p=(list*)malloc(sizeof(list)); //创建新结点
printf("输入该结点的值: ");
scanf("%d", &p->data);
q->next=p;


printf("输入完毕\n\n");
p->next=NULL;

return head;


struct list * output()//输出表长与结点值函数

struct list *p;
p=head;

printf("\n当前链表中存有的元素:\n");
while(p!=NULL)

printf("%d\n",p->data);
p=p->next;

printf("当前的表长是: %d\n\n",length);//输出当前表长
return head;

void insert()//插入结点函数

struct list *k,*p,*q;
int x;
printf("请输入你要在哪个结点值之前插入新结点: ");
scanf("%d",&x);

k=(list*)malloc(sizeof(list));//创建新结点
printf("请输入新结点的值: ");
scanf("%d",&k->data);
k->next=NULL;
if(head==NULL)//若链表为空,则直接入链表

head=k;
length=length+1;
printf("插入成功\n\n");

else if(head->data==x)//在第一个结点前插入新结点

k->next=head;
head=k;
printf("插入成功\n\n");
length=length+1;

else

q=head;
p=head->next;
while((p != NULL) && (p->data != x))//找出值为X的结点的位置

q = p;
p = p->next;


if (p == NULL)

q->next=k;//在链表末插入新结点
printf("插入成功\n");
length=length+1;

else if(p->data == x)//在要求的X结点前插入新结点

k->next=p;
q->next=k;
printf("插入成功\n\n");
length=length+1;



output();


int delet()//删除结点函数

struct list *q,*p;
int x,y;
printf("请输入你所要删除的结点值: ");
scanf("%d",&x);

if(head==NULL)//表空

printf("表空\n");
return 0 ;

else if(x==head->data)//第一个结点为删除的结点

q=head;
head=head->next;
y=q->data;
free(q);

printf("删除成功\n\n");
length=length-1;
output();
return(y);


else

q=head;
p=head->next;
while((p != NULL) && (p->data != x))//找出值为X的结点

q=p;
p=p->next;

if(p==NULL)

printf("没有删除对象\n");


if(x==p->data)//删除值为X的结点

q->next=p->next;
y=p->data;
free(p);
printf("删除成功\n\n");
length=length-1;
output();
return (y);

else

printf("表中没有指定的结点\n");
output();
return 0;


return 0;



void find()

struct list *p;
int k,x,i=1;
char y,n;

LOOP:
p=head;
printf("请输入你要查找的结点值: ");
scanf("%d",&x);
while(p->data!=x)

p=p->next;
i++;


printf("你所查找的结点是表中第 %d 个结点!\n\n",i);
printf("是否要继续查找,请输入y/n\n\n");
k=getch();
if(k=='y')

i=1;
goto LOOP;

else
return;


void main()

printf("计Y062 200502001052 李抱和\n\n");
int a;
LOOP:
printf(" *****************\n");
printf(" ** 1 创建链表 **\n");
printf(" ** 2 链表输出 **\n");
printf(" ** 3 插入结点 **\n");
printf(" ** 4 删除结点 **\n");
printf(" ** 5 查找结点 **\n");
printf(" *****************\n");
printf("\n请选择: ");
scanf("%d",&a);

switch(a)

case 1 :
head=creat_n();
break;
case 2 :
output();
break;
case 3 :
insert();
break;
case 4 :
delet();
break;
case 5 :
find();
break;


goto LOOP;
参考技术A #include<stdio.h>#include<stdlib.h>
typedef int ElemType;

/*构建结点结构体 */typedef struct LNode

int data;
struct LNode * next;
LNode, * LinkList;
/*用于创建链表的函数 */
/*反序构建的*/
LinkList CreateList_L(LinkList L, int n)

int i;
LinkList p;
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
for(i = 0; i < n; ++i)

p = (LinkList)malloc(sizeof(LNode));
scanf("%d",&p->data);
p->next = L->next;
L->next = p;

return L;


/*用于查找的函数*/LinkList GetElem_L(LinkList L,int i,ElemType &e)

LinkList p = L;
int j;
p = L->next;j = 1;
while(p && j<i)

p = p->next;
++j;

if(!p || j>i)

printf("表中无此位置。\n");
return L;

e = p->data;
printf("%d\n",e);
return L;


/* 用于插入结点的函数 */LinkList ListInsert_L(LinkList L, int i, int newnode)

LinkList p = L;
LinkList s;
int j = 0;
while(p&&j<i-1)

p = p->next;
++j;

if(!p||j>i-1)

printf("位置小于1或大于表长。\n");
return L;

s = (LinkList)malloc(sizeof(LNode));
s->data = newnode;
s->next = p->next;
p->next = s;
return L;



/* 用于删除结点的函数 */LinkList ListDelete_L(LinkList L, int i)

LinkList p = L;
LinkList s;
int j=0;
while(p->next&&j<i-1)

p = p->next;
++j;

if(!(p->next)||j>i-1)

printf("删除位置不合理。\n");
return L;

s = p->next;
p->next = s->next;
printf("%s%d\n","被删除的结点是:",s->data);
free(s);
return L;


/*用于遍历链表的函数 */
void ListDisp_L(LinkList L)

LinkList p;
int i=0;
p = L->next;

while(p)

printf("%d:%d\n", ++i,p->data);
p = p->next;




int main()
int where;
int value;
int count;
int search;
int output;

LinkList L; printf("请输入链表初始结点数:");
scanf("%d",&count);
printf("请输入各个结点数值,每输入一个按回车确认:\n");
L = CreateList_L(L, count);

printf("请输入要查找的位置:");
scanf("%d",&search);
L = GetElem_L(L,search,output);

printf("请输入插入的位置:");
scanf("%d", &where);
printf("请输入要插入的数值:");
scanf("%d", &value);
L = ListInsert_L(L,where,value);
ListDisp_L(L);

printf("请输入要删除的位置:"); scanf("%d",&where);
L = ListDelete_L(L,where);
ListDisp_L(L);

return 0;
参考技术B   一.实验目的
熟悉线性表的表示及实现方法。掌握线性表的基本操作:插入、删除、查找等运算在链式存储结构上的运算。
二.实验内容
1.问题描述
设计一个单链表基本操作的程序
2.基本要求
编写一个程序实现单链表的各种基本运算,包括:
(1)初始化单链表;
(2)依次插入n个元素(自行编写),建立带头结点的单链表;
(3)输出单链表;
(4)计算单链表的长度;
(5)判断单链表是否为空;
(6)输出单链表的第i个元素;
(7)在第i个元素位置上插入一个数据元素;
(8)删除单链表的第i个元素。
三.实验环境
Microsoft Visual C++ 6.0
四.测试数据
自行编写测试数据进行测试
参考技术C struct node
char data;
struct node *next;
;
typedef struct node NODE;
/*This function creates a link_list with N nodes.*/
NODE *create_link_list(int n)
int i;
NODE *head, *p, *q;
if (n==0) return NULL;
head = (NODE *) malloc(sizeof(NODE));
p = head;
printf("Please input %d chars for the link list\n",n);
for (i=0; i<n; i++)
scanf("%c ", &(p->data));
q=(NODE *)malloc(sizeof(NODE));
printf("test3\n");
p->next=q;
p=q;
scanf("%c ",&(p->data));
getchar();
p->next=NULL;
return (head);
/*This function inserts a node whose value is b*/
/*before the node whose value is a, if the node is not exist,*/
/*then insert it at the end of the list*/
void insert(NODE **p_head, char a, char b)
NODE *p, *q;
q = (NODE *)malloc(sizeof(NODE));
q->data = b;
q->next =NULL;
if (* p_head == NULL) * p_head = q;
else
p=(NODE*)malloc(sizeof(NODE));
p = * p_head;
while (p->data != a && p->next != NULL)
p = p->next;
q->next = p->next;
p->next = q;

/*The function deletes the node whose value is a,*/
/*if success, return 0, or return 1*/
int deletenode(NODE **p_head, char a)
NODE *p, *q;
q=*p_head;
if (q==NULL) return(1);
if (q->data == a)
* p_head = q->next;
free(q);
return (0);
else
while (q->data != a && q->next != NULL)
p = q;
q = q->next;
if (q->data == a)
p->next = q->next;
free(q);
return(0);
else return(1);

void main()
NODE *my_head,*p;
/* create a link list with m nodes */
int m;
char ch_a,ch_b;
printf("please input the number of nodes for the link_list\nm=");
scanf("%d",&m);
getchar();
printf("test1\n");
my_head = (NODE *) malloc(sizeof(NODE));
my_head=create_link_list(m);
/*Output the link list*/
printf("The link list is like:\n");
p=my_head;
while (p != NULL)
printf("%c ",p->data);
p=p->next;

printf("\n");
/*insert a node whose value is b before a*/
printf("Please input the position for a\n ch_a=");
getchar();
scanf("%c",&ch_a);
getchar();
printf("Please input the value that you want to insert\n ch_b=");
scanf("%c",&ch_b);
getchar();
insert(&my_head,ch_a,ch_b);
printf("The link list after insertion is like:\n");
p=my_head;
while (p != NULL)
printf("%c ",p->data);
p=p->next;

printf("\n");
/*delete a node whose value is a*/
printf("Please input the position for a a=");
scanf("%c",&ch_a);
getchar();
deletenode(&my_head,ch_a);
printf("The link list after deleting is like:\n");
p=my_head;
while (p != NULL)
printf("%c ",p->data);
p=p->next;

printf("\n");

数据结构开发:线性表的链式存储结构

0.目录

1.线性表的链式存储结构

2.单链表的具体实现

3.顺序表和单链表的对比分析

4.小结

1.线性表的链式存储结构

顺序存储结构线性表的最大问题是:

  • 插入和删除需要移动大量的元素!如何解决?

链式存储的定义:

  • 为了表示每个数据元素与其直接后继元素之间的逻辑关系;数据元素除了存储本身的信息外,还需要存储其直接后继的信息。

技术分享图片

链式存储逻辑结构:

  • 基于链式存储结构的线性表中,每个结点都包含数据域指针域
    1. 数据域:存储数据元素本身
    2. 指针域:存储相邻结点的地址

技术分享图片

专业术语的统一:

  • 顺序表
    1. 基于顺序存储结构的线性表
  • 链表
    1. 基于链式存储机构的线性表
      1. 单链表:每个结点只包含直接后继的地址信息
      2. 循环链表:单链表中的最后一个结点的直接后继为第一个结点
      3. 双向链表:单链表中的结点包含直接前驱和后继的地址信息

链表中的基本概念:

  • 头结点
    1. 链表中的辅助结点,包含指向第一个数据元素的指针
  • 数据结点
    1. 链表中代表数据元素的结点,表现形式为:( 数据元素,地址 )
  • 尾结点
    1. 链表中的最后一个数据结点,包含的地址信息为空

单链表中的结点定义:
技术分享图片

单链表中的内部结构:
技术分享图片
头结点在单链表中的意义是:辅助数据元素的定位,方便插入和删除操作;因此,头结点不存储实际的数据元素

在目标位置处插入数据元素:

  1. 从头结点开始,通过current指针定位到目标位置
  2. 从堆空间申请新的Node结点
  3. 执行操作:
    1. node->value = e;
    2. node->next = current->next;
    3. current->next = node;

在目标位置处删除数据元素:

  1. 从头结点开始,通过current指针定位到目标位置
  2. 使用toDel指针指向需要删除的结点
  3. 执行操作:
    1. toDel = current->next;
    2. current->next = toDel->next;
    3. delete toDel;

2.单链表的具体实现

本节目标:

  • 完成链式存储结构线性表的实现

技术分享图片

LinkList 设计要点:

  1. 类模板,通过头结点访问后继结点
  2. 定义内部结点类型Node,用于描述数据域和指针域
  3. 实现线性表的关键操作( 增,删,查,等 )

LinkList的定义:
技术分享图片

链表的实现 LinkList.h:

#ifndef LINKLIST_H
#define LINKLIST_H

#include "List.h"
#include "Exception.h"

namespace StLib
{

template <typename T>
class LinkList : public List<T>
{
protected:
    struct Node : public Object
    {
        T value;
        Node* next;
    };

    mutable Node m_header;
    int m_length;
public:
    LinkList()
    {
        m_header.next = NULL;
        m_length = 0;
    }

    bool insert(const T& e)
    {
        return insert(m_length, e);
    }

    bool insert(int i, const T& e)
    {
        bool ret = ((0 <= i) && (i <= m_length));

        if( ret )
        {
            Node* node = new Node();

            if( node != NULL )
            {
                Node* current = &m_header;

                for(int p=0; p<i; p++)
                {
                    current = current->next;
                }

                node->value = e;
                node->next = current->next;
                current->next = node;

                m_length++;
            }
            else
            {
                THROW_EXCEPTION(NoEnoughMemoryException, "No memory to insert new element ...");
            }
        }

        return ret;
    }

    bool remove(int i)
    {
        bool ret = ((0 <= i) && (i < m_length));

        if( ret )
        {
            Node* current = &m_header;

            for(int p=0; p<i; p++)
            {
                current = current->next;
            }

            Node* toDel = current->next;

            current->next = toDel->next;

            delete toDel;

            m_length--;
        }

        return ret;
    }

    bool set(int i, const T& e)
    {
        bool ret = ((0 <= i) && (i < m_length));

        if( ret )
        {
            Node* current = &m_header;

            for(int p=0; p<i; p++)
            {
                current = current->next;
            }

            current->next->value = e;
        }

        return ret;
    }

    T get(int i) const
    {
        T ret;

        if( get(i, ret) )
        {
            return ret;
        }
        else
        {
            THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element ...");
        }

        return ret;
    }

    bool get(int i, T& e) const
    {
        bool ret = ((0 <= i) && (i < m_length));

        if( ret )
        {
            Node* current = &m_header;

            for(int p=0; p<i; p++)
            {
                current = current->next;
            }

            e = current->next->value;
        }

        return ret;
    }

    int length() const
    {
        return m_length;
    }

    void clear()
    {
        while ( m_header.next )
        {
            Node* toDel = m_header.next;

            m_header.next = toDel->next;

            delete toDel;
        }

        m_length = 0;
    }

    ~LinkList()
    {
        clear();
    }
};

}

#endif // LINKLIST_H

main.cpp测试

#include <iostream>
#include "LinkList.h"

using namespace std;
using namespace StLib;

int main()
{
    LinkList<int> list;

    for(int i=0; i<5; i++)
    {
        list.insert(0, i);
        list.set(0, i*i);
    }

    for(int i=0; i<list.length(); i++)
    {
        cout << list.get(i) << endl;
    }
    cout << endl;

    list.remove(2);

    for(int i=0; i<list.length(); i++)
    {
        cout << list.get(i) << endl;
    }
    cout << endl;

    list.clear();

    for(int i=0; i<list.length(); i++)
    {
        cout << list.get(i) << endl;
    }

    return 0;
}

运行结果为:

16
9
4
1
0

16
9
1
0

问题:

  • 头结点是否存在隐患?
  • 实现代码是否需要优化?

头结点的隐患:
技术分享图片

代码优化:

  • insert,remove,get,set等操作都涉及元素定位。

技术分享图片

代码优化(LinkList.h):

#ifndef LINKLIST_H
#define LINKLIST_H

#include "List.h"
#include "Exception.h"

namespace StLib
{

template <typename T>
class LinkList : public List<T>
{
protected:
    struct Node : public Object
    {
        T value;
        Node* next;
    };

    mutable struct : public Object
    {
        char reserved[sizeof(T)];
        Node* next;
    } m_header;

    int m_length;

    Node* position(int i) const
    {
        Node* ret = reinterpret_cast<Node*>(&m_header);

        for(int p=0; p<i; p++)
        {
            ret = ret->next;
        }

        return ret;
    }
public:
    LinkList()
    {
        m_header.next = NULL;
        m_length = 0;
    }

    bool insert(const T& e)
    {
        return insert(m_length, e);
    }

    bool insert(int i, const T& e)
    {
        bool ret = ((0 <= i) && (i <= m_length));

        if( ret )
        {
            Node* node = new Node();

            if( node != NULL )
            {
                Node* current = position(i);

                node->value = e;
                node->next = current->next;
                current->next = node;

                m_length++;
            }
            else
            {
                THROW_EXCEPTION(NoEnoughMemoryException, "No memory to insert new element ...");
            }
        }

        return ret;
    }

    bool remove(int i)
    {
        bool ret = ((0 <= i) && (i < m_length));

        if( ret )
        {
            Node* current = position(i);
            Node* toDel = current->next;

            current->next = toDel->next;

            delete toDel;

            m_length--;
        }

        return ret;
    }

    bool set(int i, const T& e)
    {
        bool ret = ((0 <= i) && (i < m_length));

        if( ret )
        {
            position(i)->next->value = e;
        }

        return ret;
    }

    T get(int i) const
    {
        T ret;

        if( get(i, ret) )
        {
            return ret;
        }
        else
        {
            THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element ...");
        }

        return ret;
    }

    bool get(int i, T& e) const
    {
        bool ret = ((0 <= i) && (i < m_length));

        if( ret )
        {
            e = position(i)->next->value;
        }

        return ret;
    }

    int length() const
    {
        return m_length;
    }

    void clear()
    {
        while ( m_header.next )
        {
            Node* toDel = m_header.next;

            m_header.next = toDel->next;

            delete toDel;
        }

        m_length = 0;
    }

    ~LinkList()
    {
        clear();
    }
};

}

#endif // LINKLIST_H

3.顺序表和单链表的对比分析

问题

  • 如何判断某个数据元素是否存在线性表中?

遗失的操作——find

  • 可以为线性表( List )增加一个查找操作
  • int find(const T& e) const;
    1. 参数:
      1. 待查找的数据元素
    2. 返回值:
      1. =0:数据元素在线性表中第一次出现的位置

      2. -1:数据元素不存在

数据元素查找示例:
技术分享图片

实现查找find函数:
在List.h中加入

virtual int find(const T& e) const = 0;

在SeqList.h中加入

    int find(const T& e) const
    {
        int ret = -1;

        for(int i=0; i<m_length; i++)
        {
            if( m_array[i] == e )
            {
                ret = i;
                break;
            }
        }

        return ret;
    }

在LinkList.h中加入

    int find(const T& e) const
    {
        int ret = -1;
        int i = 0;
        Node* node = m_header.next;

        while ( node )
        {
            if( node->value == e )
            {
                ret = i;
                break;
            }
            else
            {
                node = node->next;
                i++;
            }
        }

        return ret;
    }

但是若用类对象来进行测试,会有严重的bug:

#include <iostream>
#include "LinkList.h"

using namespace std;
using namespace StLib;

class Test
{
    int i;
public:
    Test(int v = 0)
    {
        i = v;
    }
};

int main()
{
    Test t1;
    Test t2;
    Test t3;
    LinkList<Test> list;

    return 0;
}

编译错误信息:

error C2678: 二进制“==”: 没有找到接受“Test”类型的左操作数的运算符(或没有可接受的转换)

于是应该在顶层父类Object中实现重载比较操作符
Object.h

    bool operator == (const Object& obj);
    bool operator != (const Object& obj);

Object.cpp

bool Object::operator == (const Object& obj)
{
    return (this == &obj);
}

bool Object::operator != (const Object& obj)
{
    return (this != &obj);
}

main.cpp再测试

#include <iostream>
#include "LinkList.h"

using namespace std;
using namespace StLib;

class Test : public Object
{
    int i;
public:
    Test(int v = 0)
    {
        i = v;
    }

    bool operator == (const Test& t)
    {
        return (i == t.i);
    }
};

int main()
{
    Test t1(1);
    Test t2(2);
    Test t3(3);
    LinkList<Test> list;

    list.insert(t1);
    list.insert(t2);
    list.insert(t3);

    cout << list.find(t2) << endl;

    return 0;
}

运行结果为:

1

时间复杂度对比分析:
技术分享图片

有趣的问题:

  • 顺序表的整体时间复杂度比单链表要低那么单链表还有使用价值吗?

效率的深度分析:

  • 实际工程开发中,时间复杂度只是效率的一个参考指标
    1. 对于内置基础类型,顺序表和单链表的下效率不相上
    2. 对于自定义类类型顺序表在效率上低于单链表
  • 插入和删除
    1. 顺序表:涉及大量数据对象的复制操作
    2. 单链表:只涉及指针操作,效率与数据对象无关
  • 数据访问
    1. 顺序表:随机访问,可直接定位数据对象
    2. 单链表:顺序访问,必须从头访问数据对象,无法直接定位

工程开发中的选择:

  • 顺序表
    1. 数据元素的类型相对简单,不涉及深拷贝
    2. 数据元素相对稳定,访问操作远多于插入和删除操作
  • 单链表
    1. 数据元素的类型相对复杂,复制操作相对耗时
    2. 数据元素不稳定,需要经常插入和删除,访问操作较少

4.小结

  • 链表中的数据元素在物理内存中无相邻关系
  • 链表中的结点都包含数据域指针域
  • 头结点用于辅助数据元素的定位,方便插入和删除操作
  • 插入和删除操作需要保证链表的完整性
  • 通过类模板实现链表,包含头结点成员长度成员
  • 定义结点类型,并通过堆中的结点对象构成链式存储
  • 为了避免构造错误的隐患,头结点类型需要重定义
  • 代码优化是编码完成后必不可少的环节
  • 线性表中元素的查找依赖于相等比较操作符( == )
  • 顺序表适用于访问需求量较大的场合( 随机访问 )
  • 单链表适用于数据元素频繁插入删除的场合( 顺序访问 )
  • 当数据类型相对简单时,顺序表和单链表的效率不相上下









以上是关于数据结构实验:线性表的顺序表示和链式表示及插入、删除、查找运算的主要内容,如果未能解决你的问题,请参考以下文章

数据结构:线性表顺序表以及单链表详解

《数据结构》第二章 | 线性表 知识梳理(应对期末考)

数据结构_线性表的顺序表示和链式表示

C语言数据结构——线性表的顺序表示

线性表链式表示

线性表的链式表示和实现