zoukankan      html  css  js  c++  java
  • 编程练习(2)——双向链表反转

    本文中的双向链表,具有一个首指针h,但没有尾指针,不是循环链表。链表反转时,要做两件事情,一是将数据部分的pre指针和next指针交换值;二是将h指针指向反转后的头数据节点指针,并将新链表的尾数据节点指针的next(即原链表头数据指针的next)置空。

    上代码:

    DLinkedNode.h

    #ifndef DLINKEDNODE_H_INCLUDED
    #define DLINKEDNODE_H_INCLUDED
    #endif // DLINKEDNODE_H_INCLUDED
    
    
    #include <stdio.h>
    #include <stdlib.h>
    
    typedef int DATA;
    
    typedef struct _dNode
    {
        DATA data;
        struct _dNode *pre;
        struct _dNode *next;
    }DNode,*DLinkedNode;
    
    DLinkedNode Create();
    DLinkedNode CreateN(int *N);
    void PrintH(DLinkedNode h);//按节点先后顺序输出
    int Reverse(DLinkedNode h);

    DLinkedNode.c

    #include "DLinkedNode.h"
    
    DLinkedNode Create()
    {
        DLinkedNode h;
        h=(DLinkedNode)malloc(sizeof(DNode));
        if(h==NULL)
        {
            printf("DLinkedNode:Create:分配内存失败,程序将返回!
    ");
            return NULL;
        }
        else
        {
            h->pre=NULL;
            h->next=NULL;
        }
        return h;
    }
    DLinkedNode CreateN(int *N)
    {
        DLinkedNode h=Create();
        if(h==NULL)
        {
            printf("DLinkedNode:CreateN:分配内存失败,程序将返回!
    ");
            return NULL;
        }
        int i=1;
        DATA d;
        DNode *p=h;
        printf("
    func:CreateN:请输入数据个数:
    ");
        if(scanf("%d",N)==1)
        {
            if(*N<1)
            {
                printf("
    func:CreateN:您输入的数字必须是大于0的整数,程序将结束!
    ");
                return;
            }
            printf("
    func:CreateN:您打算输入的数据个数为%d,请依次输入数据,并按回车键结束
    ",*N);
            while(i<=*N&&scanf("%d",&d)==1)
            {
                DNode *pTemp=(DLinkedNode)malloc(sizeof(DNode));
                if(pTemp!=NULL)
                {
                    pTemp->data=d;
                    pTemp->pre=p;
                    pTemp->next=NULL;
                    p->next=pTemp;
                    p=p->next;
                    i++;
                }
            }
            *N=--i;
            if(*N>=1)
            {
                printf("
    func:CreateN:您已经输入%d个有效数据,以下是您刚才输入的有效数据:
    ",*N);
                PrintH(h);
            }
            else
            {
                printf("
    您输入的有效数据为0个
    ");
            }
    
        }
        return h;
    }
    void PrintH(DLinkedNode h)
    {
        if(h==NULL||h->next==NULL)
        {
            printf("
    DLinkedNode:PrintH:链表不存在或者为空,程序将返回!
    ");
            return NULL;
        }
        DNode *p=h->next;
        printf("
    ");
        while(p!=NULL)
        {
            printf("%d	",p->data);
            p=p->next;
        }
        printf("
    ");
    }
    //反转双向链表
    int Reverse(DLinkedNode h)
    {
        if(h==NULL||h->next==NULL)
        {
            printf("
    DLinkedNode:Reverse:链表不存在或者为空,程序将返回!
    ");
            return NULL;
        }
        int i=0;
        DNode *p,*temp;
        //DNode *flag;
        p=h->next;
        while(p!=NULL)
        {
            temp=p->pre;
            p->pre=p->next;
            p->next=temp;
            //flag=p;
            if(p->pre==NULL)break;//此处也可以用预先定义的一个DNode *flag,记录p=p->pre操作之前的原始p值,结束时将h->next=flag
            else p=p->pre;
            i++;
        }
        h->next->next=NULL;
        h->next=p;
        return i;
    }

    核心的思路,还是利用一个游标指针p遍历链表并进行指针交换操作,一个临时指针变量temp存储交换值。有点类似冒泡排序中的交换值的方式。理解起来也比单链表反转操作要简单。

    需要注意的是,最后一次循环时,由于p会指向p->next,而p->next为NULL,故而会丢失指针位置。为了解决这个问题,可以在循环外先定义一个标记变量flag,记录每次p指向p->next之前的值,还可以如本代码中所用的方法,在循环中加一次判断,如果p->next已经为NULL,则提前结束循环,不再执行p=p->next。

    此外,数据节点反转完毕后,需要将反转后的尾指针的next置空,然后将h指向反转后的头数据节点p。

    下面是调试代码:

    main.c

    #include "DLinkedNode.h"
    
    void testDLinkedNode();
    int main()
    {
        printf("
    ");
        testDLinkedNode();
        return 0;
    }
    void testDLinkedNode()
    {
        int N;
        DLinkedNode h=CreateN(&N);
        Reverse(h);
        PrintH(h);
    }

    如果双向链表的h指针的pre指向链表数据节点的末尾,即双向循环链表。则无需链表反转操作,直接使用pre指针即可遍历所有数据节点。但需要判断终止条件。而这种情况下只能将h指针的数据域作为判断。还有一种思路,给双向链表一个头指针h和尾指针t,头指针pre为NULL,尾指针next为NULL,从而就可以很方便地进行双向遍历。

    双向链表遍历方便,但是插入和删除操作需要多个指针的变动(一般需要变动四个指针)。同时,内存占用也会明显增加。因而需要考虑实际情况选用。

    欢迎交流!

  • 相关阅读:
    百度mp3地址解密码
    VB 在EXE后附加信息
    截屏函数
    Base64和StrToByte
    The Android ION memory allocator, DMABUF is mentioned as well
    DDC EDID 介绍
    Memory management for graphic processors TTM的由来
    科普 写display driver的必看 How video card works [2D的四种主要操作]
    GEM vs TTM
    DMABUF 背景介绍文章 Sharing buffers between devices
  • 原文地址:https://www.cnblogs.com/myseasky/p/4865449.html
Copyright © 2011-2022 走看看