单向链表逆序

设链表节点为

typedef struct tagListNode{  
    int data;  
    struct tagListNode* next;  
}ListNode, *List; 

题目描述: 输入一个单向链表,输出逆序反转后的链表

推荐的有两种方法。

一、这是目前见过的最优解:采用一个Head指针,每次从原list解下一个node,把这个node插入到新的list中,从头插入新的节点。采用拆一节点,然后插入新链表的第一个节点位置,从而实现逆序。

不带头节点的解法,只使用头指针。

 8 #include <cstdlib>
  9 #include <cstdio>
 10 #include "slist.h"
 11 
 12 //reverse slist: by using insert node at head
 13 listnode* reverse_slist(listnode *head){
 14     if(NULL == head || head -> next == NULL)//head is null ; or head has only one node
 15         return head;
 16     listnode *newhead = NULL;
 17     while(head != NULL){
 18         if(newhead == NULL){
 19             newhead = head;
 20             head = head -> next;
 21             newhead -> next = NULL;
 22         }
 23         else{
 24             listnode *pheadnext = head -> next;
 25             head -> next = newhead;
 26             newhead = head;
 27             head = pheadnext;
 28         }
 29 
 30     }
 31 
 32     return newhead;
 33 }
 34 


二、 老实逆序操作:采用三个节点指针,分别指向当前节点、当前节点的后继、当前节点的后继的后继。在逆序时,用一个指针记录当前的节点的后继节点。用于下一次操作。

1). 若链表为空或只有一个元素,则直接返回;

  2). 设置两个前后相邻的指针p,q. q=p->next。逆置时,将p所指向的节点;作为q指向节点的后继;

  3). 重复2),直到q为空

  4). 调整链表头和链表尾

不带头节点的头指针操作:

35 listnode* reverse_slist_3point(listnode *head){
 36     if(NULL == head || head -> next == NULL)//head is null ; or head has only one node
 37         return head;
 38     listnode *newhead = NULL;
 39     listnode *pcur = NULL;
 40     listnode *pnext = NULL;
 41     listnode *pnext_next = NULL;
 42     pcur = head;
 43     //next node point must be inited before while statement
 44     pnext = pcur -> next;
 45     //pre = pcur ;
 46     while(pnext != NULL){
 47         //if(pcur -> next != NULL)
 48         //pnext = pcur -> next;
 49         //listnode *pre = pcur;
 50         //pcur = pnext;
 51         pnext_next = pnext -> next;
 52         pnext -> next = pcur;
 53         //pre = pcur;
 54         pcur = pnext;
 55         pnext = pnext_next;
 56     }
 57 
 58     head -> next = NULL;
 59     newhead = pcur;
 60     return newhead;
 61 }

上面代码需要注意的地方是,必须在while循环前,进行链表指针指向的初始化操作

除非是利用head指针:

//单链表的转置,循环方法
Node* reverseByLoop(Node *head)
{
    if(head == NULL || head->next == NULL)
        return head;
    Node *pre = NULL;
    Node *next = NULL;
    while(head != NULL)
    {
        next = head->next;
        head->next = pre;
        pre = head;
        head = next;
    }
    return pre;
}


二--A、第二种方法的图例

from http://blog.csdn.net/niuer09/article/details/5961004


要求将一带链表头List head的单向链表逆序。

分析:

  1). 若链表为空或只有一个元素,则直接返回;

  2). 设置两个前后相邻的指针p,q. 将p所指向的节点作为q指向节点的后继;

  3). 重复2),直到q为空

  4). 调整链表头和链表尾

示例:以逆序A->B->C->D为例,图示如下。实际是,每次只逆置一个节点的指向,同时用 t 来标记下一个操作的节点。


 

实现及测试代码如下:

#include <stdio.h>  
#include <stdlib.h>  
  
typedef struct tagListNode{  
    int data;  
    struct tagListNode* next;  
}ListNode, *List;  
  
void PrintList(List head);  
List ReverseList(List head);  
  
int main()  
{  
    //分配链表头结点  
    ListNode *head;  
    head = (ListNode*)malloc(sizeof(ListNode));  
    head->next = NULL;  
    head->data = -1;  
  
    //将[1,10]加入链表  
    int i;  
    ListNode *p, *q;  
    p = head;  
    for(int i = 1; i <= 10; i++)  
    {  
        q = (ListNode *)malloc(sizeof(ListNode));  
        q->data = i;  
        q->next = NULL;  
        p->next = q;  
        p = q;          
    }  
  
    PrintList(head);           /*输出原始链表*/  
    head = ReverseList(head);  /*逆序链表*/  
    PrintList(head);           /*输出逆序后的链表*/  
    return 0;  
}  
  
List ReverseList(List head)  
{  
    if(head->next == NULL || head->next->next == NULL)    
    {  
       return head;   /*链表为空或只有一个元素则直接返回*/  
    }  
  
    ListNode *t = NULL,  
             *p = head->next,  
             *q = head->next->next;  
    while(q != NULL)  
    {          
      t = q->next;  
      q->next = p;  
      p = q;  
      q = t;  
    }  
  
    /*此时q指向原始链表最后一个元素,也是逆转后的链表的表头元素*/  
    head->next->next = NULL;  /*设置链表尾*/  
    head->next = p;           /*调整链表头*/  
    return head;  
}  
  
void PrintList(List head)  
{  
    ListNode* p = head->next;  
    while(p != NULL)  
    {  
        printf("%d ", p->data);  
        p = p->next;  
    }  
    printf("/n");  
}  

三、个人认为单链表的逆序有以下几种方法:

第一种方法:堆栈法,将原链表元素依序push如堆栈中,然后再pop入新链表中,时空复杂度依然过大,但是这种方法时空复杂度较高;

第二种方法:运用数组。即先遍历单链表取出元素顺序放到数组中,然后从数组中逆序取出元素,再次遍历单链表时放入。这种方法也需要额外建立数组,而且需要遍历两次。----可以引申出,数组保存节点。也可以数组保存节点地址。使用对数组逆序遍历操作,进行指向逆转。----实际这个面试和实际操作都不推荐。
第三种方法:直接逆序。

第四种方法:递归。

四、递归操作

递归的实现:

http://blog.csdn.net/nevasun/article/details/7701942

使用循环的思想实现了链表的逆序,这种方式需要使用三个临时变量。那如何只使用一个临时变量实现单链表逆置呢?我们可以采用栈,将链表节点保存在栈结构中,但是这样也不符合只是用一个临时变量的要求。对比循环和递归的特点,对于变量个数要求的问题,递归可以很好地解决。

  1. typedef struct Node  
  2. {  
  3.     int data;  
  4.     struct Node *pnext;  
  5. }Node;  
  6.   
  7. Node *head;     /* head是逆序后的头结点 */  
  8. Node *reverse(Node *p)  
  9. {  
  10.     if(p == NULL || p->pnext == NULL)  
  11.     {  
  12.         head = p;  
  13.         return head;  
  14.     }  
  15.     reverse(p->pnext)->pnext = p;  
  16.     p->pnext = NULL;  //防止链表成为一个环,这是最关键的。
  17.     return p;  
  18. }  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值