浅谈链表(1)--链表的构建以及遍历输出

1.在学完c++的基本语法之后,可能我们在组织一类相似的数据时首先想到的就是数组,至于数组,它的大小在某种意义上还是固定的,即使是可以动态分配内存。(当然vector在一定程度上解决了这个问题,之所以说是一定程度上,是因为vector只是一个数组对象,它重载了[]运算符,才让我们感觉他比较像数组,实际是他不是一个数组。)而且,很多时候,我们需要在这个群体里面添加元素与删除元素,或者在内存已经受到限制,无法大规模提供连续的储存空间时,这个时候数组就显得很局限。为了解决这个问题,一种数据结构应运而生——链表。

2.链表也是一种群体数组的组织方式,不同于数组,它的存储时不连续的。就像他的名字一样,像是一个链子,一环扣一环。只需要找到链子的头部(当然,尾部也可以,甚至对于闭合链子我们无法找出头部或者尾部,这里我们暂时假设它是一个有头有尾的链子),就可以顺藤摸瓜的找出整条链子。这种思想在数据结构中的体现就是链表结构。今天我们先来简单的谈一谈单向链表的构建以及遍历输出。

3.基于以上事实,我们知道了另一种组织群体数据的方式-链表。我们在来考虑刚才的实例,一条链子是由许多部分组成的,作为现实世界抽象的产物,链表也是由许多部分组成,我们把每个最小的部分叫做一个节点。把所有的节点连起来就是一个链表。那么如何构建一个链表?经过以上分析,我们可以得出结论:节点+连起来=链表。好了,现在需要解决的问题就是如何创建一个节点?如何把他们连接起来?对于连接,如果c++指针理解深刻的程序员们瞬间就会想起来这个强大的工具。没错,指针就是一个路标一样的东西,他会告诉你想要的东西在哪里。所以我们可以用指针完成链接这件事,至于节点的构建。我们类比数组,首先节点需要储存数据,还要有一个指针作为“路标”,那么这个节点一定至少有两个元素。好了,为了组织这两个不同类型的数据,我们有很多种方法。首先时c++面向对象的“类”,他可以组织多种不同的数据以及提供对这些数据的操作。让后就是结构体,"一个退化的类"(这样说不准确,应该说类时升级是结构体)。这里我们首先使用结构体来组织这两个数据。

4.经过以上分析,我们知道了两个需要解决的问题。指针作为路标告诉编译器下一个数据的位置,节点保存数据和指针,指针又指向下一个节点,这样构成一条连续链子。现在我们通过代码来实先这一想法。

(1)首先创建一个节点:

struct linklist {

int data;//需要储存的数据

struct linklist*next;//指向下一个节点的指针

    linklist(int x) {
        data = x;
    }

};

实现这一步很简单,在结构体的内部添加对应的数据就好。

(2)创建一个链表:

linklist*insert() {
        linklist*head = new linklist(0);//头部节点
        linklist*p = head;//尾部节点(此时,尾部等于头部)
        linklist*node = NULL;//待插入的节点.首先初始化为空指针
        int c = 1;
        int x = 0;
        cout << "please enter the data:" << endl;
        while (c) {
            cin >> x;
            if (x != 0) {
                node = new linklist(x);
                p->next = node;
                p = node;
            }
            else
            {
                c = 0;
            }

        }
        p->next = NULL;
        return head;

    }

我们来分析一下这段代码:首先是定义了链表的开始与结束。这和我们分析的一样。然后就是定义一个待插入的节点,注意,定义该节点的时候一定要把它初始化为空指针!否则成为野指针的后果大家都懂。然后就是控制程序的运行结构,这没什么好分析的。之后就到了程序的关键了:实现节点之间的链接。我们实在尾部插入,首先初始化空节点,这也是必须的,因为定义指针只是定义了它的指向,并没有为他分配内存。之后让尾部指针指向插入节点的next,这一步十分关键,完成这一步以后,那么插入的节点就是尾部节点,把它赋值给尾部节点。到了这一步,千万不要以为大功告成,因为尾部节点的next还没有处理,再让尾部节点的next赋值为NULL,这才算是完成了一个链表的构建。

(3)好了,万事开头难,链表构建完毕以后对链表进行操作似乎就很自然了。这里我们用遍历输出作为例子来说一下链表的其他几个问题。

void print_linklist(linklist*l) {
    linklist*p = l->next;
    if (l->next == nullptr) {
        cout << "linklist is empty!" << endl;
    }
    else {
        while (p) {
            cout << p->data << " ";
            p = p->next;
        }
    }

}

这段代码的难度不大,就是顺藤摸瓜的思想。找到第一个节点以后,顺着这个节点继续下去可以找到所有的元素。因为我们设置了最后一个节点的next为空,所以就可以通过while语句遍历(这里也说明了要把尾节点的next设置为空的一个重要意义)。之后输出。下面我们来分析链表的头部与尾部的问题。尾部很简单了,注意尾部的next设置为零。这样既避免了野指针的出现,又可以通过这一特性遍历。之后就是头部了,根据我们的分析,头部是没有指针指向他的,但是我们这里还是给了一个head指针指向头部,只是为了让节点的结构统一。这样一来,所有的节点都有指针指向它并且由指向下一个节点的指针。(可以设想一下,如果让尾部节点的next指向头部,那么不久成了一个闭合的环了吗?没错,这就是我们接下来要讨论的)。好了,今天的分享就到这里。大家加油

猜你喜欢

转载自blog.csdn.net/weixin_41863129/article/details/80698186
今日推荐