数据结构与算法(2)-单链表

线性表之单链表

链表的介绍

顺序表的实现虽然很简单,但是需要对表的大小的最大值进行估计,通常需要估计的大一点,从而会浪费大量的空间,这有着很大的局限性。而且,对于顺序表,插入和删除的费用是昂贵的。例如,在位置0的插入,首先需要将整个顺序表后移一个位置,而删除第一个元素则需要将表中所有的元素前移一个位置。这是这两种操作的最坏情况,但是平均来看,这两种运算也许要移动一半的元素。因为这些缺点,所以我们一般不用顺序表来表示线性表这种数据结构。

链表是由一系列不必在内存中相连的结构组成,每个结构均含有表元素和指向下一个单元的指针,我们称为next指针,最后一个单元的next指针指向NULL。链表有带头结点和不带头结点、循环和非循环、单向和双向之分。

单链表

单链表是线性表的一种最简单的链式存储结构。在单链表中,每一个结点都包含两部分:存放每一个元素本身信息的数据域和存放其直接后继存储位置的指针域。

在学习单链表之前,我们需要先明白头结点和头指针的区别。我们把指向第一个结点的指针称为头指针,那么每次访问链表时都可以从这个头指针依次遍历链表中的每个元素。一个单链表可以由其头指针唯一确定,一般用其头指针来命名单链表。而头结点(也叫哑结点)是在链表的开始结点之前附加的一个结点。有了头结点之后头指针指向头结点。头结点不计入链表长度值

带头结点的单链表的优点

有了头结点后,不论链表是否为空,头指针总是非空,而且头结点的设置使得对链表的第一个位置上的操作与在表中其它位置上的操作一致,无需特别处理,使得各种操作方便统一。


不带头结点.png
带头节点.png

单链表的基本操作

定义结构体

struct node
{
    Elemtype data;
    struct node *next;
};

获得链表长度

想要获取要链表的长度必须要遍历一遍

/*求带头结点的单链表长度(长度不包括头结点)*/
int Length(LinkList L)
{
    Node *p;
    int j = 0;
    p = L->next;
    while(p)
    {
        j++;
        p = p->next;
    }
    return j;
}

查找元素

链表查找只能从头结点开始往下找,效率较慢

查找第i个结点

/*查找第i个元素结点,找到返回其指针,否则返回NULL*/
Node* LocateByIndex(LinkList L,int i)
{
    Node *p = L;
    int j = 0;
    while(jnext!=NULL)
    {
        p = p->next;
        j++;
    }
    if(j==i)
        return p;
    else
        return NULL;
}

按值查找

/*查找值为x的结点,找到返回其指针,否则返回NULL*/
Node* LocateByValue(LinkList L,ElemType x)
{
    Node *p = L->next;
    while(p!=NULL&&p->data!=x)
        p = p->next;
    return p;
}

插入结点

插入为两种,一种是在某结点前插入,一种是在某结点后插入,主要注意指针的改变。

插入.png
/*在结点p后面插入新结点,新结点的数据为x*/
void InsertAfterX(Node *p,ElemType x)
{
    Node *s;
    s = (Node*)malloc(sizeof(Node));
    s->data = x;
    s->next = p->next;
    p->next = s;
}

/*在结点p前面插入新结点,新结点的数据为x*/
void InsertBeforeX(LinkList L,Node* p,ElemType x)
{
    Node *s,*q;
    s = (Node*)malloc(sizeof(Node));
    s->data = x;
    q = L;
    while(q->next!=p)
        q = q->next;
    s->next = q->next;
    q->next = s;
}

删除

删除结点的主要操作就是让被删除结点前的指针指向被删除结点后的结点,如下图所示:

删除.png

/*删除p结点后面的结点,如果p是最后一个结点,返回0,否则返回1,代表删除成功*/
int DeleteAfter(Node *p)
{
    Node *r;
    if(p->next != NULL)
    {
        r = p->next;
        p->next = r->next;
        free(r);
        return 1;
    }
    return 0;
}

/*删除p结点*/
void DeleteNode(LinkList L,Node* p)
{
    Node* q;
    q = L;
    while(q->next!=p)
        q = q->next;
    q->next = p->next;
    free(p);
}

/*删除第i个结点*/
void DelectByIndex(LinkList L,int i)
{
    Node *p,*q;
    q = LocateByIndex(L,i-1);
    if(q==NULL)
    {
        printf("第i-1个结点不存在!");
    }
    else if(q->next==NULL)
    {
        printf("第i个结点不存在!");
    }else
    {
        p = q->next;
        q->next = p->next;
        free(p);
    }
}

/*删除单链表中所有值为x的结点,返回删除的结点个数*/
int DelectByValue(LinkList L,ElemType x)
{
    Node *p,*q;
    int count = 0;
    q = L;
    while(q->next!=NULL)
    {
        p = q->next;
        if(p->data==x)
        {
            q->next = p->next;
            free(p);
            count++;
        }
        else
            q = p;
    }
    return count;
}

建立链表

链表的建立分为两种,在插入结点时我们可以选择插在最后面,也可以插在最前面,所以形成了尾插法和头插法两种方法。

/*头插法建立单链表,n为要初始化的结点个数*/
LinkList CreateLinkListF(int n)
{
    Node *head;
    Node *s;
    int i;
    ElemType x;
    head = (Node*)malloc(sizeof(Node));
    head->next = NULL;
    for(i=0;i<=n;i++)
    {
        scanf("%d",&x);
        s = (Node*)malloc(sizeof(Node));
        s->data = x;
        s->next = head->next;
        head->next = s;
    }
    return head;
}

/*尾插法建立单链表,n为要初始化的结点个数*/
LinkList CreateLinkListR(int n)
{
    Node *head,*s,*r;
    int i;
    ElemType x;
    head = (Node*)malloc(sizeof(Node));
    r = head;
    for(i=0;inext = s;
        r = s;
    }
    r->next = NULL;
    return head;
}

你可能感兴趣的:(数据结构与算法(2)-单链表)