链表结构以及链表的操作(结构体实现)

结构的嵌套

#include <iostream>
#include <cstring>

using namespace std;

struct Education
{
    char major[30];
    char degree[20];
    double GPA;
};
struct Student
{
    Education school;//结构中嵌套一个Education结构
    char id[20];
    int grade;
};
int main()
{
    Student ss = { {"计算机科学与技术","本科",4.0},"123456789",3 };

    cout << "专业:" << ss.school.major << "\n"
        << "学历:" << ss.school.degree << "\n"
        << "平均学分绩点:" << ss.school.GPA << "\n"
        << "学号:" << ss.id << "\n"
        << "年级:" << ss.grade;
    return 0;
}

在c/c++是允许结构的嵌套的,而嵌套的结构数据成员的访问方式是:
<对象名>.<嵌套的结构变量名称>.<对应的数据成员名称>
例如,上述代码的中,Student结构嵌套了Education结构,在主函数定义了结构变量ss,ss.school.name就访问到了嵌套的结构变量。

注意:结构的嵌套里,结构成员不能是自身的结构变量,但可以是自身结构指针作为成员

链表结构

struct List
{
	int data;
	List *next;
};

上述结构就是一个简单的链表结构,它是通过结构指针将下一个结构变量连接起来,所以称之为链表,List *next就是一个钩子,钩住下一个结构。

使用链表结构的好处
链表结构可以方便数据的插入和删除,如果使用常规的数组,对于删除某一个元素会特别麻烦,因为:数组的内存结构是连续的,而链表结构则是随机分布的,所以对于数据的插入和删除来说,特别方便。

创建与遍历链表结构

#include <iostream>

using namespace std;

struct Node
{
    int data;
    Node *next;
};
Node *CreateListNode(int n)//无头结点创建链表
{
    Node *pS;
    Node *pEnd;
    Node *Head;
    pS=new Node;
    cin>>pS->data;
    Head=NULL;//一开始链表为空
    pEnd=pS;
    for(int i=0;i<n-1;i++)
    {
        if(Head==NULL)
            Head=pS;
        else
            pEnd->next=pS;
        pEnd=pS;//pEnd指针始终指向链表的尾部
        pS=new Node;
        cin>>pS->data;
    }
    pS=NULL;
    pEnd->next=NULL;
    return Head;
}
void showList(Node* Head)//链表的输出
{
    Node* p=Head;
    while(p)
    {
        cout<<p->data<<" ";
        p=p->next;
    }
    cout<<endl;
}
int main()
{
    Node *head=CreateListNode(5);
    showList(head);
    return 0;
}

上述代码中,CreateListNode函数创建链表是最常见的方法,这种链表创建是没有带头结点的。除此之外,链表的常用创建方法有头插法和尾插法创建。无论是哪一种创建方法,链表尾部的 next指针都是指向空指针NULL

遍历链表只需要一个头指针head即可,通过不断地移位操作
p=p->next即可让指针遍历整个链表,参照上述代码的showlist函数

尾插法创建链表的方法

Node *InitList()
{
    Node* Head=new Node;
    Head->NULL;
    return Head;
}
Node *CreateListNodeInTail(Node *Head,int n)
{
    Node *p=Head;
    Node *temp=NULL;//初始化指针
    for(int i=0;i<n;i++)
    {
        temp=new Node;
        cin>>temp.data;
        p->next=temp;
        p=p->next;
    }
    p->next=NULL;
    return Head;
}

上述代码实现了尾插法创建链表,这种链表创建方法的头结点没有用处,其优点在于:在删除链表元素的时候,不需要考虑删除的是否是头结点,如果是不带头结点的创建链表方式,这种链表的头结点也是头指针Head,那么在删除的时候就要特殊考虑,因为删除了这个结点,Head指针就和链表脱钩了。

删除链表结点

void DeleteData(Node *Head,int element)
{
    Node* p=Head;
    if(!Head)
    {
        cout<<"List is NULL"<<endl;
        return;
    }
    if(p->data==element)
    {
        Head=Head->next;
        delete p;
        cout<<element<<" the head of List have been deleted"<<endl;
        return;
    }
    
    for(Node *pGuard=Head;pGuard->next;pGuard=pGuard->next)
    {
        if(pGuard->next->data==element)
        {
            p=pGuard->next;//待删
            pGuard->next==p->next;
            delete p;
            cout<<element<<" have been deleted"<<endl;
            return;
        }
    }
    cout<<element<<"is not found!"<<endl;
}

上述代码是实现链表删除某个特定元素的方法,链表是不含头结点的,下面我们看一下带头结点的删除特定元素的方法

void Delete(Node *Head,int element)
{
    Node *p=Head;
    while(p->next)
    {
        if(p->next->data==element)
        {
            Node *temp=p->next;
            p->next=temp->next;
            delete temp;
            cout<<element<<" have been deleted"<<endl;
            return;
        }
        p=p->next;
    }
    cout<<element<<" is not found"<<endl;
}

上述代码中,Delete函数是没有考虑头结点的,通过两种是否带有链表头结点删除操作是不相同的,对于不带头结点的链表,要考虑删除的结点是否为头结点,如果是,我们要将头指针移位,即Head=Head->next,然后把头结点删掉。

void delete_index(Node* head,int index)//删除下标为index的结点
{
    Node* p=head->next;
    Node* temp;
    int flag=0;
    int count=0;
    while(p)
    {
        count++;
        if(count==index-1)//让p指向要删除的位置的前一个位置
        {
            flag=1;
            temp=p->next;
            p->next=temp->next;
            delete temp;//释放内存
        }
    }
    if(!flag)
        cout<<"error"<<endl;
}

上述代码实现的是删除下标为index的元素(含头结点)

插入链表结点

void Insert_index(Node* head,int e,int index)//在index位置插入元素e
{
    Node* p=head->next;
    Node* temp;
    int flag;
    int count=0;
    while(p)
    {
        count++;
        if(count==index-1)
        {
            temp=new Node;
            temp->data=e;
            temp->next=p->next;
            p->next=temp;
            flag=1;
        }
    }
    if(!flag)
        cout<<"Successful"<<endl;
    else
        cout<<"Failed"<<endl;
}

上述代码实现了在index位置插入一个结点

排序

void Sort_List(Node* head,int n)//冒泡排序法
{
    Node* p=head->next;
    Node* q=head;
    for(int i=0;i<n;i++)
    {
        p=q->next;//重置表头指针
        for(int j=0;j<n-i-1;j++)
        {
            if(p->data > p->next->data)
            {
                int temp=p->data;
                p->data=p->next->data;
                p->next->data=temp;
            }
            p=p->next;
        }
    }
    showList(head);
}

根据结构体某一特定成员的值的排序,我们可以使用最简单的冒泡排序,上述代码中,利用指针q记录了表头指针,每次外层循环,指针p都要重置。至于为什么都是p=q->next,因为这个链表含头结点。

查找元素

void Search_index(Node* head,int index)//查找index 位置的元素
{
    int count=0;
    Node* p=head->next;
    if(index<=0)
    {
        cout<<index<<" error"<<endl;
        return;
    }
    while(p){
        count++;
        if(count==index)
            break;
        p=p->next;
    }
    if(p!=NULL)
        cout<<p->data<<endl;
    else
        cout<<index<<" error"<<endl;

}
void Search(Node* head,int e)//查找链表是否存在数据e
{
    Node* p=head->next;
    int index=0;
    while(p)
    {
        index++;
        if(p->data==e)
            break;
        p=p->next;
    }
    if(!p)
        cout<<index<<" exsits"<<endl;
    else
        cout<<"error"<<endl;
}

上述代码分别实现了链表查找下标为index位置的元素和链表是否存在元素element,比较简单。

*注意:以上的链表均是单向链表,链表还有双向的,以及循环链表,实际的实现方式也相差无几,双向链表中,除了next指针,还需要ex指针,这两个指针分别指向的是后一个结点和前一个结点。而循环链表只是原本单向的链表的末尾连接到了它的链首。 *

struct Node
{
    int data;
    Node* ex;
    Node* next;
};

总之,最常见的是单向链表,但是其它两种链表各有优点,对于双向链表,在一些删除链表相同元素的题目中,我们可能要从链表的第二项开始,然后查找是否与前面的结点的对应的值相等,而对于具有ex指针的双向链表来说很简单,直接在里面套一层循环即可;对于循环链表,这种解决的问题比较特殊,可以比较方便地解决 约瑟夫环 这种问题,

循环链表的设置最好不要带头结点。而对于单向链表,设置了头结点对于删除结点是很好的,因为它避免了讨论删除的结点是否位于表头。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值