• 单链表反转的分析及实现


    单链表反转的分析及实现

    • 我先画一个单链表,这个单链表有4个元素。我的思路就是,每次把第二个元素提到最前面来。比如下面是第一次交换,我们先让头结点的next域指向结点a2,再让结点a1的next域指向结点a3,最后将结点a2的next域指向结点a1,就完成了第一次交换。
    第一次交换
    • 然后进行相同的交换将结点a3移动到结点a2的前面,然后再将结点a4移动到结点a3的前面就完成了反转。
    第二次交换
    第三次交换
    • 思路有了,那就可以写代码了。这里我们需要额外的两个工作指针来辅助交换。这个下面的步骤慢慢理解下,结合图片。注意结点之间的关系要先断再连。

    步骤:

    1. 定义当前结点 current,初始值为首元结点,current = L->next;
    2. 定义当前结点的后继结点 pnext, pnext = current->next; 
    3. 只要 pnext 存在,就执行以下循环:
      • 定义新节点 prev,它是 pnext的后继结点,prev = pnext->next;
      • 把pnext的后继指向current, pnext->next = current;
      • 此时,pnext 实际上已经到了 current 前一位成为新的current,所以这个时候 current 结点实际上成为新的 pnext,current = pnext;
      • 此时,新的 current 就是 pnext,current = pnext;
      • 而新的 pnext 就是 prev,pnext = prev;
    4. 最后将头结点与 current 重新连上即可,L->next = current;

    函数设计如下:

    01 /* 单链表反转/逆序 */
    02 Status ListReverse(LinkList L)
    03 {
    04     LinkList current,pnext,prev;
    05     if(L == NULL || L->next == NULL)
    06         return L;
    07     current = L->next;  /* p1指向链表头节点的下一个节点 */
    08     pnext = current->next;
    09     current->next = NULL;
    10     while(pnext)
    11     {
    12         prev = pnext->next;
    13         pnext->next = current;
    14         current = pnext;
    15         pnext = prev;
    16         printf("交换后:current = %d,next = %d ",current->data,current->next->data);
    17     }
    18     //printf("current = %d,next = %d ",current->data,current->next->data);
    19     L->next = current;  /* 将链表头节点指向p1 */
    20     return L;
    21 }
    • 其实在你写函数的时候,我也写了个函数,也能运行。思路也差不多,不过你的current一直是表的第一个结点,我这里的current始终是首元结点的值,我的函数需要每次对pnext重新赋值。一会解释下。
    01 Status ListReverse2(LinkList L)
    02 {
    03     LinkList current, p;
    04  
    05     if (L == NULL)
    06     {
    07         return NULL;
    08     }
    09     current = L->next;
    10     while (current->next != NULL)
    11     {
    12         p = current->next;
    13         current->next = p->next;
    14         p->next = L->next;
    15         L->next = p;
    16     }
    17     return L;
    18 }
    1. p = current->next; p 就相当于前面的 pnext。(图1中a2即为p)
    2. current->next = p->next; p->next 就相当于 prev的角色,这句代码意思是 current 的后继指向 prev.(相当于图1中a1->next = a3(a2->next))
    3. p->next = L->next; 这句就是 p 的后继直接指向首元节点。(相当于图1中a2->next = a1)
    4. L->next = p; 然后再将头结点指向 p。(相当于图1中L->next = a2)
    • 参照图就很容易理解上面的步骤了。我觉得我这么写比你的清晰一些。我先将current指向prev,再将pnext指向current,最后将头结点指向pnext。

    这个是程序运行的结果。

    01 整体创建L的元素(头插法):
    02 // 原链表,current = 68, pnext = 55,68指向18,55指向18,头结点指向55
    03 -> 68 -> 55 -> 18 -> 45 -> 41 -> 43 -> 5 -> 28 -> 80 -> 67
    04  
    05 // 第一次交换后,原链表变成这样
    06 -> 55 -> 68 -> 18 -> 45 -> 41 -> 43 -> 5 -> 28 -> 80 -> 67
    07 // 进行第二次交换,pnext = 18,68指向45,18变成头结点
    08 -> 18 -> 55 -> 68 -> 45 -> 41 -> 43 -> 5 -> 28 -> 80 -> 67
    09 // 进行第三次交换,pnext = current->next = 45,68指向41,45变成头结点
    10 -> 45 -> 18 -> 55 -> 68 -> 41 -> 43 -> 5 -> 28 -> 80 -> 67
    11 // ……
    12 -> 41 -> 45 -> 18 -> 55 -> 68 -> 43 -> 5 -> 28 -> 80 -> 67
    13  
    14 -> 43 -> 41 -> 45 -> 18 -> 55 -> 68 -> 5 -> 28 -> 80 -> 67
    15  
    16 -> 5 -> 43 -> 41 -> 45 -> 18 -> 55 -> 68 -> 28 -> 80 -> 67
    17  
    18 -> 28 -> 5 -> 43 -> 41 -> 45 -> 18 -> 55 -> 68 -> 80 -> 67
    19  
    20 -> 80 -> 28 -> 5 -> 43 -> 41 -> 45 -> 18 -> 55 -> 68 -> 67
    21 // current 68 没有后继,反转结束
    22 -> 67 -> 80 -> 28 -> 5 -> 43 -> 41 -> 45 -> 18 -> 55 -> 68
    23  
    24  
    25 反转L后
    26 -> 67 -> 80 -> 28 -> 5 -> 43 -> 41 -> 45 -> 18 -> 55 -> 68

    最后附上完整代码,反转有两个函数。

    • 方法1,current始终保持在第一位,pnext与prev遍历并完成交换。
    • 方法2,current始终是原链表的第一个数,然后把pnext不断移动到首位。
     View Code

    #include<stdio.h>
    #include<stdlib.h>
    #include<time.h>

    #define OK 1
    #define ERROR 0
    #define TRUE 1
    #define FALSE 0

    #define MAXSIZE 20 /* 存储空间初始分配量 */

    typedef int Status;/* Status是函数的类型,其值是函数结果状态代码,如OK等 */
    typedef int ElemType;/* ElemType类型根据实际情况而定,这里假设为int */

    typedef struct Node
    {
    ElemType data;
    struct Node *next;
    }Node;
    /* 定义LinkList */
    typedef struct Node *LinkList;

    /* 初始化顺序线性表 */
    Status InitList(LinkList *L)
    {
    *L=(LinkList)malloc(sizeof(Node)); /* 产生头结点,并使L指向此头结点 */
    if(!(*L)) /* 存储分配失败 */
    {
    return ERROR;
    }
    (*L)->next=NULL; /* 指针域为空 */

    return OK;
    }

    /* 初始条件:顺序线性表L已存在。操作结果:返回L中数据元素个数 */
    int ListLength(LinkList L)
    {
    int i=0;
    LinkList p=L->next; /* p指向第一个结点 */
    while(p)
    {
    i++;
    p=p->next;
    }
    return i;
    }

    /* 初始条件:顺序线性表L已存在。操作结果:将L重置为空表 */
    Status ClearList(LinkList *L)
    {
    LinkList p,q;
    p=(*L)->next; /* p指向第一个结点 */
    while(p) /* 没到表尾 */
    {
    q=p->next;
    free(p);
    p=q;
    }
    (*L)->next=NULL; /* 头结点指针域为空 */
    return OK;
    }

    /* 初始条件:顺序线性表L已存在 */
    /* 操作结果:依次对L的每个数据元素输出 */
    Status ListTraverse(LinkList L)
    {
    LinkList p=L->next;
    while(p)
    {
    visit(p->data);
    p=p->next;
    }
    printf(" ");
    return OK;
    }

    Status visit(ElemType c)
    {
    printf("-> %d ",c);
    return OK;
    }

    /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */
    /* 操作结果:用e返回L中第i个数据元素的值 */
    Status GetElem(LinkList L,int i,ElemType *e)
    {
    int j;
    LinkList p; /* 声明一结点p */
    p = L->next; /* 让p指向链表L的第一个结点 */
    j = 1; /* j为计数器 */
    while (p && j < i) /* p不为空或者计数器j还没有等于i时,循环继续 */
    {
    p = p->next; /* 让p指向下一个结点 */
    ++j;
    }
    if ( !p || j>i )
    return ERROR; /* 第i个元素不存在 */
    *e = p->data; /* 取第i个元素的数据 */
    return OK;
    }

    /* 初始条件:顺序线性表L已存在 */
    /* 操作结果:返回L中第1个与e满足关系的数据元素的位序。 */
    /* 若这样的数据元素不存在,则返回值为0 */
    int LocateElem(LinkList L,ElemType e)
    {
    int i=0;
    LinkList p=L->next;
    while(p)
    {
    i++;
    if(p->data==e) /* 找到这样的数据元素 */
    return i;
    p=p->next;
    }

    return 0;
    }

    /* 随机产生n个元素的值,建立带表头结点的单链线性表L(头插法) */
    void CreateListHead(LinkList *L, int n)
    {
    LinkList p;
    int i;
    srand(time(0)); /* 初始化随机数种子 */
    *L = (LinkList)malloc(sizeof(Node));
    (*L)->next = NULL; /* 先建立一个带头结点的单链表 */
    for (i=0; i < n; i++)
    {
    p = (LinkList)malloc(sizeof(Node)); /* 生成新结点 */
    p->data = rand()%100+1; /* 随机生成100以内的数字 */
    p->next = (*L)->next;
    (*L)->next = p; /* 插入到表头 */
    }
    }

    /* 随机产生n个元素的值,建立带表头结点的单链线性表L(尾插法) */
    void CreateListTail(LinkList *L, int n)
    {
    LinkList p,r;
    int i;
    srand(time(0)); /* 初始化随机数种子 */
    *L = (LinkList)malloc(sizeof(Node)); /* L为整个线性表 */
    r=*L; /* r为指向尾部的结点 */
    for (i=0; i < n; i++)
    {
    p = (Node *)malloc(sizeof(Node)); /* 生成新结点 */
    p->data = rand()%100+1; /* 随机生成100以内的数字 */
    r->next=p; /* 将表尾终端结点的指针指向新结点 */
    r = p; /* 将当前的新结点定义为表尾终端结点 */
    }
    r->next = NULL; /* 表示当前链表结束 */
    }

    /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L), */
    /* 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */
    Status ListInsert(LinkList *L,int i,ElemType e)
    {
    int j;
    LinkList p,s;
    p = *L; /* 声明一个结点 p,指向头结点 */
    j = 1;
    while (p && j < i) /* 寻找第i个结点 */
    {
    p = p->next;
    ++j;
    }
    if (!p || j > i)
    return ERROR; /* 第i个元素不存在 */
    s = (LinkList)malloc(sizeof(Node)); /* 生成新结点(C语言标准函数) */
    s->data = e;
    s->next = p->next; /* 将p的后继结点赋值给s的后继 */
    p->next = s; /* 将s赋值给p的后继 */
    return OK;
    }

    /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */
    /* 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */
    Status ListDelete(LinkList *L,int i,ElemType *e)
    {
    int j;
    LinkList p,q;
    p = *L;
    j = 1;
    while (p->next && j < i) /* 遍历寻找第i个元素 */
    {
    p = p->next;
    ++j;
    }
    if (!(p->next) || j > i)
    return ERROR; /* 第i个元素不存在 */
    q = p->next;
    p->next = q->next; /* 将q的后继赋值给p的后继 */
    *e = q->data; /* 将q结点中的数据给e */
    free(q); /* 让系统回收此结点,释放内存 */
    return OK;
    }

    /* 单链表反转/逆序 */
    Status ListReverse(LinkList L)
    {
    LinkList current,pnext,prev;
    if(L == NULL || L->next == NULL)
    return L;
    current = L->next; /* p1指向链表头节点的下一个节点 */
    pnext = current->next;
    current->next = NULL;
    while(pnext)
    {
    prev = pnext->next;
    pnext->next = current;
    current = pnext;
    pnext = prev;
    }
    //printf("current = %d,next = %d ",current->data,current->next->data);
    L->next = current; /* 将链表头节点指向p1 */
    return L;
    }

    Status ListReverse2(LinkList L)
    {
    LinkList current, p;

    if (L == NULL)
    {
    return NULL;
    }
    current = L->next;
    while (current->next != NULL)
    {
    p = current->next;
    current->next = p->next;
    p->next = L->next;
    L->next = p;
    ListTraverse(L);
    printf("current = %d, ", current -> data);
    }
    return L;
    }

    int main()
    {
    LinkList L;
    Status i;
    int j,k,pos,value;
    char opp;
    ElemType e;

    i=InitList(&L);
    printf("链表L初始化完毕,ListLength(L)=%d ",ListLength(L));

    printf(" 1.整表创建(头插法) 2.整表创建(尾插法) 3.遍历操作 4.插入操作");
    printf(" 5.删除操作 6.获取结点数据 7.查找某个数是否在链表中 8.置空链表");
    printf(" 9.链表反转逆序");
    printf(" 0.退出 请选择你的操作: ");
    while(opp != '0'){
    scanf("%c",&opp);
    switch(opp){
    case '1':
    CreateListHead(&L,10);
    printf("整体创建L的元素(头插法): ");
    ListTraverse(L);
    printf(" ");
    break;

    case '2':
    CreateListTail(&L,10);
    printf("整体创建L的元素(尾插法): ");
    ListTraverse(L);
    printf(" ");
    break;

    case '3':
    ListTraverse(L);
    printf(" ");
    break;

    case '4':
    printf("要在第几个位置插入元素?");
    scanf("%d",&pos);
    printf("插入的元素值是多少?");
    scanf("%d",&value);
    ListInsert(&L,pos,value);
    ListTraverse(L);
    printf(" ");
    break;

    case '5':
    printf("要删除第几个元素?");
    scanf("%d",&pos);
    ListDelete(&L,pos,&e);
    printf("删除第%d个元素成功,现在链表为: ", pos);
    ListTraverse(L);
    printf(" ");
    break;

    case '6':
    printf("你需要获取第几个元素?");
    scanf("%d",&pos);
    GetElem(L,pos,&e);
    printf("第%d个元素的值为:%d ", pos, e);
    printf(" ");
    break;

    case '7':
    printf("输入你需要查找的数:");
    scanf("%d",&pos);
    k=LocateElem(L,pos);
    if(k)
    printf("第%d个元素的值为%d ",k,pos);
    else
    printf("没有值为%d的元素 ",pos);
    printf(" ");
    break;

    case '8':
    i=ClearList(&L);
    printf(" 清空L后:ListLength(L)=%d ",ListLength(L));
    ListTraverse(L);
    printf(" ");
    break;

    case '9':
    ListReverse2(L);
    printf(" 反转L后 ");
    ListTraverse(L);
    printf(" ");
    break;

    case '0':
    exit(0);
    }
    }
    return 0;
    }

    有两个方法可以实现单链表的反转:

    方法一:

    复制代码
     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 
     4 typedef struct Node
     5 {
     6     int data;
     7     struct Node *next; 
     8 }Node; 
     9 Node *head,*p; 
    10 
    11 Node * ReverseLink(Node *head)
    12 {
    13     Node *p1, *p2, *p3;
    14     if(head==NULL || head->next==NULL)
    15         return head;
    16     p1=head, p2=p1->next;
    17     while(p2)
    18     {
    19         p3=p2->next;
    20         p2->next=p1;
    21         p1=p2;
    22         p2=p3;
    23     }
    24     head->next=NULL;
    25     head=p1;
    26     return head;
    27 }
    28 
    29 void CreateList(int n)
    30 {
    31     Node *q;  
    32     int i;
    33     printf("Input %2d data: ",n); 
    34     head=(Node *)malloc(sizeof(Node)); 
    35     q=head;
    36     scanf("%d",&q->data);
    37     for(i=2;i<=n;i++)
    38     {
    39         q->next=(Node *)malloc(sizeof(Node));
    40         q=q->next;
    41         scanf("%d",&q->data);
    42     }
    43     q->next=NULL;
    44 } 
    45 
    46 void PrintList() 
    47 { 
    48     Node *q; 
    49     q=head; 
    50     while (q!=NULL) 
    51     { 
    52         printf("%-8d",q->data);
    53         q=q->next;
    54     }
    55     printf("
    ");
    56 }
    57 
    58 void main()
    59 {
    60     CreateList(5);
    61     PrintList();
    62     head=ReverseLink(head);
    63     PrintList();
    64 }
    复制代码

    方法二:

    复制代码
     1 #include <iostream>
     2 #include <assert.h>
     3 using namespace std; 
     4 
     5 struct LNode{    
     6     char data;   
     7     LNode * next;
     8 }; 
     9 
    10 LNode * initList()
    11 {   
    12     LNode *head=new LNode; 
    13     LNode *curPtr, *newPtr;  
    14     curPtr=head;   
    15     int i=0;    
    16     char ch='A';   
    17     while(i++<10) 
    18     {        
    19         newPtr=new LNode; 
    20         newPtr->data=ch++; 
    21         curPtr->next=newPtr;
    22         curPtr=newPtr;
    23     }    
    24     newPtr->next=NULL;
    25     return head;
    26 } 
    27 
    28 void print(LNode *head)
    29 {    
    30     LNode *ptr=head->next;
    31     while(ptr != NULL)
    32     {        
    33         cout << ptr->data << "  ";
    34         ptr=ptr->next;
    35     }    
    36     cout << endl;
    37 }  
    38 
    39 void reverse(LNode *head)
    40 {   
    41     assert(head != NULL && head->next != NULL);
    42     LNode *ptr=head->next->next;
    43     head->next->next=NULL;     
    44     while(ptr != NULL)   
    45     {       
    46         LNode *tmp=ptr->next;    
    47         ptr->next=head->next;   
    48         head->next=ptr;        
    49         ptr=tmp;
    50     }
    51 } 
    52 
    53 int main()
    54 {   
    55     LNode *head=initList();  
    56     print(head);   
    57     cout << "After reverse: " << endl; 
    58     reverse(head);   
    59     print(head);     
    60     system("PAUSE");   
    61     return 0;
    62 }
    复制代码

    参考:http://www.nowamagic.net/librarys/veda/detail/2241

             http://blog.csdn.net/hyg0811/article/details/11113623

     

     

     

     

     

  • 相关阅读:
    启动tomcat时jmx port被占用
    Intellij Idea下tomcat设置自动编译
    IDEA的快捷键的使用
    IDEA2017注册码
    hosts文件路径及文件介绍
    关于JAVA开发工具IDEA使用
    如何用Word编辑参考文献------这是引用一位大师的
    TDK标签在SEO中的应用
    简单的线条不简单的画
    HTML--网页自动跳转 5种方法
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3305022.html
Copyright © 2020-2023  润新知