带头结点的单链表的操作集合

声明:本文章的所有代码全部使用C++语言,本文章所涉及的操作全部为基本操作。

单链表的概念:

线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素。

在这里我们称每个数据元素ai为一个节点,它包含两个域,其中存储信息的是数据域,存储直接后继存储位置的域称为指针域。

单链表的定义:

typedef int ElemType;
struct LNode
{
    ElemType data;
    LNode *next;
    LNode(ElemType _data = 0, LNode *_next = NULL) : data(_data), next(_next) {}
};
typedef LNode *LinkList;

说明:

1.ElemType为对应的数据域的数据类型,不仅仅是基本的数据类型,也可以复杂的数据类型。

2.next即为指向直接后继存储位置的指针

3.LNode *类型我们用来指向单个节点,而LinkList类型我们用来表示一个链表,也就是如果指向头结点的指针为L那么我们成该立案表为L,声明为LinkList L;

单链表的声明:

LinkList L;

这样我们声明了一个链表L,L指向头结点(因为这里我们讨论带头结点的单链表),实际上不管是带头结点还是不带头结点,L永远都指向链表的第一个元素。

单链表的初始化:

bool InitList(LinkList &L)
{
    L = new LNode();
    if(L != NULL)
        return true;
    else
        return false;
}

我们动态建立新的节点给L作为整个链表的头结点,如果节点申请成功也就是L不指向空返回true,反之申请不成功L指向空返回false。

单链表的遍历:

LNode *pcur = L->next;
while(pcur)
{
    cout << pcur->data << " ";
    pcur = pcur->next;
}

我们只要每次操作完当前结点,让当前位置的指针指向下一个位置就可以了,但是需要注意的一点是,我们往往不直接用头指针L去遍历链表,而是用一个指向结点的指针去遍历链表,这样做是为了保存链表的头指针,否则整个链表会失去起点(因为是单链表)。

单链表当中元素的查找:

1.查找链表当中第i个元素:

ElemType GetElem(LinkList L, int pos)
{
    int j = 1;
    L = L->next;
    while(L && j < pos)
    {
        L = L->next;
        j++;
    }

    if(!L || j > pos)
        return -1;
    return L->data;
}

注:上面代码实现的功能是在带头结点的单链表L中查找第pos个数据元素,如果存在则返回这个数据元素,否则返回不存在的标记(因为在这里ElemType实际上就是int,所以返回-1用来标识,具体视情况而定)。

1.首先我们标记一个计数器j用来标记当前结点是单链表的第几个结点,然后我们依次遍历整个单链表,因为单链表是带头结点的,因此我们首先L = L->next,将指针指向首元节点,然后依次遍历直到找到第pos个结点。

2.如果最后我们得到的节点指针是指向空的,也就是说明整个链表不存在第i个元素(链表当中的数据元素的个数小于i),或者是j>i(j大于i说明i是违法的,也就是i<=0)。

2.查找第一个数据域等于e的节点:

int LinkFind(LinkList L, ElemType e)
{
    int j = 1;
    L = L->next;
    while(L && L->data != e)
    {
        L = L->next;
        j++;
    }

    if(!L)
        return -1;
    return j;
}

注:该函数实现的功能为查找第一个数据为e的结点,返回结点的标号。

1.跟上一个查找第i个结点的操作很相似,只要L结点的数据域不等于e就往后遍历即可。

链表元素的插入:

1.前插法:

前插法就是将新的结点插入在链表的首元结点之前,即链表的顺序与输入顺序相反。

ElemType x;
while(cin >> x && x != -1)
{
    LNode *p = new LNode(x);
    p->next = L->next;
    L->next = p;
}

前插法的图解如下:

图中s所指向的节点即为新加节点(p)

1.首先我们将链表L的首元节点(a1)接在节点p后面(p->next = L->next),实际上只要将首元节点接在p后面,相当于将整个链表(头结点除外)接在了p后面,因为a1的next指针指向a2,而a2的next指针又指向了a3……

2.之后我们将p节点接在链表L的头结点的后面(L->next = p),在整个过程当中L始终指向链表的头结点,这时就完成了新节点p的前插。

2.后插法:

尾插法就是将新的节点插在整个链表的尾部,即链表的顺序与输入顺序相同。

LNode *pcur = L;
ElemType x;
while(cin >> x && x != -1)
{
    pcur->next = new LNode(x);
    pcur = pcur->next;
}

尾插法的图解如下:

1.我们创建一个指向结点的指针,这个指针永远都指向整个链表的最尾部的节点,每次我们都将新的节点接在当前链表的最尾结点的后面(pcur->next = new LNode(x))。

2.将链表的指针后移(pcur = pcur->next),目的是为了保证pcur指针永远指向链表的最尾部节点。

3.在第i个位置插入元素e

bool ListInsert(LinkList L, int pos, ElemType e)
{
    int j = 0;
    while(L && j < pos - 1)
    {
        L = L->next;
        j++;
    }

    if(!L || j > pos - 1)
        return false;

    LNode *s = new LNode(e);
    s->next = L->next;
    L->next = s;

    return true;
}

注:该函数实现的功能是在第i个结点之前插入数据元素e,如果插入成功返回true,否则返回false。

1.如果想要在第i个结点之前插入数据元素e,我们必须要获得第i个结点的直接前驱,也就是第i-1个结点,因此我们先遍历链表查找第i-1个结点。

2.当我们找到第i-1个结点时,此时我们可以将问题转化为类似前插法,将数据元素插入(一定要注意先后次序)。

链表元素的删除:

删除第i个位置的结点:

bool ListDelete(LinkList L, int pos)
{
    int j = 0;
    while(L && j < pos - 1)
    {
        L = L->next;
        j++;
    }

    if(!L || j > pos)
        return false;

    LNode *p = L->next;
    L->next = L->next->next;
    delete p;

    return true;
}

注:该函数实现的功能为删除链表的第i个结点,如果删除失败成功true,否则返回false。

1.要删除第i个结点我们首先要获取第i个结点的直接前驱,也就是第i-1一个结点。

2.我么将ai-1的next指针指向ai的下一个结点(L->next = L->next->next),就完成了将ai移出链表,但是我们必须要清空ai所占的内存,于是用结点指针p指向ai方便我们的删除。

链表的清空:

void ClearList(LinkList &L)
{
    LNode *p = L->next;
    L->next = NULL;
    while(p)
    {
        LNode *temp = p;
        p = p->next;
        delete temp;
    }
}

注:该函数实现的功能为,清空链表L,也就是删除除头结点之外的所有结点。

1.首先我们用结点指针指向当前要删除的结点,然后将链表L的头结点与首元结点的关系断开(L->next = NULL)。

2.然后我们用结点指针p遍历整个链表,一边遍历一遍删除结点(注意顺序)。

链表的销毁:

void DestroyList(LinkList &L)
{
    LNode *p = L;
    while(p)
    {
        LNode *temp = p;
        p = p->next;
        delete temp;
    }
    L = NULL;
}

注:该函数实现的功能为销毁整个链表,销毁与清空的区别就是销毁就是连头结点也要删除。
1.类似清空操作,只不过我们删除之后,将链表L的头指针指向空,也就是这个链表不存在。(区分L指向空与L->next指向空的区别)。

已标记关键词 清除标记
相关推荐
/*带头结点头文件 hlinklist.h*/ #include <stdio.h> typedef int datatype; typedef struct link_node { datatype data; struct link_node *next; }node; /*初始化链表*/ node *init() { node *head; head=(node *)malloc(sizeof(node)); head->next=0; return head; } /*尾插法创建一个带头结点链表*/ node *creat(node *head) { node *r,*s; int x; r=head; printf("在新链表中输入数据以0结束:"); scanf("%d",&x); while(x) { s=(node*)malloc(sizeof(node)); s->data=x; r->next=s; r=s; scanf("%d",&x); } r->next=0; return head; } /*打印链表结点值*/ void print(node *head) { node *p; p=head->next; if(!p) printf("链表内容为空!"); else while(p) { printf("%5d",p->data); p=p->next; } printf("\n"); } /*在单链表中查找第i个结点的地址*/ node *find(node *head,int i) { node *p=head; int j=0; if(i<0) {printf("不存在!");return 0;} if(i==0) return head; while(p&&i!=j) { p=p->next; j++; } return p; } /*在带头结点的单链表第i个位置后插入一个数*/ node *insert(node *head,int i,datatype x) { node *p,*q; q=find(head,i); if(!q) { printf("插入的位置不存在!\n");return head;} else { p=(node *)malloc(sizeof(node)); p->data=x; p->next=q->next; q->next=p; } return head; } /*在带头结点的单链表中删除一个为x的值*/ node *dele(node *head,datatype x) { node *pre=head,*p; p=head; while(p&&p->data!=x) { pre=p;p=p->next; } if(p) { pre->next=p->next; free(p); } return head; } /*把带头结点的单链表倒置(以结点形式 )*/ node *Dao_zhi(node *head) { node *p,*s; p=head->next; head->next=NULL; while(p) { s=p; p=p->next; s->next=head->next; head->next=s; } return head; } /*删除链表中重复的结点 */ node *dele1(node *head) { node *pre,*p,*q; if(head->next==0||!head->next->next) { printf("链表为空!"); return head; } //pre=head->next; q=head->next; while(q) { pre=q; p=q->next; while(p) { while(p&&q->data!=p->data) { pre=p;p=p->next; } if(p) { pre->next=p->next; free(p); } p=pre->next; } q=q->next; } return head; }
©️2020 CSDN 皮肤主题: 黑客帝国 设计师:白松林 返回首页