二.从零写单链表的算法之遍历节点&删除节点&逆序节点

接上节:一.从零写单链表的算法之尾部&头部插入节点
http://blog.csdn.net/wangweijundeqq/article/details/79188813

一.单链表的算法之遍历节点

E:\Linux\0.C_DEMO\1.18_danlianbiao4

1、什么是遍历

(1)遍历就是把单链表中的各个节点挨个拿出来,就叫遍历。

(2)遍历的要点:一是不能遗漏、二是不能重复、追求效率。

2、如何遍历单链表

单链表的特点就是由很多个节点组成,头指针+头节点为整个链表的起始,最后一个节点的特征是它内部的pNext指针值为NULL。从起始到结尾中间由各个节点内部的pNext指针来挂接。由起始到结尾的路径有且只有一条。单链表的这些特点就决定了它的遍历算法。

==(3)遍历方法:从头指针+头节点开始,顺着链表挂接指针依次访问链表的各个节点,取出这个节点的数据,然后再往下一个节点,直到最后一个节点,结束返回。==

3.链表遍历节点过程分析图

这里写图片描述

4.代码实现:

//遍历单链表,ph为指向单链表的头指针,将遍历的节点数据打印出来
void bianli(struct node *ph)
{
    struct node *p=ph; //头指针的后面是头节点
    printf("---------start----------\n");
    while(NULL!=p->pNext)// 是不是最后一个节点
    {
        p=p->pNext;// 走到下一个节点,也就是循环增量
        printf("node data: %d.\n",p->data);
    }
    printf("------------end----------\n");
}
/**************单链表算法之遍历节点***********************/
int main(void)
{
    // 定义一个头节点,用于存链表的节点数
    struct node *pHeader=create_node(0);

     //调用该函数使链表尾部插入新的节点
     insert_head(pHeader,create_node(1));//头部插入
     insert_tail(pHeader,create_node(2));//尾部插入
     insert_head(pHeader,create_node(3));//头部插入

    // 访问链表头结点的有效数据 
    printf("header node data: %d.\n", pHeader->data);   

    //遍历访问单链表各个节点的有效数据
    bianli(pHeader);

    return 0;
}

二.单链表的算法之删除节点

E:\Linux\0.C_DEMO\1.19_danlianbiao5

1.为什么要删除节点?

(1)一直在强调,链表到底用来干嘛的?==用来存储数据==

(2)有时候链表节点中的数据不想要了,因此要删掉这个节点。

2、删除节点的2个步骤

==(1)第一步:找到要删除的节点;第二步:删除这个节点。==

3、如何找到待删除的节点

(1)通过遍历来查找节点。从头指针+头节点开始,顺着链表依次将各个节点拿出来,按照一定的方法比对,找到我们要删除的那个节点。

4、如何删除一个节点(分两种情况)

(1)待删除的节点不是尾节点的情况:

①.首先把待删除的节点的前一个节点的pNext指针指向待删除的节点的后一个节点的首地址(这样就把这个节点从链表中摘出来了),

②.然后再将这个摘出来的节点free掉即可。

这里写图片描述

(2)待删除的节点是尾节点的情况:

①.首先把待删除的尾节点的前一个节点的pNext指针指向null(这时候就相当于原来尾节点前面的一个节点变成了新的尾节点),

②.然后将摘出来的节点free掉。

这里写图片描述

5、设计一个删除节点的算法

(1)删除节点的算法流程框图如下

这里写图片描述

(2)总结总体步骤如下:

①通过遍历链表来寻找需要删除的节点

②找到该节点又分为两种情况:一个是非尾节点,另一个是尾节点

③通过上面描述的待删除的节点不同处理方法处理;

==在这个过程中会遇到一个难点!==

删除节点的困难点在于:通过链表的遍历依次访问各个节点,找到这个节点后p指向了这个节点,==但是要删除这个节点关键要操作前一个节点,但是这时候已经没有指针指向前一个节点了,所以没法操作。==

==解决方案==就是增加一个指针pPrev在循环体中跟随p指针的步伐移动,这个pPrev指针就会一直指向当前节点的前一个节点.

这样就可以通过pPrev->pNext赋值来控制链表的链接状态了

这里写图片描述

6.代码实现

// 从链表pH中删除节点,待删除的节点的特征是数据区等于data
// 返回值:当找到并且成功删除了节点则返回0,当未找到节点时返回-1
//struct node *ph:头指针
//int data:待删除节点的有效数据
int delete_node(struct node *ph,int data)
{
    struct node *p=ph; //头指针的后面是头节点
    struct node *pPrev=NULL;//用来指向当前节点的前一个节点
    printf("---------start----------\n");

    while(NULL!=p->pNext)// 通过遍历节点,判断是不是尾节点
    {
        pPrev=p;//跟随P移动,指向p的前一个节点
        p=p->pNext;// 走到下一个节点,也就是循环增量

        //判断这个节点是否为我们要删除的节点
        if(p->data==data)
        {
            //处理找到的节点,分为两种情况
            if(NULL==p->pNext)//待删除的节点如果为尾节点,执行如下
            {
                pPrev->pNext=NULL;//待删除的尾节点的前一个节点的pNext指针指向null
                free(p);//释放待删除的尾节点的内存
            }
            else  //待删除的节点如果为非尾节点,即普通节点,执行如下
            {
                pPrev->pNext=p->pNext;//待删除的节点的前一个节点的pNext指针指向待删除的节点的后一个节点的首地址
                free(p);//释放待删除的尾节点的内存
            }

            printf("------------end----------\n");
            return 0;//成功删除该节点,退出程序返回0
        }

    }   
    printf("-----------没有找到该待删除的节点----------\n");
    return -1;
}
/**************单链表算法之删除节点***********************/
int main(void)
{
    // 定义一个头节点,用于存链表的节点数
    struct node *pHeader=create_node(0);

     //调用该函数使链表尾部插入新的节点
     insert_head(pHeader,create_node(11));//头部插入
     insert_head(pHeader,create_node(22));//头部插入
     insert_head(pHeader,create_node(33));//头部插入

    // 访问链表头结点的有效数据 
    printf("header node data: %d.\n", pHeader->data);   

    //遍历访问单链表各个节点的有效数据
    bianli(pHeader);

    delete_node(pHeader, 33);//从链表pH中删除数据为33的节点
    printf("------------------删除后-------------\n");

    //再次遍历访问单链表各个节点的有效数据
    bianli(pHeader);

    return 0;
}

三.单链表的算法之逆序

1.如何将链表有效节点 头结点->1->2->3逆序为3->2->1?

思路:

1.1 先判断链表当前没有有效节点或者只有一个有效节点时,逆序不用做任何操作

1.2 当链表有2个及2个以上节点时才需要真正进行逆序操作

①.取出链表原来的第一个有效节点,连接到头结点

==原始链表:头结点->1->2->3
新链表:头结点->1==

②再取出原来链表的第二个有效节点2,头部插入链表,并连接到头结点

==原始链表:头结点->1
新链表:头结点->2->1==

③.最后取出原来链表的第三个有效节点3,头部插入链表,并连接到头结点

==原始链表:头结点->2->1
新链表:头结点->3->2->1==
依次类推,最终实现多个节点的逆置。

2.通过理解,可以发现链表逆序的思路可以总结如下:

==首先遍历原链表,然后将原链表的头指针和头结点作为新链表的头指针和头节点,原链表中的有效节点挨个依次取出来,采用头部插入的方法插入新链表中即可。==

这里写图片描述

在这个过程中会遇到一个难点!

当前节点插入新链表后的困难点在于:通过链表的遍历依次访问各个节点,==在把当前遍历到的节点头插入到新的链表后,插入下一个有效节点的时候,会发现,它的地址已经丢失了,因为保存在上一个节点的pNext中,而上一个节点的p->pNext已经发生了更改,所以没法操作。==

==解决方案==就是增加一个指针pBack在循环体中跟随p指针的步伐移动,这个pPrev指针就会保存p节点后面一个节点的地址

==最后,在遍历到最后一个字节的时候,尾节点是不满足while循环条件的,故在循环结束后手动将尾节点插到新链表中。==

3.代码实现

流程图分析:
这里写图片描述

// 将pH指向的链表逆序
void reverse_linkedlist(struct node *ph)
{
    //p指向第一个有效节点,因为ph->pNext存放的第一个有效节点的地址
    struct node *p=ph->pNext;
    //用来保存当前节点的后一个节点地址
    struct node *pBack;

    //过程1.当链表没有有效节点或者有效节点只有一个时,不做任何操作
    if((NULL==p)||(NULL==p->pNext))
    {
        return;
    }
    //过程2.当有效节点数大于2,才进行逆序操作
    while(NULL!=p->pNext)//遍历节点 判断是不是最后一个节点
    {
        pBack=p->pNext;  //保存p节点后面一个节点的地址
        //第1种情况:如果遍历到的节点是原链表第一个有效节点
        if(p==ph->pNext)
        {
             // 原链表中第一个有效节点将是逆序后新链表的尾节点,
            //尾节点的pNext指向NULL
            p->pNext=NULL;
        }
        //第2种情况:如果遍历到的节点不是原链表第一个有效节点
        else
        {
        //即将后一个节点(pH->pNext为原第一个有效节点地址)与新的第一个节点相连 即实现尾部连接(后端)
            p->pNext=ph->pNext;
        }
        //过程3.将新的第一个有效节点地址连接到头节点,实现头部连接
        ph->pNext=p;

        //过程4.指针走向下一个节点
        //p=ph->pNext; // 这样已经不行了,因为在过程2中p->pNext已经被改过了

        /*解决方法就是增加一个指针pBack在循环体中跟随p指针的步伐移动,
        这个pPrev指针就会保存p节点后面一个节点的地址*/
        p=pBack;

    }
    //过程5.手动头插入尾节点到新链表 (因为循环结束后,最后一个节点仍然缺失)
    //因为在遍历到最后一个字节的时候,尾节点是不满足while循环条件的,故在循环结束后手动将尾节点头插入到新链表中。
    insert_head(ph,p);
}
/**************单链表算法之逆序节点***********************/
int main(void)
{
    // 定义一个头节点,用于存链表的节点数
    struct node *pHeader=create_node(0);

     //调用该函数使链表尾部插入新的节点
     insert_tail(pHeader,create_node(11));//尾部插入
     insert_tail(pHeader,create_node(22));//尾部插入
     insert_tail(pHeader,create_node(33));//尾部插入

    // 访问链表头结点的有效数据 
    printf("header node data: %d.\n", pHeader->data);   

    //遍历访问单链表各个节点的有效数据
    bianli(pHeader);

    reverse_linkedlist(pHeader);//将pH指向的链表中所有有效节点逆序
    printf("------------------逆序后-------------\n");

    //再次遍历访问单链表各个节点的有效数据
    bianli(pHeader);

    return 0;
}

这里写图片描述]![这里写图片描述

猜你喜欢

转载自blog.csdn.net/wangweijundeqq/article/details/79254252