设链表节点为
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
分析:
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
使用循环的思想实现了链表的逆序,这种方式需要使用三个临时变量。那如何只使用一个临时变量实现单链表逆置呢?我们可以采用栈,将链表节点保存在栈结构中,但是这样也不符合只是用一个临时变量的要求。对比循环和递归的特点,对于变量个数要求的问题,递归可以很好地解决。
- typedef struct Node
- {
- int data;
- struct Node *pnext;
- }Node;
- Node *head; /* head是逆序后的头结点 */
- Node *reverse(Node *p)
- {
- if(p == NULL || p->pnext == NULL)
- {
- head = p;
- return head;
- }
- reverse(p->pnext)->pnext = p;
- p->pnext = NULL; //防止链表成为一个环,这是最关键的。
- return p;
- }