将单链表翻转的两种方法

将一个单链表翻转的描述很简单,例如:

输入: 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使用了较多的辅助存储空间,但实现起来很简单,有点类似使用栈的意思。

方法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_single_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 linked list by walking aux[] */
30         *head = (list_t *)aux[0];
31         for (int i = 0; i < len-1; i++)
32                 ((list_t *)aux[i])->next = aux[i+1];
33         ((list_t *)aux[len-1])->next = NULL;
34
35         free(aux);
36 }

方法2的代码实现如下:

o foo2.c

void reverse_single_linked_list(list_t **head)
{
        list_t *newhead = NULL;
        list_t *this = *head;
        list_t *prev = NULL;

        while (this != NULL) {
                /*
                 * If this->next is NULL, this is the tail node, which should
                 * be the new head
                 */
                if (this->next == NULL)
                        newhead = this;

                /*
                 * ListIn:  prevNode -> thisNode -> nextNode
                 * ListOut: prevNode <- thisNode <- nextNode
                 *          1. thisNode->next = prevNode;
                 *          2.       prevNode = thisNode;
                 *          3.       thisNode = thisNode->next;
                 */
                list_t *t = this->next;
                this->next = prev;
                prev = this;
                this = t;
        }

        *head = newhead;
}

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

完整的代码点这里

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

  • 输入的单链表头结点指针为NULL
  • 输入的单链表只有一个结点
  • 输入的单链表包含多个结点
时间: 2024-10-03 04:21:59

将单链表翻转的两种方法的相关文章

将单链表排序的两种方法

对单链表排序,通常有两种方法.(PS:考察一个程序员的C语言编程功底,通常看他是否能娴熟的操作链表就知道了.) 方法1:将每一个结点保存到额外的数组中,对数组进行排序,然后根据有序的数组重新构建链表. 方法2:直接对链表进行插入排序,但是实现起来比较复杂一些. 显然,方法1最为简单,因为将链式存储L先转化为顺序存储a[],对顺序存储a[]排序,就避免了较为复杂的链接指针操作.一旦对顺序存储a[]排好序后,根据a[]重新构建一个链表是易如反掌的事情. 1. 单链表的定义如下 typedef str

单链表反转的2种方法

1 public class ReverseDemo { 2 3 /** 4 * 单链表反转的两种方法 5 */ 6 public static void main(String[] args) { 7 Node head =new Node("a"); 8 Node node2=new Node("b"); 9 Node node3=new Node("c"); 10 head.setNext(node2); 11 node2.setNext(

实现数组字符串翻转的两种方法

//第一种方法:递归法 #include <stdio.h> int reverse_string(char * string) {  if (*string != '\0')  {      reverse_string(string+1);      printf("%c", *string);  } } int main() {  char *string = "abcde";  printf("源字符串为:%s\n", str

字符串翻转的两种方法(1:利用额外空间 2:交换)

#include<iostream> using namespace std; #define STRLENTH 100 char *str_reverse1(char *str) { char temp[STRLENTH];//字符数组:存放逆序后的字符 char *p = str; //字符指针:指向最后一个非'\0'字符 int i = 0; /*找到最后一个非'\0'字符并使p指向它*/ while(*p != '\0') { p++;//while循环之后p指向'\0'p--后指向数

单链表翻转的几种写法

/* * 带头节点 */ ListNode * reverse(ListNode *head) { if (head == NULL || head->next == NULL) return head; ListNode nhead(-1);//头节点 nhead.next = head; ListNode *prev = head; ListNode *next = head->next; while (next != NULL) { prev->next = next->ne

MySQL单实例重置密码的两种方法

MySQL单实例重置密码的两种方法 在工作学习中,我们有时会忘记数据库的密码,下面是MySQL单实例密码重置的步骤. 说明: (1)[[email protected] ~]# cat /etc/redhat-release CentOS release 6.7 (Final) (2)[[email protected] ~]# mysql --version mysql  Ver 14.14 Distrib 5.7.13, for Linux (i686) using  EditLine wr

js阻止表单提交的两种方法

<body>     <form action="clock.html" method="post" onsubmit="return checkLength()">         <p>name:<input type="text" name="user" id="user"></p>         <input t

使用js提交form表单的两种方法

提交form表单的时候瑶族一些简单的验证,验证完后才能提交,避免无效提交. 1.当输入用户名和密码为空的时候,需要判断.这时候就用到了校验用户名和密码,这个需要在前端页面写:有两种方法,一种是用submit提交.一种是用button提交.方法一: 在jsp的前端页面的头部插入一个js方法: function checkUser(){ var result = document.getElementById("userid").value; var password = document

反转一个链表的两种方法:递归和循环

下面是反转一个链表的两种方法: 一.循环算法 //反转一个链表,循环算法 LinkList Reverse(LinkList& head) { // if(!head) // return head; //此时不用判断head是否为空,如是空的话返回的也是空 LinkList cur = head; LinkList hou; LinkList beh = 0; while (cur) { hou = cur->next; cur->next = beh; beh = cur; cur