哈工大数据结构与算法 作业1——线性结构的存储结构与应用(LinkList)

作业题目:线性表的基本存储结构的实现与应用

顺序表与单链表是线性表的两种最基本的存储结构,而静态链表是两者的 完美结合,是系统进行动态存储分配的方法基础。线性表的这三种存储结构不但是其他数据结构(如树形结构、图型结构、集合等)存储方法的重要基础,同时本身也有广泛的应用。

作业要求

1. 实现线性表的顺序存储结构(SeqList)和链式存储结构(LinkList)
2. 在上述存储结构的基础上,分别实现以下算法:
① 删除给定元素的算法。
② 对于已排好序的线性表,删除所有重复元素的算法。
③ 线性表“逆置”算法。
④ 线性表循环左移/右移 k 位的算法。
⑤ 合并两个已排好序的线性表的算法。
3. 选做:(可以不做,供学有余力、有兴趣的同学探索)
① 你能实现线性表的静态链表存储结构吗?
② 你能在静态链表上实现线性表的“逆置”算法吗?

存储结构

struct Link
{
    int data;//数据域
    struct Link *next;//指针域
};
typedef struct Link Node;

部分基本操作

新建一个结点

Node *Append(Node *head)
{
    Node *p=NULL,*pr=head;
    int data;
    p=(Node*)malloc(sizeof(Node));
    if(p==NULL)
    {
        printf("No enough memory to allocate!");
        exit(0);
    }
    if(head==NULL)//原表为空
    {
        head=p;
    }
    else
    {
        while(pr->next!=NULL)
        {
            pr=pr->next;
        }
        pr->next=p;
    }
    printf("Input node data:");
    scanf(" %d",&data);
    p->data=data;
    p->next=NULL;
    return head;
}

显示表中结点

void Display(Node *head)
{
    Node *p=head;
    int j=1;
    while(p!=NULL)
    {
        printf("%5d%10d\n",j,p->data);
        p=p->next;
        j++;
    }
}

释放链表所占内存

void DeleteMemory(Node *head)
{
    Node *p=head,*pr=NULL;
    while(p!=NULL)
    {
        pr=p;
        p=p->next;
        free(pr);
    }
}

升序排序的链表中插入一个结点

Node *InsertNode(Node *head,int Nodedata)
{
    Node *pr=head,*p=head,*temp=NULL;
    p=(Node *)malloc(sizeof(Node));//p指向待插结点
    if(p==NULL)//申请失败
    {
        printf("No enough memory!");
        exit(0);
    }
    p->next=NULL;//为p指向结点赋值为空
    p->data=Nodedata;
    if(head==NULL)//原链表为空表
    {
        head=p;//head指向新建结点p
    }
    else
    {
        while(pr->data<Nodedata&&pr->next!=NULL)
        {
            temp=pr;//temp保存当前结点指针
            pr=pr->next;//pr指向当前结点的下一结点
        }
        if(pr->data>=Nodedata)
        {
            if(pr==head)//若在头结点处插入新结点
            {
                p->next=head;//新结点指向原链表的头结点
                head=p;//head指向新结点
            }
            else//链表中插入新结点
            {
                pr=temp;
                p->next=pr->next;//新结点指向下一结点
                pr->next=p;//前一结点指向新结点
            }
        }
        else
        {
            pr->next=p;//末结点指向新结点
        }
    }
    return head;
}

作业中算法

1.删除给定元素

        先对空表情况进行单独说明,然后遍历该线性表,查找到给定元素后修改前后指针,将其所在结点释放,未找到进行说明。

Node *DeleteNode(Node *head,int Nodedata)
{
    Node *p=head,*pr=head;
    if(head==NULL)//表为空
    {
        printf("Table is empty!");
        return head;
    }
    while(Nodedata!=p->data&&p->next!=NULL)
    {
        pr=p;
        p=p->next;
    }
    if(Nodedata==p->data)
    {
        if(p==head)
        {
            head=p->next;
        }
        else
        {
            pr->next=p->next;
        }
        free(p);
    }
    else
    {
        printf("This node has not been found!");
    }
    return head;
}

2.删除重复元素(升序表)

Node *Delete_Element_duplicate(Node *head)
{
    Node *p=head,*pr=head,*temp=NULL;
    if (head == NULL)
    {
        printf("Table is empty!");
        return head;
    }
    p=pr->next;
    while(p != NULL)
    {
        if(p->data == pr->data)
        {
            pr->next=p->next;
            temp=p;
            p=p->next;
            free(temp);
        }
        else
        {
            pr=pr->next;
            p=p->next;
        }
    }
    return head;
}

3.将线性表逆置

        当表中没有元素或者只有一个元素的时候,无需进行操作,单独说明。其他情况,用p指向头结点,将头结点指向空,将p依次后移,temp记录p移动前指向的结点,head指向temp前一结点,使temp->next=head,完成该结点的一个逆置,循环直到最后一个结点,对最后一个结点的next域和head单独修改。

Node *Reverse(Node *head)
{
    Node *p=head,*temp=NULL;
    if(head == NULL)
    {
        printf("Table is empty!");
        return head;
    }
    else if(head->next == NULL)
    {
        printf("success!");
        return head;
    }
    else
    {
        head=NULL;
        while(p->next != NULL)
        {
            temp=p;
            p=p->next;
            temp->next=head;
            head=temp;
        }
        head=p;
        p->next=temp;
    }
    return head;
}

4.循环左移

        先将原来的线性表头尾链接形成一个环形链表,再在适当的位置将其断开,这里展示了循环左移的算法,右移同理。

Node *Cyclic_Move(Node *head,int k)
{
    int i=0;
    Node *p=head,*pr=head;
    while(p->next != NULL)
    {
        p=p->next;
    }
    p->next=head;
    for(i=0;i<k;i++)
    {
        pr=head;
        head=head->next;
    }
    pr->next=NULL;
    return head;
}

5.合并两个有序表

        申请一片新的空间用于存放合并后的线性表,也可以在其中一个线性表上进行将另一个表的元素添加上来的操作,但这样会破坏原有表的结构,题目未作说明两种思路均可。

Node *Merge(Node *head1,Node *head2,Node *head)
{
    Node *p=head1,*q=head2;//head为合并后的链表
    Node *s=NULL;
    Node *pr=NULL;

    s=(Node *)malloc(sizeof(Node));//存放新建表的头结点
    head=s;
    pr=head;
    if(p->data>=q->data)
    {
        pr->data=q->data;
        q=q->next;
    }
    else
    {
        pr->data=p->data;
        p=p->next;
    }
    //头结点建立完毕,此时pr指向头节点
    while(p != NULL&&q != NULL)
    {
        s=(Node *)malloc(sizeof(Node));
        if(s==NULL)
        {
            printf("No enough memory!");
            exit(0);
        }
        pr->next=s;//建立s成功,pr指向新结点
        if(p->data>=q->data)
        {
            s->data=q->data;
            q=q->next;
        }
        else
        {
            s->data=p->data;
            p=p->next;
        }
        pr=s;//pr指向新建完毕的结点
    }

    if(p == NULL)//p所指向的链表已空
    {
        while(q != NULL)
        {
            s=(Node *)malloc(sizeof(Node));
            pr->next=s;
            s->data=q->data;
            q=q->next;
            pr=s;
        }
    }
    else//(q->next == NULL)
    {
        while(p != NULL)
        {
            s=(Node *)malloc(sizeof(Node));
            pr->next=s;
            s->data=p->data;
            p=p->next;
            pr=s;
        }
    }
    pr->next=NULL;
    return head;
}

测试结果

Code::Blocks下测试结果如下:

合并算法的测试如下:

反思与总结

  • 命名一定要英文命名,包括上一级文件夹的名字(整个文件路径),否则debug时可能出现故障!(在用CodeBlocks进行调试时,提示这样的错误信息 Debugger finished with status 0 此时主要错误原因为文件路径以及文件名中含中文字符。)
  • 没有编写menu函数(太懒了),直接在main中测试代码。
  • 对合并算法未考虑其中一表为空的情况,且没有对这种情况进行测试。
  • 主函数中对线性表的输入代码过于庞大,可以写一个函数来简化数据输入过程。
  • 合并算法开头处对head的处理与对pre处理很类似,考虑是否可以对循环体进行改进,减少代码量。
  • 有时候检查函数算法错误时,错误也可能在测试函数的语句中,不要忽略(设置断点时注意)。
  • 注意函数的返回值类型,不为空时一定要赋值给相应的变量(xiaolaji在编写时就将返回值为指针类型的函数当作空函数来使用,编译器没有报错,一直以为错误在函数内部,找了好久都没发现错误,浪费了好多时间)。
  • 部分基本操作代码学习参考C语言程序设计(苏小红主编第四版)第12章,和自己相比代码更加简洁,思路更加清晰,尤其是对特殊情况的处理考虑十分周到,需要学习。

完整代码

#include <stdio.h>
#include <stdlib.h>
struct Link
{
    int data;//数据域
    struct Link *next;//指针域
};
typedef struct Link Node;

/*新建一个结点并添加到链表末尾,返回添加后链表的头指针*/
Node *Append(Node *head)
{
    Node *p=NULL,*pr=head;
    int data;
    p=(Node*)malloc(sizeof(Node));
    if(p==NULL)
    {
        printf("No enough memory to allocate!");
        exit(0);
    }
    if(head==NULL)//原表为空
    {
        head=p;
    }
    else
    {
        while(pr->next!=NULL)
        {
            pr=pr->next;
        }
        pr->next=p;
    }
    printf("Input node data:");
    scanf(" %d",&data);
    p->data=data;
    p->next=NULL;
    return head;
}

/*依次显示表中结点*/
void Display(Node *head)
{
    Node *p=head;
    int j=1;
    while(p!=NULL)
    {
        printf("%5d%10d\n",j,p->data);
        p=p->next;
        j++;
    }
}

/*释放head所指向链表所占用的内存*/
void DeleteMemory(Node *head)
{
    Node *p=head,*pr=NULL;
    while(p!=NULL)
    {
        pr=p;
        p=p->next;
        free(pr);
    }
}

/*删除链表中给定元素的结点,返回head*/
Node *DeleteNode(Node *head,int Nodedata)
{
    Node *p=head,*pr=head;
    if(head==NULL)//表为空
    {
        printf("Table is empty!");
        return head;
    }
    while(Nodedata!=p->data&&p->next!=NULL)
    {
        pr=p;
        p=p->next;
    }
    if(Nodedata==p->data)
    {
        if(p==head)
        {
            head=p->next;
        }
        else
        {
            pr->next=p->next;
        }
        free(p);
    }
    else
    {
        printf("This node has not been found!");
    }
    return head;
}

/*在已按升序排序的链表中插入一个结点,返回插入后的head*/
Node *InsertNode(Node *head,int Nodedata)
{
    Node *pr=head,*p=head,*temp=NULL;
    p=(Node *)malloc(sizeof(Node));//p指向待插结点
    if(p==NULL)//申请失败
    {
        printf("No enough memory!");
        exit(0);
    }
    p->next=NULL;//为p指向结点赋值为空
    p->data=Nodedata;
    if(head==NULL)//原链表为空表
    {
        head=p;//head指向新建结点p
    }
    else
    {
        while(pr->data<Nodedata&&pr->next!=NULL)
        {
            temp=pr;//temp保存当前结点指针
            pr=pr->next;//pr指向当前结点的下一结点
        }
        if(pr->data>=Nodedata)
        {
            if(pr==head)//若在头结点处插入新结点
            {
                p->next=head;//新结点指向原链表的头结点
                head=p;//head指向新结点
            }
            else//链表中插入新结点
            {
                pr=temp;
                p->next=pr->next;//新结点指向下一结点
                pr->next=p;//前一结点指向新结点
            }
        }
        else
        {
            pr->next=p;//末结点指向新结点
        }
    }
    return head;
}

/*对排好序的线性表,删除所有重复元素*/
Node *Delete_Element_duplicate(Node *head)
{
    Node *p=head,*pr=head,*temp=NULL;
    if (head == NULL)
    {
        printf("Table is empty!");
        return head;
    }
    p=pr->next;
    while(p != NULL)
    {
        if(p->data == pr->data)
        {
            pr->next=p->next;
            temp=p;
            p=p->next;
            free(temp);
        }
        else
        {
            pr=pr->next;
            p=p->next;
        }
    }
    return head;
}

/*逆置线性表*/
Node *Reverse(Node *head)
{
    Node *p=head,*temp=NULL;
    if(head == NULL)
    {
        printf("Table is empty!");
        return head;
    }
    else if(head->next == NULL)
    {
        printf("success!");
        return head;
    }
    else
    {
        head=NULL;
        while(p->next != NULL)
        {
            temp=p;
            p=p->next;
            temp->next=head;
            head=temp;
        }
        head=p;
        p->next=temp;
    }
    return head;
}

/*循环向左移动k位*/
Node *Cyclic_Move(Node *head,int k)
{
    int i=0;
    Node *p=head,*pr=head;
    while(p->next != NULL)
    {
        p=p->next;
    }
    p->next=head;
    for(i=0;i<k;i++)
    {
        pr=head;
        head=head->next;
    }
    pr->next=NULL;//where are the bugs?
    return head;
}

/*合并两个排好序的线性表(升序)*/
Node *Merge(Node *head1,Node *head2,Node *head)
{
    Node *p=head1,*q=head2;//head为合并后的链表
    Node *s=NULL;
    Node *pr=NULL;

    s=(Node *)malloc(sizeof(Node));//存放新建表的头结点
    head=s;
    pr=head;
    if(p->data>=q->data)
    {
        pr->data=q->data;
        q=q->next;
    }
    else
    {
        pr->data=p->data;
        p=p->next;
    }
    //头结点建立完毕,此时pr指向头节点
    while(p != NULL&&q != NULL)
    {
        s=(Node *)malloc(sizeof(Node));
        if(s==NULL)
        {
            printf("No enough memory!");
            exit(0);
        }
        pr->next=s;//建立s成功,pr指向新结点
        if(p->data>=q->data)
        {
            s->data=q->data;
            q=q->next;
        }
        else
        {
            s->data=p->data;
            p=p->next;
        }
        pr=s;//pr指向新建完毕的结点
    }

    if(p == NULL)//p所指向的链表已空
    {
        while(q != NULL)
        {
            s=(Node *)malloc(sizeof(Node));
            pr->next=s;
            s->data=q->data;
            q=q->next;
            pr=s;
        }
    }
    else//(q->next == NULL)
    {
        while(p != NULL)
        {
            s=(Node *)malloc(sizeof(Node));
            pr->next=s;
            s->data=p->data;
            p=p->next;
            pr=s;
        }
    }
    pr->next=NULL;
    return head;
}

int main()
{
    Node *head=NULL;//头指针
    char c;
    printf("Do you want to append a new node(Y/N)");
    scanf(" %c",&c);
    while(c=='Y'||c=='y')
    {
        head=Append(head);
        printf("Do you want to append a new node?(Y/N)");
        scanf(" %c",&c);
    }
    Display(head);//显示当前链表中各节点信息
    printf("table has been established!\n");
    printf("The result of deleting number 3\n:");
    head=DeleteNode(head,3);
    Display(head);
    printf("The result of deleting duplicate elements:\n");
    head=Delete_Element_duplicate(head);
    Display(head);
    printf("The result of reversing the table:\n");
    head=Reverse(head);
    Display(head);
    printf("The result of moving 3 units to the left\n");
    head=Cyclic_Move(head,3);
    Display(head);
    DeleteMemory(head);

    /*测试合并算法用*//*
    Node *head1=NULL;
    Node *head2=NULL;
    Node *head=NULL;
    char c;
    printf("Do you want to append a new node for table one ?(Y/N)");
    scanf(" %c",&c);
    while(c=='Y'||c=='y')
    {
        head1=Append(head1);
        printf("Do you want to append a new node?(Y/N)");
        scanf(" %c",&c);
    }
    printf("table one has been established!\n");
    Display(head1);//显示当前链表中各节点信息
    printf("Do you want to append a new node for table two ?(Y/N)");
    scanf(" %c",&c);
    while(c=='Y'||c=='y')
    {
        head2=Append(head2);
        printf("Do you want to append a new node?(Y/N)");
        scanf(" %c",&c);
    }
    printf("Table two has been established!\n");
    Display(head2);//显示当前链表中各节点信息
    printf("Table merged by table 1 and table 2 is:\n");
    head=Merge(head1,head2,head);
    Display(head);
    DeleteMemory(head1);
    DeleteMemory(head2);*/
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值