原文章地址:单链表反转、逆序
/*单链表的反转/逆序的三种方法*/
/*
*前面我们大约把单链表 ADT 的基本操作都过了一遍,但是这
*还不够。单链表在面试与笔试中出现的几率很高,接下来我们
*再花点时间把常见的单链表面试题尽可能过一遍,彻底掌握单
*链表~
*/
/*
*那开始我们的第一个面试题?不妨做做“单链表反转”,或者
*说“单链表逆序”吧?还是基于前面的例子。
*/
/*究竟要如何反转呢?我们不妨拿一个例子来说明一下算法。*/
/*
*我先画一个单链表,这个单链表有4个元素。我的思路就是,
*每次把第二个元素提到最前面来。比如下面是第一次交换,我
*们先让头结点的next域指向结点a2,再让结点a1的next域指向
*结点a3,最后将结点a2的next域指向结点a1,就完成了第一次交换。
*/
/*
*然后进行相同的交换将结点a3移动到结点a2的前面,然后再
*将结点a4移动到结点a3的前面就完成了反转。
*/
/*
*思路有了,那就可以写代码了。这里我们需要额外的两个工作
*指针来辅助交换。这个下面的步骤慢慢理解下,结合图片。
*注意结点之间的关系要先断再连。*/
#define OK 1
#define ERROR 0
typedef int ElemType;
typedef int Status;
typedef struct LNode{
ElemType Data;
struct LNode *Next;
}Node,*List;
/*算法一思路:
方法1,current始终是原链表的第一个数,然后把pnext不断移动到首位。
步骤:
*1.定义当前结点 current,初始值为首元结点,current = L->next;
*2.定义当前结点的后继结点 pnext, pnext = current->next;
*3.只要 pnext 存在,就执行以下循环:
* 3.1 定义新节点 prev,它是 pnext的后继结点,prev = pnext->next;
* 3.2 把pnext的后继指向current, pnext->next = current;
* 3.3 此时,pnext 实际上已经到了 current 前一位成为新的current,
* 所以这个时候 current 结点实际上成为新的 pnext,current = pnext;
* 3.4 此时,新的 current 就是 pnext,current = pnext;
* 3.5 而新的 pnext 就是 prev,pnext = prev;
*4.最后将头结点与 current 重新连上即可,L->next = current;
**/
/* 单链表反转/逆序 */
LinkList 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 \n",current->data,current->next->data);
}
//printf("current = %d,next = %d \n",current->data,current->next->data);
L->next = current; /* 将链表头节点指向p1 */
return L;
}
/*算法二思路:
方法2,current始终保持在第一位,pnext与prev遍历并完成交换。
步骤:
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)
*/
LinkList 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;
}
return L;
}
/*算法三思路:
重新建立一个单链表newList,每次将list中的第一个结点放到newList后面。(头插法)
步骤:
第一次循环:tmp保存了 newList->next,其实就是 NULL,
然后把 L 的第一个节点拿过来放到新表的首元结点位置,
然后删了 L 的那个结点。newList->next->next 这句就是补回 NULL。
这时新表为 -> a1 -> NULL.
第二次循环,这次是拿到a2了,这时新表变成 -> a2 -> a1 -> NULL,
其实就是拿个新表来拷贝而已。
*/
LinkList ListReverse3(LinkList L)
{
LinkList newList; //新链表的头结点
LinkList tmp; //指向L的第一个结点,也就是要摘除的结点
//参数为空或者内存分配失败则返回NULL
if (L == NULL || (newList = (LinkList)malloc(sizeof(Node))) == NULL)
{
return NULL;
}
//初始化newList
newList->data = L->data;
newList->next = NULL;
//依次将L的第一个结点放到newList的第一个结点位置
while (L->next != NULL)
{
tmp = newList->next; //保存newList中的后续结点
newList->next = L->next; //将L的第一个结点放到newList中
L->next = L->next->next; //从L中摘除这个结点
newList->next->next = tmp; //恢复newList中后续结点的指针
}
//原头结点应该释放掉,并返回新头结点的指针
free(L);
return newList;
}