穷究链表(六)

这篇会开始进行链表函数的实现,这里先实现两个函数。

一开始考虑最容易实现的,挑选
printlist来实现,printlist只需要遍历整个链表,然后将遍历到的节点的值逐个输出。
那就来考虑这样的问题,我定义一个临时的指针tmp,指向链表的第一个节点。如果tmpNULL,那就说明链表连第一个节点都没有,也就是说这个链表是一个空表。如果tmp->nextNULL,那就说明tmp现在指向的节点是链表的最后一个节点,其没有next的链表节点了。

对于链表节点的输出操作:
      我遍历整个链表,每遍历到一个节点,就将其进行输出,直到最后一个节点。当我遍历到最后一个节点,也就是tmp->nextNULL时,此时还需要继续进入循环,来输出最后一个节点,当输出最后一个节点之后,不需要再进入循环。而此时tmp=tmp->nexttmp的值为NULL,所以循环的判断跳出条件为tmpNULL。如果tmp!=NULL,则继续进行循环。
      那是否需要判断空链表呢?由于空链表也是tmpNULL,此时不会进入循环,不会进行任何动作,所以不需要对空链表的情况做特殊处理。
      
当然,如果对于空链表,需要输出一个“empty linked list”,则就需要进行一个额外的判断了,这里的处理是输出一个空行。
代码片断如下:

 1void printlist(linkedlist llist)
 2{
 3    listnode* tmp=llist;
 4    while (tmp)
 5    {
 6        printf("%d ",tmp->data);
 7        tmp=tmp->next;
 8    }

 9    printf("\n");
10}

11

这样的实现就可以了,但是上面是输出一个空行,下面实现是输出“empty linked list”。

片段2

也可以使用下面的实现:

片段3

片段3比片段2要在循环中少掉一行代码,这样应该会减少一点计算时间。不过也相差不多啦。反正也不是数量级的改变。
同时,这里注意一下参数的问题。因为不需要修改,只是浏览,所以这里使用的只是指针,而不是指针的指针来做。

printlist实现结束,下面来实现添加节点的操作:
      添加节点操作就要比上面的遍历操作要复杂一些了,我们需要在指定的位置pos之前添加一个节点,因为是在某个节点之前,所以我们只需要知道其之前的那个节点(pos-1),然后将其串起来即可,此时加入的新节点就会代替掉pos位置,而原来pos位置的节点则成为(pos+1)
      
这里,如果要串起来,则就需要前面有一个节点,然后将它的next指针指向新的节点(当然,在此之前,需要将它原来的指向给新的节点,这样才能够串起来)。那如果没有前面的节点呢?
      由于这里我们需要修改其内容(当为空表,或者插入位置为第一个节点时),所以需要使用到指针的指针。

根据上面的分析,再通过画图(画图都是手绘的,所以这个过程这里就省略了,直接将结果写在这里,而图在每一本数据结构书中应该都有,不过建议还是自己来画一下体会一下)得到:
可以分为5种情况,但是在画图之后,发现最终可以综合为2种情况。
1. 空表,插入新节点
2. 只有一个节点,插入新节点在第一个节点之后
3. 有两个节点(或更多,情况相同),插入节点在两个节点之间
4. 有两个节点(或更多),插入新节点在最后
5. 有一个或多个节点,插入节点在第一个节点之前
两种情况中第一种情况为1和5,第二种情况对应的是2,3,4。也就是插入第一个节点的情况和插入其他位置的情况要分开处理。

经过梳理之后,最后得到的代码如下:

片段4

如果是要插在第一个的位置上,则进入if,否则进入else,先定位到需要插入的节点,然后执行插入。这里如果pos比链表要大,则插入在最后,这里并没有进行错误处理的代码。

这里addNode由于要定位节点,然后执行插入,所以插入动作为O(1),但是整个算法复杂度为O(n)。

此时,编写的main函数如下

 1linkedlist header=NULL;
 2int i;
 3listnode *node;
 4for (i=0;i<8;i++)
 5{
 6    node = (listnode*)malloc(sizeof(listnode));
 7    node->next=NULL;
 8    node->data=i;
 9
10    addNode(&header,node,1);
11}

12

因为main中使用了malloc来分配空间,所以肯定要有free来与其进行对应。而free应该就是实现在deleteNode中的。其实这样,就是库的调用者来实现内存的分配,而库本身来实现内存的回收。这样并不一定不好,不过单纯由库来进行内存的分配和回收也是一种方法,而且这样的好处是都是由库来控制,不容易发生内存泄露的问题。

posted on 2009-10-08 20:57  cnyao  阅读(338)  评论(1编辑  收藏  举报