• 将单链表翻转的两种方法


    单链表翻转很容易理解,例如:

    输入: NODE1->NODE2->NODE3->NODE4->NODE5->NULL

    输出: NODE5->NODE4->NODE3->NODE2->NODE1->NULL

    那么,定义单链表如下: (为简单起见,将data字段定义为int, 当然实际应用中data很可能是一个复杂的结构体)

    typedef struct list_s {
            int data;
            struct list_s *next;
    } list_t;

    如何将单链表翻转,有如下两种方法。

    1. 使用N个辅助存储空间aux[],N为单链表中所有结点的个数。将所有结点的地址反向存入aux[]中,然后根据aux[]重新生成一个单链表。
    2. 只使用2个辅助存储空间,从头到尾遍历单链表并翻转。

    其中,方法1使用了较多的辅助存储空间,但实现起来很简单,借助了栈(stack)的思想。 跟方法1比起来,方法2的时间效率和空间效率都要更胜一筹,不过实现起来难度稍大一点。

    方法1的代码实现如下:

    o foo1.c

     1 static int get_length(list_t *head)
     2 {
     3         int len = 0;
     4         for (list_t *p = head; p != NULL; p = p->next)
     5                 len++;
     6         return len;
     7 }
     8 
     9 void reverse_singly_linked_list(list_t **head)
    10 {
    11         if (*head == NULL)
    12                 return;
    13 
    14         int len = get_length(*head);
    15         if (len < 2)
    16                 return;
    17 
    18         list_t **aux = (list_t **)malloc(sizeof (list_t *) * len);
    19         if (aux == NULL) /* error */
    20                 return;
    21 
    22         /* save addr of per node to aux[] */
    23         int index = 0;
    24         for (list_t *p = *head; p != NULL; p = p->next) {
    25                 aux[len-1-index] = p;
    26                 index++;
    27         }
    28 
    29         /* rebuild the singly linked list by walking aux[] */
    30         *head = aux[0];
    31         for (int i = 0; i < len-1; i++)
    32                 aux[i]->next = aux[i+1];
    33         aux[len-1]->next = NULL;
    34 
    35         free(aux);
    36 }

    方法2的代码实现如下:

    o foo2.c

     1 void reverse_singly_linked_list(list_t **head)
     2 {
     3         list_t *newhead = NULL;
     4         list_t *this = *head;
     5         list_t *prev = NULL;
     6 
     7         while (this != NULL) {
     8                 /*
     9                  * If this->next is NULL, this is the tail node, which should
    10                  * be the new head
    11                  */
    12                 if (this->next == NULL)
    13                         newhead = this;
    14 
    15                 /*
    16                  * ListIn:  prevNode -> thisNode -> nextNode
    17                  * ListOut: prevNode <- thisNode <- nextNode
    18                  *          1. thisNode->next = prevNode;
    19                  *          2.       prevNode = thisNode;
    20                  *          3.       thisNode = thisNode->next;
    21                  */
    22                 list_t *next = this->next; /* a. save the next */
    23                 this->next = prev;         /* b. reverse */
    24                 prev = this;               /* c. move prev forward */
    25                 this = next;               /* d. move this forward */
    26         }
    27 
    28         *head = newhead;
    29 }

    用meld diff foo1.c foo2.c截图如下:

    完整的C代码点这里

    最后,对单链表翻转函数做单元测试需要考虑三种情况:

    • 输入的单链表头结点指针为NULL
    • 输入的单链表只有一个结点
    • 输入的单链表包含多个结点
  • 相关阅读:
    成为一名优秀程序员所需要知道的那些事
    程序员致富的若干方法探讨
    解决Flex程序在chrome,safari,IE9等浏览器中history的后退与前进问题
    flex4里的状态(state)
    Flex和Flash定时器 setTimeout & setInterval 使用要点
    Flex十种武器(Flex Power Tools: Explorers)
    给你的组件加一个处理事件的属性
    程序员:增加编程经验的另外3种途径
    Flex常用功能代码
    常用Flex IOC框架比较分析
  • 原文地址:https://www.cnblogs.com/idorax/p/6717817.html
Copyright © 2020-2023  润新知