zoukankan      html  css  js  c++  java
  • 编程练习(1)——单链表反转

    单链表反转是链表的一种基本操作。网上看了一些文章,介绍的还是都很详细。自己也尝试着写了一些代码,调试通过。链表的反转方法有很多种。采用游标指针遍历,修改next指针是常用的方法。一般地,需要定义三个指针,比如本文中,定义为

    Node *cur,*pNext,*pre;

    其中,cur用来保存反转后的头指针;pNext是游标,用来遍历链表并将各个节点的next指针值修改为前一个节点地址;而pre指针用来在pNext修改next指针前,将next指向的下一个节点指针(即尚未反转的子链的头指针)临时保存。

    既然pNext为遍历游标,故而循环条件即为判断pNext非空。

    需要注意的是,第一个数据节点反转结束后成为尾节点,因而其next指针需要置空。

    我写的代码的两种思路:一是如前面所述,定义三个指针;另外一种思路是不单独定义临时指针pre,而是用第一个数据节点(即反转后的尾节点)的next指针作为临时指针。同时,cur指针也可以不用,直接用h-next保存转换后的第一个数据指针即可。

    上代码:

    头文件LinkedNode.h

    #ifndef LINKEDNODE_H_INCLUDED
    #define LINKEDNODE_H_INCLUDED
    #endif // LINKEDNODE_H_INCLUDED
    
    #include <stdio.h>
    #include <stdlib.h>
    
    typedef int DATA; //定义通用数据类型
    
    typedef struct _node
    {
        DATA data;
        struct _node *next;
    }Node,*LinkedNode;
    
    LinkedNode Create();//创建空链表,返回头指针
    LinkedNode CreateN(int *N);//构造一个能读入N个初始数据节点的链表,当输入的数据不足N个或有非法数据时,只取前面的有效数据,并修改N值
    void Print(LinkedNode h);//按节点先后顺序呢输出
    int Reverse_1(LinkedNode h);//反转链表
    int Reverse_2(LinkedNode h);
    int Reverse_3(LinkedNode h);
    int Reverse_4(LinkedNode h);

    四个反转方法Reverse_1和Reverse_2是思路一,区别是Reverse_2省略了cur变量。Reverse_3和Reverse_4是思路二,区别是Reverse_4初始化的变量不同,从而循环变量和循环顺序不一样。这样主要是为了练习,说明写代码时变量的定义方式和循环方式都是可以改变的。有些时候可以优化代码。

    算法文件LinkedNode.c

    #include "LinkedNode.h"
    
    //创建空链表,返回头指针
    LinkedNode Create()
    {
        LinkedNode h;
        h=malloc(sizeof(Node));
        if(h==NULL) return NULL;
        else
        {
            h->next=NULL;
        }
        return h;
    }
    
    ////构造一个能读入N个初始数据节点的链表,当输入的数据不足N个时,数据置NULL
    LinkedNode CreateN(int *N)
    {
        LinkedNode h=Create();
        if(h==NULL)return NULL;
        int i=1;
        DATA d;
        Node *p=h;
        printf("\nfunc:CreateN:请输入数据个数:\n");
        if(scanf("%d",N)==1)
        {
            if(*N<1)
            {
                printf("\nfunc:CreateN:您输入的数字必须是大于0的整数,程序将结束!\n");
                return;
            }
            printf("\nfunc:CreateN:您打算输入的数据个数为%d,请依次输入数据,并按回车键结束\n",*N);
            while(i<=*N&&scanf("%d",&d)==1)
            {
                Node *pTemp=malloc(sizeof(Node));
                if(pTemp!=NULL)
                {
                    pTemp->data=d;
                    p->next=pTemp;
                    p=p->next;
                    i++;
                }
                p->next=NULL;
            }
            *N=--i;
            printf("\nfunc:CreateN:您已经输入%d个有效数据,以下是您刚才输入的有效数据:\n",*N);
            Print(h);
        }
        return h;
    }
    
    //按节点先后顺序输出
    void Print(LinkedNode h)
    {
        if(h==NULL||h->next==NULL)
        {
            printf("\nfunc:Print:链表不存在或者为空!\n");
            return;
        }
        Node *p=h->next;
        printf("\n");
        while(p)
        {
            printf("%d\t",p->data);
            p=p->next;
        }
        printf("\n");
    }
    
    //反转链表,带表头指针,用三个指针
    //cur:记录头指针;pNext:游标,操作指针;pre:临时指针,用来临时记录未转换的链表的头位置
    //返回值:循环次数
    //循环前对游标变量pNext初始化,如果将pre也初始化,则可以更改训话顺序
    //注意翻转后的尾指针(即原始的第一个数据指针)的next需要置空
    int Reverse_1(LinkedNode h)
    {
        int i=0;//记录循环次数
        if(h==NULL||h->next==NULL)
        {
            printf("\nfunc:Reverse:链表不存在或者为空!\n");
            return;
        }
        Node *cur,*pNext,*pre;
        cur=h->next;
        pNext=cur->next;
        cur->next=NULL;
        while(pNext)
        {
            pre=pNext->next;
            pNext->next=cur;
            cur=pNext;
            pNext=pre;
            i++;
        }
        h->next=cur;
        return i;
    }
    //与Reverse_1本质一致,但不定义cur指针,而是直接修改h->next,从而减少栈中变量数量
    int Reverse_2(LinkedNode h)
    {
        int i=0;
        if(h==NULL||h->next==NULL)
        {
            printf("\nfunc:Reverse_2:链表不存在或者为空!\n");
            return;
        }
        Node *pNext,*pre;//pNext为游标指针,用来操作next指针反转,pre指针为临时指针,用来临时记录未操作部分的表头
        pNext=h->next->next;//定义游标初始位置,即转换后的尾指针位置
        h->next->next=NULL;//尾指针不使用,置空
        while(pNext)
        {
            pre=pNext->next;
            pNext->next=h->next;
            h->next=pNext;
            pNext=pre;
            i++;
        }
        return i;
    }
    //反转链表,带表头,这种方法是将后面的节点依次插入h的后面
    //为了说明使用h->next->next作为存储临时指针,这里定义Node *tail=h->next
    //这样好处是不用另外定义一个临时指针pre,且循环结束时,tail自动只想NULL,不用专门置空操作
    int Reverse_3(LinkedNode h)
    {
        int i=0;
        if(h==NULL||h->next==NULL)
        {
            printf("\nfunc:Reverse_2:链表不存在或者为空!\n");
            return;
        }
        Node *tail,*pNext;
        tail=h->next;
        pNext=tail->next;//游标指针初始位置,如果此处不初始化,应该把pNext=tail->next放在循环开始,且循环条件为tail->next!=NULL
        while(pNext!=NULL)
        {
            tail->next=pNext->next;
            pNext->next=h->next;
            h->next=pNext;
            pNext=tail->next;
            i++;
        }
        return i;
    }
    //方法同Reverse_3,但pNext在循环前不初始化,故而循环条件和顺利都改变
    int Reverse_4(LinkedNode h)
    {
        int i=0;
        if(h==NULL||h->next==NULL)
        {
            printf("\nfunc:Reverse_2:链表不存在或者为空!\n");
            return;
        }
        Node *tail=h->next;
        Node *pNext;
        while(tail->next!=NULL)
        {
            pNext=tail->next;
            tail->next=pNext->next;
            pNext->next=h->next;
            h->next=pNext;
            i++;
        }
        return i;
    }

    下面是main.c 文件,包含一个测试函数

    #include "LinkedNode.h"
    #include "tree.h"
    
    void testReverse();
    
    int main()
    {
        testReverse();
        return 0;
    }
    void testReverse()
    {
        int N=0;
        int nCount;
        LinkedNode h=CreateN(&N);
        if(N<1)
        {
            printf("\nfunc:testReverse:您输入的数字必须是大于0的整数,程序将结束!\n");
            //return;
    
        }
        nCount=Reverse_4(h);
        if(nCount<1)
        {
            printf("\nfunc:testReverse:共进行了0次链表反转循环,程序将结束\n");
            //return;
        }
        printf("\nfunc:testReverse:在本次链表反转时,共进行了%d次循环\n",nCount);
        printf("\nfunc:testReverse:以下是反转后的结果\n");
        printf("\n=========================================================\n");
        Print(h);
    }

    几个函数测试都通过。编译环境是GCC。下面是两组输入情形:

    QQ截图20151009135406

    QQ截图20151009135426

    欢迎交流!

  • 相关阅读:
    MeteoInfoLab脚本示例:Trajectory
    MeteoInfoLab脚本示例:闪电位置图
    MeteoInfoLab脚本示例:AVHRR HDF数据
    发布MeteoInfo 1.2.8
    切图,css注意事项
    C# Winform下一个热插拔的MIS/MRP/ERP框架15(窗体基类场景1)
    Xamarin Android Webview中JS调用App中的C#方法
    C# Winform下一个热插拔的MIS/MRP/ERP框架14(自动更新)
    C# Winform下一个热插拔的MIS/MRP/ERP框架13(窗体基类)
    RDLC报表的相关技巧四(报表内依照分组重置页码)
  • 原文地址:https://www.cnblogs.com/myseasky/p/4863776.html
Copyright © 2011-2022 走看看